#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/proc_internal.h>
#include <sys/systm.h>
#include <sys/systm.h>
#include <sys/mount_internal.h>
#include <sys/filedesc.h>
#include <sys/vnode_internal.h>
#include <sys/imageboot.h>
#include <kern/assert.h>
#include <pexpert/pexpert.h>
extern struct filedesc filedesc0;
extern int (*mountroot)(void);
extern char rootdevice[];
#define DEBUG_IMAGEBOOT 0
#if DEBUG_IMAGEBOOT
#define DBG_TRACE(...) printf(__VA_ARGS__)
#else
#define DBG_TRACE(...) do {} while(0)
#endif
extern int di_root_image(const char *path, char devname[], dev_t *dev_p);
static boolean_t imageboot_setup_new(void);
#define kIBFilePrefix "file://"
__private_extern__ int
imageboot_format_is_valid(const char *root_path)
{
return (strncmp(root_path, kIBFilePrefix,
strlen(kIBFilePrefix)) == 0);
}
static void
vnode_get_and_drop_always(vnode_t vp)
{
vnode_getalways(vp);
vnode_rele(vp);
vnode_put(vp);
}
__private_extern__ int
imageboot_needed(void)
{
int result = 0;
char *root_path = NULL;
DBG_TRACE("%s: checking for presence of root path\n", __FUNCTION__);
MALLOC_ZONE(root_path, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
if (root_path == NULL)
panic("%s: M_NAMEI zone exhausted", __FUNCTION__);
if (!(PE_parse_boot_argn("rp0", root_path, MAXPATHLEN) ||
PE_parse_boot_argn("rp", root_path, MAXPATHLEN) ||
PE_parse_boot_argn(IMAGEBOOT_ROOT_ARG, root_path, MAXPATHLEN))) {
goto out;
}
if (imageboot_format_is_valid(root_path)) {
DBG_TRACE("%s: Found %s\n", __FUNCTION__, root_path);
} else {
goto out;
}
result = 1;
if (!(PE_parse_boot_argn("rp1", root_path, MAXPATHLEN) ||
PE_parse_boot_argn(IMAGEBOOT_CONTAINER_ARG, root_path, MAXPATHLEN))) {
goto out;
}
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);
}
__private_extern__ int
imageboot_mount_image(const char *root_path, int height)
{
dev_t dev;
int error;
vnode_t old_rootvnode = NULL;
vnode_t newdp;
mount_t new_rootfs;
error = di_root_image(root_path, rootdevice, &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");
}
if (VFS_ROOT(TAILQ_LAST(&mountlist,mntlist), &newdp, vfs_context_kernel()))
panic("%s: cannot find root vnode", __FUNCTION__);
if (rootvnode != NULL) {
mount_t old_rootfs;
old_rootvnode = rootvnode;
old_rootfs = rootvnode->v_mount;
mount_list_remove(old_rootfs);
mount_lock(old_rootfs);
#ifdef CONFIG_IMGSRC_ACCESS
old_rootfs->mnt_kern_flag |= MNTK_BACKS_ROOT;
#endif
old_rootfs->mnt_flag &= ~MNT_ROOTFS;
mount_unlock(old_rootfs);
}
rootvnode = newdp;
new_rootfs = rootvnode->v_mount;
mount_lock(new_rootfs);
new_rootfs->mnt_flag |= MNT_ROOTFS;
mount_unlock(new_rootfs);
vnode_ref(newdp);
vnode_put(newdp);
filedesc0.fd_cdir = newdp;
DBG_TRACE("%s: root switched\n", __FUNCTION__);
if (old_rootvnode != NULL) {
#ifdef CONFIG_IMGSRC_ACCESS
if (height >= 0 && PE_imgsrc_mount_supported()) {
imgsrc_rootvnodes[height] = old_rootvnode;
} else {
vnode_get_and_drop_always(old_rootvnode);
}
#else
height = 0;
vnode_get_and_drop_always(old_rootvnode);
#endif
}
return 0;
}
static boolean_t
imageboot_setup_new()
{
int error;
char *root_path = NULL;
int height = 0;
boolean_t done = FALSE;
MALLOC_ZONE(root_path, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
assert(root_path != NULL);
if(PE_parse_boot_argn(IMAGEBOOT_CONTAINER_ARG, root_path, MAXPATHLEN) == TRUE) {
printf("%s: container image url is %s\n", __FUNCTION__, root_path);
error = imageboot_mount_image(root_path, height);
if (error != 0) {
panic("Failed to mount container image.");
}
height++;
}
if (PE_parse_boot_argn(IMAGEBOOT_ROOT_ARG, root_path, MAXPATHLEN) == FALSE) {
if (height > 0) {
panic("%s specified without %s?\n", IMAGEBOOT_CONTAINER_ARG, IMAGEBOOT_ROOT_ARG);
}
goto out;
}
printf("%s: root image url is %s\n", __FUNCTION__, root_path);
error = imageboot_mount_image(root_path, height);
if (error != 0) {
panic("Failed to mount root image.");
}
done = TRUE;
out:
FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI);
return done;
}
__private_extern__ void
imageboot_setup()
{
int error = 0;
char *root_path = NULL;
DBG_TRACE("%s: entry\n", __FUNCTION__);
if (rootvnode == NULL) {
panic("imageboot_setup: rootvnode is NULL.");
}
if (imageboot_setup_new()) {
return;
}
MALLOC_ZONE(root_path, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
assert(root_path != NULL);
if((PE_parse_boot_argn("rp", root_path, MAXPATHLEN) == FALSE) &&
(PE_parse_boot_argn("rp0", root_path, MAXPATHLEN) == FALSE)) {
panic("%s: no valid path to image.\n", __FUNCTION__);
}
printf("%s: root image url is %s\n", __FUNCTION__, root_path);
error = imageboot_mount_image(root_path, 0);
if (error) {
panic("Failed on first stage of imageboot.");
}
if(PE_parse_boot_argn("rp1", root_path, MAXPATHLEN) == FALSE) {
goto done;
}
printf("%s: second level root image url is %s\n", __FUNCTION__, root_path);
error = imageboot_mount_image(root_path, 1);
if (error) {
panic("Failed on second stage of imageboot.");
}
done:
FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI);
DBG_TRACE("%s: exit\n", __FUNCTION__);
return;
}