code:
/*	$OpenBSD: mips64r2.S,v 1.2 2012/10/03 11:18:23 miod Exp $	*/

/*
 * Copyright (c) 2011 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * Routines to access extra coprocessor 0 registers
 */

#include <machine/asm.h>
#include <mips64/mips_cpu.h>

	.set	noreorder
	.set	mips64r2

LEAF(cp0_get_config_1, 0)
	j	ra
	mfc0	v0, COP_0_CONFIG, 1
END(cp0_get_config_1)

LEAF(cp0_get_config_2, 0)
	j	ra
	mfc0	v0, COP_0_CONFIG, 2
END(cp0_get_config_2)

LEAF(cp0_get_config_3, 0)
	j	ra
	mfc0	v0, COP_0_CONFIG, 3
END(cp0_get_config_3)


code:
/*	$OpenBSD: mutex.c,v 1.1 2015/07/08 13:37:31 dlg Exp $	*/

/*
 * Copyright (c) 2004 Artur Grabowski <art@openbsd.org>
 * All rights reserved.
 *
 * 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. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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/mutex.h>
#include <sys/systm.h>
#include <sys/atomic.h>

#include <machine/intr.h>

void
__mtx_init(struct mutex *mtx, int wantipl)
{
	mtx->mtx_owner = NULL;
	mtx->mtx_wantipl = wantipl;
	mtx->mtx_oldipl = IPL_NONE;
}

#ifdef MULTIPROCESSOR
void
mtx_enter(struct mutex *mtx)
{
	while (mtx_enter_try(mtx) == 0)
		;
}

int
mtx_enter_try(struct mutex *mtx)
{
	struct cpu_info *owner, *ci = curcpu();
	int s;

 	if (mtx->mtx_wantipl != IPL_NONE)
		s = splraise(mtx->mtx_wantipl);

	owner = atomic_cas_ptr(&mtx->mtx_owner, NULL, ci);
#ifdef DIAGNOSTIC
	if (__predict_false(owner == ci))
		panic("mtx %p: locking against myself", mtx);
#endif
	if (owner == NULL) {
		if (mtx->mtx_wantipl != IPL_NONE)
			mtx->mtx_oldipl = s;
#ifdef DIAGNOSTIC
		ci->ci_mutex_level++;
#endif
		membar_enter();
		return (1);
	}

	if (mtx->mtx_wantipl != IPL_NONE)
		splx(s);

	return (0);
}
#else
void
mtx_enter(struct mutex *mtx)
{
	struct cpu_info *ci = curcpu();

#ifdef DIAGNOSTIC
	if (__predict_false(mtx->mtx_owner == ci))
		panic("mtx %p: locking against myself", mtx);
#endif

	if (mtx->mtx_wantipl != IPL_NONE)
		mtx->mtx_oldipl = splraise(mtx->mtx_wantipl);

	mtx->mtx_owner = ci;

#ifdef DIAGNOSTIC
	ci->ci_mutex_level++;
#endif
}

int
mtx_enter_try(struct mutex *mtx)
{
	mtx_enter(mtx);
	return (1);
}
#endif

void
mtx_leave(struct mutex *mtx)
{
	int s;

	MUTEX_ASSERT_LOCKED(mtx);

#ifdef MULTIPROCESSOR
	membar_exit();
#endif
#ifdef DIAGNOSTIC
	curcpu()->ci_mutex_level--;
#endif

	s = mtx->mtx_oldipl;
	mtx->mtx_owner = NULL;
	if (mtx->mtx_wantipl != IPL_NONE)
		splx(s);
}

license: unknown


code:
/*	$OpenBSD: pmap.c,v 1.80 2015/07/30 17:02:17 visa Exp $	*/

/*
 * Copyright (c) 2001-2004 Opsycon AB  (www.opsycon.se / www.opsycon.com)
 *
 * 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 AUTHOR ``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 AUTHOR 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/malloc.h>
#include <sys/user.h>
#include <sys/buf.h>
#include <sys/pool.h>
#ifdef SYSVSHM
#include <sys/shm.h>
#endif
#include <sys/atomic.h>

#include <mips64/cache.h>
#include <machine/autoconf.h>
#include <machine/cpu.h>
#include <machine/vmparam.h>

#include <uvm/uvm.h>

extern void mem_zero_page(vaddr_t);

struct pool pmap_pmap_pool;
struct pool pmap_pv_pool;
struct pool pmap_pg_pool;

#define pmap_pv_alloc()		(pv_entry_t)pool_get(&pmap_pv_pool, PR_NOWAIT)
#define pmap_pv_free(pv)	pool_put(&pmap_pv_pool, (pv))

#ifndef PMAP_PV_LOWAT
#define PMAP_PV_LOWAT   16
#endif
int	pmap_pv_lowat = PMAP_PV_LOWAT;

uint	 pmap_alloc_tlbpid(struct proc *);
int	 pmap_enter_pv(pmap_t, vaddr_t, vm_page_t, pt_entry_t *);
void	 pmap_remove_pv(pmap_t, vaddr_t, paddr_t);
void	*pmap_pg_alloc(struct pool *, int, int *);
void	 pmap_pg_free(struct pool *, void *);

struct pool_allocator pmap_pg_allocator = {
	pmap_pg_alloc, pmap_pg_free
};

void	pmap_invalidate_user_page(pmap_t, vaddr_t);
#ifdef MULTIPROCESSOR
void	pmap_invalidate_kernel_page(vaddr_t);
void	pmap_invalidate_kernel_page_action(void *);
void	pmap_invalidate_user_page_action(void *);
void	pmap_update_kernel_page_action(void *);
void	pmap_update_user_page_action(void *);
#else
#define pmap_invalidate_kernel_page(va) tlb_flush_addr(va)
#endif

#ifdef PMAPDEBUG
struct {
	int kernel;	/* entering kernel mapping */
	int user;	/* entering user mapping */
	int ptpneeded;	/* needed to allocate a PT page */
	int pwchange;	/* no mapping change, just wiring or protection */
	int wchange;	/* no mapping change, just wiring */
	int mchange;	/* was mapped but mapping to different page */
	int managed;	/* a managed page */
	int firstpv;	/* first mapping for this PA */
	int secondpv;	/* second mapping for this PA */
	int ci;		/* cache inhibited */
	int unmanaged;	/* not a managed page */
	int flushes;	/* cache flushes */
	int cachehit;	/* new entry forced valid entry out */
} enter_stats;
struct {
	int calls;
	int removes;
	int flushes;
	int pidflushes;	/* HW pid stolen */
	int pvfirst;
	int pvsearch;
} remove_stats;

#define PDB_FOLLOW	0x0001
#define PDB_INIT	0x0002
#define PDB_ENTER	0x0004
#define PDB_REMOVE	0x0008
#define PDB_CREATE	0x0010
#define PDB_PTPAGE	0x0020
#define PDB_PVENTRY	0x0040
#define PDB_BITS	0x0080
#define PDB_COLLECT	0x0100
#define PDB_PROTECT	0x0200
#define PDB_TLBPID	0x0400
#define PDB_PARANOIA	0x2000
#define PDB_WIRING	0x4000
#define PDB_PVDUMP	0x8000

#define DPRINTF(flag, printdata)	\
	if (pmapdebug & (flag)) 	\
		printf printdata;

#define stat_count(what)	atomic_inc_int(&(what))
int pmapdebug = PDB_ENTER|PDB_FOLLOW;

#else

#define DPRINTF(flag, printdata)
#define stat_count(what)

#endif	/* PMAPDEBUG */

static struct pmap	kernel_pmap_store
	[(PMAP_SIZEOF(MAXCPUS) + sizeof(struct pmap) - 1)
		/ sizeof(struct pmap)];
struct pmap *const kernel_pmap_ptr = kernel_pmap_store;

vaddr_t	virtual_start;  /* VA of first avail page (after kernel bss)*/
vaddr_t	virtual_end;	/* VA of last avail page (end of kernel AS) */

vaddr_t	pmap_prefer_mask;

static struct pmap_asid_info pmap_asid_info[MAXCPUS];

pt_entry_t	*Sysmap;		/* kernel pte table */
u_int		Sysmapsize;		/* number of pte's in Sysmap */
const vaddr_t	Sysmapbase = VM_MIN_KERNEL_ADDRESS;	/* for libkvm */

#ifdef MULTIPROCESSOR

struct pmap_invalidate_page_arg {
	pmap_t pmap;
	vaddr_t va;
};

void
pmap_invalidate_kernel_page(vaddr_t va)
{
	struct pmap_invalidate_page_arg arg;
	unsigned int cpumask = 0;
	struct cpu_info *ci;
	CPU_INFO_ITERATOR cii;

	CPU_INFO_FOREACH(cii, ci)
		if (cpuset_isset(&cpus_running, ci))
			cpumask |= 1 << ci->ci_cpuid;
	arg.va = va;

	smp_rendezvous_cpus(cpumask, pmap_invalidate_kernel_page_action, &arg);
}

void
pmap_invalidate_kernel_page_action(void *arg)
{
	vaddr_t va = ((struct pmap_invalidate_page_arg *)arg)->va;

	tlb_flush_addr(va);
}

void
pmap_invalidate_user_page(pmap_t pmap, vaddr_t va)
{
	unsigned int cpuid = cpu_number();
	unsigned int cpumask = 0;
	struct cpu_info *ci;
	CPU_INFO_ITERATOR cii;

	CPU_INFO_FOREACH(cii, ci)
		if (cpuset_isset(&cpus_running, ci)) {
			unsigned int i = ci->ci_cpuid;
			unsigned int m = 1 << i;
			if (pmap->pm_asid[i].pma_asidgen !=
			    pmap_asid_info[i].pma_asidgen)
				continue;
			else if (ci->ci_curpmap != pmap) {
				pmap->pm_asid[i].pma_asidgen = 0;
				continue;
			}
			cpumask |= m;
		}

	if (cpumask == 1 << cpuid) {
		u_long asid;

		asid = pmap->pm_asid[cpuid].pma_asid << PG_ASID_SHIFT;
		tlb_flush_addr(va | asid);
	} else if (cpumask) {
		struct pmap_invalidate_page_arg arg;
		arg.pmap = pmap;
		arg.va = va;

		smp_rendezvous_cpus(cpumask, pmap_invalidate_user_page_action,
			&arg);
	}
}

void
pmap_invalidate_user_page_action(void *arg)
{
	pmap_t pmap = ((struct pmap_invalidate_page_arg *)arg)->pmap;
	vaddr_t va = ((struct pmap_invalidate_page_arg *)arg)->va;
	unsigned int cpuid = cpu_number();
	u_long asid;

	asid = pmap->pm_asid[cpuid].pma_asid << PG_ASID_SHIFT;
	tlb_flush_addr(va | asid);
}

struct pmap_update_page_arg {
	pmap_t pmap;
	vaddr_t va;
	pt_entry_t entry;
};

void
pmap_update_kernel_page(vaddr_t va, pt_entry_t entry)
{
	struct pmap_update_page_arg arg;
	unsigned long cpumask = 0;
	struct cpu_info *ci;
	CPU_INFO_ITERATOR cii;

	CPU_INFO_FOREACH(cii, ci)
		if (cpuset_isset(&cpus_running, ci))
			cpumask |= 1 << ci->ci_cpuid;

	arg.va = va;
	arg.entry = entry;
	smp_rendezvous_cpus(cpumask,
			    pmap_update_kernel_page_action, &arg);
}

void
pmap_update_kernel_page_action(void *arg)
{
	vaddr_t va = ((struct pmap_update_page_arg *)arg)->va;
	pt_entry_t entry = ((struct pmap_update_page_arg *)arg)->entry;

	tlb_update(va, entry);
}

void
pmap_update_user_page(pmap_t pmap, vaddr_t va, pt_entry_t entry)
{
	unsigned int cpuid = cpu_number();
	unsigned long cpumask = 0;
	struct cpu_info *ci;
	CPU_INFO_ITERATOR cii;

	CPU_INFO_FOREACH(cii, ci)
		if (cpuset_isset(&cpus_running, ci)) {
			unsigned int i = ci->ci_cpuid;
			unsigned int m = 1 << i;
			if (pmap->pm_asid[i].pma_asidgen !=
			    pmap_asid_info[i].pma_asidgen)
				continue;
			else if (ci->ci_curpmap != pmap) {
				pmap->pm_asid[i].pma_asidgen = 0;
				continue;
			}
			cpumask |= m;
		}

	if (cpumask == 1 << cpuid) {
		u_long asid;

		asid = pmap->pm_asid[cpuid].pma_asid << PG_ASID_SHIFT;
		tlb_update(va | asid, entry);
	} else if (cpumask) {
		struct pmap_update_page_arg arg;
		arg.pmap = pmap;
		arg.va = va;
		arg.entry = entry;
		smp_rendezvous_cpus(cpumask,
				    pmap_update_user_page_action, &arg);
	}
}

