diff options
| author | Bobby <[email protected]> | 2026-02-25 07:02:05 +0530 |
|---|---|---|
| committer | Bobby <[email protected]> | 2026-02-25 07:02:05 +0530 |
| commit | 50e29a219dc2e7de9f336e15fe0049b2e64ede0a (patch) | |
| tree | 4c113529676f9af810fb474f02973b54d8dd215f | |
| parent | bd462bccea2a637486f863a6342b9870b35c69aa (diff) | |
| download | akiba-50e29a219dc2e7de9f336e15fe0049b2e64ede0a.tar.xz akiba-50e29a219dc2e7de9f336e15fe0049b2e64ede0a.zip | |
feat: Implement Physical Memory Manager with allocation and deallocation functionality
| -rw-r--r-- | mirai/pmm/allocate/allocate.zig | 10 | ||||
| -rw-r--r-- | mirai/pmm/allocate/contiguous.zig | 81 | ||||
| -rw-r--r-- | mirai/pmm/allocate/single.zig | 40 | ||||
| -rw-r--r-- | mirai/pmm/bitmap/bitmap.zig | 12 | ||||
| -rw-r--r-- | mirai/pmm/bitmap/operations.zig | 82 | ||||
| -rw-r--r-- | mirai/pmm/constants/constants.zig | 13 | ||||
| -rw-r--r-- | mirai/pmm/constants/limits.zig | 11 | ||||
| -rw-r--r-- | mirai/pmm/free/free.zig | 7 | ||||
| -rw-r--r-- | mirai/pmm/free/range.zig | 36 | ||||
| -rw-r--r-- | mirai/pmm/free/single.zig | 25 | ||||
| -rw-r--r-- | mirai/pmm/init/init.zig | 5 | ||||
| -rw-r--r-- | mirai/pmm/init/setup.zig | 98 | ||||
| -rw-r--r-- | mirai/pmm/pmm.zig | 37 | ||||
| -rw-r--r-- | mirai/pmm/state.zig | 33 | ||||
| -rw-r--r-- | mirai/pmm/types/page.zig | 27 | ||||
| -rw-r--r-- | mirai/pmm/types/region.zig | 28 | ||||
| -rw-r--r-- | mirai/pmm/types/statistics.zig | 26 | ||||
| -rw-r--r-- | mirai/pmm/types/types.zig | 11 |
18 files changed, 582 insertions, 0 deletions
diff --git a/mirai/pmm/allocate/allocate.zig b/mirai/pmm/allocate/allocate.zig new file mode 100644 index 0000000..1fd24e2 --- /dev/null +++ b/mirai/pmm/allocate/allocate.zig @@ -0,0 +1,10 @@ +//! Physical Page Allocation + +pub const single = @import("single.zig"); +pub const contiguous = @import("contiguous.zig"); + +pub const allocate_page = single.allocate_page; +pub const allocate_page_zeroed = single.allocate_page_zeroed; +pub const allocate_contiguous = contiguous.allocate_contiguous; +pub const allocate_contiguous_zeroed = contiguous.allocate_contiguous_zeroed; +pub const allocate_aligned = contiguous.allocate_aligned; diff --git a/mirai/pmm/allocate/contiguous.zig b/mirai/pmm/allocate/contiguous.zig new file mode 100644 index 0000000..c930d00 --- /dev/null +++ b/mirai/pmm/allocate/contiguous.zig @@ -0,0 +1,81 @@ +//! Contiguous Page Allocation + +const bitmap = @import("../bitmap/bitmap.zig"); +const state = @import("../state.zig"); +const common = @import("../../../common/common.zig"); + +const AllocationError = common.errors.memory.AllocationError; + +pub fn allocate_contiguous(page_count: u64) AllocationError!u64 { + if (page_count == 0) { + return AllocationError.InvalidSize; + } + + const pmm_state = state.get_state(); + + if (pmm_state.free_pages < page_count) { + return AllocationError.OutOfMemory; + } + + const start_page = bitmap.find_contiguous_clear( + pmm_state.bitmap, + 0, + pmm_state.total_pages, + page_count, + ) orelse return AllocationError.OutOfMemory; + + bitmap.set_range(pmm_state.bitmap, start_page, page_count); + pmm_state.free_pages -= page_count; + pmm_state.used_pages += page_count; + + return start_page << 12; +} + +pub fn allocate_contiguous_zeroed(page_count: u64) AllocationError!u64 { + const physical_address = try allocate_contiguous(page_count); + const virtual_address = physical_address + common.constants.memory.layout.physmap_base; + const total_bytes = page_count * 4096; + const page_ptr: [*]u8 = @ptrFromInt(virtual_address); + @memset(page_ptr[0..total_bytes], 0); + return physical_address; +} + +pub fn allocate_aligned(page_count: u64, alignment_pages: u64) AllocationError!u64 { + if (page_count == 0 or alignment_pages == 0) { + return AllocationError.InvalidSize; + } + + const pmm_state = state.get_state(); + + if (pmm_state.free_pages < page_count) { + return AllocationError.OutOfMemory; + } + + var search_start: u64 = 0; + while (search_start + page_count <= pmm_state.total_pages) { + const aligned_start = (search_start + alignment_pages - 1) / alignment_pages * alignment_pages; + + if (aligned_start + page_count > pmm_state.total_pages) { + break; + } + + var found = true; + var check_index: u64 = 0; + while (check_index < page_count) : (check_index += 1) { + if (bitmap.test_bit(pmm_state.bitmap, aligned_start + check_index)) { + found = false; + search_start = aligned_start + check_index + 1; + break; + } + } + + if (found) { + bitmap.set_range(pmm_state.bitmap, aligned_start, page_count); + pmm_state.free_pages -= page_count; + pmm_state.used_pages += page_count; + return aligned_start << 12; + } + } + + return AllocationError.OutOfMemory; +} diff --git a/mirai/pmm/allocate/single.zig b/mirai/pmm/allocate/single.zig new file mode 100644 index 0000000..c7a4741 --- /dev/null +++ b/mirai/pmm/allocate/single.zig @@ -0,0 +1,40 @@ +//! Single Page Allocation + +const bitmap = @import("../bitmap/bitmap.zig"); +const state = @import("../state.zig"); +const common = @import("../../../common/common.zig"); + +const AllocationError = common.errors.memory.AllocationError; + +pub fn allocate_page() AllocationError!u64 { + const pmm_state = state.get_state(); + + if (pmm_state.free_pages == 0) { + return AllocationError.OutOfMemory; + } + + const page_index = bitmap.find_first_clear( + pmm_state.bitmap, + pmm_state.search_start, + pmm_state.total_pages, + ) orelse bitmap.find_first_clear( + pmm_state.bitmap, + 0, + pmm_state.search_start, + ) orelse return AllocationError.OutOfMemory; + + bitmap.set_bit(pmm_state.bitmap, page_index); + pmm_state.free_pages -= 1; + pmm_state.used_pages += 1; + pmm_state.search_start = page_index + 1; + + return page_index << 12; +} + +pub fn allocate_page_zeroed() AllocationError!u64 { + const physical_address = try allocate_page(); + const virtual_address = physical_address + common.constants.memory.layout.physmap_base; + const page_ptr: [*]u8 = @ptrFromInt(virtual_address); + @memset(page_ptr[0..4096], 0); + return physical_address; +} diff --git a/mirai/pmm/bitmap/bitmap.zig b/mirai/pmm/bitmap/bitmap.zig new file mode 100644 index 0000000..21e11f2 --- /dev/null +++ b/mirai/pmm/bitmap/bitmap.zig @@ -0,0 +1,12 @@ +//! Bitmap Module + +pub const operations = @import("operations.zig"); + +pub const set_bit = operations.set_bit; +pub const clear_bit = operations.clear_bit; +pub const test_bit = operations.test_bit; +pub const set_range = operations.set_range; +pub const clear_range = operations.clear_range; +pub const find_first_clear = operations.find_first_clear; +pub const find_contiguous_clear = operations.find_contiguous_clear; +pub const count_clear_bits = operations.count_clear_bits; diff --git a/mirai/pmm/bitmap/operations.zig b/mirai/pmm/bitmap/operations.zig new file mode 100644 index 0000000..d793fa3 --- /dev/null +++ b/mirai/pmm/bitmap/operations.zig @@ -0,0 +1,82 @@ +//! Bitmap Operations + +pub fn set_bit(bitmap: []u8, bit_index: u64) void { + const byte_index = bit_index / 8; + const bit_offset: u3 = @truncate(bit_index % 8); + if (byte_index < bitmap.len) { + bitmap[byte_index] |= @as(u8, 1) << bit_offset; + } +} + +pub fn clear_bit(bitmap: []u8, bit_index: u64) void { + const byte_index = bit_index / 8; + const bit_offset: u3 = @truncate(bit_index % 8); + if (byte_index < bitmap.len) { + bitmap[byte_index] &= ~(@as(u8, 1) << bit_offset); + } +} + +pub fn test_bit(bitmap: []const u8, bit_index: u64) bool { + const byte_index = bit_index / 8; + const bit_offset: u3 = @truncate(bit_index % 8); + if (byte_index < bitmap.len) { + return (bitmap[byte_index] & (@as(u8, 1) << bit_offset)) != 0; + } + return true; +} + +pub fn set_range(bitmap: []u8, start_bit: u64, count: u64) void { + var bit_index = start_bit; + const end_bit = start_bit + count; + while (bit_index < end_bit) : (bit_index += 1) { + set_bit(bitmap, bit_index); + } +} + +pub fn clear_range(bitmap: []u8, start_bit: u64, count: u64) void { + var bit_index = start_bit; + const end_bit = start_bit + count; + while (bit_index < end_bit) : (bit_index += 1) { + clear_bit(bitmap, bit_index); + } +} + +pub fn find_first_clear(bitmap: []const u8, start_bit: u64, max_bit: u64) ?u64 { + var bit_index = start_bit; + while (bit_index < max_bit) : (bit_index += 1) { + if (!test_bit(bitmap, bit_index)) { + return bit_index; + } + } + return null; +} + +pub fn find_contiguous_clear(bitmap: []const u8, start_bit: u64, max_bit: u64, count: u64) ?u64 { + var bit_index = start_bit; + while (bit_index + count <= max_bit) { + var found = true; + var check_index: u64 = 0; + while (check_index < count) : (check_index += 1) { + if (test_bit(bitmap, bit_index + check_index)) { + found = false; + bit_index += check_index + 1; + break; + } + } + if (found) { + return bit_index; + } + } + return null; +} + +pub fn count_clear_bits(bitmap: []const u8, max_bit: u64) u64 { + var count: u64 = 0; + var bit_index: u64 = 0; + while (bit_index < max_bit) : (bit_index += 1) { + if (!test_bit(bitmap, bit_index)) { + count += 1; + } + } + return count; +}
\ No newline at end of file diff --git a/mirai/pmm/constants/constants.zig b/mirai/pmm/constants/constants.zig new file mode 100644 index 0000000..4a3022e --- /dev/null +++ b/mirai/pmm/constants/constants.zig @@ -0,0 +1,13 @@ +//! Physical Memory Manager Constants + +pub const limits = @import("limits.zig"); + +pub const max_physical_memory = limits.max_physical_memory; +pub const max_physical_pages = limits.max_physical_pages; +pub const bitmap_size_bytes = limits.bitmap_size_bytes; + +pub const memory_region_available = limits.memory_region_available; +pub const memory_region_reserved = limits.memory_region_reserved; +pub const memory_region_acpi_reclaimable = limits.memory_region_acpi_reclaimable; +pub const memory_region_acpi_nvs = limits.memory_region_acpi_nvs; +pub const memory_region_bad = limits.memory_region_bad; diff --git a/mirai/pmm/constants/limits.zig b/mirai/pmm/constants/limits.zig new file mode 100644 index 0000000..8efd6d4 --- /dev/null +++ b/mirai/pmm/constants/limits.zig @@ -0,0 +1,11 @@ +//! Physical Memory Manager Constants + +pub const max_physical_memory: u64 = 512 * 1024 * 1024 * 1024; +pub const max_physical_pages: u64 = max_physical_memory / 4096; +pub const bitmap_size_bytes: u64 = max_physical_pages / 8; + +pub const memory_region_available: u32 = 1; +pub const memory_region_reserved: u32 = 2; +pub const memory_region_acpi_reclaimable: u32 = 3; +pub const memory_region_acpi_nvs: u32 = 4; +pub const memory_region_bad: u32 = 5; diff --git a/mirai/pmm/free/free.zig b/mirai/pmm/free/free.zig new file mode 100644 index 0000000..d778f28 --- /dev/null +++ b/mirai/pmm/free/free.zig @@ -0,0 +1,7 @@ +//! Physical Page Free + +pub const single = @import("single.zig"); +pub const range = @import("range.zig"); + +pub const free_page = single.free_page; +pub const free_range = range.free_range; diff --git a/mirai/pmm/free/range.zig b/mirai/pmm/free/range.zig new file mode 100644 index 0000000..ebf703b --- /dev/null +++ b/mirai/pmm/free/range.zig @@ -0,0 +1,36 @@ +//! Range Page Free + +const bitmap = @import("../bitmap/bitmap.zig"); +const state = @import("../state.zig"); + +pub fn free_range(physical_address: u64, page_count: u64) void { + if (page_count == 0) { + return; + } + + const start_page = physical_address >> 12; + const pmm_state = state.get_state(); + + var freed_count: u64 = 0; + var page_index: u64 = 0; + + while (page_index < page_count) : (page_index += 1) { + const current_page = start_page + page_index; + + if (current_page >= pmm_state.total_pages) { + break; + } + + if (bitmap.test_bit(pmm_state.bitmap, current_page)) { + bitmap.clear_bit(pmm_state.bitmap, current_page); + freed_count += 1; + } + } + + pmm_state.free_pages += freed_count; + pmm_state.used_pages -= freed_count; + + if (start_page < pmm_state.search_start) { + pmm_state.search_start = start_page; + } +} diff --git a/mirai/pmm/free/single.zig b/mirai/pmm/free/single.zig new file mode 100644 index 0000000..0c93554 --- /dev/null +++ b/mirai/pmm/free/single.zig @@ -0,0 +1,25 @@ +//! Single Page Free + +const bitmap = @import("../bitmap/bitmap.zig"); +const state = @import("../state.zig"); + +pub fn free_page(physical_address: u64) void { + const page_index = physical_address >> 12; + const pmm_state = state.get_state(); + + if (page_index >= pmm_state.total_pages) { + return; + } + + if (!bitmap.test_bit(pmm_state.bitmap, page_index)) { + return; + } + + bitmap.clear_bit(pmm_state.bitmap, page_index); + pmm_state.free_pages += 1; + pmm_state.used_pages -= 1; + + if (page_index < pmm_state.search_start) { + pmm_state.search_start = page_index; + } +} diff --git a/mirai/pmm/init/init.zig b/mirai/pmm/init/init.zig new file mode 100644 index 0000000..0149cc3 --- /dev/null +++ b/mirai/pmm/init/init.zig @@ -0,0 +1,5 @@ +//! Physical Memory Manager Initialization + +pub const setup = @import("setup.zig"); + +pub const initialize_from_memory_map = setup.initialize_from_memory_map; diff --git a/mirai/pmm/init/setup.zig b/mirai/pmm/init/setup.zig new file mode 100644 index 0000000..e98ec2d --- /dev/null +++ b/mirai/pmm/init/setup.zig @@ -0,0 +1,98 @@ +//! Physical Memory Manager Setup + +const bitmap_ops = @import("../bitmap/bitmap.zig"); +const state = @import("../state.zig"); +const types = @import("../types/types.zig"); +const common = @import("../../../common/common.zig"); + +const memory_layout = common.constants.memory.layout; + +pub fn initialize_from_memory_map( + memory_map_entries: [*]const types.MemoryRegion, + entry_count: u64, + bitmap_location: u64, +) void { + const pmm_state = state.get_state(); + + var highest_address: u64 = 0; + var entry_index: u64 = 0; + while (entry_index < entry_count) : (entry_index += 1) { + const region = memory_map_entries[entry_index]; + const region_end = region.end_address(); + if (region_end > highest_address) { + highest_address = region_end; + } + } + + pmm_state.total_pages = highest_address >> 12; + pmm_state.bitmap = @ptrFromInt(bitmap_location + memory_layout.physmap_base); + pmm_state.bitmap_size = (pmm_state.total_pages + 7) / 8; + + bitmap_ops.set_range(pmm_state.bitmap, 0, pmm_state.total_pages); + pmm_state.free_pages = 0; + pmm_state.used_pages = pmm_state.total_pages; + + entry_index = 0; + while (entry_index < entry_count) : (entry_index += 1) { + const region = memory_map_entries[entry_index]; + if (region.is_usable()) { + mark_region_free(region.base_address, region.length); + } + } + + reserve_kernel_memory(); + reserve_bitmap_memory(bitmap_location); + + pmm_state.search_start = 0; + pmm_state.initialized = true; +} + +fn mark_region_free(base_address: u64, length: u64) void { + const pmm_state = state.get_state(); + + const start_page = (base_address + 4095) >> 12; + const end_page = (base_address + length) >> 12; + + if (end_page <= start_page) { + return; + } + + const page_count = end_page - start_page; + + bitmap_ops.clear_range(pmm_state.bitmap, start_page, page_count); + pmm_state.free_pages += page_count; + pmm_state.used_pages -= page_count; +} + +fn reserve_kernel_memory() void { + const pmm_state = state.get_state(); + + const kernel_start_page: u64 = 0; + const kernel_end_page: u64 = 0x200; + + var page_index = kernel_start_page; + while (page_index < kernel_end_page) : (page_index += 1) { + if (!bitmap_ops.test_bit(pmm_state.bitmap, page_index)) { + bitmap_ops.set_bit(pmm_state.bitmap, page_index); + pmm_state.free_pages -= 1; + pmm_state.used_pages += 1; + } + } +} + +fn reserve_bitmap_memory(bitmap_location: u64) void { + const pmm_state = state.get_state(); + + const bitmap_start_page = bitmap_location >> 12; + const bitmap_page_count = (pmm_state.bitmap_size + 4095) >> 12; + + var page_index: u64 = 0; + while (page_index < bitmap_page_count) : (page_index += 1) { + const current_page = bitmap_start_page + page_index; + if (!bitmap_ops.test_bit(pmm_state.bitmap, current_page)) { + bitmap_ops.set_bit(pmm_state.bitmap, current_page); + pmm_state.free_pages -= 1; + pmm_state.used_pages += 1; + } + } +} diff --git a/mirai/pmm/pmm.zig b/mirai/pmm/pmm.zig new file mode 100644 index 0000000..d45788a --- /dev/null +++ b/mirai/pmm/pmm.zig @@ -0,0 +1,37 @@ +//! Physical Memory Manager + +pub const constants = @import("constants/constants.zig"); +pub const types = @import("types/types.zig"); +pub const bitmap = @import("bitmap/bitmap.zig"); +pub const allocate = @import("allocate/allocate.zig"); +pub const free = @import("free/free.zig"); +pub const init = @import("init/init.zig"); +pub const state = @import("state.zig"); + +pub const initialize = init.initialize_from_memory_map; +pub const is_initialized = state.is_initialized; +pub const get_state = state.get_state; + +pub const allocate_page = allocate.allocate_page; +pub const allocate_page_zeroed = allocate.allocate_page_zeroed; +pub const allocate_contiguous = allocate.allocate_contiguous; +pub const allocate_contiguous_zeroed = allocate.allocate_contiguous_zeroed; +pub const allocate_aligned = allocate.allocate_aligned; + +pub const free_page = free.free_page; +pub const free_range = free.free_range; + +pub const Statistics = types.Statistics; +pub const PhysicalPage = types.PhysicalPage; +pub const MemoryRegion = types.MemoryRegion; + +pub fn get_statistics() Statistics { + const pmm_state = state.get_state(); + return Statistics{ + .total_pages = pmm_state.total_pages, + .free_pages = pmm_state.free_pages, + .used_pages = pmm_state.used_pages, + .reserved_pages = pmm_state.reserved_pages, + .wired_pages = pmm_state.wired_pages, + }; +} diff --git a/mirai/pmm/state.zig b/mirai/pmm/state.zig new file mode 100644 index 0000000..a76c1b4 --- /dev/null +++ b/mirai/pmm/state.zig @@ -0,0 +1,33 @@ +//! Physical Memory Manager State + +pub const State = struct { + bitmap: []u8, + bitmap_size: u64, + total_pages: u64, + free_pages: u64, + used_pages: u64, + reserved_pages: u64, + wired_pages: u64, + search_start: u64, + initialized: bool, +}; + +var global_state: State = .{ + .bitmap = &[_]u8{}, + .bitmap_size = 0, + .total_pages = 0, + .free_pages = 0, + .used_pages = 0, + .reserved_pages = 0, + .wired_pages = 0, + .search_start = 0, + .initialized = false, +}; + +pub fn get_state() *State { + return &global_state; +} + +pub fn is_initialized() bool { + return global_state.initialized; +} diff --git a/mirai/pmm/types/page.zig b/mirai/pmm/types/page.zig new file mode 100644 index 0000000..6928eaa --- /dev/null +++ b/mirai/pmm/types/page.zig @@ -0,0 +1,27 @@ +//! Physical Page Type + +pub const PhysicalPage = struct { + frame_number: u64, + reference_count: u32, + flags: PageFlags, + + pub const PageFlags = packed struct { + allocated: bool = false, + wired: bool = false, + reserved: bool = false, + kernel: bool = false, + padding: u28 = 0, + }; + + pub fn physical_address(self: PhysicalPage) u64 { + return self.frame_number << 12; + } + + pub fn from_physical_address(address: u64) PhysicalPage { + return PhysicalPage{ + .frame_number = address >> 12, + .reference_count = 0, + .flags = .{}, + }; + } +}; diff --git a/mirai/pmm/types/region.zig b/mirai/pmm/types/region.zig new file mode 100644 index 0000000..e1b476c --- /dev/null +++ b/mirai/pmm/types/region.zig @@ -0,0 +1,28 @@ +//! Memory Region Type + +pub const MemoryRegion = struct { + base_address: u64, + length: u64, + region_type: RegionType, + + pub const RegionType = enum(u32) { + available = 1, + reserved = 2, + acpi_reclaimable = 3, + acpi_nvs = 4, + bad = 5, + _, + }; + + pub fn end_address(self: MemoryRegion) u64 { + return self.base_address + self.length; + } + + pub fn page_count(self: MemoryRegion) u64 { + return self.length / 4096; + } + + pub fn is_usable(self: MemoryRegion) bool { + return self.region_type == .available; + } +}; diff --git a/mirai/pmm/types/statistics.zig b/mirai/pmm/types/statistics.zig new file mode 100644 index 0000000..4128de2 --- /dev/null +++ b/mirai/pmm/types/statistics.zig @@ -0,0 +1,26 @@ +//! Physical Memory Statistics + +pub const Statistics = struct { + total_pages: u64, + free_pages: u64, + used_pages: u64, + reserved_pages: u64, + wired_pages: u64, + + pub fn total_bytes(self: Statistics) u64 { + return self.total_pages * 4096; + } + + pub fn free_bytes(self: Statistics) u64 { + return self.free_pages * 4096; + } + + pub fn used_bytes(self: Statistics) u64 { + return self.used_pages * 4096; + } + + pub fn usage_percentage(self: Statistics) u8 { + if (self.total_pages == 0) return 0; + return @truncate((self.used_pages * 100) / self.total_pages); + } +}; diff --git a/mirai/pmm/types/types.zig b/mirai/pmm/types/types.zig new file mode 100644 index 0000000..3c52548 --- /dev/null +++ b/mirai/pmm/types/types.zig @@ -0,0 +1,11 @@ +//! Physical Memory Manager Types + +pub const page = @import("page.zig"); +pub const region = @import("region.zig"); +pub const statistics = @import("statistics.zig"); + +pub const PhysicalPage = page.PhysicalPage; +pub const PageFlags = page.PhysicalPage.PageFlags; +pub const MemoryRegion = region.MemoryRegion; +pub const RegionType = region.MemoryRegion.RegionType; +pub const Statistics = statistics.Statistics; |
