/*
 * Copyright (c) 1996 by Sun Microsystems, Inc.
 */

#pragma ident	"@(#)idn_xf.c	1.8	99/10/08 SMI"

#include <sys/types.h>
#include <sys/sysmacros.h>
#include <sys/open.h>
#include <sys/param.h>
#include <sys/vm_machparam.h>
#include <sys/machparam.h>
#include <sys/systm.h>
#include <sys/signal.h>
#include <sys/cred.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/uio.h>
#include <sys/buf.h>
#include <sys/file.h>
#include <sys/kmem.h>
#include <sys/stat.h>
#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/strsubr.h>
#include <sys/poll.h>
#include <sys/debug.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/errno.h>
#include <sys/modctl.h>
#include <sys/x_call.h>
#include <sys/cpuvar.h>
#include <sys/machcpuvar.h>
#include <sys/machsystm.h>

#include <sys/pda.h>
#include <sys/starfire.h>
#include <sys/idn.h>
#include <sys/idn_xf.h>

kmutex_t	idn_xf_mutex;		/* to serialize hardware access */
/*
 * 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_TIMEOUT		(30*hz)
#define	IDNCIC_TIMECHK		(hz/3)
#define	IDNCIC_UNKNOWN		0
#define	IDNCIC_OK		1	/* values for xf_errcic */
#define	IDNCIC_ERR		2
#define	IDNCIC_BUSY		3

#ifdef DEBUG
#define	NCICREGS		3	/* smmask, smbar, smlar */
#define	CICREG_SMMASK		0
#define	CICREG_SMBAR		1
#define	CICREG_SMLAR		2

#define	RESET_CIC_HISTORY() \
		(xf_cicboards = xf_cicbuses = 0, \
		bzero(xf_cicregs, sizeof (xf_cicregs)))

#define	UPDATE_CIC_HISTORY(reg, brd, bus, val) \
		(BOARDSET_ADD(xf_cicboards, (brd)), \
		BOARDSET_ADD(xf_cicbuses, (bus)), \
		xf_cicregs[brd][bus][reg] = (val))

#define	DUMP_CIC_HISTORY() \
{ \
	if (idn_debug & IDNDBG_XF) { \
		int	_bd, _bs; \
		procname_t	_proc = "dump_cic_history"; \
		for (_bd = 0; _bd < MAX_BOARDS; _bd++) { \
			if (!BOARD_IN_SET(xf_cicboards, _bd)) \
				continue; \
			for (_bs = 0; _bs < MAX_ABUSES; _bs++) { \
				if (!BOARD_IN_SET(xf_cicbuses, _bs)) \
					continue; \
				printf("%s: (bd.bs = %d.%d) m/b/l = " \
					"%x/%x/%x\n", _proc, _bd, _bs, \
					xf_cicregs[_bd][_bs][CICREG_SMMASK], \
					xf_cicregs[_bd][_bs][CICREG_SMBAR], \
					xf_cicregs[_bd][_bs][CICREG_SMLAR]); \
			} \
		} \
		DEBUG_DELAY(); \
	} \
}

/*
 * Globally updated during CIC reg updates.  Everybody has
 * a unique location, so no concern about updates stepping
 * on each other.
 */
static ushort_t	xf_cicboards, xf_cicbuses;
static uint_t	xf_cicregs[MAX_BOARDS][MAX_ABUSES][NCICREGS];
#else /* DEBUG */
#define	RESET_CIC_HISTORY()
#define	UPDATE_CIC_HISTORY(reg, brd, bus, val)
#define	DUMP_CIC_HISTORY()
#endif /* DEBUG */

struct idnxf_cic_info {			/* protected by idn_xf_mutex */
/*  0 */	short		xf_abus_mask;
/*  2 */	boardset_t	xf_boardset;
/*  4 */	uint_t		xf_smbase;
/*  8 */	uint_t		xf_smlimit;
/*  c */	int		xf_doadd;

/* 10 */	int		xf_count;	/* atomically updated */
/* 14 */	time_t		xf_start_time;
/* 18 */	kcondvar_t	xf_cv;
/* 1a */	short		xf_errtimer;
/* 1c */	int		xf_errcnt;	/* atomically updated */

/* 20 */	uchar_t		xf_errcic[MAX_BOARDS][MAX_ABUSES];

/* 60 */	kmutex_t	xf_mutex;
};	/* sizeof = 0x68 = 104 (26X) */

static struct idnxf_cic_info	idnxf_cic_info;
#ifdef DEBUG
static uint_t			o_idn_debug;
#endif /* DEBUG */

int	idn_check_cpu_per_board = 1;

static int	pc_prep_cic_buffer(int cpuid, uint_t cicdata);
static int	cic_write_sm_mask(int board, int bus, boardset_t sm_mask);
static int	cic_write_sm_bar(int board, int bus, uint_t sm_bar);
static int	cic_write_sm_lar(int board, int bus, uint_t sm_lar);
static int	cic_get_smmask_bit(void);
static int	pc_write_madr(pda_handle_t ph,
				int lboard, int rboard, uint_t madr);
static boardset_t	get_boardset(pda_handle_t ph, int *nboards);
static int	verify_smregs(int brd, int bus, boardset_t smmask,
				uint_t smbase, uint_t smlimit);
static void	idnxf_shmem_wakeup(void *arg);
static void	idnxf_shmem_update_one(uint64_t arg1, uint64_t arg2);
static int	idnxf_shmem_update_all(pda_handle_t ph,
				boardset_t boardset, uint_t smbase,
				uint_t smlimit, int doadd);

#define	PHYSIO_ST(paddr, val)		(stphysio((paddr), (val)))
#define	PHYSIO_LD(paddr)		(ldphysio(paddr))
#define	PHYSIO_STH(paddr, val)		(sthphysio((paddr), (val)))
#define	PHYSIO_LDH(paddr)		(ldhphysio(paddr))

#ifdef DEBUG
#define	DEBUG_DELAY() 	(drv_usecwait(5000))	/* 5 ms */
#else /* DEBUG */
#define	DEBUG_DELAY()
#endif /* DEBUG */


/*
 * ---------------------------------------------------------------------
 */
