feature (kernel): Implemented heap architecture

This commit is contained in:
antifallobst 2023-02-12 13:28:57 +01:00
parent 79003bf2db
commit 45118fddc8
3 changed files with 295 additions and 1 deletions

44
kernel/inc/mm/heap.h Normal file
View File

@ -0,0 +1,44 @@
/* Copyright (C) Antifallobst <antifallobst@systemausfall.org>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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

View File

@ -21,7 +21,7 @@
#include "mm/page_frame.h" #include "mm/page_frame.h"
#include "mm/page_map.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) { 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)); 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); 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 CORE_HALT_FOREVER
} }

225
kernel/src/mm/heap.c Normal file
View File

@ -0,0 +1,225 @@
/* Copyright (C) Antifallobst <antifallobst@systemausfall.org>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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, "<Free> 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);
}
}