feat: kmalloc + slab allocator

This commit is contained in:
Jordan ⌨️ 2025-05-04 13:08:03 +02:00
parent 167c99c215
commit 01773cb062
41 changed files with 454 additions and 32 deletions

View file

@ -34,9 +34,7 @@ SECTIONS {
. += CONSTANT(MAXPAGESIZE);
stack_start_addr = .;
. = ALIGN(4);
. += 128 * 1024; /* 128KB */
__stack_top = .;
stack_end_addr = .;
}

View file

@ -1,9 +1,20 @@
#include <allocators/slab.h>
#include <fmt>
#include <hal>
#include <kmalloc>
#include <logger>
#include <string.h>
void kmain(void) {
info$("Hello, World!");
kmalloc_init();
Allocator kmalloc = kmalloc_allocator();
char* hello = (char*)kmalloc.alloc(&kmalloc, 14);
memset(hello, 0, 14);
strncpy(hello, "Hello, World!", 13);
info$("%s", hello);
kmalloc.free(&kmalloc, hello, 14);
for (;;)
;
}

View file

@ -4,6 +4,6 @@
"id": "hal",
"requires": [
"hal-impl",
"heap"
"allocators"
]
}

View file

@ -1,6 +1,7 @@
#pragma once
#include <dtb>
#include <helpers/errors.h>
#include <io>
void hal_setup(void);
@ -16,15 +17,17 @@ typedef enum {
HAL_MAP_HUGE = 1 << 4,
} HalMapFlags;
typedef enum {
HAL_MAP_OK,
HAL_MAP_PHYS_UNALIGNED,
HAL_MAP_VIRT_UNALIGNED,
HAL_MAP_COULDNT_ALLOCATE,
HAL_MAP_ALREADY_MAPPED,
} HalMappingError;
#define MAPPING_ERRORS(ERR) \
ERR(HAL_MAP_OK, "Operation completed successfully") \
ERR(HAL_MAP_PHYS_UNALIGNED, "Physical address is unaligned") \
ERR(HAL_MAP_VIRT_UNALIGNED, "Virtual address is unaligned") \
ERR(HAL_MAP_COULDNT_ALLOCATE, "Couldn't allocate memory") \
ERR(HAL_MAP_ALREADY_MAPPED, "Page is already mapped")
typedef struct _page HalPage;
DECLARE_ERROR_ENUM(HalMapping, MAPPING_ERRORS)
DECLARE_ERROR_STRING(HalMapping, MAPPING_ERRORS)
typedef struct _hal_page HalPage;
HalMappingError hal_map_page(HalPage page, uintptr_t virt, uintptr_t phys, size_t length, uint8_t flags);
@ -32,6 +35,16 @@ void hal_switch_space(HalPage space);
size_t page_size(void);
void* hal_mmap_l2h(uintptr_t addr);
void* hal_mmap_h2l(uintptr_t addr);
/* === TASKS & SCHEDULING === */
typedef struct _hal_context HalContext;
HalContext* hal_create_context(void);
/* === I/O === */
void hal_putc(int ch);

View file

@ -1,7 +1,8 @@
#include <allocators/bump.h>
#include <allocators/slab.h>
#include <dtb>
#include <hal>
#include <handover-dtb>
#include <heap/bump.h>
#include <helpers/mem.h>
#include <logger>
#include <pmm>

View file

@ -1,7 +1,9 @@
.section .text.boot
.global rv32_start
.type rv32_start, @function
.global _start
_start:
mv ra, zero

View file

@ -0,0 +1,8 @@
#include <hal>
#include <string.h>
#include "context.h"
HalContext* hal_context_create(void) {
return NULL;
}

View file

@ -0,0 +1,9 @@
#pragma once
#include <hal>
#include "regs.h"
struct _hal_context {
Stackframe frame;
};

View file

