From fbf131b5c043b27e0b1543374bb144e3e426f723 Mon Sep 17 00:00:00 2001 From: Tyler Murphy <=> Date: Sun, 16 Jul 2023 02:54:32 -0400 Subject: initial --- kernel/Makefile | 28 ++++ kernel/compile_flags.txt | 12 ++ kernel/include/keycodes.h | 127 +++++++++++++++ kernel/include/panic.h | 6 + kernel/include/print.h | 23 +++ kernel/linker.ld | 31 ++++ kernel/src/acpi/acpi.c | 141 +++++++++++++++++ kernel/src/acpi/acpi.h | 106 +++++++++++++ kernel/src/boot/tag.c | 91 +++++++++++ kernel/src/boot/tag.h | 38 +++++ kernel/src/cpu/cpu.c | 20 +++ kernel/src/cpu/cpu.h | 3 + kernel/src/cpu/fpu.asm | 19 +++ kernel/src/cpu/sse.asm | 22 +++ kernel/src/drivers/ps2ctrl.c | 99 ++++++++++++ kernel/src/drivers/ps2ctrl.h | 14 ++ kernel/src/drivers/ps2kb.c | 130 ++++++++++++++++ kernel/src/drivers/ps2kb.h | 15 ++ kernel/src/drivers/ps2mouse.c | 89 +++++++++++ kernel/src/drivers/ps2mouse.h | 18 +++ kernel/src/graphics/framebuffer.h | 15 ++ kernel/src/interrupt/idt.c | 108 +++++++++++++ kernel/src/interrupt/idt.h | 36 +++++ kernel/src/interrupt/isr.asm | 99 ++++++++++++ kernel/src/interrupt/pic.c | 85 +++++++++++ kernel/src/interrupt/pic.h | 11 ++ kernel/src/main.c | 68 +++++++++ kernel/src/memory/allocator.c | 314 ++++++++++++++++++++++++++++++++++++++ kernel/src/memory/memory.c | 202 ++++++++++++++++++++++++ kernel/src/memory/memory.h | 22 +++ kernel/src/print/panic.c | 27 ++++ kernel/src/print/print.c | 128 ++++++++++++++++ kernel/src/start.asm | 75 +++++++++ kernel/src/tty/color.c | 67 ++++++++ kernel/src/tty/color.h | 62 ++++++++ kernel/src/tty/cursor.c | 30 ++++ kernel/src/tty/cursor.h | 9 ++ kernel/src/tty/term.c | 131 ++++++++++++++++ kernel/src/tty/term.h | 27 ++++ 39 files changed, 2548 insertions(+) create mode 100644 kernel/Makefile create mode 100644 kernel/compile_flags.txt create mode 100644 kernel/include/keycodes.h create mode 100644 kernel/include/panic.h create mode 100644 kernel/include/print.h create mode 100644 kernel/linker.ld create mode 100644 kernel/src/acpi/acpi.c create mode 100644 kernel/src/acpi/acpi.h create mode 100644 kernel/src/boot/tag.c create mode 100644 kernel/src/boot/tag.h create mode 100644 kernel/src/cpu/cpu.c create mode 100644 kernel/src/cpu/cpu.h create mode 100644 kernel/src/cpu/fpu.asm create mode 100644 kernel/src/cpu/sse.asm create mode 100644 kernel/src/drivers/ps2ctrl.c create mode 100644 kernel/src/drivers/ps2ctrl.h create mode 100644 kernel/src/drivers/ps2kb.c create mode 100644 kernel/src/drivers/ps2kb.h create mode 100644 kernel/src/drivers/ps2mouse.c create mode 100644 kernel/src/drivers/ps2mouse.h create mode 100644 kernel/src/graphics/framebuffer.h create mode 100644 kernel/src/interrupt/idt.c create mode 100644 kernel/src/interrupt/idt.h create mode 100644 kernel/src/interrupt/isr.asm create mode 100644 kernel/src/interrupt/pic.c create mode 100644 kernel/src/interrupt/pic.h create mode 100644 kernel/src/main.c create mode 100644 kernel/src/memory/allocator.c create mode 100644 kernel/src/memory/memory.c create mode 100644 kernel/src/memory/memory.h create mode 100644 kernel/src/print/panic.c create mode 100644 kernel/src/print/print.c create mode 100644 kernel/src/start.asm create mode 100644 kernel/src/tty/color.c create mode 100644 kernel/src/tty/color.h create mode 100644 kernel/src/tty/cursor.c create mode 100644 kernel/src/tty/cursor.h create mode 100644 kernel/src/tty/term.c create mode 100644 kernel/src/tty/term.h (limited to 'kernel') diff --git a/kernel/Makefile b/kernel/Makefile new file mode 100644 index 0000000..999498b --- /dev/null +++ b/kernel/Makefile @@ -0,0 +1,28 @@ +include ../.env + +C_SRC = $(shell find src -type f -name "*.c") +C_OBJ = $(patsubst %.c,bin/%.o, $(C_SRC)) + +ASM_SRC = $(shell find src -type f -name "*.asm") +ASM_OBJ = $(patsubst %.asm,bin/%.o, $(ASM_SRC)) + +CFLAGS += -Iinclude -Isrc -I../libk/include -std=gnu17 + +.PHONY: all + +all: bin/kernel.bin + +$(C_OBJ): bin/%.o : %.c + @mkdir -p $(@D) + $(CC) -c $(CFLAGS) -o $@ $< + +$(ASM_OBJ): bin/%.o : %.asm + @mkdir -p $(@D) + $(AS) $< -f elf -o $@ + +bin/kernel.bin: $(C_OBJ) $(ASM_OBJ) + @mkdir -p $(@D) + $(LD) -o bin/kernel.bin -T linker.ld $(C_OBJ) $(ASM_OBJ) ../libk/bin/libk.a $(LDFLAGS) + +clean: + rm -fr bin diff --git a/kernel/compile_flags.txt b/kernel/compile_flags.txt new file mode 100644 index 0000000..0380b3e --- /dev/null +++ b/kernel/compile_flags.txt @@ -0,0 +1,12 @@ +-c +-std=gnu17 +-Wall +-Wextra +-pedantic +-O2 +-ffreestanding +-I../libk/include +-Iinclude +-Isrc +-nostdlib +-DKERNEL_LOG diff --git a/kernel/include/keycodes.h b/kernel/include/keycodes.h new file mode 100644 index 0000000..b0a2568 --- /dev/null +++ b/kernel/include/keycodes.h @@ -0,0 +1,127 @@ +#pragma once + +#define KC_FLAG_KEY_DOWN 0x01 +#define KC_FLAG_KEY_UP 0x02 +#define KC_FLAG_ERROR 0x04 + +#define KEY_NONE 0x00 +#define KEY_UNKNOWN 0x01 + +#define KEY_ESCAPE 0x10 +#define KEY_1 0x11 +#define KEY_2 0x12 +#define KEY_3 0x13 +#define KEY_4 0x14 +#define KEY_5 0x15 +#define KEY_6 0x16 +#define KEY_7 0x17 +#define KEY_8 0x18 +#define KEY_9 0x19 +#define KEY_0 0x1A +#define KEY_MINUS 0x1B +#define KEY_EQUAL 0x1C +#define KEY_BACKSPACE 0x1D +#define KEY_L_SHIFT 0x1E +#define KEY_R_SHIFT 0x1F + +#define KEY_TAB 0x20 +#define KEY_Q 0x21 +#define KEY_W 0x22 +#define KEY_E 0x23 +#define KEY_R 0x24 +#define KEY_T 0x25 +#define KEY_Y 0x26 +#define KEY_U 0x27 +#define KEY_I 0x28 +#define KEY_O 0x29 +#define KEY_P 0x2A +#define KEY_L_BRACE 0x2B +#define KEY_R_BRACE 0x2C +#define KEY_BACKSLASH 0x2D +#define KEY_L_CTRL 0x2E +#define KEY_R_CTRL 0x2F + +#define KEY_CAPS_LOCK 0x30 +#define KEY_A 0x31 +#define KEY_S 0x32 +#define KEY_D 0x33 +#define KEY_F 0x34 +#define KEY_G 0x35 +#define KEY_H 0x36 +#define KEY_J 0x37 +#define KEY_K 0x38 +#define KEY_L 0x39 +#define KEY_SEMICOLON 0x3A +#define KEY_QUOTE 0x3B +#define KEY_ENTER 0x3C +#define KEY_MENU 0x3D +#define KEY_L_ALT 0x3E +#define KEY_R_ALT 0x3F + +#define KEY_SPACE 0x40 +#define KEY_Z 0x41 +#define KEY_X 0x42 +#define KEY_C 0x43 +#define KEY_V 0x44 +#define KEY_B 0x45 +#define KEY_N 0x46 +#define KEY_M 0x47 +#define KEY_COMMA 0x48 +#define KEY_PERIOD 0x49 +#define KEY_SLASH 0x4A +#define KEY_BACKTICK 0x4B +#define KEY_NUM_LOCK 0x4C +#define KEY_SCROLL_LOCK 0x4D +#define KEY_L_META 0x4E +#define KEY_R_META 0x4F + +#define KEY_NP_SLASH 0x50 +#define KEY_NP_7 0x51 +#define KEY_NP_8 0x52 +#define KEY_NP_9 0x53 +#define KEY_NP_ASTERISK 0x54 +#define KEY_NP_4 0x55 +#define KEY_NP_5 0x56 +#define KEY_NP_6 0x57 +#define KEY_NP_MINUS 0x58 +#define KEY_NP_1 0x59 +#define KEY_NP_2 0x5A +#define KEY_NP_3 0x5B +#define KEY_NP_PLUS 0x5C +#define KEY_NP_0 0x5D +#define KEY_NP_PERIOD 0x5E +#define KEY_NP_ENTER 0x5F + +#define KEY_PRINT_SCREEN 0x60 +#define KEY_PAUSE 0x61 +#define KEY_INSERT 0x62 +#define KEY_HOME 0x63 +#define KEY_PAGE_UP 0x64 +#define KEY_DELETE 0x65 +#define KEY_END 0x66 +#define KEY_PAGE_DOWN 0x67 +#define KEY_UP 0x68 +#define KEY_DOWN 0x69 +#define KEY_LEFT 0x6A +#define KEY_RIGHT 0x6B +// #define _ 0x6C +// #define _ 0x6D +// #define _ 0x6E +// #define _ 0x6F + +#define KEY_F1 0x70 +#define KEY_F2 0x71 +#define KEY_F3 0x72 +#define KEY_F4 0x73 +#define KEY_F5 0x74 +#define KEY_F6 0x75 +#define KEY_F7 0x76 +#define KEY_F8 0x77 +#define KEY_F9 0x78 +#define KEY_F10 0x79 +#define KEY_F11 0x7A +#define KEY_F12 0x7B +// #define _ 0x7C +// #define _ 0x7D +// #define _ 0x7E +// #define _ 0x7F diff --git a/kernel/include/panic.h b/kernel/include/panic.h new file mode 100644 index 0000000..db02a24 --- /dev/null +++ b/kernel/include/panic.h @@ -0,0 +1,6 @@ +#pragma once + +#define panic(msg, ...) _panic_impl(msg, __LINE__, __FILE__, ## __VA_ARGS__) + +__attribute__((noreturn)) +extern void _panic_impl(char* msg, int line, char* file, ...); diff --git a/kernel/include/print.h b/kernel/include/print.h new file mode 100644 index 0000000..dc7f862 --- /dev/null +++ b/kernel/include/print.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +extern void putchar(int c); +extern void puts(const char* s); +extern void printk(const char *restrict format, ...); +extern void vprintk(const char *restrict format, va_list ap); + +#ifdef KERNEL_LOG +#define debugk(msg, ...) _debugk_impl(msg, ## __VA_ARGS__) +#define succek(msg, ...) _succek_impl(msg, ## __VA_ARGS__) +#define errork(msg, ...) _errork_impl(msg, ## __VA_ARGS__) +#else +#define debugk(msg, ...) +#define succek(msg, ...) +#define errork(msg, ...) +#endif + +extern void _debugk_impl(char* msg, ...); +extern void _succek_impl(char* msg, ...); +extern void _errork_impl(char* msg, ...); diff --git a/kernel/linker.ld b/kernel/linker.ld new file mode 100644 index 0000000..beb44df --- /dev/null +++ b/kernel/linker.ld @@ -0,0 +1,31 @@ +ENTRY(start) + +SECTIONS { + . = 1M; + + kernel_start = .; + + .boot BLOCK(4K) : ALIGN(4K) + { + *(.multiboot) + } + + .text BLOCK(4K) : ALIGN(4K) + { + *(.text) + } + + .rodata BLOCK(4K) : ALIGN(4K) + { + *(.rodata) + } + + + .bss BLOCK(4K) : ALIGN(4K) + { + *(.bss) + } + + kernel_end = .; + +} diff --git a/kernel/src/acpi/acpi.c b/kernel/src/acpi/acpi.c new file mode 100644 index 0000000..ef1fb76 --- /dev/null +++ b/kernel/src/acpi/acpi.c @@ -0,0 +1,141 @@ +#include +#include +#include +#include +#include +#include + +#include "acpi.h" +#include "boot/tag.h" +#include "print.h" + +static struct RootSystemDescriptionTable *rsdt; +static struct FixedACPIDescriptionTable *fadt; + +static uint16_t SLP_TYPa; +static uint16_t SLP_TYPb; +static uint16_t SLP_EN; +static uint16_t SCI_EN; + +static bool is_init = false; + +bool checksum(uint8_t *data, size_t len) { + unsigned char sum = 0; + for (size_t i = 0; i < len; i++) + sum += data[i]; + return sum == 0; +} + +static void *find_fadt(void) { + int entries = (rsdt->header.length - sizeof(rsdt->header)) / 4; + + for (int i = 0; i < entries; i++) { + uintptr_t sdt_ptr = rsdt->sdt_table[i]; + struct SystemDescriptionTableHeader *h = (void*) sdt_ptr; + if (!strncmp(h->signature, "FACP", 4)) + return (void *) h; + } + + return NULL; +} + +static int read_s5_addr(void) { + uintptr_t ptr = fadt->dsdt; + char *s5_addr = (void*) (ptr + 36); + + int dsdt_len = *((int*) (ptr+1)) - 36; + while (0 < dsdt_len--) { + if ( memcmp(s5_addr, "_S5_", 4) == 0) + break; + s5_addr++; + } + + if (dsdt_len > 0) { + // check for valid AML structure + if ( ( *(s5_addr-1) == 0x08 || ( *(s5_addr-2) == 0x08 && *(s5_addr-1) == '\\') ) && *(s5_addr+4) == 0x12 ) { + s5_addr += 5; + s5_addr += ((*s5_addr &0xC0)>>6) +2; // calculate PkgLength size + + if (*s5_addr == 0x0A) + s5_addr++; // skip byteprefix + SLP_TYPa = *(s5_addr)<<10; + s5_addr++; + + if (*s5_addr == 0x0A) + s5_addr++; // skip byteprefix + SLP_TYPb = *(s5_addr)<<10; + + SLP_EN = 1<<13; + SCI_EN = 1; + + } else { + errork("\\_S5 parse error."); + return 1; + } + } else { + errork("\\_S5 not present."); + return 1; + } + return 0; +} + +void acpi_init(void) { + + is_init = false; + + debugk("Loading ACPI"); + + struct BootTag *tag; + if(!get_boot_tag(ID_RSDP, &tag)) { + errork("Could not find RSDP"); + return; + } + + debugk("Loading RSDT"); + + struct RootSystemDescriptionPointer *rsdp = tag->data.rsdp; + if (!checksum((uint8_t*) rsdp, sizeof(struct RootSystemDescriptionPointer))) { + errork("RSDP checksum failed to validate"); + return; + } + + uintptr_t rsdt_ptr = rsdp->rsdt_address; + rsdt = (void *) rsdt_ptr; + if (!checksum((uint8_t*) &rsdt->header, rsdt->header.length)) { + errork("RSDT checksum failed to validate"); + return; + } + + debugk("Loading FADT"); + + fadt = find_fadt(); + if (fadt == NULL) { + errork("Could not find FADT"); + return; + } + + if (!checksum((uint8_t*) &fadt->header, fadt->header.length)) { + errork("FADT checksum failed to validate"); + return; + } + + debugk("Reading \\_S5 Addr"); + + if (read_s5_addr()) { + return; + } + + outb(fadt->smi_command_port,fadt->acpi_enable); + + succek("ACPI has been loaded"); + is_init = true; +} + +void acpi_poweroff(void) { + if (is_init) { + outw((unsigned int) fadt->pm1_a_control_block, SLP_TYPb | SLP_EN); + panic("failed to shutdown"); + } else { + errork("Cannot shutdown, ACPI not loaded"); + } +} diff --git a/kernel/src/acpi/acpi.h b/kernel/src/acpi/acpi.h new file mode 100644 index 0000000..889497e --- /dev/null +++ b/kernel/src/acpi/acpi.h @@ -0,0 +1,106 @@ +#pragma once + +#include + +struct RootSystemDescriptionPointer { + char signature[8]; + uint8_t checksum; + char oemid[6]; + uint8_t revision; + uint32_t rsdt_address; +}; + +struct SystemDescriptionTableHeader { + char signature[4]; + uint32_t length; + uint8_t revision; + uint8_t checksum; + char oem_id[6]; + char oem_table_id[8]; + uint32_t oem_revision; + uint32_t creator_id; + uint32_t creator_revision; +}; + +struct RootSystemDescriptionTable { + struct SystemDescriptionTableHeader header; + uint32_t sdt_table[]; +}; + +struct GenericAddressStructure { + uint8_t address_space; + uint8_t bit_width; + uint8_t bit_offset; + uint8_t access_size; + uint64_t address; +}; + +struct FixedACPIDescriptionTable { + struct SystemDescriptionTableHeader header; + uint32_t firmware_ctrl; + uint32_t dsdt; + + // field used in ACPI 1.0; no longer in use, for compatibility only + uint8_t reserved; + + uint8_t preferred_power_management_profile; + uint16_t sci_interrupt; + uint32_t smi_command_port; + uint8_t acpi_enable; + uint8_t acpi_disable; + uint8_t s4bios_req; + uint8_t pstate_control; + uint32_t pm1_a_event_block; + uint32_t pm1_b_event_block; + uint32_t pm1_a_control_block; + uint32_t pm1_b_control_block; + uint32_t pm2_control_block; + uint32_t pm_timer_block; + uint32_t gpe0_block; + uint32_t gpe1_block; + uint8_t pm1_event_length; + uint8_t pm1_control_length; + uint8_t pm2_control_length; + uint8_t pm_timer_length; + uint8_t gpe0_length; + uint8_t gpe1_length; + uint8_t gpe1_base; + uint8_t cstate_control; + uint16_t worst_c2_latency; + uint16_t worst_c3_latency; + uint16_t flush_size; + uint16_t flush_stride; + uint8_t duty_offset; + uint8_t duty_width; + uint8_t day_alarm; + uint8_t month_alarm; + uint8_t century; + + // reserved in ACPI 1.0; used since ACPI 2.0+ + uint16_t boot_architecture_flags; + + uint8_t reserved_2; + uint32_t flags; + + // 12 byte structure; see below for details + struct GenericAddressStructure reset_reg; + + uint8_t reset_value; + uint8_t reserved_3[3]; + + // 64bit pointers - Available on ACPI 2.0+ + uint64_t x_firmware_control; + uint64_t x_dsdt; + + struct GenericAddressStructure x_pm1_a_event_block; + struct GenericAddressStructure x_pm1_b_event_block; + struct GenericAddressStructure x_pm1_a_control_block; + struct GenericAddressStructure x_pm1_b_control_block; + struct GenericAddressStructure x_pm2_control_block; + struct GenericAddressStructure x_pm_timer_block; + struct GenericAddressStructure x_gpe0_block; + struct GenericAddressStructure x_gpe1_block; +}; + +void acpi_init(void); +void acpi_poweroff(void); diff --git a/kernel/src/boot/tag.c b/kernel/src/boot/tag.c new file mode 100644 index 0000000..22ea758 --- /dev/null +++ b/kernel/src/boot/tag.c @@ -0,0 +1,91 @@ +#include +#include + +#include "print.h" +#include "tag.h" +#include "acpi/acpi.h" +#include "graphics/framebuffer.h" +#include "memory/memory.h" + +static struct BootInfo info; + +static void read_cmdline(struct BootTag *tag, char *data, uint8_t len) { + debugk("Found cmdline"); + if (len >= CMDLINE_MAX) + panic("multiboot2 cmd line to long\nmax is %d but was provided %d\n", + CMDLINE_MAX, len); + memcpy(tag->data.cmdline, data, len); + info.tags[ID_CMDLINE] = *tag; +} + +static void read_framebuffer(struct BootTag *tag, uint32_t *data) { + debugk("Found framebuffer"); + tag->data.framebuffer = (struct Framebuffer *) data; + info.tags[ID_FRAMEBUFFER] = *tag;; +} + +static void read_memorymap(struct BootTag *tag, uint32_t *data) { + debugk("Found memorymap"); + tag->data.memory_map = (struct MemoryMap *) data; + info.tags[iD_MEMORYMAP] = *tag; +} + +static void read_rsdp(struct BootTag *tag, char *data) { + debugk("Found RSDP"); + tag->data.rsdp = (struct RootSystemDescriptionPointer *) data; + info.tags[ID_RSDP] = *tag; +} + +static uint32_t *read_tag(uint32_t *data) { + struct BootTag tag; + tag.type = ((uint16_t*)data)[0]; + tag.size = data[1]; + tag.valid = 1; + + uint8_t data_len = tag.size - 2 * sizeof(uint32_t); + + switch (tag.type) { + case ID_CMDLINE: + read_cmdline(&tag, (char *)(data + 2), data_len); + break; + case ID_FRAMEBUFFER: + read_framebuffer(&tag, data + 2); + break; + case iD_MEMORYMAP: + read_memorymap(&tag, data + 2); + break; + case ID_RSDP: + read_rsdp(&tag, (char *) (data + 2)); + break; + default: + break; + } + + if(tag.size % 8 != 0) { + tag.size += 8 - (tag.size % 8); + } + + return data + tag.size / sizeof(uint32_t); +} + +void load_boot_info(void* boot_info) { + + debugk("Reading multiboot info"); + + memset(&info, 0, sizeof(boot_info)); + + uint32_t* data = (uint32_t*) boot_info; + info.total_size = *data++; + info.reserved = *data++; + + while((uint8_t*) data < (uint8_t*) boot_info + info.total_size) { + data = read_tag(data); + } + + succek("Loaded multiboot info"); +} + +bool get_boot_tag(enum BootTagID id, struct BootTag **tag) { + *tag = &info.tags[id]; + return (*tag)->valid; +} diff --git a/kernel/src/boot/tag.h b/kernel/src/boot/tag.h new file mode 100644 index 0000000..f9853e5 --- /dev/null +++ b/kernel/src/boot/tag.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +#include "acpi/acpi.h" +#include "graphics/framebuffer.h" +#include "memory/memory.h" + +#define CMDLINE_MAX 32 + +struct BootTag { + uint8_t valid; + uint32_t type; + uint32_t size; + union { + char cmdline[CMDLINE_MAX]; + struct Framebuffer *framebuffer; + struct MemoryMap *memory_map; + struct RootSystemDescriptionPointer *rsdp; + } data; +}; + +struct BootInfo { + uint32_t total_size; + uint32_t reserved; + struct BootTag tags[21]; +}; + +enum BootTagID { + ID_CMDLINE = 0, + iD_MEMORYMAP = 6, + ID_FRAMEBUFFER = 8, + ID_RSDP = 14 +}; + +void load_boot_info(void* boot_info); +bool get_boot_tag(enum BootTagID id, struct BootTag **tag); diff --git a/kernel/src/cpu/cpu.c b/kernel/src/cpu/cpu.c new file mode 100644 index 0000000..f78ef26 --- /dev/null +++ b/kernel/src/cpu/cpu.c @@ -0,0 +1,20 @@ +#include "cpu.h" + +#include "print.h" + +extern int sse_init (void); +extern int fpu_init (void); + +void init_registers (void) { + if (!sse_init()) { + debugk("Loaded SIMD"); + } else { + errork("SIMD not supported"); + } + + if (!fpu_init()) { + debugk("Loaded FPU"); + } else { + errork("FPU not supported"); + } +} diff --git a/kernel/src/cpu/cpu.h b/kernel/src/cpu/cpu.h new file mode 100644 index 0000000..3044292 --- /dev/null +++ b/kernel/src/cpu/cpu.h @@ -0,0 +1,3 @@ +#pragma once + +void init_registers (void); diff --git a/kernel/src/cpu/fpu.asm b/kernel/src/cpu/fpu.asm new file mode 100644 index 0000000..e49ab6f --- /dev/null +++ b/kernel/src/cpu/fpu.asm @@ -0,0 +1,19 @@ +global fpu_init + +fpu_init: + mov edx, cr0 + and edx, (-1) - ((1 << 3) + (1 << 4)) + mov cr0, edx + fninit + fnstsw [test] + cmp word [test], 0 + jne no_fpu + + xor eax, 0 + ret + +no_fpu: + mov eax, 1 + ret + +test: dw 0x55AA diff --git a/kernel/src/cpu/sse.asm b/kernel/src/cpu/sse.asm new file mode 100644 index 0000000..e7b5a99 --- /dev/null +++ b/kernel/src/cpu/sse.asm @@ -0,0 +1,22 @@ +global sse_init + +sse_init: + mov eax, 0x1 + cpuid + test edx, 1<<25 + jmp no_sse + + mov eax, cr0 + and ax, 0xFFFB + or ax, 0x2 + mov cr0, eax + mov eax, cr4 + or ax, 3 << 9 + mov cr4, eax + + mov eax, 0 + ret + +no_sse: + mov eax, 1 + ret diff --git a/kernel/src/drivers/ps2ctrl.c b/kernel/src/drivers/ps2ctrl.c new file mode 100644 index 0000000..527435d --- /dev/null +++ b/kernel/src/drivers/ps2ctrl.c @@ -0,0 +1,99 @@ +#include +#include + +#include "print.h" +#include "ps2ctrl.h" +#include "interrupt/pic.h" + +#define STATUS_OUT_BUF ((uint8_t)0x01) +#define STATUS_IN_BUF ((uint8_t)0x02) + +#define CONFIG_INT_0 ((uint8_t)0x01) +#define CONFIG_INT_1 ((uint8_t)0x02) +#define CONFIG_SYS ((uint8_t)0x04) +#define CONFIG_CLOCK_0 ((uint8_t)0x10) +#define CONFIG_CLOCK_1 ((uint8_t)0x20) +#define CONFIG_TRANS ((uint8_t)0x40) + +static bool is_init = false; + +uint8_t ps2ctrl_in(void) { + while((ps2ctrl_in_status() & STATUS_OUT_BUF) == 0) { + io_wait(); + } + return inb(0x60); +} + +uint8_t ps2ctrl_in_status(void) { + return inb(0x64); +} + +void ps2ctrl_out_cmd(uint8_t cmd) { + while((ps2ctrl_in_status() & STATUS_IN_BUF) != 0) { + io_wait(); + } + outb(0x64, cmd); +} + +void ps2ctrl_out_data(uint8_t data) { + while((ps2ctrl_in_status() & STATUS_IN_BUF) != 0) { + io_wait(); + } + outb(0x60, data); +} + +void ps2ctrl_set_port2(void) { + outb(0x64, 0xD4); +} + +void ps2ctrl_init(void) { + + debugk("Loading PS/2 Controller"); + + is_init = false; + + pic_mask(1); // keyboard + pic_mask(12); // mouse + + inb(0x60); + + // self-test + ps2ctrl_out_cmd(0xAA); + uint8_t response = ps2ctrl_in(); + if(response != 0x55) { + errork("PS/2 controller failed to initialize"); + return; + } + + // set config + ps2ctrl_out_cmd(0x20); + uint8_t config = ps2ctrl_in(); + config = (config | CONFIG_INT_0 | CONFIG_INT_1) & ~CONFIG_TRANS; + // config = 0xFF; + ps2ctrl_out_cmd(0x60); + ps2ctrl_out_data(config); + + // enable port 0 + ps2ctrl_out_cmd(0xAE); + + // enable port 2 + ps2ctrl_out_cmd(0xA9); + response = ps2ctrl_in(); + if (response == 0x01) { + errork("PS/2 port 2 not supported"); + return; + } + + ps2ctrl_out_cmd(0xA8); + + is_init = true; + + pic_unmask(1); + pic_unmask(12); + + succek("Loaded PS/2 Controller"); +} + +bool ps2ctrl_is_init(void) { + return is_init; +} diff --git a/kernel/src/drivers/ps2ctrl.h b/kernel/src/drivers/ps2ctrl.h new file mode 100644 index 0000000..a674c57 --- /dev/null +++ b/kernel/src/drivers/ps2ctrl.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +uint8_t ps2ctrl_in(void); +uint8_t ps2ctrl_in_status(void); +void ps2ctrl_out_cmd(uint8_t cmd); +void ps2ctrl_out_data(uint8_t data); +void ps2ctrl_set_port2(void); + +void ps2ctrl_init(void); + +bool ps2ctrl_is_init(void); diff --git a/kernel/src/drivers/ps2kb.c b/kernel/src/drivers/ps2kb.c new file mode 100644 index 0000000..dec0b87 --- /dev/null +++ b/kernel/src/drivers/ps2kb.c @@ -0,0 +1,130 @@ +#include +#include + +#include "print.h" +#include "ps2kb.h" +#include "ps2ctrl.h" +#include "interrupt/pic.h" + +#define BUFFER_LEN 16 + +#define KEYCODE_ARRAY_LEN 0x84 + +static uint8_t scancodes[] = { +// 00/08 01/09 02/0A 03/0B 04/0C 05/0D 06/0E 07/0F +/*00*/ KEY_NONE, KEY_F9, KEY_NONE, KEY_F5, KEY_F3, KEY_F1, KEY_F2, KEY_F12, +/*08*/ KEY_NONE, KEY_F10, KEY_F8, KEY_F6, KEY_F4, KEY_TAB, KEY_BACKTICK, KEY_NONE, +/*10*/ KEY_NONE, KEY_L_ALT, KEY_L_SHIFT, KEY_NONE, KEY_L_CTRL, KEY_Q, KEY_1, KEY_NONE, +/*18*/ KEY_NONE, KEY_NONE, KEY_Z, KEY_S, KEY_A, KEY_W, KEY_2, KEY_NONE, +/*20*/ KEY_NONE, KEY_C, KEY_X, KEY_D, KEY_E, KEY_4, KEY_3, KEY_NONE, +/*28*/ KEY_NONE, KEY_SPACE, KEY_V, KEY_F, KEY_T, KEY_R, KEY_5, KEY_NONE, +/*30*/ KEY_NONE, KEY_N, KEY_B, KEY_H, KEY_G, KEY_Y, KEY_6, KEY_NONE, +/*38*/ KEY_NONE, KEY_NONE, KEY_M, KEY_J, KEY_U, KEY_7, KEY_8, KEY_NONE, +/*40*/ KEY_NONE, KEY_COMMA, KEY_K, KEY_I, KEY_O, KEY_0, KEY_9, KEY_NONE, +/*48*/ KEY_NONE, KEY_PERIOD, KEY_SLASH, KEY_L, KEY_SEMICOLON, KEY_P, KEY_MINUS, KEY_NONE, +/*50*/ KEY_NONE, KEY_NONE, KEY_QUOTE, KEY_NONE, KEY_L_BRACE, KEY_EQUAL, KEY_NONE, KEY_NONE, +/*58*/ KEY_CAPS_LOCK, KEY_R_SHIFT, KEY_ENTER, KEY_R_BRACE, KEY_NONE, KEY_BACKSLASH, KEY_NONE, KEY_NONE, +/*60*/ KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_BACKSPACE, KEY_NONE, +/*68*/ KEY_NONE, KEY_NP_1, KEY_NONE, KEY_NP_4, KEY_NP_7, KEY_NONE, KEY_NONE, KEY_NONE, +/*70*/ KEY_NP_0, KEY_NP_PERIOD, KEY_NP_2, KEY_NP_5, KEY_NP_6, KEY_NP_8, KEY_ESCAPE, KEY_NUM_LOCK, +/*78*/ KEY_F11, KEY_NP_PLUS, KEY_NP_3, KEY_NP_MINUS, KEY_NP_ASTERISK, KEY_NP_9, KEY_SCROLL_LOCK, KEY_NONE, +/*80*/ KEY_NONE, KEY_NONE, KEY_NONE, KEY_F7, +}; +static uint8_t scancodes_ext[] = { +// 00/08 01/09 02/0A 03/0B 04/0C 05/0D 06/0E 07/0F +/*00*/ KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, +/*08*/ KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, +/*10*/ KEY_UNKNOWN, KEY_R_ALT, KEY_PRINT_SCREEN, KEY_NONE, KEY_R_CTRL, KEY_UNKNOWN, KEY_NONE, KEY_NONE, +/*18*/ KEY_UNKNOWN, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_L_META, +/*20*/ KEY_UNKNOWN, KEY_UNKNOWN, KEY_NONE, KEY_UNKNOWN, KEY_NONE, KEY_NONE, KEY_NONE, KEY_R_META, +/*28*/ KEY_UNKNOWN, KEY_NONE, KEY_NONE, KEY_UNKNOWN, KEY_NONE, KEY_NONE, KEY_NONE, KEY_MENU, +/*30*/ KEY_UNKNOWN, KEY_NONE, KEY_UNKNOWN, KEY_NONE, KEY_UNKNOWN, KEY_NONE, KEY_NONE, KEY_UNKNOWN, +/*38*/ KEY_UNKNOWN, KEY_NONE, KEY_UNKNOWN, KEY_UNKNOWN, KEY_NONE, KEY_NONE, KEY_NONE, KEY_UNKNOWN, +/*40*/ KEY_UNKNOWN, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, +/*48*/ KEY_UNKNOWN, KEY_NONE, KEY_NP_SLASH, KEY_NONE, KEY_NONE, KEY_UNKNOWN, KEY_NONE, KEY_NONE, +/*50*/ KEY_UNKNOWN, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, +/*58*/ KEY_NONE, KEY_NONE, KEY_NP_ENTER, KEY_NONE, KEY_NONE, KEY_NONE, KEY_UNKNOWN, KEY_NONE, +/*60*/ KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, +/*68*/ KEY_NONE, KEY_END, KEY_NONE, KEY_LEFT, KEY_HOME, KEY_NONE, KEY_NONE, KEY_NONE, +/*70*/ KEY_INSERT, KEY_DELETE, KEY_DOWN, KEY_NONE, KEY_RIGHT, KEY_UP, KEY_NONE, KEY_NONE, +/*78*/ KEY_NONE, KEY_NONE, KEY_PAGE_DOWN, KEY_NONE, KEY_NONE, KEY_PAGE_UP, KEY_NONE, KEY_NONE, +/*80*/ KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, +}; + +static struct Keycode last_keycode; + +static bool is_init = false; +static bool state_keyup = false; +static bool state_ext = false; + +void ps2kb_init(void) { + + debugk("Loading PS/2 Keyboard"); + + is_init = false; + pic_mask(1); + + uint8_t result; + + ps2ctrl_out_data(0xFF); + result = ps2ctrl_in(); + if(result != 0xFA) { + errork("Failed to reset PS/2 keyboard: expected 0xFA, got 0x%X\n", result); + return; + } + result = ps2ctrl_in(); + if(result != 0xAA) { + errork("Failed to reset PS/2 keyboard: expected 0xAA, got 0x%X\n", result); + return; + } + + ps2ctrl_out_data(0xF4); + result = ps2ctrl_in(); + if(result != 0xFA) { + errork("Failed to enable PS/2 keyboard: expected 0xFA, got 0x%X\n", result); + return; + } + + succek("PS/2 Keyboard has has been loaded"); + + pic_unmask(1); + is_init = true; +} + +void ps2kb_recv(void) { + if(!ps2ctrl_is_init() || !is_init) { + inb(0x60); + return; + } + uint8_t code = ps2ctrl_in(); + if (code == 0x00 || code == 0x0F) { + last_keycode.key = KEY_NONE; + last_keycode.flags = KC_FLAG_ERROR; + } else if(code == 0xF0) { + state_keyup = true; + } else if(code == 0xE0) { + state_ext = true; + } else if(code <= KEYCODE_ARRAY_LEN) { + uint8_t *scancode_table = state_ext ? scancodes_ext : scancodes; + uint8_t keycode = scancode_table[code]; + if(keycode != KEY_NONE) { + last_keycode.key = keycode; + last_keycode.flags = state_keyup ? KC_FLAG_KEY_UP : KC_FLAG_KEY_DOWN; + } + state_keyup = false; + state_ext = false; + } +} + +struct Keycode ps2kb_get(void) { + struct Keycode code; + if(is_init) { + code = last_keycode; + } else { + code.key = KEY_NONE; + code.flags = KC_FLAG_ERROR; + } + last_keycode.key = KEY_NONE; + last_keycode.flags = 0; + return code; +} diff --git a/kernel/src/drivers/ps2kb.h b/kernel/src/drivers/ps2kb.h new file mode 100644 index 0000000..1aaefb2 --- /dev/null +++ b/kernel/src/drivers/ps2kb.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include +#include + +struct Keycode { + uint8_t key; + uint8_t flags; +}; + +void ps2kb_init(void); + +void ps2kb_recv(void); +struct Keycode ps2kb_get(void); diff --git a/kernel/src/drivers/ps2mouse.c b/kernel/src/drivers/ps2mouse.c new file mode 100644 index 0000000..01e10cc --- /dev/null +++ b/kernel/src/drivers/ps2mouse.c @@ -0,0 +1,89 @@ +#include +#include +#include +#include + +#include "print.h" +#include "ps2mouse.h" +#include "drivers/ps2ctrl.h" +#include "interrupt/pic.h" + +static bool is_init = false; + +static struct MouseEvent last_event; +static uint8_t first_b, second_b, third_b; + +void ps2mouse_init(void) { + + debugk("Loading PS/2 Mouse"); + + is_init = false; + pic_mask(12); + + uint8_t result; + + ps2ctrl_set_port2(); + ps2ctrl_out_data(0xFF); + result = ps2ctrl_in(); + if (result != 0xFA) { + errork("Failed to reset PS/2 mouse: expected 0xFA, got 0x%X", result); + return; + } + result = ps2ctrl_in(); + if (result != 0xAA) { + errork("Failed to reset PS/2 mouse: expected 0xAA, got 0x%X", result); + return; + } + + ps2ctrl_set_port2(); + ps2ctrl_out_data(0xF4); + + pic_unmask(12); + is_init = true; + + succek("PS/2 Mouse has has been loaded"); +} + +static uint8_t packet_num = 0; +void ps2mouse_recv(void) { + if (!ps2ctrl_is_init() || !is_init) { + inb(0x60); + return; + } + + uint8_t packet = ps2ctrl_in(); + switch (packet_num) { + case 0: + first_b = packet; + break; + case 1: + second_b = packet; + break; + case 2: { + third_b = packet; + + int state, d; + state = first_b; + d = second_b; + last_event.relx = d - ((state << 4) & 0x100); + d = third_b; + last_event.rely = d - ((state << 3) & 0x100); + + last_event.lmb = first_b & 0x01; + last_event.rmb = first_b & 0x02; + last_event.mmb = first_b & 0x04; + last_event.updated = true; + break; + } + } + + packet_num += 1; + packet_num %= 3; + +} + +struct MouseEvent ps2mouse_get(void) { + struct MouseEvent event = last_event; + last_event.updated = false; + return event; +} diff --git a/kernel/src/drivers/ps2mouse.h b/kernel/src/drivers/ps2mouse.h new file mode 100644 index 0000000..9cd4818 --- /dev/null +++ b/kernel/src/drivers/ps2mouse.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +struct MouseEvent { + bool updated; + bool lmb; + bool rmb; + bool mmb; + int relx; + int rely; +}; + +void ps2mouse_init(void); + +void ps2mouse_recv(void); +struct MouseEvent ps2mouse_get(void); diff --git a/kernel/src/graphics/framebuffer.h b/kernel/src/graphics/framebuffer.h new file mode 100644 index 0000000..c3b0dc6 --- /dev/null +++ b/kernel/src/graphics/framebuffer.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +struct Framebuffer { + uint64_t addr; + uint32_t pitch; + uint32_t width; + uint32_t height; + uint8_t depth; + uint8_t type; + uint8_t reserved; + // struct FramebufferPallete palettes[]; + +} __attribute__ ((packed)); diff --git a/kernel/src/interrupt/idt.c b/kernel/src/interrupt/idt.c new file mode 100644 index 0000000..6df3793 --- /dev/null +++ b/kernel/src/interrupt/idt.c @@ -0,0 +1,108 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "acpi/acpi.h" +#include "drivers/ps2kb.h" +#include "drivers/ps2mouse.h" +#include "tty/color.h" +#include "idt.h" +#include "pic.h" +#include "tty/term.h" + +static int timer = 0; + +void idt_pic_eoi(uint8_t exception) { + pic_eoi(exception - PIC_REMAP_OFFSET); +} + +void idt_pic_timer(void) { + uint32_t state = term_save(); + term_setfg(VGA_LIGHT_GREEN); + term_setpos(60, 0); + puts(" "); + term_setpos(60, 0); + printk("%d", timer); + timer += 1; + term_load(state); +} + +void idt_pic_keyboard(void) { + ps2kb_recv(); +} + +void idt_pic_mouse(void) { + ps2mouse_recv(); +} + +void idt_exception_handler(uint8_t exception) { + char* msg; + switch(exception) { + case 0x00: + msg = "Division by zero"; + break; + case 0x02: + msg = "NMI"; + break; + case 0x04: + msg = "Overflow"; + break; + case 0x06: + msg = "invalid opcode"; + break; + case 0x08: + msg = "double fault"; + break; + case 0x0A: + msg = "invalid task state segment"; + break; + case 0x0C: + msg = "stack segment fault"; + break; + case 0x0D: + msg = "general protection fault"; + break; + case 0x0E: + msg = "page fault"; + break; + default: + msg = "unknown exception"; + break; + } + panic("E%u: %s", exception, msg); +} + +__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) { + + debugk("Loading IDT"); + + idtr.base = (uintptr_t)&idt[0]; + idtr.limit = (uint16_t)sizeof(struct IdtEntry) * IDT_SIZE - 1; + + for(int i = 0; i < IDT_INTERRUPTS; i++) { + set_descriptor(i, isr_stub_table[i], 0x8e); + } + + __asm__ volatile ("lidt %0" : : "m"(idtr)); + + succek("IDT has been loaded"); +} + diff --git a/kernel/src/interrupt/idt.h b/kernel/src/interrupt/idt.h new file mode 100644 index 0000000..86a685f --- /dev/null +++ b/kernel/src/interrupt/idt.h @@ -0,0 +1,36 @@ +#pragma once + +#include + +#define IDT_SIZE 256 +#define IDT_INTERRUPTS 256 + +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)); + +enum IDTFlags { + 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, +}; + +void idt_init(void); diff --git a/kernel/src/interrupt/isr.asm b/kernel/src/interrupt/isr.asm new file mode 100644 index 0000000..6cccdc6 --- /dev/null +++ b/kernel/src/interrupt/isr.asm @@ -0,0 +1,99 @@ +extern idt_exception_handler +extern idt_pic_timer +extern idt_pic_keyboard +extern idt_pic_mouse +extern idt_pic_eoi +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_eoi + pop eax + iret +%endmacro + +%macro PICTimer 1 +isr_stub_%+%1: + call idt_pic_timer + push dword %1 + call idt_pic_eoi + pop eax + iret +%endmacro + +%macro PICKeyboard 1 +isr_stub_%+%1: + call idt_pic_keyboard + push dword %1 + call idt_pic_eoi + pop eax + iret +%endmacro + +%macro PICMouse 1 +isr_stub_%+%1: + call idt_pic_mouse + push dword %1 + call idt_pic_eoi + 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 +align 8 +%assign i 0 +%rep 32 + ISRErrorStub i +%assign i i+1 +%endrep +PICTimer 32 ; 0 +PICKeyboard 33 ; 1 +PICGeneric 34 ; 2 +PICGeneric 35 ; 3 +PICGeneric 36 ; 4 +PICGeneric 37 ; 5 +PICGeneric 38 ; 6 +PICGeneric 39 ; 7 +PICGeneric 40 ; 8 +PICGeneric 41 ; 9 +PICGeneric 42 ; 10 +PICGeneric 43 ; 11 +PICMouse 44 ; 12 +PICGeneric 45 ; 13 +PICGeneric 46 ; 14 +PICGeneric 47 ; 15 +%assign i 48 +%rep 256 - 48 + ISRErrorStub i +%assign i i+1 +%endrep + +section .rodata +align 8 +isr_stub_table: +%assign i 0x00 +%rep 256 + dd isr_stub_%+i +%assign i i+0x01 +%endrep diff --git a/kernel/src/interrupt/pic.c b/kernel/src/interrupt/pic.c new file mode 100644 index 0000000..86056a1 --- /dev/null +++ b/kernel/src/interrupt/pic.c @@ -0,0 +1,85 @@ +#include +#include + +#include "pic.h" + +#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) { + + debugk("Remapping PIC"); + + 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, 0x04); + 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); + + succek("PIC has been remapped to offset 0x%X", offset); +} + +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)); +} + +void pic_disable(void) { + outb(PIC1_DATA_PORT, 0xff); + io_wait(); + outb(PIC2_DATA_PORT, 0xff); + io_wait(); +} + +void pic_eoi(int irq) { + if(irq >= 8) { + outb(PIC2_COMMAND_PORT, 0x20); + } + outb(PIC1_COMMAND_PORT, 0x20); +} diff --git a/kernel/src/interrupt/pic.h b/kernel/src/interrupt/pic.h new file mode 100644 index 0000000..a87420d --- /dev/null +++ b/kernel/src/interrupt/pic.h @@ -0,0 +1,11 @@ +#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); +void pic_eoi(int irq); diff --git a/kernel/src/main.c b/kernel/src/main.c new file mode 100644 index 0000000..2a04c24 --- /dev/null +++ b/kernel/src/main.c @@ -0,0 +1,68 @@ +#include "acpi/acpi.h" +#include "boot/tag.h" +#include "cpu/cpu.h" +#include "drivers/ps2ctrl.h" +#include "drivers/ps2kb.h" +#include "drivers/ps2mouse.h" +#include "interrupt/idt.h" +#include "interrupt/pic.h" +#include "tty/cursor.h" +#include "tty/term.h" + +#include +#include +#include +#include + +static double x = 0, y = 0; + +void kernel_main(void* boot_info) { + + term_init(); + cursor_enable(); + + idt_init(); + pic_remap(PIC_REMAP_OFFSET); + + load_boot_info(boot_info); + acpi_init(); + + memory_init(); + + ps2ctrl_init(); + ps2kb_init(); + ps2mouse_init(); + + init_registers(); + + while(1) { + int_wait(); + + struct Keycode code = ps2kb_get(); + if(code.key != KEY_NONE) { + if(code.flags & KC_FLAG_ERROR) { + printk("error: %X\n", code.key); + } else if(code.flags & KC_FLAG_KEY_DOWN) { + printk("pressed: %X\n", code.key); + } else { + printk("released: %X\n", code.key); + } + } + + if (code.key == KEY_ESCAPE) { + acpi_poweroff(); + } + + struct MouseEvent event = ps2mouse_get(); + if (event.updated) { + putchar(event.lmb ? 'L' : '_'); + putchar(event.rmb ? 'R' : '_'); + putchar(event.mmb ? 'M' : '_'); + x += event.relx / 10.0; + y -= event.rely / 10.0; + x = fclamp(x, 0, TERM_W); + y = fclamp(y, 0, TERM_H); + printk(" x%d y%d\n", (int)x, (int)y); + } + } +} diff --git a/kernel/src/memory/allocator.c b/kernel/src/memory/allocator.c new file mode 100644 index 0000000..6217ad4 --- /dev/null +++ b/kernel/src/memory/allocator.c @@ -0,0 +1,314 @@ +#include +#include +#include + +struct boundary_tag { + unsigned int magic; + unsigned int size; + unsigned int real_size; + int index; + + struct boundary_tag *split_left; + struct boundary_tag *split_right; + + struct boundary_tag *next; + struct boundary_tag *prev; +}; + +extern int memory_lock(void); +extern int memory_unlock(void); +extern void *memory_alloc_page(int); +extern int memory_free_page(void* ,int); + +#define ALLOC_MAGIC 0xc001c0de +#define MAXCOMPLETE 5 +#define MAXEXP 32 +#define MINEXP 8 + +#define MODE_BEST 0 +#define MODE_INSTANT 1 + +#define MODE MODE_BEST + +struct boundary_tag* l_freePages[MAXEXP]; +int l_completePages[MAXEXP]; + +static int l_initialized = 0; +static int l_pageSize = 4096; +static int l_pageCount = 16; + +static inline int getexp(unsigned int size) { + if (size < (1< size) break; + shift += 1; + } + + return shift - 1; +} + +static inline void insert_tag(struct boundary_tag *tag, int index) { + int realIndex; + + if (index < 0) { + realIndex = getexp(tag->real_size - sizeof(struct boundary_tag)); + if (realIndex < MINEXP) realIndex = MINEXP; + } else { + realIndex = index; + } + + tag->index = realIndex; + + if (l_freePages[ realIndex ] != NULL) { + l_freePages[ realIndex ]->prev = tag; + tag->next = l_freePages[ realIndex ]; + } + + l_freePages[ realIndex ] = tag; +} + +static inline void remove_tag(struct boundary_tag *tag) { + if (l_freePages[ tag->index ] == tag) l_freePages[ tag->index ] = tag->next; + + if (tag->prev != NULL) tag->prev->next = tag->next; + if (tag->next != NULL) tag->next->prev = tag->prev; + + tag->next = NULL; + tag->prev = NULL; + tag->index = -1; +} + +static inline struct boundary_tag* melt_left(struct boundary_tag *tag) { + struct boundary_tag *left = tag->split_left; + + left->real_size += tag->real_size; + left->split_right = tag->split_right; + + if (tag->split_right != NULL) tag->split_right->split_left = left; + + return left; +} + + +static inline struct boundary_tag* absorb_right(struct boundary_tag *tag) { + struct boundary_tag *right = tag->split_right; + + remove_tag(right); + + tag->real_size += right->real_size; + + tag->split_right = right->split_right; + if (right->split_right != NULL) + right->split_right->split_left = tag; + + return tag; +} + +static inline struct boundary_tag* split_tag(struct boundary_tag* tag) { + unsigned int remainder = tag->real_size - sizeof(struct boundary_tag) - tag->size; + + struct boundary_tag *new_tag = + (struct boundary_tag*)((uintptr_t)(void *)tag + sizeof(struct boundary_tag) + tag->size); + + new_tag->magic = ALLOC_MAGIC; + new_tag->real_size = remainder; + + new_tag->next = NULL; + new_tag->prev = NULL; + + new_tag->split_left = tag; + new_tag->split_right = tag->split_right; + + if (new_tag->split_right != NULL) new_tag->split_right->split_left = new_tag; + tag->split_right = new_tag; + + tag->real_size -= new_tag->real_size; + + insert_tag(new_tag, -1); + + return new_tag; +} + +static struct boundary_tag* allocate_new_tag(unsigned int size) { + unsigned int pages; + unsigned int usage; + struct boundary_tag *tag; + + usage = size + sizeof(struct boundary_tag); + + pages = usage / l_pageSize; + if ((usage % l_pageSize) != 0) pages += 1; + + if (pages < (unsigned) l_pageCount) pages = l_pageCount; + + tag = (struct boundary_tag*)memory_alloc_page(pages); + + if (tag == NULL) return NULL; + + tag->magic = ALLOC_MAGIC; + tag->size = size; + tag->real_size = pages * l_pageSize; + tag->index = -1; + + tag->next = NULL; + tag->prev = NULL; + tag->split_left = NULL; + tag->split_right = NULL; + + return tag; +} + + + +void *malloc(size_t size) { + int index; + void *ptr; + struct boundary_tag *tag = NULL; + + memory_lock(); + + if (l_initialized == 0) { + for (index = 0; index < MAXEXP; index++) { + l_freePages[index] = NULL; + l_completePages[index] = 0; + } + l_initialized = 1; + } + + index = getexp(size) + MODE; + if (index < MINEXP) index = MINEXP; + + tag = l_freePages[index]; + while (tag != NULL) { + if ( + (tag->real_size - sizeof(struct boundary_tag)) + >= (size + sizeof(struct boundary_tag)) + ) { + break; + } + tag = tag->next; + } + + if (tag == NULL) { + if ((tag = allocate_new_tag(size)) == NULL) { + memory_unlock(); + return NULL; + } + index = getexp(tag->real_size - sizeof(struct boundary_tag)); + } else { + remove_tag(tag); + + if ((tag->split_left == NULL) && (tag->split_right == NULL)) + l_completePages[ index ] -= 1; + } + + tag->size = size; + + unsigned int remainder = tag->real_size - size - sizeof(struct boundary_tag) * 2; + + if (((int)(remainder) > 0)) { + int childIndex = getexp(remainder); + + if (childIndex >= 0) { + struct boundary_tag *new_tag = split_tag(tag); + tag = new_tag; + } + } + + ptr = (void*)((uintptr_t)(void *)tag + sizeof(struct boundary_tag)); + memory_unlock(); + return ptr; +} + +void free(void *ptr) { + int index; + struct boundary_tag *tag; + + if (ptr == NULL) return; + + memory_lock(); + + tag = (struct boundary_tag*)((uintptr_t)(void *)ptr - sizeof(struct boundary_tag)); + + if (tag->magic != ALLOC_MAGIC) { + memory_unlock(); + return; + } + + while ((tag->split_left != NULL) && (tag->split_left->index >= 0)) { + tag = melt_left(tag); + remove_tag(tag); + } + + while ((tag->split_right != NULL) && (tag->split_right->index >= 0)) { + tag = absorb_right(tag); + } + + index = getexp(tag->real_size - sizeof(struct boundary_tag)); + if (index < MINEXP) index = MINEXP; + + if ((tag->split_left == NULL) && (tag->split_right == NULL)) { + if (l_completePages[index] == MAXCOMPLETE) { + unsigned int pages = tag->real_size / l_pageSize; + + if ((tag->real_size % l_pageSize) != 0) pages += 1; + if (pages < (unsigned) l_pageCount) pages = l_pageCount; + + memory_free_page(tag, pages); + memory_unlock(); + return; + } + + l_completePages[ index ] += 1; + } + + insert_tag(tag, index); + + memory_unlock(); +} + +void* calloc(size_t nobj, size_t size) { + int real_size; + void *p; + + real_size = nobj * size; + + p = malloc(real_size); + + memset(p, 0, real_size); + + return p; +} + +void* realloc(void *p, size_t size) { + void *ptr; + struct boundary_tag *tag; + size_t real_size; + + if (size == 0) { + free(p); + return NULL; + } + + if (p == NULL) { + return malloc(size); + } + + memory_lock(); + tag = (struct boundary_tag*)((uintptr_t)(void *)p - sizeof(struct boundary_tag)); + real_size = tag->size; + memory_unlock(); + + if (real_size > size) real_size = size; + + ptr = malloc(size); + memcpy(ptr, p, real_size); + free(p); + + return ptr; +} diff --git a/kernel/src/memory/memory.c b/kernel/src/memory/memory.c new file mode 100644 index 0000000..30da0fc --- /dev/null +++ b/kernel/src/memory/memory.c @@ -0,0 +1,202 @@ +#include +#include +#include +#include + +#include "memory.h" +#include "boot/tag.h" +#include "print.h" + +struct MemoryArea { + uint32_t len; + struct MemoryArea *prev; + struct MemoryArea *next; +}; + +typedef unsigned char page[4096]; + +extern unsigned char kernel_start, kernel_end; +static uintptr_t kernel_start_addr, kernel_end_addr; +static uint32_t *bitmap; +static uint32_t total_memory; +static uint32_t free_memory; +static uint32_t page_count; +static uint32_t page_free_start; +static struct MemoryArea *page_start; + +int memory_lock(void) { + int_disable(); + return 0; +} + +int memory_unlock(void) { + int_enable(); + return 0; +} + +static int n_pages(const struct MemoryArea *m) { + return (m->len - sizeof(*m)) / sizeof(page); +} + +static void *page_at(int i) { + int cur_page = 0; + for (struct MemoryArea *m = page_start; m != NULL; m = m->next) { + int pages = n_pages(m); + if (i - cur_page < pages) { + page *page_array = (page *) (m + 1); + return page_array[i - cur_page]; + } + cur_page += pages; + } + return NULL; +} + +static int page_idx(page p) { + uintptr_t addr = (uintptr_t) p; + int cur_page = 0; + for (struct MemoryArea *m = page_start; m != NULL; m = m->next) { + if ((uintptr_t) m + m->len > addr) { + return cur_page + (addr - (uintptr_t) m) / sizeof(page); + } + cur_page += n_pages(m); + } + return -1; +} + +static inline bool bitmap_get(int i) { + return (bitmap[i / 32] >> i % 32) & 1; +} + +static inline void bitmap_set(int i, bool v) { + int idx = i / 32; + bitmap[idx] &= ~(1 << i % 32); + bitmap[idx] |= (v << i % 32); +} + +void *memory_alloc_page(int pages) { + if (pages < 1) return NULL; + + int n_contiguous = 0; + int free_region_start = 0; + bool first = true; + for (uint32_t i = page_free_start; i < page_count; i++) { + bool free = !bitmap_get(i); + + if (first) { + first = false; + page_free_start = i; + } + + if (free) { + if (n_contiguous == 0) free_region_start = i; + n_contiguous++; + if (n_contiguous == pages) { + for (int j = 0; j < pages; j++) + bitmap_set(free_region_start + j, true); + return page_at(free_region_start); + } + } else n_contiguous = 0; + } + + return NULL; +} + +int memory_free_page(void *ptr, int pages) { + int idx = page_idx(ptr); + if (idx == -1) return 1; + + if ((unsigned) idx < page_free_start) page_free_start = idx; + + for (int i = 0; i < pages; i++) + bitmap_set(idx + pages, false); + return 0; +} + +void memory_init(void) { + + debugk("Loading memory pages"); + + memory_lock(); + + bitmap = NULL; + total_memory = 0; + free_memory = 0; + page_count = 0; + page_free_start = 0; + page_start = NULL; + + kernel_start_addr = (uintptr_t) &kernel_start; + kernel_end_addr = (uintptr_t) &kernel_end; + + struct BootTag *tag; + if (!get_boot_tag(iD_MEMORYMAP, &tag)) { + panic("No multiboot memory map found"); + } + + uintptr_t end = (uintptr_t) tag->data.memory_map; + end += tag->size; + + struct MemoryArea *prev = NULL; + struct MemorySegment *segment = &tag->data.memory_map->entries[0]; + for(; (uintptr_t) segment < end; segment++) { + + if (segment->type != 1) continue; + if (segment->addr >= UINT32_MAX) continue; + if (segment->addr < kernel_start_addr) continue; + + uint32_t length; + if (segment->addr + segment->len > UINT32_MAX) { + length = UINT32_MAX - segment->addr; + } else { + length = segment->len; + } + + uintptr_t addr; + if (segment->addr < kernel_end_addr) { + addr = kernel_end_addr; + length -= addr - segment->addr; + } else { + addr = segment->addr; + } + + struct MemoryArea *current = (struct MemoryArea *) addr; + current->prev = prev; + current->next = NULL; + current->len = length; + + if (prev != NULL) { + prev->next = current; + } else { + page_start = current; + } + + page_count += n_pages(current); + total_memory += length; + + prev = current; + + } + + int bitmap_pages = page_count / 32 / sizeof(page) + 1; + bitmap = (uint32_t *) page_at(page_count - bitmap_pages); + page_count -= bitmap_pages; + memset(bitmap, 0, bitmap_pages * sizeof(page)); + free_memory = page_count * sizeof(page); + + memory_unlock(); + + succek("Memory loaded. %k total %k free", total_memory, free_memory); + +} + +uint32_t memory_total(void) { + return total_memory; +} + +uint32_t memory_free(void) { + return free_memory; +} + +uint32_t memory_used(void) { + return total_memory - free_memory; +} diff --git a/kernel/src/memory/memory.h b/kernel/src/memory/memory.h new file mode 100644 index 0000000..5d99025 --- /dev/null +++ b/kernel/src/memory/memory.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +struct MemorySegment { + uint64_t addr; + uint64_t len; + uint32_t type; + uint32_t reserved; +} __attribute__((packed)); + +struct MemoryMap { + uint32_t entry_size; + uint32_t entry_version; + struct MemorySegment entries[]; +} __attribute__((packed)); + +uint32_t memory_total(void); +uint32_t memory_free(void); +uint32_t memory_used(void); + +void memory_init(void); diff --git a/kernel/src/print/panic.c b/kernel/src/print/panic.c new file mode 100644 index 0000000..5e686e1 --- /dev/null +++ b/kernel/src/print/panic.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include +#include + +#include "tty/color.h" +#include "tty/term.h" + +__attribute__((noreturn)) +void _panic_impl(char* msg, int line, char* file, ...) { + int_disable(); + va_list args; + va_start(args, file); + term_clear(); + term_setpos(0, 0); + term_setfg(VGA_LIGHT_RED); + puts("!!!PANIC!!!\n"); + term_setfg(VGA_WHITE); + vprintk(msg, args); + if (!term_newline()) putchar('\n'); + printk("\nin %s at line %d\n", file, line); + + while(1) { + halt(); + } +} diff --git a/kernel/src/print/print.c b/kernel/src/print/print.c new file mode 100644 index 0000000..46ec047 --- /dev/null +++ b/kernel/src/print/print.c @@ -0,0 +1,128 @@ +#include "tty/color.h" +#include "tty/term.h" +#include +#include +#include +#include +#include + +void printk(const char *restrict format, ...) { + va_list args; + va_start(args, format); + vprintk(format, args); + va_end(args); +} + +void vprintk(const char *restrict format, va_list args) { + char buf[80]; + for (; *format; format++) { + if (*format == '%') { + bool l = false; + char c = *++format; + if (c == 'l') { + l = true; + c = *++format; + } + switch(c) { + case '%': + putchar('%'); + break; + case 's': + puts(va_arg(args, char*)); + break; + case 'c': + putchar(va_arg(args, int)); + break; + case 'd': + if (l) ltoa(va_arg(args, long long), buf, 10); + else itoa(va_arg(args, int), buf, 10); + puts(buf); + break; + case 'u': + if (l) ultoa(va_arg(args, unsigned long long), buf, 10); + else utoa(va_arg(args, unsigned int), buf, 10); + puts(buf); + break; + case 'f': + case 'F': + ftoa(va_arg(args, double), buf); + puts(buf); + break; + case 'x': + case 'X': + utoa(va_arg(args, unsigned int), buf, 16); + puts(buf); + break; + case 'b': + va_arg(args, int) ? puts("true") : puts("false"); + break; + case 'k': { + static char disp[] = {'B', 'K', 'M', 'G'}; + uint32_t size = va_arg(args, unsigned int); + size_t i = 0; + while (size / 1024 > 0) { + size /= 1024; + i++; + } + utoa(size, buf, 10); + puts(buf); + putchar(disp[i]); + if (i > 0) putchar('B'); + break; + } + default: + break; + } + } else { + putchar(*format); + } + } +} + +void puts(const char *s) { + for(; *s; s++) putchar(*s); +} + +void printl(enum VGAColor color, const char* msg) { + term_setbg(VGA_BLACK); + term_setfg(VGA_WHITE); + putchar('['); + term_setfg(color); + printk("%s", msg); + term_setfg(VGA_WHITE); + putchar(']'); + putchar(' '); +} + +void _debugk_impl(char *format, ...) { + uint16_t color = term_save_col(); + printl(VGA_LIGHT_CYAN, "LOG"); + va_list args; + va_start(args, format); + vprintk(format, args); + va_end(args); + if (!term_newline()) putchar('\n'); + term_load_col(color); +} + +void _succek_impl(char *format, ...) { + uint16_t color = term_save_col(); + printl(VGA_LIGHT_GREEN, "OK"); + va_list args; + va_start(args, format); + vprintk(format, args); + va_end(args); + if (!term_newline()) putchar('\n'); + term_load_col(color); +} + +void _errork_impl(char *format, ...) { + uint16_t color = term_save_col(); + printl(VGA_LIGHT_RED, "ERR"); + va_list args; + va_start(args, format); + vprintk(format, args); + va_end(args); + if (!term_newline()) putchar('\n'); + term_load_col(color); +} diff --git a/kernel/src/start.asm b/kernel/src/start.asm new file mode 100644 index 0000000..95f4a05 --- /dev/null +++ b/kernel/src/start.asm @@ -0,0 +1,75 @@ +global start +global heap_start +extern kernel_main +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 + +MAGIC equ 0xe85250d6 +LENGTH equ mb_end - mb_start +CHECKSUM equ -(MAGIC + LENGTH) + +section .multiboot +align 8 +mb_start: +dd MAGIC +dd 0 +dd LENGTH +dd CHECKSUM +dw 0 +dw 0 +dd 8 +mb_end: + +section .bss +align 16 +stack_end: +resb 16384 +stack_top: + +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] + mov eax, cr0 + or al, 1 + mov cr0, eax + 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 + sti + push ebx + call kernel_main + cli +halt: + hlt + jmp halt diff --git a/kernel/src/tty/color.c b/kernel/src/tty/color.c new file mode 100644 index 0000000..c7258d9 --- /dev/null +++ b/kernel/src/tty/color.c @@ -0,0 +1,67 @@ +#include "color.h" +#include "panic.h" + +bool itoac(int i, enum AnsiiColor *color) { + if (i < 0 || i > 15) return false; + *color = i; + return true; +} + +bool itovc(int i, enum VGAColor *color) { + if ( + (i >= 30 && i <= 37) || + (i >= 40 && i <= 47) || + (i >= 90 && i <= 97) || + (i >= 100 && i <= 107) + ) { + *color = i; + return true; + } + return false; +} + +enum VGAColor atovc(enum AnsiiColor color) { + switch(color) { + case ANSII_FRONT_BLACK: + case ANSII_FRONT_BLACK_EMPH: + case ANSII_BACK_BLACK: + case ANSII_BACK_BLACK_EMPH: + return VGA_BLACK; + case ANSII_FRONT_RED: + case ANSII_FRONT_RED_EMPH: + case ANSII_BACK_RED: + case ANSII_BACK_RED_EMPH: + return VGA_LIGHT_RED; + case ANSII_FRONT_GREEN: + case ANSII_FRONT_GREEN_EMPH: + case ANSII_BACK_GREEN: + case ANSII_BACK_GREEN_EMPH: + return VGA_LIGHT_GREEN; + case ANSII_FRONT_YELLOW: + case ANSII_FRONT_YELLOW_EMPH: + case ANSII_BACK_YELLOW: + case ANSII_BACK_YELLOW_EMPH: + return VGA_LIGHT_BROWN; + case ANSII_FRONT_BLUE: + case ANSII_FRONT_BLUE_EMPH: + case ANSII_BACK_BLUE: + case ANSII_BACK_BLUE_EMPH: + return VGA_LIGHT_BLUE; + case ANSII_FRONT_PURPLE: + case ANSII_FRONT_PURPLE_EMPH: + case ANSII_BACK_PURPLE: + case ANSII_BACK_PURPLE_EMPH: + return VGA_LIGHT_MAGENTA; + case ANSII_FRONT_CYAN: + case ANSII_FRONT_CYAN_EMPH: + case ANSII_BACK_CYAN: + case ANSII_BACK_CYAN_EMPH: + return VGA_LIGHT_CYAN; + case ANSII_FRONT_WHITE: + case ANSII_FRONT_WHITE_EMPH: + case ANSII_BACK_WHITE: + case ANSII_BACK_WHITE_EMPH: + return VGA_WHITE; + } + panic("this should not be reached (make gcc quiet)"); +} diff --git a/kernel/src/tty/color.h b/kernel/src/tty/color.h new file mode 100644 index 0000000..0751353 --- /dev/null +++ b/kernel/src/tty/color.h @@ -0,0 +1,62 @@ +#pragma once + +#include +#include + +enum VGAColor { + VGA_BLACK = 0, + VGA_BLUE = 1, + VGA_GREEN = 2, + VGA_CYAN = 3, + VGA_RED = 4, + VGA_MAGENTA = 5, + VGA_BROWN = 6, + VGA_LIGHT_GREY = 7, + VGA_DARK_GREY = 8, + VGA_LIGHT_BLUE = 9, + VGA_LIGHT_GREEN = 10, + VGA_LIGHT_CYAN = 11, + VGA_LIGHT_RED = 12, + VGA_LIGHT_MAGENTA = 13, + VGA_LIGHT_BROWN = 14, + VGA_WHITE = 15, +}; + +enum AnsiiColor { + ANSII_FRONT_BLACK = 30, + ANSII_FRONT_RED = 31, + ANSII_FRONT_GREEN = 32, + ANSII_FRONT_YELLOW = 33, + ANSII_FRONT_BLUE = 34, + ANSII_FRONT_PURPLE = 35, + ANSII_FRONT_CYAN = 36, + ANSII_FRONT_WHITE = 37, + ANSII_FRONT_BLACK_EMPH = 90, + ANSII_FRONT_RED_EMPH = 91, + ANSII_FRONT_GREEN_EMPH = 92, + ANSII_FRONT_YELLOW_EMPH = 93, + ANSII_FRONT_BLUE_EMPH = 94, + ANSII_FRONT_PURPLE_EMPH = 95, + ANSII_FRONT_CYAN_EMPH = 96, + ANSII_FRONT_WHITE_EMPH = 97, + ANSII_BACK_BLACK = 40, + ANSII_BACK_RED = 41, + ANSII_BACK_GREEN = 42, + ANSII_BACK_YELLOW = 43, + ANSII_BACK_BLUE = 44, + ANSII_BACK_PURPLE = 45, + ANSII_BACK_CYAN = 46, + ANSII_BACK_WHITE = 47, + ANSII_BACK_BLACK_EMPH = 100, + ANSII_BACK_RED_EMPH = 101, + ANSII_BACK_GREEN_EMPH = 102, + ANSII_BACK_YELLOW_EMPH = 103, + ANSII_BACK_BLUE_EMPH = 104, + ANSII_BACK_PURPLE_EMPH = 105, + ANSII_BACK_CYAN_EMPH = 106, + ANSII_BACK_WHITE_EMPH = 107, +}; + +bool itoac(int i, enum AnsiiColor *color); +bool itovc(int i, enum VGAColor *color); +enum VGAColor atovc(enum AnsiiColor color); diff --git a/kernel/src/tty/cursor.c b/kernel/src/tty/cursor.c new file mode 100644 index 0000000..3a3888b --- /dev/null +++ b/kernel/src/tty/cursor.c @@ -0,0 +1,30 @@ +#include + +#include "cursor.h" +#include "term.h" + +void cursor_enable(void) { + cursor_setsize(13, 16); +} + +void cursor_disable(void) { + outb(0x3D4, 0x0A); + outb(0x3D5, 0x20); +} + +void cursor_setsize(uint8_t start, uint8_t end) { + outb(0x3D4, 0x0A); + outb(0x3D5, (inb(0x3D5) & 0xC0) | start); + + outb(0x3D4, 0x0B); + outb(0x3D5, (inb(0x3D5) & 0xE0) | end); +} + +void cursor_setpos(uint8_t x, uint8_t y) { +; uint16_t pos = y * TERM_W + x; + + outb(0x3D4, 0x0F); + outb(0x3D5, (uint8_t) (pos & 0xFF)); + outb(0x3D4, 0x0E); + outb(0x3D5, (uint8_t) ((pos >> 8) & 0xFF)); +} diff --git a/kernel/src/tty/cursor.h b/kernel/src/tty/cursor.h new file mode 100644 index 0000000..602d9cd --- /dev/null +++ b/kernel/src/tty/cursor.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +void cursor_enable(void); +void cursor_disable(void); +void cursor_setsize(uint8_t start, uint8_t end); +void cursor_setpos(uint8_t x, uint8_t y); + diff --git a/kernel/src/tty/term.c b/kernel/src/tty/term.c new file mode 100644 index 0000000..4f75788 --- /dev/null +++ b/kernel/src/tty/term.c @@ -0,0 +1,131 @@ + +#include +#include +#include +#include +#include + +#include "term.h" +#include "color.h" +#include "cursor.h" + +uint16_t *buffer; +uint8_t x, y; +uint8_t color; + +const uint16_t blank = (uint16_t) 0 | VGA_BLACK << 12 | VGA_WHITE << 8; + +static void term_clear_line(int y) { + if (y < 0 || y >= TERM_H) + return; + for (uint8_t x = 0; x < TERM_W; x++) { + const size_t index = y * TERM_W + x; + buffer[index] = blank; + } +} + +void term_init (void) { + x = 0; + y = 0; + buffer = (uint16_t*) 0xb8000; + term_setfg(VGA_WHITE); + term_setbg(VGA_BLACK); + term_clear(); +} + +void term_setpos(uint8_t xp, uint8_t yp) { + x = xp; + y = yp; + cursor_setpos(x, y); +} + +void term_scroll (int lines) { + int_disable(); + y -= lines; + if (!lines) return; + if(lines >= TERM_H || lines <= -TERM_H) { + term_clear(); + } else if(lines > 0) { + memmove(buffer, buffer + lines * TERM_W, 2 * (TERM_H - lines) * TERM_W); + term_clear_line(TERM_H - lines); + } else { + memmove(buffer + lines * TERM_W, buffer + lines, (TERM_H + lines) * TERM_W); + } + int_enable(); +} + +void term_setfg(enum VGAColor c) { + color = (color & 0xF0) | c; +} + +void term_setbg(enum VGAColor c) { + color = (color & 0x0F) | c << 4; +} + +void term_clear (void) { + for (uint8_t y = 0; y < TERM_H; y++) + term_clear_line(y); +} + +uint32_t term_save(void) { + uint32_t state = 0; + state |= (uint32_t) x << 16; + state |= (uint32_t) y << 8; + state |= (uint32_t) color << 0; + return state; +} + +void term_load(uint32_t state) { + x = (uint8_t) (state >> 16); + y = (uint8_t) (state >> 8); + color = (uint8_t) (state >> 0); + cursor_setpos(x, y); +} + +uint16_t term_save_col(void) { + return color; +} + +void term_load_col(uint16_t c) { + color = c; +} + +void putchar(int c) { + switch (c) { + case '\n': + x = 0; + y++; + break; + case '\t': + x += 4; + break; + case '\v': + case '\f': + y++; + break; + case '\r': + x = 0; + break; + default: { + const size_t index = y * TERM_W + x; + buffer[index] = c | (uint16_t) color << 8; + x++; + } + } + + if (x >= TERM_W) { + x = 0; + y++; + } + + if (y >= TERM_H) { + term_scroll(y - (TERM_H - 1)); + y = TERM_H - 1; + } + + cursor_setpos(x, y); +} + +bool term_newline(void) { + return x == 0; +} diff --git a/kernel/src/tty/term.h b/kernel/src/tty/term.h new file mode 100644 index 0000000..f6eb555 --- /dev/null +++ b/kernel/src/tty/term.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +#include "color.h" + +#define TERM_W 80 +#define TERM_H 25 + +void term_init(void); + +void term_reset(void); +void term_setfg(enum VGAColor color); +void term_setbg(enum VGAColor color); + +void term_clear(void); +void term_scroll(int lines); +void term_setpos(uint8_t x, uint8_t y); + +uint32_t term_save(void); +void term_load(uint32_t state); + +uint16_t term_save_col(void); +void term_load_col(uint16_t color); + +bool term_newline(void); -- cgit v1.2.3-freya