首页 文章

进入保护模式并执行远jmp后的三重故障[重复]

提问于
浏览
1

这个问题在这里已有答案:

我正在写一个业余爱好OS内核 . 每次内核进入保护模式并跳转到其保护模式部分时,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

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

; Puck Kernel
; Version 1.00.001a

; ----------------------
; REAL MODE PORTION
; ----------------------
[bits 16]
jmp main_16

msgStart            db  "Puck Kernel in Real Mode", 0dh, 0ah
                    db  "Version 1.00.001a", 0dh, 0ah
                    db  "Coded by Weedboi6969#4098, named by his friend <Ushiwaka>#6536", 0dh, 0ah
                    db  "<C> Copyleft 2018 Weedboi6969#4098. All wrongs reserved.", 0dh, 0ah
                    db  "THIS IS RUNNING IN REAL MODE!", 0dh, 0ah
                    db  0dh, 0ah
                    db  0
endl                db  0dh, 0ah, 0
msgA20Status        db  "A20 line status                     : ", 0
msgA20EnableBIOS    db  "Enable A20 using INT 15h            : ", 0
msgA20EnableKBC     db  "Enable A20 using keyboard controller: ", 0 
msgA20EnableFast    db  "Enable A20 using FAST A20           : ", 0
msgA20Failed        db  "ERROR: Cannot enable A20 line! System halted.", 0  
msgDisableINT       db  "Disable maskable interrupt          : ", 0
msgDisableNMI       db  "Disable non-maskable interrupt      : ", 0
msgEnterPMode       db  "Entering protected mode...", 0
msgSuccess          db  "SUCCESS!", 0dh, 0ah, 0
msgFailed           db  "FAILED!", 0dh, 0ah, 0
msgEnabled          db  "ENABLED!", 0dh, 0ah, 0
msgDisabled         db  "DISABLED!", 0dh, 0ah, 0

; GLOBAL DESCRIPTOR TABLE
; =======================
gdtPointer:
dw gdtEnd - gdtStart - 1
dd gdtStart

gdtStart:
dq 0 ; reserved
CODESEGMENT     equ $ - gdtStart
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
DATASEGMENT     equ $ - gdtStart
gdtData: ; allow full access to memory as data (writable, non-executable)
dw 0xffff       ; Limit 0:15
dw 0x0000       ; Base 0:15
db 0x00         ; Base 16:23
db 10010010b    ; Access Byte: ring 0, non-executable, segment grows up, writable
db 11001111b    ; Limit 16:19 + Flags: 32-bit protected mode, page granularity
db 0x00         ; Base 24:31
gdtEnd:

; REAL MODE PROCEDURES
; ====================
printString_16:
pusha
mov ah, 0eh
.next:
lodsb
cmp al, 0
jz .done
int 10h
jmp .next
.done:
popa
ret

a20Check:
pushf
push ds
push es
push di
push si

cli

xor ax, ax
mov es, ax

not ax
mov ds, ax

mov di, 500h
mov si, 510h

mov al, byte [es:di]
push ax

mov al, byte [ds:si]
push ax

mov byte [es:di], 00h
mov byte [ds:si], 0ffh

cmp byte [es:di], 0ffh

pop ax
mov byte [ds:si], al

pop ax
mov byte [es:di], al

mov ax, 0
je .exit

mov ax, 1
.exit:
pop si
pop di
pop es
pop ds
popf

ret

a20CheckTimeout:
pusha
xor ax, ax
int 1ah
mov bp, dx
add bp, 54 ; 3 seconds timeout
.wait:
call a20Check
push ax
xor ax, ax
int 1ah
pop ax
cmp dx, bp
jae .done
.done:
popa
ret

a20Enable_KBC:
cli

call .wait
mov al, 0adh
out 64h, al

call .wait
mov al, 0d0h
out 64h, al

call .wait2
in al, 60h
push ax

call .wait
mov al, 0d1h
out 64h, al

call .wait
pop ax
or al, 2
out 60h, al

call .wait
mov al, 0aeh
out 64h, al

call .wait
sti

