嗨,我是内核学习者,对swapgs有一些疑问。
根据AMD的文档,它交换了gs.base
隐藏寄存器和KernelGSBase MSR。
此外,以“ gs:XXXX”寻址的方式计算为“ gs.base + base +(比例*索引)+位移”
现在我的第一个问题是:
那我应该在哪里存储“基数”和“比例”?
此外,在我应该使用它的地方,我当前的项目将虚拟内存空间的上半部分用作内核,并且编译器通常不会添加“ gs:XXXX”作为寻址参考。
因此,特别是在哪里我应该使用swapgs
指令。
答案 0 :(得分:7)
没有隐藏的“基准和规模”,只有与常规寻址模式一起使用的隐藏的gs.base
。 (还有GS寄存器本身的隐藏值。这是一个选择器值,如果您实际上做了mov gs, eax
而不是仅通过修改GS基础,则它将作为GDT的索引 MSR或通过wrgsbase
。但这与完全gs:[base + index*scale]
寻址模式下的偏移量的索引部分无关)。
您在内核的swapgs
入口点处理程序中使用syscall
,然后像在线程本地存储中一样在某些负载和存储中使用GS段替代,因此,先前隐藏的{{1} }与您在每个加载或存储指令中使用的gs.base
寻址模式一起使用。例如类似[base + idx*scale]
来保存用户空间堆栈指针,mov [gs:0x10], rsp
来加载内核堆栈指针。
mov rsp, [gs:0x18]
之所以存在,是因为swapgs
不会将RSP更改为指向内核堆栈(并且不会在任何地方保存用户空间RSP)。因此,您需要某种线程本地(或实际上是内核本地)存储,以便每个内核都可以为在该内核上运行的任务获取正确的内核堆栈指针。隐藏的GS基础是该隐藏指针的存储,是一种在不破坏任何体系结构寄存器值的情况下使用它的方法。
您不能只使用常规的全局变量(绝对地址),因为该变量只能有一个所有内核都可以读取的值。您也没有任何备用寄存器(它们都包含宝贵的用户空间状态,稍后需要还原),也没有内核堆栈可将其压入。而且您不能使用用户空间RSP;在内核模式下使用用户空间RSP运行syscall
会导致用户空间在运行push
之前使RSP指向无效,从而使用户空间使内核崩溃。
最初设计x86-64时(早在2000年,比第一个芯片早几年),this mailing list message解释了syscall
的预期用途。 revised a day later是在操作系统开发人员注意到AMD如何规范它的问题之后,但原始电子邮件中包含一个仍然适用的简单示例:
示例用法
在内核入口点,操作系统可以使用SwapGS获取指向内核数据结构的指针,并同时保存 用户的GS基础。退出后,可以使用SwapGS还原用户的GS 基本:swapgs
您可能还想看看Linux内核如何在其 SystemCallEntryPoint:
SwapGS ; set up kernel pointer, save user's GS base
mov gs:[SavedUserRSP], rsp ; save user's stack pointer
mov rsp, gs:[KernelStackPtr] ; set up kernel stack
push rax ; now that we have a stack, save user's GPRs
mov rax, gs:[CPUnumber] ; get CPU number < or whatever >
. ; perform system service
.
SwapGS ; restore user's GS, save kernel pointer
入口点中使用它,最好是在较旧的内核中,在减轻Spectre / Meltdown风险使一切复杂化之前。例如Linux 4.12's entry_64.S
以syscall
开头,ENTRY(entry_SYSCALL_64)
与AMD的例子非常相似。
(另请参阅Why does Windows64 use a different calling convention from all other OSes on x86-64?,以解释从swapgs
开始的其他Linux内核入口点中发生的情况)。
Linux内核源代码中的一些注释指出,确保int 0x80
在内核执行的每个路径上仅运行一次是很不方便的。如果有两个操作码,一个用于“交换到用户gs”,另一个用于“交换到内核gs”,那么确保您不会意外地交换额外的时间会更容易。该错误将使下一个内核条目处于错误的位置。 (并且给用户空间错误的gs,但是在GNU / Linux swapgs
中用于线程本地存储。)
答案 1 :(得分:4)
如果您的内核不使用gs访问内核专用数据,则您无需使用此指令。它旨在供内核在从用户模式进入内核时使用,以允许内核保存应用程序的gsbase并加载自己的gsbase,而无需访问内存。然后,内核可以将应用程序状态保存在通过gs引用的数据结构中。
由于您显然已经构建了内核,因此可以在不使用gs的情况下保存来宾状态,因此您无需使用swapgs。您仍然可能希望在每个上下文开关以及应用程序的其余状态下保存和恢复fsbase和gsbase,以便每个用户进程可以拥有自己的值。