code:
/*	$OpenBSD: pmap.c,v 1.160 2015/07/20 00:16:16 kettenis Exp $ */

/*
 * Copyright (c) 2015 Martin Pieuchot
 * Copyright (c) 2001, 2002, 2007 Dale Rahn.
 * 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. 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.
 *
 * Effort sponsored in part by the Defense Advanced Research Projects
 * Agency (DARPA) and Air Force Research Laboratory, Air Force
 * Materiel Command, USAF, under agreement number F30602-01-2-0537.
 */

/*
 * powerpc lazy icache managment.
 * The icache does not snoop dcache accesses. The icache also will not load
 * modified data from the dcache, but the unmodified data in ram.
 * Before the icache is loaded, the dcache must be synced to ram to prevent
 * the icache from loading stale data.
 * pg->pg_flags PG_PMAP_EXE bit is used to track if the dcache is clean
 * and the icache may have valid data in it.
 * if the PG_PMAP_EXE bit is set (and the page is not currently RWX)
 * the icache will only have valid code in it. If the bit is clear
 * memory may not match the dcache contents or the icache may contain
 * data from a previous page.
 *
 * pmap enter
 * !E  NONE 	-> R	no action
 * !E  NONE|R 	-> RW	no action
 * !E  NONE|R 	-> RX	flush dcache, inval icache (that page only), set E
 * !E  NONE|R 	-> RWX	flush dcache, inval icache (that page only), set E
 * !E  NONE|RW 	-> RWX	flush dcache, inval icache (that page only), set E
 *  E  NONE 	-> R	no action
 *  E  NONE|R 	-> RW	clear PG_PMAP_EXE bit
 *  E  NONE|R 	-> RX	no action
 *  E  NONE|R 	-> RWX	no action
 *  E  NONE|RW 	-> RWX	-invalid source state
 *
 * pamp_protect
 *  E RW -> R	- invalid source state
 * !E RW -> R	- no action
 *  * RX -> R	- no action
 *  * RWX -> R	- sync dcache, inval icache
 *  * RWX -> RW	- clear PG_PMAP_EXE
 *  * RWX -> RX	- sync dcache, inval icache
 *  * * -> NONE	- no action
 *
 * pmap_page_protect (called with arg PROT_NONE if page is to be reused)
 *  * RW -> R	- as pmap_protect
 *  * RX -> R	- as pmap_protect
 *  * RWX -> R	- as pmap_protect
 *  * RWX -> RW	- as pmap_protect
 *  * RWX -> RX	- as pmap_protect
 *  * * -> NONE - clear PG_PMAP_EXE
 *
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/queue.h>
#include <sys/pool.h>
#include <sys/atomic.h>

#include <uvm/uvm_extern.h>

#include <machine/pcb.h>
#include <powerpc/powerpc.h>
#include <powerpc/bat.h>
#include <machine/pmap.h>

struct bat battable[16];

struct dumpmem dumpmem[VM_PHYSSEG_MAX];
u_int ndumpmem;

struct pmap kernel_pmap_;
static struct mem_region *pmap_mem, *pmap_avail;
struct mem_region pmap_allocated[10];
int pmap_cnt_avail;
int pmap_cnt_allocated;

struct pte_64  *pmap_ptable64;
struct pte_32  *pmap_ptable32;
int	pmap_ptab_cnt;
u_int	pmap_ptab_mask;

#define HTABSIZE_32	(pmap_ptab_cnt * 64)
#define HTABMEMSZ_64	(pmap_ptab_cnt * 8 * sizeof(struct pte_64))
#define HTABSIZE_64	(ffs(pmap_ptab_cnt) - 12)

static u_int usedsr[NPMAPS / sizeof(u_int) / 8];

struct pte_desc {
	/* Linked list of phys -> virt entries */
	LIST_ENTRY(pte_desc) pted_pv_list;
	union {
		struct pte_32 pted_pte32;
		struct pte_64 pted_pte64;
	} p;
	pmap_t pted_pmap;
	vaddr_t pted_va;
};

void pmap_attr_save(paddr_t pa, u_int32_t bits);
void pmap_pted_ro(struct pte_desc *, vm_prot_t);
void pmap_pted_ro64(struct pte_desc *, vm_prot_t);
void pmap_pted_ro32(struct pte_desc *, vm_prot_t);

/*
 * Some functions are called in real mode and cannot be profiled.
 */
#define __noprof __attribute__((__no_instrument_function__))

/* VP routines */
int pmap_vp_enter(pmap_t pm, vaddr_t va, struct pte_desc *pted, int flags);
struct pte_desc *pmap_vp_remove(pmap_t pm, vaddr_t va);
void pmap_vp_destroy(pmap_t pm);
struct pte_desc *pmap_vp_lookup(pmap_t pm, vaddr_t va) __noprof;

/* PV routines */
void pmap_enter_pv(struct pte_desc *pted, struct vm_page *);
void pmap_remove_pv(struct pte_desc *pted);

/* pte hash table routines */
static inline void *pmap_ptedinhash(struct pte_desc *);
void pte_insert32(struct pte_desc *) __noprof;
void pte_insert64(struct pte_desc *) __noprof;
void pmap_fill_pte64(pmap_t, vaddr_t, paddr_t, struct pte_desc *, vm_prot_t,
    int) __noprof;
void pmap_fill_pte32(pmap_t, vaddr_t, paddr_t, struct pte_desc *, vm_prot_t,
    int) __noprof;

void pmap_syncicache_user_virt(pmap_t pm, vaddr_t va);

void _pmap_kenter_pa(vaddr_t va, paddr_t pa, vm_prot_t prot, int flags,
    int cache);
void pmap_remove_pted(pmap_t, struct pte_desc *);

/* setup/initialization functions */
void pmap_avail_setup(void);
void pmap_avail_fixup(void);
void pmap_remove_avail(paddr_t base, paddr_t end);
void *pmap_steal_avail(size_t size, int align);

/* asm interface */
int pte_spill_r(u_int32_t, u_int32_t, u_int32_t, int) __noprof;
int pte_spill_v(pmap_t, u_int32_t, u_int32_t, int) __noprof;

u_int32_t pmap_setusr(pmap_t pm, vaddr_t va);
void pmap_popusr(u_int32_t oldsr);

/* pte invalidation */
void pte_del(void *, vaddr_t);
void pte_zap(void *, struct pte_desc *);

/* XXX - panic on pool get failures? */
struct pool pmap_pmap_pool;
struct pool pmap_vp_pool;
struct pool pmap_pted_pool;

int pmap_initialized = 0;
int physmem;
int physmaxaddr;

#ifdef MULTIPROCESSOR
struct __mp_lock pmap_hash_lock;

#define	PMAP_HASH_LOCK_INIT()		__mp_lock_init(&pmap_hash_lock)

#define	PMAP_HASH_LOCK(s)						\
do {									\
	s = ppc_intr_disable();						\
	__mp_lock(&pmap_hash_lock);					\
} while (0)

#define	PMAP_HASH_UNLOCK(s)						\
do {									\
	__mp_unlock(&pmap_hash_lock);					\
	ppc_intr_enable(s);						\
} while (0)

#define	PMAP_VP_LOCK_INIT(pm)		mtx_init(&pm->pm_mtx, IPL_VM)

#define	PMAP_VP_LOCK(pm)						\
do {									\
	if (pm != pmap_kernel())					\
		mtx_enter(&pm->pm_mtx);					\
} while (0)

#define	PMAP_VP_UNLOCK(pm)						\
do {									\
	if (pm != pmap_kernel())					\
		mtx_leave(&pm->pm_mtx);					\
} while (0)

#define PMAP_VP_ASSERT_LOCKED(pm)					\
do {									\
	if (pm != pmap_kernel())					\
		MUTEX_ASSERT_LOCKED(&pm->pm_mtx);			\
} while (0)

#else /* ! MULTIPROCESSOR */

#define	PMAP_HASH_LOCK_INIT()		/* nothing */
#define	PMAP_HASH_LOCK(s)		(void)s
#define	PMAP_HASH_UNLOCK(s)		/* nothing */

#define	PMAP_VP_LOCK_INIT(pm)		/* nothing */
#define	PMAP_VP_LOCK(pm)		/* nothing */
#define	PMAP_VP_UNLOCK(pm)		/* nothing */
#define	PMAP_VP_ASSERT_LOCKED(pm)	/* nothing */
#endif /* MULTIPROCESSOR */

/* virtual to physical helpers */
static inline int
VP_SR(vaddr_t va)
{
	return (va >>VP_SR_POS) & VP_SR_MASK;
}

static inline int
VP_IDX1(vaddr_t va)
{
	return (va >> VP_IDX1_POS) & VP_IDX1_MASK;
}

static inline int
VP_IDX2(vaddr_t va)
{
	return (va >> VP_IDX2_POS) & VP_IDX2_MASK;
}

#if VP_IDX1_SIZE != VP_IDX2_SIZE
#error pmap allocation code expects IDX1 and IDX2 size to be same
#endif
struct pmapvp {
	void *vp[VP_IDX1_SIZE];
};

/*
 * VP routines, virtual to physical translation information.
 * These data structures are based off of the pmap, per process.
 */

/*
 * This is used for pmap_kernel() mappings, they are not to be removed
 * from the vp table because they were statically initialized at the
 * initial pmap initialization. This is so that memory allocation
 * is not necessary in the pmap_kernel() mappings.
 * Otherwise bad race conditions can appear.
 */
struct pte_desc *
pmap_vp_lookup(pmap_t pm, vaddr_t va)
{
	struct pmapvp *vp1;
	struct pmapvp *vp2;
	struct pte_desc *pted;

	PMAP_VP_ASSERT_LOCKED(pm);

	vp1 = pm->pm_vp[VP_SR(va)];
	if (vp1 == NULL) {
		return NULL;
	}

	vp2 = vp1->vp[VP_IDX1(va)];
	if (vp2 == NULL) {
		return NULL;
	}

	pted = vp2->vp[VP_IDX2(va)];

	return pted;
}

/*
 * Remove, and return, pted at specified address, NULL if not present
 */
struct pte_desc *
pmap_vp_remove(pmap_t pm, vaddr_t va)
{
	struct pmapvp *vp1;
	struct pmapvp *vp2;
	struct pte_desc *pted;

	PMAP_VP_ASSERT_LOCKED(pm);

	vp1 = pm->pm_vp[VP_SR(va)];
	if (vp1 == NULL) {
		return NULL;
	}

	vp2 = vp1->vp[VP_IDX1(va)];
	if (vp2 == NULL) {
		return NULL;
	}

	pted = vp2->vp[VP_IDX2(va)];
	vp2->vp[VP_IDX2(va)] = NULL;

	return pted;
}

/*
 * Create a V -> P mapping for the given pmap and virtual address
 * with reference to the pte descriptor that is used to map the page.
 * This code should track allocations of vp table allocations
 * so they can be freed efficiently.
 */
int
pmap_vp_enter(pmap_t pm, vaddr_t va, struct pte_desc *pted, int flags)
{
	struct pmapvp *vp1;
	struct pmapvp *vp2;

	PMAP_VP_ASSERT_LOCKED(pm);

	vp1 = pm->pm_vp[VP_SR(va)];
	if (vp1 == NULL) {
		vp1 = pool_get(&pmap_vp_pool, PR_NOWAIT | PR_ZERO);
		if (vp1 == NULL) {
			if ((flags & PMAP_CANFAIL) == 0)
				panic("pmap_vp_enter: failed to allocate vp1");
			return ENOMEM;
		}
		pm->pm_vp[VP_SR(va)] = vp1;
	}

	vp2 = vp1->vp[VP_IDX1(va)];
	if (vp2 == NULL) {
		vp2 = pool_get(&pmap_vp_pool, PR_NOWAIT | PR_ZERO);
		if (vp2 == NULL) {
			if ((flags & PMAP_CANFAIL) == 0)
				panic("pmap_vp_enter: failed to allocate vp2");
			return ENOMEM;
		}
		vp1->vp[VP_IDX1(va)] = vp2;
	}

	vp2->vp[VP_IDX2(va)] = pted;

	return 0;
}

static inline void
tlbie(vaddr_t va)
{
	asm volatile ("tlbie %0" :: "r"(va & ~PAGE_MASK));
}

static inline void
tlbsync(void)
{
	asm volatile ("tlbsync");
}
static inline void
eieio(void)
{
	asm volatile ("eieio");
}

static inline void
sync(void)
{
	asm volatile ("sync");
}

static inline void
tlbia(void)
{
	vaddr_t va;

	sync();
	for (va = 0; va < 0x00040000; va += 0x00001000)
		tlbie(va);
	eieio();
	tlbsync();
	sync();
}

static inline int
ptesr(sr_t *sr, vaddr_t va)
{
	return sr[(u_int)va >> ADDR_SR_SHIFT];
}

static inline int
pteidx(sr_t sr, vaddr_t va)
{
	int hash;
	hash = (sr & SR_VSID) ^ (((u_int)va & ADDR_PIDX) >> ADDR_PIDX_SHIFT);
	return hash & pmap_ptab_mask;
}

#define PTED_VA_PTEGIDX_M	0x07
#define PTED_VA_HID_M		0x08
#define PTED_VA_MANAGED_M	0x10
#define PTED_VA_WIRED_M		0x20
#define PTED_VA_EXEC_M		0x40

static inline u_int32_t
PTED_HID(struct pte_desc *pted)
{
	return (pted->pted_va & PTED_VA_HID_M);
}

static inline u_int32_t
PTED_PTEGIDX(struct pte_desc *pted)
{
	return (pted->pted_va & PTED_VA_PTEGIDX_M);
}

static inline u_int32_t
PTED_MANAGED(struct pte_desc *pted)
{
	return (pted->pted_va & PTED_VA_MANAGED_M);
}

static inline u_int32_t
PTED_WIRED(struct pte_desc *pted)
{
	return (pted->pted_va & PTED_VA_WIRED_M);
}

static inline u_int32_t
PTED_VALID(struct pte_desc *pted)
{
	if (ppc_proc_is_64b)
		return (pted->p.pted_pte64.pte_hi & PTE_VALID_64);
	else
		return (pted->p.pted_pte32.pte_hi & PTE_VALID_32);
}

/*
 * PV entries -
 * manipulate the physical to virtual translations for the entire system.
 *
 * QUESTION: should all mapped memory be stored in PV tables? Or
 * is it alright to only store "ram" memory. Currently device mappings
 * are not stored.
 * It makes sense to pre-allocate mappings for all of "ram" memory, since
 * it is likely that it will be mapped at some point, but would it also
 * make sense to use a tree/table like is use for pmap to store device
 * mappings?
 * Further notes: It seems that the PV table is only used for pmap_protect
 * and other paging related operations. Given this, it is not necessary
 * to store any pmap_kernel() entries in PV tables and does not make
 * sense to store device mappings in PV either.
 *
 * Note: unlike other powerpc pmap designs, the array is only an array
 * of pointers. Since the same structure is used for holding information
 * in the VP table, the PV table, and for kernel mappings, the wired entries.
 * Allocate one data structure to hold all of the info, instead of replicating
 * it multiple times.
 *
 * One issue of making this a single data structure is that two pointers are
 * wasted for every page which does not map ram (device mappings), this
 * should be a low percentage of mapped pages in the system, so should not
 * have too noticable unnecessary ram consumption.
 */

