I'm trying to write a kernel in 64-bit mode. However there is this tiny teensy problem: If an interrupt fires, I get a General Protection Fault. If I do a software interrupt, a GPF fires. If I do a far jump, another General Protection Fault fires.
Concluding:
There could be something wrong with the IDT, but a far jump also fails, so the GDT should be bodgered too. Or might I have done something wrong with paging and that's why my privilige level is low? Could it be a problem with not yet having setup the TSS?
My question obviously is: Can anyone point me out the problem?
Here is all the relevant code, I'm sorry to bother anyone with this stupid problem, I've been having this for over a month and I think that someone else looking at the code would greatly help
One more explanation of how booting works in my world:
1. get everything from floppy and get into PM.
2. In PM set up paging to have the kernel use the the highest 1/512 bit of memory (the last entry in PML4), then map first 2mb of linear address space to first 2 mb of physical memory and map the kernel linear address space to 2-4 mb physical memory.
3. in long mode, we still have our important GDTR pointing to the GDT in the first mb of memory, so we try to load a new GDT. (this is where I think there is a problem)
GDT for long mode:
Code: Select all
org kernel_mem_start + kcode_start - 200000h
NULL_SELECTOR = 0
LONG_SELECTOR = 1 shl 3 ; 64-bit code selector (ring 0)
align 16
GDTR_long: ; Global Descriptors Table Register
dw 3*8-1 ; limit of GDT (size minus one)
dq GDT_long ; linear address of GDT
align 16
GDT_long:
rq 1 ; null desciptor
dw 0FFFFh,0,9A00h,0AFh ; 64-bit code desciptor
db 0,0,0,0,0,9Ah,0A0h,0 ; my own 64-bit code descriptor
rq 1 ; null descriptor
long_cs_far_pointer: ;
dt LONG_SELECTOR:long_code_segment
Code: Select all
lgdt [cs:GDTR_long]
call print_segment_registers
jmp far [long_cs_far_pointer] ;
long_code_segment:
; long_cs_far_pointer was defined on the last line of previous code window.
Code: Select all
;--------------------------------------------
; Constants
sys_table_entry = 111b ; a table entry for kernel mem.
sys_pde = 110000011b ; a 2 mb kernel PDE.
kdata_pde = 1 shl 63 + sys_pde ; a 2 mb data PDE.
;--------------------------------------------
;--------------------------------------------
sys_TE_create: ; (proc)
; Makes a pml4 or PDP entry, by:
; 1. nulling the destination table.
; 2. building the entry.
; IN: edx:eax - base address.
; OUT: edx:eax - pt entry.
; USES: eax, edx, edi (saved)
;--------------------------------------------
push eax
push edi
mov edi, eax ; edi = page table destination pointer.
xor eax, eax ; eax = 0
mov ecx, 4096/4 ; amount of dw's to zeroe.
rep stosd ; fill with zeroes.
pop edi
pop eax
and edx, 0FFFFFh
and eax, not 0FFFh
add eax, sys_table_entry
ret
;--------------------------------------------
sys_PDE_create: ; (proc)
; Makes PD entry. (doesn't put it anywhere)
; IN: edx:eax - base address.
; OUT: edx:eax - PDE.
; USES: eax, edx
;--------------------------------------------
and edx, 0FFFFFh ; clear upper bits.
and eax, not 1FFFFh ; clear lower bits.
add eax, sys_pde ; set protection, present, caching etc..
ret
Interrupts: (probably not wrong, how could my gpf otherwise handle it?)
Code: Select all
;--------------------------------------------
; Interrupt Management.
; In this file the management interface for
; subscribing to interrupts is done.
;--------------------------------------------
;--------------------------------------------
; Constants
;--------------------------------------------
;--------------------------------------------
; Variables
align 16
IDTR:
dw 256-1 ; The size of the interrupt table.
idt: ; the location of the interrupt table.
dq 0 ; Reserve 8 bytes for the location of table.
align 16
exception_gate: ; exception gate, points to exception_handler.
dw exception_handler and 0FFFFh,LONG_SELECTOR
dw 8F00h,(exception_handler shr 16) and 0FFFFh
dd (exception_handler shr 32) and 0FFFFFFFFh ; !! 'dd' so doubleword.
dd 0 ; dd too.
interrupt_gate: ; Interrupt gate, points to interrupt_handler.
dw interrupt_handler and 0FFFFh,LONG_SELECTOR
dw 8E00h,(interrupt_handler shr 16) and 0FFFFh
dd (interrupt_handler shr 32) and 0FFFFFFFFh ; !! 'dd' so doubleword.
dd 0 ; dd too.
;--------------------------------------------
;--------------------------------------------
interrupts_initialize: ; (proc)
; Summary:
; Initializes the IDT, all exception gates
; point to a generic exception handler, just
; as the interrupt gates point to an interrupt
; handler.
; IN: nothing.
; OUT: nothing.
; USES: Saves everything.
;--------------------------------------------
multipush rcx, rdi, rsi
mov rdi, 1
call kmem_getpa ; get a page aligned page.
mov [IDTR + 2], rax ; Get proper address for IDTR
lidt [IDTR] ; initialize idtr register.
mov rdi, [idt] ; create IDT (at linear address 0)
mov rcx,21
.exception_gates: ; make gates for exception handlers
mov rsi,exception_gate
movsq
movsq
loop .exception_gates
mov rcx,256-21
.interrupt_gates: ; make gates for the other interrupts
mov rsi,interrupt_gate
movsq
movsq
loop .interrupt_gates
.return:
sti
multipop rcx, rdi, rsi
ret
;--------------------------------------------
interrupt_subscribe: ; (proc)
; Summary:
; IN: rdi - vector to subscribe to.
; rsi - pointer to handler.
; OUT: nothing.
; USES: Saves everything.
;--------------------------------------------
multipush rdi, rsi, rax
mov rax, [idt]
shl rdi, 4 ; rdi = rdi * 16.
add rdi, rax ; rdi points to interrupt gate.
mov rax, rsi ; rax = vector.
stosw ; offset 15 .. 0
add rdi, 4 ; rdi = *igate + 2 + 4
shr rax, 16
stosw ; offset 31..16
shr rax, 16 ; rax = rsi shr 32 = upper dw of pointer.
stosd ; offset 63..32
multipop rdi, rsi, rax
ret
Far jump error code: FF80h
KB error: 040Bh (int 90h)
int 55h: 02AAh
Is there a good 64 bit virtual machine that I can use to debug this problem? I have 32-bits windows and 64-bit Linux.
Thank you