feature (kernel): Implemented heap architecture
This commit is contained in:
parent
79003bf2db
commit
45118fddc8
|
@ -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
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue