Created ramdisk build step
Refactored tests for the scheduler and task Revert "Refactored tests for the scheduler and task" This reverts commit 2bf56a368bc18f2bd2d33c385e3672d07e4431d9. Refactored tests for the scheduler and task Task fmt Task fmt again >:( Ramdisk Added NotOpened error for file read and write Added vfs init to initialise the root node Added the ramdisk.initrd file to grub Update makeiso to copy the ramdisk to the modules folder Add a ramdisk step to create a ramdisk to be leaded by grub and parsed by the kernel Add test files for runtime tests of ramdisk vfs.init => vfs.setRoot Improved ramdisk step Also spelling Changed name for the initrd Rename RamdiskFS => InitrdFS Add deinit for initrd Fixed VMM unmap {}
This commit is contained in:
parent
7b4a5e97aa
commit
bcc1712737
11 changed files with 900 additions and 36 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -27,6 +27,7 @@
|
||||||
*.i*86
|
*.i*86
|
||||||
*.x86_64
|
*.x86_64
|
||||||
*.hex
|
*.hex
|
||||||
|
*.initrd
|
||||||
|
|
||||||
# Debug files
|
# Debug files
|
||||||
*.dSYM/
|
*.dSYM/
|
||||||
|
|
135
build.zig
135
build.zig
|
@ -2,10 +2,13 @@ const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
const rt = @import("test/runtime_test.zig");
|
const rt = @import("test/runtime_test.zig");
|
||||||
const RuntimeStep = rt.RuntimeStep;
|
const RuntimeStep = rt.RuntimeStep;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
const Builder = std.build.Builder;
|
const Builder = std.build.Builder;
|
||||||
|
const Step = std.build.Step;
|
||||||
const Target = std.Target;
|
const Target = std.Target;
|
||||||
const CrossTarget = std.zig.CrossTarget;
|
const CrossTarget = std.zig.CrossTarget;
|
||||||
const fs = std.fs;
|
const fs = std.fs;
|
||||||
|
const File = fs.File;
|
||||||
const Mode = builtin.Mode;
|
const Mode = builtin.Mode;
|
||||||
const TestMode = rt.TestMode;
|
const TestMode = rt.TestMode;
|
||||||
const ArrayList = std.ArrayList;
|
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 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 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 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();
|
const build_mode = b.standardReleaseOptions();
|
||||||
comptime var test_mode_desc: []const u8 = "\n ";
|
comptime var test_mode_desc: []const u8 = "\n ";
|
||||||
|
@ -59,11 +63,23 @@ pub fn build(b: *Builder) !void {
|
||||||
exec.setTarget(target);
|
exec.setTarget(target);
|
||||||
|
|
||||||
const make_iso = switch (target.getCpuArch()) {
|
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,
|
else => unreachable,
|
||||||
};
|
};
|
||||||
make_iso.step.dependOn(&exec.step);
|
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);
|
b.default_step.dependOn(&make_iso.step);
|
||||||
|
|
||||||
const test_step = b.step("test", "Run tests");
|
const test_step = b.step("test", "Run tests");
|
||||||
|
@ -149,3 +165,120 @@ pub fn build(b: *Builder) !void {
|
||||||
});
|
});
|
||||||
debug_step.dependOn(&debug_cmd.step);
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -4,5 +4,6 @@ set default=0
|
||||||
menuentry "pluto" {
|
menuentry "pluto" {
|
||||||
multiboot /boot/pluto.elf
|
multiboot /boot/pluto.elf
|
||||||
module /modules/kernel.map kernel.map
|
module /modules/kernel.map kernel.map
|
||||||
|
module /modules/initrd.ramdisk initrd.ramdisk
|
||||||
boot
|
boot
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,8 @@ BOOT_DIR=$1
|
||||||
MODULES_DIR=$2
|
MODULES_DIR=$2
|
||||||
ISO_DIR=$3
|
ISO_DIR=$3
|
||||||
PLUTO_ELF=$4
|
PLUTO_ELF=$4
|
||||||
OUTPUT_FILE=$5
|
RAMDISK=$5
|
||||||
|
OUTPUT_FILE=$6
|
||||||
|
|
||||||
MAP_FILE=$MODULES_DIR/'kernel.map'
|
MAP_FILE=$MODULES_DIR/'kernel.map'
|
||||||
|
|
||||||
|
@ -23,6 +24,7 @@ mkdir -p $MODULES_DIR
|
||||||
|
|
||||||
cp -r grub $BOOT_DIR
|
cp -r grub $BOOT_DIR
|
||||||
cp $PLUTO_ELF $BOOT_DIR/"pluto.elf"
|
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
|
# 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
|
readelf -s --wide $PLUTO_ELF | grep -F "FUNC" | awk '{$1=$3=$4=$5=$6=$7=""; print $0}' | sort -k 1 > $MAP_FILE
|
||||||
|
|
|
@ -14,6 +14,7 @@ const tty = @import("../../tty.zig");
|
||||||
const mem = @import("../../mem.zig");
|
const mem = @import("../../mem.zig");
|
||||||
const vmm = @import("../../vmm.zig");
|
const vmm = @import("../../vmm.zig");
|
||||||
const multiboot = @import("multiboot.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.
|
/// 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 {
|
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
|
/// IN allocator: *Allocator - The allocator to use to map any tables needed
|
||||||
/// OUT dir: *Directory - The directory that this entry is in
|
/// 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.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.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.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.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
|
/// 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) {
|
if (phys_start > phys_end) {
|
||||||
return vmm.MapperError.InvalidPhysicalAddress;
|
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_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 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 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
|
/// IN/OUT dir: *Directory - The page directory to map within
|
||||||
///
|
///
|
||||||
/// Error: vmm.MapperError || std.mem.Allocator.Error
|
/// Error: vmm.MapperError || Allocator.Error
|
||||||
/// * - See mapDirEntry
|
/// * - 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 virt_addr = virt_start;
|
||||||
var phys_addr = phys_start;
|
var phys_addr = phys_start;
|
||||||
var page = virt_addr / PAGE_SIZE_4KB;
|
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 virtual_end: usize - The end (exclusive) of the virtual region to unmap
|
||||||
/// IN/OUT dir: *Directory - The page directory to unmap within
|
/// 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
|
/// 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 virt_addr = virtual_start;
|
||||||
var page = virt_addr / PAGE_SIZE_4KB;
|
var page = virt_addr / PAGE_SIZE_4KB;
|
||||||
var entry_idx = virt_addr / PAGE_SIZE_4MB;
|
var entry_idx = virt_addr / PAGE_SIZE_4MB;
|
||||||
|
|
675
src/kernel/initrd.zig
Normal file
675
src/kernel/initrd.zig
Normal 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", .{});
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 task = if (is_test) @import(mock_path ++ "task_mock.zig") else @import("task.zig");
|
||||||
const heap = @import("heap.zig");
|
const heap = @import("heap.zig");
|
||||||
const scheduler = @import("scheduler.zig");
|
const scheduler = @import("scheduler.zig");
|
||||||
|
const vfs = @import("vfs.zig");
|
||||||
|
const initrd = @import("initrd.zig");
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
if (!is_test) {
|
if (!is_test) {
|
||||||
|
@ -67,7 +69,7 @@ export fn kmain(boot_payload: arch.BootPayload) void {
|
||||||
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| {
|
||||||
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);
|
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);
|
tty.init(&kernel_heap.allocator, boot_payload);
|
||||||
|
|
||||||
scheduler.init(&kernel_heap.allocator) catch |e| {
|
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
|
// Initialisation is finished, now does other stuff
|
||||||
std.log.info(.kmain, "Init\n", .{});
|
std.log.info(.kmain, "Init\n", .{});
|
||||||
|
|
||||||
|
@ -105,10 +133,10 @@ export fn kmain(boot_payload: arch.BootPayload) void {
|
||||||
|
|
||||||
// Create a init2 task
|
// Create a init2 task
|
||||||
var idle_task = task.Task.create(initStage2, &kernel_heap.allocator) catch |e| {
|
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| {
|
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
|
// Can't return for now, later this can return maybe
|
||||||
|
|
|
@ -5,7 +5,7 @@ const ArrayList = std.ArrayList;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
/// Flags specifying what to do when opening a file or directory
|
/// 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 a directory if it doesn't exist
|
||||||
CREATE_DIR,
|
CREATE_DIR,
|
||||||
/// Create a file if it doesn't exist
|
/// Create a file if it doesn't exist
|
||||||
|
@ -79,10 +79,11 @@ pub const FileSystem = struct {
|
||||||
/// Return: []u8
|
/// Return: []u8
|
||||||
/// The data read as a slice of bytes. The length will be <= len, including 0 if there was no data to read
|
/// 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
|
/// 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
|
/// Write to an open file
|
||||||
|
@ -95,7 +96,7 @@ pub const FileSystem = struct {
|
||||||
/// Error: Allocator.Error
|
/// Error: Allocator.Error
|
||||||
/// Allocator.Error.OutOfMemory - There wasn't enough memory to fulfill the request
|
/// 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
|
/// 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,
|
fs: *const FileSystem,
|
||||||
|
|
||||||
/// See the documentation for FileSystem.Read
|
/// 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);
|
return self.fs.read(self.fs, self, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,7 +163,7 @@ pub const FileNode = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See the documentation for FileSystem.Write
|
/// 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);
|
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
|
/// The flags provided are invalid for the requested operation
|
||||||
InvalidFlags,
|
InvalidFlags,
|
||||||
|
|
||||||
|
/// The node is not recognised as being opened by the filesystem
|
||||||
|
NotOpened,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Errors that can be thrown when attempting to mount
|
/// 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;
|
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 TestFS = struct {
|
||||||
const TreeNode = struct {
|
const TreeNode = struct {
|
||||||
val: *Node,
|
val: *Node,
|
||||||
|
@ -453,7 +467,7 @@ const TestFS = struct {
|
||||||
|
|
||||||
fn close(fs: *const FileSystem, node: *const FileNode) void {}
|
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);
|
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
|
// 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;
|
var tree = (getTreeNode(test_fs, node) catch unreachable) orelse unreachable;
|
||||||
|
@ -464,7 +478,7 @@ const TestFS = struct {
|
||||||
return bytes;
|
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 test_fs = @fieldParentPtr(TestFS, "instance", fs.instance);
|
||||||
var tree = (try getTreeNode(test_fs, node)) orelse unreachable;
|
var tree = (try getTreeNode(test_fs, node)) orelse unreachable;
|
||||||
if (tree.data) |_| {
|
if (tree.data) |_| {
|
||||||
|
|
|
@ -290,7 +290,9 @@ pub fn VirtualMemoryManager(comptime Payload: type) type {
|
||||||
const addr = pmm.alloc() orelse unreachable;
|
const addr = pmm.alloc() orelse unreachable;
|
||||||
try block_list.append(addr);
|
try block_list.append(addr);
|
||||||
// The map function failing isn't the caller's responsibility so panic as it shouldn't happen
|
// 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;
|
vaddr += BLOCK_SIZE;
|
||||||
}
|
}
|
||||||
_ = try self.allocations.put(vaddr_start, Allocation{ .physical = block_list });
|
_ = 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
|
// Unmap the entire range
|
||||||
const region_start = entry * BLOCK_SIZE;
|
const region_start = vaddr;
|
||||||
const region_end = (entry + num_physical_allocations) * BLOCK_SIZE;
|
const region_end = vaddr + (num_physical_allocations * BLOCK_SIZE);
|
||||||
self.mapper.unmapFn(region_start, region_end, self.payload) catch |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 });
|
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
|
// 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) {
|
while (vaddr < (entry + num_to_alloc) * BLOCK_SIZE) : (vaddr += BLOCK_SIZE) {
|
||||||
if (should_be_set) {
|
if (should_be_set) {
|
||||||
// Allocation succeeded so this address should be set
|
// Allocation succeeded so this address should be set
|
||||||
|
@ -471,10 +473,10 @@ test "set" {
|
||||||
const num_entries = 512;
|
const num_entries = 512;
|
||||||
var vmm = try testInit(num_entries);
|
var vmm = try testInit(num_entries);
|
||||||
|
|
||||||
const vstart = BLOCK_SIZE * 37;
|
const vstart = vmm.start + BLOCK_SIZE * 37;
|
||||||
const vend = BLOCK_SIZE * 46;
|
const vend = vmm.start + BLOCK_SIZE * 46;
|
||||||
const pstart = vstart + 123;
|
const pstart = BLOCK_SIZE * 37 + 123;
|
||||||
const pend = vend + 123;
|
const pend = BLOCK_SIZE * 46 + 123;
|
||||||
const attrs = Attributes{ .kernel = true, .writable = true, .cachable = true };
|
const attrs = Attributes{ .kernel = true, .writable = true, .cachable = true };
|
||||||
try vmm.set(.{ .start = vstart, .end = vend }, mem.Range{ .start = pstart, .end = pend }, attrs);
|
try vmm.set(.{ .start = vstart, .end = vend }, mem.Range{ .start = pstart, .end = pend }, attrs);
|
||||||
// Make sure it put the correct address in the map
|
// 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
|
// 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) {
|
||||||
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
|
// The entries up until the virtual end should be set
|
||||||
while (vaddr < vend) : (vaddr += BLOCK_SIZE) {
|
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
|
// The entries after the virtual end should not be set
|
||||||
while (vaddr < vmm.end) : (vaddr += BLOCK_SIZE) {
|
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_allocations: ?bitmap.Bitmap(u64) = null;
|
||||||
var test_mapper = Mapper(u8){ .mapFn = testMap, .unmapFn = testUnmap };
|
var test_mapper = Mapper(u8){ .mapFn = testMap, .unmapFn = testUnmap };
|
||||||
|
const test_vaddr_start: usize = 0xC0000000;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Initialise a virtual memory manager used for testing
|
/// Initialise a virtual memory manager used for testing
|
||||||
|
@ -533,7 +536,7 @@ fn testInit(num_entries: u32) 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 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);
|
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.?).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);
|
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.?).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
1
test/ramdisk_test1.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Testing ram disk
|
1
test/ramdisk_test2.txt
Normal file
1
test/ramdisk_test2.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Testing ram disk for the second time
|
Loading…
Reference in a new issue