diff --git a/kernel/include/memory/AddressSpace.h b/kernel/include/memory/AddressSpace.h index 2b17c328..c125b0a0 100644 --- a/kernel/include/memory/AddressSpace.h +++ b/kernel/include/memory/AddressSpace.h @@ -1,9 +1,10 @@ #pragma once #include "memory/Paging.h" +#include "utils/Result.h" struct AddressSpace { - static AddressSpace create(); + static Result create(); void destroy(); diff --git a/kernel/include/memory/PMM.h b/kernel/include/memory/PMM.h index 991361d5..83d4482b 100644 --- a/kernel/include/memory/PMM.h +++ b/kernel/include/memory/PMM.h @@ -1,15 +1,13 @@ #pragma once +#include "utils/Result.h" #include -#define PMM_FAILED (void*)-1 -#define PMM_DID_FAIL(addr) (void*)addr == PMM_FAILED - namespace PMM { void init(); - void* request_page(); - void* request_pages(uint64_t count); + Result request_page(); + Result request_pages(uint64_t count); void free_page(void* address); void free_pages(void* address, uint64_t count); diff --git a/kernel/include/utils/Result.h b/kernel/include/utils/Result.h new file mode 100644 index 00000000..11cd1f2a --- /dev/null +++ b/kernel/include/utils/Result.h @@ -0,0 +1,125 @@ +#pragma once +#include "std/ensure.h" +#include "std/string.h" +#include "utils/move.h" +#include "utils/new.h" + +struct Error +{ + Error(int err) + { + error = err; + } + + int error; +}; + +template class Result +{ + public: + Result(const T& value) + { + m_storage.store_reference(value); + m_has_value = true; + m_has_error = false; + } + + Result(T&& value) + { + m_storage.store_movable_reference(move(value)); + m_has_value = true; + m_has_error = false; + } + + Result(const Result& other) + { + m_storage.store_reference(other.m_storage.fetch_reference()); + m_has_value = true; + m_has_error = false; + } + + Result(Result&& other) + { + m_storage.store_movable_reference(move(other.m_storage.fetch_reference())); + m_has_value = true; + m_has_error = false; + } + + Result(const Error& err) + { + m_error = err.error; + m_has_error = true; + m_has_value = false; + } + + bool has_error() + { + return m_has_error; + } + + bool has_value() + { + return m_has_value; + } + + int error() + { + ensure(has_error()); + return m_error; + } + + Error release_error() + { + ensure(has_error()); + return {m_error}; + } + + T value() + { + ensure(has_value()); + return m_storage.fetch_reference(); + } + + T release_value() + { + ensure(has_value()); + T item = m_storage.fetch_reference(); + m_has_value = false; + return move(item); + } + + private: + struct Storage + { + unsigned char buffer[sizeof(T)]; + + T* fetch_ptr() + { + return (T*)buffer; + } + + T& fetch_reference() + { + return *fetch_ptr(); + } + + void store_ptr(T* ptr) + { + new (buffer) T(*ptr); + } + + void store_reference(const T& ref) + { + new (buffer) T(ref); + } + + void store_movable_reference(T&& ref) + { + new (buffer) T(ref); + } + }; + Storage m_storage; + int m_error; + bool m_has_error; + bool m_has_value; +}; \ No newline at end of file diff --git a/kernel/include/utils/new.h b/kernel/include/utils/new.h new file mode 100644 index 00000000..1e6afcd1 --- /dev/null +++ b/kernel/include/utils/new.h @@ -0,0 +1,13 @@ +#pragma once +#include + +inline void* operator new(size_t, void* p) noexcept +{ + return p; +} +inline void* operator new[](size_t, void* p) noexcept +{ + return p; +} +inline void operator delete(void*, void*) noexcept {}; +inline void operator delete[](void*, void*) noexcept {}; \ No newline at end of file diff --git a/kernel/src/memory/AddressSpace.cpp b/kernel/src/memory/AddressSpace.cpp index c3c4468b..7a0e7473 100644 --- a/kernel/src/memory/AddressSpace.cpp +++ b/kernel/src/memory/AddressSpace.cpp @@ -4,14 +4,17 @@ #include "log/Log.h" #include "memory/PMM.h" #include "memory/VMM.h" +#include "std/errno.h" #include "std/stdlib.h" #include "std/string.h" #include "utils/move.h" -AddressSpace AddressSpace::create() +Result AddressSpace::create() { AddressSpace result; - result.m_pml4 = (PageTable*)PMM::request_page(); + auto page = PMM::request_page(); + if (page.has_error()) return page.release_error(); + result.m_pml4 = (PageTable*)page.release_value(); memset(result.m_pml4, 0, PAGE_SIZE); VMM::install_kernel_page_directory_into_address_space(result); return move(result); @@ -156,8 +159,9 @@ void AddressSpace::clear() static PageTable* try_clone_page_table(PageTable* source) { - PageTable* dst = (PageTable*)PMM::request_page(); - if (PMM_DID_FAIL(dst)) { return 0; } + auto page = PMM::request_page(); + if (page.has_error()) { return 0; } + PageTable* dst = (PageTable*)page.release_value(); memcpy(dst, source, sizeof(PageTable)); return dst; } diff --git a/kernel/src/memory/MemoryManager.cpp b/kernel/src/memory/MemoryManager.cpp index a087440e..c8b794f6 100644 --- a/kernel/src/memory/MemoryManager.cpp +++ b/kernel/src/memory/MemoryManager.cpp @@ -10,6 +10,8 @@ #include "misc/utils.h" #include "std/ensure.h" +// FIXME: Use Result in here. + void MemoryManager::init() { KernelHeap::clear(); @@ -123,15 +125,15 @@ void* MemoryManager::get_page(int flags) void* MemoryManager::get_page_at(uint64_t addr, int flags) { - void* physicalAddress = PMM::request_page(); - if (PMM_DID_FAIL(physicalAddress)) + auto paddr = PMM::request_page(); + if (paddr.has_error()) { #ifdef MM_DEBUG kwarnln("OOM while allocating one page of memory. this is not good..."); #endif return 0; } - VMM::map(addr, (uint64_t)physicalAddress, flags); + VMM::map(addr, (uint64_t)paddr.release_value(), flags); return (void*)addr; } @@ -172,17 +174,19 @@ void* MemoryManager::get_pages_at(uint64_t addr, uint64_t count, int flags) #endif for (uint64_t i = 0; i < count; i++) { - void* physicalAddress = PMM::request_page(); - if (PMM_DID_FAIL(physicalAddress)) // OOM: No physical memory available! Since this might be at the end of a - // long allocation, we should be able to recover most of it and allocate a - // smaller range, so this might not be fatal. + auto paddr = PMM::request_page(); + if (paddr.has_error()) // OOM: No physical memory available! Since this might be at the end of a + // long allocation, we should be able to recover most of it and allocate a + // smaller range, so this might not be fatal. { #ifdef MM_DEBUG kwarnln("OOM while allocating page %ld of memory. this might be recoverable...", i); #endif + // FIXME: Weren't we supposed to free all previously allocated pages, to avoid leaks when failing large + // allocations? return 0; } - VMM::map(addr + (i * PAGE_SIZE), (uint64_t)physicalAddress, flags); + VMM::map(addr + (i * PAGE_SIZE), (uint64_t)paddr.release_value(), flags); } return (void*)addr; } diff --git a/kernel/src/memory/PMM.cpp b/kernel/src/memory/PMM.cpp index a587b8fc..747042f2 100644 --- a/kernel/src/memory/PMM.cpp +++ b/kernel/src/memory/PMM.cpp @@ -7,6 +7,7 @@ #include "memory/MemoryManager.h" #include "misc/utils.h" #include "std/ensure.h" +#include "std/errno.h" #include "std/string.h" extern BOOTBOOT bootboot; @@ -83,7 +84,7 @@ static void bitmap_set(uint64_t index, bool value) if (value) { virtual_bitmap_addr[byteIndex] |= bitIndexer; } } -void* PMM::request_page() +Result PMM::request_page() { for (uint64_t index = start_index; index < (bitmap_size * 8); index++) { @@ -95,10 +96,10 @@ void* PMM::request_page() return (void*)(index * PAGE_SIZE); } - return PMM_FAILED; + return {ENOMEM}; } -void* PMM::request_pages(uint64_t count) +Result PMM::request_pages(uint64_t count) { uint64_t contiguous = 0; uint64_t contiguous_start = 0; @@ -125,7 +126,7 @@ void* PMM::request_pages(uint64_t count) } } - return PMM_FAILED; + return {ENOMEM}; } void PMM::free_page(void* address) diff --git a/kernel/src/memory/VMM.cpp b/kernel/src/memory/VMM.cpp index 54a26e5d..b36c3b6a 100644 --- a/kernel/src/memory/VMM.cpp +++ b/kernel/src/memory/VMM.cpp @@ -196,8 +196,9 @@ PageDirectoryEntry* VMM::create_pde_if_not_exists(PageTable* root, uint64_t vadd decompose_vaddr(vaddr, page_index, indexes[2], indexes[1], indexes[0]); auto pde_create_if_not_present = [&]() { - pt = (PageTable*)PMM::request_page(); - ensure(!(PMM_DID_FAIL(pt))); + auto pt_or_error = PMM::request_page(); + ensure(pt_or_error.has_value()); + pt = (PageTable*)pt_or_error.release_value(); memset(pt, 0, PAGE_SIZE); pde->set_address((uint64_t)pt); pde->present = true; @@ -341,7 +342,10 @@ void VMM::install_kernel_page_directory_into_address_space(AddressSpace& space) if (!space_last_pdp_pde.present) { - space_last_pdp = (PageTable*)PMM::request_page(); // FIXME: Add out-of-memory checks. + auto result = PMM::request_page(); + ensure(result.has_value()); // FIXME: Propagate this error. + space_last_pdp = (PageTable*)result.release_value(); + memset(space_last_pdp, 0, PAGE_SIZE); space_last_pdp_pde.present = true; diff --git a/kernel/src/thread/Scheduler.cpp b/kernel/src/thread/Scheduler.cpp index 224aa07f..0062c2cf 100644 --- a/kernel/src/thread/Scheduler.cpp +++ b/kernel/src/thread/Scheduler.cpp @@ -171,7 +171,7 @@ long Scheduler::load_user_task(const char* filename) Interrupts::pop(); return -ENOMEM; } - new_task->address_space = AddressSpace::create(); + new_task->address_space = AddressSpace::create().release_value(); // FIXME: Propagate this error. VMM::switch_to_user_address_space(new_task->address_space); long result; if ((result = ELFLoader::check_elf_image_from_filesystem(filename)) < 0)