#include "opt_msgbuf.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/msgbuf.h>
#include <sys/vmmeter.h>
#include <sys/mman.h>
#include <sys/vmspace.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <sys/sysctl.h>
#include <sys/lock.h>
#include <vm/vm_kern.h>
#include <vm/vm_page.h>
#include <vm/vm_map.h>
#include <vm/vm_object.h>
#include <vm/vm_extern.h>
#include <vm/vm_pageout.h>
#include <vm/vm_pager.h>
#include <vm/vm_zone.h>
#include <sys/thread2.h>
#include <sys/spinlock2.h>
#include <vm/vm_page2.h>
#include <machine/cputypes.h>
#include <machine/md_var.h>
#include <machine/specialreg.h>
#include <machine/smp.h>
#include <machine/globaldata.h>
#include <machine/pcb.h>
#include <machine/pmap.h>
#include <machine/pmap_inval.h>
#include <ddb/ddb.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#define PMAP_KEEP_PDIRS
#ifndef PMAP_SHPGPERPROC
#define PMAP_SHPGPERPROC 1000
#endif
#if defined(DIAGNOSTIC)
#define PMAP_DIAGNOSTIC
#endif
#define MINPV 2048
#if !defined(PMAP_DIAGNOSTIC)
#define PMAP_INLINE __inline
#else
#define PMAP_INLINE
#endif
static pd_entry_t *pmap_pde(pmap_t pmap, vm_offset_t va);
#define pdir_pde(m, v) (m[(vm_offset_t)(v) >> PDRSHIFT])
#define pmap_pde_v(pte) ((*(pd_entry_t *)pte & VPTE_V) != 0)
#define pmap_pte_w(pte) ((*(pt_entry_t *)pte & VPTE_WIRED) != 0)
#define pmap_pte_m(pte) ((*(pt_entry_t *)pte & VPTE_M) != 0)
#define pmap_pte_u(pte) ((*(pt_entry_t *)pte & VPTE_A) != 0)
#define pmap_pte_v(pte) ((*(pt_entry_t *)pte & VPTE_V) != 0)
#define pte_prot(m, p) \
(protection_codes[p & (VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE)])
static uint64_t protection_codes[8];
static struct pmap kernel_pmap_store;
struct pmap *kernel_pmap = &kernel_pmap_store;
static boolean_t pmap_initialized = FALSE;
static struct vm_object kptobj;
static int nkpt;
static uint64_t KPDphys;
uint64_t KPDPphys;
uint64_t KPML4phys;
extern void *vkernel_stack;
static vm_zone_t pvzone;
static struct vm_zone pvzone_store;
static vm_pindex_t pv_entry_count = 0;
static vm_pindex_t pv_entry_max = 0;
static vm_pindex_t pv_entry_high_water = 0;
static int pmap_pagedaemon_waken = 0;
static struct pv_entry *pvinit;
pt_entry_t *CMAP1 = NULL, *ptmmap;
caddr_t CADDR1 = NULL;
static pt_entry_t *msgbufmap;
uint64_t KPTphys;
static PMAP_INLINE void free_pv_entry (pv_entry_t pv);
static pv_entry_t get_pv_entry (void);
static void x86_64_protection_init (void);
static __inline void pmap_clearbit (vm_page_t m, int bit);
static void pmap_remove_all (vm_page_t m);
static int pmap_remove_pte (struct pmap *pmap, pt_entry_t *ptq,
pt_entry_t oldpte, vm_offset_t sva);
static void pmap_remove_page (struct pmap *pmap, vm_offset_t va);
static int pmap_remove_entry (struct pmap *pmap, vm_page_t m,
vm_offset_t va);
static boolean_t pmap_testbit (vm_page_t m, int bit);
static void pmap_insert_entry (pmap_t pmap, vm_offset_t va,
vm_page_t mpte, vm_page_t m, pv_entry_t);
static vm_page_t pmap_allocpte (pmap_t pmap, vm_offset_t va);
static int pmap_release_free_page (pmap_t pmap, vm_page_t p);
static vm_page_t _pmap_allocpte (pmap_t pmap, vm_pindex_t ptepindex);
static vm_page_t pmap_page_lookup (vm_object_t object, vm_pindex_t pindex);
static int pmap_unuse_pt (pmap_t, vm_offset_t, vm_page_t);
static int
pv_entry_compare(pv_entry_t pv1, pv_entry_t pv2)
{
if (pv1->pv_va < pv2->pv_va)
return(-1);
if (pv1->pv_va > pv2->pv_va)
return(1);
return(0);
}
RB_GENERATE2(pv_entry_rb_tree, pv_entry, pv_entry,
pv_entry_compare, vm_offset_t, pv_va);
static __inline vm_pindex_t
pmap_pt_pindex(vm_offset_t va)
{
return va >> PDRSHIFT;
}
static __inline vm_pindex_t
pmap_pte_index(vm_offset_t va)
{
return ((va >> PAGE_SHIFT) & ((1ul << NPTEPGSHIFT) - 1));
}
static __inline vm_pindex_t
pmap_pde_index(vm_offset_t va)
{
return ((va >> PDRSHIFT) & ((1ul << NPDEPGSHIFT) - 1));
}
static __inline vm_pindex_t
pmap_pdpe_index(vm_offset_t va)
{
return ((va >> PDPSHIFT) & ((1ul << NPDPEPGSHIFT) - 1));
}
static __inline vm_pindex_t
pmap_pml4e_index(vm_offset_t va)
{
return ((va >> PML4SHIFT) & ((1ul << NPML4EPGSHIFT) - 1));
}
static __inline pml4_entry_t *
pmap_pml4e(pmap_t pmap, vm_offset_t va)
{
return (&pmap->pm_pml4[pmap_pml4e_index(va)]);
}
static __inline pdp_entry_t *
pmap_pml4e_to_pdpe(pml4_entry_t *pml4e, vm_offset_t va)
{
pdp_entry_t *pdpe;
pdpe = (pdp_entry_t *)PHYS_TO_DMAP(*pml4e & VPTE_FRAME);
return (&pdpe[pmap_pdpe_index(va)]);
}
static __inline pdp_entry_t *
pmap_pdpe(pmap_t pmap, vm_offset_t va)
{
pml4_entry_t *pml4e;
pml4e = pmap_pml4e(pmap, va);
if ((*pml4e & VPTE_V) == 0)
return NULL;
return (pmap_pml4e_to_pdpe(pml4e, va));
}
static __inline pd_entry_t *
pmap_pdpe_to_pde(pdp_entry_t *pdpe, vm_offset_t va)
{
pd_entry_t *pde;
pde = (pd_entry_t *)PHYS_TO_DMAP(*pdpe & VPTE_FRAME);
return (&pde[pmap_pde_index(va)]);
}
static __inline pd_entry_t *
pmap_pde(pmap_t pmap, vm_offset_t va)
{
pdp_entry_t *pdpe;
pdpe = pmap_pdpe(pmap, va);
if (pdpe == NULL || (*pdpe & VPTE_V) == 0)
return NULL;
return (pmap_pdpe_to_pde(pdpe, va));
}
static __inline pt_entry_t *
pmap_pde_to_pte(pd_entry_t *pde, vm_offset_t va)
{
pt_entry_t *pte;
pte = (pt_entry_t *)PHYS_TO_DMAP(*pde & VPTE_FRAME);
return (&pte[pmap_pte_index(va)]);
}
static __inline
vm_page_t
pmap_hold_pt_page(pd_entry_t *pde, vm_offset_t va)
{
pt_entry_t pte;
vm_page_t pt_m;
pte = (pt_entry_t)*pde;
KKASSERT(pte != 0);
pt_m = PHYS_TO_VM_PAGE(pte & VPTE_FRAME);
vm_page_hold(pt_m);
return pt_m;
}
static __inline pt_entry_t *
pmap_pte(pmap_t pmap, vm_offset_t va)
{
pd_entry_t *pde;
pde = pmap_pde(pmap, va);
if (pde == NULL || (*pde & VPTE_V) == 0)
return NULL;
if ((*pde & VPTE_PS) != 0)
return ((pt_entry_t *)pde);
return (pmap_pde_to_pte(pde, va));
}
static __inline vm_offset_t
pmap_nextva(pmap_t pmap, vm_offset_t va)
{
if (pmap_pml4e(pmap, va) == NULL)
va = (va & ~(long)(NBPML4 - 1)) + NBPML4;
else if (pmap_pdpe(pmap, va) == NULL)
va = (va & ~(long)(NBPDP - 1)) + NBPDP;
else if (pmap_pde(pmap, va) == NULL)
va = (va & ~(long)(NBPDR - 1)) + NBPDR;
else
va = va + PAGE_SIZE;
return va;
}
static PMAP_INLINE pt_entry_t *
vtopte(vm_offset_t va)
{
pt_entry_t *x;
x = pmap_pte(kernel_pmap, va);
assert(x != NULL);
return x;
}
static __inline pd_entry_t *
vtopde(vm_offset_t va)
{
pd_entry_t *x;
x = pmap_pde(kernel_pmap, va);
assert(x != NULL);
return x;
}
vm_paddr_t
uservtophys(vm_offset_t va)
{
struct vmspace *vm = curproc->p_vmspace;
vm_page_t m;
vm_paddr_t pa;
int error;
int busy;
if (PAGE_SIZE - (va & PAGE_MASK) < sizeof(u_int))
return ((vm_paddr_t)-1);
m = vm_fault_page(&vm->vm_map, trunc_page(va),
VM_PROT_READ|VM_PROT_WRITE,
VM_FAULT_NORMAL,
&error, &busy);
if (error)
return ((vm_paddr_t)-1);
pa = VM_PAGE_TO_PHYS(m) | (va & PAGE_MASK);
if (busy)
vm_page_wakeup(m);
else
vm_page_unhold(m);
return pa;
}
static uint64_t
allocpages(vm_paddr_t *firstaddr, int n)
{
uint64_t ret;
ret = *firstaddr;
*firstaddr += n * PAGE_SIZE;
return (ret);
}
static void
create_pagetables(vm_paddr_t *firstaddr, int64_t ptov_offset)
{
int i;
pml4_entry_t *KPML4virt;
pdp_entry_t *KPDPvirt;
pd_entry_t *KPDvirt;
pt_entry_t *KPTvirt;
int kpml4i = pmap_pml4e_index(ptov_offset);
int kpdpi = pmap_pdpe_index(ptov_offset);
int kpdi = pmap_pde_index(ptov_offset);
nkpt = (Maxmem * (sizeof(struct vm_page) * 2) + MSGBUF_SIZE) / NBPDR;
KPML4phys = allocpages(firstaddr, 1);
KPDPphys = allocpages(firstaddr, NKPML4E);
KPDphys = allocpages(firstaddr, NKPDPE);
KPTphys = allocpages(firstaddr, nkpt);
KPML4virt = (pml4_entry_t *)PHYS_TO_DMAP(KPML4phys);
KPDPvirt = (pdp_entry_t *)PHYS_TO_DMAP(KPDPphys);
KPDvirt = (pd_entry_t *)PHYS_TO_DMAP(KPDphys);
KPTvirt = (pt_entry_t *)PHYS_TO_DMAP(KPTphys);
bzero(KPML4virt, 1 * PAGE_SIZE);
bzero(KPDPvirt, NKPML4E * PAGE_SIZE);
bzero(KPDvirt, NKPDPE * PAGE_SIZE);
bzero(KPTvirt, nkpt * PAGE_SIZE);
for (i = 0; i < nkpt; i++) {
KPDvirt[i + kpdi] = KPTphys + (i << PAGE_SHIFT);
KPDvirt[i + kpdi] |= VPTE_RW | VPTE_V | VPTE_U;
}
for (i = 0; i < NKPDPE; i++) {
KPDPvirt[i + kpdpi] = KPDphys + (i << PAGE_SHIFT);
KPDPvirt[i + kpdpi] |= VPTE_RW | VPTE_V | VPTE_U;
}
KPML4virt[PML4PML4I] = KPML4phys;
KPML4virt[PML4PML4I] |= VPTE_RW | VPTE_V | VPTE_U;
KPML4virt[kpml4i] = KPDPphys;
KPML4virt[kpml4i] |= VPTE_RW | VPTE_V | VPTE_U;
}
void
pmap_page_init(struct vm_page *m)
{
vm_page_init(m);
TAILQ_INIT(&m->md.pv_list);
}
void
pmap_bootstrap(vm_paddr_t *firstaddr, int64_t ptov_offset)
{
vm_offset_t va;
pt_entry_t *pte;
create_pagetables(firstaddr, ptov_offset);
virtual_start = KvaStart;
virtual_end = KvaEnd;
x86_64_protection_init();
kernel_pmap->pm_pml4 = (pml4_entry_t *)PHYS_TO_DMAP(KPML4phys);
kernel_pmap->pm_count = 1;
CPUMASK_ASSALLONES(kernel_pmap->pm_active);
kernel_pmap->pm_pteobj = NULL;
RB_INIT(&kernel_pmap->pm_pvroot);
spin_init(&kernel_pmap->pm_spin, "pmapbootstrap");
#define SYSMAP(c, p, v, n) \
v = (c)va; va += ((n)*PAGE_SIZE); p = pte; pte += (n);
va = virtual_start;
pte = pmap_pte(kernel_pmap, va);
SYSMAP(caddr_t, CMAP1, CADDR1, 1)
#if 0
SYSMAP(caddr_t, pt_crashdumpmap, crashdumpmap, MAXDUMPPGS);
#endif
SYSMAP(caddr_t, ptmmap, ptvmmap, 1)
SYSMAP(struct msgbuf *, msgbufmap, msgbufp,
atop(round_page(MSGBUF_SIZE)))
virtual_start = va;
*CMAP1 = 0;
cpu_invltlb();
}
void
pmap_init(void)
{
vm_pindex_t i;
vm_pindex_t initial_pvs;
vm_object_init(&kptobj, 5);
kernel_pmap->pm_pteobj = &kptobj;
for (i = 0; i < vm_page_array_size; i++) {
vm_page_t m;
m = &vm_page_array[i];
TAILQ_INIT(&m->md.pv_list);
m->md.pv_list_count = 0;
}
initial_pvs = vm_page_array_size;
if (initial_pvs < MINPV)
initial_pvs = MINPV;
pvzone = &pvzone_store;
pvinit = (struct pv_entry *)
kmem_alloc(kernel_map,
initial_pvs * sizeof (struct pv_entry),
VM_SUBSYS_PVENTRY);
zbootinit(pvzone, "PV ENTRY", sizeof (struct pv_entry), pvinit,
initial_pvs);
pmap_initialized = TRUE;
}
void
pmap_init2(void)
{
vm_pindex_t shpgperproc = PMAP_SHPGPERPROC;
TUNABLE_LONG_FETCH("vm.pmap.shpgperproc", &shpgperproc);
pv_entry_max = shpgperproc * maxproc + vm_page_array_size;
TUNABLE_LONG_FETCH("vm.pmap.pv_entries", &pv_entry_max);
pv_entry_high_water = 9 * (pv_entry_max / 10);
zinitna(pvzone, NULL, 0, pv_entry_max, ZONE_INTERRUPT);
}
static void
pmap_track_modified(pmap_t pmap, vm_offset_t va)
{
KKASSERT(pmap != kernel_pmap ||
va < clean_sva || va >= clean_eva);
}
vm_paddr_t
pmap_extract(pmap_t pmap, vm_offset_t va, void **handlep)
{
vm_paddr_t rtval;
pt_entry_t *pte;
pd_entry_t pde, *pdep;
vm_object_hold(pmap->pm_pteobj);
rtval = 0;
pdep = pmap_pde(pmap, va);
if (pdep != NULL) {
pde = *pdep;
if (pde) {
if ((pde & VPTE_PS) != 0) {
rtval = (pde & PG_PS_FRAME) | (va & PDRMASK);
} else {
pte = pmap_pde_to_pte(pdep, va);
rtval = (*pte & VPTE_FRAME) | (va & PAGE_MASK);
}
}
}
if (handlep)
*handlep = NULL;
vm_object_drop(pmap->pm_pteobj);
return rtval;
}
void
pmap_extract_done(void *handle)
{
pmap_t pmap;
if (handle) {
pmap = handle;
vm_object_drop(pmap->pm_pteobj);
}
}
vm_page_t
pmap_fault_page_quick(pmap_t pmap __unused, vm_offset_t vaddr __unused,
vm_prot_t prot __unused, int *busyp __unused)
{
return(NULL);
}
vm_paddr_t
pmap_kextract(vm_offset_t va)
{
pd_entry_t pde;
vm_paddr_t pa;
KKASSERT(va >= KvaStart && va < KvaEnd);
#if 0
if (va >= DMAP_MIN_ADDRESS && va < DMAP_MAX_ADDRESS) {
pa = DMAP_TO_PHYS(va);
} else {
#endif
pde = *vtopde(va);
if (pde & VPTE_PS) {
pa = (pde & PG_PS_FRAME) | (va & PDRMASK);
} else {
pa = *pmap_pde_to_pte(&pde, va);
pa = (pa & VPTE_FRAME) | (va & PAGE_MASK);
}
#if 0
}
#endif
return pa;
}
void
pmap_kenter(vm_offset_t va, vm_paddr_t pa)
{
pt_entry_t *ptep;
pt_entry_t npte;
KKASSERT(va >= KvaStart && va < KvaEnd);
npte = pa | VPTE_RW | VPTE_V | VPTE_U;
ptep = vtopte(va);
#if 1
pmap_inval_pte(ptep, kernel_pmap, va);
#else
if (*pte & VPTE_V)
pmap_inval_pte(ptep, kernel_pmap, va);
#endif
atomic_swap_long(ptep, npte);
}
int
pmap_kenter_quick(vm_offset_t va, vm_paddr_t pa)
{
pt_entry_t *ptep;
pt_entry_t npte;
int res;
KKASSERT(va >= KvaStart && va < KvaEnd);
npte = (vpte_t)pa | VPTE_RW | VPTE_V | VPTE_U;
ptep = vtopte(va);
#if 1
pmap_inval_pte_quick(ptep, kernel_pmap, va);
res = 1;
#else
res = (*ptep != 0);
if (*pte & VPTE_V)
pmap_inval_pte(pte, kernel_pmap, va);
#endif
atomic_swap_long(ptep, npte);
return res;
}
int
pmap_kenter_noinval(vm_offset_t va, vm_paddr_t pa)
{
pt_entry_t *ptep;
pt_entry_t npte;
int res;
KKASSERT(va >= KvaStart && va < KvaEnd);
npte = (vpte_t)pa | VPTE_RW | VPTE_V | VPTE_U;
ptep = vtopte(va);
#if 1
res = 1;
#else
res = (*ptep != 0);
#endif
atomic_swap_long(ptep, npte);
return res;
}
void
pmap_kremove(vm_offset_t va)
{
pt_entry_t *ptep;
KKASSERT(va >= KvaStart && va < KvaEnd);
ptep = vtopte(va);
atomic_swap_long(ptep, 0);
pmap_inval_pte(ptep, kernel_pmap, va);
}
void
pmap_kremove_quick(vm_offset_t va)
{
pt_entry_t *ptep;
KKASSERT(va >= KvaStart && va < KvaEnd);
ptep = vtopte(va);
atomic_swap_long(ptep, 0);
pmap_inval_pte(ptep, kernel_pmap, va);
}
void
pmap_kremove_noinval(vm_offset_t va)
{
pt_entry_t *ptep;
KKASSERT(va >= KvaStart && va < KvaEnd);
ptep = vtopte(va);
atomic_swap_long(ptep, 0);
}
vm_offset_t
pmap_map(vm_offset_t *virtp, vm_paddr_t start, vm_paddr_t end, int prot)
{
return PHYS_TO_DMAP(start);
}
static __inline void
_pmap_qenter(vm_offset_t beg_va, vm_page_t *m, int count, int doinval)
{
vm_offset_t end_va;
vm_offset_t va;
end_va = beg_va + count * PAGE_SIZE;
KKASSERT(beg_va >= KvaStart && end_va <= KvaEnd);
for (va = beg_va; va < end_va; va += PAGE_SIZE) {
pt_entry_t *ptep;
ptep = vtopte(va);
atomic_swap_long(ptep, VM_PAGE_TO_PHYS(*m) |
VPTE_RW | VPTE_V | VPTE_U);
++m;
}
if (doinval)
pmap_invalidate_range(kernel_pmap, beg_va, end_va);
}
void
pmap_qenter(vm_offset_t beg_va, vm_page_t *m, int count)
{
_pmap_qenter(beg_va, m, count, 1);
}
void
pmap_qenter_noinval(vm_offset_t beg_va, vm_page_t *m, int count)
{
_pmap_qenter(beg_va, m, count, 0);
}
void
pmap_qremove(vm_offset_t beg_va, int count)
{
vm_offset_t end_va;
vm_offset_t va;
end_va = beg_va + count * PAGE_SIZE;
KKASSERT(beg_va >= KvaStart && end_va < KvaEnd);
for (va = beg_va; va < end_va; va += PAGE_SIZE) {
pt_entry_t *ptep;
ptep = vtopte(va);
atomic_swap_long(ptep, 0);
}
pmap_invalidate_range(kernel_pmap, beg_va, end_va);
}
void
pmap_qremove_quick(vm_offset_t va, int count)
{
pmap_qremove(va, count);
}
void
pmap_qremove_noinval(vm_offset_t va, int count)
{
pmap_qremove(va, count);
}
static vm_page_t
pmap_page_lookup(vm_object_t object, vm_pindex_t pindex)
{
vm_page_t m;
ASSERT_LWKT_TOKEN_HELD(vm_object_token(object));
m = vm_page_lookup_busy_wait(object, pindex, TRUE, "pplookp");
return(m);
}
void
pmap_init_thread(thread_t td)
{
td->td_pcb = (struct pcb *)(td->td_kstack + td->td_kstack_size) - 1;
td->td_savefpu = &td->td_pcb->pcb_save;
td->td_sp = (char *)td->td_pcb - 16;
}
void
pmap_init_proc(struct proc *p)
{
}
static int
pmap_unwire_pgtable(pmap_t pmap, vm_offset_t va, vm_page_t m)
{
if (vm_page_unwire_quick(m) == 0)
return 0;
vm_page_busy_wait(m, TRUE, "pmuwpt");
KASSERT(m->queue == PQ_NONE,
("_pmap_unwire_pgtable: %p->queue != PQ_NONE", m));
if (m->wire_count == 1) {
if (m->pindex >= (NUPT_TOTAL + NUPD_TOTAL)) {
pml4_entry_t *pml4;
pml4 = pmap_pml4e(pmap, va);
*pml4 = 0;
} else if (m->pindex >= NUPT_TOTAL) {
pdp_entry_t *pdp;
pdp = pmap_pdpe(pmap, va);
*pdp = 0;
} else {
pd_entry_t *pd;
pd = pmap_pde(pmap, va);
*pd = 0;
}
KKASSERT(pmap->pm_stats.resident_count > 0);
atomic_add_long(&pmap->pm_stats.resident_count, -1);
if (pmap->pm_ptphint == m)
pmap->pm_ptphint = NULL;
if (m->pindex < NUPT_TOTAL) {
vm_page_t pdpg;
pdpg = PHYS_TO_VM_PAGE(*pmap_pdpe(pmap, va) &
VPTE_FRAME);
pmap_unwire_pgtable(pmap, va, pdpg);
}
if (m->pindex >= NUPT_TOTAL &&
m->pindex < (NUPT_TOTAL + NUPD_TOTAL)) {
vm_page_t pdppg;
pdppg = PHYS_TO_VM_PAGE(*pmap_pml4e(pmap, va) &
VPTE_FRAME);
pmap_unwire_pgtable(pmap, va, pdppg);
}
vm_page_unwire(m, 0);
KKASSERT(m->wire_count == 0);
vm_page_flag_clear(m, PG_MAPPED | PG_WRITEABLE);
vm_page_flash(m);
vm_page_free(m);
return 1;
} else {
vm_page_unwire(m, 0);
vm_page_wakeup(m);
return 0;
}
}
static int
pmap_unuse_pt(pmap_t pmap, vm_offset_t va, vm_page_t mpte)
{
vm_pindex_t ptepindex;
ASSERT_LWKT_TOKEN_HELD(vm_object_token(pmap->pm_pteobj));
if (mpte == NULL) {
if (pmap == kernel_pmap)
return(0);
ptepindex = pmap_pt_pindex(va);
if (pmap->pm_ptphint &&
(pmap->pm_ptphint->pindex == ptepindex)) {
mpte = pmap->pm_ptphint;
} else {
mpte = pmap_page_lookup(pmap->pm_pteobj, ptepindex);
pmap->pm_ptphint = mpte;
vm_page_wakeup(mpte);
}
}
return pmap_unwire_pgtable(pmap, va, mpte);
}
void
pmap_pinit0(struct pmap *pmap)
{
pmap_pinit(pmap);
}
void
pmap_pinit(struct pmap *pmap)
{
vm_page_t ptdpg;
if (pmap->pm_pml4 == NULL) {
pmap->pm_pml4 = (pml4_entry_t *)
kmem_alloc_pageable(kernel_map, PAGE_SIZE,
VM_SUBSYS_PML4);
}
if (pmap->pm_pteobj == NULL)
pmap->pm_pteobj = vm_object_allocate(OBJT_DEFAULT, NUPT_TOTAL + NUPD_TOTAL + NUPDP_TOTAL + 1);
if ((ptdpg = pmap->pm_pdirm) == NULL) {
ptdpg = vm_page_grab(pmap->pm_pteobj,
NUPT_TOTAL + NUPD_TOTAL + NUPDP_TOTAL,
VM_ALLOC_NORMAL | VM_ALLOC_RETRY |
VM_ALLOC_ZERO);
pmap->pm_pdirm = ptdpg;
vm_page_flag_clear(ptdpg, PG_MAPPED | PG_WRITEABLE);
vm_page_wire(ptdpg);
vm_page_wakeup(ptdpg);
pmap_kenter((vm_offset_t)pmap->pm_pml4, VM_PAGE_TO_PHYS(ptdpg));
}
pmap->pm_count = 1;
CPUMASK_ASSZERO(pmap->pm_active);
pmap->pm_ptphint = NULL;
RB_INIT(&pmap->pm_pvroot);
spin_init(&pmap->pm_spin, "pmapinit");
bzero(&pmap->pm_stats, sizeof pmap->pm_stats);
pmap->pm_stats.resident_count = 1;
pmap->pm_stats.wired_count = 1;
}
void
pmap_puninit(pmap_t pmap)
{
vm_page_t p;
KKASSERT(CPUMASK_TESTZERO(pmap->pm_active));
if ((p = pmap->pm_pdirm) != NULL) {
KKASSERT(pmap->pm_pml4 != NULL);
pmap_kremove((vm_offset_t)pmap->pm_pml4);
vm_page_busy_wait(p, TRUE, "pgpun");
vm_page_unwire(p, 0);
vm_page_flag_clear(p, PG_MAPPED | PG_WRITEABLE);
vm_page_free(p);
pmap->pm_pdirm = NULL;
atomic_add_long(&pmap->pm_stats.wired_count, -1);
KKASSERT(pmap->pm_stats.wired_count == 0);
}
if (pmap->pm_pml4) {
kmem_free(kernel_map, (vm_offset_t)pmap->pm_pml4, PAGE_SIZE);
pmap->pm_pml4 = NULL;
}
if (pmap->pm_pteobj) {
vm_object_deallocate(pmap->pm_pteobj);
pmap->pm_pteobj = NULL;
}
}
void
pmap_pinit2(struct pmap *pmap)
{
}
static int
pmap_release_free_page(struct pmap *pmap, vm_page_t p)
{
if (vm_page_busy_try(p, TRUE)) {
vm_page_sleep_busy(p, TRUE, "pmaprl");
return 1;
}
if (p->pindex == NUPT_TOTAL + NUPD_TOTAL + NUPDP_TOTAL) {
} else if (p->pindex >= (NUPT_TOTAL + NUPD_TOTAL)) {
vm_page_t m4;
pml4_entry_t *pml4;
int idx;
m4 = vm_page_lookup(pmap->pm_pteobj,
NUPT_TOTAL + NUPD_TOTAL + NUPDP_TOTAL);
KKASSERT(m4 != NULL);
pml4 = (pml4_entry_t *)PHYS_TO_DMAP(VM_PAGE_TO_PHYS(m4));
idx = (p->pindex - (NUPT_TOTAL + NUPD_TOTAL)) % NPML4EPG;
KKASSERT(pml4[idx] != 0);
if (pml4[idx] == 0)
kprintf("pmap_release: Unmapped PML4\n");
pml4[idx] = 0;
vm_page_unwire_quick(m4);
} else if (p->pindex >= NUPT_TOTAL) {
vm_page_t m3;
pdp_entry_t *pdp;
int idx;
m3 = vm_page_lookup(pmap->pm_pteobj,
NUPT_TOTAL + NUPD_TOTAL +
(p->pindex - NUPT_TOTAL) / NPDPEPG);
KKASSERT(m3 != NULL);
pdp = (pdp_entry_t *)PHYS_TO_DMAP(VM_PAGE_TO_PHYS(m3));
idx = (p->pindex - NUPT_TOTAL) % NPDPEPG;
KKASSERT(pdp[idx] != 0);
if (pdp[idx] == 0)
kprintf("pmap_release: Unmapped PDP %d\n", idx);
pdp[idx] = 0;
vm_page_unwire_quick(m3);
} else {
vm_page_t m2;
pd_entry_t *pd;
int idx;
m2 = vm_page_lookup(pmap->pm_pteobj,
NUPT_TOTAL + p->pindex / NPDEPG);
KKASSERT(m2 != NULL);
pd = (pd_entry_t *)PHYS_TO_DMAP(VM_PAGE_TO_PHYS(m2));
idx = p->pindex % NPDEPG;
if (pd[idx] == 0)
kprintf("pmap_release: Unmapped PD %d\n", idx);
pd[idx] = 0;
vm_page_unwire_quick(m2);
}
KKASSERT(pmap->pm_stats.resident_count > 0);
atomic_add_long(&pmap->pm_stats.resident_count, -1);
if (p->wire_count > 1) {
panic("pmap_release: freeing held pt page "
"pmap=%p pg=%p dmap=%p pi=%ld {%ld,%ld,%ld}",
pmap, p, (void *)PHYS_TO_DMAP(VM_PAGE_TO_PHYS(p)),
p->pindex, NUPT_TOTAL, NUPD_TOTAL, NUPDP_TOTAL);
}
if (pmap->pm_ptphint == p)
pmap->pm_ptphint = NULL;
if (p->pindex == NUPT_TOTAL + NUPD_TOTAL + NUPDP_TOTAL) {
bzero(pmap->pm_pml4, PAGE_SIZE);
vm_page_wakeup(p);
} else {
vm_page_unwire(p, 0);
vm_page_flag_clear(p, PG_MAPPED | PG_WRITEABLE);
vm_page_free(p);
atomic_add_long(&pmap->pm_stats.wired_count, -1);
}
return 0;
}
static vm_page_t
_pmap_allocpte(pmap_t pmap, vm_pindex_t ptepindex)
{
vm_page_t m;
vm_page_t pm;
vm_pindex_t pindex;
pt_entry_t *ptep;
pt_entry_t data;
m = vm_page_grab(pmap->pm_pteobj, ptepindex,
VM_ALLOC_NORMAL | VM_ALLOC_ZERO | VM_ALLOC_RETRY);
if (m->wire_count != 0)
return m;
vm_page_wire(m);
vm_page_unqueue(m);
atomic_add_long(&pmap->pm_stats.resident_count, 1);
vm_page_flag_set(m, PG_MAPPED | PG_WRITEABLE);
data = VM_PAGE_TO_PHYS(m) |
VPTE_RW | VPTE_V | VPTE_U | VPTE_A | VPTE_M | VPTE_WIRED;
atomic_add_long(&pmap->pm_stats.wired_count, 1);
if (ptepindex >= (NUPT_TOTAL + NUPD_TOTAL)) {
pindex = ptepindex - (NUPT_TOTAL + NUPD_TOTAL);
pindex &= (NUPDP_TOTAL - 1);
ptep = (pt_entry_t *)pmap->pm_pml4;
pm = NULL;
} else if (ptepindex >= NUPT_TOTAL) {
pindex = (ptepindex - NUPT_TOTAL) >> NPDPEPGSHIFT;
pindex += NUPT_TOTAL + NUPD_TOTAL;
pm = _pmap_allocpte(pmap, pindex);
pindex = (ptepindex - NUPT_TOTAL) & (NPDPEPG - 1);
ptep = (void *)PHYS_TO_DMAP(pm->phys_addr);
} else {
pindex = ptepindex >> NPDPEPGSHIFT;
pindex += NUPT_TOTAL;
pm = _pmap_allocpte(pmap, pindex);
pindex = ptepindex & (NPTEPG - 1);
ptep = (void *)PHYS_TO_DMAP(pm->phys_addr);
}
ptep += pindex;
data = atomic_swap_long(ptep, data);
if (pm) {
vm_page_wire_quick(pm);
vm_page_wakeup(pm);
}
pmap->pm_ptphint = pm;
return m;
}
static vm_page_t
pmap_allocpte(pmap_t pmap, vm_offset_t va)
{
vm_pindex_t ptepindex;
vm_page_t m;
ASSERT_LWKT_TOKEN_HELD(vm_object_token(pmap->pm_pteobj));
ptepindex = pmap_pt_pindex(va);
m = _pmap_allocpte(pmap, ptepindex);
return m;
}
static int pmap_release_callback(struct vm_page *p, void *data);
void
pmap_release(struct pmap *pmap)
{
vm_object_t object = pmap->pm_pteobj;
struct rb_vm_page_scan_info info;
KKASSERT(pmap != kernel_pmap);
#if defined(DIAGNOSTIC)
if (object->ref_count != 1)
panic("pmap_release: pteobj reference count != 1");
#endif
info.pmap = pmap;
info.object = object;
KASSERT(CPUMASK_TESTZERO(pmap->pm_active),
("pmap %p still active! %016jx",
pmap,
(uintmax_t)CPUMASK_LOWMASK(pmap->pm_active)));
vm_object_hold(object);
do {
info.error = 0;
info.mpte = NULL;
info.limit = object->generation;
vm_page_rb_tree_RB_SCAN(&object->rb_memq, NULL,
pmap_release_callback, &info);
if (info.error == 0 && info.mpte) {
if (pmap_release_free_page(pmap, info.mpte))
info.error = 1;
}
} while (info.error);
pmap->pm_ptphint = NULL;
KASSERT((pmap->pm_stats.wired_count == (pmap->pm_pdirm != NULL)),
("pmap_release: dangling count %p %ld",
pmap, pmap->pm_stats.wired_count));
vm_object_drop(object);
}
static int
pmap_release_callback(struct vm_page *p, void *data)
{
struct rb_vm_page_scan_info *info = data;
if (p->pindex == NUPT_TOTAL + NUPD_TOTAL + NUPDP_TOTAL) {
info->mpte = p;
return(0);
}
if (pmap_release_free_page(info->pmap, p)) {
info->error = 1;
return(-1);
}
if (info->object->generation != info->limit) {
info->error = 1;
return(-1);
}
return(0);
}
void
pmap_growkernel(vm_offset_t kstart, vm_offset_t kend)
{
vm_offset_t addr;
vm_paddr_t paddr;
vm_offset_t ptppaddr;
vm_page_t nkpg;
pd_entry_t *pde, newpdir;
pdp_entry_t newpdp;
addr = kend;
vm_object_hold(&kptobj);
if (kernel_vm_end == 0) {
kernel_vm_end = KvaStart;
nkpt = 0;
while ((*pmap_pde(kernel_pmap, kernel_vm_end) & VPTE_V) != 0) {
kernel_vm_end =
rounddown2(kernel_vm_end + PAGE_SIZE * NPTEPG,
PAGE_SIZE * NPTEPG);
nkpt++;
if (kernel_vm_end - 1 >= vm_map_max(kernel_map)) {
kernel_vm_end = vm_map_max(kernel_map);
break;
}
}
}
addr = roundup2(addr, PAGE_SIZE * NPTEPG);
if (addr - 1 >= vm_map_max(kernel_map))
addr = vm_map_max(kernel_map);
while (kernel_vm_end < addr) {
pde = pmap_pde(kernel_pmap, kernel_vm_end);
if (pde == NULL) {
nkpg = vm_page_alloc(&kptobj, nkpt,
VM_ALLOC_NORMAL |
VM_ALLOC_SYSTEM |
VM_ALLOC_INTERRUPT);
if (nkpg == NULL) {
panic("pmap_growkernel: no memory to "
"grow kernel");
}
paddr = VM_PAGE_TO_PHYS(nkpg);
pmap_zero_page(paddr);
newpdp = (pdp_entry_t)(paddr |
VPTE_V | VPTE_RW | VPTE_U |
VPTE_A | VPTE_M | VPTE_WIRED);
*pmap_pdpe(kernel_pmap, kernel_vm_end) = newpdp;
atomic_add_long(&kernel_pmap->pm_stats.wired_count, 1);
nkpt++;
continue;
}
if ((*pde & VPTE_V) != 0) {
kernel_vm_end =
rounddown2(kernel_vm_end + PAGE_SIZE * NPTEPG,
PAGE_SIZE * NPTEPG);
if (kernel_vm_end - 1 >= vm_map_max(kernel_map)) {
kernel_vm_end = vm_map_max(kernel_map);
break;
}
continue;
}
nkpg = vm_page_alloc(&kptobj, nkpt,
VM_ALLOC_NORMAL |
VM_ALLOC_SYSTEM |
VM_ALLOC_INTERRUPT);
if (nkpg == NULL)
panic("pmap_growkernel: no memory to grow kernel");
vm_page_wire(nkpg);
ptppaddr = VM_PAGE_TO_PHYS(nkpg);
pmap_zero_page(ptppaddr);
newpdir = (pd_entry_t)(ptppaddr |
VPTE_V | VPTE_RW | VPTE_U |
VPTE_A | VPTE_M | VPTE_WIRED);
*pmap_pde(kernel_pmap, kernel_vm_end) = newpdir;
atomic_add_long(&kernel_pmap->pm_stats.wired_count, 1);
nkpt++;
kernel_vm_end =
rounddown2(kernel_vm_end + PAGE_SIZE * NPTEPG,
PAGE_SIZE * NPTEPG);
if (kernel_vm_end - 1 >= vm_map_max(kernel_map)) {
kernel_vm_end = vm_map_max(kernel_map);
break;
}
}
vm_object_drop(&kptobj);
}
void
pmap_reference(pmap_t pmap)
{
if (pmap)
atomic_add_int(&pmap->pm_count, 1);
}
void
cpu_vmspace_alloc(struct vmspace *vm)
{
int r;
void *rp;
vpte_t vpte;
#define USER_SIZE (VM_MAX_USER_ADDRESS - VM_MIN_USER_ADDRESS)
if (vmspace_create(&vm->vm_pmap, 0, NULL) < 0)
panic("vmspace_create() failed");
rp = vmspace_mmap(&vm->vm_pmap, VM_MIN_USER_ADDRESS, USER_SIZE,
PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_FILE|MAP_SHARED|MAP_VPAGETABLE|MAP_FIXED,
MemImageFd, 0);
if (rp == MAP_FAILED)
panic("vmspace_mmap: failed");
vmspace_mcontrol(&vm->vm_pmap, VM_MIN_USER_ADDRESS, USER_SIZE,
MADV_NOSYNC, 0);
vpte = VM_PAGE_TO_PHYS(vmspace_pmap(vm)->pm_pdirm) |
VPTE_RW | VPTE_V | VPTE_U;
r = vmspace_mcontrol(&vm->vm_pmap, VM_MIN_USER_ADDRESS, USER_SIZE,
MADV_SETMAP, vpte);
if (r < 0)
panic("vmspace_mcontrol: failed");
}
void
cpu_vmspace_free(struct vmspace *vm)
{
if (vmspace_destroy(&vm->vm_pmap) < 0)
panic("vmspace_destroy() failed");
}
static __inline void
free_pv_entry(pv_entry_t pv)
{
atomic_add_long(&pv_entry_count, -1);
zfree(pvzone, pv);
}
static pv_entry_t
get_pv_entry(void)
{
atomic_add_long(&pv_entry_count, 1);
if (pv_entry_high_water &&
(pv_entry_count > pv_entry_high_water) &&
atomic_swap_int(&pmap_pagedaemon_waken, 1) == 0) {
wakeup(&vm_pages_needed);
}
return zalloc(pvzone);
}
void
pmap_collect(void)
{
int i;
vm_page_t m;
static int warningdone=0;
if (pmap_pagedaemon_waken == 0)
return;
pmap_pagedaemon_waken = 0;
if (warningdone < 5) {
kprintf("pmap_collect: collecting pv entries -- "
"suggest increasing PMAP_SHPGPERPROC\n");
warningdone++;
}
for (i = 0; i < vm_page_array_size; i++) {
m = &vm_page_array[i];
if (m->wire_count || m->hold_count)
continue;
if (vm_page_busy_try(m, TRUE) == 0) {
if (m->wire_count == 0 && m->hold_count == 0) {
pmap_remove_all(m);
}
vm_page_wakeup(m);
}
}
}
static int
pmap_remove_entry(struct pmap *pmap, vm_page_t m, vm_offset_t va)
{
pv_entry_t pv;
int rtval;
vm_page_spin_lock(m);
pv = pv_entry_rb_tree_RB_LOOKUP(&pmap->pm_pvroot, va);
rtval = 0;
if (pv) {
TAILQ_REMOVE(&m->md.pv_list, pv, pv_list);
if (TAILQ_EMPTY(&m->md.pv_list))
vm_page_flag_clear(m, PG_MAPPED | PG_WRITEABLE);
m->md.pv_list_count--;
KKASSERT(m->md.pv_list_count >= 0);
pv_entry_rb_tree_RB_REMOVE(&pmap->pm_pvroot, pv);
atomic_add_int(&pmap->pm_generation, 1);
vm_page_spin_unlock(m);
rtval = pmap_unuse_pt(pmap, va, pv->pv_ptem);
free_pv_entry(pv);
} else {
vm_page_spin_unlock(m);
kprintf("pmap_remove_entry: could not find "
"pmap=%p m=%p va=%016jx\n",
pmap, m, va);
}
return rtval;
}
static void
pmap_insert_entry(pmap_t pmap, vm_offset_t va, vm_page_t mpte, vm_page_t m,
pv_entry_t pv)
{
pv->pv_va = va;
pv->pv_pmap = pmap;
pv->pv_ptem = mpte;
m->md.pv_list_count++;
TAILQ_INSERT_TAIL(&m->md.pv_list, pv, pv_list);
pv = pv_entry_rb_tree_RB_INSERT(&pmap->pm_pvroot, pv);
vm_page_flag_set(m, PG_MAPPED);
KKASSERT(pv == NULL);
}
static int
pmap_remove_pte(struct pmap *pmap, pt_entry_t *ptq, pt_entry_t oldpte,
vm_offset_t va)
{
vm_page_t m;
int error;
if (ptq)
oldpte = pmap_inval_loadandclear(ptq, pmap, va);
KKASSERT(pmap->pm_stats.wired_count >= 0);
#if 0
if (oldpte & PG_G)
cpu_invlpg((void *)va);
#endif
KKASSERT(pmap->pm_stats.resident_count > 0);
atomic_add_long(&pmap->pm_stats.resident_count, -1);
if (oldpte & VPTE_MANAGED) {
m = PHYS_TO_VM_PAGE(oldpte);
if (oldpte & VPTE_M) {
#if defined(PMAP_DIAGNOSTIC)
if (pmap_nw_modified(oldpte)) {
kprintf("pmap_remove: modified page not "
"writable: va: 0x%lx, pte: 0x%lx\n",
va, oldpte);
}
#endif
pmap_track_modified(pmap, va);
vm_page_dirty(m);
}
if (oldpte & VPTE_A)
vm_page_flag_set(m, PG_REFERENCED);
if (oldpte & VPTE_WIRED) {
atomic_add_long(&pmap->pm_stats.wired_count, -1);
vm_page_unwire(m, -1);
}
error = pmap_remove_entry(pmap, m, va);
} else {
if (oldpte & VPTE_WIRED)
atomic_add_long(&pmap->pm_stats.wired_count, -1);
error = pmap_unuse_pt(pmap, va, NULL);
}
return error;
}
static void
pmap_remove_page(struct pmap *pmap, vm_offset_t va)
{
pt_entry_t *pte;
pte = pmap_pte(pmap, va);
if (pte == NULL)
return;
if ((*pte & VPTE_V) == 0)
return;
pmap_remove_pte(pmap, pte, 0, va);
}
void
pmap_remove(struct pmap *pmap, vm_offset_t sva, vm_offset_t eva)
{
vm_offset_t va_next;
pml4_entry_t *pml4e;
pdp_entry_t *pdpe;
pd_entry_t ptpaddr, *pde;
pt_entry_t *pte;
vm_page_t pt_m;
if (pmap == NULL)
return;
vm_object_hold(pmap->pm_pteobj);
KKASSERT(pmap->pm_stats.resident_count >= 0);
if (pmap->pm_stats.resident_count == 0) {
vm_object_drop(pmap->pm_pteobj);
return;
}
if (sva + PAGE_SIZE == eva) {
pde = pmap_pde(pmap, sva);
if (pde && (*pde & VPTE_PS) == 0) {
pmap_remove_page(pmap, sva);
vm_object_drop(pmap->pm_pteobj);
return;
}
}
for (; sva < eva; sva = va_next) {
pml4e = pmap_pml4e(pmap, sva);
if ((*pml4e & VPTE_V) == 0) {
va_next = (sva + NBPML4) & ~PML4MASK;
if (va_next < sva)
va_next = eva;
continue;
}
pdpe = pmap_pml4e_to_pdpe(pml4e, sva);
if ((*pdpe & VPTE_V) == 0) {
va_next = (sva + NBPDP) & ~PDPMASK;
if (va_next < sva)
va_next = eva;
continue;
}
va_next = (sva + NBPDR) & ~PDRMASK;
if (va_next < sva)
va_next = eva;
pde = pmap_pdpe_to_pde(pdpe, sva);
ptpaddr = *pde;
if (ptpaddr == 0)
continue;
if ((ptpaddr & VPTE_PS) != 0) {
KKASSERT(*pde != 0);
pmap_inval_pde(pde, pmap, sva);
atomic_add_long(&pmap->pm_stats.resident_count,
-NBPDR / PAGE_SIZE);
continue;
}
if (va_next > eva)
va_next = eva;
pt_m = pmap_hold_pt_page(pde, sva);
for (pte = pmap_pde_to_pte(pde, sva); sva != va_next; pte++,
sva += PAGE_SIZE) {
if (*pte) {
if (pmap_remove_pte(pmap, pte, 0, sva))
break;
}
}
vm_page_unhold(pt_m);
}
vm_object_drop(pmap->pm_pteobj);
}
static void
pmap_remove_all(vm_page_t m)
{
pt_entry_t *pte, tpte;
pv_entry_t pv;
vm_object_t pmobj;
pmap_t pmap;
#if defined(PMAP_DIAGNOSTIC)
if (!pmap_initialized || (m->flags & PG_FICTITIOUS)) {
panic("pmap_page_protect: illegal for unmanaged page, va: 0x%08llx", (long long)VM_PAGE_TO_PHYS(m));
}
#endif
restart:
vm_page_spin_lock(m);
while ((pv = TAILQ_FIRST(&m->md.pv_list)) != NULL) {
pmap = pv->pv_pmap;
pmobj = pmap->pm_pteobj;
if (vm_object_hold_try(pmobj) == 0) {
refcount_acquire(&pmobj->hold_count);
vm_page_spin_unlock(m);
vm_object_lock(pmobj);
vm_page_spin_lock(m);
if (pv != TAILQ_FIRST(&m->md.pv_list) ||
pmap != pv->pv_pmap ||
pmobj != pmap->pm_pteobj) {
vm_page_spin_unlock(m);
vm_object_drop(pmobj);
goto restart;
}
}
KKASSERT(pmap->pm_stats.resident_count > 0);
atomic_add_long(&pmap->pm_stats.resident_count, -1);
pte = pmap_pte(pmap, pv->pv_va);
KKASSERT(pte != NULL);
tpte = pmap_inval_loadandclear(pte, pmap, pv->pv_va);
if (tpte & VPTE_A)
vm_page_flag_set(m, PG_REFERENCED);
if (tpte & VPTE_M) {
#if defined(PMAP_DIAGNOSTIC)
if (pmap_nw_modified(tpte)) {
kprintf(
"pmap_remove_all: modified page not writable: va: 0x%lx, pte: 0x%lx\n",
pv->pv_va, tpte);
}
#endif
pmap_track_modified(pmap, pv->pv_va);
vm_page_dirty(m);
}
if (tpte & VPTE_WIRED) {
atomic_add_long(&pmap->pm_stats.wired_count, -1);
vm_page_unwire(m, -1);
}
KKASSERT(pmap->pm_stats.wired_count >= 0);
TAILQ_REMOVE(&m->md.pv_list, pv, pv_list);
if (TAILQ_EMPTY(&m->md.pv_list))
vm_page_flag_clear(m, PG_MAPPED | PG_WRITEABLE);
m->md.pv_list_count--;
KKASSERT(m->md.pv_list_count >= 0);
pv_entry_rb_tree_RB_REMOVE(&pmap->pm_pvroot, pv);
atomic_add_int(&pmap->pm_generation, 1);
vm_page_spin_unlock(m);
pmap_unuse_pt(pmap, pv->pv_va, pv->pv_ptem);
free_pv_entry(pv);
vm_object_drop(pmobj);
vm_page_spin_lock(m);
}
KKASSERT((m->flags & (PG_MAPPED|PG_WRITEABLE)) == 0);
vm_page_spin_unlock(m);
}
void
pmap_remove_specific(pmap_t pmap, vm_page_t m)
{
pt_entry_t *pte, tpte;
pv_entry_t pv;
vm_object_hold(pmap->pm_pteobj);
again:
vm_page_spin_lock(m);
TAILQ_FOREACH(pv, &m->md.pv_list, pv_list) {
if (pv->pv_pmap != pmap)
continue;
KKASSERT(pmap->pm_stats.resident_count > 0);
atomic_add_long(&pmap->pm_stats.resident_count, -1);
pte = pmap_pte(pmap, pv->pv_va);
KKASSERT(pte != NULL);
tpte = pmap_inval_loadandclear(pte, pmap, pv->pv_va);
if (tpte & VPTE_A)
vm_page_flag_set(m, PG_REFERENCED);
if (tpte & VPTE_M) {
pmap_track_modified(pmap, pv->pv_va);
vm_page_dirty(m);
}
if (tpte & VPTE_WIRED) {
atomic_add_long(&pmap->pm_stats.wired_count, -1);
vm_page_unwire(m, -1);
}
KKASSERT(pmap->pm_stats.wired_count >= 0);
TAILQ_REMOVE(&m->md.pv_list, pv, pv_list);
pv_entry_rb_tree_RB_REMOVE(&pmap->pm_pvroot, pv);
atomic_add_int(&pmap->pm_generation, 1);
m->md.pv_list_count--;
KKASSERT(m->md.pv_list_count >= 0);
if (TAILQ_EMPTY(&m->md.pv_list))
vm_page_flag_clear(m, PG_MAPPED | PG_WRITEABLE);
pmap_unuse_pt(pmap, pv->pv_va, pv->pv_ptem);
vm_page_spin_unlock(m);
free_pv_entry(pv);
goto again;
}
vm_page_spin_unlock(m);
vm_object_drop(pmap->pm_pteobj);
}
void
pmap_protect(pmap_t pmap, vm_offset_t sva, vm_offset_t eva, vm_prot_t prot)
{
vm_offset_t va_next;
pml4_entry_t *pml4e;
pdp_entry_t *pdpe;
pd_entry_t ptpaddr, *pde;
pt_entry_t *pte;
vm_page_t pt_m;
if (pmap == NULL)
return;
if ((prot & (VM_PROT_READ | VM_PROT_EXECUTE)) == VM_PROT_NONE) {
pmap_remove(pmap, sva, eva);
return;
}
if (prot & VM_PROT_WRITE)
return;
vm_object_hold(pmap->pm_pteobj);
for (; sva < eva; sva = va_next) {
pml4e = pmap_pml4e(pmap, sva);
if ((*pml4e & VPTE_V) == 0) {
va_next = (sva + NBPML4) & ~PML4MASK;
if (va_next < sva)
va_next = eva;
continue;
}
pdpe = pmap_pml4e_to_pdpe(pml4e, sva);
if ((*pdpe & VPTE_V) == 0) {
va_next = (sva + NBPDP) & ~PDPMASK;
if (va_next < sva)
va_next = eva;
continue;
}
va_next = (sva + NBPDR) & ~PDRMASK;
if (va_next < sva)
va_next = eva;
pde = pmap_pdpe_to_pde(pdpe, sva);
ptpaddr = *pde;
#if 0
if ((ptpaddr & VPTE_PS) != 0) {
pmap_clean_pde(pde, pmap, sva);
atomic_add_long(&pmap->pm_stats.resident_count,
-NBPDR / PAGE_SIZE);
continue;
}
#endif
if (ptpaddr == 0)
continue;
if (va_next > eva)
va_next = eva;
pt_m = pmap_hold_pt_page(pde, sva);
for (pte = pmap_pde_to_pte(pde, sva); sva != va_next; pte++,
sva += PAGE_SIZE) {
pmap_track_modified(pmap, sva);
pmap_clean_pte(pte, pmap, sva, NULL);
}
vm_page_unhold(pt_m);
}
vm_object_drop(pmap->pm_pteobj);
}
void
pmap_enter(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot,
boolean_t wired, vm_map_entry_t entry __unused)
{
vm_paddr_t pa;
pv_entry_t pv;
pt_entry_t *pte;
pt_entry_t origpte, newpte;
vm_paddr_t opa;
vm_page_t mpte;
if (pmap == NULL)
return;
va = trunc_page(va);
vm_object_hold(pmap->pm_pteobj);
if (pmap == kernel_pmap) {
mpte = NULL;
pte = vtopte(va);
} else {
mpte = pmap_allocpte(pmap, va);
pte = (void *)PHYS_TO_DMAP(mpte->phys_addr);
pte += pmap_pte_index(va);
}
pa = VM_PAGE_TO_PHYS(m);
origpte = pmap_inval_loadandclear(pte, pmap, va);
opa = origpte & VPTE_FRAME;
if (origpte & VPTE_PS)
panic("pmap_enter: attempted pmap_enter on 2MB page");
if ((origpte & (VPTE_MANAGED|VPTE_M)) == (VPTE_MANAGED|VPTE_M)) {
vm_page_t om;
pmap_track_modified(pmap, va);
om = PHYS_TO_VM_PAGE(opa);
vm_page_dirty(om);
}
if (origpte && (opa == pa)) {
if (wired && ((origpte & VPTE_WIRED) == 0)) {
atomic_add_long(&pmap->pm_stats.wired_count, 1);
vm_page_wire(m);
} else if (!wired && (origpte & VPTE_WIRED)) {
atomic_add_long(&pmap->pm_stats.wired_count, -1);
vm_page_unwire(m, -1);
}
if (origpte & VPTE_MANAGED) {
pa |= VPTE_MANAGED;
KKASSERT(m->flags & PG_MAPPED);
KKASSERT((m->flags & PG_FICTITIOUS) == 0);
} else {
KKASSERT((m->flags & PG_FICTITIOUS));
}
vm_page_spin_lock(m);
goto validate;
}
if (mpte)
vm_page_wire_quick(mpte);
if (opa) {
int err;
err = pmap_remove_pte(pmap, NULL, origpte, va);
origpte = 0;
if (err)
panic("pmap_enter: pte vanished, va: 0x%lx", va);
}
if (pmap_initialized) {
if ((m->flags & PG_FICTITIOUS) == 0) {
pv = get_pv_entry();
vm_page_spin_lock(m);
pmap_insert_entry(pmap, va, mpte, m, pv);
pa |= VPTE_MANAGED;
} else {
vm_page_spin_lock(m);
}
} else {
vm_page_spin_lock(m);
}
atomic_add_long(&pmap->pm_stats.resident_count, 1);
if (wired) {
atomic_add_long(&pmap->pm_stats.wired_count, 1);
vm_page_wire(m);
}
validate:
newpte = (pt_entry_t)(pa | pte_prot(pmap, prot) | VPTE_V | VPTE_U);
newpte |= VPTE_A;
if (wired)
newpte |= VPTE_WIRED;
newpte |= VPTE_U;
if (newpte & VPTE_RW)
vm_page_flag_set(m, PG_WRITEABLE);
KKASSERT((newpte & VPTE_MANAGED) == 0 || (m->flags & PG_MAPPED));
origpte = atomic_swap_long(pte, newpte);
if (origpte & VPTE_M) {
kprintf("pmap [M] race @ %016jx\n", va);
atomic_set_long(pte, VPTE_M);
}
vm_page_spin_unlock(m);
if (mpte)
vm_page_wakeup(mpte);
vm_object_drop(pmap->pm_pteobj);
}
void *
pmap_kenter_temporary(vm_paddr_t pa, long i)
{
pmap_kenter_quick(crashdumpmap + (i * PAGE_SIZE), pa);
return ((void *)crashdumpmap);
}
#define MAX_INIT_PT (96)
static int pmap_object_init_pt_callback(vm_page_t p, void *data);
void
pmap_object_init_pt(pmap_t pmap, vm_map_entry_t entry,
vm_offset_t addr, vm_size_t size, int limit)
{
vm_prot_t prot = entry->protection;
vm_object_t object = entry->ba.object;
vm_pindex_t pindex = atop(entry->ba.offset + (addr - entry->ba.start));
struct rb_vm_page_scan_info info;
struct lwp *lp;
vm_size_t psize;
if ((prot & VM_PROT_READ) == 0 || pmap == NULL || object == NULL)
return;
lp = curthread->td_lwp;
if (lp == NULL || pmap != vmspace_pmap(lp->lwp_vmspace))
return;
psize = x86_64_btop(size);
if ((object->type != OBJT_VNODE) ||
((limit & COWF_PREFAULT_PARTIAL) && (psize > MAX_INIT_PT) &&
(object->resident_page_count > MAX_INIT_PT))) {
return;
}
if (psize + pindex > object->size) {
if (object->size < pindex)
return;
psize = object->size - pindex;
}
if (psize == 0)
return;
info.start_pindex = pindex;
info.end_pindex = pindex + psize - 1;
info.limit = limit;
info.mpte = NULL;
info.addr = addr;
info.pmap = pmap;
info.entry = entry;
vm_object_hold_shared(object);
vm_page_rb_tree_RB_SCAN(&object->rb_memq, rb_vm_page_scancmp,
pmap_object_init_pt_callback, &info);
vm_object_drop(object);
}
static
int
pmap_object_init_pt_callback(vm_page_t p, void *data)
{
struct rb_vm_page_scan_info *info = data;
vm_pindex_t rel_index;
if ((info->limit & COWF_PREFAULT_MADVISE) &&
vmstats.v_free_count < vmstats.v_free_reserved) {
return(-1);
}
if (p->flags & PG_MARKER)
return 0;
if (vm_page_busy_try(p, TRUE))
return 0;
if (((p->valid & VM_PAGE_BITS_ALL) == VM_PAGE_BITS_ALL) &&
(p->flags & PG_FICTITIOUS) == 0) {
if ((p->queue - p->pc) == PQ_CACHE)
vm_page_deactivate(p);
rel_index = p->pindex - info->start_pindex;
pmap_enter(info->pmap, info->addr + x86_64_ptob(rel_index), p,
VM_PROT_READ, FALSE, info->entry);
}
vm_page_wakeup(p);
return(0);
}
int
pmap_prefault_ok(pmap_t pmap, vm_offset_t addr)
{
pt_entry_t *pte;
pd_entry_t *pde;
int ret;
vm_object_hold(pmap->pm_pteobj);
pde = pmap_pde(pmap, addr);
if (pde == NULL || *pde == 0) {
ret = 0;
} else {
pte = pmap_pde_to_pte(pde, addr);
ret = (*pte) ? 0 : 1;
}
vm_object_drop(pmap->pm_pteobj);
return (ret);
}
vm_page_t
pmap_unwire(pmap_t pmap, vm_offset_t *pva)
{
pt_entry_t *pte;
vm_paddr_t pa;
vm_page_t m;
vm_offset_t va = *pva;
*pva += PAGE_SIZE;
if (pmap == NULL)
return NULL;
vm_object_hold(pmap->pm_pteobj);
pte = pmap_pte(pmap, va);
if ((*pte & VPTE_V) == 0) {
*pva = pmap_nextva(pmap, va);
vm_object_drop(pmap->pm_pteobj);
return NULL;
}
if (pmap_pte_w(pte)) {
atomic_add_long(&pmap->pm_stats.wired_count, -1);
atomic_clear_long(pte, VPTE_WIRED);
pa = *pte & VPTE_FRAME;
m = PHYS_TO_VM_PAGE(pa);
} else {
m = NULL;
}
vm_object_drop(pmap->pm_pteobj);
return m;
}
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)
{
return;
}
void
pmap_zero_page(vm_paddr_t phys)
{
vm_offset_t va = PHYS_TO_DMAP(phys);
bzero((void *)va, PAGE_SIZE);
}
void
pmap_zero_page_area(vm_paddr_t phys, int off, int size)
{
vm_offset_t virt = PHYS_TO_DMAP(phys);
bzero((char *)virt + off, size);
}
void
pmap_copy_page(vm_paddr_t src, vm_paddr_t dst)
{
vm_offset_t src_virt, dst_virt;
src_virt = PHYS_TO_DMAP(src);
dst_virt = PHYS_TO_DMAP(dst);
bcopy((void *)src_virt, (void *)dst_virt, PAGE_SIZE);
}
void
pmap_copy_page_frag(vm_paddr_t src, vm_paddr_t dst, size_t bytes)
{
vm_offset_t src_virt, dst_virt;
src_virt = PHYS_TO_DMAP(src);
dst_virt = PHYS_TO_DMAP(dst);
bcopy((char *)src_virt + (src & PAGE_MASK),
(char *)dst_virt + (dst & PAGE_MASK),
bytes);
}
void
pmap_remove_pages(pmap_t pmap, vm_offset_t sva, vm_offset_t eva)
{
pmap_remove(pmap, sva, eva);
#if 0
pt_entry_t *pte, tpte;
pv_entry_t pv, npv;
vm_page_t m;
int save_generation;
if (pmap->pm_pteobj)
vm_object_hold(pmap->pm_pteobj);
pmap_invalidate_range(pmap, sva, eva);
for (pv = TAILQ_FIRST(&pmap->pm_pvlist); pv; pv = npv) {
if (pv->pv_va >= eva || pv->pv_va < sva) {
npv = TAILQ_NEXT(pv, pv_plist);
continue;
}
KKASSERT(pmap == pv->pv_pmap);
pte = pmap_pte(pmap, pv->pv_va);
if (*pte & VPTE_WIRED) {
npv = TAILQ_NEXT(pv, pv_plist);
continue;
}
tpte = pmap_inval_loadandclear(pte, pmap, pv->pv_va);
m = PHYS_TO_VM_PAGE(tpte & VPTE_FRAME);
vm_page_spin_lock(m);
KASSERT(m < &vm_page_array[vm_page_array_size],
("pmap_remove_pages: bad tpte %lx", tpte));
KKASSERT(pmap->pm_stats.resident_count > 0);
atomic_add_long(&pmap->pm_stats.resident_count, -1);
if (tpte & VPTE_M) {
vm_page_dirty(m);
}
npv = TAILQ_NEXT(pv, pv_plist);
TAILQ_REMOVE(&pmap->pm_pvlist, pv, pv_plist);
atomic_add_int(&pmap->pm_generation, 1);
save_generation = pmap->pm_generation;
m->md.pv_list_count--;
TAILQ_REMOVE(&m->md.pv_list, pv, pv_list);
if (TAILQ_EMPTY(&m->md.pv_list))
vm_page_flag_clear(m, PG_MAPPED | PG_WRITEABLE);
vm_page_spin_unlock(m);
pmap_unuse_pt(pmap, pv->pv_va, pv->pv_ptem);
free_pv_entry(pv);
if (save_generation != pmap->pm_generation) {
kprintf("Warning: pmap_remove_pages race-A avoided\n");
npv = TAILQ_FIRST(&pmap->pm_pvlist);
}
}
if (pmap->pm_pteobj)
vm_object_drop(pmap->pm_pteobj);
pmap_remove(pmap, sva, eva);
#endif
}
static boolean_t
pmap_testbit(vm_page_t m, int bit)
{
pv_entry_t pv;
pt_entry_t *pte;
if (!pmap_initialized || (m->flags & PG_FICTITIOUS))
return FALSE;
if (TAILQ_FIRST(&m->md.pv_list) == NULL)
return FALSE;
vm_page_spin_lock(m);
TAILQ_FOREACH(pv, &m->md.pv_list, pv_list) {
if (bit & (VPTE_A|VPTE_M))
pmap_track_modified(pv->pv_pmap, pv->pv_va);
#if defined(PMAP_DIAGNOSTIC)
if (pv->pv_pmap == NULL) {
kprintf("Null pmap (tb) at va: 0x%lx\n", pv->pv_va);
continue;
}
#endif
pte = pmap_pte(pv->pv_pmap, pv->pv_va);
if (*pte & bit) {
vm_page_spin_unlock(m);
return TRUE;
}
}
vm_page_spin_unlock(m);
return (FALSE);
}
static __inline void
pmap_clearbit(vm_page_t m, int bit)
{
pv_entry_t pv;
pt_entry_t *pte;
pt_entry_t pbits;
vm_object_t pmobj;
pmap_t pmap;
if (!pmap_initialized || (m->flags & PG_FICTITIOUS)) {
if (bit == VPTE_RW)
vm_page_flag_clear(m, PG_WRITEABLE);
return;
}
restart:
vm_page_spin_lock(m);
TAILQ_FOREACH(pv, &m->md.pv_list, pv_list) {
pmap = pv->pv_pmap;
pmobj = pmap->pm_pteobj;
if (vm_object_hold_try(pmobj) == 0) {
refcount_acquire(&pmobj->hold_count);
vm_page_spin_unlock(m);
vm_object_lock(pmobj);
vm_object_drop(pmobj);
goto restart;
}
if (bit == VPTE_RW) {
pmap_track_modified(pv->pv_pmap, pv->pv_va);
}
#if defined(PMAP_DIAGNOSTIC)
if (pv->pv_pmap == NULL) {
kprintf("Null pmap (cb) at va: 0x%lx\n", pv->pv_va);
vm_object_drop(pmobj);
continue;
}
#endif
pte = pmap_pte(pv->pv_pmap, pv->pv_va);
if (*pte & bit) {
if (bit == VPTE_RW) {
pmap_track_modified(pv->pv_pmap, pv->pv_va);
pbits = pmap_clean_pte(pte, pv->pv_pmap,
pv->pv_va, m);
} else if (bit == VPTE_M) {
atomic_clear_long(pte, VPTE_M);
if (*pte & VPTE_RW) {
pmap_invalidate_range(pv->pv_pmap,
pv->pv_va,
pv->pv_va + PAGE_SIZE);
}
} else if ((bit & (VPTE_RW|VPTE_M)) ==
(VPTE_RW|VPTE_M)) {
pmap_track_modified(pv->pv_pmap, pv->pv_va);
pmap_clean_pte(pte, pv->pv_pmap, pv->pv_va, m);
panic("shouldn't be called");
} else {
atomic_clear_long(pte, bit);
}
}
vm_object_drop(pmobj);
}
if (bit == VPTE_RW)
vm_page_flag_clear(m, PG_WRITEABLE);
vm_page_spin_unlock(m);
}
void
pmap_page_protect(vm_page_t m, vm_prot_t prot)
{
if ((prot & VM_PROT_WRITE) == 0) {
if (prot & (VM_PROT_READ | VM_PROT_EXECUTE)) {
pmap_clearbit(m, VPTE_RW);
} else {
pmap_remove_all(m);
}
}
}
vm_paddr_t
pmap_phys_address(vm_pindex_t ppn)
{
return (x86_64_ptob(ppn));
}
int
pmap_ts_referenced(vm_page_t m)
{
pv_entry_t pv, pvf, pvn;
pt_entry_t *pte;
int rtval = 0;
if (!pmap_initialized || (m->flags & PG_FICTITIOUS))
return (rtval);
vm_page_spin_lock(m);
if ((pv = TAILQ_FIRST(&m->md.pv_list)) != NULL) {
pvf = pv;
do {
pvn = TAILQ_NEXT(pv, pv_list);
TAILQ_REMOVE(&m->md.pv_list, pv, pv_list);
TAILQ_INSERT_TAIL(&m->md.pv_list, pv, pv_list);
pmap_track_modified(pv->pv_pmap, pv->pv_va);
pte = pmap_pte(pv->pv_pmap, pv->pv_va);
if (pte && (*pte & VPTE_A)) {
atomic_clear_long(pte, VPTE_A);
rtval++;
if (rtval > 4) {
break;
}
}
} while ((pv = pvn) != NULL && pv != pvf);
}
vm_page_spin_unlock(m);
return (rtval);
}
boolean_t
pmap_is_modified(vm_page_t m)
{
boolean_t res;
res = pmap_testbit(m, VPTE_M);
return (res);
}
void
pmap_clear_modify(vm_page_t m)
{
pmap_clearbit(m, VPTE_RW);
}
void
pmap_clear_reference(vm_page_t m)
{
pmap_clearbit(m, VPTE_A);
}
static void
x86_64_protection_init(void)
{
uint64_t *kp;
int prot;
kp = protection_codes;
for (prot = 0; prot < 8; prot++) {
if (prot & VM_PROT_READ)
*kp |= 0;
if (prot & VM_PROT_WRITE)
*kp |= VPTE_RW;
if (prot && (prot & VM_PROT_EXECUTE) == 0)
*kp |= VPTE_NX;
++kp;
}
}
void
pmap_page_set_memattr(vm_page_t m, vm_memattr_t ma)
{
}
void
pmap_change_attr(vm_offset_t va, vm_size_t count, int mode)
{
}
int
pmap_mincore(pmap_t pmap, vm_offset_t addr)
{
pt_entry_t *ptep, pte;
vm_page_t m;
int val = 0;
vm_object_hold(pmap->pm_pteobj);
ptep = pmap_pte(pmap, addr);
if (ptep && (pte = *ptep) != 0) {
vm_paddr_t pa;
val = MINCORE_INCORE;
if ((pte & VPTE_MANAGED) == 0)
goto done;
pa = pte & VPTE_FRAME;
m = PHYS_TO_VM_PAGE(pa);
if (pte & VPTE_M)
val |= MINCORE_MODIFIED|MINCORE_MODIFIED_OTHER;
else if (m->dirty || pmap_is_modified(m))
val |= MINCORE_MODIFIED_OTHER;
if (pte & VPTE_A)
val |= MINCORE_REFERENCED|MINCORE_REFERENCED_OTHER;
else if ((m->flags & PG_REFERENCED) || pmap_ts_referenced(m)) {
val |= MINCORE_REFERENCED_OTHER;
vm_page_flag_set(m, PG_REFERENCED);
}
}
done:
vm_object_drop(pmap->pm_pteobj);
return val;
}
void
pmap_replacevm(struct proc *p, struct vmspace *newvm, int adjrefs)
{
struct vmspace *oldvm;
struct lwp *lp;
oldvm = p->p_vmspace;
if (oldvm != newvm) {
if (adjrefs)
vmspace_ref(newvm);
KKASSERT((newvm->vm_refcnt & VM_REF_DELETED) == 0);
p->p_vmspace = newvm;
KKASSERT(p->p_nthreads == 1);
lp = RB_ROOT(&p->p_lwp_tree);
pmap_setlwpvm(lp, newvm);
if (adjrefs)
vmspace_rel(oldvm);
}
}
void
pmap_setlwpvm(struct lwp *lp, struct vmspace *newvm)
{
struct vmspace *oldvm;
struct pmap *pmap;
oldvm = lp->lwp_vmspace;
if (oldvm != newvm) {
crit_enter();
KKASSERT((newvm->vm_refcnt & VM_REF_DELETED) == 0);
lp->lwp_vmspace = newvm;
if (curthread->td_lwp == lp) {
pmap = vmspace_pmap(newvm);
ATOMIC_CPUMASK_ORBIT(pmap->pm_active, mycpu->gd_cpuid);
if (pmap->pm_active_lock & CPULOCK_EXCL)
pmap_interlock_wait(newvm);
#if defined(SWTCH_OPTIM_STATS)
tlb_flush_count++;
#endif
pmap = vmspace_pmap(oldvm);
ATOMIC_CPUMASK_NANDBIT(pmap->pm_active,
mycpu->gd_cpuid);
}
crit_exit();
}
}
void
pmap_interlock_wait (struct vmspace *vm)
{
pmap_t pmap = vmspace_pmap(vm);
if (pmap->pm_active_lock & CPULOCK_EXCL) {
crit_enter();
while (pmap->pm_active_lock & CPULOCK_EXCL) {
cpu_ccfence();
vkernel_yield();
}
crit_exit();
}
}
vm_offset_t
pmap_addr_hint(vm_object_t obj, vm_offset_t addr, vm_size_t size)
{
if ((obj == NULL) || (size < NBPDR) || (obj->type != OBJT_DEVICE)) {
return addr;
}
addr = roundup2(addr, NBPDR);
return addr;
}
vm_page_t
pmap_kvtom(vm_offset_t va)
{
vpte_t *ptep;
KKASSERT(va >= KvaStart && va < KvaEnd);
ptep = vtopte(va);
return(PHYS_TO_VM_PAGE(*ptep & PG_FRAME));
}
void
pmap_object_init(vm_object_t object)
{
}
void
pmap_object_free(vm_object_t object)
{
}
void
pmap_pgscan(struct pmap_pgscan_info *pginfo)
{
pmap_t pmap = pginfo->pmap;
vm_offset_t sva = pginfo->beg_addr;
vm_offset_t eva = pginfo->end_addr;
vm_offset_t va_next;
pml4_entry_t *pml4e;
pdp_entry_t *pdpe;
pd_entry_t ptpaddr, *pde;
pt_entry_t *pte;
vm_page_t pt_m;
int stop = 0;
vm_object_hold(pmap->pm_pteobj);
for (; sva < eva; sva = va_next) {
if (stop)
break;
pml4e = pmap_pml4e(pmap, sva);
if ((*pml4e & VPTE_V) == 0) {
va_next = (sva + NBPML4) & ~PML4MASK;
if (va_next < sva)
va_next = eva;
continue;
}
pdpe = pmap_pml4e_to_pdpe(pml4e, sva);
if ((*pdpe & VPTE_V) == 0) {
va_next = (sva + NBPDP) & ~PDPMASK;
if (va_next < sva)
va_next = eva;
continue;
}
va_next = (sva + NBPDR) & ~PDRMASK;
if (va_next < sva)
va_next = eva;
pde = pmap_pdpe_to_pde(pdpe, sva);
ptpaddr = *pde;
#if 0
if ((ptpaddr & VPTE_PS) != 0) {
#if 0
pmap_clean_pde(pde, pmap, sva);
pmap->pm_stats.resident_count -= NBPDR / PAGE_SIZE;
#endif
continue;
}
#endif
if (ptpaddr == 0)
continue;
if (va_next > eva)
va_next = eva;
pt_m = pmap_hold_pt_page(pde, sva);
for (pte = pmap_pde_to_pte(pde, sva); sva != va_next; pte++,
sva += PAGE_SIZE) {
vm_page_t m;
if (stop)
break;
if ((*pte & VPTE_MANAGED) == 0)
continue;
m = PHYS_TO_VM_PAGE(*pte & VPTE_FRAME);
if (vm_page_busy_try(m, TRUE) == 0) {
if (pginfo->callback(pginfo, sva, m) < 0)
stop = 1;
}
}
vm_page_unhold(pt_m);
}
vm_object_drop(pmap->pm_pteobj);
}
void
pmap_maybethreaded(pmap_t pmap)
{
}
int
pmap_mapped_sync(vm_page_t m)
{
return (m->flags);
}