ARMv8a 中的这种简单分页是如何工作的

How this simple paging in ARMv8a works

根据 ARM 手册:

In the case of a 4kB granule, the hardware can use a 4-level look up process. The 48-bit address has nine address bits for each level translated (that is, 512 entries each), with the final 12 bits selecting a byte within the 4kB coming directly from the original address.

Bits [47:39] of the virtual address index into the 512 entry L0 table. Each of these table entries spans a 512GB range and points to an L1 table. Within that 512 entry L1 table, bits [38:30] are used as index to select an entry and each entry points to either a 1GB block or an L2 table.

Bits [29:21] index into a 512 entry L2 table and each entry points to a 2MB block or next table level. At the last level, bits [20:12] index into a 512 entry L2 table and each entry points to a 4kB block

这对我来说 100% 有意义。 L0、L1、L2 tables 和到达物理地址的最终偏移量。

然而,看看这段代码:https://github.com/bztsrc/raspi3-tutorial/blob/abaf5a5b2bc1a9fdfe5a9d8191c061671555da3d/10_virtualmemory/mmu.c#L66, explained here:

Because we choose 4k as page size, and one translation entry is 8 bytes, that means we have 512 entries on each page. Therefore indeces 0..511 belong to the first page, 512..1023 to the second and so forth. With other words, the address of paging[0] equals to _end (first page), and paging[512] equals to _end + PAGESIZE (second page).

看起来是按照手册中的设置设置了L0、L1和L2。因此,前 512 个条目将是 L0 table 的条目,513-1024 个条目将是 L1,而 1025-1536 个条目将是 L2 table.

但是在代码中它是这样开始的:

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 table。我想我误解了一些非常错误的东西!

如果 paging[0]paging[511] 跨越第一个 table (L0),那么 paging[512]paging[2013] 跨越第二个 table ( L1) 和 paging[1024]paging[2559] 跨越最后一个 table (L2)?

r<<21r*PAGESIZE 是什么意思?

有两个table,由TTBR0和TTBR1指向。

第一个,TTBR0,直接指向&paging[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,直接指向&paging[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.

第二组偏移到每个 table 的第 511 个描述符的原因是使其位于非常高的地址。

虚拟地址解码是由翻译控制寄存器的T1SZselect进行的;它被注释为 3 级,或 39 位虚拟寻址: 12 位偏移量和 27 位 table 索引(9 位 * 3 级)。

地址位 63..40 传统上必须具有相同的值——要么全为零,要么全为 1。这可以在控制寄存器中放宽,但无论如何,第 63 位选择 TTBR[01] 中的哪一个将用于 select 两个 L0 页面 table 集合中的一个。

传统上,每个进程都会有自己的 TTBR0,内核会有一个 TTBR1 用于所有进程[因此不需要更改]。