sbdp_cpu.c revision e0731422366620894c16c1ee6515551c5f00733d
/*
* 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
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* 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/machsystm.h>
#include <sys/cpu_sgnblk_defs.h>
#include <vm/hat_sfmmu.h>
#include <vm/seg_kmem.h>
#include <sys/sbd_ioctl.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>
static int sbdp_cpu_ntries = 50000;
static int sbdp_cpu_delay = 100;
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 void sbdp_cpu_shutdown_self(void);
int
{
int rv = 0;
static fn_t f = "sbdp_disconnect_cpu";
SBDP_DBG_FUNC("%s\n", f);
/*
* 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) ||
rv = -1;
goto out;
}
/*
* Grab the lock to prevent status threads from accessing
* registers on the CPU when it is being put into reset.
*/
/*
* 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.
*/
/*
* Ask OBP to mark the CPU as in POST
*/
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.
*/
}
rv = -1;
goto out;
}
out:
}
if (rv != 0) {
}
return (rv);
}
int
{
int i;
int rv = 0;
static fn_t f = "sbdp_connect_cpu";
SBDP_DBG_FUNC("%s\n", f);
/*
* The check for determining if nodeid is valid is done inside
* sbdp_get_bd_and_wnode_num.
*/
if (SBDP_INJECT_ERROR(f, 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 the first core is not present, the start CPU
* interface needs to be called with the portid rather
* than the cpuid.
*/
}
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.
*/
/*
* 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++)
break;
}
if (i == SG_MAX_CPUS_PER_BD) {
/*
* All cpus are out of reset so it is safe to
* update the bd info
*/
}
out:
if (rv != 0)
return (rv);
}
int
{
int cpuid;
int ntries;
extern void restart_other_cpu(int);
static fn_t f = "sbdp_cpu_poweron";
SBDP_DBG_FUNC("%s\n", f);
/*
* 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);
}
/*
* 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("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.
*/
ntries--;
}
SBDP_DBG_CPU("%s: waited %d out of %d loops for cpu %d\n",
return (0);
}
#define SBDP_CPU_SRAM_ADDR 0x7fff0900000ull
#define SBDP_CPU_SRAM_SIZE 0x20000ull
static const char cpyren_key[] = "COPYREN";
static uint_t bbsram_size;
typedef struct {
int
{
static fn_t f = "sbdp_cpu_poweroff";
SBDP_DBG_FUNC("%s\n", f);
/*
* Capture all CPUs (except for detaching proc) to prevent
* crosscalls to the detaching proc until it has cleared its
* bit in cpu_ready_set.
*/
/*
* Do the cpu sram mapping now. This avoids problems with
* mutexes and high PILS
*/
if (SBDP_INJECT_ERROR(f, 0) ||
return (EBUSY);
}
/*
* Do a cross call to the cpu so it obtains the base address
*/
cpuid, cpyren_key);
return (EBUSY);
}
if ((bbsram_pa & MMU_PAGEOFFSET) != 0) {
return (EBUSY);
}
if (bbsram_size < MMU_PAGESIZE) {
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.
*/
/*
* 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.
*/
/* tell the prom the cpu is going away */
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.
*/
*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.
*/
cpuid);
}
bbsram_pa = 0;
bbsram_size = 0;
return (0);
}
{
int cpuid;
char type[OBP_MAXPROPNAME];
static fn_t f = "sbdp_get_cpuid";
SBDP_DBG_FUNC("%s\n", f);
if (sbdp_is_node_bad(nodeid))
return (-1);
else {
return (-1);
}
return (-1);
}
/*
* Check to see if property "cpuid" exists first.
* If not, check for "portid".
*/
return (-1);
}
}
int
{
int impl;
char type[OBP_MAXPROPNAME];
static fn_t f = "sbdp_cpu_get_impl";
SBDP_DBG_FUNC("%s\n", f);
if (sbdp_is_node_bad(nodeid))
return (-1);
else {
return (-1);
}
return (-1);
}
/*
* Get the implementation# property.
*/
return (-1);
return (impl);
}
struct sbdp_prom_get_node_args {
};
{
struct sbdp_prom_get_node_args arg;
static fn_t f = "sbdp_find_nearby_cpu_by_portid";
SBDP_DBG_FUNC("%s\n", f);
return (arg.result_node);
}
/*ARGSUSED*/
static int
{
int portid;
static fn_t f = "sbdp_prom_get_cpu";
SBDP_DBG_FUNC("%s\n", f);
continue;
break;
}
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
{
void (*bbsram_func)(sbdp_shutdown_t *);
extern void sbdp_shutdown_asm(sbdp_shutdown_t *);
extern void sbdp_shutdown_asm_end(void);
bbsram_func = (void (*)())bbsram_addr;
/*
* Signal to sbdp_cpu_poweroff() that we're just
* about done.
*/
(*bbsram_func)(&sht);
}
/* ARGSUSED */
void
{
struct iosram_toc *tocp;
int i;
fn_t f = "sbdp_get_cpu_sram_addr";
SBDP_DBG_FUNC("%s\n", f);
for (i = 0; i < tocp->iosram_tagno; i++) {
break;
}
if (i == tocp->iosram_tagno) {
*pap = 0;
*sizep = 0;
return;
}
/*
* The address we want is the begining of cpusram + offset
*/
}
static int
{
/*
* Do a quick sanity check to make sure we are in I/O space.
*/
if (pf_is_memory(pfn))
return (DDI_FAILURE);
return (DDI_ME_NORESOURCES);
/*
* Now map in the pages we've allocated...
*/
return (DDI_SUCCESS);
}
static void
{
*vaddrp = 0;
}
static void
sbdp_cpu_shutdown_self(void)
{
extern void flush_windows(void);
(void) spl8();
cpuid);
}
typedef struct {
int node;
int board;
int non_panther_cpus;
static int
{
int buflen;
char buf[OBP_MAXPROPNAME];
&buflen) != DDI_PROP_SUCCESS) {
return (DDI_WALK_CONTINUE);
}
return (DDI_WALK_CONTINUE);
}
return (DDI_WALK_CONTINUE);
}
return (DDI_WALK_CONTINUE);
}
/* filter out nodes not on this board */
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",
return (DDI_WALK_CONTINUE);
}
int
{
sbdp_node_walk_t arg = {0};
/*
* Root node doesn't have to be held.
*/
(void *)&arg);
return (arg.non_panther_cpus);
}