--- stand/efi/boot1/boot1.c.orig 2018-02-11 19:57:57.048971000 +0900 +++ stand/efi/boot1/boot1.c 2018-02-12 09:59:58.282714000 +0900 @@ -29,6 +29,9 @@ #include #include +#ifdef BOOT1EFI_BOOT1 +#include +#endif #include #include "boot_module.h" @@ -48,11 +49,56 @@ static const boot_module_t *boot_modules }; const UINTN num_boot_modules = nitems(boot_modules); +#ifdef BOOT1EFI_BOOT1 +/* For boot partition menu */ +static int devpath_node_str(char *, size_t, EFI_DEVICE_PATH *); +int devpath_strlcat(char *, size_t, EFI_DEVICE_PATH *); +char *devpath_str(EFI_DEVICE_PATH *); +void efi_cons_putchar(int); +int efi_cons_getchar(void); +int efi_cons_poll(void); +int getchar(void); +char *humanize_number_kmgt(UINT64); +static SIMPLE_TEXT_OUTPUT_INTERFACE *conout; +static SIMPLE_INPUT_INTERFACE *conin; +#endif + static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL; static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL; static EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL; static EFI_GUID ConsoleControlGUID = EFI_CONSOLE_CONTROL_PROTOCOL_GUID; +#ifdef BOOT1EFI_BOOT1 +#define DEFAULT_FGCOLOR EFI_LIGHTGRAY +#define DEFAULT_BGCOLOR EFI_BLACK + +/* For boot partition menu */ +char *humanize_number_kmgt(UINT64 size) { + static char buf2[6]; + UINT64 size0; + + if (size < 1024) + sprintf(buf2, "%luB", size); + else if (size0 = (size*10+512)/1024, size0 < 100) + sprintf(buf2, "%lu.%luK", size0 / 10, size0 % 10); + else if (size0 = (size+512)/1024, size0 < 1024) + sprintf(buf2, "%luK", size0); + else if (size0 = (size*10/1024+512)/1024, size0 < 100) + sprintf(buf2, "%lu.%luM", size0 / 10, size0 % 10); + else if (size0 = (size/1024+512)/1024, size0 < 1024) + sprintf(buf2, "%luM", size0); + else if (size0 = (size*10/1024/1024+512)/1024, size0 < 100) + sprintf(buf2, "%lu.%luG", size0 / 10, size0 % 10); + else if (size0 = (size/1024/1024+512)/1024, size0 < 1024) + sprintf(buf2, "%luG", size0); + else if (size0 = (size*10/1024/1024/1024+512)/1024, size0 < 100) + sprintf(buf2, "%lu.%luT", size0 / 10, size0 % 10); + else + sprintf(buf2, "%luT", (size/1024/1024/1024+512)/1024); + return(buf2); +} +#endif + /* * Provide Malloc / Free backed by EFIs AllocatePool / FreePool which ensures * memory is correctly aligned avoiding EFI_INVALID_PARAMETER returns from @@ -89,7 +130,363 @@ devpath_last(EFI_DEVICE_PATH *devpath) return (res); } +#ifdef BOOT1EFI_BOOT1 +/* + * Put back for boot partition menu support. + * devpath_node_str is a basic output method for a devpath node which + * only understands a subset of the available sub types. + * + * If we switch to UEFI 2.x then we should update it to use: + * EFI_DEVICE_PATH_TO_TEXT_PROTOCOL. + */ +static int +devpath_node_str(char *buf, size_t size, EFI_DEVICE_PATH *devpath) +{ + switch (devpath->Type) { + case MESSAGING_DEVICE_PATH: + switch (devpath->SubType) { + case MSG_ATAPI_DP: { + ATAPI_DEVICE_PATH *atapi; + + atapi = (ATAPI_DEVICE_PATH *)(void *)devpath; + return snprintf(buf, size, "ata(%s,%s,0x%x)", + (atapi->PrimarySecondary == 1) ? "Sec" : "Pri", + (atapi->SlaveMaster == 1) ? "Slave" : "Master", + atapi->Lun); + } + case MSG_USB_DP: { + USB_DEVICE_PATH *usb; + + usb = (USB_DEVICE_PATH *)devpath; + return snprintf(buf, size, "usb(0x%02x,0x%02x)", + usb->ParentPortNumber, usb->InterfaceNumber); + } + case MSG_SCSI_DP: { + SCSI_DEVICE_PATH *scsi; + + scsi = (SCSI_DEVICE_PATH *)(void *)devpath; + return snprintf(buf, size, "scsi(0x%02x,0x%02x)", + scsi->Pun, scsi->Lun); + } + case MSG_SATA_DP: { + SATA_DEVICE_PATH *sata; + + sata = (SATA_DEVICE_PATH *)(void *)devpath; + return snprintf(buf, size, "sata(0x%x,0x%x,0x%x)", + sata->HBAPortNumber, sata->PortMultiplierPortNumber, + sata->Lun); + } + default: + return snprintf(buf, size, "msg(0x%02x)", + devpath->SubType); + } + break; + case HARDWARE_DEVICE_PATH: + switch (devpath->SubType) { + case HW_PCI_DP: { + PCI_DEVICE_PATH *pci; + + pci = (PCI_DEVICE_PATH *)devpath; + return snprintf(buf, size, "pci(0x%02x,0x%02x)", + pci->Device, pci->Function); + } + default: + return snprintf(buf, size, "hw(0x%02x)", + devpath->SubType); + } + break; + case ACPI_DEVICE_PATH: { + ACPI_HID_DEVICE_PATH *acpi; + + acpi = (ACPI_HID_DEVICE_PATH *)(void *)devpath; + if ((acpi->HID & PNP_EISA_ID_MASK) == PNP_EISA_ID_CONST) { + switch (EISA_ID_TO_NUM(acpi->HID)) { + case 0x0a03: + return snprintf(buf, size, "pciroot(0x%x)", + acpi->UID); + case 0x0a08: + return snprintf(buf, size, "pcieroot(0x%x)", + acpi->UID); + case 0x0604: + return snprintf(buf, size, "floppy(0x%x)", + acpi->UID); + case 0x0301: + return snprintf(buf, size, "keyboard(0x%x)", + acpi->UID); + case 0x0501: + return snprintf(buf, size, "serial(0x%x)", + acpi->UID); + case 0x0401: + return snprintf(buf, size, "parallelport(0x%x)", + acpi->UID); + default: + return snprintf(buf, size, "acpi(pnp%04x,0x%x)", + EISA_ID_TO_NUM(acpi->HID), acpi->UID); + } + } + + return snprintf(buf, size, "acpi(0x%08x,0x%x)", acpi->HID, + acpi->UID); + } + case MEDIA_DEVICE_PATH: + switch (devpath->SubType) { + case MEDIA_CDROM_DP: { + CDROM_DEVICE_PATH *cdrom; + + cdrom = (CDROM_DEVICE_PATH *)(void *)devpath; + return snprintf(buf, size, "cdrom(%x)", + cdrom->BootEntry); + } + case MEDIA_HARDDRIVE_DP: { + HARDDRIVE_DEVICE_PATH *hd; + + hd = (HARDDRIVE_DEVICE_PATH *)(void *)devpath; + return snprintf(buf, size, "hd(p%d) (%s)", + hd->PartitionNumber, + humanize_number_kmgt(hd->PartitionSize * 512)); + } + default: + return snprintf(buf, size, "media(0x%02x)", + devpath->SubType); + } + case BBS_DEVICE_PATH: + return snprintf(buf, size, "bbs(0x%02x)", devpath->SubType); + case END_DEVICE_PATH_TYPE: + return (0); + } + + return snprintf(buf, size, "type(0x%02x, 0x%02x)", devpath->Type, + devpath->SubType); +} + +/* + * devpath_strlcat appends a text description of devpath to buf but not more + * than size - 1 characters followed by NUL-terminator. + */ +int +devpath_strlcat(char *buf, size_t size, EFI_DEVICE_PATH *devpath) +{ + size_t len, used; + const char *sep; + + sep = ""; + used = 0; + while (!IsDevicePathEnd(devpath)) { + len = snprintf(buf, size - used, "%s", sep); + used += len; + if (used > size) + return (used); + buf += len; + + len = devpath_node_str(buf, size - used, devpath); + used += len; + if (used > size) + return (used); + buf += len; + devpath = NextDevicePathNode(devpath); + sep = ":"; + } + + return (used); +} + +/* + * devpath_str is convenience method which returns the text description of + * devpath using a static buffer, so it isn't thread safe! + */ +char * +devpath_str(EFI_DEVICE_PATH *devpath) +{ + static char buf[256]; + + devpath_strlcat(buf, sizeof(buf), devpath); + + return buf; +} + + + +/* quick dirty work. */ +#define NUM_DEV_LIST 35 + +typedef struct { + const boot_module_t *modp; + dev_info_t *devinfop; +} moddev_t; + +static moddev_t dev_list[NUM_DEV_LIST]; + +static int +list_devices(void) +{ + UINTN i, j; + dev_info_t *dev; + const boot_module_t *mod; + + j = 0; + for (i = 0; i < num_boot_modules; i++) { + if (boot_modules[i] == NULL) + continue; + mod = boot_modules[i]; + for (dev = mod->devices(); dev != NULL; dev = dev->next) { + dev_list[j].devinfop = dev; + dev_list[j].modp = mod; + j++; + if (j >= NUM_DEV_LIST) + break; + } + } + + return (j); +} + +#define SELECT_TIMEOUT 10 + +static char +idx2char(int i) +{ + return (i<10 ? '0'+i : 'a'+i-10); +} + +static int +char2idx(char c) +{ + return ((c >= '0' && c <= '9') ? c - '0' : + (c >= 'A' && c <= 'Z') ? c - 'A' + 10 : + (c >= 'a' && c <= 'z') ? c - 'a' + 10 : + -1 ); +} + +static void +move_to_tol() +{ + int x,y; + + x = conout->Mode->CursorColumn; + y = conout->Mode->CursorRow; + conout->SetCursorPosition(conout, 0, y); +} + +static EFI_STATUS +select_bootdev(int ndevs, const boot_module_t **modp, dev_info_t **devinfop, + void **bufp, size_t *bufsize) +{ + int i; + int c, n; + int time_left; + EFI_STATUS status; + EFI_EVENT timer; + EFI_EVENT events[2]; + UINTN idx; + dev_info_t *dev; + const boot_module_t *mod; + + if ((ndevs <= 0) || (ndevs >= NUM_DEV_LIST)) { + return (EFI_NOT_FOUND); + } else if (ndevs == 1) { + /* Only one candidate. */ + *modp = mod = dev_list[0].modp; + *devinfop = dev = dev_list[0].devinfop; + return (mod->load(PATH_LOADER_EFI, dev, + bufp, bufsize)); + } + /* Two or more candidate exist. */ + status = BS->CreateEvent(EVT_TIMER, 0, 0, NULL, &timer); + if (status != EFI_SUCCESS) { + printf("Can't allocate timer event.\n"); + return (status); + } + printf(" 0: AutoSelected Partition (Default): 0\n"); + for (i = 0; i < ndevs; i++) { + if (dev_list[i].devinfop->preferred == TRUE) + c = '*'; + else + c = ' '; + printf(" %c%c: %s: %s: %c\n", c, idx2char(i+1), + dev_list[i].modp->name, + devpath_str(dev_list[i].devinfop->devpath), + idx2char(i+1)); + } + /* One alpha-num selection only. Is this big enough ?? */ + BS->SetTimer(timer, TimerPeriodic, 10000000); + events[0] = timer; + events[1] = conin->WaitForKey; + time_left = SELECT_TIMEOUT; + + while (1) { + if (time_left > 0) { + printf("Select from 0 to %c. Timeout in %2d seconds," + " [Space] to pause : ", + idx2char(ndevs), time_left); + } + status = BS->WaitForEvent(2, events, &idx); + if (status != EFI_SUCCESS) { + BS->CloseEvent(timer); + return (status); + } + if (idx == 0) { + time_left--; + if (time_left <=0) { + printf("\nTimeout. " + "Partition is AutoSelected.\n"); + n = 0; + break; + } else { + move_to_tol(); + } + } + if (idx == 1) { + c = getchar(); + if ((c == '\n') || (c == '\r')) { + putchar('\n'); + n = 0; + break; + } else if ((time_left > 0) && (c == ' ')) { + BS->SetTimer(timer, TimerCancel, 0); + time_left = -1; + printf("\nTimer stopeed.\n Please Key in: "); + } + n = char2idx(c); + if ((n >= 0) && (n <= ndevs)) { + BS->SetTimer(timer, TimerCancel, 0); + time_left = -1; + printf("\n %c is selected.\n", c); + if (n == 0) { /* AutoSelect */ + break; + } else { + mod = dev_list[n-1].modp; + dev = dev_list[n-1].devinfop; + status = mod->load(PATH_LOADER_EFI, + dev, bufp, bufsize); + if (status == EFI_SUCCESS) { + break; + } + printf("Failed to load '%s'\n", + PATH_LOADER_EFI); + printf("Please select again: "); + } + } else { + /* Invalid charecter. */ + move_to_tol(); + } + } + }; + BS->CloseEvent(timer); + if (n == 0) { /* AutoSelect */ + status = load_loader(modp, devinfop, bufp, bufsize, 1); + if (status != EFI_SUCCESS) { + status = load_loader(modp, devinfop, bufp, bufsize, 0); + } + } else { + *modp = dev_list[n-1].modp; + *devinfop = dev_list[n-1].devinfop; + status = EFI_SUCCESS; + } + return (status); +} +#endif + /* * try_boot only returns if it fails to load the loader. If it succeeds * it simply boots, otherwise it returns the status of last EFI call. @@ -98,11 +527,24 @@ try_boot(const boot_module_t *mod, dev_info_t *dev, void *loaderbuf, size_t loadersize) try_boot(const boot_module_t *mod, dev_info_t *dev, void *loaderbuf, size_t loadersize) { size_t bufsize, cmdsize; void *buf; char *cmd; EFI_HANDLE loaderhandle; EFI_LOADED_IMAGE *loaded_image; EFI_STATUS status; +#ifdef BOOT1EFI_BOOT1 + int ndevs; /* For boot partition menu */ + + /* For boot partition menu */ + ndevs = list_devices(); + status = select_bootdev(ndevs, &mod, &dev, &loaderbuf, &loadersize); + + if (status != EFI_SUCCESS) { + /* For boot partition menu */ + printf("Failed to load '%s'\n", PATH_LOADER_EFI); + return (EFI_NOT_FOUND); + } +#endif /* * Read in and parse the command line from /boot.config or /boot/config, @@ -151,6 +677,11 @@ try_boot(void) loaded_image->LoadOptionsSize = cmdsize; loaded_image->LoadOptions = cmd; +#ifdef BOOT1EFI_BOOT1 + /* For boot partition menu */ + DPRINTF("Boot from:'%s' in 5 seconds...", devpath_str(dev->devpath)); +#else DPRINTF("Starting '%s' in 5 seconds...", PATH_LOADER_EFI); +#endif DSTALL(1000000); DPRINTF("."); @@ -182,23 +708,36 @@ errout: return (status); } +#ifndef BOOT1EFI_BOOT1 EFI_STATUS efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE *Xsystab) +#else + +/* Divide efi_main into two for boot partition menu */ +static EFI_STATUS +init_console() +#endif { +#ifndef BOOT1EFI_BOOT1 EFI_HANDLE *handles; EFI_LOADED_IMAGE *img; EFI_DEVICE_PATH *imgpath; +#endif EFI_STATUS status; EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL; +#ifndef BOOT1EFI_BOOT1 SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL; UINTN i, hsize, nhandles; CHAR16 *text; +#endif +#ifndef BOOT1EFI_BOOT1 /* Basic initialization*/ ST = Xsystab; IH = Ximage; BS = ST->BootServices; RS = ST->RuntimeServices; +#endif /* Set up the console, so printf works. */ status = BS->LocateProtocol(&ConsoleControlGUID, NULL, @@ -210,13 +736,47 @@ efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_T * Reset the console enable the cursor. Later we'll choose a bette * console size through GOP/UGA. */ +#ifdef BOOT1EFI_BOOT1 + conin = ST->ConIn; +#endif conout = ST->ConOut; +#ifdef BOOT1EFI_BOOT1 + conin->Reset(conin, TRUE); +#endif conout->Reset(conout, TRUE); /* Explicitly set conout to mode 0, 80x25 */ conout->SetMode(conout, 0); +#ifdef BOOT1EFI_BOOT1 + + conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR, + DEFAULT_BGCOLOR)); +#endif conout->EnableCursor(conout, TRUE); conout->ClearScreen(conout); +#ifdef BOOT1EFI_BOOT1 + return (status); +} + +EFI_STATUS +efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE *Xsystab) +{ + EFI_HANDLE *handles; + EFI_LOADED_IMAGE *img; + EFI_DEVICE_PATH *imgpath; + EFI_STATUS status; + UINTN i, hsize, nhandles; + CHAR16 *text; + + /* Basic initialization*/ + ST = Xsystab; + IH = Ximage; + BS = Xsystab->BootServices; + RS = ST->RuntimeServices; + + init_console(); + +#endif printf("\n>> FreeBSD EFI boot block\n"); printf(" Loader path: %s\n\n", PATH_LOADER_EFI); printf(" Initializing modules:"); @@ -310,17 +836,74 @@ efi_panic(EFI_STATUS s, const char *fmt, BS->Exit(IH, s, 0, NULL); } +#ifdef BOOT1EFI_BOOT1 +/* Make putchar as wrapper for newly added efi_cons_putchar() for boot partition menu */ +#endif void putchar(int c) { +#ifdef BOOT1EFI_BOOT1 + efi_cons_putchar(c); +} + +void +efi_cons_putchar(int c) +{ +#endif CHAR16 buf[2]; if (c == '\n') { buf[0] = '\r'; buf[1] = 0; +#ifdef BOOT1EFI_BOOT1 + conout->OutputString(conout, buf); +#else ST->ConOut->OutputString(ST->ConOut, buf); +#endif } buf[0] = c; buf[1] = 0; +#ifdef BOOT1EFI_BOOT1 + conout->OutputString(conout, buf); +} + +int +getchar(void) +{ + return efi_cons_getchar(); +#else ST->ConOut->OutputString(ST->ConOut, buf); +#endif } +#ifdef BOOT1EFI_BOOT1 + + +int +efi_cons_getchar() +{ + EFI_INPUT_KEY key; + EFI_STATUS status; + UINTN junk; + + /* Try to read a key stroke. We wait for one if none is pending. */ + status = conin->ReadKeyStroke(conin, &key); + if (status == EFI_NOT_READY) { + BS->WaitForEvent(1, &conin->WaitForKey, &junk); + status = conin->ReadKeyStroke(conin, &key); + } + switch (key.ScanCode) { + case 0x17: /* ESC */ + return (0x1b); /* esc */ + } + + /* this can return */ + return (key.UnicodeChar); +} + +int +efi_cons_poll() +{ + /* This can clear the signaled state. */ + return (BS->CheckEvent(conin->WaitForKey) == EFI_SUCCESS); +} +#endif --- stand/efi/boot1/Makefile.orig 2018-02-11 19:57:57.048971000 +0900 +++ stand/efi/boot1/Makefile 2018-02-12 09:59:58.282714000 +0900 @@ -118,4 +118,10 @@ CLEANFILES+= ${BOOT1}.efi ${BOOT1}.efifat .endif +.if ${BOOT1} == "gptboot" +CFLAGS+= -DBOOT1EFI_GPTBOOT +.else +CFLAGS+= -DBOOT1EFI_BOOT1 +.endif + .include --- stand/efi/boot1/proto.h.orig 2018-02-11 19:57:57.048971000 +0900 +++ stand/efi/boot1/proto.h 2018-02-12 09:59:58.282714000 +0900 @@ -27,3 +27,7 @@ void choice_protocol(EFI_HANDLE *handles, UINTN nhandles, EFI_DEVICE_PATH *imgpath); EFI_STATUS try_boot(const boot_module_t *mod, dev_info_t *dev, void *loaderbuf, size_t loadersize); +#ifdef BOOT1EFI_BOOT1 +EFI_STATUS load_loader(const boot_module_t **modp, dev_info_t **devinfop, + void **bufp, size_t *bufsize, int preferred); +#endif --- stand/efi/boot1/proto.c.orig 2018-02-11 19:57:57.048971000 +0900 +++ stand/efi/boot1/proto.c 2018-02-12 09:59:58.282714000 +0900 @@ -125,7 +125,7 @@ * * Only devices which have preferred matching the preferred parameter are tried. */ -static EFI_STATUS +EFI_STATUS load_loader(const boot_module_t **modp, dev_info_t **devinfop, void **bufp, size_t *bufsize, int preferred) {