From 2fa3936238f52ada046709f8415880eb5172aa09 Mon Sep 17 00:00:00 2001 From: Bradley Myers Date: Fri, 20 Oct 2023 10:01:33 -0400 Subject: [PATCH 1/7] Port liballoc as a memory manager --- src/intf/klib/memory.h | 37 +++ src/x86_64/klib/memory.cpp | 584 +++++++++++++++++++++++++++++++++++++ 2 files changed, 621 insertions(+) create mode 100644 src/intf/klib/memory.h create mode 100644 src/x86_64/klib/memory.cpp diff --git a/src/intf/klib/memory.h b/src/intf/klib/memory.h new file mode 100644 index 0000000..972ae0c --- /dev/null +++ b/src/intf/klib/memory.h @@ -0,0 +1,37 @@ +/* + * This memory manager "liballoc" is taken from https://github.com/blanham/liballoc. + * It is licensed in the public domain. + */ + +#pragma once + +#include +#include + +/// @brief This function is supposed to lock the memory data structures. +/// It could be as simple as disabling interrupts or acquiring a spinlock. +/// @return 0 if the lock was acquired successfully. Anything else is failure. +extern "C" int liballoc_lock(); + +/// @brief This function unlocks what was previously locked by the liballoc_lock +/// function. If it disabled interrupts, it enables interrupts. If it +/// had acquiried a spinlock, it releases the spinlock. etc. +/// @return 0 if the lock was successfully released. +extern "C" int liballoc_unlock(); + +/// @brief This is the hook into the local system which allocates pages. +/// The page size was set up in the liballoc_init function. +/// @param pages The number of pages required. +/// @return A pointer to the allocated memory or NULL if the pages were not allocated. +extern "C" void* liballoc_alloc(size_t pages); + +/// @brief This frees previously allocated memory. +/// @param ptr The same value returned from a previous liballoc_alloc call. +/// @param pages The number of pages to free. +/// @return 0 if the memory was successfully freed. +extern "C" int liballoc_free(void* ptr, size_t pages); + +extern void *kmalloc(size_t); +extern void *krealloc(void *, size_t); +extern void *kcalloc(size_t, size_t); +extern void kfree(void *); diff --git a/src/x86_64/klib/memory.cpp b/src/x86_64/klib/memory.cpp new file mode 100644 index 0000000..88e18c9 --- /dev/null +++ b/src/x86_64/klib/memory.cpp @@ -0,0 +1,584 @@ +/* + * This memory manager "liballoc" is taken from https://github.com/blanham/liballoc. + * It is licensed in the public domain. + */ + +#include "klib/memory.h" + +#define VERSION "1.1" +#define ALIGNMENT 16ul + +#define ALIGN_TYPE char +#define ALIGN_INFO sizeof(ALIGN_TYPE) * 16 + +#define USE_CASE1 +#define USE_CASE2 +#define USE_CASE3 +#define USE_CASE4 +#define USE_CASE5 + +#define ALIGN(ptr) \ + if (ALIGNMENT > 1) \ + { \ + uintptr_t diff; \ + ptr = (void *)((uintptr_t)ptr + ALIGN_INFO); \ + diff = (uintptr_t)ptr & (ALIGNMENT - 1); \ + if (diff != 0) \ + { \ + diff = ALIGNMENT - diff; \ + ptr = (void *)((uintptr_t)ptr + diff); \ + } \ + *((ALIGN_TYPE *)((uintptr_t)ptr - ALIGN_INFO)) = \ + diff + ALIGN_INFO; \ + } + +#define UNALIGN(ptr) \ + if (ALIGNMENT > 1) \ + { \ + uintptr_t diff = *((ALIGN_TYPE *)((uintptr_t)ptr - ALIGN_INFO)); \ + if (diff < (ALIGNMENT + ALIGN_INFO)) \ + { \ + ptr = (void *)((uintptr_t)ptr - diff); \ + } \ + } + +#define LIBALLOC_MAGIC 0xc001c0de +#define LIBALLOC_DEAD 0xdeaddead + +/// @brief A structure found at the top of all system allocated +/// memory blocks. It details the usage of the memory block. +struct liballoc_major +{ + struct liballoc_major *prev; // Linked list information. + struct liballoc_major *next; // Linked list information. + unsigned int pages; // The number of pages in the block. + unsigned int size; // The number of pages in the block. + unsigned int usage; // The number of bytes used in the block. + struct liballoc_minor *first; // A pointer to the first allocated memory in the block. +}; + +/// @brief This is a structure found at the beginning of all +/// sections in a major block which were allocated by a +/// malloc, calloc, realloc call. +struct liballoc_minor +{ + struct liballoc_minor *prev; // Linked list information. + struct liballoc_minor *next; // Linked list information. + struct liballoc_major *block; // The owning block. A pointer to the major structure. + unsigned int magic; // A magic number to idenfity correctness. + unsigned int size; // The size of the memory allocated. Could be 1 byte or more. + unsigned int req_size; // The size of memory requested. +}; + +static struct liballoc_major *l_memRoot = NULL; // The root memory block acquired from the system. +static struct liballoc_major *l_bestBet = NULL; // The major with the most free memory. + +static unsigned int l_pageSize = 4096; // The size of an individual page. Set up in liballoc_init. +static unsigned int l_pageCount = 16; // The number of pages to request per chunk. Set up in liballoc_init. +static unsigned long long l_allocated = 0; // Running total of allocated memory. +static unsigned long long l_inuse = 0; // Running total of used memory. + +static long long l_warningCount = 0; // Number of warnings encountered +static long long l_errorCount = 0; // Number of actual errors +static long long l_possibleOverruns = 0; // Number of possible overruns + +static void *liballoc_memset(void *s, int c, size_t n) +{ + unsigned int i; + for (i = 0; i < n; i++) + ((char *)s)[i] = c; + + return s; +} + +static void *liballoc_memcpy(void *s1, const void *s2, size_t n) +{ + char *cdest; + char *csrc; + unsigned int *ldest = (unsigned int *)s1; + unsigned int *lsrc = (unsigned int *)s2; + + while (n >= sizeof(unsigned int)) + { + *ldest++ = *lsrc++; + n -= sizeof(unsigned int); + } + + cdest = (char *)ldest; + csrc = (char *)lsrc; + + while (n > 0) + { + *cdest++ = *csrc++; + n -= 1; + } + + return s1; +} + +static struct liballoc_major *allocate_new_page(unsigned int size) +{ + unsigned int st; + struct liballoc_major *maj; + + // This is how much space is required. + st = size + sizeof(struct liballoc_major); + st += sizeof(struct liballoc_minor); + + // Perfect amount of space? + if ((st % l_pageSize) == 0) + st = st / (l_pageSize); + else + st = st / (l_pageSize) + 1; + // No, add the buffer. + + // Make sure it's >= the minimum size. + if (st < l_pageCount) + st = l_pageCount; + + maj = (struct liballoc_major *)liballoc_alloc(st); + + if (maj == NULL) + { + l_warningCount += 1; + return NULL; // uh oh, we ran out of memory. + } + + maj->prev = NULL; + maj->next = NULL; + maj->pages = st; + maj->size = st * l_pageSize; + maj->usage = sizeof(struct liballoc_major); + maj->first = NULL; + + l_allocated += maj->size; + + return maj; +} + +void *kmalloc(size_t req_size) +{ + int startedBet = 0; + unsigned long long bestSize = 0; + void *p = NULL; + uintptr_t diff; + struct liballoc_major *maj; + struct liballoc_minor *min; + struct liballoc_minor *new_min; + unsigned long size = req_size; + + // For alignment, we adjust size so there's enough space to align. + if (ALIGNMENT > 1) + { + size += ALIGNMENT + ALIGN_INFO; + } + // So, ideally, we really want an alignment of 0 or 1 in order + // to save space. + + liballoc_lock(); + + if (size == 0) + { + l_warningCount += 1; + liballoc_unlock(); + return kmalloc(1); + } + + if (l_memRoot == NULL) + { + // This is the first time we are being used. + l_memRoot = allocate_new_page(size); + if (l_memRoot == NULL) + { + liballoc_unlock(); + return NULL; + } + } + + // Now we need to bounce through every major and find enough space.... + + maj = l_memRoot; + startedBet = 0; + + // Start at the best bet.... + if (l_bestBet != NULL) + { + bestSize = l_bestBet->size - l_bestBet->usage; + + if (bestSize > (size + sizeof(struct liballoc_minor))) + { + maj = l_bestBet; + startedBet = 1; + } + } + + while (maj != NULL) + { + diff = maj->size - maj->usage; + // free memory in the block + + if (bestSize < diff) + { + // Hmm.. this one has more memory then our bestBet. Remember! + l_bestBet = maj; + bestSize = diff; + } + +#ifdef USE_CASE1 + + // CASE 1: There is not enough space in this major block. + if (diff < (size + sizeof(struct liballoc_minor))) + { + // Another major block next to this one? + if (maj->next != NULL) + { + maj = maj->next; // Hop to that one. + continue; + } + + if (startedBet == 1) // If we started at the best bet, + { // let's start all over again. + maj = l_memRoot; + startedBet = 0; + continue; + } + + // Create a new major block next to this one and... + maj->next = allocate_new_page(size); // next one will be okay. + if (maj->next == NULL) + break; // no more memory. + maj->next->prev = maj; + maj = maj->next; + + // .. fall through to CASE 2 .. + } + +#endif + +#ifdef USE_CASE2 + + // CASE 2: It's a brand new block. + if (maj->first == NULL) + { + maj->first = (struct liballoc_minor *)((uintptr_t)maj + sizeof(struct liballoc_major)); + + maj->first->magic = LIBALLOC_MAGIC; + maj->first->prev = NULL; + maj->first->next = NULL; + maj->first->block = maj; + maj->first->size = size; + maj->first->req_size = req_size; + maj->usage += size + sizeof(struct liballoc_minor); + + l_inuse += size; + + p = (void *)((uintptr_t)(maj->first) + sizeof(struct liballoc_minor)); + + ALIGN(p); + + liballoc_unlock(); // release the lock + return p; + } + +#endif + +#ifdef USE_CASE3 + + // CASE 3: Block in use and enough space at the start of the block. + diff = (uintptr_t)(maj->first); + diff -= (uintptr_t)maj; + diff -= sizeof(struct liballoc_major); + + if (diff >= (size + sizeof(struct liballoc_minor))) + { + // Yes, space in front. Squeeze in. + maj->first->prev = (struct liballoc_minor *)((uintptr_t)maj + sizeof(struct liballoc_major)); + maj->first->prev->next = maj->first; + maj->first = maj->first->prev; + + maj->first->magic = LIBALLOC_MAGIC; + maj->first->prev = NULL; + maj->first->block = maj; + maj->first->size = size; + maj->first->req_size = req_size; + maj->usage += size + sizeof(struct liballoc_minor); + + l_inuse += size; + + p = (void *)((uintptr_t)(maj->first) + sizeof(struct liballoc_minor)); + ALIGN(p); + + liballoc_unlock(); // release the lock + return p; + } + +#endif + +#ifdef USE_CASE4 + + // CASE 4: There is enough space in this block. But is it contiguous? + min = maj->first; + + // Looping within the block now... + while (min != NULL) + { + // CASE 4.1: End of minors in a block. Space from last and end? + if (min->next == NULL) + { + // the rest of this block is free... is it big enough? + diff = (uintptr_t)(maj) + maj->size; + diff -= (uintptr_t)min; + diff -= sizeof(struct liballoc_minor); + diff -= min->size; + // minus already existing usage.. + + if (diff >= (size + sizeof(struct liballoc_minor))) + { + // yay.... + min->next = (struct liballoc_minor *)((uintptr_t)min + sizeof(struct liballoc_minor) + min->size); + min->next->prev = min; + min = min->next; + min->next = NULL; + min->magic = LIBALLOC_MAGIC; + min->block = maj; + min->size = size; + min->req_size = req_size; + maj->usage += size + sizeof(struct liballoc_minor); + + l_inuse += size; + + p = (void *)((uintptr_t)min + sizeof(struct liballoc_minor)); + ALIGN(p); + + liballoc_unlock(); // release the lock + return p; + } + } + + // CASE 4.2: Is there space between two minors? + if (min->next != NULL) + { + // is the difference between here and next big enough? + diff = (uintptr_t)(min->next); + diff -= (uintptr_t)min; + diff -= sizeof(struct liballoc_minor); + diff -= min->size; + // minus our existing usage. + + if (diff >= (size + sizeof(struct liballoc_minor))) + { + // yay...... + new_min = (struct liballoc_minor *)((uintptr_t)min + sizeof(struct liballoc_minor) + min->size); + + new_min->magic = LIBALLOC_MAGIC; + new_min->next = min->next; + new_min->prev = min; + new_min->size = size; + new_min->req_size = req_size; + new_min->block = maj; + min->next->prev = new_min; + min->next = new_min; + maj->usage += size + sizeof(struct liballoc_minor); + + l_inuse += size; + + p = (void *)((uintptr_t)new_min + sizeof(struct liballoc_minor)); + ALIGN(p); + + liballoc_unlock(); // release the lock + return p; + } + } // min->next != NULL + + min = min->next; + } // while min != NULL ... + +#endif + +#ifdef USE_CASE5 + + // CASE 5: Block full! Ensure next block and loop. + if (maj->next == NULL) + { + if (startedBet == 1) + { + maj = l_memRoot; + startedBet = 0; + continue; + } + + // we've run out. we need more... + maj->next = allocate_new_page(size); // next one guaranteed to be okay + if (maj->next == NULL) + break; // uh oh, no more memory..... + maj->next->prev = maj; + } + +#endif + + maj = maj->next; + } // while (maj != NULL) + + liballoc_unlock(); // release the lock + return NULL; +} + +void kfree(void *ptr) +{ + struct liballoc_minor *min; + struct liballoc_major *maj; + + if (ptr == NULL) + { + l_warningCount += 1; + return; + } + + UNALIGN(ptr); + + liballoc_lock(); // lockit + + min = (struct liballoc_minor *)((uintptr_t)ptr - sizeof(struct liballoc_minor)); + + if (min->magic != LIBALLOC_MAGIC) + { + l_errorCount += 1; + + // Check for overrun errors. For all bytes of LIBALLOC_MAGIC + if ( + ((min->magic & 0xFFFFFF) == (LIBALLOC_MAGIC & 0xFFFFFF)) || + ((min->magic & 0xFFFF) == (LIBALLOC_MAGIC & 0xFFFF)) || + ((min->magic & 0xFF) == (LIBALLOC_MAGIC & 0xFF))) + { + l_possibleOverruns += 1; + } + + liballoc_unlock(); // release the lock + return; + } + + maj = min->block; + + l_inuse -= min->size; + + maj->usage -= (min->size + sizeof(struct liballoc_minor)); + min->magic = LIBALLOC_DEAD; // No mojo. + + if (min->next != NULL) + min->next->prev = min->prev; + if (min->prev != NULL) + min->prev->next = min->next; + + if (min->prev == NULL) + maj->first = min->next; + // Might empty the block. This was the first + // minor. + + // We need to clean up after the majors now.... + + if (maj->first == NULL) // Block completely unused. + { + if (l_memRoot == maj) + l_memRoot = maj->next; + if (l_bestBet == maj) + l_bestBet = NULL; + if (maj->prev != NULL) + maj->prev->next = maj->next; + if (maj->next != NULL) + maj->next->prev = maj->prev; + l_allocated -= maj->size; + + liballoc_free(maj, maj->pages); + } + else + { + if (l_bestBet != NULL) + { + int bestSize = l_bestBet->size - l_bestBet->usage; + int majSize = maj->size - maj->usage; + + if (majSize > bestSize) + l_bestBet = maj; + } + } + + liballoc_unlock(); // release the lock +} + +void *kcalloc(size_t nobj, size_t size) +{ + int real_size; + void *p; + + real_size = nobj * size; + + p = kmalloc(real_size); + + liballoc_memset(p, 0, real_size); + + return p; +} + +void *krealloc(void *p, size_t size) +{ + void *ptr; + struct liballoc_minor *min; + unsigned int real_size; + + // Honour the case of size == 0 => free old and return NULL + if (size == 0) + { + kfree(p); + return NULL; + } + + // In the case of a NULL pointer, return a simple malloc. + if (p == NULL) + return kmalloc(size); + + // Unalign the pointer if required. + ptr = p; + UNALIGN(ptr); + + liballoc_lock(); // lockit + + min = (struct liballoc_minor *)((uintptr_t)ptr - sizeof(struct liballoc_minor)); + + // Ensure it is a valid structure. + if (min->magic != LIBALLOC_MAGIC) + { + l_errorCount += 1; + + // Check for overrun errors. For all bytes of LIBALLOC_MAGIC + if ( + ((min->magic & 0xFFFFFF) == (LIBALLOC_MAGIC & 0xFFFFFF)) || + ((min->magic & 0xFFFF) == (LIBALLOC_MAGIC & 0xFFFF)) || + ((min->magic & 0xFF) == (LIBALLOC_MAGIC & 0xFF))) + { + l_possibleOverruns += 1; + } + + liballoc_unlock(); // release the lock + return NULL; + } + + // Definitely a memory block. + + real_size = min->req_size; + + if (real_size >= size) + { + min->req_size = size; + liballoc_unlock(); + return p; + } + + liballoc_unlock(); + + // If we got here then we're reallocating to a block bigger than us. + ptr = kmalloc(size); // We need to allocate new memory + liballoc_memcpy(ptr, p, real_size); + kfree(p); + + return ptr; +} From 88409383aedd9c8ee499f73afedce5d30e844270 Mon Sep 17 00:00:00 2001 From: Bradley Myers Date: Mon, 23 Oct 2023 00:19:42 -0400 Subject: [PATCH 2/7] Moved kernel to higher-half --- Makefile | 12 ++- debug.sh | 5 +- src/{x86_64/boot => }/intf/idt.h | 8 ++ src/{x86_64/boot => }/intf/isr.h | 0 src/{x86_64/boot => }/intf/pic.h | 0 src/kernel/main.cpp | 9 +++ src/x86_64/boot/boot.asm | 12 ++- src/x86_64/boot/boot64.asm | 31 ++++---- src/x86_64/boot/gdt.asm | 40 +++------- src/x86_64/boot/heap_tmp.asm | 48 +++++++++++ src/x86_64/boot/paging.asm | 59 -------------- src/x86_64/boot/paging_setup.asm | 83 ++++++++++++++++++++ src/x86_64/boot/test_long_mode.asm | 16 ++-- src/x86_64/{boot => }/exception_handlers.cpp | 0 src/x86_64/{boot => }/idt.cpp | 3 - src/x86_64/{boot => }/isr.asm | 0 src/x86_64/{boot => }/pic.cpp | 0 targets/x86_64/linker.ld | 35 ++++++++- 18 files changed, 237 insertions(+), 124 deletions(-) rename src/{x86_64/boot => }/intf/idt.h (81%) rename src/{x86_64/boot => }/intf/isr.h (100%) rename src/{x86_64/boot => }/intf/pic.h (100%) create mode 100644 src/x86_64/boot/heap_tmp.asm delete mode 100644 src/x86_64/boot/paging.asm create mode 100644 src/x86_64/boot/paging_setup.asm rename src/x86_64/{boot => }/exception_handlers.cpp (100%) rename src/x86_64/{boot => }/idt.cpp (99%) rename src/x86_64/{boot => }/isr.asm (100%) rename src/x86_64/{boot => }/pic.cpp (100%) diff --git a/Makefile b/Makefile index b2e5c61..a31df7b 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,7 @@ +CFLAGS = -c -g -ffreestanding -mno-red-zone -mno-mmx -mno-sse -mno-sse2 -fstack-protector -mgeneral-regs-only -fPIC -mcmodel=large -Wall -Wextra +ASFLAGS = -g -F dwarf +LDFLAGS = -n + kernel_cpp_src := $(shell find src/kernel -name *.cpp) kernel_cpp_obj := $(patsubst src/kernel/%.cpp, build/kernel/%.o, $(kernel_cpp_src)) @@ -14,7 +18,7 @@ x86_64_obj := $(x86_64_asm_obj) $(x86_64_cpp_obj) ################################################## $(kernel_cpp_obj): build/kernel/%.o : src/kernel/%.cpp mkdir -p $(dir $@) && \ - x86_64-elf-g++ -c -g -I src/intf -I src/x86_64/boot/intf -ffreestanding -mno-red-zone -mno-mmx -mno-sse -mno-sse2 -mgeneral-regs-only -Wall $(patsubst build/kernel/%.o, src/kernel/%.cpp, $@) -o $@ + x86_64-elf-g++ -I src/intf -I src/x86_64/boot/intf $(CFLAGS) $(patsubst build/kernel/%.o, src/kernel/%.cpp, $@) -o $@ @@ -23,11 +27,11 @@ $(kernel_cpp_obj): build/kernel/%.o : src/kernel/%.cpp ################################################## $(x86_64_asm_obj): build/x86_64/%.o : src/x86_64/%.asm mkdir -p $(dir $@) && \ - nasm -g -F dwarf -f elf64 $(patsubst build/x86_64/%.o, src/x86_64/%.asm, $@) -o $@ + nasm $(ASFLAGS) -f elf64 $(patsubst build/x86_64/%.o, src/x86_64/%.asm, $@) -o $@ $(x86_64_cpp_obj): build/x86_64/%.o : src/x86_64/%.cpp mkdir -p $(dir $@) && \ - x86_64-elf-g++ -c -g -I src/intf -I src/x86_64/boot/intf -ffreestanding -mno-red-zone -mno-mmx -mno-sse -mno-sse2 -mgeneral-regs-only -Wall $(patsubst build/x86_64/%.o, src/x86_64/%.cpp, $@) -o $@ + x86_64-elf-g++ -I src/intf -I src/x86_64/boot/intf $(CFLAGS) $(patsubst build/x86_64/%.o, src/x86_64/%.cpp, $@) -o $@ @@ -37,7 +41,7 @@ $(x86_64_cpp_obj): build/x86_64/%.o : src/x86_64/%.cpp .PHONY: build-x86_64 build-x86_64: $(kernel_obj) $(x86_64_obj) mkdir -p dist/x86_64 && \ - x86_64-elf-ld -n -o dist/x86_64/kernel.bin -T targets/x86_64/linker.ld $(kernel_obj) $(x86_64_obj) && \ + x86_64-elf-ld -T targets/x86_64/linker.ld $(LDFLAGS) -o dist/x86_64/kernel.bin $(kernel_obj) $(x86_64_obj) && \ cp dist/x86_64/kernel.bin targets/x86_64/iso/boot/kernel.bin &&\ grub-mkrescue /usr/lib/grub/i386-pc -o dist/x86_64/kernel.iso targets/x86_64/iso diff --git a/debug.sh b/debug.sh index 29d3679..b76ecb1 100755 --- a/debug.sh +++ b/debug.sh @@ -1,5 +1,8 @@ +#!/bin/sh + set -e sh build.sh qemu-system-x86_64 -cdrom dist/x86_64/kernel.iso -s -S & -gdb --quiet -ex "target remote localhost:1234" -ex "symbol-file dist/x86_64/kernel.bin" +gdb --quiet -ex "target remote localhost:1234" -ex "set disassembly-flavor intel" \ + -ex "symbol-file dist/x86_64/kernel.bin" diff --git a/src/x86_64/boot/intf/idt.h b/src/intf/idt.h similarity index 81% rename from src/x86_64/boot/intf/idt.h rename to src/intf/idt.h index 1a3eac1..444886b 100644 --- a/src/x86_64/boot/intf/idt.h +++ b/src/intf/idt.h @@ -1,6 +1,9 @@ #pragma once #include +#include "isr.h" +#include "gdt.h" +#include "pic.h" #define IDT_ENTRIES 256 @@ -27,3 +30,8 @@ typedef struct __attribute__((packed)) IDT_Ptr { } IDT_Ptr; extern "C" void init_idt(); + +/// @brief `cli` +inline void __clear_interrupts() { asm volatile("cli"); } +/// @brief `sti` +inline void __enable_interrupts() { asm volatile("sti"); } diff --git a/src/x86_64/boot/intf/isr.h b/src/intf/isr.h similarity index 100% rename from src/x86_64/boot/intf/isr.h rename to src/intf/isr.h diff --git a/src/x86_64/boot/intf/pic.h b/src/intf/pic.h similarity index 100% rename from src/x86_64/boot/intf/pic.h rename to src/intf/pic.h diff --git a/src/kernel/main.cpp b/src/kernel/main.cpp index edac585..24dd79a 100644 --- a/src/kernel/main.cpp +++ b/src/kernel/main.cpp @@ -1,4 +1,6 @@ #include "drivers/video/vga.h" +#include "pic.h" +#include "idt.h" namespace VGA = drivers::video::VGA; namespace Color = VGA::Color; @@ -8,6 +10,13 @@ void print_welcome(); /// @brief The entry point into the StasisOS kernel. extern "C" void kernel_main() { + // Remap the PIC interrupt vectors to 0x20..0x2F + remap_pic(0x20, 0x28); + + // Set interrupt handlers + init_idt(); + __enable_interrupts(); + // Print the startup banner VGA::clear_screen(); print_welcome(); diff --git a/src/x86_64/boot/boot.asm b/src/x86_64/boot/boot.asm index f1ca243..d63ea76 100644 --- a/src/x86_64/boot/boot.asm +++ b/src/x86_64/boot/boot.asm @@ -1,18 +1,21 @@ bits 32 global start +global stack_top extern test_multiboot, test_cpuid, test_long_mode extern setup_page_tables, enable_paging extern gdt_descriptor, SEG_KERNEL_CODE extern long_mode_start -section .text +%define KERNEL_VMA 0xFFFFFFFF80000000 + +section .boot.text progbits alloc exec nowrite align=16 start: cli ; clear interrupts ; setup the stack - mov ebp, stack_top + mov ebp, stack_top - KERNEL_VMA mov esp, ebp ; test requirements for long mode support (error and hlt if fail) @@ -25,7 +28,8 @@ start: call enable_paging ; load GDT - lgdt [gdt_descriptor] + mov eax, gdt_descriptor - KERNEL_VMA + lgdt [eax] ; start long mode jmp SEG_KERNEL_CODE:long_mode_start @@ -33,5 +37,5 @@ start: section .bss align 4096 stack_bottom: - resb 4096 * 4 + resb 4096 * 4 ; 16 KiB stack_top: diff --git a/src/x86_64/boot/boot64.asm b/src/x86_64/boot/boot64.asm index 3a2fcd2..2798f0d 100644 --- a/src/x86_64/boot/boot64.asm +++ b/src/x86_64/boot/boot64.asm @@ -2,11 +2,9 @@ bits 64 global long_mode_start -extern remap_pic -extern init_idt -extern kernel_main +section .boot.text -section .text +; trampolines to the kernel_start wrapper in the higher-half long_mode_start: ; null all data segment registers xor eax, eax @@ -16,17 +14,20 @@ long_mode_start: mov gs, ax mov ss, ax - ; remap the PIC interrupt vectors to 0x20..0x2F - mov esi, 0x28 ; slave_offset (0x28..0x2F) - mov edi, 0x20 ; master_offset (0x20..0x27) - call remap_pic - - ; Enable interrupts - call init_idt - sti - - ; start the kernel - call kernel_main + jmp kernel_start + +section .text +bits 64 + +extern kernel_main +extern stack_top + +kernel_start: + ; reload the stack with the virtual memory address + lea rbp, [stack_top] + mov rsp, rbp + + call kernel_main ; jump into kernel code ; hang the CPU if the kernel falls through cli diff --git a/src/x86_64/boot/gdt.asm b/src/x86_64/boot/gdt.asm index 7a13863..472f3e0 100644 --- a/src/x86_64/boot/gdt.asm +++ b/src/x86_64/boot/gdt.asm @@ -1,7 +1,9 @@ bits 64 global gdt_descriptor -global SEG_KERNEL_CODE;, SEG_KERNEL_DATA, SEG_USER_CODE, SEG_USER_DATA +global SEG_KERNEL_CODE, SEG_KERNEL_DATA + +%define KERNEL_VMA 0xFFFFFFFF80000000 section .rodata @@ -18,38 +20,20 @@ gdt: db 10101111b ; flags [4 bits] (4 KiB, _, long-mode, _) << 4 | limit high db 0x0 ; base high - ; .seg_kernel_data: - ; dw 0xFFFF ; limit low - ; dw 0x0 ; base low - ; db 0x0 ; base mid - ; db 10010000b ; access (present, ring 0 [2 bits], code/data segment, data segment, non-conforming, non-writable, not-accessed) - ; db 10101111b ; flags [4 bits] (4 KiB, _, long-mode, _) << 4 | limit high - ; db 0x0 ; base high - - ; .seg_user_code: - ; dw 0xFFFF ; limit low - ; dw 0x0 ; base low - ; db 0x0 ; base mid - ; db 11111010b ; access (present, ring 3 [2 bits], code/data segment, code segment, non-conforming, readable, not-accessed) - ; db 10101111b ; flags [4 bits] (4 KiB, _, long-mode, _) << 4 | limit high - ; db 0x0 ; base high - - ; .seg_user_data: - ; dw 0xFFFF ; limit low - ; dw 0x0 ; base low - ; db 0x0 ; base mid - ; db 11110000b ; access (present, ring 3 [2 bits], code/data segment, data segment, non-conforming, readable, not-accessed) - ; db 10101111b ; flags [4 bits] (4 KiB, _, long-mode, _) << 4 | limit high - ; db 0x0 ; base high + .seg_kernel_data: + dw 0xFFFF ; limit low + dw 0x0 ; base low + db 0x0 ; base mid + db 10010000b ; access (present, ring 0 [2 bits], code/data segment, data segment, non-conforming, non-writable, not-accessed) + db 10101111b ; flags [4 bits] (4 KiB, _, long-mode, _) << 4 | limit high + db 0x0 ; base high gdt_end: gdt_descriptor: dw gdt_end - gdt - 1 ; limit - dd gdt ; address + dd gdt - KERNEL_VMA ; address ; global segment accessors SEG_KERNEL_CODE equ gdt.seg_kernel_code - gdt -; SEG_KERNEL_DATA equ gdt.seg_kernel_data - gdt -; SEG_USER_CODE equ gdt.seg_user_code - gdt -; SEG_USER_DATA equ gdt.seg_user_data - gdt +SEG_KERNEL_DATA equ gdt.seg_kernel_data - gdt diff --git a/src/x86_64/boot/heap_tmp.asm b/src/x86_64/boot/heap_tmp.asm new file mode 100644 index 0000000..e112395 --- /dev/null +++ b/src/x86_64/boot/heap_tmp.asm @@ -0,0 +1,48 @@ +bits 64 + +global liballoc_lock, liballoc_unlock, liballoc_alloc, liballoc_free + +section .text + +%macro pusha 0 + push rax + push rcx + push rdx + push rbx + push rsp + push rbp + push rsi + push rdi +%endmacro +%macro popa 0 + pop rdi + pop rsi + pop rbp + pop rsp + pop rbx + pop rdx + pop rcx + pop rax +%endmacro + +; Look at linux impl for ideas: +; https://github.com/blanham/liballoc/blob/master/linux.c + +; int liballoc_lock() +; returns 0 if lock was acquired successfully +liballoc_lock: + + +; int liballoc_unlock() +; returns 0 if lock was successfully released +liballoc_unlock: + + +; void* liballoc_alloc(rdi <- u64 pages) +; returns a pointer to the allocated memory or 0 if allocation fails +liballoc_alloc: + + +; int liballoc_free(rdi <- void* ptr, rsi <- u64 pages) +; returns 0 if memory was successfully freed +liballoc_free: diff --git a/src/x86_64/boot/paging.asm b/src/x86_64/boot/paging.asm deleted file mode 100644 index c96fa67..0000000 --- a/src/x86_64/boot/paging.asm +++ /dev/null @@ -1,59 +0,0 @@ -bits 32 - -global setup_page_tables -global enable_paging - -section .text - -; Initializes the page tables for memory management -setup_page_tables: - mov eax, page_table_l3 - or eax, 11b ; present, writable - mov [page_table_l4], eax - - mov eax, page_table_l2 - or eax, 11b ; present, writable - mov [page_table_l3], eax - - mov ecx, 0 - .loop: - mov eax, 0x200000 ; 2 MiB - mul ecx - or eax, 10000011b ; present, writable, huge page - mov [page_table_l2 + ecx * 8], eax - - inc ecx - cmp ecx, 512 - jne .loop - ret - -; Enables paging with the flat memory model -; Also enables PAE and long mode -enable_paging: - ; pass page table location to cpu - mov eax, page_table_l4 - mov cr3, eax - - ; enable PAE - mov eax, cr4 - or eax, 1 << 5 - mov cr4, eax - - ; enable long mode - mov ecx, 0xC0000080 - rdmsr - or eax, 1 << 8 - wrmsr - - ; enable paging - mov eax, cr0 - or eax, 1 << 31 - mov cr0, eax - - ret - -section .bss -align 4096 -page_table_l4: resb 4096 -page_table_l3: resb 4096 -page_table_l2: resb 4096 diff --git a/src/x86_64/boot/paging_setup.asm b/src/x86_64/boot/paging_setup.asm new file mode 100644 index 0000000..c811a49 --- /dev/null +++ b/src/x86_64/boot/paging_setup.asm @@ -0,0 +1,83 @@ +bits 32 + +global setup_page_tables +global enable_paging + +%define KERNEL_VMA 0xFFFFFFFF80000000 + +section .boot.text + +; initializes the kernel page table mappings +setup_page_tables: + ; identity map kernel + mov eax, PDPT - KERNEL_VMA + or eax, 11b ; writable, present + mov dword [PML4 - KERNEL_VMA], eax + + ; higher half kernel mapping + mov eax, PDPT_HH - KERNEL_VMA + or eax, 11b ; writable, present + mov dword [(PML4 - KERNEL_VMA) + 511 * 8], eax + + ; map PML4 into itself + mov eax, PML4 - KERNEL_VMA + or eax, 11b ; writable, present + mov dword [(PML4 - KERNEL_VMA) + 510 * 8], eax + + ; map kernel page directory to identity mapping + mov eax, PD_KERN - KERNEL_VMA + or eax, 11b ; writable, present + mov dword [PDPT - KERNEL_VMA], eax + + ; map kernel page directory to higher half mapping + mov eax, PD_KERN - KERNEL_VMA + or eax, 11b ; writable, present + mov dword [(PDPT_HH - KERNEL_VMA) + 510 * 8], eax + + ; map all the entries in the page directory + mov ecx, 0 + .map_page_directory: + mov eax, 0x200000 ; 2 MiB pages + mul ecx ; * page number + or eax, 10000011b ; huge page, writable, present + + ; Move computed value into page directory entry ecx * 8 + mov [(PD_KERN - KERNEL_VMA) + ecx * 8], eax + + inc ecx + cmp ecx, 512 + jne .map_page_directory + + ret + +; enables paging, PAE, and long mode +enable_paging: + ; pass page table location to cpu + mov eax, PML4 - KERNEL_VMA + mov cr3, eax + + ; enable PAE + mov eax, cr4 + or eax, 1 << 5 + mov cr4, eax + + ; enable long mode + mov ecx, 0xC0000080 + rdmsr + or eax, 1 << 8 + wrmsr + + ; enable paging + mov eax, cr0 + or eax, 1 << 31 ; Enable paging + or eax, 1 << 16 ; Disable ring 0 from writing to readonly pages + mov cr0, eax + + ret + +section .bss +align 4096 +PML4: resb 4096 +PDPT: resb 4096 ; Kernel identity mapping +PDPT_HH: resb 4096 ; Virtual higher half mapping +PD_KERN: resb 4096 ; Directory to map the kernel diff --git a/src/x86_64/boot/test_long_mode.asm b/src/x86_64/boot/test_long_mode.asm index 39c80a4..2b1081f 100644 --- a/src/x86_64/boot/test_long_mode.asm +++ b/src/x86_64/boot/test_long_mode.asm @@ -4,19 +4,21 @@ global test_multiboot global test_cpuid global test_long_mode -section .text +%define KERNEL_VMA 0xFFFFFFFF80000000 -; Tests for multiboot capability +section .boot.text + +; tests for multiboot capability test_multiboot: cmp eax, 0x36D76289 jne .no_multiboot ret .no_multiboot: - mov eax, MSG_NO_MULTIBOOT + mov eax, MSG_NO_MULTIBOOT - KERNEL_VMA jmp error -; Tests for cpuid support +; tests for cpuid support test_cpuid: pushfd pop eax @@ -33,10 +35,10 @@ test_cpuid: ret .no_cpuid: - mov eax, MSG_NO_CPUID + mov eax, MSG_NO_CPUID - KERNEL_VMA jmp error -; Tests for long mode capability +; tests for long mode capability test_long_mode: mov eax, 0x80000000 cpuid @@ -50,7 +52,7 @@ test_long_mode: ret .no_long_mode: - mov eax, MSG_NO_LONG_MODE + mov eax, MSG_NO_LONG_MODE - KERNEL_VMA jmp error ; print the error message from eax to the VGA buffer then hlt diff --git a/src/x86_64/boot/exception_handlers.cpp b/src/x86_64/exception_handlers.cpp similarity index 100% rename from src/x86_64/boot/exception_handlers.cpp rename to src/x86_64/exception_handlers.cpp diff --git a/src/x86_64/boot/idt.cpp b/src/x86_64/idt.cpp similarity index 99% rename from src/x86_64/boot/idt.cpp rename to src/x86_64/idt.cpp index 7b5d0ab..8d03135 100644 --- a/src/x86_64/boot/idt.cpp +++ b/src/x86_64/idt.cpp @@ -1,8 +1,5 @@ #include "idt.h" -#include "isr.h" -#include "gdt.h" #include "drivers/keyboard/irq_handler.h" -#include "pic.h" /// @brief The IDT to hold the ISR definitions. static IDT_Entry idt[IDT_ENTRIES]; diff --git a/src/x86_64/boot/isr.asm b/src/x86_64/isr.asm similarity index 100% rename from src/x86_64/boot/isr.asm rename to src/x86_64/isr.asm diff --git a/src/x86_64/boot/pic.cpp b/src/x86_64/pic.cpp similarity index 100% rename from src/x86_64/boot/pic.cpp rename to src/x86_64/pic.cpp diff --git a/targets/x86_64/linker.ld b/targets/x86_64/linker.ld index 804b688..fe7edb8 100644 --- a/targets/x86_64/linker.ld +++ b/targets/x86_64/linker.ld @@ -1,16 +1,45 @@ ENTRY(start) +BOOT_LMA = 0x100000; +KERNEL_VMA = 0xFFFFFFFF80000000; + SECTIONS { - . = 1M; + . = BOOT_LMA; - .boot : + .multiboot : { KEEP(*(.multiboot)) } - .text : + .boot.text : + { + *(.boot.text) + } + + KERNEL_LMA = .; + . += KERNEL_VMA; + + .text ALIGN(4096) : AT(ADDR(.text) - KERNEL_VMA) { *(.text) } + + .rodata ALIGN(4096) : AT(ADDR(.rodata) - KERNEL_VMA) + { + *(.rodata*) + } + + .data ALIGN(4096) : AT(ADDR(.data) - KERNEL_VMA) + { + *(.data) + } + + .bss ALIGN(4096) : AT(ADDR(.bss) - KERNEL_VMA) + { + *(COMMON) + *(.bss) + } + + KERNEL_END_VMA = .; } From 50cfcda4c5c8502e56961d8dd9a3009a1fc7cad6 Mon Sep 17 00:00:00 2001 From: Bradley Myers Date: Thu, 16 Nov 2023 15:09:50 -0500 Subject: [PATCH 3/7] Moved scripts to separate directory --- debug.sh | 8 -------- build.sh => scripts/build.sh | 0 scripts/debug.sh | 18 ++++++++++++++++++ 3 files changed, 18 insertions(+), 8 deletions(-) delete mode 100755 debug.sh rename build.sh => scripts/build.sh (100%) mode change 100755 => 100644 create mode 100644 scripts/debug.sh diff --git a/debug.sh b/debug.sh deleted file mode 100755 index b76ecb1..0000000 --- a/debug.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -set -e - -sh build.sh -qemu-system-x86_64 -cdrom dist/x86_64/kernel.iso -s -S & -gdb --quiet -ex "target remote localhost:1234" -ex "set disassembly-flavor intel" \ - -ex "symbol-file dist/x86_64/kernel.bin" diff --git a/build.sh b/scripts/build.sh old mode 100755 new mode 100644 similarity index 100% rename from build.sh rename to scripts/build.sh diff --git a/scripts/debug.sh b/scripts/debug.sh new file mode 100644 index 0000000..580c061 --- /dev/null +++ b/scripts/debug.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +set -e + +this_dir="${0%/*}" +proj_root_dir="$(cd $this_dir/..; pwd)" + +# Build the project +sh $this_dir/build.sh + +# Run the qemu emulator and gdb in a subshell +# Set the subshell directory to the project root +( + cd $proj_root_dir && + qemu-system-x86_64 -cdrom dist/x86_64/kernel.iso -s -S & + gdb --quiet -ex "target remote localhost:1234" -ex "set disassembly-flavor intel" \ + -ex "symbol-file dist/x86_64/kernel.bin" +) From b4721cefd2f550f8fb299e4653ab26692885755e Mon Sep 17 00:00:00 2001 From: Bradley Myers Date: Mon, 20 Nov 2023 00:37:51 -0500 Subject: [PATCH 4/7] Added multiboot2 information structure parser --- Makefile | 2 +- scripts/build.sh | 0 scripts/debug.sh | 0 src/intf/isr.h | 2 - src/intf/kernel/mb2_parser.h | 10 ++ src/intf/klib/stdio.h | 16 +++ src/intf/multiboot2.h | 213 +++++++++++++++++++++++++++++ src/kernel/main.cpp | 18 ++- src/kernel/mb2_parser.cpp | 122 +++++++++++++++++ src/x86_64/boot/boot.asm | 3 + src/x86_64/boot/test_long_mode.asm | 2 +- src/x86_64/exception_handlers.cpp | 8 +- src/x86_64/klib/stdio.cpp | 139 +++++++++++++++++++ 13 files changed, 520 insertions(+), 15 deletions(-) mode change 100644 => 100755 scripts/build.sh mode change 100644 => 100755 scripts/debug.sh create mode 100644 src/intf/kernel/mb2_parser.h create mode 100644 src/intf/klib/stdio.h create mode 100644 src/intf/multiboot2.h create mode 100644 src/kernel/mb2_parser.cpp create mode 100644 src/x86_64/klib/stdio.cpp diff --git a/Makefile b/Makefile index a31df7b..6ea6377 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -CFLAGS = -c -g -ffreestanding -mno-red-zone -mno-mmx -mno-sse -mno-sse2 -fstack-protector -mgeneral-regs-only -fPIC -mcmodel=large -Wall -Wextra +CFLAGS = -c -g -ffreestanding -mno-red-zone -mno-mmx -mno-sse -mno-sse2 -fno-stack-protector -mgeneral-regs-only -fPIC -mcmodel=large -Wall -Wextra ASFLAGS = -g -F dwarf LDFLAGS = -n diff --git a/scripts/build.sh b/scripts/build.sh old mode 100644 new mode 100755 diff --git a/scripts/debug.sh b/scripts/debug.sh old mode 100644 new mode 100755 diff --git a/src/intf/isr.h b/src/intf/isr.h index 8bb8a6d..a710a39 100644 --- a/src/intf/isr.h +++ b/src/intf/isr.h @@ -2,8 +2,6 @@ #include -#include "drivers/video/vga.h" - /// @brief Represents the stack frame for an exception including /// the interrupt number, the error code, and registers for debugging. typedef struct __attribute__((packed)) Exception_Stack_Frame { diff --git a/src/intf/kernel/mb2_parser.h b/src/intf/kernel/mb2_parser.h new file mode 100644 index 0000000..095d527 --- /dev/null +++ b/src/intf/kernel/mb2_parser.h @@ -0,0 +1,10 @@ +#pragma once + +#include "multiboot2.h" + +extern "C" uintptr_t KERNEL_VMA; + +/// @brief Parses the multiboot2 information structure for relevant +/// information to the kernel and updates data with the parsed values. +/// @param MBI A pointer to the multiboot2 information structure. +void parse_multiboot(const MB2_information_structure* MBI); diff --git a/src/intf/klib/stdio.h b/src/intf/klib/stdio.h new file mode 100644 index 0000000..28fe839 --- /dev/null +++ b/src/intf/klib/stdio.h @@ -0,0 +1,16 @@ +#pragma once + +#define PRINTF_BUFFER_SIZE (8 * sizeof(int) + 1) // 1 char per byte + null terminator + +/// @brief Converts an integer into a string with a radix. +/// @param val The integer to convert. +/// @param buf The string buffer to write to. +/// @param radix The radix of the integer to write. +/// @return A pointer to the written buffer. +char* itoa(int val, char* buf, int radix); + +/// @brief Writes the string pointed to by `fmt` to the VGA buffer. +/// When format specifiers are present, they are replaced by the +/// corresponding additional arguments. +/// @param fmt The string with optional format specifiers to print. +void printf(const char* fmt, ...); diff --git a/src/intf/multiboot2.h b/src/intf/multiboot2.h new file mode 100644 index 0000000..9f3d60f --- /dev/null +++ b/src/intf/multiboot2.h @@ -0,0 +1,213 @@ +#pragma once + +#include + +#define MB2_MAGIC 0x36D76289 + +#define MB2_TYPE_END 0 +#define MB2_TYPE_CMDLINE 1 +#define MB2_TYPE_BOOTLOADER 2 +#define MB2_TYPE_MODULES 3 +#define MB2_TYPE_MEMINFO 4 +#define MB2_TYPE_BIOSDEV 5 +#define MB2_TYPE_MEMMAP 6 +#define MB2_TYPE_VBEINFO 7 +#define MB2_TYPE_FBUFFINFO 8 +#define MB2_TYPE_ELFSYMBOLS 9 +#define MB2_TYPE_APMTABLE 10 +#define MB2_TYPE_EFISYSTAB32 11 +#define MB2_TYPE_EFISYSTAB64 12 +#define MB2_TYPE_SMBIOSTAB 13 +#define MB2_TYPE_ACPIOLD 14 +#define MB2_TYPE_ACPINEW 15 +#define MB2_TYPE_NETWORK 16 +#define MB2_TYPE_EFIMEMMAP 17 +#define MB2_TYPE_EFIBOOTNT 18 +#define MB2_TYPE_EFIIMGHND32 19 +#define MB2_TYPE_EFIIMGHND64 20 +#define MB2_TYPE_IMGLDBPA 21 + +#define MB2_MEM_AVAILABLE 1 +#define MB2_MEM_RESERVED 2 +#define MB2_MEM_USABLE_ACPI 3 +#define MB2_MEM_PRESERVE 4 +#define MB2_MEM_DEFECTIVE 5 + +#define MB2_FBUFF_TYPE_INDEXED 0 +#define MB2_FBUFF_TYPE_RGB 1 +#define MB2_FBUFF_TYPE_EGA_TEXT 2 + +typedef struct __attribute__((packed)) MB2_tag { + uint32_t type; + uint32_t size; +} MB2_tag; + +typedef struct __attribute__((packed)) MB2_information_structure { + uint32_t size; + uint32_t reserved; +} MB2_information_structure; + +typedef struct __attribute__((packed)) MB2_tag_cmdline { + uint32_t type; + uint32_t size; + uint8_t* string; +} MB2_tag_cmdline; + +typedef struct __attribute__((packed)) MB2_tag_bootloader { + uint32_t type; + uint32_t size; + uint8_t* string; +} MB2_tag_bootloader; + +typedef struct __attribute__((packed)) MB2_tag_modules { + uint32_t type; + uint32_t size; + uint32_t mod_start; + uint32_t mod_end; + uint8_t* string; +} MB2_tag_modules; + +typedef struct __attribute__((packed)) MB2_tag_meminfo { + uint32_t type; + uint32_t size; + uint32_t mem_lower; + uint32_t mem_upper; +} MB2_tag_meminfo; + +typedef struct __attribute__((packed)) MB2_tag_biosdev { + uint32_t type; + uint32_t size; + uint32_t biosdev; + uint32_t partition; + uint32_t sub_partition; +} MB2_tag_biosdev; + +typedef struct __attribute__((packed)) MB2_memmap_entry { + uint64_t base_addr; + uint64_t length; + uint32_t type; + uint32_t reserved; +} MB2_memmap_entry; + +typedef struct __attribute__((packed)) MB2_tag_memmap { + uint32_t type; + uint32_t size; + uint32_t entry_size; + uint32_t entry_version; + MB2_memmap_entry* entries; +} MB2_tag_memmap; + +typedef struct __attribute__((packed)) MB2_tag_vbeinfo { + uint32_t type; + uint32_t size; + uint16_t vbe_mode; + uint16_t vbe_interface_seg; + uint16_t vbe_interface_off; + uint16_t vbe_interface_len; + uint8_t vbe_control_info[512]; + uint8_t vbe_mode_info[256]; +} MB2_tag_vbeinfo; + +typedef struct __attribute__((packed)) MB2_color { + uint8_t red; + uint8_t green; + uint8_t blue; +} MB2_color; + +typedef struct __attribute__((packed)) MB2_tag_fbuffinfo { + uint32_t type; + uint32_t size; + uint64_t framebuffer_addr; + uint32_t framebuffer_pitch; + uint32_t framebuffer_width; + uint32_t framebuffer_height; + uint8_t framebuffer_bpp; + uint8_t framebuffer_type; + uint8_t reserved; + + union color_info + { + struct { + uint16_t framebuffer_palette_num_colors; + MB2_color* framebuffer_palette; + }; + struct { + uint8_t framebuffer_red_field_position; + uint8_t framebuffer_red_mask_size; + uint8_t framebuffer_green_field_position; + uint8_t framebuffer_green_mask_size; + uint8_t framebuffer_blue_field_position; + uint8_t framebuffer_blue_mask_size; + }; + }; +} MB2_tag_fbuffinfo; + +typedef struct __attribute__((packed)) MB2_tag_efisystab32 { + uint32_t type; + uint32_t size; + uint32_t pointer; +} MB2_tag_efisystab32; + +typedef struct __attribute__((packed)) MB2_tag_efisystab64 { + uint32_t type; + uint32_t size; + uint64_t pointer; +} MB2_tag_efisystab64; + +typedef struct __attribute__((packed)) MB2_tag_smbiostab { + uint32_t type; + uint32_t size; + uint8_t major; + uint8_t minor; + uint8_t reserved[6]; + uint8_t* smbios_tables; +} MB2_tag_smbiostab; + +typedef struct __attribute__((packed)) MB2_tag_acpiold { + uint32_t type; + uint32_t size; + uint8_t* rsdp; +} MB2_tag_acpiold; + +typedef struct __attribute__((packed)) MB2_tag_acpinew { + uint32_t type; + uint32_t size; + uint8_t* rsdp; +} MB2_tag_acpinew; + +typedef struct __attribute__((packed)) MB2_tag_network { + uint32_t type; + uint32_t size; + uint8_t* dhcpack; +} MB2_tag_network; + +typedef struct __attribute__((packed)) MB2_tag_efimemmap { + uint32_t type; + uint32_t size; + uint32_t descriptor_size; + uint32_t descriptor_version; + uint8_t* efi_memmap; +} MB2_tag_efimemmap; + +typedef struct __attribute__((packed)) MB2_tag_efibootnt { + uint32_t type; + uint32_t size; +} MB2_tag_efibootnt; + +typedef struct __attribute__((packed)) MB2_efiimghnd32 { + uint32_t type; + uint32_t size; + uint32_t pointer; +} MB2_efiimghnd32; + +typedef struct __attribute__((packed)) MB2_efiimghnd64 { + uint32_t type; + uint32_t size; + uint64_t pointer; +} MB2_efiimghnd64; + +typedef struct __attribute__((packed)) MB2_tag_imgldbpa { + uint32_t type; + uint32_t size; + int32_t load_base_addr; +} MB2_tag_imgldbpa; diff --git a/src/kernel/main.cpp b/src/kernel/main.cpp index 24dd79a..8dc5632 100644 --- a/src/kernel/main.cpp +++ b/src/kernel/main.cpp @@ -1,6 +1,9 @@ #include "drivers/video/vga.h" +#include "klib/stdio.h" #include "pic.h" #include "idt.h" +#include "multiboot2.h" +#include "kernel/mb2_parser.h" namespace VGA = drivers::video::VGA; namespace Color = VGA::Color; @@ -8,8 +11,11 @@ namespace Color = VGA::Color; void print_welcome(); /// @brief The entry point into the StasisOS kernel. -extern "C" void kernel_main() +extern "C" void kernel_main(const MB2_information_structure* MBI) { + // Parse the relevant multiboot information + parse_multiboot(MBI); + // Remap the PIC interrupt vectors to 0x20..0x2F remap_pic(0x20, 0x28); @@ -22,8 +28,7 @@ extern "C" void kernel_main() print_welcome(); // Create temporary text input prompt - VGA::print_str("\n\n"); - VGA::print_str("> "); + printf("\n\n> "); while (true); } @@ -32,12 +37,11 @@ extern "C" void kernel_main() void print_welcome() { VGA::set_color(Color::GREEN, Color::BLACK); - VGA::print_str("Welcome to StasisOS!\n"); - VGA::print_chr('\n'); + printf("Welcome to StasisOS!\n\n"); VGA::set_color(Color::WHITE, Color::BLACK); - VGA::print_str("The OS that is unchanging. The OS that is always in equilibrium.\n\n"); + printf("The OS that is unchanging. The OS that is always in equilibrium.\n\n"); VGA::set_color(Color::LIGHT_GRAY, Color::BLACK); - VGA::print_str("Copyright (c) 2023 Bradley Myers. All rights reserved."); + printf("Copyright (c) 2023 Bradley Myers. All rights reserved."); } diff --git a/src/kernel/mb2_parser.cpp b/src/kernel/mb2_parser.cpp new file mode 100644 index 0000000..8b257b5 --- /dev/null +++ b/src/kernel/mb2_parser.cpp @@ -0,0 +1,122 @@ +#include "kernel/mb2_parser.h" + +#include "drivers/video/vga.h" +#include "klib/stdio.h" + +namespace VGA = drivers::video::VGA; +namespace Color = VGA::Color; + +void parse_multiboot(const MB2_information_structure* MBI) +{ + const uintptr_t mbi_addr = reinterpret_cast(MBI); + + // Ensure the MBI is properly aligned + if (mbi_addr & 7) + { + VGA::set_color(Color::RED, Color::BLACK); + printf("Err: Unaligned MBI"); + return; + } + + VGA::clear_screen(); + VGA::set_color(Color::CYAN, Color::BLACK); + + for (MB2_tag* tag = reinterpret_cast(mbi_addr + sizeof(MBI)); + tag->type != MB2_TYPE_END; + tag = reinterpret_cast(reinterpret_cast(tag) + ((tag->size + 7) & ~7))) + { + switch (tag->type) + { + case MB2_TYPE_CMDLINE: + printf("Command line found.\n"); + break; + + case MB2_TYPE_BOOTLOADER: + printf("Boot loader found.\n"); + break; + + case MB2_TYPE_MODULES: + printf("Module found.\n"); + break; + + case MB2_TYPE_MEMINFO: + printf("Memory info found.\n"); + printf("\tLower: 0x%x\n", reinterpret_cast(tag)->mem_lower); + printf("\tUpper: 0x%x\n", reinterpret_cast(tag)->mem_upper); + break; + + case MB2_TYPE_BIOSDEV: + printf("Bios boot device found.\n"); + break; + + case MB2_TYPE_MEMMAP: + printf("Memory map found.\n"); + break; + + case MB2_TYPE_VBEINFO: + printf("VBE info found.\n"); + break; + + case MB2_TYPE_FBUFFINFO: + printf("Frame buffer info found.\n"); + break; + + case MB2_TYPE_ELFSYMBOLS: + printf("ELF symbols info found.\n"); + break; + + case MB2_TYPE_APMTABLE: + printf("APM table info found.\n"); + break; + + case MB2_TYPE_EFISYSTAB32: + printf("EFI x32 system table info found.\n"); + break; + + case MB2_TYPE_EFISYSTAB64: + printf("EFI x64 system table info found.\n"); + break; + + case MB2_TYPE_SMBIOSTAB: + printf("SM BIOS table info found.\n"); + break; + + case MB2_TYPE_ACPIOLD: + printf("Old ACPI info found.\n"); + break; + + case MB2_TYPE_ACPINEW: + printf("New ACPI info found.\n"); + break; + + case MB2_TYPE_NETWORK: + printf("Network info found.\n"); + break; + + case MB2_TYPE_EFIMEMMAP: + printf("EFI Memory Map found.\n"); + break; + + case MB2_TYPE_EFIBOOTNT: + printf("EFI boot not terminated.\n"); + break; + + case MB2_TYPE_EFIIMGHND32: + printf("EFI x32 image handle found.\n"); + break; + + case MB2_TYPE_EFIIMGHND64: + printf("EFI x64 image handle found.\n"); + break; + + case MB2_TYPE_IMGLDBPA: + printf("Image base address found.\n"); + printf("\t0x%x\n", reinterpret_cast(tag)->load_base_addr); + break; + + default: + printf("Unrecognized tag found.\n"); + break; + } + } +} diff --git a/src/x86_64/boot/boot.asm b/src/x86_64/boot/boot.asm index d63ea76..dc2e6aa 100644 --- a/src/x86_64/boot/boot.asm +++ b/src/x86_64/boot/boot.asm @@ -14,6 +14,9 @@ section .boot.text progbits alloc exec nowrite align=16 start: cli ; clear interrupts + mov esi, eax ; save MB2 magic + mov edi, ebx ; save MBI pointer + ; setup the stack mov ebp, stack_top - KERNEL_VMA mov esp, ebp diff --git a/src/x86_64/boot/test_long_mode.asm b/src/x86_64/boot/test_long_mode.asm index 2b1081f..9b36217 100644 --- a/src/x86_64/boot/test_long_mode.asm +++ b/src/x86_64/boot/test_long_mode.asm @@ -10,7 +10,7 @@ section .boot.text ; tests for multiboot capability test_multiboot: - cmp eax, 0x36D76289 + cmp esi, 0x36D76289 jne .no_multiboot ret diff --git a/src/x86_64/exception_handlers.cpp b/src/x86_64/exception_handlers.cpp index 1551ec7..5115631 100644 --- a/src/x86_64/exception_handlers.cpp +++ b/src/x86_64/exception_handlers.cpp @@ -1,5 +1,8 @@ #include "isr.h" +#include "drivers/video/vga.h" +#include "klib/stdio.h" + namespace VGA = drivers::video::VGA; namespace Color = VGA::Color; @@ -20,9 +23,6 @@ const char* exception_names[] = { /// @param frame The stack frame for the exception. void exception_handler(Exception_Stack_Frame frame) { - const char* exception_name = exception_names[frame.int_no]; - VGA::set_color(Color::WHITE, Color::RED); - VGA::print_str("ERR: #"); - VGA::print_str(exception_name); + printf("ERR: #%s", exception_names[frame.int_no]); } diff --git a/src/x86_64/klib/stdio.cpp b/src/x86_64/klib/stdio.cpp new file mode 100644 index 0000000..2a1a788 --- /dev/null +++ b/src/x86_64/klib/stdio.cpp @@ -0,0 +1,139 @@ +#include "klib/stdio.h" +#include "drivers/video/vga.h" +#include + +namespace VGA = drivers::video::VGA; +namespace Color = VGA::Color; + +static inline void swap(char& a, char& b) +{ + char temp = a; + a = b; + b = temp; +} + +static void reverse(char* str, int length) +{ + int start = 0; + int end = length - 1; + while (start < end) + { + swap(str[start], str[end]); + start++; + end--; + } +} + +char* itoa(int val, char* buf, int radix) +{ + // Handle 0 explicitly, otherwise empty string is printed + if (val == 0) + { + buf[0] = '0'; + buf[1] = '\0'; + return buf; + } + + // Process individual digits + int i = 0; + bool isNegative = false; + + if (val < 0 && radix == 10) + { + isNegative = true; + val = -val; + } + + if (radix == 16) + { + while (val != 0) + { + int rem = val % radix; + buf[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0'; + val = val / radix; + } + } + else + { + while (val != 0) + { + int rem = val % radix; + buf[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0'; + val = val / radix; + } + } + + // Append negative sign for base 10 + if (isNegative && radix == 10) + { + buf[i++] = '-'; + } + + buf[i] = '\0'; + + reverse(buf, i); + return buf; +} + +void printf(const char* fmt, ...) +{ + char buf[PRINTF_BUFFER_SIZE]; + + va_list args; + va_start(args, fmt); + + while (*fmt != '\0') + { + if (*fmt != '%') + { + VGA::print_chr(*fmt); + } + else + { + fmt++; + switch (*fmt) + { + case '%': + { + // Escaped % character + VGA::print_chr('%'); + break; + } + case 'd': + { + int value = va_arg(args, int); + itoa(value, buf, 10); + VGA::print_str(buf); + break; + } + case 's': + { + const char* str = va_arg(args, const char*); + VGA::print_str(str); + break; + } + case 'x': + { + int value = va_arg(args, int); + itoa(value, buf, 16); + VGA::print_str(buf); + break; + } + case 'c': + { + char c = va_arg(args, int); + VGA::print_chr(c); + break; + } + + default: + printf("Err: unsupported format %%%c", *fmt); + break; + } + } + + fmt++; + } + + va_end(args); +} From 6aafd42d4a332db98a5137d8c952a545c9f324ac Mon Sep 17 00:00:00 2001 From: Bradley Myers Date: Tue, 21 Nov 2023 20:14:26 -0500 Subject: [PATCH 5/7] Added kpanic function and attribute macros --- src/intf/drivers/keyboard/irq_handler.h | 6 +-- src/intf/drivers/video/vga.h | 3 +- src/intf/idt.h | 10 ++--- src/intf/isr.h | 3 +- src/intf/kernel/util/panic.h | 8 ++++ src/intf/klib/stdio.h | 9 ++++ src/intf/multiboot2.h | 47 +++++++++++---------- src/intf/util/asm_snippets.h | 6 +++ src/intf/util/attributes.h | 5 +++ src/kernel/main.cpp | 1 + src/kernel/util/panic.cpp | 23 ++++++++++ src/x86_64/boot/boot64.asm | 4 +- src/x86_64/drivers/keyboard/irq_handler.cpp | 4 +- src/x86_64/exception_handlers.cpp | 4 +- src/x86_64/klib/stdio.cpp | 25 +++++++---- 15 files changed, 108 insertions(+), 50 deletions(-) create mode 100644 src/intf/kernel/util/panic.h create mode 100644 src/intf/util/asm_snippets.h create mode 100644 src/intf/util/attributes.h create mode 100644 src/kernel/util/panic.cpp diff --git a/src/intf/drivers/keyboard/irq_handler.h b/src/intf/drivers/keyboard/irq_handler.h index ff06d91..6e6bcf6 100644 --- a/src/intf/drivers/keyboard/irq_handler.h +++ b/src/intf/drivers/keyboard/irq_handler.h @@ -1,12 +1,12 @@ #pragma once #include "keyboard.h" +#include "util/attributes.h" namespace drivers::keyboard { - /// @brief Required by __attribute__((interrupt)) + /// @brief Required by __interrupt struct interrupt_frame; - __attribute__((interrupt)) - void irq_handler(interrupt_frame *frame); + void __interrupt irq_handler(interrupt_frame *frame); } diff --git a/src/intf/drivers/video/vga.h b/src/intf/drivers/video/vga.h index 2da08f3..cb48217 100644 --- a/src/intf/drivers/video/vga.h +++ b/src/intf/drivers/video/vga.h @@ -2,6 +2,7 @@ #include #include +#include "util/attributes.h" #define VGA_BUFFER ((VGA_Cell*) 0xB8000) @@ -47,7 +48,7 @@ namespace drivers::video::VGA } Cursor; /// @brief Represents a single cell in the VGA tracking both the character and the brush for that character. - typedef struct __attribute__((packed)) VGA_Cell { + typedef struct __packed VGA_Cell { uint8_t character; uint8_t color; } VGA_Cell; diff --git a/src/intf/idt.h b/src/intf/idt.h index 444886b..3ca740a 100644 --- a/src/intf/idt.h +++ b/src/intf/idt.h @@ -4,6 +4,7 @@ #include "isr.h" #include "gdt.h" #include "pic.h" +#include "util/attributes.h" #define IDT_ENTRIES 256 @@ -13,7 +14,7 @@ #define ENTRY_GATE_TRAP 0xF /// @brief Represents an IDT entry in the appropriate memory layout. -typedef struct __attribute__((packed)) IDT_Entry { +typedef struct __packed IDT_Entry { uint16_t offset_low; uint16_t segment; // GDT segment on which to run the ISR uint8_t ist; // 3 bit offset into the IST (ignored if 0) @@ -24,14 +25,9 @@ typedef struct __attribute__((packed)) IDT_Entry { } IDT_Entry; /// @brief Represents the IDT pointer to be loaded into the idtr register. -typedef struct __attribute__((packed)) IDT_Ptr { +typedef struct __packed IDT_Ptr { uint16_t limit; uint64_t base; } IDT_Ptr; extern "C" void init_idt(); - -/// @brief `cli` -inline void __clear_interrupts() { asm volatile("cli"); } -/// @brief `sti` -inline void __enable_interrupts() { asm volatile("sti"); } diff --git a/src/intf/isr.h b/src/intf/isr.h index a710a39..5facc69 100644 --- a/src/intf/isr.h +++ b/src/intf/isr.h @@ -1,10 +1,11 @@ #pragma once #include +#include "util/attributes.h" /// @brief Represents the stack frame for an exception including /// the interrupt number, the error code, and registers for debugging. -typedef struct __attribute__((packed)) Exception_Stack_Frame { +typedef struct __packed Exception_Stack_Frame { uint64_t rdi, rsi, rbp, rdx, rcx, rbx, rax; uint64_t int_no, err_code; uint64_t rip, cs, rflags, rsp, ss; diff --git a/src/intf/kernel/util/panic.h b/src/intf/kernel/util/panic.h new file mode 100644 index 0000000..eb200b6 --- /dev/null +++ b/src/intf/kernel/util/panic.h @@ -0,0 +1,8 @@ +#pragma once + +#include "util/attributes.h" + +/// @brief Prints a panic message to the screen in white on red. +/// Clears interrupts and hangs the CPU indefinitely. +/// @param fmt The message to print. +void __noreturn kpanic(const char* fmt, ...); diff --git a/src/intf/klib/stdio.h b/src/intf/klib/stdio.h index 28fe839..5750a2f 100644 --- a/src/intf/klib/stdio.h +++ b/src/intf/klib/stdio.h @@ -1,5 +1,7 @@ #pragma once +#include + #define PRINTF_BUFFER_SIZE (8 * sizeof(int) + 1) // 1 char per byte + null terminator /// @brief Converts an integer into a string with a radix. @@ -14,3 +16,10 @@ char* itoa(int val, char* buf, int radix); /// corresponding additional arguments. /// @param fmt The string with optional format specifiers to print. void printf(const char* fmt, ...); + +/// @brief Writes the string pointed to by `fmt` to the VGA buffer. +/// When format specefiers are present, they are replaced by the +/// corresponding additional arguments. +/// @param fmt The string with optional format specifiers to print. +/// @param args The argument list. +void vprintf(const char* fmt, va_list args); diff --git a/src/intf/multiboot2.h b/src/intf/multiboot2.h index 9f3d60f..fc7046c 100644 --- a/src/intf/multiboot2.h +++ b/src/intf/multiboot2.h @@ -1,6 +1,7 @@ #pragma once #include +#include "util/attributes.h" #define MB2_MAGIC 0x36D76289 @@ -37,29 +38,29 @@ #define MB2_FBUFF_TYPE_RGB 1 #define MB2_FBUFF_TYPE_EGA_TEXT 2 -typedef struct __attribute__((packed)) MB2_tag { +typedef struct __packed MB2_tag { uint32_t type; uint32_t size; } MB2_tag; -typedef struct __attribute__((packed)) MB2_information_structure { +typedef struct __packed MB2_information_structure { uint32_t size; uint32_t reserved; } MB2_information_structure; -typedef struct __attribute__((packed)) MB2_tag_cmdline { +typedef struct __packed MB2_tag_cmdline { uint32_t type; uint32_t size; uint8_t* string; } MB2_tag_cmdline; -typedef struct __attribute__((packed)) MB2_tag_bootloader { +typedef struct __packed MB2_tag_bootloader { uint32_t type; uint32_t size; uint8_t* string; } MB2_tag_bootloader; -typedef struct __attribute__((packed)) MB2_tag_modules { +typedef struct __packed MB2_tag_modules { uint32_t type; uint32_t size; uint32_t mod_start; @@ -67,14 +68,14 @@ typedef struct __attribute__((packed)) MB2_tag_modules { uint8_t* string; } MB2_tag_modules; -typedef struct __attribute__((packed)) MB2_tag_meminfo { +typedef struct __packed MB2_tag_meminfo { uint32_t type; uint32_t size; uint32_t mem_lower; uint32_t mem_upper; } MB2_tag_meminfo; -typedef struct __attribute__((packed)) MB2_tag_biosdev { +typedef struct __packed MB2_tag_biosdev { uint32_t type; uint32_t size; uint32_t biosdev; @@ -82,14 +83,14 @@ typedef struct __attribute__((packed)) MB2_tag_biosdev { uint32_t sub_partition; } MB2_tag_biosdev; -typedef struct __attribute__((packed)) MB2_memmap_entry { +typedef struct __packed MB2_memmap_entry { uint64_t base_addr; uint64_t length; uint32_t type; uint32_t reserved; } MB2_memmap_entry; -typedef struct __attribute__((packed)) MB2_tag_memmap { +typedef struct __packed MB2_tag_memmap { uint32_t type; uint32_t size; uint32_t entry_size; @@ -97,7 +98,7 @@ typedef struct __attribute__((packed)) MB2_tag_memmap { MB2_memmap_entry* entries; } MB2_tag_memmap; -typedef struct __attribute__((packed)) MB2_tag_vbeinfo { +typedef struct __packed MB2_tag_vbeinfo { uint32_t type; uint32_t size; uint16_t vbe_mode; @@ -108,13 +109,13 @@ typedef struct __attribute__((packed)) MB2_tag_vbeinfo { uint8_t vbe_mode_info[256]; } MB2_tag_vbeinfo; -typedef struct __attribute__((packed)) MB2_color { +typedef struct __packed MB2_color { uint8_t red; uint8_t green; uint8_t blue; } MB2_color; -typedef struct __attribute__((packed)) MB2_tag_fbuffinfo { +typedef struct __packed MB2_tag_fbuffinfo { uint32_t type; uint32_t size; uint64_t framebuffer_addr; @@ -142,19 +143,19 @@ typedef struct __attribute__((packed)) MB2_tag_fbuffinfo { }; } MB2_tag_fbuffinfo; -typedef struct __attribute__((packed)) MB2_tag_efisystab32 { +typedef struct __packed MB2_tag_efisystab32 { uint32_t type; uint32_t size; uint32_t pointer; } MB2_tag_efisystab32; -typedef struct __attribute__((packed)) MB2_tag_efisystab64 { +typedef struct __packed MB2_tag_efisystab64 { uint32_t type; uint32_t size; uint64_t pointer; } MB2_tag_efisystab64; -typedef struct __attribute__((packed)) MB2_tag_smbiostab { +typedef struct __packed MB2_tag_smbiostab { uint32_t type; uint32_t size; uint8_t major; @@ -163,25 +164,25 @@ typedef struct __attribute__((packed)) MB2_tag_smbiostab { uint8_t* smbios_tables; } MB2_tag_smbiostab; -typedef struct __attribute__((packed)) MB2_tag_acpiold { +typedef struct __packed MB2_tag_acpiold { uint32_t type; uint32_t size; uint8_t* rsdp; } MB2_tag_acpiold; -typedef struct __attribute__((packed)) MB2_tag_acpinew { +typedef struct __packed MB2_tag_acpinew { uint32_t type; uint32_t size; uint8_t* rsdp; } MB2_tag_acpinew; -typedef struct __attribute__((packed)) MB2_tag_network { +typedef struct __packed MB2_tag_network { uint32_t type; uint32_t size; uint8_t* dhcpack; } MB2_tag_network; -typedef struct __attribute__((packed)) MB2_tag_efimemmap { +typedef struct __packed MB2_tag_efimemmap { uint32_t type; uint32_t size; uint32_t descriptor_size; @@ -189,24 +190,24 @@ typedef struct __attribute__((packed)) MB2_tag_efimemmap { uint8_t* efi_memmap; } MB2_tag_efimemmap; -typedef struct __attribute__((packed)) MB2_tag_efibootnt { +typedef struct __packed MB2_tag_efibootnt { uint32_t type; uint32_t size; } MB2_tag_efibootnt; -typedef struct __attribute__((packed)) MB2_efiimghnd32 { +typedef struct __packed MB2_efiimghnd32 { uint32_t type; uint32_t size; uint32_t pointer; } MB2_efiimghnd32; -typedef struct __attribute__((packed)) MB2_efiimghnd64 { +typedef struct __packed MB2_efiimghnd64 { uint32_t type; uint32_t size; uint64_t pointer; } MB2_efiimghnd64; -typedef struct __attribute__((packed)) MB2_tag_imgldbpa { +typedef struct __packed MB2_tag_imgldbpa { uint32_t type; uint32_t size; int32_t load_base_addr; diff --git a/src/intf/util/asm_snippets.h b/src/intf/util/asm_snippets.h new file mode 100644 index 0000000..1f3970d --- /dev/null +++ b/src/intf/util/asm_snippets.h @@ -0,0 +1,6 @@ +#pragma once + +#define __clear_interrupts() asm volatile("cli") +#define __enable_interrupts() asm volatile("sti") + +#define __hlt() asm volatile("hlt") diff --git a/src/intf/util/attributes.h b/src/intf/util/attributes.h new file mode 100644 index 0000000..6c6ab08 --- /dev/null +++ b/src/intf/util/attributes.h @@ -0,0 +1,5 @@ +#pragma once + +#define __noreturn __attribute__((noreturn)) +#define __packed __attribute__((packed)) +#define __interrupt __attribute__((interrupt)) diff --git a/src/kernel/main.cpp b/src/kernel/main.cpp index 8dc5632..d6600d9 100644 --- a/src/kernel/main.cpp +++ b/src/kernel/main.cpp @@ -2,6 +2,7 @@ #include "klib/stdio.h" #include "pic.h" #include "idt.h" +#include "util/asm_snippets.h" #include "multiboot2.h" #include "kernel/mb2_parser.h" diff --git a/src/kernel/util/panic.cpp b/src/kernel/util/panic.cpp new file mode 100644 index 0000000..48ee721 --- /dev/null +++ b/src/kernel/util/panic.cpp @@ -0,0 +1,23 @@ +#include "kernel/util/panic.h" + +#include "drivers/video/vga.h" +#include "klib/stdio.h" +#include "util/asm_snippets.h" +#include + +namespace VGA = drivers::video::VGA; +namespace Color = VGA::Color; + +void __noreturn kpanic(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + + VGA::set_color(Color::WHITE, Color::RED); + printf("[PANIC] "); + vprintf(fmt, args); + + __clear_interrupts(); + __hlt(); + __builtin_unreachable(); +} diff --git a/src/x86_64/boot/boot64.asm b/src/x86_64/boot/boot64.asm index 2798f0d..b1d996b 100644 --- a/src/x86_64/boot/boot64.asm +++ b/src/x86_64/boot/boot64.asm @@ -31,6 +31,4 @@ kernel_start: ; hang the CPU if the kernel falls through cli - .hang: - hlt - jmp .hang + hlt diff --git a/src/x86_64/drivers/keyboard/irq_handler.cpp b/src/x86_64/drivers/keyboard/irq_handler.cpp index 5b8f424..5b90b75 100644 --- a/src/x86_64/drivers/keyboard/irq_handler.cpp +++ b/src/x86_64/drivers/keyboard/irq_handler.cpp @@ -9,8 +9,8 @@ namespace Color = VGA::Color; namespace drivers::keyboard { /// @brief The handler for the keyboard's IRQ1. - /// @param frame Required for __attribute__((interrupt)) - void irq_handler(struct interrupt_frame *frame) + /// @param frame Required for __interrupt + void __interrupt irq_handler(struct interrupt_frame *frame) { sendEOI(1); // ACK and ask for scan code diff --git a/src/x86_64/exception_handlers.cpp b/src/x86_64/exception_handlers.cpp index 5115631..187cf54 100644 --- a/src/x86_64/exception_handlers.cpp +++ b/src/x86_64/exception_handlers.cpp @@ -2,6 +2,7 @@ #include "drivers/video/vga.h" #include "klib/stdio.h" +#include "kernel/util/panic.h" namespace VGA = drivers::video::VGA; namespace Color = VGA::Color; @@ -23,6 +24,5 @@ const char* exception_names[] = { /// @param frame The stack frame for the exception. void exception_handler(Exception_Stack_Frame frame) { - VGA::set_color(Color::WHITE, Color::RED); - printf("ERR: #%s", exception_names[frame.int_no]); + kpanic("ERR: #%s", exception_names[frame.int_no]); } diff --git a/src/x86_64/klib/stdio.cpp b/src/x86_64/klib/stdio.cpp index 2a1a788..6ae34bc 100644 --- a/src/x86_64/klib/stdio.cpp +++ b/src/x86_64/klib/stdio.cpp @@ -1,6 +1,5 @@ #include "klib/stdio.h" #include "drivers/video/vga.h" -#include namespace VGA = drivers::video::VGA; namespace Color = VGA::Color; @@ -77,11 +76,21 @@ char* itoa(int val, char* buf, int radix) void printf(const char* fmt, ...) { - char buf[PRINTF_BUFFER_SIZE]; - va_list args; va_start(args, fmt); + vprintf(fmt, args); + + va_end(args); +} + +void vprintf(const char* fmt, va_list args) +{ + char buf[PRINTF_BUFFER_SIZE]; + + va_list vargs; + va_copy(vargs, args); + while (*fmt != '\0') { if (*fmt != '%') @@ -101,27 +110,27 @@ void printf(const char* fmt, ...) } case 'd': { - int value = va_arg(args, int); + int value = va_arg(vargs, int); itoa(value, buf, 10); VGA::print_str(buf); break; } case 's': { - const char* str = va_arg(args, const char*); + const char* str = va_arg(vargs, const char*); VGA::print_str(str); break; } case 'x': { - int value = va_arg(args, int); + int value = va_arg(vargs, int); itoa(value, buf, 16); VGA::print_str(buf); break; } case 'c': { - char c = va_arg(args, int); + char c = va_arg(vargs, int); VGA::print_chr(c); break; } @@ -135,5 +144,5 @@ void printf(const char* fmt, ...) fmt++; } - va_end(args); + va_end(vargs); } From 9a9de5c5357f76d64d44cc35a8de4265c4de3566 Mon Sep 17 00:00:00 2001 From: Bradley Myers Date: Wed, 22 Nov 2023 00:07:30 -0500 Subject: [PATCH 6/7] Added memory map parser --- src/intf/kernel/mb2_parser.h | 5 +++++ src/intf/multiboot2.h | 30 +++++++++++++++--------------- src/kernel/mb2_parser.cpp | 35 +++++++++++++++++++++++++++-------- src/x86_64/klib/stdio.cpp | 4 ++-- 4 files changed, 49 insertions(+), 25 deletions(-) diff --git a/src/intf/kernel/mb2_parser.h b/src/intf/kernel/mb2_parser.h index 095d527..4f2073a 100644 --- a/src/intf/kernel/mb2_parser.h +++ b/src/intf/kernel/mb2_parser.h @@ -8,3 +8,8 @@ extern "C" uintptr_t KERNEL_VMA; /// information to the kernel and updates data with the parsed values. /// @param MBI A pointer to the multiboot2 information structure. void parse_multiboot(const MB2_information_structure* MBI); + +/// @brief Parses the multiboot2 memory map tag and entries to +/// collect information about the available memory regions. +/// @param memmap_tag The memory map tag to parse. +void parse_memmap(MB2_tag_memmap* memmap_tag); diff --git a/src/intf/multiboot2.h b/src/intf/multiboot2.h index fc7046c..77f97da 100644 --- a/src/intf/multiboot2.h +++ b/src/intf/multiboot2.h @@ -8,7 +8,7 @@ #define MB2_TYPE_END 0 #define MB2_TYPE_CMDLINE 1 #define MB2_TYPE_BOOTLOADER 2 -#define MB2_TYPE_MODULES 3 +#define MB2_TYPE_MODULE 3 #define MB2_TYPE_MEMINFO 4 #define MB2_TYPE_BIOSDEV 5 #define MB2_TYPE_MEMMAP 6 @@ -30,8 +30,8 @@ #define MB2_MEM_AVAILABLE 1 #define MB2_MEM_RESERVED 2 -#define MB2_MEM_USABLE_ACPI 3 -#define MB2_MEM_PRESERVE 4 +#define MB2_MEM_RECLAIMABLE 3 +#define MB2_MEM_NVS 4 #define MB2_MEM_DEFECTIVE 5 #define MB2_FBUFF_TYPE_INDEXED 0 @@ -51,22 +51,22 @@ typedef struct __packed MB2_information_structure { typedef struct __packed MB2_tag_cmdline { uint32_t type; uint32_t size; - uint8_t* string; + uint8_t string[]; } MB2_tag_cmdline; typedef struct __packed MB2_tag_bootloader { uint32_t type; uint32_t size; - uint8_t* string; + uint8_t string[]; } MB2_tag_bootloader; -typedef struct __packed MB2_tag_modules { +typedef struct __packed MB2_tag_module { uint32_t type; uint32_t size; uint32_t mod_start; uint32_t mod_end; - uint8_t* string; -} MB2_tag_modules; + uint8_t string[]; +} MB2_tag_module; typedef struct __packed MB2_tag_meminfo { uint32_t type; @@ -95,7 +95,7 @@ typedef struct __packed MB2_tag_memmap { uint32_t size; uint32_t entry_size; uint32_t entry_version; - MB2_memmap_entry* entries; + MB2_memmap_entry entries[]; } MB2_tag_memmap; typedef struct __packed MB2_tag_vbeinfo { @@ -130,7 +130,7 @@ typedef struct __packed MB2_tag_fbuffinfo { { struct { uint16_t framebuffer_palette_num_colors; - MB2_color* framebuffer_palette; + MB2_color framebuffer_palette[]; }; struct { uint8_t framebuffer_red_field_position; @@ -161,25 +161,25 @@ typedef struct __packed MB2_tag_smbiostab { uint8_t major; uint8_t minor; uint8_t reserved[6]; - uint8_t* smbios_tables; + uint8_t smbios_tables[]; } MB2_tag_smbiostab; typedef struct __packed MB2_tag_acpiold { uint32_t type; uint32_t size; - uint8_t* rsdp; + uint8_t rsdp[]; } MB2_tag_acpiold; typedef struct __packed MB2_tag_acpinew { uint32_t type; uint32_t size; - uint8_t* rsdp; + uint8_t rsdp[]; } MB2_tag_acpinew; typedef struct __packed MB2_tag_network { uint32_t type; uint32_t size; - uint8_t* dhcpack; + uint8_t dhcpack[]; } MB2_tag_network; typedef struct __packed MB2_tag_efimemmap { @@ -187,7 +187,7 @@ typedef struct __packed MB2_tag_efimemmap { uint32_t size; uint32_t descriptor_size; uint32_t descriptor_version; - uint8_t* efi_memmap; + uint8_t efi_memmap[]; } MB2_tag_efimemmap; typedef struct __packed MB2_tag_efibootnt { diff --git a/src/kernel/mb2_parser.cpp b/src/kernel/mb2_parser.cpp index 8b257b5..141f6ff 100644 --- a/src/kernel/mb2_parser.cpp +++ b/src/kernel/mb2_parser.cpp @@ -2,6 +2,7 @@ #include "drivers/video/vga.h" #include "klib/stdio.h" +#include "kernel/util/panic.h" namespace VGA = drivers::video::VGA; namespace Color = VGA::Color; @@ -13,8 +14,7 @@ void parse_multiboot(const MB2_information_structure* MBI) // Ensure the MBI is properly aligned if (mbi_addr & 7) { - VGA::set_color(Color::RED, Color::BLACK); - printf("Err: Unaligned MBI"); + kpanic("Err: Unaligned MBI"); return; } @@ -28,15 +28,15 @@ void parse_multiboot(const MB2_information_structure* MBI) switch (tag->type) { case MB2_TYPE_CMDLINE: - printf("Command line found.\n"); + printf("Command line found: %s\n", reinterpret_cast(tag)->string); break; case MB2_TYPE_BOOTLOADER: - printf("Boot loader found.\n"); + printf("Boot loader found: %s\n", reinterpret_cast(tag)->string); break; - case MB2_TYPE_MODULES: - printf("Module found.\n"); + case MB2_TYPE_MODULE: + printf("Module found: %s\n", reinterpret_cast(tag)->string); break; case MB2_TYPE_MEMINFO: @@ -51,6 +51,7 @@ void parse_multiboot(const MB2_information_structure* MBI) case MB2_TYPE_MEMMAP: printf("Memory map found.\n"); + parse_memmap(reinterpret_cast(tag)); break; case MB2_TYPE_VBEINFO: @@ -110,8 +111,7 @@ void parse_multiboot(const MB2_information_structure* MBI) break; case MB2_TYPE_IMGLDBPA: - printf("Image base address found.\n"); - printf("\t0x%x\n", reinterpret_cast(tag)->load_base_addr); + printf("Image base address found: 0x%x\n", reinterpret_cast(tag)->load_base_addr); break; default: @@ -120,3 +120,22 @@ void parse_multiboot(const MB2_information_structure* MBI) } } } + +void parse_memmap(MB2_tag_memmap* memmap_tag) +{ + size_t num_entries = (memmap_tag->size - sizeof(MB2_tag_memmap)) / memmap_tag->entry_size; + size_t available = 0; + + for (size_t i = 0; i < num_entries; i++) + { + MB2_memmap_entry* entry = &memmap_tag->entries[i]; + printf("\t[%x, %x] - Type %d\n", entry->base_addr, entry->base_addr + entry->length, entry->type); + + if (entry->type == MB2_MEM_AVAILABLE) + { + available += entry->length; + } + } + + printf("\tAvailable bytes: %d\n", available); +} diff --git a/src/x86_64/klib/stdio.cpp b/src/x86_64/klib/stdio.cpp index 6ae34bc..1ec2213 100644 --- a/src/x86_64/klib/stdio.cpp +++ b/src/x86_64/klib/stdio.cpp @@ -47,7 +47,7 @@ char* itoa(int val, char* buf, int radix) { while (val != 0) { - int rem = val % radix; + uint32_t rem = (uint32_t)val % radix; buf[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0'; val = val / radix; } @@ -123,7 +123,7 @@ void vprintf(const char* fmt, va_list args) } case 'x': { - int value = va_arg(vargs, int); + uint32_t value = va_arg(vargs, uint32_t); itoa(value, buf, 16); VGA::print_str(buf); break; From 9c4543034396d854dda29d1952747040a7518396 Mon Sep 17 00:00:00 2001 From: Bradley Myers Date: Sat, 6 Jun 2026 18:05:34 -0400 Subject: [PATCH 7/7] old wip --- dev-notes.todo | 1 + src/intf/kernel/mb2_parser.h | 1 + src/intf/kernel/mem/bitmap.h | 23 ++++++ src/intf/kernel/mem/virtmem.h | 18 +++++ src/intf/klib/string.h | 9 +++ src/intf/paging.h | 59 ++++++++++++++ src/intf/util/alignment.h | 4 + src/kernel/mb2_parser.cpp | 1 + src/kernel/mem/bitmap.cpp | 13 +++ src/kernel/mem/virtmem.cpp | 23 ++++++ src/x86_64/klib/string.cpp | 32 ++++++++ src/x86_64/paging.cpp | 146 ++++++++++++++++++++++++++++++++++ 12 files changed, 330 insertions(+) create mode 100644 dev-notes.todo create mode 100644 src/intf/kernel/mem/bitmap.h create mode 100644 src/intf/kernel/mem/virtmem.h create mode 100644 src/intf/klib/string.h create mode 100644 src/intf/paging.h create mode 100644 src/intf/util/alignment.h create mode 100644 src/kernel/mem/bitmap.cpp create mode 100644 src/kernel/mem/virtmem.cpp create mode 100644 src/x86_64/klib/string.cpp create mode 100644 src/x86_64/paging.cpp diff --git a/dev-notes.todo b/dev-notes.todo new file mode 100644 index 0000000..20c4f6d --- /dev/null +++ b/dev-notes.todo @@ -0,0 +1 @@ +- Consider moving src/intf/kernel into src/kernel/intf to be more clear diff --git a/src/intf/kernel/mb2_parser.h b/src/intf/kernel/mb2_parser.h index 4f2073a..af0137f 100644 --- a/src/intf/kernel/mb2_parser.h +++ b/src/intf/kernel/mb2_parser.h @@ -1,6 +1,7 @@ #pragma once #include "multiboot2.h" +#include "kernel/mem/virtmem.h" extern "C" uintptr_t KERNEL_VMA; diff --git a/src/intf/kernel/mem/bitmap.h b/src/intf/kernel/mem/bitmap.h new file mode 100644 index 0000000..7cf1ce6 --- /dev/null +++ b/src/intf/kernel/mem/bitmap.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +#define BIT_TO_INDEX(n) (n / (8 * sizeof(size_t))) +#define BIT_TO_OFFSET(n) (n % (8 * sizeof(size_t))) + +typedef struct bitmap { +private: + size_t length; + size_t* data; + +public: + bitmap(void* data, size_t length) : length(length), data(reinterpret_cast(data)) + { + memset(data, 0, DIV_ROUND_UP(length, sizeof(size_t)) * sizeof(size_t)); + } + + inline bool test(size_t bit); + inline void set(size_t bit); + inline void clear(size_t bit); +} bitmap; diff --git a/src/intf/kernel/mem/virtmem.h b/src/intf/kernel/mem/virtmem.h new file mode 100644 index 0000000..f107986 --- /dev/null +++ b/src/intf/kernel/mem/virtmem.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#include "multiboot2.h" +#include "kernel/mem/bitmap.h" + +#define PAGE_SIZE 4096 + +extern "C" uint64_t PML4[]; +extern "C" uint64_t PDPT[]; +extern "C" uint64_t PDPT_HH[]; +extern "C" uint64_t PD_KERN[]; + +void init_virtmem(MB2_tag_memmap* memmap); +void* virtmem_alloc(size_t pages); +int virtmem_free(void* ptr, size_t pages); diff --git a/src/intf/klib/string.h b/src/intf/klib/string.h new file mode 100644 index 0000000..c7ed9c4 --- /dev/null +++ b/src/intf/klib/string.h @@ -0,0 +1,9 @@ +#pragma once + +#include +#include + +void* memset(void* dest, int ch, size_t count); +void* memcpy(void* dest, const void* src, size_t count); + +size_t strlen(const char* str); diff --git a/src/intf/paging.h b/src/intf/paging.h new file mode 100644 index 0000000..e8dea83 --- /dev/null +++ b/src/intf/paging.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include "klib/memory.h" + +#define ENTRY_PRESENT 1 << 0 // Present +#define ENTRY_WRITEABLE 1 << 1 // Writable when set, else readonly +#define ENTRY_USER 1 << 2 // Ring 3 accessible when set, else ring 0 only +#define ENTRY_PWT 1 << 3 // Write-through caching enabled when set +#define ENTRY_PCD 1 << 4 // Page caching in TLD disabled when set +#define ENTRY_PS 1 << 7 // Page size is large page when set +#define ENTRY_GLOBAL 1 << 8 // Global page not invalidated in TLB upon a MOV to CR3 when set +#define ENTRY_NOEXEC 1 << 63 // Execute disabled for memory when set + +extern "C" uintptr_t BOOT_LMA; +extern "C" uintptr_t KERNEL_LMA; +extern "C" uintptr_t KERNEL_VMA; + +extern "C" uint64_t PML4[512]; + +/// @brief Represents a physical address and its corresponding flags. +struct PhysicalAddress { + uintptr_t address; // The 52-bit physical address + uint16_t flags; // The 12-bit flags +}; + +/// @brief Gets a page table entry from a page table at a specified index. +/// @param pt_base The base page table address. +/// @param index The index of the page table entry. +/// @return The page table entry at the specified index. +inline uint64_t get_pte(uintptr_t pt_base, uint64_t index) { + return *((uintptr_t*)(pt_base + index * 8)); +} + +/// @brief Creates a page table entry in a page table at a specified index. +/// @param pt_base The base page table address. +/// @param index The target index of the page table entry. +/// @param pte The page table entry to insert. +inline void set_pte(uintptr_t pt_base, uint64_t index, uint64_t pte) { + *((uintptr_t*)(pt_base + index * 8)) = pte; +} + +/// @brief Allocates memory for a new page table. +/// @return The address of the new page table. +inline uintptr_t alloc_pt() { + return (uintptr_t)kmalloc(4096); +} + +PhysicalAddress get_physical_addr_from(uintptr_t virtual_address, uintptr_t pml4_base, uint16_t flags); +void insert_mapping(uintptr_t virtual_address, PhysicalAddress physical_address, uintptr_t pml4_base); + +/// @brief Maps a virtual address to the same physical address. +/// @param addr The address to identity map. +/// @param flags The flags to set for the entry. +/// @param pml4_base The address of the PML4 table. +inline void identity_map(uintptr_t addr, uint16_t flags, uintptr_t pml4_base) +{ + insert_mapping(addr, PhysicalAddress { addr, flags }, pml4_base); +} diff --git a/src/intf/util/alignment.h b/src/intf/util/alignment.h new file mode 100644 index 0000000..996aecf --- /dev/null +++ b/src/intf/util/alignment.h @@ -0,0 +1,4 @@ +#pragma once + +#define ALIGN_ADJUSTMENT(addr, align) (align - (addr & (align - 1))) +#define ALIGN(addr, align) (addr + ALIGN_ADJUSTMENT(addr, align)) diff --git a/src/kernel/mb2_parser.cpp b/src/kernel/mb2_parser.cpp index 141f6ff..26f8e10 100644 --- a/src/kernel/mb2_parser.cpp +++ b/src/kernel/mb2_parser.cpp @@ -52,6 +52,7 @@ void parse_multiboot(const MB2_information_structure* MBI) case MB2_TYPE_MEMMAP: printf("Memory map found.\n"); parse_memmap(reinterpret_cast(tag)); + init_virtmem(reinterpret_cast(tag)); break; case MB2_TYPE_VBEINFO: diff --git a/src/kernel/mem/bitmap.cpp b/src/kernel/mem/bitmap.cpp new file mode 100644 index 0000000..59ad948 --- /dev/null +++ b/src/kernel/mem/bitmap.cpp @@ -0,0 +1,13 @@ +#include "kernel/mem/bitmap.h" + +bool bitmap::test(size_t bit) { + return (data[BIT_TO_INDEX(bit)] >> (BIT_TO_OFFSET(bit))) & 1; +} + +void bitmap::set(size_t bit) { + data[BIT_TO_INDEX(bit)] |= 1 << (BIT_TO_OFFSET(bit)); +} + +void bitmap::clear(size_t bit) { + data[BIT_TO_INDEX(bit)] &= ~(1 << (BIT_TO_OFFSET(bit))); +} diff --git a/src/kernel/mem/virtmem.cpp b/src/kernel/mem/virtmem.cpp new file mode 100644 index 0000000..7b08ab7 --- /dev/null +++ b/src/kernel/mem/virtmem.cpp @@ -0,0 +1,23 @@ +#include "kernel/mem/virtmem.h" + +void init_virtmem(MB2_tag_memmap* memmap) +{ + size_t memmap_entries = (memmap->size - sizeof(MB2_tag_memmap)) / memmap->entry_size; + for (size_t i = 0; i < memmap_entries; i++) + { + MB2_memmap_entry* entry = &memmap->entries[i]; + if (entry->type != MB2_MEM_AVAILABLE) continue; + + + } +} + +void* virtmem_alloc(size_t pages) +{ + +} + +int virtmem_free(void* ptr, size_t pages) +{ + +} diff --git a/src/x86_64/klib/string.cpp b/src/x86_64/klib/string.cpp new file mode 100644 index 0000000..a98cd1f --- /dev/null +++ b/src/x86_64/klib/string.cpp @@ -0,0 +1,32 @@ +#include "klib/string.h" + +void* memset(void* dest, int ch, size_t count) +{ + uint8_t* destination = (uint8_t*)dest; + for (size_t i = 0; i < count; i++) + { + destination[i] = ch; + } + return dest; +} + +void* memcpy(void* dest, const void* src, size_t count) +{ + const uint8_t* source = (uint8_t*)src; + uint8_t* destination = (uint8_t*)dest; + + for (size_t i = 0; i < count; i++) + { + destination[i] = source[i]; + } + + return dest; +} + +size_t strlen(const char* str) +{ + const char* end = str; + while (*end != '\0') + ++end; + return end - str; +} diff --git a/src/x86_64/paging.cpp b/src/x86_64/paging.cpp new file mode 100644 index 0000000..40cbab6 --- /dev/null +++ b/src/x86_64/paging.cpp @@ -0,0 +1,146 @@ +#include "paging.h" + +/// @brief Gets a physical address from its corresponding virtual address. +/// @param virtual_address The virtual memory address. +/// @param pml4_base The address of the PML4 table. +/// @param flags The expected flags for the address. +/// @return The physical address on success, PhysicalAddress {0, 0} on failure. +PhysicalAddress get_physical_addr_from(uintptr_t virtual_address, uintptr_t pml4_base, uint16_t flags) { + // Extract the four level indices from the virtual address + uint64_t pml4_index = (virtual_address >> 39) & 0x1FF; // Bits 39-47 + uint64_t pdp_index = (virtual_address >> 30) & 0x1FF; // Bits 30-38 + uint64_t pd_index = (virtual_address >> 21) & 0x1FF; // Bits 21-29 + uint64_t pt_index = (virtual_address >> 12) & 0x1FF; // Bits 12-20 + + // Get the page table entry from the PML4 table + uint64_t pml4_entry = get_pte(pml4_base, pml4_index); + + // Ensure the PML4 entry is present and has the required flags + if ((pml4_entry & ENTRY_PRESENT) == 0 || (pml4_entry & flags) != flags) { + return {0, 0}; + } + + // Get the page table base address from the PML4 entry + uintptr_t pdp_base = pml4_entry & 0xFFFFFFFFF000; // Bits 12-51 + + // Get the page table entry from the PDP table + uint64_t pdp_entry = get_pte(pdp_base, pdp_index); + + // Ensure the PDP entry is present and has the required flags + if ((pdp_entry & ENTRY_PRESENT) == 0 || (pdp_entry & flags) != flags) { + return {0, 0}; + } + + // Check if the PDP entry is a large page (1 GB) + if ((pdp_entry & ENTRY_PS) != 0) { + // The PDP entry is a large page, extract the physical address and the flags from it + uint64_t physical_address = pdp_entry & 0xFFFFFC0000000; // Bits 30-51 + uint16_t physical_flags = pdp_entry & 0xFFF; // Bits 0-11 + + // Add the offset from the virtual address to the physical address + physical_address += virtual_address & 0x3FFFFFFF; // Bits 0-29 + + // Return the physical address and the flags + return {physical_address, physical_flags}; + } + + // Get the page table base address from the PDP entry + uintptr_t pd_base = pdp_entry & 0xFFFFFFFFF000; // Bits 12-51 + + // Get the page table entry from the PD table + uint64_t pd_entry = get_pte(pd_base, pd_index); + + // Ensure the PD entry is present and has the required flags + if ((pd_entry & ENTRY_PRESENT) == 0 || (pd_entry & flags) != flags) { + return {0, 0}; + } + + // Check if the PD entry is a large page (2 MB) + if ((pd_entry & ENTRY_PS) != 0) { + // The PD entry is a large page, extract the physical address and the flags from it + uint64_t physical_address = pd_entry & 0xFFFFFFE00000; // Bits 21-51 + uint16_t physical_flags = pd_entry & 0xFFF; // Bits 0-11 + + // Add the offset from the virtual address to the physical address + physical_address += virtual_address & 0x1FFFFF; // Bits 0-20 + + // Return the physical address and the flags + return {physical_address, physical_flags}; + } + + // Get the page table base address from the PD entry + uintptr_t pt_base = pd_entry & 0xFFFFFFFFF000; // Bits 12-51 + + // Get the page table entry from the PT table + uint64_t pt_entry = get_pte(pt_base, pt_index); + + // Ensure the PT entry is present and has the required flags + if ((pt_entry & ENTRY_PRESENT) == 0 || (pt_entry & flags) != flags) { + return {0, 0}; + } + + // The PT entry is a normal page (4 KB), extract the physical address and the flags from it + uint64_t physical_address = pt_entry & 0xFFFFFFFFF000; // Bits 12-51 + uint16_t physical_flags = pt_entry & 0xFFF; // Bits 0-11 + + // Add the offset from the virtual address to the physical address + physical_address += virtual_address & 0xFFF; // Bits 0-11 + + // Return the physical address and the flags + return {physical_address, physical_flags}; +} + +/// @brief Insert an entry into the appropriate page table to map a virtual address to a physical one +/// @param virtual_address The virtual address to map. +/// @param physical_address The physical address the virual one should map to. +/// @param pml4_base The address of the PML4 table. +void insert_mapping(uintptr_t virtual_address, PhysicalAddress physical_address, uintptr_t pml4_base) { + // Extract the four level indices from the virtual address + uint64_t pml4_index = (virtual_address >> 39) & 0x1FF; // Bits 39-47 + uint64_t pdp_index = (virtual_address >> 30) & 0x1FF; // Bits 30-38 + uint64_t pd_index = (virtual_address >> 21) & 0x1FF; // Bits 21-29 + uint64_t pt_index = (virtual_address >> 12) & 0x1FF; // Bits 12-20 + + // Get the page table entry from the PML4 table + uint64_t pml4_entry = get_pte(pml4_base, pml4_index); + + // Ensure the PML4 entry is present or allocate a new PDP table and save it to PML4 + if ((pml4_entry & ENTRY_PRESENT) == 0) { + uintptr_t pdp_base = alloc_pt(); + pml4_entry = pdp_base | ENTRY_PRESENT; + set_pte(pml4_base, pml4_index, pml4_entry); + } + + // Get the page table base address from the PML4 entry + uintptr_t pdp_base = pml4_entry & 0xFFFFFFFFF000; // Bits 12-51 + + // Get the page table entry from the PDP table + uint64_t pdp_entry = get_pte(pdp_base, pdp_index); + + // Ensure the PDP entry is present or allocate a new PD table and save it to PDP + if ((pdp_entry & ENTRY_PRESENT) == 0) { + uintptr_t pd_base = alloc_pt(); + pdp_entry = pd_base | ENTRY_PRESENT; + set_pte(pdp_base, pdp_index, pdp_entry); + } + + // Get the page table base address from the PDP entry + uintptr_t pd_base = pdp_entry & 0xFFFFFFFFF000; // Bits 12-51 + + // Get the page table entry from the PD table + uint64_t pd_entry = get_pte(pd_base, pd_index); + + // Ensure the PD entry is present or allocate a new PT table and save it to PD + if ((pd_entry & ENTRY_PRESENT) == 0) { + uintptr_t pt_base = alloc_pt(); + pd_entry = pt_base | ENTRY_PRESENT; + set_pte(pd_base, pd_index, pd_entry); + } + + // Get the page table base address from the PD entry + uintptr_t pt_base = pd_entry & 0xFFFFFFFFF000; // Bits 12-51 + + // Set the page table entry in the PT table with the physical address and the flags + uint64_t pt_entry = (physical_address.address & 0xFFFFFFFFF000) | (physical_address.flags & 0xFFF); + set_pte(pt_base, pt_index, pt_entry); +}