#include "opt_ddb.h"
#include "opt_msgbuf.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/msgbuf.h>
#include <sys/vmmeter.h>
#include <sys/mman.h>
#include <sys/systm.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/cpu.h>
#include <machine/md_var.h>
#include <machine/specialreg.h>
#include <machine/smp.h>
#include <machine_base/apic/apicreg.h>
#include <machine/globaldata.h>
#include <machine/pmap.h>
#include <machine/pmap_inval.h>
#include <ddb/ddb.h>
#define PMAP_KEEP_PDIRS
#if defined(DIAGNOSTIC)
#define PMAP_DIAGNOSTIC
#endif
#define MINPV 2048
#ifdef PMAP_DEBUG
#define PMAP_DEBUG_DECL , const char *func, int lineno
#define PMAP_DEBUG_ARGS , __func__, __LINE__
#define PMAP_DEBUG_COPY , func, lineno
#define pv_get(pmap, pindex, pmarkp) _pv_get(pmap, pindex, pmarkp \
PMAP_DEBUG_ARGS)
#define pv_lock(pv) _pv_lock(pv \
PMAP_DEBUG_ARGS)
#define pv_hold_try(pv) _pv_hold_try(pv \
PMAP_DEBUG_ARGS)
#define pv_alloc(pmap, pindex, isnewp) _pv_alloc(pmap, pindex, isnewp \
PMAP_DEBUG_ARGS)
#define pv_free(pv, pvp) _pv_free(pv, pvp PMAP_DEBUG_ARGS)
#else
#define PMAP_DEBUG_DECL
#define PMAP_DEBUG_ARGS
#define PMAP_DEBUG_COPY
#define pv_get(pmap, pindex, pmarkp) _pv_get(pmap, pindex, pmarkp)
#define pv_lock(pv) _pv_lock(pv)
#define pv_hold_try(pv) _pv_hold_try(pv)
#define pv_alloc(pmap, pindex, isnewp) _pv_alloc(pmap, pindex, isnewp)
#define pv_free(pv, pvp) _pv_free(pv, pvp)
#endif
#define pdir_pde(m, v) (m[(vm_offset_t)(v) >> PDRSHIFT])
#define pmap_pde_v(pmap, pde) \
((*(pd_entry_t *)pde & pmap->pmap_bits[PG_V_IDX]) != 0)
#define pmap_pte_w(pmap, pte) \
((*(pt_entry_t *)pte & pmap->pmap_bits[PG_W_IDX]) != 0)
#define pmap_pte_m(pmap, pte) \
((*(pt_entry_t *)pte & pmap->pmap_bits[PG_M_IDX]) != 0)
#define pmap_pte_u(pmap, pte) \
((*(pt_entry_t *)pte & pmap->pmap_bits[PG_U_IDX]) != 0)
#define pmap_pte_v(pmap, pte) \
((*(pt_entry_t *)pte & pmap->pmap_bits[PG_V_IDX]) != 0)
#define pte_prot(m, p) \
(m->protection_codes[p & (VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE)])
static uint64_t protection_codes[PROTECTION_CODES_SIZE];
#define PMAP_PAGE_BACKING_SCAN(m, match_pmap, ipmap, iptep, ipte, iva) \
if (m->object) { \
vm_object_t iobj = m->object; \
vm_map_backing_t iba, next_ba; \
struct pmap *ipmap; \
pt_entry_t ipte; \
pt_entry_t *iptep; \
vm_offset_t iva; \
vm_pindex_t ipindex_start; \
vm_pindex_t ipindex_end; \
\
lockmgr(&iobj->backing_lk, LK_SHARED); \
next_ba = TAILQ_FIRST(&iobj->backing_list); \
while ((iba = next_ba) != NULL) { \
next_ba = TAILQ_NEXT(iba, entry); \
if (iba->flags & VM_MAP_BACK_VPAGETABLE) \
continue; \
ipmap = iba->pmap; \
if (match_pmap && ipmap != match_pmap) \
continue; \
ipindex_start = iba->offset >> PAGE_SHIFT; \
ipindex_end = ipindex_start + \
((iba->end - iba->start) >> PAGE_SHIFT); \
if (m->pindex < ipindex_start || \
m->pindex >= ipindex_end) { \
continue; \
} \
iva = iba->start + \
((m->pindex - ipindex_start) << PAGE_SHIFT); \
iptep = pmap_pte(ipmap, iva); \
if (iptep == NULL) \
continue; \
ipte = *iptep; \
cpu_ccfence(); \
if (m->phys_addr != (ipte & PG_FRAME)) \
continue; \
#define PMAP_PAGE_BACKING_RETRY \
{ \
next_ba = iba; \
continue; \
} \
#define PMAP_PAGE_BACKING_DONE \
} \
lockmgr(&iobj->backing_lk, LK_RELEASE); \
} \
static struct pmap iso_pmap;
static struct pmap kernel_pmap_store;
struct pmap *kernel_pmap = &kernel_pmap_store;
vm_paddr_t avail_start;
vm_paddr_t avail_end;
vm_offset_t virtual2_start;
vm_offset_t virtual2_end;
vm_offset_t virtual_start;
vm_offset_t virtual_end;
vm_offset_t KvaStart;
vm_offset_t KvaEnd;
vm_offset_t KvaSize;
vm_offset_t DMapMaxAddress;
__read_frequently static boolean_t pmap_initialized = FALSE;
static uint64_t PatMsr;
static int ndmpdp;
static vm_paddr_t dmaplimit;
vm_offset_t kernel_vm_end = VM_MIN_KERNEL_ADDRESS;
static pt_entry_t pat_pte_index[PAT_INDEX_SIZE];
static pt_entry_t pat_pde_index[PAT_INDEX_SIZE];
uint64_t KPDPphys;
uint64_t KPML4phys;
static uint64_t DMPDphys;
static uint64_t DMPDPphys;
__read_mostly static vm_zone_t pvzone;
__read_mostly static int pmap_pagedaemon_waken = 0;
static struct vm_zone pvzone_store;
static struct pv_entry *pvinit;
pt_entry_t *CMAP1 = NULL;
caddr_t CADDR1 = NULL, ptvmmap = NULL;
static pt_entry_t *msgbufmap, *ptmmap;
struct msgbuf *msgbufp = NULL;
__read_frequently static uint64_t pmap_bits_default[PG_BITS_SIZE] = {
[TYPE_IDX] = REGULAR_PMAP,
[PG_V_IDX] = X86_PG_V,
[PG_RW_IDX] = X86_PG_RW,
[PG_U_IDX] = X86_PG_U,
[PG_A_IDX] = X86_PG_A,
[PG_M_IDX] = X86_PG_M,
[PG_PS_IDX] = X86_PG_PS,
[PG_G_IDX] = X86_PG_G,
[PG_W_IDX] = X86_PG_AVAIL1,
[PG_MANAGED_IDX] = X86_PG_AVAIL2,
[PG_N_IDX] = X86_PG_NC_PWT | X86_PG_NC_PCD,
[PG_NX_IDX] = X86_PG_NX,
};
static pt_entry_t *pt_crashdumpmap;
static caddr_t crashdumpmap;
static int pmap_debug = 0;
SYSCTL_INT(_machdep, OID_AUTO, pmap_debug, CTLFLAG_RW,
&pmap_debug, 0, "Debug pmap's");
#ifdef PMAP_DEBUG2
static int pmap_enter_debug = 0;
SYSCTL_INT(_machdep, OID_AUTO, pmap_enter_debug, CTLFLAG_RW,
&pmap_enter_debug, 0, "Debug pmap_enter's");
#endif
static int pmap_yield_count = 64;
SYSCTL_INT(_machdep, OID_AUTO, pmap_yield_count, CTLFLAG_RW,
&pmap_yield_count, 0, "Yield during init_pt/release");
static int pmap_fast_kernel_cpusync = 0;
SYSCTL_INT(_machdep, OID_AUTO, pmap_fast_kernel_cpusync, CTLFLAG_RW,
&pmap_fast_kernel_cpusync, 0, "Share page table pages when possible");
static int pmap_dynamic_delete = 0;
SYSCTL_INT(_machdep, OID_AUTO, pmap_dynamic_delete, CTLFLAG_RW,
&pmap_dynamic_delete, 0, "Dynamically delete PT/PD/PDPs");
static int pmap_lock_delay = 100;
SYSCTL_INT(_machdep, OID_AUTO, pmap_lock_delay, CTLFLAG_RW,
&pmap_lock_delay, 0, "Spin loops");
static int meltdown_mitigation = -1;
TUNABLE_INT("machdep.meltdown_mitigation", &meltdown_mitigation);
SYSCTL_INT(_machdep, OID_AUTO, meltdown_mitigation, CTLFLAG_RW,
&meltdown_mitigation, 0, "Userland pmap isolation");
static int pmap_nx_enable = -1;
SYSCTL_INT(_machdep, OID_AUTO, pmap_nx_enable, CTLFLAG_RD,
&pmap_nx_enable, 0,
"no-execute support (0=disabled, 1=w/READ, 2=w/READ & WRITE)");
static int pmap_pv_debug = 50;
SYSCTL_INT(_machdep, OID_AUTO, pmap_pv_debug, CTLFLAG_RW,
&pmap_pv_debug, 0, "");
static long vm_pmap_pv_entries;
SYSCTL_LONG(_vm, OID_AUTO, pmap_pv_entries, CTLFLAG_RD,
&vm_pmap_pv_entries, 0, "");
extern int std_copyinstr (const void *udaddr, void *kaddr, size_t len,
size_t *lencopied);
extern int std_copyin (const void *udaddr, void *kaddr, size_t len);
extern int std_copyout (const void *kaddr, void *udaddr, size_t len);
extern int std_fubyte (const uint8_t *base);
extern int std_subyte (uint8_t *base, uint8_t byte);
extern int32_t std_fuword32 (const uint32_t *base);
extern int64_t std_fuword64 (const uint64_t *base);
extern int std_suword64 (uint64_t *base, uint64_t word);
extern int std_suword32 (uint32_t *base, int word);
extern uint32_t std_swapu32 (volatile uint32_t *base, uint32_t v);
extern uint64_t std_swapu64 (volatile uint64_t *base, uint64_t v);
extern uint32_t std_fuwordadd32 (volatile uint32_t *base, uint32_t v);
extern uint64_t std_fuwordadd64 (volatile uint64_t *base, uint64_t v);
#if 0
static void pv_hold(pv_entry_t pv);
#endif
static int _pv_hold_try(pv_entry_t pv
PMAP_DEBUG_DECL);
static void pv_drop(pv_entry_t pv);
static void _pv_lock(pv_entry_t pv
PMAP_DEBUG_DECL);
static void pv_unlock(pv_entry_t pv);
static pv_entry_t _pv_alloc(pmap_t pmap, vm_pindex_t pindex, int *isnew
PMAP_DEBUG_DECL);
static pv_entry_t _pv_get(pmap_t pmap, vm_pindex_t pindex, vm_pindex_t **pmarkp
PMAP_DEBUG_DECL);
static void _pv_free(pv_entry_t pv, pv_entry_t pvp PMAP_DEBUG_DECL);
static pv_entry_t pv_get_try(pmap_t pmap, vm_pindex_t pindex,
vm_pindex_t **pmarkp, int *errorp);
static void pv_put(pv_entry_t pv);
static void *pv_pte_lookup(pv_entry_t pv, vm_pindex_t pindex);
static pv_entry_t pmap_allocpte(pmap_t pmap, vm_pindex_t ptepindex,
pv_entry_t *pvpp);
static void pmap_remove_pv_pte(pv_entry_t pv, pv_entry_t pvp,
pmap_inval_bulk_t *bulk, int destroy);
static vm_page_t pmap_remove_pv_page(pv_entry_t pv, int clrpgbits);
static int pmap_release_pv(pv_entry_t pv, pv_entry_t pvp,
pmap_inval_bulk_t *bulk);
struct pmap_scan_info;
static void pmap_remove_callback(pmap_t pmap, struct pmap_scan_info *info,
vm_pindex_t *pte_placemark, pv_entry_t pt_pv,
vm_offset_t va, pt_entry_t *ptep, void *arg __unused);
static void pmap_protect_callback(pmap_t pmap, struct pmap_scan_info *info,
vm_pindex_t *pte_placemark, pv_entry_t pt_pv,
vm_offset_t va, pt_entry_t *ptep, void *arg __unused);
static void x86_64_protection_init (void);
static void create_pagetables(vm_paddr_t *firstaddr);
static void pmap_remove_all (vm_page_t m);
static boolean_t pmap_testbit (vm_page_t m, int bit);
static pt_entry_t *pmap_pte_quick (pmap_t pmap, vm_offset_t va);
static vm_offset_t pmap_kmem_choose(vm_offset_t addr);
static void pmap_pinit_defaults(struct pmap *pmap);
static void pv_placemarker_wait(pmap_t pmap, vm_pindex_t *pmark);
static void pv_placemarker_wakeup(pmap_t pmap, vm_pindex_t *pmark);
static int
pv_entry_compare(pv_entry_t pv1, pv_entry_t pv2)
{
if (pv1->pv_pindex < pv2->pv_pindex)
return(-1);
if (pv1->pv_pindex > pv2->pv_pindex)
return(1);
return(0);
}
RB_GENERATE2(pv_entry_rb_tree, pv_entry, pv_entry,
pv_entry_compare, vm_pindex_t, pv_pindex);
static __inline
void
pmap_removed_pte(pmap_t pmap, vm_page_t m, pt_entry_t pte)
{
int flags;
int nflags;
flags = m->flags;
cpu_ccfence();
while ((flags & PG_MAPPEDMULTI) == 0) {
nflags = flags & ~(PG_MAPPED | PG_WRITEABLE);
if (atomic_fcmpset_int(&m->flags, &flags, nflags))
break;
}
if (pte & pmap->pmap_bits[PG_W_IDX])
vm_page_unwire(m, -1);
}
static
vm_offset_t
pmap_kmem_choose(vm_offset_t addr)
{
vm_offset_t newaddr = addr;
newaddr = roundup2(addr, NBPDR);
return newaddr;
}
static __inline
vm_pindex_t
pmap_pte_pindex(vm_offset_t va)
{
return ((va >> PAGE_SHIFT) & (NUPTE_TOTAL - 1));
}
static __inline
vm_pindex_t
pmap_pt_pindex(vm_offset_t va)
{
return (NUPTE_TOTAL + ((va >> PDRSHIFT) & (NUPT_TOTAL - 1)));
}
static __inline
vm_pindex_t
pmap_pd_pindex(vm_offset_t va)
{
return (NUPTE_TOTAL + NUPT_TOTAL +
((va >> PDPSHIFT) & (NUPD_TOTAL - 1)));
}
static __inline
vm_pindex_t
pmap_pdp_pindex(vm_offset_t va)
{
return (NUPTE_TOTAL + NUPT_TOTAL + NUPD_TOTAL +
((va >> PML4SHIFT) & (NUPDP_TOTAL - 1)));
}
static __inline
vm_pindex_t
pmap_pml4_pindex(void)
{
return (NUPTE_TOTAL + NUPT_TOTAL + NUPD_TOTAL + NUPDP_TOTAL);
}
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_pt_index(vm_offset_t va)
{
return ((va >> PDRSHIFT) & ((1UL << NPDEPGSHIFT) - 1));
}
static __inline
vm_pindex_t
pmap_pd_index(vm_offset_t va)
{
return ((va >> PDPSHIFT) & ((1UL << NPDPEPGSHIFT) - 1));
}
static __inline
vm_pindex_t
pmap_pdp_index(vm_offset_t va)
{
return ((va >> PML4SHIFT) & ((1UL << NPML4EPGSHIFT) - 1));
}
static __inline
void
pv_cache(pmap_t pmap, pv_entry_t pv, vm_pindex_t pindex)
{
if (pindex < pmap_pt_pindex(0)) {
;
} else if (pindex < pmap_pd_pindex(0)) {
pmap->pm_pvhint_pt = pv;
}
}
static __inline
pv_entry_t
pv_entry_lookup(pmap_t pmap, vm_pindex_t pindex)
{
pv_entry_t pv;
if (pindex < pmap_pt_pindex(0))
return NULL;
#if 1
if (pindex < pmap_pd_pindex(0))
pv = pmap->pm_pvhint_pt;
else
pv = NULL;
cpu_ccfence();
if (pv == NULL || pv->pv_pmap != pmap) {
pv = pv_entry_rb_tree_RB_LOOKUP(&pmap->pm_pvroot, pindex);
if (pv)
pv_cache(pmap, pv, pindex);
} else if (pv->pv_pindex != pindex) {
pv = pv_entry_rb_tree_RB_LOOKUP_REL(&pmap->pm_pvroot,
pindex, pv);
if (pv)
pv_cache(pmap, pv, pindex);
}
#else
pv = pv_entry_rb_tree_RB_LOOKUP(&pmap->pm_pvroot, pindex);
#endif
return pv;
}
static __inline pt_entry_t *pmap_pte(pmap_t pmap, vm_offset_t va);
static
pt_entry_t *
pmap_pte_quick(pmap_t pmap, vm_offset_t va)
{
return pmap_pte(pmap, va);
}
#define PM_PLACE_BASE (PM_PLACEMARKS >> 2)
static __inline
vm_pindex_t *
pmap_placemarker_hash(pmap_t pmap, vm_pindex_t pindex)
{
int hi;
if (pindex < pmap_pt_pindex(0))
hi = 0;
else if (pindex < pmap_pd_pindex(0))
hi = PM_PLACE_BASE;
else if (pindex < pmap_pdp_pindex(0))
hi = PM_PLACE_BASE << 1;
else
hi = PM_PLACE_BASE | (PM_PLACE_BASE << 1);
hi += pindex & (PM_PLACE_BASE - 1);
return (&pmap->pm_placemarks[hi]);
}
static
void *
pv_pte_lookup(pv_entry_t pv, vm_pindex_t pindex)
{
pt_entry_t *pte;
pte = (pt_entry_t *)PHYS_TO_DMAP(VM_PAGE_TO_PHYS(pv->pv_m));
return(&pte[pindex]);
}
static __inline
pml4_entry_t *
pmap_pdp(pmap_t pmap, vm_offset_t va)
{
return (&pmap->pm_pml4[pmap_pdp_index(va)]);
}
static __inline
pdp_entry_t *
pmap_pdp_to_pd(pml4_entry_t pdp_pte, vm_offset_t va)
{
pdp_entry_t *pd;
pd = (pdp_entry_t *)PHYS_TO_DMAP(pdp_pte & PG_FRAME);
return (&pd[pmap_pd_index(va)]);
}
static __inline
pdp_entry_t *
pmap_pd(pmap_t pmap, vm_offset_t va)
{
pml4_entry_t *pdp;
pdp = pmap_pdp(pmap, va);
if ((*pdp & pmap->pmap_bits[PG_V_IDX]) == 0)
return NULL;
return (pmap_pdp_to_pd(*pdp, va));
}
static __inline
pd_entry_t *
pmap_pd_to_pt(pdp_entry_t pd_pte, vm_offset_t va)
{
pd_entry_t *pt;
pt = (pd_entry_t *)PHYS_TO_DMAP(pd_pte & PG_FRAME);
return (&pt[pmap_pt_index(va)]);
}
static __inline
pd_entry_t *
pmap_pt(pmap_t pmap, vm_offset_t va)
{
pdp_entry_t *pd;
pv_entry_t pv;
vm_pindex_t pd_pindex;
vm_paddr_t phys;
if (pmap->pm_flags & PMAP_FLAG_SIMPLE) {
pd_pindex = pmap_pd_pindex(va);
spin_lock_shared(&pmap->pm_spin);
pv = pv_entry_rb_tree_RB_LOOKUP(&pmap->pm_pvroot, pd_pindex);
if (pv == NULL || pv->pv_m == NULL) {
spin_unlock_shared(&pmap->pm_spin);
return NULL;
}
phys = VM_PAGE_TO_PHYS(pv->pv_m);
spin_unlock_shared(&pmap->pm_spin);
return (pmap_pd_to_pt(phys, va));
} else {
pd = pmap_pd(pmap, va);
if (pd == NULL || (*pd & pmap->pmap_bits[PG_V_IDX]) == 0)
return NULL;
return (pmap_pd_to_pt(*pd, va));
}
}
static __inline
pt_entry_t *
pmap_pt_to_pte(pd_entry_t pt_pte, vm_offset_t va)
{
pt_entry_t *pte;
pte = (pt_entry_t *)PHYS_TO_DMAP(pt_pte & PG_FRAME);
return (&pte[pmap_pte_index(va)]);
}
static __inline
pt_entry_t *
pmap_pte(pmap_t pmap, vm_offset_t va)
{
pd_entry_t *pt;
pt = pmap_pt(pmap, va);
if (pt == NULL || (*pt & pmap->pmap_bits[PG_V_IDX]) == 0)
return NULL;
if ((*pt & pmap->pmap_bits[PG_PS_IDX]) != 0)
return ((pt_entry_t *)pt);
return (pmap_pt_to_pte(*pt, va));
}
static __inline
pd_entry_t *
vtopt(vm_offset_t va)
{
uint64_t mask = ((1ul << (NPDEPGSHIFT + NPDPEPGSHIFT +
NPML4EPGSHIFT)) - 1);
return (PDmap + ((va >> PDRSHIFT) & mask));
}
static __inline
pt_entry_t *
vtopte(vm_offset_t va)
{
uint64_t mask = ((1ul << (NPTEPGSHIFT + NPDEPGSHIFT +
NPDPEPGSHIFT + NPML4EPGSHIFT)) - 1);
return (PTmap + ((va >> PAGE_SHIFT) & mask));
}
vm_paddr_t
uservtophys(vm_offset_t va)
{
uint64_t mask = ((1ul << (NPTEPGSHIFT + NPDEPGSHIFT +
NPDPEPGSHIFT + NPML4EPGSHIFT)) - 1);
vm_paddr_t pa;
pt_entry_t pte;
pmap_t pmap;
pmap = vmspace_pmap(mycpu->gd_curthread->td_lwp->lwp_vmspace);
pa = (vm_paddr_t)-1;
if (va < VM_MAX_USER_ADDRESS) {
pte = kreadmem64(PTmap + ((va >> PAGE_SHIFT) & mask));
if (pte & pmap->pmap_bits[PG_V_IDX])
pa = (pte & PG_FRAME) | (va & PAGE_MASK);
}
return pa;
}
static uint64_t
allocpages(vm_paddr_t *firstaddr, long n)
{
uint64_t ret;
ret = *firstaddr;
bzero((void *)ret, n * PAGE_SIZE);
*firstaddr += n * PAGE_SIZE;
return (ret);
}
static
void
create_pagetables(vm_paddr_t *firstaddr)
{
uint64_t kpt_base;
uint64_t kpt_phys;
uint64_t kpd_base;
uint64_t kpd_phys;
long i;
long nkpt_base;
long nkpt_phys;
long nkpd_phys;
int j;
ndmpdp = (ptoa(Maxmem) + NBPDP - 1) >> PDPSHIFT;
if (ndmpdp < 4)
ndmpdp = 4;
#if 0
if (ndmpdp < 512)
ndmpdp = 512;
#endif
KKASSERT(ndmpdp <= NDMPML4E * NPML4EPG);
DMapMaxAddress = DMAP_MIN_ADDRESS +
((ndmpdp * NPDEPG) << PDRSHIFT);
nkpt_base = (NPDPEPG - KPDPI) * NPTEPG;
nkpt_phys = howmany(Maxmem * sizeof(struct vm_page), NBPDR);
nkpt_phys += howmany(Maxmem * sizeof(struct pv_entry), NBPDR);
nkpt_phys += 128;
nkpd_phys = (nkpt_phys + NPDPEPG - 1) / NPDPEPG;
kpt_base = allocpages(firstaddr, nkpt_base);
kpt_phys = allocpages(firstaddr, nkpt_phys);
KPML4phys = allocpages(firstaddr, 1);
KPDPphys = allocpages(firstaddr, NKPML4E);
kpd_phys = allocpages(firstaddr, nkpd_phys);
kpd_base = allocpages(firstaddr, NPDPEPG - KPDPI);
DMPDPphys = allocpages(firstaddr, NDMPML4E);
#if 1
DMPDphys = allocpages(firstaddr, ndmpdp);
#else
if ((amd_feature & AMDID_PAGE1GB) == 0)
DMPDphys = allocpages(firstaddr, ndmpdp);
#endif
dmaplimit = (vm_paddr_t)ndmpdp << PDPSHIFT;
for (i = 0; (i << PAGE_SHIFT) < *firstaddr; i++) {
((pt_entry_t *)kpt_base)[i] = i << PAGE_SHIFT;
((pt_entry_t *)kpt_base)[i] |=
pmap_bits_default[PG_RW_IDX] |
pmap_bits_default[PG_V_IDX] |
pmap_bits_default[PG_G_IDX];
}
for (i = 0; i < nkpt_base; i++) {
((pd_entry_t *)kpd_base)[i] = kpt_base + (i << PAGE_SHIFT);
((pd_entry_t *)kpd_base)[i] |=
pmap_bits_default[PG_RW_IDX] |
pmap_bits_default[PG_V_IDX];
}
for (i = 0; i < nkpt_phys; i++) {
((pd_entry_t *)kpd_phys)[i] = kpt_phys + (i << PAGE_SHIFT);
((pd_entry_t *)kpd_phys)[i] |=
pmap_bits_default[PG_RW_IDX] |
pmap_bits_default[PG_V_IDX];
}
for (i = 0; (i << PDRSHIFT) < *firstaddr; i++) {
((pd_entry_t *)kpd_base)[i] = i << PDRSHIFT;
((pd_entry_t *)kpd_base)[i] |=
pmap_bits_default[PG_RW_IDX] |
pmap_bits_default[PG_V_IDX] |
pmap_bits_default[PG_PS_IDX] |
pmap_bits_default[PG_G_IDX];
}
for (i = 0; i < nkpd_phys; i++) {
((pdp_entry_t *)KPDPphys)[NKPML4E * NPDPEPG - NKPDPE + i] =
kpd_phys + (i << PAGE_SHIFT);
((pdp_entry_t *)KPDPphys)[NKPML4E * NPDPEPG - NKPDPE + i] |=
pmap_bits_default[PG_RW_IDX] |
pmap_bits_default[PG_V_IDX] |
pmap_bits_default[PG_A_IDX];
}
i = (NKPML4E - 1) * NPDPEPG + KPDPI;
for (j = 0; j < NPDPEPG - KPDPI; ++j) {
((pdp_entry_t *)KPDPphys)[i + j] =
kpd_base + (j << PAGE_SHIFT);
((pdp_entry_t *)KPDPphys)[i + j] |=
pmap_bits_default[PG_RW_IDX] |
pmap_bits_default[PG_V_IDX] |
pmap_bits_default[PG_A_IDX];
}
#if 0
if ((amd_feature & AMDID_PAGE1GB) == 0)
#endif
{
for (i = 0; i < NPDEPG * ndmpdp; i++) {
((pd_entry_t *)DMPDphys)[i] = i << PDRSHIFT;
((pd_entry_t *)DMPDphys)[i] |=
pmap_bits_default[PG_RW_IDX] |
pmap_bits_default[PG_V_IDX] |
pmap_bits_default[PG_PS_IDX] |
pmap_bits_default[PG_G_IDX] |
pmap_bits_default[PG_M_IDX] |
pmap_bits_default[PG_A_IDX];
}
for (i = 0; i < ndmpdp; i++) {
((pdp_entry_t *)DMPDPphys)[i] = DMPDphys +
(i << PAGE_SHIFT);
((pdp_entry_t *)DMPDPphys)[i] |=
pmap_bits_default[PG_RW_IDX] |
pmap_bits_default[PG_V_IDX] |
pmap_bits_default[PG_A_IDX];
}
}
#if 0
else {
for (i = 0; i < ndmpdp; i++) {
((pdp_entry_t *)DMPDPphys)[i] =
(vm_paddr_t)i << PDPSHIFT;
((pdp_entry_t *)DMPDPphys)[i] |=
pmap_bits_default[PG_RW_IDX] |
pmap_bits_default[PG_V_IDX] |
pmap_bits_default[PG_PS_IDX] |
pmap_bits_default[PG_G_IDX] |
pmap_bits_default[PG_M_IDX] |
pmap_bits_default[PG_A_IDX];
}
}
#endif
((pdp_entry_t *)KPML4phys)[PML4PML4I] = KPML4phys;
((pdp_entry_t *)KPML4phys)[PML4PML4I] |=
pmap_bits_default[PG_RW_IDX] |
pmap_bits_default[PG_V_IDX] |
pmap_bits_default[PG_A_IDX];
for (j = 0; j < NDMPML4E; ++j) {
((pdp_entry_t *)KPML4phys)[DMPML4I + j] =
(DMPDPphys + ((vm_paddr_t)j << PAGE_SHIFT)) |
pmap_bits_default[PG_RW_IDX] |
pmap_bits_default[PG_V_IDX] |
pmap_bits_default[PG_A_IDX];
}
for (j = 0; j < NKPML4E; ++j) {
((pdp_entry_t *)KPML4phys)[KPML4I + j] =
KPDPphys + ((vm_paddr_t)j << PAGE_SHIFT);
((pdp_entry_t *)KPML4phys)[KPML4I + j] |=
pmap_bits_default[PG_RW_IDX] |
pmap_bits_default[PG_V_IDX] |
pmap_bits_default[PG_A_IDX];
}
cpu_mfence();
cpu_invltlb();
}
void
pmap_bootstrap(vm_paddr_t *firstaddr)
{
vm_offset_t va;
pt_entry_t *pte;
int i;
KvaStart = VM_MIN_KERNEL_ADDRESS;
KvaEnd = VM_MAX_KERNEL_ADDRESS;
KvaSize = KvaEnd - KvaStart;
avail_start = *firstaddr;
create_pagetables(firstaddr);
virtual2_start = KvaStart;
virtual2_end = PTOV_OFFSET;
virtual_start = (vm_offset_t) PTOV_OFFSET + *firstaddr;
virtual_start = pmap_kmem_choose(virtual_start);
virtual_end = VM_MAX_KERNEL_ADDRESS;
load_cr4(rcr4() | CR4_PGE | CR4_PSE);
load_cr3(KPML4phys);
x86_64_protection_init();
kernel_pmap->pm_pml4 = (pdp_entry_t *) (PTOV_OFFSET + KPML4phys);
kernel_pmap->pm_count = 1;
CPUMASK_ASSALLONES(kernel_pmap->pm_active);
RB_INIT(&kernel_pmap->pm_pvroot);
spin_init(&kernel_pmap->pm_spin, "pmapbootstrap");
for (i = 0; i < PM_PLACEMARKS; ++i)
kernel_pmap->pm_placemarks[i] = PM_NOPLACEMARK;
#define SYSMAP(c, p, v, n) \
v = (c)va; va += ((n)*PAGE_SIZE); p = pte; pte += (n);
va = virtual_start;
pte = vtopte(va);
SYSMAP(caddr_t, CMAP1, CADDR1, 1)
SYSMAP(caddr_t, pt_crashdumpmap, crashdumpmap, MAXDUMPPGS);
SYSMAP(caddr_t, ptmmap, ptvmmap, 1)
SYSMAP(struct msgbuf *, msgbufmap, msgbufp,
atop(round_page(MSGBUF_SIZE)))
virtual_start = va;
virtual_start = pmap_kmem_choose(virtual_start);
*CMAP1 = 0;
cpu_invltlb();
pmap_init_pat();
pmap_pinit_defaults(kernel_pmap);
TUNABLE_INT_FETCH("machdep.pmap_fast_kernel_cpusync",
&pmap_fast_kernel_cpusync);
}
void
pmap_init_pat(void)
{
uint64_t pat_msr;
u_long cr0, cr4;
int i;
pat_msr = PAT_VALUE(0, PAT_WRITE_BACK) |
PAT_VALUE(1, PAT_WRITE_THROUGH) |
PAT_VALUE(2, PAT_UNCACHED) |
PAT_VALUE(3, PAT_UNCACHEABLE) |
PAT_VALUE(4, PAT_WRITE_BACK) |
PAT_VALUE(5, PAT_WRITE_THROUGH) |
PAT_VALUE(6, PAT_UNCACHED) |
PAT_VALUE(7, PAT_UNCACHEABLE);
pat_pte_index[PAT_WRITE_BACK] = 0;
pat_pte_index[PAT_WRITE_THROUGH]= 0 | X86_PG_NC_PWT;
pat_pte_index[PAT_UNCACHED] = X86_PG_NC_PCD;
pat_pte_index[PAT_UNCACHEABLE] = X86_PG_NC_PCD | X86_PG_NC_PWT;
pat_pte_index[PAT_WRITE_PROTECTED] = pat_pte_index[PAT_UNCACHEABLE];
pat_pte_index[PAT_WRITE_COMBINING] = pat_pte_index[PAT_UNCACHEABLE];
if (cpu_feature & CPUID_PAT) {
pat_msr = (pat_msr & ~PAT_MASK(5)) |
PAT_VALUE(5, PAT_WRITE_PROTECTED);
pat_msr = (pat_msr & ~PAT_MASK(6)) |
PAT_VALUE(6, PAT_WRITE_COMBINING);
pat_pte_index[PAT_WRITE_PROTECTED] = X86_PG_PTE_PAT | X86_PG_NC_PWT;
pat_pte_index[PAT_WRITE_COMBINING] = X86_PG_PTE_PAT | X86_PG_NC_PCD;
cr4 = rcr4();
load_cr4(cr4 & ~CR4_PGE);
cr0 = rcr0();
load_cr0((cr0 & ~CR0_NW) | CR0_CD);
wbinvd();
cpu_invltlb();
wrmsr(MSR_PAT, pat_msr);
wbinvd();
cpu_invltlb();
load_cr0(cr0);
load_cr4(cr4);
PatMsr = pat_msr;
}
for (i = 0; i < 8; ++i) {
pt_entry_t pte;
pte = pat_pte_index[i];
if (pte & X86_PG_PTE_PAT) {
pte &= ~X86_PG_PTE_PAT;
pte |= X86_PG_PDE_PAT;
}
pat_pde_index[i] = pte;
}
}
void
pmap_set_opt(void)
{
if (cpu_feature & CPUID_PSE) {
load_cr4(rcr4() | CR4_PSE);
if (mycpu->gd_cpuid == 0)
cpu_invltlb();
}
if (cpu_stdext_feature & CPUID_STDEXT_SMAP) {
load_cr4(rcr4() | CR4_SMAP);
}
if (cpu_stdext_feature & CPUID_STDEXT_SMEP) {
load_cr4(rcr4() | CR4_SMEP);
}
}
void
smap_smep_disable(void)
{
smap_open();
curthread->td_pcb->pcb_onfault = (void *)1;
if (cpu_stdext_feature & CPUID_STDEXT_SMEP)
load_cr4(rcr4() & ~CR4_SMEP);
}
void
smap_smep_enable(void)
{
if (cpu_stdext_feature & CPUID_STDEXT_SMEP)
load_cr4(rcr4() | CR4_SMEP);
curthread->td_pcb->pcb_onfault = NULL;
smap_close();
}
void
pmap_init(void)
{
vm_pindex_t initial_pvs;
vm_pindex_t i;
for (i = 0; i < vm_page_array_size; i++) {
vm_page_t m;
m = &vm_page_array[i];
m->md.interlock_count = 0;
}
initial_pvs = vm_page_array_size;
if (initial_pvs < MINPV)
initial_pvs = MINPV;
pvzone = &pvzone_store;
pvinit = (void *)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;
}
static void pmap_init_iso_range(vm_offset_t base, size_t bytes);
static void pmap_init2_iso_pmap(void);
#if 0
static void dump_pmap(pmap_t pmap, pt_entry_t pte, int level, vm_offset_t base);
#endif
void
pmap_init2(void)
{
vm_pindex_t entry_max;
entry_max = maxproc * 32 + vm_page_array_size / 16;
TUNABLE_LONG_FETCH("vm.pmap.pv_entries", &entry_max);
vm_pmap_pv_entries = entry_max;
if (entry_max <= MINPV)
entry_max = MINPV;
zinitna(pvzone, NULL, 0, entry_max, ZONE_INTERRUPT);
#if 0
if (pmap_dynamic_delete < 0) {
if (vmstats.v_page_count < 7LL * 1024 * 1024 * 1024 / PAGE_SIZE)
pmap_dynamic_delete = 1;
else
pmap_dynamic_delete = 0;
}
#endif
pmap_dynamic_delete = 0;
if (meltdown_mitigation < 0) {
if (cpu_vendor_id == CPU_VENDOR_INTEL) {
meltdown_mitigation = 1;
if (cpu_ia32_arch_caps & IA32_ARCH_CAP_RDCL_NO)
meltdown_mitigation = 0;
} else {
meltdown_mitigation = 0;
}
}
if (meltdown_mitigation) {
kprintf("machdep.meltdown_mitigation enabled to "
"protect against (mostly Intel) meltdown bug\n");
kprintf("system call performance will be impacted\n");
}
pmap_init2_iso_pmap();
}
static void
pmap_init2_iso_pmap(void)
{
int n;
if (bootverbose)
kprintf("Initialize isolation pmap\n");
pmap_pinit(&iso_pmap);
bzero(iso_pmap.pm_pml4, PAGE_SIZE);
for (n = 0; n < ncpus; ++n) {
struct privatespace *ps;
ps = CPU_prvspace[n];
pmap_init_iso_range((vm_offset_t)&ps->trampoline,
sizeof(ps->trampoline));
pmap_init_iso_range((vm_offset_t)&ps->dblstack,
sizeof(ps->dblstack));
pmap_init_iso_range((vm_offset_t)&ps->dbgstack,
sizeof(ps->dbgstack));
pmap_init_iso_range((vm_offset_t)&ps->common_tss,
sizeof(ps->common_tss));
pmap_init_iso_range(r_idt_arr[n].rd_base,
r_idt_arr[n].rd_limit + 1);
pmap_init_iso_range((register_t)ps->mdglobaldata.gd_gdt,
MAXGDT_LIMIT);
}
pmap_init_iso_range((vm_offset_t)(int *)btext,
(vm_offset_t)(int *)etext -
(vm_offset_t)(int *)btext);
#if 0
kprintf("Dump iso_pmap:\n");
dump_pmap(&iso_pmap, vtophys(iso_pmap.pm_pml4), 0, 0);
kprintf("\nDump kernel_pmap:\n");
dump_pmap(kernel_pmap, vtophys(kernel_pmap->pm_pml4), 0, 0);
#endif
}
static void
pmap_init_iso_range(vm_offset_t base, size_t bytes)
{
pv_entry_t pv;
pv_entry_t pvp;
pt_entry_t *ptep;
pt_entry_t pte;
vm_offset_t va;
if (bootverbose) {
kprintf("isolate %016jx-%016jx (%zd)\n",
base, base + bytes, bytes);
}
va = base & ~(vm_offset_t)PAGE_MASK;
while (va < base + bytes) {
if ((va & PDRMASK) == 0 && va + NBPDR <= base + bytes &&
(ptep = pmap_pt(kernel_pmap, va)) != NULL &&
(*ptep & kernel_pmap->pmap_bits[PG_V_IDX]) &&
(*ptep & kernel_pmap->pmap_bits[PG_PS_IDX])) {
pte = *ptep;
pv = pmap_allocpte(&iso_pmap, pmap_pd_pindex(va), &pvp);
ptep = pv_pte_lookup(pv, (va >> PDRSHIFT) & 511);
*ptep = pte;
va += NBPDR;
} else {
pv = pmap_allocpte(&iso_pmap, pmap_pt_pindex(va), &pvp);
ptep = pv_pte_lookup(pv, (va >> PAGE_SHIFT) & 511);
*ptep = vtophys(va) | kernel_pmap->pmap_bits[PG_RW_IDX] |
kernel_pmap->pmap_bits[PG_V_IDX] |
kernel_pmap->pmap_bits[PG_A_IDX] |
kernel_pmap->pmap_bits[PG_M_IDX];
va += PAGE_SIZE;
}
pv_put(pv);
pv_put(pvp);
}
}
#if 0
static
void
dump_pmap(pmap_t pmap, pt_entry_t pte, int level, vm_offset_t base)
{
pt_entry_t *ptp;
vm_offset_t incr;
int i;
switch(level) {
case 0:
incr = (1LL << 48) / 512;
break;
case 1:
incr = (1LL << 39) / 512;
break;
case 2:
incr = (1LL << 30) / 512;
break;
case 3:
incr = (1LL << 21) / 512;
break;
default:
incr = 0;
break;
}
if (level == 0)
kprintf("cr3 %016jx @ va=%016jx\n", pte, base);
ptp = (void *)PHYS_TO_DMAP(pte & ~(pt_entry_t)PAGE_MASK);
for (i = 0; i < 512; ++i) {
if (level == 0 && i == 128)
base += 0xFFFF000000000000LLU;
if (ptp[i]) {
kprintf("%*.*s ", level * 4, level * 4, "");
if (level == 1 && (ptp[i] & 0x180) == 0x180) {
kprintf("va=%016jx %3d term %016jx (1GB)\n",
base, i, ptp[i]);
} else if (level == 2 && (ptp[i] & 0x180) == 0x180) {
kprintf("va=%016jx %3d term %016jx (2MB)\n",
base, i, ptp[i]);
} else if (level == 3) {
kprintf("va=%016jx %3d term %016jx\n",
base, i, ptp[i]);
} else {
kprintf("va=%016jx %3d deep %016jx\n",
base, i, ptp[i]);
dump_pmap(pmap, ptp[i], level + 1, base);
}
}
base += incr;
}
}
#endif
void
pmap_page_init(struct vm_page *m)
{
vm_page_init(m);
m->md.interlock_count = 0;
}
vm_paddr_t
pmap_extract(pmap_t pmap, vm_offset_t va, void **handlep)
{
vm_paddr_t rtval;
pv_entry_t pt_pv;
pt_entry_t *ptep;
rtval = 0;
if (va >= VM_MAX_USER_ADDRESS) {
pd_entry_t *pt;
pt = pmap_pt(pmap, va);
if (pt && (*pt & pmap->pmap_bits[PG_V_IDX])) {
if (*pt & pmap->pmap_bits[PG_PS_IDX]) {
rtval = *pt & PG_PS_FRAME;
rtval |= va & PDRMASK;
} else {
ptep = pmap_pt_to_pte(*pt, va);
if (*pt & pmap->pmap_bits[PG_V_IDX]) {
rtval = *ptep & PG_FRAME;
rtval |= va & PAGE_MASK;
}
}
}
if (handlep)
*handlep = NULL;
} else {
pt_pv = pv_get(pmap, pmap_pt_pindex(va), NULL);
if (pt_pv) {
ptep = pv_pte_lookup(pt_pv, pmap_pte_index(va));
if (*ptep & pmap->pmap_bits[PG_V_IDX]) {
rtval = *ptep & PG_FRAME;
rtval |= va & PAGE_MASK;
}
if (handlep)
*handlep = pt_pv;
else
pv_put (pt_pv);
} else if (handlep) {
*handlep = NULL;
}
}
return rtval;
}
void
pmap_extract_done(void *handle)
{
if (handle)
pv_put((pv_entry_t)handle);
}
vm_page_t
pmap_fault_page_quick(pmap_t pmap, vm_offset_t va, vm_prot_t prot, int *busyp)
{
if (pmap &&
va < VM_MAX_USER_ADDRESS &&
(pmap->pm_flags & PMAP_HVM) == 0) {
pv_entry_t pt_pv;
pv_entry_t pte_pv;
pt_entry_t *ptep;
pt_entry_t req;
vm_page_t m;
int error;
req = pmap->pmap_bits[PG_V_IDX] |
pmap->pmap_bits[PG_U_IDX];
if (prot & VM_PROT_WRITE)
req |= pmap->pmap_bits[PG_RW_IDX];
pt_pv = pv_get(pmap, pmap_pt_pindex(va), NULL);
if (pt_pv == NULL)
return (NULL);
ptep = pv_pte_lookup(pt_pv, pmap_pte_index(va));
if ((*ptep & req) != req) {
pv_put(pt_pv);
return (NULL);
}
pte_pv = pv_get_try(pmap, pmap_pte_pindex(va), NULL, &error);
if (pte_pv && error == 0) {
m = pte_pv->pv_m;
if (prot & VM_PROT_WRITE) {
vm_page_dirty(m);
}
if (busyp) {
if (prot & VM_PROT_WRITE) {
if (vm_page_busy_try(m, TRUE))
m = NULL;
*busyp = 1;
} else {
vm_page_hold(m);
*busyp = 0;
}
} else {
vm_page_hold(m);
}
pv_put(pte_pv);
} else if (pte_pv) {
pv_drop(pte_pv);
m = NULL;
} else {
m = NULL;
}
pv_put(pt_pv);
return(m);
} else {
return(NULL);
}
}
vm_paddr_t
pmap_kextract(vm_offset_t va)
{
pd_entry_t pt;
vm_paddr_t pa;
if (va >= DMAP_MIN_ADDRESS && va < DMAP_MAX_ADDRESS) {
pa = DMAP_TO_PHYS(va);
} else {
pt = *vtopt(va);
if (pt & kernel_pmap->pmap_bits[PG_PS_IDX]) {
pa = (pt & PG_PS_FRAME) | (va & PDRMASK);
} else {
pa = *pmap_pt_to_pte(pt, va);
pa = (pa & PG_FRAME) | (va & PAGE_MASK);
}
}
return pa;
}
void
pmap_kenter(vm_offset_t va, vm_paddr_t pa)
{
pt_entry_t *ptep;
pt_entry_t npte;
npte = pa |
kernel_pmap->pmap_bits[PG_RW_IDX] |
kernel_pmap->pmap_bits[PG_V_IDX];
ptep = vtopte(va);
#if 1
pmap_inval_smp(kernel_pmap, va, 1, ptep, npte);
#else
if (*ptep)
pmap_inval_smp(kernel_pmap, va, ptep, npte);
else
*ptep = npte;
#endif
}
int
pmap_kenter_quick(vm_offset_t va, vm_paddr_t pa)
{
pt_entry_t *ptep;
pt_entry_t npte;
int res;
npte = pa | kernel_pmap->pmap_bits[PG_RW_IDX] |
kernel_pmap->pmap_bits[PG_V_IDX];
ptep = vtopte(va);
#if 1
res = 1;
#else
res = (*ptep != 0);
#endif
atomic_swap_long(ptep, npte);
cpu_invlpg((void *)va);
return res;
}
int
pmap_kenter_noinval(vm_offset_t va, vm_paddr_t pa)
{
pt_entry_t *ptep;
pt_entry_t npte;
int res;
npte = pa |
kernel_pmap->pmap_bits[PG_RW_IDX] |
kernel_pmap->pmap_bits[PG_V_IDX];
ptep = vtopte(va);
#if 1
res = 1;
#else
res = (*ptep != 0);
#endif
atomic_swap_long(ptep, npte);
cpu_invlpg((void *)va);
return res;
}
void
pmap_kremove(vm_offset_t va)
{
pt_entry_t *ptep;
ptep = vtopte(va);
pmap_inval_smp(kernel_pmap, va, 1, ptep, 0);
}
void
pmap_kremove_quick(vm_offset_t va)
{
pt_entry_t *ptep;
ptep = vtopte(va);
atomic_readandclear_long(ptep);
cpu_invlpg((void *)va);
}
void
pmap_kremove_noinval(vm_offset_t va)
{
pt_entry_t *ptep;
ptep = vtopte(va);
atomic_readandclear_long(ptep);
}
void
pmap_kmodify_rw(vm_offset_t va)
{
atomic_set_long(vtopte(va), kernel_pmap->pmap_bits[PG_RW_IDX]);
cpu_invlpg((void *)va);
}
vm_offset_t
pmap_map(vm_offset_t *virtp, vm_paddr_t start, vm_paddr_t end, int prot)
{
vm_offset_t va;
vm_offset_t va_start;
va_start = *virtp;
va = va_start;
while (start < end) {
pmap_kenter_quick(va, start);
va += PAGE_SIZE;
start += PAGE_SIZE;
}
*virtp = va;
return va_start;
}
#define PMAP_CLFLUSH_THRESHOLD (2 * 1024 * 1024)
void
pmap_invalidate_cache_pages(vm_page_t *pages, int count)
{
vm_offset_t daddr, eva;
int i;
if (count >= PMAP_CLFLUSH_THRESHOLD / PAGE_SIZE ||
(cpu_feature & CPUID_CLFSH) == 0)
wbinvd();
else {
cpu_mfence();
for (i = 0; i < count; i++) {
daddr = PHYS_TO_DMAP(VM_PAGE_TO_PHYS(pages[i]));
eva = daddr + PAGE_SIZE;
for (; daddr < eva; daddr += cpu_clflush_line_size)
clflush(daddr);
}
cpu_mfence();
}
}
void
pmap_invalidate_cache_range(vm_offset_t sva, vm_offset_t eva)
{
KASSERT((sva & PAGE_MASK) == 0,
("pmap_invalidate_cache_range: sva not page-aligned"));
KASSERT((eva & PAGE_MASK) == 0,
("pmap_invalidate_cache_range: eva not page-aligned"));
if (cpu_feature & CPUID_SS) {
;
} else {
cpu_wbinvd_on_all_cpus();
}
}
void
pmap_invalidate_range(pmap_t pmap, vm_offset_t sva, vm_offset_t eva)
{
pmap_inval_smp(pmap, sva, (eva - sva) >> PAGE_SHIFT, NULL, 0);
}
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;
for (va = beg_va; va < end_va; va += PAGE_SIZE) {
pt_entry_t pte;
pt_entry_t *ptep;
ptep = vtopte(va);
pte = VM_PAGE_TO_PHYS(*m) |
kernel_pmap->pmap_bits[PG_RW_IDX] |
kernel_pmap->pmap_bits[PG_V_IDX] |
kernel_pmap->pmap_cache_bits_pte[(*m)->pat_mode];
atomic_swap_long(ptep, pte);
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;
for (va = beg_va; va < end_va; va += PAGE_SIZE) {
pt_entry_t *pte;
pte = vtopte(va);
atomic_readandclear_long(pte);
cpu_invlpg((void *)va);
}
pmap_invalidate_range(kernel_pmap, beg_va, end_va);
}
void
pmap_qremove_quick(vm_offset_t beg_va, int count)
{
vm_offset_t end_va;
vm_offset_t va;
end_va = beg_va + count * PAGE_SIZE;
for (va = beg_va; va < end_va; va += PAGE_SIZE) {
pt_entry_t *pte;
pte = vtopte(va);
atomic_readandclear_long(pte);
cpu_invlpg((void *)va);
}
}
void
pmap_qremove_noinval(vm_offset_t beg_va, int count)
{
vm_offset_t end_va;
vm_offset_t va;
end_va = beg_va + count * PAGE_SIZE;
for (va = beg_va; va < end_va; va += PAGE_SIZE) {
pt_entry_t *pte;
pte = vtopte(va);
atomic_readandclear_long(pte);
}
}
void
pmap_init_thread(thread_t td)
{
td->td_pcb = (struct pcb *)(td->td_kstack + td->td_kstack_size) - 1;
td->td_pcb = (struct pcb *)((intptr_t)td->td_pcb & ~(intptr_t)0xF);
td->td_savefpu = &td->td_pcb->pcb_save;
td->td_sp = (char *)td->td_pcb;
}
void
pmap_init_proc(struct proc *p)
{
}
static void
pmap_pinit_defaults(struct pmap *pmap)
{
bcopy(pmap_bits_default, pmap->pmap_bits,
sizeof(pmap_bits_default));
bcopy(protection_codes, pmap->protection_codes,
sizeof(protection_codes));
bcopy(pat_pte_index, pmap->pmap_cache_bits_pte,
sizeof(pat_pte_index));
bcopy(pat_pde_index, pmap->pmap_cache_bits_pde,
sizeof(pat_pte_index));
pmap->pmap_cache_mask_pte = X86_PG_NC_PWT | X86_PG_NC_PCD | X86_PG_PTE_PAT;
pmap->pmap_cache_mask_pde = X86_PG_NC_PWT | X86_PG_NC_PCD | X86_PG_PDE_PAT;
pmap->copyinstr = std_copyinstr;
pmap->copyin = std_copyin;
pmap->copyout = std_copyout;
pmap->fubyte = std_fubyte;
pmap->subyte = std_subyte;
pmap->fuword32 = std_fuword32;
pmap->fuword64 = std_fuword64;
pmap->suword32 = std_suword32;
pmap->suword64 = std_suword64;
pmap->swapu32 = std_swapu32;
pmap->swapu64 = std_swapu64;
pmap->fuwordadd32 = std_fuwordadd32;
pmap->fuwordadd64 = std_fuwordadd64;
}
void
pmap_pinit0(struct pmap *pmap)
{
int i;
pmap->pm_pml4 = (pml4_entry_t *)(PTOV_OFFSET + KPML4phys);
pmap->pm_count = 1;
CPUMASK_ASSZERO(pmap->pm_active);
pmap->pm_pvhint_pt = NULL;
pmap->pm_pvhint_unused = NULL;
RB_INIT(&pmap->pm_pvroot);
spin_init(&pmap->pm_spin, "pmapinit0");
for (i = 0; i < PM_PLACEMARKS; ++i)
pmap->pm_placemarks[i] = PM_NOPLACEMARK;
bzero(&pmap->pm_stats, sizeof pmap->pm_stats);
pmap_pinit_defaults(pmap);
}
static void
pmap_pinit_simple(struct pmap *pmap)
{
int i;
pmap->pm_count = 1;
CPUMASK_ASSZERO(pmap->pm_active);
pmap->pm_pvhint_pt = NULL;
pmap->pm_pvhint_unused = NULL;
pmap->pm_flags = PMAP_FLAG_SIMPLE;
pmap_pinit_defaults(pmap);
if (pmap->pm_pmlpv == NULL) {
RB_INIT(&pmap->pm_pvroot);
bzero(&pmap->pm_stats, sizeof pmap->pm_stats);
spin_init(&pmap->pm_spin, "pmapinitsimple");
for (i = 0; i < PM_PLACEMARKS; ++i)
pmap->pm_placemarks[i] = PM_NOPLACEMARK;
}
}
void
pmap_pinit(struct pmap *pmap)
{
pv_entry_t pv;
int j;
if (pmap->pm_pmlpv) {
if (pmap->pmap_bits[TYPE_IDX] != REGULAR_PMAP) {
pmap_puninit(pmap);
}
}
pmap_pinit_simple(pmap);
pmap->pm_flags &= ~PMAP_FLAG_SIMPLE;
if (pmap->pm_pml4 == NULL) {
pmap->pm_pml4 =
(pml4_entry_t *)kmem_alloc_pageable(kernel_map,
PAGE_SIZE * 2,
VM_SUBSYS_PML4);
pmap->pm_pml4_iso = (void *)((char *)pmap->pm_pml4 + PAGE_SIZE);
}
if ((pv = pmap->pm_pmlpv) == NULL) {
pv = pmap_allocpte(pmap, pmap_pml4_pindex(), NULL);
pmap->pm_pmlpv = pv;
pmap_kenter((vm_offset_t)pmap->pm_pml4,
VM_PAGE_TO_PHYS(pv->pv_m));
pv_put(pv);
for (j = 0; j < NDMPML4E; ++j) {
pmap->pm_pml4[DMPML4I + j] =
(DMPDPphys + ((vm_paddr_t)j << PAGE_SHIFT)) |
pmap->pmap_bits[PG_RW_IDX] |
pmap->pmap_bits[PG_V_IDX] |
pmap->pmap_bits[PG_A_IDX];
}
for (j = 0; j < NKPML4E; ++j) {
pmap->pm_pml4[KPML4I + j] =
(KPDPphys + ((vm_paddr_t)j << PAGE_SHIFT)) |
pmap->pmap_bits[PG_RW_IDX] |
pmap->pmap_bits[PG_V_IDX] |
pmap->pmap_bits[PG_A_IDX];
}
pmap->pm_pml4[PML4PML4I] = VM_PAGE_TO_PHYS(pv->pv_m) |
pmap->pmap_bits[PG_V_IDX] |
pmap->pmap_bits[PG_RW_IDX] |
pmap->pmap_bits[PG_A_IDX];
} else {
KKASSERT(pv->pv_m->flags & PG_MAPPED);
KKASSERT(pv->pv_m->flags & PG_WRITEABLE);
}
KKASSERT(pmap->pm_pml4[255] == 0);
if ((pv = pmap->pm_pmlpv_iso) == NULL && meltdown_mitigation &&
pmap != &iso_pmap) {
pv = pmap_allocpte(pmap, pmap_pml4_pindex() + 1, NULL);
pmap->pm_pmlpv_iso = pv;
pmap_kenter((vm_offset_t)pmap->pm_pml4_iso,
VM_PAGE_TO_PHYS(pv->pv_m));
pv_put(pv);
for (j = 0; j < NKPML4E; ++j) {
pmap->pm_pml4_iso[KPML4I + j] =
iso_pmap.pm_pml4[KPML4I + j];
}
} else if (pv) {
KKASSERT(pv->pv_m->flags & PG_MAPPED);
KKASSERT(pv->pv_m->flags & PG_WRITEABLE);
}
}
void
pmap_puninit(pmap_t pmap)
{
pv_entry_t pv;
vm_page_t p;
KKASSERT(CPUMASK_TESTZERO(pmap->pm_active));
if ((pv = pmap->pm_pmlpv) != NULL) {
if (pv_hold_try(pv) == 0)
pv_lock(pv);
KKASSERT(pv == pmap->pm_pmlpv);
p = pmap_remove_pv_page(pv, 1);
pv_free(pv, NULL);
pv = NULL;
pmap_kremove((vm_offset_t)pmap->pm_pml4);
vm_page_busy_wait(p, FALSE, "pgpun");
KKASSERT(p->flags & PG_UNQUEUED);
vm_page_unwire(p, 0);
vm_page_flag_clear(p, PG_MAPPED | PG_WRITEABLE);
vm_page_free(p);
pmap->pm_pmlpv = NULL;
}
if ((pv = pmap->pm_pmlpv_iso) != NULL) {
if (pv_hold_try(pv) == 0)
pv_lock(pv);
KKASSERT(pv == pmap->pm_pmlpv_iso);
p = pmap_remove_pv_page(pv, 1);
pv_free(pv, NULL);
pv = NULL;
pmap_kremove((vm_offset_t)pmap->pm_pml4_iso);
vm_page_busy_wait(p, FALSE, "pgpun");
KKASSERT(p->flags & PG_UNQUEUED);
vm_page_unwire(p, 0);
vm_page_flag_clear(p, PG_MAPPED | PG_WRITEABLE);
vm_page_free(p);
pmap->pm_pmlpv_iso = NULL;
}
if (pmap->pm_pml4) {
KKASSERT(pmap->pm_pml4 != (void *)(PTOV_OFFSET + KPML4phys));
kmem_free(kernel_map,
(vm_offset_t)pmap->pm_pml4, PAGE_SIZE * 2);
pmap->pm_pml4 = NULL;
pmap->pm_pml4_iso = NULL;
}
KKASSERT(pmap->pm_stats.resident_count == 0);
KKASSERT(pmap->pm_stats.wired_count == 0);
}
void
pmap_pinit2(struct pmap *pmap)
{
}
void
pmap_ept_transform(pmap_t pmap, int flags)
{
uint64_t pmap_bits_ept[PG_BITS_SIZE] = {
[TYPE_IDX] = EPT_PMAP,
[PG_V_IDX] = EPT_PG_READ | EPT_PG_EXECUTE,
[PG_RW_IDX] = EPT_PG_WRITE,
[PG_U_IDX] = 0,
[PG_A_IDX] = EPT_PG_A,
[PG_M_IDX] = EPT_PG_M,
[PG_PS_IDX] = EPT_PG_PS,
[PG_G_IDX] = 0,
[PG_W_IDX] = EPT_PG_AVAIL1,
[PG_MANAGED_IDX] = EPT_PG_AVAIL2,
[PG_N_IDX] = EPT_PG_IGNORE_PAT | EPT_MEM_TYPE_UC,
[PG_NX_IDX] = 0,
};
uint64_t protection_codes_ept[PROTECTION_CODES_SIZE] = {
[VM_PROT_NONE | VM_PROT_NONE | VM_PROT_NONE ] = 0,
[VM_PROT_READ | VM_PROT_NONE | VM_PROT_NONE ] = 0,
[VM_PROT_READ | VM_PROT_NONE | VM_PROT_EXECUTE] = 0,
[VM_PROT_NONE | VM_PROT_NONE | VM_PROT_EXECUTE] = 0,
[VM_PROT_NONE | VM_PROT_WRITE | VM_PROT_NONE ] =
pmap_bits_ept[PG_RW_IDX],
[VM_PROT_NONE | VM_PROT_WRITE | VM_PROT_EXECUTE] =
pmap_bits_ept[PG_RW_IDX],
[VM_PROT_READ | VM_PROT_WRITE | VM_PROT_NONE ] =
pmap_bits_ept[PG_RW_IDX],
[VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE] =
pmap_bits_ept[PG_RW_IDX],
};
pt_entry_t pmap_cache_bits_ept[PAT_INDEX_SIZE] = {
[PAT_UNCACHEABLE] = EPT_PG_IGNORE_PAT | EPT_MEM_TYPE_UC,
[PAT_WRITE_COMBINING] = EPT_PG_IGNORE_PAT | EPT_MEM_TYPE_WC,
[PAT_WRITE_THROUGH] = EPT_PG_IGNORE_PAT | EPT_MEM_TYPE_WT,
[PAT_WRITE_PROTECTED] = EPT_PG_IGNORE_PAT | EPT_MEM_TYPE_WP,
[PAT_WRITE_BACK] = EPT_PG_IGNORE_PAT | EPT_MEM_TYPE_WB,
[PAT_UNCACHED] = EPT_PG_IGNORE_PAT | EPT_MEM_TYPE_UC,
};
pt_entry_t pmap_cache_mask_ept = EPT_PG_IGNORE_PAT | EPT_MEM_TYPE_MASK;
pmap->pm_flags |= (flags | PMAP_HVM);
bcopy(pmap_bits_ept, pmap->pmap_bits, sizeof(pmap_bits_ept));
bcopy(protection_codes_ept, pmap->protection_codes,
sizeof(protection_codes_ept));
bcopy(pmap_cache_bits_ept, pmap->pmap_cache_bits_pte,
sizeof(pmap_cache_bits_ept));
bcopy(pmap_cache_bits_ept, pmap->pmap_cache_bits_pde,
sizeof(pmap_cache_bits_ept));
pmap->pmap_cache_mask_pte = pmap_cache_mask_ept;
pmap->pmap_cache_mask_pde = pmap_cache_mask_ept;
if (pmap->pm_pmlpv_iso != NULL)
bzero(pmap->pm_pml4, PAGE_SIZE * 2);
else
bzero(pmap->pm_pml4, PAGE_SIZE);
}
void
pmap_npt_transform(pmap_t pmap, int flags)
{
uint64_t protection_codes_npt[PROTECTION_CODES_SIZE] = {
[VM_PROT_NONE | VM_PROT_NONE | VM_PROT_NONE ] = 0,
[VM_PROT_READ | VM_PROT_NONE | VM_PROT_NONE ] = 0,
[VM_PROT_READ | VM_PROT_NONE | VM_PROT_EXECUTE] = 0,
[VM_PROT_NONE | VM_PROT_NONE | VM_PROT_EXECUTE] = 0,
[VM_PROT_NONE | VM_PROT_WRITE | VM_PROT_NONE ] =
pmap_bits_default[PG_RW_IDX],
[VM_PROT_NONE | VM_PROT_WRITE | VM_PROT_EXECUTE] =
pmap_bits_default[PG_RW_IDX],
[VM_PROT_READ | VM_PROT_WRITE | VM_PROT_NONE ] =
pmap_bits_default[PG_RW_IDX],
[VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE] =
pmap_bits_default[PG_RW_IDX],
};
pmap->pm_flags |= (flags | PMAP_HVM);
pmap->pmap_bits[TYPE_IDX] = NPT_PMAP;
pmap->pmap_bits[PG_G_IDX] = 0;
pmap->pmap_bits[PG_NX_IDX] = 0;
bcopy(protection_codes_npt, pmap->protection_codes,
sizeof(protection_codes_npt));
if (pmap->pm_pmlpv_iso != NULL)
bzero(pmap->pm_pml4, PAGE_SIZE * 2);
else
bzero(pmap->pm_pml4, PAGE_SIZE);
}
static
pv_entry_t
pmap_allocpte(pmap_t pmap, vm_pindex_t ptepindex, pv_entry_t *pvpp)
{
pt_entry_t *ptep;
pt_entry_t *ptep_iso;
pv_entry_t pv;
pv_entry_t pvp;
pt_entry_t v;
vm_page_t m;
int isnew;
int ispt;
ispt = 0;
pv = pv_alloc(pmap, ptepindex, &isnew);
if (isnew == 0 && pvpp == NULL)
return(pv);
KKASSERT(ptepindex >= pmap_pt_pindex(0));
KKASSERT(pmap != kernel_pmap);
if (ptepindex < pmap_pd_pindex(0)) {
ptepindex = (ptepindex - pmap_pt_pindex(0)) >> NPDEPGSHIFT;
ptepindex += NUPTE_TOTAL + NUPT_TOTAL;
pvp = pmap_allocpte(pmap, ptepindex, NULL);
ptepindex = pv->pv_pindex - pmap_pt_pindex(0);
ptepindex &= ((1ul << NPDEPGSHIFT) - 1);
ispt = 1;
} else if (ptepindex < pmap_pdp_pindex(0)) {
ptepindex = (ptepindex - pmap_pd_pindex(0)) >> NPDPEPGSHIFT;
ptepindex += NUPTE_TOTAL + NUPT_TOTAL + NUPD_TOTAL;
if (pmap->pm_flags & PMAP_FLAG_SIMPLE) {
KKASSERT(pvpp == NULL);
pvp = NULL;
} else {
pvp = pmap_allocpte(pmap, ptepindex, NULL);
}
ptepindex = pv->pv_pindex - pmap_pd_pindex(0);
ptepindex &= ((1ul << NPDPEPGSHIFT) - 1);
} else if (ptepindex < pmap_pml4_pindex()) {
pvp = pmap_allocpte(pmap, pmap_pml4_pindex(), NULL);
ptepindex = pv->pv_pindex - pmap_pdp_pindex(0);
ptepindex &= ((1ul << NPML4EPGSHIFT) - 1);
} else {
pvp = NULL;
}
if (isnew == 0)
goto notnew;
if (pvp)
vm_page_wire_quick(pvp->pv_m);
for (;;) {
m = vm_page_alloc(NULL, pv->pv_pindex,
VM_ALLOC_NORMAL | VM_ALLOC_SYSTEM |
VM_ALLOC_INTERRUPT);
if (m)
break;
vm_wait(0);
}
vm_page_wire(m);
pmap_zero_page(VM_PAGE_TO_PHYS(m));
m->valid = VM_PAGE_BITS_ALL;
vm_page_flag_set(m, PG_MAPPED | PG_WRITEABLE | PG_UNQUEUED);
KKASSERT(m->queue == PQ_NONE);
pv->pv_m = m;
if (pvp) {
v = VM_PAGE_TO_PHYS(m) |
(pmap->pmap_bits[PG_RW_IDX] |
pmap->pmap_bits[PG_V_IDX] |
pmap->pmap_bits[PG_A_IDX]);
if (ptepindex < NUPTE_USER)
v |= pmap->pmap_bits[PG_U_IDX];
if (ptepindex < pmap_pt_pindex(0))
v |= pmap->pmap_bits[PG_M_IDX];
ptep = pv_pte_lookup(pvp, ptepindex);
if (pvp == pmap->pm_pmlpv && pmap->pm_pmlpv_iso)
ptep_iso = pv_pte_lookup(pmap->pm_pmlpv_iso, ptepindex);
else
ptep_iso = NULL;
if (*ptep & pmap->pmap_bits[PG_V_IDX]) {
panic("pmap_allocpte: ptpte present without pv_entry!");
} else {
pt_entry_t pte;
pte = atomic_swap_long(ptep, v);
if (ptep_iso)
atomic_swap_long(ptep_iso, v);
if (pte != 0) {
kprintf("install pgtbl mixup 0x%016jx "
"old/new 0x%016jx/0x%016jx\n",
(intmax_t)ptepindex, pte, v);
}
}
}
vm_page_wakeup(m);
notnew:
if (pvp) {
KKASSERT(pvp->pv_m != NULL);
ptep = pv_pte_lookup(pvp, ptepindex);
v = VM_PAGE_TO_PHYS(pv->pv_m) |
(pmap->pmap_bits[PG_RW_IDX] |
pmap->pmap_bits[PG_V_IDX] |
pmap->pmap_bits[PG_A_IDX]);
if (ptepindex < NUPTE_USER)
v |= pmap->pmap_bits[PG_U_IDX];
if (ptepindex < pmap_pt_pindex(0))
v |= pmap->pmap_bits[PG_M_IDX];
if (*ptep != v) {
kprintf("mismatched upper level pt %016jx/%016jx\n",
*ptep, v);
}
}
if (pvpp)
*pvpp = pvp;
else if (pvp)
pv_put(pvp);
return (pv);
}
struct pmap_release_info {
pmap_t pmap;
int retry;
pv_entry_t pvp;
};
static int pmap_release_callback(pv_entry_t pv, void *data);
void
pmap_release(struct pmap *pmap)
{
struct pmap_release_info info;
KASSERT(CPUMASK_TESTZERO(pmap->pm_active),
("pmap still active! %016jx",
(uintmax_t)CPUMASK_LOWMASK(pmap->pm_active)));
info.pmap = pmap;
do {
info.retry = 0;
info.pvp = NULL;
spin_lock(&pmap->pm_spin);
RB_SCAN(pv_entry_rb_tree, &pmap->pm_pvroot, NULL,
pmap_release_callback, &info);
spin_unlock(&pmap->pm_spin);
if (info.pvp)
pv_put(info.pvp);
} while (info.retry);
int expected_res = 0;
if ((pmap->pm_flags & PMAP_FLAG_SIMPLE) == 0)
++expected_res;
if (pmap->pm_pmlpv_iso)
++expected_res;
#if 1
if (pmap->pm_stats.resident_count != expected_res ||
pmap->pm_stats.wired_count != 0) {
kprintf("fatal pmap problem - pmap %p flags %08x "
"rescnt=%jd wirecnt=%jd\n",
pmap,
pmap->pm_flags,
pmap->pm_stats.resident_count,
pmap->pm_stats.wired_count);
tsleep(pmap, 0, "DEAD", 0);
}
#else
KKASSERT(pmap->pm_stats.resident_count == expected_res);
KKASSERT(pmap->pm_stats.wired_count == 0);
#endif
}
static int
pmap_release_callback(pv_entry_t pv, void *data)
{
struct pmap_release_info *info = data;
pmap_t pmap = info->pmap;
vm_pindex_t pindex;
int r;
pindex = pv->pv_pindex;
if (info->pvp == pv) {
spin_unlock(&pmap->pm_spin);
info->pvp = NULL;
} else if (pv_hold_try(pv)) {
spin_unlock(&pmap->pm_spin);
} else {
spin_unlock(&pmap->pm_spin);
pv_lock(pv);
pv_put(pv);
info->retry = 1;
spin_lock(&pmap->pm_spin);
return -1;
}
KKASSERT(pv->pv_pmap == pmap && pindex == pv->pv_pindex);
if (pv->pv_pindex < pmap_pt_pindex(0)) {
pindex = pv->pv_pindex >> NPTEPGSHIFT;
pindex += NUPTE_TOTAL;
} else if (pv->pv_pindex < pmap_pd_pindex(0)) {
pindex = (pv->pv_pindex - NUPTE_TOTAL) >> NPDEPGSHIFT;
pindex += NUPTE_TOTAL + NUPT_TOTAL;
} else if (pv->pv_pindex < pmap_pdp_pindex(0)) {
pindex = (pv->pv_pindex - NUPTE_TOTAL - NUPT_TOTAL) >>
NPDPEPGSHIFT;
pindex += NUPTE_TOTAL + NUPT_TOTAL + NUPD_TOTAL;
} else if (pv->pv_pindex < pmap_pml4_pindex()) {
pindex = pmap_pml4_pindex();
} else {
if (info->pvp) {
pv_put(info->pvp);
info->pvp = NULL;
}
pindex = 0;
}
if (pindex) {
if (info->pvp && info->pvp->pv_pindex != pindex) {
pv_put(info->pvp);
info->pvp = NULL;
}
if (info->pvp == NULL)
info->pvp = pv_get(pmap, pindex, NULL);
} else {
if (info->pvp) {
pv_put(info->pvp);
info->pvp = NULL;
}
}
r = pmap_release_pv(pv, info->pvp, NULL);
spin_lock(&pmap->pm_spin);
return(r);
}
static int
pmap_release_pv(pv_entry_t pv, pv_entry_t pvp, pmap_inval_bulk_t *bulk)
{
vm_page_t p;
pmap_remove_pv_pte(pv, pvp, bulk, 0);
if (pv->pv_pindex < pmap_pt_pindex(0)) {
pmap_remove_pv_page(pv, 0);
goto skip;
}
if (pv->pv_pindex >= pmap_pml4_pindex()) {
pv_put(pv);
return(-1);
}
p = pmap_remove_pv_page(pv, 1);
vm_page_busy_wait(p, FALSE, "pmaprl");
if (p->wire_count != 1) {
const char *tstr;
if (pv->pv_pindex >= pmap_pdp_pindex(0))
tstr = "PDP";
else if (pv->pv_pindex >= pmap_pd_pindex(0))
tstr = "PD";
else if (pv->pv_pindex >= pmap_pt_pindex(0))
tstr = "PT";
else
tstr = "PTE";
kprintf("p(%s) p->wire_count was %016lx %d\n",
tstr, pv->pv_pindex, p->wire_count);
}
KKASSERT(p->wire_count == 1);
KKASSERT(p->flags & PG_UNQUEUED);
vm_page_unwire(p, 0);
KKASSERT(p->wire_count == 0);
vm_page_free(p);
skip:
pv_free(pv, pvp);
return 0;
}
static
void
pmap_remove_pv_pte(pv_entry_t pv, pv_entry_t pvp, pmap_inval_bulk_t *bulk,
int destroy)
{
vm_pindex_t ptepindex = pv->pv_pindex;
pmap_t pmap = pv->pv_pmap;
vm_page_t p;
int gotpvp = 0;
KKASSERT(pmap);
if (ptepindex >= pmap_pml4_pindex()) {
p = pmap->pm_pmlpv->pv_m;
KKASSERT(pv->pv_m == p);
} else if (ptepindex >= pmap_pdp_pindex(0)) {
vm_pindex_t pml4_pindex;
vm_pindex_t pdp_index;
pml4_entry_t *pdp;
pml4_entry_t *pdp_iso;
pdp_index = ptepindex - pmap_pdp_pindex(0);
if (pvp == NULL) {
pml4_pindex = pmap_pml4_pindex();
pvp = pv_get(pv->pv_pmap, pml4_pindex, NULL);
KKASSERT(pvp);
gotpvp = 1;
}
pdp = &pmap->pm_pml4[pdp_index & ((1ul << NPML4EPGSHIFT) - 1)];
KKASSERT((*pdp & pmap->pmap_bits[PG_V_IDX]) != 0);
p = PHYS_TO_VM_PAGE(*pdp & PG_FRAME);
pmap_inval_bulk(bulk, (vm_offset_t)-1, pdp, 0);
if (pvp == pmap->pm_pmlpv && pmap->pm_pmlpv_iso) {
pdp_iso = &pmap->pm_pml4_iso[pdp_index &
((1ul << NPML4EPGSHIFT) - 1)];
pmap_inval_bulk(bulk, (vm_offset_t)-1, pdp_iso, 0);
}
KKASSERT(pv->pv_m == p);
} else if (ptepindex >= pmap_pd_pindex(0)) {
vm_pindex_t pdp_pindex;
vm_pindex_t pd_index;
pdp_entry_t *pd;
pd_index = ptepindex - pmap_pd_pindex(0);
if (pvp == NULL) {
pdp_pindex = NUPTE_TOTAL + NUPT_TOTAL + NUPD_TOTAL +
(pd_index >> NPML4EPGSHIFT);
pvp = pv_get(pv->pv_pmap, pdp_pindex, NULL);
gotpvp = 1;
}
if (pvp) {
pd = pv_pte_lookup(pvp, pd_index &
((1ul << NPDPEPGSHIFT) - 1));
KKASSERT((*pd & pmap->pmap_bits[PG_V_IDX]) != 0);
p = PHYS_TO_VM_PAGE(*pd & PG_FRAME);
pmap_inval_bulk(bulk, (vm_offset_t)-1, pd, 0);
} else {
KKASSERT(pmap->pm_flags & PMAP_FLAG_SIMPLE);
p = pv->pv_m;
}
KKASSERT(pv->pv_m == p);
} else if (ptepindex >= pmap_pt_pindex(0)) {
vm_pindex_t pd_pindex;
vm_pindex_t pt_index;
pd_entry_t *pt;
pt_index = ptepindex - pmap_pt_pindex(0);
if (pvp == NULL) {
pd_pindex = NUPTE_TOTAL + NUPT_TOTAL +
(pt_index >> NPDPEPGSHIFT);
pvp = pv_get(pv->pv_pmap, pd_pindex, NULL);
KKASSERT(pvp);
gotpvp = 1;
}
pt = pv_pte_lookup(pvp, pt_index & ((1ul << NPDPEPGSHIFT) - 1));
#if 0
KASSERT((*pt & pmap->pmap_bits[PG_V_IDX]) != 0,
("*pt unexpectedly invalid %016jx "
"gotpvp=%d ptepindex=%ld ptindex=%ld pv=%p pvp=%p",
*pt, gotpvp, ptepindex, pt_index, pv, pvp));
p = PHYS_TO_VM_PAGE(*pt & PG_FRAME);
#else
if ((*pt & pmap->pmap_bits[PG_V_IDX]) == 0) {
kprintf("*pt unexpectedly invalid %016jx "
"gotpvp=%d ptepindex=%ld ptindex=%ld "
"pv=%p pvp=%p\n",
*pt, gotpvp, ptepindex, pt_index, pv, pvp);
tsleep(pt, 0, "DEAD", 0);
p = pv->pv_m;
} else {
p = PHYS_TO_VM_PAGE(*pt & PG_FRAME);
}
#endif
pmap_inval_bulk(bulk, (vm_offset_t)-1, pt, 0);
KKASSERT(pv->pv_m == p);
} else {
KKASSERT(0);
}
if (destroy == 1) {
KKASSERT(pv->pv_m->wire_count == 1);
p = pmap_remove_pv_page(pv, 1);
pv_free(pv, pvp);
pv = NULL;
vm_page_busy_wait(p, FALSE, "pgpun");
vm_page_unwire(p, 0);
vm_page_flag_clear(p, PG_MAPPED | PG_WRITEABLE);
vm_page_free(p);
}
if (pvp && gotpvp) {
if (pmap_dynamic_delete &&
pvp->pv_m &&
pvp->pv_m->wire_count == 1 &&
(pvp->pv_hold & PV_HOLD_MASK) == 2 &&
pvp->pv_pindex < pmap_pml4_pindex()) {
if (pmap != kernel_pmap) {
pmap_remove_pv_pte(pvp, NULL, bulk, 1);
pvp = NULL;
} else {
kprintf("Attempt to remove kernel_pmap pindex "
"%jd\n", pvp->pv_pindex);
pv_put(pvp);
}
} else {
pv_put(pvp);
}
}
}
static
vm_page_t
pmap_remove_pv_page(pv_entry_t pv, int clrpgbits)
{
vm_page_t m;
m = pv->pv_m;
pv->pv_m = NULL;
if (clrpgbits)
vm_page_flag_clear(m, PG_MAPPED | PG_WRITEABLE);
return(m);
}
void
pmap_growkernel(vm_offset_t kstart, vm_offset_t kend)
{
vm_paddr_t paddr;
vm_offset_t ptppaddr;
vm_page_t nkpg;
pd_entry_t *pt, newpt;
pdp_entry_t *pd, newpd;
int update_kernel_vm_end;
if (kernel_vm_end == 0) {
kernel_vm_end = VM_MIN_KERNEL_ADDRESS;
for (;;) {
pt = pmap_pt(kernel_pmap, kernel_vm_end);
if (pt == NULL)
break;
if ((*pt & kernel_pmap->pmap_bits[PG_V_IDX]) == 0)
break;
kernel_vm_end = (kernel_vm_end + PAGE_SIZE * NPTEPG) &
~(vm_offset_t)(PAGE_SIZE * NPTEPG - 1);
if (kernel_vm_end - 1 >= vm_map_max(kernel_map)) {
kernel_vm_end = vm_map_max(kernel_map);
break;
}
}
}
if (kstart < KERNBASE) {
if (kstart > kernel_vm_end)
kstart = kernel_vm_end;
KKASSERT(kend <= KERNBASE);
update_kernel_vm_end = 1;
} else {
update_kernel_vm_end = 0;
}
kstart = rounddown2(kstart, (vm_offset_t)(PAGE_SIZE * NPTEPG));
kend = roundup2(kend, (vm_offset_t)(PAGE_SIZE * NPTEPG));
if (kend - 1 >= vm_map_max(kernel_map))
kend = vm_map_max(kernel_map);
while (kstart < kend) {
pt = pmap_pt(kernel_pmap, kstart);
if (pt == NULL) {
nkpg = vm_page_alloc(NULL, mycpu->gd_rand_incr++,
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);
pd = pmap_pd(kernel_pmap, kstart);
newpd = (pdp_entry_t)
(paddr |
kernel_pmap->pmap_bits[PG_V_IDX] |
kernel_pmap->pmap_bits[PG_RW_IDX] |
kernel_pmap->pmap_bits[PG_A_IDX]);
atomic_swap_long(pd, newpd);
#if 0
kprintf("NEWPD pd=%p pde=%016jx phys=%016jx\n",
pd, newpd, paddr);
#endif
continue;
}
if ((*pt & kernel_pmap->pmap_bits[PG_V_IDX]) != 0) {
kstart = (kstart + PAGE_SIZE * NPTEPG) &
~(vm_offset_t)(PAGE_SIZE * NPTEPG - 1);
if (kstart - 1 >= vm_map_max(kernel_map)) {
kstart = vm_map_max(kernel_map);
break;
}
continue;
}
nkpg = vm_page_alloc(NULL, mycpu->gd_rand_incr++,
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);
newpt = (pd_entry_t)(ptppaddr |
kernel_pmap->pmap_bits[PG_V_IDX] |
kernel_pmap->pmap_bits[PG_RW_IDX] |
kernel_pmap->pmap_bits[PG_A_IDX]);
atomic_swap_long(pt, newpt);
kstart = (kstart + PAGE_SIZE * NPTEPG) &
~(vm_offset_t)(PAGE_SIZE * NPTEPG - 1);
if (kstart - 1 >= vm_map_max(kernel_map)) {
kstart = vm_map_max(kernel_map);
break;
}
}
if (update_kernel_vm_end && kernel_vm_end < kstart)
kernel_vm_end = kstart;
}
void
pmap_reference(pmap_t pmap)
{
if (pmap != NULL)
atomic_add_int(&pmap->pm_count, 1);
}
void
pmap_maybethreaded(pmap_t pmap)
{
atomic_set_int(&pmap->pm_flags, PMAP_MULTI);
}
int
pmap_mapped_sync(vm_page_t m)
{
return (m->flags);
}
#if 0
static void
pv_hold(pv_entry_t pv)
{
atomic_add_int(&pv->pv_hold, 1);
}
#endif
static int
_pv_hold_try(pv_entry_t pv PMAP_DEBUG_DECL)
{
u_int count;
count = pv->pv_hold;
cpu_ccfence();
for (;;) {
if ((count & PV_HOLD_LOCKED) == 0) {
if (atomic_fcmpset_int(&pv->pv_hold, &count,
(count + 1) | PV_HOLD_LOCKED)) {
#ifdef PMAP_DEBUG
pv->pv_func = func;
pv->pv_line = lineno;
#endif
return TRUE;
}
} else {
if (atomic_fcmpset_int(&pv->pv_hold, &count, count + 1))
return FALSE;
}
}
}
static void
pv_drop(pv_entry_t pv)
{
u_int count;
for (;;) {
count = pv->pv_hold;
cpu_ccfence();
KKASSERT((count & PV_HOLD_MASK) > 0);
KKASSERT((count & (PV_HOLD_LOCKED | PV_HOLD_MASK)) !=
(PV_HOLD_LOCKED | 1));
if (atomic_cmpset_int(&pv->pv_hold, count, count - 1)) {
if ((count & PV_HOLD_MASK) == 1) {
#ifdef PMAP_DEBUG2
if (pmap_enter_debug > 0) {
--pmap_enter_debug;
kprintf("pv_drop: free pv %p\n", pv);
}
#endif
KKASSERT(count == 1);
KKASSERT(pv->pv_pmap == NULL);
zfree(pvzone, pv);
}
return;
}
}
}
static
pv_entry_t
_pv_alloc(pmap_t pmap, vm_pindex_t pindex, int *isnew PMAP_DEBUG_DECL)
{
struct mdglobaldata *md = mdcpu;
pv_entry_t pv;
pv_entry_t pnew;
int pmap_excl = 0;
pnew = NULL;
if (md->gd_newpv) {
#if 1
pnew = atomic_swap_ptr((void *)&md->gd_newpv, NULL);
#else
crit_enter();
pnew = md->gd_newpv;
md->gd_newpv = NULL;
crit_exit();
#endif
}
if (pnew == NULL)
pnew = zalloc(pvzone);
spin_lock_shared(&pmap->pm_spin);
for (;;) {
pv = pv_entry_lookup(pmap, pindex);
if (pv == NULL) {
vm_pindex_t *pmark;
if (pmap_excl == 0) {
pmap_excl = 1;
if (!spin_lock_upgrade_try(&pmap->pm_spin)) {
spin_unlock_shared(&pmap->pm_spin);
spin_lock(&pmap->pm_spin);
continue;
}
}
pmark = pmap_placemarker_hash(pmap, pindex);
if (((*pmark ^ pindex) & ~PM_PLACEMARK_WAKEUP) == 0) {
tsleep_interlock(pmark, 0);
atomic_set_long(pmark, PM_PLACEMARK_WAKEUP);
if (((*pmark ^ pindex) &
~PM_PLACEMARK_WAKEUP) == 0) {
spin_unlock(&pmap->pm_spin);
tsleep(pmark, PINTERLOCKED, "pvplc", 0);
spin_lock(&pmap->pm_spin);
}
continue;
}
pnew->pv_pmap = pmap;
pnew->pv_pindex = pindex;
pnew->pv_hold = PV_HOLD_LOCKED | 2;
pnew->pv_flags = 0;
#ifdef PMAP_DEBUG
pnew->pv_func = func;
pnew->pv_line = lineno;
if (pnew->pv_line_lastfree > 0) {
pnew->pv_line_lastfree =
-pnew->pv_line_lastfree;
}
#endif
pv = pv_entry_rb_tree_RB_INSERT(&pmap->pm_pvroot, pnew);
atomic_add_long(&pmap->pm_stats.resident_count, 1);
spin_unlock(&pmap->pm_spin);
*isnew = 1;
KASSERT(pv == NULL, ("pv insert failed %p->%p", pnew, pv));
return(pnew);
}
if (__predict_true(_pv_hold_try(pv PMAP_DEBUG_COPY))) {
if (pmap_excl)
spin_unlock(&pmap->pm_spin);
else
spin_unlock_shared(&pmap->pm_spin);
#if 1
pnew = atomic_swap_ptr((void *)&md->gd_newpv, pnew);
if (pnew)
zfree(pvzone, pnew);
#else
crit_enter();
if (md->gd_newpv == NULL)
md->gd_newpv = pnew;
else
zfree(pvzone, pnew);
crit_exit();
#endif
KKASSERT(pv->pv_pmap == pmap &&
pv->pv_pindex == pindex);
*isnew = 0;
return(pv);
}
if (pmap_excl) {
spin_unlock(&pmap->pm_spin);
_pv_lock(pv PMAP_DEBUG_COPY);
pv_put(pv);
spin_lock(&pmap->pm_spin);
} else {
spin_unlock_shared(&pmap->pm_spin);
_pv_lock(pv PMAP_DEBUG_COPY);
pv_put(pv);
spin_lock_shared(&pmap->pm_spin);
}
}
}
static
pv_entry_t
_pv_get(pmap_t pmap, vm_pindex_t pindex, vm_pindex_t **pmarkp PMAP_DEBUG_DECL)
{
pv_entry_t pv;
int pmap_excl = 0;
spin_lock_shared(&pmap->pm_spin);
for (;;) {
pv = pv_entry_lookup(pmap, pindex);
if (pv == NULL) {
vm_pindex_t *pmark;
if (pmap_excl == 0) {
pmap_excl = 1;
if (!spin_lock_upgrade_try(&pmap->pm_spin)) {
spin_unlock_shared(&pmap->pm_spin);
spin_lock(&pmap->pm_spin);
continue;
}
}
pmark = pmap_placemarker_hash(pmap, pindex);
if ((pmarkp && *pmark != PM_NOPLACEMARK) ||
((*pmark ^ pindex) & ~PM_PLACEMARK_WAKEUP) == 0) {
tsleep_interlock(pmark, 0);
atomic_set_long(pmark, PM_PLACEMARK_WAKEUP);
if ((pmarkp && *pmark != PM_NOPLACEMARK) ||
((*pmark ^ pindex) &
~PM_PLACEMARK_WAKEUP) == 0) {
spin_unlock(&pmap->pm_spin);
tsleep(pmark, PINTERLOCKED, "pvpld", 0);
spin_lock(&pmap->pm_spin);
}
continue;
}
if (pmarkp) {
if (atomic_swap_long(pmark, pindex) !=
PM_NOPLACEMARK) {
panic("_pv_get: pmark race");
}
*pmarkp = pmark;
}
spin_unlock(&pmap->pm_spin);
return NULL;
}
if (_pv_hold_try(pv PMAP_DEBUG_COPY)) {
if (pmap_excl)
spin_unlock(&pmap->pm_spin);
else
spin_unlock_shared(&pmap->pm_spin);
KKASSERT(pv->pv_pmap == pmap &&
pv->pv_pindex == pindex);
return(pv);
}
if (pmap_excl) {
spin_unlock(&pmap->pm_spin);
_pv_lock(pv PMAP_DEBUG_COPY);
pv_put(pv);
spin_lock(&pmap->pm_spin);
} else {
spin_unlock_shared(&pmap->pm_spin);
_pv_lock(pv PMAP_DEBUG_COPY);
pv_put(pv);
spin_lock_shared(&pmap->pm_spin);
}
}
}
static
pv_entry_t
pv_get_try(pmap_t pmap, vm_pindex_t pindex, vm_pindex_t **pmarkp, int *errorp)
{
pv_entry_t pv;
spin_lock_shared(&pmap->pm_spin);
pv = pv_entry_lookup(pmap, pindex);
if (pv == NULL) {
vm_pindex_t *pmark;
pmark = pmap_placemarker_hash(pmap, pindex);
if (((*pmark ^ pindex) & ~PM_PLACEMARK_WAKEUP) == 0) {
*errorp = 1;
} else if (pmarkp &&
atomic_cmpset_long(pmark, PM_NOPLACEMARK, pindex)) {
*errorp = 0;
} else {
*errorp = 1;
}
if (pmarkp)
*pmarkp = pmark;
spin_unlock_shared(&pmap->pm_spin);
return NULL;
}
if (pv_hold_try(pv)) {
spin_unlock_shared(&pmap->pm_spin);
*errorp = 0;
KKASSERT(pv->pv_pmap == pmap && pv->pv_pindex == pindex);
return(pv);
}
spin_unlock_shared(&pmap->pm_spin);
*errorp = 1;
return (pv);
}
static
void
_pv_lock(pv_entry_t pv PMAP_DEBUG_DECL)
{
u_int count;
for (;;) {
count = pv->pv_hold;
cpu_ccfence();
if ((count & PV_HOLD_LOCKED) == 0) {
if (atomic_cmpset_int(&pv->pv_hold, count,
count | PV_HOLD_LOCKED)) {
#ifdef PMAP_DEBUG
pv->pv_func = func;
pv->pv_line = lineno;
#endif
return;
}
continue;
}
tsleep_interlock(pv, 0);
if (atomic_cmpset_int(&pv->pv_hold, count,
count | PV_HOLD_WAITING)) {
#ifdef PMAP_DEBUG2
if (pmap_enter_debug > 0) {
--pmap_enter_debug;
kprintf("pv waiting on %s:%d\n",
pv->pv_func, pv->pv_line);
}
#endif
tsleep(pv, PINTERLOCKED, "pvwait", hz);
}
}
}
static
void
pv_unlock(pv_entry_t pv)
{
u_int count;
for (;;) {
count = pv->pv_hold;
cpu_ccfence();
KKASSERT((count & (PV_HOLD_LOCKED | PV_HOLD_MASK)) >=
(PV_HOLD_LOCKED | 1));
if (atomic_cmpset_int(&pv->pv_hold, count,
count &
~(PV_HOLD_LOCKED | PV_HOLD_WAITING))) {
if (count & PV_HOLD_WAITING)
wakeup(pv);
break;
}
}
}
static
void
pv_put(pv_entry_t pv)
{
#ifdef PMAP_DEBUG2
if (pmap_enter_debug > 0) {
--pmap_enter_debug;
kprintf("pv_put pv=%p hold=%08x\n", pv, pv->pv_hold);
}
#endif
KKASSERT(pv->pv_pmap == NULL || pv->pv_m != NULL);
if (atomic_cmpset_int(&pv->pv_hold, PV_HOLD_LOCKED | 2, 1))
return;
pv_unlock(pv);
pv_drop(pv);
}
static
void
_pv_free(pv_entry_t pv, pv_entry_t pvp PMAP_DEBUG_DECL)
{
pmap_t pmap;
#ifdef PMAP_DEBUG
pv->pv_func_lastfree = func;
pv->pv_line_lastfree = lineno;
#endif
KKASSERT(pv->pv_m == NULL);
KKASSERT((pv->pv_hold & (PV_HOLD_LOCKED|PV_HOLD_MASK)) >=
(PV_HOLD_LOCKED|1));
if ((pmap = pv->pv_pmap) != NULL) {
spin_lock(&pmap->pm_spin);
KKASSERT(pv->pv_pmap == pmap);
if (pmap->pm_pvhint_pt == pv)
pmap->pm_pvhint_pt = NULL;
if (pmap->pm_pvhint_unused == pv)
pmap->pm_pvhint_unused = NULL;
pv_entry_rb_tree_RB_REMOVE(&pmap->pm_pvroot, pv);
atomic_add_long(&pmap->pm_stats.resident_count, -1);
pv->pv_pmap = NULL;
pv->pv_pindex = 0;
spin_unlock(&pmap->pm_spin);
if (pvp) {
if (vm_page_unwire_quick(pvp->pv_m))
panic("_pv_free: bad wirecount on pvp");
}
if (atomic_cmpset_int(&pv->pv_hold, PV_HOLD_LOCKED | 2, 0)) {
#ifdef PMAP_DEBUG2
if (pmap_enter_debug > 0) {
--pmap_enter_debug;
kprintf("pv_free: free pv %p\n", pv);
}
#endif
zfree(pvzone, pv);
return;
}
pv_drop(pv);
}
pv_unlock(pv);
pv_drop(pv);
}
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: pv_entries exhausted -- "
"suggest increasing vm.pmap_pv_entries above %ld\n",
vm_pmap_pv_entries);
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);
}
}
}
struct pmap_scan_info {
struct pmap *pmap;
vm_offset_t sva;
vm_offset_t eva;
vm_pindex_t sva_pd_pindex;
vm_pindex_t eva_pd_pindex;
void (*func)(pmap_t, struct pmap_scan_info *,
vm_pindex_t *, pv_entry_t, vm_offset_t,
pt_entry_t *, void *);
void *arg;
pmap_inval_bulk_t bulk_core;
pmap_inval_bulk_t *bulk;
int count;
int stop;
};
static int pmap_scan_cmp(pv_entry_t pv, void *data);
static int pmap_scan_callback(pv_entry_t pv, void *data);
static void
pmap_scan(struct pmap_scan_info *info, int smp_inval)
{
struct pmap *pmap = info->pmap;
pv_entry_t pt_pv;
pv_entry_t pte_pv;
vm_pindex_t *pte_placemark;
vm_pindex_t *pt_placemark;
pt_entry_t *ptep;
pt_entry_t oldpte;
struct pv_entry dummy_pv;
info->stop = 0;
if (pmap == NULL)
return;
if (info->sva == info->eva)
return;
if (smp_inval) {
info->bulk = &info->bulk_core;
pmap_inval_bulk_init(&info->bulk_core, pmap);
} else {
info->bulk = NULL;
}
#if 0
if (pmap->pm_stats.resident_count == 0) {
return;
}
#endif
info->count = 0;
if (info->sva + PAGE_SIZE == info->eva) {
if (info->sva >= VM_MAX_USER_ADDRESS) {
pt_pv = NULL;
pte_pv = pv_get(pmap, pmap_pte_pindex(info->sva),
&pte_placemark);
KKASSERT(pte_pv == NULL);
ptep = vtopte(info->sva);
} else {
pte_pv = pv_get(pmap, pmap_pte_pindex(info->sva),
&pte_placemark);
KKASSERT(pte_pv == NULL);
pt_pv = pv_get(pmap, pmap_pt_pindex(info->sva),
&pt_placemark);
if (pt_pv == NULL) {
#if 0
KKASSERT(0);
pd_pv = pv_get(pmap,
pmap_pd_pindex(info->sva),
NULL);
if (pd_pv) {
ptep = pv_pte_lookup(pd_pv,
pmap_pt_index(info->sva));
if (*ptep) {
info->func(pmap, info,
pt_placemark, pd_pv,
info->sva, ptep,
info->arg);
} else {
pv_placemarker_wakeup(pmap,
pt_placemark);
}
pv_put(pd_pv);
} else {
pv_placemarker_wakeup(pmap,
pt_placemark);
}
#else
pv_placemarker_wakeup(pmap, pt_placemark);
#endif
pv_placemarker_wakeup(pmap, pte_placemark);
goto fast_skip;
}
ptep = pv_pte_lookup(pt_pv, pmap_pte_index(info->sva));
}
oldpte = *ptep;
cpu_ccfence();
if (oldpte == 0) {
KKASSERT(pte_pv == NULL);
pv_placemarker_wakeup(pmap, pte_placemark);
} else {
KASSERT((oldpte & pmap->pmap_bits[PG_V_IDX]) ==
pmap->pmap_bits[PG_V_IDX],
("badB *ptep %016lx/%016lx sva %016lx pte_pv NULL",
*ptep, oldpte, info->sva));
info->func(pmap, info, pte_placemark, pt_pv,
info->sva, ptep, info->arg);
}
if (pt_pv)
pv_put(pt_pv);
fast_skip:
pmap_inval_bulk_flush(info->bulk);
return;
}
info->sva_pd_pindex = pmap_pd_pindex(info->sva);
info->eva_pd_pindex = pmap_pd_pindex(info->eva - PAGE_SIZE);
if (info->sva >= VM_MAX_USER_ADDRESS) {
bzero(&dummy_pv, sizeof(dummy_pv));
dummy_pv.pv_pindex = info->sva_pd_pindex;
spin_lock(&pmap->pm_spin);
while (dummy_pv.pv_pindex <= info->eva_pd_pindex) {
pmap_scan_callback(&dummy_pv, info);
++dummy_pv.pv_pindex;
if (dummy_pv.pv_pindex < info->sva_pd_pindex)
break;
}
spin_unlock(&pmap->pm_spin);
} else {
spin_lock(&pmap->pm_spin);
pv_entry_rb_tree_RB_SCAN(&pmap->pm_pvroot, pmap_scan_cmp,
pmap_scan_callback, info);
spin_unlock(&pmap->pm_spin);
}
pmap_inval_bulk_flush(info->bulk);
}
static int
pmap_scan_cmp(pv_entry_t pv, void *data)
{
struct pmap_scan_info *info = data;
if (pv->pv_pindex < info->sva_pd_pindex)
return(-1);
if (pv->pv_pindex > info->eva_pd_pindex)
return(1);
return(0);
}
static int
pmap_scan_callback(pv_entry_t pv, void *data)
{
struct pmap_scan_info *info = data;
struct pmap *pmap = info->pmap;
pv_entry_t pd_pv;
pv_entry_t pt_pv;
vm_pindex_t *pt_placemark;
pt_entry_t *ptep;
pt_entry_t oldpte;
vm_offset_t sva;
vm_offset_t eva;
vm_offset_t va_next;
vm_pindex_t pd_pindex;
int error;
if (info->stop)
return -1;
pd_pindex = pv->pv_pindex;
spin_unlock(&pmap->pm_spin);
pv = NULL;
sva = (pd_pindex - pmap_pd_pindex(0)) << PDPSHIFT;
if ((pmap->pm_flags & PMAP_FLAG_SIMPLE) == 0 &&
(sva & PML4_SIGNMASK)) {
sva |= PML4_SIGNMASK;
}
eva = sva + NBPDP;
if (sva < info->sva)
sva = info->sva;
if (eva < info->sva || eva > info->eva)
eva = info->eva;
pd_pv = NULL;
pt_pv = NULL;
for (; sva < eva; sva = va_next) {
if (info->stop)
break;
if (sva >= VM_MAX_USER_ADDRESS) {
if (pt_pv) {
pv_put(pt_pv);
pt_pv = NULL;
}
goto kernel_skip;
}
if (pd_pv == NULL) {
pd_pv = pv_get(pmap, pmap_pd_pindex(sva), NULL);
} else if (pd_pv->pv_pmap != pmap ||
pd_pv->pv_pindex != pmap_pd_pindex(sva)) {
pv_put(pd_pv);
pd_pv = pv_get(pmap, pmap_pd_pindex(sva), NULL);
}
if (pd_pv == NULL) {
va_next = (sva + NBPDP) & ~PDPMASK;
if (va_next < sva)
va_next = eva;
continue;
}
if (pt_pv && (pt_pv->pv_pmap != pmap ||
pt_pv->pv_pindex != pmap_pt_pindex(sva))) {
pv_put(pt_pv);
pt_pv = NULL;
}
if (pt_pv == NULL) {
pt_pv = pv_get_try(pmap, pmap_pt_pindex(sva),
&pt_placemark, &error);
if (error) {
pv_put(pd_pv);
pd_pv = NULL;
if (pt_pv) {
pv_lock(pt_pv);
pv_put(pt_pv);
pt_pv = NULL;
} else {
pv_placemarker_wait(pmap, pt_placemark);
}
va_next = sva;
continue;
}
}
if (pt_pv == NULL) {
#if 0
KKASSERT(0);
ptep = pv_pte_lookup(pd_pv, pmap_pt_index(sva));
if (*ptep & pmap->pmap_bits[PG_V_IDX]) {
info->func(pmap, info, pt_placemark, pd_pv,
sva, ptep, info->arg);
} else {
pv_placemarker_wakeup(pmap, pt_placemark);
}
#else
pv_placemarker_wakeup(pmap, pt_placemark);
#endif
va_next = (sva + NBPDR) & ~PDRMASK;
if (va_next < sva)
va_next = eva;
continue;
}
kernel_skip:
va_next = (sva + NBPDR) & ~PDRMASK;
if (va_next < sva)
va_next = eva;
if (va_next > eva)
va_next = eva;
if (pt_pv)
ptep = pv_pte_lookup(pt_pv, pmap_pte_index(sva));
else
ptep = vtopte(sva);
while (sva < va_next) {
vm_pindex_t *pte_placemark;
pv_entry_t pte_pv;
if ((++info->count & 63) == 0)
lwkt_user_yield();
if (info->stop)
break;
if (*ptep == 0) {
sva += PAGE_SIZE;
++ptep;
continue;
}
cpu_ccfence();
pte_pv = pv_get_try(pmap, pmap_pte_pindex(sva),
&pte_placemark, &error);
KKASSERT(pte_pv == NULL);
if (error) {
if (pd_pv) {
pv_put(pd_pv);
pd_pv = NULL;
}
if (pt_pv) {
pv_put(pt_pv);
pt_pv = NULL;
}
pv_placemarker_wait(pmap, pte_placemark);
va_next = sva;
break;
}
cpu_ccfence();
oldpte = *ptep;
if (oldpte == 0) {
pv_placemarker_wakeup(pmap, pte_placemark);
sva += PAGE_SIZE;
++ptep;
continue;
}
if (pd_pv) {
vm_page_wire_quick(pd_pv->pv_m);
pv_unlock(pd_pv);
}
if (oldpte & pmap->pmap_bits[PG_MANAGED_IDX]) {
KASSERT((oldpte & pmap->pmap_bits[PG_V_IDX]),
("badC *ptep %016lx/%016lx sva %016lx",
*ptep, oldpte, sva));
info->func(pmap, info, pte_placemark, pt_pv,
sva, ptep, info->arg);
} else {
KASSERT((oldpte & pmap->pmap_bits[PG_V_IDX]),
("badD *ptep %016lx/%016lx sva %016lx ",
*ptep, oldpte, sva));
info->func(pmap, info, pte_placemark, pt_pv,
sva, ptep, info->arg);
}
if (pd_pv) {
pv_lock(pd_pv);
if (vm_page_unwire_quick(pd_pv->pv_m)) {
panic("pmap_scan_callback: "
"bad wirecount on pd_pv");
}
if (pd_pv->pv_pmap == NULL) {
va_next = sva;
break;
}
}
if (pt_pv && pt_pv->pv_pmap != pmap)
break;
sva += PAGE_SIZE;
++ptep;
}
}
if (pd_pv) {
pv_put(pd_pv);
pd_pv = NULL;
}
if (pt_pv) {
pv_put(pt_pv);
pt_pv = NULL;
}
if ((++info->count & 7) == 0)
lwkt_user_yield();
spin_lock(&pmap->pm_spin);
return (0);
}
void
pmap_remove(struct pmap *pmap, vm_offset_t sva, vm_offset_t eva)
{
struct pmap_scan_info info;
info.pmap = pmap;
info.sva = sva;
info.eva = eva;
info.func = pmap_remove_callback;
info.arg = NULL;
pmap_scan(&info, 1);
#if 0
cpu_invltlb();
if (eva - sva < 1024*1024) {
while (sva < eva) {
cpu_invlpg((void *)sva);
sva += PAGE_SIZE;
}
}
#endif
}
static void
pmap_remove_noinval(struct pmap *pmap, vm_offset_t sva, vm_offset_t eva)
{
struct pmap_scan_info info;
info.pmap = pmap;
info.sva = sva;
info.eva = eva;
info.func = pmap_remove_callback;
info.arg = NULL;
pmap_scan(&info, 0);
}
static void
pmap_remove_callback(pmap_t pmap, struct pmap_scan_info *info,
vm_pindex_t *pte_placemark, pv_entry_t pt_pv,
vm_offset_t va, pt_entry_t *ptep, void *arg __unused)
{
pt_entry_t pte;
vm_page_t oldm;
pte = *ptep;
if (pte & pmap->pmap_bits[PG_MANAGED_IDX]) {
oldm = PHYS_TO_VM_PAGE(pte & PG_FRAME);
atomic_add_long(&oldm->md.interlock_count, 1);
} else {
oldm = NULL;
}
pte = pmap_inval_bulk(info->bulk, va, ptep, 0);
if (pte & pmap->pmap_bits[PG_MANAGED_IDX]) {
vm_page_t p;
p = PHYS_TO_VM_PAGE(pte & PG_FRAME);
KKASSERT(pte & pmap->pmap_bits[PG_V_IDX]);
if (pte & pmap->pmap_bits[PG_M_IDX])
vm_page_dirty(p);
if (pte & pmap->pmap_bits[PG_A_IDX])
vm_page_flag_set(p, PG_REFERENCED);
pmap_removed_pte(pmap, p, pte);
}
if (pte & pmap->pmap_bits[PG_V_IDX]) {
atomic_add_long(&pmap->pm_stats.resident_count, -1);
if (pt_pv && vm_page_unwire_quick(pt_pv->pv_m))
panic("pmap_remove: insufficient wirecount");
}
if (pte & pmap->pmap_bits[PG_W_IDX])
atomic_add_long(&pmap->pm_stats.wired_count, -1);
if (pte & pmap->pmap_bits[PG_G_IDX])
cpu_invlpg((void *)va);
pv_placemarker_wakeup(pmap, pte_placemark);
if (oldm) {
if ((atomic_fetchadd_long(&oldm->md.interlock_count, -1) &
0x7FFFFFFFFFFFFFFFLU) == 0x4000000000000001LU) {
atomic_clear_long(&oldm->md.interlock_count,
0x4000000000000000LU);
wakeup(&oldm->md.interlock_count);
}
}
}
static
void
pmap_remove_all(vm_page_t m)
{
long icount;
int retry;
if (__predict_false(!pmap_initialized))
return;
#if 0
if (m->md.pmap_count == 0)
return;
#endif
if ((m->flags & PG_MAPPED) == 0)
return;
#if 0
if (m->flags & PG_VPTMAPPED) {
vm_page_dirty(m);
vm_page_flag_set(m, PG_REFERENCED);
}
#endif
retry = ticks + hz * 60;
again:
PMAP_PAGE_BACKING_SCAN(m, NULL, ipmap, iptep, ipte, iva) {
if (!pmap_inval_smp_cmpset(ipmap, iva, iptep, ipte, 0))
PMAP_PAGE_BACKING_RETRY;
if (ipte & ipmap->pmap_bits[PG_MANAGED_IDX]) {
if (ipte & ipmap->pmap_bits[PG_M_IDX])
vm_page_dirty(m);
if (ipte & ipmap->pmap_bits[PG_A_IDX])
vm_page_flag_set(m, PG_REFERENCED);
pmap_removed_pte(ipmap, m, ipte);
}
if (ipmap != kernel_pmap) {
pv_entry_t pt_pv;
spin_lock_shared(&ipmap->pm_spin);
pt_pv = pv_entry_lookup(ipmap, pmap_pt_pindex(iva));
spin_unlock_shared(&ipmap->pm_spin);
if (pt_pv) {
if (vm_page_unwire_quick(pt_pv->pv_m)) {
panic("pmap_remove_all: bad "
"wire_count on pt_pv");
}
atomic_add_long(
&ipmap->pm_stats.resident_count, -1);
}
}
if (ipte & ipmap->pmap_bits[PG_W_IDX])
atomic_add_long(&ipmap->pm_stats.wired_count, -1);
if (ipte & ipmap->pmap_bits[PG_G_IDX])
cpu_invlpg((void *)iva);
} PMAP_PAGE_BACKING_DONE;
icount = atomic_fetchadd_long(&m->md.interlock_count,
0x8000000000000000LU) +
0x8000000000000000LU;
cpu_ccfence();
while (icount & 0x3FFFFFFFFFFFFFFFLU) {
tsleep_interlock(&m->md.interlock_count, 0);
if (atomic_fcmpset_long(&m->md.interlock_count, &icount,
icount | 0x4000000000000000LU)) {
tsleep(&m->md.interlock_count, PINTERLOCKED,
"pgunm", 1);
icount = m->md.interlock_count;
if (retry - ticks > 0)
goto again;
panic("pmap_remove_all: cannot return interlock_count "
"to 0 (%p, %ld)",
m, m->md.interlock_count);
}
}
vm_page_flag_clear(m, PG_MAPPED | PG_MAPPEDMULTI | PG_WRITEABLE
#if 0
| PG_VPTMAPPED
#endif
);
}
void
pmap_remove_specific(pmap_t pmap_match, vm_page_t m)
{
if (__predict_false(!pmap_initialized))
return;
if ((m->flags & PG_MAPPED) == 0)
return;
PMAP_PAGE_BACKING_SCAN(m, pmap_match, ipmap, iptep, ipte, iva) {
if (!pmap_inval_smp_cmpset(ipmap, iva, iptep, ipte, 0))
PMAP_PAGE_BACKING_RETRY;
if (ipte & ipmap->pmap_bits[PG_MANAGED_IDX]) {
if (ipte & ipmap->pmap_bits[PG_M_IDX])
vm_page_dirty(m);
if (ipte & ipmap->pmap_bits[PG_A_IDX])
vm_page_flag_set(m, PG_REFERENCED);
pmap_removed_pte(ipmap, m, ipte);
}
if (ipmap != kernel_pmap) {
pv_entry_t pt_pv;
spin_lock_shared(&ipmap->pm_spin);
pt_pv = pv_entry_lookup(ipmap, pmap_pt_pindex(iva));
spin_unlock_shared(&ipmap->pm_spin);
if (pt_pv) {
atomic_add_long(
&ipmap->pm_stats.resident_count, -1);
if (vm_page_unwire_quick(pt_pv->pv_m)) {
panic("pmap_remove_specific: bad "
"wire_count on pt_pv");
}
}
}
if (ipte & ipmap->pmap_bits[PG_W_IDX])
atomic_add_long(&ipmap->pm_stats.wired_count, -1);
if (ipte & ipmap->pmap_bits[PG_G_IDX])
cpu_invlpg((void *)iva);
} PMAP_PAGE_BACKING_DONE;
}
void
pmap_protect(pmap_t pmap, vm_offset_t sva, vm_offset_t eva, vm_prot_t prot)
{
struct pmap_scan_info info;
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;
info.pmap = pmap;
info.sva = sva;
info.eva = eva;
info.func = pmap_protect_callback;
info.arg = &prot;
pmap_scan(&info, 1);
}
static
void
pmap_protect_callback(pmap_t pmap, struct pmap_scan_info *info,
vm_pindex_t *pte_placemark,
pv_entry_t pt_pv, vm_offset_t va,
pt_entry_t *ptep, void *arg __unused)
{
pt_entry_t pbits;
pt_entry_t cbits;
vm_page_t m;
again:
pbits = *ptep;
cpu_ccfence();
cbits = pbits;
if (pbits & pmap->pmap_bits[PG_MANAGED_IDX]) {
cbits &= ~pmap->pmap_bits[PG_A_IDX];
cbits &= ~pmap->pmap_bits[PG_M_IDX];
}
if (ptep) {
cbits &= ~pmap->pmap_bits[PG_RW_IDX];
#ifdef PMAP_DEBUG2
if (pmap_enter_debug > 0) {
--pmap_enter_debug;
kprintf("pmap_protect va=%lx ptep=%p "
"pt_pv=%p cbits=%08lx\n",
va, ptep, pt_pv, cbits
);
}
#endif
if (pbits != cbits) {
if (!pmap_inval_smp_cmpset(pmap, va,
ptep, pbits, cbits)) {
goto again;
}
}
if (pbits & pmap->pmap_bits[PG_MANAGED_IDX]) {
m = PHYS_TO_VM_PAGE(pbits & PG_FRAME);
if (pbits & pmap->pmap_bits[PG_A_IDX])
vm_page_flag_set(m, PG_REFERENCED);
if (pbits & pmap->pmap_bits[PG_M_IDX])
vm_page_dirty(m);
}
}
pv_placemarker_wakeup(pmap, pte_placemark);
}
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)
{
pv_entry_t pt_pv;
pv_entry_t pte_pv;
vm_pindex_t *pte_placemark;
pt_entry_t *ptep;
pt_entry_t origpte;
vm_paddr_t opa;
vm_page_t oldm;
pt_entry_t newpte;
vm_paddr_t pa;
int flags;
int nflags;
if (pmap == NULL)
return;
va = trunc_page(va);
#ifdef PMAP_DIAGNOSTIC
if (va >= KvaEnd)
panic("pmap_enter: toobig");
if ((va >= UPT_MIN_ADDRESS) && (va < UPT_MAX_ADDRESS))
panic("pmap_enter: invalid to pmap_enter page table "
"pages (va: 0x%lx)", va);
#endif
if (va < UPT_MAX_ADDRESS && pmap == kernel_pmap) {
kprintf("Warning: pmap_enter called on UVA with "
"kernel_pmap\n");
#ifdef DDB
print_backtrace(-1);
#endif
}
if (va >= UPT_MAX_ADDRESS && pmap != kernel_pmap) {
kprintf("Warning: pmap_enter called on KVA without"
"kernel_pmap\n");
#ifdef DDB
print_backtrace(-1);
#endif
}
if (__predict_false(pmap_initialized == FALSE)) {
pte_pv = NULL;
pt_pv = NULL;
pte_placemark = NULL;
ptep = vtopte(va);
origpte = *ptep;
} else {
pte_pv = pv_get(pmap, pmap_pte_pindex(va), &pte_placemark);
KKASSERT(pte_pv == NULL);
if (va >= VM_MAX_USER_ADDRESS) {
pt_pv = NULL;
ptep = vtopte(va);
} else {
pt_pv = pmap_allocpte(pmap, pmap_pt_pindex(va), NULL);
ptep = pv_pte_lookup(pt_pv, pmap_pte_index(va));
}
origpte = *ptep;
cpu_ccfence();
}
pa = VM_PAGE_TO_PHYS(m);
newpte = (pt_entry_t)(pa | pte_prot(pmap, prot) |
pmap->pmap_bits[PG_V_IDX] | pmap->pmap_bits[PG_A_IDX]);
if (wired)
newpte |= pmap->pmap_bits[PG_W_IDX];
if (va < VM_MAX_USER_ADDRESS)
newpte |= pmap->pmap_bits[PG_U_IDX];
if ((m->flags & PG_FICTITIOUS) == 0)
newpte |= pmap->pmap_bits[PG_MANAGED_IDX];
newpte |= pmap->pmap_cache_bits_pte[m->pat_mode];
if (((origpte ^ newpte) &
~(pt_entry_t)(pmap->pmap_bits[PG_M_IDX] |
pmap->pmap_bits[PG_A_IDX])) == 0) {
goto done;
}
flags = m->flags;
cpu_ccfence();
for (;;) {
nflags = PG_MAPPED;
if (newpte & pmap->pmap_bits[PG_RW_IDX])
nflags |= PG_WRITEABLE;
if (flags & PG_MAPPED)
nflags |= PG_MAPPEDMULTI;
if (flags == (flags | nflags))
break;
if (atomic_fcmpset_int(&m->flags, &flags, flags | nflags))
break;
}
opa = origpte & PG_FRAME;
if (opa && (origpte & pmap->pmap_bits[PG_MANAGED_IDX])) {
oldm = PHYS_TO_VM_PAGE(opa);
KKASSERT(opa == oldm->phys_addr);
KKASSERT(entry != NULL);
atomic_add_long(&oldm->md.interlock_count, 1);
} else {
oldm = NULL;
}
if ((prot & VM_PROT_NOSYNC) || (opa == 0 && pt_pv != NULL)) {
origpte = atomic_swap_long(ptep, newpte);
if (opa)
cpu_invlpg((void *)va);
} else {
origpte = pmap_inval_smp(pmap, va, 1, ptep, newpte);
}
opa = origpte & PG_FRAME;
#ifdef PMAP_DEBUG2
if (pmap_enter_debug > 0) {
--pmap_enter_debug;
kprintf("pmap_enter: va=%lx m=%p origpte=%lx newpte=%lx ptep=%p"
" pte_pv=%p pt_pv=%p opa=%lx prot=%02x\n",
va, m,
origpte, newpte, ptep,
pte_pv, pt_pv, opa, prot);
}
#endif
if (pt_pv && opa == 0) {
vm_page_wire_quick(pt_pv->pv_m);
atomic_add_long(&pt_pv->pv_pmap->pm_stats.resident_count, 1);
}
if (wired) {
atomic_add_long(&pmap->pm_stats.wired_count, 1);
if ((m->flags & PG_FICTITIOUS) == 0)
vm_page_wire(m);
}
if (opa && (origpte & pmap->pmap_bits[PG_MANAGED_IDX])) {
KKASSERT(oldm == PHYS_TO_VM_PAGE(opa));
if (origpte & pmap->pmap_bits[PG_M_IDX])
vm_page_dirty(oldm);
if (origpte & pmap->pmap_bits[PG_A_IDX])
vm_page_flag_set(oldm, PG_REFERENCED);
pmap_removed_pte(pmap, oldm, origpte);
}
if ((origpte & pmap->pmap_bits[PG_V_IDX]) &&
(origpte & pmap->pmap_bits[PG_W_IDX]))
{
atomic_add_long(&pmap->pm_stats.wired_count, -1);
}
if (oldm) {
if ((atomic_fetchadd_long(&oldm->md.interlock_count, -1) &
0x7FFFFFFFFFFFFFFFLU) == 0x4000000000000001LU) {
atomic_clear_long(&oldm->md.interlock_count,
0x4000000000000000LU);
wakeup(&oldm->md.interlock_count);
}
}
done:
KKASSERT((newpte & pmap->pmap_bits[PG_MANAGED_IDX]) == 0 ||
(m->flags & PG_MAPPED));
if (pte_placemark)
pv_placemarker_wakeup(pmap, pte_placemark);
if (pt_pv)
pv_put(pt_pv);
}
void *
pmap_kenter_temporary(vm_paddr_t pa, long i)
{
pmap_kenter_quick((vm_offset_t)crashdumpmap + (i * PAGE_SIZE), pa);
return ((void *)crashdumpmap);
}
#if 0
#define MAX_INIT_PT (96)
static int pmap_object_init_pt_callback(vm_page_t p, void *data);
#endif
void
pmap_object_init_pt(pmap_t pmap, vm_map_entry_t entry,
vm_offset_t addr, vm_size_t size, int limit)
{
#if 0
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 (pindex + psize > object->size) {
if (object->size < pindex)
return;
psize = object->size - pindex;
}
if (psize == 0)
return;
if ((addr & SEG_MASK) == 0 &&
(ctob(psize) & SEG_MASK) == 0 &&
(ctob(pindex) & SEG_MASK) == 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.object = object;
info.entry = entry;
vm_object_hold_shared(object);
vm_page_rb_tree_RB_SCAN_NOLK(&object->rb_memq, rb_vm_page_scancmp,
pmap_object_init_pt_callback, &info);
vm_object_drop(object);
#endif
}
#if 0
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;
int hard_busy;
if ((info->limit & COWF_PREFAULT_MADVISE) &&
vmstats.v_free_count < vmstats.v_free_reserved) {
return(-1);
}
if (p->flags & PG_MARKER)
return 0;
hard_busy = 0;
again:
if (hard_busy) {
if (vm_page_busy_try(p, TRUE))
return 0;
} else {
if (vm_page_sbusy_try(p))
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) {
if (hard_busy == 0) {
vm_page_sbusy_drop(p);
hard_busy = 1;
goto again;
}
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);
}
if (hard_busy)
vm_page_wakeup(p);
else
vm_page_sbusy_drop(p);
if (p->object != info->object)
return -1;
lwkt_yield();
return(0);
}
#endif
int
pmap_prefault_ok(pmap_t pmap, vm_offset_t addr)
{
pt_entry_t *pte;
if ((pte = pmap_pte(pmap, addr)) != NULL) {
if (*pte & pmap->pmap_bits[PG_V_IDX]) {
return FALSE;
}
}
return TRUE;
}
vm_page_t
pmap_unwire(pmap_t pmap, vm_offset_t *pva)
{
pt_entry_t *ptep;
pv_entry_t pt_pv;
vm_paddr_t pa;
vm_page_t m;
vm_offset_t va = *pva;
*pva = va + PAGE_SIZE;
if (pmap == NULL)
return NULL;
m = NULL;
if (pmap == kernel_pmap) {
if (pmap_pt(pmap, va) == NULL) {
if (pmap_pdp(pmap, va) == NULL)
*pva = (va & ~(long)(NBPML4 - 1)) + NBPML4;
else if (pmap_pd(pmap, va) == NULL)
*pva = (va & ~(long)(NBPDP - 1)) + NBPDP;
else
*pva = (va & ~(long)(NBPDR - 1)) + NBPDR;
return NULL;
}
ptep = pmap_pte_quick(pmap, va);
if (pmap_pte_v(pmap, ptep) && pmap_pte_w(pmap, ptep)) {
atomic_add_long(&pmap->pm_stats.wired_count, -1);
atomic_clear_long(ptep, pmap->pmap_bits[PG_W_IDX]);
pa = *ptep & PG_FRAME;
m = PHYS_TO_VM_PAGE(pa);
}
} else {
pt_pv = pv_get(pmap, pmap_pt_pindex(va), NULL);
if (pt_pv == NULL) {
if (pmap_pdp(pmap, va) == NULL)
*pva = (va & ~(long)(NBPML4 - 1)) + NBPML4;
else if (pmap_pd(pmap, va) == NULL)
*pva = (va & ~(long)(NBPDP - 1)) + NBPDP;
else
*pva = (va & ~(long)(NBPDR - 1)) + NBPDR;
return NULL;
}
ptep = pv_pte_lookup(pt_pv, pmap_pte_index(va));
if ((*ptep & pmap->pmap_bits[PG_V_IDX]) == 0) {
pv_put(pt_pv);
return NULL;
}
if (pmap_pte_w(pmap, ptep)) {
atomic_add_long(&pt_pv->pv_pmap->pm_stats.wired_count,
-1);
atomic_clear_long(ptep, pmap->pmap_bits[PG_W_IDX]);
pa = *ptep & PG_FRAME;
m = PHYS_TO_VM_PAGE(pa);
}
pv_put(pt_pv);
}
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)
{
}
void
pmap_zero_page(vm_paddr_t phys)
{
vm_offset_t va = PHYS_TO_DMAP(phys);
pagezero((void *)va);
}
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_noinval(pmap, sva, eva);
cpu_invltlb();
}
static
boolean_t
pmap_testbit(vm_page_t m, int bit)
{
int res = FALSE;
if (__predict_false(!pmap_initialized || (m->flags & PG_FICTITIOUS)))
return FALSE;
if (bit == PG_M_IDX && (m->flags & PG_WRITEABLE) == 0)
return FALSE;
#if 0
if (m->flags & PG_VPTMAPPED)
return TRUE;
#endif
PMAP_PAGE_BACKING_SCAN(m, NULL, ipmap, iptep, ipte, iva) {
if (ipte & ipmap->pmap_bits[bit]) {
res = TRUE;
break;
}
} PMAP_PAGE_BACKING_DONE;
return res;
}
static __inline
void
pmap_clearbit(vm_page_t m, int bit_index)
{
pt_entry_t npte;
int retry;
long icount;
if (__predict_false(!pmap_initialized)) {
if (bit_index == PG_RW_IDX)
vm_page_flag_clear(m, PG_WRITEABLE);
return;
}
if ((m->flags & (PG_MAPPED | PG_WRITEABLE)) == 0)
return;
#if 0
if (m->flags & PG_VPTMAPPED) {
if (bit_index == PG_RW_IDX) {
vm_page_dirty(m);
vm_page_flag_clear(m, PG_WRITEABLE);
}
return;
}
#endif
if (bit_index != PG_RW_IDX) {
#if 0
long icount;
icount = 0;
#endif
PMAP_PAGE_BACKING_SCAN(m, NULL, ipmap, iptep, ipte, iva) {
#if 0
++icount;
#endif
if (ipte & ipmap->pmap_bits[bit_index]) {
atomic_clear_long(iptep,
ipmap->pmap_bits[bit_index]);
}
} PMAP_PAGE_BACKING_DONE;
#if 0
if (icount == 0) {
icount = atomic_fetchadd_long(&m->md.interlock_count,
0x8000000000000000LU);
if ((icount & 0x3FFFFFFFFFFFFFFFLU) == 0) {
vm_page_flag_clear(m, PG_MAPPED |
PG_MAPPEDMULTI |
PG_WRITEABLE);
}
}
#endif
return;
}
if ((m->flags & PG_WRITEABLE) == 0)
return;
retry = ticks + hz * 60;
again:
PMAP_PAGE_BACKING_SCAN(m, NULL, ipmap, iptep, ipte, iva) {
#if 0
if ((ipte & ipmap->pmap_bits[PG_MANAGED_IDX]) == 0)
continue;
#endif
if ((ipte & ipmap->pmap_bits[PG_RW_IDX]) == 0)
continue;
npte = ipte & ~(ipmap->pmap_bits[PG_RW_IDX] |
ipmap->pmap_bits[PG_M_IDX]);
if (!pmap_inval_smp_cmpset(ipmap, iva, iptep, ipte, npte))
PMAP_PAGE_BACKING_RETRY;
if (ipte & ipmap->pmap_bits[PG_M_IDX])
vm_page_dirty(m);
} PMAP_PAGE_BACKING_DONE;
icount = atomic_fetchadd_long(&m->md.interlock_count,
0x8000000000000000LU) +
0x8000000000000000LU;
cpu_ccfence();
while (icount & 0x3FFFFFFFFFFFFFFFLU) {
tsleep_interlock(&m->md.interlock_count, 0);
if (atomic_fcmpset_long(&m->md.interlock_count, &icount,
icount | 0x4000000000000000LU)) {
tsleep(&m->md.interlock_count, PINTERLOCKED,
"pgunm", 1);
icount = m->md.interlock_count;
if (retry - ticks > 0)
goto again;
panic("pmap_clearbit: cannot return interlock_count "
"to 0 (%p, %ld)",
m, m->md.interlock_count);
}
}
vm_page_flag_clear(m, PG_WRITEABLE);
}
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, PG_RW_IDX);
} 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)
{
int rval = 0;
pt_entry_t npte;
if (__predict_false(!pmap_initialized || (m->flags & PG_FICTITIOUS)))
return rval;
#if 0
if (m->flags & PG_VPTMAPPED)
return 1;
#endif
PMAP_PAGE_BACKING_SCAN(m, NULL, ipmap, iptep, ipte, iva) {
if (ipte & ipmap->pmap_bits[PG_A_IDX]) {
npte = ipte & ~ipmap->pmap_bits[PG_A_IDX];
if (!atomic_cmpset_long(iptep, ipte, npte))
PMAP_PAGE_BACKING_RETRY;
++rval;
if (rval > 4)
break;
}
} PMAP_PAGE_BACKING_DONE;
return rval;
}
boolean_t
pmap_is_modified(vm_page_t m)
{
boolean_t res;
res = pmap_testbit(m, PG_M_IDX);
return (res);
}
void
pmap_clear_modify(vm_page_t m)
{
pmap_clearbit(m, PG_M_IDX);
}
void
pmap_clear_reference(vm_page_t m)
{
pmap_clearbit(m, PG_A_IDX);
}
static
void
x86_64_protection_init(void)
{
uint64_t *kp;
int prot;
TUNABLE_INT_FETCH("machdep.pmap_nx_enable", &pmap_nx_enable);
if ((amd_feature & AMDID_NX) == 0) {
pmap_bits_default[PG_NX_IDX] = 0;
pmap_nx_enable = 0;
} else if (pmap_nx_enable < 0) {
pmap_nx_enable = 1;
}
kp = protection_codes;
for (prot = 0; prot < PROTECTION_CODES_SIZE; prot++) {
switch (prot) {
case VM_PROT_NONE | VM_PROT_NONE | VM_PROT_NONE:
*kp = 0;
break;
case VM_PROT_READ | VM_PROT_NONE | VM_PROT_NONE:
if (pmap_nx_enable >= 1)
*kp = pmap_bits_default[PG_NX_IDX];
break;
case VM_PROT_READ | VM_PROT_NONE | VM_PROT_EXECUTE:
case VM_PROT_NONE | VM_PROT_NONE | VM_PROT_EXECUTE:
*kp = 0;
break;
case VM_PROT_NONE | VM_PROT_WRITE | VM_PROT_NONE:
case VM_PROT_READ | VM_PROT_WRITE | VM_PROT_NONE:
*kp = pmap_bits_default[PG_RW_IDX];
if (pmap_nx_enable >= 2)
*kp |= pmap_bits_default[PG_NX_IDX];
break;
case VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE:
case VM_PROT_NONE | VM_PROT_WRITE | VM_PROT_EXECUTE:
*kp = pmap_bits_default[PG_RW_IDX];
break;
}
++kp;
}
}
void *
pmap_mapdev(vm_paddr_t pa, vm_size_t size)
{
return(pmap_mapdev_attr(pa, size, PAT_WRITE_BACK));
}
void *
pmap_mapdev_uncacheable(vm_paddr_t pa, vm_size_t size)
{
return(pmap_mapdev_attr(pa, size, PAT_UNCACHEABLE));
}
void *
pmap_mapbios(vm_paddr_t pa, vm_size_t size)
{
return (pmap_mapdev_attr(pa, size, PAT_WRITE_BACK));
}
void *
pmap_mapdev_attr(vm_paddr_t pa, vm_size_t size, int mode)
{
vm_offset_t va, tmpva, offset;
pt_entry_t *pte;
vm_size_t tmpsize;
offset = pa & PAGE_MASK;
size = roundup(offset + size, PAGE_SIZE);
va = kmem_alloc_nofault(kernel_map, size, VM_SUBSYS_MAPDEV, PAGE_SIZE);
if (va == 0)
panic("pmap_mapdev: Couldn't alloc kernel virtual memory");
pa = pa & ~PAGE_MASK;
for (tmpva = va, tmpsize = size; tmpsize > 0;) {
pte = vtopte(tmpva);
*pte = pa |
kernel_pmap->pmap_bits[PG_RW_IDX] |
kernel_pmap->pmap_bits[PG_V_IDX] |
kernel_pmap->pmap_cache_bits_pte[mode];
tmpsize -= PAGE_SIZE;
tmpva += PAGE_SIZE;
pa += PAGE_SIZE;
}
pmap_invalidate_range(kernel_pmap, va, va + size);
pmap_invalidate_cache_range(va, va + size);
return ((void *)(va + offset));
}
void
pmap_unmapdev(vm_offset_t va, vm_size_t size)
{
vm_offset_t base, offset;
base = va & ~PAGE_MASK;
offset = va & PAGE_MASK;
size = roundup(offset + size, PAGE_SIZE);
pmap_qremove(va, size >> PAGE_SHIFT);
kmem_free(kernel_map, base, size);
}
void
pmap_page_set_memattr(vm_page_t m, vm_memattr_t ma)
{
m->pat_mode = ma;
if ((m->flags & PG_FICTITIOUS) == 0)
pmap_change_attr(PHYS_TO_DMAP(VM_PAGE_TO_PHYS(m)), 1, m->pat_mode);
}
void
pmap_change_attr(vm_offset_t va, vm_size_t count, int mode)
{
pt_entry_t *pte;
vm_offset_t base;
int changed = 0;
if (va == 0)
panic("pmap_change_attr: va is NULL");
base = trunc_page(va);
if (va >= DMAP_MIN_ADDRESS && va < DMAP_MAX_ADDRESS) {
pd_entry_t *pd;
KKASSERT(va < DMapMaxAddress);
pd = (pd_entry_t *)PHYS_TO_DMAP(DMPDphys);
pd += (va - DMAP_MIN_ADDRESS) >> PDRSHIFT;
while ((long)count > 0) {
*pd =
(*pd & ~(pd_entry_t)(kernel_pmap->pmap_cache_mask_pde)) |
kernel_pmap->pmap_cache_bits_pde[mode];
count -= NBPDR / PAGE_SIZE;
va += NBPDR;
++pd;
}
} else {
while (count) {
pte = vtopte(va);
*pte =
(*pte & ~(pt_entry_t)(kernel_pmap->pmap_cache_mask_pte)) |
kernel_pmap->pmap_cache_bits_pte[mode];
--count;
va += PAGE_SIZE;
}
}
changed = 1;
if (changed) {
pmap_invalidate_range(kernel_pmap, base, va);
pmap_invalidate_cache_range(base, va);
}
}
int
pmap_mincore(pmap_t pmap, vm_offset_t addr)
{
pt_entry_t *ptep, pte;
vm_page_t m;
int val = 0;
ptep = pmap_pte(pmap, addr);
if (ptep && (pte = *ptep) != 0) {
vm_offset_t pa;
val = MINCORE_INCORE;
pa = pte & PG_FRAME;
if (pte & pmap->pmap_bits[PG_MANAGED_IDX])
m = PHYS_TO_VM_PAGE(pa);
else
m = NULL;
if (pte & pmap->pmap_bits[PG_M_IDX])
val |= MINCORE_MODIFIED|MINCORE_MODIFIED_OTHER;
else if (m && (m->dirty || pmap_is_modified(m)))
val |= MINCORE_MODIFIED_OTHER;
if (pte & pmap->pmap_bits[PG_A_IDX]) {
val |= MINCORE_REFERENCED|MINCORE_REFERENCED_OTHER;
} else if (m && ((m->flags & PG_REFERENCED) ||
pmap_ts_referenced(m))) {
val |= MINCORE_REFERENCED_OTHER;
vm_page_flag_set(m, PG_REFERENCED);
}
}
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);
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;
thread_t td;
oldvm = lp->lwp_vmspace;
if (oldvm != newvm) {
crit_enter();
td = curthread;
KKASSERT((newvm->vm_refcnt & VM_REF_DELETED) == 0);
lp->lwp_vmspace = newvm;
if (td->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
if (pmap->pmap_bits[TYPE_IDX] == REGULAR_PMAP) {
td->td_pcb->pcb_cr3 = vtophys(pmap->pm_pml4);
if (meltdown_mitigation && pmap->pm_pmlpv_iso) {
td->td_pcb->pcb_cr3_iso =
vtophys(pmap->pm_pml4_iso);
td->td_pcb->pcb_flags |= PCB_ISOMMU;
} else {
td->td_pcb->pcb_cr3_iso = 0;
td->td_pcb->pcb_flags &= ~PCB_ISOMMU;
}
} else if (pmap->pmap_bits[TYPE_IDX] == EPT_PMAP) {
td->td_pcb->pcb_cr3 = KPML4phys;
td->td_pcb->pcb_cr3_iso = 0;
td->td_pcb->pcb_flags &= ~PCB_ISOMMU;
} else {
panic("pmap_setlwpvm: unknown pmap type\n");
}
{
struct trampframe *tramp;
tramp = &pscpu->trampoline;
tramp->tr_pcb_cr3 = td->td_pcb->pcb_cr3;
tramp->tr_pcb_cr3_iso = td->td_pcb->pcb_cr3_iso;
tramp->tr_pcb_flags = td->td_pcb->pcb_flags;
tramp->tr_pcb_rsp = (register_t)td->td_pcb;
}
load_cr3(td->td_pcb->pcb_cr3);
pmap = vmspace_pmap(oldvm);
ATOMIC_CPUMASK_NANDBIT(pmap->pm_active,
mycpu->gd_cpuid);
}
crit_exit();
}
}
void
pmap_add_cpu(struct vmspace *vm, int cpuid)
{
ATOMIC_CPUMASK_ORBIT(vm->vm_pmap.pm_active, mycpu->gd_cpuid);
crit_enter();
pmap_interlock_wait(vm);
crit_exit();
}
void
pmap_del_cpu(struct vmspace *vm, int cpuid)
{
ATOMIC_CPUMASK_NANDBIT(vm->vm_pmap.pm_active, mycpu->gd_cpuid);
}
void
pmap_del_all_cpus(struct vmspace *vm)
{
CPUMASK_ASSZERO(vm->vm_pmap.pm_active);
}
void
pmap_interlock_wait(struct vmspace *vm)
{
struct pmap *pmap = &vm->vm_pmap;
if (pmap->pm_active_lock & CPULOCK_EXCL) {
crit_enter();
KKASSERT(curthread->td_critcount >= 2);
DEBUG_PUSH_INFO("pmap_interlock_wait");
while (pmap->pm_active_lock & CPULOCK_EXCL) {
cpu_ccfence();
lwkt_process_ipiq();
}
DEBUG_POP_INFO();
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) && (obj->type != OBJT_MGTDEVICE))) {
return addr;
}
addr = roundup2(addr, NBPDR);
return addr;
}
vm_page_t
pmap_kvtom(vm_offset_t va)
{
pt_entry_t *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)
{
}
static
void
pmap_pgscan_callback(pmap_t pmap, struct pmap_scan_info *info,
vm_pindex_t *pte_placemark,
pv_entry_t pt_pv, vm_offset_t va,
pt_entry_t *ptep, void *arg)
{
struct pmap_pgscan_info *pginfo = arg;
vm_page_t m;
pt_entry_t pte;
pte = *ptep;
cpu_ccfence();
if (pte & pmap->pmap_bits[PG_MANAGED_IDX]) {
m = PHYS_TO_VM_PAGE(*ptep & PG_FRAME);
if (vm_page_busy_try(m, TRUE) == 0) {
if (m == PHYS_TO_VM_PAGE(*ptep & PG_FRAME)) {
pv_placemarker_wakeup(pmap, pte_placemark);
if (pt_pv) {
vm_page_wire_quick(pt_pv->pv_m);
pv_unlock(pt_pv);
}
if (pginfo->callback(pginfo, va, m) < 0)
info->stop = 1;
if (pt_pv) {
pv_lock(pt_pv);
if (vm_page_unwire_quick(pt_pv->pv_m)) {
panic("pmap_pgscan: bad wire_"
"count on pt_pv");
}
}
} else {
vm_page_wakeup(m);
pv_placemarker_wakeup(pmap, pte_placemark);
}
} else {
++pginfo->busycount;
pv_placemarker_wakeup(pmap, pte_placemark);
}
} else {
pv_placemarker_wakeup(pmap, pte_placemark);
}
}
void
pmap_pgscan(struct pmap_pgscan_info *pginfo)
{
struct pmap_scan_info info;
pginfo->offset = pginfo->beg_addr;
info.pmap = pginfo->pmap;
info.sva = pginfo->beg_addr;
info.eva = pginfo->end_addr;
info.func = pmap_pgscan_callback;
info.arg = pginfo;
pmap_scan(&info, 0);
if (info.stop == 0)
pginfo->offset = pginfo->end_addr;
}
static
void
pv_placemarker_wait(pmap_t pmap, vm_pindex_t *pmark)
{
vm_pindex_t mark;
mark = *pmark;
cpu_ccfence();
while (mark != PM_NOPLACEMARK) {
tsleep_interlock(pmark, 0);
if (atomic_fcmpset_long(pmark, &mark,
mark | PM_PLACEMARK_WAKEUP)) {
tsleep(pmark, PINTERLOCKED, "pvplw", 0);
break;
}
}
}
static
void
pv_placemarker_wakeup(pmap_t pmap, vm_pindex_t *pmark)
{
vm_pindex_t pindex;
pindex = atomic_swap_long(pmark, PM_NOPLACEMARK);
KKASSERT(pindex != PM_NOPLACEMARK);
if (pindex & PM_PLACEMARK_WAKEUP)
wakeup(pmark);
}