103831d35Sstevel /*
203831d35Sstevel  * CDDL HEADER START
303831d35Sstevel  *
403831d35Sstevel  * The contents of this file are subject to the terms of the
503831d35Sstevel  * Common Development and Distribution License (the "License").
603831d35Sstevel  * You may not use this file except in compliance with the License.
703831d35Sstevel  *
803831d35Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
903831d35Sstevel  * or http://www.opensolaris.org/os/licensing.
1003831d35Sstevel  * See the License for the specific language governing permissions
1103831d35Sstevel  * and limitations under the License.
1203831d35Sstevel  *
1303831d35Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
1403831d35Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1503831d35Sstevel  * If applicable, add the following below this CDDL HEADER, with the
1603831d35Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
1703831d35Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
1803831d35Sstevel  *
1903831d35Sstevel  * CDDL HEADER END
2003831d35Sstevel  */
2103831d35Sstevel 
2203831d35Sstevel /*
2307d06da5SSurya Prakki  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2403831d35Sstevel  * Use is subject to license terms.
25*48bbca81SDaniel Hoffman  * Copyright (c) 2016 by Delphix. All rights reserved.
2603831d35Sstevel  */
2703831d35Sstevel 
2803831d35Sstevel /*
2903831d35Sstevel  * CPU management for serengeti DR
3003831d35Sstevel  *
3103831d35Sstevel  * There are three states a CPU can be in:
3203831d35Sstevel  *
3303831d35Sstevel  *	disconnected:		In reset
3403831d35Sstevel  *	connect,unconfigured:	Idling in OBP's idle loop
3503831d35Sstevel  *	configured:		Running Solaris
3603831d35Sstevel  *
3703831d35Sstevel  * State transitions:
3803831d35Sstevel  *
3903831d35Sstevel  *                connect              configure
4003831d35Sstevel  *              ------------>         ------------>
4103831d35Sstevel  * disconnected              connected             configured
4203831d35Sstevel  *                          unconfigured
4303831d35Sstevel  *              <-----------         <-------------
4403831d35Sstevel  *                disconnect           unconfigure
4503831d35Sstevel  *
4603831d35Sstevel  * Firmware involvements
4703831d35Sstevel  *
4803831d35Sstevel  *              start_cpu(SC)
4903831d35Sstevel  *      prom_serengeti_wakeupcpu(OBP)
5003831d35Sstevel  *              ------------>         ------------------------->
5103831d35Sstevel  * disconnected              connected                         configured
5203831d35Sstevel  *                          unconfigured
5303831d35Sstevel  *              <-----------          <-------------------------
5403831d35Sstevel  *      prom_serengeti_cpu_off(OBP)  prom_serengeti_cpu_off(OBP)
5503831d35Sstevel  *               stop_cpu(SC)        prom_serengeti_wakeupcpu(OBP)
5603831d35Sstevel  *
5703831d35Sstevel  * SIR (Software Initiated Reset) is used to unconfigure a CPU.
5803831d35Sstevel  * After the CPU has completed flushing the caches, it issues an
5903831d35Sstevel  * sir instruction to put itself through POST.  POST detects that
6003831d35Sstevel  * it is an SIR, and re-enters OBP as a slave.  When the operation
6103831d35Sstevel  * completes successfully, the CPU will be idling in OBP.
6203831d35Sstevel  */
6303831d35Sstevel 
6403831d35Sstevel #include <sys/obpdefs.h>
6503831d35Sstevel #include <sys/types.h>
6603831d35Sstevel #include <sys/cmn_err.h>
6703831d35Sstevel #include <sys/cpuvar.h>
6803831d35Sstevel #include <sys/membar.h>
6903831d35Sstevel #include <sys/x_call.h>
7003831d35Sstevel #include <sys/machsystm.h>
7103831d35Sstevel #include <sys/cpu_sgnblk_defs.h>
7203831d35Sstevel #include <sys/pte.h>
7303831d35Sstevel #include <vm/hat_sfmmu.h>
7403831d35Sstevel #include <sys/promif.h>
7503831d35Sstevel #include <sys/note.h>
7603831d35Sstevel #include <sys/vmsystm.h>
7703831d35Sstevel #include <vm/seg_kmem.h>
7803831d35Sstevel 
7903831d35Sstevel #include <sys/sbd_ioctl.h>
8003831d35Sstevel #include <sys/sbd.h>
8103831d35Sstevel #include <sys/sbdp_priv.h>
8203831d35Sstevel #include <sys/sbdp_mem.h>
8303831d35Sstevel #include <sys/sbdp_error.h>
8403831d35Sstevel #include <sys/sgsbbc_iosram.h>
8503831d35Sstevel #include <sys/prom_plat.h>
8603831d35Sstevel #include <sys/cheetahregs.h>
8703831d35Sstevel 
8803831d35Sstevel uint64_t	*sbdp_valp;
8903831d35Sstevel extern uint64_t	va_to_pa(void *);
9003831d35Sstevel static int	sbdp_cpu_ntries = 50000;
9103831d35Sstevel static int	sbdp_cpu_delay = 100;
9203831d35Sstevel void		sbdp_get_cpu_sram_addr(uint64_t, uint64_t);
9303831d35Sstevel static int	cpusram_map(caddr_t *, pgcnt_t *);
9403831d35Sstevel static void	cpusram_unmap(caddr_t *, pgcnt_t);
9503831d35Sstevel extern int	prom_serengeti_wakeupcpu(pnode_t);
9603831d35Sstevel extern int	prom_serengeti_cpu_off(pnode_t);
9703831d35Sstevel extern sbdp_wnode_t *sbdp_get_wnodep(int);
9803831d35Sstevel extern caddr_t	sbdp_shutdown_va;
9903831d35Sstevel static int	sbdp_prom_get_cpu(void *arg, int changed);
1008682d1efSRichard Lowe static void	sbdp_cpu_shutdown_self(void);
10103831d35Sstevel 
10203831d35Sstevel int
sbdp_disconnect_cpu(sbdp_handle_t * hp,dev_info_t * dip,processorid_t cpuid)10303831d35Sstevel sbdp_disconnect_cpu(sbdp_handle_t *hp, dev_info_t *dip, processorid_t cpuid)
10403831d35Sstevel {
10503831d35Sstevel 	pnode_t		nodeid;
10603831d35Sstevel 	int		bd, wnode;
10703831d35Sstevel 	sbdp_wnode_t	*wnodep;
10803831d35Sstevel 	sbdp_bd_t	*bdp = NULL;
10903831d35Sstevel 	int		rv = 0;
11003831d35Sstevel 	processorid_t	cpu = cpuid;
11103831d35Sstevel 	processorid_t	portid;
11203831d35Sstevel 	static fn_t	f = "sbdp_disconnect_cpu";
11303831d35Sstevel 
11403831d35Sstevel 	SBDP_DBG_FUNC("%s\n", f);
11503831d35Sstevel 
11603831d35Sstevel 	nodeid = ddi_get_nodeid(dip);
11703831d35Sstevel 
11803831d35Sstevel 	/*
11903831d35Sstevel 	 * Get board number and node number
12003831d35Sstevel 	 * The check for determining if nodeid is valid is done inside
12103831d35Sstevel 	 * sbdp_get_bd_and_wnode_num.
12203831d35Sstevel 	 */
12303831d35Sstevel 	if (SBDP_INJECT_ERROR(f, 0) ||
12403831d35Sstevel 	    sbdp_get_bd_and_wnode_num(nodeid, &bd, &wnode) != 0) {
12503831d35Sstevel 
12603831d35Sstevel 		rv = -1;
12703831d35Sstevel 		goto out;
12803831d35Sstevel 	}
12903831d35Sstevel 
13003831d35Sstevel 	/*
13103831d35Sstevel 	 * Grab the lock to prevent status threads from accessing
13203831d35Sstevel 	 * registers on the CPU when it is being put into reset.
13303831d35Sstevel 	 */
13403831d35Sstevel 	wnodep = sbdp_get_wnodep(wnode);
13503831d35Sstevel 	bdp = &wnodep->bds[bd];
13603831d35Sstevel 	ASSERT(bdp);
13703831d35Sstevel 	mutex_enter(&bdp->bd_mutex);
13803831d35Sstevel 
13903831d35Sstevel 	/*
14003831d35Sstevel 	 * Mark the CPU in reset.  This should be done before calling
14103831d35Sstevel 	 * the SC because we won't know at which stage it failed if
14203831d35Sstevel 	 * the SC call returns failure.
14303831d35Sstevel 	 */
14403831d35Sstevel 	sbdp_cpu_in_reset(wnode, bd, SG_CPUID_TO_CPU_UNIT(cpuid), 1);
14503831d35Sstevel 
14603831d35Sstevel 	/*
14703831d35Sstevel 	 * Ask OBP to mark the CPU as in POST
14803831d35Sstevel 	 */
14903831d35Sstevel 	if (SBDP_INJECT_ERROR(f, 1) || prom_serengeti_cpu_off(nodeid) != 0) {
15003831d35Sstevel 
15103831d35Sstevel 		rv = -1;
15203831d35Sstevel 		goto out;
15303831d35Sstevel 	}
15403831d35Sstevel 
15503831d35Sstevel 	/*
15603831d35Sstevel 	 * Ask the SC to put the CPU into reset. If the first
15703831d35Sstevel 	 * core is not present, the stop CPU interface needs
15803831d35Sstevel 	 * to be called with the portid rather than the cpuid.
15903831d35Sstevel 	 */
16003831d35Sstevel 	portid = SG_CPUID_TO_PORTID(cpuid);
16103831d35Sstevel 	if (!SBDP_IS_CPU_PRESENT(bdp, SG_CPUID_TO_CPU_UNIT(portid))) {
16203831d35Sstevel 		cpu = portid;
16303831d35Sstevel 	}
16403831d35Sstevel 
16503831d35Sstevel 	if (SBDP_INJECT_ERROR(f, 2) || sbdp_stop_cpu(cpu) != 0) {
16603831d35Sstevel 
16703831d35Sstevel 		rv = -1;
16803831d35Sstevel 		goto out;
16903831d35Sstevel 	}
17003831d35Sstevel 
17103831d35Sstevel out:
17203831d35Sstevel 	if (bdp != NULL) {
17303831d35Sstevel 		mutex_exit(&bdp->bd_mutex);
17403831d35Sstevel 	}
17503831d35Sstevel 
17603831d35Sstevel 	if (rv != 0) {
17703831d35Sstevel 		sbdp_set_err(hp->h_err, ESGT_STOPCPU, NULL);
17803831d35Sstevel 	}
17903831d35Sstevel 
18003831d35Sstevel 	return (rv);
18103831d35Sstevel }
18203831d35Sstevel 
18303831d35Sstevel int
sbdp_connect_cpu(sbdp_handle_t * hp,dev_info_t * dip,processorid_t cpuid)18403831d35Sstevel sbdp_connect_cpu(sbdp_handle_t *hp, dev_info_t *dip, processorid_t cpuid)
18503831d35Sstevel {
18603831d35Sstevel 	pnode_t		nodeid;
18703831d35Sstevel 	sbd_error_t	*sep;
18803831d35Sstevel 	int		i;
18903831d35Sstevel 	int		bd, wnode;
19003831d35Sstevel 	int		rv = 0;
19103831d35Sstevel 	static fn_t	f = "sbdp_connect_cpu";
19203831d35Sstevel 
19303831d35Sstevel 	SBDP_DBG_FUNC("%s\n", f);
19403831d35Sstevel 
19503831d35Sstevel 	sep = hp->h_err;
19603831d35Sstevel 
19703831d35Sstevel 	nodeid = ddi_get_nodeid(dip);
19803831d35Sstevel 
19903831d35Sstevel 	/*
20003831d35Sstevel 	 * The check for determining if nodeid is valid is done inside
20103831d35Sstevel 	 * sbdp_get_bd_and_wnode_num.
20203831d35Sstevel 	 */
20303831d35Sstevel 	if (SBDP_INJECT_ERROR(f, 0) ||
20403831d35Sstevel 	    sbdp_get_bd_and_wnode_num(nodeid, &bd, &wnode) != 0) {
20503831d35Sstevel 
20603831d35Sstevel 		rv = -1;
20703831d35Sstevel 		goto out;
20803831d35Sstevel 	}
20903831d35Sstevel 
21003831d35Sstevel 	/*
21103831d35Sstevel 	 * Ask the SC to bring the CPU out of reset.
21203831d35Sstevel 	 * At this point, the sb_dev_present bit is not set for the CPU.
21303831d35Sstevel 	 * From sbd point of view the CPU is not present yet.  No
21403831d35Sstevel 	 * status threads will try to read registers off the CPU.
21503831d35Sstevel 	 * Since we are already holding sb_mutex, it is not necessary
21603831d35Sstevel 	 * to grab the board mutex when checking and setting the
21703831d35Sstevel 	 * cpus_in_reset bit.
21803831d35Sstevel 	 */
21903831d35Sstevel 	if (sbdp_is_cpu_in_reset(wnode, bd, SG_CPUID_TO_CPU_UNIT(cpuid))) {
22003831d35Sstevel 
22103831d35Sstevel 		sbdp_wnode_t	*wnodep;
22203831d35Sstevel 		sbdp_bd_t	*bdp = NULL;
22303831d35Sstevel 		processorid_t	cpu = cpuid;
22403831d35Sstevel 		processorid_t	portid;
22503831d35Sstevel 
22603831d35Sstevel 		wnodep = sbdp_get_wnodep(wnode);
22703831d35Sstevel 		bdp = &wnodep->bds[bd];
22803831d35Sstevel 		ASSERT(bdp);
22903831d35Sstevel 
23003831d35Sstevel 		/*
23103831d35Sstevel 		 * If the first core is not present, the start CPU
23203831d35Sstevel 		 * interface needs to be called with the portid rather
23303831d35Sstevel 		 * than the cpuid.
23403831d35Sstevel 		 */
23503831d35Sstevel 		portid = SG_CPUID_TO_PORTID(cpuid);
23603831d35Sstevel 		if (!SBDP_IS_CPU_PRESENT(bdp, SG_CPUID_TO_CPU_UNIT(portid))) {
23703831d35Sstevel 			cpu = portid;
23803831d35Sstevel 		}
23903831d35Sstevel 
24003831d35Sstevel 		if (SBDP_INJECT_ERROR(f, 1) || sbdp_start_cpu(cpu) != 0) {
24103831d35Sstevel 
24203831d35Sstevel 			rv = -1;
24303831d35Sstevel 			goto out;
24403831d35Sstevel 		}
24503831d35Sstevel 
24603831d35Sstevel 		if (SBDP_INJECT_ERROR(f, 2) ||
24703831d35Sstevel 		    prom_serengeti_wakeupcpu(nodeid) != 0) {
24803831d35Sstevel 
24903831d35Sstevel 			rv = -1;
25003831d35Sstevel 			goto out;
25103831d35Sstevel 		}
25203831d35Sstevel 	}
25303831d35Sstevel 
25403831d35Sstevel 	/*
25503831d35Sstevel 	 * Mark the CPU out of reset.
25603831d35Sstevel 	 */
25703831d35Sstevel 	sbdp_cpu_in_reset(wnode, bd, SG_CPUID_TO_CPU_UNIT(cpuid), 0);
25803831d35Sstevel 
25903831d35Sstevel 	/*
26003831d35Sstevel 	 * Refresh the bd info
26103831d35Sstevel 	 * we need to wait until all cpus are out of reset
26203831d35Sstevel 	 */
26303831d35Sstevel 	for (i = 0; i < SG_MAX_CPUS_PER_BD; i++)
26403831d35Sstevel 		if (sbdp_is_cpu_present(wnode, bd, i) &&
26503831d35Sstevel 		    sbdp_is_cpu_in_reset(wnode, bd, i) == 1) {
26603831d35Sstevel 			break;
26703831d35Sstevel 		}
26803831d35Sstevel 
26903831d35Sstevel 	if (i == SG_MAX_CPUS_PER_BD) {
27003831d35Sstevel 		/*
27103831d35Sstevel 		 * All cpus are out of reset so it is safe to
27203831d35Sstevel 		 * update the bd info
27303831d35Sstevel 		 */
27403831d35Sstevel 		sbdp_add_new_bd_info(wnode, bd);
27503831d35Sstevel 	}
27603831d35Sstevel 
27703831d35Sstevel out:
27803831d35Sstevel 	if (rv != 0)
27903831d35Sstevel 		sbdp_set_err(sep, ESGT_WAKEUPCPU, NULL);
28003831d35Sstevel 
28103831d35Sstevel 	return (rv);
28203831d35Sstevel }
28303831d35Sstevel 
28403831d35Sstevel int
sbdp_cpu_poweron(struct cpu * cp)28503831d35Sstevel sbdp_cpu_poweron(struct cpu *cp)
28603831d35Sstevel {
28703831d35Sstevel 	int		cpuid;
28803831d35Sstevel 	int		ntries;
28903831d35Sstevel 	pnode_t		nodeid;
29003831d35Sstevel 	extern void	restart_other_cpu(int);
29103831d35Sstevel 	static fn_t	f = "sbdp_cpu_poweron";
29203831d35Sstevel 
29303831d35Sstevel 	SBDP_DBG_FUNC("%s\n", f);
29403831d35Sstevel 
29503831d35Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
29603831d35Sstevel 
29703831d35Sstevel 	ntries = sbdp_cpu_ntries;
29803831d35Sstevel 	cpuid = cp->cpu_id;
29903831d35Sstevel 
30003831d35Sstevel 	nodeid = cpunodes[cpuid].nodeid;
30103831d35Sstevel 	ASSERT(nodeid != (pnode_t)0);
30203831d35Sstevel 
30303831d35Sstevel 	/*
30403831d35Sstevel 	 * This is a safe guard in case the CPU has taken a trap
30503831d35Sstevel 	 * and idling in POST.
30603831d35Sstevel 	 */
30703831d35Sstevel 	if (SBDP_INJECT_ERROR(f, 0) ||
30803831d35Sstevel 	    prom_serengeti_wakeupcpu(nodeid) != 0) {
30903831d35Sstevel 
31003831d35Sstevel 		return (EBUSY);
31103831d35Sstevel 	}
31203831d35Sstevel 
31303831d35Sstevel 	cp->cpu_flags &= ~CPU_POWEROFF;
31403831d35Sstevel 
31503831d35Sstevel 	/*
31603831d35Sstevel 	 * NOTE: restart_other_cpu pauses cpus during the
31703831d35Sstevel 	 *	slave cpu start.  This helps to quiesce the
31803831d35Sstevel 	 *	bus traffic a bit which makes the tick sync
31903831d35Sstevel 	 *	routine in the prom more robust.
32003831d35Sstevel 	 */
32103831d35Sstevel 	SBDP_DBG_CPU("%s: COLD START for cpu (%d)\n", f, cpuid);
32203831d35Sstevel 
32303831d35Sstevel 	restart_other_cpu(cpuid);
32403831d35Sstevel 
32503831d35Sstevel 	SBDP_DBG_CPU("after restarting other cpus\n");
32603831d35Sstevel 
32703831d35Sstevel 	/*
32803831d35Sstevel 	 * Wait for the cpu to reach its idle thread before
329*48bbca81SDaniel Hoffman 	 * we zap it with a request to blow away the mappings
330*48bbca81SDaniel Hoffman 	 * it (might) have for the sbdp_shutdown_asm code
331*48bbca81SDaniel Hoffman 	 * it may have executed on unconfigure.
33203831d35Sstevel 	 */
33303831d35Sstevel 	while ((cp->cpu_thread != cp->cpu_idle_thread) && (ntries > 0)) {
33403831d35Sstevel 		DELAY(sbdp_cpu_delay);
33503831d35Sstevel 		ntries--;
33603831d35Sstevel 	}
33703831d35Sstevel 
33803831d35Sstevel 	SBDP_DBG_CPU("%s: waited %d out of %d loops for cpu %d\n",
33903831d35Sstevel 	    f, sbdp_cpu_ntries - ntries, sbdp_cpu_ntries, cpuid);
34003831d35Sstevel 
34103831d35Sstevel 	return (0);
34203831d35Sstevel }
34303831d35Sstevel 
34403831d35Sstevel 
34503831d35Sstevel #define	SBDP_CPU_SRAM_ADDR	0x7fff0900000ull
34603831d35Sstevel #define	SBDP_CPU_SRAM_SIZE	0x20000ull
34703831d35Sstevel 
34803831d35Sstevel static const char cpyren_key[] = "COPYREN";
34903831d35Sstevel 
35003831d35Sstevel static uint64_t bbsram_pa;
35103831d35Sstevel static uint_t bbsram_size;
35203831d35Sstevel 
35303831d35Sstevel typedef struct {
35403831d35Sstevel 	caddr_t		vaddr;
35503831d35Sstevel 	pgcnt_t		npages;
35603831d35Sstevel 	uint64_t	*pa;
35703831d35Sstevel 	uint_t		*size;
35803831d35Sstevel } sbdp_cpu_sram_map_t;
35903831d35Sstevel 
36003831d35Sstevel int
sbdp_cpu_poweroff(struct cpu * cp)36103831d35Sstevel sbdp_cpu_poweroff(struct cpu *cp)
36203831d35Sstevel {
36303831d35Sstevel 	processorid_t	cpuid;
36403831d35Sstevel 	pnode_t		nodeid;
36503831d35Sstevel 	sbdp_cpu_sram_map_t	map;
36603831d35Sstevel 	static fn_t	f = "sbdp_cpu_poweroff";
36703831d35Sstevel 
36803831d35Sstevel 	SBDP_DBG_FUNC("%s\n", f);
36903831d35Sstevel 
37003831d35Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
37103831d35Sstevel 
37203831d35Sstevel 	/*
37303831d35Sstevel 	 * Capture all CPUs (except for detaching proc) to prevent
37403831d35Sstevel 	 * crosscalls to the detaching proc until it has cleared its
37503831d35Sstevel 	 * bit in cpu_ready_set.
37603831d35Sstevel 	 */
37703831d35Sstevel 	cpuid = cp->cpu_id;
37803831d35Sstevel 
37903831d35Sstevel 	nodeid = cpunodes[cpuid].nodeid;
38003831d35Sstevel 	ASSERT(nodeid != (pnode_t)0);
38103831d35Sstevel 
38203831d35Sstevel 	*sbdp_valp = 0ull;
38303831d35Sstevel 	/*
38403831d35Sstevel 	 * Do the cpu sram mapping now.  This avoids problems with
38503831d35Sstevel 	 * mutexes and high PILS
38603831d35Sstevel 	 */
38703831d35Sstevel 	if (SBDP_INJECT_ERROR(f, 0) ||
38803831d35Sstevel 	    cpusram_map(&map.vaddr, &map.npages) != DDI_SUCCESS) {
38903831d35Sstevel 		return (EBUSY);
39003831d35Sstevel 	}
39103831d35Sstevel 
39203831d35Sstevel 	map.pa = &bbsram_pa;
39303831d35Sstevel 	map.size = &bbsram_size;
39403831d35Sstevel 
39503831d35Sstevel 	/*
39603831d35Sstevel 	 * Do a cross call to the cpu so it obtains the base address
39703831d35Sstevel 	 */
39803831d35Sstevel 	xc_one(cpuid, sbdp_get_cpu_sram_addr, (uint64_t)&map,
39903831d35Sstevel 	    (uint64_t)NULL);
40003831d35Sstevel 
40103831d35Sstevel 	cpusram_unmap(&map.vaddr, map.npages);
40203831d35Sstevel 
40303831d35Sstevel 	if (SBDP_INJECT_ERROR(f, 1) || bbsram_size == 0) {
40403831d35Sstevel 		cmn_err(CE_WARN, "cpu%d: Key \"%s\" missing from CPU SRAM TOC",
40503831d35Sstevel 		    cpuid, cpyren_key);
40603831d35Sstevel 		return (EBUSY);
40703831d35Sstevel 	}
40803831d35Sstevel 
40903831d35Sstevel 	if ((bbsram_pa & MMU_PAGEOFFSET) != 0) {
41003831d35Sstevel 		cmn_err(CE_WARN, "cpu%d: CPU SRAM key \"%s\" not page aligned, "
41103831d35Sstevel 		    "offset = 0x%lx", cpuid, cpyren_key,
41207d06da5SSurya Prakki 		    (bbsram_pa - (uint64_t)SBDP_CPU_SRAM_ADDR));
41303831d35Sstevel 		return (EBUSY);
41403831d35Sstevel 	}
41503831d35Sstevel 
41603831d35Sstevel 	if (bbsram_size < MMU_PAGESIZE) {
41703831d35Sstevel 		cmn_err(CE_WARN, "cpu%d: CPU SRAM key \"%s\" too small, "
41803831d35Sstevel 		    "size = 0x%x", cpuid, cpyren_key, bbsram_size);
41903831d35Sstevel 		return (EBUSY);
42003831d35Sstevel 	}
42103831d35Sstevel 
42203831d35Sstevel 	/*
42303831d35Sstevel 	 * Capture all CPUs (except for detaching proc) to prevent
42403831d35Sstevel 	 * crosscalls to the detaching proc until it has cleared its
42503831d35Sstevel 	 * bit in cpu_ready_set.
42603831d35Sstevel 	 *
42703831d35Sstevel 	 * The CPU's remain paused and the prom_mutex is known to be free.
42803831d35Sstevel 	 * This prevents the x-trap victim from blocking when doing prom
42903831d35Sstevel 	 * IEEE-1275 calls at a high PIL level.
43003831d35Sstevel 	 */
43103831d35Sstevel 
43203831d35Sstevel 	promsafe_pause_cpus();
43303831d35Sstevel 
43403831d35Sstevel 	/*
43503831d35Sstevel 	 * Quiesce interrupts on the target CPU. We do this by setting
43603831d35Sstevel 	 * the CPU 'not ready'- (i.e. removing the CPU from cpu_ready_set) to
43703831d35Sstevel 	 * prevent it from receiving cross calls and cross traps.
43803831d35Sstevel 	 * This prevents the processor from receiving any new soft interrupts.
43903831d35Sstevel 	 */
44003831d35Sstevel 
44103831d35Sstevel 	mp_cpu_quiesce(cp);
44203831d35Sstevel 
44303831d35Sstevel 	/* tell the prom the cpu is going away */
44403831d35Sstevel 	if (SBDP_INJECT_ERROR(f, 2) || prom_serengeti_cpu_off(nodeid) != 0)
44503831d35Sstevel 		return (EBUSY);
44603831d35Sstevel 
44703831d35Sstevel 	/*
44803831d35Sstevel 	 * An sir instruction is issued at the end of the shutdown
44903831d35Sstevel 	 * routine to make the CPU go through POST and re-enter OBP.
45003831d35Sstevel 	 */
45103831d35Sstevel 	xt_one_unchecked(cp->cpu_id, (xcfunc_t *)idle_stop_xcall,
45203831d35Sstevel 	    (uint64_t)sbdp_cpu_shutdown_self, 0);
45303831d35Sstevel 
45403831d35Sstevel 	*sbdp_valp = 3ull;
45503831d35Sstevel 
45603831d35Sstevel 	start_cpus();
45703831d35Sstevel 
45803831d35Sstevel 	/*
45903831d35Sstevel 	 * Wait until we reach the OBP idle loop or time out.
46003831d35Sstevel 	 * prom_serengeti_wakeupcpu waits for up to 60 seconds for the
46103831d35Sstevel 	 * CPU to reach OBP idle loop.
46203831d35Sstevel 	 */
46303831d35Sstevel 	if (SBDP_INJECT_ERROR(f, 3) ||
46403831d35Sstevel 	    prom_serengeti_wakeupcpu(nodeid) != 0) {
46503831d35Sstevel 
46603831d35Sstevel 		/*
46703831d35Sstevel 		 * If it fails here, we still consider the unconfigure
46803831d35Sstevel 		 * operation as successful.
46903831d35Sstevel 		 */
47003831d35Sstevel 		cmn_err(CE_WARN, "cpu%d: CPU failed to enter OBP idle loop.\n",
47103831d35Sstevel 		    cpuid);
47203831d35Sstevel 	}
47303831d35Sstevel 
47403831d35Sstevel 	ASSERT(!(CPU_IN_SET(cpu_ready_set, cpuid)));
47503831d35Sstevel 
47603831d35Sstevel 	bbsram_pa = 0;
47703831d35Sstevel 	bbsram_size = 0;
47803831d35Sstevel 
47903831d35Sstevel 	return (0);
48003831d35Sstevel }
48103831d35Sstevel 
48203831d35Sstevel processorid_t
sbdp_get_cpuid(sbdp_handle_t * hp,dev_info_t * dip)48303831d35Sstevel sbdp_get_cpuid(sbdp_handle_t *hp, dev_info_t *dip)
48403831d35Sstevel {
48503831d35Sstevel 	int		cpuid;
48603831d35Sstevel 	char		type[OBP_MAXPROPNAME];
48703831d35Sstevel 	pnode_t		nodeid;
48803831d35Sstevel 	sbd_error_t	*sep;
48903831d35Sstevel 	static fn_t	f = "sbdp_get_cpuid";
49003831d35Sstevel 
49103831d35Sstevel 	SBDP_DBG_FUNC("%s\n", f);
49203831d35Sstevel 
49303831d35Sstevel 	nodeid = ddi_get_nodeid(dip);
49403831d35Sstevel 	if (sbdp_is_node_bad(nodeid))
49503831d35Sstevel 		return (-1);
49603831d35Sstevel 
49703831d35Sstevel 	sep = hp->h_err;
49803831d35Sstevel 
49903831d35Sstevel 	if (prom_getproplen(nodeid, "device_type") < OBP_MAXPROPNAME)
50003831d35Sstevel 		(void) prom_getprop(nodeid, "device_type", (caddr_t)type);
50103831d35Sstevel 	else {
50203831d35Sstevel 		sbdp_set_err(sep, ESGT_NO_DEV_TYPE, NULL);
50303831d35Sstevel 		return (-1);
50403831d35Sstevel 	}
50503831d35Sstevel 
50603831d35Sstevel 	if (strcmp(type, "cpu") != 0) {
50703831d35Sstevel 		sbdp_set_err(sep, ESGT_NOT_CPUTYPE, NULL);
50803831d35Sstevel 		return (-1);
50903831d35Sstevel 	}
51003831d35Sstevel 
51103831d35Sstevel 	/*
51203831d35Sstevel 	 * Check to see if property "cpuid" exists first.
51303831d35Sstevel 	 * If not, check for "portid".
51403831d35Sstevel 	 */
51503831d35Sstevel 	if (prom_getprop(nodeid, "cpuid", (caddr_t)&cpuid) == -1)
51603831d35Sstevel 		if (prom_getprop(nodeid, "portid", (caddr_t)&cpuid) == -1) {
51703831d35Sstevel 
51803831d35Sstevel 			return (-1);
51903831d35Sstevel 	}
52003831d35Sstevel 
52103831d35Sstevel 	return ((processorid_t)cpuid & SG_CPU_ID_MASK);
52203831d35Sstevel }
52303831d35Sstevel 
52403831d35Sstevel int
sbdp_cpu_get_impl(sbdp_handle_t * hp,dev_info_t * dip)52503831d35Sstevel sbdp_cpu_get_impl(sbdp_handle_t *hp, dev_info_t *dip)
52603831d35Sstevel {
52703831d35Sstevel 	int		impl;
52803831d35Sstevel 	char		type[OBP_MAXPROPNAME];
52903831d35Sstevel 	pnode_t		nodeid;
53003831d35Sstevel 	sbd_error_t	*sep;
53103831d35Sstevel 	static fn_t	f = "sbdp_cpu_get_impl";
53203831d35Sstevel 
53303831d35Sstevel 	SBDP_DBG_FUNC("%s\n", f);
53403831d35Sstevel 
53503831d35Sstevel 	nodeid = ddi_get_nodeid(dip);
53603831d35Sstevel 	if (sbdp_is_node_bad(nodeid))
53703831d35Sstevel 		return (-1);
53803831d35Sstevel 
53903831d35Sstevel 	sep = hp->h_err;
54003831d35Sstevel 
54103831d35Sstevel 	if (prom_getproplen(nodeid, "device_type") < OBP_MAXPROPNAME)
54203831d35Sstevel 		(void) prom_getprop(nodeid, "device_type", (caddr_t)type);
54303831d35Sstevel 	else {
54403831d35Sstevel 		sbdp_set_err(sep, ESGT_NO_DEV_TYPE, NULL);
54503831d35Sstevel 		return (-1);
54603831d35Sstevel 	}
54703831d35Sstevel 
54803831d35Sstevel 	if (strcmp(type, "cpu") != 0) {
54903831d35Sstevel 		sbdp_set_err(sep, ESGT_NOT_CPUTYPE, NULL);
55003831d35Sstevel 		return (-1);
55103831d35Sstevel 	}
55203831d35Sstevel 
55303831d35Sstevel 	/*
55403831d35Sstevel 	 * Get the implementation# property.
55503831d35Sstevel 	 */
55603831d35Sstevel 	if (prom_getprop(nodeid, "implementation#", (caddr_t)&impl) == -1)
55703831d35Sstevel 		return (-1);
55803831d35Sstevel 
55903831d35Sstevel 	return (impl);
56003831d35Sstevel }
56103831d35Sstevel 
56203831d35Sstevel struct sbdp_prom_get_node_args {
56303831d35Sstevel 	pnode_t node;		/* current node */
56403831d35Sstevel 	processorid_t portid;	/* portid we are looking for */
56503831d35Sstevel 	pnode_t result_node;	/* node found with the above portid */
56603831d35Sstevel };
56703831d35Sstevel 
56803831d35Sstevel pnode_t
sbdp_find_nearby_cpu_by_portid(pnode_t nodeid,processorid_t portid)56903831d35Sstevel sbdp_find_nearby_cpu_by_portid(pnode_t nodeid, processorid_t portid)
57003831d35Sstevel {
57103831d35Sstevel 	struct sbdp_prom_get_node_args arg;
57203831d35Sstevel 	static fn_t	f = "sbdp_find_nearby_cpu_by_portid";
57303831d35Sstevel 
57403831d35Sstevel 	SBDP_DBG_FUNC("%s\n", f);
57503831d35Sstevel 
57603831d35Sstevel 	arg.node = nodeid;
57703831d35Sstevel 	arg.portid = portid;
57803831d35Sstevel 	(void) prom_tree_access(sbdp_prom_get_cpu, &arg, NULL);
57903831d35Sstevel 
58003831d35Sstevel 	return (arg.result_node);
58103831d35Sstevel }
58203831d35Sstevel 
58303831d35Sstevel /*ARGSUSED*/
58403831d35Sstevel static int
sbdp_prom_get_cpu(void * arg,int changed)58503831d35Sstevel sbdp_prom_get_cpu(void *arg, int changed)
58603831d35Sstevel {
58703831d35Sstevel 	int	portid;
58803831d35Sstevel 	pnode_t	parent, cur_node;
58903831d35Sstevel 	struct sbdp_prom_get_node_args *argp = arg;
59003831d35Sstevel 	static fn_t	f = "sbdp_prom_get_cpu";
59103831d35Sstevel 
59203831d35Sstevel 	SBDP_DBG_FUNC("%s\n", f);
59303831d35Sstevel 
59403831d35Sstevel 	parent = prom_parentnode(argp->node);
59503831d35Sstevel 
59603831d35Sstevel 	for (cur_node = prom_childnode(parent); cur_node != OBP_NONODE;
59703831d35Sstevel 	    cur_node = prom_nextnode(cur_node)) {
59803831d35Sstevel 
59903831d35Sstevel 		if (prom_getprop(cur_node, OBP_PORTID, (caddr_t)&portid) < 0)
60003831d35Sstevel 			continue;
60103831d35Sstevel 
60203831d35Sstevel 		if ((portid == argp->portid) && (cur_node != argp->node))
60303831d35Sstevel 			break;
60403831d35Sstevel 	}
60503831d35Sstevel 
60603831d35Sstevel 	argp->result_node = cur_node;
60703831d35Sstevel 
60803831d35Sstevel 	return (0);
60903831d35Sstevel }
61003831d35Sstevel 
61103831d35Sstevel 
61203831d35Sstevel /*
61303831d35Sstevel  * A detaching CPU is xcalled with an xtrap to sbdp_cpu_stop_self() after
61403831d35Sstevel  * it has been offlined. The function of this routine is to get the cpu
61503831d35Sstevel  * spinning in a safe place. The requirement is that the system will not
61603831d35Sstevel  * reference anything on the detaching board (memory and i/o is detached
61703831d35Sstevel  * elsewhere) and that the CPU not reference anything on any other board
61803831d35Sstevel  * in the system.  This isolation is required during and after the writes
61903831d35Sstevel  * to the domain masks to remove the board from the domain.
62003831d35Sstevel  *
62103831d35Sstevel  * To accomplish this isolation the following is done:
62203831d35Sstevel  *	0) Map the CPUSRAM to obtain the correct address in SRAM
62303831d35Sstevel  *      1) Create a locked mapping to a location in CPU SRAM where
62403831d35Sstevel  *      the cpu will execute.
62503831d35Sstevel  *      2) Copy the target function (sbdp_shutdown_asm) in which
62603831d35Sstevel  *      the cpu will execute into CPU SRAM.
62703831d35Sstevel  *      3) Jump into function with CPU SRAM.
62803831d35Sstevel  *      Function will:
62903831d35Sstevel  *      3.1) Flush its Ecache (displacement).
63003831d35Sstevel  *      3.2) Flush its Dcache with HW mechanism.
63103831d35Sstevel  *      3.3) Flush its Icache with HW mechanism.
63203831d35Sstevel  *      3.4) Flush all valid and _unlocked_ D-TLB entries.
63303831d35Sstevel  *      3.5) Flush all valid and _unlocked_ I-TLB entries.
63403831d35Sstevel  *      4) Jump into a tight loop.
63503831d35Sstevel  */
63603831d35Sstevel 
63703831d35Sstevel static void
sbdp_cpu_stop_self(uint64_t pa)63803831d35Sstevel sbdp_cpu_stop_self(uint64_t pa)
63903831d35Sstevel {
64003831d35Sstevel 	cpu_t		*cp = CPU;
64103831d35Sstevel 	int		cpuid = cp->cpu_id;
64203831d35Sstevel 	tte_t		tte;
64303831d35Sstevel 	volatile uint_t	*src, *dst;
644e0731422SRichard Lowe 	size_t		funclen;
64503831d35Sstevel 	sbdp_shutdown_t	sht;
64603831d35Sstevel 	uint_t		bbsram_pfn;
64703831d35Sstevel 	uint64_t	bbsram_addr;
64803831d35Sstevel 	void		(*bbsram_func)(sbdp_shutdown_t *);
64903831d35Sstevel 	extern void	sbdp_shutdown_asm(sbdp_shutdown_t *);
65003831d35Sstevel 	extern void	sbdp_shutdown_asm_end(void);
65103831d35Sstevel 
652e0731422SRichard Lowe 	funclen = (uintptr_t)sbdp_shutdown_asm_end -
653e0731422SRichard Lowe 	    (uintptr_t)sbdp_shutdown_asm;
65403831d35Sstevel 	ASSERT(funclen <= MMU_PAGESIZE);
65503831d35Sstevel 	ASSERT(bbsram_pa != 0);
65603831d35Sstevel 	ASSERT((bbsram_pa & MMU_PAGEOFFSET) == 0);
65703831d35Sstevel 	ASSERT(bbsram_size >= MMU_PAGESIZE);
65803831d35Sstevel 
65903831d35Sstevel 	stdphys(pa, 3);
66003831d35Sstevel 	bbsram_pfn = (uint_t)(bbsram_pa >> MMU_PAGESHIFT);
66103831d35Sstevel 
66203831d35Sstevel 	bbsram_addr = (uint64_t)sbdp_shutdown_va;
66303831d35Sstevel 	sht.estack = bbsram_addr + MMU_PAGESIZE;
66403831d35Sstevel 	sht.flushaddr = ecache_flushaddr;
66503831d35Sstevel 
66603831d35Sstevel 	tte.tte_inthi = TTE_VALID_INT | TTE_SZ_INT(TTE8K) |
66703831d35Sstevel 	    TTE_PFN_INTHI(bbsram_pfn);
66803831d35Sstevel 	tte.tte_intlo = TTE_PFN_INTLO(bbsram_pfn) |
66903831d35Sstevel 	    TTE_HWWR_INT | TTE_PRIV_INT | TTE_LCK_INT;
6701e2e7a75Shuah 	sfmmu_dtlb_ld_kva(sbdp_shutdown_va, &tte); /* load dtlb */
6711e2e7a75Shuah 	sfmmu_itlb_ld_kva(sbdp_shutdown_va, &tte); /* load itlb */
67203831d35Sstevel 
67303831d35Sstevel 	for (src = (uint_t *)sbdp_shutdown_asm, dst = (uint_t *)bbsram_addr;
67403831d35Sstevel 	    src < (uint_t *)sbdp_shutdown_asm_end; src++, dst++)
67503831d35Sstevel 	*dst = *src;
67603831d35Sstevel 
67703831d35Sstevel 	bbsram_func = (void (*)())bbsram_addr;
67803831d35Sstevel 	sht.size = (uint32_t)cpunodes[cpuid].ecache_size << 1;
67903831d35Sstevel 	sht.linesize = (uint32_t)cpunodes[cpuid].ecache_linesize;
68003831d35Sstevel 	sht.physaddr = pa;
68103831d35Sstevel 
68203831d35Sstevel 	/*
68303831d35Sstevel 	 * Signal to sbdp_cpu_poweroff() that we're just
68403831d35Sstevel 	 * about done.
68503831d35Sstevel 	 */
68603831d35Sstevel 	cp->cpu_m.in_prom = 1;
68703831d35Sstevel 
68803831d35Sstevel 	stdphys(pa, 4);
68903831d35Sstevel 	(*bbsram_func)(&sht);
69003831d35Sstevel }
69103831d35Sstevel 
69203831d35Sstevel /* ARGSUSED */
69303831d35Sstevel void
sbdp_get_cpu_sram_addr(uint64_t arg1,uint64_t arg2)69403831d35Sstevel sbdp_get_cpu_sram_addr(uint64_t arg1, uint64_t arg2)
69503831d35Sstevel {
69603831d35Sstevel 	uint64_t	*pap;
69703831d35Sstevel 	uint_t		*sizep;
69803831d35Sstevel 	struct iosram_toc *tocp;
69903831d35Sstevel 	uint_t		offset;
70003831d35Sstevel 	uint_t		size;
70103831d35Sstevel 	sbdp_cpu_sram_map_t *map;
70203831d35Sstevel 	int		i;
70303831d35Sstevel 	fn_t		f = "sbdp_get_cpu_sram_addr";
70403831d35Sstevel 
70503831d35Sstevel 	SBDP_DBG_FUNC("%s\n", f);
70603831d35Sstevel 
70703831d35Sstevel 	map = (sbdp_cpu_sram_map_t *)arg1;
70803831d35Sstevel 	tocp = (struct iosram_toc *)map->vaddr;
70903831d35Sstevel 	pap = map->pa;
71003831d35Sstevel 	sizep = map->size;
71103831d35Sstevel 
71203831d35Sstevel 	for (i = 0; i < tocp->iosram_tagno; i++) {
71303831d35Sstevel 		if (strcmp(tocp->iosram_keys[i].key, cpyren_key) == 0)
71403831d35Sstevel 			break;
71503831d35Sstevel 	}
71603831d35Sstevel 	if (i == tocp->iosram_tagno) {
71703831d35Sstevel 		*pap = 0;
71803831d35Sstevel 		*sizep = 0;
71903831d35Sstevel 		return;
72003831d35Sstevel 	}
72103831d35Sstevel 	offset = tocp->iosram_keys[i].offset;
72203831d35Sstevel 	size = tocp->iosram_keys[i].size;
72303831d35Sstevel 
72403831d35Sstevel 	/*
72503831d35Sstevel 	 * The address we want is the begining of cpusram + offset
72603831d35Sstevel 	 */
72703831d35Sstevel 	*pap = SBDP_CPU_SRAM_ADDR + offset;
72803831d35Sstevel 
72903831d35Sstevel 	*sizep = size;
73003831d35Sstevel }
73103831d35Sstevel 
73203831d35Sstevel static int
cpusram_map(caddr_t * vaddrp,pgcnt_t * npp)73303831d35Sstevel cpusram_map(caddr_t *vaddrp, pgcnt_t *npp)
73403831d35Sstevel {
73503831d35Sstevel 	uint_t		pgoffset;
73603831d35Sstevel 	pgcnt_t		npages;
73703831d35Sstevel 	pfn_t		pfn;
73803831d35Sstevel 	uint64_t	base;
73903831d35Sstevel 	caddr_t		kaddr;
74003831d35Sstevel 	uint_t		mapping_attr;
74103831d35Sstevel 
74203831d35Sstevel 	base = (uint64_t)SBDP_CPU_SRAM_ADDR & (~MMU_PAGEOFFSET);
74303831d35Sstevel 	pfn = mmu_btop(base);
74403831d35Sstevel 
74503831d35Sstevel 	/*
74603831d35Sstevel 	 * Do a quick sanity check to make sure we are in I/O space.
74703831d35Sstevel 	 */
74803831d35Sstevel 	if (pf_is_memory(pfn))
74903831d35Sstevel 		return (DDI_FAILURE);
75003831d35Sstevel 
75103831d35Sstevel 	pgoffset = (ulong_t)SBDP_CPU_SRAM_ADDR & MMU_PAGEOFFSET;
75203831d35Sstevel 	npages = mmu_btopr(SBDP_CPU_SRAM_SIZE + pgoffset);
75303831d35Sstevel 
75403831d35Sstevel 	kaddr = vmem_alloc(heap_arena, ptob(npages), VM_NOSLEEP);
75503831d35Sstevel 	if (kaddr == NULL)
75603831d35Sstevel 		return (DDI_ME_NORESOURCES);
75703831d35Sstevel 
75803831d35Sstevel 	mapping_attr = PROT_READ;
75903831d35Sstevel 	/*
76003831d35Sstevel 	 * Now map in the pages we've allocated...
76103831d35Sstevel 	 */
76203831d35Sstevel 	hat_devload(kas.a_hat, kaddr, ptob(npages), pfn, mapping_attr,
76303831d35Sstevel 	    HAT_LOAD_LOCK);
76403831d35Sstevel 
76503831d35Sstevel 	*vaddrp = kaddr + pgoffset;
76603831d35Sstevel 	*npp = npages;
76703831d35Sstevel 
76803831d35Sstevel 	return (DDI_SUCCESS);
76903831d35Sstevel }
77003831d35Sstevel 
77103831d35Sstevel static void
cpusram_unmap(caddr_t * vaddrp,pgcnt_t npages)77203831d35Sstevel cpusram_unmap(caddr_t *vaddrp, pgcnt_t npages)
77303831d35Sstevel {
77403831d35Sstevel 	uint_t  pgoffset;
77503831d35Sstevel 	caddr_t base;
77603831d35Sstevel 	caddr_t addr = *vaddrp;
77703831d35Sstevel 
77803831d35Sstevel 
77903831d35Sstevel 	pgoffset = (ulong_t)SBDP_CPU_SRAM_ADDR & MMU_PAGEOFFSET;
78003831d35Sstevel 	base = addr - pgoffset;
78103831d35Sstevel 	hat_unload(kas.a_hat, base, ptob(npages), HAT_UNLOAD_UNLOCK);
78203831d35Sstevel 	vmem_free(heap_arena, base, ptob(npages));
78303831d35Sstevel 
78403831d35Sstevel 	*vaddrp = 0;
78503831d35Sstevel }
78603831d35Sstevel 
78703831d35Sstevel 
78803831d35Sstevel static void
sbdp_cpu_shutdown_self(void)78903831d35Sstevel sbdp_cpu_shutdown_self(void)
79003831d35Sstevel {
79103831d35Sstevel 	cpu_t		*cp = CPU;
79203831d35Sstevel 	int		cpuid = cp->cpu_id;
79303831d35Sstevel 	extern void	flush_windows(void);
79403831d35Sstevel 	uint64_t	pa = va_to_pa((void *)sbdp_valp);
79503831d35Sstevel 
79603831d35Sstevel 	stdphys(pa, 8);
79703831d35Sstevel 	flush_windows();
79803831d35Sstevel 
79903831d35Sstevel 	(void) spl8();
80003831d35Sstevel 
80103831d35Sstevel 	stdphys(pa, 6);
80203831d35Sstevel 
80303831d35Sstevel 	ASSERT(cp->cpu_intr_actv == 0);
80403831d35Sstevel 	ASSERT(cp->cpu_thread == cp->cpu_idle_thread ||
80503831d35Sstevel 	    cp->cpu_thread == cp->cpu_startup_thread);
80603831d35Sstevel 
80703831d35Sstevel 	cp->cpu_flags = CPU_OFFLINE | CPU_QUIESCED | CPU_POWEROFF;
80803831d35Sstevel 
80903831d35Sstevel 	CPU_SIGNATURE(OS_SIG, SIGST_DETACHED, SIGSUBST_NULL, cpuid);
81003831d35Sstevel 
81103831d35Sstevel 	stdphys(pa, 7);
81203831d35Sstevel 	sbdp_cpu_stop_self(pa);
81303831d35Sstevel 
81403831d35Sstevel 	cmn_err(CE_PANIC, "sbdp_cpu_shutdown_self: CPU %d FAILED TO SHUTDOWN",
81503831d35Sstevel 	    cpuid);
81603831d35Sstevel }
81703831d35Sstevel 
81803831d35Sstevel typedef struct {
81903831d35Sstevel 	int	node;
82003831d35Sstevel 	int	board;
82103831d35Sstevel 	int 	non_panther_cpus;
82203831d35Sstevel } sbdp_node_walk_t;
82303831d35Sstevel 
82403831d35Sstevel static int
sbdp_find_non_panther_cpus(dev_info_t * dip,void * node_args)82503831d35Sstevel sbdp_find_non_panther_cpus(dev_info_t *dip, void *node_args)
82603831d35Sstevel {
82703831d35Sstevel 	int	impl, cpuid, portid;
82803831d35Sstevel 	int	buflen;
82903831d35Sstevel 	char	buf[OBP_MAXPROPNAME];
83003831d35Sstevel 	sbdp_node_walk_t *args = (sbdp_node_walk_t *)node_args;
83103831d35Sstevel 
83203831d35Sstevel 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
83303831d35Sstevel 	    DDI_PROP_DONTPASS, OBP_DEVICETYPE, (caddr_t)buf,
83403831d35Sstevel 	    &buflen) != DDI_PROP_SUCCESS) {
83503831d35Sstevel 		return (DDI_WALK_CONTINUE);
83603831d35Sstevel 	}
83703831d35Sstevel 
83803831d35Sstevel 	if (strcmp(buf, "cpu") != 0) {
83903831d35Sstevel 		return (DDI_WALK_CONTINUE);
84003831d35Sstevel 	}
84103831d35Sstevel 
84203831d35Sstevel 	if ((impl = ddi_getprop(DDI_DEV_T_ANY, dip,
84303831d35Sstevel 	    DDI_PROP_DONTPASS, "implementation#", -1)) == -1) {
84403831d35Sstevel 		return (DDI_WALK_CONTINUE);
84503831d35Sstevel 	}
84603831d35Sstevel 
84703831d35Sstevel 	if ((cpuid = ddi_getprop(DDI_DEV_T_ANY, dip,
84803831d35Sstevel 	    DDI_PROP_DONTPASS, "cpuid", -1)) == -1) {
84903831d35Sstevel 		return (DDI_WALK_CONTINUE);
85003831d35Sstevel 	}
85103831d35Sstevel 
85203831d35Sstevel 	portid = SG_CPUID_TO_PORTID(cpuid);
85303831d35Sstevel 
85403831d35Sstevel 	/* filter out nodes not on this board */
85503831d35Sstevel 	if (SG_PORTID_TO_BOARD_NUM(portid) != args->board ||
85603831d35Sstevel 	    SG_PORTID_TO_NODEID(portid) != args->node) {
85703831d35Sstevel 		return (DDI_WALK_PRUNECHILD);
85803831d35Sstevel 	}
85903831d35Sstevel 
86003831d35Sstevel 	switch (impl) {
86103831d35Sstevel 	case CHEETAH_IMPL:
86203831d35Sstevel 	case CHEETAH_PLUS_IMPL:
86303831d35Sstevel 	case JAGUAR_IMPL:
86403831d35Sstevel 		args->non_panther_cpus++;
86503831d35Sstevel 		break;
86603831d35Sstevel 	case PANTHER_IMPL:
86703831d35Sstevel 		break;
86803831d35Sstevel 	default:
86903831d35Sstevel 		ASSERT(0);
87003831d35Sstevel 		args->non_panther_cpus++;
87103831d35Sstevel 		break;
87203831d35Sstevel 	}
87303831d35Sstevel 
87403831d35Sstevel 	SBDP_DBG_CPU("cpuid=0x%x, portid=0x%x, impl=0x%x, device_type=%s",
87503831d35Sstevel 	    cpuid, portid, impl, buf);
87603831d35Sstevel 
87703831d35Sstevel 	return (DDI_WALK_CONTINUE);
87803831d35Sstevel }
87903831d35Sstevel 
88003831d35Sstevel int
sbdp_board_non_panther_cpus(int node,int board)88103831d35Sstevel sbdp_board_non_panther_cpus(int node, int board)
88203831d35Sstevel {
88303831d35Sstevel 	sbdp_node_walk_t arg = {0};
88403831d35Sstevel 
88503831d35Sstevel 	arg.node = node;
88603831d35Sstevel 	arg.board = board;
88703831d35Sstevel 
88803831d35Sstevel 	/*
88903831d35Sstevel 	 * Root node doesn't have to be held.
89003831d35Sstevel 	 */
89103831d35Sstevel 	ddi_walk_devs(ddi_root_node(), sbdp_find_non_panther_cpus,
89203831d35Sstevel 	    (void *)&arg);
89303831d35Sstevel 
89403831d35Sstevel 	return (arg.non_panther_cpus);
89503831d35Sstevel }
896