如何在NASM组装进入32位保护模式? [英] How do I enter 32-bit protected mode in NASM assembly?

查看:238
本文介绍了如何在NASM组装进入32位保护模式?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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


  1. 负载从包含的内核文件的软盘14个行业;

  2. 搜索标记这些部门的文件 kernel.feo ;

  3. 的文件到内存偏移为0x2000 负载;

  4. 执行使用远跳 JMP为0x2000内核:0×0000

所以,我必须位于内核code为0x2000:0 在记忆中。 CS 可能是正确的,因为使用的是远跳设置。在这个内核code,我想进入32位保护模式,但我不知道怎么气体放电管都在工作。当我运行低于code虚拟机上的(QEMU),它没有做任何事情。

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


  

这是说,你有以下问题:


  
  

      
  1. 您承担code。在 0x7c00被加载:0 由于组织0 ,但可能不是这种情况。保证的唯一事情就是物理地址。您应该使用远跳转到你的切入点,使 CS 设置正确。

  2.   
  3. 您是出于某种原因,设置 DS 为0x2000 让你的code不会找到任何数据在所有。你应该设置 DS 来匹配 CS ,或使用 CS 覆盖无处不在(不推荐)。

  4.   您的设置当然冲突
  5. 保护模式code假定从零开始段,这反过来又意味着,预计组织0x7c00 其中。您应该切换到组织0x7c00 和细分 0

  6.   
  7. 的VGA文本模式段在 0xb8000 不是 0xb80000 (少了一个零)。

  8.   
  9. 您不具备引导签名字节将0x55和0xAA 在引导扇区的末尾。

  10.   

我在code纠正了这些东西:


  1. [组织为0x0] 修正为 [组织为0x2000] 和段被设置为 0 ;

  2. DS 修正为 0 而不是为0x2000 ,所以现在用匹配CS ;

  3. VGA文本模式段修正为 0xb8000 ;

不过,code不能与这些校正工作,它应打印两个字符串,但它没有做任何事情!

请注意,这个内核code不应与引导签名将0x55和0xAA ,结束,因为它不是一个引导扇区。

下面是更正后的内核code(即不工作):

  [位16]
[组织为0x2000]    JMP 0:kernel_startgdt_start:gdt_null:
    DD为0x0
    DD为0x0gdt_ code:
    DW为0xFFFF
    DW为0x0
    分贝为0x0
    DB 10011010b
    DB 11001111b
    分贝为0x0gdt_data:
    DW为0xFFFF
    DW为0x0
    分贝为0x0
    DB 10010010b
    DB 11001111b
    分贝为0x0gdt_end:gdt_descriptor:
    DW gdt_end - gdt_start
    DD gdt_startcode_SEG EQU gdt_ code - gdt_start
DATA_SEG EQU gdt_data - gdt_start打印:
    MOV啊,14
    MOV BH,0
    LODSB
    CMP人,0
    JE .done
    INT为0x10
    JMP打印
.done:
    RETuzenet_real分贝'uzenet16',0
uzenet_prot分贝'uzenet32',0kernel_start:
    MOV AX,0
    MOV SS,AX
    MOV SP,0xFFFC    MOV AX,0
    MOV DS,AX
    MOV ES,AX
    MOV FS,斧
    MOV GS,斧    MOV SI,uzenet_real
    调用打印    CLI
    LGDT [gdt_descriptor]
    MOV EAX,CR0
    或EAX,为0x1
    MOV CRO,EAX
    JMP code_SEG:B32[比特32]VIDEO_MEMORY EQU 0xb8000
WHITE_ON_BLACK EQU为0x0Fprint32:
    PUSHA
    MOV EDX,VIDEO_MEMORY
。循环:
    MOV人,[EBX]
    MOV啊,WHITE_ON_BLACK
    CMP人,0
    JE .done
    MOV [EDX],斧
    加EBX,1
    添加EDX,2
    JMP .loop
.done:
    波帕
    RETB32:
    MOV AX,DATA_SEG
    MOV DS,AX
    MOV ES,AX
    MOV FS,斧
    MOV GS,斧
    MOV SS,AX    MOV EBP,0x90000
    MOV ESP,EBP    MOV EBX,uzenet_prot
    电话print32    JMP $


解决方案

编程的操作系统是一项高级任务。您至少希望能够使用调试器找出自己的错误,并了解基本的东西。你可能要重新考虑是否拥有所有这项工作的prerequisites。

这是说,你有以下问题:


  1. 您承担code。在 0x7c00被加载:0 由于组织0 ,但可能不是这种情况。保证的唯一事情就是物理地址。您应该使用远跳转到你的切入点,使 CS 设置正确。

  2. 您是出于某种原因,设置 DS 为0x2000 让你的code不会找到任何数据在所有。你应该设置 DS 来匹配 CS ,或使用 CS 覆盖无处不在(不推荐)。

  3. 您的设置当然冲突
  4. 保护模式code假定从零开始段,这反过来又意味着,预计组织0x7c00 其中。您应该切换到组织0x7c00 和细分 0

  5. 的VGA文本模式段在 0xb8000 不是 0xb80000 (少了一个零)。

  6. 您不具备引导签名字节将0x55和0xAA 在引导扇区的末尾。

固定code:

  [位16]
[有机0x7c00]    JMP 0:kernel_startgdt_start:gdt_null:
    DD为0x0
    DD为0x0gdt_ code:
    DW为0xFFFF
    DW为0x0
    分贝为0x0
    DB 10011010b
    DB 11001111b
    分贝为0x0gdt_data:
    DW为0xFFFF
    DW为0x0
    分贝为0x0
    DB 10010010b
    DB 11001111b
    分贝为0x0gdt_end:gdt_descriptor:
    DW gdt_end - gdt_start
    DD gdt_startcode_SEG EQU gdt_ code - gdt_start
