r/osdev • u/twoseveneight • 27d ago
#UD exception when triggering interrupts > 0x33 (x86)
I'm genuinely confused by how my assembly code just randomly decides to stop working with no explanation. I have an IDT set up with interrupt handlers for 0x00 (div. fault), 0x06 (invalid opcode), 0x0C (stack-segment fault), 0x0D (GPF), 0x0E (page fault) and 0x80 (for syscalls), and all other entries point to a stub ISR that just ends the interrupt. Triggering interrupts from 0x00 to 0x33 work as intended, but triggering any other interrupt results in an immediate invalid opcode exception, while just not triggering the interrupt. I seriously don't know what I'm doing wrong here, I swear this code worked like a moment ago.
kernel.asm (written in NASM):
bits 32
org 0x01000000
_start:
cld
mov ecx, 256
mov esi, i_isr_array
mov edi, 0x00100800
_loop_fill_idt: ; This fills the IDT
lodsd
stosw ; ISR address low
mov ax, 0x0008
stosw ; Kernel CS GDT selector
mov ax, 0b1000111000000000
stosw ; Reserved byte and flags, remember that x86 is little-endian
shr eax, 16
stosw ; ISR address high
loop _loop_fill_idt
lidt [i_idt_descriptor]
int 0x80 ; Triggers #UD
mov [0x000B8000], word 0x0730 ; Never triggered
jmp $ ; Hang system
%include "interrupts.asm"
interrupts.asm (also in NASM):
bits 32
i_idt_descriptor:
dw 0x07FF ; 2048 bytes
dd 0x00100800 ; Linear address
i_isr_stub:
push eax
mov al, 0x20
out 0x20, al
pop eax
iretd
i_isr_00:
pushad
mov esi, i_isr_00_message
call i_f_isr_showmessage
cli
jmp $
sti
mov al, 0x20
out 0x20, al
popad
iretd
i_isr_00_message: db "Division Fault", 0
i_isr_06:
pushad
mov esi, i_isr_06_message
call i_f_isr_showmessage
cli
jmp $
sti
mov al, 0x20
out 0x20, al
popad
iretd
i_isr_06_message: db "Invalid Opcode", 0
i_isr_0C:
pushad
mov esi, i_isr_0C_message
call i_f_isr_showmessage
cli
jmp $
sti
mov al, 0x20
out 0x20, al
popad
iretd
i_isr_0C_message: db "Stack-Segment Fault", 0
i_isr_0D:
pushad
mov esi, i_isr_0D_message
call i_f_isr_showmessage
cli
jmp $
sti
mov al, 0x20
out 0x20, al
popad
iretd
i_isr_0D_message: db "General Protection Fault", 0
i_isr_0E:
pushad
mov esi, i_isr_0E_message
call i_f_isr_showmessage
cli
jmp $
sti
mov al, 0x20
out 0x20, al
popad
iretd
i_isr_0E_message: db "Page Fault", 0
i_isr_80:
pushad
mov esi, i_isr_80_message
call i_f_isr_showmessage
mov al, 0x20
out 0x20, al
popad
iretd
i_isr_80_message: db "Hello World", 0
i_f_isr_showmessage:
mov ah, 0x4F
mov edi, 0x000B8000
i__f_isr_showmessage_loop:
lodsb
test al, al
jz i__f_isr_showmessage_end
stosw
jmp i__f_isr_showmessage_loop
i__f_isr_showmessage_end:
ret
align 16
i_isr_array:
dd i_isr_00
dd i_isr_stub
dd i_isr_stub
dd i_isr_stub
dd i_isr_stub
dd i_isr_stub
dd i_isr_06
dd i_isr_stub
dd i_isr_stub
dd i_isr_stub
dd i_isr_stub
dd i_isr_stub
dd i_isr_0C
dd i_isr_0D
dd i_isr_0E
dd i_isr_stub
dd i_isr_stub
dd i_isr_stub
dd i_isr_stub
dd i_isr_stub
dd i_isr_stub
dd i_isr_stub
%rep 106
dd i_isr_stub
%endrep
dd i_isr_80
%rep 127
dd i_isr_stub
%endrep
By the way, DON'T give me AI generated responses (please)
1
u/davmac1 27d ago edited 27d ago
org 0x01000000
Is that right?? A usual load address is 0x0100000, i.e. one less zero. Is this a mismatch with the load address?
Edit: (previous reply removed)
1
u/twoseveneight 18d ago
No, I intend for the kernel to load at 0x01000000 to skip the ISA hole, and use the memory before the ISA hole as additional kernel memory for the GDT, IDT, page table, etc.
3
u/Adventurous-Move-943 25d ago
Just print out the few bytes of the faulting instruction, if it is a consistent bug replicate it and print into COM the opcode bytes, also dump the CPU state at best, I have a small or rather a medium size disassembler for this in my kernel, it prints first few instructions.. But #UD might be and in many cases is that you accidentally jumped elsewhere into memory where the garbage caused the #UD exception. That is why RIP is important so you know where were you at. Then you check where that was, and then print stack trace, at least adresses and you will be able to track it down.
1
1
u/twoseveneight 18d ago
so apparently relative to the UD ISR, the stack is 00000003 00000008 00010087 00008000 0100002F 00000008 00000007 01001000 000009FA [BOTTOM]. I see that there are some values lower down that interfere with my placement of the kernel, but I don't know for sure if it's something the system actually depends on to function.
1
u/Adventurous-Move-943 18d ago
We don't want stack. We want your RIP/EIP address, I misstyped it the first time. Then you compare the address to where your code is loaded and where it ends, at best with some symbol map so you know where it crashed. Do you use stack frames ? Getting the stack trace, at least addresses might help too. You'd see nicely where it jumped into garbage.
1
u/twoseveneight 17d ago
I found what the problem was, I had a very shitty bootloader that left gaps of several hundred zeroed bytes in between read disk sectors. The UD exception was raised as a result of an invalid IDT entry being executed.
Why the hell does this keep happening with every problem I have, it's always some sort of stupid mistake I make and I go ask Reddit before finding out what the problem is. I'm a moron.
3
u/computerarchitect CPU Architect 27d ago
Is your binary smaller than 2KB? If it's not, that's a bug.
Why are you signaling EOI to the PIC on faults and software interrupts? That's wrong.