在不同的虚拟机中lgdt之后,引导加载程序会产生不同的结果

时间:2018-06-05 12:59:02

标签: assembly nasm x86-64 bootloader protected-mode

我正在编写一个简单的启动加载程序。引导加载程序由两个程序集文件编译:boot.asm,protected_start.asm。 boot.asm将protected_start加载到0x10000,设置GDTR,进入保护模式并跳转到0x10000。所以编码在boot.asm中设置为16位,在protected_start.asm中设置为32位。

我在Windows上使用QEMU,VMware和NASM。两个虚拟机都有128MB内存。进入保护模式后,QEMU成功跳转到0x10000并打印给定的字符串,但VMWare重新启动。我使用“jmp $”来查找错误发生的位置,并在boot.asm中发现“jmp dword 0x08:0x0000”发生错误。

QEMU的版本是20180519,VMware是Workstation Player 14.1.2 build-8497320。

为什么LGDT出错后“jmp dword 0x08:0x0000”?我该怎么做才能解决这个问题?

boot.asm

[org 0x7c00]
[bits 16]

load:
    mov     ax,     0x1000
    mov     es,     ax
    mov     bx,     0x0000;

    mov     ah,     0x02;   Read sectors from drive
    mov     al,     0x08;   Number of sectors to read
    mov     ch,     0x00;   Cylinder index
    mov     cl,     0x02;   Sector
    mov     dh,     0x00;   Head
    mov     dl,     0x00;   Drive

    int     0x13
    jc      load

    cli

    lgdt    [gdtr]
    mov     eax,    cr0
    or      eax,    0x00000001
    mov     cr0,    eax

    jmp     dword   0x08:0x0000

gdt_list:
    dw      0
    dw      0
    db      0
    db      0
    db      0
    db      0

.gdt_code:
    dw      0xFFFF
    dw      0x0000
    db      0x01
    db      0x9A
    db      0xCF
    db      0x00

.gdt_data:
    dw      0xFFFF
    dw      0x0000
    db      0x00
    db      0x92
    db      0xCF
    db      0x00

.gdt_video:
    dw      0xFFFF
    dw      0x8000
    db      0x0B
    db      0x92
    db      0xCF
    db      0x00

gdtr:
    dw      4*8
    dd      gdt_list
    times 510 - ($ - $$) db 0
    dw 0xAA55

protected_start.asm

[org 0x10000]
[bits 32]

protected:

    mov     ax,     0x10

    mov     ds,     ax
    mov     fs,     ax
    mov     gs,     ax
    mov     ss,     ax
    mov     esp,    0xFFFF
    mov     ebp,    0xFFFF

    mov     ax,     0x18
    mov     es,     ax

    mov     edi,    0x0000
    mov     esi,    my_str
    call    write

    jmp     $
write:
    push    eax
    push    ebx
    mov     ah,     0x0e
    mov     bx,     0x0007
.loop:
    lodsb
    or      al,     al
    jz      .end
    mov     byte [es:edi],  al
    inc     di
    mov     byte [es:edi],  0x0f
    inc     di
    jmp     .loop
.end:
    pop     ebx
    pop     eax
    ret

my_str:
    db "Protected", 0x0 

times 512 - ($ - $$) db 0

我曾经在PowerShell中调用虚拟机:

qemu-system-x86_64 -m 128 -smp 1 -fda ".\bin\os.img"
Invoke-Item ".\virtual\os.vmx"

1 个答案:

答案 0 :(得分:0)

在lgdt解决问题之前初始化 ds 。因为lgdt [gdtr]实际上意味着lgdt [ds:gdtr],如果未设置ds,则可能发生错误。由于我使用[org 0x7c00],我将ds设置为0x0000。它适用于QEMU和VMware。

boot.asm的编辑版本:

[org 0x7c00]
[bits 16]

load:
    ; edited part: initialize ds
    mov     ax,     0x0000
    mov     ds,     ax

    mov     ax,     0x1000
    mov     es,     ax
    mov     bx,     0x0000;

    mov     ah,     0x02;   Read sectors from drive
    mov     al,     0x08;   Number of sectors to read
    mov     ch,     0x00;   Cylinder index
    mov     cl,     0x02;   Sector
    mov     dh,     0x00;   Head
    mov     dl,     0x00;   Drive

    int     0x13
    jc      load

    cli

    lgdt    [gdtr]
    mov     eax,    cr0
    or      eax,    0x00000001
    mov     cr0,    eax

    jmp     dword   0x08:0x0000

gdt_list:
    dw      0
    dw      0
    db      0
    db      0
    db      0
    db      0

.gdt_code:
    dw      0xffff
    dw      0x0000
    db      0x01
    db      0x9a
    db      0xcf
    db      0x00

.gdt_data:
    dw      0xffff
    dw      0x0000
    db      0x00
    db      0x92
    db      0xcf
    db      0x00

.gdt_video:
    dw      0xffff
    dw      0x8000
    db      0x0b
    db      0x92
    db      0xcf
    db      0x00

gdtr:
    dw      4*8
    dd      gdt_list

    times 510 - ($ - $$) db 0
    dw 0xAA55