@ -42,7 +42,7 @@ static void panic_handler(Stackframe* frame, uint32_t scause, uint32_t stval, ui
}
}
void exception_handler(Stackframe* frame) {
Stackframe* exception_handler(Stackframe* frame) {
uint32_t scause = read_csr$(scause);
uint32_t stval = read_csr$(stval);
uint32_t user_pc = read_csr$(sepc);
@ -52,4 +52,6 @@ void exception_handler(Stackframe* frame) {
} else {
panic_handler(frame, scause, stval, user_pc);
}
return frame;
}

View file

@ -4,4 +4,4 @@
extern void interrupt_kernel(void);
void exception_handler(Stackframe* frame);
Stackframe* exception_handler(Stackframe* frame);

View file

@ -40,6 +40,7 @@ interrupt_kernel:
mv a0, sp
call exception_handler
mv sp, a0
lw ra, 4 * 0(sp)
lw gp, 4 * 1(sp)

View file

@ -13,3 +13,11 @@ void hal_brkpoint(void) {
size_t page_size(void) {
return 4096;
}
void* hal_mmap_l2h(uintptr_t addr) {
return (void*)addr;
}
void* hal_mmap_h2l(uintptr_t addr) {
return (void*)addr;
}

View file

@ -7,5 +7,5 @@
"sys": ["kernel"]
},
"provides": ["hal-impl"],
"requires": ["dtb", "pmm", "handover-dtb"]
"requires": ["dtb", "pmm", "handover-dtb", "kmalloc"]
}

View file

