config root man

Current Path : /sys/ia64/ia64/

FreeBSD hs32.drive.ne.jp 9.1-RELEASE FreeBSD 9.1-RELEASE #1: Wed Jan 14 12:18:08 JST 2015 root@hs32.drive.ne.jp:/sys/amd64/compile/hs32 amd64
Upload File :
Current File : //sys/ia64/ia64/pmap.c

/*-
 * Copyright (c) 1991 Regents of the University of California.
 * All rights reserved.
 * Copyright (c) 1994 John S. Dyson
 * All rights reserved.
 * Copyright (c) 1994 David Greenman
 * All rights reserved.
 * Copyright (c) 1998,2000 Doug Rabson
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * the Systems Programming Group of the University of Utah Computer
 * Science Department and William Jolitz of UUNET Technologies 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 by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	from:	@(#)pmap.c	7.7 (Berkeley)	5/12/91
 *	from:	i386 Id: pmap.c,v 1.193 1998/04/19 15:22:48 bde Exp
 *		with some ideas from NetBSD's alpha pmap
 */

#include <sys/cdefs.h>
__FBSDID("$FreeBSD: release/9.1.0/sys/ia64/ia64/pmap.c 226319 2011-10-12 20:08:25Z kib $");

#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/mman.h>
#include <sys/mutex.h>
#include <sys/proc.h>
#include <sys/smp.h>
#include <sys/sysctl.h>
#include <sys/systm.h>

#include <vm/vm.h>
#include <vm/vm_page.h>
#include <vm/vm_map.h>
#include <vm/vm_object.h>
#include <vm/vm_pageout.h>
#include <vm/uma.h>

#include <machine/bootinfo.h>
#include <machine/efi.h>
#include <machine/md_var.h>
#include <machine/pal.h>

/*
 *	Manages physical address maps.
 *
 *	In addition to hardware address maps, this
 *	module is called upon to provide software-use-only
 *	maps which may or may not be stored in the same
 *	form as hardware maps.  These pseudo-maps are
 *	used to store intermediate results from copy
 *	operations to and from address spaces.
 *
 *	Since the information managed by this module is
 *	also stored by the logical address mapping module,
 *	this module may throw away valid virtual-to-physical
 *	mappings at almost any time.  However, invalidations
 *	of virtual-to-physical mappings must be done as
 *	requested.
 *
 *	In order to cope with hardware architectures which
 *	make virtual-to-physical map invalidates expensive,
 *	this module may delay invalidate or reduced protection
 *	operations until such time as they are actually
 *	necessary.  This module is given full information as
 *	to which processors are currently using which maps,
 *	and to when physical maps must be made correct.
 */

/*
 * Following the Linux model, region IDs are allocated in groups of
 * eight so that a single region ID can be used for as many RRs as we
 * want by encoding the RR number into the low bits of the ID.
 *
 * We reserve region ID 0 for the kernel and allocate the remaining
 * IDs for user pmaps.
 *
 * Region 0-3:	User virtually mapped
 * Region 4:	PBVM and special mappings
 * Region 5:	Kernel virtual memory
 * Region 6:	Direct-mapped uncacheable
 * Region 7:	Direct-mapped cacheable
 */

/* XXX move to a header. */
extern uint64_t ia64_gateway_page[];

#ifndef PMAP_SHPGPERPROC
#define PMAP_SHPGPERPROC 200
#endif

#if !defined(DIAGNOSTIC)
#define PMAP_INLINE __inline
#else
#define PMAP_INLINE
#endif

#define	pmap_accessed(lpte)		((lpte)->pte & PTE_ACCESSED)
#define	pmap_dirty(lpte)		((lpte)->pte & PTE_DIRTY)
#define	pmap_exec(lpte)			((lpte)->pte & PTE_AR_RX)
#define	pmap_managed(lpte)		((lpte)->pte & PTE_MANAGED)
#define	pmap_ppn(lpte)			((lpte)->pte & PTE_PPN_MASK)
#define	pmap_present(lpte)		((lpte)->pte & PTE_PRESENT)
#define	pmap_prot(lpte)			(((lpte)->pte & PTE_PROT_MASK) >> 56)
#define	pmap_wired(lpte)		((lpte)->pte & PTE_WIRED)

#define	pmap_clear_accessed(lpte)	(lpte)->pte &= ~PTE_ACCESSED
#define	pmap_clear_dirty(lpte)		(lpte)->pte &= ~PTE_DIRTY
#define	pmap_clear_present(lpte)	(lpte)->pte &= ~PTE_PRESENT
#define	pmap_clear_wired(lpte)		(lpte)->pte &= ~PTE_WIRED

#define	pmap_set_wired(lpte)		(lpte)->pte |= PTE_WIRED

/*
 * The VHPT bucket head structure.
 */
struct ia64_bucket {
	uint64_t	chain;
	struct mtx	mutex;
	u_int		length;
};

/*
 * Statically allocated kernel pmap
 */
struct pmap kernel_pmap_store;

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

/*
 * Kernel virtual memory management.
 */
static int nkpt;
extern struct ia64_lpte ***ia64_kptdir;

#define KPTE_DIR0_INDEX(va) \
	(((va) >> (3*PAGE_SHIFT-8)) & ((1<<(PAGE_SHIFT-3))-1))
#define KPTE_DIR1_INDEX(va) \
	(((va) >> (2*PAGE_SHIFT-5)) & ((1<<(PAGE_SHIFT-3))-1))
#define KPTE_PTE_INDEX(va) \
	(((va) >> PAGE_SHIFT) & ((1<<(PAGE_SHIFT-5))-1))
#define NKPTEPG		(PAGE_SIZE / sizeof(struct ia64_lpte))

vm_offset_t kernel_vm_end;

/* Values for ptc.e. XXX values for SKI. */
static uint64_t pmap_ptc_e_base = 0x100000000;
static uint64_t pmap_ptc_e_count1 = 3;
static uint64_t pmap_ptc_e_count2 = 2;
static uint64_t pmap_ptc_e_stride1 = 0x2000;
static uint64_t pmap_ptc_e_stride2 = 0x100000000;

struct mtx pmap_ptc_mutex;

/*
 * Data for the RID allocator
 */
static int pmap_ridcount;
static int pmap_rididx;
static int pmap_ridmapsz;
static int pmap_ridmax;
static uint64_t *pmap_ridmap;
struct mtx pmap_ridmutex;

/*
 * Data for the pv entry allocation mechanism
 */
static uma_zone_t pvzone;
static int pv_entry_count = 0, pv_entry_max = 0, pv_entry_high_water = 0;

/*
 * Data for allocating PTEs for user processes.
 */
static uma_zone_t ptezone;

/*
 * Virtual Hash Page Table (VHPT) data.
 */
/* SYSCTL_DECL(_machdep); */
SYSCTL_NODE(_machdep, OID_AUTO, vhpt, CTLFLAG_RD, 0, "");

struct ia64_bucket *pmap_vhpt_bucket;

int pmap_vhpt_nbuckets;
SYSCTL_INT(_machdep_vhpt, OID_AUTO, nbuckets, CTLFLAG_RD,
    &pmap_vhpt_nbuckets, 0, "");

int pmap_vhpt_log2size = 0;
TUNABLE_INT("machdep.vhpt.log2size", &pmap_vhpt_log2size);
SYSCTL_INT(_machdep_vhpt, OID_AUTO, log2size, CTLFLAG_RD,
    &pmap_vhpt_log2size, 0, "");

static int pmap_vhpt_inserts;
SYSCTL_INT(_machdep_vhpt, OID_AUTO, inserts, CTLFLAG_RD,
    &pmap_vhpt_inserts, 0, "");

static int pmap_vhpt_population(SYSCTL_HANDLER_ARGS);
SYSCTL_PROC(_machdep_vhpt, OID_AUTO, population, CTLTYPE_INT | CTLFLAG_RD,
    NULL, 0, pmap_vhpt_population, "I", "");

static struct ia64_lpte *pmap_find_vhpt(vm_offset_t va);

static PMAP_INLINE void	free_pv_entry(pv_entry_t pv);
static pv_entry_t get_pv_entry(pmap_t locked_pmap);

static void	pmap_enter_quick_locked(pmap_t pmap, vm_offset_t va,
		    vm_page_t m, vm_prot_t prot);
static void	pmap_free_pte(struct ia64_lpte *pte, vm_offset_t va);
static void	pmap_invalidate_all(void);
static int	pmap_remove_pte(pmap_t pmap, struct ia64_lpte *pte,
		    vm_offset_t va, pv_entry_t pv, int freepte);
static int	pmap_remove_vhpt(vm_offset_t va);
static boolean_t pmap_try_insert_pv_entry(pmap_t pmap, vm_offset_t va,
		    vm_page_t m);

vm_offset_t
pmap_steal_memory(vm_size_t size)
{
	vm_size_t bank_size;
	vm_offset_t pa, va;

	size = round_page(size);

	bank_size = phys_avail[1] - phys_avail[0];
	while (size > bank_size) {
		int i;
		for (i = 0; phys_avail[i+2]; i+= 2) {
			phys_avail[i] = phys_avail[i+2];
			phys_avail[i+1] = phys_avail[i+3];
		}
		phys_avail[i] = 0;
		phys_avail[i+1] = 0;
		if (!phys_avail[0])
			panic("pmap_steal_memory: out of memory");
		bank_size = phys_avail[1] - phys_avail[0];
	}

	pa = phys_avail[0];
	phys_avail[0] += size;

	va = IA64_PHYS_TO_RR7(pa);
	bzero((caddr_t) va, size);
	return va;
}

static void
pmap_initialize_vhpt(vm_offset_t vhpt)
{
	struct ia64_lpte *pte;
	u_int i;

	pte = (struct ia64_lpte *)vhpt;
	for (i = 0; i < pmap_vhpt_nbuckets; i++) {
		pte[i].pte = 0;
		pte[i].itir = 0;
		pte[i].tag = 1UL << 63; /* Invalid tag */
		pte[i].chain = (uintptr_t)(pmap_vhpt_bucket + i);
	}
}

#ifdef SMP
MALLOC_DECLARE(M_SMP);

vm_offset_t
pmap_alloc_vhpt(void)
{
	vm_offset_t vhpt;
	vm_size_t size;

	size = 1UL << pmap_vhpt_log2size;
	vhpt = (uintptr_t)contigmalloc(size, M_SMP, 0, 0UL, ~0UL, size, 0UL);
	if (vhpt != 0) {
		vhpt = IA64_PHYS_TO_RR7(ia64_tpa(vhpt));
		pmap_initialize_vhpt(vhpt);
	}
	return (vhpt);
}
#endif

/*
 *	Bootstrap the system enough to run with virtual memory.
 */
