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