跳入保护模式时出现三重故障(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 thejmp CODE_SEG:init_pm
instruction, it idles there and does not triple fault. When I place it after that instruction, within the labelinit_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
最满意答案
使用领带 :
#!/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;
相关问答
更多-
TCP/IP模型是一个________。[2023-10-02]
a -
下列中不属于面向对象的编程语言的是?[2022-05-30]
a -
总结这个问题:你想使用符号加载模块而不是硬编码。 使用常量应该为你做到这一点: constant some-module = 'Some::Module'; use ::(some-module); 您还可以使用require在运行时加载模块,这将允许运行时计算值: my $some-module = 'Some::Module'; require ::($some-module); ::($some-module).foo 在尝试use-ok之后做到这一点是有道理的。 要获得额外的功劳,您可能会发现本 ...
-
Perl类属性继承(Perl class attribute inheritance)[2022-12-26]
默认的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};