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