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