code:
/*	$OpenBSD: intr.c,v 1.40 2014/11/22 22:48:38 miod Exp $ */
/*	$NetBSD: intr.c,v 1.20 1997/07/29 09:42:03 fair Exp $ */

/*
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This software was developed by the Computer Systems Engineering group
 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
 * contributed to Berkeley.
 *
 * All advertising materials mentioning features or use of this software
 * must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Lawrence Berkeley Laboratory.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)intr.c	8.3 (Berkeley) 11/11/93
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <sys/malloc.h>

#include <uvm/uvm_extern.h>

#include <dev/cons.h>

#include <machine/atomic.h>
#include <machine/cpu.h>
#include <machine/ctlreg.h>
#include <machine/instr.h>
#include <machine/trap.h>

#include <sparc/sparc/cpuvar.h>

extern void raise(int, int);

void	ih_insert(struct intrhand **, struct intrhand *);
void	ih_remove(struct intrhand **, struct intrhand *);

void	strayintr(struct clockframe *);

/*
 * Stray interrupt handler.  Clear it if possible.
 * If not, and if we get 10 interrupts in 10 seconds, panic.
 */
void
strayintr(fp)
	struct clockframe *fp;
{
	static int nstray;
	static time_t straytime;
	time_t timesince;

	printf("stray interrupt ipl 0x%x pc=0x%x npc=0x%x psr=%b\n",
		fp->ipl, fp->pc, fp->npc, fp->psr, PSR_BITS);
	timesince = time_second - straytime;
	if (timesince <= 10) {
		if (++nstray > 9)
			panic("crazy interrupts");
	} else {
		straytime = time_second;
		nstray = 1;
	}
}

#if defined(SUN4M)
void	nmi_hard(void);

int	(*memerr_handler)(void);

void
nmi_hard()
{
	/*
         * A level 15 hard interrupt.
         */
	int fatal = 0;
	u_int32_t si;
	u_int afsr, afva;

	afsr = afva = 0;
	if ((*cpuinfo.get_asyncflt)(&afsr, &afva) == 0) {
		printf("Async registers (mid %d): afsr=%b; afva=0x%x%x\n",
		       cpuinfo.mid, afsr, AFSR_BITS,
		       (afsr & AFSR_AFA) >> AFSR_AFA_RSHIFT, afva);
	}

	if (cpuinfo.master == 0) {
		/*
		 * For now, just return.
		 * Should wait on damage analysis done by the master.
		 */
		return;
	}

	/*
	 * Examine pending system interrupts.
	 */
	si = *((u_int32_t *)ICR_SI_PEND);
	printf("NMI: system interrupts: %b\n", si, SINTR_BITS);

	if ((si & SINTR_M) != 0) {
		/* ECC memory error */
                if (memerr_handler != NULL)
                        fatal |= (*memerr_handler)();
        }
#ifdef notyet
        if ((si & SINTR_I) != 0) {
                /* MBus/SBus async error */
                if (sbuserr_handler != NULL)
                        fatal |= (*sbuserr_handler)();
        }
        if ((si & SINTR_V) != 0) {
                /* VME async error */
                if (vmeerr_handler != NULL)
                        fatal |= (*vmeerr_handler)();
        }
        if ((si & SINTR_ME) != 0) {
                /* Module async error */
                if (moduleerr_handler != NULL)
                        fatal |= (*moduleerr_handler)();
        }
#else
	fatal |= si & (SINTR_I | SINTR_V | SINTR_ME);
#endif

        if (fatal)
                panic("nmi");
}
#endif

/*
 * Level 15 interrupts are special, and not vectored here.
 * Only `prewired' interrupts appear here; boot-time configured devices
 * are attached via intr_establish() below.
 */
struct intrhand *intrhand[15] = {
	NULL,			/*  0 = error */
	NULL,			/*  1 = software level 1 + SBus */
	NULL,	 		/*  2 = SBus level 2 (4m: SBus L1) */
	NULL,			/*  3 = SCSI + DMA + SBus level 3 (4m: L2,lpt)*/
	NULL,			/*  4 = software level 4 (tty softint) (scsi) */
	NULL,			/*  5 = Ethernet + SBus level 4 (4m: SBus L3) */
	NULL,			/*  6 = software level 6 (not used) (4m: enet)*/
	NULL,			/*  7 = video + SBus level 5 */
	NULL,			/*  8 = SBus level 6 */
	NULL,			/*  9 = SBus level 7 */
	NULL,			/* 10 = counter 0 = clock */
	NULL,			/* 11 = floppy */
	NULL,			/* 12 = zs hardware interrupt */
	NULL,			/* 13 = audio chip */
	NULL,			/* 14 = counter 1 = profiling timer */
};

/*
 * Soft interrupts use a separate set of handler chains.
 * This is necessary since soft interrupt handlers do not return a value
 * and therefore can not be mixed with hardware interrupt handlers on a
 * shared handler chain.
 */
struct intrhand *sintrhand[15];

void
ih_insert(struct intrhand **head, struct intrhand *ih)
{
	struct intrhand **p, *q;

	/*
	 * This is O(N^2) for long chains, but chains are never long
	 * and we do want to preserve order.
	 */
	for (p = head; (q = *p) != NULL; p = &q->ih_next)
		continue;
	*p = ih;
	ih->ih_next = NULL;
}

void
ih_remove(struct intrhand **head, struct intrhand *ih)
{
	struct intrhand **p, *q;

	for (p = head; (q = *p) != ih; p = &q->ih_next)
		continue;
	if (q == NULL)
		panic("ih_remove: intrhand %p (fn %p arg %p) not found from %p",
		    ih, ih->ih_fun, ih->ih_arg, head);

	*p = q->ih_next;
	q->ih_next = NULL;
}

static int fastvec;		/* marks fast vectors (see below) */
static struct {
	int (*cb)(void *);
	void *data;
} fastvec_share[15];

extern int sparc_interrupt4m[];
extern int sparc_interrupt44c[];

/*
 * Attach an interrupt handler to the vector chain for the given level.
 * This may not be possible if it has been taken away as a fast vector.
 */
void
intr_establish(level, ih, ipl_block, name)
	int level;
	struct intrhand *ih;
	int ipl_block;
	const char *name;
{
#ifdef DIAGNOSTIC
	struct trapvec *tv;
	int displ;
#endif
	int s;

	if (ipl_block == -1)
		ipl_block = level;

#ifdef DIAGNOSTIC
	/*
	 * If the level we're supposed to block is lower than this interrupts
	 * level someone is doing something very wrong. Most likely it
	 * means that some IPL_ constant in machine/psl.h is preconfigured too
	 * low.
	 */
	if (ipl_block < level)
		panic("intr_establish: level (%d) > block (%d)", level,
		    ipl_block);
	if (ipl_block > 15)
		panic("intr_establish: strange block level: %d", ipl_block);
#endif

	/*
	 * We store the ipl pre-shifted so that we can avoid one instruction
	 * in the interrupt handlers.
	 */
	ih->ih_vec = ipl_block;
	ih->ih_ipl = (ipl_block << 8);
	if (name != NULL)
		evcount_attach(&ih->ih_count, name, &ih->ih_vec);

	s = splhigh();

	/*
	 * Check if this interrupt is already being handled by a fast trap.
	 * If so, attempt to change it back to a regular (thus) shareable
	 * trap.
	 */
	if (fastvec & (1 << level)) {
		if (fastvec_share[level].cb == NULL ||
		    (*fastvec_share[level].cb)(fastvec_share[level].data) != 0)
			panic("intr_establish: level %d interrupt tied to"
			    " unremovable fast vector", level);
	}

#ifdef DIAGNOSTIC
	/* double check for legal hardware interrupt */
	if ((level != 1 && level != 4 && level != 6) || CPU_ISSUN4M ) {
		tv = &trapbase[T_L1INT - 1 + level];
		displ = (CPU_ISSUN4M)
			? &sparc_interrupt4m[0] - &tv->tv_instr[1]
			: &sparc_interrupt44c[0] - &tv->tv_instr[1];

		/* has to be `mov level,%l3; ba _sparc_interrupt; rdpsr %l0' */
		if (tv->tv_instr[0] != I_MOVi(I_L3, level) ||
		    tv->tv_instr[1] != I_BA(0, displ) ||
		    tv->tv_instr[2] != I_RDPSR(I_L0))
			panic("intr_establish(%d, %p)\n0x%x 0x%x 0x%x != 0x%x 0x%x 0x%x",
			    level, ih,
			    tv->tv_instr[0], tv->tv_instr[1], tv->tv_instr[2],
			    I_MOVi(I_L3, level), I_BA(0, displ), I_RDPSR(I_L0));
	}
#endif
	ih_insert(&intrhand[level], ih);
	splx(s);
}

/*
 * Like intr_establish, but wires a fast trap vector.  Only one such fast
 * trap is legal for any interrupt, and it must be a hardware interrupt.
 * In case some other device wants to share the interrupt, we also register
 * a callback which will be able to revert this and register a slower, but
 * shareable trap vector if necessary (for example, to share int 13 between
 * audioamd and stp).
 */
int
intr_fasttrap(int level, void (*vec)(void), int (*share)(void *), void *cbdata)
{
	struct trapvec *tv;
	u_long hi22, lo10;
#ifdef DIAGNOSTIC
	int displ;	/* suspenders, belt, and buttons too */
#endif
	int s, i;
	int instr[3];
	char *instrp;
	char *tvp;

	tv = &trapbase[T_L1INT - 1 + level];
	hi22 = ((u_long)vec) >> 10;
	lo10 = ((u_long)vec) & 0x3ff;
	s = splhigh();

	/*
	 * If this interrupt is already being handled, or if it is also used
	 * for software interrupts, we fail; the caller will either panic or
	 * try to register a slow (shareable) trap.
	 */
	if ((fastvec & (1 << level)) != 0 ||
	    intrhand[level] != NULL || sintrhand[level] != NULL) {
		splx(s);
		return (EBUSY);
	}

#ifdef DIAGNOSTIC
	displ = (CPU_ISSUN4M)
		? &sparc_interrupt4m[0] - &tv->tv_instr[1]
		: &sparc_interrupt44c[0] - &tv->tv_instr[1];

	/* has to be `mov level,%l3; ba _sparc_interrupt; rdpsr %l0' */
	if (tv->tv_instr[0] != I_MOVi(I_L3, level) ||
	    tv->tv_instr[1] != I_BA(0, displ) ||
	    tv->tv_instr[2] != I_RDPSR(I_L0))
		panic("intr_fasttrap(%d, %p)\n0x%x 0x%x 0x%x != 0x%x 0x%x 0x%x",
		    level, vec,
		    tv->tv_instr[0], tv->tv_instr[1], tv->tv_instr[2],
		    I_MOVi(I_L3, level), I_BA(0, displ), I_RDPSR(I_L0));
#endif

	instr[0] = I_SETHI(I_L3, hi22);		/* sethi %hi(vec),%l3 */
	instr[1] = I_JMPLri(I_G0, I_L3, lo10);	/* jmpl %l3+%lo(vec),%g0 */
	instr[2] = I_RDPSR(I_L0);		/* mov %psr, %l0 */

	fastvec_share[level].cb = share;
	fastvec_share[level].data = cbdata;

	tvp = (char *)tv->tv_instr;
	instrp = (char *)instr;
	for (i = 0; i < sizeof(int) * 3; i++, instrp++, tvp++)
		pmap_writetext(tvp, *instrp);
	fastvec |= 1 << level;
	splx(s);

	return (0);
}

void
intr_fastuntrap(int level)
{
	struct trapvec *tv;
	int i, s;
	int displ;
	int instr[3];
	char *instrp;
	char *tvp;

	tv = &trapbase[T_L1INT - 1 + level];

	/* restore to `mov level,%l3; ba _sparc_interrupt; rdpsr %l0' */
	displ = (CPU_ISSUN4M)
		? &sparc_interrupt4m[0] - &tv->tv_instr[1]
		: &sparc_interrupt44c[0] - &tv->tv_instr[1];
	instr[0] = I_MOVi(I_L3, level);
	instr[1] = I_BA(0, displ);
	instr[2] = I_RDPSR(I_L0);

	s = splhigh();

#ifdef DIAGNOSTIC
	if ((fastvec & (1 << level)) == 0) {
		splx(s);
		return;
	}
#endif

	tvp = (char *)tv->tv_instr;
	instrp = (char *)instr;
	for (i = 0; i < sizeof(int) * 3; i++, instrp++, tvp++)
		pmap_writetext(tvp, *instrp);
	fastvec &= ~(1 << level);
	fastvec_share[level].cb = NULL;

	splx(s);
}

void
softintr_disestablish(void *arg)
{
	struct sintrhand *sih = (struct sintrhand *)arg;

	ih_remove(&sintrhand[sih->sih_ipl], &sih->sih_ih);
	free(sih, M_DEVBUF, 0);
}

void *
softintr_establish(int level, void (*fn)(void *), void *arg)
{
	struct sintrhand *sih;
	struct intrhand *ih;
	int ipl, hw;
	int s;

	/*
	 * On a sun4m, the interrupt level is stored unmodified
	 * to be passed to raise().
	 * On a sun4 or sun4c, the appropriate bit to set
	 * in the interrupt enable register is stored, to be
	 * passed to intreg_set_44c().
	 */
	ipl = hw = level;
#if defined(SUN4) || defined(SUN4C) || defined(SUN4E)
	if (CPU_ISSUN4OR4COR4E) {
		/*
		 * Select the most suitable of the three available
		 * softintr levels.
		 */
		if (level < 4) {
			ipl = 1;
			hw = IE_L1;
		} else if (level < 6) {
			ipl = 4;
			hw = IE_L4;
		} else {
			ipl = 6;
			hw = IE_L6;
		}
	}
#endif

	sih = (struct sintrhand *)malloc(sizeof *sih, M_DEVBUF,
	    M_NOWAIT|M_ZERO);
	if (sih == NULL)
		return NULL;

	sih->sih_ipl = ipl;
	sih->sih_hw = hw;

	ih = &sih->sih_ih;
	ih->ih_fun = (int (*)(void *))fn;
	ih->ih_arg = arg;
	/*
	 * We store the ipl pre-shifted so that we can avoid one instruction
	 * in the interrupt handlers.
	 */
	ih->ih_ipl = level << 8;

	s = splhigh();

	/*
	 * Check if this interrupt is already being handled by a fast trap.
	 * If so, attempt to change it back to a regular (thus) shareable
	 * trap.
	 */
	if (fastvec & (1 << ipl)) {
		if (fastvec_share[ipl].cb == NULL ||
		    (*fastvec_share[ipl].cb)(fastvec_share[ipl].data) != 0)
			panic("softintr_establish: level %d interrupt tied to"
			    " unremovable fast vector", ipl);
	}

	ih_insert(&sintrhand[ipl], ih);
	splx(s);

	return sih;
}

