Home | History | Annotate | Line # | Download | only in inst
inst.c revision 1.1
      1 /*	$NetBSD: inst.c,v 1.1 1997/02/04 03:52:57 thorpej Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1995, 1996 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 CPU\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;
    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.  Make sure it's
    493 		 * an even number of blocks...
    494 		 */
    495 		if (fstat(sfd, &st) < 0) {
    496 			printf("can't stat %s\n", line);
    497 			goto done;
    498 		}
    499 		if (st.st_size % DEV_BSIZE) {
    500 			printf("Miniroot size must be an even multiple of %d\n",
    501 			    DEV_BSIZE);
    502 			return;
    503 		}
    504 		nblks = (int)(st.st_size / sizeof(block));
    505 
    506 		printf("Copying %d blocks from %s to %s...", nblks, line,
    507 		    diskname);
    508 		break;
    509 
    510 	case 't':
    511 	case 'T':
    512  name_of_tape_miniroot:
    513 		printf("Which tape device? ");
    514 		bzero(line, sizeof(line));
    515 		bzero(minirootname, sizeof(minirootname));
    516 		bzero(tapename, sizeof(tapename));
    517 		gets(line);
    518 		if (line[0] == '\0')
    519 			goto name_of_tape_miniroot;
    520 		strcat(minirootname, line);
    521 		strcat(tapename, line);
    522 
    523 		printf("File number (first == 1)? ");
    524 		bzero(line, sizeof(line));
    525 		gets(line);
    526 		fileno = a2int(line);
    527 		if (fileno < 1 || fileno > 8) {
    528 			printf("Invalid file number: %s\n", line);
    529 			goto getsource;
    530 		}
    531 		for (i = 0; i < sizeof(minirootname); ++i) {
    532 			if (minirootname[i] == '\0')
    533 				break;
    534 		}
    535 		if (i == sizeof(minirootname) ||
    536 		    (sizeof(minirootname) - i) < 8) {
    537 			printf("Invalid device name: %s\n", tapename);
    538 			goto getsource;
    539 		}
    540 		minirootname[i++] = 'a' + (fileno - 1);
    541 		minirootname[i++] = ':';
    542 		strcat(minirootname, "XXX");	/* lameness in open() */
    543 
    544 		printf("Copy how many %d byte blocks? ", DEV_BSIZE);
    545 		bzero(line, sizeof(line));
    546 		gets(line);
    547 		nblks = a2int(line);
    548 		if (nblks < 0) {
    549 			printf("Invalid block count: %s\n", line);
    550 			goto getsource;
    551 		} else if (nblks == 0) {
    552 			printf("Zero blocks?  Ok, aborting.\n");
    553 			return;
    554 		}
    555 
    556 		if ((sfd = open(minirootname, 0)) < 0) {
    557 			printf("can't open %s file %c\n", tapename, fileno);
    558 			return;
    559 		}
    560 
    561 		printf("Copying %s file %c to %s...", tapename, fileno,
    562 		    diskname);
    563 		break;
    564 
    565 	case 'd':
    566 	case 'D':
    567 		return;
    568 
    569 	default:
    570 		printf("Unknown source: %s\n", line);
    571 		goto getsource;
    572 	}
    573 
    574 	/*
    575 	 * Copy loop...
    576 	 * This is fairly slow... if someone wants to speed it
    577 	 * up, they'll get no complaints from me.
    578 	 */
    579 	for (i = 0; i < nblks; ++i) {
    580 		if (read(sfd, block, sizeof(block)) != sizeof(block)) {
    581 			printf("Short read, errno = %d\n", errno);
    582 			goto done;
    583 		}
    584 		if ((*disk_ofp->f_dev->dv_strategy)(disk_ofp->f_devdata,
    585 		    F_WRITE, i, sizeof(block), block, &xfersize) ||
    586 		    xfersize != sizeof(block)) {
    587 			printf("Bad write at block %d, errno = %d\n",
    588 			    i, errno);
    589 			goto done;
    590 		}
    591 	}
    592 	printf("done\n");
    593 
    594 	printf("Successfully copied miniroot image.\n");
    595 
    596  done:
    597 	close(sfd);
    598 	close(dfd);
    599 }
    600 
    601 /*
    602  * Boot the kernel from the miniroot image into single-user.
    603  */
    604 void
    605 bootmini()
    606 {
    607 	char diskname[64], bootname[64];
    608 	int i;
    609 
    610  getdiskname:
    611 	printf("Disk to boot from? ");
    612 	bzero(diskname, sizeof(diskname));
    613 	bzero(bootname, sizeof(bootname));
    614 	gets(diskname);
    615 	if (diskname[0] == '\n' || diskname[0] == '\0')
    616 		goto getdiskname;
    617 
    618 	/*
    619 	 * devopen() is picky.  Make sure it gets the sort of string it
    620 	 * wants.
    621 	 */
    622 	(void)strcat(bootname, diskname);
    623 	for (i = 0; bootname[i + 1] != '\0'; ++i)
    624 		/* Nothing. */ ;
    625 	if (bootname[i] < '0' || bootname[i] > '9') {
    626 		printf("invalid disk name %s\n", diskname);
    627 		goto getdiskname;
    628 	}
    629 	bootname[++i] = 'b'; bootname[++i] = ':';
    630 	(void)strcat(bootname, kernel_name);
    631 
    632 	howto = RB_SINGLE;	/* _Always_ */
    633 
    634 	printf("booting: %s -s\n", bootname);
    635 	exec(bootname, lowram, howto);
    636 	printf("boot: %s\n", strerror(errno));
    637 }
    638 
    639 /*
    640  * Reset the system.
    641  */
    642 void
    643 resetsys()
    644 {
    645 
    646 	call_req_reboot();
    647 	printf("panic: can't reboot, halting\n");
    648 	asm("stop #0x2700");
    649 }
    650 
    651 /*
    652  * XXX Should have a generic atoi for libkern/libsa.
    653  */
    654 int
    655 a2int(cp)
    656 	char *cp;
    657 {
    658 	int i = 0;
    659 
    660 	if (*cp == '\0')
    661 		return (-1);
    662 
    663 	while (*cp != '\0')
    664 		i = i * 10 + *cp++ - '0';
    665 	return (i);
    666 }
    667