void
pmap_update_user_page_action(void *arg)
{
	pmap_t pmap = ((struct pmap_update_page_arg *)arg)->pmap;
	vaddr_t va = ((struct pmap_update_page_arg *)arg)->va;
	pt_entry_t entry = ((struct pmap_update_page_arg *)arg)->entry;
	unsigned int cpuid = cpu_number();
	u_long asid;

	asid = pmap->pm_asid[cpuid].pma_asid << PG_ASID_SHIFT;
	tlb_update(va | asid, entry);
}
#else
void
pmap_invalidate_user_page(pmap_t pmap, vaddr_t va)
{
	u_long cpuid = cpu_number();
	u_long asid = pmap->pm_asid[cpuid].pma_asid << PG_ASID_SHIFT;

	if (pmap->pm_asid[cpuid].pma_asidgen ==
	    pmap_asid_info[cpuid].pma_asidgen) {
#ifdef CPU_R4000
		if (r4000_errata != 0)
			eop_tlb_flush_addr(pmap, va, asid);
		else
#endif
			tlb_flush_addr(va | asid);
	}
}

void
pmap_update_user_page(pmap_t pmap, vaddr_t va, pt_entry_t entry)
{
	u_long cpuid = cpu_number();
	u_long asid = pmap->pm_asid[cpuid].pma_asid << PG_ASID_SHIFT;

	if (pmap->pm_asid[cpuid].pma_asidgen ==
	    pmap_asid_info[cpuid].pma_asidgen)
		tlb_update(va | asid, entry);
}
#endif

/*
 *	Bootstrap the system enough to run with virtual memory.
 */
void
pmap_bootstrap(void)
{
	u_int i;
#ifndef CPU_R8000
	pt_entry_t *spte;
#endif

	/*
	 * Create a mapping table for kernel virtual memory. This
	 * table is a linear table in contrast to the user process
	 * mapping tables which are built with segment/page tables.
	 * Create 1GB of map (this will only use 1MB of memory).
	 */
	virtual_start = VM_MIN_KERNEL_ADDRESS;
	virtual_end = VM_MAX_KERNEL_ADDRESS;

	Sysmapsize = (VM_MAX_KERNEL_ADDRESS - VM_MIN_KERNEL_ADDRESS) /
	    PAGE_SIZE;
#ifndef CPU_R8000
	if (Sysmapsize & 1)
		Sysmapsize++;	/* force even number of pages */
#endif

	Sysmap = (pt_entry_t *)
	    uvm_pageboot_alloc(sizeof(pt_entry_t) * Sysmapsize);

	pool_init(&pmap_pmap_pool, PMAP_SIZEOF(ncpusfound), 0, 0, 0,"pmappl", NULL);
	pool_init(&pmap_pv_pool, sizeof(struct pv_entry), 0, 0, 0,"pvpl", NULL);
	pool_init(&pmap_pg_pool, PMAP_L2SIZE, PMAP_L2SIZE, 0, 0, "pmappgpl",
	    &pmap_pg_allocator);

	pmap_kernel()->pm_count = 1;

#ifndef CPU_R8000
	/*
	 * The 64 bit Mips architecture stores the AND result
	 * of the Global bits in the pte pair in the on chip
	 * translation lookaside buffer. Thus invalid entries
	 * must have the Global bit set so when Entry LO and
	 * Entry HI G bits are ANDed together they will produce
	 * a global bit to store in the tlb.
	 */
	for (i = Sysmapsize, spte = Sysmap; i != 0; i--, spte++)
		*spte = PG_G;
#else
	bzero(Sysmap, sizeof(pt_entry_t) * Sysmapsize);
#endif
	tlb_set_gbase((vaddr_t)Sysmap, Sysmapsize);

	for (i = 0; i < MAXCPUS; i++) {
		pmap_asid_info[i].pma_asidgen = 1;
		pmap_asid_info[i].pma_asid = MIN_USER_ASID + 1;
	}
}

/*
 *  Page steal allocator used during bootup.
 */
vaddr_t
pmap_steal_memory(vsize_t size, vaddr_t *vstartp, vaddr_t *vendp)
{
	int i, j;
	uint npg;
	vaddr_t va;
	paddr_t pa;

#ifdef DIAGNOSTIC
	if (uvm.page_init_done) {
		panic("pmap_steal_memory: too late, vm is running!");
	}
#endif

	size = round_page(size);
	npg = atop(size);
	va = 0;

	for(i = 0; i < vm_nphysseg && va == 0; i++) {
		if (vm_physmem[i].avail_start != vm_physmem[i].start ||
		    vm_physmem[i].avail_start >= vm_physmem[i].avail_end) {
			continue;
		}

		if ((vm_physmem[i].avail_end - vm_physmem[i].avail_start) < npg)
			continue;

		pa = ptoa(vm_physmem[i].avail_start);
		vm_physmem[i].avail_start += npg;
		vm_physmem[i].start += npg;

		if (vm_physmem[i].avail_start == vm_physmem[i].end) {
			if (vm_nphysseg == 1)
				panic("pmap_steal_memory: out of memory!");

			vm_nphysseg--;
			for (j = i; j < vm_nphysseg; j++)
				vm_physmem[j] = vm_physmem[j + 1];
		}
		if (vstartp)
			*vstartp = round_page(virtual_start);
		if (vendp)
			*vendp = virtual_end;

#ifdef __sgi__
#ifndef CPU_R8000
		/*
		 * Return a CKSEG0 address whenever possible.
		 */
		if (pa + size < CKSEG_SIZE)
			va = PHYS_TO_CKSEG0(pa);
		else
#endif
			va = PHYS_TO_XKPHYS(pa, CCA_CACHED);
#else
		va = PHYS_TO_XKPHYS(pa, CCA_CACHED);
#endif

		bzero((void *)va, size);
		return (va);
	}

	panic("pmap_steal_memory: no memory to steal");
}

/*
 *	Initialize the pmap module.
 *	Called by uvm_init, to initialize any structures that the pmap
 *	system needs to map virtual memory.
 */
void
pmap_init()
{

	DPRINTF(PDB_FOLLOW|PDB_INIT, ("pmap_init()\n"));

#if 0 /* too early */
	pool_setlowat(&pmap_pv_pool, pmap_pv_lowat);
#endif
}

static pv_entry_t pg_to_pvh(struct vm_page *);
static __inline pv_entry_t
pg_to_pvh(struct vm_page *pg)
{
	return &pg->mdpage.pv_ent;
}

/*
 *	Create and return a physical map.
 */
pmap_t
pmap_create()
{
	pmap_t pmap;
	int i, s;

extern struct vmspace vmspace0;
extern struct user *proc0paddr;

	DPRINTF(PDB_FOLLOW|PDB_CREATE, ("pmap_create()\n"));

	s = splvm();
	pmap = pool_get(&pmap_pmap_pool, PR_WAITOK | PR_ZERO);
	splx(s);

	pmap->pm_count = 1;

	pmap->pm_segtab = (struct segtab *)pool_get(&pmap_pg_pool,
	    PR_WAITOK | PR_ZERO);

	if (pmap == vmspace0.vm_map.pmap) {
		/*
		 * The initial process has already been allocated a TLBPID
		 * in mips_init().
		 */
		for (i = 0; i < ncpusfound; i++) {
			pmap->pm_asid[i].pma_asid = MIN_USER_ASID;
			pmap->pm_asid[i].pma_asidgen =
				pmap_asid_info[i].pma_asidgen;
		}
		proc0paddr->u_pcb.pcb_segtab = pmap->pm_segtab;
	} else {
		for (i = 0; i < ncpusfound; i++) {
			pmap->pm_asid[i].pma_asid = 0;
			pmap->pm_asid[i].pma_asidgen = 0;
		}
	}

	return (pmap);
}

/*
 *	Retire the given physical map from service.
 *	Should only be called if the map contains
 *	no valid mappings.
 */
void
pmap_destroy(pmap_t pmap)
{
	int s, count;

	DPRINTF(PDB_FOLLOW|PDB_CREATE, ("pmap_destroy(%p)\n", pmap));

	count = --pmap->pm_count;
	if (count > 0)
		return;

	if (pmap->pm_segtab) {
		pt_entry_t *pte;
		int i;
#ifdef PARANOIA
		int j;
#endif

		for (i = 0; i < PMAP_SEGTABSIZE; i++) {
			/* get pointer to segment map */
			pte = pmap->pm_segtab->seg_tab[i];
			if (!pte)
				continue;
#ifdef PARANOIA
			for (j = 0; j < NPTEPG; j++) {
				if (pte[j] != PG_NV)
					panic("pmap_destroy: segmap %p not empty at index %d", pte, j);
			}
#endif
			pool_put(&pmap_pg_pool, pte);
#ifdef PARANOIA
			pmap->pm_segtab->seg_tab[i] = NULL;
#endif
		}
		pool_put(&pmap_pg_pool, pmap->pm_segtab);
#ifdef PARANOIA
		pmap->pm_segtab = NULL;
#endif
	}

	s = splvm();
	pool_put(&pmap_pmap_pool, pmap);
	splx(s);
}

/*
 *	Add a reference to the specified pmap.
 */
void
pmap_reference(pmap_t pmap)
{

	DPRINTF(PDB_FOLLOW, ("pmap_reference(%p)\n", pmap));

	if (pmap)
		pmap->pm_count++;
}

/*
 *      Make a new pmap (vmspace) active for the given process.
 */
void
pmap_activate(struct proc *p)
{
	pmap_t pmap = p->p_vmspace->vm_map.pmap;
	struct cpu_info *ci = curcpu();
	uint id;

	ci->ci_curpmap = pmap;
	p->p_addr->u_pcb.pcb_segtab = pmap->pm_segtab;
	id = pmap_alloc_tlbpid(p);
	if (p == ci->ci_curproc)
		tlb_set_pid(id);
}

/*
 *      Make a previously active pmap (vmspace) inactive.
 */
void
pmap_deactivate(struct proc *p)
{
	struct cpu_info *ci = curcpu();

	ci->ci_curpmap = NULL;
}

/*
 *	Remove the given range of addresses from the specified map.
 *
 *	It is assumed that the start and end are properly
 *	rounded to the page size.
 */
void
pmap_remove(pmap_t pmap, vaddr_t sva, vaddr_t eva)
{
	vaddr_t nssva;
	pt_entry_t *pte, entry;
	paddr_t pa;
	struct cpu_info *ci = curcpu();

	DPRINTF(PDB_FOLLOW|PDB_REMOVE|PDB_PROTECT,
		("pmap_remove(%p, %p, %p)\n", pmap, (void *)sva, (void *)eva));

	stat_count(remove_stats.calls);

	if (pmap == NULL)
		return;

	KERNEL_LOCK();
	if (pmap == pmap_kernel()) {
		/* remove entries from kernel pmap */
#ifdef DIAGNOSTIC
		if (sva < VM_MIN_KERNEL_ADDRESS ||
		    eva >= VM_MAX_KERNEL_ADDRESS || eva < sva)
			panic("pmap_remove(%p, %p): not in range",
			    (void *)sva, (void *)eva);
#endif
		pte = kvtopte(sva);
		for(; sva < eva; sva += PAGE_SIZE, pte++) {
			entry = *pte;
			if (!(entry & PG_V))
				continue;
			if (entry & PG_WIRED)
				pmap->pm_stats.wired_count--;
			pmap->pm_stats.resident_count--;
			pa = pfn_to_pad(entry);
			if ((entry & PG_CACHEMODE) == PG_CACHED)
				Mips_HitSyncDCache(ci, sva, PAGE_SIZE);
			pmap_remove_pv(pmap, sva, pa);
			*pte = PG_NV | PG_G;
			/*
			 * Flush the TLB for the given address.
			 */
			pmap_invalidate_kernel_page(sva);
			stat_count(remove_stats.flushes);
		}
		KERNEL_UNLOCK();
		return;
	}

#ifdef DIAGNOSTIC
	if (eva > VM_MAXUSER_ADDRESS)
		panic("pmap_remove: uva not in range");
#endif
	while (sva < eva) {
		nssva = mips_trunc_seg(sva) + NBSEG;
		if (nssva == 0 || nssva > eva)
			nssva = eva;
		/*
		 * If VA belongs to an unallocated segment,
		 * skip to the next segment boundary.
		 */
		if (!(pte = pmap_segmap(pmap, sva))) {
			sva = nssva;
			continue;
		}
		/*
		 * Invalidate every valid mapping within this segment.
		 */
		pte += uvtopte(sva);
		for (; sva < nssva; sva += PAGE_SIZE, pte++) {
			entry = *pte;
			if (!(entry & PG_V))
				continue;
			if (entry & PG_WIRED)
				pmap->pm_stats.wired_count--;
			pmap->pm_stats.resident_count--;
			pa = pfn_to_pad(entry);
			if ((entry & PG_CACHEMODE) == PG_CACHED)
				Mips_SyncDCachePage(ci, sva, pa);
			pmap_remove_pv(pmap, sva, pa);
			*pte = PG_NV;
			/*
			 * Flush the TLB for the given address.
			 */
			pmap_invalidate_user_page(pmap, sva);
			stat_count(remove_stats.flushes);
		}
	}
	KERNEL_UNLOCK();
}

