#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysmacros.h>
#include <sys/signal.h>
#include <sys/cred.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/conf.h>
#include <sys/buf.h>
#include <sys/systm.h>
#include <sys/vm.h>
#include <sys/reboot.h>
#include <sys/file.h>
#include <sys/vfs.h>
#include <sys/vnode.h>
#include <sys/errno.h>
#include <sys/kmem.h>
#include <sys/uio.h>
#include <sys/open.h>
#include <sys/mount.h>
#include <sys/kobj.h>
#include <sys/bootconf.h>
#include <sys/sysconf.h>
#include <sys/modctl.h>
#include <sys/autoconf.h>
#include <sys/debug.h>
#include <sys/fs/snode.h>
#include <fs/fs_subr.h>
#include <sys/socket.h>
#include <net/if.h>
#include <sys/mkdev.h>
#include <sys/cmn_err.h>
#include <sys/console.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/hwconf.h>
#include <sys/dc_ki.h>
#include <sys/promif.h>
#include <sys/bootprops.h>
static int preload_module(struct sysparam *, void *);
static struct vfssw *getfstype(char *, char *, size_t);
static int getphysdev(char *, char *, size_t);
static int load_bootpath_drivers(char *bootpath);
static int load_boot_driver(char *drv);
static int load_boot_platform_modules(char *drv);
static dev_info_t *path_to_devinfo(char *path);
static boolean_t netboot_over_ib(char *bootpath);
static boolean_t netboot_over_iscsi(void);
static struct modlmisc modlmisc = {
&mod_miscops, "root and swap configuration"
};
static struct modlinkage modlinkage = {
MODREV_1, (void *)&modlmisc, NULL
};
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
_fini(void)
{
return (mod_remove(&modlinkage));
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
extern ib_boot_prop_t *iscsiboot_prop;
int
rootconf(void)
{
int error;
struct vfssw *vsw;
extern void pm_init(void);
int ret = -1;
BMDPRINTF(("rootconf: fstype %s\n", rootfs.bo_fstype));
BMDPRINTF(("rootconf: name %s\n", rootfs.bo_name));
BMDPRINTF(("rootconf: flags 0x%x\n", rootfs.bo_flags));
BMDPRINTF(("rootconf: obp_bootpath %s\n", obp_bootpath));
if (error = clboot_rootconf())
return (error);
if (strncmp(rootfs.bo_fstype, "nfs", 3) == 0) {
if (modload("fs", "nfs") == -1) {
cmn_err(CE_CONT, "Cannot initialize %s filesystem\n",
rootfs.bo_fstype);
return (ENXIO);
}
} else {
if (modload("fs", rootfs.bo_fstype) == -1) {
cmn_err(CE_CONT, "Cannot initialize %s filesystem\n",
rootfs.bo_fstype);
return (ENXIO);
}
}
RLOCK_VFSSW();
vsw = vfs_getvfsswbyname(rootfs.bo_fstype);
RUNLOCK_VFSSW();
if (vsw == NULL) {
cmn_err(CE_CONT, "Cannot find %s filesystem\n",
rootfs.bo_fstype);
return (ENXIO);
}
VFS_INIT(rootvfs, &vsw->vsw_vfsops, (caddr_t)0);
VFS_HOLD(rootvfs);
pm_init();
if (netboot && iscsiboot_prop) {
cmn_err(CE_WARN, "NFS boot and iSCSI boot"
" shouldn't happen in the same time");
return (EINVAL);
}
if (netboot || iscsiboot_prop) {
ret = strplumb();
if (ret != 0) {
cmn_err(CE_WARN, "Cannot plumb network device %d", ret);
return (EFAULT);
}
}
if ((ret == 0) && iscsiboot_prop) {
ret = modload("drv", "iscsi");
if (ret == -1) {
cmn_err(CE_WARN, "Failed to load iscsi module");
iscsi_boot_prop_free();
return (EINVAL);
} else {
if (!i_ddi_attach_pseudo_node("iscsi")) {
cmn_err(CE_WARN,
"Failed to attach iscsi driver");
iscsi_boot_prop_free();
return (ENODEV);
}
}
}
BMDPRINTF(("rootconf: calling VFS_MOUNTROOT %s\n", rootfs.bo_fstype));
error = VFS_MOUNTROOT(rootvfs, ROOT_INIT);
vfs_unrefvfssw(vsw);
rootdev = rootvfs->vfs_dev;
if (error)
cmn_err(CE_CONT, "Cannot mount root on %s fstype %s\n",
rootfs.bo_name, rootfs.bo_fstype);
else
cmn_err(CE_CONT, "?root on %s fstype %s\n",
rootfs.bo_name, rootfs.bo_fstype);
return (error);
}
dev_t
getrootdev(void)
{
dev_t d;
d = ddi_pathname_to_dev_t(rootfs.bo_name);
if ((d == NODEV) && (iscsiboot_prop != NULL)) {
get_iscsi_bootpath_phy(rootfs.bo_name);
d = ddi_pathname_to_dev_t(rootfs.bo_name);
}
if (d == NODEV)
cmn_err(CE_CONT, "Cannot assemble drivers for root %s\n",
rootfs.bo_name);
return (d);
}
void
getfsname(char *askfor, char *name, size_t namelen)
{
if (boothowto & RB_ASKNAME) {
printf("%s name: ", askfor);
console_gets(name, namelen);
}
}
static int
preload_module(struct sysparam *sysp, void *p)
{
static char *wmesg = "forceload of %s failed";
char *name;
name = sysp->sys_ptr;
BMDPRINTF(("preload_module: %s\n", name));
if (modloadonly(NULL, name) < 0)
cmn_err(CE_WARN, wmesg, name);
return (0);
}
int
loadrootmodules(void)
{
struct vfssw *vsw;
char *this;
char *name;
int err;
int i, proplen;
extern char *impl_module_list[];
extern char *platform_module_list[];
ASSERT(ddi_root_node());
BMDPRINTF(("loadrootmodules: fstype %s\n", rootfs.bo_fstype));
BMDPRINTF(("loadrootmodules: name %s\n", rootfs.bo_name));
BMDPRINTF(("loadrootmodules: flags 0x%x\n", rootfs.bo_flags));
rootfs.bo_fstype[0] = '\0';
rootfs.bo_name[0] = '\0';
if ((vsw = getfstype("root", rootfs.bo_fstype, BO_MAXFSNAME)) == NULL)
return (ENXIO);
(void) strcpy(rootfs.bo_fstype, vsw->vsw_name);
vfs_unrefvfssw(vsw);
BMDPRINTF(("loadrootmodules: impl_module_list\n"));
for (i = 0; (this = impl_module_list[i]) != NULL; i++) {
if ((err = load_boot_driver(this)) != 0) {
cmn_err(CE_WARN, "Cannot load drv/%s", this);
return (err);
}
}
BMDPRINTF(("loadrootmodules: platform_module_list\n"));
for (i = 0; (this = platform_module_list[i]) != NULL; i++) {
if ((err = load_boot_platform_modules(this)) != 0) {
cmn_err(CE_WARN, "Cannot load drv/%s", this);
return (err);
}
}
loop:
(void) getphysdev("root", rootfs.bo_name, BO_MAXOBJNAME);
err = 0;
BMDPRINTF(("loadrootmodules: rootfs %s\n", rootfs.bo_name));
name = rootfs.bo_name;
err = load_bootpath_drivers(rootfs.bo_name);
if ((err == 0) && obp_bootpath[0] != '\0') {
BMDPRINTF(("loadrootmodules: obp_bootpath %s\n", obp_bootpath));
name = obp_bootpath;
err = load_bootpath_drivers(obp_bootpath);
}
if (err != 0) {
cmn_err(CE_CONT, "Cannot load drivers for %s\n", name);
goto out;
}
proplen = BOP_GETPROPLEN(bootops, "bootp-response");
if (proplen > 0) {
dhcack = kmem_zalloc(proplen, KM_SLEEP);
if (BOP_GETPROP(bootops, "bootp-response", dhcack) == -1) {
cmn_err(CE_WARN, "BOP_GETPROP of "
"\"bootp-response\" failed\n");
kmem_free(dhcack, dhcacklen);
dhcack = NULL;
goto out;
}
dhcacklen = proplen;
proplen = BOP_GETPROPLEN(bootops, "netdev-path");
if (proplen > 0) {
netdev_path = kmem_zalloc(proplen, KM_SLEEP);
if (BOP_GETPROP(bootops, "netdev-path",
(uchar_t *)netdev_path) == -1) {
cmn_err(CE_WARN, "BOP_GETPROP of "
"\"netdev-path\" failed\n");
kmem_free(netdev_path, proplen);
goto out;
}
}
}
BMDPRINTF(("loadrootmodules: preload_module\n"));
(void) mod_sysctl_type(MOD_FORCELOAD, preload_module, NULL);
if (strncmp(rootfs.bo_fstype, "nfs", 3) == 0) {
++netboot;
if ((err = modloadonly("dacf", "net_dacf")) < 0) {
cmn_err(CE_CONT, "Cannot load dacf/net_dacf\n");
goto out;
}
if ((err = modload("misc", "tlimod")) < 0) {
cmn_err(CE_CONT, "Cannot load misc/tlimod\n");
goto out;
}
if ((err = modload("strmod", "rpcmod")) < 0) {
cmn_err(CE_CONT, "Cannot load strmod/rpcmod\n");
goto out;
}
if ((err = modload("misc", "nfs_dlboot")) < 0) {
cmn_err(CE_CONT, "Cannot load misc/nfs_dlboot\n");
goto out;
}
if ((err = modload("mac", "mac_ether")) < 0) {
cmn_err(CE_CONT, "Cannot load mac/mac_ether\n");
goto out;
}
if ((err = modload("misc", "strplumb")) < 0) {
cmn_err(CE_CONT, "Cannot load misc/strplumb\n");
goto out;
}
if ((err = strplumb_load()) < 0) {
goto out;
}
}
if (netboot_over_iscsi() == B_TRUE) {
if ((err = modloadonly("dacf", "net_dacf")) < 0) {
cmn_err(CE_CONT, "Cannot load dacf/net_dacf\n");
goto out;
}
if ((err = modload("misc", "tlimod")) < 0) {
cmn_err(CE_CONT, "Cannot load misc/tlimod\n");
goto out;
}
if ((err = modload("mac", "mac_ether")) < 0) {
cmn_err(CE_CONT, "Cannot load mac/mac_ether\n");
goto out;
}
if ((err = modloadonly("drv", "iscsi")) < 0) {
cmn_err(CE_CONT, "Cannot load drv/iscsi\n");
goto out;
}
if ((err = modloadonly("drv", "ssd")) < 0) {
cmn_err(CE_CONT, "Cannot load drv/ssd\n");
goto out;
}
if ((err = modloadonly("drv", "sd")) < 0) {
cmn_err(CE_CONT, "Cannot load drv/sd\n");
goto out;
}
if ((err = modload("misc", "strplumb")) < 0) {
cmn_err(CE_CONT, "Cannot load misc/strplumb\n");
goto out;
}
if ((err = strplumb_load()) < 0) {
goto out;
}
}
err = clboot_loadrootmodules();
out:
if (err != 0 && (boothowto & RB_ASKNAME))
goto loop;
return (err);
}
static int
get_bootpath_prop(char *bootpath)
{
if (root_is_ramdisk) {
if (BOP_GETPROP(bootops, "bootarchive", bootpath) == -1)
return (-1);
(void) strlcat(bootpath, ":a", BO_MAXOBJNAME);
} else {
if ((BOP_GETPROP(bootops, "bootpath", bootpath) == -1) ||
strlen(bootpath) == 0) {
if (BOP_GETPROP(bootops,
"boot-path", bootpath) == -1)
return (-1);
}
if (memcmp(bootpath, BP_ISCSI_DISK,
strlen(BP_ISCSI_DISK)) == 0) {
get_iscsi_bootpath_vhci(bootpath);
}
}
return (0);
}
static int
get_fstype_prop(char *fstype)
{
char *prop = (root_is_ramdisk) ? "archive-fstype" : "fstype";
return (BOP_GETPROP(bootops, prop, fstype));
}
static struct vfssw *
getfstype(char *askfor, char *fsname, size_t fsnamelen)
{
struct vfssw *vsw;
static char defaultfs[BO_MAXFSNAME];
int root = 0;
if (strcmp(askfor, "root") == 0) {
(void) get_fstype_prop(defaultfs);
root++;
} else {
(void) strcpy(defaultfs, "swapfs");
}
if (boothowto & RB_ASKNAME) {
for (*fsname = '\0'; *fsname == '\0'; *fsname = '\0') {
printf("%s filesystem type [%s]: ", askfor, defaultfs);
console_gets(fsname, fsnamelen);
if (*fsname == '\0')
(void) strcpy(fsname, defaultfs);
if (root) {
if (strcmp(fsname, "nfs2") == 0)
(void) strcpy(fsname, "nfs");
else if (strcmp(fsname, "nfs") == 0)
(void) strcpy(fsname, "nfsdyn");
}
if ((vsw = vfs_getvfssw(fsname)) != NULL)
return (vsw);
printf("Unknown filesystem type '%s'\n", fsname);
}
} else if (*fsname == '\0') {
fsname = defaultfs;
}
if (*fsname == '\0') {
return (NULL);
}
if (root) {
if (strcmp(fsname, "nfs2") == 0)
(void) strcpy(fsname, "nfs");
else if (strcmp(fsname, "nfs") == 0)
(void) strcpy(fsname, "nfsdyn");
}
return (vfs_getvfssw(fsname));
}
static int
getphysdev(char *askfor, char *name, size_t namelen)
{
static char fmt[] = "Enter physical name of %s device\n[%s]: ";
dev_t dev;
static char defaultpath[BO_MAXOBJNAME];
if (strcmp(askfor, "root") == 0) {
if (get_bootpath_prop(defaultpath) == -1)
boothowto |= RB_ASKNAME | RB_VERBOSE;
} else {
(void) strcpy(defaultpath, rootfs.bo_name);
defaultpath[strlen(defaultpath) - 1] = 'b';
}
retry:
if (boothowto & RB_ASKNAME) {
printf(fmt, askfor, defaultpath);
console_gets(name, namelen);
}
if (*name == '\0')
(void) strcpy(name, defaultpath);
if (strcmp(askfor, "swap") == 0) {
dev = ddi_pathname_to_dev_t(name);
if (dev == (dev_t)-1) {
printf("Not a supported device for swap.\n");
boothowto |= RB_ASKNAME | RB_VERBOSE;
goto retry;
}
if (strncmp(ddi_major_to_name(getmajor(dev)), "fd", 2) == 0) {
printf("Too dangerous to swap on the floppy\n");
if (boothowto & RB_ASKNAME)
goto retry;
return (-1);
}
}
return (0);
}
static int
load_boot_driver(char *drv)
{
char *drvname;
major_t major;
#ifdef sparc
struct devnames *dnp;
ddi_prop_t *propp;
char *module;
char *dir, *mf;
int plen;
int mlen;
#endif
if ((major = ddi_name_to_major(drv)) == DDI_MAJOR_T_NONE) {
cmn_err(CE_CONT, "%s: no major number\n", drv);
return (-1);
}
drvname = ddi_major_to_name(major);
#ifdef DEBUG
if (strcmp(drv, drvname) == 0) {
BMDPRINTF(("load_boot_driver: %s\n", drv));
} else {
BMDPRINTF(("load_boot_driver: %s -> %s\n", drv, drvname));
}
#endif
if (modloadonly("drv", drvname) == -1) {
cmn_err(CE_CONT, "%s: cannot load driver\n", drvname);
return (-1);
}
#ifdef sparc
dnp = &devnamesp[major];
if (dnp->dn_global_prop_ptr && dnp->dn_global_prop_ptr->prop_list &&
((propp = i_ddi_prop_search(DDI_DEV_T_ANY,
"ddi-forceload", DDI_PROP_TYPE_STRING,
&dnp->dn_global_prop_ptr->prop_list)) != NULL)) {
module = (char *)propp->prop_val;
plen = propp->prop_len;
while (plen > 0) {
mlen = strlen(module);
mf = strrchr(module, '/');
if (mf) {
dir = module;
*mf++ = '\0';
} else {
dir = "misc";
mf = module;
}
if (modloadonly(dir, mf) == -1)
cmn_err(CE_CONT,
"misc/%s: can't load module\n", mf);
if (mf != module)
*(mf - 1) = '/';
module += mlen + 1;
plen -= mlen + 1;
}
}
#endif
return (0);
}
static int
load_parent_drivers(dev_info_t *dip, char *path)
{
int rval = 0;
major_t major = DDI_MAJOR_T_NONE;
char *drv;
char *p;
while (dip) {
if (path)
major = ddi_name_to_major(path);
else
major = DDI_MAJOR_T_NONE;
if (major != DDI_MAJOR_T_NONE)
drv = ddi_major_to_name(major);
else
drv = ddi_binding_name(dip);
if (load_boot_driver(drv) != 0)
rval = -1;
dip = ddi_get_parent(dip);
if (path) {
p = strrchr(path, '/');
if (p)
*p = 0;
}
}
return (rval);
}
static int
load_bootpath_drivers(char *bootpath)
{
dev_info_t *dip;
char *pathcopy;
int pathcopy_len;
int rval;
char *p;
int proplen;
char iscsi_network_path[BO_MAXOBJNAME];
if (bootpath == NULL || *bootpath == 0)
return (-1);
BMDPRINTF(("load_bootpath_drivers: %s\n", bootpath));
#ifdef _OBP
if (netboot_over_iscsi()) {
if (root_is_ramdisk) {
if (modloadonly("drv", "ramdisk") < 0)
return (-1);
}
proplen = BOP_GETPROPLEN(bootops, BP_ISCSI_NETWORK_BOOTPATH);
if (proplen > 0) {
if (BOP_GETPROP(bootops, BP_ISCSI_NETWORK_BOOTPATH,
iscsi_network_path) > 0) {
p = strchr(iscsi_network_path, ':');
if (p != NULL) {
*p = '\0';
}
pathcopy = i_ddi_strdup(iscsi_network_path,
KM_SLEEP);
pathcopy_len = strlen(pathcopy) + 1;
} else {
return (-1);
}
} else {
return (-1);
}
} else {
#endif
pathcopy = i_ddi_strdup(bootpath, KM_SLEEP);
pathcopy_len = strlen(pathcopy) + 1;
#ifdef _OBP
}
#endif
dip = path_to_devinfo(pathcopy);
#if defined(__x86)
if (dip == NULL) {
char *leaf;
p = strrchr(pathcopy, '/');
*p++ = 0;
leaf = p;
p = strchr(leaf, '@');
*p = 0;
BMDPRINTF(("load_bootpath_drivers: parent=%s leaf=%s\n",
bootpath, leaf));
dip = path_to_devinfo(pathcopy);
if (leaf) {
rval = load_boot_driver(leaf, NULL);
if (rval == -1) {
kmem_free(pathcopy, pathcopy_len);
return (0);
}
}
}
#endif
if (dip == NULL) {
cmn_err(CE_WARN, "can't bind driver for boot path <%s>",
bootpath);
kmem_free(pathcopy, pathcopy_len);
return (0);
}
if (netboot_over_ib(bootpath) &&
modloadonly("drv", "ibp") == -1) {
cmn_err(CE_CONT, "ibp: cannot load platform driver\n");
kmem_free(pathcopy, pathcopy_len);
return (0);
}
if (strstr(bootpath, "/hub@") && modloadonly("drv", "hubd") == -1) {
cmn_err(CE_WARN, "bootpath contains a USB hub, "
"but cannot load hubd driver");
}
p = strrchr(pathcopy, '/');
if (p) {
p = strchr(p, ':');
if (p)
*p = 0;
}
rval = load_parent_drivers(dip, pathcopy);
kmem_free(pathcopy, pathcopy_len);
return (rval);
}
static int
load_boot_platform_modules(char *drv)
{
major_t major;
dev_info_t *dip;
char *drvname;
int rval = 0;
if ((major = ddi_name_to_major(drv)) == DDI_MAJOR_T_NONE) {
cmn_err(CE_CONT, "%s: no major number\n", drv);
return (-1);
}
drvname = ddi_major_to_name(major);
if ((major = ddi_name_to_major(drvname)) == DDI_MAJOR_T_NONE)
return (-1);
#ifdef DEBUG
if (strcmp(drv, drvname) == 0) {
BMDPRINTF(("load_boot_platform_modules: %s\n", drv));
} else {
BMDPRINTF(("load_boot_platform_modules: %s -> %s\n",
drv, drvname));
}
#endif
dip = devnamesp[major].dn_head;
if (dip == NULL) {
if (modloadonly("drv", drvname) == -1) {
cmn_err(CE_CONT, "%s: cannot load platform driver\n",
drvname);
rval = -1;
}
} else {
while (dip) {
if (load_parent_drivers(dip, NULL) != 0)
rval = -1;
dip = ddi_get_next(dip);
}
}
return (rval);
}
struct i_path_findnode {
pnode_t nodeid;
dev_info_t *dip;
};
static int
i_path_find_node(dev_info_t *dev, void *arg)
{
struct i_path_findnode *f = (struct i_path_findnode *)arg;
if (ddi_get_nodeid(dev) == (int)f->nodeid) {
f->dip = dev;
return (DDI_WALK_TERMINATE);
}
return (DDI_WALK_CONTINUE);
}
static dev_info_t *
path_to_devinfo(char *path)
{
struct i_path_findnode fn;
extern dev_info_t *top_devinfo;
fn.dip = NULL;
fn.nodeid = prom_finddevice(path);
if (fn.nodeid != OBP_BADNODE) {
ddi_walk_devs(top_devinfo, i_path_find_node, (void *)(&fn));
}
#ifdef DEBUG
if (fn.dip) {
char *p, *q;
p = ddi_binding_name(fn.dip);
q = ddi_node_name(fn.dip);
if (p && q && (strcmp(p, q) != 0)) {
BMDPRINTF(("path_to_devinfo: %s bound to %s\n",
path, p));
}
}
#endif
return (fn.dip);
}
static boolean_t
netboot_over_ib(char *bootpath)
{
char *temp;
boolean_t ret = B_FALSE;
pnode_t node = prom_finddevice(bootpath);
int len;
char devicetype[OBP_MAXDRVNAME];
if (node == OBP_BADNODE || node == OBP_NONODE) {
return (B_FALSE);
}
len = prom_getproplen(node, OBP_DEVICETYPE);
if (len <= 1 || len >= OBP_MAXDRVNAME)
return (B_FALSE);
(void) prom_getprop(node, OBP_DEVICETYPE, (caddr_t)devicetype);
if (strncmp("ib", devicetype, 2) == 0) {
if ((temp = strstr(bootpath, ":port=")) != NULL) {
if ((temp = strstr(temp, ",pkey=")) != NULL)
if ((temp = strstr(temp,
",protocol=ip")) != NULL) {
ret = B_TRUE;
}
}
}
return (ret);
}
static boolean_t
netboot_over_iscsi(void)
{
int proplen;
boolean_t ret = B_FALSE;
char bootpath[OBP_MAXPATHLEN];
proplen = BOP_GETPROPLEN(bootops, BP_BOOTPATH);
if (proplen > 0) {
if (BOP_GETPROP(bootops, BP_BOOTPATH, bootpath) > 0) {
if (memcmp(bootpath, BP_ISCSI_DISK,
strlen(BP_ISCSI_DISK)) == 0) {
ret = B_TRUE;
}
}
}
return (ret);
}