[SOLVED] Long Mode: Interrupt problem

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
sevobal
Member
Member
Posts: 63
Joined: Sun Oct 22, 2006 7:11 am

[SOLVED] Long Mode: Interrupt problem

Post by sevobal »

Hi,
I've written a kernel which starts in 16-Bit Mode, than enters 32-Bit Mode and loads a GDT and IDT (both work perfectly) and finaly switches to long mode and load a valid 64-Bit GDT and IDT. So far so good. I print some characters on the screen to verify, that long mode ist working. But now I've a problem.
GDT64 is working perfectly and IDT64 seems to be valid, because the CPU doesn't generates a fault. But my IRQ handling doesn't seem to work in long mode. If I force a protection fault to test handling nothing happens: the system just hangs, doesn't execute the handler code and doesn't reboot.
I don't know where the problem could be.

Code: Select all

;some 16 Bit code here
.
.
.
    call InitA20
    
    xor ebx,ebx
    mov bx,ds                       ; BX=segment
    shl ebx,4                       ; BX="linear" address of segment base
    mov eax,ebx
    mov [gdt2 + 2],ax               ; set base address of 32-bit segments
    mov [gdt3 + 2],ax
    mov [gdt4 + 2],ax               
    mov [gdt5 + 2],ax
    mov [gdt1_x64 + 2],ax       ; set base address of 64-bit segments
    mov [gdt2_x64 + 2],ax
    shr eax,16
    mov [gdt2 + 4],al
    mov [gdt3 + 4],al
    mov [gdt4 + 4],al
    mov [gdt5 + 4],al
    mov [gdt1_x64 + 4],al
    mov [gdt2_x64 + 4],al
		
    mov [gdt2 + 7],ah
    mov [gdt3 + 7],ah
    mov [gdt4 + 7],ah
    mov [gdt5 + 7],ah
    mov [gdt1_x64 + 7],ah
    mov [gdt2_x64 + 7],ah

    lea eax,[gdt + ebx]
    mov [gdtr + 2],eax
    lea eax,[gdt_x64 + ebx]
    mov [gdtr_x64 + 2],eax
        
    lea eax,[start_of_idt + ebx] 
    mov [idt_pointer + 2],eax
    lea eax,[start_of_idt_X64 + ebx] 
    mov [idt_pointer_X64 + 2],eax

; Enbaling Protected Mode, init pic, testing interrupts, init IA32e-Mode and jumping to a 64-Bit Segment
.
.
.
    jmp SYS_CODE_SEL_X64:do_long

[BITS 64]

do_long:
    xor edi,edi
    xor esi,esi

    mov ax, SYS_DATA_SEL_X64
    mov ds, ax
    mov ss, ax

    mov ax,LINEAR_SEL_X64
    mov es,ax

    mov rsp, 0x0000000000010000
   	
    lidt[idt_pointer_X64]			; load IDT register

    mov byte [0xB8000], 'L'	                ;works

    ; generate protection fault to test irq handling
    mov ax, 0x1234
    mov es, ax

hang_x64:
    hlt
    jmp hang_x64
    
;***********************
;* Include Files x64 *
;***********************
%include "./system/x64/basic/idt.inc"
And here is my "idt.inc"

Code: Select all

%macro mkirqring0_x64 2 ;make interrupt for ring 0 (operatingsystem)
        dw %1  ;offset (the handler are all below 0x10000)
        dw %2  ;selector
        db 0
        db 10001110b
        dw 0
        dd 0
%endmacro

;*************************************
;* Die IDT mit und ihre Deskriptoren *
;*************************************
start_of_idt_X64:

