0

I am writing an OS to run on QEMU, and I have defined custom ISR's, however, when I trigger interrupt using

__asm__ ("int $0");

It is handled correctly, but when I do:

int a = 0;
int b = 5;
int c = b/a;

It results in a handle loop calling this over and over. I have tried clearing the bits and masking and nothing seems to work. This is the definition for my handle that is linked using extern

void ISR::__interrupt_handler(InterruptRegister *reg) {
    disp.print_screen("Recieved interrupt: ");
    char s[3];
    int_to_string(reg->int_no, s);
    disp.print_screen(s);
...

}

interrupt.asm

[extern interrupt_handler]

; Common Interrupt Service Routine
isr_common:
    cli ; Disable interrupts
    ; Save the registers
    pusha
    mov ax, ds ; Save the data segment
    push eax; Save the data segment
    mov ax, 0x10 ; Set the data segment to 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    ; Call the interrupt handler
    push esp ; Push the stack pointer
    call interrupt_handler
    pop eax ; Pop the stack pointer

    ; Restore the registers
    pop eax
    mov eax, 1
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    popa

    ;Clean the pushed error code
    add esp, 8

    ;sti ; Enable interrupts
    iret ; Pops CS, EIP, EFLAGS, SS, ESP

isr0:
    push byte 0
    push byte 0
    jmp isr_common
Jjkivai
  • 157
  • 1
  • 10
  • 1
    in the `int $0` case the instruction completed successfully, so after the interrupt eip will get incremented past the instruction. the div instruction that will be generated from your c code will not be able to complete successfully (that's why it invokes your interrupt handler in the first place), so after the interrupt handler returns eip will NOT be incremented and the same instruction is executed again. the div will of course trigger another int 0 which will call your interrupt handler again, etc... – Turtlefight Mar 15 '23 at 00:22
  • 1
    Your options to fix this are either (1) halt and catch fire - (2) change the eip value stored in the stack of your isr so execution resumes somewhere else after iret - (3) "fixup" the division by replacing the 0 with something else in your isr - (4) modify the stored eip in your isr to point to the instruction after the div. for (3) and (4) you'll have to disassemble the div instruction within your isr to find out which register(s) are used / how many bytes you need to step over. – Turtlefight Mar 15 '23 at 00:34
  • Also most likely a dupe: see: [this question](https://stackoverflow.com/q/34704121/8411406) - or [this question](https://stackoverflow.com/q/59218028/8411406) - or [this question](https://stackoverflow.com/q/33029457/8411406) – Turtlefight Mar 15 '23 at 00:39
  • `reg->eip += 2;` fixed the problem. An explanation of this would be very much appreciated. Especially since it isn't a valid asm register – Jjkivai Mar 15 '23 at 01:58
  • Before the cpu calls your interrupt handler it will push EFLAGS, CS and EIP [onto the stack](https://wiki.osdev.org/Interrupt_Service_Routines#x86) - the iret at the end of your interrupt handler then pops those values back into the corresponding registers. normal execution then resumes at the popped eip address. – Turtlefight Mar 15 '23 at 02:59
  • as for why you need to manually increment the stored eip value: if the interrupt is due to a fault exception the eip will not be incremented before calling the isr - if its due to a trap exception the eip will be incremented before calling the isr. ([heres a small table](https://wiki.osdev.org/Exceptions) - as a special case software invoked interrupts (the `int x` instruction) is treated like a trap - so eip will be incremented before calling your isr. – Turtlefight Mar 15 '23 at 03:11

0 Answers0