void
pmap_enter_pv(struct pte_desc *pted, struct vm_page *pg)
{
	if (__predict_false(!pmap_initialized)) {
		return;
	}

	LIST_INSERT_HEAD(&(pg->mdpage.pv_list), pted, pted_pv_list);
	pted->pted_va |= PTED_VA_MANAGED_M;
}

void
pmap_remove_pv(struct pte_desc *pted)
{
	pted->pted_va &= ~PTED_VA_MANAGED_M;
	LIST_REMOVE(pted, pted_pv_list);
}

/* PTE_CHG_32 == PTE_CHG_64 */
/* PTE_REF_32 == PTE_REF_64 */
static __inline u_int
pmap_pte2flags(u_int32_t pte)
{
	return (((pte & PTE_REF_32) ? PG_PMAP_REF : 0) |
	    ((pte & PTE_CHG_32) ? PG_PMAP_MOD : 0));
}

static __inline u_int
pmap_flags2pte(u_int32_t flags)
{
	return (((flags & PG_PMAP_REF) ? PTE_REF_32 : 0) |
	    ((flags & PG_PMAP_MOD) ? PTE_CHG_32 : 0));
}

void
pmap_attr_save(paddr_t pa, u_int32_t bits)
{
	struct vm_page *pg;

	pg = PHYS_TO_VM_PAGE(pa);
	if (pg == NULL)
		return;

	atomic_setbits_int(&pg->pg_flags,  pmap_pte2flags(bits));
}

int
pmap_enter(pmap_t pm, vaddr_t va, paddr_t pa, vm_prot_t prot, int flags)
{
	struct pte_desc *pted;
	struct vm_page *pg;
	boolean_t nocache = (pa & PMAP_NOCACHE) != 0;
	boolean_t wt = (pa & PMAP_WT) != 0;
	int need_sync = 0;
	int cache, error = 0;

	KASSERT(!(wt && nocache));
	pa &= PMAP_PA_MASK;

	PMAP_VP_LOCK(pm);
	pted = pmap_vp_lookup(pm, va);
	if (pted && PTED_VALID(pted)) {
		pmap_remove_pted(pm, pted);
		/* we lost our pted if it was user */
		if (pm != pmap_kernel())
			pted = pmap_vp_lookup(pm, va);
	}

	pm->pm_stats.resident_count++;

	/* Do not have pted for this, get one and put it in VP */
	if (pted == NULL) {
		pted = pool_get(&pmap_pted_pool, PR_NOWAIT | PR_ZERO);
		if (pted == NULL) {
			if ((flags & PMAP_CANFAIL) == 0) {
				error = ENOMEM;
				goto out;
			}
			panic("pmap_enter: failed to allocate pted");
		}
		error = pmap_vp_enter(pm, va, pted, flags);
		if (error) {
			pool_put(&pmap_pted_pool, pted);
			goto out;
		}
	}

	pg = PHYS_TO_VM_PAGE(pa);
	if (wt)
		cache = PMAP_CACHE_WT;
	else if (pg != NULL && !(pg->pg_flags & PG_DEV) && !nocache)
		cache = PMAP_CACHE_WB;
	else
		cache = PMAP_CACHE_CI;

	/* Calculate PTE */
	if (ppc_proc_is_64b)
		pmap_fill_pte64(pm, va, pa, pted, prot, cache);
	else
		pmap_fill_pte32(pm, va, pa, pted, prot, cache);

	if (pg != NULL) {
		pmap_enter_pv(pted, pg); /* only managed mem */
	}

	/*
	 * Insert into HTAB
	 * We were told to map the page, probably called from vm_fault,
	 * so map the page!
	 */
	if (ppc_proc_is_64b)
		pte_insert64(pted);
	else
		pte_insert32(pted);

        if (prot & PROT_EXEC) {
		u_int sn = VP_SR(va);

        	pm->pm_exec[sn]++;
		if (pm->pm_sr[sn] & SR_NOEXEC)
			pm->pm_sr[sn] &= ~SR_NOEXEC;

		if (pg != NULL) {
			need_sync = ((pg->pg_flags & PG_PMAP_EXE) == 0);
			if (prot & PROT_WRITE)
				atomic_clearbits_int(&pg->pg_flags,
				    PG_PMAP_EXE);
			else
				atomic_setbits_int(&pg->pg_flags,
				    PG_PMAP_EXE);
		} else
			need_sync = 1;
	} else {
		/*
		 * Should we be paranoid about writeable non-exec
		 * mappings ? if so, clear the exec tag
		 */
		if ((prot & PROT_WRITE) && (pg != NULL))
			atomic_clearbits_int(&pg->pg_flags, PG_PMAP_EXE);
	}

	/* only instruction sync executable pages */
	if (need_sync)
		pmap_syncicache_user_virt(pm, va);

out:
	PMAP_VP_UNLOCK(pm);
	return (error);
}

/*
 * Remove the given range of mapping entries.
 */
void
pmap_remove(pmap_t pm, vaddr_t sva, vaddr_t eva)
{
	struct pte_desc *pted;
	vaddr_t va;

	KERNEL_LOCK();
	PMAP_VP_LOCK(pm);
	for (va = sva; va < eva; va += PAGE_SIZE) {
		pted = pmap_vp_lookup(pm, va);
		if (pted && PTED_VALID(pted))
			pmap_remove_pted(pm, pted);
	}
	PMAP_VP_UNLOCK(pm);
	KERNEL_UNLOCK();
}

/*
 * remove a single mapping, notice that this code is O(1)
 */
void
pmap_remove_pted(pmap_t pm, struct pte_desc *pted)
{
	void *pte;
	int s;

	KASSERT(pm == pted->pted_pmap);
	PMAP_VP_ASSERT_LOCKED(pm);

	pm->pm_stats.resident_count--;

	PMAP_HASH_LOCK(s);
	if ((pte = pmap_ptedinhash(pted)) != NULL)
		pte_zap(pte, pted);
	PMAP_HASH_UNLOCK(s);

	if (pted->pted_va & PTED_VA_EXEC_M) {
		u_int sn = VP_SR(pted->pted_va);

		pted->pted_va &= ~PTED_VA_EXEC_M;
		pm->pm_exec[sn]--;
		if (pm->pm_exec[sn] == 0)
			pm->pm_sr[sn] |= SR_NOEXEC;
	}

	if (ppc_proc_is_64b)
		pted->p.pted_pte64.pte_hi &= ~PTE_VALID_64;
	else
		pted->p.pted_pte32.pte_hi &= ~PTE_VALID_32;

	if (PTED_MANAGED(pted))
		pmap_remove_pv(pted);

	if (pm != pmap_kernel()) {
		(void)pmap_vp_remove(pm, pted->pted_va);
		pool_put(&pmap_pted_pool, pted);
	}
}

/*
 * Enter a kernel mapping for the given page.
 * kernel mappings have a larger set of prerequisites than normal mappings.
 *
 * 1. no memory should be allocated to create a kernel mapping.
 * 2. a vp mapping should already exist, even if invalid. (see 1)
 * 3. all vp tree mappings should already exist (see 1)
 *
 */
void
pmap_kenter_pa(vaddr_t va, paddr_t pa, vm_prot_t prot)
{
	struct pte_desc *pted;
	struct vm_page *pg;
	boolean_t nocache = (pa & PMAP_NOCACHE) != 0;
	boolean_t wt = (pa & PMAP_WT) != 0;
	pmap_t pm;
	int cache;

	KASSERT(!(wt && nocache));
	pa &= PMAP_PA_MASK;

	pm = pmap_kernel();

	pted = pmap_vp_lookup(pm, va);
	if (pted && PTED_VALID(pted))
		pmap_remove_pted(pm, pted); /* pted is reused */

	pm->pm_stats.resident_count++;

	if (prot & PROT_WRITE) {
		pg = PHYS_TO_VM_PAGE(pa);
		if (pg != NULL)
			atomic_clearbits_int(&pg->pg_flags, PG_PMAP_EXE);
	}

	/* Do not have pted for this, get one and put it in VP */
	if (pted == NULL) {
		panic("pted not preallocated in pmap_kernel() va %lx pa %lx",
		    va, pa);
	}

	pg = PHYS_TO_VM_PAGE(pa);
	if (wt)
		cache = PMAP_CACHE_WT;
	else if (pg != NULL && !(pg->pg_flags & PG_DEV) && !nocache)
		cache = PMAP_CACHE_WB;
	else
		cache = PMAP_CACHE_CI;

	/* Calculate PTE */
	if (ppc_proc_is_64b)
		pmap_fill_pte64(pm, va, pa, pted, prot, cache);
	else
		pmap_fill_pte32(pm, va, pa, pted, prot, cache);

	/*
	 * Insert into HTAB
	 * We were told to map the page, probably called from vm_fault,
	 * so map the page!
	 */
	if (ppc_proc_is_64b)
		pte_insert64(pted);
	else
		pte_insert32(pted);

	pted->pted_va |= PTED_VA_WIRED_M;

        if (prot & PROT_EXEC) {
		u_int sn = VP_SR(va);

        	pm->pm_exec[sn]++;
		if (pm->pm_sr[sn] & SR_NOEXEC)
			pm->pm_sr[sn] &= ~SR_NOEXEC;
	}
}

/*
 * remove kernel (pmap_kernel()) mappings
 */
void
pmap_kremove(vaddr_t va, vsize_t len)
{
	struct pte_desc *pted;

	for (len >>= PAGE_SHIFT; len > 0; len--, va += PAGE_SIZE) {
		pted = pmap_vp_lookup(pmap_kernel(), va);
		if (pted && PTED_VALID(pted))
			pmap_remove_pted(pmap_kernel(), pted);
	}
}

static inline void *
pmap_ptedinhash(struct pte_desc *pted)
{
	vaddr_t va = pted->pted_va & ~PAGE_MASK;
	pmap_t pm = pted->pted_pmap;
	int sr, idx;

	sr = ptesr(pm->pm_sr, va);
	idx = pteidx(sr, va);

	if (ppc_proc_is_64b) {
		struct pte_64 *pte = pmap_ptable64;

		pte += (idx ^ (PTED_HID(pted) ? pmap_ptab_mask : 0)) * 8;
		pte += PTED_PTEGIDX(pted);

		/*
		 * We now have the pointer to where it will be, if it is
		 * currently mapped. If the mapping was thrown away in
		 * exchange for another page mapping, then this page is
		 * not currently in the HASH.
		 */
		if ((pted->p.pted_pte64.pte_hi |
		    (PTED_HID(pted) ? PTE_HID_64 : 0)) == pte->pte_hi)
			return (pte);
	} else {
		struct pte_32 *pte = pmap_ptable32;

		pte += (idx ^ (PTED_HID(pted) ? pmap_ptab_mask : 0)) * 8;
		pte += PTED_PTEGIDX(pted);

		/*
		 * We now have the pointer to where it will be, if it is
		 * currently mapped. If the mapping was thrown away in
		 * exchange for another page mapping, then this page is
		 * not currently in the HASH.
		 */
		if ((pted->p.pted_pte32.pte_hi |
		    (PTED_HID(pted) ? PTE_HID_32 : 0)) == pte->pte_hi)
			return (pte);
	}

	return (NULL);
}

/*
 * Delete a Page Table Entry, section 7.6.3.3.
 *
 * Note: pte must be locked.
 */
void
pte_del(void *pte, vaddr_t va)
{
	if (ppc_proc_is_64b)
		((struct pte_64 *)pte)->pte_hi &= ~PTE_VALID_64;
	else
		((struct pte_32 *)pte)->pte_hi &= ~PTE_VALID_32;

	sync();		/* Ensure update completed. */
	tlbie(va);	/* Invalidate old translation. */
	eieio();	/* Order tlbie before tlbsync. */
	tlbsync();	/* Ensure tlbie completed on all processors. */
	sync();		/* Ensure tlbsync and update completed. */
}

void
pte_zap(void *pte, struct pte_desc *pted)
{
	pte_del(pte, pted->pted_va);

	if (!PTED_MANAGED(pted))
		return;

	if (ppc_proc_is_64b) {
		pmap_attr_save(pted->p.pted_pte64.pte_lo & PTE_RPGN_64,
		    ((struct pte_64 *)pte)->pte_lo & (PTE_REF_64|PTE_CHG_64));
	} else {
		pmap_attr_save(pted->p.pted_pte32.pte_lo & PTE_RPGN_32,
		    ((struct pte_32 *)pte)->pte_lo & (PTE_REF_32|PTE_CHG_32));
	}
}

/*
 * What about execution control? Even at only a segment granularity.
 */
void
pmap_fill_pte64(pmap_t pm, vaddr_t va, paddr_t pa, struct pte_desc *pted,
	vm_prot_t prot, int cache)
{
	sr_t sr;
	struct pte_64 *pte64;

	sr = ptesr(pm->pm_sr, va);
	pte64 = &pted->p.pted_pte64;

	pte64->pte_hi = (((u_int64_t)sr & SR_VSID) <<
	   PTE_VSID_SHIFT_64) |
	    ((va >> ADDR_API_SHIFT_64) & PTE_API_64) | PTE_VALID_64;
	pte64->pte_lo = (pa & PTE_RPGN_64);

	if (cache == PMAP_CACHE_WB)
		pte64->pte_lo |= PTE_M_64;
	else if (cache == PMAP_CACHE_WT)
		pte64->pte_lo |= (PTE_W_64 | PTE_M_64);
	else
		pte64->pte_lo |= (PTE_M_64 | PTE_I_64 | PTE_G_64);

	if (prot & PROT_WRITE)
		pte64->pte_lo |= PTE_RW_64;
	else
		pte64->pte_lo |= PTE_RO_64;

	pted->pted_va = va & ~PAGE_MASK;

	if (prot & PROT_EXEC)
		pted->pted_va  |= PTED_VA_EXEC_M;
	else
		pte64->pte_lo |= PTE_N_64;

	pted->pted_pmap = pm;
}

/*
 * What about execution control? Even at only a segment granularity.
 */
void
pmap_fill_pte32(pmap_t pm, vaddr_t va, paddr_t pa, struct pte_desc *pted,
	vm_prot_t prot, int cache)
{
	sr_t sr;
	struct pte_32 *pte32;

	sr = ptesr(pm->pm_sr, va);
	pte32 = &pted->p.pted_pte32;

	pte32->pte_hi = ((sr & SR_VSID) << PTE_VSID_SHIFT_32) |
	    ((va >> ADDR_API_SHIFT_32) & PTE_API_32) | PTE_VALID_32;
	pte32->pte_lo = (pa & PTE_RPGN_32);

	if (cache == PMAP_CACHE_WB)
		pte32->pte_lo |= PTE_M_32;
	else if (cache == PMAP_CACHE_WT)
		pte32->pte_lo |= (PTE_W_32 | PTE_M_32);
	else
		pte32->pte_lo |= (PTE_M_32 | PTE_I_32 | PTE_G_32);

	if (prot & PROT_WRITE)
		pte32->pte_lo |= PTE_RW_32;
	else
		pte32->pte_lo |= PTE_RO_32;

	pted->pted_va = va & ~PAGE_MASK;

	/* XXX Per-page execution control. */
	if (prot & PROT_EXEC)
		pted->pted_va  |= PTED_VA_EXEC_M;

	pted->pted_pmap = pm;
}

