/*
* 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.
*/
#include <sys/sysmacros.h>
#include <sys/vm_machparam.h>
#include <sys/machparam.h>
#include <sys/machcpuvar.h>
#include <sys/machsystm.h>
#include <sys/starfire.h>
/*
* This data structure is referenced during the cross-call
* update of the CICs. The semaphore is used for synchronization
* when waiting for completion of the respective operation.
* We want IDNCIC_TIMEOUT ticks for all the cpus to check-in
* before we bail out and fail the operation.
*/
#define IDNCIC_UNKNOWN 0
#ifdef DEBUG
#define CICREG_SMMASK 0
#define RESET_CIC_HISTORY() \
(xf_cicboards = xf_cicbuses = 0, \
#define DUMP_CIC_HISTORY() \
{ \
continue; \
continue; \
printf("%s: (bd.bs = %d.%d) m/b/l = " \
} \
} \
DEBUG_DELAY(); \
} \
}
/*
* Globally updated during CIC reg updates. Everybody has
* a unique location, so no concern about updates stepping
* on each other.
*/
#else /* DEBUG */
#define RESET_CIC_HISTORY()
#define DUMP_CIC_HISTORY()
#endif /* DEBUG */
}; /* sizeof = 0x68 = 104 (26X) */
#ifdef DEBUG
#endif /* DEBUG */
static int cic_get_smmask_bit(void);
static void idnxf_shmem_wakeup(void *arg);
#ifdef DEBUG
#else /* DEBUG */
#define DEBUG_DELAY()
#endif /* DEBUG */
/*
* ---------------------------------------------------------------------
*/
{
bus);
PR_XF("%s: (bd=%d, bs=%d) csr_addr = 0x%llx\n",
return (domain_mask);
}
{
bus);
PR_XF("%s: (bd=%d, bs=%d) csr_addr = 0x%llx\n",
return (sm_mask);
}
static int
{
int cnt;
sm_mask &= 0xffff;
/*
* Before we can write to the CIC, we need to set
* up the CIC write data buffer in the PC.
*/
return (-1);
/*
* Now we can write to the CIC.
*/
bus);
PR_XF("%s: (bd=%d, bs=%d) csr_addr = 0x%llx\n",
PR_XF("%s: writing sm_mask = 0x%x\n",
/*
* Read back for verification.
*/
;
}
{
bus);
PR_XF("%s:MSB: (bd=%d, bs=%d) csr_addr = 0x%llx\n",
sm_bar <<= 16;
bus);
PR_XF("%s:LSB: (bd=%d, bs=%d) csr_addr = 0x%llx\n",
return (sm_bar);
}
static int
{
int cnt;
/*
* Before we can write to the CIC, we need to set
* up the CIC write data buffer in the PC.
*/
return (-1);
bus);
PR_XF("%s:MSB: (bd=%d, bs=%d) csr_addr = 0x%llx\n",
PR_XF("%s:MSB: sm_bar[31:16] = 0x%x\n",
for (cnt = 0;
cnt++)
;
if (cnt == 10) {
"IDN: 500: failed to write sm_bar (msb) (0x%x)",
(uint_t)sm_bar_msb);
return (-1);
}
/*
* Now to LSB portion.
*/
return (-1);
bus);
PR_XF("%s:LSB: (bd=%d, bs=%d) csr_addr = 0x%llx\n",
PR_XF("%s:LSB: sm_bar[15:0] = 0x%x\n",
for (cnt = 0;
cnt++)
;
if (cnt == 10) {
"IDN: 500: failed to write sm_bar (lsb) (0x%x)",
(uint_t)sm_bar_lsb);
return (-1);
}
return (0);
}
{
bus);
PR_XF("%s:MSB: (bd=%d, bs=%d) csr_addr = 0x%llx\n",
sm_lar <<= 16;
bus);
PR_XF("%s:LSB: (bd=%d, bs=%d) csr_addr = 0x%llx\n",
return (sm_lar);
}
static int
{
int cnt;
/*
* Before we can write to the CIC, we need to set
* up the CIC write data buffer in the PC.
*/
return (-1);
bus);
PR_XF("%s:MSB: (bd=%d, bs=%d) csr_addr = 0x%llx\n",
PR_XF("%s:MSB: sm_lar[31:16] = 0x%x\n",
for (cnt = 0;
cnt++)
;
if (cnt == 10) {
"IDN: 501: failed to write sm_lar (msb) (0x%x)",
(uint_t)sm_lar_msb);
return (-1);
}
/*
* Now to LSB portion.
*/
return (-1);
bus);
PR_XF("%s:LSB: (bd=%d, bs=%d) csr_addr = 0x%llx\n",
PR_XF("%s:LSB: sm_lar[15:0] = 0x%x\n",
for (cnt = 0;
cnt++)
;
if (cnt == 10) {
"IDN: 501: failed to write sm_lar (lsb) (0x%x)",
(uint_t)sm_lar_lsb);
return (-1);
}
return (0);
}
static int
cic_get_smmask_bit(void)
{
int board;
/*
* Now that I'm stuck on this cpu I can go look at this
* board's CIC registers.
*/
PR_XF("%s: (bd=%d) csr_addr = 0x%llx (via cpu %d)\n",
return (config1);
}
static int
{
int rv;
register int cnt;
/*
* csr_addr now points to CIC write buffer which resides
* in PC register space.
*/
/*
* Now we need to read back the data to guarantee
* it got there. Part of the PC protocol.
*/
cnt++)
;
rv = 0;
if (cnt == 10) {
"IDN: 502: unable to store data (0x%x) to "
"CIC buffer (0x%llx)",
rv = -1;
} else if (cnt >= 1) {
PR_XF("%s: MULTIPLE READS (cpu=%d) cnt = %d\n",
}
return (rv);
}
/*
* --------------------------------------------------
* Write the given MC address decoding register contents (madr) of
* the respective remote board (rboard) into all the PCs located on
* the local board (lboard).
* --------------------------------------------------
*/
static int
{
register int p, ioc;
int rv = 0;
/*
* Update the PCs for the cpus.
*/
int i;
continue;
rboard, p);
/*
* On this first iteration of updating the PC
* we need to turn off the MADR VALID bit so that
* there's no accidental usage of the entry before
* all four bytes have been updated in the PC.
*/
if (madr != 0) {
/*
* Need to clear valid bit on first
* go around.
*/
}
PR_XF("%s: write madr(0x%x) to pc_addr(0x%llx) "
"[lb=%d, rb=%d, cpu=%d]\n",
DEBUG_DELAY();
for (i = 0; i < 20; i++) {
/*
* Read back for sanity check.
*/
break;
}
if (i > 0) {
PR_XF("%s: WARNING: (1) lb=%d, rb=%d, "
"madr=0x%x (i=%d)\n",
}
"IDN: 503: (invalidate) failed to update "
"PC madr (expected 0x%x, actual 0x%x)",
rv++;
continue;
}
if (madr == 0) {
continue;
} else {
/*
* Turn the valid bit back on.
*/
}
PR_XF("%s: write madr(0x%x) to pc_addr(0x%llx) "
"[lb=%d, rb=%d, cpu=%d]\n",
DEBUG_DELAY();
for (i = 0; i < 20; i++) {
/*
* Read back for sanity check.
*/
break;
}
if (i > 0) {
PR_XF("%s: WARNING: (2) lb=%d, rb=%d, "
"madr=0x%x (i=%d)\n",
}
"IDN: 503: (validate) failed to update "
"PC madr (expected 0x%x, actual 0x%x)",
rv++;
}
}
/*
* Update the PCs for the iocs.
*/
int i;
continue;
if (madr != 0) {
/*
* Need to clear valid bit on first
* go around.
*/
}
PR_XF("%s: write madr(0x%x) to iopc_madr_addr(0x%llx) "
"[lb=%d, rb=%d, ioc=%d]\n",
DEBUG_DELAY();
for (i = 0; i < 20; i++) {
/*
* Read back for sanity check.
*/
break;
}
if (i > 0) {
PR_XF("%s: WARNING: (3) lb=%d, rb=%d, "
"madr=0x%x (i=%d)\n",
}
"IDN: 504: (invalidate) failed to update "
"IOPC madr (expected 0x%x, actual 0x%x)",
rv++;
continue;
}
if (madr == 0) {
continue;
} else {
/*
* Turn the valid bit back on.
*/
}
PR_XF("%s: write madr(0x%x) to iopc_madr_addr(0x%llx) "
"[lb=%d, rb=%d, ioc=%d]\n",
DEBUG_DELAY();
for (i = 0; i < 20; i++) {
/*
* Read back for sanity check.
*/
break;
}
if (i > 0) {
PR_XF("%s: WARNING: (4) lb=%d, rb=%d, "
"madr=0x%x (i=%d)\n",
}
"IDN: 504: (validate) failed to update "
"IOPC madr (expected 0x%x, actual 0x%x)",
rv++;
}
}
return (rv ? -1 : 0);
}
/*
* --------------------------------------------------
* Read the array of MC address decoding registers from one of the
* PCs on the local board (lboard) into the given in array (mc_adr).
* --------------------------------------------------
*/
void
{
register int p, ioc;
int brd;
break;
if (p == MAX_PROCMODS) {
/*
* Couldn't find a PC off a cpu, let's check the
* IOCs.
*/
break;
"IDN: 505: board %d missing any valid PCs",
lboard);
return;
}
p = ioc + 4;
}
/*
* pc_madr_addr = Starts at entry for board 0.
*/
/*
* It's possible our local PC may have old entries to
* AWOL domains. Only want to pay attention to PC
* entries corresponding to our boards.
*/
else
}
}
/*
* --------------------------------------------------
* Read the MC address decoding register contents for all
* possible boards and store the results in their respective
* slot in mc_adr. Keep a count of non-zero MC ADRs and
* return that.
* --------------------------------------------------
*/
void
{
int brd;
/*
* Note that each PC has a complete copy of all MC contents
* and so all we have to do is read one PC rather than
* each of the MCs in the system.
*/
*nmcadr = 0;
(*nmcadr)++;
}
static boardset_t
{
int brd;
int nbrds = 0;
*nboards = 0;
bmask = 0;
nbrds++;
}
}
return (bmask);
}
int
{
int c;
PR_PROTO("%s: NEW HW CONFIG (old_bset = 0x%x, "
"new_bset = 0x%x)\n",
for (c = 0; c < NCPU; c++) {
}
}
return (1);
} else {
return (0);
}
}
int
{
int bd;
int nmcadr;
int nboards;
/*
* See if sm_mask is writable.
* XXX - Should be the same for all CIC's. Do we
* we need to verify?
*/
if (cic_get_smmask_bit() == 0) {
/*
* If smmask is not writable, we can not allow
* IDN operations.
*/
"IDN: 506: cic sm_mask is not writeable");
return (-1);
}
/*
* Map in the post2obp structure so we can find
* valid boards and hardware asics.
*/
"IDN: 507: failed to map-in post2obp structure");
return (-1);
} else if (!pda_is_valid(ph)) {
return (-1);
}
/*
* Need to read the MC address decoding registers
* so that they can be given to other domains.
*/
/*
* There will always be a bus 0 (logical).
*/
return (-1);
}
#ifdef DEBUG
{
int brd;
PR_XF("%s: brd %d, mc = 0x%x\n",
}
}
#endif /* DEBUG */
return (-1);
else
return (0);
}
/*
* Function called via timeout() to wakeup a possibly stuck
* idnxf_shmem_update_all() should not all cpus check-in after a
* x-call to update their respective CICs.
*/
/*ARGSUSED0*/
static void
{
int count;
int expired;
IDNCIC_TIMEOUT) ? 1 : 0;
/*
* Everybody has finished. Wakeup the requester.
*/
/*
* There are still active cic updaters and time
* has expired. Bail on them.
*/
#ifdef DEBUG
/*
* Special debug case since idn_debug
* may have been temporarily cleared
* during xc_some.
*/
printf("%s: TIMEOUT...bailing on %d lost CIC "
#endif /* DEBUG */
} else {
}
}
/*
* Called indirectly from idnxf_shmem_update_all() via a xcall
* for the recepient cpu to update the CICs on its respective
* board.
* IMPORTANT: NO console output from this routine!!
*/
static void
{
/*
* Ooops! Not my place to intrude.
*/
goto done;
}
/*
* We're executing out of the context of a cross-call
* so we're effectively bound! :)
*/
/*
* XXX - need to worry about shuffle??
*/
continue;
idnxfp->xf_smlimit);
}
/*
* Verify data got there!
*/
idnxfp->xf_smlimit);
} else {
if (!smmask) {
/*
* Update the LAR first so that we effectively
* disable the register without possibly
* opening the window to transaction we
* don't care about. Updating the LAR first
* will guarantee we effectively turn it
* off immediately.
*/
} else {
}
}
if (rv) {
} else {
}
}
done:
}
static int
{
int target_count;
int rv = 0;
short abus_mask;
target_count = 0;
/*
* Build a cpuset of target cpus (one per board) to
* be used to send the CIC update xcall.
*/
/*
* Need to target an available cpu on the target board
* so that we can look at the CICs on that board.
*/
if (c == -1) {
/*
* If there's no cpu on this board, no
* need to update the CICs.
*/
continue;
}
CPUSET_ADD(target_cpuset, c);
target_count++;
}
if (CPUSET_ISNULL(target_cpuset)) {
return (0);
}
/*
* Broadcast out the CIC update request and then
* sit back and wait for dinner to arrive!
* Let's set up the global structure all the xcall
* recepients will read.
*/
start_time = ddi_get_lbolt();
/*
* Set the start time. Make sure it's different
* then the previous run.
*/
start_time++;
idnxf_cic_info.xf_errcnt = 0;
/*
* Broadcast out the xcall to do the task.
*/
#ifdef DEBUG
{
PR_REGS("%s: (start %ld) broadcasting CIC - "
"%s to cpus 0x%x.%0x\n",
}
/*
* Can't dump debug during cross-calls.
*/
idn_debug = 0;
#endif /* DEBUG */
#ifdef DEBUG
o_idn_debug = 0;
#endif /* DEBUG */
PR_REGS("%s: waiting for completion of %d CIC - %s...\n",
PR_REGS("%s: CIC - %s have checked IN.\n",
/*
* Modifying xf_start_time effectively disables any
* possible outstanding xcall's since they don't touch
* idnxf_cic_info unless their given start_time matches
* that in the idnxf_cic_info structure.
*/
PR_REGS("%s: xf_errcnt = %d, xf_errtimer = %d\n",
/*
* Should errors be fatal? (panic).
*/
rv = 0;
for (c = 0; c < NCPU; c++) {
if (!CPU_IN_SET(target_cpuset, c))
continue;
brd = CPUID_TO_BOARDID(c);
continue;
case IDNCIC_UNKNOWN:
/*
* Unknown is only an error if the
* timer expired.
*/
if (!idnxf_cic_info.xf_errtimer)
break;
"IDN: 509: CPU %d never responded "
"to CIC update", c);
/*FALLTHROUGH*/
case IDNCIC_ERR:
"IDN: 510: failed write-smregs "
"(bd=%d, bs=%d, sm(bar=0x%x, "
"lar=0x%x))",
rv++;
break;
case IDNCIC_BUSY:
"(cpu=%d, bd=%d) time conflict",
c, brd);
/*
* Should never occur. Not fatal,
* just continue.
*/
break;
default:
PR_REGS("%s: board %d, bus %d "
"(bar=0x%x,lar=0x%x) - update OK\n",
break;
}
}
}
return (rv ? -1 : 0);
}
/*
* domain's hardware configuration with respect to the SMR.
*
* is_master Indicates remote domain is a master.
*/
int
{
int rv = 0;
if (pfnbase != PFN_INVALID) {
} else {
}
PR_REGS("%s: is_master=%d, boardset=0x%x, smbase=0x%x, smlimit=%x\n",
/*
* Need to serialize hardware access so we don't have multiple
* threads attempting to access hardware regs simulataneously.
* This should not be a significant performance penalty since
* the hardware is only touched when domains are linking or
* unlinking.
*/
/*
* Map in the post2obp structure so we can find
* bus config information.
*/
"IDN: 507: failed to map-in post2obp structure");
rv = -1;
goto done;
} else if (!pda_is_valid(ph)) {
rv = -1;
goto done;
}
/*
* Take a checkpoint in bbsram for diagnostic purposes.
*/
goto done;
/*
* If this is a slave (i.e. remote domain is_master),
* then we need to deprogram our PCs.
*/
PR_REGS("%s: updating PC regs (lboardset=0x%x, rboardset=0x%x)\n",
continue;
/*
* If this is a slave (i.e. remote domain is_master),
* then we need to program our PCs.
*/
continue;
/*
* Write the MC adr for the respective
* remote board (rbrd) into the PCs of
* the given local board (brd).
*/
"IDN: 512: failed [add] write-madr "
"(bd=%d, rbd=%d, madr=0x%x)",
rv = -1;
goto done;
}
}
}
done:
if (ph)
/*
* XXX
*
* Failure here is fatal. Disable IDN?
* NOn-zero return value will at least prevent
* linkage with domain - probably sufficient.
*/
return (rv);
}
/*
* Remove the respective boardset from the local domain's
* hardware configuration with respect to the SMR.
*
* is_master Indicates remote domain is a master.
*/
int
{
int rv = 0;
PR_REGS("%s: is_master=%d, boardset=0x%x\n",
/*
* Need to serialize hardware access so we don't have multiple
* threads attempting to access hardware regs simulataneously.
* This should not be a significant performance penalty since
* the hardware is only touched when domains are linking or
* unlinking.
*/
/*
* Map in the post2obp structure so we can find
* bus config information.
*/
"IDN: 507: failed to map-in post2obp structure");
rv = -1;
goto done;
} else if (!pda_is_valid(ph)) {
rv = -1;
goto done;
}
/*
* Take a checkpoint in bbsram for diagnostic purposes.
*/
goto done;
/*
* If this is a slave (i.e. remote domain is_master),
* then we need to deprogram our PCs.
*/
PR_REGS("%s: reseting PC regs (lboardset=0x%x, rboardset=0x%x)\n",
continue;
continue;
/*
* Clear the MC adr for the respective
* remote board (rbrd) into the PCs of
* the given local board (brd).
*/
"IDN: 512: failed [del] write-madr "
"(bd=%d, rbd=%d, madr=0x%x)",
rv = -1;
goto done;
}
}
}
done:
if (ph)
return (rv);
}
/*
* We cannot cross-trap cpu_flush_ecache since it references
* %g7 via CPU. It's possible that %g7 may not be set up
* when the trap comes in, and could thus cause a crash.
* Well...at least that's what has been happening when I
* tried x-calls within an xc_attention (KMISS) panic.
* Instead we use cross-calls. However, since we can't
* xc_attention around a cross-call, we have not guaranteed
* way of knowing the operation succeeded. To synchronize
* this flush operation across cpus, we use a semaphore
* which is V'd by the receiving cpus and P'd by the caller
* initiating the all-cpus flush.
*/
/*ARGSUSED0*/
void
{
extern void cpu_flush_ecache(void);
/*
* Paranoia...Give things a chance to drain.
*/
}
/*
* Flush the ecache's of all the cpus within this domain of
* any possible SMR references.
* This logic is borrowed from ecc.c:cpu_flush_ecache().
*/
void
{
/*
* We tell each cpu to do a flush and then we hit
* a semaphore to synchronize with all of them
* to guarantee they have completed the flush before
* we continue on. We have to do this type of
* sychronization since we can't xc_attention around
* a cross-call.
*/
xc_all(idn_flush_ecache, 0, 0);
}
/*
* --------------------------------------------------
*/
static int
{
int rv = 0;
"IDN: 513: sm-mask error "
"(expected = 0x%x, actual = 0x%x)",
rv++;
}
}
"IDN: 514: sm-base error "
"(expected = 0x%x, actual = 0x%x)",
rv++;
}
}
"IDN: 515: sm-limit error "
"(expected = 0x%x, actual = 0x%x)",
rv++;
}
}
return (rv ? -1 : 0);
}
/*
* -------------------------------------------------------------
*/
int
{
int b, err = 0;
if (!idn_check_cpu_per_board)
return (1);
/*
* Every board has at least one cpu, we're happy.
*/
return (1);
/*
* Well, not all boards had cpus. That's okay so
* long as they don't have memory also.
* Get rid of the boards that have a cpu.
*/
/*
* None of the remaining boards in the set shold have mem.
*/
err = 0;
/*
* A NULL post2obp pointer indicates we're checking
* the config of a remote domain. Since we can't
* look at the post2obp of the remote domain, we'll
* have to trust what he passed us in his config.
*/
return (0);
}
for (b = 0; b < MAX_BOARDS; b++) {
if (!BOARD_IN_SET(bset, b))
continue;
if ((lbp &&
err++;
"IDN: 516: (%s) board %d has memory, "
"but no CPUs - CPU per memory board REQUIRED",
}
}
return (err ? 0 : 1);
}