/*
 *	pmap_page_protect:
 *
 *	Lower the permission for all mappings to a given page.
 */
void
pmap_page_protect(struct vm_page *pg, vm_prot_t prot)
{
	pv_entry_t pv;
	vaddr_t va;
	int s;

	if (prot == PROT_NONE) {
		DPRINTF(PDB_REMOVE, ("pmap_page_protect(%p, 0x%x)\n", pg, prot));
	} else {
		DPRINTF(PDB_FOLLOW|PDB_PROTECT,
			("pmap_page_protect(%p, 0x%x)\n", pg, prot));
	}

	switch (prot) {
	case PROT_READ | PROT_WRITE:
	case PROT_MASK:
		break;

	/* copy_on_write */
	case PROT_READ:
	case PROT_READ | PROT_EXEC:
		pv = pg_to_pvh(pg);
		s = splvm();
		/*
		 * Loop over all current mappings setting/clearing as apropos.
		 */
		if (pv->pv_pmap != NULL) {
			for (; pv; pv = pv->pv_next) {
				va = pv->pv_va;
				pmap_protect(pv->pv_pmap, va, va + PAGE_SIZE,
				    prot);
			}
		}
		splx(s);
		break;

	/* remove_all */
	default:
		pv = pg_to_pvh(pg);
		s = splvm();
		while (pv->pv_pmap != NULL) {
			va = pv->pv_va;
			pmap_remove(pv->pv_pmap, va, va + PAGE_SIZE);
		}
		splx(s);
	}
}

/*
 *	Set the physical protection on the
 *	specified range of this map as requested.
 */
void
pmap_protect(pmap_t pmap, vaddr_t sva, vaddr_t eva, vm_prot_t prot)
{
	vaddr_t nssva;
	pt_entry_t *pte, entry, p;
	struct cpu_info *ci = curcpu();

	DPRINTF(PDB_FOLLOW|PDB_PROTECT,
		("pmap_protect(%p, %p, %p, 0x%x)\n",
		    pmap, (void *)sva, (void *)eva, prot));

	if ((prot & PROT_READ) == PROT_NONE) {
		pmap_remove(pmap, sva, eva);
		return;
	}

	p = (prot & PROT_WRITE) ? PG_M : PG_RO;

	if (pmap == pmap_kernel()) {
		/*
		 * Change entries in kernel pmap.
		 * This will trap if the page is writeable (in order to set
		 * the dirty bit) even if the dirty bit is already set. The
		 * optimization isn't worth the effort since this code isn't
		 * executed much. The common case is to make a user page
		 * read-only.
		 */
#ifdef DIAGNOSTIC
		if (sva < VM_MIN_KERNEL_ADDRESS ||
		    eva >= VM_MAX_KERNEL_ADDRESS || eva < sva)
			panic("pmap_protect(%p, %p): not in range",
			    (void *)sva, (void *)eva);
#endif
		pte = kvtopte(sva);
		for (; sva < eva; sva += PAGE_SIZE, pte++) {
			entry = *pte;
			if (!(entry & PG_V))
				continue;
			if ((entry & PG_M) != 0 /* && p != PG_M */)
				if ((entry & PG_CACHEMODE) == PG_CACHED)
					Mips_HitSyncDCache(ci, sva, PAGE_SIZE);
			entry = (entry & ~(PG_M | PG_RO)) | p;
			*pte = entry;
			/*
			 * Update the TLB if the given address is in the cache.
			 */
			pmap_update_kernel_page(sva, entry);
		}
		return;
	}

#ifdef DIAGNOSTIC
	if (eva > VM_MAXUSER_ADDRESS)
		panic("pmap_protect: uva not in range");
#endif
	while (sva < eva) {
		nssva = mips_trunc_seg(sva) + NBSEG;
		if (nssva == 0 || nssva > eva)
			nssva = eva;
		/*
		 * If VA belongs to an unallocated segment,
		 * skip to the next segment boundary.
		 */
		if (!(pte = pmap_segmap(pmap, sva))) {
			sva = nssva;
			continue;
		}
		/*
		 * Change protection on every valid mapping within this segment.
		 */
		pte += uvtopte(sva);
		for (; sva < nssva; sva += PAGE_SIZE, pte++) {
			entry = *pte;
			if (!(entry & PG_V))
				continue;
			if ((entry & PG_M) != 0 /* && p != PG_M */)
				if ((entry & PG_CACHEMODE) == PG_CACHED)
					Mips_SyncDCachePage(ci, sva,
					    pfn_to_pad(entry));
			entry = (entry & ~(PG_M | PG_RO)) | p;
			*pte = entry;
			pmap_update_user_page(pmap, sva, entry);
		}
	}
}

/*
 *	Insert the given physical page (p) at
 *	the specified virtual address (v) in the
 *	target physical map with the protection requested.
 *
 *	NB:  This is the only routine which MAY NOT lazy-evaluate
 *	or lose information.  That is, this routine must actually
 *	insert this page into the given map NOW.
 */
int
pmap_enter(pmap_t pmap, vaddr_t va, paddr_t pa, vm_prot_t prot, int flags)
{
	pt_entry_t *pte, npte;
	vm_page_t pg;
	struct cpu_info *ci = curcpu();
	u_long cpuid = ci->ci_cpuid;
	boolean_t wired = (flags & PMAP_WIRED) != 0;

	DPRINTF(PDB_FOLLOW|PDB_ENTER,
		("pmap_enter(%p, %p, %p, 0x%x, 0x%x)\n",
		    pmap, (void *)va, (void *)pa, prot, flags));

#ifdef DIAGNOSTIC
	if (pmap == pmap_kernel()) {
		stat_count(enter_stats.kernel);
		if (va < VM_MIN_KERNEL_ADDRESS ||
		    va >= VM_MAX_KERNEL_ADDRESS)
			panic("pmap_enter: kva %p", (void *)va);
	} else {
		stat_count(enter_stats.user);
		if (va >= VM_MAXUSER_ADDRESS)
			panic("pmap_enter: uva %p", (void *)va);
	}
#endif

	pg = PHYS_TO_VM_PAGE(pa);

	if (pg != NULL) {
		if (!(prot & PROT_WRITE)) {
			npte = PG_ROPAGE;
		} else {
			if (pmap == pmap_kernel()) {
				/*
				 * Don't bother to trap on kernel writes,
				 * just record page as dirty.
				 */
				npte = PG_RWPAGE;
			} else {
				if (pg->pg_flags & PGF_ATTR_MOD) {
					npte = PG_RWPAGE;
				} else {
					npte = PG_CWPAGE;
				}
			}
		}
		if (flags & PMAP_NOCACHE) {
			npte &= ~PG_CACHED;
			npte |= PG_UNCACHED;
		}

		/* Set page referenced/modified status based on flags */
		if (flags & PROT_WRITE)
			atomic_setbits_int(&pg->pg_flags,
			    PGF_ATTR_MOD | PGF_ATTR_REF);
		else if (flags & PROT_MASK)
			atomic_setbits_int(&pg->pg_flags, PGF_ATTR_REF);

		stat_count(enter_stats.managed);
	} else {
		/*
		 * Assumption: if it is not part of our managed memory
		 * then it must be device memory which may be volatile.
		 */
		stat_count(enter_stats.unmanaged);
		if (prot & PROT_WRITE) {
			npte = PG_IOPAGE & ~PG_G;
		} else {
			npte = (PG_IOPAGE | PG_RO) & ~(PG_G | PG_M);
		}
	}

	if (pmap == pmap_kernel()) {
		if (pg != NULL) {
			if (pmap_enter_pv(pmap, va, pg, &npte) != 0) {
				if (flags & PMAP_CANFAIL)
					return ENOMEM;
				panic("pmap_enter: pmap_enter_pv() failed");
			}
		}

		pte = kvtopte(va);
		if ((*pte & PG_V) && pa != pfn_to_pad(*pte)) {
			pmap_remove(pmap, va, va + PAGE_SIZE);
			stat_count(enter_stats.mchange);
		}
		if ((*pte & PG_V) == 0) {
			pmap->pm_stats.resident_count++;
			if (wired)
				pmap->pm_stats.wired_count++;
		} else {
			if ((*pte & PG_WIRED) != 0 && wired == 0)
				pmap->pm_stats.wired_count--;
			else if ((*pte & PG_WIRED) == 0 && wired != 0)
				pmap->pm_stats.wired_count++;
		}
		npte |= vad_to_pfn(pa) | PG_G;
		if (wired)
			npte |= PG_WIRED;

		/*
		 * Update the same virtual address entry.
		 */
		*pte = npte;
		pmap_update_kernel_page(va, npte);
		return 0;
	}

	/*
	 *  User space mapping. Do table build.
	 */
	if ((pte = pmap_segmap(pmap, va)) == NULL) {
		unsigned int wflags = PR_WAITOK | PR_ZERO;

		if (flags & PMAP_CANFAIL)
			wflags |= PR_LIMITFAIL;

		pte = (pt_entry_t *)pool_get(&pmap_pg_pool, wflags);
		if (pte == NULL)
			return ENOMEM;	/* can only happen if PMAP_CANFAIL */

		pmap_segmap(pmap, va) = pte;
	}

	if (pg != NULL) {
		if (pmap_enter_pv(pmap, va, pg, &npte) != 0) {
			if (flags & PMAP_CANFAIL)
				return ENOMEM;
			panic("pmap_enter: pmap_enter_pv() failed");
		}
	}

	pte += uvtopte(va);

	/*
	 * Now validate mapping with desired protection/wiring.
	 * Assume uniform modified and referenced status for all
	 * MIPS pages in a OpenBSD page.
	 */
	if ((*pte & PG_V) && pa != pfn_to_pad(*pte)) {
		pmap_remove(pmap, va, va + PAGE_SIZE);
		stat_count(enter_stats.mchange);
	}
	if ((*pte & PG_V) == 0) {
		pmap->pm_stats.resident_count++;
		if (wired)
			pmap->pm_stats.wired_count++;
	} else {
		if ((*pte & PG_WIRED) != 0 && wired == 0)
			pmap->pm_stats.wired_count--;
		else if ((*pte & PG_WIRED) == 0 && wired != 0)
			pmap->pm_stats.wired_count++;
	}

	npte |= vad_to_pfn(pa);
	if (wired)
		npte |= PG_WIRED;

	if (pmap->pm_asid[cpuid].pma_asidgen ==
	    pmap_asid_info[cpuid].pma_asidgen) {
		DPRINTF(PDB_ENTER, ("pmap_enter: new pte 0x%08x tlbpid %u\n",
			npte, pmap->pm_asid[cpuid].pma_asid));
	} else {
		DPRINTF(PDB_ENTER, ("pmap_enter: new pte 0x%08x\n", npte));
	}

#ifdef CPU_R4000
	/*
	 * If mapping an executable page, check for the R4000 EOP bug, and
	 * flag it in the pte.
	 */
	if (r4000_errata != 0) {
		if (pg != NULL && (prot & PROT_EXEC)) {
			if ((pg->pg_flags & PGF_EOP_CHECKED) == 0)
				atomic_setbits_int(&pg->pg_flags,
				     PGF_EOP_CHECKED |
				     eop_page_check(pa));

			if (pg->pg_flags & PGF_EOP_VULN)
				npte |= PG_SP;
		}
	}
#endif

	*pte = npte;
	pmap_update_user_page(pmap, va, npte);

	/*
	 * If mapping an executable page, invalidate ICache.
	 */
	if (pg != NULL && (prot & PROT_EXEC))
		Mips_InvalidateICache(ci, va, PAGE_SIZE);

	return 0;
}

