Home | History | Annotate | Line # | Download | only in inst
inst.c revision 1.11
      1 /*	$NetBSD: inst.c,v 1.11 2003/11/14 16:52:40 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) __P((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()
    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()
    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()
    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 	u_int8_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(lp, partno)
    309 	struct disklabel *lp;
    310 	int partno;
    311 {
    312 	static int blocksize = 8192;	/* XXX */
    313 	struct partition *pp = &lp->d_partitions[partno];
    314 	struct fsname_to_type *np;
    315 	int fragsize;
    316 	char line[80], str[80];
    317 
    318 	if (pp->p_size == 0) {
    319 		/*
    320 		 * No need to bother asking for a zero-sized partition.
    321 		 */
    322 		pp->p_fstype = FS_UNUSED;
    323 		return;
    324 	}
    325 
    326 	/*
    327 	 * Select a default.
    328 	 * XXX Should we check what might be in the label already?
    329 	 */
    330 	if (partno == 1)
    331 		strcpy(str, "swap");
    332 	else if (partno == RAW_PART)
    333 		strcpy(str, "boot");
    334 	else
    335 		strcpy(str, "ffs");
    336 
    337  again:
    338 	GETSTR("             fstype? [%s] ", str);
    339 
    340 	for (np = n_to_t; np->name != NULL; np++)
    341 		if (strcmp(str, np->name) == 0)
    342 			break;
    343 
    344 	if (np->name == NULL) {
    345 		printf("Please use one of: ");
    346 		for (np = n_to_t; np->name != NULL; np++)
    347 			printf(" %s", np->name);
    348 		printf(".\n");
    349 		goto again;
    350 	}
    351 
    352 	pp->p_fstype = np->type;
    353 
    354 	if (pp->p_fstype != FS_BSDFFS)
    355 		return;
    356 
    357 	/*
    358 	 * Get additional information needed for FFS.
    359 	 */
    360  ffs_again:
    361 	GETNUM("             FFS block size? [%d] ", blocksize);
    362 	if (blocksize < NBPG || (blocksize % NBPG) != 0) {
    363 		printf("FFS block size must be a multiple of %d.\n", NBPG);
    364 		goto ffs_again;
    365 	}
    366 
    367 	fragsize = blocksize / 8;	/* XXX */
    368 	fragsize = max(fragsize, lp->d_secsize);
    369 	GETNUM("             FFS fragment size? [%d] ", fragsize);
    370 	if (fragsize < lp->d_secsize || (fragsize % lp->d_secsize) != 0) {
    371 		printf("FFS fragment size must be a multiple of sector size"
    372 		    " (%d).\n", lp->d_secsize);
    373 		goto ffs_again;
    374 	}
    375 	if ((blocksize % fragsize) != 0) {
    376 		printf("FFS fragment size must be an even divisor of FFS"
    377 		    " block size (%d).\n", blocksize);
    378 		goto ffs_again;
    379 	}
    380 
    381 	/*
    382 	 * XXX Better sanity checking?
    383 	 */
    384 
    385 	pp->p_frag = blocksize / fragsize;
    386 	pp->p_fsize = fragsize;
    387 }
    388 
    389 void
    390 disklabel_edit(lp)
    391 	struct disklabel *lp;
    392 {
    393 	int i;
    394 
    395 	printf("Select disk type.  Valid types:\n");
    396 	for (i = 0; i < DKMAXTYPES; i++)
    397 		printf("%d     %s\n", i, dktypenames[i]);
    398 	printf("\n");
    399 
    400 	GETNUM("Disk type (number)? [%d] ", lp->d_type);
    401 	GETSTR("Disk model name? [%s] ", lp->d_typename);
    402 	GETSTR("Disk pack name? [%s] ", lp->d_packname);
    403 	FLAGS("Bad sectoring? [%c] ", D_BADSECT);
    404 	FLAGS("Ecc? [%c] ", D_ECC);
    405 	FLAGS("Removable? [%c] ", D_REMOVABLE);
    406 
    407 	printf("\n");
    408 
    409 	GETNUM("Interleave? [%d] ", lp->d_interleave);
    410 	GETNUM("Rpm? [%d] ", lp->d_rpm);
    411 	GETNUM("Trackskew? [%d] ", lp->d_trackskew);
    412 	GETNUM("Cylinderskew? [%d] ", lp->d_cylskew);
    413 	GETNUM("Headswitch? [%d] ", lp->d_headswitch);
    414 	GETNUM("Track-to-track? [%d] ", lp->d_trkseek);
    415 	GETNUM("Drivedata 0? [%d] ", lp->d_drivedata[0]);
    416 	GETNUM("Drivedata 1? [%d] ", lp->d_drivedata[1]);
    417 	GETNUM("Drivedata 2? [%d] ", lp->d_drivedata[2]);
    418 	GETNUM("Drivedata 3? [%d] ", lp->d_drivedata[3]);
    419 	GETNUM("Drivedata 4? [%d] ", lp->d_drivedata[4]);
    420 
    421 	printf("\n");
    422 
    423 	GETNUM("Bytes/sector? [%d] ", lp->d_secsize);
    424 	GETNUM("Sectors/track? [%d] ", lp->d_nsectors);
    425 	GETNUM("Tracks/cylinder? [%d] ", lp->d_ntracks);
    426 	if (lp->d_secpercyl == 0)
    427 		lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
    428 	GETNUM("Sectors/cylinder? [%d] ", lp->d_secpercyl);
    429 	GETNUM("Cylinders? [%d] ", lp->d_ncylinders);
    430 	if (lp->d_secperunit == 0)
    431 		lp->d_secperunit = lp->d_ncylinders * lp->d_secpercyl;
    432 	GETNUM("Total sectors? [%d] ", lp->d_secperunit);
    433 
    434 	printf(
    435 "Enter partition table.  Note, sizes and offsets are in sectors.\n\n");
    436 
    437 	lp->d_npartitions = MAXPARTITIONS;
    438 	for (i = 0; i < lp->d_npartitions; ++i) {
    439 		GETNUM2("%c partition: offset? [%d] ", ('a' + i),
    440 		    lp->d_partitions[i].p_offset);
    441 		GETNUM("             size? [%d] ", lp->d_partitions[i].p_size);
    442 		get_fstype(lp, i);
    443 	}
    444 
    445 	/* Perform magic. */
    446 	lp->d_magic = lp->d_magic2 = DISKMAGIC;
    447 
    448 	/* Calculate disklabel checksum. */
    449 	lp->d_checksum = 0;
    450 	lp->d_checksum = dkcksum(lp);
    451 }
    452 
    453 void
    454 disklabel_show(lp)
    455 	struct disklabel *lp;
    456 {
    457 	int i;
    458 	struct partition *pp;
    459 
    460 	/*
    461 	 * Check for valid disklabel.
    462 	 */
    463 	if (lp->d_magic != DISKMAGIC || lp->d_magic2 != DISKMAGIC) {
    464 		printf("No disklabel to show.\n");
    465 		return;
    466 	}
    467 
    468 	if (lp->d_npartitions > MAXPARTITIONS || dkcksum(lp) != 0) {
    469 		printf("Corrupted disklabel.\n");
    470 		return;
    471 	}
    472 
    473 	printf("\ndisk type %d (%s), %s: %s%s%s\n", lp->d_type,
    474 	    lp->d_type < DKMAXTYPES ? dktypenames[lp->d_type] :
    475 	    dktypenames[0], lp->d_typename,
    476 	    (lp->d_flags & D_REMOVABLE) ? " removable" : "",
    477 	    (lp->d_flags & D_ECC) ? " ecc" : "",
    478 	    (lp->d_flags & D_BADSECT) ? " badsect" : "");
    479 
    480 	printf("interleave %d, rpm %d, trackskew %d, cylinderskew %d\n",
    481 	    lp->d_interleave, lp->d_rpm, lp->d_trackskew, lp->d_cylskew);
    482 
    483 	printf("headswitch %d, track-to-track %d, drivedata: %d %d %d %d %d\n",
    484 	    lp->d_headswitch, lp->d_trkseek, lp->d_drivedata[0],
    485 	    lp->d_drivedata[1], lp->d_drivedata[2], lp->d_drivedata[3],
    486 	    lp->d_drivedata[4]);
    487 
    488 	printf("\nbytes/sector: %d\n", lp->d_secsize);
    489 	printf("sectors/track: %d\n", lp->d_nsectors);
    490 	printf("tracks/cylinder: %d\n", lp->d_ntracks);
    491 	printf("sectors/cylinder: %d\n", lp->d_secpercyl);
    492 	printf("cylinders: %d\n", lp->d_ncylinders);
    493 	printf("total sectors: %d\n", lp->d_secperunit);
    494 
    495 	printf("\n%d partitions:\n", lp->d_npartitions);
    496 	printf("     size   offset\n");
    497 	pp = lp->d_partitions;
    498 	for (i = 0; i < lp->d_npartitions; i++) {
    499 		printf("%c:   %d,    %d\n", 97 + i, lp->d_partitions[i].p_size,
    500 		    lp->d_partitions[i].p_offset);
    501 	}
    502 	printf("\n");
    503 }
    504 
    505 int
    506 disklabel_write(block, len, ofp)
    507 	char *block;
    508 	int len;
    509 	struct open_file *ofp;
    510 {
    511 	int error = 0;
    512 	size_t xfersize;
    513 
    514 	if ((error = (*ofp->f_dev->dv_strategy)(ofp->f_devdata, F_WRITE,
    515 	    LABELSECTOR, len, block, &xfersize)) != 0)
    516 		printf("cannot write disklabel, errno = %d\n", error);
    517 
    518 	return (error);
    519 }
    520 
    521 int
    522 opendisk(question, diskname, len, partition, fdp)
    523 	char *question, *diskname;
    524 	int len;
    525 	char partition;
    526 	int *fdp;
    527 {
    528 	char fulldiskname[64];
    529 	int i;
    530 
    531  getdiskname:
    532 	printf("%s ", question);
    533 	memset(diskname, 0, len);
    534 	memset(fulldiskname, 0, sizeof(fulldiskname));
    535 	gets(diskname);
    536 	if (diskname[0] == '\n' || diskname[0] == '\0')
    537 		goto getdiskname;
    538 
    539 	/*
    540 	 * devopen() is picky.  Make sure it gets the sort of string it
    541 	 * wants.
    542 	 */
    543 	memcpy(fulldiskname, diskname,
    544 	    len < sizeof(fulldiskname) ? len : sizeof(fulldiskname));
    545 	for (i = 0; fulldiskname[i + 1] != '\0'; ++i)
    546 		/* Nothing. */ ;
    547 	if (fulldiskname[i] < '0' || fulldiskname[i] > '9') {
    548 		printf("invalid disk name %s\n", diskname);
    549 		goto getdiskname;
    550 	}
    551 	fulldiskname[++i] = partition; fulldiskname[++i] = ':';
    552 
    553 	/*
    554 	 * We always open for writing.
    555 	 */
    556 	if ((*fdp = open(fulldiskname, 1)) < 0) {
    557 		printf("cannot open %s\n", diskname);
    558 		return 1;
    559 	}
    560 
    561 	return 0;
    562 }
    563 
    564 /*
    565  * Copy a miniroot image from an NFS server or tape to the `b' partition
    566  * of the specified disk.  Note, this assumes 512 byte sectors.
    567  */
    568 void
    569 miniroot()
    570 {
    571 	int sfd, dfd, i, nblks;
    572 	char diskname[64], minirootname[128];
    573 	char block[DEV_BSIZE];
    574 	char tapename[64];
    575 	int fileno, ignoreshread, eof, len;
    576 	struct stat st;
    577 	size_t xfersize;
    578 	struct open_file *disk_ofp;
    579 	extern struct open_file files[];
    580 
    581 	/* Error message printed by opendisk() */
    582 	if (opendisk("Disk for miniroot?", diskname, sizeof(diskname),
    583 	    'b', &dfd))
    584 		return;
    585 
    586 	disk_ofp = &files[dfd];
    587 
    588  getsource:
    589 	printf("Source? (N)FS, (t)ape, (d)one > ");
    590 	memset(line, 0, sizeof(line));
    591 	gets(line);
    592 	if (line[0] == '\0')
    593 		goto getsource;
    594 
    595 	switch (line[0]) {
    596 	case 'n':
    597 	case 'N':
    598  name_of_nfs_miniroot:
    599 		printf("Name of miniroot file? ");
    600 		memset(line, 0, sizeof(line));
    601 		memset(minirootname, 0, sizeof(minirootname));
    602 		gets(line);
    603 		if (line[0] == '\0')
    604 			goto name_of_nfs_miniroot;
    605 		(void)strcat(minirootname, "le0a:");
    606 		(void)strcat(minirootname, line);
    607 		if ((sfd = open(minirootname, 0)) < 0) {
    608 			printf("can't open %s\n", line);
    609 			return;
    610 		}
    611 
    612 		/*
    613 		 * Find out how big the miniroot is... we can't
    614 		 * check for size because it may be compressed.
    615 		 */
    616 		ignoreshread = 1;
    617 		if (fstat(sfd, &st) < 0) {
    618 			printf("can't stat %s\n", line);
    619 			goto done;
    620 		}
    621 		nblks = (int)(st.st_size / sizeof(block));
    622 
    623 		printf("Copying miniroot from %s to %s...", line,
    624 		    diskname);
    625 		break;
    626 
    627 	case 't':
    628 	case 'T':
    629  name_of_tape_miniroot:
    630 		printf("Which tape device? ");
    631 		memset(line, 0, sizeof(line));
    632 		memset(minirootname, 0, sizeof(minirootname));
    633 		memset(tapename, 0, sizeof(tapename));
    634 		gets(line);
    635 		if (line[0] == '\0')
    636 			goto name_of_tape_miniroot;
    637 		strcat(minirootname, line);
    638 		strcat(tapename, line);
    639 
    640 		printf("File number (first == 1)? ");
    641 		memset(line, 0, sizeof(line));
    642 		gets(line);
    643 		fileno = a2int(line);
    644 		if (fileno < 1 || fileno > 8) {
    645 			printf("Invalid file number: %s\n", line);
    646 			goto getsource;
    647 		}
    648 		for (i = 0; i < sizeof(minirootname); ++i) {
    649 			if (minirootname[i] == '\0')
    650 				break;
    651 		}
    652 		if (i == sizeof(minirootname) ||
    653 		    (sizeof(minirootname) - i) < 8) {
    654 			printf("Invalid device name: %s\n", tapename);
    655 			goto getsource;
    656 		}
    657 		minirootname[i++] = 'a' + (fileno - 1);
    658 		minirootname[i++] = ':';
    659 		strcat(minirootname, "XXX");	/* lameness in open() */
    660 
    661 		ignoreshread = 0;
    662 		printf("Copy how many %d byte blocks? ", DEV_BSIZE);
    663 		memset(line, 0, sizeof(line));
    664 		gets(line);
    665 		nblks = a2int(line);
    666 		if (nblks < 0) {
    667 			printf("Invalid block count: %s\n", line);
    668 			goto getsource;
    669 		} else if (nblks == 0) {
    670 			printf("Zero blocks?  Ok, aborting.\n");
    671 			return;
    672 		}
    673 
    674 		if ((sfd = open(minirootname, 0)) < 0) {
    675 			printf("can't open %s file %c\n", tapename, fileno);
    676 			return;
    677 		}
    678 
    679 		printf("Copying %s file %d to %s...", tapename, fileno,
    680 		    diskname);
    681 		break;
    682 
    683 	case 'd':
    684 	case 'D':
    685 		return;
    686 
    687 	default:
    688 		printf("Unknown source: %s\n", line);
    689 		goto getsource;
    690 	}
    691 
    692 	/*
    693 	 * Copy loop...
    694 	 * This is fairly slow... if someone wants to speed it
    695 	 * up, they'll get no complaints from me.
    696 	 */
    697 	for (i = 0, eof = 0; i < nblks || ignoreshread == 0; i++) {
    698 		if ((len = read(sfd, block, sizeof(block))) < 0) {
    699 			printf("Read error, errno = %d\n", errno);
    700 			goto out;
    701 		}
    702 
    703 		/*
    704 		 * Check for end-of-file.
    705 		 */
    706 		if (len == 0)
    707 			goto done;
    708 		else if (len < sizeof(block))
    709 			eof = 1;
    710 
    711 		if ((*disk_ofp->f_dev->dv_strategy)(disk_ofp->f_devdata,
    712 		    F_WRITE, i, len, block, &xfersize) || xfersize != len) {
    713 			printf("Bad write at block %d, errno = %d\n",
    714 			    i, errno);
    715 			goto out;
    716 		}
    717 
    718 		if (eof)
    719 			goto done;
    720 	}
    721  done:
    722 	printf("done\n");
    723 
    724 	printf("Successfully copied miniroot image.\n");
    725 
    726  out:
    727 	close(sfd);
    728 	close(dfd);
    729 }
    730 
    731 /*
    732  * Boot the kernel from the miniroot image into single-user.
    733  */
    734 void
    735 bootmini()
    736 {
    737 	char diskname[64], bootname[64];
    738 	int i;
    739 
    740  getdiskname:
    741 	printf("Disk to boot from? ");
    742 	memset(diskname, 0, sizeof(diskname));
    743 	memset(bootname, 0, sizeof(bootname));
    744 	gets(diskname);
    745 	if (diskname[0] == '\n' || diskname[0] == '\0')
    746 		goto getdiskname;
    747 
    748 	/*
    749 	 * devopen() is picky.  Make sure it gets the sort of string it
    750 	 * wants.
    751 	 */
    752 	(void)strcat(bootname, diskname);
    753 	for (i = 0; bootname[i + 1] != '\0'; ++i)
    754 		/* Nothing. */ ;
    755 	if (bootname[i] < '0' || bootname[i] > '9') {
    756 		printf("invalid disk name %s\n", diskname);
    757 		goto getdiskname;
    758 	}
    759 	bootname[++i] = 'b'; bootname[++i] = ':';
    760 	(void)strcat(bootname, kernel_name);
    761 
    762 	howto = RB_SINGLE;	/* _Always_ */
    763 
    764 	printf("booting: %s -s\n", bootname);
    765 	exec_hp300(bootname, (u_long)lowram, howto);
    766 	printf("boot: %s\n", strerror(errno));
    767 }
    768 
    769 /*
    770  * Reset the system.
    771  */
    772 void
    773 resetsys()
    774 {
    775 
    776 	call_req_reboot();
    777 	printf("panic: can't reboot, halting\n");
    778 	asm("stop #0x2700");
    779 }
    780 
    781 /*
    782  * XXX Should have a generic atoi for libkern/libsa.
    783  */
    784 int
    785 a2int(cp)
    786 	char *cp;
    787 {
    788 	int i = 0;
    789 
    790 	if (*cp == '\0')
    791 		return (-1);
    792 
    793 	while (*cp != '\0')
    794 		i = i * 10 + *cp++ - '0';
    795 	return (i);
    796 }
    797