c - ARMv8a 中这个简单的分页是如何工作的
问题描述
根据ARM手册:
在 4kB 颗粒的情况下,硬件可以使用 4 级查找过程。48 位地址有 9 个地址位,每个转换级别(即每个 512 个条目),最后 12 位选择 4kB 中直接来自原始地址的字节。
512 条目 L0 表中的虚拟地址索引的位 [47:39]。这些表条目中的每一个都跨越 512GB 范围并指向一个 L1 表。在该 512 条目 L1 表中,位 [38:30] 用作选择条目的索引,每个条目指向 1GB 块或 L2 表。
位 [29:21] 索引到 512 条目 L2 表,每个条目指向 2MB 块或下一个表级别。在最后一级,位 [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 个条目。因此,索引 0..511 属于第一页,512..1023 属于第二页,依此类推。换句话说,paging[0]的地址等于_end(第一页),paging[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 表。我想我误解了一些非常错误的东西!
是否应该跨越第一个表 (L0),然后跨越paging[0]
第二个表 (L1)并跨越最后一个表 (L2)?paging[511]
paging[512]
paging[2013]
paging[1024]
paging[2559]
r<<21
和,这些是什么r*PAGESIZE
意思?
解决方案
有两个表,分别由 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.
第二组偏移到每个表的第 511 个描述符的原因是使其位于非常高的地址。
虚拟地址解码由翻译控制寄存器的T1SZ选择;它被注释为 3 级或 39 位虚拟寻址:12 位偏移和 27 位表索引(9 位 * 3 级)。
传统上,地址位 63..40 必须具有相同的值——全零或全一。这可以在控制寄存器中放松,但无论如何,位 63 选择 TTBR[01] 中的哪一个将用于选择两个 L0 页表集之一。
传统上,每个进程都有自己的 TTBR0,内核将有一个用于所有进程的 TTBR1 [因此无需更改]。
推荐阅读
- wix - 组件规则问题:在现有应用程序旁边安装依赖版本的插件文件
- java - 可以在 Runtime.exec() 中传递的最大参数数是多少?
- php - 我正在寻找一个函数来增加 ACF 循环中数据选择器的值
- php - 有没有办法在使用 JOIN 时为两个数据库放置 WHERE?
- outlook - Outlook 会议邀请在电子邮件客户端的代码中显示
- r - 从 R 中的数据框列表中删除 Nan
- oracle - 在sql中添加字段查询
- c++ - C++ 函数中两个类之间的交互
- sql - Postgresql 选择项目(书),如果它有两个或多个具有多对多关系的值(标签)
- angular - Monaco Editor - 如何禁用错误(打字稿)