1/* $NetBSD: kern_subr.c,v 1.217 2016/05/12 02:24:16 ozaki-r Exp $ */
2
3/*-
4 * Copyright (c) 1997, 1998, 1999, 2002, 2007, 2008 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 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33/*
34 * Copyright (c) 1982, 1986, 1991, 1993
35 * The Regents of the University of California. All rights reserved.
36 * (c) UNIX System Laboratories, Inc.
37 * All or some portions of this file are derived from material licensed
38 * to the University of California by American Telephone and Telegraph
39 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
40 * the permission of UNIX System Laboratories, Inc.
41 *
42 * Copyright (c) 1992, 1993
43 * The Regents of the University of California. All rights reserved.
44 *
45 * This software was developed by the Computer Systems Engineering group
46 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
47 * contributed to Berkeley.
48 *
49 * All advertising materials mentioning features or use of this software
50 * must display the following acknowledgement:
51 * This product includes software developed by the University of
52 * California, Lawrence Berkeley Laboratory.
53 *
54 * Redistribution and use in source and binary forms, with or without
55 * modification, are permitted provided that the following conditions
56 * are met:
57 * 1. Redistributions of source code must retain the above copyright
58 * notice, this list of conditions and the following disclaimer.
59 * 2. Redistributions in binary form must reproduce the above copyright
60 * notice, this list of conditions and the following disclaimer in the
61 * documentation and/or other materials provided with the distribution.
62 * 3. Neither the name of the University nor the names of its contributors
63 * may be used to endorse or promote products derived from this software
64 * without specific prior written permission.
65 *
66 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
67 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
68 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
69 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
70 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
71 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
72 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
73 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
74 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
75 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
76 * SUCH DAMAGE.
77 *
78 * @(#)kern_subr.c 8.4 (Berkeley) 2/14/95
79 */
80
81#include <sys/cdefs.h>
82__KERNEL_RCSID(0, "$NetBSD: kern_subr.c,v 1.217 2016/05/12 02:24:16 ozaki-r Exp $");
83
84#include "opt_ddb.h"
85#include "opt_md.h"
86#include "opt_tftproot.h"
87
88#include <sys/param.h>
89#include <sys/systm.h>
90#include <sys/proc.h>
91#include <sys/mount.h>
92#include <sys/device.h>
93#include <sys/reboot.h>
94#include <sys/conf.h>
95#include <sys/disk.h>
96#include <sys/disklabel.h>
97#include <sys/queue.h>
98#include <sys/fcntl.h>
99#include <sys/kauth.h>
100#include <sys/stat.h>
101#include <sys/vnode.h>
102#include <sys/module.h>
103
104#include <dev/cons.h>
105
106#include <net/if.h>
107
108/* XXX these should eventually move to subr_autoconf.c */
109static device_t finddevice(const char *);
110static device_t getdisk(char *, int, int, dev_t *, int);
111static device_t parsedisk(char *, int, int, dev_t *);
112static const char *getwedgename(const char *, int);
113
114#ifdef TFTPROOT
115int tftproot_dhcpboot(device_t);
116#endif
117
118dev_t dumpcdev; /* for savecore */
119
120static int
121isswap(device_t dv)
122{
123 struct dkwedge_info wi;
124 struct vnode *vn;
125 int error;
126
127 if (device_class(dv) != DV_DISK || !device_is_a(dv, "dk"))
128 return 0;
129
130 if ((vn = opendisk(dv)) == NULL)
131 return 0;
132
133 error = VOP_IOCTL(vn, DIOCGWEDGEINFO, &wi, FREAD, NOCRED);
134 VOP_CLOSE(vn, FREAD, NOCRED);
135 vput(vn);
136 if (error) {
137#ifdef DEBUG_WEDGE
138 printf("%s: Get wedge info returned %d\n", device_xname(dv), error);
139#endif
140 return 0;
141 }
142 return strcmp(wi.dkw_ptype, DKW_PTYPE_SWAP) == 0;
143}
144
145/*
146 * Determine the root device and, if instructed to, the root file system.
147 */
148
149#ifdef MEMORY_DISK_IS_ROOT
150int md_is_root = 1;
151#else
152int md_is_root = 0;
153#endif
154
155/*
156 * The device and partition that we booted from.
157 */
158device_t booted_device;
159int booted_partition;
160daddr_t booted_startblk;
161uint64_t booted_nblks;
162char *bootspec;
163
164/*
165 * Use partition letters if it's a disk class but not a wedge.
166 * XXX Check for wedge is kinda gross.
167 */
168#define DEV_USES_PARTITIONS(dv) \
169 (device_class((dv)) == DV_DISK && \
170 !device_is_a((dv), "dk"))
171
172void
173setroot(device_t bootdv, int bootpartition)
174{
175 device_t dv;
176 deviter_t di;
177 int len, majdev;
178 dev_t nrootdev;
179 dev_t ndumpdev = NODEV;
180 char buf[128];
181 const char *rootdevname;
182 const char *dumpdevname;
183 device_t rootdv = NULL; /* XXX gcc -Wuninitialized */
184 device_t dumpdv = NULL;
185 struct ifnet *ifp;
186 const char *deffsname;
187 struct vfsops *vops;
188
189#ifdef TFTPROOT
190 if (tftproot_dhcpboot(bootdv) != 0)
191 boothowto |= RB_ASKNAME;
192#endif
193
194 /*
195 * For root on md0 we have to force the attachment of md0.
196 */
197 if (md_is_root) {
198 int md_major;
199 dev_t md_dev;
200
201 bootdv = NULL;
202 md_major = devsw_name2blk("md", NULL, 0);
203 if (md_major >= 0) {
204 md_dev = MAKEDISKDEV(md_major, 0, RAW_PART);
205 if (bdev_open(md_dev, FREAD, S_IFBLK, curlwp) == 0)
206 bootdv = device_find_by_xname("md0");
207 }
208 if (bootdv == NULL)
209 panic("Cannot open \"md0\" (root)");
210 }
211
212 /*
213 * Let bootcode augment "rootspec".
214 */
215 if (rootspec == NULL)
216 rootspec = bootspec;
217
218 /*
219 * If NFS is specified as the file system, and we found
220 * a DV_DISK boot device (or no boot device at all), then
221 * find a reasonable network interface for "rootspec".
222 */
223 vops = vfs_getopsbyname(MOUNT_NFS);
224 if (vops != NULL && strcmp(rootfstype, MOUNT_NFS) == 0 &&
225 rootspec == NULL &&
226 (bootdv == NULL || device_class(bootdv) != DV_IFNET)) {
227 int s = pserialize_read_enter();
228 IFNET_READER_FOREACH(ifp) {
229 if ((ifp->if_flags &
230 (IFF_LOOPBACK|IFF_POINTOPOINT)) == 0)
231 break;
232 }
233 if (ifp == NULL) {
234 /*
235 * Can't find a suitable interface; ask the
236 * user.
237 */
238 boothowto |= RB_ASKNAME;
239 } else {
240 /*
241 * Have a suitable interface; behave as if
242 * the user specified this interface.
243 */
244 rootspec = (const char *)ifp->if_xname;
245 }
246 pserialize_read_exit(s);
247 }
248 if (vops != NULL)
249 vfs_delref(vops);
250
251 /*
252 * If wildcarded root and we the boot device wasn't determined,
253 * ask the user.
254 */
255 if (rootspec == NULL && bootdv == NULL)
256 boothowto |= RB_ASKNAME;
257
258 top:
259 if (boothowto & RB_ASKNAME) {
260 device_t defdumpdv;
261
262 for (;;) {
263 printf("root device");
264 if (bootdv != NULL) {
265 printf(" (default %s", device_xname(bootdv));
266 if (DEV_USES_PARTITIONS(bootdv))
267 printf("%c", bootpartition + 'a');
268 printf(")");
269 }
270 printf(": ");
271 len = cngetsn(buf, sizeof(buf));
272 if (len == 0 && bootdv != NULL) {
273 strlcpy(buf, device_xname(bootdv), sizeof(buf));
274 len = strlen(buf);
275 }
276 if (len > 0 && buf[len - 1] == '*') {
277 buf[--len] = '\0';
278 dv = getdisk(buf, len, 1, &nrootdev, 0);
279 if (dv != NULL) {
280 rootdv = dv;
281 break;
282 }
283 }
284 dv = getdisk(buf, len, bootpartition, &nrootdev, 0);
285 if (dv != NULL) {
286 rootdv = dv;
287 break;
288 }
289 }
290
291 /*
292 * Set up the default dump device. If root is on
293 * a network device, there is no default dump
294 * device, since we don't support dumps to the
295 * network.
296 */
297 if (DEV_USES_PARTITIONS(rootdv) == 0)
298 defdumpdv = NULL;
299 else
300 defdumpdv = rootdv;
301
302 for (;;) {
303 printf("dump device");
304 if (defdumpdv != NULL) {
305 /*
306 * Note, we know it's a disk if we get here.
307 */
308 printf(" (default %sb)", device_xname(defdumpdv));
309 }
310 printf(": ");
311 len = cngetsn(buf, sizeof(buf));
312 if (len == 0) {
313 if (defdumpdv != NULL) {
314 ndumpdev = MAKEDISKDEV(major(nrootdev),
315 DISKUNIT(nrootdev), 1);
316 }
317 dumpdv = defdumpdv;
318 break;
319 }
320 if (len == 4 && strcmp(buf, "none") == 0) {
321 dumpdv = NULL;
322 break;
323 }
324 dv = getdisk(buf, len, 1, &ndumpdev, 1);
325 if (dv != NULL) {
326 dumpdv = dv;
327 break;
328 }
329 }
330
331 rootdev = nrootdev;
332 dumpdev = ndumpdev;
333
334 for (vops = LIST_FIRST(&vfs_list); vops != NULL;
335 vops = LIST_NEXT(vops, vfs_list)) {
336 if (vops->vfs_mountroot != NULL &&
337 strcmp(rootfstype, vops->vfs_name) == 0)
338 break;
339 }
340
341 if (vops == NULL) {
342 deffsname = "generic";
343 } else
344 deffsname = vops->vfs_name;
345
346 for (;;) {
347 printf("file system (default %s): ", deffsname);
348 len = cngetsn(buf, sizeof(buf));
349 if (len == 0) {
350 if (strcmp(deffsname, "generic") == 0)
351 rootfstype = ROOT_FSTYPE_ANY;
352 break;
353 }
354 if (len == 4 && strcmp(buf, "halt") == 0)
355 cpu_reboot(RB_HALT, NULL);
356 else if (len == 6 && strcmp(buf, "reboot") == 0)
357 cpu_reboot(0, NULL);
358#if defined(DDB)
359 else if (len == 3 && strcmp(buf, "ddb") == 0) {
360 console_debugger();
361 }
362#endif
363 else if (len == 7 && strcmp(buf, "generic") == 0) {
364 rootfstype = ROOT_FSTYPE_ANY;
365 break;
366 }
367 vops = vfs_getopsbyname(buf);
368 if (vops == NULL || vops->vfs_mountroot == NULL) {
369 printf("use one of: generic");
370 for (vops = LIST_FIRST(&vfs_list);
371 vops != NULL;
372 vops = LIST_NEXT(vops, vfs_list)) {
373 if (vops->vfs_mountroot != NULL)
374 printf(" %s", vops->vfs_name);
375 }
376 if (vops != NULL)
377 vfs_delref(vops);
378#if defined(DDB)
379 printf(" ddb");
380#endif
381 printf(" halt reboot\n");
382 } else {
383 /*
384 * XXX If *vops gets freed between here and
385 * the call to mountroot(), rootfstype will
386 * point to something unexpected. But in
387 * this case the system will fail anyway.
388 */
389 rootfstype = vops->vfs_name;
390 vfs_delref(vops);
391 break;
392 }
393 }
394
395 } else if (rootspec == NULL) {
396 /*
397 * Wildcarded root; use the boot device.
398 */
399 rootdv = bootdv;
400
401 if (bootdv)
402 majdev = devsw_name2blk(device_xname(bootdv), NULL, 0);
403 else
404 majdev = -1;
405 if (majdev >= 0) {
406 /*
407 * Root is on a disk. `bootpartition' is root,
408 * unless the device does not use partitions.
409 */
410 if (DEV_USES_PARTITIONS(bootdv))
411 rootdev = MAKEDISKDEV(majdev,
412 device_unit(bootdv),
413 bootpartition);
414 else
415 rootdev = makedev(majdev, device_unit(bootdv));
416 }
417 } else {
418
419 /*
420 * `root on <dev> ...'
421 */
422
423 /*
424 * If it's a network interface, we can bail out
425 * early.
426 */
427 dv = finddevice(rootspec);
428 if (dv != NULL && device_class(dv) == DV_IFNET) {
429 rootdv = dv;
430 goto haveroot;
431 }
432
433 if (rootdev == NODEV &&
434 dv != NULL && device_class(dv) == DV_DISK &&
435 device_is_a(dv, "dk") &&
436 (majdev = devsw_name2blk(device_xname(dv), NULL, 0)) >= 0)
437 rootdev = makedev(majdev, device_unit(dv));
438
439 rootdevname = devsw_blk2name(major(rootdev));
440 if (rootdevname == NULL) {
441 printf("unknown device major 0x%llx\n",
442 (unsigned long long)rootdev);
443 boothowto |= RB_ASKNAME;
444 goto top;
445 }
446 memset(buf, 0, sizeof(buf));
447 snprintf(buf, sizeof(buf), "%s%llu", rootdevname,
448 (unsigned long long)DISKUNIT(rootdev));
449
450 rootdv = finddevice(buf);
451 if (rootdv == NULL) {
452 printf("device %s (0x%llx) not configured\n",
453 buf, (unsigned long long)rootdev);
454 boothowto |= RB_ASKNAME;
455 goto top;
456 }
457 }
458
459 haveroot:
460
461 root_device = rootdv;
462
463 switch (device_class(rootdv)) {
464 case DV_IFNET:
465 case DV_DISK:
466 aprint_normal("root on %s", device_xname(rootdv));
467 if (DEV_USES_PARTITIONS(rootdv))
468 aprint_normal("%c", (int)DISKPART(rootdev) + 'a');
469 break;
470
471 default:
472 printf("can't determine root device\n");
473 boothowto |= RB_ASKNAME;
474 goto top;
475 }
476
477 /*
478 * Now configure the dump device.
479 *
480 * If we haven't figured out the dump device, do so, with
481 * the following rules:
482 *
483 * (a) We already know dumpdv in the RB_ASKNAME case.
484 *
485 * (b) If dumpspec is set, try to use it. If the device
486 * is not available, punt.
487 *
488 * (c) If dumpspec is not set, the dump device is
489 * wildcarded or unspecified. If the root device
490 * is DV_IFNET, punt. Otherwise, use partition b
491 * of the root device.
492 */
493
494 if (boothowto & RB_ASKNAME) { /* (a) */
495 if (dumpdv == NULL)
496 goto nodumpdev;
497 } else if (dumpspec != NULL) { /* (b) */
498 if (strcmp(dumpspec, "none") == 0 || dumpdev == NODEV) {
499 /*
500 * Operator doesn't want a dump device.
501 * Or looks like they tried to pick a network
502 * device. Oops.
503 */
504 goto nodumpdev;
505 }
506
507 dumpdevname = devsw_blk2name(major(dumpdev));
508 if (dumpdevname == NULL)
509 goto nodumpdev;
510 memset(buf, 0, sizeof(buf));
511 snprintf(buf, sizeof(buf), "%s%llu", dumpdevname,
512 (unsigned long long)DISKUNIT(dumpdev));
513
514 dumpdv = finddevice(buf);
515 if (dumpdv == NULL) {
516 /*
517 * Device not configured.
518 */
519 goto nodumpdev;
520 }
521 } else { /* (c) */
522 if (DEV_USES_PARTITIONS(rootdv) == 0) {
523 for (dv = deviter_first(&di, DEVITER_F_ROOT_FIRST);
524 dv != NULL;
525 dv = deviter_next(&di))
526 if (isswap(dv))
527 break;
528 deviter_release(&di);
529 if (dv == NULL)
530 goto nodumpdev;
531
532 majdev = devsw_name2blk(device_xname(dv), NULL, 0);
533 if (majdev < 0)
534 goto nodumpdev;
535 dumpdv = dv;
536 dumpdev = makedev(majdev, device_unit(dumpdv));
537 } else {
538 dumpdv = rootdv;
539 dumpdev = MAKEDISKDEV(major(rootdev),
540 device_unit(dumpdv), 1);
541 }
542 }
543
544 dumpcdev = devsw_blk2chr(dumpdev);
545 aprint_normal(" dumps on %s", device_xname(dumpdv));
546 if (DEV_USES_PARTITIONS(dumpdv))
547 aprint_normal("%c", (int)DISKPART(dumpdev) + 'a');
548 aprint_normal("\n");
549 return;
550
551 nodumpdev:
552 dumpdev = NODEV;
553 dumpcdev = NODEV;
554 aprint_normal("\n");
555}
556
557static device_t
558finddevice(const char *name)
559{
560 const char *wname;
561
562 if ((wname = getwedgename(name, strlen(name))) != NULL)
563 return dkwedge_find_by_wname(wname);
564
565 return device_find_by_xname(name);
566}
567
568static device_t
569getdisk(char *str, int len, int defpart, dev_t *devp, int isdump)
570{
571 device_t dv;
572 deviter_t di;
573
574 if ((dv = parsedisk(str, len, defpart, devp)) == NULL) {
575 printf("use one of:");
576 for (dv = deviter_first(&di, DEVITER_F_ROOT_FIRST); dv != NULL;
577 dv = deviter_next(&di)) {
578 if (DEV_USES_PARTITIONS(dv))
579 printf(" %s[a-%c]", device_xname(dv),
580 'a' + MAXPARTITIONS - 1);
581 else if (device_class(dv) == DV_DISK)
582 printf(" %s", device_xname(dv));
583 if (isdump == 0 && device_class(dv) == DV_IFNET)
584 printf(" %s", device_xname(dv));
585 }
586 deviter_release(&di);
587 dkwedge_print_wnames();
588 if (isdump)
589 printf(" none");
590#if defined(DDB)
591 printf(" ddb");
592#endif
593 printf(" halt reboot\n");
594 }
595 return dv;
596}
597
598static const char *
599getwedgename(const char *name, int namelen)
600{
601 const char *wpfx = "wedge:";
602 const int wpfxlen = strlen(wpfx);
603
604 if (namelen < wpfxlen || strncmp(name, wpfx, wpfxlen) != 0)
605 return NULL;
606
607 return name + wpfxlen;
608}
609
610static device_t
611parsedisk(char *str, int len, int defpart, dev_t *devp)
612{
613 device_t dv;
614 const char *wname;
615 char *cp, c;
616 int majdev, part;
617 if (len == 0)
618 return (NULL);
619
620 if (len == 4 && strcmp(str, "halt") == 0)
621 cpu_reboot(RB_HALT, NULL);
622 else if (len == 6 && strcmp(str, "reboot") == 0)
623 cpu_reboot(0, NULL);
624#if defined(DDB)
625 else if (len == 3 && strcmp(str, "ddb") == 0)
626 console_debugger();
627#endif
628
629 cp = str + len - 1;
630 c = *cp;
631
632 if ((wname = getwedgename(str, len)) != NULL) {
633 if ((dv = dkwedge_find_by_wname(wname)) == NULL)
634 return NULL;
635 part = defpart;
636 goto gotdisk;
637 } else if (c >= 'a' && c <= ('a' + MAXPARTITIONS - 1)) {
638 part = c - 'a';
639 *cp = '\0';
640 } else
641 part = defpart;
642
643 dv = finddevice(str);
644 if (dv != NULL) {
645 if (device_class(dv) == DV_DISK) {
646 gotdisk:
647 majdev = devsw_name2blk(device_xname(dv), NULL, 0);
648 if (majdev < 0)
649 panic("parsedisk");
650 if (DEV_USES_PARTITIONS(dv))
651 *devp = MAKEDISKDEV(majdev, device_unit(dv),
652 part);
653 else
654 *devp = makedev(majdev, device_unit(dv));
655 }
656
657 if (device_class(dv) == DV_IFNET)
658 *devp = NODEV;
659 }
660
661 *cp = c;
662 return (dv);
663}
664