+ buf = kheap_alloc(kheap, (size_t)fsize, Z_WAITOK);
+ if (buf == NULL) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ if ((err = vn_rdwr(UIO_READ, vp, (caddr_t)buf, (int)fsize, offset, UIO_SYSSPACE, IO_NODELOCKED, kerncred, &resid, p)) != 0) {
+ AUTHPRNT("Cannot read %d bytes at offset %d from %s - %d", (int)fsize, (int)offset, path, err);
+ goto out;
+ }
+
+ if (resid) {
+ /* didnt get everything we wanted */
+ AUTHPRNT("Short read of %d bytes at offset %d from %s - %d", (int)fsize, (int)offset, path, resid);
+ err = EINVAL;
+ goto out;
+ }
+
+out:
+ if (doclose) {
+ VNOP_CLOSE(vp, FREAD, ctx);
+ }
+ if (vp) {
+ vnode_put(vp);
+ vp = NULL;
+ }
+
+ if (err) {
+ kheap_free_safe(kheap, buf, (size_t)fsize);
+ } else {
+ *bufp = buf;
+ *bufszp = (size_t)fsize;
+ }
+
+ return err;
+}
+
+int
+imageboot_read_file(kalloc_heap_t kheap, const char *path, void **bufp, size_t *bufszp)
+{
+ return imageboot_read_file_from_offset(kheap, path, 0, bufp, bufszp);
+}
+
+#if CONFIG_IMAGEBOOT_IMG4 || CONFIG_IMAGEBOOT_CHUNKLIST
+vnode_t
+imgboot_get_image_file(const char *path, off_t *fsize, int *errp)
+{
+ struct nameidata ndp = {};
+ vnode_t vp = NULL;
+ vfs_context_t ctx = vfs_context_kernel();
+ int err;
+
+ NDINIT(&ndp, LOOKUP, OP_OPEN, LOCKLEAF, UIO_SYSSPACE, CAST_USER_ADDR_T(path), ctx);
+ if ((err = namei(&ndp)) != 0) {
+ AUTHPRNT("Cannot find %s - error %d", path, err);
+ } else {
+ nameidone(&ndp);
+ vp = ndp.ni_vp;
+
+ if (vp->v_type != VREG) {
+ err = EINVAL;
+ AUTHPRNT("%s it not a regular file", path);
+ } else if (fsize) {
+ if ((err = vnode_size(vp, fsize, ctx)) != 0) {
+ AUTHPRNT("Cannot get file size of %s - error %d", path, err);
+ }
+ }
+ }
+
+ if (err) {
+ *errp = err;
+ vp = NULL;
+ }
+ return vp;
+}
+#endif /* CONFIG_IMAGEBOOT_CHUNKLIST || CONFIG_IMAGEBOOT_CHUNKLIST */
+
+#if CONFIG_IMAGEBOOT_IMG4
+
+#define APTICKET_NAME "apticket.der"
+
+static char *
+imgboot_get_apticket_path(const char *rootpath, size_t *sz)
+{
+ size_t plen = strlen(rootpath) + sizeof(APTICKET_NAME) + 1;
+ char *path = kheap_alloc(KHEAP_TEMP, plen, Z_WAITOK);
+
+ if (path) {
+ char *slash;
+
+ strlcpy(path, rootpath, plen);
+ slash = strrchr(path, '/');
+ if (slash == NULL) {
+ slash = path;
+ } else {
+ slash++;
+ }
+ strlcpy(slash, APTICKET_NAME, sizeof(APTICKET_NAME) + 1);
+ }
+
+ *sz = plen;
+ return path;
+}
+
+static int
+authenticate_root_with_img4(const char *rootpath)
+{
+ errno_t rv;
+ vnode_t vp;
+ size_t ticket_pathsz = 0;
+ char *ticket_path;
+ img4_buff_t tck = IMG4_BUFF_INIT;
+ img4_firmware_execution_context_t exec = {
+ .i4fex_version = IMG4_FIRMWARE_EXECUTION_CONTEXT_STRUCT_VERSION,
+ .i4fex_execute = NULL,
+ .i4fex_context = NULL,
+ };
+ img4_firmware_t fw = NULL;
+ img4_firmware_flags_t fw_flags = IMG4_FIRMWARE_FLAG_BARE |
+ IMG4_FIRMWARE_FLAG_SUBSEQUENT_STAGE;
+
+ DBG_TRACE("Check %s\n", rootpath);
+
+ if (img4if == NULL) {
+ AUTHPRNT("AppleImage4 is not ready");
+ return EAGAIN;
+ }
+
+ ticket_path = imgboot_get_apticket_path(rootpath, &ticket_pathsz);
+ if (ticket_path == NULL) {
+ AUTHPRNT("Cannot construct ticket path - out of memory");
+ return ENOMEM;
+ }
+
+ rv = imageboot_read_file(KHEAP_TEMP, ticket_path, (void **)&tck.i4b_bytes, &tck.i4b_len);
+ if (rv) {
+ AUTHPRNT("Cannot get a ticket from %s - %d\n", ticket_path, rv);
+ goto out_with_ticket_path;
+ }
+
+ DBG_TRACE("Got %lu bytes of manifest from %s\n", tck.i4b_len, ticket_path);
+
+ vp = imgboot_get_image_file(rootpath, NULL, &rv);
+ if (vp == NULL) {
+ /* Error message had been printed already */
+ rv = EIO;
+ goto out_with_ticket_bytes;
+ }
+
+ fw = img4_firmware_new_from_vnode_4xnu(IMG4_RUNTIME_DEFAULT, &exec, 'rosi',
+ vp, fw_flags);
+ if (!fw) {
+ AUTHPRNT("Could not allocate new firmware");
+ rv = ENOMEM;
+ goto out_with_ticket_bytes;
+ }
+
+ img4_firmware_attach_manifest(fw, &tck);
+ rv = img4_firmware_evaluate(fw, img4_chip_select_personalized_ap(), NULL);
+
+out_with_ticket_bytes:
+ kheap_free_safe(KHEAP_TEMP, tck.i4b_bytes, tck.i4b_len);
+out_with_ticket_path:
+ kheap_free_safe(KHEAP_TEMP, ticket_path, ticket_pathsz);
+
+ img4_firmware_destroy(&fw);
+ return rv;
+}
+#endif /* CONFIG_IMAGEBOOT_IMG4 */
+
+
+/*
+ * Attach the image at 'path' as a ramdisk and mount it as our new rootfs.
+ * All existing mounts are first umounted.
+ */
+static int
+imageboot_mount_ramdisk(const char *path)
+{
+ int err = 0;
+ size_t bufsz = 0;
+ void *buf = NULL;
+ dev_t dev;
+ vnode_t newdp;
+ vnode_t tvp;
+ mount_t new_rootfs;
+
+ /* Read our target image from disk */
+ err = imageboot_read_file(KHEAP_DATA_BUFFERS, path, &buf, &bufsz);
+ if (err) {
+ printf("%s: failed: imageboot_read_file() = %d\n", __func__, err);
+ goto out;
+ }
+ DBG_TRACE("%s: read '%s' sz = %lu\n", __func__, path, bufsz);
+
+#if CONFIG_IMGSRC_ACCESS
+ /* Re-add all root mounts to the mount list in the correct order... */
+ mount_list_remove(rootvnode->v_mount);
+ for (int i = 0; i < MAX_IMAGEBOOT_NESTING; i++) {
+ struct vnode *vn = imgsrc_rootvnodes[i];
+ if (vn) {
+ vnode_getalways(vn);
+ imgsrc_rootvnodes[i] = NULLVP;
+
+ mount_t mnt = vn->v_mount;
+ mount_lock(mnt);
+ mnt->mnt_flag |= MNT_ROOTFS;
+ mount_list_add(mnt);
+ mount_unlock(mnt);
+
+ vnode_rele(vn);
+ vnode_put(vn);
+ }
+ }
+ mount_list_add(rootvnode->v_mount);
+#endif
+
+ /* ... and unmount everything */
+ vfs_unmountall();
+
+ lck_rw_lock_exclusive(rootvnode_rw_lock);
+ filedesc0.fd_cdir = NULL;
+ tvp = rootvnode;
+ rootvnode = NULL;
+ rootvp = NULLVP;
+ rootdev = NODEV;
+ lck_rw_unlock_exclusive(rootvnode_rw_lock);
+ vnode_get_and_drop_always(tvp);
+
+ /* Attach the ramfs image ... */
+ err = di_root_ramfile_buf(buf, bufsz, rootdevice, DEVMAXNAMESIZE, &dev);
+ if (err) {
+ printf("%s: failed: di_root_ramfile_buf() = %d\n", __func__, err);
+ goto out;
+ }
+
+ /* ... and mount it */