diff --git a/kernel/inc/mm/heap.h b/kernel/inc/mm/heap.h new file mode 100644 index 0000000..2483be5 --- /dev/null +++ b/kernel/inc/mm/heap.h @@ -0,0 +1,44 @@ +/* Copyright (C) Antifallobst + * + * NoxOS is free software: + * you can redistribute it and/or modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * NoxOS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . + */ + +#ifndef NOX_HEAP_H +#define NOX_HEAP_H + +#include "utils/stdtypes.h" + +#define HEAP_SEGMENT_MAGIC 0xA1C3A1B2 + +typedef struct heap_segment_T heap_segment_T; +struct heap_segment_T { + uint32_t magic; + uint64_t size; + bool free; + heap_segment_T* next; + heap_segment_T* prev; +}; + +typedef struct { + void* start; + void* end; + heap_segment_T* last_segment; +} heap_T; + +heap_T heap_create (void* base); +void* heap_memory_allocate (heap_T* heap, uint64_t size); +void heap_memory_free (heap_T* heap, void* address); +void heap_dump_segments (heap_T* heap); +void heap_destruct (heap_T* heap); + +#endif //NOX_HEAP_H diff --git a/kernel/src/kmain.c b/kernel/src/kmain.c index 9c1d326..36bc10d 100644 --- a/kernel/src/kmain.c +++ b/kernel/src/kmain.c @@ -21,7 +21,7 @@ #include "mm/page_frame.h" #include "mm/page_map.h" -#include "utils/memory.h" +#include "mm/heap.h" void limine_terminal_print(boot_info_T* boot_info, string_t string) { boot_info->terminal->write(boot_info->terminal->terminals[0], string, string_length(string)); @@ -41,5 +41,30 @@ void kmain(boot_info_T boot_info) { kernel_init(&boot_info); + heap_T test_heap = heap_create((void*)0x100000000000); + void *debug1, *debug2, *debug3, *debug4 = NULL; + + heap_dump_segments(&test_heap); + + debug1 = heap_memory_allocate(&test_heap, 16); + heap_dump_segments(&test_heap); + + debug2 = heap_memory_allocate(&test_heap, 16); + heap_dump_segments(&test_heap); + + debug3 = heap_memory_allocate(&test_heap, 0x3000); + heap_dump_segments(&test_heap); + + heap_memory_free(&test_heap, debug2); + heap_dump_segments(&test_heap); + + heap_memory_free(&test_heap, debug3); + heap_dump_segments(&test_heap); + + debug4 = heap_memory_allocate(&test_heap, 16); + heap_dump_segments(&test_heap); + + heap_destruct(&test_heap); + CORE_HALT_FOREVER } diff --git a/kernel/src/mm/heap.c b/kernel/src/mm/heap.c new file mode 100644 index 0000000..45440bb --- /dev/null +++ b/kernel/src/mm/heap.c @@ -0,0 +1,225 @@ +/* Copyright (C) Antifallobst + * + * NoxOS is free software: + * you can redistribute it and/or modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * NoxOS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . + */ + +#include "mm/heap.h" +#include "mm/page_map.h" +#include "mm/page_frame.h" +#include "utils/math.h" +#include "utils/logger.h" + +void heap_segment_init(heap_segment_T* segment, heap_segment_T* next, heap_segment_T* prev, uint64_t size) { + segment->magic = HEAP_SEGMENT_MAGIC; + segment->next = next; + segment->prev = prev; + segment->size = size; + segment->free = true; +} + +bool heap_segment_validate(heap_segment_T* segment) { + return segment->magic == HEAP_SEGMENT_MAGIC; +} + +bool heap_segment_combine_prev(heap_segment_T* segment) { + if (segment->prev == NULL) { return false; } + if (!segment->free || !segment->prev->free) { return false; } + + // link around the segment + segment->prev->next = segment->next; + if (segment->next != NULL) { + segment->next->prev = segment->prev; + } + + // fuse with previous segment + segment->prev->size += sizeof(heap_segment_T) + segment->size; + + // invalidate the segment + segment->magic = 0; + return true; +} + +bool heap_segment_combine_next(heap_segment_T* segment) { + if (segment->next == NULL) { return false; } + + return heap_segment_combine_prev(segment->next); +} + +bool heap_segment_shrink(heap_T* heap, heap_segment_T* segment, uint64_t size) { + if (segment->size - size <= sizeof(heap_segment_T) + 0x10) { return false; } + + // break out remainding part into its own segment + heap_segment_T* new_segment = (heap_segment_T*)((uint64_t)segment + sizeof(heap_segment_T) + size); + heap_segment_init(new_segment, segment->next, segment->prev, segment->size - size - sizeof(heap_segment_T)); + + segment->size = size; + + // bind new segment into chain + segment->next = new_segment; + if (new_segment->next != NULL) { + new_segment->next->prev = new_segment; + } + new_segment->prev = segment; + + // try to combine the new segment with the segment that comes after it + heap_segment_combine_next(new_segment); + + if (segment == heap->last_segment) { + heap->last_segment = new_segment; + } + + return true; +} + +heap_T heap_create(void* base) { + heap_T heap; + + uint64_t num_pages = 2; + uint64_t size = num_pages * PFRAME_SIZE; + + for (int i = 0; i < num_pages; i++) { + page_map_map_memory(g_kernel_page_map, &base[i * PFRAME_SIZE], pframe_request(), PM_FLAG_READ_WRITE); + } + + heap_segment_T* base_segment = base; + + heap_segment_init(base_segment, NULL, NULL, size - sizeof(heap_segment_T)); + + heap.start = base; + heap.end = base + size; + heap.last_segment = base_segment; + + return heap; +} + +heap_segment_T* heap_append_segment(heap_T* heap, uint64_t minimum_size) { + uint64_t size = CEIL_TO((minimum_size + sizeof(heap_segment_T)), PFRAME_SIZE); + + uint32_t num_pages = size / PFRAME_SIZE; + for (uint32_t i = 0; i < num_pages; i++) { + page_map_map_memory(g_kernel_page_map, heap->end, pframe_request(), PM_FLAG_READ_WRITE); + heap->end += PFRAME_SIZE; + } + + heap_segment_T* segment = heap->end - size; + heap_segment_init(segment, NULL, heap->last_segment, size - sizeof(heap_segment_T)); + heap->last_segment->next = segment; + + if (heap_segment_combine_prev(segment)) { + segment = heap->last_segment; + } else { + heap->last_segment = segment; + } + + return segment; +} + +void* heap_memory_allocate(heap_T* heap, uint64_t size) { + if (heap->start == NULL) { return NULL; } + + size = CEIL_TO(size, 0x10); + + // search for free segments, that are big enough + heap_segment_T* segment = heap->start; + while (segment != NULL) { + if (!heap_segment_validate(segment)) { + log(LOG_ERROR, "Heap Segment Invalid! while trying to allocate memory"); + return NULL; + } + + if (!segment->free || segment->size < size) { + segment = segment->next; + continue; + } + + if (segment->size > size) { + heap_segment_shrink(heap, segment, size); + } + + segment->free = false; + return (heap_segment_T*)((uint64_t)segment + sizeof(heap_segment_T)); + } + + segment = heap_append_segment(heap, size); + if (segment->size > size) { + heap_segment_shrink(heap, segment, size); + } + segment->free = false; + + return (heap_segment_T*)((uint64_t)segment + sizeof(heap_segment_T)); +} + +void heap_dump_segments(heap_T* heap) { + log(LOG_DEBUG, "<=====[ Dumping Heap Segments ]=====>"); + + log(LOG_NONE, " |---------------------|"); + log(LOG_NONE, " | START |"); + log(LOG_NONE, " | 0x%x |", heap->start); + log(LOG_NONE, " |----------v----------|"); + + heap_segment_T* segment = heap->start; + while (segment != NULL) { + log(LOG_NONE, " |"); + log(LOG_NONE, " |"); + log(LOG_NONE, " |----------^----------|"); + log(LOG_NONE, " | 0x%x |", (uint64_t)segment); + log(LOG_NONE, " | Valid: %? |", heap_segment_validate(segment)); + log(LOG_NONE, " | Free: %? |", segment->free); + log(LOG_NONE, " | Size: 0x%xw |", segment->size); + log(LOG_NONE, " |----------v----------|"); + + segment = segment->next; + } + + log(LOG_NONE, " |"); + log(LOG_NONE, " |"); + log(LOG_NONE, " |----------^----------|"); + log(LOG_NONE, " | END |"); + log(LOG_NONE, " | 0x%x |", heap->end); + log(LOG_NONE, " |---------------------|"); +} + +void heap_memory_free(heap_T* heap, void* address) { + heap_segment_T* segment = (heap_segment_T*)(address - sizeof(heap_segment_T)); + if (!heap_segment_validate(segment)) { + log(LOG_ERROR, "Heap Segment Invalid! while trying to free 0x%x", address); + return; + } + segment->free = true; + + // try to combine in both directions, so we don't splitter the heap in a ton of small segments + // TODO: the following combination is not working yet + log(LOG_DEBUG, " CombinePrev[%?] CombineNext[%?]", + heap_segment_combine_prev(segment), + heap_segment_combine_next(segment)); + +} + +void heap_destruct(heap_T* heap) { + log(LOG_INFO, "Destructing heap 0x%x", heap); + + heap_segment_T* segment = heap->start; + while (segment != NULL) { + segment->magic = 0; + log(LOG_INFO, " > Invalidated segment 0x%x + 0x%xw", segment, segment->size); + segment = segment->next; + } + + for (uint64_t i = (uint64_t)heap->start; i < (uint64_t)heap->end; i += PFRAME_SIZE) { + void* pframe = page_map_get_physical_address(g_kernel_page_map, (void*)i); + pframe_free(pframe); + log(LOG_INFO, " > Freed pframe 0x%xd", pframe); + page_map_unmap_memory(g_kernel_page_map, (void*)i); + log(LOG_INFO, " > Unmapped 0x%x", i); + } +}