root/sys/arch/ia64/ia64/pmap.c
/* $NetBSD: pmap.c,v 1.44 2024/10/31 10:09:56 andvar Exp $ */

/*-
 * Copyright (c) 1998, 1999, 2000, 2001 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
 * NASA Ames Research Center and by Chris G. Demetriou.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*-
 * Copyright (c) 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
 */

/* __FBSDID("$FreeBSD: src/sys/ia64/ia64/pmap.c,v 1.172 2005/11/20 06:09:48 alc Exp $"); */

#include <sys/cdefs.h>

__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.44 2024/10/31 10:09:56 andvar Exp $");

#include <sys/param.h>
#include <sys/atomic.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/reboot.h>
#include <sys/lock.h>
#include <sys/pool.h>
#include <sys/sched.h>
#include <sys/bitops.h>

#include <uvm/uvm.h>
#include <uvm/uvm_physseg.h>

#include <machine/pal.h>
#include <machine/atomic.h>
#include <machine/pte.h>
#include <machine/cpufunc.h>
#include <machine/md_var.h>
#include <machine/vmparam.h>

/*
 *      Manages physical address maps.
 *
 *      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
 */

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

#ifdef PV_STATS
#define PV_STAT(x)      do { x ; } while (0)
#else
#define PV_STAT(x)      do { } while (0)
#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

/*
 * Individual PV entries are stored in per-pmap chunks.  This saves
 * space by eliminating the need to record the pmap within every PV
 * entry.
 */
#if PAGE_SIZE == 8192
#define _NPCM   6
#define _NPCPV  337
#define _NPCS   2
#elif PAGE_SIZE == 16384
#define _NPCM   11
#define _NPCPV  677
#define _NPCS   1
#else
#error "invalid page size"
#endif

struct pv_chunk {
        pmap_t                  pc_pmap;
        TAILQ_ENTRY(pv_chunk)   pc_list;
        u_long                  pc_map[_NPCM];  /* bitmap; 1 = free */
        TAILQ_ENTRY(pv_chunk)   pc_lru;
        u_long                  pc_spare[_NPCS];
        struct pv_entry         pc_pventry[_NPCPV];
};

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

/*
 * Statically allocated kernel pmap
 */
static struct pmap kernel_pmap_store;/* the kernel's pmap (proc0) */
struct pmap *const kernel_pmap_ptr = &kernel_pmap_store;

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

/* XXX freebsd, needs to be sorted out */
#define kernel_pmap                     pmap_kernel()
#define critical_enter()                kpreempt_disable()
#define critical_exit()                 kpreempt_enable()
/* flags the entire page as dirty */
#define vm_page_dirty(page)             (page->flags &= ~PG_CLEAN)
#define vm_page_is_managed(page) (pmap_initialized && uvm_pageismanaged(VM_PAGE_TO_PHYS(page)))

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

vaddr_t kernel_vm_end;

/* Defaults for ptc.e. */
/*
static uint64_t pmap_ptc_e_base = 0;
static uint32_t pmap_ptc_e_count1 = 1;
static uint32_t pmap_ptc_e_count2 = 1;
static uint32_t pmap_ptc_e_stride1 = 0;
static uint32_t pmap_ptc_e_stride2 = 0;
*/
/* Values for ptc.e. XXX values for SKI, add SKI kernel option methinks */
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;

kmutex_t 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;
kmutex_t pmap_ridmutex;

static krwlock_t pvh_global_lock __attribute__ ((aligned (128)));

static pool_cache_t pmap_pool_cache;

/*
 * Data for the pv entry allocation mechanism
 */
static TAILQ_HEAD(pch, pv_chunk) pv_chunks = TAILQ_HEAD_INITIALIZER(pv_chunks);
static int pv_entry_count;

/*
 * Data for allocating PTEs for user processes.
 */
static pool_cache_t pte_pool_cache;

struct ia64_bucket *pmap_vhpt_bucket;

/* XXX For freebsd, these are sysctl variables */
static uint64_t pmap_vhpt_nbuckets = 0;
uint64_t pmap_vhpt_log2size = 0;
static uint64_t pmap_vhpt_inserts = 0;

static bool pmap_initialized = false;   /* Has pmap_init completed? */
static uint64_t pmap_pages_stolen = 0;  /* instrumentation */

static struct ia64_lpte *pmap_find_vhpt(vaddr_t va);

static void free_pv_chunk(struct pv_chunk *pc);
static void free_pv_entry(pmap_t pmap, pv_entry_t pv);
static pv_entry_t get_pv_entry(pmap_t pmap, bool try);
static struct vm_page *pmap_pv_reclaim(pmap_t locked_pmap);

static void     pmap_free_pte(struct ia64_lpte *pte, vaddr_t va);
static int      pmap_remove_pte(pmap_t pmap, struct ia64_lpte *pte,
                    vaddr_t va, pv_entry_t pv, int freepte);
static int      pmap_remove_vhpt(vaddr_t va);

static vaddr_t  pmap_steal_vhpt_memory(vsize_t);

static struct vm_page *vm_page_alloc1(void);
static void vm_page_free1(struct vm_page *pg);

static vm_memattr_t pmap_flags_to_memattr(u_int flags);

#if DEBUG
static void pmap_testout(void);
#endif

