1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate /* 30*0Sstevel@tonic-gate * sun4u specific DDI implementation 31*0Sstevel@tonic-gate */ 32*0Sstevel@tonic-gate #include <sys/bootconf.h> 33*0Sstevel@tonic-gate #include <sys/conf.h> 34*0Sstevel@tonic-gate #include <sys/ddi_subrdefs.h> 35*0Sstevel@tonic-gate #include <sys/ethernet.h> 36*0Sstevel@tonic-gate #include <sys/idprom.h> 37*0Sstevel@tonic-gate #include <sys/machsystm.h> 38*0Sstevel@tonic-gate #include <sys/modhash.h> 39*0Sstevel@tonic-gate #include <sys/promif.h> 40*0Sstevel@tonic-gate #include <sys/prom_plat.h> 41*0Sstevel@tonic-gate #include <sys/sunndi.h> 42*0Sstevel@tonic-gate #include <sys/systeminfo.h> 43*0Sstevel@tonic-gate #include <sys/fpu/fpusystm.h> 44*0Sstevel@tonic-gate #include <sys/vm.h> 45*0Sstevel@tonic-gate #include <sys/fs/dv_node.h> 46*0Sstevel@tonic-gate #include <sys/fs/snode.h> 47*0Sstevel@tonic-gate 48*0Sstevel@tonic-gate /* 49*0Sstevel@tonic-gate * Favored drivers of this implementation 50*0Sstevel@tonic-gate * architecture. These drivers MUST be present for 51*0Sstevel@tonic-gate * the system to boot at all. 52*0Sstevel@tonic-gate */ 53*0Sstevel@tonic-gate char *impl_module_list[] = { 54*0Sstevel@tonic-gate "rootnex", 55*0Sstevel@tonic-gate "options", 56*0Sstevel@tonic-gate "sad", /* Referenced via init_tbl[] */ 57*0Sstevel@tonic-gate "pseudo", 58*0Sstevel@tonic-gate "clone", 59*0Sstevel@tonic-gate "scsi_vhci", 60*0Sstevel@tonic-gate (char *)0 61*0Sstevel@tonic-gate }; 62*0Sstevel@tonic-gate 63*0Sstevel@tonic-gate /* 64*0Sstevel@tonic-gate * These strings passed to not_serviced in locore.s 65*0Sstevel@tonic-gate */ 66*0Sstevel@tonic-gate const char busname_ovec[] = "onboard "; 67*0Sstevel@tonic-gate const char busname_svec[] = "SBus "; 68*0Sstevel@tonic-gate const char busname_vec[] = ""; 69*0Sstevel@tonic-gate 70*0Sstevel@tonic-gate 71*0Sstevel@tonic-gate static uint64_t *intr_map_reg[32]; 72*0Sstevel@tonic-gate 73*0Sstevel@tonic-gate /* 74*0Sstevel@tonic-gate * Forward declarations 75*0Sstevel@tonic-gate */ 76*0Sstevel@tonic-gate static int getlongprop_buf(); 77*0Sstevel@tonic-gate static int get_boardnum(int nid, dev_info_t *par); 78*0Sstevel@tonic-gate 79*0Sstevel@tonic-gate /* 80*0Sstevel@tonic-gate * Check the status of the device node passed as an argument. 81*0Sstevel@tonic-gate * 82*0Sstevel@tonic-gate * if ((status is OKAY) || (status is DISABLED)) 83*0Sstevel@tonic-gate * return DDI_SUCCESS 84*0Sstevel@tonic-gate * else 85*0Sstevel@tonic-gate * print a warning and return DDI_FAILURE 86*0Sstevel@tonic-gate */ 87*0Sstevel@tonic-gate /*ARGSUSED*/ 88*0Sstevel@tonic-gate int 89*0Sstevel@tonic-gate check_status(int id, char *buf, dev_info_t *parent) 90*0Sstevel@tonic-gate { 91*0Sstevel@tonic-gate char status_buf[64]; 92*0Sstevel@tonic-gate char devtype_buf[OBP_MAXPROPNAME]; 93*0Sstevel@tonic-gate char board_buf[32]; 94*0Sstevel@tonic-gate char path[OBP_MAXPATHLEN]; 95*0Sstevel@tonic-gate int boardnum; 96*0Sstevel@tonic-gate int retval = DDI_FAILURE; 97*0Sstevel@tonic-gate extern int status_okay(int, char *, int); 98*0Sstevel@tonic-gate 99*0Sstevel@tonic-gate /* 100*0Sstevel@tonic-gate * is the status okay? 101*0Sstevel@tonic-gate */ 102*0Sstevel@tonic-gate if (status_okay(id, status_buf, sizeof (status_buf))) 103*0Sstevel@tonic-gate return (DDI_SUCCESS); 104*0Sstevel@tonic-gate 105*0Sstevel@tonic-gate /* 106*0Sstevel@tonic-gate * a status property indicating bad memory will be associated 107*0Sstevel@tonic-gate * with a node which has a "device_type" property with a value of 108*0Sstevel@tonic-gate * "memory-controller". in this situation, return DDI_SUCCESS 109*0Sstevel@tonic-gate */ 110*0Sstevel@tonic-gate if (getlongprop_buf(id, OBP_DEVICETYPE, devtype_buf, 111*0Sstevel@tonic-gate sizeof (devtype_buf)) > 0) { 112*0Sstevel@tonic-gate if (strcmp(devtype_buf, "memory-controller") == 0) 113*0Sstevel@tonic-gate retval = DDI_SUCCESS; 114*0Sstevel@tonic-gate } 115*0Sstevel@tonic-gate 116*0Sstevel@tonic-gate /* 117*0Sstevel@tonic-gate * get the full OBP pathname of this node 118*0Sstevel@tonic-gate */ 119*0Sstevel@tonic-gate if (prom_phandle_to_path((phandle_t)id, path, sizeof (path)) < 0) 120*0Sstevel@tonic-gate cmn_err(CE_WARN, "prom_phandle_to_path(%d) failed", id); 121*0Sstevel@tonic-gate 122*0Sstevel@tonic-gate /* 123*0Sstevel@tonic-gate * get the board number, if one exists 124*0Sstevel@tonic-gate */ 125*0Sstevel@tonic-gate if ((boardnum = get_boardnum(id, parent)) >= 0) 126*0Sstevel@tonic-gate (void) sprintf(board_buf, " on board %d", boardnum); 127*0Sstevel@tonic-gate else 128*0Sstevel@tonic-gate board_buf[0] = '\0'; 129*0Sstevel@tonic-gate 130*0Sstevel@tonic-gate /* 131*0Sstevel@tonic-gate * print the status property information 132*0Sstevel@tonic-gate */ 133*0Sstevel@tonic-gate cmn_err(CE_WARN, "status '%s' for '%s'%s", 134*0Sstevel@tonic-gate status_buf, path, board_buf); 135*0Sstevel@tonic-gate return (retval); 136*0Sstevel@tonic-gate } 137*0Sstevel@tonic-gate 138*0Sstevel@tonic-gate /* 139*0Sstevel@tonic-gate * determine the board number associated with this nodeid 140*0Sstevel@tonic-gate */ 141*0Sstevel@tonic-gate static int 142*0Sstevel@tonic-gate get_boardnum(int nid, dev_info_t *par) 143*0Sstevel@tonic-gate { 144*0Sstevel@tonic-gate int board_num; 145*0Sstevel@tonic-gate 146*0Sstevel@tonic-gate if (prom_getprop((dnode_t)nid, OBP_BOARDNUM, 147*0Sstevel@tonic-gate (caddr_t)&board_num) != -1) 148*0Sstevel@tonic-gate return (board_num); 149*0Sstevel@tonic-gate 150*0Sstevel@tonic-gate /* 151*0Sstevel@tonic-gate * Look at current node and up the parent chain 152*0Sstevel@tonic-gate * till we find a node with an OBP_BOARDNUM. 153*0Sstevel@tonic-gate */ 154*0Sstevel@tonic-gate while (par) { 155*0Sstevel@tonic-gate nid = ddi_get_nodeid(par); 156*0Sstevel@tonic-gate 157*0Sstevel@tonic-gate if (prom_getprop((dnode_t)nid, OBP_BOARDNUM, 158*0Sstevel@tonic-gate (caddr_t)&board_num) != -1) 159*0Sstevel@tonic-gate return (board_num); 160*0Sstevel@tonic-gate 161*0Sstevel@tonic-gate par = ddi_get_parent(par); 162*0Sstevel@tonic-gate } 163*0Sstevel@tonic-gate return (-1); 164*0Sstevel@tonic-gate } 165*0Sstevel@tonic-gate 166*0Sstevel@tonic-gate /* 167*0Sstevel@tonic-gate * Note that this routine does not take into account the endianness 168*0Sstevel@tonic-gate * of the host or the device (or PROM) when retrieving properties. 169*0Sstevel@tonic-gate */ 170*0Sstevel@tonic-gate static int 171*0Sstevel@tonic-gate getlongprop_buf(int id, char *name, char *buf, int maxlen) 172*0Sstevel@tonic-gate { 173*0Sstevel@tonic-gate int size; 174*0Sstevel@tonic-gate 175*0Sstevel@tonic-gate size = prom_getproplen((dnode_t)id, name); 176*0Sstevel@tonic-gate if (size <= 0 || (size > maxlen - 1)) 177*0Sstevel@tonic-gate return (-1); 178*0Sstevel@tonic-gate 179*0Sstevel@tonic-gate if (-1 == prom_getprop((dnode_t)id, name, buf)) 180*0Sstevel@tonic-gate return (-1); 181*0Sstevel@tonic-gate 182*0Sstevel@tonic-gate /* 183*0Sstevel@tonic-gate * Workaround for bugid 1085575 - OBP may return a "name" property 184*0Sstevel@tonic-gate * without null terminating the string with '\0'. When this occurs, 185*0Sstevel@tonic-gate * append a '\0' and return (size + 1). 186*0Sstevel@tonic-gate */ 187*0Sstevel@tonic-gate if (strcmp("name", name) == 0) { 188*0Sstevel@tonic-gate if (buf[size - 1] != '\0') { 189*0Sstevel@tonic-gate buf[size] = '\0'; 190*0Sstevel@tonic-gate size += 1; 191*0Sstevel@tonic-gate } 192*0Sstevel@tonic-gate } 193*0Sstevel@tonic-gate 194*0Sstevel@tonic-gate return (size); 195*0Sstevel@tonic-gate } 196*0Sstevel@tonic-gate 197*0Sstevel@tonic-gate /* 198*0Sstevel@tonic-gate * Routines to set/get UPA slave only device interrupt mapping registers. 199*0Sstevel@tonic-gate * set_intr_mapping_reg() is called by the UPA master to register the address 200*0Sstevel@tonic-gate * of an interrupt mapping register. The upa id is that of the master. If 201*0Sstevel@tonic-gate * this routine is called on behalf of a slave device, the framework 202*0Sstevel@tonic-gate * determines the upa id of the slave based on that supplied by the master. 203*0Sstevel@tonic-gate * 204*0Sstevel@tonic-gate * get_intr_mapping_reg() is called by the UPA nexus driver on behalf 205*0Sstevel@tonic-gate * of a child device to get and program the interrupt mapping register of 206*0Sstevel@tonic-gate * one of it's child nodes. It uses the upa id of the child device to 207*0Sstevel@tonic-gate * index into a table of mapping registers. If the routine is called on 208*0Sstevel@tonic-gate * behalf of a slave device and the mapping register has not been set, 209*0Sstevel@tonic-gate * the framework determines the devinfo node of the corresponding master 210*0Sstevel@tonic-gate * nexus which owns the mapping register of the slave and installs that 211*0Sstevel@tonic-gate * driver. The device driver which owns the mapping register must call 212*0Sstevel@tonic-gate * set_intr_mapping_reg() in its attach routine to register the slaves 213*0Sstevel@tonic-gate * mapping register with the system. 214*0Sstevel@tonic-gate */ 215*0Sstevel@tonic-gate void 216*0Sstevel@tonic-gate set_intr_mapping_reg(int upaid, uint64_t *addr, int slave) 217*0Sstevel@tonic-gate { 218*0Sstevel@tonic-gate int affin_upaid; 219*0Sstevel@tonic-gate 220*0Sstevel@tonic-gate /* For UPA master devices, set the mapping reg addr and we're done */ 221*0Sstevel@tonic-gate if (slave == 0) { 222*0Sstevel@tonic-gate intr_map_reg[upaid] = addr; 223*0Sstevel@tonic-gate return; 224*0Sstevel@tonic-gate } 225*0Sstevel@tonic-gate 226*0Sstevel@tonic-gate /* 227*0Sstevel@tonic-gate * If we get here, we're adding an entry for a UPA slave only device. 228*0Sstevel@tonic-gate * The UPA id of the device which has affinity with that requesting, 229*0Sstevel@tonic-gate * will be the device with the same UPA id minus the slave number. 230*0Sstevel@tonic-gate * If the affin_upaid is negative, silently return to the caller. 231*0Sstevel@tonic-gate */ 232*0Sstevel@tonic-gate if ((affin_upaid = upaid - slave) < 0) 233*0Sstevel@tonic-gate return; 234*0Sstevel@tonic-gate 235*0Sstevel@tonic-gate /* 236*0Sstevel@tonic-gate * Load the address of the mapping register in the correct slot 237*0Sstevel@tonic-gate * for the slave device. 238*0Sstevel@tonic-gate */ 239*0Sstevel@tonic-gate intr_map_reg[affin_upaid] = addr; 240*0Sstevel@tonic-gate } 241*0Sstevel@tonic-gate 242*0Sstevel@tonic-gate uint64_t * 243*0Sstevel@tonic-gate get_intr_mapping_reg(int upaid, int slave) 244*0Sstevel@tonic-gate { 245*0Sstevel@tonic-gate int affin_upaid; 246*0Sstevel@tonic-gate dev_info_t *affin_dip; 247*0Sstevel@tonic-gate uint64_t *addr = intr_map_reg[upaid]; 248*0Sstevel@tonic-gate 249*0Sstevel@tonic-gate /* If we're a UPA master, or we have a valid mapping register. */ 250*0Sstevel@tonic-gate if (!slave || addr != NULL) 251*0Sstevel@tonic-gate return (addr); 252*0Sstevel@tonic-gate 253*0Sstevel@tonic-gate /* 254*0Sstevel@tonic-gate * We only get here if we're a UPA slave only device whose interrupt 255*0Sstevel@tonic-gate * mapping register has not been set. 256*0Sstevel@tonic-gate * We need to try and install the nexus whose physical address 257*0Sstevel@tonic-gate * space is where the slaves mapping register resides. They 258*0Sstevel@tonic-gate * should call set_intr_mapping_reg() in their xxattach() to register 259*0Sstevel@tonic-gate * the mapping register with the system. 260*0Sstevel@tonic-gate */ 261*0Sstevel@tonic-gate 262*0Sstevel@tonic-gate /* 263*0Sstevel@tonic-gate * We don't know if a single- or multi-interrupt proxy is fielding 264*0Sstevel@tonic-gate * our UPA slave interrupt, we must check both cases. 265*0Sstevel@tonic-gate * Start out by assuming the multi-interrupt case. 266*0Sstevel@tonic-gate * We assume that single- and multi- interrupters are not 267*0Sstevel@tonic-gate * overlapping in UPA portid space. 268*0Sstevel@tonic-gate */ 269*0Sstevel@tonic-gate 270*0Sstevel@tonic-gate affin_upaid = upaid | 3; 271*0Sstevel@tonic-gate 272*0Sstevel@tonic-gate /* 273*0Sstevel@tonic-gate * We start looking for the multi-interrupter affinity node. 274*0Sstevel@tonic-gate * We know it's ONLY a child of the root node since the root 275*0Sstevel@tonic-gate * node defines UPA space. 276*0Sstevel@tonic-gate */ 277*0Sstevel@tonic-gate for (affin_dip = ddi_get_child(ddi_root_node()); affin_dip; 278*0Sstevel@tonic-gate affin_dip = ddi_get_next_sibling(affin_dip)) 279*0Sstevel@tonic-gate if (ddi_prop_get_int(DDI_DEV_T_ANY, affin_dip, 280*0Sstevel@tonic-gate DDI_PROP_DONTPASS, "upa-portid", -1) == affin_upaid) 281*0Sstevel@tonic-gate break; 282*0Sstevel@tonic-gate 283*0Sstevel@tonic-gate if (affin_dip) { 284*0Sstevel@tonic-gate if (i_ddi_attach_node_hierarchy(affin_dip) == DDI_SUCCESS) { 285*0Sstevel@tonic-gate /* try again to get the mapping register. */ 286*0Sstevel@tonic-gate addr = intr_map_reg[upaid]; 287*0Sstevel@tonic-gate } 288*0Sstevel@tonic-gate } 289*0Sstevel@tonic-gate 290*0Sstevel@tonic-gate /* 291*0Sstevel@tonic-gate * If we still don't have a mapping register try single -interrupter 292*0Sstevel@tonic-gate * case. 293*0Sstevel@tonic-gate */ 294*0Sstevel@tonic-gate if (addr == NULL) { 295*0Sstevel@tonic-gate 296*0Sstevel@tonic-gate affin_upaid = upaid | 1; 297*0Sstevel@tonic-gate 298*0Sstevel@tonic-gate for (affin_dip = ddi_get_child(ddi_root_node()); affin_dip; 299*0Sstevel@tonic-gate affin_dip = ddi_get_next_sibling(affin_dip)) 300*0Sstevel@tonic-gate if (ddi_prop_get_int(DDI_DEV_T_ANY, affin_dip, 301*0Sstevel@tonic-gate DDI_PROP_DONTPASS, "upa-portid", -1) == affin_upaid) 302*0Sstevel@tonic-gate break; 303*0Sstevel@tonic-gate 304*0Sstevel@tonic-gate if (affin_dip) { 305*0Sstevel@tonic-gate if (i_ddi_attach_node_hierarchy(affin_dip) 306*0Sstevel@tonic-gate == DDI_SUCCESS) { 307*0Sstevel@tonic-gate /* try again to get the mapping register. */ 308*0Sstevel@tonic-gate addr = intr_map_reg[upaid]; 309*0Sstevel@tonic-gate } 310*0Sstevel@tonic-gate } 311*0Sstevel@tonic-gate } 312*0Sstevel@tonic-gate return (addr); 313*0Sstevel@tonic-gate } 314*0Sstevel@tonic-gate 315*0Sstevel@tonic-gate 316*0Sstevel@tonic-gate static struct upa_dma_pfns { 317*0Sstevel@tonic-gate pfn_t hipfn; 318*0Sstevel@tonic-gate pfn_t lopfn; 319*0Sstevel@tonic-gate } upa_dma_pfn_array[MAX_UPA]; 320*0Sstevel@tonic-gate 321*0Sstevel@tonic-gate static int upa_dma_pfn_ndx = 0; 322*0Sstevel@tonic-gate 323*0Sstevel@tonic-gate /* 324*0Sstevel@tonic-gate * Certain UPA busses cannot accept dma transactions from any other source 325*0Sstevel@tonic-gate * except for memory due to livelock conditions in their hardware. (e.g. sbus 326*0Sstevel@tonic-gate * and PCI). These routines allow devices or busses on the UPA to register 327*0Sstevel@tonic-gate * a physical address block within it's own register space where DMA can be 328*0Sstevel@tonic-gate * performed. Currently, the FFB is the only such device which supports 329*0Sstevel@tonic-gate * device DMA on the UPA. 330*0Sstevel@tonic-gate */ 331*0Sstevel@tonic-gate void 332*0Sstevel@tonic-gate pf_set_dmacapable(pfn_t hipfn, pfn_t lopfn) 333*0Sstevel@tonic-gate { 334*0Sstevel@tonic-gate int i = upa_dma_pfn_ndx; 335*0Sstevel@tonic-gate 336*0Sstevel@tonic-gate upa_dma_pfn_ndx++; 337*0Sstevel@tonic-gate 338*0Sstevel@tonic-gate upa_dma_pfn_array[i].hipfn = hipfn; 339*0Sstevel@tonic-gate upa_dma_pfn_array[i].lopfn = lopfn; 340*0Sstevel@tonic-gate } 341*0Sstevel@tonic-gate 342*0Sstevel@tonic-gate void 343*0Sstevel@tonic-gate pf_unset_dmacapable(pfn_t pfn) 344*0Sstevel@tonic-gate { 345*0Sstevel@tonic-gate int i; 346*0Sstevel@tonic-gate 347*0Sstevel@tonic-gate for (i = 0; i < upa_dma_pfn_ndx; i++) { 348*0Sstevel@tonic-gate if (pfn <= upa_dma_pfn_array[i].hipfn && 349*0Sstevel@tonic-gate pfn >= upa_dma_pfn_array[i].lopfn) { 350*0Sstevel@tonic-gate upa_dma_pfn_array[i].hipfn = 351*0Sstevel@tonic-gate upa_dma_pfn_array[upa_dma_pfn_ndx - 1].hipfn; 352*0Sstevel@tonic-gate upa_dma_pfn_array[i].lopfn = 353*0Sstevel@tonic-gate upa_dma_pfn_array[upa_dma_pfn_ndx - 1].lopfn; 354*0Sstevel@tonic-gate upa_dma_pfn_ndx--; 355*0Sstevel@tonic-gate break; 356*0Sstevel@tonic-gate } 357*0Sstevel@tonic-gate } 358*0Sstevel@tonic-gate } 359*0Sstevel@tonic-gate 360*0Sstevel@tonic-gate /* 361*0Sstevel@tonic-gate * This routine should only be called using a pfn that is known to reside 362*0Sstevel@tonic-gate * in IO space. The function pf_is_memory() can be used to determine this. 363*0Sstevel@tonic-gate */ 364*0Sstevel@tonic-gate int 365*0Sstevel@tonic-gate pf_is_dmacapable(pfn_t pfn) 366*0Sstevel@tonic-gate { 367*0Sstevel@tonic-gate int i, j; 368*0Sstevel@tonic-gate 369*0Sstevel@tonic-gate /* If the caller passed in a memory pfn, return true. */ 370*0Sstevel@tonic-gate if (pf_is_memory(pfn)) 371*0Sstevel@tonic-gate return (1); 372*0Sstevel@tonic-gate 373*0Sstevel@tonic-gate for (i = upa_dma_pfn_ndx, j = 0; j < i; j++) 374*0Sstevel@tonic-gate if (pfn <= upa_dma_pfn_array[j].hipfn && 375*0Sstevel@tonic-gate pfn >= upa_dma_pfn_array[j].lopfn) 376*0Sstevel@tonic-gate return (1); 377*0Sstevel@tonic-gate 378*0Sstevel@tonic-gate return (0); 379*0Sstevel@tonic-gate } 380*0Sstevel@tonic-gate 381*0Sstevel@tonic-gate 382*0Sstevel@tonic-gate /* 383*0Sstevel@tonic-gate * Find cpu_id corresponding to the dip of a CPU device node 384*0Sstevel@tonic-gate */ 385*0Sstevel@tonic-gate int 386*0Sstevel@tonic-gate dip_to_cpu_id(dev_info_t *dip, processorid_t *cpu_id) 387*0Sstevel@tonic-gate { 388*0Sstevel@tonic-gate dnode_t nodeid; 389*0Sstevel@tonic-gate int i; 390*0Sstevel@tonic-gate 391*0Sstevel@tonic-gate nodeid = (dnode_t)ddi_get_nodeid(dip); 392*0Sstevel@tonic-gate for (i = 0; i < NCPU; i++) { 393*0Sstevel@tonic-gate if (cpunodes[i].nodeid == nodeid) { 394*0Sstevel@tonic-gate *cpu_id = i; 395*0Sstevel@tonic-gate return (DDI_SUCCESS); 396*0Sstevel@tonic-gate } 397*0Sstevel@tonic-gate } 398*0Sstevel@tonic-gate return (DDI_FAILURE); 399*0Sstevel@tonic-gate } 400*0Sstevel@tonic-gate 401*0Sstevel@tonic-gate /* 402*0Sstevel@tonic-gate * Platform independent DR routines 403*0Sstevel@tonic-gate */ 404*0Sstevel@tonic-gate 405*0Sstevel@tonic-gate static int 406*0Sstevel@tonic-gate ndi2errno(int n) 407*0Sstevel@tonic-gate { 408*0Sstevel@tonic-gate int err = 0; 409*0Sstevel@tonic-gate 410*0Sstevel@tonic-gate switch (n) { 411*0Sstevel@tonic-gate case NDI_NOMEM: 412*0Sstevel@tonic-gate err = ENOMEM; 413*0Sstevel@tonic-gate break; 414*0Sstevel@tonic-gate case NDI_BUSY: 415*0Sstevel@tonic-gate err = EBUSY; 416*0Sstevel@tonic-gate break; 417*0Sstevel@tonic-gate case NDI_FAULT: 418*0Sstevel@tonic-gate err = EFAULT; 419*0Sstevel@tonic-gate break; 420*0Sstevel@tonic-gate case NDI_FAILURE: 421*0Sstevel@tonic-gate err = EIO; 422*0Sstevel@tonic-gate break; 423*0Sstevel@tonic-gate case NDI_SUCCESS: 424*0Sstevel@tonic-gate break; 425*0Sstevel@tonic-gate case NDI_BADHANDLE: 426*0Sstevel@tonic-gate default: 427*0Sstevel@tonic-gate err = EINVAL; 428*0Sstevel@tonic-gate break; 429*0Sstevel@tonic-gate } 430*0Sstevel@tonic-gate return (err); 431*0Sstevel@tonic-gate } 432*0Sstevel@tonic-gate 433*0Sstevel@tonic-gate /* 434*0Sstevel@tonic-gate * Prom tree node list 435*0Sstevel@tonic-gate */ 436*0Sstevel@tonic-gate struct ptnode { 437*0Sstevel@tonic-gate dnode_t nodeid; 438*0Sstevel@tonic-gate struct ptnode *next; 439*0Sstevel@tonic-gate }; 440*0Sstevel@tonic-gate 441*0Sstevel@tonic-gate /* 442*0Sstevel@tonic-gate * Prom tree walk arg 443*0Sstevel@tonic-gate */ 444*0Sstevel@tonic-gate struct pta { 445*0Sstevel@tonic-gate dev_info_t *pdip; 446*0Sstevel@tonic-gate devi_branch_t *bp; 447*0Sstevel@tonic-gate uint_t flags; 448*0Sstevel@tonic-gate dev_info_t *fdip; 449*0Sstevel@tonic-gate struct ptnode *head; 450*0Sstevel@tonic-gate }; 451*0Sstevel@tonic-gate 452*0Sstevel@tonic-gate static void 453*0Sstevel@tonic-gate visit_node(dnode_t nodeid, struct pta *ap) 454*0Sstevel@tonic-gate { 455*0Sstevel@tonic-gate struct ptnode **nextp; 456*0Sstevel@tonic-gate int (*select)(dnode_t, void *, uint_t); 457*0Sstevel@tonic-gate 458*0Sstevel@tonic-gate ASSERT(nodeid != OBP_NONODE && nodeid != OBP_BADNODE); 459*0Sstevel@tonic-gate 460*0Sstevel@tonic-gate select = ap->bp->create.prom_branch_select; 461*0Sstevel@tonic-gate 462*0Sstevel@tonic-gate ASSERT(select); 463*0Sstevel@tonic-gate 464*0Sstevel@tonic-gate if (select(nodeid, ap->bp->arg, 0) == DDI_SUCCESS) { 465*0Sstevel@tonic-gate 466*0Sstevel@tonic-gate for (nextp = &ap->head; *nextp; nextp = &(*nextp)->next) 467*0Sstevel@tonic-gate ; 468*0Sstevel@tonic-gate 469*0Sstevel@tonic-gate *nextp = kmem_zalloc(sizeof (struct ptnode), KM_SLEEP); 470*0Sstevel@tonic-gate 471*0Sstevel@tonic-gate (*nextp)->nodeid = nodeid; 472*0Sstevel@tonic-gate } 473*0Sstevel@tonic-gate 474*0Sstevel@tonic-gate if ((ap->flags & DEVI_BRANCH_CHILD) == DEVI_BRANCH_CHILD) 475*0Sstevel@tonic-gate return; 476*0Sstevel@tonic-gate 477*0Sstevel@tonic-gate nodeid = prom_childnode(nodeid); 478*0Sstevel@tonic-gate while (nodeid != OBP_NONODE && nodeid != OBP_BADNODE) { 479*0Sstevel@tonic-gate visit_node(nodeid, ap); 480*0Sstevel@tonic-gate nodeid = prom_nextnode(nodeid); 481*0Sstevel@tonic-gate } 482*0Sstevel@tonic-gate } 483*0Sstevel@tonic-gate 484*0Sstevel@tonic-gate /*ARGSUSED*/ 485*0Sstevel@tonic-gate static int 486*0Sstevel@tonic-gate set_dip_offline(dev_info_t *dip, void *arg) 487*0Sstevel@tonic-gate { 488*0Sstevel@tonic-gate ASSERT(dip); 489*0Sstevel@tonic-gate 490*0Sstevel@tonic-gate if (!DEVI_IS_DEVICE_OFFLINE(dip)) 491*0Sstevel@tonic-gate DEVI_SET_DEVICE_OFFLINE(dip); 492*0Sstevel@tonic-gate 493*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 494*0Sstevel@tonic-gate } 495*0Sstevel@tonic-gate 496*0Sstevel@tonic-gate /*ARGSUSED*/ 497*0Sstevel@tonic-gate static int 498*0Sstevel@tonic-gate create_prom_branch(void *arg, int has_changed) 499*0Sstevel@tonic-gate { 500*0Sstevel@tonic-gate int circ, c; 501*0Sstevel@tonic-gate int exists, rv; 502*0Sstevel@tonic-gate dnode_t nodeid; 503*0Sstevel@tonic-gate struct ptnode *tnp; 504*0Sstevel@tonic-gate dev_info_t *dip; 505*0Sstevel@tonic-gate struct pta *ap = arg; 506*0Sstevel@tonic-gate devi_branch_t *bp; 507*0Sstevel@tonic-gate 508*0Sstevel@tonic-gate ASSERT(ap); 509*0Sstevel@tonic-gate ASSERT(ap->fdip == NULL); 510*0Sstevel@tonic-gate ASSERT(ap->pdip && ndi_dev_is_prom_node(ap->pdip)); 511*0Sstevel@tonic-gate 512*0Sstevel@tonic-gate bp = ap->bp; 513*0Sstevel@tonic-gate 514*0Sstevel@tonic-gate nodeid = ddi_get_nodeid(ap->pdip); 515*0Sstevel@tonic-gate if (nodeid == OBP_NONODE || nodeid == OBP_BADNODE) { 516*0Sstevel@tonic-gate cmn_err(CE_WARN, "create_prom_branch: invalid " 517*0Sstevel@tonic-gate "nodeid: 0x%x", nodeid); 518*0Sstevel@tonic-gate return (EINVAL); 519*0Sstevel@tonic-gate } 520*0Sstevel@tonic-gate 521*0Sstevel@tonic-gate ap->head = NULL; 522*0Sstevel@tonic-gate 523*0Sstevel@tonic-gate nodeid = prom_childnode(nodeid); 524*0Sstevel@tonic-gate while (nodeid != OBP_NONODE && nodeid != OBP_BADNODE) { 525*0Sstevel@tonic-gate visit_node(nodeid, ap); 526*0Sstevel@tonic-gate nodeid = prom_nextnode(nodeid); 527*0Sstevel@tonic-gate } 528*0Sstevel@tonic-gate 529*0Sstevel@tonic-gate if (ap->head == NULL) 530*0Sstevel@tonic-gate return (ENODEV); 531*0Sstevel@tonic-gate 532*0Sstevel@tonic-gate rv = 0; 533*0Sstevel@tonic-gate while ((tnp = ap->head) != NULL) { 534*0Sstevel@tonic-gate ap->head = tnp->next; 535*0Sstevel@tonic-gate 536*0Sstevel@tonic-gate ndi_devi_enter(ap->pdip, &circ); 537*0Sstevel@tonic-gate 538*0Sstevel@tonic-gate /* 539*0Sstevel@tonic-gate * Check if the branch already exists. 540*0Sstevel@tonic-gate */ 541*0Sstevel@tonic-gate exists = 0; 542*0Sstevel@tonic-gate dip = e_ddi_nodeid_to_dip(tnp->nodeid); 543*0Sstevel@tonic-gate if (dip != NULL) { 544*0Sstevel@tonic-gate exists = 1; 545*0Sstevel@tonic-gate 546*0Sstevel@tonic-gate /* Parent is held busy, so release hold */ 547*0Sstevel@tonic-gate ndi_rele_devi(dip); 548*0Sstevel@tonic-gate #ifdef DEBUG 549*0Sstevel@tonic-gate cmn_err(CE_WARN, "create_prom_branch: dip(%p) exists" 550*0Sstevel@tonic-gate " for nodeid 0x%x", (void *)dip, tnp->nodeid); 551*0Sstevel@tonic-gate #endif 552*0Sstevel@tonic-gate } else { 553*0Sstevel@tonic-gate dip = i_ddi_create_branch(ap->pdip, tnp->nodeid); 554*0Sstevel@tonic-gate } 555*0Sstevel@tonic-gate 556*0Sstevel@tonic-gate kmem_free(tnp, sizeof (struct ptnode)); 557*0Sstevel@tonic-gate 558*0Sstevel@tonic-gate if (dip == NULL) { 559*0Sstevel@tonic-gate ndi_devi_exit(ap->pdip, circ); 560*0Sstevel@tonic-gate rv = EIO; 561*0Sstevel@tonic-gate continue; 562*0Sstevel@tonic-gate } 563*0Sstevel@tonic-gate 564*0Sstevel@tonic-gate ASSERT(ddi_get_parent(dip) == ap->pdip); 565*0Sstevel@tonic-gate 566*0Sstevel@tonic-gate /* 567*0Sstevel@tonic-gate * Hold the branch if it is not already held 568*0Sstevel@tonic-gate */ 569*0Sstevel@tonic-gate if (!exists) 570*0Sstevel@tonic-gate e_ddi_branch_hold(dip); 571*0Sstevel@tonic-gate 572*0Sstevel@tonic-gate ASSERT(e_ddi_branch_held(dip)); 573*0Sstevel@tonic-gate 574*0Sstevel@tonic-gate /* 575*0Sstevel@tonic-gate * Set all dips in the branch offline so that 576*0Sstevel@tonic-gate * only a "configure" operation can attach 577*0Sstevel@tonic-gate * the branch 578*0Sstevel@tonic-gate */ 579*0Sstevel@tonic-gate (void) set_dip_offline(dip, NULL); 580*0Sstevel@tonic-gate 581*0Sstevel@tonic-gate ndi_devi_enter(dip, &c); 582*0Sstevel@tonic-gate ddi_walk_devs(ddi_get_child(dip), set_dip_offline, NULL); 583*0Sstevel@tonic-gate ndi_devi_exit(dip, c); 584*0Sstevel@tonic-gate 585*0Sstevel@tonic-gate ndi_devi_exit(ap->pdip, circ); 586*0Sstevel@tonic-gate 587*0Sstevel@tonic-gate if (ap->flags & DEVI_BRANCH_CONFIGURE) { 588*0Sstevel@tonic-gate int error = e_ddi_branch_configure(dip, &ap->fdip, 0); 589*0Sstevel@tonic-gate if (error && rv == 0) 590*0Sstevel@tonic-gate rv = error; 591*0Sstevel@tonic-gate } 592*0Sstevel@tonic-gate 593*0Sstevel@tonic-gate /* 594*0Sstevel@tonic-gate * Invoke devi_branch_callback() (if it exists) only for 595*0Sstevel@tonic-gate * newly created branches 596*0Sstevel@tonic-gate */ 597*0Sstevel@tonic-gate if (bp->devi_branch_callback && !exists) 598*0Sstevel@tonic-gate bp->devi_branch_callback(dip, bp->arg, 0); 599*0Sstevel@tonic-gate } 600*0Sstevel@tonic-gate 601*0Sstevel@tonic-gate return (rv); 602*0Sstevel@tonic-gate } 603*0Sstevel@tonic-gate 604*0Sstevel@tonic-gate static int 605*0Sstevel@tonic-gate sid_node_create(dev_info_t *pdip, devi_branch_t *bp, dev_info_t **rdipp) 606*0Sstevel@tonic-gate { 607*0Sstevel@tonic-gate int rv, circ, len; 608*0Sstevel@tonic-gate int i, flags; 609*0Sstevel@tonic-gate dev_info_t *dip; 610*0Sstevel@tonic-gate char *nbuf; 611*0Sstevel@tonic-gate static const char *noname = "<none>"; 612*0Sstevel@tonic-gate 613*0Sstevel@tonic-gate ASSERT(pdip); 614*0Sstevel@tonic-gate ASSERT(DEVI_BUSY_OWNED(pdip)); 615*0Sstevel@tonic-gate 616*0Sstevel@tonic-gate flags = 0; 617*0Sstevel@tonic-gate 618*0Sstevel@tonic-gate /* 619*0Sstevel@tonic-gate * Creating the root of a branch ? 620*0Sstevel@tonic-gate */ 621*0Sstevel@tonic-gate if (rdipp) { 622*0Sstevel@tonic-gate *rdipp = NULL; 623*0Sstevel@tonic-gate flags = DEVI_BRANCH_ROOT; 624*0Sstevel@tonic-gate } 625*0Sstevel@tonic-gate 626*0Sstevel@tonic-gate ndi_devi_alloc_sleep(pdip, (char *)noname, DEVI_SID_NODEID, &dip); 627*0Sstevel@tonic-gate rv = bp->create.sid_branch_create(dip, bp->arg, flags); 628*0Sstevel@tonic-gate 629*0Sstevel@tonic-gate nbuf = kmem_alloc(OBP_MAXDRVNAME, KM_SLEEP); 630*0Sstevel@tonic-gate 631*0Sstevel@tonic-gate if (rv == DDI_WALK_ERROR) { 632*0Sstevel@tonic-gate cmn_err(CE_WARN, "e_ddi_branch_create: Error setting" 633*0Sstevel@tonic-gate " properties on devinfo node %p", (void *)dip); 634*0Sstevel@tonic-gate goto fail; 635*0Sstevel@tonic-gate } 636*0Sstevel@tonic-gate 637*0Sstevel@tonic-gate len = OBP_MAXDRVNAME; 638*0Sstevel@tonic-gate if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 639*0Sstevel@tonic-gate DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "name", nbuf, &len) 640*0Sstevel@tonic-gate != DDI_PROP_SUCCESS) { 641*0Sstevel@tonic-gate cmn_err(CE_WARN, "e_ddi_branch_create: devinfo node %p has" 642*0Sstevel@tonic-gate "no name property", (void *)dip); 643*0Sstevel@tonic-gate goto fail; 644*0Sstevel@tonic-gate } 645*0Sstevel@tonic-gate 646*0Sstevel@tonic-gate ASSERT(i_ddi_node_state(dip) == DS_PROTO); 647*0Sstevel@tonic-gate if (ndi_devi_set_nodename(dip, nbuf, 0) != NDI_SUCCESS) { 648*0Sstevel@tonic-gate cmn_err(CE_WARN, "e_ddi_branch_create: cannot set name (%s)" 649*0Sstevel@tonic-gate " for devinfo node %p", nbuf, (void *)dip); 650*0Sstevel@tonic-gate goto fail; 651*0Sstevel@tonic-gate } 652*0Sstevel@tonic-gate 653*0Sstevel@tonic-gate kmem_free(nbuf, OBP_MAXDRVNAME); 654*0Sstevel@tonic-gate 655*0Sstevel@tonic-gate /* 656*0Sstevel@tonic-gate * Ignore bind failures just like boot does 657*0Sstevel@tonic-gate */ 658*0Sstevel@tonic-gate (void) ndi_devi_bind_driver(dip, 0); 659*0Sstevel@tonic-gate 660*0Sstevel@tonic-gate switch (rv) { 661*0Sstevel@tonic-gate case DDI_WALK_CONTINUE: 662*0Sstevel@tonic-gate case DDI_WALK_PRUNESIB: 663*0Sstevel@tonic-gate ndi_devi_enter(dip, &circ); 664*0Sstevel@tonic-gate 665*0Sstevel@tonic-gate i = DDI_WALK_CONTINUE; 666*0Sstevel@tonic-gate for (; i == DDI_WALK_CONTINUE; ) { 667*0Sstevel@tonic-gate i = sid_node_create(dip, bp, NULL); 668*0Sstevel@tonic-gate } 669*0Sstevel@tonic-gate 670*0Sstevel@tonic-gate ASSERT(i == DDI_WALK_ERROR || i == DDI_WALK_PRUNESIB); 671*0Sstevel@tonic-gate if (i == DDI_WALK_ERROR) 672*0Sstevel@tonic-gate rv = i; 673*0Sstevel@tonic-gate /* 674*0Sstevel@tonic-gate * If PRUNESIB stop creating siblings 675*0Sstevel@tonic-gate * of dip's child. Subsequent walk behavior 676*0Sstevel@tonic-gate * is determined by rv returned by dip. 677*0Sstevel@tonic-gate */ 678*0Sstevel@tonic-gate 679*0Sstevel@tonic-gate ndi_devi_exit(dip, circ); 680*0Sstevel@tonic-gate break; 681*0Sstevel@tonic-gate case DDI_WALK_TERMINATE: 682*0Sstevel@tonic-gate /* 683*0Sstevel@tonic-gate * Don't create children and ask our parent 684*0Sstevel@tonic-gate * to not create siblings either. 685*0Sstevel@tonic-gate */ 686*0Sstevel@tonic-gate rv = DDI_WALK_PRUNESIB; 687*0Sstevel@tonic-gate break; 688*0Sstevel@tonic-gate case DDI_WALK_PRUNECHILD: 689*0Sstevel@tonic-gate /* 690*0Sstevel@tonic-gate * Don't create children, but ask parent to continue 691*0Sstevel@tonic-gate * with siblings. 692*0Sstevel@tonic-gate */ 693*0Sstevel@tonic-gate rv = DDI_WALK_CONTINUE; 694*0Sstevel@tonic-gate break; 695*0Sstevel@tonic-gate default: 696*0Sstevel@tonic-gate ASSERT(0); 697*0Sstevel@tonic-gate break; 698*0Sstevel@tonic-gate } 699*0Sstevel@tonic-gate 700*0Sstevel@tonic-gate if (rdipp) 701*0Sstevel@tonic-gate *rdipp = dip; 702*0Sstevel@tonic-gate 703*0Sstevel@tonic-gate /* 704*0Sstevel@tonic-gate * Set device offline - only the "configure" op should cause an attach 705*0Sstevel@tonic-gate */ 706*0Sstevel@tonic-gate (void) set_dip_offline(dip, NULL); 707*0Sstevel@tonic-gate 708*0Sstevel@tonic-gate return (rv); 709*0Sstevel@tonic-gate fail: 710*0Sstevel@tonic-gate (void) ndi_devi_free(dip); 711*0Sstevel@tonic-gate kmem_free(nbuf, OBP_MAXDRVNAME); 712*0Sstevel@tonic-gate return (DDI_WALK_ERROR); 713*0Sstevel@tonic-gate } 714*0Sstevel@tonic-gate 715*0Sstevel@tonic-gate static int 716*0Sstevel@tonic-gate create_sid_branch( 717*0Sstevel@tonic-gate dev_info_t *pdip, 718*0Sstevel@tonic-gate devi_branch_t *bp, 719*0Sstevel@tonic-gate dev_info_t **dipp, 720*0Sstevel@tonic-gate uint_t flags) 721*0Sstevel@tonic-gate { 722*0Sstevel@tonic-gate int rv = 0, state = DDI_WALK_CONTINUE; 723*0Sstevel@tonic-gate dev_info_t *rdip; 724*0Sstevel@tonic-gate 725*0Sstevel@tonic-gate while (state == DDI_WALK_CONTINUE) { 726*0Sstevel@tonic-gate int circ; 727*0Sstevel@tonic-gate 728*0Sstevel@tonic-gate ndi_devi_enter(pdip, &circ); 729*0Sstevel@tonic-gate 730*0Sstevel@tonic-gate state = sid_node_create(pdip, bp, &rdip); 731*0Sstevel@tonic-gate if (rdip == NULL) { 732*0Sstevel@tonic-gate ndi_devi_exit(pdip, circ); 733*0Sstevel@tonic-gate ASSERT(state == DDI_WALK_ERROR); 734*0Sstevel@tonic-gate break; 735*0Sstevel@tonic-gate } 736*0Sstevel@tonic-gate 737*0Sstevel@tonic-gate e_ddi_branch_hold(rdip); 738*0Sstevel@tonic-gate 739*0Sstevel@tonic-gate ndi_devi_exit(pdip, circ); 740*0Sstevel@tonic-gate 741*0Sstevel@tonic-gate if (flags & DEVI_BRANCH_CONFIGURE) { 742*0Sstevel@tonic-gate int error = e_ddi_branch_configure(rdip, dipp, 0); 743*0Sstevel@tonic-gate if (error && rv == 0) 744*0Sstevel@tonic-gate rv = error; 745*0Sstevel@tonic-gate } 746*0Sstevel@tonic-gate 747*0Sstevel@tonic-gate /* 748*0Sstevel@tonic-gate * devi_branch_callback() is optional 749*0Sstevel@tonic-gate */ 750*0Sstevel@tonic-gate if (bp->devi_branch_callback) 751*0Sstevel@tonic-gate bp->devi_branch_callback(rdip, bp->arg, 0); 752*0Sstevel@tonic-gate } 753*0Sstevel@tonic-gate 754*0Sstevel@tonic-gate ASSERT(state == DDI_WALK_ERROR || state == DDI_WALK_PRUNESIB); 755*0Sstevel@tonic-gate 756*0Sstevel@tonic-gate return (state == DDI_WALK_ERROR ? EIO : rv); 757*0Sstevel@tonic-gate } 758*0Sstevel@tonic-gate 759*0Sstevel@tonic-gate int 760*0Sstevel@tonic-gate e_ddi_branch_create( 761*0Sstevel@tonic-gate dev_info_t *pdip, 762*0Sstevel@tonic-gate devi_branch_t *bp, 763*0Sstevel@tonic-gate dev_info_t **dipp, 764*0Sstevel@tonic-gate uint_t flags) 765*0Sstevel@tonic-gate { 766*0Sstevel@tonic-gate int prom_devi, sid_devi, error; 767*0Sstevel@tonic-gate 768*0Sstevel@tonic-gate if (pdip == NULL || bp == NULL || bp->type == 0) 769*0Sstevel@tonic-gate return (EINVAL); 770*0Sstevel@tonic-gate 771*0Sstevel@tonic-gate prom_devi = (bp->type == DEVI_BRANCH_PROM) ? 1 : 0; 772*0Sstevel@tonic-gate sid_devi = (bp->type == DEVI_BRANCH_SID) ? 1 : 0; 773*0Sstevel@tonic-gate 774*0Sstevel@tonic-gate if (prom_devi && bp->create.prom_branch_select == NULL) 775*0Sstevel@tonic-gate return (EINVAL); 776*0Sstevel@tonic-gate else if (sid_devi && bp->create.sid_branch_create == NULL) 777*0Sstevel@tonic-gate return (EINVAL); 778*0Sstevel@tonic-gate else if (!prom_devi && !sid_devi) 779*0Sstevel@tonic-gate return (EINVAL); 780*0Sstevel@tonic-gate 781*0Sstevel@tonic-gate if (flags & DEVI_BRANCH_EVENT) 782*0Sstevel@tonic-gate return (EINVAL); 783*0Sstevel@tonic-gate 784*0Sstevel@tonic-gate if (prom_devi) { 785*0Sstevel@tonic-gate struct pta pta = {0}; 786*0Sstevel@tonic-gate 787*0Sstevel@tonic-gate pta.pdip = pdip; 788*0Sstevel@tonic-gate pta.bp = bp; 789*0Sstevel@tonic-gate pta.flags = flags; 790*0Sstevel@tonic-gate 791*0Sstevel@tonic-gate error = prom_tree_access(create_prom_branch, &pta, NULL); 792*0Sstevel@tonic-gate 793*0Sstevel@tonic-gate if (dipp) 794*0Sstevel@tonic-gate *dipp = pta.fdip; 795*0Sstevel@tonic-gate else if (pta.fdip) 796*0Sstevel@tonic-gate ndi_rele_devi(pta.fdip); 797*0Sstevel@tonic-gate } else { 798*0Sstevel@tonic-gate error = create_sid_branch(pdip, bp, dipp, flags); 799*0Sstevel@tonic-gate } 800*0Sstevel@tonic-gate 801*0Sstevel@tonic-gate return (error); 802*0Sstevel@tonic-gate } 803*0Sstevel@tonic-gate 804*0Sstevel@tonic-gate int 805*0Sstevel@tonic-gate e_ddi_branch_configure(dev_info_t *rdip, dev_info_t **dipp, uint_t flags) 806*0Sstevel@tonic-gate { 807*0Sstevel@tonic-gate int circ, rv; 808*0Sstevel@tonic-gate char *devnm; 809*0Sstevel@tonic-gate dev_info_t *pdip; 810*0Sstevel@tonic-gate 811*0Sstevel@tonic-gate if (dipp) 812*0Sstevel@tonic-gate *dipp = NULL; 813*0Sstevel@tonic-gate 814*0Sstevel@tonic-gate if (rdip == NULL || flags != 0 || (flags & DEVI_BRANCH_EVENT)) 815*0Sstevel@tonic-gate return (EINVAL); 816*0Sstevel@tonic-gate 817*0Sstevel@tonic-gate pdip = ddi_get_parent(rdip); 818*0Sstevel@tonic-gate 819*0Sstevel@tonic-gate ndi_devi_enter(pdip, &circ); 820*0Sstevel@tonic-gate 821*0Sstevel@tonic-gate if (!e_ddi_branch_held(rdip)) { 822*0Sstevel@tonic-gate ndi_devi_exit(pdip, circ); 823*0Sstevel@tonic-gate cmn_err(CE_WARN, "e_ddi_branch_configure: " 824*0Sstevel@tonic-gate "dip(%p) not held", (void *)rdip); 825*0Sstevel@tonic-gate return (EINVAL); 826*0Sstevel@tonic-gate } 827*0Sstevel@tonic-gate 828*0Sstevel@tonic-gate if (i_ddi_node_state(rdip) < DS_INITIALIZED) { 829*0Sstevel@tonic-gate /* 830*0Sstevel@tonic-gate * First attempt to bind a driver. If we fail, return 831*0Sstevel@tonic-gate * success (On some platforms, dips for some device 832*0Sstevel@tonic-gate * types (CPUs) may not have a driver) 833*0Sstevel@tonic-gate */ 834*0Sstevel@tonic-gate if (ndi_devi_bind_driver(rdip, 0) != NDI_SUCCESS) { 835*0Sstevel@tonic-gate ndi_devi_exit(pdip, circ); 836*0Sstevel@tonic-gate return (0); 837*0Sstevel@tonic-gate } 838*0Sstevel@tonic-gate 839*0Sstevel@tonic-gate if (ddi_initchild(pdip, rdip) != DDI_SUCCESS) { 840*0Sstevel@tonic-gate rv = NDI_FAILURE; 841*0Sstevel@tonic-gate goto out; 842*0Sstevel@tonic-gate } 843*0Sstevel@tonic-gate } 844*0Sstevel@tonic-gate 845*0Sstevel@tonic-gate ASSERT(i_ddi_node_state(rdip) >= DS_INITIALIZED); 846*0Sstevel@tonic-gate 847*0Sstevel@tonic-gate devnm = kmem_alloc(MAXNAMELEN + 1, KM_SLEEP); 848*0Sstevel@tonic-gate 849*0Sstevel@tonic-gate (void) ddi_deviname(rdip, devnm); 850*0Sstevel@tonic-gate 851*0Sstevel@tonic-gate if ((rv = ndi_devi_config_one(pdip, devnm+1, &rdip, 852*0Sstevel@tonic-gate NDI_DEVI_ONLINE | NDI_CONFIG)) == NDI_SUCCESS) { 853*0Sstevel@tonic-gate /* release hold from ndi_devi_config_one() */ 854*0Sstevel@tonic-gate ndi_rele_devi(rdip); 855*0Sstevel@tonic-gate } 856*0Sstevel@tonic-gate 857*0Sstevel@tonic-gate kmem_free(devnm, MAXNAMELEN + 1); 858*0Sstevel@tonic-gate out: 859*0Sstevel@tonic-gate if (rv != NDI_SUCCESS && dipp) { 860*0Sstevel@tonic-gate ndi_hold_devi(rdip); 861*0Sstevel@tonic-gate *dipp = rdip; 862*0Sstevel@tonic-gate } 863*0Sstevel@tonic-gate ndi_devi_exit(pdip, circ); 864*0Sstevel@tonic-gate return (ndi2errno(rv)); 865*0Sstevel@tonic-gate } 866*0Sstevel@tonic-gate 867*0Sstevel@tonic-gate void 868*0Sstevel@tonic-gate e_ddi_branch_hold(dev_info_t *rdip) 869*0Sstevel@tonic-gate { 870*0Sstevel@tonic-gate if (e_ddi_branch_held(rdip)) { 871*0Sstevel@tonic-gate cmn_err(CE_WARN, "e_ddi_branch_hold: branch already held"); 872*0Sstevel@tonic-gate return; 873*0Sstevel@tonic-gate } 874*0Sstevel@tonic-gate 875*0Sstevel@tonic-gate mutex_enter(&DEVI(rdip)->devi_lock); 876*0Sstevel@tonic-gate if ((DEVI(rdip)->devi_flags & DEVI_BRANCH_HELD) == 0) { 877*0Sstevel@tonic-gate DEVI(rdip)->devi_flags |= DEVI_BRANCH_HELD; 878*0Sstevel@tonic-gate DEVI(rdip)->devi_ref++; 879*0Sstevel@tonic-gate } 880*0Sstevel@tonic-gate ASSERT(DEVI(rdip)->devi_ref > 0); 881*0Sstevel@tonic-gate mutex_exit(&DEVI(rdip)->devi_lock); 882*0Sstevel@tonic-gate } 883*0Sstevel@tonic-gate 884*0Sstevel@tonic-gate int 885*0Sstevel@tonic-gate e_ddi_branch_held(dev_info_t *rdip) 886*0Sstevel@tonic-gate { 887*0Sstevel@tonic-gate int rv = 0; 888*0Sstevel@tonic-gate 889*0Sstevel@tonic-gate mutex_enter(&DEVI(rdip)->devi_lock); 890*0Sstevel@tonic-gate if ((DEVI(rdip)->devi_flags & DEVI_BRANCH_HELD) && 891*0Sstevel@tonic-gate DEVI(rdip)->devi_ref > 0) { 892*0Sstevel@tonic-gate rv = 1; 893*0Sstevel@tonic-gate } 894*0Sstevel@tonic-gate mutex_exit(&DEVI(rdip)->devi_lock); 895*0Sstevel@tonic-gate 896*0Sstevel@tonic-gate return (rv); 897*0Sstevel@tonic-gate } 898*0Sstevel@tonic-gate void 899*0Sstevel@tonic-gate e_ddi_branch_rele(dev_info_t *rdip) 900*0Sstevel@tonic-gate { 901*0Sstevel@tonic-gate mutex_enter(&DEVI(rdip)->devi_lock); 902*0Sstevel@tonic-gate DEVI(rdip)->devi_flags &= ~DEVI_BRANCH_HELD; 903*0Sstevel@tonic-gate DEVI(rdip)->devi_ref--; 904*0Sstevel@tonic-gate mutex_exit(&DEVI(rdip)->devi_lock); 905*0Sstevel@tonic-gate } 906*0Sstevel@tonic-gate 907*0Sstevel@tonic-gate int 908*0Sstevel@tonic-gate e_ddi_branch_unconfigure( 909*0Sstevel@tonic-gate dev_info_t *rdip, 910*0Sstevel@tonic-gate dev_info_t **dipp, 911*0Sstevel@tonic-gate uint_t flags) 912*0Sstevel@tonic-gate { 913*0Sstevel@tonic-gate int circ, rv; 914*0Sstevel@tonic-gate int destroy; 915*0Sstevel@tonic-gate char *devnm; 916*0Sstevel@tonic-gate uint_t nflags; 917*0Sstevel@tonic-gate dev_info_t *pdip; 918*0Sstevel@tonic-gate 919*0Sstevel@tonic-gate if (dipp) 920*0Sstevel@tonic-gate *dipp = NULL; 921*0Sstevel@tonic-gate 922*0Sstevel@tonic-gate if (rdip == NULL) 923*0Sstevel@tonic-gate return (EINVAL); 924*0Sstevel@tonic-gate 925*0Sstevel@tonic-gate pdip = ddi_get_parent(rdip); 926*0Sstevel@tonic-gate 927*0Sstevel@tonic-gate ASSERT(pdip); 928*0Sstevel@tonic-gate 929*0Sstevel@tonic-gate /* 930*0Sstevel@tonic-gate * Check if caller holds pdip busy - can cause deadlocks during 931*0Sstevel@tonic-gate * devfs_clean() 932*0Sstevel@tonic-gate */ 933*0Sstevel@tonic-gate if (DEVI_BUSY_OWNED(pdip)) { 934*0Sstevel@tonic-gate cmn_err(CE_WARN, "e_ddi_branch_unconfigure: failed: parent" 935*0Sstevel@tonic-gate " devinfo node(%p) is busy held", (void *)pdip); 936*0Sstevel@tonic-gate return (EINVAL); 937*0Sstevel@tonic-gate } 938*0Sstevel@tonic-gate 939*0Sstevel@tonic-gate destroy = (flags & DEVI_BRANCH_DESTROY) ? 1 : 0; 940*0Sstevel@tonic-gate 941*0Sstevel@tonic-gate devnm = kmem_alloc(MAXNAMELEN + 1, KM_SLEEP); 942*0Sstevel@tonic-gate 943*0Sstevel@tonic-gate ndi_devi_enter(pdip, &circ); 944*0Sstevel@tonic-gate (void) ddi_deviname(rdip, devnm); 945*0Sstevel@tonic-gate ndi_devi_exit(pdip, circ); 946*0Sstevel@tonic-gate 947*0Sstevel@tonic-gate /* 948*0Sstevel@tonic-gate * ddi_deviname() returns a component name with / prepended. 949*0Sstevel@tonic-gate */ 950*0Sstevel@tonic-gate rv = devfs_clean(pdip, devnm + 1, DV_CLEAN_FORCE); 951*0Sstevel@tonic-gate if (rv) { 952*0Sstevel@tonic-gate kmem_free(devnm, MAXNAMELEN + 1); 953*0Sstevel@tonic-gate return (rv); 954*0Sstevel@tonic-gate } 955*0Sstevel@tonic-gate 956*0Sstevel@tonic-gate ndi_devi_enter(pdip, &circ); 957*0Sstevel@tonic-gate 958*0Sstevel@tonic-gate /* 959*0Sstevel@tonic-gate * Recreate device name as it may have changed state (init/uninit) 960*0Sstevel@tonic-gate * when parent busy lock was dropped for devfs_clean() 961*0Sstevel@tonic-gate */ 962*0Sstevel@tonic-gate (void) ddi_deviname(rdip, devnm); 963*0Sstevel@tonic-gate 964*0Sstevel@tonic-gate if (!e_ddi_branch_held(rdip)) { 965*0Sstevel@tonic-gate kmem_free(devnm, MAXNAMELEN + 1); 966*0Sstevel@tonic-gate ndi_devi_exit(pdip, circ); 967*0Sstevel@tonic-gate cmn_err(CE_WARN, "e_ddi_%s_branch: dip(%p) not held", 968*0Sstevel@tonic-gate destroy ? "destroy" : "unconfigure", (void *)rdip); 969*0Sstevel@tonic-gate return (EINVAL); 970*0Sstevel@tonic-gate } 971*0Sstevel@tonic-gate 972*0Sstevel@tonic-gate /* 973*0Sstevel@tonic-gate * Release hold on the branch. This is ok since we are holding the 974*0Sstevel@tonic-gate * parent busy. If rdip is not removed, we must do a hold on the 975*0Sstevel@tonic-gate * branch before returning. 976*0Sstevel@tonic-gate */ 977*0Sstevel@tonic-gate e_ddi_branch_rele(rdip); 978*0Sstevel@tonic-gate 979*0Sstevel@tonic-gate nflags = NDI_DEVI_OFFLINE; 980*0Sstevel@tonic-gate if (destroy || (flags & DEVI_BRANCH_DESTROY)) { 981*0Sstevel@tonic-gate nflags |= NDI_DEVI_REMOVE; 982*0Sstevel@tonic-gate destroy = 1; 983*0Sstevel@tonic-gate } else { 984*0Sstevel@tonic-gate nflags |= NDI_UNCONFIG; /* uninit but don't remove */ 985*0Sstevel@tonic-gate } 986*0Sstevel@tonic-gate 987*0Sstevel@tonic-gate if (flags & DEVI_BRANCH_EVENT) 988*0Sstevel@tonic-gate nflags |= NDI_POST_EVENT; 989*0Sstevel@tonic-gate 990*0Sstevel@tonic-gate if (i_ddi_node_state(pdip) == DS_READY && 991*0Sstevel@tonic-gate i_ddi_node_state(rdip) >= DS_INITIALIZED) { 992*0Sstevel@tonic-gate rv = ndi_devi_unconfig_one(pdip, devnm+1, dipp, nflags); 993*0Sstevel@tonic-gate } else { 994*0Sstevel@tonic-gate rv = e_ddi_devi_unconfig(rdip, dipp, nflags); 995*0Sstevel@tonic-gate if (rv == NDI_SUCCESS) { 996*0Sstevel@tonic-gate ASSERT(!destroy || ddi_get_child(rdip) == NULL); 997*0Sstevel@tonic-gate rv = ndi_devi_offline(rdip, nflags); 998*0Sstevel@tonic-gate } 999*0Sstevel@tonic-gate } 1000*0Sstevel@tonic-gate 1001*0Sstevel@tonic-gate if (!destroy || rv != NDI_SUCCESS) { 1002*0Sstevel@tonic-gate /* The dip still exists, so do a hold */ 1003*0Sstevel@tonic-gate e_ddi_branch_hold(rdip); 1004*0Sstevel@tonic-gate } 1005*0Sstevel@tonic-gate out: 1006*0Sstevel@tonic-gate kmem_free(devnm, MAXNAMELEN + 1); 1007*0Sstevel@tonic-gate ndi_devi_exit(pdip, circ); 1008*0Sstevel@tonic-gate return (ndi2errno(rv)); 1009*0Sstevel@tonic-gate } 1010*0Sstevel@tonic-gate 1011*0Sstevel@tonic-gate int 1012*0Sstevel@tonic-gate e_ddi_branch_destroy(dev_info_t *rdip, dev_info_t **dipp, uint_t flag) 1013*0Sstevel@tonic-gate { 1014*0Sstevel@tonic-gate return (e_ddi_branch_unconfigure(rdip, dipp, 1015*0Sstevel@tonic-gate flag|DEVI_BRANCH_DESTROY)); 1016*0Sstevel@tonic-gate } 1017*0Sstevel@tonic-gate 1018*0Sstevel@tonic-gate /* 1019*0Sstevel@tonic-gate * Number of chains for hash table 1020*0Sstevel@tonic-gate */ 1021*0Sstevel@tonic-gate #define NUMCHAINS 17 1022*0Sstevel@tonic-gate 1023*0Sstevel@tonic-gate /* 1024*0Sstevel@tonic-gate * Devinfo busy arg 1025*0Sstevel@tonic-gate */ 1026*0Sstevel@tonic-gate struct devi_busy { 1027*0Sstevel@tonic-gate int dv_total; 1028*0Sstevel@tonic-gate int s_total; 1029*0Sstevel@tonic-gate mod_hash_t *dv_hash; 1030*0Sstevel@tonic-gate mod_hash_t *s_hash; 1031*0Sstevel@tonic-gate int (*callback)(dev_info_t *, void *, uint_t); 1032*0Sstevel@tonic-gate void *arg; 1033*0Sstevel@tonic-gate }; 1034*0Sstevel@tonic-gate 1035*0Sstevel@tonic-gate static int 1036*0Sstevel@tonic-gate visit_dip(dev_info_t *dip, void *arg) 1037*0Sstevel@tonic-gate { 1038*0Sstevel@tonic-gate uintptr_t sbusy, dvbusy, ref; 1039*0Sstevel@tonic-gate struct devi_busy *bsp = arg; 1040*0Sstevel@tonic-gate 1041*0Sstevel@tonic-gate ASSERT(bsp->callback); 1042*0Sstevel@tonic-gate 1043*0Sstevel@tonic-gate /* 1044*0Sstevel@tonic-gate * A dip cannot be busy if its reference count is 0 1045*0Sstevel@tonic-gate */ 1046*0Sstevel@tonic-gate if ((ref = e_ddi_devi_holdcnt(dip)) == 0) { 1047*0Sstevel@tonic-gate return (bsp->callback(dip, bsp->arg, 0)); 1048*0Sstevel@tonic-gate } 1049*0Sstevel@tonic-gate 1050*0Sstevel@tonic-gate if (mod_hash_find(bsp->dv_hash, dip, (mod_hash_val_t *)&dvbusy)) 1051*0Sstevel@tonic-gate dvbusy = 0; 1052*0Sstevel@tonic-gate 1053*0Sstevel@tonic-gate /* 1054*0Sstevel@tonic-gate * To catch device opens currently maintained on specfs common snodes. 1055*0Sstevel@tonic-gate */ 1056*0Sstevel@tonic-gate if (mod_hash_find(bsp->s_hash, dip, (mod_hash_val_t *)&sbusy)) 1057*0Sstevel@tonic-gate sbusy = 0; 1058*0Sstevel@tonic-gate 1059*0Sstevel@tonic-gate #ifdef DEBUG 1060*0Sstevel@tonic-gate if (ref < sbusy || ref < dvbusy) { 1061*0Sstevel@tonic-gate cmn_err(CE_WARN, "dip(%p): sopen = %lu, dvopen = %lu " 1062*0Sstevel@tonic-gate "dip ref = %lu\n", (void *)dip, sbusy, dvbusy, ref); 1063*0Sstevel@tonic-gate } 1064*0Sstevel@tonic-gate #endif 1065*0Sstevel@tonic-gate 1066*0Sstevel@tonic-gate dvbusy = (sbusy > dvbusy) ? sbusy : dvbusy; 1067*0Sstevel@tonic-gate 1068*0Sstevel@tonic-gate return (bsp->callback(dip, bsp->arg, dvbusy)); 1069*0Sstevel@tonic-gate } 1070*0Sstevel@tonic-gate 1071*0Sstevel@tonic-gate static int 1072*0Sstevel@tonic-gate visit_snode(struct snode *sp, void *arg) 1073*0Sstevel@tonic-gate { 1074*0Sstevel@tonic-gate uintptr_t sbusy; 1075*0Sstevel@tonic-gate dev_info_t *dip; 1076*0Sstevel@tonic-gate int count; 1077*0Sstevel@tonic-gate struct devi_busy *bsp = arg; 1078*0Sstevel@tonic-gate 1079*0Sstevel@tonic-gate ASSERT(sp); 1080*0Sstevel@tonic-gate 1081*0Sstevel@tonic-gate /* 1082*0Sstevel@tonic-gate * The stable lock is held. This prevents 1083*0Sstevel@tonic-gate * the snode and its associated dip from 1084*0Sstevel@tonic-gate * going away. 1085*0Sstevel@tonic-gate */ 1086*0Sstevel@tonic-gate dip = NULL; 1087*0Sstevel@tonic-gate count = spec_devi_open_count(sp, &dip); 1088*0Sstevel@tonic-gate 1089*0Sstevel@tonic-gate if (count <= 0) 1090*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 1091*0Sstevel@tonic-gate 1092*0Sstevel@tonic-gate ASSERT(dip); 1093*0Sstevel@tonic-gate 1094*0Sstevel@tonic-gate if (mod_hash_remove(bsp->s_hash, dip, (mod_hash_val_t *)&sbusy)) 1095*0Sstevel@tonic-gate sbusy = count; 1096*0Sstevel@tonic-gate else 1097*0Sstevel@tonic-gate sbusy += count; 1098*0Sstevel@tonic-gate 1099*0Sstevel@tonic-gate if (mod_hash_insert(bsp->s_hash, dip, (mod_hash_val_t)sbusy)) { 1100*0Sstevel@tonic-gate cmn_err(CE_WARN, "%s: s_hash insert failed: dip=0x%p, " 1101*0Sstevel@tonic-gate "sbusy = %lu", "e_ddi_branch_referenced", 1102*0Sstevel@tonic-gate (void *)dip, sbusy); 1103*0Sstevel@tonic-gate } 1104*0Sstevel@tonic-gate 1105*0Sstevel@tonic-gate bsp->s_total += count; 1106*0Sstevel@tonic-gate 1107*0Sstevel@tonic-gate return (DDI_WALK_CONTINUE); 1108*0Sstevel@tonic-gate } 1109*0Sstevel@tonic-gate 1110*0Sstevel@tonic-gate static void 1111*0Sstevel@tonic-gate visit_dvnode(struct dv_node *dv, void *arg) 1112*0Sstevel@tonic-gate { 1113*0Sstevel@tonic-gate uintptr_t dvbusy; 1114*0Sstevel@tonic-gate uint_t count; 1115*0Sstevel@tonic-gate struct vnode *vp; 1116*0Sstevel@tonic-gate struct devi_busy *bsp = arg; 1117*0Sstevel@tonic-gate 1118*0Sstevel@tonic-gate ASSERT(dv && dv->dv_devi); 1119*0Sstevel@tonic-gate 1120*0Sstevel@tonic-gate vp = DVTOV(dv); 1121*0Sstevel@tonic-gate 1122*0Sstevel@tonic-gate mutex_enter(&vp->v_lock); 1123*0Sstevel@tonic-gate count = vp->v_count; 1124*0Sstevel@tonic-gate mutex_exit(&vp->v_lock); 1125*0Sstevel@tonic-gate 1126*0Sstevel@tonic-gate if (!count) 1127*0Sstevel@tonic-gate return; 1128*0Sstevel@tonic-gate 1129*0Sstevel@tonic-gate if (mod_hash_remove(bsp->dv_hash, dv->dv_devi, 1130*0Sstevel@tonic-gate (mod_hash_val_t *)&dvbusy)) 1131*0Sstevel@tonic-gate dvbusy = count; 1132*0Sstevel@tonic-gate else 1133*0Sstevel@tonic-gate dvbusy += count; 1134*0Sstevel@tonic-gate 1135*0Sstevel@tonic-gate if (mod_hash_insert(bsp->dv_hash, dv->dv_devi, 1136*0Sstevel@tonic-gate (mod_hash_val_t)dvbusy)) { 1137*0Sstevel@tonic-gate cmn_err(CE_WARN, "%s: dv_hash insert failed: dip=0x%p, " 1138*0Sstevel@tonic-gate "dvbusy=%lu", "e_ddi_branch_referenced", 1139*0Sstevel@tonic-gate (void *)dv->dv_devi, dvbusy); 1140*0Sstevel@tonic-gate } 1141*0Sstevel@tonic-gate 1142*0Sstevel@tonic-gate bsp->dv_total += count; 1143*0Sstevel@tonic-gate } 1144*0Sstevel@tonic-gate 1145*0Sstevel@tonic-gate /* 1146*0Sstevel@tonic-gate * Returns reference count on success or -1 on failure. 1147*0Sstevel@tonic-gate */ 1148*0Sstevel@tonic-gate int 1149*0Sstevel@tonic-gate e_ddi_branch_referenced( 1150*0Sstevel@tonic-gate dev_info_t *rdip, 1151*0Sstevel@tonic-gate int (*callback)(dev_info_t *dip, void *arg, uint_t ref), 1152*0Sstevel@tonic-gate void *arg) 1153*0Sstevel@tonic-gate { 1154*0Sstevel@tonic-gate int circ; 1155*0Sstevel@tonic-gate char *path; 1156*0Sstevel@tonic-gate dev_info_t *pdip; 1157*0Sstevel@tonic-gate struct devi_busy bsa = {0}; 1158*0Sstevel@tonic-gate 1159*0Sstevel@tonic-gate ASSERT(rdip); 1160*0Sstevel@tonic-gate 1161*0Sstevel@tonic-gate path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 1162*0Sstevel@tonic-gate 1163*0Sstevel@tonic-gate ndi_hold_devi(rdip); 1164*0Sstevel@tonic-gate 1165*0Sstevel@tonic-gate pdip = ddi_get_parent(rdip); 1166*0Sstevel@tonic-gate 1167*0Sstevel@tonic-gate ASSERT(pdip); 1168*0Sstevel@tonic-gate 1169*0Sstevel@tonic-gate /* 1170*0Sstevel@tonic-gate * Check if caller holds pdip busy - can cause deadlocks during 1171*0Sstevel@tonic-gate * devfs_walk() 1172*0Sstevel@tonic-gate */ 1173*0Sstevel@tonic-gate if (!e_ddi_branch_held(rdip) || DEVI_BUSY_OWNED(pdip)) { 1174*0Sstevel@tonic-gate cmn_err(CE_WARN, "e_ddi_branch_referenced: failed: " 1175*0Sstevel@tonic-gate "devinfo branch(%p) not held or parent busy held", 1176*0Sstevel@tonic-gate (void *)rdip); 1177*0Sstevel@tonic-gate ndi_rele_devi(rdip); 1178*0Sstevel@tonic-gate kmem_free(path, MAXPATHLEN); 1179*0Sstevel@tonic-gate return (-1); 1180*0Sstevel@tonic-gate } 1181*0Sstevel@tonic-gate 1182*0Sstevel@tonic-gate ndi_devi_enter(pdip, &circ); 1183*0Sstevel@tonic-gate (void) ddi_pathname(rdip, path); 1184*0Sstevel@tonic-gate ndi_devi_exit(pdip, circ); 1185*0Sstevel@tonic-gate 1186*0Sstevel@tonic-gate bsa.dv_hash = mod_hash_create_ptrhash("dv_node busy hash", NUMCHAINS, 1187*0Sstevel@tonic-gate mod_hash_null_valdtor, sizeof (struct dev_info)); 1188*0Sstevel@tonic-gate 1189*0Sstevel@tonic-gate bsa.s_hash = mod_hash_create_ptrhash("snode busy hash", NUMCHAINS, 1190*0Sstevel@tonic-gate mod_hash_null_valdtor, sizeof (struct snode)); 1191*0Sstevel@tonic-gate 1192*0Sstevel@tonic-gate if (devfs_walk(path, visit_dvnode, &bsa)) { 1193*0Sstevel@tonic-gate cmn_err(CE_WARN, "e_ddi_branch_referenced: " 1194*0Sstevel@tonic-gate "devfs walk failed for: %s", path); 1195*0Sstevel@tonic-gate kmem_free(path, MAXPATHLEN); 1196*0Sstevel@tonic-gate bsa.s_total = bsa.dv_total = -1; 1197*0Sstevel@tonic-gate goto out; 1198*0Sstevel@tonic-gate } 1199*0Sstevel@tonic-gate 1200*0Sstevel@tonic-gate kmem_free(path, MAXPATHLEN); 1201*0Sstevel@tonic-gate 1202*0Sstevel@tonic-gate /* 1203*0Sstevel@tonic-gate * Walk the snode table to detect device opens, which are currently 1204*0Sstevel@tonic-gate * maintained on specfs common snodes. 1205*0Sstevel@tonic-gate */ 1206*0Sstevel@tonic-gate spec_snode_walk(visit_snode, &bsa); 1207*0Sstevel@tonic-gate 1208*0Sstevel@tonic-gate if (callback == NULL) 1209*0Sstevel@tonic-gate goto out; 1210*0Sstevel@tonic-gate 1211*0Sstevel@tonic-gate bsa.callback = callback; 1212*0Sstevel@tonic-gate bsa.arg = arg; 1213*0Sstevel@tonic-gate 1214*0Sstevel@tonic-gate if (visit_dip(rdip, &bsa) == DDI_WALK_CONTINUE) { 1215*0Sstevel@tonic-gate ndi_devi_enter(rdip, &circ); 1216*0Sstevel@tonic-gate ddi_walk_devs(ddi_get_child(rdip), visit_dip, &bsa); 1217*0Sstevel@tonic-gate ndi_devi_exit(rdip, circ); 1218*0Sstevel@tonic-gate } 1219*0Sstevel@tonic-gate 1220*0Sstevel@tonic-gate out: 1221*0Sstevel@tonic-gate ndi_rele_devi(rdip); 1222*0Sstevel@tonic-gate mod_hash_destroy_ptrhash(bsa.s_hash); 1223*0Sstevel@tonic-gate mod_hash_destroy_ptrhash(bsa.dv_hash); 1224*0Sstevel@tonic-gate return (bsa.s_total > bsa.dv_total ? bsa.s_total : bsa.dv_total); 1225*0Sstevel@tonic-gate } 1226