如何在NASM程序集中输入32位保护模式?


2

我正在学习x86汇编,我试图在NASM中制作一个玩具操作系统,但我不明白一些事情。

我做了一个引导程序是成功引导我的内核:

  1. 载荷从包含的内核文件的软盘14个行业;
  2. 在这些行业中搜索标有kernel.feo的文件;
  3. 将该文件加载到内存中的偏移量为0x2000;
  4. 使用远程跳转执行内核jmp 0x2000:0x0000

所以我的内核代码位于内存中的0x2000:0CS可能会被正确设置,因为使用了远程跳转。在这个内核代码中,我想输入32位保护模式,但我不确定GDT是如何工作的。当我在虚拟机(QEMU)上运行下面的代码时,它不会执行任何操作。

我想请你帮我输入32位保护模式!

这就是说,你有以下的问题:

  1. 你呈现的代码在0x7c00:0加载由于org 0,但可能并非如此。唯一保证的是物理地址。您应该使用远程跳转到您的入口点,以便正确设置CS
  2. 由于某种原因,您将DS设置为0x2000,因此您的代码根本找不到任何数据。您应该设置DS以匹配CS,或使用覆盖到处(不推荐)。
  3. 受保护的模式代码假定从零开始的段,这反过来意味着它预计org 0x7c00这当然与您的设置冲突。您应切换到org 0x7c00和段0
  4. VGA文本模式段在0xb8000而不是0xb80000(零减1)。
  5. 引导扇区末尾没有引导签名字节0x55 0xaa

我已经在我的代码纠正了这些东西:

  1. [org 0x0]修正为[org 0x2000]和段被设置为0;
  2. DS纠正为0而不是0x2000,所以现在它匹配CS;
  3. VGA文本模式段更正为0xb8000;

但是,这些代码将无法与这些更正,它应该打印两个字符串,但它不会做任何事情!

请注意,此内核代码不应以引导签名0x55 0xAA结尾,因为它不是引导扇区。

以下是更正后的内核代码(即不工作):

[bits 16] 
[org 0x2000] 

    jmp 0:kernel_start 

gdt_start: 

gdt_null: 
    dd 0x0 
    dd 0x0 

gdt_code: 
    dw 0xffff 
    dw 0x0 
    db 0x0 
    db 10011010b 
    db 11001111b 
    db 0x0 

gdt_data: 
    dw 0xffff 
    dw 0x0 
    db 0x0 
    db 10010010b 
    db 11001111b 
    db 0x0 

gdt_end: 

gdt_descriptor: 
    dw gdt_end - gdt_start 
    dd gdt_start 

CODE_SEG equ gdt_code - gdt_start 
DATA_SEG equ gdt_data - gdt_start 

print: 
    mov ah, 14 
    mov bh, 0 
    lodsb 
    cmp al, 0 
    je .done 
    int 0x10 
    jmp print 
.done: 
    ret 

uzenet_real db 'uzenet16', 0 
uzenet_prot db 'uzenet32', 0 

kernel_start: 
    mov ax, 0 
    mov ss, ax 
    mov sp, 0xFFFC 

    mov ax, 0 
    mov ds, ax 
    mov es, ax 
    mov fs, ax 
    mov gs, ax 

    mov si, uzenet_real 
    call print 

    cli 
    lgdt[gdt_descriptor] 
    mov eax, cr0 
    or eax, 0x1 
    mov cr0, eax 
    jmp CODE_SEG:b32 

[bits 32] 

VIDEO_MEMORY equ 0xb8000 
WHITE_ON_BLACK equ 0x0f 

print32: 
    pusha 
    mov edx, VIDEO_MEMORY 
.loop: 
    mov al, [ebx] 
    mov ah, WHITE_ON_BLACK 
    cmp al, 0 
    je .done 
    mov [edx], ax 
    add ebx, 1 
    add edx, 2 
    jmp .loop 
.done: 
    popa 
    ret 

b32: 
    mov ax, DATA_SEG 
    mov ds, ax 
    mov es, ax 
    mov fs, ax 
    mov gs, ax 
    mov ss, ax 

    mov ebp, 0x90000 
    mov esp, ebp 

    mov ebx, uzenet_prot 
    call print32 

    jmp $ 
4