static void
pmap_initialize_vhpt(vaddr_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 MULTIPROCESSOR
vaddr_t
pmap_alloc_vhpt(void)
{
        vaddr_t vhpt;
        struct vm_page *m;
        vsize_t size;

        size = 1UL << pmap_vhpt_log2size;
        m = vm_page_alloc_contig(NULL, 0, VM_ALLOC_SYSTEM | VM_ALLOC_NOOBJ |
            VM_ALLOC_WIRED, atop(size), 0UL, ~0UL, size, 0UL,
            VM_MEMATTR_DEFAULT);
        if (m != NULL) {
                vhpt = IA64_PHYS_TO_RR7(VM_PAGE_TO_PHYS(m));
                pmap_initialize_vhpt(vhpt);
                return (vhpt);
        }
        return (0);
}
#endif

/*
 *      Bootstrap the system enough to run with virtual memory.
 */
void
pmap_bootstrap(void)
{
        struct ia64_pal_result res;
        vaddr_t base;
        size_t size;
        vsize_t bufsz;
        int i, 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);

        mutex_init(&pmap_ptc_mutex, MUTEX_DEFAULT, IPL_VM);

        /*
         * 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 *)uvm_pageboot_alloc(pmap_ridmax / 8);
        pmap_ridmap[0] |= 0xff;
        pmap_rididx = 0;
        pmap_ridcount = 8;
        mutex_init(&pmap_ridmutex, MUTEX_DEFAULT, IPL_VM);

        /*
         * Compute the number of pages kmem_map will have.
         */
        kmeminit_nkmempages();

        /*
         * Figure out how many initial PTE's are necessary to map the
         * kernel.  We also reserve space for kmem_alloc_pageable()
         * for vm_fork().
         */

        /* Get size of buffer cache and set an upper limit */
        bufsz = buf_memcalc();
        buf_setvalimit(bufsz);

        nkpt = (((ubc_nwins << ubc_winshift) +
                 bufsz + 16 * NCARGS + pager_map_size) / PAGE_SIZE +
                USRIOSIZE + (maxproc * UPAGES) + nkmempages) / NKPTEPG;

        /*
         * Allocate some memory for initial kernel 'page tables'.
         */
        ia64_kptdir = (void *)uvm_pageboot_alloc(PAGE_SIZE);
        memset((void *)ia64_kptdir, 0, PAGE_SIZE);
        nkpt = 0;
        kernel_vm_end = VM_INIT_KERNEL_ADDRESS;

        /*
         * Determine a valid (mappable) VHPT size.
         */
        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--;

        size = 1UL << pmap_vhpt_log2size;
        /* XXX add some retries here */
        base = pmap_steal_vhpt_memory(size);

        curcpu()->ci_vhpt = base;

        if (base == 0)
                panic("Unable to allocate VHPT");

        pmap_vhpt_nbuckets = size / sizeof(struct ia64_lpte);
        pmap_vhpt_bucket = (void *)uvm_pageboot_alloc(pmap_vhpt_nbuckets *
                                                      sizeof(struct ia64_bucket));
        if (bootverbose)
                printf("VHPT: address=%#lx, size=%#lx, buckets=%ld, address=%lx\n",
                       base, size, pmap_vhpt_nbuckets, (long unsigned int)&pmap_vhpt_bucket[0]);

        for (i = 0; i < pmap_vhpt_nbuckets; i++) {
                /* Stolen memory is zeroed. */
                mutex_init(&pmap_vhpt_bucket[i].mutex, MUTEX_DEFAULT, IPL_VM);
        }

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

        /* XXX
         virtual_avail = VM_INIT_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_pvchunk);

        bzero(&kernel_pmap->pm_stats, sizeof kernel_pmap->pm_stats);
        kernel_pmap->pm_refcount = 1;

        curcpu()->ci_pmap = kernel_pmap;

        /*
         * Initialize the global pv list lock.
         */
        rw_init(&pvh_global_lock);

        /* 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();
}

vaddr_t
pmap_page_to_va(struct vm_page *m)
{
        paddr_t pa;
        vaddr_t va;

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

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

static void
pmap_invalidate_page(vaddr_t va)
{
        struct ia64_lpte *pte;
        //struct pcpu *pc;
        uint64_t tag;
        u_int vhpt_ofs;
        struct cpu_info *ci;
        CPU_INFO_ITERATOR cii;

        critical_enter();

        vhpt_ofs = ia64_thash(va) - curcpu()->ci_vhpt;

        tag = ia64_ttag(va);

        for (CPU_INFO_FOREACH(cii,ci)) {
                pte = (struct ia64_lpte *)(ci->ci_vhpt + vhpt_ofs);
                atomic_cmpset_64(&pte->tag, tag, 1UL << 63);
        }

        mutex_spin_enter(&pmap_ptc_mutex);

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

        mutex_spin_exit(&pmap_ptc_mutex);

        ia64_invala();

        critical_exit();
}

void
pmap_invalidate_all(void)
{
        uint64_t addr;
        int i, j;

        UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);

        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;
        }
        ia64_srlz_i();
}

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

        mutex_enter(&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++;

        mutex_exit(&pmap_ridmutex);

        return rid;
}

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

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

        mutex_enter(&pmap_ridmutex);
        pmap_ridmap[idx] &= bit;
        pmap_ridcount--;

        mutex_exit(&pmap_ridmutex);
}

/***************************************************
 *  Page table page management routines.....
 ***************************************************/
CTASSERT(sizeof(struct pv_chunk) == PAGE_SIZE);

static __inline struct pv_chunk *
pv_to_chunk(pv_entry_t pv)
{
        return ((struct pv_chunk *)((uintptr_t)pv & ~(uintptr_t)PAGE_MASK));
}

#define PV_PMAP(pv) (pv_to_chunk(pv)->pc_pmap)

#define PC_FREE_FULL    0xfffffffffffffffful
#define PC_FREE_PARTIAL \
        ((1UL << (_NPCPV - sizeof(u_long) * 8 * (_NPCM - 1))) - 1)

#if PAGE_SIZE == 8192
static const u_long pc_freemask[_NPCM] = {
        PC_FREE_FULL, PC_FREE_FULL, PC_FREE_FULL,
        PC_FREE_FULL, PC_FREE_FULL, PC_FREE_PARTIAL
};
#elif PAGE_SIZE == 16384
static const u_long pc_freemask[_NPCM] = {
        PC_FREE_FULL, PC_FREE_FULL, PC_FREE_FULL,
        PC_FREE_FULL, PC_FREE_FULL, PC_FREE_FULL,
        PC_FREE_FULL, PC_FREE_FULL, PC_FREE_FULL,
        PC_FREE_FULL, PC_FREE_PARTIAL
};
#endif

#ifdef PV_STATS
static int pc_chunk_count, pc_chunk_allocs, pc_chunk_frees, pc_chunk_tryfail;
static long pv_entry_frees, pv_entry_allocs;
static int pv_entry_spare;
#endif

/*
 * We are in a serious low memory condition.  Resort to
 * drastic measures to free some pages so we can allocate
 * another pv entry chunk.
 */
static struct vm_page
*pmap_pv_reclaim(pmap_t locked_pmap)
{
        struct pch newtail;
        struct pv_chunk *pc;
        struct ia64_lpte *pte;
        pmap_t pmap;
        pv_entry_t pv;
        vaddr_t va;
        struct vm_page *m;
        struct vm_page *m_pc;
        u_long inuse;
        int bit, field, freed, idx;

        PMAP_LOCK_ASSERT(locked_pmap);
        pmap = NULL;
        m_pc = NULL;
        TAILQ_INIT(&newtail);
        while ((pc = TAILQ_FIRST(&pv_chunks)) != NULL) {
                TAILQ_REMOVE(&pv_chunks, pc, pc_lru);
                if (pmap != pc->pc_pmap) {
                        if (pmap != NULL) {
                                if (pmap != locked_pmap) {
                                        pmap_switch(locked_pmap);
                                        PMAP_UNLOCK(pmap);
                                }
                        }
                        pmap = pc->pc_pmap;
                        /* Avoid deadlock and lock recursion. */
                        if (pmap > locked_pmap)
                                PMAP_LOCK(pmap);
                        else if (pmap != locked_pmap && !PMAP_TRYLOCK(pmap)) {
                                pmap = NULL;
                                TAILQ_INSERT_TAIL(&newtail, pc, pc_lru);
                                continue;
                        }
                        pmap_switch(pmap);
                }

                /*
                 * Destroy every non-wired, 8 KB page mapping in the chunk.
                 */
                freed = 0;
                for (field = 0; field < _NPCM; field++) {
                        for (inuse = ~pc->pc_map[field] & pc_freemask[field];
                            inuse != 0; inuse &= ~(1UL << bit)) {
                                bit = ffs64(inuse) - 1;
                                idx = field * sizeof(inuse) * NBBY + bit;
                                pv = &pc->pc_pventry[idx];
                                va = pv->pv_va;
                                pte = pmap_find_vhpt(va);
                                KASSERTMSG(pte != NULL, "pte");
                                if (pmap_wired(pte))
                                        continue;
                                pmap_remove_vhpt(va);
                                pmap_invalidate_page(va);
                                m = PHYS_TO_VM_PAGE(pmap_ppn(pte));
                                if (pmap_dirty(pte))
                                        vm_page_dirty(m);
                                pmap_free_pte(pte, va);
                                TAILQ_REMOVE(&m->mdpage.pv_list, pv, pv_list);
                                /* XXX
                                if (TAILQ_EMPTY(&m->mdpage.pv_list))
                                        //vm_page_aflag_clear(m, PGA_WRITEABLE);
                                        m->flags |= PG_RDONLY;
                                */
                                pc->pc_map[field] |= 1UL << bit;
                                freed++;
                        }
                }
                if (freed == 0) {
                        TAILQ_INSERT_TAIL(&newtail, pc, pc_lru);
                        continue;
                }
                /* Every freed mapping is for a 8 KB page. */
                pmap->pm_stats.resident_count -= freed;
                PV_STAT(pv_entry_frees += freed);
                PV_STAT(pv_entry_spare += freed);
                pv_entry_count -= freed;
                TAILQ_REMOVE(&pmap->pm_pvchunk, pc, pc_list);
                for (field = 0; field < _NPCM; field++)
                        if (pc->pc_map[field] != pc_freemask[field]) {
                                TAILQ_INSERT_HEAD(&pmap->pm_pvchunk, pc,
                                    pc_list);
                                TAILQ_INSERT_TAIL(&newtail, pc, pc_lru);

                                /*
                                 * One freed pv entry in locked_pmap is
                                 * sufficient.
                                 */
                                if (pmap == locked_pmap)
                                        goto out;
                                break;
                        }
                if (field == _NPCM) {
                        PV_STAT(pv_entry_spare -= _NPCPV);
                        PV_STAT(pc_chunk_count--);
                        PV_STAT(pc_chunk_frees++);
                        /* Entire chunk is free; return it. */
                        m_pc = PHYS_TO_VM_PAGE(IA64_RR_MASK((vaddr_t)pc));
                        break;
                }
        }
out:
        TAILQ_CONCAT(&pv_chunks, &newtail, pc_lru);
        if (pmap != NULL) {
                if (pmap != locked_pmap) {
                        pmap_switch(locked_pmap);
                        PMAP_UNLOCK(pmap);
                }
        }
        return (m_pc);
}

/*
 * free the pv_entry back to the free list
 */
static void
free_pv_entry(pmap_t pmap, pv_entry_t pv)
{
        struct pv_chunk *pc;
        int bit, field, idx;

        KASSERT(rw_write_held(&pvh_global_lock));
        PMAP_LOCK_ASSERT(pmap);
        PV_STAT(pv_entry_frees++);
        PV_STAT(pv_entry_spare++);
        pv_entry_count--;
        pc = pv_to_chunk(pv);
        idx = pv - &pc->pc_pventry[0];
        field = idx / (sizeof(u_long) * NBBY);
        bit = idx % (sizeof(u_long) * NBBY);
        pc->pc_map[field] |= 1ul << bit;
        for (idx = 0; idx < _NPCM; idx++)
                if (pc->pc_map[idx] != pc_freemask[idx]) {
                        /*
                         * 98% of the time, pc is already at the head of the
                         * list.  If it isn't already, move it to the head.
                         */
                        if (__predict_false(TAILQ_FIRST(&pmap->pm_pvchunk) !=
                            pc)) {
                                TAILQ_REMOVE(&pmap->pm_pvchunk, pc, pc_list);
                                TAILQ_INSERT_HEAD(&pmap->pm_pvchunk, pc,
                                    pc_list);
                        }
                        return;
                }
        TAILQ_REMOVE(&pmap->pm_pvchunk, pc, pc_list);
        free_pv_chunk(pc);
}

static void
free_pv_chunk(struct pv_chunk *pc)
{
        struct vm_page *m;

        TAILQ_REMOVE(&pv_chunks, pc, pc_lru);
        PV_STAT(pv_entry_spare -= _NPCPV);
        PV_STAT(pc_chunk_count--);
        PV_STAT(pc_chunk_frees++);
        /* entire chunk is free, return it */
        m = PHYS_TO_VM_PAGE(IA64_RR_MASK((vaddr_t)pc));
#if 0
        /*  XXX freebsd these move pages around in queue */
        vm_page_unwire(m, 0); // releases one wiring and moves page back active/inactive queue
        vm_page_free(m);      // moves page to "free" queue & disassociate with object

        /* XXX might to need locks/other checks here, uvm_unwire, pmap_kremove... */
        uvm_pagefree(m);
#endif
        vm_page_free1(m);
}

/*
 * get a new pv_entry, allocating a block from the system
 * when needed.
 */
static pv_entry_t
get_pv_entry(pmap_t pmap, bool try)
{
        struct pv_chunk *pc;
        pv_entry_t pv;
        struct vm_page *m;
        int bit, field, idx;

        KASSERT(rw_write_held(&pvh_global_lock));
        PMAP_LOCK_ASSERT(pmap);
        PV_STAT(pv_entry_allocs++);
        pv_entry_count++;
retry:
        pc = TAILQ_FIRST(&pmap->pm_pvchunk);
        if (pc != NULL) {
                for (field = 0; field < _NPCM; field++) {
                        if (pc->pc_map[field]) {
                                bit = ffs64(pc->pc_map[field]) - 1;
                                break;
                        }
                }
                if (field < _NPCM) {
                        idx = field * sizeof(pc->pc_map[field]) * NBBY + bit;
                        pv = &pc->pc_pventry[idx];
                        pc->pc_map[field] &= ~(1ul << bit);
                        /* If this was the last item, move it to tail */
                        for (field = 0; field < _NPCM; field++)
                                if (pc->pc_map[field] != 0) {
                                        PV_STAT(pv_entry_spare--);
                                        return (pv);    /* not full, return */
                                }
                        TAILQ_REMOVE(&pmap->pm_pvchunk, pc, pc_list);
                        TAILQ_INSERT_TAIL(&pmap->pm_pvchunk, pc, pc_list);
                        PV_STAT(pv_entry_spare--);
                        return (pv);
                }
        }

        /* No free items, allocate another chunk */
        m = vm_page_alloc1();

        if (m == NULL) {
                if (try) {
                        pv_entry_count--;
                        PV_STAT(pc_chunk_tryfail++);
                        return (NULL);
                }
                m = pmap_pv_reclaim(pmap);
                if (m == NULL)
                        goto retry;
        }

        PV_STAT(pc_chunk_count++);
        PV_STAT(pc_chunk_allocs++);
        pc = (struct pv_chunk *)IA64_PHYS_TO_RR7(VM_PAGE_TO_PHYS(m));
        pc->pc_pmap = pmap;
        pc->pc_map[0] = pc_freemask[0] & ~1ul;  /* preallocated bit 0 */
        for (field = 1; field < _NPCM; field++)
                pc->pc_map[field] = pc_freemask[field];
        TAILQ_INSERT_TAIL(&pv_chunks, pc, pc_lru);
        pv = &pc->pc_pventry[0];
        TAILQ_INSERT_HEAD(&pmap->pm_pvchunk, pc, pc_list);
        PV_STAT(pv_entry_spare += _NPCPV - 1);
        return (pv);
}

/*
 * Conditionally create a pv entry.
 */
#if 0
static bool
pmap_try_insert_pv_entry(pmap_t pmap, vaddr_t va, struct vm_page *m)
{
        pv_entry_t pv;

        PMAP_LOCK_ASSERT(pmap);
        KASSERT(rw_write_held(&pvh_global_lock));
        if ((pv = get_pv_entry(pmap, true)) != NULL) {
                pv->pv_va = va;
                TAILQ_INSERT_TAIL(&m->mdpage.pv_list, pv, pv_list);
                return (true);
        } else
                return (false);
}
#endif

/*
 * Add an ia64_lpte to the VHPT.
 */
static void
pmap_enter_vhpt(struct ia64_lpte *pte, vaddr_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((vaddr_t)pte);

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

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

        pmap_vhpt_inserts++;
        bckt->length++;
        mutex_spin_exit(&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(vaddr_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;
        mutex_spin_enter(&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) {
                mutex_spin_exit(&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--;

        mutex_spin_exit(&bckt->mutex);
        return (0);
}

/*
 * Find the ia64_lpte for the given va, if any.
 */
static struct ia64_lpte *
pmap_find_vhpt(vaddr_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;

        mutex_spin_enter(&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);
        }

        mutex_spin_exit(&bckt->mutex);
        return ((chain != 0) ? pte : NULL);
}

/*
 * Remove an entry from the list of managed mappings.
 */
static int
pmap_remove_entry(pmap_t pmap, struct vm_page *m, vaddr_t va, pv_entry_t pv)
{

        KASSERT(rw_write_held(&pvh_global_lock));
        if (!pv) {
                TAILQ_FOREACH(pv, &m->mdpage.pv_list, pv_list) {
                        if (pmap == PV_PMAP(pv) && va == pv->pv_va)
                                break;
                }
        }

        if (pv) {
                TAILQ_REMOVE(&m->mdpage.pv_list, pv, pv_list);
                /* XXX
                if (TAILQ_FIRST(&m->mdpage.pv_list) == NULL)
                        //vm_page_aflag_clear(m, PGA_WRITEABLE);
                        m->flags |= PG_RDONLY;
                */
                free_pv_entry(pmap, 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, vaddr_t va, struct vm_page *m)
{
        pv_entry_t pv;

        KASSERT(rw_write_held(&pvh_global_lock));
        pv = get_pv_entry(pmap, false);
        pv->pv_va = va;
        TAILQ_INSERT_TAIL(&m->mdpage.pv_list, pv, pv_list);
}

/***************************************************
 * 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(vaddr_t va)
{
        struct ia64_lpte **dir1;
        struct ia64_lpte *leaf;

        UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
        UVMHIST_LOG(maphist, "(va=%p)", va, 0, 0, 0);

        KASSERTMSG((va >> 61) == 5, "kernel mapping 0x%lx not in region 5", va);

        KASSERTMSG(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)];

        UVMHIST_LOG(maphist, "(kpte_dir0=%#lx, kpte_dir1=%#lx, kpte_pte=%#lx)",
                    KPTE_DIR0_INDEX(va), KPTE_DIR1_INDEX(va), KPTE_PTE_INDEX(va), 0);
        UVMHIST_LOG(maphist, "(dir1=%p, leaf=%p ret=%p)",
                    dir1, leaf, &leaf[KPTE_PTE_INDEX(va)], 0);

        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(vaddr_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 = pool_cache_get(pte_pool_cache, PR_NOWAIT);
                if (pte != NULL) {
                        memset((void *)pte, 0, sizeof(struct ia64_lpte));
                        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, vaddr_t va)
{
        if (va < VM_MAXUSER_ADDRESS)
                pool_cache_put(pte_pool_cache, 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 originally 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, vaddr_t va, vaddr_t pa,
    bool wired, bool 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;

        ia64_mf();

        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, vaddr_t va,
                pv_entry_t pv, int freepte)
{
        int error;
        struct vm_page *m;

        /*
         * First remove from the VHPT.
         */
        error = pmap_remove_vhpt(va);
        KASSERTMSG(error == 0, "%s: pmap_remove_vhpt returned %d",__func__, 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);

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

        return (error);
}

/*
 * pmap_init:                   [ INTERFACE ]
 *
 *      Initialize the pmap module.  Called by vm_init(), to initialize any
 *      structures that the pmap system needs to map virtual memory.
 *
 *      Note: no locking is necessary in this function.
 */
void
pmap_init(void)
{
        UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);

        pmap_pool_cache = pool_cache_init(sizeof(struct pmap), 0, 0, 0,
                                          "pmap_pool_cache", NULL, IPL_VM,
                                          NULL, NULL, NULL);
        if (pmap_pool_cache == NULL)
                panic("%s cannot allocate pmap pool", __func__);

        pte_pool_cache = pool_cache_init(sizeof(struct ia64_lpte), 0, 0, 0,
                                         "pte_pool_cache", NULL, IPL_VM,
                                         NULL, NULL, NULL);
        if (pte_pool_cache == NULL)
                panic("%s cannot allocate pte pool", __func__);


        pmap_initialized = true;

#if DEBUG
        if (0) pmap_testout();
#endif
}

/*
 * pmap_virtual_space:          [ INTERFACE ]
 *
 *      Define the initial bounds of the kernel virtual address space.
 */
void
pmap_virtual_space(vaddr_t *vstartp, vaddr_t *vendp)
{
        *vstartp = VM_MIN_KERNEL_ADDRESS;
        *vendp = VM_MAX_KERNEL_ADDRESS;
}

/*
 * pmap_steal_memory:           [ INTERFACE ]
 *
 *      Bootstrap memory allocator (alternative to uvm_pageboot_alloc()).
 *      This function allows for early dynamic memory allocation until the
 *      virtual memory system has been bootstrapped.  After that point, either
 *      kmem_alloc or malloc should be used.  This function works by stealing
 *      pages from the (to be) managed page pool, then implicitly mapping the
 *      pages (by using their RR7 addresses) and zeroing them.
 *
 *      It may be used once the physical memory segments have been pre-loaded
 *      into the vm_physmem[] array.  Early memory allocation MUST use this
 *      interface!  This cannot be used after uvm_page_init(), and will
 *      generate a panic if tried.
 *
 *      Note that this memory will never be freed, and in essence it is wired
 *      down.
 *
 *      We must adjust *vstartp and/or *vendp iff we use address space
 *      from the kernel virtual address range defined by pmap_virtual_space().
 *
 *      Note: no locking is necessary in this function.
 */
vaddr_t
pmap_steal_memory(vsize_t size, vaddr_t *vstartp, vaddr_t *vendp)
{
        int npgs;
        uvm_physseg_t upm;
        vaddr_t va;
        paddr_t pa;

        size = round_page(size);
        npgs = atop(size);

        for (upm = uvm_physseg_get_first();
             uvm_physseg_valid_p(upm);
             upm = uvm_physseg_get_next(upm)) {
                if (uvm.page_init_done == true)
                        panic("pmap_steal_memory: called _after_ bootstrap");

                if (uvm_physseg_get_avail_start(upm) != uvm_physseg_get_start(upm) ||
                    uvm_physseg_get_avail_start(upm) >= uvm_physseg_get_avail_end(upm))
                        continue;

                if ((uvm_physseg_get_avail_end(upm) - uvm_physseg_get_avail_start(upm))
                    < npgs)
                        continue;

                /*
                 * There are enough pages here; steal them!
                 */
                pa = ptoa(uvm_physseg_get_start(upm));
                uvm_physseg_unplug(atop(pa), npgs);

                va = IA64_PHYS_TO_RR7(pa);
                memset((void *)va, 0, size);
                pmap_pages_stolen += npgs;
                return va;
        }

        /*
         * If we got here, this was no memory left.
         */
        panic("pmap_steal_memory: no memory to steal");
}

/*
 * pmap_steal_vhpt_memory:      Derived from alpha/pmap.c:pmap_steal_memory()
 * Note: This function is not visible outside the pmap module.
 * Based on pmap_steal_memory();
 * Assumptions: size is always a power of 2.
 * Returns: Allocated memory at a naturally aligned address
 */
static vaddr_t
pmap_steal_vhpt_memory(vsize_t size)
{
        int npgs;
        uvm_physseg_t upm;
        vaddr_t va;
        paddr_t pa = 0;
        paddr_t vhpt_start = 0, start1, start2, end1, end2;

        size = round_page(size);
        npgs = atop(size);

        for (upm = uvm_physseg_get_first();
             uvm_physseg_valid_p(upm);
             upm = uvm_physseg_get_next(upm)) {
                if (uvm.page_init_done == true)
                        panic("pmap_vhpt_steal_memory: called _after_ bootstrap");

                if (uvm_physseg_get_avail_start(upm) != uvm_physseg_get_start(upm) || /* XXX: ??? */
                    uvm_physseg_get_avail_start(upm) >= uvm_physseg_get_avail_end(upm))
                        continue;

                /* Break off a VHPT sized, aligned chunk off this segment. */

                start1 = uvm_physseg_get_avail_start(upm);

                /* Align requested start address on requested size boundary */
                end1 = vhpt_start = roundup(start1, npgs);

                start2 = vhpt_start + npgs;
                end2 = uvm_physseg_get_avail_end(upm);

                /* Case 1: Doesn't fit. skip this segment */

                if (start2 > end2) {
                        vhpt_start = 0;
                        continue;
                }

                /* For all cases of fit:
                 *      - Remove segment.
                 *      - Re-insert fragments via uvm_page_physload();
                 */

                /*
                 * We _fail_ on a vhpt request which exhausts memory.
                 */
                if (start1 == end1 &&
                    start2 == end2 &&
                    uvm_physseg_get_first() == uvm_physseg_get_last() /* single segment */) {
#ifdef DEBUG
                        printf("pmap_vhpt_steal_memory: out of memory!");
#endif
                        return -1;
                }

                /* Remove this segment from the list. */
                if (uvm_physseg_unplug(uvm_physseg_get_start(upm),
                                       uvm_physseg_get_end(upm) - uvm_physseg_get_start(upm)) == false) {
                        panic("%s: uvm_physseg_unplug(%"PRIxPADDR", %"PRIxPADDR") failed\n",
                              __func__, uvm_physseg_get_start(upm),
                              uvm_physseg_get_end(upm) - uvm_physseg_get_start(upm));
                }

                /* Case 2: Perfect fit - skip segment reload. */

                if (start1 == end1 && start2 == end2) break;

                /* Case 3: Left unfit - reload it.
                 */

                if (start1 != end1)
                        uvm_page_physload(start1, end1, start1, end1,
                                          VM_FREELIST_DEFAULT);

                /* Case 4: Right unfit - reload it. */

                if (start2 != end2)
                        uvm_page_physload(start2, end2, start2, end2,
                                          VM_FREELIST_DEFAULT);

                /* Case 5: Both unfit - Redundant, isn't it ?  */
                break;
        }

        /*
         * If we got here, we couldn't find a fit.
         */
        if (vhpt_start == 0) {
#ifdef DEBUG
                printf("pmap_steal_vhpt_memory: no VHPT aligned fit found.");
#endif
                return -1;
        }

        /*
         * There are enough pages here; steal them!
         */
        pa = ptoa(vhpt_start);
        va = IA64_PHYS_TO_RR7(pa);
        memset((void *)va, 0, size);
        pmap_pages_stolen += npgs;
        return va;
}

/*
 * pmap_create:                 [ INTERFACE ]
 *
 *      Create and return a physical map.
 *
 *      Note: no locking is necessary in this function.
 */
pmap_t
pmap_create(void)
{
        pmap_t pmap;
        int i;

        pmap = pool_cache_get(pmap_pool_cache, PR_WAITOK);

        if (pmap == NULL)
                panic("%s no pool", __func__);

        PMAP_LOCK_INIT(pmap);

        for (i = 0; i < IA64_VM_MINKERN_REGION; i++)
                pmap->pm_rid[i] = pmap_allocate_rid();

        TAILQ_INIT(&pmap->pm_pvchunk);

        pmap->pm_stats.resident_count = 0;
        pmap->pm_stats.wired_count = 0;

        pmap->pm_refcount = 1;

        UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
        UVMHIST_LOG(maphist, "(pm=%p)", pmap, 0, 0, 0);

        return pmap;
}

/*
 * pmap_destroy:                [ INTERFACE ]
 *
 *      Drop the reference count on the specified pmap, releasing
 *      all resources if the reference count drops to zero.
 */
void
pmap_destroy(pmap_t pmap)
{
        int i;

        UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
        UVMHIST_LOG(maphist, "(pm=%p)", pmap, 0, 0, 0);

        membar_release();
        if (atomic_dec_64_nv(&pmap->pm_refcount) > 0)
                return;
        membar_acquire();

        KASSERT(pmap->pm_stats.resident_count == 0);
        KASSERT(pmap->pm_stats.wired_count == 0);

        KASSERT(TAILQ_EMPTY(&pmap->pm_pvchunk)); /* XXX hmmm */
        KASSERT(!PMAP_LOCKED(pmap)); /* XXX hmmm */
        /*PMAP_LOCK(pmap); */  /* XXX overkill */

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

        /*PMAP_UNLOCK(pmap);*/ /* XXX hmm */
        PMAP_LOCK_DESTROY(pmap);

        pool_cache_put(pmap_pool_cache, pmap);
}

/*
 * pmap_reference:              [ INTERFACE ]
 *
 *      Add a reference to the specified pmap.
 */
void
pmap_reference(pmap_t pmap)
{
        atomic_inc_64(&pmap->pm_refcount);
}

/*
 * pmap_resident_count:         [ INTERFACE ]
 *
 *      Query the ``resident pages'' statistic for pmap.
 */
long
pmap_resident_count(pmap_t pmap)
{
        return (pmap->pm_stats.resident_count);
}

/*
 * pmap_wired_count:            [ INTERFACE ]
 *
 *      Query the ``wired pages'' statistic for pmap.
 *
 */
long
pmap_wired_count(pmap_t pmap)
{
        return (pmap->pm_stats.wired_count);
}

/*
 * pmap_growkernel:             [ INTERFACE ]
 *
 */
vaddr_t
pmap_growkernel(vaddr_t maxkvaddr)
{
        struct ia64_lpte **dir1;
        struct ia64_lpte *leaf;
        struct vm_page *pg;
#if 0
        struct vm_page *nkpg;
        paddr_t pa;
        vaddr_t va;
#endif

        /* XXX this function may still need serious fixin' */
        UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
        UVMHIST_LOG(maphist, "(va=%#lx, nkpt=%ld, kvm_end=%#lx before)", maxkvaddr, nkpt, kernel_vm_end, 0);

        vaddr_t addr = maxkvaddr;

        /* XXX use uvm_pageboot_alloc if not done? */
        if (!uvm.page_init_done)
                panic("uvm_page init not done");

        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) {
#if 0
                        /* FreeBSD does it this way... */
                        nkpg = vm_page_alloc(NULL, nkpt++, VM_ALLOC_NOOBJ|VM_ALLOC_INTERRUPT|VM_ALLOC_WIRED);
                        pg = uvm_pagealloc(NULL, 0, NULL, UVM_PGA_USERESERVE|UVM_PGA_ZERO);
#endif
                        pg = vm_page_alloc1();
                        if (!pg)
                                panic("%s: cannot add dir1 page", __func__);
                        nkpt++;

#if 0
                        dir1 = (struct ia64_lpte **)pmap_page_to_va(nkpg);
                        bzero(dir1, PAGE_SIZE);
#endif
                        dir1 = (struct ia64_lpte **)pmap_page_to_va(pg);

                        ia64_kptdir[KPTE_DIR0_INDEX(kernel_vm_end)] = dir1;
                }

#if 0
                nkpg = vm_page_alloc(NULL, nkpt++, VM_ALLOC_NOOBJ|VM_ALLOC_INTERRUPT|VM_ALLOC_WIRED);
                pg = uvm_pagealloc(NULL, 0, NULL, UVM_PGA_USERESERVE|UVM_PGA_ZERO);
#endif
                pg = vm_page_alloc1();
                if (!pg)
                        panic("%s: cannot add PTE page", __func__);
                nkpt++;
#if 0
                leaf = (struct ia64_lpte *)pmap_page_to_va(nkpg);
                bzero(leaf, PAGE_SIZE);
#endif
                leaf = (struct ia64_lpte *)pmap_page_to_va(pg);

                dir1[KPTE_DIR1_INDEX(kernel_vm_end)] = leaf;

                kernel_vm_end += PAGE_SIZE * NKPTEPG;
        }

        UVMHIST_LOG(maphist, "(va=%#lx, nkpt=%ld, kvm_end=%#lx after)", maxkvaddr, nkpt, kernel_vm_end, 0);

        /* XXX fix */
        return kernel_vm_end;
}

/*
 * pmap_enter:                  [ INTERFACE ]
 *
 *      Create a mapping in physical map pmap for the physical
 *      address pa at the virtual address va with protection speci-
 *      fied by bits in prot.
 */
int
pmap_enter(pmap_t pmap, vaddr_t va, paddr_t pa, vm_prot_t prot, u_int flags)
{
        pmap_t oldpmap;
        struct vm_page *m;
        vaddr_t opa;
        struct ia64_lpte origpte;
        struct ia64_lpte *pte;
        bool icache_inval, managed, wired, canfail;
        /* vm_memattr_t ma; */

        /* XXX this needs work */

        UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
        UVMHIST_LOG(maphist, "(pm=%p, va=%#lx, pa=%p, prot=%#x)", pmap, va, pa, prot);

        /* wired = (flags & PMAP_ENTER_WIRED) != 0; */
        wired = (flags & PMAP_WIRED) != 0;
        canfail = (flags & PMAP_CANFAIL) != 0;

        /* ma = pmap_flags_to_memattr(flags); */

        rw_enter(&pvh_global_lock, RW_WRITER);
        PMAP_LOCK(pmap);
        oldpmap = pmap_switch(pmap);

        va &= ~PAGE_MASK;
        KASSERTMSG(va <= VM_MAX_KERNEL_ADDRESS, "%s: toobig", __func__);

        /* XXX
        if ((m->oflags & VPO_UNMANAGED) == 0 && !vm_page_xbusied(m))
                VM_OBJECT_ASSERT_LOCKED(m->object);
        */

        /*
         * Find (or create) a pte for the given mapping.
         */
        pte = pmap_find_pte(va);

        if (pte == NULL) {
                pmap_switch(oldpmap);
                PMAP_UNLOCK(pmap);
                rw_exit(&pvh_global_lock);

                if (canfail)
                        return (ENOMEM);
                else
                        panic("%s: no pte available", __func__);
        }

        origpte = *pte;
        if (!pmap_present(pte)) {
                opa = ~0UL;
                pmap_enter_vhpt(pte, va);
        } else
                opa = pmap_ppn(pte);

        managed = false;
        /* XXX hmm
        pa = VM_PAGE_TO_PHYS(m);
        */

        m = PHYS_TO_VM_PAGE(pa);
        if (m == NULL) {
                /* implies page not managed? */
                panic("%s: new page needed", __func__);
        }
        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 (vm_page_is_managed(m)) {
#if 0
                KASSERTMSG(va < kmi.clean_sva || va >= kmi.clean_eva,
                           ("pmap_enter: managed mapping within the clean submap"));
#endif
                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->mdpage.memattr);
        pmap_set_pte(pte, va, pa, wired, managed);

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

        /* XXX
        if ((prot & VM_PROT_WRITE) != 0 && managed)
                //vm_page_aflag_set(m, PGA_WRITEABLE);
                m->flags &= ~PG_RDONLY;
        */
        rw_exit(&pvh_global_lock);
        pmap_switch(oldpmap);
        PMAP_UNLOCK(pmap);
        return (0);
}

/*
 * pmap_remove:                 [ INTERFACE ]
 *
 *      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.
 *
 *      Sparsely used ranges are inefficiently removed.  The VHPT is
 *      probed for every page within the range.  XXX
 */
void
pmap_remove(pmap_t pmap, vaddr_t sva, vaddr_t eva)
{
        pmap_t oldpmap;
        vaddr_t va;
        struct ia64_lpte *pte;

        UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
        UVMHIST_LOG(maphist, "(pm=%p, sva=%#lx, eva=%#lx)", pmap, sva, eva, 0);

        /*
         * Perform an unsynchronized read.  This is, however, safe.
         */
        if (pmap->pm_stats.resident_count == 0)
                return;

        rw_enter(&pvh_global_lock, RW_WRITER);
        PMAP_LOCK(pmap);
        oldpmap = pmap_switch(pmap);
        for (va = sva; va < eva; va += PAGE_SIZE) {
                pte = pmap_find_vhpt(va);
                if (pte != NULL)
                        pmap_remove_pte(pmap, pte, va, 0, 1);
        }

        rw_exit(&pvh_global_lock);
        pmap_switch(oldpmap);
        PMAP_UNLOCK(pmap);
}

/*
 * pmap_remove_all:             [ INTERFACE ]
 *
 *      This function is a hint to the pmap implementation that all
 *      entries in pmap will be removed before any more entries are
 *      entered.
 */
bool
pmap_remove_all(pmap_t pmap)
{
        /* XXX do nothing */
        return false;
}

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

        UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
        UVMHIST_LOG(maphist, "(pm=%p, sva=%#lx, eva=%#lx, prot=%#x)",
                    pmap, sva, eva, prot);
        UVMHIST_LOG(maphist, "(VM_PROT_READ=%u, VM_PROT_WRITE=%u, VM_PROT_EXECUTE=%u)",
                    (prot & VM_PROT_READ) != 0, (prot & VM_PROT_WRITE) != 0,
                    (prot & VM_PROT_EXECUTE) != 0, 0);

        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");
        sva = trunc_page(sva);
        eva = round_page(eva) - 1;

        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;

                /* wired pages unaffected by prot changes */
                if (pmap_wired(pte))
                        continue;

                if ((prot & VM_PROT_WRITE) == 0 &&
                    pmap_managed(pte) && pmap_dirty(pte)) {
                        paddr_t pa = pmap_ppn(pte);
                        struct vm_page *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);
}

/*
 * pmap_unwire:                 [ INTERFACE ]
 *
 *      Clear the wired attribute for a map/virtual-address pair.
 *
 *      The wired attribute of the page table entry is not a hardware feature,
 *      so there is no need to invalidate any TLB entries.
 *
 *      The mapping must already exist in the pmap.
 */
void
pmap_unwire(pmap_t pmap, vaddr_t va)
{
        pmap_t oldpmap;
        struct ia64_lpte *pte;

        UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
        UVMHIST_LOG(maphist, "(pm=%p, va=%#x)", pmap, va, 0, 0);

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

        pte = pmap_find_vhpt(va);

        /* XXX panic if no pte or not wired? */
        if (pte == NULL)
                panic("pmap_unwire: %lx not found in vhpt", va);

        if (!pmap_wired(pte))
                panic("pmap_unwire: pte %p isn't wired", pte);

        pmap->pm_stats.wired_count--;
        pmap_clear_wired(pte);

        pmap_switch(oldpmap);
        PMAP_UNLOCK(pmap);
}

/*
 * pmap_extract:                [ INTERFACE ]
 *
 *      Extract the physical address associated with the given
 *      pmap/virtual address pair.
 */
bool
pmap_extract(pmap_t pmap, vaddr_t va, paddr_t *pap)
{
        struct ia64_lpte *pte;
        pmap_t oldpmap;
        paddr_t pa;

        UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
        UVMHIST_LOG(maphist, "(pm=%p, va=%#lx, pap=%#lx)", pmap, va, pap, 0);

        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);

        if (pa && (pap != NULL))
                *pap = pa;

        UVMHIST_LOG(maphist, "(pa=%#lx)", pa, 0, 0, 0);

        return (pa != 0);
}
/*
 * Extract the physical page address associated with a kernel
 * virtual address.
 */