void
softintr_schedule(void *arg)
{
	struct sintrhand *sih = (struct sintrhand *)arg;
	int s;

	s = splhigh();
	if (sih->sih_pending == 0) {
		sih->sih_pending++;

		switch (cputyp) {
		default:
#if defined(SUN4M)
		case CPU_SUN4M:
			raise(0, sih->sih_hw);
			break;
#endif
#if defined(SUN4) || defined(SUN4C) || defined(SUN4E)
		case CPU_SUN4:
		case CPU_SUN4E:
		case CPU_SUN4C:
			intreg_set_44c(sih->sih_hw);
			break;
#endif
#if defined(solbourne)
		case CPU_KAP:
			ienab_bis(sih->sih_hw);
			break;
#endif
		}
	}
	splx(s);
}

#ifdef DIAGNOSTIC
void
splassert_check(int wantipl, const char *func)
{
	int oldipl = (getpsr() & PSR_PIL) >> 8;

	if (oldipl < wantipl) {
		splassert_fail(wantipl, oldipl, func);
		/*
		 * If the splassert_ctl is set to not panic, raise the ipl
		 * in a feeble attempt to reduce damage.
		 */
		setpsr((getpsr() & ~PSR_PIL) | wantipl << 8);
	}
}
#endif


code:
/*	$OpenBSD: intreg.h,v 1.11 2010/07/10 19:32:25 miod Exp $	*/
/*	$NetBSD: intreg.h,v 1.6 1997/07/22 20:19:10 pk Exp $ */

/*
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This software was developed by the Computer Systems Engineering group
 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
 * contributed to Berkeley.
 *
 * All advertising materials mentioning features or use of this software
 * must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Lawrence Berkeley Laboratory.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)intreg.h	8.1 (Berkeley) 6/11/93
 */

#include <sparc/sparc/vaddrs.h>

#if defined(SUN4) || defined(SUN4C) || defined(SUN4E)

/*
 * sun4c interrupt enable register.
 *
 * The register is a single byte.  C code must use the intreg_set_44c and
 * intreg_clr_44c functions found in locore.s.
 *
 * The register's physical address is defined here as the register
 * must be mapped early in the boot process (otherwise NMI handling
 * will fail).
 */
#define	INT_ENABLE_REG_PHYSADR_44C	0xf5000000
#define	INT_ENABLE_REG_PHYSADR_4E	0xea000000

/*
 * Bits in interrupt enable register.  Software interrupt requests must
 * be cleared in software.  This is done in locore.s.  The ALLIE bit must
 * be cleared to clear asynchronous memory error (level 15) interrupts.
 */
#define	IE_L14		0x80	/* enable level 14 (counter 1) interrupts */
#define	IE_L10		0x20	/* enable level 10 (counter 0) interrupts */
#define	IE_L8		0x10	/* enable level 8 interrupts */
#define	IE_L6		0x08	/* request software level 6 interrupt */
#define	IE_L4		0x04	/* request software level 4 interrupt */
#define	IE_L1		0x02	/* request software level 1 interrupt */
#define	IE_ALLIE	0x01	/* enable interrupts */

#ifndef _LOCORE
void	intreg_set_44c(int);	/* set given bits */
void	intreg_clr_44c(int);	/* clear given bits */
#endif

#endif /* SUN4 || SUN4C || SUN4E */

#if defined(SUN4M)

/*
 * Interrupt Control Registers, located in IO space.
 * (mapped to `locore' for now..)
 * There are two sets of interrupt registers called `Processor Interrupts'
 * and `System Interrupts'. The `Processor' set corresponds to the 15
 * interrupt levels as seen by the CPU. The `System' set corresponds to
 * a set of devices supported by the implementing chip-set.
 *
 * Briefly, the ICR_PI_* are per-processor interrupts; the ICR_SI_* are
 * system-wide interrupts, and the ICR_ITR selects the processor to get
 * the system's interrupts.
 */
#define ICR_PI_PEND		(PI_INTR_VA + 0x0)
#define ICR_PI_CLR		(PI_INTR_VA + 0x4)
#define ICR_PI_SET		(PI_INTR_VA + 0x8)
#define ICR_SI_PEND		(SI_INTR_VA)
#define ICR_SI_MASK		(SI_INTR_VA + 0x4)
#define ICR_SI_CLR		(SI_INTR_VA + 0x8)
#define ICR_SI_SET		(SI_INTR_VA + 0xc)
#define ICR_ITR			(SI_INTR_VA + 0x10)

/*
 * Bits in interrupt registers.  Software interrupt requests must
 * be cleared in software.  This is done in locore.s.
 * There are separate registers for reading pending interrupts and
 * setting/clearing (software) interrupts.
 */
#define PINTR_SINTRLEV(n)	(1 << (16 + (n)))
#define PINTR_IC		0x8000		/* Level 15 clear */

#define SINTR_MA		0x80000000	/* Mask All interrupts */
#define SINTR_ME		0x40000000	/* Module Error (async) */
#define SINTR_I			0x20000000	/* MSI (MBus-SBus) */
#define SINTR_M			0x10000000	/* ECC Memory controller */
#define SINTR_V			0x08000000	/* VME Async error */
#define SINTR_RSVD2		0x07800000
#define SINTR_F			0x00400000	/* Floppy */
#define SINTR_MI		0x00200000	/* Module interrupt */
#define SINTR_VI		0x00100000	/* Video (Supersparc only) */
#define SINTR_T			0x00080000	/* Level 10 counter */
#define SINTR_SC		0x00040000	/* SCSI */
#define SINTR_A			0x00020000	/* Audio/ISDN */
#define SINTR_E			0x00010000	/* Ethernet */
#define SINTR_S			0x00008000	/* Serial port */
#define SINTR_K			0x00004000	/* Keyboard/mouse */
#define SINTR_SBUSMASK		0x00003f80	/* SBus */
#define SINTR_SBUS(n)		(1 << (7+(n)-1))
#define SINTR_VMEMASK		0x0000007f	/* VME */
#define SINTR_VME(n)		(1 << ((n)-1))
#define SINTR_BITS		"\020" \
				"\01VME0\02VME1\03VME2\04VME3\05VME4\06VME5" \
				"\07VME6\010SBUS0\011SBUS1\012SBUS2\013SBUS3" \
				"\014SBUS4\015SBUS5\016SBUS6\017K\020S\021E" \
				"\022A\023SC\024T\025VI\065MI\027F" \
				"\034V\035M\036I\037ME\040MA"