void
pmap_bootstrap()
{
	struct ia64_pal_result res;
	vm_offset_t base;
	size_t size;
	int i, j, count, ridbits;

	/*
	 * Query the PAL Code to find the loop parameters for the
	 * ptc.e instruction.
	 */
	res = ia64_call_pal_static(PAL_PTCE_INFO, 0, 0, 0);
	if (res.pal_status != 0)
		panic("Can't configure ptc.e parameters");
	pmap_ptc_e_base = res.pal_result[0];
	pmap_ptc_e_count1 = res.pal_result[1] >> 32;
	pmap_ptc_e_count2 = res.pal_result[1] & ((1L<<32) - 1);
	pmap_ptc_e_stride1 = res.pal_result[2] >> 32;
	pmap_ptc_e_stride2 = res.pal_result[2] & ((1L<<32) - 1);
	if (bootverbose)
		printf("ptc.e base=0x%lx, count1=%ld, count2=%ld, "
		       "stride1=0x%lx, stride2=0x%lx\n",
		       pmap_ptc_e_base,
		       pmap_ptc_e_count1,
		       pmap_ptc_e_count2,
		       pmap_ptc_e_stride1,
		       pmap_ptc_e_stride2);

	mtx_init(&pmap_ptc_mutex, "PTC.G mutex", NULL, MTX_SPIN);

	/*
	 * Setup RIDs. RIDs 0..7 are reserved for the kernel.
	 *
	 * We currently need at least 19 bits in the RID because PID_MAX
	 * can only be encoded in 17 bits and we need RIDs for 4 regions
	 * per process. With PID_MAX equalling 99999 this means that we
	 * need to be able to encode 399996 (=4*PID_MAX).
	 * The Itanium processor only has 18 bits and the architected
	 * minimum is exactly that. So, we cannot use a PID based scheme
	 * in those cases. Enter pmap_ridmap...
	 * We should avoid the map when running on a processor that has
	 * implemented enough bits. This means that we should pass the
	 * process/thread ID to pmap. This we currently don't do, so we
	 * use the map anyway. However, we don't want to allocate a map
	 * that is large enough to cover the range dictated by the number
	 * of bits in the RID, because that may result in a RID map of
	 * 2MB in size for a 24-bit RID. A 64KB map is enough.
	 * The bottomline: we create a 32KB map when the processor only
	 * implements 18 bits (or when we can't figure it out). Otherwise
	 * we create a 64KB map.
	 */
	res = ia64_call_pal_static(PAL_VM_SUMMARY, 0, 0, 0);
	if (res.pal_status != 0) {
		if (bootverbose)
			printf("Can't read VM Summary - assuming 18 Region ID bits\n");
		ridbits = 18; /* guaranteed minimum */
	} else {
		ridbits = (res.pal_result[1] >> 8) & 0xff;
		if (bootverbose)
			printf("Processor supports %d Region ID bits\n",
			    ridbits);
	}
	if (ridbits > 19)
		ridbits = 19;

	pmap_ridmax = (1 << ridbits);
	pmap_ridmapsz = pmap_ridmax / 64;
	pmap_ridmap = (uint64_t *)pmap_steal_memory(pmap_ridmax / 8);
	pmap_ridmap[0] |= 0xff;
	pmap_rididx = 0;
	pmap_ridcount = 8;
	mtx_init(&pmap_ridmutex, "RID allocator lock", NULL, MTX_DEF);

	/*
	 * Allocate some memory for initial kernel 'page tables'.
	 */
	ia64_kptdir = (void *)pmap_steal_memory(PAGE_SIZE);
	nkpt = 0;
	kernel_vm_end = VM_MIN_KERNEL_ADDRESS;

	for (i = 0; phys_avail[i+2]; i+= 2)
		;
	count = i+2;

	/*
	 * Determine a valid (mappable) VHPT size.
	 */
	TUNABLE_INT_FETCH("machdep.vhpt.log2size", &pmap_vhpt_log2size);
	if (pmap_vhpt_log2size == 0)
		pmap_vhpt_log2size = 20;
	else if (pmap_vhpt_log2size < 16)
		pmap_vhpt_log2size = 16;
	else if (pmap_vhpt_log2size > 28)
		pmap_vhpt_log2size = 28;
	if (pmap_vhpt_log2size & 1)
		pmap_vhpt_log2size--;

	base = 0;
	size = 1UL << pmap_vhpt_log2size;
	for (i = 0; i < count; i += 2) {
		base = (phys_avail[i] + size - 1) & ~(size - 1);
		if (base + size <= phys_avail[i+1])
			break;
	}
	if (!phys_avail[i])
		panic("Unable to allocate VHPT");

	if (base != phys_avail[i]) {
		/* Split this region. */
		for (j = count; j > i; j -= 2) {
			phys_avail[j] = phys_avail[j-2];
			phys_avail[j+1] = phys_avail[j-2+1];
		}
		phys_avail[i+1] = base;
		phys_avail[i+2] = base + size;
	} else
		phys_avail[i] = base + size;

	base = IA64_PHYS_TO_RR7(base);
	PCPU_SET(md.vhpt, base);
	if (bootverbose)
		printf("VHPT: address=%#lx, size=%#lx\n", base, size);

	pmap_vhpt_nbuckets = size / sizeof(struct ia64_lpte);
	pmap_vhpt_bucket = (void *)pmap_steal_memory(pmap_vhpt_nbuckets *
	    sizeof(struct ia64_bucket));
	for (i = 0; i < pmap_vhpt_nbuckets; i++) {
		/* Stolen memory is zeroed. */
		mtx_init(&pmap_vhpt_bucket[i].mutex, "VHPT bucket lock", NULL,
		    MTX_NOWITNESS | MTX_SPIN);
	}

	pmap_initialize_vhpt(base);
	map_vhpt(base);
	ia64_set_pta(base + (1 << 8) + (pmap_vhpt_log2size << 2) + 1);
	ia64_srlz_i();

	virtual_avail = VM_MIN_KERNEL_ADDRESS;
	virtual_end = VM_MAX_KERNEL_ADDRESS;

	/*
	 * Initialize the kernel pmap (which is statically allocated).
	 */
	PMAP_LOCK_INIT(kernel_pmap);
	for (i = 0; i < IA64_VM_MINKERN_REGION; i++)
		kernel_pmap->pm_rid[i] = 0;
	TAILQ_INIT(&kernel_pmap->pm_pvlist);
	PCPU_SET(md.current_pmap, kernel_pmap);

	/* Region 5 is mapped via the VHPT. */
	ia64_set_rr(IA64_RR_BASE(5), (5 << 8) | (PAGE_SHIFT << 2) | 1);

	/*
	 * Clear out any random TLB entries left over from booting.
	 */
	pmap_invalidate_all();

	map_gateway_page();
}

static int
pmap_vhpt_population(SYSCTL_HANDLER_ARGS)
{
	int count, error, i;

	count = 0;
	for (i = 0; i < pmap_vhpt_nbuckets; i++)
		count += pmap_vhpt_bucket[i].length;

	error = SYSCTL_OUT(req, &count, sizeof(count));
	return (error);
}

vm_offset_t
pmap_page_to_va(vm_page_t m)
{
	vm_paddr_t pa;
	vm_offset_t va;

	pa = VM_PAGE_TO_PHYS(m);
	va = (m->md.memattr == VM_MEMATTR_UNCACHEABLE) ? IA64_PHYS_TO_RR6(pa) :
	    IA64_PHYS_TO_RR7(pa);
	return (va);
}

/*
 *	Initialize a vm_page's machine-dependent fields.
 */
void
pmap_page_init(vm_page_t m)
{

	TAILQ_INIT(&m->md.pv_list);
	m->md.pv_list_count = 0;
	m->md.memattr = VM_MEMATTR_DEFAULT;
}

/*
 *	Initialize the pmap module.
 *	Called by vm_init, to initialize any structures that the pmap
 *	system needs to map virtual memory.
 */
void
pmap_init(void)
{
	int shpgperproc = PMAP_SHPGPERPROC;

	/*
	 * Initialize the address space (zone) for the pv entries.  Set a
	 * high water mark so that the system can recover from excessive
	 * numbers of pv entries.
	 */
	pvzone = uma_zcreate("PV ENTRY", sizeof(struct pv_entry), NULL, NULL,
	    NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_VM | UMA_ZONE_NOFREE);
	TUNABLE_INT_FETCH("vm.pmap.shpgperproc", &shpgperproc);
	pv_entry_max = shpgperproc * maxproc + cnt.v_page_count;
	TUNABLE_INT_FETCH("vm.pmap.pv_entries", &pv_entry_max);
	pv_entry_high_water = 9 * (pv_entry_max / 10);

	ptezone = uma_zcreate("PT ENTRY", sizeof (struct ia64_lpte), 
	    NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_VM|UMA_ZONE_NOFREE);
}


/***************************************************
 * Manipulate TLBs for a pmap
 ***************************************************/

static void
pmap_invalidate_page(vm_offset_t va)
{
	struct ia64_lpte *pte;
	struct pcpu *pc;
	uint64_t tag;
	u_int vhpt_ofs;

	critical_enter();

	vhpt_ofs = ia64_thash(va) - PCPU_GET(md.vhpt);
	tag = ia64_ttag(va);
	STAILQ_FOREACH(pc, &cpuhead, pc_allcpu) {
		pte = (struct ia64_lpte *)(pc->pc_md.vhpt + vhpt_ofs);
		atomic_cmpset_64(&pte->tag, tag, 1UL << 63);
	}

	mtx_lock_spin(&pmap_ptc_mutex);

	ia64_ptc_ga(va, PAGE_SHIFT << 2);
	ia64_mf();
	ia64_srlz_i();

	mtx_unlock_spin(&pmap_ptc_mutex);

	ia64_invala();

	critical_exit();
}

static void
pmap_invalidate_all_1(void *arg)
{
	uint64_t addr;
	int i, j;

	critical_enter();
	addr = pmap_ptc_e_base;
	for (i = 0; i < pmap_ptc_e_count1; i++) {
		for (j = 0; j < pmap_ptc_e_count2; j++) {
			ia64_ptc_e(addr);
			addr += pmap_ptc_e_stride2;
		}
		addr += pmap_ptc_e_stride1;
	}
	critical_exit();
}

static void
pmap_invalidate_all(void)
{

#ifdef SMP
	if (mp_ncpus > 1) {
		smp_rendezvous(NULL, pmap_invalidate_all_1, NULL, NULL);
		return;
	}
#endif
	pmap_invalidate_all_1(NULL);
}