void
pmap_kenter_pa(vaddr_t va, paddr_t pa, vm_prot_t prot)
{
	pt_entry_t *pte, npte;

	DPRINTF(PDB_FOLLOW|PDB_ENTER,
		("pmap_kenter_pa(%p, %p, 0x%x)\n", (void *)va, (void *)pa, prot));

#ifdef DIAGNOSTIC
	if (va < VM_MIN_KERNEL_ADDRESS ||
	    va >= VM_MAX_KERNEL_ADDRESS)
		panic("pmap_kenter_pa: kva %p", (void *)va);
#endif

	npte = vad_to_pfn(pa) | PG_G | PG_WIRED;
	if (prot & PROT_WRITE)
		npte |= PG_RWPAGE;
	else
		npte |= PG_ROPAGE;
	pte = kvtopte(va);
	if ((*pte & PG_V) == 0) {
		pmap_kernel()->pm_stats.resident_count++;
		pmap_kernel()->pm_stats.wired_count++;
	} else {
		if ((*pte & PG_WIRED) == 0)
			pmap_kernel()->pm_stats.wired_count++;
	}
	*pte = npte;
	pmap_update_kernel_page(va, npte);
}

/*
 *  Remove a mapping from the kernel map table. When doing this
 *  the cache must be synced for the VA mapped since we mapped
 *  pages behind the back of the VP tracking system.
 */
void
pmap_kremove(vaddr_t va, vsize_t len)
{
	pt_entry_t *pte, entry;
	vaddr_t eva;
	struct cpu_info *ci = curcpu();

	DPRINTF(PDB_FOLLOW|PDB_REMOVE,
		("pmap_kremove(%p, 0x%lx)\n", (void *)va, len));

	eva = va + len;
#ifdef DIAGNOSTIC
	if (va < VM_MIN_KERNEL_ADDRESS ||
	    eva >= VM_MAX_KERNEL_ADDRESS || eva < va)
		panic("pmap_kremove: va %p len %lx", (void *)va, len);
#endif
	pte = kvtopte(va);
	for (; va < eva; va += PAGE_SIZE, pte++) {
		entry = *pte;
		if (!(entry & PG_V))
			continue;
		if ((entry & PG_CACHEMODE) == PG_CACHED)
			Mips_HitSyncDCache(ci, va, PAGE_SIZE);
		*pte = PG_NV | PG_G;
		pmap_invalidate_kernel_page(va);
		pmap_kernel()->pm_stats.wired_count--;
		pmap_kernel()->pm_stats.resident_count--;
	}
}

void
pmap_unwire(pmap_t pmap, vaddr_t va)
{
	pt_entry_t *pte;

	if (pmap == pmap_kernel())
		pte = kvtopte(va);
	else {
		if ((pte = pmap_segmap(pmap, va)) == NULL)
			return;
		pte += uvtopte(va);
	}

	if (*pte & PG_V) {
		if (*pte & PG_WIRED) {
			*pte &= ~PG_WIRED;
			pmap->pm_stats.wired_count--;
		}
	}
}

/*
 *	Routine:	pmap_extract
 *	Function:
 *		Extract the physical page address associated
 *		with the given map/virtual_address pair.
 */
boolean_t
pmap_extract(pmap_t pmap, vaddr_t va, paddr_t *pap)
{
	boolean_t rv = TRUE;
	paddr_t pa;
	pt_entry_t *pte;

	if (pmap == pmap_kernel()) {
		if (IS_XKPHYS(va))
			pa = XKPHYS_TO_PHYS(va);
#ifndef CPU_R8000
		else if (va >= (vaddr_t)CKSEG0_BASE &&
		    va < (vaddr_t)CKSEG0_BASE + CKSEG_SIZE)
			pa = CKSEG0_TO_PHYS(va);
		else if (va >= (vaddr_t)CKSEG1_BASE &&
		    va < (vaddr_t)CKSEG1_BASE + CKSEG_SIZE)
			pa = CKSEG1_TO_PHYS(va);
#endif
		else {
#ifdef DIAGNOSTIC
			if (va < VM_MIN_KERNEL_ADDRESS ||
			    va >= VM_MAX_KERNEL_ADDRESS)
				panic("pmap_extract(%p, %p)", pmap, (void *)va);
#endif
			pte = kvtopte(va);
			if (*pte & PG_V)
				pa = pfn_to_pad(*pte) | (va & PAGE_MASK);
			else
				rv = FALSE;
		}
	} else {
		if (!(pte = pmap_segmap(pmap, va)))
			rv = FALSE;
		else {
			pte += uvtopte(va);
			if (*pte & PG_V)
				pa = pfn_to_pad(*pte) | (va & PAGE_MASK);
			else
				rv = FALSE;
		}
	}
	if (rv != FALSE)
		*pap = pa;

	DPRINTF(PDB_FOLLOW, ("pmap_extract(%p, %p)=%p(%d)",
		pmap, (void *)va, (void *)pa, rv));

	return (rv);
}

/*
 * Find first virtual address >= *vap that
 * will not cause cache aliases.
 */
vaddr_t
pmap_prefer(paddr_t foff, vaddr_t va)
{
	if (pmap_prefer_mask != 0)
		va += (foff - va) & pmap_prefer_mask;

	return va;
}

/*
 *	Copy the range specified by src_addr/len
 *	from the source map to the range dst_addr/len
 *	in the destination map.
 *
 *	This routine is only advisory and need not do anything.
 */
void
pmap_copy(dst_pmap, src_pmap, dst_addr, len, src_addr)
	pmap_t dst_pmap;
	pmap_t src_pmap;
	vaddr_t dst_addr;
	vsize_t len;
	vaddr_t src_addr;
{

	DPRINTF(PDB_FOLLOW,("pmap_copy(%p, %p, %p, 0x%lx, %p)\n",
	       dst_pmap, src_pmap, (void *)dst_addr, len, (void *)src_addr));
}

/*
 *	pmap_zero_page zeros the specified (machine independent) page.
 */
void
pmap_zero_page(struct vm_page *pg)
{
	paddr_t phys = VM_PAGE_TO_PHYS(pg);
	vaddr_t va;
	pv_entry_t pv;
	struct cpu_info *ci = curcpu();
	int df = 0;

	DPRINTF(PDB_FOLLOW, ("pmap_zero_page(%p)\n", (void *)phys));

	va = (vaddr_t)PHYS_TO_XKPHYS(phys, CCA_CACHED);
	pv = pg_to_pvh(pg);
	if (pg->pg_flags & PGF_UNCACHED)
		df = 1;
	else if (pg->pg_flags & PGF_CACHED) {
		df = ((pv->pv_va ^ va) & cache_valias_mask) != 0;
		if (df)
			Mips_SyncDCachePage(ci, pv->pv_va, phys);
	}
	mem_zero_page(va);
	if (df || cache_valias_mask != 0)
		Mips_HitSyncDCache(ci, va, PAGE_SIZE);

#ifdef CPU_R4000
	atomic_clearbits_int(&pg->pg_flags, PGF_EOP_CHECKED | PGF_EOP_VULN);
#endif
}

/*
 *	pmap_copy_page copies the specified (machine independent) page.
 *
 *	We do the copy phys to phys and need to check if there may be
 *	a virtual coherence problem. If so flush the cache for the
 *	areas before copying, and flush afterwards.
 */
void
pmap_copy_page(struct vm_page *srcpg, struct vm_page *dstpg)
{
	paddr_t src, dst;
	vaddr_t s, d;
	int sf, df;
	pv_entry_t pv;
	struct cpu_info *ci = curcpu();

	sf = df = 0;
	src = VM_PAGE_TO_PHYS(srcpg);
	dst = VM_PAGE_TO_PHYS(dstpg);
	s = (vaddr_t)PHYS_TO_XKPHYS(src, CCA_CACHED);
	d = (vaddr_t)PHYS_TO_XKPHYS(dst, CCA_CACHED);

	DPRINTF(PDB_FOLLOW,
		("pmap_copy_page(%p, %p)\n", (void *)src, (void *)dst));

	pv = pg_to_pvh(srcpg);
	if (srcpg->pg_flags & PGF_UNCACHED)
		sf = 1;
	else if (srcpg->pg_flags & PGF_CACHED) {
		sf = ((pv->pv_va ^ s) & cache_valias_mask) != 0;
		if (sf)
			Mips_SyncDCachePage(ci, pv->pv_va, src);
	}
	pv = pg_to_pvh(dstpg);
	if (dstpg->pg_flags & PGF_UNCACHED)
		df = 1;
	else if (dstpg->pg_flags & PGF_CACHED) {
		df = ((pv->pv_va ^ s) & cache_valias_mask) != 0;
		if (df)
			Mips_SyncDCachePage(ci, pv->pv_va, dst);
	}

	memcpy((void *)d, (void *)s, PAGE_SIZE);

	if (sf)
		Mips_HitInvalidateDCache(ci, s, PAGE_SIZE);
	if (df || cache_valias_mask != 0)
		Mips_HitSyncDCache(ci, d, PAGE_SIZE);

#ifdef CPU_R4000
	atomic_clearbits_int(&dstpg->pg_flags, PGF_EOP_CHECKED | PGF_EOP_VULN);
	atomic_setbits_int(&dstpg->pg_flags,
	    srcpg->pg_flags & (PGF_EOP_CHECKED | PGF_EOP_VULN));
#endif
}

/*
 *  Clear the modify bits on the specified physical page.
 *  Also sync the cache so it reflects the new clean state of the page.
 */
boolean_t
pmap_clear_modify(struct vm_page *pg)
{
	pv_entry_t pv;
	pt_entry_t *pte, entry;
	boolean_t rv = FALSE;
	int s;
	paddr_t pa;
	struct cpu_info *ci = curcpu();

	DPRINTF(PDB_FOLLOW,
		("pmap_clear_modify(%p)\n", (void *)VM_PAGE_TO_PHYS(pg)));

	pa = VM_PAGE_TO_PHYS(pg);
	pv = pg_to_pvh(pg);
	s = splvm();
	if (pg->pg_flags & PGF_ATTR_MOD) {
		atomic_clearbits_int(&pg->pg_flags, PGF_ATTR_MOD);
		rv = TRUE;
	}

	for (; pv != NULL; pv = pv->pv_next) {
		if (pv->pv_pmap == pmap_kernel()) {
#ifdef DIAGNOSTIC
			if (pv->pv_va < VM_MIN_KERNEL_ADDRESS ||
			    pv->pv_va >= VM_MAX_KERNEL_ADDRESS)
				panic("pmap_clear_modify(%p)",
				    (void *)pv->pv_va);
#endif
			pte = kvtopte(pv->pv_va);
			entry = *pte;
			if ((entry & PG_V) != 0 && (entry & PG_M) != 0) {
				if (pg->pg_flags & PGF_CACHED)
					Mips_HitSyncDCache(ci, pv->pv_va,
					    PAGE_SIZE);
				rv = TRUE;
				entry &= ~PG_M;
				*pte = entry;
				pmap_update_kernel_page(pv->pv_va, entry);
			}
		} else if (pv->pv_pmap != NULL) {
			if ((pte = pmap_segmap(pv->pv_pmap, pv->pv_va)) == NULL)
				continue;
			pte += uvtopte(pv->pv_va);
			entry = *pte;
			if ((entry & PG_V) != 0 && (entry & PG_M) != 0) {
				if (pg->pg_flags & PGF_CACHED)
					Mips_SyncDCachePage(ci, pv->pv_va, pa);
				rv = TRUE;
				entry &= ~PG_M;
				*pte = entry;
				pmap_update_user_page(pv->pv_pmap, pv->pv_va, entry);
			}
		}
	}
	splx(s);

	return rv;
}

void
pmap_set_modify(struct vm_page *pg)
{
	atomic_setbits_int(&pg->pg_flags, PGF_ATTR_MOD | PGF_ATTR_REF);
}

/*
 *	pmap_clear_reference:
 *
 *	Clear the reference bit on the specified physical page.
 */
boolean_t
pmap_clear_reference(struct vm_page *pg)
{
	boolean_t rv;

	DPRINTF(PDB_FOLLOW, ("pmap_clear_reference(%p)\n",
	    (void *)VM_PAGE_TO_PHYS(pg)));

	rv = (pg->pg_flags & PGF_ATTR_REF) != 0;
	atomic_clearbits_int(&pg->pg_flags, PGF_ATTR_REF);
	return rv;
}

/*
 *	pmap_is_referenced:
 *
 *	Return whether or not the specified physical page is referenced
 *	by any physical maps.
 */
boolean_t
pmap_is_referenced(struct vm_page *pg)
{
	return (pg->pg_flags & PGF_ATTR_REF) != 0;
}

/*
 *	pmap_is_modified:
 *
 *	Return whether or not the specified physical page is modified
 *	by any physical maps.
 */
boolean_t
pmap_is_modified(struct vm_page *pg)
{
	return (pg->pg_flags & PGF_ATTR_MOD) != 0;
}

/*
 * Miscellaneous support routines not part of the pmap API
 */

