[x86-64] GPF fires on interrupt and far jump.

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
allih
Posts: 4
Joined: Tue Dec 11, 2007 11:29 am
Location: netherlands

[x86-64] GPF fires on interrupt and far jump.

Post by allih »

Good morning, (for me at least ;) )
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
The code to do a far jump (which GPF's):

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.
Less likely erronous code: (Paging) These are the functions that produce page table entries. (TE = table entry)

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
What does a GPF error code tell you? I got codes around 480h, depending on whether the keyboard or the timer interrupt fired.
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 :)
jtlb
Member
Member
Posts: 29
Joined: Sat May 12, 2007 8:24 am

Post by jtlb »

i had the same problem and i finally choosed to "emulate" it in the GPF handler ie: edit the ip pointer on the stack.
iammisc
Member
Member
Posts: 269
Joined: Thu Nov 09, 2006 6:23 pm

Post by iammisc »

qemu can emulate and x86-64 system.

Also, doesn't long mode get rid of segmentation?
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

Hi,

I use Bochs, which can emulate a 64 bit AMD processor. With this, you will be given a very specific error message about why you got the GPF.

Just one thing - on your interrupt handlers are you using iretq instead of iret? That was one of the causes of a GPF when I got started. Also, you appear to have two code segments in your GDT rather than a code and data segment, according to your own comments (I havent manually checked that your segment descriptors are valid).
Also, doesn't long mode get rid of segmentation?
Almost - you still have to have valid segment descriptors for long mode or compatibility mode, but bounds checking is pretty much disabled.

Cheers,
Adam
allih
Posts: 4
Joined: Tue Dec 11, 2007 11:29 am
Location: netherlands

Post by allih »

thanks for the replies.
AJ, I thought data descriptors weren't needed in long mode? (I'm intending pure 64 bit OS, no 32-bit compatability).

I never even thought of the possibility that Bochs could emulate an x64 on a 32-bit platform. Is this possible? (I tried to find out on the Bochs site, I'm away from my own computer, so can't test it..)
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

Yes - it's possible. In fact, because I do a lot of my development on a 32 bit machine, that's my main way of testing (just run tests occasionally on a 64 bit host to check all is well).

As for the data descriptor thing, I'm just looking that up. Looking through my code, I have a data descriptor but that may just be a throw back to the early stages of my os dev. It's terrible I can't remember that off the top of my head :?

Cheers,
Adam
Last edited by AJ on Wed Mar 05, 2008 6:04 am, edited 1 time in total.
User avatar
JoeKayzA
Member
Member
Posts: 79
Joined: Wed Aug 24, 2005 11:00 pm
Location: Graz/Austria

Post by JoeKayzA »

According to the docs, DS, ES and SS are ignored if you are running from a 64-bit CS. So, it seems you don't even need to load a segment selector there. I haven't tried this though, I've always been running with a 32bit flat data segment in ds and ss - consider it legacy from the 32bit portion of the startup code - it didn't hurt either.
Post Reply