boot: mirroring cannot stall the machine, and the loader says where it is

Two reports from an Asus TUF FA607NU, which now gets as far as the splash:
the progress bar does not move, and after a resolution change the screen keeps
the background colour with no mark and no bar. Both point at the same code,
and it is code this loader did not have a week ago.

**Modes were being matched on every frame.** mirrors_match() ran from inside
mirrors_show(), so every menu redraw and every segment of the kernel load
asked another display adapter to change mode. On QEMU that is instant and
idempotent. On real hardware a mode set can take the best part of a second, can
blank a panel while it happens, and -- on a laptop -- is quite often being
asked of the *same* panel reached through a second GOP. A loader that does that
in the middle of drawing a progress bar is a loader that shows a background
colour and then waits on a firmware call that is not coming back.

It now happens exactly once, at discovery, before anything is drawn. The
second head is centred and cropped afterwards rather than re-moded, and the
booted system matches resolutions anyway because the kernel driver and vgpud
both follow the console.

**And mirroring no longer runs per segment of the kernel load.** Copying a
megabyte-and-a-half framebuffer to every other adapter, three times, to animate
a bar on a screen nobody is looking at.

**One refusal now turns it off for the rest of the boot**, and
`\schos\nomirror` -- an empty file whose only job is to exist -- turns it off
before it starts. There is no console to ask a question on at that point and no
menu to reach if the menu is what is broken, so the answer has to already be on
the volume. That also makes it a one-file experiment on hardware we cannot
instrument.

**The loader names its stages on screen.** After the splash, print() goes to
the serial port and nowhere else, so on a machine with no serial port a stall
leaves a progress bar and no information at all. Every stage after the kernel
load now labels itself under the bar -- Reading the console font, Reading the
memory map, Leaving the firmware -- so the last thing on the screen names the
call that did not return. Past ExitBootServices there is nothing left to report
with, which is itself worth knowing: a machine stopped on the last label
stopped in the jump.

Opening the boot volume moved ahead of the display, because what is on the
volume now decides some of what the display does.

Both supported configurations: one boot each, no traps, no panics, socktest and
memfdtest green. Mirroring still verified end to end -- F8, a resolution
change, boot, and both heads byte-identical at the new mode.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Nexoniarz 2026-09-23 19:50:28 +02:00
commit 3a0b570c23
2 changed files with 130 additions and 15 deletions

View file

