根据ARM手册:
在4kB颗粒的情况下,硬件可以使用4级查找过程48位地址每级有9个地址位已翻译(即,每个512个条目(,最后12位在直接来自原件的4kB内选择一个字节住址
虚拟地址索引的比特[47:39]进入512条目L0表。这些表条目中的每一个都跨越512GB的范围,并指向L1桌子在512条目L1表中,位[38:30]用作索引选择一个条目,每个条目指向1GB块或L2表。
Bits[29:21]索引到512个条目的L2表中,并且每个条目指向2MB块或下一个表级别。在最后一级,bits[20:12]索引进入512条目的L2表,并且每个条目指向4kB块
这对我来说是100%有意义的。L0、L1、L2表和到达物理地址的最终偏移量。
但是,请查看以下代码:https://github.com/bztsrc/raspi3-tutorial/blob/abaf5a5b2bc1a9fdfe5a9d8191c061671555da3d/10_virtualmemory/mmu.c#L66,在此解释:
因为我们选择4k作为页面大小,并且一个翻译条目是8字节,这意味着我们在每页上有512个条目。因此indeces0..511属于第一页,512..1023属于第二页,依此类推。换句话说,分页[0]的地址等于_end(首先页面(,并且分页[512]等于_end+PAGESIZE(第二页面(。
看起来它正在设置手册中提到的L0、L1和L2。因此,前512个条目是L0表的条目,513-1024个条目是L1,1025-1536个条目是L2表。
然而,在代码中,它开始这样做:
paging[4*512+511]=(unsigned long)((unsigned char*)&_end+5*PAGESIZE) | // physical address
PT_PAGE | // we have area in it mapped by pages
PT_AF | // accessed flag
PT_KERNEL | // privileged
PT_ISH | // inner shareable
PT_MEM; // normal memory
索引4*512+511 = 2559
远远超过了我想象的L2表。我想我误解了一些非常错误的东西!
paging[0]
和paging[511]
是否应该跨越第一个表(L0(,然后paging[512]
和paging[2013]
是否应该跨越第二个表(L1(,paging[1024]
和paging[2559]
是否应该跨越最后一个表(L2(?
r<<21
和r*PAGESIZE
是什么意思?
有两个表,由TTBR0和TTBR1指向。
第一个,TTBR0,直接指向&分页[0],并形成L0、L1、L2页面继承:
Paging[0] points at &paging[512*2]
Paging[512*2] points at &paging[512*3]
Paging[512*3..512*3+511] contains page descriptors for physical memory at 0..200000.
附加
Paging[512*2+1..512*2+511] contains large descriptors for physical memory at 400000..40000000
第二个(内核(,TTBR1,直接指向&分页[512],形成类似的L0、L1、L2继承:
Paging[512+511] points at &paging[512*4]
Paging[512*4+511] points at &paging[512*5]
Paging[512*5] contains a descriptor for MMIO_BASE+0x201000.
第二个集合偏移到每个表的第511个描述符的原因是使它处于非常高的地址。
虚拟地址解码由转换控制寄存器的T1SZ选择;它被注释为3个级别或39位的虚拟寻址:12位偏移和表索引的27位(9位*3级(。
地址位63..40传统上必须具有相同的值——要么全为零,要么全为一。这可以在控制寄存器中放松,但不管怎样,位63都会选择TTBR[01]中的哪一个将用于选择两个L0页表集之一。
传统上,每个进程都有自己的TTBR0,内核将有一个用于所有进程的TTBR1[因此不必更改]。