Beispiel #1
0
/*
 * Initialize memory controller's data structure and status.
 */
static int
fipe_mc_init(dev_info_t *dip)
{
	ddi_acc_handle_t handle;

	bzero(&fipe_mc_ctrl, sizeof (fipe_mc_ctrl));

	/* Hold one reference count and will be released in fipe_mc_fini. */
	ndi_hold_devi(dip);

	/* Setup pci configuration handler. */
	if (pci_config_setup(dip, &handle) != DDI_SUCCESS) {
		cmn_err(CE_WARN,
		    "!fipe: failed to setup pcicfg handler in mc_init.");
		ndi_rele_devi(dip);
		return (-1);
	}

	/* Save original configuration. */
	fipe_mc_ctrl.mc_thrtctrl = pci_config_get8(handle, FIPE_MC_THRTCTRL);
	fipe_mc_ctrl.mc_thrtlow = pci_config_get8(handle, FIPE_MC_THRTLOW);
	fipe_mc_ctrl.mc_gblact = pci_config_get8(handle, FIPE_MC_GBLACT);
	fipe_mc_ctrl.mc_dip = dip;
	fipe_mc_ctrl.mc_pci_hdl = handle;
	fipe_mc_ctrl.mc_initialized = B_TRUE;

	return (0);
}
/*
 * Starting from the root node suspend all devices in the device tree.
 * Assumes that all devices have already been marked busy.
 */
static int
sbdp_suspend_devices_(dev_info_t *dip, sbdp_sr_handle_t *srh)
{
	major_t	major;
	char	*dname;

	for (; dip != NULL; dip = ddi_get_next_sibling(dip)) {
		char	d_name[40], d_alias[40], *d_info;

		if (sbdp_suspend_devices_(ddi_get_child(dip), srh)) {
			return (ENXIO);
		}

		if (!sbdp_is_real_device(dip))
			continue;

		major = (major_t)-1;
		if ((dname = DEVI(dip)->devi_binding_name) != NULL)
			major = ddi_name_to_major(dname);

#ifdef DEBUG
		if (sbdp_bypass_device(dname)) {
			SBDP_DBG_QR("bypassed suspend of %s (major# %d)\n",
			    dname, major);
			continue;
		}
#endif

		if ((d_info = ddi_get_name_addr(dip)) == NULL)
			d_info = "<null>";

		d_name[0] = 0;
		if (sbdp_resolve_devname(dip, d_name, d_alias) == 0) {
			if (d_alias[0] != 0) {
				SBDP_DBG_QR("\tsuspending %s@%s (aka %s)\n",
					d_name, d_info, d_alias);
			} else {
				SBDP_DBG_QR("\tsuspending %s@%s\n",
					d_name, d_info);
			}
		} else {
			SBDP_DBG_QR("\tsuspending %s@%s\n", dname, d_info);
		}

		if (devi_detach(dip, DDI_SUSPEND) != DDI_SUCCESS) {
			(void) sprintf(sbdp_get_err_buf(&srh->sep),
			    "%d", major);

			sbdp_set_err(&srh->sep, ESGT_SUSPEND, NULL);
			ndi_hold_devi(dip);
			SR_FAILED_DIP(srh) = dip;
			return (DDI_FAILURE);
		}
	}

	return (DDI_SUCCESS);
}
Beispiel #3
0
/*ARGSUSED*/
static int
fipe_search_ioat_dev(dev_info_t *dip, void *arg)
{
	char *unit;
	struct fipe_pci_ioat_id *id;
	int i, max, venid, devid, subvenid, subsysid;

	/* Query PCI id properties. */
	venid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
	    "vendor-id", 0xffffffff);
	if (venid == 0xffffffff) {
		return (DDI_WALK_CONTINUE);
	}
	devid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
	    "device-id", 0xffffffff);
	if (devid == 0xffffffff) {
		return (DDI_WALK_CONTINUE);
	}
	subvenid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
	    "subsystem-vendor-id", 0xffffffff);
	if (subvenid == 0xffffffff) {
		return (DDI_WALK_CONTINUE);
	}
	subsysid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
	    "subsystem-id", 0xffffffff);
	if (subvenid == 0xffffffff) {
		return (DDI_WALK_CONTINUE);
	}
	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
	    "unit-address", &unit) != DDI_PROP_SUCCESS) {
		return (DDI_WALK_CONTINUE);
	}

	max = sizeof (fipe_pci_ioat_ids) / sizeof (fipe_pci_ioat_ids[0]);
	for (i = 0; i < max; i++) {
		id = &fipe_pci_ioat_ids[i];
		if ((id->venid == 0xffffu || id->venid == venid) &&
		    (id->devid == 0xffffu || id->devid == devid) &&
		    (id->subvenid == 0xffffu || id->subvenid == subvenid) &&
		    (id->subsysid == 0xffffu || id->subsysid == subsysid) &&
		    (id->unitaddr == NULL || strcmp(id->unitaddr, unit) == 0)) {
			break;
		}
	}
	ddi_prop_free(unit);
	if (i >= max) {
		return (DDI_WALK_CONTINUE);
	}

	/* Found IOAT device, hold one reference count. */
	ndi_hold_devi(dip);
	fipe_ioat_ctrl.ioat_dev_info = dip;

	return (DDI_WALK_TERMINATE);
}
/*
 * Get the parent dip.
 */
