xref: /illumos-gate/usr/src/uts/sun4u/serengeti/io/sbdp.c (revision dd51520e127b452179a2ce4ea3bd8dee949f9afe)
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 #include <sys/types.h>
28 #include <sys/cmn_err.h>
29 #include <sys/conf.h>
30 #include <sys/ddi_impldefs.h>
31 #include <sys/autoconf.h>
32 #include <sys/systm.h>
33 #include <sys/modctl.h>
34 #include <sys/ddi.h>
35 #include <sys/sunddi.h>
36 #include <sys/sunndi.h>
37 #include <sys/ndi_impldefs.h>
38 #include <sys/promif.h>
39 #include <sys/stat.h>
40 #include <sys/kmem.h>
41 #include <sys/promif.h>
42 #include <sys/conf.h>
43 #include <sys/obpdefs.h>
44 #include <sys/cpuvar.h>
45 #include <vm/seg_kmem.h>
46 #include <sys/prom_plat.h>
47 #include <sys/machsystm.h>
48 #include <sys/note.h>
49 #include <sys/memlist.h>
50 #include <sys/ssm.h>
51 
52 #include <sys/sbd_ioctl.h>
53 #include <sys/sbd.h>
54 #include <sys/sbdp_priv.h>
55 #include <sys/sbdp_mem.h>
56 #include <sys/sbdp_error.h>
57 #include <sys/serengeti.h>
58 
59 #include <sys/sgsbbc.h>		/* To get fn_t type definition */
60 
61 /*
62  * Config information
63  */
64 #ifdef DEBUG
65 uint_t sbdp_debug = 0x0;
66 #endif /* DEBUG */
67 
68 /*
69  * Enable or disable dr
70  */
71 int sbdp_dr_available = 1;
72 
73 /* name properties for some Serengeti device nodes */
74 #define	CMP_DEVNAME		"cmp"
75 #define	MEM_DEVNAME		"memory"
76 #define	CPU_DEVNAME		"cpu"
77 #define	IO_PCI_DEVNAME		"pci"
78 #define	IO_SGHSC_DEVNAME	"sghsc"
79 #define	IO_WCI_DEVNAME		"wci"
80 
81 static	sbd_devattr_t	sbdp_devattr[] = {
82 	{ CMP_DEVNAME,		"cmp",			SBD_COMP_CMP },
83 	{ MEM_DEVNAME,		"memory-controller",	SBD_COMP_MEM },
84 	{ CPU_DEVNAME,		"cpu",			SBD_COMP_CPU },
85 	{ IO_PCI_DEVNAME,	"pci",			SBD_COMP_IO },
86 	{ IO_SGHSC_DEVNAME,	"sghsc",		SBD_COMP_IO },
87 	{ IO_WCI_DEVNAME,	"wci",			SBD_COMP_IO },
88 	/* last item must be blank */
89 	{ NULL,			NULL,			SBD_COMP_UNKNOWN }
90 };
91 
92 /*
93  * In the case of a busy mbox, if a status cmd comes in we return a cached
94  * copy.  This cache is a link list of wnodes that contains bd structs with
95  * the appropriate info.  When a new wnode is created a whole entry is added
96  * to the list.
97  */
98 sbdp_wnode_t	*first_node = NULL; /* first wnode. Entry to the link list */
99 int		cur_num_wnodes = 0; /* how many nodes are currently running */
100 
101 /* Macros to access fields in the previous array */
102 #define	SBDP_CT(i)		sbdp_devattr[i].s_dnodetype
103 #define	SBDP_DEVNAME(i)		sbdp_devattr[(i)].s_devname
104 #define	SBDP_OTYPE(i)		sbdp_devattr[(i)].s_obp_type
105 
106 /*
107  * Prototypes
108  */
109 sbdp_wnode_t *sbdp_get_wnodep(int);
110 
111 /*
112  * Module linkage information for the kernel.
113  */
114 
115 static struct modlmisc modlmisc = {
116 	&mod_miscops,
117 	"Serengeti sbdp",
118 };
119 
120 static struct modlinkage modlinkage = {
121 	MODREV_1,
122 	(void *)&modlmisc,
123 	NULL
124 };
125 
126 /*
127  * VA area used during CPU shutdown.
128  */
129 caddr_t	sbdp_shutdown_va;
130 
131 /*
132  * Mutex to protect our inventory
133  */
134 kmutex_t	sbdp_wnode_mutex;
135 
136 int
137 _init(void)
138 {
139 	int e;
140 
141 	e = mod_install(&modlinkage);
142 	if (e != 0)
143 		return (e);
144 
145 	sbdp_shutdown_va = vmem_alloc(heap_arena, PAGESIZE, VM_SLEEP);
146 	ASSERT(sbdp_shutdown_va != NULL);
147 	sbdp_valp = (uint64_t *)vmem_alloc(static_alloc_arena,
148 	    sizeof (uint64_t), VM_SLEEP);
149 
150 	mutex_init(&sbdp_wnode_mutex, NULL, MUTEX_DRIVER, NULL);
151 	return (e);
152 }
153 
154 int
155 _fini(void)
156 {
157 	int e;
158 
159 	/*
160 	 * Remove the module.
161 	 */
162 	e = mod_remove(&modlinkage);
163 	if (e != 0)
164 		return (e);
165 
166 	vmem_free(heap_arena, sbdp_shutdown_va, PAGESIZE);
167 	sbdp_shutdown_va = NULL;
168 	vmem_free(static_alloc_arena, (void *)sbdp_valp, sizeof (uint64_t));
169 	sbdp_valp = NULL;
170 
171 	mutex_destroy(&sbdp_wnode_mutex);
172 	return (e);
173 }
174 
175 int
176 _info(struct modinfo *modinfop)
177 {
178 	return (mod_info(&modlinkage, modinfop));
179 }
180 
181 int
182 sbdp_get_bd_and_wnode_num(pnode_t nodeid, int *bd, int *wnode)
183 {
184 	int portid;
185 	static fn_t	f = "sbdp_get_bd_and_wnode_num";
186 	extern int	get_portid(pnode_t node, pnode_t *cmpp);
187 
188 	SBDP_DBG_FUNC("%s\n", f);
189 
190 	if (sbdp_is_node_bad(nodeid))
191 		return (-1);
192 
193 	if ((portid = get_portid(nodeid, NULL)) == -1)
194 		return (-1);
195 
196 	/*
197 	 * decode the board number
198 	 */
199 	*bd = SG_PORTID_TO_BOARD_NUM(portid);
200 	*wnode = SG_PORTID_TO_NODEID(portid);
201 
202 	return (0);
203 }
204 
205 int
206 sbdp_get_board_num(sbdp_handle_t *hp, dev_info_t *dip)
207 {
208 	_NOTE(ARGUNUSED(hp))
209 
210 	pnode_t		nodeid;
211 	int		bd, wnode;
212 	static fn_t	f = "sbdp_get_board_num";
213 
214 	SBDP_DBG_FUNC("%s\n", f);
215 
216 	if (dip == NULL)
217 		return (-1);
218 
219 	nodeid = ddi_get_nodeid(dip);
220 
221 	/*
222 	 * Portid has encoded the nodeid and the agent id.  The top
223 	 * 4 bits are correspond to the wcnodeid and the lower 5 are the
224 	 * agent id.
225 	 * Each agent id represents a physical location hence we can
226 	 * obtain the board number
227 	 */
228 	if (sbdp_get_bd_and_wnode_num(nodeid, &bd, &wnode) < 0)
229 		return (-1);
230 
231 	return (bd);
232 }
233 
234 
235 sbd_devattr_t *
236 sbdp_get_devattr(void)
237 {
238 	return (&sbdp_devattr[0]);
239 }
240 
241 int
242 sbdp_portid_to_cpu_unit(int cmp, int core)
243 {
244 	return (SG_PORTID_TO_CPU_UNIT(cmp, core));
245 }
246 
247 int
248 sbdp_get_unit_num(sbdp_handle_t *hp, dev_info_t *dip)
249 {
250 	int		unit = -1;
251 	int		portid;
252 	processorid_t	cpuid;
253 	sbd_comp_type_t	type;
254 	char		dev_type[OBP_MAXPROPNAME];
255 	int		i;
256 	pnode_t		nodeid;
257 	static fn_t	f = "sbdp_get_unit_num";
258 
259 	SBDP_DBG_FUNC("%s\n", f);
260 
261 	if (dip == NULL)
262 		return (-1);
263 
264 	nodeid = ddi_get_nodeid(dip);
265 
266 	if (sbdp_is_node_bad(nodeid))
267 		return (-1);
268 
269 	if (prom_getprop(nodeid, "device_type", (caddr_t)dev_type) < 0) {
270 		SBDP_DBG_MISC("%s: couldn't get device_type\n", f);
271 		return (-1);
272 	}
273 
274 	for (i = 0; SBDP_CT(i) != SBD_COMP_UNKNOWN; i++) {
275 		if (strcmp(dev_type, SBDP_OTYPE(i)) != 0)
276 			continue;
277 		type = SBDP_CT(i);
278 	}
279 
280 	switch (type) {
281 	case SBD_COMP_CPU:
282 		if ((cpuid = sbdp_get_cpuid(hp, dip)) != -1) {
283 			unit = SG_CPUID_TO_CPU_UNIT(cpuid);
284 		}
285 		break;
286 	case SBD_COMP_MEM:
287 		unit = 0;
288 		break;
289 	case SBD_COMP_IO: {
290 		regspace_t	regs[3];
291 		int		len = 0;
292 
293 		/*
294 		 * Check to see if this is a cpci node
295 		 * cpci nodes are assign unit nums of 5 for now
296 		 * So they don't conflict with the pci unit nums
297 		 */
298 
299 		if (strcmp(dev_type, "sghsc") == 0) {
300 			SBDP_DBG_MISC("it is a sghsc\n");
301 			return (4);
302 		}
303 
304 		if (prom_getprop(nodeid, "portid", (caddr_t)&portid) <= 0) {
305 			SBDP_DBG_MISC("%s: couldn't get portid\n", f);
306 			return (-1);
307 		}
308 
309 		len = prom_getproplen(nodeid, "reg");
310 		if (len <= 0) {
311 			SBDP_DBG_MISC("%s: couldn't get length\n", f);
312 			return (-1);
313 		}
314 
315 		if (prom_getprop(nodeid, "reg", (caddr_t)regs) < 0) {
316 			SBDP_DBG_MISC("%s: couldn't get registers\n", f);
317 			return (-1);
318 		}
319 
320 		if ((portid % 2) != 0)
321 			if ((regs[0].regspec_addr_lo & 0x700000) ==
322 			    0x700000)
323 				unit = 0;
324 			else
325 				unit = 1;
326 		else
327 			if ((regs[0].regspec_addr_lo & 0x700000) ==
328 			    0x700000)
329 				unit = 2;
330 			else
331 				unit = 3;
332 
333 		SBDP_DBG_MISC("unit is %d\n", unit);
334 		break;
335 	}
336 	default:
337 		break;
338 
339 	}
340 
341 	return (unit);
342 }
343 
344 struct sbdp_mem_dip {
345 	sbdp_bd_t	*bdp;
346 	dev_info_t	*dip;
347 };
348 
349 static int
350 sbdp_get_mem_dip(pnode_t node, void *arg, uint_t flags)
351 {
352 	_NOTE(ARGUNUSED(flags))
353 
354 	struct sbdp_mem_dip	*smdp = (struct sbdp_mem_dip *)arg;
355 	mem_op_t	mem = {0};
356 
357 	if (node == OBP_NONODE || node == OBP_BADNODE)
358 		return (DDI_FAILURE);
359 
360 	mem.nodes = smdp->bdp->nodes;
361 	mem.board = smdp->bdp->bd;
362 	mem.nmem  = smdp->bdp->nnum;
363 
364 	(void) sbdp_is_mem(node, &mem);
365 
366 	/*
367 	 * We need to find the dip only for the first nodeid
368 	 */
369 	if (smdp->bdp->nnum == 0 && mem.nmem == 1) {
370 		ASSERT(smdp->dip == NULL);
371 		smdp->dip = e_ddi_nodeid_to_dip(node);
372 	}
373 
374 	smdp->bdp->nnum = mem.nmem;
375 
376 	return (DDI_SUCCESS);
377 }
378 
379 
380 /*
381  * Update the board info.  Required after a copy rename
382  */
383 void
384 sbdp_update_bd_info(sbdp_bd_t *bdp)
385 {
386 	attach_pkt_t		apkt, *apktp = &apkt;
387 	struct sbdp_mem_dip	smd = {0};
388 	static fn_t	f = "sbdp_update_bd_info";
389 
390 	SBDP_DBG_FUNC("%s\n", f);
391 
392 	if (bdp == NULL) {
393 		return;
394 	}
395 	/*
396 	 * Grab the lock
397 	 */
398 	mutex_enter(&bdp->bd_mutex);
399 
400 	/*
401 	 * we get the top nodes here.  This will have a side effect of
402 	 * updating the present bit for cpus
403 	 */
404 	apktp->node = bdp->wnode;
405 	apktp->board = bdp->bd;
406 	apktp->num_of_nodes = 0;
407 	apktp->flags = 0;
408 	sbdp_walk_prom_tree(prom_rootnode(), sbdp_select_top_nodes,
409 	    (void *) apktp);
410 
411 	/*
412 	 * We need to clear nnum since we are looking again for the
413 	 * nodes
414 	 */
415 	bdp->nnum = 0;
416 	smd.bdp = bdp;
417 
418 	/*
419 	 * If a dip is found by sbdp_get_mem_dip(), it will be
420 	 * returned held
421 	 */
422 	sbdp_walk_prom_tree(prom_rootnode(), sbdp_get_mem_dip, &smd);
423 	if (smd.dip != NULL) {
424 		sbdp_handle_t		*hp;
425 
426 		hp = kmem_zalloc(sizeof (sbdp_handle_t), KM_SLEEP);
427 		hp->h_board = bdp->bd;
428 		hp->h_wnode = bdp->wnode;
429 		hp->h_err = kmem_zalloc(sizeof (*hp->h_err), KM_SLEEP);
430 		if (bdp->ml != NULL) {
431 			(void) sbdp_del_memlist(hp, bdp->ml);
432 		}
433 		bdp->ml = sbdp_get_memlist(hp, (dev_info_t *)NULL);
434 		/*
435 		 * if the board doesn't have banks initialize them,
436 		 * otherwise we assume they have been updated if
437 		 * necessary
438 		 */
439 		if (bdp->banks == NULL) {
440 			sbdp_init_bd_banks(bdp);
441 		}
442 #ifdef DEBUG
443 		sbdp_print_bd_banks(bdp);
444 #endif
445 
446 		if (sbdphw_get_base_physaddr(hp, smd.dip, &bdp->bpa))
447 			bdp->bpa = -1;
448 		ddi_release_devi(smd.dip);
449 		kmem_free(hp->h_err, sizeof (*hp->h_err));
450 		kmem_free(hp, sizeof (sbdp_handle_t));
451 	}
452 	mutex_exit(&bdp->bd_mutex);
453 }
454 
455 /*
456  * Initialize the board struct.  This remains cached.  We update it
457  * every time we have a successful show_board and after a copy-rename
458  */
459 void
460 sbdp_bd_init(sbdp_bd_t *bdp, int bd, int wnode)
461 {
462 	static fn_t	f = "sbdp_bd_init";
463 
464 	SBDP_DBG_FUNC("%s\n", f);
465 
466 	bdp->bd = bd;
467 	bdp->wnode = wnode;
468 
469 	SBDP_UNSET_ALL_CPUS_IN_RESET(bdp);
470 
471 	bdp->cpus_present = 0;
472 
473 	sbdp_update_bd_info(bdp);
474 
475 	mutex_init(&bdp->bd_mutex, NULL, MUTEX_DRIVER, NULL);
476 	bdp->bd_sc = (show_board_t *)kmem_zalloc(sizeof (show_board_t),
477 	    KM_SLEEP);
478 	bdp->valid_cp = -1;
479 }
480 
481 /*
482  * This entry is going away.  Clean up
483  */
484 void
485 sbdp_bd_fini(sbdp_bd_t *bdp)
486 {
487 	static fn_t	f = "sbdp_bd_fini";
488 
489 	SBDP_DBG_FUNC("%s\n", f);
490 
491 	sbdp_cleanup_bd(bdp->wnode, bdp->bd);
492 	kmem_free(bdp->bd_sc, sizeof (show_board_t));
493 	bdp->bd_sc = NULL;
494 	mutex_destroy(&bdp->bd_mutex);
495 #ifdef DEBUG
496 	sbdp_print_all_segs();
497 #endif
498 }
499 
500 /*
501  * A new wnode has arrived.  Initialize the struct and create
502  * the board structures.
503  */
504 void
505 sbdp_wnode_init(sbdp_wnode_t *wnodep, int wnode, int boards)
506 {
507 	int		i;
508 	static fn_t	f = "sbdp_wnode_init";
509 
510 	SBDP_DBG_FUNC("%s\n", f);
511 
512 	wnodep->wnode = wnode;
513 	wnodep->nbds = boards;
514 	wnodep->bds = kmem_zalloc(sizeof (sbdp_bd_t) * boards, KM_SLEEP);
515 	wnodep->next = wnodep->prev = NULL;
516 
517 	for (i = 0; i < boards; i++)
518 		sbdp_bd_init(&wnodep->bds[i], i, wnode);
519 }
520 
521 /*
522  * Wnode got DRed out.  Clean up all the node stuff including the boards
523  */
524 void
525 sbdp_wnode_fini(sbdp_wnode_t *wnodep)
526 {
527 	int	boards;
528 	int	i;
529 	static fn_t	f = "sbdp_wnode_fini";
530 
531 	SBDP_DBG_FUNC("%s\n", f);
532 
533 	boards = wnodep->nbds;
534 
535 	for (i = 0; i < boards; i++)
536 		sbdp_bd_fini(&wnodep->bds[i]);
537 
538 	kmem_free(wnodep->bds, sizeof (sbdp_bd_t) * boards);
539 	wnodep->next = wnodep->prev = NULL;
540 	kmem_free(wnodep, sizeof (sbdp_wnode_t));
541 }
542 
543 /*
544  * Add all the necessary fields to this board's struct
545  */
546 void
547 sbdp_add_new_bd_info(int wnode, int board)
548 {
549 	sbdp_wnode_t	*cur;
550 	static fn_t	f = "sbdp_add_new_bd_info";
551 
552 	SBDP_DBG_FUNC("%s\n", f);
553 
554 	cur = sbdp_get_wnodep(wnode);
555 
556 	SBDP_DBG_MISC("adding new board info %d\n", board);
557 
558 	sbdp_update_bd_info(&cur->bds[board]);
559 
560 }
561 
562 /*
563  * This board has gone away.  Clean the necessary fields
564  */
565 void
566 sbdp_cleanup_bd(int wnode, int board)
567 {
568 	sbdp_wnode_t	*cur;
569 	sbdp_handle_t	handle, *hp;
570 	sbdp_bd_t	*bdp;
571 	int		i;
572 	static fn_t	f = "sbdp_cleanup_bd";
573 
574 	SBDP_DBG_FUNC("%s\n", f);
575 
576 	cur = sbdp_get_wnodep(wnode);
577 
578 	SBDP_DBG_MISC("cleaning up bd info for bd %d\n", board);
579 	if (cur == NULL) {
580 		SBDP_DBG_MISC("cur is null\n");
581 		return;
582 	}
583 
584 	bdp = &cur->bds[board];
585 
586 	/*
587 	 * Grab the lock
588 	 */
589 	mutex_enter(&bdp->bd_mutex);
590 
591 	for (i = 0; i < bdp->nnum; i++)
592 		bdp->nodes[i] = (pnode_t)0;
593 	bdp->nnum = 0;
594 
595 	sbdp_fini_bd_banks(bdp);
596 
597 	hp = &handle;
598 	hp->h_board = bdp->bd;
599 	hp->h_wnode = bdp->wnode;
600 	if (bdp->ml) {
601 		(void) sbdp_del_memlist(hp, bdp->ml);
602 	}
603 
604 	bdp->ml = NULL;
605 
606 	bdp->bpa = -1;
607 
608 	sbdp_cpu_in_reset(wnode, bdp->bd, SBDP_ALL_CPUS, 0);
609 
610 	bdp->cpus_present = 0;
611 
612 	mutex_exit(&bdp->bd_mutex);
613 }
614 
615 /*
616  *  Traverse the list looking for wnode. Return it when found
617  */
618 sbdp_wnode_t *
619 sbdp_get_wnodep(int wnode)
620 {
621 	sbdp_wnode_t	*cur;
622 	int		i;
623 	static fn_t	f = "sbdp_get_wnodep";
624 
625 	SBDP_DBG_FUNC("%s\n", f);
626 
627 	mutex_enter(&sbdp_wnode_mutex);
628 	for (i = 0, cur = first_node; i < cur_num_wnodes; i++,
629 	    cur = cur->next) {
630 		if (cur->wnode == wnode) {
631 			mutex_exit(&sbdp_wnode_mutex);
632 			return (cur);
633 		}
634 	}
635 	mutex_exit(&sbdp_wnode_mutex);
636 
637 	return (NULL);
638 }
639 
640 /*
641  * Insert this brand new node into our master list. It leaves it all
642  * initialized
643  */
644 void
645 sbdp_insert_wnode(int wnode, int max_boards)
646 {
647 	sbdp_wnode_t	*wnodep;
648 	sbdp_wnode_t	*cur;
649 	static fn_t	f = "sbdp_insert_wnode";
650 
651 	SBDP_DBG_FUNC("%s\n", f);
652 
653 	wnodep = kmem_zalloc(sizeof (sbdp_wnode_t), KM_SLEEP);
654 
655 	mutex_enter(&sbdp_wnode_mutex);
656 	if (first_node == NULL) {
657 		first_node = wnodep;
658 		cur_num_wnodes++;
659 	} else {
660 		cur = first_node + cur_num_wnodes++;
661 		cur->next = wnodep;
662 		wnodep->prev = cur;
663 	}
664 	mutex_exit(&sbdp_wnode_mutex);
665 	sbdp_wnode_init(wnodep, wnode, max_boards);
666 }
667 
668 /*
669  * This node is gone.  Remove it from the list and also clean up
670  */
671 void
672 sbdp_remove_wnode(sbdp_wnode_t *wnodep)
673 {
674 	sbdp_wnode_t	*cur;
675 	static fn_t	f = "sbdp_remove_wnode";
676 
677 	SBDP_DBG_FUNC("%s\n", f);
678 
679 	if (wnodep != NULL) {
680 		sbdp_wnode_fini(wnodep);
681 		mutex_enter(&sbdp_wnode_mutex);
682 
683 		if (first_node == wnodep)
684 			first_node = NULL;
685 		else {
686 			cur = wnodep->prev;
687 			if (cur != NULL)
688 				cur->next = wnodep->next;
689 			if (wnodep->next != NULL)
690 				wnodep->next->prev = cur;
691 		}
692 
693 		cur_num_wnodes--;
694 		mutex_exit(&sbdp_wnode_mutex);
695 	}
696 }
697 
698 /*
699  * Entry point from sbd.  This is called when a new node is added.  We
700  * create an entry in our inventory and initialize all the stuff that will be
701  * needed
702  */
703 int
704 sbdp_setup_instance(caddr_t arg)
705 {
706 	ssm_sbdp_info_t	*sbdp_info;
707 	int		instance;
708 	int		wnode;
709 	int		max_boards;
710 	static fn_t	f = "sbdp_setup_instance";
711 
712 	SBDP_DBG_FUNC("%s\n", f);
713 
714 	/*
715 	 * We get this directly from ssm
716 	 */
717 	sbdp_info = (ssm_sbdp_info_t *)arg;
718 
719 	instance = sbdp_info->instance;
720 	wnode = sbdp_info->wnode;
721 	max_boards = plat_max_boards();
722 
723 	SBDP_DBG_MISC("sbdp_setup_instance: instance %d wnode %d\n", instance,
724 	    sbdp_info->wnode);
725 
726 	if (sbdp_get_wnodep(wnode) == NULL) {
727 		/*
728 		 * This node has not been instanstiated
729 		 * create one
730 		 */
731 		sbdp_insert_wnode(wnode, max_boards);
732 	}
733 
734 	return (DDI_SUCCESS);
735 }
736 
737 /*
738  * Entry point from sbd. This is called when a node has been removed (or is
739  * going away. We do all the necessary cleanup
740  */
741 int
742 sbdp_teardown_instance(caddr_t arg)
743 {
744 	ssm_sbdp_info_t	*sbdp_info;
745 	int		instance;
746 	int		wnode;
747 	sbdp_wnode_t	*wnodep;
748 	static fn_t	f = "sbdp_teardown_instance";
749 
750 	SBDP_DBG_FUNC("%s\n", f);
751 
752 	/*
753 	 * ssm should have set this up
754 	 */
755 	sbdp_info = (ssm_sbdp_info_t *)arg;
756 
757 	instance = sbdp_info->instance;
758 	wnode = sbdp_info->wnode;
759 
760 	SBDP_DBG_MISC("sbdp_teardown_instance: instance %d wnode %d\n",
761 	    instance, wnode);
762 
763 	/*
764 	 * Find this node and then remove it
765 	 */
766 	if ((wnodep = sbdp_get_wnodep(wnode)) != NULL) {
767 		sbdp_remove_wnode(wnodep);
768 	}
769 	return (DDI_SUCCESS);
770 }
771 
772 int
773 sbdp_disabled_component(sbdp_handle_t *hp)
774 {
775 #ifdef lint
776 	hp = hp;
777 #endif
778 	return (0);
779 }
780 
781 /* ARGSUSED */
782 int
783 sbdp_release_component(sbdp_handle_t *hp, dev_info_t *dip)
784 {
785 	return (0);
786 }
787 
788 void
789 sbdp_set_err(sbd_error_t *ep, int ecode, char *rsc)
790 {
791 	static fn_t	f = "sbdp_set_err";
792 
793 	SBDP_DBG_FUNC("%s\n", f);
794 	ASSERT(ep != NULL);
795 	ep->e_code = ecode;
796 
797 	if (rsc != NULL) {
798 		(void) strcpy((caddr_t)(ep->e_rsc), (caddr_t)rsc);
799 	}
800 }
801 
802 /*
803  * Serengeti DR passthrus are for debugging purposes only.
804  */
805 static struct {
806 	const char	*name;
807 	int		(*handler)(sbdp_handle_t *, void *);
808 } sbdp_passthrus[] = {
809 #ifdef DEBUG
810 	{ "readmem",		sbdp_passthru_readmem		},
811 	{ "prep-script",	sbdp_passthru_prep_script	},
812 	{ "test-quiesce",	sbdp_passthru_test_quiesce	},
813 	{ "inject-error",	sbdp_passthru_inject_error	},
814 	{ "reset-error",	sbdp_passthru_reset_error	},
815 #endif
816 
817 	/* the following line must always be last */
818 	{ NULL,			NULL				}
819 };
820 
821 
822 /*ARGSUSED*/
823 int
824 sbdp_ioctl(sbdp_handle_t *hp, sbdp_ioctl_arg_t *sbdpi)
825 {
826 #ifdef DEBUG
827 	char buf[512];
828 	int rv;
829 	sbd_ioctl_arg_t *sbdi   = (sbd_ioctl_arg_t *)sbdpi->h_iap;
830 	int		i;
831 	static fn_t	f = "sbdp_ioctl";
832 
833 	SBDP_DBG_FUNC("%s\n", f);
834 
835 	if (sbdi->i_len >= sizeof (buf) ||
836 	    ddi_copyin(sbdi->i_opts, buf, sbdi->i_len, sbdpi->h_mode)) {
837 		sbdp_set_err(hp->h_err, ESBD_FAULT, NULL);
838 		return (-1);
839 	}
840 
841 	i = 0;
842 	while (sbdp_passthrus[i].name != NULL) {
843 		int	len;
844 
845 		len = strlen(sbdp_passthrus[i].name);
846 		if (strncmp(sbdp_passthrus[i].name, buf, len) == 0)
847 			break;
848 		i++;
849 	}
850 
851 	if (sbdp_passthrus[i].name == NULL) {
852 		sbdp_set_err(hp->h_err, ESBD_INVAL, NULL);
853 		rv = EIO;
854 	} else {
855 		rv = (*sbdp_passthrus[i].handler)(hp, buf);
856 		if (rv != ESBD_NOERROR) {
857 			sbdp_set_err(hp->h_err, rv, NULL);
858 			rv = EIO;
859 		}
860 
861 	}
862 
863 	return (rv);
864 #else
865 	return (0);
866 #endif
867 }
868 
869 /*
870  * Check the dnode we obtained.  Need to find a better way to determine
871  * if the node has the correct starting address
872  */
873 int
874 sbdp_is_node_bad(pnode_t node)
875 {
876 	static fn_t	f = "sbdp_is_node_bad";
877 
878 	SBDP_DBG_FUNC("%s\n", f);
879 
880 	return ((node == OBP_NONODE) || (node == OBP_BADNODE) ||
881 	    ((node & 0x80000000u) != 0x80000000u));
882 }
883 
884 /*
885  * Retrieve the information we have on this board from
886  * the inventory
887  */
888 sbdp_bd_t *
889 sbdp_get_bd_info(int wnode, int board)
890 {
891 	sbdp_wnode_t	*wnodep;
892 	sbdp_bd_t	*bdp;
893 	int		max_bds;
894 	static fn_t	f = "sbdp_get_bd_info";
895 
896 	SBDP_DBG_FUNC("%s\n", f);
897 
898 	wnodep = sbdp_get_wnodep(wnode);
899 	max_bds = plat_max_boards();
900 
901 	if ((wnodep == NULL) || ((board < 0) && (board > max_bds))) {
902 		return (NULL);
903 	}
904 
905 	bdp = &wnodep->bds[board];
906 
907 	/*
908 	 * We might not have the complete bd info.  With cheetah we
909 	 * cannot access the memory decode registers when then cpu is
910 	 * in reset. If the mem info is incomplete, then we try to gather it
911 	 * here
912 	 */
913 	sbdp_update_bd_info(bdp);
914 
915 	return (bdp);
916 }
917 
918 /*
919  * There are certain cases where obp marks components as failed
920  * If the status is ok the node won't have any status property. It
921  * is only there if the status is other than ok.
922  */
923 sbd_cond_t
924 sbdp_get_comp_status(pnode_t nodeid)
925 {
926 	char			status_buf[OBP_MAXPROPNAME];
927 	static const char	*status = "status";
928 	static const char	*failed = "fail";
929 	static const char	*disabled = "disabled";
930 	static fn_t		f = "sbdp_get_comp_status";
931 
932 	SBDP_DBG_FUNC("%s\n", f);
933 
934 	if (sbdp_is_node_bad(nodeid)) {
935 		SBDP_DBG_STATE("node is not ok\n");
936 		return (SBD_COND_UNKNOWN);
937 	}
938 
939 	if (prom_getproplen(nodeid, (char *)status) <= 0) {
940 		SBDP_DBG_STATE("status is ok\n");
941 		return (SBD_COND_OK);
942 	}
943 
944 	if (prom_getprop(nodeid, (char *)status, status_buf) < 0) {
945 		SBDP_DBG_STATE("status is unknown\n");
946 		return (SBD_COND_UNKNOWN);
947 	}
948 
949 	if (strncmp(status_buf, failed, strlen(failed)) == 0) {
950 		SBDP_DBG_STATE("status of failed\n");
951 		return (SBD_COND_FAILED);
952 	}
953 
954 	if (strcmp(status_buf, disabled) == 0) {
955 		SBDP_DBG_STATE("status of unusable\n");
956 		return (SBD_COND_UNUSABLE);
957 	}
958 
959 	return (SBD_COND_OK);
960 }
961 
962 void
963 sbdp_cpu_in_reset(int node, int bd, int unit, int reset)
964 {
965 	sbdp_wnode_t	*cur;
966 	sbdp_bd_t	*bdp;
967 	static fn_t	f = "sbdp_cpu_in_reset";
968 
969 	SBDP_DBG_FUNC("%s\n", f);
970 
971 	if ((unit < -1) || (bd < 0) || (node < 0)) {
972 		return;
973 	}
974 
975 	cur = sbdp_get_wnodep(node);
976 
977 	SBDP_DBG_MISC("marking cpu %d %s for board %d\n", unit,
978 	    (reset) ? "in reset" : "out of reset", bd);
979 
980 	if (cur == NULL) {
981 		return;
982 	}
983 
984 	bdp = &cur->bds[bd];
985 
986 	if (unit == SBDP_ALL_CPUS)
987 		if (reset == 1)
988 			SBDP_SET_ALL_CPUS_IN_RESET(bdp);
989 		else
990 			SBDP_UNSET_ALL_CPUS_IN_RESET(bdp);
991 	else
992 		if (reset == 1)
993 			SBDP_SET_CPU_IN_RESET(bdp, unit);
994 		else
995 			SBDP_UNSET_CPU_IN_RESET(bdp, unit);
996 }
997 
998 int
999 sbdp_set_cpu_present(int node, int bd, int unit)
1000 {
1001 	sbdp_wnode_t	*cur;
1002 	sbdp_bd_t	*bdp;
1003 	static fn_t	f = "sbdp_set_cpu_present";
1004 
1005 	SBDP_DBG_FUNC("%s\n", f);
1006 
1007 	if ((unit < 0) || (bd < 0) || (node < 0)) {
1008 		return (-1);
1009 	}
1010 
1011 	cur = sbdp_get_wnodep(node);
1012 	if (cur == NULL) {
1013 		return (-1);
1014 	}
1015 
1016 	bdp = &cur->bds[bd];
1017 
1018 	SBDP_SET_CPU_PRESENT(bdp, unit);
1019 
1020 	return (0);
1021 }
1022 
1023 int
1024 sbdp_is_cpu_present(int node, int bd, int unit)
1025 {
1026 	sbdp_wnode_t	*cur;
1027 	sbdp_bd_t	*bdp;
1028 	static fn_t	f = "sbdp_is_cpu_present";
1029 
1030 	SBDP_DBG_FUNC("%s\n", f);
1031 
1032 	if ((unit < 0) || (bd < 0) || (node < 0)) {
1033 		return (-1);
1034 	}
1035 
1036 	cur = sbdp_get_wnodep(node);
1037 	if (cur == NULL) {
1038 		return (-1);
1039 	}
1040 
1041 	bdp = &cur->bds[bd];
1042 
1043 	return (SBDP_IS_CPU_PRESENT(bdp, unit));
1044 }
1045 
1046 int
1047 sbdp_is_cpu_in_reset(int node, int bd, int unit)
1048 {
1049 	sbdp_wnode_t	*cur;
1050 	sbdp_bd_t	*bdp;
1051 	static fn_t	f = "sbdp_is_cpu_in_reset";
1052 
1053 	SBDP_DBG_FUNC("%s\n", f);
1054 
1055 	if ((unit < 0) || (bd < 0) || (node < 0)) {
1056 		return (-1);
1057 	}
1058 
1059 	cur = sbdp_get_wnodep(node);
1060 
1061 	if (cur == NULL) {
1062 		return (-1);
1063 	}
1064 
1065 	bdp = &cur->bds[bd];
1066 
1067 	return (SBDP_IS_CPU_IN_RESET(bdp, unit));
1068 }
1069 
1070 int
1071 sbdp_dr_avail(void)
1072 {
1073 	static fn_t	f = "sbdp_dr_avail";
1074 
1075 	SBDP_DBG_FUNC("%s\n", f);
1076 
1077 	if (sbdp_dr_available)
1078 		if (sg_prom_sb_dr_check() == 0)
1079 			return (1);
1080 	return (0);
1081 }
1082