2021-09-21 10:50:33 -05:00

214 lines
6.1 KiB
C

#include <paging.h>
#include <printf.h>
#include <libc.h>
#include <io.h>
/** a disclaimer-
* I've never made an OS, so I don't know what kind of structures I'll have.
* Because of this, I'm not YET using a slab allocator.
* This allocator allocates sizes of 2^n, starting at 32, which can waste lots of space.
* In other places, you'll see me reallocating when it's not nessesary,
* as if my allocator doesn't over-allocate.
*
* Later in development, I will create a slab allocator, making it so that this isn't an issue.
**/
#define MIN_CHUNK_ORDER 5 //equal to 32 bytes for heap_chunk struct. Keeping away from 24 for easy division.
#define MAX_CHUNK_I 6
#define CHUNK_LIST_LEN MAX_CHUNK_I + 1
#define LOG_SUB 63 - MIN_CHUNK_ORDER
#define PAGE_SIZE 0x1000 //TODO [minor] maybe move this to some header somewhere
#define CHUNK_SIZE_FROM_INDEX(i) ((1 << ((i) + MIN_CHUNK_ORDER)))
#define CHUNK_INDEX_FROM_SIZE(s) ((63 - MIN_CHUNK_ORDER - __builtin_clzl((s))))
typedef struct __attribute__((packed)) heap_chunk {
unsigned int size:4; //will use with flags later if needed
unsigned int lsize:4;
unsigned int free:1;
unsigned int mutex:1;
unsigned long reserved:54;
struct heap_chunk *fd;
struct heap_chunk *bk;
} chunk;
struct heap_chunk_roots {
void *fd;
};
//TODO this wastes some memory
chunk arena[CHUNK_LIST_LEN];
void debug_heap() {
chunk *on_chunk, *last_chunk;
size_t chunk_expected_size;
size_t chunk_actual_size;
for(unsigned int l = 0; l < CHUNK_LIST_LEN; l++) {
chunk_expected_size = CHUNK_SIZE_FROM_INDEX(l);
printf("\n%i sized chunks:\n", chunk_expected_size);
for(on_chunk = arena[l].fd; on_chunk != 0; on_chunk = on_chunk->fd) {
chunk_actual_size = CHUNK_SIZE_FROM_INDEX(on_chunk->size);
printf("\t%p", on_chunk);
if(chunk_expected_size != chunk_actual_size) {
printf("\nFound chunk of size %i in %i sized freebin!!!!\n", chunk_actual_size, chunk_expected_size);
hlt();
}
if(!on_chunk->free) {
printf("\nChunk found in freebin isn't free!\n");
hlt();
}
//may take a while
if((uintptr_t)on_chunk & (0x1000 - 1)) {
last_chunk = (void *)on_chunk - CHUNK_SIZE_FROM_INDEX(on_chunk->lsize);
if(last_chunk->size != on_chunk->lsize) {
printf("\nChunk before this one is a different size then lsize!");
hlt();
}
}
}
}
printf("\n\n");
}
void malloc_init() {
bzero(&arena, sizeof(arena));
}
void free(void *addr) {
chunk *on_chunk, *buddy_chunk, *next_chunk, *chunk_iter;
on_chunk = addr - 24;
for(;;) {
if(on_chunk->size == (MAX_CHUNK_I + 1)) {
pfree(on_chunk, 0x1000);
printf("freeing page %p\n", on_chunk);
return;
}
unsigned int chunk_size = CHUNK_SIZE_FROM_INDEX(on_chunk->size);
if((uintptr_t)on_chunk & ((chunk_size * 2) - 1)) {
buddy_chunk = (void *)on_chunk - CHUNK_SIZE_FROM_INDEX(on_chunk->lsize);
if((buddy_chunk->size != on_chunk->size) || !(buddy_chunk->free)) {
next_chunk = (void *)on_chunk + chunk_size;
break;
}
chunk_iter = buddy_chunk;
}
else {
buddy_chunk = (void *)on_chunk + chunk_size;
if((buddy_chunk->size != on_chunk->size) || !(buddy_chunk->free)) {
next_chunk = buddy_chunk;
break;
};
chunk_iter = on_chunk;
}
if(buddy_chunk->fd) (buddy_chunk->fd)->bk = buddy_chunk->bk;
(buddy_chunk->bk)->fd = buddy_chunk->fd;
on_chunk = chunk_iter;
on_chunk->size++;
}
next_chunk->lsize = on_chunk->size;
on_chunk->free = 1;
on_chunk->bk = (chunk *)&arena[on_chunk->size];
on_chunk->fd = arena[on_chunk->size].fd;
if(arena[on_chunk->size].fd) (arena[on_chunk->size].fd)->bk = on_chunk;
arena[on_chunk->size].fd = on_chunk;
}
static void split_chunk(chunk *full_chunk, unsigned int full_size, unsigned int req_size) {
chunk *on_chunk;
full_chunk->size = req_size;
full_chunk->free = 0;
//chunk isn't newly allocated page
//our free funciton won't check lsize if addr is divisible by 0x1000
if(full_chunk->bk) {
if(full_chunk->fd) (full_chunk->fd)->bk = (chunk *)&arena[full_size];
arena[full_size].fd = full_chunk->fd;
on_chunk = (void *)full_chunk + CHUNK_SIZE_FROM_INDEX(full_size);
if((uintptr_t)on_chunk & (0x1000 - 1)) on_chunk->lsize = full_size - 1;
}
on_chunk = (void *)full_chunk + CHUNK_SIZE_FROM_INDEX(req_size);
for(unsigned int sz = req_size; sz < full_size; sz++) {
on_chunk->lsize = (sz == req_size) ? req_size : sz - 1;
on_chunk->fd = arena[sz].fd;
on_chunk->bk = (chunk *)&arena[sz];
on_chunk->size = sz;
on_chunk->free = 1;
arena[sz].fd = on_chunk;
on_chunk = (void *)on_chunk + CHUNK_SIZE_FROM_INDEX(sz);
}
}
void *malloc(size_t size) {
size += sizeof(chunk);
chunk *on_chunk;
unsigned int chunk_sz_i; //desired chunk size index
unsigned int chunk_ck_i; //backup chunk size index in case we need to count up
//rounding size to nearest 2^n to find which size it fits into
if(size < 32) size = 32;
chunk_sz_i = CHUNK_INDEX_FROM_SIZE(size);
if(size & (size - 1)) chunk_sz_i++;
if(chunk_sz_i > MAX_CHUNK_I) return(void *)0;
//search for direct hits
if(arena[chunk_sz_i].fd) {
on_chunk = arena[chunk_sz_i].fd;
if(on_chunk->fd) (on_chunk->fd)->bk = (chunk *)&arena[chunk_sz_i];
arena[chunk_sz_i].fd = on_chunk->fd;
on_chunk->free = 0;
return (void *)on_chunk + sizeof(chunk);
}
//search for chunks above our size
for(chunk_ck_i = chunk_sz_i + 1; chunk_ck_i < CHUNK_LIST_LEN; chunk_ck_i++) {
if(arena[chunk_ck_i].fd) {
on_chunk = arena[chunk_ck_i].fd;
split_chunk(on_chunk, chunk_ck_i, chunk_sz_i);
return (void *)on_chunk + sizeof(chunk);
}
}
on_chunk = (void *)palloc(0x1000);
printf("allocating page %p\n", on_chunk);
split_chunk(on_chunk, 7, chunk_sz_i);
return (void *)on_chunk + sizeof(chunk);
}
void *realloc(void *old_chunk, size_t size) {
void *new_chunk = malloc(size);
memcpy(new_chunk, old_chunk, CHUNK_SIZE_FROM_INDEX(((chunk *)(old_chunk-24))->size));
free(old_chunk);
return new_chunk;
}