/* $OpenBSD: efiboot.c,v 1.42 2024/04/25 18:31:49 kn Exp $ */ /* * Copyright (c) 2015 YASUOKA Masahiko * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include "libsa.h" #include "disk.h" #include #include #include #include #include "efidev.h" #include "efiboot.h" #include "run_i386.h" #define KERN_LOADSPACE_SIZE (64 * 1024 * 1024) EFI_SYSTEM_TABLE *ST; EFI_BOOT_SERVICES *BS; EFI_RUNTIME_SERVICES *RS; EFI_HANDLE IH; EFI_DEVICE_PATH *efi_bootdp = NULL; EFI_PHYSICAL_ADDRESS heap; EFI_LOADED_IMAGE *loadedImage; UINTN heapsiz = 1 * 1024 * 1024; UINTN mmap_key; static EFI_GUID imgp_guid = LOADED_IMAGE_PROTOCOL; static EFI_GUID blkio_guid = BLOCK_IO_PROTOCOL; static EFI_GUID devp_guid = DEVICE_PATH_PROTOCOL; u_long efi_loadaddr; int efi_device_path_depth(EFI_DEVICE_PATH *dp, int); int efi_device_path_ncmp(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *, int); static void efi_heap_init(void); static int efi_memprobe_internal(void); static void efi_video_init(void); static void efi_video_reset(void); static EFI_STATUS efi_gop_setmode(int mode); EFI_STATUS efi_main(EFI_HANDLE, EFI_SYSTEM_TABLE *); void (*run_i386)(u_long, u_long, int, int, int, int, int, int, int, int) __attribute__((noreturn)); extern int bios_bootdev; EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab) { extern char *progname; EFI_LOADED_IMAGE *imgp; EFI_DEVICE_PATH *dp0 = NULL, *dp; EFI_STATUS status; EFI_PHYSICAL_ADDRESS stack; ST = systab; BS = ST->BootServices; RS = ST->RuntimeServices; IH = image; /* disable reset by watchdog after 5 minutes */ BS->SetWatchdogTimer(0, 0, 0, NULL); efi_video_init(); efi_heap_init(); status = BS->HandleProtocol(image, &imgp_guid, (void **)&imgp); if (status == EFI_SUCCESS) status = BS->HandleProtocol(imgp->DeviceHandle, &devp_guid, (void **)&dp0); if (status == EFI_SUCCESS) { for (dp = dp0; !IsDevicePathEnd(dp); dp = NextDevicePathNode(dp)) { if (DevicePathType(dp) == MEDIA_DEVICE_PATH && (DevicePathSubType(dp) == MEDIA_HARDDRIVE_DP || DevicePathSubType(dp) == MEDIA_CDROM_DP)) { bios_bootdev = (DevicePathSubType(dp) == MEDIA_CDROM_DP) ? 0x1e0 : 0x80; efi_bootdp = dp0; break; } else if (DevicePathType(dp) == MESSAGING_DEVICE_PATH&& DevicePathSubType(dp) == MSG_MAC_ADDR_DP) { bios_bootdev = 0x0; efi_bootdp = dp0; break; } } } #ifdef __amd64__ /* allocate run_i386_start() on heap */ if ((run_i386 = alloc(run_i386_size)) == NULL) panic("alloc() failed"); memcpy(run_i386, run_i386_start, run_i386_size); #endif /* can't use sa_cleanup since printf is used after sa_cleanup() */ /* sa_cleanup = efi_cleanup; */ #ifdef __amd64__ progname = "BOOTX64"; #else progname = "BOOTIA32"; #endif /* * Move the stack before calling boot(). UEFI on some machines * locate the stack on our kernel load address. */ stack = heap + heapsiz; #if defined(__amd64__) asm("movq %0, %%rsp;" "mov %1, %%edi;" "call boot;" :: "r"(stack - 32), "r"(bios_bootdev)); #else asm("movl %0, %%esp;" "movl %1, (%%esp);" "call boot;" :: "r"(stack - 32), "r"(bios_bootdev)); #endif /* must not reach here */ return (EFI_SUCCESS); } void efi_cleanup(void) { int retry; EFI_STATUS status; /* retry once in case of failure */ for (retry = 1; retry >= 0; retry--) { efi_memprobe_internal(); /* sync the current map */ status = BS->ExitBootServices(IH, mmap_key); if (status == EFI_SUCCESS) break; if (retry == 0) panic("ExitBootServices failed (%d)", status); } } /*********************************************************************** * Disk ***********************************************************************/ struct disklist_lh efi_disklist; void efi_diskprobe(void) { int i, bootdev = 0, depth = -1; UINTN sz; EFI_STATUS status; EFI_HANDLE *handles = NULL; EFI_BLOCK_IO *blkio; EFI_BLOCK_IO_MEDIA *media; struct diskinfo *di; EFI_DEVICE_PATH *dp; TAILQ_INIT(&efi_disklist); sz = 0; status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, 0); if (status == EFI_BUFFER_TOO_SMALL) { handles = alloc(sz); status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, handles); } if (handles == NULL || EFI_ERROR(status)) panic("BS->LocateHandle() returns %d", status); if (efi_bootdp != NULL) depth = efi_device_path_depth(efi_bootdp, MEDIA_DEVICE_PATH); /* * U-Boot incorrectly represents devices with a single * MEDIA_DEVICE_PATH component. In that case include that * component into the matching, otherwise we'll blindly select * the first device. */ if (depth == 0) depth = 1; for (i = 0; i < sz / sizeof(EFI_HANDLE); i++) { status = BS->HandleProtocol(handles[i], &blkio_guid, (void **)&blkio); if (EFI_ERROR(status)) panic("BS->HandleProtocol() returns %d", status); media = blkio->Media; if (media->LogicalPartition || !media->MediaPresent) continue; di = alloc(sizeof(struct diskinfo)); efid_init(di, blkio); if (efi_bootdp == NULL || depth == -1 || bootdev != 0) goto next; status = BS->HandleProtocol(handles[i], &devp_guid, (void **)&dp); if (EFI_ERROR(status)) goto next; if (efi_device_path_ncmp(efi_bootdp, dp, depth) == 0) { TAILQ_INSERT_HEAD(&efi_disklist, di, list); bootdev = 1; continue; } next: TAILQ_INSERT_TAIL(&efi_disklist, di, list); } free(handles, sz); } /* * Determine the number of nodes up to, but not including, the first * node of the specified type. */ int efi_device_path_depth(EFI_DEVICE_PATH *dp, int dptype) { int i; for (i = 0; !IsDevicePathEnd(dp); dp = NextDevicePathNode(dp), i++) { if (DevicePathType(dp) == dptype) return (i); } return (i); } int efi_device_path_ncmp(EFI_DEVICE_PATH *dpa, EFI_DEVICE_PATH *dpb, int deptn) { int i, cmp; for (i = 0; i < deptn; i++) { if (IsDevicePathEnd(dpa) || IsDevicePathEnd(dpb)) return ((IsDevicePathEnd(dpa) && IsDevicePathEnd(dpb)) ? 0 : (IsDevicePathEnd(dpa))? -1 : 1); cmp = DevicePathNodeLength(dpa) - DevicePathNodeLength(dpb); if (cmp) return (cmp); cmp = memcmp(dpa, dpb, DevicePathNodeLength(dpa)); if (cmp) return (cmp); dpa = NextDevicePathNode(dpa); dpb = NextDevicePathNode(dpb); } return (0); } /*********************************************************************** * Memory ***********************************************************************/ bios_memmap_t bios_memmap[128]; bios_efiinfo_t bios_efiinfo; static void efi_heap_init(void) { EFI_STATUS status; heap = HEAP_LIMIT; status = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData, EFI_SIZE_TO_PAGES(heapsiz), &heap); if (status != EFI_SUCCESS) panic("BS->AllocatePages()"); } void efi_memprobe(void) { u_int n = 0; bios_memmap_t *bm; EFI_STATUS status; EFI_PHYSICAL_ADDRESS addr = 0x10000000ULL; /* Below 256MB */ int error; status = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData, EFI_SIZE_TO_PAGES(KERN_LOADSPACE_SIZE), &addr); if (status != EFI_SUCCESS) panic("BS->AllocatePages()"); efi_loadaddr = addr; printf(" mem["); error = efi_memprobe_internal(); for (bm = bios_memmap; bm->type != BIOS_MAP_END; bm++) { if (bm->type == BIOS_MAP_FREE && bm->size > 12 * 1024) { if (n++ != 0) printf(" "); if (bm->size > 1024 * 1024) printf("%uM", bm->size / 1024 / 1024); else printf("%uK", bm->size / 1024); } } if (error == E2BIG) printf(" overflow"); printf("]"); } static int efi_memprobe_internal(void) { EFI_STATUS status; UINTN mapkey, mmsiz, siz; UINT32 mmver; EFI_MEMORY_DESCRIPTOR *mm0, *mm; int i, n; bios_memmap_t *bm, bm0; int error = 0; cnvmem = extmem = 0; bios_memmap[0].type = BIOS_MAP_END; if (bios_efiinfo.mmap_start != 0) free((void *)bios_efiinfo.mmap_start, bios_efiinfo.mmap_size); siz = 0; status = BS->GetMemoryMap(&siz, NULL, &mapkey, &mmsiz, &mmver); if (status != EFI_BUFFER_TOO_SMALL) panic("cannot get the size of memory map"); mm0 = alloc(siz); status = BS->GetMemoryMap(&siz, mm0, &mapkey, &mmsiz, &mmver); if (status != EFI_SUCCESS) panic("cannot get the memory map"); n = siz / mmsiz; mmap_key = mapkey; for (i = 0, mm = mm0; i < n; i++, mm = NextMemoryDescriptor(mm, mmsiz)){ bm0.type = BIOS_MAP_END; bm0.addr = mm->PhysicalStart; bm0.size = mm->NumberOfPages * EFI_PAGE_SIZE; if (mm->Type == EfiReservedMemoryType || mm->Type == EfiUnusableMemory || mm->Type == EfiRuntimeServicesCode || mm->Type == EfiRuntimeServicesData) bm0.type = BIOS_MAP_RES; else if (mm->Type == EfiLoaderCode || mm->Type == EfiLoaderData || mm->Type == EfiBootServicesCode || mm->Type == EfiBootServicesData || mm->Type == EfiConventionalMemory) bm0.type = BIOS_MAP_FREE; else if (mm->Type == EfiACPIReclaimMemory) bm0.type = BIOS_MAP_ACPI; else if (mm->Type == EfiACPIMemoryNVS) bm0.type = BIOS_MAP_NVS; else /* * XXX Is there anything to do for EfiMemoryMappedIO * XXX EfiMemoryMappedIOPortSpace EfiPalCode? */ bm0.type = BIOS_MAP_RES; for (bm = bios_memmap; bm->type != BIOS_MAP_END; bm++) { if (bm->type != bm0.type) continue; if (bm->addr <= bm0.addr && bm0.addr <= bm->addr + bm->size) { bm->size = bm0.addr + bm0.size - bm->addr; break; } else if (bm0.addr <= bm->addr && bm->addr <= bm0.addr + bm0.size) { bm->size = bm->addr + bm->size - bm0.addr; bm->addr = bm0.addr; break; } } if (bm->type == BIOS_MAP_END) { if (bm == &bios_memmap[nitems(bios_memmap) - 1]) { error = E2BIG; break; } *bm = bm0; (++bm)->type = BIOS_MAP_END; } } for (bm = bios_memmap; bm->type != BIOS_MAP_END; bm++) { if (bm->addr < IOM_BEGIN) /* Below memory hole */ cnvmem = max(cnvmem, (bm->addr + bm->size) / 1024); if (bm->addr >= IOM_END /* Above the memory hole */ && bm->addr / 1024 == extmem + 1024) extmem += bm->size / 1024; } bios_efiinfo.mmap_desc_ver = mmver; bios_efiinfo.mmap_desc_size = mmsiz; bios_efiinfo.mmap_size = siz; bios_efiinfo.mmap_start = (uintptr_t)mm0; return error; } /*********************************************************************** * Console ***********************************************************************/ static SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL; static SIMPLE_INPUT_INTERFACE *conin; static EFI_GRAPHICS_OUTPUT *gop = NULL; static EFI_GUID con_guid = EFI_CONSOLE_CONTROL_PROTOCOL_GUID; static EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; static EFI_GUID serio_guid = SERIAL_IO_PROTOCOL; struct efi_video { int cols; int rows; } efi_video[32]; static void efi_video_init(void) { EFI_CONSOLE_CONTROL_PROTOCOL *conctrl = NULL; int i, mode80x25, mode100x31; UINTN cols, rows; EFI_STATUS status; EFI_HANDLE *handles; UINTN nhandles; EFI_GRAPHICS_OUTPUT *first_gop = NULL; EFI_DEVICE_PATH *devp_test = NULL; status = BS->LocateHandleBuffer(ByProtocol, &gop_guid, NULL, &nhandles, &handles); if (!EFI_ERROR(status)) { for (i = 0; i < nhandles; i++) { status = BS->HandleProtocol(handles[i], &gop_guid, (void **)&gop); if (first_gop == NULL) first_gop = gop; status = BS->HandleProtocol(handles[i], &devp_guid, (void **)&devp_test); if (status == EFI_SUCCESS) break; } if (status != EFI_SUCCESS) gop = first_gop; BS->FreePool(handles); } conout = ST->ConOut; status = BS->LocateProtocol(&con_guid, NULL, (void **)&conctrl); if (status == EFI_SUCCESS) conctrl->SetMode(conctrl, EfiConsoleControlScreenText); mode80x25 = -1; mode100x31 = -1; for (i = 0; i < conout->Mode->MaxMode; i++) { status = conout->QueryMode(conout, i, &cols, &rows); if (EFI_ERROR(status)) continue; if (mode80x25 < 0 && cols == 80 && rows == 25) mode80x25 = i; if (mode100x31 < 0 && cols == 100 && rows == 31) mode100x31 = i; if (i < nitems(efi_video)) { efi_video[i].cols = cols; efi_video[i].rows = rows; } } if (mode100x31 >= 0) conout->SetMode(conout, mode100x31); else if (mode80x25 >= 0) conout->SetMode(conout, mode80x25); conin = ST->ConIn; efi_video_reset(); } static void efi_video_reset(void) { conout->EnableCursor(conout, TRUE); conout->SetAttribute(conout, EFI_TEXT_ATTR(EFI_LIGHTGRAY, EFI_BLACK)); conout->ClearScreen(conout); } void efi_cons_probe(struct consdev *cn) { cn->cn_pri = CN_MIDPRI; cn->cn_dev = makedev(12, 0); printf(" pc%d", minor(cn->cn_dev)); } void efi_cons_init(struct consdev *cp) { } int efi_cons_getc(dev_t dev) { EFI_INPUT_KEY key; EFI_STATUS status; UINTN dummy; static int lastchar = 0; if (lastchar) { int r = lastchar; if ((dev & 0x80) == 0) lastchar = 0; return (r); } status = conin->ReadKeyStroke(conin, &key); while (status == EFI_NOT_READY || key.UnicodeChar == 0) { if (dev & 0x80) return (0); BS->WaitForEvent(1, &conin->WaitForKey, &dummy); status = conin->ReadKeyStroke(conin, &key); } if (dev & 0x80) lastchar = key.UnicodeChar; return (key.UnicodeChar); } void efi_cons_putc(dev_t dev, int c) { CHAR16 buf[2]; if (c == '\n') efi_cons_putc(dev, '\r'); buf[0] = c; buf[1] = 0; conout->OutputString(conout, buf); } int efi_cons_getshifts(dev_t dev) { /* XXX */ return (0); } int com_addr = -1; int com_speed = -1; static SERIAL_IO_INTERFACE *serios[4]; const int comports[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 }; /* call with sp == 0 to query the current speed */ int pio_comspeed(dev_t dev, int sp) { int port = (com_addr == -1) ? comports[minor(dev)] : com_addr; int i, newsp; int err; if (sp <= 0) return com_speed; /* valid baud rate? */ if (115200 < sp || sp < 75) return -1; /* * Accepted speeds: * 75 150 300 600 1200 2400 4800 9600 19200 38400 76800 and * 14400 28800 57600 115200 */ for (i = sp; i != 75 && i != 14400; i >>= 1) if (i & 1) return -1; /* ripped screaming from dev/ic/com.c */ #define divrnd(n, q) (((n)*2/(q)+1)/2) /* divide and round off */ newsp = divrnd((COM_FREQ / 16), sp); if (newsp <= 0) return -1; err = divrnd((COM_FREQ / 16) * 1000, sp * newsp) - 1000; if (err < 0) err = -err; if (err > COM_TOLERANCE) return -1; #undef divrnd if (com_speed != -1 && cn_tab && cn_tab->cn_dev == dev && com_speed != sp) { printf("com%d: changing speed to %d baud in 5 seconds, " "change your terminal to match!\n\a", minor(dev), sp); sleep(5); } outb(port + com_cfcr, LCR_DLAB); outb(port + com_dlbl, newsp); outb(port + com_dlbh, newsp>>8); outb(port + com_cfcr, LCR_8BITS); if (com_speed != -1) printf("\ncom%d: %d baud\n", minor(dev), sp); newsp = com_speed; com_speed = sp; return newsp; } int pio_com_getc(dev_t dev) { int port = (com_addr == -1) ? comports[minor(dev & 0x7f)] : com_addr; if (dev & 0x80) return (inb(port + com_lsr) & LSR_RXRDY); while ((inb(port + com_lsr) & LSR_RXRDY) == 0) ; return (inb(port + com_data) & 0xff); } void pio_com_putc(dev_t dev, int c) { int port = (com_addr == -1) ? comports[minor(dev)] : com_addr; while ((inb(port + com_lsr) & LSR_TXRDY) == 0) ; outb(port + com_data, c); } void efi_com_probe(struct consdev *cn) { EFI_HANDLE *handles = NULL; SERIAL_IO_INTERFACE *serio; EFI_STATUS status; EFI_DEVICE_PATH *dp, *dp0; EFI_DEV_PATH_PTR dpp; UINTN sz; int i, uid = -1; cn->cn_pri = CN_LOWPRI; cn->cn_dev = makedev(8, 0); sz = 0; status = BS->LocateHandle(ByProtocol, &serio_guid, 0, &sz, 0); if (status == EFI_BUFFER_TOO_SMALL) { handles = alloc(sz); status = BS->LocateHandle(ByProtocol, &serio_guid, 0, &sz, handles); } if (handles == NULL || EFI_ERROR(status)) { free(handles, sz); return; } for (i = 0; i < sz / sizeof(EFI_HANDLE); i++) { /* * Identify port number of the handle. This assumes ACPI * UID 0-3 map to legacy COM[1-4] and they use the legacy * port address. */ status = BS->HandleProtocol(handles[i], &devp_guid, (void **)&dp0); if (EFI_ERROR(status)) continue; uid = -1; for (dp = dp0; !IsDevicePathEnd(dp); dp = NextDevicePathNode(dp)) { dpp = (EFI_DEV_PATH_PTR)dp; if (DevicePathType(dp) == ACPI_DEVICE_PATH && DevicePathSubType(dp) == ACPI_DP) if (dpp.Acpi->HID == EFI_PNP_ID(0x0501)) { uid = dpp.Acpi->UID; break; } } if (uid < 0 || nitems(serios) <= uid) continue; /* Prepare SERIAL_IO_INTERFACE */ status = BS->HandleProtocol(handles[i], &serio_guid, (void **)&serio); if (EFI_ERROR(status)) continue; serios[uid] = serio; } free(handles, sz); for (i = 0; i < nitems(serios); i++) { if (serios[i] != NULL) printf(" com%d", i); } } int efi_valid_com(dev_t dev) { return (minor(dev) < nitems(serios) && serios[minor(dev)] != NULL); } int comspeed(dev_t dev, int sp) { EFI_STATUS status; SERIAL_IO_INTERFACE *serio = serios[minor(dev)]; int newsp; if (sp <= 0) return com_speed; if (!efi_valid_com(dev)) return pio_comspeed(dev, sp); if (serio->Mode->BaudRate != sp) { status = serio->SetAttributes(serio, sp, serio->Mode->ReceiveFifoDepth, serio->Mode->Timeout, serio->Mode->Parity, serio->Mode->DataBits, serio->Mode->StopBits); if (EFI_ERROR(status)) { printf("com%d: SetAttribute() failed with status=%d\n", minor(dev), status); return (-1); } if (com_speed != -1) printf("\ncom%d: %d baud\n", minor(dev), sp); } /* same as comspeed() in libsa/bioscons.c */ newsp = com_speed; com_speed = sp; return (newsp); } void efi_com_init(struct consdev *cn) { if (!efi_valid_com(cn->cn_dev)) /* This actually happens if the machine has another serial. */ return; if (com_speed == -1) comspeed(cn->cn_dev, 9600); /* default speed is 9600 baud */ } int efi_com_getc(dev_t dev) { EFI_STATUS status; SERIAL_IO_INTERFACE *serio; UINTN sz; u_char buf; static u_char lastchar = 0; if (!efi_valid_com(dev & 0x7f)) return pio_com_getc(dev); serio = serios[minor(dev & 0x7f)]; if (lastchar != 0) { int r = lastchar; if ((dev & 0x80) == 0) lastchar = 0; return (r); } for (;;) { sz = 1; status = serio->Read(serio, &sz, &buf); if (status == EFI_SUCCESS && sz > 0) break; if (status != EFI_TIMEOUT && EFI_ERROR(status)) panic("Error reading from serial status=%d", status); if (dev & 0x80) return (0); } if (dev & 0x80) lastchar = buf; return (buf); } void efi_com_putc(dev_t dev, int c) { SERIAL_IO_INTERFACE *serio; UINTN sz = 1; u_char buf; if (!efi_valid_com(dev)) { pio_com_putc(dev, c); return; } serio = serios[minor(dev)]; buf = c; serio->Write(serio, &sz, &buf); } /*********************************************************************** * Miscellaneous ***********************************************************************/ /* * ACPI GUID is confusing in UEFI spec. * {EFI_,}_ACPI_20_TABLE_GUID or EFI_ACPI_TABLE_GUID means * ACPI 2.0 or above. */ static EFI_GUID acpi_guid = ACPI_20_TABLE_GUID; static EFI_GUID smbios_guid = SMBIOS_TABLE_GUID; static EFI_GUID esrt_guid = EFI_SYSTEM_RESOURCE_TABLE_GUID; static int gopmode = -1; #define efi_guidcmp(_a, _b) memcmp((_a), (_b), sizeof(EFI_GUID)) static EFI_STATUS efi_gop_setmode(int mode) { EFI_STATUS status; status = gop->SetMode(gop, mode); if (EFI_ERROR(status) || gop->Mode->Mode != mode) printf("GOP SetMode() failed (%d)\n", status); return (status); } void efi_makebootargs(void) { int i; EFI_STATUS status; EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *gopi; bios_efiinfo_t *ei = &bios_efiinfo; int curmode; UINTN sz, gopsiz, bestsiz = 0; /* * ACPI, BIOS configuration table */ for (i = 0; i < ST->NumberOfTableEntries; i++) { if (efi_guidcmp(&acpi_guid, &ST->ConfigurationTable[i].VendorGuid) == 0) ei->config_acpi = (uintptr_t) ST->ConfigurationTable[i].VendorTable; else if (efi_guidcmp(&smbios_guid, &ST->ConfigurationTable[i].VendorGuid) == 0) ei->config_smbios = (uintptr_t) ST->ConfigurationTable[i].VendorTable; else if (efi_guidcmp(&esrt_guid, &ST->ConfigurationTable[i].VendorGuid) == 0) ei->config_esrt = (uintptr_t) ST->ConfigurationTable[i].VendorTable; } /* * Need to copy ESRT because call to ExitBootServices() frees memory of * type EfiBootServicesData in which ESRT resides. */ if (ei->config_esrt != 0) { EFI_SYSTEM_RESOURCE_TABLE *esrt = (EFI_SYSTEM_RESOURCE_TABLE *)ei->config_esrt; size_t esrt_size = sizeof(*esrt) + esrt->FwResourceCount * sizeof(EFI_SYSTEM_RESOURCE_ENTRY); void *esrt_copy; /* * Using EfiRuntimeServicesData as it maps to BIOS_MAP_RES, * while EfiLoaderData becomes BIOS_MAP_FREE. */ status = BS->AllocatePool(EfiRuntimeServicesData, esrt_size, &esrt_copy); if (status == EFI_SUCCESS) { memcpy(esrt_copy, esrt, esrt_size); ei->config_esrt = (uintptr_t)esrt_copy; ei->flags |= BEI_ESRT; } } /* * Frame buffer */ if (gop != NULL) { if (gopmode < 0) { for (i = 0; i < gop->Mode->MaxMode; i++) { status = gop->QueryMode(gop, i, &sz, &gopi); if (EFI_ERROR(status)) continue; gopsiz = gopi->HorizontalResolution * gopi->VerticalResolution; if (gopsiz > bestsiz) { gopmode = i; bestsiz = gopsiz; } } } if (gopmode >= 0 && gopmode != gop->Mode->Mode) { curmode = gop->Mode->Mode; if (efi_gop_setmode(gopmode) != EFI_SUCCESS) (void)efi_gop_setmode(curmode); } gopi = gop->Mode->Info; switch (gopi->PixelFormat) { case PixelBlueGreenRedReserved8BitPerColor: ei->fb_red_mask = 0x00ff0000; ei->fb_green_mask = 0x0000ff00; ei->fb_blue_mask = 0x000000ff; ei->fb_reserved_mask = 0xff000000; break; case PixelRedGreenBlueReserved8BitPerColor: ei->fb_red_mask = 0x000000ff; ei->fb_green_mask = 0x0000ff00; ei->fb_blue_mask = 0x00ff0000; ei->fb_reserved_mask = 0xff000000; break; case PixelBitMask: ei->fb_red_mask = gopi->PixelInformation.RedMask; ei->fb_green_mask = gopi->PixelInformation.GreenMask; ei->fb_blue_mask = gopi->PixelInformation.BlueMask; ei->fb_reserved_mask = gopi->PixelInformation.ReservedMask; break; default: break; } ei->fb_addr = gop->Mode->FrameBufferBase; ei->fb_size = gop->Mode->FrameBufferSize; ei->fb_height = gopi->VerticalResolution; ei->fb_width = gopi->HorizontalResolution; ei->fb_pixpsl = gopi->PixelsPerScanLine; } /* * EFI system table */ ei->system_table = (uintptr_t)ST; #ifdef __amd64__ ei->flags |= BEI_64BIT; #endif addbootarg(BOOTARG_EFIINFO, sizeof(bios_efiinfo), &bios_efiinfo); } /* Vendor device path used to indicate the mmio UART on AMD SoCs. */ #define AMDSOC_DEVPATH \ { 0xe76fd4e9, 0x0a30, 0x4ca9, \ { 0x95, 0x40, 0xd7, 0x99, 0x53, 0x4c, 0xc4, 0xff } } void efi_setconsdev(void) { bios_consdev_t cd; EFI_STATUS status; UINT8 data[128]; UINTN size = sizeof(data); EFI_DEVICE_PATH *dp = (void *)data; VENDOR_DEVICE_PATH *vdp; UART_DEVICE_PATH *udp; EFI_GUID global = EFI_GLOBAL_VARIABLE; EFI_GUID amdsoc = AMDSOC_DEVPATH; memset(&cd, 0, sizeof(cd)); cd.consdev = cn_tab->cn_dev; cd.conspeed = com_speed; cd.consaddr = com_addr; /* * If the ConOut variable indicates we're using a serial * console, use it to determine the baud rate. */ status = RS->GetVariable(L"ConOut", &global, NULL, &size, &data); if (status == EFI_SUCCESS) { for (dp = (void *)data; !IsDevicePathEnd(dp); dp = NextDevicePathNode(dp)) { /* * AMD Ryzen Embedded V1000 SoCs integrate a * Synopsys DesignWare UART that is not * compatible with the traditional 8250 UART * found on the IBM PC. Pass the magic * parameters to the kernel to make this UART * work. */ if (DevicePathType(dp) == HARDWARE_DEVICE_PATH && DevicePathSubType(dp) == HW_VENDOR_DP) { vdp = (VENDOR_DEVICE_PATH *)dp; if (efi_guidcmp(&vdp->Guid, &amdsoc) == 0) { cd.consdev = makedev(8, 4); cd.consaddr = *(uint64_t *)(vdp + 1); cd.consfreq = 48000000; cd.flags = BCD_MMIO; cd.reg_width = 4; cd.reg_shift = 2; } } if (DevicePathType(dp) == MESSAGING_DEVICE_PATH && DevicePathSubType(dp) == MSG_UART_DP) { udp = (UART_DEVICE_PATH *)dp; if (cd.conspeed == -1) cd.conspeed = udp->BaudRate; } } } addbootarg(BOOTARG_CONSDEV, sizeof(cd), &cd); } void _rtt(void) { #ifdef EFI_DEBUG printf("Hit any key to reboot\n"); efi_cons_getc(0); #endif RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL); for (;;) continue; } time_t getsecs(void) { EFI_TIME t; time_t r = 0; int y = 0; const int daytab[][14] = { { 0, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364 }, { 0, -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, }; #define isleap(_y) (((_y) % 4) == 0 && (((_y) % 100) != 0 || ((_y) % 400) == 0)) ST->RuntimeServices->GetTime(&t, NULL); /* Calc days from UNIX epoch */ r = (t.Year - 1970) * 365; for (y = 1970; y < t.Year; y++) { if (isleap(y)) r++; } r += daytab[isleap(t.Year)? 1 : 0][t.Month] + t.Day; /* Calc secs */ r *= 60 * 60 * 24; r += ((t.Hour * 60) + t.Minute) * 60 + t.Second; if (-24 * 60 < t.TimeZone && t.TimeZone < 24 * 60) r += t.TimeZone * 60; return (r); } u_int sleep(u_int i) { time_t t; u_int intr = 0; /* * Loop for the requested number of seconds, polling, * so that it may handle interrupts. */ for (t = getsecs() + i; intr == 0 && getsecs() < t; intr = cnischar()) ; return intr; } #ifdef IDLE_POWEROFF CHAR16 *idle_name = L"IdlePoweroff"; EFI_STATUS idle_status; /* randomly generated f948e8a9-0570-4338-ad10-29f4cf12849d */ EFI_GUID openbsd_guid = { 0xf948e8a9, 0x0570, 0x4338, { 0xad, 0x10, 0x29, 0xf4, 0xcf, 0x12, 0x84, 0x9d } }; /* Non-Volatile, Boot Service Access, Runtime Service Access */ UINT32 idle_attrs = 0x1 | 0x2 | 0x4; UINT16 idle_secs; UINTN idle_sz = sizeof(idle_secs); int get_idle_timeout(void) { idle_status = RS->GetVariable(idle_name, &openbsd_guid, NULL, &idle_sz, &idle_secs); if (idle_status != EFI_SUCCESS) { if (idle_status != EFI_NOT_FOUND) { printf("%s: %d\n", __func__, idle_status); return 1; } return -1; } return 0; } int set_idle_timeout(int secs) { idle_secs = secs; idle_sz = idle_secs > 0 ? sizeof(idle_secs) : 0; idle_status = RS->SetVariable(idle_name, &openbsd_guid, idle_attrs, idle_sz, &idle_secs); if (idle_status != EFI_SUCCESS) { printf("%s: %d\n", __func__, idle_status); return -1; } return 0; } /* see lib/libsa/softraid.c sr_crypto_passphrase_decrypt() */ void idle_poweroff(void) { if (get_idle_timeout() == 0 && sleep(idle_secs) == 0) { printf("\nno input after %us, powering off...\n", idle_secs); Xpoweroff_efi(); } } #endif /* IDLE_POWEROFF */ /*********************************************************************** * Commands ***********************************************************************/ int Xexit_efi(void) { BS->Exit(IH, 0, 0, NULL); for (;;) continue; return (0); } int Xvideo_efi(void) { int i, mode = -1; if (cmd.argc >= 2) { mode = strtol(cmd.argv[1], NULL, 10); if (0 <= mode && mode < nitems(efi_video) && efi_video[mode].cols > 0) { conout->SetMode(conout, mode); efi_video_reset(); } } else { for (i = 0; i < nitems(efi_video) && i < conout->Mode->MaxMode; i++) { if (efi_video[i].cols > 0) printf("Mode %d: %d x %d\n", i, efi_video[i].cols, efi_video[i].rows); } printf("\n"); } printf("Current Mode = %d\n", conout->Mode->Mode); return (0); } int Xpoweroff_efi(void) { RS->ResetSystem(EfiResetShutdown, EFI_SUCCESS, 0, NULL); return (0); } int Xgop_efi(void) { EFI_STATUS status; int i, mode = -1; UINTN sz; EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *gopi; if (gop == NULL) { printf("No GOP found\n"); return (0); } if (cmd.argc >= 2) { mode = strtol(cmd.argv[1], NULL, 10); if (0 <= mode && mode < gop->Mode->MaxMode) { status = gop->QueryMode(gop, mode, &sz, &gopi); if (!EFI_ERROR(status)) { if (efi_gop_setmode(mode) == EFI_SUCCESS) gopmode = mode; } } } else { for (i = 0; i < gop->Mode->MaxMode; i++) { status = gop->QueryMode(gop, i, &sz, &gopi); if (EFI_ERROR(status)) continue; printf("Mode %d: %d x %d (stride = %d)\n", i, gopi->HorizontalResolution, gopi->VerticalResolution, gopi->PixelsPerScanLine); } printf("\n"); } printf("Current Mode = %d\n", gop->Mode->Mode); return (0); } #ifdef IDLE_POWEROFF int Xidle_efi(void) { if (cmd.argc >= 2) { int secs; secs = strtol(cmd.argv[1], NULL, 10); if (0 <= secs && secs < UINT16_MAX) set_idle_timeout(secs); } else { if (get_idle_timeout() == 0) printf("Timeout = %us\n", idle_secs); } return 0; } #endif /* IDLE_POWEROFF */