boardset_t
cic_read_domain_mask(int board, int bus)
{
	u_longlong_t	csr_addr;
	boardset_t	domain_mask;
	procname_t	proc = "cic_read_domain_mask";

	ASSERT(CPUID_TO_BOARDID(CPU->cpu_id) == board);

	csr_addr = MAKE_CIC_CSR_PA(board,
				    CSR_TYPE_CIC,
				    CIC_DOMAIN_MASK_ADDR,
				    bus);
	PR_XF("%s: (bd=%d, bs=%d) csr_addr = 0x%llx\n",
			proc, board, bus, csr_addr);

	domain_mask = (boardset_t)PHYSIO_LDH(csr_addr);

	return (domain_mask);
}

boardset_t
cic_read_sm_mask(int board, int bus)
{
	u_longlong_t	csr_addr;
	boardset_t	sm_mask;
	procname_t	proc = "cic_read_sm_mask";

	ASSERT(CPUID_TO_BOARDID(CPU->cpu_id) == board);

	csr_addr = MAKE_CIC_CSR_PA(board,
				    CSR_TYPE_CIC,
				    CIC_SM_MASK_ADDR,
				    bus);
	PR_XF("%s: (bd=%d, bs=%d) csr_addr = 0x%llx\n",
			proc, board, bus, csr_addr);

	sm_mask = (boardset_t)PHYSIO_LDH(csr_addr);

	return (sm_mask);
}

static int
cic_write_sm_mask(int board, int bus, boardset_t sm_mask)
{
	u_longlong_t	csr_addr;
	int		cnt;
	procname_t	proc = "cic_write_sm_mask";

	ASSERT(CPUID_TO_BOARDID(CPU->cpu_id) == board);

	sm_mask &= 0xffff;
	/*
	 * Before we can write to the CIC, we need to set
	 * up the CIC write data buffer in the PC.
	 */
	if (pc_prep_cic_buffer(CPU->cpu_id, (uint_t)sm_mask) < 0)
		return (-1);

	/*
	 * Now we can write to the CIC.
	 */
	csr_addr = MAKE_CIC_CSR_PA(board,
				    CSR_TYPE_CIC,
				    CIC_SM_MASK_ADDR,
				    bus);
	PR_XF("%s: (bd=%d, bs=%d) csr_addr = 0x%llx\n",
		proc, board, bus, csr_addr);
	PR_XF("%s: writing sm_mask = 0x%x\n",
		proc, (ushort_t)sm_mask);

	UPDATE_CIC_HISTORY(CICREG_SMMASK, board, bus, sm_mask);

	PHYSIO_STH(csr_addr, (ushort_t)sm_mask);
	/*
	 * Read back for verification.
	 */
	for (cnt = 0; (PHYSIO_LDH(csr_addr) != sm_mask) && (cnt < 10); cnt++)
		;

	return ((cnt == 10) ? -1 : 0);
}

uint_t
cic_read_sm_bar(int board, int bus)
{
	u_longlong_t	csr_addr;
	uint_t		sm_bar;
	procname_t	proc = "cic_read_sm_bar";

	ASSERT(CPUID_TO_BOARDID(CPU->cpu_id) == board);

	csr_addr = MAKE_CIC_CSR_PA(board,
				    CSR_TYPE_CIC,
				    CIC_SM_BAR_MSB_ADDR,
				    bus);
	PR_XF("%s:MSB: (bd=%d, bs=%d) csr_addr = 0x%llx\n",
		proc, board, bus, csr_addr);

	sm_bar = (uint_t)PHYSIO_LDH(csr_addr);
	sm_bar <<= 16;

	csr_addr = MAKE_CIC_CSR_PA(board,
				    CSR_TYPE_CIC,
				    CIC_SM_BAR_LSB_ADDR,
				    bus);
	PR_XF("%s:LSB: (bd=%d, bs=%d) csr_addr = 0x%llx\n",
		proc, board, bus, csr_addr);

	sm_bar |= (uint_t)PHYSIO_LDH(csr_addr);

	return (sm_bar);
}

static int
cic_write_sm_bar(int board, int bus, uint_t sm_bar)
{
	int		cnt;
	u_longlong_t	csr_addr;
	uint_t		sm_bar_lsb, sm_bar_msb;
	procname_t	proc = "cic_write_sm_bar";

	ASSERT(CPUID_TO_BOARDID(CPU->cpu_id) == board);

	sm_bar_lsb = sm_bar & 0xffff;
	sm_bar_msb = (sm_bar >> 16) & 0xffff;

	/*
	 * Before we can write to the CIC, we need to set
	 * up the CIC write data buffer in the PC.
	 */
	if (pc_prep_cic_buffer(CPU->cpu_id, sm_bar_msb) < 0)
		return (-1);

	csr_addr = MAKE_CIC_CSR_PA(board,
				    CSR_TYPE_CIC,
				    CIC_SM_BAR_MSB_ADDR,
				    bus);
	PR_XF("%s:MSB: (bd=%d, bs=%d) csr_addr = 0x%llx\n",
		proc, board, bus, csr_addr);
	PR_XF("%s:MSB: sm_bar[31:16] = 0x%x\n",
		proc, (ushort_t)sm_bar_msb);

	UPDATE_CIC_HISTORY(CICREG_SMBAR, board, bus, sm_bar);

	PHYSIO_STH(csr_addr, (ushort_t)sm_bar_msb);
	for (cnt = 0;
	    ((uint_t)PHYSIO_LDH(csr_addr) != sm_bar_msb) && (cnt < 10);
	    cnt++)
		;
	if (cnt == 10) {
		cmn_err(CE_WARN,
			"IDN: 500: failed to write sm_bar (msb) (0x%x)",
			(uint_t)sm_bar_msb);
		return (-1);
	}

	/*
	 * Now to LSB portion.
	 */
	if (pc_prep_cic_buffer(CPU->cpu_id, sm_bar_lsb) < 0)
		return (-1);

	csr_addr = MAKE_CIC_CSR_PA(board,
				    CSR_TYPE_CIC,
				    CIC_SM_BAR_LSB_ADDR,
				    bus);
	PR_XF("%s:LSB: (bd=%d, bs=%d) csr_addr = 0x%llx\n",
		proc, board, bus, csr_addr);
	PR_XF("%s:LSB: sm_bar[15:0] = 0x%x\n",
		proc, (ushort_t)sm_bar_lsb);

	PHYSIO_STH(csr_addr, (ushort_t)sm_bar_lsb);
	for (cnt = 0;
	    ((uint_t)PHYSIO_LDH(csr_addr) != sm_bar_lsb) && (cnt < 10);
	    cnt++)
		;
	if (cnt == 10) {
		cmn_err(CE_WARN,
			"IDN: 500: failed to write sm_bar (lsb) (0x%x)",
			(uint_t)sm_bar_lsb);
		return (-1);
	}

	return (0);
}

