kern_subr.c revision 1.219 1 /* $NetBSD: kern_subr.c,v 1.219 2017/11/14 14:14:29 jmcneill 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.219 2017/11/14 14:14:29 jmcneill 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 */
109 static device_t finddevice(const char *);
110 static device_t getdisk(char *, int, int, dev_t *, int);
111 static device_t parsedisk(char *, int, int, dev_t *);
112 static const char *getwedgename(const char *, int);
113
114 #ifdef TFTPROOT
115 int tftproot_dhcpboot(device_t);
116 #endif
117
118 dev_t dumpcdev; /* for savecore */
119
120 static int
121 isswap(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
150 int md_is_root = 1;
151 #else
152 int md_is_root = 0;
153 #endif
154
155 /*
156 * The device and partition that we booted from.
157 */
158 device_t booted_device;
159 const char *booted_method;
160 int booted_partition;
161 daddr_t booted_startblk;
162 uint64_t booted_nblks;
163 char *bootspec;
164
165 /*
166 * Use partition letters if it's a disk class but not a wedge or flash.
167 * XXX Check for wedge/flash is kinda gross.
168 */
169 #define DEV_USES_PARTITIONS(dv) \
170 (device_class((dv)) == DV_DISK && \
171 !device_is_a((dv), "dk") && \
172 !device_is_a((dv), "flash"))
173
174 void
175 setroot(device_t bootdv, int bootpartition)
176 {
177 device_t dv;
178 deviter_t di;
179 int len, majdev;
180 dev_t nrootdev;
181 dev_t ndumpdev = NODEV;
182 char buf[128];
183 const char *rootdevname;
184 const char *dumpdevname;
185 device_t rootdv = NULL; /* XXX gcc -Wuninitialized */
186 device_t dumpdv = NULL;
187 struct ifnet *ifp;
188 const char *deffsname;
189 struct vfsops *vops;
190
191 #ifdef TFTPROOT
192 if (tftproot_dhcpboot(bootdv) != 0)
193 boothowto |= RB_ASKNAME;
194 #endif
195
196 /*
197 * For root on md0 we have to force the attachment of md0.
198 */
199 if (md_is_root) {
200 int md_major;
201 dev_t md_dev;
202
203 bootdv = NULL;
204 md_major = devsw_name2blk("md", NULL, 0);
205 if (md_major >= 0) {
206 md_dev = MAKEDISKDEV(md_major, 0, RAW_PART);
207 if (bdev_open(md_dev, FREAD, S_IFBLK, curlwp) == 0)
208 bootdv = device_find_by_xname("md0");
209 }
210 if (bootdv == NULL)
211 panic("Cannot open \"md0\" (root)");
212 }
213
214 /*
215 * Let bootcode augment "rootspec".
216 */
217 if (rootspec == NULL)
218 rootspec = bootspec;
219
220 /*
221 * If NFS is specified as the file system, and we found
222 * a DV_DISK boot device (or no boot device at all), then
223 * find a reasonable network interface for "rootspec".
224 */
225 vops = vfs_getopsbyname(MOUNT_NFS);
226 if (vops != NULL && strcmp(rootfstype, MOUNT_NFS) == 0 &&
227 rootspec == NULL &&
228 (bootdv == NULL || device_class(bootdv) != DV_IFNET)) {
229 int s = pserialize_read_enter();
230 IFNET_READER_FOREACH(ifp) {
231 if ((ifp->if_flags &
232 (IFF_LOOPBACK|IFF_POINTOPOINT)) == 0)
233 break;
234 }
235 if (ifp == NULL) {
236 /*
237 * Can't find a suitable interface; ask the
238 * user.
239 */
240 boothowto |= RB_ASKNAME;
241 } else {
242 /*
243 * Have a suitable interface; behave as if
244 * the user specified this interface.
245 */
246 rootspec = (const char *)ifp->if_xname;
247 }
248 pserialize_read_exit(s);
249 }
250 if (vops != NULL)
251 vfs_delref(vops);
252
253 /*
254 * If wildcarded root and we the boot device wasn't determined,
255 * ask the user.
256 */
257 if (rootspec == NULL && bootdv == NULL)
258 boothowto |= RB_ASKNAME;
259
260 top:
261 if (boothowto & RB_ASKNAME) {
262 device_t defdumpdv;
263
264 for (;;) {
265 printf("root device");
266 if (bootdv != NULL) {
267 printf(" (default %s", device_xname(bootdv));
268 if (DEV_USES_PARTITIONS(bootdv))
269 printf("%c", bootpartition + 'a');
270 printf(")");
271 }
272 printf(": ");
273 len = cngetsn(buf, sizeof(buf));
274 if (len == 0 && bootdv != NULL) {
275 strlcpy(buf, device_xname(bootdv), sizeof(buf));
276 len = strlen(buf);
277 }
278 if (len > 0 && buf[len - 1] == '*') {
279 buf[--len] = '\0';
280 dv = getdisk(buf, len, 1, &nrootdev, 0);
281 if (dv != NULL) {
282 rootdv = dv;
283 break;
284 }
285 }
286 dv = getdisk(buf, len, bootpartition, &nrootdev, 0);
287 if (dv != NULL) {
288 rootdv = dv;
289 break;
290 }
291 }
292
293 /*
294 * Set up the default dump device. If root is on
295 * a network device, there is no default dump
296 * device, since we don't support dumps to the
297 * network.
298 */
299 if (DEV_USES_PARTITIONS(rootdv) == 0)
300 defdumpdv = NULL;
301 else
302 defdumpdv = rootdv;
303
304 for (;;) {
305 printf("dump device");
306 if (defdumpdv != NULL) {
307 /*
308 * Note, we know it's a disk if we get here.
309 */
310 printf(" (default %sb)", device_xname(defdumpdv));
311 }
312 printf(": ");
313 len = cngetsn(buf, sizeof(buf));
314 if (len == 0) {
315 if (defdumpdv != NULL) {
316 ndumpdev = MAKEDISKDEV(major(nrootdev),
317 DISKUNIT(nrootdev), 1);
318 }
319 dumpdv = defdumpdv;
320 break;
321 }
322 if (len == 4 && strcmp(buf, "none") == 0) {
323 dumpdv = NULL;
324 break;
325 }
326 dv = getdisk(buf, len, 1, &ndumpdev, 1);
327 if (dv != NULL) {
328 dumpdv = dv;
329 break;
330 }
331 }
332
333 rootdev = nrootdev;
334 dumpdev = ndumpdev;
335
336 for (vops = LIST_FIRST(&vfs_list); vops != NULL;
337 vops = LIST_NEXT(vops, vfs_list)) {
338 if (vops->vfs_mountroot != NULL &&
339 strcmp(rootfstype, vops->vfs_name) == 0)
340 break;
341 }
342
343 if (vops == NULL) {
344 deffsname = "generic";
345 } else
346 deffsname = vops->vfs_name;
347
348 for (;;) {
349 printf("file system (default %s): ", deffsname);
350 len = cngetsn(buf, sizeof(buf));
351 if (len == 0) {
352 if (strcmp(deffsname, "generic") == 0)
353 rootfstype = ROOT_FSTYPE_ANY;
354 break;
355 }
356 if (len == 4 && strcmp(buf, "halt") == 0)
357 cpu_reboot(RB_HALT, NULL);
358 else if (len == 6 && strcmp(buf, "reboot") == 0)
359 cpu_reboot(0, NULL);
360 #if defined(DDB)
361 else if (len == 3 && strcmp(buf, "ddb") == 0) {
362 console_debugger();
363 }
364 #endif
365 else if (len == 7 && strcmp(buf, "generic") == 0) {
366 rootfstype = ROOT_FSTYPE_ANY;
367 break;
368 }
369 vops = vfs_getopsbyname(buf);
370 if (vops == NULL || vops->vfs_mountroot == NULL) {
371 printf("use one of: generic");
372 for (vops = LIST_FIRST(&vfs_list);
373 vops != NULL;
374 vops = LIST_NEXT(vops, vfs_list)) {
375 if (vops->vfs_mountroot != NULL)
376 printf(" %s", vops->vfs_name);
377 }
378 if (vops != NULL)
379 vfs_delref(vops);
380 #if defined(DDB)
381 printf(" ddb");
382 #endif
383 printf(" halt reboot\n");
384 } else {
385 /*
386 * XXX If *vops gets freed between here and
387 * the call to mountroot(), rootfstype will
388 * point to something unexpected. But in
389 * this case the system will fail anyway.
390 */
391 rootfstype = vops->vfs_name;
392 vfs_delref(vops);
393 break;
394 }
395 }
396
397 } else if (rootspec == NULL) {
398 /*
399 * Wildcarded root; use the boot device.
400 */
401 rootdv = bootdv;
402
403 if (bootdv)
404 majdev = devsw_name2blk(device_xname(bootdv), NULL, 0);
405 else
406 majdev = -1;
407 if (majdev >= 0) {
408 /*
409 * Root is on a disk. `bootpartition' is root,
410 * unless the device does not use partitions.
411 */
412 if (DEV_USES_PARTITIONS(bootdv))
413 rootdev = MAKEDISKDEV(majdev,
414 device_unit(bootdv),
415 bootpartition);
416 else
417 rootdev = makedev(majdev, device_unit(bootdv));
418 }
419 } else {
420
421 /*
422 * `root on <dev> ...'
423 */
424
425 /*
426 * If it's a network interface, we can bail out
427 * early.
428 */
429 dv = finddevice(rootspec);
430 if (dv != NULL && device_class(dv) == DV_IFNET) {
431 rootdv = dv;
432 goto haveroot;
433 }
434
435 if (rootdev == NODEV &&
436 dv != NULL && device_class(dv) == DV_DISK &&
437 device_is_a(dv, "dk") &&
438 (majdev = devsw_name2blk(device_xname(dv), NULL, 0)) >= 0)
439 rootdev = makedev(majdev, device_unit(dv));
440
441 rootdevname = devsw_blk2name(major(rootdev));
442 if (rootdevname == NULL) {
443 printf("unknown device major 0x%llx\n",
444 (unsigned long long)rootdev);
445 boothowto |= RB_ASKNAME;
446 goto top;
447 }
448 memset(buf, 0, sizeof(buf));
449 snprintf(buf, sizeof(buf), "%s%llu", rootdevname,
450 (unsigned long long)DISKUNIT(rootdev));
451
452 rootdv = finddevice(buf);
453 if (rootdv == NULL) {
454 printf("device %s (0x%llx) not configured\n",
455 buf, (unsigned long long)rootdev);
456 boothowto |= RB_ASKNAME;
457 goto top;
458 }
459 }
460
461 haveroot:
462
463 root_device = rootdv;
464
465 switch (device_class(rootdv)) {
466 case DV_IFNET:
467 case DV_DISK:
468 aprint_normal("root on %s", device_xname(rootdv));
469 if (DEV_USES_PARTITIONS(rootdv))
470 aprint_normal("%c", (int)DISKPART(rootdev) + 'a');
471 break;
472
473 default:
474 printf("can't determine root device\n");
475 boothowto |= RB_ASKNAME;
476 goto top;
477 }
478
479 /*
480 * Now configure the dump device.
481 *
482 * If we haven't figured out the dump device, do so, with
483 * the following rules:
484 *
485 * (a) We already know dumpdv in the RB_ASKNAME case.
486 *
487 * (b) If dumpspec is set, try to use it. If the device
488 * is not available, punt.
489 *
490 * (c) If dumpspec is not set, the dump device is
491 * wildcarded or unspecified. If the root device
492 * is DV_IFNET, punt. Otherwise, use partition b
493 * of the root device.
494 */
495
496 if (boothowto & RB_ASKNAME) { /* (a) */
497 if (dumpdv == NULL)
498 goto nodumpdev;
499 } else if (dumpspec != NULL) { /* (b) */
500 if (strcmp(dumpspec, "none") == 0 || dumpdev == NODEV) {
501 /*
502 * Operator doesn't want a dump device.
503 * Or looks like they tried to pick a network
504 * device. Oops.
505 */
506 goto nodumpdev;
507 }
508
509 dumpdevname = devsw_blk2name(major(dumpdev));
510 if (dumpdevname == NULL)
511 goto nodumpdev;
512 memset(buf, 0, sizeof(buf));
513 snprintf(buf, sizeof(buf), "%s%llu", dumpdevname,
514 (unsigned long long)DISKUNIT(dumpdev));
515
516 dumpdv = finddevice(buf);
517 if (dumpdv == NULL) {
518 /*
519 * Device not configured.
520 */
521 goto nodumpdev;
522 }
523 } else { /* (c) */
524 if (DEV_USES_PARTITIONS(rootdv) == 0) {
525 for (dv = deviter_first(&di, DEVITER_F_ROOT_FIRST);
526 dv != NULL;
527 dv = deviter_next(&di))
528 if (isswap(dv))
529 break;
530 deviter_release(&di);
531 if (dv == NULL)
532 goto nodumpdev;
533
534 majdev = devsw_name2blk(device_xname(dv), NULL, 0);
535 if (majdev < 0)
536 goto nodumpdev;
537 dumpdv = dv;
538 dumpdev = makedev(majdev, device_unit(dumpdv));
539 } else {
540 dumpdv = rootdv;
541 dumpdev = MAKEDISKDEV(major(rootdev),
542 device_unit(dumpdv), 1);
543 }
544 }
545
546 dumpcdev = devsw_blk2chr(dumpdev);
547 aprint_normal(" dumps on %s", device_xname(dumpdv));
548 if (DEV_USES_PARTITIONS(dumpdv))
549 aprint_normal("%c", (int)DISKPART(dumpdev) + 'a');
550 aprint_normal("\n");
551 return;
552
553 nodumpdev:
554 dumpdev = NODEV;
555 dumpcdev = NODEV;
556 aprint_normal("\n");
557 }
558
559 static device_t
560 finddevice(const char *name)
561 {
562 const char *wname;
563
564 if ((wname = getwedgename(name, strlen(name))) != NULL)
565 return dkwedge_find_by_wname(wname);
566
567 return device_find_by_xname(name);
568 }
569
570 static device_t
571 getdisk(char *str, int len, int defpart, dev_t *devp, int isdump)
572 {
573 device_t dv;
574 deviter_t di;
575
576 if ((dv = parsedisk(str, len, defpart, devp)) == NULL) {
577 printf("use one of:");
578 for (dv = deviter_first(&di, DEVITER_F_ROOT_FIRST); dv != NULL;
579 dv = deviter_next(&di)) {
580 if (DEV_USES_PARTITIONS(dv))
581 printf(" %s[a-%c]", device_xname(dv),
582 'a' + MAXPARTITIONS - 1);
583 else if (device_class(dv) == DV_DISK)
584 printf(" %s", device_xname(dv));
585 if (isdump == 0 && device_class(dv) == DV_IFNET)
586 printf(" %s", device_xname(dv));
587 }
588 deviter_release(&di);
589 dkwedge_print_wnames();
590 if (isdump)
591 printf(" none");
592 #if defined(DDB)
593 printf(" ddb");
594 #endif
595 printf(" halt reboot\n");
596 }
597 return dv;
598 }
599
600 static const char *
601 getwedgename(const char *name, int namelen)
602 {
603 const char *wpfx = "wedge:";
604 const int wpfxlen = strlen(wpfx);
605
606 if (namelen < wpfxlen || strncmp(name, wpfx, wpfxlen) != 0)
607 return NULL;
608
609 return name + wpfxlen;
610 }
611
612 static device_t
613 parsedisk(char *str, int len, int defpart, dev_t *devp)
614 {
615 device_t dv;
616 const char *wname;
617 char *cp, c;
618 int majdev, part;
619 if (len == 0)
620 return (NULL);
621
622 if (len == 4 && strcmp(str, "halt") == 0)
623 cpu_reboot(RB_HALT, NULL);
624 else if (len == 6 && strcmp(str, "reboot") == 0)
625 cpu_reboot(0, NULL);
626 #if defined(DDB)
627 else if (len == 3 && strcmp(str, "ddb") == 0)
628 console_debugger();
629 #endif
630
631 cp = str + len - 1;
632 c = *cp;
633
634 if ((wname = getwedgename(str, len)) != NULL) {
635 if ((dv = dkwedge_find_by_wname(wname)) == NULL)
636 return NULL;
637 part = defpart;
638 goto gotdisk;
639 } else if (c >= 'a' && c <= ('a' + MAXPARTITIONS - 1)) {
640 part = c - 'a';
641 *cp = '\0';
642 } else
643 part = defpart;
644
645 dv = finddevice(str);
646 if (dv != NULL) {
647 if (device_class(dv) == DV_DISK) {
648 gotdisk:
649 majdev = devsw_name2blk(device_xname(dv), NULL, 0);
650 if (majdev < 0)
651 panic("parsedisk");
652 if (DEV_USES_PARTITIONS(dv))
653 *devp = MAKEDISKDEV(majdev, device_unit(dv),
654 part);
655 else
656 *devp = makedev(majdev, device_unit(dv));
657 }
658
659 if (device_class(dv) == DV_IFNET)
660 *devp = NODEV;
661 }
662
663 *cp = c;
664 return (dv);
665 }
666