diff --git a/kernel/cpu/Makefile b/kernel/cpu/Makefile index ef907f4..586f446 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 idt.asm.o +OBJS = multiboot.asm.o kernel_loader.asm.o ports.asm.o gdt.asm.o idt.asm.o interrupts.asm.o ASM = nasm ASMFLAGS = -f elf diff --git a/kernel/cpu/gdt.asm b/kernel/cpu/gdt.asm index f1eab18..8c8fa90 100644 --- a/kernel/cpu/gdt.asm +++ b/kernel/cpu/gdt.asm @@ -1,3 +1,10 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; iceOS Kernel ; +; Developed by Marco 'icebit' Cetica ; +; (c) 2019 ; +; Released under GPLv3 ; +; https://github.com/ice-bit/iceOS ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; global gdt_flush ; for drivers/gdt.c section .text diff --git a/kernel/cpu/idt.asm b/kernel/cpu/idt.asm index 40ca7e8..1eea4a2 100644 --- a/kernel/cpu/idt.asm +++ b/kernel/cpu/idt.asm @@ -1,3 +1,10 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; iceOS Kernel ; +; Developed by Marco 'icebit' Cetica ; +; (c) 2019 ; +; Released under GPLv3 ; +; https://github.com/ice-bit/iceOS ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; global idt_flush ; for drivers/idt.c section .text diff --git a/kernel/cpu/interrupts.asm b/kernel/cpu/interrupts.asm new file mode 100644 index 0000000..23970a1 --- /dev/null +++ b/kernel/cpu/interrupts.asm @@ -0,0 +1,159 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; iceOS Kernel ; +; Developed by Marco 'icebit' Cetica ; +; (c) 2019 ; +; Released under GPLv3 ; +; https://github.com/ice-bit/iceOS ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +extern isr_handler ; Defined in drivers/isr.h +extern irq_handler ; Defined in drivers/isr.h + +; Let's implement all ISR in a very handy way +%macro ISR_NOERRCODE 1 + global isr%1 + isr%1: + cli ; Disable interrupts + push byte 0 ; Push dummy error code + push byte 1 ; Push interrupt number + jmp isr_common ; goto ISR handler +%endmacro + +%macro ISR_ERRCODE 1 + global isr%1 + isr%1: + cli ; Disable interrupts + push byte %1 ; Push interrupt number + jmp isr_common ; goto ISR handler +%endmacro + +; Now we have to do the same thing for Interrupt Requests, +; in this case the first parameter is the IRQ number while +; the second one is the ISR number to be remapped to +%macro IRQ 2 + global irq%1 + irq%1: + cli ; Disable interrupts + push byte 0 ; Push dummy error code + push byte %2 ; Push interrupt number + jmp irq_common ; goto IRQ handler +%endmacro + +; isr_common is a common handler for all +; Interrupt Service Routines declared in the system +; It's main scope is to save current register's states +; into the stack, call the C high level handler +; and restore the register's original values from +; the stack +isr_common: + ;; Save register's content into the stack ;; + pusha ; Push edi,esi,ebp,esp,ebx,edx,ecx,eax + + mov ax, ds ; Get 16 bits of eax(e.g ds) + push eax + + mov ax, 0x10 ; Load the kernel data segment descriptor + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + ;; Call C handler ;; + call isr_handler ; Call C handler + + ;; Restore register's content from the stack ;; + pop eax ; Restore original data segment selector + + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + popa ; Pop edi,esi,ebp,esp,ebx,edx,ecx,eax + add esp, 8 ; Cleans up pushed error code and ISR number + sti ; Re-enable interrupts + iret ; Pops 5 things: CS, EIP, EFLAGS, SS and ESp + + +; irq_common is a common handler for all +; Interrupt Requests, it's very similar to the +; ISR one +irq_common: + ;; Save register's content into the stack ;; + pusha ; Push edi,esi,ebp,esp,ebx,edx,ecx,eax + + mov ax, ds ; Get 16 bits of eax(e.g ds) + push eax + + mov ax, 0x10 ; Load the kernel data segment descriptor + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + ;; Call C handler ;; + call irq_handler ; Call C handler + + ;; Restore register's content from the stack ;; + pop ebx ; Restore original data segment selector + + mov ds, bx + mov es, bx + mov fs, bx + mov gs, bx + + popa ; Pop edi,esi,ebp,esp,ebx,edx,ecx,eax + add esp, 8 ; Cleans up pushed error code and ISR number + sti ; Re-enable interrupts + iret ; Pops 5 things: CS, EIP, EFLAGS, SS and ESp + +; Standard x86 ISRs (only 8,10-14 and 17 requires to push error codes to the stack) +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_NOERRCODE 30 +ISR_NOERRCODE 31 + +IRQ 0, 32 +IRQ 1, 33 +IRQ 2, 34 +IRQ 3, 35 +IRQ 4, 36 +IRQ 5, 37 +IRQ 6, 38 +IRQ 7, 39 +IRQ 8, 40 +IRQ 9, 41 +IRQ 10, 42 +IRQ 11, 43 +IRQ 12, 44 +IRQ 13, 45 +IRQ 14, 46 +IRQ 15, 47 \ No newline at end of file diff --git a/kernel/drivers/Makefile b/kernel/drivers/Makefile index e1d07b0..1e3bb34 100644 --- a/kernel/drivers/Makefile +++ b/kernel/drivers/Makefile @@ -1,4 +1,4 @@ -OBJS = tty.o gdt.o idt.o +OBJS = tty.o gdt.o idt.o isr.o CC = i686-elf-gcc # cross-compiler CFLAGS = -m32 -fno-stack-protector -ffreestanding -Wall -Wextra -Werror -g -c diff --git a/kernel/drivers/idt.c b/kernel/drivers/idt.c index 5d26b05..b722873 100644 --- a/kernel/drivers/idt.c +++ b/kernel/drivers/idt.c @@ -1,10 +1,12 @@ #include "idt.h" #include "../libc/string.h" +#include "ports.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); +static void pic_remap(uint8_t offset1, uint8_t offset2); idt_entry_t idt_entries[256]; // 256 interrupts idt_ptr_t idt_ptr; @@ -68,6 +70,9 @@ static void init_idt() { idt_set_gate(30, isr30, 0x08, flags); idt_set_gate(31, isr31, 0x08, flags); + // Remap PIC + pic_remap(PIC1_START_INTERRUPT, PIC2_START_INTERRUPT); + // Also remap 15 entries for IRQs idt_set_gate(32, irq0, 0x08, flags); idt_set_gate(33, irq1, 0x08, flags); @@ -90,5 +95,28 @@ static void init_idt() { // Finally enable hardware interrupts with an assembly instruction __asm__ __volatile__ ("sti"); +} +// Taken here: http://wiki.osdev.org/8259_PIC +static void pic_remap(uint8_t offset1, uint8_t offset2) { + uint8_t a1, a2; + + a1 = inb(PIC1_DATA); // Save masks + a2 = inb(PIC2_DATA); + + outb(PIC1_COMMAND, ICW1_INIT+ICW1_ICW4); // Start init sequence + outb(PIC2_COMMAND, ICW1_INIT+ICW1_ICW4); + + outb(PIC1_DATA, offset1); + outb(PIC2_DATA, offset2); + + outb(PIC1_DATA, 4); // Tell master PIC that there is a slave PIC at IRQ2 + outb(PIC1_DATA, 2); // Tell salve PIC it's cascade identity + + outb(PIC1_DATA, ICW4_8086); + outb(PIC2_DATA, ICW4_8086); + + // Restore saved masks + outb(PIC1_DATA, a1); + outb(PIC2_DATA, a2); } \ No newline at end of file diff --git a/kernel/drivers/idt.h b/kernel/drivers/idt.h index b3c25b7..d8e0727 100644 --- a/kernel/drivers/idt.h +++ b/kernel/drivers/idt.h @@ -23,6 +23,27 @@ // Reserved bits in IDT entries #define IDT_FLAG_RESERVED 0x0E +// PIC +#define PIC1 0x20 // I/O address for master PIC +#define PIC2 0xA0 // I/O address for slave PIC +#define PIC1_COMMAND PIC1 +#define PIC1_DATA (PIC1+1) +#define PIC2_COMMAND PIC2 +#define PIC2_DATA (PIC2+1) +#define PIC1_START_INTERRUPT 0x20 // Master PIC after remapping +#define PIC2_START_INTERRUPT 0x28 // Slave PIC after remapping +#define PIC_EOI 0x20 // End of interrupt +#define ICW1_ICW4 0x01 +#define ICW1_SINGLE 0x02 +#define ICW1_INTERVAL4 0x04 +#define ICW1_LEVEL 0x08 +#define ICW1_INIT 0x10 +#define ICW4_8086 0x01 // 8086/88 (MCS-80/85) mode +#define ICW4_AUTO 0x02 +#define ICW4_BUF_SLAVE 0x08 +#define ICW4_BUF_MASTER 0x0C +#define ICW4_SFNM 0x10 + /* Interrupt Descriptor Table */ /* idt_flags contains access flag of a single IDT entry * | 0 - 4 | 5 - 6 | 7 | diff --git a/kernel/drivers/isr.c b/kernel/drivers/isr.c new file mode 100644 index 0000000..ea44170 --- /dev/null +++ b/kernel/drivers/isr.c @@ -0,0 +1,87 @@ +#include +#include "isr.h" +#include "../libc/string.h" +#include "tty.h" +#include "ports.h" + +#define PIC1 0x20 // I/O address for master PIC +#define PIC2 0xA0 // I/O address for slave PIC +#define PIC1_COMMAND PIC1 +#define PIC1_DATA (PIC1+1) +#define PIC2_COMMAND PIC2 +#define PIC2_DATA (PIC2+1) +#define PIC_EOI 0x20 // End Of Interrupt command + + +// List of messages for known interrupts +uint8_t *interrupts_messages[] = { + (uint8_t*)"Division by Zero", // 0 + (uint8_t*)"Debug", + (uint8_t*)"Non-maskable interrupt", + (uint8_t*)"Breakpoint", + (uint8_t*)"Detected overflow", + (uint8_t*)"Out-of-bounds", // 5 + (uint8_t*)"Invalid opcode", + (uint8_t*)"No coprocessor", + (uint8_t*)"Double fault", + (uint8_t*)"Coprocessor segment overrun", + (uint8_t*)"Bad TSS", // 10 + (uint8_t*)"Segment not present", + (uint8_t*)"Stack fault", + (uint8_t*)"General protection fault", + (uint8_t*)"Page fault", + (uint8_t*)"Unknown interrupt", // 15 + (uint8_t*)"Coprocessor fault", + (uint8_t*)"Alignment check", + (uint8_t*)"Machine check", + (uint8_t*)"Reserved", + (uint8_t*)"Reserved", + (uint8_t*)"Reserved", + (uint8_t*)"Reserved", + (uint8_t*)"Reserved", + (uint8_t*)"Reserved", + (uint8_t*)"Reserved", + (uint8_t*)"Reserved", + (uint8_t*)"Reserved", + (uint8_t*)"Reserved", + (uint8_t*)"Reserved", + (uint8_t*)"Reserved", + (uint8_t*)"Reserved" +}; + +isr_t interrupt_handler[256]; + +void isr_handler(registers_t regs) { + if(interrupt_handler[regs.int_num] != 0) { + isr_t handler = interrupt_handler[regs.int_num]; + handler(regs); + } else { + kprint_c((uint8_t*)"Received interrupt: ", 20, LIGHT_BROWN, BLACK); + kprint_c(interrupts_messages[(uint8_t)regs.int_num], + strlen(interrupts_messages[(uint8_t)regs.int_num]), + WHITE, + BLACK + ); + kprint((uint8_t*)"\n"); + } +} + +void ack_irq(uint32_t int_num) { + // Send and End Of Interrupt(EOF) at the PICs. + if(int_num >= 40) + outb(PIC2_COMMAND, PIC_EOI); // Send reset signal to slave + outb(PIC2_COMMAND, PIC_EOI); // In any case, reset the master +} + +void irq_handler(registers_t regs) { + ack_irq(regs.int_num); + + if(interrupt_handler[regs.int_num] != 0) { + isr_t handler = interrupt_handler[regs.int_num]; + handler(regs); + } +} + +void register_interrupt_handler(uint8_t n, isr_t handler) { + interrupt_handler[n] = handler; +} \ No newline at end of file diff --git a/kernel/drivers/isr.h b/kernel/drivers/isr.h new file mode 100644 index 0000000..20b55c8 --- /dev/null +++ b/kernel/drivers/isr.h @@ -0,0 +1,71 @@ +/************************************** + * iceOS Kernel * + * Developed by Marco 'icebit' Cetica * + * (c) 2019 * + * Released under GPLv3 * + * https://github.com/ice-bit/iceOS * + ***************************************/ +#ifndef _ISR_H_ +#define _ISR_H_ +/* + * When we implement ISRs we have to keep in mind that the first 32 interrupts(and so the + * first 32 ISRs) are reserved by the CPU to signal the kernel about critical actions, + * such as divide-by-zero or a stack overflow/buffer overflow. + * + * Below ther's a list of the first, reserved, interrupts...and yeah, we have to implement + * all of them by ourself(btw in Assembly) :D + * + * 0 - Division by zero exception + * 1 - Debug exception + * 2 - Non maskable interrupt + * 3 - Breakpoint exception + * 4 - Into detected overflow + * 5 - Out of bounds exception + * 6 - Invalid opcode exception + * 7 - No coprocessor exception + * 8 - Double fault (pushes an error code) + * 9 - Coprocessor segment overrun + * 10 - Bad TSS (pushes an error code) + * 11 - Segment not present (pushes an error code) + * 12 - Stack fault (pushes an error code) + * 13 - General protection fault (pushes an error code) + * 14 - Page fault (pushes an error code) + * 15 - Unknown interrupt exception + * 16 - Coprocessor fault + * 17 - Alignment check exception + * 18 - Machine check exception + * 19-31 - Reserved */ + +#include + +#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 + +typedef struct registers { + uint32_t ds; // Data segment + uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; // Pushed with pusha + uint32_t int_num, err_code; // Interrupt number and error code + uint32_t eip, cs, eflags, usereap, ss; // Pushed by CPU +} registers_t; + +typedef void (*isr_t)(registers_t); + +void ack_irq(uint32_t int_num); +void register_interrupt_handler(uint8_t n, isr_t handler); + + +#endif diff --git a/kernel/kernel_main.c b/kernel/kernel_main.c index 18d9d5f..cd1e50c 100644 --- a/kernel/kernel_main.c +++ b/kernel/kernel_main.c @@ -1,11 +1,14 @@ #include "drivers/tty.h" #include "drivers/gdt.h" +#include "drivers/idt.h" +#include "drivers/isr.h" #include "libc/stdio.h" void kernel_main() { clear_prompt(); init_prompt(); // Initialize frame buffer gdt_setup(); // Setup Global Descriptor Table + idt_setup(); // Setup Interrupt Descriptor Table puts("Hello World!"); } \ No newline at end of file