Pass argv to processes on a SysV entry stack; grow the user stack to 32 KiB
Processes now start with C-compatible arguments: the kernel builds the System V AMD64 entry block (argc, argv, empty envp, auxiliary vector) at the top of the stack, argv[0] is the path or initial-ramdisk name the process was spawned as, and system_spawn carries an optional NUL-separated blob that becomes argv[1..]. The runtime parses the block (runtime.argumentCount/argument) and its spawn wrappers pass arguments through. The name is also recorded on the task, so a fault report says which binary died, not just its id. The user stack grows from one page to eight (32 KiB, parameters.user_stack_pages), with the page below left unmapped as a guard so an overflow faults into a clean process kill rather than corrupting the image. Task.name_buffer is zero-initialised, not undefined: an undefined default is materialised as a 0xAA fill that moved the static task pool out of .bss and made the whole kernel ~7x slower under QEMU TCG (caught by the affinity test). Proven end to end by the new args test: args-echo respawns itself with arguments via the syscall blob, burns more stack than one page could hold, and echoes its argv intact. Full suite: 44/44.
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
//! args-echo — a test fixture for process arguments (bundled in the
|
||||
//! initial-ramdisk, spawned only by the `args` test case). Run with no arguments,
|
||||
//! it respawns itself *with* some via `spawnWithArguments` — exercising the
|
||||
//! system_spawn argument blob. Run with arguments, it burns more stack than one
|
||||
//! page could hold (proving the multi-page stack: on a single-page stack the
|
||||
//! recursion would hit the guard and the process would be killed before echoing),
|
||||
//! then echoes its whole argv in one `debug_write` the kernel test asserts on —
|
||||
//! proving the kernel-built System V entry stack (argc, argv pointers,
|
||||
//! NUL-terminated strings) and the runtime's parsing of it, end to end.
|
||||
|
||||
const runtime = @import("runtime");
|
||||
|
||||
/// Recurse with a real frame each level: `depth` levels of ~0.5 KiB, touched
|
||||
/// through a volatile pointer so no optimiser can flatten the frames away.
|
||||
fn burnStack(depth: usize) u8 {
|
||||
var frame: [512]u8 = undefined;
|
||||
const touch: *volatile [512]u8 = &frame;
|
||||
touch[0] = @truncate(depth);
|
||||
touch[511] = touch[0];
|
||||
if (depth == 0) return touch[511];
|
||||
return touch[0] +% burnStack(depth - 1);
|
||||
}
|
||||
|
||||
pub fn main() void {
|
||||
if (runtime.argumentCount() <= 1) {
|
||||
// First instance: spawn the second with real arguments, then exit.
|
||||
_ = runtime.system.spawnWithArguments("args-echo", &.{ "alpha", "beta-42" });
|
||||
return;
|
||||
}
|
||||
|
||||
// ~16 x 0.5 KiB frames: comfortably past one page, well inside the 32 KiB stack.
|
||||
_ = burnStack(16);
|
||||
|
||||
// Second instance: echo "args: <argv0> <argv1> ..." for the test to match.
|
||||
var buffer: [128]u8 = undefined;
|
||||
const prefix = "args:";
|
||||
@memcpy(buffer[0..prefix.len], prefix);
|
||||
var len: usize = prefix.len;
|
||||
for (0..runtime.argumentCount()) |i| {
|
||||
const argument = runtime.argument(i);
|
||||
if (len + 1 + argument.len + 1 > buffer.len) break;
|
||||
buffer[len] = ' ';
|
||||
len += 1;
|
||||
@memcpy(buffer[len..][0..argument.len], argument);
|
||||
len += argument.len;
|
||||
}
|
||||
buffer[len] = '\n';
|
||||
len += 1;
|
||||
_ = runtime.system.write(buffer[0..len]);
|
||||
}
|
||||
|
||||
pub const panic = runtime.panic;
|
||||
comptime {
|
||||
_ = &runtime.start._start; // pull the runtime entry shim into the image
|
||||
}
|
||||
Reference in New Issue
Block a user