#include "opt_ddb.h"
#include "opt_kgdb.h"
#include "opt_m68k_arch.h"
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: pmap_68k.c,v 1.75 2026/07/06 14:33:55 thorpej Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/evcnt.h>
#include <sys/proc.h>
#include <sys/pool.h>
#include <sys/cpu.h>
#include <sys/atomic.h>
#include <sys/kmem.h>
#include <machine/pcb.h>
#include <uvm/uvm.h>
#include <uvm/uvm_physseg.h>
#include <m68k/cacheops.h>
#include <m68k/mmu.h>
#if !defined(M68K_MMU_MOTOROLA) && !defined(M68K_MMU_HP)
#error Hit the road, Jack...
#endif
#define PMAP_CRIT_ENTER(code) code
#define PMAP_CRIT_EXIT(code) code
#define PMAP_CRIT_ASSERT() __nothing
static inline void
pmap_busy(pmap_t pmap)
{
pmap->pm_busy++;
KDASSERT(pmap->pm_busy != 0);
}
static inline void
pmap_unbusy(pmap_t pmap)
{
KDASSERT(pmap->pm_busy != 0);
pmap->pm_busy--;
}
#include "opt_m68k_arch.h"
#if defined(M68K_MMU_68030)
#include <m68k/mmu_30.h>
#endif
#define MMU_CLASS_68851 0
#define MMU_CLASS_68040 1
#define MMU_CLASS_HP 3
static int pmap_mmuclass __read_mostly;
#if defined(M68K_MMU_68851) || defined(M68K_MMU_68030)
#define MMU_CONFIG_68851_CLASS 1
#else
#define MMU_CONFIG_68851_CLASS 0
#endif
#if defined(M68K_MMU_68040) || defined(M68K_MMU_68060)
#define MMU_CONFIG_68040_CLASS 1
#else
#define MMU_CONFIG_68040_CLASS 0
#endif
#if defined(M68K_MMU_HP)
#define MMU_CONFIG_HP_CLASS 1
#else
#define MMU_CONFIG_HP_CLASS 0
#endif
#define MMU_CONFIG_NCLASSES (MMU_CONFIG_68851_CLASS + \
MMU_CONFIG_68040_CLASS + \
MMU_CONFIG_HP_CLASS)
#if MMU_CONFIG_NCLASSES == 1
#if MMU_CONFIG_68851_CLASS
#define MMU_IS_68851_CLASS 1
#elif MMU_CONFIG_68040_CLASS
#define MMU_IS_68040_CLASS 1
#elif MMU_CONFIG_HP_CLASS
#define MMU_IS_HP_CLASS 1
#else
#error Single MMU config predicate error.
#endif
#else
#if MMU_CONFIG_68851_CLASS
#define MMU_IS_68851_CLASS (pmap_mmuclass == MMU_CLASS_68851)
#endif
#if MMU_CONFIG_68040_CLASS
#define MMU_IS_68040_CLASS (pmap_mmuclass == MMU_CLASS_68040)
#endif
#if MMU_CONFIG_HP_CLASS
#define MMU_IS_HP_CLASS (pmap_mmuclass == MMU_CLASS_HP)
#endif
#endif
#ifndef MMU_IS_68851_CLASS
#define MMU_IS_68851_CLASS 0
#endif
#ifndef MMU_IS_68040_CLASS
#define MMU_IS_68040_CLASS 0
#endif
#ifndef MMU_IS_HP_CLASS
#define MMU_IS_HP_CLASS 0
#endif
#define MMU_USE_3L (MMU_IS_68040_CLASS)
#define MMU_USE_2L (!MMU_USE_3L)
#define PMAP_EVENT_COUNTERS
static struct evcnt pmap_nkptpages_initial_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap nkptpages", "initial");
static struct evcnt pmap_nkptpages_current_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap nkptpages", "current");
EVCNT_ATTACH_STATIC(pmap_nkptpages_initial_ev);
EVCNT_ATTACH_STATIC(pmap_nkptpages_current_ev);
static struct evcnt pmap_nkstpages_initial_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap nkstpages", "initial");
static struct evcnt pmap_nkstpages_current_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap nkstpages", "current");
EVCNT_ATTACH_STATIC(pmap_nkstpages_initial_ev);
EVCNT_ATTACH_STATIC(pmap_nkstpages_current_ev);
static struct evcnt pmap_nptpages_current_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap nptpages", "current");
static struct evcnt pmap_nptpages_hiwat_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap nptpages", "hiwat");
EVCNT_ATTACH_STATIC(pmap_nptpages_current_ev);
EVCNT_ATTACH_STATIC(pmap_nptpages_hiwat_ev);
static struct evcnt pmap_maxkva_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap", "maxkva");
EVCNT_ATTACH_STATIC(pmap_maxkva_ev);
static struct evcnt pmap_kvalimit_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap", "kvalimit");
EVCNT_ATTACH_STATIC(pmap_kvalimit_ev);
#ifdef PMAP_EVENT_COUNTERS
static struct evcnt pmap_pv_alloc_wait_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap pv_alloc", "wait");
EVCNT_ATTACH_STATIC(pmap_pv_alloc_wait_ev);
static struct evcnt pmap_pv_alloc_nowait_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap pv_alloc", "nowait");
EVCNT_ATTACH_STATIC(pmap_pv_alloc_nowait_ev);
static struct evcnt pmap_pv_enter_called_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap pv_enter", "called");
EVCNT_ATTACH_STATIC(pmap_pv_enter_called_ev);
static struct evcnt pmap_pv_enter_usr_ci_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap pv_enter", "usr_ci");
EVCNT_ATTACH_STATIC(pmap_pv_enter_usr_ci_ev);
#if MMU_CONFIG_HP_CLASS
static struct evcnt pmap_pv_enter_vac_ci_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap pv_enter", "vac_ci");
EVCNT_ATTACH_STATIC(pmap_pv_enter_vac_ci_ev);
#endif
static struct evcnt pmap_pv_enter_ci_multi_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap pv_enter", "ci_multi");
EVCNT_ATTACH_STATIC(pmap_pv_enter_ci_multi_ev);
static struct evcnt pmap_pv_remove_called_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap pv_remove", "called");
EVCNT_ATTACH_STATIC(pmap_pv_remove_called_ev);
static struct evcnt pmap_pv_remove_ci_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap pv_remove", "ci");
EVCNT_ATTACH_STATIC(pmap_pv_remove_ci_ev);
static struct evcnt pmap_pt_cache_hit_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap pt_cache", "hit");
EVCNT_ATTACH_STATIC(pmap_pt_cache_hit_ev);
static struct evcnt pmap_pt_cache_miss_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap pt_cache", "miss");
EVCNT_ATTACH_STATIC(pmap_pt_cache_miss_ev);
static struct evcnt pmap_enter_nowait_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap enter", "nowait");
EVCNT_ATTACH_STATIC(pmap_enter_nowait_ev);
static struct evcnt pmap_enter_yeswait_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap enter", "yeswait");
EVCNT_ATTACH_STATIC(pmap_enter_yeswait_ev);
static struct evcnt pmap_enter_pte_alloc_fail_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap enter", "pte alloc failed");
EVCNT_ATTACH_STATIC(pmap_enter_pte_alloc_fail_ev);
static struct evcnt pmap_enter_pv_alloc_fail_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap enter", "pv alloc failed");
EVCNT_ATTACH_STATIC(pmap_enter_pv_alloc_fail_ev);
static struct evcnt pmap_enter_valid_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap enter", "valid");
EVCNT_ATTACH_STATIC(pmap_enter_valid_ev);
static struct evcnt pmap_enter_wire_change_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap enter", "wire change");
EVCNT_ATTACH_STATIC(pmap_enter_wire_change_ev);
static struct evcnt pmap_enter_prot_change_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap enter", "prot change");
EVCNT_ATTACH_STATIC(pmap_enter_prot_change_ev);
static struct evcnt pmap_enter_pa_change_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap enter", "pa change");
EVCNT_ATTACH_STATIC(pmap_enter_pa_change_ev);
static struct evcnt pmap_enter_pv_recycle_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap enter", "pv recycle");
EVCNT_ATTACH_STATIC(pmap_enter_pv_recycle_ev);
static struct evcnt pmap_prm_got_pg_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap prm", "got pg");
static struct evcnt pmap_prm_lookup_pg_hit_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap prm", "lookup pg hit");
static struct evcnt pmap_prm_lookup_pg_miss_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap prm", "lookup pg miss");
EVCNT_ATTACH_STATIC(pmap_prm_got_pg_ev);
EVCNT_ATTACH_STATIC(pmap_prm_lookup_pg_hit_ev);
EVCNT_ATTACH_STATIC(pmap_prm_lookup_pg_miss_ev);
#define pmap_evcnt(e) pmap_ ## e ## _ev.ev_count++
#else
#define pmap_evcnt(e) __nothing
#endif
static void (*pmap_load_urp_func)(paddr_t) __read_mostly;
static void
pmap_mmuclass_init(void)
{
switch (mmutype) {
#if MMU_CONFIG_68040_CLASS
case MMU_68040:
case MMU_68060:
pmap_mmuclass = MMU_CLASS_68040;
#if defined(M68040)
if (cputype == CPU_68040) {
pmap_load_urp_func = mmu_load_urp40;
}
#endif
#if defined(M68060)
if (cputype == CPU_68060) {
pmap_load_urp_func = mmu_load_urp60;
}
#endif
break;
#endif
#if MMU_CONFIG_68851_CLASS
case MMU_68851:
case MMU_68030:
pmap_mmuclass = MMU_CLASS_68851;
protorp[0] = MMU51_CRP_BITS;
pmap_load_urp_func = mmu_load_urp51;
break;
#endif
#if MMU_CONFIG_HP_CLASS
case MMU_HP:
pmap_mmuclass = MMU_CLASS_HP;
pmap_load_urp_func = mmu_load_urp20hp;
break;
#endif
default:
panic("%s: mmutype=%d not configured?", __func__, mmutype);
}
if (pmap_load_urp_func == NULL) {
panic("%s: No mmu_load_*() for cputype=%d mmutype=%d",
__func__, cputype, mmutype);
}
}
static inline void
pmap_load_urp(paddr_t urp)
{
(*pmap_load_urp_func)(urp);
}
#if MMU_CONFIG_HP_CLASS
static vaddr_t pmap_aliasmask __read_mostly;
#endif
void
pmap_init_vac(size_t vacsize)
{
#if MMU_CONFIG_HP_CLASS
KASSERT(pmap_aliasmask == 0);
KASSERT(powerof2(vacsize));
pmap_aliasmask = vacsize - 1;
#endif
}
static bool pmap_initialized_p;
static inline struct vm_page *
pmap_pa_to_pg(paddr_t pa)
{
return pmap_initialized_p ? PHYS_TO_VM_PAGE(pa) : NULL;
}
static pt_entry_t pmap_changebit(struct vm_page *, pt_entry_t, pt_entry_t);
static struct pmap kernel_pmap_store;
struct pmap * const kernel_pmap_ptr = &kernel_pmap_store;
paddr_t Sysseg_pa;
#undef pmap_kernel
#define pmap_kernel() (&kernel_pmap_store)
static inline bool
active_pmap(pmap_t pmap)
{
return pmap == pmap_kernel() ||
pmap == curproc->p_vmspace->vm_map.pmap;
}
static inline bool
active_user_pmap(pmap_t pmap)
{
return curproc != NULL &&
pmap != pmap_kernel() &&
pmap == curproc->p_vmspace->vm_map.pmap;
}
static unsigned int pmap_ptpage_table_counts[2];
__CTASSERT(LA40_L1_COUNT == LA40_L2_COUNT);
static void
pmap_ptpage_init(void)
{
if (MMU_USE_3L) {
pmap_ptpage_table_counts[0] = PAGE_SIZE / TBL40_L3_SIZE;
pmap_ptpage_table_counts[1] = PAGE_SIZE / TBL40_L2_SIZE;
} else {
pmap_ptpage_table_counts[0] = 1;
pmap_ptpage_table_counts[1] = 1;
}
}
static struct vm_page *
pmap_page_alloc(bool nowait)
{
struct vm_page *pg;
const int flags = nowait ? UVM_PGA_USERESERVE : 0;
while ((pg = uvm_pagealloc(NULL, 0, NULL, flags)) == NULL) {
if (nowait) {
return NULL;
}
uvm_wait("pmappg");
}
pg->flags &= ~PG_BUSY;
return pg;
}
static struct pmap_ptpage *
pmap_ptpage_alloc(bool segtab, bool nowait)
{
const unsigned int tabcnt = pmap_ptpage_table_counts[segtab];
const size_t size = sizeof(struct pmap_ptpage) +
(sizeof(struct pmap_table) * tabcnt);
const size_t tabsize = PAGE_SIZE / tabcnt;
struct pmap_ptpage *ptp;
struct pmap_table *pt;
struct vm_page *pg;
const int uvm_f_nowait = nowait ? UVM_KMF_NOWAIT : 0;
vaddr_t ptpva;
ptp = kmem_zalloc(size, nowait ? KM_NOSLEEP : KM_SLEEP);
if (__predict_false(ptp == NULL)) {
return NULL;
}
ptpva = uvm_km_alloc(kernel_map, PAGE_SIZE, 0,
UVM_KMF_VAONLY | uvm_f_nowait);
if (__predict_false(ptpva == 0)) {
kmem_free(ptp, size);
return NULL;
}
pg = pmap_page_alloc(nowait);
if (__predict_false(pg == NULL)) {
uvm_km_free(kernel_map, ptpva, PAGE_SIZE, UVM_KMF_VAONLY);
kmem_free(ptp, size);
return NULL;
}
pmap_kenter_pa(ptpva, VM_PAGE_TO_PHYS(pg),
UVM_PROT_READ | UVM_PROT_WRITE, PMAP_NOCACHE);
zeropage((void *)ptpva);
LIST_INIT(&ptp->ptp_freelist);
ptp->ptp_pg = pg;
ptp->ptp_vpagenum = m68k_btop(ptpva);
ptp->ptp_freecnt = tabcnt;
ptp->ptp_segtab = segtab;
for (unsigned int i = 0; i < tabcnt; ptpva += tabsize, i++) {
pt = &ptp->ptp_tables[i];
pt->pt_ptpage = ptp;
pt->pt_entries = (pt_entry_t *)ptpva;
LIST_INSERT_HEAD(&ptp->ptp_freelist, pt, pt_freelist);
}
#ifdef PMAP_EVENT_COUNTERS
pmap_nptpages_current_ev.ev_count32++;
if (pmap_nptpages_current_ev.ev_count32 >
pmap_nptpages_hiwat_ev.ev_count32) {
pmap_nptpages_hiwat_ev.ev_count32 =
pmap_nptpages_current_ev.ev_count32;
}
#endif
return ptp;
}
static void
pmap_ptpage_free(struct pmap_ptpage *ptp)
{
const unsigned int tabcnt = pmap_ptpage_table_counts[ptp->ptp_segtab];
const size_t size = sizeof(struct pmap_ptpage) +
(sizeof(struct pmap_table) * tabcnt);
uvm_km_free(kernel_map, m68k_ptob(ptp->ptp_vpagenum), PAGE_SIZE,
UVM_KMF_WIRED);
kmem_free(ptp, size);
#ifdef PMAP_EVENT_COUNTERS
pmap_nptpages_current_ev.ev_count32--;
#endif
}
static struct pool pmap_pool;
static struct pool pmap_pv_pool;
#define PMAP_PV_LOWAT 16
static void
pmap_alloc_init(void)
{
pool_init(&pmap_pv_pool, sizeof(struct pv_entry),
PVH_ATTR_MASK + 1,
0,
0,
"pmappv",
&pool_allocator_meta,
IPL_VM);
pool_setlowat(&pmap_pv_pool, PMAP_PV_LOWAT);
pool_init(&pmap_pool, sizeof(struct pmap),
0,
0,
0,
"pmappl",
&pool_allocator_kmem,
IPL_NONE);
}
static inline pmap_t
pmap_alloc(void)
{
pmap_t pmap = pool_get(&pmap_pool, PR_WAITOK);
memset(pmap, 0, sizeof(*pmap));
return pmap;
}
static inline void
pmap_free(pmap_t pmap)
{
pool_put(&pmap_pool, pmap);
}
static struct pv_entry *
pmap_pv_alloc(bool nowait)
{
struct pv_entry *pv;
#ifdef PMAP_EVENT_COUNTERS
if (nowait) {
pmap_evcnt(pv_alloc_nowait);
} else {
pmap_evcnt(pv_alloc_wait);
}
#endif
pv = pool_get(&pmap_pv_pool, nowait ? PR_NOWAIT : 0);
if (__predict_true(pv != NULL)) {
KASSERT((((uintptr_t)pv) & PVH_ATTR_MASK) == 0);
}
return pv;
}
static void
pmap_pv_free(struct pv_entry *pv)
{
pool_put(&pmap_pv_pool, pv);
}
struct pmap_completion {
struct pmap_ptpage_list pc_ptpages;
struct pmap_pv_list pc_pvlist;
};
static inline void
pmap_completion_init(struct pmap_completion *pc)
{
TAILQ_INIT(&pc->pc_ptpages);
LIST_INIT(&pc->pc_pvlist);
}
static void
pmap_completion_fini(struct pmap_completion *pc)
{
struct pmap_ptpage *ptp;
struct pv_entry *pv;
while ((ptp = TAILQ_FIRST(&pc->pc_ptpages)) != NULL) {
TAILQ_REMOVE(&pc->pc_ptpages, ptp, ptp_list);
pmap_ptpage_free(ptp);
}
while ((pv = LIST_FIRST(&pc->pc_pvlist)) != NULL) {
LIST_REMOVE(pv, pv_pmlist);
pmap_pv_free(pv);
}
}
__CTASSERT(DT51_PAGE == PTE40_RESIDENT);
__CTASSERT(PTE51_WP == PTE40_W);
__CTASSERT(PTE51_U == PTE40_U);
__CTASSERT(PTE51_M == PTE40_M);
__CTASSERT(PTE51_CI == PTE40_CM_NC_SER);
static pt_entry_t pmap_pte_proto[UVM_PROT_ALL + 1];
static pt_entry_t pmap_pte_proto_ci[UVM_PROT_ALL + 1];
static pt_entry_t pmap_pte_proto_um[UVM_PROT_ALL + 1];
static pt_entry_t pmap_ste_proto;
static inline paddr_t
pte_pa(pt_entry_t pte)
{
return pte & PTE40_PGA;
}
static inline bool
pte_valid_p(pt_entry_t pte)
{
return !!(pte & PTE_VALID);
}
static inline bool
pte_wired_p(pt_entry_t pte)
{
return !!(pte & PTE_WIRED);
}
static inline bool
pte_managed_p(pt_entry_t pte)
{
return !!(pte & PTE_PVLIST);
}
static inline bool
pte_ci_p(pt_entry_t pte)
{
return !!(pte & PTE51_CI);
}
#define PTE_PROT_CHANGE_BITS (PTE_WP | PTE_CMASK)
static inline pt_entry_t
pte_change_prot(pt_entry_t opte, vm_prot_t prot)
{
pt_entry_t *pte_proto = pte_ci_p(opte) ? pmap_pte_proto_ci
: pmap_pte_proto;
return (opte & ~PTE_PROT_CHANGE_BITS) | pte_proto[prot];
}
static inline pt_entry_t
pte_load(volatile pt_entry_t *ptep)
{
return *ptep;
}
static inline void
pte_store(volatile pt_entry_t *ptep, pt_entry_t npte)
{
*ptep = npte;
}
static inline bool
pte_update(volatile pt_entry_t *ptep, pt_entry_t opte, pt_entry_t npte)
{
return atomic_cas_uint(ptep, opte, npte) == opte;
}
#if MMU_CONFIG_HP_CLASS
static inline void
pte_set(volatile pt_entry_t *ptep, pt_entry_t bits)
{
*ptep |= bits;
}
static inline void
pte_mask(volatile pt_entry_t *ptep, pt_entry_t mask)
{
*ptep &= mask;
}
#endif
static inline pt_entry_t
pte_set_ci(pt_entry_t pte)
{
return (pte & ~PTE_CMASK) | (MMU_IS_68040_CLASS ? PTE40_CM_NC_SER
: PTE51_CI);
}
static inline pt_entry_t
pte_clr_ci(pt_entry_t pte)
{
pte &= ~PTE_CMASK;
if (MMU_IS_68040_CLASS) {
pte |= (pte & PTE_WP) ? PTE40_CM_WT
: PTE40_CM_CB;
}
return pte;
}
static void
pmap_pte_proto_init(void)
{
pt_entry_t c_bits, ro_c_bits, rw_c_bits, ci_bits, prot_bits, um_bits;
int prot;
if (MMU_IS_68040_CLASS) {
ro_c_bits = PTE40_CM_WT;
rw_c_bits = PTE40_CM_CB;
ci_bits = PTE40_CM_NC_SER;
} else {
ro_c_bits = rw_c_bits = 0;
ci_bits = PTE51_CI;
}
for (prot = 1; prot <= UVM_PROT_ALL; prot++) {
prot_bits = um_bits = 0;
if (prot & UVM_PROT_WRITE) {
um_bits = PTE_U | PTE_M;
} else if (prot & (UVM_PROT_READ|UVM_PROT_EXEC)) {
prot_bits = PTE_WP;
um_bits = PTE_U;
}
c_bits = (prot & UVM_PROT_WRITE) ? rw_c_bits : ro_c_bits;
pmap_pte_proto[prot] = PTE_VALID | prot_bits | c_bits;
pmap_pte_proto_ci[prot] = PTE_VALID | prot_bits | ci_bits;
pmap_pte_proto_um[prot] = um_bits;
}
pmap_ste_proto = DTE51_U | DT51_SHORT;
}
static inline pt_entry_t
pmap_make_pte(paddr_t pa, vm_prot_t prot, u_int flags)
{
pt_entry_t *pte_proto = (flags & PMAP_NOCACHE) ? pmap_pte_proto_ci
: pmap_pte_proto;
prot &= UVM_PROT_ALL;
KASSERT(prot != 0);
pt_entry_t npte = pa | pte_proto[prot] |
pmap_pte_proto_um[flags & UVM_PROT_ALL];
if (flags & PMAP_WIRED) {
npte |= PTE_WIRED;
}
return npte;
}
#define PTPAGEVASZ ((PAGE_SIZE / sizeof(pt_entry_t)) * PAGE_SIZE)
#define PTPAGEVAOFS (PTPAGEVASZ - 1)
#define pmap_round_ptpage(va) (((va) + PTPAGEVAOFS) & ~PTPAGEVAOFS)
#define pmap_trunc_ptpage(va) ((va) & ~PTPAGEVAOFS)
#define KERNEL_MAX_ADDRESS ((vaddr_t)0 - PAGE_SIZE)
static vaddr_t kernel_virtual_start, kernel_virtual_end;
vaddr_t kernel_virtual_max = KERNEL_MAX_ADDRESS;
static paddr_t kernel_stnext_pa, kernel_stnext_endpa;
static paddr_t null_segtab_pa __read_mostly;
static inline void
pmap_set_lev1map(pmap_t pmap, struct pmap_table *pt, paddr_t pa)
{
pmap->pm_lev1map = pt;
pmap->pm_lev1pa = pa;
if (active_user_pmap(pmap)) {
#if MMU_CONFIG_HP_CLASS
#endif
pmap_load_urp(pmap->pm_lev1pa);
TBIAU();
ICIA();
}
}
static inline unsigned int
pmap_pagenum(vaddr_t va)
{
return ((va) >> PAGE_SHIFT);
}
static inline unsigned int
pmap_segnum(vaddr_t va)
{
return MMU_USE_3L ? ((va) >> SEGSHIFT3L) : ((va) >> SEGSHIFT2L);
}
static inline unsigned int
pmap_st1_index(vaddr_t va)
{
return MMU_USE_3L ? LA40_RI(va) : LA2L_RI(va);
}
static inline unsigned int
pmap_st_index(vaddr_t va)
{
return MMU_USE_3L ? LA40_PI(va) : LA2L_RI(va);
}
static inline unsigned int
pmap_pt_index(vaddr_t va)
{
return MMU_USE_3L ? LA40_PGI(va) : LA2L_PGI(va);
}
static inline vaddr_t
pmap_trunc_seg(vaddr_t va)
{
return MMU_USE_3L ? pmap_trunc_seg_3L(va) : pmap_trunc_seg_2L(va);
}
static inline vaddr_t
pmap_trunc_seg1(vaddr_t va)
{
KASSERT(MMU_USE_3L);
return pmap_trunc_seg1_3L(va);
}
static inline vaddr_t
pmap_round_seg(vaddr_t va)
{
return MMU_USE_3L ? pmap_round_seg_3L(va) : pmap_round_seg_2L(va);
}
static inline vaddr_t
pmap_next_seg(vaddr_t va)
{
return pmap_round_seg(va + PAGE_SIZE);
}
static paddr_t
pmap_table_pa(const struct pmap_table * const pt)
{
const struct pmap_ptpage * const ptp = pt->pt_ptpage;
const vaddr_t ptpva = m68k_ptob(ptp->ptp_vpagenum);
const vaddr_t ptva = (vaddr_t)pt->pt_entries;
return VM_PAGE_TO_PHYS(ptp->ptp_pg) + (ptva - ptpva);
}
static inline unsigned int
pmap_table_make_key(unsigned int segnum, bool segtab)
{
KASSERT((segnum & 0x80000000) == 0);
return (segnum << 1) | (unsigned int)segtab;
}
static int
pmap_table_rb_compare_key(void *v __unused, const void *n, const void *k)
{
const struct pmap_table * const pt1 = n;
const unsigned int k1 = pt1->pt_key;
const unsigned int k2 = *(const unsigned int *)k;
return (int)(k1 - k2);
}
static int
pmap_table_rb_compare_nodes(void *v, const void *n1, const void *n2)
{
const struct pmap_table * const pt2 = n2;
return pmap_table_rb_compare_key(v, n1, &pt2->pt_key);
}
static const rb_tree_ops_t pmap_table_rb_ops = {
.rbto_compare_nodes = pmap_table_rb_compare_nodes,
.rbto_compare_key = pmap_table_rb_compare_key,
.rbto_node_offset = offsetof(struct pmap_table, pt_node),
};
static struct pmap_table *
pmap_table_alloc(pmap_t pmap, bool segtab, bool nowait,
struct pmap_completion *pc)
{
struct pmap_ptpage_list *pmlist = &pmap->pm_ptpages[segtab];
struct pmap_ptpage *ptp, *newptp = NULL;
struct pmap_table *pt;
KASSERT(pc != NULL);
try_again:
if ((ptp = TAILQ_FIRST(pmlist)) == NULL || ptp->ptp_freecnt == 0) {
KASSERT(ptp == NULL || LIST_FIRST(&ptp->ptp_freelist) == NULL);
if (newptp == NULL) {
newptp = pmap_ptpage_alloc(segtab, nowait);
if (newptp == NULL) {
KASSERT(nowait);
return NULL;
}
goto try_again;
}
ptp = newptp;
TAILQ_INSERT_HEAD(pmlist, newptp, ptp_list);
}
if (__predict_false(newptp != NULL && ptp != newptp)) {
TAILQ_INSERT_TAIL(&pc->pc_ptpages, newptp, ptp_list);
}
pt = LIST_FIRST(&ptp->ptp_freelist);
KASSERT(pt != NULL);
LIST_REMOVE(pt, pt_freelist);
ptp->ptp_freecnt--;
if (ptp->ptp_freecnt == 0 &&
TAILQ_NEXT(ptp, ptp_list) != NULL) {
TAILQ_REMOVE(pmlist, ptp, ptp_list);
TAILQ_INSERT_TAIL(pmlist, ptp, ptp_list);
}
KASSERT(pt->pt_st == NULL);
pt->pt_holdcnt = 1;
return pt;
}
static void
pmap_table_free(pmap_t pmap, struct pmap_table *pt,
struct pmap_completion *pc)
{
struct pmap_ptpage *ptp = pt->pt_ptpage;
struct pmap_ptpage_list *pmlist = &pmap->pm_ptpages[ptp->ptp_segtab];
KASSERT(pt->pt_st == NULL);
LIST_INSERT_HEAD(&ptp->ptp_freelist, pt, pt_freelist);
KASSERT(ptp->ptp_freecnt < pmap_ptpage_table_counts[ptp->ptp_segtab]);
ptp->ptp_freecnt++;
if (ptp->ptp_freecnt == pmap_ptpage_table_counts[ptp->ptp_segtab]) {
TAILQ_REMOVE(pmlist, ptp, ptp_list);
TAILQ_INSERT_TAIL(&pc->pc_ptpages, ptp, ptp_list);
}
else if (ptp->ptp_freecnt == 1) {
TAILQ_REMOVE(pmlist, ptp, ptp_list);
TAILQ_INSERT_HEAD(pmlist, ptp, ptp_list);
}
else {
struct pmap_ptpage *next_ptp;
for (next_ptp = TAILQ_NEXT(ptp, ptp_list);
next_ptp != NULL;
next_ptp = TAILQ_NEXT(next_ptp, ptp_list)) {
if (next_ptp->ptp_freecnt < ptp->ptp_freecnt) {
break;
}
}
if (next_ptp != NULL &&
next_ptp != TAILQ_NEXT(ptp, ptp_list) &&
next_ptp->ptp_freecnt != 0) {
TAILQ_REMOVE(pmlist, ptp, ptp_list);
TAILQ_INSERT_AFTER(pmlist, next_ptp, ptp, ptp_list);
}
}
}
static inline void
pmap_table_retain(struct pmap_table *pt)
{
if (__predict_true(pt != NULL)) {
pt->pt_holdcnt++;
KASSERT(pt->pt_holdcnt != 0);
}
}
static __noinline void
pmap_table_release_slow(pmap_t pmap, struct pmap_table *pt,
struct pmap_completion *pc)
{
KASSERT(pt != NULL);
KASSERT(pt->pt_holdcnt != 0);
pt->pt_holdcnt--;
if (__predict_false(pt->pt_holdcnt != 0)) {
return;
}
KASSERT(pc != NULL);
if (__predict_true(pt == pmap->pm_pt_cache)) {
pmap->pm_pt_cache = NULL;
}
if (__predict_true(pt->pt_st != NULL)) {
pte_store(&pt->pt_st->pt_entries[pt->pt_stidx], 0);
rb_tree_remove_node(&pmap->pm_tables, pt);
pmap_table_release_slow(pmap, pt->pt_st, pc);
pt->pt_st = NULL;
} else if (pt == pmap->pm_lev1map) {
pmap_set_lev1map(pmap, NULL, null_segtab_pa);
}
pmap_table_free(pmap, pt, pc);
}
static inline void
pmap_table_release(pmap_t pmap, struct pmap_table *pt,
struct pmap_completion *pc)
{
if (__predict_true(pt != NULL)) {
if (__predict_true(pt->pt_holdcnt > 1)) {
pt->pt_holdcnt--;
return;
}
pmap_table_release_slow(pmap, pt, pc);
}
}
static struct pmap_table *
pmap_table_lookup(pmap_t pmap, unsigned int segnum, bool segtab)
{
const unsigned int key = pmap_table_make_key(segnum, segtab);
struct pmap_table *pt;
if ((pt = pmap->pm_pt_cache) == NULL || pt->pt_key != key) {
pmap_evcnt(pt_cache_miss);
pt = rb_tree_find_node(&pmap->pm_tables, &key);
if (__predict_true(!segtab)) {
pmap->pm_pt_cache = pt;
}
} else {
pmap_evcnt(pt_cache_hit);
}
if (pt != NULL) {
pmap_table_retain(pt);
}
return pt;
}
static struct pmap_table *
pmap_table_insert(pmap_t pmap, struct pmap_table *t1, unsigned int stidx,
unsigned int segnum, bool segtab, bool nowait, struct pmap_completion *pc)
{
struct pmap_table *t2, *ret_t;
t2 = pmap_table_lookup(pmap, segnum, segtab);
if (t2 != NULL) {
pmap_table_release(pmap, t1, NULL);
return t2;
}
PMAP_CRIT_EXIT();
t2 = pmap_table_alloc(pmap, segtab, nowait, pc);
PMAP_CRIT_ENTER();
if (__predict_false(t2 == NULL)) {
pmap_table_release(pmap, t1, pc);
return NULL;
}
t2->pt_key = pmap_table_make_key(segnum, segtab);
ret_t = rb_tree_insert_node(&pmap->pm_tables, t2);
if (__predict_false(ret_t != t2)) {
pmap_table_retain(ret_t);
pmap_table_release(pmap, t2, pc);
pmap_table_release(pmap, t1, NULL);
return ret_t;
}
t2->pt_st = t1;
t2->pt_stidx = (unsigned short)stidx;
pte_store(&t1->pt_entries[stidx], pmap_ste_proto | pmap_table_pa(t2));
return t2;
}
static pt_entry_t *kernel_ptes;
pt_entry_t *
pmap_kernel_pte(vaddr_t va)
{
KASSERT(va >= VM_MIN_KERNEL_ADDRESS);
return &kernel_ptes[m68k_btop(va - VM_MIN_KERNEL_ADDRESS)];
}
static pt_entry_t *
pmap_pte_lookup(pmap_t pmap, vaddr_t va, struct pmap_table **out_pt)
{
if (pmap == pmap_kernel()) {
*out_pt = NULL;
return pmap_kernel_pte(va);
}
const unsigned int segnum = pmap_segnum(va);
struct pmap_table *pt = pmap_table_lookup(pmap, segnum, false);
if (__predict_true(pt != NULL)) {
*out_pt = pt;
return &pt->pt_entries[pmap_pt_index(va)];
}
*out_pt = NULL;
return NULL;
}
static pt_entry_t *
pmap_pte_alloc(pmap_t pmap, vaddr_t va, struct pmap_table **out_pt,
bool nowait, struct pmap_completion *pc)
{
struct pmap_table *st, *pt;
pt_entry_t *ptep;
PMAP_CRIT_ASSERT();
ptep = pmap_pte_lookup(pmap, va, out_pt);
if (__predict_true(ptep != NULL)) {
return ptep;
}
if (__predict_true((st = pmap->pm_lev1map) != NULL)) {
pmap_table_retain(st);
} else {
PMAP_CRIT_EXIT();
st = pmap_table_alloc(pmap, true, nowait, pc);
PMAP_CRIT_ENTER();
if (__predict_false(st == NULL)) {
return NULL;
}
if (__predict_false(pmap->pm_lev1map != NULL)) {
pmap_table_release(pmap, st, pc);
st = pmap->pm_lev1map;
pmap_table_retain(st);
} else {
pmap_set_lev1map(pmap, st, pmap_table_pa(st));
}
}
if (MMU_USE_3L) {
struct pmap_table * const st1 = st;
st = pmap_table_insert(pmap, st1, pmap_st1_index(va),
pmap_st1_index(va), true, nowait, pc);
if (__predict_false(st == NULL)) {
pmap_table_release(pmap, st1, pc);
return NULL;
}
}
pt = pmap_table_insert(pmap, st, pmap_st_index(va), pmap_segnum(va),
false, nowait, pc);
if (__predict_false(pt == NULL)) {
pmap_table_release(pmap, st, pc);
return NULL;
}
*out_pt = pt;
return &pt->pt_entries[pmap_pt_index(va)];
}
static inline pt_entry_t *
pmap_pv_pte(struct pv_entry * const pv)
{
const vaddr_t va = PV_VA(pv);
if (__predict_true(pv->pv_pmap != pmap_kernel())) {
KASSERT(pv->pv_pt != NULL);
return &pv->pv_pt->pt_entries[pmap_pt_index(va)];
}
return pmap_kernel_pte(va);
}
#define MATCHING_PMAP(p1, p2) \
((p1) == (p2) || \
(p1) == pmap_kernel() || (p2) == pmap_kernel())
#define CONFLICTING_ALIAS(va1, va2) \
(((va1) & pmap_aliasmask) != ((va2) & pmap_aliasmask))
static void
pmap_pv_enter(pmap_t pmap, struct vm_page *pg, vaddr_t va, vm_prot_t prot,
struct pmap_table *pt, pt_entry_t npte, struct pv_entry *newpv)
{
const bool usr_ci = pte_ci_p(npte);
struct pv_entry *pv;
pt_entry_t opte;
pmap_evcnt(pv_enter_called);
PMAP_CRIT_ASSERT();
KASSERT(newpv != NULL);
npte |= PTE_PVLIST;
newpv->pv_pmap = pmap;
newpv->pv_vf = va;
newpv->pv_pt = pt;
pt_entry_t *ptep = pmap_pv_pte(newpv);
#ifdef DEBUG
for (pv = VM_MDPAGE_PVS(pg); pv != NULL; pv = pv->pv_next) {
if (pmap == pv->pv_pmap && va == PV_VA(pv)) {
panic("%s: pmap=%p va=0x%08lx already in PV table",
__func__, pmap, va);
}
}
#endif
if (__predict_false(usr_ci)) {
newpv->pv_vf |= PV_F_CI_USR;
}
newpv->pv_next = VM_MDPAGE_PVS(pg);
VM_MDPAGE_SETPVP(VM_MDPAGE_HEAD_PVP(pg), newpv);
LIST_INSERT_HEAD(&pmap->pm_pvlist, newpv, pv_pmlist);
VM_MDPAGE_ADD_UM(pg, npte);
if (prot & UVM_PROT_EXEC) {
#if MMU_CONFIG_68040_CLASS
if (MMU_IS_68040_CLASS) {
const paddr_t pa = VM_PAGE_TO_PHYS(pg);
DCFP(pa);
ICPP(pa);
}
#endif
}
#if MMU_CONFIG_HP_CLASS
if (MMU_IS_HP_CLASS) {
goto hp_mmu_vac_shenanigans;
}
#endif
if (__predict_false(VM_MDPAGE_CI_P(pg))) {
npte = pte_set_ci(npte);
pte_store(ptep, npte);
if (active_pmap(pmap)) {
TBIS(va);
}
return;
}
pte_store(ptep, npte);
if (active_pmap(pmap)) {
TBIS(va);
}
if (__predict_false(usr_ci)) {
VM_MDPAGE_SET_CI(pg);
pmap_evcnt(pv_enter_usr_ci);
for (pv = newpv->pv_next; pv != NULL; pv = pv->pv_next) {
pmap_evcnt(pv_enter_ci_multi);
ptep = pmap_pv_pte(pv);
for (;;) {
opte = pte_load(ptep);
npte = pte_set_ci(opte);
if (pte_update(ptep, opte, npte)) {
if (active_pmap(pv->pv_pmap)) {
TBIS(PV_VA(pv));
}
break;
}
}
}
#if MMU_CONFIG_68040_CLASS
if (MMU_IS_68040_CLASS) {
const paddr_t pa = VM_PAGE_TO_PHYS(pg);
DCFP(pa);
ICPP(pa);
}
#endif
}
return;
#if MMU_CONFIG_HP_CLASS
hp_mmu_vac_shenanigans:
bool flush_s_vac = false;
bool flush_u_vac = false;
pte_store(ptep, npte);
if (active_pmap(pmap)) {
TBIS(va);
}
vaddr_t pv_flags = newpv->pv_vf & PV_F_CI_USR;
if (usr_ci) {
pmap_evcnt(pv_enter_usr_ci);
}
for (pv = newpv->pv_next; pv != NULL; pv = pv->pv_next) {
if (MATCHING_PMAP(pmap, pv->pv_pmap) &&
CONFLICTING_ALIAS(va, PV_VA(pv))) {
pmap_evcnt(pv_enter_vac_ci);
pv_flags |= PV_F_CI_VAC;
break;
}
}
if (__predict_true(pv_flags == 0)) {
return;
}
VM_MDPAGE_SET_CI(pg);
for (pv = VM_MDPAGE_PVS(pg); pv != NULL; pv = pv->pv_next) {
if (MATCHING_PMAP(pmap, pv->pv_pmap)) {
pmap_evcnt(pv_enter_ci_multi);
pv->pv_vf |= pv_flags;
pte_set(pmap_pv_pte(pv), PTE51_CI);
if (active_pmap(pv->pv_pmap)) {
TBIS(PV_VA(pv));
if (pv->pv_pmap == pmap_kernel()) {
flush_s_vac = true;
} else {
flush_u_vac = true;
}
}
}
}
if (flush_u_vac && flush_s_vac) {
DCIA();
} else if (flush_u_vac) {
DCIU();
} else if (flush_s_vac) {
DCIS();
}
#endif
}
static void
pmap_pv_remove(pmap_t pmap, struct vm_page *pg, vaddr_t va,
struct pmap_completion *pc)
{
struct pv_entry **pvp, *pv;
pt_entry_t *ptep, opte, npte;
pmap_evcnt(pv_remove_called);
PMAP_CRIT_ASSERT();
for (pvp = VM_MDPAGE_HEAD_PVP(pg), pv = VM_MDPAGE_PVS(pg);
pv != NULL;
pvp = &pv->pv_next, pv = *pvp) {
if (pmap == pv->pv_pmap && va == PV_VA(pv)) {
break;
}
}
KASSERT(pv != NULL);
VM_MDPAGE_SETPVP(pvp, pv->pv_next);
LIST_REMOVE(pv, pv_pmlist);
KASSERT(pc != NULL);
LIST_INSERT_HEAD(&pc->pc_pvlist, pv, pv_pmlist);
#if MMU_CONFIG_HP_CLASS
if (MMU_IS_HP_CLASS) {
goto hp_mmu_vac_shenanigans;
}
#endif
if (__predict_false(VM_MDPAGE_CI_P(pg))) {
pmap_evcnt(pv_remove_ci);
for (pv = VM_MDPAGE_PVS(pg); pv != NULL; pv = pv->pv_next) {
if (pv->pv_vf & PV_F_CI_USR) {
return;
}
}
KASSERT(pv == NULL);
VM_MDPAGE_CLR_CI(pg);
for (pv = VM_MDPAGE_PVS(pg); pv != NULL; pv = pv->pv_next) {
ptep = pmap_pv_pte(pv);
for (;;) {
opte = pte_load(ptep);
npte = pte_clr_ci(opte);
if (pte_update(ptep, opte, npte)) {
if (active_pmap(pv->pv_pmap)) {
TBIS(PV_VA(pv));
}
break;
}
}
}
}
return;
#if MMU_CONFIG_HP_CLASS
hp_mmu_vac_shenanigans:
if (__predict_false(VM_MDPAGE_CI_P(pg))) {
vaddr_t all_ci_flags = PV_F_CI_USR;
pmap_evcnt(pv_remove_ci);
for (pv = VM_MDPAGE_PVS(pg); pv != NULL; pv = pv->pv_next) {
if (! MATCHING_PMAP(pmap, pv->pv_pmap)) {
continue;
}
if (pv->pv_vf & all_ci_flags) {
return;
}
all_ci_flags |= pv->pv_vf & PV_F_CI_VAC;
}
KASSERT(pv == NULL);
all_ci_flags = 0;
for (pv = VM_MDPAGE_PVS(pg); pv != NULL; pv = pv->pv_next) {
if (! MATCHING_PMAP(pmap, pv->pv_pmap)) {
all_ci_flags |= pv->pv_vf;
continue;
}
pte_mask(pmap_pv_pte(pv), ~((uint32_t)PTE51_CI));
if (active_pmap(pv->pv_pmap)) {
TBIS(PV_VA(pv));
}
}
all_ci_flags &= PV_F_CI_USR | PV_F_CI_VAC;
if (__predict_true(all_ci_flags == 0)) {
VM_MDPAGE_CLR_CI(pg);
}
}
#endif
}
#undef CONFLICTING_ALIAS
#undef MATCHING_PMAP
static inline void
pmap_stat_update_impl(long *valp, int val)
{
*valp += val;
}
#define pmap_stat_update(pm, stat, delta) \
pmap_stat_update_impl(&(pm)->pm_stats.stat, (delta))
static inline void
pmap_stat_set_impl(long *valp, int val)
{
atomic_store_relaxed(valp, val);
}
#define pmap_stat_set(pm, stat, val) \
pmap_stat_set_impl(&(pm)->pm_stats.stat, (val))
static void
pmap_pinit(pmap_t pmap, paddr_t lev1pa)
{
pmap->pm_lev1pa = lev1pa;
rb_tree_init(&pmap->pm_tables, &pmap_table_rb_ops);
TAILQ_INIT(&pmap->pm_ptpages[0]);
TAILQ_INIT(&pmap->pm_ptpages[1]);
LIST_INIT(&pmap->pm_pvlist);
pmap->pm_refcnt = 1;
}
void
pmap_virtual_space(vaddr_t *vstartp, vaddr_t *vendp)
{
*vstartp = kernel_virtual_start;
*vendp = kernel_virtual_max;
}
void
pmap_init(void)
{
pmap_alloc_init();
pmap_ptpage_init();
pmap_initialized_p = true;
}
pmap_t
pmap_create(void)
{
pmap_t pmap;
pmap = pmap_alloc();
pmap_pinit(pmap, null_segtab_pa);
return pmap;
}
void
pmap_destroy(pmap_t pmap)
{
unsigned int newval;
PMAP_CRIT_ENTER();
KASSERT(pmap->pm_refcnt > 0);
newval = --pmap->pm_refcnt;
PMAP_CRIT_EXIT();
if (newval) {
return;
}
KASSERT(pmap->pm_lev1map == NULL);
KASSERT(pmap->pm_lev1pa == null_segtab_pa);
pmap_free(pmap);
}
void
pmap_reference(pmap_t pmap)
{
PMAP_CRIT_ENTER();
pmap->pm_refcnt++;
KASSERT(pmap->pm_refcnt > 0);
PMAP_CRIT_EXIT();
}
#define PRM_TFLUSH __BIT(0)
#define PRM_CFLUSH __BIT(1)
static void
pmap_remove_mapping(pmap_t pmap, vaddr_t va, pt_entry_t *ptep,
struct pmap_table *pt, struct vm_page *pg,
int flags, struct pmap_completion *pc)
{
KASSERT(pt != NULL || pmap == pmap_kernel());
KASSERT(ptep != NULL);
const paddr_t opte = pte_load(ptep);
KASSERT(pte_valid_p(opte));
const paddr_t pa = pte_pa(opte);
KASSERT(pg == NULL || pa == VM_PAGE_TO_PHYS(pg));
if (pte_wired_p(opte)) {
pmap_stat_update(pmap, wired_count, -1);
}
pmap_stat_update(pmap, resident_count, -1);
if (flags & PRM_CFLUSH) {
#if MMU_CONFIG_68040_CLASS
if (MMU_IS_68040_CLASS) {
DCFP(pa);
ICPP(pa);
}
#endif
#if MMU_CONFIG_HP_CLASS
if (MMU_IS_HP_CLASS) {
if (pmap == pmap_kernel()) {
DCIS();
} else if (active_user_pmap(pmap)) {
DCIU();
}
}
#endif
}
if (__predict_false(pg == NULL)) {
pg = pmap_pa_to_pg(pa);
if (pg != NULL) {
pmap_evcnt(prm_lookup_pg_hit);
} else {
pmap_evcnt(prm_lookup_pg_miss);
}
} else {
pmap_evcnt(prm_got_pg);
}
if (__predict_true(pg != NULL)) {
KASSERT(pte_managed_p(opte));
VM_MDPAGE_ADD_UM(pg, opte);
pmap_pv_remove(pmap, pg, va, pc);
} else {
KASSERT(! pte_managed_p(opte));
}
pte_store(ptep, 0);
pmap_table_release(pmap, pt, pc);
if (__predict_true((flags & PRM_TFLUSH) != 0 && active_pmap(pmap))) {
TBIS(va);
}
}
static void
pmap_remove_internal(pmap_t pmap, vaddr_t sva, vaddr_t eva,
struct pmap_completion *pc)
{
pt_entry_t opte, *ptep;
struct pmap_table *pt;
vaddr_t nextseg;
int prm_flags;
#if MMU_CONFIG_HP_CLASS
pt_entry_t all_ci = PTE51_CI;
#endif
prm_flags = active_pmap(pmap) ? PRM_TFLUSH : 0;
if (pmap == pmap_kernel()) {
PMAP_CRIT_ENTER(pmap_busy(pmap));
for (ptep = pmap_kernel_pte(sva); sva < eva;
ptep++, sva += PAGE_SIZE) {
opte = pte_load(ptep);
if (pte_valid_p(opte)) {
#if MMU_CONFIG_HP_CLASS
all_ci &= opte;
#endif
pmap_remove_mapping(pmap, sva, ptep, NULL,
NULL, prm_flags, pc);
}
}
#if MMU_CONFIG_HP_CLASS
if (MMU_IS_HP_CLASS && !all_ci) {
DCIS();
}
#endif
PMAP_CRIT_EXIT(pmap_unbusy(pmap));
KASSERT(TAILQ_EMPTY(&pc->pc_ptpages));
pmap_completion_fini(pc);
return;
}
PMAP_CRIT_ENTER(pmap_busy(pmap));
while (sva < eva) {
nextseg = pmap_next_seg(sva);
if (nextseg == 0 || nextseg > eva) {
nextseg = eva;
}
ptep = pmap_pte_lookup(pmap, sva, &pt);
if (ptep == NULL) {
sva = nextseg;
continue;
}
for (; sva < nextseg; ptep++, sva += PAGE_SIZE) {
opte = pte_load(ptep);
if (! pte_valid_p(opte)) {
continue;
}
#if MMU_CONFIG_HP_CLASS
all_ci &= opte;
#endif
pmap_remove_mapping(pmap, sva, ptep, pt, NULL,
prm_flags, pc);
}
pmap_table_release(pmap, pt, pc);
}
#if MMU_CONFIG_HP_CLASS
if (MMU_IS_HP_CLASS && !all_ci) {
if (pmap == pmap_kernel()) {
DCIS();
} else if (active_user_pmap(pmap)) {
DCIU();
}
}
#endif
PMAP_CRIT_EXIT(pmap_unbusy(pmap));
pmap_completion_fini(pc);
}
void
pmap_remove(pmap_t pmap, vaddr_t sva, vaddr_t eva)
{
struct pmap_completion pc;
pmap_completion_init(&pc);
pmap_remove_internal(pmap, sva, eva, &pc);
}
bool
pmap_remove_all(pmap_t pmap)
{
struct pmap_completion pc;
struct pv_entry *pv;
struct vm_page *pg;
pt_entry_t opte;
KASSERT(pmap != pmap_kernel());
pmap_completion_init(&pc);
PMAP_CRIT_ENTER(pmap_busy(pmap));
pmap_set_lev1map(pmap, NULL, null_segtab_pa);
pmap->pm_pt_cache = NULL;
TAILQ_CONCAT(&pc.pc_ptpages, &pmap->pm_ptpages[0], ptp_list);
TAILQ_CONCAT(&pc.pc_ptpages, &pmap->pm_ptpages[1], ptp_list);
memset(&pmap->pm_tables, 0, sizeof(pmap->pm_tables));
rb_tree_init(&pmap->pm_tables, &pmap_table_rb_ops);
KASSERT(RB_TREE_MIN(&pmap->pm_tables) == NULL);
while ((pv = LIST_FIRST(&pmap->pm_pvlist)) != NULL) {
KASSERT(pv->pv_pmap == pmap);
opte = pte_load(pmap_pv_pte(pv));
pg = pmap_pa_to_pg(pte_pa(opte));
VM_MDPAGE_ADD_UM(pg, opte);
pmap_pv_remove(pmap, pg, PV_VA(pv), &pc);
}
pmap_stat_set(pmap, wired_count, 0);
pmap_stat_set(pmap, resident_count, 0);
PMAP_CRIT_EXIT(pmap_unbusy(pmap));
pmap_completion_fini(&pc);
return true;
}
void
pmap_page_protect(struct vm_page *pg, vm_prot_t prot)
{
struct pmap_completion pc;
struct pv_entry *pv;
if (prot & UVM_PROT_WRITE) {
return;
}
if (prot & UVM_PROT_READ) {
pmap_changebit(pg, PTE_WP, ~0U);
return;
}
pmap_completion_init(&pc);
PMAP_CRIT_ENTER();
while ((pv = VM_MDPAGE_PVS(pg)) != NULL) {
pmap_busy(pv->pv_pmap);
pmap_remove_mapping(pv->pv_pmap, PV_VA(pv), pmap_pv_pte(pv),
pv->pv_pt, pg, PRM_TFLUSH|PRM_CFLUSH, &pc);
pmap_unbusy(pv->pv_pmap);
}
PMAP_CRIT_EXIT();
pmap_completion_fini(&pc);
}
void
pmap_protect(pmap_t pmap, vaddr_t sva, vaddr_t eva, vm_prot_t prot)
{
pt_entry_t *ptep, opte, npte;
struct pmap_table *pt;
vaddr_t nextseg;
#if MMU_CONFIG_68040_CLASS
bool removing_write;
#endif
bool need_tflush;
if ((prot & UVM_PROT_READ) == 0) {
struct pmap_completion pc;
pmap_completion_init(&pc);
pmap_remove_internal(pmap, sva, eva, &pc);
return;
}
PMAP_CRIT_ENTER(pmap_busy(pmap));
#if MMU_CONFIG_68040_CLASS
removing_write = (prot & UVM_PROT_WRITE) == 0;
#endif
need_tflush = active_pmap(pmap);
while (sva < eva) {
nextseg = pmap_next_seg(sva);
if (nextseg == 0 || nextseg > eva) {
nextseg = eva;
}
ptep = pmap_pte_lookup(pmap, sva, &pt);
if (ptep == NULL) {
sva = nextseg;
continue;
}
for (; sva < nextseg; ptep++, sva += PAGE_SIZE) {
try_again:
opte = pte_load(ptep);
if (! pte_valid_p(opte)) {
continue;
}
npte = pte_change_prot(opte, prot);
if (npte == opte) {
continue;
}
#if MMU_CONFIG_68040_CLASS
if (MMU_IS_68040_CLASS && removing_write) {
paddr_t pa = pte_pa(opte);
DCFP(pa);
ICPP(pa);
}
#endif
if (! pte_update(ptep, opte, npte)) {
goto try_again;
}
if (need_tflush) {
TBIS(sva);
}
}
pmap_table_release(pmap, pt, NULL);
}
PMAP_CRIT_EXIT(pmap_unbusy(pmap));
}
int
pmap_enter(pmap_t pmap, vaddr_t va, paddr_t pa, vm_prot_t prot, u_int flags)
{
struct pmap_table *pt;
pt_entry_t *ptep, npte, opte;
struct pv_entry *newpv;
struct pmap_completion pc;
int error = 0;
const bool nowait = !!(flags & PMAP_CANFAIL);
pmap_completion_init(&pc);
struct vm_page * const pg = pmap_pa_to_pg(pa);
if (__predict_false(pg == NULL)) {
flags |= PMAP_NOCACHE;
}
PMAP_CRIT_ENTER(pmap_busy(pmap));
if (nowait) {
pmap_evcnt(enter_nowait);
} else {
pmap_evcnt(enter_yeswait);
}
ptep = pmap_pte_alloc(pmap, va, &pt, nowait, &pc);
if (__predict_false(ptep == NULL)) {
pmap_evcnt(enter_pte_alloc_fail);
error = ENOMEM;
goto out;
}
npte = pmap_make_pte(pa, prot, flags);
opte = pte_load(ptep);
if (pte_valid_p(opte)) {
pmap_evcnt(enter_valid);
restart:
if (pte_pa(opte) == pa) {
if (__predict_true(pg != NULL)) {
VM_MDPAGE_ADD_UM(pg, opte | npte);
KASSERT(pte_managed_p(opte));
npte |= PTE_PVLIST;
}
if (__predict_false(pte_ci_p(opte))) {
npte =
(npte & ~PTE_CMASK) | (opte & PTE_CMASK);
}
pte_store(ptep, npte);
const pt_entry_t diff = opte ^ npte;
#ifdef PMAP_EVENT_COUNTERS
if (diff & PTE_WIRED) {
pmap_evcnt(enter_wire_change);
}
if (diff & PTE_WP) {
pmap_evcnt(enter_prot_change);
}
#endif
if (pte_wired_p(diff)) {
pmap_stat_update(pmap, wired_count,
pte_wired_p(npte) ? 1 : -1);
}
if (diff) {
#if MMU_CONFIG_68040_CLASS
if (MMU_IS_68040_CLASS) {
DCFP(pa);
ICPP(pa);
}
#endif
if (active_pmap(pmap)) {
TBIS(va);
#if MMU_CONFIG_HP_CLASS
if (__predict_false(MMU_IS_HP_CLASS &&
pte_ci_p(diff) &&
pte_ci_p(npte))) {
DCIA();
}
#endif
}
}
goto out_release;
}
pmap_evcnt(enter_pa_change);
pmap_remove_mapping(pmap, va, ptep, pt, NULL,
PRM_TFLUSH|PRM_CFLUSH, &pc);
}
KASSERT(! pte_valid_p(pte_load(ptep)));
pmap_stat_update(pmap, resident_count, 1);
if (__predict_false(pte_wired_p(npte))) {
pmap_stat_update(pmap, wired_count, 1);
}
if (__predict_true(pg != NULL)) {
newpv = LIST_FIRST(&pc.pc_pvlist);
if (__predict_true(newpv == NULL)) {
newpv = pmap_pv_alloc(true);
if (__predict_false(newpv == NULL)) {
if (nowait) {
pmap_evcnt(enter_pv_alloc_fail);
error = ENOMEM;
goto out_release;
}
PMAP_CRIT_EXIT();
newpv = pmap_pv_alloc(false);
KASSERT(newpv != NULL);
PMAP_CRIT_ENTER();
opte = pte_load(ptep);
if (__predict_false(pte_valid_p(opte))) {
pmap_stat_update(pmap,
resident_count, -1);
if (pte_wired_p(npte)) {
pmap_stat_update(pmap,
wired_count, -1);
}
LIST_INSERT_HEAD(&pc.pc_pvlist,
newpv, pv_pmlist);
goto restart;
}
}
} else {
pmap_evcnt(enter_pv_recycle);
LIST_REMOVE(newpv, pv_pmlist);
}
pmap_pv_enter(pmap, pg, va, prot, pt, npte, newpv);
goto out_crit_exit;
}
pte_store(ptep, npte);
if (active_pmap(pmap)) {
TBIS(va);
}
goto out_crit_exit;
out_release:
pmap_table_release(pmap, pt, &pc);
out_crit_exit:
PMAP_CRIT_EXIT(pmap_unbusy(pmap));
out:
pmap_completion_fini(&pc);
return error;
}
void
pmap_kenter_pa(vaddr_t va, paddr_t pa, vm_prot_t prot, u_int flags)
{
pmap_t const pmap = pmap_kernel();
KASSERT(va >= VM_MIN_KERNEL_ADDRESS);
pt_entry_t * const ptep = pmap_kernel_pte(va);
const pt_entry_t npte = pmap_make_pte(pa, prot, flags | PMAP_WIRED);
if (__predict_false(prot & UVM_PROT_EXEC)) {
#if MMU_CONFIG_68040_CLASS
if (MMU_IS_68040_CLASS) {
DCFP(pa);
ICPP(pa);
}
#endif
}
KASSERT(! pte_valid_p(pte_load(ptep)));
pte_store(ptep, npte);
pmap_stat_update(pmap, resident_count, 1);
pmap_stat_update(pmap, wired_count, 1);
TBIS(va);
}
void
pmap_kremove(vaddr_t va, vsize_t size)
{
pt_entry_t *ptep, opte;
pmap_t const pmap = pmap_kernel();
int count = 0;
#if MMU_CONFIG_HP_CLASS
pt_entry_t all_ci = PTE51_CI;
#endif
KASSERT(va >= VM_MIN_KERNEL_ADDRESS);
for (ptep = pmap_kernel_pte(va); size != 0;
ptep++, size -= PAGE_SIZE, va += PAGE_SIZE) {
opte = pte_load(ptep);
if (pte_valid_p(opte)) {
KASSERT(! pte_managed_p(opte));
KASSERT(pte_wired_p(opte));
#if MMU_CONFIG_HP_CLASS
all_ci &= opte;
#endif
pte_store(ptep, 0);
TBIS(va);
count++;
}
}
#if MMU_CONFIG_HP_CLASS
if (MMU_IS_HP_CLASS && !all_ci) {
DCIS();
}
#endif
if (__predict_true(count != 0)) {
pmap_stat_update(pmap, resident_count, -count);
pmap_stat_update(pmap, wired_count, -count);
}
}
void
pmap_unwire(pmap_t pmap, vaddr_t va)
{
struct pmap_table *pt;
pt_entry_t opte, npte, *ptep;
PMAP_CRIT_ENTER(pmap_busy(pmap));
ptep = pmap_pte_lookup(pmap, va, &pt);
KASSERT(ptep != NULL);
for (;;) {
opte = pte_load(ptep);
KASSERT(pte_valid_p(opte));
if (! pte_wired_p(opte)) {
break;
}
npte = opte & ~PTE_WIRED;
if (pte_update(ptep, opte, npte)) {
pmap_stat_update(pmap, wired_count, -1);
break;
}
}
pmap_table_release(pmap, pt, NULL);
PMAP_CRIT_EXIT(pmap_unbusy(pmap));
}
bool
pmap_extract_info(pmap_t pmap, vaddr_t va, paddr_t *pap, int *flagsp)
{
struct pmap_table *pt;
pt_entry_t pte, *ptep;
bool rv = false;
if (__predict_false(pmap == pmap_kernel() &&
va >= kernel_virtual_end)) {
return false;
}
PMAP_CRIT_ENTER(pmap_busy(pmap));
ptep = pmap_pte_lookup(pmap, va, &pt);
if (__predict_true(ptep != NULL)) {
pte = pte_load(ptep);
if (__predict_true(pte_valid_p(pte))) {
if (__predict_true(pap != NULL)) {
*pap = pte_pa(pte) | (va & PAGE_MASK);
}
if (__predict_false(flagsp != NULL)) {
*flagsp =
(pte_wired_p(pte) ? PMAP_WIRED : 0) |
(pte_ci_p(pte) ? PMAP_NOCACHE : 0);
}
rv = true;
}
pmap_table_release(pmap, pt, NULL);
}
PMAP_CRIT_EXIT(pmap_unbusy(pmap));
return rv;
}
bool
pmap_extract(pmap_t pmap, vaddr_t va, paddr_t *pap)
{
return pmap_extract_info(pmap, va, pap, NULL);
}
paddr_t
vtophys(vaddr_t va)
{
paddr_t pa;
bool rv __diagused;
rv = pmap_extract_info(pmap_kernel(), va, &pa, NULL);
KASSERT(rv);
return rv ? pa : -1;
}
int
kvtop(void *v)
{
return (int)vtophys((vaddr_t)v);
}
void
pmap_activate(struct lwp *l)
{
pmap_t pmap = l->l_proc->p_vmspace->vm_map.pmap;
KASSERT(l == curlwp);
if (pmap != pmap_kernel()) {
PMAP_CRIT_ENTER(pmap_busy(pmap));
pmap_load_urp(pmap->pm_lev1pa);
PMAP_CRIT_EXIT();
}
}
void
pmap_deactivate(struct lwp *l)
{
pmap_t pmap = l->l_proc->p_vmspace->vm_map.pmap;
if (pmap != pmap_kernel()) {
PMAP_CRIT_ENTER();
PMAP_CRIT_EXIT(pmap_unbusy(pmap));
}
}
static vaddr_t pmap_tmpmap_srcva;
static vaddr_t pmap_tmpmap_dstva;
void
pmap_zero_page(paddr_t pa)
{
const int flags = MMU_IS_HP_CLASS ? PMAP_NOCACHE|PMAP_WIRED
: PMAP_WIRED;
pt_entry_t * const dst_ptep = pmap_kernel_pte(pmap_tmpmap_dstva);
const pt_entry_t dst_pte =
pmap_make_pte(pa, UVM_PROT_READ | UVM_PROT_WRITE, flags);
KASSERT(! pte_valid_p(pte_load(dst_ptep)));
pte_store(dst_ptep, dst_pte);
TBIS(pmap_tmpmap_dstva);
zeropage((void *)pmap_tmpmap_dstva);
pte_store(dst_ptep, 0);
TBIS(pmap_tmpmap_dstva);
}
void
pmap_copy_page(paddr_t src, paddr_t dst)
{
const int flags = MMU_IS_HP_CLASS ? PMAP_NOCACHE|PMAP_WIRED
: PMAP_WIRED;
pt_entry_t * const src_ptep = pmap_kernel_pte(pmap_tmpmap_srcva);
pt_entry_t * const dst_ptep = pmap_kernel_pte(pmap_tmpmap_dstva);
const pt_entry_t src_pte =
pmap_make_pte(src, UVM_PROT_READ, flags);
const pt_entry_t dst_pte =
pmap_make_pte(dst, UVM_PROT_READ | UVM_PROT_WRITE, flags);
KASSERT(! pte_valid_p(pte_load(src_ptep)));
pte_store(src_ptep, src_pte);
TBIS(pmap_tmpmap_srcva);
KASSERT(! pte_valid_p(pte_load(dst_ptep)));
pte_store(dst_ptep, dst_pte);
TBIS(pmap_tmpmap_dstva);
copypage((void *)pmap_tmpmap_srcva, (void *)pmap_tmpmap_dstva);
pte_store(src_ptep, 0);
TBIS(pmap_tmpmap_srcva);
pte_store(dst_ptep, 0);
TBIS(pmap_tmpmap_dstva);
}
static bool
pmap_testbit(struct vm_page *pg, pt_entry_t bit)
{
struct pv_entry *pv;
pt_entry_t combined_pte = 0;
PMAP_CRIT_ENTER();
combined_pte = VM_MDPAGE_UM(pg);
for (pv = VM_MDPAGE_PVS(pg);
(combined_pte & bit) == 0 && pv != NULL;
pv = pv->pv_next) {
pmap_busy(pv->pv_pmap);
combined_pte |= pte_load(pmap_pv_pte(pv));
pmap_unbusy(pv->pv_pmap);
}
VM_MDPAGE_ADD_UM(pg, combined_pte);
PMAP_CRIT_EXIT();
return (combined_pte & bit) != 0;
}
bool
pmap_is_referenced(struct vm_page *pg)
{
return pmap_testbit(pg, PTE_U);
}
bool
pmap_is_modified(struct vm_page *pg)
{
return pmap_testbit(pg, PTE_M);
}
static pt_entry_t
pmap_changebit(struct vm_page *pg, pt_entry_t set, pt_entry_t mask)
{
struct pv_entry *pv;
pt_entry_t *ptep, combined_pte, opte, npte;
#if MMU_CONFIG_68040_CLASS
bool cflush_040;
if (MMU_IS_68040_CLASS &&
((set & PTE_CRIT_BITS) != 0 ||
(mask & PTE_CRIT_BITS) == 0)) {
cflush_040 = true;
} else {
cflush_040 = false;
}
#endif
PMAP_CRIT_ENTER();
combined_pte = VM_MDPAGE_UM(pg);
for (pv = VM_MDPAGE_PVS(pg); pv != NULL; pv = pv->pv_next) {
pmap_busy(pv->pv_pmap);
for (;;) {
ptep = pmap_pv_pte(pv);
opte = pte_load(ptep);
npte = (opte | set) & mask;
combined_pte |= opte;
if (opte == npte) {
break;
}
#if MMU_CONFIG_68040_CLASS
if (__predict_false(cflush_040)) {
paddr_t pa = VM_PAGE_TO_PHYS(pg);
DCFP(pa);
ICPP(pa);
cflush_040 = false;
}
#endif
if (pte_update(ptep, opte, npte)) {
break;
}
}
if (active_pmap(pv->pv_pmap)) {
TBIS(PV_VA(pv));
}
pmap_unbusy(pv->pv_pmap);
}
VM_MDPAGE_SET_UM(pg, (combined_pte | set) & mask);
PMAP_CRIT_EXIT();
return combined_pte;
}
bool
pmap_clear_modify(struct vm_page *pg)
{
return (pmap_changebit(pg, 0, (pt_entry_t)~PTE_M) & PTE_M) != 0;
}
bool
pmap_clear_reference(struct vm_page *pg)
{
return (pmap_changebit(pg, 0, (pt_entry_t)~PTE_U) & PTE_U) != 0;
}
paddr_t
pmap_phys_address(paddr_t cookie)
{
return m68k_ptob(cookie);
}
static pt_entry_t *kernel_lev1map;
static paddr_t
pmap_growkernel_alloc_page(void)
{
if (! uvm.page_init_done) {
panic("%s: called before UVM initialized", __func__);
}
struct vm_page *pg = pmap_page_alloc(true);
if (pg == NULL) {
panic("%s: out of memory", __func__);
}
paddr_t pa = VM_PAGE_TO_PHYS(pg);
pmap_zero_page(pa);
#if MMU_CONFIG_68040_CLASS
if (MMU_IS_68040_CLASS) {
DCFP(pa);
}
#endif
return pa;
}
static void
pmap_growkernel_link_kptpage(vaddr_t va, paddr_t ptp_pa)
{
if (MMU_USE_2L) {
KASSERT((kernel_lev1map[LA2L_RI(va)] & DT51_SHORT) == 0);
kernel_lev1map[LA2L_RI(va)] = pmap_ste_proto | ptp_pa;
return;
}
const vaddr_t stpg_va = pmap_tmpmap_srcva;
paddr_t stpa, stpg_pa, stpgoff, last_stpg_pa = (paddr_t)-1;
paddr_t pa = ptp_pa, end_pa = ptp_pa + PAGE_SIZE;
pt_entry_t *stes;
for (; pa < end_pa; va += NBSEG3L, pa += TBL40_L3_SIZE) {
if ((kernel_lev1map[LA40_RI(va)] & UTE40_RESIDENT) == 0) {
if (kernel_stnext_pa == kernel_stnext_endpa) {
kernel_stnext_pa = pmap_growkernel_alloc_page();
kernel_stnext_endpa =
kernel_stnext_pa + PAGE_SIZE;
pmap_nkstpages_current_ev.ev_count++;
}
kernel_lev1map[LA40_RI(va)] =
pmap_ste_proto | kernel_stnext_pa;
kernel_stnext_pa += TBL40_L2_SIZE;
}
stpa = kernel_lev1map[LA40_RI(va)] & UTE40_PTA;
stpg_pa = m68k_trunc_page(stpa);
if (stpg_pa != last_stpg_pa) {
if (last_stpg_pa != (paddr_t)-1) {
pmap_kremove(stpg_va, PAGE_SIZE);
}
pmap_kenter_pa(stpg_va, stpg_pa,
UVM_PROT_READ | UVM_PROT_WRITE,
PMAP_WIRED | PMAP_NOCACHE);
last_stpg_pa = stpg_pa;
}
stpgoff = stpa - stpg_pa;
stes = (pt_entry_t *)(stpg_va + stpgoff);
stes[LA40_PI(va)] = pmap_ste_proto | pa;
}
if (last_stpg_pa != (paddr_t)-1) {
pmap_kremove(stpg_va, PAGE_SIZE);
}
}
vaddr_t
pmap_growkernel(vaddr_t maxkvaddr)
{
PMAP_CRIT_ENTER();
KASSERT((kernel_virtual_end & PTPAGEVAOFS) == 0);
vaddr_t new_maxkva = pmap_round_ptpage(maxkvaddr);
if (new_maxkva < kernel_virtual_end) {
new_maxkva = kernel_virtual_end;
goto done;
}
if (new_maxkva > kernel_virtual_max) {
panic("%s: out of kernel VA space (req=0x%08lx limit=0x%08lx)",
__func__, maxkvaddr, kernel_virtual_max);
}
vaddr_t va, ptp_pa;
for (va = kernel_virtual_end; va < new_maxkva; va += PTPAGEVASZ) {
ptp_pa = pmap_growkernel_alloc_page();
pmap_growkernel_link_kptpage(va, ptp_pa);
pmap_nkptpages_current_ev.ev_count++;
pmap_kenter_pa((vaddr_t)pmap_kernel_pte(va),
ptp_pa, UVM_PROT_READ | UVM_PROT_WRITE,
PMAP_WIRED | PMAP_NOCACHE);
}
kernel_virtual_end = new_maxkva;
done:
pmap_maxkva_ev.ev_count32 = new_maxkva;
pmap_kvalimit_ev.ev_count32 = kernel_virtual_max;
PMAP_CRIT_EXIT();
return new_maxkva;
}
#if MMU_CONFIG_HP_CLASS
static struct evcnt pmap_prefer_nochange_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap prefer", "nochange");
static struct evcnt pmap_prefer_change_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "pmap prefer", "change");
EVCNT_ATTACH_STATIC(pmap_prefer_change_ev);
EVCNT_ATTACH_STATIC(pmap_prefer_nochange_ev);
#endif
void
pmap_prefer(vaddr_t hint, vaddr_t *vap, int td)
{
#if MMU_CONFIG_HP_CLASS
if (MMU_IS_HP_CLASS) {
vaddr_t va = *vap;
ptrdiff_t diff = (hint - va) & pmap_aliasmask;
if (diff == 0) {
pmap_prefer_nochange_ev.ev_count++;
} else {
pmap_prefer_change_ev.ev_count++;
if (__predict_false(td)) {
va -= pmap_aliasmask + 1;
}
*vap = va + diff;
}
}
#endif
}
void
pmap_procwr(struct proc *p, vaddr_t va, size_t len)
{
(void)cachectl1(0x80000004, va, len, p);
}
#if defined(DDB) || defined(KGDB)
bool
pmap_db_write_text_enter(vaddr_t pgva, struct pmap_db_write_text_context *ctx)
{
pt_entry_t *pte = pmap_kernel_pte(pgva);
#ifdef M68K_MMU_HP
if (MMU_IS_HP_CLASS) {
DCIS();
}
#endif
pt_entry_t opte = pte_load(pte);
if (! pte_valid_p(opte)) {
return false;
}
pt_entry_t npte = (opte & ~PTE51_WP) | PTE51_CI;
pte_store(pte, npte);
TBIS(pgva);
ctx->pgva = pgva;
ctx->ptep = pte;
ctx->opte = opte;
return true;
}
void
pmap_db_write_text_exit(struct pmap_db_write_text_context *ctx)
{
pte_store(ctx->ptep, ctx->opte);
TBIS(ctx->pgva);
}
#endif
static paddr_t kernel_reloc_offset;
static vaddr_t kernel_reloc_end;
phys_ram_seg_t *
pmap_init_kcore_hdr(cpu_kcore_hdr_t *h)
{
struct gen68k_kcore_hdr *m = &h->un._gen68k;
memset(h, 0, sizeof(*h));
strcpy(h->name, "gen68k");
h->page_size = PAGE_SIZE;
h->kernbase = VM_MIN_KERNEL_ADDRESS;
m->reloc = kernel_reloc_offset;
m->relocend = kernel_reloc_end;
m->mmutype = mmutype;
m->tcr = MMU_USE_3L ? MMU51_3L_TCR_BITS : MMU51_TCR_BITS;
m->srp[0] = MMU51_SRP_BITS;
m->srp[1] = Sysseg_pa;
#if MMU_CONFIG_68040_CLASS
if (MMU_IS_68040_CLASS) {
m->itt0 = mmu_tt40[MMU_TTREG_ITT0];
m->itt1 = mmu_tt40[MMU_TTREG_ITT1];
m->tt0 = mmu_tt40[MMU_TTREG_DTT0];
m->tt1 = mmu_tt40[MMU_TTREG_DTT1];
}
#endif
#if defined(M68K_MMU_68030)
if (mmutype == MMU_68030) {
m->tt0 = mmu_tt30[MMU_TTREG_TT0];
m->tt1 = mmu_tt30[MMU_TTREG_TT1];
}
#endif
return m->ram_segs;
}
bool
pmap_pa_has_static_mapping(paddr_t pa, size_t len, vm_prot_t prot,
vaddr_t *vap, int *flagsp)
{
#if defined(M68030) || defined(M68040) || defined(M68060)
if (cputype >= CPU_68030 &&
mmu_range_is_tt(pa, len, prot, flagsp)) {
*vap = pa;
return true;
}
#endif
const struct pmap_bootmap *pmbm = machine_bootmap;
paddr_t lastpg = m68k_btop(pa + (len - 1));
paddr_t firstpg = m68k_btop(pa);
paddr_t tfirst, tlast;
vaddr_t tblva;
bool need_write = !!(prot & UVM_PROT_WRITE);
for (; pmbm->pmbm_vaddr != (vaddr_t)-1; pmbm++) {
if (pmbm->pmbm_flags & (PMBM_F_VAONLY|PMBM_F_KEEPOUT)) {
continue;
}
if (pmbm->pmbm_flags & PMBM_F_FIXEDVA) {
tblva = pmbm->pmbm_vaddr;
} else {
tblva = *pmbm->pmbm_vaddr_ptr;
}
tfirst = m68k_btop(pmbm->pmbm_paddr);
tlast = m68k_btop(pmbm->pmbm_paddr + (pmbm->pmbm_size - 1));
if (firstpg >= tfirst && lastpg <= tlast) {
if (need_write &&
(pmbm->pmbm_flags & PMBM_F_RO) != 0) {
return false;
}
*vap = tblva + (pa - pmbm->pmbm_paddr);
if (pmbm->pmbm_flags & PMBM_F_CI) {
*flagsp = PMAP_NOCACHE;
} else {
*flagsp = 0;
}
return true;
}
}
return false;
}
bool
pmap_va_is_static_mapping(vaddr_t va, size_t len)
{
#if defined(M68030) || defined(M68040) || defined(M68060)
int map_flags;
if (cputype >= CPU_68030 &&
(mmu_range_is_tt(va, len, UVM_PROT_READ, &map_flags) ||
mmu_range_is_tt(va, len, UVM_PROT_WRITE, &map_flags) ||
mmu_range_is_tt(va, len, UVM_PROT_EXEC, &map_flags))) {
return true;
}
#endif
const struct pmap_bootmap *pmbm = machine_bootmap;
vaddr_t lastpg = m68k_btop(va + (len - 1));
vaddr_t firstpg = m68k_btop(va);
vaddr_t tblva, tfirst, tlast;
for (; pmbm->pmbm_vaddr != (vaddr_t)-1; pmbm++) {
if (pmbm->pmbm_flags & (PMBM_F_VAONLY|PMBM_F_KEEPOUT)) {
continue;
}
if (pmbm->pmbm_flags & PMBM_F_FIXEDVA) {
tblva = pmbm->pmbm_vaddr;
} else {
tblva = *pmbm->pmbm_vaddr_ptr;
}
tfirst = m68k_btop(tblva);
tlast = m68k_btop(tblva + (pmbm->pmbm_size - 1));
if (firstpg >= tfirst && lastpg <= tlast) {
return true;
}
}
return false;
}
__CTASSERT(VM_MIN_KERNEL_ADDRESS == 0);
#define SYSMAP_VA_SIZE (((0xffffffffU >> PAGE_SHIFT) + 1) * sizeof(pt_entry_t))
static vaddr_t lwp0uarea;
char * vmmap;
paddr_t avail_start;
paddr_t avail_end;
extern char * kernel_text;
extern char * etext;
extern void * msgbufaddr;
paddr_t __attribute__((no_instrument_function))
pmap_bootstrap1(paddr_t nextpa, paddr_t reloff)
{
paddr_t lwp0upa, stnext_endpa, stnext_pa;
paddr_t pa, kernimg_endpa, kern_lev1pa;
vaddr_t va, nextva, kern_lev1va;
pt_entry_t *pte, *epte;
const struct pmap_bootmap *pmbm;
int entry_count = 0;
#ifdef SYSMAP_VA
#define VA_RANGE_DEFAULT 0
#define VA_RANGE_KPTES 1
#define INIT_NRANGES 2
#else
#define VA_RANGE_DEFAULT 0
#define VA_RANGE_KPTES 0
#define INIT_NRANGES 1
#endif
#define MAX_RANGES 8
struct va_range {
vaddr_t start_va;
vaddr_t end_va;
paddr_t start_ptp;
paddr_t end_ptp;
} va_ranges[MAX_RANGES], *var;
int nranges = INIT_NRANGES, r;
#define VA_IN_RANGE(va, var) \
((va) >= (var)->start_va && \
((va) < (var)->end_va || (var)->end_va == 0))
#define VA_PTE_BASE(va, var) \
(&((pt_entry_t *) \
PMAP_BOOTSTRAP_RELOC_PA((var)->start_ptp))[ \
m68k_btop((va) - (var)->start_va)])
#define RELOC(v, t) *((t *)PMAP_BOOTSTRAP_RELOC_GLOB(&(v)))
RELOC(kernel_reloc_offset, paddr_t) = reloff;
const bool is_68040_class = RELOC(mmutype, int) == MMU_68040 ||
RELOC(mmutype, int) == MMU_68060;
const bool use_3l = is_68040_class;
pt_entry_t proto_ro_pte;
pt_entry_t proto_rw_pte;
pt_entry_t proto_rw_cwt_pte;
pt_entry_t proto_rw_ci_pte;
pt_entry_t proto_ste;
if (is_68040_class) {
proto_ro_pte = PTE_VALID|PTE_WIRED|PTE_WP|PTE40_CM_WT;
proto_rw_pte = PTE_VALID|PTE_WIRED |PTE40_CM_CB;
proto_rw_cwt_pte = PTE_VALID|PTE_WIRED |PTE40_CM_WT;
proto_rw_ci_pte = PTE_VALID|PTE_WIRED |PTE40_CM_NC_SER;
} else {
proto_ro_pte = PTE_VALID|PTE_WIRED|PTE_WP;
proto_rw_pte = PTE_VALID|PTE_WIRED;
proto_rw_cwt_pte = PTE_VALID|PTE_WIRED;
proto_rw_ci_pte = PTE_VALID|PTE_WIRED |PTE51_CI;
}
proto_ste = DTE51_U | DT51_SHORT;
nextpa = m68k_round_page(nextpa);
nextva = PMAP_BOOTSTRAP_PA_TO_VA(nextpa);
kernimg_endpa = nextpa;
lwp0upa = nextpa;
nextpa += USPACE;
RELOC(lwp0uarea, vaddr_t) = nextva;
nextva += USPACE;
size_t nstpages = 0;
RELOC(Sysseg_pa, paddr_t) = kern_lev1pa = nextpa;
nextpa += PAGE_SIZE;
RELOC(kernel_lev1map, vaddr_t) = kern_lev1va = nextva;
nextva += PAGE_SIZE;
nstpages++;
RELOC(kernel_reloc_end, vaddr_t) = nextva;
stnext_pa = kern_lev1pa + TBL40_L1_SIZE;
stnext_endpa = m68k_round_page(stnext_pa);
#ifdef NULL_SEGTAB_PA
RELOC(null_segtab_pa, paddr_t) = (paddr_t)NULL_SEGTAB_PA;
#else
RELOC(null_segtab_pa, paddr_t) = nextpa;
nextpa += PAGE_SIZE;
#endif
RELOC(pmap_tmpmap_srcva, vaddr_t) = nextva;
nextva += PAGE_SIZE;
RELOC(pmap_tmpmap_dstva, vaddr_t) = nextva;
nextva += PAGE_SIZE;
RELOC(vmmap, vaddr_t) = nextva;
nextva += PAGE_SIZE;
RELOC(msgbufaddr, vaddr_t) = nextva;
nextva += m68k_round_page(MSGBUFSIZE);
#ifdef SYSMAP_VA
if ((vaddr_t)SYSMAP_VA < RELOC(kernel_virtual_max, vaddr_t)) {
RELOC(kernel_virtual_max, vaddr_t) = (vaddr_t)SYSMAP_VA;
}
RELOC(kernel_ptes, vaddr_t) = (vaddr_t)SYSMAP_VA;
va_ranges[VA_RANGE_KPTES].start_va = (vaddr_t)SYSMAP_VA;
va_ranges[VA_RANGE_KPTES].end_va = 0;
#else
RELOC(kernel_ptes, vaddr_t) = nextva;
nextva += SYSMAP_VA_SIZE;
#endif
pmbm = (const struct pmap_bootmap *)
PMAP_BOOTSTRAP_RELOC_GLOB(machine_bootmap);
for (; pmbm->pmbm_vaddr != (vaddr_t)-1; pmbm++) {
if (pmbm->pmbm_size == 0) {
continue;
}
if (pmbm->pmbm_flags & (PMBM_F_FIXEDVA | PMBM_F_KEEPOUT)) {
va = m68k_trunc_page(pmbm->pmbm_vaddr);
if (va < RELOC(kernel_virtual_max, vaddr_t)) {
RELOC(kernel_virtual_max, vaddr_t) = va;
}
} else {
*(vaddr_t *)
PMAP_BOOTSTRAP_RELOC_GLOB(pmbm->pmbm_vaddr_ptr) =
nextva;
nextva += m68k_round_page(pmbm->pmbm_size);
}
}
RELOC(kernel_virtual_start, vaddr_t) = nextva;
nextva += RELOC(physmem, psize_t) << PAGE_SHIFT;
nextva = pmap_round_ptpage(nextva);
if (nextva > RELOC(kernel_virtual_max, vaddr_t) ||
nextva < RELOC(kernel_virtual_start, vaddr_t)) {
nextva = RELOC(kernel_virtual_max, vaddr_t);
}
va_ranges[VA_RANGE_DEFAULT].start_va =
pmap_trunc_ptpage(VM_MIN_KERNEL_ADDRESS);
va_ranges[VA_RANGE_DEFAULT].end_va =
pmap_round_ptpage(nextva);
RELOC(kernel_virtual_end, vaddr_t) = nextva;
pmbm = (const struct pmap_bootmap *)
PMAP_BOOTSTRAP_RELOC_GLOB(machine_bootmap);
for (; pmbm->pmbm_vaddr != (vaddr_t)-1; pmbm++) {
if (pmbm->pmbm_size == 0) {
continue;
}
if ((pmbm->pmbm_flags & (PMBM_F_FIXEDVA | PMBM_F_KEEPOUT)) !=
PMBM_F_FIXEDVA) {
continue;
}
for (r = 0; r < nranges; r++) {
var = &va_ranges[r];
if (VA_IN_RANGE(pmbm->pmbm_vaddr, var)) {
break;
}
}
if (r < nranges) {
continue;
}
if (r == MAX_RANGES) {
break;
}
r = nranges++;
va_ranges[r].start_va = pmap_trunc_ptpage(pmbm->pmbm_vaddr);
va_ranges[r].end_va =
pmap_round_ptpage(pmbm->pmbm_vaddr + pmbm->pmbm_size);
}
size_t nptpages, total_ptpages = 0;
for (r = 0; r < nranges; r++) {
var = &va_ranges[r];
nptpages = (var->end_va - var->start_va) / PTPAGEVASZ;
var->start_ptp = nextpa;
nextpa += nptpages * PAGE_SIZE;
var->end_ptp = nextpa;
total_ptpages += nptpages;
}
#ifdef PMAP_MACHINE_CHECK_BOOTSTRAP_ALLOCATIONS
void (*alloc_checkfn)(paddr_t, paddr_t) = (void *)
PMAP_BOOTSTRAP_RELOC_GLOB(pmap_machine_check_bootstrap_allocations);
(*alloc_checkfn)(nextpa, reloff);
#endif
pte = (pt_entry_t *)PMAP_BOOTSTRAP_RELOC_PA(kernimg_endpa);
epte = (pt_entry_t *)PMAP_BOOTSTRAP_RELOC_PA(nextpa);
while (pte < epte) {
*pte++ = 0;
}
var = &va_ranges[VA_RANGE_DEFAULT];
#ifndef PMAP_BOOTSTRAP_TEXT_PROTO_PTE
#define PMAP_BOOTSTRAP_TEXT_PROTO_PTE proto_ro_pte
#endif
pa = PMAP_BOOTSTRAP_VA_TO_PA(m68k_trunc_page(&kernel_text));
pte = VA_PTE_BASE(&kernel_text, var);
epte = VA_PTE_BASE(&etext, var);
while (pte < epte) {
*pte++ = PMAP_BOOTSTRAP_TEXT_PROTO_PTE | pa;
pa += PAGE_SIZE;
entry_count++;
}
epte = VA_PTE_BASE(PMAP_BOOTSTRAP_PA_TO_VA(kernimg_endpa), var);
while (pte < epte) {
*pte++ = proto_rw_pte | pa;
pa += PAGE_SIZE;
entry_count++;
}
pa = lwp0upa;
pte = VA_PTE_BASE(RELOC(lwp0uarea, vaddr_t), var);
epte = VA_PTE_BASE(RELOC(lwp0uarea, vaddr_t) + USPACE, var);
while (pte < epte) {
*pte++ = proto_rw_pte | pa;
pa += PAGE_SIZE;
entry_count++;
}
pte = VA_PTE_BASE(kern_lev1va, var);
*pte = proto_rw_ci_pte | kern_lev1pa;
entry_count++;
va = RELOC(kernel_ptes, vaddr_t);
pt_entry_t *kptes = (pt_entry_t *)va;
struct va_range *kpte_var = &va_ranges[VA_RANGE_KPTES];
for (r = 0; r < nranges; r++) {
var = &va_ranges[r];
va = (vaddr_t)(&kptes[m68k_btop(var->start_va)]);
pte = VA_PTE_BASE(va, kpte_var);
for (pa = var->start_ptp; pa < var->end_ptp; pa += PAGE_SIZE) {
*pte++ = proto_rw_ci_pte | pa;
entry_count++;
}
}
pmbm = (const struct pmap_bootmap *)
PMAP_BOOTSTRAP_RELOC_GLOB(machine_bootmap);
for (; pmbm->pmbm_vaddr != (vaddr_t)-1; pmbm++) {
pt_entry_t proto;
if (pmbm->pmbm_size == 0 ||
(pmbm->pmbm_flags & (PMBM_F_VAONLY | PMBM_F_KEEPOUT))) {
continue;
}
if (pmbm->pmbm_flags & PMBM_F_FIXEDVA) {
va = pmbm->pmbm_vaddr;
} else {
va = *(vaddr_t *)
PMAP_BOOTSTRAP_RELOC_GLOB(pmbm->pmbm_vaddr_ptr);
}
for (r = 0; r < nranges; r++) {
var = &va_ranges[r];
if (VA_IN_RANGE(va, var)) {
break;
}
}
pa = pmbm->pmbm_paddr;
pte = VA_PTE_BASE(va, var);
switch (pmbm->pmbm_flags & (PMBM_F_CI|PMBM_F_CWT|PMBM_F_RO)) {
case PMBM_F_CI|PMBM_F_RO:
proto = proto_rw_ci_pte | PTE_WP;
break;
case PMBM_F_CWT:
proto = proto_rw_cwt_pte;
break;
case PMBM_F_CI:
proto = proto_rw_ci_pte;
break;
case PMBM_F_RO:
proto = proto_ro_pte;
break;
default:
proto = proto_rw_pte;
break;
}
for (vsize_t size = m68k_round_page(pmbm->pmbm_size);
size != 0;
va += PAGE_SIZE, pa += PAGE_SIZE, size -= PAGE_SIZE) {
*pte++ = proto | pa;
entry_count++;
}
}
for (r = 0; r < nranges; r++) {
var = &va_ranges[r];
if (use_3l) {
pt_entry_t *stes, *stes1 = (pt_entry_t *)
PMAP_BOOTSTRAP_RELOC_PA(kern_lev1pa);
for (va = var->start_va, pa = var->start_ptp;
pa < var->end_ptp;
va += NBSEG3L, pa += TBL40_L3_SIZE) {
unsigned int ri = LA40_RI(va);
if ((stes1[ri] & UTE40_RESIDENT) == 0) {
if (stnext_pa == stnext_endpa) {
stnext_pa = nextpa;
nextpa += PAGE_SIZE;
stnext_endpa = nextpa;
nstpages++;
#ifdef PMAP_MACHINE_CHECK_BOOTSTRAP_ALLOCATIONS
(*alloc_checkfn)(nextpa,
reloff);
#endif
pte = (pt_entry_t *)
PMAP_BOOTSTRAP_RELOC_PA(
stnext_pa);
epte = (pt_entry_t *)
PMAP_BOOTSTRAP_RELOC_PA(
stnext_endpa);
while (pte < epte) {
*pte++ = 0;
}
}
stes1[ri] = proto_ste | stnext_pa;
stnext_pa += TBL40_L2_SIZE;
}
stes = (pt_entry_t *)
PMAP_BOOTSTRAP_RELOC_PA(
stes1[ri] & UTE40_PTA);
stes[LA40_PI(va)] = proto_ste | pa;
}
} else {
pt_entry_t *stes = (pt_entry_t *)
PMAP_BOOTSTRAP_RELOC_PA(kern_lev1pa);
for (va = var->start_va, pa = var->start_ptp;
pa < var->end_ptp;
va += NBSEG2L, pa += PAGE_SIZE) {
stes[LA2L_RI(va)] = proto_ste | pa;
}
}
}
RELOC(pmap_nkptpages_initial_ev.ev_count32, uint32_t) =
RELOC(pmap_nkptpages_current_ev.ev_count32, uint32_t) = total_ptpages;
RELOC(pmap_nkstpages_initial_ev.ev_count32, uint32_t) =
RELOC(pmap_nkstpages_current_ev.ev_count32, uint32_t) = nstpages;
RELOC(kernel_pmap_store.pm_stats.resident_count, long) = entry_count;
RELOC(kernel_pmap_store.pm_stats.wired_count, long) = entry_count;
RELOC(kernel_stnext_pa, paddr_t) = stnext_pa;
RELOC(kernel_stnext_endpa, paddr_t) = stnext_endpa;
return nextpa;
}
__CTASSERT(PAGE_SIZE != 0);
void *
pmap_bootstrap2(void)
{
pmap_mmuclass_init();
uvmexp.pagesize = PAGE_SIZE;
uvm_md_init();
pmap_pte_proto_init();
pmap_pinit(pmap_kernel(), Sysseg_pa);
memset((void *)lwp0uarea, 0, USPACE);
uvm_lwp_setuarea(&lwp0, lwp0uarea);
curlwp = &lwp0;
curpcb = lwp_getpcb(&lwp0);
struct trapframe *tf = (struct trapframe *)(lwp0uarea + USPACE) - 1;
tf->tf_sr = PSL_USER;
lwp0.l_md.md_regs = (int *)tf;
setsfc(FC_USERD);
setdfc(FC_USERD);
return tf;
}
#ifdef PMAP_DEBUG
void pmap_test_mod_ref(void);
void
pmap_test_mod_ref(void)
{
struct vm_page *pg = pmap_page_alloc(false);
paddr_t pa = VM_PAGE_TO_PHYS(pg);
vaddr_t va = (vaddr_t)vmmap;
volatile int *loc = (volatile int *)va;
int val;
bool mod, ref;
bool exp_mod, exp_ref;
pmap_zero_page(pa);
pmap_clear_modify(pg);
pmap_clear_reference(pg);
mod = pmap_is_modified(pg);
ref = pmap_is_referenced(pg);
exp_mod = false;
exp_ref = false;
printf("%s: validating pristine page: mod=%d(%d) ref=%d(%d) (%s)\n",
__func__,
mod, exp_mod, ref, exp_ref,
mod == exp_mod && ref == exp_ref ? "OK" : "FAIL");
pmap_enter(pmap_kernel(), va, pa, UVM_PROT_ALL, 0);
mod = pmap_is_modified(pg);
ref = pmap_is_referenced(pg);
exp_mod = false;
exp_ref = false;
printf("%s: enter(ALL, 0): mod=%d(%d) ref=%d(%d) (%s)\n",
__func__,
mod, exp_mod, ref, exp_ref,
mod == exp_mod && ref == exp_ref ? "OK" : "FAIL");
val = *loc;
mod = pmap_is_modified(pg);
ref = pmap_is_referenced(pg);
exp_mod = false;
exp_ref = true;
printf("%s: ref val=%d: mod=%d(%d) ref=%d(%d) (%s)\n",
__func__,
val,
mod, exp_mod, ref, exp_ref,
mod == exp_mod && ref == exp_ref ? "OK" : "FAIL");
exp_mod = mod;
exp_ref = ref;
mod = pmap_clear_modify(pg);
ref = pmap_clear_reference(pg);
printf("%s: checking clear 1: mod=%d(%d) ref=%d(%d) (%s)\n",
__func__,
mod, exp_mod, ref, exp_ref,
mod == exp_mod && ref == exp_ref ? "OK" : "FAIL");
mod = pmap_is_modified(pg);
ref = pmap_is_referenced(pg);
exp_mod = false;
exp_ref = false;
printf("%s: checking clear 2: mod=%d(%d) ref=%d(%d) (%s)\n",
__func__,
mod, exp_mod, ref, exp_ref,
mod == exp_mod && ref == exp_ref ? "OK" : "FAIL");
*loc = 0xff;
mod = pmap_is_modified(pg);
ref = pmap_is_referenced(pg);
exp_mod = true;
exp_ref = true;
printf("%s: mod 1: mod=%d(%d) ref=%d(%d) (%s)\n",
__func__,
mod, exp_mod, ref, exp_ref,
mod == exp_mod && ref == exp_ref ? "OK" : "FAIL");
pmap_page_protect(pg, UVM_PROT_READ);
mod = pmap_clear_modify(pg);
ref = pmap_clear_reference(pg);
exp_mod = true;
exp_ref = true;
printf("%s: page_protect(READ) mod 2: mod=%d(%d) ref=%d(%d) (%s)\n",
__func__,
mod, exp_mod, ref, exp_ref,
mod == exp_mod && ref == exp_ref ? "OK" : "FAIL");
mod = pmap_is_modified(pg);
ref = pmap_is_referenced(pg);
exp_mod = false;
exp_ref = false;
printf("%s: checking clear 3: mod=%d(%d) ref=%d(%d) (%s)\n",
__func__,
mod, exp_mod, ref, exp_ref,
mod == exp_mod && ref == exp_ref ? "OK" : "FAIL");
pmap_enter(pmap_kernel(), va, pa, UVM_PROT_ALL, 0);
*loc = 0xaa;
mod = pmap_is_modified(pg);
ref = pmap_is_referenced(pg);
exp_mod = true;
exp_ref = true;
printf("%s: mod 3: mod=%d(%d) ref=%d(%d) (%s)\n",
__func__,
mod, exp_mod, ref, exp_ref,
mod == exp_mod && ref == exp_ref ? "OK" : "FAIL");
pmap_page_protect(pg, UVM_PROT_NONE);
mod = pmap_clear_modify(pg);
ref = pmap_clear_reference(pg);
exp_mod = true;
exp_ref = true;
printf("%s: page_protect(NONE) mod 4: mod=%d(%d) ref=%d(%d) (%s)\n",
__func__,
mod, exp_mod, ref, exp_ref,
mod == exp_mod && ref == exp_ref ? "OK" : "FAIL");
pmap_enter(pmap_kernel(), va, pa, UVM_PROT_ALL, 0);
*loc = 0xaa;
mod = pmap_is_modified(pg);
ref = pmap_is_referenced(pg);
exp_mod = true;
exp_ref = true;
printf("%s: mod 5: mod=%d(%d) ref=%d(%d) (%s)\n",
__func__,
mod, exp_mod, ref, exp_ref,
mod == exp_mod && ref == exp_ref ? "OK" : "FAIL");
pmap_protect(pmap_kernel(), va, va+PAGE_SIZE, UVM_PROT_READ);
mod = pmap_is_modified(pg);
ref = pmap_is_referenced(pg);
exp_mod = true;
exp_ref = true;
printf("%s: va prot(READ) mod 6: mod=%d(%d) ref=%d(%d) (%s)\n",
__func__,
mod, exp_mod, ref, exp_ref,
mod == exp_mod && ref == exp_ref ? "OK" : "FAIL");
pmap_protect(pmap_kernel(), va, va+PAGE_SIZE, UVM_PROT_NONE);
mod = pmap_is_modified(pg);
ref = pmap_is_referenced(pg);
exp_mod = true;
exp_ref = true;
printf("%s: va prot(NONE) mod 7: mod=%d(%d) ref=%d(%d) (%s)\n",
__func__,
mod, exp_mod, ref, exp_ref,
mod == exp_mod && ref == exp_ref ? "OK" : "FAIL");
pmap_clear_modify(pg);
pmap_clear_reference(pg);
mod = pmap_is_modified(pg);
ref = pmap_is_referenced(pg);
exp_mod = false;
exp_ref = false;
printf("%s: validating pristine page: mod=%d(%d) ref=%d(%d) (%s)\n",
__func__,
mod, exp_mod, ref, exp_ref,
mod == exp_mod && ref == exp_ref ? "OK" : "FAIL");
printf("%s: verifying mapping does not exist: (%s)\n",
__func__,
pmap_extract(pmap_kernel(), va, NULL) ? "FAIL" : "OK");
pmap_enter(pmap_kernel(), va, pa, UVM_PROT_READ|UVM_PROT_WRITE,
UVM_PROT_READ);
mod = pmap_is_modified(pg);
ref = pmap_is_referenced(pg);
exp_mod = false;
exp_ref = true;
printf("%s: enter(R|W, R): mod=%d(%d) ref=%d(%d) (%s)\n",
__func__,
mod, exp_mod, ref, exp_ref,
mod == exp_mod && ref == exp_ref ? "OK" : "FAIL");
pmap_enter(pmap_kernel(), va, pa, UVM_PROT_READ|UVM_PROT_WRITE,
UVM_PROT_WRITE);
mod = pmap_is_modified(pg);
ref = pmap_is_referenced(pg);
exp_mod = true;
exp_ref = true;
printf("%s: enter(R|W, W): mod=%d(%d) ref=%d(%d) (%s)\n",
__func__,
mod, exp_mod, ref, exp_ref,
mod == exp_mod && ref == exp_ref ? "OK" : "FAIL");
pmap_clear_modify(pg);
pmap_clear_reference(pg);
mod = pmap_is_modified(pg);
ref = pmap_is_referenced(pg);
exp_mod = false;
exp_ref = false;
printf("%s: validating pristine page: mod=%d(%d) ref=%d(%d) (%s)\n",
__func__,
mod, exp_mod, ref, exp_ref,
mod == exp_mod && ref == exp_ref ? "OK" : "FAIL");
pt_entry_t *ptep = pmap_kernel_pte(va);
pt_entry_t ref_pte, mod_pte;
int s = splhigh();
val = *loc;
ref_pte = pte_load(ptep);
*loc = 0;
mod_pte = pte_load(ptep);
splx(s);
mod = (mod_pte & PTE_M) != 0;
ref = (ref_pte & PTE_U) != 0;
exp_mod = true;
exp_ref = true;
printf("%s: validating MMU behavior: mod=%d(%d) ref=%d(%d) (%s)\n",
__func__,
mod, exp_mod, ref, exp_ref,
mod == exp_mod && ref == exp_ref ? "OK" : "FAIL");
pmap_remove(pmap_kernel(), va, va+PAGE_SIZE);
printf("%s: done\n", __func__);
}
#endif