我正在写一个爱好操作系统内核。每次内核进入保护模式并跳转到其保护模式部分时,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:
我对保护模式缺乏经验,因此对此有所了解。有人可以帮我吗?
答案 0 :(得分:-1)
Your GDT has no problem. The problem is about what we will do after lgdt
.
There are two solutions for you
This is my kernel code, supposed to be loaded from 2000h:0000h and assembled using NASM
The address where your kernel is loaded is literally 0x20000
, but you decide to use segment registers to store this base as 0x2000
, which is good for a real mode OS but not quite for protected mode because the symbol main
has an offset of zero. You may change the address where your kernel is loaded to somewhere that can be expressed in 16 bit integer (e.g. 0x1000), and then change your kernel source like this:
[ORG 0x1000] ; The generic offset to the address
; of the label (e.g. main -> main+0x1000)
; Your code...
The disadvantage of this way is that your bootloader at 0x7C00
may be overrided by the kernel as if the size grows too much, so you need to move the boot loader to somewhere else in order to avoid this
From your kernel source
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
The declaration of kernel code segment states that the base address is zero while your kernel is loaded at 0x20000
. So you can either change the base of your GDT or simply change your code of far jump:
jmp CODESEGMENT:main+0x20000
NOTE: As we know, this portion of code may be referred by other people who might not see the cli
much before the switch, so please put this line before lgdt
; NOTE: This operation needs you to have interrupt flag clear