如何在NASM组装进入32位保护模式? [英] How do I enter 32-bit protected mode in NASM assembly?
问题描述
我学习x86汇编,我试图做一个玩具的操作系统在NASM,但我不明白一些事情。
我做了一个引导程序被成功引导我的内核:
- 负载从包含的内核文件的软盘14个行业;
- 搜索标记这些部门的文件
kernel.feo
; - 的文件到内存偏移
为0x2000
负载; - 执行使用远跳
JMP为0x2000内核:0×0000
所以,我必须位于内核code为0x2000:0
在记忆中。 CS
可能是正确的,因为使用的是远跳设置。在这个内核code,我想进入32位保护模式,但我不知道怎么气体放电管都在工作。当我运行低于code虚拟机上的(QEMU)
,它没有做任何事情。
我想请你帮我进入32位保护模式!
这是说,你有以下问题:
- 您承担code。在
0x7c00被加载:0
由于组织0
,但可能不是这种情况。保证的唯一事情就是物理地址。您应该使用远跳转到你的切入点,使CS
设置正确。
- 您是出于某种原因,设置
DS
到为0x2000
让你的code不会找到任何数据在所有。你应该设置DS
来匹配CS
,或使用CS
覆盖无处不在(不推荐)。
您的设置当然冲突- 保护模式code假定从零开始段,这反过来又意味着,预计
组织0x7c00
其中。您应该切换到组织0x7c00
和细分0
。
- 的VGA文本模式段在
0xb8000
不是0xb80000
(少了一个零)。
- 您不具备引导签名字节
将0x55和0xAA
在引导扇区的末尾。
块引用>我在code纠正了这些东西:
[组织为0x0]
修正为[组织为0x2000]
和段被设置为0
;DS
修正为0
而不是为0x2000
,所以现在用匹配CS
;- 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。
这是说,你有以下问题:
- 您承担code。在
0x7c00被加载:0
由于组织0
,但可能不是这种情况。保证的唯一事情就是物理地址。您应该使用远跳转到你的切入点,使CS
设置正确。- 您是出于某种原因,设置
DS
到为0x2000
让你的code不会找到任何数据在所有。你应该设置DS
来匹配CS
,或使用CS
覆盖无处不在(不推荐)。
您的设置当然冲突- 保护模式code假定从零开始段,这反过来又意味着,预计
组织0x7c00
其中。您应该切换到组织0x7c00
和细分0
。- 的VGA文本模式段在
0xb8000
不是0xb80000
(少了一个零)。- 您不具备引导签名字节
将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 0AA55hI'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:
- Loads 14 sectors from the diskette which contains the kernel file;
- Search a file in these sectors labeled
kernel.feo
;- Loads that file into the memory to the offset
0x2000
;- 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:
- You assume the code is loaded at
0x7c00:0
due to theorg 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 thatCS
is properly set.- You are for some reason setting
DS
to0x2000
so your code won't find any data at all. You should setDS
to matchCS
, or use aCS
override everywhere (not recommended).- 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 toorg 0x7c00
and segments0
.- The VGA text mode segment is at
0xb8000
not0xb80000
(one less zero).- 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:
[org 0x0]
is corrected to[org 0x2000]
and segments are set to0
;DS
is corrected to0
instead of0x2000
, so now it matches withCS
;- 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:
- You assume the code is loaded at
0x7c00:0
due to theorg 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 thatCS
is properly set.- You are for some reason setting
DS
to0x2000
so your code won't find any data at all. You should setDS
to matchCS
, or use aCS
override everywhere (not recommended).- 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 toorg 0x7c00
and segments0
.- The VGA text mode segment is at
0xb8000
not0xb80000
(one less zero).- 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 aboutExecutes 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屋!