Triple Fault while calling for an interrupt

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
elowarp
Posts: 5
Joined: Thu Nov 13, 2025 10:00 am

Triple Fault while calling for an interrupt

Post by elowarp »

Hello, I'm currently loosing my mind to this General Triple Fault.

I tried to setup a GDT and an IDT for the x86_64 target following multiple sources, from this website and other.
From what I can understand, qemu crashes when I make the call

Code: Select all

asm volatile("int $30");
which should be handled by my isr_handler(), after setting up GDT & IDT.

Running qemu with -D -d int gives me this :

Code: Select all

...
0: v=1e e=0000 i=1 cpl=0 IP=0008:ffffffff80003535 pc=ffffffff80003535 SP=0010:ffff800007f9afd8 env->regs[R_EAX]=0000000000000000
RAX=0000000000000000 RBX=ffffffff80003530 RCX=00000000000003e8 RDX=0000000000000000
RSI=0000000000000000 RDI=000000000000001e RBP=ffff800007f9aff0 RSP=ffff800007f9afd8
R8 =ffffffff80006d40 R9 =0000000000000019 R10=0000000000000028 R11=0000000000000500
R12=ffffffff800036e0 R13=0000000000000000 R14=0000000000000000 R15=0000000000000000
RIP=ffffffff80003535 RFL=00000246 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
CS =0008 0000000000000000 ffffffff 00af9a00 DPL=0 CS64 [-R-]
SS =0010 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
DS =0010 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
FS =0010 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
GS =0010 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
LDT=0000 0000000000000000 00000000 00008200 DPL=0 LDT
TR =0000 0000000000000000 0000ffff 00008b00 DPL=0 TSS64-busy
GDT=     ffffffff8005ae40 00000017
IDT=     ffffffff80059e40 0000000f
CR0=80010011 CR2=0000000000000000 CR3=0000000007f8a000 CR4=00000020
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=000000000000001e CCD=0000000000000000 CCO=SUBL
EFER=0000000000000d00
check_exception old: 0xffffffff new 0xd
     1: v=0d e=01e2 i=0 cpl=0 IP=0008:ffffffff80003535 pc=ffffffff80003535 SP=0010:ffff800007f9afd8 env->regs[R_EAX]=0000000000000000
RAX=0000000000000000 RBX=ffffffff80003530 RCX=00000000000003e8 RDX=0000000000000000
RSI=0000000000000000 RDI=000000000000001e RBP=ffff800007f9aff0 RSP=ffff800007f9afd8
R8 =ffffffff80006d40 R9 =0000000000000019 R10=0000000000000028 R11=0000000000000500
R12=ffffffff800036e0 R13=0000000000000000 R14=0000000000000000 R15=0000000000000000
RIP=ffffffff80003535 RFL=00000246 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
CS =0008 0000000000000000 ffffffff 00af9a00 DPL=0 CS64 [-R-]
SS =0010 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
DS =0010 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
FS =0010 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
GS =0010 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
LDT=0000 0000000000000000 00000000 00008200 DPL=0 LDT
TR =0000 0000000000000000 0000ffff 00008b00 DPL=0 TSS64-busy
GDT=     ffffffff8005ae40 00000017
IDT=     ffffffff80059e40 0000000f
CR0=80010011 CR2=0000000000000000 CR3=0000000007f8a000 CR4=00000020
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=000000000000001e CCD=0000000000000000 CCO=SUBL
EFER=0000000000000d00
check_exception old: 0xd new 0xd
     2: v=08 e=0000 i=0 cpl=0 IP=0008:ffffffff80003535 pc=ffffffff80003535 SP=0010:ffff800007f9afd8 env->regs[R_EAX]=0000000000000000
RAX=0000000000000000 RBX=ffffffff80003530 RCX=00000000000003e8 RDX=0000000000000000
RSI=0000000000000000 RDI=000000000000001e RBP=ffff800007f9aff0 RSP=ffff800007f9afd8
R8 =ffffffff80006d40 R9 =0000000000000019 R10=0000000000000028 R11=0000000000000500
R12=ffffffff800036e0 R13=0000000000000000 R14=0000000000000000 R15=0000000000000000
RIP=ffffffff80003535 RFL=00000246 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
CS =0008 0000000000000000 ffffffff 00af9a00 DPL=0 CS64 [-R-]
SS =0010 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
DS =0010 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
FS =0010 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
GS =0010 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
LDT=0000 0000000000000000 00000000 00008200 DPL=0 LDT
TR =0000 0000000000000000 0000ffff 00008b00 DPL=0 TSS64-busy
GDT=     ffffffff8005ae40 00000017
IDT=     ffffffff80059e40 0000000f
CR0=80010011 CR2=0000000000000000 CR3=0000000007f8a000 CR4=00000020
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=000000000000001e CCD=0000000000000000 CCO=SUBL
EFER=0000000000000d00
check_exception old: 0x8 new 0xd