static dev_info_t *
get_parent(dev_info_t *dip, struct parinfo *info)
{
	dev_info_t *pdip;

	pdip = ddi_get_parent(dip);
	ndi_hold_devi(pdip);
	info->dip = dip;
	info->pdip = pdip;
	return (pdip);
}
Beispiel #5
0
static int
acpidev_cpu_create_dip(cpu_t *cp, dev_info_t **dipp)
{
	if (acpidev_cpu_lookup_dip(cp, dipp) == PSM_SUCCESS) {
		ndi_hold_devi(*dipp);
		return (PSM_SUCCESS);
	}
	if (psm_cpu_create_devinfo_old != NULL) {
		return (psm_cpu_create_devinfo_old(cp, dipp));
	} else {
		return (PSM_FAILURE);
	}
}
Beispiel #6
0
static void
create_devinfo_tree(void)
{
	major_t major;
	pnode_t nodeid;

	i_ddi_node_cache_init();
#if defined(__sparc)
	nodeid = prom_nextnode(0);
#else /* x86 */
	nodeid = DEVI_SID_NODEID;
#endif
	top_devinfo = i_ddi_alloc_node(NULL, rootname,
	    nodeid, -1, NULL, KM_SLEEP);
	ndi_hold_devi(top_devinfo);	/* never release the root */

	i_ddi_add_devimap(top_devinfo);

	/*
	 * Bind root node.
	 * This code is special because root node has no parent
	 */
	major = ddi_name_to_major("rootnex");
	ASSERT(major != DDI_MAJOR_T_NONE);
	DEVI(top_devinfo)->devi_major = major;
	devnamesp[major].dn_head = top_devinfo;
	i_ddi_set_binding_name(top_devinfo, rootname);
	i_ddi_set_node_state(top_devinfo, DS_BOUND);

	/*
	 * Record that devinfos have been made for "rootnex."
	 * di_dfs() is used to read the prom because it doesn't get the
	 * next sibling until the function returns, unlike ddi_walk_devs().
	 */
	di_dfs(ddi_root_node(), get_neighbors, 0);

#if !defined(__sparc)
	/*
	 * On x86, there is no prom. Create device tree by
	 * probing pci config space
	 */
	{
		extern void impl_setup_ddi(void);
		impl_setup_ddi();
	}
#endif /* x86 */
}
Beispiel #7
0
dev_info_t *
iommulib_iommu_getdip(iommulib_handle_t handle)
{
	iommulib_unit_t *unitp;
	dev_info_t *dip;

	unitp = (iommulib_unit_t *)handle;

	ASSERT(unitp);

	mutex_enter(&unitp->ilu_lock);
	dip = unitp->ilu_dip;
	ASSERT(dip);
	ndi_hold_devi(dip);
	mutex_exit(&unitp->ilu_lock);

	return (dip);
}
/*
 * check for a possible substitute node.  This routine searches the
 * children of parent_dip, looking for a node that:
 *	1. is a prom node
 *	2. binds to the same major number
 *	3. there is no need to verify that the unit-address information
 *		match since it is likely that the substitute node
 *		will have none (e.g. disk) - this would be the reason the
 *		framework rejected it in the first place.
 *
 * assumes parent_dip is held
 */