/*
 *	Return RO protection of page.
 */
int
pmap_is_page_ro(pmap_t pmap, vaddr_t va, pt_entry_t entry)
{
	return ((entry & PG_RO) != 0);
}

/*
 *  Walk the PV tree for a physical page and change all its
 *  mappings to cached or uncached.
 */
void
pmap_page_cache(vm_page_t pg, u_int mode)
{
	pv_entry_t pv;
	pt_entry_t *pte, entry;
	pt_entry_t newmode;
	int s;

	DPRINTF(PDB_FOLLOW|PDB_ENTER, ("pmap_page_cache(%p)\n", pg));

	newmode = mode & PGF_CACHED ? PG_CACHED : PG_UNCACHED;
	pv = pg_to_pvh(pg);

	s = splvm();
	for (; pv != NULL; pv = pv->pv_next) {
		if (pv->pv_pmap == pmap_kernel()) {
#ifdef DIAGNOSTIC
			if (pv->pv_va < VM_MIN_KERNEL_ADDRESS ||
			    pv->pv_va >= VM_MAX_KERNEL_ADDRESS)
				panic("pmap_page_cache(%p)", (void *)pv->pv_va);
#endif
			pte = kvtopte(pv->pv_va);
			entry = *pte;
			if (entry & PG_V) {
				entry = (entry & ~PG_CACHEMODE) | newmode;
				*pte = entry;
				pmap_update_kernel_page(pv->pv_va, entry);
			}
		} else if (pv->pv_pmap != NULL) {
			if ((pte = pmap_segmap(pv->pv_pmap, pv->pv_va))) {
				pte += uvtopte(pv->pv_va);
				entry = *pte;
				if (entry & PG_V) {
					entry = (entry & ~PG_CACHEMODE) | newmode;
					*pte = entry;
					pmap_update_user_page(pv->pv_pmap, pv->pv_va, entry);
				}
			}
		}
	}
	atomic_clearbits_int(&pg->pg_flags, PGF_CACHED | PGF_UNCACHED);
	atomic_setbits_int(&pg->pg_flags, mode);
	splx(s);
}

/*
 * Allocate a hardware PID and return it.
 * It takes almost as much or more time to search the TLB for a
 * specific PID and flush those entries as it does to flush the entire TLB.
 * Therefore, when we allocate a new PID, we just take the next number. When
 * we run out of numbers, we flush the TLB, increment the generation count
 * and start over. PID zero is reserved for kernel use.
 * This is called only by switch().
 */
uint
pmap_alloc_tlbpid(struct proc *p)
{
	pmap_t pmap;
	uint id;
	struct cpu_info *ci = curcpu();
	u_long cpuid = ci->ci_cpuid;

	pmap = p->p_vmspace->vm_map.pmap;
	if (pmap->pm_asid[cpuid].pma_asidgen !=
	    pmap_asid_info[cpuid].pma_asidgen) {
		id = pmap_asid_info[cpuid].pma_asid;
		if (id >= PG_ASID_COUNT) {
			tlb_asid_wrap(ci);
			/* reserve tlbpid_gen == 0 to alway mean invalid */
			if (++pmap_asid_info[cpuid].pma_asidgen == 0)
				pmap_asid_info[cpuid].pma_asidgen = 1;
			id = MIN_USER_ASID;
		}
		pmap_asid_info[cpuid].pma_asid = id + 1;
		pmap->pm_asid[cpuid].pma_asid = id;
		pmap->pm_asid[cpuid].pma_asidgen =
			pmap_asid_info[cpuid].pma_asidgen;
	} else {
		id = pmap->pm_asid[cpuid].pma_asid;
	}

	if (curproc) {
		DPRINTF(PDB_FOLLOW|PDB_TLBPID,
			("pmap_alloc_tlbpid: curproc %d '%s' ",
				curproc->p_pid, curproc->p_comm));
	} else {
		DPRINTF(PDB_FOLLOW|PDB_TLBPID,
			("pmap_alloc_tlbpid: curproc <none> "));
	}
	DPRINTF(PDB_FOLLOW|PDB_TLBPID, ("segtab %p tlbpid %u pid %d '%s'\n",
			pmap->pm_segtab, id, p->p_pid, p->p_comm));

	return (id);
}

/*
 * Enter the pmap and virtual address into the physical to virtual map table.
 */
int
pmap_enter_pv(pmap_t pmap, vaddr_t va, vm_page_t pg, pt_entry_t *npte)
{
	pv_entry_t pv, npv;
	int s;

	pv = pg_to_pvh(pg);

	s = splvm();
	if (pv->pv_pmap == NULL) {
		/*
		 * No entries yet, use header as the first entry
		 */

		DPRINTF(PDB_PVENTRY,
			("pmap_enter: first pv: pmap %p va %p pa %p\n",
				pmap, (void *)va, (void *)VM_PAGE_TO_PHYS(pg)));

		stat_count(enter_stats.firstpv);

		pv->pv_va = va;
		if (*npte & PG_CACHED)
			atomic_setbits_int(&pg->pg_flags, PGF_CACHED);
		if (*npte & PG_UNCACHED)
			atomic_setbits_int(&pg->pg_flags, PGF_UNCACHED);
		pv->pv_pmap = pmap;
		pv->pv_next = NULL;
	} else {
		/*
		 * There is at least one other VA mapping this page.
		 * We'll place this entry after the header.
		 */

		if ((pg->pg_flags & PGF_CACHED) == 0) {
			/*
			 * If page is not mapped cached it's either because
			 * an uncached mapping was explicitely requested or
			 * we have a VAC situation.
			 * Map this page uncached as well.
			 */
			*npte = (*npte & ~PG_CACHEMODE) | PG_UNCACHED;
		}

		/*
		 * The entry may already be in the list if
		 * we are only changing the protection bits.
		 */
		for (npv = pv; npv; npv = npv->pv_next) {
			if (pmap == npv->pv_pmap && va == npv->pv_va) {
				splx(s);
				return 0;
			}
		}

		DPRINTF(PDB_PVENTRY,
			("pmap_enter: new pv: pmap %p va %p pg %p\n",
			    pmap, (void *)va, (void *)VM_PAGE_TO_PHYS(pg)));

		npv = pmap_pv_alloc();
		if (npv == NULL) {
			splx(s);
			return ENOMEM;
		}

		if ((*npte & PG_CACHED) != 0 &&
		    (pg->pg_flags & PGF_CACHED) != 0 && cache_valias_mask != 0) {
			/*
			 * We have a VAC possibility.  Check if virtual
			 * address of current mappings are compatible
			 * with this new mapping. Only need to check first
			 * since all others have been checked compatible
			 * when added. If they are incompatible, update
			 * all mappings to be mapped uncached, flush the
			 * cache and set page to not be mapped cached.
			 */
			if (((pv->pv_va ^ va) & cache_valias_mask) != 0) {
#ifdef PMAPDEBUG
				printf("%s: uncaching page pa %p, va %p/%p\n",
				    __func__, (void *)VM_PAGE_TO_PHYS(pg),
				    (void *)pv->pv_va, (void *)va);
#endif
				pmap_page_cache(pg, 0);
				Mips_SyncDCachePage(curcpu(), pv->pv_va,
				    VM_PAGE_TO_PHYS(pg));
				*npte = (*npte & ~PG_CACHEMODE) | PG_UNCACHED;
			}
		}

		npv->pv_va = va;
		npv->pv_pmap = pmap;
		npv->pv_next = pv->pv_next;
		pv->pv_next = npv;

		if (!npv->pv_next)
			stat_count(enter_stats.secondpv);
	}

	splx(s);
	return 0;
}

/*
 * Remove a physical to virtual address translation from the PV table.
 */
void
pmap_remove_pv(pmap_t pmap, vaddr_t va, paddr_t pa)
{
	pv_entry_t pv, npv;
	vm_page_t pg;
	int s;

	DPRINTF(PDB_FOLLOW|PDB_PVENTRY,
		("pmap_remove_pv(%p, %p, %p)\n", pmap, (void *)va, (void *)pa));

	/*
	 * Remove page from the PV table
	 */
	pg = PHYS_TO_VM_PAGE(pa);
	if (pg == NULL)
		return;

	pv = pg_to_pvh(pg);
	s = splvm();
	/*
	 * If we are removing the first entry on the list, copy up
	 * the next entry, if any, and free that pv item since the
	 * first root item can't be freed. Else walk the list.
	 */
	if (pmap == pv->pv_pmap && va == pv->pv_va) {
		npv = pv->pv_next;
		if (npv) {
			*pv = *npv;
			pmap_pv_free(npv);
		} else {
			pv->pv_pmap = NULL;
			atomic_clearbits_int(&pg->pg_flags,
			    PG_PMAPMASK & ~PGF_PRESERVE);
		}
		stat_count(remove_stats.pvfirst);
	} else {
		for (npv = pv->pv_next; npv; pv = npv, npv = npv->pv_next) {
			stat_count(remove_stats.pvsearch);
			if (pmap == npv->pv_pmap && va == npv->pv_va)
				break;
		}
		if (npv != NULL) {
			pv->pv_next = npv->pv_next;
			pmap_pv_free(npv);
		} else {
#ifdef DIAGNOSTIC
			panic("pmap_remove_pv(%p, %p, %p) not found",
			    pmap, (void *)va, (void *)pa);
#endif
		}
	}

	if ((pg->pg_flags & (PGF_CACHED | PGF_UNCACHED)) == 0 &&
	    cache_valias_mask != 0 && pv->pv_pmap != NULL) {
		/*
		 * If this page had been mapped uncached due to aliasing,
		 * check if it can be mapped cached again after the current
		 * entry's removal.
		 */
		pv = pg_to_pvh(pg);
		va = pv->pv_va;
		for (pv = pv->pv_next; pv != NULL; pv = pv->pv_next) {
			if (((pv->pv_va ^ va) & cache_valias_mask) != 0)
				break;
		}

		if (pv == NULL) {
#ifdef PMAPDEBUG
			printf("%s: caching page pa %p, va %p again\n",
			    __func__, (void *)VM_PAGE_TO_PHYS(pg), (void *)va);
#endif
			pmap_page_cache(pg, PGF_CACHED);
		}
	}

	splx(s);
}

/*
 * Allocator for smaller-than-a-page structures pool (pm_segtab, and
 * second level page tables).  Pages backing this poll are mapped in
 * XKPHYS to avoid additional page faults when servicing a TLB miss.
 */

void *
pmap_pg_alloc(struct pool *pp, int flags, int *slowdown)
{
	vm_page_t pg;

	*slowdown = 0;
	for (;;) {
		pg = uvm_pagealloc(NULL, 0, NULL,
		    UVM_PGA_USERESERVE | UVM_PGA_ZERO);
		if (pg != NULL)
			break;

		*slowdown = 1;
		if (flags & PR_WAITOK)
			uvm_wait(__func__);
		else
			break;
	}

	if (pg != NULL)
		return (void *)PHYS_TO_XKPHYS(VM_PAGE_TO_PHYS(pg), CCA_CACHED);
	else
		return NULL;
}

void
pmap_pg_free(struct pool *pp, void *item)
{
	vaddr_t va = (vaddr_t)item;
	paddr_t pa = XKPHYS_TO_PHYS(va);
	vm_page_t pg = PHYS_TO_VM_PAGE(pa);

	if (cache_valias_mask)
		Mips_HitSyncDCache(curcpu(), va, PAGE_SIZE);
	uvm_pagefree(pg);
}

void
pmap_proc_iflush(struct proc *p, vaddr_t va, vsize_t len)
{
#ifdef MULTIPROCESSOR
	struct pmap *pmap = vm_map_pmap(&p->p_vmspace->vm_map);
	CPU_INFO_ITERATOR cii;
	struct cpu_info *ci;

	CPU_INFO_FOREACH(cii, ci) {
		if (ci->ci_curpmap == pmap) {
			Mips_InvalidateICache(ci, va, len);
			break;
		}
	}
#else
	Mips_InvalidateICache(curcpu(), va, len);
#endif
}

#ifdef __HAVE_PMAP_DIRECT
vaddr_t
pmap_map_direct(vm_page_t pg)
{
	paddr_t pa = VM_PAGE_TO_PHYS(pg);
	vaddr_t va;

#ifdef __sgi__
#ifndef CPU_R8000
	/*
	 * Return a CKSEG0 address whenever possible.
	 */
	if (pa < CKSEG_SIZE)
		va = PHYS_TO_CKSEG0(pa);
	else
#endif
		va = PHYS_TO_XKPHYS(pa, CCA_CACHED);
#else
	va = PHYS_TO_XKPHYS(pa, CCA_CACHED);
#endif

	return va;
}

