Merge pull request #202 from ZystemOS/feature/initrd

Feature/initrd
This commit is contained in:
Edward Dean 2020-07-25 11:23:50 +01:00 committed by GitHub
commit 18284daaa1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 900 additions and 36 deletions

1
.gitignore vendored
View file

@ -27,6 +27,7 @@
*.i*86
*.x86_64
*.hex
*.initrd
# Debug files
*.dSYM/

135
build.zig
View file

@ -2,10 +2,13 @@ const std = @import("std");
const builtin = @import("builtin");
const rt = @import("test/runtime_test.zig");
const RuntimeStep = rt.RuntimeStep;
const Allocator = std.mem.Allocator;
const Builder = std.build.Builder;
const Step = std.build.Step;
const Target = std.Target;
const CrossTarget = std.zig.CrossTarget;
const fs = std.fs;
const File = fs.File;
const Mode = builtin.Mode;
const TestMode = rt.TestMode;
const ArrayList = std.ArrayList;
@ -38,6 +41,7 @@ pub fn build(b: *Builder) !void {
const iso_dir_path = try fs.path.join(b.allocator, &[_][]const u8{ b.exe_dir, "iso" });
const boot_path = try fs.path.join(b.allocator, &[_][]const u8{ b.exe_dir, "iso", "boot" });
const modules_path = try fs.path.join(b.allocator, &[_][]const u8{ b.exe_dir, "iso", "modules" });
const ramdisk_path = try fs.path.join(b.allocator, &[_][]const u8{ b.install_path, "initrd.ramdisk" });
const build_mode = b.standardReleaseOptions();
comptime var test_mode_desc: []const u8 = "\n ";
@ -59,11 +63,23 @@ pub fn build(b: *Builder) !void {
exec.setTarget(target);
const make_iso = switch (target.getCpuArch()) {
.i386 => b.addSystemCommand(&[_][]const u8{ "./makeiso.sh", boot_path, modules_path, iso_dir_path, exec.getOutputPath(), output_iso }),
.i386 => b.addSystemCommand(&[_][]const u8{ "./makeiso.sh", boot_path, modules_path, iso_dir_path, exec.getOutputPath(), ramdisk_path, output_iso }),
else => unreachable,
};
make_iso.step.dependOn(&exec.step);
var ramdisk_files_al = ArrayList([]const u8).init(b.allocator);
defer ramdisk_files_al.deinit();
// Add some test files for the ramdisk runtime tests
if (test_mode == .Initialisation) {
try ramdisk_files_al.append("test/ramdisk_test1.txt");
try ramdisk_files_al.append("test/ramdisk_test2.txt");
}
const ramdisk_step = RamdiskStep.create(b, target, ramdisk_files_al.toOwnedSlice(), ramdisk_path);
make_iso.step.dependOn(&ramdisk_step.step);
b.default_step.dependOn(&make_iso.step);
const test_step = b.step("test", "Run tests");
@ -149,3 +165,120 @@ pub fn build(b: *Builder) !void {
});
debug_step.dependOn(&debug_cmd.step);
}
/// The ramdisk make step for creating the initial ramdisk.
const RamdiskStep = struct {
/// The Step, that is all you need to know
step: Step,
/// The builder pointer, also all you need to know
builder: *Builder,
/// The target for the build
target: CrossTarget,
/// The list of files to be added to the ramdisk
files: []const []const u8,
/// The path to where the ramdisk will be written to.
out_file_path: []const u8,
/// The possible errors for creating a ramdisk
const Error = (error{EndOfStream} || File.ReadError || File.GetPosError || Allocator.Error || File.WriteError || File.OpenError);
///
/// Create and write the files to a raw ramdisk in the format:
/// (NumOfFiles:usize)[(name_length:usize)(name:u8[name_length])(content_length:usize)(content:u8[content_length])]*
///
/// Argument:
/// IN comptime Usize: type - The usize type for the architecture.
/// IN self: *RamdiskStep - Self.
///
/// Error: Error
/// Errors for opening, reading and writing to and from files and for allocating memory.
///
fn writeRamdisk(comptime Usize: type, self: *RamdiskStep) Error!void {
// 1MB, don't think the ram disk should be very big
const max_file_size = 1024 * 1024 * 1024;
// Open the out file
var ramdisk = try fs.cwd().createFile(self.out_file_path, .{});
defer ramdisk.close();
// Get the targets endian
const endian = self.target.getCpuArch().endian();
// First write the number of files/headers
std.debug.assert(self.files.len < std.math.maxInt(Usize));
try ramdisk.writer().writeInt(Usize, @truncate(Usize, self.files.len), endian);
var current_offset: usize = 0;
for (self.files) |file_path| {
// Open, and read the file. Can get the size from this as well
const file_content = try fs.cwd().readFileAlloc(self.builder.allocator, file_path, max_file_size);
// Get the last occurrence of / for the file name, if there isn't one, then the file_path is the name
const file_name_index = if (std.mem.lastIndexOf(u8, file_path, "/")) |index| index + 1 else 0;
// Write the header and file content to the ramdisk
// Name length
std.debug.assert(file_path[file_name_index..].len < std.math.maxInt(Usize));
try ramdisk.writer().writeInt(Usize, @truncate(Usize, file_path[file_name_index..].len), endian);
// Name
try ramdisk.writer().writeAll(file_path[file_name_index..]);
// Length
std.debug.assert(file_content.len < std.math.maxInt(Usize));
try ramdisk.writer().writeInt(Usize, @truncate(Usize, file_content.len), endian);
// File contest
try ramdisk.writer().writeAll(file_content);
// Increment the offset to the new location
current_offset += @sizeOf(Usize) * 3 + file_path[file_name_index..].len + file_content.len;
}
}
///
/// The make function that is called by the builder. This will create the qemu process with the
/// stdout as a Pipe. Then create the read thread to read the logs from the qemu stdout. Then
/// will call the test function to test a specifics part of the OS defined by the test mode.
///
/// Arguments:
/// IN step: *Step - The step of this step.
///
/// Error: Error
/// Errors for opening, reading and writing to and from files and for allocating memory.
///
fn make(step: *Step) Error!void {
const self = @fieldParentPtr(RamdiskStep, "step", step);
switch (self.target.getCpuArch()) {
.i386 => try writeRamdisk(u32, self),
else => unreachable,
}
}
///
/// Create a ramdisk step.
///
/// Argument:
/// IN builder: *Builder - The build builder.
/// IN target: CrossTarget - The target for the build.
/// IN files: []const []const u8 - The file names to be added to the ramdisk.
/// IN out_file_path: []const u8 - The output file path.
///
/// Return: *RamdiskStep
/// The ramdisk step pointer to add to the build process.
///
pub fn create(builder: *Builder, target: CrossTarget, files: []const []const u8, out_file_path: []const u8) *RamdiskStep {
const ramdisk_step = builder.allocator.create(RamdiskStep) catch unreachable;
ramdisk_step.* = .{
.step = Step.init(.Custom, builder.fmt("Ramdisk", .{}), builder.allocator, make),
.builder = builder,
.target = target,
.files = files,
.out_file_path = out_file_path,
};
return ramdisk_step;
}
};

