From 0fa2351940d3e17045cd402eb73d2f0c994c9a6b Mon Sep 17 00:00:00 2001 From: TriMill Date: Tue, 18 Apr 2023 23:44:10 -0400 Subject: [PATCH] initial commit --- .gitignore | 2 + Makefile | 49 ++++++++++++++++++++++ grub.cfg | 7 ++++ kernel/interrupt/idt.asm | 87 ++++++++++++++++++++++++++++++++++++++++ kernel/interrupt/idt.c | 81 +++++++++++++++++++++++++++++++++++++ kernel/interrupt/idt.h | 36 +++++++++++++++++ kernel/interrupt/pic.c | 82 +++++++++++++++++++++++++++++++++++++ kernel/interrupt/pic.h | 10 +++++ kernel/main.c | 22 ++++++++++ kernel/start.asm | 66 ++++++++++++++++++++++++++++++ kernel/term.c | 63 +++++++++++++++++++++++++++++ kernel/term.h | 15 +++++++ libk/include/stdlib.h | 4 ++ libk/include/string.h | 8 ++++ libk/include/sys.h | 12 ++++++ libk/stdlib.c | 46 +++++++++++++++++++++ libk/string.c | 47 ++++++++++++++++++++++ libk/sys.c | 31 ++++++++++++++ linker.ld | 25 ++++++++++++ 19 files changed, 693 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 grub.cfg create mode 100644 kernel/interrupt/idt.asm create mode 100644 kernel/interrupt/idt.c create mode 100644 kernel/interrupt/idt.h create mode 100644 kernel/interrupt/pic.c create mode 100644 kernel/interrupt/pic.h create mode 100644 kernel/main.c create mode 100644 kernel/start.asm create mode 100644 kernel/term.c create mode 100644 kernel/term.h create mode 100644 libk/include/stdlib.h create mode 100644 libk/include/string.h create mode 100644 libk/include/sys.h create mode 100644 libk/stdlib.c create mode 100644 libk/string.c create mode 100644 libk/sys.c create mode 100644 linker.ld diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b1b15b8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +compile_flags.txt +bin diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1393f90 --- /dev/null +++ b/Makefile @@ -0,0 +1,49 @@ +CC=i386-elf-gcc +LD=i386-elf-ld +CFLAGS=-ffreestanding -g -m32 -std=c2x -O2 -Wall -Wextra -pedantic -lgcc -isystem ./libk/include + +C_SOURCE=$(shell find kernel -type f -name "*.c") +C_OBJ=$(patsubst %.c,bin/%.o,$(C_SOURCE)) + +A_SOURCE=$(shell find kernel -type f -name "*.asm") +A_OBJ=$(patsubst %.asm,bin/%_asm.o,$(A_SOURCE)) + +LIBK_SOURCE=$(shell find libk -type f -name "*.c") +LIBK_OBJ=$(patsubst %.c,bin/%.o,$(LIBK_SOURCE)) + +.PHONY: test all run clean + +all: bin/os.iso + +$(A_OBJ): bin/%_asm.o : %.asm + @mkdir -p $(@D) + nasm $< -f elf -o $@ + +$(C_OBJ): bin/%.o : %.c + @mkdir -p $(@D) + $(CC) -c $(CFLAGS) -o $@ $< + +$(LIBK_OBJ): bin/%.o : %.c + @mkdir -p $(@D) + $(CC) -c $(CFLAGS) -o $@ $< + +bin/libk.a: $(LIBK_OBJ) + @mkdir -p $(@D) + $(AR) rcs bin/libk.a $(LIBK_OBJ) + +bin/kernel.bin: bin/libk.a $(A_OBJ) $(C_OBJ) + @mkdir -p $(@D) + $(LD) -nmagic -o bin/kernel.bin -T linker.ld $(A_OBJ) $(C_OBJ) bin/libk.a + +bin/os.iso: bin/kernel.bin + @mkdir -p $(@D) + @mkdir -p bin/iso/boot/grub + cp grub.cfg bin/iso/boot/grub + cp bin/kernel.bin bin/iso/boot + grub-mkrescue -o bin/os.iso bin/iso + +run: all + qemu-system-i386 -cdrom bin/os.iso + +clean: + rm -rf bin diff --git a/grub.cfg b/grub.cfg new file mode 100644 index 0000000..6c9df17 --- /dev/null +++ b/grub.cfg @@ -0,0 +1,7 @@ +set timeout=0 +set default=0 + +menuentry "trios" { + multiboot2 /boot/kernel.bin + boot +} diff --git a/kernel/interrupt/idt.asm b/kernel/interrupt/idt.asm new file mode 100644 index 0000000..40d4521 --- /dev/null +++ b/kernel/interrupt/idt.asm @@ -0,0 +1,87 @@ +extern idt_exception_handler +extern idt_pic_timer +extern idt_pic_keyboard +extern idt_pic_handler +global isr_stub_table + +%macro ISRErrorStub 1 +isr_stub_%+%1: + push dword %1 + call idt_exception_handler + pop eax + iret +%endmacro + +%macro PICGeneric 1 +isr_stub_%+%1: + push dword %1 + call idt_pic_handler + pop eax + iret +%endmacro + +%macro PICTimer 1 +isr_stub_%+%1: + push dword %1 + call idt_pic_handler + call idt_pic_timer + pop eax + iret +%endmacro + +%macro PICKeyboard 1 +isr_stub_%+%1: + push dword %1 + call idt_pic_handler + call idt_pic_keyboard + pop eax + iret +%endmacro + +%macro ISRSyscall 1 +isr_stub_%+%1: + push eax + push ebx + push ecx + push edx + call idt_syscall + add esp, 16 + pop eax + iret +%endmacro + +section .text +%assign i 0 +%rep 32 + ISRErrorStub i +%assign i i+1 +%endrep +PICTimer 32 +PICKeyboard 33 +PICGeneric 34 +PICGeneric 35 +PICGeneric 36 +PICGeneric 37 +PICGeneric 38 +PICGeneric 39 +PICGeneric 40 +PICGeneric 41 +PICGeneric 42 +PICGeneric 43 +PICGeneric 44 +PICGeneric 45 +PICGeneric 46 +PICGeneric 47 +%assign i 48 +%rep 256 - 48 + ISRErrorStub i +%assign i i+1 +%endrep + +section .rodata +isr_stub_table: +%assign i 0x00 +%rep 256 + dd isr_stub_%+i +%assign i i+0x01 +%endrep diff --git a/kernel/interrupt/idt.c b/kernel/interrupt/idt.c new file mode 100644 index 0000000..92b9cae --- /dev/null +++ b/kernel/interrupt/idt.c @@ -0,0 +1,81 @@ +#include +#include +#include +#include +#include +#include "idt.h" +#include "../term.h" +#include "pic.h" + +static int timer = 0; + +void idt_pic_handler(uint8_t exception) { + pic_eoi(exception - PIC_REMAP_OFFSET); +} + +void idt_pic_timer(void) { + uint32_t state = term_save(); + term_setcol(0x0a); + char buf[20]; + itoa(timer, buf); + term_setpos(0, 20); + puts(" "); + term_setpos(0, 20); + puts(buf); + timer += 1; + term_load(state); +} + +void idt_pic_keyboard(void) { + uint8_t c = inb(0x60); + uint32_t state = term_save(); + term_setcol(0x0c); + term_setpos(0, 21); + puts(" "); + term_setpos(0, 21); + char buf[20]; + itoa(c, buf); + puts(buf); + term_load(state); +} + +void idt_exception_handler(uint8_t exception) { + switch(exception) { + case 0x00: + puts("Div by zero"); + break; + case 0x08: + puts("Double fault"); + break; + default: + puts("Error"); + break; + } + halt(); +} + +__attribute__((aligned(0x10))) +static struct IdtEntry idt[256]; +static struct Idtr idtr; +extern void* isr_stub_table[]; + +static void set_descriptor(uint8_t vector, void* isr, uint8_t flags) { + struct IdtEntry* entry = &idt[vector]; + entry->isr_low = (size_t)isr & 0xffff; + entry->kernel_cs = 0x08; + entry->attributes = flags; + entry->isr_high = (size_t)isr >> 16; + entry->_reserved = 0; +} + +void idt_init(void) { + idtr.base = (uintptr_t)&idt[0]; + idtr.limit = (uint16_t)sizeof(struct IdtEntry) * IDT_SIZE - 1; + + for(uint8_t i = 0; i < IDT_INTERRUPTS; i++) { + set_descriptor(i, isr_stub_table[i], 0x8e); + } + + __asm__ volatile ("lidt %0" : : "m"(idtr)); +} + diff --git a/kernel/interrupt/idt.h b/kernel/interrupt/idt.h new file mode 100644 index 0000000..f0509a7 --- /dev/null +++ b/kernel/interrupt/idt.h @@ -0,0 +1,36 @@ +#pragma once +#include + +#define IDT_SIZE 256 +#define IDT_INTERRUPTS 48 + +struct IdtEntry { + uint16_t isr_low; + uint16_t kernel_cs; + uint8_t _reserved; + uint8_t attributes; + uint16_t isr_high; +} __attribute__((packed)); + +struct Idtr { + uint16_t limit; + uint32_t base; +} __attribute__((packed)); + +typedef enum { + IDT_FLAG_GATE_TASK = 0x5, + IDT_FLAG_GATE_16BIT_INT = 0x6, + IDT_FLAG_GATE_16BIT_TRAP = 0x7, + IDT_FLAG_GATE_32BIT_INT = 0xE, + IDT_FLAG_GATE_32BIT_TRAP = 0xF, + + IDT_FLAG_RING0 = (0 << 5), + IDT_FLAG_RING1 = (1 << 5), + IDT_FLAG_RING2 = (2 << 5), + IDT_FLAG_RING3 = (3 << 5), + + IDT_FLAG_PRESENT = 0x80, + +} IDTFlags; + +void idt_init(void); diff --git a/kernel/interrupt/pic.c b/kernel/interrupt/pic.c new file mode 100644 index 0000000..093b26b --- /dev/null +++ b/kernel/interrupt/pic.c @@ -0,0 +1,82 @@ +#include "pic.h" +#include "../term.h" +#include + +#define PIC1_COMMAND_PORT 0x20 +#define PIC1_DATA_PORT 0x21 +#define PIC2_COMMAND_PORT 0xA0 +#define PIC2_DATA_PORT 0xA1 + + +void pic_remap(uint8_t offset) { + char a1 = inb(PIC1_DATA_PORT); + char a2 = inb(PIC2_DATA_PORT); + // control word 1 + // 0x11: initialize, enable ICW4 + outb(PIC1_COMMAND_PORT, 0x11); + io_wait(); + outb(PIC2_COMMAND_PORT, 0x11); + io_wait(); + // control word 2 + // interrupt offset + outb(PIC1_DATA_PORT, offset); + io_wait(); + outb(PIC2_DATA_PORT, offset + 8); + io_wait(); + // control word 3 + // primary pic: set which pin secondary is connected to + // (pin 2) + outb(PIC1_DATA_PORT, 0b00000100); + io_wait(); + outb(PIC2_DATA_PORT, 2); + io_wait(); + // control word 3 + // 0x01: enable 8086 mode + outb(PIC1_DATA_PORT, 0x01); + io_wait(); + outb(PIC2_DATA_PORT, 0x01); + io_wait(); + // clear data registers + outb(PIC1_DATA_PORT, a1); + outb(PIC2_DATA_PORT, a2); +} + +void pic_mask(int irq) { + uint8_t port; + if(irq < 8) { + port = PIC1_DATA_PORT; + } else { + irq -= 8; + port = PIC2_DATA_PORT; + } + uint8_t mask = inb(port); + outb(port, mask | (1 << irq)); +} + +void pic_unmask(int irq) { + uint8_t port; + if(irq < 8) { + port = PIC1_DATA_PORT; + } else { + irq -= 8; + port = PIC2_DATA_PORT; + } + uint8_t mask = inb(port); + outb(port, mask & ~(1 << irq)); +} + +// completely disable the PIC +void pic_disable() { + outb(PIC1_DATA_PORT, 0xff); + io_wait(); + outb(PIC2_DATA_PORT, 0xff); + io_wait(); +} + +// end of interrupt +void pic_eoi(int irq) { + if(irq >= 8) { + outb(PIC2_COMMAND_PORT, 0x20); + } + outb(PIC1_COMMAND_PORT, 0x20); +} diff --git a/kernel/interrupt/pic.h b/kernel/interrupt/pic.h new file mode 100644 index 0000000..cd2fae2 --- /dev/null +++ b/kernel/interrupt/pic.h @@ -0,0 +1,10 @@ +#pragma once +#include + +#define PIC_REMAP_OFFSET 0x20 + +void pic_remap(uint8_t offset); +void pic_mask(int irq); +void pic_unmask(int irq); +void pic_disable(); +void pic_eoi(int irq); diff --git a/kernel/main.c b/kernel/main.c new file mode 100644 index 0000000..08638d3 --- /dev/null +++ b/kernel/main.c @@ -0,0 +1,22 @@ +#include +#include "term.h" +#include "interrupt/pic.h" +#include "interrupt/idt.h" + +extern void kmain(void) { + term_clear(); + term_setcol(0x0f); + puts("loading kernel\n"); + idt_init(); + puts("initialized idt\n"); + pic_remap(PIC_REMAP_OFFSET); + puts("remapped pic\n"); + int_enable(); + puts("enabled interrupts\n"); + outb(0x60, 0xF4); + puts("enabled keyboard\n"); + + while(1) { + int_wait(); + } +} diff --git a/kernel/start.asm b/kernel/start.asm new file mode 100644 index 0000000..f37c503 --- /dev/null +++ b/kernel/start.asm @@ -0,0 +1,66 @@ +global start +extern kmain +bits 32 + +; base, limit, access, flags +%macro gdt_entry 4 + db %2 & 0xff + db (%2 >> 8) & 0xff + db %1 & 0xff + db (%1 >> 8) & 0xff + db (%1 >> 16) & 0xff + db %3 + db ((%2 >> 16) & 0x0f) | (%4 << 4) + db (%1 >> 24) & 0xff +%endmacro + +section .multiboot_header +align 8 +mb_start: +dd 0xe85250d6 +dd 0 +dd mb_end - mb_start +dd 0x100000000 - (0xe85250d6 + (mb_end - mb_start)) +dw 0 +dw 0 +dd 8 +mb_end: + +section .bss +align 16 +stack_start: + resb 16384 +stack_end: + +section .rodata +align 16 +gdt_start: +gdt_entry 0, 0, 0, 0 +gdt_entry 0, 0xFFFFF, 0x9A, 0xC +gdt_entry 0, 0xFFFFF, 0x92, 0xC +gdt_end: +gdt_descriptor: + dw gdt_end - gdt_start - 1 + dd gdt_start + + +section .text +align 8 +start: + cli + lgdt [gdt_descriptor] + jmp 0x08:after_lgdt +after_lgdt: + mov ax, 0x10 + mov ds, ax + mov ss, ax + mov es, ax + mov fs, ax + mov gs, ax + mov esp, stack_end + mov ebp, stack_end + call kmain + cli +halt: + hlt + jmp halt diff --git a/kernel/term.c b/kernel/term.c new file mode 100644 index 0000000..324565b --- /dev/null +++ b/kernel/term.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include "term.h" + +static uint16_t* screen = (uint16_t*)0xb8000; +static size_t x = 0; +static size_t y = 0; +static uint8_t color = 0x0f; + +static inline uint16_t screen_entry(char c, uint8_t col) { + return (uint16_t)c | ((uint16_t)col << 8); +} + +void putc(char c) { + switch(c) { + case '\n': + x = TERM_W; + break; + case '\t': + if(x % 8 == 0) x += 8; + x += 7 - (x % 8); + break; + default: + screen[x + y * TERM_W] = screen_entry(c, color); + x += 1; + break; + } + if(x >= TERM_W) { + x = 0; + y += 1; + } +} + +void puts(char* s) { + for(char* c = s; *c != '\0'; c++) { + putc(*c); + } +} + +void term_clear(void) { + memset(screen, 0, TERM_W * TERM_H * 2); +} + +void term_setpos(size_t new_x, size_t new_y) { + x = new_x; + y = new_y; +} + +void term_setcol(uint8_t new_col) { + color = new_col; +} + +uint32_t term_save(void) { + return color | ((x + y * TERM_W) << 8); +} + +void term_load(uint32_t state) { + color = state & 0xff; + state >>= 8; + x = state % TERM_W; + y = state / TERM_W; +} diff --git a/kernel/term.h b/kernel/term.h new file mode 100644 index 0000000..978051e --- /dev/null +++ b/kernel/term.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +#define TERM_W 80 +#define TERM_H 25 + +void putc(char c); +void puts(char* s); +void term_clear(void); +void term_setcol(uint8_t new_col); +void term_setpos(size_t new_x, size_t new_y); +uint32_t term_save(void); +void term_load(uint32_t pos); diff --git a/libk/include/stdlib.h b/libk/include/stdlib.h new file mode 100644 index 0000000..ec130e1 --- /dev/null +++ b/libk/include/stdlib.h @@ -0,0 +1,4 @@ +#pragma once + +int atoi(const char* s); +void itoa(int n, char* buf); diff --git a/libk/include/string.h b/libk/include/string.h new file mode 100644 index 0000000..cc3f1d5 --- /dev/null +++ b/libk/include/string.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +int memcmp(const void *lhs, const void *rhs, size_t n); +void *memcpy(void *restrict dst, const void *restrict src, size_t n); +void *memmove(void *dst, const void *src, size_t n); +void *memset(void *dest, int c, size_t n); diff --git a/libk/include/sys.h b/libk/include/sys.h new file mode 100644 index 0000000..28eee70 --- /dev/null +++ b/libk/include/sys.h @@ -0,0 +1,12 @@ +#include +#include + +uint8_t inb(uint16_t port); +void outb(uint16_t port, uint8_t val); +void io_wait(void); + +void int_enable(void); +void int_disable(void); +void int_wait(void); + +void halt(void); diff --git a/libk/stdlib.c b/libk/stdlib.c new file mode 100644 index 0000000..d0a9bbb --- /dev/null +++ b/libk/stdlib.c @@ -0,0 +1,46 @@ +#include +#include "include/stdlib.h" + +int atoi(const char* s) { + bool neg = false; + if(*s == '+') { + s++; + } else if(*s == '-') { + neg = true; + s++; + } + int n = 0; + while(*s >= '0' && *s <= '9') { + n *= 10; + n += (*s - '0'); + s++; + } + return neg ? -n : n; +} + +void itoa(int n, char* buf) { + if(n < 0) { + n = -n; + *buf = '-'; + buf++; + } else if(n == 0) { + buf[0] = '0'; + buf[1] = '\0'; + return; + } + char* start = buf; + while(n > 0) { + *buf= n % 10 + '0'; + n /= 10; + buf++; + } + *buf = '\0'; + buf--; + while(start < buf) { + char tmp = *start; + *start = *buf; + *buf = tmp; + start++; + buf--; + } +} diff --git a/libk/string.c b/libk/string.c new file mode 100644 index 0000000..85dcdb2 --- /dev/null +++ b/libk/string.c @@ -0,0 +1,47 @@ +#include +#include "include/string.h" + +int memcmp(const void *lhs, const void *rhs, size_t n) { + const unsigned char *l = lhs; + const unsigned char *r = rhs; + while(n != 0 && *l == *r) { + n--; + l++; + r++; + } + return (n == 0) ? 0 : *l - *r; +} + +void *memcpy(void *restrict dst, const void *restrict src, size_t n) { + char *d = dst; + const char *s = src; + while(n--) { + *d++ = *s++; + } + return dst; +} + +void *memmove(void *dst, const void *src, size_t n) { + char *d = dst; + const char *s = src; + if(s < d) { + d += n; + s += n; + while(n--) { + *--d = *--s; + } + } else { + while(n--) { + *d++ = *s++; + } + } + return dst; +} + +void *memset(void *dest, int c, size_t n) { + unsigned char *d = dest; + while(n--) { + *d = c; + } + return dest; +} diff --git a/libk/sys.c b/libk/sys.c new file mode 100644 index 0000000..8c3f7ba --- /dev/null +++ b/libk/sys.c @@ -0,0 +1,31 @@ +#include + +uint8_t inb(uint16_t port) { + uint8_t ret; + __asm__ volatile ("inb %1, %0" : "=a"(ret) : "Nd"(port)); + return ret; +} + +void outb(uint16_t port, uint8_t val) { + __asm__ volatile ("outb %0, %1" : : "a"(val), "Nd"(port)); +} + +void io_wait(void) { + outb(0x80, 0); +} + +void int_enable(void) { + __asm__ volatile ("sti"); +} + +void int_disable(void) { + __asm__ volatile ("cli"); +} + +void int_wait(void) { + __asm__ volatile ("hlt"); +} + +void halt(void) { + __asm__ volatile ("cli; hlt"); +} diff --git a/linker.ld b/linker.ld new file mode 100644 index 0000000..f59b18a --- /dev/null +++ b/linker.ld @@ -0,0 +1,25 @@ +ENTRY(start) + +SECTIONS { + . = 1M; + + .boot : + { + *(.multiboot_header) + } + + .rodata : + { + *(.rodata) + } + + .bss : + { + *(.bss) + } + + .text : + { + *(.text) + } +}