static uint32_t
pmap_allocate_rid(void)
{
	uint64_t bit, bits;
	int rid;

	mtx_lock(&pmap_ridmutex);
	if (pmap_ridcount == pmap_ridmax)
		panic("pmap_allocate_rid: All Region IDs used");

	/* Find an index with a free bit. */
	while ((bits = pmap_ridmap[pmap_rididx]) == ~0UL) {
		pmap_rididx++;
		if (pmap_rididx == pmap_ridmapsz)
			pmap_rididx = 0;
	}
	rid = pmap_rididx * 64;

	/* Find a free bit. */
	bit = 1UL;
	while (bits & bit) {
		rid++;
		bit <<= 1;
	}

	pmap_ridmap[pmap_rididx] |= bit;
	pmap_ridcount++;
	mtx_unlock(&pmap_ridmutex);

	return rid;
}

static void
pmap_free_rid(uint32_t rid)
{
	uint64_t bit;
	int idx;

	idx = rid / 64;
	bit = ~(1UL << (rid & 63));

	mtx_lock(&pmap_ridmutex);
	pmap_ridmap[idx] &= bit;
	pmap_ridcount--;
	mtx_unlock(&pmap_ridmutex);
}

/***************************************************
 * Page table page management routines.....
 ***************************************************/

void
pmap_pinit0(struct pmap *pmap)
{
	/* kernel_pmap is the same as any other pmap. */
	pmap_pinit(pmap);
}

/*
 * Initialize a preallocated and zeroed pmap structure,
 * such as one in a vmspace structure.
 */
int
pmap_pinit(struct pmap *pmap)
{
	int i;

	PMAP_LOCK_INIT(pmap);
	for (i = 0; i < IA64_VM_MINKERN_REGION; i++)
		pmap->pm_rid[i] = pmap_allocate_rid();
	TAILQ_INIT(&pmap->pm_pvlist);
	bzero(&pmap->pm_stats, sizeof pmap->pm_stats);
	return (1);
}

/***************************************************
 * Pmap allocation/deallocation routines.
 ***************************************************/

/*
 * Release any resources held by the given physical map.
 * Called when a pmap initialized by pmap_pinit is being released.
 * Should only be called if the map contains no valid mappings.
 */
void
pmap_release(pmap_t pmap)
{
	int i;

	for (i = 0; i < IA64_VM_MINKERN_REGION; i++)
		if (pmap->pm_rid[i])
			pmap_free_rid(pmap->pm_rid[i]);
	PMAP_LOCK_DESTROY(pmap);
}

/*
 * grow the number of kernel page table entries, if needed
 */
void
pmap_growkernel(vm_offset_t addr)
{
	struct ia64_lpte **dir1;
	struct ia64_lpte *leaf;
	vm_page_t nkpg;

	while (kernel_vm_end <= addr) {
		if (nkpt == PAGE_SIZE/8 + PAGE_SIZE*PAGE_SIZE/64)
			panic("%s: out of kernel address space", __func__);

		dir1 = ia64_kptdir[KPTE_DIR0_INDEX(kernel_vm_end)];
		if (dir1 == NULL) {
			nkpg = vm_page_alloc(NULL, nkpt++,
			    VM_ALLOC_NOOBJ|VM_ALLOC_INTERRUPT|VM_ALLOC_WIRED);
			if (!nkpg)
				panic("%s: cannot add dir. page", __func__);

			dir1 = (struct ia64_lpte **)pmap_page_to_va(nkpg);
			bzero(dir1, PAGE_SIZE);
			ia64_kptdir[KPTE_DIR0_INDEX(kernel_vm_end)] = dir1;
		}

		nkpg = vm_page_alloc(NULL, nkpt++,
		    VM_ALLOC_NOOBJ|VM_ALLOC_INTERRUPT|VM_ALLOC_WIRED);
		if (!nkpg)
			panic("%s: cannot add PTE page", __func__);

		leaf = (struct ia64_lpte *)pmap_page_to_va(nkpg);
		bzero(leaf, PAGE_SIZE);
		dir1[KPTE_DIR1_INDEX(kernel_vm_end)] = leaf;

		kernel_vm_end += PAGE_SIZE * NKPTEPG;
	}
}

/***************************************************
 * page management routines.
 ***************************************************/

/*
 * free the pv_entry back to the free list
 */
static PMAP_INLINE void
free_pv_entry(pv_entry_t pv)
{
	pv_entry_count--;
	uma_zfree(pvzone, pv);
}

/*
 * get a new pv_entry, allocating a block from the system
 * when needed.
 */
static pv_entry_t
get_pv_entry(pmap_t locked_pmap)
{
	static const struct timeval printinterval = { 60, 0 };
	static struct timeval lastprint;
	struct vpgqueues *vpq;
	struct ia64_lpte *pte;
	pmap_t oldpmap, pmap;
	pv_entry_t allocated_pv, next_pv, pv;
	vm_offset_t va;
	vm_page_t m;

	PMAP_LOCK_ASSERT(locked_pmap, MA_OWNED);
	mtx_assert(&vm_page_queue_mtx, MA_OWNED);
	allocated_pv = uma_zalloc(pvzone, M_NOWAIT);
	if (allocated_pv != NULL) {
		pv_entry_count++;
		if (pv_entry_count > pv_entry_high_water)
			pagedaemon_wakeup();
		else
			return (allocated_pv);
	}

	/*
	 * Reclaim pv entries: At first, destroy mappings to inactive
	 * pages.  After that, if a pv entry is still needed, destroy
	 * mappings to active pages.
	 */
	if (ratecheck(&lastprint, &printinterval))
		printf("Approaching the limit on PV entries, "
		    "increase the vm.pmap.shpgperproc tunable.\n");
	vpq = &vm_page_queues[PQ_INACTIVE];
retry:
	TAILQ_FOREACH(m, &vpq->pl, pageq) {
		if ((m->flags & PG_MARKER) != 0 || m->hold_count || m->busy)
			continue;
		TAILQ_FOREACH_SAFE(pv, &m->md.pv_list, pv_list, next_pv) {
			va = pv->pv_va;
			pmap = pv->pv_pmap;
			/* Avoid deadlock and lock recursion. */
			if (pmap > locked_pmap)
				PMAP_LOCK(pmap);
			else if (pmap != locked_pmap && !PMAP_TRYLOCK(pmap))
				continue;
			pmap->pm_stats.resident_count--;
			oldpmap = pmap_switch(pmap);
			pte = pmap_find_vhpt(va);
			KASSERT(pte != NULL, ("pte"));
			pmap_remove_vhpt(va);
			pmap_invalidate_page(va);
			pmap_switch(oldpmap);
			if (pmap_accessed(pte))
				vm_page_aflag_set(m, PGA_REFERENCED);
			if (pmap_dirty(pte))
				vm_page_dirty(m);
			pmap_free_pte(pte, va);
			TAILQ_REMOVE(&pmap->pm_pvlist, pv, pv_plist);
			m->md.pv_list_count--;
			TAILQ_REMOVE(&m->md.pv_list, pv, pv_list);
			if (pmap != locked_pmap)
				PMAP_UNLOCK(pmap);
			if (allocated_pv == NULL)
				allocated_pv = pv;
			else
				free_pv_entry(pv);
		}
		if (TAILQ_EMPTY(&m->md.pv_list))
			vm_page_aflag_clear(m, PGA_WRITEABLE);
	}
	if (allocated_pv == NULL) {
		if (vpq == &vm_page_queues[PQ_INACTIVE]) {
			vpq = &vm_page_queues[PQ_ACTIVE];
			goto retry;
		}
		panic("get_pv_entry: increase the vm.pmap.shpgperproc tunable");
	}
	return (allocated_pv);
}

/*
 * Conditionally create a pv entry.
 */
static boolean_t
pmap_try_insert_pv_entry(pmap_t pmap, vm_offset_t va, vm_page_t m)
{
	pv_entry_t pv;

	PMAP_LOCK_ASSERT(pmap, MA_OWNED);
	mtx_assert(&vm_page_queue_mtx, MA_OWNED);
	if (pv_entry_count < pv_entry_high_water && 
	    (pv = uma_zalloc(pvzone, M_NOWAIT)) != NULL) {
		pv_entry_count++;
		pv->pv_va = va;
		pv->pv_pmap = pmap;
		TAILQ_INSERT_TAIL(&pmap->pm_pvlist, pv, pv_plist);
		TAILQ_INSERT_TAIL(&m->md.pv_list, pv, pv_list);
		m->md.pv_list_count++;
		return (TRUE);
	} else
		return (FALSE);
}

/*
 * Add an ia64_lpte to the VHPT.
 */
static void
pmap_enter_vhpt(struct ia64_lpte *pte, vm_offset_t va)
{
	struct ia64_bucket *bckt;
	struct ia64_lpte *vhpte;
	uint64_t pte_pa;

	/* Can fault, so get it out of the way. */
	pte_pa = ia64_tpa((vm_offset_t)pte);

	vhpte = (struct ia64_lpte *)ia64_thash(va);
	bckt = (struct ia64_bucket *)vhpte->chain;

	mtx_lock_spin(&bckt->mutex);
	pte->chain = bckt->chain;
	ia64_mf();
	bckt->chain = pte_pa;

	pmap_vhpt_inserts++;
	bckt->length++;
	mtx_unlock_spin(&bckt->mutex);
}

/*
 * Remove the ia64_lpte matching va from the VHPT. Return zero if it
 * worked or an appropriate error code otherwise.
 */
static int
pmap_remove_vhpt(vm_offset_t va)
{
	struct ia64_bucket *bckt;
	struct ia64_lpte *pte;
	struct ia64_lpte *lpte;
	struct ia64_lpte *vhpte;
	uint64_t chain, tag;

	tag = ia64_ttag(va);
	vhpte = (struct ia64_lpte *)ia64_thash(va);
	bckt = (struct ia64_bucket *)vhpte->chain;

	lpte = NULL;
	mtx_lock_spin(&bckt->mutex);
	chain = bckt->chain;
	pte = (struct ia64_lpte *)IA64_PHYS_TO_RR7(chain);
	while (chain != 0 && pte->tag != tag) {
		lpte = pte;
		chain = pte->chain;
		pte = (struct ia64_lpte *)IA64_PHYS_TO_RR7(chain);
	}
	if (chain == 0) {
		mtx_unlock_spin(&bckt->mutex);
		return (ENOENT);
	}

	/* Snip this pv_entry out of the collision chain. */
	if (lpte == NULL)
		bckt->chain = pte->chain;
	else
		lpte->chain = pte->chain;
	ia64_mf();

	bckt->length--;
	mtx_unlock_spin(&bckt->mutex);
	return (0);
}

/*
 * Find the ia64_lpte for the given va, if any.
 */
