boot: one line when it works, all of it when it does not

A boot printed about thirty lines before reaching a prompt. Every one was
useful once, and printed on every boot for ever they train the person in front
of the machine to stop reading their own console -- which is expensive on the
day one of them changes.

    SchOS UEFI loader
    Press F8 for boot options... booting

    Welcome to SchOS!  (F8 at boot for the full log, or dmesg once you are in)

    root@schos / »

**Nothing was deleted, it was routed.** consputc() already wrote to the klog
ring before anywhere else, so a line can be kept in full while being held back
from the two places a person looks. kinfo() is cprintf() with that routing, and
`dmesg` still has the whole boot with timestamps afterwards. The rule for which
is which is not how interesting a line is -- everything was interesting to
somebody once -- but whether you would want to see it on a machine that is
*working*. `pci: 7 devices` is yes the first time and no every time after;
`e1000: no DMA memory for the rings` is yes for ever and stays a cprintf.

The verbose boot the F8 menu already offered now means something much bigger,
which is the other half of this: it is the only way to see any of it live.

Userspace follows through /proc/bootmode, one word. schinit, schinitd and vgpud
hold back their progress lines and never their failures -- a supervisor that
goes quiet when a job dies is worse than one that chatters.

Two things found by testing rather than by reading:

  - schinit read /proc/bootmode *before* bootstrap() mounted /proc, so it got
    "quiet" every time and the verbose boot was quiet in the one process that
    runs first. It reads after the mount now.
  - resolvconf announced "already exists; -f to replace it" on every boot after
    the first, which is a line about having done nothing. It is quiet when the
    file already says what it would have written, and speaks only when the two
    disagree -- which means the nameserver changed and the machine is using the
    old one.

Verified both ways: quiet boot is six lines, a forced-verbose build prints all
thirty plus the `verbose boot:` steps, and /proc/kmsg holds every line in both.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VQ7Qx8E1q1M1bbCq8QLHS2
This commit is contained in:
Nexoniarz 2026-09-14 21:19:50 +02:00
commit 617a4aec0a
24 changed files with 373 additions and 60 deletions

View file

@ -85,19 +85,30 @@ print_int(int64 xx, int base, int sign)
consputc(buf[i]);
}
void
cprintf(char *fmt, ...)
// Everything in the log; only what matters on the screen.
//
// A boot used to print about thirty lines of diagnostics to the serial line
// and the console, and they are genuinely useful -- once. After that they are
// the first thing you see every time you start the machine, and the message
// that actually matters is buried among them.
//
// So they are not deleted, they are *routed*. consputc() writes to the klog
// ring before anything else, so a line can be kept in full while being held
// back from the two places a person is looking. `dmesg`, /proc/kmsg and the
// next boot's "carried over from the previous boot" all still have it.
//
// Set for the duration of one kinfo() and no longer. Not a mode the system
// sits in: a panic, a driver failing, anything that is not routine still goes
// straight to the screen, because those are lines nobody should have to go
// looking for.
static int cons_logonly;
static void
vcprintf(char *fmt, va_list ap)
{
va_list ap;
int i, c, locking;
int i, c;
char *s;
va_start(ap, fmt);
locking = cons.locking;
if (locking)
acquire(&cons.lock);
if (fmt == 0)
panic("null fmt");
@ -143,9 +154,47 @@ cprintf(char *fmt, ...)
break;
}
}
}
void
cprintf(char *fmt, ...)
{
va_list ap;
int locking = cons.locking;
va_start(ap, fmt);
if (locking)
acquire(&cons.lock);
vcprintf(fmt, ap);
if (locking)
release(&cons.lock);
va_end(ap);
}
// A line for the log, and for the screen only on a verbose boot.
//
// The rule for choosing between this and cprintf is not "how interesting is
// it" -- everything here was interesting to somebody once. It is: **would you
// want to see this on a machine that is working?** `pci: 7 devices` is a yes
// the first time and a no every time after. `e1000: no memory for its rings`
// is a yes for ever, and stays a cprintf.
void
kinfo(char *fmt, ...)
{
va_list ap;
int locking = cons.locking;
va_start(ap, fmt);
if (locking)
acquire(&cons.lock);
if (!boot_verbose)
cons_logonly++;
vcprintf(fmt, ap);
if (!boot_verbose)
cons_logonly--;
if (locking)
release(&cons.lock);
va_end(ap);
}
// Hand the console over to panic().
@ -212,6 +261,10 @@ consputc(int c)
// three in the morning. See include/klog.h.
klog_putc(c);
// Held back from the screen and the wire, kept in the log. See kinfo().
if (cons_logonly)
return;
if (c == BACKSPACE) {
uartputc('\b'); uartputc(' '); uartputc('\b');
if (!cons_serial_only)