如何在自由支持引导代码中启用SSE?

8

这个问题最初是关于CVTSI2SD指令以及我认为它在Pentium M CPU上不起作用,但实际上是因为我使用了自定义操作系统,需要手动启用SSE。

我有一个Pentium M CPU和一个自定义操作系统,到目前为止没有使用过任何SSE指令,但现在我需要使用它们。

尝试执行任何SSE指令会导致中断6,非法操作码(在Linux中会导致SIGILL,但这不是Linux),在Intel architectures software developer's manual(从现在开始我将称之为IASDM)中也称为#UD - Invalid Opcode (UnDefined Opcode)

编辑:Peter Cordes实际上确定了正确的原因,并指出了解决方案,我在下面总结:

如果您正在运行不支持在上下文切换时保存XMM寄存器的古老操作系统,则机器控制寄存器中的SSE启用位将不会设置。

确实,IASDM提到了这一点:

如果操作系统未提供足够的SSE系统级支持,则执行SSE或SSE2指令也会生成#UD。

Peter Cordes指向了SSE OSDev wiki,其中描述了如何通过写入CR0CR4控制寄存器来启用SSE:

clear the CR0.EM bit (bit 2) [ CR0 &= ~(1 << 2) ]
set the CR0.MP bit (bit 1) [ CR0 |= (1 << 1) ]
set the CR4.OSFXSR bit (bit 9) [ CR4 |= (1 << 9) ]
set the CR4.OSXMMEXCPT bit (bit 10) [ CR4 |= (1 << 10) ]

请注意,在写入这些寄存器之前,如果您处于保护模式,则需要处于特权级0。此问题的答案解释了如何测试它:(此链接)如果在保护模式下,也就是当CR0中的位0(PE)设置为1时,然后可以从CS选择器中测试位0和位1,它们应该都是0。
最后,自定义操作系统必须在上下文切换期间正确处理XMM寄存器,必要时保存并恢复它们。

3
然而,Pentium M无法识别CVTSI2SD。来源? - harold
1
导致崩溃的原因是什么——SIGILL(“非法指令”)还是其他原因? - Paul R
1
请您能否在GDB下运行该应用程序,并在崩溃现场输出“(gdb) disas /r”的错误和输出信息? - Iwillnotexist Idonotexist
1
你确定你实际上没有一台Pentium III-M电脑吗? - Iwillnotexist Idonotexist
1
我更新了我的回答并附上了一个链接。是的,这是一件事情。在SSE刚出现时,它在旧文档中引起了更多的讨论。引入必须在上下文切换时保存的新架构状态是一件大事。假定对于256b ymm寄存器也有类似的位,因为只保存/恢复低128位的操作系统将是一个大问题。 - Peter Cordes
显示剩余10条评论
2个回答

8
如果您正在运行一个不支持在上下文切换时保存XMM寄存器的古老或自定义操作系统,则它不会设置机器控制寄存器中的SSE启用位。在这种情况下,所有涉及xmm寄存器的指令都将出现故障。
需要一点时间来找到,但http://wiki.osdev.org/SSE解释了如何更改CR0和CR4以允许在裸金属上运行SSE指令而不出现#UD错误。

我对你旧版本问题的第一个想法是,你可能使用了-mavx-march=sandybridge或类似的编译选项,导致编译器发出VEX编码版本的所有内容。

CVTSI2SD   xmm1, xmm2/m32         ; SSE2
VCVTSI2SD  xmm1, xmm2, xmm3/m32   ; AVX

请参阅https://stackoverflow.com/tags/x86/info以获取链接,包括英特尔指令集参考手册。
大多数实际的内核都使用选项构建,以防止编译器自行使用SSE或x87指令,例如gcc -mgeneral-regs-only。 在旧版GCC中,-mno-sse -mno-mmx并避免任何使用floatdouble类型来避免x87。 这样内核只需要在中断和系统调用时保存/恢复整数寄存器,在完整上下文切换到不同的用户空间任务时才执行SIMD / FP状态。 在存在并使用该选项之前,使用double的Linux内核代码可能会无声地破坏用户空间状态
如果您有一个不尝试在用户空间任务之间进行上下文切换的独立程序,请放心让编译器使用SSE / AVX。

相关:哪些版本的Windows支持/需要哪些CPU多媒体扩展?(如何检查SSE或AVX是否完全可用?)提供了一些关于如何检查AVX和AVX512支持的详细信息(这些扩展还引入了新的架构状态,因此操作系统必须设置一个位或硬件将会出现故障)。它从另一个角度来看待问题,但链接应该指示如何激活/禁用AVX支持。


我正在使用一個舊的GCC,它似乎沒有那個架構。我已經查看了組合語言並嘗試通過 asm() 直接插入指令,因此很少可能是這種情況。 - anol
1
这是-mavx,糟糕。看看反汇编代码。如果是vcvt...,并且你的程序出现SIGILL错误,则可能是AVX问题。否则,你可能会收到SIGSEGV而不是SIGILL。如果是SIGILL,那么就有一些奇怪的事情发生了,你应该在gdb下运行它,以便它停在导致错误的确切指令上。 - Peter Cordes
我尝试编译一些包含特定的CVTSI2SD汇编代码,以确保它是罪魁祸首,并且我得到了中断6。但是,使用CVTSI2SS也发生了同样的情况,因此这实际上是一个与SSE相关的问题。包含对%xmmN寄存器的引用的指令无法正常工作。 - anol
在https://dev59.com/QW025IYBdhLWcg3wQDhb上有一个关于检测操作系统支持的答案。但是我记得,英特尔建议检测操作系统支持的方法是尝试运行SSE指令,并查看是否出现#UD错误。这非常糟糕,特别是对于库或其他东西来说,因为这会强制调用程序处理SIGILL!我记得,操作系统必须设置的机器控制寄存器甚至不能被非特权代码读取。 - Peter Cordes
1
@anol:我进行了一次编辑,就像我所建议的那样。希望这能吸引一些来自操作系统方面的专家帮助。当您尝试使用“mov cr”指令时,您可能需要提及CPU处于什么状态(实模式?32位保护模式?)。 - Peter Cordes
显示剩余5条评论

2

我建议您在遇到这类问题时查阅英特尔手册

手册清楚地说明CVTSI2SD是SSE2指令。


CPUID报告支持的指令集是什么?如果您使用Linux,则可以使用“less /proc/cpuinfo | grep flags”命令。 - Paul R
1
@anol 在手册中,你需要阅读的部分是_卷3,章节13.1 提供SSE扩展的操作系统支持_。该部分具有非常清晰的描述和列出了必须为SSE支持所做的内容清单。迈克尔,你应该在你的回答中明确提及这个部分的参考。 - Iwillnotexist Idonotexist

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接