GPA 到 HVA的过程
QEMU通过GPA找到MemoryRegion从而完成到HVA的转换,而QEMU总是先从tlb中找tlbentry,再利用tlbentry找mr。GPA到HVA的问题就变为了,GPA找tlbentry的问题。
不是完整的状态机
- 指令经过qemu动态翻译后得到GPA地址字面值
addr
,qemu需要根据该地址找到对应的MR然后执行相应的读写操作 - GPA地址字面值在
XXX_helper
(如load_helper
)中先查tlb,在利用查到的tlbentry索引出MemoryRegionSection(MR section)- 如果tlb没命中,则先
tlb_fill() -> -> tlb_set_page_with_attrs()
更新tlb。如果命中,则返回tlbentry
(TODO:iotlbentry只是一种情况)用于下一阶段section的查找 - 在
tlb_set_page_with_attrs() -> address_space_translate_for_iotlb()
中完成具体tlb更新操作。会执行phys_page_find()
查找section,计算出section的地址更新tlb,之后再查tlb就有对应的entry索引section了1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
hwaddr paddr, MemTxAttrs attrs, int prot,
int mmu_idx, target_ulong size)
{
section = address_space_translate_for_iotlb(cpu, asidx, paddr_page,
&xlat, &sz, attrs, &prot);
if (is_ram) {
iotlb = memory_region_get_ram_addr(section->mr) + xlat;
...
} else {
iotlb = memory_region_section_get_iotlb(cpu, section) + xlat;
...
}
desc->iotlb[index].addr = iotlb - vaddr_page;
desc->iotlb[index].attrs = attrs;
}
- 如果tlb没命中,则先
- 经过
XXX_helper
,得到tlbentry
,真正的读写将通过tlbentry
查找MR section从而找到MemoryRegion完成访存操作
查找MemoryReginoSection
的过程,主要发生在查找tlb中,因为tlb命中后直接用命中的iotlbentry
来索引整个线性空间查找section。如下面io_readx
这个例子,直接用tlbentry来索引整个线性空间就找到了section(return §ions[index & ~TARGET_PAGE_MASK]
)
1 | static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry, |
接下来将介绍究竟是如何找到MR section来填充tlb的。首先需要介绍几个结构:AddressSpaceDispatch
, PhysPageEntry
, PhysPageMap
1 | struct PhysPageEntry { |
qemu维护了一个类似多级页表的结构用于检索MR section。在上述结构中PhysPageEntry
相当于页表项,PhysPageMap
用于维护整个地址空间。
- PhysPageMap
PhysPageMap
中的nodes
是用于检索的多级页表结构; sections
表示被索引的整个地址空间,索引到section后(tlbentry)直接sections[index]
获取section
- AddressSpaceDispatch
AddressSpaceDispatch
是一段地址空间的入口,mru_section
是利用局部性原理的section缓存; phys_map
是第一级表(nodes
)的索引; map
是内存映射结构的引用
整体关系大致如下图
基本结构了解后可以看看查找MR section的核心函数phys_page_find
1 | static MemoryRegionSection *phys_page_find(AddressSpaceDispatch *d, hwaddr addr) |
因此,tlbentry就相当于记录了直接到sections
的索引,加速了查表速度,获取到section后就可以section->mr
获取到对应的MemoryRegion,从而完成访存操作