paddr_t
pmap_kextract(vaddr_t va)
{
        struct ia64_lpte *pte;
        /*uint64_t *pbvm_pgtbl;*/
        paddr_t pa;
        /*u_int idx;*/

        UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
        UVMHIST_LOG(maphist, "(va=%#lx)", va, 0, 0, 0);

        KASSERTMSG(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_INIT_KERNEL_ADDRESS) {
                pte = pmap_find_kpte(va);
                pa = pmap_present(pte) ? pmap_ppn(pte) | (va & PAGE_MASK) : 0;
                goto out;
        }
#if 0 /* XXX fix */
        /* 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;
        }
#endif

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

 out:
        UVMHIST_LOG(maphist, "(pa=%#lx)", pa, 0, 0, 0);
        return (pa);
}

/*
 * pmap_kenter_pa:              [ INTERFACE ]
 *
 *      Enter a va -> pa mapping into the kernel pmap without any
 *      physical->virtual tracking.
 *
 *      Note: no locking is necessary in this function.
 */
void
pmap_kenter_pa(vaddr_t va, paddr_t pa, vm_prot_t prot, u_int flags)
{
        struct ia64_lpte *pte;
        vm_memattr_t attr;
        const bool managed = false;  /* don't gather ref/mod info */
        const bool wired = true;     /* pmap_kenter_pa always wired */

        UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
        UVMHIST_LOG(maphist, "(va=%#lx, pa=%#lx, prot=%p, flags=%p)", va, pa, prot, flags);

        KASSERT(va >= VM_MIN_KERNEL_ADDRESS && va < VM_MAX_KERNEL_ADDRESS);

        attr = pmap_flags_to_memattr(flags);

        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, attr);
        pmap_set_pte(pte, va, pa, wired, managed);
}

