1

I am currently writing a toy operating system, and I have been having this issue for a while now. When setting up the IDT for a hobby OS, and triggering an interrupt, the interrupt at first happens with what seems like no error's, however I then start receiving general protection faults in a loop.

I assume this is a problem with my assembly, as when rewriting the C functions, the error still occurs.

idt.asm

jmp Idt_End                                                                     ;Jump to prevent any functions from being called

[GLOBAL Idt_Flush]                                                              ;Allow C to call the flush function
Idt_Flush:                                                                      ;Load an IDT passed from a pointer
    mov                     eax, [esp+4]                                        ;IDT is going to be passed as a pointer
    lidt                    [eax]                                               ;Load the IDT passed
    ret                                                                         ;Return from the function

 %macro ISR_NOERRCODE 1                                                         ;Macro for generating errorless ISR's
    [GLOBAL Idt_Isr%1]                                                          ;Create a ISR with peram number
    Idt_Isr%1:                                                                  ;Actual ISR code
        ;cli
        push byte 0                                                     
        push byte %1
        jmp isr_common_stub
%endmacro

%macro ISR_ERRCODE 1                                                            ;Same macro as above with with errors
    [GLOBAL Idt_Isr%1]
    Idt_Isr%1:
        ;cli
        push byte %1
        jmp isr_common_stub
%endmacro 

ISR_NOERRCODE               0                                                   ;ISR num 0
ISR_NOERRCODE               1                                                   ;ISR num 1
ISR_NOERRCODE               2                                                   ;ISR num 2
ISR_NOERRCODE               3                                                   ;ISR num 3
ISR_NOERRCODE               4                                                   ;ISR num 4
ISR_NOERRCODE               5                                                   ;ISR num 5
ISR_NOERRCODE               6                                                   ;ISR num 6
ISR_NOERRCODE               7                                                   ;ISR num 7
ISR_ERRCODE                 8                                                   ;ISR num 8      (ISR WITH ERROR CODE)
ISR_NOERRCODE               9                                                   ;ISR num 9
ISR_ERRCODE                 10                                                  ;ISR num 10     (ISR WITH ERROR CODE)
ISR_ERRCODE                 11                                                  ;ISR num 11     (ISR WITH ERROR CODE)
ISR_ERRCODE                 12                                                  ;ISR num 12     (ISR WITH ERROR CODE)
ISR_ERRCODE                 13                                                  ;ISR num 13     (ISR WITH ERROR CODE)
ISR_ERRCODE                 14                                                  ;ISR num 14     (ISR WITH ERROR CODE)
ISR_NOERRCODE               15                                                  ;ISR num 15
ISR_NOERRCODE               16                                                  ;ISR num 16
ISR_ERRCODE                 17                                                  ;ISR num 17     (ISR WITH ERROR CODE)
ISR_NOERRCODE               18                                                  ;ISR num 18
ISR_NOERRCODE               19                                                  ;ISR num 19
ISR_NOERRCODE               20                                                  ;ISR num 20
ISR_NOERRCODE               21                                                  ;ISR num 21
ISR_NOERRCODE               22                                                  ;ISR num 22
ISR_NOERRCODE               23                                                  ;ISR num 23
ISR_NOERRCODE               24                                                  ;ISR num 24
ISR_NOERRCODE               25                                                  ;ISR num 25
ISR_NOERRCODE               26                                                  ;ISR num 26
ISR_NOERRCODE               27                                                  ;ISR num 27
ISR_NOERRCODE               28                                                  ;ISR num 28
ISR_NOERRCODE               29                                                  ;ISR num 29
ISR_ERRCODE                 30                                                  ;ISR num 30
ISR_NOERRCODE               31                                                  ;ISR num 31