;*****************************
;* Interrupts für Exceptions *
;*****************************
mkirqring0_x64 IRQ_00h_x64,SYS_CODE_SEL_X64
mkirqring0_x64 IRQ_01h_x64,SYS_CODE_SEL_X64
mkirqring0_x64 IRQ_02h_x64,SYS_CODE_SEL_X64
mkirqring0_x64 IRQ_03h_x64,SYS_CODE_SEL_X64
mkirqring0_x64 IRQ_04h_x64,SYS_CODE_SEL_X64
mkirqring0_x64 IRQ_05h_x64,SYS_CODE_SEL_X64
mkirqring0_x64 IRQ_06h_x64,SYS_CODE_SEL_X64
mkirqring0_x64 IRQ_07h_x64,SYS_CODE_SEL_X64 
mkirqring0_x64 IRQ_08h_x64,SYS_CODE_SEL_X64
mkirqring0_x64 IRQ_09h_x64,SYS_CODE_SEL_X64 
mkirqring0_x64 IRQ_0ah_x64,SYS_CODE_SEL_X64
mkirqring0_x64 IRQ_0bh_x64,SYS_CODE_SEL_X64
mkirqring0_x64 IRQ_0ch_x64,SYS_CODE_SEL_X64
mkirqring0_x64 IRQ_0dh_x64,SYS_CODE_SEL_X64
mkirqring0_x64 IRQ_0eh_x64,SYS_CODE_SEL_X64
dq 0                            ;Interrupt 0f reserved
dq 0
mkirqring0_x64 IRQ_10h_x64,SYS_CODE_SEL_X64
mkirqring0_x64 IRQ_11h_x64,SYS_CODE_SEL_X64
TIMES 28 dq 0                   ;ignore 14 Intel reserved interrupts 

;*******************************
;* Interrupts für die Hardware *
;*******************************
mkirqring0_x64 IRQ_20h_x64,SYS_CODE_SEL_X64 ; Timer
mkirqring0_x64 IRQ_21h_x64,SYS_CODE_SEL_X64 ; Keyboard
mkirqring0_x64 IRQ_22h_x64,SYS_CODE_SEL_X64 ; unbenutzt
mkirqring0_x64 IRQ_23h_x64,SYS_CODE_SEL_X64 ; Com 2
mkirqring0_x64 IRQ_24h_x64,SYS_CODE_SEL_X64 ; Com 1
mkirqring0_x64 IRQ_25h_x64,SYS_CODE_SEL_X64 ; LPT 2
mkirqring0_x64 IRQ_26h_x64,SYS_CODE_SEL_X64 ; FDD
mkirqring0_x64 IRQ_27h_x64,SYS_CODE_SEL_X64 ; LPT 1
mkirqring0_x64 IRQ_28h_x64,SYS_CODE_SEL_X64 ; CMOS Real Time Clock
mkirqring0_x64 IRQ_29h_x64,SYS_CODE_SEL_X64 ; unbenutzt
mkirqring0_x64 IRQ_2ah_x64,SYS_CODE_SEL_X64 ; unbenutzt
mkirqring0_x64 IRQ_2bh_x64,SYS_CODE_SEL_X64 ; unbenutzt
mkirqring0_x64 IRQ_2ch_x64,SYS_CODE_SEL_X64 ; PS / 2 Mouse
mkirqring0_x64 IRQ_2dh_x64,SYS_CODE_SEL_X64 ; unbenutzt
mkirqring0_x64 IRQ_2eh_x64,SYS_CODE_SEL_X64 ; unbenutzt
mkirqring0_x64 IRQ_2fh_x64,SYS_CODE_SEL_X64 ; unbenutzt

end_of_idt_X64:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; now for the IDT pointer
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
idt_pointer_X64:
  dw end_of_idt_X64 - start_of_idt_X64 - 1
  dq start_of_idt_X64

;exceptions
IRQ_00h_x64:
  mov byte [0xB8000], 'X'
  jmp $ 
.
.
.
;Hardware Interrupts
IRQ_21h_x64:
  mov byte [0xB8000], '+'
  mov     al,20H             ;send End-Of-Interrupt signal
  out     20H,al             
  iret
.
.
.
I don't know what's wrong, maybe some of you can help me!
Last edited by sevobal on Fri Sep 19, 2008 5:50 am, edited 1 time in total.
ru2aqare
Member
Member
Posts: 342
Joined: Fri Jul 11, 2008 5:15 am
Location: Hungary

Re: Long Mode: Interrupt problem

Post by ru2aqare »

sevobal wrote:

Code: Select all

    ; generate protection fault to test irq handling
    mov ax, 0x1234
    mov es, ax
hang_x64:
    hlt
    jmp hang_x64
If I remember correctly, in long mode the processor ignores the cs: ds: es: segment registers, and doesn't generate a fault. So basically your code is turned into an infinite loop with interrupts disabled.
sevobal
Member
Member
Posts: 63
Joined: Sun Oct 22, 2006 7:11 am

