Restart rebooted harts on a coroutine stack
A guest reboot used to longjmp from the SBI reset handler back to a setjmp in rv_step(), which set the jump point up on every step and jumped across whatever interpreter, JIT or T2C frames lay in between. Native system builds now run the hart on a coroutine (src/coro.c: a short context switch on x86-64 and Arm64, ucontext elsewhere). A reboot abandons the coroutine stack, and the next resume starts the hart's run loop from the top. The coroutine returns to rv_run() only when the hart halts or restarts; under gdbstub it yields after every instruction. The Emscripten build keeps a direct-step path. The coroutine stack is 8 MiB of lazily committed memory with a 64 KiB guard region, and an overflow into it prints a diagnostic instead of corrupting the heap. All of this is gated by a new RV32_HAS_HART_CORO (SYSTEM_MMIO and not Emscripten); user mode, the ELF loader and wasm are unaffected.
Validation, on x86-64 (Threadripper 2990WX) unless noted:
- Builds with gcc and clang, all passing:
defconfig,jit_defconfig(T2C),system_defconfig,system_interpreter_defconfig,system_jit_defconfig(T2C), system withELF_LOADER, and system and user mode withGDBSTUB. .ci/boot-linux-test.sh, built as CI does (INITRD_SIZE=32 ENABLE_MOP_FUSION=0, thenmake artifact), onsystem_defconfig(gcc),system_interpreter_defconfig(clang) andsystem_jit_defconfig(clang): 26/26 each, covering boot, cold and warm reboot, virtio-blk, and virtio-net over TAP and SLIRP.make checkondefconfig,jit_defconfigand system withELF_LOADER: all pass.- Arm64 (Ampere eMAG, clang,
ENABLE_SDL=0):system_defconfigandsystem_interpreter_defconfigbuild; cold and warm reboot pass on both. - A standalone test linking
src/coro.ckeeps integer and floating-point values live across a million switches in both directions, and checks restart, the overflow diagnostic, and that other faults are passed on. It passes at -O0, -O2 and -O3 with gcc and clang, on x86-64 (asm and ucontext) and Arm64. - Boot time to the login prompt is unchanged: x86-64
system_defconfigtakes 3.07 to 3.10 s on this branch against 3.08 s on master, and Arm64 takes 8.50 s against 8.58 s. On x86-64, identical source built in two trees differed by up to 6%, so only differences beyond that were treated as real.
Not covered: the wasm build (no emcc here), and gdbstub beyond the first few dozen stepi. Master already hangs in system-mode gdbstub at the same point (pc 0x100), and this change does not alter that.
Summary by cubic
Reboots used to longjmp from the SBI reset handler to a setjmp point set up on every step. Native system builds now run each hart on a coroutine, so a guest reboot abandons the whole host call stack and restarts the hart's run loop from the top on the next resume.
- Adds
src/coro.c: assembly context switches on x86-64 and Arm64, ucontext elsewhere; 8 MiB lazily committed stack with a 64 KiB guard region that prints a diagnostic on overflow. - Gated by
RV32_HAS_HART_CORO(system mode, no Emscripten); user mode, the ELF loader, and wasm are unaffected. - The coroutine returns to
rv_run()only on halt or restart; under gdbstub it yields after every instruction. - The Emscripten build keeps the direct-step path and resets dispatcher state after a reboot instead.
rv_debug()now releases the breakpoint map and gdbstub connection when a session ends.- Boot time to the login prompt is unchanged on x86-64 and Arm64.