我正在写一个玩具操作系统。我正在尝试为操作系统实现内存保护。我想做的就是为内核创建用户空间程序的保护。我想纯粹用分段而不是分页来做这件事。
下面是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
将成为用户空间(环3)。当我在内核空间(环0)时,尝试通过执行以下命令切换数据段:
mov ax, 0x20
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
我得到一个分割错误(13)。
我做错了什么?任何指导都将不胜感激。
1条答案
按热度按时间tktrz96b1#
您正在加载
ss
,其数据段的DPL为3,而CPL为0。这是不允许的。您的堆栈必须始终与您处于相同的特权级别。(此外,加载ss
而不同时加载esp
没有任何意义。)切换到用户的堆栈需要与转换到CPL 3同时发生。主要方法有:
RETF
或IRET
。当目标段的权限较低时,它将从(旧)堆栈中弹出ss:esp
以及cs:eip
。ss:esp
与所有其他寄存器沿着从TSS加载。调用门也会执行堆栈切换,但只能使用它们来调用特权更高的代码,而不是相反。
此外,正如注解中所指出的,您的base和limit字段似乎混淆了。就目前而言,您的用户段的base为
0x00000008
,而您的所有段的limit为0xffff7000
。请注意,“bits 0-15”是 * 最低 * 有效位。因此,如果U_data
应该是内存的上半部分,那么您需要请注意,这可能仍然不是您想要的,因为那里可能有也可能没有任何内存(例如,如果您的机器具有小于2 GB的内存),并且还可能有用户不应该访问的I/OMap到该区域。因此,您确实需要检查内存Map以决定将用户的段放在哪里。