static dev_info_t *
find_alternate_node(dev_info_t *parent_dip, major_t major)
{
	int circ;
	dev_info_t *child_dip;

	/* lock down parent to keep children from being removed */
	ndi_devi_enter(parent_dip, &circ);
	for (child_dip = ddi_get_child(parent_dip); child_dip != NULL;
	    child_dip = ddi_get_next_sibling(child_dip)) {

		/* look for obp node with matching major */
		if ((ndi_dev_is_prom_node(child_dip) != 0) &&
		    (ddi_driver_major(child_dip) == major)) {
			ndi_hold_devi(child_dip);
			break;
		}
	}
	ndi_devi_exit(parent_dip, circ);
	return (child_dip);
}
Beispiel #9
0
int
iommulib_iommu_register(dev_info_t *dip, iommulib_ops_t *ops,
    iommulib_handle_t *handle)
{
	const char *vendor;
	iommulib_unit_t *unitp;
	int instance = ddi_get_instance(dip);
	const char *driver = ddi_driver_name(dip);
	const char *f = "iommulib_register";

	ASSERT(ops);
	ASSERT(handle);

	if (ops->ilops_vers != IOMMU_OPS_VERSION) {
		cmn_err(CE_WARN, "%s: %s%d: Invalid IOMMULIB ops version "
		    "in ops vector (%p). Failing registration", f, driver,
		    instance, (void *)ops);
		return (DDI_FAILURE);
	}

	switch (ops->ilops_vendor) {
	case AMD_IOMMU:
		vendor = "AMD";
		break;
	case INTEL_IOMMU:
		vendor = "Intel";
		break;
	case INVALID_VENDOR:
		cmn_err(CE_WARN, "%s: %s%d: vendor field (%x) not initialized. "
		    "Failing registration for ops vector: %p", f,
		    driver, instance, ops->ilops_vendor, (void *)ops);
		return (DDI_FAILURE);
	default:
		cmn_err(CE_WARN, "%s: %s%d: Invalid vendor field (%x). "
		    "Failing registration for ops vector: %p", f,
		    driver, instance, ops->ilops_vendor, (void *)ops);
		return (DDI_FAILURE);
	}

	cmn_err(CE_NOTE, "!%s: %s%d: Detected IOMMU registration from vendor"
	    " %s", f, driver, instance, vendor);

	if (ops->ilops_data == NULL) {
		cmn_err(CE_WARN, "%s: %s%d: NULL IOMMU data field. "
		    "Failing registration for ops vector: %p", f,
		    driver, instance, (void *)ops);
		return (DDI_FAILURE);
	}

	if (ops->ilops_id == NULL) {
		cmn_err(CE_WARN, "%s: %s%d: NULL ID field. "
		    "Failing registration for ops vector: %p", f,
		    driver, instance, (void *)ops);
		return (DDI_FAILURE);
	}

	if (ops->ilops_probe == NULL) {
		cmn_err(CE_WARN, "%s: %s%d: NULL probe op. "
		    "Failing registration for ops vector: %p", f,
		    driver, instance, (void *)ops);
		return (DDI_FAILURE);
	}

	if (ops->ilops_dma_allochdl == NULL) {
		cmn_err(CE_WARN, "%s: %s%d: NULL dma_allochdl op. "
		    "Failing registration for ops vector: %p", f,
		    driver, instance, (void *)ops);
		return (DDI_FAILURE);
	}

	if (ops->ilops_dma_freehdl == NULL) {
		cmn_err(CE_WARN, "%s: %s%d: NULL dma_freehdl op. "
		    "Failing registration for ops vector: %p", f,
		    driver, instance, (void *)ops);
		return (DDI_FAILURE);
	}

	if (ops->ilops_dma_bindhdl == NULL) {
		cmn_err(CE_WARN, "%s: %s%d: NULL dma_bindhdl op. "
		    "Failing registration for ops vector: %p", f,
		    driver, instance, (void *)ops);
		return (DDI_FAILURE);
	}

	if (ops->ilops_dma_sync == NULL) {
		cmn_err(CE_WARN, "%s: %s%d: NULL dma_sync op. "
		    "Failing registration for ops vector: %p", f,
		    driver, instance, (void *)ops);
		return (DDI_FAILURE);
	}

	if (ops->ilops_dma_win == NULL) {
		cmn_err(CE_WARN, "%s: %s%d: NULL dma_win op. "
		    "Failing registration for ops vector: %p", f,
		    driver, instance, (void *)ops);
		return (DDI_FAILURE);
	}

	unitp = kmem_zalloc(sizeof (iommulib_unit_t), KM_SLEEP);
	mutex_enter(&iommulib_lock);
	if (iommulib_fini == 1) {
		mutex_exit(&iommulib_lock);
		cmn_err(CE_WARN, "%s: IOMMULIB unloading. Failing register.",
		    f);
		kmem_free(unitp, sizeof (iommulib_unit_t));
		return (DDI_FAILURE);
	}

	/*
	 * fini/register race conditions have been handled. Now create the
	 * IOMMU unit
	 */
	mutex_init(&unitp->ilu_lock, NULL, MUTEX_DEFAULT, NULL);

	mutex_enter(&unitp->ilu_lock);
	unitp->ilu_unitid = ++iommulib_unit_ids;
	unitp->ilu_ref = 0;
	ndi_hold_devi(dip);
	unitp->ilu_dip = dip;
	unitp->ilu_ops = ops;
	unitp->ilu_data = ops->ilops_data;

	unitp->ilu_next = iommulib_list;
	iommulib_list = unitp;
	unitp->ilu_prev = NULL;
	if (unitp->ilu_next)
		unitp->ilu_next->ilu_prev = unitp;

	/*
	 * The IOMMU device itself is not controlled by an IOMMU.
	 */
	DEVI(dip)->devi_iommulib_handle = IOMMU_HANDLE_UNUSED;

	mutex_exit(&unitp->ilu_lock);

	iommulib_num_units++;

	*handle = unitp;

	mutex_exit(&iommulib_lock);

	cmn_err(CE_NOTE, "!%s: %s%d: Succesfully registered IOMMU unit "
	    "from vendor=%s, ops=%p, data=%p, IOMMULIB unitid=%u",
	    f, driver, instance, vendor, (void *)ops, (void *)unitp->ilu_data,
	    unitp->ilu_unitid);

	return (DDI_SUCCESS);
}
Beispiel #10
0
int
iommulib_nexus_register(dev_info_t *dip, iommulib_nexops_t *nexops,
    iommulib_nexhandle_t *handle)
{
	iommulib_nex_t *nexp;
	int instance = ddi_get_instance(dip);
	const char *driver = ddi_driver_name(dip);
	dev_info_t *pdip = ddi_get_parent(dip);
	const char *f = "iommulib_nexus_register";

	ASSERT(nexops);
	ASSERT(handle);

	*handle = NULL;

	/*
	 * Root node is never busy held
	 */
	if (dip != ddi_root_node() && (i_ddi_node_state(dip) < DS_PROBED ||
	    !DEVI_BUSY_OWNED(pdip))) {
		cmn_err(CE_WARN, "%s: NEXUS devinfo node not in DS_PROBED "
		    "or busy held for nexops vector (%p). Failing registration",
		    f, (void *)nexops);
		return (DDI_FAILURE);
	}

	if (nexops->nops_vers != IOMMU_NEXOPS_VERSION) {
		cmn_err(CE_WARN, "%s: %s%d: Invalid IOMMULIB nexops version "
		    "in nexops vector (%p). Failing NEXUS registration",
		    f, driver, instance, (void *)nexops);
		return (DDI_FAILURE);
	}

	ASSERT(nexops->nops_data == NULL);

	if (nexops->nops_id == NULL) {
		cmn_err(CE_WARN, "%s: %s%d: NULL ID field. "
		    "Failing registration for nexops vector: %p",
		    f, driver, instance, (void *)nexops);
		return (DDI_FAILURE);
	}

	if (nexops->nops_dma_allochdl == NULL) {
		cmn_err(CE_WARN, "%s: %s%d: NULL nops_dma_allochdl op. "
		    "Failing registration for ops vector: %p", f,
		    driver, instance, (void *)nexops);
		return (DDI_FAILURE);
	}

	if (nexops->nops_dma_freehdl == NULL) {
		cmn_err(CE_WARN, "%s: %s%d: NULL nops_dma_freehdl op. "
		    "Failing registration for ops vector: %p", f,
		    driver, instance, (void *)nexops);
		return (DDI_FAILURE);
	}

	if (nexops->nops_dma_bindhdl == NULL) {
		cmn_err(CE_WARN, "%s: %s%d: NULL nops_dma_bindhdl op. "
		    "Failing registration for ops vector: %p", f,
		    driver, instance, (void *)nexops);
		return (DDI_FAILURE);
	}

	if (nexops->nops_dma_sync == NULL) {
		cmn_err(CE_WARN, "%s: %s%d: NULL nops_dma_sync op. "
		    "Failing registration for ops vector: %p", f,
		    driver, instance, (void *)nexops);
		return (DDI_FAILURE);
	}

	if (nexops->nops_dma_reset_cookies == NULL) {
		cmn_err(CE_WARN, "%s: %s%d: NULL nops_dma_reset_cookies op. "
		    "Failing registration for ops vector: %p", f,
		    driver, instance, (void *)nexops);
		return (DDI_FAILURE);
	}

	if (nexops->nops_dma_get_cookies == NULL) {
		cmn_err(CE_WARN, "%s: %s%d: NULL nops_dma_get_cookies op. "
		    "Failing registration for ops vector: %p", f,
		    driver, instance, (void *)nexops);
		return (DDI_FAILURE);
	}

	if (nexops->nops_dma_set_cookies == NULL) {
		cmn_err(CE_WARN, "%s: %s%d: NULL nops_dma_set_cookies op. "
		    "Failing registration for ops vector: %p", f,
		    driver, instance, (void *)nexops);
		return (DDI_FAILURE);
	}

	if (nexops->nops_dma_clear_cookies == NULL) {
		cmn_err(CE_WARN, "%s: %s%d: NULL nops_dma_clear_cookies op. "
		    "Failing registration for ops vector: %p", f,
		    driver, instance, (void *)nexops);
		return (DDI_FAILURE);
	}

	if (nexops->nops_dma_get_sleep_flags == NULL) {
		cmn_err(CE_WARN, "%s: %s%d: NULL nops_dma_get_sleep_flags op. "
		    "Failing registration for ops vector: %p", f,
		    driver, instance, (void *)nexops);
		return (DDI_FAILURE);
	}

	if (nexops->nops_dma_win == NULL) {
		cmn_err(CE_WARN, "%s: %s%d: NULL nops_dma_win op. "
		    "Failing registration for ops vector: %p", f,
		    driver, instance, (void *)nexops);
		return (DDI_FAILURE);
	}

	if (nexops->nops_dmahdl_setprivate == NULL) {
		cmn_err(CE_WARN, "%s: %s%d: NULL nops_dmahdl_setprivate op. "
		    "Failing registration for ops vector: %p", f,
		    driver, instance, (void *)nexops);
		return (DDI_FAILURE);
	}

	if (nexops->nops_dmahdl_getprivate == NULL) {
		cmn_err(CE_WARN, "%s: %s%d: NULL nops_dmahdl_getprivate op. "
		    "Failing registration for ops vector: %p", f,
		    driver, instance, (void *)nexops);
		return (DDI_FAILURE);
	}

	nexp = kmem_zalloc(sizeof (iommulib_nex_t), KM_SLEEP);

	mutex_enter(&iommulib_lock);
	if (iommulib_fini == 1) {
		mutex_exit(&iommulib_lock);
		cmn_err(CE_WARN, "%s: IOMMULIB unloading. "
		    "Failing NEXUS register.", f);
		kmem_free(nexp, sizeof (iommulib_nex_t));
		return (DDI_FAILURE);
	}

	/*
	 * fini/register race conditions have been handled. Now create the
	 * nexus struct
	 */
	ndi_hold_devi(dip);
	nexp->nex_dip = dip;
	nexp->nex_ops = *nexops;

	mutex_enter(&iommulib_nexus_lock);
	nexp->nex_next = iommulib_nexus_list;
	iommulib_nexus_list = nexp;
	nexp->nex_prev = NULL;

	if (nexp->nex_next != NULL)
		nexp->nex_next->nex_prev = nexp;

	nexp->nex_ref = 0;

	/*
	 * The nexus device won't be controlled by an IOMMU.
	 */
	DEVI(dip)->devi_iommulib_handle = IOMMU_HANDLE_UNUSED;

	DEVI(dip)->devi_iommulib_nex_handle = nexp;

	mutex_exit(&iommulib_nexus_lock);
	mutex_exit(&iommulib_lock);

	cmn_err(CE_NOTE, "!%s: %s%d: Succesfully registered NEXUS %s "
	    "nexops=%p", f, driver, instance, ddi_node_name(dip),
	    (void *)nexops);

	*handle = nexp;

	return (DDI_SUCCESS);
}
/*
 * The function is to get prom name according non-client dip node.
 * And the function will set the alternate node of dip to alt_dip
 * if it is exist which must be PROM node.
 */
