/* * 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); }
/*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); }
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); } }
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 */ }
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); }
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); }
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); }
/* * 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); }
/* * 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); }
/* * 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"); }