+ }
+
+#if CONFIG_LOCKERBOOT
+ if (PE_parse_boot_argn(IMAGEBOOT_LOCKER_ARG, root_path, MAXPATHLEN)) {
+ result = IMAGEBOOT_LOCKER;
+ goto out;
+ }
+#endif
+
+ /* Check for first layer */
+ if (!(PE_parse_boot_argn("rp0", root_path, MAXPATHLEN) ||
+#if CONFIG_IMAGEBOOT_IMG4
+ PE_parse_boot_argn("arp0", root_path, MAXPATHLEN) ||
+#endif
+ PE_parse_boot_argn("rp", root_path, MAXPATHLEN) ||
+ PE_parse_boot_argn(IMAGEBOOT_ROOT_ARG, root_path, MAXPATHLEN) ||
+ PE_parse_boot_argn(IMAGEBOOT_AUTHROOT_ARG, root_path, MAXPATHLEN))) {
+ goto out;
+ }
+
+ /* Sanity-check first layer */
+ if (imageboot_format_is_valid(root_path)) {
+ DBG_TRACE("%s: Found %s\n", __FUNCTION__, root_path);
+ } else {
+ goto out;
+ }
+
+ result = IMAGEBOOT_DMG;
+
+ /* Check for second layer */
+ if (!(PE_parse_boot_argn("rp1", root_path, MAXPATHLEN) ||
+ PE_parse_boot_argn(IMAGEBOOT_CONTAINER_ARG, root_path, MAXPATHLEN))) {
+ goto out;
+ }
+
+ /* Sanity-check second layer */
+ if (imageboot_format_is_valid(root_path)) {
+ DBG_TRACE("%s: Found %s\n", __FUNCTION__, root_path);
+ } else {
+ panic("%s: Invalid URL scheme for %s\n",
+ __FUNCTION__, root_path);
+ }
+
+out:
+ FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI);
+
+ return result;
+}
+
+
+/*
+ * Swaps in new root filesystem based on image path.
+ * Current root filesystem is removed from mount list and
+ * tagged MNTK_BACKS_ROOT, MNT_ROOTFS is cleared on it, and
+ * "rootvnode" is reset. Root vnode of currentroot filesystem
+ * is returned with usecount (no iocount).
+ */
+__private_extern__ int
+imageboot_mount_image(const char *root_path, int height, imageboot_type_t type)
+{
+ dev_t dev;
+ int error;
+ /*
+ * Need to stash this here since we may do a kernel_mount() on /, which will
+ * automatically update the rootvnode global. Note that vfs_mountroot() does
+ * not update that global, which is a bit weird.
+ */
+ vnode_t old_rootvnode = rootvnode;
+ vnode_t newdp;
+ mount_t new_rootfs;
+ boolean_t update_rootvnode = FALSE;
+
+ if (type == IMAGEBOOT_DMG) {
+ error = di_root_image(root_path, rootdevice, DEVMAXNAMESIZE, &dev);
+ if (error) {
+ panic("%s: di_root_image failed: %d\n", __FUNCTION__, error);
+ }
+
+ rootdev = dev;
+ mountroot = NULL;
+ printf("%s: root device 0x%x\n", __FUNCTION__, rootdev);
+ error = vfs_mountroot();
+ if (error != 0) {
+ panic("vfs_mountroot() failed.\n");
+ }
+
+ update_rootvnode = TRUE;
+ }
+#if CONFIG_LOCKERBOOT
+ else if (type == IMAGEBOOT_LOCKER) {
+ locker_mount_args_t *mntargs = kalloc(sizeof(*mntargs));
+ if (!mntargs) {
+ panic("could not alloc mount args");
+ }
+
+ strlcpy(mntargs->lmnt_path, root_path, sizeof(mntargs->lmnt_path));
+ mntargs->lmnt_preferred_hash = 0;
+
+ DBG_TRACE("%s: mounting locker: %s\n", __FUNCTION__, root_path);
+ error = kernel_mount(LOCKERFS_NAME, NULLVP, NULLVP, "/",
+ mntargs, sizeof(*mntargs), 0, 0, vfs_context_kernel());
+ if (error) {
+ panic("failed to mount locker: %d", error);
+ }
+ kfree(mntargs, sizeof(*mntargs));
+
+ /* Clear the old mount association. */
+ old_rootvnode->v_mountedhere = NULL;
+ rootvnode->v_mount->mnt_vnodecovered = NULL;
+ }
+#endif
+ else {
+ panic("invalid imageboot type: %d", type);
+ }
+
+ /*
+ * Get the vnode for '/'.
+ * Set fdp->fd_fd.fd_cdir to reference it.
+ */
+ if (VFS_ROOT(TAILQ_LAST(&mountlist, mntlist), &newdp, vfs_context_kernel())) {
+ panic("%s: cannot find root vnode", __FUNCTION__);
+ }
+ DBG_TRACE("%s: old root fsname: %s\n", __FUNCTION__, old_rootvnode->v_mount->mnt_vtable->vfc_name);