用LGDT加载GDT会引发一般保护错误

问题描述

我一直在开发Bootloader,当我尝试用LGDT指令加载GDT时,它会引发一般保护错误,我不确定如何处理。

bootloader / gdt.asm

gdt_start: ; don't remove the labels,they're needed to compute sizes and jumps
gdt_null:
    dq 0


; GDT for code segment. base = 0x00000000,length = 0xfffff
gdt_code: 
    dw 0xffff    ; segment length,bits 0-15
    dw 0x0       ; segment base,bits 0-15
    db 0x0       ; segment base,bits 16-23
    db 10011010b ; flags (8 bits)
    db 11001111b ; flags (4 bits) + segment length,bits 16-19
    db 0x0       ; segment base,bits 24-31

; GDT for data segment. base and length identical to code segment
gdt_data:
    dw 0xffff
    dw 0x0
    db 0x0
    db 10010010b
    db 11001111b
    db 0x0

gdt_end:

; GDT descriptor
gdt_descriptor:
    dw gdt_end - gdt_start - 1 ; size (16 bit),always one less of its true size
    dd gdt_start ; address (32 bit)

; define some constants for later use
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start

[bits 16]
switch_to_pm:
    cli 
    lgdt [gdt_descriptor]  ; THIS is where the general protection fault occours 
    mov eax,0xff
    mov [0xa006],eax
    mov eax,cr0
    or eax,0x1 ; 3. set 32-bit mode bit in cr0
    mov cr0,eax
    jmp CODE_SEG:init_pm ; 4. far jump by using a different segment

; somehow forgot to copy this in
[bits 32]
init_pm: ; we are now using 32-bit instructions
    mov ax,DATA_SEG ; 5. update the segment registers
    mov ds,ax
    mov ss,ax
    mov es,ax
    mov fs,ax
    mov gs,ax

    mov eax,0xee
    mov [0xa000],eax

    mov ebp,0x90000 ; 6. update the stack right at the top of the free space
    mov esp,ebp

    call ready ; 7. Call a well-known label with useful code

bootloader.asm

[BITS 16]
[ORG 0x7c00]
    jmp 0:start

%include "bootloader/gdt.asm"
[BITS 16]

start:
    ; setup video mode
    mov ah,00h
    mov al,13h
    int 10h
    mov dx,0
    call print_dbg_x
    ; LOAD the system
    mov ah,02h
    mov al,20
    mov ch,0
    mov cl,02h
    mov dh,0
    mov dl,0
    mov ebx,PROGRAM
    int 13h
    call print_dbg_x
    ; next setup the gdt
    call print_dbg_x
    cli
    mov ax,0x2401
    int 0x15 ; enable A20 bit

    call switch_to_pm

load_kern_err:
    mov ah,0Ch
    mov al,0xC
    xor bh,bh
    inc cx
    int 10h
    hlt
print_dbg_x:
    mov ah,0xF
    xor bh,bh
    inc cx
    int 10h
    ret

[BITS 32]
ready:
    mov eax,0xff
    mov [0xa000],eax

    mov esp,090000h

    mov ax,DATA_SEG 
    mov ds,ax

    mov ebp,0x90000
    mov esp,ebp

    call PROGRAM
    jmp $

PROGRAM equ 0x1000

times 510 - ($ - $$) db 0
dw 0AA55h

switch_to_pm在需要加载GDT并切换到pmode时由引导加载程序跳转到。在此之前,它从FDD加载内核并将其放入地址0x1000中。我还没有加载IDT,因为pmode的内核可以。

我使用nasm,它被编译成二进制文件(它包含在主引导程序中)。

Bochs在重启之前将其报告为异常,可能是#GPF的来源

(0).[14992474] [0x000000007c40] 0000:0000000000007c40 (unk. ctxt): jmpf 0x0008:7c45 ; ea457c0800

显然,CPU进入了保护模式,因此jumpf出了点问题。

00014992474i[CPU0  ] CPU is in protected mode (active)
00014992474i[CPU0  ] CS.mode = 16 bit
00014992474i[CPU0  ] SS.mode = 16 bit
00014992474i[CPU0  ] EFER   = 0x00000000
00014992474i[CPU0  ] | EAX=60000011  EBX=00000000  ECX=00090004  EDX=00000000
00014992474i[CPU0  ] | ESP=0000ffd4  EBP=00000000  ESI=000e0000  EDI=0000ffac
00014992474i[CPU0  ] | IOPL=0 id vip vif ac vm RF nt of df if tf sf zf af PF cf
00014992474i[CPU0  ] | SEG sltr(index|ti|rpl)     base    limit G D
00014992474i[CPU0  ] |  CS:0000( 0004| 0|  0) 00000000 0000ffff 0 0
00014992474i[CPU0  ] |  DS:0010( 0005| 0|  0) 00000100 0000ffff 0 0
00014992474i[CPU0  ] |  SS:0010( 0005| 0|  0) 00000100 0000ffff 0 0
00014992474i[CPU0  ] |  ES:0010( 0005| 0|  0) 00000100 0000ffff 0 0
00014992474i[CPU0  ] |  FS:0010( 0005| 0|  0) 00000100 0000ffff 0 0
00014992474i[CPU0  ] |  GS:0010( 0005| 0|  0) 00000100 0000ffff 0 0
00014992474i[CPU0  ] | EIP=00007c40 (00007c40)
00014992474i[CPU0  ] | CR0=0x60000011 CR2=0x00000000
00014992474i[CPU0  ] | CR3=0x00000000 CR4=0x00000000

这是引导程序将从第二个扇区加载的代码,内核是在编译时与此代码链接的C外部代码。

kentry.asm

[BITS 32]

global _start
_start:
    mov ebp,ebp
    extern kernel
    call kernel
    ret

global idt_load
extern idtp
idt_load:
    lidt [idtp]
    ret

我先编译引导程序,然后编译内核,然后将代码放在一起,然后再用cat将引导程序放在内核中的二进制文件中。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...