calcite/src/kernel/heap.c

104 lines
2.8 KiB
C

/* Calcite, src/kernel/heap.c
* Copyright 2025 Benji Dial
*
* This program 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.
*
* This program 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 "paging.h"
#include "heap.h"
struct dealloc_record {
//0 for unused
uint64_t length;
void *start;
};
struct dealloc_record_page {
struct dealloc_record records[255];
struct dealloc_record_page *prev_page;
};
static struct dealloc_record_page *last_page;
static void *map_pages(uint64_t page_count) {
void *vma = find_free_kernel_region(page_count * 4096);
for (uint64_t i = 0; i < page_count; ++i) {
uint64_t pma = take_free_physical_page();
map_in_kernel_page_table(pma, vma + i * 4096, 1, 0);
}
return vma;
}
static struct dealloc_record *get_unused_record() {
for (struct dealloc_record_page *page = last_page;
page != 0; page = page->prev_page)
for (int i = 0; i < 255; ++i)
if (page->records[i].length == 0)
return &page->records[i];
struct dealloc_record_page *page = map_pages(1);
page->prev_page = last_page;
last_page = page;
page->records[0].start = (void *)page + 4096 - 8;
page->records[0].length = 8;
for (int i = 2; i < 255; ++i)
page->records[i].length = 0;
return &page->records[1];
}
void *heap_alloc(uint64_t length) {
for (struct dealloc_record_page *page = last_page;
page != 0; page = page->prev_page)
for (int i = 0; i < 255; ++i)
if (page->records[i].length == length) {
page->records[i].length = 0;
return page->records[i].start;
}
for (struct dealloc_record_page *page = last_page;
page != 0; page = page->prev_page)
for (int i = 0; i < 255; ++i)
if (page->records[i].length > length) {
page->records[i].length -= length;
return page->records[i].start + page->records[i].length;
}
uint64_t pages = (length - 1) / 4096 + 1;
void *vma = map_pages(pages);
if (pages * 4096 != length) {
struct dealloc_record *record = get_unused_record();
record->start = vma + length;
record->length = pages * 4096 - length;
}
return vma;
}
void heap_dealloc(void *start, uint64_t length) {
struct dealloc_record *record = get_unused_record();
record->start = start;
record->length = length;
}