int
pmap_test_attrs(struct vm_page *pg, u_int flagbit)
{
	u_int bits;
	struct pte_desc *pted;
	u_int ptebit = pmap_flags2pte(flagbit);
	int s;

	/* PTE_CHG_32 == PTE_CHG_64 */
	/* PTE_REF_32 == PTE_REF_64 */

	bits = pg->pg_flags & flagbit;
	if ((bits == flagbit))
		return bits;

	LIST_FOREACH(pted, &(pg->mdpage.pv_list), pted_pv_list) {
		void *pte;

		PMAP_HASH_LOCK(s);
		if ((pte = pmap_ptedinhash(pted)) != NULL) {
			if (ppc_proc_is_64b) {
				struct pte_64 *ptp64 = pte;
				bits |=	pmap_pte2flags(ptp64->pte_lo & ptebit);
			} else {
				struct pte_32 *ptp32 = pte;
				bits |=	pmap_pte2flags(ptp32->pte_lo & ptebit);
			}
		}
		PMAP_HASH_UNLOCK(s);

		if (bits == flagbit)
			break;
	}

	atomic_setbits_int(&pg->pg_flags,  bits);

	return bits;
}
int
pmap_clear_attrs(struct vm_page *pg, u_int flagbit)
{
	u_int bits;
	struct pte_desc *pted;
	u_int ptebit = pmap_flags2pte(flagbit);
	int s;

	/* PTE_CHG_32 == PTE_CHG_64 */
	/* PTE_REF_32 == PTE_REF_64 */

	bits = pg->pg_flags & flagbit;

	LIST_FOREACH(pted, &(pg->mdpage.pv_list), pted_pv_list) {
		void *pte;

		PMAP_HASH_LOCK(s);
		if ((pte = pmap_ptedinhash(pted)) != NULL) {
			if (ppc_proc_is_64b) {
				struct pte_64 *ptp64 = pte;

				bits |=	pmap_pte2flags(ptp64->pte_lo & ptebit);

				pte_del(ptp64, pted->pted_va);

				ptp64->pte_lo &= ~ptebit;
				eieio();
				ptp64->pte_hi |= PTE_VALID_64;
				sync();
			} else {
				struct pte_32 *ptp32 = pte;

				bits |=	pmap_pte2flags(ptp32->pte_lo & ptebit);

				pte_del(ptp32, pted->pted_va);

				ptp32->pte_lo &= ~ptebit;
				eieio();
				ptp32->pte_hi |= PTE_VALID_32;
				sync();
			}
		}
		PMAP_HASH_UNLOCK(s);
	}

	/*
	 * this is done a second time, because while walking the list
	 * a bit could have been promoted via pmap_attr_save()
	 */
	bits |= pg->pg_flags & flagbit;
	atomic_clearbits_int(&pg->pg_flags,  flagbit);

	return bits;
}

/*
 * Garbage collects the physical map system for pages which are
 * no longer used. Success need not be guaranteed -- that is, there
 * may well be pages which are not referenced, but others may be collected
 * Called by the pageout daemon when pages are scarce.
 */
void
pmap_collect(pmap_t pm)
{
	/* This could return unused v->p table layers which
	 * are empty.
	 * could malicious programs allocate memory and eat
	 * these wired pages? These are allocated via pool.
	 * Are there pool functions which could be called
	 * to lower the pool usage here?
	 */
}

/*
 * Fill the given physical page with zeros.
 */
void
pmap_zero_page(struct vm_page *pg)
{
	vaddr_t va = pmap_map_direct(pg);
	int i;

	/*
	 * Loop over & zero cache lines.  This code assumes that 64-bit
	 * CPUs have 128-byte cache lines.  We explicitely use ``dcbzl''
	 * here because we do not clear the DCBZ_SIZE bit of the HID5
	 * register in order to be compatible with code using ``dcbz''
	 * and assuming that cache line size is 32.
	 */
	if (ppc_proc_is_64b) {
		for (i = 0; i < PAGE_SIZE; i += 128)
			asm volatile ("dcbzl 0,%0" :: "r"(va + i));
		return;
	}

	for (i = 0; i < PAGE_SIZE; i += CACHELINESIZE)
		asm volatile ("dcbz 0,%0" :: "r"(va + i));
}

/*
 * Copy a page.
 */
void
pmap_copy_page(struct vm_page *srcpg, struct vm_page *dstpg)
{
	vaddr_t srcva = pmap_map_direct(srcpg);
	vaddr_t dstva = pmap_map_direct(dstpg);

	memcpy((void *)dstva, (void *)srcva, PAGE_SIZE);
}

int pmap_id_avail = 0;

pmap_t
pmap_create(void)
{
	int i, k, try, tblidx, tbloff;
	int s, seg;
	pmap_t pm;

	pm = pool_get(&pmap_pmap_pool, PR_WAITOK|PR_ZERO);

	pmap_reference(pm);
	PMAP_VP_LOCK_INIT(pm);

	/*
	 * Allocate segment registers for this pmap.
	 * Try not to reuse pmap ids, to spread the hash table usage.
	 */
again:
	for (i = 0; i < NPMAPS; i++) {
		try = pmap_id_avail + i;
		try = try % NPMAPS; /* truncate back into bounds */
		tblidx = try / (8 * sizeof usedsr[0]);
		tbloff = try % (8 * sizeof usedsr[0]);
		if ((usedsr[tblidx] & (1 << tbloff)) == 0) {
			/* pmap create lock? */
			s = splvm();
			if ((usedsr[tblidx] & (1 << tbloff)) == 1) {
				/* entry was stolen out from under us, retry */
				splx(s); /* pmap create unlock */
				goto again;
			}
			usedsr[tblidx] |= (1 << tbloff);
			pmap_id_avail = try + 1;
			splx(s); /* pmap create unlock */

			seg = try << 4;
			for (k = 0; k < 16; k++)
				pm->pm_sr[k] = (seg + k) | SR_NOEXEC;
			return (pm);
		}
	}
	panic("out of pmap slots");
}

/*
 * Add a reference to a given pmap.
 */
void
pmap_reference(pmap_t pm)
{
	pm->pm_refs++;
}

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

	refs = --pm->pm_refs;
	if (refs == -1)
		panic("re-entering pmap_destroy");
	if (refs > 0)
		return;

	/*
	 * reference count is zero, free pmap resources and free pmap.
	 */
	pmap_release(pm);
	pool_put(&pmap_pmap_pool, pm);
}

/*
 * Release any resources held by the given physical map.
 * Called when a pmap initialized by pmap_pinit is being released.
 */
void
pmap_release(pmap_t pm)
{
	int i, tblidx, tbloff;
	int s;

	pmap_vp_destroy(pm);
	i = (pm->pm_sr[0] & SR_VSID) >> 4;
	tblidx = i / (8  * sizeof usedsr[0]);
	tbloff = i % (8  * sizeof usedsr[0]);

	/* LOCK? */
	s = splvm();
	usedsr[tblidx] &= ~(1 << tbloff);
	splx(s);
}

void
pmap_vp_destroy(pmap_t pm)
{
	int i, j;
	struct pmapvp *vp1;
	struct pmapvp *vp2;

	for (i = 0; i < VP_SR_SIZE; i++) {
		vp1 = pm->pm_vp[i];
		if (vp1 == NULL)
			continue;

		for (j = 0; j < VP_IDX1_SIZE; j++) {
			vp2 = vp1->vp[j];
			if (vp2 == NULL)
				continue;

			pool_put(&pmap_vp_pool, vp2);
		}
		pm->pm_vp[i] = NULL;
		pool_put(&pmap_vp_pool, vp1);
	}
}

void
pmap_avail_setup(void)
{
	struct mem_region *mp;
	int pmap_physmem;

	ppc_mem_regions(&pmap_mem, &pmap_avail);
	pmap_cnt_avail = 0;
	pmap_physmem = 0;

	ndumpmem = 0;
	for (mp = pmap_mem; mp->size !=0; mp++, ndumpmem++) {
		pmap_physmem += atop(mp->size);
		dumpmem[ndumpmem].start = atop(mp->start);
		dumpmem[ndumpmem].end = atop(mp->start + mp->size);
	}

	if (physmem == 0)
		physmem = pmap_physmem;

	for (mp = pmap_avail; mp->size !=0 ; mp++) {
		if (physmaxaddr <  mp->start + mp->size)
			physmaxaddr = mp->start + mp->size;
	}

	for (mp = pmap_avail; mp->size !=0; mp++)
		pmap_cnt_avail += 1;
}

void
pmap_avail_fixup(void)
{
	struct mem_region *mp;
	u_int32_t align;
	u_int32_t end;

	mp = pmap_avail;
	while(mp->size !=0) {
		align = round_page(mp->start);
		if (mp->start != align) {
			pmap_remove_avail(mp->start, align);
			mp = pmap_avail;
			continue;
		}
		end = mp->start+mp->size;
		align = trunc_page(end);
		if (end != align) {
			pmap_remove_avail(align, end);
			mp = pmap_avail;
			continue;
		}
		mp++;
	}
}

/* remove a given region from avail memory */
void
pmap_remove_avail(paddr_t base, paddr_t end)
{
	struct mem_region *mp;
	int i;
	int mpend;

	/* remove given region from available */
	for (mp = pmap_avail; mp->size; mp++) {
		/*
		 * Check if this region holds all of the region
		 */
		mpend = mp->start + mp->size;
		if (base > mpend) {
			continue;
		}
		if (base <= mp->start) {
			if (end <= mp->start)
				break; /* region not present -??? */

			if (end >= mpend) {
				/* covers whole region */
				/* shorten */
				for (i = mp - pmap_avail;
				    i < pmap_cnt_avail;
				    i++) {
					pmap_avail[i] = pmap_avail[i+1];
				}
				pmap_cnt_avail--;
				pmap_avail[pmap_cnt_avail].size = 0;
			} else {
				mp->start = end;
				mp->size = mpend - end;
			}
		} else {
			/* start after the beginning */
			if (end >= mpend) {
				/* just truncate */
				mp->size = base - mp->start;
			} else {
				/* split */
				for (i = pmap_cnt_avail;
				    i > (mp - pmap_avail);
				    i--) {
					pmap_avail[i] = pmap_avail[i - 1];
				}
				pmap_cnt_avail++;
				mp->size = base - mp->start;
				mp++;
				mp->start = end;
				mp->size = mpend - end;
			}
		}
	}
	for (mp = pmap_allocated; mp->size != 0; mp++) {
		if (base < mp->start) {
			if (end == mp->start) {
				mp->start = base;
				mp->size += end - base;
				break;
			}
			/* lengthen */
			for (i = pmap_cnt_allocated; i > (mp - pmap_allocated);
			    i--) {
				pmap_allocated[i] = pmap_allocated[i - 1];
			}
			pmap_cnt_allocated++;
			mp->start = base;
			mp->size = end - base;
			return;
		}
		if (base == (mp->start + mp->size)) {
			mp->size += end - base;
			return;
		}
	}
	if (mp->size == 0) {
		mp->start = base;
		mp->size  = end - base;
		pmap_cnt_allocated++;
	}
}

void *
pmap_steal_avail(size_t size, int align)
{
	struct mem_region *mp;
	int start;
	int remsize;

	for (mp = pmap_avail; mp->size; mp++) {
		if (mp->size > size) {
			start = (mp->start + (align -1)) & ~(align -1);
			remsize = mp->size - (start - mp->start);
			if (remsize >= 0) {
				pmap_remove_avail(start, start+size);
				return (void *)start;
			}
		}
	}
	panic ("unable to allocate region with size %zx align %x",
	    size, align);
}

/*
 * Similar to pmap_steal_avail, but operating on vm_physmem since
 * uvm_page_physload() has been called.
 */
vaddr_t
pmap_steal_memory(vsize_t size, vaddr_t *start, vaddr_t *end)
{
	int segno;
	u_int npg;
	vaddr_t va;
	paddr_t pa;
	struct vm_physseg *seg;

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

	for (segno = 0, seg = vm_physmem; segno < vm_nphysseg; segno++, seg++) {
		if (seg->avail_end - seg->avail_start < npg)
			continue;
		/*
		 * We can only steal at an ``unused'' segment boundary,
		 * i.e. either at the start or at the end.
		 */
		if (seg->avail_start == seg->start ||
		    seg->avail_end == seg->end)
			break;
	}
	if (segno == vm_nphysseg)
		va = 0;
	else {
		if (seg->avail_start == seg->start) {
			pa = ptoa(seg->avail_start);
			seg->avail_start += npg;
			seg->start += npg;
		} else {
			pa = ptoa(seg->avail_end) - size;
			seg->avail_end -= npg;
			seg->end -= npg;
		}
		/*
		 * If all the segment has been consumed now, remove it.
		 * Note that the crash dump code still knows about it
		 * and will dump it correctly.
		 */
		if (seg->start == seg->end) {
			if (vm_nphysseg-- == 1)
				panic("pmap_steal_memory: out of memory");
			while (segno < vm_nphysseg) {
				seg[0] = seg[1]; /* struct copy */
				seg++;
				segno++;
			}
		}

		va = (vaddr_t)pa;	/* 1:1 mapping */
		bzero((void *)va, size);
	}

	if (start != NULL)
		*start = VM_MIN_KERNEL_ADDRESS;
	if (end != NULL)
		*end = VM_MAX_KERNEL_ADDRESS;

	return (va);
}

void *msgbuf_addr;

/*
 * Initialize pmap setup.
 * ALL of the code which deals with avail needs rewritten as an actual
 * memory allocation.
 */
