切换到保护模式并进行远跳后出现错误



我正在使用FASM (Flat Assembler)编写一个引导加载程序。我在16位模式下成功了,但我在切换到32位模式时遇到了错误。我看了一个类似的答案(事实上相同的问题在GPF后远跳到保护模式),但解决方案并没有解决我的问题。

这是我的引导加载程序-

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: To fix change bits = 32 To use32

使FASM为运行在32位模式的处理器生成指令。FASM文档在 1.1.4输出格式:

默认情况下,当源文件中没有格式指令时,平面汇编器只是将生成的指令代码输出,从而创建这样的平面二进制文件。默认生成16位代码,但您可以通过使用use16或use32指令将其转换为16位或32位模式

在将NASM代码转换为FASM时似乎使用了bits = 32,而FASM不接受bits 32bits=32设置名为bits的常量值为32。它没有告诉FASM生成32位模式下处理器使用的指令。虽然bits = 32组装没有错误,但它没有达到您的期望。

通过不使用use32,您告诉FASMinit_pm之后生成使用32位地址和16位实模式操作数的指令的代码,而不是使用32位地址和32位保护模式操作数的指令。


虽然我不能测试你的代码,但当我试图理解你发布的代码可能发生了什么时,我要做这些观察。首先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转储CS:0008的时候,这意味着CS被设置为值0008 (= CODE_SEG)。DS选择器为0,这是不寻常的,因为在JMP之后将其设置为DATA_SEG (0x10),但是ESSS段选择器被设置为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
当我使用-b32选项(强制NDISASM解码为32位目标)转储带有NDISASM的测试代码时,它解码为:
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=d88e0010EAX的低16位被移动到ES所以ES=0x0010匹配你的BOCHS输出ES:0010。类似的事情也适用于SS的设置。BP设为0,这在BOCHS输出BP:0000中得到确认。这个指令导致错误和崩溃:

0000000D  0900              or [eax],eax

or [eax],eaxor ds:[eax],eax相同。[eax]有对DS的隐式引用。将此指令与BOCHS输出进行比较:

0x0000000000007d2f>> or dword ptr ds:[eax], eax : 0900

啊哈,这就是这个不寻常的指令的来源(您可以忽略DWORD PTR)。被错误解码的指令试图使用指向NULL (0x0000)描述符的DS。这会导致处理器故障,以及随后由BOCHS和状态转储报告的错误。


正如我在注释中所述,在BOCHS中使用内部调试器非常有价值,特别是在调试引导加载程序和内核时。如果您在调试器中一步一步地执行引导加载程序指令,您可能会发现您的FAR JMP to init_pm按预期工作。然后,您将观察到正在执行的意外指令,最终导致处理器故障。

相关内容

  • 没有找到相关文章

最新更新