--- stand/efi//boot1/proto.c.orig 2020-03-12 09:06:54.000000000 +0900 +++ stand/efi/boot1/proto.c 2020-03-24 00:17:42.629508000 +0900 @@ -30,6 +30,7 @@ #include #include #include +#include #include "boot_module.h" #include "paths.h" @@ -45,7 +46,325 @@ "better" }; +/* quick and dirty work for boot partition menu */ + +#define NUM_DEV_LIST 35 +#define SELECT_TIMEOUT 10 + +typedef struct { + const boot_module_t *modp; + dev_info_t *devinfop; +} moddev_t; + +static moddev_t dev_list[NUM_DEV_LIST]; + +static EFI_STATUS +load_loader(const boot_module_t **modp, dev_info_t **devinfop, void **bufp, + size_t *bufsize, int preferred); + +static 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); +} + +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() +{ + SIMPLE_TEXT_OUTPUT_INTERFACE *conout; + int x,y; + + conout = ST->ConOut; + x = conout->Mode->CursorColumn; + y = conout->Mode->CursorRow; + conout->SetCursorPosition(conout, 0, y); +} + +static int +mygetchar(void) +{ + EFI_INPUT_KEY key; + EFI_STATUS status; + UINTN junk; + + /* Try to read a key stroke. We wait for one if none is pending. */ + status = ST->ConIn->ReadKeyStroke(ST->ConIn, &key); + if (status == EFI_NOT_READY) { + BS->WaitForEvent(1, &ST->ConIn->WaitForKey, &junk); + status = ST->ConIn->ReadKeyStroke(ST->ConIn, &key); + } + switch (key.ScanCode) { + case 0x17: /* ESC */ + return (0x1b); /* esc */ + } + + /* this can return */ + return (key.UnicodeChar); +} + /* + * Output of EFI_DEVICE_PATH_TO_TEXT_PROTOCOL is not easy to read. + " I hope this looks better. + */ +static int +mediapath_node_str(char *buf, size_t size, EFI_DEVICE_PATH *devpath) +{ + int rv; + char *str; + + switch (devpath->Type) { + 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; + uuid_to_string((const uuid_t *)(void *)&hd->Signature[0], &str, &rv); + if (rv != uuid_s_ok) { + rv = snprintf(buf, size, "hd(p%d) (%s)", + hd->PartitionNumber, + humanize_number_kmgt(hd->PartitionSize * 512)); + } else { + rv = snprintf(buf, size, "hd(p%d) (%s) uuid:%s", + hd->PartitionNumber, + humanize_number_kmgt(hd->PartitionSize * 512), str); + free(str); + } + return rv; + } + default: + return snprintf(buf, size, "media(0x%02x)", + devpath->SubType); + } + } + + return snprintf(buf, size, "type(0x%02x, 0x%02x)", devpath->Type, + devpath->SubType); +} + +/* + * mediapath_str is convenience method which returns the text description of + * mediapath using a static buffer, so it isn't thread safe! + */ +static char * +mediapath_str(EFI_DEVICE_PATH *devpath) +{ + static char buf[256]; + + mediapath_node_str(buf, sizeof(buf), devpath); + + return buf; +} + +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); +} + + +static int +get_sel(int ndevs,int time_left) +{ + int i; + int c, n; + EFI_STATUS status; + EFI_EVENT timer; + EFI_EVENT events[2]; + EFI_DEVICE_PATH *mediapath; + UINTN idx; + + status = BS->CreateEvent(EVT_TIMER, 0, 0, NULL, &timer); + if (status != EFI_SUCCESS) { + printf("Can't allocate timer event.\n"); + return (-1); + } + printf(" 0: AutoSelected Partition (Default): 0\n"); + for (i = 0; i < ndevs; i++) { + if (dev_list[i].devinfop->preferred == TRUE) + c = '*'; + else + c = ' '; + mediapath = efi_devpath_to_media_path( + dev_list[i].devinfop->devpath); + printf(" %c%c: %s: %s: %c\n", c, idx2char(i+1), + dev_list[i].modp->name, + mediapath_str(mediapath), + idx2char(i+1)); + } + /* One alpha-num selection only. Is this big enough ?? */ + BS->SetTimer(timer, TimerPeriodic, 10000000); + events[0] = timer; + events[1] = ST->ConIn->WaitForKey; + + 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 (-1); + } + 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 = mygetchar(); + 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); + break; + } else { + /* Invalid charecter. */ + move_to_tol(); + } + } + }; + BS->CloseEvent(timer); + return (n); +} + +static EFI_STATUS +select_loader(const boot_module_t **modp, dev_info_t **devinfop, + void **bufp, size_t *bufsize) +{ + int n; + int time_left; + EFI_STATUS status; + dev_info_t *dev; + const boot_module_t *mod; + int ndevs; + + ndevs = list_devices(); + if ((ndevs <= 0) || (ndevs >= NUM_DEV_LIST)) { + return (EFI_NOT_FOUND); + } else if (ndevs == 1) { + /* Only one condidate. */ + *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. */ + + /* Reset the console input. */ + ST->ConIn->Reset(ST->ConIn, TRUE); + + time_left = SELECT_TIMEOUT; + while (1) { + n = get_sel(ndevs, time_left); + if (n < 0) { + return (EFI_NOT_FOUND); + } else 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: "); + time_left = -1; + } + } + if (n == 0) { /* AutoSelect */ + status = load_loader(modp, devinfop, bufp, bufsize, TRUE); + if (status != EFI_SUCCESS) { + status = load_loader(modp, devinfop, bufp, bufsize, FALSE); + } + } else { + *modp = dev_list[n-1].modp; + *devinfop = dev_list[n-1].devinfop; + status = EFI_SUCCESS; + } + return (status); +} + +/* * probe_handle determines if the passed handle represents a logical partition * if it does it uses each module in order to probe it and if successful it * returns EFI_SUCCESS. @@ -210,13 +529,10 @@ boot_modules[i]->status(); } - status = load_loader(&mod, &dev, &loaderbuf, &loadersize, 1); + status = select_loader(&mod, &dev, &loaderbuf, &loadersize); if (status != EFI_SUCCESS) { - status = load_loader(&mod, &dev, &loaderbuf, &loadersize, 0); - if (status != EFI_SUCCESS) { - printf("Failed to load '%s'\n", PATH_LOADER_EFI); - return; - } + printf("Failed to load '%s'\n", PATH_LOADER_EFI); + return; } try_boot(mod, dev, loaderbuf, loadersize);