]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/imageboot.c
xnu-3789.1.32.tar.gz
[apple/xnu.git] / bsd / kern / imageboot.c
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
42 extern struct filedesc filedesc0;
43
44 extern int (*mountroot)(void);
45 extern 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
55 extern int di_root_image(const char *path, char devname[], dev_t *dev_p);
56 static boolean_t imageboot_setup_new(void);
57
58 #define kIBFilePrefix "file://"
59
60 __private_extern__ int
61 imageboot_format_is_valid(const char *root_path)
62 {
63 return (strncmp(root_path, kIBFilePrefix,
64 strlen(kIBFilePrefix)) == 0);
65 }
66
67 static void
68 vnode_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
76 imageboot_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
117 out:
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
132 imageboot_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
205 static boolean_t
206 imageboot_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
243 out:
244 FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI);
245 return done;
246 }
247
248 __private_extern__ void
249 imageboot_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
309 done:
310 FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI);
311
312 DBG_TRACE("%s: exit\n", __FUNCTION__);
313
314 return;
315 }