uint_t
cic_read_sm_lar(int board, int bus)
{
	u_longlong_t	csr_addr;
	uint_t		sm_lar;
	procname_t	proc = "cic_read_sm_lar";

	ASSERT(CPUID_TO_BOARDID(CPU->cpu_id) == board);

	csr_addr = MAKE_CIC_CSR_PA(board,
				    CSR_TYPE_CIC,
				    CIC_SM_LAR_MSB_ADDR,
				    bus);
	PR_XF("%s:MSB: (bd=%d, bs=%d) csr_addr = 0x%llx\n",
		proc, board, bus, csr_addr);

	sm_lar = (uint_t)PHYSIO_LDH(csr_addr);
	sm_lar <<= 16;

	csr_addr = MAKE_CIC_CSR_PA(board,
				    CSR_TYPE_CIC,
				    CIC_SM_LAR_LSB_ADDR,
				    bus);
	PR_XF("%s:LSB: (bd=%d, bs=%d) csr_addr = 0x%llx\n",
		proc, board, bus, csr_addr);

	sm_lar |= (uint_t)PHYSIO_LDH(csr_addr);

	return (sm_lar);
}

static int
cic_write_sm_lar(int board, int bus, uint_t sm_lar)
{
	int		cnt;
	u_longlong_t	csr_addr;
	uint_t		sm_lar_lsb, sm_lar_msb;
	procname_t	proc = "cic_write_sm_lar";

	ASSERT(CPUID_TO_BOARDID(CPU->cpu_id) == board);

	sm_lar_lsb = sm_lar & 0xffff;
	sm_lar_msb = (sm_lar >> 16) & 0xffff;

	/*
	 * Before we can write to the CIC, we need to set
	 * up the CIC write data buffer in the PC.
	 */
	if (pc_prep_cic_buffer(CPU->cpu_id, sm_lar_msb) < 0)
		return (-1);

	csr_addr = MAKE_CIC_CSR_PA(board,
				    CSR_TYPE_CIC,
				    CIC_SM_LAR_MSB_ADDR,
				    bus);
	PR_XF("%s:MSB: (bd=%d, bs=%d) csr_addr = 0x%llx\n",
		proc, board, bus, csr_addr);
	PR_XF("%s:MSB: sm_lar[31:16] = 0x%x\n",
		proc, (ushort_t)sm_lar_msb);

	UPDATE_CIC_HISTORY(CICREG_SMLAR, board, bus, sm_lar);

	PHYSIO_STH(csr_addr, (ushort_t)sm_lar_msb);
	for (cnt = 0;
	    ((uint_t)PHYSIO_LDH(csr_addr) != sm_lar_msb) && (cnt < 10);
	    cnt++)
		;
	if (cnt == 10) {
		cmn_err(CE_WARN,
			"IDN: 501: failed to write sm_lar (msb) (0x%x)",
			(uint_t)sm_lar_msb);
		return (-1);
	}

	/*
	 * Now to LSB portion.
	 */
	if (pc_prep_cic_buffer(CPU->cpu_id, sm_lar_lsb) < 0)
		return (-1);

	csr_addr = MAKE_CIC_CSR_PA(board,
				    CSR_TYPE_CIC,
				    CIC_SM_LAR_LSB_ADDR,
				    bus);
	PR_XF("%s:LSB: (bd=%d, bs=%d) csr_addr = 0x%llx\n",
		proc, board, bus, csr_addr);
	PR_XF("%s:LSB: sm_lar[15:0] = 0x%x\n",
		proc, (ushort_t)sm_lar_lsb);

	PHYSIO_STH(csr_addr, (ushort_t)sm_lar_lsb);
	for (cnt = 0;
	    ((uint_t)PHYSIO_LDH(csr_addr) != sm_lar_lsb) && (cnt < 10);
	    cnt++)
		;
	if (cnt == 10) {
		cmn_err(CE_WARN,
			"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)
{
	u_longlong_t	csr_addr;
	int		board;
	uint_t		config1;
	procname_t	proc = "cic_get_smmask_bit";

	affinity_set(CPU_CURRENT);

	board = CPUID_TO_BOARDID(CPU->cpu_id);
	/*
	 * Now that I'm stuck on this cpu I can go look at this
	 * board's CIC registers.
	 */
	csr_addr = MAKE_CIC_CSR_PA(board,
				    CSR_TYPE_CIC,
				    CIC_CONFIG1_ADDR,
				    0);
	PR_XF("%s: (bd=%d) csr_addr = 0x%llx (via cpu %d)\n",
		proc, board, csr_addr, (int)CPU->cpu_id);

	config1 = (uint_t)PHYSIO_LDH(csr_addr);

	config1 = CIC_CONFIG1_SMMASK_BIT(config1);

	affinity_clear();

	return (config1);
}

static int
pc_prep_cic_buffer(int cpuid, uint_t cicdata)
{
	int		rv;
	int		brd, port;
	u_longlong_t	csr_addr;
	register int	cnt;
	procname_t	proc = "pc_prep_cic_buffer";


	ASSERT(CPU->cpu_id == cpuid);

	port = cpuid % plat_max_cpu_units_per_board();
	brd = CPUID_TO_BOARDID(cpuid);

	csr_addr = STARFIRE_PC_CICBUF_ADDR(brd, port);

	/*
	 * csr_addr now points to CIC write buffer which resides
	 * in PC register space.
	 */
	PR_XF("%s: (cpu=%d) csr_addr = 0x%llx\n", proc, cpuid, csr_addr);

	PHYSIO_ST(csr_addr, cicdata);

	/*
	 * Now we need to read back the data to guarantee
	 * it got there.  Part of the PC protocol.
	 */
	for (cnt = 0; (PHYSIO_LD(csr_addr) != cicdata) && (cnt < 10);
	    cnt++)
		;

	rv = 0;
	if (cnt == 10) {
		cmn_err(CE_WARN,
			"IDN: 502: unable to store data (0x%x) to "
			"CIC buffer (0x%llx)",
			cicdata, csr_addr);
		rv = -1;
	} else if (cnt >= 1) {
		PR_XF("%s: MULTIPLE READS (cpu=%d) cnt = %d\n",
			proc, cpuid, cnt);
	}

	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
pc_write_madr(pda_handle_t ph, int lboard, int rboard, uint_t madr)
{
	u_longlong_t	pc_madr_addr;
	register int	p, ioc;
	register ushort_t	procset, iocset;
	int		rv = 0;
	uint_t		rd_madr;
	board_desc_t	*lbp;
	procname_t	proc = "pc_write_madr";

	lbp = pda_get_board_info(ph, lboard);

	ASSERT(lbp);
	ASSERT((lbp->bda_board & BDAN_MASK) == BDAN_GOOD);

	procset = lbp->bda_proc;
	iocset  = lbp->bda_ioc;

	/*
	 * Update the PCs for the cpus.
	 */
	for (p = 0; p < MAX_PROCMODS; procset >>= 4, p++) {
		int	i;

		if (!((procset & BDAN_MASK) == BDAN_GOOD))
			continue;

		pc_madr_addr = (u_longlong_t)STARFIRE_PC_MADR_ADDR(lboard,
							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.
			 */
			madr &= ~STARFIRE_PC_MADR_VALIDBIT;
		}
		PR_XF("%s: write madr(0x%x) to pc_addr(0x%llx) "
			"[lb=%d, rb=%d, cpu=%d]\n",
			proc, madr, pc_madr_addr, lboard, rboard, p);
		DEBUG_DELAY();

		for (i = 0; i < 20; i++) {
			PHYSIO_ST(pc_madr_addr, madr);
			/*
			 * Read back for sanity check.
			 */
			rd_madr = PHYSIO_LD(pc_madr_addr);
			if (madr == rd_madr)
				break;
		}
		if (i > 0) {
			PR_XF("%s: WARNING: (1) lb=%d, rb=%d, "
				"madr=0x%x (i=%d)\n",
				proc, lboard, rboard, madr, i);
		}
		if (rd_madr != madr) {
			cmn_err(CE_WARN,
				"IDN: 503: (invalidate) failed to update "
				"PC madr (expected 0x%x, actual 0x%x)",
				madr, rd_madr);
			rv++;
			continue;
		}
		if (madr == 0) {
			continue;
		} else {
			/*
			 * Turn the valid bit back on.
			 */
			madr |= STARFIRE_PC_MADR_VALIDBIT;
		}
		PR_XF("%s: write madr(0x%x) to pc_addr(0x%llx) "
			"[lb=%d, rb=%d, cpu=%d]\n",
			proc, madr, pc_madr_addr, lboard, rboard, p);
		DEBUG_DELAY();

		for (i = 0; i < 20; i++) {
			PHYSIO_ST(pc_madr_addr, madr);
			/*
			 * Read back for sanity check.
			 */
			rd_madr = PHYSIO_LD(pc_madr_addr);
			if (madr == rd_madr)
				break;
		}
		if (i > 0) {
			PR_XF("%s: WARNING: (2) lb=%d, rb=%d, "
				"madr=0x%x (i=%d)\n",
				proc, lboard, rboard, madr, i);
		}
		if (rd_madr != madr) {
			cmn_err(CE_WARN,
				"IDN: 503: (validate) failed to update "
				"PC madr (expected 0x%x, actual 0x%x)",
				madr, rd_madr);
			rv++;
		}
	}
	/*
	 * Update the PCs for the iocs.
	 */
	for (ioc = 0; ioc < MAX_IOCS; iocset >>= 4, ioc++) {
		int	i;

		if (!((iocset & BDAN_MASK) == BDAN_GOOD))
			continue;

		pc_madr_addr = (u_longlong_t)STARFIRE_PC_MADR_ADDR(lboard,
							rboard, ioc + 4);

		if (madr != 0) {
			/*
			 * Need to clear valid bit on first
			 * go around.
			 */
			madr &= ~STARFIRE_PC_MADR_VALIDBIT;
		}
		PR_XF("%s: write madr(0x%x) to iopc_madr_addr(0x%llx) "
			"[lb=%d, rb=%d, ioc=%d]\n",
			proc, madr, pc_madr_addr, lboard, rboard, ioc);
		DEBUG_DELAY();

		for (i = 0; i < 20; i++) {
			PHYSIO_ST(pc_madr_addr, madr);
			/*
			 * Read back for sanity check.
			 */
			rd_madr = PHYSIO_LD(pc_madr_addr);
			if (madr == rd_madr)
				break;
		}
		if (i > 0) {
			PR_XF("%s: WARNING: (3) lb=%d, rb=%d, "
				"madr=0x%x (i=%d)\n",
				proc, lboard, rboard, madr, i);
		}
		if (rd_madr != madr) {
			cmn_err(CE_WARN,
				"IDN: 504: (invalidate) failed to update "
				"IOPC madr (expected 0x%x, actual 0x%x)",
				madr, rd_madr);
			rv++;
			continue;
		}

		if (madr == 0) {
			continue;
		} else {
			/*
			 * Turn the valid bit back on.
			 */
			madr |= STARFIRE_PC_MADR_VALIDBIT;
		}

		PR_XF("%s: write madr(0x%x) to iopc_madr_addr(0x%llx) "
			"[lb=%d, rb=%d, ioc=%d]\n",
			proc, madr, pc_madr_addr, lboard, rboard, ioc);
		DEBUG_DELAY();

		for (i = 0; i < 20; i++) {
			PHYSIO_ST(pc_madr_addr, madr);
			/*
			 * Read back for sanity check.
			 */
			rd_madr = PHYSIO_LD(pc_madr_addr);
			if (madr == rd_madr)
				break;
		}
		if (i > 0) {
			PR_XF("%s: WARNING: (4) lb=%d, rb=%d, "
				"madr=0x%x (i=%d)\n",
				proc, lboard, rboard, madr, i);
		}
		if (rd_madr != madr) {
			cmn_err(CE_WARN,
				"IDN: 504: (validate) failed to update "
				"IOPC madr (expected 0x%x, actual 0x%x)",
				madr, rd_madr);
			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
pc_read_madr(pda_handle_t ph, int lboard, uint_t mc_adr[], int local_only)
{
	u_longlong_t	pc_madr_addr;
	register int	p, ioc;
	register ushort_t	procset, iocset;
	int		brd;
	board_desc_t	*lbp;

	lbp = pda_get_board_info(ph, lboard);

	ASSERT(lbp);
	ASSERT((lbp->bda_board & BDAN_MASK) == BDAN_GOOD);

	procset = lbp->bda_proc;
	iocset = lbp->bda_ioc;

	for (p = 0; p < MAX_PROCMODS; procset >>= 4, p++)
		if ((procset & BDAN_MASK) == BDAN_GOOD)
			break;

	if (p == MAX_PROCMODS) {
		/*
		 * Couldn't find a PC off a cpu, let's check the
		 * IOCs.
		 */
		for (ioc = 0; ioc < MAX_IOCS; iocset >>= 4, ioc++)
			if ((iocset & BDAN_MASK) == BDAN_GOOD)
				break;
		if (ioc == MAX_IOCS) {
			cmn_err(CE_WARN,
				"IDN: 505: board %d missing any valid PCs",
				lboard);
			return;
		}
		p = ioc + 4;
	}

	pc_madr_addr = (u_longlong_t)STARFIRE_PC_MADR_ADDR(lboard, 0, p);
	/*
	 * pc_madr_addr = Starts at entry for board 0.
	 */
	for (brd = 0; brd < MAX_BOARDS; brd++) {
		/*
		 * 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.
		 */
		lbp = pda_get_board_info(ph, brd);
		if (!local_only ||
				((lbp->bda_board & BDAN_MASK) == BDAN_GOOD))
			mc_adr[brd] = PHYSIO_LD(pc_madr_addr);
		else
			mc_adr[brd] = 0;

		pc_madr_addr += ((u_longlong_t)1 <<
					STARFIRE_PC_MADR_BOARD_SHIFT);
	}
}

/*
 * --------------------------------------------------
 * 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
mc_get_adr_all(pda_handle_t ph, uint_t mc_adr[], int *nmcadr)
{
	int	brd;
	uint_t	madr[MAX_BOARDS];

	/*
	 * 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.
	 */
	brd = CPUID_TO_BOARDID(CPU->cpu_id);
	pc_read_madr(ph, brd, madr, 1);

	*nmcadr = 0;
	for (brd = 0; brd < MAX_BOARDS; brd++)
		if ((mc_adr[brd] = madr[brd]) != 0)
			(*nmcadr)++;
}

static boardset_t
get_boardset(pda_handle_t ph, int *nboards)
{
	int		brd;
	int		nbrds = 0;
	boardset_t	bmask;

	if (nboards != NULL)
		*nboards = 0;

	bmask = 0;
	for (brd = 0; brd < MAX_BOARDS; brd++) {
		if (pda_board_present(ph, brd)) {
			bmask |= 1 << brd;
			nbrds++;
		}
	}
	if (nboards != NULL)
		*nboards = (short)nbrds;

	return (bmask);
}

int
update_local_hw_config(idn_domain_t *ldp, struct hwconfig *loc_hw)
{
	procname_t	proc = "update_local_hw_config";

	ASSERT(IDN_DLOCK_IS_EXCL(ldp->domid));
	ASSERT(IDN_GLOCK_IS_EXCL());
	ASSERT(ldp == &idn_domain[idn.localid]);

	if (ldp->dhw.dh_boardset != loc_hw->dh_boardset) {
		int	c;

		PR_PROTO("%s: NEW HW CONFIG (old_bset = 0x%x, "
			"new_bset = 0x%x)\n",
			proc, ldp->dhw.dh_boardset, loc_hw->dh_boardset);

		PR_PROTO("%s: clearing boardset 0x%x\n", proc,
			ldp->dhw.dh_boardset & ~loc_hw->dh_boardset);
		PR_PROTO("%s: setting boardset  0x%x\n", proc,
			loc_hw->dh_boardset & ~ldp->dhw.dh_boardset);

		idn.dc_boardset &= ~ldp->dhw.dh_boardset;
		idn.dc_boardset |= loc_hw->dh_boardset;
		for (c = 0; c < NCPU; c++) {
			if (CPU_IN_SET(ldp->dcpuset, c)) {
				CPUSET_DEL(idn.dc_cpuset, c);
			}
		}
		CPUSET_OR(idn.dc_cpuset, cpu_ready_set);

		bcopy(loc_hw, &ldp->dhw, sizeof (ldp->dhw));
		ldp->dcpuset = cpu_ready_set;
		ldp->dcpu    = cpu0.cpu_id;
		ldp->dncpus  = (int)ncpus;
		ldp->dvote.v.nmembrds = ldp->dhw.dh_nmcadr - 1;
		ldp->dvote.v.ncpus    = (int)ldp->dncpus - 1;
		ldp->dvote.v.board    = CPUID_TO_BOARDID(CPU->cpu_id);

		return (1);
	} else {
		PR_PROTO("%s: NO change detected\n", proc);
		return (0);
	}
}

int
get_hw_config(struct hwconfig *loc_hw)
{
	pda_handle_t	ph;
	boardset_t	domainset;
	int		bd;
	int		nmcadr;
	int		nboards;
	procname_t	proc = "get_hw_config";

	ASSERT(loc_hw != NULL);

	bzero(loc_hw, sizeof (*loc_hw));
	/*
	 * 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.
		 */
		cmn_err(CE_WARN,
			"IDN: 506: cic sm_mask is not writeable");
		return (-1);
	}
	/*
	 * Map in the post2obp structure so we can find
	 * valid boards and hardware asics.
	 */
	ph = pda_open();
	if (ph == (pda_handle_t)NULL) {
		cmn_err(CE_WARN,
			"IDN: 507: failed to map-in post2obp structure");
		return (-1);
	} else if (!pda_is_valid(ph)) {
		cmn_err(CE_WARN, "IDN: 508: post2obp checksum invalid");
		pda_close(ph);
		return (-1);
	}
	/*
	 * Need to read the MC address decoding registers
	 * so that they can be given to other domains.
	 */
	loc_hw->dh_boardset = get_boardset(ph, &nboards);
	loc_hw->dh_nboards = (short)nboards;
	ASSERT(loc_hw->dh_boardset & (1 << CPUID_TO_BOARDID(CPU->cpu_id)));

	mc_get_adr_all(ph, loc_hw->dh_mcadr, &nmcadr);
	loc_hw->dh_nmcadr = (short)nmcadr;

	affinity_set(CPU_CURRENT);
	/*
	 * There will always be a bus 0 (logical).
	 */
	bd = CPUID_TO_BOARDID(CPU->cpu_id);
	domainset = (boardset_t)cic_read_domain_mask(bd, 0);
	affinity_clear();

	if (!idn_cpu_per_board(ph, cpu_ready_set, loc_hw)) {
		pda_close(ph);
		return (-1);
	}

	pda_close(ph);


#ifdef DEBUG
	{
		int	brd;

		for (brd = 0; brd < MAX_BOARDS; brd++)
			if (loc_hw->dh_mcadr[brd] != 0) {
				PR_XF("%s: brd %d, mc = 0x%x\n",
					proc, brd, loc_hw->dh_mcadr[brd]);
			}
	}
#endif /* DEBUG */

	if ((loc_hw->dh_boardset != domainset) || (loc_hw->dh_nmcadr < 1))
		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
idnxf_shmem_wakeup(void *arg)
{
	struct idnxf_cic_info	*idnxfp = (struct idnxf_cic_info *)arg;
	int		count;
	int		expired;
	procname_t	proc = "idnxf_shmem_wakeup";

	expired = ((lbolt - idnxfp->xf_start_time) >= IDNCIC_TIMEOUT) ? 1 : 0;

	if ((count = idnxfp->xf_count) == 0) {
		/*
		 * Everybody has finished.  Wakeup the requester.
		 */
		mutex_enter(&idnxfp->xf_mutex);
		cv_signal(&idnxfp->xf_cv);
		mutex_exit(&idnxfp->xf_mutex);

	} else if ((count > 0) && expired) {
		/*
		 * There are still active cic updaters and time
		 * has expired.  Bail on them.
		 */
		idnxfp->xf_errtimer = 1;
#ifdef DEBUG
		/*
		 * Special debug case since idn_debug
		 * may have been temporarily cleared
		 * during xc_some.
		 */
		if ((idn_debug | o_idn_debug) & IDNDBG_REGS)
			printf("%s: TIMEOUT...bailing on %d lost CIC "
				"updates...\n", proc, count);
#endif /* DEBUG */

		ATOMIC_SUB(idnxfp->xf_count, count);

		mutex_enter(&idnxfp->xf_mutex);
		cv_signal(&idnxfp->xf_cv);
		mutex_exit(&idnxfp->xf_mutex);

	} else {
		(void) timeout(idnxf_shmem_wakeup, (caddr_t)idnxfp,
						(clock_t)IDNCIC_TIMECHK);
	}
}

/*
 * 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
idnxf_shmem_update_one(uint64_t arg1, uint64_t arg2)
{
	struct idnxf_cic_info	*idnxfp = (struct idnxf_cic_info *)arg1;
	time_t		start_time = (time_t)arg2;
	int		rv, cpuid, brd, bus;
	boardset_t	smmask;


	cpuid = CPU->cpu_id;
	brd   = CPUID_TO_BOARDID(cpuid);

	if (idnxfp->xf_start_time != start_time) {
		/*
		 * Ooops!  Not my place to intrude.
		 */
		idnxf_cic_info.xf_errcic[brd][0] = IDNCIC_BUSY;
		ATOMIC_INC(idnxf_cic_info.xf_errcnt);

		goto done;
	}

	/*
	 * We're executing out of the context of a cross-call
	 * so we're effectively bound! :)
	 */
	for (bus = 0; bus < MAX_ABUSES; bus++) {
		/*
		 * XXX - need to worry about shuffle??
		 */
		if (!(idnxfp->xf_abus_mask & (1 << bus)))
			continue;
		smmask = cic_read_sm_mask(brd, bus);

		if (idnxfp->xf_doadd) {
			smmask |= idnxfp->xf_boardset;
			(void) cic_write_sm_mask(brd, bus, smmask);

			if (idnxfp->xf_smbase != (uint_t)-1) {
				(void) cic_write_sm_bar(brd, bus,
							idnxfp->xf_smbase);
				(void) cic_write_sm_lar(brd, bus,
							idnxfp->xf_smlimit);
			}
			/*
			 * Verify data got there!
			 */
			rv = verify_smregs(brd, bus, smmask, idnxfp->xf_smbase,
							idnxfp->xf_smlimit);
		} else {
			smmask &= ~idnxfp->xf_boardset;
			(void) cic_write_sm_mask(brd, bus, smmask);

			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.
				 */
				(void) cic_write_sm_lar(brd, bus, 0);
				(void) cic_write_sm_bar(brd, bus, 1);

				rv = verify_smregs(brd, bus, smmask, 1, 0);
			} else {
				rv = verify_smregs(brd, bus, smmask,
							(uint_t)-1, (uint_t)-1);
			}
		}
		if (rv) {
			idnxf_cic_info.xf_errcic[brd][bus] = IDNCIC_ERR;
			ATOMIC_INC(idnxf_cic_info.xf_errcnt);
		} else {
			idnxf_cic_info.xf_errcic[brd][bus] = IDNCIC_OK;
		}
	}

done:
	ATOMIC_DEC(idnxf_cic_info.xf_count);
}

static int
idnxf_shmem_update_all(pda_handle_t ph, boardset_t boardset,
			uint_t smbase, uint_t smlimit, int doadd)
{
	cpuset_t	target_cpuset;
	int		target_count;
	int		rv = 0;
	int		c, brd, bus;
	short		abus_mask;
	time_t		start_time;
	procname_t	proc = "idnxf_shmem_update_all";


	ASSERT(MUTEX_HELD(&idn_xf_mutex));

	pda_get_busmask(ph, &abus_mask, NULL);

	CPUSET_ZERO(target_cpuset);
	target_count = 0;
	/*
	 * Build a cpuset of target cpus (one per board) to
	 * be used to send the CIC update xcall.
	 */
	for (brd = 0; brd < MAX_BOARDS; brd++) {
		/*
		 * Need to target an available cpu on the target board
		 * so that we can look at the CICs on that board.
		 */
		c = board_to_ready_cpu(brd, cpu_ready_set);

		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)) {
		PR_REGS("%s: NO target cpus to update!!\n", proc);
		return (0);
	}

	RESET_CIC_HISTORY();

	/*
	 * 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 = lbolt;
	/*
	 * Set the start time.  Make sure it's different
	 * then the previous run.
	 */
	if (start_time <= idnxf_cic_info.xf_start_time)
		start_time++;
	idnxf_cic_info.xf_start_time = start_time;

	idnxf_cic_info.xf_abus_mask = abus_mask;
	idnxf_cic_info.xf_boardset = boardset;
	idnxf_cic_info.xf_smbase = smbase;
	idnxf_cic_info.xf_smlimit = smlimit;
	idnxf_cic_info.xf_doadd = doadd;
	idnxf_cic_info.xf_count = target_count;
	idnxf_cic_info.xf_errcnt = 0;
	idnxf_cic_info.xf_errtimer = 0;
	bzero(&idnxf_cic_info.xf_errcic, sizeof (idnxf_cic_info.xf_errcic));

	/*
	 * Broadcast out the xcall to do the task.
	 */
#ifdef DEBUG
	{
		uint_t	tu32, tl32;

		tu32 = UPPER32_CPUMASK(target_cpuset);
		tl32 = LOWER32_CPUMASK(target_cpuset);
		PR_REGS("%s: (start %ld) broadcasting CIC - "
			"%s to cpus 0x%x.%0x\n",
			proc, start_time, doadd ? "LINK" : "UNLINK",
			tu32, tl32);
	}

	/*
	 * Can't dump debug during cross-calls.
	 */
	o_idn_debug = idn_debug;
	idn_debug = 0;
#endif /* DEBUG */

	xc_attention(target_cpuset);

	xc_some(target_cpuset, idnxf_shmem_update_one,
			(uint64_t)&idnxf_cic_info, (uint64_t)start_time);

	xc_dismissed(target_cpuset);

	ASSERT(idnxf_cic_info.xf_count == 0);

#ifdef DEBUG
	idn_debug = o_idn_debug;
	o_idn_debug = 0;
#endif /* DEBUG */

	PR_REGS("%s: waiting for completion of %d CIC - %s...\n",
		proc, idnxf_cic_info.xf_count, doadd ? "LINKS" : "UNLINKS");
	PR_REGS("%s: CIC - %s have checked IN.\n",
		proc, doadd ? "LINKS" : "UNLINKS");

	/*
	 * 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.
	 */
	idnxf_cic_info.xf_start_time++;

	PR_REGS("%s: xf_errcnt = %d, xf_errtimer = %d\n",
		proc, idnxf_cic_info.xf_errcnt, idnxf_cic_info.xf_errtimer);
	DUMP_CIC_HISTORY();
	/*
	 * 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);

		for (bus = 0; bus < MAX_ABUSES; bus++) {

			if (!(abus_mask & (1 << bus)))
				continue;

			switch (idnxf_cic_info.xf_errcic[brd][bus]) {
			case IDNCIC_UNKNOWN:
				/*
				 * Unknown is only an error if the
				 * timer expired.
				 */
				if (!idnxf_cic_info.xf_errtimer)
					break;
				cmn_err(CE_WARN,
					"IDN: 509: CPU %d never responded "
					"to CIC update", c);
				/*FALLTHROUGH*/

			case IDNCIC_ERR:
				cmn_err(CE_WARN,
					"IDN: 510: failed write-smregs "
					"(bd=%d, bs=%d, sm(bar=0x%x, "
					"lar=0x%x))",
					brd, bus, smbase, smlimit);
				rv++;
				break;

			case IDNCIC_BUSY:
				cmn_err(CE_WARN, "IDN: 511: update-one "
					"(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",
					proc, brd, bus, smbase, smlimit);
				break;
			}
		}
	}

	return (rv ? -1 : 0);
}

