diff --git a/.gitignore b/.gitignore index e4f4aca..954ab34 100644 --- a/.gitignore +++ b/.gitignore @@ -60,4 +60,8 @@ dkms.conf *.img -.vscode \ No newline at end of file +.vscode + + +build/* +build/*.inc \ No newline at end of file diff --git a/Makefile b/Makefile index 7299871..aa320e7 100644 --- a/Makefile +++ b/Makefile @@ -35,6 +35,8 @@ DOCKER_RUN := docker run --rm \ -e PATH=/home/linuxbrew/.linuxbrew/bin:$$PATH \ $(DOCKER_IMAGE) +INCLUDE_DIRS := $(shell find $(INCLUDE_DIR) -type d) + CFLAGS := \ -g \ -O0 \ @@ -44,8 +46,8 @@ CFLAGS := \ -nostdlib \ -nostartfiles \ -nodefaultlibs \ - -I$(INCLUDE_DIR) - + $(foreach dir,$(INCLUDE_DIRS),-I$(dir)) + .PHONY: all clean run debug format format-check lint all: $(OS_BIN) @@ -109,6 +111,12 @@ run: $(OS_BIN) -m 512M \ -serial stdio +setup: + @if ! docker image inspect $(DOCKER_IMAGE):latest >/dev/null 2>&1; then \ + docker build -t $(DOCKER_IMAGE):latest .; \ + fi + $(MAKE) + debug: $(OS_BIN) qemu-system-i386 \ diff --git a/includes/ps2.h b/includes/driver/ps2.h similarity index 100% rename from includes/ps2.h rename to includes/driver/ps2.h diff --git a/includes/vga.h b/includes/driver/vga.h similarity index 93% rename from includes/vga.h rename to includes/driver/vga.h index 8c9c797..7ac5eaf 100644 --- a/includes/vga.h +++ b/includes/driver/vga.h @@ -29,6 +29,9 @@ #define LIGHT_FLAG 0x08 +#define DEFUALT_F LIGHT_GRAY +#define DEFUALT_B BLACK + typedef union vga_entry { uint16_t value; @@ -46,6 +49,8 @@ void vga_print_char(char c); void vga_print(const char *str); +void vga_print_hex(uint32_t value); + void vga_scan_char(char c); void vga_scan(const char *str); diff --git a/includes/idt.h b/includes/interrupt/idt.h similarity index 92% rename from includes/idt.h rename to includes/interrupt/idt.h index 4d44bd0..c5ec1cd 100644 --- a/includes/idt.h +++ b/includes/interrupt/idt.h @@ -8,7 +8,8 @@ #define IDT_INTERRUPT_GATE 0x8E #define KERNEL_CODE_SELECTOR 0x08 -extern uint32_t isr_table[32]; +extern uint32_t isr_table[256]; +extern uint32_t irq_table[16]; struct idt_entry { uint16_t offset_low; // handler address bits 0..15 diff --git a/includes/interrupt/irq.h b/includes/interrupt/irq.h new file mode 100644 index 0000000..e69de29 diff --git a/includes/interrupt/isr.h b/includes/interrupt/isr.h new file mode 100644 index 0000000..a719e6e --- /dev/null +++ b/includes/interrupt/isr.h @@ -0,0 +1,43 @@ +#ifndef ISR_H +#define ISR_H + +#include + +char *EXCEPTIONS[] = { + "Division Error", // 00 + "Debug", // 01 + "Non-maskable Interrupt", // 02 + "Breakpoint", // 03 + "Overflow", // 04 + "Bound Range Exceeded", // 05 + "Invalid Opcode", // 06 + "Device Not Available", // 07 + "Double Fault", // 08 + "Coprocessor Segment Overrun", // 09 + "Invalid TSS", // 10 + "Segment Not Present", // 11 + "Stack-Segment Fault", // 12 + "General Protection Fault", // 13 + "Page Fault", // 14 + "-15", // 15 + "x87 Floating-Point Exception", // 16 + "Alignment Check", // 17 + "Machine Check", // 18 + "SIMD Floating-Point Exception", // 19 + "Virtualization Exception", // 20 + "Control Protection Exception", // 21 + "-22", // 22 + "-23", // 23 + "-24", // 24 + "-25", // 25 + "-26", // 26 + "-27", // 27 + "Hypervisor Injection Exception", // 28 + "VMM Communication Exception", // 29 + "Security Exception", // 30 + "-31" // 31 +}; + +void isr_handler(uint32_t vector); + +#endif \ No newline at end of file diff --git a/includes/isr.h b/includes/isr.h deleted file mode 100644 index 786d4dc..0000000 --- a/includes/isr.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef ISR_H -#define ISR_H - -void isr_handler(); - -#endif \ No newline at end of file diff --git a/includes/lib/vga_lib.h b/includes/lib/vga_lib.h new file mode 100644 index 0000000..492d4a5 --- /dev/null +++ b/includes/lib/vga_lib.h @@ -0,0 +1,35 @@ +#ifndef VGA_LIB_H +#define VGA_LIB_H + +#include + +/* + * Logging + */ +void vga_log_info(const char *str0, const char *str, const char *str2); +void vga_log_success(const char *str0, const char *str, const char *str2); +void vga_log_warning(const char *str0, const char *str, const char *str2); +void vga_log_error(const char *str0, const char *str, const char *str2); + +/* + * Text utilities + */ +void vga_println(const char *str); +void vga_print_at_end(const char *str); +void vga_print_at_end_c(const char *str, uint8_t color); +void vga_print_line_at(uint8_t row, const char *str); +void vga_print_centered(const char *str, uint8_t row); + +/* + * Screen utilities + */ +void vga_clear_line(uint8_t row); +void vga_clear_screen(void); + +/* + * Cursor utilities + */ +void vga_newline(void); +void vga_home(void); + +#endif \ No newline at end of file diff --git a/includes/panic.h b/includes/panic.h new file mode 100644 index 0000000..2054d72 --- /dev/null +++ b/includes/panic.h @@ -0,0 +1,6 @@ +#ifndef PANIC_H +#define PANIC_H + +void panic(char *err); +void exception(char *err); +#endif \ No newline at end of file diff --git a/includes/utils.h b/includes/utils.h index cd800ef..eeb906f 100644 --- a/includes/utils.h +++ b/includes/utils.h @@ -12,4 +12,6 @@ void cli(void); void sti(void); void hlt(void); +uint32_t read_eflags(void); + #endif \ No newline at end of file diff --git a/src/driver/pit.c b/src/driver/pit.c new file mode 100644 index 0000000..e69de29 diff --git a/src/driver/vga.c b/src/driver/vga.c index b5541e5..d475c34 100644 --- a/src/driver/vga.c +++ b/src/driver/vga.c @@ -46,6 +46,17 @@ void vga_print(const char *str) { } } +void vga_print_hex(uint32_t value) { + const char hex[] = "0123456789ABCDEF"; + + vga_print("0x"); + + for (int i = 7; i >= 0; i--) { + uint8_t digit = (value >> (i * 4)) & 0xF; + vga_print_char(hex[digit]); + } +} + void vga_set_color(uint8_t foreground, uint8_t background) { current_color = (background << 4 | foreground); } diff --git a/src/interrupt/idt.c b/src/interrupt/idt.c index a1bfac8..c2ae253 100644 --- a/src/interrupt/idt.c +++ b/src/interrupt/idt.c @@ -29,9 +29,15 @@ void idt_init(void) { idt_init_descriptor(); + // isr for (uint8_t i = 0; i < 32; i++) { idt_set_gate(i, isr_table[i], KERNEL_CODE_SELECTOR, IDT_INTERRUPT_GATE); } + // irq + for (uint8_t i = 32; i < 48; i++) { + idt_set_gate(i, irq_table[i - 32], KERNEL_CODE_SELECTOR, IDT_INTERRUPT_GATE); + } + idt_load(); } diff --git a/src/interrupt/irq.asm b/src/interrupt/irq.asm new file mode 100644 index 0000000..f34115f --- /dev/null +++ b/src/interrupt/irq.asm @@ -0,0 +1,149 @@ +; ============================================================ +; IRQ handlers 0-15 +; +; PIC IRQs: +; IRQ 0 -> IDT vector 32 +; IRQ 1 -> IDT vector 33 +; ... +; IRQ 15 -> IDT vector 47 +; +; The IDT uses vectors 32-47, but we pass IRQ numbers +; 0-15 to the C IRQ handler. +; ============================================================ + +[BITS 32] + + +; ============================================================ +; Export IRQ handlers and IRQ table +; ============================================================ + +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 + +global irq_table + + +extern irq_handler + + +; ============================================================ +; IRQ macro +; +; IRQs do NOT automatically push an error code. +; +; We push: +; +; 1. dummy error code +; 2. IRQ number +; +; This gives us a predictable stack layout. +; ============================================================ + +%macro IRQ 1 + +irq%1: + + cli + + push dword 0 ; dummy error code + push dword %1 ; IRQ number: 0-15 + + jmp irq_common_stub + +%endmacro + + +; ============================================================ +; IRQ entry points +; ============================================================ + +IRQ 0 +IRQ 1 +IRQ 2 +IRQ 3 +IRQ 4 +IRQ 5 +IRQ 6 +IRQ 7 +IRQ 8 +IRQ 9 +IRQ 10 +IRQ 11 +IRQ 12 +IRQ 13 +IRQ 14 +IRQ 15 + + +; ============================================================ +; IRQ table +; +; irq_table[0] -> irq0 +; irq_table[1] -> irq1 +; ... +; irq_table[15] -> irq15 +; ============================================================ + +section .data + +irq_table: + dd irq0 + dd irq1 + dd irq2 + dd irq3 + dd irq4 + dd irq5 + dd irq6 + dd irq7 + dd irq8 + dd irq9 + dd irq10 + dd irq11 + dd irq12 + dd irq13 + dd irq14 + dd irq15 + + +; ============================================================ +; Common IRQ stub +; ============================================================ + +section .text + +irq_common_stub: + + ; Save general-purpose registers + pusha + + ; Pass pointer to interrupt frame + push esp + call irq_handler + add esp, 4 + + ; Restore general-purpose registers + popa + + ; Remove: + ; + ; 4 bytes = IRQ number + ; 4 bytes = dummy error code + ; + add esp, 8 + + iretd \ No newline at end of file diff --git a/src/interrupt/irq.c b/src/interrupt/irq.c new file mode 100644 index 0000000..84a97a4 --- /dev/null +++ b/src/interrupt/irq.c @@ -0,0 +1,9 @@ +#include "irq.h" +#include "vga.h" +#include "vga_lib.h" + +void irq_handler(uint32_t vector) { + vga_print("IRQ: "); + vga_print_hex(vector); + vga_print("\n"); +} \ No newline at end of file diff --git a/src/interrupt/isr.asm b/src/interrupt/isr.asm index 60ff058..458775d 100644 --- a/src/interrupt/isr.asm +++ b/src/interrupt/isr.asm @@ -14,7 +14,7 @@ global isr%1 isr%1: pusha - push %1 + push dword %1 call isr_handler add esp, 4 @@ -35,20 +35,22 @@ global isr%1 isr%1: pusha - push %1 + push dword %1 call isr_handler add esp, 4 popa + ; Remove CPU-pushed error code add esp, 4 + iret %endmacro ; ============================================================ -; EXCEPTIONS 0-31 +; CPU EXCEPTIONS 0-31 ; ============================================================ ISR_NOERRCODE 0 @@ -118,17 +120,13 @@ section .rodata global isr_table -%macro ISR_TABLE_ENTRY 1 - dd isr%1 -%endmacro - isr_table: %assign i 0 %rep 256 - ISR_TABLE_ENTRY i + dd isr%+i %assign i i + 1 diff --git a/src/interrupt/isr.c b/src/interrupt/isr.c index ce59cb1..40184fa 100644 --- a/src/interrupt/isr.c +++ b/src/interrupt/isr.c @@ -1,3 +1,12 @@ #include "isr.h" +#include "panic.h" +#include "vga.h" -void isr_handler() {} \ No newline at end of file +void isr_handler(uint32_t vector) { + if (vector <= 31) { + vga_print("\n"); + exception(EXCEPTIONS[vector]); + } else { + panic("Unknown Exception"); + } +} diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index 3c41309..446dac9 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -1,15 +1,32 @@ #include "kernel.h" +#include "idt.h" +#include "panic.h" #include "pic.h" +#include "utils.h" #include "vga.h" +#include "vga_lib.h" void kernel_main() { vga_init(); - vga_print("[init] vga driver ... Ok\n"); + vga_log_info("[", "init", "] vga"); + vga_print_at_end_c("... Ok\n", GREEN); - vga_print("[init] pic"); + vga_log_info("[", "init", "] pic"); pic_init(); - vga_print("... Ok"); + vga_print_at_end_c("... Ok\n", GREEN); + + vga_log_info("[", "init", "] idt"); + idt_init(); + vga_print_at_end_c("... Ok\n", GREEN); + + asm volatile("int $0x22"); + + panic("testing panics"); + + for (;;) { + asm volatile("cli"); + } return; } diff --git a/src/kernel/panic.c b/src/kernel/panic.c new file mode 100644 index 0000000..a9e5818 --- /dev/null +++ b/src/kernel/panic.c @@ -0,0 +1,24 @@ +#include "panic.h" +#include "utils.h" +#include "vga.h" +#include "vga_lib.h" + +void panic(char *err) { + vga_print("\n"); + vga_log_error("", "\nKERNEL PANIC: ", err); + + cli(); + + for (;;) + hlt(); +} + +void exception(char *err) { + vga_print("\n"); + vga_log_error("", "\nKERNEL EXCEPTION: ", err); + + cli(); + + for (;;) + hlt(); +} \ No newline at end of file diff --git a/src/libs/vga_lib.c b/src/libs/vga_lib.c new file mode 100644 index 0000000..c4fafc6 --- /dev/null +++ b/src/libs/vga_lib.c @@ -0,0 +1,139 @@ +#include "vga_lib.h" +#include "vga.h" + +/* + * Logging + */ + +void vga_log_info(const char *str0, const char *str, const char *str2) { + vga_print(str0); + + vga_set_color(BLUE, DEFUALT_B); + + vga_print(str); + + vga_set_color(DEFUALT_F, DEFUALT_B); + + vga_print(str2); +} + +void vga_log_success(const char *str0, const char *str, const char *str2) { + vga_print(str0); + + vga_set_color(GREEN, DEFUALT_B); + + vga_print(str); + + vga_set_color(DEFUALT_F, DEFUALT_B); + + vga_print(str2); +} + +void vga_log_warning(const char *str0, const char *str, const char *str2) { + vga_print(str0); + + vga_set_color(BROWN, DEFUALT_B); + + vga_print(str); + + vga_set_color(DEFUALT_F, DEFUALT_B); + + vga_print(str2); +} + +void vga_log_error(const char *str0, const char *str, const char *str2) { + vga_print(str0); + + vga_set_color(RED, DEFUALT_B); + + vga_print(str); + + vga_set_color(DEFUALT_F, DEFUALT_B); + + vga_print(str2); +} + +/* + * Text utilities + */ + +void vga_println(const char *str) { + vga_print(str); + vga_newline(); +} + +void vga_print_at_end(const char *str) { + uint8_t length = 0; + + while (str[length] != '\0') + length++; + + if (length >= VGA_WIDTH) + length = VGA_WIDTH; + + vga_set_cursor(VGA_WIDTH - length, vga_get_cursor_y()); + vga_print(str); +} + +void vga_print_at_end_c(const char *str, uint8_t color) { + + vga_set_color(color, DEFUALT_B); + vga_print_at_end(str); + vga_set_color(DEFUALT_F, DEFUALT_B); +} + +void vga_print_line_at(uint8_t row, const char *str) { + if (row >= VGA_HEIGHT) + return; + + vga_set_cursor(0, row); + vga_print(str); +} + +void vga_print_centered(const char *str, uint8_t row) { + uint8_t length = 0; + + if (row >= VGA_HEIGHT) + return; + + while (str[length] != '\0') + length++; + + if (length >= VGA_WIDTH) + length = VGA_WIDTH; + + uint8_t x = (VGA_WIDTH - length) / 2; + + vga_set_cursor(x, row); + vga_print(str); +} + +/* + * Screen utilities + */ + +void vga_clear_line(uint8_t row) { + if (row >= VGA_HEIGHT) + return; + + vga_set_cursor(0, row); + + for (uint8_t x = 0; x < VGA_WIDTH; x++) + vga_print_char(' '); +} + +void vga_clear_screen(void) { vga_clear(); } + +/* + * Cursor utilities + */ + +void vga_newline(void) { + uint8_t y = vga_get_cursor_y(); + + if (y < VGA_HEIGHT - 1) { + vga_set_cursor(0, y + 1); + } +} + +void vga_home(void) { vga_set_cursor(0, 0); } \ No newline at end of file diff --git a/src/utils/utils.c b/src/utils/utils.c index 81e83f2..ee949a3 100644 --- a/src/utils/utils.c +++ b/src/utils/utils.c @@ -13,20 +13,21 @@ uint8_t inb(uint16_t port) { return value; } +void cli(void) { __asm__ volatile("cli"); } -void cli(void){ - __asm__ volatile("cli"); -} +void sti(void) { __asm__ volatile("sti"); } -void sti(void) { - __asm__ volatile ("sti"); -} - -void hlt(void) { - __asm__ volatile ("hlt"); -} +void hlt(void) { __asm__ volatile("hlt"); } void io_wait(void) { // delay for I/O operation __asm__ volatile("outb %%al, $0x80" : : "a"(0)); } + +uint32_t read_eflags(void) { + uint32_t flags; + asm volatile("pushf\n" + "pop %0" + : "=r"(flags)); + return flags; +} \ No newline at end of file