对 arm64 中的堆栈指针感到困惑,当内核在 EL2 中为 运行 时,sp 与 SP_EL2 寄存器不同

confused about stack pointer in arm64, sp is not same as SP_EL2 register when kernel is running at EL2

在虚拟机上调试linux 启动时,我发现堆栈指针寄存器很奇怪。我在 start_kernel 函数的早期处于 printk 例程中。 (串口未连接,此时数据只是写入log buffer)。 当我在函数 vsnprintf (linux-5.4.21) 中,当我检查堆栈指针时,它是这样的(这是在 arm64 虚拟机中看到的 运行在 qemu 上)。

(gdb) info reg sp
sp             0xffffffc0105d3bf0  0xffffffc0105d3bf0
(gdb) info reg SP_EL2
SP_EL2         0x407cd70           67620208
(gdb) info reg SP_EL1
SP_EL1         0x0                 0
(gdb) info reg SP_EL0
SP_EL0         0xffffffc0105d9f00  -274603335936

在我的例子中,内核以 EL2 运行,所以我认为 sp 值将等于 SP_EL2,但事实并非如此。相反,SP_EL2 仍然设置为 u-boot 程序设置的物理地址(u-boot 程序 运行 在 0x4000000 ~ 范围内, linux 内核是运行 在 0x80000000 ~ 范围内,只有 8MB)。
我知道 PS_EL0 包含 init_task 当内核在 EL2 为 运行 时的地址。但是为什么 SP_EL2 没有被使用,这个真正的 sp 值是什么,它在我执行函数调用时递减? 添加:我检查过 SPSel 寄存器设置为 1。所以 SP_EL2 被使用。所以我的问题是:为什么 SP_EL2 没有被使用?实际使用的 sp 寄存器是什么?

https://developer.arm.com/documentation/ddi0595/2021-06/AArch64-Registers/SP-EL2--Stack-Pointer--EL2-

保存与 EL2 关联的堆栈指针。在 EL2 执行时,SPSel 的值。 SP确定当前栈指针:

这只是 QEMU 将 SP_ELx 系统寄存器显示到 gdbstub 中的一个小错误:您应该忽略来宾当前所处异常级别的 SP_ELx 值,并查看改为普通的 sp 寄存器。

该错误的原因是在体系结构上,如果您在来宾 CPU 上编写代码 运行,则您无法访问 SP_ELx 系统寄存器以获得等于的异常级别或高于您 运行 所在的位置。所以 EL2 代码本身不能读取 SP_EL2——只有 EL3 可以使用 SP_EL2 系统寄存器。 EL2 代码可以查看自己的堆栈指针的唯一方法是使用 SP。 QEMU 利用这一点来避免必须做额外的工作来确保与当前 SP 相同的 SP_ELx 系统寄存器值与真实 SP 寄存器同步。但是 gdbstub 访问器让你作为 gdb 用户读取系统寄存器,当前 运行 代码无法访问。大多数情况下都可以正常工作,但偶尔您会得到无意义的结果,例如这个。