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