进入保护模式时出现三重故障 [英] Triple fault when jumping into protected mode
问题描述
我正在开发一个引导加载程序,它将在进入保护模式后引导到一个简单的内核中.我使用了本文作为教程,在第四,五章的某个地方.从理论上讲,它应该以16位实模式启动,将内核加载到内存中,切换到32位保护模式,然后开始执行内核代码.
I'm developing a boot loader, which will boot into a simple kernel after switching into protected mode. I used this paper as a tutorial, somewhere in chapter four or five. In theory it is supposed to start in 16-bit real mode, load the kernel into memory, switch to 32-bit protected mode and start executing the kernel code.
但是,当我进入保护模式并跳远或跳到另一段时,它会导致三重故障.这是主要的引导扇区代码:
However, when I switch into protected mode and far jump or jump to another segment, it triple faults. Here is the main boot sector code:
[org 0x7c00]
KERNEL_OFFSET equ 0x1000
mov [BOOT_DRIVE], dl ;Get the current boot drive from the BIOS
mov bp, 0x9000 ;Set up stack, with enough room to grow downwards
mov sp, bp
mov bx, REAL_MODE_MSG
call print_string
call load_kernel
call switch_to_pm
jmp $ ;Jump to current position and loop forever
%include "boot/util/print_string.asm"
%include "boot/util/disk.asm"
%include "boot/gdt/gdt.asm"
%include "boot/util/print_string_pm.asm"
%include "boot/switch_to_pm.asm"
[bits 16]
load_kernel:
mov bx, LOAD_KERNEL_MSG ;Print a message saying we are loading the kernel
call print_string
mov bx, KERNEL_OFFSET ;Set up disk_load routine parameters
mov dh, 15
mov dl, [BOOT_DRIVE]
call disk_load ;Call disk_load
ret
[bits 32]
BEGIN_PM:
mov ebx, PROT_MODE_MSG
call print_string_pm
call KERNEL_OFFSET
jmp $
; Data
BOOT_DRIVE: db 0
REAL_MODE_MSG: db "Started in real mode.", 0
PROT_MODE_MSG: db "Successfully entered 32-bit protected mode.", 0
LOAD_KERNEL_MSG: db "Loading Kernel into memory", 0
; Bootsector padding
times 510-($-$$) db 0
dw 0xaa55
以下是GDT:
;Global Descriptor Table
gdt_start:
gdt_null: ; We need a null descriptor at the start (8 bytes)
dd 0x0
dd 0x0
gdt_code: ; Code segment descriptor
; Base=0x0, Limit=0xfffff
; 1st flags : (present)1 (privilege)00 (descriptor type)1 -> 1001b
; type flags : (code)1 (conforming)0 (readable)1 (accessed)0 -> 1010b
; 2nd flags : (granularity)1 (32 - bit default)1 (64 - bit seg)0 (AVL)0 -> 1100b
dw 0xffff ; Limit (bits 0-15)
dw 0x0 ; Base (0-15)
dw 0x0 ; Base (16-23)
db 10011010b ; 1st flags and type flags
db 11001111b ; 2nd flags and Limit (16-19)
db 0x0 ; Base (24-31)
gdt_data: ; Data segment descriptor
;Same as CSD except for type flags
; (code)0 (expand down)0 (writable)1 (accessed)0 -> 0010b
dw 0xffff ; Limit (bits 0-15)
dw 0x0 ; Base (0-15)
dw 0x0 ; Base (16-23)
db 10010010b ; 1st flags and type flags
db 11001111b ; 2nd flags and Limit (16-19)
db 0x0 ; Base (24-31)
gdt_end:
;GDT Descriptor
gdt_descriptor:
dw gdt_end - gdt_start - 1
dd gdt_start
;Some Constants
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
这是用于切换到保护模式的代码,在该模式下它会导致三重故障:
Here is the code for switching into protected mode, where it triple faults:
[bits 16]
switch_to_pm:
cli
lgdt [gdt_descriptor] ; load the gdt
mov eax, cr0 ; turn pm on
or eax, 0x1
mov cr0, eax
jmp CODE_SEG:init_pm ; THIS IS WHERE THE PROBLEM IS!
[bits 32]
init_pm:
mov ax, DATA_SEG ; Point segment registers to the data
mov ds, ax ; selector defined in the gdt
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ebp, 0x90000 ; Update our stack
mov esp, ebp
call BEGIN_PM ;Move on
当我在某个位置将jmp $
指令置于空闲位置时,恰好在jmp CODE_SEG:init_pm
指令之前,它在那里空闲并且不会造成三重故障.当我将其放置在该指令后的标签init_pm
中时,它会发生三重错误.因此,我相当确定这是原因.我不太清楚为什么,也许这是GDT的问题.我是操作系统开发和引导加载程序的新手.关于如何解决此问题有什么建议吗?
When I place a jmp $
instruction to idle at a certain spot, right before the jmp CODE_SEG:init_pm
instruction, it idles there and does not triple fault. When I place it after that instruction, within the label init_pm
, it does triple fault. So I am fairly sure that it is the cause. I'm not too sure why, maybe it's an issue with the GDT. I am new to operating system development and boot loaders. Any suggestions on how to solve this problem?
推荐答案
Michael Petch在评论中给出了此问题的正确答案.不幸的是,这似乎已经被几个人遗漏了,因为现在已经发布了三个错误的答案,其中两个犯了同样的错误.然后在这里将他的评论发布为答案,以期使其更显眼:
Michael Petch gave the correct answer to this question in the comments. Unfortunately this has seem to been missed by several people as there have now been three incorrect answers posted, two of them making the same mistake. Here then is his comment posted as answer in the hopes that it makes it more visible:
您确定自己的GDT是正确的吗?我认为从粗略的外观中脱颖而出的是,您的每个条目都是9字节(72位). GDT条目为8字节(64位).看来您的意思是
db 0x0 ; Base (16-23)
而不是dw 0x0 ; Base (16-23)
?请注意,不同之处在于dw
更改为db
.错误的GDT条目将产生三重错误.
Are you sure your GDT is correct? I think the thing that stands out upon cursory look is that each of your entries is 9 byte (72 bits). A GDT entry is 8 bytes (64-bits). it appears that maybe you meant
db 0x0 ; Base (16-23)
instead ofdw 0x0 ; Base (16-23)
? Note the difference is thatdw
is changed todb
. Wrong GDT entries would generate a triple fault.
Michael Petch也发表了很好的后续评论,指出了引导加载程序的其他问题:
Michael Petch also made a good followup comment that pointed out other problems with the bootloader:
我还建议您查看我的常规Bootloader提示.您假设输入时DS(数据段)寄存器为零(因为使用org 0x7c00).您应该将其显式设置为零.您还以一种奇怪的方式设置了堆栈.您将SP设置为9000,但未设置SS,这意味着您实际上并不真正知道将堆栈放置在内存中的位置.您应该先设置SS寄存器,然后再设置SP寄存器.我的引导程序提示提供了一个示例.
I'd also recommend looking at my general bootloader tips. You make the assumption that the DS (data segment) register is zero upon entry (since you use org 0x7c00). You should set it to zero explicitly. You also set the stack in an odd way. You set SP to 9000 but you don't set SS which means you don't really know where you are putting the stack in memory. You should set the SS register followed by setting the SP register. My bootloader tips offer an example.
这篇关于进入保护模式时出现三重故障的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!