Index: sys/boot/efi/boot1/boot1.c =================================================================== --- sys/boot/efi/boot1/boot1.c (revision 295683) +++ sys/boot/efi/boot1/boot1.c (working copy) @@ -29,6 +29,7 @@ #include #include +#include #include "boot_module.h" #include "paths.h" @@ -47,12 +48,19 @@ /* The initial number of handles used to query EFI for partitions. */ #define NUM_HANDLES_INIT 24 +void efi_cons_putchar(int); +int efi_cons_getchar(void); +int efi_cons_poll(void); +int getchar(void); void putchar(int c); +char *humanize_number_kmgt(UINT64); EFI_STATUS efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE* Xsystab); EFI_SYSTEM_TABLE *systab; EFI_BOOT_SERVICES *bs; static EFI_HANDLE *image; +static SIMPLE_TEXT_OUTPUT_INTERFACE *conout; +static SIMPLE_INPUT_INTERFACE *conin; static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL; static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL; @@ -59,6 +67,35 @@ static EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL; static EFI_GUID ConsoleControlGUID = EFI_CONSOLE_CONTROL_PROTOCOL_GUID; + +#define DEFAULT_FGCOLOR EFI_LIGHTGRAY +#define DEFAULT_BGCOLOR EFI_BLACK + +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); +} + /* * Provide Malloc / Free backed by EFIs AllocatePool / FreePool which ensures * memory is correctly aligned avoiding EFI_INVALID_PARAMETER returns from @@ -151,7 +188,6 @@ static int devpath_node_str(char *buf, size_t size, EFI_DEVICE_PATH *devpath) { - switch (devpath->Type) { case MESSAGING_DEVICE_PATH: switch (devpath->SubType) { @@ -251,8 +287,9 @@ HARDDRIVE_DEVICE_PATH *hd; hd = (HARDDRIVE_DEVICE_PATH *)(void *)devpath; - return snprintf(buf, size, "hd(%x)", - hd->PartitionNumber); + return snprintf(buf, size, "hd(p%d) (%s)", + hd->PartitionNumber, + humanize_number_kmgt(hd->PartitionSize * 512)); } default: return snprintf(buf, size, "media(0x%02x)", @@ -350,6 +387,186 @@ return (EFI_NOT_FOUND); } +/* 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 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. */ + 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, 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); +} + /* * 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. @@ -358,7 +575,7 @@ try_boot() { size_t bufsize, loadersize, cmdsize; - void *buf, *loaderbuf; + void *buf = NULL, *loaderbuf = NULL; char *cmd; dev_info_t *dev; const boot_module_t *mod; @@ -365,15 +582,13 @@ EFI_HANDLE loaderhandle; EFI_LOADED_IMAGE *loaded_image; EFI_STATUS status; + int ndevs; - status = load_loader(&mod, &dev, &loaderbuf, &loadersize, TRUE); + ndevs = list_devices(); + status = select_bootdev(ndevs, &mod, &dev, &loaderbuf, &loadersize); if (status != EFI_SUCCESS) { - status = load_loader(&mod, &dev, &loaderbuf, &loadersize, - FALSE); - if (status != EFI_SUCCESS) { - printf("Failed to load '%s'\n", PATH_LOADER_EFI); - return (status); - } + printf("Failed to load '%s'\n", PATH_LOADER_EFI); + return (EFI_NOT_FOUND); } /* @@ -423,6 +638,9 @@ loaded_image->LoadOptionsSize = cmdsize; loaded_image->LoadOptions = cmd; + printf("Boot from: %s in 1 second\n", devpath_str(dev->devpath)); + bs->Stall(1000000); + DPRINTF("Starting '%s' in 5 seconds...", PATH_LOADER_EFI); DSTALL(1000000); DPRINTF("."); @@ -559,21 +777,13 @@ DSTALL(500000); } -EFI_STATUS -efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE *Xsystab) +static EFI_STATUS +init_console() { - EFI_HANDLE *handles; - EFI_LOADED_IMAGE *img; - EFI_DEVICE_PATH *imgpath; EFI_STATUS status; EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL; - SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL; - UINTN i, max_dim, best_mode, cols, rows, hsize, nhandles; - /* Basic initialization*/ - systab = Xsystab; - image = Ximage; - bs = Xsystab->BootServices; + UINTN i, max_dim, best_mode, cols, rows; /* Set up the console, so printf works. */ status = bs->LocateProtocol(&ConsoleControlGUID, NULL, @@ -584,8 +794,11 @@ /* * Reset the console and find the best text mode. */ + conin = systab->ConIn; conout = systab->ConOut; + conin->Reset(conin, TRUE); conout->Reset(conout, TRUE); + max_dim = best_mode = 0; for (i = 0; ; i++) { status = conout->QueryMode(conout, i, &cols, &rows); @@ -598,9 +811,31 @@ } if (max_dim > 0) conout->SetMode(conout, best_mode); + + conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR, + DEFAULT_BGCOLOR)); conout->EnableCursor(conout, TRUE); conout->ClearScreen(conout); + 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; + + /* Basic initialization*/ + systab = Xsystab; + image = Ximage; + bs = Xsystab->BootServices; + + init_console(); + printf("\n>> FreeBSD EFI boot block\n"); printf(" Loader path: %s\n\n", PATH_LOADER_EFI); printf(" Initializing modules:"); @@ -715,14 +950,57 @@ void putchar(int c) { + efi_cons_putchar(c); +} + +void +efi_cons_putchar(int c) +{ CHAR16 buf[2]; if (c == '\n') { buf[0] = '\r'; buf[1] = 0; - systab->ConOut->OutputString(systab->ConOut, buf); + conout->OutputString(conout, buf); } buf[0] = c; buf[1] = 0; - systab->ConOut->OutputString(systab->ConOut, buf); + conout->OutputString(conout, buf); } + +int +getchar(void) +{ + return efi_cons_getchar(); +} + + +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); +} +