M3: real user processes — address spaces, syscall/sysret, swapgs
Per-process address spaces (AddressSpace = a PML4 with an empty user half and the shared kernel half copied in; create/destroy in paging.zig) with CR3 switched on context switch and TSS.rsp0/kernel_rsp published per switch. The GS base now points at an arch per-CPU block and every ring transition observes the swapgs discipline, so a ring-3 `mov %ax,%gs` can no longer poison per-CPU access. syscall/sysret is the primary user entry (int 0x80 kept as a test path); one handler, installed once at boot, serves both and dispatches on whether the caller is a scheduled process or a borrowed test thread. spawnProcess loads an ELF into a fresh address space and schedules it; exit frees the address space after switching to the kernel tables. New `process` test: init runs twice as a real process (create/exit/recreate) on its own page tables, coexisting with a kernel task under preemption. Suite 28/28. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
5d57e7b01c
commit
37fb3cb0cf
@@ -99,6 +99,8 @@ pub fn run(case: []const u8, boot_info: *const BootInfo) void {
|
||||
userPfTest();
|
||||
} else if (eql(case, "init")) {
|
||||
initTest(boot_info);
|
||||
} else if (eql(case, "process")) {
|
||||
processTest(boot_info);
|
||||
} else if (eql(case, "poweroff")) {
|
||||
powerTest(.off);
|
||||
} else if (eql(case, "reboot")) {
|
||||
@@ -732,6 +734,66 @@ fn userTest() void {
|
||||
result();
|
||||
}
|
||||
|
||||
var proc_worker_run: bool = true;
|
||||
var proc_worker_ran: bool = false;
|
||||
|
||||
/// A kernel task that spins while a process runs, to prove the two coexist under
|
||||
/// preemption (a process on its own CR3 does not stall kernel work).
|
||||
fn procWorker() void {
|
||||
const running: *volatile bool = &proc_worker_run;
|
||||
const ran: *volatile bool = &proc_worker_ran;
|
||||
while (running.*) ran.* = true;
|
||||
sched.exit();
|
||||
}
|
||||
|
||||
/// Real processes: load /sbin/init as a scheduled ring-3 process with its own
|
||||
/// address space, twice in succession. The first run proves a process executes
|
||||
/// on its own page tables (write from CPL 3) and coexists preemptively with a
|
||||
/// kernel task; its exit frees the address space. The second run reuses those
|
||||
/// reclaimed frames — succeeding proves create/exit/teardown/recreate is sound.
|
||||
fn processTest(boot_info: *const BootInfo) void {
|
||||
log("DANOS-TEST-BEGIN: process\n", .{});
|
||||
check("bootloader handed over sbin/init", boot_info.init_len != 0);
|
||||
if (boot_info.init_len == 0) {
|
||||
result();
|
||||
return;
|
||||
}
|
||||
const image = @as([*]const u8, @ptrFromInt(danos.physToVirt(boot_info.init_base)))[0..boot_info.init_len];
|
||||
const expected = "init: hello from user space\n";
|
||||
|
||||
proc_worker_run = true;
|
||||
proc_worker_ran = false;
|
||||
sched.spawn(procWorker, 4); // kernel task, same priority as the processes
|
||||
|
||||
var runs: u32 = 0;
|
||||
var last_cs: u64 = 0;
|
||||
sched.setPriority(1); // drop below the workers so they get the cores
|
||||
var round: u32 = 0;
|
||||
while (round < 2) : (round += 1) {
|
||||
usermode.write_len = 0;
|
||||
usermode.write_cs = 0;
|
||||
usermode.exit_code = 0xdead;
|
||||
usermode.spawnProcess(image, 4) catch {
|
||||
log("DANOS-PROC: spawn round {d} failed\n", .{round});
|
||||
continue;
|
||||
};
|
||||
var spins: u64 = 0;
|
||||
while (usermode.exit_code == 0xdead and spins < 5_000_000_000) : (spins += 1) sched.yield();
|
||||
log("DANOS-PROC: round {d} write_len={d} exit_code=0x{x}\n", .{ round, usermode.write_len, usermode.exit_code });
|
||||
if (eql(usermode.write_buf[0..usermode.write_len], expected)) {
|
||||
runs += 1;
|
||||
last_cs = usermode.write_cs;
|
||||
}
|
||||
}
|
||||
sched.setPriority(4);
|
||||
proc_worker_run = false;
|
||||
|
||||
check("process ran twice on its own address space (create/exit/recreate)", runs == 2);
|
||||
check("process wrote from CPL 3 (CS = user selector | RPL 3)", last_cs == 0x23);
|
||||
check("a kernel task coexisted with the process (preemption)", proc_worker_ran);
|
||||
result();
|
||||
}
|
||||
|
||||
/// Isolation: a ring-3 read of a kernel-only page (the LAPIC page — present,
|
||||
/// supervisor) must page-fault with error code 0x5 (present | user) at the user
|
||||
/// RIP. The fault report is the pass signal (matched by the harness); if the
|
||||
|
||||
Reference in New Issue
Block a user