1 /*- 2 * Copyright (c) 1999 Michael Smith 3 * All rights reserved. 4 * Copyright (c) 1999 Poul-Henning Kamp 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $FreeBSD$ 29 */ 30 31 /* 32 * Locate and mount the root filesystem. 33 * 34 * The root filesystem is detailed in the kernel environment variable 35 * vfs.root.mountfrom, which is expected to be in the general format 36 * 37 * <vfsname>:[<path>] 38 * vfsname := the name of a VFS known to the kernel and capable 39 * of being mounted as root 40 * path := disk device name or other data used by the filesystem 41 * to locate its physical store 42 * 43 */ 44 45 #include "opt_rootdevname.h" 46 47 #include <sys/param.h> 48 #include <sys/kernel.h> 49 #include <sys/systm.h> 50 #include <sys/vnode.h> 51 #include <sys/mount.h> 52 #include <sys/malloc.h> 53 #include <sys/reboot.h> 54 #include <sys/diskslice.h> 55 #include <sys/disklabel.h> 56 #include <sys/conf.h> 57 #include <sys/cons.h> 58 59 #include "opt_ddb.h" 60 #ifdef DDB 61 #include <ddb/ddb.h> 62 #endif 63 64 MALLOC_DEFINE(M_MOUNT, "mount", "vfs mount structure"); 65 66 #define ROOTNAME "root_device" 67 68 struct vnode *rootvnode; 69 70 /* 71 * The root specifiers we will try if RB_CDROM is specified. 72 */ 73 static char *cdrom_rootdevnames[] = { 74 "cd9660:cd0a", 75 "cd9660:acd0a", 76 "cd9660:wcd0a", 77 NULL 78 }; 79 80 static void vfs_mountroot(void *junk); 81 static int vfs_mountroot_try(char *mountfrom); 82 static int vfs_mountroot_ask(void); 83 static void gets(char *cp); 84 85 /* legacy find-root code */ 86 char *rootdevnames[2] = {NULL, NULL}; 87 static int setrootbyname(char *name); 88 89 SYSINIT(mountroot, SI_SUB_MOUNT_ROOT, SI_ORDER_SECOND, vfs_mountroot, NULL); 90 91 /* 92 * Find and mount the root filesystem 93 */ 94 static void 95 vfs_mountroot(void *junk) 96 { 97 int i; 98 99 /* 100 * The root filesystem information is compiled in, and we are 101 * booted with instructions to use it. 102 */ 103 #ifdef ROOTDEVNAME 104 if ((boothowto & RB_DFLTROOT) && 105 !vfs_mountroot_try(ROOTDEVNAME)) 106 return; 107 #endif 108 /* 109 * We are booted with instructions to prompt for the root filesystem, 110 * or to use the compiled-in default when it doesn't exist. 111 */ 112 if (boothowto & (RB_DFLTROOT | RB_ASKNAME)) { 113 if (!vfs_mountroot_ask()) 114 return; 115 } 116 117 /* 118 * We've been given the generic "use CDROM as root" flag. This is 119 * necessary because one media may be used in many different 120 * devices, so we need to search for them. 121 */ 122 if (boothowto & RB_CDROM) { 123 for (i = 0; cdrom_rootdevnames[i] != NULL; i++) { 124 if (!vfs_mountroot_try(cdrom_rootdevnames[i])) 125 return; 126 } 127 } 128 129 /* 130 * Try to use the value read by the loader from /etc/fstab, or 131 * supplied via some other means. This is the preferred 132 * mechanism. 133 */ 134 if (!vfs_mountroot_try(getenv("vfs.root.mountfrom"))) 135 return; 136 137 /* 138 * Try values that may have been computed by the machine-dependant 139 * legacy code. 140 */ 141 if (!vfs_mountroot_try(rootdevnames[0])) 142 return; 143 if (!vfs_mountroot_try(rootdevnames[1])) 144 return; 145 146 /* 147 * If we have a compiled-in default, and haven't already tried it, try 148 * it now. 149 */ 150 #ifdef ROOTDEVNAME 151 if (!(boothowto & RB_DFLTROOT)) 152 if (!vfs_mountroot_try(ROOTDEVNAME)) 153 return; 154 #endif 155 156 /* 157 * Everything so far has failed, prompt on the console if we haven't 158 * already tried that. 159 */ 160 if (!(boothowto & (RB_DFLTROOT | RB_ASKNAME)) && !vfs_mountroot_ask()) 161 return; 162 panic("Root mount failed, startup aborted."); 163 } 164 165 /* 166 * Mount (mountfrom) as the root filesystem. 167 */ 168 static int 169 vfs_mountroot_try(char *mountfrom) 170 { 171 struct mount *mp; 172 char *vfsname, *path; 173 int error; 174 char patt[32]; 175 int s; 176 177 vfsname = NULL; 178 path = NULL; 179 mp = NULL; 180 error = EINVAL; 181 182 if (mountfrom == NULL) 183 return(error); /* don't complain */ 184 185 s = splcam(); /* Overkill, but annoying without it */ 186 printf("Mounting root from %s\n", mountfrom); 187 splx(s); 188 189 /* parse vfs name and path */ 190 vfsname = malloc(MFSNAMELEN, M_MOUNT, M_WAITOK); 191 path = malloc(MNAMELEN, M_MOUNT, M_WAITOK); 192 vfsname[0] = path[0] = 0; 193 sprintf(patt, "%%%d[a-z0-9]:%%%ds", MFSNAMELEN, MNAMELEN); 194 if (sscanf(mountfrom, patt, vfsname, path) < 1) 195 goto done; 196 197 /* allocate a root mount */ 198 error = vfs_rootmountalloc(vfsname, path[0] != 0 ? path : ROOTNAME, 199 &mp); 200 if (error != 0) { 201 printf("Can't allocate root mount for filesystem '%s': %d\n", 202 vfsname, error); 203 goto done; 204 } 205 mp->mnt_flag |= MNT_ROOTFS; 206 207 /* do our best to set rootdev */ 208 if ((path[0] != 0) && setrootbyname(path)) 209 printf("setrootbyname failed\n"); 210 211 /* If the root device is a type "memory disk", mount RW */ 212 if (rootdev != NODEV && devsw(rootdev) && 213 (devsw(rootdev)->d_flags & D_MEMDISK)) 214 mp->mnt_flag &= ~MNT_RDONLY; 215 216 error = VFS_MOUNT(mp, NULL, NULL, NULL, curproc); 217 218 done: 219 if (vfsname != NULL) 220 free(vfsname, M_MOUNT); 221 if (path != NULL) 222 free(path, M_MOUNT); 223 if (error != 0) { 224 if (mp != NULL) { 225 vfs_unbusy(mp, curproc); 226 free(mp, M_MOUNT); 227 } 228 printf("Root mount failed: %d\n", error); 229 } else { 230 231 /* register with list of mounted filesystems */ 232 mtx_enter(&mountlist_mtx, MTX_DEF); 233 TAILQ_INSERT_HEAD(&mountlist, mp, mnt_list); 234 mtx_exit(&mountlist_mtx, MTX_DEF); 235 236 /* sanity check system clock against root filesystem timestamp */ 237 inittodr(mp->mnt_time); 238 vfs_unbusy(mp, curproc); 239 } 240 return(error); 241 } 242 243 /* 244 * Spin prompting on the console for a suitable root filesystem 245 */ 246 static int 247 vfs_mountroot_ask(void) 248 { 249 char name[128]; 250 int i; 251 dev_t dev; 252 253 for(;;) { 254 printf("\nManual root filesystem specification:\n"); 255 printf(" <fstype>:<device> Mount <device> using filesystem <fstype>\n"); 256 printf(" eg. ufs:/dev/da0s1a\n"); 257 printf(" ? List valid disk boot devices\n"); 258 printf(" <empty line> Abort manual input\n"); 259 printf("\nmountroot> "); 260 gets(name); 261 if (name[0] == 0) 262 return(1); 263 if (name[0] == '?') { 264 printf("Possibly valid devices for 'ufs' root:\n"); 265 for (i = 0; i < NUMCDEVSW; i++) { 266 dev = makedev(i, 0); 267 if (devsw(dev) != NULL) 268 printf(" \"%s\"", devsw(dev)->d_name); 269 } 270 printf("\n"); 271 continue; 272 } 273 if (!vfs_mountroot_try(name)) 274 return(0); 275 } 276 } 277 278 static void 279 gets(char *cp) 280 { 281 char *lp; 282 int c; 283 284 lp = cp; 285 for (;;) { 286 printf("%c", c = cngetc() & 0177); 287 switch (c) { 288 case -1: 289 case '\n': 290 case '\r': 291 *lp++ = '\0'; 292 return; 293 case '\b': 294 case '\177': 295 if (lp > cp) { 296 printf(" \b"); 297 lp--; 298 } 299 continue; 300 case '#': 301 lp--; 302 if (lp < cp) 303 lp = cp; 304 continue; 305 case '@': 306 case 'u' & 037: 307 lp = cp; 308 printf("%c", '\n'); 309 continue; 310 default: 311 *lp++ = c; 312 } 313 } 314 } 315 316 /* 317 * Convert a given name to the dev_t of the disk-like device 318 * it refers to. 319 */ 320 dev_t 321 getdiskbyname(char *name) { 322 char *cp; 323 dev_t dev; 324 325 cp = name; 326 if (!bcmp(cp, "/dev/", 5)) 327 cp += 5; 328 329 dev = NODEV; 330 EVENTHANDLER_INVOKE(dev_clone, cp, strlen(cp), &dev); 331 return (dev); 332 } 333 334 /* 335 * Set rootdev to match (name), given that we expect it to 336 * refer to a disk-like device. 337 */ 338 static int 339 setrootbyname(char *name) 340 { 341 dev_t diskdev; 342 343 diskdev = getdiskbyname(name); 344 if (diskdev != NODEV) { 345 rootdev = diskdev; 346 return (0); 347 } 348 349 return (1); 350 } 351 352 #ifdef DDB 353 DB_SHOW_COMMAND(disk, db_getdiskbyname) 354 { 355 dev_t dev; 356 357 if (modif[0] == '\0') { 358 db_error("usage: show disk/devicename"); 359 return; 360 } 361 dev = getdiskbyname(modif); 362 if (dev != NODEV) 363 db_printf("dev_t = %p\n", dev); 364 else 365 db_printf("No disk device matched.\n"); 366 } 367 #endif 368