void
pmap_bootstrap(u_int kernelstart, u_int kernelend)
{
	struct mem_region *mp;
	int i, k;
	struct pmapvp *vp1;
	struct pmapvp *vp2;

	/*
	 * set the page size (default value is 4K which is ok)
	 */
	uvm_setpagesize();

	/*
	 * Get memory.
	 */
	pmap_avail_setup();

	/*
	 * Page align all regions.
	 * Non-page memory isn't very interesting to us.
	 * Also, sort the entries for ascending addresses.
	 */
	kernelstart = trunc_page(kernelstart);
	kernelend = round_page(kernelend);
	pmap_remove_avail(kernelstart, kernelend);

	msgbuf_addr = pmap_steal_avail(MSGBUFSIZE,4);

#ifdef DEBUG
	for (mp = pmap_avail; mp->size; mp++) {
		bzero((void *)mp->start, mp->size);
	}
#endif

#define HTABENTS_32 1024
#define HTABENTS_64 2048

	if (ppc_proc_is_64b) {
		pmap_ptab_cnt = HTABENTS_64;
		while (pmap_ptab_cnt * 2 < physmem)
			pmap_ptab_cnt <<= 1;
	} else {
		pmap_ptab_cnt = HTABENTS_32;
		while (HTABSIZE_32 < (ptoa(physmem) >> 7))
			pmap_ptab_cnt <<= 1;
	}
	/*
	 * allocate suitably aligned memory for HTAB
	 */
	if (ppc_proc_is_64b) {
		pmap_ptable64 = pmap_steal_avail(HTABMEMSZ_64, HTABMEMSZ_64);
		bzero((void *)pmap_ptable64, HTABMEMSZ_64);
		pmap_ptab_mask = pmap_ptab_cnt - 1;
	} else {
		pmap_ptable32 = pmap_steal_avail(HTABSIZE_32, HTABSIZE_32);
		bzero((void *)pmap_ptable32, HTABSIZE_32);
		pmap_ptab_mask = pmap_ptab_cnt - 1;
	}

	/* allocate v->p mappings for pmap_kernel() */
	for (i = 0; i < VP_SR_SIZE; i++) {
		pmap_kernel()->pm_vp[i] = NULL;
	}
	vp1 = pmap_steal_avail(sizeof (struct pmapvp), 4);
	bzero (vp1, sizeof(struct pmapvp));
	pmap_kernel()->pm_vp[PPC_KERNEL_SR] = vp1;
	for (i = 0; i < VP_IDX1_SIZE; i++) {
		vp2 = vp1->vp[i] = pmap_steal_avail(sizeof (struct pmapvp), 4);
		bzero (vp2, sizeof(struct pmapvp));
		for (k = 0; k < VP_IDX2_SIZE; k++) {
			struct pte_desc *pted;
			pted = pmap_steal_avail(sizeof (struct pte_desc), 4);
			bzero (pted, sizeof (struct pte_desc));
			vp2->vp[k] = pted;
		}
	}

	/*
	 * Initialize kernel pmap and hardware.
	 */
#if NPMAPS >= PPC_KERNEL_SEGMENT / 16
	usedsr[PPC_KERNEL_SEGMENT / 16 / (sizeof usedsr[0] * 8)]
		|= 1 << ((PPC_KERNEL_SEGMENT / 16) % (sizeof usedsr[0] * 8));
#endif
	for (i = 0; i < 16; i++)
		pmap_kernel()->pm_sr[i] = (PPC_KERNEL_SEG0 + i) | SR_NOEXEC;

	if (ppc_nobat) {
		vp1 = pmap_steal_avail(sizeof (struct pmapvp), 4);
		bzero (vp1, sizeof(struct pmapvp));
		pmap_kernel()->pm_vp[0] = vp1;
		for (i = 0; i < VP_IDX1_SIZE; i++) {
			vp2 = vp1->vp[i] =
			    pmap_steal_avail(sizeof (struct pmapvp), 4);
			bzero (vp2, sizeof(struct pmapvp));
			for (k = 0; k < VP_IDX2_SIZE; k++) {
				struct pte_desc *pted;
				pted = pmap_steal_avail(sizeof (struct pte_desc), 4);
				bzero (pted, sizeof (struct pte_desc));
				vp2->vp[k] = pted;
			}
		}

		/* first segment contains executable pages */
		pmap_kernel()->pm_exec[0]++;
		pmap_kernel()->pm_sr[0] &= ~SR_NOEXEC;
	} else {
		/*
		 * Setup fixed BAT registers.
		 *
		 * Note that we still run in real mode, and the BAT
		 * registers were cleared in cpu_bootstrap().
		 */
		battable[0].batl = BATL(0x00000000, BAT_M);
		if (physmem > atop(0x08000000))
			battable[0].batu = BATU(0x00000000, BAT_BL_256M);
		else
			battable[0].batu = BATU(0x00000000, BAT_BL_128M);

		/* Map physical memory with BATs. */
		if (physmem > atop(0x10000000)) {
			battable[0x1].batl = BATL(0x10000000, BAT_M);
			battable[0x1].batu = BATU(0x10000000, BAT_BL_256M);
		}
		if (physmem > atop(0x20000000)) {
			battable[0x2].batl = BATL(0x20000000, BAT_M);
			battable[0x2].batu = BATU(0x20000000, BAT_BL_256M);
		}
		if (physmem > atop(0x30000000)) {
			battable[0x3].batl = BATL(0x30000000, BAT_M);
			battable[0x3].batu = BATU(0x30000000, BAT_BL_256M);
		}
		if (physmem > atop(0x40000000)) {
			battable[0x4].batl = BATL(0x40000000, BAT_M);
			battable[0x4].batu = BATU(0x40000000, BAT_BL_256M);
		}
		if (physmem > atop(0x50000000)) {
			battable[0x5].batl = BATL(0x50000000, BAT_M);
			battable[0x5].batu = BATU(0x50000000, BAT_BL_256M);
		}
		if (physmem > atop(0x60000000)) {
			battable[0x6].batl = BATL(0x60000000, BAT_M);
			battable[0x6].batu = BATU(0x60000000, BAT_BL_256M);
		}
		if (physmem > atop(0x70000000)) {
			battable[0x7].batl = BATL(0x70000000, BAT_M);
			battable[0x7].batu = BATU(0x70000000, BAT_BL_256M);
		}
	}

	ppc_kvm_stolen += reserve_dumppages( (caddr_t)(VM_MIN_KERNEL_ADDRESS +
	    ppc_kvm_stolen));

	pmap_avail_fixup();
	for (mp = pmap_avail; mp->size; mp++) {
		if (mp->start > 0x80000000)
			continue;
		if (mp->start + mp->size > 0x80000000)
			mp->size = 0x80000000 - mp->start;
		uvm_page_physload(atop(mp->start), atop(mp->start+mp->size),
		    atop(mp->start), atop(mp->start+mp->size), 0);
	}
}

void
pmap_enable_mmu(void)
{
	uint32_t scratch, sdr1;
	int i;

	if (!ppc_nobat) {
		/* DBAT0 used for initial segment */
		ppc_mtdbat0l(battable[0].batl);
		ppc_mtdbat0u(battable[0].batu);

		/* IBAT0 only covering the kernel .text */
		ppc_mtibat0l(battable[0].batl);
		ppc_mtibat0u(BATU(0x00000000, BAT_BL_8M));
	}

	for (i = 0; i < 16; i++)
		ppc_mtsrin(PPC_KERNEL_SEG0 + i, i << ADDR_SR_SHIFT);

	if (ppc_proc_is_64b)
		sdr1 = (uint32_t)pmap_ptable64 | HTABSIZE_64;
	else
		sdr1 = (uint32_t)pmap_ptable32 | (pmap_ptab_mask >> 10);

	asm volatile ("sync; mtsdr1 %0; isync" :: "r"(sdr1));
	tlbia();

	asm volatile ("eieio; mfmsr %0; ori %0,%0,%1; mtmsr %0; sync; isync"
	    : "=r"(scratch) : "K"(PSL_IR|PSL_DR|PSL_ME|PSL_RI));
}

/*
 * activate a pmap entry
 * NOOP on powerpc, all PTE entries exist in the same hash table.
 * Segment registers are filled on exit to user mode.
 */
void
pmap_activate(struct proc *p)
{
}

/*
 * deactivate a pmap entry
 * NOOP on powerpc
 */
void
pmap_deactivate(struct proc *p)
{
}

/*
 * pmap_extract: extract a PA for the given VA
 */

boolean_t
pmap_extract(pmap_t pm, vaddr_t va, paddr_t *pa)
{
	struct pte_desc *pted;

	if (pm == pmap_kernel() && va < physmaxaddr) {
		*pa = va;
		return TRUE;
	}

	PMAP_VP_LOCK(pm);
	pted = pmap_vp_lookup(pm, va);
	if (pted == NULL || !PTED_VALID(pted)) {
		PMAP_VP_UNLOCK(pm);
		return FALSE;
	}

	if (ppc_proc_is_64b)
		*pa = (pted->p.pted_pte64.pte_lo & PTE_RPGN_64) |
		    (va & ~PTE_RPGN_64);
	else
		*pa = (pted->p.pted_pte32.pte_lo & PTE_RPGN_32) |
		    (va & ~PTE_RPGN_32);

	PMAP_VP_UNLOCK(pm);
	return TRUE;
}

u_int32_t
pmap_setusr(pmap_t pm, vaddr_t va)
{
	u_int32_t sr;
	u_int32_t oldsr;

	sr = ptesr(pm->pm_sr, va);

	/* user address range lock?? */
	asm volatile ("mfsr %0,%1" : "=r" (oldsr): "n"(PPC_USER_SR));
	asm volatile ("isync; mtsr %0,%1; isync" :: "n"(PPC_USER_SR), "r"(sr));
	return oldsr;
}

void
pmap_popusr(u_int32_t sr)
{
	asm volatile ("isync; mtsr %0,%1; isync"
	    :: "n"(PPC_USER_SR), "r"(sr));
}

int
copyin(const void *udaddr, void *kaddr, size_t len)
{
	void *p;
	size_t l;
	u_int32_t oldsr;
	faultbuf env;
	void *oldh = curpcb->pcb_onfault;

	while (len > 0) {
		p = PPC_USER_ADDR + ((u_int)udaddr & ~PPC_SEGMENT_MASK);
		l = (PPC_USER_ADDR + PPC_SEGMENT_LENGTH) - p;
		if (l > len)
			l = len;
		oldsr = pmap_setusr(curpcb->pcb_pm, (vaddr_t)udaddr);
		if (setfault(&env)) {
			pmap_popusr(oldsr);
			curpcb->pcb_onfault = oldh;
			return EFAULT;
		}
		bcopy(p, kaddr, l);
		pmap_popusr(oldsr);
		udaddr += l;
		kaddr += l;
		len -= l;
	}
	curpcb->pcb_onfault = oldh;
	return 0;
}

int
copyout(const void *kaddr, void *udaddr, size_t len)
{
	void *p;
	size_t l;
	u_int32_t oldsr;
	faultbuf env;
	void *oldh = curpcb->pcb_onfault;

	while (len > 0) {
		p = PPC_USER_ADDR + ((u_int)udaddr & ~PPC_SEGMENT_MASK);
		l = (PPC_USER_ADDR + PPC_SEGMENT_LENGTH) - p;
		if (l > len)
			l = len;
		oldsr = pmap_setusr(curpcb->pcb_pm, (vaddr_t)udaddr);
		if (setfault(&env)) {
			pmap_popusr(oldsr);
			curpcb->pcb_onfault = oldh;
			return EFAULT;
		}

		bcopy(kaddr, p, l);
		pmap_popusr(oldsr);
		udaddr += l;
		kaddr += l;
		len -= l;
	}
	curpcb->pcb_onfault = oldh;
	return 0;
}

int
copyinstr(const void *udaddr, void *kaddr, size_t len, size_t *done)
{
	const u_char *uaddr = udaddr;
	u_char *kp    = kaddr;
	u_char *up;
	u_char c;
	void   *p;
	size_t	 l;
	u_int32_t oldsr;
	int cnt = 0;
	faultbuf env;
	void *oldh = curpcb->pcb_onfault;

	while (len > 0) {
		p = PPC_USER_ADDR + ((u_int)uaddr & ~PPC_SEGMENT_MASK);
		l = (PPC_USER_ADDR + PPC_SEGMENT_LENGTH) - p;
		up = p;
		if (l > len)
			l = len;
		len -= l;
		oldsr = pmap_setusr(curpcb->pcb_pm, (vaddr_t)uaddr);
		if (setfault(&env)) {
			if (done != NULL)
				*done =  cnt;

			curpcb->pcb_onfault = oldh;
			pmap_popusr(oldsr);
			return EFAULT;
		}
		while (l > 0) {
			c = *up;
			*kp = c;
			if (c == 0) {
				if (done != NULL)
					*done = cnt + 1;

				curpcb->pcb_onfault = oldh;
				pmap_popusr(oldsr);
				return 0;
			}
			up++;
			kp++;
			l--;
			cnt++;
			uaddr++;
		}
		pmap_popusr(oldsr);
	}
	curpcb->pcb_onfault = oldh;
	if (done != NULL)
		*done = cnt;

	return ENAMETOOLONG;
}

int
copyoutstr(const void *kaddr, void *udaddr, size_t len, size_t *done)
{
	u_char *uaddr = (void *)udaddr;
	const u_char *kp    = kaddr;
	u_char *up;
	u_char c;
	void   *p;
	size_t	 l;
	u_int32_t oldsr;
	int cnt = 0;
	faultbuf env;
	void *oldh = curpcb->pcb_onfault;

	while (len > 0) {
		p = PPC_USER_ADDR + ((u_int)uaddr & ~PPC_SEGMENT_MASK);
		l = (PPC_USER_ADDR + PPC_SEGMENT_LENGTH) - p;
		up = p;
		if (l > len)
			l = len;
		len -= l;
		oldsr = pmap_setusr(curpcb->pcb_pm, (vaddr_t)uaddr);
		if (setfault(&env)) {
			if (done != NULL)
				*done =  cnt;

			curpcb->pcb_onfault = oldh;
			pmap_popusr(oldsr);
			return EFAULT;
		}
		while (l > 0) {
			c = *kp;
			*up = c;
			if (c == 0) {
				if (done != NULL)
					*done = cnt + 1;

				curpcb->pcb_onfault = oldh;
				pmap_popusr(oldsr);
				return 0;
			}
			up++;
			kp++;
			l--;
			cnt++;
			uaddr++;
		}
		pmap_popusr(oldsr);
	}
	curpcb->pcb_onfault = oldh;
	if (done != NULL)
		*done = cnt;

	return ENAMETOOLONG;
}

/*
 * sync instruction cache for user virtual address.
 * The address WAS JUST MAPPED, so we have a VALID USERSPACE mapping
 */
void
pmap_syncicache_user_virt(pmap_t pm, vaddr_t va)
{
	vaddr_t start;
	int oldsr;

	if (pm != pmap_kernel()) {
		start = ((u_int)PPC_USER_ADDR + ((u_int)va &
		    ~PPC_SEGMENT_MASK));
		/* will only ever be page size, will not cross segments */

		/* USER SEGMENT LOCK - MPXXX */
		oldsr = pmap_setusr(pm, va);
	} else {
		start = va; /* flush mapped page */
	}

	syncicache((void *)start, PAGE_SIZE);

	if (pm != pmap_kernel()) {
		pmap_popusr(oldsr);
		/* USER SEGMENT UNLOCK -MPXXX */
	}
}

void
pmap_pted_ro(struct pte_desc *pted, vm_prot_t prot)
{
	if (ppc_proc_is_64b)
		pmap_pted_ro64(pted, prot);
	else
		pmap_pted_ro32(pted, prot);
}

void
pmap_pted_ro64(struct pte_desc *pted, vm_prot_t prot)
{
	pmap_t pm = pted->pted_pmap;
	vaddr_t va = pted->pted_va & ~PAGE_MASK;
	struct vm_page *pg;
	void *pte;
	int s;

	pg = PHYS_TO_VM_PAGE(pted->p.pted_pte64.pte_lo & PTE_RPGN_64);
	if (pg->pg_flags & PG_PMAP_EXE) {
		if ((prot & (PROT_WRITE | PROT_EXEC)) == PROT_WRITE) {
			atomic_clearbits_int(&pg->pg_flags, PG_PMAP_EXE);
		} else {
			pmap_syncicache_user_virt(pm, va);
		}
	}

	pted->p.pted_pte64.pte_lo &= ~PTE_PP_64;
	pted->p.pted_pte64.pte_lo |= PTE_RO_64;

	if ((prot & PROT_EXEC) == 0)
		pted->p.pted_pte64.pte_lo |= PTE_N_64;

	PMAP_HASH_LOCK(s);
	if ((pte = pmap_ptedinhash(pted)) != NULL) {
		struct pte_64 *ptp64 = pte;

		pte_del(ptp64, va);

		if (PTED_MANAGED(pted)) { /* XXX */
			pmap_attr_save(ptp64->pte_lo & PTE_RPGN_64,
			    ptp64->pte_lo & (PTE_REF_64|PTE_CHG_64));
		}

		/* Add a Page Table Entry, section 7.6.3.1. */
		ptp64->pte_lo &= ~(PTE_CHG_64|PTE_PP_64);
		ptp64->pte_lo |= PTE_RO_64;
		eieio();	/* Order 1st PTE update before 2nd. */
		ptp64->pte_hi |= PTE_VALID_64;
		sync();		/* Ensure updates completed. */
	}
	PMAP_HASH_UNLOCK(s);
}

