From e8243ae6c647eeb918242a0b5f2d85af4d0c6dae Mon Sep 17 00:00:00 2001 From: jonpas Date: Thu, 18 Jan 2018 19:12:50 +0100 Subject: [PATCH] Add GDT, IDT and interrupt handlers --- copy.txt | 25 ++++++++++ src/boot.asm | 4 +- src/descriptor_tables.c | 104 ++++++++++++++++++++++++++++++++++++++++ src/descriptor_tables.h | 82 +++++++++++++++++++++++++++++++ src/gdt.asm | 25 ++++++++++ src/interrupt.asm | 82 +++++++++++++++++++++++++++++++ src/isr.c | 9 ++++ src/isr.h | 19 ++++++++ src/main.c | 7 ++- 9 files changed, 353 insertions(+), 4 deletions(-) create mode 100644 copy.txt create mode 100644 src/descriptor_tables.c create mode 100644 src/descriptor_tables.h create mode 100644 src/gdt.asm create mode 100644 src/interrupt.asm create mode 100644 src/isr.c create mode 100644 src/isr.h diff --git a/copy.txt b/copy.txt new file mode 100644 index 0000000..51b6a97 --- /dev/null +++ b/copy.txt @@ -0,0 +1,25 @@ +Welcome to Tyr (0x547972), the one-handed OS! +ß + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/boot.asm b/src/boot.asm index 6f43fe2..9cca525 100644 --- a/src/boot.asm +++ b/src/boot.asm @@ -32,11 +32,11 @@ mboot: section .text [GLOBAL start] ; Kernel entry point -[EXTERN main] ; C code entry point +[EXTERN kernel_main] ; C code entry point start: ; Load multiboot information push ebx ; Save mboot information structure cli ; Disable interrupts - call main ; Call 'main()' C function + call kernel_main ; Call 'kernel_main()' C function jmp $ ; Continue loop diff --git a/src/descriptor_tables.c b/src/descriptor_tables.c new file mode 100644 index 0000000..642c5e4 --- /dev/null +++ b/src/descriptor_tables.c @@ -0,0 +1,104 @@ +#include "descriptor_tables.h" + +// Get access to ASM functions from C code +extern void gdt_flush(u32int); +extern void idt_flush(u32int); + +// Internal function prototypes +static void init_gdt(); +static void init_idt(); +static void gdt_set_gate(s32int,u32int,u32int,u8int,u8int); +static void idt_set_gate(u8int,u32int,u16int,u8int); + +gdt_entry_t gdt_entries[5]; +gdt_ptr_t gdt_ptr; +idt_entry_t idt_entries[256]; +idt_ptr_t idt_ptr; + +// Initialization routine - zeroes all the interrupt service routines, initializes GDT and IDT +void init_descriptor_tables() { + // Initialize global descriptor table + init_gdt(); + + // Initialize interrupt descriptor table + init_idt(); +} + +static void init_gdt() { + gdt_ptr.limit = (sizeof(gdt_entry_t) * 5) - 1; + gdt_ptr.base = (u32int)&gdt_entries; + + gdt_set_gate(0, 0, 0, 0, 0); // Null segment + gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); // Code segment + gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); // Data segment + gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF); // User mode code segment + gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF); // User mode data segment + + gdt_flush((u32int)&gdt_ptr); +} + +// Sets the value of one GDT entry +static void gdt_set_gate(s32int num, u32int base, u32int limit, u8int access, u8int gran) { + gdt_entries[num].base_low = (base & 0xFFFF); + gdt_entries[num].base_middle = (base >> 16) & 0xFF; + gdt_entries[num].base_high = (base >> 24) & 0xFF; + + gdt_entries[num].limit_low = (limit & 0xFFFF); + gdt_entries[num].granularity = (limit >> 16) & 0x0F; + + gdt_entries[num].granularity |= gran & 0xF0; + gdt_entries[num].access = access; +} + +static void init_idt() { + idt_ptr.limit = sizeof(idt_entry_t) * 256 - 1; + idt_ptr.base = (u32int)&idt_entries; + + memset(&idt_entries, 0, sizeof(idt_entry_t) * 256); + + idt_set_gate( 0, (u32int)isr0 , 0x08, 0x8E); + idt_set_gate( 1, (u32int)isr1 , 0x08, 0x8E); + idt_set_gate( 2, (u32int)isr2 , 0x08, 0x8E); + idt_set_gate( 3, (u32int)isr3 , 0x08, 0x8E); + idt_set_gate( 4, (u32int)isr4 , 0x08, 0x8E); + idt_set_gate( 5, (u32int)isr5 , 0x08, 0x8E); + idt_set_gate( 6, (u32int)isr6 , 0x08, 0x8E); + idt_set_gate( 7, (u32int)isr7 , 0x08, 0x8E); + idt_set_gate( 8, (u32int)isr8 , 0x08, 0x8E); + idt_set_gate( 9, (u32int)isr9 , 0x08, 0x8E); + idt_set_gate(10, (u32int)isr10, 0x08, 0x8E); + idt_set_gate(11, (u32int)isr11, 0x08, 0x8E); + idt_set_gate(12, (u32int)isr12, 0x08, 0x8E); + idt_set_gate(13, (u32int)isr13, 0x08, 0x8E); + idt_set_gate(14, (u32int)isr14, 0x08, 0x8E); + idt_set_gate(15, (u32int)isr15, 0x08, 0x8E); + idt_set_gate(16, (u32int)isr16, 0x08, 0x8E); + idt_set_gate(17, (u32int)isr17, 0x08, 0x8E); + idt_set_gate(18, (u32int)isr18, 0x08, 0x8E); + idt_set_gate(19, (u32int)isr19, 0x08, 0x8E); + idt_set_gate(20, (u32int)isr20, 0x08, 0x8E); + idt_set_gate(21, (u32int)isr21, 0x08, 0x8E); + idt_set_gate(22, (u32int)isr22, 0x08, 0x8E); + idt_set_gate(23, (u32int)isr23, 0x08, 0x8E); + idt_set_gate(24, (u32int)isr24, 0x08, 0x8E); + idt_set_gate(25, (u32int)isr25, 0x08, 0x8E); + idt_set_gate(26, (u32int)isr26, 0x08, 0x8E); + idt_set_gate(27, (u32int)isr27, 0x08, 0x8E); + idt_set_gate(28, (u32int)isr28, 0x08, 0x8E); + idt_set_gate(29, (u32int)isr29, 0x08, 0x8E); + idt_set_gate(30, (u32int)isr30, 0x08, 0x8E); + idt_set_gate(31, (u32int)isr31, 0x08, 0x8E); + + idt_flush((u32int)&idt_ptr); +} + +static void idt_set_gate(u8int num, u32int base, u16int sel, u8int flags) { + idt_entries[num].base_lo = base & 0xFFFF; + idt_entries[num].base_hi = (base >> 16) & 0xFFFF; + + idt_entries[num].sel = sel; + idt_entries[num].always0 = 0; + + // Uncomment the OR below when user-mode is implemented, it sets the interrupt gate's privilege level to 3 + idt_entries[num].flags = flags /* | 0x60 */; +} diff --git a/src/descriptor_tables.h b/src/descriptor_tables.h new file mode 100644 index 0000000..53268f9 --- /dev/null +++ b/src/descriptor_tables.h @@ -0,0 +1,82 @@ +// Defines the interface and structures for initialising the GDT and IDT + +#include "common.h" + +// Value of one GDT entry +// Aattribute 'packed' for GCC not to change any of the alignment in the structure +struct gdt_entry_struct { + u16int limit_low; // Lower 16 bits of the limit + u16int base_low; // Lower 16 bits of the base + u8int base_middle; // Next 8 bits of the base + u8int access; // Access flags, determine what ring this segment can be used in + u8int granularity; + u8int base_high; // Last 8 bits of the base +} __attribute__((packed)); + +typedef struct gdt_entry_struct gdt_entry_t; + +// GDT pointer pointing to the start of array of GDT entries +// In format required by 'lgdt' instruction +struct gdt_ptr_struct { + u16int limit; // Upper 16 bits of all selector limits + u32int base; // Address of the first gdt_entry_t struct +} __attribute__((packed)); + +typedef struct gdt_ptr_struct gdt_ptr_t; + +// Interrupt gate +struct idt_entry_struct { + u16int base_lo; // Lower 16 bits of the address to jump to when this interrupt fires + u16int sel; // Kernel segment selector + u8int always0; // Must always be zero + u8int flags; + u16int base_hi; // Upper 16 bits of the address to jump to +} __attribute__((packed)); + +typedef struct idt_entry_struct idt_entry_t; + +// Pointer to an array of interrupt handlers +// In format suitable for 'lidt' +struct idt_ptr_struct { + u16int limit; + u32int base; // Address of the first element in 'idt_entry_t' array +} __attribute__((packed)); + +typedef struct idt_ptr_struct idt_ptr_t; + +// Extern directives for accessing the addresses of ASM ISR handlers +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(); + +// Initialization function +void init_descriptor_tables(); diff --git a/src/gdt.asm b/src/gdt.asm new file mode 100644 index 0000000..a72faa7 --- /dev/null +++ b/src/gdt.asm @@ -0,0 +1,25 @@ +; Sets up global descriptor table and interrupt descriptor table. + +[GLOBAL gdt_flush] ; Make 'gdt_flush' accessible from C + +gdt_flush: + mov eax, [esp+4] ; Get pointer to GDT, passed as a parameter + lgdt [eax] ; Load new GDT pointer + + mov ax, 0x10 ; 0x10 is offset in GDT to our data segment + mov ds, ax ; Load all data segment selectors + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + jmp 0x08:.flush ; 0x08 is offset to our code segment +.flush: + ret + + +[GLOBAL idt_flush] ; Make 'idt_flush' accessible from C + +idt_flush: + mov eax, [esp+4] ; Get pointer to the IDT, passed as a parameter + lidt [eax] ; Load IDT pointer + ret diff --git a/src/interrupt.asm b/src/interrupt.asm new file mode 100644 index 0000000..6649720 --- /dev/null +++ b/src/interrupt.asm @@ -0,0 +1,82 @@ +; Contains interrupt service routine wrappers. + +; Macro creating a stub for an ISR which does NOT pass it's own error code (adds a dummy errcode byte) +%macro ISR_NOERRCODE 1 + [GLOBAL isr%1] + isr%1: + cli ; Disable interrupts firstly + push byte 0 ; Push a dummy error code + push byte %1 ; Push the interrupt number + jmp isr_common_stub ; Go to common handler code +%endmacro + +; Macro creating a stub for an ISR which passes it's own error code. +%macro ISR_ERRCODE 1 + [GLOBAL isr%1] + isr%1: + cli ; Disable interrupts + push byte %1 ; Push the interrupt number + jmp isr_common_stub +%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_ERRCODE 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_ERRCODE 30 +ISR_NOERRCODE 31 + +[EXTERN isr_handler] ; In isr.c + +; Common ISR stub, saves the processor state, sets up for kernel mode segments, +; calls the C-level fault handler, and finally restores the stack frame +isr_common_stub: + pusha ; Push registers + + mov ax, ds ; Lower 16-bits of eax = ds + push eax ; Save the data segment descriptor + + mov ax, 0x10 ; load the kernel data segment descriptor + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + call isr_handler + + pop ebx ; Reload the original data segment descriptor + mov ds, bx + mov es, bx + mov fs, bx + mov gs, bx + + popa ; Pop registers + add esp, 8 ; Clean up the pushed error code and pushed ISR number + sti + iret ; Pop CS, EIP, EFLAGS, SS, and ESP diff --git a/src/isr.c b/src/isr.c new file mode 100644 index 0000000..fd7711b --- /dev/null +++ b/src/isr.c @@ -0,0 +1,9 @@ +#include "isr.h" +#include "monitor.h" + +// Called from ASM interrupt handler stub +void isr_handler(registers_t regs) { + monitor_write("recieved interrupt: "); + monitor_write_dec(regs.int_no); + monitor_put('\n'); +} diff --git a/src/isr.h b/src/isr.h new file mode 100644 index 0000000..447935b --- /dev/null +++ b/src/isr.h @@ -0,0 +1,19 @@ +// Interface and structures for high level interrupt service routines + +#include "common.h" + +typedef struct registers { + // Data segment selector + u32int ds; + + // Pushed by pusha + // 'useless_value' instead of 'esp', because it has to do with the current stack context, not what was interrupted (proper 'esp' below) + u32int edi, esi, ebp, useless_value, ebx, edx, ecx, eax; + + // Interrupt number and error code (if applicable) + u32int int_no, err_code; + + // Pushed by the processor automatically + // Proper 'esp' here + u32int eip, cs, eflags, esp, ss; +} registers_t; diff --git a/src/main.c b/src/main.c index 21b52fd..50d8f9d 100644 --- a/src/main.c +++ b/src/main.c @@ -2,11 +2,14 @@ struct multiboot; -int main(struct multiboot *mboot_ptr) { +int kernel_main(struct multiboot *mboot_ptr) { monitor_clear(); monitor_write("Welcome to Tyr ("); monitor_write_hex(0x547972); - monitor_write("), the one-handed OS!"); + monitor_write("), the one-handed OS!\n"); + + asm volatile("int $0x3"); + //asm volatile("int $0x4"); return 0; }