From ee9839beaa7fddf1b9108c77ea6d787dd7b3deae Mon Sep 17 00:00:00 2001 From: pjht Date: Sun, 10 Feb 2019 14:11:07 -0600 Subject: [PATCH] Get GDT,IDT and usermode working! --- cpu/i386/cpu_init.c | 4 + cpu/i386/gdt.c | 14 +- cpu/i386/idt.c | 20 ++ cpu/i386/idt.h | 39 ++++ cpu/i386/interrupt.asm | 431 +++++++++++++++++++++++++++++++++++++++++ cpu/i386/isr.c | 181 +++++++++++++++++ cpu/i386/isr.h | 89 +++++++++ cpu/i386/seg_upd.asm | 11 ++ cpu/i386/seg_upd.h | 6 + kernel/kernel.c | 19 ++ psinfo/i386/o.txt | 2 + 11 files changed, 810 insertions(+), 6 deletions(-) create mode 100644 cpu/i386/idt.c create mode 100644 cpu/i386/idt.h create mode 100644 cpu/i386/interrupt.asm create mode 100644 cpu/i386/isr.c create mode 100644 cpu/i386/isr.h create mode 100644 cpu/i386/seg_upd.asm create mode 100644 cpu/i386/seg_upd.h diff --git a/cpu/i386/cpu_init.c b/cpu/i386/cpu_init.c index 5a709e8..b015d04 100644 --- a/cpu/i386/cpu_init.c +++ b/cpu/i386/cpu_init.c @@ -1,6 +1,10 @@ #include "gdt.h" #include "paging.h" +#include "isr.h" + void cpu_init() { gdt_init(); + isr_install(); + asm volatile("sti"); paging_init(); } diff --git a/cpu/i386/gdt.c b/cpu/i386/gdt.c index 25e9559..76cbf83 100644 --- a/cpu/i386/gdt.c +++ b/cpu/i386/gdt.c @@ -1,4 +1,5 @@ #include +#include "seg_upd.h" #define NUM_ENTRIES 5 typedef struct { @@ -8,14 +9,14 @@ typedef struct { uint8_t access; uint8_t limit_flags; uint8_t base_high8; -} gdt_entry; +} __attribute__((packed)) gdt_entry; typedef struct { uint16_t size; uint32_t address; -} gdt_description; +} __attribute__((packed)) gdt_description; -gdt_entry gdt[NUM_ENTRIES-1]; +gdt_entry gdt[NUM_ENTRIES]; gdt_description gdt_desc; @@ -26,8 +27,9 @@ void gdt_init() { set_entry(3,0,0xFFFFF,0xFA); set_entry(4,0,0xFFFFF,0xF2); gdt_desc.size=(sizeof(gdt_entry)*NUM_ENTRIES)-1; - gdt_desc.address=&gdt[0]; - asm volatile("lgdt gdt_desc"); + gdt_desc.address=&gdt; + asm volatile("lgdt (%%eax)"::"a"((uint32_t)&gdt_desc)); + seg_upd(); } void set_entry(int i,uint32_t base,uint32_t limit,uint8_t access) { @@ -36,6 +38,6 @@ void set_entry(int i,uint32_t base,uint32_t limit,uint8_t access) { gdt[i].base_mid8=(base&0xFF0000)>>16; gdt[i].access=access; uint8_t limit_high4=(limit&0xF0000)>>16; - gdt[i].limit_flags=0xC0&limit_high4; + gdt[i].limit_flags=0xC0|limit_high4; gdt[i].base_high8=(base&0xFF000000)>>24; } diff --git a/cpu/i386/idt.c b/cpu/i386/idt.c new file mode 100644 index 0000000..fc1551e --- /dev/null +++ b/cpu/i386/idt.c @@ -0,0 +1,20 @@ +#include "idt.h" +#include + +#define low_16(address) (uint16_t)((address) & 0xFFFF) +#define high_16(address) (uint16_t)(((address) >> 16) & 0xFFFF) + +void set_idt_gate(int n,uint32_t handler) { + idt[n].low_offset=low_16(handler); + idt[n].sel=KERNEL_CS; + idt[n].always0=0; + idt[n].flags=0x8E; + idt[n].high_offset=high_16(handler); +} + +void set_idt() { + idt_reg.base=(uint32_t) &idt; + idt_reg.limit=IDT_ENTRIES * sizeof(idt_gate_t) - 1; + /* Don't make the mistake of loading &idt -- always load &idt_reg */ + asm volatile("lidtl (%0)":: "r" (&idt_reg)); +} diff --git a/cpu/i386/idt.h b/cpu/i386/idt.h new file mode 100644 index 0000000..9796723 --- /dev/null +++ b/cpu/i386/idt.h @@ -0,0 +1,39 @@ +#ifndef IDT_H +#define IDT_H + +#include + +/* Segment selectors */ +#define KERNEL_CS 0x08 + +/* How every interrupt gate (handler) is defined */ +typedef struct { + uint16_t low_offset; /* Lower 16 bits of handler function address */ + uint16_t sel; /* Kernel segment selector */ + uint8_t always0; + /* First byte + * Bit 7: "Interrupt is present" + * Bits 6-5: Privilege level of caller (0=kernel..3=user) + * Bit 4: Set to 0 for interrupt gates + * Bits 3-0: bits 1110 = decimal 14 = "32 bit interrupt gate" */ + uint8_t flags; + uint16_t high_offset; /* Higher 16 bits of handler function address */ +} __attribute__((packed)) idt_gate_t ; + +/* A pointer to the array of interrupt handlers. + * Assembly instruction 'lidt' will read it */ +typedef struct { + uint16_t limit; + uint32_t base; +} __attribute__((packed)) idt_register_t; + +#define IDT_ENTRIES 256 +idt_gate_t idt[IDT_ENTRIES]; +idt_register_t idt_reg; + + +/* Functions implemented in idt.c */ +void set_idt_gate(int n,uint32_t handler); +void set_idt(); + +#endif diff --git a/cpu/i386/interrupt.asm b/cpu/i386/interrupt.asm new file mode 100644 index 0000000..525c956 --- /dev/null +++ b/cpu/i386/interrupt.asm @@ -0,0 +1,431 @@ +; Defined in isr.c +[extern isr_handler] +[extern irq_handler] + +; Common ISR code +isr_common_stub: + ; 1. Save CPU state + pusha ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax + mov ax, ds ; Lower 16-bits of eax = ds. + push eax ; save the data segment descriptor + mov ax, 0x10 ; kernel data segment descriptor + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + ; 2. Call C handler + call isr_handler + ; 3. Restore state + pop eax + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + popa + add esp, 8 ; Cleans up the pushed error code and pushed ISR number + sti + iret ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP + +; Common IRQ code. Identical to ISR code except for the 'call' +; and the 'pop ebx' +irq_common_stub: + pusha + mov ax, ds + push eax + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + call irq_handler ; Different than the ISR code + pop ebx ; Different than the ISR code + mov ds, bx + mov es, bx + mov fs, bx + mov gs, bx + popa + add esp, 8 + sti + iret + +; We don't get information about which interrupt was caller +; when the handler is run, so we will need to have a different handler +; for every interrupt. +; Furthermore, some interrupts push an error code onto the stack but others +; don't, so we will push a dummy error code for those which don't, so that +; we have a consistent stack for all of them. + +; First make the ISRs global +global isr0 +global isr1 +global isr2 +global isr3 +global isr4 +global isr5 +global isr6 +global isr7 +global isr8 +global isr9 +global isr10 +global isr11 +global isr12 +global isr13 +global isr14 +global isr15 +global isr16 +global isr17 +global isr18 +global isr19 +global isr20 +global isr21 +global isr22 +global isr23 +global isr24 +global isr25 +global isr26 +global isr27 +global isr28 +global isr29 +global isr30 +global isr31 +global isr80 +; IRQs +global irq0 +global irq1 +global irq2 +global irq3 +global irq4 +global irq5 +global irq6 +global irq7 +global irq8 +global irq9 +global irq10 +global irq11 +global irq12 +global irq13 +global irq14 +global irq15 + +; 0: Divide By Zero Exception +isr0: + cli + push byte 0 + push byte 0 + jmp isr_common_stub + +; 1: Debug Exception +isr1: + cli + push byte 0 + push byte 1 + jmp isr_common_stub + +; 2: Non Maskable Interrupt Exception +isr2: + cli + push byte 0 + push byte 2 + jmp isr_common_stub + +; 3: Int 3 Exception +isr3: + cli + push byte 0 + push byte 3 + jmp isr_common_stub + +; 4: INTO Exception +isr4: + cli + push byte 0 + push byte 4 + jmp isr_common_stub + +; 5: Out of Bounds Exception +isr5: + cli + push byte 0 + push byte 5 + jmp isr_common_stub + +; 6: Invalid Opcode Exception +isr6: + cli + push byte 0 + push byte 6 + jmp isr_common_stub + +; 7: Coprocessor Not Available Exception +isr7: + cli + push byte 0 + push byte 7 + jmp isr_common_stub + +; 8: Double Fault Exception (With Error Code!) +isr8: + cli + push byte 8 + jmp isr_common_stub + +; 9: Coprocessor Segment Overrun Exception +isr9: + cli + push byte 0 + push byte 9 + jmp isr_common_stub + +; 10: Bad TSS Exception (With Error Code!) +isr10: + cli + push byte 10 + jmp isr_common_stub + +; 11: Segment Not Present Exception (With Error Code!) +isr11: + cli + push byte 11 + jmp isr_common_stub + +; 12: Stack Fault Exception (With Error Code!) +isr12: + cli + push byte 12 + jmp isr_common_stub + +; 13: General Protection Fault Exception (With Error Code!) +isr13: + cli + push byte 13 + jmp isr_common_stub + +; 14: Page Fault Exception (With Error Code!) +isr14: + cli + push byte 14 + jmp isr_common_stub + +; 15: Reserved Exception +isr15: + cli + push byte 0 + push byte 15 + jmp isr_common_stub + +; 16: Floating Point Exception +isr16: + cli + push byte 0 + push byte 16 + jmp isr_common_stub + +; 17: Alignment Check Exception +isr17: + cli + push byte 0 + push byte 17 + jmp isr_common_stub + +; 18: Machine Check Exception +isr18: + cli + push byte 0 + push byte 18 + jmp isr_common_stub + +; 19: Reserved +isr19: + cli + push byte 0 + push byte 19 + jmp isr_common_stub + +; 20: Reserved +isr20: + cli + push byte 0 + push byte 20 + jmp isr_common_stub + +; 21: Reserved +isr21: + cli + push byte 0 + push byte 21 + jmp isr_common_stub + +; 22: Reserved +isr22: + cli + push byte 0 + push byte 22 + jmp isr_common_stub + +; 23: Reserved +isr23: + cli + push byte 0 + push byte 23 + jmp isr_common_stub + +; 24: Reserved +isr24: + cli + push byte 0 + push byte 24 + jmp isr_common_stub + +; 25: Reserved +isr25: + cli + push byte 0 + push byte 25 + jmp isr_common_stub + +; 26: Reserved +isr26: + cli + push byte 0 + push byte 26 + jmp isr_common_stub + +; 27: Reserved +isr27: + cli + push byte 0 + push byte 27 + jmp isr_common_stub + +; 28: Reserved +isr28: + cli + push byte 0 + push byte 28 + jmp isr_common_stub + +; 29: Reserved +isr29: + cli + push byte 0 + push byte 29 + jmp isr_common_stub + +; 30: Reserved +isr30: + cli + push byte 0 + push byte 30 + jmp isr_common_stub + +; 31: Reserved +isr31: + cli + push byte 0 + push byte 31 + jmp isr_common_stub + +; 80: Syscalls +isr80: + cli + push byte 0 + push byte 80 + jmp isr_common_stub + +; IRQ handlers +irq0: + cli + push byte 0 + push byte 32 + jmp irq_common_stub + +irq1: + cli + push byte 1 + push byte 33 + jmp irq_common_stub + +irq2: + cli + push byte 2 + push byte 34 + jmp irq_common_stub + +irq3: + cli + push byte 3 + push byte 35 + jmp irq_common_stub + +irq4: + cli + push byte 4 + push byte 36 + jmp irq_common_stub + +irq5: + cli + push byte 5 + push byte 37 + jmp irq_common_stub + +irq6: + cli + push byte 6 + push byte 38 + jmp irq_common_stub + +irq7: + cli + push byte 7 + push byte 39 + jmp irq_common_stub + +irq8: + cli + push byte 8 + push byte 40 + jmp irq_common_stub + +irq9: + cli + push byte 9 + push byte 41 + jmp irq_common_stub + +irq10: + cli + push byte 10 + push byte 42 + jmp irq_common_stub + +irq11: + cli + push byte 11 + push byte 43 + jmp irq_common_stub + +irq12: + cli + push byte 12 + push byte 44 + jmp irq_common_stub + +irq13: + cli + push byte 13 + push byte 45 + jmp irq_common_stub + +irq14: + cli + push byte 14 + push byte 46 + jmp irq_common_stub + +irq15: + cli + push byte 15 + push byte 47 + jmp irq_common_stub diff --git a/cpu/i386/isr.c b/cpu/i386/isr.c new file mode 100644 index 0000000..a139ebe --- /dev/null +++ b/cpu/i386/isr.c @@ -0,0 +1,181 @@ +#include "isr.h" +#include "idt.h" +#include "ports.h" +#include "../halt.h" +#include "../drivers/vga.h" +#include +#include +void irq_handler(registers_t r); +isr_t interrupt_handlers[256]; + +/* Can't do this with a loop because we need the address + * of the function names */ +void isr_install() { + set_idt_gate(0,(uint32_t)isr0); + set_idt_gate(1,(uint32_t)isr1); + set_idt_gate(2,(uint32_t)isr2); + set_idt_gate(3,(uint32_t)isr3); + set_idt_gate(4,(uint32_t)isr4); + set_idt_gate(5,(uint32_t)isr5); + set_idt_gate(6,(uint32_t)isr6); + set_idt_gate(7,(uint32_t)isr7); + set_idt_gate(8,(uint32_t)isr8); + set_idt_gate(9,(uint32_t)isr9); + set_idt_gate(10,(uint32_t)isr10); + set_idt_gate(11,(uint32_t)isr11); + set_idt_gate(12,(uint32_t)isr12); + set_idt_gate(13,(uint32_t)isr13); + set_idt_gate(14,(uint32_t)isr14); + set_idt_gate(15,(uint32_t)isr15); + set_idt_gate(16,(uint32_t)isr16); + set_idt_gate(17,(uint32_t)isr17); + set_idt_gate(18,(uint32_t)isr18); + set_idt_gate(19,(uint32_t)isr19); + set_idt_gate(20,(uint32_t)isr20); + set_idt_gate(21,(uint32_t)isr21); + set_idt_gate(22,(uint32_t)isr22); + set_idt_gate(23,(uint32_t)isr23); + set_idt_gate(24,(uint32_t)isr24); + set_idt_gate(25,(uint32_t)isr25); + set_idt_gate(26,(uint32_t)isr26); + set_idt_gate(27,(uint32_t)isr27); + set_idt_gate(28,(uint32_t)isr28); + set_idt_gate(29,(uint32_t)isr29); + set_idt_gate(30,(uint32_t)isr30); + set_idt_gate(31,(uint32_t)isr31); + set_idt_gate(80,(uint32_t)isr80); + // Remap the PIC + port_byte_out(0x20,0x11); + port_byte_out(0xA0,0x11); + port_byte_out(0x21,0x20); + port_byte_out(0xA1,0x28); + port_byte_out(0x21,0x04); + port_byte_out(0xA1,0x02); + port_byte_out(0x21,0x01); + port_byte_out(0xA1,0x01); + port_byte_out(0x21,0x0); + port_byte_out(0xA1,0x0); + + // Install the IRQs + set_idt_gate(32,(uint32_t)irq0); + set_idt_gate(33,(uint32_t)irq1); + set_idt_gate(34,(uint32_t)irq2); + set_idt_gate(35,(uint32_t)irq3); + set_idt_gate(36,(uint32_t)irq4); + set_idt_gate(37,(uint32_t)irq5); + set_idt_gate(38,(uint32_t)irq6); + set_idt_gate(39,(uint32_t)irq7); + set_idt_gate(40,(uint32_t)irq8); + set_idt_gate(41,(uint32_t)irq9); + set_idt_gate(42,(uint32_t)irq10); + set_idt_gate(43,(uint32_t)irq11); + set_idt_gate(44,(uint32_t)irq12); + set_idt_gate(45,(uint32_t)irq13); + set_idt_gate(46,(uint32_t)irq14); + set_idt_gate(47,(uint32_t)irq15); + + set_idt(); // Load with ASM +} + + +/* To print the message which defines every exception */ +char *exception_messages[] = { + "Division By Zero", + "Debug", + "Non Maskable Interrupt", + "Breakpoint", + "Into Detected Overflow", + "Out of Bounds", + "Invalid Opcode", + "No Coprocessor", + + "Double Fault", + "Coprocessor Segment Overrun", + "Bad TSS", + "Segment Not Present", + "Stack Fault", + "General Protection Fault", + "Page Fault", + "Unknown Interrupt", + + "Coprocessor Fault", + "Alignment Check", + "Machine Check", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved" +}; + +void isr_handler(registers_t r) { + switch (r.int_no) { + case 14: { + uint32_t addr; + asm("movl %%cr2,%0": "=r"(addr)); + vga_write_string("FAULT\n"); + // if (r.err_code==0) { + // klog("PANIC","Kernel process tried to read a non-present page entry at address %x",addr); + // } else if (r.err_code==1) { + // klog("PANIC","Kernel process tried to read a page and caused a protection fault at address %x",addr); + // } else if (r.err_code==2) { + // klog("PANIC","Kernel process tried to write to a non-present page entry at address %x",addr); + // } else if (r.err_code==3) { + // klog("PANIC","Kernel process tried to write a page and caused a protection fault at address %x",addr); + // } else if (r.err_code==4) { + // klog("PANIC","User process tried to read a non-present page entry at address %x",addr); + // } else if (r.err_code==5) { + // klog("PANIC","User process tried to read a page and caused a protection fault at address %x",addr); + // } else if (r.err_code==6) { + // klog("PANIC","User process tried to write to a non-present page entry at address %x",addr); + // } else if (r.err_code==7) { + // klog("PANIC","User process tried to write a page and caused a protection fault at address %x",addr); + // } + // if ((r.err_code&1)==0) { + // // int dir_entry=(addr&0xFFC00000)>>22; + // // int table_entry=(addr&0x3FF000)>12; + // // if (dir_entry_present(dir_entry)) { + // // set_table_entry(dir_entry,table_entry,((dir_entry*1024)+table_entry)*0x1000,1,1,1); + // // for(int page=0;page<1024;page++) { + // // asm volatile("invlpg (%0)"::"r"(((dir_entry*1024)+page)*0x1000):"memory"); + // // } + // // } else { + // // for(int page=0;page<1024;page++) { + // // set_table_entry(dir_entry,page,0x0,1,1,0); + // // } + // // set_table_entry(dir_entry,table_entry,((dir_entry*1024)+table_entry)*0x1000,1,1,1); + // // set_directory_entry(dir_entry,dir_entry,1,1,1); + // // } + // // return; + // } + halt(); + break; + } + } +} + + +void register_interrupt_handler(uint8_t n,isr_t handler) { + interrupt_handlers[n] = handler; +} + +void irq_handler(registers_t r) { + /* After every interrupt we need to send an EOI to the PICs + * or they will not send another interrupt again */ + if (r.int_no >= 40) port_byte_out(0xA0,0x20); /* slave */ + port_byte_out(0x20,0x20); /* master */ + /* Handle the interrupt in a more modular way */ + if (interrupt_handlers[r.int_no] != 0) { + isr_t handler = interrupt_handlers[r.int_no]; + handler(r); + } +} diff --git a/cpu/i386/isr.h b/cpu/i386/isr.h new file mode 100644 index 0000000..fa63adf --- /dev/null +++ b/cpu/i386/isr.h @@ -0,0 +1,89 @@ +#ifndef ISR_H +#define ISR_H + +#include + +/* ISRs reserved for CPU exceptions */ +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 isr80(); +/* IRQ definitions */ +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(); + +#define IRQ0 32 +#define IRQ1 33 +#define IRQ2 34 +#define IRQ3 35 +#define IRQ4 36 +#define IRQ5 37 +#define IRQ6 38 +#define IRQ7 39 +#define IRQ8 40 +#define IRQ9 41 +#define IRQ10 42 +#define IRQ11 43 +#define IRQ12 44 +#define IRQ13 45 +#define IRQ14 46 +#define IRQ15 47 + +/* Struct which aggregates many registers */ +typedef struct { + uint32_t ds; /* Data segment selector */ + uint32_t edi,esi,ebp,esp,ebx,edx,ecx,eax; /* Pushed by pusha. */ + uint32_t int_no,err_code; /* Interrupt number and error code (if applicable) */ + uint32_t eip,cs,eflags,useresp,ss; /* Pushed by the processor automatically */ +} registers_t; + +void isr_install(); +void isr_handler(registers_t r); + +typedef void (*isr_t)(registers_t); +void register_interrupt_handler(uint8_t n,isr_t handler); + +#endif diff --git a/cpu/i386/seg_upd.asm b/cpu/i386/seg_upd.asm new file mode 100644 index 0000000..d627c8d --- /dev/null +++ b/cpu/i386/seg_upd.asm @@ -0,0 +1,11 @@ +global seg_upd +seg_upd: +jmp 0x8:code_upd +code_upd: +mov ax, 0x10 +mov ds, ax +mov ss, ax +mov es, ax +mov fs, ax +mov gs, ax +ret diff --git a/cpu/i386/seg_upd.h b/cpu/i386/seg_upd.h new file mode 100644 index 0000000..76c0248 --- /dev/null +++ b/cpu/i386/seg_upd.h @@ -0,0 +1,6 @@ +#ifndef SEG_UPD_H +#define SEG_UPD_H + +void seg_upd(); + +#endif diff --git a/kernel/kernel.c b/kernel/kernel.c index 9c1899e..0cd1d23 100644 --- a/kernel/kernel.c +++ b/kernel/kernel.c @@ -17,4 +17,23 @@ void kmain(multiboot_info_t* header) { info.height=25; } vga_init(info); + vga_write_string("Hello\n"); + asm volatile(" \ + cli; \ + mov $0x23, %ax; \ + mov %ax, %ds; \ + mov %ax, %es; \ + mov %ax, %fs; \ + mov %ax, %gs; \ + \ + mov %esp, %eax; \ + pushl $0x23; \ + pushl %eax; \ + pushf; \ + pushl $0x1B; \ + push $1f; \ + iret; \ + 1: \ + "); + vga_write_string("UMODE!"); } diff --git a/psinfo/i386/o.txt b/psinfo/i386/o.txt index 7f48480..47e5a76 100644 --- a/psinfo/i386/o.txt +++ b/psinfo/i386/o.txt @@ -1,2 +1,4 @@ +cpu/i386/interrupt.o cpu/i386/paging_helpers.o +cpu/i386/seg_upd.o kernel/boot.o