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