/*
 * Add the respective boardset/base/limit/mcadr's to the local
 * domain's hardware configuration with respect to the SMR.
 *
 * is_master	Indicates remote domain is a master.
 */
int
idnxf_shmem_add(int is_master, boardset_t boardset,
		pfn_t pfnbase, pfn_t pfnlimit,
		uint_t *mcadr)
{
	int		rv = 0;
	register int	brd, rbrd;
	register boardset_t	localboardset;
	uint_t		madr;
	uint_t		smbase, smlimit;
	pda_handle_t	ph;
	procname_t	proc = "idnxf_shmem_add";


	localboardset = idn_domain[idn.localid].dhw.dh_boardset;

	ASSERT(localboardset && boardset && ((localboardset & boardset) == 0));
	ASSERT(is_master ? (pfnbase && pfnlimit && mcadr) : 1);

	if (pfnbase != PFN_INVALID) {
		smbase  = (uint_t)PFN_TO_SMADDR(pfnbase);
		smlimit = (uint_t)PFN_TO_SMADDR(pfnlimit);
	} else {
		smbase = smlimit = (uint_t)-1;
	}
	PR_REGS("%s: is_master=%d, boardset=0x%x, smbase=0x%x, smlimit=%x\n",
		proc, is_master, boardset, smbase, smlimit);

	/*
	 * 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.
	 */
	mutex_enter(&idn_xf_mutex);

	/*
	 * Map in the post2obp structure so we can find
	 * bus config information.
	 */
	ph = pda_open();
	if (ph == (pda_handle_t)NULL) {
		cmn_err(CE_WARN,
			"IDN: 507: failed to map-in post2obp structure");
		rv = -1;
		goto done;

	} else if (!pda_is_valid(ph)) {
		cmn_err(CE_WARN, "IDN: 508: post2obp checksum invalid");
		rv = -1;
		goto done;
	}
	/*
	 * Take a checkpoint in bbsram for diagnostic purposes.
	 */
	CHECKPOINT_OPENED(IDNSB_CHKPT_SMR, boardset, 1);

	rv = idnxf_shmem_update_all(ph, boardset, smbase, smlimit, 1);

	if (rv || (is_master == 0))
		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",
		proc, localboardset, boardset);

	for (brd = 0; brd < MAX_BOARDS; brd++) {

		if (!BOARD_IN_SET(localboardset, brd))
			continue;
		/*
		 * If this is a slave (i.e. remote domain is_master),
		 * then we need to program our PCs.
		 */
		for (rbrd = 0; rbrd < MAX_BOARDS; rbrd++) {

			if ((madr = mcadr[rbrd]) == 0)
				continue;

			ASSERT(BOARD_IN_SET(boardset, rbrd));
			/*
			 * Write the MC adr for the respective
			 * remote board (rbrd) into the PCs of
			 * the given local board (brd).
			 */
			if (pc_write_madr(ph, brd, rbrd, madr) < 0) {
				cmn_err(CE_WARN,
					"IDN: 512: failed [add] write-madr "
					"(bd=%d, rbd=%d, madr=0x%x)",
					brd, rbrd, madr);
				rv = -1;
				goto done;
			}
		}
	}