编程的OS是一种先进的任务。您至少期望能够使用调试器来发现自己的错误并理解基本的东西。你可能想重新考虑你是否有这个努力的所有先决条件。

这就是说,你有以下的问题:

  1. 你呈现的代码在0x7c00:0加载由于org 0,但可能并非如此。唯一保证的是物理地址。您应该使用远程跳转到您的入口点,以便正确设置CS
  2. 由于某种原因,您将DS设置为0x2000,因此您的代码根本找不到任何数据。您应该设置DS以匹配CS,或使用覆盖到处(不推荐)。
  3. 受保护的模式代码假定从零开始的段,这反过来意味着它预计org 0x7c00这当然与您的设置冲突。您应切换到org 0x7c00和段0
  4. VGA文本模式段在0xb8000而不是0xb80000(零减1)。
  5. 引导扇区末尾没有引导签名字节0x55 0xaa

固定的代码:

[bits 16] 
[org 0x7c00] 

    jmp 0:kernel_start 

gdt_start: 

gdt_null: 
    dd 0x0 
    dd 0x0 

gdt_code: 
    dw 0xffff 
    dw 0x0 
    db 0x0 
    db 10011010b 
    db 11001111b 
    db 0x0 

gdt_data: 
    dw 0xffff 
    dw 0x0 
    db 0x0 
    db 10010010b 
    db 11001111b 
    db 0x0 

gdt_end: 

gdt_descriptor: 
    dw gdt_end - gdt_start 
    dd gdt_start 

CODE_SEG equ gdt_code - gdt_start 
DATA_SEG equ gdt_data - gdt_start 

print: 
    pusha 
    mov ah, 14 
    mov bh, 0 
.loop: 
    lodsb 
    cmp al, 0 
    je .done 
    int 0x10 
    jmp .loop 
.done: 
    popa 
    ret 

uzenet16 db 'uzenet16', 0 
uzenet32 db 'uzenet32', 0 

kernel_start: 
    mov ax, 0 
    mov ss, ax 
    mov sp, 0xFFFC 

    mov ax, 0 
    mov ds, ax 
    mov es, ax 
    mov fs, ax 
    mov gs, ax 

    mov si, uzenet16 
    call print 

    cli 
    lgdt[gdt_descriptor] 
    mov eax, cr0 
    or eax, 0x1 
    mov cr0, eax 
    jmp CODE_SEG:b32 

[bits 32] 

VIDEO_MEMORY equ 0xb8000 
WHITE_ON_BLACK equ 0x0f 

print32: 
    pusha 
    mov edx, VIDEO_MEMORY 
.loop: 
    mov al, [ebx] 
    mov ah, WHITE_ON_BLACK 
    cmp al, 0 
    je .done 
    mov [edx], ax 
    add ebx, 1 
    add edx, 2 
    jmp .loop 
.done: 
    popa 
    ret 

b32: 
    mov ax, DATA_SEG 
    mov ds, ax 
    mov es, ax 
    mov fs, ax 
    mov gs, ax 
    mov ss, ax 

    mov ebp, 0x2000 
    mov esp, ebp 

    mov ebx, uzenet32 
    call print32 

    jmp $ 

[SECTION signature start=0x7dfe] 
dw 0AA55h 

你更新的问题似乎仍然感到困惑,其中的代码加载:你说offset 0x2000但再谈谈Executes the kernel using a far jump jmp 0x2000:0x0000这当然是错误的,因为它在该段中还有一个零点,并且无论如何应该是零段远程跳转:jmp 0:0x2000。除此之外,请验证您的代码确实已在正确的位置加载到内存中。学习使用调试器。

这是一个小引导扇区,它从第二个扇区加载上述代码到地址为0x2000。它工作正常,问题不在于GDT的东西,尤其是如果你甚至没有打印真正的模式信息(你不清楚这一点)。

[bits 16] 
[org 0x7c00] 
mov ax, 0201h 
mov cx, 0002h 
mov dh, 0 
mov bx, 0 
mov es, bx 
mov bx, 2000h 
int 13h 
jmp 0:2000h 

[SECTION signature start=0x7dfe] 
dw 0AA55h 
  0

对不起,我错过了我的描述中的一些重要信息,但现在我添加了它们!请查看! 22 2月. 152015-02-22 12:36:11

  0

也更新了答案。 22 2月. 152015-02-22 12:54:06