added physical memory manager
This commit is contained in:
+13
-7
@@ -262,7 +262,7 @@ fn convertMemoryMap(map: MemoryMapSlice, out: []u8) danos.MemoryMap {
|
||||
const d: *const uefi.tables.MemoryDescriptor =
|
||||
@ptrCast(@alignCast(map.ptr + i * map.info.descriptor_size));
|
||||
if (d.number_of_pages == 0) continue;
|
||||
const kind = classify(d.@"type");
|
||||
const kind = classify(d);
|
||||
|
||||
// Coalesce with the previous region if it's the same kind and contiguous.
|
||||
if (count > 0) {
|
||||
@@ -284,16 +284,22 @@ fn convertMemoryMap(map: MemoryMapSlice, out: []u8) danos.MemoryMap {
|
||||
return .{ .regions = @intFromPtr(regions), .len = count };
|
||||
}
|
||||
|
||||
/// Map a UEFI memory type to danos's neutral kind. Anything we don't explicitly
|
||||
/// recognise is treated as `reserved` — the safe default. Our own loader data
|
||||
/// (the kernel image, these buffers) is LoaderData, which falls here too and so
|
||||
/// stays reserved until the kernel decides to reclaim it.
|
||||
fn classify(t: uefi.tables.MemoryType) danos.MemoryKind {
|
||||
return switch (t) {
|
||||
/// Map a UEFI descriptor to danos's neutral kind. A region that isn't
|
||||
/// writeback-cacheable (`wb`) isn't backed by real RAM — it's device registers or
|
||||
/// a reserved address-space window (e.g. PCIe config space) — so it's `mmio`
|
||||
/// regardless of type. UEFI overloads `reserved_memory_type` for both reserved RAM
|
||||
/// and such holes, and the cache attribute is what actually tells them apart.
|
||||
/// Among RAM regions, anything we don't recognise is `reserved` — the safe
|
||||
/// default; our own LoaderData (kernel image, these buffers) lands there too and
|
||||
/// stays reserved until the kernel reclaims it.
|
||||
fn classify(d: *const uefi.tables.MemoryDescriptor) danos.MemoryKind {
|
||||
if (!d.attribute.wb) return .mmio;
|
||||
return switch (d.@"type") {
|
||||
.conventional_memory => .usable,
|
||||
.boot_services_code, .boot_services_data => .reclaimable,
|
||||
.acpi_reclaim_memory => .acpi_tables,
|
||||
.acpi_memory_nvs => .acpi_nvs,
|
||||
.memory_mapped_io, .memory_mapped_io_port_space => .mmio,
|
||||
else => .reserved,
|
||||
};
|
||||
}
|
||||
|
||||
+38
-3
@@ -40,11 +40,41 @@ fn kmain(boot_info: *const BootInfo) noreturn {
|
||||
// own MemoryRegion, so this is a plain slice — no firmware layout in sight.
|
||||
const regions = @as([*]const danos.MemoryRegion, @ptrFromInt(boot_info.memory_map.regions))[0..boot_info.memory_map.len];
|
||||
var usable_pages: u64 = 0;
|
||||
var reclaim_pages: u64 = 0;
|
||||
var reserved_pages: u64 = 0; // reserved RAM only — MMIO is device space, not RAM
|
||||
for (regions) |r| {
|
||||
if (r.kind == .usable) usable_pages += r.pages;
|
||||
switch (r.kind) {
|
||||
.usable => usable_pages += r.pages,
|
||||
.reclaimable => reclaim_pages += r.pages,
|
||||
.reserved, .acpi_tables, .acpi_nvs => reserved_pages += r.pages,
|
||||
.mmio => {},
|
||||
}
|
||||
}
|
||||
con.print(" mem regions: {d}\n", .{regions.len});
|
||||
con.print(" usable RAM : {d} MiB\n", .{usable_pages * danos.page_size / (1024 * 1024)});
|
||||
const total_pages = usable_pages + reclaim_pages + reserved_pages;
|
||||
const total_bytes = total_pages * danos.page_size;
|
||||
const gib = 1 << 30;
|
||||
|
||||
con.write("\ndanos: physical memory\n");
|
||||
con.print(" total RAM : {d}.{d:0>2} GiB ({d} MiB) - RAM the firmware reported\n", .{ total_bytes / gib, (total_bytes % gib) * 100 / gib, mib(total_pages) });
|
||||
con.print(" usable : {d} MiB - free now; owned by the frame allocator\n", .{mib(usable_pages)});
|
||||
con.print(" reclaimable: {d} MiB - UEFI boot-services memory, free after exit\n", .{mib(reclaim_pages)});
|
||||
con.print(" reserved : {d} MiB - kernel image, ACPI, runtime services\n", .{mib(reserved_pages)});
|
||||
con.print(" regions : {d} - entries in the firmware memory map\n", .{regions.len});
|
||||
|
||||
// Bring up the physical frame allocator over that map, and prove it works:
|
||||
// allocate three frames, then hand them back.
|
||||
pmm.init(boot_info.memory_map);
|
||||
const s = pmm.stats();
|
||||
con.print("\ndanos: frame allocator online\n", .{});
|
||||
con.print(" free frames: {d} ({d} MiB)\n", .{ s.free_frames, mib(s.free_frames) });
|
||||
const f0 = pmm.alloc();
|
||||
const f1 = pmm.alloc();
|
||||
const f2 = pmm.alloc();
|
||||
con.print(" alloc x3 : 0x{x} 0x{x} 0x{x}\n", .{ f0 orelse 0, f1 orelse 0, f2 orelse 0 });
|
||||
if (f0) |p| pmm.free(p);
|
||||
if (f1) |p| pmm.free(p);
|
||||
if (f2) |p| pmm.free(p);
|
||||
con.print(" after free : {d} frames free\n", .{pmm.stats().free_frames});
|
||||
|
||||
// Bring up the physical frame allocator over that map, and prove it works:
|
||||
// allocate three frames, then hand them back.
|
||||
@@ -66,6 +96,11 @@ fn kmain(boot_info: *const BootInfo) noreturn {
|
||||
arch.halt();
|
||||
}
|
||||
|
||||
/// Frames (4 KiB pages) to whole MiB.
|
||||
fn mib(pages: u64) u64 {
|
||||
return pages * danos.page_size / (1024 * 1024);
|
||||
}
|
||||
|
||||
/// Freestanding has no OS to receive a panic. Print it to the console (if it is
|
||||
/// up yet) in red, then halt.
|
||||
pub const panic = std.debug.FullPanic(struct {
|
||||
|
||||
@@ -52,6 +52,10 @@ pub const MemoryKind = enum(u32) {
|
||||
acpi_tables,
|
||||
/// ACPI non-volatile storage: preserve across sleep, do not allocate.
|
||||
acpi_nvs,
|
||||
/// Not backed by RAM: memory-mapped device registers or a reserved
|
||||
/// address-space window (e.g. PCIe config space). Kept distinct from
|
||||
/// `reserved` so RAM accounting doesn't count device address space.
|
||||
mmio,
|
||||
};
|
||||
|
||||
/// One contiguous span of physical memory. Because danos defines this layout
|
||||
|
||||
Reference in New Issue
Block a user