static struct ia64_lpte *
pmap_find_vhpt(vm_offset_t va)
{
	struct ia64_bucket *bckt;
	struct ia64_lpte *pte;
	uint64_t chain, tag;

	tag = ia64_ttag(va);
	pte = (struct ia64_lpte *)ia64_thash(va);
	bckt = (struct ia64_bucket *)pte->chain;

	mtx_lock_spin(&bckt->mutex);
	chain = bckt->chain;
	pte = (struct ia64_lpte *)IA64_PHYS_TO_RR7(chain);
	while (chain != 0 && pte->tag != tag) {
		chain = pte->chain;
		pte = (struct ia64_lpte *)IA64_PHYS_TO_RR7(chain);
	}
	mtx_unlock_spin(&bckt->mutex);
	return ((chain != 0) ? pte : NULL);
}

/*
 * Remove an entry from the list of managed mappings.
 */
static int
pmap_remove_entry(pmap_t pmap, vm_page_t m, vm_offset_t va, pv_entry_t pv)
{
	if (!pv) {
		if (m->md.pv_list_count < pmap->pm_stats.resident_count) {
			TAILQ_FOREACH(pv, &m->md.pv_list, pv_list) {
				if (pmap == pv->pv_pmap && va == pv->pv_va) 
					break;
			}
		} else {
			TAILQ_FOREACH(pv, &pmap->pm_pvlist, pv_plist) {
				if (va == pv->pv_va) 
					break;
			}
		}
	}

	if (pv) {
		TAILQ_REMOVE(&m->md.pv_list, pv, pv_list);
		m->md.pv_list_count--;
		if (TAILQ_FIRST(&m->md.pv_list) == NULL)
			vm_page_aflag_clear(m, PGA_WRITEABLE);

		TAILQ_REMOVE(&pmap->pm_pvlist, pv, pv_plist);
		free_pv_entry(pv);
		return 0;
	} else {
		return ENOENT;
	}
}

/*
 * Create a pv entry for page at pa for
 * (pmap, va).
 */
static void
pmap_insert_entry(pmap_t pmap, vm_offset_t va, vm_page_t m)
{
	pv_entry_t pv;

	pv = get_pv_entry(pmap);
	pv->pv_pmap = pmap;
	pv->pv_va = va;

	PMAP_LOCK_ASSERT(pmap, MA_OWNED);
	mtx_assert(&vm_page_queue_mtx, MA_OWNED);
	TAILQ_INSERT_TAIL(&pmap->pm_pvlist, pv, pv_plist);
	TAILQ_INSERT_TAIL(&m->md.pv_list, pv, pv_list);
	m->md.pv_list_count++;
}

/*
 *	Routine:	pmap_extract
 *	Function:
 *		Extract the physical page address associated
 *		with the given map/virtual_address pair.
 */
vm_paddr_t
pmap_extract(pmap_t pmap, vm_offset_t va)
{
	struct ia64_lpte *pte;
	pmap_t oldpmap;
	vm_paddr_t pa;

	pa = 0;
	PMAP_LOCK(pmap);
	oldpmap = pmap_switch(pmap);
	pte = pmap_find_vhpt(va);
	if (pte != NULL && pmap_present(pte))
		pa = pmap_ppn(pte);
	pmap_switch(oldpmap);
	PMAP_UNLOCK(pmap);
	return (pa);
}

/*
 *	Routine:	pmap_extract_and_hold
 *	Function:
 *		Atomically extract and hold the physical page
 *		with the given pmap and virtual address pair
 *		if that mapping permits the given protection.
 */
vm_page_t
pmap_extract_and_hold(pmap_t pmap, vm_offset_t va, vm_prot_t prot)
{
	struct ia64_lpte *pte;
	pmap_t oldpmap;
	vm_page_t m;
	vm_paddr_t pa;

	pa = 0;
	m = NULL;
	PMAP_LOCK(pmap);
	oldpmap = pmap_switch(pmap);
retry:
	pte = pmap_find_vhpt(va);
	if (pte != NULL && pmap_present(pte) &&
	    (pmap_prot(pte) & prot) == prot) {
		m = PHYS_TO_VM_PAGE(pmap_ppn(pte));
		if (vm_page_pa_tryrelock(pmap, pmap_ppn(pte), &pa))
			goto retry;
		vm_page_hold(m);
	}
	PA_UNLOCK_COND(pa);
	pmap_switch(oldpmap);
	PMAP_UNLOCK(pmap);
	return (m);
}

/***************************************************
 * Low level mapping routines.....
 ***************************************************/

/*
 * Find the kernel lpte for mapping the given virtual address, which
 * must be in the part of region 5 which we can cover with our kernel
 * 'page tables'.
 */
static struct ia64_lpte *
pmap_find_kpte(vm_offset_t va)
{
	struct ia64_lpte **dir1;
	struct ia64_lpte *leaf;

	KASSERT((va >> 61) == 5,
		("kernel mapping 0x%lx not in region 5", va));
	KASSERT(va < kernel_vm_end,
		("kernel mapping 0x%lx out of range", va));

	dir1 = ia64_kptdir[KPTE_DIR0_INDEX(va)];
	leaf = dir1[KPTE_DIR1_INDEX(va)];
	return (&leaf[KPTE_PTE_INDEX(va)]);
}

/*
 * Find a pte suitable for mapping a user-space address. If one exists 
 * in the VHPT, that one will be returned, otherwise a new pte is
 * allocated.
 */
static struct ia64_lpte *
pmap_find_pte(vm_offset_t va)
{
	struct ia64_lpte *pte;

	if (va >= VM_MAXUSER_ADDRESS)
		return pmap_find_kpte(va);

	pte = pmap_find_vhpt(va);
	if (pte == NULL) {
		pte = uma_zalloc(ptezone, M_NOWAIT | M_ZERO);
		pte->tag = 1UL << 63;
	}
	return (pte);
}

/*
 * Free a pte which is now unused. This simply returns it to the zone
 * allocator if it is a user mapping. For kernel mappings, clear the
 * valid bit to make it clear that the mapping is not currently used.
 */
static void
pmap_free_pte(struct ia64_lpte *pte, vm_offset_t va)
{
	if (va < VM_MAXUSER_ADDRESS)
		uma_zfree(ptezone, pte);
	else
		pmap_clear_present(pte);
}

static PMAP_INLINE void
pmap_pte_prot(pmap_t pm, struct ia64_lpte *pte, vm_prot_t prot)
{
	static long prot2ar[4] = {
		PTE_AR_R,		/* VM_PROT_NONE */
		PTE_AR_RW,		/* VM_PROT_WRITE */
		PTE_AR_RX|PTE_ED,	/* VM_PROT_EXECUTE */
		PTE_AR_RWX|PTE_ED	/* VM_PROT_WRITE|VM_PROT_EXECUTE */
	};

	pte->pte &= ~(PTE_PROT_MASK | PTE_PL_MASK | PTE_AR_MASK | PTE_ED);
	pte->pte |= (uint64_t)(prot & VM_PROT_ALL) << 56;
	pte->pte |= (prot == VM_PROT_NONE || pm == kernel_pmap)
	    ? PTE_PL_KERN : PTE_PL_USER;
	pte->pte |= prot2ar[(prot & VM_PROT_ALL) >> 1];
}

static PMAP_INLINE void
pmap_pte_attr(struct ia64_lpte *pte, vm_memattr_t ma)
{

	pte->pte &= ~PTE_MA_MASK;
	pte->pte |= (ma & PTE_MA_MASK);
}

/*
 * Set a pte to contain a valid mapping and enter it in the VHPT. If
 * the pte was orginally valid, then its assumed to already be in the
 * VHPT.
 * This functions does not set the protection bits.  It's expected
 * that those have been set correctly prior to calling this function.
 */
static void
pmap_set_pte(struct ia64_lpte *pte, vm_offset_t va, vm_offset_t pa,
    boolean_t wired, boolean_t managed)
{

	pte->pte &= PTE_PROT_MASK | PTE_MA_MASK | PTE_PL_MASK |
	    PTE_AR_MASK | PTE_ED;
	pte->pte |= PTE_PRESENT;
	pte->pte |= (managed) ? PTE_MANAGED : (PTE_DIRTY | PTE_ACCESSED);
	pte->pte |= (wired) ? PTE_WIRED : 0;
	pte->pte |= pa & PTE_PPN_MASK;

	pte->itir = PAGE_SHIFT << 2;

	pte->tag = ia64_ttag(va);
}

/*
 * Remove the (possibly managed) mapping represented by pte from the
 * given pmap.
 */
static int
pmap_remove_pte(pmap_t pmap, struct ia64_lpte *pte, vm_offset_t va,
		pv_entry_t pv, int freepte)
{
	int error;
	vm_page_t m;

	/*
	 * First remove from the VHPT.
	 */
	error = pmap_remove_vhpt(va);
	if (error)
		return (error);

	pmap_invalidate_page(va);

	if (pmap_wired(pte))
		pmap->pm_stats.wired_count -= 1;

	pmap->pm_stats.resident_count -= 1;
	if (pmap_managed(pte)) {
		m = PHYS_TO_VM_PAGE(pmap_ppn(pte));
		if (pmap_dirty(pte))
			vm_page_dirty(m);
		if (pmap_accessed(pte))
			vm_page_aflag_set(m, PGA_REFERENCED);

		error = pmap_remove_entry(pmap, m, va, pv);
	}
	if (freepte)
		pmap_free_pte(pte, va);

	return (error);
}

/*
 * Extract the physical page address associated with a kernel
 * virtual address.
 */
vm_paddr_t
pmap_kextract(vm_offset_t va)
{
	struct ia64_lpte *pte;
	uint64_t *pbvm_pgtbl;
	vm_paddr_t pa;
	u_int idx;

	KASSERT(va >= VM_MAXUSER_ADDRESS, ("Must be kernel VA"));

	/* Regions 6 and 7 are direct mapped. */
	if (va >= IA64_RR_BASE(6)) {
		pa = IA64_RR_MASK(va);
		goto out;
	}

	/* Region 5 is our KVA. Bail out if the VA is beyond our limits. */
	if (va >= kernel_vm_end)
		goto err_out;
	if (va >= VM_MIN_KERNEL_ADDRESS) {
		pte = pmap_find_kpte(va);
		pa = pmap_present(pte) ? pmap_ppn(pte) | (va & PAGE_MASK) : 0;
		goto out;
	}

	/* The PBVM page table. */
	if (va >= IA64_PBVM_PGTBL + bootinfo->bi_pbvm_pgtblsz)
		goto err_out;
	if (va >= IA64_PBVM_PGTBL) {
		pa = (va - IA64_PBVM_PGTBL) + bootinfo->bi_pbvm_pgtbl;
		goto out;
	}

	/* The PBVM itself. */
	if (va >= IA64_PBVM_BASE) {
		pbvm_pgtbl = (void *)IA64_PBVM_PGTBL;
		idx = (va - IA64_PBVM_BASE) >> IA64_PBVM_PAGE_SHIFT;
		if (idx >= (bootinfo->bi_pbvm_pgtblsz >> 3))
			goto err_out;
		if ((pbvm_pgtbl[idx] & PTE_PRESENT) == 0)
			goto err_out;
		pa = (pbvm_pgtbl[idx] & PTE_PPN_MASK) +
		    (va & IA64_PBVM_PAGE_MASK);
		goto out;
	}

 err_out:
	printf("XXX: %s: va=%#lx is invalid\n", __func__, va);
	pa = 0;
	/* FALLTHROUGH */

 out:
	return (pa);
}

