CLOSE

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 the idt array.
  • memset(&idt, 0, sizeof(struct idt_entry) * 256): Initialize the entire IDT with zeros.
  • idt_load(): Load the IDT using the lidt 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.