boot/uefi: the system information panel, and the offset that faked a version
ui_panel was written for this screen during the loader restyle and then never called -- dead code sitting in boot/common/ui.c waiting for the row that would use it. This is that row: F8 -> System Information, showing firmware vendor and revision, UEFI revision, CPU brand, usable memory, current mode and whether ACPI was found. Everything is read when the screen is drawn rather than captured at startup, because the resolution row above it can have changed the mode, and a panel still reporting what the machine booted with is worse than no panel at all. Two facts are deliberately read through the same code that feeds the kernel -- efi_type_to_e820() for what counts as usable memory, and find_rsdp() for ACPI. A panel with its own idea of either would be a second implementation to keep in step, and the ACPI line exists precisely because a missing RSDP means no MADT, no IOAPIC and no SMP, which has already been debugged once from the far end. The screenshot caught a bug in the screen it was taken to verify: UEFI revision read "21587.2286". EFI_TABLE_HEADER begins with a UINT64 Signature, so Revision is at offset 8 and I had read offset 4 -- the upper half of "IBI SYST", formatted as a version number. It now reads 2.7 under OVMF, which is correct. Verified by driving the menu over the QEMU monitor and reading the pixels: five rows with the hint updated to 1-5, the panel centred, and every value cross-checkable -- EDK II, firmware 1.0, UEFI 2.7, "QEMU Virtual CPU version 2.5+" against -cpu qemu64, 1018 MB of the 1 GB given, 1280x800 matching what fbshot captures, ACPI present. The highlight sits 9 px inside both panel borders, symmetric. F8 has a 1.5 s window, so the capture presses it repeatedly through boot rather than guessing when OVMF finishes -- and only F8, since any other key breaks the wait and boots. Also: every ESP drive now says format=raw. QEMU warned about probing the format on each of the fifteen or so places that attach it, which is new noise on what is now the only boot path. 110 [ok], no failures, no warnings.
This commit is contained in:
parent
141f2785ab
commit
ed77244df0
11 changed files with 211 additions and 21 deletions
4
Makefile
4
Makefile
|
|
@ -463,7 +463,7 @@ ovmf: $(OVMF_CODE) $(OVMF_VARS)
|
|||
all: $(OVMF_CODE) $(OVMF_VARS)
|
||||
|
||||
qemu-uefi: esp $(BUILD)/schfs.img $(OVMF_CODE) $(OVMF_VARS)
|
||||
$(QEMU) -machine pc-i440fx-9.2 -drive if=pflash,format=raw,unit=0,readonly=on,file=$(OVMF_CODE) -drive if=pflash,format=raw,unit=1,file=$(OVMF_VARS) -drive file=fat:rw:$(BUILD)/esp,index=0,media=disk -drive file=$(BUILD)/schfs.img,format=raw,index=1,media=disk -m 1G -smp 1 -cpu qemu64,+rdtscp -serial mon:stdio $(QEMU_DISPLAY) $(QEMU_INPUT)
|
||||
$(QEMU) -machine pc-i440fx-9.2 -drive if=pflash,format=raw,unit=0,readonly=on,file=$(OVMF_CODE) -drive if=pflash,format=raw,unit=1,file=$(OVMF_VARS) -drive file=fat:rw:$(BUILD)/esp,index=0,media=disk,format=raw -drive file=$(BUILD)/schfs.img,format=raw,index=1,media=disk -m 1G -smp 1 -cpu qemu64,+rdtscp -serial mon:stdio $(QEMU_DISPLAY) $(QEMU_INPUT)
|
||||
|
||||
# Build the ramdisk-root variant. This is a target rather than just
|
||||
# "make RAMROOT=1" because toggling RAMROOT changes CFLAGS (-DRAMDISK_ROOT),
|
||||
|
|
@ -898,7 +898,7 @@ schcom-test: esp $(BUILD)/schfs.img
|
|||
# ends up with a writable firmware image and nobody notices.
|
||||
OVMF_DRIVES = -drive if=pflash,format=raw,unit=0,readonly=on,file=$(OVMF_CODE) \
|
||||
-drive if=pflash,format=raw,unit=1,file=$(OVMF_VARS) \
|
||||
-drive file=fat:rw:$(BUILD)/esp,index=0,media=disk \
|
||||
-drive file=fat:rw:$(BUILD)/esp,index=0,media=disk,format=raw \
|
||||
-drive file=$(BUILD)/schfs.img,format=raw,index=1,media=disk
|
||||
|
||||
qemu-kvm: esp $(BUILD)/schfs.img $(OVMF_CODE) $(OVMF_VARS)
|
||||
|
|
|
|||
210
boot/uefi/main.c
210
boot/uefi/main.c
|
|
@ -570,6 +570,194 @@ res_choose(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop)
|
|||
}
|
||||
}
|
||||
|
||||
/* Both defined below, next to the hand-off code they belong to. The system
|
||||
* information panel reads the same two facts the kernel will be handed, on
|
||||
* purpose: a panel with its own idea of what counts as usable memory, or its
|
||||
* own ACPI search, would be a second implementation to keep in step. */
|
||||
static UINT32 efi_type_to_e820(UINT32 t);
|
||||
static UINT64 find_rsdp(void);
|
||||
|
||||
/* CPU brand string, from CPUID leaves 0x80000002..4. Forty-eight bytes of
|
||||
* ASCII split across twelve registers, usually with leading spaces on Intel
|
||||
* parts, so it is trimmed on the way out. A CPU that does not implement the
|
||||
* extended leaves reports a maximum below 0x80000004, and there is nothing to
|
||||
* substitute -- saying "unknown" is better than printing whatever registers
|
||||
* happened to hold. */
|
||||
static void
|
||||
cpu_brand(char *out, int cap)
|
||||
{
|
||||
UINT32 eax = 0, ebx = 0, ecx = 0, edx = 0;
|
||||
__asm__ volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx)
|
||||
: "a"(0x80000000u));
|
||||
if (eax < 0x80000004u) {
|
||||
int i = 0;
|
||||
for (const char *q = "unknown"; *q && i < cap - 1; q++) out[i++] = *q;
|
||||
out[i] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
char raw[49];
|
||||
int n = 0;
|
||||
for (UINT32 leaf = 0x80000002u; leaf <= 0x80000004u; leaf++) {
|
||||
__asm__ volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx)
|
||||
: "a"(leaf));
|
||||
UINT32 regs[4] = { eax, ebx, ecx, edx };
|
||||
for (int r = 0; r < 4; r++)
|
||||
for (int b = 0; b < 4; b++)
|
||||
raw[n++] = (char)((regs[r] >> (b * 8)) & 0xFF);
|
||||
}
|
||||
raw[48] = 0;
|
||||
|
||||
const char *src = raw;
|
||||
while (*src == ' ') src++;
|
||||
int i = 0;
|
||||
while (*src && i < cap - 1) out[i++] = *src++;
|
||||
while (i > 0 && out[i - 1] == ' ') i--;
|
||||
out[i] = 0;
|
||||
}
|
||||
|
||||
/* Total conventional memory, in MB, from the UEFI memory map.
|
||||
*
|
||||
* This is a *look*, not the hand-off: GetMemoryMap is called again at exit
|
||||
* because any allocation between here and there invalidates the map key, and
|
||||
* calling it once for both purposes is how a loader gets EFI_INVALID_PARAMETER
|
||||
* from ExitBootServices long after the mistake. The count is deliberately of
|
||||
* the types the kernel will treat as usable, so it matches what the system
|
||||
* later reports rather than what the firmware owns. */
|
||||
static UINT64
|
||||
memory_mb(void)
|
||||
{
|
||||
UINTN size = 0, mapkey = 0, dsize = 0;
|
||||
UINT32 dver = 0;
|
||||
static char buf[16384];
|
||||
|
||||
size = sizeof buf;
|
||||
if (EFI_ERROR(BS->GetMemoryMap(&size, (EFI_MEMORY_DESCRIPTOR *)buf,
|
||||
&mapkey, &dsize, &dver)))
|
||||
return 0;
|
||||
|
||||
UINT64 pages = 0;
|
||||
for (UINTN off = 0; off + dsize <= size; off += dsize) {
|
||||
EFI_MEMORY_DESCRIPTOR *d = (EFI_MEMORY_DESCRIPTOR *)(buf + off);
|
||||
if (efi_type_to_e820(d->Type) == E820_USABLE)
|
||||
pages += d->NumberOfPages;
|
||||
}
|
||||
return (pages * 4096u) / (1024u * 1024u);
|
||||
}
|
||||
|
||||
/* Narrow a CHAR16 string into ASCII for the panel. The firmware vendor is the
|
||||
* only UTF-16 string on this screen; anything outside ASCII becomes '?' rather
|
||||
* than being silently dropped, so a name that does not fit still looks like a
|
||||
* name. */
|
||||
static void
|
||||
narrow(const CHAR16 *src, char *out, int cap)
|
||||
{
|
||||
int i = 0;
|
||||
if (src)
|
||||
while (src[i] && i < cap - 1) {
|
||||
CHAR16 c = src[i];
|
||||
out[i] = (c >= 0x20 && c < 0x7F) ? (char)c : '?';
|
||||
i++;
|
||||
}
|
||||
out[i] = 0;
|
||||
}
|
||||
|
||||
/* The system information screen.
|
||||
*
|
||||
* Everything here is read at the moment it is shown rather than captured at
|
||||
* startup: the resolution can have been changed by the row above it, and a
|
||||
* panel that still reports the mode the machine booted with is worse than no
|
||||
* panel. */
|
||||
static void
|
||||
sys_info(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop)
|
||||
{
|
||||
char vendor[40], cpu[52], mode[32], mem[24], fw[24], spec[24], acpi[24];
|
||||
|
||||
narrow(ST->FirmwareVendor, vendor, sizeof vendor);
|
||||
cpu_brand(cpu, sizeof cpu);
|
||||
|
||||
{
|
||||
char w[16], h[16];
|
||||
u64_to_dec(gop->Mode->Info->HorizontalResolution, w, sizeof w);
|
||||
u64_to_dec(gop->Mode->Info->VerticalResolution, h, sizeof h);
|
||||
int i = 0;
|
||||
for (char *q = w; *q && i < (int)sizeof mode - 1; q++) mode[i++] = *q;
|
||||
if (i < (int)sizeof mode - 1) mode[i++] = 'x';
|
||||
for (char *q = h; *q && i < (int)sizeof mode - 1; q++) mode[i++] = *q;
|
||||
mode[i] = 0;
|
||||
}
|
||||
|
||||
{
|
||||
char n[24];
|
||||
u64_to_dec(memory_mb(), n, sizeof n);
|
||||
int i = 0;
|
||||
for (char *q = n; *q && i < (int)sizeof mem - 1; q++) mem[i++] = *q;
|
||||
for (const char *q = " MB usable"; *q && i < (int)sizeof mem - 1; q++)
|
||||
mem[i++] = *q;
|
||||
mem[i] = 0;
|
||||
}
|
||||
|
||||
/* The firmware's own revision, and the UEFI revision it implements. These
|
||||
* disagree often and the difference matters when reading a bug report:
|
||||
* one names the build, the other names the contract. */
|
||||
{
|
||||
char hi[16], lo[16];
|
||||
u64_to_dec(ST->FirmwareRevision >> 16, hi, sizeof hi);
|
||||
u64_to_dec(ST->FirmwareRevision & 0xFFFF, lo, sizeof lo);
|
||||
int i = 0;
|
||||
for (char *q = hi; *q && i < (int)sizeof fw - 1; q++) fw[i++] = *q;
|
||||
if (i < (int)sizeof fw - 1) fw[i++] = '.';
|
||||
for (char *q = lo; *q && i < (int)sizeof fw - 1; q++) fw[i++] = *q;
|
||||
fw[i] = 0;
|
||||
}
|
||||
{
|
||||
/* EFI_TABLE_HEADER is { UINT64 Signature; UINT32 Revision; ... }, so
|
||||
* Revision is at offset 8. At offset 4 this read the upper half of the
|
||||
* "IBI SYST" signature and displayed it as "21587.2286". */
|
||||
UINT32 rev = *(UINT32 *)(ST->Hdr + 8);
|
||||
char hi[16], lo[16];
|
||||
u64_to_dec(rev >> 16, hi, sizeof hi);
|
||||
u64_to_dec((rev & 0xFFFF) / 10, lo, sizeof lo);
|
||||
int i = 0;
|
||||
for (char *q = hi; *q && i < (int)sizeof spec - 1; q++) spec[i++] = *q;
|
||||
if (i < (int)sizeof spec - 1) spec[i++] = '.';
|
||||
for (char *q = lo; *q && i < (int)sizeof spec - 1; q++) spec[i++] = *q;
|
||||
spec[i] = 0;
|
||||
}
|
||||
|
||||
/* Whether ACPI was found, because its absence is not cosmetic: no RSDP
|
||||
* means no MADT, no IOAPIC and no SMP, and that has already been debugged
|
||||
* once from the far end. */
|
||||
{
|
||||
const char *v = find_rsdp() ? "present" : "NOT FOUND";
|
||||
int i = 0;
|
||||
for (const char *q = v; *q && i < (int)sizeof acpi - 1; q++) acpi[i++] = *q;
|
||||
acpi[i] = 0;
|
||||
}
|
||||
|
||||
static const char *const labels[] = {
|
||||
"Firmware", "Firmware revision", "UEFI revision",
|
||||
"Processor", "Memory", "Display", "ACPI",
|
||||
};
|
||||
const char *values[7];
|
||||
values[0] = vendor;
|
||||
values[1] = fw;
|
||||
values[2] = spec;
|
||||
values[3] = cpu;
|
||||
values[4] = mem;
|
||||
values[5] = mode;
|
||||
values[6] = acpi;
|
||||
|
||||
ui_panel("System Information", labels, values, 7, "Any key to go back");
|
||||
|
||||
EFI_INPUT_KEY key;
|
||||
for (;;) {
|
||||
if (ST->ConIn && !EFI_ERROR(ST->ConIn->ReadKeyStroke(ST->ConIn, &key)))
|
||||
return;
|
||||
BS->Stall(10000);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
boot_menu(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop)
|
||||
{
|
||||
|
|
@ -589,14 +777,15 @@ boot_menu(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop)
|
|||
|
||||
res_row(gop, resbuf, sizeof resbuf);
|
||||
|
||||
const char *items[4];
|
||||
const char *items[5];
|
||||
items[0] = "Boot SchOS";
|
||||
items[1] = "Boot SchOS (verbose)";
|
||||
items[2] = resbuf;
|
||||
items[3] = "Reboot to firmware";
|
||||
items[3] = "System Information";
|
||||
items[4] = "Reboot to firmware";
|
||||
|
||||
ui_menu("Boot Manager", items, 4, selected,
|
||||
"W/S or 1-4 to choose, Enter to select");
|
||||
ui_menu("Boot Manager", items, 5, selected,
|
||||
"W/S or 1-5 to choose, Enter to select");
|
||||
|
||||
EFI_INPUT_KEY key;
|
||||
CHAR16 c = 0;
|
||||
|
|
@ -609,17 +798,18 @@ boot_menu(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop)
|
|||
BS->Stall(10000);
|
||||
}
|
||||
|
||||
if (scan == 0x01) { selected = (selected + 3) % 4; continue; } /* up */
|
||||
if (scan == 0x02) { selected = (selected + 1) % 4; continue; } /* down */
|
||||
if (c == L'w' || c == L'W') { selected = (selected + 3) % 4; continue; }
|
||||
if (c == L's' || c == L'S') { selected = (selected + 1) % 4; continue; }
|
||||
if (c >= L'1' && c <= L'4') { selected = (int)(c - L'1'); continue; }
|
||||
if (scan == 0x01) { selected = (selected + 4) % 5; continue; } /* up */
|
||||
if (scan == 0x02) { selected = (selected + 1) % 5; continue; } /* down */
|
||||
if (c == L'w' || c == L'W') { selected = (selected + 4) % 5; continue; }
|
||||
if (c == L's' || c == L'S') { selected = (selected + 1) % 5; continue; }
|
||||
if (c >= L'1' && c <= L'5') { selected = (int)(c - L'1'); continue; }
|
||||
|
||||
if (c == L'\r' || c == L'\n') {
|
||||
if (selected == 0) return 0;
|
||||
if (selected == 1) return 1;
|
||||
if (selected == 2) { res_choose(gop); continue; }
|
||||
if (selected == 3) {
|
||||
if (selected == 3) { sys_info(gop); continue; }
|
||||
if (selected == 4) {
|
||||
ui_clear();
|
||||
ui_text_centered((int)ui_height() / 2, "Halted -- reset to reach firmware setup",
|
||||
0xB9BEC7u);
|
||||
|
|
|
|||
2
run.ps1
2
run.ps1
|
|
@ -121,7 +121,7 @@ if ($buf[0] -ne 0x46 -or $buf[1] -ne 0x68 -or $buf[2] -ne 0x63 -or $buf[3] -ne 0
|
|||
$qemuArgs = @(
|
||||
'-drive', "if=pflash,format=raw,unit=0,readonly=on,file=$code"
|
||||
'-drive', "if=pflash,format=raw,unit=1,file=$runVars"
|
||||
'-drive', "file=fat:rw:$esp,index=0,media=disk"
|
||||
'-drive', "file=fat:rw:$esp,index=0,media=disk,format=raw"
|
||||
'-drive', "file=$hdb,format=raw,index=1,media=disk"
|
||||
'-m', '1G'
|
||||
'-smp', '2'
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ EOF
|
|||
-machine pc-i440fx-9.2 -cpu qemu64,+rdtscp \
|
||||
-drive if=pflash,format=raw,unit=0,readonly=on,file=build/OVMF_CODE.fd \
|
||||
-drive if=pflash,format=raw,unit=1,file=build/schcom-OVMF_VARS.fd \
|
||||
-drive file=fat:rw:build/esp,index=0,media=disk \
|
||||
-drive file=fat:rw:build/esp,index=0,media=disk,format=raw \
|
||||
-drive file=build/schfs.img,format=raw,index=1,media=disk \
|
||||
-m 1G -smp 1 \
|
||||
-display none -vga std \
|
||||
|
|
|
|||
|
|
@ -272,7 +272,7 @@ $drives = if ($Iso) {
|
|||
if (-not (Test-Path (Join-Path $esp 'EFI/BOOT/BOOTX64.EFI'))) {
|
||||
throw "no staged ESP at $esp -- run 'make esp' first"
|
||||
}
|
||||
$firmware + @("-drive file=fat:rw:`"$esp`",index=0,media=disk"
|
||||
$firmware + @("-drive file=fat:rw:`"$esp`",index=0,media=disk,format=raw"
|
||||
"-drive file=`"$hdb`",format=raw,index=1,media=disk")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ $psi.FileName = $Qemu
|
|||
$psi.Arguments = @(
|
||||
"-drive if=pflash,format=raw,unit=0,readonly=on,file=`"$code`""
|
||||
"-drive if=pflash,format=raw,unit=1,file=`"$runVars`""
|
||||
"-drive file=fat:rw:`"$esp`",index=0,media=disk"
|
||||
"-drive file=fat:rw:`"$esp`",index=0,media=disk,format=raw"
|
||||
"-drive file=`"$hdb`",format=raw,index=1,media=disk"
|
||||
'-m 1G -smp 1 -cpu qemu64,+rdtscp -machine pc-i440fx-9.2'
|
||||
'-display none'
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ $psi.FileName = $Qemu
|
|||
$psi.Arguments = @(
|
||||
"-drive if=pflash,format=raw,unit=0,readonly=on,file=`"$code`""
|
||||
"-drive if=pflash,format=raw,unit=1,file=`"$runVars`""
|
||||
"-drive file=fat:rw:`"$esp`",index=0,media=disk"
|
||||
"-drive file=fat:rw:`"$esp`",index=0,media=disk,format=raw"
|
||||
"-drive file=`"$hdb`",format=raw,index=1,media=disk"
|
||||
'-m 1G -smp 1 -cpu qemu64,+rdtscp -machine pc-i440fx-9.2'
|
||||
'-display none'
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ $psi.FileName = $Qemu
|
|||
$psi.Arguments = @(
|
||||
"-drive if=pflash,format=raw,unit=0,readonly=on,file=`"$code`""
|
||||
"-drive if=pflash,format=raw,unit=1,file=`"$runVars`""
|
||||
"-drive file=fat:rw:`"$esp`",index=0,media=disk"
|
||||
"-drive file=fat:rw:`"$esp`",index=0,media=disk,format=raw"
|
||||
"-drive file=`"$hdb`",format=raw,index=1,media=disk"
|
||||
'-m 1G -smp 1 -cpu qemu64,+rdtscp -machine pc-i440fx-9.2'
|
||||
'-display none'
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ $psi.Arguments = @(
|
|||
# are untouched either way.
|
||||
"-drive if=pflash,format=raw,unit=0,readonly=on,file=`"$code`""
|
||||
"-drive if=pflash,format=raw,unit=1,file=`"$runVars`""
|
||||
"-drive file=fat:rw:`"$esp`",index=0,media=disk"
|
||||
"-drive file=fat:rw:`"$esp`",index=0,media=disk,format=raw"
|
||||
"-drive file=`"$hdb`",format=raw,index=1,media=disk,file.locking=off"
|
||||
'-snapshot'
|
||||
'-m 1G -smp 1 -cpu qemu64,+rdtscp -machine pc-i440fx-9.2'
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ $psi.FileName = $Qemu
|
|||
$psi.Arguments = @(
|
||||
"-drive if=pflash,format=raw,unit=0,readonly=on,file=`"$code`""
|
||||
"-drive if=pflash,format=raw,unit=1,file=`"$runVars`""
|
||||
"-drive file=fat:rw:`"$esp`",index=0,media=disk"
|
||||
"-drive file=fat:rw:`"$esp`",index=0,media=disk,format=raw"
|
||||
"-drive file=`"$hdb`",format=raw,index=1,media=disk"
|
||||
'-m 1G -smp 1 -cpu qemu64,+rdtscp -machine pc-i440fx-9.2'
|
||||
'-display none'
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ $psi.FileName = $Qemu
|
|||
$psi.Arguments = @(
|
||||
"-drive if=pflash,format=raw,unit=0,readonly=on,file=`"$code`""
|
||||
"-drive if=pflash,format=raw,unit=1,file=`"$runVars`""
|
||||
"-drive file=fat:rw:`"$esp`",index=0,media=disk"
|
||||
"-drive file=fat:rw:`"$esp`",index=0,media=disk,format=raw"
|
||||
"-drive file=`"$hdb`",format=raw,index=1,media=disk"
|
||||
'-m 1G -smp 1 -cpu qemu64,+rdtscp -machine pc-i440fx-9.2'
|
||||
'-display none'
|
||||
|
|
|
|||
Loading…
Reference in a new issue