init: pin daemon stdio to boot-log VT1 so the login prompt lands last

Daemon stdio (fd 0/1/2) is inherited from bootstrap pointing at /scheme/debug
— the kernel console — which fbcond renders onto the *active* VT. Once
29_activate_console switches the active VT to 2 for the getty login, any daemon
that logs afterwards paints on top of the login banner/prompt: late async
driver attach (i2c-hidd, evdevd, dw-acpi-i2cd, all via common::setup_logging ->
stderr) and smolnetd's bounded NIC wait. So the banner ended up "near the end"
but never last.

Re-enable switch_stdio, redirecting daemon stdio to a fixed boot-log VT
(/scheme/fbcon/1) right after the /usr switchroot, before the /usr units run.
VT1 is the active console during boot (so the full boot stays visible there),
fbcond still mirrors it to serial, and VT2 is left clean for a prompt that
lands last. This is NOT the old /scheme/log redirect that went dark: that
relied on fbbootlogd surfacing /scheme/log to the framebuffer (racy). A real VT
renders directly. Best-effort: if fbcon/1 can't be opened, stdio stays on the
kernel console exactly as before.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Red Bear OS
2026-07-25 17:51:03 +09:00
parent 2e5e516587
commit e54484e553
+23 -10
View File
@@ -130,16 +130,6 @@ fn main() {
scheduler
.schedule_start_and_report_errors(&mut unit_store, UnitId("00_logd.service".to_owned()));
scheduler.step(&mut unit_store, &mut init_config);
// NOTE: init/daemon stdio is deliberately kept on the kernel console
// rather than being redirected into /scheme/log. Redirecting made the boot
// "go dark" after logd on text/recovery ISOs (bare/mini): the graphical
// bootlog (fbbootlogd, VT1) does not reliably surface /scheme/log to the
// framebuffer, and logd's console mirror is racy, so the console showed
// nothing between logd and the login VT — indistinguishable from a hang.
// Keeping stdio on the console makes the full boot observable on
// `-serial` and the framebuffer. (A quiet/splash mode for the graphical
// full ISO can re-enable the redirect once fbbootlogd rendering is fixed.)
let _ = switch_stdio;
let runtime_target = UnitId("00_runtime.target".to_owned());
scheduler.schedule_start_and_report_errors(&mut unit_store, runtime_target.clone());
@@ -155,6 +145,29 @@ fn main() {
Path::new("/usr"),
Path::new("/etc"),
);
// Pin init/daemon stdio to the dedicated boot-log VT (fbcon/1) before the
// /usr units run. Rationale: stdio otherwise stays on the kernel console
// (/scheme/debug), which fbcond renders onto the *active* VT. Once the
// `29_activate_console` unit switches the active VT to 2 for the getty
// login, any daemon that logs afterwards — late async driver attach
// (i2c-hidd, evdevd, dw-acpi-i2cd) or smolnetd's bounded NIC wait — paints
// its output on top of the login banner/prompt, so the prompt is never the
// last thing on screen. Pinning daemon output to VT1 (a fixed VT, not the
// active one) keeps the boot log fully visible — VT1 is the active console
// during boot, and fbcond still mirrors it to the serial line — while
// leaving VT2 clean for a login prompt that lands last.
//
// This is deliberately NOT the old `/scheme/log` redirect that made boot
// "go dark": that depended on fbbootlogd surfacing /scheme/log to the
// framebuffer (racy/unreliable). A real VT renders directly, so full-boot
// observability is preserved. Best-effort: if fbcon/1 cannot be opened
// (e.g. fbcond absent on a headless/serial-only boot), stdio stays on the
// kernel console and the boot remains observable exactly as before.
if let Err(err) = switch_stdio("/scheme/fbcon/1") {
eprintln!("INIT: keeping stdio on kernel console (fbcon/1 unavailable: {err})");
}
{
// FIXME introduce multi-user.target unit and replace the config dir iteration
// scheduler.schedule_start_and_report_errors(&mut unit_store, UnitId("multi-user.target".to_owned()));