For more in depth concepts visit:
1 idt.c
Similar to the gdt, we have to set the interrupt descriptor.
#include <system.h>
/*
* IDT Entry
*/
struct idt_entry {
unsigned short base_low;
unsigned short sel;
unsigned char zero;
unsigned char flags;
unsigned short base_high;
} __attribute__((packed));
struct idt_ptr {
unsigned short limit;
unsigned int base;
} __attribute__((packed));
struct idt_entry idt[256];
struct idt_ptr idtp;
extern void idt_load();
/*
* idt_set_gate
* Set an IDT gate
*/
void
set_idt_gate(
unsigned char num,
unsigned long base,
unsigned short sel,
unsigned char flags
) {
idt[num].base_low = (base & 0xFFFF);
idt[num].base_high = (base >> 16) & 0xFFFF;
idt[num].sel = sel;
idt[num].zero = 0;
idt[num].flags = flags;
}
/*
* idt_install
* Install the IDTs
*/
void
idt_install() {
idtp.limit = (sizeof(struct idt_entry) * 256) - 1;
idtp.base = &idt;
memset(&idt, 0, sizeof(struct idt_entry) * 256);
idt_load();
}
Data Structures
IDT Entry Structure
:
struct idt_entry {
unsigned short base_low;
unsigned short sel;
unsigned char zero;
unsigned char flags;
unsigned short base_high;
} __attribute__((packed));
base_low
: Lower 16 bits of the handler function address.sel
: Code segment selector in GDT or LDT.zero
: Always set to 0.flags
: Type and attributes of the interrupt gate.base_high
: Upper 16 bits of the handler function address.__attribute__((packed))
: Ensures there is no padding between the members of the structure.
IDT Pointer Structure
:
struct idt_ptr {
unsigned short limit;
unsigned int base;
} __attribute__((packed));
limit
: Size of the IDT - 1 (in bytes).base
: Address of the first element in the IDT.
Global Variables
IDT and IDT Pointer
:
struct idt_entry idt[256];
struct idt_ptr idtp;
idt[256]
: Array of IDT entries. The x86 architecture supports up to 256 interrupts.idtp
: The IDT pointer, which holds the base address and size of the IDT.
External Function
idt_load
:
extern void idt_load();
- This external function is implemented in assembly. It loads the IDT using the
lidt
instruction.
Functions:
set_idt_gate
:
void set_idt_gate(unsigned char num, unsigned long base, unsigned short sel, unsigned char flags) {
idt[num].base_low = (base & 0xFFFF);
idt[num].base_high = (base >> 16) & 0xFFFF;
idt[num].sel = sel;
idt[num].zero = 0;
idt[num].flags = flags;
}
num
: Index of the IDT entry.base
: Address of the interrupt handler.sel
: Segment selector.flags
: Type and attributes.- This function sets up a specific IDT entry with the provided base address, selector, and flags.
idt_install
:
void idt_install() {
idtp.limit = (sizeof(struct idt_entry) * 256) - 1;
idtp.base = &idt;
memset(&idt, 0, sizeof(struct idt_entry) * 256);
idt_load();
}
idtp.limit
: Set to the size of the IDT in bytes minus 1.idtp.base
: Set to the address of theidt
array.memset(&idt, 0, sizeof(struct idt_entry) * 256)
: Initialize the entire IDT with zeros.idt_load()
: Load the IDT using thelidt
instruction (implemented in assembly).
2 idt_asm.asm
The function idt_load
will be implemented here, which is called in the idt_install
file.
; Interrupt Descriptor Table
global idt_load
extern idtp
idt_load:
lidt [idtp]
ret
3 Modify main.c
to call idt_install()
We did all the setup, its time to call the idt_install()
from the kernel main function:
int
main() {
gdt_install();
............................
idt_install();
............................
init_video();
puts("Hello world!\n");
for (;;);
4 Modify Makefile
Modify the Makefile to build newly added source files.