【问题标题】:OsDev syscall/sysret and sysenter/sysexit instructions enablingOsDev syscall/sysret 和 sysenter/sysexit 指令启用
【发布时间】:2018-02-11 19:32:26
【问题描述】:

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

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

编辑:我的主要问题是如何启用syscallsysenter 指令! (无重复)

【问题讨论】:

  • 您是否尝试在手册或在线资源中查找此内容?你发现了什么?
  • @BoPersson 问题似乎不相关:另一个问题只针对Linux;本题关注自写内核。
  • @Martin - 它回答了这个问题英特尔处理器不支持 32 位系统调用指令是真的

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


【解决方案1】:

英特尔处理器不支持 32 位系统调用指令,所以我不能使用它,这是真的吗?

至少维基百科是这么说的。

更重要的是:似乎任何 32 位 CPU(甚至 AMD 都不支持)系统调用,但仅在 64 位 AMD CPU 的 32 位模式下支持。

我正在组装一个 32 位操作系统。

那么为什么要使用 syscall 或 sysenter?

几乎所有 32 位 x86 操作系统都使用中断(例如 Linux)或调用门(例如 Solaris)进入内核...

【讨论】:

  • syscall 得到大量 32 位 AMD cpu 的支持,例如1998 年的 K6-2。使用 syscall/sysenter,因为它们更快。 Linux 已经使用它们十多年了,即使在 32 位模式下也是如此。中断只是作为遗留后备保留。
【解决方案2】:

syscall 不能在 x86 上使用,只能在 x86_64 上使用(至少可移植)。话虽如此,在 x86_64 上,通过将用户模式和内核模式的正确 CS 选择器加载到 IA32_STAR 特定于模型的寄存器中来启用指令,然后在 syscall 为执行到IA32_LSTAR。您还需要仔细处理这些指令的执行上下文,因为它们会破坏一些寄存器等。

我建议阅读手册 - 英特尔手册本身和 AMD64 手册的第 2 卷都是不错的起点。

【讨论】:

    【解决方案3】:

    请参阅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 出现之前由 Intel 引入,不久之后(多年前)被 AMD 采用。适用于所有现代 x86 CPU。非常简约的设计,需要用户空间合作才能让内核返回,因为它不会在任何地方保存 EIP、ESP 或 EFLAGS

      Linux 在 32 位和 64 位内核中仅支持来自 32 位进程的系统调用。 IDK,如果您可以设计一个将其用于 64 位系统调用的内核 / 代替。 (我知道这不是问题,但它是相关的。)

      使用sysenter需要用户空间合作提供返回地址并保存自己的ESP和EFLAGS。在 Linux 中,内核导出一页代码,其中包含此舞蹈的用户空间方面。用户空间应该call 这个代码而不是直接使用sysenter,但是你可以随意设计你的操作系统。如果您在其他地方没有找到示例,查看 Linux 的代码可能会有所帮助。

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

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

    • syscall 来自 32 位用户空间:与长模式 syscall 完全不同的指令,仅适用于 AMD CPU。 32 位内核(传统模式)与运行 32 位用户空间(兼容模式)的 64 位内核的内核端接口不同。

      Linux 内核上有一些有用的 cmets:

    entry_64_compat.S 32-bit SYSCALL entry(32 位 syscall 进入 64 位内核的入口点)

     /* ...
     *  - 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,如果您对操作系统和 CPU 设计感兴趣,那么找出 ISA 设计的问题可能会很有趣。

    顺便说一句,当 AMD 设计 AMD64 时,他们在 amd64 邮件列表上从 Linux 内核开发人员那里得到了一些反馈,这些反馈改进了 64 位 syscall 的设计(以可配置地屏蔽 RFLAGS),因为他们的初始设计对于Linux。那些归档邮件列表帖子的链接in this answer


    建议:将sysenter 用于您的 32 位内核。它应该可以在任何地方使用,包括多年来在 AMD CPU 上的使用。如果您想添加第二个兼容性 ABI,不支持它的古代 CPU 可以使用 int 0x80 ABI(或您为操作系统选择的任何编号)。

    Linux 内核入口点有很好的注释,并且写得相当易读。在编写What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code? 时,我很容易使用syscall(本机64 位系统调用)或int 0x80sysenter(32 -bit 系统调用,通常来自兼容模式,但 int 0x80 支持 64 位进程。但它仍然调用 32 位 ABI!)如果启用各种跟踪/调试,则会发生很多复杂的事情,但其他部分相当容易理解。请参阅该答案,了解 Linux 的一些系统调用处理内部结构。

    arch/x86/entry 中,这些是感兴趣的主要文件:

    • entry_32.S:用于从用户空间进入的 32 位内核代码。 (传统模式)
    • entry_64_compat.S:用于从 32 位用户空间进入的 64 位内核代码(兼容模式 -> 长模式)。
    • 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 指令不安全是真的吗?

    返回 64 位用户空间时,英特尔和 AMD 都存在与非规范 RIP 不同的错误。例如在 Intel 上,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 位内核。

    如果 32 位内核 sysret 或 64 位内核 sysret-to-compat-mode 中存在任何类似错误,请 IDK。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-08-09
      • 2010-12-11
      • 2015-06-27
      • 2012-05-21
      • 1970-01-01
      • 1970-01-01
      • 2023-03-12
      • 2021-09-08
      相关资源
      最近更新 更多