[extern IDT_IsrHandler]                                                         ;Extern the ISR handling code
isr_common_stub:
    pusha                                                                       ;Push registers to the stack
    
    mov                     ax, ds                                              ;lower 16 bits is in the ds register
    push                    eax                                                 ;Save the segment descriptor onto stack

    mov                     ax, 0x10                                            ;Load the kernel segment descriptor
    mov                     ds, ax                                              ;Set the segment register's
    mov                     es, ax                                              ;Set the segment register's
    mov                     fs, ax                                              ;Set the segment register's
    mov                     gs, ax                                              ;Set the segment register's

    call                    IDT_IsrHandler                                      ;C function to handle ISR's

    pop                     eax                                                 ;Reload the original segment descripors
    mov                     ds, ax                                              ;Clear segment register's
    mov                     es, ax                                              ;Clear segment register's
    mov                     fs, ax                                              ;Clear segment register's
    mov                     gs, ax                                              ;Clear segment register's

    popa                                                                        ;Pops registers pushed in `pusha`
    add                     esp, 8                                              ;Clean up error code and push number
    sti                                                                         ;Set the current interrupt being handled

    iret                                                                        ;Pop CS, EIP, EFLAGS, ESP, and SS and return

Idt_End:

idt.h

#ifndef __KERNEL_IDT_H__
#define __KERNEL_IDT_H__

#include <stdint.h>
#include <stddef.h>
#include <string.h>

#include "./tty.h"

#define IDT_ENTRIES     246
#define KERNEL_CS       0x08
#define low_16(addr)    (uint16_t)((addr) & 0xFFFF)
#define high_16(addr)   (uint16_t)(((addr) >> 16) & 0xFFFF)

typedef struct {                                                                /*Struct containing IDT gate info        */
    uint16_t low_offset;                                                        /*Lower 16 bits of function address      */
    uint16_t sel;                                                               /*Kernel selector                        */
    uint8_t  zero;                                                              /*Has to be zero for some reason         */
    uint8_t  flags;                                                             /*Table flags                            */
    uint16_t high_offset;                                                       /*HIgher 16 bits of function address     */
} __attribute__((packed)) Idt_gate_t;                                           /*Idt gate struct                        */

typedef struct {                                                                /*Used to tell where handlers are        */
    uint16_t limit;
    uint32_t base;
} __attribute__((packed)) Idt_register_t;

typedef struct {                                                                /*Struct containing register data        */
    uint32_t ds;                                                                /*Data segment selector                  */
    uint32_t edi;                                                               /*Register pushed by pusha               */
    uint32_t esi;                                                               /*Register pushed by pusha               */
    uint32_t ebp;                                                               /*Register pushed by pusha               */
    uint32_t esp;                                                               /*Register pushed by pusha               */
    uint32_t ebx;                                                               /*Register pushed by pusha               */
    uint32_t edx;                                                               /*Register pushed by pusha               */
    uint32_t ecx;                                                               /*Register pushed by pusha               */
    uint32_t eax;                                                               /*Register pushed by pusha               */
    uint32_t int_number;                                                        /*Current interupt number given          */
    uint32_t err_number;                                                        /*Error number that the CPU passes to us */
    uint32_t eip;                                                               /*Pushed by the CPU                      */
    uint32_t cs;                                                                /*Pushed by the CPU                      */
    uint32_t eflags;                                                            /*Pushed by the CPU                      */
    uint32_t useresp;                                                           /*Pushed by the CPU                      */
    uint32_t ss;                                                                /*Pushed by the CPU                      */
} Reg_registers_t;                                                              /*Structure containing register data     */

Idt_gate_t     Idt_idt[IDT_ENTRIES];                                            /*The actual interupt descriptor table   */
Idt_register_t idt_reg;                                                         /*IDT register pointer                   */

extern void Idt_Flush(uint32_t);                                                /*Initalize the Interupt descriptor table*/

