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 `rooted_dmg` is true,
203 * then ignore then 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
)
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.
328 if (rooted_dmg
|| authenticated_dmg_chunklist
) {
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
343 #if defined(__arm64__) && XNU_TARGET_OS_OSX
344 panic("%s: could not authenticate the pivot image: %d. giving up.\n", __FUNCTION__
, error
);
346 printf("%s: could not authenticate the pivot image: %d. giving up.\n", __FUNCTION__
, error
);
351 mount_t root_mp
= vnode_mount(rootvnode
);
352 if (root_mp
&& (root_mp
->mnt_kern_flag
& MNTK_SSD
)) {
353 rootvp_is_ssd
= true;
357 * pivot the incoming and outgoing filesystems
359 error
= vfs_switch_root(mount_path
, outgoing_root_path
, 0);
361 panic("%s: vfs_switch_root failed: %d\n", __FUNCTION__
, error
);
365 * Mark the filesystem containing the image as backing root, so it
366 * won't be unmountable.
368 * vfs_switch_root() clears this flag, so we have to set it after
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.
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
);
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.
390 if (imagevp
!= NULLVP
) {
397 extern uuid_string_t fake_bootuuid
;
400 set_fake_bootuuid(mount_t mp
)
404 VFSATTR_WANTED(&va
, f_uuid
);
406 if (vfs_getattr(mp
, &va
, vfs_context_current()) != 0) {
410 if (!VFSATTR_IS_SUPPORTED(&va
, f_uuid
)) {
414 uuid_unparse(va
.f_uuid
, fake_bootuuid
);
418 * Swaps in new root filesystem based on image path.
419 * Current root filesystem is removed from mount list and
420 * tagged MNTK_BACKS_ROOT, MNT_ROOTFS is cleared on it, and
421 * "rootvnode" is reset. Root vnode of currentroot filesystem
422 * is returned with usecount (no iocount).
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.)
426 __private_extern__
int
427 imageboot_mount_image(const char *root_path
, int height
, imageboot_type_t type
)
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.
436 vnode_t old_rootvnode
= rootvnode
;
439 boolean_t update_rootvnode
= FALSE
;
441 if (type
== IMAGEBOOT_DMG
) {
442 error
= di_root_image(root_path
, rootdevice
, DEVMAXNAMESIZE
, &dev
);
444 panic("%s: di_root_image failed: %d\n", __FUNCTION__
, error
);
449 printf("%s: root device 0x%x\n", __FUNCTION__
, rootdev
);
450 error
= vfs_mountroot();
452 panic("vfs_mountroot() failed.\n");
455 update_rootvnode
= TRUE
;
457 #if CONFIG_LOCKERBOOT
458 else if (type
== IMAGEBOOT_LOCKER
) {
459 locker_mount_args_t
*mntargs
= kheap_alloc(KHEAP_TEMP
,
460 sizeof(*mntargs
), Z_WAITOK
);
462 panic("could not alloc mount args");
465 strlcpy(mntargs
->lmnt_path
, root_path
, sizeof(mntargs
->lmnt_path
));
466 mntargs
->lmnt_preferred_hash
= 0;
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());
472 panic("failed to mount locker: %d", error
);
474 kheap_free(KHEAP_TEMP
, mntargs
, sizeof(*mntargs
));
476 /* Clear the old mount association. */
477 old_rootvnode
->v_mountedhere
= NULL
;
478 rootvnode
->v_mount
->mnt_vnodecovered
= NULL
;
482 panic("invalid imageboot type: %d", type
);
486 * Get the vnode for '/'.
487 * Set fdp->fd_fd.fd_cdir to reference it.
489 if (VFS_ROOT(TAILQ_LAST(&mountlist
, mntlist
), &newdp
, vfs_context_kernel())) {
490 panic("%s: cannot find root vnode", __FUNCTION__
);
492 DBG_TRACE("%s: old root fsname: %s\n", __FUNCTION__
, old_rootvnode
->v_mount
->mnt_vtable
->vfc_name
);
494 if (old_rootvnode
!= NULL
) {
495 /* remember the old rootvnode, but remove it from mountlist */
496 mount_t old_rootfs
= old_rootvnode
->v_mount
;
498 mount_list_remove(old_rootfs
);
499 mount_lock(old_rootfs
);
500 old_rootfs
->mnt_kern_flag
|= MNTK_BACKS_ROOT
;
501 old_rootfs
->mnt_flag
&= ~MNT_ROOTFS
;
502 mount_unlock(old_rootfs
);
508 lck_rw_lock_exclusive(rootvnode_rw_lock
);
509 /* switch to the new rootvnode */
510 if (update_rootvnode
) {
512 set_fake_bootuuid(rootvnode
->v_mount
);
515 new_rootfs
= rootvnode
->v_mount
;
516 mount_lock(new_rootfs
);
517 new_rootfs
->mnt_flag
|= MNT_ROOTFS
;
518 mount_unlock(new_rootfs
);
520 filedesc0
.fd_cdir
= newdp
;
521 lck_rw_unlock_exclusive(rootvnode_rw_lock
);
523 DBG_TRACE("%s: root switched\n", __FUNCTION__
);
525 if (old_rootvnode
!= NULL
) {
526 #ifdef CONFIG_IMGSRC_ACCESS
527 if (height
>= 0 && PE_imgsrc_mount_supported()) {
528 imgsrc_rootvnodes
[height
] = old_rootvnode
;
530 vnode_get_and_drop_always(old_rootvnode
);
533 #pragma unused(height)
534 vnode_get_and_drop_always(old_rootvnode
);
535 #endif /* CONFIG_IMGSRC_ACCESS */
541 * Return a memory object for given file path.
542 * Also returns a vnode reference for the given file path.
545 ubc_getobject_from_filename(const char *filename
, struct vnode
**vpp
, off_t
*file_size
)
548 struct nameidata ndp
= {};
549 struct vnode
*vp
= NULL
;
551 vfs_context_t ctx
= vfs_context_kernel();
552 void *control
= NULL
;
554 NDINIT(&ndp
, LOOKUP
, OP_OPEN
, LOCKLEAF
, UIO_SYSSPACE
, CAST_USER_ADDR_T(filename
), ctx
);
555 if ((err
= namei(&ndp
)) != 0) {
561 if ((err
= vnode_size(vp
, &fsize
, ctx
)) != 0) {
569 control
= ubc_getobject(vp
, UBC_FLAGS_NONE
);
570 if (control
== NULL
) {
586 imageboot_read_file_from_offset(kalloc_heap_t kheap
, const char *path
, off_t offset
, void **bufp
, size_t *bufszp
)
589 struct nameidata ndp
= {};
590 struct vnode
*vp
= NULL
;
594 bool doclose
= false;
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
);
600 NDINIT(&ndp
, LOOKUP
, OP_OPEN
, LOCKLEAF
, UIO_SYSSPACE
, CAST_USER_ADDR_T(path
), ctx
);
601 if ((err
= namei(&ndp
)) != 0) {
602 AUTHPRNT("namei failed (%s) - %d", path
, err
);
608 if ((err
= vnode_size(vp
, &fsize
, ctx
)) != 0) {
609 AUTHPRNT("failed to get vnode size of %s - %d", path
, err
);
613 panic("negative file size");
616 if ((err
= VNOP_OPEN(vp
, FREAD
, ctx
)) != 0) {
617 AUTHPRNT("failed to open %s - %d", path
, err
);
622 /* if bufsz is non-zero, cap the read at bufsz bytes */
623 if (*bufszp
&& *bufszp
< (size_t)fsize
) {
627 fsize
= (off_t
)MIN((size_t)fsize
, INT_MAX
);
629 buf
= kheap_alloc(kheap
, (size_t)fsize
, Z_WAITOK
);
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
);
641 /* didnt get everything we wanted */
642 AUTHPRNT("Short read of %d bytes at offset %d from %s - %d", (int)fsize
, (int)offset
, path
, resid
);
649 VNOP_CLOSE(vp
, FREAD
, ctx
);
657 kheap_free_safe(kheap
, buf
, (size_t)fsize
);
660 *bufszp
= (size_t)fsize
;
667 imageboot_read_file(kalloc_heap_t kheap
, const char *path
, void **bufp
, size_t *bufszp
)
669 return imageboot_read_file_from_offset(kheap
, path
, 0, bufp
, bufszp
);
672 #if CONFIG_IMAGEBOOT_IMG4 || CONFIG_IMAGEBOOT_CHUNKLIST
674 imgboot_get_image_file(const char *path
, off_t
*fsize
, int *errp
)
676 struct nameidata ndp
= {};
678 vfs_context_t ctx
= vfs_context_kernel();
681 NDINIT(&ndp
, LOOKUP
, OP_OPEN
, LOCKLEAF
, UIO_SYSSPACE
, CAST_USER_ADDR_T(path
), ctx
);
682 if ((err
= namei(&ndp
)) != 0) {
683 AUTHPRNT("Cannot find %s - error %d", path
, err
);
688 if (vp
->v_type
!= VREG
) {
690 AUTHPRNT("%s it not a regular file", path
);
692 if ((err
= vnode_size(vp
, fsize
, ctx
)) != 0) {
693 AUTHPRNT("Cannot get file size of %s - error %d", path
, err
);
704 #endif /* CONFIG_IMAGEBOOT_CHUNKLIST || CONFIG_IMAGEBOOT_CHUNKLIST */
706 #if CONFIG_IMAGEBOOT_IMG4
708 #define APTICKET_NAME "apticket.der"
711 imgboot_get_apticket_path(const char *rootpath
, size_t *sz
)
713 size_t plen
= strlen(rootpath
) + sizeof(APTICKET_NAME
) + 1;
714 char *path
= kheap_alloc(KHEAP_TEMP
, plen
, Z_WAITOK
);
719 strlcpy(path
, rootpath
, plen
);
720 slash
= strrchr(path
, '/');
726 strlcpy(slash
, APTICKET_NAME
, sizeof(APTICKET_NAME
) + 1);
734 authenticate_root_with_img4(const char *rootpath
)
738 size_t ticket_pathsz
= 0;
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
,
746 img4_firmware_t fw
= NULL
;
747 img4_firmware_flags_t fw_flags
= IMG4_FIRMWARE_FLAG_BARE
|
748 IMG4_FIRMWARE_FLAG_SUBSEQUENT_STAGE
;
750 DBG_TRACE("Check %s\n", rootpath
);
752 if (img4if
== NULL
) {
753 AUTHPRNT("AppleImage4 is not ready");
757 ticket_path
= imgboot_get_apticket_path(rootpath
, &ticket_pathsz
);
758 if (ticket_path
== NULL
) {
759 AUTHPRNT("Cannot construct ticket path - out of memory");
763 rv
= imageboot_read_file(KHEAP_TEMP
, ticket_path
, (void **)&tck
.i4b_bytes
, &tck
.i4b_len
);
765 AUTHPRNT("Cannot get a ticket from %s - %d\n", ticket_path
, rv
);
766 goto out_with_ticket_path
;
769 DBG_TRACE("Got %lu bytes of manifest from %s\n", tck
.i4b_len
, ticket_path
);
771 vp
= imgboot_get_image_file(rootpath
, NULL
, &rv
);
773 /* Error message had been printed already */
775 goto out_with_ticket_bytes
;
778 fw
= img4_firmware_new_from_vnode_4xnu(IMG4_RUNTIME_DEFAULT
, &exec
, 'rosi',
781 AUTHPRNT("Could not allocate new firmware");
783 goto out_with_ticket_bytes
;
786 img4_firmware_attach_manifest(fw
, &tck
);
787 rv
= img4_firmware_evaluate(fw
, img4_chip_select_personalized_ap(), NULL
);
789 out_with_ticket_bytes
:
790 kheap_free_safe(KHEAP_TEMP
, tck
.i4b_bytes
, tck
.i4b_len
);
791 out_with_ticket_path
:
792 kheap_free_safe(KHEAP_TEMP
, ticket_path
, ticket_pathsz
);
794 img4_firmware_destroy(&fw
);
797 #endif /* CONFIG_IMAGEBOOT_IMG4 */
801 * Attach the image at 'path' as a ramdisk and mount it as our new rootfs.
802 * All existing mounts are first umounted.
805 imageboot_mount_ramdisk(const char *path
)
815 /* Read our target image from disk */
816 err
= imageboot_read_file(KHEAP_DATA_BUFFERS
, path
, &buf
, &bufsz
);
818 printf("%s: failed: imageboot_read_file() = %d\n", __func__
, err
);
821 DBG_TRACE("%s: read '%s' sz = %lu\n", __func__
, path
, bufsz
);
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
];
830 imgsrc_rootvnodes
[i
] = NULLVP
;
832 mount_t mnt
= vn
->v_mount
;
834 mnt
->mnt_flag
|= MNT_ROOTFS
;
842 mount_list_add(rootvnode
->v_mount
);
845 /* ... and unmount everything */
848 lck_rw_lock_exclusive(rootvnode_rw_lock
);
849 filedesc0
.fd_cdir
= NULL
;
854 lck_rw_unlock_exclusive(rootvnode_rw_lock
);
855 vnode_get_and_drop_always(tvp
);
857 /* Attach the ramfs image ... */
858 err
= di_root_ramfile_buf(buf
, bufsz
, rootdevice
, DEVMAXNAMESIZE
, &dev
);
860 printf("%s: failed: di_root_ramfile_buf() = %d\n", __func__
, err
);
864 /* ... and mount it */
867 err
= vfs_mountroot();
869 printf("%s: failed: vfs_mountroot() = %d\n", __func__
, err
);
873 /* Switch to new root vnode */
874 if (VFS_ROOT(TAILQ_LAST(&mountlist
, mntlist
), &newdp
, vfs_context_kernel())) {
875 panic("%s: cannot find root vnode", __func__
);
879 lck_rw_lock_exclusive(rootvnode_rw_lock
);
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
);
887 set_fake_bootuuid(new_rootfs
);
889 filedesc0
.fd_cdir
= newdp
;
890 lck_rw_unlock_exclusive(rootvnode_rw_lock
);
894 DBG_TRACE("%s: root switched\n", __func__
);
898 kheap_free_safe(KHEAP_DATA_BUFFERS
, buf
, bufsz
);
904 * If the path is in <file://> URL format then we allocate memory and decode it,
905 * otherwise return the same pointer.
907 * Caller is expected to check if the pointers are different.
910 url_to_path(char *url_path
, size_t *sz
)
912 char *path
= url_path
;
913 size_t len
= strlen(kIBFilePrefix
);
915 if (strncmp(kIBFilePrefix
, url_path
, len
) == 0) {
916 /* its a URL - remove the file:// prefix and percent-decode */
919 len
= strlen(url_path
);
921 /* Make a copy of the path to URL-decode */
922 path
= kheap_alloc(KHEAP_TEMP
, len
+ 1, Z_WAITOK
);
924 panic("imageboot path allocation failed - cannot allocate %d bytes\n", (int)len
);
927 strlcpy(path
, url_path
, len
+ 1);
931 panic("Bogus imageboot path URL - missing path\n");
934 DBG_TRACE("%s: root image URL <%s> becomes %s\n", __func__
, url_path
, path
);
941 imageboot_setup_new(imageboot_type_t type
)
944 char *root_path
= NULL
;
946 boolean_t done
= FALSE
;
947 boolean_t auth_root
= TRUE
;
948 boolean_t ramdisk_root
= FALSE
;
950 root_path
= zalloc(ZV_NAMEI
);
951 assert(root_path
!= NULL
);
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");
959 DBG_TRACE("%s: root fsname: %s\n", __FUNCTION__
, rootvnode
->v_mount
->mnt_vtable
->vfc_name
);
962 * The locker path is a path, not a URL, so just pass it directly to
963 * imageboot_mount_image().
965 error
= imageboot_mount_image(root_path
, 0, type
);
967 panic("failed to mount system locker: %d", error
);
973 #endif /* CONFIG_LOCKERBOOT */
975 unsigned imgboot_arg
;
976 if (PE_parse_boot_argn("-rootdmg-ramdisk", &imgboot_arg
, sizeof(imgboot_arg
))) {
980 if (PE_parse_boot_argn(IMAGEBOOT_CONTAINER_ARG
, root_path
, MAXPATHLEN
) == TRUE
) {
981 printf("%s: container image url is %s\n", __FUNCTION__
, root_path
);
982 error
= imageboot_mount_image(root_path
, height
, type
);
984 panic("Failed to mount container image.");
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
) {
993 panic("%s specified without %s or %s?\n", IMAGEBOOT_CONTAINER_ARG
, IMAGEBOOT_AUTHROOT_ARG
, IMAGEBOOT_ROOT_ARG
);
998 printf("%s: root image URL is '%s'\n", __func__
, root_path
);
1000 /* Make a copy of the path to URL-decode */
1002 char *path
= url_to_path(root_path
, &pathsz
);
1005 #if CONFIG_IMAGEBOOT_CHUNKLIST
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.
1012 AUTHDBG("authenticating root image at %s", path
);
1013 error
= authenticate_root_with_chunklist(path
, &auth_root
);
1015 panic("root image authentication failed (err = %d)\n", error
);
1017 AUTHDBG("successfully authenticated %s", path
);
1022 error
= imageboot_mount_ramdisk(path
);
1024 error
= imageboot_mount_image(root_path
, height
, type
);
1027 if (path
!= root_path
) {
1028 kheap_free_safe(KHEAP_TEMP
, path
, pathsz
);
1032 panic("Failed to mount root image (err=%d, auth=%d, ramdisk=%d)\n",
1033 error
, auth_root
, ramdisk_root
);
1036 #if CONFIG_IMAGEBOOT_CHUNKLIST
1038 /* check that the image version matches the running kernel */
1039 AUTHDBG("checking root image version");
1040 error
= authenticate_root_version_check();
1042 panic("root image version check failed");
1044 AUTHDBG("root image version matches kernel");
1052 zfree(ZV_NAMEI
, root_path
);
1056 __private_extern__
void
1057 imageboot_setup(imageboot_type_t type
)
1060 char *root_path
= NULL
;
1062 DBG_TRACE("%s: entry\n", __FUNCTION__
);
1064 if (rootvnode
== NULL
) {
1065 panic("imageboot_setup: rootvnode is NULL.");
1069 * New boot-arg scheme:
1070 * root-dmg : the dmg that will be the root filesystem, authenticated by default.
1071 * auth-root-dmg : same as root-dmg.
1072 * container-dmg : an optional dmg that contains the root-dmg.
1073 * locker : the locker that will be the root filesystem -- mutually
1074 * exclusive with any other boot-arg.
1076 if (imageboot_setup_new(type
)) {
1080 root_path
= zalloc(ZV_NAMEI
);
1081 assert(root_path
!= NULL
);
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
1087 * device vnode created for it, and should not show up in getfsstat() until exposed
1088 * with MNT_IMGSRC. We just make it the temporary root.
1090 #if CONFIG_IMAGEBOOT_IMG4
1091 if (PE_parse_boot_argn("arp0", root_path
, MAXPATHLEN
)) {
1093 char *path
= url_to_path(root_path
, &pathsz
);
1097 if (authenticate_root_with_img4(path
)) {
1098 panic("Root image %s does not match the manifest\n", root_path
);
1100 if (path
!= root_path
) {
1101 kheap_free_safe(KHEAP_TEMP
, path
, pathsz
);
1104 #endif /* CONFIG_IMAGEBOOT_IMG4 */
1105 if ((PE_parse_boot_argn("rp", root_path
, MAXPATHLEN
) == FALSE
) &&
1106 (PE_parse_boot_argn("rp0", root_path
, MAXPATHLEN
) == FALSE
)) {
1107 panic("%s: no valid path to image.\n", __FUNCTION__
);
1110 DBG_TRACE("%s: root image url is %s\n", __FUNCTION__
, root_path
);
1112 error
= imageboot_mount_image(root_path
, 0, type
);
1114 panic("Failed on first stage of imageboot.");
1118 * See if we are rooting from a nested image
1120 if (PE_parse_boot_argn("rp1", root_path
, MAXPATHLEN
) == FALSE
) {
1124 printf("%s: second level root image url is %s\n", __FUNCTION__
, root_path
);
1127 * If we fail to set up second image, it's not a given that we
1128 * can safely root off the first.
1130 error
= imageboot_mount_image(root_path
, 1, type
);
1132 panic("Failed on second stage of imageboot.");
1136 zfree(ZV_NAMEI
, root_path
);
1138 DBG_TRACE("%s: exit\n", __FUNCTION__
);