View file

@ -4,5 +4,6 @@ set default=0
menuentry "pluto" {
multiboot /boot/pluto.elf
module /modules/kernel.map kernel.map
module /modules/initrd.ramdisk initrd.ramdisk
boot
}

View file

@ -4,7 +4,8 @@ BOOT_DIR=$1
MODULES_DIR=$2
ISO_DIR=$3
PLUTO_ELF=$4
OUTPUT_FILE=$5
RAMDISK=$5
OUTPUT_FILE=$6
MAP_FILE=$MODULES_DIR/'kernel.map'
@ -23,6 +24,7 @@ mkdir -p $MODULES_DIR
cp -r grub $BOOT_DIR
cp $PLUTO_ELF $BOOT_DIR/"pluto.elf"
cp $RAMDISK $MODULES_DIR/"initrd.ramdisk"
# Read the symbols from the binary, remove all the unnecessary columns with awk and emit to a map file
readelf -s --wide $PLUTO_ELF | grep -F "FUNC" | awk '{$1=$3=$4=$5=$6=$7=""; print $0}' | sort -k 1 > $MAP_FILE

View file

@ -14,6 +14,7 @@ const tty = @import("../../tty.zig");
const mem = @import("../../mem.zig");
const vmm = @import("../../vmm.zig");
const multiboot = @import("multiboot.zig");
const Allocator = std.mem.Allocator;
/// An array of directory entries and page tables. Forms the first level of paging and covers the entire 4GB memory space.
pub const Directory = packed struct {
@ -169,15 +170,15 @@ inline fn clearAttribute(val: *align(1) u32, attr: u32) void {
/// IN allocator: *Allocator - The allocator to use to map any tables needed
/// OUT dir: *Directory - The directory that this entry is in
///
/// Error: vmm.MapperError || std.mem.Allocator.Error
/// Error: vmm.MapperError || Allocator.Error
/// vmm.MapperError.InvalidPhysicalAddress - The physical start address is greater than the end
/// vmm.MapperError.InvalidVirtualAddress - The virtual start address is greater than the end or is larger than 4GB
/// vmm.MapperError.AddressMismatch - The differences between the virtual addresses and the physical addresses aren't the same
/// vmm.MapperError.MisalignedPhysicalAddress - One or both of the physical addresses aren't page size aligned
/// vmm.MapperError.MisalignedVirtualAddress - One or both of the virtual addresses aren't page size aligned
/// std.mem.Allocator.Error.* - See std.mem.Allocator.alignedAlloc
/// Allocator.Error.* - See Allocator.alignedAlloc
///
fn mapDirEntry(dir: *Directory, virt_start: usize, virt_end: usize, phys_start: usize, phys_end: usize, attrs: vmm.Attributes, allocator: *std.mem.Allocator) (vmm.MapperError || std.mem.Allocator.Error)!void {
fn mapDirEntry(dir: *Directory, virt_start: usize, virt_end: usize, phys_start: usize, phys_end: usize, attrs: vmm.Attributes, allocator: *Allocator) (vmm.MapperError || Allocator.Error)!void {
if (phys_start > phys_end) {
return vmm.MapperError.InvalidPhysicalAddress;
}
@ -301,13 +302,13 @@ fn mapTableEntry(entry: *align(1) TableEntry, phys_addr: usize, attrs: vmm.Attri
/// IN physical_start: usize - The start of the physical region to map to
/// IN physical_end: usize - The end (exclusive) of the physical region to map to
/// IN attrs: vmm.Attributes - The attributes to apply to this mapping
/// IN/OUT allocator: *std.mem.Allocator - The allocator to use to allocate any intermediate data structures required to map this region
/// IN/OUT allocator: *Allocator - The allocator to use to allocate any intermediate data structures required to map this region
/// IN/OUT dir: *Directory - The page directory to map within
///
/// Error: vmm.MapperError || std.mem.Allocator.Error
/// Error: vmm.MapperError || Allocator.Error
/// * - See mapDirEntry
///
pub fn map(virt_start: usize, virt_end: usize, phys_start: usize, phys_end: usize, attrs: vmm.Attributes, allocator: *std.mem.Allocator, dir: *Directory) (std.mem.Allocator.Error || vmm.MapperError)!void {
pub fn map(virt_start: usize, virt_end: usize, phys_start: usize, phys_end: usize, attrs: vmm.Attributes, allocator: *Allocator, dir: *Directory) (Allocator.Error || vmm.MapperError)!void {
var virt_addr = virt_start;
var phys_addr = phys_start;
var page = virt_addr / PAGE_SIZE_4KB;
@ -329,10 +330,10 @@ pub fn map(virt_start: usize, virt_end: usize, phys_start: usize, phys_end: usiz
/// IN virtual_end: usize - The end (exclusive) of the virtual region to unmap
/// IN/OUT dir: *Directory - The page directory to unmap within
///
/// Error: std.mem.Allocator.Error || vmm.MapperError
/// Error: Allocator.Error || vmm.MapperError
/// vmm.MapperError.NotMapped - If the region being unmapped wasn't mapped in the first place
///
pub fn unmap(virtual_start: usize, virtual_end: usize, dir: *Directory) (std.mem.Allocator.Error || vmm.MapperError)!void {
pub fn unmap(virtual_start: usize, virtual_end: usize, dir: *Directory) (Allocator.Error || vmm.MapperError)!void {
var virt_addr = virtual_start;
var page = virt_addr / PAGE_SIZE_4KB;
var entry_idx = virt_addr / PAGE_SIZE_4MB;

675
src/kernel/initrd.zig Normal file
View file

@ -0,0 +1,675 @@
const std = @import("std");
const builtin = @import("builtin");
const is_test = builtin.is_test;
const expect = std.testing.expect;
const expectEqual = std.testing.expectEqual;
const expectError = std.testing.expectError;
const expectEqualSlices = std.testing.expectEqualSlices;
const build_options = @import("build_options");
const mock_path = build_options.mock_path;
const Allocator = std.mem.Allocator;
const AutoHashMap = std.AutoHashMap;
const vfs = @import("vfs.zig");
const mem = if (is_test) @import(mock_path ++ "mem_mock.zig") else @import("mem.zig");
const panic = if (is_test) @import(mock_path ++ "panic_mock.zig").panic else @import("panic.zig").panic;
/// The Initrd file system struct.
/// Format of raw ramdisk:
/// (NumOfFiles:usize)[(name_length:usize)(name:u8[name_length])(content_length:usize)(content:u8[content_length])]*
pub const InitrdFS = struct {
/// The ramdisk header that stores pointers to the raw ramdisk for the name and file content.
/// As the ramdisk is read only, these can be const pointers.
const InitrdHeader = struct {
/// The name of the file
name: []const u8,
/// The content of the file
content: []const u8,
};
/// The error set for the ramdisk file system.
const Error = error{
/// The error for an invalid raw ramdisk when
/// parsing.
InvalidRamDisk,
};
const Self = @This();
/// A mapping of opened files so can easily retrieved opened files for reading.
opened_files: AutoHashMap(*const vfs.Node, *InitrdHeader),
/// The underlying file system
fs: *vfs.FileSystem,
/// The allocator used for allocating memory for opening and reading.
allocator: *Allocator,
/// The list of files in the ram disk. These will be pointers into the raw ramdisk to save on
/// allocations.
files: []InitrdHeader,
/// The root node for the ramdisk file system. This is just a root directory as there is not
/// subdirectories.
root_node: *vfs.Node,
/// See vfs.FileSystem.instance
instance: usize,
/// See vfs.FileSystem.getRootNode
fn getRootNode(fs: *const vfs.FileSystem) *const vfs.DirNode {
var self = @fieldParentPtr(InitrdFS, "instance", fs.instance);
return &self.root_node.Dir;
}
/// See vfs.FileSystem.close
fn close(fs: *const vfs.FileSystem, node: *const vfs.FileNode) void {
var self = @fieldParentPtr(InitrdFS, "instance", fs.instance);
const cast_node = @ptrCast(*const vfs.Node, node);
// As close can't error, if provided with a invalid Node that isn't opened or try to close
// the same file twice, will just do nothing.
if (self.opened_files.contains(cast_node)) {
_ = self.opened_files.remove(cast_node);
self.allocator.destroy(node);
}
}
/// See vfs.FileSystem.read
fn read(fs: *const vfs.FileSystem, node: *const vfs.FileNode, len: usize) (Allocator.Error || vfs.Error)![]u8 {
var self = @fieldParentPtr(InitrdFS, "instance", fs.instance);
const cast_node = @ptrCast(*const vfs.Node, node);
const file_header = self.opened_files.get(cast_node) orelse return vfs.Error.NotOpened;
const length = std.math.min(len, file_header.content.len);
const buff = try self.allocator.alloc(u8, length);
std.mem.copy(u8, buff, file_header.content[0..length]);
return buff;
}
/// See vfs.FileSystem.write
fn write(fs: *const vfs.FileSystem, node: *const vfs.FileNode, bytes: []const u8) (Allocator.Error || vfs.Error)!void {}
/// See vfs.FileSystem.open
fn open(fs: *const vfs.FileSystem, dir: *const vfs.DirNode, name: []const u8, flags: vfs.OpenFlags) (Allocator.Error || vfs.Error)!*vfs.Node {
var self = @fieldParentPtr(InitrdFS, "instance", fs.instance);
switch (flags) {
.CREATE_DIR, .CREATE_FILE => return vfs.Error.InvalidFlags,
.NO_CREATION => {
for (self.files) |*file| {
if (std.mem.eql(u8, file.name, name)) {
// Opening 2 files of the same name, will create 2 different Nodes
// Create a node
var file_node = try self.allocator.create(vfs.Node);
file_node.* = .{ .File = .{ .fs = self.fs } };
try self.opened_files.put(file_node, file);
return file_node;
}
}
return vfs.Error.NoSuchFileOrDir;
},
}
}
///
/// Free all memory allocated.
///
/// Arguments:
/// IN self: *Self - Self
///
pub fn deinit(self: *Self) void {
// If there are any files open, then we have a error.
std.debug.assert(self.opened_files.items().len == 0);
self.allocator.destroy(self.root_node);
self.allocator.destroy(self.fs);
self.opened_files.deinit();
self.allocator.free(self.files);
self.allocator.destroy(self);
}
///
/// Initialise a ramdisk file system from a raw ramdisk in memory provided by the bootloader.
/// Any memory allocated will be freed.
///
/// Arguments:
/// IN rd_module: mem.Module - The bootloader module that contains the raw ramdisk.
/// IN allocator: *Allocator - The allocator used for initialising any memory needed.
///
/// Return: *InitrdFS
/// A pointer to the ram disk file system.
///
/// Error: Allocator.Error || Error
/// error.OutOfMemory - If there isn't enough memory for initialisation. Any memory
/// allocated will be freed.
/// error.InvalidRamDisk - If the provided raw ramdisk is invalid. This can be due to a
/// mis-match of the number of files to the length of the raw
/// ramdisk or the wrong length provided to cause undefined parsed
/// lengths for other parts of the ramdisk.
///
pub fn init(rd_module: mem.Module, allocator: *Allocator) (Allocator.Error || Error)!*InitrdFS {
std.log.info(.initrd, "Init\n", .{});
defer std.log.info(.initrd, "Done\n", .{});
const rd_len: usize = rd_module.region.end - rd_module.region.start;
const ramdisk_bytes = @intToPtr([*]u8, rd_module.region.start)[0..rd_len];
// First @sizeOf(usize) bytes is the number of files
const num_of_files = std.mem.readIntSlice(usize, ramdisk_bytes[0..], builtin.endian);
var headers = try allocator.alloc(InitrdHeader, num_of_files);
errdefer allocator.free(headers);
// Populate the headers
var i: usize = 0;
// Keep track of the offset into the ramdisk memory
var current_offset: usize = @sizeOf(usize);
while (i < num_of_files) : (i += 1) {
// We don't need to store the name length any more as we have the name.len
const name_len = std.mem.readIntSlice(usize, ramdisk_bytes[current_offset..], builtin.endian);
current_offset += @sizeOf(usize);
if (current_offset >= rd_len) {
return Error.InvalidRamDisk;
}
headers[i].name = ramdisk_bytes[current_offset .. current_offset + name_len];
current_offset += name_len;
if (current_offset >= rd_len) {
return Error.InvalidRamDisk;
}
const content_len = std.mem.readIntSlice(usize, ramdisk_bytes[current_offset..], builtin.endian);
current_offset += @sizeOf(usize);
if (current_offset >= rd_len) {
return Error.InvalidRamDisk;
}
headers[i].content = ramdisk_bytes[current_offset .. current_offset + content_len];
current_offset += content_len;
// We could be at the end of the ramdisk, so only need to check for grater than.
if (current_offset > rd_len) {
return Error.InvalidRamDisk;
}
}
// Make sure we are at the end
if (current_offset != rd_len) {
return Error.InvalidRamDisk;
}
var rd_fs = try allocator.create(InitrdFS);
errdefer allocator.destroy(rd_fs);
var fs = try allocator.create(vfs.FileSystem);
errdefer allocator.destroy(fs);
var root_node = try allocator.create(vfs.Node);
root_node.* = .{ .Dir = .{ .fs = fs, .mount = null } };
fs.* = .{ .open = open, .close = close, .read = read, .write = write, .instance = &rd_fs.instance, .getRootNode = getRootNode };
rd_fs.* = .{
.opened_files = AutoHashMap(*const vfs.Node, *InitrdHeader).init(allocator),
.fs = fs,
.allocator = allocator,
.files = headers,
.root_node = root_node,
.instance = 1,
};
switch (build_options.test_mode) {
.Initialisation => runtimeTests(rd_fs),
else => {},
}
return rd_fs;
}
};
///
/// Crate a raw ramdisk in memory to be used to initialise the ramdisk file system. This create
/// three files: test1.txt, test2.txt and test3.txt.
///
/// Arguments:
/// IN allocator: *Allocator - The allocator to alloc the raw ramdisk.
///
/// Return: []u8
/// The bytes of the raw ramdisk in memory.
///
/// Error: Allocator.Error
/// error.OutOfMemory - If there isn't enough memory for the in memory ramdisk.
///
fn createInitrd(allocator: *Allocator) Allocator.Error![]u8 {
// Create 3 valid ramdisk files in memory
const file_names = [_][]const u8{ "test1.txt", "test2.txt", "test3.txt" };
const file_contents = [_][]const u8{ "This is a test", "This is a test: part 2", "This is a test: the prequel" };
var sum: usize = 0;
const files_length = for ([_]usize{ 0, 1, 2 }) |i| {
sum += @sizeOf(usize) + file_names[i].len + @sizeOf(usize) + file_contents[i].len;
} else sum;
const total_ramdisk_len = @sizeOf(usize) + files_length;
var ramdisk_mem = try allocator.alloc(u8, total_ramdisk_len);
// Copy the data into the allocated memory
std.mem.writeIntSlice(usize, ramdisk_mem[0..], 3, builtin.endian);
var current_offset: usize = @sizeOf(usize);
inline for ([_]usize{ 0, 1, 2 }) |i| {
// Name len
std.mem.writeIntSlice(usize, ramdisk_mem[current_offset..], file_names[i].len, builtin.endian);
current_offset += @sizeOf(usize);
// Name
std.mem.copy(u8, ramdisk_mem[current_offset..], file_names[i]);
current_offset += file_names[i].len;
// File len
std.mem.writeIntSlice(usize, ramdisk_mem[current_offset..], file_contents[i].len, builtin.endian);
current_offset += @sizeOf(usize);
// File content
std.mem.copy(u8, ramdisk_mem[current_offset..], file_contents[i]);
current_offset += file_contents[i].len;
}
// Make sure we are full
expectEqual(current_offset, total_ramdisk_len);
return ramdisk_mem;
}
test "init with files valid" {
var ramdisk_mem = try createInitrd(std.testing.allocator);
defer std.testing.allocator.free(ramdisk_mem);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len };
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" };
var fs = try InitrdFS.init(ramdisk_module, std.testing.allocator);
defer fs.deinit();
expectEqual(fs.files.len, 3);
expectEqualSlices(u8, fs.files[0].name, "test1.txt");
expectEqualSlices(u8, fs.files[1].content, "This is a test: part 2");
expectEqual(fs.opened_files.items().len, 0);
}
test "init with files invalid - invalid number of files" {
var ramdisk_mem = try createInitrd(std.testing.allocator);
// Override the number of files
std.mem.writeIntSlice(usize, ramdisk_mem[0..], 10, builtin.endian);
defer std.testing.allocator.free(ramdisk_mem);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len };
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" };
expectError(error.InvalidRamDisk, InitrdFS.init(ramdisk_module, std.testing.allocator));
// Override the number of files
std.mem.writeIntSlice(usize, ramdisk_mem[0..], 0, builtin.endian);
expectError(error.InvalidRamDisk, InitrdFS.init(ramdisk_module, std.testing.allocator));
}
test "init with files invalid - mix - bad" {
// TODO: Craft a ramdisk that would parse but is invalid
// This is possible, but will think about this another time
// Challenge, make this a effective security vulnerability
// P.S. I don't know if adding magics will stop this
{
var ramdisk_mem = try createInitrd(std.testing.allocator);
// Override the first file name length, make is shorter
std.mem.writeIntSlice(usize, ramdisk_mem[4..], 2, builtin.endian);
defer std.testing.allocator.free(ramdisk_mem);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len };
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" };
expectError(error.InvalidRamDisk, InitrdFS.init(ramdisk_module, std.testing.allocator));
}
{
var ramdisk_mem = try createInitrd(std.testing.allocator);
// Override the first file name length, make is 4 shorter
std.mem.writeIntSlice(usize, ramdisk_mem[4..], 5, builtin.endian);
// Override the second file name length, make is 4 longer
std.mem.writeIntSlice(usize, ramdisk_mem[35..], 13, builtin.endian);
defer std.testing.allocator.free(ramdisk_mem);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len };
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" };
expectError(error.InvalidRamDisk, InitrdFS.init(ramdisk_module, std.testing.allocator));
}
}
test "init with files cleans memory if OutOfMemory" {
// There are 4 allocations
for ([_]usize{ 0, 1, 2, 3 }) |i| {
{
var fa = std.testing.FailingAllocator.init(std.testing.allocator, i);
var ramdisk_mem = try createInitrd(std.testing.allocator);
defer std.testing.allocator.free(ramdisk_mem);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len };
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" };
expectError(error.OutOfMemory, InitrdFS.init(ramdisk_module, &fa.allocator));
}
// Ensure we have freed any memory allocated
try std.testing.allocator_instance.validate();
}
}
test "getRootNode" {
var ramdisk_mem = try createInitrd(std.testing.allocator);
defer std.testing.allocator.free(ramdisk_mem);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len };
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" };
var fs = try InitrdFS.init(ramdisk_module, std.testing.allocator);
defer fs.deinit();
expectEqual(fs.fs.getRootNode(fs.fs), &fs.root_node.Dir);
}
test "open valid file" {
var ramdisk_mem = try createInitrd(std.testing.allocator);
defer std.testing.allocator.free(ramdisk_mem);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len };
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" };
var fs = try InitrdFS.init(ramdisk_module, std.testing.allocator);
defer fs.deinit();
vfs.setRoot(fs.root_node);
var file1 = try vfs.openFile("/test1.txt", .NO_CREATION);
defer file1.close();
var file1_node = @ptrCast(*const vfs.Node, file1);
expectEqual(fs.opened_files.items().len, 1);
expectEqualSlices(u8, fs.opened_files.get(file1_node).?.name, "test1.txt");
var file3_node = try vfs.open("/test3.txt", .NO_CREATION);
defer file3_node.File.close();
expectEqual(fs.opened_files.items().len, 2);
expectEqualSlices(u8, fs.opened_files.get(file3_node).?.content, "This is a test: the prequel");
var dir1 = try vfs.openDir("/", .NO_CREATION);
expectEqual(&fs.root_node.Dir, dir1);
var file2 = &(try dir1.open("test2.txt", .NO_CREATION)).File;
defer file2.close();
expectEqual(fs.opened_files.items().len, 3);
}
test "open fail with invalid flags" {
var ramdisk_mem = try createInitrd(std.testing.allocator);
defer std.testing.allocator.free(ramdisk_mem);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len };
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" };
var fs = try InitrdFS.init(ramdisk_module, std.testing.allocator);
defer fs.deinit();
vfs.setRoot(fs.root_node);
expectError(error.InvalidFlags, vfs.openFile("/text10.txt", .CREATE_DIR));
expectError(error.InvalidFlags, vfs.openFile("/text10.txt", .CREATE_FILE));
expectError(error.InvalidFlags, vfs.openDir("/text10.txt", .CREATE_DIR));
expectError(error.InvalidFlags, vfs.openDir("/text10.txt", .CREATE_FILE));
expectError(error.InvalidFlags, vfs.openFile("/test/", .CREATE_DIR));
expectError(error.InvalidFlags, vfs.openFile("/test/", .CREATE_FILE));
expectError(error.InvalidFlags, vfs.openDir("/test/", .CREATE_DIR));
expectError(error.InvalidFlags, vfs.openDir("/test/", .CREATE_FILE));
}
test "open fail with NoSuchFileOrDir" {
expectError(error.NoSuchFileOrDir, vfs.openFile("/text10.txt", .NO_CREATION));
expectError(error.NoSuchFileOrDir, vfs.openDir("/temp/", .NO_CREATION));
}
test "open a file, out of memory" {
var fa = std.testing.FailingAllocator.init(std.testing.allocator, 4);
var ramdisk_mem = try createInitrd(std.testing.allocator);
defer std.testing.allocator.free(ramdisk_mem);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len };
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" };
var fs = try InitrdFS.init(ramdisk_module, &fa.allocator);
defer fs.deinit();
vfs.setRoot(fs.root_node);
expectError(error.OutOfMemory, vfs.openFile("/test1.txt", .NO_CREATION));
}
test "open two of the same file" {
var ramdisk_mem = try createInitrd(std.testing.allocator);
defer std.testing.allocator.free(ramdisk_mem);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len };
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" };
var fs = try InitrdFS.init(ramdisk_module, std.testing.allocator);
defer fs.deinit();
vfs.setRoot(fs.root_node);
const file1 = try vfs.openFile("/test1.txt", .NO_CREATION);
defer file1.close();
const file2 = try vfs.openFile("/test1.txt", .NO_CREATION);
defer file2.close();
expectEqual(fs.opened_files.items().len, 2);
expect(file1 != file2);
const b1 = try file1.read(128);
defer std.testing.allocator.free(b1);
const b2 = try file2.read(128);
defer std.testing.allocator.free(b2);
expectEqualSlices(u8, b1, b2);
}
test "close a file" {
var ramdisk_mem = try createInitrd(std.testing.allocator);
defer std.testing.allocator.free(ramdisk_mem);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len };
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" };
var fs = try InitrdFS.init(ramdisk_module, std.testing.allocator);
defer fs.deinit();
vfs.setRoot(fs.root_node);
var file1 = try vfs.openFile("/test1.txt", .NO_CREATION);
var file1_node = @ptrCast(*const vfs.Node, file1);
expectEqual(fs.opened_files.items().len, 1);
var file3_node = try vfs.open("/test3.txt", .NO_CREATION);
expectEqual(fs.opened_files.items().len, 2);
file1.close();
expectEqual(fs.opened_files.items().len, 1);
var dir1 = try vfs.openDir("/", .NO_CREATION);
expectEqual(&fs.root_node.Dir, dir1);
var file2 = &(try dir1.open("test2.txt", .NO_CREATION)).File;
defer file2.close();
expectEqual(fs.opened_files.items().len, 2);
file3_node.File.close();
expectEqual(fs.opened_files.items().len, 1);
}
test "close a non-opened file" {
var ramdisk_mem = try createInitrd(std.testing.allocator);
defer std.testing.allocator.free(ramdisk_mem);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len };
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" };
var fs = try InitrdFS.init(ramdisk_module, std.testing.allocator);
defer fs.deinit();
vfs.setRoot(fs.root_node);
// Open a valid file
var file1 = try vfs.openFile("/test1.txt", .NO_CREATION);
defer file1.close();
// Only one file open
expectEqual(fs.opened_files.items().len, 1);
// Craft a Node
var fake_node = try std.testing.allocator.create(vfs.Node);
defer std.testing.allocator.destroy(fake_node);
fake_node.* = .{ .File = .{ .fs = fs.fs } };
fake_node.File.close();
// Still only one file open
expectEqual(fs.opened_files.items().len, 1);
}
test "read a file" {
var ramdisk_mem = try createInitrd(std.testing.allocator);
defer std.testing.allocator.free(ramdisk_mem);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len };
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" };
var fs = try InitrdFS.init(ramdisk_module, std.testing.allocator);
defer fs.deinit();
vfs.setRoot(fs.root_node);
var file1 = try vfs.openFile("/test1.txt", .NO_CREATION);
defer file1.close();
const bytes1 = try file1.read(128);
defer std.testing.allocator.free(bytes1);
expectEqualSlices(u8, bytes1, "This is a test");
const bytes2 = try file1.read(5);
defer std.testing.allocator.free(bytes2);
expectEqualSlices(u8, bytes2, "This ");
}
test "read a file, out of memory" {
var fa = std.testing.FailingAllocator.init(std.testing.allocator, 6);
var ramdisk_mem = try createInitrd(std.testing.allocator);
defer std.testing.allocator.free(ramdisk_mem);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len };
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" };
var fs = try InitrdFS.init(ramdisk_module, &fa.allocator);
defer fs.deinit();
vfs.setRoot(fs.root_node);
var file1 = try vfs.openFile("/test1.txt", .NO_CREATION);
defer file1.close();
expectError(error.OutOfMemory, file1.read(1));
}
test "read a file, invalid/not opened/crafted *const Node" {
var ramdisk_mem = try createInitrd(std.testing.allocator);
defer std.testing.allocator.free(ramdisk_mem);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len };
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" };
var fs = try InitrdFS.init(ramdisk_module, std.testing.allocator);
defer fs.deinit();
vfs.setRoot(fs.root_node);
// Open a valid file
var file1 = try vfs.openFile("/test1.txt", .NO_CREATION);
defer file1.close();
// Only one file open
expectEqual(fs.opened_files.items().len, 1);
// Craft a Node
var fake_node = try std.testing.allocator.create(vfs.Node);
defer std.testing.allocator.destroy(fake_node);
fake_node.* = .{ .File = .{ .fs = fs.fs } };
expectError(error.NotOpened, fake_node.File.read(128));
// Still only one file open
expectEqual(fs.opened_files.items().len, 1);
}
test "write does nothing" {
var ramdisk_mem = try createInitrd(std.testing.allocator);
defer std.testing.allocator.free(ramdisk_mem);
const ramdisk_range = .{ .start = @ptrToInt(ramdisk_mem.ptr), .end = @ptrToInt(ramdisk_mem.ptr) + ramdisk_mem.len };
const ramdisk_module = .{ .region = ramdisk_range, .name = "ramdisk.initrd" };
var fs = try InitrdFS.init(ramdisk_module, std.testing.allocator);
defer fs.deinit();
vfs.setRoot(fs.root_node);
// Open a valid file
var file1 = try vfs.openFile("/test1.txt", .NO_CREATION);
defer file1.close();
try file1.write("Blah");
// Unchanged file content
expectEqualSlices(u8, fs.opened_files.get(@ptrCast(*const vfs.Node, file1)).?.content, "This is a test");
}
/// See std.testing.expectEqualSlices. As need our panic.
fn expectEqualSlicesClone(comptime T: type, expected: []const T, actual: []const T) void {
if (expected.len != actual.len) {
panic(@errorReturnTrace(), "slice lengths differ. expected {}, found {}", .{ expected.len, actual.len });
}
var i: usize = 0;
while (i < expected.len) : (i += 1) {
if (!std.meta.eql(expected[i], actual[i])) {
panic(@errorReturnTrace(), "index {} incorrect. expected {}, found {}", .{ i, expected[i], actual[i] });
}
}
}
///
/// Test that we can open, read and close a file
///
/// Arguments:
/// IN allocator: *Allocator - The allocator used for reading.
///
fn rt_openReadClose(allocator: *Allocator) void {
const f1 = vfs.openFile("/ramdisk_test1.txt", .NO_CREATION) catch |e| {
panic(@errorReturnTrace(), "FAILURE: Failed to open file: {}\n", .{e});
};
const bytes1 = f1.read(128) catch |e| {
panic(@errorReturnTrace(), "FAILURE: Failed to read file: {}\n", .{e});
};
defer f1.close();
expectEqualSlicesClone(u8, bytes1, "Testing ram disk");
const f2 = vfs.openFile("/ramdisk_test2.txt", .NO_CREATION) catch |e| {
panic(@errorReturnTrace(), "Failed to open file: {}\n", .{e});
};
const bytes2 = f2.read(128) catch |e| {
panic(@errorReturnTrace(), "FAILURE: Failed to read file: {}\n", .{e});
};
defer f2.close();
expectEqualSlicesClone(u8, bytes2, "Testing ram disk for the second time");
// Try open a non-existent file
_ = vfs.openFile("/nope.txt", .NO_CREATION) catch |e| switch (e) {
error.NoSuchFileOrDir => {},
else => panic(@errorReturnTrace(), "FAILURE: Expected error\n", .{}),
};
std.log.info(.initrd, "Opened, read and closed\n", .{});
}
///
/// The ramdisk runtime tests that will test the ramdisks functionality.
///
/// Arguments:
/// IN rd_fs: *InitrdFS - The initialised ramdisk to play with.
///
fn runtimeTests(rd_fs: *InitrdFS) void {
// There will be test files provided for the runtime tests
// Need to init the VFS. This will be overridden after the tests.
vfs.setRoot(rd_fs.root_node);
rt_openReadClose(rd_fs.allocator);
if (rd_fs.opened_files.items().len != 0) {
panic(@errorReturnTrace(), "FAILURE: Didn't close all files\n", .{});
}
}

