sbdp_cpu.c revision 03831d35f7499c87d51205817c93e9a8d42c4bae
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* CPU management for serengeti DR
*
* There are three states a CPU can be in:
*
* disconnected: In reset
* connect,unconfigured: Idling in OBP's idle loop
* configured: Running Solaris
*
* State transitions:
*
* connect configure
* ------------> ------------>
* disconnected connected configured
* unconfigured
* <----------- <-------------
* disconnect unconfigure
*
* Firmware involvements
*
* start_cpu(SC)
* prom_serengeti_wakeupcpu(OBP)
* ------------> ------------------------->
* disconnected connected configured
* unconfigured
* <----------- <-------------------------
* prom_serengeti_cpu_off(OBP) prom_serengeti_cpu_off(OBP)
* stop_cpu(SC) prom_serengeti_wakeupcpu(OBP)
*
* SIR (Software Initiated Reset) is used to unconfigure a CPU.
* After the CPU has completed flushing the caches, it issues an
* sir instruction to put itself through POST. POST detects that
* it is an SIR, and re-enters OBP as a slave. When the operation
* completes successfully, the CPU will be idling in OBP.
*/
#include <sys/obpdefs.h>
#include <sys/types.h>
#include <sys/cmn_err.h>
#include <sys/cpuvar.h>
#include <sys/membar.h>
#include <sys/x_call.h>
#include <sys/machsystm.h>
#include <sys/cpu_sgnblk_defs.h>
#include <sys/pte.h>
#include <vm/hat_sfmmu.h>
#include <sys/promif.h>
#include <sys/note.h>
#include <sys/vmsystm.h>
#include <vm/seg_kmem.h>
#include <sys/sbd_ioctl.h>
#include <sys/sbd.h>
#include <sys/sbdp_priv.h>
#include <sys/sbdp_mem.h>
#include <sys/sbdp_error.h>
#include <sys/sgsbbc_iosram.h>
#include <sys/prom_plat.h>
#include <sys/cheetahregs.h>
uint64_t *sbdp_valp;
extern uint64_t va_to_pa(void *);
static int sbdp_cpu_ntries = 50000;
static int sbdp_cpu_delay = 100;
void sbdp_get_cpu_sram_addr(uint64_t, uint64_t);
static int cpusram_map(caddr_t *, pgcnt_t *);
static void cpusram_unmap(caddr_t *, pgcnt_t);
extern int prom_serengeti_wakeupcpu(pnode_t);
extern int prom_serengeti_cpu_off(pnode_t);
extern sbdp_wnode_t *sbdp_get_wnodep(int);
extern caddr_t sbdp_shutdown_va;
static int sbdp_prom_get_cpu(void *arg, int changed);
int
sbdp_disconnect_cpu(sbdp_handle_t *hp, dev_info_t *dip, processorid_t cpuid)
{
pnode_t nodeid;
int bd, wnode;
sbdp_wnode_t *wnodep;
sbdp_bd_t *bdp = NULL;
int rv = 0;
processorid_t cpu = cpuid;
processorid_t portid;
static fn_t f = "sbdp_disconnect_cpu";
SBDP_DBG_FUNC("%s\n", f);
nodeid = ddi_get_nodeid(dip);
/*
* Get board number and node number
* The check for determining if nodeid is valid is done inside
* sbdp_get_bd_and_wnode_num.
*/
if (SBDP_INJECT_ERROR(f, 0) ||
sbdp_get_bd_and_wnode_num(nodeid, &bd, &wnode) != 0) {
rv = -1;
goto out;
}
/*
* Grab the lock to prevent status threads from accessing
* registers on the CPU when it is being put into reset.
*/
wnodep = sbdp_get_wnodep(wnode);
bdp = &wnodep->bds[bd];
ASSERT(bdp);
mutex_enter(&bdp->bd_mutex);
/*
* Mark the CPU in reset. This should be done before calling
* the SC because we won't know at which stage it failed if
* the SC call returns failure.
*/
sbdp_cpu_in_reset(wnode, bd, SG_CPUID_TO_CPU_UNIT(cpuid), 1);
/*
* Ask OBP to mark the CPU as in POST
*/
if (SBDP_INJECT_ERROR(f, 1) || prom_serengeti_cpu_off(nodeid) != 0) {
rv = -1;
goto out;
}
/*
* Ask the SC to put the CPU into reset. If the first
* core is not present, the stop CPU interface needs
* to be called with the portid rather than the cpuid.
*/
portid = SG_CPUID_TO_PORTID(cpuid);
if (!SBDP_IS_CPU_PRESENT(bdp, SG_CPUID_TO_CPU_UNIT(portid))) {
cpu = portid;
}
if (SBDP_INJECT_ERROR(f, 2) || sbdp_stop_cpu(cpu) != 0) {
rv = -1;
goto out;
}
out:
if (bdp != NULL) {
mutex_exit(&bdp->bd_mutex);
}
if (rv != 0) {
sbdp_set_err(hp->h_err, ESGT_STOPCPU, NULL);
}
return (rv);
}
int
sbdp_connect_cpu(sbdp_handle_t *hp, dev_info_t *dip, processorid_t cpuid)
{
pnode_t nodeid;
sbd_error_t *sep;
int i;
int bd, wnode;
int rv = 0;
static fn_t f = "sbdp_connect_cpu";
SBDP_DBG_FUNC("%s\n", f);
sep = hp->h_err;
nodeid = ddi_get_nodeid(dip);
/*
* The check for determining if nodeid is valid is done inside
* sbdp_get_bd_and_wnode_num.
*/
if (SBDP_INJECT_ERROR(f, 0) ||
sbdp_get_bd_and_wnode_num(nodeid, &bd, &wnode) != 0) {
rv = -1;
goto out;
}
/*
* Ask the SC to bring the CPU out of reset.
* At this point, the sb_dev_present bit is not set for the CPU.
* From sbd point of view the CPU is not present yet. No
* status threads will try to read registers off the CPU.
* Since we are already holding sb_mutex, it is not necessary
* to grab the board mutex when checking and setting the
* cpus_in_reset bit.
*/
if (sbdp_is_cpu_in_reset(wnode, bd, SG_CPUID_TO_CPU_UNIT(cpuid))) {
sbdp_wnode_t *wnodep;
sbdp_bd_t *bdp = NULL;
processorid_t cpu = cpuid;
processorid_t portid;
wnodep = sbdp_get_wnodep(wnode);
bdp = &wnodep->bds[bd];
ASSERT(bdp);
/*
* If the first core is not present, the start CPU
* interface needs to be called with the portid rather
* than the cpuid.
*/
portid = SG_CPUID_TO_PORTID(cpuid);
if (!SBDP_IS_CPU_PRESENT(bdp, SG_CPUID_TO_CPU_UNIT(portid))) {
cpu = portid;
}
if (SBDP_INJECT_ERROR(f, 1) || sbdp_start_cpu(cpu) != 0) {
rv = -1;
goto out;
}
if (SBDP_INJECT_ERROR(f, 2) ||
prom_serengeti_wakeupcpu(nodeid) != 0) {
rv = -1;
goto out;
}
}
/*
* Mark the CPU out of reset.
*/
sbdp_cpu_in_reset(wnode, bd, SG_CPUID_TO_CPU_UNIT(cpuid), 0);
/*
* Refresh the bd info
* we need to wait until all cpus are out of reset
*/
for (i = 0; i < SG_MAX_CPUS_PER_BD; i++)
if (sbdp_is_cpu_present(wnode, bd, i) &&
sbdp_is_cpu_in_reset(wnode, bd, i) == 1) {
break;
}
if (i == SG_MAX_CPUS_PER_BD) {
/*
* All cpus are out of reset so it is safe to
* update the bd info
*/
sbdp_add_new_bd_info(wnode, bd);
}
out:
if (rv != 0)
sbdp_set_err(sep, ESGT_WAKEUPCPU, NULL);
return (rv);
}
int
sbdp_cpu_poweron(struct cpu *cp)
{
int cpuid;
int ntries;
pnode_t nodeid;
extern void restart_other_cpu(int);
static fn_t f = "sbdp_cpu_poweron";
SBDP_DBG_FUNC("%s\n", f);
ASSERT(MUTEX_HELD(&cpu_lock));
ntries = sbdp_cpu_ntries;
cpuid = cp->cpu_id;
nodeid = cpunodes[cpuid].nodeid;
ASSERT(nodeid != (pnode_t)0);
/*
* This is a safe guard in case the CPU has taken a trap
* and idling in POST.
*/
if (SBDP_INJECT_ERROR(f, 0) ||
prom_serengeti_wakeupcpu(nodeid) != 0) {
return (EBUSY);
}
cp->cpu_flags &= ~CPU_POWEROFF;
/*
* NOTE: restart_other_cpu pauses cpus during the
* slave cpu start. This helps to quiesce the
* bus traffic a bit which makes the tick sync
* routine in the prom more robust.
*/
SBDP_DBG_CPU("%s: COLD START for cpu (%d)\n", f, cpuid);
restart_other_cpu(cpuid);
SBDP_DBG_CPU("after restarting other cpus\n");
/*
* Wait for the cpu to reach its idle thread before
* we zap him with a request to blow away the mappings
* he (might) have for the sbdp_shutdown_asm code
* he may have executed on unconfigure.
*/
while ((cp->cpu_thread != cp->cpu_idle_thread) && (ntries > 0)) {
DELAY(sbdp_cpu_delay);
ntries--;
}
SBDP_DBG_CPU("%s: waited %d out of %d loops for cpu %d\n",
f, sbdp_cpu_ntries - ntries, sbdp_cpu_ntries, cpuid);
return (0);
}
#define SBDP_CPU_SRAM_ADDR 0x7fff0900000ull
#define SBDP_CPU_SRAM_SIZE 0x20000ull
static const char cpyren_key[] = "COPYREN";
static uint64_t bbsram_pa;
static uint_t bbsram_size;
typedef struct {
caddr_t vaddr;
pgcnt_t npages;
uint64_t *pa;
uint_t *size;
} sbdp_cpu_sram_map_t;
int
sbdp_cpu_poweroff(struct cpu *cp)
{
processorid_t cpuid;
static void sbdp_cpu_shutdown_self(void);
pnode_t nodeid;
sbdp_cpu_sram_map_t map;
static fn_t f = "sbdp_cpu_poweroff";
SBDP_DBG_FUNC("%s\n", f);
ASSERT(MUTEX_HELD(&cpu_lock));
/*
* Capture all CPUs (except for detaching proc) to prevent
* crosscalls to the detaching proc until it has cleared its
* bit in cpu_ready_set.
*/
cpuid = cp->cpu_id;
nodeid = cpunodes[cpuid].nodeid;
ASSERT(nodeid != (pnode_t)0);
*sbdp_valp = 0ull;
/*
* Do the cpu sram mapping now. This avoids problems with
* mutexes and high PILS
*/
if (SBDP_INJECT_ERROR(f, 0) ||
cpusram_map(&map.vaddr, &map.npages) != DDI_SUCCESS) {
return (EBUSY);
}
map.pa = &bbsram_pa;
map.size = &bbsram_size;
/*
* Do a cross call to the cpu so it obtains the base address
*/
xc_one(cpuid, sbdp_get_cpu_sram_addr, (uint64_t)&map,
(uint64_t)NULL);
cpusram_unmap(&map.vaddr, map.npages);
if (SBDP_INJECT_ERROR(f, 1) || bbsram_size == 0) {
cmn_err(CE_WARN, "cpu%d: Key \"%s\" missing from CPU SRAM TOC",
cpuid, cpyren_key);
return (EBUSY);
}
if ((bbsram_pa & MMU_PAGEOFFSET) != 0) {
cmn_err(CE_WARN, "cpu%d: CPU SRAM key \"%s\" not page aligned, "
"offset = 0x%lx", cpuid, cpyren_key,
bbsram_pa - SBDP_CPU_SRAM_ADDR);
return (EBUSY);
}
if (bbsram_size < MMU_PAGESIZE) {
cmn_err(CE_WARN, "cpu%d: CPU SRAM key \"%s\" too small, "
"size = 0x%x", cpuid, cpyren_key, bbsram_size);
return (EBUSY);
}
/*
* Capture all CPUs (except for detaching proc) to prevent
* crosscalls to the detaching proc until it has cleared its
* bit in cpu_ready_set.
*
* The CPU's remain paused and the prom_mutex is known to be free.
* This prevents the x-trap victim from blocking when doing prom
* IEEE-1275 calls at a high PIL level.
*/
promsafe_pause_cpus();
/*
* Quiesce interrupts on the target CPU. We do this by setting
* the CPU 'not ready'- (i.e. removing the CPU from cpu_ready_set) to
* prevent it from receiving cross calls and cross traps.
* This prevents the processor from receiving any new soft interrupts.
*/
mp_cpu_quiesce(cp);
/* tell the prom the cpu is going away */
if (SBDP_INJECT_ERROR(f, 2) || prom_serengeti_cpu_off(nodeid) != 0)
return (EBUSY);
/*
* An sir instruction is issued at the end of the shutdown
* routine to make the CPU go through POST and re-enter OBP.
*/
xt_one_unchecked(cp->cpu_id, (xcfunc_t *)idle_stop_xcall,
(uint64_t)sbdp_cpu_shutdown_self, 0);
*sbdp_valp = 3ull;
start_cpus();
/*
* Wait until we reach the OBP idle loop or time out.
* prom_serengeti_wakeupcpu waits for up to 60 seconds for the
* CPU to reach OBP idle loop.
*/
if (SBDP_INJECT_ERROR(f, 3) ||
prom_serengeti_wakeupcpu(nodeid) != 0) {
/*
* If it fails here, we still consider the unconfigure
* operation as successful.
*/
cmn_err(CE_WARN, "cpu%d: CPU failed to enter OBP idle loop.\n",
cpuid);
}
ASSERT(!(CPU_IN_SET(cpu_ready_set, cpuid)));
bbsram_pa = 0;
bbsram_size = 0;
return (0);
}
processorid_t
sbdp_get_cpuid(sbdp_handle_t *hp, dev_info_t *dip)
{
int cpuid;
char type[OBP_MAXPROPNAME];
pnode_t nodeid;
sbd_error_t *sep;
static fn_t f = "sbdp_get_cpuid";
SBDP_DBG_FUNC("%s\n", f);
nodeid = ddi_get_nodeid(dip);
if (sbdp_is_node_bad(nodeid))
return (-1);
sep = hp->h_err;
if (prom_getproplen(nodeid, "device_type") < OBP_MAXPROPNAME)
(void) prom_getprop(nodeid, "device_type", (caddr_t)type);
else {
sbdp_set_err(sep, ESGT_NO_DEV_TYPE, NULL);
return (-1);
}
if (strcmp(type, "cpu") != 0) {
sbdp_set_err(sep, ESGT_NOT_CPUTYPE, NULL);
return (-1);
}
/*
* Check to see if property "cpuid" exists first.
* If not, check for "portid".
*/
if (prom_getprop(nodeid, "cpuid", (caddr_t)&cpuid) == -1)
if (prom_getprop(nodeid, "portid", (caddr_t)&cpuid) == -1) {
return (-1);
}
return ((processorid_t)cpuid & SG_CPU_ID_MASK);
}
int
sbdp_cpu_get_impl(sbdp_handle_t *hp, dev_info_t *dip)
{
int impl;
char type[OBP_MAXPROPNAME];
pnode_t nodeid;
sbd_error_t *sep;
static fn_t f = "sbdp_cpu_get_impl";
SBDP_DBG_FUNC("%s\n", f);
nodeid = ddi_get_nodeid(dip);
if (sbdp_is_node_bad(nodeid))
return (-1);
sep = hp->h_err;
if (prom_getproplen(nodeid, "device_type") < OBP_MAXPROPNAME)
(void) prom_getprop(nodeid, "device_type", (caddr_t)type);
else {
sbdp_set_err(sep, ESGT_NO_DEV_TYPE, NULL);
return (-1);
}
if (strcmp(type, "cpu") != 0) {
sbdp_set_err(sep, ESGT_NOT_CPUTYPE, NULL);
return (-1);
}
/*
* Get the implementation# property.
*/
if (prom_getprop(nodeid, "implementation#", (caddr_t)&impl) == -1)
return (-1);
return (impl);
}
struct sbdp_prom_get_node_args {
pnode_t node; /* current node */
processorid_t portid; /* portid we are looking for */
pnode_t result_node; /* node found with the above portid */
};
pnode_t
sbdp_find_nearby_cpu_by_portid(pnode_t nodeid, processorid_t portid)
{
struct sbdp_prom_get_node_args arg;
static fn_t f = "sbdp_find_nearby_cpu_by_portid";
SBDP_DBG_FUNC("%s\n", f);
arg.node = nodeid;
arg.portid = portid;
(void) prom_tree_access(sbdp_prom_get_cpu, &arg, NULL);
return (arg.result_node);
}
/*ARGSUSED*/
static int
sbdp_prom_get_cpu(void *arg, int changed)
{
int portid;
pnode_t parent, cur_node;
struct sbdp_prom_get_node_args *argp = arg;
static fn_t f = "sbdp_prom_get_cpu";
SBDP_DBG_FUNC("%s\n", f);
parent = prom_parentnode(argp->node);
for (cur_node = prom_childnode(parent); cur_node != OBP_NONODE;
cur_node = prom_nextnode(cur_node)) {
if (prom_getprop(cur_node, OBP_PORTID, (caddr_t)&portid) < 0)
continue;
if ((portid == argp->portid) && (cur_node != argp->node))
break;
}
argp->result_node = cur_node;
return (0);
}
/*
* A detaching CPU is xcalled with an xtrap to sbdp_cpu_stop_self() after
* it has been offlined. The function of this routine is to get the cpu
* spinning in a safe place. The requirement is that the system will not
* reference anything on the detaching board (memory and i/o is detached
* elsewhere) and that the CPU not reference anything on any other board
* in the system. This isolation is required during and after the writes
* to the domain masks to remove the board from the domain.
*
* To accomplish this isolation the following is done:
* 0) Map the CPUSRAM to obtain the correct address in SRAM
* 1) Create a locked mapping to a location in CPU SRAM where
* the cpu will execute.
* 2) Copy the target function (sbdp_shutdown_asm) in which
* the cpu will execute into CPU SRAM.
* 3) Jump into function with CPU SRAM.
* Function will:
* 3.1) Flush its Ecache (displacement).
* 3.2) Flush its Dcache with HW mechanism.
* 3.3) Flush its Icache with HW mechanism.
* 3.4) Flush all valid and _unlocked_ D-TLB entries.
* 3.5) Flush all valid and _unlocked_ I-TLB entries.
* 4) Jump into a tight loop.
*/
static void
sbdp_cpu_stop_self(uint64_t pa)
{
cpu_t *cp = CPU;
int cpuid = cp->cpu_id;
tte_t tte;
volatile uint_t *src, *dst;
uint_t funclen;
sbdp_shutdown_t sht;
uint_t bbsram_pfn;
uint64_t bbsram_addr;
void (*bbsram_func)(sbdp_shutdown_t *);
extern void sbdp_shutdown_asm(sbdp_shutdown_t *);
extern void sbdp_shutdown_asm_end(void);
funclen = (uint_t)sbdp_shutdown_asm_end - (uint_t)sbdp_shutdown_asm;
ASSERT(funclen <= MMU_PAGESIZE);
ASSERT(bbsram_pa != 0);
ASSERT((bbsram_pa & MMU_PAGEOFFSET) == 0);
ASSERT(bbsram_size >= MMU_PAGESIZE);
stdphys(pa, 3);
bbsram_pfn = (uint_t)(bbsram_pa >> MMU_PAGESHIFT);
bbsram_addr = (uint64_t)sbdp_shutdown_va;
sht.estack = bbsram_addr + MMU_PAGESIZE;
sht.flushaddr = ecache_flushaddr;
tte.tte_inthi = TTE_VALID_INT | TTE_SZ_INT(TTE8K) |
TTE_PFN_INTHI(bbsram_pfn);
tte.tte_intlo = TTE_PFN_INTLO(bbsram_pfn) |
TTE_HWWR_INT | TTE_PRIV_INT | TTE_LCK_INT;
sfmmu_dtlb_ld(sbdp_shutdown_va, KCONTEXT, &tte); /* load dtlb */
sfmmu_itlb_ld(sbdp_shutdown_va, KCONTEXT, &tte); /* load itlb */
for (src = (uint_t *)sbdp_shutdown_asm, dst = (uint_t *)bbsram_addr;
src < (uint_t *)sbdp_shutdown_asm_end; src++, dst++)
*dst = *src;
bbsram_func = (void (*)())bbsram_addr;
sht.size = (uint32_t)cpunodes[cpuid].ecache_size << 1;
sht.linesize = (uint32_t)cpunodes[cpuid].ecache_linesize;
sht.physaddr = pa;
/*
* Signal to sbdp_cpu_poweroff() that we're just
* about done.
*/
cp->cpu_m.in_prom = 1;
stdphys(pa, 4);
(*bbsram_func)(&sht);
}
/* ARGSUSED */
void
sbdp_get_cpu_sram_addr(uint64_t arg1, uint64_t arg2)
{
uint64_t *pap;
uint_t *sizep;
struct iosram_toc *tocp;
uint_t offset;
uint_t size;
sbdp_cpu_sram_map_t *map;
int i;
fn_t f = "sbdp_get_cpu_sram_addr";
SBDP_DBG_FUNC("%s\n", f);
map = (sbdp_cpu_sram_map_t *)arg1;
tocp = (struct iosram_toc *)map->vaddr;
pap = map->pa;
sizep = map->size;
for (i = 0; i < tocp->iosram_tagno; i++) {
if (strcmp(tocp->iosram_keys[i].key, cpyren_key) == 0)
break;
}
if (i == tocp->iosram_tagno) {
*pap = 0;
*sizep = 0;
return;
}
offset = tocp->iosram_keys[i].offset;
size = tocp->iosram_keys[i].size;
/*
* The address we want is the begining of cpusram + offset
*/
*pap = SBDP_CPU_SRAM_ADDR + offset;
*sizep = size;
}
static int
cpusram_map(caddr_t *vaddrp, pgcnt_t *npp)
{
uint_t pgoffset;
pgcnt_t npages;
pfn_t pfn;
uint64_t base;
caddr_t kaddr;
uint_t mapping_attr;
base = (uint64_t)SBDP_CPU_SRAM_ADDR & (~MMU_PAGEOFFSET);
pfn = mmu_btop(base);
/*
* Do a quick sanity check to make sure we are in I/O space.
*/
if (pf_is_memory(pfn))
return (DDI_FAILURE);
pgoffset = (ulong_t)SBDP_CPU_SRAM_ADDR & MMU_PAGEOFFSET;
npages = mmu_btopr(SBDP_CPU_SRAM_SIZE + pgoffset);
kaddr = vmem_alloc(heap_arena, ptob(npages), VM_NOSLEEP);
if (kaddr == NULL)
return (DDI_ME_NORESOURCES);
mapping_attr = PROT_READ;
/*
* Now map in the pages we've allocated...
*/
hat_devload(kas.a_hat, kaddr, ptob(npages), pfn, mapping_attr,
HAT_LOAD_LOCK);
*vaddrp = kaddr + pgoffset;
*npp = npages;
return (DDI_SUCCESS);
}
static void
cpusram_unmap(caddr_t *vaddrp, pgcnt_t npages)
{
uint_t pgoffset;
caddr_t base;
caddr_t addr = *vaddrp;
pgoffset = (ulong_t)SBDP_CPU_SRAM_ADDR & MMU_PAGEOFFSET;
base = addr - pgoffset;
hat_unload(kas.a_hat, base, ptob(npages), HAT_UNLOAD_UNLOCK);
vmem_free(heap_arena, base, ptob(npages));
*vaddrp = 0;
}
static void
sbdp_cpu_shutdown_self(void)
{
cpu_t *cp = CPU;
int cpuid = cp->cpu_id;
extern void flush_windows(void);
uint64_t pa = va_to_pa((void *)sbdp_valp);
stdphys(pa, 8);
flush_windows();
(void) spl8();
stdphys(pa, 6);
ASSERT(cp->cpu_intr_actv == 0);
ASSERT(cp->cpu_thread == cp->cpu_idle_thread ||
cp->cpu_thread == cp->cpu_startup_thread);
cp->cpu_flags = CPU_OFFLINE | CPU_QUIESCED | CPU_POWEROFF;
CPU_SIGNATURE(OS_SIG, SIGST_DETACHED, SIGSUBST_NULL, cpuid);
stdphys(pa, 7);
sbdp_cpu_stop_self(pa);
cmn_err(CE_PANIC, "sbdp_cpu_shutdown_self: CPU %d FAILED TO SHUTDOWN",
cpuid);
}
typedef struct {
int node;
int board;
int non_panther_cpus;
} sbdp_node_walk_t;
static int
sbdp_find_non_panther_cpus(dev_info_t *dip, void *node_args)
{
int impl, cpuid, portid;
int buflen;
char buf[OBP_MAXPROPNAME];
sbdp_node_walk_t *args = (sbdp_node_walk_t *)node_args;
if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, OBP_DEVICETYPE, (caddr_t)buf,
&buflen) != DDI_PROP_SUCCESS) {
return (DDI_WALK_CONTINUE);
}
if (strcmp(buf, "cpu") != 0) {
return (DDI_WALK_CONTINUE);
}
if ((impl = ddi_getprop(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, "implementation#", -1)) == -1) {
return (DDI_WALK_CONTINUE);
}
if ((cpuid = ddi_getprop(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, "cpuid", -1)) == -1) {
return (DDI_WALK_CONTINUE);
}
portid = SG_CPUID_TO_PORTID(cpuid);
/* filter out nodes not on this board */
if (SG_PORTID_TO_BOARD_NUM(portid) != args->board ||
SG_PORTID_TO_NODEID(portid) != args->node) {
return (DDI_WALK_PRUNECHILD);
}
switch (impl) {
case CHEETAH_IMPL:
case CHEETAH_PLUS_IMPL:
case JAGUAR_IMPL:
args->non_panther_cpus++;
break;
case PANTHER_IMPL:
break;
default:
ASSERT(0);
args->non_panther_cpus++;
break;
}
SBDP_DBG_CPU("cpuid=0x%x, portid=0x%x, impl=0x%x, device_type=%s",
cpuid, portid, impl, buf);
return (DDI_WALK_CONTINUE);
}
int
sbdp_board_non_panther_cpus(int node, int board)
{
sbdp_node_walk_t arg = {0};
arg.node = node;
arg.board = board;
/*
* Root node doesn't have to be held.
*/
ddi_walk_devs(ddi_root_node(), sbdp_find_non_panther_cpus,
(void *)&arg);
return (arg.non_panther_cpus);
}