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