diff --git a/src/arena.zig b/src/arena.zig index 6ea0228..bfca6f1 100644 --- a/src/arena.zig +++ b/src/arena.zig @@ -1,5 +1,7 @@ //! Simple bump allocator arena. +const std = @import("std"); + const phys_memory = @import("mem/phys.zig"); const log = @import("debug.zig").log; const mem = @import("mem.zig"); diff --git a/src/mem/phys.zig b/src/mem/phys.zig index d552b54..af9360f 100644 --- a/src/mem/phys.zig +++ b/src/mem/phys.zig @@ -43,21 +43,22 @@ const PhysicalMemoryManager = struct { page_array: []Page, offset: u64 = 0, last_free: usize = 0, + bitmap: []u64, // Each bit represents a page const RECORDS_PER_PAGE: usize = vmm.PAGE_SIZE / @sizeOf(Page); fn alloc_page(self: *@This()) ?mem.PhysicalAddress { - for (self.last_free..self.page_array.len) |i| { - if (self.page_array[i].refcount == 0) { - self.page_array[i].refcount += 1; - self.last_free = (i + 1) % self.page_array.len; + for (self.last_free..self.bitmap.len * 64) |i| { + if (!self.is_page_used(i)) { + self.set_page_used(i); + self.last_free = (i + 1) % (self.bitmap.len * 64); return .{ .raw = self.offset + i * vmm.PAGE_SIZE }; } } for (0..self.last_free) |i| { - if (self.page_array[i].refcount == 0) { - self.page_array[i].refcount += 1; - self.last_free = (i + 1) % self.page_array.len; + if (!self.is_page_used(i)) { + self.set_page_used(i); + self.last_free = (i + 1) % (self.bitmap.len * 64); return .{ .raw = self.offset + i * vmm.PAGE_SIZE }; } } @@ -107,25 +108,44 @@ const PhysicalMemoryManager = struct { fn free_page(self: *@This(), page: mem.PhysicalAddress) void { const index = self.valid_index(page); - if (self.page_array[index].refcount == 0) { + if (!self.is_page_used(index)) { log.panic("free_page: double free of page 0x{x} detected", .{page.raw}); } - self.page_array[index].refcount -= 1; - if (self.page_array[index].refcount == 0) { - self.last_free = index; - } + self.clear_page_used(index); + self.last_free = index; } fn get_page(self: *@This(), page: mem.PhysicalAddress) *Page { const index = self.valid_index(page); return &self.page_array[index]; } + + fn is_page_used(self: *@This(), index: usize) bool { + const word_index = index / 64; + const bit_index = index % 64; + return (self.bitmap[word_index] & (@as(u64, 1) << @intCast(bit_index))) != 0; + } + + fn set_page_used(self: *@This(), index: usize) void { + const word_index = index / 64; + const bit_index = index % 64; + self.bitmap[word_index] |= (@as(u64, 1) << @intCast(bit_index)); + } + + fn clear_page_used(self: *@This(), index: usize) void { + const word_index = index / 64; + const bit_index = index % 64; + self.bitmap[word_index] &= ~(@as(u64, 1) << @intCast(bit_index)); + } }; var g_memory_regions: std.BoundedArray(MemoryRegion, 16) = .{}; var g_reserved_regions: std.BoundedArray(MemoryRegion, 16) = .{}; var g_physical_memory_lock = Spinlock{}; -var g_physical_memory = PhysicalMemoryManager{ .page_array = undefined }; +var g_physical_memory = PhysicalMemoryManager{ + .page_array = undefined, + .bitmap = undefined, +}; /// Adds an available memory region to the list. /// @@ -198,6 +218,21 @@ fn alloc_page_array(page_count: usize) []Page { @panic("TODO"); } +fn alloc_bitmap(pages: usize) []u64 { + // Round up to the upper u64 that has at least `pages` bits + const bitmap_entries = (pages + 63) / 64; + for (g_memory_regions.constSlice()) |region| { + if (alloc_from_region(®ion, "bitmap", bitmap_entries * @sizeOf(u64))) |phys_address| { + const vaddr = (mem.PhysicalAddress{ .raw = phys_address }).virtualize(); + const ptr: [*]u64 = @ptrFromInt(vaddr); + const slice: []u64 = ptr[0..bitmap_entries]; + @memset(slice, 0); // Clear the bitmap + return slice; + } + } + @panic("Failed to allocate bitmap"); +} + /// Initializes the physical memory management. /// /// # Notes @@ -218,10 +253,13 @@ pub fn init() void { } const memory_pages = (memory_end - memory_start) / vmm.PAGE_SIZE; // == bitmap bits required + const bitmap = alloc_bitmap(memory_pages); + const page_array_pages = (memory_pages + PhysicalMemoryManager.RECORDS_PER_PAGE - 1) // - / PhysicalMemoryManager.RECORDS_PER_PAGE; + / PhysicalMemoryManager.RECORDS_PER_PAGE; const page_array = alloc_page_array(page_array_pages); + var available_pages: usize = 0; for (g_memory_regions.constSlice()) |region| { @@ -248,6 +286,7 @@ pub fn init() void { g_physical_memory.page_array = page_array; g_physical_memory.offset = memory_start; + g_physical_memory.bitmap = bitmap; } fn trace_allocation(count: usize, page: ?mem.PhysicalAddress) void {