Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Assembly 进入保护模式并执行远jmp后出现三重故障_Assembly_X86_Nasm_Bootloader_Osdev - Fatal编程技术网

Assembly 进入保护模式并执行远jmp后出现三重故障

Assembly 进入保护模式并执行远jmp后出现三重故障,assembly,x86,nasm,bootloader,osdev,Assembly,X86,Nasm,Bootloader,Osdev,我正在写一个爱好操作系统内核。每次内核进入保护模式并跳转到其保护模式部分时,Bochs都会出现三重故障,并给出以下信息: 00014918914i[BIOS ] Booting from 0000:7c00 00016345509e[CPU0 ] jump_protected: gate type 0 unsupported 00016345509e[CPU0 ] interrupt(): gate descriptor is not valid sys seg (vector=0 x0d

我正在写一个爱好操作系统内核。每次内核进入保护模式并跳转到其保护模式部分时,Bochs都会出现三重故障,并给出以下信息:

00014918914i[BIOS  ] Booting from 0000:7c00
00016345509e[CPU0  ] jump_protected: gate type 0 unsupported
00016345509e[CPU0  ] interrupt(): gate descriptor is not valid sys seg (vector=0
x0d)
00016345509e[CPU0  ] interrupt(): gate descriptor is not valid sys seg (vector=0
x08)
00016345509i[CPU0  ] CPU is in protected mode (active)
00016345509i[CPU0  ] CS.mode = 16 bit
00016345509i[CPU0  ] SS.mode = 16 bit
00016345509i[CPU0  ] EFER   = 0x00000000
00016345509i[CPU0  ] | EAX=60000011  EBX=00000002  ECX=00090011  EDX=00000000
00016345509i[CPU0  ] | ESP=00001000  EBP=00000000  ESI=000e01e7  EDI=00000200
00016345509i[CPU0  ] | IOPL=0 id vip vif ac vm RF nt of df if tf sf zf af PF cf
00016345509i[CPU0  ] | SEG sltr(index|ti|rpl)     base    limit G D
00016345509i[CPU0  ] |  CS:2000( 0004| 0|  0) 00020000 0000ffff 0 0
00016345509i[CPU0  ] |  DS:2000( 0005| 0|  0) 00020000 0000ffff 0 0
00016345509i[CPU0  ] |  SS:09e0( 0005| 0|  0) 00009e00 0000ffff 0 0
00016345509i[CPU0  ] |  ES:2000( 0005| 0|  0) 00020000 0000ffff 0 0
00016345509i[CPU0  ] |  FS:0000( 0005| 0|  0) 00000000 0000ffff 0 0
00016345509i[CPU0  ] |  GS:0000( 0005| 0|  0) 00000000 0000ffff 0 0
00016345509i[CPU0  ] | EIP=000003da (000003da)
00016345509i[CPU0  ] | CR0=0x60000011 CR2=0x00000000
00016345509i[CPU0  ] | CR3=0x00000000 CR4=0x00000000
00016345509i[CPU0  ] 0x00000000000003da>> jmpf 0x0008:03e2 : EAE2030800
00016345509p[CPU0  ] >>PANIC<< exception(): 3rd (13) exception with no resolutio
n

我对保护模式有点缺乏经验,因此对它只了解一点。有人能帮我吗?

你的GDT没有问题。问题在于lgdt之后我们将做什么

有两种解决方案

不要在内核中使用段 这是我的内核代码,应该从2000h:0000h加载并使用NASM组装

内核加载的地址实际上是0x20000,但您决定使用段寄存器将该基址存储为0x2000,这对于实模式操作系统很好,但对于保护模式则不太好,因为符号main的偏移量为零。您可以更改内核加载到可以用16位整数表示的某个位置的地址,例如0x1000,然后更改内核源代码,如下所示:

[ORG 0x1000]    ; The generic offset to the address
                ; of the label (e.g. main -> main+0x1000)
; Your code...
这种方法的缺点是,0x7C00处的引导加载程序可能会被内核覆盖,就像大小增长过大一样,因此需要将引导加载程序移动到其他地方以避免这种情况

在跳远过程中添加内核的基址 从内核源代码

gdtCode: ; allow full access to memory as code (read-only, executable)
dw 0xffff       ; Limit 0:15
dw 0x0000       ; Base 0:15
db 0x00         ; Base 16:23
db 10011010b    ; Access Byte: ring 0, executable, can only be executed from ring 0, readable
db 11001111b    ; Limit 16:19 + Flags: 32-bit protected mode, page granularity
db 0x00         ; Base 24:31
内核代码段声明声明,当内核在0x20000处加载时,基址为零。因此,您可以更改GDT的基础,也可以简单地更改跳远代码:

jmp CODESEGMENT:main+0x20000
注意:正如我们所知,这部分代码可能会被其他人引用,他们可能在切换之前看不到cli,因此请将这一行放在lgdt之前


使用bochs调试器验证GDT。您的实际模式段值似乎是错误的,因此您加载了错误的GDT,但无法真正分辨,因为这不是一个。正如Jester指出的,这不是一个MCVE。如果你也发布你的引导加载程序,那会有帮助的。但是,请注意,您需要GDT指针中的线性地址,而不是realmode offset。gdtPointer将gdtStart作为GDT的基础。问题是,它与从0x2000:0x0000开始的实模式段有关。0x2000:0x0000是线性地址0x2000I注意到了一些东西,它可能与Jester关于片段的评论有关。您说内核在2000h:0000h加载,然后向我们显示代码和boxhs输出。在bochs输出中,它表示CS设置为0x0800 CS:0800 0004 | 0 | 0。我的问题是-您是否在0x0800:0x0000或0x2000:0x0000处加载了该代码?BOCHs输出根本不表明您是从0x2000:0x0000运行的。@MichaelPetch在调试期间的某一点上,我稍微更改了引导加载程序代码,以便从0800:0000加载内核。我已经编辑了Bochs输出。@MichaelPetch您关于向gdtStart添加20000h的建议,而远跳偏移量给了我以下信息:write_virtual_checks:write over limit,r/wHe已经有一个CLI在LGDT上方几行:call printString_16 CLI mov si,msgSuccess。中断已经停止了——这不是他的问题的原因。请参阅问题和重复问题下的注释,了解错误所在以及如何修复。@MichaelPetch现在已更改了答案,但您在代码中找到的cli并不专用于切换到保护模式。如果在切换之前添加cli,则可能有助于提高代码的可读性。如果您知道中断已被禁用,请在cli距离较远时添加注释。在汇编语言中,您不会为了人类的利益而使用冗余指令;没有编译器来优化它们。@PeterCordes是的,你是对的。在实模式下,我们不需要任何冗余的代码或字节,因为实模式下RAM的寻址空间对我们来说太小了。如果每个字节和/或时钟周期都无关紧要,那么您可以使用C或其他语言编写代码,并让编译器进行代码生成。
gdtCode: ; allow full access to memory as code (read-only, executable)
dw 0xffff       ; Limit 0:15
dw 0x0000       ; Base 0:15
db 0x00         ; Base 16:23
db 10011010b    ; Access Byte: ring 0, executable, can only be executed from ring 0, readable
db 11001111b    ; Limit 16:19 + Flags: 32-bit protected mode, page granularity
db 0x00         ; Base 24:31
jmp CODESEGMENT:main+0x20000
; NOTE: This operation needs you to have interrupt flag clear