diff --git a/Makefile b/Makefile index 0e0cd75..c7a9046 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ export O?=$(abspath build) include conf/make/none.mk include conf/make/$(ARCH).mk -all: mkdirs _doc $(TARGETS) +all: mkdirs $(TARGETS) clean: @rm -rf $(O) diff --git a/conf/make/amd64.mk b/conf/make/amd64.mk index 7698578..77f0119 100644 --- a/conf/make/amd64.mk +++ b/conf/make/amd64.mk @@ -1,15 +1,31 @@ all: ### Kernel build -OBJS+=$(O)/arch/amd64/entry.o +DEFINES+=-DARCH_AMD64 +OBJS+=$(O)/arch/amd64/kernel.o \ + $(O)/arch/amd64/mm/pool.o \ + $(O)/arch/amd64/mm/mm.o \ + $(O)/arch/amd64/hw/rs232.o \ + $(O)/arch/amd64/hw/gdt.o \ + $(O)/arch/amd64/hw/gdt_s.o +kernel_OBJS=$(O)/arch/amd64/entry.o \ + $(OBJS) kernel_LINKER=$(S)/arch/amd64/link.ld kernel_LDFLAGS=-nostdlib -T$(kernel_LINKER) -kernel_CFLAGS=-ffreestanding -I$(S) +kernel_CFLAGS=-ffreestanding -I$(S) $(DEFINES) $(CFLAGS) +DIRS+=$(O)/arch/amd64/mm \ + $(O)/arch/amd64/hw -$(O)/kernel.elf: $(OBJS) $(kernel_LINKER) - $(CROSSLD) $(kernel_LDFLAGS) -o $@ $(OBJS) +$(O)/kernel.elf: $(kernel_OBJS) $(kernel_LINKER) + @printf " LD\t%s\n" $@ + @$(CROSSLD) $(kernel_LDFLAGS) -o $@ $(kernel_OBJS) -$(O)/arch/amd64/%.o: $(S)/arch/amd64/%.S - $(CROSSCC) $(kernel_CFLAGS) -c -o $@ $< +$(O)/%.o: $(S)/%.S + @printf " AS\t%s\n" $@ + @$(CROSSCC) $(kernel_CFLAGS) -c -o $@ $< + +$(O)/%.o: $(S)/%.c + @printf " CC\t%s\n" $@ + @$(CROSSCC) $(kernel_CFLAGS) -c -o $@ $< ### Kernel loader build TARGETS+=$(O)/loader.elf $(O)/kernel.elf diff --git a/conf/make/none.mk b/conf/make/none.mk index b27d7a0..70a8388 100644 --- a/conf/make/none.mk +++ b/conf/make/none.mk @@ -1,3 +1,11 @@ HEADERS=$(shell find $(S) -name "*.h") -OBJS+= +CFLAGS+=-Wall \ + -Wextra \ + -Werror \ + -Wno-unused-parameter + +DIRS+=$(O)/sys +OBJS+=$(O)/sys/mem.o \ + $(O)/sys/string.o \ + $(O)/sys/debug.o diff --git a/src/arch/amd64/entry.S b/src/arch/amd64/entry.S index d11e6fd..93beb2c 100644 --- a/src/arch/amd64/entry.S +++ b/src/arch/amd64/entry.S @@ -1,5 +1,17 @@ .section .text .global _start _start: + cli + movabsq $kernel_stack_top, %rsp + + call kernel_main + 1: + cli + hlt jmp 1b + +.section .bss +kernel_stack_bottom: + .skip 65536 +kernel_stack_top: diff --git a/src/arch/amd64/hw/gdt.c b/src/arch/amd64/hw/gdt.c new file mode 100644 index 0000000..625c175 --- /dev/null +++ b/src/arch/amd64/hw/gdt.c @@ -0,0 +1,44 @@ +#include "gdt.h" + +extern void amd64_reload_segs(void); + +#define AMD64_GDT_SIZE 3 + +#define GDT_ACC_AC (1 << 0) +#define GDT_ACC_RW (1 << 1) +#define GDT_ACC_DC (1 << 2) +#define GDT_ACC_EX (1 << 3) +#define GDT_ACC_S (1 << 4) +#define GDT_ACC_R0 (0 << 5) +#define GDT_ACC_R1 (1 << 5) +#define GDT_ACC_R2 (2 << 5) +#define GDT_ACC_R3 (3 << 5) +#define GDT_ACC_PR (1 << 7) + +#define GDT_FLG_LONG (1 << 5) +#define GDT_FLG_SZ (1 << 6) +#define GDT_FLG_GR (1 << 7) + +static amd64_gdt_entry_t gdt[AMD64_GDT_SIZE]; +static amd64_gdt_ptr_t gdtr; + +static void amd64_gdt_set(int idx, uint32_t base, uint32_t limit, uint8_t flags, uint8_t access) { + gdt[idx].base_lo = base & 0xFFFF; + gdt[idx].base_mi = (base >> 16) & 0xFF; + gdt[idx].base_hi = (base >> 24) & 0xFF; + gdt[idx].access = access; + gdt[idx].flags = (flags & 0xF0) | ((limit >> 16) & 0xF); + gdt[idx].limit_lo = limit & 0xFFFF; +} + +void amd64_gdt_init(void) { + gdtr.size = sizeof(gdt) - 1; + gdtr.offset = (uintptr_t) gdt; + + amd64_gdt_set(0, 0, 0, 0, 0); + amd64_gdt_set(1, 0, 0, GDT_FLG_LONG, GDT_ACC_PR | GDT_ACC_R0 | GDT_ACC_EX | GDT_ACC_S | GDT_ACC_RW); + amd64_gdt_set(2, 0, 0, 0, GDT_ACC_PR | GDT_ACC_R0 | GDT_ACC_S | GDT_ACC_RW); + + asm volatile ("lea gdtr(%rip), %rax; lgdt (%rax)"); + amd64_reload_segs(); +} diff --git a/src/arch/amd64/hw/gdt.h b/src/arch/amd64/hw/gdt.h new file mode 100644 index 0000000..d1913ed --- /dev/null +++ b/src/arch/amd64/hw/gdt.h @@ -0,0 +1,19 @@ +#pragma once +#include +#include + +typedef struct { + uint16_t limit_lo; + uint16_t base_lo; + uint8_t base_mi; + uint8_t access; + uint8_t flags; + uint8_t base_hi; +} amd64_gdt_entry_t; + +typedef struct { + uint16_t size; + uintptr_t offset; +} __attribute__((packed)) amd64_gdt_ptr_t; + +void amd64_gdt_init(void); diff --git a/src/arch/amd64/hw/gdt_s.S b/src/arch/amd64/hw/gdt_s.S new file mode 100644 index 0000000..ddafcef --- /dev/null +++ b/src/arch/amd64/hw/gdt_s.S @@ -0,0 +1,21 @@ +.section .text +.global amd64_reload_segs +amd64_reload_segs: + cli + + movabsq $.jmpbuf, %rax + rex.w ljmp *(%rax) + +.reload_data: + mov $0x10, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + mov %ax, %ss + + ret + +.jmpbuf: + .quad .reload_data + .word 0x08 diff --git a/src/arch/amd64/hw/io.h b/src/arch/amd64/hw/io.h new file mode 100644 index 0000000..a9d6c04 --- /dev/null +++ b/src/arch/amd64/hw/io.h @@ -0,0 +1,28 @@ +#pragma once +#include + +static inline void outb(uint16_t addr, uint8_t v) { + asm volatile("outb %0, %1"::"a"(v), "Nd"(addr)); +} + +static inline uint8_t inb(uint16_t addr) { + uint8_t v; + asm volatile("inb %1, %0":"=a"(v):"Nd"(addr)); + return v; +} + +static inline void outl(uint16_t addr, uint32_t v) { + asm volatile("outl %0, %1"::"a"(v), "Nd"(addr)); +} + +static inline uint32_t inl(uint16_t addr) { + uint32_t v; + asm volatile("inl %1, %0":"=a"(v):"Nd"(addr)); + return v; +} + +static inline void io_wait(void) { + asm volatile("jmp 1f\n\t" + "1:jmp 2f\n\t" + "2:" ); +} diff --git a/src/arch/amd64/hw/rs232.c b/src/arch/amd64/hw/rs232.c new file mode 100644 index 0000000..c929e25 --- /dev/null +++ b/src/arch/amd64/hw/rs232.c @@ -0,0 +1,11 @@ +#include "rs232.h" +#include "io.h" + +void rs232_init(uint16_t port) { + // Do nothing yet +} + +void rs232_send(uint16_t port, char v) { + while (!(inb(port + 5) & 0x20)) {} + outb(port, v); +} diff --git a/src/arch/amd64/hw/rs232.h b/src/arch/amd64/hw/rs232.h new file mode 100644 index 0000000..2b1591d --- /dev/null +++ b/src/arch/amd64/hw/rs232.h @@ -0,0 +1,8 @@ +#pragma once +#include + +#define RS232_COM0 0x3F8 + +void rs232_init(uint16_t port); +void rs232_send(uint16_t port, char c); +char rs232_recv(uint16_t port); diff --git a/src/arch/amd64/kernel.c b/src/arch/amd64/kernel.c new file mode 100644 index 0000000..7e0277a --- /dev/null +++ b/src/arch/amd64/kernel.c @@ -0,0 +1,11 @@ +#include "sys/mm.h" +#include "sys/debug.h" +#include "arch/amd64/hw/gdt.h" + +void kernel_main(void) { + kdebug("Booting\n"); + + // Memory management + amd64_mm_init(); + amd64_gdt_init(); +} diff --git a/src/arch/amd64/link.ld b/src/arch/amd64/link.ld index 1fa910b..de45f25 100644 --- a/src/arch/amd64/link.ld +++ b/src/arch/amd64/link.ld @@ -25,6 +25,7 @@ SECTIONS { .data ALIGN(4K) : AT(ADDR(.data) - _kernel_base) { *(.data) + *(.data.rel.local) } .bss ALIGN(4K) : AT(ADDR(.bss) - _kernel_base) diff --git a/src/arch/amd64/mm.h b/src/arch/amd64/mm.h index 551d99c..531d318 100644 --- a/src/arch/amd64/mm.h +++ b/src/arch/amd64/mm.h @@ -3,6 +3,10 @@ /// The place where the kernel pages are virtually mapped to #define KERNEL_VIRT_BASE 0xFFFFFF0000000000 +/// amd64 standard states that addresses' upper bits are copies of the 47th bit, so these need to be +/// stripped down to only 48 bits +#define AMD64_MM_MASK(a) ((uintptr_t) (a) & 0xFFFFFFFFFFFF) + /// Page map level 4 typedef uint64_t *mm_pml4_t; /// Page directory pointer table @@ -14,3 +18,5 @@ typedef uint64_t *mm_pagetab_t; /// Virtual memory space typedef mm_pml4_t mm_space_t; + +void amd64_mm_init(void); diff --git a/src/arch/amd64/mm/mm.c b/src/arch/amd64/mm/mm.c new file mode 100644 index 0000000..e956643 --- /dev/null +++ b/src/arch/amd64/mm/mm.c @@ -0,0 +1,28 @@ +#include "sys/mem.h" +#include "sys/debug.h" +#include "sys/mm.h" + +mm_space_t mm_kernel; + +void amd64_mm_init(void) { + kdebug("Memory manager init\n"); + + // Physical memory at this point: + // 0x000000 - 0x400000 - Used by kernel and info passed from the loader + // Virtual memory: + // Lower 1GiB mapped to itself for loader access + // 0xFFFFFF0000000000 (1GiB) is mapped to 0 for kernel access + + // XXX: assuming nothing important is there + uint64_t *pml4 = (uint64_t *) (0x200000 - 0x1000); + uint64_t *pdpt = (uint64_t *) (0x200000 - 0x2000); + + memset((void *) (0x200000 - 2 * 0x1000), 0, 0x1000 * 2); + + // 0xFFFFFF0000000000 -> 0 (1GiB) mapping + pml4[AMD64_MM_MASK(KERNEL_VIRT_BASE) >> 39] = (uint64_t) pdpt | 1 | 2; + pdpt[(AMD64_MM_MASK(KERNEL_VIRT_BASE) >> 30) & 0x1FF] = 1 | 2 | (1 << 7); + + // Load the new table + asm volatile ("mov %0, %%cr3"::"a"(pml4):"memory"); +} diff --git a/src/arch/amd64/mm/pool.c b/src/arch/amd64/mm/pool.c new file mode 100644 index 0000000..68c4239 --- /dev/null +++ b/src/arch/amd64/mm/pool.c @@ -0,0 +1,14 @@ +#include "pool.h" +#include "sys/mem.h" + +static struct { + uint64_t track[512]; + uintptr_t start; + size_t size; +} amd64_mm_pool; + +void amd64_mm_pool_init(uintptr_t begin, size_t size) { + amd64_mm_pool.start = begin; + amd64_mm_pool.size = size; + memset(amd64_mm_pool.track, 0, 512 * sizeof(uint64_t)); +} diff --git a/src/arch/amd64/mm/pool.h b/src/arch/amd64/mm/pool.h new file mode 100644 index 0000000..9108072 --- /dev/null +++ b/src/arch/amd64/mm/pool.h @@ -0,0 +1,12 @@ +#pragma once +#include +#include + +/// Initialize the paging structure allocation pool +void amd64_mm_pool_init(uintptr_t base, size_t size); + +/// Allocate a paging structure +uint64_t *amd64_mm_pool_alloc(void); + +/// Free a paging structure +void amd64_mm_pool_free(uint64_t *obj); diff --git a/src/sys/attr.h b/src/sys/attr.h new file mode 100644 index 0000000..d0ba21d --- /dev/null +++ b/src/sys/attr.h @@ -0,0 +1,3 @@ +#pragma once + +#define __weak __attribute__((weak)) diff --git a/src/sys/debug.c b/src/sys/debug.c new file mode 100644 index 0000000..298c8b2 --- /dev/null +++ b/src/sys/debug.c @@ -0,0 +1,254 @@ +#include "debug.h" +#include "string.h" +#include +#include "sys/attr.h" + +#if defined(ARCH_AMD64) +#include "arch/amd64/hw/rs232.h" +#endif + +static const char *s_debug_xs_set0 = "0123456789abcdef"; +static const char *s_debug_xs_set1 = "0123456789ABCDEF"; + +// TODO: make debugc a __weak function of a character +void debugc(int level, char c) { +#if defined(ARCH_AMD64) + rs232_send(RS232_COM0, c); +#endif +} + +void debugs(int level, const char *s) { + char c; + while ((c = *(s++))) { + debugc(level, c); + } +} + +static void debugspl(int level, const char *s, char p, size_t c) { + size_t l = strlen(s); + for (size_t i = l; i < c; ++i) { + debugc(level, p); + } + debugs(level, s); +} + +static void debugspr(int level, const char *s, char p, size_t c) { + size_t l = strlen(s); + debugs(level, s); + for (size_t i = l; i < c; ++i) { + debugc(level, p); + } +} + +static void debugsp(int level, const char *s, char padc, int padl) { + if (padl > 0) { + debugspl(level, s, padc, padl); + } else { + debugspr(level, s, padc, -padl); + } +} + +void debug_ds(int64_t x, char *res, int s, int sz) { + if (!x) { + res[0] = '0'; + res[1] = 0; + return; + } + + int c; + uint64_t v; + + if (sz) { + if (s && x < 0) { + v = (uint64_t) -x; + } else { + s = 0; + v = (uint64_t) x; + } + } else { + if (s && ((int32_t) x) < 0) { + v = (uint64_t) -((int32_t) x); + } else { + s = 0; + v = (uint64_t) x; + } + } + + c = 0; + + while (v) { + res[c++] = '0' + v % 10; + v /= 10; + } + + if (s) { + res[c++] = '-'; + } + + res[c] = 0; + + for (int i = 0, j = c - 1; i < j; ++i, --j) { + res[i] ^= res[j]; + res[j] ^= res[i]; + res[i] ^= res[j]; + } +} + +void debug_xs(uint64_t v, char *res, const char *set) { + if (!v) { + res[0] = '0'; + res[1] = 0; + return; + } + + int c = 0; + + while (v) { + res[c++] = set[v & 0xF]; + v >>= 4; + } + + res[c] = 0; + + for (int i = 0, j = c - 1; i < j; ++i, --j) { + res[i] ^= res[j]; + res[j] ^= res[i]; + res[i] ^= res[j]; + } +} + +void debugf(int level, const char *f, ...) { + va_list args; + va_start(args, f); + debugfv(level, f, args); + va_end(args); +} + +void debugfv(int level, const char *fmt, va_list args) { + char c; + union { + const char *v_string; + char v_char; + int32_t v_int32; + uint32_t v_uint32; + int64_t v_int64; + uint64_t v_uint64; + uintptr_t v_ptr; + } value; + char buf[64]; + char padc; + int padn; + int padd; + + while ((c = *fmt)) { + switch (c) { + case '%': + c = *(++fmt); + + padc = ' '; + padd = 1; + padn = 0; + if (c == '0') { + padc = c; + } + + if (c == '-') { + padd = -1; + c = *(++fmt); + } + + while (c >= '0' && c <= '9') { + padn *= 10; + padn += padd * (int) (c - '0'); + c = *(++fmt); + } + + switch (c) { + case 'l': + c = *(++fmt); + switch (c) { + case 'd': + value.v_int64 = va_arg(args, int64_t); + debug_ds(value.v_int64, buf, 1, 1); + debugsp(level, buf, padc, padn); + break; + case 'u': + value.v_uint64 = va_arg(args, uint64_t); + debug_ds(value.v_uint64, buf, 0, 1); + debugsp(level, buf, padc, padn); + break; + case 'x': + value.v_uint64 = va_arg(args, uint64_t); + debug_xs(value.v_uint64, buf, s_debug_xs_set0); + debugsp(level, buf, padc, padn); + break; + case 'X': + value.v_uint64 = va_arg(args, uint64_t); + debug_xs(value.v_uint64, buf, s_debug_xs_set1); + debugsp(level, buf, padc, padn); + break; + case 'p': + value.v_uint64 = va_arg(args, uint64_t); + debugc(level, '0'); + debugc(level, 'x'); + debug_xs(value.v_uint64, buf, s_debug_xs_set0); + debugspl(level, buf, '0', sizeof(uint64_t) * 2); + break; + default: + debugc(level, '%'); + debugc(level, 'l'); + debugc(level, c); + break; + } + break; + case 'c': + // char is promoted to int + value.v_char = va_arg(args, int); + debugc(level, value.v_char); + break; + case 'd': + value.v_int64 = va_arg(args, int32_t); + debug_ds(value.v_int64 & 0xFFFFFFFF, buf, 1, 0); + debugsp(level, buf, padc, padn); + break; + case 'u': + value.v_uint64 = va_arg(args, uint32_t); + debug_ds(value.v_uint64 & 0xFFFFFFFF, buf, 0, 0); + debugsp(level, buf, padc, padn); + break; + case 'x': + value.v_uint64 = va_arg(args, uint32_t); + debug_xs(value.v_uint64 & 0xFFFFFFFF, buf, s_debug_xs_set0); + debugsp(level, buf, padc, padn); + break; + case 'X': + value.v_uint64 = va_arg(args, uint32_t); + debug_xs(value.v_uint64 & 0xFFFFFFFF, buf, s_debug_xs_set1); + debugsp(level, buf, padc, padn); + break; + case 'p': + value.v_ptr = va_arg(args, uintptr_t); + debugc(level, '0'); + debugc(level, 'x'); + debug_xs(value.v_ptr, buf, s_debug_xs_set0); + debugspl(level, buf, '0', sizeof(uintptr_t) * 2); + break; + case 's': + value.v_string = va_arg(args, const char *); + debugsp(level, value.v_string ? value.v_string : "(null)", padc, padn); + break; + default: + debugc(level, '%'); + debugc(level, c); + break; + } + break; + default: + debugc(level, c); + break; + } + + ++fmt; + } +} + diff --git a/src/sys/debug.h b/src/sys/debug.h new file mode 100644 index 0000000..d5ca6cc --- /dev/null +++ b/src/sys/debug.h @@ -0,0 +1,31 @@ +#pragma once +#include +#include +#include + +#define DEBUG_DEFAULT 0 +#define DEBUG_INFO 1 +#define DEBUG_WARN 2 +#define DEBUG_ERROR 3 +#define DEBUG_FATAL 4 + +#define DEBUG_OUT_SERIAL 0 +#define DEBUG_OUT_DISP 1 + +#define kdebug(f, ...) debugf(DEBUG_DEFAULT, "[%s] " f, __func__, ##__VA_ARGS__) +#define kinfo(f, ...) debugf(DEBUG_INFO, "[%s] " f, __func__, ##__VA_ARGS__) +#define kwarn(f, ...) debugf(DEBUG_WARN, "[%s] " f, __func__, ##__VA_ARGS__) +#define kerror(f, ...) debugf(DEBUG_ERROR, "[%s] " f, __func__, ##__VA_ARGS__) +#define kfatal(f, ...) debugf(DEBUG_FATAL, "[%s] " f, __func__, ##__VA_ARGS__) +#define kprint(l, f, ...) debugf(l, "[%s] " f, __func__, ##__VA_ARGS__) + +void debugc(int level, char c); +void debugs(int level, const char *s); + +void debug_xs(uint64_t v, char *res, const char *set); +void debug_ds(int64_t x, char *res, int s, int sz); +void debug_init(void); +void debugf(int level, const char *fmt, ...); +void debugfv(int level, const char *fmt, va_list args); + +void debug_dump(int level, const void *block, size_t count); diff --git a/src/sys/mem.c b/src/sys/mem.c new file mode 100644 index 0000000..fe6dad0 --- /dev/null +++ b/src/sys/mem.c @@ -0,0 +1,8 @@ +#include "mem.h" + +void *memset(void *blk, int v, size_t sz) { + for (size_t i = 0; i < sz; ++i) { + ((char *) blk)[i] = v; + } + return blk; +} diff --git a/src/sys/mem.h b/src/sys/mem.h new file mode 100644 index 0000000..5a0580f --- /dev/null +++ b/src/sys/mem.h @@ -0,0 +1,5 @@ +#pragma once +#include +#include + +void *memset(void *blk, int v, size_t sz); diff --git a/src/sys/string.c b/src/sys/string.c new file mode 100644 index 0000000..bbedab2 --- /dev/null +++ b/src/sys/string.c @@ -0,0 +1,9 @@ +#include "string.h" + +size_t strlen(const char *a) { + size_t s = 0; + while (*a++) { + ++s; + } + return s; +} diff --git a/src/sys/string.h b/src/sys/string.h new file mode 100644 index 0000000..1679ac4 --- /dev/null +++ b/src/sys/string.h @@ -0,0 +1,4 @@ +#pragma once +#include + +size_t strlen(const char *s);