static int
i_devi_to_promname(dev_info_t *dip, char *prom_path, dev_info_t **alt_dipp)
{
	dev_info_t *pdip, *cdip, *idip;
	char *unit_address, *nodename;
	major_t major;
	int depth, old_depth = 0;
	struct parinfo *parinfo = NULL;
	struct parinfo *info;
	int ret = 0;

	if (MDI_CLIENT(dip))
		return (EINVAL);

	if (ddi_pathname_obp(dip, prom_path) != NULL) {
		return (0);
	}
	/*
	 * ddi_pathname_obp return NULL, but the obp path still could
	 * be different with the devfs path name, so need use a parents
	 * stack to compose the path name string layer by layer.
	 */

	/* find the closest ancestor which is a prom node */
	pdip = dip;
	parinfo = kmem_alloc(OBP_STACKDEPTH * sizeof (*parinfo),
	    KM_SLEEP);
	for (depth = 0; ndi_dev_is_prom_node(pdip) == 0; depth++) {
		if (depth == OBP_STACKDEPTH) {
			ret = EINVAL;
			/* must not have been an obp node */
			goto out;
		}
		pdip = get_parent(pdip, &parinfo[depth]);
	}
	old_depth = depth;
	ASSERT(pdip);	/* at least root is prom node */
	if (pdip)
		(void) ddi_pathname(pdip, prom_path);

	ndi_hold_devi(pdip);

	for (depth = old_depth; depth > 0; depth--) {
		info = &parinfo[depth - 1];
		idip = info->dip;
		nodename = ddi_node_name(idip);
		unit_address = ddi_get_name_addr(idip);

		if (pdip) {
			major = ddi_driver_major(idip);
			cdip = find_alternate_node(pdip, major);
			ndi_rele_devi(pdip);
			if (cdip) {
				nodename = ddi_node_name(cdip);
			}
		}

		/*
		 * node name + unitaddr to the prom_path
		 */
		(void) strcat(prom_path, "/");
		(void) strcat(prom_path, nodename);
		if (unit_address && (*unit_address)) {
			(void) strcat(prom_path, "@");
			(void) strcat(prom_path, unit_address);
		}
		pdip = cdip;
	}

	if (pdip) {
		ndi_rele_devi(pdip); /* hold from find_alternate_node */
	}
	/*
	 * Now pdip is the alternate node which is same hierarchy as dip
	 * if it exists.
	 */
	*alt_dipp = pdip;
out:
	if (parinfo) {
		/* release holds from get_parent() */
		for (depth = old_depth; depth > 0; depth--) {
			info = &parinfo[depth - 1];
			if (info && info->pdip)
				ndi_rele_devi(info->pdip);
		}
		kmem_free(parinfo, OBP_STACKDEPTH * sizeof (*parinfo));
	}
	return (ret);
}
/*
 * translate a devfs pathname to one that will be acceptable
 * by the prom.  In most cases, there is no translation needed.
 * For systems supporting generically named devices, the prom
 * may support nodes such as 'disk' that do not have any unit
 * address information (i.e. target,lun info).  If this is the
 * case, the ddi framework will reject the node as invalid and
 * populate the devinfo tree with nodes froms the .conf file
 * (e.g. sd).  In this case, the names that show up in /devices
 * are sd - since the prom only knows about 'disk' nodes, this
 * routine detects this situation and does the conversion
 * There are also cases such as pluto where the disk node in the
 * prom is named "SUNW,ssd" but in /devices the name is "ssd".
 *
 * If MPxIO is enabled, the translation involves following
 * pathinfo nodes to the "best" parent.
 *
 * return a 0 on success with the new device string in ret_buf.
 * Otherwise return the appropriate error code as we may be called
 * from the openprom driver.
 */
