As the article http://shangavin.7ta.cn/Article/1617946/229307 indicates, QEMU has to supply fake physical address to the upper guest OS. The page cooresponding to the faked physical address is called as Guest Physical Address (GPA). On the other hand, the QEMU is one of running processes on top of host OS, which means what QEMU is aware is virtual address. The virtual address that QEMU has is called Host Virtual Address (HVA). All right, GPA and HVA would be the topic of the article. Actually, how the conversion between GPA and HVA is achieved is critical important across the virtualized system.
All the memory that requested from guest OS should be allocated in QEMU through calling glibc based functions like memalign(). From QEMU's respective, the memory that owned by the upper guest OS is divided into multiple memory blocks and those memory blocks are linked to form memory block list. In essence本质上,that's the memory block list supplies the mechanism to translate GPA to HVA, or conversely. Each memory block in the list is descripted by following data structure, which is defined in cpu-all.h
typedef struct RAMBlock {
uint8_t *host; /* HVA, pointing to memory block allocated by memalign() or vmalloc() */
ram_addr_t offset; /* GPA, which was faked by QEMU */
ram_addr_t length; /* length of the memory block */
char idstr[256]; /* unique ID string of the associated memory block */
QLIST_ENTRY(RAMBlock) next;
#if defined(__linux__) && !defined(TARGET_S390X)
int fd; /* I don't know its function at this moment */
#endif
} RAMBlock;
Since you're acquainted with the memory block descriptor from the above section, you're supposed to be 被认为是 read for knowing how the memory block list is managed. First of all, the physical memory block requsted by guest OS should allocated by QEMU through function qemu_ram_alloc(). During the function, the GPA will be figured out through function call find_ram_offset(). Because of the importance of GPA, lets take a look insight into the function. It's obvious that the GPA will be allocated from 0 to higher address.
static ram_addr_t find_ram_offset(ram_addr_t size)
{
RAMBlock *block, *next_block;
ram_addr_t offset = RAM_ADDR_MAX, mingap = RAM_ADDR_MAX;
if (QLIST_EMPTY(&ram_list.blocks))
return 0;
QLIST_FOREACH(block, &ram_list.blocks, next) {
ram_addr_t end, next = RAM_ADDR_MAX;
end = block->offset + block->length;
QLIST_FOREACH(next_block, &ram_list.blocks, next) {
if (next_block->offset >= end) {
next = MIN(next, next_block->offset);
}
}
if (next - end >= size && next - end < mingap) {
offset = end;
mingap = next - end;
}
}
if (offset == RAM_ADDR_MAX) {
fprintf(stderr, "Failed to find gap of requested size: %" PRIu64 "\n",
(uint64_t)size);
abort();
}
return offset;
}
After the memory allocated from qemu_ram_alloc(), it should be registered through function call cpu_register_physical_memory(). Finally, the physical memory block will be sychronized with KVM so that the host OS can handle page fault happening in guest OS.
cpu_register_physical_memory
cpu_register_physical_memory_offset
cpu_notify_set_memory
kvm_client_set_memory
kvm_set_phys_mem
kvm_set_user_memory_region
原文链接:
http://shangavin.7ta.cn/Article/1617946/229483/