我正在组装中构建32位操作系统
我已经设置了IDT,我通过int
指令处理程序interruptus。
如何启用syscall
和sysenter
说明以及如何处理/返回?
是的,英特尔处理器32位不支持syscall
指令,所以我不能使用它?
sysret
指令不安全吗?
在某处有一个教程吗?
编辑:我的主要问题是如何启用syscall
和sysenter
说明! (没有重复)
答案 0 :(得分:7)
请参阅OSdev wiki for details on sysenter
,其中包括有关如何避免安全/安全问题的说明。另请参阅Intel / AMD手册。它们涉及操作系统开发人员需要的许多细节。有关链接,请参阅x86代码Wiki。
各种系统调用说明概述:
int
:永远可用(8086)far call
)。有关该信息和陷阱的详细信息,请参阅OSdev链接。 sysenter
:( http://wiki.osdev.org/Sysenter)在x86-64存在之前由英特尔推出,不久之后(多年前)被AMD采用。适用于所有现代x86 CPU。非常简约的设计,需要用户空间合作才能使内核能够返回,因为它不会将EIP,ESP或EFLAGS保存在任何地方。
Linux支持32位和64位内核,仅用于32位进程的系统调用。 IDK,如果你可以设计一个内核,它也可以用于64位系统调用。 (我知道这不是问题,但它是相关的。)
使用sysenter
需要用户空间合作来提供返回地址并保存自己的ESP和EFLAGS。在Linux中,内核导出一段代码页面,该页面具有此舞蹈的用户空间。用户空间预计为call
此代码,而不是直接使用sysenter
,但您可以随意设计自己的操作系统。如果你在其他地方没有找到一个例子,那么看看这个舞蹈双方的Linux代码可能会有用。
syscall
:随处可用,因为英特尔与其他AMD64一起实施了它。精心设计的界面在进入内核之前屏蔽RFLAGS(带有可配置的掩码),因此您可以避免竞争窗口(如果您必须使用cli
手动禁用中断)。与swapgs
一起使用,以便内核可以访问其堆栈等等。
在主流x86操作系统(如Linux)上,syscall
是进行64位系统调用的唯一方法。
syscall
:与长模式syscall
完全不同的指令,仅适用于AMD CPU 。内核端接口对于32位内核(传统模式)与运行32位用户空间(compat模式)的64位内核不同。
Linux内核对它有一些有用的评论:
entry_64_compat.S
32-bit SYSCALL entry(进入64位内核的32位syscall
入口点)/* ... * - Most programmers do not directly target AMD CPUs, and the 32-bit * SYSCALL instruction does not exist on Intel CPUs. Even on AMD * CPUs, Linux disables the SYSCALL instruction on 32-bit kernels * because the SYSCALL instruction in legacy/native 32-bit mode (as * opposed to compat mode) is sufficiently poorly designed as to be * essentially unusable.
也许玩具操作系统可以使用它而不用担心任何问题使它不适合Linux,IDK。但除非你只是好奇,否则不要浪费你的时间。 OTOH,如果你对OS&amp ;;感兴趣CPU设计,找出ISA设计的错误可能会很有趣。
BTW,当AMD设计AMD64时,他们从amd64邮件列表上的Linux内核开发者那里获得了一些反馈,这些反馈改进了64位syscall
的设计(以配置掩盖RFLAGS),因为他们的初始设计本来是对Linux有问题。指向已归档邮件列表帖子的链接in this answer。
建议:对您的32位内核使用sysenter
。它应该可以在任何地方使用,包括多年来在AMD CPU上使用。如果你想添加第二个兼容性ABI,那些不支持它的古老CPU可以使用int 0x80
ABI(或你为你的OS选择的任何数字)。
Linux内核入口点得到很好的评论,写得相当可读。在编写What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?时,我很容易理解使用syscall
(本机64位系统调用)或int 0x80
在64位内核的入口点发生了什么。或sysenter
(32位系统调用,通常来自compat模式,但{64}进程支持int 0x80
。但它仍然调用32位ABI!)有一堆复杂的在启用各种跟踪/调试的情况下继续进行,但其他部分相当容易理解。请参阅该答案,了解Linux系统调用处理内部的一些内容。
在arch/x86/entry
中,这些是感兴趣的主要文件:
entry_32.S
:用于从用户空间进入的32位内核代码。 (传统模式)entry_64_compat.S
:用于从32位用户空间进入的64位内核代码(compat模式 - >长模式)。entry_64.S
:64位内核代码,用于从64位用户空间进入(长模式 - >长模式)。您应该能够在sysenter
舞的用户空间一侧找到Linux的VDSO代码,该代码将内核传递给返回用户空间所需的值。 (What is better "int 0x80" or "syscall"?)。相关:What is better "int 0x80" or "syscall"?和The Definitive Guide to Linux System Calls将提供有关Linux所做设计选择的一些有用信息。
sysret
指令是否安全?
英特尔和AMD在返回64位用户空间时都有非规范RIP的单独错误。例如在英特尔,Linux's entry_64.S
以这种方式描述:
/* * On Intel CPUs, SYSRET with non-canonical RCX/RIP will #GP * in kernel space. This essentially lets the user take over * the kernel, since userspace controls RSP.
如果ptrace
系统调用(例如,由调试程序生成)将进程的RIP
的已保存值更改为非规范地址,则可能发生这种情况。
Linux检查它是否可以使用sysret
,如果不使用其iret
返回路径。 (sysret
路径足够快,值得做额外的工作来检查它是否安全。
请注意,如果系统调用阻塞/休眠,那么"主副本"用户空间的整数寄存器状态位于其内核堆栈上,系统调用入口点将其推送到内核堆栈。 (在Linux中。其他设计是可能的!)但是,无论如何,这就是为什么它可能最终得到奇怪的保存状态,即用户空间无法运行syscall
(因为它会已将jmp
发送到非规范地址,或saved_rcx != saved_RIP
(64位syscall
设置RCX = RIP,R11 = RFLAGS(屏蔽前),因此它会破坏RCX和R11但允许内核恢复RIP和RFLAGS。)
我不知道32位syscall
是如何工作的,抱歉我在这里找到了主题。但是我怀疑你可能已经读过关于sysret
不安全的内容是在谈论64位内核。
IDK,如果在32位内核sysret
或64位内核sysret
中有任何类似的错误,那就是compat-mode。
答案 1 :(得分:2)
syscall
不能在x86上使用,只能在x86_64上使用(至少可以移植)。话虽如此,在x86_64上,通过将用户模式和内核模式的正确CS选择器加载到IA32_STAR
模型特定的寄存器中,然后将syscall
模型专用寄存器的地址加载到{{{ 1}}被执行到IA32_LSTAR
。您还需要仔细处理这些指令的执行上下文,因为它们会破坏某些寄存器等。
我建议阅读手册 - 英特尔手册本身和AMD64手册的第2卷都是很好的起点。
答案 2 :(得分:1)
英特尔处理器32位不支持系统调用指令,所以我不能使用它?
至少维基百科说这个。
更重要的是:系统调用似乎甚至不被任何32位CPU(甚至不是AMD)支持,而只支持32位模式的64位AMD CPU。
我正在构建一个32位操作系统。
那你为什么要使用系统调用或sysenter?
几乎所有32位x86操作系统都使用中断(例如Linux)或调用门(例如Solaris)来进入内核......