done:
	if (ph)
		pda_close(ph);

	mutex_exit(&idn_xf_mutex);
	/*
	 * 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
idnxf_shmem_sub(int is_master, boardset_t boardset)
{
	int		rv = 0;
	register int	brd, rbrd;
	register boardset_t	localboardset;
	pda_handle_t	ph;
	procname_t	proc = "idnxf_shmem_sub";

	localboardset = idn_domain[idn.localid].dhw.dh_boardset;

	ASSERT(localboardset && boardset && ((localboardset & boardset) == 0));

	PR_REGS("%s: is_master=%d, boardset=0x%x\n",
		proc, is_master, boardset);

	/*
	 * 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.
	 */
	mutex_enter(&idn_xf_mutex);

	/*
	 * Map in the post2obp structure so we can find
	 * bus config information.
	 */
	ph = pda_open();
	if (ph == (pda_handle_t)NULL) {
		cmn_err(CE_WARN,
			"IDN: 507: failed to map-in post2obp structure");
		rv = -1;
		goto done;

	} else if (!pda_is_valid(ph)) {
		cmn_err(CE_WARN, "IDN: 508: post2obp checksum invalid");
		rv = -1;
		goto done;
	}
	/*
	 * Take a checkpoint in bbsram for diagnostic purposes.
	 */
	CHECKPOINT_CLOSED(IDNSB_CHKPT_SMR, boardset, 2);

	rv = idnxf_shmem_update_all(ph, boardset, (uint_t)-1, (uint_t)-1, 0);

	if (rv || (is_master == 0))
		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",
		proc, localboardset, boardset);

	for (brd = 0; brd < MAX_BOARDS; brd++) {

		if (!BOARD_IN_SET(localboardset, brd))
			continue;

		for (rbrd = 0; rbrd < MAX_BOARDS; rbrd++) {

			if (!BOARD_IN_SET(boardset, rbrd))
				continue;
			/*
			 * Clear the MC adr for the respective
			 * remote board (rbrd) into the PCs of
			 * the given local board (brd).
			 */
			if (pc_write_madr(ph, brd, rbrd, 0) < 0) {
				cmn_err(CE_WARN,
					"IDN: 512: failed [del] write-madr "
					"(bd=%d, rbd=%d, madr=0x%x)",
					brd, rbrd, 0);
				rv = -1;
				goto done;
			}
		}
	}

