Page 1 of 1

Implementing IDT in long mode

Posted: Sat Nov 27, 2021 7:59 am
by NeonLightions
Hi,
I've been stuck in this problem for quite a while (specifically 3 days :D). Can anyone give me advice? Here is the code, please fix it for me:

In idt.h

Code: Select all

#ifndef IDT_H
#define IDT_H

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

/**
 * @brief 64-bit IDT entry, 16 byte wise
 * 
 */
typedef struct idt_entry {
    uint16_t base_low;
    uint16_t segment_selector;
    uint8_t ist;      
    uint8_t gate_type           : 4;
    uint8_t reserved1           : 1;
    uint8_t dpl                 : 2;
    uint8_t present             : 1;
    uint16_t base_middle;
    uint32_t base_high;
    uint32_t reserved2;
}__attribute__((packed)) idt_entry_t;

typedef struct idt_descriptor {
    uint16_t size;
    uint64_t base;
}__attribute__((packed)) idt_descriptor_t;

void idt_init();

#endif
In idt.c:

Code: Select all

#include "idt.h"

/** We have 256 interrupt gate, entry from 0..31 are 
 * reserved for CPU, 32..47 will reserved for IRQ, 
 * all gates from 48..255 are free for our OS
 */
static idt_entry_t idt_table[256];

/**
 * @brief IDT descriptor, use for load IDT
 */
static idt_descriptor_t idtr;

static inline 
void idt_set_gate(uint8_t num, uint64_t base, uint16_t seg_sel, 
                    uint8_t gate_type, uint8_t dpl)
{
    idt_entry_t *this = &idt_table[num];
    this->base_low = base & 0xFFFFFFFF;
    this->base_middle = (base >> 16) & 0xFFFFFFFF;
    this->base_high = (base >> 32) & 0xFFFFFFFF;
    this->segment_selector = seg_sel;
    this->gate_type = gate_type;
    this->dpl = dpl;
    this->present = 1;

    this->reserved1 = 0;
    this->reserved2 = 0;
    this->ist = 0;
}

#include "isr.h"
#include "../memory/gdt.h"

