Merge pull request #206 from ZystemOS/bugfix/vmm-map-correctly

Bugfix/vmm map correctly
This commit is contained in:
Edward Dean 2020-07-24 23:54:03 +01:00 committed by GitHub
commit 7b4a5e97aa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 348 additions and 263 deletions

View file

@ -339,11 +339,10 @@ pub fn initMem(mb_info: BootPayload) Allocator.Error!MemProfile {
mem.ADDR_OFFSET = @ptrToInt(&KERNEL_ADDR_OFFSET); mem.ADDR_OFFSET = @ptrToInt(&KERNEL_ADDR_OFFSET);
const mmap_addr = mb_info.mmap_addr; const mmap_addr = mb_info.mmap_addr;
const num_mmap_entries = mb_info.mmap_length / @sizeOf(multiboot.multiboot_memory_map_t); const num_mmap_entries = mb_info.mmap_length / @sizeOf(multiboot.multiboot_memory_map_t);
const vaddr_end = @ptrCast([*]u8, &KERNEL_VADDR_END);
var allocator = std.heap.FixedBufferAllocator.init(vaddr_end[0..mem.FIXED_ALLOC_SIZE]); const allocator = &mem.fixed_buffer_allocator.allocator;
var reserved_physical_mem = std.ArrayList(mem.Range).init(&allocator.allocator); var reserved_physical_mem = std.ArrayList(mem.Range).init(allocator);
var reserved_virtual_mem = std.ArrayList(mem.Map).init(&allocator.allocator); var reserved_virtual_mem = std.ArrayList(mem.Map).init(allocator);
const mem_map = @intToPtr([*]multiboot.multiboot_memory_map_t, mmap_addr)[0..num_mmap_entries]; const mem_map = @intToPtr([*]multiboot.multiboot_memory_map_t, mmap_addr)[0..num_mmap_entries];
// Reserve the unavailable sections from the multiboot memory map // Reserve the unavailable sections from the multiboot memory map
@ -351,7 +350,10 @@ pub fn initMem(mb_info: BootPayload) Allocator.Error!MemProfile {
if (entry.@"type" != multiboot.MULTIBOOT_MEMORY_AVAILABLE) { if (entry.@"type" != multiboot.MULTIBOOT_MEMORY_AVAILABLE) {
// If addr + len is greater than maxInt(usize) just ignore whatever comes after maxInt(usize) since it can't be addressed anyway // If addr + len is greater than maxInt(usize) just ignore whatever comes after maxInt(usize) since it can't be addressed anyway
const end: usize = if (entry.addr > std.math.maxInt(usize) - entry.len) std.math.maxInt(usize) else @intCast(usize, entry.addr + entry.len); const end: usize = if (entry.addr > std.math.maxInt(usize) - entry.len) std.math.maxInt(usize) else @intCast(usize, entry.addr + entry.len);
try reserved_physical_mem.append(.{ .start = @intCast(usize, entry.addr), .end = end }); try reserved_physical_mem.append(.{
.start = @intCast(usize, entry.addr),
.end = end,
});
} }
} }
@ -360,8 +362,14 @@ pub fn initMem(mb_info: BootPayload) Allocator.Error!MemProfile {
.start = @ptrToInt(mb_info), .start = @ptrToInt(mb_info),
.end = @ptrToInt(mb_info) + @sizeOf(multiboot.multiboot_info_t), .end = @ptrToInt(mb_info) + @sizeOf(multiboot.multiboot_info_t),
}; };
const mb_physical = mem.Range{ .start = mem.virtToPhys(mb_region.start), .end = mem.virtToPhys(mb_region.end) }; const mb_physical = mem.Range{
try reserved_virtual_mem.append(.{ .virtual = mb_region, .physical = mb_physical }); .start = mem.virtToPhys(mb_region.start),
.end = mem.virtToPhys(mb_region.end),
};
try reserved_virtual_mem.append(.{
.virtual = mb_region,
.physical = mb_physical,
});
// Map the tty buffer // Map the tty buffer
const tty_addr = mem.virtToPhys(tty.getVideoBufferAddress()); const tty_addr = mem.virtToPhys(tty.getVideoBufferAddress());
@ -379,16 +387,56 @@ pub fn initMem(mb_info: BootPayload) Allocator.Error!MemProfile {
// Map the boot modules // Map the boot modules
const boot_modules = @intToPtr([*]multiboot.multiboot_mod_list, mem.physToVirt(mb_info.mods_addr))[0..mods_count]; const boot_modules = @intToPtr([*]multiboot.multiboot_mod_list, mem.physToVirt(mb_info.mods_addr))[0..mods_count];
var modules = std.ArrayList(mem.Module).init(&allocator.allocator); var modules = std.ArrayList(mem.Module).init(allocator);
for (boot_modules) |module| { for (boot_modules) |module| {
const virtual = mem.Range{ .start = mem.physToVirt(module.mod_start), .end = mem.physToVirt(module.mod_end) }; const virtual = mem.Range{
const physical = mem.Range{ .start = module.mod_start, .end = module.mod_end }; .start = mem.physToVirt(module.mod_start),
try modules.append(.{ .region = virtual, .name = std.mem.span(mem.physToVirt(@intToPtr([*:0]u8, module.cmdline))) }); .end = mem.physToVirt(module.mod_end),
try reserved_virtual_mem.append(.{ .physical = physical, .virtual = virtual }); };
const physical = mem.Range{
.start = module.mod_start,
.end = module.mod_end,
};
try modules.append(.{
.region = virtual,
.name = std.mem.span(mem.physToVirt(@intToPtr([*:0]u8, module.cmdline))),
});
try reserved_virtual_mem.append(.{
.physical = physical,
.virtual = virtual,
});
} }
// Map the kernel stack
const kernel_stack_virt = mem.Range{
.start = @ptrToInt(&KERNEL_STACK_START),
.end = @ptrToInt(&KERNEL_STACK_END),
};
const kernel_stack_phy = mem.Range{
.start = mem.virtToPhys(kernel_stack_virt.start),
.end = mem.virtToPhys(kernel_stack_virt.end),
};
try reserved_virtual_mem.append(.{
.virtual = kernel_stack_virt,
.physical = kernel_stack_phy,
});
// Map the rest of the kernel
const kernel_virt = mem.Range{
.start = @ptrToInt(&KERNEL_VADDR_START),
.end = @ptrToInt(&KERNEL_STACK_START),
};
const kernel_phy = mem.Range{
.start = mem.virtToPhys(kernel_virt.start),
.end = mem.virtToPhys(kernel_virt.end),
};
try reserved_virtual_mem.append(.{
.virtual = kernel_virt,
.physical = kernel_phy,
});
return MemProfile{ return MemProfile{
.vaddr_end = vaddr_end, .vaddr_end = @ptrCast([*]u8, &KERNEL_VADDR_END),
.vaddr_start = @ptrCast([*]u8, &KERNEL_VADDR_START), .vaddr_start = @ptrCast([*]u8, &KERNEL_VADDR_START),
.physaddr_end = @ptrCast([*]u8, &KERNEL_PHYSADDR_END), .physaddr_end = @ptrCast([*]u8, &KERNEL_PHYSADDR_END),
.physaddr_start = @ptrCast([*]u8, &KERNEL_PHYSADDR_START), .physaddr_start = @ptrCast([*]u8, &KERNEL_PHYSADDR_START),
@ -397,7 +445,7 @@ pub fn initMem(mb_info: BootPayload) Allocator.Error!MemProfile {
.modules = modules.items, .modules = modules.items,
.physical_reserved = reserved_physical_mem.items, .physical_reserved = reserved_physical_mem.items,
.virtual_reserved = reserved_virtual_mem.items, .virtual_reserved = reserved_virtual_mem.items,
.fixed_allocator = allocator, .fixed_allocator = mem.fixed_buffer_allocator,
}; };
} }
@ -452,12 +500,10 @@ pub fn initTaskStack(entry_point: usize, allocator: *Allocator) Allocator.Error!
/// Initialise the architecture /// Initialise the architecture
/// ///
/// Arguments: /// Arguments:
/// IN boot_payload: BootPayload - The multiboot information from the GRUB bootloader.
/// IN mem_profile: *const MemProfile - The memory profile of the computer. Used to set up /// IN mem_profile: *const MemProfile - The memory profile of the computer. Used to set up
/// paging. /// paging.
/// IN allocator: *Allocator - The allocator use to handle memory.
/// ///
pub fn init(boot_payload: BootPayload, mem_profile: *const MemProfile, allocator: *Allocator) void { pub fn init(mem_profile: *const MemProfile) void {
gdt.init(); gdt.init();
idt.init(); idt.init();
@ -465,7 +511,7 @@ pub fn init(boot_payload: BootPayload, mem_profile: *const MemProfile, allocator
isr.init(); isr.init();
irq.init(); irq.init();
paging.init(boot_payload, mem_profile, allocator); paging.init(mem_profile);
pit.init(); pit.init();
rtc.init(); rtc.init();

View file

@ -101,6 +101,7 @@ export fn start_higher_half() callconv(.Naked) noreturn {
asm volatile ( asm volatile (
\\.extern KERNEL_STACK_END \\.extern KERNEL_STACK_END
\\mov $KERNEL_STACK_END, %%esp \\mov $KERNEL_STACK_END, %%esp
\\sub $32, %%esp
\\mov %%esp, %%ebp \\mov %%esp, %%ebp
); );

View file

@ -387,9 +387,8 @@ fn pageFault(state: *arch.CpuState) u32 {
/// ///
/// Arguments: /// Arguments:
/// IN mem_profile: *const MemProfile - The memory profile of the system and kernel /// IN mem_profile: *const MemProfile - The memory profile of the system and kernel
/// IN allocator: *std.mem.Allocator - The allocator to use
/// ///
pub fn init(mb_info: *multiboot.multiboot_info_t, mem_profile: *const MemProfile, allocator: *std.mem.Allocator) void { pub fn init(mem_profile: *const MemProfile) void {
std.log.info(.paging, "Init\n", .{}); std.log.info(.paging, "Init\n", .{});
defer std.log.info(.paging, "Done\n", .{}); defer std.log.info(.paging, "Done\n", .{});
@ -401,7 +400,7 @@ pub fn init(mb_info: *multiboot.multiboot_info_t, mem_profile: *const MemProfile
: :
: [addr] "{eax}" (dir_physaddr) : [addr] "{eax}" (dir_physaddr)
); );
const v_end = std.mem.alignForward(@ptrToInt(mem_profile.vaddr_end) + mem.FIXED_ALLOC_SIZE, PAGE_SIZE_4KB); const v_end = std.mem.alignForward(@ptrToInt(mem_profile.vaddr_end), PAGE_SIZE_4KB);
switch (build_options.test_mode) { switch (build_options.test_mode) {
.Initialisation => runtimeTests(v_end), .Initialisation => runtimeTests(v_end),
else => {}, else => {},

View file

@ -414,6 +414,8 @@ const FreeListAllocator = struct {
var free_list = &(try FreeListAllocator.init(start, size)); var free_list = &(try FreeListAllocator.init(start, size));
var allocator = &free_list.allocator; var allocator = &free_list.allocator;
std.debug.warn("", .{});
const alloc0 = try alloc(allocator, 64, 0, 0); const alloc0 = try alloc(allocator, 64, 0, 0);
const alloc0_addr = @ptrToInt(alloc0.ptr); const alloc0_addr = @ptrToInt(alloc0.ptr);
// Should be at the start of the heap // Should be at the start of the heap
@ -424,6 +426,8 @@ const FreeListAllocator = struct {
testing.expectEqual(header.next_free, null); testing.expectEqual(header.next_free, null);
testing.expectEqual(free_list.first_free, header); testing.expectEqual(free_list.first_free, header);
std.debug.warn("", .{});
// 64 bytes aligned to 4 bytes // 64 bytes aligned to 4 bytes
const alloc1 = try alloc(allocator, 64, 4, 0); const alloc1 = try alloc(allocator, 64, 4, 0);
const alloc1_addr = @ptrToInt(alloc1.ptr); const alloc1_addr = @ptrToInt(alloc1.ptr);

View file

@ -61,7 +61,9 @@ export fn kmain(boot_payload: arch.BootPayload) void {
log_root.init(serial_stream); log_root.init(serial_stream);
const mem_profile = arch.initMem(boot_payload) catch |e| panic_root.panic(@errorReturnTrace(), "Failed to initialise memory profile: {}", .{e}); const mem_profile = arch.initMem(boot_payload) catch |e| {
panic_root.panic(@errorReturnTrace(), "Failed to initialise memory profile: {}", .{e});
};
var fixed_allocator = mem_profile.fixed_allocator; var fixed_allocator = mem_profile.fixed_allocator;
panic_root.init(&mem_profile, &fixed_allocator.allocator) catch |e| { panic_root.init(&mem_profile, &fixed_allocator.allocator) catch |e| {
@ -69,10 +71,12 @@ export fn kmain(boot_payload: arch.BootPayload) void {
}; };
pmm.init(&mem_profile, &fixed_allocator.allocator); pmm.init(&mem_profile, &fixed_allocator.allocator);
kernel_vmm = vmm.init(&mem_profile, &fixed_allocator.allocator) catch |e| panic_root.panic(@errorReturnTrace(), "Failed to initialise kernel VMM: {}", .{e}); kernel_vmm = vmm.init(&mem_profile, &fixed_allocator.allocator) catch |e| {
panic_root.panic(@errorReturnTrace(), "Failed to initialise kernel VMM: {}", .{e});
};
std.log.info(.kmain, "Init arch " ++ @tagName(builtin.arch) ++ "\n", .{}); std.log.info(.kmain, "Init arch " ++ @tagName(builtin.arch) ++ "\n", .{});
arch.init(boot_payload, &mem_profile, &fixed_allocator.allocator); arch.init(&mem_profile);
std.log.info(.kmain, "Arch init done\n", .{}); std.log.info(.kmain, "Arch init done\n", .{});
// Give the kernel heap 10% of the available memory. This can be fine-tuned as time goes on. // Give the kernel heap 10% of the available memory. This can be fine-tuned as time goes on.

View file

@ -53,7 +53,10 @@ pub const MemProfile = struct {
}; };
/// The size of the fixed allocator used before the heap is set up. Set to 1MiB. /// The size of the fixed allocator used before the heap is set up. Set to 1MiB.
pub const FIXED_ALLOC_SIZE: usize = 1024 * 1024; pub var fixed_buffer: [1024 * 1024]u8 = undefined;
/// The fixed allocator used before the heap is set up.
pub var fixed_buffer_allocator: std.heap.FixedBufferAllocator = std.heap.FixedBufferAllocator.init(fixed_buffer[0..]);
/// The kernel's virtual address offset. It's assigned in the init function and this file's tests. /// The kernel's virtual address offset. It's assigned in the init function and this file's tests.
/// We can't just use KERNEL_ADDR_OFFSET since using externs in the virtToPhys test is broken in /// We can't just use KERNEL_ADDR_OFFSET since using externs in the virtToPhys test is broken in

View file

@ -6,6 +6,7 @@ const multiboot = @import("multiboot.zig");
const mem = @import("mem.zig"); const mem = @import("mem.zig");
const build_options = @import("build_options"); const build_options = @import("build_options");
const ArrayList = std.ArrayList; const ArrayList = std.ArrayList;
const Allocator = std.mem.Allocator;
const testing = std.testing; const testing = std.testing;
/// The possible errors from panic code /// The possible errors from panic code
@ -31,12 +32,12 @@ const SymbolMap = struct {
/// Initialise an empty symbol map. /// Initialise an empty symbol map.
/// ///
/// Arguments: /// Arguments:
/// IN allocator: *std.mem.Allocator - The allocator to use to initialise the array list. /// IN allocator: *Allocator - The allocator to use to initialise the array list.
/// ///
/// Return: SymbolMap /// Return: SymbolMap
/// The symbol map. /// The symbol map.
/// ///
pub fn init(allocator: *std.mem.Allocator) SymbolMap { pub fn init(allocator: *Allocator) SymbolMap {
return SymbolMap{ return SymbolMap{
.symbols = ArrayList(MapEntry).init(allocator), .symbols = ArrayList(MapEntry).init(allocator),
}; };
@ -56,14 +57,23 @@ const SymbolMap = struct {
/// IN name: []const u8 - The name of the entry. /// IN name: []const u8 - The name of the entry.
/// IN addr: usize - The address for the entry. /// IN addr: usize - The address for the entry.
/// ///
/// Error: std.mem.Allocator.Error /// Error: Allocator.Error
/// * - See ArrayList.append /// error.OutOfMemory - If there isn't enough memory to append a map entry.
/// ///
pub fn add(self: *SymbolMap, name: []const u8, addr: usize) !void { pub fn add(self: *SymbolMap, name: []const u8, addr: usize) Allocator.Error!void {
try self.addEntry(MapEntry{ .addr = addr, .func_name = name }); try self.addEntry(MapEntry{ .addr = addr, .func_name = name });
} }
pub fn addEntry(self: *SymbolMap, entry: MapEntry) !void { ///
/// Add a symbol map entry.
///
/// Arguments:
/// IN entry: MapEntry - The entry.
///
/// Error: Allocator.Error
/// error.OutOfMemory - If there isn't enough memory to append a map entry.
///
pub fn addEntry(self: *SymbolMap, entry: MapEntry) Allocator.Error!void {
try self.symbols.append(entry); try self.symbols.append(entry);
} }
@ -104,6 +114,168 @@ fn logTraceAddress(addr: usize) void {
std.log.emerg(.panic, "{x}: {}\n", .{ addr, str }); std.log.emerg(.panic, "{x}: {}\n", .{ addr, str });
} }
///
/// Parse a hexadecimal address from the pointer up until the end pointer. Must be terminated by a
/// whitespace character.
///
/// Arguments:
/// IN/OUT ptr: *[*]const u8 - The address at which to start looking, updated after all
/// characters have been consumed.
/// IN end: *const u8 - The end address at which to start looking. A whitespace character must
/// be found before this.
///
/// Return: usize
/// The address parsed.
///
/// Error: PanicError || std.fmt.ParseIntError
/// PanicError.InvalidSymbolFile - A terminating whitespace wasn't found before the end address.
/// std.fmt.ParseIntError - See std.fmt.parseInt
///
fn parseAddr(ptr: *[*]const u8, end: *const u8) (PanicError || std.fmt.ParseIntError)!usize {
const addr_start = ptr.*;
ptr.* = try parseNonWhitespace(ptr.*, end);
const len = @ptrToInt(ptr.*) - @ptrToInt(addr_start);
const addr_str = addr_start[0..len];
return std.fmt.parseInt(usize, addr_str, 16);
}
///
/// Parse a single character. The address given cannot be greater than or equal to the end address
/// given.
///
/// Arguments:
/// IN ptr: [*]const u8 - The address at which to get the character from.
/// IN end: *const u8 - The end address at which to start looking. ptr cannot be greater than or
/// equal to this.
///
/// Return: u8
/// The character parsed.
///
/// Error: PanicError
/// PanicError.InvalidSymbolFile - The address given is greater than or equal to the end address.
///
fn parseChar(ptr: [*]const u8, end: *const u8) PanicError!u8 {
if (@ptrToInt(ptr) >= @ptrToInt(end)) {
return PanicError.InvalidSymbolFile;
}
return ptr[0];
}
///
/// Parse until a non-whitespace character. Must be terminated by a non-whitespace character before
/// the end address.
///
/// Arguments:
/// IN ptr: [*]const u8 - The address at which to start looking.
/// IN end: *const u8 - The end address at which to start looking. A non-whitespace character
/// must be found before this.
///
/// Return: [*]const u8
/// ptr plus the number of whitespace characters consumed.
///
/// Error: PanicError
/// PanicError.InvalidSymbolFile - A terminating non-whitespace character wasn't found before
/// the end address.
///
fn parseWhitespace(ptr: [*]const u8, end: *const u8) PanicError![*]const u8 {
var i: u32 = 0;
while (std.fmt.isWhiteSpace(try parseChar(ptr + i, end))) : (i += 1) {}
return ptr + i;
}
///
/// Parse until a whitespace character. Must be terminated by a whitespace character before the end
/// address.
///
/// Arguments:
/// IN ptr: [*]const u8 - The address at which to start looking.
/// IN end: *const u8 - The end address at which to start looking. A whitespace character must
/// be found before this.
///
/// Return: [*]const u8
/// ptr plus the number of non-whitespace characters consumed.
///
/// Error: PanicError
/// PanicError.InvalidSymbolFile - A terminating whitespace character wasn't found before the
/// end address.
///
fn parseNonWhitespace(ptr: [*]const u8, end: *const u8) PanicError![*]const u8 {
var i: u32 = 0;
while (!std.fmt.isWhiteSpace(try parseChar(ptr + i, end))) : (i += 1) {}
return ptr + i;
}
///
/// Parse until a newline character. Must be terminated by a newline character before the end
/// address.
///
/// Arguments:
/// IN ptr: [*]const u8 - The address at which to start looking.
/// IN end: *const u8 - The end address at which to start looking. A newline character must
/// be found before this.
///
/// Return: [*]const u8
/// ptr plus the number of non-newline characters consumed.
///
/// Error: PanicError
/// PanicError.InvalidSymbolFile - A terminating newline character wasn't found before the
/// end address.
///
fn parseNonNewLine(ptr: [*]const u8, end: *const u8) PanicError![*]const u8 {
var i: u32 = 0;
while ((try parseChar(ptr + i, end)) != '\n') : (i += 1) {}
return ptr + i;
}
///
/// Parse a name from the pointer up until the end pointer. Must be terminated by a whitespace
/// character.
///
/// Arguments:
/// IN/OUT ptr: *[*]const u8 - The address at which to start looking, updated after all
/// characters have been consumed.
/// IN end: *const u8 - The end address at which to start looking. A whitespace character must
/// be found before this.
///
/// Return: []const u8
/// The name parsed.
///
/// Error: PanicError
/// PanicError.InvalidSymbolFile - A terminating whitespace wasn't found before the end address.
///
fn parseName(ptr: *[*]const u8, end: *const u8) PanicError![]const u8 {
const name_start = ptr.*;
ptr.* = try parseNonNewLine(ptr.*, end);
const len = @ptrToInt(ptr.*) - @ptrToInt(name_start);
return name_start[0..len];
}
///
/// Parse a symbol map entry from the pointer up until the end pointer,
/// in the format of '\d+\w+[a-zA-Z0-9]+'. Must be terminated by a whitespace character.
///
/// Arguments:
/// IN/OUT ptr: *[*]const u8 - The address at which to start looking, updated once after the
/// address has been consumed and once again after the name has been consumed.
/// IN end: *const u8 - The end address at which to start looking. A whitespace character must
/// be found before this.
///
/// Return: MapEntry
/// The entry parsed.
///
/// Error: PanicError || std.fmt.ParseIntError
/// PanicError.InvalidSymbolFile - A terminating whitespace wasn't found before the end address.
/// std.fmt.ParseIntError - See parseAddr.
///
fn parseMapEntry(start: *[*]const u8, end: *const u8) (PanicError || std.fmt.ParseIntError)!MapEntry {
var ptr = try parseWhitespace(start.*, end);
defer start.* = ptr;
const addr = try parseAddr(&ptr, end);
ptr = try parseWhitespace(ptr, end);
const name = try parseName(&ptr, end);
return MapEntry{ .addr = addr, .func_name = name };
}
pub fn panic(trace: ?*builtin.StackTrace, comptime format: []const u8, args: anytype) noreturn { pub fn panic(trace: ?*builtin.StackTrace, comptime format: []const u8, args: anytype) noreturn {
@setCold(true); @setCold(true);
std.log.emerg(.panic, "Kernel panic: " ++ format ++ "\n", args); std.log.emerg(.panic, "Kernel panic: " ++ format ++ "\n", args);
@ -125,168 +297,6 @@ pub fn panic(trace: ?*builtin.StackTrace, comptime format: []const u8, args: any
arch.haltNoInterrupts(); arch.haltNoInterrupts();
} }
///
/// Parse a hexadecimal address from the pointer up until the end pointer. Must be terminated by a
/// whitespace character.
///
/// Arguments:
/// IN/OUT ptr: *[*]const u8 - The address at which to start looking, updated after all
/// characters have been consumed.
/// IN end: *const u8 - The end address at which to start looking. A whitespace character must
/// be found before this.
///
/// Return: usize
/// The address parsed.
///
/// Error: PanicError || std.fmt.ParseUnsignedError
/// PanicError.InvalidSymbolFile: A terminating whitespace wasn't found before the end address.
/// std.fmt.ParseUnsignedError: See std.fmt.parseInt
///
fn parseAddr(ptr: *[*]const u8, end: *const u8) !usize {
const addr_start = ptr.*;
ptr.* = try parseNonWhitespace(ptr.*, end);
const len = @ptrToInt(ptr.*) - @ptrToInt(addr_start);
const addr_str = addr_start[0..len];
return try std.fmt.parseInt(usize, addr_str, 16);
}
///
/// Parse a single character. The address given cannot be greater than or equal to the end address
/// given.
///
/// Arguments:
/// IN ptr: [*]const u8 - The address at which to get the character from.
/// IN end: *const u8 - The end address at which to start looking. ptr cannot be greater than or
/// equal to this.
///
/// Return: u8
/// The character parsed.
///
/// Error: PanicError
/// PanicError.InvalidSymbolFile: The address given is greater than or equal to the end address.
///
fn parseChar(ptr: [*]const u8, end: *const u8) PanicError!u8 {
if (@ptrToInt(ptr) >= @ptrToInt(end)) {
return PanicError.InvalidSymbolFile;
}
return ptr[0];
}
///
/// Parse until a non-whitespace character. Must be terminated by a non-whitespace character before
/// the end address.
///
/// Arguments:
/// IN ptr: [*]const u8 - The address at which to start looking.
/// IN end: *const u8 - The end address at which to start looking. A non-whitespace character
/// must be found before this.
///
/// Return: [*]const u8
/// ptr plus the number of whitespace characters consumed.
///
/// Error: PanicError
/// PanicError.InvalidSymbolFile: A terminating non-whitespace character wasn't found before the
/// end address.
///
fn parseWhitespace(ptr: [*]const u8, end: *const u8) PanicError![*]const u8 {
var i: u32 = 0;
while (std.fmt.isWhiteSpace(try parseChar(ptr + i, end))) : (i += 1) {}
return ptr + i;
}
///
/// Parse until a whitespace character. Must be terminated by a whitespace character before the end
/// address.
///
/// Arguments:
/// IN ptr: [*]const u8 - The address at which to start looking.
/// IN end: *const u8 - The end address at which to start looking. A whitespace character must
/// be found before this.
///
/// Return: [*]const u8
/// ptr plus the number of non-whitespace characters consumed.
///
/// Error: PanicError
/// PanicError.InvalidSymbolFile: A terminating whitespace character wasn't found before the end
/// address.
///
fn parseNonWhitespace(ptr: [*]const u8, end: *const u8) PanicError![*]const u8 {
var i: u32 = 0;
while (!std.fmt.isWhiteSpace(try parseChar(ptr + i, end))) : (i += 1) {}
return ptr + i;
}
///
/// Parse until a newline character. Must be terminated by a newline character before the end
/// address.
///
/// Arguments:
/// IN ptr: [*]const u8 - The address at which to start looking.
/// IN end: *const u8 - The end address at which to start looking. A newline character must
/// be found before this.
///
/// Return: [*]const u8
/// ptr plus the number of non-newline characters consumed.
///
/// Error: PanicError
/// PanicError.InvalidSymbolFile: A terminating newline character wasn't found before the end
/// address.
///
fn parseNonNewLine(ptr: [*]const u8, end: *const u8) PanicError![*]const u8 {
var i: u32 = 0;
while ((try parseChar(ptr + i, end)) != '\n') : (i += 1) {}
return ptr + i;
}
///
/// Parse a name from the pointer up until the end pointer. Must be terminated by a whitespace
/// character.
///
/// Arguments:
/// IN/OUT ptr: *[*]const u8 - The address at which to start looking, updated after all
/// characters have been consumed.
/// IN end: *const u8 - The end address at which to start looking. A whitespace character must
/// be found before this.
///
/// Return: []const u8
/// The name parsed.
///
/// Error: PanicError
/// PanicError.InvalidSymbolFile: A terminating whitespace wasn't found before the end address.
///
fn parseName(ptr: *[*]const u8, end: *const u8) PanicError![]const u8 {
const name_start = ptr.*;
ptr.* = try parseNonNewLine(ptr.*, end);
const len = @ptrToInt(ptr.*) - @ptrToInt(name_start);
return name_start[0..len];
}
///
/// Parse a symbol map entry from the pointer up until the end pointer,
/// in the format of '\d+\w+[a-zA-Z0-9]+'. Must be terminated by a whitespace character.
///
/// Arguments:
/// IN/OUT ptr: *[*]const u8 - The address at which to start looking, updated once after the
/// address has been consumed and once again after the name has been consumed.
/// IN end: *const u8 - The end address at which to start looking. A whitespace character must
/// be found before this.
///
/// Return: MapEntry
/// The entry parsed.
///
/// Error: PanicError || std.fmt.ParseUnsignedError
/// PanicError.InvalidSymbolFile: A terminating whitespace wasn't found before the end address.
/// std.fmt.ParseUnsignedError: See parseAddr.
///
fn parseMapEntry(start: *[*]const u8, end: *const u8) !MapEntry {
var ptr = try parseWhitespace(start.*, end);
defer start.* = ptr;
const addr = try parseAddr(&ptr, end);
ptr = try parseWhitespace(ptr, end);
const name = try parseName(&ptr, end);
return MapEntry{ .addr = addr, .func_name = name };
}
/// ///
/// Initialise the panic subsystem by looking for a boot module called "kernel.map" and loading the /// Initialise the panic subsystem by looking for a boot module called "kernel.map" and loading the
/// symbols from it. Exits early if no such module was found. /// symbols from it. Exits early if no such module was found.
@ -294,13 +304,14 @@ fn parseMapEntry(start: *[*]const u8, end: *const u8) !MapEntry {
/// Arguments: /// Arguments:
/// IN mem_profile: *const mem.MemProfile - The memory profile from which to get the loaded boot /// IN mem_profile: *const mem.MemProfile - The memory profile from which to get the loaded boot
/// modules. /// modules.
/// IN allocator: *std.mem.Allocator - The allocator to use to store the symbol map. /// IN allocator: *Allocator - The allocator to use to store the symbol map.
/// ///
/// Error: PanicError || std.fmt.ParseUnsignedError /// Error: PanicError || Allocator.Error || std.fmt.ParseIntError
/// PanicError.InvalidSymbolFile: A terminating whitespace wasn't found before the end address. /// PanicError.InvalidSymbolFile - A terminating whitespace wasn't found before the end address.
/// std.fmt.ParseUnsignedError: See parseMapEntry. /// Allocator.Error.OutOfMemory - If there wasn't enough memory.
/// std.fmt.ParseIntError - See parseMapEntry.
/// ///
pub fn init(mem_profile: *const mem.MemProfile, allocator: *std.mem.Allocator) !void { pub fn init(mem_profile: *const mem.MemProfile, allocator: *Allocator) (PanicError || Allocator.Error || std.fmt.ParseIntError)!void {
std.log.info(.panic, "Init\n", .{}); std.log.info(.panic, "Init\n", .{});
defer std.log.info(.panic, "Done\n", .{}); defer std.log.info(.panic, "Done\n", .{});

View file

@ -7,6 +7,7 @@ const MemProfile = (if (is_test) @import(mock_path ++ "mem_mock.zig") else @impo
const testing = std.testing; const testing = std.testing;
const panic = @import("panic.zig").panic; const panic = @import("panic.zig").panic;
const Bitmap = @import("bitmap.zig").Bitmap; const Bitmap = @import("bitmap.zig").Bitmap;
const Allocator = std.mem.Allocator;
const PmmBitmap = Bitmap(u32); const PmmBitmap = Bitmap(u32);
@ -94,16 +95,18 @@ pub fn blocksFree() usize {
/// ///
/// Arguments: /// Arguments:
/// IN mem: *const MemProfile - The system's memory profile. /// IN mem: *const MemProfile - The system's memory profile.
/// IN allocator: *std.mem.Allocator - The allocator to use to allocate the bitmaps. /// IN allocator: *Allocator - The allocator to use to allocate the bitmaps.
/// ///
pub fn init(mem: *const MemProfile, allocator: *std.mem.Allocator) void { pub fn init(mem_profile: *const MemProfile, allocator: *Allocator) void {
std.log.info(.pmm, "Init\n", .{}); std.log.info(.pmm, "Init\n", .{});
defer std.log.info(.pmm, "Done\n", .{}); defer std.log.info(.pmm, "Done\n", .{});
bitmap = PmmBitmap.init(mem.mem_kb * 1024 / BLOCK_SIZE, allocator) catch @panic("Bitmap allocation failed"); bitmap = PmmBitmap.init(mem_profile.mem_kb * 1024 / BLOCK_SIZE, allocator) catch |e| {
panic(@errorReturnTrace(), "Bitmap allocation failed: {}\n", .{e});
};
// Occupy the regions of memory that the memory map describes as reserved // Occupy the regions of memory that the memory map describes as reserved
for (mem.physical_reserved) |entry| { for (mem_profile.physical_reserved) |entry| {
var addr = std.mem.alignBackward(entry.start, BLOCK_SIZE); var addr = std.mem.alignBackward(entry.start, BLOCK_SIZE);
var end = entry.end - 1; var end = entry.end - 1;
// If the end address can be aligned without overflowing then align it // If the end address can be aligned without overflowing then align it
@ -120,7 +123,7 @@ pub fn init(mem: *const MemProfile, allocator: *std.mem.Allocator) void {
} }
switch (build_options.test_mode) { switch (build_options.test_mode) {
.Initialisation => runtimeTests(mem, allocator), .Initialisation => runtimeTests(mem_profile, allocator),
else => {}, else => {},
} }
} }
@ -200,10 +203,10 @@ test "setAddr and isSet" {
/// Allocate all blocks and make sure they don't overlap with any reserved addresses. /// Allocate all blocks and make sure they don't overlap with any reserved addresses.
/// ///
/// Arguments: /// Arguments:
/// IN mem: *const MemProfile - The memory profile to check for reserved memory regions. /// IN mem_profile: *const MemProfile - The memory profile to check for reserved memory regions.
/// IN/OUT allocator: *std.mem.Allocator - The allocator to use when needing to create intermediate structures used for testing /// IN/OUT allocator: *Allocator - The allocator to use when needing to create intermediate structures used for testing
/// ///
fn runtimeTests(mem: *const MemProfile, allocator: *std.mem.Allocator) void { fn runtimeTests(mem_profile: *const MemProfile, allocator: *Allocator) void {
// Make sure that occupied memory can't be allocated // Make sure that occupied memory can't be allocated
var prev_alloc: usize = std.math.maxInt(usize); var prev_alloc: usize = std.math.maxInt(usize);
var alloc_list = std.ArrayList(usize).init(allocator); var alloc_list = std.ArrayList(usize).init(allocator);
@ -213,17 +216,21 @@ fn runtimeTests(mem: *const MemProfile, allocator: *std.mem.Allocator) void {
panic(null, "FAILURE: PMM allocated the same address twice: 0x{x}", .{alloced}); panic(null, "FAILURE: PMM allocated the same address twice: 0x{x}", .{alloced});
} }
prev_alloc = alloced; prev_alloc = alloced;
for (mem.physical_reserved) |entry| { for (mem_profile.physical_reserved) |entry| {
var addr = std.mem.alignBackward(@intCast(usize, entry.start), BLOCK_SIZE); var addr = std.mem.alignBackward(@intCast(usize, entry.start), BLOCK_SIZE);
if (addr == alloced) { if (addr == alloced) {
panic(null, "FAILURE: PMM allocated an address that should be reserved by the memory map: 0x{x}", .{addr}); panic(null, "FAILURE: PMM allocated an address that should be reserved by the memory map: 0x{x}", .{addr});
} }
} }
alloc_list.append(alloced) catch |e| panic(@errorReturnTrace(), "FAILURE: Failed to add PMM allocation to list: {}", .{e}); alloc_list.append(alloced) catch |e| {
panic(@errorReturnTrace(), "FAILURE: Failed to add PMM allocation to list: {}", .{e});
};
} }
// Clean up // Clean up
for (alloc_list.items) |alloced| { for (alloc_list.items) |alloced| {
free(alloced) catch |e| panic(@errorReturnTrace(), "FAILURE: Failed freeing allocation in PMM rt test: {}", .{e}); free(alloced) catch |e| {
panic(@errorReturnTrace(), "FAILURE: Failed freeing allocation in PMM rt test: {}", .{e});
};
} }
std.log.info(.pmm, "Tested allocation\n", .{}); std.log.info(.pmm, "Tested allocation\n", .{});
} }

View file

@ -9,6 +9,7 @@ const mem = if (is_test) @import(mock_path ++ "mem_mock.zig") else @import("mem.
const tty = @import("tty.zig"); const tty = @import("tty.zig");
const panic = @import("panic.zig").panic; const panic = @import("panic.zig").panic;
const arch = @import("arch.zig").internals; const arch = @import("arch.zig").internals;
const Allocator = std.mem.Allocator;
/// Attributes for a virtual memory allocation /// Attributes for a virtual memory allocation
pub const Attributes = struct { pub const Attributes = struct {
@ -63,13 +64,13 @@ pub fn Mapper(comptime Payload: type) type {
/// IN physical_start: usize - The start of the physical memory to map to /// IN physical_start: usize - The start of the physical memory to map to
/// IN physical_end: usize - The end of the physical memory to map to /// IN physical_end: usize - The end of the physical memory to map to
/// IN attrs: Attributes - The attributes to apply to this region of memory /// IN attrs: Attributes - The attributes to apply to this region of memory
/// IN/OUT allocator: std.mem.Allocator - The allocator to use when mapping, if required /// IN/OUT allocator: Allocator - The allocator to use when mapping, if required
/// IN spec: Payload - The payload to pass to the mapper /// IN spec: Payload - The payload to pass to the mapper
/// ///
/// Error: std.mem.AllocatorError || MapperError /// Error: AllocatorError || MapperError
/// The causes depend on the mapper used /// The causes depend on the mapper used
/// ///
mapFn: fn (virtual_start: usize, virtual_end: usize, physical_start: usize, physical_end: usize, attrs: Attributes, allocator: *std.mem.Allocator, spec: Payload) (std.mem.Allocator.Error || MapperError)!void, mapFn: fn (virtual_start: usize, virtual_end: usize, physical_start: usize, physical_end: usize, attrs: Attributes, allocator: *Allocator, spec: Payload) (Allocator.Error || MapperError)!void,
/// ///
/// Unmap a region (can span more than one block) of virtual memory from its physical memory. After a call to this function, the memory should not be accessible without error. /// Unmap a region (can span more than one block) of virtual memory from its physical memory. After a call to this function, the memory should not be accessible without error.
@ -79,10 +80,10 @@ pub fn Mapper(comptime Payload: type) type {
/// IN virtual_end: usize - The end of the virtual region to unmap /// IN virtual_end: usize - The end of the virtual region to unmap
/// IN spec: Payload - The payload to pass to the mapper /// IN spec: Payload - The payload to pass to the mapper
/// ///
/// Error: std.mem.AllocatorError || MapperError /// Error: AllocatorError || MapperError
/// The causes depend on the mapper used /// The causes depend on the mapper used
/// ///
unmapFn: fn (virtual_start: usize, virtual_end: usize, spec: Payload) (std.mem.Allocator.Error || MapperError)!void, unmapFn: fn (virtual_start: usize, virtual_end: usize, spec: Payload) (Allocator.Error || MapperError)!void,
}; };
} }
@ -132,7 +133,7 @@ pub fn VirtualMemoryManager(comptime Payload: type) type {
end: usize, end: usize,
/// The allocator to use when allocating and freeing regions /// The allocator to use when allocating and freeing regions
allocator: *std.mem.Allocator, allocator: *Allocator,
/// All allocations that have been made with this manager /// All allocations that have been made with this manager
allocations: std.hash_map.AutoHashMap(usize, Allocation), allocations: std.hash_map.AutoHashMap(usize, Allocation),
@ -151,17 +152,17 @@ pub fn VirtualMemoryManager(comptime Payload: type) type {
/// Arguments: /// Arguments:
/// IN start: usize - The start of the memory region to manage /// IN start: usize - The start of the memory region to manage
/// IN end: usize - The end of the memory region to manage. Must be greater than the start /// IN end: usize - The end of the memory region to manage. Must be greater than the start
/// IN/OUT allocator: *std.mem.Allocator - The allocator to use when allocating and freeing regions /// IN/OUT allocator: *Allocator - The allocator to use when allocating and freeing regions
/// IN mapper: Mapper - The mapper to use when allocating and freeing regions /// IN mapper: Mapper - The mapper to use when allocating and freeing regions
/// IN payload: Payload - The payload data to be passed to the mapper /// IN payload: Payload - The payload data to be passed to the mapper
/// ///
/// Return: Self /// Return: Self
/// The manager constructed /// The manager constructed
/// ///
/// Error: std.mem.Allocator.Error /// Error: Allocator.Error
/// std.mem.Allocator.Error.OutOfMemory - The allocator cannot allocate the memory required /// error.OutOfMemory - The allocator cannot allocate the memory required
/// ///
pub fn init(start: usize, end: usize, allocator: *std.mem.Allocator, mapper: Mapper(Payload), payload: Payload) std.mem.Allocator.Error!Self { pub fn init(start: usize, end: usize, allocator: *Allocator, mapper: Mapper(Payload), payload: Payload) Allocator.Error!Self {
const size = end - start; const size = end - start;
var bmp = try bitmap.Bitmap(usize).init(std.mem.alignForward(size, pmm.BLOCK_SIZE) / pmm.BLOCK_SIZE, allocator); var bmp = try bitmap.Bitmap(usize).init(std.mem.alignForward(size, pmm.BLOCK_SIZE) / pmm.BLOCK_SIZE, allocator);
return Self{ return Self{
@ -189,7 +190,7 @@ pub fn VirtualMemoryManager(comptime Payload: type) type {
/// Bitmap(u32).Error.OutOfBounds - The address given is outside of the memory managed /// Bitmap(u32).Error.OutOfBounds - The address given is outside of the memory managed
/// ///
pub fn isSet(self: *const Self, virt: usize) bitmap.Bitmap(u32).BitmapError!bool { pub fn isSet(self: *const Self, virt: usize) bitmap.Bitmap(u32).BitmapError!bool {
return try self.bmp.isSet((virt - self.start) / BLOCK_SIZE); return self.bmp.isSet((virt - self.start) / BLOCK_SIZE);
} }
/// ///
@ -201,22 +202,23 @@ pub fn VirtualMemoryManager(comptime Payload: type) type {
/// IN physical: ?mem.Range - The physical region to map to or null if only the virtual region is to be set /// IN physical: ?mem.Range - The physical region to map to or null if only the virtual region is to be set
/// IN attrs: Attributes - The attributes to apply to the memory regions /// IN attrs: Attributes - The attributes to apply to the memory regions
/// ///
/// Error: VmmError || Bitmap(u32).BitmapError || std.mem.Allocator.Error || MapperError /// Error: VmmError || Bitmap(u32).BitmapError || Allocator.Error || MapperError
/// VmmError.AlreadyAllocated - The virtual address has already been allocated /// VmmError.AlreadyAllocated - The virtual address has already been allocated
/// VmmError.PhysicalAlreadyAllocated - The physical address has already been allocated /// VmmError.PhysicalAlreadyAllocated - The physical address has already been allocated
/// VmmError.PhysicalVirtualMismatch - The physical region and virtual region are of different sizes /// VmmError.PhysicalVirtualMismatch - The physical region and virtual region are of different sizes
/// VmmError.InvalidVirtAddresses - The start virtual address is greater than the end address /// VmmError.InvalidVirtAddresses - The start virtual address is greater than the end address
/// VmmError.InvalidPhysicalAddresses - The start physical address is greater than the end address /// VmmError.InvalidPhysicalAddresses - The start physical address is greater than the end address
/// Bitmap.BitmapError.OutOfBounds - The physical or virtual addresses are out of bounds /// Bitmap.BitmapError.OutOfBounds - The physical or virtual addresses are out of bounds
/// std.mem.Allocator.Error.OutOfMemory - Allocating the required memory failed /// Allocator.Error.OutOfMemory - Allocating the required memory failed
/// MapperError.* - The causes depend on the mapper used /// MapperError.* - The causes depend on the mapper used
/// ///
pub fn set(self: *Self, virtual: mem.Range, physical: ?mem.Range, attrs: Attributes) (VmmError || bitmap.Bitmap(u32).BitmapError || std.mem.Allocator.Error || MapperError)!void { pub fn set(self: *Self, virtual: mem.Range, physical: ?mem.Range, attrs: Attributes) (VmmError || bitmap.Bitmap(u32).BitmapError || Allocator.Error || MapperError)!void {
var virt = virtual.start; var virt = virtual.start;
while (virt < virtual.end) : (virt += BLOCK_SIZE) { while (virt < virtual.end) : (virt += BLOCK_SIZE) {
if (try self.isSet(virt)) if (try self.isSet(virt)) {
return VmmError.AlreadyAllocated; return VmmError.AlreadyAllocated;
} }
}
if (virtual.start > virtual.end) { if (virtual.start > virtual.end) {
return VmmError.InvalidVirtAddresses; return VmmError.InvalidVirtAddresses;
} }
@ -266,12 +268,13 @@ pub fn VirtualMemoryManager(comptime Payload: type) type {
/// Return: ?usize /// Return: ?usize
/// The address at the start of the allocated region, or null if no region could be allocated due to a lack of contiguous blocks. /// The address at the start of the allocated region, or null if no region could be allocated due to a lack of contiguous blocks.
/// ///
/// Error: std.mem.Allocator.Error /// Error: Allocator.Error
/// std.mem.AllocatorError.OutOfMemory: The required amount of memory couldn't be allocated /// error.OutOfMemory: The required amount of memory couldn't be allocated
/// ///
pub fn alloc(self: *Self, num: usize, attrs: Attributes) std.mem.Allocator.Error!?usize { pub fn alloc(self: *Self, num: usize, attrs: Attributes) Allocator.Error!?usize {
if (num == 0) if (num == 0) {
return null; return null;
}
// Ensure that there is both enough physical and virtual address space free // Ensure that there is both enough physical and virtual address space free
if (pmm.blocksFree() >= num and self.bmp.num_free_entries >= num) { if (pmm.blocksFree() >= num and self.bmp.num_free_entries >= num) {
// The virtual address space must be contiguous // The virtual address space must be contiguous
@ -312,19 +315,23 @@ pub fn VirtualMemoryManager(comptime Payload: type) type {
const entry = (vaddr - self.start) / BLOCK_SIZE; const entry = (vaddr - self.start) / BLOCK_SIZE;
if (try self.bmp.isSet(entry)) { if (try self.bmp.isSet(entry)) {
// There will be an allocation associated with this virtual address // There will be an allocation associated with this virtual address
const allocation = self.allocations.get(vaddr) orelse unreachable; const allocation = self.allocations.get(vaddr).?;
const physical = allocation.physical; const physical = allocation.physical;
defer physical.deinit(); defer physical.deinit();
const num_physical_allocations = physical.items.len; const num_physical_allocations = physical.items.len;
for (physical.items) |block, i| { for (physical.items) |block, i| {
// Clear the address space entry and free the physical memory // Clear the address space entry and free the physical memory
try self.bmp.clearEntry(entry + i); try self.bmp.clearEntry(entry + i);
pmm.free(block) catch |e| panic(@errorReturnTrace(), "Failed to free PMM reserved memory at 0x{X}: {}\n", .{ block * BLOCK_SIZE, e }); pmm.free(block) catch |e| {
panic(@errorReturnTrace(), "Failed to free PMM reserved memory at 0x{X}: {}\n", .{ block * BLOCK_SIZE, e });
};
} }
// Unmap the entire range // Unmap the entire range
const region_start = entry * BLOCK_SIZE; const region_start = entry * BLOCK_SIZE;
const region_end = (entry + num_physical_allocations) * BLOCK_SIZE; const region_end = (entry + num_physical_allocations) * BLOCK_SIZE;
self.mapper.unmapFn(region_start, region_end, self.payload) catch |e| panic(@errorReturnTrace(), "Failed to unmap VMM reserved memory from 0x{X} to 0x{X}: {}\n", .{ region_start, region_end, e }); self.mapper.unmapFn(region_start, region_end, self.payload) catch |e| {
panic(@errorReturnTrace(), "Failed to unmap VMM reserved memory from 0x{X} to 0x{X}: {}\n", .{ region_start, region_end, e });
};
// The allocation is freed so remove from the map // The allocation is freed so remove from the map
self.allocations.removeAssertDiscard(vaddr); self.allocations.removeAssertDiscard(vaddr);
} else { } else {
@ -339,31 +346,33 @@ pub fn VirtualMemoryManager(comptime Payload: type) type {
/// ///
/// Arguments: /// Arguments:
/// IN mem_profile: *const mem.MemProfile - The system's memory profile. This is used to find the kernel code region and boot modules /// IN mem_profile: *const mem.MemProfile - The system's memory profile. This is used to find the kernel code region and boot modules
/// IN/OUT allocator: *std.mem.Allocator - The allocator to use when needing to allocate memory /// IN/OUT allocator: *Allocator - The allocator to use when needing to allocate memory
/// ///
/// Return: VirtualMemoryManager /// Return: VirtualMemoryManager
/// The virtual memory manager created with all reserved virtual regions allocated /// The virtual memory manager created with all reserved virtual regions allocated
/// ///
/// Error: std.mem.Allocator.Error /// Error: Allocator.Error
/// std.mem.Allocator.Error.OutOfMemory - The allocator cannot allocate the memory required /// error.OutOfMemory - The allocator cannot allocate the memory required
/// ///
pub fn init(mem_profile: *const mem.MemProfile, allocator: *std.mem.Allocator) std.mem.Allocator.Error!VirtualMemoryManager(arch.VmmPayload) { pub fn init(mem_profile: *const mem.MemProfile, allocator: *Allocator) Allocator.Error!VirtualMemoryManager(arch.VmmPayload) {
std.log.info(.tty, "Init\n", .{}); std.log.info(.vmm, "Init\n", .{});
defer std.log.info(.tty, "Done\n", .{}); defer std.log.info(.vmm, "Done\n", .{});
var vmm = try VirtualMemoryManager(arch.VmmPayload).init(@ptrToInt(&KERNEL_ADDR_OFFSET), 0xFFFFFFFF, allocator, arch.VMM_MAPPER, arch.KERNEL_VMM_PAYLOAD); var vmm = try VirtualMemoryManager(arch.VmmPayload).init(@ptrToInt(&KERNEL_ADDR_OFFSET), 0xFFFFFFFF, allocator, arch.VMM_MAPPER, arch.KERNEL_VMM_PAYLOAD);
// Map in kernel // Map all the reserved virtual addresses.
// Calculate start and end of mapping
const v_start = std.mem.alignBackward(@ptrToInt(mem_profile.vaddr_start), BLOCK_SIZE);
const v_end = std.mem.alignForward(@ptrToInt(mem_profile.vaddr_end) + mem.FIXED_ALLOC_SIZE, BLOCK_SIZE);
const p_start = std.mem.alignBackward(@ptrToInt(mem_profile.physaddr_start), BLOCK_SIZE);
const p_end = std.mem.alignForward(@ptrToInt(mem_profile.physaddr_end) + mem.FIXED_ALLOC_SIZE, BLOCK_SIZE);
vmm.set(.{ .start = v_start, .end = v_end }, mem.Range{ .start = p_start, .end = p_end }, .{ .kernel = true, .writable = false, .cachable = true }) catch |e| panic(@errorReturnTrace(), "Failed mapping kernel code in VMM: {}", .{e});
for (mem_profile.virtual_reserved) |entry| { for (mem_profile.virtual_reserved) |entry| {
const virtual = mem.Range{ .start = std.mem.alignBackward(entry.virtual.start, BLOCK_SIZE), .end = std.mem.alignForward(entry.virtual.end, BLOCK_SIZE) }; const virtual = mem.Range{
const physical: ?mem.Range = if (entry.physical) |phys| mem.Range{ .start = std.mem.alignBackward(phys.start, BLOCK_SIZE), .end = std.mem.alignForward(phys.end, BLOCK_SIZE) } else null; .start = std.mem.alignBackward(entry.virtual.start, BLOCK_SIZE),
.end = std.mem.alignForward(entry.virtual.end, BLOCK_SIZE),
};
const physical: ?mem.Range = if (entry.physical) |phys|
mem.Range{
.start = std.mem.alignBackward(phys.start, BLOCK_SIZE),
.end = std.mem.alignForward(phys.end, BLOCK_SIZE),
}
else
null;
vmm.set(virtual, physical, .{ .kernel = true, .writable = true, .cachable = true }) catch |e| switch (e) { vmm.set(virtual, physical, .{ .kernel = true, .writable = true, .cachable = true }) catch |e| switch (e) {
VmmError.AlreadyAllocated => {}, VmmError.AlreadyAllocated => {},
else => panic(@errorReturnTrace(), "Failed mapping region in VMM {}: {}\n", .{ entry, e }), else => panic(@errorReturnTrace(), "Failed mapping region in VMM {}: {}\n", .{ entry, e }),
@ -380,7 +389,7 @@ pub fn init(mem_profile: *const mem.MemProfile, allocator: *std.mem.Allocator) s
test "alloc and free" { test "alloc and free" {
const num_entries = 512; const num_entries = 512;
var vmm = try testInit(num_entries); var vmm = try testInit(num_entries);
var allocations = test_allocations orelse unreachable; var allocations = test_allocations.?;
var virtual_allocations = std.ArrayList(usize).init(std.testing.allocator); var virtual_allocations = std.ArrayList(usize).init(std.testing.allocator);
defer virtual_allocations.deinit(); defer virtual_allocations.deinit();
@ -399,7 +408,7 @@ test "alloc and free" {
} else { } else {
// Else it should have succeeded and allocated the correct address // Else it should have succeeded and allocated the correct address
std.testing.expectEqual(@as(?usize, vmm.start + entry * BLOCK_SIZE), result); std.testing.expectEqual(@as(?usize, vmm.start + entry * BLOCK_SIZE), result);
try virtual_allocations.append(result orelse unreachable); try virtual_allocations.append(result.?);
} }
// Make sure that the entries are set or not depending on the allocation success // Make sure that the entries are set or not depending on the allocation success
@ -471,7 +480,7 @@ test "set" {
// Make sure it put the correct address in the map // Make sure it put the correct address in the map
std.testing.expect(vmm.allocations.get(vstart) != null); std.testing.expect(vmm.allocations.get(vstart) != null);
var allocations = test_allocations orelse unreachable; var allocations = test_allocations.?;
// The entries before the virtual start shouldn't be set // The entries before the virtual start shouldn't be set
var vaddr = vmm.start; var vaddr = vmm.start;
while (vaddr < vstart) : (vaddr += BLOCK_SIZE) { while (vaddr < vstart) : (vaddr += BLOCK_SIZE) {
@ -499,10 +508,10 @@ var test_mapper = Mapper(u8){ .mapFn = testMap, .unmapFn = testUnmap };
/// Return: VirtualMemoryManager(u8) /// Return: VirtualMemoryManager(u8)
/// The VMM constructed /// The VMM constructed
/// ///
/// Error: std.mem.Allocator.Error /// Error: Allocator.Error
/// OutOfMemory: The allocator couldn't allocate the structures needed /// OutOfMemory: The allocator couldn't allocate the structures needed
/// ///
fn testInit(num_entries: u32) std.mem.Allocator.Error!VirtualMemoryManager(u8) { fn testInit(num_entries: u32) Allocator.Error!VirtualMemoryManager(u8) {
if (test_allocations == null) { if (test_allocations == null) {
test_allocations = try bitmap.Bitmap(u64).init(num_entries, std.heap.page_allocator); test_allocations = try bitmap.Bitmap(u64).init(num_entries, std.heap.page_allocator);
} else |allocations| { } else |allocations| {
@ -524,7 +533,7 @@ fn testInit(num_entries: u32) std.mem.Allocator.Error!VirtualMemoryManager(u8) {
.modules = &[_]mem.Module{}, .modules = &[_]mem.Module{},
}; };
pmm.init(&mem_profile, std.heap.page_allocator); pmm.init(&mem_profile, std.heap.page_allocator);
return try VirtualMemoryManager(u8).init(0, num_entries * BLOCK_SIZE, std.heap.page_allocator, test_mapper, 39); return VirtualMemoryManager(u8).init(0, num_entries * BLOCK_SIZE, std.heap.page_allocator, test_mapper, 39);
} }
/// ///
@ -536,14 +545,14 @@ fn testInit(num_entries: u32) std.mem.Allocator.Error!VirtualMemoryManager(u8) {
/// IN pstart: usize - The start of the physical region to map /// IN pstart: usize - The start of the physical region to map
/// IN pend: usize - The end of the physical region to map /// IN pend: usize - The end of the physical region to map
/// IN attrs: Attributes - The attributes to map with /// IN attrs: Attributes - The attributes to map with
/// IN/OUT allocator: *std.mem.Allocator - The allocator to use. Ignored /// IN/OUT allocator: *Allocator - The allocator to use. Ignored
/// IN payload: u8 - The payload value. Expected to be 39 /// IN payload: u8 - The payload value. Expected to be 39
/// ///
fn testMap(vstart: usize, vend: usize, pstart: usize, pend: usize, attrs: Attributes, allocator: *std.mem.Allocator, payload: u8) (std.mem.Allocator.Error || MapperError)!void { fn testMap(vstart: usize, vend: usize, pstart: usize, pend: usize, attrs: Attributes, allocator: *Allocator, payload: u8) (Allocator.Error || MapperError)!void {
std.testing.expectEqual(@as(u8, 39), payload); std.testing.expectEqual(@as(u8, 39), payload);
var vaddr = vstart; var vaddr = vstart;
while (vaddr < vend) : (vaddr += BLOCK_SIZE) { while (vaddr < vend) : (vaddr += BLOCK_SIZE) {
(test_allocations orelse unreachable).setEntry(vaddr / BLOCK_SIZE) catch unreachable; (test_allocations.?).setEntry(vaddr / BLOCK_SIZE) catch unreachable;
} }
} }
@ -555,11 +564,11 @@ fn testMap(vstart: usize, vend: usize, pstart: usize, pend: usize, attrs: Attrib
/// IN vend: usize - The end of the virtual region to unmap /// IN vend: usize - The end of the virtual region to unmap
/// IN payload: u8 - The payload value. Expected to be 39 /// IN payload: u8 - The payload value. Expected to be 39
/// ///
fn testUnmap(vstart: usize, vend: usize, payload: u8) (std.mem.Allocator.Error || MapperError)!void { fn testUnmap(vstart: usize, vend: usize, payload: u8) (Allocator.Error || MapperError)!void {
std.testing.expectEqual(@as(u8, 39), payload); std.testing.expectEqual(@as(u8, 39), payload);
var vaddr = vstart; var vaddr = vstart;
while (vaddr < vend) : (vaddr += BLOCK_SIZE) { while (vaddr < vend) : (vaddr += BLOCK_SIZE) {
(test_allocations orelse unreachable).clearEntry(vaddr / BLOCK_SIZE) catch unreachable; (test_allocations.?).clearEntry(vaddr / BLOCK_SIZE) catch unreachable;
} }
} }
@ -574,7 +583,7 @@ fn testUnmap(vstart: usize, vend: usize, payload: u8) (std.mem.Allocator.Error |
/// ///
fn runtimeTests(comptime Payload: type, vmm: VirtualMemoryManager(Payload), mem_profile: *const mem.MemProfile) void { fn runtimeTests(comptime Payload: type, vmm: VirtualMemoryManager(Payload), mem_profile: *const mem.MemProfile) void {
const v_start = std.mem.alignBackward(@ptrToInt(mem_profile.vaddr_start), BLOCK_SIZE); const v_start = std.mem.alignBackward(@ptrToInt(mem_profile.vaddr_start), BLOCK_SIZE);
const v_end = std.mem.alignForward(@ptrToInt(mem_profile.vaddr_end) + mem.FIXED_ALLOC_SIZE, BLOCK_SIZE); const v_end = std.mem.alignForward(@ptrToInt(mem_profile.vaddr_end), BLOCK_SIZE);
var vaddr = vmm.start; var vaddr = vmm.start;
while (vaddr < vmm.end - BLOCK_SIZE) : (vaddr += BLOCK_SIZE) { while (vaddr < vmm.end - BLOCK_SIZE) : (vaddr += BLOCK_SIZE) {
@ -597,5 +606,5 @@ fn runtimeTests(comptime Payload: type, vmm: VirtualMemoryManager(Payload), mem_
} }
} }
std.log.info(.tty, "Tested allocations\n", .{}); std.log.info(.vmm, "Tested allocations\n", .{});
} }

View file

@ -122,7 +122,7 @@ pub fn initTTY(boot_payload: BootPayload) TTY {
}; };
} }
pub fn initMem(payload: BootPayload) std.mem.Allocator.Error!mem.MemProfile { pub fn initMem(payload: BootPayload) Allocator.Error!mem.MemProfile {
return MemProfile{ return MemProfile{
.vaddr_end = @ptrCast([*]u8, &KERNEL_VADDR_END), .vaddr_end = @ptrCast([*]u8, &KERNEL_VADDR_END),
.vaddr_start = @ptrCast([*]u8, &KERNEL_VADDR_START), .vaddr_start = @ptrCast([*]u8, &KERNEL_VADDR_START),
@ -142,7 +142,7 @@ pub fn initTaskStack(entry_point: usize, allocator: *Allocator) Allocator.Error!
return ret; return ret;
} }
pub fn init(payload: BootPayload, mem_profile: *const MemProfile, allocator: *Allocator) void { pub fn init(mem_profile: *const MemProfile) void {
// I'll get back to this as this doesn't effect the current testing. // I'll get back to this as this doesn't effect the current testing.
// When I come on to the mem.zig testing, I'll fix :) // When I come on to the mem.zig testing, I'll fix :)
//return mock_framework.performAction("init", void, mem_profile, allocator); //return mock_framework.performAction("init", void, mem_profile, allocator);

View file

@ -28,7 +28,6 @@ pub const MemProfile = struct {
fixed_allocator: std.heap.FixedBufferAllocator, fixed_allocator: std.heap.FixedBufferAllocator,
}; };
// The size of the fixed allocator used before the heap is set up. Set to 1MiB.
const FIXED_ALLOC_SIZE = 1024 * 1024; const FIXED_ALLOC_SIZE = 1024 * 1024;
pub fn virtToPhys(virt: anytype) @TypeOf(virt) { pub fn virtToPhys(virt: anytype) @TypeOf(virt) {

View file

@ -120,6 +120,8 @@ pub const RuntimeStep = struct {
std.debug.warn("{}\n", .{msg}); std.debug.warn("{}\n", .{msg});
if (std.mem.indexOf(u8, msg, "FAILURE")) |_| { if (std.mem.indexOf(u8, msg, "FAILURE")) |_| {
return false; return false;
} else if (std.mem.indexOf(u8, msg, "Kernel panic")) |_| {
return false;
} else if (std.mem.eql(u8, msg, "[info] (kmain): SUCCESS")) { } else if (std.mem.eql(u8, msg, "[info] (kmain): SUCCESS")) {
return true; return true;
} }