void
pmap_pted_ro32(struct pte_desc *pted, vm_prot_t prot)
{
	pmap_t pm = pted->pted_pmap;
	vaddr_t va = pted->pted_va & ~PAGE_MASK;
	struct vm_page *pg;
	void *pte;
	int s;

	pg = PHYS_TO_VM_PAGE(pted->p.pted_pte32.pte_lo & PTE_RPGN_32);
	if (pg->pg_flags & PG_PMAP_EXE) {
		if ((prot & (PROT_WRITE | PROT_EXEC)) == PROT_WRITE) {
			atomic_clearbits_int(&pg->pg_flags, PG_PMAP_EXE);
		} else {
			pmap_syncicache_user_virt(pm, va);
		}
	}

	pted->p.pted_pte32.pte_lo &= ~PTE_PP_32;
	pted->p.pted_pte32.pte_lo |= PTE_RO_32;

	PMAP_HASH_LOCK(s);
	if ((pte = pmap_ptedinhash(pted)) != NULL) {
		struct pte_32 *ptp32 = pte;

		pte_del(ptp32, va);

		if (PTED_MANAGED(pted)) { /* XXX */
			pmap_attr_save(ptp32->pte_lo & PTE_RPGN_32,
			    ptp32->pte_lo & (PTE_REF_32|PTE_CHG_32));
		}

		/* Add a Page Table Entry, section 7.6.3.1. */
		ptp32->pte_lo &= ~(PTE_CHG_32|PTE_PP_32);
		ptp32->pte_lo |= PTE_RO_32;
		eieio();	/* Order 1st PTE update before 2nd. */
		ptp32->pte_hi |= PTE_VALID_32;
		sync();		/* Ensure updates completed. */
	}
	PMAP_HASH_UNLOCK(s);
}

/*
 * Lower the protection on the specified physical page.
 *
 * There are only two cases, either the protection is going to 0,
 * or it is going to read-only.
 */
void
pmap_page_protect(struct vm_page *pg, vm_prot_t prot)
{
	struct pte_desc *pted;
	pmap_t pm;

	if (prot == PROT_NONE) {
		while ((pted = LIST_FIRST(&(pg->mdpage.pv_list))) != NULL) {
			pm = pted->pted_pmap;
			PMAP_VP_LOCK(pm);
			pmap_remove_pted(pm, pted);
			PMAP_VP_UNLOCK(pm);
		}
		/* page is being reclaimed, sync icache next use */
		atomic_clearbits_int(&pg->pg_flags, PG_PMAP_EXE);
		return;
	}

	LIST_FOREACH(pted, &(pg->mdpage.pv_list), pted_pv_list)
		pmap_pted_ro(pted, prot);
}

void
pmap_protect(pmap_t pm, vaddr_t sva, vaddr_t eva, vm_prot_t prot)
{
	if (prot & (PROT_READ | PROT_EXEC)) {
		struct pte_desc *pted;

		PMAP_VP_LOCK(pm);
		while (sva < eva) {
			pted = pmap_vp_lookup(pm, sva);
			if (pted && PTED_VALID(pted))
				pmap_pted_ro(pted, prot);
			sva += PAGE_SIZE;
		}
		PMAP_VP_UNLOCK(pm);
		return;
	}
	pmap_remove(pm, sva, eva);
}

/*
 * Restrict given range to physical memory
 */
void
pmap_real_memory(paddr_t *start, vsize_t *size)
{
	struct mem_region *mp;

	for (mp = pmap_mem; mp->size; mp++) {
		if (((*start + *size) > mp->start)
			&& (*start < (mp->start + mp->size)))
		{
			if (*start < mp->start) {
				*size -= mp->start - *start;
				*start = mp->start;
			}
			if ((*start + *size) > (mp->start + mp->size))
				*size = mp->start + mp->size - *start;
			return;
		}
	}
	*size = 0;
}

void
pmap_init()
{
	pool_init(&pmap_pmap_pool, sizeof(struct pmap), 0, 0, 0, "pmap", NULL);
	pool_setlowat(&pmap_pmap_pool, 2);
	pool_init(&pmap_vp_pool, sizeof(struct pmapvp), 0, 0, 0, "vp",
	    &pool_allocator_nointr);
	pool_setlowat(&pmap_vp_pool, 10);
	pool_init(&pmap_pted_pool, sizeof(struct pte_desc), 0, 0, 0, "pted",
	    NULL);
	pool_setlowat(&pmap_pted_pool, 20);

	PMAP_HASH_LOCK_INIT();

	pmap_initialized = 1;
}

void
pmap_proc_iflush(struct proc *p, vaddr_t addr, vsize_t len)
{
	paddr_t pa;
	vsize_t clen;

	while (len > 0) {
		/* add one to always round up to the next page */
		clen = round_page(addr + 1) - addr;
		if (clen > len)
			clen = len;

		if (pmap_extract(p->p_vmspace->vm_map.pmap, addr, &pa)) {
			syncicache((void *)pa, clen);
		}

		len -= clen;
		addr += clen;
	}
}

/*
 * There are two routines, pte_spill_r and pte_spill_v
 * the _r version only handles kernel faults which are not user
 * accesses. The _v version handles all user faults and kernel copyin/copyout
 * "user" accesses.
 */
int
pte_spill_r(u_int32_t va, u_int32_t msr, u_int32_t dsisr, int exec_fault)
{
	pmap_t pm;
	struct pte_desc *pted;
	struct pte_desc pted_store;

	/* lookup is done physical to prevent faults */

	/*
	 * This function only handles kernel faults, not supervisor copyins.
	 */
	if (msr & PSL_PR)
		return 0;

	/* if copyin, throw to full excption handler */
	if (VP_SR(va) == PPC_USER_SR)
		return 0;

	pm = pmap_kernel();

	/* 0 - physmaxaddr mapped 1-1 */
	if (va < physmaxaddr) {
		u_int32_t aligned_va;
		vm_prot_t prot = PROT_READ | PROT_WRITE;
		extern caddr_t kernel_text;
		extern caddr_t etext;

		pted = &pted_store;

		if (va >= trunc_page((vaddr_t)&kernel_text) &&
		    va < round_page((vaddr_t)&etext)) {
			prot |= PROT_EXEC;
		}

		aligned_va = trunc_page(va);
		if (ppc_proc_is_64b) {
			pmap_fill_pte64(pm, aligned_va, aligned_va,
			    pted, prot, PMAP_CACHE_WB);
			pte_insert64(pted);
		} else {
			pmap_fill_pte32(pm, aligned_va, aligned_va,
			    pted, prot, PMAP_CACHE_WB);
			pte_insert32(pted);
		}
		return 1;
	}

	return pte_spill_v(pm, va, dsisr, exec_fault);
}

int
pte_spill_v(pmap_t pm, u_int32_t va, u_int32_t dsisr, int exec_fault)
{
	struct pte_desc *pted;
	int inserted = 0;

	/*
	 * If the current mapping is RO and the access was a write
	 * we return 0
	 */
	PMAP_VP_LOCK(pm);
	pted = pmap_vp_lookup(pm, va);
	if (pted == NULL || !PTED_VALID(pted))
		goto out;

	/* Attempted to write a read-only page. */
	if (dsisr & DSISR_STORE) {
		if (ppc_proc_is_64b) {
			if (pted->p.pted_pte64.pte_lo & PTE_RO_64)
				goto out;
		} else {
			if (pted->p.pted_pte32.pte_lo & PTE_RO_32)
				goto out;
		}
	}

	/* Attempted to execute non-executable page. */
	if ((exec_fault != 0) && ((pted->pted_va & PTED_VA_EXEC_M) == 0))
		goto out;

	inserted = 1;
	if (ppc_proc_is_64b)
		pte_insert64(pted);
	else
		pte_insert32(pted);

out:
	PMAP_VP_UNLOCK(pm);
	return (inserted);
}

/*
 * should pte_insert code avoid wired mappings?
 * is the stack safe?
 * is the pted safe? (physical)
 * -ugh
 */
void
pte_insert64(struct pte_desc *pted)
{
	struct pte_64 *ptp64;
	int off, secondary;
	int sr, idx, i;
	void *pte;
	int s;

	PMAP_HASH_LOCK(s);
	if ((pte = pmap_ptedinhash(pted)) != NULL)
		pte_zap(pte, pted);

	pted->pted_va &= ~(PTED_VA_HID_M|PTED_VA_PTEGIDX_M);

	sr = ptesr(pted->pted_pmap->pm_sr, pted->pted_va);
	idx = pteidx(sr, pted->pted_va);

	/*
	 * instead of starting at the beginning of each pteg,
	 * the code should pick a random location with in the primary
	 * then search all of the entries, then if not yet found,
	 * do the same for the secondary.
	 * this would reduce the frontloading of the pteg.
	 */

	/* first just try fill of primary hash */
	ptp64 = pmap_ptable64 + (idx) * 8;
	for (i = 0; i < 8; i++) {
		if (ptp64[i].pte_hi & PTE_VALID_64)
			continue;

		pted->pted_va |= i;

		/* Add a Page Table Entry, section 7.6.3.1. */
		ptp64[i].pte_hi = pted->p.pted_pte64.pte_hi & ~PTE_VALID_64;
		ptp64[i].pte_lo = pted->p.pted_pte64.pte_lo;
		eieio();	/* Order 1st PTE update before 2nd. */
		ptp64[i].pte_hi |= PTE_VALID_64;
		sync();		/* Ensure updates completed. */

		goto out;
	}

	/* try fill of secondary hash */
	ptp64 = pmap_ptable64 + (idx ^ pmap_ptab_mask) * 8;
	for (i = 0; i < 8; i++) {
		if (ptp64[i].pte_hi & PTE_VALID_64)
			continue;

		pted->pted_va |= (i | PTED_VA_HID_M);

		/* Add a Page Table Entry, section 7.6.3.1. */
		ptp64[i].pte_hi = pted->p.pted_pte64.pte_hi & ~PTE_VALID_64;
		ptp64[i].pte_lo = pted->p.pted_pte64.pte_lo;
		eieio();	/* Order 1st PTE update before 2nd. */
		ptp64[i].pte_hi |= (PTE_HID_64|PTE_VALID_64);
		sync();		/* Ensure updates completed. */

		goto out;
	}

	/* need decent replacement algorithm */
	off = ppc_mftb();
	secondary = off & 8;

	pted->pted_va |= off & (PTED_VA_PTEGIDX_M|PTED_VA_HID_M);

	idx = (idx ^ (PTED_HID(pted) ? pmap_ptab_mask : 0));

	ptp64 = pmap_ptable64 + (idx * 8);
	ptp64 += PTED_PTEGIDX(pted); /* increment by index into pteg */

	if (ptp64->pte_hi & PTE_VALID_64) {
		vaddr_t va;

		/* Bits 9-19 */
		idx = (idx ^ ((ptp64->pte_hi & PTE_HID_64) ?
		    pmap_ptab_mask : 0));
		va = (ptp64->pte_hi >> PTE_VSID_SHIFT_64) ^ idx;
		va <<= ADDR_PIDX_SHIFT;
		/* Bits 4-8 */
		va |= (ptp64->pte_hi & PTE_API_64) << ADDR_API_SHIFT_32;
		/* Bits 0-3 */
		va |= (ptp64->pte_hi >> PTE_VSID_SHIFT_64)
		    << ADDR_SR_SHIFT;

		pte_del(ptp64, va);

		pmap_attr_save(ptp64->pte_lo & PTE_RPGN_64,
		    ptp64->pte_lo & (PTE_REF_64|PTE_CHG_64));
	}

	/* Add a Page Table Entry, section 7.6.3.1. */
	ptp64->pte_hi = pted->p.pted_pte64.pte_hi & ~PTE_VALID_64;
	if (secondary)
		ptp64->pte_hi |= PTE_HID_64;
	ptp64->pte_lo = pted->p.pted_pte64.pte_lo;
	eieio();	/* Order 1st PTE update before 2nd. */
	ptp64->pte_hi |= PTE_VALID_64;
	sync();		/* Ensure updates completed. */

out:
	PMAP_HASH_UNLOCK(s);
}

void
pte_insert32(struct pte_desc *pted)
{
	struct pte_32 *ptp32;
	int off, secondary;
	int sr, idx, i;
	void *pte;
	int s;

	PMAP_HASH_LOCK(s);
	if ((pte = pmap_ptedinhash(pted)) != NULL)
		pte_zap(pte, pted);

	pted->pted_va &= ~(PTED_VA_HID_M|PTED_VA_PTEGIDX_M);

	sr = ptesr(pted->pted_pmap->pm_sr, pted->pted_va);
	idx = pteidx(sr, pted->pted_va);

	/*
	 * instead of starting at the beginning of each pteg,
	 * the code should pick a random location with in the primary
	 * then search all of the entries, then if not yet found,
	 * do the same for the secondary.
	 * this would reduce the frontloading of the pteg.
	 */

	/* first just try fill of primary hash */
	ptp32 = pmap_ptable32 + (idx) * 8;
	for (i = 0; i < 8; i++) {
		if (ptp32[i].pte_hi & PTE_VALID_32)
			continue;

		pted->pted_va |= i;

		/* Add a Page Table Entry, section 7.6.3.1. */
		ptp32[i].pte_hi = pted->p.pted_pte32.pte_hi & ~PTE_VALID_32;
		ptp32[i].pte_lo = pted->p.pted_pte32.pte_lo;
		eieio();	/* Order 1st PTE update before 2nd. */
		ptp32[i].pte_hi |= PTE_VALID_32;
		sync();		/* Ensure updates completed. */

		goto out;
	}

	/* try fill of secondary hash */
	ptp32 = pmap_ptable32 + (idx ^ pmap_ptab_mask) * 8;
	for (i = 0; i < 8; i++) {
		if (ptp32[i].pte_hi & PTE_VALID_32)
			continue;

		pted->pted_va |= (i | PTED_VA_HID_M);

		/* Add a Page Table Entry, section 7.6.3.1. */
		ptp32[i].pte_hi = pted->p.pted_pte32.pte_hi & ~PTE_VALID_32;
		ptp32[i].pte_lo = pted->p.pted_pte32.pte_lo;
		eieio();	/* Order 1st PTE update before 2nd. */
		ptp32[i].pte_hi |= (PTE_HID_32|PTE_VALID_32);
		sync();		/* Ensure updates completed. */

		goto out;
	}

	/* need decent replacement algorithm */
	off = ppc_mftb();
	secondary = off & 8;

	pted->pted_va |= off & (PTED_VA_PTEGIDX_M|PTED_VA_HID_M);

	idx = (idx ^ (PTED_HID(pted) ? pmap_ptab_mask : 0));

	ptp32 = pmap_ptable32 + (idx * 8);
	ptp32 += PTED_PTEGIDX(pted); /* increment by index into pteg */

	if (ptp32->pte_hi & PTE_VALID_32) {
		vaddr_t va;

		va = ((ptp32->pte_hi & PTE_API_32) << ADDR_API_SHIFT_32) |
		     ((((ptp32->pte_hi >> PTE_VSID_SHIFT_32) & SR_VSID)
			^(idx ^ ((ptp32->pte_hi & PTE_HID_32) ? 0x3ff : 0)))
			    & 0x3ff) << PAGE_SHIFT;

		pte_del(ptp32, va);

		pmap_attr_save(ptp32->pte_lo & PTE_RPGN_32,
		    ptp32->pte_lo & (PTE_REF_32|PTE_CHG_32));
	}

	/* Add a Page Table Entry, section 7.6.3.1. */
	ptp32->pte_hi = pted->p.pted_pte32.pte_hi & ~PTE_VALID_32;
	if (secondary)
		ptp32->pte_hi |= PTE_HID_32;
	ptp32->pte_lo = pted->p.pted_pte32.pte_lo;
	eieio();	/* Order 1st PTE update before 2nd. */
	ptp32->pte_hi |= PTE_VALID_32;
	sync();		/* Ensure updates completed. */

out:
	PMAP_HASH_UNLOCK(s);
}