Here, pc point to my call to int mentioned in the beginning. The GTF does not occur during the setup of GDT&IDT because qemu crashes only there in the kernel : (Note : test_interrupt(k) print k on the screen if k!=30 else call `int $30`)

Code: Select all

void kmain(void) {
    // Ensure the bootloader actually understands our base revision (see spec).
    if (LIMINE_BASE_REVISION_SUPPORTED == false) abort();

    GDT_initialize();
    IDT_initialize();
    terminal_initialize();
    
    wait(1000);
    test_interrupt(1);

    wait(1000);
    test_interrupt(30); // There, it crashes 

    // We're done, just hang...
    abort();
}
From what I read and understood,
- it may be an error of paging but Limine (the bootloader) already set a working paging
- I may have messed up my setup of IDT or GDT but according to GDB, just before calling test_interrupt(30), IDTR and GDTR are both well defined
- I already do a long jump after the GDT setup (ref https://wiki.osdev.org/I_Can%27t_Get_In ... _interrupt)

Here are my GDT & IDT Files
GDT
load.asm

Code: Select all

[bits 64]
; Tells the CPU where the GDT is located 

global GDT_load
GDT_load:
     lgdt [rdi]
     call reload_segments

reload_segments:
     push 0x08
     lea rax, [rel .reload_cs]
     push rax
     retfq

.reload_cs:
     ; reload data segments
     mov ax, 0x10
     mov ds, ax
     mov es, ax
     mov fs, ax
     mov gs, ax
     mov ss, ax
     ret
gdt.c

Code: Select all

#include <stdlib.h> 
#include <stdint.h>

#include <kernel/gdt.h>

typedef struct {
    uint16_t LimitLow;           // limit (bits 0-15)
    uint16_t BaseLow;            // base (bits 0-15)
    uint8_t BaseMiddle;          // base (bits 16-13)
    uint8_t Access;              // access (bits 0-7)
    uint8_t FlagsLimitHi;        // limit (bits 16-19) | flags
    uint8_t BaseHigh;            // base (bits 24-31)
} __attribute__((packed)) GDTEntry;

typedef struct {
    uint16_t Limit;             // sizeof(gdt) - 1
    GDTEntry* Ptr;              // address of GDT
} __attribute__((packed)) GDTDescriptor;

typedef enum {
    GDT_ACCESS_CODE_READABLE            = 0x02,
    GDT_ACCESS_DATA_WRITABLE            = 0x02,

    GDT_ACCESS_CODE_CONFORMING          = 0x04,
    GDT_ACCESS_DATA_DIRECTION_NORMAL    = 0x00,
    GDT_ACCESS_DATA_DIRECTION_DOWN      = 0x04,

    GDT_ACCESS_DATA_SEGMENT             = 0x10,
    GDT_ACCESS_CODE_SEGMENT             = 0x18,

    GDT_ACCESS_DSECRIPTOR_TSS           = 0x00,

    GDT_ACCESS_RING0                    = 0x00,
    GDT_ACCESS_RING1                    = 0x20,
    GDT_ACCESS_RING2                    = 0x40,
    GDT_ACCESS_RING3                    = 0x60,

    GDT_ACCESS_PRESENT                  = 0x80,
} GDT_ACCESS;

typedef enum {
    GDT_FLAG_64BIT                      = 0x20,
    GDT_FLAG_32BIT                      = 0x40,
    GDT_FLAG_16BIT                      = 0x00,

    GDT_FLAG_GRANULARITY_1B             = 0x00,
    GDT_FLAG_GRANULARITY_4K             = 0x80,
} GDT_FLAGS;

GDTEntry g_GDT[3];
GDTDescriptor g_GDTDescriptor = { sizeof(g_GDT) - 1, g_GDT};

// Asm Function
void GDT_load(GDTDescriptor* descriptor, uint16_t codeSegment, uint16_t dataSegment);

GDTEntry encodeGDTEntry(uint32_t base, uint32_t limit, uint8_t access, uint8_t flags) {
    return (GDTEntry) {
        limit & 0xFFFF,
        base & 0xFFFF,
        ((base >> 16) & 0xFF),
        access,
        (((limit>>16) & 0x0F) | (flags & 0xF0)),
        ((base >> 24) & 0xFF)
    };
}

void GDT_initialize(void){
    asm volatile("cli");
    
    // Null
    g_GDT[0] = encodeGDTEntry(0, 0, 0, 0);

    // Kernel 64-bit code segment 
    g_GDT[1] = encodeGDTEntry(0, 
            0xFFFFF, 
            GDT_ACCESS_PRESENT | GDT_ACCESS_RING0 | GDT_ACCESS_CODE_SEGMENT | GDT_ACCESS_CODE_READABLE, 
            GDT_FLAG_64BIT | GDT_FLAG_GRANULARITY_4K);

    // Kernel 64-bit data segment 
    g_GDT[2] = encodeGDTEntry(0, 
            0xFFFFF, 
            GDT_ACCESS_PRESENT | GDT_ACCESS_RING0 | GDT_ACCESS_DATA_SEGMENT | GDT_ACCESS_DATA_WRITABLE, 
            GDT_FLAG_64BIT | GDT_FLAG_GRANULARITY_4K);
    

    GDT_load(&g_GDTDescriptor, x86_64_GDT_CODE_SEGMENT, x86_64_GDT_DATA_SEGMENT);
    
    asm volatile("sti");
}
gdt.h

Code: Select all

#ifndef _GDT_H
#define _GDT_H

#define x86_64_GDT_CODE_SEGMENT 0x8
#define x86_64_GDT_DATA_SEGMENT 0x10

void GDT_initialize(void);

#endif
IDT
idt.c

Code: Select all

#include <stdint.h>
#include <stdio.h>

#include <kernel/idt.h>
#include <kernel/gdt.h>
#include <kernel/tty.h>
#include "irq.h"
#include "isr.h"

uint64_t irq(){return 1LL<<63;};

typedef struct {
    uint16_t BaseLow;
    uint16_t SegmentSelector;
    uint8_t IstReserved;
    uint8_t Flags;
    uint16_t BaseHigh;
    uint32_t Offset;
    uint32_t Zeros;
} __attribute__((packed)) IDTEntry;

typedef struct {
    uint16_t Limit;
    IDTEntry* Ptr;
} __attribute__((packed)) IDTDescriptor;

typedef enum {
    IDT_FLAG_GATE_TASK          = 0x05,
    IDT_FLAG_GATE_16BIT_INT     = 0x06,
    IDT_FLAG_GATE_16BIT_TRAP    = 0x07,
    IDT_FLAG_GATE_32BIT_INT     = 0x0E,
    IDT_FLAG_GATE_32BIT_TRAP    = 0x0F,

    IDT_FLAG_RING0              = (0 << 5),
    IDT_FLAG_RING1              = (1 << 5),
    IDT_FLAG_RING2              = (2 << 5),
    IDT_FLAG_RING3              = (3 << 5),

    IDT_FLAG_PRESENT            = 0x80,
} IDT_FLAGS;

IDTEntry g_IDT[256];
IDTDescriptor g_IDTDescriptor = { sizeof(*g_IDT) - 1, g_IDT };

// Asm Function
extern void IDT_load(IDTDescriptor* descriptor);

IDTEntry IDT_set_gate(uint64_t base, uint16_t segmentDescriptor, uint8_t flags){
    return (IDTEntry) {
        base & 0xFFFF,
        segmentDescriptor,
        0,
        flags,
        (base >> 16) & 0xFFFF,
        (base >> 32) & 0xFFFFFFFF,
        0
    };
}

void IDT_enable_gate(int interrupt){
    g_IDT[interrupt].Flags |= IDT_FLAG_PRESENT;
}

void IDT_disable_gate(int interrupt){
    g_IDT[interrupt].Flags &= ~(IDT_FLAG_PRESENT);
}

void IDT_mapping(IDTEntry* g_IDT){
    // Software input
    g_IDT[0] = IDT_set_gate((uint64_t) &isr0, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[1] = IDT_set_gate((uint64_t) &isr1, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[2] = IDT_set_gate((uint64_t) &isr2, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[3] = IDT_set_gate((uint64_t) &isr3, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[4] = IDT_set_gate((uint64_t) &isr4, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[5] = IDT_set_gate((uint64_t) &isr5, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[6] = IDT_set_gate((uint64_t) &isr6, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[7] = IDT_set_gate((uint64_t) &isr7, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[8] = IDT_set_gate((uint64_t) &isr8, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[9] = IDT_set_gate((uint64_t) &isr9, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[10] = IDT_set_gate((uint64_t) &isr10, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[11] = IDT_set_gate((uint64_t) &isr11, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[12] = IDT_set_gate((uint64_t) &isr12, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[13] = IDT_set_gate((uint64_t) &isr13, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[14] = IDT_set_gate((uint64_t) &isr14, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[15] = IDT_set_gate((uint64_t) &isr15, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[16] = IDT_set_gate((uint64_t) &isr16, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[17] = IDT_set_gate((uint64_t) &isr17, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[18] = IDT_set_gate((uint64_t) &isr18, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[19] = IDT_set_gate((uint64_t) &isr19, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[20] = IDT_set_gate((uint64_t) &isr20, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[21] = IDT_set_gate((uint64_t) &isr21, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[22] = IDT_set_gate((uint64_t) &isr22, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[23] = IDT_set_gate((uint64_t) &isr23, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[24] = IDT_set_gate((uint64_t) &isr24, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[25] = IDT_set_gate((uint64_t) &isr25, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[26] = IDT_set_gate((uint64_t) &isr26, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[27] = IDT_set_gate((uint64_t) &isr27, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[28] = IDT_set_gate((uint64_t) &isr28, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[29] = IDT_set_gate((uint64_t) &isr29, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[30] = IDT_set_gate((uint64_t) &isr30, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[31] = IDT_set_gate((uint64_t) &isr31, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    
    // Hardware input
    g_IDT[32] = IDT_set_gate((uint64_t) &irq0, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[33] = IDT_set_gate((uint64_t) &irq1, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[34] = IDT_set_gate((uint64_t) &irq2, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[35] = IDT_set_gate((uint64_t) &irq3, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[36] = IDT_set_gate((uint64_t) &irq4, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[37] = IDT_set_gate((uint64_t) &irq5, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[38] = IDT_set_gate((uint64_t) &irq6, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[39] = IDT_set_gate((uint64_t) &irq7, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[40] = IDT_set_gate((uint64_t) &isr8, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[41] = IDT_set_gate((uint64_t) &isr9, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[42] = IDT_set_gate((uint64_t) &irq10, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[43] = IDT_set_gate((uint64_t) &irq11, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[44] = IDT_set_gate((uint64_t) &irq12, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[45] = IDT_set_gate((uint64_t) &irq13, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[46] = IDT_set_gate((uint64_t) &irq14, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);
    g_IDT[47] = IDT_set_gate((uint64_t) &irq15, x86_64_GDT_CODE_SEGMENT, IDT_FLAG_RING0 | IDT_FLAG_GATE_32BIT_INT);

    // ... Systems calls etc
}

void IDT_initialize(){
    asm volatile("cli");

    IDT_mapping(g_IDT);
    IDT_load(&g_IDTDescriptor);

    asm volatile("sti");
}

void test_interrupt(int int_no){
    if (int_no == 30){
        asm volatile("int $30");
        
    } else {
        printf("Interrupt test on %d called !\n", int_no);
    }
}
interrupt_handler.c

Code: Select all

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>

#include <kernel/idt.h>
#include <kernel/tty.h>

char* exception_messages[] = {
    "Division by 0",
    "Debug",
    "Non Maskable Interrupt",
    "Breakpoint",
    "Into Detected Overflow",
    "Out of bounds",
    "Invalid Opcode",
    "No Coprocessor",
    "Double fault (pushes an error code)",
    "Coprocessor Segment Overrun",
    "Bad TSS (pushes an error code)",
    "Segment not present (pushed an error code)",
    "Stack Fault (pushed an error code)",
    "General Protection fault (pushed an error code)",
    "Page Fault (pushed an error code)",
    "Unknown Interrupt",
    "Coprocessor Fault",
    "Alignement Fault",
    "Machine Check",
    "SIMD (SSE/AVX) error",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved",
};

void cpu_exception_handler(registers_t* regs){
    printf("Received Interrupt : %d\n%s\nError Code :%d\nSystem Halted!\n",
        regs->int_no, exception_messages[regs->int_no], regs->err_code);
    abort();
}

// Handler for general page fault
void gpf_handler(registers_t* regs){
    printf("%s\n", exception_messages[regs->int_no]);
    printf("Received Interrupt: %d\n", regs->int_no);
    printf("Error code: %d\n", regs->err_code);
    printf("CS: %d, RIP: %d\n", regs->iret_cs, regs->iret_rip);

    uint64_t* rsp = (uint64_t*) regs->iret_rsp;
    printf("Stack (rsp = %d) Contents(First 26):\n", (int) rsp);

    for(int i=0; i<26; i++){
        printf("    [%d] = %d\n", rsp+i, rsp[i]);
    }

    printf("System Halted! \n");
    abort();
}

void page_fault_handler(registers_t* regs){
    uint64_t faulting_address;
    asm volatile("mov %%cr2, %0" : "=r"(faulting_address));

    int present = !(regs->err_code & 0x1);  // Page not present
    int rw = regs->err_code & 0x2;          // Write operation
    int us = regs->err_code & 0x4;          // Processor was in user mode ?
    int reserved = regs->err_code & 0x8;    // Overwritten CPU-reserved bits of page entry ?
    int id = regs->err_code & 0x10;         // Cased by an intruction fetch ?

    printf("Page fault ! (");
    if (present) printf("Page not present, ");
    if (rw) printf("Not writable, ");
    if (us) printf("User-mode, ");
    if (reserved) printf("Reserved, ");
    if (id) printf("Intruction fetch, ");
    printf(") at address %d\n", 0);

    printf("Halting the system due to page fault.\n");
    abort();
}

void isr_handler(registers_t* regs){
    if (regs->int_no == 14){
        // page fault 
        abort();
        return;

    } else if (regs->int_no == 13){
        gpf_handler(regs);
        return;

    } else if (regs->int_no < 32){
        cpu_exception_handler(regs);
        return;

    } else {
        terminal_setcolor(0x00FF00);
        for(int i=0; i<regs->int_no; i++) printf("Received Interrupt : %d\n", regs->int_no);
        terminal_setcolor(0xFFFFFF);
        return;
    }
}

// All IRQ Routines (256 - 32 first reserved)
void (*irq_routines[224])(registers_t *) = {0};

void irq_handler(registers_t* regs){
    void (*handler)(registers_t *regs); // Blank function pointer
    
    int irq_no = regs->int_no - 32; // Int number without the 32 first reserved
    handler = irq_routines[irq_no];

    if (handler){
        handler(regs);
    }

    // Apic ?
}

void irq_install(int irq_no, void(*handler)(registers_t *r)){
    irq_routines[irq_no] = handler;
}


void irq_uninstall(int irq_no){
    irq_routines[irq_no] = 0;
}
isr_wrapper.asm

Code: Select all








[extern isr_handler]

%macro ISR_NOERRCODE 1
    [global isr%1]
    isr%1:
        cli;

        push 0      ; Push dummy error code
        push %1     ; Interrupt number

        push r15    ; Save general-purpose registers in reverse order (to match restore_registers)
        push r14
        push r13
        push r12
        push r11
        push r10
        push r9
        push r8
        push rsi
        push rdi
        push rbp
        push rdx
        push rcx
        push rbx
        push rax
        mov ax, ds  ; save segment registers
        push rax
        mov ax, es
        push rax
        push fs
        push gs

        mov rdi, rsp ; pass pointer to the registers_t structure
        cld
        call isr_handler

        pop gs      ; restore segment registers
        pop fs
        pop rax
        mov es, ax
        pop rax
        mov ds, ax
        pop rax     ; restore general-purpose registers
        pop rbx
        pop rcx
        pop rdx
        pop rbp
        pop rdi
        pop rsi
        pop r8
        pop r9
        pop r10
        pop r11
        pop r12
        pop r13
        pop r14
        pop r15

        add rsp, 16 ; clean up interrupt no & dummy error

        iretq       ; return from interrupt
%endmacro



%macro ISR_ERRCODE 1
    [global isr%1]
    isr%1:
        cli;

        push %1     ; Interrupt number

        push r15    ; Save general-purpose registers in reverse order (to match restore_registers)
        push r14
        push r13
        push r12
        push r11
        push r10
        push r9
        push r8
        push rsi
        push rdi
        push rbp
        push rdx
        push rcx
        push rbx
        push rax
        mov ax, ds  ; save segment registers
        push rax
        mov ax, es
        push rax
        push fs
        push gs

        mov rdi, rsp ; pass pointer to the registers_t structure
        cld
        call isr_handler

        pop gs      ; restore segment registers
        pop fs
        pop rax
        mov es, ax
        pop rax
        mov ds, ax
        pop rax     ; restore general-purpose registers
        pop rbx
        pop rcx
        pop rdx
        pop rbp
        pop rdi
        pop rsi
        pop r8
        pop r9
        pop r10
        pop r11
        pop r12
        pop r13
        pop r14
        pop r15

        add rsp, 16 ; clean up interrupt no & dummy error

        iretq       ; return from interrupt
%endmacro

ISR_NOERRCODE 0
ISR_NOERRCODE 1
ISR_NOERRCODE 2
ISR_NOERRCODE 3
ISR_NOERRCODE 4
ISR_NOERRCODE 5
ISR_NOERRCODE 6
ISR_NOERRCODE 7

ISR_ERRCODE 8

ISR_NOERRCODE 9

ISR_ERRCODE 10
ISR_ERRCODE 11
ISR_ERRCODE 12
ISR_ERRCODE 13
ISR_ERRCODE 14

ISR_NOERRCODE 15
ISR_NOERRCODE 16
ISR_NOERRCODE 17
ISR_NOERRCODE 18
ISR_NOERRCODE 19
ISR_NOERRCODE 20
ISR_NOERRCODE 21
ISR_NOERRCODE 22
ISR_NOERRCODE 23
ISR_NOERRCODE 24
ISR_NOERRCODE 25
ISR_NOERRCODE 26
ISR_NOERRCODE 27
ISR_NOERRCODE 28
ISR_NOERRCODE 29
ISR_NOERRCODE 30
ISR_NOERRCODE 31


ISR_NOERRCODE 48    ; APIC Timer
ISR_NOERRCODE 49    ; HPET Timer

irq_wrapper.asm

Code: Select all

[extern irq_handler]

%macro IRQ 2
    [global irq%1]
    irq%1:
        cli;

        push 0      ; Dummy errorcode
        push %2     ; Interrupt number

        push r15    ; Save general-purpose registers in reverse order (to match restore_registers)
        push r14
        push r13
        push r12
        push r11
        push r10
        push r9
        push r8
        push rsi
        push rdi
        push rbp
        push rdx
        push rcx
        push rbx
        push rax
        mov ax, ds  ; save segment registers
        push rax
        mov ax, es
        push rax
        push fs
        push gs

        mov rdi, rsp ; pass pointer to the registers_t structure
        cld
        call irq_handler

        pop gs      ; restore segment registers
        pop fs
        pop rax
        mov es, ax
        pop rax
        mov ds, ax
        pop rax     ; restore general-purpose registers
        pop rbx
        pop rcx
        pop rdx
        pop rbp
        pop rdi
        pop rsi
        pop r8
        pop r9
        pop r10
        pop r11
        pop r12
        pop r13
        pop r14
        pop r15
        add rsp, 16 ; clean up interrupt no & dummy error

        iretq       ; return from interrupt
%endmacro

; First the irq number then the interrupt number
IRQ 0, 32
IRQ 1, 33   ; Keyboard Interrupt
IRQ 2, 34
IRQ 3, 35
IRQ 4, 36
IRQ 5, 37
IRQ 6, 38
IRQ 7, 39
IRQ 8, 40
IRQ 9, 41
IRQ 10, 42
IRQ 11, 43
IRQ 12, 44  ; Mouse Interrupt
IRQ 13, 45
IRQ 14, 46
IRQ 15, 47

IRQ 16, 48  ; APIC Timer Interrupt
IRQ 17, 49  ; HPET Timer Interrupt
IRQ 18, 50  ; IPI
IRQ 19, 51

IRQ 96, 128 ; System Call
load.asm

Code: Select all

[bits 64]
; Tells the CPU where the IDT is located 

global IDT_load
IDT_load:
     cli
     lidt [rdi]
     sti
     ret
idt.h

Code: Select all

#ifndef _IDT_H
#define _IDT_H

#include <stdint.h>

typedef struct {
    // Segment registers
    uint64_t gs;
    uint64_t fs;
    uint64_t es;
    uint64_t ds;

    // General purpose registers
    uint64_t rax;
    uint64_t rbx;
    uint64_t rcx;
    uint64_t rdx;
    uint64_t rbp;
    uint64_t rdi;
    uint64_t rsi;
    uint64_t r9;
    uint64_t r10;
    uint64_t r11;
    uint64_t r12;
    uint64_t r13;
    uint64_t r14;
    uint64_t r15;

    uint64_t int_no;
    uint64_t err_code;

    // Automattically pushed registers during interrupt 
    uint64_t iret_rip;
    uint64_t iret_cs;
    uint64_t iret_rflags;
    uint64_t iret_rsp;
    uint64_t iret_ss;

} __attribute__((packed)) registers_t;

void IDT_initialize(void);
void test_interrupt(int int_no);

extern void irq_install(int irq_no, void(*handler)(registers_t*));

#endif
Thank you for your interest :)
Last edited by elowarp on Thu Nov 13, 2025 12:47 pm, edited 1 time in total.
nullplan
Member
Member
Posts: 2034
Joined: Wed Aug 30, 2017 8:24 am

Re: Triple Fault while calling for an interrupt

Post by nullplan »

First of all, what is the first problem happening?

Code: Select all

check_exception old: 0xffffffff new 0xd
     1: v=0d e=01e2
That's where it all starts to go down hill. What does this say? You are getting a GPF (vector 13) and the error code is 0x1e2. That means, it has a problem with an IDT entry, namely IDT entry number 60. Since that doesn't make a lick of sense, I'm guessing the selector index is doubled in 64-bit mode, seeing as all the IDT entries are doubled in size.

So that means the CPU doesn't like your 30th interrupt gate. And I'm guessing it also doesn't like any of the others, seeing as you are tripple faulting. However, I can't really figure out what exactly it dislikes. The code seems correct from here.
Carpe diem!
User avatar
Demindiro
Member
Member
Posts: 165
Joined: Fri Jun 11, 2021 6:02 am
Libera.chat IRC: demindiro
Location: Belgium
Contact:

Re: Triple Fault while calling for an interrupt

Post by Demindiro »

Something that stands out to me:

Code: Select all

IDT=     ffffffff80059e40 0000000f
compare to mine, with all 256 IDT entries mapped:

Code: Select all

IDT=     ffff800000013400 00000fff
To me, it seems you are telling the CPU there is only 1 IDT entry.

And the reason for that:

Code: Select all

sizeof(*g_IDT) - 1
You shouldn't be dereferencing.
GeneSYS exokernel (Codeberg)
Lemmings! micro-/multikernel (Github, Codeberg)
Waddle container tool (Codeberg)
elowarp
Posts: 5
Joined: Thu Nov 13, 2025 10:00 am

Re: Triple Fault while calling for an interrupt

Post by elowarp »

Demindiro wrote: Thu Nov 13, 2025 12:21 pm

Code: Select all

sizeof(*g_IDT) - 1
You shouldn't be dereferencing.
You are totally right !! I changed it however it doesn't solve the issue

Code: Select all

IDT=     ffffffff80059e40 00000fff
nullplan wrote: Thu Nov 13, 2025 12:14 pm So that means the CPU doesn't like your 30th interrupt gate. And I'm guessing it also doesn't like any of the others, seeing as you are tripple faulting. However, I can't really figure out what exactly it dislikes. The code seems correct from here.
I tried to change the call from `int $30` to `int $28` and indeed the error code changed, the index is now 56 (2 times 28) so you seem to be right, neither of the gates are working but I don't see where the issue is in the definitions.
elowarp
Posts: 5
Joined: Thu Nov 13, 2025 10:00 am

Re: Triple Fault while calling for an interrupt

Post by elowarp »

I didn't see but the fault changed ! I am now getting a Segment not present (0xb)

Code: Select all

     0: v=1c e=0000 i=1 cpl=0 IP=0008:ffffffff80003535 pc=ffffffff80003535 SP=0010:ffff800007eeefd8 env->regs[R_EAX]=0000000000000000
RAX=0000000000000000 RBX=ffffffff80003530 RCX=00000000000003e8 RDX=0000000000000000
RSI=0000000000000000 RDI=000000000000001e RBP=ffff800007eeeff0 RSP=ffff800007eeefd8
R8 =ffffffff80006d40 R9 =0000000000000019 R10=0000000000000028 R11=0000000000000500
R12=ffffffff800036e0 R13=0000000000000000 R14=0000000000000000 R15=0000000000000000
RIP=ffffffff80003535 RFL=00000246 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
CS =0008 0000000000000000 ffffffff 00af9a00 DPL=0 CS64 [-R-]
SS =0010 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
DS =0010 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
FS =0010 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
GS =0010 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
LDT=0000 0000000000000000 00000000 00008200 DPL=0 LDT
TR =0000 0000000000000000 0000ffff 00008b00 DPL=0 TSS64-busy
GDT=     ffffffff8005ae40 00000017
IDT=     ffffffff80059e40 00000fff
CR0=80010011 CR2=0000000000000000 CR3=0000000007ede000 CR4=00000020
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=000000000000001e CCD=0000000000000000 CCO=SUBL
EFER=0000000000000d00
check_exception old: 0xffffffff new 0xb
     1: v=0b e=01c2 i=0 cpl=0 IP=0008:ffffffff80003535 pc=ffffffff80003535 SP=0010:ffff800007eeefd8 env->regs[R_EAX]=0000000000000000
RAX=0000000000000000 RBX=ffffffff80003530 RCX=00000000000003e8 RDX=0000000000000000
RSI=0000000000000000 RDI=000000000000001e RBP=ffff800007eeeff0 RSP=ffff800007eeefd8
R8 =ffffffff80006d40 R9 =0000000000000019 R10=0000000000000028 R11=0000000000000500
R12=ffffffff800036e0 R13=0000000000000000 R14=0000000000000000 R15=0000000000000000
RIP=ffffffff80003535 RFL=00000246 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
CS =0008 0000000000000000 ffffffff 00af9a00 DPL=0 CS64 [-R-]
SS =0010 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
DS =0010 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
FS =0010 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
GS =0010 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
LDT=0000 0000000000000000 00000000 00008200 DPL=0 LDT
TR =0000 0000000000000000 0000ffff 00008b00 DPL=0 TSS64-busy
GDT=     ffffffff8005ae40 00000017
IDT=     ffffffff80059e40 00000fff
CR0=80010011 CR2=0000000000000000 CR3=0000000007ede000 CR4=00000020
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=000000000000001e CCD=0000000000000000 CCO=SUBL
EFER=0000000000000d00
check_exception old: 0xb new 0xb
     2: v=08 e=0000 i=0 cpl=0 IP=0008:ffffffff80003535 pc=ffffffff80003535 SP=0010:ffff800007eeefd8 env->regs[R_EAX]=0000000000000000
RAX=0000000000000000 RBX=ffffffff80003530 RCX=00000000000003e8 RDX=0000000000000000
RSI=0000000000000000 RDI=000000000000001e RBP=ffff800007eeeff0 RSP=ffff800007eeefd8
R8 =ffffffff80006d40 R9 =0000000000000019 R10=0000000000000028 R11=0000000000000500
R12=ffffffff800036e0 R13=0000000000000000 R14=0000000000000000 R15=0000000000000000
RIP=ffffffff80003535 RFL=00000246 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
CS =0008 0000000000000000 ffffffff 00af9a00 DPL=0 CS64 [-R-]
SS =0010 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
DS =0010 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
FS =0010 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
GS =0010 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
LDT=0000 0000000000000000 00000000 00008200 DPL=0 LDT
TR =0000 0000000000000000 0000ffff 00008b00 DPL=0 TSS64-busy
GDT=     ffffffff8005ae40 00000017
IDT=     ffffffff80059e40 00000fff
CR0=80010011 CR2=0000000000000000 CR3=0000000007ede000 CR4=00000020
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=000000000000001e CCD=0000000000000000 CCO=SUBL
EFER=0000000000000d00
check_exception old: 0x8 new 0xb
I looking forward to it
elowarp
Posts: 5
Joined: Thu Nov 13, 2025 10:00 am

Re: Triple Fault while calling for an interrupt

Post by elowarp »

Alright thank you for all, I found the issue : all my entries were not set to present !! I'm so sorry for bothering you for such a issue
User avatar
Demindiro
Member
Member
Posts: 165
Joined: Fri Jun 11, 2021 6:02 am
Libera.chat IRC: demindiro
Location: Belgium
Contact:

Re: Triple Fault while calling for an interrupt

Post by Demindiro »

(wrote this right when you found another issue, but will post anyway)

Your GDT is missing a TSS at minimum, which is required for using alternate interrupt stacks. Might not be necessary if using IST0, but you''ll need to sometime anyway.

Your load_GDT function is weird. You're supposed to do retfq after setting the segment selectors, not before. Perhaps not, after checking the wiki. Oh well.
GeneSYS exokernel (Codeberg)
Lemmings! micro-/multikernel (Github, Codeberg)
Waddle container tool (Codeberg)
elowarp
Posts: 5
Joined: Thu Nov 13, 2025 10:00 am

Re: Triple Fault while calling for an interrupt

Post by elowarp »

Fixed it, i'll put a TSS later in the week, thank you !
MichaelPetch
Member
Member
Posts: 857
Joined: Fri Aug 26, 2016 1:41 pm
Libera.chat IRC: mpetch

Re: Triple Fault while calling for an interrupt

Post by MichaelPetch »

A bit of an aside. The selector error code is incorrect because of a QEMU bug that effectively doubles the selector index when in 64-bit mode on older version of QEMU. A fix was made when I reported this issue a few years ago: https://gitlab.com/qemu-project/qemu/-/issues/1382
Post Reply