aboutsummaryrefslogtreecommitdiff
path: root/arch/x86_64/src/memory
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86_64/src/memory')
-rw-r--r--arch/x86_64/src/memory/allocator/area_frame_allocator.cpp85
-rw-r--r--arch/x86_64/src/memory/allocator/physical_frame.cpp24
-rw-r--r--arch/x86_64/src/memory/allocator/tiny_frame_allocator.cpp34
-rw-r--r--arch/x86_64/src/memory/heap/bump_allocator.cpp54
-rw-r--r--arch/x86_64/src/memory/heap/global_heap_allocator.cpp135
-rw-r--r--arch/x86_64/src/memory/heap/linked_list_allocator.cpp177
-rw-r--r--arch/x86_64/src/memory/heap/memory_block.cpp15
-rw-r--r--arch/x86_64/src/memory/heap/user_heap_allocator.cpp200
-rw-r--r--arch/x86_64/src/memory/kernel_mapper.cpp111
-rw-r--r--arch/x86_64/src/memory/main.cpp77
-rw-r--r--arch/x86_64/src/memory/mmu.cpp21
-rw-r--r--arch/x86_64/src/memory/multiboot/elf_symbols_section.cpp13
-rw-r--r--arch/x86_64/src/memory/multiboot/reader.cpp131
-rw-r--r--arch/x86_64/src/memory/page_table.cpp82
-rw-r--r--arch/x86_64/src/memory/paging/active_page_table.cpp98
-rw-r--r--arch/x86_64/src/memory/paging/inactive_page_table.cpp20
-rw-r--r--arch/x86_64/src/memory/paging/page_entry.cpp63
-rw-r--r--arch/x86_64/src/memory/paging/page_table.cpp128
-rw-r--r--arch/x86_64/src/memory/paging/temporary_page.cpp29
-rw-r--r--arch/x86_64/src/memory/paging/virtual_page.cpp33
-rw-r--r--arch/x86_64/src/memory/paging_root.cpp19
-rw-r--r--arch/x86_64/src/memory/recursive_page_mapper.cpp119
-rw-r--r--arch/x86_64/src/memory/region_allocator.cpp92
-rw-r--r--arch/x86_64/src/memory/scoped_mapping.cpp69
24 files changed, 513 insertions, 1316 deletions
diff --git a/arch/x86_64/src/memory/allocator/area_frame_allocator.cpp b/arch/x86_64/src/memory/allocator/area_frame_allocator.cpp
deleted file mode 100644
index a5a1b49..0000000
--- a/arch/x86_64/src/memory/allocator/area_frame_allocator.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-#include "arch/memory/allocator/area_frame_allocator.hpp"
-
-#include "arch/exception_handling/assert.hpp"
-
-#include <algorithm>
-#include <array>
-#include <ranges>
-
-namespace teachos::arch::memory::allocator
-{
- area_frame_allocator::area_frame_allocator(multiboot::memory_information const & mem_info)
- : next_free_frame()
- , current_area(std::nullopt)
- , memory_areas(mem_info.areas)
- , kernel_start(physical_frame::containing_address(mem_info.kernel_start))
- , kernel_end(physical_frame::containing_address(mem_info.kernel_end))
- , multiboot_start(physical_frame::containing_address(mem_info.multiboot_start))
- , multiboot_end(physical_frame::containing_address(mem_info.multiboot_end))
- {
- choose_next_area();
- }
-
- auto area_frame_allocator::choose_next_area() -> void
- {
- current_area = std::nullopt;
- auto next_area_with_free_frames = memory_areas | std::views::filter([this](auto const & area) {
- auto address = area.base_address + area.area_length - 1;
- return physical_frame::containing_address(address) >= next_free_frame;
- });
-
- auto const lowest_area_with_free_frames = std::ranges::min_element(
- next_area_with_free_frames, [](auto const & a, auto const & b) { return a.base_address < b.base_address; });
-
- if (lowest_area_with_free_frames != next_area_with_free_frames.end())
- {
- current_area = *lowest_area_with_free_frames;
- // Update the `next_free_frame` according to the new memory area
- auto const start_frame = physical_frame::containing_address(current_area.value().base_address);
- if (next_free_frame < start_frame)
- {
- next_free_frame = start_frame;
- }
- }
- }
-
- auto area_frame_allocator::allocate_frame() -> std::optional<physical_frame>
- {
- // Only try to allocate memory if current_area is not null, because
- // the current_area is null if there is no more available memory.
- if (!current_area.has_value())
- {
- return std::nullopt;
- }
-
- auto const address = current_area.value().base_address + current_area.value().area_length - 1;
- physical_frame current_area_last_frame = physical_frame::containing_address(address);
-
- if (next_free_frame > current_area_last_frame)
- {
- // All frames of current area are used, switch to next area.
- choose_next_area();
- }
- else if (next_free_frame >= kernel_start && next_free_frame <= kernel_end)
- {
- // `physical_frame` is used by the kernel or multiboot information structure.
- next_free_frame = allocator::physical_frame{kernel_end.frame_number + 1};
- }
- else if (next_free_frame >= multiboot_start && next_free_frame <= multiboot_end)
- {
- // `physical_frame` is used by the kernel or multiboot information structure.
- next_free_frame = allocator::physical_frame{multiboot_end.frame_number + 1};
- }
- else
- {
- // Frame is unused, increment `next_free_frame` and return it.
- next_free_frame.frame_number += 1;
- return next_free_frame;
- }
-
- // `physical_frame` was not valid, try it again with the updated `next_free_frame`.
- return allocate_frame();
- }
-
- auto area_frame_allocator::deallocate_frame(physical_frame const & physical_frame) -> void { (void)physical_frame; }
-} // namespace teachos::arch::memory::allocator
diff --git a/arch/x86_64/src/memory/allocator/physical_frame.cpp b/arch/x86_64/src/memory/allocator/physical_frame.cpp
deleted file mode 100644
index ec387a1..0000000
--- a/arch/x86_64/src/memory/allocator/physical_frame.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-#include "arch/memory/allocator/physical_frame.hpp"
-
-namespace teachos::arch::memory::allocator
-{
- auto physical_frame::containing_address(physical_address address) -> physical_frame
- {
- return physical_frame{address / PAGE_FRAME_SIZE};
- }
-
- auto physical_frame::start_address() const -> physical_address { return frame_number * PAGE_FRAME_SIZE; }
-
- auto physical_frame::operator++(int) -> physical_frame
- {
- physical_frame const old_value = *this;
- ++frame_number;
- return old_value;
- }
-
- auto physical_frame::operator++() -> physical_frame &
- {
- ++frame_number;
- return *this;
- }
-} // namespace teachos::arch::memory::allocator
diff --git a/arch/x86_64/src/memory/allocator/tiny_frame_allocator.cpp b/arch/x86_64/src/memory/allocator/tiny_frame_allocator.cpp
deleted file mode 100644
index 3cdf9c7..0000000
--- a/arch/x86_64/src/memory/allocator/tiny_frame_allocator.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-#include "arch/memory/allocator/tiny_frame_allocator.hpp"
-
-#include "arch/exception_handling/panic.hpp"
-
-namespace teachos::arch::memory::allocator
-{
- auto tiny_frame_allocator::allocate_frame() -> std::optional<physical_frame>
- {
- for (auto & frame_option : frames)
- {
- if (frame_option.has_value())
- {
- auto value = frame_option;
- frame_option.reset();
- return value;
- }
- }
- return std::nullopt;
- }
-
- auto tiny_frame_allocator::deallocate_frame(physical_frame const & physical_frame) -> void
- {
- for (auto & frame_option : frames)
- {
- if (!frame_option.has_value())
- {
- frame_option.emplace(physical_frame);
- return;
- }
- }
- exception_handling::panic(
- "[Tiny Frame Allocator] Attempted to deallocate more than the 3 frames, that can be held");
- }
-} // namespace teachos::arch::memory::allocator
diff --git a/arch/x86_64/src/memory/heap/bump_allocator.cpp b/arch/x86_64/src/memory/heap/bump_allocator.cpp
deleted file mode 100644
index 525f45c..0000000
--- a/arch/x86_64/src/memory/heap/bump_allocator.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-#include "arch/memory/heap/bump_allocator.hpp"
-
-#include "arch/exception_handling/assert.hpp"
-
-#include <limits>
-#include <type_traits>
-
-namespace teachos::arch::memory::heap
-{
- namespace
- {
- template<typename T>
- auto saturating_add(T x, T y) -> T
- requires std::is_unsigned_v<T>
- {
- if (x > std::numeric_limits<T>::max() - y)
- {
- return std::numeric_limits<T>::max();
- }
- T result = x + y;
- return result;
- }
- } // namespace
-
- auto bump_allocator::allocate(std::size_t size) -> void *
- {
- // Reading the value only has to be done once, because compare_exchange_weak updates the value as well if the
- // exchange failed, becuase the value was not the expected one.
- auto alloc_start = next.load(std::memory_order::relaxed);
- // Repeat allocation until it succeeds, has to be done, because another allocator could overtake it at any time
- // causing the value to differ and the calculation to have to be redone.
- for (;;)
- {
- auto const alloc_end = saturating_add(alloc_start, size);
- arch::exception_handling::assert(alloc_end <= heap_end, "[Heap Allocator] Out of memory");
- // Check if the atomic value is still the one initally loaded, if it isn't we have been overtaken by another
- // thread and need to redo the calculation. Spurious failure by weak can be ignored, because the whole allocation
- // is wrapped in an infinite for loop so a failure that wasn't actually one will simply be retried until it works.
- auto const updated = next.compare_exchange_weak(alloc_start, alloc_end, std::memory_order::relaxed);
- if (updated)
- {
- return reinterpret_cast<void *>(alloc_start);
- }
- }
- }
-
- auto bump_allocator::deallocate(void * pointer) noexcept -> void
- {
- if (pointer)
- {
- }
- }
-
-} // namespace teachos::arch::memory::heap
diff --git a/arch/x86_64/src/memory/heap/global_heap_allocator.cpp b/arch/x86_64/src/memory/heap/global_heap_allocator.cpp
deleted file mode 100644
index 35cd623..0000000
--- a/arch/x86_64/src/memory/heap/global_heap_allocator.cpp
+++ /dev/null
@@ -1,135 +0,0 @@
-#include "arch/memory/heap/global_heap_allocator.hpp"
-
-#include "arch/context_switching/syscall/main.hpp"
-#include "arch/exception_handling/assert.hpp"
-#include "arch/kernel/cpu/segment_register.hpp"
-#include "arch/memory/heap/bump_allocator.hpp"
-#include "arch/memory/heap/linked_list_allocator.hpp"
-#include "arch/memory/heap/user_heap_allocator.hpp"
-
-namespace teachos::arch::memory::heap
-{
- namespace
- {
- constexpr char NOT_REGISTRED_ERROR_MESSAGE[] =
- "Attempted to allocate or deallocate using the global_heap_allocator before "
- "register_heap_allocation_type was called.";
- constexpr uint16_t KERNEL_CODE_INDEX = 1U;
-
- [[gnu::section(".user_text")]]
- auto os_in_kernel_mode() -> bool
- {
- auto const cs = teachos::arch::kernel::cpu::read_code_segment_register();
- return cs.get_index() == KERNEL_CODE_INDEX;
- }
- } // namespace
-
- heap_allocator * global_heap_allocator::kernel_allocator_instance = nullptr;
- user_heap_allocator * global_heap_allocator::user_allocator_instance = nullptr;
-
- auto global_heap_allocator::kmalloc(std::size_t size) -> void * { return kernel().allocate(size); }
-
- auto global_heap_allocator::kfree(void * pointer) noexcept -> void { kernel().deallocate(pointer); }
-
- auto global_heap_allocator::malloc(std::size_t size) -> void * { return user().allocate(size); }
-
- auto global_heap_allocator::free(void * pointer) noexcept -> void { user().deallocate(pointer); }
-
- auto global_heap_allocator::register_heap_allocator(heap_allocator_type new_type) -> void
- {
- exception_handling::assert(kernel_allocator_instance == nullptr,
- "Calling register_heap_allocator_type can only be done once.");
-
- switch (new_type)
- {
- case heap_allocator_type::NONE:
- // Nothing to do
- break;
- case heap_allocator_type::BUMP: {
- static bump_allocator kernel_allocator{KERNEL_HEAP_START, KERNEL_HEAP_START + KERNEL_HEAP_SIZE};
- kernel_allocator_instance = &kernel_allocator;
- break;
- }
- case heap_allocator_type::LINKED_LIST: {
- static linked_list_allocator kernel_allocator{KERNEL_HEAP_START, KERNEL_HEAP_START + KERNEL_HEAP_SIZE};
- kernel_allocator_instance = &kernel_allocator;
- break;
- }
- }
-
- [[gnu::section(".user_data")]]
- static user_heap_allocator user_allocator{};
- user_allocator_instance = &user_allocator;
- }
-
- auto global_heap_allocator::kernel() -> heap_allocator &
- {
- exception_handling::assert(kernel_allocator_instance != nullptr, NOT_REGISTRED_ERROR_MESSAGE);
-
- return *kernel_allocator_instance;
- }
-
- auto global_heap_allocator::user() -> user_heap_allocator &
- {
- context_switching::syscall::syscall(
- context_switching::syscall::type::ASSERT,
- {user_allocator_instance != nullptr, reinterpret_cast<uint64_t>(&NOT_REGISTRED_ERROR_MESSAGE)});
- return *user_allocator_instance;
- }
-} // namespace teachos::arch::memory::heap
-
-auto operator new(std::size_t size) -> void *
-{
- if (teachos::arch::memory::heap::os_in_kernel_mode())
- {
- return teachos::arch::memory::heap::global_heap_allocator::kmalloc(size);
- }
- return teachos::arch::memory::heap::global_heap_allocator::malloc(size);
-}
-
-auto operator delete(void * pointer) noexcept -> void
-{
- if (teachos::arch::memory::heap::os_in_kernel_mode())
- {
- teachos::arch::memory::heap::global_heap_allocator::kfree(pointer);
- }
- teachos::arch::memory::heap::global_heap_allocator::free(pointer);
-}
-
-auto operator delete(void * pointer, std::size_t size) noexcept -> void
-{
- (void)size;
- if (teachos::arch::memory::heap::os_in_kernel_mode())
- {
- teachos::arch::memory::heap::global_heap_allocator::kfree(pointer);
- }
- teachos::arch::memory::heap::global_heap_allocator::free(pointer);
-}
-
-auto operator new[](std::size_t size) -> void *
-{
- if (teachos::arch::memory::heap::os_in_kernel_mode())
- {
- return teachos::arch::memory::heap::global_heap_allocator::kmalloc(size);
- }
- return teachos::arch::memory::heap::global_heap_allocator::malloc(size);
-}
-
-auto operator delete[](void * pointer) noexcept -> void
-{
- if (teachos::arch::memory::heap::os_in_kernel_mode())
- {
- teachos::arch::memory::heap::global_heap_allocator::kfree(pointer);
- }
- teachos::arch::memory::heap::global_heap_allocator::free(pointer);
-}
-
-auto operator delete[](void * pointer, std::size_t size) noexcept -> void
-{
- (void)size;
- if (teachos::arch::memory::heap::os_in_kernel_mode())
- {
- teachos::arch::memory::heap::global_heap_allocator::kfree(pointer);
- }
- teachos::arch::memory::heap::global_heap_allocator::free(pointer);
-}
diff --git a/arch/x86_64/src/memory/heap/linked_list_allocator.cpp b/arch/x86_64/src/memory/heap/linked_list_allocator.cpp
deleted file mode 100644
index 63a6111..0000000
--- a/arch/x86_64/src/memory/heap/linked_list_allocator.cpp
+++ /dev/null
@@ -1,177 +0,0 @@
-#include "arch/memory/heap/linked_list_allocator.hpp"
-
-#include "arch/exception_handling/assert.hpp"
-#include "arch/exception_handling/panic.hpp"
-
-#include <algorithm>
-
-namespace teachos::arch::memory::heap
-{
- linked_list_allocator::linked_list_allocator(std::size_t heap_start, std::size_t heap_end)
- : first(nullptr)
- , mutex{stl::mutex{}}
- {
- auto const heap_size = heap_end - heap_start;
- exception_handling::assert(
- heap_size > min_allocatable_size(),
- "[Linked List Allocator] Total heap size can not be smaller than minimum of 16 bytes to hold "
- "atleast one memory hole entry");
- first = new (reinterpret_cast<void *>(heap_start)) memory_block(heap_size, nullptr);
- }
-
- auto linked_list_allocator::allocate(std::size_t size) -> void *
- {
- // Add size of size_t to the total allocated size, because we add a header that includes the size of the allocated
- // block, to allow for deallocation without the need to call with the corresponding size
- auto const total_size = size + sizeof(std::size_t);
- mutex.lock();
-
- memory_block * previous = nullptr;
- auto current = first;
-
- while (current != nullptr)
- {
- if (current->size == total_size)
- {
- auto const memory_address = remove_free_memory_block(previous, current);
- new (memory_address) std::size_t(total_size);
- mutex.unlock();
- return reinterpret_cast<void *>(reinterpret_cast<std::size_t>(memory_address) + sizeof(std::size_t));
- }
- else if (current->size >= total_size + min_allocatable_size())
- {
- // Ensure that the allocated size block is atleast 16 bytes (required because if we free the hole afterwards
- // there needs to be enough space for a memory block). Therefore we allocate more than is actually required if
- // the total size was less and simply deallocate it as well
- auto const max_size = std::max(total_size, min_allocatable_size());
- auto const memory_address = split_free_memory_block(previous, current, max_size);
- new (memory_address) std::size_t(max_size);
- mutex.unlock();
- return reinterpret_cast<void *>(reinterpret_cast<std::size_t>(memory_address) + sizeof(std::size_t));
- }
-
- previous = current;
- current = current->next;
- }
-
- exception_handling::panic("[Linked List Allocator] Out of memory");
- }
-
- auto linked_list_allocator::deallocate(void * pointer) noexcept -> void
- {
- mutex.lock();
-
- // Read configured header size of the complete allocated block
- auto const header_pointer = reinterpret_cast<void *>(reinterpret_cast<std::size_t>(pointer) - sizeof(std::size_t));
- auto const total_size = *reinterpret_cast<std::size_t *>(header_pointer);
-
- auto const start_address = reinterpret_cast<std::size_t>(header_pointer);
- auto const end_address = start_address + total_size;
-
- memory_block * previous = nullptr;
- auto current = first;
-
- while (current != nullptr)
- {
- // Current address of the free memory block now points to an address that is after our block to deallocate in heap
- // memory space.
- if (reinterpret_cast<std::size_t>(current) >= end_address)
- {
- break;
- }
-
- previous = current;
- current = current->next;
- }
-
- coalesce_free_memory_block(previous, current, header_pointer, total_size);
- mutex.unlock();
- }
-
- auto linked_list_allocator::remove_free_memory_block(memory_block * previous_block, memory_block * current_block)
- -> void *
- {
- return replace_free_memory_block(previous_block, current_block, current_block->next);
- }
-
- auto linked_list_allocator::split_free_memory_block(memory_block * previous_block, memory_block * current_block,
- std::size_t size) -> void *
- {
- auto const end_address = reinterpret_cast<std::size_t>(current_block) + size;
- auto const new_block =
- new (reinterpret_cast<void *>(end_address)) memory_block(current_block->size - size, current_block->next);
- return replace_free_memory_block(previous_block, current_block, new_block);
- }
-
- auto linked_list_allocator::replace_free_memory_block(memory_block * previous_block, memory_block * current_block,
- memory_block * new_block) -> void *
- {
- auto const start_address = reinterpret_cast<std::size_t>(current_block);
- // If we want to allocate into the first block that is before any other free block, then there exists no previous
- // free block (nullptr). Therefore we have to overwrite the first block instead of overwriting its next value.
- if (previous_block == nullptr)
- {
- first = new_block;
- }
- else
- {
- previous_block->next = new_block;
- }
- current_block->~memory_block();
- return reinterpret_cast<void *>(start_address);
- }
-
- auto linked_list_allocator::coalesce_free_memory_block(memory_block * previous_block, memory_block * current_block,
- void * pointer, std::size_t size) -> void
- {
- auto const start_address = reinterpret_cast<std::size_t>(pointer);
- auto const end_address = start_address + size;
-
- // Inital values if there are no adjacent blocks either before or after, meaning we have to simply create a free
- // memory block that is placed in between the previous and next block.
- auto block_size = size;
- auto next_block = current_block;
-
- // If the block we want to deallocate is before another free block and we can therefore combine both into one.
- // This is done by deleting the current free block and creating a new block at the start address of the block to
- // deallocate with both the size of the block to deallcoate and the free block next to it.
- if (end_address == reinterpret_cast<std::size_t>(current_block))
- {
- block_size += current_block->size;
- next_block = current_block->next;
- current_block->~memory_block();
- }
-
- // If the block we want to deallocate is behind another free block and we can therefore combine both into one.
- // This is done by simply changin the size of the previous block to include the size of the block to deallocate.
- // This is done, because the previous block might still be referencered by the next field of other memory blocks.
- if (previous_block != nullptr &&
- start_address == (reinterpret_cast<std::size_t>(previous_block) + previous_block->size))
- {
- block_size += previous_block->size;
-
- previous_block->size = block_size;
- previous_block->next = next_block;
- return;
- }
-
- // Check if the block we want to deallocate is contained in the previous block, because if it is it can only mean
- // that the block has already been deallocated and we therefore attempted a double free.
- exception_handling::assert(previous_block == nullptr ||
- start_address >=
- (reinterpret_cast<std::size_t>(previous_block) + previous_block->size),
- "[Linked List Allocator] Attempted double free detected");
-
- auto const new_block = new (pointer) memory_block(block_size, next_block);
- // If we want to deallocate the first block that is before any other free block, then there exists no previous free
- // block (nullptr). Therefore we have to overwrite the first block instead of overwriting its
- // next value.
- if (previous_block == nullptr)
- {
- first = new_block;
- return;
- }
- previous_block->next = new_block;
- }
-
-} // namespace teachos::arch::memory::heap
diff --git a/arch/x86_64/src/memory/heap/memory_block.cpp b/arch/x86_64/src/memory/heap/memory_block.cpp
deleted file mode 100644
index bc97bd6..0000000
--- a/arch/x86_64/src/memory/heap/memory_block.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-#include "arch/memory/heap/memory_block.hpp"
-
-#include <string.h>
-
-namespace teachos::arch::memory::heap
-{
- memory_block::memory_block(std::size_t size, memory_block * next)
- {
- memset(static_cast<void *>(this), 0U, size);
- this->size = size;
- this->next = next;
- }
-
- memory_block::~memory_block() { memset(static_cast<void *>(this), 0U, sizeof(memory_block)); }
-} // namespace teachos::arch::memory::heap
diff --git a/arch/x86_64/src/memory/heap/user_heap_allocator.cpp b/arch/x86_64/src/memory/heap/user_heap_allocator.cpp
deleted file mode 100644
index 427a68a..0000000
--- a/arch/x86_64/src/memory/heap/user_heap_allocator.cpp
+++ /dev/null
@@ -1,200 +0,0 @@
-#include "arch/memory/heap/user_heap_allocator.hpp"
-
-#include "arch/context_switching/syscall/main.hpp"
-
-#include <algorithm>
-
-namespace teachos::arch::memory::heap
-{
- auto user_heap_allocator::allocate(std::size_t size) -> void *
- {
- // Add size of size_t to the total allocated size, because we add a header that includes the size of the allocated
- // block, to allow for deallocation without the need to call with the corresponding size
- auto const total_size = size + sizeof(std::size_t);
- mutex.lock();
-
- memory_block * previous = nullptr;
- auto current = first;
-
- while (current != nullptr)
- {
- auto memory = allocate_into_memory_block_if_big_enough(current, previous, total_size);
- if (memory.has_value())
- {
- return memory.value();
- }
-
- previous = current;
- current = current->next;
- }
-
- current = expand_heap_if_full();
-
- if (current != nullptr)
- {
- auto memory = allocate_into_memory_block_if_big_enough(current, previous, total_size);
- if (memory.has_value())
- {
- return memory.value();
- }
- }
-
- char constexpr OUT_OF_MEMORY_ERROR_MESSAGE[] = "[Linked List Allocator] Out of memory";
- context_switching::syscall::syscall(context_switching::syscall::type::ASSERT,
- {false, reinterpret_cast<uint64_t>(&OUT_OF_MEMORY_ERROR_MESSAGE)});
- return nullptr;
- }
-
- auto user_heap_allocator::deallocate(void * pointer) noexcept -> void
- {
- mutex.lock();
-
- // Read configured header size of the complete allocated block
- auto const header_pointer = reinterpret_cast<void *>(reinterpret_cast<std::size_t>(pointer) - sizeof(std::size_t));
- auto const total_size = *reinterpret_cast<std::size_t *>(header_pointer);
-
- auto const start_address = reinterpret_cast<std::size_t>(header_pointer);
- auto const end_address = start_address + total_size;
-
- memory_block * previous = nullptr;
- auto current = first;
-
- while (current != nullptr)
- {
- // Current address of the free memory block now points to an address that is after our block to deallocate in heap
- // memory space.
- if (reinterpret_cast<std::size_t>(current) >= end_address)
- {
- break;
- }
-
- previous = current;
- current = current->next;
- }
-
- coalesce_free_memory_block(previous, current, header_pointer, total_size);
- mutex.unlock();
- }
-
- auto user_heap_allocator::allocate_into_memory_block_if_big_enough(memory_block * current, memory_block * previous,
- std::size_t total_size) -> std::optional<void *>
- {
- if (current->size == total_size)
- {
- auto const memory_address = remove_free_memory_block(previous, current);
- new (memory_address) std::size_t(total_size);
- mutex.unlock();
- return reinterpret_cast<void *>(reinterpret_cast<std::size_t>(memory_address) + sizeof(std::size_t));
- }
- else if (current->size >= total_size + min_allocatable_size())
- {
- // Ensure that the allocated size block is atleast 16 bytes (required because if we free the hole afterwards
- // there needs to be enough space for a memory block). Therefore we allocate more than is actually required if
- // the total size was less and simply deallocate it as well
- auto const max_size = std::max(total_size, min_allocatable_size());
- auto const memory_address = split_free_memory_block(previous, current, max_size);
- new (memory_address) std::size_t(max_size);
- mutex.unlock();
- return reinterpret_cast<void *>(reinterpret_cast<std::size_t>(memory_address) + sizeof(std::size_t));
- }
- return std::nullopt;
- }
-
- auto user_heap_allocator::expand_heap_if_full() -> memory_block *
- {
- auto const result = context_switching::syscall::syscall(context_switching::syscall::type::EXPAND_HEAP);
-
- uint64_t const heap_start = result.values.arg_0;
- uint64_t const heap_size = result.values.arg_1;
- return !result.error_code ? new (reinterpret_cast<void *>(heap_start)) memory_block(heap_size, nullptr) : nullptr;
- }
-
- auto user_heap_allocator::remove_free_memory_block(memory_block * previous_block, memory_block * current_block)
- -> void *
- {
- return replace_free_memory_block(previous_block, current_block, current_block->next);
- }
-
- auto user_heap_allocator::split_free_memory_block(memory_block * previous_block, memory_block * current_block,
- std::size_t size) -> void *
- {
- auto const end_address = reinterpret_cast<std::size_t>(current_block) + size;
- auto const new_block =
- new (reinterpret_cast<void *>(end_address)) memory_block(current_block->size - size, current_block->next);
- return replace_free_memory_block(previous_block, current_block, new_block);
- }
-
- auto user_heap_allocator::replace_free_memory_block(memory_block * previous_block, memory_block * current_block,
- memory_block * new_block) -> void *
- {
- auto const start_address = reinterpret_cast<std::size_t>(current_block);
- // If we want to allocate into the first block that is before any other free block, then there exists no previous
- // free block (nullptr). Therefore we have to overwrite the first block instead of overwriting its next value.
- if (previous_block == nullptr)
- {
- first = new_block;
- }
- else
- {
- previous_block->next = new_block;
- }
- current_block->~memory_block();
- return reinterpret_cast<void *>(start_address);
- }
-
- auto user_heap_allocator::coalesce_free_memory_block(memory_block * previous_block, memory_block * current_block,
- void * pointer, std::size_t size) -> void
- {
- auto const start_address = reinterpret_cast<std::size_t>(pointer);
- auto const end_address = start_address + size;
-
- // Inital values if there are no adjacent blocks either before or after, meaning we have to simply create a free
- // memory block that is placed in between the previous and next block.
- auto block_size = size;
- auto next_block = current_block;
-
- // If the block we want to deallocate is before another free block and we can therefore combine both into one.
- // This is done by deleting the current free block and creating a new block at the start address of the block to
- // deallocate with both the size of the block to deallcoate and the free block next to it.
- if (end_address == reinterpret_cast<std::size_t>(current_block))
- {
- block_size += current_block->size;
- next_block = current_block->next;
- current_block->~memory_block();
- }
-
- // If the block we want to deallocate is behind another free block and we can therefore combine both into one.
- // This is done by simply changin the size of the previous block to include the size of the block to deallocate.
- // This is done, because the previous block might still be referencered by the next field of other memory blocks.
- if (previous_block != nullptr &&
- start_address == (reinterpret_cast<std::size_t>(previous_block) + previous_block->size))
- {
- block_size += previous_block->size;
-
- previous_block->size = block_size;
- previous_block->next = next_block;
- return;
- }
-
- // Check if the block we want to deallocate is contained in the previous block, because if it is it can only mean
- // that the block has already been deallocated and we therefore attempted a double free.
- char constexpr DOUBLE_FREE_ERROR_MESSAGE[] = "[Linked List Allocator] Attempted double free detected";
- context_switching::syscall::syscall(
- context_switching::syscall::type::ASSERT,
- {previous_block == nullptr ||
- start_address >= (reinterpret_cast<std::size_t>(previous_block) + previous_block->size),
- reinterpret_cast<uint64_t>(&DOUBLE_FREE_ERROR_MESSAGE)});
-
- auto const new_block = new (pointer) memory_block(block_size, next_block);
- // If we want to deallocate the first block that is before any other free block, then there exists no previous free
- // block (nullptr). Therefore we have to overwrite the first block instead of overwriting its
- // next value.
- if (previous_block == nullptr)
- {
- first = new_block;
- return;
- }
- previous_block->next = new_block;
- }
-