aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBobby <[email protected]>2026-02-25 07:17:49 +0530
committerBobby <[email protected]>2026-02-25 07:17:49 +0530
commit15f9ee5984f97bdca1b75a4f732c4a503ee504dc (patch)
treeb1da26512642a740756f67497d4af4e9b22615ed
parent50e29a219dc2e7de9f336e15fe0049b2e64ede0a (diff)
downloadakiba-15f9ee5984f97bdca1b75a4f732c4a503ee504dc.tar.xz
akiba-15f9ee5984f97bdca1b75a4f732c4a503ee504dc.zip
feat: Implement Kagami page table abstraction with allocation, mapping, and protection functionalities
-rw-r--r--mirai/kagami/allocate/allocate.zig35
-rw-r--r--mirai/kagami/constants/constants.zig4
-rw-r--r--mirai/kagami/constants/protection.zig19
-rw-r--r--mirai/kagami/constants/tables.zig12
-rw-r--r--mirai/kagami/create/create.zig71
-rw-r--r--mirai/kagami/destroy/destroy.zig78
-rw-r--r--mirai/kagami/enter/enter.zig87
-rw-r--r--mirai/kagami/extract/extract.zig35
-rw-r--r--mirai/kagami/kagami.zig51
-rw-r--r--mirai/kagami/protect/protect.zig41
-rw-r--r--mirai/kagami/remove/remove.zig40
-rw-r--r--mirai/kagami/state.zig41
-rw-r--r--mirai/kagami/tables/allocate.zig100
-rw-r--r--mirai/kagami/tables/tables.zig18
-rw-r--r--mirai/kagami/tables/walk.zig75
-rw-r--r--mirai/kagami/types/entry.zig56
-rw-r--r--mirai/kagami/types/kagami.zig56
-rw-r--r--mirai/kagami/types/table.zig38
-rw-r--r--mirai/kagami/types/types.zig9
19 files changed, 866 insertions, 0 deletions
diff --git a/mirai/kagami/allocate/allocate.zig b/mirai/kagami/allocate/allocate.zig
new file mode 100644
index 0000000..d57f140
--- /dev/null
+++ b/mirai/kagami/allocate/allocate.zig
@@ -0,0 +1,35 @@
+//! Extract Physical Address
+
+const types = @import("../types/types.zig");
+const tables = @import("../tables/tables.zig");
+
+const Entry = types.Entry;
+const Kagami = types.Kagami;
+
+pub fn extract(kagami: *const Kagami, virtual_address: u64) ?u64 {
+ const entry = tables.walk_to_entry(kagami.pml4_physical, virtual_address) orelse return null;
+
+ if (!entry.is_present()) {
+ return null;
+ }
+
+ const physical_base = entry.get_physical_address();
+ const offset = virtual_address & 0xFFF;
+
+ return physical_base | offset;
+}
+
+pub fn is_mapped(kagami: *const Kagami, virtual_address: u64) bool {
+ const entry = tables.walk_to_entry(kagami.pml4_physical, virtual_address) orelse return false;
+ return entry.is_present();
+}
+
+pub fn is_writable(kagami: *const Kagami, virtual_address: u64) bool {
+ const entry = tables.walk_to_entry(kagami.pml4_physical, virtual_address) orelse return false;
+ return entry.is_present() and entry.is_writable();
+}
+
+pub fn is_user_accessible(kagami: *const Kagami, virtual_address: u64) bool {
+ const entry = tables.walk_to_entry(kagami.pml4_physical, virtual_address) orelse return false;
+ return entry.is_present() and entry.is_user();
+}
diff --git a/mirai/kagami/constants/constants.zig b/mirai/kagami/constants/constants.zig
new file mode 100644
index 0000000..10e5379
--- /dev/null
+++ b/mirai/kagami/constants/constants.zig
@@ -0,0 +1,4 @@
+//! Kagami Constants
+
+pub const tables = @import("tables.zig");
+pub const protection = @import("protection.zig");
diff --git a/mirai/kagami/constants/protection.zig b/mirai/kagami/constants/protection.zig
new file mode 100644
index 0000000..c1f0311
--- /dev/null
+++ b/mirai/kagami/constants/protection.zig
@@ -0,0 +1,19 @@
+//! Kagami Protection Constants
+
+pub const none: u8 = 0x00;
+pub const read: u8 = 0x01;
+pub const write: u8 = 0x02;
+pub const execute: u8 = 0x04;
+pub const user: u8 = 0x08;
+pub const wired: u8 = 0x10;
+pub const nocache: u8 = 0x20;
+
+pub const kernel_read: u8 = read;
+pub const kernel_write: u8 = read | write;
+pub const kernel_execute: u8 = read | execute;
+pub const kernel_all: u8 = read | write | execute;
+
+pub const user_read: u8 = read | user;
+pub const user_write: u8 = read | write | user;
+pub const user_execute: u8 = read | execute | user;
+pub const user_all: u8 = read | write | execute | user;
diff --git a/mirai/kagami/constants/tables.zig b/mirai/kagami/constants/tables.zig
new file mode 100644
index 0000000..0e83553
--- /dev/null
+++ b/mirai/kagami/constants/tables.zig
@@ -0,0 +1,12 @@
+//! Kagami Table Constants
+
+pub const pml4_entries: u64 = 512;
+pub const pdpt_entries: u64 = 512;
+pub const pd_entries: u64 = 512;
+pub const pt_entries: u64 = 512;
+
+pub const kernel_pml4_start: u64 = 256;
+pub const kernel_pml4_end: u64 = 512;
+
+pub const user_pml4_start: u64 = 0;
+pub const user_pml4_end: u64 = 256;
diff --git a/mirai/kagami/create/create.zig b/mirai/kagami/create/create.zig
new file mode 100644
index 0000000..8e7bad3
--- /dev/null
+++ b/mirai/kagami/create/create.zig
@@ -0,0 +1,71 @@
+//! Create Kagami
+
+const common = @import("../../../common/common.zig");
+const pmm = @import("../../pmm/pmm.zig");
+const types = @import("../types/types.zig");
+const state = @import("../state.zig");
+const tables = @import("../tables/tables.zig");
+
+const AllocationError = common.errors.memory.AllocationError;
+const Kagami = types.Kagami;
+const Table = types.Table;
+
+var kagami_pool: [256]Kagami = undefined;
+var kagami_pool_bitmap: [32]u8 = [_]u8{0} ** 32;
+
+pub fn create() AllocationError!*Kagami {
+ const pml4_physical = try pmm.allocate_page_zeroed();
+
+ const kagami = allocate_kagami_struct() orelse {
+ pmm.free_page(pml4_physical);
+ return AllocationError.OutOfMemory;
+ };
+
+ kagami.* = Kagami{
+ .pml4_physical = pml4_physical,
+ .reference_count = 1,
+ .resident_pages = 0,
+ .wired_pages = 0,
+ .table_pages = 1,
+ .lock = false,
+ };
+
+ const kernel_kagami = state.get_kernel_kagami();
+ const new_pml4 = tables.get_pml4(pml4_physical);
+ const kernel_pml4 = tables.get_pml4(kernel_kagami.pml4_physical);
+
+ new_pml4.copy_kernel_entries(kernel_pml4);
+
+ return kagami;
+}
+
+fn allocate_kagami_struct() ?*Kagami {
+ var index: usize = 0;
+ while (index < 256) : (index += 1) {
+ const byte_index = index / 8;
+ const bit_index: u3 = @truncate(index % 8);
+
+ if ((kagami_pool_bitmap[byte_index] & (@as(u8, 1) << bit_index)) == 0) {
+ kagami_pool_bitmap[byte_index] |= (@as(u8, 1) << bit_index);
+ return &kagami_pool[index];
+ }
+ }
+ return null;
+}
+
+pub fn free_kagami_struct(kagami: *Kagami) void {
+ const base_ptr: usize = @intFromPtr(&kagami_pool[0]);
+ const kagami_ptr: usize = @intFromPtr(kagami);
+
+ if (kagami_ptr < base_ptr) return;
+
+ const offset = kagami_ptr - base_ptr;
+ const index = offset / @sizeOf(Kagami);
+
+ if (index >= 256) return;
+
+ const byte_index = index / 8;
+ const bit_index: u3 = @truncate(index % 8);
+
+ kagami_pool_bitmap[byte_index] &= ~(@as(u8, 1) << bit_index);
+}
diff --git a/mirai/kagami/destroy/destroy.zig b/mirai/kagami/destroy/destroy.zig
new file mode 100644
index 0000000..e8d9a5e
--- /dev/null
+++ b/mirai/kagami/destroy/destroy.zig
@@ -0,0 +1,78 @@
+//! Destroy Kagami
+
+const pmm = @import("../../pmm/pmm.zig");
+const types = @import("../types/types.zig");
+const state = @import("../state.zig");
+const tables = @import("../tables/tables.zig");
+const create_module = @import("../create/create.zig");
+
+const Kagami = types.Kagami;
+const Table = types.Table;
+
+pub fn destroy(kagami: *Kagami) void {
+ if (kagami.is_kernel()) {
+ return;
+ }
+
+ const remaining = kagami.decrement_reference();
+ if (remaining > 0) {
+ return;
+ }
+
+ free_user_tables(kagami);
+
+ pmm.free_page(kagami.pml4_physical);
+
+ create_module.free_kagami_struct(kagami);
+}
+
+fn free_user_tables(kagami: *Kagami) void {
+ const pml4 = tables.get_pml4(kagami.pml4_physical);
+
+ var pml4_index: u9 = 0;
+ while (pml4_index < 256) : (pml4_index += 1) {
+ const pml4_entry = pml4.get_entry(pml4_index);
+ if (!pml4_entry.is_present()) continue;
+
+ const pdpt = tables.get_table_from_physical(pml4_entry.get_physical_address());
+ free_pdpt(pdpt);
+
+ pmm.free_page(pml4_entry.get_physical_address());
+ pml4_entry.clear();
+ }
+}
+
+fn free_pdpt(pdpt: *Table) void {
+ var pdpt_index: u9 = 0;
+ while (pdpt_index < 512) : (pdpt_index += 1) {
+ const pdpt_entry = pdpt.get_entry(pdpt_index);
+ if (!pdpt_entry.is_present()) continue;
+ if (pdpt_entry.is_huge()) continue;
+
+ const pd = tables.get_table_from_physical(pdpt_entry.get_physical_address());
+ free_pd(pd);
+
+ pmm.free_page(pdpt_entry.get_physical_address());
+ pdpt_entry.clear();
+ }
+}
+
+fn free_pd(pd: *Table) void {
+ var pd_index: u9 = 0;
+ while (pd_index < 512) : (pd_index += 1) {
+ const pd_entry = pd.get_entry(pd_index);
+ if (!pd_entry.is_present()) continue;
+ if (pd_entry.is_huge()) continue;
+
+ pmm.free_page(pd_entry.get_physical_address());
+ pd_entry.clear();
+ }
+}
+
+pub fn reference(kagami: *Kagami) void {
+ kagami.increment_reference();
+}
+
+pub fn release(kagami: *Kagami) void {
+ destroy(kagami);
+}
diff --git a/mirai/kagami/enter/enter.zig b/mirai/kagami/enter/enter.zig
new file mode 100644
index 0000000..e8e5441
--- /dev/null
+++ b/mirai/kagami/enter/enter.zig
@@ -0,0 +1,87 @@
+//! Enter Mapping (VA -> PA)
+
+const common = @import("../../../common/common.zig");
+const types = @import("../types/types.zig");
+const tables = @import("../tables/tables.zig");
+const constants = @import("../constants/constants.zig");
+const asm_cpu = @import("../../asm/cpu/cpu.zig");
+
+const paging_flags = common.constants.paging.flags;
+const MappingError = common.errors.memory.MappingError;
+const AllocationError = common.errors.memory.AllocationError;
+
+const Entry = types.Entry;
+const Kagami = types.Kagami;
+
+pub fn enter(
+ kagami: *Kagami,
+ virtual_address: u64,
+ physical_address: u64,
+ protection: u8,
+) (MappingError || AllocationError)!void {
+ if ((virtual_address & 0xFFF) != 0) {
+ return MappingError.AddressNotAligned;
+ }
+
+ if ((physical_address & 0xFFF) != 0) {
+ return MappingError.AddressNotAligned;
+ }
+
+ const entry = try tables.ensure_tables(kagami, virtual_address);
+
+ if (entry.is_present()) {
+ return MappingError.AlreadyMapped;
+ }
+
+ entry.* = build_entry(physical_address, protection);
+
+ kagami.add_resident();
+
+ if ((protection & constants.protection.wired) != 0) {
+ kagami.add_wired();
+ }
+
+ asm_cpu.invalidate_page(virtual_address);
+}
+
+pub fn enter_replace(
+ kagami: *Kagami,
+ virtual_address: u64,
+ physical_address: u64,
+ protection: u8,
+) (MappingError || AllocationError)!void {
+ if ((virtual_address & 0xFFF) != 0) {
+ return MappingError.AddressNotAligned;
+ }
+
+ if ((physical_address & 0xFFF) != 0) {
+ return MappingError.AddressNotAligned;
+ }
+
+ const entry = try tables.ensure_tables(kagami, virtual_address);
+
+ const was_present = entry.is_present();
+
+ entry.* = build_entry(physical_address, protection);
+
+ if (!was_present) {
+ kagami.add_resident();
+ }
+
+ asm_cpu.invalidate_page(virtual_address);
+}
+
+fn build_entry(physical_address: u64, protection: u8) Entry {
+ var entry = Entry{
+ .present = true,
+ .writable = (protection & constants.protection.write) != 0,
+ .user_accessible = (protection & constants.protection.user) != 0,
+ .cache_disabled = (protection & constants.protection.nocache) != 0,
+ .global = (protection & constants.protection.user) == 0,
+ .no_execute = (protection & constants.protection.execute) == 0,
+ };
+
+ entry.set_physical_address(physical_address);
+
+ return entry;
+}
diff --git a/mirai/kagami/extract/extract.zig b/mirai/kagami/extract/extract.zig
new file mode 100644
index 0000000..d57f140
--- /dev/null
+++ b/mirai/kagami/extract/extract.zig
@@ -0,0 +1,35 @@
+//! Extract Physical Address
+
+const types = @import("../types/types.zig");
+const tables = @import("../tables/tables.zig");
+
+const Entry = types.Entry;
+const Kagami = types.Kagami;
+
+pub fn extract(kagami: *const Kagami, virtual_address: u64) ?u64 {
+ const entry = tables.walk_to_entry(kagami.pml4_physical, virtual_address) orelse return null;
+
+ if (!entry.is_present()) {
+ return null;
+ }
+
+ const physical_base = entry.get_physical_address();
+ const offset = virtual_address & 0xFFF;
+
+ return physical_base | offset;
+}
+
+pub fn is_mapped(kagami: *const Kagami, virtual_address: u64) bool {
+ const entry = tables.walk_to_entry(kagami.pml4_physical, virtual_address) orelse return false;
+ return entry.is_present();
+}
+
+pub fn is_writable(kagami: *const Kagami, virtual_address: u64) bool {
+ const entry = tables.walk_to_entry(kagami.pml4_physical, virtual_address) orelse return false;
+ return entry.is_present() and entry.is_writable();
+}
+
+pub fn is_user_accessible(kagami: *const Kagami, virtual_address: u64) bool {
+ const entry = tables.walk_to_entry(kagami.pml4_physical, virtual_address) orelse return false;
+ return entry.is_present() and entry.is_user();
+}
diff --git a/mirai/kagami/kagami.zig b/mirai/kagami/kagami.zig
new file mode 100644
index 0000000..23c129b
--- /dev/null
+++ b/mirai/kagami/kagami.zig
@@ -0,0 +1,51 @@
+//! Kagami - Page Table Abstraction
+
+pub const constants = @import("constants/constants.zig");
+pub const types = @import("types/types.zig");
+pub const tables = @import("tables/tables.zig");
+pub const state = @import("state.zig");
+
+pub const create_module = @import("create/create.zig");
+pub const destroy_module = @import("destroy/destroy.zig");
+pub const enter_module = @import("enter/enter.zig");
+pub const remove_module = @import("remove/remove.zig");
+pub const protect_module = @import("protect/protect.zig");
+pub const extract_module = @import("extract/extract.zig");
+pub const activate_module = @import("activate/activate.zig");
+
+pub const Kagami = types.Kagami;
+pub const Entry = types.Entry;
+pub const Table = types.Table;
+
+pub const create = create_module.create;
+pub const destroy = destroy_module.destroy;
+pub const reference = destroy_module.reference;
+pub const release = destroy_module.release;
+
+pub const enter = enter_module.enter;
+pub const enter_replace = enter_module.enter_replace;
+
+pub const remove = remove_module.remove;
+pub const remove_range = remove_module.remove_range;
+
+pub const protect = protect_module.protect;
+pub const protect_range = protect_module.protect_range;
+
+pub const extract = extract_module.extract;
+pub const is_mapped = extract_module.is_mapped;
+pub const is_writable = extract_module.is_writable;
+pub const is_user_accessible = extract_module.is_user_accessible;
+
+pub const activate = activate_module.activate;
+pub const activate_kernel = activate_module.activate_kernel;
+pub const get_active_pml4 = activate_module.get_active_pml4;
+pub const is_active = activate_module.is_active;
+
+pub const kernel = state.get_kernel_kagami;
+pub const current = state.get_current_kagami;
+pub const is_initialized = state.is_initialized;
+
+pub fn initialize(pml4_physical: u64) void {
+ state.set_kernel_pml4(pml4_physical);
+ state.set_initialized();
+}
diff --git a/mirai/kagami/protect/protect.zig b/mirai/kagami/protect/protect.zig
new file mode 100644
index 0000000..fc08f18
--- /dev/null
+++ b/mirai/kagami/protect/protect.zig
@@ -0,0 +1,41 @@
+//! Change Protection
+
+const common = @import("../../../common/common.zig");
+const types = @import("../types/types.zig");
+const tables = @import("../tables/tables.zig");
+const constants = @import("../constants/constants.zig");
+const asm_cpu = @import("../../asm/cpu/cpu.zig");
+
+const MappingError = common.errors.memory.MappingError;
+const Entry = types.Entry;
+const Kagami = types.Kagami;
+
+pub fn protect(kagami: *Kagami, virtual_address: u64, protection: u8) MappingError!void {
+ const entry = tables.walk_to_entry(kagami.pml4_physical, virtual_address) orelse {
+ return MappingError.NotMapped;
+ };
+
+ if (!entry.is_present()) {
+ return MappingError.NotMapped;
+ }
+
+ entry.writable = (protection & constants.protection.write) != 0;
+ entry.user_accessible = (protection & constants.protection.user) != 0;
+ entry.cache_disabled = (protection & constants.protection.nocache) != 0;
+ entry.no_execute = (protection & constants.protection.execute) == 0;
+
+ asm_cpu.invalidate_page(virtual_address);
+}
+
+pub fn protect_range(kagami: *Kagami, start_address: u64, page_count: u64, protection: u8) u64 {
+ var protected_count: u64 = 0;
+ var offset: u64 = 0;
+
+ while (offset < page_count) : (offset += 1) {
+ const virtual_address = start_address + (offset * 4096);
+ protect(kagami, virtual_address, protection) catch continue;
+ protected_count += 1;
+ }
+
+ return protected_count;
+}
diff --git a/mirai/kagami/remove/remove.zig b/mirai/kagami/remove/remove.zig
new file mode 100644
index 0000000..1133f09
--- /dev/null
+++ b/mirai/kagami/remove/remove.zig
@@ -0,0 +1,40 @@
+//! Remove Mapping
+
+const types = @import("../types/types.zig");
+const tables = @import("../tables/tables.zig");
+const asm_cpu = @import("../../asm/cpu/cpu.zig");
+
+const Entry = types.Entry;
+const Kagami = types.Kagami;
+
+pub fn remove(kagami: *Kagami, virtual_address: u64) ?u64 {
+ const entry = tables.walk_to_entry(kagami.pml4_physical, virtual_address) orelse return null;
+
+ if (!entry.is_present()) {
+ return null;
+ }
+
+ const physical_address = entry.get_physical_address();
+
+ entry.clear();
+
+ kagami.remove_resident();
+
+ asm_cpu.invalidate_page(virtual_address);
+
+ return physical_address;
+}
+
+pub fn remove_range(kagami: *Kagami, start_address: u64, page_count: u64) u64 {
+ var removed_count: u64 = 0;
+ var offset: u64 = 0;
+
+ while (offset < page_count) : (offset += 1) {
+ const virtual_address = start_address + (offset * 4096);
+ if (remove(kagami, virtual_address) != null) {
+ removed_count += 1;
+ }
+ }
+
+ return removed_count;
+}
diff --git a/mirai/kagami/state.zig b/mirai/kagami/state.zig
new file mode 100644
index 0000000..86a77ed
--- /dev/null
+++ b/mirai/kagami/state.zig
@@ -0,0 +1,41 @@
+//! Kagami State
+
+const types = @import("types/types.zig");
+const Kagami = types.Kagami;
+
+var kernel_kagami: Kagami = .{
+ .pml4_physical = 0,
+ .reference_count = 1,
+ .resident_pages = 0,
+ .wired_pages = 0,
+ .table_pages = 0,
+ .lock = false,
+};
+
+var current_kagami: *Kagami = &kernel_kagami;
+
+var initialized: bool = false;
+
+pub fn get_kernel_kagami() *Kagami {
+ return &kernel_kagami;
+}
+
+pub fn get_current_kagami() *Kagami {
+ return current_kagami;
+}
+
+pub fn set_current_kagami(kagami: *Kagami) void {
+ current_kagami = kagami;
+}
+
+pub fn set_kernel_pml4(pml4_physical: u64) void {
+ kernel_kagami.pml4_physical = pml4_physical;
+}
+
+pub fn is_initialized() bool {
+ return initialized;
+}
+
+pub fn set_initialized() void {
+ initialized = true;
+}
diff --git a/mirai/kagami/tables/allocate.zig b/mirai/kagami/tables/allocate.zig
new file mode 100644
index 0000000..6526bae
--- /dev/null
+++ b/mirai/kagami/tables/allocate.zig
@@ -0,0 +1,100 @@
+//! Page Table Allocation
+
+const common = @import("../../../common/common.zig");
+const pmm = @import("../../pmm/pmm.zig");
+const types = @import("../types/types.zig");
+const walk = @import("walk.zig");
+
+const paging = common.constants.paging;
+const memory = common.constants.memory;
+const AllocationError = common.errors.memory.AllocationError;
+
+const Entry = types.Entry;
+const Table = types.Table;
+const Kagami = types.Kagami;
+
+pub fn allocate_table() AllocationError!u64 {
+ const physical_address = try pmm.allocate_page_zeroed();
+ return physical_address;
+}
+
+pub fn free_table(physical_address: u64) void {
+ pmm.free_page(physical_address);
+}
+
+pub fn ensure_pdpt(kagami: *Kagami, virtual_address: u64) AllocationError!*Table {
+ const pml4 = walk.get_pml4(kagami.pml4_physical);
+ const pml4_index = paging.indices.extract_pml4_index(virtual_address);
+ const entry = pml4.get_entry(pml4_index);
+
+ if (entry.is_present()) {
+ return walk.get_table_from_physical(entry.get_physical_address());
+ }
+
+ const new_table_physical = try allocate_table();
+ kagami.add_table();
+
+ entry.* = Entry{
+ .present = true,
+ .writable = true,
+ .user_accessible = (pml4_index < 256),
+ };
+ entry.set_physical_address(new_table_physical);
+
+ return walk.get_table_from_physical(new_table_physical);
+}
+
+pub fn ensure_pd(kagami: *Kagami, pdpt: *Table, virtual_address: u64) AllocationError!*Table {
+ const pdpt_index = paging.indices.extract_pdpt_index(virtual_address);
+ const entry = pdpt.get_entry(pdpt_index);
+
+ if (entry.is_present()) {
+ return walk.get_table_from_physical(entry.get_physical_address());
+ }
+
+ const new_table_physical = try allocate_table();
+ kagami.add_table();
+
+ const pml4_index = paging.indices.extract_pml4_index(virtual_address);
+
+ entry.* = Entry{
+ .present = true,
+ .writable = true,
+ .user_accessible = (pml4_index < 256),
+ };
+ entry.set_physical_address(new_table_physical);
+
+ return walk.get_table_from_physical(new_table_physical);
+}
+
+pub fn ensure_pt(kagami: *Kagami, pd: *Table, virtual_address: u64) AllocationError!*Table {
+ const pd_index = paging.indices.extract_pd_index(virtual_address);
+ const entry = pd.get_entry(pd_index);
+
+ if (entry.is_present()) {
+ return walk.get_table_from_physical(entry.get_physical_address());
+ }
+
+ const new_table_physical = try allocate_table();
+ kagami.add_table();
+
+ const pml4_index = paging.indices.extract_pml4_index(virtual_address);
+
+ entry.* = Entry{
+ .present = true,
+ .writable = true,
+ .user_accessible = (pml4_index < 256),
+ };
+ entry.set_physical_address(new_table_physical);
+
+ return walk.get_table_from_physical(new_table_physical);
+}
+
+pub fn ensure_tables(kagami: *Kagami, virtual_address: u64) AllocationError!*Entry {
+ const pdpt = try ensure_pdpt(kagami, virtual_address);
+ const pd = try ensure_pd(kagami, pdpt, virtual_address);
+ const pt = try ensure_pt(kagami, pd, virtual_address);
+
+ const pt_index = paging.indices.extract_pt_index(virtual_address);
+ return pt.get_entry(pt_index);
+}
diff --git a/mirai/kagami/tables/tables.zig b/mirai/kagami/tables/tables.zig
new file mode 100644
index 0000000..2ee664a
--- /dev/null
+++ b/mirai/kagami/tables/tables.zig
@@ -0,0 +1,18 @@
+//! Page Table Operations
+
+pub const walk = @import("walk.zig");
+pub const allocate = @import("allocate.zig");
+
+pub const get_table_from_physical = walk.get_table_from_physical;
+pub const get_pml4 = walk.get_pml4;
+pub const get_pdpt = walk.get_pdpt;
+pub const get_pd = walk.get_pd;
+pub const get_pt = walk.get_pt;
+pub const walk_to_entry = walk.walk_to_entry;
+
+pub const allocate_table = allocate.allocate_table;
+pub const free_table = allocate.free_table;
+pub const ensure_pdpt = allocate.ensure_pdpt;
+pub const ensure_pd = allocate.ensure_pd;
+pub const ensure_pt = allocate.ensure_pt;
+pub const ensure_tables = allocate.ensure_tables;
diff --git a/mirai/kagami/tables/walk.zig b/mirai/kagami/tables/walk.zig
new file mode 100644
index 0000000..eeb37e7
--- /dev/null
+++ b/mirai/kagami/tables/walk.zig
@@ -0,0 +1,75 @@
+//! Page Table Walking
+
+const common = @import("../../../common/common.zig");
+const types = @import("../types/types.zig");
+
+const paging = common.constants.paging;
+const memory = common.constants.memory;
+
+const Entry = types.Entry;
+const Table = types.Table;
+
+pub fn get_table_from_physical(physical_address: u64) *Table {
+ const virtual_address = physical_address + memory.layout.physmap_base;
+ return @ptrFromInt(virtual_address);
+}
+
+pub fn get_pml4(pml4_physical: u64) *Table {
+ return get_table_from_physical(pml4_physical);
+}
+
+pub fn get_pdpt(pml4: *Table, virtual_address: u64) ?*Table {
+ const pml4_index = paging.indices.extract_pml4_index(virtual_address);
+ const entry = pml4.get_entry(pml4_index);
+
+ if (!entry.is_present()) {
+ return null;
+ }
+
+ return get_table_from_physical(entry.get_physical_address());
+}
+
+pub fn get_pd(pdpt: *Table, virtual_address: u64) ?*Table {
+ const pdpt_index = paging.indices.extract_pdpt_index(virtual_address);
+ const entry = pdpt.get_entry(pdpt_index);
+
+ if (!entry.is_present()) {
+ return null;
+ }
+
+ if (entry.is_huge()) {
+ return null;
+ }
+
+ return get_table_from_physical(entry.get_physical_address());
+}
+
+pub fn get_pt(pd: *Table, virtual_address: u64) ?*Table {
+ const pd_index = paging.indices.extract_pd_index(virtual_address);
+ const entry = pd.get_entry(pd_index);
+
+ if (!entry.is_present()) {
+ return null;
+ }
+
+ if (entry.is_huge()) {
+ return null;
+ }
+
+ return get_table_from_physical(entry.get_physical_address());
+}
+
+pub fn get_page_entry(pt: *Table, virtual_address: u64) *Entry {
+ const pt_index = paging.indices.extract_pt_index(virtual_address);
+ return pt.get_entry(pt_index);
+}
+
+pub fn walk_to_entry(pml4_physical: u64, virtual_address: u64) ?*Entry {
+ const pml4 = get_pml4(pml4_physical);
+
+ const pdpt = get_pdpt(pml4, virtual_address) orelse return null;
+ const pd = get_pd(pdpt, virtual_address) orelse return null;
+ const pt = get_pt(pd, virtual_address) orelse return null;
+
+ return get_page_entry(pt, virtual_address);
+}
diff --git a/mirai/kagami/types/entry.zig b/mirai/kagami/types/entry.zig
new file mode 100644
index 0000000..5fc7a8d
--- /dev/null
+++ b/mirai/kagami/types/entry.zig
@@ -0,0 +1,56 @@
+//! Page Table Entry Type
+
+const common = @import("../../../common/common.zig");
+const paging_flags = common.constants.paging.flags;
+
+pub const Entry = packed struct {
+ present: bool = false,
+ writable: bool = false,
+ user_accessible: bool = false,
+ write_through: bool = false,
+ cache_disabled: bool = false,
+ accessed: bool = false,
+ dirty: bool = false,
+ huge_page: bool = false,
+ global: bool = false,
+ available_low: u3 = 0,
+ physical_address: u40 = 0,
+ available_high: u11 = 0,
+ no_execute: bool = false,
+
+ pub fn is_present(self: Entry) bool {
+ return self.present;
+ }
+
+ pub fn is_writable(self: Entry) bool {
+ return self.writable;
+ }
+
+ pub fn is_user(self: Entry) bool {
+ return self.user_accessible;
+ }
+
+ pub fn is_huge(self: Entry) bool {
+ return self.huge_page;
+ }
+
+ pub fn get_physical_address(self: Entry) u64 {
+ return @as(u64, self.physical_address) << 12;
+ }
+
+ pub fn set_physical_address(self: *Entry, address: u64) void {
+ self.physical_address = @truncate(address >> 12);
+ }
+
+ pub fn clear(self: *Entry) void {
+ self.* = Entry{};
+ }
+
+ pub fn from_raw(raw: u64) Entry {
+ return @bitCast(raw);
+ }
+
+ pub fn to_raw(self: Entry) u64 {
+ return @bitCast(self);
+ }
+};
diff --git a/mirai/kagami/types/kagami.zig b/mirai/kagami/types/kagami.zig
new file mode 100644
index 0000000..f89863f
--- /dev/null
+++ b/mirai/kagami/types/kagami.zig
@@ -0,0 +1,56 @@
+//! Kagami Structure
+
+pub const Kagami = struct {
+ pml4_physical: u64,
+ reference_count: u32,
+ resident_pages: u64,
+ wired_pages: u64,
+ table_pages: u64,
+ lock: bool,
+
+ pub fn is_kernel(self: *const Kagami) bool {
+ const kernel_kagami = @import("../state.zig").get_kernel_kagami();
+ return self.pml4_physical == kernel_kagami.pml4_physical;
+ }
+
+ pub fn increment_reference(self: *Kagami) void {
+ self.reference_count += 1;
+ }
+
+ pub fn decrement_reference(self: *Kagami) u32 {
+ if (self.reference_count > 0) {
+ self.reference_count -= 1;
+ }
+ return self.reference_count;
+ }
+
+ pub fn add_resident(self: *Kagami) void {
+ self.resident_pages += 1;
+ }
+
+ pub fn remove_resident(self: *Kagami) void {
+ if (self.resident_pages > 0) {
+ self.resident_pages -= 1;
+ }
+ }
+
+ pub fn add_wired(self: *Kagami) void {
+ self.wired_pages += 1;
+ }
+
+ pub fn remove_wired(self: *Kagami) void {
+ if (self.wired_pages > 0) {
+ self.wired_pages -= 1;
+ }
+ }
+
+ pub fn add_table(self: *Kagami) void {
+ self.table_pages += 1;
+ }
+
+ pub fn remove_table(self: *Kagami) void {
+ if (self.table_pages > 0) {
+ self.table_pages -= 1;
+ }
+ }
+};
diff --git a/mirai/kagami/types/table.zig b/mirai/kagami/types/table.zig
new file mode 100644
index 0000000..33a072c
--- /dev/null
+++ b/mirai/kagami/types/table.zig
@@ -0,0 +1,38 @@
+//! Page Table Type
+
+const Entry = @import("entry.zig").Entry;
+
+pub const Table = struct {
+ entries: [512]Entry,
+
+ pub fn get_entry(self: *Table, index: u9) *Entry {
+ return &self.entries[index];
+ }
+
+ pub fn get_entry_const(self: *const Table, index: u9) *const Entry {
+ return &self.entries[index];
+ }
+
+ pub fn clear_all(self: *Table) void {
+ for (&self.entries) |*entry| {
+ entry.clear();
+ }
+ }
+
+ pub fn copy_kernel_entries(self: *Table, source: *const Table) void {
+ var index: usize = 256;
+ while (index < 512) : (index += 1) {
+ self.entries[index] = source.entries[index];
+ }
+ }
+
+ pub fn count_present(self: *const Table) u32 {
+ var count: u32 = 0;
+ for (self.entries) |entry| {
+ if (entry.is_present()) {
+ count += 1;
+ }
+ }
+ return count;
+ }
+};
diff --git a/mirai/kagami/types/types.zig b/mirai/kagami/types/types.zig
new file mode 100644
index 0000000..a333b86
--- /dev/null
+++ b/mirai/kagami/types/types.zig
@@ -0,0 +1,9 @@
+//! Kagami Types
+
+pub const entry = @import("entry.zig");
+pub const table = @import("table.zig");
+pub const kagami = @import("kagami.zig");
+
+pub const Entry = entry.Entry;
+pub const Table = table.Table;
+pub const Kagami = kagami.Kagami;