]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2006-2020 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/fsctl.h> | |
36 | #include <sys/filedesc.h> | |
37 | #include <sys/vnode_internal.h> | |
38 | #include <sys/imageboot.h> | |
39 | #include <kern/assert.h> | |
40 | ||
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> | |
46 | #include <miscfs/devfs/devfsdefs.h> | |
47 | #include <libkern/crypto/sha2.h> | |
48 | #include <libkern/crypto/rsa.h> | |
49 | #include <libkern/OSKextLibPrivate.h> | |
50 | #include <sys/ubc_internal.h> | |
51 | ||
52 | #if CONFIG_IMAGEBOOT_IMG4 | |
53 | #include <libkern/img4/interface.h> | |
54 | #include <img4/firmware.h> | |
55 | #endif | |
56 | ||
57 | #include <kern/kalloc.h> | |
58 | ||
59 | #include <pexpert/pexpert.h> | |
60 | #include <kern/chunklist.h> | |
61 | ||
62 | extern struct filedesc filedesc0; | |
63 | ||
64 | extern int (*mountroot)(void); | |
65 | extern char rootdevice[DEVMAXNAMESIZE]; | |
66 | ||
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 | ||
74 | #define DEBUG_IMAGEBOOT 0 | |
75 | ||
76 | #if DEBUG_IMAGEBOOT | |
77 | #define DBG_TRACE(...) printf("imageboot: " __VA_ARGS__) | |
78 | #else | |
79 | #define DBG_TRACE(...) do {} while(0) | |
80 | #endif | |
81 | ||
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) | |
84 | #define kheap_free_safe(h, x, l) do { if ((x)) { kheap_free(h, x, l); (x) = NULL; } } while (0) | |
85 | ||
86 | extern int di_root_image_ext(const char *path, char *devname, size_t devsz, dev_t *dev_p, bool removable); | |
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 | ||
90 | static boolean_t imageboot_setup_new(imageboot_type_t type); | |
91 | ||
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; | |
95 | ||
96 | #define kIBFilePrefix "file://" | |
97 | ||
98 | __private_extern__ int | |
99 | imageboot_format_is_valid(const char *root_path) | |
100 | { | |
101 | return strncmp(root_path, kIBFilePrefix, | |
102 | strlen(kIBFilePrefix)) == 0; | |
103 | } | |
104 | ||
105 | static void | |
106 | vnode_get_and_drop_always(vnode_t vp) | |
107 | { | |
108 | vnode_getalways(vp); | |
109 | vnode_rele(vp); | |
110 | vnode_put(vp); | |
111 | } | |
112 | ||
113 | __private_extern__ bool | |
114 | imageboot_desired(void) | |
115 | { | |
116 | bool do_imageboot = false; | |
117 | ||
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 | */ | |
132 | if (!(PE_parse_boot_argn("rp0", root_path, MAXPATHLEN) || | |
133 | #if CONFIG_IMAGEBOOT_IMG4 | |
134 | PE_parse_boot_argn("arp0", root_path, MAXPATHLEN) || | |
135 | #endif | |
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))) { | |
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 | } | |
151 | } | |
152 | ||
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()) { | |
166 | goto out; | |
167 | } | |
168 | ||
169 | root_path = zalloc(ZV_NAMEI); | |
170 | result = IMAGEBOOT_DMG; | |
171 | ||
172 | /* Check for second layer */ | |
173 | if (!(PE_parse_boot_argn("rp1", root_path, MAXPATHLEN) || | |
174 | PE_parse_boot_argn(IMAGEBOOT_CONTAINER_ARG, root_path, MAXPATHLEN))) { | |
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", | |
183 | __FUNCTION__, root_path); | |
184 | } | |
185 | ||
186 | out: | |
187 | if (root_path != NULL) { | |
188 | zfree(ZV_NAMEI, root_path); | |
189 | } | |
190 | return result; | |
191 | } | |
192 | ||
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 `skip_signature_check` is true, | |
203 | * then ignore the 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, const bool skip_signature_check) | |
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 | * Or, if CSR_ALLOW_ANY_RECOVERY_OS is set on Development or Debug build variant. | |
328 | */ | |
329 | if (rooted_dmg || authenticated_dmg_chunklist || skip_signature_check) { | |
330 | rootauth = 0; | |
331 | } | |
332 | error = rootauth; | |
333 | } | |
334 | vnode_put(mount_vp); | |
335 | mount_vp = NULLVP; | |
336 | ||
337 | if (error) { | |
338 | /* | |
339 | * Failure here exclusively means that the mount failed to authenticate. | |
340 | * This means that the disk image either was not sealed (authapfs), or it was | |
341 | * not hosted on a chunklisted DMG. Both scenarios may be fatal depending | |
342 | * on the platform. | |
343 | */ | |
344 | #if defined(__arm64__) && XNU_TARGET_OS_OSX | |
345 | panic("%s: could not authenticate the pivot image: %d. giving up.\n", __FUNCTION__, error); | |
346 | #endif | |
347 | printf("%s: could not authenticate the pivot image: %d. giving up.\n", __FUNCTION__, error); | |
348 | goto done; | |
349 | } | |
350 | ||
351 | if (rootvnode) { | |
352 | mount_t root_mp = vnode_mount(rootvnode); | |
353 | if (root_mp && (root_mp->mnt_kern_flag & MNTK_SSD)) { | |
354 | rootvp_is_ssd = true; | |
355 | } | |
356 | } | |
357 | /* | |
358 | * pivot the incoming and outgoing filesystems | |
359 | */ | |
360 | error = vfs_switch_root(mount_path, outgoing_root_path, 0); | |
361 | if (error) { | |
362 | panic("%s: vfs_switch_root failed: %d\n", __FUNCTION__, error); | |
363 | } | |
364 | ||
365 | /* | |
366 | * Mark the filesystem containing the image as backing root, so it | |
367 | * won't be unmountable. | |
368 | * | |
369 | * vfs_switch_root() clears this flag, so we have to set it after | |
370 | * the pivot call. | |
371 | * If the system later pivots out of the image, vfs_switch_root | |
372 | * will clear it again, so the backing filesystem can be unmounted. | |
373 | */ | |
374 | mount_t imagemp = imagevp->v_mount; | |
375 | lck_rw_lock_exclusive(&imagemp->mnt_rwlock); | |
376 | imagemp->mnt_kern_flag |= MNTK_BACKS_ROOT; | |
377 | lck_rw_done(&imagemp->mnt_rwlock); | |
378 | ||
379 | error = 0; | |
380 | ||
381 | /* | |
382 | * Note that we do NOT change kern.bootuuid here - | |
383 | * imageboot_mount_image() does, but imageboot_pivot_image() doesn't. | |
384 | * imageboot_mount_image() is used when the root volume uuid was | |
385 | * "always supposed to be" the one inside the dmg. imageboot_pivot_ | |
386 | * image() is used when the true root volume just needs to be | |
387 | * obscured for a moment by the dmg. | |
388 | */ | |
389 | ||
390 | done: | |
391 | if (imagevp != NULLVP) { | |
392 | vnode_put(imagevp); | |
393 | } | |
394 | return error; | |
395 | } | |
396 | ||
397 | /* kern_sysctl.c */ | |
398 | extern uuid_string_t fake_bootuuid; | |
399 | ||
400 | static void | |
401 | set_fake_bootuuid(mount_t mp) | |
402 | { | |
403 | struct vfs_attr va; | |
404 | VFSATTR_INIT(&va); | |
405 | VFSATTR_WANTED(&va, f_uuid); | |
406 | ||
407 | if (vfs_getattr(mp, &va, vfs_context_current()) != 0) { | |
408 | return; | |
409 | } | |
410 | ||
411 | if (!VFSATTR_IS_SUPPORTED(&va, f_uuid)) { | |
412 | return; | |
413 | } | |
414 | ||
415 | uuid_unparse(va.f_uuid, fake_bootuuid); | |
416 | } | |
417 | ||
418 | /* | |
419 | * Swaps in new root filesystem based on image path. | |
420 | * Current root filesystem is removed from mount list and | |
421 | * tagged MNTK_BACKS_ROOT, MNT_ROOTFS is cleared on it, and | |
422 | * "rootvnode" is reset. Root vnode of currentroot filesystem | |
423 | * is returned with usecount (no iocount). | |
424 | * kern.bootuuid is arranged to return the UUID of the mounted image. (If | |
425 | * we did nothing here, it would be the UUID of the image source volume.) | |
426 | */ | |
427 | __private_extern__ int | |
428 | imageboot_mount_image(const char *root_path, int height, imageboot_type_t type) | |
429 | { | |
430 | dev_t dev; | |
431 | int error; | |
432 | /* | |
433 | * Need to stash this here since we may do a kernel_mount() on /, which will | |
434 | * automatically update the rootvnode global. Note that vfs_mountroot() does | |
435 | * not update that global, which is a bit weird. | |
436 | */ | |
437 | vnode_t old_rootvnode = rootvnode; | |
438 | vnode_t newdp; | |
439 | mount_t new_rootfs; | |
440 | boolean_t update_rootvnode = FALSE; | |
441 | ||
442 | if (type == IMAGEBOOT_DMG) { | |
443 | error = di_root_image(root_path, rootdevice, DEVMAXNAMESIZE, &dev); | |
444 | if (error) { | |
445 | panic("%s: di_root_image failed: %d\n", __FUNCTION__, error); | |
446 | } | |
447 | ||
448 | rootdev = dev; | |
449 | mountroot = NULL; | |
450 | printf("%s: root device 0x%x\n", __FUNCTION__, rootdev); | |
451 | error = vfs_mountroot(); | |
452 | if (error != 0) { | |
453 | panic("vfs_mountroot() failed.\n"); | |
454 | } | |
455 | ||
456 | update_rootvnode = TRUE; | |
457 | } | |
458 | #if CONFIG_LOCKERBOOT | |
459 | else if (type == IMAGEBOOT_LOCKER) { | |
460 | locker_mount_args_t *mntargs = kheap_alloc(KHEAP_TEMP, | |
461 | sizeof(*mntargs), Z_WAITOK); | |
462 | if (!mntargs) { | |
463 | panic("could not alloc mount args"); | |
464 | } | |
465 | ||
466 | strlcpy(mntargs->lmnt_path, root_path, sizeof(mntargs->lmnt_path)); | |
467 | mntargs->lmnt_preferred_hash = 0; | |
468 | ||
469 | DBG_TRACE("%s: mounting locker: %s\n", __FUNCTION__, root_path); | |
470 | error = kernel_mount(LOCKERFS_NAME, NULLVP, NULLVP, "/", | |
471 | mntargs, sizeof(*mntargs), 0, 0, vfs_context_kernel()); | |
472 | if (error) { | |
473 | panic("failed to mount locker: %d", error); | |
474 | } | |
475 | kheap_free(KHEAP_TEMP, mntargs, sizeof(*mntargs)); | |
476 | ||
477 | /* Clear the old mount association. */ | |
478 | old_rootvnode->v_mountedhere = NULL; | |
479 | rootvnode->v_mount->mnt_vnodecovered = NULL; | |
480 | } | |
481 | #endif | |
482 | else { | |
483 | panic("invalid imageboot type: %d", type); | |
484 | } | |
485 | ||
486 | /* | |
487 | * Get the vnode for '/'. | |
488 | * Set fdp->fd_fd.fd_cdir to reference it. | |
489 | */ | |
490 | if (VFS_ROOT(TAILQ_LAST(&mountlist, mntlist), &newdp, vfs_context_kernel())) { | |
491 | panic("%s: cannot find root vnode", __FUNCTION__); | |
492 | } | |
493 | DBG_TRACE("%s: old root fsname: %s\n", __FUNCTION__, old_rootvnode->v_mount->mnt_vtable->vfc_name); | |
494 | ||
495 | if (old_rootvnode != NULL) { | |
496 | /* remember the old rootvnode, but remove it from mountlist */ | |
497 | mount_t old_rootfs = old_rootvnode->v_mount; | |
498 | ||
499 | mount_list_remove(old_rootfs); | |
500 | mount_lock(old_rootfs); | |
501 | old_rootfs->mnt_kern_flag |= MNTK_BACKS_ROOT; | |
502 | old_rootfs->mnt_flag &= ~MNT_ROOTFS; | |
503 | mount_unlock(old_rootfs); | |
504 | } | |
505 | ||
506 | vnode_ref(newdp); | |
507 | vnode_put(newdp); | |
508 | ||
509 | lck_rw_lock_exclusive(&rootvnode_rw_lock); | |
510 | /* switch to the new rootvnode */ | |
511 | if (update_rootvnode) { | |
512 | rootvnode = newdp; | |
513 | set_fake_bootuuid(rootvnode->v_mount); | |
514 | } | |
515 | ||
516 | new_rootfs = rootvnode->v_mount; | |
517 | mount_lock(new_rootfs); | |
518 | new_rootfs->mnt_flag |= MNT_ROOTFS; | |
519 | mount_unlock(new_rootfs); | |
520 | ||
521 | filedesc0.fd_cdir = newdp; | |
522 | lck_rw_unlock_exclusive(&rootvnode_rw_lock); | |
523 | ||
524 | DBG_TRACE("%s: root switched\n", __FUNCTION__); | |
525 | ||
526 | if (old_rootvnode != NULL) { | |
527 | #ifdef CONFIG_IMGSRC_ACCESS | |
528 | if (height >= 0 && PE_imgsrc_mount_supported()) { | |
529 | imgsrc_rootvnodes[height] = old_rootvnode; | |
530 | } else { | |
531 | vnode_get_and_drop_always(old_rootvnode); | |
532 | } | |
533 | #else | |
534 | #pragma unused(height) | |
535 | vnode_get_and_drop_always(old_rootvnode); | |
536 | #endif /* CONFIG_IMGSRC_ACCESS */ | |
537 | } | |
538 | return 0; | |
539 | } | |
540 | ||
541 | /* | |
542 | * Return a memory object for given file path. | |
543 | * Also returns a vnode reference for the given file path. | |
544 | */ | |
545 | void * | |
546 | ubc_getobject_from_filename(const char *filename, struct vnode **vpp, off_t *file_size) | |
547 | { | |
548 | int err = 0; | |
549 | struct nameidata ndp = {}; | |
550 | struct vnode *vp = NULL; | |
551 | off_t fsize = 0; | |
552 | vfs_context_t ctx = vfs_context_kernel(); | |
553 | void *control = NULL; | |
554 | ||
555 | NDINIT(&ndp, LOOKUP, OP_OPEN, LOCKLEAF, UIO_SYSSPACE, CAST_USER_ADDR_T(filename), ctx); | |
556 | if ((err = namei(&ndp)) != 0) { | |
557 | goto errorout; | |
558 | } | |
559 | nameidone(&ndp); | |
560 | vp = ndp.ni_vp; | |
561 | ||
562 | if ((err = vnode_size(vp, &fsize, ctx)) != 0) { | |
563 | goto errorout; | |
564 | } | |
565 | ||
566 | if (fsize < 0) { | |
567 | goto errorout; | |
568 | } | |
569 | ||
570 | control = ubc_getobject(vp, UBC_FLAGS_NONE); | |
571 | if (control == NULL) { | |
572 | goto errorout; | |
573 | } | |
574 | ||
575 | *file_size = fsize; | |
576 | *vpp = vp; | |
577 | vp = NULL; | |
578 | ||
579 | errorout: | |
580 | if (vp) { | |
581 | vnode_put(vp); | |
582 | } | |
583 | return control; | |
584 | } | |
585 | ||
586 | int | |
587 | imageboot_read_file_from_offset(kalloc_heap_t kheap, const char *path, off_t offset, void **bufp, size_t *bufszp) | |
588 | { | |
589 | int err = 0; | |
590 | struct nameidata ndp = {}; | |
591 | struct vnode *vp = NULL; | |
592 | off_t fsize = 0; | |
593 | int resid = 0; | |
594 | char *buf = NULL; | |
595 | bool doclose = false; | |
596 | ||
597 | vfs_context_t ctx = vfs_context_kernel(); | |
598 | proc_t p = vfs_context_proc(ctx); | |
599 | kauth_cred_t kerncred = vfs_context_ucred(ctx); | |
600 | ||
601 | NDINIT(&ndp, LOOKUP, OP_OPEN, LOCKLEAF, UIO_SYSSPACE, CAST_USER_ADDR_T(path), ctx); | |
602 | if ((err = namei(&ndp)) != 0) { | |
603 | AUTHPRNT("namei failed (%s) - %d", path, err); | |
604 | goto out; | |
605 | } | |
606 | nameidone(&ndp); | |
607 | vp = ndp.ni_vp; | |
608 | ||
609 | if ((err = vnode_size(vp, &fsize, ctx)) != 0) { | |
610 | AUTHPRNT("failed to get vnode size of %s - %d", path, err); | |
611 | goto out; | |
612 | } | |
613 | if (fsize < 0) { | |
614 | panic("negative file size"); | |
615 | } | |
616 | ||
617 | if ((err = VNOP_OPEN(vp, FREAD, ctx)) != 0) { | |
618 | AUTHPRNT("failed to open %s - %d", path, err); | |
619 | goto out; | |
620 | } | |
621 | doclose = true; | |
622 | ||
623 | /* if bufsz is non-zero, cap the read at bufsz bytes */ | |
624 | if (*bufszp && *bufszp < (size_t)fsize) { | |
625 | fsize = *bufszp; | |
626 | } | |
627 | ||
628 | fsize = (off_t)MIN((size_t)fsize, INT_MAX); | |
629 | ||
630 | buf = kheap_alloc(kheap, (size_t)fsize, Z_WAITOK); | |
631 | if (buf == NULL) { | |
632 | err = ENOMEM; | |
633 | goto out; | |
634 | } | |
635 | ||
636 | if ((err = vn_rdwr(UIO_READ, vp, (caddr_t)buf, (int)fsize, offset, UIO_SYSSPACE, IO_NODELOCKED, kerncred, &resid, p)) != 0) { | |
637 | AUTHPRNT("Cannot read %d bytes at offset %d from %s - %d", (int)fsize, (int)offset, path, err); | |
638 | goto out; | |
639 | } | |
640 | ||
641 | if (resid) { | |
642 | /* didnt get everything we wanted */ | |
643 | AUTHPRNT("Short read of %d bytes at offset %d from %s - %d", (int)fsize, (int)offset, path, resid); | |
644 | err = EINVAL; | |
645 | goto out; | |
646 | } | |
647 | ||
648 | out: | |
649 | if (doclose) { | |
650 | VNOP_CLOSE(vp, FREAD, ctx); | |
651 | } | |
652 | if (vp) { | |
653 | vnode_put(vp); | |
654 | vp = NULL; | |
655 | } | |
656 | ||
657 | if (err) { | |
658 | kheap_free_safe(kheap, buf, (size_t)fsize); | |
659 | } else { | |
660 | *bufp = buf; | |
661 | *bufszp = (size_t)fsize; | |
662 | } | |
663 | ||
664 | return err; | |
665 | } | |
666 | ||
667 | int | |
668 | imageboot_read_file(kalloc_heap_t kheap, const char *path, void **bufp, size_t *bufszp) | |
669 | { | |
670 | return imageboot_read_file_from_offset(kheap, path, 0, bufp, bufszp); | |
671 | } | |
672 | ||
673 | #if CONFIG_IMAGEBOOT_IMG4 || CONFIG_IMAGEBOOT_CHUNKLIST | |
674 | vnode_t | |
675 | imgboot_get_image_file(const char *path, off_t *fsize, int *errp) | |
676 | { | |
677 | struct nameidata ndp = {}; | |
678 | vnode_t vp = NULL; | |
679 | vfs_context_t ctx = vfs_context_kernel(); | |
680 | int err; | |
681 | ||
682 | NDINIT(&ndp, LOOKUP, OP_OPEN, LOCKLEAF, UIO_SYSSPACE, CAST_USER_ADDR_T(path), ctx); | |
683 | if ((err = namei(&ndp)) != 0) { | |
684 | AUTHPRNT("Cannot find %s - error %d", path, err); | |
685 | } else { | |
686 | nameidone(&ndp); | |
687 | vp = ndp.ni_vp; | |
688 | ||
689 | if (vp->v_type != VREG) { | |
690 | err = EINVAL; | |
691 | AUTHPRNT("%s it not a regular file", path); | |
692 | } else if (fsize) { | |
693 | if ((err = vnode_size(vp, fsize, ctx)) != 0) { | |
694 | AUTHPRNT("Cannot get file size of %s - error %d", path, err); | |
695 | } | |
696 | } | |
697 | } | |
698 | ||
699 | if (err) { | |
700 | if (vp) { | |
701 | vnode_put(vp); | |
702 | } | |
703 | *errp = err; | |
704 | vp = NULL; | |
705 | } | |
706 | return vp; | |
707 | } | |
708 | #endif /* CONFIG_IMAGEBOOT_CHUNKLIST || CONFIG_IMAGEBOOT_CHUNKLIST */ | |
709 | ||
710 | #if CONFIG_IMAGEBOOT_IMG4 | |
711 | ||
712 | #define APTICKET_NAME "apticket.der" | |
713 | ||
714 | static char * | |
715 | imgboot_get_apticket_path(const char *rootpath, size_t *sz) | |
716 | { | |
717 | size_t plen = strlen(rootpath) + sizeof(APTICKET_NAME) + 1; | |
718 | char *path = kheap_alloc(KHEAP_TEMP, plen, Z_WAITOK); | |
719 | ||
720 | if (path) { | |
721 | char *slash; | |
722 | ||
723 | strlcpy(path, rootpath, plen); | |
724 | slash = strrchr(path, '/'); | |
725 | if (slash == NULL) { | |
726 | slash = path; | |
727 | } else { | |
728 | slash++; | |
729 | } | |
730 | strlcpy(slash, APTICKET_NAME, sizeof(APTICKET_NAME) + 1); | |
731 | } | |
732 | ||
733 | *sz = plen; | |
734 | return path; | |
735 | } | |
736 | ||
737 | static int | |
738 | authenticate_root_with_img4(const char *rootpath) | |
739 | { | |
740 | errno_t rv; | |
741 | vnode_t vp; | |
742 | size_t ticket_pathsz = 0; | |
743 | char *ticket_path; | |
744 | img4_buff_t tck = IMG4_BUFF_INIT; | |
745 | img4_firmware_execution_context_t exec = { | |
746 | .i4fex_version = IMG4_FIRMWARE_EXECUTION_CONTEXT_STRUCT_VERSION, | |
747 | .i4fex_execute = NULL, | |
748 | .i4fex_context = NULL, | |
749 | }; | |
750 | img4_firmware_t fw = NULL; | |
751 | img4_firmware_flags_t fw_flags = IMG4_FIRMWARE_FLAG_BARE | | |
752 | IMG4_FIRMWARE_FLAG_SUBSEQUENT_STAGE; | |
753 | ||
754 | DBG_TRACE("Check %s\n", rootpath); | |
755 | ||
756 | if (img4if == NULL) { | |
757 | AUTHPRNT("AppleImage4 is not ready"); | |
758 | return EAGAIN; | |
759 | } | |
760 | ||
761 | ticket_path = imgboot_get_apticket_path(rootpath, &ticket_pathsz); | |
762 | if (ticket_path == NULL) { | |
763 | AUTHPRNT("Cannot construct ticket path - out of memory"); | |
764 | return ENOMEM; | |
765 | } | |
766 | ||
767 | rv = imageboot_read_file(KHEAP_TEMP, ticket_path, (void **)&tck.i4b_bytes, &tck.i4b_len); | |
768 | if (rv) { | |
769 | AUTHPRNT("Cannot get a ticket from %s - %d\n", ticket_path, rv); | |
770 | goto out_with_ticket_path; | |
771 | } | |
772 | ||
773 | DBG_TRACE("Got %lu bytes of manifest from %s\n", tck.i4b_len, ticket_path); | |
774 | ||
775 | vp = imgboot_get_image_file(rootpath, NULL, &rv); | |
776 | if (vp == NULL) { | |
777 | /* Error message had been printed already */ | |
778 | rv = EIO; | |
779 | goto out_with_ticket_bytes; | |
780 | } | |
781 | ||
782 | fw = img4_firmware_new_from_vnode_4xnu(IMG4_RUNTIME_DEFAULT, &exec, 'rosi', | |
783 | vp, fw_flags); | |
784 | if (!fw) { | |
785 | AUTHPRNT("Could not allocate new firmware"); | |
786 | rv = ENOMEM; | |
787 | goto out_with_ticket_bytes; | |
788 | } | |
789 | ||
790 | img4_firmware_attach_manifest(fw, &tck); | |
791 | rv = img4_firmware_evaluate(fw, img4_chip_select_personalized_ap(), NULL); | |
792 | ||
793 | out_with_ticket_bytes: | |
794 | kheap_free_safe(KHEAP_TEMP, tck.i4b_bytes, tck.i4b_len); | |
795 | out_with_ticket_path: | |
796 | kheap_free_safe(KHEAP_TEMP, ticket_path, ticket_pathsz); | |
797 | ||
798 | img4_firmware_destroy(&fw); | |
799 | return rv; | |
800 | } | |
801 | #endif /* CONFIG_IMAGEBOOT_IMG4 */ | |
802 | ||
803 | ||
804 | /* | |
805 | * Attach the image at 'path' as a ramdisk and mount it as our new rootfs. | |
806 | * All existing mounts are first umounted. | |
807 | */ | |
808 | static int | |
809 | imageboot_mount_ramdisk(const char *path) | |
810 | { | |
811 | int err = 0; | |
812 | size_t bufsz = 0; | |
813 | void *buf = NULL; | |
814 | dev_t dev; | |
815 | vnode_t newdp; | |
816 | vnode_t tvp; | |
817 | mount_t new_rootfs; | |
818 | ||
819 | /* Read our target image from disk */ | |
820 | err = imageboot_read_file(KHEAP_DATA_BUFFERS, path, &buf, &bufsz); | |
821 | if (err) { | |
822 | printf("%s: failed: imageboot_read_file() = %d\n", __func__, err); | |
823 | goto out; | |
824 | } | |
825 | DBG_TRACE("%s: read '%s' sz = %lu\n", __func__, path, bufsz); | |
826 | ||
827 | #if CONFIG_IMGSRC_ACCESS | |
828 | /* Re-add all root mounts to the mount list in the correct order... */ | |
829 | mount_list_remove(rootvnode->v_mount); | |
830 | for (int i = 0; i < MAX_IMAGEBOOT_NESTING; i++) { | |
831 | struct vnode *vn = imgsrc_rootvnodes[i]; | |
832 | if (vn) { | |
833 | vnode_getalways(vn); | |
834 | imgsrc_rootvnodes[i] = NULLVP; | |
835 | ||
836 | mount_t mnt = vn->v_mount; | |
837 | mount_lock(mnt); | |
838 | mnt->mnt_flag |= MNT_ROOTFS; | |
839 | mount_list_add(mnt); | |
840 | mount_unlock(mnt); | |
841 | ||
842 | vnode_rele(vn); | |
843 | vnode_put(vn); | |
844 | } | |
845 | } | |
846 | mount_list_add(rootvnode->v_mount); | |
847 | #endif | |
848 | ||
849 | /* ... and unmount everything */ | |
850 | vfs_unmountall(FALSE); | |
851 | ||
852 | lck_rw_lock_exclusive(&rootvnode_rw_lock); | |
853 | filedesc0.fd_cdir = NULL; | |
854 | tvp = rootvnode; | |
855 | rootvnode = NULL; | |
856 | rootvp = NULLVP; | |
857 | rootdev = NODEV; | |
858 | lck_rw_unlock_exclusive(&rootvnode_rw_lock); | |
859 | vnode_get_and_drop_always(tvp); | |
860 | ||
861 | /* Attach the ramfs image ... */ | |
862 | err = di_root_ramfile_buf(buf, bufsz, rootdevice, DEVMAXNAMESIZE, &dev); | |
863 | if (err) { | |
864 | printf("%s: failed: di_root_ramfile_buf() = %d\n", __func__, err); | |
865 | goto out; | |
866 | } | |
867 | ||
868 | /* ... and mount it */ | |
869 | rootdev = dev; | |
870 | mountroot = NULL; | |
871 | err = vfs_mountroot(); | |
872 | if (err) { | |
873 | printf("%s: failed: vfs_mountroot() = %d\n", __func__, err); | |
874 | goto out; | |
875 | } | |
876 | ||
877 | /* Switch to new root vnode */ | |
878 | if (VFS_ROOT(TAILQ_LAST(&mountlist, mntlist), &newdp, vfs_context_kernel())) { | |
879 | panic("%s: cannot find root vnode", __func__); | |
880 | } | |
881 | vnode_ref(newdp); | |
882 | ||
883 | lck_rw_lock_exclusive(&rootvnode_rw_lock); | |
884 | rootvnode = newdp; | |
885 | rootvnode->v_flag |= VROOT; | |
886 | new_rootfs = rootvnode->v_mount; | |
887 | mount_lock(new_rootfs); | |
888 | new_rootfs->mnt_flag |= MNT_ROOTFS; | |
889 | mount_unlock(new_rootfs); | |
890 | ||
891 | set_fake_bootuuid(new_rootfs); | |
892 | ||
893 | filedesc0.fd_cdir = newdp; | |
894 | lck_rw_unlock_exclusive(&rootvnode_rw_lock); | |
895 | ||
896 | vnode_put(newdp); | |
897 | ||
898 | DBG_TRACE("%s: root switched\n", __func__); | |
899 | ||
900 | out: | |
901 | if (err) { | |
902 | kheap_free_safe(KHEAP_DATA_BUFFERS, buf, bufsz); | |
903 | } | |
904 | return err; | |
905 | } | |
906 | ||
907 | /* | |
908 | * If the path is in <file://> URL format then we allocate memory and decode it, | |
909 | * otherwise return the same pointer. | |
910 | * | |
911 | * Caller is expected to check if the pointers are different. | |
912 | */ | |
913 | static char * | |
914 | url_to_path(char *url_path, size_t *sz) | |
915 | { | |
916 | char *path = url_path; | |
917 | size_t len = strlen(kIBFilePrefix); | |
918 | ||
919 | if (strncmp(kIBFilePrefix, url_path, len) == 0) { | |
920 | /* its a URL - remove the file:// prefix and percent-decode */ | |
921 | url_path += len; | |
922 | ||
923 | len = strlen(url_path); | |
924 | if (len) { | |
925 | /* Make a copy of the path to URL-decode */ | |
926 | path = kheap_alloc(KHEAP_TEMP, len + 1, Z_WAITOK); | |
927 | if (path == NULL) { | |
928 | panic("imageboot path allocation failed - cannot allocate %d bytes\n", (int)len); | |
929 | } | |
930 | ||
931 | strlcpy(path, url_path, len + 1); | |
932 | *sz = len + 1; | |
933 | url_decode(path); | |
934 | } else { | |
935 | panic("Bogus imageboot path URL - missing path\n"); | |
936 | } | |
937 | ||
938 | DBG_TRACE("%s: root image URL <%s> becomes %s\n", __func__, url_path, path); | |
939 | } | |
940 | ||
941 | return path; | |
942 | } | |
943 | ||
944 | static boolean_t | |
945 | imageboot_setup_new(imageboot_type_t type) | |
946 | { | |
947 | int error; | |
948 | char *root_path = NULL; | |
949 | int height = 0; | |
950 | boolean_t done = FALSE; | |
951 | boolean_t auth_root = TRUE; | |
952 | boolean_t ramdisk_root = FALSE; | |
953 | ||
954 | root_path = zalloc(ZV_NAMEI); | |
955 | assert(root_path != NULL); | |
956 | ||
957 | #if CONFIG_LOCKERBOOT | |
958 | if (type == IMAGEBOOT_LOCKER) { | |
959 | if (!PE_parse_boot_argn(IMAGEBOOT_LOCKER_ARG, root_path, MAXPATHLEN)) { | |
960 | panic("locker boot with no locker given"); | |
961 | } | |
962 | ||
963 | DBG_TRACE("%s: root fsname: %s\n", __FUNCTION__, rootvnode->v_mount->mnt_vtable->vfc_name); | |
964 | ||
965 | /* | |
966 | * The locker path is a path, not a URL, so just pass it directly to | |
967 | * imageboot_mount_image(). | |
968 | */ | |
969 | error = imageboot_mount_image(root_path, 0, type); | |
970 | if (error) { | |
971 | panic("failed to mount system locker: %d", error); | |
972 | } | |
973 | ||
974 | done = TRUE; | |
975 | goto out; | |
976 | } | |
977 | #endif /* CONFIG_LOCKERBOOT */ | |
978 | ||
979 | unsigned imgboot_arg; | |
980 | if (PE_parse_boot_argn("-rootdmg-ramdisk", &imgboot_arg, sizeof(imgboot_arg))) { | |
981 | ramdisk_root = TRUE; | |
982 | } | |
983 | ||
984 | if (PE_parse_boot_argn(IMAGEBOOT_CONTAINER_ARG, root_path, MAXPATHLEN) == TRUE) { | |
985 | printf("%s: container image url is %s\n", __FUNCTION__, root_path); | |
986 | error = imageboot_mount_image(root_path, height, type); | |
987 | if (error != 0) { | |
988 | panic("Failed to mount container image."); | |
989 | } | |
990 | ||
991 | height++; | |
992 | } | |
993 | ||
994 | if (PE_parse_boot_argn(IMAGEBOOT_AUTHROOT_ARG, root_path, MAXPATHLEN) == FALSE && | |
995 | PE_parse_boot_argn(IMAGEBOOT_ROOT_ARG, root_path, MAXPATHLEN) == FALSE) { | |
996 | if (height > 0) { | |
997 | panic("%s specified without %s or %s?\n", IMAGEBOOT_CONTAINER_ARG, IMAGEBOOT_AUTHROOT_ARG, IMAGEBOOT_ROOT_ARG); | |
998 | } | |
999 | goto out; | |
1000 | } | |
1001 | ||
1002 | printf("%s: root image URL is '%s'\n", __func__, root_path); | |
1003 | ||
1004 | /* Make a copy of the path to URL-decode */ | |
1005 | size_t pathsz; | |
1006 | char *path = url_to_path(root_path, &pathsz); | |
1007 | assert(path); | |
1008 | ||
1009 | #if CONFIG_IMAGEBOOT_CHUNKLIST | |
1010 | if (auth_root) { | |
1011 | /* | |
1012 | * This updates auth_root to reflect whether chunklist was | |
1013 | * actually enforced. In effect, this clears auth_root if | |
1014 | * CSR_ALLOW_ANY_RECOVERY_OS allowed an invalid image. | |
1015 | */ | |
1016 | AUTHDBG("authenticating root image at %s", path); | |
1017 | error = authenticate_root_with_chunklist(path, &auth_root); | |
1018 | if (error) { | |
1019 | panic("root image authentication failed (err = %d)\n", error); | |
1020 | } | |
1021 | AUTHDBG("successfully authenticated %s", path); | |
1022 | } | |
1023 | #endif | |
1024 | ||
1025 | if (ramdisk_root) { | |
1026 | error = imageboot_mount_ramdisk(path); | |
1027 | } else { | |
1028 | error = imageboot_mount_image(root_path, height, type); | |
1029 | } | |
1030 | ||
1031 | if (path != root_path) { | |
1032 | kheap_free_safe(KHEAP_TEMP, path, pathsz); | |
1033 | } | |
1034 | ||
1035 | if (error) { | |
1036 | panic("Failed to mount root image (err=%d, auth=%d, ramdisk=%d)\n", | |
1037 | error, auth_root, ramdisk_root); | |
1038 | } | |
1039 | ||
1040 | #if CONFIG_IMAGEBOOT_CHUNKLIST | |
1041 | if (auth_root) { | |
1042 | /* check that the image version matches the running kernel */ | |
1043 | AUTHDBG("checking root image version"); | |
1044 | error = authenticate_root_version_check(); | |
1045 | if (error) { | |
1046 | panic("root image version check failed"); | |
1047 | } else { | |
1048 | AUTHDBG("root image version matches kernel"); | |
1049 | } | |
1050 | } | |
1051 | #endif | |
1052 | ||
1053 | done = TRUE; | |
1054 | ||
1055 | out: | |
1056 | zfree(ZV_NAMEI, root_path); | |
1057 | return done; | |
1058 | } | |
1059 | ||
1060 | __private_extern__ void | |
1061 | imageboot_setup(imageboot_type_t type) | |
1062 | { | |
1063 | int error = 0; | |
1064 | char *root_path = NULL; | |
1065 | ||
1066 | DBG_TRACE("%s: entry\n", __FUNCTION__); | |
1067 | ||
1068 | if (rootvnode == NULL) { | |
1069 | panic("imageboot_setup: rootvnode is NULL."); | |
1070 | } | |
1071 | ||
1072 | /* | |
1073 | * New boot-arg scheme: | |
1074 | * root-dmg : the dmg that will be the root filesystem, authenticated by default. | |
1075 | * auth-root-dmg : same as root-dmg. | |
1076 | * container-dmg : an optional dmg that contains the root-dmg. | |
1077 | * locker : the locker that will be the root filesystem -- mutually | |
1078 | * exclusive with any other boot-arg. | |
1079 | */ | |
1080 | if (imageboot_setup_new(type)) { | |
1081 | return; | |
1082 | } | |
1083 | ||
1084 | root_path = zalloc(ZV_NAMEI); | |
1085 | assert(root_path != NULL); | |
1086 | ||
1087 | /* | |
1088 | * Look for outermost disk image to root from. If we're doing a nested boot, | |
1089 | * there's some sense in which the outer image never needs to be the root filesystem, | |
1090 | * but it does need very similar treatment: it must not be unmounted, needs a fake | |
1091 | * device vnode created for it, and should not show up in getfsstat() until exposed | |
1092 | * with MNT_IMGSRC. We just make it the temporary root. | |
1093 | */ | |
1094 | #if CONFIG_IMAGEBOOT_IMG4 | |
1095 | if (PE_parse_boot_argn("arp0", root_path, MAXPATHLEN)) { | |
1096 | size_t pathsz; | |
1097 | char *path = url_to_path(root_path, &pathsz); | |
1098 | ||
1099 | assert(path); | |
1100 | ||
1101 | if (authenticate_root_with_img4(path)) { | |
1102 | panic("Root image %s does not match the manifest\n", root_path); | |
1103 | } | |
1104 | if (path != root_path) { | |
1105 | kheap_free_safe(KHEAP_TEMP, path, pathsz); | |
1106 | } | |
1107 | } else | |
1108 | #endif /* CONFIG_IMAGEBOOT_IMG4 */ | |
1109 | if ((PE_parse_boot_argn("rp", root_path, MAXPATHLEN) == FALSE) && | |
1110 | (PE_parse_boot_argn("rp0", root_path, MAXPATHLEN) == FALSE)) { | |
1111 | panic("%s: no valid path to image.\n", __FUNCTION__); | |
1112 | } | |
1113 | ||
1114 | DBG_TRACE("%s: root image url is %s\n", __FUNCTION__, root_path); | |
1115 | ||
1116 | error = imageboot_mount_image(root_path, 0, type); | |
1117 | if (error) { | |
1118 | panic("Failed on first stage of imageboot."); | |
1119 | } | |
1120 | ||
1121 | /* | |
1122 | * See if we are rooting from a nested image | |
1123 | */ | |
1124 | if (PE_parse_boot_argn("rp1", root_path, MAXPATHLEN) == FALSE) { | |
1125 | goto done; | |
1126 | } | |
1127 | ||
1128 | printf("%s: second level root image url is %s\n", __FUNCTION__, root_path); | |
1129 | ||
1130 | /* | |
1131 | * If we fail to set up second image, it's not a given that we | |
1132 | * can safely root off the first. | |
1133 | */ | |
1134 | error = imageboot_mount_image(root_path, 1, type); | |
1135 | if (error) { | |
1136 | panic("Failed on second stage of imageboot."); | |
1137 | } | |
1138 | ||
1139 | done: | |
1140 | zfree(ZV_NAMEI, root_path); | |
1141 | ||
1142 | DBG_TRACE("%s: exit\n", __FUNCTION__); | |
1143 | ||
1144 | return; | |
1145 | } |