Home | History | Annotate | Download | only in io
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 #include <sys/types.h>
     27 #include <sys/conf.h>
     28 #include <sys/ddi.h>
     29 #include <sys/sunddi.h>
     30 #include <sys/modctl.h>
     31 #include <sys/sunndi.h>
     32 #include <sys/ddi_impldefs.h>
     33 #include <sys/obpdefs.h>
     34 #include <sys/cmn_err.h>
     35 #include <sys/errno.h>
     36 #include <sys/kmem.h>
     37 #include <sys/debug.h>
     38 #include <sys/sysmacros.h>
     39 #include <sys/autoconf.h>
     40 #include <sys/stat.h>
     41 #include <sys/serengeti.h>
     42 #include <sys/ssm.h>
     43 #include <sys/sgsbbc_mailbox.h>
     44 #include <sys/sgevents.h>
     45 #include <sys/sysevent.h>
     46 #include <sys/sysevent/dr.h>
     47 #include <sys/sysevent/eventdefs.h>
     48 #include <sys/ndi_impldefs.h>
     49 #include <sys/ddifm.h>
     50 #include <sys/ndifm.h>
     51 #include <sys/sbd_ioctl.h>
     52 
     53 /* Useful debugging Stuff */
     54 #include <sys/nexusdebug.h>
     55 
     56 /*
     57  * module ssm.c
     58  *
     59  * This module is a nexus driver designed to support the ssm nexus driver
     60  * and all children below it. This driver does not handle any of the
     61  * DDI functions passed up to it by the ssm driver, but instead allows
     62  * them to bubble up to the root node.
     63  */
     64 
     65 
     66 /*
     67  * Function prototypes
     68  */
     69 extern int plat_max_boards();
     70 
     71 static int
     72 ssm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result);
     73 
     74 static int
     75 ssm_attach(dev_info_t *, ddi_attach_cmd_t);
     76 
     77 static int
     78 ssm_detach(dev_info_t *, ddi_detach_cmd_t);
     79 
     80 static int
     81 ssm_open(dev_t *, int, int, cred_t *);
     82 
     83 static int
     84 ssm_close(dev_t, int, int, cred_t *);
     85 
     86 static int
     87 ssm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
     88 
     89 static int
     90 ssm_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, void *);
     91 
     92 static int
     93 ssm_make_nodes(dev_info_t *dip, int instance, int ssm_nodeid);
     94 
     95 static int
     96 ssm_generate_event(int node, int board, int hint);
     97 
     98 /*
     99  * FMA error callback
    100  * Register error handling callback with our parent. We will just call
    101  * our children's error callbacks and return their status.
    102  */
    103 static int
    104 ssm_err_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *impl_data);
    105 
    106 /*
    107  * fm_init busop to initialize our children
    108  */
    109 static int
    110 ssm_fm_init_child(dev_info_t *dip, dev_info_t *tdip, int cap,
    111 		ddi_iblock_cookie_t *ibc);
    112 
    113 /*
    114  * init/fini routines to alloc/dealloc fm structures and
    115  * register/unregister our callback.
    116  */
    117 static void
    118 ssm_fm_init(struct ssm_soft_state *softsp);
    119 
    120 static void
    121 ssm_fm_fini(struct ssm_soft_state *softsp);
    122 
    123 /*
    124  * DR event handlers
    125  * We want to register the event handlers once for all instances. In the
    126  * other hand we have register them after the sbbc has been attached.
    127  * event_initialize gives us the logic of only registering the events only
    128  * once
    129  */
    130 int event_initialized = 0;
    131 uint_t ssm_dr_event_handler(char *);
    132 
    133 /*
    134  * Event lock and state
    135  */
    136 static kmutex_t ssm_event_lock;
    137 int ssm_event_state;
    138 
    139 /*
    140  * DR event msg and payload
    141  */
    142 static sbbc_msg_t event_msg;
    143 static sg_system_fru_descriptor_t payload;
    144 
    145 struct ssm_node2inst {
    146 	int	nodeid;		/* serengeti node #, NOT prom nodeid */
    147 	int	inst;
    148 	struct ssm_node2inst *next;
    149 };
    150 static kmutex_t ssm_node2inst_lock;
    151 static struct ssm_node2inst ssm_node2inst_map = {-1, -1, NULL};
    152 
    153 
    154 /*
    155  * Configuration data structures
    156  */
    157 static struct bus_ops ssm_bus_ops = {
    158 	BUSO_REV,
    159 	ddi_bus_map,		/* map */
    160 	0,			/* get_intrspec */
    161 	0,			/* add_intrspec */
    162 	0,			/* remove_intrspec */
    163 	i_ddi_map_fault,	/* map_fault */
    164 	ddi_dma_map,		/* dma_map */
    165 	ddi_dma_allochdl,
    166 	ddi_dma_freehdl,
    167 	ddi_dma_bindhdl,
    168 	ddi_dma_unbindhdl,
    169 	ddi_dma_flush,
    170 	ddi_dma_win,
    171 	ddi_dma_mctl,		/* dma_ctl */
    172 	ssm_ctlops,		/* ctl */
    173 	ddi_bus_prop_op,	/* prop_op */
    174 	ndi_busop_get_eventcookie,
    175 	ndi_busop_add_eventcall,
    176 	ndi_busop_remove_eventcall,
    177 	ndi_post_event,
    178 	0,
    179 	0,
    180 	0,
    181 	ssm_fm_init_child,
    182 	NULL,
    183 	NULL,
    184 	NULL,
    185 	0,
    186 	i_ddi_intr_ops
    187 };
    188 
    189 static struct cb_ops ssm_cb_ops = {
    190 	ssm_open,			/* open */
    191 	ssm_close,			/* close */
    192 	nodev,				/* strategy */
    193 	nodev,				/* print */
    194 	nodev,				/* dump */
    195 	nodev,				/* read */
    196 	nodev,				/* write */
    197 	ssm_ioctl,			/* ioctl */
    198 	nodev,				/* devmap */
    199 	nodev,				/* mmap */
    200 	nodev,				/* segmap */
    201 	nochpoll,			/* poll */
    202 	ddi_prop_op,			/* cb_prop_op */
    203 	NULL,				/* streamtab */
    204 	D_NEW | D_MP | D_HOTPLUG,	/* Driver compatibility flag */
    205 	CB_REV,				/* rev */
    206 	nodev,				/* int (*cb_aread)() */
    207 	nodev				/* int (*cb_awrite)() */
    208 };
    209 
    210 static struct dev_ops ssm_ops = {
    211 	DEVO_REV,		/* devo_rev, */
    212 	0,			/* refcnt */
    213 	ssm_info,		/* getinfo */
    214 	nulldev,		/* identify */
    215 	nulldev,		/* probe */
    216 	ssm_attach,		/* attach */
    217 	ssm_detach,		/* detach */
    218 	nulldev,		/* reset */
    219 	&ssm_cb_ops,		/* driver operations */
    220 	&ssm_bus_ops,		/* bus_ops */
    221 	nulldev,		/* power */
    222 	ddi_quiesce_not_needed,		/* quiesce */
    223 };
    224 
    225 /*
    226  * Driver globals
    227  */
    228 static void *ssm_softstates;		/* ssm soft state hook */
    229 
    230 extern struct mod_ops mod_driverops;
    231 
    232 static struct modldrv modldrv = {
    233 	&mod_driverops,		/* Type of module.  This one is a driver */
    234 	"SSM Nexus",		/* name of module */
    235 	&ssm_ops,		/* driver ops */
    236 };
    237 
    238 static struct modlinkage modlinkage = {
    239 	MODREV_1,		/* rev */
    240 	(void *)&modldrv,
    241 	NULL
    242 };
    243 
    244 static int ssm_loaded_sbd = FALSE;
    245 kmutex_t ssm_lock;
    246 static int init_child(dev_info_t *child);
    247 
    248 /*
    249  * These are the module initialization routines.
    250  */
    251 
    252 int
    253 _init(void)
    254 {
    255 	int error;
    256 
    257 #if defined(DEBUG)
    258 	debug_print_level = 0x0;
    259 #endif
    260 
    261 	/* Initialize soft state pointer. */
    262 	if ((error = ddi_soft_state_init(&ssm_softstates,
    263 	    sizeof (struct ssm_soft_state), SSM_MAX_INSTANCES)) != 0)
    264 		return (error);
    265 
    266 	/* Install the module. */
    267 	error = mod_install(&modlinkage);
    268 	if (error != 0)
    269 		ddi_soft_state_fini(&ssm_softstates);
    270 
    271 	mutex_init(&ssm_lock, NULL, MUTEX_DRIVER, NULL);
    272 
    273 	return (error);
    274 }
    275 
    276 int
    277 _fini(void)
    278 {
    279 	int error;
    280 
    281 	/* Remove the module. */
    282 	if ((error = mod_remove(&modlinkage)) != 0)
    283 		return (error);
    284 
    285 	/*
    286 	 * Unregister the event handler
    287 	 */
    288 	(void) sbbc_mbox_unreg_intr(MBOX_EVENT_GENERIC, ssm_dr_event_handler);
    289 	mutex_destroy(&ssm_event_lock);
    290 
    291 	/* Free the soft state info. */
    292 	ddi_soft_state_fini(&ssm_softstates);
    293 	mutex_destroy(&ssm_lock);
    294 
    295 	return (0);
    296 }
    297 
    298 int
    299 _info(struct modinfo *modinfop)
    300 {
    301 	return (mod_info(&modlinkage, modinfop));
    302 }
    303 
    304 /* device driver entry points */
    305 
    306 /*
    307  * info entry point:
    308  */
    309 
    310 /* ARGSUSED */
    311 static int
    312 ssm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
    313 {
    314 	dev_t	dev;
    315 	int	instance;
    316 
    317 	if (infocmd == DDI_INFO_DEVT2INSTANCE) {
    318 		dev = (dev_t)arg;
    319 		instance = (getminor(dev) >> SSM_INSTANCE_SHIFT);
    320 		*result = (void *)(uintptr_t)instance;
    321 		return (DDI_SUCCESS);
    322 	}
    323 	return (DDI_FAILURE);
    324 }
    325 
    326 /*
    327  * attach entry point:
    328  */
    329 
    330 static int
    331 ssm_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
    332 {
    333 	int instance;
    334 	struct ssm_soft_state *softsp;
    335 	struct ssm_node2inst *prev, *sp, *tsp;
    336 
    337 	DPRINTF(SSM_ATTACH_DEBUG, ("ssm_attach\n"));
    338 
    339 	switch (cmd) {
    340 	case DDI_ATTACH:
    341 		break;
    342 
    343 	case DDI_RESUME:
    344 		return (DDI_SUCCESS);
    345 
    346 	default:
    347 		return (DDI_FAILURE);
    348 	}
    349 
    350 	instance = ddi_get_instance(devi);
    351 
    352 	if (ddi_soft_state_zalloc(ssm_softstates, instance) != DDI_SUCCESS)
    353 		return (DDI_FAILURE);
    354 
    355 	softsp = ddi_get_soft_state(ssm_softstates, instance);
    356 
    357 	/* Set the dip in the soft state */
    358 	softsp->dip = devi;
    359 	softsp->top_node = devi;
    360 	mutex_init(&softsp->ssm_sft_lock, NULL, MUTEX_DRIVER, NULL);
    361 
    362 	DPRINTF(SSM_ATTACH_DEBUG, ("ssm-%d: devi= 0x%p, softsp=0x%p\n",
    363 	    instance, (void *)devi, (void *)softsp));
    364 
    365 	if ((softsp->ssm_nodeid = (int)ddi_getprop(DDI_DEV_T_ANY, softsp->dip,
    366 	    DDI_PROP_DONTPASS, "nodeid", -1)) == -1) {
    367 		cmn_err(CE_WARN, "ssm%d: unable to retrieve %s property",
    368 		    instance, "nodeid");
    369 		ddi_soft_state_free(ssm_softstates, instance);
    370 		return (DDI_FAILURE);
    371 	}
    372 
    373 	/* nothing to suspend/resume here */
    374 	(void) ddi_prop_create(DDI_DEV_T_NONE, devi, DDI_PROP_CANSLEEP,
    375 	    "pm-hardware-state", (caddr_t)"no-suspend-resume",
    376 	    strlen("no-suspend-resume") + 1);
    377 
    378 #if DEBUG
    379 	if (ddi_create_minor_node(devi, "debug", S_IFCHR, instance,
    380 	    DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
    381 		ddi_soft_state_free(ssm_softstates, instance);
    382 		return (DDI_FAILURE);
    383 	}
    384 #endif
    385 
    386 	if (ssm_make_nodes(devi, instance, softsp->ssm_nodeid)) {
    387 		cmn_err(CE_WARN, "ssm:%s:%d: failed to make nodes",
    388 		    ddi_driver_name(devi), instance);
    389 		ddi_remove_minor_node(devi, NULL);
    390 		ddi_soft_state_free(ssm_softstates, instance);
    391 		return (DDI_FAILURE);
    392 	}
    393 	ssm_fm_init(softsp);
    394 	ddi_report_dev(devi);
    395 
    396 	if (event_initialized == 0) {
    397 		int rv;
    398 		/*
    399 		 * Register DR event handler
    400 		 */
    401 		mutex_init(&ssm_event_lock,  NULL, MUTEX_DRIVER, NULL);
    402 		event_msg.msg_buf = (caddr_t)&payload;
    403 		event_msg.msg_len = sizeof (payload);
    404 
    405 		rv = sbbc_mbox_reg_intr(MBOX_EVENT_GENERIC,
    406 		    ssm_dr_event_handler, &event_msg,
    407 		    (uint_t *)&ssm_event_state, &ssm_event_lock);
    408 
    409 		if (rv == EINVAL)
    410 		event_initialized = 1;
    411 	}
    412 
    413 	/*
    414 	 * Preallocate to avoid sleeping with ssm_node2inst_lock held -
    415 	 * low level interrupts use this mutex.
    416 	 */
    417 	tsp = kmem_zalloc(sizeof (struct ssm_node2inst), KM_SLEEP);
    418 
    419 	mutex_enter(&ssm_node2inst_lock);
    420 
    421 	for (prev = NULL, sp = &ssm_node2inst_map; sp != NULL;
    422 	    prev = sp, sp = sp->next) {
    423 		ASSERT(sp->inst != instance);
    424 		ASSERT(sp->nodeid != softsp->ssm_nodeid);
    425 		if (sp->inst == -1)
    426 			break;
    427 	}
    428 
    429 	if (sp == NULL) {
    430 		ASSERT(prev->next == NULL);
    431 		sp = prev->next = tsp;
    432 		tsp = NULL;
    433 		sp->next = NULL;
    434 	}
    435 
    436 	sp->inst = instance;
    437 	sp->nodeid = softsp->ssm_nodeid;
    438 
    439 	mutex_exit(&ssm_node2inst_lock);
    440 
    441 	if (tsp != NULL)
    442 		kmem_free(tsp, sizeof (struct ssm_node2inst));
    443 
    444 	return (DDI_SUCCESS);
    445 }
    446 
    447 /*
    448  * detach entry point:
    449  */
    450 /*ARGSUSED*/
    451 static int
    452 ssm_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
    453 {
    454 	int instance, rv;
    455 	int (*sbd_teardown_instance) (int, caddr_t);
    456 	ssm_sbdp_info_t	sbdp_info;
    457 	struct ssm_soft_state *softsp;
    458 	struct ssm_node2inst *prev, *sp;
    459 
    460 	instance = ddi_get_instance(devi);
    461 	softsp = ddi_get_soft_state(ssm_softstates, instance);
    462 
    463 	if (softsp == NULL) {
    464 		cmn_err(CE_WARN,
    465 		    "ssm_open bad instance number %d", instance);
    466 		return (ENXIO);
    467 	}
    468 
    469 	instance = ddi_get_instance(devi);
    470 
    471 	switch (cmd) {
    472 	case DDI_DETACH:
    473 		ddi_remove_minor_node(devi, NULL);
    474 
    475 		sbd_teardown_instance = (int (*) (int, caddr_t))
    476 		    modlookup("misc/sbd", "sbd_teardown_instance");
    477 
    478 		if (!sbd_teardown_instance) {
    479 			cmn_err(CE_WARN, "cannot find sbd_teardown_instance");
    480 			return (DDI_FAILURE);
    481 		}
    482 
    483 		sbdp_info.instance = instance;
    484 		sbdp_info.wnode = softsp->ssm_nodeid;
    485 		rv = (*sbd_teardown_instance)(instance, (caddr_t)&sbdp_info);
    486 
    487 		if (rv != DDI_SUCCESS) {
    488 			cmn_err(CE_WARN, "cannot run sbd_teardown_instance");
    489 			return (DDI_FAILURE);
    490 		}
    491 		ssm_fm_fini(softsp);
    492 		mutex_destroy(&softsp->ssm_sft_lock);
    493 		ddi_soft_state_free(ssm_softstates, instance);
    494 
    495 		mutex_enter(&ssm_node2inst_lock);
    496 		for (prev = NULL, sp = &ssm_node2inst_map; sp != NULL;
    497 		    prev = sp, sp = sp->next) {
    498 			/* Only the head of the list can persist if unused */
    499 			ASSERT(prev == NULL || sp->inst != -1);
    500 			if (sp->inst == instance)
    501 				break;
    502 		}
    503 		ASSERT(sp != NULL);
    504 
    505 		if (sp != &ssm_node2inst_map) {
    506 			prev->next = sp->next;
    507 			kmem_free(sp, sizeof (struct ssm_node2inst));
    508 		} else {
    509 			/*
    510 			 * Invalidate the head element, but retain the rest
    511 			 * of the list - "next" is still valid.
    512 			 */
    513 
    514 			sp->nodeid = -1;
    515 			sp->inst = -1;
    516 		}
    517 		mutex_exit(&ssm_node2inst_lock);
    518 
    519 		return (DDI_SUCCESS);
    520 
    521 	case DDI_SUSPEND:
    522 		return (DDI_SUCCESS);
    523 
    524 	default:
    525 		return (DDI_FAILURE);
    526 	}
    527 }
    528 
    529 extern void make_ddi_ppd(dev_info_t *, struct ddi_parent_private_data **);
    530 extern struct ddi_parent_private_data *init_regspec_64(dev_info_t *);
    531 
    532 static int
    533 name_child(dev_info_t *child, char *name, int namelen)
    534 {
    535 	struct regspec *rp;
    536 	struct ddi_parent_private_data *pdptr;
    537 	int portid = 0;
    538 	int regbase = -1;
    539 	extern uint_t root_phys_addr_lo_mask;
    540 
    541 	make_ddi_ppd(child, &pdptr);
    542 	ddi_set_parent_data(child, pdptr);
    543 
    544 	name[0] = '\0';
    545 	if (sparc_pd_getnreg(child) == 0)
    546 		return (DDI_SUCCESS);
    547 
    548 	rp = sparc_pd_getreg(child, 0);
    549 
    550 	portid = ddi_prop_get_int(DDI_DEV_T_ANY, child,
    551 	    DDI_PROP_DONTPASS, "portid", -1);
    552 	if (portid == -1) {
    553 		cmn_err(CE_WARN, "could not find portid property in %s",
    554 		    DEVI(child)->devi_node_name);
    555 	} else {
    556 		regbase = rp->regspec_addr & root_phys_addr_lo_mask;
    557 	}
    558 	(void) snprintf(name, namelen, "%x,%x", portid, regbase);
    559 	return (DDI_SUCCESS);
    560 }
    561 
    562 static int
    563 init_child(dev_info_t *child)
    564 {
    565 	char name[MAXNAMELEN];
    566 
    567 	(void) name_child(child, name, MAXNAMELEN);
    568 	ddi_set_name_addr(child, name);
    569 	if ((ndi_dev_is_persistent_node(child) == 0) &&
    570 	    (ndi_merge_node(child, name_child) == DDI_SUCCESS)) {
    571 		impl_ddi_sunbus_removechild(child);
    572 		return (DDI_FAILURE);
    573 	}
    574 
    575 	(void) init_regspec_64(child);
    576 	return (DDI_SUCCESS);
    577 }
    578 
    579 /*
    580  * Control ops entry point:
    581  *
    582  * Requests handled completely:
    583  *      DDI_CTLOPS_INITCHILD
    584  *      DDI_CTLOPS_UNINITCHILD
    585  * 	DDI_CTLOPS_REPORTDEV
    586  * All others are passed to the parent.
    587  * The name of the ssm node is ssm@nodeid,0.
    588  * ssm is the equivalent of rootnex.
    589  */
    590 static int
    591 ssm_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op, void *arg,
    592     void *result)
    593 {
    594 	int rval;
    595 
    596 	switch (op) {
    597 	case DDI_CTLOPS_INITCHILD: {
    598 		DPRINTF(SSM_CTLOPS_DEBUG, ("DDI_CTLOPS_INITCHILD\n"));
    599 		return (init_child((dev_info_t *)arg));
    600 	}
    601 
    602 	case DDI_CTLOPS_UNINITCHILD: {
    603 		DPRINTF(SSM_CTLOPS_DEBUG, ("DDI_CTLOPS_UNINITCHILD\n"));
    604 		impl_ddi_sunbus_removechild((dev_info_t *)arg);
    605 		return (DDI_SUCCESS);
    606 	}
    607 
    608 	case DDI_CTLOPS_REPORTDEV: {
    609 		char buf[80];
    610 		char *p = buf;
    611 		dev_info_t *parent;
    612 		int portid;
    613 
    614 		DPRINTF(SSM_CTLOPS_DEBUG, ("DDI_CTLOPS_REPORTDEV\n"));
    615 		parent = ddi_get_parent(rdip);
    616 
    617 		(void) sprintf(p, "%s%d at %s%d", DEVI(rdip)->devi_name,
    618 		    DEVI(rdip)->devi_instance, ddi_get_name(parent),
    619 		    ddi_get_instance(parent));
    620 		p += strlen(p);
    621 
    622 		/* Fetch Safari Extended Agent ID of this device. */
    623 		portid = (int)ddi_getprop(DDI_DEV_T_ANY, rdip,
    624 		    DDI_PROP_DONTPASS, "portid", -1);
    625 
    626 		/*
    627 		 * If this is one of the ssm children it will have
    628 		 * portid property and its parent will be ssm.
    629 		 * In this case report Node number and Safari id.
    630 		 */
    631 		if (portid != -1 &&
    632 		    strcmp("ssm", ddi_get_name(parent)) == 0) {
    633 			struct regspec *rp;
    634 			int node;
    635 			int safid;
    636 			int n;
    637 
    638 			rp = sparc_pd_getreg(rdip, 0);
    639 			n = sparc_pd_getnreg(rdip);
    640 			ASSERT(n > 0);
    641 
    642 			node  = SG_PORTID_TO_NODEID(portid);
    643 			safid = SG_PORTID_TO_SAFARI_ID(portid);
    644 
    645 			(void) strcpy(p, ": ");
    646 			p += strlen(p);
    647 
    648 			(void) sprintf(p, "Node %d Safari id %d 0x%x%s",
    649 			    node, safid,
    650 			    rp->regspec_addr,
    651 			    (n > 1 ? "" : " ..."));
    652 			p += strlen(p);
    653 		}
    654 
    655 		cmn_err(CE_CONT, "?%s\n", buf);
    656 		rval = DDI_SUCCESS;
    657 
    658 		break;
    659 	}
    660 
    661 	default:
    662 		rval = ddi_ctlops(dip, rdip, op, arg, result);
    663 
    664 		break;
    665 	}
    666 
    667 	return (rval);
    668 }
    669 
    670 /*ARGSUSED*/
    671 static int
    672 ssm_make_nodes(dev_info_t *dip, int instance, int ssm_nodeid)
    673 {
    674 	int		rv;
    675 	minor_t		minor_num, bd;
    676 	auto char	filename[20];
    677 
    678 	for (bd = 0; bd < plat_max_boards(); bd++) {
    679 		if (SG_BOARD_IS_CPU_TYPE(bd))
    680 			(void) sprintf(filename, "N%d.SB%d", ssm_nodeid, bd);
    681 		else
    682 			(void) sprintf(filename, "N%d.IB%d", ssm_nodeid, bd);
    683 
    684 		minor_num = (instance << SSM_INSTANCE_SHIFT) | bd;
    685 
    686 		rv = ddi_create_minor_node(dip, filename, S_IFCHR,
    687 		    minor_num, DDI_NT_SBD_ATTACHMENT_POINT, NULL);
    688 		if (rv == DDI_FAILURE) {
    689 			cmn_err(CE_WARN,
    690 			    "ssm_make_nodes:%d: failed to create "
    691 			    "minor node (%s, 0x%x)",
    692 			    instance, filename, minor_num);
    693 			return (-1);
    694 		}
    695 	}
    696 
    697 	return (0);
    698 }
    699 
    700 
    701 /* ARGSUSED */
    702 static int
    703 ssm_open(dev_t *devi, int flags, int otyp, cred_t *credp)
    704 {
    705 	struct ssm_soft_state *softsp;
    706 	minor_t board, instance;
    707 	int (*sbd_setup_instance)(int, dev_info_t *, int, int, caddr_t);
    708 	ssm_sbdp_info_t	sbdp_info;
    709 	int rv;
    710 
    711 	instance = (getminor(*devi) >> SSM_INSTANCE_SHIFT);
    712 
    713 	softsp = ddi_get_soft_state(ssm_softstates, instance);
    714 	if (softsp == NULL) {
    715 		cmn_err(CE_WARN, "ssm_open bad instance number %d", instance);
    716 		return (ENXIO);
    717 	}
    718 
    719 	board = (getminor(*devi) & SSM_BOARD_MASK);
    720 
    721 	if (board < 0 || board > plat_max_boards()) {
    722 		return (ENXIO);
    723 	}
    724 
    725 	mutex_enter(&ssm_lock);
    726 	if (instance == 0 && ssm_loaded_sbd == FALSE) {
    727 
    728 		if (modload("misc", "sbd") == -1) {
    729 			cmn_err(CE_WARN, "ssm_open: cannot load sbd");
    730 			mutex_exit(&ssm_lock);
    731 			return (EIO);
    732 		}
    733 		ssm_loaded_sbd = TRUE;
    734 	}
    735 	mutex_exit(&ssm_lock);
    736 
    737 	mutex_enter(&softsp->ssm_sft_lock);
    738 	if (softsp->initialized == FALSE) {
    739 
    740 		if (softsp->top_node == NULL) {
    741 			cmn_err(CE_WARN, "cannot find ssm top dnode");
    742 			mutex_exit(&softsp->ssm_sft_lock);
    743 			return (EIO);
    744 		}
    745 
    746 		sbd_setup_instance = (int (*)(int, dev_info_t *, int, int,
    747 		    caddr_t))modlookup("misc/sbd", "sbd_setup_instance");
    748 
    749 		if (!sbd_setup_instance) {
    750 			cmn_err(CE_WARN, "cannot find sbd_setup_instance");
    751 			mutex_exit(&softsp->ssm_sft_lock);
    752 			return (EIO);
    753 		}
    754 
    755 		sbdp_info.instance = instance;
    756 		sbdp_info.wnode = softsp->ssm_nodeid;
    757 
    758 		rv = (*sbd_setup_instance)(instance, softsp->top_node,
    759 		    plat_max_boards(), softsp->ssm_nodeid,
    760 		    (caddr_t)&sbdp_info);
    761 		if (rv != DDI_SUCCESS) {
    762 			cmn_err(CE_WARN, "cannot run sbd_setup_instance");
    763 			mutex_exit(&softsp->ssm_sft_lock);
    764 			return (EIO);
    765 		}
    766 		softsp->initialized = TRUE;
    767 	}
    768 	mutex_exit(&softsp->ssm_sft_lock);
    769 
    770 	return (DDI_SUCCESS);
    771 }
    772 
    773 
    774 /* ARGSUSED */
    775 static int
    776 ssm_close(dev_t dev, int flags, int otyp, cred_t *credp)
    777 {
    778 	struct ssm_soft_state *softsp;
    779 	minor_t board, instance;
    780 
    781 	instance = (getminor(dev) >> SSM_INSTANCE_SHIFT);
    782 
    783 	softsp = ddi_get_soft_state(ssm_softstates, instance);
    784 	if (softsp == NULL)
    785 		return (ENXIO);
    786 
    787 	board = (getminor(dev) & SSM_BOARD_MASK);
    788 
    789 	if (board < 0 || board > plat_max_boards())
    790 		return (ENXIO);
    791 
    792 	return (DDI_SUCCESS);
    793 }
    794 
    795 /* ARGSUSED */
    796 static int
    797 ssm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
    798 	int *rvalp)
    799 {
    800 	struct ssm_soft_state *softsp;
    801 	char *addr;
    802 	struct devctl_iocdata *dcp;
    803 	int instance, rv = 0;
    804 	int (*sbd_ioctl) (dev_t, int, intptr_t, int, char *);
    805 
    806 	instance = (getminor(dev) >> SSM_INSTANCE_SHIFT);
    807 	softsp = ddi_get_soft_state(ssm_softstates, instance);
    808 	if (softsp == NULL)
    809 		return (ENXIO);
    810 
    811 	switch (cmd) {
    812 
    813 	case DEVCTL_BUS_CONFIGURE:
    814 		/*
    815 		 * read devctl ioctl data
    816 		 */
    817 		if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)
    818 			return (EFAULT);
    819 
    820 		addr = ndi_dc_getaddr(dcp);
    821 		cmn_err(CE_NOTE,
    822 		    "DEVCTL_BUS_CONFIGURE: device id is %s\n", addr);
    823 		ndi_dc_freehdl(dcp);
    824 		break;
    825 
    826 	case DEVCTL_BUS_UNCONFIGURE:
    827 		if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)
    828 			return (EFAULT);
    829 
    830 		addr = ndi_dc_getaddr(dcp);
    831 		cmn_err(CE_NOTE,
    832 		    "DEVCTL_BUS_UNCONFIGURE: device id is %s\n", addr);
    833 		ndi_dc_freehdl(dcp);
    834 		break;
    835 
    836 #ifdef DEBUG
    837 	case SSM_TEARDOWN_SBD: {
    838 		ssm_sbdp_info_t	sbdp_info;
    839 		int (*sbd_teardown_instance) (int, caddr_t);
    840 		sbd_teardown_instance = (int (*) (int, caddr_t))
    841 		    modlookup("misc/sbd", "sbd_teardown_instance");
    842 
    843 		if (!sbd_teardown_instance) {
    844 			cmn_err(CE_WARN, "cannot find sbd_teardown_instance");
    845 			return (EFAULT);
    846 		}
    847 
    848 		sbdp_info.instance = instance;
    849 		sbdp_info.wnode = softsp->ssm_nodeid;
    850 		rv = (*sbd_teardown_instance)(instance, (caddr_t)&sbdp_info);
    851 		if (rv != DDI_SUCCESS) {
    852 			cmn_err(CE_WARN, "cannot run sbd_teardown_instance");
    853 			return (EFAULT);
    854 		}
    855 
    856 		ssm_loaded_sbd = FALSE;
    857 		softsp->initialized = FALSE;
    858 	}
    859 #endif
    860 
    861 
    862 	default: {
    863 		char	event = 0;
    864 
    865 		sbd_ioctl = (int (*) (dev_t, int, intptr_t, int, char *))
    866 		    modlookup("misc/sbd", "sbd_ioctl");
    867 
    868 		if (sbd_ioctl)
    869 			rv = (*sbd_ioctl) (dev, cmd, arg, mode, &event);
    870 		else {
    871 			cmn_err(CE_WARN, "cannot find sbd_ioctl");
    872 			return (ENXIO);
    873 		}
    874 		/*
    875 		 * Check to see if we need to send an event
    876 		 */
    877 		if (event == 1) {
    878 			int slot;
    879 			int hint = SE_NO_HINT;
    880 
    881 			if (rv == 0) {
    882 				if (cmd == SBD_CMD_CONNECT ||
    883 				    cmd == SBD_CMD_CONFIGURE)
    884 					hint = SE_HINT_INSERT;
    885 				else if (cmd == SBD_CMD_UNCONFIGURE ||
    886 				    cmd == SBD_CMD_DISCONNECT)
    887 					hint = SE_HINT_REMOVE;
    888 			}
    889 
    890 			slot = (getminor(dev) & SSM_BOARD_MASK);
    891 			(void) ssm_generate_event(softsp->ssm_nodeid, slot,
    892 			    hint);
    893 		}
    894 		break;
    895 	}
    896 	}
    897 
    898 	return (rv);
    899 }
    900 
    901 void
    902 ssm_get_attch_pnt(int node, int board, char *attach_pnt)
    903 {
    904 	struct ssm_node2inst	*sp;
    905 
    906 	/*
    907 	 * Hold this mutex, until we are done so that ssm dip
    908 	 * doesn't detach.
    909 	 */
    910 	mutex_enter(&ssm_node2inst_lock);
    911 
    912 	for (sp = &ssm_node2inst_map; sp != NULL; sp = sp->next) {
    913 		if (sp->inst == -1)
    914 			continue;
    915 		if (sp->nodeid == node)
    916 			break;
    917 	}
    918 
    919 	if (sp == NULL) {
    920 		/* We didn't find the ssm dip, return failure */
    921 		attach_pnt[0] = '\0';
    922 		mutex_exit(&ssm_node2inst_lock);
    923 		return;
    924 	}
    925 
    926 	/*
    927 	 * we have the instance, and the board, construct the attch pnt
    928 	 */
    929 	if (SG_BOARD_IS_CPU_TYPE(board))
    930 		(void) sprintf(attach_pnt, "ssm%d:N%d.SB%d",
    931 		    sp->inst, node, board);
    932 	else
    933 		(void) sprintf(attach_pnt, "ssm%d:N%d.IB%d",
    934 		    sp->inst, node, board);
    935 
    936 	mutex_exit(&ssm_node2inst_lock);
    937 }
    938 
    939 /*
    940  * Generate an event to sysevent
    941  */
    942 static int
    943 ssm_generate_event(int node, int board, int hint)
    944 {
    945 	sysevent_t			*ev;
    946 	sysevent_id_t			eid;
    947 	int				rv = 0;
    948 	sysevent_value_t		evnt_val;
    949 	sysevent_attr_list_t		*evnt_attr_list = NULL;
    950 	char				attach_pnt[MAXPATHLEN];
    951 
    952 
    953 	attach_pnt[0] = '\0';
    954 	ssm_get_attch_pnt(node, board, attach_pnt);
    955 
    956 	if (attach_pnt[0] == '\0')
    957 		return (-1);
    958 
    959 	ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE, EP_DDI,
    960 	    KM_SLEEP);
    961 	evnt_val.value_type = SE_DATA_TYPE_STRING;
    962 	evnt_val.value.sv_string = attach_pnt;
    963 
    964 	rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID, &evnt_val, KM_SLEEP);
    965 	if (rv != 0) {
    966 		cmn_err(CE_WARN, "Failed to add attr [%s] for %s event",
    967 		    DR_AP_ID, EC_DR);
    968 		sysevent_free(ev);
    969 		return (rv);
    970 	}
    971 
    972 	/*
    973 	 * Add the hint
    974 	 */
    975 	evnt_val.value_type = SE_DATA_TYPE_STRING;
    976 	evnt_val.value.sv_string = SE_HINT2STR(hint);
    977 
    978 	rv = sysevent_add_attr(&evnt_attr_list, DR_HINT, &evnt_val, KM_SLEEP);
    979 	if (rv != 0) {
    980 		cmn_err(CE_WARN, "Failed to add attr [%s] for %s event",
    981 		    DR_HINT, EC_DR);
    982 		sysevent_free_attr(evnt_attr_list);
    983 		sysevent_free(ev);
    984 		return (-1);
    985 	}
    986 
    987 	if (sysevent_attach_attributes(ev, evnt_attr_list) != 0) {
    988 		cmn_err(CE_WARN, "Failed to attach attr list for %s event",
    989 		    EC_DR);
    990 		sysevent_free_attr(evnt_attr_list);
    991 		sysevent_free(ev);
    992 		return (-1);
    993 	}
    994 
    995 	rv = log_sysevent(ev, KM_NOSLEEP, &eid);
    996 	if (rv != 0) {
    997 		cmn_err(CE_WARN, "ssm_dr_event_handler: failed to log event");
    998 	}
    999 
   1000 	sysevent_free(ev);
   1001 
   1002 	return (rv);
   1003 }
   1004 
   1005 /*
   1006  * DR Event Handler
   1007  */
   1008 uint_t
   1009 ssm_dr_event_handler(char *arg)
   1010 {
   1011 	sg_system_fru_descriptor_t	*fdp;
   1012 	int				hint;
   1013 
   1014 
   1015 	fdp = (sg_system_fru_descriptor_t *)(((sbbc_msg_t *)arg)->msg_buf);
   1016 	if (fdp == NULL) {
   1017 		DPRINTF(SSM_EVENT_DEBUG,
   1018 		    ("ssm_dr_event_handler: ARG is null\n"));
   1019 		return (DDI_INTR_CLAIMED);
   1020 	}
   1021 #ifdef DEBUG
   1022 	DPRINTF(SSM_EVENT_DEBUG, ("ssm_dr_event_handler called\n"));
   1023 	DPRINTF(SSM_EVENT_DEBUG, ("\tnode\t%d\n", fdp->node));
   1024 	DPRINTF(SSM_EVENT_DEBUG, ("\tslot\t%d\n", fdp->slot));
   1025 	DPRINTF(SSM_EVENT_DEBUG, ("\tparent_hdl\t0x%lx\n", fdp->parent_hdl));
   1026 	DPRINTF(SSM_EVENT_DEBUG, ("\tchild_hdl\t0x%lx\n", fdp->child_hdl));
   1027 	DPRINTF(SSM_EVENT_DEBUG, ("\tevent_details\t%s\n",
   1028 	    EVNT2STR(fdp->event_details)));
   1029 #endif
   1030 
   1031 	switch (fdp->event_details) {
   1032 	case SG_EVT_BOARD_ABSENT:
   1033 		hint = SE_HINT_REMOVE;
   1034 		break;
   1035 	case SG_EVT_BOARD_PRESENT:
   1036 		hint = SE_HINT_INSERT;
   1037 		break;
   1038 	default:
   1039 		hint = SE_NO_HINT;
   1040 		break;
   1041 
   1042 	}
   1043 
   1044 	(void) ssm_generate_event(fdp->node, fdp->slot, hint);
   1045 
   1046 	return (DDI_INTR_CLAIMED);
   1047 }
   1048 
   1049 /*
   1050  * Initialize our FMA resources
   1051  */
   1052 static void
   1053 ssm_fm_init(struct ssm_soft_state *softsp)
   1054 {
   1055 	softsp->ssm_fm_cap = DDI_FM_EREPORT_CAPABLE | DDI_FM_ERRCB_CAPABLE |
   1056 	    DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE;
   1057 
   1058 	/*
   1059 	 * Request or capability level and get our parents capability
   1060 	 * and ibc.
   1061 	 */
   1062 	ddi_fm_init(softsp->dip, &softsp->ssm_fm_cap, &softsp->ssm_fm_ibc);
   1063 	ASSERT((softsp->ssm_fm_cap & DDI_FM_EREPORT_CAPABLE) &&
   1064 	    (softsp->ssm_fm_cap & DDI_FM_ERRCB_CAPABLE));
   1065 	/*
   1066 	 * Register error callback with our parent.
   1067 	 */
   1068 	ddi_fm_handler_register(softsp->dip, ssm_err_callback, NULL);
   1069 }
   1070 
   1071 /*
   1072  * Breakdown our FMA resources
   1073  */
   1074 static void
   1075 ssm_fm_fini(struct ssm_soft_state *softsp)
   1076 {
   1077 	/*
   1078 	 * Clean up allocated fm structures
   1079 	 */
   1080 	ASSERT(softsp->ssm_fm_cap & DDI_FM_EREPORT_CAPABLE);
   1081 	ddi_fm_handler_unregister(softsp->dip);
   1082 	ddi_fm_fini(softsp->dip);
   1083 }
   1084 
   1085 /*
   1086  * Initialize FMA resources for children devices. Called when
   1087  * child calls ddi_fm_init().
   1088  */
   1089 /*ARGSUSED*/
   1090 static int
   1091 ssm_fm_init_child(dev_info_t *dip, dev_info_t *tdip, int cap,
   1092 		ddi_iblock_cookie_t *ibc)
   1093 {
   1094 	struct ssm_soft_state *softsp = ddi_get_soft_state(ssm_softstates,
   1095 	    ddi_get_instance(dip));
   1096 
   1097 	*ibc = softsp->ssm_fm_ibc;
   1098 	return (softsp->ssm_fm_cap);
   1099 }
   1100 
   1101 /*
   1102  * FMA registered error callback
   1103  */
   1104 /*ARGSUSED*/
   1105 static int
   1106 ssm_err_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *impl_data)
   1107 {
   1108 	/* Call our children error handlers */
   1109 	return (ndi_fm_handler_dispatch(dip, NULL, derr));
   1110 }
   1111