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