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