#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.122 2023/08/02 09:18:14 macallan Exp $");
#include "opt_cputype.h"
#include <sys/param.h>
#include <sys/atomic.h>
#include <sys/mutex.h>
#include <sys/pool.h>
#include <sys/proc.h>
#include <sys/rwlock.h>
#include <sys/systm.h>
#include <uvm/uvm.h>
#include <uvm/uvm_page_array.h>
#include <machine/cpu.h>
#include <machine/cpufunc.h>
#include <machine/iomod.h>
#include <machine/pcb.h>
#include <machine/pmap.h>
#include <machine/psl.h>
#include <machine/pte.h>
#include <machine/reg.h>
#include <hppa/hppa/hpt.h>
#include <hppa/hppa/machdep.h>
#if defined(DDB)
#include <ddb/db_output.h>
#endif
int pmap_hptsize = 16 * PAGE_SIZE;
vaddr_t pmap_hpt;
static struct pmap kernel_pmap_store;
struct pmap *const kernel_pmap_ptr = &kernel_pmap_store;
int hppa_sid_max = HPPA_SID_MAX;
struct pool pmap_pool;
struct pool pmap_pv_pool;
int pmap_pvlowat = 252;
bool pmap_initialized = false;
static kmutex_t pmaps_lock;
static union pmap_pv_locks {
kmutex_t lock;
char padding[COHERENCY_UNIT];
} pmap_pv_locks[64] __aligned(COHERENCY_UNIT);
#define PMAP_PV_LOCK(md) \
((uintptr_t)(md) >> 7 & (__arraycount(pmap_pv_locks) - 1))
u_int hppa_prot[8];
u_int sid_counter;
static const struct uvm_pagerops pmap_pager = {
};
#define NON_EQUIVALENT_ALIAS(sp1, va1, sp2, va2) \
(((((va1) ^ (va2)) & ~HPPA_PGAMASK) != 0) || \
((((sp1) ^ (sp2)) & ~HPPA_SPAMASK) != 0))
struct vm_page *pmap_pagealloc(struct uvm_object *, voff_t);
void pmap_pagefree(struct vm_page *);
static inline void pmap_lock(struct pmap *);
static inline void pmap_unlock(struct pmap *);
static inline bool pmap_trylock(struct pmap *);
static inline void pmap_sdir_set(pa_space_t, volatile uint32_t *);
static inline uint32_t *pmap_sdir_get(pa_space_t);
static inline volatile pt_entry_t *pmap_pde_get(volatile uint32_t *, vaddr_t);
static inline void pmap_pde_set(pmap_t, vaddr_t, paddr_t);
static inline pt_entry_t *pmap_pde_alloc(pmap_t, vaddr_t, struct vm_page **);
static inline struct vm_page *pmap_pde_ptp(pmap_t, volatile pt_entry_t *);
static inline void pmap_pde_release(pmap_t, vaddr_t, struct vm_page *);
static inline volatile pt_entry_t *pmap_pde_get(volatile uint32_t *, vaddr_t);
static inline void pmap_pde_set(pmap_t, vaddr_t, paddr_t);
void pmap_pte_flush(pmap_t, vaddr_t, pt_entry_t);
static inline pt_entry_t pmap_pte_get(volatile pt_entry_t *, vaddr_t);
static inline void pmap_pte_set(volatile pt_entry_t *, vaddr_t, pt_entry_t);
static inline pt_entry_t pmap_vp_find(pmap_t, vaddr_t);
static inline struct pv_entry *pmap_pv_alloc(void);
static inline void pmap_pv_free(struct pv_entry *);
static inline void pmap_pv_enter(struct vm_page *, struct pv_entry *, pmap_t,
vaddr_t , struct vm_page *, u_int);
static inline struct pv_entry *pmap_pv_remove(struct vm_page *, pmap_t,
vaddr_t);
static inline void pmap_pv_lock(const struct vm_page_md *md);
static inline void pmap_pv_unlock(const struct vm_page_md *md);
static inline bool pmap_pv_locked(const struct vm_page_md *md);
static inline void pmap_flush_page(struct vm_page *, bool);
static void pmap_resolve_alias(struct vm_page *, struct pmap *, vaddr_t,
pt_entry_t);
static void pmap_syncicache_page(struct vm_page *, pmap_t, vaddr_t);
static void pmap_page_physload(paddr_t, paddr_t);
void pmap_copy_page(paddr_t, paddr_t);
#ifdef USE_HPT
static inline struct hpt_entry *pmap_hash(pmap_t, vaddr_t);
static inline uint32_t pmap_vtag(pmap_t, vaddr_t);
#ifdef DDB
void pmap_hptdump(void);
#endif
#endif
#ifdef DDB
void pmap_dump_table(pa_space_t, vaddr_t);
void pmap_dump_pv(paddr_t);
#endif
#define IS_IOPAGE_P(pa) ((pa) >= HPPA_IOBEGIN)
#define IS_PVFEXEC_P(f) (((f) & PVF_EXEC) != 0)
#define pmap_pvh_attrs(a) \
(((a) & (PVF_MOD|PVF_REF)) ^ PVF_REF)
static inline void
pmap_lock(struct pmap *pm)
{
rw_enter(pm->pm_lock, RW_WRITER);
}
static inline void
pmap_unlock(struct pmap *pm)
{
rw_exit(pm->pm_lock);
}
static inline bool
pmap_trylock(struct pmap *pm)
{
return rw_tryenter(pm->pm_lock, RW_WRITER);
}
static inline void
pmap_pv_lock(const struct vm_page_md *md)
{
mutex_enter(&pmap_pv_locks[PMAP_PV_LOCK(md)].lock);
}
static inline void
pmap_pv_unlock(const struct vm_page_md *md)
{
mutex_exit(&pmap_pv_locks[PMAP_PV_LOCK(md)].lock);
}
static inline bool
pmap_pv_locked(const struct vm_page_md *md)
{
return mutex_owned(&pmap_pv_locks[PMAP_PV_LOCK(md)].lock);
}
struct vm_page *
pmap_pagealloc(struct uvm_object *obj, voff_t off)
{
struct vm_page *pg;
if ((pg = uvm_pagealloc(obj, off, NULL,
UVM_PGA_USERESERVE | UVM_PGA_ZERO)) == NULL)
printf("pmap_pagealloc fail\n");
return (pg);
}
void
pmap_pagefree(struct vm_page *pg)
{
paddr_t pa = VM_PAGE_TO_PHYS(pg);
pdcache(HPPA_SID_KERNEL, pa, PAGE_SIZE);
#if defined(HP8000_CPU) || defined(HP8200_CPU) || \
defined(HP8500_CPU) || defined(HP8600_CPU)
pdtlb(HPPA_SID_KERNEL, pa);
pitlb(HPPA_SID_KERNEL, pa);
#endif
uvm_pagefree(pg);
}
#ifdef USE_HPT
static inline struct hpt_entry *
pmap_hash(pmap_t pmap, vaddr_t va)
{
return (struct hpt_entry *)(pmap_hpt +
(((va >> 8) ^ (pmap->pm_space << 9)) & (pmap_hptsize - 1)));
}
static inline uint32_t
pmap_vtag(pmap_t pmap, vaddr_t va)
{
return (0x80000000 | (pmap->pm_space & 0xffff) |
((va >> 1) & 0x7fff0000));
}
#endif
static inline void
pmap_sdir_set(pa_space_t space, volatile uint32_t *pd)
{
volatile uint32_t *vtop;
mfctl(CR_VTOP, vtop);
KASSERT(vtop != NULL);
vtop[space] = (uint32_t)pd;
}
static inline uint32_t *
pmap_sdir_get(pa_space_t space)
{
uint32_t *vtop;
mfctl(CR_VTOP, vtop);
return ((uint32_t *)vtop[space]);
}
static inline volatile pt_entry_t *
pmap_pde_get(volatile uint32_t *pd, vaddr_t va)
{
return ((pt_entry_t *)pd[va >> 22]);
}
static inline void
pmap_pde_set(pmap_t pm, vaddr_t va, paddr_t ptp)
{
UVMHIST_FUNC(__func__);
UVMHIST_CALLARGS(maphist, "pm %#jx va %#jx ptp %#jx", (uintptr_t)pm,
va, ptp, 0);
KASSERT((ptp & PGOFSET) == 0);
pm->pm_pdir[va >> 22] = ptp;
}
static inline pt_entry_t *
pmap_pde_alloc(pmap_t pm, vaddr_t va, struct vm_page **pdep)
{
struct vm_page *pg;
paddr_t pa;
UVMHIST_FUNC(__func__);
UVMHIST_CALLARGS(maphist, "pm %#jx va %#jx pdep %#jx", (uintptr_t)pm,
va, (uintptr_t)pdep, 0);
KASSERT(pm != pmap_kernel());
KASSERT(rw_write_held(pm->pm_lock));
pg = pmap_pagealloc(&pm->pm_obj, va);
if (pg == NULL)
return NULL;
pa = VM_PAGE_TO_PHYS(pg);
UVMHIST_LOG(maphist, "pde %#jx", pa, 0, 0, 0);
pg->flags &= ~PG_BUSY;
pg->wire_count = 1;
pmap_pde_set(pm, va, pa);
pm->pm_stats.resident_count++;
pm->pm_ptphint = pg;
if (pdep)
*pdep = pg;
return ((pt_entry_t *)pa);
}
static inline struct vm_page *
pmap_pde_ptp(pmap_t pm, volatile pt_entry_t *pde)
{
paddr_t pa = (paddr_t)pde;
UVMHIST_FUNC(__func__);
UVMHIST_CALLARGS(maphist, "pm %#jx va %#jx pdep %#jx", (uintptr_t)pm,
(uintptr_t)pde, 0, 0);
if (pm->pm_ptphint && VM_PAGE_TO_PHYS(pm->pm_ptphint) == pa)
return (pm->pm_ptphint);
UVMHIST_LOG(maphist, "<--- done (%#jx)",
(uintptr_t)PHYS_TO_VM_PAGE(pa), 0, 0, 0);
return (PHYS_TO_VM_PAGE(pa));
}
static inline void
pmap_pde_release(pmap_t pmap, vaddr_t va, struct vm_page *ptp)
{
UVMHIST_FUNC(__func__);
UVMHIST_CALLARGS(maphist, "pm %#jx va %#jx ptp %#jx", (uintptr_t)pmap,
va, (uintptr_t)ptp, 0);
KASSERT(pmap != pmap_kernel());
if (--ptp->wire_count <= 1) {
UVMHIST_LOG(maphist, "disposing ptp %#jx", (uintptr_t)ptp, 0,
0, 0);
pmap_pde_set(pmap, va, 0);
pmap->pm_stats.resident_count--;
if (pmap->pm_ptphint == ptp)
pmap->pm_ptphint = NULL;
ptp->wire_count = 0;
KASSERT((ptp->flags & PG_BUSY) == 0);
pmap_pagefree(ptp);
}
}
static inline pt_entry_t
pmap_pte_get(volatile pt_entry_t *pde, vaddr_t va)
{
return (pde[(va >> 12) & 0x3ff]);
}
static inline void
pmap_pte_set(volatile pt_entry_t *pde, vaddr_t va, pt_entry_t pte)
{
#if 0
UVMHIST_FUNC(__func__);
UVMHIST_CALLARGS(maphist, "pdep %#jx va %#jx pte %#jx", (uintptr_t)pde,
va, pte, 0);
#endif
KASSERT(pde != NULL);
KASSERT(((paddr_t)pde & PGOFSET) == 0);
pde[(va >> 12) & 0x3ff] = pte;
}
void
pmap_pte_flush(pmap_t pmap, vaddr_t va, pt_entry_t pte)
{
UVMHIST_FUNC(__func__);
if (pmap != pmap_kernel() && va != 0) {
UVMHIST_CALLARGS(maphist, "pm %#jx va %#jx pte %#jx",
(uintptr_t)pmap, va, (uintptr_t)pte, 0);
}
fdcache(pmap->pm_space, va, PAGE_SIZE);
if (pte & PTE_PROT(TLB_EXECUTE)) {
ficache(pmap->pm_space, va, PAGE_SIZE);
pitlb(pmap->pm_space, va);
}
pdtlb(pmap->pm_space, va);
#ifdef USE_HPT
if (pmap_hpt) {
struct hpt_entry *hpt;
hpt = pmap_hash(pmap, va);
if (hpt->hpt_valid &&
hpt->hpt_space == pmap->pm_space &&
hpt->hpt_vpn == ((va >> 1) & 0x7fff0000))
hpt->hpt_space = 0xffff;
}
#endif
}
static inline pt_entry_t
pmap_vp_find(pmap_t pm, vaddr_t va)
{
volatile pt_entry_t *pde;
if (!(pde = pmap_pde_get(pm->pm_pdir, va)))
return (0);
return (pmap_pte_get(pde, va));
}
#ifdef DDB
void
pmap_dump_table(pa_space_t space, vaddr_t sva)
{
char buf[64];
volatile pt_entry_t *pde = NULL;
vaddr_t va = sva;
vaddr_t pdemask = 1;
pt_entry_t pte;
uint32_t *pd;
if (space > hppa_sid_max)
return;
pd = pmap_sdir_get(space);
if (!pd)
return;
do {
if (pdemask != (va & PDE_MASK)) {
pdemask = va & PDE_MASK;
pde = pmap_pde_get(pd, va);
if (!pde) {
va = pdemask + PDE_SIZE;
continue;
}
db_printf("%x:%8p:\n", space, pde);
}
pte = pmap_pte_get(pde, va);
if (pte) {
snprintb(buf, sizeof(buf), TLB_BITS,
TLB_PROT(pte & PAGE_MASK));
db_printf("0x%08lx-0x%08x:%s\n", va, pte & ~PAGE_MASK,
buf);
}
va += PAGE_SIZE;
} while (va != 0);
}
void
pmap_dump_pv(paddr_t pa)
{
struct vm_page *pg;
struct vm_page_md *md;
struct pv_entry *pve;
pg = PHYS_TO_VM_PAGE(pa);
if (pg == NULL)
return;
md = VM_PAGE_TO_MD(pg);
db_printf("pg %p attr 0x%08x\n", pg, md->pvh_attrs);
for (pve = md->pvh_list; pve; pve = pve->pv_next)
db_printf("%x:%lx\n", pve->pv_pmap->pm_space,
pve->pv_va & PV_VAMASK);
}
#endif
static void
pmap_resolve_alias(struct vm_page *pg, struct pmap *pm, vaddr_t va,
pt_entry_t pte)
{
UVMHIST_FUNC(__func__);
UVMHIST_CALLARGS(maphist, "pg %#jx pm %#jx va %#jx pte %#jx",
(uintptr_t)pg, (uintptr_t)pm, va, pte);
struct vm_page_md * const md = VM_PAGE_TO_MD(pg);
struct pv_entry *pve, *npve, **pvp;
restart:
pmap_pv_lock(md);
pvp = &md->pvh_list;
for (pve = md->pvh_list; pve; pve = npve) {
const pmap_t ppm = pve->pv_pmap;
const vaddr_t pva = pve->pv_va & PV_VAMASK;
UVMHIST_LOG(maphist, "... pm %#jx va %#jx", (uintptr_t)ppm,
pva, 0, 0);
npve = pve->pv_next;
volatile pt_entry_t *pde;
pt_entry_t ppte;
if (pve->pv_va & PV_KENTER) {
pde = pmap_pde_get(ppm->pm_pdir, pva);
ppte = pmap_pte_get(pde, pva);
} else {
UVMHIST_LOG(maphist, "... pm %#jx va %#jx... checking",
(uintptr_t)ppm, pva, 0, 0);
bool locked = true;
if (pm != ppm) {
pmap_reference(ppm);
locked = pmap_trylock(ppm);
}
if (!locked) {
pmap_pv_unlock(md);
pmap_lock(ppm);
pmap_unlock(ppm);
pmap_destroy(ppm);
UVMHIST_LOG(maphist, "... failed lock", 0, 0, 0,
0);
goto restart;
}
pde = pmap_pde_get(ppm->pm_pdir, pva);
ppte = pmap_pte_get(pde, pva);
md->pvh_attrs |= pmap_pvh_attrs(ppte);
}
const bool writeable =
((pte | ppte) & PTE_PROT(TLB_WRITE)) != 0;
if ((va & HPPA_PGAOFF) != (pva & HPPA_PGAOFF) && writeable) {
UVMHIST_LOG(maphist,
"aliased writeable mapping %#jx:%#jx",
ppm->pm_space, pva, 0, 0);
pmap_pte_flush(ppm, pva, ppte);
if (ppte & PTE_PROT(TLB_WIRED))
ppm->pm_stats.wired_count--;
ppm->pm_stats.resident_count--;
if (pve->pv_va & PV_KENTER) {
*pvp = pve;
pvp = &pve->pv_next;
} else {
pmap_pte_set(pde, pva, 0);
*pvp = npve;
pmap_pv_unlock(md);
pmap_pv_free(pve);
if (pm != ppm) {
pmap_unlock(ppm);
pmap_destroy(ppm);
}
UVMHIST_LOG(maphist, "... removed", 0,
0, 0, 0);
goto restart;
}
} else {
UVMHIST_LOG(maphist, "not aliased writeable mapping",
0,0,0,0);
if (!(pve->pv_va & PV_KENTER) && pm != ppm) {
pmap_unlock(ppm);
pmap_destroy(ppm);
}
*pvp = pve;
pvp = &pve->pv_next;
}
}
md->pvh_attrs &= ~PVF_EXEC;
*pvp = NULL;
#ifdef DEBUG
int ret = 0;
for (pve = md->pvh_list; pve; pve = pve->pv_next) {
vaddr_t pva = pve->pv_va & PV_VAMASK;
UVMHIST_LOG(maphist, "... pm %#jx va %#jx",
(uintptr_t)pve->pv_pmap, pva, 0, 0);
pte |= pmap_vp_find(pve->pv_pmap, pva);
if ((va & HPPA_PGAOFF) != (pva & HPPA_PGAOFF) &&
(pte & PTE_PROT(TLB_WRITE))) {
UVMHIST_LOG(maphist,
"aliased writable mapping %#jx:%#jx",
pve->pv_pmap->pm_space, pve->pv_va, 0, 0);
ret++;
}
}
UVMHIST_LOG(maphist, "check returned %jd", ret, 0, 0, 0);
#endif
pmap_pv_unlock(md);
UVMHIST_LOG(maphist, "<--- done", 0, 0, 0, 0);
return;
}
static inline struct pv_entry *
pmap_pv_alloc(void)
{
struct pv_entry *pv;
pv = pool_get(&pmap_pv_pool, PR_NOWAIT);
return (pv);
}
static inline void
pmap_pv_free(struct pv_entry *pv)
{
if (pv->pv_ptp)
pmap_pde_release(pv->pv_pmap, pv->pv_va & PV_VAMASK,
pv->pv_ptp);
pool_put(&pmap_pv_pool, pv);
}
static inline void
pmap_pv_enter(struct vm_page *pg, struct pv_entry *pve, pmap_t pm,
vaddr_t va, struct vm_page *pdep, u_int flags)
{
struct vm_page_md * const md = VM_PAGE_TO_MD(pg);
UVMHIST_FUNC(__func__);
UVMHIST_CALLARGS(maphist, "pg %#jx pve %#jx pm %#jx va %#jx",
(uintptr_t)pg, (uintptr_t)pve, (uintptr_t)pm, va);
UVMHIST_LOG(maphist, "...pdep %#jx flags %#jx",
(uintptr_t)pdep, flags, 0, 0);
KASSERT(pmap_pv_locked(md));
pve->pv_pmap = pm;
pve->pv_va = va | flags;
pve->pv_ptp = pdep;
pve->pv_next = md->pvh_list;
md->pvh_list = pve;
}
static inline struct pv_entry *
pmap_pv_remove(struct vm_page *pg, pmap_t pmap, vaddr_t va)
{
UVMHIST_FUNC(__func__);
UVMHIST_CALLARGS(maphist, "pg %#jx pm %#jx va %#jx",
(uintptr_t)pg, (uintptr_t)pmap, va, 0);
struct vm_page_md * const md = VM_PAGE_TO_MD(pg);
struct pv_entry **pve, *pv;
KASSERT(pmap_pv_locked(md));
for (pv = *(pve = &md->pvh_list);
pv; pv = *(pve = &(*pve)->pv_next)) {
if (pv->pv_pmap == pmap && (pv->pv_va & PV_VAMASK) == va) {
*pve = pv->pv_next;
break;
}
}
if (IS_PVFEXEC_P(md->pvh_attrs)) {
if (md->pvh_list == NULL) {
md->pvh_attrs &= ~PVF_EXEC;
} else {
pmap_syncicache_page(pg, pmap, va);
}
}
return (pv);
}
#define FIRST_16M atop(16 * 1024 * 1024)
static void
pmap_page_physload(paddr_t spa, paddr_t epa)
{
if (spa == epa)
return;
if (spa < FIRST_16M && epa <= FIRST_16M) {
uvm_page_physload(spa, epa, spa, epa, VM_FREELIST_ISADMA);
} else if (spa < FIRST_16M && epa > FIRST_16M) {
uvm_page_physload(spa, FIRST_16M, spa, FIRST_16M,
VM_FREELIST_ISADMA);
uvm_page_physload(FIRST_16M, epa, FIRST_16M, epa,
VM_FREELIST_DEFAULT);
} else {
uvm_page_physload(spa, epa, spa, epa, VM_FREELIST_DEFAULT);
}
availphysmem += epa - spa;
}
void
pmap_bootstrap(vaddr_t vstart)
{
UVMHIST_FUNC(__func__);
UVMHIST_CALLED(maphist);
vaddr_t va, addr;
vsize_t size;
extern paddr_t hppa_vtop;
pmap_t kpm;
int npdes, nkpdes;
extern int resvphysmem;
vsize_t btlb_entry_min, btlb_entry_max, btlb_entry_got;
paddr_t ksrx, kerx, ksro, kero, ksrw, kerw;
extern int usebtlb;
extern int kernel_text, etext;
extern int __rodata_start, __rodata_end;
extern int __data_start;
uvm_md_init();
hppa_prot[UVM_PROT_NONE] = TLB_AR_NA;
hppa_prot[UVM_PROT_READ] = TLB_AR_R;
hppa_prot[UVM_PROT_WRITE] = TLB_AR_RW;
hppa_prot[UVM_PROT_RW] = TLB_AR_RW;
hppa_prot[UVM_PROT_EXEC] = TLB_AR_RX;
hppa_prot[UVM_PROT_RX] = TLB_AR_RX;
hppa_prot[UVM_PROT_WX] = TLB_AR_RWX;
hppa_prot[UVM_PROT_RWX] = TLB_AR_RWX;
addr = round_page(vstart);
kpm = pmap_kernel();
memset(kpm, 0, sizeof(*kpm));
rw_init(&kpm->pm_obj_lock);
uvm_obj_init(&kpm->pm_obj, &pmap_pager, false, 1);
uvm_obj_setlock(&kpm->pm_obj, &kpm->pm_obj_lock);
kpm->pm_space = HPPA_SID_KERNEL;
kpm->pm_pid = HPPA_PID_KERNEL;
kpm->pm_pdir_pg = NULL;
kpm->pm_pdir = (uint32_t *)addr;
memset((void *)addr, 0, PAGE_SIZE);
fdcache(HPPA_SID_KERNEL, addr, PAGE_SIZE);
addr += PAGE_SIZE;
mtctl(addr, CR_VTOP);
hppa_vtop = addr;
size = round_page((hppa_sid_max + 1) * 4);
memset((void *)addr, 0, size);
fdcache(HPPA_SID_KERNEL, addr, size);
addr += size;
pmap_sdir_set(HPPA_SID_KERNEL, kpm->pm_pdir);
#ifdef USE_HPT
if (pmap_hptsize) {
struct hpt_entry *hptp;
int i, error;
if (addr & (pmap_hptsize - 1))
addr += pmap_hptsize;
addr &= ~(pmap_hptsize - 1);
memset((void *)addr, 0, pmap_hptsize);
hptp = (struct hpt_entry *)addr;
for (i = pmap_hptsize / sizeof(struct hpt_entry); i--; ) {
hptp[i].hpt_valid = 0;
hptp[i].hpt_space = 0xffff;
hptp[i].hpt_vpn = 0;
}
pmap_hpt = addr;
addr += pmap_hptsize;
UVMHIST_LOG(maphist, "hpt_table %#jx @ %#jx",
pmap_hptsize, addr, 0, 0);
if ((error = (cpu_hpt_init)(pmap_hpt, pmap_hptsize)) < 0) {
printf("WARNING: HPT init error %d -- DISABLED\n",
error);
pmap_hpt = 0;
} else {
UVMHIST_LOG(maphist,
"HPT installed for %jd entries @ %#jx",
pmap_hptsize / sizeof(struct hpt_entry), addr, 0,
0);
}
}
#endif
lwp0.l_md.md_regs->tf_vtop = hppa_vtop;
nkpdes = (VM_MAX_KERNEL_ADDRESS - VM_MIN_KERNEL_ADDRESS) / PDE_SIZE;
nkpdes += HPPA_IOLEN / PDE_SIZE;
npdes = nkpdes + (physmem + atop(PDE_SIZE) - 1) / atop(PDE_SIZE);
UVMHIST_LOG(maphist, "npdes %jd", npdes, 0, 0, 0);
for (va = 0; npdes--; va += PDE_SIZE, addr += PAGE_SIZE) {
if (npdes == nkpdes - 1)
va = SYSCALLGATE;
if (npdes == HPPA_IOLEN / PDE_SIZE - 1)
va = HPPA_IOBEGIN;
memset((void *)addr, 0, PAGE_SIZE);
UVMHIST_LOG(maphist, "pde premap 0x%08jx 0x%08jx", va,
addr, 0, 0);
pmap_pde_set(kpm, va, addr);
kpm->pm_stats.resident_count++;
}
resvphysmem = atop(addr);
ksrx = (paddr_t) &kernel_text;
kerx = (paddr_t) &etext;
ksro = (paddr_t) &__rodata_start;
kero = (paddr_t) &__rodata_end;
ksrw = (paddr_t) &__data_start;
kerw = addr;
btlb_entry_min = (vsize_t) hppa_btlb_size_min * PAGE_SIZE;
btlb_entry_max = (vsize_t) hppa_btlb_size_max * PAGE_SIZE;
btlb_entry_min = (vaddr_t) &kernel_text;
if (usebtlb) {
#define BTLB_SET_SIZE 16
vaddr_t btlb_entry_start[BTLB_SET_SIZE];
vsize_t btlb_entry_size[BTLB_SET_SIZE];
int btlb_entry_vm_prot[BTLB_SET_SIZE];
int btlb_i;
int btlb_j;
addr = ksrx;
UVMHIST_LOG(maphist,
"BTLB mapping text and rodata @ %#jx - %#jx", addr, kero,
0, 0);
btlb_j = 0;
while (addr < (vaddr_t) kero) {
KASSERT(btlb_j < BTLB_SET_SIZE);
btlb_entry_start[btlb_j] = addr;
btlb_entry_size[btlb_j] = btlb_entry_min;
btlb_entry_vm_prot[btlb_j] =
VM_PROT_READ | VM_PROT_EXECUTE;
if (addr + btlb_entry_min > kero)
btlb_entry_vm_prot[btlb_j] |= VM_PROT_WRITE;
while (btlb_j > 0 &&
btlb_entry_vm_prot[btlb_j] ==
btlb_entry_vm_prot[btlb_j - 1] &&
btlb_entry_size[btlb_j] ==
btlb_entry_size[btlb_j - 1] &&
!(btlb_entry_start[btlb_j - 1] &
((btlb_entry_size[btlb_j - 1] << 1) - 1)) &&
(btlb_entry_size[btlb_j - 1] << 1) <=
btlb_entry_max)
btlb_entry_size[--btlb_j] <<= 1;
addr =
btlb_entry_start[btlb_j] + btlb_entry_size[btlb_j];
btlb_j++;
}
UVMHIST_LOG(maphist, "mapping data, bss, etc @ %#jx - %#jx",
addr, kerw, 0, 0);
while (addr < kerw) {
KASSERT(btlb_j < BTLB_SET_SIZE);
size = btlb_entry_min;
while ((addr + size) < kerw &&
(size << 1) < btlb_entry_max &&
!(addr & ((size << 1) - 1)))
size <<= 1;
btlb_entry_start[btlb_j] = addr;
btlb_entry_size[btlb_j] = size;
btlb_entry_vm_prot[btlb_j] =
VM_PROT_READ | VM_PROT_WRITE;
addr =
btlb_entry_start[btlb_j] + btlb_entry_size[btlb_j];
btlb_j++;
}
for (btlb_i = 0; btlb_i < btlb_j; btlb_i++) {
int error;
int prot;
btlb_entry_got = btlb_entry_size[btlb_i];
prot = btlb_entry_vm_prot[btlb_i];
error = hppa_btlb_insert(kpm->pm_space,
btlb_entry_start[btlb_i], btlb_entry_start[btlb_i],
&btlb_entry_got,
kpm->pm_pid | pmap_prot(kpm, prot));
if (error)
panic("%s: cannot insert BTLB entry",
__func__);
if (btlb_entry_got != btlb_entry_size[btlb_i])
panic("%s: BTLB entry mapped wrong amount",
__func__);
}
kerw =
btlb_entry_start[btlb_j - 1] + btlb_entry_size[btlb_j - 1];
}
availphysmem = 0;
pmap_page_physload(resvmem, atop(ksrx));
pmap_page_physload(atop(kero), atop(ksrw));
pmap_page_physload(atop(kerw), physmem);
mutex_init(&pmaps_lock, MUTEX_DEFAULT, IPL_NONE);
for (va = PAGE_SIZE; va < ptoa(physmem); va += PAGE_SIZE) {
vm_prot_t prot = UVM_PROT_RW;
if (va < resvmem)
prot = UVM_PROT_RX;
else if (va >= ksrx && va < kerx)
prot = UVM_PROT_RX;
else if (va >= ksro && va < kero)
prot = UVM_PROT_R;
#ifdef DIAGNOSTIC
else if (va == uvm_lwp_getuarea(&lwp0) + USPACE - PAGE_SIZE)
prot = UVM_PROT_NONE;
#endif
pmap_kenter_pa(va, va, prot, PMAP_DIRECTMAP);
}
UVMHIST_LOG(maphist, "mapped %#jx - %#jx", ksro, kero, 0, 0);
UVMHIST_LOG(maphist, "mapped %#jx - %#jx", ksrw, kerw, 0, 0);
}
void
pmap_init(void)
{
extern void gateway_page(void);
volatile pt_entry_t *pde;
int i;
UVMHIST_FUNC(__func__)
UVMHIST_CALLED(maphist);
sid_counter = HPPA_SID_KERNEL;
pool_init(&pmap_pool, sizeof(struct pmap), 0, 0, 0, "pmappl",
&pool_allocator_nointr, IPL_NONE);
pool_init(&pmap_pv_pool, sizeof(struct pv_entry), 0, 0, 0, "pmappv",
&pool_allocator_nointr, IPL_NONE);
pool_setlowat(&pmap_pv_pool, pmap_pvlowat);
pool_sethiwat(&pmap_pv_pool, pmap_pvlowat * 32);
if (!(pde = pmap_pde_get(pmap_kernel()->pm_pdir, SYSCALLGATE)) &&
!(pde = pmap_pde_alloc(pmap_kernel(), SYSCALLGATE, NULL)))
panic("pmap_init: cannot allocate pde");
pmap_pte_set(pde, SYSCALLGATE, (paddr_t)&gateway_page |
PTE_PROT(TLB_GATE_PROT));
for (i = 0; i < __arraycount(pmap_pv_locks); i++)
mutex_init(&pmap_pv_locks[i].lock, MUTEX_DEFAULT, IPL_VM);
pmap_initialized = true;
UVMHIST_LOG(maphist, "<--- done", 0, 0, 0, 0);
}
void
pmap_virtual_space(vaddr_t *startp, vaddr_t *endp)
{
*startp = SYSCALLGATE + PAGE_SIZE;
*endp = VM_MAX_KERNEL_ADDRESS;
}
pmap_t
pmap_create(void)
{
pmap_t pmap;
pa_space_t space;
UVMHIST_FUNC(__func__)
UVMHIST_CALLED(maphist);
pmap = pool_get(&pmap_pool, PR_WAITOK);
UVMHIST_LOG(maphist, "pm %#jx", (uintptr_t)pmap, 0, 0, 0);
rw_init(&pmap->pm_obj_lock);
uvm_obj_init(&pmap->pm_obj, &pmap_pager, false, 1);
uvm_obj_setlock(&pmap->pm_obj, &pmap->pm_obj_lock);
mutex_enter(&pmaps_lock);
for (space = sid_counter; pmap_sdir_get(space);
space = (space + 1) % hppa_sid_max)
;
if ((pmap->pm_pdir_pg = pmap_pagealloc(NULL, 0)) == NULL)
panic("pmap_create: no pages");
pmap->pm_ptphint = NULL;
pmap->pm_pdir = (uint32_t *)VM_PAGE_TO_PHYS(pmap->pm_pdir_pg);
pmap_sdir_set(space, pmap->pm_pdir);
pmap->pm_space = space;
pmap->pm_pid = (space + 1) << 1;
pmap->pm_stats.resident_count = 1;
pmap->pm_stats.wired_count = 0;
mutex_exit(&pmaps_lock);
UVMHIST_LOG(maphist, "pm %#jx, space %jd, pid %jd",
(uintptr_t)pmap, space, pmap->pm_pid, 0);
return (pmap);
}
void
pmap_destroy(pmap_t pmap)
{
UVMHIST_FUNC(__func__)
UVMHIST_CALLARGS(maphist, "pm %#jx", (uintptr_t)pmap, 0, 0, 0);
#ifdef DIAGNOSTIC
struct uvm_page_array a;
struct vm_page *pg;
off_t off;
#endif
membar_release();
if (atomic_dec_uint_nv(&pmap->pm_obj.uo_refs) > 0)
return;
membar_acquire();
#ifdef DIAGNOSTIC
uvm_page_array_init(&a, &pmap->pm_obj, 0);
off = 0;
rw_enter(pmap->pm_lock, RW_WRITER);
while ((pg = uvm_page_array_fill_and_peek(&a, off, 0)) != NULL) {
pt_entry_t *pde, *epde;
struct vm_page *spg;
struct pv_entry *pv, *npv;
paddr_t pa;
vaddr_t va;
off = pg->offset + PAGE_SIZE;
uvm_page_array_advance(&a);
KASSERT(pg != pmap->pm_pdir_pg);
pa = VM_PAGE_TO_PHYS(pg);
UVMHIST_LOG(maphist, "pm %#jx: stray ptp %#jx w/ %jd entries:",
(uintptr_t)pmap, pa, pg->wire_count - 1, 0);
pde = (pt_entry_t *)pa;
epde = (pt_entry_t *)(pa + PAGE_SIZE);
for (; pde < epde; pde++) {
if (*pde == 0)
continue;
spg = PHYS_TO_VM_PAGE(PTE_PAGE(*pde));
if (spg == NULL)
continue;
struct vm_page_md * const md = VM_PAGE_TO_MD(spg);
pmap_pv_lock(md);
for (pv = md->pvh_list; pv != NULL; pv = npv) {
npv = pv->pv_next;
if (pv->pv_pmap != pmap)
continue;
UVMHIST_LOG(maphist, " %#jx", pv->pv_va, 0, 0,
0);
va = pv->pv_va & PV_VAMASK;
pmap_pv_unlock(md);
pmap_remove(pmap, va, va + PAGE_SIZE);
pmap_pv_lock(md);
npv = md->pvh_list;
}
pmap_pv_unlock(md);
}
}
rw_exit(pmap->pm_lock);
uvm_page_array_fini(&a);
#endif
pmap_sdir_set(pmap->pm_space, 0);
rw_enter(pmap->pm_lock, RW_WRITER);
pmap_pagefree(pmap->pm_pdir_pg);
rw_exit(pmap->pm_lock);
uvm_obj_destroy(&pmap->pm_obj, false);
rw_destroy(&pmap->pm_obj_lock);
pool_put(&pmap_pool, pmap);
}
void
pmap_reference(pmap_t pmap)
{
UVMHIST_FUNC(__func__)
UVMHIST_CALLARGS(maphist, "pm %#jx", (uintptr_t)pmap, 0, 0, 0);
atomic_inc_uint(&pmap->pm_obj.uo_refs);
}
void
pmap_syncicache_page(struct vm_page *pg, pmap_t pm, vaddr_t va)
{
struct vm_page_md * const md = VM_PAGE_TO_MD(pg);
struct pv_entry *pve = md->pvh_list;
for (; pve; pve = pve->pv_next) {
pmap_t fpm = pve->pv_pmap;
vaddr_t fva = pve->pv_va & PV_VAMASK;
pt_entry_t pte = pmap_vp_find(fpm, fva);
if ((pte & PTE_PROT(TLB_DIRTY)) == 0)
continue;
if (pm == fpm && va == fva)
continue;
fdcache(fpm->pm_space, fva, PAGE_SIZE);
ficache(fpm->pm_space, fva, PAGE_SIZE);
break;
}
}
int
pmap_enter(pmap_t pmap, vaddr_t va, paddr_t pa, vm_prot_t prot, u_int flags)
{
volatile pt_entry_t *pde;
pt_entry_t pte;
struct vm_page *pg = NULL, *ptp = NULL;
struct pv_entry *pve = NULL;
bool wired = (flags & PMAP_WIRED) != 0;
UVMHIST_FUNC(__func__);
UVMHIST_CALLARGS(maphist, "pm %#jx va %#jx pa %#jx prot %#jx",
(uintptr_t)pmap, va, pa, prot);
UVMHIST_LOG(maphist, "...flags %#jx", flags, 0, 0, 0);
pmap_lock(pmap);
if (!(pde = pmap_pde_get(pmap->pm_pdir, va)) &&
!(pde = pmap_pde_alloc(pmap, va, &ptp))) {
if (flags & PMAP_CANFAIL) {
pmap_unlock(pmap);
return (ENOMEM);
}
panic("pmap_enter: cannot allocate pde");
}
if (!ptp)
ptp = pmap_pde_ptp(pmap, pde);
if ((pte = pmap_pte_get(pde, va))) {
UVMHIST_LOG(maphist, "remapping %#jx -> %#jx", pte, pa, 0, 0);
pmap_pte_flush(pmap, va, pte);
if (wired && !(pte & PTE_PROT(TLB_WIRED)))
pmap->pm_stats.wired_count++;
else if (!wired && (pte & PTE_PROT(TLB_WIRED)))
pmap->pm_stats.wired_count--;
pg = PHYS_TO_VM_PAGE(PTE_PAGE(pte));
if (PTE_PAGE(pte) == pa) {
UVMHIST_LOG(maphist, "same page", 0, 0, 0, 0);
goto enter;
}
if (pg != NULL) {
struct vm_page_md * const md = VM_PAGE_TO_MD(pg);
pmap_pv_lock(md);
pve = pmap_pv_remove(pg, pmap, va);
md->pvh_attrs |= pmap_pvh_attrs(pte);
pmap_pv_unlock(md);
}
} else {
UVMHIST_LOG(maphist, "new mapping %#jx -> %#jx",
va, pa, 0, 0);
pte = PTE_PROT(TLB_REFTRAP);
pmap->pm_stats.resident_count++;
if (wired)
pmap->pm_stats.wired_count++;
if (ptp)
ptp->wire_count++;
}
if (pmap_initialized && (pg = PHYS_TO_VM_PAGE(pa))) {
struct vm_page_md * const md = VM_PAGE_TO_MD(pg);
if (!pve && !(pve = pmap_pv_alloc())) {
if (flags & PMAP_CANFAIL) {
pmap_unlock(pmap);
return (ENOMEM);
}
panic("%s: no pv entries available", __func__);
}
pte |= PTE_PROT(pmap_prot(pmap, prot));
pmap_resolve_alias(pg, pmap, va, pte);
pmap_pv_lock(md);
pmap_pv_enter(pg, pve, pmap, va, ptp, 0);
pmap_pv_unlock(md);
} else if (pve) {
pmap_pv_free(pve);
}
enter:
pte = pa | PTE_PROT(pmap_prot(pmap, prot)) |
(pte & PTE_PROT(TLB_UNCACHEABLE|TLB_DIRTY|TLB_REFTRAP));
if (pg != NULL) {
struct vm_page_md * const md = VM_PAGE_TO_MD(pg);
if ((pte & PTE_PROT(TLB_EXECUTE)) != 0 &&
!IS_PVFEXEC_P(md->pvh_attrs)) {
pmap_syncicache_page(pg, pmap, va);
md->pvh_attrs |= PVF_EXEC;
}
}
if (IS_IOPAGE_P(pa))
pte |= PTE_PROT(TLB_UNCACHEABLE);
if (wired)
pte |= PTE_PROT(TLB_WIRED);
pmap_pte_set(pde, va, pte);
pmap_unlock(pmap);
UVMHIST_LOG(maphist, "<--- done (0)", 0, 0, 0, 0);
return (0);
}
void
pmap_remove(pmap_t pmap, vaddr_t sva, vaddr_t eva)
{
UVMHIST_FUNC(__func__);
UVMHIST_CALLARGS(maphist, "sva %#jx eva %#jx", sva, eva, 0, 0);
struct pv_entry *pve;
volatile pt_entry_t *pde = NULL;
pt_entry_t pte;
struct vm_page *pg, *ptp;
vaddr_t pdemask;
int batch;
pmap_lock(pmap);
for (batch = 0; sva < eva; sva += PAGE_SIZE) {
pdemask = sva & PDE_MASK;
if (!(pde = pmap_pde_get(pmap->pm_pdir, sva))) {
sva = pdemask + PDE_SIZE - PAGE_SIZE;
continue;
}
batch = pdemask == sva && sva + PDE_SIZE <= eva;
if ((pte = pmap_pte_get(pde, sva))) {
pmap_pte_flush(pmap, sva, pte);
if (pte & PTE_PROT(TLB_WIRED))
pmap->pm_stats.wired_count--;
pmap->pm_stats.resident_count--;
if (!batch)
pmap_pte_set(pde, sva, 0);
if (pmap_initialized &&
(pg = PHYS_TO_VM_PAGE(PTE_PAGE(pte)))) {
struct vm_page_md * const md =
VM_PAGE_TO_MD(pg);
pmap_pv_lock(md);
pve = pmap_pv_remove(pg, pmap, sva);
md->pvh_attrs |= pmap_pvh_attrs(pte);
pmap_pv_unlock(md);
if (pve != NULL)
pmap_pv_free(pve);
} else {
if (IS_IOPAGE_P(PTE_PAGE(pte))) {
ptp = pmap_pde_ptp(pmap, pde);
if (ptp != NULL)
pmap_pde_release(pmap, sva,
ptp);
}
}
}
}
pmap_unlock(pmap);
UVMHIST_LOG(maphist, "<--- done", 0, 0, 0, 0);
}
void
pmap_write_protect(pmap_t pmap, vaddr_t sva, vaddr_t eva, vm_prot_t prot)
{
UVMHIST_FUNC(__func__);
UVMHIST_CALLARGS(maphist, "pm %#jx sva %#jx eva %#jx prot %#jx",
(uintptr_t)pmap, sva, eva, prot);
struct vm_page *pg;
volatile pt_entry_t *pde = NULL;
pt_entry_t pte;
u_int pteprot, pdemask;
sva = trunc_page(sva);
pteprot = PTE_PROT(pmap_prot(pmap, prot));
pmap_lock(pmap);
for (pdemask = 1; sva < eva; sva += PAGE_SIZE) {
if (pdemask != (sva & PDE_MASK)) {
pdemask = sva & PDE_MASK;
if (!(pde = pmap_pde_get(pmap->pm_pdir, sva))) {
sva = pdemask + PDE_SIZE - PAGE_SIZE;
continue;
}
}
if ((pte = pmap_pte_get(pde, sva))) {
UVMHIST_LOG(maphist, "va% #jx pte %#jx", sva, pte,
0, 0);
if ((pte & PTE_PROT(TLB_AR_MASK)) == pteprot)
continue;
pg = PHYS_TO_VM_PAGE(PTE_PAGE(pte));
if (pg != NULL) {
struct vm_page_md * const md =
VM_PAGE_TO_MD(pg);
pmap_pv_lock(md);
md->pvh_attrs |= pmap_pvh_attrs(pte);
pmap_pv_unlock(md);
}
pmap_pte_flush(pmap, sva, pte);
pte &= ~PTE_PROT(TLB_AR_MASK);
pte |= pteprot;
pmap_pte_set(pde, sva, pte);
}
}
pmap_unlock(pmap);
}
void
pmap_page_remove(struct vm_page *pg)
{
UVMHIST_FUNC(__func__)
UVMHIST_CALLARGS(maphist, "pg %#jx", (uintptr_t)pg, 0, 0, 0);
struct vm_page_md * const md = VM_PAGE_TO_MD(pg);
struct pv_entry *pve, *npve, **pvp;
if (md->pvh_list == NULL) {
KASSERT((md->pvh_attrs & PVF_EXEC) == 0);
return;
}
restart:
pmap_pv_lock(md);
pvp = &md->pvh_list;
for (pve = md->pvh_list; pve; pve = npve) {
pmap_t pmap = pve->pv_pmap;
vaddr_t va = pve->pv_va & PV_VAMASK;
volatile pt_entry_t *pde;
pt_entry_t pte;
bool locked;
UVMHIST_LOG(maphist, "... pm %#jx va %#jx", (uintptr_t)pmap,
va, 0, 0);
npve = pve->pv_next;
if (pve->pv_va & PV_KENTER) {
*pvp = pve;
pvp = &pve->pv_next;
pde = pmap_pde_get(pmap->pm_pdir, va);
pte = pmap_pte_get(pde, va);
} else {
UVMHIST_LOG(maphist, "... pm %#jx va %#jx... removing",
(uintptr_t)pmap, va, 0, 0);
pmap_reference(pmap);
locked = pmap_trylock(pmap);
if (!locked) {
pmap_pv_unlock(md);
pmap_lock(pmap);
pmap_unlock(pmap);
pmap_destroy(pmap);
UVMHIST_LOG(maphist, "... failed lock", 0, 0, 0,
0);
goto restart;
}
pde = pmap_pde_get(pmap->pm_pdir, va);
pte = pmap_pte_get(pde, va);
md->pvh_attrs |= pmap_pvh_attrs(pte);
}
pmap_pte_flush(pmap, va, pte);
if (pte & PTE_PROT(TLB_WIRED))
pmap->pm_stats.wired_count--;
pmap->pm_stats.resident_count--;
if (!(pve->pv_va & PV_KENTER)) {
pmap_pte_set(pde, va, 0);
pmap_pv_unlock(md);
pmap_pv_free(pve);
pmap_unlock(pmap);
pmap_destroy(pmap);
UVMHIST_LOG(maphist, "... removed", 0, 0, 0, 0);
*pvp = npve;
goto restart;
}
}
md->pvh_attrs &= ~PVF_EXEC;
*pvp = NULL;
pmap_pv_unlock(md);
UVMHIST_LOG(maphist, "<--- done", 0, 0, 0, 0);
}
void
pmap_unwire(pmap_t pmap, vaddr_t va)
{
UVMHIST_FUNC(__func__);
UVMHIST_CALLARGS(maphist, "pm %#jx va %#jx", (uintptr_t)pmap, va, 0, 0);
volatile pt_entry_t *pde;
pt_entry_t pte = 0;
pmap_lock(pmap);
if ((pde = pmap_pde_get(pmap->pm_pdir, va))) {
pte = pmap_pte_get(pde, va);
KASSERT(pte);
if (pte & PTE_PROT(TLB_WIRED)) {
pte &= ~PTE_PROT(TLB_WIRED);
pmap->pm_stats.wired_count--;
pmap_pte_set(pde, va, pte);
}
}
pmap_unlock(pmap);
UVMHIST_LOG(maphist, "<--- done", 0, 0, 0, 0);
}
bool
pmap_changebit(struct vm_page *pg, u_int set, u_int clear)
{
UVMHIST_FUNC(__func__);
UVMHIST_CALLARGS(maphist, "pg %#jx (md %#jx) set %#jx clear %#jx",
(uintptr_t)pg, (uintptr_t)VM_PAGE_TO_MD(pg), set, clear);
struct vm_page_md * const md = VM_PAGE_TO_MD(pg);
struct pv_entry *pve;
int res;
KASSERT((set & clear) == 0);
KASSERT((set & ~(PVF_REF|PVF_UNCACHEABLE)) == 0);
KASSERT((clear & ~(PVF_MOD|PVF_WRITE|PVF_UNCACHEABLE)) == 0);
pmap_pv_lock(md);
res = md->pvh_attrs & (set | clear);
md->pvh_attrs ^= res;
for (pve = md->pvh_list; pve; pve = pve->pv_next) {
pmap_t pmap = pve->pv_pmap;
vaddr_t va = pve->pv_va & PV_VAMASK;
volatile pt_entry_t *pde;
pt_entry_t opte, pte;
if ((pde = pmap_pde_get(pmap->pm_pdir, va))) {
opte = pte = pmap_pte_get(pde, va);
#ifdef DEBUG
if (!pte) {
UVMHIST_LOG(maphist, "zero pte for %#jx",
va, 0, 0, 0);
continue;
}
#endif
pte &= ~clear;
pte |= set;
if (!(pve->pv_va & PV_KENTER)) {
md->pvh_attrs |= pmap_pvh_attrs(pte);
res |= pmap_pvh_attrs(opte);
}
if (opte != pte) {
pmap_pte_flush(pmap, va, opte);
pmap_pte_set(pde, va, pte);
}
}
}
pmap_pv_unlock(md);
return ((res & (clear | set)) != 0);
}
bool
pmap_testbit(struct vm_page *pg, u_int bit)
{
UVMHIST_FUNC(__func__);
UVMHIST_CALLARGS(maphist, "pg %#jx (md %#jx) bit %#jx",
(uintptr_t)pg, (uintptr_t)VM_PAGE_TO_MD(pg), bit, 0);
struct vm_page_md * const md = VM_PAGE_TO_MD(pg);
struct pv_entry *pve;
pt_entry_t pte;
int ret;
pmap_pv_lock(md);
for (pve = md->pvh_list; !(md->pvh_attrs & bit) && pve;
pve = pve->pv_next) {
pmap_t pm = pve->pv_pmap;
pte = pmap_vp_find(pm, pve->pv_va & PV_VAMASK);
if (pve->pv_va & PV_KENTER)
continue;
md->pvh_attrs |= pmap_pvh_attrs(pte);
}
ret = ((md->pvh_attrs & bit) != 0);
pmap_pv_unlock(md);
return ret;
}
bool
pmap_extract(pmap_t pmap, vaddr_t va, paddr_t *pap)
{
UVMHIST_FUNC(__func__);
UVMHIST_CALLARGS(maphist, "pm %#jx va %#jx", (uintptr_t)pmap, va, 0, 0);
pt_entry_t pte;
if (pmap != pmap_kernel()) {
pmap_lock(pmap);
pte = pmap_vp_find(pmap, va);
pmap_unlock(pmap);
} else {
pte = pmap_vp_find(pmap, va);
}
if (pte) {
if (pap)
*pap = (pte & ~PGOFSET) | (va & PGOFSET);
return true;
}
return false;
}
void
pmap_activate(struct lwp *l)
{
struct proc *p = l->l_proc;
pmap_t pmap = p->p_vmspace->vm_map.pmap;
pa_space_t space = pmap->pm_space;
struct pcb *pcb = lwp_getpcb(l);
pcb->pcb_space = space;
fdcache(HPPA_SID_KERNEL, (vaddr_t)pcb, sizeof(struct pcb));
if (p == curproc)
mtctl(pmap->pm_pid, CR_PIDR2);
}
void
pmap_procwr(struct proc *p, vaddr_t va, size_t len)
{
const pmap_t pmap = p->p_vmspace->vm_map.pmap;
const pa_space_t space = pmap->pm_space;
fdcache(space, va, len);
ficache(space, va, len);
pdtlb(space, va);
pitlb(space, va);
}
static inline void
pmap_flush_page(struct vm_page *pg, bool purge)
{
UVMHIST_FUNC(__func__);
UVMHIST_CALLARGS(maphist, "pg %#jx (md %#jx) purge %jd",
(uintptr_t)pg, (uintptr_t)VM_PAGE_TO_MD(pg), purge, 0);
struct vm_page_md * const md = VM_PAGE_TO_MD(pg);
struct pv_entry *pve;
for (pve = md->pvh_list; pve; pve = pve->pv_next) {
vaddr_t va = pve->pv_va & PV_VAMASK;
pa_space_t sp = pve->pv_pmap->pm_space;
if (purge)
pdcache(sp, va, PAGE_SIZE);
else
fdcache(sp, va, PAGE_SIZE);
#if defined(HP8000_CPU) || defined(HP8200_CPU) || \
defined(HP8500_CPU) || defined(HP8600_CPU)
ficache(sp, va, PAGE_SIZE);
pdtlb(sp, va);
pitlb(sp, va);
#endif
}
}
void
pmap_zero_page(paddr_t pa)
{
UVMHIST_FUNC(__func__);
UVMHIST_CALLARGS(maphist, "pa %#jx (pg %#jx)", pa,
(uintptr_t)PHYS_TO_VM_PAGE(pa), 0, 0);
KASSERT(VM_PAGE_TO_MD(PHYS_TO_VM_PAGE(pa))->pvh_list == NULL);
KASSERT((VM_PAGE_TO_MD(PHYS_TO_VM_PAGE(pa))->pvh_attrs & PVF_EXEC) == 0);
memset((void *)pa, 0, PAGE_SIZE);
fdcache(HPPA_SID_KERNEL, pa, PAGE_SIZE);
#if defined(HP8000_CPU) || defined(HP8200_CPU) || \
defined(HP8500_CPU) || defined(HP8600_CPU)
ficache(HPPA_SID_KERNEL, pa, PAGE_SIZE);
pdtlb(HPPA_SID_KERNEL, pa);
pitlb(HPPA_SID_KERNEL, pa);
#endif
}
void
pmap_copy_page(paddr_t spa, paddr_t dpa)
{
UVMHIST_FUNC(__func__);
UVMHIST_CALLARGS(maphist, "spa %#jx (pg %#jx) dpa %#jx (pg %#jx)",
spa, (uintptr_t)PHYS_TO_VM_PAGE(spa),
dpa, (uintptr_t)PHYS_TO_VM_PAGE(dpa));
struct vm_page *srcpg = PHYS_TO_VM_PAGE(spa);
KASSERT(VM_PAGE_TO_MD(PHYS_TO_VM_PAGE(dpa))->pvh_list == NULL);
KASSERT((VM_PAGE_TO_MD(PHYS_TO_VM_PAGE(dpa))->pvh_attrs & PVF_EXEC) == 0);
pmap_flush_page(srcpg, false);
memcpy((void *)dpa, (void *)spa, PAGE_SIZE);
pdcache(HPPA_SID_KERNEL, spa, PAGE_SIZE);
fdcache(HPPA_SID_KERNEL, dpa, PAGE_SIZE);
#if defined(HP8000_CPU) || defined(HP8200_CPU) || \
defined(HP8500_CPU) || defined(HP8600_CPU)
ficache(HPPA_SID_KERNEL, spa, PAGE_SIZE);
ficache(HPPA_SID_KERNEL, dpa, PAGE_SIZE);
pdtlb(HPPA_SID_KERNEL, spa);
pdtlb(HPPA_SID_KERNEL, dpa);
pitlb(HPPA_SID_KERNEL, spa);
pitlb(HPPA_SID_KERNEL, dpa);
#endif
}
void
pmap_kenter_pa(vaddr_t va, paddr_t pa, vm_prot_t prot, u_int flags)
{
UVMHIST_FUNC(__func__);
if (va != 0) {
UVMHIST_CALLARGS(maphist, "va %#jx pa %#jx prot %#jx flags %#jx",
va, pa, prot, flags);
}
volatile pt_entry_t *pde;
pt_entry_t pte, opte;
struct vm_page *pg;
if (!(pde = pmap_pde_get(pmap_kernel()->pm_pdir, va)) &&
!(pde = pmap_pde_alloc(pmap_kernel(), va, NULL)))
panic("pmap_kenter_pa: cannot allocate pde for va=0x%lx", va);
opte = pmap_pte_get(pde, va);
pte = pa | PTE_PROT(TLB_WIRED | TLB_REFTRAP |
pmap_prot(pmap_kernel(), prot & VM_PROT_ALL));
if (IS_IOPAGE_P(pa) || (flags & PMAP_NOCACHE))
pte |= PTE_PROT(TLB_UNCACHEABLE);
if ((flags & PMAP_DIRECTMAP) == 0) {
pmap_kernel()->pm_stats.wired_count++;
pmap_kernel()->pm_stats.resident_count++;
}
if (opte)
pmap_pte_flush(pmap_kernel(), va, opte);
pg = pmap_initialized ? PHYS_TO_VM_PAGE(PTE_PAGE(pte)) : NULL;
if (pg != NULL) {
KASSERT(pa < HPPA_IOBEGIN);
struct vm_page_md * const md = VM_PAGE_TO_MD(pg);
struct pv_entry *pve;
pve = pmap_pv_alloc();
if (!pve)
panic("%s: no pv entries available", __func__);
UVMHIST_LOG(maphist, "va %#jx pa %#jx pte %#jx TLB_KENTER",
va, pa, pte, 0);
pmap_resolve_alias(pg, pmap_kernel(), va, pte);
pmap_pv_lock(md);
pmap_pv_enter(pg, pve, pmap_kernel(), va, NULL, PV_KENTER);
pmap_pv_unlock(md);
}
pmap_pte_set(pde, va, pte);
if (va != 0) {
UVMHIST_LOG(maphist, "<--- done", 0, 0, 0, 0);
}
}
void
pmap_kremove(vaddr_t va, vsize_t size)
{
UVMHIST_FUNC(__func__);
bool pzero = false;
if (va != 0) {
UVMHIST_CALLARGS(maphist, "va %#jx...%#jx", va, va + size, 0,
0);
pzero = true;
}
struct pv_entry *pve;
vaddr_t eva, pdemask;
volatile pt_entry_t *pde = NULL;
pt_entry_t pte;
struct vm_page *pg;
pmap_t pmap = pmap_kernel();
#ifdef DEBUG
if (va != 0 && va < ptoa(physmem)) {
UVMHIST_LOG(maphist, "va %#jx size %#jx: unmapping physmem", va,
size, 0, 0);
return;
}
#endif
for (pdemask = 1, eva = va + size; va < eva; va += PAGE_SIZE) {
if (pdemask != (va & PDE_MASK)) {
pdemask = va & PDE_MASK;
if (!(pde = pmap_pde_get(pmap->pm_pdir, va))) {
va = pdemask + PDE_SIZE - PAGE_SIZE;
continue;
}
}
if (!(pte = pmap_pte_get(pde, va))) {
UVMHIST_LOG(maphist, "unmapping unmapped %#jx",
va, 0, 0, 0);
continue;
}
pmap_pte_flush(pmap, va, pte);
pmap_pte_set(pde, va, 0);
pmap->pm_stats.wired_count--;
pmap->pm_stats.resident_count--;
pg = pmap_initialized ? PHYS_TO_VM_PAGE(PTE_PAGE(pte)) : NULL;
if (pg != NULL) {
struct vm_page_md * const md = VM_PAGE_TO_MD(pg);
pmap_pv_lock(md);
pve = pmap_pv_remove(pg, pmap, va);
pmap_pv_unlock(md);
if (pve != NULL)
pmap_pv_free(pve);
}
}
if (pzero) {
UVMHIST_LOG(maphist, "<--- done", 0, 0, 0, 0);
}
}
#if defined(USE_HPT)
#if defined(DDB)
void
pmap_hptdump(void)
{
struct hpt_entry *hpt, *ehpt;
hpt = (struct hpt_entry *)pmap_hpt;
ehpt = (struct hpt_entry *)((int)hpt + pmap_hptsize);
db_printf("HPT dump %p-%p:\n", hpt, ehpt);
for (; hpt < ehpt; hpt++)
if (hpt->hpt_valid) {
char buf[128];
snprintb(buf, sizeof(buf), TLB_BITS, hpt->hpt_tlbprot);
db_printf("hpt@%p: %x{%sv=%x:%x},%s,%x\n",
hpt, *(int *)hpt, (hpt->hpt_valid?"ok,":""),
hpt->hpt_space, hpt->hpt_vpn << 9,
buf, tlbptob(hpt->hpt_tlbpage));
}
}
#endif
#endif