call a20CheckTimeout
ret
.wait:
in al, 64h
test al, 2
jnz .wait
ret
.wait2:
in al, 64h
test al, 1
jz .wait2
ret

a20Enable_Fast:
in al, 92h
test al, 2
jnz .done
or al, 2
and al, 0feh
out 92h, al
.done:
call a20CheckTimeout
ret

a20Enable_BIOS:
mov ax, 2403h
int 15h
jb .done
cmp ah, 0
jnz .done

mov ax, 2402h
int 15h
jb .done
cmp ah, 0
jnz .done

cmp al, 1
jz .done

mov ax, 2401h
int 15h
.done:
call a20Check
ret

nmiDisable:
push ax
in al, 70h
or al, 80h
out 70h, al
pop ax
ret

; REAL MODE MAIN PROGRAM
; ======================
main_16:
mov ax, cs
mov ds, ax
mov es, ax

mov ax, 2
int 10h

mov si, msgStart
call printString_16

mov si, msgA20Status
call printString_16
call a20Check
cmp ax, 1
je a20AlreadyEnabled
mov si, msgDisabled
call printString_16

mov si, msgA20EnableBIOS
call printString_16
call a20Enable_BIOS
cmp ax, 1
je a20EnabledByProc
mov si, msgFailed
call printString_16

mov si, msgA20EnableKBC
call printString_16
call a20Enable_KBC
cmp ax, 1
je a20EnabledByProc
mov si, msgFailed
call printString_16

mov si, msgA20EnableFast
call printString_16
call a20Enable_Fast
cmp ax, 1
je a20EnabledByProc
mov si, msgFailed
call printString_16

mov si, msgA20Failed
call printString_16
jmp $ ; friendly halt because Bochs sometimes hates hlt

a20AlreadyEnabled:
mov si, msgEnabled
call printString_16
jmp a20Enabled
a20EnabledByProc:
mov si, msgSuccess
call printString_16
a20Enabled:
mov si, msgDisableINT
call printString_16
cli
mov si, msgSuccess
call printString_16

mov si, msgDisableNMI
call printString_16
call nmiDisable
mov si, msgSuccess
call printString_16

mov si, msgEnterPMode
call printString_16

lgdt [gdtPointer]
mov eax, cr0
or al, 1
mov cr0, eax

jmp CODESEGMENT:main

jmp $

; ----------------------
; PROTECTED MODE PORTION
; ----------------------
[bits 32]
main:

这是我的引导程序:

; Puck Bootloader (PRML)
; Version 1.00.001 (started on Mar 29 2018)
; Based on MikeOS bootloader

BITS 16

jmp short start
nop

; Description table
bpbOEMLabel                 db  "PILOT   "
bpbBytesPerSector           dw  512
bpbSectorsPerCluster        db  1
bpbReservedForBoot          dw  1
bpbNumberOfFATs             db  2
bpbRootDirEntries           dw  224
bpbLogicalSectors           dw  2880
bpbMediumByte               db  0F0h
bpbSectorsPerFAT            dw  9
bpbSectorsPerTrack          dw  18
bpbSides                    dw  2
bpbHiddenSectors            dd  0
bpbLargeSectors             dd  0
bpbDriveNo                  dw  0
bpbSignature                db  41
bpbVolumeID                 dd  13371337h
bpbVolumeLabel              db  "PILOTLOADER"
bpbFileSystem               db  "FAT12   "

start:
mov ax, 7c0h
add ax, 544
cli
mov ss, ax
mov sp, 4096
sti

mov ax, 7c0h
mov ds, ax

; cmp dl, 0
; je dlNoChange
; mov [bootdev], dl
; mov ah, 8
; int 13h
; jc fatalError
; and cx, 3fh
; mov [bpbSectorsPerTrack], cx
; movzx dx, dh
; inc dx
; mov [bpbSides], dx

; dlNoChange:
; mov eax, 0

mov ax, ds
mov es, ax
mov bx, buffer

mov ax, 19
call lbaConvert

mov ah, 2
mov al, 14

pusha
readRootDir:
popa
pusha

stc
int 13h

jnc searchDir
call resetFloppy
jnc readRootDir