done:
	if (ph)
		pda_close(ph);
	mutex_exit(&idn_xf_mutex);

	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
idn_flush_ecache(uint64_t arg1, uint64_t arg2)
{
	extern void	cpu_flush_ecache(void);

	cpu_flush_ecache();
	/*
	 * Paranoia...Give things a chance to drain.
	 */
	drv_usecwait(500000);	/* 500 msec */
}
/*
 * 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
idnxf_flushall_ecache()
{
	cpuset_t	local_cpuset;
	procname_t	proc = "idnxf_flushall_ecache";


	PR_XF("%s: flushing ecache (cpu_ready_set = 0x%x.%x)\n",
		proc, UPPER32_CPUMASK(cpu_ready_set),
		LOWER32_CPUMASK(cpu_ready_set));

	CHECKPOINT_CACHE_CLEAR_DEBUG(1);
	CHECKPOINT_CACHE_STEP_DEBUG(0x1, 2);

	local_cpuset = cpu_ready_set;

	xc_attention(local_cpuset);

	/*
	 * 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.
	 */
	CHECKPOINT_CACHE_STEP_DEBUG(0x2, 3);

	xc_all(idn_flush_ecache, 0, 0);

	CHECKPOINT_CACHE_STEP_DEBUG(0x4, 4);

	xc_dismissed(local_cpuset);

	CHECKPOINT_CACHE_STEP_DEBUG(0x8, 5);
}

