Home | History | Annotate | Line # | Download | only in inst
inst.c revision 1.3
      1 /*	$NetBSD: inst.c,v 1.3 1997/04/27 21:17:14 thorpej Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1995, 1996, 1997 Jason R. Thorpe.
      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  * 3. All advertising materials mentioning features or use of this software
     16  *    must display the following acknowledgement:
     17  *	This product includes software developed for the NetBSD Project
     18  *	by Jason R. Thorpe.
     19  * 4. The name of the author may not be used to endorse or promote products
     20  *    derived from this software without specific prior written permission.
     21  *
     22  * Portions of this program are inspired by (and have borrowed code from)
     23  * the `editlabel' program that accompanies NetBSD/vax, which carries
     24  * the following notice:
     25  *
     26  * Copyright (c) 1995 Ludd, University of Lule}, Sweden.
     27  * All rights reserved.
     28  *
     29  * Redistribution and use in source and binary forms, with or without
     30  * modification, are permitted provided that the following conditions
     31  * are met:
     32  * 1. Redistributions of source code must retain the above copyright
     33  *    notice, this list of conditions and the following disclaimer.
     34  * 2. Redistributions in binary form must reproduce the above copyright
     35  *    notice, this list of conditions and the following disclaimer in the
     36  *    documentation and/or other materials provided with the distribution.
     37  * 3. All advertising materials mentioning features or use of this software
     38  *    must display the following acknowledgement:
     39  *	This product includes software developed at Ludd, University of
     40  *	Lule}, Sweden and its contributors.
     41  * 4. The name of the author may not be used to endorse or promote products
     42  *    derived from this software without specific prior written permission
     43  *
     44  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     45  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     46  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     47  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     48  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     49  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     50  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     51  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     52  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     53  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     54  * SUCH DAMAGE.
     55  */
     56 
     57 #define DKTYPENAMES
     58 
     59 #include <sys/param.h>
     60 #include <sys/reboot.h>
     61 #include <sys/disklabel.h>
     62 #include <a.out.h>
     63 
     64 #include <lib/libsa/stand.h>
     65 
     66 #include <hp300/stand/common/samachdep.h>
     67 
     68 char line[100];
     69 
     70 extern	u_int opendev;
     71 extern	char *lowram;
     72 extern	int noconsole;
     73 extern	int netio_ask;
     74 
     75 char	*kernel_name = "/netbsd";
     76 
     77 void	dsklabel __P((void));
     78 void	miniroot __P((void));
     79 void	bootmini __P((void));
     80 void	resetsys __P((void));
     81 void	gethelp __P((void));
     82 int	opendisk __P((char *, char *, int, char, int *));
     83 void	disklabel_edit __P((struct disklabel *));
     84 void	disklabel_show __P((struct disklabel *));
     85 int	disklabel_write __P((char *, int, struct open_file *));
     86 int	a2int __P((char *));
     87 
     88 struct	inst_command {
     89 	char	*ic_cmd;		/* command name */
     90 	char	*ic_desc;		/* command description */
     91 	void	(*ic_func) __P((void));	/* handling function */
     92 } inst_commands[] = {
     93 	{ "disklabel",	"place partition map on disk",	dsklabel },
     94 	{ "miniroot",	"place miniroot on disk",	miniroot },
     95 	{ "boot",	"boot from miniroot",		bootmini },
     96 	{ "reset",	"reset the system",		resetsys },
     97 	{ "help",	"display command list",		gethelp },
     98 };
     99 #define NCMDS	(sizeof(inst_commands) / sizeof(inst_commands[0]))
    100 
    101 main()
    102 {
    103 	int i, currname = 0;
    104 
    105 	/*
    106 	 * We want netopen() to ask for IP address, etc, rather
    107 	 * that using bootparams.
    108 	 */
    109 	netio_ask = 1;
    110 
    111 	printf("\n");
    112 	printf(">> %s, Revision %s\n", bootprog_name, bootprog_rev);
    113 	printf(">> (%s, %s)\n", bootprog_maker, bootprog_date);
    114 	printf(">> HP 9000/%s SPU\n", getmachineid());
    115 	gethelp();
    116 
    117 	for (;;) {
    118 		printf("sys_inst> ");
    119 		bzero(line, sizeof(line));
    120 		gets(line);
    121 		if (line[0] == '\n' || line[0] == '\0')
    122 			continue;
    123 
    124 		for (i = 0; i < NCMDS; ++i)
    125 			if (strcmp(line, inst_commands[i].ic_cmd) == 0) {
    126 				(*inst_commands[i].ic_func)();
    127 				break;
    128 			}
    129 
    130 
    131 		if (i == NCMDS)
    132 			printf("unknown command: %s\n", line);
    133 	}
    134 }
    135 
    136 void
    137 gethelp()
    138 {
    139 	int i;
    140 
    141 	printf(">> Available commands:\n");
    142 	for (i = 0; i < NCMDS; ++i)
    143 		printf(">>     %s - %s\n", inst_commands[i].ic_cmd,
    144 		    inst_commands[i].ic_desc);
    145 }
    146 
    147 /*
    148  * Do all the steps necessary to place a disklabel on a disk.
    149  * Note, this assumes 512 byte sectors.
    150  */
    151 void
    152 dsklabel()
    153 {
    154 	struct disklabel *lp;
    155 	struct open_file *disk_ofp;
    156 	int dfd, error;
    157 	size_t xfersize;
    158 	char block[DEV_BSIZE], diskname[64];
    159 	extern struct open_file files[];
    160 
    161 	printf("
    162 You will be asked several questions about your disk, most of which
    163 require prior knowledge of the disk's geometry.  There is no easy way
    164 for the system to provide this information for you.  If you do not have
    165 this information, please consult your disk's manual or another
    166 informative source.\n\n");
    167 
    168 	/* Error message printed by opendisk() */
    169 	if (opendisk("Disk to label?", diskname, sizeof(diskname),
    170 	    ('a' + RAW_PART), &dfd))
    171 		return;
    172 
    173 	disk_ofp = &files[dfd];
    174 
    175 	bzero(block, sizeof(block));
    176 	if (error = (*disk_ofp->f_dev->dv_strategy)(disk_ofp->f_devdata,
    177 	    F_READ, LABELSECTOR, sizeof(block), block, &xfersize)) {
    178 		printf("cannot read disk %s, errno = %d\n", diskname, error);
    179 		return;
    180 	}
    181 
    182 	printf("Sucessfully read %d bytes from %s\n", xfersize, diskname);
    183 
    184 	lp = (struct disklabel *)((void *)(&block[LABELOFFSET]));
    185 
    186  disklabel_loop:
    187 	bzero(line, sizeof(line));
    188 	printf("(z)ap, (e)dit, (s)how, (w)rite, (d)one > ");
    189 	gets(line);
    190 	if (line[0] == '\n' || line[0] == '\0')
    191 		goto disklabel_loop;
    192 
    193 	switch (line[0]) {
    194 	case 'z':
    195 	case 'Z': {
    196 		char zap[DEV_BSIZE];
    197 		bzero(zap, sizeof(zap));
    198 		(void)(*disk_ofp->f_dev->dv_strategy)(disk_ofp->f_devdata,
    199 		    F_WRITE, LABELSECTOR, sizeof(zap), zap, &xfersize);
    200 		}
    201 		goto out;
    202 		/* NOTREACHED */
    203 
    204 	case 'e':
    205 	case 'E':
    206 		disklabel_edit(lp);
    207 		break;
    208 
    209 	case 's':
    210 	case 'S':
    211 		disklabel_show(lp);
    212 		break;
    213 
    214 	case 'w':
    215 	case 'W':
    216 		/*
    217 		 * Error message will be displayed by disklabel_write()
    218 		 */
    219 		if (disklabel_write(block, sizeof(block), disk_ofp))
    220 			goto out;
    221 		else
    222 			printf("Sucessfully wrote label to %s\n", diskname);
    223 		break;
    224 
    225 	case 'd':
    226 	case 'D':
    227 		goto out;
    228 		/* NOTREACHED */
    229 
    230 	default:
    231 		printf("unkown command: %s\n", line);
    232 	}
    233 
    234 	goto disklabel_loop;
    235 	/* NOTREACHED */
    236 
    237  out:
    238 	/*
    239 	 * Close disk.  Marks disk `not alive' so that partition
    240 	 * information will be reloaded upon next open.
    241 	 */
    242 	(void)close(dfd);
    243 }
    244 
    245 #define GETNUM(out, num)						\
    246 	printf((out), (num));						\
    247 	bzero(line, sizeof(line));					\
    248 	gets(line);							\
    249 	if (line[0])							\
    250 		(num) = atoi(line);
    251 
    252 #define GETNUM2(out, num1, num2)					\
    253 	printf((out), (num1), (num2));					\
    254 	bzero(line, sizeof(line));					\
    255 	gets(line);							\
    256 	if (line[0])							\
    257 		(num2) = atoi(line);
    258 
    259 #define GETSTR(out, str)						\
    260 	printf((out), (str));						\
    261 	bzero(line, sizeof(line));					\
    262 	gets(line);							\
    263 	if (line[0])							\
    264 		strcpy((str), line);
    265 
    266 #define FLAGS(out, flag)						\
    267 	printf((out), lp->d_flags & (flag) ? 'y' : 'n');		\
    268 	bzero(line, sizeof(line));					\
    269 	gets(line);							\
    270 	if (line[0] == 'y' || line[0] == 'Y')				\
    271 		lp->d_flags |= (flag);					\
    272 	else								\
    273 		lp->d_flags &= ~(flag);
    274 
    275 void
    276 disklabel_edit(lp)
    277 	struct disklabel *lp;
    278 {
    279 	int i;
    280 
    281 	printf("Select disk type.  Valid types:\n");
    282 	for (i = 0; i < DKMAXTYPES; i++)
    283 		printf("%d     %s\n", i, dktypenames[i]);
    284 	printf("\n");
    285 
    286 	GETNUM("Disk type (number)? [%d] ", lp->d_type);
    287 	GETSTR("Disk model name? [%s] ", lp->d_typename);
    288 	GETSTR("Disk pack name? [%s] ", lp->d_packname);
    289 	FLAGS("Bad sectoring? [%c] ", D_BADSECT);
    290 	FLAGS("Ecc? [%c] ", D_ECC);
    291 	FLAGS("Removable? [%c] ", D_REMOVABLE);
    292 
    293 	printf("\n");
    294 
    295 	GETNUM("Interleave? [%d] ", lp->d_interleave);
    296 	GETNUM("Rpm? [%d] ", lp->d_rpm);
    297 	GETNUM("Trackskew? [%d] ", lp->d_trackskew);
    298 	GETNUM("Cylinderskew? [%d] ", lp->d_cylskew);
    299 	GETNUM("Headswitch? [%d] ", lp->d_headswitch);
    300 	GETNUM("Track-to-track? [%d] ", lp->d_trkseek);
    301 	GETNUM("Drivedata 0? [%d] ", lp->d_drivedata[0]);
    302 	GETNUM("Drivedata 1? [%d] ", lp->d_drivedata[1]);
    303 	GETNUM("Drivedata 2? [%d] ", lp->d_drivedata[2]);
    304 	GETNUM("Drivedata 3? [%d] ", lp->d_drivedata[3]);
    305 	GETNUM("Drivedata 4? [%d] ", lp->d_drivedata[4]);
    306 
    307 	printf("\n");
    308 
    309 	GETNUM("Bytes/sector? [%d] ", lp->d_secsize);
    310 	GETNUM("Sectors/track? [%d] ", lp->d_nsectors);
    311 	GETNUM("Tracks/cylinder? [%d] ", lp->d_ntracks);
    312 	GETNUM("Sectors/cylinder? [%d] ", lp->d_secpercyl);
    313 	GETNUM("Cylinders? [%d] ", lp->d_ncylinders);
    314 
    315 	printf("
    316 Enter partition table.  Note, sizes and offsets are in sectors.\n\n");
    317 
    318 	lp->d_npartitions = MAXPARTITIONS;
    319 	for (i = 0; i < lp->d_npartitions; ++i) {
    320 		GETNUM2("%c partition: offset? [%d] ", ('a' + i),
    321 		    lp->d_partitions[i].p_offset);
    322 		GETNUM("             size? [%d] ", lp->d_partitions[i].p_size);
    323 	}
    324 
    325 	/* Perform magic. */
    326 	lp->d_magic = lp->d_magic2 = DISKMAGIC;
    327 
    328 	/* Calculate disklabel checksum. */
    329 	lp->d_checksum = 0;
    330 	lp->d_checksum = dkcksum(lp);
    331 }
    332 
    333 void
    334 disklabel_show(lp)
    335 	struct disklabel *lp;
    336 {
    337 	int i, npart;
    338 	struct partition *pp;
    339 
    340 	/*
    341 	 * Check for valid disklabel.
    342 	 */
    343 	if (lp->d_magic != DISKMAGIC || lp->d_magic2 != DISKMAGIC) {
    344 		printf("No disklabel to show.\n");
    345 		return;
    346 	}
    347 
    348 	if (lp->d_npartitions > MAXPARTITIONS || dkcksum(lp) != 0) {
    349 		printf("Corrupted disklabel.\n");
    350 		return;
    351 	}
    352 
    353 	printf("\ndisk type %d (%s), %s: %s%s%s\n", lp->d_type,
    354 	    lp->d_type < DKMAXTYPES ? dktypenames[lp->d_type] :
    355 	    dktypenames[0], lp->d_typename,
    356 	    (lp->d_flags & D_REMOVABLE) ? " removable" : "",
    357 	    (lp->d_flags & D_ECC) ? " ecc" : "",
    358 	    (lp->d_flags & D_BADSECT) ? " badsect" : "");
    359 
    360 	printf("interleave %d, rpm %d, trackskew %d, cylinderskew %d\n",
    361 	    lp->d_interleave, lp->d_rpm, lp->d_trackskew, lp->d_cylskew);
    362 
    363 	printf("headswitch %d, track-to-track %d, drivedata: %d %d %d %d %d\n",
    364 	    lp->d_headswitch, lp->d_trkseek, lp->d_drivedata[0],
    365 	    lp->d_drivedata[1], lp->d_drivedata[2], lp->d_drivedata[3],
    366 	    lp->d_drivedata[4]);
    367 
    368 	printf("\nbytes/sector: %d\n", lp->d_secsize);
    369 	printf("sectors/track: %d\n", lp->d_nsectors);
    370 	printf("tracks/cylinder: %d\n", lp->d_ntracks);
    371 	printf("sectors/cylinder: %d\n", lp->d_secpercyl);
    372 	printf("cylinders: %d\n", lp->d_ncylinders);
    373 
    374 	printf("\n%d partitions:\n", lp->d_npartitions);
    375 	printf("     size   offset\n");
    376 	pp = lp->d_partitions;
    377 	for (i = 0; i < lp->d_npartitions; i++) {
    378 		printf("%c:   %d,    %d\n", 97 + i, lp->d_partitions[i].p_size,
    379 		    lp->d_partitions[i].p_offset);
    380 	}
    381 	printf("\n");
    382 }
    383 
    384 int
    385 disklabel_write(block, len, ofp)
    386 	char *block;
    387 	int len;
    388 	struct open_file *ofp;
    389 {
    390 	int error = 0;
    391 	size_t xfersize;
    392 
    393 	if (error = (*ofp->f_dev->dv_strategy)(ofp->f_devdata, F_WRITE,
    394 	    LABELSECTOR, len, block, &xfersize))
    395 		printf("cannot write disklabel, errno = %d\n", error);
    396 
    397 	return (error);
    398 }
    399 
    400 int
    401 opendisk(question, diskname, len, partition, fdp)
    402 	char *question, *diskname;
    403 	int len;
    404 	char partition;
    405 	int *fdp;
    406 {
    407 	char fulldiskname[64], *filename;
    408 	int i, error = 0;
    409 
    410  getdiskname:
    411 	printf("%s ", question);
    412 	bzero(diskname, len);
    413 	bzero(fulldiskname, sizeof(fulldiskname));
    414 	gets(diskname);
    415 	if (diskname[0] == '\n' || diskname[0] == '\0')
    416 		goto getdiskname;
    417 
    418 	/*
    419 	 * devopen() is picky.  Make sure it gets the sort of string it
    420 	 * wants.
    421 	 */
    422 	bcopy(diskname, fulldiskname,
    423 	    len < sizeof(fulldiskname) ? len : sizeof(fulldiskname));
    424 	for (i = 0; fulldiskname[i + 1] != '\0'; ++i)
    425 		/* Nothing. */ ;
    426 	if (fulldiskname[i] < '0' || fulldiskname[i] > '9') {
    427 		printf("invalid disk name %s\n", diskname);
    428 		goto getdiskname;
    429 	}
    430 	fulldiskname[++i] = partition; fulldiskname[++i] = ':';
    431 
    432 	/*
    433 	 * We always open for writing.
    434 	 */
    435 	if ((*fdp = open(fulldiskname, 1)) < 0) {
    436 		printf("cannot open %s\n", diskname);
    437 		return (1);
    438 	}
    439 
    440 	return (0);
    441 }
    442 
    443 /*
    444  * Copy a miniroot image from an NFS server or tape to the `b' partition
    445  * of the specified disk.  Note, this assumes 512 byte sectors.
    446  */
    447 void
    448 miniroot()
    449 {
    450 	int sfd, dfd, i, nblks;
    451 	char diskname[64], minirootname[128];
    452 	char block[DEV_BSIZE];
    453 	char tapename[64];
    454 	int fileno, ignoreshread, eof, len;
    455 	struct stat st;
    456 	size_t xfersize;
    457 	struct open_file *disk_ofp;
    458 	extern struct open_file files[];
    459 
    460 	/* Error message printed by opendisk() */
    461 	if (opendisk("Disk for miniroot?", diskname, sizeof(diskname),
    462 	    'b', &dfd))
    463 		return;
    464 
    465 	disk_ofp = &files[dfd];
    466 
    467  getsource:
    468 	printf("Source? (N)FS, (t)ape, (d)one > ");
    469 	bzero(line, sizeof(line));
    470 	gets(line);
    471 	if (line[0] == '\0')
    472 		goto getsource;
    473 
    474 	switch (line[0]) {
    475 	case 'n':
    476 	case 'N':
    477  name_of_nfs_miniroot:
    478 		printf("Name of miniroot file? ");
    479 		bzero(line, sizeof(line));
    480 		bzero(minirootname, sizeof(minirootname));
    481 		gets(line);
    482 		if (line[0] == '\0')
    483 			goto name_of_nfs_miniroot;
    484 		(void)strcat(minirootname, "le0a:");
    485 		(void)strcat(minirootname, line);
    486 		if ((sfd = open(minirootname, 0)) < 0) {
    487 			printf("can't open %s\n", line);
    488 			return;
    489 		}
    490 
    491 		/*
    492 		 * Find out how big the miniroot is... we can't
    493 		 * check for size because it may be compressed.
    494 		 */
    495 		ignoreshread = 1;
    496 		if (fstat(sfd, &st) < 0) {
    497 			printf("can't stat %s\n", line);
    498 			goto done;
    499 		}
    500 		nblks = (int)(st.st_size / sizeof(block));
    501 
    502 		printf("Copying miniroot from %s to %s...", line,
    503 		    diskname);
    504 		break;
    505 
    506 	case 't':
    507 	case 'T':
    508  name_of_tape_miniroot:
    509 		printf("Which tape device? ");
    510 		bzero(line, sizeof(line));
    511 		bzero(minirootname, sizeof(minirootname));
    512 		bzero(tapename, sizeof(tapename));
    513 		gets(line);
    514 		if (line[0] == '\0')
    515 			goto name_of_tape_miniroot;
    516 		strcat(minirootname, line);
    517 		strcat(tapename, line);
    518 
    519 		printf("File number (first == 1)? ");
    520 		bzero(line, sizeof(line));
    521 		gets(line);
    522 		fileno = a2int(line);
    523 		if (fileno < 1 || fileno > 8) {
    524 			printf("Invalid file number: %s\n", line);
    525 			goto getsource;
    526 		}
    527 		for (i = 0; i < sizeof(minirootname); ++i) {
    528 			if (minirootname[i] == '\0')
    529 				break;
    530 		}
    531 		if (i == sizeof(minirootname) ||
    532 		    (sizeof(minirootname) - i) < 8) {
    533 			printf("Invalid device name: %s\n", tapename);
    534 			goto getsource;
    535 		}
    536 		minirootname[i++] = 'a' + (fileno - 1);
    537 		minirootname[i++] = ':';
    538 		strcat(minirootname, "XXX");	/* lameness in open() */
    539 
    540 		ignoreshread = 0;
    541 		printf("Copy how many %d byte blocks? ", DEV_BSIZE);
    542 		bzero(line, sizeof(line));
    543 		gets(line);
    544 		nblks = a2int(line);
    545 		if (nblks < 0) {
    546 			printf("Invalid block count: %s\n", line);
    547 			goto getsource;
    548 		} else if (nblks == 0) {
    549 			printf("Zero blocks?  Ok, aborting.\n");
    550 			return;
    551 		}
    552 
    553 		if ((sfd = open(minirootname, 0)) < 0) {
    554 			printf("can't open %s file %c\n", tapename, fileno);
    555 			return;
    556 		}
    557 
    558 		printf("Copying %s file %d to %s...", tapename, fileno,
    559 		    diskname);
    560 		break;
    561 
    562 	case 'd':
    563 	case 'D':
    564 		return;
    565 
    566 	default:
    567 		printf("Unknown source: %s\n", line);
    568 		goto getsource;
    569 	}
    570 
    571 	/*
    572 	 * Copy loop...
    573 	 * This is fairly slow... if someone wants to speed it
    574 	 * up, they'll get no complaints from me.
    575 	 */
    576 	for (i = 0, eof = 0; i < nblks || ignoreshread == 0; i++) {
    577 		if ((len = read(sfd, block, sizeof(block))) < 0) {
    578 			printf("Read error, errno = %d\n", errno);
    579 			goto out;
    580 		}
    581 
    582 		/*
    583 		 * Check for end-of-file.
    584 		 */
    585 		if (len == 0)
    586 			goto done;
    587 		else if (len < sizeof(block))
    588 			eof = 1;
    589 
    590 		if ((*disk_ofp->f_dev->dv_strategy)(disk_ofp->f_devdata,
    591 		    F_WRITE, i, len, block, &xfersize) || xfersize != len) {
    592 			printf("Bad write at block %d, errno = %d\n",
    593 			    i, errno);
    594 			goto out;
    595 		}
    596 
    597 		if (eof)
    598 			goto done;
    599 	}
    600  done:
    601 	printf("done\n");
    602 
    603 	printf("Successfully copied miniroot image.\n");
    604 
    605  out:
    606 	close(sfd);
    607 	close(dfd);
    608 }
    609 
    610 /*
    611  * Boot the kernel from the miniroot image into single-user.
    612  */
    613 void
    614 bootmini()
    615 {
    616 	char diskname[64], bootname[64];
    617 	int i;
    618 
    619  getdiskname:
    620 	printf("Disk to boot from? ");
    621 	bzero(diskname, sizeof(diskname));
    622 	bzero(bootname, sizeof(bootname));
    623 	gets(diskname);
    624 	if (diskname[0] == '\n' || diskname[0] == '\0')
    625 		goto getdiskname;
    626 
    627 	/*
    628 	 * devopen() is picky.  Make sure it gets the sort of string it
    629 	 * wants.
    630 	 */
    631 	(void)strcat(bootname, diskname);
    632 	for (i = 0; bootname[i + 1] != '\0'; ++i)
    633 		/* Nothing. */ ;
    634 	if (bootname[i] < '0' || bootname[i] > '9') {
    635 		printf("invalid disk name %s\n", diskname);
    636 		goto getdiskname;
    637 	}
    638 	bootname[++i] = 'b'; bootname[++i] = ':';
    639 	(void)strcat(bootname, kernel_name);
    640 
    641 	howto = RB_SINGLE;	/* _Always_ */
    642 
    643 	printf("booting: %s -s\n", bootname);
    644 	exec(bootname, lowram, howto);
    645 	printf("boot: %s\n", strerror(errno));
    646 }
    647 
    648 /*
    649  * Reset the system.
    650  */
    651 void
    652 resetsys()
    653 {
    654 
    655 	call_req_reboot();
    656 	printf("panic: can't reboot, halting\n");
    657 	asm("stop #0x2700");
    658 }
    659 
    660 /*
    661  * XXX Should have a generic atoi for libkern/libsa.
    662  */
    663 int
    664 a2int(cp)
    665 	char *cp;
    666 {
    667 	int i = 0;
    668 
    669 	if (*cp == '\0')
    670 		return (-1);
    671 
    672 	while (*cp != '\0')
    673 		i = i * 10 + *cp++ - '0';
    674 	return (i);
    675 }
    676