]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/imageboot.c
xnu-1699.32.7.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
6d2010ae 198 vnode_get_and_drop_always(old_rootvnode);
b7266188 199#endif /* CONFIG_IMGSRC_ACCESS */
6d2010ae
A
200 }
201 return 0;
202}
b7266188 203
6d2010ae
A
204static boolean_t
205imageboot_setup_new()
206{
207 int error;
208 char *root_path = NULL;
209 int height = 0;
210 boolean_t done = FALSE;
211
212 MALLOC_ZONE(root_path, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
213 assert(root_path != NULL);
214
215 if(PE_parse_boot_argn(IMAGEBOOT_CONTAINER_ARG, root_path, MAXPATHLEN) == TRUE) {
216 printf("%s: container image url is %s\n", __FUNCTION__, root_path);
217 error = imageboot_mount_image(root_path, height);
218 if (error != 0) {
219 panic("Failed to mount container image.");
220 }
221
222 height++;
223 }
224
225 if (PE_parse_boot_argn(IMAGEBOOT_ROOT_ARG, root_path, MAXPATHLEN) == FALSE) {
226 if (height > 0) {
227 panic("%s specified without %s?\n", IMAGEBOOT_CONTAINER_ARG, IMAGEBOOT_ROOT_ARG);
228 }
229 goto out;
b7266188 230
2d21ac55 231 }
6d2010ae
A
232
233 printf("%s: root image url is %s\n", __FUNCTION__, root_path);
234
235 error = imageboot_mount_image(root_path, height);
236 if (error != 0) {
237 panic("Failed to mount root image.");
238 }
239
240 done = TRUE;
241
242out:
243 FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI);
244 return done;
245}
246
247__private_extern__ void
248imageboot_setup()
249{
250 int error = 0;
251 char *root_path = NULL;
252
253 DBG_TRACE("%s: entry\n", __FUNCTION__);
254
255 if (rootvnode == NULL) {
256 panic("imageboot_setup: rootvnode is NULL.");
257 }
258
259 /*
260 * New boot-arg scheme:
261 * root-dmg : the dmg that will be the root filesystem.
262 * container-dmg : an optional dmg that contains the root-dmg.
263 */
264 if (imageboot_setup_new()) {
265 return;
266 }
267
268 MALLOC_ZONE(root_path, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
269 assert(root_path != NULL);
270
271 /*
272 * Look for outermost disk image to root from. If we're doing a nested boot,
273 * there's some sense in which the outer image never needs to be the root filesystem,
274 * but it does need very similar treatment: it must not be unmounted, needs a fake
275 * device vnode created for it, and should not show up in getfsstat() until exposed
276 * with MNT_IMGSRC. We just make it the temporary root.
277 */
278 if((PE_parse_boot_argn("rp", root_path, MAXPATHLEN) == FALSE) &&
279 (PE_parse_boot_argn("rp0", root_path, MAXPATHLEN) == FALSE)) {
280 panic("%s: no valid path to image.\n", __FUNCTION__);
281 }
282
283 printf("%s: root image url is %s\n", __FUNCTION__, root_path);
284
285 error = imageboot_mount_image(root_path, 0);
286 if (error) {
287 panic("Failed on first stage of imageboot.");
288 }
289
290 /*
291 * See if we are rooting from a nested image
292 */
293 if(PE_parse_boot_argn("rp1", root_path, MAXPATHLEN) == FALSE) {
294 goto done;
295 }
296
297 printf("%s: second level root image url is %s\n", __FUNCTION__, root_path);
298
299 /*
300 * If we fail to set up second image, it's not a given that we
301 * can safely root off the first.
302 */
303 error = imageboot_mount_image(root_path, 1);
304 if (error) {
305 panic("Failed on second stage of imageboot.");
306 }
307
2d21ac55
A
308done:
309 FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI);
310
311 DBG_TRACE("%s: exit\n", __FUNCTION__);
312
6d2010ae 313 return;
2d21ac55 314}