#ifndef _LOCORE
void	intreg_clr_4m(int);
void	intreg_set_4m(int);
#endif

#endif	/* SUN4M */

license: unknown


code:
/*	$OpenBSD: iommu.c,v 1.29 2015/03/30 20:30:22 miod Exp $	*/
/*	$NetBSD: iommu.c,v 1.13 1997/07/29 09:42:04 fair Exp $ */

/*
 * Copyright (c) 1996
 * 	The President and Fellows of Harvard College. All rights reserved.
 * Copyright (c) 1995 	Paul Kranenburg
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Aaron Brown and
 *	Harvard University.
 *	This product includes software developed by Paul Kranenburg.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/device.h>
#include <sys/extent.h>
#include <sys/mbuf.h>

#include <uvm/uvm_extern.h>

#include <machine/autoconf.h>
#include <machine/bus.h>
#include <machine/ctlreg.h>
#include <sparc/sparc/asm.h>
#include <sparc/sparc/vaddrs.h>
#include <sparc/sparc/cpuvar.h>
#include <sparc/sparc/iommureg.h>

struct iommu_softc {
	struct device	sc_dev;		/* base device */
	struct iommureg	*sc_reg;
	u_int		sc_pagesize;
	u_int		sc_range;
	u_int		sc_dvmabase;
	iopte_t		*sc_ptes;
	int		sc_hasiocache;
#define sc_cachecoherent sc_hasiocache

/*
 * Note: operations on the extent map are being protected with
 * splhigh(), since we cannot predict at which interrupt priority
 * our clients will run.
 */
	struct sparc_bus_dma_tag sc_dmatag;
	struct extent *sc_dvmamap;
};

struct	iommu_softc *iommu_sc;/*XXX*/
struct sparc_bus_dma_tag *iommu_dmatag;/*XXX*/
int	has_iocache;

/* autoconfiguration driver */
int	iommu_print(void *, const char *);
void	iommu_attach(struct device *, struct device *, void *);
int	iommu_match(struct device *, void *, void *);

struct cfattach iommu_ca = {
	sizeof(struct iommu_softc), iommu_match, iommu_attach
};

struct cfdriver iommu_cd = {
	NULL, "iommu", DV_DULL
};

/* IOMMU DMA map functions */
int	iommu_dmamap_create(bus_dma_tag_t, bus_size_t, int, bus_size_t,
			bus_size_t, int, bus_dmamap_t *);
int	iommu_dmamap_load(bus_dma_tag_t, bus_dmamap_t, void *,
			bus_size_t, struct proc *, int);
int	iommu_dmamap_load_mbuf(bus_dma_tag_t, bus_dmamap_t,
			struct mbuf *, int);
int	iommu_dmamap_load_uio(bus_dma_tag_t, bus_dmamap_t,
			struct uio *, int);
int	iommu_dmamap_load_raw(bus_dma_tag_t, bus_dmamap_t,
			bus_dma_segment_t *, int, bus_size_t, int);
void	iommu_dmamap_unload(bus_dma_tag_t, bus_dmamap_t);
void	iommu_dmamap_sync(bus_dma_tag_t, bus_dmamap_t, bus_addr_t,
			bus_size_t, int);

int	iommu_dmamem_map(bus_dma_tag_t, bus_dma_segment_t *,
			int, size_t, caddr_t *, int);
void	iommu_dmamem_unmap(bus_dma_tag_t, void *, size_t);
paddr_t	iommu_dmamem_mmap(bus_dma_tag_t, bus_dma_segment_t *,
			int, off_t, int, int);
int	iommu_dvma_alloc(struct iommu_softc *, bus_dmamap_t, vaddr_t,
			 bus_size_t, int, bus_addr_t *, bus_size_t *);

int	iommu_dmamap_load_buffer(bus_dma_tag_t, bus_dmamap_t, void *,
				 bus_size_t, struct proc *, int);

/*
 * Print the location of some iommu-attached device (called just
 * before attaching that device).  If `iommu' is not NULL, the
 * device was found but not configured; print the iommu as well.
 * Return UNCONF (config_find ignores this if the device was configured).
 */
int
iommu_print(args, iommu)
	void *args;
	const char *iommu;
{
	register struct confargs *ca = args;

	if (iommu)
		printf("%s at %s", ca->ca_ra.ra_name, iommu);
	return (UNCONF);
}

int
iommu_match(parent, vcf, aux)
	struct device *parent;
	void *vcf, *aux;
{
	register struct cfdata *cf = vcf;
	register struct confargs *ca = aux;
	register struct romaux *ra = &ca->ca_ra;

	if (CPU_ISSUN4OR4COR4E)
		return (0);
	return (strcmp(cf->cf_driver->cd_name, ra->ra_name) == 0);
}

/*
 * Attach the iommu.
 */
