Home | History | Annotate | Line # | Download | only in inst
inst.c revision 1.5
      1 /*	$NetBSD: inst.c,v 1.5 1997/12/15 23:17:19 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 void	get_fstype __P((struct disklabel *lp, int));
    105 int	a2int __P((char *));
    106 
    107 struct	inst_command {
    108 	char	*ic_cmd;		/* command name */
    109 	char	*ic_desc;		/* command description */
    110 	void	(*ic_func) __P((void));	/* handling function */
    111 } inst_commands[] = {
    112 	{ "disklabel",	"place partition map on disk",	dsklabel },
    113 	{ "miniroot",	"place miniroot on disk",	miniroot },
    114 	{ "boot",	"boot from miniroot",		bootmini },
    115 	{ "reset",	"reset the system",		resetsys },
    116 	{ "help",	"display command list",		gethelp },
    117 };
    118 #define NCMDS	(sizeof(inst_commands) / sizeof(inst_commands[0]))
    119 
    120 main()
    121 {
    122 	int i, currname = 0;
    123 
    124 	/*
    125 	 * We want netopen() to ask for IP address, etc, rather
    126 	 * that using bootparams.
    127 	 */
    128 	netio_ask = 1;
    129 
    130 	printf("\n");
    131 	printf(">> %s, Revision %s\n", bootprog_name, bootprog_rev);
    132 	printf(">> (%s, %s)\n", bootprog_maker, bootprog_date);
    133 	printf(">> HP 9000/%s SPU\n", getmachineid());
    134 	gethelp();
    135 
    136 	for (;;) {
    137 		printf("sys_inst> ");
    138 		bzero(line, sizeof(line));
    139 		gets(line);
    140 		if (line[0] == '\n' || line[0] == '\0')
    141 			continue;
    142 
    143 		for (i = 0; i < NCMDS; ++i)
    144 			if (strcmp(line, inst_commands[i].ic_cmd) == 0) {
    145 				(*inst_commands[i].ic_func)();
    146 				break;
    147 			}
    148 
    149 
    150 		if (i == NCMDS)
    151 			printf("unknown command: %s\n", line);
    152 	}
    153 }
    154 
    155 void
    156 gethelp()
    157 {
    158 	int i;
    159 
    160 	printf(">> Available commands:\n");
    161 	for (i = 0; i < NCMDS; ++i)
    162 		printf(">>     %s - %s\n", inst_commands[i].ic_cmd,
    163 		    inst_commands[i].ic_desc);
    164 }
    165 
    166 /*
    167  * Do all the steps necessary to place a disklabel on a disk.
    168  * Note, this assumes 512 byte sectors.
    169  */
    170 void
    171 dsklabel()
    172 {
    173 	struct disklabel *lp;
    174 	struct open_file *disk_ofp;
    175 	int dfd, error;
    176 	size_t xfersize;
    177 	char block[DEV_BSIZE], diskname[64];
    178 	extern struct open_file files[];
    179 
    180 	printf("
    181 You will be asked several questions about your disk, most of which
    182 require prior knowledge of the disk's geometry.  There is no easy way
    183 for the system to provide this information for you.  If you do not have
    184 this information, please consult your disk's manual or another
    185 informative source.\n\n");
    186 
    187 	/* Error message printed by opendisk() */
    188 	if (opendisk("Disk to label?", diskname, sizeof(diskname),
    189 	    ('a' + RAW_PART), &dfd))
    190 		return;
    191 
    192 	disk_ofp = &files[dfd];
    193 
    194 	bzero(block, sizeof(block));
    195 	if (error = (*disk_ofp->f_dev->dv_strategy)(disk_ofp->f_devdata,
    196 	    F_READ, LABELSECTOR, sizeof(block), block, &xfersize)) {
    197 		printf("cannot read disk %s, errno = %d\n", diskname, error);
    198 		return;
    199 	}
    200 
    201 	printf("Sucessfully read %d bytes from %s\n", xfersize, diskname);
    202 
    203 	lp = (struct disklabel *)((void *)(&block[LABELOFFSET]));
    204 
    205  disklabel_loop:
    206 	bzero(line, sizeof(line));
    207 	printf("(z)ap, (e)dit, (s)how, (w)rite, (d)one > ");
    208 	gets(line);
    209 	if (line[0] == '\n' || line[0] == '\0')
    210 		goto disklabel_loop;
    211 
    212 	switch (line[0]) {
    213 	case 'z':
    214 	case 'Z': {
    215 		char zap[DEV_BSIZE];
    216 		bzero(zap, sizeof(zap));
    217 		(void)(*disk_ofp->f_dev->dv_strategy)(disk_ofp->f_devdata,
    218 		    F_WRITE, LABELSECTOR, sizeof(zap), zap, &xfersize);
    219 		}
    220 		goto out;
    221 		/* NOTREACHED */
    222 
    223 	case 'e':
    224 	case 'E':
    225 		disklabel_edit(lp);
    226 		break;
    227 
    228 	case 's':
    229 	case 'S':
    230 		disklabel_show(lp);
    231 		break;
    232 
    233 	case 'w':
    234 	case 'W':
    235 		/*
    236 		 * Error message will be displayed by disklabel_write()
    237 		 */
    238 		if (disklabel_write(block, sizeof(block), disk_ofp))
    239 			goto out;
    240 		else
    241 			printf("Sucessfully wrote label to %s\n", diskname);
    242 		break;
    243 
    244 	case 'd':
    245 	case 'D':
    246 		goto out;
    247 		/* NOTREACHED */
    248 
    249 	default:
    250 		printf("unkown command: %s\n", line);
    251 	}
    252 
    253 	goto disklabel_loop;
    254 	/* NOTREACHED */
    255 
    256  out:
    257 	/*
    258 	 * Close disk.  Marks disk `not alive' so that partition
    259 	 * information will be reloaded upon next open.
    260 	 */
    261 	(void)close(dfd);
    262 }
    263 
    264 #define GETNUM(out, num)						\
    265 	printf((out), (num));						\
    266 	bzero(line, sizeof(line));					\
    267 	gets(line);							\
    268 	if (line[0])							\
    269 		(num) = atoi(line);
    270 
    271 #define GETNUM2(out, num1, num2)					\
    272 	printf((out), (num1), (num2));					\
    273 	bzero(line, sizeof(line));					\
    274 	gets(line);							\
    275 	if (line[0])							\
    276 		(num2) = atoi(line);
    277 
    278 #define GETSTR(out, str)						\
    279 	printf((out), (str));						\
    280 	bzero(line, sizeof(line));					\
    281 	gets(line);							\
    282 	if (line[0])							\
    283 		strcpy((str), line);
    284 
    285 #define FLAGS(out, flag)						\
    286 	printf((out), lp->d_flags & (flag) ? 'y' : 'n');		\
    287 	bzero(line, sizeof(line));					\
    288 	gets(line);							\
    289 	if (line[0] == 'y' || line[0] == 'Y')				\
    290 		lp->d_flags |= (flag);					\
    291 	else								\
    292 		lp->d_flags &= ~(flag);
    293 
    294 struct fsname_to_type {
    295 	const char *name;
    296 	u_int8_t type;
    297 } n_to_t[] = {
    298 	{ "unused",	FS_UNUSED },
    299 	{ "ffs",	FS_BSDFFS },
    300 	{ "swap",	FS_SWAP },
    301 	{ "boot",	FS_BOOT },
    302 	{ NULL,		0 },
    303 };
    304 
    305 void
    306 get_fstype(lp, partno)
    307 	struct disklabel *lp;
    308 	int partno;
    309 {
    310 	static int blocksize = 8192;	/* XXX */
    311 	struct partition *pp = &lp->d_partitions[partno];
    312 	struct fsname_to_type *np;
    313 	int fragsize;
    314 	char line[80], str[80];
    315 
    316 	if (pp->p_size == 0) {
    317 		/*
    318 		 * No need to bother asking for a zero-sized partition.
    319 		 */
    320 		pp->p_fstype = FS_UNUSED;
    321 		return;
    322 	}
    323 
    324 	/*
    325 	 * Select a default.
    326 	 * XXX Should we check what might be in the label already?
    327 	 */
    328 	if (partno == 1)
    329 		strcpy(str, "swap");
    330 	else if (partno == RAW_PART)
    331 		strcpy(str, "boot");
    332 	else
    333 		strcpy(str, "ffs");
    334 
    335  again:
    336 	GETSTR("             fstype? [%s] ", str);
    337 
    338 	for (np = n_to_t; np->name != NULL; np++)
    339 		if (strcmp(str, np->name) == 0)
    340 			break;
    341 
    342 	if (np->name == NULL) {
    343 		printf("Please use one of: ");
    344 		for (np = n_to_t; np->name != NULL; np++)
    345 			printf(" %s", np->name);
    346 		printf(".\n");
    347 		goto again;
    348 	}
    349 
    350 	pp->p_fstype = np->type;
    351 
    352 	if (pp->p_fstype != FS_BSDFFS)
    353 		return;
    354 
    355 	/*
    356 	 * Get additional information needed for FFS.
    357 	 */
    358  ffs_again:
    359 	GETNUM("             FFS block size? [%d] ", blocksize);
    360 	if (blocksize < NBPG || (blocksize % NBPG) != 0) {
    361 		printf("FFS block size must be a multiple of %d.\n", NBPG);
    362 		goto ffs_again;
    363 	}
    364 
    365 	fragsize = blocksize / 8;	/* XXX */
    366 	fragsize = max(fragsize, lp->d_secsize);
    367 	GETNUM("             FFS fragment size? [%d] ", fragsize);
    368 	if (fragsize < lp->d_secsize || (fragsize % lp->d_secsize) != 0) {
    369 		printf("FFS fragment size must be a multiple of sector size"
    370 		    " (%d).\n", lp->d_secsize);
    371 		goto ffs_again;
    372 	}
    373 	if ((blocksize % fragsize) != 0) {
    374 		printf("FFS fragment size must be an even divisor of FFS"
    375 		    " block size (%d).\n", blocksize);
    376 		goto ffs_again;
    377 	}
    378 
    379 	/*
    380 	 * XXX Better sanity checking?
    381 	 */
    382 
    383 	pp->p_frag = blocksize / fragsize;
    384 	pp->p_fsize = fragsize;
    385 }
    386 
    387 void
    388 disklabel_edit(lp)
    389 	struct disklabel *lp;
    390 {
    391 	int i;
    392 
    393 	printf("Select disk type.  Valid types:\n");
    394 	for (i = 0; i < DKMAXTYPES; i++)
    395 		printf("%d     %s\n", i, dktypenames[i]);
    396 	printf("\n");
    397 
    398 	GETNUM("Disk type (number)? [%d] ", lp->d_type);
    399 	GETSTR("Disk model name? [%s] ", lp->d_typename);
    400 	GETSTR("Disk pack name? [%s] ", lp->d_packname);
    401 	FLAGS("Bad sectoring? [%c] ", D_BADSECT);
    402 	FLAGS("Ecc? [%c] ", D_ECC);
    403 	FLAGS("Removable? [%c] ", D_REMOVABLE);
    404 
    405 	printf("\n");
    406 
    407 	GETNUM("Interleave? [%d] ", lp->d_interleave);
    408 	GETNUM("Rpm? [%d] ", lp->d_rpm);
    409 	GETNUM("Trackskew? [%d] ", lp->d_trackskew);
    410 	GETNUM("Cylinderskew? [%d] ", lp->d_cylskew);
    411 	GETNUM("Headswitch? [%d] ", lp->d_headswitch);
    412 	GETNUM("Track-to-track? [%d] ", lp->d_trkseek);
    413 	GETNUM("Drivedata 0? [%d] ", lp->d_drivedata[0]);
    414 	GETNUM("Drivedata 1? [%d] ", lp->d_drivedata[1]);
    415 	GETNUM("Drivedata 2? [%d] ", lp->d_drivedata[2]);
    416 	GETNUM("Drivedata 3? [%d] ", lp->d_drivedata[3]);
    417 	GETNUM("Drivedata 4? [%d] ", lp->d_drivedata[4]);
    418 
    419 	printf("\n");
    420 
    421 	GETNUM("Bytes/sector? [%d] ", lp->d_secsize);
    422 	GETNUM("Sectors/track? [%d] ", lp->d_nsectors);
    423 	GETNUM("Tracks/cylinder? [%d] ", lp->d_ntracks);
    424 	GETNUM("Sectors/cylinder? [%d] ", lp->d_secpercyl);
    425 	GETNUM("Cylinders? [%d] ", lp->d_ncylinders);
    426 
    427 	printf("
    428 Enter partition table.  Note, sizes and offsets are in sectors.\n\n");
    429 
    430 	lp->d_npartitions = MAXPARTITIONS;
    431 	for (i = 0; i < lp->d_npartitions; ++i) {
    432 		GETNUM2("%c partition: offset? [%d] ", ('a' + i),
    433 		    lp->d_partitions[i].p_offset);
    434 		GETNUM("             size? [%d] ", lp->d_partitions[i].p_size);
    435 		get_fstype(lp, i);
    436 	}
    437 
    438 	/* Perform magic. */
    439 	lp->d_magic = lp->d_magic2 = DISKMAGIC;
    440 
    441 	/* Calculate disklabel checksum. */
    442 	lp->d_checksum = 0;
    443 	lp->d_checksum = dkcksum(lp);
    444 }
    445 
    446 void
    447 disklabel_show(lp)
    448 	struct disklabel *lp;
    449 {
    450 	int i, npart;
    451 	struct partition *pp;
    452 
    453 	/*
    454 	 * Check for valid disklabel.
    455 	 */
    456 	if (lp->d_magic != DISKMAGIC || lp->d_magic2 != DISKMAGIC) {
    457 		printf("No disklabel to show.\n");
    458 		return;
    459 	}
    460 
    461 	if (lp->d_npartitions > MAXPARTITIONS || dkcksum(lp) != 0) {
    462 		printf("Corrupted disklabel.\n");
    463 		return;
    464 	}
    465 
    466 	printf("\ndisk type %d (%s), %s: %s%s%s\n", lp->d_type,
    467 	    lp->d_type < DKMAXTYPES ? dktypenames[lp->d_type] :
    468 	    dktypenames[0], lp->d_typename,
    469 	    (lp->d_flags & D_REMOVABLE) ? " removable" : "",
    470 	    (lp->d_flags & D_ECC) ? " ecc" : "",
    471 	    (lp->d_flags & D_BADSECT) ? " badsect" : "");
    472 
    473 	printf("interleave %d, rpm %d, trackskew %d, cylinderskew %d\n",
    474 	    lp->d_interleave, lp->d_rpm, lp->d_trackskew, lp->d_cylskew);
    475 
    476 	printf("headswitch %d, track-to-track %d, drivedata: %d %d %d %d %d\n",
    477 	    lp->d_headswitch, lp->d_trkseek, lp->d_drivedata[0],
    478 	    lp->d_drivedata[1], lp->d_drivedata[2], lp->d_drivedata[3],
    479 	    lp->d_drivedata[4]);
    480 
    481 	printf("\nbytes/sector: %d\n", lp->d_secsize);
    482 	printf("sectors/track: %d\n", lp->d_nsectors);
    483 	printf("tracks/cylinder: %d\n", lp->d_ntracks);
    484 	printf("sectors/cylinder: %d\n", lp->d_secpercyl);
    485 	printf("cylinders: %d\n", lp->d_ncylinders);
    486 
    487 	printf("\n%d partitions:\n", lp->d_npartitions);
    488 	printf("     size   offset\n");
    489 	pp = lp->d_partitions;
    490 	for (i = 0; i < lp->d_npartitions; i++) {
    491 		printf("%c:   %d,    %d\n", 97 + i, lp->d_partitions[i].p_size,
    492 		    lp->d_partitions[i].p_offset);
    493 	}
    494 	printf("\n");
    495 }
    496 
    497 int
    498 disklabel_write(block, len, ofp)
    499 	char *block;
    500 	int len;
    501 	struct open_file *ofp;
    502 {
    503 	int error = 0;
    504 	size_t xfersize;
    505 
    506 	if (error = (*ofp->f_dev->dv_strategy)(ofp->f_devdata, F_WRITE,
    507 	    LABELSECTOR, len, block, &xfersize))
    508 		printf("cannot write disklabel, errno = %d\n", error);
    509 
    510 	return (error);
    511 }
    512 
    513 int
    514 opendisk(question, diskname, len, partition, fdp)
    515 	char *question, *diskname;
    516 	int len;
    517 	char partition;
    518 	int *fdp;
    519 {
    520 	char fulldiskname[64], *filename;
    521 	int i, error = 0;
    522 
    523  getdiskname:
    524 	printf("%s ", question);
    525 	bzero(diskname, len);
    526 	bzero(fulldiskname, sizeof(fulldiskname));
    527 	gets(diskname);
    528 	if (diskname[0] == '\n' || diskname[0] == '\0')
    529 		goto getdiskname;
    530 
    531 	/*
    532 	 * devopen() is picky.  Make sure it gets the sort of string it
    533 	 * wants.
    534 	 */
    535 	bcopy(diskname, fulldiskname,
    536 	    len < sizeof(fulldiskname) ? len : sizeof(fulldiskname));
    537 	for (i = 0; fulldiskname[i + 1] != '\0'; ++i)
    538 		/* Nothing. */ ;
    539 	if (fulldiskname[i] < '0' || fulldiskname[i] > '9') {
    540 		printf("invalid disk name %s\n", diskname);
    541 		goto getdiskname;
    542 	}
    543 	fulldiskname[++i] = partition; fulldiskname[++i] = ':';
    544 
    545 	/*
    546 	 * We always open for writing.
    547 	 */
    548 	if ((*fdp = open(fulldiskname, 1)) < 0) {
    549 		printf("cannot open %s\n", diskname);
    550 		return (1);
    551 	}
    552 
    553 	return (0);
    554 }
    555 
    556 /*
    557  * Copy a miniroot image from an NFS server or tape to the `b' partition
    558  * of the specified disk.  Note, this assumes 512 byte sectors.
    559  */
    560 void
    561 miniroot()
    562 {
    563 	int sfd, dfd, i, nblks;
    564 	char diskname[64], minirootname[128];
    565 	char block[DEV_BSIZE];
    566 	char tapename[64];
    567 	int fileno, ignoreshread, eof, len;
    568 	struct stat st;
    569 	size_t xfersize;
    570 	struct open_file *disk_ofp;
    571 	extern struct open_file files[];
    572 
    573 	/* Error message printed by opendisk() */
    574 	if (opendisk("Disk for miniroot?", diskname, sizeof(diskname),
    575 	    'b', &dfd))
    576 		return;
    577 
    578 	disk_ofp = &files[dfd];
    579 
    580  getsource:
    581 	printf("Source? (N)FS, (t)ape, (d)one > ");
    582 	bzero(line, sizeof(line));
    583 	gets(line);
    584 	if (line[0] == '\0')
    585 		goto getsource;
    586 
    587 	switch (line[0]) {
    588 	case 'n':
    589 	case 'N':
    590  name_of_nfs_miniroot:
    591 		printf("Name of miniroot file? ");
    592 		bzero(line, sizeof(line));
    593 		bzero(minirootname, sizeof(minirootname));
    594 		gets(line);
    595 		if (line[0] == '\0')
    596 			goto name_of_nfs_miniroot;
    597 		(void)strcat(minirootname, "le0a:");
    598 		(void)strcat(minirootname, line);
    599 		if ((sfd = open(minirootname, 0)) < 0) {
    600 			printf("can't open %s\n", line);
    601 			return;
    602 		}
    603 
    604 		/*
    605 		 * Find out how big the miniroot is... we can't
    606 		 * check for size because it may be compressed.
    607 		 */
    608 		ignoreshread = 1;
    609 		if (fstat(sfd, &st) < 0) {
    610 			printf("can't stat %s\n", line);
    611 			goto done;
    612 		}
    613 		nblks = (int)(st.st_size / sizeof(block));
    614 
    615 		printf("Copying miniroot from %s to %s...", line,
    616 		    diskname);
    617 		break;
    618 
    619 	case 't':
    620 	case 'T':
    621  name_of_tape_miniroot:
    622 		printf("Which tape device? ");
    623 		bzero(line, sizeof(line));
    624 		bzero(minirootname, sizeof(minirootname));
    625 		bzero(tapename, sizeof(tapename));
    626 		gets(line);
    627 		if (line[0] == '\0')
    628 			goto name_of_tape_miniroot;
    629 		strcat(minirootname, line);
    630 		strcat(tapename, line);
    631 
    632 		printf("File number (first == 1)? ");
    633 		bzero(line, sizeof(line));
    634 		gets(line);
    635 		fileno = a2int(line);
    636 		if (fileno < 1 || fileno > 8) {
    637 			printf("Invalid file number: %s\n", line);
    638 			goto getsource;
    639 		}
    640 		for (i = 0; i < sizeof(minirootname); ++i) {
    641 			if (minirootname[i] == '\0')
    642 				break;
    643 		}
    644 		if (i == sizeof(minirootname) ||
    645 		    (sizeof(minirootname) - i) < 8) {
    646 			printf("Invalid device name: %s\n", tapename);
    647 			goto getsource;
    648 		}
    649 		minirootname[i++] = 'a' + (fileno - 1);
    650 		minirootname[i++] = ':';
    651 		strcat(minirootname, "XXX");	/* lameness in open() */
    652 
    653 		ignoreshread = 0;
    654 		printf("Copy how many %d byte blocks? ", DEV_BSIZE);
    655 		bzero(line, sizeof(line));
    656 		gets(line);
    657 		nblks = a2int(line);
    658 		if (nblks < 0) {
    659 			printf("Invalid block count: %s\n", line);
    660 			goto getsource;
    661 		} else if (nblks == 0) {
    662 			printf("Zero blocks?  Ok, aborting.\n");
    663 			return;
    664 		}
    665 
    666 		if ((sfd = open(minirootname, 0)) < 0) {
    667 			printf("can't open %s file %c\n", tapename, fileno);
    668 			return;
    669 		}
    670 
    671 		printf("Copying %s file %d to %s...", tapename, fileno,
    672 		    diskname);
    673 		break;
    674 
    675 	case 'd':
    676 	case 'D':
    677 		return;
    678 
    679 	default:
    680 		printf("Unknown source: %s\n", line);
    681 		goto getsource;
    682 	}
    683 
    684 	/*
    685 	 * Copy loop...
    686 	 * This is fairly slow... if someone wants to speed it
    687 	 * up, they'll get no complaints from me.
    688 	 */
    689 	for (i = 0, eof = 0; i < nblks || ignoreshread == 0; i++) {
    690 		if ((len = read(sfd, block, sizeof(block))) < 0) {
    691 			printf("Read error, errno = %d\n", errno);
    692 			goto out;
    693 		}
    694 
    695 		/*
    696 		 * Check for end-of-file.
    697 		 */
    698 		if (len == 0)
    699 			goto done;
    700 		else if (len < sizeof(block))
    701 			eof = 1;
    702 
    703 		if ((*disk_ofp->f_dev->dv_strategy)(disk_ofp->f_devdata,
    704 		    F_WRITE, i, len, block, &xfersize) || xfersize != len) {
    705 			printf("Bad write at block %d, errno = %d\n",
    706 			    i, errno);
    707 			goto out;
    708 		}
    709 
    710 		if (eof)
    711 			goto done;
    712 	}
    713  done:
    714 	printf("done\n");
    715 
    716 	printf("Successfully copied miniroot image.\n");
    717 
    718  out:
    719 	close(sfd);
    720 	close(dfd);
    721 }
    722 
    723 /*
    724  * Boot the kernel from the miniroot image into single-user.
    725  */
    726 void
    727 bootmini()
    728 {
    729 	char diskname[64], bootname[64];
    730 	int i;
    731 
    732  getdiskname:
    733 	printf("Disk to boot from? ");
    734 	bzero(diskname, sizeof(diskname));
    735 	bzero(bootname, sizeof(bootname));
    736 	gets(diskname);
    737 	if (diskname[0] == '\n' || diskname[0] == '\0')
    738 		goto getdiskname;
    739 
    740 	/*
    741 	 * devopen() is picky.  Make sure it gets the sort of string it
    742 	 * wants.
    743 	 */
    744 	(void)strcat(bootname, diskname);
    745 	for (i = 0; bootname[i + 1] != '\0'; ++i)
    746 		/* Nothing. */ ;
    747 	if (bootname[i] < '0' || bootname[i] > '9') {
    748 		printf("invalid disk name %s\n", diskname);
    749 		goto getdiskname;
    750 	}
    751 	bootname[++i] = 'b'; bootname[++i] = ':';
    752 	(void)strcat(bootname, kernel_name);
    753 
    754 	howto = RB_SINGLE;	/* _Always_ */
    755 
    756 	printf("booting: %s -s\n", bootname);
    757 	exec(bootname, lowram, howto);
    758 	printf("boot: %s\n", strerror(errno));
    759 }
    760 
    761 /*
    762  * Reset the system.
    763  */
    764 void
    765 resetsys()
    766 {
    767 
    768 	call_req_reboot();
    769 	printf("panic: can't reboot, halting\n");
    770 	asm("stop #0x2700");
    771 }
    772 
    773 /*
    774  * XXX Should have a generic atoi for libkern/libsa.
    775  */
    776 int
    777 a2int(cp)
    778 	char *cp;
    779 {
    780 	int i = 0;
    781 
    782 	if (*cp == '\0')
    783 		return (-1);
    784 
    785 	while (*cp != '\0')
    786 		i = i * 10 + *cp++ - '0';
    787 	return (i);
    788 }
    789