aboutsummaryrefslogtreecommitdiff
path: root/mirai.old/memory/heap.zig
diff options
context:
space:
mode:
Diffstat (limited to 'mirai.old/memory/heap.zig')
-rw-r--r--mirai.old/memory/heap.zig215
1 files changed, 215 insertions, 0 deletions
diff --git a/mirai.old/memory/heap.zig b/mirai.old/memory/heap.zig
new file mode 100644
index 0000000..e21456e
--- /dev/null
+++ b/mirai.old/memory/heap.zig
@@ -0,0 +1,215 @@
+//! Kernel Heap Allocator
+
+const heap_const = @import("../common/constants/heap.zig");
+const memory_const = @import("../common/constants/memory.zig");
+const pmm = @import("pmm.zig");
+const serial = @import("../drivers/serial/serial.zig");
+
+const PAGE_SIZE = memory_const.PAGE_SIZE;
+const HIGHER_HALF = memory_const.HIGHER_HALF_START;
+
+const SlabHeader = struct {
+ free_count: usize,
+ total_count: usize,
+ object_size: usize,
+ next_slab: ?*SlabHeader,
+ prev_slab: ?*SlabHeader,
+ first_free: ?*FreeObject,
+};
+
+const FreeObject = struct {
+ next: ?*FreeObject,
+};
+
+const SlabCache = struct {
+ object_size: usize,
+ objects_per_slab: usize,
+ slab_list: ?*SlabHeader,
+};
+
+var caches: [heap_const.NUM_CACHES]SlabCache = undefined;
+var initialized: bool = false;
+
+pub fn init() void {
+ serial.print("\n=== Heap ===\n");
+
+ for (0..heap_const.NUM_CACHES) |i| {
+ const size = heap_const.SIZE_CLASSES[i];
+ const usable_space = PAGE_SIZE - @sizeOf(SlabHeader);
+ const objects_per_slab = usable_space / size;
+
+ caches[i] = SlabCache{
+ .object_size = size,
+ .objects_per_slab = objects_per_slab,
+ .slab_list = null,
+ };
+
+ serial.printf("Cache {}B: {} objects/slab\n", .{ size, objects_per_slab });
+ }
+
+ initialized = true;
+ serial.print("Heap ready\n");
+}
+
+pub fn alloc(size: usize) ?[*]u8 {
+ if (!initialized) return null;
+ if (size == 0) return null;
+
+ if (size > heap_const.LARGE_ALLOC_THRESHOLD) {
+ return alloc_large(size);
+ }
+
+ const cache_idx = find_cache_index(size);
+ return alloc_from_cache(&caches[cache_idx]);
+}
+
+pub fn free(ptr: [*]u8, size: usize) void {
+ if (!initialized) return;
+ if (size == 0) return;
+
+ if (size > heap_const.LARGE_ALLOC_THRESHOLD) {
+ free_large(ptr, size);
+ return;
+ }
+
+ const cache_idx = find_cache_index(size);
+ free_to_cache(&caches[cache_idx], ptr);
+}
+
+fn find_cache_index(size: usize) usize {
+ for (0..heap_const.NUM_CACHES) |i| {
+ if (size <= heap_const.SIZE_CLASSES[i]) {
+ return i;
+ }
+ }
+ return heap_const.NUM_CACHES - 1;
+}
+
+fn alloc_from_cache(cache: *SlabCache) ?[*]u8 {
+ var current_slab = cache.slab_list;
+ while (current_slab) |slab| {
+ if (slab.free_count > 0) {
+ return alloc_from_slab(slab);
+ }
+ current_slab = slab.next_slab;
+ }
+
+ const new_slab = create_slab(cache) orelse return null;
+ new_slab.next_slab = cache.slab_list;
+ new_slab.prev_slab = null;
+
+ if (cache.slab_list) |first| {
+ first.prev_slab = new_slab;
+ }
+
+ cache.slab_list = new_slab;
+
+ return alloc_from_slab(new_slab);
+}
+
+fn create_slab(cache: *SlabCache) ?*SlabHeader {
+ const phys_page = pmm.alloc_page() orelse return null;
+ const virt_addr = phys_page + HIGHER_HALF;
+
+ const page_ptr: [*]volatile u8 = @ptrFromInt(virt_addr);
+ for (0..PAGE_SIZE) |i| {
+ page_ptr[i] = 0;
+ }
+
+ const slab: *SlabHeader = @ptrFromInt(virt_addr);
+ slab.free_count = cache.objects_per_slab;
+ slab.total_count = cache.objects_per_slab;
+ slab.object_size = cache.object_size;
+ slab.next_slab = null;
+ slab.prev_slab = null;
+
+ const first_obj_addr = virt_addr + @sizeOf(SlabHeader);
+ for (0..cache.objects_per_slab) |j| {
+ const obj_addr = first_obj_addr + (j * cache.object_size);
+ const obj: *FreeObject = @ptrFromInt(obj_addr);
+
+ if (j + 1 < cache.objects_per_slab) {
+ const next_addr = obj_addr + cache.object_size;
+ obj.next = @ptrFromInt(next_addr);
+ } else {
+ obj.next = null;
+ }
+ }
+
+ slab.first_free = @ptrFromInt(first_obj_addr);
+
+ return slab;
+}
+
+fn alloc_from_slab(slab: *SlabHeader) ?[*]u8 {
+ if (slab.first_free) |free_obj| {
+ slab.first_free = free_obj.next;
+ slab.free_count -= 1;
+
+ return @ptrFromInt(@intFromPtr(free_obj));
+ }
+
+ return null;
+}
+
+fn free_to_cache(cache: *SlabCache, ptr: [*]u8) void {
+ const ptr_addr = @intFromPtr(ptr);
+ const slab_addr = ptr_addr & ~@as(u64, PAGE_SIZE - 1);
+ const slab: *SlabHeader = @ptrFromInt(slab_addr);
+
+ const obj: *FreeObject = @ptrFromInt(ptr_addr);
+ obj.next = slab.first_free;
+ slab.first_free = obj;
+ slab.free_count += 1;
+
+ if (slab.free_count == slab.total_count) {
+ if (slab.next_slab != null or slab.prev_slab != null) {
+ return_slab_to_pmm(cache, slab);
+ }
+ }
+}
+
+fn return_slab_to_pmm(cache: *SlabCache, slab: *SlabHeader) void {
+ if (slab.prev_slab) |prev| {
+ prev.next_slab = slab.next_slab;
+ } else {
+ cache.slab_list = slab.next_slab;
+ }
+
+ if (slab.next_slab) |next| {
+ next.prev_slab = slab.prev_slab;
+ }
+
+ const slab_addr = @intFromPtr(slab);
+ const phys_addr = slab_addr - HIGHER_HALF;
+ pmm.free_page(phys_addr);
+}
+
+fn alloc_large(size: usize) ?[*]u8 {
+ const num_pages = (size + PAGE_SIZE - 1) / PAGE_SIZE;
+
+ var pages: [heap_const.MAX_LARGE_PAGES]u64 = undefined;
+ if (num_pages > heap_const.MAX_LARGE_PAGES) return null;
+
+ for (0..num_pages) |i| {
+ pages[i] = pmm.alloc_page() orelse {
+ for (0..i) |j| {
+ pmm.free_page(pages[j]);
+ }
+ return null;
+ };
+ }
+
+ const result: [*]u8 = @ptrFromInt(pages[0] + HIGHER_HALF);
+ return result;
+}
+
+fn free_large(ptr: [*]u8, size: usize) void {
+ const num_pages = (size + PAGE_SIZE - 1) / PAGE_SIZE;
+ const base_virt = @intFromPtr(ptr);
+ const base_phys = base_virt - HIGHER_HALF;
+
+ for (0..num_pages) |i| {
+ pmm.free_page(base_phys + (i * PAGE_SIZE));
+ }
+}