gfx: the chosen resolution reaches every head, and the greeting stops racing

Two things.

**The F8 resolution now applies to the other displays as well.** It already
reached them once vgpud started -- that was the last commit but one -- but not
before: choosing 1024x768 changed the screen it was chosen on and left the
other at 1280x800, so the menu and then the splash appeared on it as a small
rectangle in the middle of a larger black one. Which is a fair reading of "the
resolution change is not changing resolution".

The resolution offered in that menu is a statement about the machine's display,
not about one adapter of it, so both stages before vgpud now follow it:

  the loader   mirrors_match() puts every mirror GOP into the primary's mode,
               called from mirrors_show() so it cannot be forgotten at a new
               call site. Two integer comparisons in the common case.
  the kernel   setup_scanout() takes gfx's dimensions over GET_DISPLAY_INFO's.
               Same argument as vgpud's bring_up(): the host window's size is
               a fact about QEMU, and the console's mode is the one somebody
               chose.

**And the greeting is no longer a race.** "Welcome to SchOS!" was printed after
every job had been started, including the shell on the console -- so which of
the two reached the screen first was up to the scheduler, and it went both ways
about evenly. A boot that sometimes greets you above the prompt and sometimes
below it does not read as two orderings of one boot; it reads as a line printed
by mistake.

schinitd now holds back the job that owns the console -- `own_tty`, which is
exactly the job whose output lands there -- until after the greeting is
written. That is enough to settle it: this process writes the line, and only
then asks PID 1 to spawn the shell, so the fork has not happened yet. Nothing
waits on it that was not already waiting.

Verified: F8 -> Resolution -> 1024x768 -> boot, with the virtio head
screendumped over QMP at three points during startup and once at the prompt --
1024x768 throughout, and byte-identical to the stdvga head. Three consecutive
boots put the greeting at the same offset, ahead of the prompt every time.
Both supported configurations: one boot each, no traps, no panics, socktest and
memfdtest green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Nexoniarz 2026-09-22 22:19:00 +02:00
commit 9ab8a7a426
4 changed files with 95 additions and 1 deletions

View file

@ -631,9 +631,11 @@ main(void)
unsigned int highest = adopt();
next_cookie = highest + 1;
// Everything except whatever owns the console. That one waits for the
// greeting below -- see the comment on it.
for (int i = 0; i < njobs; i++) {
struct job *j = &jobs[i];
if (j->disabled || !j->run_at_load)
if (j->disabled || !j->run_at_load || j->own_tty)
continue;
if (j->pid)
continue; // adopted; it never stopped running
@ -658,6 +660,35 @@ main(void)
out("\nWelcome to SchOS!"
" (F8 at boot for the full log, or dmesg once you are in)\n\n");
// *Now* the shell on the console.
//
// It used to be started with everything else, and then this greeting was
// printed -- which made the two a race that the shell won about half the
// time. A boot that sometimes reads
//
// Welcome to SchOS! (F8 at boot for the full log, ...)
// root@schos / »
//
// and sometimes reads the same two lines the other way round is not two
// orderings of one boot, it is a greeting that looks like it was printed
// by mistake.
//
// Holding it back until after the write is enough to settle it: this
// process writes the line, and only then asks PID 1 to spawn the shell, so
// the fork cannot even have happened yet. Nothing else waits on it -- the
// console shell is the last job to start either way, and starting it a
// few microseconds later costs nothing a person can see.
//
// `own_tty` rather than a name: the job that claims the console as its
// controlling terminal is exactly the job whose output lands here.
for (int i = 0; i < njobs; i++) {
struct job *j = &jobs[i];
if (j->disabled || !j->run_at_load || !j->own_tty || j->pid)
continue;
vout("schinitd: starting "); vout(j->label); vout("\n");
ask_spawn(j);
}
// From here on this program does two things: answer PID 1's heartbeat, and
// act on the exits it reports. There is no wait() in this program and
// there cannot be -- the children are not ours.