vm_page_t
pmap_unmap_direct(vaddr_t va)
{
	paddr_t pa;
	vm_page_t pg;

#ifdef __sgi__
#ifndef CPU_R8000
	if (va >= CKSEG0_BASE)
		pa = CKSEG0_TO_PHYS(va);
	else
#endif
		pa = XKPHYS_TO_PHYS(va);
#else
	pa = XKPHYS_TO_PHYS(va);
#endif

	pg = PHYS_TO_VM_PAGE(pa);
	if (cache_valias_mask)
		Mips_HitSyncDCache(curcpu(), va, PAGE_SIZE);

	return pg;
}
#endif

void
pmap_update(struct pmap *pmap)
{
	Mips_SyncICache(curcpu());
}


code:
/*	$OpenBSD: process_machdep.c,v 1.16 2010/11/24 21:01:03 miod Exp $	*/

/*
 * Copyright (c) 1994 Adam Glass
 * Copyright (c) 1993 The Regents of the University of California.
 * Copyright (c) 1993 Jan-Simon Pendry
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Jan-Simon Pendry.
 *
 * 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 the University of
 *	California, Berkeley and its contributors.
 * 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.
 *
 * From:
 *	Id: procfs_i386.c,v 4.1 1993/12/17 10:47:45 jsp Rel
 *
 *	$Id: process_machdep.c,v 1.16 2010/11/24 21:01:03 miod Exp $
 */

/*
 * This file may seem a bit stylized, but that so that it's easier to port.
 * Functions to be implemented here are:
 *
 * process_read_regs(proc, regs)
 *	Get the current user-visible register set from the process
 *	and copy it into the regs structure (<machine/reg.h>).
 *	The process is stopped at the time read_regs is called.
 *
 * process_write_regs(proc, regs)
 *	Update the current register set from the passed in regs
 *	structure.  Take care to avoid clobbering special CPU
 *	registers or privileged bits in the PSL.
 *	The process is stopped at the time write_regs is called.
 *
 * process_sstep(proc)
 *	Arrange for the process to trap after executing a single instruction.
 *
 * process_set_pc(proc)
 *	Set the process's program counter.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/ptrace.h>
#include <machine/fpu.h>
#include <machine/frame.h>
#include <machine/reg.h>

#define	REGSIZE (sizeof(struct trap_frame) - sizeof(register_t))

int
process_read_regs(p, regs)
	struct proc *p;
	struct reg *regs;
{
	struct cpu_info *ci = curcpu();

	if (p == ci->ci_fpuproc)
		save_fpu();

	bcopy(&p->p_md.md_regs->ast, &regs->r_regs[AST], REGSIZE);
	regs->r_regs[ZERO] = 0;
	return (0);
}

#ifdef	PTRACE

int
process_write_regs(p, regs)
	struct proc *p;
	struct reg *regs;
{
	struct cpu_info *ci = curcpu();
	register_t sr, ic, ipl;

	if (p == ci->ci_fpuproc)
		save_fpu();

	sr = p->p_md.md_regs->sr;
	ic = p->p_md.md_regs->ic;
	ipl = p->p_md.md_regs->ipl;
	bcopy(&regs->r_regs[AST], &p->p_md.md_regs->ast, REGSIZE);
	p->p_md.md_regs->fsr &= ~FPCSR_C_MASK;
	p->p_md.md_regs->sr = sr;
	p->p_md.md_regs->ic = ic;
	p->p_md.md_regs->ipl = ipl;
	return (0);
}

/* process_sstep is in trap.c */

int
process_set_pc(p, addr)
	struct proc *p;
	caddr_t addr;
{
	p->p_md.md_regs->pc = (register_t)addr;
	return (0);
}

#endif	/* PTRACE */


code:
/*	$OpenBSD: r4000_errata.c,v 1.5 2014/11/16 12:30:58 deraadt Exp $	*/

/*
 * Copyright (c) 2014 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * The following routines attempt to workaround the `end-of-page' errata
 * affecting R4000 processors rev < 3.
 *
 * This particular errata, scarcely documented as errata #4 and #14 in the
 * `R4000PC, R4000SC Errata, Processor Revision 2.2 and 3.0' document,
 * is not recoverable.
 *
 *
 * This errata is triggered by branch instructions in the last word of a
 * page, when the next page (containing the delay slot instruction) causes
 * a TLB miss.  The only safe way to avoid it is to have the toolchain
 * force all branch instructions to be aligned on 8-byte boundaries, but
 * that wouldn't prevent a rogue binary (or just-in-time compilation) to
 * fail this requirement.
 *
 * The following logic is a ``best effort'' (well, ok, ``lazy man's effort'')
 * at trying to prevent the errata from triggering. It will not be enough
 * when confronted to a carefully crafted binary (but then, there are easier
 * way to get kernel mode privileges from userland, when running on the R4000
 * processors vulnerable to the end-of-page errata, so why bother?). Yet,
 * experience has shown this code is surprisingly good enough to allow for
 * regular binaries to run, with a minimal performance hit.
 *
 *
 * The idea behind this code is simple:
 * - executable pages are checked - with eop_page_check() - for a branch in
 *   their last word. If they are vulnerable to this errata, page table entries
 *   for these pages get the `special' bit set.
 * - tlb miss handlers will check for the `special' bit set in the pte and
 *   will always defer to the C code in trap() in that case. trap() will
 *   then invoke eop_tlb_miss_handler(), which will 1) force the next page
 *   to be faulted in, and 2) set up wired TLB entries for both the vulnerable
 *   page and the next page (and their neighbors if they do not share the same
 *   TLB pair), so that there is no risk of a TLB miss when the branch
 *   instruction is reached.
 * - context switches will remove these wired entries.
 * - tlb modification handlers will check for the current exception PC, and
 *   will remove the wired entries if the exception PC is no longer in the
 *   vulnerable page.
 *
 *
 * There are a few limitations:
 * - heavy paging may cause the page next to a vulnerable page to be swapped
 *   out (this code does not attempt to wire the vm_page). It would be worth
 *   mapping a page full of special break instructions when the page gets
 *   swapped out.
 * - there might be other vulnerable pages in the wired tlb entries being
 *   set up. It should be simple enough to walk the next pages until the last
 *   would-be-wired TLB pair contains two safe pages.  However, the amount of
 *   TLB is quite limited, so a limit has to be set at some point.
 * - no effort has been put to catch executable pages being temporarily made
 *   writable, then vulnerable (by putting a branch instruction in the last
 *   word). This is unlikely to happen (except for just-in-time compilation).
 *
 *
 * Note that, by using 16KB page sizes, the number of vulnerable pages is
 * reduced.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/user.h>

#include <machine/cpu.h>

#include <uvm/uvm_extern.h>

int	r4000_errata;

static inline void eop_undo(struct pcb *);

static inline void
eop_undo(struct pcb *pcb)
{
	tlb_set_wired(UPAGES / 2);
	tlb_flush((UPAGES / 2) + pcb->pcb_nwired);
	pcb->pcb_nwired = 0;
}

/*
 * Check for an R4000 end-of-page errata condition in an executable code page.
 * Returns a bitmask to set in the given page pg_flags.
 */
u_int
eop_page_check(paddr_t pa)
{
	uint32_t insn;

	insn = *(uint32_t *)PHYS_TO_XKPHYS(pa + PAGE_SIZE - 4, CCA_CACHED);
	if (classify_insn(insn) != INSNCLASS_NEUTRAL)
		return PGF_EOP_VULN;

	return 0;
}

/*
 * Invalidate a TLB entry. If it is part of a wired pair, drop all wired
 * entries.
 *
 * Note that, in case of heavy swapping, this can cause the page following
 * a vulnerable page to be swapped out and immediately faulted back in,
 * iff the userland pc is in the vulnerable page. Help me, Obi Wan LRU.
 * You are my only hope.
 */
void
eop_tlb_flush_addr(struct pmap *pmap, vaddr_t va, u_long asid)
{
	struct proc *p = curproc;
	struct pcb *pcb;

	if (p->p_vmspace->vm_map.pmap == pmap) {
		pcb = &p->p_addr->u_pcb;
		if (pcb->pcb_nwired != 0 &&
		    (va - pcb->pcb_wiredva) < ptoa(pcb->pcb_nwired * 2)) {
			eop_undo(pcb);
			return;
		}
	}

	tlb_flush_addr(va | asid);
}

/*
 * Handle a TLB miss exception for a page marked as able to trigger the
 * end-of-page errata.
 * Returns nonzero if the exception has been completely serviced, and no
 * further processing in the trap handler is necessary.
 */
int
eop_tlb_miss_handler(struct trap_frame *trapframe, struct cpu_info *ci,
    struct proc *p)
{
	struct pcb *pcb;
	vaddr_t va, faultva;
	struct vmspace *vm;
	vm_map_t map;
	pmap_t pmap;
	pt_entry_t *pte, entry;
	int onfault;
	u_long asid;
	uint i, npairs;
	int64_t tlbidx;

	/*
	 * Check for a valid pte with the `special' bit set (PG_SP)
	 * in order to apply the end-of-page errata workaround.
	 */

	vm = p->p_vmspace;
	map = &vm->vm_map;
	faultva = trunc_page((vaddr_t)trapframe->badvaddr);
	pmap = map->pmap;

	pte = pmap_segmap(pmap, faultva);
	if (pte == NULL)
		return 0;

	pte += uvtopte(faultva);
	entry = *pte;
	if ((entry & PG_SP) == 0)
		return 0;

	pcb = &p->p_addr->u_pcb;
	asid = pmap->pm_asid[ci->ci_cpuid].pma_asid << PG_ASID_SHIFT;

	/*
	 * For now, only allow one EOP vulnerable page to get a wired TLB
	 * entry.  We will aggressively attempt to recycle the wired TLB
	 * entries created for that purpose, as soon as we are no longer
	 * needing the EOP page resident in the TLB.
	 */

	/*
	 * Figure out how many pages to wire in the TLB.
	 */

	if ((faultva & PG_ODDPG) != 0) {
		/* odd page: need two pairs */
		npairs = 2;
	} else {
		/* even page: only need one pair */
		npairs = 1;
	}

	/*
	 * Fault-in the next page.
	 */

	va = faultva + PAGE_SIZE;
	pte = pmap_segmap(pmap, va);
	if (pte != NULL)
		pte += uvtopte(va);

	if (pte == NULL || (*pte & PG_V) == 0) {
		onfault = pcb->pcb_onfault;
		pcb->pcb_onfault = 0;
		KERNEL_LOCK();
		(void)uvm_fault(map, va, 0, PROT_READ | PROT_EXEC);
		KERNEL_UNLOCK();
		pcb->pcb_onfault = onfault;
	}

	/*
	 * Clear possible TLB entries for the pages we're about to wire.
	 */

	for (i = npairs, va = faultva & PG_HVPN; i != 0;
	    i--, va += 2 * PAGE_SIZE) {
		tlbidx = tlb_probe(va | asid);
		if (tlbidx >= 0)
			tlb_update_indexed(CKSEG0_BASE, PG_NV, PG_NV, tlbidx);
	}

	/*
	 * Reserve the extra wired TLB, and fill them with the existing ptes.
	 */

	tlb_set_wired((UPAGES / 2) + npairs);
	for (i = 0, va = faultva & PG_HVPN; i != npairs;
	    i++, va += 2 * PAGE_SIZE) {
		pte = pmap_segmap(pmap, va);
		if (pte == NULL)
			tlb_update_indexed(va | asid,
			    PG_NV, PG_NV, (UPAGES / 2) + i);
		else {
			pte += uvtopte(va);
			tlb_update_indexed(va | asid,
			    pte[0], pte[1], (UPAGES / 2) + i);
		}
	}

	/*
	 * Save the base address of the EOP vulnerable page, to be able to
	 * figure out when the wired entry is no longer necessary.
	 */

	pcb->pcb_nwired = npairs;
	pcb->pcb_wiredva = faultva & PG_HVPN;
	pcb->pcb_wiredpc = faultva;

	return 1;
}

/*
 * Attempt to cleanup the current end-of-page errata workaround, if the
 * current pc is no longer in an errata vulnerable page.
 */
void
eop_cleanup(struct trap_frame *trapframe, struct proc *p)
{
	struct pcb *pcb;

	pcb = &p->p_addr->u_pcb;
	if (pcb->pcb_nwired != 0) {
		if (trunc_page(trapframe->pc) != pcb->pcb_wiredpc)
			eop_undo(pcb);
	}
}


code:
/*	$OpenBSD: sendsig.c,v 1.24 2015/02/09 08:48:23 miod Exp $ */

/*
 * Copyright (c) 1990 The Regents of the University of California.
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * William Jolitz and Don Ahn.
 *
 * 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 the University of
 *      California, Berkeley and its contributors.
 * 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.
 *
 */
