Tri*_*ubé 3 virtual-memory mmu arm64 armv8
我正在做一个业余爱好操作系统项目,并试图设置虚拟内存。我在 x86 架构中还有另一个使用页表的项目,但我现在正在学习 ArmV8。
现在,用于寻址的最大位数是 48 [1]。最后 12 到 16 位“按原样”用于在所选区域内进行索引(取决于选择的颗粒大小[2])。
我只是不明白我们如何获得这些中间位。显然,文档显示使用了中间表[3],但尚不清楚如何使用这些表。
在下图的前半部分中,我们看到使用 4k 颗粒并使用 38 个地址位的地址转换。

我完全无法理解这个图像。“偏移量”,例如位 38 到 30,指向 L1 表中的条目。该表是如何定义的以及在哪里定义的?
我认为正在发生的是,这是一个 12+8+8+8 地址转换方案。从右边开始,12 位查找 4096 内存块内的偏移量。右边是 L3 的 8 位,这意味着 L3 索引 256 个 4096 字节 (1MB) 的块。右边的 L2 也有 8 位,因此有 256 个 (256*4096) 条目,每个 L2 条目总计 256MB。L2 的右侧是 L1,也是 8 位,256 个 256MB 的条目意味着总可寻址内存为 64GB 物理 RAM。
我认为这是不正确的,因为这只允许内存的 1:1 映射。每个表描述符需要携带一些访问标志等等。因此回到问题:这些表是如何定义的。每个偏移部分都是 8 位,这不足以包含转换表的地址。
无论如何,我完全迷失了。如果有人能给我一个关于如何完成翻译表行走的“简单英语”解释,我将不胜感激?一张图表会很好,但可能太费力了,我会制作一个图表并在之后分享以帮助我综合信息。或者至少,如果有人有一个好的视频/指南的链接,其中的信息没有完全混淆?
以下是我查阅过的材料清单:
https://developer.arm.com/documentation/den0024/a/The-Memory-Management-Unit/Translated-a-Virtual-Address-to-a-Physical-Address https://forums.raspberrypi.com/viewtopic .php?t=227139
https://github.com/bztsrc/raspi3-tutorial/blob/master/10_virtualmemory/mmu.c
[1] https://developer.arm.com/documentation/den0024/a/The-Memory-Management-Unit/Translation-tables-in-ARMv8-A
[2] https://developer.arm.com/documentation/den0024/a/The-Memory-Management-Unit/Translation-tables-in-ARMv8-A/Effect-of-capsule-sizes-on-translation-tables
[3] https://developer.arm.com/documentation/den0024/a/The-Memory-Management-Unit/Translated-a-Virtual-Address-to-a-Physical-Address
转换表背后的整个模型由三个值组成:转换表条目 (TTE) 的大小、硬件页大小(也称为“转换颗粒”)以及用于虚拟寻址的位数。
在arm64上,TTE始终是8字节。硬件页面大小可以是 4KiB、16KiB 或 64KiB(0x1000、0x4000 或 0x10000 字节)之一,具体取决于硬件支持和运行时配置。用于虚拟寻址的位数同样取决于硬件支持和运行时配置,但具有更复杂的约束。
为了简单起见,我们考虑 TTBR0_EL1 下的地址转换,没有块映射,没有进行虚拟化,没有指针验证,没有内存标记,没有“大物理地址”支持,并且“顶部字节忽略”功能处于非活动状态。让我们选择 0x1000 字节的硬件页面大小和 39 位虚拟寻址。
从这里开始,我发现最容易从结果开始并向后追溯,以了解我们为何到达这里。因此,假设您有一个虚拟地址,0x123456000并且硬件将其映射到物理地址0x800040000。因为页面大小为 0x1000 字节,这意味着对于0 <= n <= 0xfff,所有对虚拟地址的访问都0x123456000+n将转到物理地址0x800040000+n。因为 0x1000 = 2^12,这意味着虚拟地址的最低 12 位不用于地址转换,而是索引到结果页面。虽然 ARMv8 手册没有使用这个术语,但它们通常被称为“页面偏移量”。
63 12 11 0
+------------------------------------------------------------+-------------+
| upper bits | page offset |
+------------------------------------------------------------+-------------+
Run Code Online (Sandbox Code Playgroud)
现在明显的问题是:我们是如何得到的0x800040000?显而易见的答案是:我们从翻译表中得到它。具体来说,是一个“3 级”翻译表。让我们暂时推迟我们是如何发现它的0x800037000,并假设我们知道它位于。值得注意的一件事是,转换表也遵循硬件页面大小,因此我们在那里有 0x1000 字节的转换信息。因为我们知道一个 TTE 是 8 个字节,所以我们得到 0x1000/8 = 0x200,或者该表中有 512 个条目。512 = 2^9,因此我们需要虚拟地址中的 9 位来索引该表。由于我们已经使用低 12 位作为页偏移量,因此我们在这里采用位 20:12,这对于我们选择的地址会产生值0x56( (0x123456000 >> 12) & 0x1ff)。乘以 TTE 大小,添加到转换表地址,我们知道给我们的 TTE0x800040000写入地址0x8000372b0。
63 21 20 12 11 0
+------------------------------------------------------------+-------------+
| upper bits | L3 index | page offset |
+------------------------------------------------------------+-------------+
Run Code Online (Sandbox Code Playgroud)
现在,您重复相同的过程来获得0x800037000,这次来自 2 级转换表中的 TTE。您再次从虚拟地址中取出 9 位来索引该表,这次的值为0x11a( (0x123456000 >> 21) & 0x1ff)。
63 30 29 21 20 12 11 0
+------------------------------------------------------------+-------------+
| upper bits | L2 index | L3 index | page offset |
+------------------------------------------------------------+-------------+
Run Code Online (Sandbox Code Playgroud)
再次对于 1 级转换表:
63 40 39 30 29 21 20 12 11 0
+------------------------------------------------------------+-------------+
| upper bits | L1 index | L2 index | L3 index | page offset |
+------------------------------------------------------------+-------------+
Run Code Online (Sandbox Code Playgroud)
此时,您已经使用了虚拟地址的全部 39 位,所以您已经完成了。如果您有 40 位寻址,则需要遍历另一个 L0 表。如果您有 38 位寻址,那么我们仍然会采用 L1 表,但它只会跨越 0x800 字节而不是 0x1000。
但是L1翻译表是从哪里来的呢?嗯,从TTBR0_EL1. 它的物理地址就在那里,作为地址转换的根。
现在,要执行实际的翻译,您必须反向执行整个过程。您从 的转换表开始TTBR0_EL1,但您不知道它是 L0、L1 等。要弄清楚这一点,您必须查看转换粒度和用于虚拟寻址的位数。对于 4KiB 页面,有 12 位页面偏移量和每个级别的转换表 9 位,因此对于 39 位,您正在查看 L1 表。然后,您将虚拟地址的第 39:30 位进行索引,从而得到 L2 表的地址。冲洗并重复 L2 的位 29:21 和 L3 的位 20:12,您就到达了目标页的物理地址。
| 归档时间: |
|
| 查看次数: |
1347 次 |
| 最近记录: |