问题描述
我正在创建一个手写操作系统,但在中断处理方面存在一些问题。 问题是当我尝试从中断返回到 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有问题。感谢您的帮助。