设置全局描述符表和保护模式后不断重新启动

时间:2016-12-22 11:26:12

标签: x86 bootloader osdev protected-mode gdt

我必须对GDT setup和切换到protected mode做错了,因为它会不断重启。

我的kernel.asm应设置GDT并切换为protected mode

    bits 16

    jmp main
    %include "gdt.inc"

    main:
        cli
        xor ax,ax
        mov ds,ax
        mov es,ax
        mov ax,0x9000
        mov ss,ax
        mov sp,0xffff
        sti

        call InstallGDT

        cli
        mov eax,cr0
        or eax,1
        jmp 08h:Stage3

    bits 32
    Stage3:

        mov ax,0x10
        mov ds,ax
        mov ss,ax
        mov es,ax
        mov esp,90000h

    Stop:

        mov byte [0xb8000],'A'
        cli
        hlt

并且有gdt.inc

bits 16
InstallGDT:

    cli
    pusha
    lgdt   [toc]
    sti
    popa
    ret
gdt_data:
    dd 0
    dd 0

    dw 0ffffh
    dw 0
    db 0
    db 10011010b
    db 11001111b
    db 0

    dw 0ffffh
    dw 0
    db 0
    db 10010010b
    db 11001111b
    db 0
end_of_gdt:
toc:
    dw end_of_gdt - gdt_data -1
    dd gdt_data

我的bootloader.asm将10个扇区加载到0x1000:0x000,然后跳转到那里。

我使用命令测试代码:

nasm -f bin -o bootloader.bin bootloader.asm
nasm -f bin -o kernel.bin kernel.asm
cat bootloader.bin kernel.bin>OS.bin
qemu-system-i386 OS.bin

我的错在哪里?

1 个答案:

答案 0 :(得分:2)

由于我只能假设你已经正确地将扇区读入存储器0x1000:0x0000,我只能指出kernel.asmgdt.inc中的潜在问题。

代码问题

如果您使用jmp 0x1000:0x0000到达了内核阶段(我怀疑是这种情况),那么在kernel.asm中您错误地设置了 DS ,和 ES 段注册到错误的值。在这种情况下,您需要将这两个寄存器设置为0x1000,而不是0x0000。这段代码:

    xor ax,ax
    mov ds,ax
    mov es,ax

需要更改为:

    mov ax,0x1000
    mov ds,ax
    mov es,ax

您遇到的下一个主要问题是GDT记录(在toc内)采用线性地址。实模式中的线性地址与物理地址相同。从说明书手册中可以看出:

  

源操作数指定一个6字节的内存位置,其中包含基本地址(线性地址)和全局描述符表(GDT)的限制(以字节为单位的表的大小)

你已经为kernel.asm使用了 ORG 0x0000(因为你没有指定一个),所以 NASM 假设生成的所有偏移都来自于0x0000包括标签gdt_data。所以当你这样做时:

toc:
dw end_of_gdt - gdt_data -1
dd gdt_data

gdt_data将是一个小于0x0000的小偏移量。在物理内存中, GDT 记录实际上是0x1000:0x0000 +(小偏移)。物理(线性)内存中的0x1000:0x0000为(0x1000<<4)+0x0000 = 0x10000,因此您需要将其添加到gdt_data。您的toc应如下所示:

toc:
dw end_of_gdt - gdt_data -1
dd gdt_data+0x10000

您遇到的下一个问题是您实际上没有打开保护模式标志。你有这个:

    mov eax,cr0
    or eax,1

应该是:

    mov eax,cr0
    or eax,1
    mov cr0, eax

将该位设置为1后,需要更新 CR0 寄存器中的保护模式位。

GDT 问题相关,您已从0x00000000的偏移量创建了代码段的 GDT 条目,其中包含整个4gb地址空间。这是对的。但是,由于 NASM 从0x0000创建了偏移量并且您的代码实际上是在0x1000:0x0000(物理地址0x10000)处加载的,因此需要在stage3标签的值添加0x10000 > JMP 最终设置保护模式。同样,因为我们编码的值高于0xFFFF,我们需要强制 NASM 使用32位操作数,因此我们在dword上使用JMP限定符。你有这个:

jmp 08h:Stage3

应该是这样的:

jmp dword 08h:Stage3+0x10000