/*
 * --------------------------------------------------
 */
static int
verify_smregs(int brd, int bus, boardset_t smmask,
		uint_t smbase, uint_t smlimit)
{
	int		rv = 0;
	uint_t		smreg;

	if (smmask != (boardset_t)-1) {
		smreg = (uint_t)cic_read_sm_mask(brd, bus);
		if (smreg != (uint_t)smmask) {
			cmn_err(CE_WARN,
				"IDN: 513: sm-mask error "
				"(expected = 0x%x, actual = 0x%x)",
				(uint_t)smmask, smreg);
			rv++;
		}
	}

	if (smbase != (uint_t)-1) {
		smreg = cic_read_sm_bar(brd, bus);
		if (smreg != smbase) {
			cmn_err(CE_WARN,
				"IDN: 514: sm-base error "
				"(expected = 0x%x, actual = 0x%x)",
				smbase, smreg);
			rv++;
		}
	}

	if (smlimit != (uint_t)-1) {
		smreg = cic_read_sm_lar(brd, bus);
		if (smreg != smlimit) {
			cmn_err(CE_WARN,
				"IDN: 515: sm-limit error "
				"(expected = 0x%x, actual = 0x%x)",
				smlimit, smreg);
			rv++;
		}
	}

	return (rv ? -1 : 0);
}