Re: Long Mode: Interrupt problem

Post by sevobal »

And how can I fire up an exception to test my code?
ru2aqare
Member
Member
Posts: 342
Joined: Fri Jul 11, 2008 5:15 am
Location: Hungary

Re: Long Mode: Interrupt problem

Post by ru2aqare »

sevobal wrote:And how can I fire up an exception to test my code?
Division by zero? Works miracles :)

I finished extending my boot loader to support x64 as well a few days ago (it was surprisingly easy), and I used a divison-by-zero exception to test whether exceptions are correctly handled. Correct handling and routing of IRQs to v86 mode was tested by faking a timer interrupt (int 20h).

Also you may want to load a TSS descriptor into the task register. If you want to use interrupt stack redirection, then you will need a valid TSS.
sevobal
Member
Member
Posts: 63
Joined: Sun Oct 22, 2006 7:11 am

Re: Long Mode: Interrupt problem

Post by sevobal »

Hey thank you. A division by zero worked create (Handler code gets executed).

Now I've tried to reenable hardware interrupts:

Code: Select all

                mov al, 0x11
	        mov dx, 0x20
		out dx, al
		
		mov al, 0x11
		mov dx, 0xA0
		out dx, al
		
		mov al, 0x20
		mov dx, 0x21
		out dx, al
		
		mov al, 0x28
		mov dx, 0xA1
		out dx, al
		
		mov al, 0x04
		mov dx, 0x21
		out dx, al
		
		mov al, 0x02
		mov dx, 0xA1
		out dx, al
		
		mov al, 0x01
		mov dx, 0x21
		out dx, al
		
		mov al, 0x01
		mov dx, 0xA1
		out dx, al
		
		mov al, 0x00
		mov dx, 0x21
		out dx, al
		
		mov al, 0x00
		mov dx, 0xA1
		out dx, al
	        sti