jmp fatalError

searchDir:
popa
mov ax, ds
mov es, ax
mov di, buffer

mov cx, word [bpbRootDirEntries]
readRootEntry: ; will be used in 2nd stage
xchg cx, dx
mov si, loaderName
mov cx, 11
repe cmpsb
je foundFile

add di, 32

xchg dx, cx
loop readRootEntry

jmp noLoader
foundFile:
push ds
pop es
mov ax, word [es:di+0fh]
mov word [cluster], ax

mov ax, 1
call lbaConvert

mov di, buffer
mov bx, di

mov ah, 2
mov al, 9
pusha
readFAT:
popa
pusha

stc
int 13h

jnc readFAT_done
call resetFloppy
jnc readFAT
fatalError:
mov si, msgFatal
call printString
xor ax, ax
int 16h
xor ax, ax
int 19h
hlt ; just in case
msgFatal    db  "Fatal error, press any key to reboot...", 0
noLoader:
mov si, msgLoader
call printString
xor ax, ax
int 16h
xor ax, ax
int 19h
hlt ; just in case
msgLoader   db  "Loader not found", 0

readFAT_done:
popa
mov ax, 2000h
mov es, ax
xor bx, bx

mov ah, 2
mov al, 1

push ax
loadFileSector:
mov ax, word [cluster]
add ax, 31
call lbaConvert

mov ax, 2000h
mov es, ax
mov bx, word [pointer]

pop ax
push ax

stc
int 13h

jnc calculateNextCluster

call resetFloppy
jmp loadFileSector

calculateNextCluster:
push ax
pop ax
mov ax, [cluster]
mov dx, 0
mov bx, 3
mul bx
mov bx, 2
div bx
mov si, buffer
add si, ax
mov ax, word [ds:si]

or dx, dx
jz .even
.odd:
shr ax, 4
jmp short .nextCluster
.even:
and ax, 0fffh
.nextCluster:
mov word [cluster], ax
cmp ax, 0ff8h
jae endLoad

add word [pointer], 512
jmp loadFileSector

endLoad:
pop ax
; mov si, msgLoad
; call printString
; xor ax, ax
; int 16h
mov dl, byte [bootdev] ; give 2nd stage information about boot device
jmp 2000h:0000h ; jump to 2nd stage
; msgLoad   db  "2nd stage loaded!", 0

printString:
pusha
mov ah, 0eh
.next:
lodsb
cmp al, 0
je .done
int 10h
jmp short .next
.done:
popa
ret

resetFloppy:
push ax
push dx
mov ax, 0
mov dl, byte [bootdev]
stc
int 13h
pop dx
pop ax
ret

lbaConvert:
push bx
push ax

mov bx, ax

xor dx, dx
div word [bpbSectorsPerTrack]
inc dl
mov cl, dl
mov ax, bx

xor dx, dx
div word [bpbSectorsPerTrack]
xor dx, dx
div word [bpbSides]
mov dh, dl
mov ch, al

pop ax
pop bx

mov dl, byte [bootdev]
ret

loaderName  db  "KERNEL     "
; msgTest       db "Booting...", 0

bootdev     db  0
cluster     db  0
pointer     db  0


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

buffer:

我对保护模式缺乏经验,因此对此有所了解 . 有人可以帮助我吗?

1 回答

  • -1

    你的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 :;允许完全访问内存作为代码(只读,可执行)
    dw 0xffff;限制0:15
    dw 0x0000;基地0:15
    db 0x00;基地16:23
    db 10011010b;访问字节:环0,可执行,只能从环0执行,可读
    db 11001111b;限制16:19标志:32位保护模式,页面粒度
    db 0x00;基地24:31

    内核代码段的声明表明,在 0x20000 加载内核时,基址为零 . 因此,您可以更改GDT的基础或只是更改远程跳转代码:

    jmp CODESEGMENT:main+0x20000
    

    注意:正如我们所知,这部分代码可能会被其他人推荐,这些人在切换之前可能看不到 cli ,所以请在 lgdt 之前放置这一行 .

    ; NOTE: This operation needs you to have interrupt flag clear
    

相关问题