int
i_devname_to_promname(char *dev_name, char *ret_buf, size_t len)
{
	dev_info_t *dip, *pdip, *cdip, *alt_dip = NULL;
	mdi_pathinfo_t *pip = NULL;
	char *dev_path, *prom_path;
	char *unit_address, *minorname, *nodename;
	major_t major;
	char *rptr, *optr, *offline;
	size_t olen, rlen;
	int circ;
	int ret = 0;

	/* do some sanity checks */
	if ((dev_name == NULL) || (ret_buf == NULL) ||
	    (strlen(dev_name) > MAXPATHLEN)) {
		return (EINVAL);
	}

	/*
	 * Convert to a /devices name. Fail the translation if
	 * the name doesn't exist.
	 */
	dev_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
	if (resolve_devfs_name(dev_name, dev_path) != 0 ||
	    strncmp(dev_path, "/devices/", 9) != 0) {
		kmem_free(dev_path, MAXPATHLEN);
		return (EINVAL);
	}
	dev_name = dev_path + sizeof ("/devices") - 1;

	bzero(ret_buf, len);

	if (prom_finddevice(dev_name) != OBP_BADNODE) {
		/* we are done */
		(void) snprintf(ret_buf, len, "%s", dev_name);
		kmem_free(dev_path, MAXPATHLEN);
		return (0);
	}

	/*
	 * if we get here, then some portion of the device path is
	 * not understood by the prom.  We need to look for alternate
	 * names (e.g. replace ssd by disk) and mpxio enabled devices.
	 */
	dip = e_ddi_hold_devi_by_path(dev_name, 0);
	if (dip == NULL) {
		cmn_err(CE_NOTE, "cannot find dip for %s", dev_name);
		kmem_free(dev_path, MAXPATHLEN);
		return (EINVAL);
	}

	prom_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
	rlen = len;
	rptr = ret_buf;

	if (!MDI_CLIENT(dip)) {
		ret = i_devi_to_promname(dip, prom_path, &alt_dip);
		if (ret == 0) {
			minorname = strrchr(dev_name, ':');
			if (minorname && (minorname[1] != '\0')) {
				(void) strcat(prom_path, minorname);
			}
			(void) snprintf(rptr, rlen, "%s", prom_path);
		}
	} else {
		/*
		 * if get to here, means dip is a vhci client
		 */
		offline = kmem_zalloc(len, KM_SLEEP); /* offline paths */
		olen = len;
		optr = offline;
		/*
		 * The following code assumes that the phci client is at leaf
		 * level.
		 */
		ndi_devi_enter(dip, &circ);
		while ((pip = mdi_get_next_phci_path(dip, pip)) != NULL) {
			/*
			 * walk all paths associated to the client node
			 */
			bzero(prom_path, MAXPATHLEN);

			/*
			 * replace with mdi_hold_path() when mpxio goes into
			 * genunix
			 */
			MDI_PI_LOCK(pip);
			MDI_PI_HOLD(pip);
			MDI_PI_UNLOCK(pip);

			if (mdi_pi_pathname_obp(pip, prom_path) != NULL) {
				/*
				 * The path has different obp path
				 */
				goto minor_pathinfo;
			}

			pdip = mdi_pi_get_phci(pip);
			ndi_hold_devi(pdip);

			/*
			 * Get obp path name of the phci node firstly.
			 * NOTE: if the alternate node of pdip exists,
			 * the third argument of the i_devi_to_promname()
			 * would be set to the alternate node.
			 */
			(void) i_devi_to_promname(pdip, prom_path, &alt_dip);
			if (alt_dip != NULL) {
				ndi_rele_devi(pdip);
				pdip = alt_dip;
				ndi_hold_devi(pdip);
			}

			nodename = ddi_node_name(dip);
			unit_address = MDI_PI(pip)->pi_addr;

			major = ddi_driver_major(dip);
			cdip = find_alternate_node(pdip, major);

			if (cdip) {
				nodename = ddi_node_name(cdip);
			}
			/*
			 * node name + unitaddr to the prom_path
			 */
			(void) strcat(prom_path, "/");
			(void) strcat(prom_path, nodename);
			if (unit_address && (*unit_address)) {
				(void) strcat(prom_path, "@");
				(void) strcat(prom_path, unit_address);
			}
			if (cdip) {
				/* hold from find_alternate_node */
				ndi_rele_devi(cdip);
			}
			ndi_rele_devi(pdip);
minor_pathinfo:
			minorname = strrchr(dev_name, ':');
			if (minorname && (minorname[1] != '\0')) {
				(void) strcat(prom_path, minorname);
			}

			if (MDI_PI_IS_ONLINE(pip)) {
				(void) snprintf(rptr, rlen, "%s", prom_path);
				rlen -= strlen(rptr) + 1;
				rptr += strlen(rptr) + 1;
				if (rlen <= 0) /* drop paths we can't store */
					break;
			} else {	/* path is offline */
				(void) snprintf(optr, olen, "%s", prom_path);
				olen -= strlen(optr) + 1;
				if (olen > 0) /* drop paths we can't store */
					optr += strlen(optr) + 1;
			}
			MDI_PI_LOCK(pip);
			MDI_PI_RELE(pip);
			if (MDI_PI(pip)->pi_ref_cnt == 0)
				cv_broadcast(&MDI_PI(pip)->pi_ref_cv);
			MDI_PI_UNLOCK(pip);
		}
		ndi_devi_exit(dip, circ);
		ret = 0;
		if (rlen > 0) {
			/* now add as much of offline to ret_buf as possible */
			bcopy(offline, rptr, rlen);
		}
		kmem_free(offline, len);
	}
	/* release hold from e_ddi_hold_devi_by_path() */
	ndi_rele_devi(dip);
	ret_buf[len - 1] = '\0';
	ret_buf[len - 2] = '\0';
	kmem_free(dev_path, MAXPATHLEN);
	kmem_free(prom_path, MAXPATHLEN);

	return (ret);
}
Beispiel #13
0
/*
 * Search for cached entries matching a devid
 * Return two lists:
 *	a list of dev_info nodes, for those devices in the attached state
 *	a list of pathnames whose instances registered the given devid
 * If the lists passed in are not sufficient to return the matching
 * references, return the size of lists required.
 * The dev_info nodes are returned with a hold that the caller must release.
 */
