assembly 将分段从Ring0(内核)更改为Ring3(用户)导致分段故障

0ve6wy6x  于 2023-04-06  发布在  其他
关注(0)|答案(1)|浏览(129)

我正在写一个玩具操作系统。我正在尝试为操作系统实现内存保护。我想做的就是为内核创建用户空间程序的保护。我想纯粹用分段而不是分页来做这件事。
下面是GDT:

gdt_start:
    
    dd 0x0 ; 4 byte
    dd 0x0 ; 4 byte

gdt_code: 
    dw 0xfff7    ; segment length, bits 0-15
    dw 0x0       ; segment base, bits 0-15
    db 0x0       ; seg2ment base, bits 16-23
    db 10011010b ; flags (8 bits)
    db 11001111b ; flags (4 bits) + segment length, bits 16-19
    db 0x0       ; segment base, bits 24-31

gdt_data:
    dw 0xfff7
    dw 0x0
    db 0x0
    db 10010010b
    db 11001111b
    db 0x0

U_code: 
    dw 0xfff7    ; segment length, bits 0-15
    dw 0x0008       ; segment base, bits 0-15
    db 0x0       ; seg2ment base, bits 16-23
    db 11111010b ; flags (8 bits)
    db 11001111b ; flags (4 bits) + segment length, bits 16-19
    db 0x0       ; segment base, bits 24-31

U_data:
    dw 0xfff7
    dw 0x0008
    db 0x0
    db 11110010b
    db 11001111b
    db 0x0
gdt_end:

U_dataU_code将成为用户空间(环3)。当我在内核空间(环0)时,尝试通过执行以下命令切换数据段:

mov ax, 0x20 
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax

我得到一个分割错误(13)。
我做错了什么?任何指导都将不胜感激。

tktrz96b

tktrz96b1#

mov ss, ax

您正在加载ss,其数据段的DPL为3,而CPL为0。这是不允许的。您的堆栈必须始终与您处于相同的特权级别。(此外,加载ss而不同时加载esp没有任何意义。)
切换到用户的堆栈需要与转换到CPL 3同时发生。主要方法有:

  • RETFIRET。当目标段的权限较低时,它将从(旧)堆栈中弹出ss:esp以及cs:eip
  • 一个硬件任务开关,在这种情况下,ss:esp与所有其他寄存器沿着从TSS加载。

调用门也会执行堆栈切换,但只能使用它们来调用特权更高的代码,而不是相反。
此外,正如注解中所指出的,您的base和limit字段似乎混淆了。就目前而言,您的用户段的base为0x00000008,而您的所有段的limit为0xffff7000。请注意,“bits 0-15”是 * 最低 * 有效位。因此,如果U_data应该是内存的上半部分,那么您需要

dw 0xffff
    dw 0x0000
    db 0x0
    db 11110010b
    db 11000111b  ; note the low nibble is now 7
    db 0x80

请注意,这可能仍然不是您想要的,因为那里可能有也可能没有任何内存(例如,如果您的机器具有小于2 GB的内存),并且还可能有用户不应该访问的I/OMap到该区域。因此,您确实需要检查内存Map以决定将用户的段放在哪里。

相关问题