diff --git a/kernel/cpu/Makefile b/kernel/cpu/Makefile index 45c3653..ef907f4 100644 --- a/kernel/cpu/Makefile +++ b/kernel/cpu/Makefile @@ -1,4 +1,4 @@ -OBJS = multiboot.asm.o kernel_loader.asm.o ports.asm.o gdt.asm.o +OBJS = multiboot.asm.o kernel_loader.asm.o ports.asm.o gdt.asm.o idt.asm.o ASM = nasm ASMFLAGS = -f elf diff --git a/kernel/cpu/idt.asm b/kernel/cpu/idt.asm new file mode 100644 index 0000000..40ca7e8 --- /dev/null +++ b/kernel/cpu/idt.asm @@ -0,0 +1,7 @@ +global idt_flush ; for drivers/idt.c + +section .text +idt_flush: + mov eax, [esp+4] ; Retrieve idt_ptr_t* + lidt [eax] + ret \ No newline at end of file diff --git a/kernel/drivers/Makefile b/kernel/drivers/Makefile index 6c3e3c3..e1d07b0 100644 --- a/kernel/drivers/Makefile +++ b/kernel/drivers/Makefile @@ -1,4 +1,4 @@ -OBJS = tty.o gdt.o +OBJS = tty.o gdt.o idt.o CC = i686-elf-gcc # cross-compiler CFLAGS = -m32 -fno-stack-protector -ffreestanding -Wall -Wextra -Werror -g -c diff --git a/kernel/drivers/gdt.c b/kernel/drivers/gdt.c index 6b5d7ea..0fb13dc 100644 --- a/kernel/drivers/gdt.c +++ b/kernel/drivers/gdt.c @@ -1,5 +1,4 @@ #include "gdt.h" -#include "ports.h" // Internal method extern void gdt_flush(uint32_t); // Defined on cpu/gdt.asm diff --git a/kernel/drivers/gdt.h b/kernel/drivers/gdt.h index 8234a7c..d0092b3 100644 --- a/kernel/drivers/gdt.h +++ b/kernel/drivers/gdt.h @@ -12,7 +12,7 @@ * GDT(Global Descriptor Table) is a complex data structure used in x86 systems * to define memory areas. * Technically speaking GDT is formed by an array of 8-bytes segment descriptors, - * the first descriptor of the GDT is always a NULL one and CANNNOT be used to allocate + * the first descriptor of the GDT is always a NULL one and CANNOT be used to allocate * memory; so we need at least two descriptors(plus the null descriptor) to successfully allocate * data on our memory. * In x86 architecture there're two methods to provide virtual memory: Segmentation and Paging: @@ -34,14 +34,14 @@ /* Those values were taken from Intel's developer manual */ // GDT fields -#define GDT_BASE 0x00000000 +#define GDT_BASE 0x00000000 #define GDT_LIMIT 0xFFFFFFFF // GDT granularity -#define GDT_SEGMENT_LENGTH 0xF +#define GDT_SEGMENT_LENGTH 0xF #define GDT_OPERAND_SIZE_16 0 #define GDT_OPERAND_SIZE_32 1 -#define GDT_GRANULARITY_1K 0 -#define GDT_GRANULARITY_4K 1 +#define GDT_GRANULARITY_1K 0 +#define GDT_GRANULARITY_4K 1 // GDT access type fields #define GDT_DATA_TYPE_READ_ONLY 0x0 #define GDT_DATA_TYPE_READ_ONLY_ACCESSED 0x1 @@ -82,10 +82,10 @@ * P: is segment present? (bool) */ struct gdt_access { - uint8_t type:4; // 4 Byte - uint8_t dt:1; // 1 Byte - uint8_t dpl:2; // 2 Byte - uint8_t p:1; // 1 Byte + uint8_t type: 4; // 4 Byte + uint8_t dt: 1; // 1 Byte + uint8_t dpl: 2; // 2 Byte + uint8_t p: 1; // 1 Byte }__attribute__((packed)); typedef struct gdt_access gdt_access_t; @@ -98,10 +98,10 @@ typedef struct gdt_access gdt_access_t; * G: granularity (0 = 1 Byte, 1 = 4KiB) */ struct gdt_granularity { - uint8_t seglen:4; - uint8_t zero:2; - uint8_t d:1; - uint8_t g:1; + uint8_t seglen: 4; + uint8_t zero: 2; + uint8_t d: 1; + uint8_t g: 1; }__attribute__((packed)); typedef struct gdt_granularity gdt_gran_t; diff --git a/kernel/drivers/idt.c b/kernel/drivers/idt.c new file mode 100644 index 0000000..5d26b05 --- /dev/null +++ b/kernel/drivers/idt.c @@ -0,0 +1,94 @@ +#include "idt.h" +#include "../libc/string.h" + +// Internal method +extern void idt_flush(idt_ptr_t*); // defined on cpu/idt.asm +static void init_idt(); +static void idt_set_gate(uint8_t idx, void(*base), uint16_t selector, idt_flags_t flags); + +idt_entry_t idt_entries[256]; // 256 interrupts +idt_ptr_t idt_ptr; + +// This method will be called by the kernel +void idt_setup() { + init_idt(); +} + +static void idt_set_gate(uint8_t idx, void(*base), uint16_t selector, idt_flags_t flags) { + idt_entries[idx] = (struct idt_entry) { + .base_low = (uint32_t)base & 0xFFFF, + .base_high = ((uint32_t)base >> 16) & 0xFFFF, + .seg_sel = selector, + .flags = flags + }; +} + +static void init_idt() { + idt_ptr.limit = sizeof(idt_entry_t) * 256 - 1; + idt_ptr.base = idt_entries; + + memset(&idt_entries, 0, sizeof(idt_entry_t) * 256); + + idt_flags_t flags = { + .reserved = IDT_FLAG_RESERVED, // Always 0x0E + .dpl = 0, + .p = 1 + }; + // Remap IDT with ISRs + idt_set_gate(0, isr0, 0x08, flags); + idt_set_gate(1, isr1, 0x08, flags); + idt_set_gate(2, isr2, 0x08, flags); + idt_set_gate(3, isr3, 0x08, flags); + idt_set_gate(4, isr4, 0x08, flags); + idt_set_gate(5, isr5, 0x08, flags); + idt_set_gate(6, isr6, 0x08, flags); + idt_set_gate(7, isr7, 0x08, flags); + idt_set_gate(8, isr8, 0x08, flags); + idt_set_gate(9, isr9, 0x08, flags); + idt_set_gate(10, isr10, 0x08, flags); + idt_set_gate(11, isr11, 0x08, flags); + idt_set_gate(12, isr12, 0x08, flags); + idt_set_gate(13, isr13, 0x08, flags); + idt_set_gate(14, isr14, 0x08, flags); + idt_set_gate(15, isr15, 0x08, flags); + idt_set_gate(16, isr16, 0x08, flags); + idt_set_gate(17, isr17, 0x08, flags); + idt_set_gate(18, isr18, 0x08, flags); + idt_set_gate(19, isr19, 0x08, flags); + idt_set_gate(20, isr20, 0x08, flags); + idt_set_gate(21, isr21, 0x08, flags); + idt_set_gate(22, isr22, 0x08, flags); + idt_set_gate(23, isr23, 0x08, flags); + idt_set_gate(24, isr24, 0x08, flags); + idt_set_gate(25, isr25, 0x08, flags); + idt_set_gate(26, isr26, 0x08, flags); + idt_set_gate(27, isr27, 0x08, flags); + idt_set_gate(28, isr28, 0x08, flags); + idt_set_gate(29, isr29, 0x08, flags); + idt_set_gate(30, isr30, 0x08, flags); + idt_set_gate(31, isr31, 0x08, flags); + + // Also remap 15 entries for IRQs + idt_set_gate(32, irq0, 0x08, flags); + idt_set_gate(33, irq1, 0x08, flags); + idt_set_gate(34, irq2, 0x08, flags); + idt_set_gate(35, irq3, 0x08, flags); + idt_set_gate(36, irq4, 0x08, flags); + idt_set_gate(37, irq5, 0x08, flags); + idt_set_gate(38, irq6, 0x08, flags); + idt_set_gate(39, irq7, 0x08, flags); + idt_set_gate(40, irq8, 0x08, flags); + idt_set_gate(41, irq9, 0x08, flags); + idt_set_gate(42, irq10, 0x08, flags); + idt_set_gate(43, irq11, 0x08, flags); + idt_set_gate(44, irq12, 0x08, flags); + idt_set_gate(45, irq13, 0x08, flags); + idt_set_gate(46, irq14, 0x08, flags); + idt_set_gate(47, irq15, 0x08, flags); + + idt_flush(&idt_ptr); + + // Finally enable hardware interrupts with an assembly instruction + __asm__ __volatile__ ("sti"); + +} \ No newline at end of file diff --git a/kernel/drivers/idt.h b/kernel/drivers/idt.h new file mode 100644 index 0000000..b3c25b7 --- /dev/null +++ b/kernel/drivers/idt.h @@ -0,0 +1,123 @@ +/************************************** + * iceOS Kernel * + * Developed by Marco 'icebit' Cetica * + * (c) 2019 * + * Released under GPLv3 * + * https://github.com/ice-bit/iceOS * + ***************************************/ +#ifndef _IDT_H_ +#define _IDT_H_ +/* + * First a bit of theory: + * Sometimes you want to interrupt the processor from what it is currently doing + * and force it to do something more "critical", such as a timer update or a keyboard interrupt + * request(IRQ) fires. The processor, then, will handle those IRQs activating an + * interrupt handler(ISR, Interrupt Service Routines). In order to know where to find those + * ISRs, the CPU will use the IDT. So we can say that the Interrupt Description Table + * is another data structure(organized the same way as the GDT) that will provide a list + * of interrupts handlers(ISRs) to the CPU. + */ + +#include + +// Reserved bits in IDT entries +#define IDT_FLAG_RESERVED 0x0E + +/* Interrupt Descriptor Table */ +/* idt_flags contains access flag of a single IDT entry + * | 0 - 4 | 5 - 6 | 7 | + * | res | dpl | p | + * res: always 0x0E + * dpl: ring num (0 - 3) + * p: segment present (bool) +*/ +struct idt_flags { + uint8_t reserved: 5; + uint8_t dpl: 2; + uint8_t p: 1; +}__attribute__((packed)); +typedef struct idt_flags idt_flags_t; + + +/* idt_entry contains the value of an IDT entry + * Each entry is 64 bits(like GDT entry) + * | 0 - 15 | 16 - 31 | 32 - 39 | 40 - 47 | 48 - 63 | + * | b low | seg sel | res | flags | b high | + * b low: Lower 16 bits of the base + * seg sel: Segment selector for code segment + * res: always 0 + * flags: idt_flags struct + * b high: Higher 16 bits of the base +*/ +struct idt_entry { + uint16_t base_low; + uint16_t seg_sel; + uint8_t reserved; + idt_flags_t flags; + uint16_t base_high; +}__attribute__((packed)); +typedef struct idt_entry idt_entry_t; + +/* Also we have to define a pointer to the data structure + * This is needed to locate it later */ +struct idt_ptr { + uint16_t limit; + idt_entry_t *base; +}__attribute__((packed)); +typedef struct idt_ptr idt_ptr_t; + +/* IDT Kernel API */ +void idt_setup(); + +// ISRs method declaration +extern void isr0(); +extern void isr1(); +extern void isr2(); +extern void isr3(); +extern void isr4(); +extern void isr5(); +extern void isr6(); +extern void isr7(); +extern void isr8(); +extern void isr9(); +extern void isr10(); +extern void isr11(); +extern void isr12(); +extern void isr13(); +extern void isr14(); +extern void isr15(); +extern void isr16(); +extern void isr17(); +extern void isr18(); +extern void isr19(); +extern void isr20(); +extern void isr21(); +extern void isr22(); +extern void isr23(); +extern void isr24(); +extern void isr25(); +extern void isr26(); +extern void isr27(); +extern void isr28(); +extern void isr29(); +extern void isr30(); +extern void isr31(); + +extern void irq0(); +extern void irq1(); +extern void irq2(); +extern void irq3(); +extern void irq4(); +extern void irq5(); +extern void irq6(); +extern void irq7(); +extern void irq8(); +extern void irq9(); +extern void irq10(); +extern void irq11(); +extern void irq12(); +extern void irq13(); +extern void irq14(); +extern void irq15(); + +#endif \ No newline at end of file