static int
e_devid_cache_devi_path_lists(ddi_devid_t devid, int retmax,
	int *retndevis, dev_info_t **retdevis, int *retnpaths, char **retpaths)
{
	nvp_devid_t *np;
	int ndevis, npaths;
	dev_info_t *dip, *pdip;
	int circ;
	int maxdevis = 0;
	int maxpaths = 0;

	ndevis = 0;
	npaths = 0;
	for (np = NVF_DEVID_LIST(dcfd); np; np = NVP_DEVID_NEXT(np)) {
		if (np->nvp_devid == NULL)
			continue;
		if (ddi_devid_valid(np->nvp_devid) != DDI_SUCCESS) {
			DEVIDERR((CE_CONT,
			    "find: invalid devid %s\n",
			    np->nvp_devpath));
			continue;
		}
		if (ddi_devid_compare(devid, np->nvp_devid) == 0) {
			DEVID_DEBUG2((CE_CONT,
			    "find: devid match: %s 0x%x\n",
			    np->nvp_devpath, np->nvp_flags));
			DEVID_LOG_MATCH(("find", devid, np->nvp_devpath));
			DEVID_LOG_PATHS((CE_CONT, "%s\n", np->nvp_devpath));

			/*
			 * Check if we have a cached devinfo reference for this
			 * devid.  Place a hold on it to prevent detach
			 * Otherwise, use the path instead.
			 * Note: returns with a hold on each dev_info
			 * node in the list.
			 */
			dip = NULL;
			if (np->nvp_flags & NVP_DEVID_DIP) {
				pdip = ddi_get_parent(np->nvp_dip);
				if (ndi_devi_tryenter(pdip, &circ)) {
					dip = np->nvp_dip;
					ndi_hold_devi(dip);
					ndi_devi_exit(pdip, circ);
					ASSERT(!DEVI_IS_ATTACHING(dip));
					ASSERT(!DEVI_IS_DETACHING(dip));
				} else {
					DEVID_LOG_DETACH((CE_CONT,
					    "may be detaching: %s\n",
					    np->nvp_devpath));
				}
			}

			if (dip) {
				if (ndevis < retmax) {
					retdevis[ndevis++] = dip;
				} else {
					ndi_rele_devi(dip);
				}
				maxdevis++;
			} else {
				if (npaths < retmax)
					retpaths[npaths++] = np->nvp_devpath;
				maxpaths++;
			}
		}
	}

	*retndevis = ndevis;
	*retnpaths = npaths;
	return (maxdevis > maxpaths ? maxdevis : maxpaths);
}
Beispiel #14
0
/*
 * The "dip" argument's parent (if it exists) must be held busy.
 */