DATA_SEG EQU gdt_data - gdt_start打印:
    PUSHA
    MOV啊,14
    MOV BH,0
。循环:
    LODSB
    CMP人,0
    JE .done
    INT为0x10
    JMP .loop
.done:
    波帕
    RETuzenet16分贝'uzenet16',0
uzenet32分贝'uzenet32',0kernel_start:
    MOV AX,0
    MOV SS,AX
    MOV SP,0xFFFC    MOV AX,0
    MOV DS,AX
    MOV ES,AX
    MOV FS,斧
    MOV GS,斧    MOV SI,uzenet16
    调用打印    CLI
    LGDT [gdt_descriptor]
    MOV EAX,CR0
    或EAX,为0x1
    MOV CRO,EAX
    JMP code_SEG:B32[比特32]VIDEO_MEMORY EQU 0xb8000
WHITE_ON_BLACK EQU为0x0Fprint32:
    PUSHA
    MOV EDX,VIDEO_MEMORY
。循环:
    MOV人,[EBX]
    MOV啊,WHITE_ON_BLACK
    CMP人,0
    JE .done
    MOV [EDX],斧
    加EBX,1
    添加EDX,2
    JMP .loop
.done:
    波帕
    RETB32:
    MOV AX,DATA_SEG
    MOV DS,AX
    MOV ES,AX
    MOV FS,斧
    MOV GS,斧
    MOV SS,AX    MOV EBP,为0x2000
    MOV ESP,EBP    MOV EBX,uzenet32
    电话print32    JMP $[SECTION签名开始= 0x7dfe]
DW 0AA55h


您更新的问题似乎仍然是困惑的code装入其中:你说的偏移为0x2000 但后来谈执行的0×0000 这当然是错误的,因为它在段多一个零,而应该是一个零段远反正跳:利用远跳转JMP为0x2000内核 JMP 0:为0x2000 。除此之外,确认您的code确实是加载到内存在正确的地方。了解如何使用调试器。

下面是加载从第二扇形上述code,以解决为0x2000 小引导扇区。它工作正常,问题是不与GDT的东西,特别是如果你甚至不得到真正的模式信息打印(你是不是清楚,要么)。

  [位16]
[有机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签名开始= 0x7dfe]
DW 0AA55h

I'm learning x86 assembly, and I'm trying to make a toy operating system in NASM, but I don't understand some things.

I made a bootloader that is successfully boots my kernel:

  1. Loads 14 sectors from the diskette which contains the kernel file;
  2. Search a file in these sectors labeled kernel.feo;
  3. Loads that file into the memory to the offset 0x2000;
  4. Executes the kernel using a far jump jmp 0x2000:0x0000.

So I have the kernel code located at 0x2000:0 in the memory. CS might be properly set because the using of a far jump. In this kernel code, I want to enter 32-bit protected mode, but I'm not sure how GDTs are working. When I run the code below on a virtual machine (QEMU), it is don't do anything.

I want to please you to help me entering 32-bit protected mode!

That said, you have the following problems:

  1. You assume the code is loaded at 0x7c00:0 due to the org 0, but that might not be the case. The only thing guaranteed is the physical address. You should use a far jump to your entry point so that CS is properly set.
  2. You are for some reason setting DS to 0x2000 so your code won't find any data at all. You should set DS to match CS, or use a CS override everywhere (not recommended).
  3. The protected mode code assumes zero-based segment, which in turn means it expects org 0x7c00 which of course conflicts with your setup. You should switch to org 0x7c00 and segments 0.
  4. The VGA text mode segment is at 0xb8000 not 0xb80000 (one less zero).
  5. You don't have the boot signature bytes 0x55 0xaa at the end of the boot sector.

I have corrected these things in my code:

  1. [org 0x0] is corrected to [org 0x2000] and segments are set to 0;
  2. DS is corrected to 0 instead of 0x2000, so now it matches with CS;
  3. VGA Text Mode segment is corrected to 0xb8000;

But the code won't work with these corrections, it should print two strings but it don't do anything!

Note that this kernel code should not end with a boot signature 0x55 0xAA, because it isn't a boot sector.

Here is the corrected kernel code (that not working):

[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 $

解决方案

Programming an OS is an advanced task. You are at least expected to be able to use a debugger to find your own mistakes and understand basic things. You might want to reconsider whether you have all the prerequisites for this endeavour.

That said, you have the following problems:

  1. You assume the code is loaded at 0x7c00:0 due to the org 0, but that might not be the case. The only thing guaranteed is the physical address. You should use a far jump to your entry point so that CS is properly set.
  2. You are for some reason setting DS to 0x2000 so your code won't find any data at all. You should set DS to match CS, or use a CS override everywhere (not recommended).
  3. The protected mode code assumes zero-based segment, which in turn means it expects org 0x7c00 which of course conflicts with your setup. You should switch to org 0x7c00 and segments 0.
  4. The VGA text mode segment is at 0xb8000 not 0xb80000 (one less zero).
  5. You don't have the boot signature bytes 0x55 0xaa at the end of the boot sector.

The fixed code:

[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


Your updated question still seems to be confused about where the code is loaded: you say offset 0x2000 but then talk about Executes the kernel using a far jump jmp 0x2000:0x0000 which is of course wrong, because it has one more zero in the segment, and should be a zero-segment far jump anyway: jmp 0:0x2000. Other than that, verify that your code is indeed loaded into memory at the correct place. Learn to use a debugger.

Here is a small boot sector which loads the above code from the second sector to address 0x2000. It works fine, the problem is not with the GDT stuff, especially if you don't even get the real mode message printed (you were not clear about that either).

[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

这篇关于如何在NASM组装进入32位保护模式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