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
65uint_t sbdp_debug = 0x0;
66#endif /* DEBUG */
67
68/*
69 * Enable or disable dr
70 */
71int 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
81static	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 */
98sbdp_wnode_t	*first_node = NULL; /* first wnode. Entry to the link list */
99int		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 */
109sbdp_wnode_t *sbdp_get_wnodep(int);
110
111/*
112 * Module linkage information for the kernel.
113 */
114
115static struct modlmisc modlmisc = {
116	&mod_miscops,
117	"Serengeti sbdp",
118};
119
120static struct modlinkage modlinkage = {
121	MODREV_1,
122	(void *)&modlmisc,
123	NULL
124};
125
126/*
127 * VA area used during CPU shutdown.
128 */
129caddr_t	sbdp_shutdown_va;
130
131/*
132 * Mutex to protect our inventory
133 */
134kmutex_t	sbdp_wnode_mutex;
135
136int
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
154int
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
175int
176_info(struct modinfo *modinfop)
177{
178	return (mod_info(&modlinkage, modinfop));
179}
180
181int
182sbdp_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
205int
206sbdp_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
235sbd_devattr_t *
236sbdp_get_devattr(void)
237{
238	return (&sbdp_devattr[0]);
239}
240
241int
242sbdp_portid_to_cpu_unit(int cmp, int core)
243{
244	return (SG_PORTID_TO_CPU_UNIT(cmp, core));
245}
246
247int
248sbdp_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
344struct sbdp_mem_dip {
345	sbdp_bd_t	*bdp;
346	dev_info_t	*dip;
347};
348
349static int
350sbdp_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 */
383void
384sbdp_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 */
459void
460sbdp_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 */
484void
485sbdp_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 */
504void
505sbdp_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 */
524void
525sbdp_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 */
546void
547sbdp_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 */
565void
566sbdp_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 */
618sbdp_wnode_t *
619sbdp_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 */
644void
645sbdp_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 */
671void
672sbdp_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 */
703int
704sbdp_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 */
741int
742sbdp_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
772int
773sbdp_disabled_component(sbdp_handle_t *hp)
774{
775#ifdef lint
776	hp = hp;
777#endif
778	return (0);
779}
780
781/* ARGSUSED */
782int
783sbdp_release_component(sbdp_handle_t *hp, dev_info_t *dip)
784{
785	return (0);
786}
787
788void
789sbdp_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 */
805static 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*/
823int
824sbdp_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 */
873int
874sbdp_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 */
888sbdp_bd_t *
889sbdp_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 */
923sbd_cond_t
924sbdp_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
962void
963sbdp_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
998int
999sbdp_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
1023int
1024sbdp_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
1046int
1047sbdp_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
1070int
1071sbdp_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