extern void Idt_Isr0();
extern void Idt_Isr1();
extern void Idt_Isr2();
extern void Idt_Isr3();
extern void Idt_Isr4();
extern void Idt_Isr5();
extern void Idt_Isr6();
extern void Idt_Isr7();
extern void Idt_Isr8();
extern void Idt_Isr9();
extern void Idt_Isr10();
extern void Idt_Isr11();
extern void Idt_Isr12();
extern void Idt_Isr13();
extern void Idt_Isr14();
extern void Idt_Isr15();
extern void Idt_Isr16();
extern void Idt_Isr17();
extern void Idt_Isr18();
extern void Idt_Isr19();
extern void Idt_Isr20();
extern void Idt_Isr21();
extern void Idt_Isr22();
extern void Idt_Isr23();
extern void Idt_Isr24();
extern void Idt_Isr25();
extern void Idt_Isr26();
extern void Idt_Isr27();
extern void Idt_Isr28();
extern void Idt_Isr29();
extern void Idt_Isr30();
extern void Idt_Isr31();

void Idt_SetGate(int number, uint32_t handler) {                                /*Set the value to for 1 gate            */
    Idt_idt[number].low_offset  = low_16(handler);                              /*Set the lower 16 bytes of the address  */
    Idt_idt[number].high_offset = high_16(handler);                             /*Set the higher 16 bytes of the address */

    Idt_idt[number].zero  = 0;                                                  /*No idea why intel thought this was good*/
    Idt_idt[number].flags = 0x8E;                                               /*Set the flags of the current IDT       */
    Idt_idt[number].sel   = KERNEL_CS;                                          /*Set the selector of IDT to kernel CS   */
}

void Idt_Install() {                                                            /*Install idt **assuming already setup** */
    idt_reg.base          = (uint32_t) &Idt_idt;                                /*Set the base to IDT addr               */
    idt_reg.limit         = IDT_ENTRIES * sizeof(Idt_gate_t) - 1;               /*Limit to size of the IDT array         */

    __asm__ volatile ("lidtl (%0)" : : "r" (&idt_reg));                         /*Assembly function to load the IDT      */
}

void Idt_Isr_Install() {                                                        /*Install isr's for interrupts to work   */
    Idt_SetGate(0 , (uint32_t)Idt_Isr0);                                        /*Install the current IDT gate           */
    Idt_SetGate(1 , (uint32_t)Idt_Isr1);                                        /*Install the current IDT gate           */
    Idt_SetGate(2 , (uint32_t)Idt_Isr2);                                        /*Install the current IDT gate           */
    Idt_SetGate(3 , (uint32_t)Idt_Isr3);                                        /*Install the current IDT gate           */
    Idt_SetGate(4 , (uint32_t)Idt_Isr4);                                        /*Install the current IDT gate           */
    Idt_SetGate(5 , (uint32_t)Idt_Isr5);                                        /*Install the current IDT gate           */
    Idt_SetGate(6 , (uint32_t)Idt_Isr6);                                        /*Install the current IDT gate           */
    Idt_SetGate(7 , (uint32_t)Idt_Isr7);                                        /*Install the current IDT gate           */
    Idt_SetGate(8 , (uint32_t)Idt_Isr8);                                        /*Install the current IDT gate           */
    Idt_SetGate(9 , (uint32_t)Idt_Isr9);                                        /*Install the current IDT gate           */
    Idt_SetGate(10 ,(uint32_t)Idt_Isr10);                                       /*Install the current IDT gate           */
    Idt_SetGate(11 ,(uint32_t)Idt_Isr11);                                       /*Install the current IDT gate           */
    Idt_SetGate(12 ,(uint32_t)Idt_Isr12);                                       /*Install the current IDT gate           */
    Idt_SetGate(13 ,(uint32_t)Idt_Isr13);                                       /*Install the current IDT gate           */
    Idt_SetGate(14 ,(uint32_t)Idt_Isr14);                                       /*Install the current IDT gate           */
    Idt_SetGate(15 ,(uint32_t)Idt_Isr15);                                       /*Install the current IDT gate           */
    Idt_SetGate(16 ,(uint32_t)Idt_Isr16);                                       /*Install the current IDT gate           */
    Idt_SetGate(17 ,(uint32_t)Idt_Isr17);                                       /*Install the current IDT gate           */
    Idt_SetGate(18 ,(uint32_t)Idt_Isr18);                                       /*Install the current IDT gate           */
    Idt_SetGate(19 ,(uint32_t)Idt_Isr19);                                       /*Install the current IDT gate           */
    Idt_SetGate(20 ,(uint32_t)Idt_Isr20);                                       /*Install the current IDT gate           */
    Idt_SetGate(21 ,(uint32_t)Idt_Isr21);                                       /*Install the current IDT gate           */
    Idt_SetGate(22 ,(uint32_t)Idt_Isr22);                                       /*Install the current IDT gate           */
    Idt_SetGate(23 ,(uint32_t)Idt_Isr23);                                       /*Install the current IDT gate           */
    Idt_SetGate(24 ,(uint32_t)Idt_Isr24);                                       /*Install the current IDT gate           */
    Idt_SetGate(25 ,(uint32_t)Idt_Isr25);                                       /*Install the current IDT gate           */
    Idt_SetGate(26 ,(uint32_t)Idt_Isr26);                                       /*Install the current IDT gate           */
    Idt_SetGate(27 ,(uint32_t)Idt_Isr27);                                       /*Install the current IDT gate           */
    Idt_SetGate(28 ,(uint32_t)Idt_Isr28);                                       /*Install the current IDT gate           */
    Idt_SetGate(29 ,(uint32_t)Idt_Isr29);                                       /*Install the current IDT gate           */
    Idt_SetGate(30 ,(uint32_t)Idt_Isr30);                                       /*Install the current IDT gate           */
    Idt_SetGate(31 ,(uint32_t)Idt_Isr31);                                       /*Install the current IDT gate           */

    Idt_Install();                                                              /*Actually install the current gate      */
}