code:
/*	$OpenBSD: process_machdep.c,v 1.13 2007/09/09 20:49:18 kettenis Exp $	*/
/*	$NetBSD: process_machdep.c,v 1.1 1996/09/30 16:34:53 ws Exp $	*/

/*
 * Copyright (C) 1995, 1996 Wolfgang Solfrank.
 * Copyright (C) 1995, 1996 TooLs GmbH.
 * 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. 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 TooLs GmbH.
 * 4. The name of TooLs GmbH may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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/ptrace.h>
#include <sys/user.h>

#include <machine/fpu.h>
#include <machine/pcb.h>
#include <machine/psl.h>
#include <machine/reg.h>

int
process_read_regs(struct proc *p, struct reg *regs)
{
	struct cpu_info *ci = curcpu();
	struct trapframe *tf = trapframe(p);
	struct pcb *pcb = &p->p_addr->u_pcb;

	bcopy(tf->fixreg, regs->gpr, sizeof(regs->gpr));

	if (!(pcb->pcb_flags & PCB_FPU)) {
		bzero(regs->fpr, sizeof(regs->fpr));
	} else {
		/* XXX What if the state is on the other cpu? */
		if (p == ci->ci_fpuproc)
			save_fpu();
		bcopy(pcb->pcb_fpu.fpr, regs->fpr, sizeof(regs->fpr));
	}

	regs->pc  = tf->srr0;
	regs->ps  = tf->srr1; /* is this the correct value for this ? */
	regs->cnd = tf->cr;
	regs->lr  = tf->lr;
	regs->cnt = tf->ctr;
	regs->xer = tf->xer;
	regs->mq  = 0; /*  what should this really be? */

	return (0);
}

int
process_read_fpregs(struct proc *p, struct fpreg *regs)
{
	struct cpu_info *ci = curcpu();
	struct pcb *pcb = &p->p_addr->u_pcb;

	if (!(pcb->pcb_flags & PCB_FPU)) {
		bzero(regs->fpr, sizeof(regs->fpr));
		regs->fpscr = 0;
	} else {
		/* XXX What if the state is on the other cpu? */
		if (p == ci->ci_fpuproc)
			save_fpu();
		bcopy(pcb->pcb_fpu.fpr, regs->fpr, sizeof(regs->fpr));
		regs->fpscr = *(u_int64_t *)&pcb->pcb_fpu.fpcsr;
	}

	return (0);
}

#ifdef PTRACE

/*
 * Set the process's program counter.
 */
int
process_set_pc(struct proc *p, caddr_t addr)
{
	struct trapframe *tf = trapframe(p);

	tf->srr0 = (u_int32_t)addr;
	return 0;
}

int
process_sstep(struct proc *p, int sstep)
{
	struct trapframe *tf = trapframe(p);

	if (sstep)
		tf->srr1 |= PSL_SE;
	else
		tf->srr1 &= ~PSL_SE;
	return 0;
}

int
process_write_regs(struct proc *p, struct reg *regs)
{
	struct cpu_info *ci = curcpu();
	struct trapframe *tf = trapframe(p);
	struct pcb *pcb = &p->p_addr->u_pcb;

	if ((regs->ps ^ tf->srr1) & PSL_USERSTATIC)
		return EINVAL;

	bcopy(regs->gpr, tf->fixreg, sizeof(regs->gpr));

	/* XXX What if the state is on the other cpu? */
	if (p == ci->ci_fpuproc) {	/* release the fpu */
		save_fpu();
		ci->ci_fpuproc = NULL;
	}

	bcopy(regs->fpr, pcb->pcb_fpu.fpr, sizeof(regs->fpr));
	if (!(pcb->pcb_flags & PCB_FPU)) {
		pcb->pcb_fpu.fpcsr = 0;
		pcb->pcb_flags |= PCB_FPU;
	}

	tf->srr0 = regs->pc;
	tf->srr1 = regs->ps;  /* is this the correct value for this ? */
	tf->cr   = regs->cnd;
	tf->lr   = regs->lr;
	tf->ctr  = regs->cnt;
	tf->xer  = regs->xer;
	/*  regs->mq = 0; what should this really be? */

	return (0);
}

int
process_write_fpregs(struct proc *p, struct fpreg *regs)
{
	struct cpu_info *ci = curcpu();
	struct pcb *pcb = &p->p_addr->u_pcb;
	u_int64_t fpscr = regs->fpscr;

	/* XXX What if the state is on the other cpu? */
	if (p == ci->ci_fpuproc) {	/* release the fpu */
		save_fpu();
		ci->ci_fpuproc = NULL;
	}

	bcopy(regs->fpr, pcb->pcb_fpu.fpr, sizeof(regs->fpr));
	pcb->pcb_fpu.fpcsr = *(double *)&fpscr;
	pcb->pcb_flags |= PCB_FPU;

	return (0);
}

#endif	/* PTRACE */

license: unknown


code:
/*	$OpenBSD: setjmp.S,v 1.9 2008/05/21 19:42:07 miod Exp $	*/
/* kernel version of this file, does not have signal goop */
/* int setjmp(jmp_buf env) */

/*
 * Copyright (c) 1996 Dale Rahn.
 * 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. 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 <machine/asm.h>
#define JMP_r1	0x04
#define JMP_r14	0x08
#define JMP_r15	0x0c
#define JMP_r16	0x10
#define JMP_r17	0x14
#define JMP_r18	0x18
#define JMP_r19	0x1c
#define JMP_r20	0x20
#define JMP_r21	0x24
#define JMP_r22	0x28
#define JMP_r23	0x2c
#define JMP_r24	0x30
#define JMP_r25	0x34
#define JMP_r26	0x38
#define JMP_r27	0x3c
#define JMP_r28	0x40
#define JMP_r29	0x44
#define JMP_r30	0x48
#define JMP_r31	0x4c
#define JMP_lr  0x50
#define JMP_cr  0x54
#define JMP_ctr	0x58
#define JMP_xer	0x5c
#define JMP_sig	0x60

ENTRY(_C_LABEL(setjmp))
	stw %r31, JMP_r31(%r3)
	/* r1, r14-r30 */
	stw %r1,  JMP_r1 (%r3)
	stw %r14, JMP_r14(%r3)
	stw %r15, JMP_r15(%r3)
	stw %r16, JMP_r16(%r3)
	stw %r17, JMP_r17(%r3)
	stw %r18, JMP_r18(%r3)
	stw %r19, JMP_r19(%r3)
	stw %r20, JMP_r20(%r3)
	stw %r21, JMP_r21(%r3)
	stw %r22, JMP_r22(%r3)
	stw %r23, JMP_r23(%r3)
	stw %r24, JMP_r24(%r3)
	stw %r25, JMP_r25(%r3)
	stw %r26, JMP_r26(%r3)
	stw %r27, JMP_r27(%r3)
	stw %r28, JMP_r28(%r3)
	stw %r29, JMP_r29(%r3)
	stw %r30, JMP_r30(%r3)
	/* cr, lr, ctr, xer */
	mfcr %r0
	stw %r0, JMP_cr(%r3)
	mflr %r0
	stw %r0, JMP_lr(%r3)
	mfctr %r0
	stw %r0, JMP_ctr(%r3)
	mfxer %r0
	stw %r0, JMP_xer(%r3)
	/* f14-f31, fpscr */
	li %r3, 0
	blr

ENTRY(_C_LABEL(longjmp))
	lwz %r31, JMP_r31(%r3)
	/* r1, r14-r30 */
	lwz %r1,  JMP_r1 (%r3)
	lwz %r14, JMP_r14(%r3)
	lwz %r15, JMP_r15(%r3)
	lwz %r16, JMP_r16(%r3)
	lwz %r17, JMP_r17(%r3)
	lwz %r18, JMP_r18(%r3)
	lwz %r19, JMP_r19(%r3)
	lwz %r20, JMP_r20(%r3)
	lwz %r21, JMP_r21(%r3)
	lwz %r22, JMP_r22(%r3)
	lwz %r23, JMP_r23(%r3)
	lwz %r24, JMP_r24(%r3)
	lwz %r25, JMP_r25(%r3)
	lwz %r26, JMP_r26(%r3)
	lwz %r27, JMP_r27(%r3)
	lwz %r28, JMP_r28(%r3)
	lwz %r29, JMP_r29(%r3)
	lwz %r30, JMP_r30(%r3)
	/* cr, lr, ctr, xer */
	lwz %r0, JMP_cr(%r3)
	mtcr %r0
	lwz %r0, JMP_lr(%r3)
	mtlr %r0
	lwz %r0, JMP_ctr(%r3)
	mtctr %r0
	lwz %r0, JMP_xer(%r3)
	mtxer %r0
	/* f14-f31, fpscr */
	li %r3, 1
	blr


code:
/*	$OpenBSD: softintr.c,v 1.7 2015/01/04 13:01:42 mpi 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/malloc.h>
#include <sys/mutex.h>

#include <uvm/uvm_extern.h>

#include <machine/atomic.h>
#include <machine/intr.h>

struct soft_intrq soft_intrq[SI_NQUEUES];

struct soft_intrhand *softnet_intrhand;

/*
 * 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;

		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) {
#if 0
	case IPL_SOFT:
		si = SI_SOFT;
		break;
#endif
	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 soft_intrhand *sih = (struct soft_intrhand *)arg;
	struct soft_intrq *siq = sih->sih_siq;
	struct cpu_info *ci = curcpu();

	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_ipending, SI_TO_IRQBIT(siq->siq_si));
	}
	mtx_leave(&siq->siq_mtx);
}

void
dosoftint(int pcpl)
{
	struct cpu_info *ci = curcpu();
	int sir, q, mask;

	ppc_intr_enable(1);
	KERNEL_LOCK();

	while ((sir = (ci->ci_ipending & ppc_smask[pcpl])) != 0) {
		atomic_clearbits_int(&ci->ci_ipending, sir);

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

	KERNEL_UNLOCK();
	(void)ppc_intr_disable();
}


code:
/*	$OpenBSD: sys_machdep.c,v 1.4 2003/10/15 02:43:09 drahn Exp $	*/
/*	$NetBSD: sys_machdep.c,v 1.1 1996/09/30 16:34:56 ws Exp $	*/

/*
 * Copyright (C) 1996 Wolfgang Solfrank.
 * Copyright (C) 1996 TooLs GmbH.
 * 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. 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 TooLs GmbH.
 * 4. The name of TooLs GmbH may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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/mount.h>
#include <sys/syscallargs.h>

int
sys_sysarch(struct proc *p, void *v, register_t *retval)
{
	/*
	 * Currently no special system calls
	 */
	return EINVAL;
}


code:
/*	$OpenBSD: trap.c,v 1.105 2015/07/29 18:52:44 miod Exp $	*/
/*	$NetBSD: trap.c,v 1.3 1996/10/13 03:31:37 christos Exp $	*/

/*
 * Copyright (C) 1995, 1996 Wolfgang Solfrank.
 * Copyright (C) 1995, 1996 TooLs GmbH.
 * 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. 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 TooLs GmbH.
 * 4. The name of TooLs GmbH may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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/proc.h>
#include <sys/signalvar.h>
#include <sys/reboot.h>
#include <sys/systm.h>
#include <sys/user.h>
#include <sys/pool.h>
#include <sys/syscall.h>
#include <sys/syscall_mi.h>

#include <dev/cons.h>

#include <uvm/uvm_extern.h>

#include <machine/cpu.h>
#include <machine/fpu.h>
#include <machine/frame.h>
#include <machine/pcb.h>
#include <machine/psl.h>
#include <machine/trap.h>
#include <machine/db_machdep.h>

#include <ddb/db_extern.h>
#include <ddb/db_sym.h>
#include <ddb/db_output.h>

static int fix_unaligned(struct proc *p, struct trapframe *frame);
int badaddr(char *addr, u_int32_t len);
void trap(struct trapframe *frame);

/* These definitions should probably be somewhere else				XXX */
#define	FIRSTARG	3		/* first argument is in reg 3 */
#define	NARGREG		8		/* 8 args are in registers */
#define	MOREARGS(sp)	((caddr_t)((int)(sp) + 8)) /* more args go here */

#ifdef ALTIVEC
/*
 * Save state of the vector processor, This is done lazily in the hope
 * that few processes in the system will be using the vector unit
 * and that the exception time taken to switch them will be less than
 * the necessary time to save the vector on every context switch.
 *
 * Also note that in this version, the VRSAVE register is saved with
 * the state of the current process holding the vector processor,
 * and the contents of that register are not used to optimize the save.
 *
 * This can lead to VRSAVE corruption, data passing between processes,
 * because this register is accessable without the MSR[VEC] bit set.
 * To store/restore this cleanly a processor identifier bit would need
 * to be saved and this register saved on every context switch.
 * Since we do not use the information, we may be able to get by
 * with not saving it rigorously.
 */