View file

@ -15,6 +15,8 @@ const panic_root = if (is_test) @import(mock_path ++ "panic_mock.zig") else @imp
const task = if (is_test) @import(mock_path ++ "task_mock.zig") else @import("task.zig");
const heap = @import("heap.zig");
const scheduler = @import("scheduler.zig");
const vfs = @import("vfs.zig");
const initrd = @import("initrd.zig");
comptime {
if (!is_test) {
@ -67,7 +69,7 @@ export fn kmain(boot_payload: arch.BootPayload) void {
var fixed_allocator = mem_profile.fixed_allocator;
panic_root.init(&mem_profile, &fixed_allocator.allocator) catch |e| {
panic_root.panic(@errorReturnTrace(), "Failed to initialise panic: {}", .{e});
panic_root.panic(@errorReturnTrace(), "Failed to initialise panic: {}\n", .{e});
};
pmm.init(&mem_profile, &fixed_allocator.allocator);
@ -92,9 +94,35 @@ export fn kmain(boot_payload: arch.BootPayload) void {
tty.init(&kernel_heap.allocator, boot_payload);
scheduler.init(&kernel_heap.allocator) catch |e| {
panic_root.panic(@errorReturnTrace(), "Failed to initialise scheduler: {}", .{e});
panic_root.panic(@errorReturnTrace(), "Failed to initialise scheduler: {}\n", .{e});
};
// Get the ramdisk module
const rd_module = for (mem_profile.modules) |module| {
if (std.mem.eql(u8, module.name, "initrd.ramdisk")) {
break module;
}
} else null;
if (rd_module) |module| {
// Load the ram disk
var ramdisk_filesystem = initrd.InitrdFS.init(module, &kernel_heap.allocator) catch |e| {
panic_root.panic(@errorReturnTrace(), "Failed to initialise ramdisk: {}\n", .{e});
};
defer {
ramdisk_filesystem.deinit();
// Free the raw ramdisk module as we are done
kernel_vmm.free(module.region.start) catch |e| {
panic_root.panic(@errorReturnTrace(), "Failed to free ramdisk: {}\n", .{e});
};
}
// Need to init the vfs after the ramdisk as we need the root node from the ramdisk filesystem
vfs.setRoot(ramdisk_filesystem.root_node);
// Load all files here
}
// Initialisation is finished, now does other stuff
std.log.info(.kmain, "Init\n", .{});
@ -105,10 +133,10 @@ export fn kmain(boot_payload: arch.BootPayload) void {
// Create a init2 task
var idle_task = task.Task.create(initStage2, &kernel_heap.allocator) catch |e| {
panic_root.panic(@errorReturnTrace(), "Failed to create init stage 2 task: {}", .{e});
panic_root.panic(@errorReturnTrace(), "Failed to create init stage 2 task: {}\n", .{e});
};
scheduler.scheduleTask(idle_task, &kernel_heap.allocator) catch |e| {
panic_root.panic(@errorReturnTrace(), "Failed to schedule init stage 2 task: {}", .{e});
panic_root.panic(@errorReturnTrace(), "Failed to schedule init stage 2 task: {}\n", .{e});
};
// Can't return for now, later this can return maybe

View file

@ -5,7 +5,7 @@ const ArrayList = std.ArrayList;
const Allocator = std.mem.Allocator;
/// Flags specifying what to do when opening a file or directory
const OpenFlags = enum {
pub const OpenFlags = enum {
/// Create a directory if it doesn't exist
CREATE_DIR,
/// Create a file if it doesn't exist
@ -79,10 +79,11 @@ pub const FileSystem = struct {
/// Return: []u8
/// The data read as a slice of bytes. The length will be <= len, including 0 if there was no data to read
///
/// Error: Allocator.Error
/// Error: Allocator.Error || Error
/// Allocator.Error.OutOfMemory - There wasn't enough memory to fulfill the request
/// Error.NotOpened - If the node provided is not one that the file system recognised as being opened.
///
const Read = fn (self: *const Self, node: *const FileNode, len: usize) Allocator.Error![]u8;
const Read = fn (self: *const Self, node: *const FileNode, len: usize) (Allocator.Error || Error)![]u8;
///
/// Write to an open file
@ -95,7 +96,7 @@ pub const FileSystem = struct {
/// Error: Allocator.Error
/// Allocator.Error.OutOfMemory - There wasn't enough memory to fulfill the request
///
const Write = fn (self: *const Self, node: *const FileNode, bytes: []const u8) Allocator.Error!void;
const Write = fn (self: *const Self, node: *const FileNode, bytes: []const u8) (Allocator.Error || Error)!void;
///
/// Open a file/dir within the filesystem. The result can then be used for write, read or close operations
@ -152,7 +153,7 @@ pub const FileNode = struct {
fs: *const FileSystem,
/// See the documentation for FileSystem.Read
pub fn read(self: *const FileNode, len: usize) Allocator.Error![]u8 {
pub fn read(self: *const FileNode, len: usize) (Allocator.Error || Error)![]u8 {
return self.fs.read(self.fs, self, len);
}
@ -162,7 +163,7 @@ pub const FileNode = struct {
}
/// See the documentation for FileSystem.Write
pub fn write(self: *const FileNode, bytes: []const u8) Allocator.Error!void {
pub fn write(self: *const FileNode, bytes: []const u8) (Allocator.Error || Error)!void {
return self.fs.write(self.fs, self, bytes);
}
};
@ -203,6 +204,9 @@ pub const Error = error{
/// The flags provided are invalid for the requested operation
InvalidFlags,
/// The node is not recognised as being opened by the filesystem
NotOpened,
};
/// Errors that can be thrown when attempting to mount
@ -383,6 +387,16 @@ pub fn isAbsolute(path: []const u8) bool {
return path.len > 0 and path[0] == SEPARATOR;
}
///
/// Initialise the virtual file system with a root Node. This will be a Directory node.
///
/// Arguments:
/// IN node: *Node - The node to initialise the root node.
///
pub fn setRoot(node: *Node) void {
root = node;
}
const TestFS = struct {
const TreeNode = struct {
val: *Node,
@ -453,7 +467,7 @@ const TestFS = struct {
fn close(fs: *const FileSystem, node: *const FileNode) void {}
fn read(fs: *const FileSystem, node: *const FileNode, len: usize) Allocator.Error![]u8 {
fn read(fs: *const FileSystem, node: *const FileNode, len: usize) (Allocator.Error || Error)![]u8 {
var test_fs = @fieldParentPtr(TestFS, "instance", fs.instance);
// Get the tree that corresponds to the node. Cannot error as the file is already open so it does exist
var tree = (getTreeNode(test_fs, node) catch unreachable) orelse unreachable;
@ -464,7 +478,7 @@ const TestFS = struct {
return bytes;
}
fn write(fs: *const FileSystem, node: *const FileNode, bytes: []const u8) Allocator.Error!void {
fn write(fs: *const FileSystem, node: *const FileNode, bytes: []const u8) (Allocator.Error || Error)!void {
var test_fs = @fieldParentPtr(TestFS, "instance", fs.instance);
var tree = (try getTreeNode(test_fs, node)) orelse unreachable;
if (tree.data) |_| {

View file

@ -290,7 +290,9 @@ pub fn VirtualMemoryManager(comptime Payload: type) type {
const addr = pmm.alloc() orelse unreachable;
try block_list.append(addr);
// The map function failing isn't the caller's responsibility so panic as it shouldn't happen
self.mapper.mapFn(vaddr, vaddr + BLOCK_SIZE, addr, addr + BLOCK_SIZE, attrs, self.allocator, self.payload) catch |e| panic(@errorReturnTrace(), "Failed to map virtual memory: {}\n", .{e});
self.mapper.mapFn(vaddr, vaddr + BLOCK_SIZE, addr, addr + BLOCK_SIZE, attrs, self.allocator, self.payload) catch |e| {
panic(@errorReturnTrace(), "Failed to map virtual memory: {}\n", .{e});
};
vaddr += BLOCK_SIZE;
}
_ = try self.allocations.put(vaddr_start, Allocation{ .physical = block_list });
@ -327,8 +329,8 @@ pub fn VirtualMemoryManager(comptime Payload: type) type {
};
}
// Unmap the entire range
const region_start = entry * BLOCK_SIZE;
const region_end = (entry + num_physical_allocations) * BLOCK_SIZE;
const region_start = vaddr;
const region_end = vaddr + (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 });
};
@ -412,7 +414,7 @@ test "alloc and free" {
}
// Make sure that the entries are set or not depending on the allocation success
var vaddr = entry * BLOCK_SIZE;
var vaddr = vmm.start + entry * BLOCK_SIZE;
while (vaddr < (entry + num_to_alloc) * BLOCK_SIZE) : (vaddr += BLOCK_SIZE) {
if (should_be_set) {
// Allocation succeeded so this address should be set
@ -471,10 +473,10 @@ test "set" {
const num_entries = 512;
var vmm = try testInit(num_entries);
const vstart = BLOCK_SIZE * 37;
const vend = BLOCK_SIZE * 46;
const pstart = vstart + 123;
const pend = vend + 123;
const vstart = vmm.start + BLOCK_SIZE * 37;
const vend = vmm.start + BLOCK_SIZE * 46;
const pstart = BLOCK_SIZE * 37 + 123;
const pend = BLOCK_SIZE * 46 + 123;
const attrs = Attributes{ .kernel = true, .writable = true, .cachable = true };
try vmm.set(.{ .start = vstart, .end = vend }, mem.Range{ .start = pstart, .end = pend }, attrs);
// Make sure it put the correct address in the map
@ -484,20 +486,21 @@ test "set" {
// The entries before the virtual start shouldn't be set
var vaddr = vmm.start;
while (vaddr < vstart) : (vaddr += BLOCK_SIZE) {
std.testing.expect(!(try allocations.isSet(vaddr / BLOCK_SIZE)));
std.testing.expect(!(try allocations.isSet((vaddr - vmm.start) / BLOCK_SIZE)));
}
// The entries up until the virtual end should be set
while (vaddr < vend) : (vaddr += BLOCK_SIZE) {
std.testing.expect(try allocations.isSet(vaddr / BLOCK_SIZE));
std.testing.expect(try allocations.isSet((vaddr - vmm.start) / BLOCK_SIZE));
}
// The entries after the virtual end should not be set
while (vaddr < vmm.end) : (vaddr += BLOCK_SIZE) {
std.testing.expect(!(try allocations.isSet(vaddr / BLOCK_SIZE)));
std.testing.expect(!(try allocations.isSet((vaddr - vmm.start) / BLOCK_SIZE)));
}
}
var test_allocations: ?bitmap.Bitmap(u64) = null;
var test_mapper = Mapper(u8){ .mapFn = testMap, .unmapFn = testUnmap };
const test_vaddr_start: usize = 0xC0000000;
///
/// Initialise a virtual memory manager used for testing
@ -533,7 +536,7 @@ fn testInit(num_entries: u32) Allocator.Error!VirtualMemoryManager(u8) {
.modules = &[_]mem.Module{},
};
pmm.init(&mem_profile, std.heap.page_allocator);
return VirtualMemoryManager(u8).init(0, num_entries * BLOCK_SIZE, std.heap.page_allocator, test_mapper, 39);
return VirtualMemoryManager(u8).init(test_vaddr_start, test_vaddr_start + num_entries * BLOCK_SIZE, std.heap.page_allocator, test_mapper, 39);
}
///
@ -552,7 +555,7 @@ fn testMap(vstart: usize, vend: usize, pstart: usize, pend: usize, attrs: Attrib
std.testing.expectEqual(@as(u8, 39), payload);
var vaddr = vstart;
while (vaddr < vend) : (vaddr += BLOCK_SIZE) {
(test_allocations.?).setEntry(vaddr / BLOCK_SIZE) catch unreachable;
(test_allocations.?).setEntry((vaddr - test_vaddr_start) / BLOCK_SIZE) catch unreachable;
}
}
@ -568,7 +571,11 @@ fn testUnmap(vstart: usize, vend: usize, payload: u8) (Allocator.Error || Mapper
std.testing.expectEqual(@as(u8, 39), payload);
var vaddr = vstart;
while (vaddr < vend) : (vaddr += BLOCK_SIZE) {
(test_allocations.?).clearEntry(vaddr / BLOCK_SIZE) catch unreachable;
if ((test_allocations.?).isSet((vaddr - test_vaddr_start) / BLOCK_SIZE) catch unreachable) {
(test_allocations.?).clearEntry((vaddr - test_vaddr_start) / BLOCK_SIZE) catch unreachable;
} else {
return MapperError.NotMapped;
}
}
}

1
test/ramdisk_test1.txt Normal file
View file

@ -0,0 +1 @@
Testing ram disk

1
test/ramdisk_test2.txt Normal file
View file

@ -0,0 +1 @@
Testing ram disk for the second time