/*
 * Copyright (c) 2001 Opsycon AB  (www.opsycon.se)
 *
 * 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 AUTHOR ``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 AUTHOR 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/signalvar.h>
#include <sys/proc.h>
#include <sys/exec.h>
#include <sys/mount.h>
#include <sys/syscallargs.h>

#include <uvm/uvm_extern.h>

#include <machine/regnum.h>
#include <mips64/mips_cpu.h>

/*
 * WARNING: code in locore.s assumes the layout shown for sf_signum
 * thru sf_handler so... don't screw with them!
 */
struct sigframe {
	int	sf_signum;		/* signo for handler */
	siginfo_t *sf_sip;		/* pointer to siginfo_t */
	struct	sigcontext *sf_scp;	/* context ptr for handler */
	sig_t	sf_handler;		/* handler addr for u_sigc */
	struct	sigcontext sf_sc;	/* actual context */
	siginfo_t sf_si;
};

#ifdef DEBUG
int sigdebug = 0;
pid_t sigpid = 0;
#define SDB_FOLLOW	0x01
#define SDB_KSTACK	0x02
#define SDB_FPSTATE	0x04
#endif

/*
 * Send an interrupt to process.
 */
void
sendsig(catcher, sig, mask, code, type, val)
	sig_t catcher;
	int sig, mask;
	u_long code;
	int type;
	union sigval val;
{
	struct cpu_info *ci = curcpu();
	struct proc *p = curproc;
	struct sigframe *fp;
	struct trap_frame *regs;
	struct sigacts *psp = p->p_p->ps_sigacts;
	int fsize;
	struct sigcontext ksc;

	regs = p->p_md.md_regs;

	/*
	 * Allocate space for the signal handler context.
	 */
	fsize = sizeof(struct sigframe);
	if (!(psp->ps_siginfo & sigmask(sig)))
		fsize -= sizeof(siginfo_t);
	if ((p->p_sigstk.ss_flags & SS_DISABLE) == 0 &&
	    !sigonstack(regs->sp) && (psp->ps_sigonstack & sigmask(sig)))
		fp = (struct sigframe *)(p->p_sigstk.ss_sp +
					 p->p_sigstk.ss_size - fsize);
	else
		fp = (struct sigframe *)(regs->sp - fsize);
#ifdef DEBUG
	if ((sigdebug & SDB_FOLLOW) ||
	    ((sigdebug & SDB_KSTACK) && (p->p_pid == sigpid)))
		printf("sendsig(%d): sig %d ssp %p usp %p scp %p\n",
		       p->p_pid, sig, &ksc, fp, &fp->sf_sc);
#endif
	/*
	 * Build the signal context to be used by sigreturn.
	 */
	bzero(&ksc, sizeof(ksc));
	ksc.sc_mask = mask;
	ksc.sc_pc = regs->pc;
	ksc.mullo = regs->mullo;
	ksc.mulhi = regs->mulhi;
	ksc.sc_regs[ZERO] = 0xACEDBADE;		/* magic number */
	bcopy((caddr_t)&regs->ast, (caddr_t)&ksc.sc_regs[1],
		sizeof(ksc.sc_regs) - sizeof(register_t));
	ksc.sc_fpused = p->p_md.md_flags & MDP_FPUSED;
	if (ksc.sc_fpused) {
		/* if FPU has current state, save it first */
		if (p == ci->ci_fpuproc)
			save_fpu();

		bcopy((caddr_t)&p->p_md.md_regs->f0, (caddr_t)ksc.sc_fpregs,
			sizeof(ksc.sc_fpregs));
	}

	if (psp->ps_siginfo & sigmask(sig)) {
		siginfo_t si;

		initsiginfo(&si, sig, code, type, val);
		if (copyout((caddr_t)&si, (caddr_t)&fp->sf_si, sizeof si))
			goto bail;
	}

	if (copyout((caddr_t)&ksc, (caddr_t)&fp->sf_sc, sizeof(ksc))) {
bail:
		/*
		 * Process has trashed its stack; give it an illegal
		 * instruction to halt it in its tracks.
		 */
		sigexit(p, SIGILL);
		/* NOTREACHED */
	}
	/*
	 * Build the argument list for the signal handler.
	 */
	regs->a0 = sig;
	regs->a1 = (psp->ps_siginfo & sigmask(sig)) ? (register_t)&fp->sf_si : 0;
	regs->a2 = (register_t)&fp->sf_sc;
	regs->a3 = (register_t)catcher;

	regs->pc = (register_t)catcher;
	regs->t9 = (register_t)catcher;
	regs->sp = (register_t)fp;

	regs->ra = p->p_p->ps_sigcode;
#ifdef DEBUG
	if ((sigdebug & SDB_FOLLOW) ||
	    ((sigdebug & SDB_KSTACK) && (p->p_pid == sigpid)))
		printf("sendsig(%d): sig %d returns\n",
		       p->p_pid, sig);
#endif
}

/*
 * System call to cleanup state after a signal
 * has been taken.  Reset signal mask and
 * stack state from context left by sendsig (above).
 * Return to previous pc and psl as specified by
 * context left by sendsig. Check carefully to
 * make sure that the user has not modified the
 * psl to gain improper privileges or to cause
 * a machine fault.
 */
/* ARGSUSED */
int
sys_sigreturn(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct cpu_info *ci = curcpu();
	struct sys_sigreturn_args /* {
		syscallarg(struct sigcontext *) sigcntxp;
	} */ *uap = v;
	struct sigcontext *scp;
	struct trap_frame *regs;
	struct sigcontext ksc;
	int error;

	scp = SCARG(uap, sigcntxp);
#ifdef DEBUG
	if (sigdebug & SDB_FOLLOW)
		printf("sigreturn: pid %d, scp %p\n", p->p_pid, scp);
#endif
	regs = p->p_md.md_regs;
	/*
	 * Test and fetch the context structure.
	 * We grab it all at once for speed.
	 */
	error = copyin((caddr_t)scp, (caddr_t)&ksc, sizeof(ksc));
	if (error || ksc.sc_regs[ZERO] != 0xACEDBADE) {
#ifdef DEBUG
		if (!(sigdebug & SDB_FOLLOW))
			printf("sigreturn: pid %d, scp %p\n", p->p_pid, scp);
		printf("  old sp %lx ra %lx pc %lx\n",
			regs->sp, regs->ra, regs->pc);
		printf("  new sp %lx ra %lx pc %lx err %d z %lx\n",
			ksc.sc_regs[SP], ksc.sc_regs[RA], ksc.sc_regs[PC],
			error, ksc.sc_regs[ZERO]);
#endif
		return (EINVAL);
	}
	scp = &ksc;
	/*
	 * Restore the user supplied information
	 */
	p->p_sigmask = scp->sc_mask &~ sigcantmask;
	regs->pc = scp->sc_pc;
	regs->mullo = scp->mullo;
	regs->mulhi = scp->mulhi;
	regs->sr &= ~SR_COP_1_BIT;	/* Zap current FP state */
	if (p == ci->ci_fpuproc)
		ci->ci_fpuproc = NULL;
	bcopy((caddr_t)&scp->sc_regs[1], (caddr_t)&regs->ast,
		sizeof(scp->sc_regs) - sizeof(register_t));
	if (scp->sc_fpused)
		bcopy((caddr_t)scp->sc_fpregs, (caddr_t)&p->p_md.md_regs->f0,
			sizeof(scp->sc_fpregs));
	return (EJUSTRETURN);
}


code:
/*	$OpenBSD: softintr.c,v 1.18 2014/09/30 06:51:58 jmatthew Exp $	*/
/*	$NetBSD: softintr.c,v 1.2 2003/07/15 00:24:39 lukem Exp $	*/

/*
 * Copyright (c) 2001 Wasabi Systems, Inc.
 * All rights reserved.
 *
 * Written by Jason R. Thorpe for Wasabi Systems, Inc.
 *
 * 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 for the NetBSD Project by
 *	Wasabi Systems, Inc.
 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
 *    or promote products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
 * 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/mutex.h>
#include <sys/malloc.h>
#include <sys/atomic.h>

#include <uvm/uvm_extern.h>

#include <machine/intr.h>
#ifdef MULTIPROCESSOR
#include <mips64/mips_cpu.h>
#endif

struct soft_intrq soft_intrq[SI_NQUEUES];

void	netintr(void);

/*
 * Initialize the software interrupt system.
 */
void
softintr_init(void)
{
	struct soft_intrq *siq;
	int i;

	for (i = 0; i < SI_NQUEUES; i++) {
		siq = &soft_intrq[i];
		TAILQ_INIT(&siq->siq_list);
		siq->siq_si = i;
		mtx_init(&siq->siq_mtx, IPL_HIGH);
	}
}

/*
 * Process pending software interrupts on the specified queue.
 *
 * NOTE: We must already be at the correct interrupt priority level.
 */
void
softintr_dispatch(int si)
{
	struct soft_intrq *siq = &soft_intrq[si];
	struct soft_intrhand *sih;

	for (;;) {
		mtx_enter(&siq->siq_mtx);
		sih = TAILQ_FIRST(&siq->siq_list);
		if (sih == NULL) {
			mtx_leave(&siq->siq_mtx);
			break;
		}

		TAILQ_REMOVE(&siq->siq_list, sih, sih_list);
		sih->sih_pending = 0;

		atomic_inc_int(&uvmexp.softs);

		mtx_leave(&siq->siq_mtx);

		(*sih->sih_func)(sih->sih_arg);
	}
}

/*
 * Register a software interrupt handler.
 */
void *
softintr_establish(int ipl, void (*func)(void *), void *arg)
{
	struct soft_intrhand *sih;
	int si;

	switch (ipl) {
	case IPL_SOFT:
		si = SI_SOFT;
		break;
	case IPL_SOFTCLOCK:
		si = SI_SOFTCLOCK;
		break;
	case IPL_SOFTNET:
		si = SI_SOFTNET;
		break;
	case IPL_TTY:			/* XXX until MI code is fixed */
	case IPL_SOFTTTY:
		si = SI_SOFTTTY;
		break;
	default:
		printf("softintr_establish: unknown soft IPL %d\n", ipl);
		return NULL;
	}

	sih = malloc(sizeof(*sih), M_DEVBUF, M_NOWAIT);
	if (__predict_true(sih != NULL)) {
		sih->sih_func = func;
		sih->sih_arg = arg;
		sih->sih_siq = &soft_intrq[si];
		sih->sih_pending = 0;
	}
	return (sih);
}

/*
 * Unregister a software interrupt handler.
 */
void
softintr_disestablish(void *arg)
{
	struct soft_intrhand *sih = arg;
	struct soft_intrq *siq = sih->sih_siq;

	mtx_enter(&siq->siq_mtx);
	if (sih->sih_pending) {
		TAILQ_REMOVE(&siq->siq_list, sih, sih_list);
		sih->sih_pending = 0;
	}
	mtx_leave(&siq->siq_mtx);

	free(sih, M_DEVBUF, 0);
}

/*
 * Schedule a software interrupt.
 */
void
softintr_schedule(void *arg)
{
	struct cpu_info *ci = curcpu();
	struct soft_intrhand *sih = (struct soft_intrhand *)arg;
	struct soft_intrq *siq = sih->sih_siq;

	mtx_enter(&siq->siq_mtx);
	if (sih->sih_pending == 0) {
		TAILQ_INSERT_TAIL(&siq->siq_list, sih, sih_list);
		sih->sih_pending = 1;
		atomic_setbits_int(&ci->ci_softpending, SINTMASK(siq->siq_si));
	}
	mtx_leave(&siq->siq_mtx);
}

void
dosoftint()
{
	struct cpu_info *ci = curcpu();
	int sir, q, mask;
#ifdef MULTIPROCESSOR
	register_t sr;

	/* Enable interrupts */
	sr = getsr();
	ENABLEIPI();
	__mp_lock(&kernel_lock);
#endif

	while ((sir = ci->ci_softpending) != 0) {
		atomic_clearbits_int(&ci->ci_softpending, sir);

		for (q = SI_NQUEUES - 1; q >= 0; q--) {
			mask = SINTMASK(q);
			if (sir & mask)
				softintr_dispatch(q);
		}
	}

#ifdef MULTIPROCESSOR
	__mp_unlock(&kernel_lock);
	setsr(sr);
#endif
}


code:
/*	$OpenBSD: sys_machdep.c,v 1.8 2014/04/03 08:07:16 mpi Exp $	*/