/*
 * Add a list of wired pages to the kva this routine is only used for
 * temporary kernel mappings that do not need to have page modification
 * or references recorded.  Note that old mappings are simply written
 * over.  The page is effectively wired, but it's customary to not have
 * the PTE reflect that, nor update statistics.
 */
void
pmap_qenter(vm_offset_t va, vm_page_t *m, int count)
{
	struct ia64_lpte *pte;
	int i;

	for (i = 0; i < count; i++) {
		pte = pmap_find_kpte(va);
		if (pmap_present(pte))
			pmap_invalidate_page(va);
		else
			pmap_enter_vhpt(pte, va);
		pmap_pte_prot(kernel_pmap, pte, VM_PROT_ALL);
		pmap_pte_attr(pte, m[i]->md.memattr);
		pmap_set_pte(pte, va, VM_PAGE_TO_PHYS(m[i]), FALSE, FALSE);
		va += PAGE_SIZE;
	}
}

/*
 * this routine jerks page mappings from the
 * kernel -- it is meant only for temporary mappings.
 */
void
pmap_qremove(vm_offset_t va, int count)
{
	struct ia64_lpte *pte;
	int i;

	for (i = 0; i < count; i++) {
		pte = pmap_find_kpte(va);
		if (pmap_present(pte)) {
			pmap_remove_vhpt(va);
			pmap_invalidate_page(va);
			pmap_clear_present(pte);
		}
		va += PAGE_SIZE;
	}
}

/*
 * Add a wired page to the kva.  As for pmap_qenter(), it's customary
 * to not have the PTE reflect that, nor update statistics.
 */
void 
pmap_kenter(vm_offset_t va, vm_offset_t pa)
{
	struct ia64_lpte *pte;

	pte = pmap_find_kpte(va);
	if (pmap_present(pte))
		pmap_invalidate_page(va);
	else
		pmap_enter_vhpt(pte, va);
	pmap_pte_prot(kernel_pmap, pte, VM_PROT_ALL);
	pmap_pte_attr(pte, VM_MEMATTR_DEFAULT);
	pmap_set_pte(pte, va, pa, FALSE, FALSE);
}

/*
 * Remove a page from the kva
 */
void
pmap_kremove(vm_offset_t va)
{
	struct ia64_lpte *pte;

	pte = pmap_find_kpte(va);
	if (pmap_present(pte)) {
		pmap_remove_vhpt(va);
		pmap_invalidate_page(va);
		pmap_clear_present(pte);
	}
}

/*
 *	Used to map a range of physical addresses into kernel
 *	virtual address space.
 *
 *	The value passed in '*virt' is a suggested virtual address for
 *	the mapping. Architectures which can support a direct-mapped
 *	physical to virtual region can return the appropriate address
 *	within that region, leaving '*virt' unchanged. Other
 *	architectures should map the pages starting at '*virt' and
 *	update '*virt' with the first usable address after the mapped
 *	region.
 */
vm_offset_t
pmap_map(vm_offset_t *virt, vm_offset_t start, vm_offset_t end, int prot)
{
	return IA64_PHYS_TO_RR7(start);
}

/*
 *	Remove the given range of addresses from the specified map.
 *
 *	It is assumed that the start and end are properly
 *	rounded to the page size.
 */
void
pmap_remove(pmap_t pmap, vm_offset_t sva, vm_offset_t eva)
{
	pmap_t oldpmap;
	vm_offset_t va;
	pv_entry_t npv, pv;
	struct ia64_lpte *pte;

	if (pmap->pm_stats.resident_count == 0)
		return;

	vm_page_lock_queues();
	PMAP_LOCK(pmap);
	oldpmap = pmap_switch(pmap);

	/*
	 * special handling of removing one page.  a very
	 * common operation and easy to short circuit some
	 * code.
	 */
	if (sva + PAGE_SIZE == eva) {
		pte = pmap_find_vhpt(sva);
		if (pte != NULL)
			pmap_remove_pte(pmap, pte, sva, 0, 1);
		goto out;
	}

	if (pmap->pm_stats.resident_count < ((eva - sva) >> PAGE_SHIFT)) {
		TAILQ_FOREACH_SAFE(pv, &pmap->pm_pvlist, pv_plist, npv) {
			va = pv->pv_va;
			if (va >= sva && va < eva) {
				pte = pmap_find_vhpt(va);
				KASSERT(pte != NULL, ("pte"));
				pmap_remove_pte(pmap, pte, va, pv, 1);
			}
		}
	} else {
		for (va = sva; va < eva; va += PAGE_SIZE) {
			pte = pmap_find_vhpt(va);
			if (pte != NULL)
				pmap_remove_pte(pmap, pte, va, 0, 1);
		}
	}

out:
	vm_page_unlock_queues();
	pmap_switch(oldpmap);
	PMAP_UNLOCK(pmap);
}

/*
 *	Routine:	pmap_remove_all
 *	Function:
 *		Removes this physical page from
 *		all physical maps in which it resides.
 *		Reflects back modify bits to the pager.
 *
 *	Notes:
 *		Original versions of this routine were very
 *		inefficient because they iteratively called
 *		pmap_remove (slow...)
 */

void
pmap_remove_all(vm_page_t m)
{
	pmap_t oldpmap;
	pv_entry_t pv;

	KASSERT((m->oflags & VPO_UNMANAGED) == 0,
	    ("pmap_remove_all: page %p is not managed", m));
	vm_page_lock_queues();
	while ((pv = TAILQ_FIRST(&m->md.pv_list)) != NULL) {
		struct ia64_lpte *pte;
		pmap_t pmap = pv->pv_pmap;
		vm_offset_t va = pv->pv_va;

		PMAP_LOCK(pmap);
		oldpmap = pmap_switch(pmap);
		pte = pmap_find_vhpt(va);
		KASSERT(pte != NULL, ("pte"));
		if (pmap_ppn(pte) != VM_PAGE_TO_PHYS(m))
			panic("pmap_remove_all: pv_table for %lx is inconsistent", VM_PAGE_TO_PHYS(m));
		pmap_remove_pte(pmap, pte, va, pv, 1);
		pmap_switch(oldpmap);
		PMAP_UNLOCK(pmap);
	}
	vm_page_aflag_clear(m, PGA_WRITEABLE);
	vm_page_unlock_queues();
}

/*
 *	Set the physical protection on the
 *	specified range of this map as requested.
 */
void
pmap_protect(pmap_t pmap, vm_offset_t sva, vm_offset_t eva, vm_prot_t prot)
{
	pmap_t oldpmap;
	struct ia64_lpte *pte;

	if ((prot & VM_PROT_READ) == VM_PROT_NONE) {
		pmap_remove(pmap, sva, eva);
		return;
	}

	if ((prot & (VM_PROT_WRITE|VM_PROT_EXECUTE)) ==
	    (VM_PROT_WRITE|VM_PROT_EXECUTE))
		return;

	if ((sva & PAGE_MASK) || (eva & PAGE_MASK))
		panic("pmap_protect: unaligned addresses");

	PMAP_LOCK(pmap);
	oldpmap = pmap_switch(pmap);
	for ( ; sva < eva; sva += PAGE_SIZE) {
		/* If page is invalid, skip this page */
		pte = pmap_find_vhpt(sva);
		if (pte == NULL)
			continue;

		/* If there's no change, skip it too */
		if (pmap_prot(pte) == prot)
			continue;

		if ((prot & VM_PROT_WRITE) == 0 &&
		    pmap_managed(pte) && pmap_dirty(pte)) {
			vm_paddr_t pa = pmap_ppn(pte);
			vm_page_t m = PHYS_TO_VM_PAGE(pa);

			vm_page_dirty(m);
			pmap_clear_dirty(pte);
		}

		if (prot & VM_PROT_EXECUTE)
			ia64_sync_icache(sva, PAGE_SIZE);

		pmap_pte_prot(pmap, pte, prot);
		pmap_invalidate_page(sva);
	}
	pmap_switch(oldpmap);
	PMAP_UNLOCK(pmap);
}

/*
 *	Insert the given physical page (p) at
 *	the specified virtual address (v) in the
 *	target physical map with the protection requested.
 *
 *	If specified, the page will be wired down, meaning
 *	that the related pte can not be reclaimed.
 *
 *	NB:  This is the only routine which MAY NOT lazy-evaluate
 *	or lose information.  That is, this routine must actually
 *	insert this page into the given map NOW.
 */
void
pmap_enter(pmap_t pmap, vm_offset_t va, vm_prot_t access, vm_page_t m,
    vm_prot_t prot, boolean_t wired)
{
	pmap_t oldpmap;
	vm_offset_t pa;
	vm_offset_t opa;
	struct ia64_lpte origpte;
	struct ia64_lpte *pte;
	boolean_t icache_inval, managed;

	vm_page_lock_queues();
	PMAP_LOCK(pmap);
	oldpmap = pmap_switch(pmap);

	va &= ~PAGE_MASK;
 	KASSERT(va <= VM_MAX_KERNEL_ADDRESS, ("pmap_enter: toobig"));
	KASSERT((m->oflags & (VPO_UNMANAGED | VPO_BUSY)) != 0,
	    ("pmap_enter: page %p is not busy", m));

	/*
	 * Find (or create) a pte for the given mapping.
	 */
	while ((pte = pmap_find_pte(va)) == NULL) {
		pmap_switch(oldpmap);
		PMAP_UNLOCK(pmap);
		vm_page_unlock_queues();
		VM_WAIT;
		vm_page_lock_queues();
		PMAP_LOCK(pmap);
		oldpmap = pmap_switch(pmap);
	}
	origpte = *pte;
	if (!pmap_present(pte)) {
		opa = ~0UL;
		pmap_enter_vhpt(pte, va);
	} else
		opa = pmap_ppn(pte);
	managed = FALSE;
	pa = VM_PAGE_TO_PHYS(m);

	icache_inval = (prot & VM_PROT_EXECUTE) ? TRUE : FALSE;

	/*
	 * Mapping has not changed, must be protection or wiring change.
	 */
	if (opa == pa) {
		/*
		 * Wiring change, just update stats. We don't worry about
		 * wiring PT pages as they remain resident as long as there
		 * are valid mappings in them. Hence, if a user page is wired,
		 * the PT page will be also.
		 */
		if (wired && !pmap_wired(&origpte))
			pmap->pm_stats.wired_count++;
		else if (!wired && pmap_wired(&origpte))
			pmap->pm_stats.wired_count--;

		managed = (pmap_managed(&origpte)) ? TRUE : FALSE;

		/*
		 * We might be turning off write access to the page,
		 * so we go ahead and sense modify status. Otherwise,
		 * we can avoid I-cache invalidation if the page
		 * already allowed execution.
		 */
		if (managed && pmap_dirty(&origpte))
			vm_page_dirty(m);
		else if (pmap_exec(&origpte))
			icache_inval = FALSE;

		pmap_invalidate_page(va);
		goto validate;
	}

	/*
	 * Mapping has changed, invalidate old range and fall
	 * through to handle validating new mapping.
	 */
	if (opa != ~0UL) {
		pmap_remove_pte(pmap, pte, va, 0, 0);
		pmap_enter_vhpt(pte, va);
	}

	/*
	 * Enter on the PV list if part of our managed memory.
	 */
	if ((m->oflags & VPO_UNMANAGED) == 0) {
		KASSERT(va < kmi.clean_sva || va >= kmi.clean_eva,
		    ("pmap_enter: managed mapping within the clean submap"));
		pmap_insert_entry(pmap, va, m);
		managed = TRUE;
	}

	/*
	 * Increment counters
	 */
	pmap->pm_stats.resident_count++;
	if (wired)
		pmap->pm_stats.wired_count++;

validate:

	/*
	 * Now validate mapping with desired protection/wiring. This
	 * adds the pte to the VHPT if necessary.
	 */
	pmap_pte_prot(pmap, pte, prot);
	pmap_pte_attr(pte, m->md.memattr);
	pmap_set_pte(pte, va, pa, wired, managed);

	/* Invalidate the I-cache when needed. */
	if (icache_inval)
		ia64_sync_icache(va, PAGE_SIZE);

	if ((prot & VM_PROT_WRITE) != 0 && managed)
		vm_page_aflag_set(m, PGA_WRITEABLE);
	vm_page_unlock_queues();
	pmap_switch(oldpmap);
	PMAP_UNLOCK(pmap);
}