static int
dr_suspend_devices(dev_info_t *dip, dr_sr_handle_t *srh)
{
	dr_handle_t	*handle;
	major_t		major;
	char		*dname;
	int		circ;

	/*
	 * If dip is the root node, it has no siblings and it is
	 * always held. If dip is not the root node, dr_suspend_devices()
	 * will be invoked with the parent held busy.
	 */
	for (; dip != NULL; dip = ddi_get_next_sibling(dip)) {
		char	d_name[40], d_alias[40], *d_info;

		ndi_devi_enter(dip, &circ);
		if (dr_suspend_devices(ddi_get_child(dip), srh)) {
			ndi_devi_exit(dip, circ);
			return (ENXIO);
		}
		ndi_devi_exit(dip, circ);

		if (!dr_is_real_device(dip))
			continue;

		major = (major_t)-1;
		if ((dname = ddi_binding_name(dip)) != NULL)
			major = ddi_name_to_major(dname);

		if (dr_bypass_device(dname)) {
			PR_QR(" bypassed suspend of %s (major# %d)\n", dname,
				major);
			continue;
		}

		if (drmach_verify_sr(dip, 1)) {
			PR_QR(" bypassed suspend of %s (major# %d)\n", dname,
				major);
			continue;
		}

		if ((d_info = ddi_get_name_addr(dip)) == NULL)
			d_info = "<null>";

		d_name[0] = 0;
		if (dr_resolve_devname(dip, d_name, d_alias) == 0) {
			if (d_alias[0] != 0) {
				prom_printf("\tsuspending %s@%s (aka %s)\n",
					d_name, d_info, d_alias);
			} else {
				prom_printf("\tsuspending %s@%s\n",
					d_name, d_info);
			}
		} else {
			prom_printf("\tsuspending %s@%s\n", dname, d_info);
		}

		if (devi_detach(dip, DDI_SUSPEND) != DDI_SUCCESS) {
			prom_printf("\tFAILED to suspend %s@%s\n",
				d_name[0] ? d_name : dname, d_info);

			srh->sr_err_idx = dr_add_int(srh->sr_err_ints,
				srh->sr_err_idx, DR_MAX_ERR_INT,
				(uint64_t)major);

			ndi_hold_devi(dip);
			srh->sr_failed_dip = dip;

			handle = srh->sr_dr_handlep;
			dr_op_err(CE_IGNORE, handle, ESBD_SUSPEND, "%s@%s",
				d_name[0] ? d_name : dname, d_info);

			return (DDI_FAILURE);
		}
	}

	return (DDI_SUCCESS);
}
Beispiel #15
0
/*
 * Init and attach the root node. root node is the first one to be
 * attached, so the process is somewhat "handcrafted".
 */
