us3_cheetahplus.c revision 3a5a1f38c4f5cd4367c0fa11310aa99aeb30f93c
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/sysmacros.h>
#include <sys/archsystm.h>
#include <sys/machparam.h>
#include <sys/machsystm.h>
#include <sys/machthread.h>
#include <sys/elf_SPARC.h>
#include <vm/hat_sfmmu.h>
#include <vm/seg_kmem.h>
#include <sys/cheetahregs.h>
#include <sys/us3_module.h>
#include <sys/dditypes.h>
#include <sys/prom_debug.h>
#include <sys/prom_plat.h>
#include <sys/cpu_module.h>
#include <sys/sysmacros.h>
#include <sys/platform_module.h>
#include <sys/machtrap.h>
#include <sys/bootconf.h>
#ifdef CHEETAHPLUS_ERRATUM_25
#endif /* CHEETAHPLUS_ERRATUM_25 */
/*
* See comment above cpu_scrub_cpu_setup() for description
*/
#define SCRUBBER_NEITHER_CORE_ONLINE 0x0
#define SCRUBBER_CORE_0_ONLINE 0x1
#define SCRUBBER_CORE_1_ONLINE 0x2
#define SCRUBBER_BOTH_CORES_ONLINE (SCRUBBER_CORE_0_ONLINE | \
static void cpu_async_log_tlb_parity_err(void *flt);
/*
* Setup trap handlers.
*/
void
cpu_init_trap(void)
{
}
/*
* Set the magic constants of the implementation.
*/
/*ARGSUSED*/
void
{
int i, a;
/*
* ecache_setsize needs to maximum of all cpu ecache setsizes
*/
i = 0; a = vac_size;
while (a >>= 1)
++i;
vac_shift = i;
vac = 1;
}
/*
* Use Panther values for Panther-only domains.
* See Panther PRM, 1.5.4 Cache Hierarchy
*/
void
cpu_fix_allpanther(void)
{
/* dcache same as Ch+ */
/* vac same as Ch+ */
}
void
{
#if (NCPU > IDSR_BN_SETS)
int index = 0;
int ncpuids = 0;
#endif
#ifdef CHEETAHPLUS_ERRATUM_25
int recovered = 0;
int cpuid;
#endif
#if (NCPU <= IDSR_BN_SETS)
for (i = 0; i < NCPU; i++)
if (CPU_IN_SET(set, i)) {
CPUSET_DEL(set, i);
if (CPUSET_ISNULL(set))
break;
}
#else
for (i = 0; i < NCPU; i++)
if (CPU_IN_SET(set, i)) {
ncpuids++;
/*
* Ship only to the first (IDSR_BN_SETS) CPUs. If we
* find we have shipped to more than (IDSR_BN_SETS)
* CPUs, set "index" to the highest numbered CPU in
* the set so we can ship to other CPUs a bit later on.
*/
if (shipped < IDSR_BN_SETS) {
CPUSET_DEL(set, i);
if (CPUSET_ISNULL(set))
break;
} else
index = (int)i;
}
#endif
for (;;) {
#if (NCPU <= IDSR_BN_SETS)
if (idsr == 0)
break;
#else
break;
#endif
/*
* If there is a big jump between the current tick
* count and lasttick, we have probably hit a break
* point. Adjust endtick accordingly to avoid panic.
*/
if (panic_quiesce)
return;
#ifdef CHEETAHPLUS_ERRATUM_25
cpuid = -1;
for (i = 0; i < IDSR_BN_SETS; i++) {
if (idsr & (IDSR_NACK_BIT(i) |
IDSR_BUSY_BIT(i))) {
break;
}
}
recovered == 0) {
if (mondo_recover(cpuid, i)) {
/*
* We claimed the whole memory or
* full scan is disabled.
*/
recovered++;
}
/*
* Recheck idsr
*/
continue;
} else
#endif /* CHEETAHPLUS_ERRATUM_25 */
{
"[%d NACK %d BUSY]\nIDSR 0x%"
for (i = 0; i < IDSR_BN_SETS; i++) {
if (idsr & (IDSR_NACK_BIT(i) |
IDSR_BUSY_BIT(i))) {
cpuids[i]);
}
}
}
}
#if (NCPU > IDSR_BN_SETS)
if (cpus_left) {
do {
/*
* Sequence through and ship to the
* remainder of the CPUs in the system
* (e.g. other than the first
* (IDSR_BN_SETS)) in reverse order.
*/
i = IDSR_BUSY_IDX(lo);
shipped++;
/*
* If we've processed all the CPUs,
* exit the loop now and save
* instructions.
*/
break;
break;
}
} while (cpus_left);
#ifdef CHEETAHPLUS_ERRATUM_25
/*
* Clear recovered because we are sending to
* a new set of targets.
*/
recovered = 0;
#endif
continue;
}
}
#endif
if (curbusy) {
busy++;
continue;
}
#ifdef SEND_MONDO_STATS
{
if (n < 8192)
x_nack_stimes[n >> 7]++;
}
#endif
;
do {
i = IDSR_NACK_IDX(lo);
} while (curnack);
nack++;
busy = 0;
}
#ifdef SEND_MONDO_STATS
{
if (n < 8192)
x_set_stimes[n >> 7]++;
else
}
x_set_cpus[shipped]++;
#endif
}
/*
* Handles error logging for implementation specific error types
*/
/*ARGSUSED1*/
int
{
case CPU_IC_PARITY:
return (CH_ASYNC_LOG_DONE);
case CPU_DC_PARITY:
return (CH_ASYNC_LOG_DONE);
case CPU_DUE:
return (CH_ASYNC_LOG_DONE);
case CPU_ITLB_PARITY:
case CPU_DTLB_PARITY:
return (CH_ASYNC_LOG_DONE);
default:
return (CH_ASYNC_LOG_UNKNOWN);
}
}
/*
* Figure out if Ecache is direct-mapped (Cheetah or Cheetah+ with Ecache
* control ECCR_ASSOC bit off or 2-way (Cheetah+ with ECCR_ASSOC on).
* We need to do this on the fly because we may have mixed Cheetah+'s with
* both direct and 2-way Ecaches. Panther only supports 4-way L3$.
*/
int
cpu_ecache_nway(void)
{
return (PN_L3_NWAYS);
}
/*
* Note that these are entered into the table: Fatal Errors (PERR, IERR, ISAP,
* Afar overwrite policy is:
* Class 4:
* AFSR -- UCC, UCU, TUE, TSCE, TUE_SH
* AFSR_EXT -- L3_UCC, L3_UCU, L3_TUE, L3_TUE_SH
* Class 3:
* AFSR -- UE, DUE, EDU, WDU, CPU
* AFSR_EXT -- L3_EDU, L3_WDU, L3_CPU
* Class 2:
* AFSR -- CE, EDC, EMC, WDC, CPC, THCE
* AFSR_EXT -- L3_EDC, L3_WDC, L3_CPC, L3_THCE
* Class 1:
* AFSR -- TO, DTO, BERR, DBERR
*/
/* Fatal Errors */
CPU_FATAL, "PERR Fatal",
CPU_FATAL, "IERR Fatal",
CPU_FATAL, "ISAP Fatal",
CPU_FATAL, "L3_TUE_SH Fatal",
CPU_FATAL, "L3_TUE Fatal",
CPU_FATAL, "TUE_SH Fatal",
CPU_FATAL, "TUE Fatal",
CPU_FATAL, "EMU Fatal",
CPU_FATAL, "IMU Fatal",
/* L3$ Address parity errors are reported via the MECC bit */
CPU_L3_ADDR_PE, "L3 Address Parity",
CPU_ORPH, "Orphaned L3_UCU",
CPU_ORPH, "Orphaned L3_UCC",
CPU_ORPH, "Orphaned UCU",
CPU_ORPH, "Orphaned UCC",
/* UCU, UCC */
CPU_UE_ECACHE, "L3_UCU",
CPU_CE_ECACHE, "L3_UCC",
CPU_UE_ECACHE, "UCU",
CPU_CE_ECACHE, "UCC",
CPU_CE_ECACHE, "TSCE",
/* UE, EDU:ST, EDU:BLD, WDU, CPU */
CPU_UE, "Uncorrectable system bus (UE)",
CPU_UE_ECACHE_RETIRE, "L3_EDU:ST",
CPU_UE_ECACHE_RETIRE, "L3_EDU:BLD",
CPU_UE_ECACHE_RETIRE, "L3_WDU",
CPU_UE_ECACHE, "L3_CPU",
CPU_UE_ECACHE_RETIRE, "EDU:ST",
CPU_UE_ECACHE_RETIRE, "EDU:BLD",
CPU_UE_ECACHE_RETIRE, "WDU",
CPU_UE_ECACHE, "CPU",
CPU_DUE, "DUE",
/* CE, EDC, EMC, WDC, CPC */
CPU_CE, "Corrected system bus (CE)",
CPU_CE_ECACHE, "L3_EDC",
CPU_CE_ECACHE, "EDC",
CPU_EMC, "EMC",
CPU_CE_ECACHE, "L3_WDC",
CPU_CE_ECACHE, "L3_CPC",
CPU_CE_ECACHE, "L3_THCE",
CPU_CE_ECACHE, "WDC",
CPU_CE_ECACHE, "CPC",
CPU_CE_ECACHE, "THCE",
/* TO, BERR */
CPU_TO, "Timeout (TO)",
CPU_BERR, "Bus Error (BERR)",
CPU_TO, "Disrupting Timeout (DTO)",
CPU_BERR, "Disrupting Bus Error (DBERR)",
/* IVU, IVC, IMC */
CPU_IV, "IVU",
CPU_IV, "IVC",
CPU_IV, "IMC",
0, NULL, 0,
0, NULL,
};
/*
* See Cheetah+ Delta PRM 10.9 and section P.6.1 of the Panther PRM
* Class 4:
* AFSR -- UCC, UCU, TUE, TSCE, TUE_SH
* AFSR_EXT -- L3_UCC, L3_UCU, L3_TUE, L3_TUE_SH
* Class 3:
* AFSR -- UE, DUE, EDU, EMU, WDU, CPU
* AFSR_EXT -- L3_EDU, L3_WDU, L3_CPU
* Class 2:
* AFSR -- CE, EDC, EMC, WDC, CPC, THCE
* AFSR_EXT -- L3_EDC, L3_WDC, L3_CPC, L3_THCE
* Class 1:
* AFSR -- TO, DTO, BERR, DBERR
* AFSR_EXT --
*/
uint64_t afar_overwrite[] = {
/* class 4: */
/* class 3: */
/* class 2: */
/* class 1: */
0
};
/*
* See Cheetah+ Delta PRM 10.9.
* Class 2: UE, DUE, IVU, EDU, WDU, UCU, CPU
* Class 1: CE, IVC, EDC, WDC, UCC, CPC
*/
uint64_t esynd_overwrite[] = {
/* class 2: */
/* class 1: */
0
};
/*
* In panther, the E_SYND overwrite policy changed a little bit
* by adding one more level.
* class 3:
* AFSR -- UCU, UCC
* AFSR_EXT -- L3_UCU, L3_UCC
* Class 2:
* AFSR -- UE, DUE, IVU, EDU, WDU, CPU
* AFSR_EXT -- L3_EDU, L3_WDU, L3_CPU
* Class 1:
* AFSR -- CE, IVC, EDC, WDC, CPC
* AFSR_EXT -- L3_EDC, L3_WDC, L3_CPC
*/
uint64_t pn_esynd_overwrite[] = {
/* class 3: */
C_AFSR_UCU | C_AFSR_UCC |
/* class 2: */
/* class 1: */
0
};
int
{
}
/*
* Prioritized list of Error bits for MSYND overwrite.
* See Cheetah PRM P.6.3
* Class 2: EMU
* Class 1: EMC
*
* Panther adds IMU and IMC.
*/
uint64_t msynd_overwrite[] = {
/* class 2: */
/* class 1: */
0
};
/*
* change cpu speed bits -- new speed will be normal-speed/divisor.
*
* The Jalapeno memory controllers are required to drain outstanding
* memory transactions within 32 JBus clocks in order to be ready
* to enter Estar mode. In some corner cases however, that time
* fell short.
*
* A safe software solution is to force MCU to act like in Estar mode,
* then delay 1us (in ppm code) prior to assert J_CHNG_L signal.
* To reverse the effect, upon exiting Estar, software restores the
* MCU to its original state.
*/
/* ARGSUSED1 */
void
{
continue;
reg = get_safari_config();
return;
}
/*
* We will reach here only if OBP and kernel don't agree on
* the speeds supported by the CPU.
*/
}
/*
* Cpu private initialization. This includes allocating the cpu_private
* data structure, initializing it, and initializing the scrubber for this
* cpu. This function calls cpu_init_ecache_scrub_dr to init the scrubber.
* We use kmem_cache_create for the cheetah private data structure because
* it needs to be allocated on a PAGESIZE (8192) byte boundary.
*/
void
{
int i;
/* LINTED: E_TRUE_LOGICAL_EXPR */
/*
* Running with Cheetah CPUs in a Cheetah+, Jaguar, Panther or
* configuration. Attempting to do so may result in unpredictable
* failures (e.g. running Cheetah+ CPUs with Cheetah E$ disp flush)
* so don't allow it.
*
* This is just defensive code since this configuration mismatch
* should have been caught prior to OS execution.
*/
}
/*
* If the ch_private_cache has not been created, create it.
*/
if (ch_private_cache == NULL) {
}
for (i = 0; i < CH_ERR_TL1_TLMAX; i++)
/* Panther has a larger Icache compared to cheetahplus or Jaguar */
} else {
}
/*
* Panther's L2$ and E$ are shared between cores, so the scrubber is
* only needed on one of the cores. At this point, we assume all cores
* are online, and we only enable the scrubber on core 0.
*/
CACHE_SCRUBBER_INFO_E] = 0;
}
}
}
/*
* Clear the error state registers for this CPU.
* For Cheetah+/Jaguar, just clear the AFSR but
* for Panther we also have to clear the AFSR_EXT.
*/
void
{
}
}
void
uint8_t log_way_mask = 0;
int i;
/*
* Only Panther CPUs have the additional L2$ data that needs
* to be logged here
*/
return;
/*
* We'll use a simple bit mask to keep track of which way(s)
* of the stored cache line we want to log. The idea is to
* log the entry if it is a valid line and it matches our
* fault AFAR. If no match is found, we will simply log all
* the ways.
*/
for (i = 0; i < PN_L2_NWAYS; i++)
log_way_mask |= (1 << i);
/* If no matching valid lines were found, we log all ways */
if (log_way_mask == 0)
/* Log the cache lines */
for (i = 0; i < PN_L2_NWAYS; i++)
if (log_way_mask & (1 << i))
}
/*
* For this routine to return true, the L2 tag in question must be valid
* and the tag PA must match the fault address (faddr) assuming the correct
* index is being used.
*/
static int
return (1);
return (0);
}
/*
* This array is used to convert the 3 digit PgSz encoding (as used in
* various MMU registers such as MMU_TAG_ACCESS_EXT) into the corresponding
* page size.
*/
static uint64_t tlb_pgsz_to_size[] = {
/* 000 = 8KB: */
0x2000,
/* 001 = 64KB: */
0x10000,
/* 010 = 512KB: */
0x80000,
/* 011 = 4MB: */
0x400000,
/* 100 = 32MB: */
0x2000000,
/* 101 = 256MB: */
0x10000000,
/* undefined for encodings 110 and 111: */
0, 0
};
/*
* The itlb_parity_trap and dtlb_parity_trap handlers transfer control here
* after collecting logout information related to the TLB parity error and
* flushing the offending TTE entries from the ITLB or DTLB.
*
* DTLB traps which occur at TL>0 are not recoverable because we will most
* likely be corrupting some other trap handler's alternate globals. As
* such, we simply panic here when that happens. ITLB parity errors are
* not expected to happen at TL>0.
*/
void
char *error_class;
/*
* Get the CPU log out info. If we can't find our CPU private
* pointer, or if the logout information does not correspond to
* this error, then we will have to make due without detailed
* logout information.
*/
if (CPU_PRIVATE(CPU)) {
}
if (tlop) {
/* Zero out + invalidate TLB logout. */
} else {
/*
* Copy what logout information we have and mark
* it incomplete.
*/
}
/*
* Log the error.
*/
if (immu_parity) {
} else {
}
/*
* The TLB entries have already been flushed by the TL1 trap
* handler so at this point the only thing left to do is log
* the error message.
*/
/*
* Panic here if aflt->flt_panic has been set. Enqueued
* errors will be logged as part of the panic flow.
*/
} else {
}
}
/*
* This routine is called when a TLB parity error event is 'ue_drain'ed
* or 'ce_drain'ed from the errorq.
*/
void
cpu_async_log_tlb_parity_err(void *flt) {
#ifdef lint
#endif
/*
* We only capture TLB information if we encountered
* a TLB parity error and Panther is the only CPU which
* can detect a TLB parity error.
*/
if (ch_flt->flt_data_incomplete == 0) {
else /* parity error is in DTLB */
}
}
/*
* Add L1 Prefetch cache data to the ereport payload.
*/
void
{
int i, ways_logged = 0;
/*
* We only capture P$ information if we encountered
* a P$ parity error and Panther is the only CPU which
* can detect a P$ parity error.
*/
for (i = 0; i < CH_PCACHE_NWAY; i++) {
sizeof (ch_pc_data_t));
ways_logged++;
}
}
/*
* Add the pcache data to the payload.
*/
if (ways_logged != 0) {
}
}
/*
* Add TLB diagnostic data to the ereport payload.
*/
void
{
/*
* We only capture TLB information if we encountered
* a TLB parity error and Panther is the only CPU which
* can detect a TLB parity error.
*/
/*
* Add the TLB diagnostic data to the payload
* if it was collected.
*/
NULL);
}
} else {
/*
* Add the TLB diagnostic data to the payload
* if it was collected.
*/
NULL);
}
}
}
/*
* Panther Cache Scrubbing:
*
* In Jaguar, the E$ was split between cores, so the scrubber must run on both
* cores. For Panther, however, the L2$ and L3$ are shared across cores.
* Therefore, the E$ scrubber only needs to run on one of the two cores.
*
* There are four possible states for the E$ scrubber:
*
* 0. If both cores are offline, add core 0 to cpu_offline_set so that
* the offline scrubber will run on it.
* 1. If core 0 is online and core 1 off, we run the scrubber on core 0.
* 2. If core 1 is online and core 0 off, we move the scrubber to run
* on core 1.
* 3. If both cores are online, only run the scrubber on core 0.
*
* These states are enumerated by the SCRUBBER_[BOTH|CORE|NEITHER]_* defines
* above. One of those values is stored in
* chpr_scrub_misc->chsm_core_state on each core.
*
* Also note that, for Panther, ecache_flush_line() will flush out the L2$
* before the E$, so the L2$ will be scrubbed by the E$ scrubber. No
* additional code is necessary to scrub the L2$.
*
* For all cpu types, whenever a cpu or core is offlined, add it to
* cpu_offline_set so the necessary scrubbers can still run. This is still
* necessary on Panther so the D$ scrubber can still run.
*/
/*ARGSUSED*/
int
{
int old_state, i;
switch (what) {
case CPU_ON:
case CPU_INIT:
break;
case CPU_OFF:
break;
default:
return (0);
}
return (0);
}
/*
* Update the chsm_enable[CACHE_SCRUBBER_INFO_E] value
* if necessary
*/
for (i = 0; i < 2; i++) {
/*
* This may happen during DR - one core is offlined
* and completely unconfigured before the second
* core is offlined. Give up and return quietly,
* since the second core should quickly be removed
* anyways.
*/
return (0);
}
}
/* cpuid is core 0 */
}
}
} else {
/* cpuid is core 1 */
if (cpu_is_active(core_cpus[0])) {
}
}
}
return (0);
}
if (old_state == SCRUBBER_CORE_1_ONLINE) {
/*
* We need to move the scrubber state from core 1
* back to core 0. This data is not protected by
* locks, but the worst that can happen is some
* lines are scrubbed multiple times. chsm_oustanding is
* set to 0 to make sure an interrupt is scheduled the
* first time through do_scrub().
*/
}
switch (new_state) {
case SCRUBBER_CORE_0_ONLINE:
break;
case SCRUBBER_CORE_1_ONLINE:
default:
/*
* We need to move the scrubber state from core 0
* to core 1.
*/
break;
}
return (0);
}
/*
* Returns a pointer to the cpu structure of the argument's sibling core.
* If no sibling core can be found, return NULL.
*/
static cpu_t *
{
return (NULL);
return (NULL);
return (nextp);
}