/*
 * -------------------------------------------------------------
 */
int
idn_cpu_per_board(pda_handle_t ph, cpuset_t cset, struct hwconfig *hwp)
{
	int		b, err = 0;
	boardset_t	bset, cpu_bset;
	board_desc_t	*lbp;

	if (!idn_check_cpu_per_board)
		return (1);

	bset = hwp->dh_boardset;
	CPUSET_TO_BOARDSET(cset, cpu_bset);

	/*
	 * Every board has at least one cpu, we're happy.
	 */
	if (cpu_bset == bset)
		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.
	 */
	bset &= ~cpu_bset;
	/*
	 * 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.
	 */
	if (ph && !pda_is_valid(ph)) {
		cmn_err(CE_WARN, "IDN: 508: post2obp checksum invalid");
		return (0);
	}

	for (b = 0; b < MAX_BOARDS; b++) {
		if (!BOARD_IN_SET(bset, b))
			continue;

		lbp = ph ? pda_get_board_info(ph, b) : NULL;

		if ((lbp &&
			(BDA_NBL(lbp->bda_board, BDA_MC_NBL) == BDAN_GOOD)) ||
			(!lbp && hwp->dh_mcadr[b])) {
			err++;
			cmn_err(CE_WARN,
				"IDN: 516: (%s) board %d has memory, "
				"but no CPUs - CPU per memory board REQUIRED",
				ph ? "local" : "remote", b);
		}
	}

	return (err ? 0 : 1);
}
