混淆了arm64中的堆栈指针,当内核在EL2上运行时,sp和sp_EL2寄存器不一样



在虚拟机上调试linux引导时,我发现堆栈指针寄存器很奇怪。在start_kernel函数的早期,我使用了printk例程。(串行端口未连接,因此此时数据只是写入日志缓冲区(。当我在函数vsnprintf(linux-5.4.21(中时,当我检查堆栈指针时,它是这样的(这是在qemu上运行的arm64虚拟机上看到的(。

(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((
我知道当内核在EL2上运行时,PS_EL0包含init_task的地址。但是,为什么SP_EL2没有被使用,这个真正的sp值是什么?当我向下调用函数时,它会递减?附加:我检查了SPSel寄存器是否设置为1。因此,正在使用SP_EL2。所以我的问题是:为什么没有使用SP_EL2?实际使用的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用户读取当前运行的代码无法访问的系统寄存器。大多数情况下,这还可以,但偶尔你会得到一些毫无意义的结果,比如这个。

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

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

最新更新