1/* 2 * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29#include <sys/param.h> 30#include <sys/kernel.h> 31#include <sys/proc_internal.h> 32#include <sys/systm.h> 33#include <sys/systm.h> 34#include <sys/mount_internal.h> 35#include <sys/filedesc.h> 36#include <sys/vnode_internal.h> 37#include <sys/imageboot.h> 38#include <kern/assert.h> 39 40#include <pexpert/pexpert.h> 41 42extern struct filedesc filedesc0; 43 44extern int (*mountroot)(void); 45extern char rootdevice[]; 46 47#define DEBUG_IMAGEBOOT 0 48 49#if DEBUG_IMAGEBOOT 50#define DBG_TRACE(...) printf(__VA_ARGS__) 51#else 52#define DBG_TRACE(...) do {} while(0) 53#endif 54 55extern int di_root_image(const char *path, char devname[], dev_t *dev_p); 56static boolean_t imageboot_setup_new(void); 57 58#define kIBFilePrefix "file://" 59 60__private_extern__ int 61imageboot_format_is_valid(const char *root_path) 62{ 63 return (strncmp(root_path, kIBFilePrefix, 64 strlen(kIBFilePrefix)) == 0); 65} 66 67static void 68vnode_get_and_drop_always(vnode_t vp) 69{ 70 vnode_getalways(vp); 71 vnode_rele(vp); 72 vnode_put(vp); 73} 74 75__private_extern__ int 76imageboot_needed(void) 77{ 78 int result = 0; 79 char *root_path = NULL; 80 81 DBG_TRACE("%s: checking for presence of root path\n", __FUNCTION__); 82 83 MALLOC_ZONE(root_path, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK); 84 if (root_path == NULL) 85 panic("%s: M_NAMEI zone exhausted", __FUNCTION__); 86 87 /* Check for first layer */ 88 if (!(PE_parse_boot_argn("rp0", root_path, MAXPATHLEN) || 89 PE_parse_boot_argn("rp", root_path, MAXPATHLEN) || 90 PE_parse_boot_argn(IMAGEBOOT_ROOT_ARG, root_path, MAXPATHLEN))) { 91 goto out; 92 } 93 94 /* Sanity-check first layer */ 95 if (imageboot_format_is_valid(root_path)) { 96 DBG_TRACE("%s: Found %s\n", __FUNCTION__, root_path); 97 } else { 98 goto out; 99 } 100 101 result = 1; 102 103 /* Check for second layer */ 104 if (!(PE_parse_boot_argn("rp1", root_path, MAXPATHLEN) || 105 PE_parse_boot_argn(IMAGEBOOT_CONTAINER_ARG, root_path, MAXPATHLEN))) { 106 goto out; 107 } 108 109 /* Sanity-check second layer */ 110 if (imageboot_format_is_valid(root_path)) { 111 DBG_TRACE("%s: Found %s\n", __FUNCTION__, root_path); 112 } else { 113 panic("%s: Invalid URL scheme for %s\n", 114 __FUNCTION__, root_path); 115 } 116 117out: 118 FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI); 119 120 return (result); 121} 122 123 124/* 125 * Swaps in new root filesystem based on image path. 126 * Current root filesystem is removed from mount list and 127 * tagged MNTK_BACKS_ROOT, MNT_ROOTFS is cleared on it, and 128 * "rootvnode" is reset. Root vnode of currentroot filesystem 129 * is returned with usecount (no iocount). 130 */ 131__private_extern__ int 132imageboot_mount_image(const char *root_path, int height) 133{ 134 dev_t dev; 135 int error; 136 vnode_t old_rootvnode = NULL; 137 vnode_t newdp; 138 mount_t new_rootfs; 139 140 error = di_root_image(root_path, rootdevice, &dev); 141 if (error) { 142 panic("%s: di_root_image failed: %d\n", __FUNCTION__, error); 143 } 144 145 rootdev = dev; 146 mountroot = NULL; 147 printf("%s: root device 0x%x\n", __FUNCTION__, rootdev); 148 error = vfs_mountroot(); 149 if (error != 0) { 150 panic("vfs_mountroot() failed.\n"); 151 } 152 153 /* 154 * Get the vnode for '/'. 155 * Set fdp->fd_fd.fd_cdir to reference it. 156 */ 157 if (VFS_ROOT(TAILQ_LAST(&mountlist,mntlist), &newdp, vfs_context_kernel())) 158 panic("%s: cannot find root vnode", __FUNCTION__); 159 160 if (rootvnode != NULL) { 161 /* remember the old rootvnode, but remove it from mountlist */ 162 mount_t old_rootfs; 163 164 old_rootvnode = rootvnode; 165 old_rootfs = rootvnode->v_mount; 166 167 mount_list_remove(old_rootfs); 168 169 mount_lock(old_rootfs); 170#ifdef CONFIG_IMGSRC_ACCESS 171 old_rootfs->mnt_kern_flag |= MNTK_BACKS_ROOT; 172#endif /* CONFIG_IMGSRC_ACCESS */ 173 old_rootfs->mnt_flag &= ~MNT_ROOTFS; 174 mount_unlock(old_rootfs); 175 } 176 177 /* switch to the new rootvnode */ 178 rootvnode = newdp; 179 180 new_rootfs = rootvnode->v_mount; 181 mount_lock(new_rootfs); 182 new_rootfs->mnt_flag |= MNT_ROOTFS; 183 mount_unlock(new_rootfs); 184 185 vnode_ref(newdp); 186 vnode_put(newdp); 187 filedesc0.fd_cdir = newdp; 188 DBG_TRACE("%s: root switched\n", __FUNCTION__); 189 190 if (old_rootvnode != NULL) { 191#ifdef CONFIG_IMGSRC_ACCESS 192 if (height >= 0 && PE_imgsrc_mount_supported()) { 193 imgsrc_rootvnodes[height] = old_rootvnode; 194 } else { 195 vnode_get_and_drop_always(old_rootvnode); 196 } 197#else 198 height = 0; /* keep the compiler from complaining */ 199 vnode_get_and_drop_always(old_rootvnode); 200#endif /* CONFIG_IMGSRC_ACCESS */ 201 } 202 return 0; 203} 204 205static boolean_t 206imageboot_setup_new() 207{ 208 int error; 209 char *root_path = NULL; 210 int height = 0; 211 boolean_t done = FALSE; 212 213 MALLOC_ZONE(root_path, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK); 214 assert(root_path != NULL); 215 216 if(PE_parse_boot_argn(IMAGEBOOT_CONTAINER_ARG, root_path, MAXPATHLEN) == TRUE) { 217 printf("%s: container image url is %s\n", __FUNCTION__, root_path); 218 error = imageboot_mount_image(root_path, height); 219 if (error != 0) { 220 panic("Failed to mount container image."); 221 } 222 223 height++; 224 } 225 226 if (PE_parse_boot_argn(IMAGEBOOT_ROOT_ARG, root_path, MAXPATHLEN) == FALSE) { 227 if (height > 0) { 228 panic("%s specified without %s?\n", IMAGEBOOT_CONTAINER_ARG, IMAGEBOOT_ROOT_ARG); 229 } 230 goto out; 231 232 } 233 234 printf("%s: root image url is %s\n", __FUNCTION__, root_path); 235 236 error = imageboot_mount_image(root_path, height); 237 if (error != 0) { 238 panic("Failed to mount root image."); 239 } 240 241 done = TRUE; 242 243out: 244 FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI); 245 return done; 246} 247 248__private_extern__ void 249imageboot_setup() 250{ 251 int error = 0; 252 char *root_path = NULL; 253 254 DBG_TRACE("%s: entry\n", __FUNCTION__); 255 256 if (rootvnode == NULL) { 257 panic("imageboot_setup: rootvnode is NULL."); 258 } 259 260 /* 261 * New boot-arg scheme: 262 * root-dmg : the dmg that will be the root filesystem. 263 * container-dmg : an optional dmg that contains the root-dmg. 264 */ 265 if (imageboot_setup_new()) { 266 return; 267 } 268 269 MALLOC_ZONE(root_path, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK); 270 assert(root_path != NULL); 271 272 /* 273 * Look for outermost disk image to root from. If we're doing a nested boot, 274 * there's some sense in which the outer image never needs to be the root filesystem, 275 * but it does need very similar treatment: it must not be unmounted, needs a fake 276 * device vnode created for it, and should not show up in getfsstat() until exposed 277 * with MNT_IMGSRC. We just make it the temporary root. 278 */ 279 if((PE_parse_boot_argn("rp", root_path, MAXPATHLEN) == FALSE) && 280 (PE_parse_boot_argn("rp0", root_path, MAXPATHLEN) == FALSE)) { 281 panic("%s: no valid path to image.\n", __FUNCTION__); 282 } 283 284 printf("%s: root image url is %s\n", __FUNCTION__, root_path); 285 286 error = imageboot_mount_image(root_path, 0); 287 if (error) { 288 panic("Failed on first stage of imageboot."); 289 } 290 291 /* 292 * See if we are rooting from a nested image 293 */ 294 if(PE_parse_boot_argn("rp1", root_path, MAXPATHLEN) == FALSE) { 295 goto done; 296 } 297 298 printf("%s: second level root image url is %s\n", __FUNCTION__, root_path); 299 300 /* 301 * If we fail to set up second image, it's not a given that we 302 * can safely root off the first. 303 */ 304 error = imageboot_mount_image(root_path, 1); 305 if (error) { 306 panic("Failed on second stage of imageboot."); 307 } 308 309done: 310 FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI); 311 312 DBG_TRACE("%s: exit\n", __FUNCTION__); 313 314 return; 315} 316