/*
 * Maps a sequence of resident pages belonging to the same object.
 * The sequence begins with the given page m_start.  This page is
 * mapped at the given virtual address start.  Each subsequent page is
 * mapped at a virtual address that is offset from start by the same
 * amount as the page is offset from m_start within the object.  The
 * last page in the sequence is the page with the largest offset from
 * m_start that can be mapped at a virtual address less than the given
 * virtual address end.  Not every virtual page between start and end
 * is mapped; only those for which a resident page exists with the
 * corresponding offset from m_start are mapped.
 */
void
pmap_enter_object(pmap_t pmap, vm_offset_t start, vm_offset_t end,
    vm_page_t m_start, vm_prot_t prot)
{
	pmap_t oldpmap;
	vm_page_t m;
	vm_pindex_t diff, psize;

	VM_OBJECT_LOCK_ASSERT(m_start->object, MA_OWNED);
	psize = atop(end - start);
	m = m_start;
	vm_page_lock_queues();
	PMAP_LOCK(pmap);
	oldpmap = pmap_switch(pmap);
	while (m != NULL && (diff = m->pindex - m_start->pindex) < psize) {
		pmap_enter_quick_locked(pmap, start + ptoa(diff), m, prot);
		m = TAILQ_NEXT(m, listq);
	}
	vm_page_unlock_queues();
	pmap_switch(oldpmap);
 	PMAP_UNLOCK(pmap);
}

/*
 * this code makes some *MAJOR* assumptions:
 * 1. Current pmap & pmap exists.
 * 2. Not wired.
 * 3. Read access.
 * 4. No page table pages.
 * but is *MUCH* faster than pmap_enter...
 */

void
pmap_enter_quick(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot)
{
	pmap_t oldpmap;

	vm_page_lock_queues();
	PMAP_LOCK(pmap);
	oldpmap = pmap_switch(pmap);
	pmap_enter_quick_locked(pmap, va, m, prot);
	vm_page_unlock_queues();
	pmap_switch(oldpmap);
	PMAP_UNLOCK(pmap);
}

static void
pmap_enter_quick_locked(pmap_t pmap, vm_offset_t va, vm_page_t m,
    vm_prot_t prot)
{
	struct ia64_lpte *pte;
	boolean_t managed;

	KASSERT(va < kmi.clean_sva || va >= kmi.clean_eva ||
	    (m->oflags & VPO_UNMANAGED) != 0,
	    ("pmap_enter_quick_locked: managed mapping within the clean submap"));
	mtx_assert(&vm_page_queue_mtx, MA_OWNED);
	PMAP_LOCK_ASSERT(pmap, MA_OWNED);

	if ((pte = pmap_find_pte(va)) == NULL)
		return;

	if (!pmap_present(pte)) {
		/* Enter on the PV list if the page is managed. */
		if ((m->oflags & VPO_UNMANAGED) == 0) {
			if (!pmap_try_insert_pv_entry(pmap, va, m)) {
				pmap_free_pte(pte, va);
				return;
			}
			managed = TRUE;
		} else
			managed = FALSE;

		/* Increment counters. */
		pmap->pm_stats.resident_count++;

		/* Initialise with R/O protection and enter into VHPT. */
		pmap_enter_vhpt(pte, va);
		pmap_pte_prot(pmap, pte,
		    prot & (VM_PROT_READ | VM_PROT_EXECUTE));
		pmap_pte_attr(pte, m->md.memattr);
		pmap_set_pte(pte, va, VM_PAGE_TO_PHYS(m), FALSE, managed);

		if (prot & VM_PROT_EXECUTE)
			ia64_sync_icache(va, PAGE_SIZE);
	}
}

/*
 * pmap_object_init_pt preloads the ptes for a given object
 * into the specified pmap.  This eliminates the blast of soft
 * faults on process startup and immediately after an mmap.
 */
void
pmap_object_init_pt(pmap_t pmap, vm_offset_t addr,
		    vm_object_t object, vm_pindex_t pindex,
		    vm_size_t size)
{

	VM_OBJECT_LOCK_ASSERT(object, MA_OWNED);
	KASSERT(object->type == OBJT_DEVICE || object->type == OBJT_SG,
	    ("pmap_object_init_pt: non-device object"));
}

/*
 *	Routine:	pmap_change_wiring
 *	Function:	Change the wiring attribute for a map/virtual-address
 *			pair.
 *	In/out conditions:
 *			The mapping must already exist in the pmap.
 */
void
pmap_change_wiring(pmap, va, wired)
	register pmap_t pmap;
	vm_offset_t va;
	boolean_t wired;
{
	pmap_t oldpmap;
	struct ia64_lpte *pte;

	PMAP_LOCK(pmap);
	oldpmap = pmap_switch(pmap);

	pte = pmap_find_vhpt(va);
	KASSERT(pte != NULL, ("pte"));
	if (wired && !pmap_wired(pte)) {
		pmap->pm_stats.wired_count++;
		pmap_set_wired(pte);
	} else if (!wired && pmap_wired(pte)) {
		pmap->pm_stats.wired_count--;
		pmap_clear_wired(pte);
	}

	pmap_switch(oldpmap);
	PMAP_UNLOCK(pmap);
}



/*
 *	Copy the range specified by src_addr/len
 *	from the source map to the range dst_addr/len
 *	in the destination map.
 *
 *	This routine is only advisory and need not do anything.
 */

void
pmap_copy(pmap_t dst_pmap, pmap_t src_pmap, vm_offset_t dst_addr, vm_size_t len,
	  vm_offset_t src_addr)
{
}	


/*
 *	pmap_zero_page zeros the specified hardware page by
 *	mapping it into virtual memory and using bzero to clear
 *	its contents.
 */

void
pmap_zero_page(vm_page_t m)
{
	void *p;

	p = (void *)pmap_page_to_va(m);
	bzero(p, PAGE_SIZE);
}


/*
 *	pmap_zero_page_area zeros the specified hardware page by
 *	mapping it into virtual memory and using bzero to clear
 *	its contents.
 *
 *	off and size must reside within a single page.
 */

void
pmap_zero_page_area(vm_page_t m, int off, int size)
{
	char *p;

	p = (void *)pmap_page_to_va(m);
	bzero(p + off, size);
}


/*
 *	pmap_zero_page_idle zeros the specified hardware page by
 *	mapping it into virtual memory and using bzero to clear
 *	its contents.  This is for the vm_idlezero process.
 */

void
pmap_zero_page_idle(vm_page_t m)
{
	void *p;

	p = (void *)pmap_page_to_va(m);
	bzero(p, PAGE_SIZE);
}


/*
 *	pmap_copy_page copies the specified (machine independent)
 *	page by mapping the page into virtual memory and using
 *	bcopy to copy the page, one machine dependent page at a
 *	time.
 */
void
pmap_copy_page(vm_page_t msrc, vm_page_t mdst)
{
	void *dst, *src;

	src = (void *)pmap_page_to_va(msrc);
	dst = (void *)pmap_page_to_va(mdst);
	bcopy(src, dst, PAGE_SIZE);
}

/*
 * Returns true if the pmap's pv is one of the first
 * 16 pvs linked to from this page.  This count may
 * be changed upwards or downwards in the future; it
 * is only necessary that true be returned for a small
 * subset of pmaps for proper page aging.
 */
boolean_t
pmap_page_exists_quick(pmap_t pmap, vm_page_t m)
{
	pv_entry_t pv;
	int loops = 0;
	boolean_t rv;

	KASSERT((m->oflags & VPO_UNMANAGED) == 0,
	    ("pmap_page_exists_quick: page %p is not managed", m));
	rv = FALSE;
	vm_page_lock_queues();
	TAILQ_FOREACH(pv, &m->md.pv_list, pv_list) {
		if (pv->pv_pmap == pmap) {
			rv = TRUE;
			break;
		}
		loops++;
		if (loops >= 16)
			break;
	}
	vm_page_unlock_queues();
	return (rv);
}

/*
 *	pmap_page_wired_mappings:
 *
 *	Return the number of managed mappings to the given physical page
 *	that are wired.
 */
int
pmap_page_wired_mappings(vm_page_t m)
{
	struct ia64_lpte *pte;
	pmap_t oldpmap, pmap;
	pv_entry_t pv;
	int count;

	count = 0;
	if ((m->oflags & VPO_UNMANAGED) != 0)
		return (count);
	vm_page_lock_queues();
	TAILQ_FOREACH(pv, &m->md.pv_list, pv_list) {
		pmap = pv->pv_pmap;
		PMAP_LOCK(pmap);
		oldpmap = pmap_switch(pmap);
		pte = pmap_find_vhpt(pv->pv_va);
		KASSERT(pte != NULL, ("pte"));
		if (pmap_wired(pte))
			count++;
		pmap_switch(oldpmap);
		PMAP_UNLOCK(pmap);
	}
	vm_page_unlock_queues();
	return (count);
}