/*
 * pmap_kremove:                [ INTERFACE ]
 *
 *   Remove all mappings starting at virtual address va for size
 *     bytes from the kernel physical map.  All mappings that are
 *     removed must be the ``unmanaged'' type created with
 *     pmap_kenter_pa().  The implementation may assert this.
 */
void
pmap_kremove(vaddr_t va, vsize_t size)
{
        struct ia64_lpte *pte;
        vaddr_t eva = va + size;

        UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
        UVMHIST_LOG(maphist, "(va=%#lx)", va, 0, 0, 0);

        while (va < eva) {
                pte = pmap_find_kpte(va);
                if (pmap_present(pte)) {
                        KASSERT(pmap_managed(pte) != 0);
                        pmap_remove_vhpt(va);
                        pmap_invalidate_page(va);
                        pmap_clear_present(pte);
                }
                va += PAGE_SIZE;
        }
}

/*
 * pmap_copy:                   [ INTERFACE ]
 *
 *      This function copies the mappings starting at src_addr in
 *      src_map for len bytes into dst_map starting at dst_addr.
 *
 *      Note that while this function is required to be provided by
 *      a pmap implementation, it is not actually required to do
 *      anything.  pmap_copy() is merely advisory (it is used in
 *      the fork(2) path to ``pre-fault'' the child's address
 *      space).
 */