void 
idt_init()
{
    asm volatile("cli");
    idtr.base = (uint64_t) &idt_table;
    idtr.size = sizeof(idt_entry_t) * 256;
    memset(&idt_table, 0, sizeof(idt_entry_t) * 256);


    idt_set_gate(0, (uint64_t) isr_0, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(1, (uint64_t) isr_1, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(2, (uint64_t) isr_2, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(3, (uint64_t) isr_3, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(4, (uint64_t) isr_4, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(5, (uint64_t) isr_5, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(6, (uint64_t) isr_6, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(7, (uint64_t) isr_7, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(8, (uint64_t) isr_8, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(9, (uint64_t) isr_9, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(10, (uint64_t) isr_10, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(11, (uint64_t) isr_11, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(12, (uint64_t) isr_12, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(13, (uint64_t) isr_13, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(14, (uint64_t) isr_14, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(15, (uint64_t) isr_15, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(16, (uint64_t) isr_16, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(17, (uint64_t) isr_17, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(18, (uint64_t) isr_18, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(19, (uint64_t) isr_19, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(20, (uint64_t) isr_20, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(21, (uint64_t) isr_21, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(22, (uint64_t) isr_22, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(23, (uint64_t) isr_23, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(24, (uint64_t) isr_24, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(5, (uint64_t) isr_25, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(26, (uint64_t) isr_26, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(27, (uint64_t) isr_27, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(28, (uint64_t) isr_28, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(29, (uint64_t) isr_29, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(30, (uint64_t) isr_30, SEGMENT_KCODE_OFFSET, 0xE, 0);
    idt_set_gate(31, (uint64_t) isr_31, SEGMENT_KCODE_OFFSET, 0xE, 0);

    asm volatile("sti");
}
In isr-stub.asm:

Code: Select all

section .text

%macro ISR_NOERR 1
global isr_%1
isr_%1:
    push qword 0
    push qword %1
    jmp isr_handler_stub
%endmacro

%macro ISR_ERR 1
global isr_%1
isr_%1:
    push qword %1
    jmp isr_handler_stub
%endmacro

%macro pushaq 1
    push rax
    push rcx
    push rdx
    push rbx
    push rbp
    push rsi
    push rdi
%endmacro

%macro popaq 1
    pop rdi
    pop rsi
    pop rbp
    pop rbx
    pop rdx
    pop rcx 
    pop rax
%endmacro

ISR_NOERR 0
ISR_NOERR 1
ISR_NOERR 2
ISR_NOERR 3
ISR_NOERR 4
ISR_NOERR 5
ISR_NOERR 6
ISR_NOERR 7
ISR_ERR 8
ISR_NOERR 9
ISR_ERR 10
ISR_ERR 11
ISR_ERR 12
ISR_ERR 13
ISR_ERR 14
ISR_NOERR 15
ISR_NOERR 16
ISR_ERR 17
ISR_NOERR 18
ISR_NOERR 19
ISR_NOERR 20
ISR_ERR 21
ISR_NOERR 22
ISR_NOERR 23
ISR_NOERR 24
ISR_NOERR 25
ISR_NOERR 26
ISR_NOERR 27
ISR_NOERR 28
ISR_NOERR 29
ISR_NOERR 30
ISR_NOERR 31

extern isr_handler
isr_handler_stub:
    ;push rax
    ;push rcx
    ;push rdx
    ;push rbx
    ;push rsp
    ;push rbp
    ;push rsi
    ;push rdi
    ;push r8
    ;push r9
    ;push r10
    ;push r11
    ;push r12
    ;push r13
    ;push r14
    ;push r15
    pushaq 1

    mov word ax, ds
    push rax

    mov rax, rsp
    push rax

    mov word ax, 0x10
    mov word ds, ax
    mov word es, ax
    mov word fs, ax
    mov word gs, ax

    call isr_handler

    add qword rsp, 8

    pop rax
    mov word ds, ax
    mov word es, ax
    mov word fs, ax
    mov word gs, ax

    ;pop r15
    ;pop r14
    ;pop r13
    ;pop r12
    ;pop r11
    ;pop r10
    ;pop r9
    ;pop r8
    ;pop rdi
    ;pop rsi
    ;pop rbp
    ;pop rsp
    ;pop rbx
    ;pop rdx
    ;pop rcx
    ;pop rax
    popaq 1

    add qword rsp, 16

    iret
In isr.h:

Code: Select all

#ifndef ISR_H
#define ISR_H

#include <stdint.h>

#include "../common/printf.h"

typedef struct interrupt_state {
    uint64_t ds;                                            /** Pushed by isr_handler_stub */
    //uint64_t r15, r14, r13, r12, r11, r10, r9, r8;          /** Pushed by isr_handler_stub */
    uint64_t rdi, rsi, rbp, useless, rbx, rdx, rcx, rax;    /** Pushed by isr_handler_stub */
    uint64_t int_no, err_code;                              /** Pushed by CPU when interrupt fired */
    uint64_t rip, cs, rflags, rsp, ss;                      /** Pushed by CPU when interrupt fired */
}__attribute__((packed)) interrupt_state_t;


typedef void (*isr_t)(interrupt_state_t *);

void isr_register(uint8_t int_no, isr_t handler);

void isr_handler(interrupt_state_t *state);

extern void isr_0();
extern void isr_1();
extern void isr_2();
extern void isr_3();
extern void isr_4();
extern void isr_5();
extern void isr_6();
extern void isr_7();
extern void isr_8();
extern void isr_9();
extern void isr_10();
extern void isr_11();
extern void isr_12();
extern void isr_13();
extern void isr_14();
extern void isr_15();
extern void isr_16();
extern void isr_17();
extern void isr_18();
extern void isr_19();
extern void isr_20();
extern void isr_21();
extern void isr_22();
extern void isr_23();
extern void isr_24();
extern void isr_25();
extern void isr_26();
extern void isr_27();
extern void isr_28();
extern void isr_29();
extern void isr_30();
extern void isr_31();

#endif
In isr.c:

Code: Select all

#include "isr.h"

static isr_t isr_table[256] = { NULL };

inline void 
isr_register(uint8_t int_no, isr_t handler)
{
    if (isr_table[int_no] != NULL) {
        cprintf(VGA_COLOR_RED, "ERROR: isr_register: handler for interrupt %#x already registered", int_no);
        return;
    }
    isr_table[int_no] = handler;
}


void 
isr_handler(interrupt_state_t *state)
{
    uint8_t num = state->int_no;

    cprintf(VGA_COLOR_CYAN, "INFO: isr_handler: caught interrupt %#x", num);
}
Thank you everyone!

NeonLightions

Re: Implementing IDT in long mode

Posted: Sat Nov 27, 2021 11:29 am
by iansjack
Can I suggest that you make your source code available on an online repository, rather than posting large amounts of code here. It makes the forum less cluttered and provides anyone who is inclined to debug your code all the information they need.

Re: Implementing IDT in long mode

Posted: Sat Nov 27, 2021 1:30 pm
by nullplan
NeonLightions wrote:I've been stuck in this problem for quite a while (specifically 3 days :D). Can anyone give me advice? Here is the code, please fix it for me:
No, I won't, if you ask like that. Your question is not indicative of what is wrong or what you have done to rectify the issue. Rather, you dump a whole lot of source code on me and expect me to read it in my off time. No, not happening. Maybe the others are feeling more charitable, but I won't do your homework for you.

Besides, IDT? Just read the CPU manuals, that tells you all about how to fill the IDT correctly.

Re: Implementing IDT in long mode

Posted: Sat Nov 27, 2021 2:58 pm
by neon
Hi,

Looks like you reached the point where you need to learn how to use a debugger to proceed. I personally use Bochs (..but plan to switch to a custom one hopefully soon.) Most use GDB since it supports source level debugging. Pick a debugger that you prefer, learn its basic functions (Break, Continue, Watch, Regs, Read/Write memory) and learn to work with it (Using memory maps to jump to function entry points, loading symbolic information etc.)

As for this thread...it would for sure be helpful to put your code in a repository (I use BitBucket but most use Github) and provide detail about the expected output and what is happening and your efforts to debug. 3 days is not a long time. Honestly am not convinced its in long mode.

Out of interest, why are you enabling hardware interrupts still? If you are getting or testing the IDT, keep hardware interrupts disabled until you have remapped hardware IRQ's via the PIC or APIC and the timer IRQ's are in place.

Re: Implementing IDT in long mode

Posted: Mon Dec 20, 2021 1:40 am
by NeonLightions
Hi,
like you said, I have used debugger and debug patiently. But I still can't debug it, now it causes General Protection Fault. Can you help me fix it?

Here is my repo: https://github.com/NeonLightions/Amore-OS-x64

Sorry everyone for being so hasty.

Re: Implementing IDT in long mode

Posted: Mon Dec 20, 2021 2:37 am
by nullplan
NeonLightions wrote:But I still can't debug it, now it causes General Protection Fault.
What does? When does it fault? What is, say, QEMU saying when you crank up the debug options?

Re: Implementing IDT in long mode

Posted: Mon Dec 20, 2021 5:54 am
by NeonLightions
nullplan wrote:
NeonLightions wrote:But I still can't debug it, now it causes General Protection Fault.
What does? When does it fault? What is, say, QEMU saying when you crank up the debug options?
It faults after handling 1 interrupt (Interrupt 0, for example)

Re: Implementing IDT in long mode

Posted: Mon Dec 20, 2021 5:26 pm
by NeonLightions
Oh, sorry to bother. I just realized that IDT causes GPF because I added 8 to rsp when I didn't push anything to stack :D. Sorry to bother you.