diff options
| author | Ada Christine <adachristine18@gmail.com> | 2021-12-19 22:41:15 +0000 |
|---|---|---|
| committer | Ada Christine <adachristine18@gmail.com> | 2021-12-19 22:41:15 +0000 |
| commit | e303a03e7c96bc5bf7fa93f7b46ce63196893d7a (patch) | |
| tree | f187b374feec85015d6df1f94db618a3f5cc893f /kc/core | |
| parent | bff63a4337eb3388617d230970c5eb2684d6a215 (diff) | |
moving files around
Diffstat (limited to 'kc/core')
| -rw-r--r-- | kc/core/Makefile | 39 | ||||
| -rw-r--r-- | kc/core/cpu.c | 225 | ||||
| -rw-r--r-- | kc/core/cpu.h | 9 | ||||
| -rw-r--r-- | kc/core/exceptions.S | 54 | ||||
| -rw-r--r-- | kc/core/exceptions.h | 7 | ||||
| -rw-r--r-- | kc/core/kc_main.c | 69 | ||||
| -rw-r--r-- | kc/core/kprint.c | 18 | ||||
| -rw-r--r-- | kc/core/kprint.h | 5 | ||||
| -rw-r--r-- | kc/core/main.c | 4 | ||||
| -rw-r--r-- | kc/core/memory.c | 534 | ||||
| -rw-r--r-- | kc/core/memory.h | 53 | ||||
| -rw-r--r-- | kc/core/panic.c | 25 | ||||
| -rw-r--r-- | kc/core/panic.h | 13 | ||||
| -rw-r--r-- | kc/core/port.c | 14 | ||||
| -rw-r--r-- | kc/core/port.h | 5 | ||||
| -rw-r--r-- | kc/core/serial.c | 41 | ||||
| -rw-r--r-- | kc/core/serial.h | 5 |
17 files changed, 1120 insertions, 0 deletions
diff --git a/kc/core/Makefile b/kc/core/Makefile new file mode 100644 index 0000000..2893b42 --- /dev/null +++ b/kc/core/Makefile @@ -0,0 +1,39 @@ +include ../../defaults.mk + +VPATH := ../../lib ../ + +TARGET := kernel.os +.DEFAULT: $(TARGET) + +CPPFLAGS += -I../../api +CFLAGS += -Wno-unused-const-variable -Wno-unused-function -fvisibility=hidden -g + +LIBDIRS += -L$(dir $(shell $(CC) -print-libgcc-file-name)) +LDSCRIPT := ../component.ld +LDFLAGS += -z max-page-size=4096 --export-dynamic -pic --no-dynamic-linker +LOADLIBES := -lgcc + +SOBJS := +GOBJS := entry_x86_64.o reloc_x86_64.o kprint.o serial.o port.o \ + kc_main.o memory.o memset.o memcpy.o memmove.o memcmp.o cpu.o \ + exceptions.o panic.o +IOBJS := + +# __attribute__((interrupt)) requires -mgeneral-regs-only +$(IOBJS): CFLAGS += -mgeneral-regs-only +$(IOBJS) $(GOBJS): CFLAGS += -mcmodel=small -fPIC + +OBJS := $(SOBJS) $(GOBJS) $(IOBJS) +DEPS := $(patsubst %.o,%.d,$(OBJS)) + +$(TARGET): $(LDSCRIPT) $(MAKEFILE_LIST) +$(TARGET): $(OBJS) + +CLEANLIST := $(wildcard $(OBJS) $(TARGET)) +DCLEANLIST := $(wildcard $(DEPS)) + +include ../../rules.mk +-include $(DEPS) + +.DEFAULT: $(TARGET) + diff --git a/kc/core/cpu.c b/kc/core/cpu.c new file mode 100644 index 0000000..ed1a04e --- /dev/null +++ b/kc/core/cpu.c @@ -0,0 +1,225 @@ +#include "cpu.h" +#include "exceptions.h" + +#include <stdint.h> + +#define GDT_ENTRIES 16 +#define IDT_ENTRIES 256 + +#define CODE_SEG_INDEX 1 +#define DATA_SEG_INDEX 2 +#define TASK_SEG_INDEX 3 + +struct dtr64 +{ + uint16_t limit; + uint64_t base; +} __attribute__((packed)); + +struct segment_descriptor +{ + uint16_t limit0; + uint16_t base0; + uint8_t base16; + uint8_t access; + uint8_t flags_limit16; + uint8_t base24; +} __attribute__((packed)); + +enum segment_descriptor_type +{ + CODE64_SEG, + DATA_SEG, + TASK64_SEG, +}; + +struct gate_descriptor +{ + uint16_t offset0; + uint16_t selector; + uint8_t ist; + uint8_t access; + uint16_t offset16; + uint64_t offset32; +} __attribute__((packed)); + +enum gate_descriptor_type +{ + INT64_GATE, + TRAP64_GATE, +}; + +struct task64_segment +{ + uint32_t reserved0; + uint64_t rsp0; + uint64_t rsp1; + uint64_t rsp2; + uint64_t reserved1; + uint64_t ist[7]; + uint16_t reserved2; + uint16_t iomap_base; +}; + +static struct segment_descriptor gdt[GDT_ENTRIES]; +static struct gate_descriptor idt[IDT_ENTRIES]; +static struct task64_segment tss; + +static void set_gdt(int index, + uint64_t base, + uint64_t limit, + enum segment_descriptor_type type) +{ + gdt[index].limit0 = limit & 0xffff; + gdt[index].base0 = base & 0xffff; + gdt[index].base16 = (base >> 16) & 0xff; + gdt[index].flags_limit16 = (limit >> 16) & 0xf; + gdt[index].base24 = (base >> 24) & 0xff; + + switch (type) + { + case CODE64_SEG: + gdt[index].access = 0x9a; + gdt[index].flags_limit16 |= 0xa0; + break; + case DATA_SEG: + gdt[index].access = 0x92; + gdt[index].flags_limit16 |= 0xc0; + break; + case TASK64_SEG: + gdt[index].access = 0x89; + //TODO: make this cleaner i don't like it + gdt[index+1].limit0 = (base >> 32) & 0xffff; + gdt[index+1].base0 = (base >> 48) & 0xffff; + break; + }; +} + +static void set_idt(int vec, + uint16_t selector, + uint64_t offset, + enum gate_descriptor_type type) +{ + idt[vec].offset0 = offset & 0xffff; + idt[vec].selector = selector; + idt[vec].offset16 = (offset >> 16) & 0xffff; + idt[vec].offset32 = (offset >> 32) & 0xffffffff; + + switch (type) + { + case INT64_GATE: + idt[vec].access = 0x8e; + break; + case TRAP64_GATE: + idt[vec].access = 0x8f; + break; + } +} + +void set_ist(int vec, int ist_index) +{ + // non-destructively set the ist index. + idt[vec].access |= ist_index & 0x7; +} + +static void gdt_init(void) +{ + set_gdt(CODE_SEG_INDEX, 0, (uint64_t)-1, CODE64_SEG); + set_gdt(DATA_SEG_INDEX, 0, (uint64_t)-1, DATA_SEG); + set_gdt(TASK_SEG_INDEX, (uint64_t)&tss, sizeof(tss) - 1, TASK64_SEG); + + struct dtr64 gdtr = {sizeof(gdt) - 1, (uint64_t)&gdt}; + + __asm__ volatile + ( + // can't have interrupts during GDT reload + // but don't re-enable interrupts if they were already disabled + "pushf\n" + "cli\n" + "lgdt %0\n" + "mov %w2, %%ds\n" + "mov %w2, %%es\n" + "mov %w2, %%fs\n" + "mov %w2, %%gs\n" + "mov %w2, %%ss\n" + // make a far return frame on the stack + // [ss] @rsp+8 + // [rip] @rsp + "pushq %1\n" + "lea .Lflush(%%rip), %%rax\n" + "push %%rax\n" + // far return, now we're in the code segment + // defined here. + "lretq\n" + ".Lflush:\n" + "ltr %w3\n" + "popf\n" + // dependency on pre-boot GDT is now gone + : + : "m"(gdtr), + "i"(CODE_SEG_INDEX << 3), + "r"(DATA_SEG_INDEX << 3), + "r"(TASK_SEG_INDEX << 3) + ); +} + +static void idt_init(void) +{ + struct dtr64 idtr = {sizeof(idt) - 1, (uint64_t)&idt}; + + __asm__ volatile + ( + // can't have interrupts enabled during IDT reload--obviously. + // as with GDT don't re-enable interrupts if they were already + // disabled. + "pushf\n" + "cli\n" + "lidt %0\n" + "popf\n" + // much more straightforward than the gdt code. + :: "m"(idtr) + ); +} + +void cpu_init(void) +{ + gdt_init(); + idt_init(); + exceptions_init(); +} + +void exceptions_init(void) +{ + isr_install(PAGE_FAULT_VECTOR, &page_fault_isr, 0, 0); +} + +void isr_install(int vec, void (*isr)(void), int trap, int ist) +{ + enum gate_descriptor_type type = INT64_GATE; + + if (trap) + { + type = TRAP64_GATE; + } + + set_idt(vec, CODE_SEG_INDEX << 3, (uint64_t)isr, type); + if (ist) + { + set_ist(vec, ist); + } +} + +void ist_install(int ist, void *stack) +{ + tss.ist[ist] = (uint64_t)stack; +} + +void int_enable(void) +{ + __asm__ ("sti"); +} + +void int_disable(void) +{ + __asm__ ("cli"); +} diff --git a/kc/core/cpu.h b/kc/core/cpu.h new file mode 100644 index 0000000..56a4073 --- /dev/null +++ b/kc/core/cpu.h @@ -0,0 +1,9 @@ +#pragma once + +void cpu_init(void); + +void isr_install(int vec, void (*isr)(void), int ist, int trap); +void ist_install(int ist, void *stack); + +void int_enable(void); +void int_disable(void); diff --git a/kc/core/exceptions.S b/kc/core/exceptions.S new file mode 100644 index 0000000..80d49ee --- /dev/null +++ b/kc/core/exceptions.S @@ -0,0 +1,54 @@ +.extern page_fault_handler + +.global page_fault_isr +.type page_fault_isr, @function +page_fault_isr: + /* just in case something tried to be clever before */ + cld + /* push param registers */ + push %rdi + push %rsi + push %rdx + push %rcx + push %r8 + push %r9 + /* collect parameters for page fault handler */ + mov 48(%rsp), %rdi /* error code is first parameter */ + mov %cr2, %rsi /* PFLA is next parameter */ + /* caller-saved registers */ + push %rax + push %r10 + push %r11 + /* callee-saved registers */ + push %rbx + push %rbp + push %r12 + push %r13 + push %r14 + push %r15 + /* stack has to be aligned */ + mov %rsp, %rbp + and $-16, %rsp + call page_fault_handler + mov %rbp, %rsp + /* TODO: test for error code here and panic() */ + pop %r15 + pop %r14 + pop %r13 + pop %r12 + pop %rbp + pop %rbx + pop %r11 + pop %r10 + pop %rax + pop %r9 + pop %r8 + pop %rcx + pop %rdx + pop %rsi + pop %rdi + /* unwind the error code from the stack */ + add $8, %rsp + /* leave the isr */ + iretq +.size page_fault_isr, . - page_fault_isr diff --git a/kc/core/exceptions.h b/kc/core/exceptions.h new file mode 100644 index 0000000..5d22284 --- /dev/null +++ b/kc/core/exceptions.h @@ -0,0 +1,7 @@ +#pragma once + +extern void page_fault_isr(void); + +#define PAGE_FAULT_VECTOR 0xe + +void exceptions_init(void); diff --git a/kc/core/kc_main.c b/kc/core/kc_main.c new file mode 100644 index 0000000..5eecaf2 --- /dev/null +++ b/kc/core/kc_main.c @@ -0,0 +1,69 @@ +#include "cpu.h" +#include "serial.h" +#include "kprint.h" +#include "memory.h" +#include "panic.h" + +#include <loader/data_efi.h> + +#include <kernel/entry.h> + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <stdnoreturn.h> + +static void efi_memory_init(struct efi_memory_map_data *map) +{ + // FIXME: bad girl shit: overwriting on aliased pointers. + // this is a temporary solution to shortcomings with + // loader boot data. it's sort-of OK because the memory_range struct + // is smaller than EFI_MEMORY_DESCRIPTOR. it's still not good. + struct memory_range *ranges = (struct memory_range *)map->data; + int entries = map->size / map->descsize; + + for (int i = 0; i < entries; i++) + { + EFI_MEMORY_DESCRIPTOR *desc; + desc = (EFI_MEMORY_DESCRIPTOR *)((char *)map->data + i * map->descsize); + + enum memory_range_type type; + phys_addr_t base; + size_t size; + + base = desc->PhysicalStart; + size = desc->NumberOfPages * EFI_PAGE_SIZE; + + switch (desc->Type) + { + case EfiConventionalMemory: + case EfiBootServicesData: + case EfiBootServicesCode: + case EfiLoaderData: + case EfiLoaderCode: + type = AVAILABLE_MEMORY; + break; + case SystemMemoryType: + type = SYSTEM_MEMORY; + break; + default: + type = RESERVED_MEMORY; + } + + ranges[i].type = type; + ranges[i].base = base; + ranges[i].size = size; + } + + memory_init(ranges, entries); +} + +noreturn void kernel_entry(struct efi_boot_data *data) +{ + (void)data; + cpu_init(); + serial_init(); + kputs("<hacker voice> i'm in\r\n"); + efi_memory_init(data->memory_map); + halt(); +} diff --git a/kc/core/kprint.c b/kc/core/kprint.c new file mode 100644 index 0000000..0a15efd --- /dev/null +++ b/kc/core/kprint.c @@ -0,0 +1,18 @@ +#include "kprint.h" +#include "serial.h" + +int kputchar(int c) +{ + return serial_putchar(c); +} + +int kputs(const char *s) +{ + while (*s) + { + kputchar(*s++); + } + + return 0; +} + diff --git a/kc/core/kprint.h b/kc/core/kprint.h new file mode 100644 index 0000000..5b307a2 --- /dev/null +++ b/kc/core/kprint.h @@ -0,0 +1,5 @@ +#pragma once + +int kputchar(int c); +int kputs(const char *s); + diff --git a/kc/core/main.c b/kc/core/main.c new file mode 100644 index 0000000..2fb2e3e --- /dev/null +++ b/kc/core/main.c @@ -0,0 +1,4 @@ +void kernel_main(void) +{ +} + diff --git a/kc/core/memory.c b/kc/core/memory.c new file mode 100644 index 0000000..62f3a38 --- /dev/null +++ b/kc/core/memory.c @@ -0,0 +1,534 @@ +#include "memory.h" +#include "kprint.h" +#include "panic.h" + +#include <stdint.h> + +#include <kernel/entry.h> +#include <kernel/memory/paging.h> + +/* kernel virtual space guarantees + * + * the loader must set up the address space as follows + * 1. kernel virtual space is 2GiB in size on 2GiB alignment. + * 2. mappings begin at +0x7fc00000. this mapping space is sparse and its + * own mapping tables begin at +0x7fffe000. + */ + +#define kpm1_index(v) (((uint64_t)v >> pte_index_bits(1)) & 0x7ffff) +#define kpm2_index(v) (((uint64_t)v >> pte_index_bits(2)) & 0x3ff) + +#define align_next(v, a) (((uint64_t)v + a - 1) & ~(a - 1)) + +// a conservative 128MB for kernel heap +#define KERNEL_HEAP_SPACE_SIZE (128 << 20) + +typedef int (*page_fault_handler_func)(uint32_t code, void *address); + +enum memory_space_flags +{ + SYSTEM_MEMORY_SPACE, // memory that cannot fault + ANONYMOUS_MEMORY_SPACE, // memory that can fault +}; + +struct memory_space +{ + enum memory_space_flags flags; + page_fault_handler_func handler; + void *base; + void *head; + struct memory_space *next; + struct memory_space *prev; +}; + +struct page +{ + uint32_t next: 31; + uint32_t used: 1; +}; + +extern char k_virt_base; +extern char k_text_begin; +extern char k_text_end; +extern char k_data_begin; +extern char k_data_end; + +static struct page *const page_array = (struct page *)0xffffffd800000000; +static int first_free_page_index = -1; +static size_t page_array_entries = 0; +static size_t free_pages = 0; + +// the temporary mapping place. never use this permanently. +static void *const temp = (void *)0xffffffffffa00000; +static uint64_t *const kernel_pm1 = (uint64_t *)0xffffffffffc00000; +static uint64_t *const kernel_pm2 = (uint64_t *)0xffffffffffffe000; + +static uint64_t *get_kernel_pm1e(void *vaddr); +static uint64_t *get_kernel_pm2e(void *vaddr); +static void *get_virtual_page(enum page_map_flags flags); +static void *page_map_at(void *vaddr, phys_addr_t paddr, enum page_map_flags flags); + +// handlers for memory space types +int anonymous_page_handler(uint32_t code, void *address); + +// the system memory_space's that are always present +static struct memory_space kernel_image_space; +static struct memory_space kernel_stack_space; +static struct memory_space kernel_pagemap_space; +// the kernel's own heap space +static struct memory_space kernel_heap_space; + +// NULL if the system address spaces are not set up +static struct memory_space *root_memory_space; + +static struct memory_range init_grab_pages(struct memory_range *ranges, + int count, + size_t size) +{ + struct memory_range request = {SYSTEM_MEMORY, + 0, + align_next(size, PAGE_SIZE)}; + + for (int i = 0; i < count; i++) + { + if (ranges[i].type != AVAILABLE_MEMORY || + ranges[i].base < (1 << 20)) // leave pages below 1MiB alone. + { + continue; + } + else if (ranges[i].size > request.size) + { + request.base = ranges[i].base; + ranges[i].base += request.size; + ranges[i].size -= request.size; + return request; + } + } + + request.type = INVALID_MEMORY; + return request; +} + +static size_t init_get_max_paddr(struct memory_range *ranges, int count) +{ + phys_addr_t max_paddr = 0; + + // get the highest usable physical address in all memory ranges. + for (int i = 0; i < count; i++) + { + if (ranges[i].type != AVAILABLE_MEMORY) + { + continue; + } + + if ((ranges[i].base + ranges[i].size - 1) > max_paddr) + { + max_paddr = ranges[i].base + ranges[i].size - 1; + } + } + + return max_paddr; +} + +static phys_addr_t get_kernel_pm4_phys(void) +{ + phys_addr_t pm4_phys; + __asm__ ( + "mov %%cr3, %0\n\t" + : "=r"(pm4_phys) + ); + + return page_address(pm4_phys, 1); +} + +static phys_addr_t get_kernel_pm3_phys(void) +{ + phys_addr_t pm3_phys; + // make a temporary mapping to read pm4 + uint64_t *pm4 = page_map_at(temp, + get_kernel_pm4_phys(), + CONTENT_RODATA|SIZE_2M); + + // pm3_phys is in pm4. + pm3_phys = page_address(pm4[pte_index(&k_virt_base, 4)], 1); + // never leave a temporary mapping + page_unmap(pm4); + + return pm3_phys; +} + +static void init_map_page_array_pm2(struct memory_range *pm2_pages) +{ + // now we need to write the physical address of each pm2 page for the + // page_array in the kernel's pm3. + uint64_t *pm3 = page_map_at(temp, + get_kernel_pm3_phys(), + CONTENT_RWDATA|SIZE_2M); + + // the first index is NOT zero! + for (size_t i = 0; i < (pm2_pages->size / PAGE_SIZE); i++) + { + pm3[pte_index(page_array, 3) + i] = (pm2_pages->base + i * + PAGE_SIZE)|PAGE_NX|PAGE_WR|PAGE_PR; + } + // never forget to unmap temporary mappings. + page_unmap(pm3); +} + +static void init_create_page_array_map(struct memory_range *pages, + struct memory_range *maps) +{ + // indices for page_array always start at 0 + size_t entry_count = pages->size / PAGE_SIZE; + + // every 512 entries we need to re-map and clean. + for (size_t i = 0; i < entry_count; i += PAGE_TABLE_INDEX_MASK) + { + uint64_t *pm1 = page_map_at(temp, + maps->base + i * PAGE_SIZE, + CONTENT_RWDATA|SIZE_2M); + memset(pm1, 0, PAGE_SIZE); + for (size_t j = 0; + (j < PAGE_TABLE_INDEX_MASK) && ((j + i) < entry_count); + j++) + { + pm1[j] = (pages->base + (i + j) * PAGE_SIZE)| + PAGE_NX|PAGE_WR|PAGE_PR; + } + + page_unmap(pm1); + } +} + +static void init_set_memory_range(struct memory_range *range) +{ + for (size_t i = 0; i < range->size / PAGE_SIZE; i++) + { + if (range->base / PAGE_SIZE + i > page_array_entries) + { + return; + } + + if (range->type == AVAILABLE_MEMORY) + { + page_free(range->base + PAGE_SIZE * i); + } + else + { + struct page *page = &page_array[range->base / PAGE_SIZE + i]; + page->used = 1; + } + } +} + +static void init_populate_page_array(struct memory_range *ranges, int count) +{ + for (int i = 0; i < count; i++) + { + init_set_memory_range(&ranges[i]); + } +} + +static void init_create_page_array(struct memory_range *ranges, int count) +{ + // the highest actual physical memory address. + size_t max_paddr = init_get_max_paddr(ranges, count); + page_array_entries = max_paddr / page_size(1); + size_t page_array_size = page_array_entries * sizeof(struct page); + + // the physical pages that will contain page_array + struct memory_range pa_pages = init_grab_pages(ranges, count, page_array_size); + + // the number of page tables needed to map pa_pages + // this will be 1 on systems with less than 2GB of memory. + size_t pm1_count = page_count(pa_pages.size, 2); + struct memory_range pm1_pages = init_grab_pages(ranges, + count, + pm1_count * page_size(1)); + + init_create_page_array_map(&pa_pages, &pm1_pages); + + // the number of page directories needed to map pa_pages + // this will be 1 on systems with less than 1TB of memory. + // who the fuck has 1TB of memory lol + size_t pm2_count = page_count(pa_pages.size, 3); + struct memory_range pm2_pages = init_grab_pages(ranges, + count, + pm2_count * page_size(1)); + + init_create_page_array_map(&pm1_pages, &pm2_pages); + + // ok here goes + init_map_page_array_pm2(&pm2_pages); + + memset(page_array, 0, page_array_size); + + init_populate_page_array(ranges, count); + init_populate_page_array(&pa_pages, 1); + init_populate_page_array(&pm1_pages, 1); + init_populate_page_array(&pm2_pages, 1); +} + +static struct memory_space init_system_space(void *base, void *head) +{ + struct memory_space space = {SYSTEM_MEMORY_SPACE, NULL, base, head, NULL, NULL}; + return space; +} + +static struct memory_space init_anonymous_space(void *base, void *head) +{ + struct memory_space space = {ANONYMOUS_MEMORY_SPACE, + &anonymous_page_handler, + base, + head, + NULL, + NULL}; + + return space; +} + +void memory_init(struct memory_range *ranges, int count) +{ + init_create_page_array(ranges, count); + // initialize always-present memory spaces for the vmm + kernel_image_space = init_system_space((void *)&k_text_begin, + (void *)align_next(&k_data_end, PAGE_SIZE)); + kernel_stack_space = init_system_space((void *)KERNEL_ENTRY_STACK_BASE, + (void *)KERNEL_ENTRY_STACK_HEAD); + kernel_pagemap_space = init_system_space(kernel_pm1, (void *)-1LL); + + // initialize the kernel heap space + kernel_heap_space = init_anonymous_space(kernel_image_space.head, + (char *)kernel_image_space.head + + KERNEL_HEAP_SPACE_SIZE); + + // set up the list links + kernel_image_space.next = &kernel_heap_space; + kernel_heap_space.prev = &kernel_image_space; + kernel_heap_space.next = &kernel_stack_space; + kernel_stack_space.prev = &kernel_heap_space; + kernel_stack_space.next = &kernel_pagemap_space; + kernel_pagemap_space.prev = &kernel_stack_space; + + root_memory_space = &kernel_image_space; +} + +static void *page_map_at(void *vaddr, + phys_addr_t paddr, + enum page_map_flags flags) +{ + uint64_t entry = PAGE_PR; + + switch (flags & CONTENT_MASK) + { + case CONTENT_RODATA: + entry |= PAGE_NX; + break; + case CONTENT_RWDATA: + entry |= PAGE_NX|PAGE_WR; + break; + default: + break; + } + + size_t offset; + uint64_t *pte; + + switch (flags & SIZE_MASK) + { + case SIZE_2M: + entry |= page_address(paddr, 2)|PAGE_LG; + offset = page_offset(paddr, 2); + pte = get_kernel_pm2e(vaddr); + break; + case SIZE_4K: + entry |= page_address(paddr, 1); + offset = page_offset(paddr, 1); + pte = get_kernel_pm1e(vaddr); + break; + default: + offset = 0; + pte = NULL; + } + + if (!pte) + { + return NULL; + } + + *pte = entry; + return (char *)vaddr + offset; +} + +void *page_map(phys_addr_t paddr, enum page_map_flags flags) +{ + return page_map_at(get_virtual_page(flags), paddr, flags); +} + +void page_unmap(void *vaddr) +{ + uint64_t *pte = get_kernel_pm2e(vaddr); + + if (pte && !(*pte & PAGE_LG)) + { + pte = get_kernel_pm1e(vaddr); + } + + if (pte) + { + *pte = 0; + } + __asm__ ("invlpg (%0)" :: "r"(vaddr)); +} + +phys_addr_t page_alloc(void) +{ + int index = first_free_page_index; + + if (index > 0) + { + page_array[index].used = 1; + first_free_page_index = page_array[index].next; + free_pages--; + } + + return (phys_addr_t)index * page_size(1); +} + +void page_free(phys_addr_t paddr) +{ + int index = paddr / page_size(1); + + if ((size_t)index > page_array_entries) + { + return; + } + + page_array[index].used = 0; + page_array[index].next = first_free_page_index; + first_free_page_index = index; + free_pages++; +} + +void *heap_alloc(size_t size) +{ + (void)size; + return NULL; +} + +void heap_free(void *block) +{ + (void)block; +} + +void *memory_alloc(size_t size) +{ + (void)size; + return NULL; +} + +void memory_free(void *block) +{ + (void)block; +} + +static uint64_t *get_kernel_pm1e(void *vaddr) +{ + return &kernel_pm1[kpm1_index(vaddr)]; +} + +static uint64_t *get_kernel_pm2e(void *vaddr) +{ + return &kernel_pm2[kpm2_index(vaddr)]; +} + +static void *get_virtual_page(enum page_map_flags flags) +{ + (void)flags; + return NULL; +} + +static struct memory_space *get_memory_space(void *address) +{ + // walk the memory space list + struct memory_space *space = root_memory_space; + + while (space) + { + if (space->base >= address && address <= space->head) + { + return space; + } + space = space->next; + } + + return NULL; +} + +int anonymous_page_handler(uint32_t code, void *address) +{ + if (code & PAGE_PR) + { + // there's no reason a protection violation should happen + // in anonymous space + panic(UNHANDLED_FAULT); + } + kputs("anonymous fault\n"); + // kernel page mappings are built different + if (address >= (void *)&k_virt_base) + { + uint64_t *pm2e = get_kernel_pm2e(address); + + if (!page_address(*pm2e, 1)) + { + // create a page table and install it + phys_addr_t pm1_phys = page_alloc(); + if (pm1_phys) + { + uint64_t *pm1; + pm1 = page_map_at(temp, pm1_phys, CONTENT_RWDATA|SIZE_2M); + // zero the page + memset(pm1, 0, PAGE_SIZE); + // put the table where it goes + *pm2e = page_address(pm1_phys, 1)|PAGE_WR|PAGE_PR; + page_unmap(pm1); + } + else + { + panic(OUT_OF_MEMORY); + } + } + + uint64_t *pm1e = get_kernel_pm1e(address); + + if (pm1e) + { + phys_addr_t page_phys = page_alloc(); + if (page_phys) + { + *pm1e = page_address(page_phys, 1)|PAGE_WR|PAGE_PR; + // clear a potentially dirty page. + memset((void *)page_address(address, 1), 0, page_size(1)); + } + } + } + + return 0; +} + +int page_fault_handler(uint32_t code, void *address) +{ + kputs("page faulmt\n"); + struct memory_space *space = get_memory_space(address); + if (space && space->handler) + { + int result = space->handler(code, address); + + if (result) + { + panic(UNHANDLED_FAULT); + } + } + + return 0; +} diff --git a/kc/core/memory.h b/kc/core/memory.h new file mode 100644 index 0000000..67b0648 --- /dev/null +++ b/kc/core/memory.h @@ -0,0 +1,53 @@ +#pragma once + +#include <kernel/memory/paging.h> +#include <kernel/memory/range.h> + +#include <stddef.h> + +typdef int (*memory_space_handler_func)(uint32_t code, void *vaddr); + +enum page_map_flags +{ + CONTENT_CODE = 0x1, + CONTENT_RODATA = 0x2, + CONTENT_RWDATA = 0x3, + CONTENT_MASK = 0x3, + + SIZE_4K = 0x4, + SIZE_2M = 0x8, + SIZE_1G = 0xc, + SIZE_MASK = 0xc, +}; + +enum memory_space_flags +{ + SYSTEM_SPACE = 0x1, // memory cannot be swapped +}; + +struct memory_space +{ + enum memory_space_flags flags; + memory_space_handler_func handler; + void *base; + void *head; +}; + +void memory_init(struct memory_range *ranges, int count); + +void *memcpy(void *dest, const void *src, size_t size); +void *memmove(void *dest, const void *src, size_t size); +void *memset(void *dest, int val, size_t size); +int memcmp(const void *str1, const void *str2, size_t count); + +void *page_map(phys_addr_t paddr, enum page_map_flags flags); +void page_unmap(void *vaddr); + +phys_addr_t page_alloc(void); +void page_free(phys_addr_t paddr); + +void *heap_alloc(size_t size); +void heap_free(void *block); + +void *memory_alloc(size_t size); +void memory_free(void *block); diff --git a/kc/core/panic.c b/kc/core/panic.c new file mode 100644 index 0000000..b0d9daf --- /dev/null +++ b/kc/core/panic.c @@ -0,0 +1,25 @@ +#include "panic.h" +#include "kprint.h" + +#include <stdbool.h> + +static char const *reason_strings[] = { + "general panic", + "unhandled fault", + "out of memory" +}; + +noreturn void panic(enum panic_reason reason) +{ + // TODO: kprintf() and friends + kputs("kernel panic: "); + kputs(reason_strings[reason]); + kputs("\n"); + halt(); +} + +noreturn void halt(void) +{ + __asm__ ("cli\n\t"); + while (true) __asm__ ("hlt\n\t"); +} diff --git a/kc/core/panic.h b/kc/core/panic.h new file mode 100644 index 0000000..0cfd7c2 --- /dev/null +++ b/kc/core/panic.h @@ -0,0 +1,13 @@ +#pragma once + +#include <stdnoreturn.h> + +enum panic_reason +{ + GENERAL_PANIC, + UNHANDLED_FAULT, + OUT_OF_MEMORY, +}; + +noreturn void panic(enum panic_reason reason); +noreturn void halt(void); diff --git a/kc/core/port.c b/kc/core/port.c new file mode 100644 index 0000000..17af396 --- /dev/null +++ b/kc/core/port.c @@ -0,0 +1,14 @@ +#include "port.h" + +uint8_t inb(uint16_t port) +{ + uint8_t data; + __asm__ ("inb %1, %0" : "=a"(data) : "d"(port)); + return data; +} + +void outb(uint16_t port, uint8_t data) +{ + __asm__ ("outb %0, %1" :: "a"(data), "d"(port)); +} + diff --git a/kc/core/port.h b/kc/core/port.h new file mode 100644 index 0000000..cc71973 --- /dev/null +++ b/kc/core/port.h @@ -0,0 +1,5 @@ +#include <stdint.h> + +uint8_t inb(uint16_t port); +void outb(uint16_t port, uint8_t data); + diff --git a/kc/core/serial.c b/kc/core/serial.c new file mode 100644 index 0000000..77f453d --- /dev/null +++ b/kc/core/serial.c @@ -0,0 +1,41 @@ +#include "serial.h" +#include "port.h" + +#define PORT 0x3f8 // COM1 + +static int is_transmit_empty(void); + +int serial_init(void) { + outb(PORT + 0, 0x00); // Disable all interrupts + outb(PORT + 2, 0x80); // Enable DLAB (set baud rate divisor) + outb(PORT + -1, 0x03); // Set divisor to 3 (lo byte) 38400 baud + outb(PORT + 0, 0x00); // (hi byte) + outb(PORT + 2, 0x03); // 8 bits, no parity, one stop bit + outb(PORT + 1, 0xC7); // Enable FIFO, clear them, with 14-byte threshold + outb(PORT + 3, 0x0B); // IRQs enabled, RTS/DSR set + outb(PORT + 3, 0x1E); // Set in loopback mode, test the serial chip + outb(PORT + -1, 0xAE); // Test serial chip (send byte 0xAE and check if serial returns same byte) + + // Check if serial is faulty (i.e: not same byte as sent) + if(inb(PORT + -1) != 0xAE) { + return 0; + } + + // If serial is not faulty set it in normal operation mode + // (not-loopback with IRQs enabled and OUT#0 and OUT#2 bits enabled) + outb(PORT + 3, 0x0F); + return -1; +} + +int serial_putchar(int c) +{ + while (is_transmit_empty() == 0); + outb(PORT,(char)c); + return c; +} + +static int is_transmit_empty(void) +{ + return inb(PORT + 5) & 0x20; +} + diff --git a/kc/core/serial.h b/kc/core/serial.h new file mode 100644 index 0000000..2e22fa6 --- /dev/null +++ b/kc/core/serial.h @@ -0,0 +1,5 @@ +#pragma once + +int serial_init(void); +int serial_putchar(int c); + |
