我正在写一个玩具操作系统。我正在尝试为操作系统实施内存保护。我想要做的就是为用户空间程序保护内核。我只想通过分段而不是分页来做到这一点。
这是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_data 和 U_code 将成为用户空间。当我在内核空间并尝试通过执行切换数据段时:
mov ax, 0x20
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
我得到一个分割错误(13)。
我做错了什么?任何指导将不胜感激。
mov ss, ax
您正在加载
ss
的数据段,其 DPL 为 3,而您的 CPL 为 0。这是不允许的。您的堆栈必须始终与您处于相同的特权级别。 (此外,加载ss
而不同时加载esp
是没有任何意义的。)
切换到用户堆栈需要与过渡到 CPL 3 同时发生。实现此目的的主要方法是:
RETF
或IRET
。当目标段的特权较低时,它将从(旧)堆栈中弹出ss:esp
以及cs:eip
.
硬件任务开关,在这种情况下,
ss:esp
与所有其他寄存器一起从 TSS 加载。
调用门也执行堆栈切换,但你只能使用它们来调用更高权限的代码,而不是相反。
此外,如评论中所述,您的基本字段和限制字段似乎混淆了。就目前而言,您的用户细分的基数为
0x00000008
,所有细分的限制为 0xffff7000
。请注意,“位 0-15”是 least 有效位。所以如果它 U_data
应该是内存的上半部分,那么你会想要
dw 0xffff
dw 0x0000
db 0x0
db 11110010b
db 11000111b ; note the low nibble is now 7
db 0x80
请注意,这可能仍然不是您想要的,因为那里可能有也可能没有任何内存(例如,如果您的机器少于 2 GB),并且也可能有 I/O 映射到用户应该无权访问。所以你真的需要检查内存映射来决定把用户的段放在哪里。