切换到保护模式并进行远程跳转后出错

时间:2016-09-19 15:34:18

标签: assembly x86 bootloader fasm bochs

我正在使用 FASM (Flat Assembler)编写启动加载程序。我在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

编辑 - 代码现在有效!!

1 个答案:

答案 0 :(得分:3)

TL; DR :要将更改bits = 32修改为use32

FASM 为以32位模式运行的处理器生成指令。 第1.1.4节“输出格式”中的FASM Documentation个状态

  

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

在将 NASM 代码转换为 FASM 并且 FASM 未接受bits = 32时,您似乎使用了bits 32 >。 bits=32将名为bits的常量值设置为值32.它不会告诉 FASM 生成32位模式下处理器使用的指令。尽管bits = 32组合时没有错误,但它没有达到您的预期。

通过不使用use32,您告诉 FASM init_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(= {{1} })。 DS 选择器为0,这是不常见的,因为在 JMP 之后,您将其设置为DATA_SEG(0x10)但是 ES SS 段选择器设置为0x10。这一切都表明CODE_SEG代码已被执行但不知何故最终没有按预期执行。

此时我意识到你已经编写了init_pm,它有效地将常量设置为值32.它没有告诉 FASM 生成将针对CPU的代码以32位模式执行。

考虑到这一点,我决定接受指令并让汇编程序将它们编码为16位模式:

bits = 32

当我使用init_pm: mov ax,DATA_SEG mov ds,ax mov ss,ax mov es,ax mov ebp, 0x90000 选项使用 NDISASM 转储我的测试代码时(强制 NDISASM 解码为32位目标),它将其解码到:

-b32

错误的解码首先进行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 。这解释了为什么在您的 BOCHS 转储中有mov eax, 0xd88e0010 EAX 的低16位已移至 ES ,因此ES = 0x0010与 BOCHS 输出EAX=d88e0010匹配。类似的事情适用于 SS 被设置。 BP 设置为0,在 BOCHS 输出ES:0010中确认。该指令导致故障并崩溃:

BP:0000

0000000D 0900 or [eax],eax or [eax],eax相同。 or ds:[eax],eax隐含引用 DS 。将此指令与 BOCHS 输出进行比较:

[eax]

AHA,这是这个不寻常的指令来自的地方(你可以忽略0x0000000000007d2f>> or dword ptr ds:[eax], eax : 0900 )。错误解码的指令尝试使用指向NULL(0x0000)描述符的 DS 。这会导致处理器故障,以及 BOCHS 和状态转储报告的后续错误。

正如我在评论中所述,在 BOCHS 中使用内部调试器非常有用,尤其是在调试引导加载程序和内核时。如果您在调试器中逐步完成了一个引导加载程序指令,则可能会发现 FAR JMP DWORD PTR的工作正常。然后,您将观察到正在执行的意外指令,最终导致processor fault