void
save_vec(struct proc *p)
{
	struct pcb *pcb = &p->p_addr->u_pcb;
	struct vreg *pcb_vr = pcb->pcb_vr;
	u_int32_t oldmsr, msr;

	/* first we enable vector so that we dont throw an exception
	 * in kernel mode
	 */
	oldmsr = ppc_mfmsr();
	msr = (oldmsr & ~PSL_EE) | PSL_VEC;
	ppc_mtmsr(msr);
	__asm__ volatile ("sync;isync");

	pcb->pcb_vr->vrsave = ppc_mfvrsave();

#define STR(x) #x
#define SAVE_VEC_REG(reg, addr)   \
	__asm__ volatile ("stvxl %0, 0, %1" :: "n"(reg),"r" (addr));

	SAVE_VEC_REG(0,&pcb_vr->vreg[0]);
	SAVE_VEC_REG(1,&pcb_vr->vreg[1]);
	SAVE_VEC_REG(2,&pcb_vr->vreg[2]);
	SAVE_VEC_REG(3,&pcb_vr->vreg[3]);
	SAVE_VEC_REG(4,&pcb_vr->vreg[4]);
	SAVE_VEC_REG(5,&pcb_vr->vreg[5]);
	SAVE_VEC_REG(6,&pcb_vr->vreg[6]);
	SAVE_VEC_REG(7,&pcb_vr->vreg[7]);
	SAVE_VEC_REG(8,&pcb_vr->vreg[8]);
	SAVE_VEC_REG(9,&pcb_vr->vreg[9]);
	SAVE_VEC_REG(10,&pcb_vr->vreg[10]);
	SAVE_VEC_REG(11,&pcb_vr->vreg[11]);
	SAVE_VEC_REG(12,&pcb_vr->vreg[12]);
	SAVE_VEC_REG(13,&pcb_vr->vreg[13]);
	SAVE_VEC_REG(14,&pcb_vr->vreg[14]);
	SAVE_VEC_REG(15,&pcb_vr->vreg[15]);
	SAVE_VEC_REG(16,&pcb_vr->vreg[16]);
	SAVE_VEC_REG(17,&pcb_vr->vreg[17]);
	SAVE_VEC_REG(18,&pcb_vr->vreg[18]);
	SAVE_VEC_REG(19,&pcb_vr->vreg[19]);
	SAVE_VEC_REG(20,&pcb_vr->vreg[20]);
	SAVE_VEC_REG(21,&pcb_vr->vreg[21]);
	SAVE_VEC_REG(22,&pcb_vr->vreg[22]);
	SAVE_VEC_REG(23,&pcb_vr->vreg[23]);
	SAVE_VEC_REG(24,&pcb_vr->vreg[24]);
	SAVE_VEC_REG(25,&pcb_vr->vreg[25]);
	SAVE_VEC_REG(26,&pcb_vr->vreg[26]);
	SAVE_VEC_REG(27,&pcb_vr->vreg[27]);
	SAVE_VEC_REG(28,&pcb_vr->vreg[28]);
	SAVE_VEC_REG(29,&pcb_vr->vreg[29]);
	SAVE_VEC_REG(30,&pcb_vr->vreg[30]);
	SAVE_VEC_REG(31,&pcb_vr->vreg[31]);
	__asm__ volatile ("mfvscr 0");
	SAVE_VEC_REG(0,&pcb_vr->vscr);

	curcpu()->ci_vecproc = NULL;
	pcb->pcb_veccpu = NULL;

	/* fix kernel msr back */
	ppc_mtmsr(oldmsr);
}

/*
 * Copy the context of a given process into the vector registers.
 */
void
enable_vec(struct proc *p)
{
	struct pcb *pcb = &p->p_addr->u_pcb;
	struct vreg *pcb_vr = pcb->pcb_vr;
	struct cpu_info *ci = curcpu();
	u_int32_t oldmsr, msr;

	/* If this is the very first altivec instruction executed
	 * by this process, create a context.
	 */
	if (pcb->pcb_vr == NULL)
		pcb->pcb_vr = pool_get(&ppc_vecpl, PR_WAITOK | PR_ZERO);

	if (curcpu()->ci_vecproc != NULL || pcb->pcb_veccpu != NULL)
		printf("attempting to restore vector in use vecproc %p"
		    " veccpu %p\n", curcpu()->ci_vecproc, pcb->pcb_veccpu);

	/* first we enable vector so that we dont throw an exception
	 * in kernel mode
	 */
	oldmsr = ppc_mfmsr();
	msr = (oldmsr & ~PSL_EE) | PSL_VEC;
	ppc_mtmsr(msr);
	__asm__ volatile ("sync;isync");
	ci->ci_vecproc = p;
	pcb->pcb_veccpu = ci;

#define LOAD_VEC_REG(reg, addr)   \
	__asm__ volatile ("lvxl %0, 0, %1" :: "n"(reg), "r" (addr));

	LOAD_VEC_REG(0, &pcb_vr->vscr);
	__asm__ volatile ("mtvscr 0");
	ppc_mtvrsave(pcb_vr->vrsave);

	LOAD_VEC_REG(0, &pcb_vr->vreg[0]);
	LOAD_VEC_REG(1, &pcb_vr->vreg[1]);
	LOAD_VEC_REG(2, &pcb_vr->vreg[2]);
	LOAD_VEC_REG(3, &pcb_vr->vreg[3]);
	LOAD_VEC_REG(4, &pcb_vr->vreg[4]);
	LOAD_VEC_REG(5, &pcb_vr->vreg[5]);
	LOAD_VEC_REG(6, &pcb_vr->vreg[6]);
	LOAD_VEC_REG(7, &pcb_vr->vreg[7]);
	LOAD_VEC_REG(8, &pcb_vr->vreg[8]);
	LOAD_VEC_REG(9, &pcb_vr->vreg[9]);
	LOAD_VEC_REG(10, &pcb_vr->vreg[10]);
	LOAD_VEC_REG(11, &pcb_vr->vreg[11]);
	LOAD_VEC_REG(12, &pcb_vr->vreg[12]);
	LOAD_VEC_REG(13, &pcb_vr->vreg[13]);
	LOAD_VEC_REG(14, &pcb_vr->vreg[14]);
	LOAD_VEC_REG(15, &pcb_vr->vreg[15]);
	LOAD_VEC_REG(16, &pcb_vr->vreg[16]);
	LOAD_VEC_REG(17, &pcb_vr->vreg[17]);
	LOAD_VEC_REG(18, &pcb_vr->vreg[18]);
	LOAD_VEC_REG(19, &pcb_vr->vreg[19]);
	LOAD_VEC_REG(20, &pcb_vr->vreg[20]);
	LOAD_VEC_REG(21, &pcb_vr->vreg[21]);
	LOAD_VEC_REG(22, &pcb_vr->vreg[22]);
	LOAD_VEC_REG(23, &pcb_vr->vreg[23]);
	LOAD_VEC_REG(24, &pcb_vr->vreg[24]);
	LOAD_VEC_REG(25, &pcb_vr->vreg[25]);
	LOAD_VEC_REG(26, &pcb_vr->vreg[26]);
	LOAD_VEC_REG(27, &pcb_vr->vreg[27]);
	LOAD_VEC_REG(28, &pcb_vr->vreg[28]);
	LOAD_VEC_REG(29, &pcb_vr->vreg[29]);
	LOAD_VEC_REG(30, &pcb_vr->vreg[30]);
	LOAD_VEC_REG(31, &pcb_vr->vreg[31]);

	/* fix kernel msr back */
	ppc_mtmsr(oldmsr);
}
#endif /* ALTIVEC */

void
trap(struct trapframe *frame)
{
	struct cpu_info *ci = curcpu();
	struct proc *p = curproc;
	int type = frame->exc;
	union sigval sv;
	char *name;
	db_expr_t offset;

	if (frame->srr1 & PSL_PR) {
		type |= EXC_USER;
		refreshcreds(p);
	}

	switch (type) {
	case EXC_TRC|EXC_USER:
		{
			sv.sival_int = frame->srr0;
			KERNEL_LOCK();
			trapsignal(p, SIGTRAP, type, TRAP_TRACE, sv);
			KERNEL_UNLOCK();
		}
		break;

	case EXC_MCHK:
		{
			faultbuf *fb;

			if ((fb = p->p_addr->u_pcb.pcb_onfault)) {
				p->p_addr->u_pcb.pcb_onfault = 0;
				frame->srr0 = fb->pc;		/* PC */
				frame->fixreg[1] = fb->sp;	/* SP */
				frame->fixreg[3] = 1;		/* != 0 */
				frame->cr = fb->cr;
				bcopy(&fb->regs[0], &frame->fixreg[13], 19*4);
				return;
			}
		}
		goto brain_damage;

	case EXC_DSI:
		{
			struct vm_map *map;
			vaddr_t va;
			int ftype;
			faultbuf *fb;

			map = kernel_map;
			va = frame->dar;
			if ((va >> ADDR_SR_SHIFT) == PPC_USER_SR) {
				sr_t user_sr;

				asm ("mfsr %0, %1"
				     : "=r"(user_sr) : "K"(PPC_USER_SR));
				va &= ADDR_PIDX | ADDR_POFF;
				va |= user_sr << ADDR_SR_SHIFT;
				map = &p->p_vmspace->vm_map;
				if (pte_spill_v(map->pmap, va, frame->dsisr, 0))
					return;
			}
			if (frame->dsisr & DSISR_STORE)
				ftype = PROT_READ | PROT_WRITE;
			else
				ftype = PROT_READ;
			KERNEL_LOCK();
			if (uvm_fault(map, trunc_page(va), 0, ftype) == 0) {
				KERNEL_UNLOCK();
				return;
			}
			KERNEL_UNLOCK();

			if ((fb = p->p_addr->u_pcb.pcb_onfault)) {
				p->p_addr->u_pcb.pcb_onfault = 0;
				frame->srr0 = fb->pc;		/* PC */
				frame->fixreg[1] = fb->sp;	/* SP */
				frame->fixreg[3] = 1;		/* != 0 */
				frame->cr = fb->cr;
				bcopy(&fb->regs[0], &frame->fixreg[13], 19*4);
				return;
			}
			map = kernel_map;
		}
printf("kern dsi on addr %x iar %x\n", frame->dar, frame->srr0);
		goto brain_damage;
	case EXC_DSI|EXC_USER:
		{
			int ftype, vftype;

			/* Try spill handler first */
			if (pte_spill_v(p->p_vmspace->vm_map.pmap,
			    frame->dar, frame->dsisr, 0))
				break;

			KERNEL_LOCK();
			if (frame->dsisr & DSISR_STORE) {
				ftype = PROT_READ | PROT_WRITE;
				vftype = PROT_WRITE;
			} else
				vftype = ftype = PROT_READ;
			if (uvm_fault(&p->p_vmspace->vm_map,
				     trunc_page(frame->dar), 0, ftype) == 0) {
				uvm_grow(p, trunc_page(frame->dar));
				KERNEL_UNLOCK();
				break;
			}

#if 0
printf("dsi on addr %x iar %x lr %x\n", frame->dar, frame->srr0,frame->lr);
#endif
/*
 * keep this for later in case we want it later.
*/
			sv.sival_int = frame->dar;
			trapsignal(p, SIGSEGV, vftype, SEGV_MAPERR, sv);
			KERNEL_UNLOCK();
		}
		break;
	case EXC_ISI|EXC_USER:
		{
			int ftype;

			/* Try spill handler */
			if (pte_spill_v(p->p_vmspace->vm_map.pmap,
			    frame->srr0, 0, 1))
				break;

			KERNEL_LOCK();
			ftype = PROT_READ | PROT_EXEC;
			if (uvm_fault(&p->p_vmspace->vm_map,
			    trunc_page(frame->srr0), 0, ftype) == 0) {
				uvm_grow(p, trunc_page(frame->srr0));
				KERNEL_UNLOCK();
				break;
			}
			KERNEL_UNLOCK();
		}
#if 0
printf("isi iar %x lr %x\n", frame->srr0, frame->lr);
#endif
		/* FALLTHROUGH */
	case EXC_MCHK|EXC_USER:
/* XXX Likely that returning from this trap is bogus... */
/* XXX Have to make sure that sigreturn does the right thing. */
		sv.sival_int = frame->srr0;
		KERNEL_LOCK();
		trapsignal(p, SIGSEGV, PROT_EXEC, SEGV_MAPERR, sv);
		KERNEL_UNLOCK();
		break;
	case EXC_SC|EXC_USER:
		{
			struct sysent *callp;
			size_t argsize;
			register_t code, error;
			register_t *params, rval[2];
			int nsys, n;
			register_t args[10];

			uvmexp.syscalls++;

			nsys = p->p_p->ps_emul->e_nsysent;
			callp = p->p_p->ps_emul->e_sysent;

			code = frame->fixreg[0];
			params = frame->fixreg + FIRSTARG;

			switch (code) {
			case SYS_syscall:
				/*
				 * code is first argument,
				 * followed by actual args.
				 */
				code = *params++;
				break;
			case SYS___syscall:
				/*
				 * Like syscall, but code is a quad,
				 * so as to maintain quad alignment
				 * for the rest of the args.
				 */
				if (callp != sysent)
					break;
				params++;
				code = *params++;
				break;
			default:
				break;
			}
			if (code < 0 || code >= nsys)
				callp += p->p_p->ps_emul->e_nosys;
			else
				callp += code;
			argsize = callp->sy_argsize;
			n = NARGREG - (params - (frame->fixreg + FIRSTARG));
			if (argsize > n * sizeof(register_t)) {
				bcopy(params, args, n * sizeof(register_t));

				if ((error = copyin(MOREARGS(frame->fixreg[1]),
				   args + n,
				   argsize - n * sizeof(register_t))))
					goto bad;
				params = args;
			}

			rval[0] = 0;
			rval[1] = frame->fixreg[FIRSTARG + 1];

			error = mi_syscall(p, code, callp, params, rval);

			switch (error) {
			case 0:
				frame->fixreg[0] = error;
				frame->fixreg[FIRSTARG] = rval[0];
				frame->fixreg[FIRSTARG + 1] = rval[1];
				frame->cr &= ~0x10000000;
				break;
			case ERESTART:
				/*
				 * Set user's pc back to redo the system call.
				 */
				frame->srr0 -= 4;
				break;
			case EJUSTRETURN:
				/* nothing to do */
				break;
			default:
			bad:
				frame->fixreg[0] = error;
				frame->fixreg[FIRSTARG] = error;
				frame->fixreg[FIRSTARG + 1] = rval[1];
				frame->cr |= 0x10000000;
				break;
			}

			mi_syscall_return(p, code, error, rval);
			goto finish;
		}

	case EXC_FPU|EXC_USER:
		if (ci->ci_fpuproc)
			save_fpu();
		uvmexp.fpswtch++;
		enable_fpu(p);
		break;

	case EXC_ALI|EXC_USER:
		/* alignment exception
		 * we check to see if this can be fixed up
		 * by the code that fixes the typical gcc misaligned code
		 * then kill the process if not.
		 */
		if (fix_unaligned(p, frame) == 0)
			frame->srr0 += 4;
		else {
			sv.sival_int = frame->srr0;
			KERNEL_LOCK();
			trapsignal(p, SIGBUS, PROT_EXEC, BUS_ADRALN,
				sv);
			KERNEL_UNLOCK();
		}
		break;

	default:

brain_damage:
/*
mpc_print_pci_stat();
*/

#ifdef DDB
		/* set up registers */
		db_save_regs(frame);
		db_find_sym_and_offset(frame->srr0, &name, &offset);
#else
		name = NULL;
#endif
		if (!name) {
			name = "0";
			offset = frame->srr0;
		}
		panic ("trap type %x at %x (%s+0x%lx) lr %lx",
			type, frame->srr0, name, offset, frame->lr);

	case EXC_PGM|EXC_USER:
	{
#if 0
		char *errstr[8];
		int errnum = 0;

		if (frame->srr1 & (1<<(31-11))) {
			/* floating point enabled program exception */
			errstr[errnum] = "floating point";
			errnum++;
		}
		if (frame->srr1 & (1<<(31-12))) {
			/* illegal instruction program exception */
			errstr[errnum] = "illegal instruction";
			errnum++;
		}
		if (frame->srr1 & (1<<(31-13))) {
			/* privileged instruction exception */
			errstr[errnum] = "privileged instr";
			errnum++;
		}
#endif
		if (frame->srr1 & (1<<(31-14))) {
#if 0
			errstr[errnum] = "trap instr";
			errnum++;
#endif
			sv.sival_int = frame->srr0;
			KERNEL_LOCK();
			trapsignal(p, SIGTRAP, type, TRAP_BRKPT, sv);
			KERNEL_UNLOCK();
			break;
		}
#if 0
		if (frame->srr1 & (1<<(31-15))) {
			errstr[errnum] = "previous address";
			errnum++;
		}
#endif
#if 0
printf("pgm iar %x srr1 %x\n", frame->srr0, frame->srr1);
{
int i;
for (i = 0; i < errnum; i++) {
	printf("\t[%s]\n", errstr[i]);
}
}
#endif
		sv.sival_int = frame->srr0;
		KERNEL_LOCK();
		trapsignal(p, SIGILL, 0, ILL_ILLOPC, sv);
		KERNEL_UNLOCK();
		break;
	}
	case EXC_PGM:
		/* should check for correct byte here or panic */
#ifdef DDB
		db_save_regs(frame);
		cnpollc(TRUE);
		db_trap(T_BREAKPOINT, 0);
		cnpollc(FALSE);
#else
		panic("trap EXC_PGM");
#endif
		break;

	/* This is not really a perf exception, but is an ALTIVEC unavail
	 * if we do not handle it, kill the process with illegal instruction.
	 */
	case EXC_PERF|EXC_USER:
#ifdef ALTIVEC
	case EXC_VEC|EXC_USER:
		if (ci->ci_vecproc)
			save_vec(ci->ci_vecproc);

		enable_vec(p);
		break;
#else  /* ALTIVEC */
		sv.sival_int = frame->srr0;
		KERNEL_LOCK();
		trapsignal(p, SIGILL, 0, ILL_ILLOPC, sv);
		KERNEL_UNLOCK();
		break;
#endif

	case EXC_VECAST|EXC_USER:
		KERNEL_LOCK();
		trapsignal(p, SIGFPE, 0, FPE_FLTRES, sv);
		KERNEL_UNLOCK();
		break;

	case EXC_AST|EXC_USER:
		p->p_md.md_astpending = 0;	/* we are about to do it */
		uvmexp.softs++;
		mi_ast(p, ci->ci_want_resched);
		break;
	}

	userret(p);

finish:
	/*
	 * If someone stole the fpu while we were away, disable it
	 */
	if (p != ci->ci_fpuproc)
		frame->srr1 &= ~PSL_FP;
	else if (p->p_addr->u_pcb.pcb_flags & PCB_FPU)
		frame->srr1 |= PSL_FP;

#ifdef ALTIVEC
	/*
	 * If someone stole the vector unit while we were away, disable it
	 */
	if (p == ci->ci_vecproc)
		frame->srr1 |= PSL_VEC;
	else
		frame->srr1 &= ~PSL_VEC;
#endif /* ALTIVEC */
}

