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

3
我正在使用Flat Assembler(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

与您的问题无关,但是mov bp,0x9000 mov sp,bp不是设置实模式堆栈指针的适当方式。您应该设置_SS_后跟SP。如果您想要堆栈位于0x0000:0x9000或物理地址0x9000,则执行xor ax,ax mov ss,ax mov sp,0x9000 - Michael Petch
使用调试器将是您能够确定 jmp CODE_SEG:init_pm 是否成功的最佳选择。如果没有看到 bios.asm、print32.asm、gdt.asm 和完整的引导加载程序代码,很难说。 - Michael Petch
其实我刚刚注意到你代码中可能存在问题的一点。你写成了 bits = 32,应该改为 bits 32,去掉等号。很可能你的指令仍然被编码为16位,而实际上应该是32位,在保护模式下运行时会被错误地解码。bits = 32 只是设置了一个名为 bits 的常量值为32,这不是你想要的结果。试试 bits 32,看看会发生什么,并让我知道结果。 - Michael Petch
好的,我现在可能无法发布它,因为我没有使用电脑。所以我会尽快发布代码并让您知道。谢谢。 - Anish Sharma
1
我现在非常确定问题出在他们展示的代码中,这是因为他们没有正确设置32位指令生成。我认为发生的事情是,在init_pm之后的代码被错误解码,并且奇怪的dword ptr ds:[eax],eax指令是从这些指令的部分解码而来的:mov ebp,0x90000 mov esp,ebp - Michael Petch
显示剩余8条评论
1个回答

3
TL;DR:将bits = 32更改为use32即可修复。
为了让生成适用于32位模式的处理器的指令。FASM Documentation第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相匹配。类似的事情也适用于设置SSBP设置为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

AHA,这就是这个不寻常指令的来源(您可以忽略DWORD PTR)。错误解码的指令尝试使用指向NULL(0x0000)描述符的DS。这会导致处理器故障,并引起BOCHS和状态转储中报告的后续错误。
正如我在评论中所述,使用BOCHS内部调试器对于调试引导程序和内核非常有价值。如果你在调试器中逐步执行引导程序指令,你可能会发现你的FAR JMPinit_pm按预期工作。然后,你会观察到意外执行的指令,最终导致处理器故障

@AneeshSharma。你的新问题应该是一个新的问题,并且可能需要使用调试器来解决,但是为什么你的打印不起作用,我看不到你实际上更新了视频内存的地方。似乎你可能错过了在je print_string32_end之后添加mov [edx], ax的步骤。 - Michael Petch
那是我犯的一个愚蠢错误。我添加了它,但它仍然不起作用。 - Anish Sharma
是的,它确实打印了那个。 - Anish Sharma
@AneeshSharma 现在你使用了 use32,你确定 bios.asm 中的 16 位代码有 use16 吗? - Michael Petch
我已经添加了问题。你能检查一下吗 - http://stackoverflow.com/questions/39608643/printing-text-in-protected-mode-doesnt-work - Anish Sharma
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接