GDT x86 32 位保护模式下的切换段

问题描述 投票:0回答:1

我正在写一个玩具操作系统。我正在尝试为操作系统实施内存保护。我想要做的就是为用户空间程序保护内核。我只想通过分段而不是分页来做到这一点。

这是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)。

我做错了什么?任何指导将不胜感激。

assembly x86 osdev memory-segmentation gdt
1个回答
3
投票
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 映射到用户应该无权访问。所以你真的需要检查内存映射来决定把用户的段放在哪里。

© www.soinside.com 2019 - 2024. All rights reserved.