@ -320,14 +320,15 @@ copy(void *dst, const void *src, UINTN n)
// read from the same place the kernel was. Set by open_kernel().
static EFI_FILE_PROTOCOL *boot_root;
static EFI_FILE_PROTOCOL *
open_kernel(EFI_HANDLE image)
// Opened before the display is touched, because what is on the volume decides
// some of what the display does -- see nomirror in mirrors_find().
static void
open_boot_volume(EFI_HANDLE image)
{
EFI_GUID li_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
EFI_GUID fs_guid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
EFI_LOADED_IMAGE_PROTOCOL *li = 0;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *fs = 0;
EFI_FILE_PROTOCOL *root = 0, *file = 0;
EFI_STATUS st;
// The volume this loader was itself loaded from, rather than a search of
@ -339,10 +340,34 @@ open_kernel(EFI_HANDLE image)
st = BS->HandleProtocol(li->DeviceHandle, &fs_guid, (void **)&fs);
if (EFI_ERROR(st)) die(L"boot volume has no filesystem", st);
st = fs->OpenVolume(fs, &root);
st = fs->OpenVolume(fs, &boot_root);
if (EFI_ERROR(st)) die(L"cannot open the boot volume", st);
}
boot_root = root;
// Is there a file of this name on the boot volume? Used for the switches that
// have to be answerable before there is any way to ask a person.
static int
boot_file_exists(const CHAR16 *path)
{
EFI_FILE_PROTOCOL *f = 0;
if (!boot_root)
return 0;
if (EFI_ERROR(boot_root->Open(boot_root, &f, (CHAR16 *)path,
EFI_FILE_MODE_READ, 0)) || !f)
return 0;
f->Close(f);
return 1;
}
static EFI_FILE_PROTOCOL *
open_kernel(EFI_HANDLE image)
{
EFI_FILE_PROTOCOL *root = boot_root, *file = 0;
EFI_STATUS st;
(void)image;
if (!root) die(L"boot volume was not opened", 0);
st = root->Open(root, &file, (CHAR16 *)KERNEL_PATH, EFI_FILE_MODE_READ, 0);
if (EFI_ERROR(st))
@ -524,7 +549,6 @@ load_elf(EFI_FILE_PROTOCOL *file)
done += ph.p_memsz;
ui_progress_tick((ui_u32)done);
mirrors_show(); /* once per segment: a handful of times, not a loop */
if (ph.p_paddr < lowest) lowest = ph.p_paddr;
if (ph.p_paddr + ph.p_memsz > highest) highest = ph.p_paddr + ph.p_memsz;
@ -1147,6 +1171,8 @@ static EFI_GRAPHICS_OUTPUT_PROTOCOL *mirror_gop[MAX_MIRRORS];
static EFI_GRAPHICS_OUTPUT_PROTOCOL *primary_gop;
static UINTN mirror_n;
static void mirrors_match(EFI_GRAPHICS_OUTPUT_PROTOCOL *primary);
static void
mirrors_find(EFI_GRAPHICS_OUTPUT_PROTOCOL *primary)
{
@ -1156,6 +1182,18 @@ mirrors_find(EFI_GRAPHICS_OUTPUT_PROTOCOL *primary)
primary_gop = primary;
// An off switch, because this is a nicety and booting is not.
//
// Everything below talks to display adapters the machine may be using for
// something else, through firmware drivers of wildly varying quality. If
// that goes wrong there is no console left to say so on and no way to ask
// a person anything -- so the answer has to already be on the volume.
// `\schos\nomirror` is an empty file whose only job is to exist.
if (boot_file_exists(L"\\schos\\nomirror")) {
print(L"schos: \\schos\\nomirror present; not mirroring\r\n");
return;
}
if (!BS->LocateHandleBuffer ||
EFI_ERROR(BS->LocateHandleBuffer(ByProtocol, &gop_guid, 0,
&count, &handles)) || !handles)
@ -1169,6 +1207,9 @@ mirrors_find(EFI_GRAPHICS_OUTPUT_PROTOCOL *primary)
continue;
mirror_gop[mirror_n++] = g;
}
if (mirror_n)
mirrors_match(primary);
}
/* Put every mirror into the primary's mode, if it has one.
@ -1187,6 +1228,18 @@ mirrors_find(EFI_GRAPHICS_OUTPUT_PROTOCOL *primary)
static void
mirrors_match(EFI_GRAPHICS_OUTPUT_PROTOCOL *primary)
{
// Called exactly once, from mirrors_find(), before anything has been
// drawn. It used to be called from mirrors_show() -- which is to say on
// every menu redraw and on every segment of the kernel load -- and that is
// not a safe thing to do to another display adapter.
//
// Setting a mode is not a cheap call on real hardware. It can take the
// best part of a second, it can blank a panel while it happens, and on a
// laptop the "other" adapter is quite often the *same* panel reached
// through a second GOP -- so doing it repeatedly, in the middle of drawing
// a progress bar, is a good way to produce a machine that shows a
// background colour and nothing else while the loader waits on a firmware
// call that is not coming back.
UINT32 w = primary->Mode->Info->HorizontalResolution;
UINT32 h = primary->Mode->Info->VerticalResolution;
@ -1223,8 +1276,6 @@ mirrors_show(void)
if (!primary->Mode->FrameBufferBase)
return; /* nothing to copy *from* */
mirrors_match(primary);
UINTN sw = primary->Mode->Info->HorizontalResolution;
UINTN sh = primary->Mode->Info->VerticalResolution;
UINTN delta = (UINTN)primary->Mode->Info->PixelsPerScanLine * 4;
@ -1236,9 +1287,15 @@ mirrors_show(void)
UINTN cw = w < sw ? w : sw;
UINTN ch = h < sh ? h : sh;
g->Blt(g, (void *)(UINTN)primary->Mode->FrameBufferBase,
EfiBltBufferToVideo, 0, 0, (w - cw) / 2, (h - ch) / 2,
cw, ch, delta);
// One refusal and the whole feature stops for this boot. A firmware
// Blt that fails once will fail every time, and retrying it on every
// screen is how a cosmetic feature turns into a hang.
if (EFI_ERROR(g->Blt(g, (void *)(UINTN)primary->Mode->FrameBufferBase,
EfiBltBufferToVideo, 0, 0,
(w - cw) / 2, (h - ch) / 2, cw, ch, delta))) {
mirror_n = 0;
return;
}
}
}
@ -1611,6 +1668,10 @@ EfiMain(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab)
// framebuffer that did not exist yet -- every tick was silently discarded
// and then the splash painted over the result. Setting the mode first also
// puts the menu where a person expects it: at the start.
// The volume first: what is on it decides some of what the display does,
// and the display is the only thing that can report a failure afterwards.
open_boot_volume(image);
UINT32 fb = setup_framebuffer();
EFI_FILE_PROTOCOL *file = open_kernel(image);
@ -1618,10 +1679,16 @@ EfiMain(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab)
ui_progress_end();
file->Close(file);
// After open_kernel, because that is what finds the volume, and after the
// kernel rather than before it: the font is optional and the kernel is
// not, so a machine that is going to fail should fail on the thing that
// matters.
// After the kernel rather than before it: the font is optional and the
// kernel is not, so a machine that is going to fail should fail on the
// thing that matters.
//
// Every stage from here to the jump names itself under the progress bar.
// On a desktop this is invisible -- the stages take microseconds -- and on
// a machine that stops, the label is the last thing on the screen and says
// which call did not come back. That is the only diagnostic a laptop with
// no serial port can give, and it costs a string.
ui_progress_label("Reading the console font");
load_console_font();
vprint(L"schos: kernel entry "); vprint_hex(entry); vprint(L"\r\n");
@ -1636,6 +1703,8 @@ EfiMain(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab)
// ExitBootServices: the key it returns is invalidated by any allocation,
// and AllocatePool for the map buffer is itself an allocation -- hence the
// static buffer and the retry.
ui_progress_label("Reading the memory map");
static UINT8 mapbuf[32768];
UINTN mapsize = sizeof mapbuf, mapkey = 0, descsize = 0;
UINT32 descver = 0;
@ -1659,6 +1728,7 @@ EfiMain(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab)
if (nent >= E820_MAXENTRIES)
print(L"schos: WARNING: memory map truncated, some RAM will be unused\r\n");
ui_progress_label("Leaving the firmware");
vprint(L"schos: exiting boot services\r\n");
st = BS->ExitBootServices(image, mapkey);
@ -1680,6 +1750,12 @@ EfiMain(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab)
}
// ---- no firmware services exist past this line ----
//
// Nothing below can report anything: ConOut is gone, and ui_progress_label
// would be drawing into a framebuffer the firmware may no longer own. The
// last label on screen is therefore "Leaving the firmware", and a machine
// stopped on that one stopped somewhere in the handful of instructions
// between here and the kernel's first.
__asm__ volatile("cli");