void
pmap_copy(pmap_t dst_map, pmap_t src_map, vaddr_t dst_addr, vsize_t len,
         vaddr_t src_addr)
{
        /* nothing required */
}

/*
 * pmap_update:                 [ INTERFACE ]
 *
 *      If a pmap implementation does not delay virtual-to-physical
 *      mapping updates, pmap_update() has no operation.
 */
void
pmap_update(pmap_t pmap)
{
        /* nothing required */
}

/*
 * pmap_activate:               [ INTERFACE ]
 *
 *      Activate the pmap used by the specified process.  This includes
 *      reloading the MMU context if the current process, and marking
 *      the pmap in use by the processor.
 *
 *      Note: We may use only spin locks here, since we are called
 *      by a critical section in cpu_switch()!
 */
void
pmap_activate(struct lwp *l)
{
        UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
        UVMHIST_LOG(maphist, "(lwp=%p)", l, 0, 0, 0);
        KASSERT(l == curlwp);
#if 0
        /* pmap_switch(vmspace_pmap(td->td_proc->p_vmspace)); */
        pmap_switch(vm_map_pmap(&l->l_proc->p_vmspace->vm_map));
#else
        struct pmap *pmap = l->l_proc->p_vmspace->vm_map.pmap;

        if (pmap == pmap_kernel()) {
                return;
        }

        if (l != curlwp) {
                return;
        }

        pmap_switch(pmap);
#endif
}

