isolation M1: ring 3 + a real /sbin/init, end to end
Ring 3 works: user GDT descriptors (sysret-ready layout), TSS.rsp0, U/S-bit user mappings (W^X preserved), an int 0x80 syscall gate with a mutable trap frame, and a setjmp-style enter/exit path. /sbin/init is a real freestanding Zig binary built from sbin/, shipped on the ESP, loaded by the bootloader (BootInfo.init_base/len), validated and mapped by an in-kernel user-ELF loader, and run at CPL 3 — syscalls: exit, ping, write. Tests: user, user-pf (U/S isolation proof, error code 0x5), init. Suite 27/27. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
7501bd1703
commit
546dd44a2a
@@ -145,6 +145,31 @@ pub fn build(b: *std.Build) void {
|
||||
|
||||
b.installArtifact(exe);
|
||||
|
||||
// --- /sbin/init: the first user-space program ---
|
||||
// Its own tiny freestanding binary, linked at a fixed address inside the
|
||||
// kernel's user region (usermode.zig) and started in ring 3 by the kernel's
|
||||
// user-ELF loader. `.large` because the image base is above 4 GiB — small/
|
||||
// medium code models emit 32-bit absolute relocations that can't reach.
|
||||
// Pinned to ReleaseSmall: the user region gives it a 2 MiB budget and its
|
||||
// size has no reason to track the kernel's optimize mode.
|
||||
const init_exe = b.addExecutable(.{
|
||||
.name = "init",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("sbin/init.zig"),
|
||||
.target = kernel_target,
|
||||
.optimize = .ReleaseSmall,
|
||||
.code_model = .large,
|
||||
.single_threaded = true,
|
||||
.sanitize_c = .off,
|
||||
.stack_check = false,
|
||||
.stack_protector = false,
|
||||
}),
|
||||
});
|
||||
init_exe.setLinkerScript(b.path("sbin/linker.ld"));
|
||||
init_exe.entry = .{ .symbol_name = "_start" };
|
||||
init_exe.image_base = 0x7000_0000_0000;
|
||||
b.installArtifact(init_exe);
|
||||
|
||||
// Boot methods live in src/boot/, one per way of getting the kernel running.
|
||||
// Each is its own binary/entry (a loader is built for its own target); today
|
||||
// that's UEFI for x86-64, with room for e.g. a device-tree path for the Pis.
|
||||
@@ -203,6 +228,10 @@ pub fn build(b: *std.Build) void {
|
||||
const kernel_install = b.addInstallArtifact(exe, .{
|
||||
.dest_dir = .{ .override = .{ .custom = "esp" } },
|
||||
});
|
||||
// The bootloader loads init from sbin/init on the same volume.
|
||||
const init_install = b.addInstallArtifact(init_exe, .{
|
||||
.dest_dir = .{ .override = .{ .custom = "esp/sbin" } },
|
||||
});
|
||||
|
||||
// The firmware needs to write NVRAM, so give it a writable copy of the vars.
|
||||
const vars_copy = b.addSystemCommand(&.{ "cp", "-f", ovmf_vars });
|
||||
@@ -239,6 +268,7 @@ pub fn build(b: *std.Build) void {
|
||||
run_efi.addArgs(&.{ "-serial", b.fmt("file:{s}", .{serial_log}) });
|
||||
run_efi.step.dependOn(&efi_install.step);
|
||||
run_efi.step.dependOn(&kernel_install.step);
|
||||
run_efi.step.dependOn(&init_install.step);
|
||||
|
||||
const run_efi_step = b.step("run-x86-64", "Boot the x86-64 kernel in QEMU (UEFI/OVMF); serial0 is logged to zig-out/run-x86-64-serial0-<timestamp>.log");
|
||||
run_efi_step.dependOn(&run_efi.step);
|
||||
|
||||
Reference in New Issue
Block a user