如何调试 aarch64 翻译错误?
How to debug an aarch64 translation fault?
我正在用 armv8 (aarch64) 编写一个简单的内核。
MMU 配置:
- 48 VA 位 (T1SZ=64-48=16)
- 4K 页面大小
- 所有物理 RAM 平面映射到内核虚拟内存(TTBR1_EL1)
(MMU 在 TTBR0_EL1=0 时处于活动状态,所以我只使用 0xffff
中的地址,所有平面映射到物理内存)
我正在将一个新地址 space(从 1<<40 开始)映射到一些可用的物理区域。当我尝试访问地址 1<<40 时,出现异常("EL1 using SP1, synchronous" 类型):
ESR_EL1=0x96000044
FAR_EL1=0xffff010000000000
检查其他寄存器,我有:
TTBR1_EL1=0x82000000
TTBR1_EL1[2]=0x0000000082003003
因此,基于 ARMv8 的 ARM 体系结构参考手册(ARMv8-A 配置文件):
- ESR(异常综合症寄存器)转化为:异常Class=100101(数据异常中止,异常级别)在页面D7-1933 sq.; WnR=1(错误指令是写); DFSC=0b000100( 级别 0 的转换错误 )(第 D7-1958 页);
- FAR_EL1 是故障地址;它表示使用了 TTBR1_EL1(因为高位都是 1)。 VA 的前 9 位是 0b000000010,表示条目 2 在 table ;
中使用
- table中的条目 2 表示物理地址 0x82003000 的下一级 table(低位 0b11)。
因此,翻译在 0 级失败,而它不应该失败。
我的问题是:我做错了什么吗?我是否遗漏了一些可能导致翻译错误的信息?而且,更一般地说,如何调试翻译错误?
更新:
当我在启用 MMU 之前写入 tables 时一切正常。
每当我在启用 MMU 后写入 tables(通过平面映射 table 区域)时,映射永远不会工作。我想知道为什么会这样。
我还尝试手动写入选定的 tables(以消除我的 mmapping 函数的任何副作用):相同的结果(在 MMU 打开之前完成写入时,它有效;之后,它失败).
我尝试执行 tlbi
和 dsb sy
指令,然后执行 isb
,但没有效果。目前只有一个 CPU 是 运行,所以缓存应该不是问题 - 写入指令和 MMU 与相同的缓存通信(但我接下来会测试它)。
我忽略了单核中的缓存问题。问题是,在打开 MMU 后,CPU 和 table 步行单元没有相同的内存视图。 ARMv8 Cortex-A 编程指南指出,在修改 tables.
后,缓存必须 cleaned/invalidated 到统一点(单核的相同视图)
有两种可能性可以解释这种行为(我还没有完全理解缓存是如何工作的):
- 第一种可能性: MMU 在其内部遍历缓存中没有所需的地址。
在这种情况下,当更新常规数据并使其可用于其他内核的 L1 时,dsb
指令只是等待所有内核都具有同步状态(感谢一致性网络):其他内核将知道该行必须更新,当他们尝试访问它时,它会更新到 L2 或从以前核心的 L1 迁移到他们的 L1。
MMU(没有一致性参与)不会发生这种情况,所以它仍然看到 L2 中的旧值。
然而,如果是这种情况,同样的事情应该发生在 MMU 被打开之前(因为缓存在之前被激活),除非所有内存在 MMU 被激活之前被认为是 L1 不可缓存的(这是可能的,我'我必须仔细检查一下)。
解决该问题的一种最小方法可能是更改 table 页面的缓存策略,但仍然需要缓存维护以清除 MMU 中可能的旧值。
- 第二种可能性:在所有测试的情况下,MMU在其内部步行缓存中已经有故障地址,与数据L1或L2不一致。
在这种情况下,只有显式无效才能从 MMU 缓存中弹出旧行。在打开MMU之前,缓存中什么都没有,永远不会得到旧值(0),只有新值。
我仍然认为这种情况不太可能,因为我测试了很多情况,有时会出现预先映射的内存(例如,级别 1 table 中的条目 0)和新映射的内存(例如,相同的条目 128)之间的偏移级别 1 table) 大于缓存行大小(在本例中为 1024 字节,大于任何缓存行大小)。
所以,我仍然不确定到底是什么导致了这个问题,但是 cleaning/invalidating 所有更新的地址都有效。
我正在用 armv8 (aarch64) 编写一个简单的内核。
MMU 配置:
- 48 VA 位 (T1SZ=64-48=16)
- 4K 页面大小
- 所有物理 RAM 平面映射到内核虚拟内存(TTBR1_EL1)
(MMU 在 TTBR0_EL1=0 时处于活动状态,所以我只使用 0xffff
中的地址,所有平面映射到物理内存)
我正在将一个新地址 space(从 1<<40 开始)映射到一些可用的物理区域。当我尝试访问地址 1<<40 时,出现异常("EL1 using SP1, synchronous" 类型):
ESR_EL1=0x96000044
FAR_EL1=0xffff010000000000
检查其他寄存器,我有:
TTBR1_EL1=0x82000000
TTBR1_EL1[2]=0x0000000082003003
因此,基于 ARMv8 的 ARM 体系结构参考手册(ARMv8-A 配置文件):
- ESR(异常综合症寄存器)转化为:异常Class=100101(数据异常中止,异常级别)在页面D7-1933 sq.; WnR=1(错误指令是写); DFSC=0b000100( 级别 0 的转换错误 )(第 D7-1958 页);
- FAR_EL1 是故障地址;它表示使用了 TTBR1_EL1(因为高位都是 1)。 VA 的前 9 位是 0b000000010,表示条目 2 在 table ; 中使用
- table中的条目 2 表示物理地址 0x82003000 的下一级 table(低位 0b11)。
因此,翻译在 0 级失败,而它不应该失败。
我的问题是:我做错了什么吗?我是否遗漏了一些可能导致翻译错误的信息?而且,更一般地说,如何调试翻译错误?
更新:
当我在启用 MMU 之前写入 tables 时一切正常。
每当我在启用 MMU 后写入 tables(通过平面映射 table 区域)时,映射永远不会工作。我想知道为什么会这样。
我还尝试手动写入选定的 tables(以消除我的 mmapping 函数的任何副作用):相同的结果(在 MMU 打开之前完成写入时,它有效;之后,它失败).
我尝试执行 tlbi
和 dsb sy
指令,然后执行 isb
,但没有效果。目前只有一个 CPU 是 运行,所以缓存应该不是问题 - 写入指令和 MMU 与相同的缓存通信(但我接下来会测试它)。
我忽略了单核中的缓存问题。问题是,在打开 MMU 后,CPU 和 table 步行单元没有相同的内存视图。 ARMv8 Cortex-A 编程指南指出,在修改 tables.
后,缓存必须 cleaned/invalidated 到统一点(单核的相同视图)有两种可能性可以解释这种行为(我还没有完全理解缓存是如何工作的):
- 第一种可能性: MMU 在其内部遍历缓存中没有所需的地址。
在这种情况下,当更新常规数据并使其可用于其他内核的 L1 时,dsb
指令只是等待所有内核都具有同步状态(感谢一致性网络):其他内核将知道该行必须更新,当他们尝试访问它时,它会更新到 L2 或从以前核心的 L1 迁移到他们的 L1。
MMU(没有一致性参与)不会发生这种情况,所以它仍然看到 L2 中的旧值。
然而,如果是这种情况,同样的事情应该发生在 MMU 被打开之前(因为缓存在之前被激活),除非所有内存在 MMU 被激活之前被认为是 L1 不可缓存的(这是可能的,我'我必须仔细检查一下)。
解决该问题的一种最小方法可能是更改 table 页面的缓存策略,但仍然需要缓存维护以清除 MMU 中可能的旧值。 - 第二种可能性:在所有测试的情况下,MMU在其内部步行缓存中已经有故障地址,与数据L1或L2不一致。
在这种情况下,只有显式无效才能从 MMU 缓存中弹出旧行。在打开MMU之前,缓存中什么都没有,永远不会得到旧值(0),只有新值。
我仍然认为这种情况不太可能,因为我测试了很多情况,有时会出现预先映射的内存(例如,级别 1 table 中的条目 0)和新映射的内存(例如,相同的条目 128)之间的偏移级别 1 table) 大于缓存行大小(在本例中为 1024 字节,大于任何缓存行大小)。
所以,我仍然不确定到底是什么导致了这个问题,但是 cleaning/invalidating 所有更新的地址都有效。