Microkernel thing OS experiment (Zig ⚡)
1/// Simple Multi Bump Allocator, because the old buddy system wasn't
2/// suitable to pass memory down to the root task, and we never
3/// have to free memory
4const std = @import("std");
5const common = @import("../root.zig");
6const arch = @import("arch");
7const log = std.log.scoped(.bootmem);
8
9/// This is meant for pageframe allocation, does bump allocation with
10/// a first fit from the end. Ideally we do as little memory allocation
11/// as possible in the microkernel anyways.
12pub const BootPmm = struct {
13 // Store this in a chunk of ram which you remove from the region
14 regions: []Region = undefined,
15 //top_region: usize = undefined,
16
17 const Self = @This();
18 pub const Region = struct {
19 base: usize,
20 length: usize,
21 type: RegionType,
22 };
23 pub const RegionType = enum(usize) {
24 usable = 0,
25 acpi_reclaimable = 1,
26 bootloader_reclaimable = 2,
27 executable_and_modules = 3,
28 framebuffer = 4,
29 // Just forget about reserved regions for now
30 reserved = 5,
31 };
32
33 // Calculate the needed size to allocate the []Region slice
34 pub fn calculateMetadataSize(entries: usize) usize {
35 return entries * @sizeOf(Region);
36 }
37
38 /// For the love of god len(regions) must be > 0
39 /// Only call this function once
40 pub fn initialize(self: *Self, regions: []Region) void {
41 self.regions = regions;
42 //self.top_region = regions.len - 1;
43 }
44
45 /// Allocates physical memory, aligned to page size
46 pub fn allocPhys(self: *Self, size: usize) !usize {
47 if (self.regions.len == 0) {
48 @branchHint(.cold);
49 return error.NoMemory;
50 }
51 const true_alloc_size = std.mem.alignForward(usize, size, std.heap.pageSize());
52
53 var i: usize = self.regions.len;
54 while (i > 0) : (i -= 1) {
55 const region = &self.regions[i - 1];
56 if (region.type != .usable) continue;
57 if (true_alloc_size > region.length) continue;
58 region.length -= true_alloc_size;
59 return region.base + region.length;
60 }
61 return error.OutOfMemory;
62 }
63
64 pub fn allocMem(self: *Self, size: usize) !usize {
65 return try self.allocPhys(size) + common.init_data.hhdm_slide;
66 }
67
68 pub fn debugInfo(self: *Self) void {
69 var total: usize = 0;
70 var usable: usize = 0;
71
72 for (self.regions) |region| {
73 total += region.length;
74 if (region.type == .usable) usable += region.length;
75 }
76
77 const total_gib = total / (1 << 30);
78 total -= total_gib * (1 << 30);
79 const total_mib = total / (1 << 20);
80 total -= total_mib * (1 << 20);
81 const total_kib = total / (1 << 10);
82 log.debug("Total Memory: {} GiB + {} MiB + {} KiB", .{ total_gib, total_mib, total_kib });
83
84 const usable_gib = usable / (1 << 30);
85 usable -= usable_gib * (1 << 30);
86 const usable_mib = usable / (1 << 20);
87 usable -= usable_mib * (1 << 20);
88 const usable_kib = usable / (1 << 10);
89 log.debug("Usable: {} GiB + {} MiB + {} KiB", .{ usable_gib, usable_mib, usable_kib });
90 }
91};
92
93pub fn init() void {
94 const memmap = arch.boot.limine_requests.memmap.response.?.getEntries();
95
96 const bootmem_structure_size, const region_count = blk: {
97 var region_count: usize = 0;
98 for (memmap) |region| {
99 switch (region.type) {
100 .usable, .acpi_reclaimable, .bootloader_reclaimable, .executable_and_modules, .framebuffer => region_count += 1,
101 else => {},
102 }
103 }
104
105 break :blk .{ BootPmm.calculateMetadataSize(region_count), region_count };
106 };
107 const bootmem_pages = std.mem.alignForward(usize, bootmem_structure_size, std.heap.pageSize());
108
109 // Given the bootmem structure size, find a page to hold it
110 const bootmem_struct: []BootPmm.Region = blk: {
111 var i: usize = memmap.len;
112 while (i > 0) : (i -= 1) {
113 const region = memmap[i - 1];
114 if (bootmem_pages > region.length) continue;
115 switch (region.type) {
116 .usable => {},
117 else => continue,
118 }
119 // Unfortunately, we can't modify the limine memory map itself
120 // So, remember the region we ate from instead
121 region.length -= bootmem_pages;
122 const bootmem_struct_ptr = common.mm.physToHHDM([*]BootPmm.Region, region.base + region.length);
123 break :blk bootmem_struct_ptr[0..region_count];
124 }
125 @panic("Couldn't allocate bootmem structure!");
126 };
127 // Now, fill the bootmem structure out from the limine memmap
128 var i: usize = 0;
129 for (memmap) |region| {
130 switch (region.type) {
131 .usable => bootmem_struct[i].type = .usable,
132 .acpi_reclaimable => bootmem_struct[i].type = .acpi_reclaimable,
133 .bootloader_reclaimable => bootmem_struct[i].type = .bootloader_reclaimable,
134 .executable_and_modules => bootmem_struct[i].type = .executable_and_modules,
135 .framebuffer => bootmem_struct[i].type = .framebuffer,
136 else => continue,
137 }
138 bootmem_struct[i].base = region.base;
139 bootmem_struct[i].length = region.length;
140 i += 1;
141 }
142
143 // Finally, initialize the global bootmem
144 common.init_data.bootmem.initialize(bootmem_struct);
145}