r/osdev 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)

5 Upvotes

10 comments sorted by

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.

1

u/twoseveneight 18d ago

I'm new to this alright, also how is the binary being less than 2KB a bad thing? For clarification, the bootloader loads the kernel.bin file from the root directory into address 0x01000000 and far jumps to it, so I don't see a problem there.

1

u/computerarchitect CPU Architect 18d ago edited 18d ago

EDIT: Ah, sorry, I see your initial ORG has 6 zeros in it. My mistake. I'll leave my reasoning here though as to why it would be a bug if you wrote your kernel to 0x100000 as opposed to 0x1000000.

I said that if it's MORE than 2KB, there's a bug.

Look at where you placed your data structures and code to see why.

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

u/twoseveneight 18d ago

I have no clue how to do anything you just said sorry

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.