在虚拟机上调试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确定当前堆栈指针: