启用

时间:2017-09-03 09:52:29

标签: assembly system-calls interrupt-handling osdev protected-mode

我正在组装中构建32位操作系统 我已经设置了IDT,我通过int指令处理程序interruptus。

如何启用syscallsysenter说明以及如何处理/返回?
是的,英特尔处理器32位不支持syscall指令,所以我不能使用它? sysret指令不安全吗? 在某处有一个教程吗?

编辑:我的主要问题是如何启用syscallsysenter说明! (没有重复)

3 个答案:

答案 0 :(得分:7)

请参阅OSdev wiki for details on sysenter,其中包括有关如何避免安全/安全问题的说明。另请参阅Intel / AMD手册。它们涉及操作系统开发人员需要的许多细节。有关链接,请参阅代码Wiki。

各种系统调用说明概述:

  • int :永远可用(8086)
  • 陷阱执行无效指令,显然是the fastest way to enter the kernel on 80386。 (但事实并非如此)。
  • call gate (即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代码可能会有用。

  • 来自64位用户空间的
  • syscall :随处可用,因为英特尔与其他AMD64一起实施了它。精心设计的界面在进入内核之前屏蔽RFLAGS(带有可配置的掩码),因此您可以避免竞争窗口(如果您必须使用cli手动禁用中断)。与swapgs一起使用,以便内核可以访问其堆栈等等。

    在主流x86操作系统(如Linux)上,syscall是进行64位系统调用的唯一方法。

  • 来自32位用户空间的
  • 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)来进入内核......