void
child_return(void *arg)
{
	struct proc *p = (struct proc *)arg;
	struct trapframe *tf = trapframe(p);

	tf->fixreg[0] = 0;
	tf->fixreg[FIRSTARG] = 0;
	tf->fixreg[FIRSTARG + 1] = 1;
	tf->cr &= ~0x10000000;
	/* Disable FPU, VECT, as we can't be fpuproc */
	tf->srr1 &= ~(PSL_FP|PSL_VEC);

	KERNEL_UNLOCK();

	mi_child_return(p);
}

int
badaddr(char *addr, u_int32_t len)
{
	faultbuf env;
	u_int32_t v;
	void *oldh = curpcb->pcb_onfault;

	if (setfault(&env)) {
		curpcb->pcb_onfault = oldh;
		return EFAULT;
	}
	switch(len) {
	case 4:
		v = *((volatile u_int32_t *)addr);
		break;
	case 2:
		v = *((volatile u_int16_t *)addr);
		break;
	default:
		v = *((volatile u_int8_t *)addr);
		break;
	}
	/* Make sure all loads retire before turning off fault handling!! */
	__asm__ volatile ("sync");
	curpcb->pcb_onfault = oldh;
	return(0);
}

/*
 * For now, this only deals with the particular unaligned access case
 * that gcc tends to generate.  Eventually it should handle all of the
 * possibilities that can happen on a 32-bit PowerPC in big-endian mode.
 */

static int
fix_unaligned(struct proc *p, struct trapframe *frame)
{
	int indicator = EXC_ALI_OPCODE_INDICATOR(frame->dsisr);
	struct cpu_info *ci = curcpu();

	switch (indicator) {
	case EXC_ALI_LFD:
	case EXC_ALI_STFD:
		{
			int reg = EXC_ALI_RST(frame->dsisr);
			double *fpr = &p->p_addr->u_pcb.pcb_fpu.fpr[reg];

			/* Juggle the FPU to ensure that we've initialized
			 * the FPRs, and that their current state is in
			 * the PCB.
			 */
			if (ci->ci_fpuproc != p) {
				if (ci->ci_fpuproc)
					save_fpu();
				enable_fpu(p);
			}
			save_fpu();

			if (indicator == EXC_ALI_LFD) {
				if (copyin((void *)frame->dar, fpr,
				    sizeof(double)) != 0)
					return -1;
			} else {
				if (copyout(fpr, (void *)frame->dar,
				    sizeof(double)) != 0)
					return -1;
			}
			enable_fpu(p);
			return 0;
		}
		break;
	}
	return -1;
}


code:
/*	$OpenBSD: vm_machdep.c,v 1.48 2015/05/05 02:13:47 guenther Exp $	*/
/*	$NetBSD: vm_machdep.c,v 1.1 1996/09/30 16:34:57 ws Exp $	*/

/*
 * Copyright (C) 1995, 1996 Wolfgang Solfrank.
 * Copyright (C) 1995, 1996 TooLs GmbH.
 * 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. 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 TooLs GmbH.
 * 4. The name of TooLs GmbH may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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/exec.h>
#include <sys/pool.h>
#include <sys/proc.h>
#include <sys/signalvar.h>
#include <sys/user.h>
#include <sys/vnode.h>
#include <sys/ptrace.h>

#include <uvm/uvm_extern.h>

#include <machine/pcb.h>
#include <machine/fpu.h>

/*
 * Finish a fork operation, with process p2 nearly set up.
 */
void
cpu_fork(struct proc *p1, struct proc *p2, void *stack, size_t stacksize,
    void (*func)(void *), void *arg)
{
	struct trapframe *tf;
	struct callframe *cf;
	struct switchframe *sf;
	caddr_t stktop1, stktop2;
	extern void fork_trampoline(void);
	struct pcb *pcb = &p2->p_addr->u_pcb;
	struct cpu_info *ci = curcpu();

	if (p1 == ci->ci_fpuproc)
		save_fpu();
	*pcb = p1->p_addr->u_pcb;

#ifdef ALTIVEC
	if (p1->p_addr->u_pcb.pcb_vr != NULL) {
		if (p1 == ci->ci_vecproc)
			save_vec(p1);
		pcb->pcb_vr = pool_get(&ppc_vecpl, PR_WAITOK);
		*pcb->pcb_vr = *p1->p_addr->u_pcb.pcb_vr;
	} else
		pcb->pcb_vr = NULL;

#endif /* ALTIVEC */

	pcb->pcb_pm = p2->p_vmspace->vm_map.pmap;

	pmap_extract(pmap_kernel(),
	    (vaddr_t)pcb->pcb_pm, (paddr_t *)&pcb->pcb_pmreal);

	/*
	 * Setup the trap frame for the new process
	 */
	stktop1 = (caddr_t)trapframe(p1);
	stktop2 = (caddr_t)trapframe(p2);
	bcopy(stktop1, stktop2, sizeof(struct trapframe));

	/*
	 * If specified, give the child a different stack.
	 */
	if (stack != NULL) {
		tf = trapframe(p2);
		tf->fixreg[1] = (register_t)stack + stacksize;
	}

	stktop2 = (caddr_t)((u_long)stktop2 & ~15);  /* Align stack pointer */

	/*
	 * There happens to be a callframe, too.
	 */
	cf = (struct callframe *)stktop2;
	cf->lr = (int)fork_trampoline;

	/*
	 * Below the trap frame, there is another call frame:
	 */
	stktop2 -= 16;
	cf = (struct callframe *)stktop2;
	cf->r31 = (register_t)func;
	cf->r30 = (register_t)arg;

	/*
	 * Below that, we allocate the switch frame:
	 */
	/* must match SFRAMELEN in genassym */
	stktop2 -= roundup(sizeof *sf, 16);

	sf = (struct switchframe *)stktop2;
	bzero((void *)sf, sizeof *sf);		/* just in case */
	sf->sp = (int)cf;
	sf->user_sr = pmap_kernel()->pm_sr[PPC_USER_SR]; /* just in case */
	pcb->pcb_sp = (int)stktop2;
}

/*
 * cpu_exit is called as the last action during exit.
 * We release the address space and machine-dependent resources,
 * including the memory for the user structure and kernel stack.
 *
 * Since we don't have curproc anymore, we cannot sleep, and therefor
 * this is at least incorrect for the multiprocessor version.
 * Not sure whether we can get away with this in the single proc version.		XXX
 */
void
cpu_exit(struct proc *p)
{
	struct cpu_info *ci = curcpu();
#ifdef ALTIVEC
	struct pcb *pcb = &p->p_addr->u_pcb;
#endif /* ALTIVEC */

	/* XXX What if the state is on the other cpu? */
	if (p == ci->ci_fpuproc) 	/* release the fpu */
		ci->ci_fpuproc = NULL;

#ifdef ALTIVEC
	/* XXX What if the state is on the other cpu? */
	if (p == ci->ci_vecproc)
		ci->ci_vecproc = NULL; 	/* release the Altivec Unit */
	if (pcb->pcb_vr != NULL)
		pool_put(&ppc_vecpl, pcb->pcb_vr);
#endif /* ALTIVEC */

	pmap_deactivate(p);
	sched_exit(p);
}

/*
 * Map an IO request into kernel virtual address space.
 */
void
vmapbuf(struct buf *bp, vsize_t len)
{
	vaddr_t faddr, taddr, off;
	paddr_t pa;

#ifdef	DIAGNOSTIC
	if (!(bp->b_flags & B_PHYS))
		panic("vmapbuf");
#endif
	faddr = trunc_page((vaddr_t)(bp->b_saveaddr = bp->b_data));
	off = (vaddr_t)bp->b_data - faddr;
	len = round_page(off + len);
	taddr = uvm_km_valloc_wait(phys_map, len);
	bp->b_data = (caddr_t)(taddr + off);
	for (; len > 0; len -= NBPG) {
		pmap_extract(vm_map_pmap(&bp->b_proc->p_vmspace->vm_map),
		    faddr, &pa);
		pmap_enter(vm_map_pmap(phys_map), taddr, pa,
		    PROT_READ | PROT_WRITE, PMAP_WIRED);
		faddr += NBPG;
		taddr += NBPG;
	}
	pmap_update(vm_map_pmap(phys_map));
}

/*
 * Free the io map addresses associated with this IO operation.
 */
void
vunmapbuf(struct buf *bp, vsize_t len)
{
	vaddr_t addr, off;

#ifdef	DIAGNOSTIC
	if (!(bp->b_flags & B_PHYS))
		panic("vunmapbuf");
#endif
	addr = trunc_page((vaddr_t)bp->b_data);
	off = (vaddr_t)bp->b_data - addr;
	len = round_page(off + len);
	pmap_remove(vm_map_pmap(phys_map), addr, addr + len);
	pmap_update(vm_map_pmap(phys_map));
	uvm_km_free_wakeup(phys_map, addr, len);
	bp->b_data = bp->b_saveaddr;
	bp->b_saveaddr = 0;
}


code:
/Makefile/1.8/Sun Dec  8 14:46:39 2013//TOPENBSD_5_8_BASE
D/compile////
D/conf////
D/dev////
D/gio////
D/hpc////
D/include////
D/localbus////
D/pci////
D/sgi////
D/stand////
D/xbow////

path: sys/arch/sgi/CVS/Entries


code: src/sys/arch/sgi

path: sys/arch/sgi/CVS/Repository


code:
#	$OpenBSD: Makefile,v 1.8 2013/12/08 14:46:39 espie Exp $

S=	${.CURDIR}/../..
KFILE=	GENERIC-IP30
.if exists(conf/GENERIC.MP)
KFILE=	GENERIC-IP30.MP
.endif
TDIRS=	${_arch} include pci gio hpc localbus xbow
TAGS=	${.CURDIR}/tags

NOPROG=
NOMAN=
NOOBJ=
SUBDIR=	stand

# config the fattest kernel we can find into a temporary dir
# to create a Makefile.  Then use make to pull some variables
# out and push them into the sub-shell to expand the paths,
# and finally run ctags.
tags::
	TDIR=`mktemp -d /tmp/_tagXXXXXXXXXX` || exit 1; \
	eval "S=${S}" && \
	config -s ${S} -b $${TDIR} ${.CURDIR}/conf/${KFILE} && \
	eval "_arch=\"`make -V _arch -f $${TDIR}/Makefile`\"" && \
	eval "_mach=\"`make -V _mach -f $${TDIR}/Makefile`\"" && \
	eval "_machdir=\$S/arch/$${_mach}" && \
	eval "_archdir=\$S/arch/$${_arch}" && \
	eval "HFILES=\"`find $S \( -path $S/'arch' -o -path $S/stand -o -path $S/lib/libsa -o -path $S'/lib/libkern/arch' \) -prune -o -name '*.h'; find $${_machdir} $${_archdir} $S/lib/libkern/arch/$${_mach} \( -name boot -o -name stand \) -prune -o -name '*.h'`\"" && \
	eval "SFILES=\"`make -V SFILES -f $${TDIR}/Makefile`\"" && \
	eval "CFILES=\"`make -V CFILES -f $${TDIR}/Makefile`\"" && \
	eval "AFILES=\"`make -V AFILES -f $${TDIR}/Makefile`\"" && \
	ctags -wd -f ${TAGS} $${CFILES} $${HFILES} && \
	egrep "^[_A-Z]*ENTRY[_A-Z]*\(.*\)" $${SFILES} $${AFILES} | \
	    sed "s;\\([^:]*\\):\\([^(]*\\)(\\([^, )]*\\)\\(.*\\);\\3	\\1	/^\\2(\\3\\4$$/;" \
	    >> ${TAGS} && \
	sort -o ${TAGS} ${TAGS} && \
	rm -rf $${TDIR}

links:
	-for i in conf ${TDIRS}; do \
	    (cd $$i && rm -f tags; ln -s tags tags); done

obj:	_SUBDIRUSE

.include <bsd.prog.mk>

path: sys/arch/sgi/Makefile


code:
GENERIC-IP22
GENERIC-IP26
GENERIC-IP27
GENERIC-IP27.MP
GENERIC-IP28
GENERIC-IP30
GENERIC-IP30.MP
GENERIC-IP32
RAMDISK-IP22
RAMDISK-IP26
RAMDISK-IP27
RAMDISK-IP28
RAMDISK-IP30
RAMDISK-IP32


code:
/.cvsignore/1.6/Sat Sep 29 21:46:00 2012//TOPENBSD_5_8_BASE
D

path: sys/arch/sgi/compile/CVS/Entries


code: src/sys/arch/sgi/compile

path: sys/arch/sgi/compile/CVS/Repository


code: src/sys/arch/sgi/conf

path: sys/arch/sgi/conf/CVS/Repository