static void Idt_Init() {                                                        /*Init the IDT descriptor table          */
    Idt_Isr_Install();
}


void IDT_IsrHandler(Reg_registers_t reg) {
    Terminal_WriteString("Interrupt executed: ");
    Terminal_WriteHex(reg.int_number);
    Terminal_WriteString("\n");
}

                      

#endif
Michael Petch
  • 46,082
  • 8
  • 107
  • 198
mig21 bis
  • 11
  • 2
  • 2
  • 1
    Do not `sti` before the `iret`. That defeats the purpose of the interrupt handler. Also I don't see you pet the PIC when an external interrupt occurs, so you'll just get interrupted again and again. Combined with the `sti` before `iret`, this may cause a rapid stack overflow. – fuz Jun 06 '22 at 00:38
  • When you run this in a debugger / simulator (like Bochs or a VM with GDB attached), what instruction triggers the GPF? – Peter Cordes Jun 06 '22 at 00:59
  • `void IDT_IsrHandler(Reg_registers_t reg)` . This is a potential problem. Do not pass the reg structure by value, pass a pointer to it like `void IDT_IsrHandler(Reg_registers_t *reg)`. You will have to modify your assembly code to pass the address with a `push esp` just before you call the interrupt handler then you can do an extra `pop eax` right after the call to restore the stack. When you pass the structure by value the structure can be modified by the function which means the saved state of the register could be munged and may cause things to fault. – Michael Petch Jun 06 '22 at 06:33
  • 2
    The James Malloy tutorials has code similar to this and it was a well known problem. If you are in fact using James Malloy code be aware the OSDev community has a list of errata and bugs for that turoial here: https://wiki.osdev.org/James_Molloy%27s_Tutorial_Known_Bugs . This problem is one of them. – Michael Petch Jun 06 '22 at 06:39
  • https://stackoverflow.com/a/56486184/3857942 is another possibly related answer. Your problem could be a number of things and it may not be possible to know without a minimal reproducible example . Do you have a project on github or other service? – Michael Petch Jun 06 '22 at 07:39

0 Answers0