2 * Copyright (c) 2006-2020 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
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>
41 #include <sys/namei.h>
42 #include <sys/fcntl.h>
43 #include <sys/vnode.h>
44 #include <sys/sysproto.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>
52 #if CONFIG_IMAGEBOOT_IMG4
53 #include <libkern/img4/interface.h>
54 #include <img4/firmware.h>
57 #include <kern/kalloc.h>
59 #include <pexpert/pexpert.h>
60 #include <kern/chunklist.h>
62 extern struct filedesc filedesc0
;
64 extern int (*mountroot
)(void);
65 extern char rootdevice
[DEVMAXNAMESIZE
];
68 typedef struct _locker_mount_args
{
69 char lmnt_path
[PATH_MAX
];
70 uint16_t lmnt_preferred_hash
;
71 } locker_mount_args_t
;
74 #define DEBUG_IMAGEBOOT 0
77 #define DBG_TRACE(...) printf("imageboot: " __VA_ARGS__)
79 #define DBG_TRACE(...) do {} while(0)
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)
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
);
90 static boolean_t
imageboot_setup_new(imageboot_type_t type
);
92 void *ubc_getobject_from_filename(const char *filename
, struct vnode
**vpp
, off_t
*file_size
);
94 extern lck_rw_t rootvnode_rw_lock
;
96 #define kIBFilePrefix "file://"
98 __private_extern__
int
99 imageboot_format_is_valid(const char *root_path
)
101 return strncmp(root_path
, kIBFilePrefix
,
102 strlen(kIBFilePrefix
)) == 0;
106 vnode_get_and_drop_always(vnode_t vp
)
113 __private_extern__
bool
114 imageboot_desired(void)
116 bool do_imageboot
= false;
118 char *root_path
= NULL
;
119 root_path
= zalloc(ZV_NAMEI
);
121 * Check for first layer DMG rooting.
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.
126 * If NONE of the boot-args are present, then assume that image-rooting
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...!!]
132 if (!(PE_parse_boot_argn("rp0", root_path
, MAXPATHLEN
) ||
133 #if CONFIG_IMAGEBOOT_IMG4
134 PE_parse_boot_argn("arp0", root_path
, MAXPATHLEN
) ||
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;
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 */
148 /* explicitly set to false */
149 do_imageboot
= false;
153 zfree(ZV_NAMEI
, root_path
);
157 __private_extern__ imageboot_type_t
158 imageboot_needed(void)
160 imageboot_type_t result
= IMAGEBOOT_NONE
;
161 char *root_path
= NULL
;
163 DBG_TRACE("%s: checking for presence of root path\n", __FUNCTION__
);
165 if (!imageboot_desired()) {
169 root_path
= zalloc(ZV_NAMEI
);
170 result
= IMAGEBOOT_DMG
;
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
))) {
178 /* Sanity-check second layer */
179 if (imageboot_format_is_valid(root_path
)) {
180 DBG_TRACE("%s: Found %s\n", __FUNCTION__
, root_path
);
182 panic("%s: Invalid URL scheme for %s\n",
183 __FUNCTION__
, root_path
);
187 if (root_path
!= NULL
) {
188 zfree(ZV_NAMEI
, root_path
);
193 extern bool IOBaseSystemARVRootHashAvailable(void);
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
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
)
210 boolean_t authenticated_dmg_chunklist
= false;
211 vnode_t mount_vp
= NULLVP
;
215 if (type
!= IMAGEBOOT_DMG
) {
216 panic("not supported");
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
224 vnode_t imagevp
= NULLVP
;
225 error
= vnode_lookup(image_path
, 0, &imagevp
, vfs_context_kernel());
227 printf("%s: image file not found or couldn't be read: %d\n", __FUNCTION__
, error
);
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).
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.
244 char devname
[DEVMAXNAMESIZE
];
246 error
= di_root_image_ext(image_path
, devname
, DEVMAXNAMESIZE
, &dev
, true);
248 panic("%s: di_root_image failed: %d\n", __FUNCTION__
, error
);
251 printf("%s: attached disk image %s as %s\n", __FUNCTION__
, image_path
, devname
);
254 #if CONFIG_IMAGEBOOT_CHUNKLIST
255 if ((rooted_dmg
== false) && !IOBaseSystemARVRootHashAvailable()) {
256 error
= authenticate_root_with_chunklist(image_path
, NULL
);
258 printf("authenticated root-dmg via chunklist...\n");
259 authenticated_dmg_chunklist
= true;
261 /* root hash was not available, and image is NOT chunklisted? */
262 printf("failed to chunklist-authenticate root-dmg @ %s\n", image_path
);
267 char fulldevname
[DEVMAXNAMESIZE
+ 5]; // "/dev/"
268 strlcpy(fulldevname
, "/dev/", sizeof(fulldevname
));
269 strlcat(fulldevname
, devname
, sizeof(fulldevname
));
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.
277 char *fulldevnamep
= &(fulldevname
[0]);
278 char **fulldevnamepp
= &fulldevnamep
;
280 #define PIVOTMNT "/System/Volumes/BaseSystem"
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());
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());
292 /* If we didn't mount as either HFS or APFS, then bail out */
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).
303 #if defined(__arm64__) && XNU_TARGET_OS_OSX
304 panic("%s: failed to mount pivot image(%d)!", __FUNCTION__
, error
);
306 printf("%s: failed to mount pivot image(%d) !", __FUNCTION__
, error
);
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());
313 #if defined(__arm64__) && XNU_TARGET_OS_OSX
314 panic("%s: failed to lookup pivot root (%d) !", __FUNCTION__
, error
);
316 printf("%s: failed to lookup pivot root (%d)!", __FUNCTION__
, error
);
320 /* the 0x1 implies base system */
321 rootauth
= VNOP_IOCTL(mount_vp
, FSIOC_KERNEL_ROOTAUTH
, (caddr_t
)0x1, 0, vfs_context_kernel());
323 printf("BS-DMG failed to authenticate intra-FS \n");
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.
329 if (rooted_dmg
|| authenticated_dmg_chunklist
|| skip_signature_check
) {
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
344 #if defined(__arm64__) && XNU_TARGET_OS_OSX
345 panic("%s: could not authenticate the pivot image: %d. giving up.\n", __FUNCTION__
, error
);
347 printf("%s: could not authenticate the pivot image: %d. giving up.\n", __FUNCTION__
, error
);
352 mount_t root_mp
= vnode_mount(rootvnode
);
353 if (root_mp
&& (root_mp
->mnt_kern_flag
& MNTK_SSD
)) {
354 rootvp_is_ssd
= true;
358 * pivot the incoming and outgoing filesystems
360 error
= vfs_switch_root(mount_path
, outgoing_root_path
, 0);
362 panic("%s: vfs_switch_root failed: %d\n", __FUNCTION__
, error
);
366 * Mark the filesystem containing the image as backing root, so it
367 * won't be unmountable.
369 * vfs_switch_root() clears this flag, so we have to set it after
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.
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
);
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.
391 if (imagevp
!= NULLVP
) {
398 extern uuid_string_t fake_bootuuid
;
401 set_fake_bootuuid(mount_t mp
)
405 VFSATTR_WANTED(&va
, f_uuid
);
407 if (vfs_getattr(mp
, &va
, vfs_context_current()) != 0) {
411 if (!VFSATTR_IS_SUPPORTED(&va
, f_uuid
)) {
415 uuid_unparse(va
.f_uuid
, fake_bootuuid
);
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.)
427 __private_extern__
int
428 imageboot_mount_image(const char *root_path
, int height
, imageboot_type_t type
)
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.
437 vnode_t old_rootvnode
= rootvnode
;
440 boolean_t update_rootvnode
= FALSE
;
442 if (type
== IMAGEBOOT_DMG
) {
443 error
= di_root_image(root_path
, rootdevice
, DEVMAXNAMESIZE
, &dev
);
445 panic("%s: di_root_image failed: %d\n", __FUNCTION__
, error
);
450 printf("%s: root device 0x%x\n", __FUNCTION__
, rootdev
);
451 error
= vfs_mountroot();
453 panic("vfs_mountroot() failed.\n");
456 update_rootvnode
= TRUE
;
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
);
463 panic("could not alloc mount args");
466 strlcpy(mntargs
->lmnt_path
, root_path
, sizeof(mntargs
->lmnt_path
));
467 mntargs
->lmnt_preferred_hash
= 0;
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());
473 panic("failed to mount locker: %d", error
);
475 kheap_free(KHEAP_TEMP
, mntargs
, sizeof(*mntargs
));
477 /* Clear the old mount association. */
478 old_rootvnode
->v_mountedhere
= NULL
;
479 rootvnode
->v_mount
->mnt_vnodecovered
= NULL
;
483 panic("invalid imageboot type: %d", type
);
487 * Get the vnode for '/'.
488 * Set fdp->fd_fd.fd_cdir to reference it.
490 if (VFS_ROOT(TAILQ_LAST(&mountlist
, mntlist
), &newdp
, vfs_context_kernel())) {
491 panic("%s: cannot find root vnode", __FUNCTION__
);
493 DBG_TRACE("%s: old root fsname: %s\n", __FUNCTION__
, old_rootvnode
->v_mount
->mnt_vtable
->vfc_name
);
495 if (old_rootvnode
!= NULL
) {
496 /* remember the old rootvnode, but remove it from mountlist */
497 mount_t old_rootfs
= old_rootvnode
->v_mount
;
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
);
509 lck_rw_lock_exclusive(&rootvnode_rw_lock
);
510 /* switch to the new rootvnode */
511 if (update_rootvnode
) {
513 set_fake_bootuuid(rootvnode
->v_mount
);
516 new_rootfs
= rootvnode
->v_mount
;
517 mount_lock(new_rootfs
);
518 new_rootfs
->mnt_flag
|= MNT_ROOTFS
;
519 mount_unlock(new_rootfs
);
521 filedesc0
.fd_cdir
= newdp
;
522 lck_rw_unlock_exclusive(&rootvnode_rw_lock
);
524 DBG_TRACE("%s: root switched\n", __FUNCTION__
);
526 if (old_rootvnode
!= NULL
) {
527 #ifdef CONFIG_IMGSRC_ACCESS
528 if (height
>= 0 && PE_imgsrc_mount_supported()) {
529 imgsrc_rootvnodes
[height
] = old_rootvnode
;
531 vnode_get_and_drop_always(old_rootvnode
);
534 #pragma unused(height)
535 vnode_get_and_drop_always(old_rootvnode
);
536 #endif /* CONFIG_IMGSRC_ACCESS */
542 * Return a memory object for given file path.
543 * Also returns a vnode reference for the given file path.
546 ubc_getobject_from_filename(const char *filename
, struct vnode
**vpp
, off_t
*file_size
)
549 struct nameidata ndp
= {};
550 struct vnode
*vp
= NULL
;
552 vfs_context_t ctx
= vfs_context_kernel();
553 void *control
= NULL
;
555 NDINIT(&ndp
, LOOKUP
, OP_OPEN
, LOCKLEAF
, UIO_SYSSPACE
, CAST_USER_ADDR_T(filename
), ctx
);
556 if ((err
= namei(&ndp
)) != 0) {
562 if ((err
= vnode_size(vp
, &fsize
, ctx
)) != 0) {
570 control
= ubc_getobject(vp
, UBC_FLAGS_NONE
);
571 if (control
== NULL
) {
587 imageboot_read_file_from_offset(kalloc_heap_t kheap
, const char *path
, off_t offset
, void **bufp
, size_t *bufszp
)
590 struct nameidata ndp
= {};
591 struct vnode
*vp
= NULL
;
595 bool doclose
= false;
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
);
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
);
609 if ((err
= vnode_size(vp
, &fsize
, ctx
)) != 0) {
610 AUTHPRNT("failed to get vnode size of %s - %d", path
, err
);
614 panic("negative file size");
617 if ((err
= VNOP_OPEN(vp
, FREAD
, ctx
)) != 0) {
618 AUTHPRNT("failed to open %s - %d", path
, err
);
623 /* if bufsz is non-zero, cap the read at bufsz bytes */
624 if (*bufszp
&& *bufszp
< (size_t)fsize
) {
628 fsize
= (off_t
)MIN((size_t)fsize
, INT_MAX
);
630 buf
= kheap_alloc(kheap
, (size_t)fsize
, Z_WAITOK
);
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
);
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
);
650 VNOP_CLOSE(vp
, FREAD
, ctx
);
658 kheap_free_safe(kheap
, buf
, (size_t)fsize
);
661 *bufszp
= (size_t)fsize
;
668 imageboot_read_file(kalloc_heap_t kheap
, const char *path
, void **bufp
, size_t *bufszp
)
670 return imageboot_read_file_from_offset(kheap
, path
, 0, bufp
, bufszp
);
673 #if CONFIG_IMAGEBOOT_IMG4 || CONFIG_IMAGEBOOT_CHUNKLIST
675 imgboot_get_image_file(const char *path
, off_t
*fsize
, int *errp
)
677 struct nameidata ndp
= {};
679 vfs_context_t ctx
= vfs_context_kernel();
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
);
689 if (vp
->v_type
!= VREG
) {
691 AUTHPRNT("%s it not a regular file", path
);
693 if ((err
= vnode_size(vp
, fsize
, ctx
)) != 0) {
694 AUTHPRNT("Cannot get file size of %s - error %d", path
, err
);
708 #endif /* CONFIG_IMAGEBOOT_CHUNKLIST || CONFIG_IMAGEBOOT_CHUNKLIST */
710 #if CONFIG_IMAGEBOOT_IMG4
712 #define APTICKET_NAME "apticket.der"
715 imgboot_get_apticket_path(const char *rootpath
, size_t *sz
)
717 size_t plen
= strlen(rootpath
) + sizeof(APTICKET_NAME
) + 1;
718 char *path
= kheap_alloc(KHEAP_TEMP
, plen
, Z_WAITOK
);
723 strlcpy(path
, rootpath
, plen
);
724 slash
= strrchr(path
, '/');
730 strlcpy(slash
, APTICKET_NAME
, sizeof(APTICKET_NAME
) + 1);
738 authenticate_root_with_img4(const char *rootpath
)
742 size_t ticket_pathsz
= 0;
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
,
750 img4_firmware_t fw
= NULL
;
751 img4_firmware_flags_t fw_flags
= IMG4_FIRMWARE_FLAG_BARE
|
752 IMG4_FIRMWARE_FLAG_SUBSEQUENT_STAGE
;
754 DBG_TRACE("Check %s\n", rootpath
);
756 if (img4if
== NULL
) {
757 AUTHPRNT("AppleImage4 is not ready");
761 ticket_path
= imgboot_get_apticket_path(rootpath
, &ticket_pathsz
);
762 if (ticket_path
== NULL
) {
763 AUTHPRNT("Cannot construct ticket path - out of memory");
767 rv
= imageboot_read_file(KHEAP_TEMP
, ticket_path
, (void **)&tck
.i4b_bytes
, &tck
.i4b_len
);
769 AUTHPRNT("Cannot get a ticket from %s - %d\n", ticket_path
, rv
);
770 goto out_with_ticket_path
;
773 DBG_TRACE("Got %lu bytes of manifest from %s\n", tck
.i4b_len
, ticket_path
);
775 vp
= imgboot_get_image_file(rootpath
, NULL
, &rv
);
777 /* Error message had been printed already */
779 goto out_with_ticket_bytes
;
782 fw
= img4_firmware_new_from_vnode_4xnu(IMG4_RUNTIME_DEFAULT
, &exec
, 'rosi',
785 AUTHPRNT("Could not allocate new firmware");
787 goto out_with_ticket_bytes
;
790 img4_firmware_attach_manifest(fw
, &tck
);
791 rv
= img4_firmware_evaluate(fw
, img4_chip_select_personalized_ap(), NULL
);
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
);
798 img4_firmware_destroy(&fw
);
801 #endif /* CONFIG_IMAGEBOOT_IMG4 */
805 * Attach the image at 'path' as a ramdisk and mount it as our new rootfs.
806 * All existing mounts are first umounted.
809 imageboot_mount_ramdisk(const char *path
)
819 /* Read our target image from disk */
820 err
= imageboot_read_file(KHEAP_DATA_BUFFERS
, path
, &buf
, &bufsz
);
822 printf("%s: failed: imageboot_read_file() = %d\n", __func__
, err
);
825 DBG_TRACE("%s: read '%s' sz = %lu\n", __func__
, path
, bufsz
);
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
];
834 imgsrc_rootvnodes
[i
] = NULLVP
;
836 mount_t mnt
= vn
->v_mount
;
838 mnt
->mnt_flag
|= MNT_ROOTFS
;
846 mount_list_add(rootvnode
->v_mount
);
849 /* ... and unmount everything */
850 vfs_unmountall(FALSE
);
852 lck_rw_lock_exclusive(&rootvnode_rw_lock
);
853 filedesc0
.fd_cdir
= NULL
;
858 lck_rw_unlock_exclusive(&rootvnode_rw_lock
);
859 vnode_get_and_drop_always(tvp
);
861 /* Attach the ramfs image ... */
862 err
= di_root_ramfile_buf(buf
, bufsz
, rootdevice
, DEVMAXNAMESIZE
, &dev
);
864 printf("%s: failed: di_root_ramfile_buf() = %d\n", __func__
, err
);
868 /* ... and mount it */
871 err
= vfs_mountroot();
873 printf("%s: failed: vfs_mountroot() = %d\n", __func__
, err
);
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__
);
883 lck_rw_lock_exclusive(&rootvnode_rw_lock
);
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
);
891 set_fake_bootuuid(new_rootfs
);
893 filedesc0
.fd_cdir
= newdp
;
894 lck_rw_unlock_exclusive(&rootvnode_rw_lock
);
898 DBG_TRACE("%s: root switched\n", __func__
);
902 kheap_free_safe(KHEAP_DATA_BUFFERS
, buf
, bufsz
);
908 * If the path is in <file://> URL format then we allocate memory and decode it,
909 * otherwise return the same pointer.
911 * Caller is expected to check if the pointers are different.
914 url_to_path(char *url_path
, size_t *sz
)
916 char *path
= url_path
;
917 size_t len
= strlen(kIBFilePrefix
);
919 if (strncmp(kIBFilePrefix
, url_path
, len
) == 0) {
920 /* its a URL - remove the file:// prefix and percent-decode */
923 len
= strlen(url_path
);
925 /* Make a copy of the path to URL-decode */
926 path
= kheap_alloc(KHEAP_TEMP
, len
+ 1, Z_WAITOK
);
928 panic("imageboot path allocation failed - cannot allocate %d bytes\n", (int)len
);
931 strlcpy(path
, url_path
, len
+ 1);
935 panic("Bogus imageboot path URL - missing path\n");
938 DBG_TRACE("%s: root image URL <%s> becomes %s\n", __func__
, url_path
, path
);
945 imageboot_setup_new(imageboot_type_t type
)
948 char *root_path
= NULL
;
950 boolean_t done
= FALSE
;
951 boolean_t auth_root
= TRUE
;
952 boolean_t ramdisk_root
= FALSE
;
954 root_path
= zalloc(ZV_NAMEI
);
955 assert(root_path
!= NULL
);
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");
963 DBG_TRACE("%s: root fsname: %s\n", __FUNCTION__
, rootvnode
->v_mount
->mnt_vtable
->vfc_name
);
966 * The locker path is a path, not a URL, so just pass it directly to
967 * imageboot_mount_image().
969 error
= imageboot_mount_image(root_path
, 0, type
);
971 panic("failed to mount system locker: %d", error
);
977 #endif /* CONFIG_LOCKERBOOT */
979 unsigned imgboot_arg
;
980 if (PE_parse_boot_argn("-rootdmg-ramdisk", &imgboot_arg
, sizeof(imgboot_arg
))) {
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
);
988 panic("Failed to mount container image.");
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
) {
997 panic("%s specified without %s or %s?\n", IMAGEBOOT_CONTAINER_ARG
, IMAGEBOOT_AUTHROOT_ARG
, IMAGEBOOT_ROOT_ARG
);
1002 printf("%s: root image URL is '%s'\n", __func__
, root_path
);
1004 /* Make a copy of the path to URL-decode */
1006 char *path
= url_to_path(root_path
, &pathsz
);
1009 #if CONFIG_IMAGEBOOT_CHUNKLIST
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.
1016 AUTHDBG("authenticating root image at %s", path
);
1017 error
= authenticate_root_with_chunklist(path
, &auth_root
);
1019 panic("root image authentication failed (err = %d)\n", error
);
1021 AUTHDBG("successfully authenticated %s", path
);
1026 error
= imageboot_mount_ramdisk(path
);
1028 error
= imageboot_mount_image(root_path
, height
, type
);
1031 if (path
!= root_path
) {
1032 kheap_free_safe(KHEAP_TEMP
, path
, pathsz
);
1036 panic("Failed to mount root image (err=%d, auth=%d, ramdisk=%d)\n",
1037 error
, auth_root
, ramdisk_root
);
1040 #if CONFIG_IMAGEBOOT_CHUNKLIST
1042 /* check that the image version matches the running kernel */
1043 AUTHDBG("checking root image version");
1044 error
= authenticate_root_version_check();
1046 panic("root image version check failed");
1048 AUTHDBG("root image version matches kernel");
1056 zfree(ZV_NAMEI
, root_path
);
1060 __private_extern__
void
1061 imageboot_setup(imageboot_type_t type
)
1064 char *root_path
= NULL
;
1066 DBG_TRACE("%s: entry\n", __FUNCTION__
);
1068 if (rootvnode
== NULL
) {
1069 panic("imageboot_setup: rootvnode is NULL.");
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.
1080 if (imageboot_setup_new(type
)) {
1084 root_path
= zalloc(ZV_NAMEI
);
1085 assert(root_path
!= NULL
);
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.
1094 #if CONFIG_IMAGEBOOT_IMG4
1095 if (PE_parse_boot_argn("arp0", root_path
, MAXPATHLEN
)) {
1097 char *path
= url_to_path(root_path
, &pathsz
);
1101 if (authenticate_root_with_img4(path
)) {
1102 panic("Root image %s does not match the manifest\n", root_path
);
1104 if (path
!= root_path
) {
1105 kheap_free_safe(KHEAP_TEMP
, path
, pathsz
);
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__
);
1114 DBG_TRACE("%s: root image url is %s\n", __FUNCTION__
, root_path
);
1116 error
= imageboot_mount_image(root_path
, 0, type
);
1118 panic("Failed on first stage of imageboot.");
1122 * See if we are rooting from a nested image
1124 if (PE_parse_boot_argn("rp1", root_path
, MAXPATHLEN
) == FALSE
) {
1128 printf("%s: second level root image url is %s\n", __FUNCTION__
, root_path
);
1131 * If we fail to set up second image, it's not a given that we
1132 * can safely root off the first.
1134 error
= imageboot_mount_image(root_path
, 1, type
);
1136 panic("Failed on second stage of imageboot.");
1140 zfree(ZV_NAMEI
, root_path
);
1142 DBG_TRACE("%s: exit\n", __FUNCTION__
);