void
i_ddi_init_root()
{
#ifdef  DDI_PROP_DEBUG
	(void) ddi_prop_debug(1);	/* Enable property debugging */
#endif  /* DDI_PROP_DEBUG */

	/*
	 * Initialize root node
	 */
	if (impl_ddi_sunbus_initchild(top_devinfo) != DDI_SUCCESS)
		panic("Could not initialize root nexus");

	/*
	 * Attach root node (no need to probe)
	 * Hold both devinfo and rootnex driver so they can't go away.
	 */
	DEVI(top_devinfo)->devi_ops = ndi_hold_driver(top_devinfo);
	ASSERT(DEV_OPS_HELD(DEVI(top_devinfo)->devi_ops));
	DEVI(top_devinfo)->devi_instance = e_ddi_assign_instance(top_devinfo);

	(void) i_ddi_load_drvconf(DEVI(top_devinfo)->devi_major);

	mutex_enter(&(DEVI(top_devinfo)->devi_lock));
	DEVI_SET_ATTACHING(top_devinfo);
	mutex_exit(&(DEVI(top_devinfo)->devi_lock));

	if (devi_attach(top_devinfo, DDI_ATTACH) != DDI_SUCCESS)
		panic("Could not attach root nexus");

	mutex_enter(&(DEVI(top_devinfo)->devi_lock));
	DEVI_CLR_ATTACHING(top_devinfo);
	mutex_exit(&(DEVI(top_devinfo)->devi_lock));

	mutex_init(&global_vhci_lock, NULL, MUTEX_DEFAULT, NULL);

	ndi_hold_devi(top_devinfo);	/* hold it forever */
	i_ddi_set_node_state(top_devinfo, DS_READY);

	/*
	 * Now, expand .conf children of root
	 */
	(void) i_ndi_make_spec_children(top_devinfo, 0);

	/*
	 * Must be set up before attaching root or pseudo drivers
	 */
	pm_init_locks();

	/*
	 * Attach options dip
	 */
	options_dip = i_ddi_attach_pseudo_node("options");

	/*
	 * Attach pseudo nexus and enumerate its children
	 */
	pseudo_dip = i_ddi_attach_pseudo_node(DEVI_PSEUDO_NEXNAME);
	(void) i_ndi_make_spec_children(pseudo_dip, 0);

	/*
	 * Attach and hold clone dip
	 */
	clone_dip = i_ddi_attach_pseudo_node("clone");
	clone_major = ddi_driver_major(clone_dip);
	mm_major = ddi_name_to_major("mm");
	nulldriver_major = ddi_name_to_major("nulldriver");

	/*
	 * Attach scsi_vhci for MPXIO, this registers scsi vhci class
	 * with the MPXIO framework.
	 */
	scsi_vhci_dip = i_ddi_attach_pseudo_node("scsi_vhci");
}