微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在长模式下使用 IRET 进入 CPL=3

如何解决在长模式下使用 IRET 进入 CPL=3

我正在创建一个手写操作系统,但在中断处理方面存在一些问题。 问题是当我尝试从中断返回到 CPL=3 时,我遇到了一般保护错误。 我在intel手册上查了一下,我的结论是在long模式下,不能用iret返回到不同的代码段。 所以我想出了一个解决方案,它使用 sysret 返回一个 CPL=3 代码,然后跳转用户代码。它似乎有效,但有一个小问题。有时中断会出现在 CPL=3 代码的中间。 我不能在 CPL=3 代码的末尾启用中断,因为它只能在 CPL=0 中使用。

我是否遗漏了关于愤怒指令的任何内容? 还是我使用了一些像远归这样的黑魔法?

这段代码可以工作,但过一段时间就会中断,因为在 CPL3 部分时会出现中断。

我的代码如下:(NASM 语法)

    ; Registers are restored and neccessary values are pushed onto the stack
    mov rax,cpu_cs ; check if return is to cpl0
    mov rax,[rax]
    cmp rax,0x8 ; 0x8 is the kernel segment
    jne iretq_cpl3
    
    ; return to cpl0
    mov rax,cpu_rax ; restore rax
    mov rax,[rax]

    iretq

iretq_cpl3: ;return to cpl3
    mov rax,cpu_rax ; restore rax from current_state
    push qword [rax]

    mov rax,cpl3_rax ; save rax to cpl_rax
    pop qword [rax]

    mov rax,cpl3_rcx ; save rcx
    mov [rax],rcx

    add rax,8 ; save r11
    mov [rax],r11

    add rax,8 ; pop and save rip
    pop qword [rax]

    add rsp,8 ; dispose cs

    pop r11 ; rflags into r11 for sysret; save it for cpl3 code
    
    pop rsp ; restore rsp

    mov rcx,cpl3_returner ; go to cpl3_returner

    o64 sysret

CPL=3 代码

; cpl3
align(4096)

global cpl3_kernel_part
global cpl3_reg_save
global cpl3_returner
cpl3_kernel_part:
cpl3_reg_save:
    cpl3_rax: dq 0
    cpl3_rcx: dq 0
    cpl3_r11: dq 0
    cpl3_rip: dq 0
cpl3_returner:
    mov rax,cpl3_r11 ; restore r11
    mov r11,[rax]

    mov rax,cpl3_rcx ; restore rcx
    mov rcx,cpl3_rip ; push rip on the stack for the return
    push qword [rax]

    mov rax,cpl3_rax ; restore rax
    mov rax,[rax]

    ret

编辑:

这是我尝试使用 IRETQ 时的错误代码,所以基本上当我也将 cpl3 代码与 cpl3 一起使用时。

当我尝试使用 IRETQ 返回 CPL3 时遇到的异常:

!!! Exception: 0xD //#GP
Error code: 0x28 //User code segment
SS: 0x10 //Kernel stack segment
CS: 0x8 //Kernel code segment
RSP: 0x20000024F98
RAX: 0x0
RIP: 0x200000031DA
instr: 0x48 0xCF 0x48 0xB8 0x60 0x37 0x0 0x0 0x0 0x2 0x0 0x0 0xC6 0x0 0x0 //IRETQ
!!!

IRETQ 之前的堆栈:

(gdb) x/5gx 0x20000024f98 //rsp
0x20000024f98:  0x0000000000000000 // User RIP    0x000000000000002b // User CS is 0x28
0x20000024fa8:  0x0000000000000202 // RFLAGS      0x0000000000006fec // User RSP
0x20000024fb8:  0x0000000000000023 // User SS is 0x20

RIP 是故意设置为 0x0。用户代码只是jmp $,它被映射到第一页。

GDT 如下所示:

__attribute__((aligned(4096)))
struct {
  struct gdt_entry null;
  struct gdt_entry kernel_code;
  struct gdt_entry kernel_data;
  struct gdt_entry null2;
  struct gdt_entry user_data;
  struct gdt_entry user_code;
  struct gdt_entry ovmf_data;
  struct gdt_entry ovmf_code;
  struct gdt_entry tss_low;
  struct gdt_entry tss_high;
} gdt_table = {
    {0,0x00,0},/* 0x00 null  */
    {0,0x9a,0xa0,/* 0x08 kernel code (kernel base selector) */
    {0,0x92,/* 0x10 kernel data */
    {0,/* 0x18 null (user base selector) */
    {0,/* 0x20 user data */
    {0,/* 0x28 user code */
    {0,/* 0x30 ovmf data */
    {0,/* 0x38 ovmf code */
    {0,0x89,/* 0x40 tss low */
    {0,/* 0x48 tss high */

};

来自 this 网站。

我让它更稳定一点,通过添加一个标志,如果它没有设置,则立即返回中断。 cpl3 代码在返回前设置了,但仍然不是一个完美的解决方案。

不过我注意到了一件有趣的事情。如果我只是在 qemu 中运行它,它会在几秒钟后崩溃,但是如果我附加 GDB,它会运行几分钟。

这是我的 GitHub repository

解决方法

原来是我的gdt有问题。感谢您的帮助。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。