]>
Commit | Line | Data |
---|---|---|
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 | ||
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); | |
6d2010ae | 56 | static boolean_t imageboot_setup_new(void); |
2d21ac55 A |
57 | |
58 | #define kIBFilePrefix "file://" | |
59 | ||
6d2010ae A |
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 | |
2d21ac55 A |
76 | imageboot_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 | |
117 | out: | |
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 |
132 | imageboot_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 |
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; | |
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 | ||
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 | ||
2d21ac55 A |
309 | done: |
310 | FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI); | |
311 | ||
312 | DBG_TRACE("%s: exit\n", __FUNCTION__); | |
313 | ||
6d2010ae | 314 | return; |
2d21ac55 | 315 | } |