xref: /onnv-gate/usr/src/uts/sun4u/serengeti/io/sbdp_cpu.c (revision 11311:639e7bc0b42f)
11708Sstevel /*
21708Sstevel  * CDDL HEADER START
31708Sstevel  *
41708Sstevel  * The contents of this file are subject to the terms of the
51708Sstevel  * Common Development and Distribution License (the "License").
61708Sstevel  * 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  */
211708Sstevel 
221708Sstevel /*
23*11311SSurya.Prakki@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
241708Sstevel  * Use is subject to license terms.
251708Sstevel  */
261708Sstevel 
271708Sstevel /*
281708Sstevel  * CPU management for serengeti DR
291708Sstevel  *
301708Sstevel  * There are three states a CPU can be in:
311708Sstevel  *
321708Sstevel  *	disconnected:		In reset
331708Sstevel  *	connect,unconfigured:	Idling in OBP's idle loop
341708Sstevel  *	configured:		Running Solaris
351708Sstevel  *
361708Sstevel  * State transitions:
371708Sstevel  *
381708Sstevel  *                connect              configure
391708Sstevel  *              ------------>         ------------>
401708Sstevel  * disconnected              connected             configured
411708Sstevel  *                          unconfigured
421708Sstevel  *              <-----------         <-------------
431708Sstevel  *                disconnect           unconfigure
441708Sstevel  *
451708Sstevel  * Firmware involvements
461708Sstevel  *
471708Sstevel  *              start_cpu(SC)
481708Sstevel  *      prom_serengeti_wakeupcpu(OBP)
491708Sstevel  *              ------------>         ------------------------->
501708Sstevel  * disconnected              connected                         configured
511708Sstevel  *                          unconfigured
521708Sstevel  *              <-----------          <-------------------------
531708Sstevel  *      prom_serengeti_cpu_off(OBP)  prom_serengeti_cpu_off(OBP)
541708Sstevel  *               stop_cpu(SC)        prom_serengeti_wakeupcpu(OBP)
551708Sstevel  *
561708Sstevel  * SIR (Software Initiated Reset) is used to unconfigure a CPU.
571708Sstevel  * After the CPU has completed flushing the caches, it issues an
581708Sstevel  * sir instruction to put itself through POST.  POST detects that
591708Sstevel  * it is an SIR, and re-enters OBP as a slave.  When the operation
601708Sstevel  * completes successfully, the CPU will be idling in OBP.
611708Sstevel  */
621708Sstevel 
631708Sstevel #include <sys/obpdefs.h>
641708Sstevel #include <sys/types.h>
651708Sstevel #include <sys/cmn_err.h>
661708Sstevel #include <sys/cpuvar.h>
671708Sstevel #include <sys/membar.h>
681708Sstevel #include <sys/x_call.h>
691708Sstevel #include <sys/machsystm.h>
701708Sstevel #include <sys/cpu_sgnblk_defs.h>
711708Sstevel #include <sys/pte.h>
721708Sstevel #include <vm/hat_sfmmu.h>
731708Sstevel #include <sys/promif.h>
741708Sstevel #include <sys/note.h>
751708Sstevel #include <sys/vmsystm.h>
761708Sstevel #include <vm/seg_kmem.h>
771708Sstevel 
781708Sstevel #include <sys/sbd_ioctl.h>
791708Sstevel #include <sys/sbd.h>
801708Sstevel #include <sys/sbdp_priv.h>
811708Sstevel #include <sys/sbdp_mem.h>
821708Sstevel #include <sys/sbdp_error.h>
831708Sstevel #include <sys/sgsbbc_iosram.h>
841708Sstevel #include <sys/prom_plat.h>
851708Sstevel #include <sys/cheetahregs.h>
861708Sstevel 
871708Sstevel uint64_t	*sbdp_valp;
881708Sstevel extern uint64_t	va_to_pa(void *);
891708Sstevel static int	sbdp_cpu_ntries = 50000;
901708Sstevel static int	sbdp_cpu_delay = 100;
911708Sstevel void		sbdp_get_cpu_sram_addr(uint64_t, uint64_t);
921708Sstevel static int	cpusram_map(caddr_t *, pgcnt_t *);
931708Sstevel static void	cpusram_unmap(caddr_t *, pgcnt_t);
941708Sstevel extern int	prom_serengeti_wakeupcpu(pnode_t);
951708Sstevel extern int	prom_serengeti_cpu_off(pnode_t);
961708Sstevel extern sbdp_wnode_t *sbdp_get_wnodep(int);
971708Sstevel extern caddr_t	sbdp_shutdown_va;
981708Sstevel static int	sbdp_prom_get_cpu(void *arg, int changed);
991708Sstevel 
1001708Sstevel 
1011708Sstevel int
sbdp_disconnect_cpu(sbdp_handle_t * hp,dev_info_t * dip,processorid_t cpuid)1021708Sstevel sbdp_disconnect_cpu(sbdp_handle_t *hp, dev_info_t *dip, processorid_t cpuid)
1031708Sstevel {
1041708Sstevel 	pnode_t		nodeid;
1051708Sstevel 	int		bd, wnode;
1061708Sstevel 	sbdp_wnode_t	*wnodep;
1071708Sstevel 	sbdp_bd_t	*bdp = NULL;
1081708Sstevel 	int		rv = 0;
1091708Sstevel 	processorid_t	cpu = cpuid;
1101708Sstevel 	processorid_t	portid;
1111708Sstevel 	static fn_t	f = "sbdp_disconnect_cpu";
1121708Sstevel 
1131708Sstevel 	SBDP_DBG_FUNC("%s\n", f);
1141708Sstevel 
1151708Sstevel 	nodeid = ddi_get_nodeid(dip);
1161708Sstevel 
1171708Sstevel 	/*
1181708Sstevel 	 * Get board number and node number
1191708Sstevel 	 * The check for determining if nodeid is valid is done inside
1201708Sstevel 	 * sbdp_get_bd_and_wnode_num.
1211708Sstevel 	 */
1221708Sstevel 	if (SBDP_INJECT_ERROR(f, 0) ||
1231708Sstevel 	    sbdp_get_bd_and_wnode_num(nodeid, &bd, &wnode) != 0) {
1241708Sstevel 
1251708Sstevel 		rv = -1;
1261708Sstevel 		goto out;
1271708Sstevel 	}
1281708Sstevel 
1291708Sstevel 	/*
1301708Sstevel 	 * Grab the lock to prevent status threads from accessing
1311708Sstevel 	 * registers on the CPU when it is being put into reset.
1321708Sstevel 	 */
1331708Sstevel 	wnodep = sbdp_get_wnodep(wnode);
1341708Sstevel 	bdp = &wnodep->bds[bd];
1351708Sstevel 	ASSERT(bdp);
1361708Sstevel 	mutex_enter(&bdp->bd_mutex);
1371708Sstevel 
1381708Sstevel 	/*
1391708Sstevel 	 * Mark the CPU in reset.  This should be done before calling
1401708Sstevel 	 * the SC because we won't know at which stage it failed if
1411708Sstevel 	 * the SC call returns failure.
1421708Sstevel 	 */
1431708Sstevel 	sbdp_cpu_in_reset(wnode, bd, SG_CPUID_TO_CPU_UNIT(cpuid), 1);
1441708Sstevel 
1451708Sstevel 	/*
1461708Sstevel 	 * Ask OBP to mark the CPU as in POST
1471708Sstevel 	 */
1481708Sstevel 	if (SBDP_INJECT_ERROR(f, 1) || prom_serengeti_cpu_off(nodeid) != 0) {
1491708Sstevel 
1501708Sstevel 		rv = -1;
1511708Sstevel 		goto out;
1521708Sstevel 	}
1531708Sstevel 
1541708Sstevel 	/*
1551708Sstevel 	 * Ask the SC to put the CPU into reset. If the first
1561708Sstevel 	 * core is not present, the stop CPU interface needs
1571708Sstevel 	 * to be called with the portid rather than the cpuid.
1581708Sstevel 	 */
1591708Sstevel 	portid = SG_CPUID_TO_PORTID(cpuid);
1601708Sstevel 	if (!SBDP_IS_CPU_PRESENT(bdp, SG_CPUID_TO_CPU_UNIT(portid))) {
1611708Sstevel 		cpu = portid;
1621708Sstevel 	}
1631708Sstevel 
1641708Sstevel 	if (SBDP_INJECT_ERROR(f, 2) || sbdp_stop_cpu(cpu) != 0) {
1651708Sstevel 
1661708Sstevel 		rv = -1;
1671708Sstevel 		goto out;
1681708Sstevel 	}
1691708Sstevel 
1701708Sstevel out:
1711708Sstevel 	if (bdp != NULL) {
1721708Sstevel 		mutex_exit(&bdp->bd_mutex);
1731708Sstevel 	}
1741708Sstevel 
1751708Sstevel 	if (rv != 0) {
1761708Sstevel 		sbdp_set_err(hp->h_err, ESGT_STOPCPU, NULL);
1771708Sstevel 	}
1781708Sstevel 
1791708Sstevel 	return (rv);
1801708Sstevel }
1811708Sstevel 
1821708Sstevel int
sbdp_connect_cpu(sbdp_handle_t * hp,dev_info_t * dip,processorid_t cpuid)1831708Sstevel sbdp_connect_cpu(sbdp_handle_t *hp, dev_info_t *dip, processorid_t cpuid)
1841708Sstevel {
1851708Sstevel 	pnode_t		nodeid;
1861708Sstevel 	sbd_error_t	*sep;
1871708Sstevel 	int		i;
1881708Sstevel 	int		bd, wnode;
1891708Sstevel 	int		rv = 0;
1901708Sstevel 	static fn_t	f = "sbdp_connect_cpu";
1911708Sstevel 
1921708Sstevel 	SBDP_DBG_FUNC("%s\n", f);
1931708Sstevel 
1941708Sstevel 	sep = hp->h_err;
1951708Sstevel 
1961708Sstevel 	nodeid = ddi_get_nodeid(dip);
1971708Sstevel 
1981708Sstevel 	/*
1991708Sstevel 	 * The check for determining if nodeid is valid is done inside
2001708Sstevel 	 * sbdp_get_bd_and_wnode_num.
2011708Sstevel 	 */
2021708Sstevel 	if (SBDP_INJECT_ERROR(f, 0) ||
2031708Sstevel 	    sbdp_get_bd_and_wnode_num(nodeid, &bd, &wnode) != 0) {
2041708Sstevel 
2051708Sstevel 		rv = -1;
2061708Sstevel 		goto out;
2071708Sstevel 	}
2081708Sstevel 
2091708Sstevel 	/*
2101708Sstevel 	 * Ask the SC to bring the CPU out of reset.
2111708Sstevel 	 * At this point, the sb_dev_present bit is not set for the CPU.
2121708Sstevel 	 * From sbd point of view the CPU is not present yet.  No
2131708Sstevel 	 * status threads will try to read registers off the CPU.
2141708Sstevel 	 * Since we are already holding sb_mutex, it is not necessary
2151708Sstevel 	 * to grab the board mutex when checking and setting the
2161708Sstevel 	 * cpus_in_reset bit.
2171708Sstevel 	 */
2181708Sstevel 	if (sbdp_is_cpu_in_reset(wnode, bd, SG_CPUID_TO_CPU_UNIT(cpuid))) {
2191708Sstevel 
2201708Sstevel 		sbdp_wnode_t	*wnodep;
2211708Sstevel 		sbdp_bd_t	*bdp = NULL;
2221708Sstevel 		processorid_t	cpu = cpuid;
2231708Sstevel 		processorid_t	portid;
2241708Sstevel 
2251708Sstevel 		wnodep = sbdp_get_wnodep(wnode);
2261708Sstevel 		bdp = &wnodep->bds[bd];
2271708Sstevel 		ASSERT(bdp);
2281708Sstevel 
2291708Sstevel 		/*
2301708Sstevel 		 * If the first core is not present, the start CPU
2311708Sstevel 		 * interface needs to be called with the portid rather
2321708Sstevel 		 * than the cpuid.
2331708Sstevel 		 */
2341708Sstevel 		portid = SG_CPUID_TO_PORTID(cpuid);
2351708Sstevel 		if (!SBDP_IS_CPU_PRESENT(bdp, SG_CPUID_TO_CPU_UNIT(portid))) {
2361708Sstevel 			cpu = portid;
2371708Sstevel 		}
2381708Sstevel 
2391708Sstevel 		if (SBDP_INJECT_ERROR(f, 1) || sbdp_start_cpu(cpu) != 0) {
2401708Sstevel 
2411708Sstevel 			rv = -1;
2421708Sstevel 			goto out;
2431708Sstevel 		}
2441708Sstevel 
2451708Sstevel 		if (SBDP_INJECT_ERROR(f, 2) ||
2461708Sstevel 		    prom_serengeti_wakeupcpu(nodeid) != 0) {
2471708Sstevel 
2481708Sstevel 			rv = -1;
2491708Sstevel 			goto out;
2501708Sstevel 		}
2511708Sstevel 	}
2521708Sstevel 
2531708Sstevel 	/*
2541708Sstevel 	 * Mark the CPU out of reset.
2551708Sstevel 	 */
2561708Sstevel 	sbdp_cpu_in_reset(wnode, bd, SG_CPUID_TO_CPU_UNIT(cpuid), 0);
2571708Sstevel 
2581708Sstevel 	/*
2591708Sstevel 	 * Refresh the bd info
2601708Sstevel 	 * we need to wait until all cpus are out of reset
2611708Sstevel 	 */
2621708Sstevel 	for (i = 0; i < SG_MAX_CPUS_PER_BD; i++)
2631708Sstevel 		if (sbdp_is_cpu_present(wnode, bd, i) &&
2641708Sstevel 		    sbdp_is_cpu_in_reset(wnode, bd, i) == 1) {
2651708Sstevel 			break;
2661708Sstevel 		}
2671708Sstevel 
2681708Sstevel 	if (i == SG_MAX_CPUS_PER_BD) {
2691708Sstevel 		/*
2701708Sstevel 		 * All cpus are out of reset so it is safe to
2711708Sstevel 		 * update the bd info
2721708Sstevel 		 */
2731708Sstevel 		sbdp_add_new_bd_info(wnode, bd);
2741708Sstevel 	}
2751708Sstevel 
2761708Sstevel out:
2771708Sstevel 	if (rv != 0)
2781708Sstevel 		sbdp_set_err(sep, ESGT_WAKEUPCPU, NULL);
2791708Sstevel 
2801708Sstevel 	return (rv);
2811708Sstevel }
2821708Sstevel 
2831708Sstevel int
sbdp_cpu_poweron(struct cpu * cp)2841708Sstevel sbdp_cpu_poweron(struct cpu *cp)
2851708Sstevel {
2861708Sstevel 	int		cpuid;
2871708Sstevel 	int		ntries;
2881708Sstevel 	pnode_t		nodeid;
2891708Sstevel 	extern void	restart_other_cpu(int);
2901708Sstevel 	static fn_t	f = "sbdp_cpu_poweron";
2911708Sstevel 
2921708Sstevel 	SBDP_DBG_FUNC("%s\n", f);
2931708Sstevel 
2941708Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
2951708Sstevel 
2961708Sstevel 	ntries = sbdp_cpu_ntries;
2971708Sstevel 	cpuid = cp->cpu_id;
2981708Sstevel 
2991708Sstevel 	nodeid = cpunodes[cpuid].nodeid;
3001708Sstevel 	ASSERT(nodeid != (pnode_t)0);
3011708Sstevel 
3021708Sstevel 	/*
3031708Sstevel 	 * This is a safe guard in case the CPU has taken a trap
3041708Sstevel 	 * and idling in POST.
3051708Sstevel 	 */
3061708Sstevel 	if (SBDP_INJECT_ERROR(f, 0) ||
3071708Sstevel 	    prom_serengeti_wakeupcpu(nodeid) != 0) {
3081708Sstevel 
3091708Sstevel 		return (EBUSY);
3101708Sstevel 	}
3111708Sstevel 
3121708Sstevel 	cp->cpu_flags &= ~CPU_POWEROFF;
3131708Sstevel 
3141708Sstevel 	/*
3151708Sstevel 	 * NOTE: restart_other_cpu pauses cpus during the
3161708Sstevel 	 *	slave cpu start.  This helps to quiesce the
3171708Sstevel 	 *	bus traffic a bit which makes the tick sync
3181708Sstevel 	 *	routine in the prom more robust.
3191708Sstevel 	 */
3201708Sstevel 	SBDP_DBG_CPU("%s: COLD START for cpu (%d)\n", f, cpuid);
3211708Sstevel 
3221708Sstevel 	restart_other_cpu(cpuid);
3231708Sstevel 
3241708Sstevel 	SBDP_DBG_CPU("after restarting other cpus\n");
3251708Sstevel 
3261708Sstevel 	/*
3271708Sstevel 	 * Wait for the cpu to reach its idle thread before
3281708Sstevel 	 * we zap him with a request to blow away the mappings
3291708Sstevel 	 * he (might) have for the sbdp_shutdown_asm code
3301708Sstevel 	 * he may have executed on unconfigure.
3311708Sstevel 	 */
3321708Sstevel 	while ((cp->cpu_thread != cp->cpu_idle_thread) && (ntries > 0)) {
3331708Sstevel 		DELAY(sbdp_cpu_delay);
3341708Sstevel 		ntries--;
3351708Sstevel 	}
3361708Sstevel 
3371708Sstevel 	SBDP_DBG_CPU("%s: waited %d out of %d loops for cpu %d\n",
3381708Sstevel 	    f, sbdp_cpu_ntries - ntries, sbdp_cpu_ntries, cpuid);
3391708Sstevel 
3401708Sstevel 	return (0);
3411708Sstevel }
3421708Sstevel 
3431708Sstevel 
3441708Sstevel #define	SBDP_CPU_SRAM_ADDR	0x7fff0900000ull
3451708Sstevel #define	SBDP_CPU_SRAM_SIZE	0x20000ull
3461708Sstevel 
3471708Sstevel static const char cpyren_key[] = "COPYREN";
3481708Sstevel 
3491708Sstevel static uint64_t bbsram_pa;
3501708Sstevel static uint_t bbsram_size;
3511708Sstevel 
3521708Sstevel typedef struct {
3531708Sstevel 	caddr_t		vaddr;
3541708Sstevel 	pgcnt_t		npages;
3551708Sstevel 	uint64_t	*pa;
3561708Sstevel 	uint_t		*size;
3571708Sstevel } sbdp_cpu_sram_map_t;
3581708Sstevel 
3591708Sstevel int
sbdp_cpu_poweroff(struct cpu * cp)3601708Sstevel sbdp_cpu_poweroff(struct cpu *cp)
3611708Sstevel {
3621708Sstevel 	processorid_t	cpuid;
3631708Sstevel 	static void	sbdp_cpu_shutdown_self(void);
3641708Sstevel 	pnode_t		nodeid;
3651708Sstevel 	sbdp_cpu_sram_map_t	map;
3661708Sstevel 	static fn_t	f = "sbdp_cpu_poweroff";
3671708Sstevel 
3681708Sstevel 	SBDP_DBG_FUNC("%s\n", f);
3691708Sstevel 
3701708Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
3711708Sstevel 
3721708Sstevel 	/*
3731708Sstevel 	 * Capture all CPUs (except for detaching proc) to prevent
3741708Sstevel 	 * crosscalls to the detaching proc until it has cleared its
3751708Sstevel 	 * bit in cpu_ready_set.
3761708Sstevel 	 */
3771708Sstevel 	cpuid = cp->cpu_id;
3781708Sstevel 
3791708Sstevel 	nodeid = cpunodes[cpuid].nodeid;
3801708Sstevel 	ASSERT(nodeid != (pnode_t)0);
3811708Sstevel 
3821708Sstevel 	*sbdp_valp = 0ull;
3831708Sstevel 	/*
3841708Sstevel 	 * Do the cpu sram mapping now.  This avoids problems with
3851708Sstevel 	 * mutexes and high PILS
3861708Sstevel 	 */
3871708Sstevel 	if (SBDP_INJECT_ERROR(f, 0) ||
3881708Sstevel 	    cpusram_map(&map.vaddr, &map.npages) != DDI_SUCCESS) {
3891708Sstevel 		return (EBUSY);
3901708Sstevel 	}
3911708Sstevel 
3921708Sstevel 	map.pa = &bbsram_pa;
3931708Sstevel 	map.size = &bbsram_size;
3941708Sstevel 
3951708Sstevel 	/*
3961708Sstevel 	 * Do a cross call to the cpu so it obtains the base address
3971708Sstevel 	 */
3981708Sstevel 	xc_one(cpuid, sbdp_get_cpu_sram_addr, (uint64_t)&map,
3991708Sstevel 	    (uint64_t)NULL);
4001708Sstevel 
4011708Sstevel 	cpusram_unmap(&map.vaddr, map.npages);
4021708Sstevel 
4031708Sstevel 	if (SBDP_INJECT_ERROR(f, 1) || bbsram_size == 0) {
4041708Sstevel 		cmn_err(CE_WARN, "cpu%d: Key \"%s\" missing from CPU SRAM TOC",
4051708Sstevel 		    cpuid, cpyren_key);
4061708Sstevel 		return (EBUSY);
4071708Sstevel 	}
4081708Sstevel 
4091708Sstevel 	if ((bbsram_pa & MMU_PAGEOFFSET) != 0) {
4101708Sstevel 		cmn_err(CE_WARN, "cpu%d: CPU SRAM key \"%s\" not page aligned, "
4111708Sstevel 		    "offset = 0x%lx", cpuid, cpyren_key,
412*11311SSurya.Prakki@Sun.COM 		    (bbsram_pa - (uint64_t)SBDP_CPU_SRAM_ADDR));
4131708Sstevel 		return (EBUSY);
4141708Sstevel 	}
4151708Sstevel 
4161708Sstevel 	if (bbsram_size < MMU_PAGESIZE) {
4171708Sstevel 		cmn_err(CE_WARN, "cpu%d: CPU SRAM key \"%s\" too small, "
4181708Sstevel 		    "size = 0x%x", cpuid, cpyren_key, bbsram_size);
4191708Sstevel 		return (EBUSY);
4201708Sstevel 	}
4211708Sstevel 
4221708Sstevel 	/*
4231708Sstevel 	 * Capture all CPUs (except for detaching proc) to prevent
4241708Sstevel 	 * crosscalls to the detaching proc until it has cleared its
4251708Sstevel 	 * bit in cpu_ready_set.
4261708Sstevel 	 *
4271708Sstevel 	 * The CPU's remain paused and the prom_mutex is known to be free.
4281708Sstevel 	 * This prevents the x-trap victim from blocking when doing prom
4291708Sstevel 	 * IEEE-1275 calls at a high PIL level.
4301708Sstevel 	 */
4311708Sstevel 
4321708Sstevel 	promsafe_pause_cpus();
4331708Sstevel 
4341708Sstevel 	/*
4351708Sstevel 	 * Quiesce interrupts on the target CPU. We do this by setting
4361708Sstevel 	 * the CPU 'not ready'- (i.e. removing the CPU from cpu_ready_set) to
4371708Sstevel 	 * prevent it from receiving cross calls and cross traps.
4381708Sstevel 	 * This prevents the processor from receiving any new soft interrupts.
4391708Sstevel 	 */
4401708Sstevel 
4411708Sstevel 	mp_cpu_quiesce(cp);
4421708Sstevel 
4431708Sstevel 	/* tell the prom the cpu is going away */
4441708Sstevel 	if (SBDP_INJECT_ERROR(f, 2) || prom_serengeti_cpu_off(nodeid) != 0)
4451708Sstevel 		return (EBUSY);
4461708Sstevel 
4471708Sstevel 	/*
4481708Sstevel 	 * An sir instruction is issued at the end of the shutdown
4491708Sstevel 	 * routine to make the CPU go through POST and re-enter OBP.
4501708Sstevel 	 */
4511708Sstevel 	xt_one_unchecked(cp->cpu_id, (xcfunc_t *)idle_stop_xcall,
4521708Sstevel 	    (uint64_t)sbdp_cpu_shutdown_self, 0);
4531708Sstevel 
4541708Sstevel 	*sbdp_valp = 3ull;
4551708Sstevel 
4561708Sstevel 	start_cpus();
4571708Sstevel 
4581708Sstevel 	/*
4591708Sstevel 	 * Wait until we reach the OBP idle loop or time out.
4601708Sstevel 	 * prom_serengeti_wakeupcpu waits for up to 60 seconds for the
4611708Sstevel 	 * CPU to reach OBP idle loop.
4621708Sstevel 	 */
4631708Sstevel 	if (SBDP_INJECT_ERROR(f, 3) ||
4641708Sstevel 	    prom_serengeti_wakeupcpu(nodeid) != 0) {
4651708Sstevel 
4661708Sstevel 		/*
4671708Sstevel 		 * If it fails here, we still consider the unconfigure
4681708Sstevel 		 * operation as successful.
4691708Sstevel 		 */
4701708Sstevel 		cmn_err(CE_WARN, "cpu%d: CPU failed to enter OBP idle loop.\n",
4711708Sstevel 		    cpuid);
4721708Sstevel 	}
4731708Sstevel 
4741708Sstevel 	ASSERT(!(CPU_IN_SET(cpu_ready_set, cpuid)));
4751708Sstevel 
4761708Sstevel 	bbsram_pa = 0;
4771708Sstevel 	bbsram_size = 0;
4781708Sstevel 
4791708Sstevel 	return (0);
4801708Sstevel }
4811708Sstevel 
4821708Sstevel processorid_t
sbdp_get_cpuid(sbdp_handle_t * hp,dev_info_t * dip)4831708Sstevel sbdp_get_cpuid(sbdp_handle_t *hp, dev_info_t *dip)
4841708Sstevel {
4851708Sstevel 	int		cpuid;
4861708Sstevel 	char		type[OBP_MAXPROPNAME];
4871708Sstevel 	pnode_t		nodeid;
4881708Sstevel 	sbd_error_t	*sep;
4891708Sstevel 	static fn_t	f = "sbdp_get_cpuid";
4901708Sstevel 
4911708Sstevel 	SBDP_DBG_FUNC("%s\n", f);
4921708Sstevel 
4931708Sstevel 	nodeid = ddi_get_nodeid(dip);
4941708Sstevel 	if (sbdp_is_node_bad(nodeid))
4951708Sstevel 		return (-1);
4961708Sstevel 
4971708Sstevel 	sep = hp->h_err;
4981708Sstevel 
4991708Sstevel 	if (prom_getproplen(nodeid, "device_type") < OBP_MAXPROPNAME)
5001708Sstevel 		(void) prom_getprop(nodeid, "device_type", (caddr_t)type);
5011708Sstevel 	else {
5021708Sstevel 		sbdp_set_err(sep, ESGT_NO_DEV_TYPE, NULL);
5031708Sstevel 		return (-1);
5041708Sstevel 	}
5051708Sstevel 
5061708Sstevel 	if (strcmp(type, "cpu") != 0) {
5071708Sstevel 		sbdp_set_err(sep, ESGT_NOT_CPUTYPE, NULL);
5081708Sstevel 		return (-1);
5091708Sstevel 	}
5101708Sstevel 
5111708Sstevel 	/*
5121708Sstevel 	 * Check to see if property "cpuid" exists first.
5131708Sstevel 	 * If not, check for "portid".
5141708Sstevel 	 */
5151708Sstevel 	if (prom_getprop(nodeid, "cpuid", (caddr_t)&cpuid) == -1)
5161708Sstevel 		if (prom_getprop(nodeid, "portid", (caddr_t)&cpuid) == -1) {
5171708Sstevel 
5181708Sstevel 			return (-1);
5191708Sstevel 	}
5201708Sstevel 
5211708Sstevel 	return ((processorid_t)cpuid & SG_CPU_ID_MASK);
5221708Sstevel }
5231708Sstevel 
5241708Sstevel int
sbdp_cpu_get_impl(sbdp_handle_t * hp,dev_info_t * dip)5251708Sstevel sbdp_cpu_get_impl(sbdp_handle_t *hp, dev_info_t *dip)
5261708Sstevel {
5271708Sstevel 	int		impl;
5281708Sstevel 	char		type[OBP_MAXPROPNAME];
5291708Sstevel 	pnode_t		nodeid;
5301708Sstevel 	sbd_error_t	*sep;
5311708Sstevel 	static fn_t	f = "sbdp_cpu_get_impl";
5321708Sstevel 
5331708Sstevel 	SBDP_DBG_FUNC("%s\n", f);
5341708Sstevel 
5351708Sstevel 	nodeid = ddi_get_nodeid(dip);
5361708Sstevel 	if (sbdp_is_node_bad(nodeid))
5371708Sstevel 		return (-1);
5381708Sstevel 
5391708Sstevel 	sep = hp->h_err;
5401708Sstevel 
5411708Sstevel 	if (prom_getproplen(nodeid, "device_type") < OBP_MAXPROPNAME)
5421708Sstevel 		(void) prom_getprop(nodeid, "device_type", (caddr_t)type);
5431708Sstevel 	else {
5441708Sstevel 		sbdp_set_err(sep, ESGT_NO_DEV_TYPE, NULL);
5451708Sstevel 		return (-1);
5461708Sstevel 	}
5471708Sstevel 
5481708Sstevel 	if (strcmp(type, "cpu") != 0) {
5491708Sstevel 		sbdp_set_err(sep, ESGT_NOT_CPUTYPE, NULL);
5501708Sstevel 		return (-1);
5511708Sstevel 	}
5521708Sstevel 
5531708Sstevel 	/*
5541708Sstevel 	 * Get the implementation# property.
5551708Sstevel 	 */
5561708Sstevel 	if (prom_getprop(nodeid, "implementation#", (caddr_t)&impl) == -1)
5571708Sstevel 		return (-1);
5581708Sstevel 
5591708Sstevel 	return (impl);
5601708Sstevel }
5611708Sstevel 
5621708Sstevel struct sbdp_prom_get_node_args {
5631708Sstevel 	pnode_t node;		/* current node */
5641708Sstevel 	processorid_t portid;	/* portid we are looking for */
5651708Sstevel 	pnode_t result_node;	/* node found with the above portid */
5661708Sstevel };
5671708Sstevel 
5681708Sstevel pnode_t
sbdp_find_nearby_cpu_by_portid(pnode_t nodeid,processorid_t portid)5691708Sstevel sbdp_find_nearby_cpu_by_portid(pnode_t nodeid, processorid_t portid)
5701708Sstevel {
5711708Sstevel 	struct sbdp_prom_get_node_args arg;
5721708Sstevel 	static fn_t	f = "sbdp_find_nearby_cpu_by_portid";
5731708Sstevel 
5741708Sstevel 	SBDP_DBG_FUNC("%s\n", f);
5751708Sstevel 
5761708Sstevel 	arg.node = nodeid;
5771708Sstevel 	arg.portid = portid;
5781708Sstevel 	(void) prom_tree_access(sbdp_prom_get_cpu, &arg, NULL);
5791708Sstevel 
5801708Sstevel 	return (arg.result_node);
5811708Sstevel }
5821708Sstevel 
5831708Sstevel /*ARGSUSED*/
5841708Sstevel static int
sbdp_prom_get_cpu(void * arg,int changed)5851708Sstevel sbdp_prom_get_cpu(void *arg, int changed)
5861708Sstevel {
5871708Sstevel 	int	portid;
5881708Sstevel 	pnode_t	parent, cur_node;
5891708Sstevel 	struct sbdp_prom_get_node_args *argp = arg;
5901708Sstevel 	static fn_t	f = "sbdp_prom_get_cpu";
5911708Sstevel 
5921708Sstevel 	SBDP_DBG_FUNC("%s\n", f);
5931708Sstevel 
5941708Sstevel 	parent = prom_parentnode(argp->node);
5951708Sstevel 
5961708Sstevel 	for (cur_node = prom_childnode(parent); cur_node != OBP_NONODE;
5971708Sstevel 	    cur_node = prom_nextnode(cur_node)) {
5981708Sstevel 
5991708Sstevel 		if (prom_getprop(cur_node, OBP_PORTID, (caddr_t)&portid) < 0)
6001708Sstevel 			continue;
6011708Sstevel 
6021708Sstevel 		if ((portid == argp->portid) && (cur_node != argp->node))
6031708Sstevel 			break;
6041708Sstevel 	}
6051708Sstevel 
6061708Sstevel 	argp->result_node = cur_node;
6071708Sstevel 
6081708Sstevel 	return (0);
6091708Sstevel }
6101708Sstevel 
6111708Sstevel 
6121708Sstevel /*
6131708Sstevel  * A detaching CPU is xcalled with an xtrap to sbdp_cpu_stop_self() after
6141708Sstevel  * it has been offlined. The function of this routine is to get the cpu
6151708Sstevel  * spinning in a safe place. The requirement is that the system will not
6161708Sstevel  * reference anything on the detaching board (memory and i/o is detached
6171708Sstevel  * elsewhere) and that the CPU not reference anything on any other board
6181708Sstevel  * in the system.  This isolation is required during and after the writes
6191708Sstevel  * to the domain masks to remove the board from the domain.
6201708Sstevel  *
6211708Sstevel  * To accomplish this isolation the following is done:
6221708Sstevel  *	0) Map the CPUSRAM to obtain the correct address in SRAM
6231708Sstevel  *      1) Create a locked mapping to a location in CPU SRAM where
6241708Sstevel  *      the cpu will execute.
6251708Sstevel  *      2) Copy the target function (sbdp_shutdown_asm) in which
6261708Sstevel  *      the cpu will execute into CPU SRAM.
6271708Sstevel  *      3) Jump into function with CPU SRAM.
6281708Sstevel  *      Function will:
6291708Sstevel  *      3.1) Flush its Ecache (displacement).
6301708Sstevel  *      3.2) Flush its Dcache with HW mechanism.
6311708Sstevel  *      3.3) Flush its Icache with HW mechanism.
6321708Sstevel  *      3.4) Flush all valid and _unlocked_ D-TLB entries.
6331708Sstevel  *      3.5) Flush all valid and _unlocked_ I-TLB entries.
6341708Sstevel  *      4) Jump into a tight loop.
6351708Sstevel  */
6361708Sstevel 
6371708Sstevel static void
sbdp_cpu_stop_self(uint64_t pa)6381708Sstevel sbdp_cpu_stop_self(uint64_t pa)
6391708Sstevel {
6401708Sstevel 	cpu_t		*cp = CPU;
6411708Sstevel 	int		cpuid = cp->cpu_id;
6421708Sstevel 	tte_t		tte;
6431708Sstevel 	volatile uint_t	*src, *dst;
6441708Sstevel 	uint_t		funclen;
6451708Sstevel 	sbdp_shutdown_t	sht;
6461708Sstevel 	uint_t		bbsram_pfn;
6471708Sstevel 	uint64_t	bbsram_addr;
6481708Sstevel 	void		(*bbsram_func)(sbdp_shutdown_t *);
6491708Sstevel 	extern void	sbdp_shutdown_asm(sbdp_shutdown_t *);
6501708Sstevel 	extern void	sbdp_shutdown_asm_end(void);
6511708Sstevel 
6521708Sstevel 	funclen = (uint_t)sbdp_shutdown_asm_end - (uint_t)sbdp_shutdown_asm;
6531708Sstevel 	ASSERT(funclen <= MMU_PAGESIZE);
6541708Sstevel 	ASSERT(bbsram_pa != 0);
6551708Sstevel 	ASSERT((bbsram_pa & MMU_PAGEOFFSET) == 0);
6561708Sstevel 	ASSERT(bbsram_size >= MMU_PAGESIZE);
6571708Sstevel 
6581708Sstevel 	stdphys(pa, 3);
6591708Sstevel 	bbsram_pfn = (uint_t)(bbsram_pa >> MMU_PAGESHIFT);
6601708Sstevel 
6611708Sstevel 	bbsram_addr = (uint64_t)sbdp_shutdown_va;
6621708Sstevel 	sht.estack = bbsram_addr + MMU_PAGESIZE;
6631708Sstevel 	sht.flushaddr = ecache_flushaddr;
6641708Sstevel 
6651708Sstevel 	tte.tte_inthi = TTE_VALID_INT | TTE_SZ_INT(TTE8K) |
6661708Sstevel 	    TTE_PFN_INTHI(bbsram_pfn);
6671708Sstevel 	tte.tte_intlo = TTE_PFN_INTLO(bbsram_pfn) |
6681708Sstevel 	    TTE_HWWR_INT | TTE_PRIV_INT | TTE_LCK_INT;
6692241Shuah 	sfmmu_dtlb_ld_kva(sbdp_shutdown_va, &tte); /* load dtlb */
6702241Shuah 	sfmmu_itlb_ld_kva(sbdp_shutdown_va, &tte); /* load itlb */
6711708Sstevel 
6721708Sstevel 	for (src = (uint_t *)sbdp_shutdown_asm, dst = (uint_t *)bbsram_addr;
6731708Sstevel 	    src < (uint_t *)sbdp_shutdown_asm_end; src++, dst++)
6741708Sstevel 	*dst = *src;
6751708Sstevel 
6761708Sstevel 	bbsram_func = (void (*)())bbsram_addr;
6771708Sstevel 	sht.size = (uint32_t)cpunodes[cpuid].ecache_size << 1;
6781708Sstevel 	sht.linesize = (uint32_t)cpunodes[cpuid].ecache_linesize;
6791708Sstevel 	sht.physaddr = pa;
6801708Sstevel 
6811708Sstevel 	/*
6821708Sstevel 	 * Signal to sbdp_cpu_poweroff() that we're just
6831708Sstevel 	 * about done.
6841708Sstevel 	 */
6851708Sstevel 	cp->cpu_m.in_prom = 1;
6861708Sstevel 
6871708Sstevel 	stdphys(pa, 4);
6881708Sstevel 	(*bbsram_func)(&sht);
6891708Sstevel }
6901708Sstevel 
6911708Sstevel /* ARGSUSED */
6921708Sstevel void
sbdp_get_cpu_sram_addr(uint64_t arg1,uint64_t arg2)6931708Sstevel sbdp_get_cpu_sram_addr(uint64_t arg1, uint64_t arg2)
6941708Sstevel {
6951708Sstevel 	uint64_t	*pap;
6961708Sstevel 	uint_t		*sizep;
6971708Sstevel 	struct iosram_toc *tocp;
6981708Sstevel 	uint_t		offset;
6991708Sstevel 	uint_t		size;
7001708Sstevel 	sbdp_cpu_sram_map_t *map;
7011708Sstevel 	int		i;
7021708Sstevel 	fn_t		f = "sbdp_get_cpu_sram_addr";
7031708Sstevel 
7041708Sstevel 	SBDP_DBG_FUNC("%s\n", f);
7051708Sstevel 
7061708Sstevel 	map = (sbdp_cpu_sram_map_t *)arg1;
7071708Sstevel 	tocp = (struct iosram_toc *)map->vaddr;
7081708Sstevel 	pap = map->pa;
7091708Sstevel 	sizep = map->size;
7101708Sstevel 
7111708Sstevel 	for (i = 0; i < tocp->iosram_tagno; i++) {
7121708Sstevel 		if (strcmp(tocp->iosram_keys[i].key, cpyren_key) == 0)
7131708Sstevel 			break;
7141708Sstevel 	}
7151708Sstevel 	if (i == tocp->iosram_tagno) {
7161708Sstevel 		*pap = 0;
7171708Sstevel 		*sizep = 0;
7181708Sstevel 		return;
7191708Sstevel 	}
7201708Sstevel 	offset = tocp->iosram_keys[i].offset;
7211708Sstevel 	size = tocp->iosram_keys[i].size;
7221708Sstevel 
7231708Sstevel 	/*
7241708Sstevel 	 * The address we want is the begining of cpusram + offset
7251708Sstevel 	 */
7261708Sstevel 	*pap = SBDP_CPU_SRAM_ADDR + offset;
7271708Sstevel 
7281708Sstevel 	*sizep = size;
7291708Sstevel }
7301708Sstevel 
7311708Sstevel static int
cpusram_map(caddr_t * vaddrp,pgcnt_t * npp)7321708Sstevel cpusram_map(caddr_t *vaddrp, pgcnt_t *npp)
7331708Sstevel {
7341708Sstevel 	uint_t		pgoffset;
7351708Sstevel 	pgcnt_t		npages;
7361708Sstevel 	pfn_t		pfn;
7371708Sstevel 	uint64_t	base;
7381708Sstevel 	caddr_t		kaddr;
7391708Sstevel 	uint_t		mapping_attr;
7401708Sstevel 
7411708Sstevel 	base = (uint64_t)SBDP_CPU_SRAM_ADDR & (~MMU_PAGEOFFSET);
7421708Sstevel 	pfn = mmu_btop(base);
7431708Sstevel 
7441708Sstevel 	/*
7451708Sstevel 	 * Do a quick sanity check to make sure we are in I/O space.
7461708Sstevel 	 */
7471708Sstevel 	if (pf_is_memory(pfn))
7481708Sstevel 		return (DDI_FAILURE);
7491708Sstevel 
7501708Sstevel 	pgoffset = (ulong_t)SBDP_CPU_SRAM_ADDR & MMU_PAGEOFFSET;
7511708Sstevel 	npages = mmu_btopr(SBDP_CPU_SRAM_SIZE + pgoffset);
7521708Sstevel 
7531708Sstevel 	kaddr = vmem_alloc(heap_arena, ptob(npages), VM_NOSLEEP);
7541708Sstevel 	if (kaddr == NULL)
7551708Sstevel 		return (DDI_ME_NORESOURCES);
7561708Sstevel 
7571708Sstevel 	mapping_attr = PROT_READ;
7581708Sstevel 	/*
7591708Sstevel 	 * Now map in the pages we've allocated...
7601708Sstevel 	 */
7611708Sstevel 	hat_devload(kas.a_hat, kaddr, ptob(npages), pfn, mapping_attr,
7621708Sstevel 	    HAT_LOAD_LOCK);
7631708Sstevel 
7641708Sstevel 	*vaddrp = kaddr + pgoffset;
7651708Sstevel 	*npp = npages;
7661708Sstevel 
7671708Sstevel 	return (DDI_SUCCESS);
7681708Sstevel }
7691708Sstevel 
7701708Sstevel static void
cpusram_unmap(caddr_t * vaddrp,pgcnt_t npages)7711708Sstevel cpusram_unmap(caddr_t *vaddrp, pgcnt_t npages)
7721708Sstevel {
7731708Sstevel 	uint_t  pgoffset;
7741708Sstevel 	caddr_t base;
7751708Sstevel 	caddr_t addr = *vaddrp;
7761708Sstevel 
7771708Sstevel 
7781708Sstevel 	pgoffset = (ulong_t)SBDP_CPU_SRAM_ADDR & MMU_PAGEOFFSET;
7791708Sstevel 	base = addr - pgoffset;
7801708Sstevel 	hat_unload(kas.a_hat, base, ptob(npages), HAT_UNLOAD_UNLOCK);
7811708Sstevel 	vmem_free(heap_arena, base, ptob(npages));
7821708Sstevel 
7831708Sstevel 	*vaddrp = 0;
7841708Sstevel }
7851708Sstevel 
7861708Sstevel 
7871708Sstevel static void
sbdp_cpu_shutdown_self(void)7881708Sstevel sbdp_cpu_shutdown_self(void)
7891708Sstevel {
7901708Sstevel 	cpu_t		*cp = CPU;
7911708Sstevel 	int		cpuid = cp->cpu_id;
7921708Sstevel 	extern void	flush_windows(void);
7931708Sstevel 	uint64_t	pa = va_to_pa((void *)sbdp_valp);
7941708Sstevel 
7951708Sstevel 	stdphys(pa, 8);
7961708Sstevel 	flush_windows();
7971708Sstevel 
7981708Sstevel 	(void) spl8();
7991708Sstevel 
8001708Sstevel 	stdphys(pa, 6);
8011708Sstevel 
8021708Sstevel 	ASSERT(cp->cpu_intr_actv == 0);
8031708Sstevel 	ASSERT(cp->cpu_thread == cp->cpu_idle_thread ||
8041708Sstevel 	    cp->cpu_thread == cp->cpu_startup_thread);
8051708Sstevel 
8061708Sstevel 	cp->cpu_flags = CPU_OFFLINE | CPU_QUIESCED | CPU_POWEROFF;
8071708Sstevel 
8081708Sstevel 	CPU_SIGNATURE(OS_SIG, SIGST_DETACHED, SIGSUBST_NULL, cpuid);
8091708Sstevel 
8101708Sstevel 	stdphys(pa, 7);
8111708Sstevel 	sbdp_cpu_stop_self(pa);
8121708Sstevel 
8131708Sstevel 	cmn_err(CE_PANIC, "sbdp_cpu_shutdown_self: CPU %d FAILED TO SHUTDOWN",
8141708Sstevel 	    cpuid);
8151708Sstevel }
8161708Sstevel 
8171708Sstevel typedef struct {
8181708Sstevel 	int	node;
8191708Sstevel 	int	board;
8201708Sstevel 	int 	non_panther_cpus;
8211708Sstevel } sbdp_node_walk_t;
8221708Sstevel 
8231708Sstevel static int
sbdp_find_non_panther_cpus(dev_info_t * dip,void * node_args)8241708Sstevel sbdp_find_non_panther_cpus(dev_info_t *dip, void *node_args)
8251708Sstevel {
8261708Sstevel 	int	impl, cpuid, portid;
8271708Sstevel 	int	buflen;
8281708Sstevel 	char	buf[OBP_MAXPROPNAME];
8291708Sstevel 	sbdp_node_walk_t *args = (sbdp_node_walk_t *)node_args;
8301708Sstevel 
8311708Sstevel 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
8321708Sstevel 	    DDI_PROP_DONTPASS, OBP_DEVICETYPE, (caddr_t)buf,
8331708Sstevel 	    &buflen) != DDI_PROP_SUCCESS) {
8341708Sstevel 		return (DDI_WALK_CONTINUE);
8351708Sstevel 	}
8361708Sstevel 
8371708Sstevel 	if (strcmp(buf, "cpu") != 0) {
8381708Sstevel 		return (DDI_WALK_CONTINUE);
8391708Sstevel 	}
8401708Sstevel 
8411708Sstevel 	if ((impl = ddi_getprop(DDI_DEV_T_ANY, dip,
8421708Sstevel 	    DDI_PROP_DONTPASS, "implementation#", -1)) == -1) {
8431708Sstevel 		return (DDI_WALK_CONTINUE);
8441708Sstevel 	}
8451708Sstevel 
8461708Sstevel 	if ((cpuid = ddi_getprop(DDI_DEV_T_ANY, dip,
8471708Sstevel 	    DDI_PROP_DONTPASS, "cpuid", -1)) == -1) {
8481708Sstevel 		return (DDI_WALK_CONTINUE);
8491708Sstevel 	}
8501708Sstevel 
8511708Sstevel 	portid = SG_CPUID_TO_PORTID(cpuid);
8521708Sstevel 
8531708Sstevel 	/* filter out nodes not on this board */
8541708Sstevel 	if (SG_PORTID_TO_BOARD_NUM(portid) != args->board ||
8551708Sstevel 	    SG_PORTID_TO_NODEID(portid) != args->node) {
8561708Sstevel 		return (DDI_WALK_PRUNECHILD);
8571708Sstevel 	}
8581708Sstevel 
8591708Sstevel 	switch (impl) {
8601708Sstevel 	case CHEETAH_IMPL:
8611708Sstevel 	case CHEETAH_PLUS_IMPL:
8621708Sstevel 	case JAGUAR_IMPL:
8631708Sstevel 		args->non_panther_cpus++;
8641708Sstevel 		break;
8651708Sstevel 	case PANTHER_IMPL:
8661708Sstevel 		break;
8671708Sstevel 	default:
8681708Sstevel 		ASSERT(0);
8691708Sstevel 		args->non_panther_cpus++;
8701708Sstevel 		break;
8711708Sstevel 	}
8721708Sstevel 
8731708Sstevel 	SBDP_DBG_CPU("cpuid=0x%x, portid=0x%x, impl=0x%x, device_type=%s",
8741708Sstevel 	    cpuid, portid, impl, buf);
8751708Sstevel 
8761708Sstevel 	return (DDI_WALK_CONTINUE);
8771708Sstevel }
8781708Sstevel 
8791708Sstevel int
sbdp_board_non_panther_cpus(int node,int board)8801708Sstevel sbdp_board_non_panther_cpus(int node, int board)
8811708Sstevel {
8821708Sstevel 	sbdp_node_walk_t arg = {0};
8831708Sstevel 
8841708Sstevel 	arg.node = node;
8851708Sstevel 	arg.board = board;
8861708Sstevel 
8871708Sstevel 	/*
8881708Sstevel 	 * Root node doesn't have to be held.
8891708Sstevel 	 */
8901708Sstevel 	ddi_walk_devs(ddi_root_node(), sbdp_find_non_panther_cpus,
8911708Sstevel 	    (void *)&arg);
8921708Sstevel 
8931708Sstevel 	return (arg.non_panther_cpus);
8941708Sstevel }
895