进入保护模式时出现三重故障 [英] Triple fault when jumping into protected mode

查看:268
本文介绍了进入保护模式时出现三重故障的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个引导加载程序,它将在进入保护模式后引导到一个简单的内核中.我使用了本文作为教程,在第四,五章的某个地方.从理论上讲,它应该以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 of dw 0x0 ; Base (16-23)? Note the difference is that dw is changed to db. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