众所周知,MMU的页表是由软件建立在外部内存中的。实际上有两条硬件路径需要访问这个页表:
- MMU硬件进行page table walk时,它会读取查找这个页表。
- 软件修改这个页表的页表项(Page table entry)时,需要load/store指令通过load-store unit (LSU)访问。
MMU page table walk
MMU page table walk这条路径上对全部页表项访问的属性(Cacheability, Shareability)是通过寄存器TCR_ELx.IRGNy, TCR_ELx.ORGNy, TCR_ELx.SHy (x=1, 2 or 3, and y= 0 or 1)来设置的。
TCR_ELx.SHy可以设置MMU page table walk这条路径上对页表访问的Shareability属性。TCR_ELx.IRGNy, TCR_ELx.ORGNy可以分别设置访问的Inner 和Outer Cacheability属性为:
* Normal memory, Non-cacheable.
* Normal memory, Write-Back Read-Allocate Write-Allocate Cacheable.
* Normal memory, Write-Through Read-Allocate No Write-Allocate Cacheable.
* Normal memory, Write-Back Read-Allocate No Write-Allocate Cacheable.
当设置为Non-cacheable时,MMU page table walk时不查找cache, 直接在外部内存中查找页表。
当设置为Cacheable时, MMU page table walk unit可以将外部内存中的页表项缓存在cache中,page table walk时先查找cache,检查需要的页表项是否在cache中,如果不在则继续去外部内存中页表中查找。
软件修改页表
当MMU enable时,软件对页表本身的访问也是使用虚拟地址,因而MMU page table中某些页表项是用来描述页表本身所在的 page的访问属性,软件使用load/store指令对页表项的访问使用这个属性。
假设,要修改下图中标为绿色的页表项,这个页表项所在的page的属性由图中标为橙色的页表项描述。
- load/store指令需要利用上一章节‘MMU page table walk’的过程,通过MMU获得访问的地址(对标为绿色的页表项的访问)对应的属性(在标为橙色的页表项中描述)
- 获取到物理地址和访问属性后,LSU接下来可以访问页表项(标为绿色)
a. 如果要修改的页表项访问属性为Normal Non Cacheable/Device type
那么这个load/store访问的页表项不在cache中缓存,不查找cache
b. 如果要修改的页表项访问属性为Normal Cacheable
那么这个load/store访问的页表项可以在cache中缓存
如何配置MMU page table walk的访问属性
如果需要在MMU enable的情况下,运行时修改页表项。如上所述,因为MMU page table walk的访问属性和load/store访问页表本身的访问属性是分别设置的,不匹配的设置可能会导致一致性问题。
例如,如果MMU page table walk的访问属性设置为 Normal Cacheable,而load/store访问页表本身的访问属性设置为Normal Non Cacheable,
- MMU page table walk查找页表时可以cache这些页表项。
- 接下来软件通过load/store修改页表项时,不会在cache中查找,而直接修改在外部内存中的页表,不会更新cache。
- 下一次MMU page table walk时,MMU table walk依然只看到cache中stale的页表项,而不使用步骤2更新的页表项。
因此,在需要动态修改页表的情况下,软件最好将MMU page table walk的访问属性和load/store访问页表本身的访问属性设置为一样的,都是Normal Cacheable或都是Normal Non Cacheable。Shareability属性也应该设置为一样。
以Linux kernel为例
Linux kernel会动态修改页表。
如以下代码所示,
https://elixir.bootlin.com/li...
https://elixir.bootlin.com/li...
https://elixir.bootlin.com/li...
mov_q tcr, TCR_TxSZ(VA_BITS) | TCR_CACHE_FLAGS | TCR_SMP_FLAGS | \
TCR_TG_FLAGS | TCR_KASLR_FLAGS | TCR_ASID16 | \
TCR_TBI0 | TCR_A1 | TCR_KASAN_SW_FLAGS
/* PTWs cacheable, inner/outer WBWA */
#define TCR_CACHE_FLAGS TCR_IRGN_WBWA | TCR_ORGN_WBWA
#define TCR_SMP_FLAGS TCR_SHARED
#define TCR_SHARED (TCR_SH0_INNER | TCR_SH1_INNER)
MMU page table walk的访问属性设置为 Inner Shareable Normal Inner/Outer Write-Back Write-Allocate
而Linux kernel的页表是从一般的page中分配的,load/store对页表本身的访问属性也是Inner Shareable Normal Inner/Outer Write-Back Write-Allocate
https://elixir.bootlin.com/li...
#define PROT_NORMAL (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_WRITE | PTE_ATTRINDX(MT_NORMAL))
#define _PROT_DEFAULT (PTE_TYPE_PAGE | PTE_AF | PTE_SHARED)
#define _PAGE_DEFAULT (_PROT_DEFAULT | PTE_ATTRINDX(MT_NORMAL))
因此Linux Kernel中,MMU page table walk的访问属性和load/store访问页表本身的访问属性设置都是Inner Shareable Normal Inner/Outer Write-Back Write-Allocate,因为这个属性是可以利用硬件一致性,在kernel中使用set_pte函数修改页表项时,不需要附加对这个页表项的cache maintenance操作。