Search code examples
cassemblyx86interruptosdev

registers not loading properly in ISR's


I'm trying to make a simple OS and I'm currently working on setting up the interrupts.
The way it works is that there is an assembly function that pushes all the registers, the interrupt number and a dummy error code (if the cpu doesn't pushes one) to the stack and then the assenbly function calls a C function that takes the registers as a parameter and will handle the interrupt.
Now, I have two problems:

  1. There is some problem with the error code that the cpu pushes to the stack, because every time I make call an exception that has an error code I get a General Protection Fault
  2. I think that the registers aren't pushed to the stack correctly because when I print them they are either set to 0 or to 0x38e7.
    I tried to debug these but bochs is freezing every time I want to view the stack.

assembly function:

%macro ISR_NOERRORCODE 1
global ISR%1:
ISR%1:
    push 0                  ; dummy error code
    push %1                 ; push interrupt number
    jmp isr_common
%endmacro

%macro ISR_ERRORCODE 1
global ISR%1:
ISR%1:
                                ; error code is pushed by the cpu
    push %1                     ; push interrupt number
    jmp isr_common
%endmacro

%include "isr_functions.as"


extern ISR_regsHandler
isr_common:
    pusha

    xor eax, eax
    mov ax, ds
    push eax                ; push ds to the stack

    mov ax, 0x10            ; use kernel segments
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    push esp                ; address of stack
    call ISR_regsHandler        ; call the C function
    add esp, 4

    pop eax                 ; restore all the segments
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    popa
    add esp, 8              ; remove the code error
    iret                    ; special interrupt return

Regsiters definition:

typedef struct {
    // in the reverse order they were pushed to the stack
    u32 ds;                                             // we pushed to the stack
    u32 edi, esi, ebp, cs_kernel, ebx, edx, ecx, eax;   // we pushed with pusha
    u32 interrupt, error;                               // we push interrupt and error is auto (or dummy)
    u32 eip, cs, eflags, esp, ss;                       // pushed auto
} PACKED Registers_t;

C function:

void CDECL ISR_regsHandler(Registers_t* regs){

    } else if (regs->interrupt >= 32) { 
        QemuPrintf("Unhandeled interrupt %d --> 0x%x\n", regs->interrupt, regs->interrupt);
    } else {
        QemuPrintf("Unhandeled exception %x: ", regs->interrupt);
        QemuPrintf("%s\n", g_Exceptions[regs->interrupt]);

        QemuPrintf("Registers values: \neax->%x ebx->%x ecx->%x edx->%x ebp->%x esi->%x edi->%x\n", regs->eax, regs->ebx, regs->ecx, regs->edx, regs->ebp, regs->esi, regs->edi);
        QemuPrintf("eip->%x cs->%x eflags->%x esp->%x ss->%x\n", regs->eip, regs->cs, regs->eflags, regs->esp, regs->ss);

        QemuPrintf("Interrupt->%x, error_code->%x\n", regs->interrupt, regs->error);
        QemuPrintf("KERNEL PANIC!!\n");
    }
}

Output (with int 0x01):

Unhandeled exception 1: Double Fault
Registers values:
eax->0 ebx->0 ecx->0 edx->0 ebp->0 esi->0 edi->0
eip->38e7 cs->38e7 eflags->38e7 esp->38e7 ss->38e7
Interrupt->1, error_code->1
KERNEL PANIC!!

Solution

  • To quote the System Programming Guide, section 6.13 ERROR CODE:

    Error codes are not pushed on the stack for exceptions that are generated externally (with the INTR or LINT[1:0] pins) or the INT n instruction, even if an error code is normally produced for those exceptions.

    Since you trigger via int 0x08 that will not put an error code on the stack even though an actual exception would. As such your interrupt return pops 4 bytes too many.

    Also your QemuPrintf does not advance the argument pointer for %x (among others) so it will print the same argument multiple times.