/*
 * pmap_deactivate:             [ INTERFACE ]
 *
 *      Deactivate the physical map used by the process behind lwp
 *      l.  It is generally used in conjunction with
 *      pmap_activate().  Like pmap_activate(), pmap_deactivate()
 *      may not always be called when l is the current lwp.
 *
 */
void
pmap_deactivate(struct lwp *l)
{
        /* XXX ? */
}

/*
 * pmap_zero_page:              [ INTERFACE ]
 *
 *      Zero the specified (machine independent) page by mapping the page
 *      into virtual memory and clear its contents, one machine dependent
 *      page at a time.
 *
 *      Note: no locking is necessary in this function.
 */
void
pmap_zero_page(paddr_t phys)
{
        struct vm_page *m;
        vaddr_t va;

        UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
        UVMHIST_LOG(maphist, "(pa=%p)", phys, 0, 0, 0);

        m = PHYS_TO_VM_PAGE(phys);
        KASSERT(m != NULL);

        va = pmap_page_to_va(m);
        KASSERT(trunc_page(va) == va);

        UVMHIST_LOG(maphist, "(pa=%p, va=%p)", phys, va, 0, 0);
        memset((void *)va, 0, PAGE_SIZE);
}

/*
 * pmap_copy_page:              [ INTERFACE ]
 *
 *      Copy the specified (machine independent) page by mapping the page
 *      into virtual memory and using memcpy to copy the page, one machine
 *      dependent page at a time.
 *
 *      Note: no locking is necessary in this function.
 */
void
pmap_copy_page(paddr_t psrc, paddr_t pdst)
{
        struct vm_page *md, *ms;
        vaddr_t dst_va, src_va;

        UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
        UVMHIST_LOG(maphist, "(sp=%p, dp=%p)", psrc, pdst, 0, 0);

        md = PHYS_TO_VM_PAGE(pdst);
        ms = PHYS_TO_VM_PAGE(psrc);
        KASSERT(md != NULL && ms != NULL);

        dst_va = pmap_page_to_va(md);
        src_va = pmap_page_to_va(ms);
        KASSERT(trunc_page(dst_va) == dst_va && trunc_page(src_va) == src_va);

        memcpy((void *)dst_va, (void *)src_va, PAGE_SIZE);
}

/*
 * pmap_page_protect:           [ INTERFACE ]
 *
 *  Lower the permissions for all mappings of the page pg to
 *     prot.  This function is used by the virtual memory system
 *     to implement copy-on-write (called with VM_PROT_READ set in
 *     prot) and to revoke all mappings when cleaning a page
 *     (called with no bits set in prot).  Access permissions must
 *     never be added to a page as a result of this call.
 */
void
pmap_page_protect(struct vm_page *pg, vm_prot_t prot)
{
        //struct ia64_lpte *pte;
        pmap_t pmap;
        pv_entry_t pv;
        vaddr_t va;

        UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
        UVMHIST_LOG(maphist, "(m=%p, prot=%p)", pg, prot, 0, 0);

        if ((prot & VM_PROT_WRITE) != 0)
                return;
        if (prot & (VM_PROT_READ | VM_PROT_EXECUTE)) {
                /* XXX FreeBSD
                if ((pg->mdpage.aflags & PGA_WRITEABLE) == 0)
                        return;
                */
                if (pg->flags & PG_RDONLY)
                        return;
                rw_enter(&pvh_global_lock, RW_WRITER);
                TAILQ_FOREACH(pv, &pg->mdpage.pv_list, pv_list) {
                        pmap = PV_PMAP(pv);
                        va = pv->pv_va;
                        // locking of pmap done in pmap_protect
                        pmap_protect(pmap, va, va + PAGE_SIZE, prot);
                }
                /* XXX
                vm_page_aflag_clear(pg, PGA_WRITEABLE);
                */
                pg->flags |= PG_RDONLY;

                rw_exit(&pvh_global_lock);
        } else {
                pmap_remove_all_phys(pg);
        }
}

/*
 * pmap_clear_modify:           [ INTERFACE ]
 *
 *      Clear the modify bits on the specified physical page.
 */
