QEMU 6.2 源代码分析之 [ 6 ] —— MMU 虚拟化

对于qemu来说读写内存涉及到内存模拟模块,qemu还模拟了tlb,因此读写一块Guest OS的虚拟内存地址(Guest Virtual Address -> GVA)首先会查询tlb,如果tlb不命中的话会走tlb慢路径。tlb慢路径要经由guest的mmu经页表转换为物理内存地址(Guest Physics Address -> GPA),再经过qemu内存管理模块转换为qemu进程的虚拟地址(Host Virtual Address -> HVA)。
那么读写GVA的arm指令编译成X86_64指令就是读写对应的HVA即可。
tlb相应的数据结构在include/exec/cpu-defs.h文件中定义,其中结构体CPUTLB由ArchCPU中的CPUNegativeOffsetState neg所引用。
TLB命中时对应CPUTLBEntry对象的addend + GVA = HVA。

code_gen_buffer
-》helper_le_ldq_mmu
-》load_helper
-》tlb_fill
-》arm_cpu_tlb_fill
					-get_phys_addr (&cpu->env, address, access_type, core_to_arm_mmu_idx(&cpu->env, mmu_idx)), 
&phys_addr, &attrs, &prot, &page_size,&fi, &cacheattrs);   // phys_addr 中存放返回的GPA: 0X4XXXXXXX地址
               //根据 ttbr_el1 寄存器中取得页表基地址,将GVA转成GPA
		   				-get_phys_addr_lpae()  //进行页表转换
							-》ttbr = regime_ttbr(env, mmu_idx, param.select)  
								-return env->cp15.ttbr1_el    //从 ttbr_el1 中获得一级页表的基地址
					-tlb_set_page_with_attrs(cs, address, phys_addr, attrs, prot, mmu_idx, page_size)  // Add a new TLB entry,设置页属性
						-》addend = (uintptr_t)memory_region_get_ram_ptr(section->mr) + xlat;  //通过GPA得到HVA地址
						-》tn.addend = addend - vaddr_page;  //HVA – GVA 得到 addend

get_phys_addr()函数会遍历页表并(如果映射存在)将页面添加到 TLB。成功时返回 true。否则,如果正在探测,返回 false。否则使用 ARM DFSR/IFSR 故障寄存器格式填充 fsr,并发出故障信号。

假设已经知道了 GPA 地址,接下来,又该如何读写到里面的数据呢?
如果QEMU已经知道了Guest OS中的一个 GPA 地址,需要向GPA写数据,可以调用如下2个函数进行读写:

//addr:  虚拟机物理地址
//buf:    要写的数据
//len: 要写的数据长度
static inline void cpu_physical_memory_write(hwaddr addr,
                                             const void *buf, hwaddr len)
{
    cpu_physical_memory_rw(addr, (void *)buf, len, true);
}

static inline void cpu_physical_memory_read(hwaddr addr,
                                            void *buf, hwaddr len)
{
    cpu_physical_memory_rw(addr, buf, len, false);
}

调用流程如下:

cpu_physical_memory_read
	-cpu_physical_memory_rw(addr, buf, len, false)
		-address_space_rw(&address_space_memory, addr, MEMTXATTRS_UNSPECIFIED, buf, len, is_write)
			-address_space_read_full(as, addr, attrs, buf, len)
				-》fv = address_space_to_flatview(as);
					-return qatomic_rcu_read(&as->current_map)
        				-flatview_read(fv, addr, attrs, buf, len);
					-》mr = flatview_translate(fv, addr, &addr1, &l, false, attrs)
						-》section = flatview_do_translate(fv, addr, xlat, plen, NULL, is_write, true, &as, attrs);
							-》section = address_space_translate_internal(flatview_to_dispatch(fv), addr, xlat, plen_out, is_mmio)
								section = address_space_lookup_region(d, addr, resolve_subpage);
    								addr -= section->offset_within_address_space;
    								*xlat = addr + section->offset_within_region;
    						-》mr = section.mr
					-flatview_read_continue(fv, addr, attrs, buf, len, addr1, l, mr)
						-》ram_ptr = qemu_ram_ptr_length(mr->ram_block, addr1, &l, false);
							-ramblock_ptr(block, addr)
								-return (char *)block->host + offset  //哈哈,到这里,算是跟踪到底了,例如一个GPA地址是val:0x42a68000,  而虚拟机的内存起始地址是 0x40000000,那么,这里的 offset 就是 0x2a68000,而 block->host 指向为虚拟机分配2GB 空间时,mmap() 返回的HVA地址,所以,两者相加,就是该 GPA 对应的HVA的地址
            						-memcpy(buf, ram_ptr, l);

===[qemu_anon_ram_alloc ]—[248 ] ptr:0x7fa793e00000, size:0x80000000
===block->host:0x7fa793e00000

总结一下:
假如现在有一个GPA地址0x42a68000,Guest的物理内存区间是[0x40000000,+2GB],这2GB空间,是由 HOST通过 mmap分配得来的,位于HVA: 0x7fa793e00000处,那么,
• section->offset_within_address_space:0x40000000
• section->offset_within_region:0
• block->host:0x7fa793e00000
QEMU根据GPA转换HVA的大致过程是这样的,首先,通过全局变量address_space_memory,找到它里面的as->current_map,得到 flatview,接着,得到fv-> dispatch,通过address_space_lookup_region(),在 AddressSpaceDispatch中遍历查找包含该GPA地址0x42a68000的那个section,接着再用 GPA 地址 0x42a68000减去 section->offset_within_address_space,这个值是0x40000000, 得到 0x2a68000,再加上section->offset_within_region,这个值是0,所以得到 0x2a68000
接着,再通过 section.mr,得到MemoryRegion, 再通过 mr->ram_block,得到 ram_block,再 ram_block->host + 0x2a68000,而 ram_block->host 是0x7fa793e00000,所以最终得到的HVA就是( 0x7fa793e00000 + 0x2a68000 )

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值