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