diff --git a/src/kernel/apic.cpp b/src/kernel/apic.cpp index d56d500..fc7dd69 100644 --- a/src/kernel/apic.cpp +++ b/src/kernel/apic.cpp @@ -1,8 +1,8 @@ +#include "kutil/assert.h" #include "apic.h" -#include "assert.h" #include "interrupts.h" #include "log.h" -#include "memory_pages.h" +#include "page_manager.h" static uint32_t diff --git a/src/kernel/assert.cpp b/src/kernel/assert.cpp index 66ad5ed..4f0dd38 100644 --- a/src/kernel/assert.cpp +++ b/src/kernel/assert.cpp @@ -1,4 +1,4 @@ -#include "assert.h" +#include "kutil/assert.h" #include "console.h" [[noreturn]] void diff --git a/src/kernel/assert.h b/src/kernel/assert.h deleted file mode 100644 index 1019d28..0000000 --- a/src/kernel/assert.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -[[noreturn]] void __kernel_assert(const char *file, unsigned line, const char *message); - -#define kassert(stmt, message) if(!(stmt)) { __kernel_assert(__FILE__, __LINE__, (message)); } else {} diff --git a/src/kernel/device_manager.cpp b/src/kernel/device_manager.cpp index 5b2b07d..1b98c11 100644 --- a/src/kernel/device_manager.cpp +++ b/src/kernel/device_manager.cpp @@ -1,15 +1,15 @@ #include #include +#include "kutil/assert.h" #include "kutil/memory.h" #include "acpi_tables.h" #include "apic.h" -#include "assert.h" #include "device_manager.h" #include "interrupts.h" #include "log.h" #include "memory.h" -#include "memory_pages.h" +#include "page_manager.h" static const char expected_signature[] = "RSD PTR "; diff --git a/src/kernel/font.cpp b/src/kernel/font.cpp index de571be..e5320eb 100644 --- a/src/kernel/font.cpp +++ b/src/kernel/font.cpp @@ -1,4 +1,4 @@ -#include "assert.h" +#include "kutil/assert.h" #include "font.h" /* PSF2 header format diff --git a/src/kernel/interrupts.cpp b/src/kernel/interrupts.cpp index 9788be3..b03fd06 100644 --- a/src/kernel/interrupts.cpp +++ b/src/kernel/interrupts.cpp @@ -7,7 +7,7 @@ #include "interrupts.h" #include "io.h" #include "log.h" -#include "memory_pages.h" +#include "page_manager.h" enum class gdt_flags : uint8_t { diff --git a/src/kernel/kalloc.cpp b/src/kernel/kalloc.cpp new file mode 100644 index 0000000..af23047 --- /dev/null +++ b/src/kernel/kalloc.cpp @@ -0,0 +1,10 @@ +#include "kutil/memory_manager.h" + +kutil::memory_manager g_kernel_memory_manager; + +void * kalloc(size_t length) { return g_kernel_memory_manager.allocate(length); } +void kfree(void *p) { g_kernel_memory_manager.free(p); } +void * operator new (size_t n) { return g_kernel_memory_manager.allocate(n); } +void * operator new[] (size_t n) { return g_kernel_memory_manager.allocate(n); } +void operator delete (void *p) { return g_kernel_memory_manager.free(p); } +void operator delete[] (void *p){ return g_kernel_memory_manager.free(p); } diff --git a/src/kernel/kalloc.h b/src/kernel/kalloc.h new file mode 100644 index 0000000..e45eaed --- /dev/null +++ b/src/kernel/kalloc.h @@ -0,0 +1,17 @@ +#pragma once +/// \file kalloc.h +/// Definitions of kalloc() and kfree() + +#include + + +/// Allocate kernel space memory. +/// \arg length The amount of memory to allocate, in bytes +/// \returns A pointer to the allocated memory, or nullptr if +/// allocation failed. +inline void * kalloc(size_t length); + +/// Free kernel space memory. +/// \arg p The pointer to free +inline void kfree(void *p); + diff --git a/src/kernel/log.cpp b/src/kernel/log.cpp index a393e82..c1b6231 100644 --- a/src/kernel/log.cpp +++ b/src/kernel/log.cpp @@ -1,6 +1,6 @@ #include +#include "kutil/assert.h" #include "kutil/memory.h" -#include "assert.h" #include "console.h" #include "log.h" diff --git a/src/kernel/main.cpp b/src/kernel/main.cpp index bb716ba..375cda5 100644 --- a/src/kernel/main.cpp +++ b/src/kernel/main.cpp @@ -1,6 +1,7 @@ #include #include +#include "kutil/assert.h" #include "kutil/memory.h" #include "console.h" #include "cpu.h" @@ -11,7 +12,7 @@ #include "kernel_data.h" #include "log.h" #include "memory.h" -#include "memory_pages.h" +#include "page_manager.h" #include "screen.h" #include "serial.h" @@ -22,6 +23,9 @@ extern "C" { void *__bss_start, *__bss_end; } +extern [[noreturn]] void +__kernel_assert(const char *file, unsigned line, const char *message); + void init_console(const popcorn_data *header) { @@ -58,9 +62,11 @@ void do_error_1() { do_error_2(); } void kernel_main(popcorn_data *header) { + kutil::assert_set_callback(__kernel_assert); + page_manager *pager = new (&g_page_manager) page_manager; - memory_initialize_managers( + memory_initialize( header->memory_map, header->memory_map_length, header->memory_map_desc_size); diff --git a/src/kernel/memory_bootstrap.cpp b/src/kernel/memory_bootstrap.cpp index 077a9dc..7c3297f 100644 --- a/src/kernel/memory_bootstrap.cpp +++ b/src/kernel/memory_bootstrap.cpp @@ -1,7 +1,7 @@ +#include "kutil/assert.h" #include "kutil/memory.h" -#include "assert.h" #include "memory.h" -#include "memory_pages.h" +#include "page_manager.h" const unsigned efi_page_size = 0x1000; @@ -407,7 +407,7 @@ page_in_ident( } void -memory_initialize_managers(const void *memory_map, size_t map_length, size_t desc_length) +memory_initialize(const void *memory_map, size_t map_length, size_t desc_length) { // The bootloader reserved 16 pages for page tables, which we'll use to bootstrap. // The first one is the already-installed PML4, so grab it from CR3. diff --git a/src/kernel/memory_pages.cpp b/src/kernel/page_manager.cpp similarity index 95% rename from src/kernel/memory_pages.cpp rename to src/kernel/page_manager.cpp index f2030dd..469f762 100644 --- a/src/kernel/memory_pages.cpp +++ b/src/kernel/page_manager.cpp @@ -1,14 +1,12 @@ #include -#include "assert.h" +#include "kutil/assert.h" +#include "kutil/memory_manager.h" #include "log.h" -#include "memory.h" -#include "memory_pages.h" +#include "page_manager.h" page_manager g_page_manager; -using addr_t = page_manager::addr_t; - static addr_t pt_to_phys(page_table *pt) @@ -16,6 +14,7 @@ pt_to_phys(page_table *pt) return reinterpret_cast(pt) - page_manager::page_offset; } + static page_table * pt_from_phys(addr_t p) { @@ -29,6 +28,18 @@ struct free_page_header size_t count; }; + +void mm_grow_callback(void *next, size_t length) +{ + kassert(length % page_manager::page_size == 0, + "Heap manager requested a fractional page."); + + size_t pages = length / page_manager::page_size; + log::info(logs::memory, "Heap manager growing heap by %d pages.", pages); + g_page_manager.map_pages(reinterpret_cast(next), pages); +} + + size_t page_block::length(page_block *list) { @@ -203,7 +214,10 @@ page_manager::init( } } - new (&g_kernel_memory_manager) memory_manager(reinterpret_cast(end)); + extern kutil::memory_manager g_kernel_memory_manager; + new (&g_kernel_memory_manager) kutil::memory_manager( + reinterpret_cast(end), + mm_grow_callback); } void diff --git a/src/kernel/memory_pages.h b/src/kernel/page_manager.h similarity index 97% rename from src/kernel/memory_pages.h rename to src/kernel/page_manager.h index cb94804..508fc8d 100644 --- a/src/kernel/memory_pages.h +++ b/src/kernel/page_manager.h @@ -1,10 +1,11 @@ #pragma once -/// \file memory_pages.h +/// \file page_manager.h /// The page memory manager and related definitions. #include #include +#include "kutil/memory.h" #include "kutil/enum_bitfields.h" struct page_block; @@ -16,8 +17,6 @@ struct free_page_header; class page_manager { public: - using addr_t = uint64_t; - /// Size of a single page. static const size_t page_size = 0x1000; @@ -53,8 +52,6 @@ public: static page_manager * get(); private: - friend void memory_initialize_managers(const void *, size_t, size_t); - /// Set up the memory manager from bootstraped memory void init( page_block *free, @@ -143,6 +140,7 @@ private: page_block *m_block_cache; ///< Cache of unused page_block structs free_page_header *m_page_cache; ///< Cache of free pages to use for tables + friend void memory_initialize(const void *, size_t, size_t); page_manager(const page_manager &) = delete; }; @@ -175,8 +173,6 @@ IS_BITFIELD(page_block_flags); /// linked list of such structures. struct page_block { - using addr_t = page_manager::addr_t; - addr_t physical_address; addr_t virtual_address; uint32_t count; @@ -294,3 +290,6 @@ template inline T page_align(T p) /// \returns The next page-table-aligned address _after_ `p`. template inline T page_table_align(T p) { return ((p - 1) & ~0x1fffffull) + 0x200000; } + +/// Bootstrap the memory managers. +void memory_initialize(const void *memory_map, size_t map_length, size_t desc_length); diff --git a/src/modules/kutil/assert.cpp b/src/modules/kutil/assert.cpp new file mode 100644 index 0000000..c02907c --- /dev/null +++ b/src/modules/kutil/assert.cpp @@ -0,0 +1,16 @@ +#include "assert.h" + +namespace kutil { + +assert_callback __kernel_assert_p = nullptr; + +assert_callback +assert_set_callback(assert_callback f) +{ + assert_callback old = __kernel_assert_p; + __kernel_assert_p = f; + return old; +} + + +} // namespace kutil diff --git a/src/modules/kutil/assert.h b/src/modules/kutil/assert.h new file mode 100644 index 0000000..aceaea6 --- /dev/null +++ b/src/modules/kutil/assert.h @@ -0,0 +1,19 @@ +#pragma once + +namespace kutil { + +using assert_callback = + [[noreturn]] void (*) + (const char *file, unsigned line, const char *message); + +/// Set the global kernel assert callback +/// \args f The new callback +/// \returns The old callback +assert_callback assert_set_callback(assert_callback f); + +extern assert_callback __kernel_assert_p; + +} // namespace kutil + + +#define kassert(stmt, message) if(!(stmt)) { ::kutil::__kernel_assert_p(__FILE__, __LINE__, (message)); } else {} diff --git a/src/modules/kutil/memory.cpp b/src/modules/kutil/memory.cpp index 8ad1d5f..617daa0 100644 --- a/src/modules/kutil/memory.cpp +++ b/src/modules/kutil/memory.cpp @@ -1,5 +1,7 @@ #include "memory.h" +void * operator new (size_t, void *p) { return p; } + namespace kutil { void * diff --git a/src/modules/kutil/memory.h b/src/modules/kutil/memory.h index b94b5d4..9c4663d 100644 --- a/src/modules/kutil/memory.h +++ b/src/modules/kutil/memory.h @@ -5,6 +5,8 @@ using addr_t = uint64_t; +void * operator new (size_t, void *p); + namespace kutil { diff --git a/src/kernel/memory.cpp b/src/modules/kutil/memory_manager.cpp similarity index 68% rename from src/kernel/memory.cpp rename to src/modules/kutil/memory_manager.cpp index 37f33d3..5646d2d 100644 --- a/src/kernel/memory.cpp +++ b/src/modules/kutil/memory_manager.cpp @@ -1,11 +1,10 @@ -#include "kutil/enum_bitfields.h" -#include "kutil/memory.h" +#include #include "assert.h" -#include "log.h" #include "memory.h" -#include "memory_pages.h" +#include "memory_manager.h" + +namespace kutil { -memory_manager g_kernel_memory_manager; struct memory_manager::mem_header { @@ -70,14 +69,16 @@ private: memory_manager::memory_manager() : m_start(nullptr), - m_length(0) + m_length(0), + m_grow(nullptr) { kutil::memset(m_free, 0, sizeof(m_free)); } -memory_manager::memory_manager(void *start) : +memory_manager::memory_manager(void *start, grow_callback grow_cb) : m_start(start), - m_length(0) + m_length(0), + m_grow(grow_cb) { kutil::memset(m_free, 0, sizeof(m_free)); grow_memory(); @@ -89,13 +90,10 @@ memory_manager::allocate(size_t length) size_t total = length + sizeof(mem_header); unsigned size = min_size; while (total > (1 << size)) size++; - kassert(size < max_size, "Tried to allocate a block bigger than max_size"); - log::debug(logs::memory, "Allocating %d bytes, which is size %d", total, size); + kassert(size <= max_size, "Tried to allocate a block bigger than max_size"); mem_header *header = pop_free(size); header->set_used(true); - - log::debug(logs::memory, " Returning %d bytes at %lx", length, header + 1); return header + 1; } @@ -106,16 +104,12 @@ memory_manager::free(void *p) header -= 1; // p points after the header header->set_used(false); - log::debug(logs::memory, "Freeing a block of size %2d at %lx", header->size(), header); - while (true) { mem_header *buddy = header->buddy(); if (buddy->used() || buddy->size() != header->size()) break; - log::debug(logs::memory, " buddy is same size at %lx", buddy); buddy->remove(); header = header->eldest() ? header : buddy; header->set_size(header->size() + 1); - log::debug(logs::memory, " joined into size %2d at %lx", header->size(), header); } uint8_t size = header->size(); @@ -131,19 +125,14 @@ memory_manager::grow_memory() size_t length = (1 << max_size); void *next = kutil::offset_pointer(m_start, m_length); - - g_page_manager.map_pages( - reinterpret_cast(next), - length / page_manager::page_size); + kassert(m_grow, "Tried to grow heap without a growth callback"); + m_grow(next, length); mem_header *block = new (next) mem_header(nullptr, get_free(max_size), max_size); get_free(max_size) = block; if (block->next()) block->next()->set_prev(block); m_length += length; - - log::debug(logs::memory, "Allocated new block at %lx: size %d next %lx", - block, max_size, block->next()); } void @@ -163,10 +152,6 @@ memory_manager::ensure_block(unsigned size) orig->set_next(next); orig->set_size(size); get_free(size) = orig; - - log::debug(logs::memory, "ensure_block[%2d] split blocks:", size); - log::debug(logs::memory, " %lx: size %d next %lx", orig, size, orig->next()); - log::debug(logs::memory, " %lx: size %d next %lx", next, size, next->next()); } memory_manager::mem_header * @@ -180,8 +165,4 @@ memory_manager::pop_free(unsigned size) return block; } -void * operator new (size_t, void *p) { return p; } -void * operator new (size_t n) { return g_kernel_memory_manager.allocate(n); } -void * operator new[] (size_t n) { return g_kernel_memory_manager.allocate(n); } -void operator delete (void *p) { return g_kernel_memory_manager.free(p); } -void operator delete[] (void *p){ return g_kernel_memory_manager.free(p); } +} // namespace kutil diff --git a/src/kernel/memory.h b/src/modules/kutil/memory_manager.h similarity index 62% rename from src/kernel/memory.h rename to src/modules/kutil/memory_manager.h index eb028c9..632119c 100644 --- a/src/kernel/memory.h +++ b/src/modules/kutil/memory_manager.h @@ -1,17 +1,25 @@ #pragma once -/// \file memory.h -/// The block memory manager and related definitions. +/// \file memory_manager.h +/// A buddy allocator and related definitions. #include -#include + +namespace kutil { /// Manager for allocation of virtual memory. class memory_manager { public: + using grow_callback = void (*)(void *start, size_t length); + + /// Default constructor. Creates an invalid manager. memory_manager(); - memory_manager(void *start); + + /// Constructor. + /// \arg start Pointer to the start of the heap to be managed + /// \arg grow_cb Function pointer to grow the heap size + memory_manager(void *start, grow_callback grow_cb); /// Allocate memory from the area managed. /// \arg length The amount of memory to allocate, in bytes @@ -23,6 +31,7 @@ public: /// \arg p A pointer previously retuned by allocate() void free(void *p); + private: class mem_header; @@ -49,23 +58,9 @@ private: void *m_start; size_t m_length; - friend class page_manager; + grow_callback m_grow; + memory_manager(const memory_manager &) = delete; }; -extern memory_manager g_kernel_memory_manager; - -/// Bootstrap the memory managers. -void memory_initialize_managers(const void *memory_map, size_t map_length, size_t desc_length); - -/// Allocate kernel space memory. -/// \arg length The amount of memory to allocate, in bytes -/// \returns A pointer to the allocated memory, or nullptr if -/// allocation failed. -inline void * kalloc(size_t length) { return g_kernel_memory_manager.allocate(length); } - -/// Free kernel space memory. -/// \arg p The pointer to free -inline void kfree(void *p) { g_kernel_memory_manager.free(p); } - -void * operator new (size_t, void *p); +} // namespace kutil