xref: /titanic_44/usr/src/uts/sun4u/lw8/os/lw8_platmod.c (revision 40e5e17b3361b3eea56a9723071c406894a20b78)
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 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/time.h>
30 #include <sys/cpuvar.h>
31 #include <sys/dditypes.h>
32 #include <sys/ddipropdefs.h>
33 #include <sys/ddi_impldefs.h>
34 #include <sys/sunddi.h>
35 #include <sys/esunddi.h>
36 #include <sys/sunndi.h>
37 #include <sys/platform_module.h>
38 #include <sys/errno.h>
39 #include <sys/conf.h>
40 #include <sys/modctl.h>
41 #include <sys/promif.h>
42 #include <sys/promimpl.h>
43 #include <sys/prom_plat.h>
44 #include <sys/cmn_err.h>
45 #include <sys/sysmacros.h>
46 #include <sys/mem_cage.h>
47 #include <sys/kobj.h>
48 #include <sys/utsname.h>
49 #include <sys/cpu_sgnblk_defs.h>
50 #include <sys/atomic.h>
51 #include <sys/kdi_impl.h>
52 
53 #include <sys/sgsbbc.h>
54 #include <sys/sgsbbc_iosram.h>
55 #include <sys/sgsbbc_iosram_priv.h>
56 #include <sys/sgsbbc_mailbox.h>
57 #include <sys/sgsgn.h>
58 #include <sys/serengeti.h>
59 #include <sys/sgfrutypes.h>
60 #include <sys/machsystm.h>
61 #include <sys/sbd_ioctl.h>
62 #include <sys/sbd.h>
63 #include <sys/sbdp_mem.h>
64 #include <sys/sgcn.h>
65 
66 #include <sys/memnode.h>
67 #include <vm/vm_dep.h>
68 #include <vm/page.h>
69 
70 #include <sys/cheetahregs.h>
71 #include <sys/plat_ecc_unum.h>
72 #include <sys/plat_ecc_dimm.h>
73 
74 #include <sys/lgrp.h>
75 
76 static int sg_debug = 0;
77 
78 #ifdef DEBUG
79 #define	DCMNERR if (sg_debug) cmn_err
80 #else
81 #define	DCMNERR
82 #endif
83 
84 int (*p2get_mem_unum)(int, uint64_t, char *, int, int *);
85 
86 /* local functions */
87 static void cpu_sgn_update(ushort_t sgn, uchar_t state,
88     uchar_t sub_state, int cpuid);
89 
90 
91 /*
92  * Local data.
93  *
94  * iosram_write_ptr is a pointer to iosram_write().  Because of
95  * kernel dynamic linking, we can't get to the function by name,
96  * but we can look up its address, and store it in this variable
97  * instead.
98  *
99  * We include the extern for iosram_write() here not because we call
100  * it, but to force compilation errors if its prototype doesn't
101  * match the prototype of iosram_write_ptr.
102  *
103  * The same issues apply to iosram_read() and iosram_read_ptr.
104  */
105 /*CSTYLED*/
106 extern int   iosram_write     (int, uint32_t, caddr_t, uint32_t);
107 static int (*iosram_write_ptr)(int, uint32_t, caddr_t, uint32_t) = NULL;
108 /*CSTYLED*/
109 extern int   iosram_read     (int, uint32_t, caddr_t, uint32_t);
110 static int (*iosram_read_ptr)(int, uint32_t, caddr_t, uint32_t) = NULL;
111 
112 
113 /*
114  * Variable to indicate if the date should be obtained from the SC or not.
115  */
116 int todsg_use_sc = FALSE;	/* set the false at the beginning */
117 
118 /*
119  * Preallocation of spare tsb's for DR
120  *
121  * We don't allocate spares for Wildcat since TSBs should come
122  * out of memory local to the node.
123  */
124 #define	IOMMU_PER_SCHIZO	2
125 int serengeti_tsb_spares = (SG_MAX_IO_BDS * SG_SCHIZO_PER_IO_BD *
126 	IOMMU_PER_SCHIZO);
127 
128 /*
129  * sg_max_ncpus is the maximum number of CPUs supported on lw8.
130  * sg_max_ncpus is set to be smaller than NCPU to reduce the amount of
131  * memory the logs take up until we have a dynamic log memory allocation
132  * solution.
133  */
134 int sg_max_ncpus = (12 * 2);    /* (max # of processors * # of cores/proc) */
135 
136 /*
137  * variables to control mailbox message timeouts.
138  * These can be patched via /etc/system or mdb.
139  */
140 int	sbbc_mbox_default_timeout = MBOX_DEFAULT_TIMEOUT;
141 int	sbbc_mbox_min_timeout = MBOX_MIN_TIMEOUT;
142 
143 /* cached 'chosen' node_id */
144 pnode_t chosen_nodeid = (pnode_t)0;
145 
146 /*
147  * Table that maps memory slices to a specific memnode.
148  */
149 int slice_to_memnode[SG_MAX_SLICE];
150 
151 /*
152  * We define and use LW8_MAX_CPU_BDS here instead of SG_MAX_CPU_BDS
153  * since a LW8 machine will never have a CPU/Mem board #5 (SB5).
154  * A LW8 machine can only have a maximum of three CPU/Mem boards, but
155  * the board numbers assigned are 0, 2, and 4.  LW8_MAX_CPU_BDS is
156  * defined to be 5 since the entries in the domain_dimm_sids array
157  * are keyed by board number.  Not perfect but some wasted space
158  * is avoided.
159  */
160 #define	LW8_MAX_CPU_BDS		5
161 
162 plat_dimm_sid_board_t	domain_dimm_sids[LW8_MAX_CPU_BDS];
163 
164 int
165 set_platform_tsb_spares()
166 {
167 	return (MIN(serengeti_tsb_spares, MAX_UPA));
168 }
169 
170 #pragma weak mmu_init_large_pages
171 
172 void
173 set_platform_defaults(void)
174 {
175 	extern int watchdog_enable;
176 	extern uint64_t xc_tick_limit_scale;
177 	extern void mmu_init_large_pages(size_t);
178 
179 #ifdef DEBUG
180 	char *todsg_name = "todsg";
181 	ce_verbose_memory = 2;
182 	ce_verbose_other = 2;
183 #endif /* DEBUG */
184 
185 	watchdog_enable = TRUE;
186 	watchdog_available = TRUE;
187 
188 	cpu_sgn_func = cpu_sgn_update;
189 
190 #ifdef DEBUG
191 	/* tod_module_name should be set to "todsg" from OBP property */
192 	if (tod_module_name && (strcmp(tod_module_name, todsg_name) == 0))
193 		prom_printf("Using todsg driver\n");
194 	else {
195 		prom_printf("Force using todsg driver\n");
196 		tod_module_name = todsg_name;
197 	}
198 #endif /* DEBUG */
199 
200 	/* lw8 does not support forthdebug */
201 	forthdebug_supported = 0;
202 
203 
204 	/*
205 	 * Some DR operations require the system to be sync paused.
206 	 * Sync pause on Serengeti could potentially take up to 4
207 	 * seconds to complete depending on the load on the SC.  To
208 	 * avoid send_mond panics during such operations, we need to
209 	 * increase xc_tick_limit to a larger value on Serengeti by
210 	 * setting xc_tick_limit_scale to 5.
211 	 */
212 	xc_tick_limit_scale = 5;
213 
214 	if ((mmu_page_sizes == max_mmu_page_sizes) &&
215 	    (mmu_ism_pagesize != DEFAULT_ISM_PAGESIZE)) {
216 		if (&mmu_init_large_pages)
217 			mmu_init_large_pages(mmu_ism_pagesize);
218 	}
219 }
220 
221 void
222 load_platform_modules(void)
223 {
224 	if (modload("misc", "pcihp") < 0) {
225 		cmn_err(CE_NOTE, "pcihp driver failed to load");
226 	}
227 }
228 
229 /*ARGSUSED*/
230 int
231 plat_cpu_poweron(struct cpu *cp)
232 {
233 	int (*serengeti_cpu_poweron)(struct cpu *) = NULL;
234 
235 	serengeti_cpu_poweron =
236 	    (int (*)(struct cpu *))modgetsymvalue("sbdp_cpu_poweron", 0);
237 
238 	if (serengeti_cpu_poweron == NULL)
239 		return (ENOTSUP);
240 	else
241 		return ((serengeti_cpu_poweron)(cp));
242 }
243 
244 /*ARGSUSED*/
245 int
246 plat_cpu_poweroff(struct cpu *cp)
247 {
248 	int (*serengeti_cpu_poweroff)(struct cpu *) = NULL;
249 
250 	serengeti_cpu_poweroff =
251 	    (int (*)(struct cpu *))modgetsymvalue("sbdp_cpu_poweroff", 0);
252 
253 	if (serengeti_cpu_poweroff == NULL)
254 		return (ENOTSUP);
255 	else
256 		return ((serengeti_cpu_poweroff)(cp));
257 }
258 
259 #ifdef DEBUG
260 pgcnt_t serengeti_cage_size_limit;
261 #endif
262 
263 /* Preferred minimum cage size (expressed in pages)... for DR */
264 pgcnt_t serengeti_minimum_cage_size = 0;
265 
266 void
267 set_platform_cage_params(void)
268 {
269 	extern pgcnt_t total_pages;
270 	extern struct memlist *phys_avail;
271 	int ret;
272 
273 	if (kernel_cage_enable) {
274 		pgcnt_t preferred_cage_size;
275 
276 		preferred_cage_size =
277 		    MAX(serengeti_minimum_cage_size, total_pages / 256);
278 #ifdef DEBUG
279 		if (serengeti_cage_size_limit)
280 			preferred_cage_size = serengeti_cage_size_limit;
281 #endif
282 		kcage_range_lock();
283 		/*
284 		 * Post copies obp into the lowest slice.  This requires the
285 		 * cage to grow upwards
286 		 */
287 		ret = kcage_range_init(phys_avail, 0);
288 		if (ret == 0)
289 			kcage_init(preferred_cage_size);
290 		kcage_range_unlock();
291 	}
292 
293 	/* Only note when the cage is off since it should always be on. */
294 	if (!kcage_on)
295 		cmn_err(CE_NOTE, "!DR Kernel Cage is DISABLED");
296 }
297 
298 #define	ALIGN(x, a)	((a) == 0 ? (uint64_t)(x) : \
299 	(((uint64_t)(x) + (uint64_t)(a) - 1l) & ~((uint64_t)(a) - 1l)))
300 
301 void
302 update_mem_bounds(int brd, uint64_t base, uint64_t sz)
303 {
304 	uint64_t	end;
305 	int		mnode;
306 
307 	end = base + sz - 1;
308 
309 	/*
310 	 * First see if this board already has a memnode associated
311 	 * with it.  If not, see if this slice has a memnode.  This
312 	 * covers the cases where a single slice covers multiple
313 	 * boards (cross-board interleaving) and where a single
314 	 * board has multiple slices (1+GB DIMMs).
315 	 */
316 	if ((mnode = plat_lgrphand_to_mem_node(brd)) == -1) {
317 		if ((mnode = slice_to_memnode[PA_2_SLICE(base)]) == -1)
318 			mnode = mem_node_alloc();
319 		plat_assign_lgrphand_to_mem_node(brd, mnode);
320 	}
321 
322 	/*
323 	 * Align base at 16GB boundary
324 	 */
325 	base = ALIGN(base, (1ul << PA_SLICE_SHIFT));
326 
327 	while (base < end) {
328 		slice_to_memnode[PA_2_SLICE(base)] = mnode;
329 		base += (1ul << PA_SLICE_SHIFT);
330 	}
331 }
332 
333 /*
334  * Dynamically detect memory slices in the system by decoding
335  * the cpu memory decoder registers at boot time.
336  */
337 void
338 plat_fill_mc(pnode_t nodeid)
339 {
340 	uint64_t	mc_addr, mask;
341 	uint64_t	mc_decode[SG_MAX_BANKS_PER_MC];
342 	uint64_t	base, size;
343 	uint32_t	regs[4];
344 	int		len;
345 	int		local_mc;
346 	int		portid;
347 	int		boardid;
348 	int		i;
349 
350 	if ((prom_getprop(nodeid, "portid", (caddr_t)&portid) < 0) ||
351 	    (portid == -1))
352 		return;
353 
354 	/*
355 	 * Decode the board number from the MC portid
356 	 */
357 	boardid = SG_PORTID_TO_BOARD_NUM(portid);
358 
359 	/*
360 	 * The "reg" property returns 4 32-bit values. The first two are
361 	 * combined to form a 64-bit address.  The second two are for a
362 	 * 64-bit size, but we don't actually need to look at that value.
363 	 */
364 	len = prom_getproplen(nodeid, "reg");
365 	if (len != (sizeof (uint32_t) * 4)) {
366 		prom_printf("Warning: malformed 'reg' property\n");
367 		return;
368 	}
369 	if (prom_getprop(nodeid, "reg", (caddr_t)regs) < 0)
370 		return;
371 	mc_addr = ((uint64_t)regs[0]) << 32;
372 	mc_addr |= (uint64_t)regs[1];
373 
374 	/*
375 	 * Figure out whether the memory controller we are examining
376 	 * belongs to this CPU or a different one.
377 	 */
378 	if (portid == cpunodes[CPU->cpu_id].portid)
379 		local_mc = 1;
380 	else
381 		local_mc = 0;
382 
383 	for (i = 0; i < SG_MAX_BANKS_PER_MC; i++) {
384 		mask = SG_REG_2_OFFSET(i);
385 
386 		/*
387 		 * If the memory controller is local to this CPU, we use
388 		 * the special ASI to read the decode registers.
389 		 * Otherwise, we load the values from a magic address in
390 		 * I/O space.
391 		 */
392 		if (local_mc)
393 			mc_decode[i] = lddmcdecode(mask & MC_OFFSET_MASK);
394 		else
395 			mc_decode[i] = lddphysio((mc_addr | mask));
396 
397 		if (mc_decode[i] >> MC_VALID_SHIFT) {
398 			/*
399 			 * The memory decode register is a bitmask field,
400 			 * so we can decode that into both a base and
401 			 * a span.
402 			 */
403 			base = MC_BASE(mc_decode[i]) << PHYS2UM_SHIFT;
404 			size = MC_UK2SPAN(mc_decode[i]);
405 			update_mem_bounds(boardid, base, size);
406 		}
407 	}
408 }
409 
410 /*
411  * This routine is run midway through the boot process.  By the time we get
412  * here, we know about all the active CPU boards in the system, and we have
413  * extracted information about each board's memory from the memory
414  * controllers.  We have also figured out which ranges of memory will be
415  * assigned to which memnodes, so we walk the slice table to build the table
416  * of memnodes.
417  */
418 /* ARGSUSED */
419 void
420 plat_build_mem_nodes(u_longlong_t *list, size_t  nelems)
421 {
422 	int	slice;
423 	pfn_t	basepfn;
424 	pgcnt_t	npgs;
425 
426 	mem_node_pfn_shift = PFN_SLICE_SHIFT;
427 	mem_node_physalign = (1ull << PA_SLICE_SHIFT);
428 
429 	for (slice = 0; slice < SG_MAX_SLICE; slice++) {
430 		if (slice_to_memnode[slice] == -1)
431 			continue;
432 		basepfn = (uint64_t)slice << PFN_SLICE_SHIFT;
433 		npgs = 1ull << PFN_SLICE_SHIFT;
434 		mem_node_add_slice(basepfn, basepfn + npgs - 1);
435 	}
436 }
437 
438 int
439 plat_pfn_to_mem_node(pfn_t pfn)
440 {
441 	int node;
442 
443 	node = slice_to_memnode[PFN_2_SLICE(pfn)];
444 
445 	return (node);
446 }
447 
448 /*
449  * Serengeti support for lgroups.
450  *
451  * On Serengeti, an lgroup platform handle == board number.
452  *
453  * Mappings between lgroup handles and memnodes are managed
454  * in addition to mappings between memory slices and memnodes
455  * to support cross-board interleaving as well as multiple
456  * slices per board (e.g. >1GB DIMMs). The initial mapping
457  * of memnodes to lgroup handles is determined at boot time.
458  * A DR addition of memory adds a new mapping. A DR copy-rename
459  * swaps mappings.
460  */
461 
462 /*
463  * Macro for extracting the board number from the CPU id
464  */
465 #define	CPUID_TO_BOARD(id)	(((id) >> 2) & 0x7)
466 
467 /*
468  * Return the platform handle for the lgroup containing the given CPU
469  *
470  * For Serengeti, lgroup platform handle == board number
471  */
472 lgrp_handle_t
473 plat_lgrp_cpu_to_hand(processorid_t id)
474 {
475 	return (CPUID_TO_BOARD(id));
476 }
477 
478 /*
479  * Platform specific lgroup initialization
480  */
481 void
482 plat_lgrp_init(void)
483 {
484 	int i;
485 	extern uint32_t lgrp_expand_proc_thresh;
486 	extern uint32_t lgrp_expand_proc_diff;
487 
488 	/*
489 	 * Initialize lookup tables to invalid values so we catch
490 	 * any illegal use of them.
491 	 */
492 	for (i = 0; i < SG_MAX_SLICE; i++) {
493 		slice_to_memnode[i] = -1;
494 	}
495 
496 	/*
497 	 * Set tuneables for Serengeti architecture
498 	 *
499 	 * lgrp_expand_proc_thresh is the minimum load on the lgroups
500 	 * this process is currently running on before considering
501 	 * expanding threads to another lgroup.
502 	 *
503 	 * lgrp_expand_proc_diff determines how much less the remote lgroup
504 	 * must be loaded before expanding to it.
505 	 *
506 	 * Bandwidth is maximized on Serengeti by spreading load across
507 	 * the machine. The impact to inter-thread communication isn't
508 	 * too costly since remote latencies are relatively low.  These
509 	 * values equate to one CPU's load and so attempt to spread the
510 	 * load out across as many lgroups as possible one CPU at a time.
511 	 */
512 	lgrp_expand_proc_thresh = LGRP_LOADAVG_THREAD_MAX;
513 	lgrp_expand_proc_diff = LGRP_LOADAVG_THREAD_MAX;
514 }
515 
516 /*
517  * Platform notification of lgroup (re)configuration changes
518  */
519 /*ARGSUSED*/
520 void
521 plat_lgrp_config(lgrp_config_flag_t evt, uintptr_t arg)
522 {
523 	update_membounds_t	*umb;
524 	lgrp_config_mem_rename_t lmr;
525 	lgrp_handle_t		shand, thand;
526 	int			snode, tnode;
527 
528 	switch (evt) {
529 
530 	case LGRP_CONFIG_MEM_ADD:
531 		umb = (update_membounds_t *)arg;
532 		update_mem_bounds(umb->u_board, umb->u_base, umb->u_len);
533 
534 		break;
535 
536 	case LGRP_CONFIG_MEM_DEL:
537 		/* We don't have to do anything */
538 
539 		break;
540 
541 	case LGRP_CONFIG_MEM_RENAME:
542 		/*
543 		 * During a DR copy-rename operation, all of the memory
544 		 * on one board is moved to another board -- but the
545 		 * addresses/pfns and memnodes don't change. This means
546 		 * the memory has changed locations without changing identity.
547 		 *
548 		 * Source is where we are copying from and target is where we
549 		 * are copying to.  After source memnode is copied to target
550 		 * memnode, the physical addresses of the target memnode are
551 		 * renamed to match what the source memnode had.  Then target
552 		 * memnode can be removed and source memnode can take its
553 		 * place.
554 		 *
555 		 * To do this, swap the lgroup handle to memnode mappings for
556 		 * the boards, so target lgroup will have source memnode and
557 		 * source lgroup will have empty target memnode which is where
558 		 * its memory will go (if any is added to it later).
559 		 *
560 		 * Then source memnode needs to be removed from its lgroup
561 		 * and added to the target lgroup where the memory was living
562 		 * but under a different name/memnode.  The memory was in the
563 		 * target memnode and now lives in the source memnode with
564 		 * different physical addresses even though it is the same
565 		 * memory.
566 		 */
567 		shand = arg & 0xffff;
568 		thand = (arg & 0xffff0000) >> 16;
569 		snode = plat_lgrphand_to_mem_node(shand);
570 		tnode = plat_lgrphand_to_mem_node(thand);
571 
572 		plat_assign_lgrphand_to_mem_node(thand, snode);
573 		plat_assign_lgrphand_to_mem_node(shand, tnode);
574 
575 		/*
576 		 * Remove source memnode of copy rename from its lgroup
577 		 * and add it to its new target lgroup
578 		 */
579 		lmr.lmem_rename_from = shand;
580 		lmr.lmem_rename_to = thand;
581 
582 		lgrp_config(LGRP_CONFIG_MEM_RENAME, (uintptr_t)snode,
583 		    (uintptr_t)&lmr);
584 
585 		break;
586 
587 	default:
588 		break;
589 	}
590 }
591 
592 /*
593  * Return latency between "from" and "to" lgroups
594  *
595  * This latency number can only be used for relative comparison
596  * between lgroups on the running system, cannot be used across platforms,
597  * and may not reflect the actual latency.  It is platform and implementation
598  * specific, so platform gets to decide its value.  It would be nice if the
599  * number was at least proportional to make comparisons more meaningful though.
600  * NOTE: The numbers below are supposed to be load latencies for uncached
601  * memory divided by 10.
602  */
603 int
604 plat_lgrp_latency(lgrp_handle_t from, lgrp_handle_t to)
605 {
606 	/*
607 	 * Return min remote latency when there are more than two lgroups
608 	 * (root and child) and getting latency between two different lgroups
609 	 * or root is involved
610 	 */
611 	if (lgrp_optimizations() && (from != to ||
612 	    from == LGRP_DEFAULT_HANDLE || to == LGRP_DEFAULT_HANDLE))
613 		return (28);
614 	else
615 		return (23);
616 }
617 
618 /* ARGSUSED */
619 void
620 plat_freelist_process(int mnode)
621 {
622 }
623 
624 /*
625  * Find dip for chosen IOSRAM
626  */
627 dev_info_t *
628 find_chosen_dip(void)
629 {
630 	dev_info_t	*dip;
631 	char		master_sbbc[MAXNAMELEN];
632 	int		nodeid;
633 	uint_t		tunnel;
634 
635 	/*
636 	 * find the /chosen SBBC node, prom interface will handle errors
637 	 */
638 	nodeid = prom_chosennode();
639 	/*
640 	 * get the 'iosram' property from the /chosen node
641 	 */
642 	if (prom_getprop(nodeid, IOSRAM_CHOSEN_PROP, (caddr_t)&tunnel) <= 0) {
643 		SBBC_ERR(CE_PANIC, "No iosram property found! \n");
644 	}
645 
646 	if (prom_phandle_to_path((phandle_t)tunnel, master_sbbc,
647 	    sizeof (master_sbbc)) < 0) {
648 		SBBC_ERR1(CE_PANIC, "prom_phandle_to_path(%d) failed\n",
649 		    tunnel);
650 	}
651 
652 	chosen_nodeid = nodeid;
653 
654 	/*
655 	 * load and attach the sgsbbc driver.
656 	 * This will also attach all the sgsbbc driver instances
657 	 */
658 	if (i_ddi_attach_hw_nodes("sgsbbc") != DDI_SUCCESS) {
659 		cmn_err(CE_WARN, "sgsbbc failed to load\n");
660 	}
661 	/* translate a path name to a dev_info_t */
662 	dip = e_ddi_hold_devi_by_path(master_sbbc, 0);
663 	if ((dip == NULL) || (ddi_get_nodeid(dip) != tunnel)) {
664 		cmn_err(CE_PANIC,
665 			"e_ddi_hold_devi_by_path(%x) failed for SBBC\n",
666 		    tunnel);
667 	}
668 
669 	/* make sure devi_ref is ZERO */
670 	ndi_rele_devi(dip);
671 	DCMNERR(CE_CONT, "Chosen IOSRAM is at %s \n", master_sbbc);
672 
673 	return (dip);
674 }
675 
676 void
677 load_platform_drivers(void)
678 {
679 	int ret;
680 
681 	/*
682 	 * Load the mc-us3 memory driver.
683 	 */
684 	if (i_ddi_attach_hw_nodes("mc-us3") != DDI_SUCCESS)
685 		cmn_err(CE_WARN, "mc-us3 failed to load");
686 	else
687 		(void) ddi_hold_driver(ddi_name_to_major("mc-us3"));
688 
689 	/*
690 	 * Initialize the chosen IOSRAM before its clients
691 	 * are loaded.
692 	 */
693 	(void) find_chosen_dip();
694 
695 	/*
696 	 * Load the environmentals driver (sgenv)
697 	 *
698 	 * We need this driver to handle events from the SC when state
699 	 * changes occur in the environmental data.
700 	 */
701 	if (i_ddi_attach_hw_nodes("sgenv") != DDI_SUCCESS)
702 		cmn_err(CE_WARN, "sgenv failed to load");
703 
704 	/*
705 	 * Ideally, we'd do this in set_platform_defaults(), but
706 	 * at that point it's too early to look up symbols.
707 	 */
708 	iosram_write_ptr = (int (*)(int, uint32_t, caddr_t, uint32_t))
709 	    modgetsymvalue("iosram_write", 0);
710 
711 	if (iosram_write_ptr == NULL) {
712 		DCMNERR(CE_WARN, "load_platform_defaults: iosram_write()"
713 		    " not found; signatures will not be updated\n");
714 	} else {
715 		/*
716 		 * The iosram read ptr is only needed if we can actually
717 		 * write CPU signatures, so only bother setting it if we
718 		 * set a valid write pointer, above.
719 		 */
720 		iosram_read_ptr = (int (*)(int, uint32_t, caddr_t, uint32_t))
721 		    modgetsymvalue("iosram_read", 0);
722 
723 		if (iosram_read_ptr == NULL)
724 			DCMNERR(CE_WARN, "load_platform_defaults: iosram_read()"
725 			    " not found\n");
726 	}
727 
728 	/*
729 	 * Set todsg_use_sc to TRUE so that we will be getting date
730 	 * from the SC.
731 	 */
732 	todsg_use_sc = TRUE;
733 
734 	/*
735 	 * Now is a good time to activate hardware watchdog (if one exists).
736 	 */
737 	mutex_enter(&tod_lock);
738 	if (watchdog_enable)
739 		ret = tod_ops.tod_set_watchdog_timer(watchdog_timeout_seconds);
740 	mutex_exit(&tod_lock);
741 	if (ret != 0)
742 		printf("Hardware watchdog enabled\n");
743 
744 	plat_ecc_init();
745 }
746 
747 /*
748  * No platform drivers on this platform
749  */
750 char *platform_module_list[] = {
751 	(char *)0
752 };
753 
754 /*ARGSUSED*/
755 void
756 plat_tod_fault(enum tod_fault_type tod_bad)
757 {
758 }
759 int
760 plat_max_boards()
761 {
762 	return (SG_MAX_BDS);
763 }
764 int
765 plat_max_io_units_per_board()
766 {
767 	return (SG_MAX_IO_PER_BD);
768 }
769 int
770 plat_max_cmp_units_per_board()
771 {
772 	return (SG_MAX_CMPS_PER_BD);
773 }
774 int
775 plat_max_cpu_units_per_board()
776 {
777 	return (SG_MAX_CPUS_PER_BD);
778 }
779 
780 int
781 plat_max_mc_units_per_board()
782 {
783 	return (SG_MAX_CMPS_PER_BD); /* each CPU die has a memory controller */
784 }
785 
786 int
787 plat_max_mem_units_per_board()
788 {
789 	return (SG_MAX_MEM_PER_BD);
790 }
791 
792 int
793 plat_max_cpumem_boards(void)
794 {
795 	return (LW8_MAX_CPU_BDS);
796 }
797 
798 int
799 set_platform_max_ncpus(void)
800 {
801 	return (sg_max_ncpus);
802 }
803 
804 void
805 plat_dmv_params(uint_t *hwint, uint_t *swint)
806 {
807 	*hwint = MAX_UPA;
808 	*swint = 0;
809 }
810 
811 static int (*sg_mbox)(sbbc_msg_t *, sbbc_msg_t *, time_t) = NULL;
812 
813 /*
814  * Our nodename has been set, pass it along to the SC.
815  */
816 void
817 plat_nodename_set(void)
818 {
819 	sbbc_msg_t	req;	/* request */
820 	sbbc_msg_t	resp;	/* response */
821 	int		rv;	/* return value from call to mbox */
822 	struct nodename_info {
823 		int32_t	namelen;
824 		char	nodename[_SYS_NMLN];
825 	} nni;
826 
827 	/*
828 	 * find the symbol for the mailbox routine
829 	 */
830 	if (sg_mbox == NULL)
831 		sg_mbox = (int (*)(sbbc_msg_t *, sbbc_msg_t *, time_t))
832 			modgetsymvalue("sbbc_mbox_request_response", 0);
833 
834 	if (sg_mbox == NULL) {
835 		cmn_err(CE_NOTE, "!plat_nodename_set: sg_mbox not found\n");
836 		return;
837 	}
838 
839 	/*
840 	 * construct the message telling the SC our nodename
841 	 */
842 	(void) strcpy(nni.nodename, utsname.nodename);
843 	nni.namelen = (int32_t)strlen(nni.nodename);
844 
845 	req.msg_type.type = INFO_MBOX;
846 	req.msg_type.sub_type = INFO_MBOX_NODENAME;
847 	req.msg_status = 0;
848 	req.msg_len = (int)(nni.namelen + sizeof (nni.namelen));
849 	req.msg_bytes = 0;
850 	req.msg_buf = (caddr_t)&nni;
851 	req.msg_data[0] = 0;
852 	req.msg_data[1] = 0;
853 
854 	/*
855 	 * initialize the response back from the SC
856 	 */
857 	resp.msg_type.type = INFO_MBOX;
858 	resp.msg_type.sub_type = INFO_MBOX_NODENAME;
859 	resp.msg_status = 0;
860 	resp.msg_len = 0;
861 	resp.msg_bytes = 0;
862 	resp.msg_buf = (caddr_t)0;
863 	resp.msg_data[0] = 0;
864 	resp.msg_data[1] = 0;
865 
866 	/*
867 	 * ship it and check for success
868 	 */
869 	rv = (sg_mbox)(&req, &resp, sbbc_mbox_default_timeout);
870 
871 	if (rv != 0) {
872 		cmn_err(CE_NOTE, "!plat_nodename_set: sg_mbox retval %d\n", rv);
873 	} else if (resp.msg_status != 0) {
874 		cmn_err(CE_NOTE, "!plat_nodename_set: msg_status %d\n",
875 			resp.msg_status);
876 	} else {
877 		DCMNERR(CE_NOTE, "!plat_nodename_set was successful\n");
878 
879 		/*
880 		 * It is necessary to exchange capability the bitmap
881 		 * with SC before sending any ecc error information and
882 		 * indictment. We are calling the plat_ecc_capability_send()
883 		 * here just after sending the nodename successfully.
884 		 */
885 		rv = plat_ecc_capability_send();
886 		if (rv == 0) {
887 			DCMNERR(CE_NOTE, "!plat_ecc_capability_send was"
888 			    "successful\n");
889 		}
890 	}
891 }
892 
893 /*
894  * flag to allow users switch between using OBP's
895  * prom_get_unum() and mc-us3 driver's p2get_mem_unum()
896  * (for main memory errors only).
897  */
898 int sg_use_prom_get_unum = 0;
899 
900 /*
901  * Debugging flag: set to 1 to call into obp for get_unum, or set it to 0
902  * to call into the unum cache system.  This is the E$ equivalent of
903  * sg_use_prom_get_unum.
904  */
905 int sg_use_prom_ecache_unum = 0;
906 
907 /* used for logging ECC errors to the SC */
908 #define	SG_MEMORY_ECC	1
909 #define	SG_ECACHE_ECC	2
910 #define	SG_UNKNOWN_ECC	(-1)
911 
912 /*
913  * plat_get_mem_unum() generates a string identifying either the
914  * memory or E$ DIMM(s) during error logging. Depending on whether
915  * the error is E$ or memory related, the appropriate support
916  * routine is called to assist in the string generation.
917  *
918  * - For main memory errors we can use the mc-us3 drivers p2getunum()
919  *   (or prom_get_unum() for debugging purposes).
920  *
921  * - For E$ errors we call sg_get_ecacheunum() to generate the unum (or
922  *   prom_serengeti_get_ecacheunum() for debugging purposes).
923  */
924 
925 static int
926 sg_prom_get_unum(int synd_code, uint64_t paddr, char *buf, int buflen,
927     int *lenp)
928 {
929 	if ((prom_get_unum(synd_code, (unsigned long long)paddr,
930 	    buf, buflen, lenp)) != 0)
931 		return (EIO);
932 	else if (*lenp <= 1)
933 		return (EINVAL);
934 	else
935 		return (0);
936 }
937 
938 /*ARGSUSED*/
939 int
940 plat_get_mem_unum(int synd_code, uint64_t flt_addr, int flt_bus_id,
941     int flt_in_memory, ushort_t flt_status, char *buf, int buflen, int *lenp)
942 {
943 	/*
944 	 * unum_func will either point to the memory drivers p2get_mem_unum()
945 	 * or to prom_get_unum() for memory errors.
946 	 */
947 	int (*unum_func)(int synd_code, uint64_t paddr, char *buf,
948 	    int buflen, int *lenp) = p2get_mem_unum;
949 
950 	/*
951 	 * check if it's a Memory or an Ecache error.
952 	 */
953 	if (flt_in_memory) {
954 		/*
955 		 * It's a main memory error.
956 		 *
957 		 * For debugging we allow the user to switch between
958 		 * using OBP's get_unum and the memory driver's get_unum
959 		 * so we create a pointer to the functions and switch
960 		 * depending on the sg_use_prom_get_unum flag.
961 		 */
962 		if (sg_use_prom_get_unum) {
963 			DCMNERR(CE_NOTE, "Using prom_get_unum from OBP");
964 			return (sg_prom_get_unum(synd_code,
965 			    P2ALIGN(flt_addr, 8), buf, buflen, lenp));
966 		} else if (unum_func != NULL) {
967 			return (unum_func(synd_code, P2ALIGN(flt_addr, 8),
968 			    buf, buflen, lenp));
969 		} else {
970 			return (ENOTSUP);
971 		}
972 	} else if (flt_status & ECC_ECACHE) {
973 		/*
974 		 * It's an E$ error.
975 		 */
976 		if (sg_use_prom_ecache_unum) {
977 			/*
978 			 * We call to OBP to handle this.
979 			 */
980 			DCMNERR(CE_NOTE,
981 			    "Using prom_serengeti_get_ecacheunum from OBP");
982 			if (prom_serengeti_get_ecacheunum(flt_bus_id,
983 			    P2ALIGN(flt_addr, 8), buf, buflen, lenp) != 0) {
984 				return (EIO);
985 			}
986 		} else {
987 			return (sg_get_ecacheunum(flt_bus_id, flt_addr,
988 			    buf, buflen, lenp));
989 		}
990 	} else {
991 		return (ENOTSUP);
992 	}
993 
994 	return (0);
995 }
996 
997 /*
998  * This platform hook gets called from mc_add_mem_unum_label() in the mc-us3
999  * driver giving each platform the opportunity to add platform
1000  * specific label information to the unum for ECC error logging purposes.
1001  */
1002 void
1003 plat_add_mem_unum_label(char *unum, int mcid, int bank, int dimm)
1004 {
1005 	char	new_unum[UNUM_NAMLEN] = "";
1006 	int	node = SG_PORTID_TO_NODEID(mcid);
1007 	int	board = SG_CPU_BD_PORTID_TO_BD_NUM(mcid);
1008 	int	position = SG_PORTID_TO_CPU_POSN(mcid);
1009 
1010 	/*
1011 	 * The mc-us3 driver deals with logical banks but for unum
1012 	 * purposes we need to use physical banks so that the correct
1013 	 * dimm can be physically located. Logical banks 0 and 2
1014 	 * make up physical bank 0. Logical banks 1 and 3 make up
1015 	 * physical bank 1. Here we do the necessary conversion.
1016 	 */
1017 	bank = (bank % 2);
1018 
1019 	if (dimm == -1) {
1020 		SG_SET_FRU_NAME_NODE(new_unum, node);
1021 		SG_SET_FRU_NAME_CPU_BOARD(new_unum, board);
1022 		SG_SET_FRU_NAME_MODULE(new_unum, position);
1023 		SG_SET_FRU_NAME_BANK(new_unum, bank);
1024 
1025 	} else {
1026 		SG_SET_FRU_NAME_NODE(new_unum, node);
1027 		SG_SET_FRU_NAME_CPU_BOARD(new_unum, board);
1028 		SG_SET_FRU_NAME_MODULE(new_unum, position);
1029 		SG_SET_FRU_NAME_BANK(new_unum, bank);
1030 		SG_SET_FRU_NAME_DIMM(new_unum, dimm);
1031 
1032 		strcat(new_unum, " ");
1033 		strcat(new_unum, unum);
1034 	}
1035 
1036 	strcpy(unum, new_unum);
1037 }
1038 
1039 int
1040 plat_get_cpu_unum(int cpuid, char *buf, int buflen, int *lenp)
1041 {
1042 	int	node = SG_PORTID_TO_NODEID(cpuid);
1043 	int	board = SG_CPU_BD_PORTID_TO_BD_NUM(cpuid);
1044 
1045 	if (snprintf(buf, buflen, "/N%d/%s%d", node,
1046 	    SG_HPU_TYPE_CPU_BOARD_ID, board) >= buflen) {
1047 		return (ENOSPC);
1048 	} else {
1049 		*lenp = strlen(buf);
1050 		return (0);
1051 	}
1052 }
1053 
1054 static void (*sg_ecc_taskq_func)(sbbc_ecc_mbox_t *) = NULL;
1055 static int (*sg_ecc_mbox_func)(sbbc_ecc_mbox_t *) = NULL;
1056 
1057 /*
1058  * We log all ECC errors to the SC so we send a mailbox
1059  * message to the SC passing it the relevant data.
1060  * ECC mailbox messages are sent via a taskq mechanism to
1061  * prevent impaired system performance during ECC floods.
1062  * Indictments have already passed through a taskq, so they
1063  * are not queued here.
1064  */
1065 int
1066 plat_send_ecc_mailbox_msg(plat_ecc_message_type_t msg_type, void *datap)
1067 {
1068 	sbbc_ecc_mbox_t	*msgp;
1069 	uint16_t	msg_subtype;
1070 	int		sleep_flag, log_error;
1071 	size_t		msg_size;
1072 
1073 	if (sg_ecc_taskq_func == NULL) {
1074 		sg_ecc_taskq_func = (void (*)(sbbc_ecc_mbox_t *))
1075 		    modgetsymvalue("sbbc_mbox_queue_ecc_event", 0);
1076 		if (sg_ecc_taskq_func == NULL) {
1077 			cmn_err(CE_NOTE, "!plat_send_ecc_mailbox_msg: "
1078 			    "sbbc_mbox_queue_ecc_event not found");
1079 			return (ENODEV);
1080 		}
1081 	}
1082 	if (sg_ecc_mbox_func == NULL) {
1083 		sg_ecc_mbox_func = (int (*)(sbbc_ecc_mbox_t *))
1084 		    modgetsymvalue("sbbc_mbox_ecc_output", 0);
1085 		if (sg_ecc_mbox_func == NULL) {
1086 			cmn_err(CE_NOTE, "!plat_send_ecc_mailbox_msg: "
1087 			    "sbbc_mbox_ecc_output not found");
1088 			return (ENODEV);
1089 		}
1090 	}
1091 
1092 	/*
1093 	 * Initialize the request and response structures
1094 	 */
1095 	switch (msg_type) {
1096 	case PLAT_ECC_ERROR_MESSAGE:
1097 		msg_subtype = INFO_MBOX_ERROR_ECC;
1098 		msg_size = sizeof (plat_ecc_error_data_t);
1099 		sleep_flag = KM_NOSLEEP;
1100 		log_error = 1;
1101 		break;
1102 	case PLAT_ECC_ERROR2_MESSAGE:
1103 		msg_subtype = INFO_MBOX_ECC;
1104 		msg_size = sizeof (plat_ecc_error2_data_t);
1105 		sleep_flag = KM_NOSLEEP;
1106 		log_error = 1;
1107 		break;
1108 	case PLAT_ECC_INDICTMENT_MESSAGE:
1109 		msg_subtype = INFO_MBOX_ERROR_INDICT;
1110 		msg_size = sizeof (plat_ecc_indictment_data_t);
1111 		sleep_flag = KM_SLEEP;
1112 		log_error = 0;
1113 		break;
1114 	case PLAT_ECC_INDICTMENT2_MESSAGE:
1115 		msg_subtype = INFO_MBOX_ECC;
1116 		msg_size = sizeof (plat_ecc_indictment2_data_t);
1117 		sleep_flag = KM_SLEEP;
1118 		log_error = 0;
1119 		break;
1120 	case PLAT_ECC_CAPABILITY_MESSAGE:
1121 		msg_subtype = INFO_MBOX_ECC_CAP;
1122 		msg_size = sizeof (plat_capability_data_t) +
1123 		    strlen(utsname.release) + strlen(utsname.version) + 2;
1124 		sleep_flag = KM_SLEEP;
1125 		log_error = 0;
1126 		break;
1127 	case PLAT_ECC_DIMM_SID_MESSAGE:
1128 		msg_subtype = INFO_MBOX_ECC;
1129 		msg_size = sizeof (plat_dimm_sid_request_data_t);
1130 		sleep_flag = KM_SLEEP;
1131 		log_error = 0;
1132 		break;
1133 	default:
1134 		return (EINVAL);
1135 	}
1136 
1137 	msgp = (sbbc_ecc_mbox_t *)kmem_zalloc(sizeof (sbbc_ecc_mbox_t),
1138 		sleep_flag);
1139 	if (msgp == NULL) {
1140 		cmn_err(CE_NOTE, "!plat_send_ecc_mailbox_msg: "
1141 				"unable to allocate sbbc_ecc_mbox");
1142 		return (ENOMEM);
1143 	}
1144 
1145 	msgp->ecc_log_error = log_error;
1146 
1147 	msgp->ecc_req.msg_type.type = INFO_MBOX;
1148 	msgp->ecc_req.msg_type.sub_type = msg_subtype;
1149 	msgp->ecc_req.msg_status = 0;
1150 	msgp->ecc_req.msg_len = (int)msg_size;
1151 	msgp->ecc_req.msg_bytes = 0;
1152 	msgp->ecc_req.msg_buf = (caddr_t)kmem_zalloc(msg_size, sleep_flag);
1153 	msgp->ecc_req.msg_data[0] = 0;
1154 	msgp->ecc_req.msg_data[1] = 0;
1155 
1156 	if (msgp->ecc_req.msg_buf == NULL) {
1157 		cmn_err(CE_NOTE, "!plat_send_ecc_mailbox_msg: "
1158 				"unable to allocate request msg_buf");
1159 		kmem_free((void *)msgp, sizeof (sbbc_ecc_mbox_t));
1160 		return (ENOMEM);
1161 	}
1162 
1163 	bcopy(datap, (void *)msgp->ecc_req.msg_buf, msg_size);
1164 
1165 	/*
1166 	 * initialize the response back from the SC
1167 	 */
1168 	msgp->ecc_resp.msg_type.type = INFO_MBOX;
1169 	msgp->ecc_resp.msg_type.sub_type = msg_subtype;
1170 	msgp->ecc_resp.msg_status = 0;
1171 	msgp->ecc_resp.msg_len = 0;
1172 	msgp->ecc_resp.msg_bytes = 0;
1173 	msgp->ecc_resp.msg_buf = NULL;
1174 	msgp->ecc_resp.msg_data[0] = 0;
1175 	msgp->ecc_resp.msg_data[1] = 0;
1176 
1177 	switch (msg_type) {
1178 	case PLAT_ECC_ERROR_MESSAGE:
1179 	case PLAT_ECC_ERROR2_MESSAGE:
1180 		/*
1181 		 * For Error Messages, we go through a taskq.
1182 		 * Queue up message for processing
1183 		 */
1184 		(*sg_ecc_taskq_func)(msgp);
1185 		return (0);
1186 
1187 	case PLAT_ECC_CAPABILITY_MESSAGE:
1188 		/*
1189 		 * For indictment and capability messages, we've already gone
1190 		 * through the taskq, so we can call the mailbox routine
1191 		 * directly.  Find the symbol for the routine that sends
1192 		 * the mailbox msg
1193 		 */
1194 		msgp->ecc_resp.msg_len = (int)msg_size;
1195 		msgp->ecc_resp.msg_buf = (caddr_t)kmem_zalloc(msg_size,
1196 		    sleep_flag);
1197 		/* FALLTHRU */
1198 
1199 	case PLAT_ECC_INDICTMENT_MESSAGE:
1200 	case PLAT_ECC_INDICTMENT2_MESSAGE:
1201 		return ((*sg_ecc_mbox_func)(msgp));
1202 
1203 	case PLAT_ECC_DIMM_SID_MESSAGE:
1204 		msgp->ecc_resp.msg_len = sizeof (plat_dimm_sid_board_data_t);
1205 		msgp->ecc_resp.msg_buf = (caddr_t)kmem_zalloc(
1206 		    sizeof (plat_dimm_sid_board_data_t), sleep_flag);
1207 
1208 		return ((*sg_ecc_mbox_func)(msgp));
1209 
1210 	default:
1211 		ASSERT(0);
1212 		return (EINVAL);
1213 	}
1214 }
1215 
1216 /*
1217  * m is redundant on serengeti as the multiplyer is always 4
1218  */
1219 /*ARGSUSED*/
1220 int
1221 plat_make_fru_cpuid(int sb, int m, int proc)
1222 {
1223 	return (MAKE_CPUID(sb, proc));
1224 }
1225 
1226 /*
1227  * board number for a given proc
1228  */
1229 int
1230 plat_make_fru_boardnum(int proc)
1231 {
1232 	return (SG_PORTID_TO_BOARD_NUM(proc));
1233 }
1234 
1235 static
1236 void
1237 cpu_sgn_update(ushort_t sig, uchar_t state, uchar_t sub_state, int cpuid)
1238 {
1239 	uint32_t signature = CPU_SIG_BLD(sig, state, sub_state);
1240 	sig_state_t current_sgn;
1241 	int i;
1242 
1243 	if (iosram_write_ptr == NULL) {
1244 		/*
1245 		 * If the IOSRAM write pointer isn't set, we won't be able
1246 		 * to write signatures to ANYTHING, so we may as well just
1247 		 * write out an error message (if desired) and exit this
1248 		 * routine now...
1249 		 */
1250 		DCMNERR(CE_WARN,
1251 		    "cpu_sgn_update: iosram_write() not found;"
1252 		    " cannot write signature 0x%x for CPU(s) or domain\n",
1253 		    signature);
1254 		return;
1255 	}
1256 
1257 
1258 	/*
1259 	 * Differentiate a panic reboot from a non-panic reboot in the
1260 	 * setting of the substate of the signature.
1261 	 *
1262 	 * If the new substate is REBOOT and we're rebooting due to a panic,
1263 	 * then set the new substate to a special value indicating a panic
1264 	 * reboot, SIGSUBST_PANIC_REBOOT.
1265 	 *
1266 	 * A panic reboot is detected by a current (previous) domain signature
1267 	 * state of SIGST_EXIT, and a new signature substate of SIGSUBST_REBOOT.
1268 	 * The domain signature state SIGST_EXIT is used as the panic flow
1269 	 * progresses.
1270 	 *
1271 	 * At the end of the panic flow, the reboot occurs but we should now
1272 	 * one that was involuntary, something that may be quite useful to know
1273 	 * at OBP level.
1274 	 */
1275 	if (sub_state == SIGSUBST_REBOOT) {
1276 		if (iosram_read_ptr == NULL) {
1277 			DCMNERR(CE_WARN,
1278 			    "cpu_sgn_update: iosram_read() not found;"
1279 			    " could not check current domain signature\n");
1280 		} else {
1281 			(void) (*iosram_read_ptr)(SBBC_SIGBLCK_KEY,
1282 				SG_SGNBLK_DOMAINSIG_OFFSET,
1283 				(char *)&current_sgn, sizeof (current_sgn));
1284 			if (current_sgn.state_t.state == SIGST_EXIT)
1285 				signature = CPU_SIG_BLD(sig, state,
1286 					SIGSUBST_PANIC_REBOOT);
1287 		}
1288 	}
1289 
1290 	/*
1291 	 * cpuid == -1 indicates that the operation applies to all cpus.
1292 	 */
1293 	if (cpuid >= 0) {
1294 		(void) (*iosram_write_ptr)(SBBC_SIGBLCK_KEY,
1295 			SG_SGNBLK_CPUSIG_OFFSET(cpuid), (char *)&signature,
1296 			sizeof (signature));
1297 	} else {
1298 		for (i = 0; i < NCPU; i++) {
1299 			if (cpu[i] == NULL || !(cpu[i]->cpu_flags &
1300 				(CPU_EXISTS|CPU_QUIESCED))) {
1301 				continue;
1302 			}
1303 			(void) (*iosram_write_ptr)(SBBC_SIGBLCK_KEY,
1304 				SG_SGNBLK_CPUSIG_OFFSET(i), (char *)&signature,
1305 				sizeof (signature));
1306 		}
1307 	}
1308 
1309 	if (state == SIGST_OFFLINE || state == SIGST_DETACHED) {
1310 		return;
1311 	}
1312 
1313 	(void) (*iosram_write_ptr)(SBBC_SIGBLCK_KEY,
1314 		SG_SGNBLK_DOMAINSIG_OFFSET, (char *)&signature,
1315 		sizeof (signature));
1316 }
1317 
1318 void
1319 startup_platform(void)
1320 {
1321 }
1322 
1323 /*
1324  * A routine to convert a number (represented as a string) to
1325  * the integer value it represents.
1326  */
1327 
1328 static int
1329 isdigit(int ch)
1330 {
1331 	return (ch >= '0' && ch <= '9');
1332 }
1333 
1334 #define	isspace(c)	((c) == ' ' || (c) == '\t' || (c) == '\n')
1335 
1336 static int
1337 strtoi(char *p, char **pos)
1338 {
1339 	int n;
1340 	int c, neg = 0;
1341 
1342 	if (!isdigit(c = *p)) {
1343 		while (isspace(c))
1344 			c = *++p;
1345 		switch (c) {
1346 			case '-':
1347 				neg++;
1348 				/* FALLTHROUGH */
1349 			case '+':
1350 			c = *++p;
1351 		}
1352 		if (!isdigit(c)) {
1353 			if (pos != NULL)
1354 				*pos = p;
1355 			return (0);
1356 		}
1357 	}
1358 	for (n = '0' - c; isdigit(c = *++p); ) {
1359 		n *= 10; /* two steps to avoid unnecessary overflow */
1360 		n += '0' - c; /* accum neg to avoid surprises at MAX */
1361 	}
1362 	if (pos != NULL)
1363 		*pos = p;
1364 	return (neg ? n : -n);
1365 }
1366 
1367 /*
1368  * Get the three parts of the Serengeti PROM version.
1369  * Used for feature readiness tests.
1370  *
1371  * Return 0 if version extracted successfully, -1 otherwise.
1372  */
1373 
1374 int
1375 sg_get_prom_version(int *sysp, int *intfp, int *bldp)
1376 {
1377 	int plen;
1378 	char vers[512];
1379 	static pnode_t node;
1380 	static char version[] = "version";
1381 	char *verp, *ep;
1382 
1383 	node = prom_finddevice("/openprom");
1384 	if (node == OBP_BADNODE)
1385 		return (-1);
1386 
1387 	plen = prom_getproplen(node, version);
1388 	if (plen <= 0 || plen >= sizeof (vers))
1389 		return (-1);
1390 	(void) prom_getprop(node, version, vers);
1391 	vers[plen] = '\0';
1392 
1393 	/* Make sure it's an OBP flashprom */
1394 	if (vers[0] != 'O' && vers[1] != 'B' && vers[2] != 'P') {
1395 		cmn_err(CE_WARN, "sg_get_prom_version: "
1396 		    "unknown <version> string in </openprom>\n");
1397 		return (-1);
1398 	}
1399 	verp = &vers[4];
1400 
1401 	*sysp = strtoi(verp, &ep);
1402 	if (ep == verp || *ep != '.')
1403 		return (-1);
1404 	verp = ep + 1;
1405 
1406 	*intfp = strtoi(verp, &ep);
1407 	if (ep == verp || *ep != '.')
1408 		return (-1);
1409 	verp = ep + 1;
1410 
1411 	*bldp = strtoi(verp, &ep);
1412 	if (ep == verp || (*ep != '\0' && !isspace(*ep)))
1413 		return (-1);
1414 	return (0);
1415 }
1416 
1417 /*
1418  * Return 0 if system board Dynamic Reconfiguration
1419  * is supported by the firmware, -1 otherwise.
1420  */
1421 int
1422 sg_prom_sb_dr_check(void)
1423 {
1424 	static int prom_res = 1;
1425 
1426 	if (prom_res == 1) {
1427 		int sys, intf, bld;
1428 		int rv;
1429 
1430 		rv = sg_get_prom_version(&sys, &intf, &bld);
1431 		if (rv == 0 && sys == 5 &&
1432 		    (intf >= 12 || (intf == 11 && bld >= 200))) {
1433 			prom_res = 0;
1434 		} else {
1435 			prom_res = -1;
1436 		}
1437 	}
1438 	return (prom_res);
1439 }
1440 
1441 /*
1442  * Return 0 if cPCI Dynamic Reconfiguration
1443  * is supported by the firmware, -1 otherwise.
1444  */
1445 int
1446 sg_prom_cpci_dr_check(void)
1447 {
1448 	/*
1449 	 * The version check is currently the same as for
1450 	 * system boards. Since the two DR sub-systems are
1451 	 * independent, this could change.
1452 	 */
1453 	return (sg_prom_sb_dr_check());
1454 }
1455 
1456 /*
1457  * Our implementation of this KDI op updates the CPU signature in the system
1458  * controller.  Note that we set the signature to OBP_SIG, rather than DBG_SIG.
1459  * The Forth words we execute will, among other things, transform our OBP_SIG
1460  * into DBG_SIG.  They won't function properly if we try to use DBG_SIG.
1461  */
1462 static void
1463 sg_system_claim(void)
1464 {
1465 	prom_interpret("sigb-sig! my-sigb-sig!", OBP_SIG, OBP_SIG, 0, 0, 0);
1466 }
1467 
1468 static void
1469 sg_system_release(void)
1470 {
1471 	prom_interpret("sigb-sig! my-sigb-sig!", OS_SIG, OS_SIG, 0, 0, 0);
1472 }
1473 
1474 static void
1475 sg_console_claim(void)
1476 {
1477 	prom_serengeti_set_console_input(SGCN_OBP_STR);
1478 }
1479 
1480 static void
1481 sg_console_release(void)
1482 {
1483 	prom_serengeti_set_console_input(SGCN_CLNT_STR);
1484 }
1485 
1486 void
1487 plat_kdi_init(kdi_t *kdi)
1488 {
1489 	kdi->pkdi_system_claim = sg_system_claim;
1490 	kdi->pkdi_system_release = sg_system_release;
1491 	kdi->pkdi_console_claim = sg_console_claim;
1492 	kdi->pkdi_console_release = sg_console_release;
1493 }
1494