void
iommu_attach(parent, self, aux)
	struct device *parent;
	struct device *self;
	void *aux;
{
#if defined(SUN4M)
	struct iommu_softc *sc = (struct iommu_softc *)self;
	struct confargs oca, *ca = aux;
	struct sparc_bus_dma_tag *dmat = &sc->sc_dmatag;
	register struct romaux *ra = &ca->ca_ra;
	register int node;
	register char *name;
	register u_int pbase, pa;
	register int i, mmupcrsave, s;
	register iopte_t *tpte_p;
	struct pglist mlist;
	struct vm_page *m;
	vaddr_t va;
	paddr_t iopte_pa;

	iommu_sc = sc;
	iommu_dmatag = dmat;
	/*
	 * XXX there is only one iommu, for now -- do not know how to
	 * address children on others
	 */
	if (sc->sc_dev.dv_unit > 0) {
		printf(" unsupported\n");
		return;
	}

	dmat->_cookie = sc;
	dmat->_dmamap_create = iommu_dmamap_create;
	dmat->_dmamap_destroy = _bus_dmamap_destroy;
	dmat->_dmamap_load = iommu_dmamap_load;
	dmat->_dmamap_load_mbuf = iommu_dmamap_load_mbuf;
	dmat->_dmamap_load_uio = iommu_dmamap_load_uio;
	dmat->_dmamap_load_raw = iommu_dmamap_load_raw;
	dmat->_dmamap_unload = iommu_dmamap_unload;
	dmat->_dmamap_sync = iommu_dmamap_sync;

	dmat->_dmamem_alloc = _bus_dmamem_alloc;
	dmat->_dmamem_free = _bus_dmamem_free;
	dmat->_dmamem_map = iommu_dmamem_map;
	dmat->_dmamem_unmap = _bus_dmamem_unmap;
	dmat->_dmamem_mmap = iommu_dmamem_mmap;

	node = ra->ra_node;

#if 0
	if (ra->ra_vaddr)
		sc->sc_reg = (struct iommureg *)ca->ca_ra.ra_vaddr;
#else
	/*
	 * Map registers into our space. The PROM may have done this
	 * already, but I feel better if we have our own copy. Plus, the
	 * prom doesn't map the entire register set
	 *
	 * XXX struct iommureg is bigger than ra->ra_len; what are the
	 *     other fields for?
	 */
	sc->sc_reg = (struct iommureg *)
		mapiodev(ra->ra_reg, 0, ra->ra_len);
#endif

	sc->sc_hasiocache = node_has_property(node, "cache-coherence?");
	if (CACHEINFO.c_enabled == 0) /* XXX - is this correct? */
		sc->sc_hasiocache = 0;
	has_iocache = sc->sc_hasiocache; /* Set global flag */

	sc->sc_pagesize = getpropint(node, "page-size", NBPG),
	sc->sc_range = (1 << 24) <<
	    ((sc->sc_reg->io_cr & IOMMU_CTL_RANGE) >> IOMMU_CTL_RANGESHFT);
#if 0
	sc->sc_dvmabase = (0 - sc->sc_range);
#endif
	pbase = (sc->sc_reg->io_bar & IOMMU_BAR_IBA) <<
			(14 - IOMMU_BAR_IBASHFT);

	/*
	 * Allocate memory for I/O pagetables. This takes 64k of memory
	 * since we want to have 64M of dvma space (this actually depends
	 * on the definition of DVMA4M_BASE...we may drop it back to 32M).
	 * The table must be aligned on a (-DVMA4M_BASE/NBPG) boundary
	 * (i.e. 64K for 64M of dvma space).
	 */
	TAILQ_INIT(&mlist);
#define DVMA_PTESIZE ((0 - DVMA4M_BASE) / 1024)
	if (uvm_pglistalloc(DVMA_PTESIZE, 0, 0xffffffff, DVMA_PTESIZE,
			    0, &mlist, 1, UVM_PLA_NOWAIT) ||
	    (va = uvm_km_valloc(kernel_map, DVMA_PTESIZE)) == 0)
		panic("iommu_attach: can't allocate memory for pagetables");
#undef DVMA_PTESIZE
	m = TAILQ_FIRST(&mlist);
	iopte_pa = VM_PAGE_TO_PHYS(m);
	sc->sc_ptes = (iopte_t *) va;

	while (m) {
		paddr_t pa = VM_PAGE_TO_PHYS(m);
		pmap_kenter_pa(va, pa | PMAP_NC, PROT_READ | PROT_WRITE);
		va += PAGE_SIZE;
		m = TAILQ_NEXT(m, pageq);
	}
	pmap_update(pmap_kernel());

	/*
	 * Now we build our own copy of the IOMMU page tables. We need to
	 * do this since we're going to change the range to give us 64M of
	 * mappings, and thus we can move DVMA space down to 0xfd000000 to
	 * give us lots of space and to avoid bumping into the PROM, etc.
	 *
	 * XXX Note that this is rather messy.
	 */

	/*
	 * Ok. We've got to read in the original table using MMU bypass,
	 * and copy all of its entries to the appropriate place in our
	 * new table, even if the sizes are different.
	 * This is pretty easy since we know DVMA ends at 0xffffffff.
	 *
	 * XXX: PGOFSET, NBPG assume same page size as SRMMU
	 */
	if (cpuinfo.cpu_vers == 4 && cpuinfo.mxcc) {
		/* set MMU AC bit */
		sta(SRMMU_PCR, ASI_SRMMU,
		    ((mmupcrsave = lda(SRMMU_PCR, ASI_SRMMU)) | VIKING_PCR_AC));
	}

	for (tpte_p = &sc->sc_ptes[((0 - DVMA4M_BASE)/NBPG) - 1],
	     pa = (u_int)pbase - sizeof(iopte_t) +
		   ((u_int)sc->sc_range/NBPG)*sizeof(iopte_t);
	     tpte_p >= &sc->sc_ptes[0] && pa >= (u_int)pbase;
	     tpte_p--, pa -= sizeof(iopte_t)) {

		IOMMU_FLUSHPAGE(sc,
			        (tpte_p - &sc->sc_ptes[0])*NBPG + DVMA4M_BASE);
		*tpte_p = lda(pa, ASI_BYPASS);
	}
	if (cpuinfo.cpu_vers == 4 && cpuinfo.mxcc) {
		/* restore mmu after bug-avoidance */
		sta(SRMMU_PCR, ASI_SRMMU, mmupcrsave);
	}

	/*
	 * Now we can install our new pagetable into the IOMMU
	 */
	sc->sc_range = 0 - DVMA4M_BASE;
	sc->sc_dvmabase = DVMA4M_BASE;

	/* calculate log2(sc->sc_range/16MB) */
	i = ffs(sc->sc_range/(1 << 24)) - 1;
	if ((1 << i) != (sc->sc_range/(1 << 24)))
		panic("bad iommu range: %d",i);

	s = splhigh();
	IOMMU_FLUSHALL(sc);

	sc->sc_reg->io_cr = (sc->sc_reg->io_cr & ~IOMMU_CTL_RANGE) |
			  (i << IOMMU_CTL_RANGESHFT) | IOMMU_CTL_ME;
	sc->sc_reg->io_bar = (iopte_pa >> 4) & IOMMU_BAR_IBA;

	IOMMU_FLUSHALL(sc);
	splx(s);

	printf(": version 0x%x/0x%x, page-size %d, range %dMB\n",
		(sc->sc_reg->io_cr & IOMMU_CTL_VER) >> 24,
		(sc->sc_reg->io_cr & IOMMU_CTL_IMPL) >> 28,
		sc->sc_pagesize,
		sc->sc_range >> 20);

	sc->sc_dvmamap = dvmamap_extent; /* XXX */

	/* Propagate bootpath */
	if (ra->ra_bp != NULL && strcmp(ra->ra_bp->name, "iommu") == 0)
		oca.ca_ra.ra_bp = ra->ra_bp + 1;
	else
		oca.ca_ra.ra_bp = NULL;

	/*
	 * Loop through ROM children (expect SBus among them).
	 */
	oca.ca_dmat = dmat;
	for (node = firstchild(node); node; node = nextsibling(node)) {
		name = getpropstring(node, "name");
		if (!romprop(&oca.ca_ra, name, node))
			continue;
		oca.ca_bustype = BUS_MAIN; /* ??? */
		oca.ca_dmat = dmat;
		config_found(&sc->sc_dev, (void *)&oca, iommu_print);
	}
#endif
}

void
iommu_enter(va, pa)
	u_int va, pa;
{
	struct iommu_softc *sc = iommu_sc;
	int pte;

#ifdef DEBUG
	if (va < sc->sc_dvmabase)
		panic("iommu_enter: va 0x%x not in DVMA space",va);
#endif

	pte = atop(pa) << IOPTE_PPNSHFT;
	pte &= IOPTE_PPN;
	pte |= IOPTE_V | IOPTE_W | (has_iocache ? IOPTE_C : 0);
	sc->sc_ptes[atop(va - sc->sc_dvmabase)] = pte;
	IOMMU_FLUSHPAGE(sc, va);
}

/*
 * iommu_remove: clears mappings created by iommu_enter
 */
