phys: make reserved/available regions operate on PFNs

Use PFNs instead of raw physical addresses for more clarity.
This commit is contained in:
2025-03-24 10:14:41 +02:00
parent 23bb7bb63e
commit bc91b5c07c
7 changed files with 70 additions and 52 deletions
+2
View File
@@ -10,6 +10,8 @@ pub const impl = switch (builtin.cpu.arch) {
else => @compileError("Unsupported architecture"),
};
pub const vmm = impl.vmm;
/// Halts the CPU execution indefinitely, without ever returning.
pub inline fn halt() noreturn {
impl.halt();
+2
View File
@@ -3,6 +3,8 @@ const std = @import("std");
const boot = @import("aarch64/boot.zig");
const regs = @import("aarch64/regs.zig");
pub const vmm = @import("aarch64/vmm.zig");
export const _ = boot.aa64_bsp_lower_entry;
pub const Context = @import("aarch64/context.zig").Context;
+1 -1
View File
@@ -8,7 +8,7 @@ pub const KERNEL_L1_INDEX: usize = L1.index(KERNEL_VIRTUAL_BASE);
pub const L1 = mem.TranslationLevel(30);
pub const L2 = mem.TranslationLevel(21);
pub const L3 = mem.TranslationLevel(12);
pub const L3 = mem.vmm.L3;
pub const RawEntry = packed struct(u64) {
// 0
+2
View File
@@ -5,6 +5,8 @@ const regs = @import("riscv64/regs.zig");
const std = @import("std");
const builtin = @import("builtin");
pub const vmm = @import("riscv64/vmm.zig");
export const _ = boot.rv64_bsp_lower_entry;
/// This CPU's HART (HARdware Thread) ID.
+1 -1
View File
@@ -12,7 +12,7 @@ const EARLY_MAPPING_SIZE: usize = 16;
pub const L1 = mem.TranslationLevel(30);
pub const L2 = mem.TranslationLevel(21);
pub const L3 = mem.TranslationLevel(12);
pub const L3 = mem.vmm.L3;
pub const RawEntry = packed struct(u64) {
// 0: Valid
+48 -49
View File
@@ -15,12 +15,12 @@ const Spinlock = sync.Spinlock;
pub const MemoryRegion = struct {
/// Name string, used to represent where the memory comes from.
name: []const u8,
/// Byte range of the memory region.
/// Page frame number range of the region.
range: Range(u64),
};
/// Represents information about a single managed physical memory page.
pub const Page = packed struct (u128) {
pub const Page = packed struct(u128) {
unused: @Vector(4, u32),
};
@@ -52,28 +52,28 @@ const Bitmap = struct {
};
const PhysicalMemoryManager = struct {
page_array: []Page,
memory_start: u64,
last_free: usize,
len: usize,
/// Each bit represents a page, there can be more u64s than needed
usage_bitmap: Bitmap,
page_refcounters: []u32,
const empty: @This() = .{
.page_array = &.{},
.memory_start = 0,
.last_free = 0,
.len = 0,
.usage_bitmap = .empty,
.page_refcounters = &.{},
};
fn alloc_page(self: *@This()) ?mem.PhysicalAddress {
for (self.last_free..self.page_array.len) |i| {
for (self.last_free..self.len) |i| {
if (!self.is_page_used(i)) {
self.page_refcounters[i] += 1;
self.set_page_used(i);
self.last_free = (i + 1) % self.page_array.len;
self.last_free = (i + 1) % self.len;
return .{ .raw = self.memory_start + i * vmm.PAGE_SIZE };
}
}
@@ -81,7 +81,7 @@ const PhysicalMemoryManager = struct {
if (!self.is_page_used(i)) {
self.page_refcounters[i] += 1;
self.set_page_used(i);
self.last_free = (i + 1) % self.page_array.len;
self.last_free = (i + 1) % self.len;
return .{ .raw = self.memory_start + i * vmm.PAGE_SIZE };
}
}
@@ -89,8 +89,8 @@ const PhysicalMemoryManager = struct {
}
fn alloc_pages(self: *@This(), count: usize) ?mem.PhysicalAddress {
if (self.last_free + count < self.page_array.len) {
if (self.alloc_from(self.last_free, self.page_array.len, count)) |p| {
if (self.last_free + count < self.len) {
if (self.alloc_from(self.last_free, self.len, count)) |p| {
return p;
}
}
@@ -123,7 +123,7 @@ const PhysicalMemoryManager = struct {
log.panic("free_page: invalid page 0x{x}: outside of the allocation range", .{page.raw});
}
const index = (page.raw - self.memory_start) / vmm.PAGE_SIZE;
if (index >= self.page_array.len) {
if (index >= self.len) {
log.panic("free_page: invalid page 0x{x}: outside of the allocation range", .{page.raw});
}
return index;
@@ -141,11 +141,6 @@ const PhysicalMemoryManager = struct {
}
}
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 {
return self.usage_bitmap.get_bit(index) == 1;
}
@@ -166,30 +161,45 @@ var g_physical_memory = PhysicalMemoryManager.empty;
/// Adds an available memory region to the list.
///
/// `base` and `size` are in bytes. Regions are page-aligned "inwards", meaning the function will
/// only add the range of full pages of the specified region. If a combination is provided that
/// does not yield any full 4KiB pages (e.g. `base=0x1234, size=0x123`), it is ignored.
///
/// # Note
///
/// Only meaningful to call before calling `init()`.
pub fn add_memory_region(name: []const u8, base: u64, size: u64) void {
log.info("Memory: '{s}', base 0x{x}, size 0x{x}", .{ name, base, size });
g_memory_regions.append(.{ .name = name, .range = .{ .start = base, .len = size } }) //
catch @panic("memory regions overflow");
const start = vmm.L3.align_up(base) / vmm.L3.SIZE;
const len = vmm.L3.align_down(base + size) / vmm.L3.SIZE - start;
if (len > 0) {
g_memory_regions.append(.{ .name = name, .range = .{ .start = start, .len = len } }) //
catch @panic("memory regions overflow");
}
}
/// Adds an reserved memory region to the list.
///
/// `base` and `size` are in bytes. Regions are page-aligned "outwards", meaning that the
/// reservation extends to any pages affected by the specified region.
///
/// # Note
///
/// Only meaningful to call before calling `init()`.
pub fn add_reserved_region(name: []const u8, base: u64, size: u64) void {
log.info("Reserved: '{s}', base 0x{x}, size 0x{x}", .{ name, base, size });
g_reserved_regions.append(.{ .name = name, .range = .{ .start = base, .len = size } }) //
catch @panic("reserved regions overflow");
const start = base / vmm.L3.SIZE;
const len = vmm.L3.align_up(base + size) / vmm.L3.SIZE - start;
if (len > 0) {
g_reserved_regions.append(.{ .name = name, .range = .{ .start = start, .len = len } }) //
catch @panic("reserved regions overflow");
}
}
fn is_reserved_in(page: u64) ?*const MemoryRegion {
fn is_reserved_in(page_index: u64) ?*const MemoryRegion {
for (0..g_reserved_regions.len) |i| {
const region = &g_reserved_regions.buffer[i];
if (page >= region.range.start and page < region.range.end()) {
if (page_index >= region.range.start and page_index < region.range.end()) {
return region;
}
}
@@ -201,7 +211,7 @@ fn alloc_from_region(region: *const MemoryRegion, reason: []const u8, page_count
while (offset < region.range.len) {
var taken: ?*const MemoryRegion = null;
for (0..page_count) |i| {
if (is_reserved_in(region.range.start + offset + i * vmm.PAGE_SIZE)) |resv| {
if (is_reserved_in(region.range.start + offset + i)) |resv| {
taken = resv;
break;
}
@@ -212,7 +222,7 @@ fn alloc_from_region(region: *const MemoryRegion, reason: []const u8, page_count
continue;
}
const base = region.range.start + offset;
const base = (region.range.start + offset) * vmm.L3.SIZE;
add_reserved_region(reason, base, page_count * vmm.PAGE_SIZE);
return base;
}
@@ -224,8 +234,7 @@ fn alloc_slice_pages(comptime T: type, reason: []const u8, page_count: usize) []
for (g_memory_regions.constSlice()) |region| {
if (alloc_from_region(&region, reason, page_count)) |physAddress| {
const vaddr = (mem.PhysicalAddress{ .raw = physAddress }).virtualize();
const items_per_page = vmm.PAGE_SIZE / @sizeOf(T);
const len = page_count * items_per_page;
const len = (page_count * vmm.PAGE_SIZE) / @sizeOf(T);
const ptr: [*]T = @ptrFromInt(vaddr);
const slice: []T = ptr[0..len];
return slice;
@@ -239,7 +248,7 @@ fn alloc_slice_pages(comptime T: type, reason: []const u8, page_count: usize) []
fn alloc_slice(comptime T: type, reason: []const u8, min_len: usize) []T {
const min_alloc_bytes = min_len * @sizeOf(T);
// Round up to make sure we have enough space for the data
const needed_pages = (min_alloc_bytes + vmm.PAGE_SIZE - 1) / vmm.PAGE_SIZE;
const needed_pages = vmm.L3.page_count(min_alloc_bytes);
const slice = alloc_slice_pages(T, reason, needed_pages);
const slice_as_bytes = std.mem.sliceAsBytes(slice);
@memset(slice_as_bytes, 0);
@@ -272,6 +281,7 @@ pub fn init() void {
var memory_end: u64 = std.math.minInt(u64);
for (g_memory_regions.constSlice()) |region| {
log.info("Region: {}..{}", .{ region.range.start, region.range.end() });
if (region.range.start < memory_start) {
memory_start = region.range.start;
}
@@ -280,24 +290,23 @@ pub fn init() void {
}
}
const memory_pages = (memory_end - memory_start) / vmm.PAGE_SIZE; // == bitmap bits required
const memory_pages = memory_end - memory_start; // == bitmap bits required
var bitmap = alloc_bitmap(memory_pages);
const page_array = alloc_slice(Page, "page-array", memory_pages);
const refcounters = alloc_refcounters(memory_pages);
var available_pages: usize = 0;
for (g_memory_regions.constSlice()) |region| {
const offset = (region.range.start - memory_start) / vmm.PAGE_SIZE;
for (0..region.range.len / vmm.PAGE_SIZE) |i| {
const offset = region.range.start - memory_start;
for (0..region.range.len) |i| {
refcounters[offset + i] = 0;
available_pages += 1;
}
}
for (g_reserved_regions.constSlice()) |region| {
const offset = (region.range.start - memory_start) / vmm.PAGE_SIZE;
for (0..region.range.len / vmm.PAGE_SIZE) |i| {
if (offset + i >= page_array.len) {
const offset = region.range.start - memory_start;
for (0..region.range.len) |i| {
if (offset + i >= memory_pages) {
break;
}
refcounters[offset + i] = std.math.maxInt(u32);
@@ -309,10 +318,13 @@ pub fn init() void {
var size_fmt: [64]u8 = undefined;
const size_fmt_str = mem.format_size(&size_fmt, available_pages * vmm.PAGE_SIZE);
log.info("Available memory: {s}, page array {*}", .{ size_fmt_str, page_array });
log.info(
"Available memory: {s}, bitmap {*}, refcounts {*}",
.{ size_fmt_str, bitmap.data, refcounters },
);
g_physical_memory.page_array = page_array;
g_physical_memory.memory_start = memory_start;
g_physical_memory.len = memory_pages;
g_physical_memory.memory_start = memory_start * vmm.L3.SIZE;
g_physical_memory.usage_bitmap = bitmap;
g_physical_memory.page_refcounters = refcounters;
}
@@ -366,16 +378,3 @@ pub fn free_page(page: mem.PhysicalAddress) void {
defer guard.release();
g_physical_memory.free_page(page);
}
/// Returns a `Page` struct representing the given `page`.
///
/// # Invariants
///
/// The physical memory lock must be held.
///
/// # Panics
///
/// Will panic if the `page` does not represent a valid managed page.
pub fn get_page(page: mem.PhysicalAddress) *Page {
return g_physical_memory.get_page(page);
}
+14 -1
View File
@@ -1,7 +1,12 @@
//! Platform-independent virtual memory management definitions.
const mem = @import("../mem.zig");
/// Last virtual memory translation level. Always 4KiB on all platforms.
pub const L3 = mem.TranslationLevel(12);
/// Page size is 4KiB on all platforms.
pub const PAGE_SIZE: usize = 0x1000;
pub const PAGE_SIZE: usize = L3.SIZE;
/// Helper function to construct a "Translation Level" struct type from a bit shift.
pub fn TranslationLevel(comptime shift: usize) type {
@@ -28,5 +33,13 @@ pub fn TranslationLevel(comptime shift: usize) type {
pub inline fn align_up(addr: usize) usize {
return (addr + SIZE - 1) & ~(SIZE - 1);
}
pub inline fn page_number(addr: usize) usize {
return addr >> shift;
}
pub inline fn page_count(size: usize) usize {
return (size + SIZE - 1) / SIZE;
}
};
}