7

我正在用 armv8 (aarch64) 编写一个简单的内核。

MMU 配置:

  • 48 VA 位 (T1SZ=64-48=16)
  • 4K 页面大小
  • 所有物理 RAM 平面映射到内核虚拟内存(在 TTBR1_EL1 上)(MMU 在 TTBR0_EL1=0 时处于活动状态,所以我只使用 0xffff<addr> 中的地址,所有平面映射到物理内存)

我正在将一个新的地址空间(从 1<<40 开始)映射到某个空闲的物理区域。当我尝试访问地址 1<<40 时,出现异常(类型为“使用 SP1 的 EL1,同步”):

ESR_EL1=0x96000044
FAR_EL1=0xffff010000000000

检查其他寄存器,我有:

TTBR1_EL1=0x82000000
TTBR1_EL1[2]=0x0000000082003003

因此,基于 ARM Architecture Reference Manual for ARMv8(ARMv8-A profile):

  • ESR(异常综合症寄存器)转换为: Exception Class=100101(Data abort without a change in exception level) on page D7-1933 sq.;WnR=1(错误指令是写);第 D7-1958 页上的DFSC=0b000100(级别 0 的转换错误);
  • FAR_EL1 是故障地址;它表示使用了 TTBR1_EL1(因为高位全为 1)。VA 前 9 位为 0b000000010,表示表中使用了条目 2;
  • 表中的条目 2 表示物理地址 0x82003000 处的下一级表(低位 0b11)。

因此,翻译在不应该的级别 0 处失败。

我的问题是:我做错了什么吗?我是否遗漏了一些可能导致翻译错误的信息?而且,更一般地说,如何调试翻译错误?

更新:
当我在启用 MMU 之前写入表时,一切正常。
每当我在启用 MMU 之后(通过平面映射表区域)写入表时,映射永远不会起作用。我想知道为什么会这样。

我还尝试手动写入选定的表(以消除我的映射函数的任何副作用):相同的结果(在 MMU 开启之前完成写入时,它可以工作;之后,它会失败)。

我尝试了做tlbidsb sy指示,然后是isb,没有效果。此时只有一个 CPU 正在运行,因此缓存应该不是问题 - 写入指令和 MMU 与相同的缓存通信(但我将在接下来对其进行测试)。

4

1 回答 1

5

我忽略了单个核心中的缓存问题。问题是,打开 MMU 后,CPU 和 table walk 单元没有相同的内存视图。ARMv8 Cortex-A 编程指南指出,修改表后,必须将缓存清理/失效到统一点(单个内核的相同视图)。

两种可能性可以解释这种行为(我还不完全理解缓存是如何工作的):

  1. 第一种可能性: MMU 在其内部遍历缓存中没有所需的地址。
    在这种情况下,当更新常规数据并使其可用于其他内核的 L1 时,该dsb指令只是等待所有内核具有同步状态(感谢一致性网络):其他内核将知道该线路必须更新,并且何时他们尝试访问它,它被更新到 L2 或从以前的核心的 L1 迁移到他们的 L1。
    MMU 不会发生这种情况(没有一致性参与),因此它仍然看到 L2 中的旧值。
    但是,如果是这种情况,在打开 MMU 之前应该会发生同样的事情(因为之前已经激活了缓存),除非在激活 MMU 之前所有内存都被认为是 L1 不可缓存的(这是可能的,我必须仔细检查)。
    解决问题的最小方法可能是更改表页的缓存策略,但仍然需要缓存维护以从 MMU 中清除可能的旧值。
  2. 第二种可能性:在所有测试的情况下,MMU 在其内部遍历缓存中已经有错误地址,这与数据 L1 或 L2 不一致。
    在这种情况下,只有显式无效才能从 MMU 缓存中弹出旧行。在打开 MMU 之前,缓存中什么都没有,并且永远不会获取旧值 (0),只会获取新值。
    我仍然认为这种情况不太可能,因为我测试了很多情况,有时是预先映射的内存(例如,一级表中的条目 0)和新映射的内存(例如,同一个一级表中的条目 128)之间的偏移量大于缓存线大小(在本例中为 1024 字节,大于任何缓存线大小)。

所以,我仍然不确定究竟是什么导致了这个问题,但是清理/无效所有更新的地址是可行的。

于 2017-08-19T10:16:28.433 回答