Compare commits
3 Commits
main
...
syscall-re
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4b2ade8552 | ||
![]() |
4212c4c674 | ||
![]() |
403822310e |
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@ -40,8 +40,10 @@ jobs:
|
|||||||
run: zig env
|
run: zig env
|
||||||
- name: Build test executables
|
- name: Build test executables
|
||||||
run: zig build all_tests -Dci --verbose
|
run: zig build all_tests -Dci --verbose
|
||||||
- name: Run host tests
|
- name: Set up QEMU
|
||||||
run: zig build test_host
|
uses: davidgm94/setup-qemu@main
|
||||||
|
- name: Run all tests
|
||||||
|
run: zig build test_all -Dci --verbose
|
||||||
# build_and_test:
|
# build_and_test:
|
||||||
# runs-on: [self-hosted, Linux, X64]
|
# runs-on: [self-hosted, Linux, X64]
|
||||||
# steps:
|
# steps:
|
||||||
|
233
build.zig
233
build.zig
@ -1,31 +1,45 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const common = @import("src/common.zig");
|
const ArrayList = std.ArrayList;
|
||||||
const os = common.os;
|
const assert = std.debug.assert;
|
||||||
|
const concat = std.mem.concat;
|
||||||
|
const cwd = std.fs.cwd;
|
||||||
|
const Cpu = Target.Cpu;
|
||||||
|
const CrossTarget = std.zig.CrossTarget;
|
||||||
|
const EnumArray = std.EnumArray;
|
||||||
|
const enumValues = std.enums.values;
|
||||||
|
const fields = std.meta.fields;
|
||||||
|
const json = std.json;
|
||||||
|
const maxInt = std.math.maxInt;
|
||||||
|
const OptimizeMode = std.builtin.OptimizeMode;
|
||||||
|
const Target = std.Target;
|
||||||
|
|
||||||
// Build types
|
// Build types
|
||||||
const Build = std.Build;
|
const Build = std.Build;
|
||||||
const CompileStep = std.Build.CompileStep;
|
const CompileStep = Build.CompileStep;
|
||||||
const FileSource = std.Build.FileSource;
|
const LazyPath = Build.LazyPath;
|
||||||
const Module = std.Build.Module;
|
const Module = Build.Module;
|
||||||
const ModuleDependency = std.Build.ModuleDependency;
|
const ModuleDependency = Build.ModuleDependency;
|
||||||
const OptionsStep = std.Build.OptionsStep;
|
const OptionsStep = Build.OptionsStep;
|
||||||
const RunStep = std.Build.RunStep;
|
const RunStep = Build.RunStep;
|
||||||
const Step = std.Build.Step;
|
const Step = Build.Step;
|
||||||
|
|
||||||
const assert = std.debug.assert;
|
const builtin = @import("builtin");
|
||||||
|
const os = builtin.os.tag;
|
||||||
|
const cpu = builtin.cpu;
|
||||||
|
|
||||||
|
const common = @import("src/common.zig");
|
||||||
|
const ArgumentParser = common.ArgumentParser;
|
||||||
const Bootloader = common.Bootloader;
|
const Bootloader = common.Bootloader;
|
||||||
|
const canVirtualizeWithQEMU = common.canVirtualizeWithQEMU;
|
||||||
const Configuration = common.Configuration;
|
const Configuration = common.Configuration;
|
||||||
const Cpu = common.Cpu;
|
|
||||||
const CrossTarget = common.CrossTarget;
|
|
||||||
const DiskType = common.DiskType;
|
const DiskType = common.DiskType;
|
||||||
const ExecutionType = common.ExecutionType;
|
const ExecutionType = common.ExecutionType;
|
||||||
const ExecutionEnvironment = common.ExecutionEnvironment;
|
const ExecutionEnvironment = common.ExecutionEnvironment;
|
||||||
const FilesystemType = common.FilesystemType;
|
const FilesystemType = common.FilesystemType;
|
||||||
const OptimizeMode = common.OptimizeMode;
|
const ImageConfig = common.ImageConfig;
|
||||||
const QEMUOptions = common.QEMUOptions;
|
const QEMUOptions = common.QEMUOptions;
|
||||||
const BirthProgram = common.BirthProgram;
|
|
||||||
const Suffix = common.Suffix;
|
const Suffix = common.Suffix;
|
||||||
const Target = common.Target;
|
const TraditionalExecutionMode = common.TraditionalExecutionMode;
|
||||||
|
|
||||||
const Error = error{
|
const Error = error{
|
||||||
not_implemented,
|
not_implemented,
|
||||||
@ -44,9 +58,87 @@ var modules = Modules{};
|
|||||||
var b: *Build = undefined;
|
var b: *Build = undefined;
|
||||||
var build_steps: *BuildSteps = undefined;
|
var build_steps: *BuildSteps = undefined;
|
||||||
var default_configuration: Configuration = undefined;
|
var default_configuration: Configuration = undefined;
|
||||||
var user_modules: []const common.Module = undefined;
|
var user_modules: []const UserModule = undefined;
|
||||||
var options = Options{};
|
var options = Options{};
|
||||||
|
|
||||||
|
const supported_architectures = [_]Cpu.Arch{
|
||||||
|
.x86_64,
|
||||||
|
//.aarch64,
|
||||||
|
//.riscv64,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn architectureIndex(comptime arch: Cpu.Arch) comptime_int {
|
||||||
|
inline for (supported_architectures, 0..) |architecture, index| {
|
||||||
|
if (arch == architecture) return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
@compileError("Architecture not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
const ArchitectureBootloader = struct {
|
||||||
|
id: Bootloader,
|
||||||
|
protocols: []const Bootloader.Protocol,
|
||||||
|
};
|
||||||
|
|
||||||
|
const architecture_bootloader_map = blk: {
|
||||||
|
var array: [supported_architectures.len][]const ArchitectureBootloader = undefined;
|
||||||
|
|
||||||
|
array[architectureIndex(.x86_64)] = &.{
|
||||||
|
.{
|
||||||
|
.id = .birth,
|
||||||
|
.protocols = &.{ .bios, .uefi },
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.id = .limine,
|
||||||
|
.protocols = &.{ .bios, .uefi },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// array[architectureIndex(.aarch64)] = &.{
|
||||||
|
// .{
|
||||||
|
// .id = .birth,
|
||||||
|
// .protocols = &.{.uefi},
|
||||||
|
// },
|
||||||
|
// .{
|
||||||
|
// .id = .limine,
|
||||||
|
// .protocols = &.{.uefi},
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
|
||||||
|
// array[architectureIndex(.riscv64)] = &.{
|
||||||
|
// .{
|
||||||
|
// .id = .birth,
|
||||||
|
// .protocols = &.{.uefi},
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
|
||||||
|
break :blk array;
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const UserModule = struct {
|
||||||
|
package: UserPackage,
|
||||||
|
name: []const u8,
|
||||||
|
};
|
||||||
|
pub const UserPackage = struct {
|
||||||
|
kind: Kind,
|
||||||
|
dependencies: []const Dependency,
|
||||||
|
|
||||||
|
pub const Kind = enum {
|
||||||
|
zig_exe,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Dependency = struct {
|
||||||
|
foo: u64 = 0,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const BirthProgram = enum {
|
||||||
|
bootloader,
|
||||||
|
cpu,
|
||||||
|
user,
|
||||||
|
host,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn build(b_arg: *Build) !void {
|
pub fn build(b_arg: *Build) !void {
|
||||||
b = b_arg;
|
b = b_arg;
|
||||||
ci = b.option(bool, "ci", "CI mode") orelse false;
|
ci = b.option(bool, "ci", "CI mode") orelse false;
|
||||||
@ -56,9 +148,9 @@ pub fn build(b_arg: *Build) !void {
|
|||||||
const default_cfg_override = b.option([]const u8, "default", "Default configuration JSON file") orelse "config/default.json";
|
const default_cfg_override = b.option([]const u8, "default", "Default configuration JSON file") orelse "config/default.json";
|
||||||
modules = blk: {
|
modules = blk: {
|
||||||
var mods = Modules{};
|
var mods = Modules{};
|
||||||
inline for (comptime common.enumValues(ModuleID)) |module_id| {
|
inline for (comptime enumValues(ModuleID)) |module_id| {
|
||||||
mods.modules.set(module_id, b.createModule(.{
|
mods.modules.set(module_id, b.createModule(.{
|
||||||
.source_file = FileSource.relative(switch (module_id) {
|
.source_file = LazyPath.relative(switch (module_id) {
|
||||||
.limine_installer => "src/bootloader/limine/installer.zig",
|
.limine_installer => "src/bootloader/limine/installer.zig",
|
||||||
else => switch (module_id) {
|
else => switch (module_id) {
|
||||||
.bios, .uefi, .limine => "src/bootloader",
|
.bios, .uefi, .limine => "src/bootloader",
|
||||||
@ -93,12 +185,12 @@ pub fn build(b_arg: *Build) !void {
|
|||||||
};
|
};
|
||||||
|
|
||||||
default_configuration = blk: {
|
default_configuration = blk: {
|
||||||
const default_json_file = try std.fs.cwd().readFileAlloc(b.allocator, default_cfg_override, common.maxInt(usize));
|
const default_json_file = try cwd().readFileAlloc(b.allocator, default_cfg_override, maxInt(usize));
|
||||||
const parsed_cfg = try std.json.parseFromSlice(Configuration, b.allocator, default_json_file, .{});
|
const parsed_cfg = try json.parseFromSlice(Configuration, b.allocator, default_json_file, .{});
|
||||||
const cfg = parsed_cfg.value;
|
const cfg = parsed_cfg.value;
|
||||||
|
|
||||||
const optimize_mode = b.option(
|
const optimize_mode = b.option(
|
||||||
std.builtin.Mode,
|
OptimizeMode,
|
||||||
"optimize",
|
"optimize",
|
||||||
"Prioritize performance, safety, or binary size (-O flag)",
|
"Prioritize performance, safety, or binary size (-O flag)",
|
||||||
) orelse cfg.optimize_mode;
|
) orelse cfg.optimize_mode;
|
||||||
@ -162,13 +254,13 @@ pub fn build(b_arg: *Build) !void {
|
|||||||
run_native: bool = true,
|
run_native: bool = true,
|
||||||
|
|
||||||
const C = struct {
|
const C = struct {
|
||||||
include_paths: []const []const u8,
|
include_paths: []const LazyPath,
|
||||||
source_files: []const SourceFile,
|
source_files: []const SourceFile,
|
||||||
link_libc: bool,
|
link_libc: bool,
|
||||||
link_libcpp: bool,
|
link_libcpp: bool,
|
||||||
|
|
||||||
const SourceFile = struct {
|
const SourceFile = struct {
|
||||||
path: []const u8,
|
path: LazyPath,
|
||||||
flags: []const []const u8,
|
flags: []const []const u8,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -183,10 +275,10 @@ pub fn build(b_arg: *Build) !void {
|
|||||||
.root_project_path = disk_image_root_path,
|
.root_project_path = disk_image_root_path,
|
||||||
.modules = disk_image_builder_modules,
|
.modules = disk_image_builder_modules,
|
||||||
.c = .{
|
.c = .{
|
||||||
.include_paths = &.{"src/bootloader/limine/installables"},
|
.include_paths = &.{LazyPath.relative("src/bootloader/limine/installables")},
|
||||||
.source_files = &.{
|
.source_files = &.{
|
||||||
.{
|
.{
|
||||||
.path = "src/bootloader/limine/installables/limine-deploy.c",
|
.path = LazyPath.relative("src/bootloader/limine/installables/limine-deploy.c"),
|
||||||
.flags = &.{},
|
.flags = &.{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -200,7 +292,7 @@ pub fn build(b_arg: *Build) !void {
|
|||||||
|
|
||||||
const native_test_optimize_mode = .ReleaseFast;
|
const native_test_optimize_mode = .ReleaseFast;
|
||||||
for (native_tests) |native_test| {
|
for (native_tests) |native_test| {
|
||||||
const test_name = try std.mem.concat(b.allocator, u8, &.{ native_test.name, "_", @tagName(native_test_optimize_mode) });
|
const test_name = try concat(b.allocator, u8, &.{ native_test.name, "_", @tagName(native_test_optimize_mode) });
|
||||||
const test_exe = try addCompileStep(.{
|
const test_exe = try addCompileStep(.{
|
||||||
.name = test_name,
|
.name = test_name,
|
||||||
.root_project_path = native_test.root_project_path,
|
.root_project_path = native_test.root_project_path,
|
||||||
@ -215,7 +307,7 @@ pub fn build(b_arg: *Build) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (c.source_files) |source_file| {
|
for (c.source_files) |source_file| {
|
||||||
test_exe.addCSourceFile(source_file.path, source_file.flags);
|
test_exe.addCSourceFile(.{ .file = source_file.path, .flags = source_file.flags });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c.link_libc) {
|
if (c.link_libc) {
|
||||||
@ -247,20 +339,20 @@ pub fn build(b_arg: *Build) !void {
|
|||||||
const ovmf_path = ovmf_downloader_run_step.addOutputFileArg("OVMF.fd");
|
const ovmf_path = ovmf_downloader_run_step.addOutputFileArg("OVMF.fd");
|
||||||
|
|
||||||
{
|
{
|
||||||
var user_module_list = std.ArrayList(common.Module).init(b.allocator);
|
var user_module_list = ArrayList(UserModule).init(b.allocator);
|
||||||
var user_program_dir = try std.fs.cwd().openIterableDir(user_program_dir_path, .{ .access_sub_paths = true });
|
var user_program_dir = try cwd().openIterableDir(user_program_dir_path, .{ .access_sub_paths = true });
|
||||||
defer user_program_dir.close();
|
defer user_program_dir.close();
|
||||||
|
|
||||||
var user_program_iterator = user_program_dir.iterate();
|
var user_program_iterator = user_program_dir.iterate();
|
||||||
|
|
||||||
while (try user_program_iterator.next()) |entry| {
|
while (try user_program_iterator.next()) |entry| {
|
||||||
const dir_name = entry.name;
|
const dir_name = entry.name;
|
||||||
const file_path = try std.mem.concat(b.allocator, u8, &.{ dir_name, "/module.json" });
|
const file_path = try concat(b.allocator, u8, &.{ dir_name, "/module.json" });
|
||||||
const file = try user_program_dir.dir.readFileAlloc(b.allocator, file_path, common.maxInt(usize));
|
const file = try user_program_dir.dir.readFileAlloc(b.allocator, file_path, maxInt(usize));
|
||||||
const parsed_user_program = try std.json.parseFromSlice(common.UserProgram, b.allocator, file, .{});
|
const parsed_user_package = try json.parseFromSlice(UserPackage, b.allocator, file, .{});
|
||||||
const user_program = parsed_user_program.value;
|
const user_package = parsed_user_package.value;
|
||||||
try user_module_list.append(.{
|
try user_module_list.append(.{
|
||||||
.program = user_program,
|
.package = user_package,
|
||||||
.name = b.dupe(dir_name), // we have to dupe here otherwise Windows CI fails
|
.name = b.dupe(dir_name), // we have to dupe here otherwise Windows CI fails
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -270,8 +362,8 @@ pub fn build(b_arg: *Build) !void {
|
|||||||
|
|
||||||
const executable_kinds = [2]CompileStep.Kind{ .exe, .@"test" };
|
const executable_kinds = [2]CompileStep.Kind{ .exe, .@"test" };
|
||||||
|
|
||||||
for (common.enumValues(OptimizeMode)) |optimize_mode| {
|
for (enumValues(OptimizeMode)) |optimize_mode| {
|
||||||
for (common.supported_architectures, 0..) |architecture, architecture_index| {
|
for (supported_architectures, 0..) |architecture, architecture_index| {
|
||||||
const user_target = try getTarget(architecture, .user);
|
const user_target = try getTarget(architecture, .user);
|
||||||
|
|
||||||
for (executable_kinds) |executable_kind| {
|
for (executable_kinds) |executable_kind| {
|
||||||
@ -301,7 +393,7 @@ pub fn build(b_arg: *Build) !void {
|
|||||||
else => return Error.architecture_not_supported,
|
else => return Error.architecture_not_supported,
|
||||||
};
|
};
|
||||||
|
|
||||||
const cpu_driver_linker_script_path = FileSource.relative(try std.mem.concat(b.allocator, u8, &.{ cpu_driver_path, "/arch/", switch (architecture) {
|
const cpu_driver_linker_script_path = LazyPath.relative(try concat(b.allocator, u8, &.{ cpu_driver_path, "/arch/", switch (architecture) {
|
||||||
.x86_64 => "x86/64",
|
.x86_64 => "x86/64",
|
||||||
.x86 => "x86/32",
|
.x86 => "x86/32",
|
||||||
else => @tagName(architecture),
|
else => @tagName(architecture),
|
||||||
@ -309,14 +401,14 @@ pub fn build(b_arg: *Build) !void {
|
|||||||
|
|
||||||
cpu_driver.setLinkerScriptPath(cpu_driver_linker_script_path);
|
cpu_driver.setLinkerScriptPath(cpu_driver_linker_script_path);
|
||||||
|
|
||||||
var user_module_list = try std.ArrayList(*CompileStep).initCapacity(b.allocator, user_modules.len);
|
var user_module_list = try ArrayList(*CompileStep).initCapacity(b.allocator, user_modules.len);
|
||||||
const user_architecture_source_path = try std.mem.concat(b.allocator, u8, &.{ "src/user/arch/", @tagName(architecture), "/" });
|
const user_architecture_source_path = try concat(b.allocator, u8, &.{ "src/user/arch/", @tagName(architecture), "/" });
|
||||||
const user_linker_script_path = FileSource.relative(try std.mem.concat(b.allocator, u8, &.{ user_architecture_source_path, "linker_script.ld" }));
|
const user_linker_script_path = LazyPath.relative(try concat(b.allocator, u8, &.{ user_architecture_source_path, "linker_script.ld" }));
|
||||||
for (user_modules) |module| {
|
for (user_modules) |module| {
|
||||||
const user_module = try addCompileStep(.{
|
const user_module = try addCompileStep(.{
|
||||||
.kind = executable_kind,
|
.kind = executable_kind,
|
||||||
.name = module.name,
|
.name = module.name,
|
||||||
.root_project_path = try std.mem.concat(b.allocator, u8, &.{ user_program_dir_path, "/", module.name }),
|
.root_project_path = try concat(b.allocator, u8, &.{ user_program_dir_path, "/", module.name }),
|
||||||
.target = user_target,
|
.target = user_target,
|
||||||
.optimize_mode = optimize_mode,
|
.optimize_mode = optimize_mode,
|
||||||
.modules = &.{ .lib, .user, .birth },
|
.modules = &.{ .lib, .user, .birth },
|
||||||
@ -328,7 +420,7 @@ pub fn build(b_arg: *Build) !void {
|
|||||||
user_module_list.appendAssumeCapacity(user_module);
|
user_module_list.appendAssumeCapacity(user_module);
|
||||||
}
|
}
|
||||||
|
|
||||||
const bootloaders = common.architecture_bootloader_map[architecture_index];
|
const bootloaders = architecture_bootloader_map[architecture_index];
|
||||||
for (bootloaders) |bootloader_struct| {
|
for (bootloaders) |bootloader_struct| {
|
||||||
const bootloader = bootloader_struct.id;
|
const bootloader = bootloader_struct.id;
|
||||||
for (bootloader_struct.protocols) |boot_protocol| {
|
for (bootloader_struct.protocols) |boot_protocol| {
|
||||||
@ -353,9 +445,9 @@ pub fn build(b_arg: *Build) !void {
|
|||||||
|
|
||||||
executable.strip = true;
|
executable.strip = true;
|
||||||
|
|
||||||
executable.addAssemblyFile("src/bootloader/arch/x86/64/smp_trampoline.S");
|
executable.addAssemblyFile(LazyPath.relative("src/bootloader/arch/x86/64/smp_trampoline.S"));
|
||||||
executable.addAssemblyFile(bootloader_path ++ "/unreal_mode.S");
|
executable.addAssemblyFile(LazyPath.relative(bootloader_path ++ "/unreal_mode.S"));
|
||||||
executable.setLinkerScriptPath(FileSource.relative(bootloader_path ++ "/linker_script.ld"));
|
executable.setLinkerScriptPath(LazyPath.relative(bootloader_path ++ "/linker_script.ld"));
|
||||||
executable.code_model = .small;
|
executable.code_model = .small;
|
||||||
|
|
||||||
break :blk executable;
|
break :blk executable;
|
||||||
@ -380,7 +472,7 @@ pub fn build(b_arg: *Build) !void {
|
|||||||
executable.strip = true;
|
executable.strip = true;
|
||||||
|
|
||||||
switch (architecture) {
|
switch (architecture) {
|
||||||
.x86_64 => executable.addAssemblyFile("src/bootloader/arch/x86/64/smp_trampoline.S"),
|
.x86_64 => executable.addAssemblyFile(LazyPath.relative("src/bootloader/arch/x86/64/smp_trampoline.S")),
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -405,7 +497,7 @@ pub fn build(b_arg: *Build) !void {
|
|||||||
|
|
||||||
executable.code_model = cpu_driver.code_model;
|
executable.code_model = cpu_driver.code_model;
|
||||||
|
|
||||||
executable.setLinkerScriptPath(FileSource.relative(try common.concat(b.allocator, u8, &.{ limine_loader_path ++ "arch/", @tagName(architecture), "/linker_script.ld" })));
|
executable.setLinkerScriptPath(LazyPath.relative(try concat(b.allocator, u8, &.{ limine_loader_path ++ "arch/", @tagName(architecture), "/linker_script.ld" })));
|
||||||
|
|
||||||
break :blk executable;
|
break :blk executable;
|
||||||
},
|
},
|
||||||
@ -431,7 +523,7 @@ pub fn build(b_arg: *Build) !void {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const execution_types: []const ExecutionType =
|
const execution_types: []const ExecutionType =
|
||||||
switch (common.canVirtualizeWithQEMU(architecture, ci)) {
|
switch (canVirtualizeWithQEMU(architecture, ci)) {
|
||||||
true => &.{ .emulated, .accelerated },
|
true => &.{ .emulated, .accelerated },
|
||||||
false => &.{.emulated},
|
false => &.{.emulated},
|
||||||
};
|
};
|
||||||
@ -448,13 +540,13 @@ pub fn build(b_arg: *Build) !void {
|
|||||||
.executable_kind = executable_kind,
|
.executable_kind = executable_kind,
|
||||||
};
|
};
|
||||||
|
|
||||||
var disk_argument_parser = common.ArgumentParser.DiskImageBuilder{};
|
var disk_argument_parser = ArgumentParser.DiskImageBuilder{};
|
||||||
const disk_image_builder_run = b.addRunArtifact(disk_image_builder);
|
const disk_image_builder_run = b.addRunArtifact(disk_image_builder);
|
||||||
const disk_image_path = disk_image_builder_run.addOutputFileArg("disk.hdd");
|
const disk_image_path = disk_image_builder_run.addOutputFileArg("disk.hdd");
|
||||||
|
|
||||||
while (disk_argument_parser.next()) |argument_type| switch (argument_type) {
|
while (disk_argument_parser.next()) |argument_type| switch (argument_type) {
|
||||||
.configuration => inline for (common.fields(Configuration)) |field| disk_image_builder_run.addArg(@tagName(@field(configuration, field.name))),
|
.configuration => inline for (fields(Configuration)) |field| disk_image_builder_run.addArg(@tagName(@field(configuration, field.name))),
|
||||||
.image_configuration_path => disk_image_builder_run.addArg(common.ImageConfig.default_path),
|
.image_configuration_path => disk_image_builder_run.addArg(ImageConfig.default_path),
|
||||||
.disk_image_path => {
|
.disk_image_path => {
|
||||||
// Must be first
|
// Must be first
|
||||||
assert(@intFromEnum(argument_type) == 0);
|
assert(@intFromEnum(argument_type) == 0);
|
||||||
@ -540,7 +632,7 @@ pub fn build(b_arg: *Build) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Options = struct {
|
const Options = struct {
|
||||||
arr: std.EnumArray(BirthProgram, *OptionsStep) = std.EnumArray(BirthProgram, *OptionsStep).initUndefined(),
|
arr: EnumArray(BirthProgram, *OptionsStep) = EnumArray(BirthProgram, *OptionsStep).initUndefined(),
|
||||||
|
|
||||||
pub fn createOption(options_struct: *Options, birth_program: BirthProgram) void {
|
pub fn createOption(options_struct: *Options, birth_program: BirthProgram) void {
|
||||||
const new_options = b.addOptions();
|
const new_options = b.addOptions();
|
||||||
@ -591,31 +683,31 @@ fn addFileSize(artifact: *CompileStep, comptime name: []const u8) void {
|
|||||||
|
|
||||||
fn newRunnerRunArtifact(arguments: struct {
|
fn newRunnerRunArtifact(arguments: struct {
|
||||||
configuration: Configuration,
|
configuration: Configuration,
|
||||||
disk_image_path: FileSource,
|
disk_image_path: LazyPath,
|
||||||
loader: *CompileStep,
|
loader: *CompileStep,
|
||||||
runner: *CompileStep,
|
runner: *CompileStep,
|
||||||
cpu_driver: *CompileStep,
|
cpu_driver: *CompileStep,
|
||||||
user_init: *CompileStep,
|
user_init: *CompileStep,
|
||||||
qemu_options: QEMUOptions,
|
qemu_options: QEMUOptions,
|
||||||
ovmf_path: FileSource,
|
ovmf_path: LazyPath,
|
||||||
is_default: bool,
|
is_default: bool,
|
||||||
}) !*RunStep {
|
}) !*RunStep {
|
||||||
const runner = b.addRunArtifact(arguments.runner);
|
const runner = b.addRunArtifact(arguments.runner);
|
||||||
|
|
||||||
var argument_parser = common.ArgumentParser.Runner{};
|
var argument_parser = ArgumentParser.Runner{};
|
||||||
|
|
||||||
while (argument_parser.next()) |argument_type| switch (argument_type) {
|
while (argument_parser.next()) |argument_type| switch (argument_type) {
|
||||||
.configuration => inline for (common.fields(Configuration)) |field| runner.addArg(@tagName(@field(arguments.configuration, field.name))),
|
.configuration => inline for (fields(Configuration)) |field| runner.addArg(@tagName(@field(arguments.configuration, field.name))),
|
||||||
.image_configuration_path => runner.addArg(common.ImageConfig.default_path),
|
.image_configuration_path => runner.addArg(ImageConfig.default_path),
|
||||||
.cpu_driver => runner.addArtifactArg(arguments.cpu_driver),
|
.cpu_driver => runner.addArtifactArg(arguments.cpu_driver),
|
||||||
.loader_path => runner.addArtifactArg(arguments.loader),
|
.loader_path => runner.addArtifactArg(arguments.loader),
|
||||||
.init => runner.addArtifactArg(arguments.user_init),
|
.init => runner.addArtifactArg(arguments.user_init),
|
||||||
.disk_image_path => runner.addFileSourceArg(arguments.disk_image_path),
|
.disk_image_path => runner.addFileArg(arguments.disk_image_path),
|
||||||
.qemu_options => inline for (common.fields(QEMUOptions)) |field| runner.addArg(if (@field(arguments.qemu_options, field.name)) "true" else "false"),
|
.qemu_options => inline for (fields(QEMUOptions)) |field| runner.addArg(if (@field(arguments.qemu_options, field.name)) "true" else "false"),
|
||||||
.ci => runner.addArg(if (ci) "true" else "false"),
|
.ci => runner.addArg(if (ci) "true" else "false"),
|
||||||
.debug_user => runner.addArg(if (debug_user) "true" else "false"),
|
.debug_user => runner.addArg(if (debug_user) "true" else "false"),
|
||||||
.debug_loader => runner.addArg(if (debug_loader) "true" else "false"),
|
.debug_loader => runner.addArg(if (debug_loader) "true" else "false"),
|
||||||
.ovmf_path => runner.addFileSourceArg(arguments.ovmf_path),
|
.ovmf_path => runner.addFileArg(arguments.ovmf_path),
|
||||||
.is_default => runner.addArg(if (arguments.is_default) "true" else "false"),
|
.is_default => runner.addArg(if (arguments.is_default) "true" else "false"),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -631,15 +723,17 @@ const ExecutableDescriptor = struct {
|
|||||||
modules: []const ModuleID,
|
modules: []const ModuleID,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const main_package_path = LazyPath.relative(source_root_dir);
|
||||||
fn addCompileStep(executable_descriptor: ExecutableDescriptor) !*CompileStep {
|
fn addCompileStep(executable_descriptor: ExecutableDescriptor) !*CompileStep {
|
||||||
const main_file = try std.mem.concat(b.allocator, u8, &.{ executable_descriptor.root_project_path, "/main.zig" });
|
const main_file = try concat(b.allocator, u8, &.{ executable_descriptor.root_project_path, "/main.zig" });
|
||||||
const compile_step = switch (executable_descriptor.kind) {
|
const compile_step = switch (executable_descriptor.kind) {
|
||||||
.exe => blk: {
|
.exe => blk: {
|
||||||
const executable = b.addExecutable(.{
|
const executable = b.addExecutable(.{
|
||||||
.name = executable_descriptor.name,
|
.name = executable_descriptor.name,
|
||||||
.root_source_file = FileSource.relative(main_file),
|
.root_source_file = LazyPath.relative(main_file),
|
||||||
.target = executable_descriptor.target,
|
.target = executable_descriptor.target,
|
||||||
.optimize = executable_descriptor.optimize_mode,
|
.optimize = executable_descriptor.optimize_mode,
|
||||||
|
.main_pkg_path = main_package_path,
|
||||||
});
|
});
|
||||||
|
|
||||||
build_steps.build_all.dependOn(&executable.step);
|
build_steps.build_all.dependOn(&executable.step);
|
||||||
@ -647,13 +741,14 @@ fn addCompileStep(executable_descriptor: ExecutableDescriptor) !*CompileStep {
|
|||||||
break :blk executable;
|
break :blk executable;
|
||||||
},
|
},
|
||||||
.@"test" => blk: {
|
.@"test" => blk: {
|
||||||
const test_file = FileSource.relative(try std.mem.concat(b.allocator, u8, &.{ executable_descriptor.root_project_path, "/test.zig" }));
|
const test_file = LazyPath.relative(try concat(b.allocator, u8, &.{ executable_descriptor.root_project_path, "/test.zig" }));
|
||||||
const test_exe = b.addTest(.{
|
const test_exe = b.addTest(.{
|
||||||
.name = executable_descriptor.name,
|
.name = executable_descriptor.name,
|
||||||
.root_source_file = test_file,
|
.root_source_file = test_file,
|
||||||
.target = executable_descriptor.target,
|
.target = executable_descriptor.target,
|
||||||
.optimize = executable_descriptor.optimize_mode,
|
.optimize = executable_descriptor.optimize_mode,
|
||||||
.test_runner = if (executable_descriptor.target.os_tag) |_| main_file else null,
|
.test_runner = if (executable_descriptor.target.os_tag) |_| main_file else null,
|
||||||
|
.main_pkg_path = main_package_path,
|
||||||
});
|
});
|
||||||
|
|
||||||
build_steps.build_all_tests.dependOn(&test_exe.step);
|
build_steps.build_all_tests.dependOn(&test_exe.step);
|
||||||
@ -669,8 +764,6 @@ fn addCompileStep(executable_descriptor: ExecutableDescriptor) !*CompileStep {
|
|||||||
compile_step.entry_symbol_name = "_start";
|
compile_step.entry_symbol_name = "_start";
|
||||||
}
|
}
|
||||||
|
|
||||||
compile_step.setMainPkgPath(source_root_dir);
|
|
||||||
|
|
||||||
for (executable_descriptor.modules) |module| {
|
for (executable_descriptor.modules) |module| {
|
||||||
modules.addModule(compile_step, module);
|
modules.addModule(compile_step, module);
|
||||||
}
|
}
|
||||||
@ -700,8 +793,8 @@ const ModuleID = enum {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub const Modules = struct {
|
pub const Modules = struct {
|
||||||
modules: std.EnumArray(ModuleID, *Module) = std.EnumArray(ModuleID, *Module).initUndefined(),
|
modules: EnumArray(ModuleID, *Module) = EnumArray(ModuleID, *Module).initUndefined(),
|
||||||
dependencies: std.EnumArray(ModuleID, []const ModuleDependency) = std.EnumArray(ModuleID, []const ModuleDependency).initUndefined(),
|
dependencies: EnumArray(ModuleID, []const ModuleDependency) = EnumArray(ModuleID, []const ModuleDependency).initUndefined(),
|
||||||
|
|
||||||
fn addModule(mods: Modules, compile_step: *CompileStep, module_id: ModuleID) void {
|
fn addModule(mods: Modules, compile_step: *CompileStep, module_id: ModuleID) void {
|
||||||
compile_step.addModule(@tagName(module_id), mods.modules.get(module_id));
|
compile_step.addModule(@tagName(module_id), mods.modules.get(module_id));
|
||||||
@ -718,7 +811,7 @@ pub const Modules = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn getTarget(asked_arch: Cpu.Arch, execution_mode: common.TraditionalExecutionMode) Error!CrossTarget {
|
fn getTarget(asked_arch: Cpu.Arch, execution_mode: TraditionalExecutionMode) Error!CrossTarget {
|
||||||
var enabled_features = Cpu.Feature.Set.empty;
|
var enabled_features = Cpu.Feature.Set.empty;
|
||||||
var disabled_features = Cpu.Feature.Set.empty;
|
var disabled_features = Cpu.Feature.Set.empty;
|
||||||
|
|
||||||
@ -743,14 +836,14 @@ fn getTarget(asked_arch: Cpu.Arch, execution_mode: common.TraditionalExecutionMo
|
|||||||
|
|
||||||
return CrossTarget{
|
return CrossTarget{
|
||||||
.cpu_arch = asked_arch,
|
.cpu_arch = asked_arch,
|
||||||
.cpu_model = switch (common.cpu.arch) {
|
.cpu_model = switch (cpu.arch) {
|
||||||
.x86 => .determined_by_cpu_arch,
|
.x86 => .determined_by_cpu_arch,
|
||||||
.x86_64 => if (execution_mode == .privileged) .determined_by_cpu_arch else
|
.x86_64 => if (execution_mode == .privileged) .determined_by_cpu_arch else
|
||||||
// zig fmt off
|
// zig fmt off
|
||||||
.determined_by_cpu_arch,
|
.determined_by_cpu_arch,
|
||||||
// .determined_by_cpu_arch,
|
// .determined_by_cpu_arch,
|
||||||
// TODO: this causes some problems: https://github.com/ziglang/zig/issues/15524
|
// TODO: this causes some problems: https://github.com/ziglang/zig/issues/15524
|
||||||
//.{ .explicit = &common.Target.x86.cpu.x86_64_v3 },
|
//.{ .explicit = &Target.x86.cpu.x86_64_v3 },
|
||||||
else => .determined_by_cpu_arch,
|
else => .determined_by_cpu_arch,
|
||||||
},
|
},
|
||||||
.os_tag = .freestanding,
|
.os_tag = .freestanding,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"architecture": "x86_64",
|
"architecture": "x86_64",
|
||||||
"bootloader": "limine",
|
"bootloader": "birth",
|
||||||
"boot_protocol": "uefi",
|
"boot_protocol": "uefi",
|
||||||
"execution_environment": "qemu",
|
"execution_environment": "qemu",
|
||||||
"optimize_mode": "Debug",
|
"optimize_mode": "Debug",
|
||||||
|
106
src/birth.zig
106
src/birth.zig
@ -1,23 +1,111 @@
|
|||||||
const lib = @import("lib");
|
const lib = @import("lib");
|
||||||
|
const Allocator = lib.Allocator;
|
||||||
|
const assert = lib.assert;
|
||||||
|
|
||||||
pub const arch = @import("birth/arch.zig");
|
pub const arch = @import("birth/arch.zig");
|
||||||
pub const capabilities = @import("birth/capabilities.zig");
|
pub const interface = @import("birth/interface.zig");
|
||||||
pub const syscall = @import("birth/syscall.zig");
|
|
||||||
|
|
||||||
/// This struct is the shared part that the user and the cpu see
|
/// This struct is the shared part that the user and the cpu see
|
||||||
pub const UserScheduler = extern struct {
|
pub const Scheduler = extern struct {
|
||||||
self: *UserScheduler,
|
common: Common,
|
||||||
|
current_thread: *Thread,
|
||||||
|
thread_queue: ?*Thread = null,
|
||||||
|
time_slice: u32,
|
||||||
|
core_id: u32,
|
||||||
|
core_state: CoreState,
|
||||||
|
bootstrap_thread: Thread,
|
||||||
|
fast_allocator: Allocator,
|
||||||
|
|
||||||
|
pub fn initializeAllocator(scheduler: *Scheduler) void {
|
||||||
|
scheduler.fast_allocator = Allocator{
|
||||||
|
.callbacks = .{
|
||||||
|
.allocate = callbackAllocate,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn callbackAllocate(allocator: *Allocator, size: u64, alignment: u64) Allocator.Allocate.Error!Allocator.Allocate.Result {
|
||||||
|
const scheduler = @fieldParentPtr(Scheduler, "fast_allocator", allocator);
|
||||||
|
assert(scheduler.common.heap.address.isAligned(alignment));
|
||||||
|
const result = scheduler.common.heap.takeSlice(size) catch return error.OutOfMemory;
|
||||||
|
return @bitCast(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const Common = extern struct {
|
||||||
|
self: *Common,
|
||||||
disabled: bool,
|
disabled: bool,
|
||||||
has_work: bool,
|
has_work: bool,
|
||||||
core_id: u32,
|
core_id: u32,
|
||||||
setup_stack: [lib.arch.valid_page_sizes[0]]u8 align(lib.arch.stack_alignment),
|
heap: lib.VirtualMemoryRegion,
|
||||||
|
setup_stack: [lib.arch.valid_page_sizes[0] * 4]u8 align(lib.arch.stack_alignment),
|
||||||
setup_stack_lock: lib.Atomic(bool),
|
setup_stack_lock: lib.Atomic(bool),
|
||||||
|
disabled_save_area: arch.RegisterArena,
|
||||||
|
|
||||||
pub inline fn architectureSpecific(user_scheduler: *UserScheduler) *arch.UserScheduler {
|
// pub fn heapAllocateFast(common: *Common, comptime T: type) !*T {
|
||||||
return @fieldParentPtr(arch.UserScheduler, "generic", user_scheduler);
|
// const size = @sizeOf(T);
|
||||||
|
// const alignment = @alignOf(T);
|
||||||
|
// lib.log.debug("Heap: {}. Size: {}. Alignment: {}", .{ common.heap, size, alignment });
|
||||||
|
// const result = try common.heap.takeSlice(size);
|
||||||
|
// const ptr = &result.access(T)[0];
|
||||||
|
// assert(lib.isAligned(@intFromPtr(ptr), alignment));
|
||||||
|
//
|
||||||
|
// return ptr;
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn enqueueThread(scheduler: *Scheduler, thread_to_queue: *Thread) void {
|
||||||
|
// TODO: check queue
|
||||||
|
// TODO: defer check queue
|
||||||
|
if (scheduler.thread_queue) |thread_queue| {
|
||||||
|
_ = thread_queue;
|
||||||
|
@panic("TODO: enqueueThread");
|
||||||
|
} else {
|
||||||
|
scheduler.thread_queue = thread_to_queue;
|
||||||
|
thread_to_queue.previous = thread_to_queue;
|
||||||
|
thread_to_queue.next = thread_to_queue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub noinline fn restore(scheduler: *Scheduler, register_arena: *const arch.RegisterArena) noreturn {
|
||||||
|
assert(scheduler.common.generic.disabled);
|
||||||
|
assert(scheduler.common.generic.has_work);
|
||||||
|
|
||||||
|
assert(register_arena.registers.rip > lib.arch.valid_page_sizes[0]);
|
||||||
|
assert(register_arena.registers.rflags.IF and register_arena.registers.rflags.reserved0);
|
||||||
|
|
||||||
|
register_arena.contextSwitch();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const CommandBuffer = struct {
|
pub const Thread = extern struct {
|
||||||
foo: u32,
|
self: *Thread,
|
||||||
|
previous: ?*Thread = null,
|
||||||
|
next: ?*Thread = null,
|
||||||
|
stack: [*]u8,
|
||||||
|
stack_top: [*]align(lib.arch.stack_alignment) u8,
|
||||||
|
register_arena: arch.RegisterArena align(arch.RegisterArena.alignment),
|
||||||
|
core_id: u32,
|
||||||
|
|
||||||
|
pub fn init(thread: *Thread, scheduler: *Scheduler) void {
|
||||||
|
thread.* = Thread{
|
||||||
|
.self = thread,
|
||||||
|
.core_id = scheduler.generic.core_id,
|
||||||
|
.stack = thread.stack,
|
||||||
|
.stack_top = thread.stack_top,
|
||||||
|
.register_arena = thread.register_arena,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const CoreState = extern struct {
|
||||||
|
virtual_address_space: *VirtualAddressSpace,
|
||||||
|
};
|
||||||
|
pub const VirtualAddressSpace = extern struct {
|
||||||
|
// TODO: physical map
|
||||||
|
// TODO: layout
|
||||||
|
regions: ?*VirtualMemoryRegion = null,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const VirtualMemoryRegion = extern struct {
|
||||||
|
next: ?*VirtualMemoryRegion = null,
|
||||||
};
|
};
|
||||||
|
@ -2,18 +2,14 @@ const lib = @import("lib");
|
|||||||
const assert = lib.assert;
|
const assert = lib.assert;
|
||||||
const birth = @import("birth");
|
const birth = @import("birth");
|
||||||
|
|
||||||
pub const UserScheduler = extern struct {
|
|
||||||
generic: birth.UserScheduler,
|
|
||||||
disabled_save_area: RegisterArena,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const RegisterArena = extern struct {
|
pub const RegisterArena = extern struct {
|
||||||
fpu: FPU align(lib.arch.stack_alignment),
|
fpu: FPU align(lib.arch.stack_alignment),
|
||||||
registers: birth.arch.Registers,
|
registers: birth.arch.Registers,
|
||||||
|
|
||||||
|
pub const alignment = lib.arch.stack_alignment;
|
||||||
|
|
||||||
pub fn contextSwitch(register_arena: *align(lib.arch.stack_alignment) const RegisterArena) noreturn {
|
pub fn contextSwitch(register_arena: *align(lib.arch.stack_alignment) const RegisterArena) noreturn {
|
||||||
assert(lib.isAligned(@intFromPtr(register_arena), lib.arch.stack_alignment));
|
assert(lib.isAligned(@intFromPtr(register_arena), lib.arch.stack_alignment));
|
||||||
//lib.log.debug("ASDASD: {}", .{register_arena});
|
|
||||||
register_arena.fpu.load();
|
register_arena.fpu.load();
|
||||||
register_arena.registers.restore();
|
register_arena.registers.restore();
|
||||||
}
|
}
|
||||||
@ -129,9 +125,9 @@ pub const FPU = extern struct {
|
|||||||
pub const user_code_selector = 0x43;
|
pub const user_code_selector = 0x43;
|
||||||
pub const user_data_selector = 0x3b;
|
pub const user_data_selector = 0x3b;
|
||||||
|
|
||||||
pub inline fn syscall(options: birth.syscall.Options, arguments: birth.syscall.Arguments) birth.syscall.Result {
|
pub inline fn syscall(options: birth.interface.Raw.Options, arguments: birth.interface.Raw.Arguments) birth.interface.Raw.Result {
|
||||||
var first: birth.syscall.Result.Birth.First = undefined;
|
var first: birth.interface.Raw.Result.Birth.First = undefined;
|
||||||
var second: birth.syscall.Result.Birth.Second = undefined;
|
var second: birth.interface.Raw.Result.Birth.Second = undefined;
|
||||||
asm volatile (
|
asm volatile (
|
||||||
\\syscall
|
\\syscall
|
||||||
: [rax] "={rax}" (first),
|
: [rax] "={rax}" (first),
|
||||||
@ -153,3 +149,28 @@ pub inline fn syscall(options: birth.syscall.Options, arguments: birth.syscall.A
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const PageTable = extern struct {
|
||||||
|
/// The frame that holds the memory of this page table
|
||||||
|
frame: birth.interface.RAM,
|
||||||
|
flags: packed struct(u8) {
|
||||||
|
level: Level4, // TODO: move to other
|
||||||
|
granularity: Granularity,
|
||||||
|
reserved: u4 = 0,
|
||||||
|
},
|
||||||
|
|
||||||
|
pub const Granularity = enum(u2) {
|
||||||
|
@"4_kb",
|
||||||
|
@"2_mb",
|
||||||
|
@"1_gb",
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Level4 = enum(u2) {
|
||||||
|
PML4 = 0,
|
||||||
|
PDP = 1,
|
||||||
|
PD = 2,
|
||||||
|
PT = 3,
|
||||||
|
|
||||||
|
pub const count = lib.enumCount(@This());
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@ -1,381 +0,0 @@
|
|||||||
const lib = @import("lib");
|
|
||||||
const assert = lib.assert;
|
|
||||||
const PhysicalAddress = lib.PhysicalAddress;
|
|
||||||
|
|
||||||
const birth = @import("birth");
|
|
||||||
const syscall = birth.syscall;
|
|
||||||
|
|
||||||
const Capabilities = @This();
|
|
||||||
|
|
||||||
pub const Type = enum(u8) {
|
|
||||||
io, // primitive
|
|
||||||
cpu, // primitive
|
|
||||||
ram, // primitive
|
|
||||||
cpu_memory, // non-primitive Barrelfish: frame
|
|
||||||
boot,
|
|
||||||
process, // Temporarily available
|
|
||||||
page_table, // Barrelfish: vnode
|
|
||||||
// TODO: device_memory, // primitive
|
|
||||||
// scheduler,
|
|
||||||
// irq_table,
|
|
||||||
|
|
||||||
// _,
|
|
||||||
|
|
||||||
pub const Type = u8;
|
|
||||||
|
|
||||||
pub const Mappable = enum {
|
|
||||||
cpu_memory,
|
|
||||||
page_table,
|
|
||||||
|
|
||||||
pub inline fn toCapability(mappable: Mappable) Capabilities.Type {
|
|
||||||
return switch (mappable) {
|
|
||||||
inline else => |mappable_cap| @field(Capabilities.Type, @tagName(mappable_cap)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Subtype = u16;
|
|
||||||
pub const AllTypes = Type;
|
|
||||||
|
|
||||||
pub fn CommandBuilder(comptime list: []const []const u8) type {
|
|
||||||
const capability_base_command_list = .{
|
|
||||||
"copy",
|
|
||||||
"mint",
|
|
||||||
"retype",
|
|
||||||
"delete",
|
|
||||||
"revoke",
|
|
||||||
"create",
|
|
||||||
} ++ list;
|
|
||||||
const enum_fields = lib.enumAddNames(&.{}, capability_base_command_list);
|
|
||||||
|
|
||||||
// TODO: make this non-exhaustive enums
|
|
||||||
// PROBLEM: https://github.com/ziglang/zig/issues/12250
|
|
||||||
// Currently waiting on this since this will enable some comptime magic
|
|
||||||
const result = @Type(.{
|
|
||||||
.Enum = .{
|
|
||||||
.tag_type = Subtype,
|
|
||||||
.fields = enum_fields,
|
|
||||||
.decls = &.{},
|
|
||||||
.is_exhaustive = true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Takes some names and integers. Then values are added to the Command enum for an specific capability
|
|
||||||
/// The number is an offset of the fields with respect to the base command enum fields
|
|
||||||
pub fn Command(comptime capability: Type) type {
|
|
||||||
const extra_command_list = switch (capability) {
|
|
||||||
.io => .{
|
|
||||||
"log",
|
|
||||||
},
|
|
||||||
.cpu => .{
|
|
||||||
"get_core_id",
|
|
||||||
"shutdown",
|
|
||||||
"get_command_buffer",
|
|
||||||
},
|
|
||||||
.ram => [_][]const u8{},
|
|
||||||
.cpu_memory => .{
|
|
||||||
"allocate",
|
|
||||||
},
|
|
||||||
.boot => .{
|
|
||||||
"get_bundle_size",
|
|
||||||
"get_bundle_file_list_size",
|
|
||||||
},
|
|
||||||
.process => .{
|
|
||||||
"exit",
|
|
||||||
},
|
|
||||||
.page_table => [_][]const u8{},
|
|
||||||
};
|
|
||||||
|
|
||||||
return CommandBuilder(&extra_command_list);
|
|
||||||
}
|
|
||||||
|
|
||||||
const success = 0;
|
|
||||||
const first_valid_error = success + 1;
|
|
||||||
|
|
||||||
pub fn ErrorSet(comptime error_names: []const []const u8) type {
|
|
||||||
return lib.ErrorSet(error_names, &.{
|
|
||||||
.{
|
|
||||||
.name = "forbidden",
|
|
||||||
.value = first_valid_error + 0,
|
|
||||||
},
|
|
||||||
.{
|
|
||||||
.name = "corrupted_input",
|
|
||||||
.value = first_valid_error + 1,
|
|
||||||
},
|
|
||||||
.{
|
|
||||||
.name = "invalid_input",
|
|
||||||
.value = first_valid_error + 2,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const raw_argument_count = @typeInfo(syscall.Arguments).Array.len;
|
|
||||||
|
|
||||||
pub fn Syscall(comptime capability_type: Type, comptime command_type: Command(capability_type)) type {
|
|
||||||
const Types = switch (capability_type) {
|
|
||||||
.io => switch (command_type) {
|
|
||||||
.copy, .mint, .retype, .delete, .revoke, .create => struct {
|
|
||||||
pub const ErrorSet = Capabilities.ErrorSet(&.{});
|
|
||||||
pub const Result = void;
|
|
||||||
pub const Arguments = void;
|
|
||||||
},
|
|
||||||
.log => struct {
|
|
||||||
pub const ErrorSet = Capabilities.ErrorSet(&.{});
|
|
||||||
pub const Result = usize;
|
|
||||||
pub const Arguments = []const u8;
|
|
||||||
|
|
||||||
inline fn toResult(raw_result: syscall.Result.Birth) Result {
|
|
||||||
return raw_result.second;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fn resultToRaw(result: Result) syscall.Result {
|
|
||||||
return syscall.Result{
|
|
||||||
.birth = .{
|
|
||||||
.first = .{},
|
|
||||||
.second = result,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fn argumentsToRaw(arguments: Arguments) syscall.Arguments {
|
|
||||||
const result = [2]usize{ @intFromPtr(arguments.ptr), arguments.len };
|
|
||||||
return result ++ .{0} ** (raw_argument_count - result.len);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fn toArguments(raw_arguments: syscall.Arguments) !Arguments {
|
|
||||||
const message_ptr = @as(?[*]const u8, @ptrFromInt(raw_arguments[0])) orelse return error.invalid_input;
|
|
||||||
const message_len = raw_arguments[1];
|
|
||||||
if (message_len == 0) return error.invalid_input;
|
|
||||||
const message = message_ptr[0..message_len];
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
.cpu => switch (command_type) {
|
|
||||||
.copy, .mint, .retype, .delete, .revoke, .create => struct {
|
|
||||||
pub const ErrorSet = Capabilities.ErrorSet(&.{});
|
|
||||||
pub const Result = void;
|
|
||||||
pub const Arguments = void;
|
|
||||||
},
|
|
||||||
.get_core_id => struct {
|
|
||||||
pub const ErrorSet = Capabilities.ErrorSet(&.{});
|
|
||||||
pub const Result = u32;
|
|
||||||
pub const Arguments = void;
|
|
||||||
|
|
||||||
inline fn toResult(raw_result: syscall.Result.birth) Result {
|
|
||||||
return @as(Result, @intCast(raw_result.second));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fn resultToRaw(result: Result) syscall.Result {
|
|
||||||
return syscall.Result{
|
|
||||||
.birth = .{
|
|
||||||
.first = .{},
|
|
||||||
.second = result,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.shutdown => struct {
|
|
||||||
pub const ErrorSet = Capabilities.ErrorSet(&.{});
|
|
||||||
pub const Result = noreturn;
|
|
||||||
pub const Arguments = void;
|
|
||||||
|
|
||||||
pub const toResult = @compileError("noreturn unexpectedly returned");
|
|
||||||
},
|
|
||||||
.get_command_buffer => struct {
|
|
||||||
pub const ErrorSet = Capabilities.ErrorSet(&.{});
|
|
||||||
pub const Result = noreturn;
|
|
||||||
pub const Arguments = *birth.CommandBuffer;
|
|
||||||
|
|
||||||
pub const toResult = @compileError("noreturn unexpectedly returned");
|
|
||||||
|
|
||||||
inline fn toArguments(raw_arguments: syscall.Arguments) !Arguments {
|
|
||||||
const ptr = @as(?*birth.CommandBuffer, @ptrFromInt(raw_arguments[0])) orelse return error.invalid_input;
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fn argumentsToRaw(arguments: Arguments) syscall.Arguments {
|
|
||||||
const result = [1]usize{@intFromPtr(arguments)};
|
|
||||||
return result ++ .{0} ** (raw_argument_count - result.len);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
.ram => struct {
|
|
||||||
pub const ErrorSet = Capabilities.ErrorSet(&.{});
|
|
||||||
pub const Result = void;
|
|
||||||
pub const Arguments = void;
|
|
||||||
},
|
|
||||||
.cpu_memory => struct {
|
|
||||||
pub const ErrorSet = Capabilities.ErrorSet(&.{
|
|
||||||
"OutOfMemory",
|
|
||||||
});
|
|
||||||
pub const Result = PhysicalAddress;
|
|
||||||
pub const Arguments = usize;
|
|
||||||
|
|
||||||
inline fn toResult(raw_result: syscall.Result.birth) Result {
|
|
||||||
return PhysicalAddress.new(raw_result.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fn resultToRaw(result: Result) syscall.Result {
|
|
||||||
return syscall.Result{
|
|
||||||
.birth = .{
|
|
||||||
.first = .{},
|
|
||||||
.second = result.value(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fn toArguments(raw_arguments: syscall.Arguments) !Arguments {
|
|
||||||
const size = raw_arguments[0];
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fn argumentsToRaw(arguments: Arguments) syscall.Arguments {
|
|
||||||
const result = [1]usize{arguments};
|
|
||||||
return result ++ .{0} ** (raw_argument_count - result.len);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.boot => switch (command_type) {
|
|
||||||
.get_bundle_file_list_size, .get_bundle_size => struct {
|
|
||||||
pub const ErrorSet = Capabilities.ErrorSet(&.{
|
|
||||||
"buffer_too_small",
|
|
||||||
});
|
|
||||||
pub const Result = usize;
|
|
||||||
pub const Arguments = void;
|
|
||||||
|
|
||||||
inline fn resultToRaw(result: Result) syscall.Result {
|
|
||||||
return syscall.Result{
|
|
||||||
.birth = .{
|
|
||||||
.first = .{},
|
|
||||||
.second = result,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fn toResult(raw_result: syscall.Result.birth) Result {
|
|
||||||
return raw_result.second;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
else => struct {
|
|
||||||
pub const ErrorSet = Capabilities.ErrorSet(&.{
|
|
||||||
"buffer_too_small",
|
|
||||||
});
|
|
||||||
pub const Result = void;
|
|
||||||
pub const Arguments = void;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
.process => switch (command_type) {
|
|
||||||
.exit => struct {
|
|
||||||
pub const ErrorSet = Capabilities.ErrorSet(&.{});
|
|
||||||
pub const Result = noreturn;
|
|
||||||
pub const Arguments = bool;
|
|
||||||
|
|
||||||
inline fn toArguments(raw_arguments: syscall.Arguments) !Arguments {
|
|
||||||
const result = raw_arguments[0] != 0;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
inline fn argumentsToRaw(arguments: Arguments) syscall.Arguments {
|
|
||||||
const result = [1]usize{@intFromBool(arguments)};
|
|
||||||
return result ++ .{0} ** (raw_argument_count - result.len);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
else => struct {
|
|
||||||
pub const ErrorSet = Capabilities.ErrorSet(&.{});
|
|
||||||
pub const Result = void;
|
|
||||||
pub const Arguments = void;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
.page_table => switch (command_type) {
|
|
||||||
else => struct {
|
|
||||||
pub const ErrorSet = Capabilities.ErrorSet(&.{});
|
|
||||||
pub const Result = void;
|
|
||||||
pub const Arguments = void;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// else => @compileError("TODO: " ++ @tagName(capability)),
|
|
||||||
};
|
|
||||||
|
|
||||||
return struct {
|
|
||||||
pub const ErrorSet = Types.ErrorSet;
|
|
||||||
pub const Result = Types.Result;
|
|
||||||
pub const Arguments = Types.Arguments;
|
|
||||||
pub const toResult = Types.toResult;
|
|
||||||
pub const toArguments = if (Arguments != void)
|
|
||||||
Types.toArguments
|
|
||||||
else
|
|
||||||
struct {
|
|
||||||
fn lambda(raw_arguments: syscall.Arguments) error{}!void {
|
|
||||||
_ = raw_arguments;
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}.lambda;
|
|
||||||
pub const capability = capability_type;
|
|
||||||
pub const command = command_type;
|
|
||||||
|
|
||||||
pub inline fn resultToRaw(result: Result) syscall.Result {
|
|
||||||
return if (@hasDecl(Types, "resultToRaw")) blk: {
|
|
||||||
comptime assert(Result != void and Result != noreturn);
|
|
||||||
break :blk Types.resultToRaw(result);
|
|
||||||
} else blk: {
|
|
||||||
if (Result != void) {
|
|
||||||
@compileError("expected void type, got " ++ @typeName(Result) ++ ". You forgot to implement a resultToRaw function" ++ " for (" ++ @tagName(capability) ++ ", " ++ @tagName(command) ++ ").");
|
|
||||||
}
|
|
||||||
|
|
||||||
break :blk syscall.Result{
|
|
||||||
.birth = .{
|
|
||||||
.first = .{},
|
|
||||||
.second = 0,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn errorToRaw(err: @This().ErrorSet.Error) syscall.Result {
|
|
||||||
const error_enum = switch (err) {
|
|
||||||
inline else => |comptime_error| @field(@This().ErrorSet.Enum, @errorName(comptime_error)),
|
|
||||||
};
|
|
||||||
return syscall.Result{
|
|
||||||
.birth = .{
|
|
||||||
.first = .{
|
|
||||||
.@"error" = @intFromEnum(error_enum),
|
|
||||||
},
|
|
||||||
.second = 0,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This is not meant to be called in the CPU driver
|
|
||||||
pub fn blocking(arguments: Arguments) @This().ErrorSet.Error!Result {
|
|
||||||
const raw_arguments = if (Arguments != void) Types.argumentsToRaw(arguments) else [1]usize{0} ** raw_argument_count;
|
|
||||||
// TODO: make this more reliable and robust?
|
|
||||||
const options = birth.syscall.Options{
|
|
||||||
.birth = .{
|
|
||||||
.type = capability,
|
|
||||||
.command = @intFromEnum(command),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const raw_result = birth.arch.syscall(options, raw_arguments);
|
|
||||||
|
|
||||||
const raw_error_value = raw_result.birth.first.@"error";
|
|
||||||
comptime {
|
|
||||||
assert(!@hasField(@This().ErrorSet.Enum, "ok"));
|
|
||||||
assert(!@hasField(@This().ErrorSet.Enum, "success"));
|
|
||||||
assert(lib.enumFields(@This().ErrorSet.Enum)[0].value == first_valid_error);
|
|
||||||
}
|
|
||||||
|
|
||||||
return switch (raw_error_value) {
|
|
||||||
success => switch (Result) {
|
|
||||||
noreturn => unreachable,
|
|
||||||
else => toResult(raw_result.birth),
|
|
||||||
},
|
|
||||||
else => switch (@as(@This().ErrorSet.Enum, @enumFromInt(raw_error_value))) {
|
|
||||||
inline else => |comptime_error_enum| @field(@This().ErrorSet.Error, @tagName(comptime_error_enum)),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
1092
src/birth/interface.zig
Normal file
1092
src/birth/interface.zig
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,117 +0,0 @@
|
|||||||
const lib = @import("lib");
|
|
||||||
const assert = lib.assert;
|
|
||||||
const log = lib.log.scoped(.Syscall);
|
|
||||||
|
|
||||||
const birth = @import("birth");
|
|
||||||
const capabilities = birth.capabilities;
|
|
||||||
|
|
||||||
pub const argument_count = 6;
|
|
||||||
pub const Arguments = [argument_count]usize;
|
|
||||||
|
|
||||||
pub const Convention = enum(u1) {
|
|
||||||
linux = 0,
|
|
||||||
birth = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Options = extern union {
|
|
||||||
general: General,
|
|
||||||
birth: Birth,
|
|
||||||
linux: Linux,
|
|
||||||
|
|
||||||
pub const General = packed struct(u64) {
|
|
||||||
number: Number,
|
|
||||||
convention: Convention,
|
|
||||||
|
|
||||||
pub const Number = lib.IntType(.unsigned, union_space_bits);
|
|
||||||
|
|
||||||
comptime {
|
|
||||||
assertSize(@This());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn getNumberInteger(general: General, comptime convention: Convention) NumberIntegerType(convention) {
|
|
||||||
const options_integer = @as(u64, @bitCast(general));
|
|
||||||
return @as(NumberIntegerType(convention), @truncate(options_integer));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn NumberIntegerType(comptime convention: Convention) type {
|
|
||||||
return switch (convention) {
|
|
||||||
.birth => birth.IDInteger,
|
|
||||||
.linux => u64,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Birth = packed struct(u64) {
|
|
||||||
type: capabilities.Type,
|
|
||||||
command: capabilities.Subtype,
|
|
||||||
reserved: lib.IntType(.unsigned, @bitSizeOf(u64) - @bitSizeOf(capabilities.Type) - @bitSizeOf(capabilities.Subtype) - @bitSizeOf(Convention)) = 0,
|
|
||||||
convention: Convention = .birth,
|
|
||||||
|
|
||||||
comptime {
|
|
||||||
Options.assertSize(@This());
|
|
||||||
}
|
|
||||||
|
|
||||||
const IDInteger = u16;
|
|
||||||
pub const ID = enum(IDInteger) {
|
|
||||||
qemu_exit = 0,
|
|
||||||
print = 1,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Linux = enum(u64) {
|
|
||||||
_,
|
|
||||||
comptime {
|
|
||||||
Options.assertSize(@This());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const union_space_bits = @bitSizeOf(u64) - @bitSizeOf(Convention);
|
|
||||||
|
|
||||||
fn assertSize(comptime T: type) void {
|
|
||||||
assert(@sizeOf(T) == @sizeOf(u64));
|
|
||||||
assert(@bitSizeOf(T) == @bitSizeOf(u64));
|
|
||||||
}
|
|
||||||
|
|
||||||
comptime {
|
|
||||||
assertSize(@This());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Result = extern union {
|
|
||||||
general: General,
|
|
||||||
birth: Birth,
|
|
||||||
linux: Linux,
|
|
||||||
|
|
||||||
pub const General = extern struct {
|
|
||||||
first: packed struct(u64) {
|
|
||||||
argument: u63,
|
|
||||||
convention: Convention,
|
|
||||||
},
|
|
||||||
second: u64,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Birth = extern struct {
|
|
||||||
first: First,
|
|
||||||
second: Second,
|
|
||||||
|
|
||||||
pub const First = packed struct(u64) {
|
|
||||||
padding1: u32 = 0,
|
|
||||||
@"error": u16 = 0,
|
|
||||||
padding2: u8 = 0,
|
|
||||||
padding3: u7 = 0,
|
|
||||||
convention: Convention = .birth,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Second = u64;
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Linux = extern struct {
|
|
||||||
result: u64,
|
|
||||||
reserved: u64 = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn assertSize(comptime T: type) void {
|
|
||||||
assert(@sizeOf(T) == @sizeOf(u64));
|
|
||||||
assert(@bitSizeOf(T) == @bitSizeOf(u64));
|
|
||||||
}
|
|
||||||
};
|
|
@ -315,6 +315,13 @@ pub const Information = extern struct {
|
|||||||
return Error.unexpected_memory_map_entry_count;
|
return Error.unexpected_memory_map_entry_count;
|
||||||
}
|
}
|
||||||
bootloader_information.configuration.memory_map_diff = @as(u8, @intCast(memory_map_entry_count - new_memory_map_entry_count));
|
bootloader_information.configuration.memory_map_diff = @as(u8, @intCast(memory_map_entry_count - new_memory_map_entry_count));
|
||||||
|
|
||||||
|
const entries = bootloader_information.getMemoryMapEntries();
|
||||||
|
const entry = entries[total_allocation.index];
|
||||||
|
assert(entry.region.address.value() == total_allocation.region.address.value());
|
||||||
|
assert(entry.region.size == total_allocation.region.size);
|
||||||
|
|
||||||
|
page_counters[total_allocation.index] = bootloader_information.getAlignedTotalSize() >> lib.arch.page_shifter(lib.arch.valid_page_sizes[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the host entry still corresponds to the same index
|
// Check if the host entry still corresponds to the same index
|
||||||
@ -366,7 +373,9 @@ pub const Information = extern struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const aligned_size = lib.alignForward(u64, ph.size_in_memory, lib.arch.valid_page_sizes[0]);
|
const aligned_size = lib.alignForward(u64, ph.size_in_memory, lib.arch.valid_page_sizes[0]);
|
||||||
const physical_allocation = try bootloader_information.allocatePages(aligned_size, lib.arch.valid_page_sizes[0], .{});
|
const physical_allocation = try bootloader_information.allocatePages(aligned_size, lib.arch.valid_page_sizes[0], .{
|
||||||
|
.virtual_address = @bitCast(@as(u64, 0)),
|
||||||
|
});
|
||||||
const physical_address = physical_allocation.address;
|
const physical_address = physical_allocation.address;
|
||||||
const virtual_address = VirtualAddress.new(ph.virtual_address);
|
const virtual_address = VirtualAddress.new(ph.virtual_address);
|
||||||
const flags = Mapping.Flags{ .write = ph.flags.writable, .execute = ph.flags.executable };
|
const flags = Mapping.Flags{ .write = ph.flags.writable, .execute = ph.flags.executable };
|
||||||
@ -469,7 +478,7 @@ pub const Information = extern struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn getSlice(information: *const Information, comptime offset_name: Slice.Name) []Slice.TypeMap[@intFromEnum(offset_name)] {
|
pub inline fn getSlice(information: *const Information, comptime offset_name: Slice.Name) []Slice.TypeMap[@intFromEnum(offset_name)] {
|
||||||
const slice_offset = information.slices.array.values[@intFromEnum(offset_name)];
|
const slice_offset = &information.slices.array.values[@intFromEnum(offset_name)];
|
||||||
return slice_offset.dereference(offset_name, information);
|
return slice_offset.dereference(offset_name, information);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -684,6 +693,10 @@ pub const MemoryMapEntry = extern struct {
|
|||||||
bad_memory = 2,
|
bad_memory = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub fn getUsedRegion(mmap_entry: MemoryMapEntry, page_counter: u32) PhysicalMemoryRegion {
|
||||||
|
return mmap_entry.region.slice(page_counter << lib.arch.page_shifter(lib.arch.valid_page_sizes[0]));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn getFreeRegion(mmap_entry: MemoryMapEntry, page_counter: u32) PhysicalMemoryRegion {
|
pub fn getFreeRegion(mmap_entry: MemoryMapEntry, page_counter: u32) PhysicalMemoryRegion {
|
||||||
return mmap_entry.region.offset(page_counter << lib.arch.page_shifter(lib.arch.valid_page_sizes[0]));
|
return mmap_entry.region.offset(page_counter << lib.arch.page_shifter(lib.arch.valid_page_sizes[0]));
|
||||||
}
|
}
|
||||||
|
@ -68,10 +68,8 @@ pub const Disk = extern struct {
|
|||||||
.segment = 0,
|
.segment = 0,
|
||||||
.lba = lba,
|
.lba = lba,
|
||||||
};
|
};
|
||||||
lib.log.debug("DAP: {}", .{dap});
|
|
||||||
|
|
||||||
const dap_address = @intFromPtr(&dap);
|
const dap_address = @intFromPtr(&dap);
|
||||||
lib.log.debug("DAP address: 0x{x}", .{dap_address});
|
|
||||||
const dap_offset = offset(dap_address);
|
const dap_offset = offset(dap_address);
|
||||||
const dap_segment = segment(dap_address);
|
const dap_segment = segment(dap_address);
|
||||||
var registers = Registers{
|
var registers = Registers{
|
||||||
@ -81,9 +79,7 @@ pub const Disk = extern struct {
|
|||||||
.ds = dap_segment,
|
.ds = dap_segment,
|
||||||
};
|
};
|
||||||
|
|
||||||
lib.log.debug("Start int", .{});
|
|
||||||
interrupt(0x13, ®isters, ®isters);
|
interrupt(0x13, ®isters, ®isters);
|
||||||
lib.log.debug("End int", .{});
|
|
||||||
|
|
||||||
if (registers.eflags.flags.carry_flag) return error.read_error;
|
if (registers.eflags.flags.carry_flag) return error.read_error;
|
||||||
|
|
||||||
@ -92,7 +88,6 @@ pub const Disk = extern struct {
|
|||||||
const src_slice = buffer[0..bytes_to_copy];
|
const src_slice = buffer[0..bytes_to_copy];
|
||||||
|
|
||||||
if (maybe_provided_buffer) |provided_buffer| {
|
if (maybe_provided_buffer) |provided_buffer| {
|
||||||
lib.log.debug("A", .{});
|
|
||||||
const dst_slice = provided_buffer[@as(usize, @intCast(provided_buffer_offset))..][0..bytes_to_copy];
|
const dst_slice = provided_buffer[@as(usize, @intCast(provided_buffer_offset))..][0..bytes_to_copy];
|
||||||
|
|
||||||
// TODO: report Zig that this codegen is so bad that we have to use rep movsb instead to make it go fast
|
// TODO: report Zig that this codegen is so bad that we have to use rep movsb instead to make it go fast
|
||||||
@ -102,19 +97,11 @@ pub const Disk = extern struct {
|
|||||||
const use_rep_movsb = true;
|
const use_rep_movsb = true;
|
||||||
if (use_rep_movsb) {
|
if (use_rep_movsb) {
|
||||||
lib.memcpy(dst_slice, src_slice);
|
lib.memcpy(dst_slice, src_slice);
|
||||||
const bytes_left = asm volatile (
|
|
||||||
\\rep movsb
|
|
||||||
: [ret] "={ecx}" (-> usize),
|
|
||||||
: [dest] "{edi}" (dst_slice.ptr),
|
|
||||||
[src] "{esi}" (src_slice.ptr),
|
|
||||||
[len] "{ecx}" (src_slice.len),
|
|
||||||
);
|
|
||||||
assert(bytes_left == 0);
|
|
||||||
} else {
|
} else {
|
||||||
@memcpy(dst_slice, src_slice);
|
@memcpy(dst_slice, src_slice);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
lib.log.debug("B", .{});
|
//lib.log.debug("B", .{});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,8 +5,7 @@ const log = lib.log;
|
|||||||
const privileged = @import("privileged");
|
const privileged = @import("privileged");
|
||||||
const ACPI = privileged.ACPI;
|
const ACPI = privileged.ACPI;
|
||||||
const MemoryManager = privileged.MemoryManager;
|
const MemoryManager = privileged.MemoryManager;
|
||||||
const PhysicalHeap = privileged.PhyicalHeap;
|
pub const writer = privileged.writer;
|
||||||
const writer = privileged.writer;
|
|
||||||
|
|
||||||
const stopCPU = privileged.arch.stopCPU;
|
const stopCPU = privileged.arch.stopCPU;
|
||||||
const GDT = privileged.arch.x86_64.GDT;
|
const GDT = privileged.arch.x86_64.GDT;
|
||||||
@ -54,10 +53,7 @@ pub const std_options = struct {
|
|||||||
_ = format;
|
_ = format;
|
||||||
_ = scope;
|
_ = scope;
|
||||||
_ = level;
|
_ = level;
|
||||||
// _ = level;
|
_ = level;
|
||||||
// writer.writeByte('[') catch stopCPU();
|
|
||||||
// writer.writeAll(@tagName(scope)) catch stopCPU();
|
|
||||||
// writer.writeAll("] ") catch stopCPU();
|
|
||||||
// lib.format(writer, format, args) catch stopCPU();
|
// lib.format(writer, format, args) catch stopCPU();
|
||||||
// writer.writeByte('\n') catch stopCPU();
|
// writer.writeByte('\n') catch stopCPU();
|
||||||
}
|
}
|
||||||
@ -83,17 +79,13 @@ const Filesystem = extern struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn readFile(filesystem: *Filesystem, file_path: []const u8, file_buffer: []u8) ![]const u8 {
|
pub fn readFile(filesystem: *Filesystem, file_path: []const u8, file_buffer: []u8) ![]const u8 {
|
||||||
log.debug("File {s} read started", .{file_path});
|
|
||||||
assert(filesystem.fat_allocator.allocated <= filesystem.fat_allocator.buffer.len);
|
assert(filesystem.fat_allocator.allocated <= filesystem.fat_allocator.buffer.len);
|
||||||
const file = try filesystem.fat_cache.readFileToBuffer(file_path, file_buffer);
|
const file = try filesystem.fat_cache.readFileToBuffer(file_path, file_buffer);
|
||||||
log.debug("File read succeeded", .{});
|
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sneakFile(filesystem: *Filesystem, file_path: []const u8, size: usize) ![]const u8 {
|
pub fn sneakFile(filesystem: *Filesystem, file_path: []const u8, size: usize) ![]const u8 {
|
||||||
log.debug("File {s} read started", .{file_path});
|
|
||||||
const file = try filesystem.fat_cache.readFileToCache(file_path, size);
|
const file = try filesystem.fat_cache.readFileToCache(file_path, size);
|
||||||
log.debug("File read succeeded", .{});
|
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,16 +14,13 @@ const uefi = @import("uefi");
|
|||||||
const BootloaderInformation = uefi.BootloaderInformation;
|
const BootloaderInformation = uefi.BootloaderInformation;
|
||||||
const BootServices = uefi.BootServices;
|
const BootServices = uefi.BootServices;
|
||||||
const ConfigurationTable = uefi.ConfigurationTable;
|
const ConfigurationTable = uefi.ConfigurationTable;
|
||||||
const FileProtocol = uefi.FileProtocol;
|
|
||||||
const Handle = uefi.Handle;
|
const Handle = uefi.Handle;
|
||||||
const LoadedImageProtocol = uefi.LoadedImageProtocol;
|
|
||||||
const LoadKernelFunction = uefi.LoadKernelFunction;
|
const LoadKernelFunction = uefi.LoadKernelFunction;
|
||||||
const MemoryCategory = uefi.MemoryCategory;
|
const MemoryCategory = uefi.MemoryCategory;
|
||||||
const MemoryDescriptor = uefi.MemoryDescriptor;
|
const MemoryDescriptor = uefi.MemoryDescriptor;
|
||||||
const ProgramSegment = uefi.ProgramSegment;
|
const ProgramSegment = uefi.ProgramSegment;
|
||||||
const Protocol = uefi.Protocol;
|
const Protocol = uefi.Protocol;
|
||||||
const page_table_estimated_size = uefi.page_table_estimated_size;
|
const page_table_estimated_size = uefi.page_table_estimated_size;
|
||||||
const SimpleFilesystemProtocol = uefi.SimpleFilesystemProtocol;
|
|
||||||
const SystemTable = uefi.SystemTable;
|
const SystemTable = uefi.SystemTable;
|
||||||
|
|
||||||
const privileged = @import("privileged");
|
const privileged = @import("privileged");
|
||||||
@ -68,7 +65,7 @@ pub fn panic(message: []const u8, _: ?*lib.StackTrace, _: ?usize) noreturn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Filesystem = extern struct {
|
const Filesystem = extern struct {
|
||||||
root: *FileProtocol,
|
root: *uefi.protocol.File,
|
||||||
buffer: [0x200 * 10]u8 = undefined,
|
buffer: [0x200 * 10]u8 = undefined,
|
||||||
|
|
||||||
pub fn deinitialize(filesystem: *Filesystem) !void {
|
pub fn deinitialize(filesystem: *Filesystem) !void {
|
||||||
@ -91,7 +88,7 @@ const Filesystem = extern struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const FileDescriptor = struct {
|
const FileDescriptor = struct {
|
||||||
handle: *FileProtocol,
|
handle: *uefi.protocol.File,
|
||||||
path_size: u32,
|
path_size: u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -111,14 +108,14 @@ const Filesystem = extern struct {
|
|||||||
return Error.boot_services_exited;
|
return Error.boot_services_exited;
|
||||||
}
|
}
|
||||||
|
|
||||||
var file: *FileProtocol = undefined;
|
var file: *uefi.protocol.File = undefined;
|
||||||
var path_buffer: [256:0]u16 = undefined;
|
var path_buffer: [256:0]u16 = undefined;
|
||||||
const length = try lib.unicode.utf8ToUtf16Le(&path_buffer, file_path);
|
const length = try lib.unicode.utf8ToUtf16Le(&path_buffer, file_path);
|
||||||
path_buffer[length] = 0;
|
path_buffer[length] = 0;
|
||||||
const path = path_buffer[0..length :0];
|
const path = path_buffer[0..length :0];
|
||||||
const uefi_path = if (path[0] == '/') path[1..] else path;
|
const uefi_path = if (path[0] == '/') path[1..] else path;
|
||||||
|
|
||||||
try uefi.Try(filesystem.root.open(&file, uefi_path, FileProtocol.efi_file_mode_read, 0));
|
try uefi.Try(filesystem.root.open(&file, uefi_path, uefi.protocol.File.efi_file_mode_read, 0));
|
||||||
|
|
||||||
const result = FileDescriptor{
|
const result = FileDescriptor{
|
||||||
.handle = file,
|
.handle = file,
|
||||||
@ -236,16 +233,16 @@ const Initialization = struct {
|
|||||||
},
|
},
|
||||||
.filesystem = .{
|
.filesystem = .{
|
||||||
.root = blk: {
|
.root = blk: {
|
||||||
const loaded_image = try Protocol.open(LoadedImageProtocol, boot_services, handle);
|
const loaded_image = try Protocol.open(uefi.protocol.LoadedImage, boot_services, handle);
|
||||||
const filesystem_protocol = try Protocol.open(SimpleFilesystemProtocol, boot_services, loaded_image.device_handle orelse @panic("No device handle"));
|
const filesystem_protocol = try Protocol.open(uefi.protocol.SimpleFileSystem, boot_services, loaded_image.device_handle orelse @panic("No device handle"));
|
||||||
|
|
||||||
var root: *FileProtocol = undefined;
|
var root: *uefi.protocol.File = undefined;
|
||||||
try uefi.Try(filesystem_protocol.openVolume(&root));
|
try uefi.Try(filesystem_protocol.openVolume(&root));
|
||||||
break :blk root;
|
break :blk root;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
.framebuffer = blk: {
|
.framebuffer = blk: {
|
||||||
const gop = try Protocol.locate(uefi.GraphicsOutputProtocol, boot_services);
|
const gop = try Protocol.locate(uefi.protocol.GraphicsOutput, boot_services);
|
||||||
|
|
||||||
const pixel_format_info: struct {
|
const pixel_format_info: struct {
|
||||||
red_color_mask: bootloader.Framebuffer.ColorMask,
|
red_color_mask: bootloader.Framebuffer.ColorMask,
|
||||||
@ -253,20 +250,19 @@ const Initialization = struct {
|
|||||||
green_color_mask: bootloader.Framebuffer.ColorMask,
|
green_color_mask: bootloader.Framebuffer.ColorMask,
|
||||||
bpp: u8,
|
bpp: u8,
|
||||||
} = switch (gop.mode.info.pixel_format) {
|
} = switch (gop.mode.info.pixel_format) {
|
||||||
.PixelRedGreenBlueReserved8BitPerColor => .{
|
.RedGreenBlueReserved8BitPerColor => .{
|
||||||
.red_color_mask = .{ .size = 8, .shift = 0 },
|
.red_color_mask = .{ .size = 8, .shift = 0 },
|
||||||
.green_color_mask = .{ .size = 8, .shift = 8 },
|
.green_color_mask = .{ .size = 8, .shift = 8 },
|
||||||
.blue_color_mask = .{ .size = 8, .shift = 16 },
|
.blue_color_mask = .{ .size = 8, .shift = 16 },
|
||||||
.bpp = 32,
|
.bpp = 32,
|
||||||
},
|
},
|
||||||
.PixelBlueGreenRedReserved8BitPerColor => .{
|
.BlueGreenRedReserved8BitPerColor => .{
|
||||||
.red_color_mask = .{ .size = 8, .shift = 16 },
|
.red_color_mask = .{ .size = 8, .shift = 16 },
|
||||||
.green_color_mask = .{ .size = 8, .shift = 8 },
|
.green_color_mask = .{ .size = 8, .shift = 8 },
|
||||||
.blue_color_mask = .{ .size = 8, .shift = 0 },
|
.blue_color_mask = .{ .size = 8, .shift = 0 },
|
||||||
.bpp = 32,
|
.bpp = 32,
|
||||||
},
|
},
|
||||||
.PixelBitMask, .PixelBltOnly => @panic("Unsupported pixel format"),
|
.BitMask, .BltOnly => @panic("Unsupported pixel format"),
|
||||||
.PixelFormatMax => @panic("Corrupted pixel format"),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
break :blk bootloader.Framebuffer{
|
break :blk bootloader.Framebuffer{
|
||||||
|
@ -8,13 +8,14 @@ const uefi = lib.uefi;
|
|||||||
pub const BootServices = uefi.tables.BootServices;
|
pub const BootServices = uefi.tables.BootServices;
|
||||||
pub const ConfigurationTable = uefi.tables.ConfigurationTable;
|
pub const ConfigurationTable = uefi.tables.ConfigurationTable;
|
||||||
pub const Error = Status.EfiError;
|
pub const Error = Status.EfiError;
|
||||||
pub const FileInfo = uefi.protocols.FileInfo;
|
pub const FileInfo = uefi.FileInfo;
|
||||||
pub const FileProtocol = uefi.protocols.FileProtocol;
|
// pub const FileProtocol = protocol.FileProtocol;
|
||||||
pub const GraphicsOutputProtocol = uefi.protocols.GraphicsOutputProtocol;
|
// pub const GraphicsOutputProtocol = protocol.GraphicsOutputProtocol;
|
||||||
pub const LoadedImageProtocol = uefi.protocols.LoadedImageProtocol;
|
// pub const LoadedImageProtocol = protocol.LoadedImageProtocol;
|
||||||
|
// pub const SimpleFilesystemProtocol = protocol.SimpleFileSystemProtocol;
|
||||||
pub const Handle = uefi.Handle;
|
pub const Handle = uefi.Handle;
|
||||||
pub const MemoryDescriptor = uefi.tables.MemoryDescriptor;
|
pub const MemoryDescriptor = uefi.tables.MemoryDescriptor;
|
||||||
pub const SimpleFilesystemProtocol = uefi.protocols.SimpleFileSystemProtocol;
|
pub const protocol = uefi.protocol;
|
||||||
pub const Status = uefi.Status;
|
pub const Status = uefi.Status;
|
||||||
pub const SystemTable = uefi.tables.SystemTable;
|
pub const SystemTable = uefi.tables.SystemTable;
|
||||||
pub const Try = Status.err;
|
pub const Try = Status.err;
|
||||||
@ -25,9 +26,6 @@ pub const page_size = 0x1000;
|
|||||||
pub const page_shifter = lib.arch.page_shifter(page_size);
|
pub const page_shifter = lib.arch.page_shifter(page_size);
|
||||||
|
|
||||||
const privileged = @import("privileged");
|
const privileged = @import("privileged");
|
||||||
const PhysicalAddress = privileged.PhysicalAddress;
|
|
||||||
const VirtualAddress = privileged.VirtualAddress;
|
|
||||||
const VirtualMemoryRegion = privileged.VirtualMemoryRegion;
|
|
||||||
const stopCPU = privileged.arch.stopCPU;
|
const stopCPU = privileged.arch.stopCPU;
|
||||||
|
|
||||||
pub fn panic(comptime format: []const u8, arguments: anytype) noreturn {
|
pub fn panic(comptime format: []const u8, arguments: anytype) noreturn {
|
||||||
|
449
src/common.zig
449
src/common.zig
@ -1,254 +1,25 @@
|
|||||||
const compiler_builtin = @import("builtin");
|
// This file is meant to be shared between all parts of the project, including build.zig
|
||||||
pub const cpu = compiler_builtin.cpu;
|
const std = @import("std");
|
||||||
pub const os = compiler_builtin.os.tag;
|
const maxInt = std.math.maxInt;
|
||||||
pub const build_mode = compiler_builtin.mode;
|
const containsAtLeast = std.mem.containsAtLeast;
|
||||||
pub const is_test = compiler_builtin.is_test;
|
const Target = std.Target;
|
||||||
|
const Cpu = Target.Cpu;
|
||||||
|
const OptimizeMode = std.builtin.OptimizeMode;
|
||||||
|
|
||||||
pub const kb = 1024;
|
const Allocator = std.mem.Allocator;
|
||||||
pub const mb = kb * 1024;
|
|
||||||
pub const gb = mb * 1024;
|
|
||||||
pub const tb = gb * 1024;
|
|
||||||
|
|
||||||
pub const SizeUnit = enum(u64) {
|
const builtin = @import("builtin");
|
||||||
byte = 1,
|
const cpu = builtin.cpu;
|
||||||
kilobyte = 1024,
|
const os = builtin.os.tag;
|
||||||
megabyte = 1024 * 1024,
|
|
||||||
gigabyte = 1024 * 1024 * 1024,
|
|
||||||
terabyte = 1024 * 1024 * 1024 * 1024,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const std = @import("std");
|
pub const Configuration = struct {
|
||||||
pub const Target = std.Target;
|
architecture: Cpu.Arch,
|
||||||
pub const Cpu = Target.Cpu;
|
bootloader: Bootloader,
|
||||||
pub const CrossTarget = std.zig.CrossTarget;
|
boot_protocol: Bootloader.Protocol,
|
||||||
|
execution_environment: ExecutionEnvironment,
|
||||||
pub const log = std.log;
|
optimize_mode: OptimizeMode,
|
||||||
|
execution_type: ExecutionType,
|
||||||
pub const Atomic = std.atomic.Atomic;
|
executable_kind: std.Build.CompileStep.Kind,
|
||||||
|
|
||||||
pub const Reader = std.io.Reader;
|
|
||||||
pub const Writer = std.io.Writer;
|
|
||||||
|
|
||||||
pub const FixedBufferStream = std.io.FixedBufferStream;
|
|
||||||
pub const fixedBufferStream = std.io.fixedBufferStream;
|
|
||||||
|
|
||||||
pub fn assert(ok: bool) void {
|
|
||||||
if (!ok) {
|
|
||||||
if (@inComptime()) {
|
|
||||||
@compileError("Assert failed!");
|
|
||||||
} else {
|
|
||||||
@panic("Assert failed!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const deflate = std.compress.deflate;
|
|
||||||
|
|
||||||
const debug = std.debug;
|
|
||||||
pub const print = debug.print;
|
|
||||||
pub const StackIterator = debug.StackIterator;
|
|
||||||
pub const dwarf = std.dwarf;
|
|
||||||
pub const ModuleDebugInfo = std.debug.ModuleDebugInfo;
|
|
||||||
|
|
||||||
pub const elf = std.elf;
|
|
||||||
|
|
||||||
const fmt = std.fmt;
|
|
||||||
pub const format = std.fmt.format;
|
|
||||||
pub const FormatOptions = fmt.FormatOptions;
|
|
||||||
pub const bufPrint = fmt.bufPrint;
|
|
||||||
pub const allocPrint = fmt.allocPrint;
|
|
||||||
pub const comptimePrint = fmt.comptimePrint;
|
|
||||||
pub const parseUnsigned = fmt.parseUnsigned;
|
|
||||||
|
|
||||||
const heap = std.heap;
|
|
||||||
pub const FixedBufferAllocator = heap.FixedBufferAllocator;
|
|
||||||
|
|
||||||
pub const json = std.json;
|
|
||||||
|
|
||||||
const mem = std.mem;
|
|
||||||
pub const ZigAllocator = mem.Allocator;
|
|
||||||
pub const equal = mem.eql;
|
|
||||||
pub const length = mem.len;
|
|
||||||
pub const startsWith = mem.startsWith;
|
|
||||||
pub const endsWith = mem.endsWith;
|
|
||||||
pub const indexOf = mem.indexOf;
|
|
||||||
// Ideal for small inputs
|
|
||||||
pub const indexOfPosLinear = mem.indexOfPosLinear;
|
|
||||||
pub const lastIndexOf = mem.lastIndexOf;
|
|
||||||
pub const asBytes = mem.asBytes;
|
|
||||||
pub const readIntBig = mem.readIntBig;
|
|
||||||
pub const readIntSliceBig = mem.readIntSliceBig;
|
|
||||||
pub const concat = mem.concat;
|
|
||||||
pub const sliceAsBytes = mem.sliceAsBytes;
|
|
||||||
pub const bytesAsSlice = mem.bytesAsSlice;
|
|
||||||
pub const alignForward = mem.alignForward;
|
|
||||||
pub const alignBackward = mem.alignBackward;
|
|
||||||
pub const isAligned = mem.isAligned;
|
|
||||||
pub const isAlignedGeneric = mem.isAlignedGeneric;
|
|
||||||
pub const reverse = mem.reverse;
|
|
||||||
pub const tokenize = mem.tokenize;
|
|
||||||
pub const containsAtLeast = mem.containsAtLeast;
|
|
||||||
pub const sliceTo = mem.sliceTo;
|
|
||||||
pub const swap = mem.swap;
|
|
||||||
|
|
||||||
pub const random = std.rand;
|
|
||||||
|
|
||||||
pub const testing = std.testing;
|
|
||||||
|
|
||||||
pub const sort = std.sort;
|
|
||||||
|
|
||||||
pub fn fieldSize(comptime T: type, field_name: []const u8) comptime_int {
|
|
||||||
var foo: T = undefined;
|
|
||||||
return @sizeOf(@TypeOf(@field(foo, field_name)));
|
|
||||||
}
|
|
||||||
|
|
||||||
const DiffError = error{
|
|
||||||
diff,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn diff(file1: []const u8, file2: []const u8) !void {
|
|
||||||
assert(file1.len == file2.len);
|
|
||||||
var different_bytes: u64 = 0;
|
|
||||||
for (file1, 0..) |byte1, index| {
|
|
||||||
const byte2 = file2[index];
|
|
||||||
const is_different_byte = byte1 != byte2;
|
|
||||||
different_bytes += @intFromBool(is_different_byte);
|
|
||||||
if (is_different_byte) {
|
|
||||||
log.debug("Byte [0x{x}] is different: 0x{x} != 0x{x}", .{ index, byte1, byte2 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (different_bytes != 0) {
|
|
||||||
log.debug("Total different bytes: 0x{x}", .{different_bytes});
|
|
||||||
return DiffError.diff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn zeroes(comptime T: type) T {
|
|
||||||
var result: T = undefined;
|
|
||||||
const slice = asBytes(&result);
|
|
||||||
@memset(slice, 0);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ascii = std.ascii;
|
|
||||||
pub const upperString = ascii.upperString;
|
|
||||||
pub const isUpper = ascii.isUpper;
|
|
||||||
pub const isAlphabetic = ascii.isAlphabetic;
|
|
||||||
|
|
||||||
const std_builtin = std.builtin;
|
|
||||||
pub const AtomicRmwOp = std_builtin.AtomicRmwOp;
|
|
||||||
pub const AtomicOrder = std_builtin.AtomicOrder;
|
|
||||||
pub const Type = std_builtin.Type;
|
|
||||||
pub const StackTrace = std_builtin.StackTrace;
|
|
||||||
pub const SourceLocation = std_builtin.SourceLocation;
|
|
||||||
|
|
||||||
pub fn FieldType(comptime T: type, comptime name: []const u8) type {
|
|
||||||
return @TypeOf(@field(@as(T, undefined), name));
|
|
||||||
}
|
|
||||||
|
|
||||||
// META PROGRAMMING
|
|
||||||
pub const AutoEnumArray = std.enums.EnumArray;
|
|
||||||
pub const fields = std.meta.fields;
|
|
||||||
pub const IntType = std.meta.Int;
|
|
||||||
pub const enumFromInt = std.meta.enumFromInt;
|
|
||||||
pub const stringToEnum = std.meta.stringToEnum;
|
|
||||||
pub const Tag = std.meta.Tag;
|
|
||||||
|
|
||||||
const math = std.math;
|
|
||||||
pub const maxInt = math.maxInt;
|
|
||||||
pub const min = math.min;
|
|
||||||
pub const divCeil = math.divCeil;
|
|
||||||
pub const clamp = math.clamp;
|
|
||||||
pub const isPowerOfTwo = math.isPowerOfTwo;
|
|
||||||
pub const mul = math.mul;
|
|
||||||
pub const cast = math.cast;
|
|
||||||
|
|
||||||
pub const unicode = std.unicode;
|
|
||||||
|
|
||||||
pub const uefi = std.os.uefi;
|
|
||||||
|
|
||||||
pub const DiskType = enum(u32) {
|
|
||||||
virtio = 0,
|
|
||||||
nvme = 1,
|
|
||||||
ahci = 2,
|
|
||||||
ide = 3,
|
|
||||||
memory = 4,
|
|
||||||
bios = 5,
|
|
||||||
|
|
||||||
pub const count = enumCount(@This());
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const FilesystemType = enum(u32) {
|
|
||||||
birth = 0,
|
|
||||||
ext2 = 1,
|
|
||||||
fat32 = 2,
|
|
||||||
|
|
||||||
pub const count = enumCount(@This());
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn enumFields(comptime E: type) []const Type.EnumField {
|
|
||||||
return @typeInfo(E).Enum.fields;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const enumValues = std.enums.values;
|
|
||||||
|
|
||||||
pub fn enumCount(comptime E: type) usize {
|
|
||||||
return enumFields(E).len;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const PartitionTableType = enum {
|
|
||||||
mbr,
|
|
||||||
gpt,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const supported_architectures = [_]Cpu.Arch{
|
|
||||||
.x86_64,
|
|
||||||
//.aarch64,
|
|
||||||
//.riscv64,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn architectureIndex(comptime arch: Cpu.Arch) comptime_int {
|
|
||||||
inline for (supported_architectures, 0..) |architecture, index| {
|
|
||||||
if (arch == architecture) return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
@compileError("Architecture not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const architecture_bootloader_map = blk: {
|
|
||||||
var array: [supported_architectures.len][]const ArchitectureBootloader = undefined;
|
|
||||||
|
|
||||||
array[architectureIndex(.x86_64)] = &.{
|
|
||||||
.{
|
|
||||||
.id = .birth,
|
|
||||||
.protocols = &.{ .bios, .uefi },
|
|
||||||
},
|
|
||||||
.{
|
|
||||||
.id = .limine,
|
|
||||||
.protocols = &.{ .bios, .uefi },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// array[architectureIndex(.aarch64)] = &.{
|
|
||||||
// .{
|
|
||||||
// .id = .birth,
|
|
||||||
// .protocols = &.{.uefi},
|
|
||||||
// },
|
|
||||||
// .{
|
|
||||||
// .id = .limine,
|
|
||||||
// .protocols = &.{.uefi},
|
|
||||||
// },
|
|
||||||
// };
|
|
||||||
|
|
||||||
// array[architectureIndex(.riscv64)] = &.{
|
|
||||||
// .{
|
|
||||||
// .id = .birth,
|
|
||||||
// .protocols = &.{.uefi},
|
|
||||||
// },
|
|
||||||
// };
|
|
||||||
|
|
||||||
break :blk array;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Bootloader = enum(u32) {
|
pub const Bootloader = enum(u32) {
|
||||||
@ -261,147 +32,18 @@ pub const Bootloader = enum(u32) {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const ArchitectureBootloader = struct {
|
|
||||||
id: Bootloader,
|
|
||||||
protocols: []const Bootloader.Protocol,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const TraditionalExecutionMode = enum(u1) {
|
|
||||||
privileged = 0,
|
|
||||||
user = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const ExecutionEnvironment = enum {
|
pub const ExecutionEnvironment = enum {
|
||||||
qemu,
|
qemu,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const ImageConfig = struct {
|
|
||||||
sector_count: u64,
|
|
||||||
sector_size: u16,
|
|
||||||
partition_table: PartitionTableType,
|
|
||||||
partition: PartitionConfig,
|
|
||||||
|
|
||||||
pub const default_path = "config/image_config.json";
|
|
||||||
|
|
||||||
pub fn get(allocator: ZigAllocator, path: []const u8) !ImageConfig {
|
|
||||||
const image_config_file = try std.fs.cwd().readFileAlloc(allocator, path, maxInt(usize));
|
|
||||||
const parsed_image_configuration = try std.json.parseFromSlice(ImageConfig, allocator, image_config_file, .{});
|
|
||||||
return parsed_image_configuration.value;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const PartitionConfig = struct {
|
|
||||||
name: []const u8,
|
|
||||||
filesystem: FilesystemType,
|
|
||||||
first_lba: u64,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const QEMU = extern struct {
|
|
||||||
pub const isa_debug_exit = ISADebugExit{};
|
|
||||||
|
|
||||||
pub const ISADebugExit = extern struct {
|
|
||||||
io_base: u8 = 0xf4,
|
|
||||||
io_size: u8 = @sizeOf(u32),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const ExitCode = enum(u32) {
|
|
||||||
success = 0x10,
|
|
||||||
failure = 0x11,
|
|
||||||
_,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const OptimizeMode = std.builtin.OptimizeMode;
|
|
||||||
|
|
||||||
pub const Configuration = struct {
|
|
||||||
architecture: Cpu.Arch,
|
|
||||||
bootloader: Bootloader,
|
|
||||||
boot_protocol: Bootloader.Protocol,
|
|
||||||
execution_environment: ExecutionEnvironment,
|
|
||||||
optimize_mode: OptimizeMode,
|
|
||||||
execution_type: ExecutionType,
|
|
||||||
executable_kind: std.Build.CompileStep.Kind,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const QEMUOptions = packed struct {
|
|
||||||
is_test: bool,
|
|
||||||
is_debug: bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const ExecutionType = enum {
|
pub const ExecutionType = enum {
|
||||||
emulated,
|
emulated,
|
||||||
accelerated,
|
accelerated,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Suffix = enum {
|
pub const TraditionalExecutionMode = enum(u1) {
|
||||||
bootloader,
|
privileged = 0,
|
||||||
cpu_driver,
|
user = 1,
|
||||||
image,
|
|
||||||
complete,
|
|
||||||
|
|
||||||
pub fn fromConfiguration(suffix: Suffix, allocator: ZigAllocator, configuration: Configuration, prefix: ?[]const u8) ![]const u8 {
|
|
||||||
const cpu_driver_suffix = [_][]const u8{
|
|
||||||
@tagName(configuration.optimize_mode),
|
|
||||||
"_",
|
|
||||||
@tagName(configuration.architecture),
|
|
||||||
"_",
|
|
||||||
@tagName(configuration.executable_kind),
|
|
||||||
};
|
|
||||||
|
|
||||||
const bootloader_suffix = [_][]const u8{
|
|
||||||
@tagName(configuration.architecture),
|
|
||||||
"_",
|
|
||||||
@tagName(configuration.bootloader),
|
|
||||||
"_",
|
|
||||||
@tagName(configuration.boot_protocol),
|
|
||||||
};
|
|
||||||
|
|
||||||
const image_suffix = [_][]const u8{
|
|
||||||
@tagName(configuration.optimize_mode),
|
|
||||||
"_",
|
|
||||||
} ++ bootloader_suffix ++ [_][]const u8{
|
|
||||||
"_",
|
|
||||||
@tagName(configuration.executable_kind),
|
|
||||||
};
|
|
||||||
|
|
||||||
const complete_suffix = image_suffix ++ [_][]const u8{
|
|
||||||
"_",
|
|
||||||
@tagName(configuration.execution_type),
|
|
||||||
"_",
|
|
||||||
@tagName(configuration.execution_environment),
|
|
||||||
};
|
|
||||||
|
|
||||||
return try std.mem.concat(allocator, u8, &switch (suffix) {
|
|
||||||
.cpu_driver => if (prefix) |pf| [1][]const u8{pf} ++ cpu_driver_suffix else cpu_driver_suffix,
|
|
||||||
.bootloader => if (prefix) |pf| [1][]const u8{pf} ++ bootloader_suffix else bootloader_suffix,
|
|
||||||
.image => if (prefix) |pf| [1][]const u8{pf} ++ image_suffix else image_suffix,
|
|
||||||
.complete => if (prefix) |pf| [1][]const u8{pf} ++ complete_suffix else complete_suffix,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Module = struct {
|
|
||||||
program: UserProgram,
|
|
||||||
name: []const u8,
|
|
||||||
};
|
|
||||||
pub const UserProgram = struct {
|
|
||||||
kind: Kind,
|
|
||||||
dependencies: []const Dependency,
|
|
||||||
|
|
||||||
pub const Kind = enum {
|
|
||||||
zig_exe,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Dependency = struct {
|
|
||||||
foo: u64 = 0,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const BirthProgram = enum {
|
|
||||||
bootloader,
|
|
||||||
cpu,
|
|
||||||
user,
|
|
||||||
host,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn canVirtualizeWithQEMU(architecture: Cpu.Arch, ci: bool) bool {
|
pub fn canVirtualizeWithQEMU(architecture: Cpu.Arch, ci: bool) bool {
|
||||||
@ -418,9 +60,6 @@ pub fn canVirtualizeWithQEMU(architecture: Cpu.Arch, ci: bool) bool {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const default_cpu_name = "/cpu";
|
|
||||||
pub const default_init_file = "/init";
|
|
||||||
|
|
||||||
pub const ArgumentParser = struct {
|
pub const ArgumentParser = struct {
|
||||||
pub const null_specifier = "-";
|
pub const null_specifier = "-";
|
||||||
|
|
||||||
@ -501,5 +140,45 @@ pub const ArgumentParser = struct {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const default_disk_size = 64 * 1024 * 1024;
|
fn enumCount(comptime E: type) comptime_int {
|
||||||
pub const default_sector_size = 0x200;
|
return @typeInfo(E).Enum.fields.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const QEMUOptions = packed struct {
|
||||||
|
is_test: bool,
|
||||||
|
is_debug: bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const PartitionTableType = enum {
|
||||||
|
mbr,
|
||||||
|
gpt,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const ImageConfig = struct {
|
||||||
|
sector_count: u64,
|
||||||
|
sector_size: u16,
|
||||||
|
partition_table: PartitionTableType,
|
||||||
|
partition: PartitionConfig,
|
||||||
|
|
||||||
|
pub const default_path = "config/image_config.json";
|
||||||
|
|
||||||
|
pub fn get(allocator: Allocator, path: []const u8) !ImageConfig {
|
||||||
|
const image_config_file = try std.fs.cwd().readFileAlloc(allocator, path, maxInt(usize));
|
||||||
|
const parsed_image_configuration = try std.json.parseFromSlice(ImageConfig, allocator, image_config_file, .{});
|
||||||
|
return parsed_image_configuration.value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const PartitionConfig = struct {
|
||||||
|
name: []const u8,
|
||||||
|
filesystem: FilesystemType,
|
||||||
|
first_lba: u64,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const FilesystemType = enum(u32) {
|
||||||
|
birth = 0,
|
||||||
|
ext2 = 1,
|
||||||
|
fat32 = 2,
|
||||||
|
|
||||||
|
pub const count = enumCount(@This());
|
||||||
|
};
|
||||||
|
716
src/cpu.zig
716
src/cpu.zig
@ -13,64 +13,44 @@ const PhysicalAddress = lib.PhysicalAddress;
|
|||||||
const PhysicalAddressSpace = lib.PhysicalAddressSpace;
|
const PhysicalAddressSpace = lib.PhysicalAddressSpace;
|
||||||
const PhysicalMemoryRegion = lib.PhysicalMemoryRegion;
|
const PhysicalMemoryRegion = lib.PhysicalMemoryRegion;
|
||||||
const stopCPU = privileged.arch.stopCPU;
|
const stopCPU = privileged.arch.stopCPU;
|
||||||
const VirtualAddress = privileged.VirtualAddress;
|
const VirtualAddress = lib.VirtualAddress;
|
||||||
const VirtualMemoryRegion = privileged.VirtualMemoryRegion;
|
const VirtualMemoryRegion = lib.VirtualMemoryRegion;
|
||||||
|
const paging = privileged.arch.paging;
|
||||||
|
|
||||||
const birth = @import("birth");
|
const birth = @import("birth");
|
||||||
|
|
||||||
pub const test_runner = @import("cpu/test_runner.zig");
|
|
||||||
pub const arch = @import("cpu/arch.zig");
|
pub const arch = @import("cpu/arch.zig");
|
||||||
pub const capabilities = @import("cpu/capabilities.zig");
|
pub const interface = @import("cpu/interface.zig");
|
||||||
|
pub const init = @import("cpu/init.zig");
|
||||||
|
|
||||||
|
const PageTableRegions = arch.init.PageTableRegions;
|
||||||
|
|
||||||
pub export var stack: [0x8000]u8 align(0x1000) = undefined;
|
pub export var stack: [0x8000]u8 align(0x1000) = undefined;
|
||||||
pub export var page_allocator = PageAllocator{
|
|
||||||
.head = null,
|
|
||||||
.list_allocator = .{
|
|
||||||
.u = .{
|
|
||||||
.primitive = .{
|
|
||||||
.backing_4k_page = undefined,
|
|
||||||
.allocated = 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
.primitive = true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub var bundle: []const u8 = &.{};
|
pub var bundle: []const u8 = &.{};
|
||||||
pub var bundle_files: []const u8 = &.{};
|
pub var bundle_files: []const u8 = &.{};
|
||||||
|
|
||||||
|
pub export var page_allocator = PageAllocator{};
|
||||||
pub export var user_scheduler: *UserScheduler = undefined;
|
pub export var user_scheduler: *UserScheduler = undefined;
|
||||||
pub export var driver: *align(lib.arch.valid_page_sizes[0]) Driver = undefined;
|
pub export var heap = HeapImplementation(false){};
|
||||||
|
pub var debug_info: lib.ModuleDebugInfo = undefined;
|
||||||
pub export var page_tables: CPUPageTables = undefined;
|
pub export var page_tables: CPUPageTables = undefined;
|
||||||
pub var file: []align(lib.default_sector_size) const u8 = undefined;
|
pub var file: []align(lib.default_sector_size) const u8 = undefined;
|
||||||
pub export var core_id: u32 = 0;
|
pub export var core_id: u32 = 0;
|
||||||
pub export var bsp = false;
|
pub export var bsp = false;
|
||||||
var panic_lock = lib.Spinlock.released;
|
var panic_lock = lib.Spinlock.released;
|
||||||
|
|
||||||
/// This data structure holds the information needed to run a core
|
|
||||||
pub const Driver = extern struct {
|
|
||||||
init_root_capability: capabilities.RootDescriptor,
|
|
||||||
valid: bool,
|
|
||||||
padding: [padding_byte_count]u8 = .{0} ** padding_byte_count,
|
|
||||||
const padding_byte_count = lib.arch.valid_page_sizes[0] - @sizeOf(bool) - @sizeOf(capabilities.RootDescriptor);
|
|
||||||
|
|
||||||
pub inline fn getRootCapability(drv: *Driver) *capabilities.Root {
|
|
||||||
return drv.init_root_capability.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
comptime {
|
|
||||||
// @compileLog(@sizeOf(Driver));
|
|
||||||
assert(lib.isAligned(@sizeOf(Driver), lib.arch.valid_page_sizes[0]));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// This data structure holds the information needed to run a program in a core (cpu side)
|
/// This data structure holds the information needed to run a program in a core (cpu side)
|
||||||
pub const UserScheduler = extern struct {
|
pub const UserScheduler = extern struct {
|
||||||
capability_root_node: capabilities.Root,
|
s: S,
|
||||||
common: *birth.UserScheduler,
|
|
||||||
padding: [padding_byte_count]u8 = .{0} ** padding_byte_count,
|
padding: [padding_byte_count]u8 = .{0} ** padding_byte_count,
|
||||||
|
|
||||||
const total_size = @sizeOf(capabilities.Root) + @sizeOf(*birth.UserScheduler);
|
const S = extern struct {
|
||||||
|
capability_root_node: interface.Root,
|
||||||
|
common: *birth.Scheduler.Common,
|
||||||
|
};
|
||||||
|
|
||||||
|
const total_size = @sizeOf(S);
|
||||||
const aligned_size = lib.alignForward(usize, total_size, lib.arch.valid_page_sizes[0]);
|
const aligned_size = lib.alignForward(usize, total_size, lib.arch.valid_page_sizes[0]);
|
||||||
const padding_byte_count = aligned_size - total_size;
|
const padding_byte_count = aligned_size - total_size;
|
||||||
|
|
||||||
@ -81,7 +61,7 @@ pub const UserScheduler = extern struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const print_stack_trace = false;
|
const print_stack_trace = true;
|
||||||
var panic_count: usize = 0;
|
var panic_count: usize = 0;
|
||||||
|
|
||||||
inline fn panicPrologue(comptime format: []const u8, arguments: anytype) !void {
|
inline fn panicPrologue(comptime format: []const u8, arguments: anytype) !void {
|
||||||
@ -92,9 +72,9 @@ inline fn panicPrologue(comptime format: []const u8, arguments: anytype) !void {
|
|||||||
try writer.writeAll(lib.Color.get(.bold));
|
try writer.writeAll(lib.Color.get(.bold));
|
||||||
try writer.writeAll(lib.Color.get(.red));
|
try writer.writeAll(lib.Color.get(.red));
|
||||||
try writer.writeAll("[CPU DRIVER] [PANIC] ");
|
try writer.writeAll("[CPU DRIVER] [PANIC] ");
|
||||||
try writer.writeAll(lib.Color.get(.reset));
|
|
||||||
try writer.print(format, arguments);
|
try writer.print(format, arguments);
|
||||||
try writer.writeByte('\n');
|
try writer.writeByte('\n');
|
||||||
|
try writer.writeAll(lib.Color.get(.reset));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fn panicEpilogue() noreturn {
|
inline fn panicEpilogue() noreturn {
|
||||||
@ -103,64 +83,68 @@ inline fn panicEpilogue() noreturn {
|
|||||||
shutdown(.failure);
|
shutdown(.failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
// inline fn printStackTrace(maybe_stack_trace: ?*lib.StackTrace) !void {
|
inline fn printStackTrace(maybe_stack_trace: ?*lib.StackTrace) !void {
|
||||||
// if (maybe_stack_trace) |stack_trace| {
|
if (maybe_stack_trace) |stack_trace| {
|
||||||
// var debug_info = try getDebugInformation();
|
try writer.writeAll("Stack trace:\n");
|
||||||
// try writer.writeAll("Stack trace:\n");
|
var frame_index: usize = 0;
|
||||||
// var frame_index: usize = 0;
|
var frames_left: usize = @min(stack_trace.index, stack_trace.instruction_addresses.len);
|
||||||
// var frames_left: usize = @min(stack_trace.index, stack_trace.instruction_addresses.len);
|
|
||||||
//
|
|
||||||
// while (frames_left != 0) : ({
|
|
||||||
// frames_left -= 1;
|
|
||||||
// frame_index = (frame_index + 1) % stack_trace.instruction_addresses.len;
|
|
||||||
// }) {
|
|
||||||
// const return_address = stack_trace.instruction_addresses[frame_index];
|
|
||||||
// try writer.print("[{}] ", .{frame_index});
|
|
||||||
// try printSourceAtAddress(&debug_info, return_address);
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// try writer.writeAll("Stack trace not available\n");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// inline fn printStackTraceFromStackIterator(return_address: usize, frame_address: usize) !void {
|
while (frames_left != 0) : ({
|
||||||
// var debug_info = try getDebugInformation();
|
frames_left -= 1;
|
||||||
// var stack_iterator = lib.StackIterator.init(return_address, frame_address);
|
frame_index = (frame_index + 1) % stack_trace.instruction_addresses.len;
|
||||||
// var frame_index: usize = 0;
|
}) {
|
||||||
// try writer.writeAll("Stack trace:\n");
|
const return_address = stack_trace.instruction_addresses[frame_index];
|
||||||
//
|
try writer.print("[{}] ", .{frame_index});
|
||||||
// try printSourceAtAddress(&debug_info, return_address);
|
try printSourceAtAddress(return_address);
|
||||||
// while (stack_iterator.next()) |address| : (frame_index += 1) {
|
try writer.writeByte('\n');
|
||||||
// try writer.print("[{}] ", .{frame_index});
|
}
|
||||||
// try printSourceAtAddress(&debug_info, address);
|
} else {
|
||||||
// }
|
try writer.writeAll("Stack trace not available\n");
|
||||||
// }
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// fn printSourceAtAddress(debug_info: *lib.ModuleDebugInfo, address: usize) !void {
|
inline fn printStackTraceFromStackIterator(return_address: usize, frame_address: usize) !void {
|
||||||
// if (debug_info.findCompileUnit(address)) |compile_unit| {
|
var stack_iterator = lib.StackIterator.init(return_address, frame_address);
|
||||||
// const symbol = .{
|
var frame_index: usize = 0;
|
||||||
// .symbol_name = debug_info.getSymbolName(address) orelse "???",
|
try writer.writeAll("Stack trace:\n");
|
||||||
// .compile_unit_name = compile_unit.die.getAttrString(debug_info, lib.dwarf.AT.name, debug_info.debug_str, compile_unit.*) catch "???",
|
|
||||||
// .line_info = debug_info.getLineNumberInfo(heap_allocator.toZig(), compile_unit.*, address) catch null,
|
while (stack_iterator.next()) |address| : (frame_index += 1) {
|
||||||
// };
|
if (address == 0) break;
|
||||||
// try writer.print("0x{x}: {s}!{s} {s}:{}:{}\n", .{ address, symbol.symbol_name, symbol.compile_unit_name, symbol.line_info.?.file_name, symbol.line_info.?.line, symbol.line_info.?.column });
|
try writer.print("[{}] ", .{frame_index});
|
||||||
// } else |err| {
|
try printSourceAtAddress(address);
|
||||||
// return err;
|
try writer.writeByte('\n');
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
fn printSourceAtAddress(address: usize) !void {
|
||||||
|
const compile_unit = debug_info.findCompileUnit(address) catch {
|
||||||
|
try writer.print("0x{x}: ???", .{address});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
const symbol_name = debug_info.getSymbolName(address) orelse "???";
|
||||||
|
const compile_unit_name = compile_unit.die.getAttrString(&debug_info, lib.dwarf.AT.name, debug_info.section(.debug_str), compile_unit.*) catch "???";
|
||||||
|
const line_info = debug_info.getLineNumberInfo(heap.allocator.zigUnwrap(), compile_unit.*, address) catch null;
|
||||||
|
const symbol = .{
|
||||||
|
.symbol_name = symbol_name,
|
||||||
|
.compile_unit_name = compile_unit_name,
|
||||||
|
.line_info = line_info,
|
||||||
|
};
|
||||||
|
|
||||||
|
const file_name = if (symbol.line_info) |li| li.file_name else "???";
|
||||||
|
const line = if (symbol.line_info) |li| li.line else 0;
|
||||||
|
const column = if (symbol.line_info) |li| li.column else 0;
|
||||||
|
try writer.print("0x{x}: {s}!{s} {s}:{}:{}", .{ address, symbol.symbol_name, symbol.compile_unit_name, file_name, line, column });
|
||||||
|
}
|
||||||
|
|
||||||
pub fn panicWithStackTrace(stack_trace: ?*lib.StackTrace, comptime format: []const u8, arguments: anytype) noreturn {
|
pub fn panicWithStackTrace(stack_trace: ?*lib.StackTrace, comptime format: []const u8, arguments: anytype) noreturn {
|
||||||
_ = stack_trace;
|
|
||||||
panicPrologue(format, arguments) catch {};
|
panicPrologue(format, arguments) catch {};
|
||||||
// if (print_stack_trace) printStackTrace(stack_trace) catch {};
|
if (print_stack_trace) printStackTrace(stack_trace) catch {};
|
||||||
panicEpilogue();
|
panicEpilogue();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn panicFromInstructionPointerAndFramePointer(return_address: usize, frame_address: usize, comptime format: []const u8, arguments: anytype) noreturn {
|
pub fn panicFromInstructionPointerAndFramePointer(return_address: usize, frame_address: usize, comptime format: []const u8, arguments: anytype) noreturn {
|
||||||
_ = frame_address;
|
|
||||||
_ = return_address;
|
|
||||||
panicPrologue(format, arguments) catch {};
|
panicPrologue(format, arguments) catch {};
|
||||||
//if (print_stack_trace) printStackTraceFromStackIterator(return_address, frame_address) catch {};
|
if (print_stack_trace) printStackTraceFromStackIterator(return_address, frame_address) catch {};
|
||||||
panicEpilogue();
|
panicEpilogue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,268 +152,410 @@ pub fn panic(comptime format: []const u8, arguments: anytype) noreturn {
|
|||||||
@call(.always_inline, panicFromInstructionPointerAndFramePointer, .{ @returnAddress(), @frameAddress(), format, arguments });
|
@call(.always_inline, panicFromInstructionPointerAndFramePointer, .{ @returnAddress(), @frameAddress(), format, arguments });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub var syscall_count: usize = 0;
|
pub var command_count: usize = 0;
|
||||||
|
|
||||||
pub inline fn shutdown(exit_code: lib.QEMU.ExitCode) noreturn {
|
pub inline fn shutdown(exit_code: lib.QEMU.ExitCode) noreturn {
|
||||||
log.debug("Printing stats...", .{});
|
log.debug("Printing stats...", .{});
|
||||||
log.debug("Syscall count: {}", .{syscall_count});
|
log.debug("System call count: {}", .{interface.system_call_count});
|
||||||
|
|
||||||
privileged.shutdown(exit_code);
|
privileged.shutdown(exit_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const PageAllocator = extern struct {
|
pub const RegionList = extern struct {
|
||||||
head: ?*Entry,
|
regions: [list_region_count]PhysicalMemoryRegion = .{PhysicalMemoryRegion.invalid()} ** list_region_count,
|
||||||
list_allocator: ListAllocator,
|
metadata: Metadata = .{},
|
||||||
total_allocated_size: u32 = 0,
|
|
||||||
|
|
||||||
fn getPageAllocatorInterface(pa: *PageAllocator) PageAllocatorInterface {
|
pub const Metadata = extern struct {
|
||||||
return .{
|
reserved: usize = 0,
|
||||||
.allocate = callbackAllocate,
|
bitset: Bitset = .{},
|
||||||
.context = pa,
|
previous: ?*RegionList = null,
|
||||||
.context_type = .cpu,
|
next: ?*RegionList = null,
|
||||||
};
|
|
||||||
|
const Bitset = lib.data_structures.BitsetU64(list_region_count);
|
||||||
|
|
||||||
|
comptime {
|
||||||
|
assert(@sizeOf(Metadata) == expected_size);
|
||||||
|
assert(@bitSizeOf(usize) - list_region_count < 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
const expected_size = 4 * @sizeOf(usize);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Error = error{
|
||||||
|
OutOfMemory,
|
||||||
|
no_space,
|
||||||
|
misalignment_page_size,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn allocateAligned(list: *RegionList, size: usize, alignment: usize) Error!PhysicalMemoryRegion {
|
||||||
|
assert(alignment % lib.arch.valid_page_sizes[0] == 0);
|
||||||
|
|
||||||
|
for (&list.regions, 0..) |*region, _index| {
|
||||||
|
const index: u6 = @intCast(_index);
|
||||||
|
assert(lib.isAligned(region.size, lib.arch.valid_page_sizes[0]));
|
||||||
|
assert(lib.isAligned(region.address.value(), lib.arch.valid_page_sizes[0]));
|
||||||
|
|
||||||
|
if (list.metadata.bitset.isSet(index)) {
|
||||||
|
if (lib.isAligned(region.address.value(), alignment)) {
|
||||||
|
if (region.size >= size) {
|
||||||
|
const result = region.takeSlice(size) catch unreachable;
|
||||||
|
if (region.size == 0) {
|
||||||
|
list.remove(@intCast(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn callbackAllocate(context: ?*anyopaque, size: u64, alignment: u64, options: PageAllocatorInterface.AllocateOptions) Allocator.Allocate.Error!PhysicalMemoryRegion {
|
|
||||||
_ = options;
|
|
||||||
const pa = @as(?*PageAllocator, @ptrCast(@alignCast(context))) orelse return Allocator.Allocate.Error.OutOfMemory;
|
|
||||||
const result = try pa.allocate(size, alignment);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn allocate(pa: *PageAllocator, size: u64, alignment: u64) Allocator.Allocate.Error!PhysicalMemoryRegion {
|
|
||||||
if (pa.head == null) {
|
|
||||||
@panic("head null");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const allocation = blk: {
|
|
||||||
var ptr = pa.head;
|
|
||||||
while (ptr) |entry| : (ptr = entry.next) {
|
|
||||||
if (lib.isAligned(entry.region.address.value(), alignment) and entry.region.size > size) {
|
|
||||||
const result = PhysicalMemoryRegion{
|
|
||||||
.address = entry.region.address,
|
|
||||||
.size = size,
|
|
||||||
};
|
|
||||||
entry.region.address = entry.region.address.offset(size);
|
|
||||||
entry.region.size -= size;
|
|
||||||
|
|
||||||
pa.total_allocated_size += @as(u32, @intCast(size));
|
|
||||||
// log.debug("Allocated 0x{x}", .{size});
|
|
||||||
|
|
||||||
break :blk result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr = pa.head;
|
return Error.OutOfMemory;
|
||||||
|
}
|
||||||
|
|
||||||
while (ptr) |entry| : (ptr = entry.next) {
|
pub fn remove(list: *RegionList, index: u6) void {
|
||||||
const aligned_address = lib.alignForward(entry.region.address.value(), alignment);
|
list.metadata.bitset.clear(index);
|
||||||
const top = entry.region.top().value();
|
}
|
||||||
if (aligned_address < top and top - aligned_address > size) {
|
|
||||||
// log.debug("Found region which we should be splitting: (0x{x}, 0x{x})", .{ entry.region.address.value(), entry.region.size });
|
|
||||||
// log.debug("User asked for 0x{x} bytes with alignment 0x{x}", .{ size, alignment });
|
|
||||||
// Split the addresses to obtain the desired result
|
|
||||||
const first_region_size = aligned_address - entry.region.address.value();
|
|
||||||
const first_region_address = entry.region.address;
|
|
||||||
const first_region_next = entry.next;
|
|
||||||
|
|
||||||
const second_region_address = aligned_address + size;
|
pub const UnalignedAllocationResult = extern struct {
|
||||||
const second_region_size = top - aligned_address + size;
|
wasted: PhysicalMemoryRegion,
|
||||||
|
allocated: PhysicalMemoryRegion,
|
||||||
const result = PhysicalMemoryRegion{
|
|
||||||
.address = PhysicalAddress.new(aligned_address),
|
|
||||||
.size = size,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// log.debug("\nFirst region: (Address: 0x{x}. Size: 0x{x}).\nRegion in the middle (allocated): (Address: 0x{x}. Size: 0x{x}).\nSecond region: (Address: 0x{x}. Size: 0x{x})", .{ first_region_address, first_region_size, result.address.value(), result.size, second_region_address, second_region_size });
|
/// Slow path
|
||||||
|
pub fn allocateAlignedSplitting(list: *RegionList, size: usize, alignment: usize) !UnalignedAllocationResult {
|
||||||
|
for (&list.regions, 0..) |*region, _index| {
|
||||||
|
const index: u6 = @intCast(_index);
|
||||||
|
const aligned_region_address = lib.alignForward(usize, region.address.value(), alignment);
|
||||||
|
const wasted_space = aligned_region_address - region.address.value();
|
||||||
|
|
||||||
const new_entry = pa.list_allocator.get();
|
if (list.metadata.bitset.isSet(index)) {
|
||||||
entry.* = .{
|
const target_size = wasted_space + size;
|
||||||
.region = .{
|
if (region.size >= target_size) {
|
||||||
.address = first_region_address,
|
const wasted_region = try region.takeSlice(wasted_space);
|
||||||
.size = first_region_size,
|
const allocated_region = try region.takeSlice(size);
|
||||||
},
|
|
||||||
.next = new_entry,
|
if (region.size == 0) {
|
||||||
|
list.remove(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
return UnalignedAllocationResult{
|
||||||
|
.wasted = wasted_region,
|
||||||
|
.allocated = allocated_region,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
new_entry.* = .{
|
|
||||||
.region = .{
|
|
||||||
.address = PhysicalAddress.new(second_region_address),
|
|
||||||
.size = second_region_size,
|
|
||||||
},
|
|
||||||
.next = first_region_next,
|
|
||||||
};
|
|
||||||
// log.debug("First entry: (Address: 0x{x}. Size: 0x{x})", .{ entry.region.address.value(), entry.region.size });
|
|
||||||
// log.debug("Second entry: (Address: 0x{x}. Size: 0x{x})", .{ new_entry.region.address.value(), new_entry.region.size });
|
|
||||||
|
|
||||||
// pa.total_allocated_size += @intCast(u32, size);
|
|
||||||
// log.debug("Allocated 0x{x}", .{size});
|
|
||||||
|
|
||||||
break :blk result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.err("Allocate error. Size: 0x{x}. Alignment: 0x{x}. Total allocated size: 0x{x}", .{ size, alignment, pa.total_allocated_size });
|
log.err("allocateAlignedSplitting", .{});
|
||||||
return Allocator.Allocate.Error.OutOfMemory;
|
return error.OutOfMemory;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn allocate(list: *RegionList, size: usize) Error!PhysicalMemoryRegion {
|
||||||
|
return list.allocateAligned(size, lib.arch.valid_page_sizes[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn append(list: *RegionList, region: PhysicalMemoryRegion) Error!birth.interface.Memory {
|
||||||
|
var block_count: usize = 0;
|
||||||
|
while (true) : (block_count += 1) {
|
||||||
|
if (!list.metadata.bitset.isFull()) {
|
||||||
|
const region_index = list.metadata.bitset.allocate() catch continue;
|
||||||
|
const block_index = block_count;
|
||||||
|
|
||||||
|
list.regions[region_index] = region;
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.block = @intCast(block_index),
|
||||||
|
.region = region_index,
|
||||||
};
|
};
|
||||||
|
} else {
|
||||||
|
return Error.no_space;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//log.debug("Physical allocation: 0x{x}, 0x{x}", .{ allocation.address.value(), allocation.size });
|
const cache_line_count = 16;
|
||||||
|
const list_region_count = @divExact((cache_line_count * lib.cache_line_size) - Metadata.expected_size, @sizeOf(PhysicalMemoryRegion));
|
||||||
|
|
||||||
@memset(allocation.toHigherHalfVirtualAddress().access(u8), 0);
|
comptime {
|
||||||
|
assert(@sizeOf(RegionList) % lib.cache_line_size == 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const UseCase = extern struct {
|
||||||
|
reason: Reason,
|
||||||
|
const Reason = enum(u8) {
|
||||||
|
heap,
|
||||||
|
privileged,
|
||||||
|
wasted,
|
||||||
|
user_protected,
|
||||||
|
user,
|
||||||
|
bootloader,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: make this more cache friendly
|
||||||
|
const UsedRegionList = extern struct {
|
||||||
|
region: PhysicalMemoryRegion,
|
||||||
|
use_case: UseCase,
|
||||||
|
next: ?*UsedRegionList = null,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const PageAllocator = extern struct {
|
||||||
|
free_regions: ?*RegionList = null,
|
||||||
|
used_regions: ?*UsedRegionList = null,
|
||||||
|
used_region_buffer: ?*UsedRegionList = null,
|
||||||
|
free_byte_count: u64 = 0,
|
||||||
|
used_byte_count: u64 = 0,
|
||||||
|
|
||||||
|
pub fn allocate(allocator: *PageAllocator, size: usize, use_case: UseCase) lib.Allocator.Allocate.Error!PhysicalMemoryRegion {
|
||||||
|
const allocation = try allocator.allocateRaw(size);
|
||||||
|
try allocator.appendUsedRegion(allocation, use_case);
|
||||||
|
return allocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn allocateRaw(allocator: *PageAllocator, size: usize) !PhysicalMemoryRegion {
|
||||||
|
var iterator = allocator.free_regions;
|
||||||
|
while (iterator) |region_list| : (iterator = region_list.metadata.next) {
|
||||||
|
const allocation = region_list.allocate(size) catch continue;
|
||||||
|
allocator.free_byte_count -= size;
|
||||||
|
allocator.used_byte_count += size;
|
||||||
|
|
||||||
return allocation;
|
return allocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn fromBSP(bootloader_information: *bootloader.Information) InitializationError!PageAllocator {
|
log.err("allocateRaw: out of memory. Used: 0x{x}. Free: 0x{x}", .{ allocator.used_byte_count, allocator.free_byte_count });
|
||||||
const memory_map_entries = bootloader_information.getMemoryMapEntries();
|
return error.OutOfMemory;
|
||||||
const page_counters = bootloader_information.getPageCounters();
|
|
||||||
|
|
||||||
var total_size: usize = 0;
|
|
||||||
const page_shifter = lib.arch.page_shifter(lib.arch.valid_page_sizes[0]);
|
|
||||||
|
|
||||||
for (memory_map_entries, page_counters) |entry, page_counter| {
|
|
||||||
if (entry.type != .usable or !lib.isAligned(entry.region.size, lib.arch.valid_page_sizes[0]) or entry.region.address.value() < lib.mb) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
total_size += entry.region.size - (page_counter << page_shifter);
|
/// The only purpose this serves is to do the trick when switching cr3
|
||||||
|
pub fn allocateAligned(allocator: *PageAllocator, size: usize, alignment: usize, use_case: UseCase) lib.Allocator.Allocate.Error!PhysicalMemoryRegion {
|
||||||
|
var iterator = allocator.free_regions;
|
||||||
|
while (iterator) |region_list| : (iterator = region_list.metadata.next) {
|
||||||
|
const unaligned_allocation = region_list.allocateAlignedSplitting(size, alignment) catch continue;
|
||||||
|
// TODO: do something with the wasted space
|
||||||
|
const total_allocation_size = unaligned_allocation.wasted.size + unaligned_allocation.allocated.size;
|
||||||
|
log.err("ALLOCATED: 0x{x}. WASTED: 0x{x}. TOTAL: 0x{x}", .{ unaligned_allocation.allocated.size, unaligned_allocation.wasted.size, total_allocation_size });
|
||||||
|
|
||||||
|
try allocator.appendUsedRegion(unaligned_allocation.allocated, use_case);
|
||||||
|
try allocator.appendUsedRegion(unaligned_allocation.wasted, .{ .reason = .wasted });
|
||||||
|
|
||||||
|
allocator.free_byte_count -= total_allocation_size;
|
||||||
|
allocator.used_byte_count += total_allocation_size;
|
||||||
|
|
||||||
|
return unaligned_allocation.allocated;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cpu_count = bootloader_information.smp.cpu_count;
|
@panic("TODO: PageAllocator.allocateAligned");
|
||||||
const total_memory_to_take = total_size / cpu_count;
|
|
||||||
|
|
||||||
// Look for a 4K page to host the memory map
|
|
||||||
const backing_4k_page = for (memory_map_entries, page_counters) |entry, *page_counter| {
|
|
||||||
const occupied_size = page_counter.* << page_shifter;
|
|
||||||
const entry_size_left = entry.region.size - occupied_size;
|
|
||||||
if (entry_size_left != 0) {
|
|
||||||
if (entry.type != .usable or !lib.isAligned(entry.region.size, lib.arch.valid_page_sizes[0]) or entry.region.address.value() < lib.mb) continue;
|
|
||||||
|
|
||||||
assert(lib.isAligned(entry_size_left, lib.arch.valid_page_sizes[0]));
|
|
||||||
page_counter.* += 1;
|
|
||||||
break entry.region.address.offset(occupied_size);
|
|
||||||
}
|
|
||||||
} else return InitializationError.bootstrap_region_not_found;
|
|
||||||
|
|
||||||
var memory_taken: usize = 0;
|
|
||||||
var backing_4k_page_memory_allocated: usize = 0;
|
|
||||||
|
|
||||||
var last_entry: ?*Entry = null;
|
|
||||||
var first_entry: ?*Entry = null;
|
|
||||||
|
|
||||||
for (memory_map_entries, page_counters) |entry, *page_counter| {
|
|
||||||
if (entry.type != .usable or !lib.isAligned(entry.region.size, lib.arch.valid_page_sizes[0]) or entry.region.address.value() < lib.mb) continue;
|
|
||||||
|
|
||||||
const occupied_size = page_counter.* << page_shifter;
|
|
||||||
|
|
||||||
if (occupied_size < entry.region.size) {
|
|
||||||
const entry_size_left = entry.region.size - occupied_size;
|
|
||||||
|
|
||||||
var memory_taken_from_region: usize = 0;
|
|
||||||
while (memory_taken + memory_taken_from_region < total_memory_to_take) {
|
|
||||||
if (memory_taken_from_region == entry_size_left) break;
|
|
||||||
|
|
||||||
const size_to_take = @min(2 * lib.mb, entry_size_left);
|
|
||||||
memory_taken_from_region += size_to_take;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
memory_taken += memory_taken_from_region;
|
pub fn appendUsedRegion(allocator: *PageAllocator, physical_region: PhysicalMemoryRegion, use_case: UseCase) lib.Allocator.Allocate.Error!void {
|
||||||
|
const need_allocation = blk: {
|
||||||
|
var result: bool = true;
|
||||||
|
var iterator = allocator.used_region_buffer;
|
||||||
|
while (iterator) |it| : (iterator = it.next) {
|
||||||
|
result = it.region.size < @sizeOf(UsedRegionList);
|
||||||
|
if (!result) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
page_counter.* += @as(u32, @intCast(memory_taken_from_region >> page_shifter));
|
break :blk result;
|
||||||
const region_descriptor = .{
|
|
||||||
.address = entry.region.offset(occupied_size).address,
|
|
||||||
.size = memory_taken_from_region,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (backing_4k_page_memory_allocated >= lib.arch.valid_page_sizes[0]) return InitializationError.memory_exceeded;
|
if (need_allocation) {
|
||||||
const entry_address = backing_4k_page.offset(backing_4k_page_memory_allocated);
|
const allocation = try allocator.allocateRaw(lib.arch.valid_page_sizes[0]);
|
||||||
const new_entry = entry_address.toHigherHalfVirtualAddress().access(*Entry);
|
const new_buffer = allocation.address.toHigherHalfVirtualAddress().access(*UsedRegionList);
|
||||||
backing_4k_page_memory_allocated += @sizeOf(Entry);
|
new_buffer.* = .{
|
||||||
|
.region = allocation,
|
||||||
new_entry.* = .{
|
.use_case = undefined,
|
||||||
.region = .{
|
};
|
||||||
.address = region_descriptor.address,
|
_ = new_buffer.region.takeSlice(@sizeOf(UsedRegionList)) catch unreachable;
|
||||||
.size = region_descriptor.size,
|
const used_region_allocation = new_buffer.region.takeSlice(@sizeOf(UsedRegionList)) catch unreachable;
|
||||||
},
|
const new_used_region = used_region_allocation.address.toHigherHalfVirtualAddress().access(*UsedRegionList);
|
||||||
.next = null,
|
new_used_region.* = .{
|
||||||
|
.region = allocation,
|
||||||
|
.use_case = .{ .reason = .privileged },
|
||||||
};
|
};
|
||||||
|
|
||||||
if (last_entry) |e| {
|
if (allocator.used_regions) |_| {
|
||||||
e.next = new_entry;
|
var iterator = allocator.used_regions;
|
||||||
|
_ = iterator;
|
||||||
|
@panic("TODO: iterate");
|
||||||
} else {
|
} else {
|
||||||
first_entry = new_entry;
|
allocator.used_regions = new_used_region;
|
||||||
}
|
}
|
||||||
|
|
||||||
last_entry = new_entry;
|
if (allocator.used_region_buffer) |_| {
|
||||||
|
var iterator = allocator.used_region_buffer;
|
||||||
if (memory_taken >= total_memory_to_take) break;
|
_ = iterator;
|
||||||
}
|
@panic("TODO: iterate 2");
|
||||||
}
|
|
||||||
|
|
||||||
const result = .{
|
|
||||||
.head = first_entry,
|
|
||||||
.list_allocator = .{
|
|
||||||
.u = .{
|
|
||||||
.primitive = .{
|
|
||||||
.backing_4k_page = backing_4k_page,
|
|
||||||
.allocated = backing_4k_page_memory_allocated,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
.primitive = true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ListAllocator = extern struct {
|
|
||||||
u: extern union {
|
|
||||||
primitive: extern struct {
|
|
||||||
backing_4k_page: PhysicalAddress,
|
|
||||||
allocated: u64,
|
|
||||||
},
|
|
||||||
normal: extern struct {
|
|
||||||
foo: u64,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
primitive: bool,
|
|
||||||
|
|
||||||
pub fn get(list_allocator: *ListAllocator) *Entry {
|
|
||||||
switch (list_allocator.primitive) {
|
|
||||||
true => {
|
|
||||||
if (list_allocator.u.primitive.allocated < 0x1000) {
|
|
||||||
const result = list_allocator.u.primitive.backing_4k_page.offset(list_allocator.u.primitive.allocated).toHigherHalfVirtualAddress().access(*Entry);
|
|
||||||
list_allocator.u.primitive.backing_4k_page = list_allocator.u.primitive.backing_4k_page.offset(@sizeOf(Entry));
|
|
||||||
return result;
|
|
||||||
} else {
|
} else {
|
||||||
@panic("reached limit");
|
allocator.used_region_buffer = new_buffer;
|
||||||
}
|
|
||||||
},
|
|
||||||
false => {
|
|
||||||
@panic("not primitive allocator not implemented");
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(new_buffer.region.size < allocation.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var iterator = allocator.used_region_buffer;
|
||||||
|
while (iterator) |it| : (iterator = it.next) {
|
||||||
|
if (it.region.size >= @sizeOf(UsedRegionList)) {
|
||||||
|
const new_used_region_allocation = it.region.takeSlice(@sizeOf(UsedRegionList)) catch unreachable;
|
||||||
|
const new_used_region = new_used_region_allocation.address.toHigherHalfVirtualAddress().access(*UsedRegionList);
|
||||||
|
new_used_region.* = .{
|
||||||
|
.region = physical_region,
|
||||||
|
.use_case = use_case,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Entry = extern struct {
|
iterator = allocator.used_regions;
|
||||||
region: PhysicalMemoryRegion,
|
|
||||||
next: ?*Entry,
|
|
||||||
};
|
|
||||||
|
|
||||||
const InitializationError = error{
|
while (iterator) |i| : (iterator = i.next) {
|
||||||
bootstrap_region_not_found,
|
if (i.next == null) {
|
||||||
memory_exceeded,
|
i.next = new_used_region;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true) @panic("TODO: PageAllocator.appendUsedRegion");
|
||||||
|
return error.OutOfMemory;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getPageTableAllocatorInterface(allocator: *PageAllocator) privileged.PageAllocator {
|
||||||
|
return .{
|
||||||
|
.allocate = pageTableAllocateCallback,
|
||||||
|
.context = allocator,
|
||||||
|
.context_type = .cpu,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pageTableAllocateCallback(context: ?*anyopaque, size: u64, alignment: u64, options: privileged.PageAllocator.AllocateOptions) error{OutOfMemory}!lib.PhysicalMemoryRegion {
|
||||||
|
const allocator: *PageAllocator = @alignCast(@ptrCast(context orelse return error.OutOfMemory));
|
||||||
|
assert(alignment == lib.arch.valid_page_sizes[0]);
|
||||||
|
assert(size == lib.arch.valid_page_sizes[0]);
|
||||||
|
assert(options.count == 1);
|
||||||
|
assert(options.level_valid);
|
||||||
|
|
||||||
|
const page_table_allocation = try allocator.allocate(size, .{ .reason = .user_protected });
|
||||||
|
// log.debug("Page table allocation: 0x{x}", .{page_table_allocation.address.value()});
|
||||||
|
|
||||||
|
// TODO: is this right?
|
||||||
|
if (options.user) {
|
||||||
|
const user_page_tables = &user_scheduler.s.capability_root_node.dynamic.page_table;
|
||||||
|
const user_allocator = &user_scheduler.s.capability_root_node.heap.allocator;
|
||||||
|
const new_page_table_ref = try user_page_tables.appendPageTable(user_allocator, .{
|
||||||
|
.region = page_table_allocation,
|
||||||
|
.mapping = page_table_allocation.address.toHigherHalfVirtualAddress(),
|
||||||
|
.flags = .{ .level = options.level },
|
||||||
|
});
|
||||||
|
|
||||||
|
const indexed = options.virtual_address;
|
||||||
|
const indices = indexed.toIndices();
|
||||||
|
|
||||||
|
var page_table_ref = user_page_tables.user;
|
||||||
|
log.debug("Level: {s}", .{@tagName(options.level)});
|
||||||
|
|
||||||
|
for (0..@intFromEnum(options.level) - 1) |level_index| {
|
||||||
|
log.debug("Fetching {s} page table", .{@tagName(@as(paging.Level, @enumFromInt(level_index)))});
|
||||||
|
const page_table = user_page_tables.getPageTable(page_table_ref) catch @panic("WTF");
|
||||||
|
page_table_ref = page_table.children[indices[level_index]];
|
||||||
|
}
|
||||||
|
|
||||||
|
const parent_page_table = user_page_tables.getPageTable(page_table_ref) catch @panic("WTF");
|
||||||
|
parent_page_table.children[indices[@intFromEnum(options.level) - 1]] = new_page_table_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
return page_table_allocation;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// fn getDebugInformation() !lib.ModuleDebugInfo {
|
pub const HeapRegion = extern struct {
|
||||||
// const debug_info = lib.getDebugInformation(heap_allocator.toZig(), file) catch |err| {
|
region: VirtualMemoryRegion,
|
||||||
// try writer.print("Failed to get debug information: {}", .{err});
|
previous: ?*HeapRegion = null,
|
||||||
// return err;
|
next: ?*HeapRegion = null,
|
||||||
// };
|
};
|
||||||
//
|
|
||||||
// return debug_info;
|
pub fn HeapImplementation(comptime user: bool) type {
|
||||||
// }
|
const use_case = .{ .reason = if (user) .user_protected else .heap };
|
||||||
|
_ = use_case;
|
||||||
|
return extern struct {
|
||||||
|
allocator: lib.Allocator = .{
|
||||||
|
.callbacks = .{
|
||||||
|
.allocate = callbackAllocate,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
regions: ?*Region = null,
|
||||||
|
|
||||||
|
const Heap = @This();
|
||||||
|
const Region = HeapRegion;
|
||||||
|
|
||||||
|
pub fn create(heap_allocator: *Heap, comptime T: type) lib.Allocator.Allocate.Error!*T {
|
||||||
|
const result = try heap_allocator.allocate(@sizeOf(T), @alignOf(T));
|
||||||
|
return @ptrFromInt(result.address);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addBootstrapingRegion(heap_allocator: *Heap, region: VirtualMemoryRegion) !void {
|
||||||
|
assert(heap_allocator.regions == null);
|
||||||
|
|
||||||
|
var region_splitter = region;
|
||||||
|
const new_region_vmr = try region_splitter.takeSlice(@sizeOf(Region));
|
||||||
|
const new_region = new_region_vmr.address.access(*Region);
|
||||||
|
new_region.* = Region{
|
||||||
|
.region = region_splitter,
|
||||||
|
};
|
||||||
|
|
||||||
|
heap_allocator.regions = new_region;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: turn the other way around: make the callback call this function
|
||||||
|
pub fn allocate(heap_allocator: *Heap, size: u64, alignment: u64) lib.Allocator.Allocate.Error!lib.Allocator.Allocate.Result {
|
||||||
|
var iterator = heap_allocator.regions;
|
||||||
|
while (iterator) |region| : (iterator = region.next) {
|
||||||
|
if (region.region.address.isAligned(alignment)) {
|
||||||
|
if (region.region.size >= size) {
|
||||||
|
const virtual_region = region.region.takeSlice(size) catch unreachable;
|
||||||
|
const should_remove = region.region.size == 0;
|
||||||
|
if (should_remove) {
|
||||||
|
// TODO: actually remove and reuse
|
||||||
|
if (region.previous) |prev| prev.next = region.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return @bitCast(virtual_region);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const new_size = lib.alignForward(usize, size + @sizeOf(HeapRegion), lib.arch.valid_page_sizes[0]);
|
||||||
|
assert(alignment <= lib.arch.valid_page_sizes[0]);
|
||||||
|
var new_physical_region = try page_allocator.allocate(new_size, .{ .reason = .heap });
|
||||||
|
const new_alloc = new_physical_region.takeSlice(@sizeOf(HeapRegion)) catch unreachable;
|
||||||
|
const new_heap_region = new_alloc.toHigherHalfVirtualAddress().address.access(*HeapRegion);
|
||||||
|
new_heap_region.* = .{
|
||||||
|
.region = new_physical_region.toHigherHalfVirtualAddress(),
|
||||||
|
};
|
||||||
|
|
||||||
|
iterator = heap.regions;
|
||||||
|
if (iterator) |_| {
|
||||||
|
while (iterator) |heap_region| : (iterator = heap_region.next) {
|
||||||
|
if (heap_region.next == null) {
|
||||||
|
heap_region.next = new_heap_region;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
@panic("NO");
|
||||||
|
}
|
||||||
|
|
||||||
|
return heap.allocate(size, alignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn callbackAllocate(allocator: *Allocator, size: u64, alignment: u64) lib.Allocator.Allocate.Error!lib.Allocator.Allocate.Result {
|
||||||
|
// This assert is triggered by the Zig std library
|
||||||
|
//assert(lib.isAligned(size, alignment));
|
||||||
|
const heap_allocator = @fieldParentPtr(Heap, "allocator", allocator);
|
||||||
|
return heap_allocator.allocate(size, alignment);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub const writer = privileged.E9Writer{ .context = {} };
|
pub const writer = privileged.E9Writer{ .context = {} };
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -24,76 +24,10 @@ const pcid_mask = 1 << pcid_bit;
|
|||||||
/// - R10: argument 3
|
/// - R10: argument 3
|
||||||
/// - R8: argument 4
|
/// - R8: argument 4
|
||||||
/// - R9: argument 5
|
/// - R9: argument 5
|
||||||
fn birthSyscall(comptime Syscall: type, raw_arguments: birth.syscall.Arguments) Syscall.ErrorSet.Error!Syscall.Result {
|
export fn syscall(registers: *const Registers) callconv(.C) birth.interface.Raw.Result {
|
||||||
cpu.syscall_count += 1;
|
const options = @as(birth.interface.Raw.Options, @bitCast(registers.syscall_number));
|
||||||
comptime assert(Syscall == birth.capabilities.Syscall(Syscall.capability, Syscall.command));
|
const arguments = birth.interface.Raw.Arguments{ registers.rdi, registers.rsi, registers.rdx, registers.r10, registers.r8, registers.r9 };
|
||||||
const capability: birth.capabilities.Type = Syscall.capability;
|
return cpu.interface.processFromRaw(options, arguments);
|
||||||
const command: birth.capabilities.Command(capability) = Syscall.command;
|
|
||||||
const arguments = try Syscall.toArguments(raw_arguments);
|
|
||||||
|
|
||||||
return if (cpu.user_scheduler.capability_root_node.hasPermissions(capability, command)) switch (capability) {
|
|
||||||
.io => switch (command) {
|
|
||||||
.copy, .mint, .retype, .delete, .revoke, .create => unreachable,
|
|
||||||
.log => blk: {
|
|
||||||
const message = arguments;
|
|
||||||
cpu.writer.writeAll(message) catch unreachable;
|
|
||||||
comptime assert(Syscall.Result == usize);
|
|
||||||
break :blk message.len;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
.cpu => switch (command) {
|
|
||||||
.copy, .mint, .retype, .delete, .revoke, .create => unreachable,
|
|
||||||
.get_core_id => cpu.core_id,
|
|
||||||
.shutdown => cpu.shutdown(.success),
|
|
||||||
.get_command_buffer => {
|
|
||||||
const command_buffer = arguments;
|
|
||||||
_ = command_buffer;
|
|
||||||
@panic("TODO: get_command_buffer");
|
|
||||||
},
|
|
||||||
},
|
|
||||||
.cpu_memory => switch (command) {
|
|
||||||
.allocate => blk: {
|
|
||||||
comptime assert(@TypeOf(arguments) == usize);
|
|
||||||
const size = arguments;
|
|
||||||
const physical_region = try cpu.user_scheduler.capability_root_node.allocatePages(size);
|
|
||||||
try cpu.user_scheduler.capability_root_node.allocateCPUMemory(physical_region, .{ .privileged = false });
|
|
||||||
break :blk physical_region.address;
|
|
||||||
},
|
|
||||||
else => @panic(@tagName(command)),
|
|
||||||
},
|
|
||||||
.ram => unreachable,
|
|
||||||
.boot => switch (command) {
|
|
||||||
.get_bundle_size => cpu.bundle.len,
|
|
||||||
.get_bundle_file_list_size => cpu.bundle_files.len,
|
|
||||||
else => @panic(@tagName(command)),
|
|
||||||
},
|
|
||||||
.process => switch (command) {
|
|
||||||
.exit => switch (arguments) {
|
|
||||||
true => cpu.shutdown(.success),
|
|
||||||
false => cpu.panic("User process panicked", .{}),
|
|
||||||
},
|
|
||||||
else => @panic(@tagName(command)),
|
|
||||||
},
|
|
||||||
.page_table => @panic("TODO: page_table"),
|
|
||||||
} else error.forbidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
export fn syscall(registers: *const Registers) callconv(.C) birth.syscall.Result {
|
|
||||||
const options = @as(birth.syscall.Options, @bitCast(registers.syscall_number));
|
|
||||||
const arguments = birth.syscall.Arguments{ registers.rdi, registers.rsi, registers.rdx, registers.r10, registers.r8, registers.r9 };
|
|
||||||
|
|
||||||
return switch (options.general.convention) {
|
|
||||||
.birth => switch (options.birth.type) {
|
|
||||||
inline else => |capability| switch (@as(birth.capabilities.Command(capability), @enumFromInt(options.birth.command))) {
|
|
||||||
inline else => |command| blk: {
|
|
||||||
const Syscall = birth.capabilities.Syscall(capability, command);
|
|
||||||
const result: Syscall.Result = birthSyscall(Syscall, arguments) catch |err| break :blk Syscall.errorToRaw(err);
|
|
||||||
break :blk Syscall.resultToRaw(result);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
.linux => @panic("linux syscall"),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// SYSCALL documentation
|
/// SYSCALL documentation
|
||||||
@ -107,7 +41,7 @@ export fn syscall(registers: *const Registers) callconv(.C) birth.syscall.Result
|
|||||||
/// - R10: argument 3
|
/// - R10: argument 3
|
||||||
/// - R8: argument 4
|
/// - R8: argument 4
|
||||||
/// - R9: argument 5
|
/// - R9: argument 5
|
||||||
pub fn entryPoint() callconv(.Naked) void {
|
pub fn entryPoint() callconv(.Naked) noreturn {
|
||||||
asm volatile (
|
asm volatile (
|
||||||
\\endbr64
|
\\endbr64
|
||||||
\\swapgs
|
\\swapgs
|
||||||
@ -252,8 +186,6 @@ pub fn entryPoint() callconv(.Naked) void {
|
|||||||
asm volatile (
|
asm volatile (
|
||||||
\\int3
|
\\int3
|
||||||
::: "memory");
|
::: "memory");
|
||||||
|
|
||||||
unreachable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const Registers = extern struct {
|
pub const Registers = extern struct {
|
||||||
|
@ -22,7 +22,7 @@ const VirtualMemoryRegion = lib.VirtualMemoryRegion;
|
|||||||
const cpu = @import("cpu");
|
const cpu = @import("cpu");
|
||||||
const Heap = cpu.Heap;
|
const Heap = cpu.Heap;
|
||||||
|
|
||||||
const init = @import("./x86/64/init.zig");
|
pub const init = @import("./x86/64/init.zig");
|
||||||
pub const syscall = @import("./x86/64/syscall.zig");
|
pub const syscall = @import("./x86/64/syscall.zig");
|
||||||
pub const entryPoint = init.entryPoint;
|
pub const entryPoint = init.entryPoint;
|
||||||
|
|
||||||
@ -56,13 +56,33 @@ pub const Registers = extern struct {
|
|||||||
|
|
||||||
const interrupt_kind: u32 = 0;
|
const interrupt_kind: u32 = 0;
|
||||||
|
|
||||||
|
const PageFaultFlags = packed struct(u32) {
|
||||||
|
present: bool,
|
||||||
|
write: bool,
|
||||||
|
user: bool,
|
||||||
|
reserved_write: bool,
|
||||||
|
instruction_fetch: bool,
|
||||||
|
protection_key: bool,
|
||||||
|
shadow_stack: bool,
|
||||||
|
reserved0: u8 = 0,
|
||||||
|
software_guard_extensions: bool,
|
||||||
|
reserved: u16 = 0,
|
||||||
|
};
|
||||||
|
|
||||||
export fn interruptHandler(regs: *const InterruptRegisters, interrupt_number: u8) void {
|
export fn interruptHandler(regs: *const InterruptRegisters, interrupt_number: u8) void {
|
||||||
switch (interrupt_number) {
|
switch (interrupt_number) {
|
||||||
local_timer_vector => {
|
local_timer_vector => {
|
||||||
APIC.write(.eoi, 0);
|
APIC.write(.eoi, 0);
|
||||||
nextTimer(10);
|
nextTimer(10);
|
||||||
},
|
},
|
||||||
else => cpu.panicFromInstructionPointerAndFramePointer(regs.rip, regs.rbp, "Exception: 0x{x}", .{interrupt_number}),
|
else => {
|
||||||
|
if (interrupt_number == 0xe) {
|
||||||
|
const pagefault_flags: PageFaultFlags = @bitCast(@as(u32, @intCast(regs.error_code)));
|
||||||
|
const fault_address = privileged.arch.x86_64.registers.cr2.read();
|
||||||
|
log.err("Page 0x{x} not mapped at IP 0x{x}. Flags: {}", .{ fault_address, regs.rip, pagefault_flags });
|
||||||
|
}
|
||||||
|
cpu.panicFromInstructionPointerAndFramePointer(regs.rip, regs.rbp, "Exception 0x{x} at IP 0x{x}", .{ interrupt_number, regs.rip });
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,7 +123,7 @@ pub const invariant_tsc = false;
|
|||||||
pub const capability_address_space_size = 1 * lib.gb;
|
pub const capability_address_space_size = 1 * lib.gb;
|
||||||
pub const capability_address_space_start = capability_address_space_stack_top - capability_address_space_size;
|
pub const capability_address_space_start = capability_address_space_stack_top - capability_address_space_size;
|
||||||
pub const capability_address_space_stack_top = 0xffff_ffff_8000_0000;
|
pub const capability_address_space_stack_top = 0xffff_ffff_8000_0000;
|
||||||
pub const capability_address_space_stack_size = privileged.default_stack_size;
|
pub const capability_address_space_stack_size = 10 * privileged.default_stack_size;
|
||||||
pub const capability_address_space_stack_alignment = lib.arch.valid_page_sizes[0];
|
pub const capability_address_space_stack_alignment = lib.arch.valid_page_sizes[0];
|
||||||
pub const capability_address_space_stack_address = VirtualAddress.new(capability_address_space_stack_top - capability_address_space_stack_size);
|
pub const capability_address_space_stack_address = VirtualAddress.new(capability_address_space_stack_top - capability_address_space_stack_size);
|
||||||
pub const code_64 = @offsetOf(GDT, "code_64");
|
pub const code_64 = @offsetOf(GDT, "code_64");
|
||||||
@ -263,3 +283,5 @@ pub const root_page_table_entry = @as(cpu.arch.PageTableEntry, @enumFromInt(0));
|
|||||||
pub const IOMap = extern struct {
|
pub const IOMap = extern struct {
|
||||||
debug: bool,
|
debug: bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const user_root_page_table_alignment = 2 * lib.arch.valid_page_sizes[0];
|
||||||
|
@ -1,419 +0,0 @@
|
|||||||
const lib = @import("lib");
|
|
||||||
const assert = lib.assert;
|
|
||||||
const Allocator = lib.Allocator;
|
|
||||||
const enumCount = lib.enumCount;
|
|
||||||
const log = lib.log.scoped(.capabilities);
|
|
||||||
|
|
||||||
const privileged = @import("privileged");
|
|
||||||
const PhysicalAddress = lib.PhysicalAddress;
|
|
||||||
const PhysicalMemoryRegion = lib.PhysicalMemoryRegion;
|
|
||||||
const birth = @import("birth");
|
|
||||||
const cpu = @import("cpu");
|
|
||||||
|
|
||||||
pub const RootDescriptor = extern struct {
|
|
||||||
value: *Root,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Static = enum {
|
|
||||||
cpu,
|
|
||||||
boot,
|
|
||||||
process,
|
|
||||||
|
|
||||||
pub const count = lib.enumCount(@This());
|
|
||||||
|
|
||||||
pub const Bitmap = @Type(.{
|
|
||||||
.Struct = blk: {
|
|
||||||
const full_bit_size = @max(@as(comptime_int, 1 << 3), @as(u8, @sizeOf(Static)) << 3);
|
|
||||||
break :blk .{
|
|
||||||
.layout = .Packed,
|
|
||||||
.backing_integer = lib.IntType(.unsigned, full_bit_size),
|
|
||||||
.fields = fields: {
|
|
||||||
var fields: []const lib.Type.StructField = &.{};
|
|
||||||
inline for (lib.enumFields(Static)) |static_field| {
|
|
||||||
fields = fields ++ [1]lib.Type.StructField{.{
|
|
||||||
.name = static_field.name,
|
|
||||||
.type = bool,
|
|
||||||
.default_value = null,
|
|
||||||
.is_comptime = false,
|
|
||||||
.alignment = 0,
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(Static.count > 0);
|
|
||||||
assert(@sizeOf(Static) > 0 or Static.count == 1);
|
|
||||||
|
|
||||||
const padding_type = lib.IntType(.unsigned, full_bit_size - Static.count);
|
|
||||||
|
|
||||||
fields = fields ++ [1]lib.Type.StructField{.{
|
|
||||||
.name = "reserved",
|
|
||||||
.type = padding_type,
|
|
||||||
.default_value = &@as(padding_type, 0),
|
|
||||||
.is_comptime = false,
|
|
||||||
.alignment = 0,
|
|
||||||
}};
|
|
||||||
break :fields fields;
|
|
||||||
},
|
|
||||||
.decls = &.{},
|
|
||||||
.is_tuple = false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Dynamic = enum {
|
|
||||||
io,
|
|
||||||
ram, // Barrelfish equivalent: RAM (no PhysAddr)
|
|
||||||
cpu_memory, // Barrelfish equivalent: Frame
|
|
||||||
page_table, // Barrelfish equivalent: VNode
|
|
||||||
// irq_table,
|
|
||||||
// device_memory,
|
|
||||||
// scheduler,
|
|
||||||
|
|
||||||
pub const Map = extern struct {
|
|
||||||
io: IO,
|
|
||||||
ram: RAM,
|
|
||||||
cpu_memory: CPUMemory,
|
|
||||||
page_table: PageTables,
|
|
||||||
|
|
||||||
comptime {
|
|
||||||
inline for (lib.fields(Dynamic.Map), lib.fields(Dynamic)) |struct_field, enum_field| {
|
|
||||||
assert(lib.equal(u8, enum_field.name, struct_field.name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const RAM = extern struct {
|
|
||||||
lists: [lib.arch.reverse_valid_page_sizes.len]?*Region = .{null} ** lib.arch.valid_page_sizes.len,
|
|
||||||
|
|
||||||
const AllocateError = error{
|
|
||||||
OutOfMemory,
|
|
||||||
};
|
|
||||||
|
|
||||||
inline fn getListIndex(size: usize) usize {
|
|
||||||
inline for (lib.arch.reverse_valid_page_sizes, 0..) |reverse_page_size, reverse_index| {
|
|
||||||
if (size >= reverse_page_size) return reverse_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
unreachable;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const Region = extern struct {
|
|
||||||
region: PhysicalMemoryRegion,
|
|
||||||
next: ?*@This() = null,
|
|
||||||
|
|
||||||
const UnalignedAllocationResult = extern struct {
|
|
||||||
wasted: PhysicalMemoryRegion,
|
|
||||||
allocated: PhysicalMemoryRegion,
|
|
||||||
};
|
|
||||||
|
|
||||||
inline fn allocateUnaligned(free_ram: *Region, size: usize, alignment: usize) ?UnalignedAllocationResult {
|
|
||||||
const aligned_region_address = lib.alignForward(usize, free_ram.region.address.value(), alignment);
|
|
||||||
const wasted_space = aligned_region_address - free_ram.region.address.value();
|
|
||||||
if (free_ram.region.size >= wasted_space + size) {
|
|
||||||
const wasted_region = free_ram.region.takeSlice(wasted_space);
|
|
||||||
const allocated_region = free_ram.region.takeSlice(size);
|
|
||||||
return UnalignedAllocationResult{
|
|
||||||
.wasted = wasted_region,
|
|
||||||
.allocated = allocated_region,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const CPUMemory = extern struct {
|
|
||||||
privileged: RAM = .{},
|
|
||||||
user: RAM = .{},
|
|
||||||
flags: Flags,
|
|
||||||
|
|
||||||
const Flags = packed struct(u64) {
|
|
||||||
allocate: bool,
|
|
||||||
reserved: u63 = 0,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const PageTables = extern struct {
|
|
||||||
foo: u32 = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const IO = extern struct {
|
|
||||||
debug: bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Scheduler = extern struct {
|
|
||||||
handle: ?*cpu.UserScheduler = null,
|
|
||||||
memory: PhysicalMemoryRegion,
|
|
||||||
};
|
|
||||||
|
|
||||||
comptime {
|
|
||||||
assert(enumCount(Dynamic) + enumCount(Static) == enumCount(birth.capabilities.Type));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const Root = extern struct {
|
|
||||||
static: Static.Bitmap,
|
|
||||||
dynamic: Dynamic.Map,
|
|
||||||
scheduler: Scheduler,
|
|
||||||
heap: Heap = .{},
|
|
||||||
padding: [padding_byte_count]u8 = .{0} ** padding_byte_count,
|
|
||||||
|
|
||||||
const max_alignment = @max(@alignOf(Static.Bitmap), @alignOf(Dynamic.Map), @alignOf(Scheduler), @alignOf(Heap));
|
|
||||||
const total_size = lib.alignForward(usize, @sizeOf(Static.Bitmap) + @sizeOf(Dynamic.Map) + @sizeOf(Scheduler) + @sizeOf(Heap), max_alignment);
|
|
||||||
const page_aligned_size = lib.alignForward(usize, total_size, lib.arch.valid_page_sizes[0]);
|
|
||||||
const padding_byte_count = page_aligned_size - total_size;
|
|
||||||
|
|
||||||
comptime {
|
|
||||||
assert(@sizeOf(Root) % lib.arch.valid_page_sizes[0] == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn copy(root: *Root, other: *Root) void {
|
|
||||||
other.static = root.static;
|
|
||||||
// TODO:
|
|
||||||
other.dynamic = root.dynamic;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn hasPermissions(root: *const Root, comptime capability_type: birth.capabilities.Type, command: birth.capabilities.Command(capability_type)) bool {
|
|
||||||
return switch (capability_type) {
|
|
||||||
// static capabilities
|
|
||||||
inline .cpu,
|
|
||||||
.boot,
|
|
||||||
.process,
|
|
||||||
=> |static_capability| @field(root.static, @tagName(static_capability)),
|
|
||||||
// dynamic capabilities
|
|
||||||
.io => switch (command) {
|
|
||||||
.copy, .mint, .retype, .delete, .revoke, .create => unreachable,
|
|
||||||
.log => root.dynamic.io.debug,
|
|
||||||
},
|
|
||||||
.cpu_memory => root.dynamic.cpu_memory.flags.allocate,
|
|
||||||
.ram => unreachable,
|
|
||||||
.page_table => unreachable,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const AllocateError = error{
|
|
||||||
OutOfMemory,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Fast path
|
|
||||||
pub fn allocatePages(root: *Root, size: usize) AllocateError!PhysicalMemoryRegion {
|
|
||||||
assert(size != 0);
|
|
||||||
assert(lib.isAligned(size, lib.arch.valid_page_sizes[0]));
|
|
||||||
var index = RAM.getListIndex(size);
|
|
||||||
|
|
||||||
const result = blk: {
|
|
||||||
while (true) : (index -= 1) {
|
|
||||||
const list = root.dynamic.ram.lists[index];
|
|
||||||
var iterator = list;
|
|
||||||
|
|
||||||
while (iterator) |free_ram| : (iterator = free_ram.next) {
|
|
||||||
if (free_ram.region.size >= size) {
|
|
||||||
if (free_ram.region.size >= size) {
|
|
||||||
const result = free_ram.region.takeSlice(size);
|
|
||||||
break :blk result;
|
|
||||||
} else {
|
|
||||||
@panic("TODO: cnsume all reigon");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index == 0) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return error.OutOfMemory;
|
|
||||||
};
|
|
||||||
|
|
||||||
@memset(result.toHigherHalfVirtualAddress().access(u8), 0);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slow uncommon path. Use cases:
|
|
||||||
// 1. CR3 switch. This is assumed to be privileged, so this function assumes privileged use of the memory
|
|
||||||
pub fn allocatePageCustomAlignment(root: *Root, size: usize, alignment: usize) AllocateError!PhysicalMemoryRegion {
|
|
||||||
assert(alignment > lib.arch.valid_page_sizes[0] and alignment < lib.arch.valid_page_sizes[1]);
|
|
||||||
|
|
||||||
comptime assert(lib.arch.valid_page_sizes.len == 3);
|
|
||||||
var index = RAM.getListIndex(size);
|
|
||||||
|
|
||||||
while (true) : (index -= 1) {
|
|
||||||
if (root.dynamic.ram.lists[index]) |smallest_region_list| {
|
|
||||||
var iterator: ?*cpu.capabilities.RAM.Region = smallest_region_list;
|
|
||||||
while (iterator) |free_ram| : (iterator = free_ram.next) {
|
|
||||||
if (lib.isAligned(free_ram.region.address.value(), alignment)) {
|
|
||||||
if (free_ram.region.size >= size) {
|
|
||||||
const allocated_region = free_ram.region.takeSlice(size);
|
|
||||||
return allocated_region;
|
|
||||||
}
|
|
||||||
} else if (free_ram.allocateUnaligned(size, alignment)) |unaligned_allocation| {
|
|
||||||
try root.addRegion(&root.dynamic.ram, unaligned_allocation.wasted);
|
|
||||||
return unaligned_allocation.allocated;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index == 0) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return AllocateError.OutOfMemory;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn allocateSingle(root: *Root, comptime T: type) AllocateError!*T {
|
|
||||||
var iterator = root.heap.first;
|
|
||||||
while (iterator) |heap_region| : (iterator = heap_region.next) {
|
|
||||||
if (heap_region.alignmentFits(@alignOf(T))) {
|
|
||||||
if (heap_region.sizeFits(@sizeOf(T))) {
|
|
||||||
const allocated_region = heap_region.takeRegion(@sizeOf(T));
|
|
||||||
const result = &allocated_region.toHigherHalfVirtualAddress().access(T)[0];
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
@panic("ELSE");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const physical_region = try root.allocatePages(lib.arch.valid_page_sizes[0]);
|
|
||||||
const heap_region = physical_region.toHigherHalfVirtualAddress().address.access(*Heap.Region);
|
|
||||||
const first = root.heap.first;
|
|
||||||
heap_region.* = .{
|
|
||||||
.descriptor = physical_region.offset(@sizeOf(Heap.Region)),
|
|
||||||
.allocated_size = @sizeOf(Heap.Region),
|
|
||||||
.next = first,
|
|
||||||
};
|
|
||||||
|
|
||||||
root.heap.first = heap_region;
|
|
||||||
|
|
||||||
return try root.allocateSingle(T);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn allocateMany(root: *Root, comptime T: type, count: usize) AllocateError![]T {
|
|
||||||
_ = count;
|
|
||||||
_ = root;
|
|
||||||
|
|
||||||
@panic("TODO many");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn addRegion(root: *Root, ram: *RAM, physical_region: PhysicalMemoryRegion) !void {
|
|
||||||
const index = RAM.getListIndex(physical_region.size);
|
|
||||||
const new_region = try root.allocateSingle(RAM.Region);
|
|
||||||
new_region.* = RAM.Region{
|
|
||||||
.region = physical_region,
|
|
||||||
.next = root.dynamic.ram.lists[index],
|
|
||||||
};
|
|
||||||
|
|
||||||
ram.lists[index] = new_region;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const AllocateCPUMemoryOptions = packed struct {
|
|
||||||
privileged: bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn allocateCPUMemory(root: *Root, physical_region: PhysicalMemoryRegion, options: AllocateCPUMemoryOptions) !void {
|
|
||||||
const ram_region = switch (options.privileged) {
|
|
||||||
true => &root.dynamic.cpu_memory.privileged,
|
|
||||||
false => &root.dynamic.cpu_memory.user,
|
|
||||||
};
|
|
||||||
|
|
||||||
try root.addRegion(ram_region, physical_region);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const Heap = extern struct {
|
|
||||||
first: ?*Region = null,
|
|
||||||
|
|
||||||
const AllocateError = error{
|
|
||||||
OutOfMemory,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn new(physical_region: PhysicalMemoryRegion, previous_allocated_size: usize) Heap {
|
|
||||||
const allocated_size = previous_allocated_size + @sizeOf(Region);
|
|
||||||
assert(physical_region.size > allocated_size);
|
|
||||||
const region = physical_region.offset(previous_allocated_size).address.toHigherHalfVirtualAddress().access(*Region);
|
|
||||||
region.* = .{
|
|
||||||
.descriptor = physical_region,
|
|
||||||
.allocated_size = allocated_size,
|
|
||||||
};
|
|
||||||
return Heap{
|
|
||||||
.first = region,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create(heap: *Heap, comptime T: type) Heap.AllocateError!*T {
|
|
||||||
const result = try heap.allocate(T, 1);
|
|
||||||
return &result[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
fn allocate(heap: *Heap, comptime T: type, count: usize) Heap.AllocateError![]T {
|
|
||||||
var iterator = heap.first;
|
|
||||||
while (iterator) |heap_region| {
|
|
||||||
const allocation = heap_region.allocate(T, count) catch continue;
|
|
||||||
return allocation;
|
|
||||||
}
|
|
||||||
@panic("TODO: allocate");
|
|
||||||
}
|
|
||||||
|
|
||||||
const Region = extern struct {
|
|
||||||
descriptor: PhysicalMemoryRegion,
|
|
||||||
allocated_size: usize,
|
|
||||||
next: ?*Region = null,
|
|
||||||
|
|
||||||
inline fn getFreeRegion(region: Region) PhysicalMemoryRegion {
|
|
||||||
const free_region = region.descriptor.offset(region.allocated_size);
|
|
||||||
assert(free_region.size > 0);
|
|
||||||
return free_region;
|
|
||||||
}
|
|
||||||
|
|
||||||
const AllocateError = error{
|
|
||||||
OutOfMemory,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn takeRegion(region: *Region, size: usize) PhysicalMemoryRegion {
|
|
||||||
var free_region = region.getFreeRegion();
|
|
||||||
assert(free_region.size >= size);
|
|
||||||
const allocated_region = free_region.takeSlice(size);
|
|
||||||
region.allocated_size += size;
|
|
||||||
return allocated_region;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn allocate(region: *Region, comptime T: type, count: usize) Region.AllocateError![]T {
|
|
||||||
const free_region = region.getFreeRegion();
|
|
||||||
_ = free_region;
|
|
||||||
_ = count;
|
|
||||||
@panic("TODO: region allocate");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create(region: *Region, comptime T: type) Region.AllocateError!*T {
|
|
||||||
const result = try region.allocate(T, 1);
|
|
||||||
return &result[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fn canAllocateDirectly(region: Region, size: usize, alignment: usize) bool {
|
|
||||||
const alignment_fits = region.alignmentFits(alignment);
|
|
||||||
const size_fits = region.sizeFits(size);
|
|
||||||
return alignment_fits and size_fits;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fn canAllocateSplitting(region: Region, size: usize, alignment: usize) bool {
|
|
||||||
const free_region = region.getFreeRegion();
|
|
||||||
const aligned_region_address = lib.alignForward(usize, free_region.address.value(), alignment);
|
|
||||||
const wasted_space = aligned_region_address - free_region.address.value();
|
|
||||||
log.warn("Wasted space: {} bytes", .{wasted_space});
|
|
||||||
_ = size;
|
|
||||||
@panic("TODO: canAllocateSplitting");
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fn sizeFits(region: Region, size: usize) bool {
|
|
||||||
return region.descriptor.size - region.allocated_size >= size;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fn alignmentFits(region: Region, alignment: usize) bool {
|
|
||||||
const result = lib.isAligned(region.getFreeRegion().address.value(), alignment);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const RootPageTableEntry = extern struct {
|
|
||||||
address: PhysicalAddress,
|
|
||||||
};
|
|
368
src/cpu/init.zig
Normal file
368
src/cpu/init.zig
Normal file
@ -0,0 +1,368 @@
|
|||||||
|
const birth = @import("birth");
|
||||||
|
const bootloader = @import("bootloader");
|
||||||
|
const cpu = @import("cpu");
|
||||||
|
const lib = @import("lib");
|
||||||
|
const privileged = @import("privileged");
|
||||||
|
|
||||||
|
const assert = lib.assert;
|
||||||
|
const log = lib.log;
|
||||||
|
const PhysicalAddress = lib.PhysicalAddress;
|
||||||
|
const PhysicalMemoryRegion = lib.PhysicalMemoryRegion;
|
||||||
|
const VirtualAddress = lib.VirtualAddress;
|
||||||
|
const VirtualMemoryRegion = lib.VirtualMemoryRegion;
|
||||||
|
|
||||||
|
const RegionList = cpu.RegionList;
|
||||||
|
const PageTableRegions = cpu.arch.init.PageTableRegions;
|
||||||
|
|
||||||
|
const paging = privileged.arch.paging;
|
||||||
|
pub const Error = error{
|
||||||
|
feature_requested_and_not_available,
|
||||||
|
no_files,
|
||||||
|
cpu_file_not_found,
|
||||||
|
init_file_not_found,
|
||||||
|
no_space_for_bootstrap_region,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn initialize(bootloader_information: *bootloader.Information) !noreturn {
|
||||||
|
// bootloader_information.draw_context.clearScreen(0xffff7f50);
|
||||||
|
// Do an integrity check so that the bootloader information is in perfect state and there is no weird memory behavior.
|
||||||
|
// This is mainly due to the transition from a 32-bit bootloader to a 64-bit CPU driver in the x86-64 architecture.
|
||||||
|
try bootloader_information.checkIntegrity();
|
||||||
|
// Informing the bootloader information struct that we have reached the CPU driver and any bootloader
|
||||||
|
// functionality is not available anymore
|
||||||
|
bootloader_information.stage = .cpu;
|
||||||
|
// Check that the bootloader has loaded some files as the CPU driver needs them to go forward
|
||||||
|
cpu.bundle = bootloader_information.getSlice(.bundle);
|
||||||
|
if (cpu.bundle.len == 0) {
|
||||||
|
return Error.no_files;
|
||||||
|
}
|
||||||
|
cpu.bundle_files = bootloader_information.getSlice(.file_list);
|
||||||
|
if (cpu.bundle_files.len == 0) {
|
||||||
|
return Error.no_files;
|
||||||
|
}
|
||||||
|
|
||||||
|
try cpu.arch.init.initialize();
|
||||||
|
|
||||||
|
const memory_map_entries = bootloader_information.getMemoryMapEntries();
|
||||||
|
const page_counters = bootloader_information.getPageCounters();
|
||||||
|
|
||||||
|
const first_heap_allocation_size = 2 * lib.arch.valid_page_sizes[0];
|
||||||
|
|
||||||
|
var heap_region_metadata: struct {
|
||||||
|
region: PhysicalMemoryRegion,
|
||||||
|
free_size: u64,
|
||||||
|
index: usize,
|
||||||
|
} = for (memory_map_entries, page_counters, 0..) |mmap_entry, page_counter, index| {
|
||||||
|
if (mmap_entry.type == .usable) {
|
||||||
|
const free_region = mmap_entry.getFreeRegion(page_counter);
|
||||||
|
if (free_region.size >= first_heap_allocation_size) {
|
||||||
|
break .{
|
||||||
|
.region = PhysicalMemoryRegion.new(.{
|
||||||
|
.address = free_region.address,
|
||||||
|
.size = free_region.size,
|
||||||
|
}),
|
||||||
|
.free_size = free_region.size - first_heap_allocation_size,
|
||||||
|
.index = index,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else return error.no_space_for_bootstrap_region;
|
||||||
|
|
||||||
|
const heap_region = try heap_region_metadata.region.takeSlice(first_heap_allocation_size);
|
||||||
|
try cpu.heap.addBootstrapingRegion(heap_region.toHigherHalfVirtualAddress());
|
||||||
|
|
||||||
|
const host_free_region_list = try cpu.heap.create(RegionList);
|
||||||
|
|
||||||
|
var free_size: u64 = 0;
|
||||||
|
_ = try host_free_region_list.append(heap_region_metadata.region);
|
||||||
|
free_size += heap_region_metadata.region.size;
|
||||||
|
|
||||||
|
var region_list_iterator = host_free_region_list;
|
||||||
|
|
||||||
|
for (memory_map_entries, page_counters, 0..) |memory_map_entry, page_counter, index| {
|
||||||
|
if (index == heap_region_metadata.index) continue;
|
||||||
|
|
||||||
|
if (memory_map_entry.type == .usable) {
|
||||||
|
const free_region = memory_map_entry.getFreeRegion(page_counter);
|
||||||
|
|
||||||
|
if (free_region.size > 0) {
|
||||||
|
assert(lib.isAligned(free_region.size, lib.arch.valid_page_sizes[0]));
|
||||||
|
_ = region_list_iterator.append(free_region) catch {
|
||||||
|
const new_region_list = try cpu.heap.create(RegionList);
|
||||||
|
region_list_iterator.metadata.next = new_region_list;
|
||||||
|
new_region_list.metadata.previous = region_list_iterator;
|
||||||
|
region_list_iterator = new_region_list;
|
||||||
|
_ = try region_list_iterator.append(free_region);
|
||||||
|
};
|
||||||
|
|
||||||
|
free_size += free_region.size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu.page_allocator.free_regions = host_free_region_list;
|
||||||
|
cpu.page_allocator.free_byte_count = free_size;
|
||||||
|
|
||||||
|
// Add used regions by the bootloader to the physical memory manager
|
||||||
|
for (memory_map_entries, page_counters) |memory_map_entry, page_counter| {
|
||||||
|
if (memory_map_entry.type == .usable) {
|
||||||
|
const used_region = memory_map_entry.getUsedRegion(page_counter);
|
||||||
|
if (used_region.size > 0) {
|
||||||
|
assert(lib.isAligned(used_region.size, lib.arch.valid_page_sizes[0]));
|
||||||
|
try cpu.page_allocator.appendUsedRegion(used_region, .{ .reason = .bootloader });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var used_regions = cpu.page_allocator.used_regions;
|
||||||
|
var used_memory_by_bootloader: usize = 0;
|
||||||
|
while (used_regions) |used_region| : (used_regions = used_region.next) {
|
||||||
|
if (used_region.use_case.reason == .bootloader) {
|
||||||
|
used_memory_by_bootloader += used_region.region.size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("Used memory by the bootloader: 0x{x} bytes", .{used_memory_by_bootloader});
|
||||||
|
|
||||||
|
try cpu.page_allocator.appendUsedRegion(heap_region, .{ .reason = .heap });
|
||||||
|
|
||||||
|
switch (cpu.bsp) {
|
||||||
|
true => {
|
||||||
|
// Setup kernel debug information
|
||||||
|
cpu.debug_info = blk: {
|
||||||
|
const cpu_driver_executable_descriptor = try bootloader_information.getFileDescriptor("cpu_driver");
|
||||||
|
const elf_file = file: {
|
||||||
|
const aligned_file_len = lib.alignForward(usize, cpu_driver_executable_descriptor.content.len, lib.arch.valid_page_sizes[0]);
|
||||||
|
const elf_file_physical_allocation = try cpu.page_allocator.allocate(aligned_file_len, .{ .reason = .privileged });
|
||||||
|
break :file elf_file_physical_allocation.toHigherHalfVirtualAddress().address.access([*]align(lib.arch.valid_page_sizes[0]) u8)[0..elf_file_physical_allocation.size];
|
||||||
|
};
|
||||||
|
lib.memcpy(elf_file[0..cpu_driver_executable_descriptor.content.len], cpu_driver_executable_descriptor.content);
|
||||||
|
const result = try lib.getDebugInformation(cpu.heap.allocator.zigUnwrap(), elf_file);
|
||||||
|
break :blk result;
|
||||||
|
};
|
||||||
|
|
||||||
|
const init_module_descriptor = try bootloader_information.getFileDescriptor("init");
|
||||||
|
|
||||||
|
try spawnInitBSP(init_module_descriptor.content, bootloader_information.cpu_page_tables);
|
||||||
|
},
|
||||||
|
false => @panic("TODO: implement APP"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ELF = lib.ELF(64);
|
||||||
|
|
||||||
|
const SpawnInitCommonResult = extern struct {
|
||||||
|
scheduler: *cpu.UserScheduler,
|
||||||
|
entry_point: u64,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const MappingArgument = extern struct {
|
||||||
|
virtual: VirtualAddress,
|
||||||
|
physical: PhysicalAddress,
|
||||||
|
size: u64,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const InitFile = struct {
|
||||||
|
content: []const u8,
|
||||||
|
segments: []const Segment,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Segment = extern struct {
|
||||||
|
virtual: VirtualAddress,
|
||||||
|
physical: PhysicalAddress,
|
||||||
|
memory_size: usize,
|
||||||
|
flags: privileged.Mapping.Flags,
|
||||||
|
file_offset: usize,
|
||||||
|
file_size: usize,
|
||||||
|
};
|
||||||
|
|
||||||
|
var once: bool = false;
|
||||||
|
|
||||||
|
fn spawnInitCommon(init_file: []const u8, cpu_page_tables: paging.CPUPageTables) !SpawnInitCommonResult {
|
||||||
|
assert(!once);
|
||||||
|
once = true;
|
||||||
|
// TODO: delete in the future
|
||||||
|
assert(cpu.bsp);
|
||||||
|
|
||||||
|
const init_cpu_scheduler = try cpu.heap.create(cpu.UserScheduler);
|
||||||
|
init_cpu_scheduler.* = cpu.UserScheduler{
|
||||||
|
.s = .{
|
||||||
|
.common = undefined,
|
||||||
|
.capability_root_node = cpu.interface.Root{
|
||||||
|
.static = .{
|
||||||
|
.cpu = true,
|
||||||
|
.boot = true,
|
||||||
|
.process = true,
|
||||||
|
},
|
||||||
|
.dynamic = .{
|
||||||
|
.io = .{
|
||||||
|
.debug = true,
|
||||||
|
},
|
||||||
|
.memory = .{},
|
||||||
|
.cpu_memory = .{
|
||||||
|
.flags = .{
|
||||||
|
.allocate = true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.page_table = cpu.interface.PageTables{
|
||||||
|
.privileged = undefined,
|
||||||
|
.user = birth.interface.PageTable{
|
||||||
|
.index = 0,
|
||||||
|
.entry_type = .page_table,
|
||||||
|
},
|
||||||
|
// .vmm = try cpu.interface.VMM.new(),
|
||||||
|
.can_map_page_tables = true,
|
||||||
|
.page_tables = .{
|
||||||
|
.ptr = undefined,
|
||||||
|
.len = 0,
|
||||||
|
.capacity = 0,
|
||||||
|
},
|
||||||
|
.leaves = .{
|
||||||
|
.ptr = undefined,
|
||||||
|
.len = 0,
|
||||||
|
.capacity = 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.command_buffer_submission = .{ .region = PhysicalMemoryRegion.invalid() },
|
||||||
|
.command_buffer_completion = .{ .region = PhysicalMemoryRegion.invalid() },
|
||||||
|
.memory_mapping = .{},
|
||||||
|
.page_table_mapping = .{},
|
||||||
|
},
|
||||||
|
.scheduler = .{
|
||||||
|
.memory = undefined,
|
||||||
|
// .memory = scheduler_physical_region,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const init_elf = try ELF.Parser.init(init_file);
|
||||||
|
const entry_point = init_elf.getEntryPoint();
|
||||||
|
const program_headers = init_elf.getProgramHeaders();
|
||||||
|
|
||||||
|
var segment_buffer: [20]Segment = undefined;
|
||||||
|
var segment_count: usize = 0;
|
||||||
|
var segment_total_size: usize = 0;
|
||||||
|
var first_address: ?u64 = null;
|
||||||
|
|
||||||
|
for (program_headers) |program_header| {
|
||||||
|
if (program_header.type == .load) {
|
||||||
|
if (first_address == null) {
|
||||||
|
first_address = program_header.virtual_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
const segment_size = lib.alignForward(usize, program_header.size_in_memory, lib.arch.valid_page_sizes[0]);
|
||||||
|
segment_total_size += segment_size;
|
||||||
|
|
||||||
|
const segment_virtual = VirtualAddress.new(program_header.virtual_address);
|
||||||
|
const segment_physical_region = try cpu.page_allocator.allocate(segment_size, .{ .reason = .user });
|
||||||
|
|
||||||
|
const segment = &segment_buffer[segment_count];
|
||||||
|
segment.* = .{
|
||||||
|
.physical = segment_physical_region.address,
|
||||||
|
.virtual = segment_virtual,
|
||||||
|
.memory_size = segment_size,
|
||||||
|
.flags = .{
|
||||||
|
.execute = program_header.flags.executable,
|
||||||
|
.write = program_header.flags.writable,
|
||||||
|
.user = true,
|
||||||
|
.huge_pages = false,
|
||||||
|
},
|
||||||
|
.file_offset = program_header.offset,
|
||||||
|
.file_size = program_header.size_in_file,
|
||||||
|
};
|
||||||
|
|
||||||
|
const src = init_file[segment.file_offset..][0..segment.file_size];
|
||||||
|
// It's necessary to use the higher half address here since the user mapping is not applied yet
|
||||||
|
const dst = segment_physical_region.toHigherHalfVirtualAddress().access(u8)[0..src.len];
|
||||||
|
lib.memcpy(dst, src);
|
||||||
|
|
||||||
|
segment_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const init_start_address = first_address orelse @panic("WTF");
|
||||||
|
const init_top_address = init_start_address + segment_total_size;
|
||||||
|
const user_scheduler_virtual_address = VirtualAddress.new(init_top_address);
|
||||||
|
init_cpu_scheduler.s.common = user_scheduler_virtual_address.access(*birth.Scheduler.Common);
|
||||||
|
|
||||||
|
const user_scheduler_virtual_region = VirtualMemoryRegion.new(.{
|
||||||
|
.address = user_scheduler_virtual_address,
|
||||||
|
.size = lib.alignForward(usize, @sizeOf(birth.Scheduler), lib.arch.valid_page_sizes[0]),
|
||||||
|
});
|
||||||
|
// Align to 2MB
|
||||||
|
const user_initial_heap_top = lib.alignForward(usize, user_scheduler_virtual_region.top().value(), lib.arch.valid_page_sizes[1]);
|
||||||
|
|
||||||
|
const segments = segment_buffer[0..segment_count];
|
||||||
|
|
||||||
|
const user_virtual_region = VirtualMemoryRegion.new(.{
|
||||||
|
.address = VirtualAddress.new(init_start_address),
|
||||||
|
.size = user_initial_heap_top - init_start_address,
|
||||||
|
});
|
||||||
|
// const page_table_regions = try PageTableRegions.create(user_virtual_region, cpu_page_tables);
|
||||||
|
log.debug("Scheduler region", .{});
|
||||||
|
const scheduler_physical_region = try cpu.page_allocator.allocate(user_scheduler_virtual_region.size, .{ .reason = .user });
|
||||||
|
init_cpu_scheduler.s.capability_root_node.scheduler.memory = scheduler_physical_region;
|
||||||
|
|
||||||
|
const scheduler_virtual_region = VirtualMemoryRegion.new(.{
|
||||||
|
.address = user_scheduler_virtual_address,
|
||||||
|
.size = scheduler_physical_region.size,
|
||||||
|
});
|
||||||
|
|
||||||
|
scheduler_physical_region.address.toHigherHalfVirtualAddress().access(*birth.Scheduler.Common).self = user_scheduler_virtual_address.access(*birth.Scheduler.Common);
|
||||||
|
|
||||||
|
const heap_virtual_region = VirtualMemoryRegion.new(.{
|
||||||
|
.address = scheduler_virtual_region.top(),
|
||||||
|
.size = lib.alignForward(usize, scheduler_virtual_region.top().value(), 64 * lib.arch.valid_page_sizes[1]) - scheduler_virtual_region.top().value(),
|
||||||
|
});
|
||||||
|
|
||||||
|
log.debug("Heap region", .{});
|
||||||
|
const heap_physical_region = try cpu.page_allocator.allocate(heap_virtual_region.size, .{ .reason = .user });
|
||||||
|
@memset(heap_physical_region.toHigherHalfVirtualAddress().access(u8), 0);
|
||||||
|
|
||||||
|
assert(scheduler_physical_region.size == scheduler_virtual_region.size);
|
||||||
|
assert(heap_physical_region.size == heap_virtual_region.size);
|
||||||
|
// Setup common variables
|
||||||
|
const higher_half_scheduler_common = scheduler_physical_region.address.toHigherHalfVirtualAddress().access(*birth.Scheduler.Common);
|
||||||
|
higher_half_scheduler_common.disabled = true;
|
||||||
|
higher_half_scheduler_common.core_id = cpu.core_id;
|
||||||
|
higher_half_scheduler_common.heap = VirtualMemoryRegion.new(.{
|
||||||
|
.address = heap_virtual_region.address,
|
||||||
|
.size = heap_virtual_region.size,
|
||||||
|
});
|
||||||
|
|
||||||
|
try cpu.arch.init.setupMapping(init_cpu_scheduler, user_virtual_region, cpu_page_tables, .{
|
||||||
|
.content = init_file,
|
||||||
|
.segments = segments,
|
||||||
|
}, .{
|
||||||
|
.scheduler = .{
|
||||||
|
.physical = scheduler_physical_region.address,
|
||||||
|
.virtual = scheduler_virtual_region.address,
|
||||||
|
.size = scheduler_virtual_region.size,
|
||||||
|
},
|
||||||
|
.heap = .{
|
||||||
|
.physical = heap_physical_region.address,
|
||||||
|
.virtual = heap_virtual_region.address,
|
||||||
|
.size = heap_virtual_region.size,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return SpawnInitCommonResult{
|
||||||
|
// .page_table_regions = page_table_regions,
|
||||||
|
.scheduler = init_cpu_scheduler,
|
||||||
|
.entry_point = entry_point,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spawnInitBSP(init_file: []const u8, cpu_page_tables: paging.CPUPageTables) !noreturn {
|
||||||
|
const spawn_init = try spawnInitCommon(init_file, cpu_page_tables);
|
||||||
|
const init_scheduler = spawn_init.scheduler;
|
||||||
|
// const page_table_regions = spawn_init.page_table_regions;
|
||||||
|
const entry_point = spawn_init.entry_point;
|
||||||
|
const scheduler_common = init_scheduler.s.common;
|
||||||
|
|
||||||
|
cpu.user_scheduler = init_scheduler;
|
||||||
|
|
||||||
|
cpu.arch.init.setupSchedulerCommon(scheduler_common, entry_point);
|
||||||
|
scheduler_common.disabled_save_area.contextSwitch();
|
||||||
|
}
|
602
src/cpu/interface.zig
Normal file
602
src/cpu/interface.zig
Normal file
@ -0,0 +1,602 @@
|
|||||||
|
const lib = @import("lib");
|
||||||
|
const assert = lib.assert;
|
||||||
|
const Allocator = lib.Allocator;
|
||||||
|
const enumCount = lib.enumCount;
|
||||||
|
const log = lib.log.scoped(.capabilities);
|
||||||
|
const SparseArray = lib.data_structures.SparseArray;
|
||||||
|
const VirtualAddress = lib.VirtualAddress;
|
||||||
|
|
||||||
|
const privileged = @import("privileged");
|
||||||
|
const paging = privileged.arch.paging;
|
||||||
|
const PhysicalAddress = lib.PhysicalAddress;
|
||||||
|
const PhysicalMemoryRegion = lib.PhysicalMemoryRegion;
|
||||||
|
const VirtualMemoryRegion = lib.VirtualMemoryRegion;
|
||||||
|
const birth = @import("birth");
|
||||||
|
const cpu = @import("cpu");
|
||||||
|
const RegionList = cpu.RegionList;
|
||||||
|
|
||||||
|
pub var system_call_count: usize = 0;
|
||||||
|
|
||||||
|
pub fn processFromRaw(options: birth.interface.Raw.Options, arguments: birth.interface.Raw.Arguments) birth.interface.Raw.Result {
|
||||||
|
defer system_call_count += 1;
|
||||||
|
return switch (options.general.convention) {
|
||||||
|
.birth => switch (options.birth.type) {
|
||||||
|
inline else => |capability| switch (@as(birth.interface.Command.fromCapability(capability), @enumFromInt(options.birth.command))) {
|
||||||
|
inline else => |command| blk: {
|
||||||
|
const Interface = birth.interface.Descriptor(capability, command);
|
||||||
|
const result = processCommand(Interface, arguments) catch |err| {
|
||||||
|
lib.log.err("Syscall ({s}, {s}) ended up in error: {}", .{ @tagName(capability), @tagName(command), err });
|
||||||
|
break :blk Interface.fromError(err);
|
||||||
|
};
|
||||||
|
break :blk Interface.fromResult(result);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.emulated => @panic("TODO: emulated"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn processCommand(comptime Descriptor: type, raw_arguments: birth.interface.Raw.Arguments) Descriptor.Error!Descriptor.Result {
|
||||||
|
defer cpu.command_count += 1;
|
||||||
|
const capability = Descriptor.Capability;
|
||||||
|
const command = Descriptor.Command;
|
||||||
|
const arguments = try Descriptor.toArguments(raw_arguments);
|
||||||
|
|
||||||
|
const root = &cpu.user_scheduler.s.capability_root_node;
|
||||||
|
// log.err("\n========\nSyscall received: {s}, {s}\n========\n", .{ @tagName(capability), @tagName(command) });
|
||||||
|
|
||||||
|
assert(root.static.process);
|
||||||
|
const has_permissions = root.hasPermissions(Descriptor, arguments);
|
||||||
|
|
||||||
|
return if (has_permissions) switch (capability) {
|
||||||
|
.io => switch (command) {
|
||||||
|
.copy, .mint, .retype, .delete, .revoke, .create => unreachable,
|
||||||
|
.log => blk: {
|
||||||
|
const message = arguments;
|
||||||
|
cpu.writer.writeAll(message) catch unreachable;
|
||||||
|
comptime assert(Descriptor.Result == usize);
|
||||||
|
break :blk message.len;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.cpu => switch (command) {
|
||||||
|
.copy, .mint, .retype, .delete, .revoke, .create => unreachable,
|
||||||
|
.get_core_id => cpu.core_id,
|
||||||
|
.shutdown => cpu.shutdown(.success),
|
||||||
|
.get_command_buffer => {
|
||||||
|
const command_buffer = arguments;
|
||||||
|
_ = command_buffer;
|
||||||
|
@panic("TODO: get_command_buffer");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.cpu_memory => switch (command) {
|
||||||
|
else => @panic(@tagName(command)),
|
||||||
|
},
|
||||||
|
.command_buffer_completion, .command_buffer_submission => switch (command) {
|
||||||
|
.map => {
|
||||||
|
const region = @field(root.dynamic, @tagName(capability)).region;
|
||||||
|
assert(region.address.value() != 0);
|
||||||
|
assert(region.size != 0);
|
||||||
|
@panic("TODO: map");
|
||||||
|
}, // TODO
|
||||||
|
else => @panic(@tagName(command)),
|
||||||
|
},
|
||||||
|
.memory => switch (command) {
|
||||||
|
.allocate => blk: {
|
||||||
|
comptime assert(@TypeOf(arguments) == usize);
|
||||||
|
const size = arguments;
|
||||||
|
// TODO: we want more fine-grained control of the reason if we want more than a simple statistic
|
||||||
|
const result = try root.allocateMemory(size);
|
||||||
|
break :blk result.reference;
|
||||||
|
},
|
||||||
|
.retype => blk: {
|
||||||
|
const source = arguments.source;
|
||||||
|
const destination = arguments.destination;
|
||||||
|
const region_ptr = root.dynamic.memory.find(source) orelse unreachable;
|
||||||
|
const region_copy = region_ptr.*;
|
||||||
|
root.dynamic.memory.remove(source);
|
||||||
|
switch (destination) {
|
||||||
|
.cpu_memory => {
|
||||||
|
// TODO: delete properly
|
||||||
|
const new_ref = root.dynamic.cpu_memory.allocated.append(region_copy) catch |err| {
|
||||||
|
log.err("Error: {}", .{err});
|
||||||
|
return error.OutOfMemory;
|
||||||
|
};
|
||||||
|
// TODO: delete properly
|
||||||
|
|
||||||
|
break :blk @bitCast(new_ref);
|
||||||
|
},
|
||||||
|
.command_buffer_submission, .command_buffer_completion => {
|
||||||
|
switch (destination) {
|
||||||
|
inline .command_buffer_completion, .command_buffer_submission => |dst| @field(root.dynamic, @tagName(dst)).region = region_copy,
|
||||||
|
else => @panic("WTF"),
|
||||||
|
}
|
||||||
|
// TODO: better value
|
||||||
|
break :blk .{ .integer = 0 };
|
||||||
|
},
|
||||||
|
else => @panic("WTF"),
|
||||||
|
}
|
||||||
|
if (true) @panic("TODO: retype");
|
||||||
|
break :blk undefined;
|
||||||
|
},
|
||||||
|
else => @panic(@tagName(command)),
|
||||||
|
},
|
||||||
|
.boot => switch (command) {
|
||||||
|
.get_bundle_size => cpu.bundle.len,
|
||||||
|
.get_bundle_file_list_size => cpu.bundle_files.len,
|
||||||
|
else => @panic(@tagName(command)),
|
||||||
|
},
|
||||||
|
.process => switch (command) {
|
||||||
|
.exit => cpu.shutdown(switch (arguments) {
|
||||||
|
true => .success,
|
||||||
|
false => .failure,
|
||||||
|
}),
|
||||||
|
.panic => cpu.panic("User process panicked with exit code 0x{x}:\n==========\n{s}\n==========", .{ arguments.exit_code, arguments.message }),
|
||||||
|
else => @panic(@tagName(command)),
|
||||||
|
},
|
||||||
|
.page_table => switch (command) {
|
||||||
|
.get => {
|
||||||
|
const descriptor = arguments.descriptor;
|
||||||
|
assert(descriptor.entry_type == .page_table);
|
||||||
|
|
||||||
|
const block = try root.dynamic.page_table.page_tables.getChecked(descriptor.block);
|
||||||
|
const page_table = &block.array[descriptor.index];
|
||||||
|
log.debug("Page table: {}", .{page_table.flags.level});
|
||||||
|
@memcpy(arguments.buffer, &page_table.children);
|
||||||
|
},
|
||||||
|
.get_leaf => {
|
||||||
|
const descriptor = arguments.descriptor;
|
||||||
|
assert(descriptor.entry_type == .leaf);
|
||||||
|
|
||||||
|
const block = try root.dynamic.page_table.leaves.getChecked(descriptor.block);
|
||||||
|
const leaf = &block.array[descriptor.index];
|
||||||
|
|
||||||
|
const user_leaf = arguments.buffer;
|
||||||
|
user_leaf.* = leaf.common;
|
||||||
|
},
|
||||||
|
else => @panic("TODO: page_table other"),
|
||||||
|
},
|
||||||
|
.memory_mapping => {
|
||||||
|
@panic("TODO: memory_mapping");
|
||||||
|
},
|
||||||
|
.page_table_mapping => {
|
||||||
|
@panic("TODO: page_table_mapping");
|
||||||
|
},
|
||||||
|
} else error.forbidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const RootDescriptor = extern struct {
|
||||||
|
value: *Root,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Static = enum {
|
||||||
|
cpu,
|
||||||
|
boot,
|
||||||
|
process,
|
||||||
|
|
||||||
|
pub const count = lib.enumCount(@This());
|
||||||
|
|
||||||
|
pub const Bitmap = @Type(.{
|
||||||
|
.Struct = blk: {
|
||||||
|
const full_bit_size = @max(@as(comptime_int, 1 << 3), @as(u8, @sizeOf(Static)) << 3);
|
||||||
|
break :blk .{
|
||||||
|
.layout = .Packed,
|
||||||
|
.backing_integer = @Type(.{
|
||||||
|
.Int = .{
|
||||||
|
.signedness = .unsigned,
|
||||||
|
.bits = full_bit_size,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
.fields = fields: {
|
||||||
|
var fields: []const lib.Type.StructField = &.{};
|
||||||
|
inline for (lib.enumFields(Static)) |static_field| {
|
||||||
|
fields = fields ++ [1]lib.Type.StructField{.{
|
||||||
|
.name = static_field.name,
|
||||||
|
.type = bool,
|
||||||
|
.default_value = null,
|
||||||
|
.is_comptime = false,
|
||||||
|
.alignment = 0,
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(Static.count > 0);
|
||||||
|
assert(@sizeOf(Static) > 0 or Static.count == 1);
|
||||||
|
|
||||||
|
const padding_type = @Type(.{
|
||||||
|
.Int = .{
|
||||||
|
.signedness = .unsigned,
|
||||||
|
.bits = full_bit_size - Static.count,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
fields = fields ++ [1]lib.Type.StructField{.{
|
||||||
|
.name = "reserved",
|
||||||
|
.type = padding_type,
|
||||||
|
.default_value = &@as(padding_type, 0),
|
||||||
|
.is_comptime = false,
|
||||||
|
.alignment = 0,
|
||||||
|
}};
|
||||||
|
break :fields fields;
|
||||||
|
},
|
||||||
|
.decls = &.{},
|
||||||
|
.is_tuple = false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const CommandBufferMemory = extern struct {
|
||||||
|
region: PhysicalMemoryRegion,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Dynamic = enum {
|
||||||
|
io,
|
||||||
|
memory, // Barrelfish equivalent: Memory (no PhysAddr)
|
||||||
|
cpu_memory, // Barrelfish equivalent: Frame
|
||||||
|
page_table, // Barrelfish equivalent: VNode
|
||||||
|
command_buffer_submission,
|
||||||
|
command_buffer_completion,
|
||||||
|
memory_mapping, // Barrelfish equivalent: Frame mapping, Device Frame Mapping
|
||||||
|
page_table_mapping, // Barrelfish equivalent: VNode mapping
|
||||||
|
// irq_table,
|
||||||
|
// device_memory,
|
||||||
|
// scheduler,
|
||||||
|
|
||||||
|
pub const Map = extern struct {
|
||||||
|
io: IO,
|
||||||
|
memory: Memory,
|
||||||
|
cpu_memory: CPUMemory,
|
||||||
|
page_table: PageTables,
|
||||||
|
command_buffer_submission: CommandBufferMemory,
|
||||||
|
command_buffer_completion: CommandBufferMemory,
|
||||||
|
memory_mapping: Memory.Mapping,
|
||||||
|
page_table_mapping: PageTables.Mapping,
|
||||||
|
|
||||||
|
comptime {
|
||||||
|
inline for (lib.fields(Dynamic.Map), lib.fields(Dynamic)) |struct_field, enum_field| {
|
||||||
|
assert(lib.equal(u8, enum_field.name, struct_field.name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Memory = extern struct {
|
||||||
|
allocated: RegionList = .{},
|
||||||
|
allocate: bool = true,
|
||||||
|
|
||||||
|
pub const Mapping = extern struct {
|
||||||
|
foo: u32 = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const AllocateError = error{
|
||||||
|
OutOfMemory,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn find(memory: *Memory, memory_descriptor: birth.interface.Memory) ?*PhysicalMemoryRegion {
|
||||||
|
var iterator: ?*RegionList = &memory.allocated;
|
||||||
|
var block_index: usize = 0;
|
||||||
|
|
||||||
|
return blk: while (iterator) |list| : ({
|
||||||
|
iterator = list.metadata.next;
|
||||||
|
block_index += 1;
|
||||||
|
}) {
|
||||||
|
if (block_index == memory_descriptor.block) {
|
||||||
|
@panic("TODO: find");
|
||||||
|
// if (memory_descriptor.region < list.metadata.count) {
|
||||||
|
// const region = &list.regions[memory_descriptor.region];
|
||||||
|
// if (region.size != 0 and region.address.value() != 0) {
|
||||||
|
// assert(lib.isAligned(region.size, lib.arch.valid_page_sizes[0]));
|
||||||
|
// assert(lib.isAligned(region.address.value(), lib.arch.valid_page_sizes[0]));
|
||||||
|
// break :blk region;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// break :blk null;
|
||||||
|
} else if (block_index > memory_descriptor.block) {
|
||||||
|
break :blk null;
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else break :blk null;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fn getListIndex(size: usize) usize {
|
||||||
|
inline for (lib.arch.reverse_valid_page_sizes, 0..) |reverse_page_size, reverse_index| {
|
||||||
|
if (size >= reverse_page_size) return reverse_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
@panic("WTF");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn appendRegion(memory: *Memory, region: PhysicalMemoryRegion) !birth.interface.Memory {
|
||||||
|
var iterator: ?*RegionList = &memory.allocated;
|
||||||
|
while (iterator) |region_list| : (iterator = region_list.metadata.next) {
|
||||||
|
const result = region_list.append(region) catch continue;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return error.OutOfMemory;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(memory: *Memory, ref: birth.interface.Memory) void {
|
||||||
|
const region_index: u6 = @intCast(ref.region);
|
||||||
|
var block_index: u32 = 0;
|
||||||
|
var iterator: ?*RegionList = &memory.allocated;
|
||||||
|
while (iterator) |region_list| : ({
|
||||||
|
iterator = region_list.metadata.next;
|
||||||
|
block_index += 1;
|
||||||
|
}) {
|
||||||
|
if (block_index == ref.block) {
|
||||||
|
region_list.remove(region_index);
|
||||||
|
break;
|
||||||
|
} else if (block_index > ref.block) {
|
||||||
|
@panic("WTF");
|
||||||
|
} else continue;
|
||||||
|
} else {
|
||||||
|
@panic("WTF");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const CPUMemory = extern struct {
|
||||||
|
allocated: RegionList = .{},
|
||||||
|
flags: Flags = .{},
|
||||||
|
|
||||||
|
const Flags = packed struct(u64) {
|
||||||
|
allocate: bool = true,
|
||||||
|
reserved: u63 = 0,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const PageTable = extern struct {
|
||||||
|
region: PhysicalMemoryRegion,
|
||||||
|
mapping: VirtualAddress,
|
||||||
|
flags: Flags,
|
||||||
|
children: Children = .{.{}} ** children_count,
|
||||||
|
|
||||||
|
pub const Children = [children_count]birth.interface.PageTable;
|
||||||
|
pub const children_count = paging.page_table_entry_count;
|
||||||
|
|
||||||
|
pub const Flags = packed struct(u64) {
|
||||||
|
level: paging.Level,
|
||||||
|
reserved: u62 = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Array = extern struct {
|
||||||
|
array: [count]PageTable,
|
||||||
|
bitset: Bitset,
|
||||||
|
next: ?*Array = null,
|
||||||
|
|
||||||
|
pub const Bitset = lib.data_structures.BitsetU64(count);
|
||||||
|
|
||||||
|
pub const count = 32;
|
||||||
|
|
||||||
|
pub fn get(array: *Array, index: u6) !*PageTable {
|
||||||
|
if (array.bitset.isSet(index)) {
|
||||||
|
return &array.array[index];
|
||||||
|
} else {
|
||||||
|
return error.index_out_of_bounds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Leaf = extern struct {
|
||||||
|
common: birth.interface.Leaf,
|
||||||
|
physical: PhysicalAddress,
|
||||||
|
flags: Flags,
|
||||||
|
|
||||||
|
pub const Flags = packed struct(u64) {
|
||||||
|
size: Size,
|
||||||
|
reserved: u62 = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Size = enum(u2) {
|
||||||
|
@"4KB",
|
||||||
|
@"2MB",
|
||||||
|
@"1GB",
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Array = extern struct {
|
||||||
|
array: [count]Leaf,
|
||||||
|
bitset: Bitset,
|
||||||
|
next: ?*Array = null,
|
||||||
|
pub const Bitset = lib.data_structures.BitsetU64(count);
|
||||||
|
pub const count = 32;
|
||||||
|
pub fn get(array: *Array, index: u6) !*PageTable {
|
||||||
|
if (array.bitset.isSet(index)) {
|
||||||
|
return &array.array[index];
|
||||||
|
} else {
|
||||||
|
return error.index_out_of_bounds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const PageTables = extern struct {
|
||||||
|
// This one has the kernel mapped
|
||||||
|
privileged: PageTable, // This one is separate as cannot be mapped
|
||||||
|
user: birth.interface.PageTable,
|
||||||
|
page_tables: SparseArray(*PageTable.Array),
|
||||||
|
leaves: SparseArray(*Leaf.Array),
|
||||||
|
// vmm: VMM,
|
||||||
|
can_map_page_tables: bool,
|
||||||
|
|
||||||
|
pub const Mapping = extern struct {
|
||||||
|
foo: u32 = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const end = privileged.arch.paging.user_address_space_end;
|
||||||
|
|
||||||
|
fn getUser(page_tables: *const PageTables) ?PhysicalMemoryRegion {
|
||||||
|
if (page_tables.user.address.value() == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (page_tables.user.size == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return page_tables.user;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn switchPrivileged(page_tables: *const PageTables) void {
|
||||||
|
paging.Specific.fromPhysicalRegion(page_tables.privileged.region).makeCurrentPrivileged();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn appendPageTable(page_tables: *PageTables, allocator: *Allocator, page_table: PageTable) !birth.interface.PageTable {
|
||||||
|
if (page_tables.page_tables.len > 0) {
|
||||||
|
const slice = page_tables.page_tables.ptr[0..page_tables.page_tables.len];
|
||||||
|
for (slice, 0..) |block, block_index| {
|
||||||
|
const index = block.bitset.allocate() catch continue;
|
||||||
|
block.array[index] = page_table;
|
||||||
|
return .{
|
||||||
|
.index = index,
|
||||||
|
.block = @intCast(block_index),
|
||||||
|
.entry_type = .page_table,
|
||||||
|
.present = true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const page_table_array = try allocator.create(PageTable.Array);
|
||||||
|
_ = try page_tables.page_tables.append(allocator, page_table_array);
|
||||||
|
return appendPageTable(page_tables, allocator, page_table);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn appendLeaf(page_tables: *PageTables, allocator: *Allocator, leaf: Leaf) !birth.interface.PageTable {
|
||||||
|
if (page_tables.leaves.len > 0) {
|
||||||
|
const slice = page_tables.leaves.ptr[0..page_tables.leaves.len];
|
||||||
|
for (slice, 0..) |block, block_index| {
|
||||||
|
const index = block.bitset.allocate() catch continue;
|
||||||
|
block.array[index] = leaf;
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.index = index,
|
||||||
|
.block = @intCast(block_index),
|
||||||
|
.entry_type = .leaf,
|
||||||
|
.present = true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const leaf_array = try allocator.create(Leaf.Array);
|
||||||
|
_ = try page_tables.leaves.append(allocator, leaf_array);
|
||||||
|
return appendLeaf(page_tables, allocator, leaf);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getPageTable(page_tables: *PageTables, page_table: birth.interface.PageTable) !*PageTable {
|
||||||
|
assert(page_table.entry_type == .page_table);
|
||||||
|
if (page_table.present) {
|
||||||
|
const page_table_block = try page_tables.page_tables.getChecked(page_table.block);
|
||||||
|
const result = try page_table_block.get(@intCast(page_table.index));
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
return error.not_present;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const IO = extern struct {
|
||||||
|
debug: bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Scheduler = extern struct {
|
||||||
|
memory: PhysicalMemoryRegion,
|
||||||
|
};
|
||||||
|
|
||||||
|
comptime {
|
||||||
|
const dynamic_count = enumCount(Dynamic);
|
||||||
|
const static_count = enumCount(Static);
|
||||||
|
const total_count = enumCount(birth.interface.Capability);
|
||||||
|
assert(dynamic_count + static_count == total_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const Root = extern struct {
|
||||||
|
static: Static.Bitmap,
|
||||||
|
dynamic: Dynamic.Map,
|
||||||
|
scheduler: Scheduler,
|
||||||
|
heap: Heap = .{},
|
||||||
|
padding: [padding_byte_count]u8 = .{0} ** padding_byte_count,
|
||||||
|
|
||||||
|
const Heap = cpu.HeapImplementation(true);
|
||||||
|
|
||||||
|
const max_alignment = @max(@alignOf(Static.Bitmap), @alignOf(Dynamic.Map), @alignOf(Scheduler), @alignOf(Heap));
|
||||||
|
const total_size = lib.alignForward(usize, @sizeOf(Static.Bitmap) + @sizeOf(Dynamic.Map) + @sizeOf(Scheduler) + @sizeOf(Heap), max_alignment);
|
||||||
|
const page_aligned_size = lib.alignForward(usize, total_size, lib.arch.valid_page_sizes[0]);
|
||||||
|
const padding_byte_count = page_aligned_size - total_size;
|
||||||
|
|
||||||
|
comptime {
|
||||||
|
assert(@sizeOf(Root) % lib.arch.valid_page_sizes[0] == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const AllocateError = error{
|
||||||
|
OutOfMemory,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn hasPermissions(root: *Root, comptime Descriptor: type, arguments: Descriptor.Arguments) bool {
|
||||||
|
const capability = Descriptor.Capability;
|
||||||
|
const command = Descriptor.Command;
|
||||||
|
|
||||||
|
if (command == .retype) {
|
||||||
|
const can_retype: bool = switch (@TypeOf(arguments)) {
|
||||||
|
void => @panic("Retype on void"),
|
||||||
|
else => switch (arguments.destination) {
|
||||||
|
inline else => |destination| blk: {
|
||||||
|
const child_types = comptime capability.getChildTypes();
|
||||||
|
inline for (child_types) |child_type| {
|
||||||
|
if (child_type == destination) {
|
||||||
|
break :blk true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break :blk false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!can_retype) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const has_permissions = switch (capability) {
|
||||||
|
// static capabilities
|
||||||
|
inline .cpu,
|
||||||
|
.boot,
|
||||||
|
=> |static_capability| @field(root.static, @tagName(static_capability)),
|
||||||
|
.process => root.static.process or command == .panic,
|
||||||
|
// dynamic capabilities
|
||||||
|
.io => switch (command) {
|
||||||
|
.copy, .mint, .retype, .delete, .revoke, .create => unreachable,
|
||||||
|
.log => root.dynamic.io.debug,
|
||||||
|
},
|
||||||
|
.cpu_memory => root.dynamic.cpu_memory.flags.allocate,
|
||||||
|
.command_buffer_completion, .command_buffer_submission => true, //TODO
|
||||||
|
.memory => switch (command) {
|
||||||
|
.allocate => root.dynamic.memory.allocate,
|
||||||
|
.retype => root.dynamic.memory.find(arguments.source) != null,
|
||||||
|
else => @panic("TODO: else => memory"),
|
||||||
|
},
|
||||||
|
.page_table => root.dynamic.page_table.can_map_page_tables, // TODO
|
||||||
|
.memory_mapping => true, // TODO
|
||||||
|
.page_table_mapping => true, // TODO
|
||||||
|
};
|
||||||
|
|
||||||
|
return has_permissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const AllocateMemoryResult = extern struct {
|
||||||
|
region: PhysicalMemoryRegion,
|
||||||
|
reference: birth.interface.Memory,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn allocateMemory(root: *Root, size: usize) !AllocateMemoryResult {
|
||||||
|
const physical_region = try cpu.page_allocator.allocate(size, .{ .reason = .user });
|
||||||
|
const reference = try root.dynamic.memory.appendRegion(physical_region);
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.region = physical_region,
|
||||||
|
.reference = reference,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
@ -7,11 +7,8 @@ const stopCPU = privileged.arch.stopCPU;
|
|||||||
|
|
||||||
const cpu = @import("cpu");
|
const cpu = @import("cpu");
|
||||||
|
|
||||||
var lock: lib.Spinlock = .released;
|
|
||||||
|
|
||||||
pub const std_options = struct {
|
pub const std_options = struct {
|
||||||
pub fn logFn(comptime level: lib.log.Level, comptime scope: @TypeOf(.EnumLiteral), comptime format: []const u8, args: anytype) void {
|
pub fn logFn(comptime level: lib.log.Level, comptime scope: @TypeOf(.EnumLiteral), comptime format: []const u8, args: anytype) void {
|
||||||
lock.acquire();
|
|
||||||
cpu.writer.writeAll("[CPU DRIVER] ") catch unreachable;
|
cpu.writer.writeAll("[CPU DRIVER] ") catch unreachable;
|
||||||
cpu.writer.writeByte('[') catch unreachable;
|
cpu.writer.writeByte('[') catch unreachable;
|
||||||
cpu.writer.writeAll(@tagName(scope)) catch unreachable;
|
cpu.writer.writeAll(@tagName(scope)) catch unreachable;
|
||||||
@ -21,8 +18,6 @@ pub const std_options = struct {
|
|||||||
cpu.writer.writeAll("] ") catch unreachable;
|
cpu.writer.writeAll("] ") catch unreachable;
|
||||||
lib.format(cpu.writer, format, args) catch unreachable;
|
lib.format(cpu.writer, format, args) catch unreachable;
|
||||||
cpu.writer.writeByte('\n') catch unreachable;
|
cpu.writer.writeByte('\n') catch unreachable;
|
||||||
|
|
||||||
lock.release();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const log_level = lib.log.Level.debug;
|
pub const log_level = lib.log.Level.debug;
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
const lib = @import("lib");
|
|
||||||
const assert = lib.assert;
|
|
||||||
const log = lib.log.scoped(.TEST);
|
|
||||||
const privileged = @import("privileged");
|
|
||||||
const QEMU = lib.QEMU;
|
|
||||||
|
|
||||||
const cpu = @import("cpu");
|
|
||||||
|
|
||||||
const RunAllTestResult = error{
|
|
||||||
failure,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn runAllTests() RunAllTestResult!void {
|
|
||||||
comptime assert(lib.is_test);
|
|
||||||
const test_functions = @import("builtin").test_functions;
|
|
||||||
var failed_test_count: usize = 0;
|
|
||||||
for (test_functions) |test_function| {
|
|
||||||
test_function.func() catch |err| {
|
|
||||||
log.err("Test failed: {}", .{err});
|
|
||||||
failed_test_count += 1;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const test_count = test_functions.len;
|
|
||||||
assert(QEMU.isa_debug_exit.io_size == @sizeOf(u32));
|
|
||||||
const exit_code = switch (failed_test_count) {
|
|
||||||
0 => blk: {
|
|
||||||
log.info("All {} tests passed.", .{test_count});
|
|
||||||
break :blk .success;
|
|
||||||
},
|
|
||||||
else => blk: {
|
|
||||||
log.info("Run {} tests. Failed {}.", .{ test_count, failed_test_count });
|
|
||||||
break :blk .failure;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
cpu.shutdown(exit_code);
|
|
||||||
}
|
|
@ -251,12 +251,20 @@ pub fn main() anyerror!void {
|
|||||||
if (arguments.log) |log_configuration| {
|
if (arguments.log) |log_configuration| {
|
||||||
var log_what = host.ArrayList(u8).init(wrapped_allocator.zigUnwrap());
|
var log_what = host.ArrayList(u8).init(wrapped_allocator.zigUnwrap());
|
||||||
|
|
||||||
if (log_configuration.guest_errors) try log_what.appendSlice("guest_errors,");
|
if (log_configuration.guest_errors) {
|
||||||
if (log_configuration.interrupts) try log_what.appendSlice("int,");
|
try log_what.appendSlice("guest_errors,");
|
||||||
if (!arguments_result.ci and log_configuration.assembly) try log_what.appendSlice("in_asm,");
|
}
|
||||||
|
|
||||||
|
if (log_configuration.interrupts) {
|
||||||
|
try log_what.appendSlice("int,");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!arguments_result.ci and log_configuration.assembly) {
|
||||||
|
try log_what.appendSlice("in_asm,");
|
||||||
|
}
|
||||||
|
|
||||||
if (log_what.items.len > 0) {
|
if (log_what.items.len > 0) {
|
||||||
// Delete the last comma
|
//Delete the last comma
|
||||||
_ = log_what.pop();
|
_ = log_what.pop();
|
||||||
|
|
||||||
try argument_list.append("-d");
|
try argument_list.append("-d");
|
||||||
@ -284,7 +292,7 @@ pub fn main() anyerror!void {
|
|||||||
// GF2, when not found in the PATH, can give problems
|
// GF2, when not found in the PATH, can give problems
|
||||||
const use_gf = switch (lib.os) {
|
const use_gf = switch (lib.os) {
|
||||||
.macos => false,
|
.macos => false,
|
||||||
.linux => false,
|
.linux => true,
|
||||||
else => false,
|
else => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -292,7 +300,8 @@ pub fn main() anyerror!void {
|
|||||||
if (use_gf) {
|
if (use_gf) {
|
||||||
try command_line_gdb.append("gf2");
|
try command_line_gdb.append("gf2");
|
||||||
} else {
|
} else {
|
||||||
try command_line_gdb.append("kitty");
|
const terminal_emulator = "foot";
|
||||||
|
try command_line_gdb.append(terminal_emulator);
|
||||||
try command_line_gdb.append(switch (lib.os) {
|
try command_line_gdb.append(switch (lib.os) {
|
||||||
.linux => "gdb",
|
.linux => "gdb",
|
||||||
.macos => "x86_64-elf-gdb",
|
.macos => "x86_64-elf-gdb",
|
||||||
@ -331,12 +340,12 @@ pub fn main() anyerror!void {
|
|||||||
try debugger_process.spawn();
|
try debugger_process.spawn();
|
||||||
}
|
}
|
||||||
|
|
||||||
var process = host.ChildProcess.init(argument_list.items, wrapped_allocator.zigUnwrap());
|
var emulator_process = host.ChildProcess.init(argument_list.items, wrapped_allocator.zigUnwrap());
|
||||||
//process.stdout_behavior = .I;
|
//process.stdout_behavior = .I;
|
||||||
const result = try process.spawnAndWait();
|
const emulator_process_result = try emulator_process.spawnAndWait();
|
||||||
|
|
||||||
if (result == .Exited) {
|
if (emulator_process_result == .Exited) {
|
||||||
const exit_code = result.Exited;
|
const exit_code = emulator_process_result.Exited;
|
||||||
if (exit_code & 1 != 0) {
|
if (exit_code & 1 != 0) {
|
||||||
const mask = lib.maxInt(@TypeOf(exit_code)) - 1;
|
const mask = lib.maxInt(@TypeOf(exit_code)) - 1;
|
||||||
const masked_exit_code = exit_code & mask;
|
const masked_exit_code = exit_code & mask;
|
||||||
@ -354,7 +363,7 @@ pub fn main() anyerror!void {
|
|||||||
} else log.err("QEMU exited with unexpected code: {}. Masked: {}", .{ exit_code, masked_exit_code });
|
} else log.err("QEMU exited with unexpected code: {}. Masked: {}", .{ exit_code, masked_exit_code });
|
||||||
} else log.err("QEMU exited with unexpected code: {}", .{exit_code});
|
} else log.err("QEMU exited with unexpected code: {}", .{exit_code});
|
||||||
} else {
|
} else {
|
||||||
log.err("QEMU was {s}", .{@tagName(result)});
|
log.err("QEMU was {s}", .{@tagName(emulator_process_result)});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (debugcon_file_used) {
|
if (debugcon_file_used) {
|
||||||
|
579
src/lib.zig
579
src/lib.zig
@ -1,6 +1,270 @@
|
|||||||
const common = @import("common.zig");
|
const common = @import("common.zig");
|
||||||
pub usingnamespace common;
|
pub usingnamespace common;
|
||||||
|
|
||||||
|
const compiler_builtin = @import("builtin");
|
||||||
|
pub const cpu = compiler_builtin.cpu;
|
||||||
|
pub const os = compiler_builtin.os.tag;
|
||||||
|
pub const build_mode = compiler_builtin.mode;
|
||||||
|
pub const is_test = compiler_builtin.is_test;
|
||||||
|
|
||||||
|
pub const kb = 1024;
|
||||||
|
pub const mb = kb * 1024;
|
||||||
|
pub const gb = mb * 1024;
|
||||||
|
pub const tb = gb * 1024;
|
||||||
|
|
||||||
|
pub const SizeUnit = enum(u64) {
|
||||||
|
byte = 1,
|
||||||
|
kilobyte = 1024,
|
||||||
|
megabyte = 1024 * 1024,
|
||||||
|
gigabyte = 1024 * 1024 * 1024,
|
||||||
|
terabyte = 1024 * 1024 * 1024 * 1024,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const std = @import("std");
|
||||||
|
pub const Target = std.Target;
|
||||||
|
pub const Cpu = Target.Cpu;
|
||||||
|
pub const CrossTarget = std.zig.CrossTarget;
|
||||||
|
|
||||||
|
pub const log = std.log;
|
||||||
|
|
||||||
|
pub const data_structures = @import("lib/data_structures.zig");
|
||||||
|
|
||||||
|
pub const Atomic = std.atomic.Atomic;
|
||||||
|
|
||||||
|
pub const Reader = std.io.Reader;
|
||||||
|
pub const Writer = std.io.Writer;
|
||||||
|
|
||||||
|
pub const FixedBufferStream = std.io.FixedBufferStream;
|
||||||
|
pub const fixedBufferStream = std.io.fixedBufferStream;
|
||||||
|
|
||||||
|
pub fn assert(ok: bool) void {
|
||||||
|
if (!ok) {
|
||||||
|
if (@inComptime()) {
|
||||||
|
@compileError("Assert failed!");
|
||||||
|
} else {
|
||||||
|
@panic("Assert failed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const deflate = std.compress.deflate;
|
||||||
|
|
||||||
|
const debug = std.debug;
|
||||||
|
pub const print = debug.print;
|
||||||
|
pub const StackIterator = debug.StackIterator;
|
||||||
|
pub const dwarf = std.dwarf;
|
||||||
|
pub const ModuleDebugInfo = std.debug.ModuleDebugInfo;
|
||||||
|
|
||||||
|
pub const elf = std.elf;
|
||||||
|
|
||||||
|
const fmt = std.fmt;
|
||||||
|
pub const format = std.fmt.format;
|
||||||
|
pub const FormatOptions = fmt.FormatOptions;
|
||||||
|
pub const bufPrint = fmt.bufPrint;
|
||||||
|
pub const allocPrint = fmt.allocPrint;
|
||||||
|
pub const comptimePrint = fmt.comptimePrint;
|
||||||
|
pub const parseUnsigned = fmt.parseUnsigned;
|
||||||
|
|
||||||
|
const heap = std.heap;
|
||||||
|
pub const FixedBufferAllocator = heap.FixedBufferAllocator;
|
||||||
|
|
||||||
|
pub const json = std.json;
|
||||||
|
|
||||||
|
const mem = std.mem;
|
||||||
|
pub const ZigAllocator = mem.Allocator;
|
||||||
|
pub const equal = mem.eql;
|
||||||
|
pub const length = mem.len;
|
||||||
|
pub const startsWith = mem.startsWith;
|
||||||
|
pub const endsWith = mem.endsWith;
|
||||||
|
pub const indexOf = mem.indexOf;
|
||||||
|
// Ideal for small inputs
|
||||||
|
pub const indexOfPosLinear = mem.indexOfPosLinear;
|
||||||
|
pub const lastIndexOf = mem.lastIndexOf;
|
||||||
|
pub const asBytes = mem.asBytes;
|
||||||
|
pub const readIntBig = mem.readIntBig;
|
||||||
|
pub const readIntSliceBig = mem.readIntSliceBig;
|
||||||
|
pub const concat = mem.concat;
|
||||||
|
pub const sliceAsBytes = mem.sliceAsBytes;
|
||||||
|
pub const bytesAsSlice = mem.bytesAsSlice;
|
||||||
|
pub const alignForward = mem.alignForward;
|
||||||
|
pub const alignBackward = mem.alignBackward;
|
||||||
|
pub const isAligned = mem.isAligned;
|
||||||
|
pub const isAlignedGeneric = mem.isAlignedGeneric;
|
||||||
|
pub const reverse = mem.reverse;
|
||||||
|
pub const tokenize = mem.tokenize;
|
||||||
|
pub const containsAtLeast = mem.containsAtLeast;
|
||||||
|
pub const sliceTo = mem.sliceTo;
|
||||||
|
pub const swap = mem.swap;
|
||||||
|
|
||||||
|
pub const random = std.rand;
|
||||||
|
|
||||||
|
pub const testing = std.testing;
|
||||||
|
|
||||||
|
pub const sort = std.sort;
|
||||||
|
|
||||||
|
pub fn fieldSize(comptime T: type, field_name: []const u8) comptime_int {
|
||||||
|
var foo: T = undefined;
|
||||||
|
return @sizeOf(@TypeOf(@field(foo, field_name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
const DiffError = error{
|
||||||
|
diff,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn diff(file1: []const u8, file2: []const u8) !void {
|
||||||
|
assert(file1.len == file2.len);
|
||||||
|
var different_bytes: u64 = 0;
|
||||||
|
for (file1, 0..) |byte1, index| {
|
||||||
|
const byte2 = file2[index];
|
||||||
|
const is_different_byte = byte1 != byte2;
|
||||||
|
different_bytes += @intFromBool(is_different_byte);
|
||||||
|
if (is_different_byte) {
|
||||||
|
log.debug("Byte [0x{x}] is different: 0x{x} != 0x{x}", .{ index, byte1, byte2 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (different_bytes != 0) {
|
||||||
|
log.debug("Total different bytes: 0x{x}", .{different_bytes});
|
||||||
|
return DiffError.diff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn zeroes(comptime T: type) T {
|
||||||
|
var result: T = undefined;
|
||||||
|
const slice = asBytes(&result);
|
||||||
|
@memset(slice, 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ascii = std.ascii;
|
||||||
|
pub const upperString = ascii.upperString;
|
||||||
|
pub const isUpper = ascii.isUpper;
|
||||||
|
pub const isAlphabetic = ascii.isAlphabetic;
|
||||||
|
|
||||||
|
const std_builtin = std.builtin;
|
||||||
|
pub const AtomicRmwOp = std_builtin.AtomicRmwOp;
|
||||||
|
pub const AtomicOrder = std_builtin.AtomicOrder;
|
||||||
|
pub const Type = std_builtin.Type;
|
||||||
|
pub const StackTrace = std_builtin.StackTrace;
|
||||||
|
pub const SourceLocation = std_builtin.SourceLocation;
|
||||||
|
|
||||||
|
pub fn FieldType(comptime T: type, comptime name: []const u8) type {
|
||||||
|
return @TypeOf(@field(@as(T, undefined), name));
|
||||||
|
}
|
||||||
|
|
||||||
|
// META PROGRAMMING
|
||||||
|
pub const AutoEnumArray = std.enums.EnumArray;
|
||||||
|
pub const fields = std.meta.fields;
|
||||||
|
pub const enumFromInt = std.meta.enumFromInt;
|
||||||
|
pub const stringToEnum = std.meta.stringToEnum;
|
||||||
|
pub const Tag = std.meta.Tag;
|
||||||
|
|
||||||
|
const math = std.math;
|
||||||
|
pub const maxInt = math.maxInt;
|
||||||
|
pub const min = math.min;
|
||||||
|
pub const divCeil = math.divCeil;
|
||||||
|
pub const clamp = math.clamp;
|
||||||
|
pub const isPowerOfTwo = math.isPowerOfTwo;
|
||||||
|
pub const mul = math.mul;
|
||||||
|
pub const cast = math.cast;
|
||||||
|
|
||||||
|
pub const unicode = std.unicode;
|
||||||
|
|
||||||
|
pub const uefi = std.os.uefi;
|
||||||
|
|
||||||
|
pub const DiskType = enum(u32) {
|
||||||
|
virtio = 0,
|
||||||
|
nvme = 1,
|
||||||
|
ahci = 2,
|
||||||
|
ide = 3,
|
||||||
|
memory = 4,
|
||||||
|
bios = 5,
|
||||||
|
|
||||||
|
pub const count = enumCount(@This());
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn enumFields(comptime E: type) []const Type.EnumField {
|
||||||
|
return @typeInfo(E).Enum.fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const enumValues = std.enums.values;
|
||||||
|
|
||||||
|
pub fn enumCount(comptime E: type) usize {
|
||||||
|
return enumFields(E).len;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const QEMU = extern struct {
|
||||||
|
pub const isa_debug_exit = ISADebugExit{};
|
||||||
|
|
||||||
|
pub const ISADebugExit = extern struct {
|
||||||
|
io_base: u8 = 0xf4,
|
||||||
|
io_size: u8 = @sizeOf(u32),
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const ExitCode = enum(u32) {
|
||||||
|
success = 0x10,
|
||||||
|
failure = 0x11,
|
||||||
|
_,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const OptimizeMode = std.builtin.OptimizeMode;
|
||||||
|
|
||||||
|
pub const Suffix = enum {
|
||||||
|
bootloader,
|
||||||
|
cpu_driver,
|
||||||
|
image,
|
||||||
|
complete,
|
||||||
|
|
||||||
|
pub fn fromConfiguration(suffix: Suffix, allocator: ZigAllocator, configuration: common.Configuration, prefix: ?[]const u8) ![]const u8 {
|
||||||
|
const cpu_driver_suffix = [_][]const u8{
|
||||||
|
@tagName(configuration.optimize_mode),
|
||||||
|
"_",
|
||||||
|
@tagName(configuration.architecture),
|
||||||
|
"_",
|
||||||
|
@tagName(configuration.executable_kind),
|
||||||
|
};
|
||||||
|
|
||||||
|
const bootloader_suffix = [_][]const u8{
|
||||||
|
@tagName(configuration.architecture),
|
||||||
|
"_",
|
||||||
|
@tagName(configuration.bootloader),
|
||||||
|
"_",
|
||||||
|
@tagName(configuration.boot_protocol),
|
||||||
|
};
|
||||||
|
|
||||||
|
const image_suffix = [_][]const u8{
|
||||||
|
@tagName(configuration.optimize_mode),
|
||||||
|
"_",
|
||||||
|
} ++ bootloader_suffix ++ [_][]const u8{
|
||||||
|
"_",
|
||||||
|
@tagName(configuration.executable_kind),
|
||||||
|
};
|
||||||
|
|
||||||
|
const complete_suffix = image_suffix ++ [_][]const u8{
|
||||||
|
"_",
|
||||||
|
@tagName(configuration.execution_type),
|
||||||
|
"_",
|
||||||
|
@tagName(configuration.execution_environment),
|
||||||
|
};
|
||||||
|
|
||||||
|
return try std.mem.concat(allocator, u8, &switch (suffix) {
|
||||||
|
.cpu_driver => if (prefix) |pf| [1][]const u8{pf} ++ cpu_driver_suffix else cpu_driver_suffix,
|
||||||
|
.bootloader => if (prefix) |pf| [1][]const u8{pf} ++ bootloader_suffix else bootloader_suffix,
|
||||||
|
.image => if (prefix) |pf| [1][]const u8{pf} ++ image_suffix else image_suffix,
|
||||||
|
.complete => if (prefix) |pf| [1][]const u8{pf} ++ complete_suffix else complete_suffix,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const default_cpu_name = "/cpu";
|
||||||
|
pub const default_init_file = "/init";
|
||||||
|
|
||||||
|
pub const default_disk_size = 64 * 1024 * 1024;
|
||||||
|
pub const default_sector_size = 0x200;
|
||||||
|
|
||||||
|
pub const cache_line_size = 64;
|
||||||
|
|
||||||
pub const arch = @import("lib/arch.zig");
|
pub const arch = @import("lib/arch.zig");
|
||||||
/// This is done so the allocator can respect allocating from different address spaces
|
/// This is done so the allocator can respect allocating from different address spaces
|
||||||
pub const config = @import("lib/config.zig");
|
pub const config = @import("lib/config.zig");
|
||||||
@ -17,10 +281,9 @@ const extern_enum_array = @import("lib/extern_enum_array.zig");
|
|||||||
pub const EnumArray = extern_enum_array.EnumArray;
|
pub const EnumArray = extern_enum_array.EnumArray;
|
||||||
|
|
||||||
pub fn memcpy(noalias destination: []u8, noalias source: []const u8) void {
|
pub fn memcpy(noalias destination: []u8, noalias source: []const u8) void {
|
||||||
@setRuntimeSafety(false);
|
|
||||||
// Using this as the Zig implementation is really slow (at least in x86 with soft_float enabled
|
// Using this as the Zig implementation is really slow (at least in x86 with soft_float enabled
|
||||||
// if (common.cpu.arch == .x86 or common.cpu.arch == .x86_64 and common.Target.x86.featureSetHas(common.cpu.features, .soft_float)) {
|
// if (cpu.arch == .x86 or cpu.arch == .x86_64 and Target.x86.featureSetHas(cpu.features, .soft_float)) {
|
||||||
const bytes_left = switch (common.cpu.arch) {
|
const bytes_left = switch (cpu.arch) {
|
||||||
.x86 => asm volatile (
|
.x86 => asm volatile (
|
||||||
\\rep movsb
|
\\rep movsb
|
||||||
: [ret] "={ecx}" (-> usize),
|
: [ret] "={ecx}" (-> usize),
|
||||||
@ -38,46 +301,16 @@ pub fn memcpy(noalias destination: []u8, noalias source: []const u8) void {
|
|||||||
else => @compileError("Unreachable"),
|
else => @compileError("Unreachable"),
|
||||||
};
|
};
|
||||||
|
|
||||||
common.assert(bytes_left == 0);
|
assert(bytes_left == 0);
|
||||||
// } else {
|
|
||||||
// @memcpy(destination, source);
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn memset(comptime T: type, slice: []T, elem: T) void {
|
|
||||||
// @setRuntimeSafety(false);
|
|
||||||
//
|
|
||||||
// const bytes_left = switch (T) {
|
|
||||||
// u8 => switch (common.cpu.arch) {
|
|
||||||
// .x86 => asm volatile (
|
|
||||||
// \\rep stosb
|
|
||||||
// : [ret] "={ecx}" (-> usize),
|
|
||||||
// : [slice] "{edi}" (slice.ptr),
|
|
||||||
// [len] "{ecx}" (slice.len),
|
|
||||||
// [element] "{al}" (elem),
|
|
||||||
// ),
|
|
||||||
// .x86_64 => asm volatile (
|
|
||||||
// \\rep movsb
|
|
||||||
// : [ret] "={rcx}" (-> usize),
|
|
||||||
// : [slice] "{rdi}" (slice.ptr),
|
|
||||||
// [len] "{rcx}" (slice.len),
|
|
||||||
// [element] "{al}" (elem),
|
|
||||||
// ),
|
|
||||||
// else => @compileError("Unsupported OS"),
|
|
||||||
// },
|
|
||||||
// else => @compileError("Type " ++ @typeName(T) ++ " not supported"),
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// common.assert(bytes_left == 0);
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub fn EnumStruct(comptime Enum: type, comptime Value: type) type {
|
pub fn EnumStruct(comptime Enum: type, comptime Value: type) type {
|
||||||
const EnumFields = common.enumFields(Enum);
|
const EnumFields = enumFields(Enum);
|
||||||
const MyEnumStruct = @Type(.{
|
const MyEnumStruct = @Type(.{
|
||||||
.Struct = .{
|
.Struct = .{
|
||||||
.layout = .Extern,
|
.layout = .Extern,
|
||||||
.fields = &blk: {
|
.fields = &blk: {
|
||||||
var arr = [1]common.Type.StructField{undefined} ** EnumFields.len;
|
var arr = [1]Type.StructField{undefined} ** EnumFields.len;
|
||||||
inline for (EnumFields) |EnumValue| {
|
inline for (EnumFields) |EnumValue| {
|
||||||
arr[EnumValue.value] = .{
|
arr[EnumValue.value] = .{
|
||||||
.name = EnumValue.name,
|
.name = EnumValue.name,
|
||||||
@ -102,8 +335,8 @@ pub fn EnumStruct(comptime Enum: type, comptime Value: type) type {
|
|||||||
pub const Array = MyEnumArray;
|
pub const Array = MyEnumArray;
|
||||||
};
|
};
|
||||||
|
|
||||||
common.assert(@sizeOf(Union.Struct) == @sizeOf(Union.Array));
|
assert(@sizeOf(Union.Struct) == @sizeOf(Union.Array));
|
||||||
common.assert(@sizeOf(Union.Array) == @sizeOf(Union));
|
assert(@sizeOf(Union.Array) == @sizeOf(Union));
|
||||||
|
|
||||||
return Union;
|
return Union;
|
||||||
}
|
}
|
||||||
@ -115,7 +348,7 @@ pub const DirectoryTokenizer = struct {
|
|||||||
total_count: usize,
|
total_count: usize,
|
||||||
|
|
||||||
pub fn init(string: []const u8) DirectoryTokenizer {
|
pub fn init(string: []const u8) DirectoryTokenizer {
|
||||||
common.assert(string.len > 0);
|
assert(string.len > 0);
|
||||||
var count: usize = 0;
|
var count: usize = 0;
|
||||||
|
|
||||||
if (string[0] == '/') {
|
if (string[0] == '/') {
|
||||||
@ -153,8 +386,8 @@ pub const DirectoryTokenizer = struct {
|
|||||||
|
|
||||||
return tokenizer.string[original_index..];
|
return tokenizer.string[original_index..];
|
||||||
} else {
|
} else {
|
||||||
common.assert(original_index == tokenizer.string.len);
|
assert(original_index == tokenizer.string.len);
|
||||||
common.assert(tokenizer.given_count == tokenizer.total_count);
|
assert(tokenizer.given_count == tokenizer.total_count);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -165,8 +398,8 @@ pub const DirectoryTokenizer = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "directory tokenizer" {
|
test "directory tokenizer" {
|
||||||
common.log.err("ajskdjsa", .{});
|
log.err("ajskdjsa", .{});
|
||||||
if (common.os != .freestanding) {
|
if (os != .freestanding) {
|
||||||
const TestCase = struct {
|
const TestCase = struct {
|
||||||
path: []const u8,
|
path: []const u8,
|
||||||
expected_result: []const []const u8,
|
expected_result: []const []const u8,
|
||||||
@ -183,13 +416,13 @@ pub const DirectoryTokenizer = struct {
|
|||||||
var result_count: usize = 0;
|
var result_count: usize = 0;
|
||||||
|
|
||||||
while (dir_tokenizer.next()) |dir| {
|
while (dir_tokenizer.next()) |dir| {
|
||||||
try common.testing.expect(result_count < results.len);
|
try testing.expect(result_count < results.len);
|
||||||
try common.testing.expectEqualStrings(case.expected_result[result_count], dir);
|
try testing.expectEqualStrings(case.expected_result[result_count], dir);
|
||||||
results[result_count] = dir;
|
results[result_count] = dir;
|
||||||
result_count += 1;
|
result_count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
try common.testing.expectEqual(case.expected_result.len, result_count);
|
try testing.expectEqual(case.expected_result.len, result_count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -212,7 +445,7 @@ pub inline fn maybePtrSub(comptime T: type, ptr: ?*T, element_offset: usize) ?*T
|
|||||||
}
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
common.log.err("test not taken into the test suite");
|
log.err("test not taken into the test suite");
|
||||||
_ = DirectoryTokenizer;
|
_ = DirectoryTokenizer;
|
||||||
_ = Filesystem;
|
_ = Filesystem;
|
||||||
_ = PartitionTable;
|
_ = PartitionTable;
|
||||||
@ -237,7 +470,7 @@ pub const Allocator = extern struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Necessary to do this hack
|
/// Necessary to do this hack
|
||||||
const Callbacks = switch (common.cpu.arch) {
|
const Callbacks = switch (cpu.arch) {
|
||||||
.x86 => extern struct {
|
.x86 => extern struct {
|
||||||
allocate: *const Allocate.Fn,
|
allocate: *const Allocate.Fn,
|
||||||
allocate_padding: u32 = 0,
|
allocate_padding: u32 = 0,
|
||||||
@ -265,7 +498,7 @@ pub const Allocator = extern struct {
|
|||||||
return &result[0];
|
return &result[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wrap(zig_allocator: common.ZigAllocator) Wrapped {
|
pub fn wrap(zig_allocator: ZigAllocator) Wrapped {
|
||||||
return .{
|
return .{
|
||||||
.allocator = .{
|
.allocator = .{
|
||||||
.callbacks = .{
|
.callbacks = .{
|
||||||
@ -279,7 +512,7 @@ pub const Allocator = extern struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn zigUnwrap(allocator: *Allocator) common.ZigAllocator {
|
pub fn zigUnwrap(allocator: *Allocator) ZigAllocator {
|
||||||
return .{
|
return .{
|
||||||
.ptr = allocator,
|
.ptr = allocator,
|
||||||
.vtable = &zig_vtable,
|
.vtable = &zig_vtable,
|
||||||
@ -293,11 +526,13 @@ pub const Allocator = extern struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub fn zigAllocate(context: *anyopaque, size: usize, ptr_align: u8, return_address: usize) ?[*]u8 {
|
pub fn zigAllocate(context: *anyopaque, size: usize, ptr_align: u8, return_address: usize) ?[*]u8 {
|
||||||
_ = context;
|
|
||||||
_ = size;
|
|
||||||
_ = ptr_align;
|
|
||||||
_ = return_address;
|
_ = return_address;
|
||||||
return null;
|
const allocator: *Allocator = @ptrCast(@alignCast(context));
|
||||||
|
// Not understanding why Zig API is like this:
|
||||||
|
const alignment = @as(u64, 1) << @as(u6, @intCast(ptr_align));
|
||||||
|
const result = allocator.allocateBytes(size, alignment) catch return null;
|
||||||
|
assert(result.size >= size);
|
||||||
|
return @ptrFromInt(result.address);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn zigResize(context: *anyopaque, buffer: []u8, buffer_alignment: u8, new_length: usize, return_address: usize) bool {
|
pub fn zigResize(context: *anyopaque, buffer: []u8, buffer_alignment: u8, new_length: usize, return_address: usize) bool {
|
||||||
@ -320,14 +555,14 @@ pub const Allocator = extern struct {
|
|||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
zig: extern struct {
|
zig: extern struct {
|
||||||
ptr: *anyopaque,
|
ptr: *anyopaque,
|
||||||
vtable: *const common.ZigAllocator.VTable,
|
vtable: *const ZigAllocator.VTable,
|
||||||
},
|
},
|
||||||
|
|
||||||
pub fn unwrap(wrapped_allocator: *Wrapped) *Allocator {
|
pub fn unwrap(wrapped_allocator: *Wrapped) *Allocator {
|
||||||
return &wrapped_allocator.allocator;
|
return &wrapped_allocator.allocator;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn zigUnwrap(wrapped_allocator: *Wrapped) common.ZigAllocator {
|
pub fn zigUnwrap(wrapped_allocator: *Wrapped) ZigAllocator {
|
||||||
return .{
|
return .{
|
||||||
.ptr = wrapped_allocator.zig.ptr,
|
.ptr = wrapped_allocator.zig.ptr,
|
||||||
.vtable = wrapped_allocator.zig.vtable,
|
.vtable = wrapped_allocator.zig.vtable,
|
||||||
@ -337,7 +572,7 @@ pub const Allocator = extern struct {
|
|||||||
pub fn wrappedCallbackAllocate(allocator: *Allocator, size: u64, alignment: u64) Allocator.Allocate.Error!Allocator.Allocate.Result {
|
pub fn wrappedCallbackAllocate(allocator: *Allocator, size: u64, alignment: u64) Allocator.Allocate.Error!Allocator.Allocate.Result {
|
||||||
const wrapped_allocator = @fieldParentPtr(Wrapped, "allocator", allocator);
|
const wrapped_allocator = @fieldParentPtr(Wrapped, "allocator", allocator);
|
||||||
const zig_allocator = wrapped_allocator.zigUnwrap();
|
const zig_allocator = wrapped_allocator.zigUnwrap();
|
||||||
if (alignment > common.maxInt(u8)) {
|
if (alignment > maxInt(u8)) {
|
||||||
@panic("alignment supported by Zig is less than asked");
|
@panic("alignment supported by Zig is less than asked");
|
||||||
}
|
}
|
||||||
const zig_result = zig_allocator.vtable.alloc(zig_allocator.ptr, size, @as(u8, @intCast(alignment)), @returnAddress());
|
const zig_result = zig_allocator.vtable.alloc(zig_allocator.ptr, size, @as(u8, @intCast(alignment)), @returnAddress());
|
||||||
@ -461,7 +696,7 @@ pub fn ELF(comptime bits: comptime_int) type {
|
|||||||
return Parser.Error.invalid_magic;
|
return Parser.Error.invalid_magic;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!common.equal(u8, &file_header.elf_id, FileHeader.elf_signature)) {
|
if (!equal(u8, &file_header.elf_id, FileHeader.elf_signature)) {
|
||||||
return Parser.Error.invalid_signature;
|
return Parser.Error.invalid_signature;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -484,7 +719,7 @@ pub fn ELF(comptime bits: comptime_int) type {
|
|||||||
|
|
||||||
pub const ProgramHeader = switch (is_64) {
|
pub const ProgramHeader = switch (is_64) {
|
||||||
true => extern struct {
|
true => extern struct {
|
||||||
type: Type = .load,
|
type: @This().Type = .load,
|
||||||
flags: Flags, //= @enumToInt(Flags.readable) | @enumToInt(Flags.executable),
|
flags: Flags, //= @enumToInt(Flags.readable) | @enumToInt(Flags.executable),
|
||||||
offset: u64,
|
offset: u64,
|
||||||
virtual_address: u64,
|
virtual_address: u64,
|
||||||
@ -518,7 +753,7 @@ pub fn ELF(comptime bits: comptime_int) type {
|
|||||||
reserved: u29,
|
reserved: u29,
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
common.assert(@sizeOf(Flags) == @sizeOf(u32));
|
assert(@sizeOf(Flags) == @sizeOf(u32));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@ -585,8 +820,8 @@ pub fn ELF(comptime bits: comptime_int) type {
|
|||||||
|
|
||||||
pub inline fn safeArchitectureCast(value: anytype) usize {
|
pub inline fn safeArchitectureCast(value: anytype) usize {
|
||||||
return switch (@sizeOf(@TypeOf(value)) > @sizeOf(usize)) {
|
return switch (@sizeOf(@TypeOf(value)) > @sizeOf(usize)) {
|
||||||
true => if (value <= common.maxInt(usize)) @as(usize, @truncate(value)) else {
|
true => if (value <= maxInt(usize)) @as(usize, @truncate(value)) else {
|
||||||
common.log.err("PANIC: virtual address is longer than usize: 0x{x}", .{value});
|
log.err("PANIC: virtual address is longer than usize: 0x{x}", .{value});
|
||||||
@panic("safeArchitectureCast");
|
@panic("safeArchitectureCast");
|
||||||
},
|
},
|
||||||
false => value,
|
false => value,
|
||||||
@ -598,11 +833,11 @@ pub const DereferenceError = error{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub inline fn tryDereferenceAddress(value: anytype) DereferenceError!usize {
|
pub inline fn tryDereferenceAddress(value: anytype) DereferenceError!usize {
|
||||||
common.assert(@sizeOf(@TypeOf(value)) > @sizeOf(usize));
|
assert(@sizeOf(@TypeOf(value)) > @sizeOf(usize));
|
||||||
return if (value <= common.maxInt(usize)) @as(usize, @truncate(value)) else return DereferenceError.address_bigger_than_usize;
|
return if (value <= maxInt(usize)) @as(usize, @truncate(value)) else return DereferenceError.address_bigger_than_usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn enumAddNames(comptime enum_fields: []const common.Type.EnumField, comptime names: []const []const u8) []const common.Type.EnumField {
|
pub fn enumAddNames(comptime enum_fields: []const Type.EnumField, comptime names: []const []const u8) []const Type.EnumField {
|
||||||
comptime var result = enum_fields;
|
comptime var result = enum_fields;
|
||||||
const previous_last_value = if (enum_fields.len > 0) enum_fields[enum_fields.len - 1].value else 0;
|
const previous_last_value = if (enum_fields.len > 0) enum_fields[enum_fields.len - 1].value else 0;
|
||||||
|
|
||||||
@ -617,13 +852,13 @@ pub fn enumAddNames(comptime enum_fields: []const common.Type.EnumField, comptim
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ErrorSet(comptime error_names: []const []const u8, comptime predefined_fields: []const common.Type.EnumField) type {
|
pub fn ErrorSet(comptime error_names: []const []const u8, comptime predefined_fields: []const Type.EnumField) type {
|
||||||
comptime var error_fields: []const common.Type.Error = &.{};
|
comptime var error_fields: []const Type.Error = &.{};
|
||||||
comptime var enum_items: []const common.Type.EnumField = predefined_fields;
|
comptime var enum_items: []const Type.EnumField = predefined_fields;
|
||||||
comptime var enum_value = enum_items[enum_items.len - 1].value + 1;
|
comptime var enum_value = enum_items[enum_items.len - 1].value + 1;
|
||||||
|
|
||||||
inline for (error_names) |error_name| {
|
inline for (error_names) |error_name| {
|
||||||
enum_items = enum_items ++ [1]common.Type.EnumField{
|
enum_items = enum_items ++ [1]Type.EnumField{
|
||||||
.{
|
.{
|
||||||
.name = error_name,
|
.name = error_name,
|
||||||
.value = enum_value,
|
.value = enum_value,
|
||||||
@ -634,23 +869,23 @@ pub fn ErrorSet(comptime error_names: []const []const u8, comptime predefined_fi
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline for (enum_items) |item| {
|
inline for (enum_items) |item| {
|
||||||
error_fields = error_fields ++ [1]common.Type.Error{
|
error_fields = error_fields ++ [1]Type.Error{
|
||||||
.{
|
.{
|
||||||
.name = item.name,
|
.name = item.name,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const EnumType = @Type(common.Type{
|
const EnumType = @Type(Type{
|
||||||
.Enum = .{
|
.Enum = .{
|
||||||
.tag_type = u16,
|
.tag_type = u15,
|
||||||
.fields = enum_items,
|
.fields = enum_items,
|
||||||
.decls = &.{},
|
.decls = &.{},
|
||||||
.is_exhaustive = true,
|
.is_exhaustive = true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const ErrorType = @Type(common.Type{
|
const ErrorType = @Type(Type{
|
||||||
.ErrorSet = error_fields,
|
.ErrorSet = error_fields,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -660,12 +895,9 @@ pub fn ErrorSet(comptime error_names: []const []const u8, comptime predefined_fi
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getDebugInformation(allocator: common.ZigAllocator, elf_file: []align(common.default_sector_size) const u8) !common.ModuleDebugInfo {
|
pub fn getDebugInformation(allocator: ZigAllocator, elf_file: []align(arch.valid_page_sizes[0]) const u8) !ModuleDebugInfo {
|
||||||
const elf = common.elf;
|
const hdr = @as(*align(1) const elf.Ehdr, @ptrCast(&elf_file[0]));
|
||||||
var module_debug_info: common.ModuleDebugInfo = undefined;
|
if (!equal(u8, hdr.e_ident[0..4], elf.MAGIC)) return error.InvalidElfMagic;
|
||||||
_ = module_debug_info;
|
|
||||||
const hdr = @as(*const elf.Ehdr, @ptrCast(&elf_file[0]));
|
|
||||||
if (!common.equal(u8, hdr.e_ident[0..4], elf.MAGIC)) return error.InvalidElfMagic;
|
|
||||||
if (hdr.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion;
|
if (hdr.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion;
|
||||||
|
|
||||||
const endian = .Little;
|
const endian = .Little;
|
||||||
@ -673,94 +905,110 @@ pub fn getDebugInformation(allocator: common.ZigAllocator, elf_file: []align(com
|
|||||||
const shoff = hdr.e_shoff;
|
const shoff = hdr.e_shoff;
|
||||||
const str_section_off = shoff + @as(u64, hdr.e_shentsize) * @as(u64, hdr.e_shstrndx);
|
const str_section_off = shoff + @as(u64, hdr.e_shentsize) * @as(u64, hdr.e_shstrndx);
|
||||||
const str_shdr = @as(
|
const str_shdr = @as(
|
||||||
*const elf.Shdr,
|
*align(1) const elf.Shdr,
|
||||||
@ptrCast(@alignCast(&elf_file[common.cast(usize, str_section_off) orelse return error.Overflow])),
|
@ptrCast(&elf_file[cast(usize, str_section_off) orelse return error.Overflow]),
|
||||||
);
|
);
|
||||||
const header_strings = elf_file[str_shdr.sh_offset .. str_shdr.sh_offset + str_shdr.sh_size];
|
const header_strings = elf_file[str_shdr.sh_offset .. str_shdr.sh_offset + str_shdr.sh_size];
|
||||||
const shdrs = @as(
|
const shdrs = @as(
|
||||||
[*]const elf.Shdr,
|
[*]align(1) const elf.Shdr,
|
||||||
@ptrCast(@alignCast(&elf_file[shoff])),
|
@ptrCast(&elf_file[shoff]),
|
||||||
)[0..hdr.e_shnum];
|
)[0..hdr.e_shnum];
|
||||||
|
|
||||||
var opt_debug_info: ?[]const u8 = null;
|
var sections: dwarf.DwarfInfo.SectionArray = dwarf.DwarfInfo.null_section_array;
|
||||||
var opt_debug_abbrev: ?[]const u8 = null;
|
|
||||||
var opt_debug_str: ?[]const u8 = null;
|
// Combine section list. This takes ownership over any owned sections from the parent scope.
|
||||||
var opt_debug_str_offsets: ?[]const u8 = null;
|
errdefer for (sections) |section| if (section) |s| if (s.owned) allocator.free(s.data);
|
||||||
var opt_debug_line: ?[]const u8 = null;
|
|
||||||
var opt_debug_line_str: ?[]const u8 = null;
|
var separate_debug_filename: ?[]const u8 = null;
|
||||||
var opt_debug_ranges: ?[]const u8 = null;
|
_ = separate_debug_filename;
|
||||||
var opt_debug_loclists: ?[]const u8 = null;
|
var separate_debug_crc: ?u32 = null;
|
||||||
var opt_debug_rnglists: ?[]const u8 = null;
|
_ = separate_debug_crc;
|
||||||
var opt_debug_addr: ?[]const u8 = null;
|
|
||||||
var opt_debug_names: ?[]const u8 = null;
|
|
||||||
var opt_debug_frame: ?[]const u8 = null;
|
|
||||||
|
|
||||||
for (shdrs) |*shdr| {
|
for (shdrs) |*shdr| {
|
||||||
if (shdr.sh_type == elf.SHT_NULL) continue;
|
if (shdr.sh_type == elf.SHT_NULL or shdr.sh_type == elf.SHT_NOBITS) continue;
|
||||||
|
const name = sliceTo(header_strings[shdr.sh_name..], 0);
|
||||||
|
|
||||||
const name = common.sliceTo(header_strings[shdr.sh_name..], 0);
|
if (equal(u8, name, ".gnu_debuglink")) {
|
||||||
if (common.equal(u8, name, ".debug_info")) {
|
@panic("WTF");
|
||||||
opt_debug_info = try chopSlice(elf_file, shdr.sh_offset, shdr.sh_size);
|
// const gnu_debuglink = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
|
||||||
} else if (common.equal(u8, name, ".debug_abbrev")) {
|
// const debug_filename = mem.sliceTo(@as([*:0]const u8, @ptrCast(gnu_debuglink.ptr)), 0);
|
||||||
opt_debug_abbrev = try chopSlice(elf_file, shdr.sh_offset, shdr.sh_size);
|
// const crc_offset = mem.alignForward(usize, @intFromPtr(&debug_filename[debug_filename.len]) + 1, 4) - @intFromPtr(gnu_debuglink.ptr);
|
||||||
} else if (common.equal(u8, name, ".debug_str")) {
|
// const crc_bytes = gnu_debuglink[crc_offset .. crc_offset + 4];
|
||||||
opt_debug_str = try chopSlice(elf_file, shdr.sh_offset, shdr.sh_size);
|
// separate_debug_crc = mem.readIntSliceNative(u32, crc_bytes);
|
||||||
} else if (common.equal(u8, name, ".debug_str_offsets")) {
|
// separate_debug_filename = debug_filename;
|
||||||
opt_debug_str_offsets = try chopSlice(elf_file, shdr.sh_offset, shdr.sh_size);
|
// continue;
|
||||||
} else if (common.equal(u8, name, ".debug_line")) {
|
|
||||||
opt_debug_line = try chopSlice(elf_file, shdr.sh_offset, shdr.sh_size);
|
|
||||||
} else if (common.equal(u8, name, ".debug_line_str")) {
|
|
||||||
opt_debug_line_str = try chopSlice(elf_file, shdr.sh_offset, shdr.sh_size);
|
|
||||||
} else if (common.equal(u8, name, ".debug_ranges")) {
|
|
||||||
opt_debug_ranges = try chopSlice(elf_file, shdr.sh_offset, shdr.sh_size);
|
|
||||||
} else if (common.equal(u8, name, ".debug_loclists")) {
|
|
||||||
opt_debug_loclists = try chopSlice(elf_file, shdr.sh_offset, shdr.sh_size);
|
|
||||||
} else if (common.equal(u8, name, ".debug_rnglists")) {
|
|
||||||
opt_debug_rnglists = try chopSlice(elf_file, shdr.sh_offset, shdr.sh_size);
|
|
||||||
} else if (common.equal(u8, name, ".debug_addr")) {
|
|
||||||
opt_debug_addr = try chopSlice(elf_file, shdr.sh_offset, shdr.sh_size);
|
|
||||||
} else if (common.equal(u8, name, ".debug_names")) {
|
|
||||||
opt_debug_names = try chopSlice(elf_file, shdr.sh_offset, shdr.sh_size);
|
|
||||||
} else if (common.equal(u8, name, ".debug_frame")) {
|
|
||||||
opt_debug_frame = try chopSlice(elf_file, shdr.sh_offset, shdr.sh_size);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var di = common.dwarf.DwarfInfo{
|
var section_index: ?usize = null;
|
||||||
|
inline for (@typeInfo(dwarf.DwarfSection).Enum.fields, 0..) |section, i| {
|
||||||
|
if (equal(u8, "." ++ section.name, name)) section_index = i;
|
||||||
|
}
|
||||||
|
if (section_index == null) continue;
|
||||||
|
if (sections[section_index.?] != null) continue;
|
||||||
|
|
||||||
|
const section_bytes = try chopSlice(elf_file, shdr.sh_offset, shdr.sh_size);
|
||||||
|
sections[section_index.?] = if ((shdr.sh_flags & elf.SHF_COMPRESSED) > 0) blk: {
|
||||||
|
var section_stream = fixedBufferStream(section_bytes);
|
||||||
|
var section_reader = section_stream.reader();
|
||||||
|
const chdr = section_reader.readStruct(elf.Chdr) catch continue;
|
||||||
|
if (chdr.ch_type != .ZLIB) continue;
|
||||||
|
|
||||||
|
if (true) @panic("ZLIB");
|
||||||
|
break :blk undefined;
|
||||||
|
// var zlib_stream = std.compress.zlib.decompressStream(allocator, section_stream.reader()) catch continue;
|
||||||
|
// defer zlib_stream.deinit();
|
||||||
|
//
|
||||||
|
// var decompressed_section = try allocator.alloc(u8, chdr.ch_size);
|
||||||
|
// errdefer allocator.free(decompressed_section);
|
||||||
|
//
|
||||||
|
// const read = zlib_stream.reader().readAll(decompressed_section) catch continue;
|
||||||
|
// assert(read == decompressed_section.len);
|
||||||
|
//
|
||||||
|
// break :blk .{
|
||||||
|
// .data = decompressed_section,
|
||||||
|
// .virtual_address = shdr.sh_addr,
|
||||||
|
// .owned = true,
|
||||||
|
// };
|
||||||
|
} else .{
|
||||||
|
.data = section_bytes,
|
||||||
|
.virtual_address = shdr.sh_addr,
|
||||||
|
.owned = false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const missing_debug_info =
|
||||||
|
sections[@intFromEnum(dwarf.DwarfSection.debug_info)] == null or
|
||||||
|
sections[@intFromEnum(dwarf.DwarfSection.debug_abbrev)] == null or
|
||||||
|
sections[@intFromEnum(dwarf.DwarfSection.debug_str)] == null or
|
||||||
|
sections[@intFromEnum(dwarf.DwarfSection.debug_line)] == null;
|
||||||
|
assert(!missing_debug_info);
|
||||||
|
|
||||||
|
var di = dwarf.DwarfInfo{
|
||||||
.endian = endian,
|
.endian = endian,
|
||||||
.debug_info = opt_debug_info orelse return error.MissingDebugInfo,
|
.sections = sections,
|
||||||
.debug_abbrev = opt_debug_abbrev orelse return error.MissingDebugInfo,
|
.is_macho = false,
|
||||||
.debug_str = opt_debug_str orelse return error.MissingDebugInfo,
|
|
||||||
.debug_str_offsets = opt_debug_str_offsets,
|
|
||||||
.debug_line = opt_debug_line orelse return error.MissingDebugInfo,
|
|
||||||
.debug_line_str = opt_debug_line_str,
|
|
||||||
.debug_ranges = opt_debug_ranges,
|
|
||||||
.debug_loclists = opt_debug_loclists,
|
|
||||||
.debug_rnglists = opt_debug_rnglists,
|
|
||||||
.debug_addr = opt_debug_addr,
|
|
||||||
.debug_names = opt_debug_names,
|
|
||||||
.debug_frame = opt_debug_frame,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
try common.dwarf.openDwarfDebugInfo(&di, allocator);
|
try dwarf.openDwarfDebugInfo(&di, allocator);
|
||||||
|
|
||||||
return di;
|
return di;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn chopSlice(ptr: []const u8, offset: u64, size: u64) error{Overflow}![]const u8 {
|
fn chopSlice(ptr: []const u8, offset: u64, size: u64) error{Overflow}![]const u8 {
|
||||||
const start = common.cast(usize, offset) orelse return error.Overflow;
|
const start = cast(usize, offset) orelse return error.Overflow;
|
||||||
const end = start + (common.cast(usize, size) orelse return error.Overflow);
|
const end = start + (cast(usize, size) orelse return error.Overflow);
|
||||||
return ptr[start..end];
|
return ptr[start..end];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn RegionInterface(comptime Region: type) type {
|
pub fn RegionInterface(comptime Region: type) type {
|
||||||
const type_info = @typeInfo(Region);
|
const type_info = @typeInfo(Region);
|
||||||
common.assert(type_info == .Struct);
|
assert(type_info == .Struct);
|
||||||
common.assert(type_info.Struct.layout == .Extern);
|
assert(type_info.Struct.layout == .Extern);
|
||||||
common.assert(type_info.Struct.fields.len == 2);
|
assert(type_info.Struct.fields.len == 2);
|
||||||
const fields = type_info.Struct.fields;
|
const region_fields = type_info.Struct.fields;
|
||||||
common.assert(common.equal(u8, fields[0].name, "address"));
|
assert(equal(u8, region_fields[0].name, "address"));
|
||||||
common.assert(common.equal(u8, fields[1].name, "size"));
|
assert(equal(u8, region_fields[1].name, "size"));
|
||||||
const Addr = fields[0].type;
|
const Addr = region_fields[0].type;
|
||||||
const AddrT = getAddrT(Addr);
|
const AddrT = getAddrT(Addr);
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
@ -773,6 +1021,14 @@ pub fn RegionInterface(comptime Region: type) type {
|
|||||||
.size = info.size,
|
.size = info.size,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub inline fn invalid() Region {
|
||||||
|
return Region{
|
||||||
|
.address = Addr.invalid(),
|
||||||
|
.size = 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub inline fn fromRaw(info: struct {
|
pub inline fn fromRaw(info: struct {
|
||||||
raw_address: AddrT,
|
raw_address: AddrT,
|
||||||
size: AddrT,
|
size: AddrT,
|
||||||
@ -808,7 +1064,7 @@ pub fn RegionInterface(comptime Region: type) type {
|
|||||||
|
|
||||||
pub inline fn fromAnytype(any: anytype, info: struct {}) Region {
|
pub inline fn fromAnytype(any: anytype, info: struct {}) Region {
|
||||||
_ = info;
|
_ = info;
|
||||||
common.assert(@typeInfo(@TypeOf(any)) == .Pointer);
|
assert(@typeInfo(@TypeOf(any)) == .Pointer);
|
||||||
return Region{
|
return Region{
|
||||||
.address = VirtualAddress.new(@intFromPtr(any)),
|
.address = VirtualAddress.new(@intFromPtr(any)),
|
||||||
.size = @sizeOf(@TypeOf(any.*)),
|
.size = @sizeOf(@TypeOf(any.*)),
|
||||||
@ -833,7 +1089,7 @@ pub fn RegionInterface(comptime Region: type) type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn shrinked(region: Region, size: AddrT) Region {
|
pub fn shrinked(region: Region, size: AddrT) Region {
|
||||||
common.assert(size <= region.size);
|
assert(size <= region.size);
|
||||||
const result = Region{
|
const result = Region{
|
||||||
.address = region.address,
|
.address = region.address,
|
||||||
.size = size,
|
.size = size,
|
||||||
@ -842,8 +1098,22 @@ pub fn RegionInterface(comptime Region: type) type {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn takeSlice(region: *Region, size: AddrT) Region {
|
const TakeSliceError = error{
|
||||||
common.assert(size <= region.size);
|
not_enough_space,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub inline fn slice(region: *const Region, size: AddrT) Region {
|
||||||
|
assert(size <= region.size);
|
||||||
|
const result = .{
|
||||||
|
.address = region.address,
|
||||||
|
.size = size,
|
||||||
|
};
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub inline fn takeSlice(region: *Region, size: AddrT) !Region {
|
||||||
|
if (size <= region.size) {
|
||||||
const result = Region{
|
const result = Region{
|
||||||
.address = region.address,
|
.address = region.address,
|
||||||
.size = size,
|
.size = size,
|
||||||
@ -853,6 +1123,9 @@ pub fn RegionInterface(comptime Region: type) type {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return TakeSliceError.not_enough_space;
|
||||||
|
}
|
||||||
|
|
||||||
pub inline fn split(region: Region, comptime count: comptime_int) [count]Region {
|
pub inline fn split(region: Region, comptime count: comptime_int) [count]Region {
|
||||||
const region_size = @divExact(region.size, count);
|
const region_size = @divExact(region.size, count);
|
||||||
var result: [count]Region = undefined;
|
var result: [count]Region = undefined;
|
||||||
@ -919,9 +1192,9 @@ pub const VirtualMemoryRegion = extern struct {
|
|||||||
|
|
||||||
fn getAddrT(comptime AddressEnum: type) type {
|
fn getAddrT(comptime AddressEnum: type) type {
|
||||||
const type_info = @typeInfo(AddressEnum);
|
const type_info = @typeInfo(AddressEnum);
|
||||||
common.assert(type_info == .Enum);
|
assert(type_info == .Enum);
|
||||||
const AddrT = type_info.Enum.tag_type;
|
const AddrT = type_info.Enum.tag_type;
|
||||||
common.assert(switch (common.cpu.arch) {
|
assert(switch (cpu.arch) {
|
||||||
.x86 => @sizeOf(AddrT) == 2 * @sizeOf(usize),
|
.x86 => @sizeOf(AddrT) == 2 * @sizeOf(usize),
|
||||||
else => @sizeOf(AddrT) == @sizeOf(usize),
|
else => @sizeOf(AddrT) == @sizeOf(usize),
|
||||||
});
|
});
|
||||||
@ -1023,12 +1296,12 @@ pub const VirtualAddress = enum(u64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn toPhysicalAddress(virtual_address: VirtualAddress) PhysicalAddress {
|
pub inline fn toPhysicalAddress(virtual_address: VirtualAddress) PhysicalAddress {
|
||||||
common.assert(virtual_address.value() >= config.cpu_driver_higher_half_address);
|
assert(virtual_address.value() >= config.cpu_driver_higher_half_address);
|
||||||
return @as(PhysicalAddress, @enumFromInt(virtual_address.value() - config.cpu_driver_higher_half_address));
|
return @as(PhysicalAddress, @enumFromInt(virtual_address.value() - config.cpu_driver_higher_half_address));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn toGuaranteedPhysicalAddress(virtual_address: VirtualAddress) PhysicalAddress {
|
pub inline fn toGuaranteedPhysicalAddress(virtual_address: VirtualAddress) PhysicalAddress {
|
||||||
common.assert(virtual_address.value() < config.cpu_driver_higher_half_address);
|
assert(virtual_address.value() < config.cpu_driver_higher_half_address);
|
||||||
return PhysicalAddress.new(virtual_address.value());
|
return PhysicalAddress.new(virtual_address.value());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -15,6 +15,8 @@ pub const current = switch (@import("builtin").cpu.arch) {
|
|||||||
pub const x86 = @import("arch/x86.zig");
|
pub const x86 = @import("arch/x86.zig");
|
||||||
pub const x86_64 = @import("arch/x86_64.zig");
|
pub const x86_64 = @import("arch/x86_64.zig");
|
||||||
|
|
||||||
|
pub const paging = x86_64.paging;
|
||||||
|
|
||||||
pub const default_page_size = current.default_page_size;
|
pub const default_page_size = current.default_page_size;
|
||||||
pub const reasonable_page_size = current.reasonable_page_size;
|
pub const reasonable_page_size = current.reasonable_page_size;
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ pub const CPUID = extern struct {
|
|||||||
ecx: u32,
|
ecx: u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub inline fn cpuid(leaf: u32) CPUID {
|
pub inline fn cpuid(leaf: u32, subleaf: u32) CPUID {
|
||||||
var eax: u32 = undefined;
|
var eax: u32 = undefined;
|
||||||
var ebx: u32 = undefined;
|
var ebx: u32 = undefined;
|
||||||
var edx: u32 = undefined;
|
var edx: u32 = undefined;
|
||||||
@ -18,6 +18,8 @@ pub inline fn cpuid(leaf: u32) CPUID {
|
|||||||
[edx] "={edx}" (edx),
|
[edx] "={edx}" (edx),
|
||||||
[ecx] "={ecx}" (ecx),
|
[ecx] "={ecx}" (ecx),
|
||||||
: [leaf] "{eax}" (leaf),
|
: [leaf] "{eax}" (leaf),
|
||||||
|
[subleaf] "{ecx}" (subleaf),
|
||||||
|
: "memory"
|
||||||
);
|
);
|
||||||
|
|
||||||
return CPUID{
|
return CPUID{
|
||||||
|
@ -2,6 +2,39 @@ const lib = @import("lib");
|
|||||||
const x86 = @import("x86/common.zig");
|
const x86 = @import("x86/common.zig");
|
||||||
pub usingnamespace x86;
|
pub usingnamespace x86;
|
||||||
|
|
||||||
|
pub const paging = struct {
|
||||||
|
pub const page_table_entry_size = @sizeOf(u64);
|
||||||
|
pub const page_table_size = lib.arch.valid_page_sizes[0];
|
||||||
|
pub const page_table_entry_count = @divExact(page_table_size, page_table_entry_size);
|
||||||
|
pub const page_table_alignment = page_table_size;
|
||||||
|
pub const page_table_mask = page_table_entry_count - 1;
|
||||||
|
pub const user_address_space_start = 0x200_000;
|
||||||
|
pub const user_address_space_end = 0x8000_0000_0000;
|
||||||
|
pub const root_page_table_level: Level = switch (Level) {
|
||||||
|
Level4 => Level.PML4,
|
||||||
|
Level5 => @compileError("TODO"),
|
||||||
|
else => @compileError("Unknown level"),
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Level = Level4;
|
||||||
|
|
||||||
|
pub const Level4 = enum(u2) {
|
||||||
|
PML4 = 0,
|
||||||
|
PDP = 1,
|
||||||
|
PD = 2,
|
||||||
|
PT = 3,
|
||||||
|
|
||||||
|
pub const count = lib.enumCount(@This());
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Level5 = enum(u3) {};
|
||||||
|
|
||||||
|
comptime {
|
||||||
|
lib.assert(page_table_alignment == page_table_size);
|
||||||
|
lib.assert(page_table_size == lib.arch.valid_page_sizes[0]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pub const valid_page_sizes = [3]comptime_int{ 0x1000, 0x1000 * 0x200, 0x1000 * 0x200 * 0x200 };
|
pub const valid_page_sizes = [3]comptime_int{ 0x1000, 0x1000 * 0x200, 0x1000 * 0x200 * 0x200 };
|
||||||
pub const reverse_valid_page_sizes = blk: {
|
pub const reverse_valid_page_sizes = blk: {
|
||||||
var reverse = valid_page_sizes;
|
var reverse = valid_page_sizes;
|
||||||
|
127
src/lib/data_structures.zig
Normal file
127
src/lib/data_structures.zig
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
const lib = @import("lib");
|
||||||
|
const Allocator = lib.Allocator;
|
||||||
|
const assert = lib.assert;
|
||||||
|
const maxInt = lib.maxInt;
|
||||||
|
|
||||||
|
pub fn BitsetU64(comptime bits: comptime_int) type {
|
||||||
|
assert(bits <= @bitSizeOf(u64));
|
||||||
|
const max_value = maxInt(@Type(.{
|
||||||
|
.Int = .{
|
||||||
|
.signedness = .unsigned,
|
||||||
|
.bits = bits,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
return packed struct(u64) {
|
||||||
|
value: u64 = 0,
|
||||||
|
|
||||||
|
const Error = error{
|
||||||
|
block_full,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub inline fn allocate(bitset: *@This()) !u6 {
|
||||||
|
if (bitset.value & max_value != max_value) {
|
||||||
|
// log.debug("Bitset: 0b{b}", .{bitset.value});
|
||||||
|
const result: u6 = @intCast(@ctz(~bitset.value));
|
||||||
|
// log.debug("Result: {}", .{result});
|
||||||
|
assert(!bitset.isSet(result));
|
||||||
|
bitset.set(result);
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
return error.block_full;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub inline fn set(bitset: *@This(), index: u6) void {
|
||||||
|
assert(index < bits);
|
||||||
|
bitset.value |= (@as(u64, 1) << index);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub inline fn clear(bitset: *@This(), index: u6) void {
|
||||||
|
assert(index < bits);
|
||||||
|
bitset.value &= ~(@as(u64, 1) << index);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub inline fn isSet(bitset: @This(), index: u6) bool {
|
||||||
|
assert(index < bits);
|
||||||
|
return bitset.value & (@as(u64, 1) << index) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub inline fn isFull(bitset: @This()) bool {
|
||||||
|
return bitset.value == max_value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn SparseArray(comptime T: type) type {
|
||||||
|
return extern struct {
|
||||||
|
ptr: [*]T,
|
||||||
|
len: usize,
|
||||||
|
capacity: usize,
|
||||||
|
|
||||||
|
const Array = @This();
|
||||||
|
|
||||||
|
pub const Error = error{
|
||||||
|
index_out_of_bounds,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn allocate(array: *Array, allocator: *Allocator) !*T {
|
||||||
|
try array.ensureCapacity(allocator, array.len + 1);
|
||||||
|
const index = array.len;
|
||||||
|
array.len += 1;
|
||||||
|
const slice = array.ptr[0..array.len];
|
||||||
|
return &slice[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn append(array: *Array, allocator: *Allocator, element: T) !usize {
|
||||||
|
try array.ensureCapacity(allocator, array.len + 1);
|
||||||
|
const index = array.len;
|
||||||
|
array.len += 1;
|
||||||
|
const slice = array.ptr[0..array.len];
|
||||||
|
slice[index] = element;
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ensureCapacity(array: *Array, allocator: *Allocator, desired_capacity: usize) !void {
|
||||||
|
if (array.capacity < desired_capacity) {
|
||||||
|
// Allocate a new array
|
||||||
|
const new_slice = try allocator.allocate(T, desired_capacity);
|
||||||
|
if (array.capacity == 0) {
|
||||||
|
array.ptr = new_slice.ptr;
|
||||||
|
array.capacity = new_slice.len;
|
||||||
|
} else {
|
||||||
|
// Reallocate
|
||||||
|
if (array.len > 0) {
|
||||||
|
@memcpy(new_slice[0..array.len], array.ptr[0..array.len]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: free
|
||||||
|
|
||||||
|
array.ptr = new_slice.ptr;
|
||||||
|
array.capacity = new_slice.len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn indexOf(array: *Array, ptr: *T) usize {
|
||||||
|
const base_int = @intFromPtr(array.ptr);
|
||||||
|
const ptr_int = @intFromPtr(ptr);
|
||||||
|
return @divExact(ptr_int - base_int, @sizeOf(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub inline fn get(array: *Array, index: usize) T {
|
||||||
|
assert(array.len > index);
|
||||||
|
const slice = array.ptr[0..array.len];
|
||||||
|
return slice[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub inline fn getChecked(array: *Array, index: usize) !T {
|
||||||
|
if (array.len > index) {
|
||||||
|
return array.get(index);
|
||||||
|
} else {
|
||||||
|
return error.index_out_of_bounds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -353,10 +353,7 @@ pub const Cache = extern struct {
|
|||||||
const aligned_file_size = lib.alignForward(usize, file_size, cache.disk.sector_size);
|
const aligned_file_size = lib.alignForward(usize, file_size, cache.disk.sector_size);
|
||||||
const lba = cache.clusterToSector(first_cluster);
|
const lba = cache.clusterToSector(first_cluster);
|
||||||
|
|
||||||
log.debug("Start disk callback", .{});
|
|
||||||
|
|
||||||
const result = try cache.disk.callbacks.read(cache.disk, @divExact(aligned_file_size, cache.disk.sector_size), lba, file_buffer);
|
const result = try cache.disk.callbacks.read(cache.disk, @divExact(aligned_file_size, cache.disk.sector_size), lba, file_buffer);
|
||||||
log.debug("End disk callback", .{});
|
|
||||||
return result.buffer[0..file_size];
|
return result.buffer[0..file_size];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ const assert = lib.assert;
|
|||||||
const log = lib.log;
|
const log = lib.log;
|
||||||
const maxInt = lib.maxInt;
|
const maxInt = lib.maxInt;
|
||||||
const Allocator = lib.Allocator;
|
const Allocator = lib.Allocator;
|
||||||
|
const VirtualAddress = lib.VirtualAddress;
|
||||||
|
|
||||||
const bootloader = @import("bootloader");
|
const bootloader = @import("bootloader");
|
||||||
|
|
||||||
@ -64,7 +65,8 @@ pub const Mapping = extern struct {
|
|||||||
execute: bool = false,
|
execute: bool = false,
|
||||||
user: bool = false,
|
user: bool = false,
|
||||||
secret: bool = false,
|
secret: bool = false,
|
||||||
reserved: u26 = 0,
|
huge_pages: bool = true,
|
||||||
|
reserved: u25 = 0,
|
||||||
|
|
||||||
pub inline fn empty() Flags {
|
pub inline fn empty() Flags {
|
||||||
return .{};
|
return .{};
|
||||||
@ -86,6 +88,7 @@ pub const PageAllocator = struct {
|
|||||||
count: u16 = 1,
|
count: u16 = 1,
|
||||||
level: arch.paging.Level,
|
level: arch.paging.Level,
|
||||||
user: bool,
|
user: bool,
|
||||||
|
virtual_address: arch.paging.IndexedVirtualAddress,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub inline fn allocatePageTable(page_allocator: PageAllocator, options: AllocatePageTablesOptions) !lib.PhysicalMemoryRegion {
|
pub inline fn allocatePageTable(page_allocator: PageAllocator, options: AllocatePageTablesOptions) !lib.PhysicalMemoryRegion {
|
||||||
@ -94,6 +97,7 @@ pub const PageAllocator = struct {
|
|||||||
.level = options.level,
|
.level = options.level,
|
||||||
.level_valid = true,
|
.level_valid = true,
|
||||||
.user = options.user,
|
.user = options.user,
|
||||||
|
.virtual_address = options.virtual_address,
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -104,6 +108,7 @@ pub const PageAllocator = struct {
|
|||||||
level: arch.paging.Level = undefined,
|
level: arch.paging.Level = undefined,
|
||||||
level_valid: bool = false,
|
level_valid: bool = false,
|
||||||
user: bool = false,
|
user: bool = false,
|
||||||
|
virtual_address: arch.paging.IndexedVirtualAddress,
|
||||||
};
|
};
|
||||||
|
|
||||||
const ContextType = enum(u32) {
|
const ContextType = enum(u32) {
|
||||||
|
@ -10,7 +10,6 @@ const zeroes = lib.zeroes;
|
|||||||
const Allocator = lib.Allocator;
|
const Allocator = lib.Allocator;
|
||||||
|
|
||||||
const privileged = @import("privileged");
|
const privileged = @import("privileged");
|
||||||
const Heap = privileged.Heap;
|
|
||||||
const PageAllocator = privileged.PageAllocator;
|
const PageAllocator = privileged.PageAllocator;
|
||||||
|
|
||||||
const valid_page_sizes = lib.arch.x86_64.valid_page_sizes;
|
const valid_page_sizes = lib.arch.x86_64.valid_page_sizes;
|
||||||
@ -22,33 +21,12 @@ const PhysicalAddress = lib.PhysicalAddress;
|
|||||||
const VirtualAddress = lib.VirtualAddress;
|
const VirtualAddress = lib.VirtualAddress;
|
||||||
const PhysicalMemoryRegion = lib.PhysicalMemoryRegion;
|
const PhysicalMemoryRegion = lib.PhysicalMemoryRegion;
|
||||||
const PhysicalAddressSpace = lib.PhysicalAddressSpace;
|
const PhysicalAddressSpace = lib.PhysicalAddressSpace;
|
||||||
const Mapping = privileged.Mapping;
|
pub const Mapping = privileged.Mapping;
|
||||||
|
|
||||||
const bootloader = @import("bootloader");
|
const bootloader = @import("bootloader");
|
||||||
|
|
||||||
const page_table_level_count = 4;
|
const paging = lib.arch.x86_64.paging;
|
||||||
pub const page_table_mask = page_table_entry_count - 1;
|
pub usingnamespace paging;
|
||||||
|
|
||||||
pub fn entryCount(comptime level: Level, limit: u64) u10 {
|
|
||||||
const index = baseFromVirtualAddress(level, limit - 1);
|
|
||||||
const result = @as(u10, index) + 1;
|
|
||||||
// @compileLog(limit, index, result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Comptime test
|
|
||||||
comptime {
|
|
||||||
const va = 134217728;
|
|
||||||
const indices = computeIndices(va);
|
|
||||||
const pml4_index = baseFromVirtualAddress(.PML4, va);
|
|
||||||
const pdp_index = baseFromVirtualAddress(.PDP, va);
|
|
||||||
const pd_index = baseFromVirtualAddress(.PD, va);
|
|
||||||
const pt_index = baseFromVirtualAddress(.PT, va);
|
|
||||||
assert(pml4_index == indices[@intFromEnum(Level.PML4)]);
|
|
||||||
assert(pdp_index == indices[@intFromEnum(Level.PDP)]);
|
|
||||||
assert(pd_index == indices[@intFromEnum(Level.PD)]);
|
|
||||||
assert(pt_index == indices[@intFromEnum(Level.PT)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const max_level_possible = 5;
|
const max_level_possible = 5;
|
||||||
pub const IndexedVirtualAddress = packed struct(u64) {
|
pub const IndexedVirtualAddress = packed struct(u64) {
|
||||||
@ -67,12 +45,24 @@ pub const IndexedVirtualAddress = packed struct(u64) {
|
|||||||
return VirtualAddress.new(raw);
|
return VirtualAddress.new(raw);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn toIndices(indexed: IndexedVirtualAddress) [Level.count]u9 {
|
||||||
|
var result: [Level.count]u9 = undefined;
|
||||||
|
inline for (@typeInfo(Level).Enum.fields) |enum_field| {
|
||||||
|
result[@intFromEnum(@field(Level, enum_field.name))] = @field(indexed, enum_field.name);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn baseFromVirtualAddress(comptime level: Level, virtual_address: u64) u9 {
|
const Level = enum(u2) {
|
||||||
const indexed = @as(IndexedVirtualAddress, @bitCast(virtual_address));
|
PML4 = 0,
|
||||||
return @field(indexed, @tagName(level));
|
PDP = 1,
|
||||||
}
|
PD = 2,
|
||||||
|
PT = 3,
|
||||||
|
|
||||||
|
const count = @typeInfo(Level).Enum.fields.len;
|
||||||
|
};
|
||||||
|
|
||||||
pub const CPUPageTables = extern struct {
|
pub const CPUPageTables = extern struct {
|
||||||
pml4_table: PhysicalAddress,
|
pml4_table: PhysicalAddress,
|
||||||
@ -82,7 +72,7 @@ pub const CPUPageTables = extern struct {
|
|||||||
|
|
||||||
const base = 0xffff_ffff_8000_0000;
|
const base = 0xffff_ffff_8000_0000;
|
||||||
const top = base + pte_count * lib.arch.valid_page_sizes[0];
|
const top = base + pte_count * lib.arch.valid_page_sizes[0];
|
||||||
const pte_count = page_table_entry_count - left_ptables;
|
const pte_count = paging.page_table_entry_count - left_ptables;
|
||||||
pub const left_ptables = 4;
|
pub const left_ptables = 4;
|
||||||
pub const pml4_index = 0x1ff;
|
pub const pml4_index = 0x1ff;
|
||||||
pub const pdp_index = 0x1fe;
|
pub const pdp_index = 0x1fe;
|
||||||
@ -94,14 +84,12 @@ pub const CPUPageTables = extern struct {
|
|||||||
1; // PT
|
1; // PT
|
||||||
const allocated_size = allocated_table_count * 0x1000;
|
const allocated_size = allocated_table_count * 0x1000;
|
||||||
|
|
||||||
const page_table_base = top;
|
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
assert(top + (left_ptables * lib.arch.valid_page_sizes[0]) == base + lib.arch.valid_page_sizes[1]);
|
assert(top + (left_ptables * lib.arch.valid_page_sizes[0]) == base + lib.arch.valid_page_sizes[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initialize(page_allocator: PageAllocator) !CPUPageTables {
|
pub fn initialize(page_allocator: PageAllocator) !CPUPageTables {
|
||||||
const page_table_allocation = try page_allocator.allocate(page_allocator.context, allocated_size, lib.arch.valid_page_sizes[0], .{});
|
const page_table_allocation = try page_allocator.allocate(page_allocator.context, allocated_size, lib.arch.valid_page_sizes[0], .{ .virtual_address = @bitCast(@as(u64, 0)) });
|
||||||
|
|
||||||
const page_tables = CPUPageTables{
|
const page_tables = CPUPageTables{
|
||||||
.pml4_table = page_table_allocation.address,
|
.pml4_table = page_table_allocation.address,
|
||||||
@ -163,8 +151,8 @@ pub const CPUPageTables = extern struct {
|
|||||||
if (asked_virtual_address.offset(size).value() > top) return CPUPageTables.MapError.upper_limit_exceeded;
|
if (asked_virtual_address.offset(size).value() > top) return CPUPageTables.MapError.upper_limit_exceeded;
|
||||||
|
|
||||||
const flags = general_flags.toArchitectureSpecific();
|
const flags = general_flags.toArchitectureSpecific();
|
||||||
const indices = computeIndices(asked_virtual_address.value());
|
const indexed: IndexedVirtualAddress = @bitCast(asked_virtual_address.value());
|
||||||
const index = indices[indices.len - 1];
|
const index = indexed.PT;
|
||||||
const iteration_count = @as(u32, @intCast(size >> lib.arch.page_shifter(lib.arch.valid_page_sizes[0])));
|
const iteration_count = @as(u32, @intCast(size >> lib.arch.page_shifter(lib.arch.valid_page_sizes[0])));
|
||||||
const p_table = cpu_page_tables.p_table.toIdentityMappedVirtualAddress().access(*PTable);
|
const p_table = cpu_page_tables.p_table.toIdentityMappedVirtualAddress().access(*PTable);
|
||||||
const p_table_slice = p_table[index .. index + iteration_count];
|
const p_table_slice = p_table[index .. index + iteration_count];
|
||||||
@ -181,10 +169,26 @@ pub const CPUPageTables = extern struct {
|
|||||||
pub const Specific = extern struct {
|
pub const Specific = extern struct {
|
||||||
cr3: cr3 align(8),
|
cr3: cr3 align(8),
|
||||||
|
|
||||||
|
pub fn current() Specific {
|
||||||
|
return .{
|
||||||
|
.cr3 = cr3.read(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub inline fn makeCurrent(specific: Specific) void {
|
pub inline fn makeCurrent(specific: Specific) void {
|
||||||
specific.getUserCr3().write();
|
specific.getUserCr3().write();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub inline fn makeCurrentPrivileged(specific: Specific) void {
|
||||||
|
specific.cr3.write();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fromPhysicalRegion(physical_memory_region: PhysicalMemoryRegion) Specific {
|
||||||
|
return Specific{
|
||||||
|
.cr3 = @bitCast(physical_memory_region.address.value()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn fromPageTables(cpu_page_tables: CPUPageTables) Specific {
|
pub fn fromPageTables(cpu_page_tables: CPUPageTables) Specific {
|
||||||
return .{
|
return .{
|
||||||
.cr3 = cr3.fromAddress(cpu_page_tables.pml4_table),
|
.cr3 = cr3.fromAddress(cpu_page_tables.pml4_table),
|
||||||
@ -199,7 +203,7 @@ pub const Specific = extern struct {
|
|||||||
if (size >= reverse_page_size) {
|
if (size >= reverse_page_size) {
|
||||||
const is_smallest_page_size = reverse_page_index == reverse_valid_page_sizes.len - 1;
|
const is_smallest_page_size = reverse_page_index == reverse_valid_page_sizes.len - 1;
|
||||||
|
|
||||||
if (is_smallest_page_size) {
|
if (is_smallest_page_size or !general_flags.huge_pages) {
|
||||||
var virtual_address = asked_virtual_address.value();
|
var virtual_address = asked_virtual_address.value();
|
||||||
var physical_address = asked_physical_address.value();
|
var physical_address = asked_physical_address.value();
|
||||||
|
|
||||||
@ -247,8 +251,8 @@ pub const Specific = extern struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn mapGeneric(specific: Specific, asked_physical_address: PhysicalAddress, asked_virtual_address: VirtualAddress, size: u64, comptime asked_page_size: comptime_int, flags: MemoryFlags, page_allocator: PageAllocator) !void {
|
fn mapGeneric(specific: Specific, asked_physical_address: PhysicalAddress, asked_virtual_address: VirtualAddress, size: u64, comptime asked_page_size: comptime_int, flags: MemoryFlags, page_allocator: PageAllocator) !void {
|
||||||
if (!isAlignedGeneric(u64, asked_physical_address.value(), asked_page_size)) {
|
if (!isAlignedGeneric(u64, asked_physical_address.value(), lib.arch.valid_page_sizes[0])) {
|
||||||
//log.debug("PA: {}. Page size: 0x{x}", .{ asked_physical_address, asked_page_size });
|
log.debug("PA: {}. Page size: 0x{x}", .{ asked_physical_address, asked_page_size });
|
||||||
@panic("Misaligned physical address in mapGeneric");
|
@panic("Misaligned physical address in mapGeneric");
|
||||||
}
|
}
|
||||||
if (!isAlignedGeneric(u64, asked_virtual_address.value(), asked_page_size)) {
|
if (!isAlignedGeneric(u64, asked_virtual_address.value(), asked_page_size)) {
|
||||||
@ -266,7 +270,7 @@ pub const Specific = extern struct {
|
|||||||
// TODO: batch better
|
// TODO: batch better
|
||||||
switch (asked_page_size) {
|
switch (asked_page_size) {
|
||||||
// 1 GB
|
// 1 GB
|
||||||
lib.arch.valid_page_sizes[0] * page_table_entry_count * page_table_entry_count => {
|
lib.arch.valid_page_sizes[0] * paging.page_table_entry_count * paging.page_table_entry_count => {
|
||||||
while (virtual_address < top_virtual_address) : ({
|
while (virtual_address < top_virtual_address) : ({
|
||||||
physical_address += asked_page_size;
|
physical_address += asked_page_size;
|
||||||
virtual_address += asked_page_size;
|
virtual_address += asked_page_size;
|
||||||
@ -275,7 +279,7 @@ pub const Specific = extern struct {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 2 MB
|
// 2 MB
|
||||||
lib.arch.valid_page_sizes[0] * page_table_entry_count => {
|
lib.arch.valid_page_sizes[0] * paging.page_table_entry_count => {
|
||||||
while (virtual_address < top_virtual_address) : ({
|
while (virtual_address < top_virtual_address) : ({
|
||||||
physical_address += asked_page_size;
|
physical_address += asked_page_size;
|
||||||
virtual_address += asked_page_size;
|
virtual_address += asked_page_size;
|
||||||
@ -297,67 +301,34 @@ pub const Specific = extern struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn map1GBPage(specific: Specific, physical_address: u64, virtual_address: u64, flags: MemoryFlags, page_allocator: PageAllocator) !void {
|
fn map1GBPage(specific: Specific, physical_address: u64, virtual_address: u64, flags: MemoryFlags, page_allocator: PageAllocator) !void {
|
||||||
const indices = computeIndices(virtual_address);
|
const indexed: IndexedVirtualAddress = @bitCast(virtual_address);
|
||||||
|
|
||||||
const pml4_table = try getPML4Table(specific.cr3);
|
const pml4_table = try getPML4Table(specific.cr3);
|
||||||
const pdp_table = try getPDPTable(pml4_table, indices, flags, page_allocator);
|
const pdp_table = try getPDPTable(pml4_table, indexed, flags, page_allocator);
|
||||||
try mapPageTable1GB(pdp_table, indices, physical_address, flags);
|
try mapPageTable1GB(pdp_table, indexed, physical_address, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map2MBPage(specific: Specific, physical_address: u64, virtual_address: u64, flags: MemoryFlags, page_allocator: PageAllocator) !void {
|
fn map2MBPage(specific: Specific, physical_address: u64, virtual_address: u64, flags: MemoryFlags, page_allocator: PageAllocator) !void {
|
||||||
const indices = computeIndices(virtual_address);
|
const indexed: IndexedVirtualAddress = @bitCast(virtual_address);
|
||||||
|
|
||||||
const pml4_table = try getPML4Table(specific.cr3);
|
const pml4_table = try getPML4Table(specific.cr3);
|
||||||
const pdp_table = try getPDPTable(pml4_table, indices, flags, page_allocator);
|
const pdp_table = try getPDPTable(pml4_table, indexed, flags, page_allocator);
|
||||||
const pd_table = try getPDTable(pdp_table, indices, flags, page_allocator);
|
const pd_table = try getPDTable(pdp_table, indexed, flags, page_allocator);
|
||||||
|
|
||||||
mapPageTable2MB(pd_table, indices, physical_address, flags) catch |err| {
|
mapPageTable2MB(pd_table, indexed, physical_address, flags) catch |err| {
|
||||||
log.err("Virtual address: 0x{x}. Physical address: 0x{x}", .{ virtual_address, physical_address });
|
log.err("Virtual address: 0x{x}. Physical address: 0x{x}", .{ virtual_address, physical_address });
|
||||||
return err;
|
return err;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map4KPage(specific: Specific, physical_address: u64, virtual_address: u64, flags: MemoryFlags, page_allocator: PageAllocator) !void {
|
fn map4KPage(specific: Specific, physical_address: u64, virtual_address: u64, flags: MemoryFlags, page_allocator: PageAllocator) !void {
|
||||||
const indices = computeIndices(virtual_address);
|
const indexed: IndexedVirtualAddress = @bitCast(virtual_address);
|
||||||
|
|
||||||
const pml4_table = try getPML4Table(specific.cr3);
|
const pml4_table = try getPML4Table(specific.cr3);
|
||||||
const pdp_table = try getPDPTable(pml4_table, indices, flags, page_allocator);
|
const pdp_table = try getPDPTable(pml4_table, indexed, flags, page_allocator);
|
||||||
const pd_table = try getPDTable(pdp_table, indices, flags, page_allocator);
|
const pd_table = try getPDTable(pdp_table, indexed, flags, page_allocator);
|
||||||
const p_table = try getPTable(pd_table, indices, flags, page_allocator);
|
const p_table = try getPTable(pd_table, indexed, flags, page_allocator);
|
||||||
try mapPageTable4KB(p_table, indices, physical_address, flags);
|
try mapPageTable4KB(p_table, indexed, physical_address, flags);
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn switchTo(specific: *Specific, execution_mode: lib.TraditionalExecutionMode) void {
|
|
||||||
const mask = ~@as(u64, 1 << 12);
|
|
||||||
const masked_cr3 = (@as(u64, @bitCast(specific.cr3)) & mask);
|
|
||||||
const privileged_or = (@as(u64, @intFromEnum(execution_mode)) << 12);
|
|
||||||
const new_cr3 = @as(cr3, @bitCast(masked_cr3 | privileged_or));
|
|
||||||
specific.cr3 = new_cr3;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn copyHigherHalfCommon(cpu_specific: Specific, pml4_physical_address: PhysicalAddress) void {
|
|
||||||
const cpu_side_pml4_table = pml4_physical_address.toHigherHalfVirtualAddress().access(*PML4Table);
|
|
||||||
const privileged_cpu_pml4_table = try getPML4Table(cpu_specific.cr3);
|
|
||||||
for (cpu_side_pml4_table[0x100..], privileged_cpu_pml4_table[0x100..]) |*pml4_entry, cpu_pml4_entry| {
|
|
||||||
pml4_entry.* = cpu_pml4_entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn copyHigherHalfPrivileged(cpu_specific: Specific, pml4_physical_address: PhysicalAddress) void {
|
|
||||||
cpu_specific.copyHigherHalfCommon(pml4_physical_address);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn copyHigherHalfUser(cpu_specific: Specific, pml4_physical_address: PhysicalAddress, page_allocator: *PageAllocator) !void {
|
|
||||||
cpu_specific.copyHigherHalfCommon(pml4_physical_address);
|
|
||||||
|
|
||||||
const pml4_table = pml4_physical_address.toHigherHalfVirtualAddress().access(*PML4Table);
|
|
||||||
const pml4_entry = pml4_table[0x1ff];
|
|
||||||
const pml4_entry_address = PhysicalAddress.new(unpackAddress(pml4_entry));
|
|
||||||
const pdp_table = pml4_entry_address.toHigherHalfVirtualAddress().access(*PDPTable);
|
|
||||||
const new_pdp_table_allocation = try page_allocator.allocate(0x1000, 0x1000);
|
|
||||||
const new_pdp_table = new_pdp_table_allocation.toHigherHalfVirtualAddress().access(PDPTE);
|
|
||||||
@memcpy(new_pdp_table, pdp_table);
|
|
||||||
new_pdp_table[0x1fd] = @as(PDPTE, @bitCast(@as(u64, 0)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const TranslateError = error{
|
pub const TranslateError = error{
|
||||||
@ -373,7 +344,7 @@ pub const Specific = extern struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub fn translateAddress(specific: Specific, virtual_address: VirtualAddress, flags: MemoryFlags) !PhysicalAddress {
|
pub fn translateAddress(specific: Specific, virtual_address: VirtualAddress, flags: MemoryFlags) !PhysicalAddress {
|
||||||
const indices = computeIndices(virtual_address.value());
|
const indexed: IndexedVirtualAddress = @bitCast(virtual_address.value());
|
||||||
const is_desired = virtual_address.value() == 0xffff_ffff_8001_f000;
|
const is_desired = virtual_address.value() == 0xffff_ffff_8001_f000;
|
||||||
|
|
||||||
const pml4_table = try getPML4Table(specific.cr3);
|
const pml4_table = try getPML4Table(specific.cr3);
|
||||||
@ -382,10 +353,10 @@ pub const Specific = extern struct {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
//log.debug("pml4 table: 0x{x}", .{@ptrToInt(pml4_table)});
|
//log.debug("pml4 table: 0x{x}", .{@ptrToInt(pml4_table)});
|
||||||
const pml4_index = indices[@intFromEnum(Level.PML4)];
|
const pml4_index = indexed.PML4;
|
||||||
const pml4_entry = pml4_table[pml4_index];
|
const pml4_entry = pml4_table[pml4_index];
|
||||||
if (!pml4_entry.present) {
|
if (!pml4_entry.present) {
|
||||||
log.err("Virtual address: 0x{x}.\nPML4 index: {}.\nValue: {}\n", .{ virtual_address.value(), pml4_index, pml4_entry });
|
log.err("Virtual address: 0x{x}.\nPML4 pointer: 0x{x}\nPML4 index: {}.\nValue: {}\n", .{ virtual_address.value(), @intFromPtr(pml4_table), pml4_index, pml4_entry });
|
||||||
return TranslateError.pml4_entry_not_present;
|
return TranslateError.pml4_entry_not_present;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -398,12 +369,12 @@ pub const Specific = extern struct {
|
|||||||
return TranslateError.pml4_entry_address_null;
|
return TranslateError.pml4_entry_address_null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const pdp_table = try getPDPTable(pml4_table, indices, undefined, null);
|
const pdp_table = try getPDPTable(pml4_table, indexed, undefined, null);
|
||||||
if (is_desired) {
|
if (is_desired) {
|
||||||
_ = try specific.translateAddress(VirtualAddress.new(@intFromPtr(pdp_table)), .{});
|
_ = try specific.translateAddress(VirtualAddress.new(@intFromPtr(pdp_table)), .{});
|
||||||
}
|
}
|
||||||
//log.debug("pdp table: 0x{x}", .{@ptrToInt(pdp_table)});
|
//log.debug("pdp table: 0x{x}", .{@ptrToInt(pdp_table)});
|
||||||
const pdp_index = indices[@intFromEnum(Level.PDP)];
|
const pdp_index = indexed.PDP;
|
||||||
const pdp_entry = &pdp_table[pdp_index];
|
const pdp_entry = &pdp_table[pdp_index];
|
||||||
if (!pdp_entry.present) {
|
if (!pdp_entry.present) {
|
||||||
log.err("PDP index {} not present in PDP table 0x{x}", .{ pdp_index, @intFromPtr(pdp_table) });
|
log.err("PDP index {} not present in PDP table 0x{x}", .{ pdp_index, @intFromPtr(pdp_table) });
|
||||||
@ -435,7 +406,7 @@ pub const Specific = extern struct {
|
|||||||
_ = try specific.translateAddress(VirtualAddress.new(@intFromPtr(pd_table)), .{});
|
_ = try specific.translateAddress(VirtualAddress.new(@intFromPtr(pd_table)), .{});
|
||||||
}
|
}
|
||||||
//log.debug("pd table: 0x{x}", .{@ptrToInt(pd_table)});
|
//log.debug("pd table: 0x{x}", .{@ptrToInt(pd_table)});
|
||||||
const pd_index = indices[@intFromEnum(Level.PD)];
|
const pd_index = indexed.PD;
|
||||||
const pd_entry = &pd_table[pd_index];
|
const pd_entry = &pd_table[pd_index];
|
||||||
if (!pd_entry.present) {
|
if (!pd_entry.present) {
|
||||||
log.err("PD index: {}", .{pd_index});
|
log.err("PD index: {}", .{pd_index});
|
||||||
@ -468,11 +439,10 @@ pub const Specific = extern struct {
|
|||||||
_ = try specific.translateAddress(VirtualAddress.new(@intFromPtr(p_table)), .{});
|
_ = try specific.translateAddress(VirtualAddress.new(@intFromPtr(p_table)), .{});
|
||||||
}
|
}
|
||||||
// log.debug("p table: 0x{x}", .{@ptrToInt(p_table)});
|
// log.debug("p table: 0x{x}", .{@ptrToInt(p_table)});
|
||||||
const pt_index = indices[@intFromEnum(Level.PT)];
|
const pt_index = indexed.PT;
|
||||||
const pt_entry = &p_table[pt_index];
|
const pt_entry = &p_table[pt_index];
|
||||||
if (!pt_entry.present) {
|
if (!pt_entry.present) {
|
||||||
log.err("Virtual address 0x{x} not mapped", .{virtual_address.value()});
|
log.err("Virtual address 0x{x} not mapped", .{virtual_address.value()});
|
||||||
log.err("Indices: {any}", .{indices});
|
|
||||||
log.err("PTE: 0x{x}", .{@intFromPtr(pt_entry)});
|
log.err("PTE: 0x{x}", .{@intFromPtr(pt_entry)});
|
||||||
log.err("PDE: 0x{x}", .{@intFromPtr(pd_entry)});
|
log.err("PDE: 0x{x}", .{@intFromPtr(pd_entry)});
|
||||||
log.err("PDPE: 0x{x}", .{@intFromPtr(pdp_entry)});
|
log.err("PDPE: 0x{x}", .{@intFromPtr(pdp_entry)});
|
||||||
@ -491,115 +461,20 @@ pub const Specific = extern struct {
|
|||||||
return pt_entry_address;
|
return pt_entry_address;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setMappingFlags(specific: Specific, virtual_address: u64, flags: Mapping.Flags) !void {
|
|
||||||
const indices = computeIndices(virtual_address);
|
|
||||||
|
|
||||||
const vas_cr3 = specific.cr3;
|
|
||||||
|
|
||||||
const pml4_physical_address = vas_cr3.getAddress();
|
|
||||||
|
|
||||||
const pml4_table = try accessPageTable(pml4_physical_address, *PML4Table);
|
|
||||||
const pml4_entry = pml4_table[indices[@intFromEnum(Level.PML4)]];
|
|
||||||
if (!pml4_entry.present) {
|
|
||||||
return TranslateError.pml4_entry_not_present;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pml4_entry_address = PhysicalAddress.new(unpackAddress(pml4_entry));
|
|
||||||
if (pml4_entry_address.value() == 0) {
|
|
||||||
return TranslateError.pml4_entry_address_null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pdp_table = try accessPageTable(pml4_entry_address, *PDPTable);
|
|
||||||
const pdp_entry = pdp_table[indices[@intFromEnum(Level.PDP)]];
|
|
||||||
if (!pdp_entry.present) {
|
|
||||||
return TranslateError.pdp_entry_not_present;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pdp_entry_address = PhysicalAddress.new(unpackAddress(pdp_entry));
|
|
||||||
if (pdp_entry_address.value() == 0) {
|
|
||||||
return TranslateError.pdp_entry_address_null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pd_table = try accessPageTable(pdp_entry_address, *PDTable);
|
|
||||||
const pd_entry = pd_table[indices[@intFromEnum(Level.PD)]];
|
|
||||||
if (!pd_entry.present) {
|
|
||||||
return TranslateError.pd_entry_not_present;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pd_entry_address = PhysicalAddress.new(unpackAddress(pd_entry));
|
|
||||||
if (pd_entry_address.value() == 0) {
|
|
||||||
return TranslateError.pd_entry_address_null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pt_table = try accessPageTable(pd_entry_address, *PTable);
|
|
||||||
const pt_entry = &pt_table[indices[@intFromEnum(Level.PT)]];
|
|
||||||
if (!pt_entry.present) {
|
|
||||||
return TranslateError.pd_entry_not_present;
|
|
||||||
}
|
|
||||||
|
|
||||||
pt_entry.write = flags.write;
|
|
||||||
pt_entry.user = flags.user;
|
|
||||||
pt_entry.page_level_cache_disable = flags.cache_disable;
|
|
||||||
pt_entry.global = flags.global;
|
|
||||||
pt_entry.execute_disable = !flags.execute;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn debugMemoryMap(specific: Specific) !void {
|
|
||||||
log.debug("[START] Memory map dump 0x{x}\n", .{specific.cr3.getAddress().value()});
|
|
||||||
|
|
||||||
const pml4 = try specific.getCpuPML4Table();
|
|
||||||
|
|
||||||
for (pml4, 0..) |*pml4te, pml4_index| {
|
|
||||||
if (pml4te.present) {
|
|
||||||
const pdp_table = try accessPageTable(PhysicalAddress.new(unpackAddress(pml4te.*)), *PDPTable);
|
|
||||||
|
|
||||||
for (pdp_table, 0..) |*pdpte, pdp_index| {
|
|
||||||
if (pdpte.present) {
|
|
||||||
if (pdpte.page_size) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pd_table = try accessPageTable(PhysicalAddress.new(unpackAddress(pdpte.*)), *PDTable);
|
|
||||||
|
|
||||||
for (pd_table, 0..) |*pdte, pd_index| {
|
|
||||||
if (pdte.present) {
|
|
||||||
if (pdte.page_size) @panic("bbbb");
|
|
||||||
|
|
||||||
const p_table = try accessPageTable(PhysicalAddress.new(unpackAddress(pdte.*)), *PTable);
|
|
||||||
|
|
||||||
for (p_table, 0..) |*pte, pt_index| {
|
|
||||||
if (pte.present) {
|
|
||||||
const indexed_virtual_address = IndexedVirtualAddress{
|
|
||||||
.PML4 = @as(u9, @intCast(pml4_index)),
|
|
||||||
.PDP = @as(u9, @intCast(pdp_index)),
|
|
||||||
.PD = @as(u9, @intCast(pd_index)),
|
|
||||||
.PT = @as(u9, @intCast(pt_index)),
|
|
||||||
};
|
|
||||||
|
|
||||||
const virtual_address = indexed_virtual_address.toVirtualAddress();
|
|
||||||
const physical_address = unpackAddress(pte.*);
|
|
||||||
log.debug("0x{x} -> 0x{x}", .{ virtual_address.value(), physical_address });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.debug("[END] Memory map dump", .{});
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fn getUserCr3(specific: Specific) cr3 {
|
inline fn getUserCr3(specific: Specific) cr3 {
|
||||||
assert(@as(u64, @bitCast(specific.cr3)) & page_table_size == 0);
|
assert(specific.isPrivileged());
|
||||||
return @as(cr3, @bitCast(@as(u64, @bitCast(specific.cr3)) | page_table_size));
|
return @as(cr3, @bitCast(@as(u64, @bitCast(specific.cr3)) | paging.page_table_size));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn getCpuPML4Table(specific: Specific) !*PML4Table {
|
pub inline fn getCpuPML4Table(specific: Specific) !*PML4Table {
|
||||||
assert(@as(u64, @bitCast(specific.cr3)) & page_table_size == 0);
|
assert(specific.isPrivileged());
|
||||||
return try specific.getPML4TableUnchecked();
|
return try specific.getPML4TableUnchecked();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn isPrivileged(specific: Specific) bool {
|
||||||
|
return @as(u64, @bitCast(specific.cr3)) & paging.page_table_size == 0;
|
||||||
|
}
|
||||||
|
|
||||||
pub inline fn getUserPML4Table(specific: Specific) !*PML4Table {
|
pub inline fn getUserPML4Table(specific: Specific) !*PML4Table {
|
||||||
return try getPML4Table(specific.getUserCr3());
|
return try getPML4Table(specific.getUserCr3());
|
||||||
}
|
}
|
||||||
@ -609,8 +484,6 @@ pub const Specific = extern struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const Indices = [enumCount(Level)]u16;
|
|
||||||
|
|
||||||
const MapError = error{
|
const MapError = error{
|
||||||
already_present_4kb,
|
already_present_4kb,
|
||||||
already_present_2mb,
|
already_present_2mb,
|
||||||
@ -650,8 +523,8 @@ fn getPML4Table(cr3r: cr3) !*PML4Table {
|
|||||||
return pml4_table;
|
return pml4_table;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getPDPTable(pml4_table: *PML4Table, indices: Indices, flags: MemoryFlags, maybe_page_allocator: ?PageAllocator) !*PDPTable {
|
fn getPDPTable(pml4_table: *PML4Table, virtual_address: IndexedVirtualAddress, flags: MemoryFlags, maybe_page_allocator: ?PageAllocator) !*PDPTable {
|
||||||
const index = indices[@intFromEnum(Level.PML4)];
|
const index = virtual_address.PML4;
|
||||||
const entry_pointer = &pml4_table[index];
|
const entry_pointer = &pml4_table[index];
|
||||||
|
|
||||||
const table_physical_address = physical_address_blk: {
|
const table_physical_address = physical_address_blk: {
|
||||||
@ -664,6 +537,7 @@ fn getPDPTable(pml4_table: *PML4Table, indices: Indices, flags: MemoryFlags, may
|
|||||||
const entry_allocation = try page_allocator.allocatePageTable(.{
|
const entry_allocation = try page_allocator.allocatePageTable(.{
|
||||||
.level = .PDP,
|
.level = .PDP,
|
||||||
.user = flags.user,
|
.user = flags.user,
|
||||||
|
.virtual_address = virtual_address,
|
||||||
});
|
});
|
||||||
|
|
||||||
entry_pointer.* = PML4TE{
|
entry_pointer.* = PML4TE{
|
||||||
@ -706,8 +580,8 @@ pub inline fn getPageEntry(comptime Entry: type, physical_address: u64, flags: M
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mapPageTable1GB(pdp_table: *PDPTable, indices: Indices, physical_address: u64, flags: MemoryFlags) MapError!void {
|
fn mapPageTable1GB(pdp_table: *PDPTable, indexed: IndexedVirtualAddress, physical_address: u64, flags: MemoryFlags) MapError!void {
|
||||||
const entry_index = indices[@intFromEnum(Level.PDP)];
|
const entry_index = indexed.PDP;
|
||||||
const entry_pointer = &pdp_table[entry_index];
|
const entry_pointer = &pdp_table[entry_index];
|
||||||
|
|
||||||
if (entry_pointer.present) return MapError.already_present_1gb;
|
if (entry_pointer.present) return MapError.already_present_1gb;
|
||||||
@ -717,8 +591,8 @@ fn mapPageTable1GB(pdp_table: *PDPTable, indices: Indices, physical_address: u64
|
|||||||
entry_pointer.* = @as(PDPTE, @bitCast(getPageEntry(PDPTE_1GB, physical_address, flags)));
|
entry_pointer.* = @as(PDPTE, @bitCast(getPageEntry(PDPTE_1GB, physical_address, flags)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mapPageTable2MB(pd_table: *PDTable, indices: Indices, physical_address: u64, flags: MemoryFlags) !void {
|
fn mapPageTable2MB(pd_table: *PDTable, indexed: IndexedVirtualAddress, physical_address: u64, flags: MemoryFlags) !void {
|
||||||
const entry_index = indices[@intFromEnum(Level.PD)];
|
const entry_index = indexed.PD;
|
||||||
const entry_pointer = &pd_table[entry_index];
|
const entry_pointer = &pd_table[entry_index];
|
||||||
const entry_value = entry_pointer.*;
|
const entry_value = entry_pointer.*;
|
||||||
|
|
||||||
@ -727,13 +601,11 @@ fn mapPageTable2MB(pd_table: *PDTable, indices: Indices, physical_address: u64,
|
|||||||
return MapError.already_present_2mb;
|
return MapError.already_present_2mb;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(isAlignedGeneric(u64, physical_address, valid_page_sizes[1]));
|
|
||||||
|
|
||||||
entry_pointer.* = @as(PDTE, @bitCast(getPageEntry(PDTE_2MB, physical_address, flags)));
|
entry_pointer.* = @as(PDTE, @bitCast(getPageEntry(PDTE_2MB, physical_address, flags)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mapPageTable4KB(p_table: *PTable, indices: Indices, physical_address: u64, flags: MemoryFlags) !void {
|
fn mapPageTable4KB(p_table: *PTable, indexed: IndexedVirtualAddress, physical_address: u64, flags: MemoryFlags) !void {
|
||||||
const entry_index = indices[@intFromEnum(Level.PT)];
|
const entry_index = indexed.PT;
|
||||||
const entry_pointer = &p_table[entry_index];
|
const entry_pointer = &p_table[entry_index];
|
||||||
|
|
||||||
if (entry_pointer.present) {
|
if (entry_pointer.present) {
|
||||||
@ -750,8 +622,8 @@ const ToImplementError = error{
|
|||||||
page_size,
|
page_size,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn getPDTable(pdp_table: *PDPTable, indices: Indices, flags: MemoryFlags, page_allocator: PageAllocator) !*PDTable {
|
fn getPDTable(pdp_table: *PDPTable, indexed: IndexedVirtualAddress, flags: MemoryFlags, page_allocator: PageAllocator) !*PDTable {
|
||||||
const entry_index = indices[@intFromEnum(Level.PDP)];
|
const entry_index = indexed.PDP;
|
||||||
const entry_pointer = &pdp_table[entry_index];
|
const entry_pointer = &pdp_table[entry_index];
|
||||||
|
|
||||||
const table_physical_address = physical_address_blk: {
|
const table_physical_address = physical_address_blk: {
|
||||||
@ -765,6 +637,7 @@ fn getPDTable(pdp_table: *PDPTable, indices: Indices, flags: MemoryFlags, page_a
|
|||||||
const entry_allocation = try page_allocator.allocatePageTable(.{
|
const entry_allocation = try page_allocator.allocatePageTable(.{
|
||||||
.level = .PD,
|
.level = .PD,
|
||||||
.user = flags.user,
|
.user = flags.user,
|
||||||
|
.virtual_address = indexed,
|
||||||
});
|
});
|
||||||
|
|
||||||
entry_pointer.* = PDPTE{
|
entry_pointer.* = PDPTE{
|
||||||
@ -781,8 +654,8 @@ fn getPDTable(pdp_table: *PDPTable, indices: Indices, flags: MemoryFlags, page_a
|
|||||||
return try accessPageTable(table_physical_address, *PDTable);
|
return try accessPageTable(table_physical_address, *PDTable);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getPTable(pd_table: *PDTable, indices: Indices, flags: MemoryFlags, page_allocator: PageAllocator) !*PTable {
|
fn getPTable(pd_table: *PDTable, indexed: IndexedVirtualAddress, flags: MemoryFlags, page_allocator: PageAllocator) !*PTable {
|
||||||
const entry_pointer = &pd_table[indices[@intFromEnum(Level.PD)]];
|
const entry_pointer = &pd_table[indexed.PD];
|
||||||
const table_physical_address = physical_address_blk: {
|
const table_physical_address = physical_address_blk: {
|
||||||
const entry_value = entry_pointer.*;
|
const entry_value = entry_pointer.*;
|
||||||
if (entry_value.present) {
|
if (entry_value.present) {
|
||||||
@ -791,7 +664,11 @@ fn getPTable(pd_table: *PDTable, indices: Indices, flags: MemoryFlags, page_allo
|
|||||||
return ToImplementError.page_size;
|
return ToImplementError.page_size;
|
||||||
} else break :physical_address_blk PhysicalAddress.new(unpackAddress(entry_value));
|
} else break :physical_address_blk PhysicalAddress.new(unpackAddress(entry_value));
|
||||||
} else {
|
} else {
|
||||||
const entry_allocation = try page_allocator.allocatePageTable(.{ .level = .PT, .user = flags.user });
|
const entry_allocation = try page_allocator.allocatePageTable(.{
|
||||||
|
.level = .PT,
|
||||||
|
.user = flags.user,
|
||||||
|
.virtual_address = indexed,
|
||||||
|
});
|
||||||
|
|
||||||
entry_pointer.* = PDTE{
|
entry_pointer.* = PDTE{
|
||||||
.present = true,
|
.present = true,
|
||||||
@ -811,21 +688,6 @@ const half_entry_count = (@sizeOf(PML4Table) / @sizeOf(PML4TE)) / 2;
|
|||||||
|
|
||||||
const needed_physical_memory_for_bootstrapping_cpu_driver_address_space = @sizeOf(PML4Table) + @sizeOf(PDPTable) * 256;
|
const needed_physical_memory_for_bootstrapping_cpu_driver_address_space = @sizeOf(PML4Table) + @sizeOf(PDPTable) * 256;
|
||||||
|
|
||||||
pub fn computeIndices(virtual_address: u64) Indices {
|
|
||||||
var indices: Indices = undefined;
|
|
||||||
var va = virtual_address;
|
|
||||||
va = va >> 12;
|
|
||||||
indices[3] = @as(u9, @truncate(va));
|
|
||||||
va = va >> 9;
|
|
||||||
indices[2] = @as(u9, @truncate(va));
|
|
||||||
va = va >> 9;
|
|
||||||
indices[1] = @as(u9, @truncate(va));
|
|
||||||
va = va >> 9;
|
|
||||||
indices[0] = @as(u9, @truncate(va));
|
|
||||||
|
|
||||||
return indices;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn newFlags(general_flags: Mapping.Flags) MemoryFlags {
|
pub inline fn newFlags(general_flags: Mapping.Flags) MemoryFlags {
|
||||||
return MemoryFlags{
|
return MemoryFlags{
|
||||||
.write = general_flags.write,
|
.write = general_flags.write,
|
||||||
@ -856,28 +718,15 @@ pub const MemoryFlags = packed struct(u64) {
|
|||||||
|
|
||||||
const address_mask: u64 = 0x0000_00ff_ffff_f000;
|
const address_mask: u64 = 0x0000_00ff_ffff_f000;
|
||||||
|
|
||||||
pub const Level = Level4;
|
|
||||||
|
|
||||||
pub const Level4 = enum(u2) {
|
|
||||||
PML4 = 0,
|
|
||||||
PDP = 1,
|
|
||||||
PD = 2,
|
|
||||||
PT = 3,
|
|
||||||
|
|
||||||
pub const count = lib.enumCount(@This());
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Level5 = enum(u3) {};
|
|
||||||
|
|
||||||
pub fn EntryTypeMapSize(comptime page_size: comptime_int) usize {
|
pub fn EntryTypeMapSize(comptime page_size: comptime_int) usize {
|
||||||
return switch (Level) {
|
return switch (paging.Level) {
|
||||||
Level4 => switch (page_size) {
|
paging.Level4 => switch (page_size) {
|
||||||
lib.arch.valid_page_sizes[0] => 4,
|
lib.arch.valid_page_sizes[0] => 4,
|
||||||
lib.arch.valid_page_sizes[1] => 3,
|
lib.arch.valid_page_sizes[1] => 3,
|
||||||
lib.arch.valid_page_sizes[2] => 2,
|
lib.arch.valid_page_sizes[2] => 2,
|
||||||
else => @compileError("Unknown page size"),
|
else => @compileError("Unknown page size"),
|
||||||
},
|
},
|
||||||
Level5 => @compileError("TODO"),
|
paging.Level5 => @compileError("TODO"),
|
||||||
else => @compileError("unreachable"),
|
else => @compileError("unreachable"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -886,28 +735,28 @@ pub fn EntryTypeMap(comptime page_size: comptime_int) [EntryTypeMapSize(page_siz
|
|||||||
const map_size = EntryTypeMapSize(page_size);
|
const map_size = EntryTypeMapSize(page_size);
|
||||||
const Result = [map_size]type;
|
const Result = [map_size]type;
|
||||||
var result: Result = undefined;
|
var result: Result = undefined;
|
||||||
switch (Level) {
|
switch (paging.Level) {
|
||||||
Level4, Level5 => {
|
paging.Level4, paging.Level5 => {
|
||||||
if (@hasField(Level, "pml5")) {
|
if (@hasField(paging.Level, "pml5")) {
|
||||||
@compileError("TODO: type_map[@enumToInt(Level.PML5)] =");
|
@compileError("TODO: type_map[@enumToInt(Level.PML5)] =");
|
||||||
}
|
}
|
||||||
|
|
||||||
result[@intFromEnum(Level.PML4)] = PML4TE;
|
result[@intFromEnum(paging.Level.PML4)] = PML4TE;
|
||||||
|
|
||||||
if (page_size == lib.arch.valid_page_sizes[2]) {
|
if (page_size == lib.arch.valid_page_sizes[2]) {
|
||||||
assert(map_size == 2 + @intFromBool(Level == Level5));
|
assert(map_size == 2 + @intFromBool(paging.Level == paging.Level5));
|
||||||
result[@intFromEnum(Level.PDP)] = PDPTE_1GB;
|
result[@intFromEnum(paging.Level.PDP)] = PDPTE_1GB;
|
||||||
} else {
|
} else {
|
||||||
result[@intFromEnum(Level.PDP)] = PDPTE;
|
result[@intFromEnum(paging.Level.PDP)] = PDPTE;
|
||||||
|
|
||||||
if (page_size == lib.arch.valid_page_sizes[1]) {
|
if (page_size == lib.arch.valid_page_sizes[1]) {
|
||||||
assert(map_size == @as(usize, 3) + @intFromBool(Level == Level5));
|
assert(map_size == @as(usize, 3) + @intFromBool(paging.Level == paging.Level5));
|
||||||
result[@intFromEnum(Level.PD)] = PDTE_2MB;
|
result[@intFromEnum(paging.Level.PD)] = PDTE_2MB;
|
||||||
} else {
|
} else {
|
||||||
assert(page_size == lib.arch.valid_page_sizes[0]);
|
assert(page_size == lib.arch.valid_page_sizes[0]);
|
||||||
|
|
||||||
result[@intFromEnum(Level.PD)] = PDTE;
|
result[@intFromEnum(paging.Level.PD)] = PDTE;
|
||||||
result[@intFromEnum(Level.PT)] = PTE;
|
result[@intFromEnum(paging.Level.PT)] = PTE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1073,16 +922,7 @@ pub const PTE = packed struct(u64) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const PML4Table = [page_table_entry_count]PML4TE;
|
pub const PML4Table = [paging.page_table_entry_count]PML4TE;
|
||||||
pub const PDPTable = [page_table_entry_count]PDPTE;
|
pub const PDPTable = [paging.page_table_entry_count]PDPTE;
|
||||||
pub const PDTable = [page_table_entry_count]PDTE;
|
pub const PDTable = [paging.page_table_entry_count]PDTE;
|
||||||
pub const PTable = [page_table_entry_count]PTE;
|
pub const PTable = [paging.page_table_entry_count]PTE;
|
||||||
pub const page_table_entry_size = @sizeOf(u64);
|
|
||||||
pub const page_table_size = lib.arch.valid_page_sizes[0];
|
|
||||||
pub const page_table_entry_count = @divExact(page_table_size, page_table_entry_size);
|
|
||||||
pub const page_table_alignment = page_table_size;
|
|
||||||
|
|
||||||
comptime {
|
|
||||||
assert(page_table_alignment == page_table_size);
|
|
||||||
assert(page_table_size == lib.arch.valid_page_sizes[0]);
|
|
||||||
}
|
|
||||||
|
118
src/user.zig
118
src/user.zig
@ -4,24 +4,20 @@ const assert = lib.assert;
|
|||||||
const ExecutionMode = lib.Syscall.ExecutionMode;
|
const ExecutionMode = lib.Syscall.ExecutionMode;
|
||||||
|
|
||||||
const birth = @import("birth");
|
const birth = @import("birth");
|
||||||
const capabilities = birth.capabilities;
|
pub const Command = birth.interface.Command;
|
||||||
pub const Syscall = birth.capabilities.Syscall;
|
pub const Interface = birth.interface.Descriptor;
|
||||||
|
pub const Scheduler = birth.Scheduler;
|
||||||
|
|
||||||
pub const arch = @import("user/arch.zig");
|
pub const arch = @import("user/arch.zig");
|
||||||
|
pub const capabilities = @import("user/capabilities.zig");
|
||||||
const core_state = @import("user/core_state.zig");
|
const core_state = @import("user/core_state.zig");
|
||||||
pub const CoreState = core_state.CoreState;
|
pub const CoreState = core_state.CoreState;
|
||||||
pub const PinnedState = core_state.PinnedState;
|
|
||||||
pub const libc = @import("user/libc.zig");
|
pub const libc = @import("user/libc.zig");
|
||||||
pub const thread = @import("user/thread.zig");
|
pub const thread = @import("user/thread.zig");
|
||||||
|
pub const Thread = thread.Thread;
|
||||||
pub const process = @import("user/process.zig");
|
pub const process = @import("user/process.zig");
|
||||||
const vas = @import("user/virtual_address_space.zig");
|
pub const Virtual = @import("user/virtual.zig");
|
||||||
const VirtualAddress = lib.VirtualAddress;
|
const VirtualAddress = lib.VirtualAddress;
|
||||||
pub const VirtualAddressSpace = vas.VirtualAddressSpace;
|
|
||||||
pub const MMUAwareVirtualAddressSpace = vas.MMUAwareVirtualAddressSpace;
|
|
||||||
|
|
||||||
pub const PhysicalMap = @import("user/physical_map.zig").PhysicalMap;
|
|
||||||
pub const PhysicalMemoryRegion = @import("user/physical_memory_region.zig").PhysicalMemoryRegion;
|
|
||||||
pub const SlotAllocator = @import("user/slot_allocator.zig").SlotAllocator;
|
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
@export(arch._start, .{ .linkage = .Strong, .name = "_start" });
|
@export(arch._start, .{ .linkage = .Strong, .name = "_start" });
|
||||||
@ -29,8 +25,8 @@ comptime {
|
|||||||
|
|
||||||
pub const writer = lib.Writer(void, Writer.Error, Writer.write){ .context = {} };
|
pub const writer = lib.Writer(void, Writer.Error, Writer.write){ .context = {} };
|
||||||
const Writer = extern struct {
|
const Writer = extern struct {
|
||||||
const syscall = Syscall(.io, .log);
|
const syscall = Interface(.io, .log);
|
||||||
const Error = Writer.syscall.ErrorSet.Error;
|
const Error = Writer.syscall.Error;
|
||||||
|
|
||||||
fn write(_: void, bytes: []const u8) Error!usize {
|
fn write(_: void, bytes: []const u8) Error!usize {
|
||||||
const result = try Writer.syscall.blocking(bytes);
|
const result = try Writer.syscall.blocking(bytes);
|
||||||
@ -52,22 +48,16 @@ pub fn zigPanic(message: []const u8, _: ?*lib.StackTrace, _: ?usize) noreturn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn panic(comptime format: []const u8, arguments: anytype) noreturn {
|
pub fn panic(comptime format: []const u8, arguments: anytype) noreturn {
|
||||||
lib.log.scoped(.PANIC).err(format, arguments);
|
var buffer: [0x100]u8 = undefined;
|
||||||
|
const message: []const u8 = lib.bufPrint(&buffer, format, arguments) catch "Failed to get panic message!";
|
||||||
while (true) {
|
while (true) {
|
||||||
Syscall(.process, .exit).blocking(false) catch |err| log.err("Exit failed: {}", .{err});
|
Interface(.process, .panic).blocking(.{
|
||||||
|
.message = message,
|
||||||
|
.exit_code = 1,
|
||||||
|
}) catch |err| log.err("Exit failed: {}", .{err});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const Scheduler = extern struct {
|
|
||||||
time_slice: u32,
|
|
||||||
core_id: u32,
|
|
||||||
core_state: CoreState,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub inline fn currentScheduler() *Scheduler {
|
|
||||||
return arch.currentScheduler();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn schedulerInitDisabled(scheduler: *arch.Scheduler) void {
|
fn schedulerInitDisabled(scheduler: *arch.Scheduler) void {
|
||||||
// Architecture-specific initialization
|
// Architecture-specific initialization
|
||||||
scheduler.generic.time_slice = 1;
|
scheduler.generic.time_slice = 1;
|
||||||
@ -75,18 +65,38 @@ fn schedulerInitDisabled(scheduler: *arch.Scheduler) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub var is_init = false;
|
pub var is_init = false;
|
||||||
pub var command_buffer: birth.CommandBuffer = undefined;
|
pub var command_buffer: Command.Buffer = undefined;
|
||||||
|
const entry_count = 50;
|
||||||
|
|
||||||
pub export fn start(scheduler: *arch.Scheduler, arg_init: bool) callconv(.C) noreturn {
|
const CommandBufferCreateError = error{
|
||||||
|
invalid_entry_count,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn createCommandBuffer(options: Command.Buffer.CreateOptions) !Command.Buffer {
|
||||||
|
// TODO: allow kernel to chop slices of memories
|
||||||
|
try capabilities.setupCommandFrame(Command.Submission, options.submission_entry_count);
|
||||||
|
try capabilities.setupCommandFrame(Command.Completion, options.completion_entry_count);
|
||||||
|
@panic("TODO: createCommandBuffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub export fn start(scheduler: *Scheduler, arg_init: bool) callconv(.C) noreturn {
|
||||||
assert(arg_init);
|
assert(arg_init);
|
||||||
is_init = arg_init;
|
is_init = arg_init;
|
||||||
if (is_init) {
|
if (is_init) {
|
||||||
assert(scheduler.common.generic.setup_stack_lock.load(.Monotonic));
|
assert(scheduler.common.setup_stack_lock.load(.Monotonic));
|
||||||
}
|
}
|
||||||
assert(scheduler.common.generic.disabled);
|
|
||||||
scheduler.initDisabled();
|
initialize() catch |err| panic("Failed to initialize: {}", .{err});
|
||||||
// command_buffer = Syscall(.cpu, .get_command_buffer).blocking(&command_buffer) catch @panic("Unable to get command buffer");
|
@import("root").main() catch |err| panic("Failed to execute main: {}", .{err});
|
||||||
Syscall(.cpu, .shutdown).blocking({}) catch unreachable;
|
|
||||||
|
while (true) {
|
||||||
|
@panic("TODO: after main");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn initialize() !void {
|
||||||
|
currentScheduler().initializeAllocator();
|
||||||
|
_ = try Virtual.AddressSpace.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
// export fn birthInitializeDisabled(scheduler: *arch.Scheduler, arg_init: bool) callconv(.C) noreturn {
|
// export fn birthInitializeDisabled(scheduler: *arch.Scheduler, arg_init: bool) callconv(.C) noreturn {
|
||||||
@ -98,45 +108,7 @@ pub export fn start(scheduler: *arch.Scheduler, arg_init: bool) callconv(.C) nor
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// Barrelfish: vregion
|
// Barrelfish: vregion
|
||||||
pub const VirtualMemoryRegion = extern struct {
|
pub inline fn currentScheduler() *birth.Scheduler {
|
||||||
virtual_address_space: *VirtualAddressSpace,
|
const result = arch.maybeCurrentScheduler().?;
|
||||||
physical_region: *PhysicalMemoryRegion,
|
return result;
|
||||||
offset: usize,
|
}
|
||||||
size: usize,
|
|
||||||
address: VirtualAddress,
|
|
||||||
flags: Flags,
|
|
||||||
next: ?*VirtualMemoryRegion = null,
|
|
||||||
|
|
||||||
pub const Flags = packed struct(u8) {
|
|
||||||
read: bool = false,
|
|
||||||
write: bool = false,
|
|
||||||
execute: bool = false,
|
|
||||||
cache_disabled: bool = false,
|
|
||||||
preferred_page_size: u2 = 0,
|
|
||||||
write_combining: bool = false,
|
|
||||||
reserved: u1 = 0,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const MoreCore = extern struct {
|
|
||||||
const InitializationError = error{
|
|
||||||
invalid_page_size,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn init(page_size: usize) InitializationError!void {
|
|
||||||
blk: inline for (lib.arch.valid_page_sizes) |valid_page_size| {
|
|
||||||
if (valid_page_size == page_size) break :blk;
|
|
||||||
} else {
|
|
||||||
return InitializationError.invalid_page_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
const morecore_state = process.getMoreCoreState();
|
|
||||||
morecore_state.mmu_state = try MMUAwareVirtualAddressSpace.initAligned(SlotAllocator.getDefault(), lib.arch.valid_page_sizes[1], lib.arch.valid_page_sizes[0], .{ .read = true, .write = true });
|
|
||||||
|
|
||||||
@panic("TODO: MoreCore.init");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const State = extern struct {
|
|
||||||
mmu_state: MMUAwareVirtualAddressSpace,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
@ -16,28 +16,6 @@ const SlotAllocator = user.SlotAllocator;
|
|||||||
const Thread = user.Thread;
|
const Thread = user.Thread;
|
||||||
const VirtualAddressSpace = user.VirtualAddressSpace;
|
const VirtualAddressSpace = user.VirtualAddressSpace;
|
||||||
|
|
||||||
pub const Scheduler = extern struct {
|
|
||||||
common: birth.arch.UserScheduler,
|
|
||||||
generic: user.Scheduler,
|
|
||||||
|
|
||||||
pub fn initDisabled(scheduler: *Scheduler) void {
|
|
||||||
_ = scheduler;
|
|
||||||
// TODO:
|
|
||||||
// *set entry points?
|
|
||||||
// *set tls registers?
|
|
||||||
}
|
|
||||||
|
|
||||||
pub noinline fn restore(scheduler: *Scheduler, register_arena: *const RegisterArena) noreturn {
|
|
||||||
assert(scheduler.common.generic.disabled);
|
|
||||||
assert(scheduler.common.generic.has_work);
|
|
||||||
|
|
||||||
assert(register_arena.registers.rip > lib.arch.valid_page_sizes[0]);
|
|
||||||
assert(register_arena.registers.rflags.IF and register_arena.registers.rflags.reserved0);
|
|
||||||
|
|
||||||
register_arena.contextSwitch();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// CRT0
|
// CRT0
|
||||||
pub fn _start() callconv(.Naked) noreturn {
|
pub fn _start() callconv(.Naked) noreturn {
|
||||||
asm volatile (
|
asm volatile (
|
||||||
@ -46,8 +24,6 @@ pub fn _start() callconv(.Naked) noreturn {
|
|||||||
:
|
:
|
||||||
: [startFunction] "r" (user.start),
|
: [startFunction] "r" (user.start),
|
||||||
);
|
);
|
||||||
|
|
||||||
unreachable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn setInitialState(register_arena: *RegisterArena, entry: VirtualAddress, stack_virtual_address: VirtualAddress, arguments: birth.syscall.Arguments) void {
|
pub inline fn setInitialState(register_arena: *RegisterArena, entry: VirtualAddress, stack_virtual_address: VirtualAddress, arguments: birth.syscall.Arguments) void {
|
||||||
@ -80,37 +56,3 @@ pub inline fn maybeCurrentScheduler() ?*user.Scheduler {
|
|||||||
: "memory"
|
: "memory"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn currentScheduler() *user.Scheduler {
|
|
||||||
const result = maybeCurrentScheduler().?;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This is an interface to user.PhysicalMap, providing the architecture-specific functionality
|
|
||||||
pub const PhysicalMapInterface = struct {
|
|
||||||
pub fn determineAddress(physical_map: *PhysicalMap, physical_memory_region: PhysicalMemoryRegion, alignment: usize) !VirtualAddress {
|
|
||||||
_ = physical_memory_region;
|
|
||||||
_ = alignment;
|
|
||||||
assert(physical_map.virtual_address_space.regions != null);
|
|
||||||
log.debug("PMap: 0x{x}", .{@intFromPtr(physical_map.virtual_address_space.regions)});
|
|
||||||
log.debug("PMap: {?}", .{physical_map.virtual_address_space.regions});
|
|
||||||
@panic("TODO: PhysicalMapInterface.determineAddress");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn initializeCurrent(physical_map: *PhysicalMap) !void {
|
|
||||||
_ = physical_map;
|
|
||||||
log.warn("TODO: PhysicalMapInterface.initializeCurrent", .{});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init(virtual_address_space: *VirtualAddressSpace, page_level: u3, slot_allocator: *SlotAllocator) !PhysicalMap {
|
|
||||||
var result = PhysicalMap{
|
|
||||||
.virtual_address_space = virtual_address_space,
|
|
||||||
.slot_allocator = slot_allocator,
|
|
||||||
};
|
|
||||||
_ = page_level;
|
|
||||||
|
|
||||||
try result.initPageTableManagement();
|
|
||||||
|
|
||||||
@panic("TODO: PhysicalMap.init");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
@ -6,7 +6,7 @@ PHDRS {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SECTIONS {
|
SECTIONS {
|
||||||
. = 0x600000;
|
. = 0x200000;
|
||||||
. = ALIGN(4K);
|
. = ALIGN(4K);
|
||||||
.text . : {
|
.text . : {
|
||||||
*(.text*)
|
*(.text*)
|
||||||
|
@ -1,20 +1,85 @@
|
|||||||
const lib = @import("lib");
|
const lib = @import("lib");
|
||||||
|
const log = lib.log;
|
||||||
const assert = lib.assert;
|
const assert = lib.assert;
|
||||||
const birth = @import("birth");
|
const birth = @import("birth");
|
||||||
|
const user = @import("user");
|
||||||
|
const Interface = user.Interface;
|
||||||
|
|
||||||
|
const Command = birth.interface.Command;
|
||||||
|
|
||||||
// TODO: ref
|
// TODO: ref
|
||||||
pub fn frameCreate(ref: usize, bytes: usize) !usize {
|
pub fn frameCreate(bytes: usize) !birth.capabilities.RAM {
|
||||||
return mappableCapabilityCreate(ref, .cpu_memory, bytes);
|
return mappableCapabilityCreate(.cpu_memory, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mappableCapabilityCreate(ref: usize, mappable_capability: birth.capabilities.Type.Mappable, bytes: usize) !usize {
|
const CommandBufferFrameType = enum {
|
||||||
_ = mappable_capability;
|
command_buffer_completion,
|
||||||
_ = ref;
|
command_buffer_submission,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn setupCommandFrame(comptime QueueType: type, entry_count: usize) !void {
|
||||||
|
assert(entry_count > 0);
|
||||||
|
comptime assert(@alignOf(QueueType) <= @sizeOf(QueueType.Header));
|
||||||
|
const total_size = lib.alignForward(usize, @sizeOf(QueueType.Header) + entry_count * @sizeOf(QueueType), lib.arch.valid_page_sizes[0]);
|
||||||
|
const capability = switch (QueueType) {
|
||||||
|
Command.Submission => .command_buffer_submission,
|
||||||
|
Command.Completion => .command_buffer_completion,
|
||||||
|
else => @compileError("Unexpected type"),
|
||||||
|
};
|
||||||
|
|
||||||
|
const allocation = try Interface(.ram, .allocate).blocking(total_size);
|
||||||
|
const dst_cap_frame = try retype(@bitCast(allocation), capability);
|
||||||
|
const flags = .{
|
||||||
|
.write = QueueType == Command.Submission,
|
||||||
|
.execute = false,
|
||||||
|
};
|
||||||
|
_ = try Interface(capability, .map).blocking(.{
|
||||||
|
.frame = dst_cap_frame,
|
||||||
|
.flags = flags,
|
||||||
|
});
|
||||||
|
|
||||||
|
@panic("TODO: setup frame");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mappableCapabilityCreate(capability: birth.capabilities.Type.Mappable, bytes: usize) !birth.capabilities.RAM {
|
||||||
assert(bytes > 0);
|
assert(bytes > 0);
|
||||||
|
|
||||||
|
return RamDescendant.create(capability, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ramDescendantCreate(
|
const Ram = extern struct {
|
||||||
ref: usize,
|
pub fn allocate(size: usize) !usize {
|
||||||
) !usize {
|
_ = size;
|
||||||
_ = ref;
|
log.err("TODO: allocate", .{});
|
||||||
|
return error.not_implemented;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const RamDescendant = extern struct {
|
||||||
|
capability: usize,
|
||||||
|
size: usize,
|
||||||
|
|
||||||
|
pub fn create(capability: birth.capabilities.Type.Mappable, size: usize) !birth.capabilities.RAM {
|
||||||
|
const allocation = try Interface(.ram, .allocate).blocking(size);
|
||||||
|
const generic_capability = switch (capability) {
|
||||||
|
inline else => |mappable_cap| @field(birth.interface.Capability, @tagName(mappable_cap)),
|
||||||
|
};
|
||||||
|
const result = try retype(@bitCast(allocation), generic_capability);
|
||||||
|
|
||||||
|
// TODO: check if the previous capability needs to be deleted (because maybe it should be deleted at the retype operation
|
||||||
|
// try destroy(@bitCast(allocation));
|
||||||
|
return @bitCast(result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: make this more complex and generic to handle all cases
|
||||||
|
pub fn retype(source: birth.interface.Reference, capability: birth.interface.Capability) !birth.interface.Reference {
|
||||||
|
const new_reference = try Interface(.ram, .retype).blocking(.{ .source = @bitCast(source), .destination = capability });
|
||||||
|
return new_reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(capability: birth.capabilities.Reference) !void {
|
||||||
|
_ = capability;
|
||||||
|
log.err("TODO: destroy", .{});
|
||||||
|
return error.not_implemented;
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,6 @@
|
|||||||
const user = @import("user");
|
const user = @import("user");
|
||||||
const MoreCore = user.MoreCore;
|
|
||||||
const PhysicalMap = user.PhysicalMap;
|
const PhysicalMap = user.PhysicalMap;
|
||||||
const PhysicalMemoryRegion = user.PhysicalMemoryRegion;
|
const PhysicalMemoryRegion = user.PhysicalMemoryRegion;
|
||||||
const SlotAllocator = user.SlotAllocator;
|
const SlotAllocator = user.SlotAllocator;
|
||||||
const VirtualAddressSpace = user.VirtualAddressSpace;
|
const VirtualAddressSpace = user.VirtualAddressSpace;
|
||||||
const VirtualMemoryRegion = user.VirtualMemoryRegion;
|
const VirtualMemoryRegion = user.VirtualMemoryRegion;
|
||||||
|
|
||||||
pub const PagingState = extern struct {
|
|
||||||
virtual_address_space: VirtualAddressSpace,
|
|
||||||
physical_map: PhysicalMap,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const PinnedState = extern struct {
|
|
||||||
physical_memory_region: PhysicalMemoryRegion.Pinned,
|
|
||||||
virtual_memory_region: VirtualMemoryRegion,
|
|
||||||
offset: usize,
|
|
||||||
// TODO: lists
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const CoreState = extern struct {
|
|
||||||
paging: PagingState,
|
|
||||||
slot_allocator: SlotAllocator.State,
|
|
||||||
virtual_address_space: VirtualAddressSpace.State,
|
|
||||||
pinned: PinnedState,
|
|
||||||
more_core: MoreCore.State,
|
|
||||||
};
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
const user = @import("user");
|
const user = @import("user");
|
||||||
|
|
||||||
pub export fn malloc(size: usize) ?*anyopaque {
|
pub export fn malloc(size: usize) ?*anyopaque {
|
||||||
const morecore_state = user.process.getMoreCoreState();
|
_ = size;
|
||||||
const result = morecore_state.mmu_state.map(size) catch return null;
|
@panic("TODO: malloc");
|
||||||
return result.ptr;
|
|
||||||
}
|
}
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
const lib = @import("lib");
|
|
||||||
const assert = lib.assert;
|
|
||||||
const log = lib.log.scoped(.MMUAwareVirtualAddressSpace);
|
|
||||||
|
|
||||||
const user = @import("user");
|
|
||||||
const PhysicalMemoryRegion = user.PhysicalMemoryRegion;
|
|
||||||
const SlotAllocator = user.SlotAllocator;
|
|
||||||
const VirtualMemoryRegion = user.VirtualMemoryRegion;
|
|
||||||
|
|
||||||
pub const MMUAwareVirtualAddressSpace = extern struct {
|
|
||||||
size: usize,
|
|
||||||
alignment: usize,
|
|
||||||
consumed: usize = 0,
|
|
||||||
/// This is a index into the architecture-specific page sizes
|
|
||||||
page_size: u8,
|
|
||||||
slot_allocator: *SlotAllocator,
|
|
||||||
physical_memory_region: PhysicalMemoryRegion.Anonymous,
|
|
||||||
virtual_memory_region: VirtualMemoryRegion,
|
|
||||||
// struct vregion vregion; ///< Needs just one vregion
|
|
||||||
// struct memobj_anon memobj; ///< Needs just one memobj
|
|
||||||
// lvaddr_t offset; ///< Offset of free space in anon
|
|
||||||
// lvaddr_t mapoffset; ///< Offset into the anon that has been mapped in
|
|
||||||
|
|
||||||
pub fn init(size: usize) !MMUAwareVirtualAddressSpace {
|
|
||||||
const slot_allocator = SlotAllocator.getDefault();
|
|
||||||
const alignment = lib.arch.valid_page_sizes[0];
|
|
||||||
return initAligned(slot_allocator, size, alignment, .{ .write = true });
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn initAligned(slot_allocator: *SlotAllocator, size: usize, alignment: usize, flags: VirtualMemoryRegion.Flags) !MMUAwareVirtualAddressSpace {
|
|
||||||
assert(flags.preferred_page_size < lib.arch.valid_page_sizes.len);
|
|
||||||
var result = MMUAwareVirtualAddressSpace{
|
|
||||||
.size = size,
|
|
||||||
.alignment = alignment,
|
|
||||||
.page_size = flags.preferred_page_size,
|
|
||||||
.slot_allocator = slot_allocator,
|
|
||||||
.physical_memory_region = try PhysicalMemoryRegion.Anonymous.new(size),
|
|
||||||
.virtual_memory_region = undefined,
|
|
||||||
};
|
|
||||||
// TODO: fix this API
|
|
||||||
result.virtual_memory_region = try user.process.getVirtualAddressSpace().mapAligned(result.physical_memory_region.getGeneric().*, 0, size, alignment, flags);
|
|
||||||
|
|
||||||
// TODO: create memobj
|
|
||||||
// TODO: map memobj into vregion
|
|
||||||
|
|
||||||
@panic("TODO: MMUAwareVirtualAddressSpace.initAligned");
|
|
||||||
}
|
|
||||||
|
|
||||||
const Error = error{
|
|
||||||
alignment,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn map(virtual_address_space: *MMUAwareVirtualAddressSpace, size: usize) ![]u8 {
|
|
||||||
if (!lib.isAligned(size, lib.arch.valid_page_sizes[0])) {
|
|
||||||
return error.alignment;
|
|
||||||
}
|
|
||||||
_ = virtual_address_space;
|
|
||||||
log.warn("[map] TODO: slot allocation", .{});
|
|
||||||
//virtual_address_space.slot_allocator.allocate();
|
|
||||||
@panic("TODO: MMUAwareVirtualAddressSpace.map");
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,25 +0,0 @@
|
|||||||
const lib = @import("lib");
|
|
||||||
const log = lib.log.scoped(.PhysicalMap);
|
|
||||||
|
|
||||||
const user = @import("user");
|
|
||||||
const SlotAllocator = user.SlotAllocator;
|
|
||||||
const VirtualAddressSpace = user.VirtualAddressSpace;
|
|
||||||
|
|
||||||
pub const PhysicalMap = extern struct {
|
|
||||||
virtual_address_space: *VirtualAddressSpace,
|
|
||||||
slot_allocator: *SlotAllocator,
|
|
||||||
|
|
||||||
pub usingnamespace user.arch.PhysicalMapInterface;
|
|
||||||
|
|
||||||
pub fn initPageTableManagement(physical_map: *PhysicalMap) !void {
|
|
||||||
const current_physical_map = user.process.getPhysicalMap();
|
|
||||||
log.debug("CURR: 0x{x}. PHYS: 0x{x}", .{ @intFromPtr(current_physical_map), @intFromPtr(physical_map) });
|
|
||||||
if (current_physical_map == physical_map) {
|
|
||||||
@panic("TODO: if");
|
|
||||||
} else {
|
|
||||||
log.warn("TODO: slab_init", .{});
|
|
||||||
_ = user.libc.malloc(lib.arch.valid_page_sizes[0]);
|
|
||||||
@panic("TODO: else");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,81 +0,0 @@
|
|||||||
const lib = @import("lib");
|
|
||||||
const assert = lib.assert;
|
|
||||||
const log = lib.log.scoped(.PhysicalMemoryRegion);
|
|
||||||
|
|
||||||
// Barrelfish: memobj
|
|
||||||
pub const PhysicalMemoryRegion = extern struct {
|
|
||||||
size: usize,
|
|
||||||
type: Type,
|
|
||||||
|
|
||||||
pub const Type = enum(u8) {
|
|
||||||
anonymous = 0,
|
|
||||||
one_frame = 1,
|
|
||||||
pinned = 3,
|
|
||||||
//one_frame_lazy,
|
|
||||||
//one_frame_one_map,
|
|
||||||
// vfs,
|
|
||||||
// fixed,
|
|
||||||
// numa,
|
|
||||||
// append,
|
|
||||||
|
|
||||||
fn map(t: Type) type {
|
|
||||||
return switch (t) {
|
|
||||||
.anonymous => Anonymous,
|
|
||||||
.one_frame => OneFrame,
|
|
||||||
.pinned => Pinned,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Anonymous = extern struct {
|
|
||||||
region: PhysicalMemoryRegion,
|
|
||||||
|
|
||||||
pub usingnamespace Interface(@This());
|
|
||||||
|
|
||||||
pub fn new(size: usize) !Anonymous {
|
|
||||||
const result = Anonymous{
|
|
||||||
.region = .{
|
|
||||||
.size = size,
|
|
||||||
.type = .anonymous,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
log.warn("[Anonymous.new] TODO: initialize memory", .{});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const OneFrame = extern struct {
|
|
||||||
pub usingnamespace Interface(@This());
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Pinned = extern struct {
|
|
||||||
region: PhysicalMemoryRegion,
|
|
||||||
pub usingnamespace Interface(@This());
|
|
||||||
|
|
||||||
pub fn new(size: usize) !Pinned {
|
|
||||||
const result = Pinned{
|
|
||||||
.region = .{
|
|
||||||
.size = size,
|
|
||||||
.type = .pinned,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
log.warn("[Pinned.new] TODO: initialize memory", .{});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
fn Interface(comptime PhysicalMemoryRegionType: type) type {
|
|
||||||
assert(@hasField(PhysicalMemoryRegionType, "region"));
|
|
||||||
assert(@TypeOf(@field(@as(PhysicalMemoryRegionType, undefined), "region")) == PhysicalMemoryRegion);
|
|
||||||
|
|
||||||
return extern struct {
|
|
||||||
pub inline fn getGeneric(r: *PhysicalMemoryRegionType) *PhysicalMemoryRegion {
|
|
||||||
return &r.region;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
@ -9,10 +9,12 @@ pub const std_options = user.std_options;
|
|||||||
export var core_id: u32 = 0;
|
export var core_id: u32 = 0;
|
||||||
|
|
||||||
pub fn main() !noreturn {
|
pub fn main() !noreturn {
|
||||||
core_id = try Syscall(.cpu, .get_core_id).blocking({});
|
@panic("TODO: main");
|
||||||
user.currentScheduler().core_id = core_id;
|
|
||||||
log.debug("Hello world! User space initialization from core #{}", .{core_id});
|
// core_id = try Syscall(.cpu, .get_core_id).blocking({});
|
||||||
const allocation = try Syscall(.cpu_memory, .allocate).blocking(0x1000);
|
// user.currentScheduler().core_id = core_id;
|
||||||
log.debug("Look allocation successful at 0x{x}", .{allocation.value()});
|
// log.debug("Hello world! User space initialization from core #{}", .{core_id});
|
||||||
try Syscall(.cpu, .shutdown).blocking({});
|
// const allocation = try Syscall(.cpu_memory, .allocate).blocking(0x1000);
|
||||||
|
// log.debug("Look allocation successful at 0x{x}", .{allocation.value()});
|
||||||
|
// try Syscall(.cpu, .shutdown).blocking({});
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ pub const std_options = user.std_options;
|
|||||||
|
|
||||||
export var core_id: u32 = 0;
|
export var core_id: u32 = 0;
|
||||||
|
|
||||||
pub fn main() !noreturn {
|
pub fn main() !void {
|
||||||
// core_id = try syscall(.cpu, .get_core_id).blocking({});
|
// core_id = try syscall(.cpu, .get_core_id).blocking({});
|
||||||
// user.currentScheduler().core_id = core_id;
|
// user.currentScheduler().core_id = core_id;
|
||||||
// log.debug("Hello world! User space initialization from core #{}", .{core_id});
|
// log.debug("Hello world! User space initialization from core #{}", .{core_id});
|
||||||
@ -21,5 +21,5 @@ pub fn main() !noreturn {
|
|||||||
// const aligned_bundle_size = lib.alignForward(usize, bundle_size, lib.arch.valid_page_sizes[0]);
|
// const aligned_bundle_size = lib.alignForward(usize, bundle_size, lib.arch.valid_page_sizes[0]);
|
||||||
// const bundle_allocation = try syscall(.cpu_memory, .allocate).blocking(aligned_bundle_size);
|
// const bundle_allocation = try syscall(.cpu_memory, .allocate).blocking(aligned_bundle_size);
|
||||||
// log.debug("Look allocation successful at 0x{x}", .{bundle_allocation.value()});
|
// log.debug("Look allocation successful at 0x{x}", .{bundle_allocation.value()});
|
||||||
try syscall(.cpu, .shutdown).blocking({});
|
// try syscall(.cpu, .shutdown).blocking({});
|
||||||
}
|
}
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
const log = @import("lib").log;
|
|
||||||
const user = @import("user");
|
|
||||||
|
|
||||||
pub const SlotAllocator = extern struct {
|
|
||||||
foo: u32 = 0,
|
|
||||||
|
|
||||||
/// This function is inlined because it's only called once
|
|
||||||
pub inline fn init() !void {
|
|
||||||
log.warn("TODO: implement the whole SlotAllocator.init", .{});
|
|
||||||
const state = user.process.getSlotAllocatorState();
|
|
||||||
const default_allocator = state.default_allocator;
|
|
||||||
_ = default_allocator;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn getDefault() *SlotAllocator {
|
|
||||||
const process_slot_allocator_state = user.process.getSlotAllocatorState();
|
|
||||||
return &process_slot_allocator_state.default_allocator.allocator;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const State = extern struct {
|
|
||||||
default_allocator: MultiSlotAllocator,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const MultiSlotAllocator = extern struct {
|
|
||||||
allocator: SlotAllocator,
|
|
||||||
// TODO:
|
|
||||||
};
|
|
@ -9,48 +9,21 @@ const SlotAllocator = user.SlotAllocator;
|
|||||||
const VirtualAddress = lib.VirtualAddress;
|
const VirtualAddress = lib.VirtualAddress;
|
||||||
const VirtualAddressSpace = user.VirtualAddressSpace;
|
const VirtualAddressSpace = user.VirtualAddressSpace;
|
||||||
|
|
||||||
|
const Thread = birth.Thread;
|
||||||
|
|
||||||
const max_thread_count = 256;
|
const max_thread_count = 256;
|
||||||
|
|
||||||
pub const Thread = extern struct {
|
pub fn initBootstrap(scheduler: *user.arch.Scheduler) noreturn {
|
||||||
self: *Thread,
|
const thread = &scheduler.generic.bootstrap_thread;
|
||||||
previous: ?*Thread,
|
thread.stack = &scheduler.common.generic.setup_stack;
|
||||||
next: ?*Thread,
|
thread.stack_top = @ptrFromInt(@intFromPtr(&scheduler.common.generic.setup_stack) + scheduler.common.generic.setup_stack.len);
|
||||||
stack: [*]u8,
|
|
||||||
stack_top: [*]align(lib.arch.stack_alignment) u8,
|
|
||||||
register_arena: birth.arch.RegisterArena align(lib.arch.stack_alignment),
|
|
||||||
core_id: u32,
|
|
||||||
|
|
||||||
pub fn init(thread: *Thread, scheduler: *user.arch.Scheduler) void {
|
|
||||||
thread.self = thread;
|
|
||||||
thread.previous = null;
|
|
||||||
thread.next = null;
|
|
||||||
thread.core_id = scheduler.generic.core_id;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Mutex = extern struct {
|
|
||||||
locked: bool = false,
|
|
||||||
|
|
||||||
pub inline fn internalLock(mutex: *volatile Mutex) void {
|
|
||||||
mutex.locked = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var static_stack: [0x10000]u8 align(lib.arch.stack_alignment) = undefined;
|
|
||||||
var static_thread: Thread = undefined;
|
|
||||||
var static_thread_lock = Mutex{};
|
|
||||||
|
|
||||||
pub fn initDisabled(scheduler: *user.arch.Scheduler) noreturn {
|
|
||||||
const thread = &static_thread;
|
|
||||||
static_thread_lock.internalLock();
|
|
||||||
thread.stack = &static_stack;
|
|
||||||
thread.stack_top = static_stack[static_stack.len..];
|
|
||||||
thread.init(scheduler);
|
thread.init(scheduler);
|
||||||
|
|
||||||
// TODO: use RAX as parameter?
|
// TODO: use RAX as parameter?
|
||||||
|
|
||||||
user.arch.setInitialState(&thread.register_arena, VirtualAddress.new(bootstrapThread), VirtualAddress.new(thread.stack_top), .{0} ** 6);
|
user.arch.setInitialState(&thread.register_arena, VirtualAddress.new(bootstrapThread), VirtualAddress.new(thread.stack_top), .{0} ** 6);
|
||||||
|
scheduler.generic.enqueueThread(thread);
|
||||||
|
scheduler.generic.current_thread = thread;
|
||||||
scheduler.common.generic.has_work = true;
|
scheduler.common.generic.has_work = true;
|
||||||
|
|
||||||
scheduler.restore(&thread.register_arena);
|
scheduler.restore(&thread.register_arena);
|
||||||
|
100
src/user/virtual.zig
Normal file
100
src/user/virtual.zig
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
const Virtual = @This();
|
||||||
|
|
||||||
|
const birth = @import("birth");
|
||||||
|
const lib = @import("lib");
|
||||||
|
const user = @import("user");
|
||||||
|
|
||||||
|
const assert = lib.assert;
|
||||||
|
const log = lib.log;
|
||||||
|
const SparseArray = lib.data_structures.SparseArray;
|
||||||
|
const VirtualAddress = lib.VirtualAddress;
|
||||||
|
|
||||||
|
const paging = lib.arch.paging;
|
||||||
|
|
||||||
|
const Leaf = birth.interface.Leaf;
|
||||||
|
|
||||||
|
pub const AddressSpace = extern struct {
|
||||||
|
// page_table: PageTable,
|
||||||
|
region: Virtual.AddressSpace.Region = .{},
|
||||||
|
minimum: VirtualAddress = VirtualAddress.new(paging.user_address_space_start),
|
||||||
|
maximum: VirtualAddress = VirtualAddress.new(paging.user_address_space_end),
|
||||||
|
root_page_table: PageTable = .{},
|
||||||
|
page_table_buffer: SparseArray(PageTable) = .{
|
||||||
|
.ptr = undefined,
|
||||||
|
.len = 0,
|
||||||
|
.capacity = 0,
|
||||||
|
},
|
||||||
|
leaf_buffer: SparseArray(Leaf) = .{
|
||||||
|
.ptr = undefined,
|
||||||
|
.len = 0,
|
||||||
|
.capacity = 0,
|
||||||
|
},
|
||||||
|
|
||||||
|
const Region = extern struct {
|
||||||
|
list: Virtual.Region.List = .{},
|
||||||
|
block_count: usize = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn create() !*AddressSpace {
|
||||||
|
const scheduler = user.currentScheduler();
|
||||||
|
const virtual_address_space = try scheduler.fast_allocator.create(AddressSpace);
|
||||||
|
virtual_address_space.* = .{};
|
||||||
|
|
||||||
|
try virtual_address_space.collectPageTables(&virtual_address_space.root_page_table, .{});
|
||||||
|
|
||||||
|
@panic("TODO: create");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collectPageTables(virtual_address_space: *Virtual.AddressSpace, page_table: *PageTable, descriptor: birth.interface.PageTable) !void {
|
||||||
|
try user.Interface(.page_table, .get).blocking(.{
|
||||||
|
.descriptor = descriptor,
|
||||||
|
.buffer = &page_table.children_handles,
|
||||||
|
});
|
||||||
|
|
||||||
|
const allocator = &user.currentScheduler().fast_allocator;
|
||||||
|
|
||||||
|
for (page_table.children_handles, &page_table.indices) |child, *index| {
|
||||||
|
if (child.present) {
|
||||||
|
switch (child.entry_type) {
|
||||||
|
.page_table => {
|
||||||
|
const page_table_index = virtual_address_space.page_table_buffer.len;
|
||||||
|
const new_page_table = try virtual_address_space.page_table_buffer.allocate(allocator);
|
||||||
|
//user.currentScheduler().fast_allocator.create(PageTable);
|
||||||
|
index.* = @intCast(page_table_index);
|
||||||
|
|
||||||
|
try virtual_address_space.collectPageTables(new_page_table, child);
|
||||||
|
},
|
||||||
|
.leaf => {
|
||||||
|
const new_leaf = try virtual_address_space.leaf_buffer.allocate(allocator);
|
||||||
|
index.* = @intCast(virtual_address_space.leaf_buffer.indexOf(new_leaf));
|
||||||
|
try getLeaf(child, new_leaf);
|
||||||
|
log.debug("New leaf: {}", .{new_leaf});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getLeaf(leaf_descriptor: birth.interface.PageTable, leaf: *Leaf) !void {
|
||||||
|
try user.Interface(.page_table, .get_leaf).blocking(.{
|
||||||
|
.descriptor = leaf_descriptor,
|
||||||
|
.buffer = leaf,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Region = extern struct {
|
||||||
|
foo: u32 = 0,
|
||||||
|
|
||||||
|
pub const List = extern struct {
|
||||||
|
regions: [region_count]Region = .{.{}} ** region_count,
|
||||||
|
next: ?*List = null,
|
||||||
|
|
||||||
|
const region_count = 20;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const PageTable = extern struct {
|
||||||
|
children_handles: [512]birth.interface.PageTable = .{.{}} ** 512,
|
||||||
|
indices: [512]u32 = .{0} ** 512,
|
||||||
|
};
|
@ -1,60 +0,0 @@
|
|||||||
const lib = @import("lib");
|
|
||||||
const log = lib.log;
|
|
||||||
|
|
||||||
const user = @import("user");
|
|
||||||
const PhysicalMap = user.PhysicalMap;
|
|
||||||
const PhysicalMemoryRegion = user.PhysicalMemoryRegion;
|
|
||||||
const VirtualMemoryRegion = user.VirtualMemoryRegion;
|
|
||||||
|
|
||||||
pub const MMUAwareVirtualAddressSpace = @import("mmu_aware_virtual_address_space.zig").MMUAwareVirtualAddressSpace;
|
|
||||||
|
|
||||||
pub const VirtualAddressSpace = extern struct {
|
|
||||||
physical_map: *PhysicalMap,
|
|
||||||
// TODO: layout
|
|
||||||
regions: ?*VirtualMemoryRegion = null,
|
|
||||||
|
|
||||||
/// The function is inlined because it's only called once
|
|
||||||
pub inline fn initializeCurrent() !void {
|
|
||||||
log.debug("VirtualAddressSpace.initializeCurrent", .{});
|
|
||||||
const virtual_address_space = user.process.getVirtualAddressSpace();
|
|
||||||
const physical_map = user.process.getPhysicalMap();
|
|
||||||
virtual_address_space.physical_map = physical_map;
|
|
||||||
|
|
||||||
const root_page_level = 0;
|
|
||||||
physical_map.* = try PhysicalMap.init(virtual_address_space, root_page_level, user.process.getSlotAllocator());
|
|
||||||
// This should be an inline call as this the only time this function is called
|
|
||||||
try physical_map.initializeCurrent();
|
|
||||||
|
|
||||||
try virtual_address_space.pinnedInit();
|
|
||||||
|
|
||||||
log.warn("TODO: VirtualAddressSpace.initializeCurrent is incomplete!", .{});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn pinnedInit(virtual_address_space: *VirtualAddressSpace) !void {
|
|
||||||
const pinned_state = user.process.getPinnedState();
|
|
||||||
const pinned_size = 128 * lib.mb;
|
|
||||||
pinned_state.physical_memory_region = try PhysicalMemoryRegion.Pinned.new(pinned_size);
|
|
||||||
|
|
||||||
pinned_state.virtual_memory_region = try virtual_address_space.map(pinned_state.physical_memory_region.getGeneric().*, 0, pinned_size, .{ .write = true });
|
|
||||||
log.warn("TODO: VirtualAddressSpace.pinnedInit", .{});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn map(virtual_address_space: *VirtualAddressSpace, physical_memory_region: PhysicalMemoryRegion, offset: usize, size: usize, flags: VirtualMemoryRegion.Flags) !VirtualMemoryRegion {
|
|
||||||
const alignment = lib.arch.valid_page_sizes[0];
|
|
||||||
return virtual_address_space.mapAligned(physical_memory_region, offset, size, alignment, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mapAligned(virtual_address_space: *VirtualAddressSpace, physical_memory_region: PhysicalMemoryRegion, offset: usize, size: usize, alignment: usize, flags: VirtualMemoryRegion.Flags) !VirtualMemoryRegion {
|
|
||||||
const virtual_address = try virtual_address_space.physical_map.determineAddress(physical_memory_region, alignment);
|
|
||||||
_ = virtual_address;
|
|
||||||
_ = offset;
|
|
||||||
_ = size;
|
|
||||||
_ = flags;
|
|
||||||
@panic("TODO: VirtualAddressSpace.mapAligned");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const State = extern struct {
|
|
||||||
virtual_address_space: VirtualAddressSpace,
|
|
||||||
physical_map: PhysicalMap,
|
|
||||||
};
|
|
||||||
};
|
|
Loading…
x
Reference in New Issue
Block a user