aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBobby <[email protected]>2026-02-25 07:02:05 +0530
committerBobby <[email protected]>2026-02-25 07:02:05 +0530
commit50e29a219dc2e7de9f336e15fe0049b2e64ede0a (patch)
tree4c113529676f9af810fb474f02973b54d8dd215f
parentbd462bccea2a637486f863a6342b9870b35c69aa (diff)
downloadakiba-50e29a219dc2e7de9f336e15fe0049b2e64ede0a.tar.xz
akiba-50e29a219dc2e7de9f336e15fe0049b2e64ede0a.zip
feat: Implement Physical Memory Manager with allocation and deallocation functionality
-rw-r--r--mirai/pmm/allocate/allocate.zig10
-rw-r--r--mirai/pmm/allocate/contiguous.zig81
-rw-r--r--mirai/pmm/allocate/single.zig40
-rw-r--r--mirai/pmm/bitmap/bitmap.zig12
-rw-r--r--mirai/pmm/bitmap/operations.zig82
-rw-r--r--mirai/pmm/constants/constants.zig13
-rw-r--r--mirai/pmm/constants/limits.zig11
-rw-r--r--mirai/pmm/free/free.zig7
-rw-r--r--mirai/pmm/free/range.zig36
-rw-r--r--mirai/pmm/free/single.zig25
-rw-r--r--mirai/pmm/init/init.zig5
-rw-r--r--mirai/pmm/init/setup.zig98
-rw-r--r--mirai/pmm/pmm.zig37
-rw-r--r--mirai/pmm/state.zig33
-rw-r--r--mirai/pmm/types/page.zig27
-rw-r--r--mirai/pmm/types/region.zig28
-rw-r--r--mirai/pmm/types/statistics.zig26
-rw-r--r--mirai/pmm/types/types.zig11
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;