#include <sys/param.h>
#include <sys/systm.h>
#include <sys/atomic.h>
#include <sys/proc.h>
#include <sys/pool.h>
#include <sys/user.h>
#include <sys/mutex.h>
#include <uvm/uvm.h>
#include <machine/specialreg.h>
#include <sys/msgbuf.h>
#include <stand/boot/bootarg.h>
#ifdef PMAP_DEBUG
#define DPRINTF(x...) do { printf(x); } while(0)
#else
#define DPRINTF(x...)
#endif
#define PG_FRAME 0xfffff000
#define PG_LGFRAME 0xffc00000
#define PTE_BASE ((pt_entry_t *) (PDSLOT_PTE * NBPD))
#define APTE_BASE ((pt_entry_t *) (PDSLOT_APTE * NBPD))
#define PDP_BASE ((pd_entry_t *)(((char *)PTE_BASE) + (PDSLOT_PTE * NBPG)))
#define APDP_BASE ((pd_entry_t *)(((char *)APTE_BASE) + (PDSLOT_APTE * NBPG)))
#define PDP_PDE (PDP_BASE + PDSLOT_PTE)
#define APDP_PDE (PDP_BASE + PDSLOT_APTE)
#define PD_MASK 0xffc00000
#define PT_MASK 0x003ff000
#define pdei(VA) (((VA) & PD_MASK) >> PDSHIFT)
#define ptei(VA) (((VA) & PT_MASK) >> PGSHIFT)
#define i386_round_pdr(x) ((((unsigned)(x)) + ~PD_MASK) & PD_MASK)
#define vtopte(VA) (PTE_BASE + atop((vaddr_t)VA))
#define ptp_i2o(I) ((I) * NBPG)
#define ptp_o2i(O) ((O) / NBPG)
#define ptp_i2v(I) ((I) * NBPD)
#define ptp_v2i(V) ((V) / NBPD)
#define PDE(pm,i) (((pd_entry_t *)(pm)->pm_pdir)[(i)])
typedef u_int32_t pd_entry_t;
typedef u_int32_t pt_entry_t;
#define NPTECL 16
struct pmap __attribute__ ((aligned (32))) kernel_pmap_store;
int nkpde = NKPTP;
int nkptp_max = 1024 - (KERNBASE / NBPD) - 1;
extern int pg_g_kern;
int pmap_pg_g = 0;
int pmap_pg_wc = PG_UCMINUS;
uint32_t protection_codes[8];
int pmap_initialized = 0;
#ifdef MULTIPROCESSOR
#define PTESLEW(pte, id) ((pte)+(id)*NPTECL)
#define VASLEW(va,id) ((va)+(id)*NPTECL*NBPG)
#else
#define PTESLEW(pte, id) (pte)
#define VASLEW(va,id) (va)
#endif
struct pool pmap_pv_pool;
#define PVE_LOWAT (PVE_PER_PVPAGE / 2)
#define PVE_HIWAT (PVE_LOWAT + (PVE_PER_PVPAGE * 2))
static vaddr_t virtual_avail;
static vaddr_t virtual_end;
struct pmap_head pmaps;
struct mutex pmaps_lock = MUTEX_INITIALIZER(IPL_VM);
struct pool pmap_pmap_pool;
pt_entry_t *csrc_pte, *cdst_pte, *zero_pte, *ptp_pte, *flsh_pte;
caddr_t pmap_csrcp, pmap_cdstp, pmap_zerop, pmap_ptpp, pmap_flshp;
caddr_t vmmap;
extern uint32_t cpu_meltdown;
struct vm_page *pmap_alloc_ptp_86(struct pmap *, int, pt_entry_t);
struct vm_page *pmap_get_ptp_86(struct pmap *, int);
pt_entry_t *pmap_map_ptes_86(struct pmap *);
void pmap_unmap_ptes_86(struct pmap *);
void pmap_do_remove_86(struct pmap *, vaddr_t, vaddr_t, int);
void pmap_remove_ptes_86(struct pmap *, struct vm_page *, vaddr_t,
vaddr_t, vaddr_t, int, struct pv_entry **);
void *pmap_pv_page_alloc(struct pool *, int, int *);
void pmap_pv_page_free(struct pool *, void *);
struct pool_allocator pmap_pv_page_allocator = {
pmap_pv_page_alloc, pmap_pv_page_free,
};
void pmap_sync_flags_pte_86(struct vm_page *, pt_entry_t);
void pmap_drop_ptp_86(struct pmap *, vaddr_t, struct vm_page *,
pt_entry_t *);
void setcslimit(struct pmap *, struct trapframe *, struct pcb *,
vaddr_t);
void pmap_pinit_pd_86(struct pmap *);
static __inline u_int
pmap_pte2flags(pt_entry_t pte)
{
return (((pte & PG_U) ? PG_PMAP_REF : 0) |
((pte & PG_M) ? PG_PMAP_MOD : 0));
}
void
pmap_sync_flags_pte_86(struct vm_page *pg, pt_entry_t pte)
{
if (pte & (PG_U|PG_M)) {
atomic_setbits_int(&pg->pg_flags, pmap_pte2flags(pte));
}
}
void
pmap_apte_flush(void)
{
pmap_tlb_shoottlb();
pmap_tlb_shootwait();
}
pt_entry_t *
pmap_map_ptes_86(struct pmap *pmap)
{
pd_entry_t opde;
if (pmap == pmap_kernel()) {
return(PTE_BASE);
}
mtx_enter(&pmap->pm_mtx);
if (pmap_is_curpmap(pmap)) {
return(PTE_BASE);
}
mtx_enter(&curcpu()->ci_curpmap->pm_apte_mtx);
opde = *APDP_PDE;
#if defined(MULTIPROCESSOR) && defined(DIAGNOSTIC)
if (pmap_valid_entry(opde))
panic("pmap_map_ptes_86: APTE valid");
#endif
if (!pmap_valid_entry(opde) || (opde & PG_FRAME) != pmap->pm_pdirpa) {
*APDP_PDE = (pd_entry_t) (pmap->pm_pdirpa | PG_RW | PG_V |
PG_U | PG_M);
if (pmap_valid_entry(opde))
pmap_apte_flush();
}
return(APTE_BASE);
}
void
pmap_unmap_ptes_86(struct pmap *pmap)
{
if (pmap == pmap_kernel())
return;
if (!pmap_is_curpmap(pmap)) {
#if defined(MULTIPROCESSOR)
*APDP_PDE = 0;
pmap_apte_flush();
#endif
mtx_leave(&curcpu()->ci_curpmap->pm_apte_mtx);
}
mtx_leave(&pmap->pm_mtx);
}
void
pmap_exec_account(struct pmap *pm, vaddr_t va,
uint32_t opte, uint32_t npte)
{
if (pm == pmap_kernel())
return;
if (curproc->p_vmspace == NULL ||
pm != vm_map_pmap(&curproc->p_vmspace->vm_map))
return;
if ((opte ^ npte) & PG_X)
pmap_tlb_shootpage(pm, va);
if (cpu_pae)
return;
if ((opte & PG_X) && (npte & PG_X) == 0 && va == pm->pm_hiexec) {
struct trapframe *tf = curproc->p_md.md_regs;
struct pcb *pcb = &curproc->p_addr->u_pcb;
KERNEL_LOCK();
pm->pm_hiexec = I386_MAX_EXE_ADDR;
setcslimit(pm, tf, pcb, I386_MAX_EXE_ADDR);
KERNEL_UNLOCK();
}
}
int
pmap_exec_fixup(struct vm_map *map, struct trapframe *tf, vaddr_t gdt_cs,
struct pcb *pcb)
{
struct vm_map_entry *ent;
struct pmap *pm = vm_map_pmap(map);
vaddr_t va = 0;
vaddr_t pm_cs;
KERNEL_LOCK();
vm_map_lock(map);
RBT_FOREACH_REVERSE(ent, uvm_map_addr, &map->addr) {
if (ent->protection & PROT_EXEC)
break;
}
if (ent)
va = trunc_page(ent->end - 1);
vm_map_unlock(map);
KERNEL_ASSERT_LOCKED();
pm_cs = SEGDESC_LIMIT(pm->pm_codeseg);
if (va <= pm->pm_hiexec && pm_cs == pm->pm_hiexec &&
gdt_cs == pm->pm_hiexec) {
KERNEL_UNLOCK();
return (0);
}
pm->pm_hiexec = va;
setcslimit(pm, tf, pcb, va);
KERNEL_UNLOCK();
return (1);
}
u_int32_t
pmap_pte_set_86(vaddr_t va, paddr_t pa, u_int32_t bits)
{
pt_entry_t pte, *ptep = vtopte(va);
pa &= PMAP_PA_MASK;
pte = i386_atomic_testset_ul(ptep, pa | bits);
return (pte & ~PG_FRAME);
}
u_int32_t
pmap_pte_setbits_86(vaddr_t va, u_int32_t set, u_int32_t clr)
{
pt_entry_t *ptep = vtopte(va);
pt_entry_t pte = *ptep;
*ptep = (pte | set) & ~clr;
return (pte & ~PG_FRAME);
}
u_int32_t
pmap_pte_bits_86(vaddr_t va)
{
pt_entry_t *ptep = vtopte(va);
return (*ptep & ~PG_FRAME);
}
paddr_t
pmap_pte_paddr_86(vaddr_t va)
{
pt_entry_t *ptep = vtopte(va);
return (*ptep & PG_FRAME);
}
vaddr_t
pmap_tmpmap_pa_86(paddr_t pa)
{
#ifdef MULTIPROCESSOR
int id = cpu_number();
#endif
pt_entry_t *ptpte;
caddr_t ptpva;
ptpte = PTESLEW(ptp_pte, id);
ptpva = VASLEW(pmap_ptpp, id);
#if defined(DIAGNOSTIC)
if (*ptpte)
panic("pmap_tmpmap_pa: ptp_pte in use?");
#endif
*ptpte = PG_V | PG_RW | pa;
return((vaddr_t)ptpva);
}
vaddr_t
pmap_tmpmap_pa(paddr_t pa)
{
if (cpu_pae)
return pmap_tmpmap_pa_pae(pa);
return pmap_tmpmap_pa_86(pa);
}
void
pmap_tmpunmap_pa_86(void)
{
#ifdef MULTIPROCESSOR
int id = cpu_number();
#endif
pt_entry_t *ptpte;
caddr_t ptpva;
ptpte = PTESLEW(ptp_pte, id);
ptpva = VASLEW(pmap_ptpp, id);
#if defined(DIAGNOSTIC)
if (!pmap_valid_entry(*ptpte))
panic("pmap_tmpunmap_pa: our pte invalid?");
#endif
*ptpte = 0;
pmap_update_pg((vaddr_t)ptpva);
#ifdef MULTIPROCESSOR
#endif
}
void
pmap_tmpunmap_pa(void)
{
if (cpu_pae) {
pmap_tmpunmap_pa_pae();
return;
}
pmap_tmpunmap_pa_86();
}
paddr_t
vtophys(vaddr_t va)
{
if (cpu_pae)
return vtophys_pae(va);
else
return ((*vtopte(va) & PG_FRAME) | (va & ~PG_FRAME));
}
void
setcslimit(struct pmap *pm, struct trapframe *tf, struct pcb *pcb,
vaddr_t limit)
{
limit = min(limit, VM_MAXUSER_ADDRESS - 1);
setsegment(&pm->pm_codeseg, 0, atop(limit),
SDT_MEMERA, SEL_UPL, 1, 1);
curcpu()->ci_gdt[GUCODE_SEL].sd = pm->pm_codeseg;
tf->tf_cs = GSEL(GUCODE_SEL, SEL_UPL);
}
void
pmap_kenter_pa(vaddr_t va, paddr_t pa, vm_prot_t prot)
{
uint32_t bits;
uint32_t global = 0;
if (!cpu_pae) {
if (va >= (vaddr_t)NBPD)
global = pmap_pg_g;
} else {
if (va >= (vaddr_t)NBPD / 2)
global = pmap_pg_g;
}
bits = pmap_pte_set(va, pa, ((prot & PROT_WRITE) ? PG_RW : PG_RO) |
PG_V | global | PG_U | PG_M |
((prot & PROT_EXEC) ? PG_X : 0) |
((pa & PMAP_NOCACHE) ? PG_N : 0) |
((pa & PMAP_WC) ? pmap_pg_wc : 0));
if (pmap_valid_entry(bits)) {
if (pa & PMAP_NOCACHE && (bits & PG_N) == 0)
wbinvd_on_all_cpus();
pmap_tlb_shootpage(pmap_kernel(), va);
pmap_tlb_shootwait();
}
}
void
pmap_kremove(vaddr_t sva, vsize_t len)
{
uint32_t bits;
vaddr_t va, eva;
eva = sva + len;
for (va = sva; va != eva; va += PAGE_SIZE) {
bits = pmap_pte_set(va, 0, 0);
#ifdef DIAGNOSTIC
if (bits & PG_PVLIST)
panic("pmap_kremove: PG_PVLIST mapping for 0x%lx", va);
#endif
}
pmap_tlb_shootrange(pmap_kernel(), sva, eva);
pmap_tlb_shootwait();
}
void
pmap_alloc_pdir_intel_x86(struct pmap *pmap)
{
vaddr_t va;
KASSERT(pmap->pm_pdir_intel == 0);
va = (vaddr_t)km_alloc(NBPG, &kv_any, &kp_zero, &kd_waitok);
if (va == 0)
panic("kernel_map out of virtual space");
pmap->pm_pdir_intel = va;
if (!pmap_extract(pmap_kernel(), (vaddr_t)pmap->pm_pdir_intel,
&pmap->pm_pdirpa_intel))
panic("can't locate PD page");
}
void
pmap_bootstrap(vaddr_t kva_start)
{
struct pmap *kpm;
vaddr_t kva;
pt_entry_t *pte;
uvm_setpagesize();
if (PAGE_SIZE != NBPG)
panic("pmap_bootstrap: PAGE_SIZE != NBPG");
virtual_avail = kva_start;
virtual_end = VM_MAX_KERNEL_ADDRESS;
protection_codes[PROT_NONE] = 0;
protection_codes[PROT_EXEC] = PG_X;
protection_codes[PROT_READ] = PG_RO;
protection_codes[PROT_READ | PROT_EXEC] = PG_X;
protection_codes[PROT_WRITE] = PG_RW;
protection_codes[PROT_WRITE | PROT_EXEC] = PG_RW|PG_X;
protection_codes[PROT_READ | PROT_WRITE] = PG_RW;
protection_codes[PROT_READ | PROT_WRITE | PROT_EXEC] = PG_RW|PG_X;
kpm = pmap_kernel();
mtx_init(&kpm->pm_mtx, -1);
mtx_init(&kpm->pm_apte_mtx, IPL_VM);
uvm_obj_init(&kpm->pm_obj, &pmap_pager, 1);
bzero(&kpm->pm_list, sizeof(kpm->pm_list));
kpm->pm_pdir = (vaddr_t)(proc0.p_addr->u_pcb.pcb_cr3 + KERNBASE);
kpm->pm_pdirpa = proc0.p_addr->u_pcb.pcb_cr3;
kpm->pm_pdir_intel = 0;
kpm->pm_pdirpa_intel = 0;
kpm->pm_stats.wired_count = kpm->pm_stats.resident_count =
atop(kva_start - VM_MIN_KERNEL_ADDRESS);
if (cpu_feature & CPUID_PGE) {
lcr4(rcr4() | CR4_PGE);
pmap_pg_g = pg_g_kern;
for (kva = VM_MIN_KERNEL_ADDRESS; kva < virtual_avail;
kva += PAGE_SIZE)
if (pmap_valid_entry(PTE_BASE[atop(kva)]))
PTE_BASE[atop(kva)] |= pmap_pg_g;
}
pte = PTE_BASE + atop(virtual_avail);
#ifdef MULTIPROCESSOR
pmap_csrcp = (caddr_t) virtual_avail; csrc_pte = pte;
pmap_cdstp = (caddr_t) virtual_avail+PAGE_SIZE; cdst_pte = pte+1;
pmap_zerop = (caddr_t) virtual_avail+PAGE_SIZE*2; zero_pte = pte+2;
pmap_ptpp = (caddr_t) virtual_avail+PAGE_SIZE*3; ptp_pte = pte+3;
pmap_flshp = (caddr_t) virtual_avail+PAGE_SIZE*4; flsh_pte = pte+4;
virtual_avail += PAGE_SIZE * MAXCPUS * NPTECL;
pte += MAXCPUS * NPTECL;
#else
pmap_csrcp = (caddr_t) virtual_avail; csrc_pte = pte;
virtual_avail += PAGE_SIZE; pte++;
pmap_cdstp = (caddr_t) virtual_avail; cdst_pte = pte;
virtual_avail += PAGE_SIZE; pte++;
pmap_zerop = (caddr_t) virtual_avail; zero_pte = pte;
virtual_avail += PAGE_SIZE; pte++;
pmap_ptpp = (caddr_t) virtual_avail; ptp_pte = pte;
virtual_avail += PAGE_SIZE; pte++;
pmap_flshp = (caddr_t) virtual_avail; flsh_pte = pte;
virtual_avail += PAGE_SIZE; pte++;
#endif
vmmap = (char *)virtual_avail;
virtual_avail += PAGE_SIZE;
msgbufp = (struct msgbuf *)virtual_avail;
virtual_avail += round_page(MSGBUFSIZE); pte++;
bootargp = (bootarg_t *)virtual_avail;
virtual_avail += round_page(bootargc); pte++;
virtual_avail = reserve_dumppages(virtual_avail);
LIST_INIT(&pmaps);
pool_init(&pmap_pmap_pool, sizeof(struct pmap), 32, IPL_NONE, 0,
"pmappl", NULL);
pool_init(&pmap_pv_pool, sizeof(struct pv_entry), 0, IPL_VM, 0,
"pvpl", &pmap_pv_page_allocator);
tlbflush();
}
void
pmap_prealloc_lowmem_ptp(void)
{
pt_entry_t *pte, npte;
vaddr_t ptpva = (vaddr_t)vtopte(0);
if (cpu_pae) {
pmap_prealloc_lowmem_ptp_pae();
return;
}
pte = vtopte(ptpva);
npte = PTP0_PA | PG_RW | PG_V | PG_U | PG_M;
i386_atomic_testset_ul(pte, npte);
memset((void *)ptpva, 0, NBPG);
}
void
pmap_init(void)
{
pool_setlowat(&pmap_pv_pool, PVE_LOWAT);
pool_sethiwat(&pmap_pv_pool, PVE_HIWAT);
pmap_initialized = 1;
}
void *
pmap_pv_page_alloc(struct pool *pp, int flags, int *slowdown)
{
struct kmem_dyn_mode kd = KMEM_DYN_INITIALIZER;
kd.kd_waitok = ISSET(flags, PR_WAITOK);
kd.kd_slowdown = slowdown;
return (km_alloc(pp->pr_pgsize,
pmap_initialized ? &kv_page : &kv_any, pp->pr_crange, &kd));
}
void
pmap_pv_page_free(struct pool *pp, void *v)
{
km_free(v, pp->pr_pgsize, &kv_page, pp->pr_crange);
}
void
pmap_enter_pv(struct vm_page *pg, struct pv_entry *pve, struct pmap *pmap,
vaddr_t va, struct vm_page *ptp)
{
pve->pv_pmap = pmap;
pve->pv_va = va;
pve->pv_ptp = ptp;
mtx_enter(&pg->mdpage.pv_mtx);
pve->pv_next = pg->mdpage.pv_list;
pg->mdpage.pv_list = pve;
mtx_leave(&pg->mdpage.pv_mtx);
}
struct pv_entry *
pmap_remove_pv(struct vm_page *pg, struct pmap *pmap, vaddr_t va)
{
struct pv_entry *pve, **prevptr;
mtx_enter(&pg->mdpage.pv_mtx);
prevptr = &pg->mdpage.pv_list;
while ((pve = *prevptr) != NULL) {
if (pve->pv_pmap == pmap && pve->pv_va == va) {
*prevptr = pve->pv_next;
break;
}
prevptr = &pve->pv_next;
}
mtx_leave(&pg->mdpage.pv_mtx);
return(pve);
}
struct vm_page *
pmap_alloc_ptp_86(struct pmap *pmap, int pde_index, pt_entry_t pde_flags)
{
struct vm_page *ptp;
pd_entry_t *pva_intel;
ptp = uvm_pagealloc(&pmap->pm_obj, ptp_i2o(pde_index), NULL,
UVM_PGA_USERESERVE|UVM_PGA_ZERO);
if (ptp == NULL)
return (NULL);
atomic_clearbits_int(&ptp->pg_flags, PG_BUSY);
ptp->wire_count = 1;
PDE(pmap, pde_index) = (pd_entry_t)(VM_PAGE_TO_PHYS(ptp) |
PG_RW | PG_V | PG_M | PG_U | pde_flags);
if (pmap->pm_pdir_intel && ptp_i2v(pde_index) < VM_MAXUSER_ADDRESS) {
pva_intel = (pd_entry_t *)pmap->pm_pdir_intel;
pva_intel[pde_index] = PDE(pmap, pde_index);
DPRINTF("%s: copying usermode PDE (content=0x%x) pde_index %d "
"from 0x%x -> 0x%x\n", __func__, PDE(pmap, pde_index),
pde_index, (uint32_t)&PDE(pmap, pde_index),
(uint32_t)&(pva_intel[pde_index]));
}
pmap->pm_stats.resident_count++;
pmap->pm_ptphint = ptp;
return(ptp);
}
struct vm_page *
pmap_get_ptp_86(struct pmap *pmap, int pde_index)
{
struct vm_page *ptp;
if (pmap_valid_entry(PDE(pmap, pde_index))) {
if (pmap->pm_ptphint &&
(PDE(pmap, pde_index) & PG_FRAME) ==
VM_PAGE_TO_PHYS(pmap->pm_ptphint))
return(pmap->pm_ptphint);
ptp = uvm_pagelookup(&pmap->pm_obj, ptp_i2o(pde_index));
#ifdef DIAGNOSTIC
if (ptp == NULL)
panic("pmap_get_ptp_86: unmanaged user PTP");
#endif
pmap->pm_ptphint = ptp;
return(ptp);
}
return (pmap_alloc_ptp_86(pmap, pde_index, PG_u));
}
void
pmap_drop_ptp_86(struct pmap *pm, vaddr_t va, struct vm_page *ptp,
pt_entry_t *ptes)
{
pd_entry_t *pva_intel;
i386_atomic_testset_ul(&PDE(pm, pdei(va)), 0);
pmap_tlb_shootpage(curcpu()->ci_curpmap, ((vaddr_t)ptes) + ptp->offset);
#ifdef MULTIPROCESSOR
pmap_tlb_shootpage(pm, ((vaddr_t)PTE_BASE) + ptp->offset);
#endif
pm->pm_stats.resident_count--;
if (pm->pm_ptphint == ptp)
pm->pm_ptphint = RBT_ROOT(uvm_objtree, &pm->pm_obj.memt);
ptp->wire_count = 0;
uvm_pagerealloc(ptp, NULL, 0);
if (pm->pm_pdir_intel) {
KASSERT(va < VM_MAXUSER_ADDRESS);
pva_intel = (pd_entry_t *)pm->pm_pdir_intel;
i386_atomic_testset_ul(&pva_intel[pdei(va)], 0);
DPRINTF("%s: cleared meltdown PDE @ index %lu "
"(va range start 0x%x)\n", __func__, pdei(va),
(uint32_t)va);
}
}
struct pmap *
pmap_create(void)
{
struct pmap *pmap;
pmap = pool_get(&pmap_pmap_pool, PR_WAITOK);
mtx_init(&pmap->pm_mtx, IPL_VM);
mtx_init(&pmap->pm_apte_mtx, IPL_VM);
uvm_obj_init(&pmap->pm_obj, &pmap_pager, 1);
pmap->pm_stats.wired_count = 0;
pmap->pm_stats.resident_count = 1;
pmap->pm_ptphint = NULL;
pmap->pm_hiexec = 0;
pmap->pm_flags = 0;
pmap->pm_pdir_intel = 0;
pmap->pm_pdirpa_intel = 0;
initcodesegment(&pmap->pm_codeseg);
pmap_pinit_pd(pmap);
return (pmap);
}
void
pmap_pinit_pd_86(struct pmap *pmap)
{
pmap->pm_pdir = (vaddr_t)km_alloc(NBPG, &kv_any, &kp_dirty, &kd_waitok);
if (pmap->pm_pdir == 0)
panic("kernel_map out of virtual space");
pmap_extract(pmap_kernel(), (vaddr_t)pmap->pm_pdir,
&pmap->pm_pdirpa);
pmap->pm_pdirsize = NBPG;
bzero((void *)pmap->pm_pdir, PDSLOT_PTE * sizeof(pd_entry_t));
PDE(pmap, PDSLOT_PTE) = pmap->pm_pdirpa | PG_V | PG_KW | PG_U | PG_M;
PDE(pmap, PDSLOT_PTE + 1) = 0;
bcopy(&PDP_BASE[PDSLOT_KERN], &PDE(pmap, PDSLOT_KERN),
nkpde * sizeof(pd_entry_t));
bzero(&PDE(pmap, PDSLOT_KERN + nkpde),
NBPG - ((PDSLOT_KERN + nkpde) * sizeof(pd_entry_t)));
if (cpu_meltdown) {
pmap_alloc_pdir_intel_x86(pmap);
bcopy((void *)pmap_kernel()->pm_pdir_intel,
(void *)pmap->pm_pdir_intel, NBPG);
DPRINTF("%s: pmap %p pm_pdir 0x%lx pm_pdirpa 0x%lx "
"pdir_intel 0x%lx pdirpa_intel 0x%lx\n",
__func__, pmap, pmap->pm_pdir, pmap->pm_pdirpa,
pmap->pm_pdir_intel, pmap->pm_pdirpa_intel);
}
mtx_enter(&pmaps_lock);
LIST_INSERT_HEAD(&pmaps, pmap, pm_list);
mtx_leave(&pmaps_lock);
}
void
pmap_destroy(struct pmap *pmap)
{
struct vm_page *pg;
int refs;
refs = atomic_dec_int_nv(&pmap->pm_obj.uo_refs);
if (refs > 0)
return;
#ifdef MULTIPROCESSOR
pmap_tlb_droppmap(pmap);
#endif
mtx_enter(&pmaps_lock);
LIST_REMOVE(pmap, pm_list);
mtx_leave(&pmaps_lock);
while ((pg = RBT_ROOT(uvm_objtree, &pmap->pm_obj.memt)) != NULL) {
pg->wire_count = 0;
uvm_pagefree(pg);
}
km_free((void *)pmap->pm_pdir, pmap->pm_pdirsize, &kv_any, &kp_dirty);
pmap->pm_pdir = 0;
if (pmap->pm_pdir_intel) {
km_free((void *)pmap->pm_pdir_intel, pmap->pm_pdirsize,
&kv_any, &kp_dirty);
pmap->pm_pdir_intel = 0;
}
pool_put(&pmap_pmap_pool, pmap);
}
void
pmap_reference(struct pmap *pmap)
{
atomic_inc_int(&pmap->pm_obj.uo_refs);
}
void
pmap_activate(struct proc *p)
{
KASSERT(curproc == p);
KASSERT(&p->p_addr->u_pcb == curpcb);
pmap_switch(NULL, p);
}
int nlazy_cr3_hit;
int nlazy_cr3;
void
pmap_switch(struct proc *o, struct proc *p)
{
struct pcb *pcb = &p->p_addr->u_pcb;
struct pmap *pmap, *opmap;
struct cpu_info *self = curcpu();
pmap = p->p_vmspace->vm_map.pmap;
opmap = self->ci_curpmap;
pcb->pcb_pmap = pmap;
pcb->pcb_cr3 = pmap->pm_pdirpa;
if (opmap == pmap) {
if (pmap != pmap_kernel())
nlazy_cr3_hit++;
} else {
self->ci_curpmap = pmap;
lcr3(pmap->pm_pdirpa);
}
if (pmap->pm_pdirpa_intel) {
self->ci_kern_cr3 = pmap->pm_pdirpa;
self->ci_user_cr3 = pmap->pm_pdirpa_intel;
}
self->ci_gdt[GUCODE_SEL].sd = pmap->pm_codeseg;
self->ci_gdt[GUFS_SEL].sd = pcb->pcb_threadsegs[TSEG_FS];
self->ci_gdt[GUGS_SEL].sd = pcb->pcb_threadsegs[TSEG_GS];
}
void
pmap_deactivate(struct proc *p)
{
}
int
pmap_extract_86(struct pmap *pmap, vaddr_t va, paddr_t *pap)
{
pt_entry_t *ptes, pte;
ptes = pmap_map_ptes_86(pmap);
if (pmap_valid_entry(PDE(pmap, pdei(va)))) {
pte = ptes[atop(va)];
pmap_unmap_ptes_86(pmap);
if (!pmap_valid_entry(pte))
return 0;
if (pap != NULL)
*pap = (pte & PG_FRAME) | (va & ~PG_FRAME);
return 1;
}
pmap_unmap_ptes_86(pmap);
return 0;
}
void
pmap_virtual_space(vaddr_t *startp, vaddr_t *endp)
{
*startp = virtual_avail;
*endp = virtual_end;
}
void (*pagezero)(void *, size_t) = bzero;
void
pmap_zero_page(struct vm_page *pg)
{
pmap_zero_phys(VM_PAGE_TO_PHYS(pg));
}
void
pmap_zero_phys_86(paddr_t pa)
{
#ifdef MULTIPROCESSOR
int id = cpu_number();
#endif
pt_entry_t *zpte = PTESLEW(zero_pte, id);
caddr_t zerova = VASLEW(pmap_zerop, id);
#ifdef DIAGNOSTIC
if (*zpte)
panic("pmap_zero_phys_86: lock botch");
#endif
*zpte = (pa & PG_FRAME) | PG_V | PG_RW;
pmap_update_pg((vaddr_t)zerova);
pagezero(zerova, PAGE_SIZE);
*zpte = 0;
}
void
pmap_flush_cache(vaddr_t addr, vsize_t len)
{
vaddr_t i;
if (curcpu()->ci_cflushsz == 0) {
wbinvd_on_all_cpus();
return;
}
mfence();
for (i = addr; i < addr + len; i += curcpu()->ci_cflushsz)
clflush(i);
mfence();
}
void
pmap_flush_page(paddr_t pa)
{
#ifdef MULTIPROCESSOR
int id = cpu_number();
#endif
pt_entry_t *pte;
caddr_t va;
KDASSERT(PHYS_TO_VM_PAGE(pa) != NULL);
if (cpu_pae) {
pmap_flush_page_pae(pa);
return;
}
pte = PTESLEW(flsh_pte, id);
va = VASLEW(pmap_flshp, id);
#ifdef DIAGNOSTIC
if (*pte)
panic("pmap_flush_page: lock botch");
#endif
*pte = (pa & PG_FRAME) | PG_V | PG_RW;
pmap_update_pg(va);
pmap_flush_cache((vaddr_t)va, PAGE_SIZE);
*pte = 0;
pmap_update_pg(va);
}
void
pmap_copy_page_86(struct vm_page *srcpg, struct vm_page *dstpg)
{
paddr_t srcpa = VM_PAGE_TO_PHYS(srcpg);
paddr_t dstpa = VM_PAGE_TO_PHYS(dstpg);
#ifdef MULTIPROCESSOR
int id = cpu_number();
#endif
pt_entry_t *spte = PTESLEW(csrc_pte, id);
pt_entry_t *dpte = PTESLEW(cdst_pte, id);
caddr_t csrcva = VASLEW(pmap_csrcp, id);
caddr_t cdstva = VASLEW(pmap_cdstp, id);
#ifdef DIAGNOSTIC
if (*spte || *dpte)
panic("pmap_copy_page_86: lock botch");
#endif
*spte = (srcpa & PG_FRAME) | PG_V | PG_RW;
*dpte = (dstpa & PG_FRAME) | PG_V | PG_RW;
pmap_update_2pg((vaddr_t)csrcva, (vaddr_t)cdstva);
bcopy(csrcva, cdstva, PAGE_SIZE);
*spte = *dpte = 0;
pmap_update_2pg((vaddr_t)csrcva, (vaddr_t)cdstva);
}
void
pmap_remove_ptes_86(struct pmap *pmap, struct vm_page *ptp, vaddr_t ptpva,
vaddr_t startva, vaddr_t endva, int flags, struct pv_entry **free_pvs)
{
struct pv_entry *pve;
pt_entry_t *pte = (pt_entry_t *) ptpva;
struct vm_page *pg;
pt_entry_t opte;
for (; startva < endva && (ptp == NULL || ptp->wire_count > 1)
; pte++, startva += NBPG) {
if (!pmap_valid_entry(*pte))
continue;
if ((flags & PMAP_REMOVE_SKIPWIRED) && (*pte & PG_W))
continue;
opte = i386_atomic_testset_ul(pte, 0);
if (opte & PG_W)
pmap->pm_stats.wired_count--;
pmap->pm_stats.resident_count--;
if (ptp)
ptp->wire_count--;
pg = PHYS_TO_VM_PAGE(opte & PG_FRAME);
if ((opte & PG_PVLIST) == 0) {
#ifdef DIAGNOSTIC
if (pg != NULL)
panic("pmap_remove_ptes_86: managed page "
"without PG_PVLIST for 0x%lx", startva);
#endif
continue;
}
#ifdef DIAGNOSTIC
if (pg == NULL)
panic("pmap_remove_ptes_86: unmanaged page marked "
"PG_PVLIST, va = 0x%lx, pa = 0x%lx",
startva, (u_long)(opte & PG_FRAME));
#endif
pmap_sync_flags_pte_86(pg, opte);
pve = pmap_remove_pv(pg, pmap, startva);
if (pve) {
pve->pv_next = *free_pvs;
*free_pvs = pve;
}
}
}
void
pmap_remove(struct pmap *pmap, vaddr_t sva, vaddr_t eva)
{
pmap_do_remove(pmap, sva, eva, PMAP_REMOVE_ALL);
}
void
pmap_do_remove_86(struct pmap *pmap, vaddr_t sva, vaddr_t eva, int flags)
{
pt_entry_t *ptes;
paddr_t ptppa;
vaddr_t blkendva;
struct vm_page *ptp;
struct pv_entry *pve;
struct pv_entry *free_pvs = NULL;
TAILQ_HEAD(, vm_page) empty_ptps;
int shootall;
vaddr_t va;
TAILQ_INIT(&empty_ptps);
ptes = pmap_map_ptes_86(pmap);
if ((eva - sva > 32 * PAGE_SIZE) && pmap != pmap_kernel())
shootall = 1;
else
shootall = 0;
for (va = sva ; va < eva ; va = blkendva) {
blkendva = i386_round_pdr(va + 1);
if (blkendva > eva)
blkendva = eva;
if (pdei(va) == PDSLOT_PTE)
continue;
if (!pmap_valid_entry(PDE(pmap, pdei(va))))
continue;
ptppa = PDE(pmap, pdei(va)) & PG_FRAME;
if (pmap == pmap_kernel()) {
ptp = NULL;
} else {
if (pmap->pm_ptphint &&
VM_PAGE_TO_PHYS(pmap->pm_ptphint) == ptppa) {
ptp = pmap->pm_ptphint;
} else {
ptp = PHYS_TO_VM_PAGE(ptppa);
#ifdef DIAGNOSTIC
if (ptp == NULL)
panic("pmap_do_remove_86: unmanaged "
"PTP detected");
#endif
}
}
pmap_remove_ptes_86(pmap, ptp, (vaddr_t)&ptes[atop(va)],
va, blkendva, flags, &free_pvs);
if (ptp && ptp->wire_count <= 1) {
pmap_drop_ptp_86(pmap, va, ptp, ptes);
TAILQ_INSERT_TAIL(&empty_ptps, ptp, pageq);
}
if (!shootall)
pmap_tlb_shootrange(pmap, va, blkendva);
}
if (shootall)
pmap_tlb_shoottlb();
pmap_unmap_ptes_86(pmap);
pmap_tlb_shootwait();
while ((pve = free_pvs) != NULL) {
free_pvs = pve->pv_next;
pool_put(&pmap_pv_pool, pve);
}
while ((ptp = TAILQ_FIRST(&empty_ptps)) != NULL) {
TAILQ_REMOVE(&empty_ptps, ptp, pageq);
uvm_pagefree(ptp);
}
}
void
pmap_page_remove_86(struct vm_page *pg)
{
struct pv_entry *pve;
struct pmap *pm;
pt_entry_t *ptes, opte;
TAILQ_HEAD(, vm_page) empty_ptps;
struct vm_page *ptp;
if (pg->mdpage.pv_list == NULL)
return;
TAILQ_INIT(&empty_ptps);
mtx_enter(&pg->mdpage.pv_mtx);
while ((pve = pg->mdpage.pv_list) != NULL) {
pmap_reference(pve->pv_pmap);
pm = pve->pv_pmap;
mtx_leave(&pg->mdpage.pv_mtx);
ptes = pmap_map_ptes_86(pm);
mtx_enter(&pg->mdpage.pv_mtx);
if ((pve = pg->mdpage.pv_list) == NULL ||
pve->pv_pmap != pm) {
mtx_leave(&pg->mdpage.pv_mtx);
pmap_unmap_ptes_86(pm);
pmap_destroy(pm);
mtx_enter(&pg->mdpage.pv_mtx);
continue;
}
pg->mdpage.pv_list = pve->pv_next;
mtx_leave(&pg->mdpage.pv_mtx);
#ifdef DIAGNOSTIC
if (pve->pv_ptp && (PDE(pve->pv_pmap, pdei(pve->pv_va)) &
PG_FRAME)
!= VM_PAGE_TO_PHYS(pve->pv_ptp)) {
printf("pmap_page_remove_86: pg=%p: va=%lx, "
"pv_ptp=%p\n",
pg, pve->pv_va, pve->pv_ptp);
printf("pmap_page_remove_86: PTP's phys addr: "
"actual=%x, recorded=%lx\n",
(PDE(pve->pv_pmap, pdei(pve->pv_va)) &
PG_FRAME), VM_PAGE_TO_PHYS(pve->pv_ptp));
panic("pmap_page_remove_86: mapped managed page has "
"invalid pv_ptp field");
}
#endif
opte = i386_atomic_testset_ul(&ptes[atop(pve->pv_va)], 0);
if (opte & PG_W)
pve->pv_pmap->pm_stats.wired_count--;
pve->pv_pmap->pm_stats.resident_count--;
pmap_sync_flags_pte_86(pg, opte);
if (pve->pv_ptp && --pve->pv_ptp->wire_count <= 1) {
pmap_drop_ptp_86(pve->pv_pmap, pve->pv_va,
pve->pv_ptp, ptes);
TAILQ_INSERT_TAIL(&empty_ptps, pve->pv_ptp, pageq);
}
pmap_tlb_shootpage(pve->pv_pmap, pve->pv_va);
pmap_unmap_ptes_86(pve->pv_pmap);
pmap_destroy(pve->pv_pmap);
pool_put(&pmap_pv_pool, pve);
mtx_enter(&pg->mdpage.pv_mtx);
}
mtx_leave(&pg->mdpage.pv_mtx);
pmap_tlb_shootwait();
while ((ptp = TAILQ_FIRST(&empty_ptps)) != NULL) {
TAILQ_REMOVE(&empty_ptps, ptp, pageq);
uvm_pagefree(ptp);
}
}
int
pmap_test_attrs_86(struct vm_page *pg, int testbits)
{
struct pv_entry *pve;
pt_entry_t *ptes, pte;
u_long mybits, testflags;
paddr_t ptppa;
testflags = pmap_pte2flags(testbits);
if (pg->pg_flags & testflags)
return 1;
mybits = 0;
mtx_enter(&pg->mdpage.pv_mtx);
for (pve = pg->mdpage.pv_list; pve != NULL && mybits == 0;
pve = pve->pv_next) {
ptppa = PDE(pve->pv_pmap, pdei(pve->pv_va)) & PG_FRAME;
ptes = (pt_entry_t *)pmap_tmpmap_pa(ptppa);
pte = ptes[ptei(pve->pv_va)];
pmap_tmpunmap_pa();
mybits |= (pte & testbits);
}
mtx_leave(&pg->mdpage.pv_mtx);
if (mybits == 0)
return 0;
atomic_setbits_int(&pg->pg_flags, pmap_pte2flags(mybits));
return 1;
}
int
pmap_clear_attrs_86(struct vm_page *pg, int clearbits)
{
struct pv_entry *pve;
pt_entry_t *ptes, opte;
u_long clearflags;
paddr_t ptppa;
int result;
clearflags = pmap_pte2flags(clearbits);
result = pg->pg_flags & clearflags;
if (result)
atomic_clearbits_int(&pg->pg_flags, clearflags);
mtx_enter(&pg->mdpage.pv_mtx);
for (pve = pg->mdpage.pv_list; pve != NULL; pve = pve->pv_next) {
ptppa = PDE(pve->pv_pmap, pdei(pve->pv_va)) & PG_FRAME;
ptes = (pt_entry_t *)pmap_tmpmap_pa(ptppa);
#ifdef DIAGNOSTIC
if (!pmap_valid_entry(PDE(pve->pv_pmap, pdei(pve->pv_va))))
panic("pmap_clear_attrs_86: mapping without PTP "
"detected");
#endif
opte = ptes[ptei(pve->pv_va)];
if (opte & clearbits) {
result = 1;
i386_atomic_clearbits_l(&ptes[ptei(pve->pv_va)],
(opte & clearbits));
pmap_tlb_shootpage(pve->pv_pmap, pve->pv_va);
}
pmap_tmpunmap_pa();
}
mtx_leave(&pg->mdpage.pv_mtx);
pmap_tlb_shootwait();
return (result != 0);
}
void
pmap_write_protect_86(struct pmap *pmap, vaddr_t sva, vaddr_t eva,
vm_prot_t prot)
{
pt_entry_t *ptes, *spte, *epte, npte, opte;
vaddr_t blkendva;
u_int32_t md_prot;
vaddr_t va;
int shootall = 0;
ptes = pmap_map_ptes_86(pmap);
if ((eva - sva > 32 * PAGE_SIZE) && pmap != pmap_kernel())
shootall = 1;
for (va = sva; va < eva; va = blkendva) {
blkendva = i386_round_pdr(va + 1);
if (blkendva > eva)
blkendva = eva;
if (pdei(va) == PDSLOT_PTE)
continue;
if (!pmap_valid_entry(PDE(pmap, pdei(va))))
continue;
md_prot = protection_codes[prot];
if (va < VM_MAXUSER_ADDRESS)
md_prot |= PG_u;
else if (va < VM_MAX_ADDRESS)
md_prot |= PG_RW;
spte = &ptes[atop(va)];
epte = &ptes[atop(blkendva)];
for (; spte < epte ; spte++, va += PAGE_SIZE) {
if (!pmap_valid_entry(*spte))
continue;
opte = *spte;
npte = (opte & ~PG_PROT) | md_prot;
if (npte != opte) {
pmap_exec_account(pmap, va, *spte, npte);
i386_atomic_clearbits_l(spte,
(~md_prot & opte) & PG_PROT);
i386_atomic_setbits_l(spte, md_prot);
}
}
}
if (shootall)
pmap_tlb_shoottlb();
else
pmap_tlb_shootrange(pmap, sva, eva);
pmap_unmap_ptes_86(pmap);
pmap_tlb_shootwait();
}
void
pmap_unwire_86(struct pmap *pmap, vaddr_t va)
{
pt_entry_t *ptes;
if (pmap_valid_entry(PDE(pmap, pdei(va)))) {
ptes = pmap_map_ptes_86(pmap);
#ifdef DIAGNOSTIC
if (!pmap_valid_entry(ptes[atop(va)]))
panic("pmap_unwire_86: invalid (unmapped) va "
"0x%lx", va);
#endif
if ((ptes[atop(va)] & PG_W) != 0) {
i386_atomic_clearbits_l(&ptes[atop(va)], PG_W);
pmap->pm_stats.wired_count--;
}
#ifdef DIAGNOSTIC
else {
printf("pmap_unwire_86: wiring for pmap %p va 0x%lx "
"didn't change!\n", pmap, va);
}
#endif
pmap_unmap_ptes_86(pmap);
}
#ifdef DIAGNOSTIC
else {
panic("pmap_unwire_86: invalid PDE");
}
#endif
}
int
pmap_enter_86(struct pmap *pmap, vaddr_t va, paddr_t pa,
vm_prot_t prot, int flags)
{
pt_entry_t *ptes, opte, npte;
struct vm_page *ptp;
struct pv_entry *pve, *opve = NULL;
int wired = (flags & PMAP_WIRED) != 0;
int nocache = (pa & PMAP_NOCACHE) != 0;
int wc = (pa & PMAP_WC) != 0;
struct vm_page *pg = NULL;
int error, wired_count, resident_count, ptp_count;
KASSERT(!(wc && nocache));
pa &= PMAP_PA_MASK;
#ifdef DIAGNOSTIC
if (va >= VM_MAX_KERNEL_ADDRESS)
panic("pmap_enter_86: too big");
if (va == (vaddr_t) PDP_BASE || va == (vaddr_t) APDP_BASE)
panic("pmap_enter_86: trying to map over PDP/APDP!");
if (va >= VM_MIN_KERNEL_ADDRESS &&
!pmap_valid_entry(PDE(pmap, pdei(va))))
panic("pmap_enter: missing kernel PTP!");
#endif
if (pmap_initialized)
pve = pool_get(&pmap_pv_pool, PR_NOWAIT);
else
pve = NULL;
wired_count = resident_count = ptp_count = 0;
ptes = pmap_map_ptes_86(pmap);
if (pmap == pmap_kernel()) {
ptp = NULL;
} else {
ptp = pmap_get_ptp_86(pmap, pdei(va));
if (ptp == NULL) {
if (flags & PMAP_CANFAIL) {
pmap_unmap_ptes_86(pmap);
error = ENOMEM;
goto out;
}
panic("pmap_enter_86: get ptp failed");
}
}
opte = ptes[atop(va)];
if (pmap_valid_entry(opte)) {
if (wired && (opte & PG_W) == 0)
wired_count++;
else if (!wired && (opte & PG_W) != 0)
wired_count--;
if ((opte & PG_FRAME) == pa) {
if (opte & PG_PVLIST) {
pg = PHYS_TO_VM_PAGE(pa);
#ifdef DIAGNOSTIC
if (pg == NULL)
panic("pmap_enter_86: same pa "
"PG_PVLIST mapping with "
"unmanaged page "
"pa = 0x%lx (0x%lx)", pa,
atop(pa));
#endif
pmap_sync_flags_pte_86(pg, opte);
}
goto enter_now;
}
if (opte & PG_PVLIST) {
pg = PHYS_TO_VM_PAGE(opte & PG_FRAME);
#ifdef DIAGNOSTIC
if (pg == NULL)
panic("pmap_enter_86: PG_PVLIST mapping with "
"unmanaged page "
"pa = 0x%lx (0x%lx)", pa, atop(pa));
#endif
pmap_sync_flags_pte_86(pg, opte);
opve = pmap_remove_pv(pg, pmap, va);
pg = NULL;
}
} else {
resident_count++;
if (wired)
wired_count++;
if (ptp)
ptp_count++;
}
if (pmap_initialized && pg == NULL)
pg = PHYS_TO_VM_PAGE(pa);
if (pg != NULL) {
if (pve == NULL) {
pve = opve;
opve = NULL;
}
if (pve == NULL) {
if (flags & PMAP_CANFAIL) {
pmap_unmap_ptes_86(pmap);
error = ENOMEM;
goto out;
}
panic("pmap_enter_86: no pv entries available");
}
pmap_enter_pv(pg, pve, pmap, va, ptp);
pve = NULL;
}
enter_now:
npte = pa | protection_codes[prot] | PG_V;
pmap_exec_account(pmap, va, opte, npte);
if (wired)
npte |= PG_W;
if (nocache)
npte |= PG_N;
if (va < VM_MAXUSER_ADDRESS)
npte |= PG_u;
else if (va < VM_MAX_ADDRESS)
npte |= PG_RW;
if (pmap == pmap_kernel())
npte |= pmap_pg_g;
if (flags & PROT_READ)
npte |= PG_U;
if (flags & PROT_WRITE)
npte |= PG_M;
if (pg) {
npte |= PG_PVLIST;
if (pg->pg_flags & PG_PMAP_WC) {
KASSERT(nocache == 0);
wc = 1;
}
pmap_sync_flags_pte_86(pg, npte);
}
if (wc)
npte |= pmap_pg_wc;
opte = i386_atomic_testset_ul(&ptes[atop(va)], npte);
if (ptp)
ptp->wire_count += ptp_count;
pmap->pm_stats.resident_count += resident_count;
pmap->pm_stats.wired_count += wired_count;
if (pmap_valid_entry(opte)) {
if (nocache && (opte & PG_N) == 0)
wbinvd_on_all_cpus();
pmap_tlb_shootpage(pmap, va);
}
pmap_unmap_ptes_86(pmap);
pmap_tlb_shootwait();
error = 0;
out:
if (pve)
pool_put(&pmap_pv_pool, pve);
if (opve)
pool_put(&pmap_pv_pool, opve);
return error;
}
void
pmap_enter_special_86(vaddr_t va, paddr_t pa, vm_prot_t prot, u_int32_t flags)
{
struct pmap *pmap = pmap_kernel();
struct vm_page *ptppg = NULL;
pd_entry_t *pd, *ptp;
pt_entry_t *ptes;
uint32_t l2idx, l1idx;
paddr_t npa;
if (!cpu_meltdown)
return;
if (va < VM_MIN_KERNEL_ADDRESS)
panic("invalid special mapping va 0x%lx requested", va);
if (!pmap->pm_pdir_intel)
pmap_alloc_pdir_intel_x86(pmap);
DPRINTF("%s: pm_pdir_intel 0x%x pm_pdirpa_intel 0x%x\n", __func__,
(uint32_t)pmap->pm_pdir_intel, (uint32_t)pmap->pm_pdirpa_intel);
l2idx = pdei(va);
l1idx = ptei(va);
DPRINTF("%s: va 0x%08lx pa 0x%08lx prot 0x%08lx flags 0x%08x "
"l2idx %u l1idx %u\n", __func__, va, pa, (unsigned long)prot,
flags, l2idx, l1idx);
if ((pd = (pd_entry_t *)pmap->pm_pdir_intel) == NULL)
panic("%s: PD not initialized for pmap @ %p", __func__, pmap);
npa = pd[l2idx] & PMAP_PA_MASK;
if (!npa) {
ptppg = uvm_pagealloc(&pmap->pm_obj, ptp_i2o(l2idx + 1024),
NULL, UVM_PGA_USERESERVE|UVM_PGA_ZERO);
if (ptppg == NULL)
panic("%s: failed to allocate PT page", __func__);
atomic_clearbits_int(&ptppg->pg_flags, PG_BUSY);
ptppg->wire_count = 1;
npa = VM_PAGE_TO_PHYS(ptppg);
pd[l2idx] = (npa | PG_RW | PG_V | PG_M | PG_U);
DPRINTF("%s: allocated new PT page at phys 0x%x, "
"setting PDE[%d] = 0x%x\n", __func__, (uint32_t)npa,
l2idx, pd[l2idx]);
}
if (ptppg == NULL && (ptppg = PHYS_TO_VM_PAGE(npa)) == NULL)
panic("%s: no vm_page for PT page", __func__);
mtx_enter(&ptppg->mdpage.pv_mtx);
ptp = (pd_entry_t *)pmap_tmpmap_pa(npa);
ptp[l1idx] = (pa | protection_codes[prot] | PG_V | PG_M | PG_U | flags);
ptppg->wire_count++;
DPRINTF("%s: setting PTE[%d] = 0x%x (wire_count %d)\n", __func__,
l1idx, ptp[l1idx], ptppg->wire_count);
pmap_tmpunmap_pa();
mtx_leave(&ptppg->mdpage.pv_mtx);
if (!(cpu_feature & CPUID_PGE))
return;
ptes = pmap_map_ptes_86(pmap);
if (pmap_valid_entry(ptes[atop(va)]))
ptes[atop(va)] |= PG_G;
else
DPRINTF("%s: no U+K mapping for special mapping?\n", __func__);
pmap_unmap_ptes_86(pmap);
}
vaddr_t
pmap_growkernel_86(vaddr_t maxkvaddr)
{
struct pmap *kpm = pmap_kernel(), *pm;
int needed_kpde;
int s;
paddr_t ptaddr;
needed_kpde = (int)(maxkvaddr - VM_MIN_KERNEL_ADDRESS + (NBPD-1))
/ NBPD;
if (needed_kpde <= nkpde)
goto out;
s = splhigh();
for ( ; nkpde < needed_kpde ; nkpde++) {
if (uvm.page_init_done == 0) {
if (uvm_page_physget(&ptaddr) == 0)
panic("pmap_growkernel: out of memory");
pmap_zero_phys_86(ptaddr);
PDE(kpm, PDSLOT_KERN + nkpde) =
ptaddr | PG_RW | PG_V | PG_U | PG_M;
kpm->pm_stats.resident_count++;
continue;
}
while (!pmap_alloc_ptp_86(kpm, PDSLOT_KERN + nkpde, 0))
uvm_wait("pmap_growkernel");
mtx_enter(&pmaps_lock);
LIST_FOREACH(pm, &pmaps, pm_list) {
PDE(pm, PDSLOT_KERN + nkpde) =
PDE(kpm, PDSLOT_KERN + nkpde);
}
mtx_leave(&pmaps_lock);
}
splx(s);
out:
return (VM_MIN_KERNEL_ADDRESS + (nkpde * NBPD));
}
#ifdef MULTIPROCESSOR
struct {
volatile int lock __attribute__((aligned(64)));
} tlb_shoot_lock __attribute__((section(".kudata")));
struct {
volatile int cpu __attribute__((aligned(64)));
} tlb_shoot_cpu __attribute__((section(".kudata")));
volatile u_int tlb_shoot_counts[MAXCPUS] __attribute__((section(".kudata")));
volatile vaddr_t tlb_shoot_addr1 __attribute__((section(".kudata")));
volatile vaddr_t tlb_shoot_addr2 __attribute__((section(".kudata")));
static inline void
pmap_start_tlb_shoot(u_int targets, const char *func)
{
u_int cpuid = curcpu()->ci_cpuid;
while (atomic_cas_uint(&tlb_shoot_lock.lock, 0, 1) != 0) {
#ifdef MP_LOCKDEBUG
long nticks = __mp_lock_spinout;
#endif
while (tlb_shoot_lock.lock != 0) {
CPU_BUSY_CYCLE();
#ifdef MP_LOCKDEBUG
if (--nticks <= 0) {
db_printf("%s: spun out", func);
db_enter();
nticks = __mp_lock_spinout;
}
#endif
}
}
tlb_shoot_cpu.cpu = cpuid;
atomic_swap_uint(&tlb_shoot_counts[cpuid], targets);
}
void
pmap_tlb_shootwait(void)
{
u_int cpuid = curcpu()->ci_cpuid;
#ifdef MP_LOCKDEBUG
long nticks = __mp_lock_spinout;
#endif
while (tlb_shoot_counts[cpuid] > 0) {
CPU_BUSY_CYCLE();
#ifdef MP_LOCKDEBUG
if (--nticks <= 0) {
db_printf("%s: spun out", __func__);
db_enter();
nticks = __mp_lock_spinout;
}
#endif
}
}
static inline void
pmap_tlb_shootfail()
{
u_int cpuid = curcpu()->ci_cpuid;
if (atomic_dec_int_nv(&tlb_shoot_counts[cpuid]) == 0)
tlb_shoot_lock.lock = 0;
}
#endif
void
pmap_tlb_shootpage(struct pmap *pm, vaddr_t va)
{
#ifdef MULTIPROCESSOR
struct cpu_info *ci, *self = curcpu();
CPU_INFO_ITERATOR cii;
int targets = 0;
u_int64_t mask = 0;
CPU_INFO_FOREACH(cii, ci) {
if (ci == self || !pmap_is_active(pm, ci) ||
!(ci->ci_flags & CPUF_RUNNING))
continue;
mask |= (1ULL << ci->ci_cpuid);
targets++;
}
if (targets) {
int s = splvm();
pmap_start_tlb_shoot(targets, __func__);
tlb_shoot_addr1 = va;
CPU_INFO_FOREACH(cii, ci) {
if ((mask & (1ULL << ci->ci_cpuid)) == 0)
continue;
if (i386_fast_ipi(ci, LAPIC_IPI_INVLPG) != 0)
pmap_tlb_shootfail();
}
splx(s);
}
#endif
if (pmap_is_curpmap(pm))
pmap_update_pg(va);
}
void
pmap_tlb_shootrange(struct pmap *pm, vaddr_t sva, vaddr_t eva)
{
vaddr_t va;
#ifdef MULTIPROCESSOR
struct cpu_info *ci, *self = curcpu();
CPU_INFO_ITERATOR cii;
int targets = 0;
u_int64_t mask = 0;
CPU_INFO_FOREACH(cii, ci) {
if (ci == self || !pmap_is_active(pm, ci) ||
!(ci->ci_flags & CPUF_RUNNING))
continue;
mask |= (1ULL << ci->ci_cpuid);
targets++;
}
if (targets) {
int s = splvm();
pmap_start_tlb_shoot(targets, __func__);
tlb_shoot_addr1 = sva;
tlb_shoot_addr2 = eva;
CPU_INFO_FOREACH(cii, ci) {
if ((mask & (1ULL << ci->ci_cpuid)) == 0)
continue;
if (i386_fast_ipi(ci, LAPIC_IPI_INVLRANGE) != 0)
pmap_tlb_shootfail();
}
splx(s);
}
#endif
if (pmap_is_curpmap(pm))
for (va = sva; va < eva; va += PAGE_SIZE)
pmap_update_pg(va);
}
void
pmap_tlb_shoottlb(void)
{
#ifdef MULTIPROCESSOR
struct cpu_info *ci, *self = curcpu();
CPU_INFO_ITERATOR cii;
int targets = 0;
u_int64_t mask = 0;
CPU_INFO_FOREACH(cii, ci) {
if (ci == self || !(ci->ci_flags & CPUF_RUNNING))
continue;
mask |= (1ULL << ci->ci_cpuid);
targets++;
}
if (targets) {
int s = splvm();
pmap_start_tlb_shoot(targets, __func__);
CPU_INFO_FOREACH(cii, ci) {
if ((mask & (1ULL << ci->ci_cpuid)) == 0)
continue;
if (i386_fast_ipi(ci, LAPIC_IPI_INVLTLB) != 0)
pmap_tlb_shootfail();
}
splx(s);
}
#endif
tlbflush();
}
void
pmap_tlb_droppmap(struct pmap *pm)
{
struct cpu_info *self = curcpu();
#ifdef MULTIPROCESSOR
struct cpu_info *ci;
CPU_INFO_ITERATOR cii;
int targets = 0;
u_int64_t mask = 0;
CPU_INFO_FOREACH(cii, ci) {
if (ci == self || !(ci->ci_flags & CPUF_RUNNING) ||
ci->ci_curpmap != pm)
continue;
mask |= (1ULL << ci->ci_cpuid);
targets++;
}
if (targets) {
int s = splvm();
pmap_start_tlb_shoot(targets, __func__);
CPU_INFO_FOREACH(cii, ci) {
if ((mask & (1ULL << ci->ci_cpuid)) == 0)
continue;
if (i386_fast_ipi(ci, LAPIC_IPI_RELOADCR3) != 0)
pmap_tlb_shootfail();
}
splx(s);
}
#endif
if (self->ci_curpmap == pm)
pmap_activate(curproc);
pmap_tlb_shootwait();
}
u_int32_t (*pmap_pte_set_p)(vaddr_t, paddr_t, u_int32_t) =
pmap_pte_set_86;
u_int32_t (*pmap_pte_setbits_p)(vaddr_t, u_int32_t, u_int32_t) =
pmap_pte_setbits_86;
u_int32_t (*pmap_pte_bits_p)(vaddr_t) = pmap_pte_bits_86;
paddr_t (*pmap_pte_paddr_p)(vaddr_t) = pmap_pte_paddr_86;
int (*pmap_clear_attrs_p)(struct vm_page *, int) =
pmap_clear_attrs_86;
int (*pmap_enter_p)(pmap_t, vaddr_t, paddr_t, vm_prot_t, int) =
pmap_enter_86;
void (*pmap_enter_special_p)(vaddr_t, paddr_t, vm_prot_t,
u_int32_t) = pmap_enter_special_86;
int (*pmap_extract_p)(pmap_t, vaddr_t, paddr_t *) =
pmap_extract_86;
vaddr_t (*pmap_growkernel_p)(vaddr_t) = pmap_growkernel_86;
void (*pmap_page_remove_p)(struct vm_page *) = pmap_page_remove_86;
void (*pmap_do_remove_p)(struct pmap *, vaddr_t, vaddr_t, int) =
pmap_do_remove_86;
int (*pmap_test_attrs_p)(struct vm_page *, int) =
pmap_test_attrs_86;
void (*pmap_unwire_p)(struct pmap *, vaddr_t) = pmap_unwire_86;
void (*pmap_write_protect_p)(struct pmap *, vaddr_t, vaddr_t,
vm_prot_t) = pmap_write_protect_86;
void (*pmap_pinit_pd_p)(pmap_t) = pmap_pinit_pd_86;
void (*pmap_zero_phys_p)(paddr_t) = pmap_zero_phys_86;
void (*pmap_copy_page_p)(struct vm_page *, struct vm_page *) =
pmap_copy_page_86;