@ -4,6 +4,7 @@
#include <pmm>
#include <string.h>
#include "base/mod.h"
#include "paging.h"
#include "regs.h"
@ -13,8 +14,7 @@ extern uint8_t rodata_start_addr[];
extern uint8_t rodata_end_addr[];
extern uint8_t data_start_addr[];
extern uint8_t data_end_addr[];
extern uint8_t stack_start_addr[];
extern uint8_t stack_end_addr[];
extern uint8_t __stack_top[];
static uint32_t* kernel_page_table = NULL;
@ -93,6 +93,7 @@ void hal_switch_space(HalPage space) {
__asm__ volatile("sfence.vma");
}
// TODO: Use new error from <helpers/errors.h>
void paging_init(void) {
PhysObj page_phys = pmm_alloc(page_size());
if (page_phys.base == 0) {
@ -107,7 +108,7 @@ void paging_init(void) {
size_t text_end = align_up$((uintptr_t)text_end_addr, page_size());
HalMappingError err = hal_map_page((HalPage){kernel_page_table}, text_start, text_start, text_end - text_start, HAL_MAP_READ | HAL_MAP_EXEC);
if (err != HAL_MAP_OK) {
error$("Couldn't map kernel text, cause: %d", err);
error$("Couldn't map kernel text, cause: %s", HalMappingErrorString(err));
hal_brkpoint();
}
@ -115,7 +116,7 @@ void paging_init(void) {
size_t rodata_end = align_up$((uintptr_t)rodata_end_addr, page_size());
err = hal_map_page((HalPage){kernel_page_table}, rodata_start, rodata_start, rodata_end - rodata_start, HAL_MAP_READ);
if (err != HAL_MAP_OK) {
error$("Couldn't map kernel rodata, cause: %d", err);
error$("Couldn't map kernel rodata, cause: %s", HalMappingErrorString(err));
hal_brkpoint();
}
@ -123,18 +124,23 @@ void paging_init(void) {
size_t data_end = align_up$((uintptr_t)data_end_addr, page_size());
err = hal_map_page((HalPage){kernel_page_table}, data_start, data_start, data_end - data_start, HAL_MAP_READ | HAL_MAP_WRITE);
if (err != HAL_MAP_OK) {
error$("Couldn't map kernel data, cause: %d", err);
error$("Couldn't map kernel data, cause: %s", HalMappingErrorString(err));
hal_brkpoint();
}
size_t stack_start = align_down$((uintptr_t)stack_start_addr, page_size());
size_t stack_end = align_up$((uintptr_t)stack_end_addr, page_size());
size_t stack_start = align_down$((uintptr_t)__stack_top - kib$(128), page_size());
size_t stack_end = align_up$((uintptr_t)__stack_top, page_size());
err = hal_map_page((HalPage){kernel_page_table}, stack_start, stack_start, stack_end - stack_start, HAL_MAP_READ | HAL_MAP_WRITE);
if (err != HAL_MAP_OK) {
error$("Couldn't map kernel stack, cause: %d", err);
error$("Couldn't map kernel stack, cause: %s", HalMappingErrorString(err));
hal_brkpoint();
}
HandoverRecord rec;
handover_foreach_record(handover(), rec) {
hal_map_page((HalPage){kernel_page_table}, rec.start, rec.start, rec.size, HAL_MAP_READ | HAL_MAP_WRITE);
}
debug$("Switching to page table %p", kernel_page_table);
hal_switch_space((HalPage){kernel_page_table});
}

View file

@ -31,7 +31,7 @@ enum mapping_flags {
MAPPING_USER = 1 << 4,
};
struct _page {
struct _hal_page {
uint32_t* ptr;
} __attribute__((packed));

View file

@ -0,0 +1,9 @@
{
"$schema": "https://schemas.cute.engineering/stable/cutekit.manifest.component.v1",
"type": "lib",
"id": "kmalloc",
"requires": [
"allocators",
"traits"
]
}

View file

@ -0,0 +1,9 @@
{
"$schema": "https://schemas.cute.engineering/stable/cutekit.manifest.component.v1",
"id": "kmalloc",
"type": "lib",
"requires": [
"allocators",
"traits"
]
}

70
src/kernel/kmalloc/mod.c Normal file
View file

@ -0,0 +1,70 @@
#include <allocators/slab.h>
#include <logger>
#include <pmm>
static SlabAllocator slabs[6] = {0};
void kmalloc_init(void) {
Allocator pmm = pmm_allocator();
slabs[0] = slab_create(8, &pmm);
slabs[1] = slab_create(16, &pmm);
slabs[2] = slab_create(32, &pmm);
slabs[3] = slab_create(64, &pmm);
slabs[4] = slab_create(128, &pmm);
slabs[5] = slab_create(256, &pmm);
}
static void* _alloc(__attribute__((unused)) void* ctx, size_t len) {
Allocator* alloc = NULL;
if (len <= 8) {
alloc = &slabs[0].base;
} else if (len <= 16) {
alloc = &slabs[1].base;
} else if (len <= 32) {
alloc = &slabs[2].base;
} else if (len <= 64) {
alloc = &slabs[3].base;
} else if (len <= 128) {
alloc = &slabs[4].base;
} else if (len <= 256) {
alloc = &slabs[5].base;
} else {
Allocator pmm = pmm_allocator();
alloc = &pmm;
}
return alloc->alloc(alloc, len);
}
static void _free(__attribute__((unused)) void* ctx, void* ptr, size_t len) {
Allocator* alloc = NULL;
if (len <= 8) {
alloc = &slabs[0].base;
} else if (len <= 16) {
alloc = &slabs[1].base;
} else if (len <= 32) {
alloc = &slabs[2].base;
} else if (len <= 64) {
alloc = &slabs[3].base;
} else if (len <= 128) {
alloc = &slabs[4].base;
} else if (len <= 256) {
alloc = &slabs[5].base;
} else {
Allocator pmm = pmm_allocator();
alloc = &pmm;
}
alloc->free(alloc, ptr, len);
}
Allocator kmalloc_allocator(void) {
return (Allocator){
.alloc = _alloc,
.free = _free,
.realloc = NULL,
};
}

7
src/kernel/kmalloc/mod.h Normal file
View file

@ -0,0 +1,7 @@
#pragma once
#include <traits/allocator.h>
void kmalloc_init(void);
Allocator kmalloc_allocator(void);

View file

@ -0,0 +1 @@
#pragma once

View file

@ -48,3 +48,14 @@ void* memcpy(void* s1, void const* s2, size_t n) {
return s1;
}
char* strncpy(char* s1, char const* s2, size_t n) {
size_t i = 0;
while (s2[i] != 0 && i < n) {
s1[i] = s2[i];
i++;
}
return s1;
}

View file

@ -3,6 +3,11 @@
#include <stddef.h>
size_t strlen(char const* s);
void* memset(void* s, int c, size_t n);
int strncmp(char const* s1, char const* s2, size_t n);
char* strncpy(char* s1, char const* s2, size_t n);
void* memcpy(void* s1, void const* s2, size_t n);

View file

@ -0,0 +1,7 @@
#include <hal>
#include "unistd.h"
int getpagesize(void) {
return page_size();
}

View file

@ -0,0 +1,3 @@
#pragma once
int getpagesize(void);

View file

@ -4,6 +4,7 @@
#include <logger>
#include <string.h>
#include "base/mod.h"
#include "mod.h"
static PmmBitmap bitmap = {0};
@ -50,6 +51,13 @@ void pmm_init(void) {
if (record.tag == HANDOVER_FREE && record.size >= bitmap.len) {
debug$("Bitmap base: %p", record.start);
bitmap.bitmap = (uint8_t*)record.start;
handover_append(handover(), (HandoverRecord){
.tag = HANDOVER_RESERVED,
.start = record.start,
.size = bitmap.len,
});
record.start += bitmap.len;
record.size -= bitmap.len;
break;
@ -109,3 +117,27 @@ PhysObj pmm_alloc(size_t len) {
void pmm_free(PhysObj obj) {
pmm_mark_free(obj.base, obj.len);
}
// === ALLOCATOR ===
static void* _alloc(__attribute__((unused)) void* ctx, size_t len) {
PhysObj obj = pmm_alloc(len);
if (obj.len == 0) {
return NULL;
}
return hal_mmap_l2h(obj.base);
}
static void _free(__attribute__((unused)) void* ctx, void* ptr, size_t len) {
PhysObj obj = {.base = (uintptr_t)hal_mmap_h2l((uintptr_t)ptr), .len = len};
pmm_free(obj);
}
Allocator pmm_allocator(void) {
return (Allocator){
.alloc = _alloc,
.free = _free,
.realloc = NULL,
};
}

View file

@ -3,6 +3,7 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <traits/allocator.h>
typedef struct
{
@ -25,3 +26,7 @@ struct pmm_alloc_param {
PhysObj pmm_alloc(size_t pages);
void pmm_init(void);
void pmm_free(PhysObj obj);
Allocator pmm_allocator(void);

View file

@ -0,0 +1,6 @@
{
"$schema": "https://schemas.cute.engineering/stable/cutekit.manifest.component.v1",
"type": "lib",
"id": "sched",
"requires": []
}

7
src/kernel/sched/mod.c Normal file
View file

@ -0,0 +1,7 @@
#include "mod.h"
static size_t pid = 0;
size_t sched_next_pid(void) {
return pid++;
}

7
src/kernel/sched/mod.h Normal file
View file

@ -0,0 +1,7 @@
#pragma once
#include <stddef.h>
void sched_init(void);
size_t sched_next_pid(void);

View file

@ -0,0 +1,6 @@
{
"$schema": "https://schemas.cute.engineering/stable/cutekit.manifest.component.v1",
"type": "lib",
"id": "task",
"requires": ["sched"]
}

23
src/kernel/task/mod.c Normal file
View file

@ -0,0 +1,23 @@
#include <helpers/math.h>
#include <sched>
#include <string.h>
#include "mod.h"
TaskError task_new(char const* name, HalPage* page_table, uintptr_t ip, Allocator* alloc) {
Task* task = alloc->alloc(alloc, sizeof(Task));
if (task == NULL) {
return TASK_COULDNT_ALLOC;
}
if (page_table == NULL) {
return TASK_PAGE_TABLE_NULL;
}
task->pid = sched_next_pid();
task->page_table = page_table;
strncpy(task->name, name, min$(strlen(name), TASK_NAME_LIMIT - 1));
return TASK_OK;
}

25
src/kernel/task/mod.h Normal file
View file

@ -0,0 +1,25 @@
#pragma once
#include <hal>
#include <helpers/errors.h>
#include <stddef.h>
#include <traits/allocator.h>
#define TASK_NAME_LIMIT (256)
typedef struct {
char name[TASK_NAME_LIMIT];
size_t pid;
HalPage* page_table;
HalContext* context;
} Task;
#define TASK_ERRORS(ERR) \
ERR(TASK_OK, "Operation completed successfully") \
ERR(TASK_COULDNT_ALLOC, "Couldn't allocate memory") \
ERR(TASK_PAGE_TABLE_NULL, "The provided page table is NULL")
DECLARE_ERROR_ENUM(Task, TASK_ERRORS)
DECLARE_ERROR_STRING(Task, TASK_ERRORS)
TaskError task_new(char const* name, HalPage* page_table, uintptr_t ip, Allocator* alloc);

View file

@ -1,7 +1,8 @@
#include "bump.h"
#include <helpers/mem.h>
#include <logger>
#include "bump.h"
static void* alloc(void* ctx, size_t len) {
size_t aligned_len = len & 7 ? align_up$(len, 8) : len;
@ -17,7 +18,7 @@ static void* alloc(void* ctx, size_t len) {
return (void*)ptr;
}
static void free(void* ctx, __attribute__((unused)) void* ptr) {
static void free(void* ctx, __attribute__((unused)) void* ptr, __attribute__((unused)) size_t len) {
BumpAllocator* self = (BumpAllocator*)ctx;
self->allocations--;

View file

@ -1,5 +1,5 @@
{
"$schema": "https://schemas.cute.engineering/stable/cutekit.manifest.component.v1",
"id": "heap",
"id": "allocators",
"type": "lib"
}

View file

@ -0,0 +1,91 @@
#include <allocators/bump.h>
#include <helpers/mem.h>
#include <logger>
#include <unistd.h>
#include "slab.h"
static size_t page_amount(size_t size) {
if (size <= 64) {
return 64;
} else if (size <= 128) {
return 32;
} else if (size <= 512) {
return 16;
} else if (size <= 1024) {
return 8;
} else {
return 4;
}
}
static void* _alloc(void* ctx, __attribute__((unused)) size_t len) {
SlabAllocator* alloc = (SlabAllocator*)ctx;
if (alloc->root == NULL) {
return NULL;
}
void* ptr = (void*)(alloc->root->ptr + sizeof(Slab));
alloc->root = alloc->root->next;
return ptr;
}
static void _free(void* ctx, void* ptr, __attribute__((unused)) size_t len) {
SlabAllocator* alloc = (SlabAllocator*)ctx;
Slab* free = (Slab*)((uintptr_t)ptr - sizeof(Slab));
if (free->ptr + sizeof(Slab) != (uintptr_t)ptr) {
error$("Invalid free %p != %p", free->ptr, ptr);
return;
}
Slab* slab = alloc->root;
while (1) {
if (slab->next == NULL) {
break;
}
slab = slab->next;
}
slab->next = free;
}
SlabAllocator slab_create(uintptr_t size, Allocator* page_alloc) {
size_t n_page = page_amount(size + sizeof(Slab));
void* page = page_alloc->alloc(page_alloc, n_page * getpagesize());
if (page == NULL) {
return (SlabAllocator){0};
}
size_t sz = align_up$(sizeof(Slab) + size, sizeof(void*));
BumpAllocator bump = bump_allocator_create(page, n_page * getpagesize());
Slab* root = bump.base.alloc(&bump, sz);
root->ptr = (uintptr_t)root;
Slab* slab = root;
while (1) {
void* ptr = bump.base.alloc(&bump, sz);
if (ptr == NULL) {
slab->next = NULL;
break;
}
slab->next = ptr;
slab->next->ptr = (uintptr_t)ptr;
slab = slab->next;
}
return (SlabAllocator){
.base = {
.alloc = _alloc,
.free = _free,
.realloc = 0,
},
.root = root,
};
}

View file

@ -0,0 +1,18 @@
#pragma once
#include <stdint.h>
#include <traits/allocator.h>
#define PAGE_SIZE 4096
typedef struct _slab {
uintptr_t ptr;
struct _slab* next;
} Slab;
typedef struct {
Allocator base;
Slab* root;
} SlabAllocator;
SlabAllocator slab_create(uintptr_t size, Allocator* page_alloc);

View file

@ -1,10 +1,7 @@
#include <helpers/mem.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "handover/base/mod.h"
#include "logger/mod.h"
#include "mod.h"
static DTBNode* dtb_root = NULL;

18
src/libs/helpers/errors.h Normal file
View file

@ -0,0 +1,18 @@
#pragma once
#define ENUM_ITEM(name, msg) name,
#define STRINGIFY(name, msg) #msg,
#define DECLARE_ERROR_ENUM(PREFIX, LIST) \
typedef enum { \
LIST(ENUM_ITEM) \
} PREFIX##Error;
#define DECLARE_ERROR_STRING(PREFIX, LIST) \
__attribute__((used)) static const char* PREFIX##ErrorStrings[] = { \
LIST(STRINGIFY) \
}; \
\
inline static char const* PREFIX##ErrorString(PREFIX##Error err) { \
return PREFIX##ErrorStrings[err]; \
}

5
src/libs/helpers/math.h Normal file
View file

@ -0,0 +1,5 @@
#pragma once
#define min$(x, y) ((x) < (y) ? (x) : (y))
#define max$(x, y) ((x) > (y) ? (x) : (y))

View file

@ -5,5 +5,5 @@
typedef struct {
void* (*alloc)(void* ctx, size_t len);
void* (*realloc)(void* ctx, void* ptr, size_t len);
void (*free)(void* ctx, void* ptr);
void (*free)(void* ctx, void* ptr, size_t len);
} Allocator;