bool
pmap_clear_modify(struct vm_page *pg)
{
        struct ia64_lpte *pte;
        pmap_t oldpmap, pmap;
        pv_entry_t pv;
        bool rv;

        UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
        UVMHIST_LOG(maphist, "(m=%p)", pg, 0, 0, 0);

        KASSERTMSG(vm_page_is_managed(pg), "%s : page %p not managed",
                   __func__, pg);

        rv = false;

        //VM_OBJECT_ASSERT_WLOCKED(m->object);
        //KASSERT(!vm_page_xbusied(m),
        //    ("pmap_clear_modify: page %p is exclusive busied", 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
         * exclusive busied, then PGA_WRITEABLE cannot be concurrently set.
         */

#if 0   /* XXX freebsd does this, looks faster but not required */
        if ((m->aflags & PGA_WRITEABLE) == 0)
                return;
        if (pg->flags & PG_RDONLY)
                return (rv);
#endif
        rw_enter(&pvh_global_lock, RW_WRITER);
        TAILQ_FOREACH(pv, &pg->mdpage.pv_list, pv_list) {
                pmap = PV_PMAP(pv);
                PMAP_LOCK(pmap);
                oldpmap = pmap_switch(pmap);
                pte = pmap_find_vhpt(pv->pv_va);
                KASSERTMSG(pte != NULL, "pte");
                if (pmap_dirty(pte)) {
                        pmap_clear_dirty(pte);
                        pmap_invalidate_page(pv->pv_va);
                        rv = true;
                }
                pmap_switch(oldpmap);
                PMAP_UNLOCK(pmap);
        }

        rw_exit(&pvh_global_lock);

        return (rv);
}

/*
 * pmap_clear_reference:        [ INTERFACE ]
 *
 *      Clear the reference bit on the specified physical page.
 *
 *      returns true or false
 *      indicating whether or not the ``referenced'' attribute was
 *      set on the page before it was cleared
 */
bool
pmap_clear_reference(struct vm_page *pg)
{
        struct ia64_lpte *pte;
        pmap_t oldpmap, pmap;
        pv_entry_t pv;
        bool rv;

        UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
        UVMHIST_LOG(maphist, "(m=%p)", pg, 0, 0, 0);

        KASSERTMSG(vm_page_is_managed(pg), "%s: page %p is not managed",
                   __func__, pg);

        rv = false;

        rw_enter(&pvh_global_lock, RW_WRITER);
        TAILQ_FOREACH(pv, &pg->mdpage.pv_list, pv_list) {
                pmap = PV_PMAP(pv);
                PMAP_LOCK(pmap);
                oldpmap = pmap_switch(pmap);
                pte = pmap_find_vhpt(pv->pv_va);
                KASSERTMSG(pte != NULL, "pte");
                if (pmap_accessed(pte)) {
                        rv = true;
                        pmap_clear_accessed(pte);
                        pmap_invalidate_page(pv->pv_va);
                }
                pmap_switch(oldpmap);
                PMAP_UNLOCK(pmap);
        }

        rw_exit(&pvh_global_lock);
        return (rv);
}

/*
 * pmap_is_modified:            [ INTERFACE ]
 *
 */
bool
pmap_is_modified(struct vm_page *pg)
{
        struct ia64_lpte *pte;
        pmap_t oldpmap, pmap;
        pv_entry_t pv;
        bool rv;

        UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
        UVMHIST_LOG(maphist, "(m=%p)", pg, 0, 0, 0);

        KASSERTMSG(vm_page_is_managed(pg), "%s: page %p is not managed",
                   __func__, pg);
        rv = false;

        /*
         * If the page is not exclusive busied, then PGA_WRITEABLE cannot be
         * concurrently set while the object is locked.  Thus, if PGA_WRITEABLE
         * is clear, no PTEs can be dirty.
         */
        /* XXX freebsd
        VM_OBJECT_ASSERT_WLOCKED(m->object);
        if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0)
                return (rv);
        */
        rw_enter(&pvh_global_lock, RW_WRITER);
        TAILQ_FOREACH(pv, &pg->mdpage.pv_list, pv_list) {
                pmap = PV_PMAP(pv);
                PMAP_LOCK(pmap);
                oldpmap = pmap_switch(pmap);
                pte = pmap_find_vhpt(pv->pv_va);
                pmap_switch(oldpmap);
                KASSERTMSG(pte != NULL, "pte");

                rv = pmap_dirty(pte) ? true : false;
                PMAP_UNLOCK(pmap);
                if (rv)
                        break;
        }

        rw_exit(&pvh_global_lock);
        return (rv);
}

/*
 * pmap_is_referenced:          [ INTERFACE ]
 *
 *      Test whether or not the ``referenced'' attribute is set on
 *      page pg.
 *
 */
bool
pmap_is_referenced(struct vm_page *pg)
{
        struct ia64_lpte *pte;
        pmap_t oldpmap, pmap;
        pv_entry_t pv;
        bool rv;

        UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
        UVMHIST_LOG(maphist, "(m=%p)", pg, 0, 0, 0);

        KASSERTMSG(vm_page_is_managed(pg), "%s: page %p is not managed",
                __func__, pg);

        rv = false;
        rw_enter(&pvh_global_lock, RW_WRITER);
        TAILQ_FOREACH(pv, &pg->mdpage.pv_list, pv_list) {
                pmap = PV_PMAP(pv);
                PMAP_LOCK(pmap);
                oldpmap = pmap_switch(pmap);
                pte = pmap_find_vhpt(pv->pv_va);
                KASSERTMSG(pte != NULL, "pte");
                rv = pmap_accessed(pte) ? true : false;
                pmap_switch(oldpmap);
                PMAP_UNLOCK(pmap);
                if (rv)
                        break;
        }

        rw_exit(&pvh_global_lock);
        return (rv);
}

/*
 * pmap_phys_address:           [ INTERFACE ]
 *
 *      Return the physical address corresponding to the specified
 *      cookie.  Used by the device pager to decode a device driver's
 *      mmap entry point return value.
 *
 *      Note: no locking is necessary in this function.
 */
paddr_t
pmap_phys_address(paddr_t ppn)
{
        return ia64_ptob(ppn);
}

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

        critical_enter();

        prevpm = curcpu()->ci_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);
                }
        }

        /* XXX */
        ia64_srlz_d();
        curcpu()->ci_pmap = pm;
        ia64_srlz_d();

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


/*
 * Synchronize CPU instruction caches of the specified range.
 * Same as freebsd
 *   void pmap_sync_icache(pmap_t pm, vaddr_t va, vsize_t sz)
 */
void
pmap_procwr(struct proc *p, vaddr_t va, vsize_t sz)
{
        pmap_t oldpm;
        struct ia64_lpte *pte;
        vaddr_t lim;
        vsize_t len;

        struct pmap * const pm = p->p_vmspace->vm_map.pmap;

        UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
        UVMHIST_LOG(maphist, "(pm=%p, va=%#lx, sz=%#lx)", pm, va, sz, 0);

        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);
}

/*
 *      Routine:        pmap_remove_all_phys
 *      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_phys(struct vm_page *m)
{
        pmap_t oldpmap;
        pv_entry_t pv;

        UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
        UVMHIST_LOG(maphist, "(m=%p)", m, 0, 0, 0);

        KASSERTMSG(vm_page_is_managed(m), "%s: page %p is not managed",
                   __func__, m);

        rw_enter(&pvh_global_lock, RW_WRITER);
        while ((pv = TAILQ_FIRST(&m->mdpage.pv_list)) != NULL) {
                struct ia64_lpte *pte;
                pmap_t pmap = PV_PMAP(pv);
                vaddr_t va = pv->pv_va;

                PMAP_LOCK(pmap);
                oldpmap = pmap_switch(pmap);
                pte = pmap_find_vhpt(va);
                KASSERTMSG(pte != NULL, "pte");
                if (pmap_ppn(pte) != VM_PAGE_TO_PHYS(m))
                        panic("%s: pv_table for %lx is inconsistent",
                              __func__, VM_PAGE_TO_PHYS(m));
                pmap_remove_pte(pmap, pte, va, pv, 1);
                pmap_switch(oldpmap);
                PMAP_UNLOCK(pmap);
        }
        /* XXX freebsd
        vm_page_aflag_clear(m, PGA_WRITEABLE);
        */
        m->flags |= PG_RDONLY;

        rw_exit(&pvh_global_lock);
}

/*
 *      vm_page_alloc1:
 *
 *      Allocate and return a memory cell with no associated object.
 */
static struct vm_page
*vm_page_alloc1(void)
{
        struct vm_page *pg;

        pg = uvm_pagealloc(NULL, 0, NULL, UVM_PGA_USERESERVE|UVM_PGA_ZERO);
        if (pg) {
                pg->wire_count = 1;     /* no mappings yet */
                pg->flags &= ~PG_BUSY;  /* never busy */
        }
        return pg;
}

/*
 *      vm_page_free1:
 *
 *      Returns the given page to the free list,
 *      disassociating it with any VM object.
 *
 *      Object and page must be locked prior to entry.
 */
