diff --git a/Makefile b/Makefile index 24d4626..ac9edba 100644 --- a/Makefile +++ b/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) diff --git a/boot/uefi/main.c b/boot/uefi/main.c index 9307d17..b247709 100644 --- a/boot/uefi/main.c +++ b/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); diff --git a/run.ps1 b/run.ps1 index 628dbd4..eb15873 100644 --- a/run.ps1 +++ b/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' diff --git a/tools/posix/schcom_test.sh b/tools/posix/schcom_test.sh index 551fa48..7b994b1 100644 --- a/tools/posix/schcom_test.sh +++ b/tools/posix/schcom_test.sh @@ -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 \ diff --git a/tools/windows/bootcheck.ps1 b/tools/windows/bootcheck.ps1 index 1f9c38b..5a396d2 100644 --- a/tools/windows/bootcheck.ps1 +++ b/tools/windows/bootcheck.ps1 @@ -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") } diff --git a/tools/windows/crashshot.ps1 b/tools/windows/crashshot.ps1 index cee2edc..ee8c6ab 100644 --- a/tools/windows/crashshot.ps1 +++ b/tools/windows/crashshot.ps1 @@ -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' diff --git a/tools/windows/dispshot.ps1 b/tools/windows/dispshot.ps1 index e38a62e..62e2f60 100644 --- a/tools/windows/dispshot.ps1 +++ b/tools/windows/dispshot.ps1 @@ -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' diff --git a/tools/windows/fbshot.ps1 b/tools/windows/fbshot.ps1 index a6445c4..34f5840 100644 --- a/tools/windows/fbshot.ps1 +++ b/tools/windows/fbshot.ps1 @@ -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' diff --git a/tools/windows/splashshot.ps1 b/tools/windows/splashshot.ps1 index 8dfd71a..6587451 100644 --- a/tools/windows/splashshot.ps1 +++ b/tools/windows/splashshot.ps1 @@ -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' diff --git a/tools/windows/textshot.ps1 b/tools/windows/textshot.ps1 index be2b97d..669ee76 100644 --- a/tools/windows/textshot.ps1 +++ b/tools/windows/textshot.ps1 @@ -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' diff --git a/tools/windows/ttyshot.ps1 b/tools/windows/ttyshot.ps1 index 54b53db..c426df3 100644 --- a/tools/windows/ttyshot.ps1 +++ b/tools/windows/ttyshot.ps1 @@ -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'