Home | History | Annotate | Line # | Download | only in boot
      1 /*	$NetBSD: boot2.c,v 1.90 2025/07/31 02:59:13 pgoyette Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
      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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26  * POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 /*
     30  * Copyright (c) 2003
     31  *	David Laight.  All rights reserved
     32  * Copyright (c) 1996, 1997, 1999
     33  * 	Matthias Drochner.  All rights reserved.
     34  * Copyright (c) 1996, 1997
     35  * 	Perry E. Metzger.  All rights reserved.
     36  * Copyright (c) 1997
     37  *	Jason R. Thorpe.  All rights reserved
     38  *
     39  * Redistribution and use in source and binary forms, with or without
     40  * modification, are permitted provided that the following conditions
     41  * are met:
     42  * 1. Redistributions of source code must retain the above copyright
     43  *    notice, this list of conditions and the following disclaimer.
     44  * 2. Redistributions in binary form must reproduce the above copyright
     45  *    notice, this list of conditions and the following disclaimer in the
     46  *    documentation and/or other materials provided with the distribution.
     47  * 3. All advertising materials mentioning features or use of this software
     48  *    must display the following acknowledgements:
     49  *	This product includes software developed for the NetBSD Project
     50  *	by Matthias Drochner.
     51  *	This product includes software developed for the NetBSD Project
     52  *	by Perry E. Metzger.
     53  * 4. The names of the authors may not be used to endorse or promote products
     54  *    derived from this software without specific prior written permission.
     55  *
     56  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     57  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     58  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     59  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     60  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     61  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     62  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     63  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     64  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     65  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     66  */
     67 
     68 /* Based on stand/biosboot/main.c */
     69 
     70 #include <sys/types.h>
     71 #include <sys/reboot.h>
     72 #include <sys/bootblock.h>
     73 
     74 #include <lib/libsa/stand.h>
     75 #include <lib/libsa/bootcfg.h>
     76 #include <lib/libsa/ufs.h>
     77 #include <lib/libkern/libkern.h>
     78 
     79 #include <libi386.h>
     80 #include <bootmod.h>
     81 #include <bootmenu.h>
     82 #include <biosdisk.h>
     83 #include <vbe.h>
     84 #include "devopen.h"
     85 
     86 #ifdef _STANDALONE
     87 #include <bootinfo.h>
     88 #endif
     89 #ifdef SUPPORT_PS2
     90 #include <biosmca.h>
     91 #endif
     92 
     93 extern struct x86_boot_params boot_params;
     94 
     95 extern	const char bootprog_name[], bootprog_rev[], bootprog_kernrev[];
     96 
     97 int errno;
     98 
     99 int boot_biosdev;
    100 daddr_t boot_biossector;
    101 
    102 static const char * const names[][2] = {
    103 	{ "netbsd", "netbsd.gz" },
    104 	{ "onetbsd", "onetbsd.gz" },
    105 	{ "netbsd.old", "netbsd.old.gz" },
    106 	{ "netbsd/kernel", "netbsd/kernel.gz" },
    107 	{ "onetbsd/kernel", "onetbsd/kernel.gz" },
    108 	{ "netbsd.old/kernel", "netbsd.old/kernel.gz" },
    109 };
    110 
    111 #define NUMNAMES (sizeof(names)/sizeof(names[0]))
    112 #define DEFFILENAME names[0][0]
    113 
    114 #ifndef NO_GPT
    115 #define MAXDEVNAME 39 /* "NAME=" + 34 char part_name */
    116 #else
    117 #define MAXDEVNAME 16
    118 #endif
    119 
    120 static char *default_devname;
    121 static int default_unit, default_partition;
    122 static const char *default_filename;
    123 static const char *default_part_name;
    124 
    125 char *sprint_bootsel(const char *);
    126 static void bootit(const char *, int);
    127 static void bootit2(char *, size_t, int);
    128 void boot2(int, uint64_t);
    129 
    130 void	command_help(char *);
    131 #if LIBSA_ENABLE_LS_OP
    132 void	command_ls(char *);
    133 #endif
    134 void	command_quit(char *);
    135 void	command_boot(char *);
    136 void	command_pkboot(char *);
    137 void	command_consdev(char *);
    138 void	command_root(char *);
    139 #ifndef SMALL
    140 void	command_menu(char *);
    141 #endif
    142 void	command_modules(char *);
    143 void	command_multiboot(char *);
    144 
    145 const struct bootblk_command commands[] = {
    146 	{ "help",	command_help },
    147 	{ "?",		command_help },
    148 #if LIBSA_ENABLE_LS_OP
    149 	{ "ls",		command_ls },
    150 #endif
    151 	{ "quit",	command_quit },
    152 	{ "boot",	command_boot },
    153 	{ "pkboot",	command_pkboot },
    154 	{ "dev",	command_dev },
    155 	{ "consdev",	command_consdev },
    156 	{ "root",	command_root },
    157 #ifndef SMALL
    158 	{ "menu",	command_menu },
    159 #endif
    160 	{ "modules",	command_modules },
    161 	{ "load",	module_add },
    162 	{ "multiboot",	command_multiboot },
    163 	{ "vesa",	command_vesa },
    164 	{ "splash",	splash_add },
    165 	{ "rndseed",	rnd_add },
    166 	{ "fs",		fs_add },
    167 	{ "userconf",	userconf_add },
    168 	{ NULL,		NULL },
    169 };
    170 
    171 int
    172 parsebootfile(const char *fname, char **fsname, char **devname,
    173 	      int *unit, int *partition, const char **file)
    174 {
    175 	const char *col;
    176 	static char savedevname[MAXDEVNAME+1];
    177 
    178 	*fsname = "ufs";
    179 	if (default_part_name == NULL) {
    180 		*devname = default_devname;
    181 	} else {
    182 		snprintf(savedevname, sizeof(savedevname),
    183 		    "NAME=%s", default_part_name);
    184 		*devname = savedevname;
    185 	}
    186 	*unit = default_unit;
    187 	*partition = default_partition;
    188 	*file = default_filename;
    189 
    190 	if (fname == NULL)
    191 		return 0;
    192 
    193 	if ((col = strchr(fname, ':')) != NULL) {	/* device given */
    194 		int devlen;
    195 		int u = 0, p = 0;
    196 		int i = 0;
    197 
    198 		devlen = col - fname;
    199 		if (devlen > MAXDEVNAME)
    200 			return EINVAL;
    201 
    202 #ifndef NO_GPT
    203 		if (strstr(fname, "NAME=") == fname) {
    204 			strlcpy(savedevname, fname, devlen + 1);
    205 			*devname = savedevname;
    206 			*unit = -1;
    207 			*partition = -1;
    208 			fname = col + 1;
    209 			goto out;
    210 		}
    211 #endif
    212 
    213 #define isvalidname(c) ((c) >= 'a' && (c) <= 'z')
    214 		if (!isvalidname(fname[i]))
    215 			return EINVAL;
    216 		do {
    217 			savedevname[i] = fname[i];
    218 			i++;
    219 		} while (isvalidname(fname[i]));
    220 		savedevname[i] = '\0';
    221 
    222 #define isnum(c) ((c) >= '0' && (c) <= '9')
    223 		if (i < devlen) {
    224 			if (!isnum(fname[i]))
    225 				return EUNIT;
    226 			do {
    227 				u *= 10;
    228 				u += fname[i++] - '0';
    229 			} while (isnum(fname[i]));
    230 		}
    231 
    232 #define isvalidpart(c) ((c) >= 'a' && (c) <= 'z')
    233 		if (i < devlen) {
    234 			if (!isvalidpart(fname[i]))
    235 				return EPART;
    236 			p = fname[i++] - 'a';
    237 		}
    238 
    239 		if (i != devlen)
    240 			return ENXIO;
    241 
    242 		*devname = savedevname;
    243 		*unit = u;
    244 		*partition = p;
    245 		fname = col + 1;
    246 	}
    247 
    248 out:
    249 	if (*fname)
    250 		*file = fname;
    251 
    252 	return 0;
    253 }
    254 
    255 char *
    256 sprint_bootsel(const char *filename)
    257 {
    258 	char *fsname, *devname;
    259 	int unit, partition;
    260 	const char *file;
    261 	static char buf[80];
    262 
    263 	if (parsebootfile(filename, &fsname, &devname, &unit,
    264 			  &partition, &file) == 0) {
    265 		if (strstr(devname, "NAME=") == devname)
    266 			snprintf(buf, sizeof(buf), "%s:%s", devname, file);
    267 		else
    268 			snprintf(buf, sizeof(buf), "%s%d%c:%s", devname, unit,
    269 			    'a' + partition, file);
    270 		return buf;
    271 	}
    272 	return "(invalid)";
    273 }
    274 
    275 static void
    276 clearit(void)
    277 {
    278 
    279 	if (bootcfg_info.clear)
    280 		clear_pc_screen();
    281 }
    282 
    283 static void
    284 bootit(const char *filename, int howto)
    285 {
    286 	if (howto & AB_VERBOSE)
    287 		printf("booting %s (howto 0x%x)\n", sprint_bootsel(filename),
    288 		    howto);
    289 
    290 	if (exec_netbsd(filename, 0, howto, boot_biosdev < 0x80, clearit) < 0)
    291 		printf("boot: %s: %s\n", sprint_bootsel(filename),
    292 		       strerror(errno));
    293 	else
    294 		printf("boot returned\n");
    295 }
    296 
    297 /*
    298  * Called from the initial entry point boot_start in biosboot.S
    299  *
    300  * biosdev: BIOS drive number the system booted from
    301  * biossector: Sector number of the NetBSD partition
    302  */
    303 void
    304 boot2(int biosdev, uint64_t biossector)
    305 {
    306 	extern char twiddle_toggle;
    307 	int currname;
    308 	char c;
    309 
    310 	twiddle_toggle = 1;	/* no twiddling until we're ready */
    311 
    312 	initio(boot_params.bp_consdev);
    313 
    314 #ifdef SUPPORT_PS2
    315 	biosmca();
    316 #endif
    317 	gateA20();
    318 
    319 	boot_modules_enabled = !(boot_params.bp_flags
    320 				 & X86_BP_FLAGS_NOMODULES);
    321 	if (boot_params.bp_flags & X86_BP_FLAGS_RESET_VIDEO)
    322 		biosvideomode();
    323 
    324 	vbe_init();
    325 
    326 	/* need to remember these */
    327 	boot_biosdev = biosdev;
    328 	boot_biossector = biossector;
    329 
    330 	/* try to set default device to what BIOS tells us */
    331 	bios2dev(biosdev, biossector, &default_devname, &default_unit,
    332 		 &default_partition, &default_part_name);
    333 
    334 	/* if the user types "boot" without filename */
    335 	default_filename = DEFFILENAME;
    336 
    337 #ifndef SMALL
    338 	if (!(boot_params.bp_flags & X86_BP_FLAGS_NOBOOTCONF)) {
    339 		parsebootconf(BOOTCFG_FILENAME);
    340 	} else {
    341 		bootcfg_info.timeout = boot_params.bp_timeout;
    342 	}
    343 
    344 
    345 	/*
    346 	 * If console set in boot.cfg, switch to it.
    347 	 * This will print the banner, so we don't need to explicitly do it
    348 	 */
    349 	if (bootcfg_info.consdev) {
    350 		command_consdev(bootcfg_info.consdev);
    351 	} else {
    352 		clearit();
    353 		print_bootcfg_banner(bootprog_name, bootprog_rev);
    354 	}
    355 
    356 	/* Display the menu, if applicable */
    357 	twiddle_toggle = 0;
    358 	if (bootcfg_info.nummenu > 0) {
    359 		/* Does not return */
    360 		doboottypemenu();
    361 	}
    362 
    363 #else
    364 	twiddle_toggle = 0;
    365 	clearit();
    366 	print_bootcfg_banner(bootprog_name, bootprog_rev);
    367 #endif
    368 
    369 	printf("Press return to boot now, any other key for boot menu\n");
    370 	for (currname = 0; currname < NUMNAMES; currname++) {
    371 		printf("booting %s - starting in ",
    372 		       sprint_bootsel(names[currname][0]));
    373 
    374 #ifdef SMALL
    375 		c = awaitkey(boot_params.bp_timeout, 1);
    376 #else
    377 		c = awaitkey((bootcfg_info.timeout < 0) ? 0
    378 		    : bootcfg_info.timeout, 1);
    379 #endif
    380 		if ((c != '\r') && (c != '\n') && (c != '\0')) {
    381 		    if ((boot_params.bp_flags & X86_BP_FLAGS_PASSWORD) == 0) {
    382 			/* do NOT ask for password */
    383 			bootmenu(); /* does not return */
    384 		    } else {
    385 			/* DO ask for password */
    386 			if (check_password((char *)boot_params.bp_password)) {
    387 			    /* password ok */
    388 			    printf("type \"?\" or \"help\" for help.\n");
    389 			    bootmenu(); /* does not return */
    390 			} else {
    391 			    /* bad password */
    392 			    printf("Wrong password.\n");
    393 			    currname = 0;
    394 			    continue;
    395 			}
    396 		    }
    397 		}
    398 
    399 		/*
    400 		 * try pairs of names[] entries, foo and foo.gz
    401 		 */
    402 		/* don't print "booting..." again */
    403 		bootit(names[currname][0], 0);
    404 		/* since it failed, try compressed bootfile. */
    405 		bootit(names[currname][1], AB_VERBOSE);
    406 	}
    407 
    408 	bootmenu();	/* does not return */
    409 }
    410 
    411 /* ARGSUSED */
    412 void
    413 command_help(char *arg)
    414 {
    415 
    416 	printf("commands are:\n"
    417 	       "boot [dev:][filename] [-12acdqsvxz]\n"
    418 #ifndef NO_RAIDFRAME
    419 	       "     dev syntax is (hd|fd|cd|raid)[N[x]]\n"
    420 #else
    421 	       "     dev syntax is (hd|fd|cd)[N[x]]n"
    422 #endif
    423 #ifndef NO_GPT
    424 	       "                or NAME=gpt_label\n"
    425 #endif
    426 	       "     (ex. \"hd0a:netbsd.old -s\")\n"
    427 	       "pkboot [dev:][filename] [-12acdqsvxz]\n"
    428 #if LIBSA_ENABLE_LS_OP
    429 	       "ls [dev:][path]\n"
    430 #endif
    431 	       "dev [dev:]\n"
    432 	       "consdev {pc|{com[0123]|com[0123]kbd|auto}[,{speed}]}\n"
    433 	       "root    {spec}\n"
    434 	       "     spec can be disk, e.g. wd0, sd0\n"
    435 	       "     or string like wedge:name\n"
    436 	       "vesa {modenum|on|off|enabled|disabled|list}\n"
    437 #ifndef SMALL
    438 	       "menu (reenters boot menu, if defined in boot.cfg)\n"
    439 #endif
    440 	       "modules {on|off|enabled|disabled}\n"
    441 	       "load {path_to_module}\n"
    442 	       "multiboot [dev:][filename] [<args>]\n"
    443 	       "splash {path_to_image_file}\n"
    444 	       "userconf {command}\n"
    445 	       "rndseed {path_to_rndseed_file}\n"
    446 	       "help|?\n"
    447 	       "quit\n");
    448 }
    449 
    450 #if LIBSA_ENABLE_LS_OP
    451 void
    452 command_ls(char *arg)
    453 {
    454 	const char *save = default_filename;
    455 
    456 	default_filename = "/";
    457 	ls(arg);
    458 	default_filename = save;
    459 }
    460 #endif
    461 
    462 /* ARGSUSED */
    463 void
    464 command_quit(char *arg)
    465 {
    466 
    467 	printf("Exiting...\n");
    468 	delay(1000000);
    469 	reboot();
    470 	/* Note: we shouldn't get to this point! */
    471 	panic("Could not reboot!");
    472 }
    473 
    474 static void
    475 bootit2(char *path, size_t plen, int howto)
    476 {
    477 	bootit(path, howto);
    478 	snprintf(path, plen, "%s.gz", path);
    479 	bootit(path, howto | AB_VERBOSE);
    480 }
    481 
    482 void
    483 command_boot(char *arg)
    484 {
    485 	char *filename;
    486 	char path[512];
    487 	int howto;
    488 
    489 	if (!parseboot(arg, &filename, &howto))
    490 		return;
    491 
    492 	if (filename != NULL && filename[0] != '\0') {
    493 		/* try old locations first to assist atf test beds */
    494 		snprintf(path, sizeof(path) - 4, "%s", filename);
    495 		bootit2(path, sizeof(path), howto);
    496 
    497 		/*
    498 		 * now treat given filename as a directory unless there
    499 		 * is already an embedded path-name separator '/' present
    500 		 */
    501 		if (strchr(filename + 1, '/') == NULL) {
    502 			snprintf(path, sizeof(path) - 4, "%s/kernel",
    503 			    filename);
    504 			bootit2(path, sizeof(path), howto);
    505 		}
    506 	} else {
    507 		int i;
    508 
    509 		for (i = 0; i < NUMNAMES; i++) {
    510 			bootit(names[i][0], howto);
    511 			bootit(names[i][1], howto);
    512 		}
    513 	}
    514 }
    515 
    516 void
    517 command_pkboot(char *arg)
    518 {
    519 	extern int has_prekern;
    520 	has_prekern = 1;
    521 	command_boot(arg);
    522 	has_prekern = 0;
    523 }
    524 
    525 void
    526 command_dev(char *arg)
    527 {
    528 	static char savedevname[MAXDEVNAME + 1];
    529 	char *fsname, *devname;
    530 	const char *file; /* dummy */
    531 
    532 	if (*arg == '\0') {
    533 		biosdisk_probe();
    534 
    535 #ifndef NO_GPT
    536 		if (default_part_name)
    537 			printf("default NAME=%s on %s%d\n", default_part_name,
    538 			       default_devname, default_unit);
    539 		else
    540 #endif
    541 			printf("default %s%d%c\n",
    542 			       default_devname, default_unit,
    543 			       'a' + default_partition);
    544 		return;
    545 	}
    546 
    547 	if (strchr(arg, ':') == NULL ||
    548 	    parsebootfile(arg, &fsname, &devname, &default_unit,
    549 			  &default_partition, &file)) {
    550 		command_help(NULL);
    551 		return;
    552 	}
    553 
    554 	/* put to own static storage */
    555 	strncpy(savedevname, devname, MAXDEVNAME + 1);
    556 	default_devname = savedevname;
    557 
    558 	/* +5 to skip leading NAME= */
    559 	if (strstr(devname, "NAME=") == devname)
    560 		default_part_name = default_devname + 5;
    561 }
    562 
    563 static const struct cons_devs {
    564 	const char	*name;
    565 	u_int		tag;
    566 } cons_devs[] = {
    567 	{ "pc",		CONSDEV_PC },
    568 	{ "com0",	CONSDEV_COM0 },
    569 	{ "com1",	CONSDEV_COM1 },
    570 	{ "com2",	CONSDEV_COM2 },
    571 	{ "com3",	CONSDEV_COM3 },
    572 	{ "com0kbd",	CONSDEV_COM0KBD },
    573 	{ "com1kbd",	CONSDEV_COM1KBD },
    574 	{ "com2kbd",	CONSDEV_COM2KBD },
    575 	{ "com3kbd",	CONSDEV_COM3KBD },
    576 	{ "auto",	CONSDEV_AUTO },
    577 	{ NULL,		0 }
    578 };
    579 
    580 void
    581 command_consdev(char *arg)
    582 {
    583 	const struct cons_devs *cdp;
    584 	char *sep;
    585 	int speed;
    586 
    587 	sep = strchr(arg, ',');
    588 	if (sep != NULL)
    589 		*sep++ = '\0';
    590 
    591 	for (cdp = cons_devs; cdp->name; cdp++) {
    592 		if (strcmp(arg, cdp->name) != 0)
    593 			continue;
    594 
    595 		if (sep != NULL) {
    596 			if (cdp->tag == CONSDEV_PC)
    597 				goto error;
    598 
    599 			speed = atoi(sep);
    600 			if (speed < 0)
    601 				goto error;
    602 			boot_params.bp_conspeed = speed;
    603 		}
    604 
    605 		initio(cdp->tag);
    606 		clearit();
    607 		print_bootcfg_banner(bootprog_name, bootprog_rev);
    608 		return;
    609 	}
    610 error:
    611 	printf("invalid console device.\n");
    612 }
    613 
    614 void
    615 command_root(char *arg)
    616 {
    617 	struct btinfo_rootdevice *biv = &bi_root;
    618 
    619 	strncpy(biv->devname, arg, sizeof(biv->devname));
    620 	if (biv->devname[sizeof(biv->devname)-1] != '\0') {
    621 		biv->devname[sizeof(biv->devname)-1] = '\0';
    622 		printf("truncated to %s\n",biv->devname);
    623 	}
    624 }
    625 
    626 #ifndef SMALL
    627 /* ARGSUSED */
    628 void
    629 command_menu(char *arg)
    630 {
    631 
    632 	if (bootcfg_info.nummenu > 0) {
    633 		/* Does not return */
    634 		doboottypemenu();
    635 	} else {
    636 		printf("No menu defined in boot.cfg\n");
    637 	}
    638 }
    639 #endif /* !SMALL */
    640 
    641 void
    642 command_modules(char *arg)
    643 {
    644 
    645 	if (strcmp(arg, "enabled") == 0 ||
    646 	    strcmp(arg, "on") == 0)
    647 		boot_modules_enabled = true;
    648 	else if (strcmp(arg, "disabled") == 0 ||
    649 	    strcmp(arg, "off") == 0)
    650 		boot_modules_enabled = false;
    651 	else
    652 		printf("invalid flag, must be 'enabled' or 'disabled'.\n");
    653 }
    654 
    655 void
    656 command_multiboot(char *arg)
    657 {
    658 	char *filename;
    659 
    660 	filename = arg;
    661 	if (exec_multiboot(filename, gettrailer(arg)) < 0)
    662 		printf("multiboot: %s: %s\n", sprint_bootsel(filename),
    663 		       strerror(errno));
    664 	else
    665 		printf("boot returned\n");
    666 }
    667 
    668