boot: the disc that could not be chainloaded, and the error nobody could see

Reported from a real laptop booting through Ventoy: the F8 menu appears,
selecting Boot SchOS gives a black screen and a white dash in the corner.
Both halves of that are bugs.

The disc could not be chainloaded. Booting it directly, firmware runs the
El Torito FAT image and the loader opens \schos\kernel on that volume,
which is where mkiso.sh put it. Ventoy does not use El Torito at all: it
mounts the ISO 9660 tree and runs \EFI\BOOT\BOOTX64.EFI from there. The
loader then looks for the kernel on the volume it was itself loaded from
-- the ISO tree -- and the kernel was only ever inside the FAT image. It
is now in both. The comment in mkiso.sh had even described this failure,
as the reason the kernel is in the FAT image, without noticing the ISO
tree had the same requirement.

The error was invisible. print() redirects to the serial port once the
splash is up, which is right for a development machine and useless on a
laptop: the loader died with a perfectly good explanation and sent it to
a wire nobody was holding. What is left on screen is the firmware's own
cursor -- the white dash. die() now draws the message on the framebuffer
too, over a cleared band so it does not land on the logo.

Verified by reproducing the exact failure: the ISO tree extracted and
booted as a plain volume, as a chainloader presents it. With the kernel
removed it dies visibly with "no kernel at \schos\kernel or \kernel, EFI
status 0x000E" -- EFI_NOT_FOUND, which is what the laptop was hitting
silently. With it present, that same volume boots through to a shell.

110 [ok] on the ESP path, the ISO check passes, no failures.
This commit is contained in:
Nexoniarz 2026-08-11 11:54:47 +02:00
commit f4107177ea
2 changed files with 53 additions and 1 deletions

View file

@ -147,6 +147,45 @@ die(const CHAR16 *msg, EFI_STATUS st)
print(L"schos: "); print(msg);
print(L" (status "); print_hex(st); print(L")\r\n");
print(L"schos: halted.\r\n");
/* Say it on the screen too, if there is one.
*
* Once the splash is up, print() goes to the serial port and nowhere else.
* That is right for a development machine and useless on a laptop, which
* has no serial port -- a loader that failed after drawing the splash left
* a black screen and the firmware's own cursor, with the explanation going
* to a wire nobody was holding. A dead machine that says why is worth the
* dozen lines. */
if (ui_active()) {
char line[96];
int i = 0;
for (const char *q = "Cannot start SchOS: "; *q && i < (int)sizeof line - 1; q++)
line[i++] = *q;
for (int j = 0; msg[j] && i < (int)sizeof line - 1; j++)
line[i++] = (msg[j] >= 0x20 && msg[j] < 0x7F) ? (char)msg[j] : '?';
line[i] = 0;
char code[32];
i = 0;
for (const char *q = "EFI status 0x"; *q && i < (int)sizeof code - 1; q++)
code[i++] = *q;
/* Low nibbles are enough to identify the error and keep this short;
* EFI_NOT_FOUND is 0x0E with the high bit set. */
const char *hex = "0123456789ABCDEF";
for (int sh = 12; sh >= 0; sh -= 4)
if (i < (int)sizeof code - 1)
code[i++] = hex[(st >> sh) & 0xF];
code[i] = 0;
/* Clear a band first. Without it the message lands on top of the
* logo, which is exactly the moment legibility matters most. */
int mid = (int)ui_height() / 2;
ui_fill_rect(0, mid - 20, (int)ui_width(), 100, 0x0C0E13u);
ui_text_centered(mid, line, 0xE8EAEEu);
ui_text_centered(mid + 24, code, 0xB9BEC7u);
ui_text_centered(mid + 56, "Halted -- power off or reset", 0x8A9099u);
}
for (;;)
__asm__ volatile("hlt");
}