void
iommu_remove(va, len)
	register u_int va, len;
{
	register struct iommu_softc *sc = iommu_sc;

#ifdef DEBUG
	if (va < sc->sc_dvmabase)
		panic("iommu_enter: va 0x%x not in DVMA space", va);
#endif

	while (len > 0) {
#ifdef notyet
#ifdef DEBUG
		if ((sc->sc_ptes[atop(va - sc->sc_dvmabase)] & IOPTE_V) == 0)
			panic("iommu_remove: clearing invalid pte at va 0x%x",
				va);
#endif
#endif
		sc->sc_ptes[atop(va - sc->sc_dvmabase)] = 0;
		IOMMU_FLUSHPAGE(sc, va);
		len -= sc->sc_pagesize;
		va += sc->sc_pagesize;
	}
}

extern u_long dvma_cachealign;

/*
 * IOMMU DMA map functions.
 */
int
iommu_dmamap_create(bus_dma_tag_t t, bus_size_t size, int nsegments,
		    bus_size_t maxsegsz, bus_size_t boundary, int flags,
		    bus_dmamap_t *dmamp)
{
	struct iommu_softc *sc = t->_cookie;
	bus_dmamap_t map;
	int error;

	if ((error = _bus_dmamap_create(t, size, nsegments, maxsegsz,
					boundary, flags, &map)) != 0)
		return (error);

	if ((flags & BUS_DMA_24BIT) != 0) {
		/* Limit this map to the range usable by `24-bit' devices */
		map->_dm_ex_start = DVMA_D24_BASE;
		map->_dm_ex_end = DVMA_D24_END;
	} else {
		/* Enable allocations from the entire map */
		map->_dm_ex_start = sc->sc_dvmamap->ex_start;
		map->_dm_ex_end = sc->sc_dvmamap->ex_end;
	}

	*dmamp = map;
	return (0);
}

/*
 * Internal routine to allocate space in the IOMMU map.
 */
int
iommu_dvma_alloc(struct iommu_softc *sc, bus_dmamap_t map,
		 vaddr_t va, bus_size_t len, int flags,
		 bus_addr_t *dvap, bus_size_t *sgsizep)
{
	bus_size_t sgsize;
	u_long align, voff, dvaddr;
	int s, error;
	int pagesz = PAGE_SIZE;

	/*
	 * Remember page offset, then truncate the buffer address to
	 * a page boundary.
	 */
	voff = va & (pagesz - 1);
	va &= -pagesz;

	if (len > map->_dm_size)
		return (EINVAL);

	sgsize = (len + voff + pagesz - 1) & -pagesz;
	align = dvma_cachealign ? dvma_cachealign : map->_dm_align;

	s = splhigh();
	error = extent_alloc_subregion(sc->sc_dvmamap, map->_dm_ex_start,
	    map->_dm_ex_end, sgsize, align, va & (align-1), map->_dm_boundary,
	    (flags & BUS_DMA_NOWAIT) == 0 ? EX_WAITOK : EX_NOWAIT, &dvaddr);
	splx(s);
	*dvap = (bus_addr_t)dvaddr;
	*sgsizep = sgsize;
	return (error);
}

int
iommu_dmamap_load_buffer(bus_dma_tag_t t, bus_dmamap_t map, void *buf,
    bus_size_t buflen, struct proc *p, int flags)
{
	struct iommu_softc *sc = t->_cookie;
	bus_size_t sgsize;
	bus_addr_t dva;
	vaddr_t va = (vaddr_t)buf;
	int pagesz = PAGE_SIZE;
	pmap_t pmap;
	int error;

	if (map->dm_nsegs >= map->_dm_segcnt)
		return (EFBIG);

	/* Allocate IOMMU resources */
	if ((error = iommu_dvma_alloc(sc, map, va, buflen, flags,
					&dva, &sgsize)) != 0)
		return (error);

	if ((sc->sc_cachecoherent == 0) ||
	    (CACHEINFO.ec_totalsize == 0))
		cpuinfo.cache_flush(buf, buflen); /* XXX - move to bus_dma_sync? */

	/*
	 * We always use just one segment.
	 */
	map->dm_segs[map->dm_nsegs].ds_addr = dva + (va & (pagesz - 1));
	map->dm_segs[map->dm_nsegs].ds_len = buflen;
	map->dm_segs[map->dm_nsegs]._ds_sgsize = sgsize;
	map->dm_nsegs++;

	if (p != NULL)
		pmap = p->p_vmspace->vm_map.pmap;
	else
		pmap = pmap_kernel();

	for (; sgsize != 0; ) {
		paddr_t pa;
		/*
		 * Get the physical address for this page.
		 */
		if (!pmap_extract(pmap, va, &pa))
			return (EFAULT);

		iommu_enter(dva, pa);

		dva += pagesz;
		va += pagesz;
		sgsize -= pagesz;
	}

	return (0);
}

/*
 * Prepare buffer for DMA transfer.
 */
int
iommu_dmamap_load(bus_dma_tag_t t, bus_dmamap_t map,
		  void *buf, bus_size_t buflen,
		  struct proc *p, int flags)
{
	int error;

	/*
	 * Make sure that on error condition we return "no valid mappings".
	 */
	map->dm_mapsize = buflen;
	map->dm_nsegs = 0;

	error = iommu_dmamap_load_buffer(t, map, buf, buflen, p, flags);
	if (error)
		iommu_dmamap_unload(t, map);

	return (error);
}

/*
 * Like _bus_dmamap_load(), but for mbufs.
 */
int
iommu_dmamap_load_mbuf(bus_dma_tag_t t, bus_dmamap_t map,
		       struct mbuf *m0, int flags)
{
	struct mbuf *m;
	int error = 0;

	/*
	 * Make sure that on error condition we return "no valid mappings".
	 */
	map->dm_mapsize = m0->m_pkthdr.len;
	map->dm_nsegs = 0;

	for (m = m0; m != NULL && error == 0; m = m->m_next) {
		if (m->m_len == 0)
			continue;
		error = iommu_dmamap_load_buffer(t, map, m->m_data, m->m_len,
		    NULL, flags);
	}

	if (error)
		iommu_dmamap_unload(t, map);

	return (error);
}

/*
 * Like _bus_dmamap_load(), but for uios.
 */
int
iommu_dmamap_load_uio(bus_dma_tag_t t, bus_dmamap_t map,
		      struct uio *uio, int flags)
{

	panic("_bus_dmamap_load_uio: not implemented");
}

/*
 * Like _bus_dmamap_load(), but for raw memory allocated with
 * bus_dmamem_alloc().
 */
int
iommu_dmamap_load_raw(bus_dma_tag_t t, bus_dmamap_t map,
		      bus_dma_segment_t *segs, int nsegs, bus_size_t size,
		      int flags)
{
	struct iommu_softc *sc = t->_cookie;
	struct vm_page *m;
	paddr_t pa;
	bus_addr_t dva;
	bus_size_t sgsize;
	struct pglist *mlist;
	int pagesz = PAGE_SIZE;
	int error;

	map->dm_nsegs = 0;

	/* Allocate IOMMU resources */
	if ((error = iommu_dvma_alloc(sc, map, segs[0]._ds_va, size,
				      flags, &dva, &sgsize)) != 0)
		return (error);

	/*
	 * Note DVMA address in case bus_dmamem_map() is called later.
	 * It can then insure cache coherency by choosing a KVA that
	 * is aligned to `ds_addr'.
	 */
	segs[0].ds_addr = dva;
	segs[0].ds_len = size;

	map->dm_segs[0].ds_addr = dva;
	map->dm_segs[0].ds_len = size;
	map->dm_segs[0]._ds_sgsize = sgsize;

	/* Map physical pages into IOMMU */
	mlist = segs[0]._ds_mlist;
	for (m = TAILQ_FIRST(mlist); m != NULL; m = TAILQ_NEXT(m,pageq)) {
		if (sgsize == 0)
			panic("iommu_dmamap_load_raw: size botch");
		pa = VM_PAGE_TO_PHYS(m);
		iommu_enter(dva, pa);
		dva += pagesz;
		sgsize -= pagesz;
	}

	map->dm_nsegs = 1;
	map->dm_mapsize = size;

	return (0);
}