/*
 * Remove all pages from specified address space
 * this aids process exit speeds.  Also, this code
 * is special cased for current process only, but
 * can have the more generic (and slightly slower)
 * mode enabled.  This is much faster than pmap_remove
 * in the case of running down an entire address space.
 */
void
pmap_remove_pages(pmap_t pmap)
{
	pmap_t oldpmap;
	pv_entry_t pv, npv;

	if (pmap != vmspace_pmap(curthread->td_proc->p_vmspace)) {
		printf("warning: %s called with non-current pmap\n",
		    __func__);
		return;
	}

	vm_page_lock_queues();
	PMAP_LOCK(pmap);
	oldpmap = pmap_switch(pmap);

	for (pv = TAILQ_FIRST(&pmap->pm_pvlist); pv; pv = npv) {
		struct ia64_lpte *pte;

		npv = TAILQ_NEXT(pv, pv_plist);

		pte = pmap_find_vhpt(pv->pv_va);
		KASSERT(pte != NULL, ("pte"));
		if (!pmap_wired(pte))
			pmap_remove_pte(pmap, pte, pv->pv_va, pv, 1);
	}

	pmap_switch(oldpmap);
	PMAP_UNLOCK(pmap);
	vm_page_unlock_queues();
}

/*
 *	pmap_ts_referenced:
 *
 *	Return a count of reference bits for a page, clearing those bits.
 *	It is not necessary for every reference bit to be cleared, but it
 *	is necessary that 0 only be returned when there are truly no
 *	reference bits set.
 * 
 *	XXX: The exact number of bits to check and clear is a matter that
 *	should be tested and standardized at some point in the future for
 *	optimal aging of shared pages.
 */
int
pmap_ts_referenced(vm_page_t m)
{
	struct ia64_lpte *pte;
	pmap_t oldpmap;
	pv_entry_t pv;
	int count = 0;

	KASSERT((m->oflags & VPO_UNMANAGED) == 0,
	    ("pmap_ts_referenced: page %p is not managed", m));
	vm_page_lock_queues();
	TAILQ_FOREACH(pv, &m->md.pv_list, pv_list) {
		PMAP_LOCK(pv->pv_pmap);
		oldpmap = pmap_switch(pv->pv_pmap);
		pte = pmap_find_vhpt(pv->pv_va);
		KASSERT(pte != NULL, ("pte"));
		if (pmap_accessed(pte)) {
			count++;
			pmap_clear_accessed(pte);
			pmap_invalidate_page(pv->pv_va);
		}
		pmap_switch(oldpmap);
		PMAP_UNLOCK(pv->pv_pmap);
	}
	vm_page_unlock_queues();
	return (count);
}

/*
 *	pmap_is_modified:
 *
 *	Return whether or not the specified physical page was modified
 *	in any physical maps.
 */
boolean_t
pmap_is_modified(vm_page_t m)
{
	struct ia64_lpte *pte;
	pmap_t oldpmap;
	pv_entry_t pv;
	boolean_t rv;

	KASSERT((m->oflags & VPO_UNMANAGED) == 0,
	    ("pmap_is_modified: page %p is not managed", m));
	rv = FALSE;

	/*
	 * If the page is not VPO_BUSY, then PGA_WRITEABLE cannot be
	 * concurrently set while the object is locked.  Thus, if PGA_WRITEABLE
	 * is clear, no PTEs can be dirty.
	 */
	VM_OBJECT_LOCK_ASSERT(m->object, MA_OWNED);
	if ((m->oflags & VPO_BUSY) == 0 &&
	    (m->aflags & PGA_WRITEABLE) == 0)
		return (rv);
	vm_page_lock_queues();
	TAILQ_FOREACH(pv, &m->md.pv_list, pv_list) {
		PMAP_LOCK(pv->pv_pmap);
		oldpmap = pmap_switch(pv->pv_pmap);
		pte = pmap_find_vhpt(pv->pv_va);
		pmap_switch(oldpmap);
		KASSERT(pte != NULL, ("pte"));
		rv = pmap_dirty(pte) ? TRUE : FALSE;
		PMAP_UNLOCK(pv->pv_pmap);
		if (rv)
			break;
	}
	vm_page_unlock_queues();
	return (rv);
}

/*
 *	pmap_is_prefaultable:
 *
 *	Return whether or not the specified virtual address is elgible
 *	for prefault.
 */
boolean_t
pmap_is_prefaultable(pmap_t pmap, vm_offset_t addr)
{
	struct ia64_lpte *pte;

	pte = pmap_find_vhpt(addr);
	if (pte != NULL && pmap_present(pte))
		return (FALSE);
	return (TRUE);
}

/*
 *	pmap_is_referenced:
 *
 *	Return whether or not the specified physical page was referenced
 *	in any physical maps.
 */
boolean_t
pmap_is_referenced(vm_page_t m)
{
	struct ia64_lpte *pte;
	pmap_t oldpmap;
	pv_entry_t pv;
	boolean_t rv;

	KASSERT((m->oflags & VPO_UNMANAGED) == 0,
	    ("pmap_is_referenced: page %p is not managed", m));
	rv = FALSE;
	vm_page_lock_queues();
	TAILQ_FOREACH(pv, &m->md.pv_list, pv_list) {
		PMAP_LOCK(pv->pv_pmap);
		oldpmap = pmap_switch(pv->pv_pmap);
		pte = pmap_find_vhpt(pv->pv_va);
		pmap_switch(oldpmap);
		KASSERT(pte != NULL, ("pte"));
		rv = pmap_accessed(pte) ? TRUE : FALSE;
		PMAP_UNLOCK(pv->pv_pmap);
		if (rv)
			break;
	}
	vm_page_unlock_queues();
	return (rv);
}

/*
 *	Clear the modify bits on the specified physical page.
 */
void
pmap_clear_modify(vm_page_t m)
{
	struct ia64_lpte *pte;
	pmap_t oldpmap;
	pv_entry_t pv;

	KASSERT((m->oflags & VPO_UNMANAGED) == 0,
	    ("pmap_clear_modify: page %p is not managed", m));
	VM_OBJECT_LOCK_ASSERT(m->object, MA_OWNED);
	KASSERT((m->oflags & VPO_BUSY) == 0,
	    ("pmap_clear_modify: page %p is busy", m));

	/*
	 * If the page is not PGA_WRITEABLE, then no PTEs can be modified.
	 * If the object containing the page is locked and the page is not
	 * VPO_BUSY, then PGA_WRITEABLE cannot be concurrently set.
	 */
	if ((m->aflags & PGA_WRITEABLE) == 0)
		return;
	vm_page_lock_queues();
	TAILQ_FOREACH(pv, &m->md.pv_list, pv_list) {
		PMAP_LOCK(pv->pv_pmap);
		oldpmap = pmap_switch(pv->pv_pmap);
		pte = pmap_find_vhpt(pv->pv_va);
		KASSERT(pte != NULL, ("pte"));
		if (pmap_dirty(pte)) {
			pmap_clear_dirty(pte);
			pmap_invalidate_page(pv->pv_va);
		}
		pmap_switch(oldpmap);
		PMAP_UNLOCK(pv->pv_pmap);
	}
	vm_page_unlock_queues();
}

/*
 *	pmap_clear_reference:
 *
 *	Clear the reference bit on the specified physical page.
 */
void
pmap_clear_reference(vm_page_t m)
{
	struct ia64_lpte *pte;
	pmap_t oldpmap;
	pv_entry_t pv;

	KASSERT((m->oflags & VPO_UNMANAGED) == 0,
	    ("pmap_clear_reference: page %p is not managed", m));
	vm_page_lock_queues();
	TAILQ_FOREACH(pv, &m->md.pv_list, pv_list) {
		PMAP_LOCK(pv->pv_pmap);
		oldpmap = pmap_switch(pv->pv_pmap);
		pte = pmap_find_vhpt(pv->pv_va);
		KASSERT(pte != NULL, ("pte"));
		if (pmap_accessed(pte)) {
			pmap_clear_accessed(pte);
			pmap_invalidate_page(pv->pv_va);
		}
		pmap_switch(oldpmap);
		PMAP_UNLOCK(pv->pv_pmap);
	}
	vm_page_unlock_queues();
}

/*
 * Clear the write and modified bits in each of the given page's mappings.
 */
void
pmap_remove_write(vm_page_t m)
{
	struct ia64_lpte *pte;
	pmap_t oldpmap, pmap;
	pv_entry_t pv;
	vm_prot_t prot;

	KASSERT((m->oflags & VPO_UNMANAGED) == 0,
	    ("pmap_remove_write: page %p is not managed", m));

	/*
	 * If the page is not VPO_BUSY, then PGA_WRITEABLE cannot be set by
	 * another thread while the object is locked.  Thus, if PGA_WRITEABLE
	 * is clear, no page table entries need updating.
	 */
	VM_OBJECT_LOCK_ASSERT(m->object, MA_OWNED);
	if ((m->oflags & VPO_BUSY) == 0 &&
	    (m->aflags & PGA_WRITEABLE) == 0)
		return;
	vm_page_lock_queues();
	TAILQ_FOREACH(pv, &m->md.pv_list, pv_list) {
		pmap = pv->pv_pmap;
		PMAP_LOCK(pmap);
		oldpmap = pmap_switch(pmap);
		pte = pmap_find_vhpt(pv->pv_va);
		KASSERT(pte != NULL, ("pte"));
		prot = pmap_prot(pte);
		if ((prot & VM_PROT_WRITE) != 0) {
			if (pmap_dirty(pte)) {
				vm_page_dirty(m);
				pmap_clear_dirty(pte);
			}
			prot &= ~VM_PROT_WRITE;
			pmap_pte_prot(pmap, pte, prot);
			pmap_pte_attr(pte, m->md.memattr);
			pmap_invalidate_page(pv->pv_va);
		}
		pmap_switch(oldpmap);
		PMAP_UNLOCK(pmap);
	}
	vm_page_aflag_clear(m, PGA_WRITEABLE);
	vm_page_unlock_queues();
}

/*
 * Map a set of physical memory pages into the kernel virtual
 * address space. Return a pointer to where it is mapped. This
 * routine is intended to be used for mapping device memory,
 * NOT real memory.
 */
