xref: /netbsd-src/sys/kern/kern_subr.c (revision 4472dbe5e3bd91ef2540bada7a7ca7384627ff9b)
1 /*	$NetBSD: kern_subr.c,v 1.70 2000/05/31 06:18:03 enami Exp $	*/
2 
3 /*-
4  * Copyright (c) 1997, 1998, 1999 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 of the Numerical Aerospace Simulation Facility,
9  * NASA Ames Research Center, and by Luke Mewburn.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *	This product includes software developed by the NetBSD
22  *	Foundation, Inc. and its contributors.
23  * 4. Neither the name of The NetBSD Foundation nor the names of its
24  *    contributors may be used to endorse or promote products derived
25  *    from this software without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37  * POSSIBILITY OF SUCH DAMAGE.
38  */
39 
40 /*
41  * Copyright (c) 1982, 1986, 1991, 1993
42  *	The Regents of the University of California.  All rights reserved.
43  * (c) UNIX System Laboratories, Inc.
44  * All or some portions of this file are derived from material licensed
45  * to the University of California by American Telephone and Telegraph
46  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
47  * the permission of UNIX System Laboratories, Inc.
48  *
49  * Copyright (c) 1992, 1993
50  *	The Regents of the University of California.  All rights reserved.
51  *
52  * This software was developed by the Computer Systems Engineering group
53  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
54  * contributed to Berkeley.
55  *
56  * All advertising materials mentioning features or use of this software
57  * must display the following acknowledgement:
58  *	This product includes software developed by the University of
59  *	California, Lawrence Berkeley Laboratory.
60  *
61  * Redistribution and use in source and binary forms, with or without
62  * modification, are permitted provided that the following conditions
63  * are met:
64  * 1. Redistributions of source code must retain the above copyright
65  *    notice, this list of conditions and the following disclaimer.
66  * 2. Redistributions in binary form must reproduce the above copyright
67  *    notice, this list of conditions and the following disclaimer in the
68  *    documentation and/or other materials provided with the distribution.
69  * 3. All advertising materials mentioning features or use of this software
70  *    must display the following acknowledgement:
71  *	This product includes software developed by the University of
72  *	California, Berkeley and its contributors.
73  * 4. Neither the name of the University nor the names of its contributors
74  *    may be used to endorse or promote products derived from this software
75  *    without specific prior written permission.
76  *
77  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
78  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
79  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
80  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
81  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
82  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
83  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
84  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
85  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
86  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
87  * SUCH DAMAGE.
88  *
89  *	@(#)kern_subr.c	8.4 (Berkeley) 2/14/95
90  */
91 
92 #include "opt_md.h"
93 
94 #include <sys/param.h>
95 #include <sys/systm.h>
96 #include <sys/proc.h>
97 #include <sys/malloc.h>
98 #include <sys/mount.h>
99 #include <sys/device.h>
100 #include <sys/reboot.h>
101 #include <sys/conf.h>
102 #include <sys/disklabel.h>
103 #include <sys/queue.h>
104 
105 #include <dev/cons.h>
106 
107 #include <net/if.h>
108 
109 /* XXX these should eventually move to subr_autoconf.c */
110 static int findblkmajor __P((const char *));
111 static const char *findblkname __P((int));
112 static struct device *finddevice __P((const char *));
113 static struct device *getdisk __P((char *, int, int, dev_t *, int));
114 static struct device *parsedisk __P((char *, int, int, dev_t *));
115 
116 int
117 uiomove(buf, n, uio)
118 	void *buf;
119 	int n;
120 	struct uio *uio;
121 {
122 	struct iovec *iov;
123 	u_int cnt;
124 	int error = 0;
125 	char *cp = buf;
126 	struct proc *p = uio->uio_procp;
127 
128 #ifdef DIAGNOSTIC
129 	if (uio->uio_rw != UIO_READ && uio->uio_rw != UIO_WRITE)
130 		panic("uiomove: mode");
131 	if (uio->uio_segflg == UIO_USERSPACE && p != curproc)
132 		panic("uiomove proc");
133 #endif
134 	while (n > 0 && uio->uio_resid) {
135 		iov = uio->uio_iov;
136 		cnt = iov->iov_len;
137 		if (cnt == 0) {
138 			uio->uio_iov++;
139 			uio->uio_iovcnt--;
140 			continue;
141 		}
142 		if (cnt > n)
143 			cnt = n;
144 		switch (uio->uio_segflg) {
145 
146 		case UIO_USERSPACE:
147 			KDASSERT(p->p_cpu != NULL);
148 			KDASSERT(p->p_cpu == curcpu());
149 			if (p->p_cpu->ci_schedstate.spc_flags &
150 			    SPCF_SHOULDYIELD)
151 				preempt(NULL);
152 			if (uio->uio_rw == UIO_READ)
153 				error = copyout(cp, iov->iov_base, cnt);
154 			else
155 				error = copyin(iov->iov_base, cp, cnt);
156 			if (error)
157 				return (error);
158 			break;
159 
160 		case UIO_SYSSPACE:
161 			if (uio->uio_rw == UIO_READ)
162 				error = kcopy(cp, iov->iov_base, cnt);
163 			else
164 				error = kcopy(iov->iov_base, cp, cnt);
165 			if (error)
166 				return (error);
167 			break;
168 		}
169 		iov->iov_base = (caddr_t)iov->iov_base + cnt;
170 		iov->iov_len -= cnt;
171 		uio->uio_resid -= cnt;
172 		uio->uio_offset += cnt;
173 		cp += cnt;
174 		n -= cnt;
175 	}
176 	return (error);
177 }
178 
179 /*
180  * Give next character to user as result of read.
181  */
182 int
183 ureadc(c, uio)
184 	int c;
185 	struct uio *uio;
186 {
187 	struct iovec *iov;
188 
189 	if (uio->uio_resid <= 0)
190 		panic("ureadc: non-positive resid");
191 again:
192 	if (uio->uio_iovcnt <= 0)
193 		panic("ureadc: non-positive iovcnt");
194 	iov = uio->uio_iov;
195 	if (iov->iov_len <= 0) {
196 		uio->uio_iovcnt--;
197 		uio->uio_iov++;
198 		goto again;
199 	}
200 	switch (uio->uio_segflg) {
201 
202 	case UIO_USERSPACE:
203 		if (subyte(iov->iov_base, c) < 0)
204 			return (EFAULT);
205 		break;
206 
207 	case UIO_SYSSPACE:
208 		*(char *)iov->iov_base = c;
209 		break;
210 	}
211 	iov->iov_base = (caddr_t)iov->iov_base + 1;
212 	iov->iov_len--;
213 	uio->uio_resid--;
214 	uio->uio_offset++;
215 	return (0);
216 }
217 
218 /*
219  * General routine to allocate a hash table.
220  * Allocate enough memory to hold at least `elements' list-head pointers.
221  * Return a pointer to the allocated space and set *hashmask to a pattern
222  * suitable for masking a value to use as an index into the returned array.
223  */
224 void *
225 hashinit(elements, type, flags, hashmask)
226 	int elements, type, flags;
227 	u_long *hashmask;
228 {
229 	long hashsize;
230 	LIST_HEAD(generic, generic) *hashtbl;
231 	int i;
232 
233 	if (elements <= 0)
234 		panic("hashinit: bad cnt");
235 	for (hashsize = 1; hashsize < elements; hashsize <<= 1)
236 		continue;
237 	hashtbl = malloc((u_long)hashsize * sizeof(*hashtbl), type, flags);
238 	if (hashtbl == NULL)
239 		return (NULL);
240 	for (i = 0; i < hashsize; i++)
241 		LIST_INIT(&hashtbl[i]);
242 	*hashmask = hashsize - 1;
243 	return (hashtbl);
244 }
245 
246 /*
247  * Free memory from hash table previosly allocated via hashinit().
248  */
249 void
250 hashdone(hashtbl, type)
251 	void *hashtbl;
252 	int type;
253 {
254 
255 	free(hashtbl, type);
256 }
257 
258 /*
259  * "Shutdown hook" types, functions, and variables.
260  */
261 
262 struct shutdownhook_desc {
263 	LIST_ENTRY(shutdownhook_desc) sfd_list;
264 	void	(*sfd_fn) __P((void *));
265 	void	*sfd_arg;
266 };
267 
268 LIST_HEAD(, shutdownhook_desc) shutdownhook_list;
269 
270 void *
271 shutdownhook_establish(fn, arg)
272 	void (*fn) __P((void *));
273 	void *arg;
274 {
275 	struct shutdownhook_desc *ndp;
276 
277 	ndp = (struct shutdownhook_desc *)
278 	    malloc(sizeof(*ndp), M_DEVBUF, M_NOWAIT);
279 	if (ndp == NULL)
280 		return (NULL);
281 
282 	ndp->sfd_fn = fn;
283 	ndp->sfd_arg = arg;
284 	LIST_INSERT_HEAD(&shutdownhook_list, ndp, sfd_list);
285 
286 	return (ndp);
287 }
288 
289 void
290 shutdownhook_disestablish(vhook)
291 	void *vhook;
292 {
293 #ifdef DIAGNOSTIC
294 	struct shutdownhook_desc *dp;
295 
296 	for (dp = shutdownhook_list.lh_first; dp != NULL;
297 	    dp = dp->sfd_list.le_next)
298                 if (dp == vhook)
299 			break;
300 	if (dp == NULL)
301 		panic("shutdownhook_disestablish: hook not established");
302 #endif
303 
304 	LIST_REMOVE((struct shutdownhook_desc *)vhook, sfd_list);
305 	free(vhook, M_DEVBUF);
306 }
307 
308 /*
309  * Run shutdown hooks.  Should be invoked immediately before the
310  * system is halted or rebooted, i.e. after file systems unmounted,
311  * after crash dump done, etc.
312  *
313  * Each shutdown hook is removed from the list before it's run, so that
314  * it won't be run again.
315  */
316 void
317 doshutdownhooks()
318 {
319 	struct shutdownhook_desc *dp;
320 
321 	while ((dp = shutdownhook_list.lh_first) != NULL) {
322 		LIST_REMOVE(dp, sfd_list);
323 		(*dp->sfd_fn)(dp->sfd_arg);
324 #if 0
325 		/*
326 		 * Don't bother freeing the hook structure,, since we may
327 		 * be rebooting because of a memory corruption problem,
328 		 * and this might only make things worse.  It doesn't
329 		 * matter, anyway, since the system is just about to
330 		 * reboot.
331 		 */
332 		free(dp, M_DEVBUF);
333 #endif
334 	}
335 }
336 
337 /*
338  * "Power hook" types, functions, and variables.
339  */
340 
341 struct powerhook_desc {
342 	LIST_ENTRY(powerhook_desc) sfd_list;
343 	void	(*sfd_fn) __P((int, void *));
344 	void	*sfd_arg;
345 };
346 
347 LIST_HEAD(, powerhook_desc) powerhook_list;
348 
349 void *
350 powerhook_establish(fn, arg)
351 	void (*fn) __P((int, void *));
352 	void *arg;
353 {
354 	struct powerhook_desc *ndp;
355 
356 	ndp = (struct powerhook_desc *)
357 	    malloc(sizeof(*ndp), M_DEVBUF, M_NOWAIT);
358 	if (ndp == NULL)
359 		return (NULL);
360 
361 	ndp->sfd_fn = fn;
362 	ndp->sfd_arg = arg;
363 	LIST_INSERT_HEAD(&powerhook_list, ndp, sfd_list);
364 
365 	return (ndp);
366 }
367 
368 void
369 powerhook_disestablish(vhook)
370 	void *vhook;
371 {
372 #ifdef DIAGNOSTIC
373 	struct powerhook_desc *dp;
374 
375 	for (dp = powerhook_list.lh_first; dp != NULL;
376 	    dp = dp->sfd_list.le_next)
377                 if (dp == vhook)
378 			break;
379 	if (dp == NULL)
380 		panic("powerhook_disestablish: hook not established");
381 #endif
382 
383 	LIST_REMOVE((struct powerhook_desc *)vhook, sfd_list);
384 	free(vhook, M_DEVBUF);
385 }
386 
387 /*
388  * Run power hooks.
389  */
390 void
391 dopowerhooks(why)
392 	int why;
393 {
394 	struct powerhook_desc *dp;
395 
396 	for (dp = LIST_FIRST(&powerhook_list);
397 	     dp != NULL;
398 	     dp = LIST_NEXT(dp, sfd_list)) {
399 		(*dp->sfd_fn)(why, dp->sfd_arg);
400 	}
401 }
402 
403 /*
404  * "Mountroot hook" types, functions, and variables.
405  */
406 
407 struct mountroothook_desc {
408 	LIST_ENTRY(mountroothook_desc) mrd_list;
409 	struct	device *mrd_device;
410 	void 	(*mrd_func) __P((struct device *));
411 };
412 
413 LIST_HEAD(, mountroothook_desc) mountroothook_list;
414 
415 void *
416 mountroothook_establish(func, dev)
417 	void (*func) __P((struct device *));
418 	struct device *dev;
419 {
420 	struct mountroothook_desc *mrd;
421 
422 	mrd = (struct mountroothook_desc *)
423 	    malloc(sizeof(*mrd), M_DEVBUF, M_NOWAIT);
424 	if (mrd == NULL)
425 		return (NULL);
426 
427 	mrd->mrd_device = dev;
428 	mrd->mrd_func = func;
429 	LIST_INSERT_HEAD(&mountroothook_list, mrd, mrd_list);
430 
431 	return (mrd);
432 }
433 
434 void
435 mountroothook_disestablish(vhook)
436 	void *vhook;
437 {
438 #ifdef DIAGNOSTIC
439 	struct mountroothook_desc *mrd;
440 
441 	for (mrd = mountroothook_list.lh_first; mrd != NULL;
442 	    mrd = mrd->mrd_list.le_next)
443                 if (mrd == vhook)
444 			break;
445 	if (mrd == NULL)
446 		panic("mountroothook_disestablish: hook not established");
447 #endif
448 
449 	LIST_REMOVE((struct mountroothook_desc *)vhook, mrd_list);
450 	free(vhook, M_DEVBUF);
451 }
452 
453 void
454 mountroothook_destroy()
455 {
456 	struct mountroothook_desc *mrd;
457 
458 	while ((mrd = mountroothook_list.lh_first) != NULL) {
459 		LIST_REMOVE(mrd, mrd_list);
460 		free(mrd, M_DEVBUF);
461 	}
462 }
463 
464 void
465 domountroothook()
466 {
467 	struct mountroothook_desc *mrd;
468 
469 	for (mrd = mountroothook_list.lh_first; mrd != NULL;
470 	    mrd = mrd->mrd_list.le_next) {
471 		if (mrd->mrd_device == root_device) {
472 			(*mrd->mrd_func)(root_device);
473 			return;
474 		}
475 	}
476 }
477 
478 /*
479  * Exec hook code.
480  */
481 
482 struct exechook_desc {
483 	LIST_ENTRY(exechook_desc) ehk_list;
484 	void	(*ehk_fn) __P((struct proc *, void *));
485 	void	*ehk_arg;
486 };
487 
488 LIST_HEAD(, exechook_desc) exechook_list;
489 
490 void *
491 exechook_establish(fn, arg)
492 	void (*fn) __P((struct proc *, void *));
493 	void *arg;
494 {
495 	struct exechook_desc *edp;
496 
497 	edp = (struct exechook_desc *)
498 	    malloc(sizeof(*edp), M_DEVBUF, M_NOWAIT);
499 	if (edp == NULL)
500 		return (NULL);
501 
502 	edp->ehk_fn = fn;
503 	edp->ehk_arg = arg;
504 	LIST_INSERT_HEAD(&exechook_list, edp, ehk_list);
505 
506 	return (edp);
507 }
508 
509 void
510 exechook_disestablish(vhook)
511 	void *vhook;
512 {
513 #ifdef DIAGNOSTIC
514 	struct exechook_desc *edp;
515 
516 	for (edp = exechook_list.lh_first; edp != NULL;
517 	    edp = edp->ehk_list.le_next)
518                 if (edp == vhook)
519 			break;
520 	if (edp == NULL)
521 		panic("exechook_disestablish: hook not established");
522 #endif
523 
524 	LIST_REMOVE((struct exechook_desc *)vhook, ehk_list);
525 	free(vhook, M_DEVBUF);
526 }
527 
528 /*
529  * Run exec hooks.
530  */
531 void
532 doexechooks(p)
533 	struct proc *p;
534 {
535 	struct exechook_desc *edp;
536 
537 	for (edp = LIST_FIRST(&exechook_list);
538 	     edp != NULL;
539 	     edp = LIST_NEXT(edp, ehk_list)) {
540 		(*edp->ehk_fn)(p, edp->ehk_arg);
541 	}
542 }
543 
544 /*
545  * Determine the root device and, if instructed to, the root file system.
546  */
547 
548 #include "md.h"
549 #if NMD == 0
550 #undef MEMORY_DISK_HOOKS
551 #endif
552 
553 #ifdef MEMORY_DISK_HOOKS
554 static struct device fakemdrootdev[NMD];
555 #endif
556 
557 #include "raid.h"
558 #if NRAID == 1
559 #define BOOT_FROM_RAID_HOOKS 1
560 #endif
561 
562 #ifdef BOOT_FROM_RAID_HOOKS
563 extern int numraid;
564 extern struct device *raidrootdev;
565 #endif
566 
567 void
568 setroot(bootdv, bootpartition)
569 	struct device *bootdv;
570 	int bootpartition;
571 {
572 	struct device *dv;
573 	int len;
574 #ifdef MEMORY_DISK_HOOKS
575 	int i;
576 #endif
577 	dev_t nrootdev;
578 	dev_t ndumpdev = NODEV;
579 	char buf[128];
580 	const char *rootdevname;
581 	const char *dumpdevname;
582 	struct device *rootdv = NULL;		/* XXX gcc -Wuninitialized */
583 	struct device *dumpdv = NULL;
584 	struct ifnet *ifp;
585 	const char *deffsname;
586 	struct vfsops *vops;
587 	extern int (*mountroot) __P((void));
588 
589 #ifdef MEMORY_DISK_HOOKS
590 	for (i = 0; i < NMD; i++) {
591 		fakemdrootdev[i].dv_class  = DV_DISK;
592 		fakemdrootdev[i].dv_cfdata = NULL;
593 		fakemdrootdev[i].dv_unit   = i;
594 		fakemdrootdev[i].dv_parent = NULL;
595 		sprintf(fakemdrootdev[i].dv_xname, "md%d", i);
596 	}
597 #endif /* MEMORY_DISK_HOOKS */
598 
599 #ifdef MEMORY_DISK_IS_ROOT
600 	bootdv = &fakemdrootdev[0];
601 	bootpartition = 0;
602 #endif
603 
604 	/*
605 	 * If NFS is specified as the file system, and we found
606 	 * a DV_DISK boot device (or no boot device at all), then
607 	 * find a reasonable network interface for "rootspec".
608 	 */
609 	vops = vfs_getopsbyname("nfs");
610 	if (vops != NULL && vops->vfs_mountroot == mountroot &&
611 	    rootspec == NULL &&
612 	    (bootdv == NULL || bootdv->dv_class != DV_IFNET)) {
613 		for (ifp = ifnet.tqh_first; ifp != NULL;
614 		    ifp = ifp->if_list.tqe_next)
615 			if ((ifp->if_flags &
616 			     (IFF_LOOPBACK|IFF_POINTOPOINT)) == 0)
617 				break;
618 		if (ifp == NULL) {
619 			/*
620 			 * Can't find a suitable interface; ask the
621 			 * user.
622 			 */
623 			boothowto |= RB_ASKNAME;
624 		} else {
625 			/*
626 			 * Have a suitable interface; behave as if
627 			 * the user specified this interface.
628 			 */
629 			rootspec = (const char *)ifp->if_xname;
630 		}
631 	}
632 
633 	/*
634 	 * If wildcarded root and we the boot device wasn't determined,
635 	 * ask the user.
636 	 */
637 	if (rootspec == NULL && bootdv == NULL)
638 		boothowto |= RB_ASKNAME;
639 
640  top:
641 	if (boothowto & RB_ASKNAME) {
642 		struct device *defdumpdv;
643 
644 		for (;;) {
645 			printf("root device");
646 			if (bootdv != NULL) {
647 				printf(" (default %s", bootdv->dv_xname);
648 				if (bootdv->dv_class == DV_DISK)
649 					printf("%c", bootpartition + 'a');
650 				printf(")");
651 			}
652 			printf(": ");
653 			len = cngetsn(buf, sizeof(buf));
654 			if (len == 0 && bootdv != NULL) {
655 				strcpy(buf, bootdv->dv_xname);
656 				len = strlen(buf);
657 			}
658 			if (len > 0 && buf[len - 1] == '*') {
659 				buf[--len] = '\0';
660 				dv = getdisk(buf, len, 1, &nrootdev, 0);
661 				if (dv != NULL) {
662 					rootdv = dv;
663 					break;
664 				}
665 			}
666 			dv = getdisk(buf, len, bootpartition, &nrootdev, 0);
667 			if (dv != NULL) {
668 				rootdv = dv;
669 				break;
670 			}
671 		}
672 
673 		/*
674 		 * Set up the default dump device.  If root is on
675 		 * a network device, there is no default dump
676 		 * device, since we don't support dumps to the
677 		 * network.
678 		 */
679 		if (rootdv->dv_class == DV_IFNET)
680 			defdumpdv = NULL;
681 		else
682 			defdumpdv = rootdv;
683 
684 		for (;;) {
685 			printf("dump device");
686 			if (defdumpdv != NULL) {
687 				/*
688 				 * Note, we know it's a disk if we get here.
689 				 */
690 				printf(" (default %sb)", defdumpdv->dv_xname);
691 			}
692 			printf(": ");
693 			len = cngetsn(buf, sizeof(buf));
694 			if (len == 0) {
695 				if (defdumpdv != NULL) {
696 					ndumpdev = MAKEDISKDEV(major(nrootdev),
697 					    DISKUNIT(nrootdev), 1);
698 				}
699 				dumpdv = defdumpdv;
700 				break;
701 			}
702 			if (len == 4 && strcmp(buf, "none") == 0) {
703 				dumpdv = NULL;
704 				break;
705 			}
706 			dv = getdisk(buf, len, 1, &ndumpdev, 1);
707 			if (dv != NULL) {
708 				dumpdv = dv;
709 				break;
710 			}
711 		}
712 
713 		rootdev = nrootdev;
714 		dumpdev = ndumpdev;
715 
716 		for (vops = LIST_FIRST(&vfs_list); vops != NULL;
717 		     vops = LIST_NEXT(vops, vfs_list)) {
718 			if (vops->vfs_mountroot != NULL &&
719 			    vops->vfs_mountroot == mountroot)
720 			break;
721 		}
722 
723 		if (vops == NULL) {
724 			mountroot = NULL;
725 			deffsname = "generic";
726 		} else
727 			deffsname = vops->vfs_name;
728 
729 		for (;;) {
730 			printf("file system (default %s): ", deffsname);
731 			len = cngetsn(buf, sizeof(buf));
732 			if (len == 0)
733 				break;
734 			if (len == 4 && strcmp(buf, "halt") == 0)
735 				cpu_reboot(RB_HALT, NULL);
736 			else if (len == 7 && strcmp(buf, "generic") == 0) {
737 				mountroot = NULL;
738 				break;
739 			}
740 			vops = vfs_getopsbyname(buf);
741 			if (vops == NULL || vops->vfs_mountroot == NULL) {
742 				printf("use one of: generic");
743 				for (vops = LIST_FIRST(&vfs_list);
744 				     vops != NULL;
745 				     vops = LIST_NEXT(vops, vfs_list)) {
746 					if (vops->vfs_mountroot != NULL)
747 						printf(" %s", vops->vfs_name);
748 				}
749 				printf(" halt\n");
750 			} else {
751 				mountroot = vops->vfs_mountroot;
752 				break;
753 			}
754 		}
755 
756 	} else if (rootspec == NULL) {
757 		int majdev;
758 
759 		/*
760 		 * Wildcarded root; use the boot device.
761 		 */
762 		rootdv = bootdv;
763 
764 		majdev = findblkmajor(bootdv->dv_xname);
765 		if (majdev >= 0) {
766 			/*
767 			 * Root is on a disk.  `bootpartition' is root.
768 			 */
769 			rootdev = MAKEDISKDEV(majdev, bootdv->dv_unit,
770 			    bootpartition);
771 		}
772 	} else {
773 
774 		/*
775 		 * `root on <dev> ...'
776 		 */
777 
778 		/*
779 		 * If it's a network interface, we can bail out
780 		 * early.
781 		 */
782 		dv = finddevice(rootspec);
783 		if (dv != NULL && dv->dv_class == DV_IFNET) {
784 			rootdv = dv;
785 			goto haveroot;
786 		}
787 
788 		rootdevname = findblkname(major(rootdev));
789 		if (rootdevname == NULL) {
790 			printf("unknown device major 0x%x\n", rootdev);
791 			boothowto |= RB_ASKNAME;
792 			goto top;
793 		}
794 		memset(buf, 0, sizeof(buf));
795 		sprintf(buf, "%s%d", rootdevname, DISKUNIT(rootdev));
796 
797 		rootdv = finddevice(buf);
798 		if (rootdv == NULL) {
799 			printf("device %s (0x%x) not configured\n",
800 			    buf, rootdev);
801 			boothowto |= RB_ASKNAME;
802 			goto top;
803 		}
804 	}
805 
806  haveroot:
807 
808 	root_device = rootdv;
809 
810 	switch (rootdv->dv_class) {
811 	case DV_IFNET:
812 		printf("root on %s", rootdv->dv_xname);
813 		break;
814 
815 	case DV_DISK:
816 		printf("root on %s%c", rootdv->dv_xname,
817 		    DISKPART(rootdev) + 'a');
818 		break;
819 
820 	default:
821 		printf("can't determine root device\n");
822 		boothowto |= RB_ASKNAME;
823 		goto top;
824 	}
825 
826 	/*
827 	 * Now configure the dump device.
828 	 *
829 	 * If we haven't figured out the dump device, do so, with
830 	 * the following rules:
831 	 *
832 	 *	(a) We already know dumpdv in the RB_ASKNAME case.
833 	 *
834 	 *	(b) If dumpspec is set, try to use it.  If the device
835 	 *	    is not available, punt.
836 	 *
837 	 *	(c) If dumpspec is not set, the dump device is
838 	 *	    wildcarded or unspecified.  If the root device
839 	 *	    is DV_IFNET, punt.  Otherwise, use partition b
840 	 *	    of the root device.
841 	 */
842 
843 	if (boothowto & RB_ASKNAME) {		/* (a) */
844 		if (dumpdv == NULL)
845 			goto nodumpdev;
846 	} else if (dumpspec != NULL) {		/* (b) */
847 		if (strcmp(dumpspec, "none") == 0 || dumpdev == NODEV) {
848 			/*
849 			 * Operator doesn't want a dump device.
850 			 * Or looks like they tried to pick a network
851 			 * device.  Oops.
852 			 */
853 			goto nodumpdev;
854 		}
855 
856 		dumpdevname = findblkname(major(dumpdev));
857 		if (dumpdevname == NULL)
858 			goto nodumpdev;
859 		memset(buf, 0, sizeof(buf));
860 		sprintf(buf, "%s%d", dumpdevname, DISKUNIT(dumpdev));
861 
862 		dumpdv = finddevice(buf);
863 		if (dumpdv == NULL) {
864 			/*
865 			 * Device not configured.
866 			 */
867 			goto nodumpdev;
868 		}
869 	} else {				/* (c) */
870 		if (rootdv->dv_class == DV_IFNET)
871 			goto nodumpdev;
872 		else {
873 			dumpdv = rootdv;
874 			dumpdev = MAKEDISKDEV(major(rootdev),
875 			    dumpdv->dv_unit, 1);
876 		}
877 	}
878 
879 	printf(" dumps on %s%c\n", dumpdv->dv_xname, DISKPART(dumpdev) + 'a');
880 	return;
881 
882  nodumpdev:
883 	dumpdev = NODEV;
884 	printf("\n");
885 }
886 
887 static int
888 findblkmajor(name)
889 	const char *name;
890 {
891 	int i;
892 
893 	for (i = 0; dev_name2blk[i].d_name != NULL; i++)
894 		if (strncmp(name, dev_name2blk[i].d_name,
895 		    strlen(dev_name2blk[i].d_name)) == 0)
896 			return (dev_name2blk[i].d_maj);
897 	return (-1);
898 }
899 
900 const char *
901 findblkname(maj)
902 	int maj;
903 {
904 	int i;
905 
906 	for (i = 0; dev_name2blk[i].d_name != NULL; i++)
907 		if (dev_name2blk[i].d_maj == maj)
908 			return (dev_name2blk[i].d_name);
909 	return (NULL);
910 }
911 
912 static struct device *
913 finddevice(name)
914 	const char *name;
915 {
916 	struct device *dv;
917 #ifdef BOOT_FROM_RAID_HOOKS
918 	int j;
919 
920 	for (j = 0; j < numraid; j++) {
921 		if (strcmp(name, raidrootdev[j].dv_xname) == 0) {
922 			dv = &raidrootdev[j];
923 			return (dv);
924 		}
925 	}
926 #endif;
927 
928 	for (dv = TAILQ_FIRST(&alldevs); dv != NULL;
929 	    dv = TAILQ_NEXT(dv, dv_list))
930 		if (strcmp(dv->dv_xname, name) == 0)
931 			break;
932 	return (dv);
933 }
934 
935 static struct device *
936 getdisk(str, len, defpart, devp, isdump)
937 	char *str;
938 	int len, defpart;
939 	dev_t *devp;
940 	int isdump;
941 {
942 	struct device	*dv;
943 #ifdef MEMORY_DISK_HOOKS
944 	int		i;
945 #endif
946 #ifdef BOOT_FROM_RAID_HOOKS
947 	int 		j;
948 #endif
949 
950 	if ((dv = parsedisk(str, len, defpart, devp)) == NULL) {
951 		printf("use one of:");
952 #ifdef MEMORY_DISK_HOOKS
953 		if (isdump == 0)
954 			for (i = 0; i < NMD; i++)
955 				printf(" %s[a-%c]", fakemdrootdev[i].dv_xname,
956 				    'a' + MAXPARTITIONS - 1);
957 #endif
958 #ifdef BOOT_FROM_RAID_HOOKS
959 		if (isdump == 0)
960 			for (j = 0; j < numraid; j++)
961 				printf(" %s[a-%c]", raidrootdev[j].dv_xname,
962 				    'a' + MAXPARTITIONS - 1);
963 #endif
964 		for (dv = alldevs.tqh_first; dv != NULL;
965 		    dv = dv->dv_list.tqe_next) {
966 			if (dv->dv_class == DV_DISK)
967 				printf(" %s[a-%c]", dv->dv_xname,
968 				    'a' + MAXPARTITIONS - 1);
969 			if (isdump == 0 && dv->dv_class == DV_IFNET)
970 				printf(" %s", dv->dv_xname);
971 		}
972 		if (isdump)
973 			printf(" none");
974 		printf(" halt\n");
975 	}
976 	return (dv);
977 }
978 
979 static struct device *
980 parsedisk(str, len, defpart, devp)
981 	char *str;
982 	int len, defpart;
983 	dev_t *devp;
984 {
985 	struct device *dv;
986 	char *cp, c;
987 	int majdev, part;
988 #ifdef MEMORY_DISK_HOOKS
989 	int i;
990 #endif
991 	if (len == 0)
992 		return (NULL);
993 
994 	if (len == 4 && strcmp(str, "halt") == 0)
995 		cpu_reboot(RB_HALT, NULL);
996 
997 	cp = str + len - 1;
998 	c = *cp;
999 	if (c >= 'a' && c <= ('a' + MAXPARTITIONS - 1)) {
1000 		part = c - 'a';
1001 		*cp = '\0';
1002 	} else
1003 		part = defpart;
1004 
1005 #ifdef MEMORY_DISK_HOOKS
1006 	for (i = 0; i < NMD; i++)
1007 		if (strcmp(str, fakemdrootdev[i].dv_xname) == 0) {
1008 			dv = &fakemdrootdev[i];
1009 			goto gotdisk;
1010 		}
1011 #endif
1012 
1013 	dv = finddevice(str);
1014 	if (dv != NULL) {
1015 		if (dv->dv_class == DV_DISK) {
1016 #ifdef MEMORY_DISK_HOOKS
1017  gotdisk:
1018 #endif
1019 			majdev = findblkmajor(dv->dv_xname);
1020 			if (majdev < 0)
1021 				panic("parsedisk");
1022 			*devp = MAKEDISKDEV(majdev, dv->dv_unit, part);
1023 		}
1024 
1025 		if (dv->dv_class == DV_IFNET)
1026 			*devp = NODEV;
1027 	}
1028 
1029 	*cp = c;
1030 	return (dv);
1031 }
1032 
1033 /*
1034  * snprintf() `bytes' into `buf', reformatting it so that the number,
1035  * plus a possible `x' + suffix extension) fits into len bytes (including
1036  * the terminating NUL).
1037  * Returns the number of bytes stored in buf, or -1 if there was a problem.
1038  * E.g, given a len of 9 and a suffix of `B':
1039  *	bytes		result
1040  *	-----		------
1041  *	99999		`99999 B'
1042  *	100000		`97 KB'
1043  *	66715648	`65152 KB'
1044  *	252215296	`240 MB'
1045  */
1046 int
1047 humanize_number(buf, len, bytes, suffix, divisor)
1048 	char		*buf;
1049 	size_t		 len;
1050 	u_int64_t	 bytes;
1051 	const char	*suffix;
1052 	int 		divisor;
1053 {
1054 		/* prefixes are: (none), Kilo, Mega, Giga, Tera, Peta, Exa */
1055 	static const char prefixes[] = " KMGTPE";
1056 
1057 	int		i, r;
1058 	u_int64_t	max;
1059 	size_t		suffixlen;
1060 
1061 	if (buf == NULL || suffix == NULL)
1062 		return (-1);
1063 	if (len > 0)
1064 		buf[0] = '\0';
1065 	suffixlen = strlen(suffix);
1066 			/* check if enough room for `x y' + suffix + `\0' */
1067 	if (len < 4 + suffixlen)
1068 		return (-1);
1069 
1070 	max = 1;
1071 	for (i = 0; i < len - suffixlen - 3; i++)
1072 		max *= 10;
1073 	for (i = 0; bytes >= max && i < sizeof(prefixes); i++)
1074 		bytes /= divisor;
1075 
1076 	r = snprintf(buf, len, "%qu%s%c%s", (unsigned long long)bytes,
1077 	    i == 0 ? "" : " ", prefixes[i], suffix);
1078 
1079 	return (r);
1080 }
1081 
1082 int
1083 format_bytes(buf, len, bytes)
1084 	char		*buf;
1085 	size_t		 len;
1086 	u_int64_t	 bytes;
1087 {
1088 	int	rv;
1089 	size_t	nlen;
1090 
1091 	rv = humanize_number(buf, len, bytes, "B", 1024);
1092 	if (rv != -1) {
1093 			/* nuke the trailing ` B' if it exists */
1094 		nlen = strlen(buf) - 2;
1095 		if (strcmp(&buf[nlen], " B") == 0)
1096 			buf[nlen] = '\0';
1097 	}
1098 	return (rv);
1099 }
1100