/*
 * Unload an IOMMU DMA map.
 */
void
iommu_dmamap_unload(bus_dma_tag_t t, bus_dmamap_t map)
{
	struct iommu_softc *sc = t->_cookie;
	bus_dma_segment_t *segs = map->dm_segs;
	int nsegs = map->dm_nsegs;
	bus_addr_t dva;
	bus_size_t len;
	int i, s, error;

	for (i = 0; i < nsegs; i++) {
		dva = segs[i].ds_addr & -PAGE_SIZE;
		len = segs[i]._ds_sgsize;

		iommu_remove(dva, len);
		s = splhigh();
		error = extent_free(sc->sc_dvmamap, dva, len, EX_NOWAIT);
		splx(s);
		if (error != 0)
			printf("warning: %ld of DVMA space lost\n", (long)len);
	}

	/* Mark the mappings as invalid. */
	map->dm_mapsize = 0;
	map->dm_nsegs = 0;
}

/*
 * DMA map synchronization.
 */
void
iommu_dmamap_sync(bus_dma_tag_t t, bus_dmamap_t map,
		  bus_addr_t offset, bus_size_t len, int ops)
{

	/*
	 * XXX Should flush CPU write buffers.
	 */
}

/*
 * Map DMA-safe memory.
 */
int
iommu_dmamem_map(bus_dma_tag_t t, bus_dma_segment_t *segs, int nsegs,
		 size_t size, caddr_t *kvap, int flags)
{
	struct iommu_softc *sc = t->_cookie;
	struct vm_page *m;
	vaddr_t va;
	bus_addr_t addr;
	struct pglist *mlist;
	int cbit;
	u_long align;
	int pagesz = PAGE_SIZE;
	const struct kmem_dyn_mode *kd;

	if (nsegs != 1)
		panic("iommu_dmamem_map: nsegs = %d", nsegs);

	cbit = sc->sc_cachecoherent ? 0 : PMAP_NC;
	align = dvma_cachealign ? dvma_cachealign : pagesz;

	size = round_page(size);

#if 0
	/*
	 * In case the segment has already been loaded by
	 * iommu_dmamap_load_raw(), find a region of kernel virtual
	 * addresses that can accommodate our alignment requirements.
	 */
	va = _bus_dma_valloc_skewed(size, 0, align,
				    segs[0].ds_addr & (align - 1));
#else
	kd = flags & BUS_DMA_NOWAIT ? &kd_trylock : &kd_waitok;
	va = (vaddr_t)km_alloc(size, &kv_any, &kp_none, kd);
#endif
	if (va == 0)
		return (ENOMEM);

	segs[0]._ds_va = va;
	*kvap = (void *)va;

	/*
	 * Map the pages allocated in _bus_dmamem_alloc() to the
	 * kernel virtual address space.
	 */
	mlist = segs[0]._ds_mlist;
	for (m = TAILQ_FIRST(mlist); m != NULL; m = TAILQ_NEXT(m,pageq)) {

		if (size == 0)
			panic("iommu_dmamem_map: size botch");

		addr = VM_PAGE_TO_PHYS(m);
		pmap_kenter_pa(va, addr | cbit, PROT_READ | PROT_WRITE);
#if 0
			if (flags & BUS_DMA_COHERENT)
				/* XXX */;
#endif
		va += pagesz;
		size -= pagesz;
	}
	pmap_update(pmap_kernel());

	return (0);
}

void
iommu_dmamem_unmap(bus_dma_tag_t t, void *kva, size_t size)
{

#ifdef DIAGNOSTIC
	if ((u_long)kva & PAGE_MASK)
		panic("iommu_dmamem_unmap");
#endif

	km_free(kva, round_page(size), &kv_any, &kp_none);
}

/*
 * mmap(2)'ing DMA-safe memory.
 */
paddr_t
iommu_dmamem_mmap(bus_dma_tag_t t, bus_dma_segment_t *segs, int nsegs,
		  off_t off, int prot, int flags)
{
	panic("_bus_dmamem_mmap: not implemented");
}


code:
/*	$OpenBSD: iommureg.h,v 1.6 1998/05/10 21:11:53 art Exp $	*/
/*	$NetBSD: iommureg.h,v 1.5 1998/05/04 23:16:59 pk Exp $	*/

/*
 * Copyright (c) 1996
 * 	The President and Fellows of Harvard College. All rights reserved.
 * Copyright (c) 1995 	Paul Kranenburg
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Aaron Brown and
 *	Harvard University.
 *	This product includes software developed by Paul Kranenburg.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

struct iommureg {
	u_int32_t	io_cr;		/* IOMMU control register */
	u_int32_t	io_bar;		/* IOMMU PTE base register */
	u_int32_t	io_fill1[3];
	u_int32_t	io_flashclear;	/* Flush all TLB entries */
	u_int32_t	io_flushpage;	/* Flush page from TLB */
};

#define IOMMU_CTL_IMPL		0xf0000000
#define IOMMU_CTL_VER		0x0f000000
#define IOMMU_CTL_RSVD1		0x00ffffe0
#define IOMMU_CTL_RANGE		0x0000001c
#define IOMMU_CTL_RANGESHFT	2
#define IOMMU_CTL_RSVD2		0x00000002
#define IOMMU_CTL_ME		0x00000001

#define IOMMU_BAR_IBA		0xfffffc00
#define IOMMU_BAR_IBASHFT	10

/* Flushpage fields */
#define IOMMU_FLPG_VADDR	0xfffff000
#define IOMMU_FLUSH_MASK	0xfffff000

/*
 * A few empty cycles after touching the IOMMU registers seems to
 * avoid utter lossage on some machines (SS4s & SS5s) where our caller
 * would see some of its local (`%lx') registers trashed.
 */
#define IOMMU_FLUSHPAGE(sc, va)	do {				\
	(sc)->sc_reg->io_flushpage = (va) & IOMMU_FLUSH_MASK;	\
	__asm("nop;nop;nop;");					\
} while (0);
#define IOMMU_FLUSHALL(sc)	do {				\
	(sc)->sc_reg->io_flashclear = 0;			\
	__asm("nop;nop;nop;");					\
} while (0)

/* to pte.h ? */
typedef u_int32_t iopte_t;

#define IOPTE_PPN	0xffffff00	/* PA<35:12> */
#define IOPTE_C		0x00000080 	/* cacheable */
#define IOPTE_W		0x00000004	/* writeable */
#define IOPTE_V		0x00000002	/* valid */
#define IOPTE_WAZ	0x00000001	/* must write as zero */