/*
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Ralph Campbell.
 *
 * 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 the University of
 *	California, Berkeley and its contributors.
 * 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.
 *
 *	@(#)sys_machdep.c	8.1 (Berkeley) 6/10/93
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/ioctl.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/proc.h>
#include <sys/kernel.h>
#include <sys/buf.h>

#include <sys/mount.h>
#include <sys/syscallargs.h>

#include <uvm/uvm_extern.h>

#include <mips64/cache.h>
#include <mips64/sysarch.h>

#include <machine/autoconf.h>

int	mips64_cacheflush(struct proc *, struct mips64_cacheflush_args *);

int
sys_sysarch(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct sys_sysarch_args /* {
		syscallarg(int) op;
		syscallarg(char *) parms;
	} */ *uap = v;
	int error = 0;

	switch(SCARG(uap, op)) {
	case MIPS64_CACHEFLUSH:
	    {
		struct mips64_cacheflush_args cfa;

		if ((error = copyin(SCARG(uap, parms), &cfa, sizeof cfa)) != 0)
			return error;
		error = mips64_cacheflush(p, &cfa);
	    }
		break;
	default:
		error = EINVAL;
		break;
	}

	return error;
}

int
mips64_cacheflush(struct proc *p, struct mips64_cacheflush_args *cfa)
{
	vaddr_t va;
	paddr_t pa;
	size_t sz, chunk;
	struct vm_map *map = &p->p_vmspace->vm_map;
	struct pmap *pm = map->pmap;
	struct vm_map_entry *entry;
	int rc = 0;

	/*
	 * Sanity checks.
	 */
	if ((cfa->which & BCACHE) != cfa->which)
		return EINVAL;

	if (cfa->which == 0)
		return 0;

	va = cfa->va;
	sz = cfa->sz;
	chunk = PAGE_SIZE - (va & PAGE_MASK);
	vm_map_lock_read(map);
	if (va < vm_map_min(map) || va + sz > vm_map_max(map) || va + sz < va)
		rc = EFAULT;
	else while (sz != 0) {
		if (chunk > sz)
			chunk = sz;

		/*
		 * Check for a resident mapping first, this is faster than
		 * uvm_map_lookup_entry().
		 */
		if (pmap_extract(pm, va, &pa) != FALSE) {
			if (cfa->which & ICACHE)
				Mips_InvalidateICache(p->p_cpu, va, chunk);
			if (cfa->which & DCACHE)
				Mips_HitSyncDCache(p->p_cpu, va, chunk);
		} else {
			if (uvm_map_lookup_entry(map, va, &entry) == FALSE) {
				rc = EFAULT;
				break;
			}
			/* else simply not resident at the moment */
		}

		va += chunk;
		sz -= chunk;
		chunk = PAGE_SIZE;
	}
	vm_map_unlock_read(map);

	return rc;
}


code:
/*	$OpenBSD: tlb_tfp.S,v 1.3 2014/02/08 09:34:04 miod Exp $	*/

/*
 * Copyright (c) 2012 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <machine/param.h>
#include <machine/asm.h>
#include <machine/cpu.h>
#include <mips64/mips_cpu.h>
#include <machine/pte.h>

#ifdef MIPS_PTE64
#error "R8000 doesn't need 64-bit PTE"
#endif

#include "assym.h"

#define	TLBR	.align 4; .word 0x43000001; SSNOP; SSNOP; SSNOP; SSNOP
#define	TLBW	.align 4; .word 0x43000002; SSNOP; SSNOP; SSNOP
#define	TLBP	.align 4; .word 0x43000008; SSNOP; SSNOP; SSNOP; SSNOP

#ifdef TLB_DEBUG
#define TLBOP(crounha)	Xtlb_##crounha
#else
#define TLBOP(crounha)	tlb_##crounha
#endif

	.set	mips4
	.set	noreorder

/*
 * void tlb_flush(int);
 * Flush all the TLB entries. Argument is ignored (128x3 layout is hardcoded).
 * Assumes there are no wired entries.
 */
LEAF(TLBOP(flush), 0)	/* { */
	DMFC0	v1, COP_0_STATUS_REG	# Save the status register.
	MFC0_HAZARD
	ori	v0, v1, SR_INT_ENAB
	xori	v0, SR_INT_ENAB
	DMTC0	v0, COP_0_STATUS_REG	# Disable interrupts
	MTC0_SR_IE_HAZARD

	LI	v0, 01
	dsll	v0, v0, 62		# KV0 base
	DMFC0	a2, COP_0_TLB_HI	# Read current ASID
	MFC0_HAZARD
	LI	a1, 2			# TLB set counter
1:
	DMTC0	a1, COP_0_TLB_SET
	MTC0_HAZARD
	LI	a0, 127			# TLB index counter
2:
	DMTC0	zero, COP_0_TLB_LO
	MTC0_HAZARD
	DMTC0	v0, COP_0_TLB_HI
	MTC0_HAZARD
	DMTC0	v0, COP_0_VADDR
	MTC0_HAZARD

	TLBW
	beqz	a0, 3f
	 addu	v0, PAGE_SIZE		# Make sure no duplicates will exist
	b	2b
	 subu	a0, 1
3:
	beqz	a1, 4f
	 NOP
	b	1b
	 subu	a1, 1
4:
	DMTC0	a2, COP_0_TLB_HI	# Restore current ASID
	MTC0_HAZARD
	MTC0_SR_IE_HAZARD
	DMTC0	v1, COP_0_STATUS_REG	# Restore the status register
	MTC0_SR_IE_HAZARD

	j	ra
	 NOP
END(TLBOP(flush))	/* } */

/*
 * void tlb_flush_addr(vaddr_t);
 * Flush matching TLB entry, if any, for the given address and ASID (the
 * ASID bits are in the same position as in EntryHi).
 */
LEAF(TLBOP(flush_addr), 0)	/* { */
	DMFC0	v1, COP_0_STATUS_REG	# Save the status register.
	MFC0_HAZARD
	ori	v0, v1, SR_INT_ENAB
	xori	v0, v0, SR_INT_ENAB
	DMTC0	v0, COP_0_STATUS_REG	# Disable interrupts
	MTC0_SR_IE_HAZARD
	DMFC0	a2, COP_0_TLB_HI	# Read current ASID
	MFC0_HAZARD

	/*
	 * NOTE: I used to only write the ASID bits of TLB_HI and not bother
	 * about the VPN bits (since we are about to invalidate that entry
	 * anyway), with this:
	 *	and	a1, a0, PG_ASID_MASK
	 *	DMTC0	a1, COP_0_TLB_HI
	 *
	 * It turns out that this was a VERY BAD idea. Doing this seems to
	 * work, but leaves the TLB in an inconsistent state (although
	 * reading the TLB entry afterwards does not report anything wrong),
	 * and eventually causes TLBX exceptions - although, at the time of
	 * the exception, the TLB values obtained by TLBR are consistent and
	 * no duplicate is to be found.
	 * -- miod
	 */
	DMTC0	a0, COP_0_TLB_HI
	MTC0_HAZARD
	DMTC0	a0, COP_0_VADDR
	MTC0_HAZARD

	TLBP
	DMFC0	v0, COP_0_TLB_SET
	MFC0_HAZARD

	bltz	v0, 1f			# Missing
	 NOP

	DMTC0	zero, COP_0_TLB_LO
	MTC0_HAZARD
	TLBW

1:
	DMTC0	a2, COP_0_TLB_HI	# restore PID
	MTC0_HAZARD
	DMTC0	v1, COP_0_STATUS_REG	# Restore the status register
	MTC0_SR_IE_HAZARD
	j	ra
	 NOP
END(TLBOP(flush_addr))	/* } */

/*
 * void tlb_update(vaddr_t, register_t);
 * Update (or create) TLB entry for given address and ASID, and PTE.
 */
LEAF(TLBOP(update), 0)	/* { */
	DMFC0	v1, COP_0_STATUS_REG	# Save the status register.
	MFC0_HAZARD
	ori	v0, v1, SR_INT_ENAB
	xori	v0, v0, SR_INT_ENAB
	DMTC0	v0, COP_0_STATUS_REG	# Disable interrupts
	MTC0_SR_IE_HAZARD
	DMFC0	a2, COP_0_TLB_HI	# Read current ASID
	MFC0_HAZARD

	DMTC0	a0, COP_0_TLB_HI
	MTC0_HAZARD
	DMTC0	a0, COP_0_VADDR
	MTC0_HAZARD

	TLBP
	DMFC0	v0, COP_0_TLB_SET
	MFC0_HAZARD

	bgez	v0, 1f
	 NOP

	/* Page not found: pick a random set */
	DMFC0	v0, COP_0_COUNT
	MFC0_HAZARD
	and	v0, 3
	beqz	v0, 9f
	 NOP
	subu	v0, 1
9:
	DMTC0	v0, COP_0_TLB_SET
	MTC0_HAZARD

1:
	DMTC0	a1, COP_0_TLB_LO
	MTC0_HAZARD
	TLBW

	DMTC0	a2, COP_0_TLB_HI	# restore PID
	MTC0_HAZARD
	DMTC0	v1, COP_0_STATUS_REG	# Restore the status register
	MTC0_SR_IE_HAZARD

	j	ra
	 NOP
END(TLBOP(update))	/* } */

/*
 * void tlb_read(int, struct tlb_entry *);
 * Read the given TLB entry. On R8000, only the tlb_hi and tlb_lo0 fields are
 * filled.
 */
LEAF(tlb_read, 0)	/* { */
	DMFC0	v1, COP_0_STATUS_REG	# Save the status register.
	MFC0_HAZARD
	ori	v0, v1, SR_INT_ENAB
	xori	v0, v0, SR_INT_ENAB
	DMTC0	v0, COP_0_STATUS_REG	# Disable interrupts
	MTC0_SR_IE_HAZARD

	DMFC0	a3, COP_0_TLB_HI	# Get current PID
	MFC0_HAZARD

	DMTC0	zero, COP_0_TLB_HI	# Force ASID to zero so we don't need
	MTC0_HAZARD			# to hash the index

	dsrl	a2, a0, 7
	DMTC0	a2, COP_0_TLB_SET
	MTC0_HAZARD
	dsll	a0, PAGE_SHIFT
	DMTC0	a0, COP_0_VADDR
	MTC0_HAZARD

	TLBR

	DMFC0	a0, COP_0_TLB_HI
	MFC0_HAZARD
	DMFC0	a2, COP_0_TLB_LO
	MFC0_HAZARD

	DMTC0	a3, COP_0_TLB_HI
	MTC0_HAZARD
	DMTC0	v1, COP_0_STATUS_REG	# Restore the status register
	MTC0_SR_IE_HAZARD

	sd	a0, 8(a1)		# tlb_hi
	j	ra
	 sd	a2, 16(a1)		# tlb_lo0
END(tlb_read)	/* } */

/*
 * uint tlb_get_pid(void);
 * Read the current TLB ASID.
 */
LEAF(tlb_get_pid, 0)	/* { */
	DMFC0	v0, COP_0_TLB_HI	# get PID
	MFC0_HAZARD
	and	v0, PG_ASID_MASK
	j	ra
	 dsrl	v0, PG_ASID_SHIFT
END(tlb_get_pid)	/* } */

/*
 * void tlb_set_pid(uint);
 * Set the current TLB ASID.
 */
LEAF(TLBOP(set_pid), 0)	/* { */
	DMFC0	v1, COP_0_STATUS_REG	# Save the status register.
	MFC0_HAZARD
	ori	v0, v1, SR_INT_ENAB
	xori	v0, v0, SR_INT_ENAB
	DMTC0	v0, COP_0_STATUS_REG	# Disable interrupts
	MTC0_SR_IE_HAZARD

	dsll	a1, a0, ICACHE_ASID_SHIFT
	dsll	a0, PG_ASID_SHIFT
	DMTC0	a0, COP_0_TLB_HI
	MTC0_HAZARD
	DMTC0	a1, COP_0_ICACHE
	MTC0_HAZARD

	DMTC0	v1, COP_0_STATUS_REG	# Restore the status register
	MTC0_SR_IE_HAZARD

	j	ra
	 NOP
END(TLBOP(set_pid))	/* } */

/*
 * void tlb_set_wired(uint32_t);
 */
LEAF(tlb_set_wired, 0)	/* { */
	DMTC0	a0, COP_0_TFP_TLB_WIRED
	MTC0_HAZARD
	j	ra
	 NOP
END(tlb_set_wired)	/* } */

/*
 * void tlb_set_gbase(vaddr_t, vsize_t);
 */
LEAF(tlb_set_gbase, 0)	/* { */
	DMTC0	a0, COP_0_GBASE
	MTC0_HAZARD
	DMTC0	a1, COP_0_WORK1
	MTC0_HAZARD
	j	ra
	 NOP
END(tlb_set_gbase)	/* } */