static void
vm_page_free1(struct vm_page *pg)
{
        KASSERT(pg->flags != (PG_CLEAN|PG_FAKE)); /* Freeing invalid page */

        pg->flags |= PG_BUSY;
        pg->wire_count = 0;
        uvm_pagefree(pg);
}

/*
 *      pmap_flag_to_attr
 *
 *      Convert pmap_enter/pmap_kenter_pa flags to memory attributes
 */
static vm_memattr_t
pmap_flags_to_memattr(u_int flags)
{
        u_int cacheflags = flags & PMAP_CACHE_MASK;

#if 0
        UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
        UVMHIST_LOG(maphist, "(PMAP_NOCACHE=%u, PMAP_WRITE_COMBINE=%u, "
                    "PMAP_WRITE_BACK=%u, PMAP_NOCACHE_OVR=%u)",
                    (flags & PMAP_NOCACHE) != 0, (flags & PMAP_WRITE_COMBINE) != 0,
                    (flags & PMAP_WRITE_BACK) != 0, (flags & PMAP_NOCACHE_OVR) != 0);
#endif
        switch (cacheflags) {
        case PMAP_NOCACHE:
                return VM_MEMATTR_UNCACHEABLE;
        case PMAP_WRITE_COMBINE:
                /* XXX implement if possible */
                KASSERT(1);
                return VM_MEMATTR_WRITE_COMBINING;
        case PMAP_WRITE_BACK:
                return VM_MEMATTR_WRITE_BACK;
        case PMAP_NOCACHE_OVR:
        default:
                return VM_MEMATTR_DEFAULT;
        }
}

#ifdef DEBUG
/*
 * Test ref/modify handling.
 */

static void
pmap_testout(void)
{
        vaddr_t va;
        volatile int *loc;
        int val = 0;
        paddr_t pa;
        struct vm_page *pg;
        int ref, mod;
        bool extracted;

        /* Allocate a page */
        va = (vaddr_t)uvm_km_alloc(kernel_map, PAGE_SIZE, 0, UVM_KMF_WIRED | UVM_KMF_ZERO);
        KASSERT(va != 0);
        loc = (int*)va;

        extracted = pmap_extract(pmap_kernel(), va, &pa);
        printf("va %p pa %lx extracted %u\n", (void *)(u_long)va, pa, extracted);
        printf("kextract %lx\n", pmap_kextract(va));

        pg = PHYS_TO_VM_PAGE(pa);
        pmap_enter(pmap_kernel(), va, pa, VM_PROT_ALL, 0);
        pmap_update(pmap_kernel());

        ref = pmap_is_referenced(pg);
        mod = pmap_is_modified(pg);

        printf("Entered page va %p pa %lx: ref %d, mod %d\n",
               (void *)(u_long)va, (long)pa, ref, mod);

        /* Now clear reference and modify */
        ref = pmap_clear_reference(pg);
        mod = pmap_clear_modify(pg);
        printf("Clearing page va %p pa %lx: ref %d, mod %d\n",
               (void *)(u_long)va, (long)pa,
               ref, mod);

        /* Check it's properly cleared */
        ref = pmap_is_referenced(pg);
        mod = pmap_is_modified(pg);
        printf("Checking cleared page: ref %d, mod %d\n",
               ref, mod);

        /* Reference page */
        val = *loc;

        ref = pmap_is_referenced(pg);
        mod = pmap_is_modified(pg);
        printf("Referenced page: ref %d, mod %d val %x\n",
               ref, mod, val);

        /* Now clear reference and modify */
        ref = pmap_clear_reference(pg);
        mod = pmap_clear_modify(pg);
        printf("Clearing page va %p pa %lx: ref %d, mod %d\n",
               (void *)(u_long)va, (long)pa,
               ref, mod);

        /* Modify page */
        *loc = 1;

        ref = pmap_is_referenced(pg);
        mod = pmap_is_modified(pg);
        printf("Modified page: ref %d, mod %d\n",
               ref, mod);

        /* Now clear reference and modify */
        ref = pmap_clear_reference(pg);
        mod = pmap_clear_modify(pg);
        printf("Clearing page va %p pa %lx: ref %d, mod %d\n",
               (void *)(u_long)va, (long)pa,
               ref, mod);

        /* Check it's properly cleared */
        ref = pmap_is_referenced(pg);
        mod = pmap_is_modified(pg);
        printf("Checking cleared page: ref %d, mod %d\n",
               ref, mod);

        /* Modify page */
        *loc = 1;

        ref = pmap_is_referenced(pg);
        mod = pmap_is_modified(pg);
        printf("Modified page: ref %d, mod %d\n",
               ref, mod);

        /* Check pmap_protect() */
        pmap_protect(pmap_kernel(), va, va+1, VM_PROT_READ);
        pmap_update(pmap_kernel());
        ref = pmap_is_referenced(pg);
        mod = pmap_is_modified(pg);
        printf("pmap_protect(VM_PROT_READ): ref %d, mod %d\n",
               ref, mod);

        /* Now clear reference and modify */
        ref = pmap_clear_reference(pg);
        mod = pmap_clear_modify(pg);
        printf("Clearing page va %p pa %lx: ref %d, mod %d\n",
               (void *)(u_long)va, (long)pa,
               ref, mod);

        /* Modify page */
        pmap_enter(pmap_kernel(), va, pa, VM_PROT_ALL, 0);
        pmap_update(pmap_kernel());
        *loc = 1;

        ref = pmap_is_referenced(pg);
        mod = pmap_is_modified(pg);
        printf("Modified page: ref %d, mod %d\n",
               ref, mod);

        /* Check pmap_protect() */
        pmap_protect(pmap_kernel(), va, va+1, VM_PROT_NONE);
        pmap_update(pmap_kernel());
        ref = pmap_is_referenced(pg);
        mod = pmap_is_modified(pg);
        printf("pmap_protect(VM_PROT_READ): ref %d, mod %d\n",
               ref, mod);

        /* Now clear reference and modify */
        ref = pmap_clear_reference(pg);
        mod = pmap_clear_modify(pg);
        printf("Clearing page va %p pa %lx: ref %d, mod %d\n",
               (void *)(u_long)va, (long)pa,
               ref, mod);

        /* Modify page */
        pmap_enter(pmap_kernel(), va, pa, VM_PROT_ALL, 0);
        pmap_update(pmap_kernel());
        *loc = 1;

        ref = pmap_is_referenced(pg);
        mod = pmap_is_modified(pg);
        printf("Modified page: ref %d, mod %d\n",
               ref, mod);

        /* Check pmap_pag_protect() */
        pmap_page_protect(pg, VM_PROT_READ);
        ref = pmap_is_referenced(pg);
        mod = pmap_is_modified(pg);
        printf("pmap_protect(): ref %d, mod %d\n",
               ref, mod);

        /* Now clear reference and modify */
        ref = pmap_clear_reference(pg);
        mod = pmap_clear_modify(pg);
        printf("Clearing page va %p pa %lx: ref %d, mod %d\n",
               (void *)(u_long)va, (long)pa,
               ref, mod);


        /* Modify page */
        pmap_enter(pmap_kernel(), va, pa, VM_PROT_ALL, 0);
        pmap_update(pmap_kernel());
        *loc = 1;

        ref = pmap_is_referenced(pg);
        mod = pmap_is_modified(pg);
        printf("Modified page: ref %d, mod %d\n",
               ref, mod);

        /* Check pmap_pag_protect() */
        pmap_page_protect(pg, VM_PROT_NONE);
        ref = pmap_is_referenced(pg);
        mod = pmap_is_modified(pg);
        printf("pmap_protect(): ref %d, mod %d\n",
               ref, mod);

        /* Now clear reference and modify */
        ref = pmap_clear_reference(pg);
        mod = pmap_clear_modify(pg);
        printf("Clearing page va %p pa %lx: ref %d, mod %d\n",
               (void *)(u_long)va, (long)pa,
               ref, mod);

        /* Unmap page */
        pmap_remove(pmap_kernel(), va, va+1);
        pmap_update(pmap_kernel());
        ref = pmap_is_referenced(pg);
        mod = pmap_is_modified(pg);
        printf("Unmapped page: ref %d, mod %d\n", ref, mod);

        /* Now clear reference and modify */
        ref = pmap_clear_reference(pg);
        mod = pmap_clear_modify(pg);
        printf("Clearing page va %p pa %lx: ref %d, mod %d\n",
               (void *)(u_long)va, (long)pa, ref, mod);

        /* Check it's properly cleared */
        ref = pmap_is_referenced(pg);
        mod = pmap_is_modified(pg);
        printf("Checking cleared page: ref %d, mod %d\n",
               ref, mod);

        pmap_remove(pmap_kernel(), va, va+1);
        pmap_update(pmap_kernel());
        //pmap_free_page(pa, cpus_active);
        uvm_km_free(kernel_map, (vaddr_t)va, PAGE_SIZE,
                    UVM_KMF_WIRED | UVM_KMF_ZERO);

        panic("end of testout");
}
#endif