#define IOPTE_PPNSHFT	8		/* shift to get ppn from IOPTE */
#define IOPTE_PPNPASHFT	4		/* shift to get pa from ioppn */

#define IOPTE_BITS "\20\10C\3W\2V\1WAZ"


code:
/*	$OpenBSD: kgdb_machdep.c,v 1.10 2010/07/10 19:32:25 miod Exp $ */
/*	$NetBSD: kgdb_machdep.c,v 1.1 1997/08/31 21:22:45 pk Exp $ */
/*-
 * Copyright (c) 1997 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Paul Kranenburg.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 * Copyright (c) 1995
 * 	The President and Fellows of Harvard College. All rights reserved.
 *
 * This software was developed by the Computer Systems Engineering group
 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
 * contributed to Berkeley.
 *
 * All advertising materials mentioning features or use of this software
 * must display the following acknowledgements:
 *	This product includes software developed by the University of
 *	California, Lawrence Berkeley Laboratory.
 *
 * 	This product includes software developed by Harvard University.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)kgdb_stub.c	8.1 (Berkeley) 6/11/93
 */

/*
 * Machine dependent routines needed by kern/kgdb_stub.c
 */
#ifdef KGDB

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/kgdb.h>

#include <uvm/uvm_extern.h>

#include <machine/ctlreg.h>
#include <machine/psl.h>
#include <machine/reg.h>
#include <machine/trap.h>
#include <machine/cpu.h>

#include <sparc/sparc/asm.h>

#if defined(SUN4M)
#define getpte4m(va) \
	lda(((vaddr_t)va & 0xFFFFF000) | ASI_SRMMUFP_L3, ASI_SRMMUFP)
#endif

#if defined(SUN4) || defined(SUN4C) || defined(SUN4E)
#define	getpte4(va)		lda(va, ASI_PTE)
#define	setpte4(va, pte)	sta(va, ASI_PTE, pte)
#endif

static __inline void kgdb_copy(char *, char *, int);
static __inline void kgdb_zero(char *, int);

/*
 * This little routine exists simply so that bcopy() can be debugged.
 */
static __inline void
kgdb_copy(src, dst, len)
	register char *src, *dst;
	register int len;
{

	while (--len >= 0)
		*dst++ = *src++;
}

/* ditto for bzero */
static __inline void
kgdb_zero(ptr, len)
	register char *ptr;
	register int len;
{
	while (--len >= 0)
		*ptr++ = (char) 0;
}

/*
 * Translate a trap number into a unix compatible signal value.
 * (gdb only understands unix signal numbers).
 * XXX should this be done at the other end?
 */
int
kgdb_signal(type)
	int type;
{
	int sigval;

	switch (type) {

	case T_AST:
		sigval = SIGINT;
		break;

	case T_TEXTFAULT:
	case T_DATAFAULT:
		sigval = SIGSEGV;
		break;

	case T_ALIGN:
		sigval = SIGBUS;
		break;

	case T_ILLINST:
	case T_PRIVINST:
	case T_DIV0:
		sigval = SIGILL;
		break;

	case T_FPE:
		sigval = SIGFPE;
		break;

	case T_BREAKPOINT:
		sigval = SIGTRAP;
		break;

	case T_KGDB_EXEC:
		sigval = SIGIOT;
		break;

	default:
		sigval = SIGEMT;
		break;
	}
	return (sigval);
}

/*
 * Definitions exported from gdb (& then made prettier).
 */
#define	GDB_G0		0
#define	GDB_O0		8
#define	GDB_L0		16
#define	GDB_I0		24
#define	GDB_FP0		32
#define	GDB_Y		64
#define	GDB_PSR		65
#define	GDB_WIM		66
#define	GDB_TBR		67
#define	GDB_PC		68
#define	GDB_NPC		69
#define	GDB_FSR		70
#define	GDB_CSR		71

#define REGISTER_BYTES		(KGDB_NUMREGS * 4)
#define REGISTER_BYTE(n)	((n) * 4)

/*
 * Translate the values stored in the kernel regs struct to the format
 * understood by gdb.
 */
void
kgdb_getregs(regs, gdb_regs)
	db_regs_t *regs;
	kgdb_reg_t *gdb_regs;
{
	struct trapframe *tf = &regs->db_tf;

	/* %g0..%g7 and %o0..%o7: from trapframe */
	gdb_regs[0] = 0;
	kgdb_copy((caddr_t)&tf->tf_global[1], (caddr_t)&gdb_regs[1], 15 * 4);

	/* %l0..%l7 and %i0..%i7: from stack */
	kgdb_copy((caddr_t)tf->tf_out[6], (caddr_t)&gdb_regs[GDB_L0], 16 * 4);

	/* %f0..%f31 -- fake, kernel does not use FP */
	kgdb_zero((caddr_t)&gdb_regs[GDB_FP0], 32 * 4);

	/* %y, %psr, %wim, %tbr, %pc, %npc, %fsr, %csr */
	gdb_regs[GDB_Y] = tf->tf_y;
	gdb_regs[GDB_PSR] = tf->tf_psr;
	gdb_regs[GDB_WIM] = tf->tf_global[0];	/* input only! */
	gdb_regs[GDB_TBR] = 0;			/* fake */
	gdb_regs[GDB_PC] = tf->tf_pc;
	gdb_regs[GDB_NPC] = tf->tf_npc;
	gdb_regs[GDB_FSR] = 0;			/* fake */
	gdb_regs[GDB_CSR] = 0;			/* fake */
}

/*
 * Reverse the above.
 */
void
kgdb_setregs(regs, gdb_regs)
	db_regs_t *regs;
	kgdb_reg_t *gdb_regs;
{
	struct trapframe *tf = &regs->db_tf;

	kgdb_copy((caddr_t)&gdb_regs[1], (caddr_t)&tf->tf_global[1], 15 * 4);
	kgdb_copy((caddr_t)&gdb_regs[GDB_L0], (caddr_t)tf->tf_out[6], 16 * 4);
	tf->tf_y = gdb_regs[GDB_Y];
	tf->tf_psr = gdb_regs[GDB_PSR];
	tf->tf_pc = gdb_regs[GDB_PC];
	tf->tf_npc = gdb_regs[GDB_NPC];
}

/*
 * Determine if memory at [va..(va+len)] is valid.
 */
int
kgdb_acc(vaddr_t va, size_t len)
{
	int pte;
	vaddr_t eva;

	eva = round_page(va + len);
	va = trunc_page(va);

	/* XXX icky: valid address but causes timeout */
	if (va >= (vaddr_t)0xfffff000)
		return (0);

	for (; va < eva; va += NBPG) {
#if defined(SUN4M)
		if (CPU_ISSUN4M) {
			pte = getpte4m(va);
			if ((pte & SRMMU_TETYPE) != SRMMU_TEPTE)
				return (0);
		}
#endif
#if defined(SUN4) || defined(SUN4C) || defined(SUN4E)
		if (CPU_ISSUN4OR4COR4E) {
			pte = getpte4(va);
			if ((pte & PG_V) == 0)
				return (0);
		}
#endif
	}

	return (1);
}
#endif