diff --git a/README.md b/README.md index e7d208f..fc1e8f4 100644 --- a/README.md +++ b/README.md @@ -22,15 +22,15 @@ practices while remaining approachable to contributors and learners alike. - [x] Halt loop - [x] Basics of the kernel + GDT, IDT and interrupt handlers - [x] PIC, timer and keyboard support -- [ ] Stack tracing support +- [x] Stack tracing support ### Memory -- [ ] Limine Memory map parsing -- [ ] Physical memory manager -- [ ] Virtual memory manager -- [ ] Kernel heap (malloc, free) -- [ ] Move to Limine 6 (APIC or x2APIC) +- [x] Limine Memory map parsing +- [x] Physical memory manager +- [x] Virtual memory manager +- [x] Kernel heap (malloc, free) +- [x] Create classes and helpers with the heap ### Internal Driver Framework @@ -39,6 +39,7 @@ practices while remaining approachable to contributors and learners alike. - [ ] Driver lifecycle - [ ] MMIO port I/O helpers - [ ] Block device abstraction +- [ ] Move to Limine 6 barely (APIC or x2APIC mapped to legacy PIC) ### Hardware discovery @@ -81,7 +82,7 @@ practices while remaining approachable to contributors and learners alike. - [ ] ACPI - [ ] MADT parsing -- [ ] x2APIC +- [ ] x2APIC and APIC - [ ] APIC timer - [ ] IPIs diff --git a/include/graphics/framebuffer.h b/include/graphics/framebuffer.h index 1ffd01b..95ce9f6 100644 --- a/include/graphics/framebuffer.h +++ b/include/graphics/framebuffer.h @@ -45,8 +45,8 @@ class FramebufferConsole { void clear(); void putChar(char c); - void write(string str); - void writeLn(string str); + void write(cstring str); + void writeLn(cstring str); void newline(); void backspace(); void backspace(char c); diff --git a/include/io/serial.h b/include/io/serial.h index 4263e85..a28a225 100644 --- a/include/io/serial.h +++ b/include/io/serial.h @@ -13,9 +13,9 @@ namespace io { void serialWriteChar(char c); - void serialWrite(string input); + void serialWrite(cstring input); void serialWriteHex(u64 num); - void serialWriteNumber(u32 num); + void serialWriteNumber(u64 num); } #endif //AVERY_SERIAL_H diff --git a/include/kernel/console.h b/include/kernel/console.h index 892d9c0..8c7b72f 100644 --- a/include/kernel/console.h +++ b/include/kernel/console.h @@ -27,10 +27,12 @@ extern FramebufferConsole* GlobalFramebufferConsole; namespace out { extern ConsoleOutputMode outputMode; void initFramebufferConsole(const Framebuffer& framebuffer); - void print(string str); - void println(string str); + void print(cstring str); + void print(const string& str); + void println(cstring str); + void println(const string& str); void printHex(u64 num); - void printNumber(u32 num); + void printNumber(u64 num); void putChar(char c); void clear(); @@ -42,7 +44,7 @@ namespace out { }; namespace in { - string getLine(string prompt = nullptr); + string getLine(cstring prompt = nullptr); char getChar(); } diff --git a/include/kernel/debug.h b/include/kernel/debug.h index d436582..fc786ba 100644 --- a/include/kernel/debug.h +++ b/include/kernel/debug.h @@ -53,6 +53,23 @@ debug::error( \ } \ } while (0) +#define TEST_RESULT(expr) \ +do \ +{ \ +if (expr) { \ +out::setColor(Color::green, Color::blue); \ +out::println("========================"); \ +out::println("TEST SUCCEEDED"); \ +out::setColor(Color::white, Color::blue); \ +} \ +else { \ +out::setColor(Color::red, Color::blue); \ +out::println("========================"); \ +out::println("TEST FAILED"); \ +out::setColor(Color::white, Color::blue); \ +} \ +} while (0) + #ifndef NDEBUG #define ASSERT(expr) VERIFY(expr) #else @@ -66,6 +83,10 @@ namespace debug { void warn(const char* message, LogType logType = LogType::Serial); void error(const char* message, LogType logType = LogType::Serial); + inline void serialError(const char* message) { + error(message, LogType::Serial); + } + struct StackFrame { StackFrame* rbp; uptr rip; diff --git a/include/kernel/memory.h b/include/kernel/memory.h index 95720c8..0f63461 100644 --- a/include/kernel/memory.h +++ b/include/kernel/memory.h @@ -23,9 +23,69 @@ namespace memory { } } - void setHHDM(volatile limine_hhdm_request& request); + void setHHDM(volatile struct limine_hhdm_request& request); u64 getHHDMOffset(); + void initMemoryServices(volatile struct limine_memmap_request& request); + + namespace heap { + void init(); + + void* allocate(u64 size); + void free(void* ptr); + void* realloc(void* ptr, u64 newSize); + + u64 getUsedMemory(); + u64 getFreeMemory(); + + struct HeapBlock { + u64 magic; + u64 size; + bool free; + HeapBlock* next; + HeapBlock* prev; + }; + + void splitBlock(HeapBlock* block, u64 size); + void mergeWithNext(HeapBlock* block); + HeapBlock* findFreeBlock(u64 size); + bool expandHeap(u64 size); + } + + template + T* allocElem(u64 count) { + return heap::allocate(sizeof(T) * count); + } + + inline void* alloc(u64 size) { + return heap::allocate(size); + } + + inline void free(void* ptr) { + heap::free(ptr); + } + + inline void* calloc(u64 count, u64 size) { + return heap::allocate(count * size); + } + + inline void* realloc(void* ptr, u64 newSize) { + return heap::realloc(ptr, newSize); + } + + template + UniquePtr makeUnique(Args&&... args) { + return UniquePtr(new T(static_cast(args)...)); + } } +void* operator new(usize size); +void* operator new[](usize size); + +void operator delete(void* ptr) noexcept; +void operator delete[](void* ptr) noexcept; + +void operator delete(void* ptr, usize size) noexcept; +void operator delete[](void* ptr, usize size) noexcept; + #endif //AVERY_MEMORY_H diff --git a/include/kernel/memory/physicalMemory.h b/include/kernel/memory/physicalMemory.h new file mode 100644 index 0000000..556fbe4 --- /dev/null +++ b/include/kernel/memory/physicalMemory.h @@ -0,0 +1,93 @@ +/* +* physicalMemory.h +* As part of the Avery project +* Created by Max Van den Eynde in 2026 +* -------------------------------------- +* Description: ${FILE_DESCRIPTION} +* Copyright (c) 2026 Max Van den Eynde +*/ + +#ifndef AVERY_PHYSICALMEMORY_H +#define AVERY_PHYSICALMEMORY_H + +#define PAGE_SIZE 4096 +#include "../../types.h" +#include "kernel/memory.h" + +struct limine_memmap_request; + +namespace pmm { + struct MemoryRegion { + physAddr base; + u64 length; + u64 type; + }; + + struct MemoryInfo { + u64 totalMemory; + u64 usableMemory; + u64 reservedMemory; + u64 pageCount; + }; + + struct PhysicalMemory { + u8* bitmap; + physAddr bitmapPhysicalAddress; + u64 bitmapSize; + + u64 totalPages; + u64 usedPages; + u64 freePages; + + physAddr highestAddress; + u64 lastAllocIndex; + }; + + extern PhysicalMemory physicalMemory; + + void init(volatile limine_memmap_request& memmap); + void markUsed(physAddr addr, u64 pageCount); + void markFree(physAddr addr, u64 pageCount); + + physAddr allocPage(); + physAddr allocPages(u64 count); + + void freePage(physAddr addr); + void freePages(physAddr addr, u64 count); + + bool isFree(physAddr addr); + u64 getFreePages(); + u64 getTotalPages(); + u64 getUsedPages(); + u64 getFreeMemory(); + + inline void* physicalToVirtual(physAddr p) { + return reinterpret_cast(p + memory::getHHDMOffset()); + } + + inline physAddr virtualToPhysicalHHDM(void* v) { + return reinterpret_cast(v) - memory::getHHDMOffset(); + } + + inline physAddr pageToPhysical(u64 page) { + return page * PAGE_SIZE; + } + + inline u64 physicalToPage(physAddr addr) { + return addr / PAGE_SIZE; + } + + inline void bitmapSet(u64 bit) { + physicalMemory.bitmap[bit / 8] |= (1 << (bit % 8)); + } + + inline void bitmapClear(u64 bit) { + physicalMemory.bitmap[bit / 8] &= ~(1 << (bit % 8)); + } + + inline bool bitmapTest(u64 bit) { + return physicalMemory.bitmap[bit / 8] & (1 << (bit % 8)); + } +} + +#endif //AVERY_PHYSICALMEMORY_H diff --git a/include/kernel/memory/virtualMemory.h b/include/kernel/memory/virtualMemory.h new file mode 100644 index 0000000..4ce072f --- /dev/null +++ b/include/kernel/memory/virtualMemory.h @@ -0,0 +1,47 @@ +/* +* virtualMemory.h +* As part of the Avery project +* Created by Max Van den Eynde in 2026 +* -------------------------------------- +* Description: Virtual Memory Manager definitions +* Copyright (c) 2026 Max Van den Eynde +*/ + +#ifndef AVERY_VIRTUALMEMORY_H +#define AVERY_VIRTUALMEMORY_H +#include "../../types.h" + +#define PAGE_ENTRIES 512 + + +namespace vmm { + struct PageTable { + u64 entries[PAGE_ENTRIES]; + }; + + constexpr u64 AddressMask = 0x000FFFFFFFFFF000ull; + + constexpr u64 FlagPresent = 1ull << 0; + constexpr u64 FlagWritable = 1ull << 1; + constexpr u64 FlagUser = 1ull << 2; + constexpr u64 FlagNX = 1ull << 63; + + void init(); + + PageTable* createAddressSpace(); + void switchAddressSpace(PageTable* table); + + bool mapPage(PageTable* pml4, virtAddr virt, physAddr phys, u64 flags); + bool unmapPage(PageTable* pml4, virtAddr virt); + bool mapRange(PageTable* pml4, virtAddr virt, physAddr phys, u64 size, u64 flags); + + physAddr virtToPhysical(PageTable* pml4, virtAddr virt); + + PageTable* newTable(); + PageTable* getNextTable(PageTable* table, u64 index, bool create); + u64* getPte(PageTable* pml4, virtAddr virt, bool create); + + PageTable* getKernelPml4(); +} + +#endif //AVERY_VIRTUALMEMORY_H diff --git a/include/tests.h b/include/tests.h new file mode 100644 index 0000000..794291b --- /dev/null +++ b/include/tests.h @@ -0,0 +1,20 @@ +/* +* tests.h +* As part of the Avery project +* Created by Max Van den Eynde in 2026 +* -------------------------------------- +* Description: ${FILE_DESCRIPTION} +* Copyright (c) 2026 Max Van den Eynde +*/ + +#ifndef AVERY_TESTS_H +#define AVERY_TESTS_H + +namespace tests { + void pmmTest(); + void vmmTest(); + void mallocTest(); + void runAllMemoryTests(); +} + +#endif //AVERY_TESTS_H diff --git a/include/types.h b/include/types.h index 08a717e..feb0264 100644 --- a/include/types.h +++ b/include/types.h @@ -16,13 +16,19 @@ using u32 = unsigned int; using u64 = unsigned long long; using usize = decltype(sizeof(0)); using uptr = decltype(sizeof(0)); +using physAddr = uptr; +using virtAddr = uptr; using i8 = char; using i16 = short; using i32 = int; using i64 = long long; -using string = const char*; +using cstring = const char*; + +namespace debug { + void serialError(const char* message); +} template struct Tuple { @@ -46,4 +52,538 @@ inline void* operator new(usize, void* ptr) noexcept { inline void operator delete(void*, void*) noexcept { } +template +concept ByteNumber = requires(T a, T b) { + a + b; + ~a; + a & b; +}; + +template + requires ByteNumber +T alignUp(T x, T a) { + return (x + a - 1) & ~(a - 1); +} + +template + requires ByteNumber +T alignDown(T x, T a) { + return (x + a - 1) & ~(a - 1); +} + +template +class Option { +public: + Option() { + present = false; + } + + Option(const T& value) { + present = true; + storage = value; + } + + [[nodiscard]] bool hasValue() const { + return present; + } + + T& value() { + if (!present) { + debug::serialError("Tried to access an option that had a null value"); + } + return storage; + } + + const T& value() const { + if (!present) { + debug::serialError("Tried to access an option that had a null value"); + } + return storage; + } + + T valueOr(const T& fallback) const { + if (!present) { + return fallback; + } + return storage; + } + + static Option none() { + return Option(); + } + +private: + bool present; + T storage; +}; + +template +class Option { +public: + Option() { + present = false; + storage = nullptr; + } + + Option(T& value) { + present = true; + storage = &value; + } + + [[nodiscard]] bool hasValue() const { + return present; + } + + T& value() { + if (!present) { + debug::serialError("Tried to access an option that had a null value"); + } + return storage; + } + + const T& value() const { + if (!present) { + debug::serialError("Tried to access an option that had a null value"); + } + return storage; + } + + T valueOr(const T& fallback) const { + if (!present) { + return fallback; + } + return storage; + } + + static Option none() { + return Option(); + } + +private: + bool present; + T* storage; +}; + +class string { +public: + string(); + string(cstring str); + string(const string& other); + string(string&& other) noexcept; + + ~string(); + + string& operator=(const string& other); + string& operator=(string&& other) noexcept; + + bool operator==(const string& other) const; + bool operator!=(const string& other) const; + + [[nodiscard]] cstring cStr() const; + [[nodiscard]] usize length() const; + [[nodiscard]] bool empty() const; + + void clear(); + void append(cstring text); + void append(char c); + char popBack(); + + Option operator[](usize index); + Option operator[](usize index) const; + +private: + char* data; + usize len; + usize capacity; + + void reserve(usize newCapacity); +}; + +template +class UniquePtr { +public: + UniquePtr() { + ptr = nullptr; + } + + explicit UniquePtr(T* ptr) { + this->ptr = ptr; + } + + ~UniquePtr() { + delete ptr; + } + + UniquePtr(const UniquePtr&) = delete; + UniquePtr& operator=(const UniquePtr&) = delete; + + UniquePtr(UniquePtr&& other) noexcept { + ptr = other.ptr; + other.ptr = nullptr; + } + + UniquePtr& operator=(UniquePtr&& other) noexcept { + if (this == &other) { + return *this; + } + + delete ptr; + + ptr = other.ptr; + other.ptr = nullptr; + + return *this; + } + + T* get() const { + return ptr; + } + + T* release() { + T* old = ptr; + ptr = nullptr; + return old; + } + + void reset(T* newPtr = nullptr) { + if (this->ptr == newPtr) { + return; + } + + delete this->ptr; + this->ptr = newPtr; + } + + T& operator*() const { + return *ptr; + } + + T* operator->() const { + return ptr; + } + + operator bool() const { + return ptr != nullptr; + } + +private: + T* ptr; +}; + +template +class LinkedList { +public: + struct Node { + T value; + Node* next; + Node* prev; + + Node(const T& value) + : value(value), next(nullptr), prev(nullptr) { + } + }; + + LinkedList() { + head = nullptr; + tail = nullptr; + count = 0; + } + + ~LinkedList() { + clear(); + } + + void pushBack(const T& value) { + Node* newNode = new Node(value); + ASSERT(newNode != nullptr); + + if (empty()) { + head = tail = newNode; + count++; + return; + } + + newNode->prev = tail; + tail->next = newNode; + tail = newNode; + + count++; + } + + void pushFront(const T& value) { + Node* newNode = new Node(value); + ASSERT(newNode != nullptr); + + if (empty()) { + head = tail = newNode; + count++; + return; + } + + newNode->next = head; + head->prev = newNode; + head = newNode; + + count++; + } + + void popBack() { + if (empty()) { + return; + } + + Node* oldNode = tail; + + if (head == tail) { + head = nullptr; + tail = nullptr; + } + else { + tail = tail->prev; + tail->next = nullptr; + } + + delete oldNode; + count--; + } + + void popFront() { + if (empty()) { + return; + } + + Node* oldNode = head; + + if (head == tail) { + head = nullptr; + tail = nullptr; + } + else { + head = head->next; + head->prev = nullptr; + } + + delete oldNode; + count--; + } + + T& front() { + ASSERT(head != nullptr); + return head->value; + } + + T& back() { + ASSERT(tail != nullptr); + return tail->value; + } + + [[nodiscard]] bool empty() const { + return count == 0; + } + + [[nodiscard]] usize size() const { + return count; + } + + void clear() { + Node* current = head; + + while (current != nullptr) { + Node* next = current->next; + delete current; + current = next; + } + + head = nullptr; + tail = nullptr; + count = 0; + } + +private: + Node* head; + Node* tail; + usize count; +}; + +template +class Queue { +public: + Queue() = default; + + void push(const T& value) { + list.pushBack(value); + } + + Option pop() { + if (list.empty()) { + return Option::none(); + } + + T value = list.front(); + list.popFront(); + + return value; + } + + T& front() { + return list.front(); + } + + [[nodiscard]] bool empty() const { + return list.empty(); + } + + [[nodiscard]] usize size() const { + return list.size(); + } + +private: + LinkedList list; +}; + + +template +class Vector { +public: + Vector() { + data = nullptr; + count = 0; + cap = 0; + } + + ~Vector() { + delete[] data; + } + + void push(const T& value) { + if (count >= cap) { + grow(); + } + + data[count] = value; + count++; + } + + void push(T&& value) { + if (count >= cap) { + grow(); + } + + data[count] = static_cast(value); + count++; + } + + void pop() { + if (count == 0) { + return; + } + + count--; + } + + + Option operator[](usize index) { + if (index >= count) { + return Option::none(); + } + + return Option(data[index]); + } + + Option operator[](usize index) const { + if (index >= count) { + return Option::none(); + } + + return Option(data[index]); + } + + + Option last() { + if (count == 0) { + return Option::none(); + } + + return Option(data[count - 1]); + } + + Option last() const { + if (count == 0) { + return Option::none(); + } + return Option(data[count - 1]); + } + + Option first() { + if (count == 0) { + return Option::none(); + } + + return Option(data[0]); + } + + Option first() const { + if (count == 0) { + return Option::none(); + } + + return Option(data[0]); + } + + [[nodiscard]] usize size() const { + return count; + } + + [[nodiscard]] usize capacity() const { + return cap; + } + + [[nodiscard]] bool empty() const { + return count == 0; + } + + void clear() { + count = 0; + } + + void reserve(usize newCapacity) { + if (newCapacity <= cap) { + return; + } + + T* newData = new T[newCapacity]; + + for (usize i = 0; i < count; i++) { + newData[i] = data[i]; + } + + delete[] data; + + data = newData; + cap = newCapacity; + } + + void resize(usize newSize) { + if (newSize > cap) { + reserve(newSize); + } + + count = newSize; + } + +private: + T* data; + usize count; + usize cap; + + void grow() { + if (cap == 0) { + reserve(4); + } + else { + reserve(cap * 2); + } + } +}; + #endif //AVERY_TYPES_H diff --git a/kernel/core/interrupts/isrs/isr.cpp b/kernel/core/interrupts/isrs/isr.cpp index 42c1d5d..18fa7f2 100644 --- a/kernel/core/interrupts/isrs/isr.cpp +++ b/kernel/core/interrupts/isrs/isr.cpp @@ -14,7 +14,7 @@ #include "kernel/console.h" #include "kernel/debug.h" -string exception_messages[] = { +cstring exception_messages[] = { "Division By Zero Exception", "Debug Exception", "Non Maskable Interrupt Exception", diff --git a/kernel/core/memory/heap.cpp b/kernel/core/memory/heap.cpp new file mode 100644 index 0000000..785d0c9 --- /dev/null +++ b/kernel/core/memory/heap.cpp @@ -0,0 +1,272 @@ +/* +* heap.cpp +* As part of the Avery project +* Created by Max Van den Eynde in 2026 +* -------------------------------------- +* Description: Heap allocator for the kernel +* Copyright (c) 2026 Max Van den Eynde +*/ + +#define KERNEL_HEAP_START 0xFFFFA00000000000ull +#define KERNEL_HEAP_INITIAL_SIZE (16 * 4096) +#define KERNEL_HEAP_MAX_SIZE (64 * 1024 * 1024) + +#define HEAP_MAGIC 0xC0FFEE1234567890ull + +#include "kernel/debug.h" +#include "kernel/memory.h" +#include "kernel/memory/physicalMemory.h" +#include "kernel/memory/virtualMemory.h" + +memory::heap::HeapBlock* head = nullptr; +virtAddr heapStart = KERNEL_HEAP_START; +virtAddr heapEnd = KERNEL_HEAP_START; +virtAddr heapMax = KERNEL_HEAP_START + KERNEL_HEAP_MAX_SIZE; + +u64 usedMemory = 0; +u64 freeMemory = 0; + +void memory::heap::splitBlock(HeapBlock* block, u64 size) { + u64 remaining = block->size - size; + + if (remaining <= sizeof(HeapBlock) + 16) { + return; + } + + auto* newBlock = reinterpret_cast(reinterpret_cast(block + 1) + size); + + newBlock->magic = HEAP_MAGIC; + newBlock->size = remaining - sizeof(HeapBlock); + newBlock->free = true; + newBlock->next = block->next; + newBlock->prev = block; + + if (newBlock->next) { + newBlock->next->prev = newBlock; + } + + block->next = newBlock; + block->size = size; +} + +void memory::heap::mergeWithNext(HeapBlock* block) { + HeapBlock* next = block->next; + + if (!next || !next->free) { + return; + } + + block->size += sizeof(HeapBlock) + next->size; + block->next = next->next; + + if (block->next) { + block->next->prev = block; + } +} + +memory::heap::HeapBlock* memory::heap::findFreeBlock(u64 size) { + HeapBlock* current = head; + + while (current) { + if (current->free && current->size >= size) { + return current; + } + + current = current->next; + } + + return nullptr; +} + +bool memory::heap::expandHeap(u64 size) { + size = alignUp(size, 4096ull); + + if (heapEnd + size > heapMax) { + return false; + } + + for (u64 offset = 0; offset < size; offset += 4096) { + physAddr phys = pmm::allocPage(); + + if (phys == 0) { + return false; + } + + bool ok = vmm::mapPage(vmm::getKernelPml4(), heapEnd + offset, phys, vmm::FlagWritable); + + if (!ok) { + pmm::freePage(phys); + return false; + } + } + + auto* newBlock = reinterpret_cast(heapEnd); + newBlock->magic = HEAP_MAGIC; + newBlock->size = size - sizeof(HeapBlock); + newBlock->next = nullptr; + newBlock->prev = nullptr; + newBlock->free = true; + + if (!head) { + head = newBlock; + } + else { + HeapBlock* last = head; + + while (last->next) { + last = last->next; + } + + last->next = newBlock; + newBlock->prev = last; + + if (last->free) { + mergeWithNext(last); + } + } + + heapEnd += size; + freeMemory += size - sizeof(HeapBlock); + + return true; +} + +void memory::heap::init() { + head = nullptr; + heapStart = KERNEL_HEAP_START; + heapEnd = KERNEL_HEAP_START; + heapMax = KERNEL_HEAP_START + KERNEL_HEAP_MAX_SIZE; + + usedMemory = 0; + freeMemory = 0; + + bool ok = expandHeap(KERNEL_HEAP_INITIAL_SIZE); + + ASSERT(ok); +} + +void* memory::heap::allocate(u64 size) { + if (size == 0) { + return nullptr; + } + + size = alignUp(size, 16ull); + + HeapBlock* block = findFreeBlock(size); + + if (!block) { + u64 expandSize = size + sizeof(HeapBlock); + + if (expandSize < 4096) { + expandSize = 4096; + } + + bool ok = expandHeap(expandSize); + + if (!ok) { + return nullptr; + } + + block = findFreeBlock(size); + + if (!block) { + return nullptr; + } + } + + splitBlock(block, size); + + block->free = false; + + usedMemory += block->size; + freeMemory -= block->size; + + return block + 1; +} + +void memory::heap::free(void* ptr) { + if (!ptr) { + debug::error("Tried to free a pointer that was null"); + return; + } + + HeapBlock* block = static_cast(ptr) - 1; + + ASSERT(block->magic == HEAP_MAGIC); + ASSERT(!block->free); + + block->free = true; + + usedMemory -= block->size; + freeMemory += block->size; + + if (block->next && block->next->free) { + mergeWithNext(block); + } + + if (block->prev && block->prev->free) { + mergeWithNext(block->prev); + } +} + +void* memory::heap::realloc(void* ptr, u64 newSize) { + if (!ptr) { + debug::warn("Tried to reallocate a null pointer"); + return allocate(newSize); + } + + ASSERT(newSize > 0); + + HeapBlock* block = static_cast(ptr) - 1; + + ASSERT(block->magic == HEAP_MAGIC); + + if (block->size >= newSize) { + return ptr; + } + + void* newPtr = allocate(newSize); + + if (!newPtr) { + return nullptr; + } + + copy(static_cast(newPtr), static_cast(ptr), static_cast(block->size)); + free(ptr); + + return newPtr; +} + +void* operator new(usize size) { + return memory::alloc(size); +} + +void* operator new[](usize size) { + return memory::alloc(size); +} + +void operator delete(void* ptr) noexcept { + memory::free(ptr); +} + +void operator delete[](void* ptr) noexcept { + memory::free(ptr); +} + +void operator delete(void* ptr, usize) noexcept { + memory::free(ptr); +} + +void operator delete[](void* ptr, usize) noexcept { + memory::free(ptr); +} + + +u64 memory::heap::getFreeMemory() { + return freeMemory; +} + +u64 memory::heap::getUsedMemory() { + return usedMemory; +} + diff --git a/kernel/core/memory/memory.cpp b/kernel/core/memory/memory.cpp index 3883f0b..a4d0470 100644 --- a/kernel/core/memory/memory.cpp +++ b/kernel/core/memory/memory.cpp @@ -10,7 +10,10 @@ #include #include +#include "io/serial.h" #include "kernel/debug.h" +#include "kernel/memory/physicalMemory.h" +#include "kernel/memory/virtualMemory.h" u64 HHDMOffset = 0; @@ -30,3 +33,19 @@ u64 memory::getHHDMOffset() { EXPECT(HHDMOffset != 0); return HHDMOffset; } + +void memory::initMemoryServices(volatile struct limine_memmap_request& request) { + ASSERT(request.response != nullptr); + pmm::init(request); + debug::log("Initialized Physical Memory"); + io::serialWrite("[LOG] Available pages: "); + io::serialWriteNumber(pmm::physicalMemory.totalPages); + io::serialWrite("\n"); + + vmm::init(); + debug::log("Initialized Virtual Memory"); + + heap::init(); + debug::log("Initialized Heap"); +} + diff --git a/kernel/core/memory/pmm.cpp b/kernel/core/memory/pmm.cpp new file mode 100644 index 0000000..6e19e49 --- /dev/null +++ b/kernel/core/memory/pmm.cpp @@ -0,0 +1,246 @@ +/* +* pmm.cpp +* As part of the Avery project +* Created by Max Van den Eynde in 2026 +* -------------------------------------- +* Description: Physical Memory Manager +* Copyright (c) 2026 Max Van den Eynde +*/ + +#include +#include + +#include "kernel/debug.h" + +pmm::PhysicalMemory pmm::physicalMemory; +#define OUT_OF_MEMORY 0 + +void pmm::init(volatile limine_memmap_request& memmapRequest) { + ASSERT(memmapRequest.response != nullptr); + limine_memmap_response* memmap = memmapRequest.response; + ASSERT(memmap->entries != nullptr); + + physicalMemory.highestAddress = 0; + + for (u64 i = 0; i < memmap->entry_count; i++) { + limine_memmap_entry* entry = memmap->entries[i]; + + u64 end = entry->base + entry->length; + if (end > physicalMemory.highestAddress) { + physicalMemory.highestAddress = end; + } + } + + physicalMemory.totalPages = alignUp(physicalMemory.highestAddress, static_cast(PAGE_SIZE)) / PAGE_SIZE; + physicalMemory.bitmapSize = alignUp(physicalMemory.totalPages, static_cast(8)) / 8; + physicalMemory.bitmapSize = alignUp(physicalMemory.bitmapSize, static_cast(PAGE_SIZE)); + + physicalMemory.bitmapPhysicalAddress = 0; + + for (u64 i = 0; i < memmap->entry_count; i++) { + limine_memmap_entry* entry = memmap->entries[i]; + + if (entry->type != LIMINE_MEMMAP_USABLE) { + continue; + } + + u64 start = alignUp(entry->base, static_cast(PAGE_SIZE)); + u64 end = alignDown(entry->base + entry->length, static_cast(PAGE_SIZE)); + + if (end <= start) { + continue; + } + + if (end - start >= physicalMemory.bitmapSize) { + physicalMemory.bitmapPhysicalAddress = start; + break; + } + } + + ASSERT(physicalMemory.bitmapPhysicalAddress != 0); + + physicalMemory.bitmap = reinterpret_cast(physicalToVirtual(physicalMemory.bitmapPhysicalAddress)); + + memory::set(physicalMemory.bitmap, static_cast(0xFF), static_cast(physicalMemory.bitmapSize)); + + physicalMemory.freePages = 0; + physicalMemory.usedPages = physicalMemory.totalPages; + + for (u64 i = 0; i < memmap->entry_count; i++) { + limine_memmap_entry* entry = memmap->entries[i]; + + if (entry->type != LIMINE_MEMMAP_USABLE) { + continue; + } + + u64 start = alignUp(entry->base, static_cast(PAGE_SIZE)); + u64 end = alignDown(entry->base + entry->length, static_cast(PAGE_SIZE)); + + if (end <= start) { + continue; + } + + markFree(start, (end - start) / PAGE_SIZE); + } + + markUsed(physicalMemory.bitmapPhysicalAddress, physicalMemory.bitmapSize / PAGE_SIZE); + + markUsed(0, 1); + + physicalMemory.lastAllocIndex = 0; +} + +void pmm::markUsed(physAddr addr, u64 pageCount) { + u64 startPage = physicalToPage(addr); + + for (u64 i = 0; i < pageCount; i++) { + u64 page = startPage + i; + + if (page >= physicalMemory.totalPages) { + break; + } + + if (!bitmapTest(page)) { + bitmapSet(page); + physicalMemory.freePages--; + physicalMemory.usedPages++; + } + } +} + +void pmm::markFree(physAddr addr, u64 pageCount) { + uint64_t start_page = physicalToPage(addr); + + for (uint64_t i = 0; i < pageCount; i++) { + uint64_t page = start_page + i; + + if (page >= physicalMemory.totalPages) { + break; + } + + if (bitmapTest(page)) { + bitmapClear(page); + physicalMemory.freePages++; + physicalMemory.usedPages--; + } + } +} + +physAddr pmm::allocPage() { + for (u64 i = physicalMemory.lastAllocIndex; i < physicalMemory.totalPages; i++) { + if (!bitmapTest(i)) { + bitmapSet(i); + + physicalMemory.usedPages++; + physicalMemory.freePages--; + physicalMemory.lastAllocIndex = i + 1; + + return pageToPhysical(i); + } + } + + for (u64 i = 0; i < physicalMemory.lastAllocIndex; i++) { + if (!bitmapTest(i)) { + bitmapSet(i); + + physicalMemory.usedPages++; + physicalMemory.freePages--; + physicalMemory.lastAllocIndex = i + 1; + + return pageToPhysical(i); + } + } + + return OUT_OF_MEMORY; +} + +void pmm::freePage(physAddr addr) { + ASSERT(addr % PAGE_SIZE == 0); + + u64 page = physicalToPage(addr); + + ASSERT(page <= physicalMemory.totalPages); + ASSERT(bitmapTest(page)); + + bitmapClear(page); + physicalMemory.freePages++; + physicalMemory.usedPages--; + + if (page < physicalMemory.lastAllocIndex) { + physicalMemory.lastAllocIndex = page; + } +} + +physAddr pmm::allocPages(u64 count) { + if (count == 0) { + debug::warn("Cannot allocate 0 pages"); + return 0; + } + + u64 runStart = 0; + u64 runLength = 0; + + for (u64 i = 0; i < physicalMemory.totalPages; i++) { + if (!bitmapTest(i)) { + if (runLength == 0) { + runStart = i; + } + + runLength++; + + if (runLength == count) { + for (u64 j = 0; j < count; j++) { + bitmapSet(runStart + j); + } + + physicalMemory.freePages -= count; + physicalMemory.usedPages += count; + physicalMemory.lastAllocIndex = runStart + count; + + return pageToPhysical(runStart); + } + } + else { + runLength = 0; + } + } + + return OUT_OF_MEMORY; +} + +void pmm::freePages(physAddr addr, u64 count) { + ASSERT(addr % PAGE_SIZE == 0); + + for (u64 i = 0; i < count; i++) { + freePage(addr + i * PAGE_SIZE); + } +} + +bool pmm::isFree(physAddr addr) { + ASSERT(addr % PAGE_SIZE == 0); + + u64 page = physicalToPage(addr); + + if (page >= physicalMemory.totalPages) { + debug::warn("Page fell out of the total pages that the memory has."); + return false; + } + + return !bitmapTest(page); +} + +u64 pmm::getFreePages() { + return physicalMemory.freePages; +} + +u64 pmm::getTotalPages() { + return physicalMemory.totalPages; +} + +u64 pmm::getFreeMemory() { + return physicalMemory.freePages * PAGE_SIZE; +} + +u64 pmm::getUsedPages() { + return physicalMemory.usedPages; +} diff --git a/kernel/core/memory/vmm.cpp b/kernel/core/memory/vmm.cpp new file mode 100644 index 0000000..eeddee9 --- /dev/null +++ b/kernel/core/memory/vmm.cpp @@ -0,0 +1,169 @@ +/* +* vmm.cpp +* As part of the Avery project +* Created by Max Van den Eynde in 2026 +* -------------------------------------- +* Description: Virtual Memory Manager +* Copyright (c) 2026 Max Van den Eynde +*/ + +#include +#include + +#include "kernel/debug.h" + +#define PTE_PRESENT (1ull << 0) +#define PTE_WRITABLE (1ull << 1) +#define PTE_USER (1ull << 2) +#define PTE_NX (1ull << 63) + +#define PTE_ADDR_MASK 0x000FFFFFFFFFF000ull + +#define PML4_INDEX(v) (((v) >> 39) & 0x1FF) +#define PDPT_INDEX(v) (((v) >> 30) & 0x1FF) +#define PD_INDEX(v) (((v) >> 21) & 0x1FF) +#define PT_INDEX(v) (((v) >> 12) & 0x1FF) + +#define OUT_OF_MEMORY 0 + +static vmm::PageTable* kernelPML4; + +vmm::PageTable* vmm::newTable() { + physAddr phys = pmm::allocPage(); + + ASSERT(phys != OUT_OF_MEMORY); + + auto* table = reinterpret_cast(pmm::physicalToVirtual(phys)); + memory::set(reinterpret_cast(table), static_cast(0), PAGE_SIZE); + + return table; +} + +vmm::PageTable* vmm::getNextTable(PageTable* table, u64 index, bool create) { + u64 entry = table->entries[index]; + + if (entry & PTE_PRESENT) { + physAddr phys = entry & PTE_ADDR_MASK; + return static_cast(pmm::physicalToVirtual(phys)); + } + + if (!create) { + return nullptr; + } + + PageTable* newlyCreated = newTable(); + physAddr newPhys = pmm::virtualToPhysicalHHDM(newlyCreated); + + table->entries[index] = newPhys | PTE_PRESENT | PTE_WRITABLE; + + return newlyCreated; +} + +u64* vmm::getPte(PageTable* pml4, virtAddr virt, bool create) { + PageTable* pdpt = getNextTable(pml4, PML4_INDEX(virt), create); + + if (!pdpt) { + return nullptr; + } + + PageTable* pd = getNextTable(pdpt, PDPT_INDEX(virt), create); + + if (!pd) { + return nullptr; + } + + PageTable* pt = getNextTable(pd, PT_INDEX(virt), create); + + if (!pt) { + return nullptr; + } + + return &pt->entries[PDPT_INDEX(virt)]; +} + +bool vmm::mapPage(PageTable* pml4, virtAddr virt, physAddr phys, u64 flags) { + if ((virt % PAGE_SIZE) != 0 || (phys % PAGE_SIZE) != 0) { + debug::error("Tried to map a page to a virtual or physical address that is not aligned."); + return false; + } + + u64* pte = getPte(pml4, virt, true); + if (!pte) { + return false; + } + + if (*pte & PTE_PRESENT) { + return false; + } + + *pte = (phys & PTE_ADDR_MASK) | flags | PTE_PRESENT; + + return true; +} + +bool vmm::unmapPage(PageTable* pml4, virtAddr virt) { + if (virt % PAGE_SIZE != 0) { + debug::error("Tried to unmap a page to a virtual address that is not aligned."); + return false; + } + + u64* pte = getPte(pml4, virt, false); + if (!pte || !(*pte & PTE_PRESENT)) { + return false; + } + + *pte = 0; + + asm volatile("invlpg (%0)" :: "r"(virt) : "memory"); + + return true; +} + +physAddr vmm::virtToPhysical(PageTable* pml4, virtAddr virt) { + u64* pte = getPte(pml4, virt, false); + + if (!pte || !(*pte & PTE_PRESENT)) { + return false; + } + + physAddr pagePhys = *pte & PTE_ADDR_MASK; + u64 offset = virt & 0xFFF; + + return pagePhys + offset; +} + +bool vmm::mapRange(PageTable* pml4, virtAddr virt, physAddr phys, u64 size, u64 flags) { + u64 pages = (size + PAGE_SIZE - 1) / PAGE_SIZE; + + for (u64 i = 0; i < pages; i++) { + bool ok = mapPage(pml4, virt + i * PAGE_SIZE, phys + i * PAGE_SIZE, flags); + + if (!ok) { + return false; + } + } + + return true; +} + +vmm::PageTable* vmm::createAddressSpace() { + return newTable(); +} + +void vmm::switchAddressSpace(PageTable* table) { + physAddr phys = pmm::virtualToPhysicalHHDM(table); + + asm volatile("mov %0, %%cr3" :: "r"(phys) : "memory"); +} + +void vmm::init() { + physAddr cr3; + + asm volatile("mov %%cr3, %0" : "=r"(cr3)); + + kernelPML4 = static_cast(pmm::physicalToVirtual(cr3 & PTE_ADDR_MASK)); +} + +vmm::PageTable* vmm::getKernelPml4() { + return kernelPML4; +} diff --git a/kernel/graphics/framebuffer.cpp b/kernel/graphics/framebuffer.cpp index 05d8d82..64cbecb 100644 --- a/kernel/graphics/framebuffer.cpp +++ b/kernel/graphics/framebuffer.cpp @@ -31,7 +31,7 @@ namespace { } if (c == ' ') { - return 4; + return 5; } const u8* glyph = font8x16[c - FONT_FIRST]; @@ -50,13 +50,13 @@ namespace { } if (maxCol < 0) { - return 4; + return 5; } - u64 advance = static_cast(maxCol) + 2; + u64 advance = static_cast(maxCol) + 3; - if (advance > FONT_WIDTH) { - return FONT_WIDTH; + if (advance > FONT_WIDTH + 1) { + return FONT_WIDTH + 1; } return advance; @@ -437,14 +437,14 @@ void FramebufferConsole::putChar(char c) { } } -void FramebufferConsole::write(string str) { +void FramebufferConsole::write(cstring str) { while (*str) { putChar(*str); str++; } } -void FramebufferConsole::writeLn(string str) { +void FramebufferConsole::writeLn(cstring str) { while (*str) { putChar(*str); str++; diff --git a/kernel/io/console.cpp b/kernel/io/console.cpp index 488657e..cf04f2e 100644 --- a/kernel/io/console.cpp +++ b/kernel/io/console.cpp @@ -32,7 +32,7 @@ void out::initFramebufferConsole(const Framebuffer& framebuffer) { GlobalFramebufferConsole = consoleMemory; } -void out::print(string str) { +void out::print(cstring str) { if (outputMode == ConsoleOutputMode::Framebuffer && consoleAccess != nullptr) { consoleAccess->write(str); } @@ -41,6 +41,10 @@ void out::print(string str) { } } +void out::print(const string& str) { + print(str.cStr()); +} + void out::clear() { if (outputMode == ConsoleOutputMode::Framebuffer && consoleAccess != nullptr) { consoleAccess->clear(); @@ -49,7 +53,7 @@ void out::clear() { } } -void out::println(string str) { +void out::println(cstring str) { if (outputMode == ConsoleOutputMode::Framebuffer && consoleAccess != nullptr) { consoleAccess->writeLn(str); } @@ -59,6 +63,10 @@ void out::println(string str) { } } +void out::println(const string& str) { + println(str.cStr()); +} + void out::setColor(Color fg, Color bg) { if (outputMode == ConsoleOutputMode::Framebuffer && consoleAccess != nullptr) { consoleAccess->setColor(fg, bg); @@ -104,16 +112,16 @@ void out::printHex(u64 num) { print(hex); } -void out::printNumber(u32 num) { - char buffer[11]; - usize index = 10; +void out::printNumber(u64 num) { + char buffer[21]; + usize index = 20; buffer[index] = '\0'; do { index--; buffer[index] = static_cast('0' + (num % 10)); - num = num / 10; + num /= 10; } while (num != 0); @@ -132,9 +140,8 @@ char in::getChar() { return keyboard::getChar(); } -string in::getLine(string prompt) { - static char buffer[256]; - usize length = 0; +string in::getLine(cstring prompt) { + string line; if (prompt != nullptr) { out::print(prompt); @@ -145,16 +152,15 @@ string in::getLine(string prompt) { if (c == '\n') { out::putChar('\n'); - buffer[length] = '\0'; - return buffer; + return line; } if (c == '\b') { - if (length > 0) { - --length; + if (!line.empty()) { + char removed = line.popBack(); if (out::outputMode == ConsoleOutputMode::Framebuffer && GlobalFramebufferConsole != nullptr) { - GlobalFramebufferConsole->backspace(buffer[length]); + GlobalFramebufferConsole->backspace(removed); } else { out::putChar('\b'); @@ -164,10 +170,7 @@ string in::getLine(string prompt) { continue; } - if (length < 255) { - buffer[length] = c; - ++length; - out::putChar(c); - } + line.append(c); + out::putChar(c); } } diff --git a/kernel/io/serial.cpp b/kernel/io/serial.cpp index 007bea9..e0e5668 100644 --- a/kernel/io/serial.cpp +++ b/kernel/io/serial.cpp @@ -15,7 +15,7 @@ void io::serialWriteChar(char c) { outb(0xE9, static_cast(c)); } -void io::serialWrite(string input) { +void io::serialWrite(cstring input) { while (*input) { serialWriteChar(*input); input++; @@ -50,17 +50,18 @@ void io::serialWriteHex(u64 num) { serialWrite(hex); } -void io::serialWriteNumber(u32 num) { - char buffer[11]; - usize index = 10; +void io::serialWriteNumber(u64 num) { + char buffer[21]; + usize index = 20; buffer[index] = '\0'; do { index--; buffer[index] = static_cast('0' + (num % 10)); - num = num / 10; - } while (num != 0); + num /= 10; + } + while (num != 0); serialWrite(&buffer[index]); } diff --git a/kernel/main.cpp b/kernel/main.cpp index 194e655..c1dcb1f 100644 --- a/kernel/main.cpp +++ b/kernel/main.cpp @@ -1,5 +1,6 @@ #include +#include "tests.h" #include "../include/kernel/console.h" #include "core/regs.h" #include "core/systems.h" @@ -26,6 +27,13 @@ static volatile struct limine_hhdm_request hddm_request = { .response = nullptr }; +__attribute__((used, section(".limine_requests"))) +static volatile struct limine_memmap_request memmap_request = { + .id = LIMINE_MEMMAP_REQUEST_ID, + .revision = 0, + .response = nullptr, +}; + __attribute__((used, section(".limine_requests_start"))) static volatile uint64_t limine_requests_start_marker[] = LIMINE_REQUESTS_START_MARKER; @@ -33,31 +41,32 @@ __attribute__((used, section(".limine_requests_end"))) static volatile uint64_t limine_requests_end_marker[] = LIMINE_REQUESTS_END_MARKER; extern "C" [[noreturn]] void _start() { - memory::setHHDM(hddm_request); - - regs::enableSSE(); - core::initSystems(); - if (LIMINE_BASE_REVISION_SUPPORTED(limine_base_revision) == false) { while (true) { asm("hlt"); } } + memory::setHHDM(hddm_request); + + regs::enableSSE(); + core::initSystems(); + memory::initMemoryServices(memmap_request); + Framebuffer framebuffer = Framebuffer::createFromLimineRequest(framebuffer_request); out::initFramebufferConsole(framebuffer); + out::setColor(Color::white, Color::black); + out::clear(); out::println("The Avery Kernel"); out::println("Version Alpha 1 (Development Edition)"); - out::println("Made by Neutral Software in 2026"); - - volatile u32 a = 12; - volatile u32 b = 0; - - volatile u32 c = a / b; - out::printNumber(reinterpret_cast(c)); - + out::println("Made by Max Van den Eynde in 2026"); while (true) { - asm("hlt"); + string input = in::getLine("> "); + if (input == "clear") { + out::clear(); + continue; + } + out::println(input); } } diff --git a/kernel/tests/memoryTests.cpp b/kernel/tests/memoryTests.cpp new file mode 100644 index 0000000..c42091d --- /dev/null +++ b/kernel/tests/memoryTests.cpp @@ -0,0 +1,259 @@ +/* +* memoryTests.cpp +* As part of the Avery project +* Created by Max Van den Eynde in 2026 +* -------------------------------------- +* Description: +* Copyright (c) 2026 Max Van den Eynde +*/ + +#include "tests.h" +#include "drivers/pit.h" +#include "kernel/console.h" +#include "kernel/debug.h" +#include "kernel/memory/physicalMemory.h" +#include "kernel/memory/virtualMemory.h" + +void tests::runAllMemoryTests() { + pmmTest(); + vmmTest(); + mallocTest(); +} + +void tests::pmmTest() { + out::setColor(Color::white, Color::blue); + out::clear(); + out::println("TEST: PHYSICAL MEMORY MANAGER TEST"); + out::println("========================"); + out::print("PMM total: "); + out::printNumber(pmm::getTotalPages()); + out::println(""); + out::print("PMM used: "); + out::printNumber(pmm::getUsedPages()); + out::println(""); + out::print("PMM available: "); + out::printNumber(pmm::getFreePages()); + out::println(""); + out::print("Free Memory: "); + out::printNumber(pmm::getFreeMemory()); + out::println(""); + + // Test allocation + physAddr a = pmm::allocPage(); + physAddr b = pmm::allocPage(); + + out::println("\n========================"); + out::println("POST ALLOC"); + out::print("PMM total: "); + out::printNumber(pmm::getTotalPages()); + out::println(""); + out::print("PMM used: "); + out::printNumber(pmm::getUsedPages()); + out::println(""); + out::print("PMM available: "); + out::printNumber(pmm::getFreePages()); + out::println(""); + out::print("Free Memory: "); + out::printNumber(pmm::getFreeMemory()); + out::println(""); + out::print("a address: "); + out::printNumber(a); + out::println(""); + out::print("b address: "); + out::printNumber(b); + out::println(""); + + // Free + alloc again + pmm::freePage(a); + physAddr c = pmm::allocPage(); + + out::println("\n========================"); + out::println("POST FREE"); + out::print("PMM total: "); + out::printNumber(pmm::getTotalPages()); + out::println(""); + out::print("PMM used: "); + out::printNumber(pmm::getUsedPages()); + out::println(""); + out::print("PMM available: "); + out::printNumber(pmm::getFreePages()); + out::println(""); + out::print("Free Memory: "); + out::printNumber(pmm::getFreeMemory()); + out::println(""); + out::print("a address: "); + out::printNumber(a); + out::println(""); + out::print("b address: "); + out::printNumber(b); + out::println(""); + out::print("c address: "); + out::printNumber(c); + out::println(""); + TEST_RESULT(a == c); + + time::wait(3000); +} + +void tests::vmmTest() { + out::setColor(Color::white, Color::blue); + out::clear(); + + out::println("TEST: VIRTUAL MEMORY MANAGER TEST"); + out::println("========================"); + + physAddr phys = pmm::allocPage(); + + out::print("Allocated physical page: "); + out::printNumber(phys); + out::println(""); + + TEST_RESULT(phys != 0); + + virtAddr virt = 0xFFFF900000000000; + + bool mapped = vmm::mapPage( + vmm::getKernelPml4(), + virt, + phys, + vmm::FlagWritable + ); + + out::println("\n========================"); + out::println("POST MAP"); + + out::print("Virtual address: "); + out::printNumber(virt); + out::println(""); + + out::print("Physical address: "); + out::printNumber(phys); + out::println(""); + + out::print("Mapped: "); + out::printNumber(mapped); + out::println(""); + + TEST_RESULT(mapped); + + + volatile u64* ptr = (volatile u64*)virt; + *ptr = 0x123456789ABCDEF0; + + u64 readValue = *ptr; + + out::println("\n========================"); + out::println("MEMORY WRITE TEST"); + + out::print("Written value: "); + out::printNumber(0x123456789ABCDEF0); + out::println(""); + + out::print("Read value: "); + out::printNumber(readValue); + out::println(""); + + TEST_RESULT(readValue == 0x123456789ABCDEF0); + + physAddr translated = vmm::virtToPhysical(vmm::getKernelPml4(), virt); + + out::println("\n========================"); + out::println("TRANSLATION TEST"); + + out::print("Translated physical: "); + out::printNumber(translated); + out::println(""); + + TEST_RESULT(translated == phys); + + bool unmapped = vmm::unmapPage(vmm::getKernelPml4(), virt); + + out::println("\n========================"); + out::println("POST UNMAP"); + + out::print("Unmapped: "); + out::printNumber(unmapped); + out::println(""); + + TEST_RESULT(unmapped); + + physAddr translatedAfterUnmap = vmm::virtToPhysical(vmm::getKernelPml4(), virt); + + out::print("Translated after unmap: "); + out::printNumber(translatedAfterUnmap); + out::println(""); + + TEST_RESULT(translatedAfterUnmap == 0); + + pmm::freePage(phys); + + out::println("\n========================"); + out::println("VMM TEST COMPLETE"); + + time::wait(3000); +} + +void tests::mallocTest() { + out::setColor(Color::white, Color::blue); + out::clear(); + + out::println("TEST: KERNEL HEAP TEST"); + out::println("========================"); + + out::print("Heap used: "); + out::printNumber(memory::heap::getUsedMemory()); + out::println(""); + + out::print("Heap free: "); + out::printNumber(memory::heap::getFreeMemory()); + out::println(""); + + void* a = memory::alloc(64); + void* b = memory::alloc(128); + + out::println("\nPOST MALLOC"); + + out::print("a: "); + out::printNumber(reinterpret_cast(a)); + out::println(""); + + out::print("b: "); + out::printNumber(reinterpret_cast(b)); + out::println(""); + + TEST_RESULT(a != nullptr); + TEST_RESULT(b != nullptr); + TEST_RESULT(a != b); + + u64* x = static_cast(a); + *x = 0x123456789ABCDEF0; + + TEST_RESULT(*x == 0x123456789ABCDEF0); + + memory::free(a); + + void* c = memory::alloc(32); + + out::println("\nPOST FREE + MALLOC"); + + out::print("c: "); + out::printNumber((u64)c); + out::println(""); + + TEST_RESULT(c == a); + + memory::free(b); + memory::free(c); + + out::println("\nFINAL HEAP STATS"); + + out::print("Heap used: "); + out::printNumber(memory::heap::getUsedMemory()); + out::println(""); + + out::print("Heap free: "); + out::printNumber(memory::heap::getFreeMemory()); + out::println(""); + + time::wait(3000); +} diff --git a/kernel/utils/types.cpp b/kernel/utils/types.cpp new file mode 100644 index 0000000..47a5fb0 --- /dev/null +++ b/kernel/utils/types.cpp @@ -0,0 +1,202 @@ +/* +* types.cpp +* As part of the Avery project +* Created by Max Van den Eynde in 2026 +* -------------------------------------- +* Description: Types definition for easy interoperability and access +* Copyright (c) 2026 Max Van den Eynde +*/ + +#include +#include + +string::string() { + len = 0; + capacity = 0; + + data = new char[1]; + data[0] = '\0'; +} + +string::string(cstring str) { + usize strLength = 0; + + while (str[strLength] != '\0') { + strLength++; + } + + len = strLength; + capacity = strLength; + + data = new char[capacity + 1]; + memory::copy(reinterpret_cast(data), reinterpret_cast(str), static_cast(len)); + data[len] = '\0'; +} + +string::string(const string& other) { + len = other.len; + capacity = other.capacity; + + data = new char[capacity + 1]; + memory::copy(reinterpret_cast(data), reinterpret_cast(other.data), static_cast(len)); + data[len] = '\0'; +} + +string::string(string&& other) noexcept { + len = other.len; + capacity = other.capacity; + data = other.data; + + other.len = 0; + other.capacity = 0; + other.data = new char[1]; + other.data[0] = '\0'; +} + +string::~string() { + delete[] data; +} + +string& string::operator=(const string& other) { + if (this == &other) { + return *this; + } + + char* newData = new char[other.capacity + 1]; + + memory::copy( + reinterpret_cast(newData), + reinterpret_cast(other.data), + static_cast(other.len) + ); + + newData[other.len] = '\0'; + + delete[] data; + + data = newData; + len = other.len; + capacity = other.capacity; + + return *this; +} + +string& string::operator=(string&& other) noexcept { + if (this == &other) { + return *this; + } + + delete[] data; + + data = other.data; + len = other.len; + capacity = other.capacity; + + other.len = 0; + other.capacity = 0; + other.data = new char[1]; + other.data[0] = '\0'; + + return *this; +} + +bool string::operator==(const string& other) const { + if (other.len != this->len) { + return false; + } + + usize strLength = other.len; + for (usize i = 0; i < strLength; i++) { + if (data[i] != other.data[i]) { + return false; + } + } + + return true; +} + +bool string::operator!=(const string& other) const { + return !(*this == other); +} + +cstring string::cStr() const { + return data; +} + +usize string::length() const { + return len; +} + +bool string::empty() const { + return len == 0; +} + +void string::clear() { + len = 0; + data[0] = '\0'; +} + +void string::reserve(usize newCapacity) { + if (newCapacity <= capacity) { + return; + } + + char* newData = new char[newCapacity + 1]; + memory::copy(reinterpret_cast(newData), reinterpret_cast(data), static_cast(len)); + newData[len] = '\0'; + delete[] data; + + data = newData; + capacity = newCapacity; +} + +void string::append(char c) { + reserve(len + 1); + memory::copy(reinterpret_cast(data) + len, reinterpret_cast(&c), 1); + len++; + data[len] = '\0'; +} + +char string::popBack() { + if (len == 0) { + return '\0'; + } + + len--; + char c = data[len]; + data[len] = '\0'; + + return c; +} + +void string::append(cstring text) { + usize textLength = 0; + + while (text[textLength] != '\0') { + textLength++; + } + + reserve(len + textLength); + memory::copy(reinterpret_cast(data) + len, reinterpret_cast(text), + static_cast(textLength)); + len += textLength; + data[len] = '\0'; +} + +Option string::operator[](usize index) { + if (index >= len) { + return Option::none(); + } + + return data[index]; +} + +Option string::operator[](usize index) const { + if (index >= len) { + return Option::none(); + } + + return data[index]; +} + +