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