/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* ONC_PLUS EXTRACT START */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* ONC_PLUS EXTRACT END */ /* * Configure root, swap and dump devices. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Local routines */ 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); /* * Module linkage information for the kernel. */ static struct modlmisc modlmisc = { &mod_miscops, "root and swap configuration %I%" }; 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)); } /* * Configure root file system. */ int rootconf(void) { int error; struct vfssw *vsw; extern void pm_init(void); 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)); /* * Install cluster modules that were only loaded during * loadrootmodules(). */ if (error = clboot_rootconf()) return (error); if (root_is_svm) { (void) strncpy(rootfs.bo_name, obp_bootpath, BO_MAXOBJNAME); BMDPRINTF(("rootconf: svm: rootfs name %s\n", rootfs.bo_name)); BMDPRINTF(("rootconf: svm: svm name %s\n", svm_bootpath)); } /* * Run _init on the root filesystem (we already loaded it * but we've been waiting until now to _init it) which will * have the side-effect of running vsw_init() on this vfs. * Because all the nfs filesystems are lumped into one * module we need to special case it. */ 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(); VFS_INIT(rootvfs, &vsw->vsw_vfsops, (caddr_t)0); VFS_HOLD(rootvfs); if (root_is_svm) { rootvfs->vfs_flag |= VFS_RDONLY; } /* * This pm-releated call has to occur before root is mounted since we * need to power up all devices. It is placed after VFS_INIT() such * that opening a device via ddi_lyr_ interface just before root has * been mounted would work. */ pm_init(); if (netboot) { if ((error = strplumb()) != 0) { cmn_err(CE_CONT, "Cannot plumb network device\n"); return (error); } } /* * ufs_mountroot() ends up calling getrootdev() * (below) which actually triggers the _init, identify, * probe and attach of the drivers that make up root device * bush; these are also quietly waiting in memory. */ 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); } /* * Remount root on an SVM mirror root device * Only supported on UFS filesystems at present */ int svm_rootconf(void) { int error; extern int ufs_remountroot(struct vfs *vfsp); ASSERT(root_is_svm == 1); if (strcmp(rootfs.bo_fstype, "ufs") != 0) { cmn_err(CE_CONT, "Mounting root on %s with filesystem " "type %s is not supported\n", rootfs.bo_name, rootfs.bo_fstype); return (EINVAL); } (void) strncpy(rootfs.bo_name, svm_bootpath, BO_MAXOBJNAME); BMDPRINTF(("svm_rootconf: rootfs %s\n", rootfs.bo_name)); error = ufs_remountroot(rootvfs); if (error) { cmn_err(CE_CONT, "Cannot remount root on %s fstype %s\n", rootfs.bo_name, rootfs.bo_fstype); } else { cmn_err(CE_CONT, "?root remounted on %s fstype %s\n", rootfs.bo_name, rootfs.bo_fstype); } return (error); } /* * Under the assumption that our root file system is on a * disk partition, get the dev_t of the partition in question. * * By now, boot has faithfully loaded all our modules into memory, and * we've taken over resource management. Before we go any further, we * have to fire up the device drivers and stuff we need to mount the * root filesystem. That's what we do here. Fingers crossed. */ dev_t getrootdev(void) { dev_t d; if ((d = ddi_pathname_to_dev_t(rootfs.bo_name)) == NODEV) cmn_err(CE_CONT, "Cannot assemble drivers for root %s\n", rootfs.bo_name); return (d); } /* * If booted with ASKNAME, prompt on the console for a filesystem * name and return it. */ void getfsname(char *askfor, char *name, size_t namelen) { if (boothowto & RB_ASKNAME) { printf("%s name: ", askfor); console_gets(name, namelen); } } /*ARGSUSED1*/ 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); } /* ONC_PLUS EXTRACT START */ /* * We want to load all the modules needed to mount the root filesystem, * so that when we start the ball rolling in 'getrootdev', every module * should already be in memory, just waiting to be init-ed. */ int loadrootmodules(void) { struct vfssw *vsw; char *this; char *name; int err; /* ONC_PLUS EXTRACT END */ int i, proplen, dhcacklen; extern char *impl_module_list[]; extern char *platform_module_list[]; /* Make sure that the PROM's devinfo tree has been created */ 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)); /* * zzz We need to honor what's in rootfs if it's not null. * non-null means use what's there. This way we can * change rootfs with /etc/system AND with tunetool. */ if (root_is_svm) { /* user replaced rootdev, record obp_bootpath */ obp_bootpath[0] = '\0'; (void) getphysdev("root", obp_bootpath, BO_MAXOBJNAME); BMDPRINTF(("loadrootmodules: obp_bootpath %s\n", obp_bootpath)); } else { /* * Get the root fstype and root device path from boot. */ rootfs.bo_fstype[0] = '\0'; rootfs.bo_name[0] = '\0'; } /* * This lookup will result in modloadonly-ing the root * filesystem module - it gets _init-ed in rootconf() */ if ((vsw = getfstype("root", rootfs.bo_fstype, BO_MAXFSNAME)) == NULL) return (ENXIO); /* in case we have no file system types */ (void) strcpy(rootfs.bo_fstype, vsw->vsw_name); vfs_unrefvfssw(vsw); /* * Load the favored drivers of the implementation. * e.g. 'sbus' and possibly 'zs' (even). * * Called whilst boot is still loaded (because boot does * the i/o for us), and DDI services are unavailable. */ 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); /* NOTREACHED */ } } /* * Now load the platform modules (if any) */ 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); /* NOTREACHED */ } } loop: (void) getphysdev("root", rootfs.bo_name, BO_MAXOBJNAME); /* * Given a physical pathname, load the correct set of driver * modules into memory, including all possible parents. * * NB: The code sets the variable 'name' for error reporting. */ err = 0; BMDPRINTF(("loadrootmodules: rootfs %s\n", rootfs.bo_name)); if (root_is_svm == 0) { BMDPRINTF(("loadrootmodules: rootfs %s\n", rootfs.bo_name)); name = rootfs.bo_name; err = load_bootpath_drivers(rootfs.bo_name); } /* * Load driver modules in obp_bootpath, this is always * required for mountroot to succeed. obp_bootpath is * is set if rootdev is set via /etc/system, which is * the case if booting of a SVM/VxVM mirror. */ 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; /* NOTREACHED */ } /* * Check to see if the booter performed DHCP configuration * ("bootp-response" boot property exists). If so, then before * bootops disappears we need to save the value of this property * such that the userland dhcpagent can adopt the DHCP management * of our primary network interface. We leave room at the beginning of * saved property to cache the interface name we used to boot the * client. This context is necessary for the user land dhcpagent * to do its job properly on a multi-homed system. */ proplen = BOP_GETPROPLEN(bootops, "bootp-response"); if (proplen > 0) { dhcacklen = proplen + IFNAMSIZ; dhcack = kmem_zalloc(dhcacklen, KM_SLEEP); if (BOP_GETPROP(bootops, "bootp-response", (uchar_t *)&dhcack[IFNAMSIZ]) == -1) { cmn_err(CE_WARN, "BOP_GETPROP of " "\"bootp-response\" failed\n"); kmem_free(dhcack, dhcacklen); dhcack = NULL; goto out; } /* * Fetch the "netdev-path" boot property (if it exists), and * stash it for later use by sysinfo(SI_DHCP_CACHE, ...). */ 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; } } } /* * Preload (load-only, no init) all modules which * were added to the /etc/system file with the * FORCELOAD keyword. */ BMDPRINTF(("loadrootmodules: preload_module\n")); (void) mod_sysctl_type(MOD_FORCELOAD, preload_module, NULL); /* ONC_PLUS EXTRACT START */ /* * If we booted otw then load in the plumbing * routine now while we still can. If we didn't * boot otw then we will load strplumb in main(). * * NFS is actually a set of modules, the core routines, * a diskless helper module, rpcmod, and the tli interface. Load * them now while we still can. * * Because we glomb all versions of nfs into a single module * we check based on the initial string "nfs". * * XXX: A better test for this is to see if device_type * XXX: from the PROM is "network". */ if (strncmp(rootfs.bo_fstype, "nfs", 3) == 0) { ++netboot; if ((err = modload("misc", "tlimod")) < 0) { cmn_err(CE_CONT, "Cannot load misc/tlimod\n"); goto out; /* NOTREACHED */ } if ((err = modload("strmod", "rpcmod")) < 0) { cmn_err(CE_CONT, "Cannot load strmod/rpcmod\n"); goto out; /* NOTREACHED */ } if ((err = modload("misc", "nfs_dlboot")) < 0) { cmn_err(CE_CONT, "Cannot load misc/nfs_dlboot\n"); goto out; /* NOTREACHED */ } if ((err = modload("misc", "strplumb")) < 0) { cmn_err(CE_CONT, "Cannot load misc/strplumb\n"); goto out; /* NOTREACHED */ } if ((err = strplumb_load()) < 0) { goto out; /* NOTREACHED */ } } /* * Preload modules needed for booting as a cluster. */ err = clboot_loadrootmodules(); out: if (err != 0 && (boothowto & RB_ASKNAME)) goto loop; return (err); } /* ONC_PLUS EXTRACT END */ /* * Get the name of the root or swap filesystem type, and return * the corresponding entry in the vfs switch. * * If we're not asking the user, and we're trying to find the * root filesystem type, we ask boot for the filesystem * type that it came from and use that. Similarly, if we're * trying to find the swap filesystem, we try and derive it from * the root filesystem type. * * If we are booting via NFS we currently have these options: * nfs - dynamically choose NFS V2. V3, or V4 (default) * nfs2 - force NFS V2 * nfs3 - force NFS V3 * nfs4 - force NFS V4 * Because we need to maintain backward compatibility with the naming * convention that the NFS V2 filesystem name is "nfs" (see vfs_conf.c) * we need to map "nfs" => "nfsdyn" and "nfs2" => "nfs". The dynamic * nfs module will map the type back to either "nfs", "nfs3", or "nfs4". * This is only for root filesystems, all other uses such as cachefs * will expect that "nfs" == NFS V2. * * If the filesystem isn't already loaded, vfs_getvfssw() will load * it for us, but if (at the time we call it) modrootloaded is * still not set, it won't run the filesystems _init routine (and * implicitly it won't run the filesystems vsw_init() entry either). * We do that explicitly in rootconf(). */ 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) BOP_GETPROP(bootops, "fstype", 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)); } /* * Get a physical device name, and maybe load and attach * the driver. * * XXX Need better checking of whether or not a device * actually exists if the user typed in a pathname. * * XXX Are we sure we want to expose users to this sort * of physical namespace gobbledygook (now there's * a word to conjure with..) * * XXX Note that on an OBP machine, we can easily ask the * prom and pretty-print some plausible set of bootable * devices. We can also user the prom to verify any * such device. Later tim.. later. */ 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]; /* * Establish 'default' values - we get the root device from * boot, and we infer the swap device is the same but with * a 'b' on the end instead of an 'a'. A first stab at * ease-of-use .. */ if (strcmp(askfor, "root") == 0) { /* * Look for the 1275 compliant name 'bootpath' first, * but make certain it has a non-NULL value as well. */ if ((BOP_GETPROP(bootops, "bootpath", defaultpath) == -1) || strlen(defaultpath) == 0) { if (BOP_GETPROP(bootops, "boot-path", 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) { /* * Try to load and install the swap device driver. */ 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; } /* * Ensure that we're not trying to swap on the floppy. */ 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); } /* * Load a driver needed to boot. */ static int load_boot_driver(char *drv) { char *drvname; major_t major; if ((major = ddi_name_to_major(drv)) == (major_t)-1) { cmn_err(CE_CONT, "%s: no major number\n", drv); return (-1); } /* * resolve aliases */ 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 /* DEBUG */ if (modloadonly("drv", drvname) == -1) { cmn_err(CE_CONT, "%s: cannot load driver\n", drvname); return (-1); } return (0); } /* * For a given instance, load that driver and its parents */ static int load_parent_drivers(dev_info_t *dip) { int rval = 0; char *drv; while (dip) { drv = ddi_get_name(dip); if (load_boot_driver(drv) != 0) rval = -1; dip = ddi_get_parent(dip); } return (rval); } /* * For a given path to a boot device, * load that driver and all its parents. */ static int load_bootpath_drivers(char *bootpath) { dev_info_t *dip; if (bootpath == NULL || *bootpath == 0) return (-1); BMDPRINTF(("load_bootpath_drivers: %s\n", bootpath)); dip = path_to_devinfo(bootpath); #if defined(__i386) || defined(__amd64) /* * i386 does not provide stub nodes for all boot devices, * but we should be able to find the node for the parent, * and the leaf of the boot path should be the driver name, * which we go ahead and load here. */ if (dip == NULL) { char *pathcopy, *leaf, *p; int len, rval; len = strlen(bootpath) + 1; pathcopy = kmem_zalloc(len, KM_SLEEP); (void) strcpy(pathcopy, bootpath); /* * Work backward to the last slash to build the * full path of the parent of the boot device */ for (p = pathcopy + len - 2; *p != '/'; p--) ; *p++ = 0; /* * Now isolate the driver name of the leaf device */ for (leaf = p; *p && *p != '@'; p++) ; *p = 0; BMDPRINTF(("load_bootpath_drivers: parent=%s leaf=%s\n", pathcopy, leaf)); dip = path_to_devinfo(pathcopy); rval = load_boot_driver(leaf); kmem_free(pathcopy, len); if (rval == -1) return (NULL); } #endif if (dip == NULL) { cmn_err(CE_WARN, "can't bind driver for boot path <%s>", bootpath); return (NULL); } /* * Load IP over IB driver when netbooting over IB. * As per IB 1275 binding, IP over IB is represented as * service on the top of the HCA node. So, there is no * PROM node and generic framework cannot pre-load * IP over IB driver based on the bootpath. The following * code preloads IP over IB driver when doing netboot over * InfiniBand. */ if (netboot_over_ib(bootpath) && modloadonly("drv", "ibd") == -1) { cmn_err(CE_CONT, "ibd: cannot load platform driver\n"); return (NULL); } return (load_parent_drivers(dip)); } /* * Load drivers required for a platform * Since all hardware nodes should be available in the device * tree, walk the per-driver list and load the parents of * each node found. If not a hardware node, try to load it. * Pseudo nexus is already loaded. */ 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)) == (major_t)-1) { cmn_err(CE_CONT, "%s: no major number\n", drv); return (-1); } /* * resolve aliases */ drvname = ddi_major_to_name(major); if ((major = ddi_name_to_major(drvname)) == (major_t)-1) 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 /* DEBUG */ dip = devnamesp[major].dn_head; if (dip == NULL) { /* pseudo node, not-enumerated, needs to be loaded */ 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) != 0) rval = -1; dip = ddi_get_next(dip); } } return (rval); } /* * i_find_node: Internal routine used by path_to_devinfo * to locate a given nodeid in the device tree. */ struct i_path_findnode { dnode_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); } /* * Return the devinfo node to a boot device */ static dev_info_t * path_to_devinfo(char *path) { struct i_path_findnode fn; extern dev_info_t *top_devinfo; /* * Get the nodeid of the given pathname, if such a mapping exists. */ fn.dip = NULL; fn.nodeid = prom_finddevice(path); if (fn.nodeid != OBP_BADNODE) { /* * Find the nodeid in our copy of the device tree and return * whatever name we used to bind this node to a driver. */ ddi_walk_devs(top_devinfo, i_path_find_node, (void *)(&fn)); } #ifdef DEBUG /* * If we're bound to something other than the nodename, * note that in the message buffer and system log. */ 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 /* DEBUG */ return (fn.dip); } /* * This routine returns B_TRUE if the bootpath corresponds to * IP over IB driver. * * The format of the bootpath for the IP over IB looks like * /pci@1f,700000/pci@1/ib@0:port=1,pkey=8001,protocol=ip * * The minor node portion "port=1,pkey=8001,protocol=ip" represents * IP over IB driver. */ static boolean_t netboot_over_ib(char *bootpath) { char *temp; boolean_t ret = B_FALSE; dnode_t node = prom_finddevice(bootpath); int len; char devicetype[OBP_MAXDRVNAME]; /* Is this IB node ? */ 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) { /* Check for proper IP over IB string */ if ((temp = strstr(bootpath, ":port=")) != NULL) { if ((temp = strstr(temp, ",pkey=")) != NULL) if ((temp = strstr(temp, ",protocol=ip")) != NULL) { ret = B_TRUE; } } } return (ret); }