But after executing "sti" the cpu fires up an "Alignment Check Exception" (Interrupt 17 (0x11). Now I'm more than confused, cause this methode worked perfect in protected mode and also the handler are the same (just placed them in 64-Bit IDT as you can see above).

Any idea...it's a damn confusing error.
ru2aqare
Member
Member
Posts: 342
Joined: Fri Jul 11, 2008 5:15 am
Location: Hungary

Re: Long Mode: Interrupt problem

Post by ru2aqare »

sevobal wrote:Hey thank you. A division by zero worked create (Handler code gets executed).

Now I've tried to reenable hardware interrupts:
...

But after executing "sti" the cpu fires up an "Alignment Check Exception" (Interrupt 17 (0x11). Now I'm more than confused, cause this methode worked perfect in protected mode and also the handler are the same (just placed them in 64-Bit IDT as you can see above).

Any idea...it's a damn confusing error.
Maybe your handler is not 8-byte aligned, and alignment check (AC flag, bit 20? in rflags) is enabled. I have never encountered this exception, however all my code is aligned to 16 bytes.

What do you mean by "handlers are the same"?
sevobal
Member
Member
Posts: 63
Joined: Sun Oct 22, 2006 7:11 am

Re: Long Mode: Interrupt problem

Post by sevobal »

The handler is of course not the same as the 32-bit handler. I've used a new IDT (64-Bit) with new handlers as you can see in my first posting. All I mean is, that the handler code itself (e.g. reading the keyboard buffer) is the same 8)
ru2aqare
Member
Member
Posts: 342
Joined: Fri Jul 11, 2008 5:15 am
Location: Hungary

Re: Long Mode: Interrupt problem

Post by ru2aqare »

sevobal wrote:The handler is of course not the same as the 32-bit handler. I've used a new IDT (64-Bit) with new handlers as you can see in my first posting. All I mean is, that the handler code itself (e.g. reading the keyboard buffer) is the same 8)
What does the Bochs debugger log say, when you reach the sti instruction and execute a few more?
User avatar
Dex
Member
Member
Posts: 1444
Joined: Fri Jan 27, 2006 12:00 am
Contact:

Re: Long Mode: Interrupt problem

Post by Dex »

Are you using

Code: Select all

iretq
To return from int's ?
sevobal
Member
Member
Posts: 63
Joined: Sun Oct 22, 2006 7:11 am

Re: Long Mode: Interrupt problem

Post by sevobal »

Yes I'm using iretq. Here is an example of a handler:

Code: Select all

IRQ_20h_x64:
  mov byte [0xB80C2], 'R'
  mov     al,20H             ;send End-Of-Interrupt signal
  out     20H,al
  iretq
I also would like to post a bochs log, but my kernel is loaded via pxe and I wasn't able to get pxe working with bochs. I'll try to write a simple floppy bootloader, so I can post a bochs log.
sevobal
Member
Member
Posts: 63
Joined: Sun Oct 22, 2006 7:11 am

Re: Long Mode: Interrupt problem

Post by sevobal »

Executing the Interrupts manually (int 0x00, int 0x01...instructions) I found out that only interrupt 0x00 seems to work. All others didn't work (interrupt 0x11 fired up). So I decided to post my idt code again, maybe someone find a mistake:

Code: Select all

%macro mkirqring0_x64 2 ;make interrupt for ring 0 (operatingsystem)
        dw %1  ;offset
        dw %2  ;selector
        db 0
        db 10001110b
        dw 0
        dd 0
%endmacro

%macro mkirqring0_x641 2 ;make interrupt for ring 0 (operatingsystem)
        dw %1  ;offset
        dw %2  ;selector
        db 0
        db 10001111b
        dw 0
        dd 0
%endmacro

;*************************************
;* Die IDT mit und ihre Deskriptoren *
;*************************************
start_of_idt_X64:

;*****************************
;* Interrupts für Exceptions *
;*****************************
mkirqring0_x64 IRQ_00h_x64,SYS_CODE_SEL_X64
mkirqring0_x64 IRQ_01h_x64,SYS_CODE_SEL_X64
mkirqring0_x64 IRQ_02h_x64,SYS_CODE_SEL_X64
mkirqring0_x64 IRQ_03h_x64,SYS_CODE_SEL_X64
mkirqring0_x64 IRQ_04h_x64,SYS_CODE_SEL_X64
mkirqring0_x64 IRQ_05h_x64,SYS_CODE_SEL_X64
mkirqring0_x64 IRQ_06h_x64,SYS_CODE_SEL_X64
mkirqring0_x64 IRQ_07h_x64,SYS_CODE_SEL_X64 ;Interrupt 07 fpu nicht installiert
mkirqring0_x64 IRQ_08h_x64,SYS_CODE_SEL_X64
mkirqring0_x64 IRQ_09h_x64,SYS_CODE_SEL_X64 ;Interrupt 09 fpu register?berlauf und keyboard events
mkirqring0_x64 IRQ_0ah_x64,SYS_CODE_SEL_X64
mkirqring0_x64 IRQ_0bh_x64,SYS_CODE_SEL_X64
mkirqring0_x64 IRQ_0ch_x64,SYS_CODE_SEL_X64
mkirqring0_x64 IRQ_0dh_x64,SYS_CODE_SEL_X64
mkirqring0_x64 IRQ_0eh_x64,SYS_CODE_SEL_X64
dq 0
dq 0
mkirqring0_x64 IRQ_10h_x64,SYS_CODE_SEL_X64
mkirqring0_x64 IRQ_11h_x64,SYS_CODE_SEL_X64
TIMES 28 dq 0 

;*******************************
;* Interrupts für die Hardware *
;*******************************
mkirqring0_x641 IRQ_20h_x64,SYS_CODE_SEL_X64 ; Timer
mkirqring0_x641 IRQ_20h_x64,SYS_CODE_SEL_X64 ; Tastatur
mkirqring0_x641 IRQ_20h_x64,SYS_CODE_SEL_X64 ; unbenutzt
mkirqring0_x641 IRQ_20h_x64,SYS_CODE_SEL_X64 ; Com 2
mkirqring0_x641 IRQ_20h_x64,SYS_CODE_SEL_X64 ; Com 1
mkirqring0_x641 IRQ_20h_x64,SYS_CODE_SEL_X64 ; LPT 2
mkirqring0_x641 IRQ_20h_x64,SYS_CODE_SEL_X64 ; FDD
mkirqring0_x641 IRQ_20h_x64,SYS_CODE_SEL_X64 ; LPT 1
mkirqring0_x641 IRQ_20h_x64,SYS_CODE_SEL_X64 ; CMOS Real Time Clock
mkirqring0_x641 IRQ_20h_x64,SYS_CODE_SEL_X64 ; unbenutzt
mkirqring0_x641 IRQ_20h_x64,SYS_CODE_SEL_X64 ; unbenutzt
mkirqring0_x641 IRQ_20h_x64,SYS_CODE_SEL_X64 ; unbenutzt
mkirqring0_x641 IRQ_20h_x64,SYS_CODE_SEL_X64 ; PS / 2 Maus (u.U.)
mkirqring0_x641 IRQ_20h_x64,SYS_CODE_SEL_X64 ; unbenutzt
mkirqring0_x641 IRQ_20h_x64,SYS_CODE_SEL_X64 ; unbenutzt
mkirqring0_x641 IRQ_20h_x64,SYS_CODE_SEL_X64 ; unbenutzt

end_of_idt_X64:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; now for the IDT pointer
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
idt_pointer_X64:
  dw end_of_idt_X64 - start_of_idt_X64 - 1
  dq start_of_idt_X64

IRQ_00h_x64:
  mov byte [0xB80A0], 'A'
  jmp $ 

IRQ_01h_x64:
  mov byte [0xB80A2], 'B'
  jmp $

IRQ_02h_x64:
  mov byte [0xB80A4], 'C'
  jmp $

IRQ_03h_x64:
  mov byte [0xB80A6], 'D'
  jmp $

IRQ_04h_x64:
  mov byte [0xB80A8], 'E'
  jmp $

IRQ_05h_x64:
  mov byte [0xB80AA], 'F'
  jmp $

IRQ_06h_x64:
  mov byte [0xB80AC], 'G'
  jmp $
  
IRQ_07h_x64:
  mov byte [0xB80AE], 'H'
  jmp $

IRQ_08h_x64:
  mov byte [0xB80B0], 'I'
  jmp $

IRQ_09h_x64:
  mov byte [0xB80B2], 'J'
  jmp $

IRQ_0ah_x64:
  mov byte [0xB80B4], 'K'
  jmp $

IRQ_0bh_x64:
  mov byte [0xB80B6], 'L'
  jmp $

IRQ_0ch_x64:
  mov byte [0xB80B8], 'M'
  jmp $

IRQ_0dh_x64:
  push rax
  mov byte [0xB80BA], 'N'
  pop rax
  jmp $

IRQ_0eh_x64:
  mov byte [0xB80BC], 'O'
  jmp $

IRQ_0fh_x64:
  mov byte [0xB80BC], 'O'
  jmp $

IRQ_10h_x64:
  mov byte [0xB80BE], 'P'
  jmp $

IRQ_11h_x64:
  mov byte [0xB80C0], 'Q'
  cli
  hlt
  jmp $
  	
IRQ_20h_x64:
  mov byte [0xB80C2], 'R'
  mov     al,20H             ;send End-Of-Interrupt signal
  out     20H,al
  jmp $

ru2aqare
Member
Member
Posts: 342
Joined: Fri Jul 11, 2008 5:15 am
Location: Hungary

Re: Long Mode: Interrupt problem

Post by ru2aqare »

sevobal wrote:Executing the Interrupts manually (int 0x00, int 0x01...instructions) I found out that only interrupt 0x00 seems to work. All others didn't work (interrupt 0x11 fired up). So I decided to post my idt code again, maybe someone find a mistake:

Code: Select all

...
I think your macro to generate an IDT entry is flawed. Try this:

Code: Select all

%macro mkirqring0_x64 2 ;make interrupt for ring 0 (operatingsystem)
        dw %1  ;offset
        dw %2  ;selector
        db 0
        db 10001110b
        dw 0
        dq 0 ; <-- quadword
%endmacro
An IDT entry in long mode is sixteen bytes long. Your macro only generated twelve bytes, hence all but the first entry were misaligned.
sevobal
Member
Member
Posts: 63
Joined: Sun Oct 22, 2006 7:11 am

Re: [SOLVED] Long Mode: Interrupt problem

Post by sevobal »

Thanks man. You're totaly right. What a bad mistake. Now everything works as it should :)
Post Reply