切换到保护模式并跳远后出错
Error After Switching to Protected Mode and Making a Far Jump
我正在使用 FASM(平面汇编程序)编写引导加载程序。我在 16 位模式下成功,但在切换到 32 位模式时遇到错误。我查看了类似的答案(实际上是 GPF after far jump to protected mode 处的相同问题),但该解决方案并未解决我的问题。
这是我的引导加载程序 -
org 0x7c00
jmp main
include 'bios.asm'
include 'print32.asm'
include 'gdt.asm'
main:
mov bp,0x9000
mov sp,bp
mov bx, bootMsg
call print_string
lgdt [gdt_descriptor]
cli
mov eax, cr0
or eax, 0x1
mov cr0, eax
jmp CODE_SEG:init_pm ;**The error seems to occurs here
jmp $
bits = 32
init_pm:
mov ax,DATA_SEG
mov ds,ax
mov ss,ax
mov es,ax
mov ebp, 0x90000
mov esp, ebp
jmp BEGIN_PM
BEGIN_PM:
mov ebx, pmMsg
call print_string32
jmp $
pmMsg:
db "Sucessfully switched to the 32-bit protected mode....",0
bootMsg:
db "Booted in 16-bit Real Mode mode....",0
times 510-($-$$) db 0
dw 0xaa55
这是 GDT -
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 - 1
dd gdt_start
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
她的是 Bochs 控制台输出 -
00478171069i[BIOS ] Booting from 0000:7c00
00478195765e[CPU0 ] write_virtual_checks(): write beyond limit, r/w
00478195765e[CPU0 ] interrupt(): gate descriptor is not valid sys seg (vector=0x0d)
00478195765e[CPU0 ] interrupt(): gate descriptor is not valid sys seg (vector=0x08)
00478195765i[CPU0 ] CPU is in protected mode (active)
00478195765i[CPU0 ] CS.mode = 32 bit
00478195765i[CPU0 ] SS.mode = 32 bit
00478195765i[CPU0 ] EFER = 0x00000000
00478195765i[CPU0 ] | EAX=d88e0010 EBX=00007d77 ECX=00090000 EDX=00000000
00478195765i[CPU0 ] | ESP=00009000 EBP=00000000 ESI=000e0000 EDI=0000ffac
00478195765i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of df if tf sf zf af PF cf
00478195765i[CPU0 ] | SEG sltr(index|ti|rpl) base limit G D
00478195765i[CPU0 ] | CS:0008( 0001| 0| 0) 00000000 ffffffff 1 1
00478195765i[CPU0 ] | DS:0000( 0005| 0| 0) 00000000 0000ffff 0 0
00478195765i[CPU0 ] | SS:0010( 0002| 0| 0) 00000000 ffffffff 1 1
00478195765i[CPU0 ] | ES:0010( 0002| 0| 0) 00000000 ffffffff 1 1
00478195765i[CPU0 ] | FS:0000( 0005| 0| 0) 00000000 0000ffff 0 0
00478195765i[CPU0 ] | GS:0000( 0005| 0| 0) 00000000 0000ffff 0 0
00478195765i[CPU0 ] | EIP=00007d2f (00007d2f)
00478195765i[CPU0 ] | CR0=0x60000011 CR2=0x00000000
00478195765i[CPU0 ] | CR3=0x00000000 CR4=0x00000000
00478195765i[CPU0 ] 0x0000000000007d2f>> or dword ptr ds:[eax], eax : 0900
00478195765e[CPU0 ] exception(): 3rd (13) exception with no resolution, shutdown status is 00h, resetting
谁能帮我解决这个问题?困扰我很久了..
编辑-
这是 print32 代码-
use32
VIDEO_MEM equ 0xb8000
W_O_B equ 0x0f
print_string32:
pusha
mov edx,VIDEO_MEM
print_string32_loop:
mov al, [ebx]
mov ah, W_O_B
cmp al,0
je print_string32_end
mov [edx],ax
inc ebx
add edx,2
jmp print_string32_loop
print_string32_end:
popa
ret
以及引导加载程序的更改代码 -
org 0x7c00
mov bp,0x9000
mov sp,bp
mov bx, bootMsg
call print_string
cli
lgdt [gdt_descriptor]
mov eax, cr0
or eax, 0x1
mov cr0, eax
jmp 0x8:init_pm
jmp $
use32
init_pm:
mov ax, 0x10
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ebp,0x90000
mov esp,0x90000
jmp BEGIN_PM
jmp $
include 'bios.asm'
include 'gdt.asm'
include 'print32.asm'
use32
BEGIN_PM:
mov ebx, pmMsg
call print_string32
jmp $
pmMsg:
db "Sucessfully switched to the 32-bit protected mode....",0
bootMsg:
db "Booted in 16-bit Real Mode mode....",0
times 510-($-$$) db 0
dw 0xaa55
TL;DR :要修复更改 bits = 32
到 use32
让 FASM 为 32 位模式的处理器 运行 生成指令。 FASM Documentation 在 节 1.1.4 输出格式 中的状态:
By default, when there is no format directive in source file, flat assembler simply puts generated instruction codes into output, creating this way flat binary file. By default it generates 16-bit code, but you can always turn it into the 16-bit or 32-bit mode by using use16 or use32 directive.
在将 NASM 代码转换为 FASM 和 bits 32
时,您似乎使用了 bits = 32
未被 FASM 接受。 bits=32
将名为 bits
的常量值设置为值 32。它不会告诉 FASM 生成处理器在 32 位模式下使用的指令.虽然 bits = 32
组装没有错误,但它没有达到您的预期。
通过不使用 use32
,您告诉 FASM 在 init_pm
之后使用使用 32 位地址和在 16- 中工作的操作数的指令生成代码位实模式,而不是使用 32 位地址的指令和在 32 位保护模式下工作的操作数。
虽然我无法测试您的代码,但我将在尝试了解您编写的代码可能发生的情况时进行这些观察 post。首先 BOCHS 转储这些行:
[CPU0 ] 0x0000000000007d2f>> or dword ptr ds:[eax], eax : 0900
[CPU0 ] exception(): 3rd (13) exception with no resolution, shutdown status is 00h, resetting
这表示在地址 0x7d2f 处遇到指令 or dword ptr ds:[eax], eax
(其编码为 0900)并生成异常 13(一般保护错误)。
当时 BOCHS 状态转储中有一些东西表明您处于保护模式:
CPU is in protected mode (active)
此外还有迹象表明 jmp CODE_SEG:init_pm
正确执行。这是因为在您出错时 BOCHS dumped CS:0008
这意味着 CS 被设置为值 0008 (= CODE_SEG
)。 DS 选择器为 0,这是不寻常的,因为在 JMP 之后您将其设置为 DATA_SEG (0x10) 但 ES 和 SS 段选择器设置为 0x10。这一切都表明 init_pm
代码已执行,但不知何故它最终没有按预期执行。
在这一点上我意识到你已经写了 bits = 32
它有效地将一个常量设置为值 32。它没有告诉 FASM 生成代码针对将在 32 位模式下执行的 CPU。
考虑到这一点,我决定采用指令并让汇编器针对 16 位模式对它们进行编码:
init_pm:
mov ax,DATA_SEG
mov ds,ax
mov ss,ax
mov es,ax
mov ebp, 0x90000
当我使用 NDISASM 转储我的测试代码时使用 -b32
选项(强制 NDISASM 解码为 32 -位目标),它将其解码为:
00000000 B810008ED8 mov eax,0xd88e0010
00000005 8ED0 mov ss,eax
00000007 8EC0 mov es,eax
00000009 66BD0000 mov bp,0x0
0000000D 0900 or [eax],eax
0000000F 6689EC mov sp,bp
首先解码不正确mov eax, 0xd88e0010
。这解释了为什么在您的 BOCHS 转储中有 EAX=d88e0010
。 EAX 的低 16 位移动到 ES 所以 ES=0x0010 匹配你的 BOCHS 输出ES:0010
。类似的事情适用于正在设置的 SS。 BP 被设置为 0,这在 BOCHS 输出 BP:0000
中得到确认。此指令导致故障和崩溃:
0000000D 0900 or [eax],eax
or [eax],eax
与 or ds:[eax],eax
相同。 [eax]
隐式引用了 DS 。将此指令与 BOCHS 输出进行比较:
0x0000000000007d2f>> or dword ptr ds:[eax], eax : 0900
啊哈,这就是这个不寻常的指令的来源(你可以忽略 DWORD PTR
)。错误解码的指令尝试使用指向 NULL (0x0000) 描述符的 DS。这会导致处理器故障,以及 BOCHS 和状态转储报告的后续错误。
正如我在评论中所述,使用 BOCHS 中的内部调试器非常有用,尤其是在调试引导加载程序和内核时。如果您在调试器中一次一个地执行引导加载程序指令,您可能会发现您的 FAR JMP 到 init_pm
按预期工作。然后您会观察到正在执行的意外指令最终导致 processor fault.
我正在使用 FASM(平面汇编程序)编写引导加载程序。我在 16 位模式下成功,但在切换到 32 位模式时遇到错误。我查看了类似的答案(实际上是 GPF after far jump to protected mode 处的相同问题),但该解决方案并未解决我的问题。
这是我的引导加载程序 -
org 0x7c00
jmp main
include 'bios.asm'
include 'print32.asm'
include 'gdt.asm'
main:
mov bp,0x9000
mov sp,bp
mov bx, bootMsg
call print_string
lgdt [gdt_descriptor]
cli
mov eax, cr0
or eax, 0x1
mov cr0, eax
jmp CODE_SEG:init_pm ;**The error seems to occurs here
jmp $
bits = 32
init_pm:
mov ax,DATA_SEG
mov ds,ax
mov ss,ax
mov es,ax
mov ebp, 0x90000
mov esp, ebp
jmp BEGIN_PM
BEGIN_PM:
mov ebx, pmMsg
call print_string32
jmp $
pmMsg:
db "Sucessfully switched to the 32-bit protected mode....",0
bootMsg:
db "Booted in 16-bit Real Mode mode....",0
times 510-($-$$) db 0
dw 0xaa55
这是 GDT -
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 - 1
dd gdt_start
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
她的是 Bochs 控制台输出 -
00478171069i[BIOS ] Booting from 0000:7c00
00478195765e[CPU0 ] write_virtual_checks(): write beyond limit, r/w
00478195765e[CPU0 ] interrupt(): gate descriptor is not valid sys seg (vector=0x0d)
00478195765e[CPU0 ] interrupt(): gate descriptor is not valid sys seg (vector=0x08)
00478195765i[CPU0 ] CPU is in protected mode (active)
00478195765i[CPU0 ] CS.mode = 32 bit
00478195765i[CPU0 ] SS.mode = 32 bit
00478195765i[CPU0 ] EFER = 0x00000000
00478195765i[CPU0 ] | EAX=d88e0010 EBX=00007d77 ECX=00090000 EDX=00000000
00478195765i[CPU0 ] | ESP=00009000 EBP=00000000 ESI=000e0000 EDI=0000ffac
00478195765i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of df if tf sf zf af PF cf
00478195765i[CPU0 ] | SEG sltr(index|ti|rpl) base limit G D
00478195765i[CPU0 ] | CS:0008( 0001| 0| 0) 00000000 ffffffff 1 1
00478195765i[CPU0 ] | DS:0000( 0005| 0| 0) 00000000 0000ffff 0 0
00478195765i[CPU0 ] | SS:0010( 0002| 0| 0) 00000000 ffffffff 1 1
00478195765i[CPU0 ] | ES:0010( 0002| 0| 0) 00000000 ffffffff 1 1
00478195765i[CPU0 ] | FS:0000( 0005| 0| 0) 00000000 0000ffff 0 0
00478195765i[CPU0 ] | GS:0000( 0005| 0| 0) 00000000 0000ffff 0 0
00478195765i[CPU0 ] | EIP=00007d2f (00007d2f)
00478195765i[CPU0 ] | CR0=0x60000011 CR2=0x00000000
00478195765i[CPU0 ] | CR3=0x00000000 CR4=0x00000000
00478195765i[CPU0 ] 0x0000000000007d2f>> or dword ptr ds:[eax], eax : 0900
00478195765e[CPU0 ] exception(): 3rd (13) exception with no resolution, shutdown status is 00h, resetting
谁能帮我解决这个问题?困扰我很久了..
编辑-
这是 print32 代码-
use32
VIDEO_MEM equ 0xb8000
W_O_B equ 0x0f
print_string32:
pusha
mov edx,VIDEO_MEM
print_string32_loop:
mov al, [ebx]
mov ah, W_O_B
cmp al,0
je print_string32_end
mov [edx],ax
inc ebx
add edx,2
jmp print_string32_loop
print_string32_end:
popa
ret
以及引导加载程序的更改代码 -
org 0x7c00
mov bp,0x9000
mov sp,bp
mov bx, bootMsg
call print_string
cli
lgdt [gdt_descriptor]
mov eax, cr0
or eax, 0x1
mov cr0, eax
jmp 0x8:init_pm
jmp $
use32
init_pm:
mov ax, 0x10
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ebp,0x90000
mov esp,0x90000
jmp BEGIN_PM
jmp $
include 'bios.asm'
include 'gdt.asm'
include 'print32.asm'
use32
BEGIN_PM:
mov ebx, pmMsg
call print_string32
jmp $
pmMsg:
db "Sucessfully switched to the 32-bit protected mode....",0
bootMsg:
db "Booted in 16-bit Real Mode mode....",0
times 510-($-$$) db 0
dw 0xaa55
TL;DR :要修复更改 bits = 32
到 use32
让 FASM 为 32 位模式的处理器 运行 生成指令。 FASM Documentation 在 节 1.1.4 输出格式 中的状态:
By default, when there is no format directive in source file, flat assembler simply puts generated instruction codes into output, creating this way flat binary file. By default it generates 16-bit code, but you can always turn it into the 16-bit or 32-bit mode by using use16 or use32 directive.
在将 NASM 代码转换为 FASM 和 bits 32
时,您似乎使用了 bits = 32
未被 FASM 接受。 bits=32
将名为 bits
的常量值设置为值 32。它不会告诉 FASM 生成处理器在 32 位模式下使用的指令.虽然 bits = 32
组装没有错误,但它没有达到您的预期。
通过不使用 use32
,您告诉 FASM 在 init_pm
之后使用使用 32 位地址和在 16- 中工作的操作数的指令生成代码位实模式,而不是使用 32 位地址的指令和在 32 位保护模式下工作的操作数。
虽然我无法测试您的代码,但我将在尝试了解您编写的代码可能发生的情况时进行这些观察 post。首先 BOCHS 转储这些行:
[CPU0 ] 0x0000000000007d2f>> or dword ptr ds:[eax], eax : 0900
[CPU0 ] exception(): 3rd (13) exception with no resolution, shutdown status is 00h, resetting
这表示在地址 0x7d2f 处遇到指令 or dword ptr ds:[eax], eax
(其编码为 0900)并生成异常 13(一般保护错误)。
当时 BOCHS 状态转储中有一些东西表明您处于保护模式:
CPU is in protected mode (active)
此外还有迹象表明 jmp CODE_SEG:init_pm
正确执行。这是因为在您出错时 BOCHS dumped CS:0008
这意味着 CS 被设置为值 0008 (= CODE_SEG
)。 DS 选择器为 0,这是不寻常的,因为在 JMP 之后您将其设置为 DATA_SEG (0x10) 但 ES 和 SS 段选择器设置为 0x10。这一切都表明 init_pm
代码已执行,但不知何故它最终没有按预期执行。
在这一点上我意识到你已经写了 bits = 32
它有效地将一个常量设置为值 32。它没有告诉 FASM 生成代码针对将在 32 位模式下执行的 CPU。
考虑到这一点,我决定采用指令并让汇编器针对 16 位模式对它们进行编码:
init_pm:
mov ax,DATA_SEG
mov ds,ax
mov ss,ax
mov es,ax
mov ebp, 0x90000
当我使用 NDISASM 转储我的测试代码时使用 -b32
选项(强制 NDISASM 解码为 32 -位目标),它将其解码为:
00000000 B810008ED8 mov eax,0xd88e0010
00000005 8ED0 mov ss,eax
00000007 8EC0 mov es,eax
00000009 66BD0000 mov bp,0x0
0000000D 0900 or [eax],eax
0000000F 6689EC mov sp,bp
首先解码不正确mov eax, 0xd88e0010
。这解释了为什么在您的 BOCHS 转储中有 EAX=d88e0010
。 EAX 的低 16 位移动到 ES 所以 ES=0x0010 匹配你的 BOCHS 输出ES:0010
。类似的事情适用于正在设置的 SS。 BP 被设置为 0,这在 BOCHS 输出 BP:0000
中得到确认。此指令导致故障和崩溃:
0000000D 0900 or [eax],eax
or [eax],eax
与 or ds:[eax],eax
相同。 [eax]
隐式引用了 DS 。将此指令与 BOCHS 输出进行比较:
0x0000000000007d2f>> or dword ptr ds:[eax], eax : 0900
啊哈,这就是这个不寻常的指令的来源(你可以忽略 DWORD PTR
)。错误解码的指令尝试使用指向 NULL (0x0000) 描述符的 DS。这会导致处理器故障,以及 BOCHS 和状态转储报告的后续错误。
正如我在评论中所述,使用 BOCHS 中的内部调试器非常有用,尤其是在调试引导加载程序和内核时。如果您在调试器中一次一个地执行引导加载程序指令,您可能会发现您的 FAR JMP 到 init_pm
按预期工作。然后您会观察到正在执行的意外指令最终导致 processor fault.