首页 \ 问答 \ 跳入保护模式时出现三重故障(Triple fault when jumping into protected mode)

跳入保护模式时出现三重故障(Triple fault when jumping into protected mode)

我正在开发一个启动加载程序,它将在切换到保护模式后启动到一个简单的内核。 我在第四章或第五章的某个地方使用了本文作为教程。 从理论上讲,它应该以16位实模式启动,将内核加载到内存中,切换到32位保护模式并开始执行内核代码。

但是,当我切换到保护模式并跳远或跳转到另一个段时,它会出现三重故障。 这是主引导扇区代码:

[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

以下是切换到保护模式的代码,其中三重故障:

[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的一个问题。 我是操作系统开发和引导加载程序的新手。 有关如何解决这个问题的任何建议?


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

Here is the 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

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?


原文:https://stackoverflow.com/questions/36562268
更新时间:2023-01-15 18:01

最满意答案

使用领带

#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };

{   package File;

    sub new {
        bless ['hello'], shift
    }

    sub text {
        my $self = shift;
        if (@_) {
            $self->[0] = shift;
        } else {
            return $self->[0]
        }
    }
}

{   package FileVar;
    use Tie::Scalar;
    use parent qw( -norequire Tie::StdScalar );

    sub TIESCALAR {
        my ($class, $obj) = @_;
        bless \$obj, $class
    }

    sub FETCH {
        my $self = shift;
        ${$self}->text()
    }

    sub STORE {
        die 'Read only!';

        # Or, do you want to change the object by changing the var, too?
        my ($self, $value) = @_;
        ${$self}->text($value);
    }

}

my $file = 'File'->new();
tie my $text, 'FileVar', $file;
say $text;
$file->text('goodbye');
say $text;

# Die or change the object:
$text = 'Magic!';
say $file->text;

Use tie:

#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };

{   package File;

    sub new {
        bless ['hello'], shift
    }

    sub text {
        my $self = shift;
        if (@_) {
            $self->[0] = shift;
        } else {
            return $self->[0]
        }
    }
}

{   package FileVar;
    use Tie::Scalar;
    use parent qw( -norequire Tie::StdScalar );

    sub TIESCALAR {
        my ($class, $obj) = @_;
        bless \$obj, $class
    }

    sub FETCH {
        my $self = shift;
        ${$self}->text()
    }

    sub STORE {
        die 'Read only!';

        # Or, do you want to change the object by changing the var, too?
        my ($self, $value) = @_;
        ${$self}->text($value);
    }

}

my $file = 'File'->new();
tie my $text, 'FileVar', $file;
say $text;
$file->text('goodbye');
say $text;

# Die or change the object:
$text = 'Magic!';
say $file->text;

相关问答

更多
  • 总结这个问题:你想使用符号加载模块而不是硬编码。 使用常量应该为你做到这一点: constant some-module = 'Some::Module'; use ::(some-module); 您还可以使用require在运行时加载模块,这将允许运行时计算值: my $some-module = 'Some::Module'; require ::($some-module); ::($some-module).foo 在尝试use-ok之后做到这一点是有道理的。 要获得额外的功劳,您可能会发现本 ...
  • 默认的Perl对象模型没有类属性的概念。 并且没有类似“当创建新子类时运行此代码”的钩子。 相反,基类可以使用类名作为键来维护计数器的哈希: package A; my %counters; sub new { my ($class) = @_; my $counter = $counters{$class} //= Counter->new; $counter++; return bless {} => $class; } sub get_counter { my ($self_o ...
  • $self可能是一个对象,包中的所有subs都可以作为方法调用。 然后: my $object = Your::Class->new(...); $object->foo(42); 在foo方法中,对象将是第一个参数: sub foo { my ($self, $meaning_of_life) = @_; say "mStrEndDate = $self->{mStrEndDate}"; ...; } 笔记: 你通常不应该在构造函数中重新使用$self 。 如果编写超类来支持继承,那么$ ...
  • TWEAK在对象被BUILD初始化后运行,这就是提供奇数编号阵列会爆炸的地方。 将强制移动到BUILD时间,事情应按预期工作: class Local::Class { has %.set; submethod BUILD (:$set) { %!set := $set.SetHash; } } 如果您可以将强制SetHash()参数类型与自动属性初始化结合使用会很好,但这会失败,因为sigil是属性名称的一部分,如果您想接受非参数,则参数不能为% -sigilled - 关联类型。 ...
  • 你可以在Perl中使用正则表达式匹配。 无需调用外部“grep”命令。 $var =~ /foo/; 请阅读文档perlrequick ,了解如何在变量中搜索模式。 同样令人感兴趣的是Perl自己的grep 。 $var="foobar"; if ( $var =~ /foo/){ print "found foo\n"; } you can just use regex matching in Perl. No need to call external "grep" command. $var ...
  • 使用领带 : #!/usr/bin/perl use warnings; use strict; use feature qw{ say }; { package File; sub new { bless ['hello'], shift } sub text { my $self = shift; if (@_) { $self->[0] = shift; } else { ...
  • 这可能没有您要求的那么多封装,但您可以local哈希的属性。 输出"CarlLennyCarl" sub Object::new { bless { _name => $_[1] }, $_[0] } } sub Object::name { $_[0]->{_name} } my $obj = Object->new("Carl"); print $obj->name; { local $obj->{_name} = "Lenny"; print $obj->name; } print ...
  • $prefs->{PREFS}->{1}->{OTHERS_POST}; $prefs->{PREFS}->{1}->{OTHERS_POST};

相关文章

更多

最新问答

更多
  • 您如何使用git diff文件,并将其应用于同一存储库的副本的本地分支?(How do you take a git diff file, and apply it to a local branch that is a copy of the same repository?)
  • 将长浮点值剪切为2个小数点并复制到字符数组(Cut Long Float Value to 2 decimal points and copy to Character Array)
  • OctoberCMS侧边栏不呈现(OctoberCMS Sidebar not rendering)
  • 页面加载后对象是否有资格进行垃圾回收?(Are objects eligible for garbage collection after the page loads?)
  • codeigniter中的语言不能按预期工作(language in codeigniter doesn' t work as expected)
  • 在计算机拍照在哪里进入
  • 使用cin.get()从c ++中的输入流中丢弃不需要的字符(Using cin.get() to discard unwanted characters from the input stream in c++)
  • No for循环将在for循环中运行。(No for loop will run inside for loop. Testing for primes)
  • 单页应用程序:页面重新加载(Single Page Application: page reload)
  • 在循环中选择具有相似模式的列名称(Selecting Column Name With Similar Pattern in a Loop)
  • System.StackOverflow错误(System.StackOverflow error)
  • KnockoutJS未在嵌套模板上应用beforeRemove和afterAdd(KnockoutJS not applying beforeRemove and afterAdd on nested templates)
  • 散列包括方法和/或嵌套属性(Hash include methods and/or nested attributes)
  • android - 如何避免使用Samsung RFS文件系统延迟/冻结?(android - how to avoid lag/freezes with Samsung RFS filesystem?)
  • TensorFlow:基于索引列表创建新张量(TensorFlow: Create a new tensor based on list of indices)
  • 企业安全培训的各项内容
  • 错误:RPC失败;(error: RPC failed; curl transfer closed with outstanding read data remaining)
  • C#类名中允许哪些字符?(What characters are allowed in C# class name?)
  • NumPy:将int64值存储在np.array中并使用dtype float64并将其转换回整数是否安全?(NumPy: Is it safe to store an int64 value in an np.array with dtype float64 and later convert it back to integer?)
  • 注销后如何隐藏导航portlet?(How to hide navigation portlet after logout?)
  • 将多个行和可变行移动到列(moving multiple and variable rows to columns)
  • 提交表单时忽略基础href,而不使用Javascript(ignore base href when submitting form, without using Javascript)
  • 对setOnInfoWindowClickListener的意图(Intent on setOnInfoWindowClickListener)
  • Angular $资源不会改变方法(Angular $resource doesn't change method)
  • 在Angular 5中不是一个函数(is not a function in Angular 5)
  • 如何配置Composite C1以将.m和桌面作为同一站点提供服务(How to configure Composite C1 to serve .m and desktop as the same site)
  • 不适用:悬停在悬停时:在元素之前[复制](Don't apply :hover when hovering on :before element [duplicate])
  • 常见的python rpc和cli接口(Common python rpc and cli interface)
  • Mysql DB单个字段匹配多个其他字段(Mysql DB single field matching to multiple other fields)
  • 产品页面上的Magento Up出售对齐问题(Magento Up sell alignment issue on the products page)