]>
Commit | Line | Data |
---|---|---|
2d21ac55 | 1 | /* |
f427ee49 | 2 | * Copyright (c) 2006-2020 Apple Computer, Inc. All rights reserved. |
2d21ac55 A |
3 | * |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ | |
0a7de745 | 5 | * |
2d21ac55 A |
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. | |
0a7de745 | 14 | * |
2d21ac55 A |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
0a7de745 | 17 | * |
2d21ac55 A |
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. | |
0a7de745 | 25 | * |
2d21ac55 A |
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> | |
f427ee49 | 35 | #include <sys/fsctl.h> |
2d21ac55 A |
36 | #include <sys/filedesc.h> |
37 | #include <sys/vnode_internal.h> | |
38 | #include <sys/imageboot.h> | |
6d2010ae | 39 | #include <kern/assert.h> |
2d21ac55 | 40 | |
813fb2f6 A |
41 | #include <sys/namei.h> |
42 | #include <sys/fcntl.h> | |
43 | #include <sys/vnode.h> | |
44 | #include <sys/sysproto.h> | |
45 | #include <sys/csr.h> | |
5ba3f43e | 46 | #include <miscfs/devfs/devfsdefs.h> |
813fb2f6 A |
47 | #include <libkern/crypto/sha2.h> |
48 | #include <libkern/crypto/rsa.h> | |
49 | #include <libkern/OSKextLibPrivate.h> | |
f427ee49 | 50 | #include <sys/ubc_internal.h> |
813fb2f6 | 51 | |
cb323159 A |
52 | #if CONFIG_IMAGEBOOT_IMG4 |
53 | #include <libkern/img4/interface.h> | |
f427ee49 | 54 | #include <img4/firmware.h> |
cb323159 A |
55 | #endif |
56 | ||
813fb2f6 A |
57 | #include <kern/kalloc.h> |
58 | ||
2d21ac55 | 59 | #include <pexpert/pexpert.h> |
813fb2f6 | 60 | #include <kern/chunklist.h> |
2d21ac55 A |
61 | |
62 | extern struct filedesc filedesc0; | |
63 | ||
64 | extern int (*mountroot)(void); | |
5ba3f43e | 65 | extern char rootdevice[DEVMAXNAMESIZE]; |
2d21ac55 | 66 | |
cb323159 A |
67 | #if CONFIG_LOCKERBOOT |
68 | typedef struct _locker_mount_args { | |
69 | char lmnt_path[PATH_MAX]; | |
70 | uint16_t lmnt_preferred_hash; | |
71 | } locker_mount_args_t; | |
72 | #endif | |
73 | ||
2d21ac55 A |
74 | #define DEBUG_IMAGEBOOT 0 |
75 | ||
76 | #if DEBUG_IMAGEBOOT | |
cb323159 | 77 | #define DBG_TRACE(...) printf("imageboot: " __VA_ARGS__) |
2d21ac55 A |
78 | #else |
79 | #define DBG_TRACE(...) do {} while(0) | |
80 | #endif | |
81 | ||
cb323159 A |
82 | #define AUTHDBG(fmt, args...) do { printf("%s: " fmt "\n", __func__, ##args); } while (0) |
83 | #define AUTHPRNT(fmt, args...) do { printf("%s: " fmt "\n", __func__, ##args); } while (0) | |
f427ee49 | 84 | #define kheap_free_safe(h, x, l) do { if ((x)) { kheap_free(h, x, l); (x) = NULL; } } while (0) |
cb323159 | 85 | |
f427ee49 | 86 | extern int di_root_image_ext(const char *path, char *devname, size_t devsz, dev_t *dev_p, bool removable); |
5ba3f43e A |
87 | extern int di_root_image(const char *path, char *devname, size_t devsz, dev_t *dev_p); |
88 | extern int di_root_ramfile_buf(void *buf, size_t bufsz, char *devname, size_t devsz, dev_t *dev_p); | |
89 | ||
cb323159 A |
90 | static boolean_t imageboot_setup_new(imageboot_type_t type); |
91 | ||
f427ee49 A |
92 | void *ubc_getobject_from_filename(const char *filename, struct vnode **vpp, off_t *file_size); |
93 | ||
94 | extern lck_rw_t * rootvnode_rw_lock; | |
2d21ac55 A |
95 | |
96 | #define kIBFilePrefix "file://" | |
97 | ||
6d2010ae A |
98 | __private_extern__ int |
99 | imageboot_format_is_valid(const char *root_path) | |
100 | { | |
0a7de745 A |
101 | return strncmp(root_path, kIBFilePrefix, |
102 | strlen(kIBFilePrefix)) == 0; | |
6d2010ae A |
103 | } |
104 | ||
105 | static void | |
0a7de745 | 106 | vnode_get_and_drop_always(vnode_t vp) |
6d2010ae A |
107 | { |
108 | vnode_getalways(vp); | |
109 | vnode_rele(vp); | |
110 | vnode_put(vp); | |
111 | } | |
112 | ||
f427ee49 A |
113 | __private_extern__ bool |
114 | imageboot_desired(void) | |
2d21ac55 | 115 | { |
f427ee49 | 116 | bool do_imageboot = false; |
cb323159 | 117 | |
f427ee49 A |
118 | char *root_path = NULL; |
119 | root_path = zalloc(ZV_NAMEI); | |
120 | /* | |
121 | * Check for first layer DMG rooting. | |
122 | * | |
123 | * Note that here we are principally concerned with whether or not we | |
124 | * SHOULD try to imageboot, not whether or not we are going to be able to. | |
125 | * | |
126 | * If NONE of the boot-args are present, then assume that image-rooting | |
127 | * is not requested. | |
128 | * | |
129 | * [!! Note parens guard the entire logically OR'd set of statements, below. It validates | |
130 | * that NONE of the below-mentioned boot-args is present...!!] | |
131 | */ | |
813fb2f6 | 132 | if (!(PE_parse_boot_argn("rp0", root_path, MAXPATHLEN) || |
cb323159 A |
133 | #if CONFIG_IMAGEBOOT_IMG4 |
134 | PE_parse_boot_argn("arp0", root_path, MAXPATHLEN) || | |
135 | #endif | |
0a7de745 A |
136 | PE_parse_boot_argn("rp", root_path, MAXPATHLEN) || |
137 | PE_parse_boot_argn(IMAGEBOOT_ROOT_ARG, root_path, MAXPATHLEN) || | |
138 | PE_parse_boot_argn(IMAGEBOOT_AUTHROOT_ARG, root_path, MAXPATHLEN))) { | |
f427ee49 A |
139 | /* explicitly set to false */ |
140 | do_imageboot = false; | |
141 | } else { | |
142 | /* now sanity check the file-path format */ | |
143 | if (imageboot_format_is_valid(root_path)) { | |
144 | DBG_TRACE("%s: Found %s\n", __FUNCTION__, root_path); | |
145 | /* root_path looks good and we have one of the aforementioned bootargs */ | |
146 | do_imageboot = true; | |
147 | } else { | |
148 | /* explicitly set to false */ | |
149 | do_imageboot = false; | |
150 | } | |
6d2010ae | 151 | } |
813fb2f6 | 152 | |
f427ee49 A |
153 | zfree(ZV_NAMEI, root_path); |
154 | return do_imageboot; | |
155 | } | |
156 | ||
157 | __private_extern__ imageboot_type_t | |
158 | imageboot_needed(void) | |
159 | { | |
160 | imageboot_type_t result = IMAGEBOOT_NONE; | |
161 | char *root_path = NULL; | |
162 | ||
163 | DBG_TRACE("%s: checking for presence of root path\n", __FUNCTION__); | |
164 | ||
165 | if (!imageboot_desired()) { | |
6d2010ae A |
166 | goto out; |
167 | } | |
2d21ac55 | 168 | |
f427ee49 | 169 | root_path = zalloc(ZV_NAMEI); |
cb323159 | 170 | result = IMAGEBOOT_DMG; |
6d2010ae A |
171 | |
172 | /* Check for second layer */ | |
173 | if (!(PE_parse_boot_argn("rp1", root_path, MAXPATHLEN) || | |
0a7de745 | 174 | PE_parse_boot_argn(IMAGEBOOT_CONTAINER_ARG, root_path, MAXPATHLEN))) { |
6d2010ae A |
175 | goto out; |
176 | } | |
177 | ||
178 | /* Sanity-check second layer */ | |
179 | if (imageboot_format_is_valid(root_path)) { | |
180 | DBG_TRACE("%s: Found %s\n", __FUNCTION__, root_path); | |
181 | } else { | |
182 | panic("%s: Invalid URL scheme for %s\n", | |
0a7de745 | 183 | __FUNCTION__, root_path); |
2d21ac55 | 184 | } |
6d2010ae A |
185 | |
186 | out: | |
f427ee49 A |
187 | if (root_path != NULL) { |
188 | zfree(ZV_NAMEI, root_path); | |
189 | } | |
0a7de745 | 190 | return result; |
2d21ac55 A |
191 | } |
192 | ||
f427ee49 A |
193 | extern bool IOBaseSystemARVRootHashAvailable(void); |
194 | ||
195 | ||
196 | /* | |
197 | * Mounts new filesystem based on image path, and pivots it to the root. | |
198 | * The image to be mounted is located at image_path. | |
199 | * It will be mounted at mount_path. | |
200 | * The vfs_switch_root operation will be performed. | |
201 | * After the pivot, the outgoing root filesystem (the filesystem at root when | |
202 | * this function begins) will be at outgoing_root_path. If `rooted_dmg` is true, | |
203 | * then ignore then chunklisted or authAPFS checks on this image | |
204 | */ | |
205 | __private_extern__ int | |
206 | imageboot_pivot_image(const char *image_path, imageboot_type_t type, const char *mount_path, | |
207 | const char *outgoing_root_path, const bool rooted_dmg) | |
208 | { | |
209 | int error; | |
210 | boolean_t authenticated_dmg_chunklist = false; | |
211 | vnode_t mount_vp = NULLVP; | |
212 | errno_t rootauth; | |
213 | ||
214 | ||
215 | if (type != IMAGEBOOT_DMG) { | |
216 | panic("not supported"); | |
217 | } | |
218 | ||
219 | /* | |
220 | * Check that the image file actually exists. | |
221 | * We also need to find the mount it's on, to mark it as backing the | |
222 | * root. | |
223 | */ | |
224 | vnode_t imagevp = NULLVP; | |
225 | error = vnode_lookup(image_path, 0, &imagevp, vfs_context_kernel()); | |
226 | if (error) { | |
227 | printf("%s: image file not found or couldn't be read: %d\n", __FUNCTION__, error); | |
228 | /* | |
229 | * bail out here to short-circuit out of panic logic below. | |
230 | * Failure to find the pivot-image should not be a fatal condition (ENOENT) | |
231 | * since it may result in natural consequences (ergo, cannot unlock filevault prompt). | |
232 | */ | |
233 | return error; | |
234 | } | |
235 | ||
236 | /* | |
237 | * load the disk image and obtain its device. | |
238 | * di_root_image's name and the names of its arguments suggest it has | |
239 | * to be mounted at the root, but that's not actually needed. | |
240 | * We just need to obtain the device info. | |
241 | */ | |
242 | ||
243 | dev_t dev; | |
244 | char devname[DEVMAXNAMESIZE]; | |
245 | ||
246 | error = di_root_image_ext(image_path, devname, DEVMAXNAMESIZE, &dev, true); | |
247 | if (error) { | |
248 | panic("%s: di_root_image failed: %d\n", __FUNCTION__, error); | |
249 | } | |
250 | ||
251 | printf("%s: attached disk image %s as %s\n", __FUNCTION__, image_path, devname); | |
252 | ||
253 | ||
254 | #if CONFIG_IMAGEBOOT_CHUNKLIST | |
255 | if ((rooted_dmg == false) && !IOBaseSystemARVRootHashAvailable()) { | |
256 | error = authenticate_root_with_chunklist(image_path, NULL); | |
257 | if (error == 0) { | |
258 | printf("authenticated root-dmg via chunklist...\n"); | |
259 | authenticated_dmg_chunklist = true; | |
260 | } else { | |
261 | /* root hash was not available, and image is NOT chunklisted? */ | |
262 | printf("failed to chunklist-authenticate root-dmg @ %s\n", image_path); | |
263 | } | |
264 | } | |
265 | #endif | |
266 | ||
267 | char fulldevname[DEVMAXNAMESIZE + 5]; // "/dev/" | |
268 | strlcpy(fulldevname, "/dev/", sizeof(fulldevname)); | |
269 | strlcat(fulldevname, devname, sizeof(fulldevname)); | |
270 | ||
271 | /* | |
272 | * mount expects another layer of indirection (because it expects to | |
273 | * be getting a user_addr_t of a char *. | |
274 | * Make a pointer-to-pointer on our stack. It won't use this | |
275 | * address after it returns so this should be safe. | |
276 | */ | |
277 | char *fulldevnamep = &(fulldevname[0]); | |
278 | char **fulldevnamepp = &fulldevnamep; | |
279 | ||
280 | #define PIVOTMNT "/System/Volumes/BaseSystem" | |
281 | ||
282 | ||
283 | /* Attempt to mount as HFS; if it fails, then try as APFS */ | |
284 | printf("%s: attempting to mount as hfs...\n", __FUNCTION__); | |
285 | error = kernel_mount("hfs", NULLVP, NULLVP, PIVOTMNT, fulldevnamepp, 0, (MNT_RDONLY | MNT_DONTBROWSE), (KERNEL_MOUNT_NOAUTH | KERNEL_MOUNT_BASESYSTEMROOT), vfs_context_kernel()); | |
286 | if (error) { | |
287 | printf("mount failed: %d\n", error); | |
288 | printf("%s: attempting to mount as apfs...\n", __FUNCTION__); | |
289 | error = kernel_mount("apfs", NULLVP, NULLVP, PIVOTMNT, fulldevnamepp, 0, (MNT_RDONLY | MNT_DONTBROWSE), (KERNEL_MOUNT_NOAUTH | KERNEL_MOUNT_BASESYSTEMROOT), vfs_context_kernel()); | |
290 | } | |
291 | ||
292 | /* If we didn't mount as either HFS or APFS, then bail out */ | |
293 | if (error) { | |
294 | /* | |
295 | * Note that for this particular failure case (failure to mount), the disk image | |
296 | * being attached may have failed to quiesce within the alloted time out (20-30 sec). | |
297 | * For example, it may be still probing, or APFS container enumeration may have not | |
298 | * completed. If so, then we may have fallen into this particular error case. However, | |
299 | * failure to complete matching should be an exceptional case as 30 sec. is quite a | |
300 | * long time to wait for matching to complete (which would have occurred in | |
301 | * di_root_image_ext). | |
302 | */ | |
303 | #if defined(__arm64__) && XNU_TARGET_OS_OSX | |
304 | panic("%s: failed to mount pivot image(%d)!", __FUNCTION__, error); | |
305 | #endif | |
306 | printf("%s: failed to mount pivot image(%d) !", __FUNCTION__, error); | |
307 | goto done; | |
308 | } | |
309 | ||
310 | /* otherwise, if the mount succeeded, then assert that the DMG is authenticated (either chunklist or authapfs) */ | |
311 | error = vnode_lookup(PIVOTMNT, 0, &mount_vp, vfs_context_kernel()); | |
312 | if (error) { | |
313 | #if defined(__arm64__) && XNU_TARGET_OS_OSX | |
314 | panic("%s: failed to lookup pivot root (%d) !", __FUNCTION__, error); | |
315 | #endif | |
316 | printf("%s: failed to lookup pivot root (%d)!", __FUNCTION__, error); | |
317 | goto done; | |
318 | } | |
319 | ||
320 | /* the 0x1 implies base system */ | |
321 | rootauth = VNOP_IOCTL(mount_vp, FSIOC_KERNEL_ROOTAUTH, (caddr_t)0x1, 0, vfs_context_kernel()); | |
322 | if (rootauth) { | |
323 | printf("BS-DMG failed to authenticate intra-FS \n"); | |
324 | /* | |
325 | * If we are using a custom rooted DMG, or if we have already authenticated | |
326 | * the DMG via chunklist, then it is permissible to use. | |
327 | */ | |
328 | if (rooted_dmg || authenticated_dmg_chunklist) { | |
329 | rootauth = 0; | |
330 | } | |
331 | error = rootauth; | |
332 | } | |
333 | vnode_put(mount_vp); | |
334 | mount_vp = NULLVP; | |
335 | ||
336 | if (error) { | |
337 | /* | |
338 | * Failure here exclusively means that the mount failed to authenticate. | |
339 | * This means that the disk image either was not sealed (authapfs), or it was | |
340 | * not hosted on a chunklisted DMG. Both scenarios may be fatal depending | |
341 | * on the platform. | |
342 | */ | |
343 | #if defined(__arm64__) && XNU_TARGET_OS_OSX | |
344 | panic("%s: could not authenticate the pivot image: %d. giving up.\n", __FUNCTION__, error); | |
345 | #endif | |
346 | printf("%s: could not authenticate the pivot image: %d. giving up.\n", __FUNCTION__, error); | |
347 | goto done; | |
348 | } | |
349 | ||
350 | if (rootvnode) { | |
351 | mount_t root_mp = vnode_mount(rootvnode); | |
352 | if (root_mp && (root_mp->mnt_kern_flag & MNTK_SSD)) { | |
353 | rootvp_is_ssd = true; | |
354 | } | |
355 | } | |
356 | /* | |
357 | * pivot the incoming and outgoing filesystems | |
358 | */ | |
359 | error = vfs_switch_root(mount_path, outgoing_root_path, 0); | |
360 | if (error) { | |
361 | panic("%s: vfs_switch_root failed: %d\n", __FUNCTION__, error); | |
362 | } | |
363 | ||
364 | /* | |
365 | * Mark the filesystem containing the image as backing root, so it | |
366 | * won't be unmountable. | |
367 | * | |
368 | * vfs_switch_root() clears this flag, so we have to set it after | |
369 | * the pivot call. | |
370 | * If the system later pivots out of the image, vfs_switch_root | |
371 | * will clear it again, so the backing filesystem can be unmounted. | |
372 | */ | |
373 | mount_t imagemp = imagevp->v_mount; | |
374 | lck_rw_lock_exclusive(&imagemp->mnt_rwlock); | |
375 | imagemp->mnt_kern_flag |= MNTK_BACKS_ROOT; | |
376 | lck_rw_done(&imagemp->mnt_rwlock); | |
377 | ||
378 | error = 0; | |
379 | ||
380 | /* | |
381 | * Note that we do NOT change kern.bootuuid here - | |
382 | * imageboot_mount_image() does, but imageboot_pivot_image() doesn't. | |
383 | * imageboot_mount_image() is used when the root volume uuid was | |
384 | * "always supposed to be" the one inside the dmg. imageboot_pivot_ | |
385 | * image() is used when the true root volume just needs to be | |
386 | * obscured for a moment by the dmg. | |
387 | */ | |
388 | ||
389 | done: | |
390 | if (imagevp != NULLVP) { | |
391 | vnode_put(imagevp); | |
392 | } | |
393 | return error; | |
394 | } | |
395 | ||
396 | /* kern_sysctl.c */ | |
397 | extern uuid_string_t fake_bootuuid; | |
398 | ||
399 | static void | |
400 | set_fake_bootuuid(mount_t mp) | |
401 | { | |
402 | struct vfs_attr va; | |
403 | VFSATTR_INIT(&va); | |
404 | VFSATTR_WANTED(&va, f_uuid); | |
405 | ||
406 | if (vfs_getattr(mp, &va, vfs_context_current()) != 0) { | |
407 | return; | |
408 | } | |
409 | ||
410 | if (!VFSATTR_IS_SUPPORTED(&va, f_uuid)) { | |
411 | return; | |
412 | } | |
413 | ||
414 | uuid_unparse(va.f_uuid, fake_bootuuid); | |
415 | } | |
2d21ac55 A |
416 | |
417 | /* | |
6d2010ae A |
418 | * Swaps in new root filesystem based on image path. |
419 | * Current root filesystem is removed from mount list and | |
0a7de745 A |
420 | * tagged MNTK_BACKS_ROOT, MNT_ROOTFS is cleared on it, and |
421 | * "rootvnode" is reset. Root vnode of currentroot filesystem | |
6d2010ae | 422 | * is returned with usecount (no iocount). |
f427ee49 A |
423 | * kern.bootuuid is arranged to return the UUID of the mounted image. (If |
424 | * we did nothing here, it would be the UUID of the image source volume.) | |
2d21ac55 | 425 | */ |
6d2010ae | 426 | __private_extern__ int |
cb323159 | 427 | imageboot_mount_image(const char *root_path, int height, imageboot_type_t type) |
2d21ac55 | 428 | { |
0a7de745 A |
429 | dev_t dev; |
430 | int error; | |
cb323159 A |
431 | /* |
432 | * Need to stash this here since we may do a kernel_mount() on /, which will | |
433 | * automatically update the rootvnode global. Note that vfs_mountroot() does | |
434 | * not update that global, which is a bit weird. | |
435 | */ | |
436 | vnode_t old_rootvnode = rootvnode; | |
0a7de745 A |
437 | vnode_t newdp; |
438 | mount_t new_rootfs; | |
cb323159 | 439 | boolean_t update_rootvnode = FALSE; |
2d21ac55 | 440 | |
cb323159 A |
441 | if (type == IMAGEBOOT_DMG) { |
442 | error = di_root_image(root_path, rootdevice, DEVMAXNAMESIZE, &dev); | |
443 | if (error) { | |
444 | panic("%s: di_root_image failed: %d\n", __FUNCTION__, error); | |
445 | } | |
446 | ||
447 | rootdev = dev; | |
448 | mountroot = NULL; | |
449 | printf("%s: root device 0x%x\n", __FUNCTION__, rootdev); | |
450 | error = vfs_mountroot(); | |
451 | if (error != 0) { | |
452 | panic("vfs_mountroot() failed.\n"); | |
453 | } | |
454 | ||
455 | update_rootvnode = TRUE; | |
2d21ac55 | 456 | } |
cb323159 A |
457 | #if CONFIG_LOCKERBOOT |
458 | else if (type == IMAGEBOOT_LOCKER) { | |
f427ee49 A |
459 | locker_mount_args_t *mntargs = kheap_alloc(KHEAP_TEMP, |
460 | sizeof(*mntargs), Z_WAITOK); | |
cb323159 A |
461 | if (!mntargs) { |
462 | panic("could not alloc mount args"); | |
463 | } | |
2d21ac55 | 464 | |
cb323159 A |
465 | strlcpy(mntargs->lmnt_path, root_path, sizeof(mntargs->lmnt_path)); |
466 | mntargs->lmnt_preferred_hash = 0; | |
467 | ||
468 | DBG_TRACE("%s: mounting locker: %s\n", __FUNCTION__, root_path); | |
469 | error = kernel_mount(LOCKERFS_NAME, NULLVP, NULLVP, "/", | |
470 | mntargs, sizeof(*mntargs), 0, 0, vfs_context_kernel()); | |
471 | if (error) { | |
472 | panic("failed to mount locker: %d", error); | |
473 | } | |
f427ee49 | 474 | kheap_free(KHEAP_TEMP, mntargs, sizeof(*mntargs)); |
cb323159 A |
475 | |
476 | /* Clear the old mount association. */ | |
477 | old_rootvnode->v_mountedhere = NULL; | |
478 | rootvnode->v_mount->mnt_vnodecovered = NULL; | |
479 | } | |
480 | #endif | |
481 | else { | |
482 | panic("invalid imageboot type: %d", type); | |
6d2010ae | 483 | } |
2d21ac55 | 484 | |
6d2010ae A |
485 | /* |
486 | * Get the vnode for '/'. | |
487 | * Set fdp->fd_fd.fd_cdir to reference it. | |
488 | */ | |
0a7de745 | 489 | if (VFS_ROOT(TAILQ_LAST(&mountlist, mntlist), &newdp, vfs_context_kernel())) { |
6d2010ae | 490 | panic("%s: cannot find root vnode", __FUNCTION__); |
0a7de745 | 491 | } |
cb323159 | 492 | DBG_TRACE("%s: old root fsname: %s\n", __FUNCTION__, old_rootvnode->v_mount->mnt_vtable->vfc_name); |
2d21ac55 | 493 | |
cb323159 | 494 | if (old_rootvnode != NULL) { |
6d2010ae | 495 | /* remember the old rootvnode, but remove it from mountlist */ |
cb323159 | 496 | mount_t old_rootfs = old_rootvnode->v_mount; |
0a7de745 | 497 | |
b7266188 | 498 | mount_list_remove(old_rootfs); |
b7266188 | 499 | mount_lock(old_rootfs); |
b7266188 | 500 | old_rootfs->mnt_kern_flag |= MNTK_BACKS_ROOT; |
b7266188 A |
501 | old_rootfs->mnt_flag &= ~MNT_ROOTFS; |
502 | mount_unlock(old_rootfs); | |
6d2010ae | 503 | } |
b7266188 | 504 | |
f427ee49 A |
505 | vnode_ref(newdp); |
506 | vnode_put(newdp); | |
507 | ||
508 | lck_rw_lock_exclusive(rootvnode_rw_lock); | |
6d2010ae | 509 | /* switch to the new rootvnode */ |
cb323159 A |
510 | if (update_rootvnode) { |
511 | rootvnode = newdp; | |
f427ee49 | 512 | set_fake_bootuuid(rootvnode->v_mount); |
cb323159 | 513 | } |
b7266188 | 514 | |
6d2010ae A |
515 | new_rootfs = rootvnode->v_mount; |
516 | mount_lock(new_rootfs); | |
517 | new_rootfs->mnt_flag |= MNT_ROOTFS; | |
518 | mount_unlock(new_rootfs); | |
b7266188 | 519 | |
6d2010ae | 520 | filedesc0.fd_cdir = newdp; |
f427ee49 A |
521 | lck_rw_unlock_exclusive(rootvnode_rw_lock); |
522 | ||
6d2010ae | 523 | DBG_TRACE("%s: root switched\n", __FUNCTION__); |
b7266188 | 524 | |
6d2010ae | 525 | if (old_rootvnode != NULL) { |
b7266188 | 526 | #ifdef CONFIG_IMGSRC_ACCESS |
0a7de745 A |
527 | if (height >= 0 && PE_imgsrc_mount_supported()) { |
528 | imgsrc_rootvnodes[height] = old_rootvnode; | |
529 | } else { | |
530 | vnode_get_and_drop_always(old_rootvnode); | |
531 | } | |
532 | #else | |
cb323159 | 533 | #pragma unused(height) |
6d2010ae | 534 | vnode_get_and_drop_always(old_rootvnode); |
b7266188 | 535 | #endif /* CONFIG_IMGSRC_ACCESS */ |
6d2010ae A |
536 | } |
537 | return 0; | |
538 | } | |
b7266188 | 539 | |
f427ee49 A |
540 | /* |
541 | * Return a memory object for given file path. | |
542 | * Also returns a vnode reference for the given file path. | |
543 | */ | |
544 | void * | |
545 | ubc_getobject_from_filename(const char *filename, struct vnode **vpp, off_t *file_size) | |
546 | { | |
547 | int err = 0; | |
548 | struct nameidata ndp = {}; | |
549 | struct vnode *vp = NULL; | |
550 | off_t fsize = 0; | |
551 | vfs_context_t ctx = vfs_context_kernel(); | |
552 | void *control = NULL; | |
553 | ||
554 | NDINIT(&ndp, LOOKUP, OP_OPEN, LOCKLEAF, UIO_SYSSPACE, CAST_USER_ADDR_T(filename), ctx); | |
555 | if ((err = namei(&ndp)) != 0) { | |
556 | goto errorout; | |
557 | } | |
558 | nameidone(&ndp); | |
559 | vp = ndp.ni_vp; | |
560 | ||
561 | if ((err = vnode_size(vp, &fsize, ctx)) != 0) { | |
562 | goto errorout; | |
563 | } | |
564 | ||
565 | if (fsize < 0) { | |
566 | goto errorout; | |
567 | } | |
568 | ||
569 | control = ubc_getobject(vp, UBC_FLAGS_NONE); | |
570 | if (control == NULL) { | |
571 | goto errorout; | |
572 | } | |
573 | ||
574 | *file_size = fsize; | |
575 | *vpp = vp; | |
576 | vp = NULL; | |
577 | ||
578 | errorout: | |
579 | if (vp) { | |
580 | vnode_put(vp); | |
581 | } | |
582 | return control; | |
583 | } | |
584 | ||
cb323159 | 585 | int |
f427ee49 | 586 | imageboot_read_file_from_offset(kalloc_heap_t kheap, const char *path, off_t offset, void **bufp, size_t *bufszp) |
813fb2f6 A |
587 | { |
588 | int err = 0; | |
589 | struct nameidata ndp = {}; | |
590 | struct vnode *vp = NULL; | |
591 | off_t fsize = 0; | |
592 | int resid = 0; | |
593 | char *buf = NULL; | |
594 | bool doclose = false; | |
595 | ||
596 | vfs_context_t ctx = vfs_context_kernel(); | |
597 | proc_t p = vfs_context_proc(ctx); | |
598 | kauth_cred_t kerncred = vfs_context_ucred(ctx); | |
599 | ||
5ba3f43e | 600 | NDINIT(&ndp, LOOKUP, OP_OPEN, LOCKLEAF, UIO_SYSSPACE, CAST_USER_ADDR_T(path), ctx); |
813fb2f6 | 601 | if ((err = namei(&ndp)) != 0) { |
cb323159 | 602 | AUTHPRNT("namei failed (%s) - %d", path, err); |
813fb2f6 A |
603 | goto out; |
604 | } | |
605 | nameidone(&ndp); | |
606 | vp = ndp.ni_vp; | |
607 | ||
608 | if ((err = vnode_size(vp, &fsize, ctx)) != 0) { | |
cb323159 | 609 | AUTHPRNT("failed to get vnode size of %s - %d", path, err); |
813fb2f6 A |
610 | goto out; |
611 | } | |
612 | if (fsize < 0) { | |
613 | panic("negative file size"); | |
614 | } | |
615 | ||
616 | if ((err = VNOP_OPEN(vp, FREAD, ctx)) != 0) { | |
cb323159 | 617 | AUTHPRNT("failed to open %s - %d", path, err); |
813fb2f6 A |
618 | goto out; |
619 | } | |
620 | doclose = true; | |
621 | ||
622 | /* if bufsz is non-zero, cap the read at bufsz bytes */ | |
623 | if (*bufszp && *bufszp < (size_t)fsize) { | |
624 | fsize = *bufszp; | |
625 | } | |
626 | ||
f427ee49 A |
627 | fsize = (off_t)MIN((size_t)fsize, INT_MAX); |
628 | ||
629 | buf = kheap_alloc(kheap, (size_t)fsize, Z_WAITOK); | |
813fb2f6 A |
630 | if (buf == NULL) { |
631 | err = ENOMEM; | |
632 | goto out; | |
633 | } | |
634 | ||
f427ee49 A |
635 | if ((err = vn_rdwr(UIO_READ, vp, (caddr_t)buf, (int)fsize, offset, UIO_SYSSPACE, IO_NODELOCKED, kerncred, &resid, p)) != 0) { |
636 | AUTHPRNT("Cannot read %d bytes at offset %d from %s - %d", (int)fsize, (int)offset, path, err); | |
813fb2f6 A |
637 | goto out; |
638 | } | |
639 | ||
640 | if (resid) { | |
641 | /* didnt get everything we wanted */ | |
f427ee49 | 642 | AUTHPRNT("Short read of %d bytes at offset %d from %s - %d", (int)fsize, (int)offset, path, resid); |
813fb2f6 A |
643 | err = EINVAL; |
644 | goto out; | |
645 | } | |
646 | ||
647 | out: | |
648 | if (doclose) { | |
649 | VNOP_CLOSE(vp, FREAD, ctx); | |
650 | } | |
651 | if (vp) { | |
652 | vnode_put(vp); | |
653 | vp = NULL; | |
654 | } | |
655 | ||
656 | if (err) { | |
f427ee49 | 657 | kheap_free_safe(kheap, buf, (size_t)fsize); |
813fb2f6 A |
658 | } else { |
659 | *bufp = buf; | |
f427ee49 | 660 | *bufszp = (size_t)fsize; |
813fb2f6 A |
661 | } |
662 | ||
663 | return err; | |
664 | } | |
665 | ||
f427ee49 A |
666 | int |
667 | imageboot_read_file(kalloc_heap_t kheap, const char *path, void **bufp, size_t *bufszp) | |
668 | { | |
669 | return imageboot_read_file_from_offset(kheap, path, 0, bufp, bufszp); | |
670 | } | |
671 | ||
cb323159 A |
672 | #if CONFIG_IMAGEBOOT_IMG4 || CONFIG_IMAGEBOOT_CHUNKLIST |
673 | vnode_t | |
674 | imgboot_get_image_file(const char *path, off_t *fsize, int *errp) | |
813fb2f6 | 675 | { |
813fb2f6 | 676 | struct nameidata ndp = {}; |
cb323159 | 677 | vnode_t vp = NULL; |
813fb2f6 | 678 | vfs_context_t ctx = vfs_context_kernel(); |
cb323159 | 679 | int err; |
813fb2f6 | 680 | |
cb323159 | 681 | NDINIT(&ndp, LOOKUP, OP_OPEN, LOCKLEAF, UIO_SYSSPACE, CAST_USER_ADDR_T(path), ctx); |
813fb2f6 | 682 | if ((err = namei(&ndp)) != 0) { |
cb323159 A |
683 | AUTHPRNT("Cannot find %s - error %d", path, err); |
684 | } else { | |
685 | nameidone(&ndp); | |
686 | vp = ndp.ni_vp; | |
813fb2f6 | 687 | |
cb323159 | 688 | if (vp->v_type != VREG) { |
813fb2f6 | 689 | err = EINVAL; |
cb323159 A |
690 | AUTHPRNT("%s it not a regular file", path); |
691 | } else if (fsize) { | |
692 | if ((err = vnode_size(vp, fsize, ctx)) != 0) { | |
693 | AUTHPRNT("Cannot get file size of %s - error %d", path, err); | |
694 | } | |
813fb2f6 | 695 | } |
813fb2f6 A |
696 | } |
697 | ||
cb323159 A |
698 | if (err) { |
699 | *errp = err; | |
813fb2f6 A |
700 | vp = NULL; |
701 | } | |
cb323159 | 702 | return vp; |
813fb2f6 | 703 | } |
cb323159 | 704 | #endif /* CONFIG_IMAGEBOOT_CHUNKLIST || CONFIG_IMAGEBOOT_CHUNKLIST */ |
813fb2f6 | 705 | |
cb323159 | 706 | #if CONFIG_IMAGEBOOT_IMG4 |
813fb2f6 | 707 | |
cb323159 | 708 | #define APTICKET_NAME "apticket.der" |
813fb2f6 | 709 | |
cb323159 | 710 | static char * |
f427ee49 | 711 | imgboot_get_apticket_path(const char *rootpath, size_t *sz) |
cb323159 | 712 | { |
f427ee49 A |
713 | size_t plen = strlen(rootpath) + sizeof(APTICKET_NAME) + 1; |
714 | char *path = kheap_alloc(KHEAP_TEMP, plen, Z_WAITOK); | |
813fb2f6 | 715 | |
cb323159 A |
716 | if (path) { |
717 | char *slash; | |
813fb2f6 | 718 | |
cb323159 A |
719 | strlcpy(path, rootpath, plen); |
720 | slash = strrchr(path, '/'); | |
721 | if (slash == NULL) { | |
722 | slash = path; | |
723 | } else { | |
724 | slash++; | |
725 | } | |
726 | strlcpy(slash, APTICKET_NAME, sizeof(APTICKET_NAME) + 1); | |
813fb2f6 | 727 | } |
f427ee49 A |
728 | |
729 | *sz = plen; | |
cb323159 | 730 | return path; |
813fb2f6 A |
731 | } |
732 | ||
733 | static int | |
cb323159 | 734 | authenticate_root_with_img4(const char *rootpath) |
813fb2f6 | 735 | { |
cb323159 | 736 | errno_t rv; |
cb323159 | 737 | vnode_t vp; |
f427ee49 | 738 | size_t ticket_pathsz = 0; |
cb323159 | 739 | char *ticket_path; |
f427ee49 A |
740 | img4_buff_t tck = IMG4_BUFF_INIT; |
741 | img4_firmware_execution_context_t exec = { | |
742 | .i4fex_version = IMG4_FIRMWARE_EXECUTION_CONTEXT_STRUCT_VERSION, | |
743 | .i4fex_execute = NULL, | |
744 | .i4fex_context = NULL, | |
745 | }; | |
746 | img4_firmware_t fw = NULL; | |
747 | img4_firmware_flags_t fw_flags = IMG4_FIRMWARE_FLAG_BARE | | |
748 | IMG4_FIRMWARE_FLAG_SUBSEQUENT_STAGE; | |
813fb2f6 | 749 | |
cb323159 | 750 | DBG_TRACE("Check %s\n", rootpath); |
813fb2f6 | 751 | |
cb323159 A |
752 | if (img4if == NULL) { |
753 | AUTHPRNT("AppleImage4 is not ready"); | |
754 | return EAGAIN; | |
813fb2f6 A |
755 | } |
756 | ||
f427ee49 | 757 | ticket_path = imgboot_get_apticket_path(rootpath, &ticket_pathsz); |
cb323159 A |
758 | if (ticket_path == NULL) { |
759 | AUTHPRNT("Cannot construct ticket path - out of memory"); | |
760 | return ENOMEM; | |
813fb2f6 A |
761 | } |
762 | ||
f427ee49 | 763 | rv = imageboot_read_file(KHEAP_TEMP, ticket_path, (void **)&tck.i4b_bytes, &tck.i4b_len); |
cb323159 A |
764 | if (rv) { |
765 | AUTHPRNT("Cannot get a ticket from %s - %d\n", ticket_path, rv); | |
766 | goto out_with_ticket_path; | |
813fb2f6 A |
767 | } |
768 | ||
f427ee49 | 769 | DBG_TRACE("Got %lu bytes of manifest from %s\n", tck.i4b_len, ticket_path); |
813fb2f6 | 770 | |
cb323159 A |
771 | vp = imgboot_get_image_file(rootpath, NULL, &rv); |
772 | if (vp == NULL) { | |
773 | /* Error message had been printed already */ | |
f427ee49 A |
774 | rv = EIO; |
775 | goto out_with_ticket_bytes; | |
813fb2f6 A |
776 | } |
777 | ||
f427ee49 A |
778 | fw = img4_firmware_new_from_vnode_4xnu(IMG4_RUNTIME_DEFAULT, &exec, 'rosi', |
779 | vp, fw_flags); | |
780 | if (!fw) { | |
781 | AUTHPRNT("Could not allocate new firmware"); | |
782 | rv = ENOMEM; | |
783 | goto out_with_ticket_bytes; | |
813fb2f6 A |
784 | } |
785 | ||
f427ee49 A |
786 | img4_firmware_attach_manifest(fw, &tck); |
787 | rv = img4_firmware_evaluate(fw, img4_chip_select_personalized_ap(), NULL); | |
813fb2f6 | 788 | |
cb323159 | 789 | out_with_ticket_bytes: |
f427ee49 | 790 | kheap_free_safe(KHEAP_TEMP, tck.i4b_bytes, tck.i4b_len); |
cb323159 | 791 | out_with_ticket_path: |
f427ee49 A |
792 | kheap_free_safe(KHEAP_TEMP, ticket_path, ticket_pathsz); |
793 | ||
794 | img4_firmware_destroy(&fw); | |
cb323159 | 795 | return rv; |
813fb2f6 | 796 | } |
cb323159 | 797 | #endif /* CONFIG_IMAGEBOOT_IMG4 */ |
813fb2f6 | 798 | |
813fb2f6 | 799 | |
5ba3f43e A |
800 | /* |
801 | * Attach the image at 'path' as a ramdisk and mount it as our new rootfs. | |
802 | * All existing mounts are first umounted. | |
803 | */ | |
804 | static int | |
805 | imageboot_mount_ramdisk(const char *path) | |
806 | { | |
807 | int err = 0; | |
808 | size_t bufsz = 0; | |
809 | void *buf = NULL; | |
810 | dev_t dev; | |
811 | vnode_t newdp; | |
f427ee49 | 812 | vnode_t tvp; |
5ba3f43e A |
813 | mount_t new_rootfs; |
814 | ||
815 | /* Read our target image from disk */ | |
f427ee49 | 816 | err = imageboot_read_file(KHEAP_DATA_BUFFERS, path, &buf, &bufsz); |
5ba3f43e | 817 | if (err) { |
f427ee49 | 818 | printf("%s: failed: imageboot_read_file() = %d\n", __func__, err); |
5ba3f43e A |
819 | goto out; |
820 | } | |
821 | DBG_TRACE("%s: read '%s' sz = %lu\n", __func__, path, bufsz); | |
822 | ||
823 | #if CONFIG_IMGSRC_ACCESS | |
824 | /* Re-add all root mounts to the mount list in the correct order... */ | |
825 | mount_list_remove(rootvnode->v_mount); | |
826 | for (int i = 0; i < MAX_IMAGEBOOT_NESTING; i++) { | |
827 | struct vnode *vn = imgsrc_rootvnodes[i]; | |
828 | if (vn) { | |
829 | vnode_getalways(vn); | |
830 | imgsrc_rootvnodes[i] = NULLVP; | |
831 | ||
832 | mount_t mnt = vn->v_mount; | |
833 | mount_lock(mnt); | |
834 | mnt->mnt_flag |= MNT_ROOTFS; | |
835 | mount_list_add(mnt); | |
836 | mount_unlock(mnt); | |
837 | ||
838 | vnode_rele(vn); | |
839 | vnode_put(vn); | |
840 | } | |
841 | } | |
842 | mount_list_add(rootvnode->v_mount); | |
843 | #endif | |
844 | ||
845 | /* ... and unmount everything */ | |
f427ee49 A |
846 | vfs_unmountall(); |
847 | ||
848 | lck_rw_lock_exclusive(rootvnode_rw_lock); | |
5ba3f43e | 849 | filedesc0.fd_cdir = NULL; |
f427ee49 | 850 | tvp = rootvnode; |
5ba3f43e | 851 | rootvnode = NULL; |
f427ee49 A |
852 | rootvp = NULLVP; |
853 | rootdev = NODEV; | |
854 | lck_rw_unlock_exclusive(rootvnode_rw_lock); | |
855 | vnode_get_and_drop_always(tvp); | |
5ba3f43e A |
856 | |
857 | /* Attach the ramfs image ... */ | |
858 | err = di_root_ramfile_buf(buf, bufsz, rootdevice, DEVMAXNAMESIZE, &dev); | |
859 | if (err) { | |
860 | printf("%s: failed: di_root_ramfile_buf() = %d\n", __func__, err); | |
861 | goto out; | |
862 | } | |
863 | ||
864 | /* ... and mount it */ | |
865 | rootdev = dev; | |
866 | mountroot = NULL; | |
867 | err = vfs_mountroot(); | |
868 | if (err) { | |
869 | printf("%s: failed: vfs_mountroot() = %d\n", __func__, err); | |
870 | goto out; | |
871 | } | |
872 | ||
873 | /* Switch to new root vnode */ | |
0a7de745 | 874 | if (VFS_ROOT(TAILQ_LAST(&mountlist, mntlist), &newdp, vfs_context_kernel())) { |
5ba3f43e A |
875 | panic("%s: cannot find root vnode", __func__); |
876 | } | |
f427ee49 A |
877 | vnode_ref(newdp); |
878 | ||
879 | lck_rw_lock_exclusive(rootvnode_rw_lock); | |
5ba3f43e A |
880 | rootvnode = newdp; |
881 | rootvnode->v_flag |= VROOT; | |
882 | new_rootfs = rootvnode->v_mount; | |
883 | mount_lock(new_rootfs); | |
884 | new_rootfs->mnt_flag |= MNT_ROOTFS; | |
885 | mount_unlock(new_rootfs); | |
886 | ||
f427ee49 A |
887 | set_fake_bootuuid(new_rootfs); |
888 | ||
5ba3f43e | 889 | filedesc0.fd_cdir = newdp; |
f427ee49 A |
890 | lck_rw_unlock_exclusive(rootvnode_rw_lock); |
891 | ||
892 | vnode_put(newdp); | |
5ba3f43e A |
893 | |
894 | DBG_TRACE("%s: root switched\n", __func__); | |
895 | ||
896 | out: | |
897 | if (err) { | |
f427ee49 | 898 | kheap_free_safe(KHEAP_DATA_BUFFERS, buf, bufsz); |
5ba3f43e A |
899 | } |
900 | return err; | |
901 | } | |
902 | ||
cb323159 A |
903 | /* |
904 | * If the path is in <file://> URL format then we allocate memory and decode it, | |
905 | * otherwise return the same pointer. | |
906 | * | |
907 | * Caller is expected to check if the pointers are different. | |
908 | */ | |
909 | static char * | |
f427ee49 | 910 | url_to_path(char *url_path, size_t *sz) |
cb323159 A |
911 | { |
912 | char *path = url_path; | |
913 | size_t len = strlen(kIBFilePrefix); | |
914 | ||
915 | if (strncmp(kIBFilePrefix, url_path, len) == 0) { | |
916 | /* its a URL - remove the file:// prefix and percent-decode */ | |
917 | url_path += len; | |
918 | ||
919 | len = strlen(url_path); | |
920 | if (len) { | |
921 | /* Make a copy of the path to URL-decode */ | |
f427ee49 | 922 | path = kheap_alloc(KHEAP_TEMP, len + 1, Z_WAITOK); |
cb323159 A |
923 | if (path == NULL) { |
924 | panic("imageboot path allocation failed - cannot allocate %d bytes\n", (int)len); | |
925 | } | |
926 | ||
927 | strlcpy(path, url_path, len + 1); | |
f427ee49 | 928 | *sz = len + 1; |
cb323159 A |
929 | url_decode(path); |
930 | } else { | |
931 | panic("Bogus imageboot path URL - missing path\n"); | |
932 | } | |
933 | ||
934 | DBG_TRACE("%s: root image URL <%s> becomes %s\n", __func__, url_path, path); | |
935 | } | |
936 | ||
937 | return path; | |
938 | } | |
939 | ||
813fb2f6 | 940 | static boolean_t |
cb323159 | 941 | imageboot_setup_new(imageboot_type_t type) |
6d2010ae A |
942 | { |
943 | int error; | |
944 | char *root_path = NULL; | |
945 | int height = 0; | |
946 | boolean_t done = FALSE; | |
cb323159 | 947 | boolean_t auth_root = TRUE; |
5ba3f43e | 948 | boolean_t ramdisk_root = FALSE; |
6d2010ae | 949 | |
f427ee49 | 950 | root_path = zalloc(ZV_NAMEI); |
6d2010ae A |
951 | assert(root_path != NULL); |
952 | ||
cb323159 A |
953 | #if CONFIG_LOCKERBOOT |
954 | if (type == IMAGEBOOT_LOCKER) { | |
955 | if (!PE_parse_boot_argn(IMAGEBOOT_LOCKER_ARG, root_path, MAXPATHLEN)) { | |
956 | panic("locker boot with no locker given"); | |
957 | } | |
958 | ||
959 | DBG_TRACE("%s: root fsname: %s\n", __FUNCTION__, rootvnode->v_mount->mnt_vtable->vfc_name); | |
960 | ||
961 | /* | |
962 | * The locker path is a path, not a URL, so just pass it directly to | |
963 | * imageboot_mount_image(). | |
964 | */ | |
965 | error = imageboot_mount_image(root_path, 0, type); | |
966 | if (error) { | |
967 | panic("failed to mount system locker: %d", error); | |
968 | } | |
969 | ||
970 | done = TRUE; | |
971 | goto out; | |
972 | } | |
973 | #endif /* CONFIG_LOCKERBOOT */ | |
974 | ||
5ba3f43e A |
975 | unsigned imgboot_arg; |
976 | if (PE_parse_boot_argn("-rootdmg-ramdisk", &imgboot_arg, sizeof(imgboot_arg))) { | |
977 | ramdisk_root = TRUE; | |
978 | } | |
979 | ||
813fb2f6 | 980 | if (PE_parse_boot_argn(IMAGEBOOT_CONTAINER_ARG, root_path, MAXPATHLEN) == TRUE) { |
6d2010ae | 981 | printf("%s: container image url is %s\n", __FUNCTION__, root_path); |
cb323159 | 982 | error = imageboot_mount_image(root_path, height, type); |
6d2010ae A |
983 | if (error != 0) { |
984 | panic("Failed to mount container image."); | |
985 | } | |
986 | ||
987 | height++; | |
988 | } | |
989 | ||
cb323159 A |
990 | if (PE_parse_boot_argn(IMAGEBOOT_AUTHROOT_ARG, root_path, MAXPATHLEN) == FALSE && |
991 | PE_parse_boot_argn(IMAGEBOOT_ROOT_ARG, root_path, MAXPATHLEN) == FALSE) { | |
6d2010ae | 992 | if (height > 0) { |
cb323159 | 993 | panic("%s specified without %s or %s?\n", IMAGEBOOT_CONTAINER_ARG, IMAGEBOOT_AUTHROOT_ARG, IMAGEBOOT_ROOT_ARG); |
6d2010ae A |
994 | } |
995 | goto out; | |
813fb2f6 A |
996 | } |
997 | ||
cb323159 | 998 | printf("%s: root image URL is '%s'\n", __func__, root_path); |
b7266188 | 999 | |
5ba3f43e | 1000 | /* Make a copy of the path to URL-decode */ |
f427ee49 A |
1001 | size_t pathsz; |
1002 | char *path = url_to_path(root_path, &pathsz); | |
cb323159 | 1003 | assert(path); |
6d2010ae | 1004 | |
cb323159 | 1005 | #if CONFIG_IMAGEBOOT_CHUNKLIST |
5ba3f43e | 1006 | if (auth_root) { |
f427ee49 A |
1007 | /* |
1008 | * This updates auth_root to reflect whether chunklist was | |
1009 | * actually enforced. In effect, this clears auth_root if | |
1010 | * CSR_ALLOW_ANY_RECOVERY_OS allowed an invalid image. | |
1011 | */ | |
813fb2f6 | 1012 | AUTHDBG("authenticating root image at %s", path); |
f427ee49 | 1013 | error = authenticate_root_with_chunklist(path, &auth_root); |
813fb2f6 A |
1014 | if (error) { |
1015 | panic("root image authentication failed (err = %d)\n", error); | |
1016 | } | |
1017 | AUTHDBG("successfully authenticated %s", path); | |
5ba3f43e | 1018 | } |
cb323159 | 1019 | #endif |
00867663 | 1020 | |
5ba3f43e A |
1021 | if (ramdisk_root) { |
1022 | error = imageboot_mount_ramdisk(path); | |
1023 | } else { | |
cb323159 | 1024 | error = imageboot_mount_image(root_path, height, type); |
813fb2f6 | 1025 | } |
6d2010ae | 1026 | |
cb323159 | 1027 | if (path != root_path) { |
f427ee49 | 1028 | kheap_free_safe(KHEAP_TEMP, path, pathsz); |
cb323159 | 1029 | } |
5ba3f43e A |
1030 | |
1031 | if (error) { | |
1032 | panic("Failed to mount root image (err=%d, auth=%d, ramdisk=%d)\n", | |
0a7de745 | 1033 | error, auth_root, ramdisk_root); |
6d2010ae A |
1034 | } |
1035 | ||
cb323159 | 1036 | #if CONFIG_IMAGEBOOT_CHUNKLIST |
813fb2f6 A |
1037 | if (auth_root) { |
1038 | /* check that the image version matches the running kernel */ | |
1039 | AUTHDBG("checking root image version"); | |
cb323159 | 1040 | error = authenticate_root_version_check(); |
813fb2f6 A |
1041 | if (error) { |
1042 | panic("root image version check failed"); | |
1043 | } else { | |
1044 | AUTHDBG("root image version matches kernel"); | |
1045 | } | |
1046 | } | |
cb323159 | 1047 | #endif |
813fb2f6 | 1048 | |
6d2010ae A |
1049 | done = TRUE; |
1050 | ||
1051 | out: | |
f427ee49 | 1052 | zfree(ZV_NAMEI, root_path); |
6d2010ae A |
1053 | return done; |
1054 | } | |
1055 | ||
1056 | __private_extern__ void | |
cb323159 | 1057 | imageboot_setup(imageboot_type_t type) |
6d2010ae A |
1058 | { |
1059 | int error = 0; | |
1060 | char *root_path = NULL; | |
1061 | ||
1062 | DBG_TRACE("%s: entry\n", __FUNCTION__); | |
1063 | ||
813fb2f6 | 1064 | if (rootvnode == NULL) { |
6d2010ae A |
1065 | panic("imageboot_setup: rootvnode is NULL."); |
1066 | } | |
1067 | ||
1068 | /* | |
1069 | * New boot-arg scheme: | |
cb323159 A |
1070 | * root-dmg : the dmg that will be the root filesystem, authenticated by default. |
1071 | * auth-root-dmg : same as root-dmg. | |
0a7de745 | 1072 | * container-dmg : an optional dmg that contains the root-dmg. |
cb323159 A |
1073 | * locker : the locker that will be the root filesystem -- mutually |
1074 | * exclusive with any other boot-arg. | |
6d2010ae | 1075 | */ |
cb323159 | 1076 | if (imageboot_setup_new(type)) { |
6d2010ae A |
1077 | return; |
1078 | } | |
813fb2f6 | 1079 | |
f427ee49 | 1080 | root_path = zalloc(ZV_NAMEI); |
6d2010ae A |
1081 | assert(root_path != NULL); |
1082 | ||
1083 | /* | |
1084 | * Look for outermost disk image to root from. If we're doing a nested boot, | |
1085 | * there's some sense in which the outer image never needs to be the root filesystem, | |
1086 | * but it does need very similar treatment: it must not be unmounted, needs a fake | |
0a7de745 | 1087 | * device vnode created for it, and should not show up in getfsstat() until exposed |
6d2010ae A |
1088 | * with MNT_IMGSRC. We just make it the temporary root. |
1089 | */ | |
cb323159 A |
1090 | #if CONFIG_IMAGEBOOT_IMG4 |
1091 | if (PE_parse_boot_argn("arp0", root_path, MAXPATHLEN)) { | |
f427ee49 A |
1092 | size_t pathsz; |
1093 | char *path = url_to_path(root_path, &pathsz); | |
cb323159 A |
1094 | |
1095 | assert(path); | |
1096 | ||
1097 | if (authenticate_root_with_img4(path)) { | |
1098 | panic("Root image %s does not match the manifest\n", root_path); | |
1099 | } | |
1100 | if (path != root_path) { | |
f427ee49 | 1101 | kheap_free_safe(KHEAP_TEMP, path, pathsz); |
cb323159 A |
1102 | } |
1103 | } else | |
1104 | #endif /* CONFIG_IMAGEBOOT_IMG4 */ | |
0a7de745 A |
1105 | if ((PE_parse_boot_argn("rp", root_path, MAXPATHLEN) == FALSE) && |
1106 | (PE_parse_boot_argn("rp0", root_path, MAXPATHLEN) == FALSE)) { | |
6d2010ae A |
1107 | panic("%s: no valid path to image.\n", __FUNCTION__); |
1108 | } | |
1109 | ||
cb323159 | 1110 | DBG_TRACE("%s: root image url is %s\n", __FUNCTION__, root_path); |
0a7de745 | 1111 | |
cb323159 | 1112 | error = imageboot_mount_image(root_path, 0, type); |
6d2010ae A |
1113 | if (error) { |
1114 | panic("Failed on first stage of imageboot."); | |
1115 | } | |
1116 | ||
1117 | /* | |
1118 | * See if we are rooting from a nested image | |
1119 | */ | |
0a7de745 | 1120 | if (PE_parse_boot_argn("rp1", root_path, MAXPATHLEN) == FALSE) { |
6d2010ae A |
1121 | goto done; |
1122 | } | |
0a7de745 | 1123 | |
6d2010ae A |
1124 | printf("%s: second level root image url is %s\n", __FUNCTION__, root_path); |
1125 | ||
1126 | /* | |
1127 | * If we fail to set up second image, it's not a given that we | |
0a7de745 | 1128 | * can safely root off the first. |
6d2010ae | 1129 | */ |
cb323159 | 1130 | error = imageboot_mount_image(root_path, 1, type); |
6d2010ae | 1131 | if (error) { |
0a7de745 | 1132 | panic("Failed on second stage of imageboot."); |
6d2010ae A |
1133 | } |
1134 | ||
2d21ac55 | 1135 | done: |
f427ee49 | 1136 | zfree(ZV_NAMEI, root_path); |
2d21ac55 A |
1137 | |
1138 | DBG_TRACE("%s: exit\n", __FUNCTION__); | |
1139 | ||
6d2010ae | 1140 | return; |
2d21ac55 | 1141 | } |