boot.c revision 1.10
1/* $NetBSD: boot.c,v 1.10 2018/04/11 10:32:09 nonaka Exp $ */ 2 3/*- 4 * Copyright (c) 2016 Kimihiro Nonaka <nonaka@netbsd.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29#include "efiboot.h" 30 31#include <sys/bootblock.h> 32#include <sys/boot_flag.h> 33#include <machine/limits.h> 34 35#include "bootcfg.h" 36#include "bootmod.h" 37#include "bootmenu.h" 38#include "devopen.h" 39 40int errno; 41int boot_biosdev; 42daddr_t boot_biossector; 43 44extern const char bootprog_name[], bootprog_rev[], bootprog_kernrev[]; 45 46extern struct x86_boot_params boot_params; 47extern char twiddle_toggle; 48 49static const char * const names[][2] = { 50 { "netbsd", "netbsd.gz" }, 51 { "onetbsd", "onetbsd.gz" }, 52 { "netbsd.old", "netbsd.old.gz" }, 53}; 54 55#define NUMNAMES __arraycount(names) 56#define DEFFILENAME names[0][0] 57 58#ifndef EFIBOOTCFG_FILENAME 59#define EFIBOOTCFG_FILENAME "esp:/EFI/NetBSD/boot.cfg" 60#endif 61 62void command_help(char *); 63void command_quit(char *); 64void command_boot(char *); 65void command_consdev(char *); 66void command_dev(char *); 67void command_devpath(char *); 68void command_efivar(char *); 69void command_gop(char *); 70#if LIBSA_ENABLE_LS_OP 71void command_ls(char *); 72#endif 73void command_memmap(char *); 74#ifndef SMALL 75void command_menu(char *); 76#endif 77void command_modules(char *); 78void command_multiboot(char *); 79void command_text(char *); 80void command_version(char *); 81 82const struct bootblk_command commands[] = { 83 { "help", command_help }, 84 { "?", command_help }, 85 { "quit", command_quit }, 86 { "boot", command_boot }, 87 { "consdev", command_consdev }, 88 { "dev", command_dev }, 89 { "devpath", command_devpath }, 90 { "efivar", command_efivar }, 91 { "fs", fs_add }, 92 { "gop", command_gop }, 93 { "load", module_add }, 94#if LIBSA_ENABLE_LS_OP 95 { "ls", command_ls }, 96#endif 97 { "memmap", command_memmap }, 98#ifndef SMALL 99 { "menu", command_menu }, 100#endif 101 { "modules", command_modules }, 102 { "multiboot", command_multiboot }, 103 { "rndseed", rnd_add }, 104 { "splash", splash_add }, 105 { "text", command_text }, 106 { "userconf", userconf_add }, 107 { "version", command_version }, 108 { NULL, NULL }, 109}; 110 111static char *default_devname; 112static int default_unit, default_partition; 113static const char *default_filename; 114 115static char *sprint_bootsel(const char *); 116static void bootit(const char *, int); 117 118int 119parsebootfile(const char *fname, char **fsname, char **devname, int *unit, 120 int *partition, const char **file) 121{ 122 const char *col; 123 124 *fsname = "ufs"; 125 *devname = default_devname; 126 *unit = default_unit; 127 *partition = default_partition; 128 *file = default_filename; 129 130 if (fname == NULL) 131 return 0; 132 133 if ((col = strchr(fname, ':')) != NULL) { /* device given */ 134 static char savedevname[MAXDEVNAME+1]; 135 int devlen; 136 int u = 0, p = 0; 137 int i = 0; 138 139 devlen = col - fname; 140 if (devlen > MAXDEVNAME) 141 return EINVAL; 142 143#define isvalidname(c) ((c) >= 'a' && (c) <= 'z') 144 if (!isvalidname(fname[i])) 145 return EINVAL; 146 do { 147 savedevname[i] = fname[i]; 148 i++; 149 } while (isvalidname(fname[i])); 150 savedevname[i] = '\0'; 151 152#define isnum(c) ((c) >= '0' && (c) <= '9') 153 if (i < devlen) { 154 if (!isnum(fname[i])) 155 return EUNIT; 156 do { 157 u *= 10; 158 u += fname[i++] - '0'; 159 } while (isnum(fname[i])); 160 } 161 162#define isvalidpart(c) ((c) >= 'a' && (c) <= 'z') 163 if (i < devlen) { 164 if (!isvalidpart(fname[i])) 165 return EPART; 166 p = fname[i++] - 'a'; 167 } 168 169 if (i != devlen) 170 return ENXIO; 171 172 *devname = savedevname; 173 *unit = u; 174 *partition = p; 175 fname = col + 1; 176 } 177 178 if (*fname) 179 *file = fname; 180 181 return 0; 182} 183 184static char * 185snprint_bootdev(char *buf, size_t bufsize, const char *devname, int unit, 186 int partition) 187{ 188 static const char *no_partition_devs[] = { "esp", "net", "nfs", "tftp" }; 189 int i; 190 191 for (i = 0; i < __arraycount(no_partition_devs); i++) 192 if (strcmp(devname, no_partition_devs[i]) == 0) 193 break; 194 snprintf(buf, bufsize, "%s%d%c", devname, unit, 195 i < __arraycount(no_partition_devs) ? '\0' : 'a' + partition); 196 return buf; 197} 198 199static char * 200sprint_bootsel(const char *filename) 201{ 202 char *fsname, *devname; 203 int unit, partition; 204 const char *file; 205 static char buf[80]; 206 207 if (parsebootfile(filename, &fsname, &devname, &unit, 208 &partition, &file) == 0) { 209 snprintf(buf, sizeof(buf), "%s:%s", snprint_bootdev(buf, 210 sizeof(buf), devname, unit, partition), file); 211 return buf; 212 } 213 return "(invalid)"; 214} 215 216void 217clearit(void) 218{ 219 220 if (bootcfg_info.clear) 221 clear_pc_screen(); 222} 223 224static void 225bootit(const char *filename, int howto) 226{ 227 EFI_STATUS status; 228 EFI_PHYSICAL_ADDRESS bouncebuf; 229 UINTN npages; 230 u_long allocsz; 231 232 if (howto & AB_VERBOSE) 233 printf("booting %s (howto 0x%x)\n", sprint_bootsel(filename), 234 howto); 235 236 if (count_netbsd(filename, &allocsz) < 0) { 237 printf("boot: %s: %s\n", sprint_bootsel(filename), 238 strerror(errno)); 239 return; 240 } 241 242 bouncebuf = EFI_ALLOCATE_MAX_ADDRESS; 243 npages = EFI_SIZE_TO_PAGES(allocsz); 244 status = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, 245 EfiLoaderData, npages, &bouncebuf); 246 if (EFI_ERROR(status)) { 247 printf("boot: %s: %s\n", sprint_bootsel(filename), 248 strerror(ENOMEM)); 249 return; 250 } 251 252 efi_loadaddr = bouncebuf; 253 if (exec_netbsd(filename, bouncebuf, howto, 0, efi_cleanup) < 0) 254 printf("boot: %s: %s\n", sprint_bootsel(filename), 255 strerror(errno)); 256 else 257 printf("boot returned\n"); 258 259 (void) uefi_call_wrapper(BS->FreePages, 2, bouncebuf, npages); 260 efi_loadaddr = 0; 261} 262 263void 264print_banner(void) 265{ 266 int n; 267 268 clearit(); 269 if (bootcfg_info.banner[0]) { 270 for (n = 0; n < BOOTCFG_MAXBANNER && bootcfg_info.banner[n]; 271 n++) 272 printf("%s\n", bootcfg_info.banner[n]); 273 } else 274 command_version("short"); 275} 276 277void 278boot(void) 279{ 280 int currname; 281 int c; 282 283 boot_modules_enabled = !(boot_params.bp_flags & X86_BP_FLAGS_NOMODULES); 284 285 /* try to set default device to what BIOS tells us */ 286 bios2dev(boot_biosdev, boot_biossector, &default_devname, &default_unit, 287 &default_partition); 288 289 /* if the user types "boot" without filename */ 290 default_filename = DEFFILENAME; 291 292 if (!(boot_params.bp_flags & X86_BP_FLAGS_NOBOOTCONF)) { 293#ifdef EFIBOOTCFG_FILENAME 294 int rv = EINVAL; 295 if (efi_bootdp_type != BOOT_DEVICE_TYPE_NET) 296 rv = parsebootconf(EFIBOOTCFG_FILENAME); 297 if (rv) 298#endif 299 parsebootconf(BOOTCFG_FILENAME); 300 } else { 301 bootcfg_info.timeout = boot_params.bp_timeout; 302 } 303 304 /* 305 * If console set in boot.cfg, switch to it. 306 * This will print the banner, so we don't need to explicitly do it 307 */ 308 if (bootcfg_info.consdev) 309 command_consdev(bootcfg_info.consdev); 310 else 311 print_banner(); 312 313 /* Display the menu, if applicable */ 314 twiddle_toggle = 0; 315 if (bootcfg_info.nummenu > 0) { 316 /* Does not return */ 317 doboottypemenu(); 318 } 319 320 printf("Press return to boot now, any other key for boot menu\n"); 321 for (currname = 0; currname < NUMNAMES; currname++) { 322 printf("booting %s - starting in ", 323 sprint_bootsel(names[currname][0])); 324 325 c = awaitkey((bootcfg_info.timeout < 0) ? 0 326 : bootcfg_info.timeout, 1); 327 if ((c != '\r') && (c != '\n') && (c != '\0')) { 328 if ((boot_params.bp_flags & X86_BP_FLAGS_PASSWORD) == 0) { 329 /* do NOT ask for password */ 330 bootmenu(); /* does not return */ 331 } else { 332 /* DO ask for password */ 333 if (check_password((char *)boot_params.bp_password)) { 334 /* password ok */ 335 printf("type \"?\" or \"help\" for help.\n"); 336 bootmenu(); /* does not return */ 337 } else { 338 /* bad password */ 339 printf("Wrong password.\n"); 340 currname = 0; 341 continue; 342 } 343 } 344 } 345 346 /* 347 * try pairs of names[] entries, foo and foo.gz 348 */ 349 /* don't print "booting..." again */ 350 bootit(names[currname][0], 0); 351 /* since it failed, try compressed bootfile. */ 352 bootit(names[currname][1], AB_VERBOSE); 353 } 354 355 bootmenu(); /* does not return */ 356} 357 358/* ARGSUSED */ 359void 360command_help(char *arg) 361{ 362 363 printf("commands are:\n" 364 "boot [xdNx:][filename] [-12acdqsvxz]\n" 365 " (ex. \"hd0a:netbsd.old -s\"\n" 366 "dev [xd[N[x]]:]\n" 367 "consdev {pc|com[0123][,{speed}]|com,{ioport}[,{speed}]}\n" 368 "devpath\n" 369 "efivar\n" 370 "gop [{modenum|list}]\n" 371 "load {path_to_module}\n" 372#if LIBSA_ENABLE_LS_OP 373 "ls [path]\n" 374#endif 375 "memmap [{sorted|unsorted}]\n" 376#ifndef SMALL 377 "menu (reenters boot menu, if defined in boot.cfg)\n" 378#endif 379 "modules {on|off|enabled|disabled}\n" 380 "multiboot [xdNx:][filename] [<args>]\n" 381 "rndseed {path_to_rndseed_file}\n" 382 "splash {path_to_image_file}\n" 383 "text [{modenum|list}]\n" 384 "userconf {command}\n" 385 "version\n" 386 "help|?\n" 387 "quit\n"); 388} 389 390#if LIBSA_ENABLE_LS_OP 391void 392command_ls(char *arg) 393{ 394 const char *save = default_filename; 395 396 default_filename = "/"; 397 ls(arg); 398 default_filename = save; 399} 400#endif 401 402/* ARGSUSED */ 403void 404command_quit(char *arg) 405{ 406 407 printf("Exiting...\n"); 408 delay(1 * 1000 * 1000); 409 reboot(); 410 /* Note: we shouldn't get to this point! */ 411 panic("Could not reboot!"); 412} 413 414void 415command_boot(char *arg) 416{ 417 char *filename; 418 int howto; 419 420 if (!parseboot(arg, &filename, &howto)) 421 return; 422 423 if (filename != NULL) { 424 bootit(filename, howto); 425 } else { 426 int i; 427 428 if (howto == 0) 429 bootdefault(); 430 for (i = 0; i < NUMNAMES; i++) { 431 bootit(names[i][0], howto); 432 bootit(names[i][1], howto); 433 } 434 } 435} 436 437void 438command_dev(char *arg) 439{ 440 static char savedevname[MAXDEVNAME + 1]; 441 char buf[80]; 442 char *fsname, *devname; 443 const char *file; /* dummy */ 444 445 if (*arg == '\0') { 446 efi_disk_show(); 447 efi_net_show(); 448 printf("default %s\n", snprint_bootdev(buf, sizeof(buf), 449 default_devname, default_unit, default_partition)); 450 return; 451 } 452 453 if (strchr(arg, ':') == NULL || 454 parsebootfile(arg, &fsname, &devname, &default_unit, 455 &default_partition, &file)) { 456 command_help(NULL); 457 return; 458 } 459 460 /* put to own static storage */ 461 strncpy(savedevname, devname, MAXDEVNAME + 1); 462 default_devname = savedevname; 463} 464 465static const struct cons_devs { 466 const char *name; 467 u_int tag; 468 int ioport; 469} cons_devs[] = { 470 { "pc", CONSDEV_PC, 0 }, 471 { "com0", CONSDEV_COM0, 0 }, 472 { "com1", CONSDEV_COM1, 0 }, 473 { "com2", CONSDEV_COM2, 0 }, 474 { "com3", CONSDEV_COM3, 0 }, 475 { "com0kbd", CONSDEV_COM0KBD, 0 }, 476 { "com1kbd", CONSDEV_COM1KBD, 0 }, 477 { "com2kbd", CONSDEV_COM2KBD, 0 }, 478 { "com3kbd", CONSDEV_COM3KBD, 0 }, 479 { "com", CONSDEV_COM0, -1 }, 480 { "auto", CONSDEV_AUTO, 0 }, 481 { NULL, 0 } 482}; 483 484void 485command_consdev(char *arg) 486{ 487 const struct cons_devs *cdp; 488 char *sep, *sep2 = NULL; 489 int ioport, speed = 0; 490 491 if (*arg == '\0') { 492 efi_cons_show(); 493 return; 494 } 495 496 sep = strchr(arg, ','); 497 if (sep != NULL) { 498 *sep++ = '\0'; 499 sep2 = strchr(sep, ','); 500 if (sep2 != NULL) 501 *sep2++ = '\0'; 502 } 503 504 for (cdp = cons_devs; cdp->name; cdp++) { 505 if (strcmp(arg, cdp->name) == 0) { 506 ioport = cdp->ioport; 507 if (cdp->tag == CONSDEV_PC || cdp->tag == CONSDEV_AUTO) { 508 if (sep != NULL || sep2 != NULL) 509 goto error; 510 } else { 511 /* com? */ 512 if (ioport == -1) { 513 if (sep != NULL) { 514 u_long t = strtoul(sep, NULL, 0); 515 if (t > INT_MAX) 516 goto error; 517 ioport = (int)t; 518 } 519 if (sep2 != NULL) { 520 speed = atoi(sep2); 521 if (speed < 0) 522 goto error; 523 } 524 } else { 525 if (sep != NULL) { 526 speed = atoi(sep); 527 if (speed < 0) 528 goto error; 529 } 530 if (sep2 != NULL) 531 goto error; 532 } 533 } 534 consinit(cdp->tag, ioport, speed); 535 print_banner(); 536 return; 537 } 538 } 539error: 540 printf("invalid console device.\n"); 541} 542 543#ifndef SMALL 544/* ARGSUSED */ 545void 546command_menu(char *arg) 547{ 548 549 if (bootcfg_info.nummenu > 0) { 550 /* Does not return */ 551 doboottypemenu(); 552 } else 553 printf("No menu defined in boot.cfg\n"); 554} 555#endif /* !SMALL */ 556 557void 558command_modules(char *arg) 559{ 560 561 if (strcmp(arg, "enabled") == 0 || 562 strcmp(arg, "on") == 0) 563 boot_modules_enabled = true; 564 else if (strcmp(arg, "disabled") == 0 || 565 strcmp(arg, "off") == 0) 566 boot_modules_enabled = false; 567 else 568 printf("invalid flag, must be 'enabled' or 'disabled'.\n"); 569} 570 571void 572command_multiboot(char *arg) 573{ 574 char *filename; 575 576 filename = arg; 577 if (exec_multiboot(filename, gettrailer(arg)) < 0) 578 printf("multiboot: %s: %s\n", sprint_bootsel(filename), 579 strerror(errno)); 580 else 581 printf("boot returned\n"); 582} 583 584void 585command_version(char *arg) 586{ 587 CHAR16 *path; 588 char *upath, *ufirmware; 589 int rv; 590 591 if (strcmp(arg, "full") == 0) { 592 printf("ImageBase: 0x%" PRIxPTR "\n", 593 (uintptr_t)efi_li->ImageBase); 594 printf("Stack: 0x%" PRIxPTR "\n", efi_main_sp); 595 printf("EFI version: %d.%02d\n", 596 ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff); 597 ufirmware = NULL; 598 rv = ucs2_to_utf8(ST->FirmwareVendor, &ufirmware); 599 if (rv == 0) { 600 printf("EFI Firmware: %s (rev %d.%02d)\n", ufirmware, 601 ST->FirmwareRevision >> 16, 602 ST->FirmwareRevision & 0xffff); 603 FreePool(ufirmware); 604 } 605 path = DevicePathToStr(efi_bootdp); 606 upath = NULL; 607 rv = ucs2_to_utf8(path, &upath); 608 FreePool(path); 609 if (rv == 0) { 610 printf("Boot DevicePath: %d:%d:%s\n", 611 DevicePathType(efi_bootdp), 612 DevicePathSubType(efi_bootdp), upath); 613 FreePool(upath); 614 } 615 } 616 617 printf("\n" 618 ">> %s, Revision %s (from NetBSD %s)\n" 619 ">> Memory: %d/%d k\n", 620 bootprog_name, bootprog_rev, bootprog_kernrev, 621 getbasemem(), getextmem()); 622} 623 624void 625command_memmap(char *arg) 626{ 627 bool sorted = true; 628 629 if (*arg == '\0' || strcmp(arg, "sorted") == 0) 630 /* Already sorted is true. */; 631 else if (strcmp(arg, "unsorted") == 0) 632 sorted = false; 633 else { 634 printf("invalid flag, " 635 "must be 'sorted' or 'unsorted'.\n"); 636 return; 637 } 638 639 efi_memory_show_map(sorted); 640} 641 642void 643command_devpath(char *arg) 644{ 645 EFI_STATUS status; 646 UINTN i, nhandles; 647 EFI_HANDLE *handles; 648 EFI_DEVICE_PATH *dp0, *dp; 649 CHAR16 *path; 650 char *upath; 651 UINTN cols, rows, row = 0; 652 int rv; 653 654 status = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, 655 ST->ConOut->Mode->Mode, &cols, &rows); 656 if (EFI_ERROR(status) || rows <= 2) 657 rows = 0; 658 else 659 rows -= 2; 660 661 /* 662 * all devices. 663 */ 664 status = LibLocateHandle(ByProtocol, &DevicePathProtocol, NULL, 665 &nhandles, &handles); 666 if (EFI_ERROR(status)) 667 return; 668 669 for (i = 0; i < nhandles; i++) { 670 status = uefi_call_wrapper(BS->HandleProtocol, 3, handles[i], 671 &DevicePathProtocol, (void **)&dp0); 672 if (EFI_ERROR(status)) 673 break; 674 675 printf("DevicePathType %d\n", DevicePathType(dp0)); 676 if (++row >= rows) { 677 row = 0; 678 printf("Press Any Key to continue :"); 679 (void) awaitkey(-1, 0); 680 printf("\n"); 681 } 682 for (dp = dp0; 683 !IsDevicePathEnd(dp); 684 dp = NextDevicePathNode(dp)) { 685 686 path = DevicePathToStr(dp); 687 upath = NULL; 688 rv = ucs2_to_utf8(path, &upath); 689 FreePool(path); 690 if (rv) { 691 printf("convert failed\n"); 692 break; 693 } 694 695 printf("%d:%d:%s\n", DevicePathType(dp), 696 DevicePathSubType(dp), upath); 697 FreePool(upath); 698 699 if (++row >= rows) { 700 row = 0; 701 printf("Press Any Key to continue :"); 702 (void) awaitkey(-1, 0); 703 printf("\n"); 704 } 705 } 706 } 707} 708 709 710void 711command_efivar(char *arg) 712{ 713 static const char header[] = 714 "GUID Variable Name Value\n" 715 "==================================== ==================== ========\n"; 716 EFI_STATUS status; 717 UINTN sz = 64, osz; 718 CHAR16 *name = NULL, *tmp, *val, guid[128]; 719 char *uname, *uval, *uguid; 720 EFI_GUID vendor; 721 UINTN cols, rows, row = 0; 722 int rv; 723 724 status = uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, 725 ST->ConOut->Mode->Mode, &cols, &rows); 726 if (EFI_ERROR(status) || rows <= 2) 727 rows = 0; 728 else 729 rows -= 2; 730 731 name = AllocatePool(sz); 732 if (name == NULL) { 733 printf("memory allocation failed: %" PRIuMAX" bytes\n", 734 (uintmax_t)sz); 735 return; 736 } 737 738 SetMem(name, sz, 0); 739 vendor = NullGuid; 740 741 printf("%s", header); 742 for (;;) { 743 osz = sz; 744 status = uefi_call_wrapper(RT->GetNextVariableName, 3, 745 &sz, name, &vendor); 746 if (EFI_ERROR(status)) { 747 if (status == EFI_NOT_FOUND) 748 break; 749 if (status != EFI_BUFFER_TOO_SMALL) { 750 printf("GetNextVariableName failed: %" PRIxMAX "\n", 751 (uintmax_t)status); 752 break; 753 } 754 755 tmp = AllocatePool(sz); 756 if (tmp == NULL) { 757 printf("memory allocation failed: %" PRIuMAX 758 "bytes\n", (uintmax_t)sz); 759 break; 760 } 761 SetMem(tmp, sz, 0); 762 CopyMem(tmp, name, osz); 763 FreePool(name); 764 name = tmp; 765 continue; 766 } 767 768 val = LibGetVariable(name, &vendor); 769 if (val != NULL) { 770 uval = NULL; 771 rv = ucs2_to_utf8(val, &uval); 772 FreePool(val); 773 if (rv) { 774 printf("value convert failed\n"); 775 break; 776 } 777 } else 778 uval = NULL; 779 uname = NULL; 780 rv = ucs2_to_utf8(name, &uname); 781 if (rv) { 782 printf("name convert failed\n"); 783 FreePool(uval); 784 break; 785 } 786 GuidToString(guid, &vendor); 787 uguid = NULL; 788 rv = ucs2_to_utf8(guid, &uguid); 789 if (rv) { 790 printf("GUID convert failed\n"); 791 FreePool(uval); 792 FreePool(uname); 793 break; 794 } 795 printf("%-35s %-20s %s\n", uguid, uname, uval ? uval : "(null)"); 796 FreePool(uguid); 797 FreePool(uname); 798 if (uval != NULL) 799 FreePool(uval); 800 801 if (++row >= rows) { 802 row = 0; 803 printf("Press Any Key to continue :"); 804 (void) awaitkey(-1, 0); 805 printf("\n"); 806 } 807 } 808 809 FreePool(name); 810} 811