]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/imageboot.c
xnu-3789.1.32.tar.gz
[apple/xnu.git] / bsd / kern / imageboot.c
CommitLineData
2d21ac55
A
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>
6d2010ae 38#include <kern/assert.h>
2d21ac55
A
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);
6d2010ae 56static boolean_t imageboot_setup_new(void);
2d21ac55
A
57
58#define kIBFilePrefix "file://"
59
6d2010ae
A
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
2d21ac55
A
76imageboot_needed(void)
77{
78 int result = 0;
79 char *root_path = NULL;
6d2010ae 80
2d21ac55
A
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
6d2010ae
A
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 }
2d21ac55 100
6d2010ae
A
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);
2d21ac55 115 }
6d2010ae
A
116
117out:
2d21ac55
A
118 FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI);
119
120 return (result);
121}
122
123
124/*
6d2010ae
A
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).
2d21ac55 130 */
6d2010ae
A
131__private_extern__ int
132imageboot_mount_image(const char *root_path, int height)
2d21ac55 133{
6d2010ae
A
134 dev_t dev;
135 int error;
136 vnode_t old_rootvnode = NULL;
137 vnode_t newdp;
138 mount_t new_rootfs;
2d21ac55 139
2d21ac55 140 error = di_root_image(root_path, rootdevice, &dev);
6d2010ae
A
141 if (error) {
142 panic("%s: di_root_image failed: %d\n", __FUNCTION__, error);
2d21ac55
A
143 }
144
145 rootdev = dev;
146 mountroot = NULL;
147 printf("%s: root device 0x%x\n", __FUNCTION__, rootdev);
148 error = vfs_mountroot();
6d2010ae
A
149 if (error != 0) {
150 panic("vfs_mountroot() failed.\n");
151 }
2d21ac55 152
6d2010ae
A
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__);
2d21ac55 159
6d2010ae
A
160 if (rootvnode != NULL) {
161 /* remember the old rootvnode, but remove it from mountlist */
162 mount_t old_rootfs;
2d21ac55 163
b7266188
A
164 old_rootvnode = rootvnode;
165 old_rootfs = rootvnode->v_mount;
6d2010ae 166
b7266188 167 mount_list_remove(old_rootfs);
6d2010ae 168
b7266188
A
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);
6d2010ae 175 }
b7266188 176
6d2010ae
A
177 /* switch to the new rootvnode */
178 rootvnode = newdp;
b7266188 179
6d2010ae
A
180 new_rootfs = rootvnode->v_mount;
181 mount_lock(new_rootfs);
182 new_rootfs->mnt_flag |= MNT_ROOTFS;
183 mount_unlock(new_rootfs);
b7266188 184
6d2010ae
A
185 vnode_ref(newdp);
186 vnode_put(newdp);
187 filedesc0.fd_cdir = newdp;
188 DBG_TRACE("%s: root switched\n", __FUNCTION__);
b7266188 189
6d2010ae 190 if (old_rootvnode != NULL) {
b7266188 191#ifdef CONFIG_IMGSRC_ACCESS
6d2010ae
A
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 }
b7266188 197#else
316670eb 198 height = 0; /* keep the compiler from complaining */
6d2010ae 199 vnode_get_and_drop_always(old_rootvnode);
b7266188 200#endif /* CONFIG_IMGSRC_ACCESS */
6d2010ae
A
201 }
202 return 0;
203}
b7266188 204
6d2010ae
A
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;
b7266188 231
2d21ac55 232 }
6d2010ae
A
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
2d21ac55
A
309done:
310 FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI);
311
312 DBG_TRACE("%s: exit\n", __FUNCTION__);
313
6d2010ae 314 return;
2d21ac55 315}