This repository has been archived on 2025-02-26. You can view files and clone it, but cannot push or open issues or pull requests.
hilbert-os/kernel/allocator.cpp
2024-01-20 17:59:40 -05:00

160 lines
3.7 KiB
C++

#include <hilbert/kernel/paging.hpp>
#include <stddef.h>
namespace hilbert::kernel::allocator {
struct free_entry {
uint64_t start;
uint64_t len;//0 for unused
};
struct free_page {
free_page *next;
free_entry entries[255];
};
free_page *first_page;
static_assert(sizeof(free_page) == 4088);
free_entry *get_entry(uint64_t start, uint64_t len) {
for (free_page *fp = first_page; fp; fp = fp->next)
for (int i = 0; i < 255; ++i)
if (fp->entries[i].start == start && fp->entries[i].len == len)
return fp->entries + i;
return 0;
}
void add_entry(uint64_t start, uint64_t len) {
for (free_page *fp = first_page; fp; fp = fp->next)
for (int i = 0; i < 255; ++i)
if (fp->entries[i].len == 0) {
fp->entries[i].start = start;
fp->entries[i].len = len;
return;
}
free_page *new_page = (free_page *)paging::map_new_kernel_pages(1);
new_page->next = first_page;
first_page = new_page;
for (int i = 2; i < 255; ++i)
new_page->entries[i].len = 0;
new_page->entries[0].start = (uint64_t)new_page + 4088;
new_page->entries[0].len = 8;
new_page->entries[1].start = start;
new_page->entries[1].len = len;
}
//len is power of 2, start is len-aligned
void free_block(uint64_t start, uint64_t len) {
free_entry *buddy = get_entry(start ^ len, len);
if (buddy) {
buddy->start = start & ~len;
buddy->len = len * 2;
}
else
add_entry(start, len);
}
void free_region(uint64_t start, uint64_t len) {
uint64_t block_size = 1;
while (block_size <= len) {
if (start & block_size) {
free_block(start, block_size);
start += block_size;
len -= block_size;
}
block_size *= 2;
}
while (len) {
block_size /= 2;
if (block_size <= len) {
free_block(start, block_size);
start += block_size;
len -= block_size;
}
}
//testing
if (len != 0)
while (1)
;
}
uint64_t take_region(uint64_t len) {
uint64_t min_size = 1;
while (min_size < len)
min_size *= 2;
free_entry *entry = 0;
for (free_page *fp = first_page; fp; fp = fp->next)
for (int i = 0; i < 255; ++i)
if (fp->entries[i].len >= min_size) {
if (fp->entries[i].len == min_size) {
entry = fp->entries + i;
goto loop_done;
}
if (entry == 0 || fp->entries[i].len < entry->len)
entry = fp->entries + i;
}
loop_done:
if (entry != 0) {
uint64_t start = entry->start;
uint64_t block_len = entry->len;
entry->len = 0;
if (block_len != len)
free_region(start + len, block_len - len);
return start;
}
uint64_t pages = (len - 1) / 4096 + 1;
uint64_t start = (uint64_t)paging::map_new_kernel_pages(pages);
if (pages * 4096 != len)
free_region(start + len, pages * 4096 - len);
return start;
}
}
using namespace hilbert::kernel::allocator;
void *_new(size_t len) {
if (len == 0)
return 0;
uint64_t vaddr = take_region(len + sizeof(size_t));
*(size_t *)vaddr = len;
return (void *)(vaddr + sizeof(size_t));
}
void _delete(void *ptr) {
if ((uint64_t)ptr == 0)
return;
uint64_t vaddr = (uint64_t)ptr - sizeof(size_t);
free_region(vaddr, *(size_t *)vaddr + sizeof(size_t));
}
void *operator new(size_t len) {
return _new(len);
}
void operator delete(void *ptr, size_t) {
return _delete(ptr);
}
void operator delete(void *ptr) {
return _delete(ptr);
}
void *operator new[](size_t len) {
return _new(len);
}
void operator delete[](void *ptr, size_t) {
return _delete(ptr);
}
void operator delete[](void *ptr) {
return _delete(ptr);
}