如何调试aarch64翻译错误?

max*_*xbc 6 kernel arm mmu arm64 armv8

我在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
Run Code Online (Sandbox Code Playgroud)

检查其他寄存器,我有:

TTBR1_EL1=0x82000000
TTBR1_EL1[2]=0x0000000082003003
Run Code Online (Sandbox Code Playgroud)

因此,基于《 ARMv8的ARM体系结构参考手册》(ARMv8-A配置文件):

  • ESR(异常征兆寄存器)转换为:D7-1933平方英尺上的异常类= 100101(数据异常中止而没有更改异常级别);WnR = 1(故障指令为写操作);D7-1958 页的DFSC = 0b000100(0级转换错误);
  • FAR_EL1是故障地址;它指示使用了TTBR1_EL1(因为高位全为1)。VA的前9位为0b000000010,表示表中使用了条目2;
  • 该表中的条目2指示位于物理地址0x82003000的下一级表(低位0b11)。

因此,转换在0级失败,而在0级应该失败。

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

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

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

我尝试进行操作tlbidsb sy说明,其后isb没有效果。此时只有一个CPU正在运行,因此缓存应该不是问题-编写指令和MMU可以访问相同的缓存(但我将在下一步进行测试)。

max*_*xbc 5

我忽略了单个内核中的缓存问题。问题是,在打开MMU之后,CPU和走台装置的内存视图不同。《 ARMv8 Cortex-A编程指南》指出,在修改表之后,必须清除/使缓存无效以达到统一点(单个内核的相同视图)。

有两种可能性可以解释这种现象(我还不完全了解缓存的工作原理):

  1. 第一种可能性: MMU在其内部步移缓存中没有所需的地址。
    在这种情况下,当更新常规数据并将其提供给其他内核的L1时,dsb指令仅等待所有内核具有同步状态(由于一致性网络):其他内核将知道必须更新该行,以及何时他们尝试访问它,将其更新为L2或从以前的内核的L1迁移到其L1。
    对于MMU(没有一致性参与),这不会发生,因此它仍然看到L2中的旧值。
    但是,如果是这种情况,则在打开MMU之前应该发生同样的事情(因为缓存是在之前激活的),除非在激活MMU之前将所有内存视为L1不可缓存(这是可能的,我我必须再次检查)。
    解决该问题的最小方法可能是更改表页面的缓存策略,但仍然需要维护缓存以从MMU中清除可能的旧值。
  2. 第二种可能性:在所有经过测试的情况下,MMU在其内部步移缓存中已经具有故障地址,该地址与数据L1或L2不协调。
    在这种情况下,只有显式的无效对象才能从MMU缓存中弹出旧行。在打开MMU之前,缓存不包含任何内容,并且永远不会获得旧值(0),而只会获得新值。
    我仍然认为这种情况不太可能,因为我测试了很多情况,有时还测试了预先映射的内存(例如,级别1表中的条目0)和新映射的内存(例如,同一级别1表中的条目128)之间的偏移量大于缓存行大小(在这种情况下为1024字节,大于任何缓存行大小)。

因此,我仍然不确定到底是什么引起了该问题,但是清除/使所有更新的地址无效是可行的。