void *
pmap_mapdev(vm_paddr_t pa, vm_size_t sz)
{
	static void *last_va = NULL;
	static vm_paddr_t last_pa = 0;
	static vm_size_t last_sz = 0;
	struct efi_md *md;
	vm_offset_t va;

	if (pa == last_pa && sz == last_sz)
		return (last_va);

	md = efi_md_find(pa);
	if (md == NULL) {
		printf("%s: [%#lx..%#lx] not covered by memory descriptor\n",
		    __func__, pa, pa + sz - 1);
		return (NULL);
	}

	if (md->md_type == EFI_MD_TYPE_FREE) {
		printf("%s: [%#lx..%#lx] is in DRAM\n", __func__, pa,
		    pa + sz - 1);
                return (NULL);
	}

	va = (md->md_attr & EFI_MD_ATTR_WB) ? IA64_PHYS_TO_RR7(pa) :
	    IA64_PHYS_TO_RR6(pa);

	last_va = (void *)va;
	last_pa = pa;
	last_sz = sz;
	return (last_va);
}

/*
 * 'Unmap' a range mapped by pmap_mapdev().
 */
void
pmap_unmapdev(vm_offset_t va, vm_size_t size)
{
}

/*
 * Sets the memory attribute for the specified page.
 */
static void
pmap_page_set_memattr_1(void *arg)
{
	struct ia64_pal_result res;
	register_t is;
	uintptr_t pp = (uintptr_t)arg;

	is = intr_disable();
	res = ia64_call_pal_static(pp, 0, 0, 0);
	intr_restore(is);
}

void
pmap_page_set_memattr(vm_page_t m, vm_memattr_t ma)
{
	struct ia64_lpte *pte;
	pmap_t oldpmap;
	pv_entry_t pv;
	void *va;

	vm_page_lock_queues();
	m->md.memattr = ma;
	TAILQ_FOREACH(pv, &m->md.pv_list, pv_list) {
		PMAP_LOCK(pv->pv_pmap);
		oldpmap = pmap_switch(pv->pv_pmap);
		pte = pmap_find_vhpt(pv->pv_va);
		KASSERT(pte != NULL, ("pte"));
		pmap_pte_attr(pte, ma);
		pmap_invalidate_page(pv->pv_va);
		pmap_switch(oldpmap);
		PMAP_UNLOCK(pv->pv_pmap);
	}
	vm_page_unlock_queues();

	if (ma == VM_MEMATTR_UNCACHEABLE) {
#ifdef SMP
		smp_rendezvous(NULL, pmap_page_set_memattr_1, NULL,
		    (void *)PAL_PREFETCH_VISIBILITY);
#else
		pmap_page_set_memattr_1((void *)PAL_PREFETCH_VISIBILITY);
#endif
		va = (void *)pmap_page_to_va(m);
		critical_enter();
		cpu_flush_dcache(va, PAGE_SIZE);
		critical_exit();
#ifdef SMP
		smp_rendezvous(NULL, pmap_page_set_memattr_1, NULL,
		    (void *)PAL_MC_DRAIN);
#else
		pmap_page_set_memattr_1((void *)PAL_MC_DRAIN);
#endif
	}
}

/*
 * perform the pmap work for mincore
 */
int
pmap_mincore(pmap_t pmap, vm_offset_t addr, vm_paddr_t *locked_pa)
{
	pmap_t oldpmap;
	struct ia64_lpte *pte, tpte;
	vm_paddr_t pa;
	int val;

	PMAP_LOCK(pmap);
retry:
	oldpmap = pmap_switch(pmap);
	pte = pmap_find_vhpt(addr);
	if (pte != NULL) {
		tpte = *pte;
		pte = &tpte;
	}
	pmap_switch(oldpmap);
	if (pte == NULL || !pmap_present(pte)) {
		val = 0;
		goto out;
	}
	val = MINCORE_INCORE;
	if (pmap_dirty(pte))
		val |= MINCORE_MODIFIED | MINCORE_MODIFIED_OTHER;
	if (pmap_accessed(pte))
		val |= MINCORE_REFERENCED | MINCORE_REFERENCED_OTHER;
	if ((val & (MINCORE_MODIFIED_OTHER | MINCORE_REFERENCED_OTHER)) !=
	    (MINCORE_MODIFIED_OTHER | MINCORE_REFERENCED_OTHER) &&
	    pmap_managed(pte)) {
		pa = pmap_ppn(pte);
		/* Ensure that "PHYS_TO_VM_PAGE(pa)->object" doesn't change. */
		if (vm_page_pa_tryrelock(pmap, pa, locked_pa))
			goto retry;
	} else
out:
		PA_UNLOCK_COND(*locked_pa);
	PMAP_UNLOCK(pmap);
	return (val);
}

void
pmap_activate(struct thread *td)
{
	pmap_switch(vmspace_pmap(td->td_proc->p_vmspace));
}

pmap_t
pmap_switch(pmap_t pm)
{
	pmap_t prevpm;
	int i;

	critical_enter();
	prevpm = PCPU_GET(md.current_pmap);
	if (prevpm == pm)
		goto out;
	if (pm == NULL) {
		for (i = 0; i < IA64_VM_MINKERN_REGION; i++) {
			ia64_set_rr(IA64_RR_BASE(i),
			    (i << 8)|(PAGE_SHIFT << 2)|1);
		}
	} else {
		for (i = 0; i < IA64_VM_MINKERN_REGION; i++) {
			ia64_set_rr(IA64_RR_BASE(i),
			    (pm->pm_rid[i] << 8)|(PAGE_SHIFT << 2)|1);
		}
	}
	PCPU_SET(md.current_pmap, pm);
	ia64_srlz_d();

out:
	critical_exit();
	return (prevpm);
}

void
pmap_sync_icache(pmap_t pm, vm_offset_t va, vm_size_t sz)
{
	pmap_t oldpm;
	struct ia64_lpte *pte;
	vm_offset_t lim;
	vm_size_t len;

	sz += va & 31;
	va &= ~31;
	sz = (sz + 31) & ~31;

	PMAP_LOCK(pm);
	oldpm = pmap_switch(pm);
	while (sz > 0) {
		lim = round_page(va);
		len = MIN(lim - va, sz);
		pte = pmap_find_vhpt(va);
		if (pte != NULL && pmap_present(pte))
			ia64_sync_icache(va, len);
		va += len;
		sz -= len;
	}
	pmap_switch(oldpm);
	PMAP_UNLOCK(pm);
}

/*
 *	Increase the starting virtual address of the given mapping if a
 *	different alignment might result in more superpage mappings.
 */
void
pmap_align_superpage(vm_object_t object, vm_ooffset_t offset,
    vm_offset_t *addr, vm_size_t size)
{
}

#include "opt_ddb.h"

#ifdef DDB

#include <ddb/ddb.h>

static const char*	psnames[] = {
	"1B",	"2B",	"4B",	"8B",
	"16B",	"32B",	"64B",	"128B",
	"256B",	"512B",	"1K",	"2K",
	"4K",	"8K",	"16K",	"32K",
	"64K",	"128K",	"256K",	"512K",
	"1M",	"2M",	"4M",	"8M",
	"16M",	"32M",	"64M",	"128M",
	"256M",	"512M",	"1G",	"2G"
};

static void
print_trs(int type)
{
	struct ia64_pal_result res;
	int i, maxtr;
	struct {
		pt_entry_t	pte;
		uint64_t	itir;
		uint64_t	ifa;
		struct ia64_rr	rr;
	} buf;
	static const char *manames[] = {
		"WB",	"bad",	"bad",	"bad",
		"UC",	"UCE",	"WC",	"NaT",
	};

	res = ia64_call_pal_static(PAL_VM_SUMMARY, 0, 0, 0);
	if (res.pal_status != 0) {
		db_printf("Can't get VM summary\n");
		return;
	}

	if (type == 0)
		maxtr = (res.pal_result[0] >> 40) & 0xff;
	else
		maxtr = (res.pal_result[0] >> 32) & 0xff;

	db_printf("V RID    Virtual Page  Physical Page PgSz ED AR PL D A MA  P KEY\n");
	for (i = 0; i <= maxtr; i++) {
		bzero(&buf, sizeof(buf));
		res = ia64_pal_physical(PAL_VM_TR_READ, i, type,
		    ia64_tpa((uint64_t)&buf));
		if (!(res.pal_result[0] & 1))
			buf.pte &= ~PTE_AR_MASK;
		if (!(res.pal_result[0] & 2))
			buf.pte &= ~PTE_PL_MASK;
		if (!(res.pal_result[0] & 4))
			pmap_clear_dirty(&buf);
		if (!(res.pal_result[0] & 8))
			buf.pte &= ~PTE_MA_MASK;
		db_printf("%d %06x %013lx %013lx %4s %d  %d  %d  %d %d %-3s "
		    "%d %06x\n", (int)buf.ifa & 1, buf.rr.rr_rid,
		    buf.ifa >> 12, (buf.pte & PTE_PPN_MASK) >> 12,
		    psnames[(buf.itir & ITIR_PS_MASK) >> 2],
		    (buf.pte & PTE_ED) ? 1 : 0,
		    (int)(buf.pte & PTE_AR_MASK) >> 9,
		    (int)(buf.pte & PTE_PL_MASK) >> 7,
		    (pmap_dirty(&buf)) ? 1 : 0,
		    (pmap_accessed(&buf)) ? 1 : 0,
		    manames[(buf.pte & PTE_MA_MASK) >> 2],
		    (pmap_present(&buf)) ? 1 : 0,
		    (int)((buf.itir & ITIR_KEY_MASK) >> 8));
	}
}

DB_COMMAND(itr, db_itr)
{
	print_trs(0);
}

DB_COMMAND(dtr, db_dtr)
{
	print_trs(1);
}

DB_COMMAND(rr, db_rr)
{
	int i;
	uint64_t t;
	struct ia64_rr rr;

	printf("RR RID    PgSz VE\n");
	for (i = 0; i < 8; i++) {
		__asm __volatile ("mov %0=rr[%1]"
				  : "=r"(t)
				  : "r"(IA64_RR_BASE(i)));
		*(uint64_t *) &rr = t;
		printf("%d  %06x %4s %d\n",
		       i, rr.rr_rid, psnames[rr.rr_ps], rr.rr_ve);
	}
}

DB_COMMAND(thash, db_thash)
{
	if (!have_addr)
		return;

	db_printf("%p\n", (void *) ia64_thash(addr));
}

DB_COMMAND(ttag, db_ttag)
{
	if (!have_addr)
		return;

	db_printf("0x%lx\n", ia64_ttag(addr));
}

DB_COMMAND(kpte, db_kpte)
{
	struct ia64_lpte *pte;

	if (!have_addr) {
		db_printf("usage: kpte <kva>\n");
		return;
	}
	if (addr < VM_MIN_KERNEL_ADDRESS) {
		db_printf("kpte: error: invalid <kva>\n");
		return;
	}
	pte = pmap_find_kpte(addr);
	db_printf("kpte at %p:\n", pte);
	db_printf("  pte  =%016lx\n", pte->pte);
	db_printf("  itir =%016lx\n", pte->itir);
	db_printf("  tag  =%016lx\n", pte->tag);
	db_printf("  chain=%016lx\n", pte->chain);
}

#endif

Man Man