pluto/build.zig
DrDeano bcc1712737
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

{}
2020-07-25 11:18:19 +01:00

284 lines
11 KiB
Zig

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;
const x86_i686 = CrossTarget{
.cpu_arch = .i386,
.os_tag = .freestanding,
.cpu_model = .{ .explicit = &Target.x86.cpu._i686 },
};
pub fn build(b: *Builder) !void {
const target = b.standardTargetOptions(.{ .whitelist = &[_]CrossTarget{x86_i686}, .default_target = x86_i686 });
const arch = switch (target.getCpuArch()) {
.i386 => "x86",
else => unreachable,
};
const fmt_step = b.addFmt(&[_][]const u8{
"build.zig",
"src",
"test",
});
b.default_step.dependOn(&fmt_step.step);
const main_src = "src/kernel/kmain.zig";
const arch_root = "src/kernel/arch";
const constants_path = try fs.path.join(b.allocator, &[_][]const u8{ arch_root, arch, "constants.zig" });
const linker_script_path = try fs.path.join(b.allocator, &[_][]const u8{ arch_root, arch, "link.ld" });
const output_iso = try fs.path.join(b.allocator, &[_][]const u8{ b.exe_dir, "pluto.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 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 ";
inline for (@typeInfo(TestMode).Enum.fields) |field| {
const tm = @field(TestMode, field.name);
test_mode_desc = test_mode_desc ++ field.name ++ " (" ++ TestMode.getDescription(tm) ++ ")";
test_mode_desc = test_mode_desc ++ "\n ";
}
const test_mode = b.option(TestMode, "test-mode", "Run a specific runtime test. This option is for the rt-test step. Available options: " ++ test_mode_desc) orelse .None;
const disable_display = b.option(bool, "disable-display", "Disable the qemu window") orelse false;
const exec = b.addExecutable("pluto.elf", main_src);
exec.addPackagePath("constants", constants_path);
exec.setOutputDir(b.cache_root);
exec.addBuildOption(TestMode, "test_mode", test_mode);
exec.setBuildMode(build_mode);
exec.setLinkerScriptPath(linker_script_path);
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(), 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");
const mock_path = "\"../../test/mock/kernel/\"";
const arch_mock_path = "\"../../../../test/mock/kernel/\"";
const unit_tests = b.addTest(main_src);
unit_tests.setBuildMode(build_mode);
unit_tests.setMainPkgPath(".");
unit_tests.addPackagePath("constants", constants_path);
unit_tests.addBuildOption(TestMode, "test_mode", test_mode);
unit_tests.addBuildOption([]const u8, "mock_path", mock_path);
unit_tests.addBuildOption([]const u8, "arch_mock_path", arch_mock_path);
if (builtin.os.tag != .windows) {
unit_tests.enable_qemu = true;
}
unit_tests.setTarget(.{ .cpu_arch = target.cpu_arch });
test_step.dependOn(&unit_tests.step);
const rt_test_step = b.step("rt-test", "Run runtime tests");
const build_mode_str = switch (build_mode) {
.Debug => "",
.ReleaseSafe => "-Drelease-safe",
.ReleaseFast => "-Drelease-fast",
.ReleaseSmall => "-Drelease-small",
};
var qemu_args_al = ArrayList([]const u8).init(b.allocator);
defer qemu_args_al.deinit();
switch (target.getCpuArch()) {
.i386 => try qemu_args_al.append("qemu-system-i386"),
else => unreachable,
}
try qemu_args_al.append("-serial");
try qemu_args_al.append("stdio");
switch (target.getCpuArch()) {
.i386 => {
try qemu_args_al.append("-boot");
try qemu_args_al.append("d");
try qemu_args_al.append("-cdrom");
try qemu_args_al.append(output_iso);
},
else => unreachable,
}
if (disable_display) {
try qemu_args_al.append("-display");
try qemu_args_al.append("none");
}
var qemu_args = qemu_args_al.toOwnedSlice();
const rt_step = RuntimeStep.create(b, test_mode, qemu_args);
rt_step.step.dependOn(&make_iso.step);
rt_test_step.dependOn(&rt_step.step);
const run_step = b.step("run", "Run with qemu");
const run_debug_step = b.step("debug-run", "Run with qemu and wait for a gdb connection");
const qemu_cmd = b.addSystemCommand(qemu_args);
const qemu_debug_cmd = b.addSystemCommand(qemu_args);
qemu_debug_cmd.addArgs(&[_][]const u8{ "-s", "-S" });
qemu_cmd.step.dependOn(&make_iso.step);
qemu_debug_cmd.step.dependOn(&make_iso.step);
run_step.dependOn(&qemu_cmd.step);
run_debug_step.dependOn(&qemu_debug_cmd.step);
const debug_step = b.step("debug", "Debug with gdb and connect to a running qemu instance");
const symbol_file_arg = try std.mem.join(b.allocator, " ", &[_][]const u8{ "symbol-file", exec.getOutputPath() });
const debug_cmd = b.addSystemCommand(&[_][]const u8{
"gdb-multiarch",
"-ex",
symbol_file_arg,
"-ex",
"set architecture auto",
});
debug_cmd.addArgs(&[_][]const u8{
"-ex",
"target remote localhost:1234",
});
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;
}
};