xref: /onnv-gate/usr/src/uts/sun4u/serengeti/os/serengeti.c (revision 11311:639e7bc0b42f)
11708Sstevel /*
21708Sstevel  * CDDL HEADER START
31708Sstevel  *
41708Sstevel  * The contents of this file are subject to the terms of the
52220Sstevel  * Common Development and Distribution License (the "License").
62220Sstevel  * You may not use this file except in compliance with the License.
71708Sstevel  *
81708Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91708Sstevel  * or http://www.opensolaris.org/os/licensing.
101708Sstevel  * See the License for the specific language governing permissions
111708Sstevel  * and limitations under the License.
121708Sstevel  *
131708Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
141708Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151708Sstevel  * If applicable, add the following below this CDDL HEADER, with the
161708Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
171708Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
181708Sstevel  *
191708Sstevel  * CDDL HEADER END
201708Sstevel  */
212220Sstevel 
221708Sstevel /*
2311066Srafael.vanoni@sun.com  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
241708Sstevel  * Use is subject to license terms.
251708Sstevel  */
261708Sstevel 
271708Sstevel #include <sys/time.h>
281708Sstevel #include <sys/cpuvar.h>
291708Sstevel #include <sys/dditypes.h>
301708Sstevel #include <sys/ddipropdefs.h>
311708Sstevel #include <sys/ddi_impldefs.h>
321708Sstevel #include <sys/sunddi.h>
331708Sstevel #include <sys/esunddi.h>
341708Sstevel #include <sys/sunndi.h>
351708Sstevel #include <sys/platform_module.h>
361708Sstevel #include <sys/errno.h>
371708Sstevel #include <sys/conf.h>
381708Sstevel #include <sys/modctl.h>
391708Sstevel #include <sys/promif.h>
401708Sstevel #include <sys/promimpl.h>
411708Sstevel #include <sys/prom_plat.h>
421708Sstevel #include <sys/cmn_err.h>
431708Sstevel #include <sys/sysmacros.h>
441708Sstevel #include <sys/mem_cage.h>
451708Sstevel #include <sys/kobj.h>
461708Sstevel #include <sys/utsname.h>
471708Sstevel #include <sys/cpu_sgnblk_defs.h>
481708Sstevel #include <sys/atomic.h>
491708Sstevel #include <sys/kdi_impl.h>
501708Sstevel 
511708Sstevel #include <sys/sgsbbc.h>
521708Sstevel #include <sys/sgsbbc_iosram.h>
531708Sstevel #include <sys/sgsbbc_iosram_priv.h>
541708Sstevel #include <sys/sgsbbc_mailbox.h>
551708Sstevel #include <sys/sgsgn.h>
561708Sstevel #include <sys/sgcn.h>
571708Sstevel #include <sys/serengeti.h>
581708Sstevel #include <sys/sgfrutypes.h>
591708Sstevel #include <sys/machsystm.h>
601708Sstevel #include <sys/sbd_ioctl.h>
611708Sstevel #include <sys/sbd.h>
621708Sstevel #include <sys/sbdp_mem.h>
631708Sstevel 
641708Sstevel #include <sys/memnode.h>
651708Sstevel #include <vm/vm_dep.h>
661708Sstevel #include <vm/page.h>
671708Sstevel 
681708Sstevel #include <sys/cheetahregs.h>
691708Sstevel #include <sys/plat_ecc_unum.h>
701708Sstevel #include <sys/plat_ecc_dimm.h>
711708Sstevel 
721708Sstevel #include <sys/lgrp.h>
7311066Srafael.vanoni@sun.com #include <sys/clock_impl.h>
741708Sstevel 
751708Sstevel static int sg_debug = 0;
761708Sstevel 
771708Sstevel #ifdef DEBUG
781708Sstevel #define	DCMNERR if (sg_debug) cmn_err
791708Sstevel #else
801708Sstevel #define	DCMNERR
811708Sstevel #endif
821708Sstevel 
831708Sstevel int (*p2get_mem_unum)(int, uint64_t, char *, int, int *);
841708Sstevel 
851708Sstevel /* local functions */
861708Sstevel static void cpu_sgn_update(ushort_t sgn, uchar_t state,
871708Sstevel     uchar_t sub_state, int cpuid);
881708Sstevel 
891708Sstevel 
901708Sstevel /*
911708Sstevel  * Local data.
921708Sstevel  *
931708Sstevel  * iosram_write_ptr is a pointer to iosram_write().  Because of
941708Sstevel  * kernel dynamic linking, we can't get to the function by name,
951708Sstevel  * but we can look up its address, and store it in this variable
961708Sstevel  * instead.
971708Sstevel  *
981708Sstevel  * We include the extern for iosram_write() here not because we call
991708Sstevel  * it, but to force compilation errors if its prototype doesn't
1001708Sstevel  * match the prototype of iosram_write_ptr.
1011708Sstevel  *
1021708Sstevel  * The same issues apply to iosram_read() and iosram_read_ptr.
1031708Sstevel  */
1041708Sstevel /*CSTYLED*/
1051708Sstevel extern int   iosram_write     (int, uint32_t, caddr_t, uint32_t);
1061708Sstevel static int (*iosram_write_ptr)(int, uint32_t, caddr_t, uint32_t) = NULL;
1071708Sstevel /*CSTYLED*/
1081708Sstevel extern int   iosram_read     (int, uint32_t, caddr_t, uint32_t);
1091708Sstevel static int (*iosram_read_ptr)(int, uint32_t, caddr_t, uint32_t) = NULL;
1101708Sstevel 
1111708Sstevel 
1121708Sstevel /*
1131708Sstevel  * Variable to indicate if the date should be obtained from the SC or not.
1141708Sstevel  */
1151708Sstevel int todsg_use_sc = FALSE;	/* set the false at the beginning */
1161708Sstevel 
1171708Sstevel /*
1181708Sstevel  * Preallocation of spare tsb's for DR
1191708Sstevel  *
1201708Sstevel  * We don't allocate spares for Wildcat since TSBs should come
1211708Sstevel  * out of memory local to the node.
1221708Sstevel  */
1231708Sstevel #define	IOMMU_PER_SCHIZO	2
1241708Sstevel int serengeti_tsb_spares = (SG_MAX_IO_BDS * SG_SCHIZO_PER_IO_BD *
1251708Sstevel 	IOMMU_PER_SCHIZO);
1261708Sstevel 
1271708Sstevel /*
1282241Shuah  * sg_max_ncpus is the maximum number of CPUs supported on Serengeti.
1292241Shuah  * sg_max_ncpus is set to be smaller than NCPU to reduce the amount of
1302241Shuah  * memory the logs take up until we have a dynamic log memory allocation
1312241Shuah  * solution.
1321708Sstevel  */
1332241Shuah int sg_max_ncpus = (24 * 2);    /* (max # of processors * # of cores/proc) */
1341708Sstevel 
1351708Sstevel /*
1361708Sstevel  * variables to control mailbox message timeouts.
1371708Sstevel  * These can be patched via /etc/system or mdb.
1381708Sstevel  */
1391708Sstevel int	sbbc_mbox_default_timeout = MBOX_DEFAULT_TIMEOUT;
1401708Sstevel int	sbbc_mbox_min_timeout = MBOX_MIN_TIMEOUT;
1411708Sstevel 
1421708Sstevel /* cached 'chosen' node_id */
1431708Sstevel pnode_t chosen_nodeid = (pnode_t)0;
1441708Sstevel 
1451708Sstevel static void (*sg_ecc_taskq_func)(sbbc_ecc_mbox_t *) = NULL;
1461708Sstevel static int (*sg_ecc_mbox_func)(sbbc_ecc_mbox_t *) = NULL;
1471708Sstevel 
1481708Sstevel /*
1491708Sstevel  * Table that maps memory slices to a specific memnode.
1501708Sstevel  */
1511708Sstevel int slice_to_memnode[SG_MAX_SLICE];
1521708Sstevel 
1531708Sstevel plat_dimm_sid_board_t	domain_dimm_sids[SG_MAX_CPU_BDS];
1541708Sstevel 
1551708Sstevel 
1561708Sstevel int
set_platform_tsb_spares()1571708Sstevel set_platform_tsb_spares()
1581708Sstevel {
1591708Sstevel 	return (MIN(serengeti_tsb_spares, MAX_UPA));
1601708Sstevel }
1611708Sstevel 
1621708Sstevel #pragma weak mmu_init_large_pages
1631708Sstevel 
1641708Sstevel void
set_platform_defaults(void)1651708Sstevel set_platform_defaults(void)
1661708Sstevel {
1671708Sstevel 	extern int watchdog_enable;
1681708Sstevel 	extern uint64_t xc_tick_limit_scale;
1691708Sstevel 	extern void mmu_init_large_pages(size_t);
1701708Sstevel 
1711708Sstevel #ifdef DEBUG
1721708Sstevel 	char *todsg_name = "todsg";
1731708Sstevel 	ce_verbose_memory = 2;
1741708Sstevel 	ce_verbose_other = 2;
1751708Sstevel #endif /* DEBUG */
1761708Sstevel 
1771708Sstevel 	watchdog_enable = TRUE;
1781708Sstevel 	watchdog_available = TRUE;
1791708Sstevel 
1801708Sstevel 	cpu_sgn_func = cpu_sgn_update;
1811708Sstevel 
1821708Sstevel #ifdef DEBUG
1831708Sstevel 	/* tod_module_name should be set to "todsg" from OBP property */
1841708Sstevel 	if (tod_module_name && (strcmp(tod_module_name, todsg_name) == 0))
1851708Sstevel 		prom_printf("Using todsg driver\n");
1861708Sstevel 	else {
1871708Sstevel 		prom_printf("Force using todsg driver\n");
1881708Sstevel 		tod_module_name = todsg_name;
1891708Sstevel 	}
1901708Sstevel #endif /* DEBUG */
1911708Sstevel 
1921708Sstevel 	/* Serengeti does not support forthdebug */
1931708Sstevel 	forthdebug_supported = 0;
1941708Sstevel 
1951708Sstevel 
1961708Sstevel 	/*
1971708Sstevel 	 * Some DR operations require the system to be sync paused.
1981708Sstevel 	 * Sync pause on Serengeti could potentially take up to 4
1991708Sstevel 	 * seconds to complete depending on the load on the SC.  To
2001708Sstevel 	 * avoid send_mond panics during such operations, we need to
2011708Sstevel 	 * increase xc_tick_limit to a larger value on Serengeti by
2021708Sstevel 	 * setting xc_tick_limit_scale to 5.
2031708Sstevel 	 */
2041708Sstevel 	xc_tick_limit_scale = 5;
2051708Sstevel 
2061708Sstevel 	if ((mmu_page_sizes == max_mmu_page_sizes) &&
2072659Ssusans 	    (mmu_ism_pagesize != DEFAULT_ISM_PAGESIZE)) {
2081708Sstevel 		if (&mmu_init_large_pages)
2091708Sstevel 			mmu_init_large_pages(mmu_ism_pagesize);
2101708Sstevel 	}
2111708Sstevel }
2121708Sstevel 
2131708Sstevel void
load_platform_modules(void)2141708Sstevel load_platform_modules(void)
2151708Sstevel {
2161708Sstevel 	if (modload("misc", "pcihp") < 0) {
2171708Sstevel 		cmn_err(CE_NOTE, "pcihp driver failed to load");
2181708Sstevel 	}
2191708Sstevel }
2201708Sstevel 
2211708Sstevel /*ARGSUSED*/
2221708Sstevel int
plat_cpu_poweron(struct cpu * cp)2231708Sstevel plat_cpu_poweron(struct cpu *cp)
2241708Sstevel {
2251708Sstevel 	int (*serengeti_cpu_poweron)(struct cpu *) = NULL;
2261708Sstevel 
2271708Sstevel 	serengeti_cpu_poweron =
2281708Sstevel 	    (int (*)(struct cpu *))modgetsymvalue("sbdp_cpu_poweron", 0);
2291708Sstevel 
2301708Sstevel 	if (serengeti_cpu_poweron == NULL)
2311708Sstevel 		return (ENOTSUP);
2321708Sstevel 	else
2331708Sstevel 		return ((serengeti_cpu_poweron)(cp));
2341708Sstevel }
2351708Sstevel 
2361708Sstevel /*ARGSUSED*/
2371708Sstevel int
plat_cpu_poweroff(struct cpu * cp)2381708Sstevel plat_cpu_poweroff(struct cpu *cp)
2391708Sstevel {
2401708Sstevel 	int (*serengeti_cpu_poweroff)(struct cpu *) = NULL;
2411708Sstevel 
2421708Sstevel 	serengeti_cpu_poweroff =
2431708Sstevel 	    (int (*)(struct cpu *))modgetsymvalue("sbdp_cpu_poweroff", 0);
2441708Sstevel 
2451708Sstevel 	if (serengeti_cpu_poweroff == NULL)
2461708Sstevel 		return (ENOTSUP);
2471708Sstevel 	else
2481708Sstevel 		return ((serengeti_cpu_poweroff)(cp));
2491708Sstevel }
2501708Sstevel 
2511708Sstevel #ifdef DEBUG
2521708Sstevel pgcnt_t serengeti_cage_size_limit;
2531708Sstevel #endif
2541708Sstevel 
2551708Sstevel /* Preferred minimum cage size (expressed in pages)... for DR */
2561708Sstevel pgcnt_t serengeti_minimum_cage_size = 0;
2571708Sstevel 
2581708Sstevel void
set_platform_cage_params(void)2591708Sstevel set_platform_cage_params(void)
2601708Sstevel {
2611708Sstevel 	extern pgcnt_t total_pages;
2621708Sstevel 	extern struct memlist *phys_avail;
2631708Sstevel 
2641708Sstevel 	if (kernel_cage_enable) {
2651708Sstevel 		pgcnt_t preferred_cage_size;
2661708Sstevel 
2671708Sstevel 		preferred_cage_size =
2681708Sstevel 		    MAX(serengeti_minimum_cage_size, total_pages / 256);
2691708Sstevel #ifdef DEBUG
2701708Sstevel 		if (serengeti_cage_size_limit)
2711708Sstevel 			preferred_cage_size = serengeti_cage_size_limit;
2721708Sstevel #endif
2731708Sstevel 		/*
2741708Sstevel 		 * Post copies obp into the lowest slice.  This requires the
2751708Sstevel 		 * cage to grow upwards
2761708Sstevel 		 */
2774266Sdp78419 		kcage_range_init(phys_avail, KCAGE_UP, preferred_cage_size);
2781708Sstevel 	}
2791708Sstevel 
2805872Ssetje 	kcage_startup_dir = KCAGE_UP;
2815872Ssetje 
2821708Sstevel 	/* Only note when the cage is off since it should always be on. */
2831708Sstevel 	if (!kcage_on)
2841708Sstevel 		cmn_err(CE_NOTE, "!DR Kernel Cage is DISABLED");
2851708Sstevel }
2861708Sstevel 
2871708Sstevel #define	ALIGN(x, a)	((a) == 0 ? (uint64_t)(x) : \
2881708Sstevel 	(((uint64_t)(x) + (uint64_t)(a) - 1l) & ~((uint64_t)(a) - 1l)))
2891708Sstevel 
2901708Sstevel void
update_mem_bounds(int brd,uint64_t base,uint64_t sz)2911708Sstevel update_mem_bounds(int brd, uint64_t base, uint64_t sz)
2921708Sstevel {
2931708Sstevel 	uint64_t	end;
2941708Sstevel 	int		mnode;
2951708Sstevel 
2961708Sstevel 	end = base + sz - 1;
2971708Sstevel 
2981708Sstevel 	/*
2991708Sstevel 	 * First see if this board already has a memnode associated
3001708Sstevel 	 * with it.  If not, see if this slice has a memnode.  This
3011708Sstevel 	 * covers the cases where a single slice covers multiple
3021708Sstevel 	 * boards (cross-board interleaving) and where a single
3031708Sstevel 	 * board has multiple slices (1+GB DIMMs).
3041708Sstevel 	 */
3051708Sstevel 	if ((mnode = plat_lgrphand_to_mem_node(brd)) == -1) {
3061708Sstevel 		if ((mnode = slice_to_memnode[PA_2_SLICE(base)]) == -1)
3071708Sstevel 			mnode = mem_node_alloc();
3081708Sstevel 		plat_assign_lgrphand_to_mem_node(brd, mnode);
3091708Sstevel 	}
3101708Sstevel 
3111708Sstevel 	/*
3121708Sstevel 	 * Align base at 16GB boundary
3131708Sstevel 	 */
3141708Sstevel 	base = ALIGN(base, (1ul << PA_SLICE_SHIFT));
3151708Sstevel 
3161708Sstevel 	while (base < end) {
3171708Sstevel 		slice_to_memnode[PA_2_SLICE(base)] = mnode;
3181708Sstevel 		base += (1ul << PA_SLICE_SHIFT);
3191708Sstevel 	}
3201708Sstevel }
3211708Sstevel 
3221708Sstevel /*
3231708Sstevel  * Dynamically detect memory slices in the system by decoding
3241708Sstevel  * the cpu memory decoder registers at boot time.
3251708Sstevel  */
3261708Sstevel void
plat_fill_mc(pnode_t nodeid)3271708Sstevel plat_fill_mc(pnode_t nodeid)
3281708Sstevel {
3291708Sstevel 	uint64_t	mc_addr, mask;
3301708Sstevel 	uint64_t	mc_decode[SG_MAX_BANKS_PER_MC];
3311708Sstevel 	uint64_t	base, size;
3321708Sstevel 	uint32_t	regs[4];
3331708Sstevel 	int		len;
3341708Sstevel 	int		local_mc;
3351708Sstevel 	int		portid;
3361708Sstevel 	int		boardid;
3371708Sstevel 	int		i;
3381708Sstevel 
3391708Sstevel 	if ((prom_getprop(nodeid, "portid", (caddr_t)&portid) < 0) ||
3401708Sstevel 	    (portid == -1))
3411708Sstevel 		return;
3421708Sstevel 
3431708Sstevel 	/*
3441708Sstevel 	 * Decode the board number from the MC portid
3451708Sstevel 	 */
3461708Sstevel 	boardid = SG_PORTID_TO_BOARD_NUM(portid);
3471708Sstevel 
3481708Sstevel 	/*
3491708Sstevel 	 * The "reg" property returns 4 32-bit values. The first two are
3501708Sstevel 	 * combined to form a 64-bit address.  The second two are for a
3511708Sstevel 	 * 64-bit size, but we don't actually need to look at that value.
3521708Sstevel 	 */
3531708Sstevel 	len = prom_getproplen(nodeid, "reg");
3541708Sstevel 	if (len != (sizeof (uint32_t) * 4)) {
3551708Sstevel 		prom_printf("Warning: malformed 'reg' property\n");
3561708Sstevel 		return;
3571708Sstevel 	}
3581708Sstevel 	if (prom_getprop(nodeid, "reg", (caddr_t)regs) < 0)
3591708Sstevel 		return;
3601708Sstevel 	mc_addr = ((uint64_t)regs[0]) << 32;
3611708Sstevel 	mc_addr |= (uint64_t)regs[1];
3621708Sstevel 
3631708Sstevel 	/*
3641708Sstevel 	 * Figure out whether the memory controller we are examining
3651708Sstevel 	 * belongs to this CPU or a different one.
3661708Sstevel 	 */
3671708Sstevel 	if (portid == cpunodes[CPU->cpu_id].portid)
3681708Sstevel 		local_mc = 1;
3691708Sstevel 	else
3701708Sstevel 		local_mc = 0;
3711708Sstevel 
3721708Sstevel 	for (i = 0; i < SG_MAX_BANKS_PER_MC; i++) {
3731708Sstevel 		mask = SG_REG_2_OFFSET(i);
3741708Sstevel 
3751708Sstevel 		/*
3761708Sstevel 		 * If the memory controller is local to this CPU, we use
3771708Sstevel 		 * the special ASI to read the decode registers.
3781708Sstevel 		 * Otherwise, we load the values from a magic address in
3791708Sstevel 		 * I/O space.
3801708Sstevel 		 */
3811708Sstevel 		if (local_mc)
3821708Sstevel 			mc_decode[i] = lddmcdecode(mask & MC_OFFSET_MASK);
3831708Sstevel 		else
3841708Sstevel 			mc_decode[i] = lddphysio((mc_addr | mask));
3851708Sstevel 
3861708Sstevel 		if (mc_decode[i] >> MC_VALID_SHIFT) {
3871708Sstevel 			/*
3881708Sstevel 			 * The memory decode register is a bitmask field,
3891708Sstevel 			 * so we can decode that into both a base and
3901708Sstevel 			 * a span.
3911708Sstevel 			 */
3921708Sstevel 			base = MC_BASE(mc_decode[i]) << PHYS2UM_SHIFT;
3931708Sstevel 			size = MC_UK2SPAN(mc_decode[i]);
3941708Sstevel 			update_mem_bounds(boardid, base, size);
3951708Sstevel 		}
3961708Sstevel 	}
3971708Sstevel }
3981708Sstevel 
3991708Sstevel /*
4001708Sstevel  * This routine is run midway through the boot process.  By the time we get
4011708Sstevel  * here, we know about all the active CPU boards in the system, and we have
4021708Sstevel  * extracted information about each board's memory from the memory
4031708Sstevel  * controllers.  We have also figured out which ranges of memory will be
4041708Sstevel  * assigned to which memnodes, so we walk the slice table to build the table
4051708Sstevel  * of memnodes.
4061708Sstevel  */
4071708Sstevel /* ARGSUSED */
4081708Sstevel void
plat_build_mem_nodes(prom_memlist_t * list,size_t nelems)4095648Ssetje plat_build_mem_nodes(prom_memlist_t *list, size_t  nelems)
4101708Sstevel {
4111708Sstevel 	int	slice;
4121708Sstevel 	pfn_t	basepfn;
4131708Sstevel 	pgcnt_t	npgs;
4141708Sstevel 
4151708Sstevel 	mem_node_pfn_shift = PFN_SLICE_SHIFT;
4161708Sstevel 	mem_node_physalign = (1ull << PA_SLICE_SHIFT);
4171708Sstevel 
4181708Sstevel 	for (slice = 0; slice < SG_MAX_SLICE; slice++) {
4191708Sstevel 		if (slice_to_memnode[slice] == -1)
4201708Sstevel 			continue;
4211708Sstevel 		basepfn = (uint64_t)slice << PFN_SLICE_SHIFT;
4221708Sstevel 		npgs = 1ull << PFN_SLICE_SHIFT;
4231708Sstevel 		mem_node_add_slice(basepfn, basepfn + npgs - 1);
4241708Sstevel 	}
4251708Sstevel }
4261708Sstevel 
4271708Sstevel int
plat_pfn_to_mem_node(pfn_t pfn)4281708Sstevel plat_pfn_to_mem_node(pfn_t pfn)
4291708Sstevel {
4301708Sstevel 	int node;
4311708Sstevel 
4321708Sstevel 	node = slice_to_memnode[PFN_2_SLICE(pfn)];
4331708Sstevel 
4341708Sstevel 	return (node);
4351708Sstevel }
4361708Sstevel 
4371708Sstevel /*
4381708Sstevel  * Serengeti support for lgroups.
4391708Sstevel  *
4401708Sstevel  * On Serengeti, an lgroup platform handle == board number.
4411708Sstevel  *
4421708Sstevel  * Mappings between lgroup handles and memnodes are managed
4431708Sstevel  * in addition to mappings between memory slices and memnodes
4441708Sstevel  * to support cross-board interleaving as well as multiple
4451708Sstevel  * slices per board (e.g. >1GB DIMMs). The initial mapping
4461708Sstevel  * of memnodes to lgroup handles is determined at boot time.
4471708Sstevel  * A DR addition of memory adds a new mapping. A DR copy-rename
4481708Sstevel  * swaps mappings.
4491708Sstevel  */
4501708Sstevel 
4511708Sstevel /*
4521708Sstevel  * Macro for extracting the board number from the CPU id
4531708Sstevel  */
4541708Sstevel #define	CPUID_TO_BOARD(id)	(((id) >> 2) & 0x7)
4551708Sstevel 
4561708Sstevel /*
4571708Sstevel  * Return the platform handle for the lgroup containing the given CPU
4581708Sstevel  *
4591708Sstevel  * For Serengeti, lgroup platform handle == board number
4601708Sstevel  */
4611708Sstevel lgrp_handle_t
plat_lgrp_cpu_to_hand(processorid_t id)4621708Sstevel plat_lgrp_cpu_to_hand(processorid_t id)
4631708Sstevel {
4641708Sstevel 	return (CPUID_TO_BOARD(id));
4651708Sstevel }
4661708Sstevel 
4671708Sstevel /*
4681708Sstevel  * Platform specific lgroup initialization
4691708Sstevel  */
4701708Sstevel void
plat_lgrp_init(void)4711708Sstevel plat_lgrp_init(void)
4721708Sstevel {
4731708Sstevel 	int i;
4741708Sstevel 	extern uint32_t lgrp_expand_proc_thresh;
4751708Sstevel 	extern uint32_t lgrp_expand_proc_diff;
4761708Sstevel 
4771708Sstevel 	/*
4781708Sstevel 	 * Initialize lookup tables to invalid values so we catch
4791708Sstevel 	 * any illegal use of them.
4801708Sstevel 	 */
4811708Sstevel 	for (i = 0; i < SG_MAX_SLICE; i++) {
4821708Sstevel 		slice_to_memnode[i] = -1;
4831708Sstevel 	}
4841708Sstevel 
4851708Sstevel 	/*
4861708Sstevel 	 * Set tuneables for Serengeti architecture
4871708Sstevel 	 *
4881708Sstevel 	 * lgrp_expand_proc_thresh is the minimum load on the lgroups
4891708Sstevel 	 * this process is currently running on before considering
4901708Sstevel 	 * expanding threads to another lgroup.
4911708Sstevel 	 *
4921708Sstevel 	 * lgrp_expand_proc_diff determines how much less the remote lgroup
4931708Sstevel 	 * must be loaded before expanding to it.
4941708Sstevel 	 *
4951708Sstevel 	 * Bandwidth is maximized on Serengeti by spreading load across
4961708Sstevel 	 * the machine. The impact to inter-thread communication isn't
4971708Sstevel 	 * too costly since remote latencies are relatively low.  These
4981708Sstevel 	 * values equate to one CPU's load and so attempt to spread the
4991708Sstevel 	 * load out across as many lgroups as possible one CPU at a time.
5001708Sstevel 	 */
5011708Sstevel 	lgrp_expand_proc_thresh = LGRP_LOADAVG_THREAD_MAX;
5021708Sstevel 	lgrp_expand_proc_diff = LGRP_LOADAVG_THREAD_MAX;
5031708Sstevel }
5041708Sstevel 
5051708Sstevel /*
5061708Sstevel  * Platform notification of lgroup (re)configuration changes
5071708Sstevel  */
5081708Sstevel /*ARGSUSED*/
5091708Sstevel void
plat_lgrp_config(lgrp_config_flag_t evt,uintptr_t arg)5101708Sstevel plat_lgrp_config(lgrp_config_flag_t evt, uintptr_t arg)
5111708Sstevel {
5121708Sstevel 	update_membounds_t	*umb;
5131708Sstevel 	lgrp_config_mem_rename_t lmr;
5141708Sstevel 	lgrp_handle_t		shand, thand;
5151708Sstevel 	int			snode, tnode;
5161708Sstevel 
5171708Sstevel 	switch (evt) {
5181708Sstevel 
5191708Sstevel 	case LGRP_CONFIG_MEM_ADD:
5201708Sstevel 		umb = (update_membounds_t *)arg;
5211708Sstevel 		update_mem_bounds(umb->u_board, umb->u_base, umb->u_len);
5221708Sstevel 
5231708Sstevel 		break;
5241708Sstevel 
5251708Sstevel 	case LGRP_CONFIG_MEM_DEL:
5261708Sstevel 		/* We don't have to do anything */
5271708Sstevel 		break;
5281708Sstevel 
5291708Sstevel 	case LGRP_CONFIG_MEM_RENAME:
5301708Sstevel 		/*
5311708Sstevel 		 * During a DR copy-rename operation, all of the memory
5321708Sstevel 		 * on one board is moved to another board -- but the
5331708Sstevel 		 * addresses/pfns and memnodes don't change. This means
5341708Sstevel 		 * the memory has changed locations without changing identity.
5351708Sstevel 		 *
5361708Sstevel 		 * Source is where we are copying from and target is where we
5371708Sstevel 		 * are copying to.  After source memnode is copied to target
5381708Sstevel 		 * memnode, the physical addresses of the target memnode are
5391708Sstevel 		 * renamed to match what the source memnode had.  Then target
5401708Sstevel 		 * memnode can be removed and source memnode can take its
5411708Sstevel 		 * place.
5421708Sstevel 		 *
5431708Sstevel 		 * To do this, swap the lgroup handle to memnode mappings for
5441708Sstevel 		 * the boards, so target lgroup will have source memnode and
5451708Sstevel 		 * source lgroup will have empty target memnode which is where
5461708Sstevel 		 * its memory will go (if any is added to it later).
5471708Sstevel 		 *
5481708Sstevel 		 * Then source memnode needs to be removed from its lgroup
5491708Sstevel 		 * and added to the target lgroup where the memory was living
5501708Sstevel 		 * but under a different name/memnode.  The memory was in the
5511708Sstevel 		 * target memnode and now lives in the source memnode with
5521708Sstevel 		 * different physical addresses even though it is the same
5531708Sstevel 		 * memory.
5541708Sstevel 		 */
5551708Sstevel 		shand = arg & 0xffff;
5561708Sstevel 		thand = (arg & 0xffff0000) >> 16;
5571708Sstevel 		snode = plat_lgrphand_to_mem_node(shand);
5581708Sstevel 		tnode = plat_lgrphand_to_mem_node(thand);
5591708Sstevel 
5601708Sstevel 		plat_assign_lgrphand_to_mem_node(thand, snode);
5611708Sstevel 		plat_assign_lgrphand_to_mem_node(shand, tnode);
5621708Sstevel 
5631708Sstevel 		/*
5641708Sstevel 		 * Remove source memnode of copy rename from its lgroup
5651708Sstevel 		 * and add it to its new target lgroup
5661708Sstevel 		 */
5671708Sstevel 		lmr.lmem_rename_from = shand;
5681708Sstevel 		lmr.lmem_rename_to = thand;
5691708Sstevel 
5701708Sstevel 		lgrp_config(LGRP_CONFIG_MEM_RENAME, (uintptr_t)snode,
5711708Sstevel 		    (uintptr_t)&lmr);
5721708Sstevel 
5731708Sstevel 		break;
5741708Sstevel 
5751708Sstevel 	default:
5761708Sstevel 		break;
5771708Sstevel 	}
5781708Sstevel }
5791708Sstevel 
5801708Sstevel /*
5811708Sstevel  * Return latency between "from" and "to" lgroups
5821708Sstevel  *
5831708Sstevel  * This latency number can only be used for relative comparison
5841708Sstevel  * between lgroups on the running system, cannot be used across platforms,
5851708Sstevel  * and may not reflect the actual latency.  It is platform and implementation
5861708Sstevel  * specific, so platform gets to decide its value.  It would be nice if the
5871708Sstevel  * number was at least proportional to make comparisons more meaningful though.
5881708Sstevel  * NOTE: The numbers below are supposed to be load latencies for uncached
5891708Sstevel  * memory divided by 10.
5901708Sstevel  */
5911708Sstevel int
plat_lgrp_latency(lgrp_handle_t from,lgrp_handle_t to)5921708Sstevel plat_lgrp_latency(lgrp_handle_t from, lgrp_handle_t to)
5931708Sstevel {
5941708Sstevel 	/*
5951708Sstevel 	 * Return min remote latency when there are more than two lgroups
5961708Sstevel 	 * (root and child) and getting latency between two different lgroups
5971708Sstevel 	 * or root is involved
5981708Sstevel 	 */
5991708Sstevel 	if (lgrp_optimizations() && (from != to ||
6001708Sstevel 	    from == LGRP_DEFAULT_HANDLE || to == LGRP_DEFAULT_HANDLE))
6011708Sstevel 		return (28);
6021708Sstevel 	else
6031708Sstevel 		return (23);
6041708Sstevel }
6051708Sstevel 
6061708Sstevel /* ARGSUSED */
6071708Sstevel void
plat_freelist_process(int mnode)6081708Sstevel plat_freelist_process(int mnode)
6091708Sstevel {
6101708Sstevel }
6111708Sstevel 
6121708Sstevel /*
6131708Sstevel  * Find dip for chosen IOSRAM
6141708Sstevel  */
6151708Sstevel dev_info_t *
find_chosen_dip(void)6161708Sstevel find_chosen_dip(void)
6171708Sstevel {
6181708Sstevel 	dev_info_t	*dip;
6191708Sstevel 	char		master_sbbc[MAXNAMELEN];
6201708Sstevel 	pnode_t		nodeid;
6211708Sstevel 	uint_t		tunnel;
6221708Sstevel 
6231708Sstevel 	/*
6241708Sstevel 	 * find the /chosen SBBC node, prom interface will handle errors
6251708Sstevel 	 */
6261708Sstevel 	nodeid = prom_chosennode();
6271708Sstevel 
6281708Sstevel 	/*
6291708Sstevel 	 * get the 'iosram' property from the /chosen node
6301708Sstevel 	 */
6311708Sstevel 	if (prom_getprop(nodeid, IOSRAM_CHOSEN_PROP, (caddr_t)&tunnel) <= 0) {
6321708Sstevel 		SBBC_ERR(CE_PANIC, "No iosram property found! \n");
6331708Sstevel 	}
6341708Sstevel 
6351708Sstevel 	if (prom_phandle_to_path((phandle_t)tunnel, master_sbbc,
6361708Sstevel 	    sizeof (master_sbbc)) < 0) {
6371708Sstevel 		SBBC_ERR1(CE_PANIC, "prom_phandle_to_path(%d) failed\n",
6381708Sstevel 		    tunnel);
6391708Sstevel 	}
6401708Sstevel 
6411708Sstevel 	chosen_nodeid = nodeid;
6421708Sstevel 
6431708Sstevel 	/*
6441708Sstevel 	 * load and attach the sgsbbc driver.
6451708Sstevel 	 * This will also attach all the sgsbbc driver instances
6461708Sstevel 	 */
6471708Sstevel 	if (i_ddi_attach_hw_nodes("sgsbbc") != DDI_SUCCESS) {
6481708Sstevel 		cmn_err(CE_WARN, "sgsbbc failed to load\n");
6491708Sstevel 	}
6501708Sstevel 
6511708Sstevel 	/* translate a path name to a dev_info_t */
6521708Sstevel 	dip = e_ddi_hold_devi_by_path(master_sbbc, 0);
6531708Sstevel 	if ((dip == NULL) || (ddi_get_nodeid(dip) != tunnel)) {
6541708Sstevel 		cmn_err(CE_PANIC, "i_ddi_path_to_devi(%x) failed for SBBC\n",
6551708Sstevel 		    tunnel);
6561708Sstevel 	}
6571708Sstevel 
6581708Sstevel 	/* make sure devi_ref is ZERO */
6591708Sstevel 	ndi_rele_devi(dip);
6601708Sstevel 
6611708Sstevel 	DCMNERR(CE_CONT, "Chosen IOSRAM is at %s \n", master_sbbc);
6621708Sstevel 
6631708Sstevel 	return (dip);
6641708Sstevel }
6651708Sstevel 
6661708Sstevel void
load_platform_drivers(void)6671708Sstevel load_platform_drivers(void)
6681708Sstevel {
6691708Sstevel 	int ret;
6701708Sstevel 
6711708Sstevel 	/*
6721708Sstevel 	 * Load and attach the mc-us3 memory driver.
6731708Sstevel 	 */
6741708Sstevel 	if (i_ddi_attach_hw_nodes("mc-us3") != DDI_SUCCESS)
6751708Sstevel 		cmn_err(CE_WARN, "mc-us3 failed to load");
6761708Sstevel 	else
6771708Sstevel 		(void) ddi_hold_driver(ddi_name_to_major("mc-us3"));
6781708Sstevel 
6791708Sstevel 	/*
6801708Sstevel 	 * Initialize the chosen IOSRAM before its clients
6811708Sstevel 	 * are loaded.
6821708Sstevel 	 */
6831708Sstevel 	(void) find_chosen_dip();
6841708Sstevel 
6851708Sstevel 	/*
6861708Sstevel 	 * Ideally, we'd do this in set_platform_defaults(), but
6871708Sstevel 	 * at that point it's too early to look up symbols.
6881708Sstevel 	 */
6891708Sstevel 	iosram_write_ptr = (int (*)(int, uint32_t, caddr_t, uint32_t))
6901708Sstevel 	    modgetsymvalue("iosram_write", 0);
6911708Sstevel 
6921708Sstevel 	if (iosram_write_ptr == NULL) {
6931708Sstevel 		DCMNERR(CE_WARN, "load_platform_defaults: iosram_write()"
6941708Sstevel 		    " not found; signatures will not be updated\n");
6951708Sstevel 	} else {
6961708Sstevel 		/*
6971708Sstevel 		 * The iosram read ptr is only needed if we can actually
6981708Sstevel 		 * write CPU signatures, so only bother setting it if we
6991708Sstevel 		 * set a valid write pointer, above.
7001708Sstevel 		 */
7011708Sstevel 		iosram_read_ptr = (int (*)(int, uint32_t, caddr_t, uint32_t))
7021708Sstevel 		    modgetsymvalue("iosram_read", 0);
7031708Sstevel 
7041708Sstevel 		if (iosram_read_ptr == NULL)
7051708Sstevel 			DCMNERR(CE_WARN, "load_platform_defaults: iosram_read()"
7061708Sstevel 			    " not found\n");
7071708Sstevel 	}
7081708Sstevel 
7091708Sstevel 	/*
7101708Sstevel 	 * Set todsg_use_sc to TRUE so that we will be getting date
7111708Sstevel 	 * from the SC.
7121708Sstevel 	 */
7131708Sstevel 	todsg_use_sc = TRUE;
7141708Sstevel 
7151708Sstevel 	/*
7161708Sstevel 	 * Now is a good time to activate hardware watchdog (if one exists).
7171708Sstevel 	 */
7181708Sstevel 	mutex_enter(&tod_lock);
7191708Sstevel 	if (watchdog_enable)
7201708Sstevel 		ret = tod_ops.tod_set_watchdog_timer(watchdog_timeout_seconds);
7211708Sstevel 	mutex_exit(&tod_lock);
7221708Sstevel 	if (ret != 0)
7231708Sstevel 		printf("Hardware watchdog enabled\n");
7241708Sstevel 
7251708Sstevel 	/*
7261708Sstevel 	 * Load and attach the schizo pci bus nexus driver.
7271708Sstevel 	 */
7281708Sstevel 	if (i_ddi_attach_hw_nodes("pcisch") != DDI_SUCCESS)
7291708Sstevel 		cmn_err(CE_WARN, "pcisch failed to load");
7301708Sstevel 
7311708Sstevel 	plat_ecc_init();
7321708Sstevel }
7331708Sstevel 
7341708Sstevel /*
7351708Sstevel  * No platform drivers on this platform
7361708Sstevel  */
7371708Sstevel char *platform_module_list[] = {
7381708Sstevel 	(char *)0
7391708Sstevel };
7401708Sstevel 
7411708Sstevel /*ARGSUSED*/
7421708Sstevel void
plat_tod_fault(enum tod_fault_type tod_bad)7431708Sstevel plat_tod_fault(enum tod_fault_type tod_bad)
7441708Sstevel {
7451708Sstevel }
7461708Sstevel int
plat_max_boards()7471708Sstevel plat_max_boards()
7481708Sstevel {
7491708Sstevel 	return (SG_MAX_BDS);
7501708Sstevel }
7511708Sstevel int
plat_max_io_units_per_board()7521708Sstevel plat_max_io_units_per_board()
7531708Sstevel {
7541708Sstevel 	return (SG_MAX_IO_PER_BD);
7551708Sstevel }
7561708Sstevel int
plat_max_cmp_units_per_board()7571708Sstevel plat_max_cmp_units_per_board()
7581708Sstevel {
7591708Sstevel 	return (SG_MAX_CMPS_PER_BD);
7601708Sstevel }
7611708Sstevel int
plat_max_cpu_units_per_board()7621708Sstevel plat_max_cpu_units_per_board()
7631708Sstevel {
7641708Sstevel 	return (SG_MAX_CPUS_PER_BD);
7651708Sstevel }
7661708Sstevel 
7671708Sstevel int
plat_max_mc_units_per_board()7681708Sstevel plat_max_mc_units_per_board()
7691708Sstevel {
7701708Sstevel 	return (SG_MAX_CMPS_PER_BD); /* each CPU die has a memory controller */
7711708Sstevel }
7721708Sstevel 
7731708Sstevel int
plat_max_mem_units_per_board()7741708Sstevel plat_max_mem_units_per_board()
7751708Sstevel {
7761708Sstevel 	return (SG_MAX_MEM_PER_BD);
7771708Sstevel }
7781708Sstevel 
7791708Sstevel int
plat_max_cpumem_boards(void)7801708Sstevel plat_max_cpumem_boards(void)
7811708Sstevel {
7821708Sstevel 	return (SG_MAX_CPU_BDS);
7831708Sstevel }
7841708Sstevel 
7851708Sstevel int
set_platform_max_ncpus(void)7861708Sstevel set_platform_max_ncpus(void)
7871708Sstevel {
7881708Sstevel 	return (sg_max_ncpus);
7891708Sstevel }
7901708Sstevel 
7911708Sstevel void
plat_dmv_params(uint_t * hwint,uint_t * swint)7921708Sstevel plat_dmv_params(uint_t *hwint, uint_t *swint)
7931708Sstevel {
7941708Sstevel 	*hwint = MAX_UPA;
7951708Sstevel 	*swint = 0;
7961708Sstevel }
7971708Sstevel 
7981708Sstevel /*
7991708Sstevel  * Our nodename has been set, pass it along to the SC.
8001708Sstevel  */
8011708Sstevel void
plat_nodename_set(void)8021708Sstevel plat_nodename_set(void)
8031708Sstevel {
8041708Sstevel 	sbbc_msg_t	req;	/* request */
8051708Sstevel 	sbbc_msg_t	resp;	/* response */
8061708Sstevel 	int		rv;	/* return value from call to mbox */
8071708Sstevel 	struct nodename_info {
8081708Sstevel 		int32_t	namelen;
8091708Sstevel 		char	nodename[_SYS_NMLN];
8101708Sstevel 	} nni;
8111708Sstevel 	int (*sg_mbox)(sbbc_msg_t *, sbbc_msg_t *, time_t) = NULL;
8121708Sstevel 
8131708Sstevel 	/*
8141708Sstevel 	 * find the symbol for the mailbox routine
8151708Sstevel 	 */
8161708Sstevel 	sg_mbox = (int (*)(sbbc_msg_t *, sbbc_msg_t *, time_t))
8175648Ssetje 	    modgetsymvalue("sbbc_mbox_request_response", 0);
8181708Sstevel 
8191708Sstevel 	if (sg_mbox == NULL) {
8201708Sstevel 		cmn_err(CE_NOTE, "!plat_nodename_set: sg_mbox not found\n");
8211708Sstevel 		return;
8221708Sstevel 	}
8231708Sstevel 
8241708Sstevel 	/*
8251708Sstevel 	 * construct the message telling the SC our nodename
8261708Sstevel 	 */
8271708Sstevel 	(void) strcpy(nni.nodename, utsname.nodename);
8281708Sstevel 	nni.namelen = (int32_t)strlen(nni.nodename);
8291708Sstevel 
8301708Sstevel 	req.msg_type.type = INFO_MBOX;
8311708Sstevel 	req.msg_type.sub_type = INFO_MBOX_NODENAME;
8321708Sstevel 	req.msg_status = 0;
8331708Sstevel 	req.msg_len = (int)(nni.namelen + sizeof (nni.namelen));
8341708Sstevel 	req.msg_bytes = 0;
8351708Sstevel 	req.msg_buf = (caddr_t)&nni;
8361708Sstevel 	req.msg_data[0] = 0;
8371708Sstevel 	req.msg_data[1] = 0;
8381708Sstevel 
8391708Sstevel 	/*
8401708Sstevel 	 * initialize the response back from the SC
8411708Sstevel 	 */
8421708Sstevel 	resp.msg_type.type = INFO_MBOX;
8431708Sstevel 	resp.msg_type.sub_type = INFO_MBOX_NODENAME;
8441708Sstevel 	resp.msg_status = 0;
8451708Sstevel 	resp.msg_len = 0;
8461708Sstevel 	resp.msg_bytes = 0;
8471708Sstevel 	resp.msg_buf = (caddr_t)0;
8481708Sstevel 	resp.msg_data[0] = 0;
8491708Sstevel 	resp.msg_data[1] = 0;
8501708Sstevel 
8511708Sstevel 	/*
8521708Sstevel 	 * ship it and check for success
8531708Sstevel 	 */
8541708Sstevel 	rv = (sg_mbox)(&req, &resp, sbbc_mbox_default_timeout);
8551708Sstevel 
8561708Sstevel 	if (rv != 0) {
8571708Sstevel 		cmn_err(CE_NOTE, "!plat_nodename_set: sg_mbox retval %d\n", rv);
8581708Sstevel 	} else if (resp.msg_status != 0) {
8591708Sstevel 		cmn_err(CE_NOTE, "!plat_nodename_set: msg_status %d\n",
8605648Ssetje 		    resp.msg_status);
8611708Sstevel 	} else {
8621708Sstevel 		DCMNERR(CE_NOTE, "!plat_nodename_set was successful\n");
8631708Sstevel 
8641708Sstevel 		/*
8651708Sstevel 		 * It is necessary to exchange the capability bitmap
8661708Sstevel 		 * with SC before sending any ecc error information and
8671708Sstevel 		 * indictment. We are calling the plat_ecc_capability_send()
8681708Sstevel 		 * here just after sending the nodename successfully.
8691708Sstevel 		 */
8701708Sstevel 		rv = plat_ecc_capability_send();
8711708Sstevel 		if (rv == 0) {
8721708Sstevel 			DCMNERR(CE_NOTE, "!plat_ecc_capability_send was"
8731708Sstevel 			    " successful\n");
8741708Sstevel 		}
8751708Sstevel 	}
8761708Sstevel }
8771708Sstevel 
8781708Sstevel /*
8791708Sstevel  * flag to allow users switch between using OBP's
8801708Sstevel  * prom_get_unum() and mc-us3 driver's p2get_mem_unum()
8811708Sstevel  * (for main memory errors only).
8821708Sstevel  */
8831708Sstevel int sg_use_prom_get_unum = 0;
8841708Sstevel 
8851708Sstevel /*
8861708Sstevel  * Debugging flag: set to 1 to call into obp for get_unum, or set it to 0
8871708Sstevel  * to call into the unum cache system.  This is the E$ equivalent of
8881708Sstevel  * sg_use_prom_get_unum.
8891708Sstevel  */
8901708Sstevel int sg_use_prom_ecache_unum = 0;
8911708Sstevel 
8921708Sstevel /* used for logging ECC errors to the SC */
8931708Sstevel #define	SG_MEMORY_ECC	1
8941708Sstevel #define	SG_ECACHE_ECC	2
8951708Sstevel #define	SG_UNKNOWN_ECC	(-1)
8961708Sstevel 
8971708Sstevel /*
8981708Sstevel  * plat_get_mem_unum() generates a string identifying either the
8991708Sstevel  * memory or E$ DIMM(s) during error logging. Depending on whether
9001708Sstevel  * the error is E$ or memory related, the appropriate support
9011708Sstevel  * routine is called to assist in the string generation.
9021708Sstevel  *
9031708Sstevel  * - For main memory errors we can use the mc-us3 drivers p2getunum()
9041708Sstevel  *   (or prom_get_unum() for debugging purposes).
9051708Sstevel  *
9061708Sstevel  * - For E$ errors we call sg_get_ecacheunum() to generate the unum (or
9071708Sstevel  *   prom_serengeti_get_ecacheunum() for debugging purposes).
9081708Sstevel  */
9091708Sstevel 
9101708Sstevel static int
sg_prom_get_unum(int synd_code,uint64_t paddr,char * buf,int buflen,int * lenp)9111708Sstevel sg_prom_get_unum(int synd_code, uint64_t paddr, char *buf, int buflen,
9121708Sstevel     int *lenp)
9131708Sstevel {
9141708Sstevel 	if ((prom_get_unum(synd_code, (unsigned long long)paddr,
9151708Sstevel 	    buf, buflen, lenp)) != 0)
9161708Sstevel 		return (EIO);
9171708Sstevel 	else if (*lenp <= 1)
9181708Sstevel 		return (EINVAL);
9191708Sstevel 	else
9201708Sstevel 		return (0);
9211708Sstevel }
9221708Sstevel 
9231708Sstevel /*ARGSUSED*/
9241708Sstevel int
plat_get_mem_unum(int synd_code,uint64_t flt_addr,int flt_bus_id,int flt_in_memory,ushort_t flt_status,char * buf,int buflen,int * lenp)9251708Sstevel plat_get_mem_unum(int synd_code, uint64_t flt_addr, int flt_bus_id,
9261708Sstevel     int flt_in_memory, ushort_t flt_status, char *buf, int buflen, int *lenp)
9271708Sstevel {
9281708Sstevel 	/*
9291708Sstevel 	 * unum_func will either point to the memory drivers p2get_mem_unum()
9301708Sstevel 	 * or to prom_get_unum() for memory errors.
9311708Sstevel 	 */
9321708Sstevel 	int (*unum_func)(int synd_code, uint64_t paddr, char *buf,
9331708Sstevel 	    int buflen, int *lenp) = p2get_mem_unum;
9341708Sstevel 
9351708Sstevel 	/*
9361708Sstevel 	 * check if it's a Memory or an Ecache error.
9371708Sstevel 	 */
9381708Sstevel 	if (flt_in_memory) {
9391708Sstevel 		/*
9401708Sstevel 		 * It's a main memory error.
9411708Sstevel 		 *
9421708Sstevel 		 * For debugging we allow the user to switch between
9431708Sstevel 		 * using OBP's get_unum and the memory driver's get_unum
9441708Sstevel 		 * so we create a pointer to the functions and switch
9451708Sstevel 		 * depending on the sg_use_prom_get_unum flag.
9461708Sstevel 		 */
9471708Sstevel 		if (sg_use_prom_get_unum) {
9481708Sstevel 			DCMNERR(CE_NOTE, "Using prom_get_unum from OBP");
9491708Sstevel 			return (sg_prom_get_unum(synd_code,
9501708Sstevel 			    P2ALIGN(flt_addr, 8), buf, buflen, lenp));
9511708Sstevel 		} else if (unum_func != NULL) {
9521708Sstevel 			return (unum_func(synd_code, P2ALIGN(flt_addr, 8),
9531708Sstevel 			    buf, buflen, lenp));
9541708Sstevel 		} else {
9551708Sstevel 			return (ENOTSUP);
9561708Sstevel 		}
9571708Sstevel 	} else if (flt_status & ECC_ECACHE) {
9581708Sstevel 		/*
9591708Sstevel 		 * It's an E$ error.
9601708Sstevel 		 */
9611708Sstevel 		if (sg_use_prom_ecache_unum) {
9621708Sstevel 			/*
9631708Sstevel 			 * We call to OBP to handle this.
9641708Sstevel 			 */
9651708Sstevel 			DCMNERR(CE_NOTE,
9661708Sstevel 			    "Using prom_serengeti_get_ecacheunum from OBP");
9671708Sstevel 			if (prom_serengeti_get_ecacheunum(flt_bus_id,
9681708Sstevel 			    P2ALIGN(flt_addr, 8), buf, buflen, lenp) != 0) {
9691708Sstevel 				return (EIO);
9701708Sstevel 			}
9711708Sstevel 		} else {
9721708Sstevel 			return (sg_get_ecacheunum(flt_bus_id, flt_addr,
9731708Sstevel 			    buf, buflen, lenp));
9741708Sstevel 		}
9751708Sstevel 	} else {
9761708Sstevel 		return (ENOTSUP);
9771708Sstevel 	}
9781708Sstevel 
9791708Sstevel 	return (0);
9801708Sstevel }
9811708Sstevel 
9821708Sstevel /*
9831708Sstevel  * This platform hook gets called from mc_add_mem_unum_label() in the mc-us3
9841708Sstevel  * driver giving each platform the opportunity to add platform
9851708Sstevel  * specific label information to the unum for ECC error logging purposes.
9861708Sstevel  */
9871708Sstevel void
plat_add_mem_unum_label(char * unum,int mcid,int bank,int dimm)9881708Sstevel plat_add_mem_unum_label(char *unum, int mcid, int bank, int dimm)
9891708Sstevel {
9901708Sstevel 	char	new_unum[UNUM_NAMLEN] = "";
9911708Sstevel 	int	node = SG_PORTID_TO_NODEID(mcid);
9921708Sstevel 	int	board = SG_CPU_BD_PORTID_TO_BD_NUM(mcid);
9931708Sstevel 	int	position = SG_PORTID_TO_CPU_POSN(mcid);
9941708Sstevel 
9951708Sstevel 	/*
9961708Sstevel 	 * The mc-us3 driver deals with logical banks but for unum
9971708Sstevel 	 * purposes we need to use physical banks so that the correct
9981708Sstevel 	 * dimm can be physically located. Logical banks 0 and 2
9991708Sstevel 	 * make up physical bank 0. Logical banks 1 and 3 make up
10001708Sstevel 	 * physical bank 1. Here we do the necessary conversion.
10011708Sstevel 	 */
10021708Sstevel 	bank = (bank % 2);
10031708Sstevel 
10041708Sstevel 	if (dimm == -1) {
10051708Sstevel 		SG_SET_FRU_NAME_NODE(new_unum, node);
10061708Sstevel 		SG_SET_FRU_NAME_CPU_BOARD(new_unum, board);
10071708Sstevel 		SG_SET_FRU_NAME_MODULE(new_unum, position);
10081708Sstevel 		SG_SET_FRU_NAME_BANK(new_unum, bank);
10091708Sstevel 
10101708Sstevel 	} else {
10111708Sstevel 		SG_SET_FRU_NAME_NODE(new_unum, node);
10121708Sstevel 		SG_SET_FRU_NAME_CPU_BOARD(new_unum, board);
10131708Sstevel 		SG_SET_FRU_NAME_MODULE(new_unum, position);
10141708Sstevel 		SG_SET_FRU_NAME_BANK(new_unum, bank);
10151708Sstevel 		SG_SET_FRU_NAME_DIMM(new_unum, dimm);
10161708Sstevel 
1017*11311SSurya.Prakki@Sun.COM 		(void) strcat(new_unum, " ");
1018*11311SSurya.Prakki@Sun.COM 		(void) strcat(new_unum, unum);
10191708Sstevel 	}
10201708Sstevel 
1021*11311SSurya.Prakki@Sun.COM 	(void) strcpy(unum, new_unum);
10221708Sstevel }
10231708Sstevel 
10241708Sstevel int
plat_get_cpu_unum(int cpuid,char * buf,int buflen,int * lenp)10251708Sstevel plat_get_cpu_unum(int cpuid, char *buf, int buflen, int *lenp)
10261708Sstevel {
10271708Sstevel 	int	node = SG_PORTID_TO_NODEID(cpuid);
10281708Sstevel 	int	board = SG_CPU_BD_PORTID_TO_BD_NUM(cpuid);
10291708Sstevel 
10301708Sstevel 	if (snprintf(buf, buflen, "/N%d/%s%d", node,
10311708Sstevel 	    SG_HPU_TYPE_CPU_BOARD_ID, board) >= buflen) {
10321708Sstevel 		return (ENOSPC);
10331708Sstevel 	} else {
10341708Sstevel 		*lenp = strlen(buf);
10351708Sstevel 		return (0);
10361708Sstevel 	}
10371708Sstevel }
10381708Sstevel 
10391708Sstevel /*
10401708Sstevel  * We log all ECC events to the SC so we send a mailbox
10411708Sstevel  * message to the SC passing it the relevant data.
10421708Sstevel  * ECC mailbox messages are sent via a taskq mechanism to
10431708Sstevel  * prevent impaired system performance during ECC floods.
10441708Sstevel  * Indictments have already passed through a taskq, so they
10451708Sstevel  * are not queued here.
10461708Sstevel  */
10471708Sstevel int
plat_send_ecc_mailbox_msg(plat_ecc_message_type_t msg_type,void * datap)10481708Sstevel plat_send_ecc_mailbox_msg(plat_ecc_message_type_t msg_type, void *datap)
10491708Sstevel {
10501708Sstevel 	sbbc_ecc_mbox_t	*msgp;
10511708Sstevel 	size_t		msg_size;
10521708Sstevel 	uint16_t	msg_subtype;
10531708Sstevel 	int		sleep_flag, log_error;
10541708Sstevel 
10551708Sstevel 	if (sg_ecc_taskq_func == NULL) {
10561708Sstevel 		sg_ecc_taskq_func = (void (*)(sbbc_ecc_mbox_t *))
10571708Sstevel 		    modgetsymvalue("sbbc_mbox_queue_ecc_event", 0);
10581708Sstevel 		if (sg_ecc_taskq_func == NULL) {
10591708Sstevel 			cmn_err(CE_NOTE, "!plat_send_ecc_mailbox_msg: "
10601708Sstevel 			    "sbbc_mbox_queue_ecc_event not found");
10611708Sstevel 			return (ENODEV);
10621708Sstevel 		}
10631708Sstevel 	}
10641708Sstevel 	if (sg_ecc_mbox_func == NULL) {
10651708Sstevel 		sg_ecc_mbox_func = (int (*)(sbbc_ecc_mbox_t *))
10661708Sstevel 		    modgetsymvalue("sbbc_mbox_ecc_output", 0);
10671708Sstevel 		if (sg_ecc_mbox_func == NULL) {
10681708Sstevel 			cmn_err(CE_NOTE, "!plat_send_ecc_mailbox_msg: "
10691708Sstevel 			    "sbbc_mbox_ecc_output not found");
10701708Sstevel 			return (ENODEV);
10711708Sstevel 		}
10721708Sstevel 	}
10731708Sstevel 
10741708Sstevel 	/*
10751708Sstevel 	 * Initialize the request and response structures
10761708Sstevel 	 */
10771708Sstevel 	switch (msg_type) {
10781708Sstevel 	case PLAT_ECC_ERROR_MESSAGE:
10791708Sstevel 		msg_subtype = INFO_MBOX_ERROR_ECC;
10801708Sstevel 		msg_size = sizeof (plat_ecc_error_data_t);
10811708Sstevel 		sleep_flag = KM_NOSLEEP;
10821708Sstevel 		log_error = 1;
10831708Sstevel 		break;
10841708Sstevel 	case PLAT_ECC_ERROR2_MESSAGE:
10851708Sstevel 		msg_subtype = INFO_MBOX_ECC;
10861708Sstevel 		msg_size = sizeof (plat_ecc_error2_data_t);
10871708Sstevel 		sleep_flag = KM_NOSLEEP;
10881708Sstevel 		log_error = 1;
10891708Sstevel 		break;
10901708Sstevel 	case PLAT_ECC_INDICTMENT_MESSAGE:
10911708Sstevel 		msg_subtype = INFO_MBOX_ERROR_INDICT;
10921708Sstevel 		msg_size = sizeof (plat_ecc_indictment_data_t);
10931708Sstevel 		sleep_flag = KM_SLEEP;
10941708Sstevel 		log_error = 0;
10951708Sstevel 		break;
10961708Sstevel 	case PLAT_ECC_INDICTMENT2_MESSAGE:
10971708Sstevel 		msg_subtype = INFO_MBOX_ECC;
10981708Sstevel 		msg_size = sizeof (plat_ecc_indictment2_data_t);
10991708Sstevel 		sleep_flag = KM_SLEEP;
11001708Sstevel 		log_error = 0;
11011708Sstevel 		break;
11021708Sstevel 	case PLAT_ECC_CAPABILITY_MESSAGE:
11031708Sstevel 		msg_subtype = INFO_MBOX_ECC_CAP;
11041708Sstevel 		msg_size = sizeof (plat_capability_data_t) +
11051708Sstevel 		    strlen(utsname.release) + strlen(utsname.version) + 2;
11061708Sstevel 		sleep_flag = KM_SLEEP;
11071708Sstevel 		log_error = 0;
11081708Sstevel 		break;
11091708Sstevel 	case PLAT_ECC_DIMM_SID_MESSAGE:
11101708Sstevel 		msg_subtype = INFO_MBOX_ECC;
11111708Sstevel 		msg_size = sizeof (plat_dimm_sid_request_data_t);
11121708Sstevel 		sleep_flag = KM_SLEEP;
11131708Sstevel 		log_error = 0;
11141708Sstevel 		break;
11151708Sstevel 	default:
11161708Sstevel 		return (EINVAL);
11171708Sstevel 	}
11181708Sstevel 
11191708Sstevel 	msgp = (sbbc_ecc_mbox_t	*)kmem_zalloc(sizeof (sbbc_ecc_mbox_t),
11205648Ssetje 	    sleep_flag);
11211708Sstevel 	if (msgp == NULL) {
11221708Sstevel 		cmn_err(CE_NOTE, "!plat_send_ecc_mailbox_msg: "
11235648Ssetje 		    "unable to allocate sbbc_ecc_mbox");
11241708Sstevel 		return (ENOMEM);
11251708Sstevel 	}
11261708Sstevel 
11271708Sstevel 	msgp->ecc_log_error = log_error;
11281708Sstevel 
11291708Sstevel 	msgp->ecc_req.msg_type.type = INFO_MBOX;
11301708Sstevel 	msgp->ecc_req.msg_type.sub_type = msg_subtype;
11311708Sstevel 	msgp->ecc_req.msg_status = 0;
11321708Sstevel 	msgp->ecc_req.msg_len = (int)msg_size;
11331708Sstevel 	msgp->ecc_req.msg_bytes = 0;
11341708Sstevel 	msgp->ecc_req.msg_buf = (caddr_t)kmem_zalloc(msg_size, sleep_flag);
11351708Sstevel 	msgp->ecc_req.msg_data[0] = 0;
11361708Sstevel 	msgp->ecc_req.msg_data[1] = 0;
11371708Sstevel 
11381708Sstevel 	if (msgp->ecc_req.msg_buf == NULL) {
11391708Sstevel 		cmn_err(CE_NOTE, "!plat_send_ecc_mailbox_msg: "
11405648Ssetje 		    "unable to allocate request msg_buf");
11411708Sstevel 		kmem_free((void *)msgp, sizeof (sbbc_ecc_mbox_t));
11421708Sstevel 		return (ENOMEM);
11431708Sstevel 	}
11441708Sstevel 	bcopy(datap, (void *)msgp->ecc_req.msg_buf, msg_size);
11451708Sstevel 
11461708Sstevel 	/*
11471708Sstevel 	 * initialize the response back from the SC
11481708Sstevel 	 */
11491708Sstevel 	msgp->ecc_resp.msg_type.type = INFO_MBOX;
11501708Sstevel 	msgp->ecc_resp.msg_type.sub_type = msg_subtype;
11511708Sstevel 	msgp->ecc_resp.msg_status = 0;
11521708Sstevel 	msgp->ecc_resp.msg_len = 0;
11531708Sstevel 	msgp->ecc_resp.msg_bytes = 0;
11541708Sstevel 	msgp->ecc_resp.msg_buf = NULL;
11551708Sstevel 	msgp->ecc_resp.msg_data[0] = 0;
11561708Sstevel 	msgp->ecc_resp.msg_data[1] = 0;
11571708Sstevel 
11581708Sstevel 	switch (msg_type) {
11591708Sstevel 	case PLAT_ECC_ERROR_MESSAGE:
11601708Sstevel 	case PLAT_ECC_ERROR2_MESSAGE:
11611708Sstevel 		/*
11621708Sstevel 		 * For Error Messages, we go through a taskq.
11631708Sstevel 		 * Queue up the message for processing
11641708Sstevel 		 */
11651708Sstevel 		(*sg_ecc_taskq_func)(msgp);
11661708Sstevel 		return (0);
11671708Sstevel 
11681708Sstevel 	case PLAT_ECC_CAPABILITY_MESSAGE:
11691708Sstevel 		/*
11701708Sstevel 		 * For indictment and capability messages, we've already gone
11711708Sstevel 		 * through the taskq, so we can call the mailbox routine
11721708Sstevel 		 * directly.  Find the symbol for the routine that sends
11731708Sstevel 		 * the mailbox msg
11741708Sstevel 		 */
11751708Sstevel 		msgp->ecc_resp.msg_len = (int)msg_size;
11761708Sstevel 		msgp->ecc_resp.msg_buf = (caddr_t)kmem_zalloc(msg_size,
11771708Sstevel 		    sleep_flag);
11781708Sstevel 		/* FALLTHRU */
11791708Sstevel 
11801708Sstevel 	case PLAT_ECC_INDICTMENT_MESSAGE:
11811708Sstevel 	case PLAT_ECC_INDICTMENT2_MESSAGE:
11821708Sstevel 		return ((*sg_ecc_mbox_func)(msgp));
11831708Sstevel 
11841708Sstevel 	case PLAT_ECC_DIMM_SID_MESSAGE:
11851708Sstevel 		msgp->ecc_resp.msg_len = sizeof (plat_dimm_sid_board_data_t);
11861708Sstevel 		msgp->ecc_resp.msg_buf = (caddr_t)kmem_zalloc(
11871708Sstevel 		    sizeof (plat_dimm_sid_board_data_t), sleep_flag);
11881708Sstevel 		return ((*sg_ecc_mbox_func)(msgp));
11891708Sstevel 
11901708Sstevel 	default:
11911708Sstevel 		ASSERT(0);
11921708Sstevel 		return (EINVAL);
11931708Sstevel 	}
11941708Sstevel }
11951708Sstevel 
11961708Sstevel /*
11971708Sstevel  * m is redundant on serengeti as the multiplier is always 4
11981708Sstevel  */
11991708Sstevel /*ARGSUSED*/
12001708Sstevel int
plat_make_fru_cpuid(int sb,int m,int proc)12011708Sstevel plat_make_fru_cpuid(int sb, int m, int proc)
12021708Sstevel {
12031708Sstevel 	return (MAKE_CPUID(sb, proc));
12041708Sstevel }
12051708Sstevel 
12061708Sstevel /*
12071708Sstevel  * board number for a given proc
12081708Sstevel  */
12091708Sstevel int
plat_make_fru_boardnum(int proc)12101708Sstevel plat_make_fru_boardnum(int proc)
12111708Sstevel {
12121708Sstevel 	return (SG_CPU_BD_PORTID_TO_BD_NUM(proc));
12131708Sstevel }
12141708Sstevel 
12151708Sstevel static
12161708Sstevel void
cpu_sgn_update(ushort_t sig,uchar_t state,uchar_t sub_state,int cpuid)12171708Sstevel cpu_sgn_update(ushort_t sig, uchar_t state, uchar_t sub_state, int cpuid)
12181708Sstevel {
12191708Sstevel 	uint32_t signature = CPU_SIG_BLD(sig, state, sub_state);
12201708Sstevel 	sig_state_t current_sgn;
12211708Sstevel 	int i;
12221708Sstevel 
12231708Sstevel 	if (iosram_write_ptr == NULL) {
12241708Sstevel 		/*
12251708Sstevel 		 * If the IOSRAM write pointer isn't set, we won't be able
12261708Sstevel 		 * to write signatures to ANYTHING, so we may as well just
12271708Sstevel 		 * write out an error message (if desired) and exit this
12281708Sstevel 		 * routine now...
12291708Sstevel 		 */
12301708Sstevel 		DCMNERR(CE_WARN,
12311708Sstevel 		    "cpu_sgn_update: iosram_write() not found;"
12321708Sstevel 		    " cannot write signature 0x%x for CPU(s) or domain\n",
12331708Sstevel 		    signature);
12341708Sstevel 		return;
12351708Sstevel 	}
12361708Sstevel 
12371708Sstevel 
12381708Sstevel 	/*
12391708Sstevel 	 * Differentiate a panic reboot from a non-panic reboot in the
12401708Sstevel 	 * setting of the substate of the signature.
12411708Sstevel 	 *
12421708Sstevel 	 * If the new substate is REBOOT and we're rebooting due to a panic,
12431708Sstevel 	 * then set the new substate to a special value indicating a panic
12441708Sstevel 	 * reboot, SIGSUBST_PANIC_REBOOT.
12451708Sstevel 	 *
12461708Sstevel 	 * A panic reboot is detected by a current (previous) domain signature
12471708Sstevel 	 * state of SIGST_EXIT, and a new signature substate of SIGSUBST_REBOOT.
12481708Sstevel 	 * The domain signature state SIGST_EXIT is used as the panic flow
12491708Sstevel 	 * progresses.
12501708Sstevel 	 *
12511708Sstevel 	 * At the end of the panic flow, the reboot occurs but we should now
12521708Sstevel 	 * one that was involuntary, something that may be quite useful to know
12531708Sstevel 	 * at OBP level.
12541708Sstevel 	 */
12551708Sstevel 	if (sub_state == SIGSUBST_REBOOT) {
12561708Sstevel 		if (iosram_read_ptr == NULL) {
12571708Sstevel 			DCMNERR(CE_WARN,
12581708Sstevel 			    "cpu_sgn_update: iosram_read() not found;"
12591708Sstevel 			    " could not check current domain signature\n");
12601708Sstevel 		} else {
12611708Sstevel 			(void) (*iosram_read_ptr)(SBBC_SIGBLCK_KEY,
12625648Ssetje 			    SG_SGNBLK_DOMAINSIG_OFFSET,
12635648Ssetje 			    (char *)&current_sgn, sizeof (current_sgn));
12641708Sstevel 			if (current_sgn.state_t.state == SIGST_EXIT)
12651708Sstevel 				signature = CPU_SIG_BLD(sig, state,
12665648Ssetje 				    SIGSUBST_PANIC_REBOOT);
12671708Sstevel 		}
12681708Sstevel 	}
12691708Sstevel 
12701708Sstevel 	/*
12711708Sstevel 	 * cpuid == -1 indicates that the operation applies to all cpus.
12721708Sstevel 	 */
12731708Sstevel 	if (cpuid >= 0) {
12741708Sstevel 		(void) (*iosram_write_ptr)(SBBC_SIGBLCK_KEY,
12755648Ssetje 		    SG_SGNBLK_CPUSIG_OFFSET(cpuid), (char *)&signature,
12765648Ssetje 		    sizeof (signature));
12771708Sstevel 	} else {
12781708Sstevel 		for (i = 0; i < NCPU; i++) {
12791708Sstevel 			if (cpu[i] == NULL || !(cpu[i]->cpu_flags &
12805648Ssetje 			    (CPU_EXISTS|CPU_QUIESCED))) {
12811708Sstevel 				continue;
12821708Sstevel 			}
12831708Sstevel 			(void) (*iosram_write_ptr)(SBBC_SIGBLCK_KEY,
12845648Ssetje 			    SG_SGNBLK_CPUSIG_OFFSET(i), (char *)&signature,
12855648Ssetje 			    sizeof (signature));
12861708Sstevel 		}
12871708Sstevel 	}
12881708Sstevel 
12891708Sstevel 	if (state == SIGST_OFFLINE || state == SIGST_DETACHED) {
12901708Sstevel 		return;
12911708Sstevel 	}
12921708Sstevel 
12931708Sstevel 	(void) (*iosram_write_ptr)(SBBC_SIGBLCK_KEY,
12945648Ssetje 	    SG_SGNBLK_DOMAINSIG_OFFSET, (char *)&signature,
12955648Ssetje 	    sizeof (signature));
12961708Sstevel }
12971708Sstevel 
12981708Sstevel void
startup_platform(void)12991708Sstevel startup_platform(void)
13001708Sstevel {
13015834Spt157919 	/* set per-platform constants for mutex backoff */
13025834Spt157919 	mutex_backoff_base = 1;
13035834Spt157919 	mutex_cap_factor = 32;
13041708Sstevel }
13051708Sstevel 
13061708Sstevel /*
13071708Sstevel  * A routine to convert a number (represented as a string) to
13081708Sstevel  * the integer value it represents.
13091708Sstevel  */
13101708Sstevel 
13111708Sstevel static int
isdigit(int ch)13121708Sstevel isdigit(int ch)
13131708Sstevel {
13141708Sstevel 	return (ch >= '0' && ch <= '9');
13151708Sstevel }
13161708Sstevel 
13171708Sstevel #define	isspace(c)	((c) == ' ' || (c) == '\t' || (c) == '\n')
13181708Sstevel 
13191708Sstevel static int
strtoi(char * p,char ** pos)13201708Sstevel strtoi(char *p, char **pos)
13211708Sstevel {
13221708Sstevel 	int n;
13231708Sstevel 	int c, neg = 0;
13241708Sstevel 
13251708Sstevel 	if (!isdigit(c = *p)) {
13261708Sstevel 		while (isspace(c))
13271708Sstevel 			c = *++p;
13281708Sstevel 		switch (c) {
13291708Sstevel 			case '-':
13301708Sstevel 				neg++;
13311708Sstevel 				/* FALLTHROUGH */
13321708Sstevel 			case '+':
13331708Sstevel 			c = *++p;
13341708Sstevel 		}
13351708Sstevel 		if (!isdigit(c)) {
13361708Sstevel 			if (pos != NULL)
13371708Sstevel 				*pos = p;
13381708Sstevel 			return (0);
13391708Sstevel 		}
13401708Sstevel 	}
13411708Sstevel 	for (n = '0' - c; isdigit(c = *++p); ) {
13421708Sstevel 		n *= 10; /* two steps to avoid unnecessary overflow */
13431708Sstevel 		n += '0' - c; /* accum neg to avoid surprises at MAX */
13441708Sstevel 	}
13451708Sstevel 	if (pos != NULL)
13461708Sstevel 		*pos = p;
13471708Sstevel 	return (neg ? n : -n);
13481708Sstevel }
13491708Sstevel 
13501708Sstevel /*
13511708Sstevel  * Get the three parts of the Serengeti PROM version.
13521708Sstevel  * Used for feature readiness tests.
13531708Sstevel  *
13541708Sstevel  * Return 0 if version extracted successfully, -1 otherwise.
13551708Sstevel  */
13561708Sstevel 
13571708Sstevel int
sg_get_prom_version(int * sysp,int * intfp,int * bldp)13581708Sstevel sg_get_prom_version(int *sysp, int *intfp, int *bldp)
13591708Sstevel {
13601708Sstevel 	int plen;
13611708Sstevel 	char vers[512];
13621708Sstevel 	static pnode_t node;
13631708Sstevel 	static char version[] = "version";
13641708Sstevel 	char *verp, *ep;
13651708Sstevel 
13661708Sstevel 	node = prom_finddevice("/openprom");
13671708Sstevel 	if (node == OBP_BADNODE)
13681708Sstevel 		return (-1);
13691708Sstevel 
13701708Sstevel 	plen = prom_getproplen(node, version);
13711708Sstevel 	if (plen <= 0 || plen >= sizeof (vers))
13721708Sstevel 		return (-1);
13731708Sstevel 	(void) prom_getprop(node, version, vers);
13741708Sstevel 	vers[plen] = '\0';
13751708Sstevel 
13761708Sstevel 	/* Make sure it's an OBP flashprom */
13771708Sstevel 	if (vers[0] != 'O' && vers[1] != 'B' && vers[2] != 'P') {
13781708Sstevel 		cmn_err(CE_WARN, "sg_get_prom_version: "
13791708Sstevel 		    "unknown <version> string in </openprom>\n");
13801708Sstevel 		return (-1);
13811708Sstevel 	}
13821708Sstevel 	verp = &vers[4];
13831708Sstevel 
13841708Sstevel 	*sysp = strtoi(verp, &ep);
13851708Sstevel 	if (ep == verp || *ep != '.')
13861708Sstevel 		return (-1);
13871708Sstevel 	verp = ep + 1;
13881708Sstevel 
13891708Sstevel 	*intfp = strtoi(verp, &ep);
13901708Sstevel 	if (ep == verp || *ep != '.')
13911708Sstevel 		return (-1);
13921708Sstevel 	verp = ep + 1;
13931708Sstevel 
13941708Sstevel 	*bldp = strtoi(verp, &ep);
13951708Sstevel 	if (ep == verp || (*ep != '\0' && !isspace(*ep)))
13961708Sstevel 		return (-1);
13971708Sstevel 	return (0);
13981708Sstevel }
13991708Sstevel 
14001708Sstevel /*
14011708Sstevel  * Return 0 if system board Dynamic Reconfiguration
14021708Sstevel  * is supported by the firmware, -1 otherwise.
14031708Sstevel  */
14041708Sstevel int
sg_prom_sb_dr_check(void)14051708Sstevel sg_prom_sb_dr_check(void)
14061708Sstevel {
14071708Sstevel 	static int prom_res = 1;
14081708Sstevel 
14091708Sstevel 	if (prom_res == 1) {
14101708Sstevel 		int sys, intf, bld;
14111708Sstevel 		int rv;
14121708Sstevel 
14131708Sstevel 		rv = sg_get_prom_version(&sys, &intf, &bld);
14141708Sstevel 		if (rv == 0 && sys == 5 &&
14151708Sstevel 		    (intf >= 12 || (intf == 11 && bld >= 200))) {
14161708Sstevel 			prom_res = 0;
14171708Sstevel 		} else {
14181708Sstevel 			prom_res = -1;
14191708Sstevel 		}
14201708Sstevel 	}
14211708Sstevel 	return (prom_res);
14221708Sstevel }
14231708Sstevel 
14241708Sstevel /*
14251708Sstevel  * Return 0 if cPCI Dynamic Reconfiguration
14261708Sstevel  * is supported by the firmware, -1 otherwise.
14271708Sstevel  */
14281708Sstevel int
sg_prom_cpci_dr_check(void)14291708Sstevel sg_prom_cpci_dr_check(void)
14301708Sstevel {
14311708Sstevel 	/*
14321708Sstevel 	 * The version check is currently the same as for
14331708Sstevel 	 * system boards. Since the two DR sub-systems are
14341708Sstevel 	 * independent, this could change.
14351708Sstevel 	 */
14361708Sstevel 	return (sg_prom_sb_dr_check());
14371708Sstevel }
14381708Sstevel 
14391708Sstevel /*
14401708Sstevel  * KDI functions - used by the in-situ kernel debugger (kmdb) to perform
14411708Sstevel  * platform-specific operations.  These functions execute when the world is
14421708Sstevel  * stopped, and as such cannot make any blocking calls, hold locks, etc.
14431708Sstevel  * promif functions are a special case, and may be used.
14441708Sstevel  */
14451708Sstevel 
14461708Sstevel /*
14471708Sstevel  * Our implementation of this KDI op updates the CPU signature in the system
14481708Sstevel  * controller.  Note that we set the signature to OBP_SIG, rather than DBG_SIG.
14491708Sstevel  * The Forth words we execute will, among other things, transform our OBP_SIG
14501708Sstevel  * into DBG_SIG.  They won't function properly if we try to use DBG_SIG.
14511708Sstevel  */
14521708Sstevel static void
sg_system_claim(void)14531708Sstevel sg_system_claim(void)
14541708Sstevel {
145511066Srafael.vanoni@sun.com 	lbolt_debug_entry();
145611066Srafael.vanoni@sun.com 
14571708Sstevel 	prom_interpret("sigb-sig! my-sigb-sig!", OBP_SIG, OBP_SIG, 0, 0, 0);
14581708Sstevel }
14591708Sstevel 
14601708Sstevel static void
sg_system_release(void)14611708Sstevel sg_system_release(void)
14621708Sstevel {
14631708Sstevel 	prom_interpret("sigb-sig! my-sigb-sig!", OS_SIG, OS_SIG, 0, 0, 0);
146411066Srafael.vanoni@sun.com 
146511066Srafael.vanoni@sun.com 	lbolt_debug_return();
14661708Sstevel }
14671708Sstevel 
14681708Sstevel static void
sg_console_claim(void)14691708Sstevel sg_console_claim(void)
14701708Sstevel {
1471*11311SSurya.Prakki@Sun.COM 	(void) prom_serengeti_set_console_input(SGCN_OBP_STR);
14721708Sstevel }
14731708Sstevel 
14741708Sstevel static void
sg_console_release(void)14751708Sstevel sg_console_release(void)
14761708Sstevel {
1477*11311SSurya.Prakki@Sun.COM 	(void) prom_serengeti_set_console_input(SGCN_CLNT_STR);
14781708Sstevel }
14791708Sstevel 
14801708Sstevel void
plat_kdi_init(kdi_t * kdi)14811708Sstevel plat_kdi_init(kdi_t *kdi)
14821708Sstevel {
14831708Sstevel 	kdi->pkdi_system_claim = sg_system_claim;
14841708Sstevel 	kdi->pkdi_system_release = sg_system_release;
14851708Sstevel 	kdi->pkdi_console_claim = sg_console_claim;
14861708Sstevel 	kdi->pkdi_console_release = sg_console_release;
14871708Sstevel }
1488