104 lines
2.8 KiB
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;
|
|
}
|