#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.379 2025/12/24 20:37:04 andvar Exp $");
#include "opt_ddb.h"
#include "opt_kgdb.h"
#include "opt_sparc_arch.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/proc.h>
#include <sys/queue.h>
#include <sys/pool.h>
#include <sys/exec.h>
#include <sys/core.h>
#include <sys/kcore.h>
#include <sys/kernel.h>
#include <sys/atomic.h>
#include <sys/exec_aout.h>
#include <uvm/uvm.h>
#include <machine/autoconf.h>
#include <machine/bsd_openprom.h>
#include <machine/oldmon.h>
#include <machine/cpu.h>
#include <machine/ctlreg.h>
#include <machine/kcore.h>
#include <machine/locore.h>
#include <sparc/sparc/asm.h>
#include <sparc/sparc/cache.h>
#include <sparc/sparc/vaddrs.h>
#include <sparc/sparc/cpuvar.h>
struct pmap_stats {
int ps_unlink_pvfirst;
int ps_unlink_pvsearch;
int ps_changeprots;
int ps_enter_firstpv;
int ps_enter_secondpv;
int ps_useless_changewire;
int ps_npg_prot_all;
int ps_npg_prot_actual;
int ps_npmeg_free;
int ps_npmeg_locked;
int ps_npmeg_lru;
} pmap_stats;
#if defined(SUN4) || defined(SUN4C)
struct evcnt mmu_stolenpmegs_evcnt =
EVCNT_INITIALIZER(EVCNT_TYPE_INTR,0,"mmu","stln pmgs");
EVCNT_ATTACH_STATIC(mmu_stolenpmegs_evcnt);
struct evcnt mmu_pagein_evcnt =
EVCNT_INITIALIZER(EVCNT_TYPE_INTR,0,"mmu","pagein");
EVCNT_ATTACH_STATIC(mmu_pagein_evcnt);
#endif
#ifdef DEBUG
#define PDB_CREATE 0x0001
#define PDB_DESTROY 0x0002
#define PDB_REMOVE 0x0004
#define PDB_CHANGEPROT 0x0008
#define PDB_ENTER 0x0010
#define PDB_FOLLOW 0x0020
#define PDB_INITLOUD 0x0040
#define PDB_MMU_ALLOC 0x0100
#define PDB_MMU_STEAL 0x0200
#define PDB_CTX_ALLOC 0x0400
#define PDB_CTX_STEAL 0x0800
#define PDB_MMUREG_ALLOC 0x1000
#define PDB_MMUREG_STEAL 0x2000
#define PDB_CACHESTUFF 0x4000
#define PDB_SWITCHMAP 0x8000
#define PDB_SANITYCHK 0x10000
int pmapdebug = 0;
#define DPRINTF(level, fmt, ...) do { \
if (pmapdebug & (level)) \
printf("%s:%d: " fmt "\n", __func__, __LINE__, ##__VA_ARGS__); \
} while (0)
#else
#define DPRINTF(level, fmt, ...)
#endif
paddr_t vm_first_phys = (paddr_t)-1;
paddr_t vm_last_phys = 0;
psize_t vm_num_phys;
#define PMAP_LOCK() mutex_enter(&pmap_lock)
#define PMAP_UNLOCK() mutex_exit(&pmap_lock)
#define PV_MOD 1
#define PV_REF 2
#define PV_NC 4
#define PV_REF4M 1
#define PV_MOD4M 2
#define PV_ANC 0x10
static struct pool pv_pool;
#if defined(SUN4) || defined(SUN4C)
static struct vm_page *
pvhead4_4c(u_int pte)
{
paddr_t pa = (pte & PG_PFNUM) << PGSHIFT;
return (PHYS_TO_VM_PAGE(pa));
}
#endif
#if defined(SUN4M) || defined(SUN4D)
static struct vm_page *
pvhead4m(u_int pte)
{
paddr_t pa = (pte & SRMMU_PPNMASK) << SRMMU_PPNPASHIFT;
return (PHYS_TO_VM_PAGE(pa));
}
#endif
struct mmuentry {
struct {
struct mmuentry *prev, *next;
} me_list;
TAILQ_ENTRY(mmuentry) me_pmchain;
struct pmap *me_pmap;
u_short me_vreg;
u_short me_vseg;
u_short me_cookie;
#ifdef DIAGNOSTIC
int *me_statp;
#endif
};
struct mmuentry *mmusegments;
struct mmuentry *mmuregions;
#if defined(SUN4) || defined(SUN4C)
struct mmuentry segm_freelist, segm_lru, segm_locked;
#if defined(SUN4_MMU3L)
struct mmuentry region_freelist, region_lru, region_locked;
#endif
static void inline
mmuq_remove(struct mmuentry *e)
{
e->me_list.next->me_list.prev = e->me_list.prev;
e->me_list.prev->me_list.next = e->me_list.next;
}
static void inline
mmuq_init(struct mmuentry *e)
{
memset(e, 0, sizeof(*e));
e->me_list.next = e;
e->me_list.prev = e;
}
static inline struct mmuentry *
mmuq_first(struct mmuentry *head)
{
KASSERT(head->me_list.next != head);
return head->me_list.next;
}
static inline bool
mmuq_empty(struct mmuentry *head)
{
return head->me_list.next == head;
}
static inline void
mmuq_insert_tail(struct mmuentry *head, struct mmuentry *e)
{
e->me_list.prev = head->me_list.prev;
e->me_list.next = head;
head->me_list.prev->me_list.next = e;
head->me_list.prev = e;
}
#endif
int seginval;
int reginval;
static kmutex_t pmap_lock;
static kmutex_t demap_lock;
static bool lock_available = false;
union ctxinfo {
union ctxinfo *c_nextfree;
struct pmap *c_pmap;
};
static kmutex_t ctx_lock;
union ctxinfo *ctxinfo;
union ctxinfo *ctx_freelist;
int ctx_kick;
int ctx_kickdir;
int ncontext;
static void ctx_alloc(struct pmap *);
static void ctx_free(struct pmap *);
smeg_t tregion;
static struct pmap kernel_pmap_store;
struct pmap *const kernel_pmap_ptr = &kernel_pmap_store;
struct regmap kernel_regmap_store[NKREG];
struct segmap kernel_segmap_store[NKREG*NSEGRG];
#if defined(SUN4M) || defined(SUN4D)
u_int *kernel_regtable_store;
u_int *kernel_segtable_store;
u_int *kernel_pagtable_store;
static struct pool L1_pool;
static struct pool L23_pool;
static void *pgt_page_alloc(struct pool *, int);
static void pgt_page_free(struct pool *, void *);
static struct pool_allocator pgt_page_allocator = {
pgt_page_alloc, pgt_page_free, 0,
};
#endif
#if defined(SUN4) || defined(SUN4C)
static struct pool pte_pool;
#endif
struct memarr *pmemarr;
int npmemarr;
static paddr_t avail_start;
static vaddr_t etext_gap_start;
static vaddr_t etext_gap_end;
static vaddr_t virtual_avail;
static vaddr_t virtual_end;
static void pmap_page_upload(void);
int mmu_has_hole;
vaddr_t prom_vstart;
vaddr_t prom_vend;
static struct pool_cache pmap_cache;
static int pmap_pmap_pool_ctor(void *, void *, int);
static void pmap_pmap_pool_dtor(void *, void *);
static struct pool segmap_pool;
#if defined(SUN4)
static u_int segfixmask = 0xffffffff;
#else
#define segfixmask 0xffffffff
#endif
#define getsegmap(va) (CPU_ISSUN4C \
? lduba(va, ASI_SEGMAP) \
: (lduha(va, ASI_SEGMAP) & segfixmask))
#define setsegmap(va, pmeg) (CPU_ISSUN4C \
? stba(va, ASI_SEGMAP, pmeg) \
: stha(va, ASI_SEGMAP, pmeg))
#define getregmap(va) ((unsigned)lduha((va)+2, ASI_REGMAP) >> 8)
#define setregmap(va, smeg) stha((va)+2, ASI_REGMAP, (smeg << 8))
#if defined(SUN4M) || defined(SUN4D)
#if 0
#if VM_PROT_READ != 1 || VM_PROT_WRITE != 2 || VM_PROT_EXECUTE != 4
#error fix protection code translation table
#endif
#endif
const u_int protection_codes[2][8] = {
{
PPROT_N_RX,
PPROT_N_RX,
PPROT_N_RWX,
PPROT_N_RWX,
PPROT_N_RX,
PPROT_N_RX,
PPROT_N_RWX,
PPROT_N_RWX,
},
{
PPROT_N_RX,
PPROT_R_R,
PPROT_RW_RW,
PPROT_RW_RW,
PPROT_X_X,
PPROT_RX_RX,
PPROT_RWX_RWX,
PPROT_RWX_RWX,
}
};
#define pte_kprot4m(prot) (protection_codes[0][(prot)])
#define pte_uprot4m(prot) (protection_codes[1][(prot)])
#define pte_prot4m(pm, prot) \
(protection_codes[(pm) == pmap_kernel() ? 0 : 1][(prot)])
void setpte4m(vaddr_t va, int pte);
void setpgt4m(int *ptep, int pte);
void setpgt4m_va(vaddr_t, int *, int, int, int, u_int);
int updatepte4m(vaddr_t, int *, int, int, int, u_int);
#endif
#if defined(MULTIPROCESSOR)
#define PMAP_SET_CPUSET(pmap, cpi) \
(pmap->pm_cpuset |= (1 << (cpi)->ci_cpuid))
#define PMAP_CLR_CPUSET(pmap, cpi) \
(pmap->pm_cpuset &= ~(1 << (cpi)->ci_cpuid))
#define PMAP_CPUSET(pmap) (pmap->pm_cpuset)
#else
#define PMAP_SET_CPUSET(pmap, cpi)
#define PMAP_CLR_CPUSET(pmap, cpi)
#define PMAP_CPUSET(pmap) 1
#endif
#if defined(SUN4M) || defined(SUN4D)
static void mmu_setup4m_L1(int, struct pmap *);
static void mmu_setup4m_L2(int, struct regmap *);
static void mmu_setup4m_L3(int, struct segmap *);
void mmu_reservemon4m(struct pmap *);
void pmap_changeprot4m(pmap_t, vaddr_t, vm_prot_t, int);
void pmap_rmk4m(struct pmap *, vaddr_t, vaddr_t, int, int);
void pmap_rmu4m(struct pmap *, vaddr_t, vaddr_t, int, int);
int pmap_enk4m(struct pmap *, vaddr_t, vm_prot_t,
int, struct vm_page *, int);
int pmap_enu4m(struct pmap *, vaddr_t, vm_prot_t,
int, struct vm_page *, int);
void pv_changepte4m(struct vm_page *, int, int);
int pv_syncflags4m(struct vm_page *);
int pv_link4m(struct vm_page *, struct pmap *, vaddr_t, u_int *);
void pv_unlink4m(struct vm_page *, struct pmap *, vaddr_t);
#endif
#if defined(SUN4) || defined(SUN4C)
void mmu_reservemon4_4c(int *, int *);
void pmap_changeprot4_4c(pmap_t, vaddr_t, vm_prot_t, int);
void pmap_rmk4_4c(struct pmap *, vaddr_t, vaddr_t, int, int);
void pmap_rmu4_4c(struct pmap *, vaddr_t, vaddr_t, int, int);
int pmap_enk4_4c(struct pmap *, vaddr_t, vm_prot_t,
int, struct vm_page *, int);
int pmap_enu4_4c(struct pmap *, vaddr_t, vm_prot_t,
int, struct vm_page *, int);
void pv_changepte4_4c(struct vm_page *, int, int);
int pv_syncflags4_4c(struct vm_page *);
int pv_link4_4c(struct vm_page *, struct pmap *, vaddr_t, u_int *);
void pv_unlink4_4c(struct vm_page *, struct pmap *, vaddr_t);
#endif
#if !(defined(SUN4M) || defined(SUN4D)) && (defined(SUN4) || defined(SUN4C))
#define pmap_rmk pmap_rmk4_4c
#define pmap_rmu pmap_rmu4_4c
#elif (defined(SUN4M) || defined(SUN4D)) && !(defined(SUN4) || defined(SUN4C))
#define pmap_rmk pmap_rmk4m
#define pmap_rmu pmap_rmu4m
#else
bool (*pmap_clear_modify_p)(struct vm_page *);
bool (*pmap_clear_reference_p)(struct vm_page *);
int (*pmap_enter_p)(pmap_t, vaddr_t, paddr_t, vm_prot_t, u_int);
bool (*pmap_extract_p)(pmap_t, vaddr_t, paddr_t *);
bool (*pmap_is_modified_p)(struct vm_page *);
bool (*pmap_is_referenced_p)(struct vm_page *);
void (*pmap_kenter_pa_p)(vaddr_t, paddr_t, vm_prot_t, u_int);
void (*pmap_kremove_p)(vaddr_t, vsize_t);
void (*pmap_kprotect_p)(vaddr_t, vsize_t, vm_prot_t);
void (*pmap_page_protect_p)(struct vm_page *, vm_prot_t);
void (*pmap_protect_p)(pmap_t, vaddr_t, vaddr_t, vm_prot_t);
void (*pmap_rmk_p)(struct pmap *, vaddr_t, vaddr_t, int, int);
void (*pmap_rmu_p)(struct pmap *, vaddr_t, vaddr_t, int, int);
#define pmap_rmk (*pmap_rmk_p)
#define pmap_rmu (*pmap_rmu_p)
#endif
#if defined(SUN4M) || defined(SUN4D)
static void
sp_tlb_flush(int va, int ctx, int lvl)
{
int opsr, octx;
va &= ~0xfff;
va |= lvl;
opsr = getpsr();
__asm volatile ("wr %0, %1, %%psr"
:: "r"(opsr), "n"(PSR_ET) : "memory");
__asm volatile ("nop; nop; nop");
octx = getcontext4m();
setcontext4m(ctx);
__asm volatile ("sta %%g0, [%0]%1" :: "r"(va), "n"(ASI_SRMMUFP));
setcontext4m(octx);
setpsr(opsr);
}
static inline void
sp_tlb_flush_all(void)
{
sta(ASI_SRMMUFP_LN, ASI_SRMMUFP, 0);
}
#if defined(MULTIPROCESSOR)
static inline void smp_tlb_flush_page (int va, int ctx, u_int cpuset);
static inline void smp_tlb_flush_segment (int va, int ctx, u_int cpuset);
static inline void smp_tlb_flush_region (int va, int ctx, u_int cpuset);
static inline void smp_tlb_flush_context (int ctx, u_int cpuset);
static inline void smp_tlb_flush_all (void);
static inline void
smp_tlb_flush_page(int va, int ctx, u_int cpuset)
{
if (CPU_ISSUN4D) {
sp_tlb_flush(va, ctx, ASI_SRMMUFP_L3);
} else
FXCALL3(sp_tlb_flush, ft_tlb_flush, va, ctx, ASI_SRMMUFP_L3, cpuset);
}
static inline void
smp_tlb_flush_segment(int va, int ctx, u_int cpuset)
{
if (CPU_ISSUN4D) {
sp_tlb_flush(va, ctx, ASI_SRMMUFP_L2);
} else
FXCALL3(sp_tlb_flush, ft_tlb_flush, va, ctx, ASI_SRMMUFP_L2, cpuset);
}
static inline void
smp_tlb_flush_region(int va, int ctx, u_int cpuset)
{
if (CPU_ISSUN4D) {
sp_tlb_flush(va, ctx, ASI_SRMMUFP_L1);
} else
FXCALL3(sp_tlb_flush, ft_tlb_flush, va, ctx, ASI_SRMMUFP_L1, cpuset);
}
static inline void
smp_tlb_flush_context(int ctx, u_int cpuset)
{
if (CPU_ISSUN4D) {
sp_tlb_flush(0, ctx, ASI_SRMMUFP_L0);
} else
FXCALL3(sp_tlb_flush, ft_tlb_flush, 0, ctx, ASI_SRMMUFP_L0, cpuset);
}
static inline void
smp_tlb_flush_all(void)
{
if (CPU_ISSUN4D) {
sp_tlb_flush_all();
} else
XCALL0(sp_tlb_flush_all, CPUSET_ALL);
}
#endif
#if defined(MULTIPROCESSOR)
#define tlb_flush_page(va,ctx,s) smp_tlb_flush_page(va,ctx,s)
#define tlb_flush_segment(va,ctx,s) smp_tlb_flush_segment(va,ctx,s)
#define tlb_flush_region(va,ctx,s) smp_tlb_flush_region(va,ctx,s)
#define tlb_flush_context(ctx,s) smp_tlb_flush_context(ctx,s)
#define tlb_flush_all() smp_tlb_flush_all()
#else
#define tlb_flush_page(va,ctx,s) sp_tlb_flush(va,ctx,ASI_SRMMUFP_L3)
#define tlb_flush_segment(va,ctx,s) sp_tlb_flush(va,ctx,ASI_SRMMUFP_L2)
#define tlb_flush_region(va,ctx,s) sp_tlb_flush(va,ctx,ASI_SRMMUFP_L1)
#define tlb_flush_context(ctx,s) sp_tlb_flush(0,ctx,ASI_SRMMUFP_L0)
#define tlb_flush_all() sp_tlb_flush_all()
#endif
static u_int VA2PA(void *);
static u_long srmmu_bypass_read(u_long);
static u_int
VA2PA(void *addr)
{
u_int pte;
pte = lda(((u_int)addr & ~0xfff) | ASI_SRMMUFP_L3, ASI_SRMMUFP);
(void)lda(SRMMU_SFSR, ASI_SRMMU);
if ((pte & SRMMU_TETYPE) == SRMMU_TEPTE)
return (((pte & SRMMU_PPNMASK) << SRMMU_PPNPASHIFT) |
((u_int)addr & 0xfff));
tlb_flush_all_real();
pte = lda(((u_int)addr & ~0xfff) | ASI_SRMMUFP_L2, ASI_SRMMUFP);
if ((pte & SRMMU_TETYPE) == SRMMU_TEPTE)
return (((pte & SRMMU_PPNMASK) << SRMMU_PPNPASHIFT) |
((u_int)addr & 0x3ffff));
pte = lda(((u_int)addr & ~0xfff) | ASI_SRMMUFP_L1, ASI_SRMMUFP);
if ((pte & SRMMU_TETYPE) == SRMMU_TEPTE)
return (((pte & SRMMU_PPNMASK) << SRMMU_PPNPASHIFT) |
((u_int)addr & 0xffffff));
pte = lda(((u_int)addr & ~0xfff) | ASI_SRMMUFP_L0, ASI_SRMMUFP);
if ((pte & SRMMU_TETYPE) == SRMMU_TEPTE)
return (((pte & SRMMU_PPNMASK) << SRMMU_PPNPASHIFT) |
((u_int)addr & 0xffffffff));
#ifdef DIAGNOSTIC
panic("VA2PA: Asked to translate unmapped VA %p", addr);
#else
return (0);
#endif
}
int
updatepte4m(vaddr_t va, int *pte, int bic, int bis, int ctx, u_int cpuset)
{
int oldval, swapval;
volatile int *vpte = (volatile int *)pte;
bool can_lock = lock_available;
if (__predict_true(can_lock))
mutex_spin_enter(&demap_lock);
oldval = 0;
do {
swapval = 0;
swap(vpte, swapval);
tlb_flush_page(va, ctx, cpuset);
oldval |= swapval;
} while (__predict_false(*vpte != 0));
swapval = (oldval & ~bic) | bis;
swap(vpte, swapval);
if (__predict_true(can_lock))
mutex_spin_exit(&demap_lock);
return (oldval);
}
inline void
setpgt4m(int *ptep, int pte)
{
kpreempt_disable();
swap(ptep, pte);
kpreempt_enable();
}
inline void
setpgt4m_va(vaddr_t va, int *ptep, int pte, int pageflush, int ctx,
u_int cpuset)
{
#if defined(MULTIPROCESSOR)
updatepte4m(va, ptep, 0xffffffff, pte, pageflush ? ctx : 0, cpuset);
#else
kpreempt_disable();
if (__predict_true(pageflush))
tlb_flush_page(va, ctx, 0);
setpgt4m(ptep, pte);
kpreempt_enable();
#endif
}
void
setpte4m(vaddr_t va, int pte)
{
struct pmap *pm;
struct regmap *rp;
struct segmap *sp;
#ifdef DEBUG
if (getcontext4m() != 0)
panic("setpte4m: user context");
#endif
pm = pmap_kernel();
rp = &pm->pm_regmap[VA_VREG(va)];
sp = &rp->rg_segmap[VA_VSEG(va)];
tlb_flush_page(va, 0, CPUSET_ALL);
setpgt4m(sp->sg_pte + VA_SUN4M_VPG(va), pte);
}
void *
pgt_page_alloc(struct pool *pp, int flags)
{
int cacheit = (CACHEINFO.c_flags & CACHE_PAGETABLES) != 0;
struct vm_page *pg;
vaddr_t va;
paddr_t pa;
while ((pg = uvm_pagealloc(NULL, 0, NULL, 0)) == NULL &&
(flags & PR_WAITOK) != 0) {
uvm_wait("pgtpg");
}
if (pg == NULL) {
KASSERT((flags & PR_WAITOK) == 0);
return NULL;
}
va = uvm_km_alloc(kernel_map, PAGE_SIZE, 0, UVM_KMF_VAONLY |
((flags & PR_WAITOK) ? 0 : UVM_KMF_NOWAIT | UVM_KMF_TRYLOCK));
if (va == 0) {
KASSERT((flags & PR_WAITOK) == 0);
uvm_pagefree(pg);
return (NULL);
}
pa = VM_PAGE_TO_PHYS(pg);
if (cacheit == 0)
pcache_flush_page(pa, 1);
pmap_kenter_pa(va, pa | (cacheit ? 0 : PMAP_NC),
VM_PROT_READ | VM_PROT_WRITE, 0);
pmap_update(pmap_kernel());
return ((void *)va);
}
void
pgt_page_free(struct pool *pp, void *v)
{
vaddr_t va;
paddr_t pa;
bool rv __diagused;
va = (vaddr_t)v;
rv = pmap_extract(pmap_kernel(), va, &pa);
KASSERT(rv);
uvm_pagefree(PHYS_TO_VM_PAGE(pa));
pmap_kremove(va, PAGE_SIZE);
uvm_km_free(kernel_map, va, PAGE_SIZE, UVM_KMF_VAONLY);
}
#endif
#if defined(SUN4_MMU3L)
#define CTX_USABLE(pm,rp) ( \
((pm)->pm_ctx != NULL && \
(!HASSUN4_MMU3L || (rp)->rg_smeg != reginval)) \
)
#else
#define CTX_USABLE(pm,rp) ((pm)->pm_ctx != NULL )
#endif
#define GAP_WIDEN(pm,vr) do if (CPU_HAS_SUNMMU) { \
if (vr + 1 == pm->pm_gap_start) \
pm->pm_gap_start = vr; \
if (vr == pm->pm_gap_end) \
pm->pm_gap_end = vr + 1; \
} while (0)
#define GAP_SHRINK(pm,vr) do if (CPU_HAS_SUNMMU) { \
int x; \
x = pm->pm_gap_start + (pm->pm_gap_end - pm->pm_gap_start) / 2; \
if (vr > x) { \
if (vr < pm->pm_gap_end) \
pm->pm_gap_end = vr; \
} else { \
if (vr >= pm->pm_gap_start && x != pm->pm_gap_start) \
pm->pm_gap_start = vr + 1; \
} \
} while (0)
static void get_phys_mem(void **);
#if 0
void kvm_iocache(char *, int);
#endif
#ifdef DEBUG
void pm_check(char *, struct pmap *);
void pm_check_k(char *, struct pmap *);
void pm_check_u(char *, struct pmap *);
#endif
static u_long va2pa_offset;
#define PMAP_BOOTSTRAP_VA2PA(v) ((paddr_t)((u_long)(v) - va2pa_offset))
#define PMAP_BOOTSTRAP_PA2VA(p) ((vaddr_t)((u_long)(p) + va2pa_offset))
void
get_phys_mem(void **top)
{
struct memarr *mp;
char *p;
int i;
p = (void *)ALIGN(*top);
pmemarr = (struct memarr *)p;
npmemarr = prom_makememarr(pmemarr, 1000, MEMARR_AVAILPHYS);
p += npmemarr * sizeof(struct memarr);
*top = p;
for (physmem = 0, mp = pmemarr, i = npmemarr; --i >= 0; mp++)
physmem += btoc(mp->len);
}
void
pmap_virtual_space(vaddr_t *v_start, vaddr_t *v_end)
{
*v_start = virtual_avail;
*v_end = virtual_end;
}
#ifdef PMAP_GROWKERNEL
vaddr_t
pmap_growkernel(vaddr_t eva)
{
struct regmap *rp;
struct segmap *sp;
int vr, evr, M, N, i;
struct vm_page *pg;
vaddr_t va;
if (eva <= virtual_end)
return (virtual_end);
KASSERT(CPU_HAS_SUNMMU);
vr = virtual_end >> RGSHIFT;
evr = (eva + NBPRG - 1) >> RGSHIFT;
eva = evr << RGSHIFT;
if (eva > VM_MAX_KERNEL_ADDRESS)
panic("growkernel: grown too large: %lx", eva);
M = NBPG / (NPTESG * sizeof(int));
N = (NBPRG/NBPSG) / M;
while (vr < evr) {
rp = &pmap_kernel()->pm_regmap[vr];
for (i = 0; i < N; i++) {
sp = &rp->rg_segmap[i * M];
va = (vaddr_t)sp->sg_pte;
pg = uvm_pagealloc(NULL, 0, NULL, UVM_PGA_USERESERVE);
if (pg == NULL)
panic("growkernel: out of memory");
pmap_kenter_pa(va, VM_PAGE_TO_PHYS(pg),
VM_PROT_READ | VM_PROT_WRITE, 0);
}
}
virtual_end = eva;
return (eva);
}
#endif
static void
pmap_page_upload(void)
{
int n;
paddr_t pstart, pend;
pstart = PMAP_BOOTSTRAP_VA2PA(etext_gap_start);
pend = PMAP_BOOTSTRAP_VA2PA(etext_gap_end);
#ifdef DIAGNOSTIC
if (avail_start <= pstart)
panic("pmap_page_upload: etext gap overlap: %lx < %lx",
(u_long)avail_start, (u_long)pstart);
#endif
if (etext_gap_start < etext_gap_end) {
vm_first_phys = pstart;
uvm_page_physload(
atop(pstart),
atop(pend),
atop(pstart),
atop(pend), VM_FREELIST_DEFAULT);
}
for (n = 0; n < npmemarr; n++) {
pstart = pmemarr[n].addr;
pend = pstart + pmemarr[n].len;
if (vm_first_phys > pstart)
vm_first_phys = pstart;
if (vm_last_phys < pend)
vm_last_phys = pend;
if (pstart < PMAP_BOOTSTRAP_VA2PA(KERNBASE)) {
paddr_t chop = PMAP_BOOTSTRAP_VA2PA(KERNBASE);
if (pend < chop)
chop = pend;
#ifdef DEBUG
prom_printf("bootstrap gap: pstart %lx, chop %lx, pend %lx\n",
pstart, chop, pend);
#endif
uvm_page_physload(
atop(pstart),
atop(chop),
atop(pstart),
atop(chop),
VM_FREELIST_DEFAULT);
pstart = chop;
}
if (pstart <= avail_start && avail_start < pend)
pstart = avail_start;
if (pstart == pend)
continue;
uvm_page_physload(
atop(pstart),
atop(pend),
atop(pstart),
atop(pend), VM_FREELIST_DEFAULT);
}
#if defined(MULTIPROCESSOR)
{
CPU_INFO_ITERATOR cpunum;
struct cpu_info *cpi;
for (CPU_INFO_FOREACH(cpunum, cpi)) {
if (cpi->ci_free_sva1)
uvm_page_physload(atop(PMAP_BOOTSTRAP_VA2PA(cpi->ci_free_sva1)),
atop(PMAP_BOOTSTRAP_VA2PA(cpi->ci_free_eva1)),
atop(PMAP_BOOTSTRAP_VA2PA(cpi->ci_free_sva1)),
atop(PMAP_BOOTSTRAP_VA2PA(cpi->ci_free_eva1)),
VM_FREELIST_DEFAULT);
if (cpi->ci_free_sva2)
uvm_page_physload(atop(PMAP_BOOTSTRAP_VA2PA(cpi->ci_free_sva2)),
atop(PMAP_BOOTSTRAP_VA2PA(cpi->ci_free_eva2)),
atop(PMAP_BOOTSTRAP_VA2PA(cpi->ci_free_sva2)),
atop(PMAP_BOOTSTRAP_VA2PA(cpi->ci_free_eva2)),
VM_FREELIST_DEFAULT);
}
}
#endif
}
int
pmap_pa_exists(paddr_t pa)
{
int nmem;
struct memarr *mp;
for (mp = pmemarr, nmem = npmemarr; --nmem >= 0; mp++) {
if (pa >= mp->addr && pa < mp->addr + mp->len)
return 1;
}
return 0;
}
#define MR4_4C(pte) (((pte) >> PG_M_SHIFT) & (PV_MOD | PV_REF))
#define MR4M(pte) (((pte) >> PG_M_SHIFT4M) & (PV_MOD4M | PV_REF4M))
#if defined(SUN4) || defined(SUN4C)
void
mmu_reservemon4_4c(int *nrp, int *nsp)
{
u_int va = 0, eva = 0;
int mmuseg, i, nr, ns, vr;
int *pte;
#if defined(SUN4_MMU3L)
int mmureg, lastvr = 0;
#endif
struct regmap *rp;
#if defined(SUN4)
if (CPU_ISSUN4) {
prom_vstart = va = OLDMON_STARTVADDR;
prom_vend = eva = OLDMON_ENDVADDR;
}
#endif
#if defined(SUN4C)
if (CPU_ISSUN4C) {
prom_vstart = va = OPENPROM_STARTVADDR;
prom_vend = eva = OPENPROM_ENDVADDR;
}
#endif
ns = *nsp;
nr = *nrp;
while (va < eva) {
vr = VA_VREG(va);
rp = &pmap_kernel()->pm_regmap[vr];
#if defined(SUN4_MMU3L)
if (HASSUN4_MMU3L && vr != lastvr) {
lastvr = vr;
mmureg = getregmap(va);
if (mmureg < nr)
rp->rg_smeg = nr = mmureg;
for (i = ncontext; --i > 0;)
prom_setcontext(i, (void *)va, mmureg);
}
#endif
mmuseg = getsegmap(va);
if (mmuseg < ns)
ns = mmuseg;
if (!HASSUN4_MMU3L)
for (i = ncontext; --i > 0;)
prom_setcontext(i, (void *)va, mmuseg);
if (mmuseg == seginval) {
va += NBPSG;
continue;
}
rp->rg_nsegmap += 1;
rp->rg_segmap[VA_VSEG(va)].sg_pmeg = mmuseg;
rp->rg_segmap[VA_VSEG(va)].sg_npte = NPTESG;
pte = rp->rg_segmap[VA_VSEG(va)].sg_pte;
for (i = NPTESG; --i >= 0; va += NBPG, pte++) {
*pte = getpte4(va) | PG_S;
setpte4(va, *pte);
}
}
*nsp = ns;
*nrp = nr;
return;
}
#endif
#if defined(SUN4M) || defined(SUN4D)
u_long
srmmu_bypass_read(u_long paddr)
{
unsigned long v;
if (cpuinfo.mxcc) {
unsigned long s = lda(SRMMU_PCR, ASI_SRMMU);
sta(SRMMU_PCR, ASI_SRMMU, s | VIKING_PCR_AC);
v = lda(paddr, ASI_BYPASS);
sta(SRMMU_PCR, ASI_SRMMU, s);
} else
v = lda(paddr, ASI_BYPASS);
return (v);
}
void
mmu_reservemon4m(struct pmap *kpmap)
{
unsigned int rom_ctxtbl;
int te;
#if !(defined(PROM_AT_F0) || defined(MSIIEP))
prom_vstart = OPENPROM_STARTVADDR;
prom_vend = OPENPROM_ENDVADDR;
#else
prom_vstart = 0xf0000000;
#if defined(MSIIEP)
prom_vend = 0xf0800000;
#else
prom_vend = 0xf0080000;
#endif
#endif
rom_ctxtbl = (lda(SRMMU_CXTPTR,ASI_SRMMU) << SRMMU_PPNPASHIFT);
te = srmmu_bypass_read(rom_ctxtbl);
switch (te & SRMMU_TETYPE) {
case SRMMU_TEINVALID:
cpuinfo.ctx_tbl[0] = SRMMU_TEINVALID;
panic("mmu_reservemon4m: no existing L0 mapping! "
"(How are we running?");
break;
case SRMMU_TEPTE:
panic("mmu_reservemon4m: can't handle ROM 4G page size");
break;
case SRMMU_TEPTD:
mmu_setup4m_L1(te, kpmap);
break;
default:
panic("mmu_reservemon4m: unknown pagetable entry type");
}
}
void
mmu_setup4m_L1(int regtblptd, struct pmap *kpmap)
{
unsigned int regtblrover;
int i;
unsigned int te;
struct regmap *rp;
int j, k;
regtblrover =
((regtblptd & ~SRMMU_TETYPE) << SRMMU_PPNPASHIFT) +
(VA_VREG(KERNBASE)+1) * sizeof(long);
for (i = VA_VREG(KERNBASE) + 1; i < SRMMU_L1SIZE;
i++, regtblrover += sizeof(long)) {
rp = &kpmap->pm_regmap[i];
te = srmmu_bypass_read(regtblrover);
switch(te & SRMMU_TETYPE) {
case SRMMU_TEINVALID:
break;
case SRMMU_TEPTE:
#ifdef DEBUG
prom_printf("mmu_setup4m_L1: "
"converting region 0x%x from L1->L3\n", i);
#endif
for (j = 0; j < SRMMU_L2SIZE; j++) {
struct segmap *sp = &rp->rg_segmap[j];
for (k = 0; k < SRMMU_L3SIZE; k++) {
setpgt4m(&sp->sg_pte[k],
(te & SRMMU_L1PPNMASK) |
(j << SRMMU_L2PPNSHFT) |
(k << SRMMU_L3PPNSHFT) |
(te & SRMMU_PGBITSMSK) |
((te & SRMMU_PROT_MASK) |
PPROT_U2S_OMASK) |
SRMMU_TEPTE);
}
}
break;
case SRMMU_TEPTD:
mmu_setup4m_L2(te, rp);
break;
default:
panic("mmu_setup4m_L1: unknown pagetable entry type");
}
}
}
void
mmu_setup4m_L2(int segtblptd, struct regmap *rp)
{
unsigned int segtblrover;
int i, k;
unsigned int te;
struct segmap *sp;
segtblrover = (segtblptd & ~SRMMU_TETYPE) << SRMMU_PPNPASHIFT;
for (i = 0; i < SRMMU_L2SIZE; i++, segtblrover += sizeof(long)) {
sp = &rp->rg_segmap[i];
te = srmmu_bypass_read(segtblrover);
switch(te & SRMMU_TETYPE) {
case SRMMU_TEINVALID:
break;
case SRMMU_TEPTE:
#ifdef DEBUG
prom_printf("mmu_setup4m_L2: converting L2 entry at segment 0x%x to L3\n",i);
#endif
for (k = 0; k < SRMMU_L3SIZE; k++) {
setpgt4m(&sp->sg_pte[k],
(te & SRMMU_L1PPNMASK) |
(te & SRMMU_L2PPNMASK) |
(k << SRMMU_L3PPNSHFT) |
(te & SRMMU_PGBITSMSK) |
((te & SRMMU_PROT_MASK) |
PPROT_U2S_OMASK) |
SRMMU_TEPTE);
}
break;
case SRMMU_TEPTD:
mmu_setup4m_L3(te, sp);
break;
default:
panic("mmu_setup4m_L2: unknown pagetable entry type");
}
}
}
void
mmu_setup4m_L3(int pagtblptd, struct segmap *sp)
{
unsigned int pagtblrover;
int i;
unsigned int te;
pagtblrover = (pagtblptd & ~SRMMU_TETYPE) << SRMMU_PPNPASHIFT;
for (i = 0; i < SRMMU_L3SIZE; i++, pagtblrover += sizeof(long)) {
te = srmmu_bypass_read(pagtblrover);
switch(te & SRMMU_TETYPE) {
case SRMMU_TEINVALID:
break;
case SRMMU_TEPTE:
setpgt4m(&sp->sg_pte[i], te | PPROT_U2S_OMASK);
pmap_kernel()->pm_stats.resident_count++;
break;
case SRMMU_TEPTD:
panic("mmu_setup4m_L3: PTD found in L3 page table");
default:
panic("mmu_setup4m_L3: unknown pagetable entry type");
}
}
}
#endif
#if defined(SUN4) || defined(SUN4C)
static int me_alloc(struct mmuentry *, struct pmap *, int, int);
static void me_free(struct pmap *, u_int);
#if defined(SUN4_MMU3L)
static int region_alloc(struct mmuentry *, struct pmap *, int);
static void region_free(struct pmap *, u_int);
#endif
static inline int
me_alloc(struct mmuentry *mh, struct pmap *newpm, int newvreg, int newvseg)
{
struct mmuentry *me;
struct pmap *pm;
int i, va, *ptep, pte;
int ctx;
struct regmap *rp;
struct segmap *sp;
if (!mmuq_empty(&segm_freelist)) {
me = mmuq_first(&segm_freelist);
mmuq_remove(me);
#ifdef DEBUG
if (me->me_pmap != NULL)
panic("me_alloc: freelist entry has pmap");
DPRINTF(PDB_MMU_ALLOC,
"me_alloc: got pmeg %d", me->me_cookie);
#endif
mmuq_insert_tail(mh, me);
TAILQ_INSERT_TAIL(&newpm->pm_seglist, me, me_pmchain);
#ifdef DIAGNOSTIC
pmap_stats.ps_npmeg_free--;
if (mh == &segm_locked) {
pmap_stats.ps_npmeg_locked++;
me->me_statp = &pmap_stats.ps_npmeg_locked;
} else {
pmap_stats.ps_npmeg_lru++;
me->me_statp = &pmap_stats.ps_npmeg_lru;
}
#endif
me->me_pmap = newpm;
me->me_vseg = newvseg;
me->me_vreg = newvreg;
return (me->me_cookie);
}
if (mmuq_empty(&segm_lru))
panic("me_alloc: all pmegs gone");
me = mmuq_first(&segm_lru);
pm = me->me_pmap;
DPRINTF(PDB_MMU_ALLOC | PDB_MMU_STEAL,
"me_alloc: stealing pmeg 0x%x from pmap %p", me->me_cookie, pm);
mmu_stolenpmegs_evcnt.ev_count++;
mmuq_remove(me);
mmuq_insert_tail(mh, me);
#ifdef DIAGNOSTIC
if (mh == &segm_locked) {
pmap_stats.ps_npmeg_lru--;
pmap_stats.ps_npmeg_locked++;
me->me_statp = &pmap_stats.ps_npmeg_locked;
} else {
me->me_statp = &pmap_stats.ps_npmeg_lru;
}
#endif
rp = &pm->pm_regmap[me->me_vreg];
sp = &rp->rg_segmap[me->me_vseg];
ptep = sp->sg_pte;
#ifdef DEBUG
if (sp->sg_pmeg != me->me_cookie)
panic("me_alloc: wrong sg_pmeg (%d != %d)",
sp->sg_pmeg, me->me_cookie);
#endif
ctx = getcontext4();
write_user_windows();
if (CTX_USABLE(pm,rp)) {
setcontext4(pm->pm_ctxnum);
va = VSTOVA(me->me_vreg, me->me_vseg);
#ifdef DEBUG
if (getsegmap(va) != me->me_cookie)
panic("me_alloc: wrong pmeg in MMU (%d != %d)",
getsegmap(va), me->me_cookie);
#endif
cache_flush_segment(me->me_vreg, me->me_vseg, pm->pm_ctxnum);
} else {
va = 0;
setcontext4(0);
if (HASSUN4_MMU3L)
setregmap(va, tregion);
setsegmap(va, me->me_cookie);
}
i = NPTESG;
do {
int swbits = *ptep & PG_MBZ;
pte = getpte4(va);
if ((pte & (PG_V | PG_TYPE)) == (PG_V | PG_OBMEM)) {
struct vm_page *pg;
if ((pg = pvhead4_4c(pte)) != NULL)
VM_MDPAGE_PVHEAD(pg)->pv_flags |= MR4_4C(pte);
}
*ptep++ = swbits | (pte & ~(PG_U|PG_M));
va += NBPG;
} while (--i > 0);
if (CTX_USABLE(pm,rp)) {
va = VSTOVA(me->me_vreg,me->me_vseg);
if (pm != pmap_kernel() || HASSUN4_MMU3L)
setsegmap(va, seginval);
else {
for (i = ncontext; --i >= 0;) {
setcontext4(i);
setsegmap(va, seginval);
}
}
}
sp->sg_pmeg = seginval;
TAILQ_REMOVE(&pm->pm_seglist, me, me_pmchain);
setcontext4(ctx);
TAILQ_INSERT_TAIL(&newpm->pm_seglist, me, me_pmchain);
me->me_pmap = newpm;
me->me_vseg = newvseg;
me->me_vreg = newvreg;
return (me->me_cookie);
}
static inline void
me_free(struct pmap *pm, u_int pmeg)
{
struct mmuentry *me = &mmusegments[pmeg];
#ifdef DEBUG
struct regmap *rp;
int i, va, tpte, ctx;
#endif
#ifdef DEBUG
rp = &pm->pm_regmap[me->me_vreg];
DPRINTF(PDB_MMU_ALLOC,
"me_free: freeing pmeg %d from pmap %p", me->me_cookie, pm);
if (me->me_cookie != pmeg)
panic("me_free: wrong mmuentry");
if (pm != me->me_pmap)
panic("me_free: pm != me_pmap");
if (rp->rg_segmap[me->me_vseg].sg_pmeg != pmeg &&
rp->rg_segmap[me->me_vseg].sg_pmeg != seginval)
panic("me_free: wrong sg_pmeg (%d != %d)",
rp->rg_segmap[me->me_vseg].sg_pmeg, pmeg);
ctx = getcontext4();
setcontext4(0);
if (HASSUN4_MMU3L)
setregmap(0, tregion);
setsegmap(0, me->me_cookie);
va = 0;
i = NPTESG;
do {
tpte = getpte4(va);
if ((tpte & PG_V) == PG_V)
panic("me_free: segment not clean (pte=%x)", tpte);
va += NBPG;
} while (--i > 0);
setcontext4(ctx);
#endif
TAILQ_REMOVE(&pm->pm_seglist, me, me_pmchain);
mmuq_remove(me);
#ifdef DIAGNOSTIC
if (me->me_statp == NULL)
panic("me_statp");
(*me->me_statp)--;
me->me_statp = NULL;
#endif
me->me_pmap = NULL;
mmuq_insert_tail(&segm_freelist, me);
#ifdef DIAGNOSTIC
pmap_stats.ps_npmeg_free++;
#endif
}
#if defined(SUN4_MMU3L)
int
region_alloc(struct mmuentry *mh, struct pmap *newpm, int newvr)
{
struct mmuentry *me;
struct pmap *pm;
int ctx;
struct regmap *rp;
if (!mmuq_empty(®ion_freelist)) {
me = mmuq_first(®ion_freelist);
mmuq_remove(me);
#ifdef DEBUG
if (me->me_pmap != NULL)
panic("region_alloc: freelist entry has pmap");
DPRINTF(PDB_MMUREG_ALLOC,
"region_alloc: got smeg 0x%x", me->me_cookie);
#endif
mmuq_insert_tail(mh, me);
TAILQ_INSERT_TAIL(&newpm->pm_reglist, me, me_pmchain);
me->me_pmap = newpm;
me->me_vreg = newvr;
return (me->me_cookie);
}
if (mmuq_empty(®ion_lru))
panic("region_alloc: all smegs gone");
me = mmuq_first(®ion_lru);
pm = me->me_pmap;
if (pm == NULL)
panic("region_alloc: LRU entry has no pmap");
if (pm == pmap_kernel())
panic("region_alloc: stealing from kernel");
DPRINTF(PDB_MMUREG_ALLOC | PDB_MMUREG_STEAL,
"region_alloc: stealing smeg 0x%x from pmap %p",
me->me_cookie, pm);
mmuq_remove(me);
mmuq_insert_tail(mh, me);
rp = &pm->pm_regmap[me->me_vreg];
ctx = getcontext4();
write_user_windows();
if (pm->pm_ctx) {
setcontext4(pm->pm_ctxnum);
cache_flush_region(me->me_vreg, pm->pm_ctxnum);
}
if (pm->pm_ctx)
setregmap(VRTOVA(me->me_vreg), reginval);
rp->rg_smeg = reginval;
TAILQ_REMOVE(&pm->pm_reglist, me, me_pmchain);
setcontext4(ctx);
TAILQ_INSERT_TAIL(&newpm->pm_reglist, me, me_pmchain);
me->me_pmap = newpm;
me->me_vreg = newvr;
return (me->me_cookie);
}
void
region_free(struct pmap *pm, u_int smeg)
{
struct mmuentry *me = &mmuregions[smeg];
DPRINTF(PDB_MMUREG_ALLOC,
"region_free: freeing smeg 0x%x from pmap %p", me->me_cookie, pm);
#ifdef DEBUG
if (me->me_cookie != smeg)
panic("region_free: wrong mmuentry");
if (pm != me->me_pmap)
panic("region_free: pm != me_pmap");
#endif
TAILQ_REMOVE(&pm->pm_reglist, me, me_pmchain);
mmuq_remove(me);
me->me_pmap = NULL;
mmuq_insert_tail(®ion_freelist, me);
}
static void
mmu_pagein_reg(struct pmap *pm, struct regmap *rp, vaddr_t va,
int vr, struct mmuentry *mh)
{
int i, s, smeg;
va = VA_ROUNDDOWNTOREG(va);
rp->rg_smeg = smeg = region_alloc(mh, pm, vr);
s = splvm();
if (pm == pmap_kernel()) {
int ctx = getcontext4();
i = ncontext - 1;
do {
setcontext4(i);
setregmap(va, smeg);
} while (--i >= 0);
setcontext4(ctx);
} else
setregmap(va, smeg);
for (i = 0; i < NSEGRG; i++) {
setsegmap(va, rp->rg_segmap[i].sg_pmeg);
va += NBPSG;
}
splx(s);
}
#endif
static void
mmu_pmeg_lock(int pmeg)
{
struct mmuentry *me = &mmusegments[pmeg];
mmuq_remove(me);
mmuq_insert_tail(&segm_locked, me);
#ifdef DIAGNOSTIC
(*me->me_statp)--;
pmap_stats.ps_npmeg_locked++;
me->me_statp = &pmap_stats.ps_npmeg_locked;
#endif
}
static void
mmu_pmeg_unlock(int pmeg)
{
struct mmuentry *me = &mmusegments[pmeg];
mmuq_remove(me);
mmuq_insert_tail(&segm_lru, me);
#ifdef DIAGNOSTIC
(*me->me_statp)--;
pmap_stats.ps_npmeg_lru++;
me->me_statp = &pmap_stats.ps_npmeg_lru;
#endif
}
static void
mmu_pagein_seg(struct pmap *pm, struct segmap *sp, vaddr_t va,
int vr, int vs, struct mmuentry *mh)
{
int s, i, pmeg, *pte;
mmu_pagein_evcnt.ev_count++;
va = VA_ROUNDDOWNTOSEG(va);
s = splvm();
sp->sg_pmeg = pmeg = me_alloc(mh, pm, vr, vs);
if (pm != pmap_kernel() || HASSUN4_MMU3L)
setsegmap(va, pmeg);
else {
int ctx = getcontext4();
i = ncontext - 1;
do {
setcontext4(i);
setsegmap(va, pmeg);
} while (--i >= 0);
setcontext4(ctx);
}
pte = sp->sg_pte;
i = NPTESG;
do {
setpte4(va, *pte++ & ~PG_MBZ);
va += NBPG;
} while (--i > 0);
splx(s);
}
int
mmu_pagein(struct pmap *pm, vaddr_t va, int prot)
{
int vr, vs, bits;
struct regmap *rp;
struct segmap *sp;
PMAP_LOCK();
if (prot != VM_PROT_NONE)
bits = PG_V | ((prot & VM_PROT_WRITE) ? PG_W : 0);
else
bits = 0;
vr = VA_VREG(va);
vs = VA_VSEG(va);
rp = &pm->pm_regmap[vr];
if (rp->rg_nsegmap == 0) {
PMAP_UNLOCK();
return (0);
}
#ifdef DIAGNOSTIC
if (rp->rg_segmap == NULL)
panic("pagein: no segmap");
#endif
#if defined(SUN4_MMU3L)
if (HASSUN4_MMU3L && rp->rg_smeg == reginval)
mmu_pagein_reg(pm, rp, va, vr, ®ion_lru);
#endif
sp = &rp->rg_segmap[vs];
if (sp->sg_npte == 0) {
PMAP_UNLOCK();
return (0);
}
if (sp->sg_pmeg != seginval) {
PMAP_UNLOCK();
return (bits && (getpte4(va) & bits) == bits ? -1 : 0);
}
mmu_pagein_seg(pm, sp, va, vr, vs, &segm_lru);
PMAP_UNLOCK();
return (1);
}
#endif
static void
ctx_alloc(struct pmap *pm)
{
union ctxinfo *c;
int cnum, i = 0, doflush;
struct regmap *rp;
int gap_start, gap_end;
vaddr_t va;
#if defined(SUN4M) || defined(SUN4D)
struct cpu_info *cpi;
#endif
KASSERT(mutex_owned(&ctx_lock));
gap_start=gap_end=0;
#ifdef DEBUG
if (pm->pm_ctx)
panic("ctx_alloc pm_ctx");
#endif
DPRINTF(PDB_CTX_ALLOC,
"ctx_alloc[%d](%p)", cpu_number(), pm);
if (CPU_HAS_SUNMMU) {
gap_start = pm->pm_gap_start;
gap_end = pm->pm_gap_end;
}
if ((c = ctx_freelist) != NULL) {
ctx_freelist = c->c_nextfree;
cnum = c - ctxinfo;
doflush = 0;
} else {
if ((ctx_kick += ctx_kickdir) >= ncontext) {
ctx_kick = ncontext - 1;
ctx_kickdir = -1;
} else if (ctx_kick < 1) {
ctx_kick = 1;
ctx_kickdir = 1;
}
c = &ctxinfo[cnum = ctx_kick];
#ifdef DEBUG
if (c->c_pmap == NULL)
panic("ctx_alloc cu_pmap");
#endif
DPRINTF(PDB_CTX_ALLOC | PDB_CTX_STEAL,
"ctx_alloc[%d]: steal context %d from %p",
cpu_number(), cnum, c->c_pmap);
c->c_pmap->pm_ctx = NULL;
c->c_pmap->pm_ctxnum = 0;
doflush = (CACHEINFO.c_vactype != VAC_NONE);
if (CPU_HAS_SUNMMU) {
if (gap_start < c->c_pmap->pm_gap_start)
gap_start = c->c_pmap->pm_gap_start;
if (gap_end > c->c_pmap->pm_gap_end)
gap_end = c->c_pmap->pm_gap_end;
}
}
c->c_pmap = pm;
pm->pm_ctx = c;
pm->pm_ctxnum = cnum;
if (CPU_HAS_SUNMMU) {
setcontext4(cnum);
if (doflush)
cache_flush_context(cnum);
rp = pm->pm_regmap;
for (va = 0, i = NUREG; --i >= 0; ) {
if (VA_VREG(va) >= gap_start) {
va = VRTOVA(gap_end);
i -= gap_end - gap_start;
rp += gap_end - gap_start;
if (i < 0)
break;
gap_start = NUREG;
}
if (HASSUN4_MMU3L) {
setregmap(va, rp++->rg_smeg);
va += NBPRG;
} else {
int j;
struct segmap *sp = rp->rg_segmap;
for (j = NSEGRG; --j >= 0; va += NBPSG)
setsegmap(va,
sp?sp++->sg_pmeg:seginval);
rp++;
}
}
} else if (CPU_HAS_SRMMU) {
#if defined(SUN4M) || defined(SUN4D)
if (doflush) {
cache_flush_context(cnum);
}
for (CPU_INFO_FOREACH(i, cpi)) {
setpgt4m(&cpi->ctx_tbl[cnum],
(pm->pm_reg_ptps_pa[i] >> SRMMU_PPNPASHIFT) |
SRMMU_TEPTD);
}
(*cpuinfo.pure_vcache_flush)();
setcontext4m(cnum);
#endif
}
}
static void
ctx_free(struct pmap *pm)
{
union ctxinfo *c;
int ctx;
#if defined(SUN4M) || defined(SUN4D)
struct cpu_info *cpi;
#endif
KASSERT(mutex_owned(&ctx_lock));
c = pm->pm_ctx;
ctx = pm->pm_ctxnum;
pm->pm_ctx = NULL;
pm->pm_ctxnum = 0;
#if defined(SUN4) || defined(SUN4C)
if (CPU_HAS_SUNMMU) {
int octx = getcontext4();
setcontext4(ctx);
cache_flush_context(ctx);
setcontext4(octx);
}
#endif
#if defined(SUN4M) || defined(SUN4D)
if (CPU_HAS_SRMMU) {
CPU_INFO_ITERATOR i;
__USE(i);
cache_flush_context(ctx);
tlb_flush_context(ctx, PMAP_CPUSET(pm));
for (CPU_INFO_FOREACH(i, cpi)) {
setpgt4m(&cpi->ctx_tbl[ctx], SRMMU_TEINVALID);
}
}
#endif
c->c_nextfree = ctx_freelist;
ctx_freelist = c;
}
#if defined(SUN4) || defined(SUN4C)
void
pv_changepte4_4c(struct vm_page *pg, int bis, int bic)
{
int pte, *ptep;
struct pvlist *pv;
struct pmap *pm;
int va, vr, vs;
int ctx, s;
struct regmap *rp;
struct segmap *sp;
pv = VM_MDPAGE_PVHEAD(pg);
write_user_windows();
s = splvm();
if (pv->pv_pmap == NULL) {
splx(s);
return;
}
ctx = getcontext4();
for (; pv != NULL; pv = pv->pv_next) {
pm = pv->pv_pmap;
va = pv->pv_va;
vr = VA_VREG(va);
vs = VA_VSEG(va);
rp = &pm->pm_regmap[vr];
sp = &rp->rg_segmap[vs];
ptep = &sp->sg_pte[VA_VPG(va)];
if (sp->sg_pmeg == seginval) {
*ptep = (*ptep | bis) & ~bic;
} else {
if (CTX_USABLE(pm,rp)) {
setcontext4(pm->pm_ctxnum);
pte = getpte4(va);
cache_flush_page(va, pm->pm_ctxnum);
} else {
setcontext4(0);
if (HASSUN4_MMU3L)
setregmap(0, tregion);
setsegmap(0, sp->sg_pmeg);
va = VA_VPG(va) << PGSHIFT;
pte = getpte4(va);
}
if (pte & PG_V)
VM_MDPAGE_PVHEAD(pg)->pv_flags |= MR4_4C(pte);
pte = (pte | bis) & ~bic;
setpte4(va, pte);
*ptep = (*ptep & PG_MBZ) | pte;
}
}
setcontext4(ctx);
splx(s);
}
int
pv_syncflags4_4c(struct vm_page *pg)
{
struct pvlist *pv;
struct pmap *pm;
int pte, va, vr, vs, pmeg, flags;
int ctx, s;
struct regmap *rp;
struct segmap *sp;
pv = VM_MDPAGE_PVHEAD(pg);
s = splvm();
if (pv->pv_pmap == NULL) {
splx(s);
return (0);
}
ctx = getcontext4();
flags = pv->pv_flags;
for (; pv != NULL; pv = pv->pv_next) {
pm = pv->pv_pmap;
va = pv->pv_va;
vr = VA_VREG(va);
vs = VA_VSEG(va);
rp = &pm->pm_regmap[vr];
sp = &rp->rg_segmap[vs];
if ((pmeg = sp->sg_pmeg) == seginval)
continue;
if (CTX_USABLE(pm,rp)) {
setcontext4(pm->pm_ctxnum);
pte = getpte4(va);
if (pte & PG_M)
cache_flush_page(va, pm->pm_ctxnum);
} else {
setcontext4(0);
if (HASSUN4_MMU3L)
setregmap(0, tregion);
setsegmap(0, pmeg);
va = VA_VPG(va) << PGSHIFT;
pte = getpte4(va);
}
if (pte & (PG_M|PG_U) && pte & PG_V) {
flags |= MR4_4C(pte);
pte &= ~(PG_M|PG_U);
setpte4(va, pte);
}
}
VM_MDPAGE_PVHEAD(pg)->pv_flags = flags;
setcontext4(ctx);
splx(s);
return (flags);
}
void
pv_unlink4_4c(struct vm_page *pg, struct pmap *pm, vaddr_t va)
{
struct pvlist *pv0, *npv;
pv0 = VM_MDPAGE_PVHEAD(pg);
npv = pv0->pv_next;
if (pv0->pv_pmap == pm && pv0->pv_va == va) {
pmap_stats.ps_unlink_pvfirst++;
if (npv != NULL) {
pv0->pv_next = npv->pv_next;
pv0->pv_pmap = npv->pv_pmap;
pv0->pv_va = npv->pv_va;
pv0->pv_flags &= ~PV_NC;
pv0->pv_flags |= (npv->pv_flags & PV_NC);
pool_put(&pv_pool, npv);
} else {
pv0->pv_pmap = NULL;
pv0->pv_flags &= ~(PV_NC|PV_ANC);
return;
}
} else {
struct pvlist *prev;
pmap_stats.ps_unlink_pvsearch++;
for (prev = pv0;; prev = npv, npv = npv->pv_next) {
if (npv == NULL) {
panic("pv_unlink: pm %p is missing on pg %p",
pm, pg);
}
if (npv->pv_pmap == pm && npv->pv_va == va)
break;
}
prev->pv_next = npv->pv_next;
pool_put(&pv_pool, npv);
}
if ((pv0->pv_flags & (PV_NC|PV_ANC)) == PV_ANC) {
va = pv0->pv_va;
for (npv = pv0->pv_next; npv != NULL; npv = npv->pv_next)
if (BADALIAS(va, npv->pv_va) ||
(npv->pv_flags & PV_NC) != 0)
return;
pv0->pv_flags &= ~PV_ANC;
pv_changepte4_4c(pg, 0, PG_NC);
}
}
int
pv_link4_4c(struct vm_page *pg, struct pmap *pm, vaddr_t va,
unsigned int *pteprotop)
{
struct pvlist *pv0, *pv, *npv;
int nc = (*pteprotop & PG_NC) != 0 ? PV_NC : 0;
pv0 = VM_MDPAGE_PVHEAD(pg);
if (pv0->pv_pmap == NULL) {
pmap_stats.ps_enter_firstpv++;
pv0->pv_next = NULL;
pv0->pv_pmap = pm;
pv0->pv_va = va;
pv0->pv_flags |= nc;
return (0);
}
npv = pool_get(&pv_pool, PR_NOWAIT);
if (npv == NULL)
return (ENOMEM);
pmap_stats.ps_enter_secondpv++;
if (pv0->pv_flags & PV_ANC) {
*pteprotop |= PG_NC;
goto link_npv;
}
for (pv = pv0; pv != NULL; pv = pv->pv_next) {
if ((pv->pv_flags & PV_NC) != 0) {
*pteprotop |= PG_NC;
#ifdef DEBUG
if (nc == 0)
printf("pv_link: proc %s, va=0x%lx: "
"unexpected uncached mapping at 0x%lx\n",
curproc ? curproc->p_comm : "--",
va, pv->pv_va);
#endif
}
if (BADALIAS(va, pv->pv_va)) {
DPRINTF(PDB_CACHESTUFF,
"pv_link: badalias: proc %s, 0x%lx<=>0x%lx, pg %p",
curproc ? curproc->p_comm : "--",
va, pv->pv_va, pg);
pv0->pv_flags |= PV_ANC;
pv_changepte4_4c(pg, PG_NC, 0);
*pteprotop |= PG_NC;
break;
}
}
link_npv:
npv->pv_next = pv0->pv_next;
npv->pv_pmap = pm;
npv->pv_va = va;
npv->pv_flags = nc;
pv0->pv_next = npv;
return (0);
}
#endif
#if defined(SUN4M) || defined(SUN4D)
void
pv_changepte4m(struct vm_page *pg, int bis, int bic)
{
struct pvlist *pv;
struct pmap *pm;
vaddr_t va;
struct regmap *rp;
struct segmap *sp;
pv = VM_MDPAGE_PVHEAD(pg);
if (pv->pv_pmap == NULL)
return;
for (; pv != NULL; pv = pv->pv_next) {
pm = pv->pv_pmap;
va = pv->pv_va;
rp = &pm->pm_regmap[VA_VREG(va)];
sp = &rp->rg_segmap[VA_VSEG(va)];
if (pm->pm_ctx) {
cache_flush_page(va, pm->pm_ctxnum);
}
KASSERT((sp->sg_pte[VA_SUN4M_VPG(va)] & SRMMU_TETYPE) ==
SRMMU_TEPTE);
VM_MDPAGE_PVHEAD(pg)->pv_flags |= MR4M(updatepte4m(va,
&sp->sg_pte[VA_SUN4M_VPG(va)], bic, bis, pm->pm_ctxnum,
PMAP_CPUSET(pm)));
}
}
int
pv_syncflags4m(struct vm_page *pg)
{
struct pvlist *pv;
struct pmap *pm;
int va, flags;
int s;
struct regmap *rp;
struct segmap *sp;
int tpte;
s = splvm();
PMAP_LOCK();
pv = VM_MDPAGE_PVHEAD(pg);
if (pv->pv_pmap == NULL) {
flags = 0;
goto out;
}
flags = pv->pv_flags;
for (; pv != NULL; pv = pv->pv_next) {
pm = pv->pv_pmap;
va = pv->pv_va;
rp = &pm->pm_regmap[VA_VREG(va)];
sp = &rp->rg_segmap[VA_VSEG(va)];
tpte = sp->sg_pte[VA_SUN4M_VPG(va)];
if ((tpte & SRMMU_TETYPE) == SRMMU_TEPTE &&
(tpte & (SRMMU_PG_R|SRMMU_PG_M)) != 0) {
if (pm->pm_ctx && (tpte & SRMMU_PG_M) == SRMMU_PG_M)
cache_flush_page(va, pm->pm_ctxnum);
flags |= MR4M(updatepte4m(va,
&sp->sg_pte[VA_SUN4M_VPG(va)],
SRMMU_PG_M | SRMMU_PG_R,
0, pm->pm_ctxnum, PMAP_CPUSET(pm)));
}
}
VM_MDPAGE_PVHEAD(pg)->pv_flags = flags;
out:
PMAP_UNLOCK();
splx(s);
return (flags);
}
void
pv_unlink4m(struct vm_page *pg, struct pmap *pm, vaddr_t va)
{
struct pvlist *pv0, *npv;
pv0 = VM_MDPAGE_PVHEAD(pg);
npv = pv0->pv_next;
if (pv0->pv_pmap == pm && pv0->pv_va == va) {
pmap_stats.ps_unlink_pvfirst++;
if (npv != NULL) {
pv0->pv_next = npv->pv_next;
pv0->pv_pmap = npv->pv_pmap;
pv0->pv_va = npv->pv_va;
pv0->pv_flags &= ~PV_NC;
pv0->pv_flags |= (npv->pv_flags & PV_NC);
pool_put(&pv_pool, npv);
} else {
pv0->pv_pmap = NULL;
pv0->pv_flags &= ~(PV_NC|PV_ANC);
return;
}
} else {
struct pvlist *prev;
pmap_stats.ps_unlink_pvsearch++;
for (prev = pv0;; prev = npv, npv = npv->pv_next) {
if (npv == NULL) {
panic("pv_unlink: pm %p is missing on pg %p",
pm, pg);
return;
}
if (npv->pv_pmap == pm && npv->pv_va == va)
break;
}
prev->pv_next = npv->pv_next;
pool_put(&pv_pool, npv);
}
if ((pv0->pv_flags & (PV_NC|PV_ANC)) == PV_ANC) {
va = pv0->pv_va;
for (npv = pv0->pv_next; npv != NULL; npv = npv->pv_next)
if (BADALIAS(va, npv->pv_va) ||
(npv->pv_flags & PV_NC) != 0)
return;
DPRINTF(PDB_CACHESTUFF,
"pv_unlink: alias ok: proc %s, va 0x%lx, pg %p",
curproc ? curproc->p_comm : "--", va, pg);
pv0->pv_flags &= ~PV_ANC;
pv_changepte4m(pg, SRMMU_PG_C, 0);
}
}
int
pv_link4m(struct vm_page *pg, struct pmap *pm, vaddr_t va,
unsigned int *pteprotop)
{
struct pvlist *pv0, *pv, *npv;
int nc = (*pteprotop & SRMMU_PG_C) == 0 ? PV_NC : 0;
int error = 0;
pv0 = VM_MDPAGE_PVHEAD(pg);
if (pv0->pv_pmap == NULL) {
pmap_stats.ps_enter_firstpv++;
pv0->pv_next = NULL;
pv0->pv_pmap = pm;
pv0->pv_va = va;
pv0->pv_flags |= nc;
goto out;
}
npv = pool_get(&pv_pool, PR_NOWAIT);
if (npv == NULL) {
error = ENOMEM;
goto out;
}
pmap_stats.ps_enter_secondpv++;
if ((pv0->pv_flags & PV_ANC) != 0) {
*pteprotop &= ~SRMMU_PG_C;
goto link_npv;
}
for (pv = pv0; pv != NULL; pv = pv->pv_next) {
if ((pv->pv_flags & PV_NC) != 0) {
*pteprotop &= ~SRMMU_PG_C;
#ifdef DEBUG
if (nc == 0)
printf("pv_link: proc %s, va=0x%lx: "
"unexpected uncached mapping at 0x%lx\n",
curproc ? curproc->p_comm : "--",
va, pv->pv_va);
#endif
}
if (BADALIAS(va, pv->pv_va)) {
DPRINTF(PDB_CACHESTUFF,
"pv_link: badalias: proc %s, 0x%lx<=>0x%lx, pg %p",
curproc ? curproc->p_comm : "--",
va, pv->pv_va, pg);
pv0->pv_flags |= PV_ANC;
pv_changepte4m(pg, 0, SRMMU_PG_C);
*pteprotop &= ~SRMMU_PG_C;
break;
}
}
link_npv:
npv->pv_next = pv0->pv_next;
npv->pv_pmap = pm;
npv->pv_va = va;
npv->pv_flags = nc;
pv0->pv_next = npv;
out:
return (error);
}
#endif
static void
pv_uncache(struct vm_page *pg)
{
struct pvlist *pv;
int s;
s = splvm();
PMAP_LOCK();
for (pv = VM_MDPAGE_PVHEAD(pg); pv != NULL; pv = pv->pv_next)
pv->pv_flags |= PV_NC;
#if defined(SUN4M) || defined(SUN4D)
if (CPU_HAS_SRMMU)
pv_changepte4m(pg, 0, SRMMU_PG_C);
#endif
#if defined(SUN4) || defined(SUN4C)
if (CPU_HAS_SUNMMU)
pv_changepte4_4c(pg, PG_NC, 0);
#endif
PMAP_UNLOCK();
splx(s);
}
#if defined(SUN4) || defined(SUN4C)
static void
pv_flushcache4_4c(struct vm_page *pg)
{
struct pvlist *pv;
struct pmap *pm;
int s, ctx;
pv = VM_MDPAGE_PVHEAD(pg);
write_user_windows();
s = splvm();
if ((pm = pv->pv_pmap) != NULL) {
ctx = getcontext4();
for (;;) {
if (pm->pm_ctx) {
setcontext4(pm->pm_ctxnum);
cache_flush_page(pv->pv_va, pm->pm_ctxnum);
}
pv = pv->pv_next;
if (pv == NULL)
break;
pm = pv->pv_pmap;
}
setcontext4(ctx);
}
splx(s);
}
#endif
#if defined(SUN4M) || defined(SUN4D)
static void
pv_flushcache4m(struct vm_page *pg)
{
struct pvlist *pv;
struct pmap *pm;
int s;
pv = VM_MDPAGE_PVHEAD(pg);
s = splvm();
if ((pm = pv->pv_pmap) != NULL) {
for (;;) {
if (pm->pm_ctx) {
cache_flush_page(pv->pv_va, pm->pm_ctxnum);
}
pv = pv->pv_next;
if (pv == NULL)
break;
pm = pv->pv_pmap;
}
}
splx(s);
}
#endif
#if defined(SUN4) && (defined(SUN4C) || defined(SUN4M) || defined(SUN4D))
int nptesg;
#endif
#if defined(SUN4M) || defined(SUN4D)
static void pmap_bootstrap4m(void *);
#endif
#if defined(SUN4) || defined(SUN4C)
static void pmap_bootstrap4_4c(void *, int, int, int);
#endif
void
pmap_bootstrap(int nctx, int nregion, int nsegment)
{
void *p;
uvmexp.pagesize = NBPG;
uvm_md_init();
#if defined(SUN4) && (defined(SUN4C) || defined(SUN4M) || defined(SUN4D))
nptesg = (NBPSG >> pgshift);
#endif
p = kernel_top;
get_phys_mem(&p);
etext_gap_start = (vaddr_t)(etext + NBPG - 1) & ~PGOFSET;
etext_gap_end = (vaddr_t)kernel_data_start & ~PGOFSET;
if (CPU_HAS_SRMMU) {
#if defined(SUN4M) || defined(SUN4D)
pmap_bootstrap4m(p);
#endif
} else if (CPU_HAS_SUNMMU) {
#if defined(SUN4) || defined(SUN4C)
pmap_bootstrap4_4c(p, nctx, nregion, nsegment);
#endif
}
pmap_page_upload();
mutex_init(&pmap_lock, MUTEX_DEFAULT, IPL_NONE);
mutex_init(&demap_lock, MUTEX_DEFAULT, IPL_VM);
mutex_init(&ctx_lock, MUTEX_DEFAULT, IPL_SCHED);
lock_available = true;
}
#if defined(SUN4) || defined(SUN4C)
void
pmap_bootstrap4_4c(void *top, int nctx, int nregion, int nsegment)
{
union ctxinfo *ci;
struct mmuentry *mmuseg;
#if defined(SUN4_MMU3L)
struct mmuentry *mmureg;
#endif
struct regmap *rp;
struct segmap *sp;
int i, j;
int npte, zseg, vr, vs;
int startscookie, scookie;
#if defined(SUN4_MMU3L)
int startrcookie = 0, rcookie = 0;
#endif
int *kptes;
int lastpage;
vaddr_t va;
vaddr_t p;
va2pa_offset = (vaddr_t)kernel_text -
((getpte4(kernel_text) & PG_PFNUM) << PGSHIFT);
ncontext = nctx;
switch (cputyp) {
case CPU_SUN4C:
mmu_has_hole = 1;
break;
case CPU_SUN4:
if (cpuinfo.cpu_type != CPUTYP_4_400) {
mmu_has_hole = 1;
break;
}
}
#if defined(SUN4)
segfixmask = nsegment - 1;
#ifdef DIAGNOSTIC
if (((nsegment & segfixmask) | (nsegment & ~segfixmask)) != nsegment) {
printf("pmap_bootstrap: unsuitable number of segments (%d)\n",
nsegment);
callrom();
}
#endif
#endif
#if defined(SUN4M) || defined(SUN4D)
pmap_clear_modify_p = pmap_clear_modify4_4c;
pmap_clear_reference_p = pmap_clear_reference4_4c;
pmap_enter_p = pmap_enter4_4c;
pmap_extract_p = pmap_extract4_4c;
pmap_is_modified_p = pmap_is_modified4_4c;
pmap_is_referenced_p = pmap_is_referenced4_4c;
pmap_kenter_pa_p = pmap_kenter_pa4_4c;
pmap_kremove_p = pmap_kremove4_4c;
pmap_kprotect_p = pmap_kprotect4_4c;
pmap_page_protect_p = pmap_page_protect4_4c;
pmap_protect_p = pmap_protect4_4c;
pmap_rmk_p = pmap_rmk4_4c;
pmap_rmu_p = pmap_rmu4_4c;
#endif
p = (vaddr_t)top;
seginval = --nsegment;
#if defined(SUN4_MMU3L)
if (HASSUN4_MMU3L)
reginval = --nregion;
#endif
#if defined(SUN4_MMU3L)
mmuregions = mmureg = (struct mmuentry *)p;
p += nregion * sizeof(struct mmuentry);
memset(mmuregions, 0, nregion * sizeof(struct mmuentry));
#endif
mmusegments = mmuseg = (struct mmuentry *)p;
p += nsegment * sizeof(struct mmuentry);
memset(mmusegments, 0, nsegment * sizeof(struct mmuentry));
pmap_kernel()->pm_ctx = ctxinfo = ci = (union ctxinfo *)p;
p += nctx * sizeof *ci;
#if defined(SUN4_MMU3L)
mmuq_init(®ion_freelist);
mmuq_init(®ion_lru);
mmuq_init(®ion_locked);
#endif
mmuq_init(&segm_freelist);
mmuq_init(&segm_lru);
mmuq_init(&segm_locked);
kernel_pmap_store.pm_refcount = 1;
#if defined(SUN4_MMU3L)
TAILQ_INIT(&kernel_pmap_store.pm_reglist);
#endif
TAILQ_INIT(&kernel_pmap_store.pm_seglist);
kptes = (int *)p;
p += NKREG * NSEGRG * NPTESG * sizeof(int);
memset(kptes, 0, NKREG * NSEGRG * NPTESG * sizeof(int));
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
kernel_pmap_store.pm_regmap = kernel_regmap_store - NUREG;
#pragma GCC diagnostic pop
for (i = NKREG; --i >= 0;) {
#if defined(SUN4_MMU3L)
kernel_regmap_store[i].rg_smeg = reginval;
#endif
kernel_regmap_store[i].rg_segmap =
&kernel_segmap_store[i * NSEGRG];
for (j = NSEGRG; --j >= 0;) {
sp = &kernel_segmap_store[i * NSEGRG + j];
sp->sg_pmeg = seginval;
sp->sg_pte = &kptes[(i * NSEGRG + j) * NPTESG];
}
}
mmu_reservemon4_4c(&nregion, &nsegment);
#if defined(SUN4_MMU3L)
if (HASSUN4_MMU3L)
tregion = --nregion;
#endif
p = (p + NBPG - 1) & ~PGOFSET;
avail_start = PMAP_BOOTSTRAP_VA2PA(p);
i = p;
cpuinfo.vpage[0] = (void *)p, p += NBPG;
cpuinfo.vpage[1] = (void *)p, p += NBPG;
p = (vaddr_t)reserve_dumppages((void *)p);
virtual_avail = p;
virtual_end = VM_MAX_KERNEL_ADDRESS;
p = i;
ci->c_pmap = pmap_kernel();
ctx_freelist = ci + 1;
for (i = 1; i < ncontext; i++) {
ci++;
ci->c_nextfree = ci + 1;
}
ci->c_nextfree = NULL;
ctx_kick = 0;
ctx_kickdir = -1;
zseg = (((p + NBPSG - 1) & ~SGOFSET) - KERNBASE) >> SGSHIFT;
lastpage = VA_VPG(p);
if (lastpage == 0)
lastpage = NPTESG;
p = KERNBASE;
vs = VA_VSEG(KERNBASE);
vr = VA_VREG(KERNBASE);
rp = &pmap_kernel()->pm_regmap[vr];
#if defined(SUN4_MMU3L)
if (HASSUN4_MMU3L)
startrcookie = rcookie = getregmap(p);
mmureg = &mmuregions[rcookie];
#endif
startscookie = scookie = getsegmap(p);
mmuseg = &mmusegments[scookie];
zseg += scookie;
for (;;) {
if ((vs % NSEGRG) == 0) {
if (VA_VREG(p) > vr) {
#ifdef DEBUG
printf("note: giant kernel!\n");
#endif
vr++, rp++;
}
#if defined(SUN4_MMU3L)
if (HASSUN4_MMU3L) {
for (i = 1; i < nctx; i++)
prom_setcontext(i, (void *)p, rcookie);
mmuq_insert_tail(®ion_locked,
mmureg);
TAILQ_INSERT_TAIL(&pmap_kernel()->pm_reglist,
mmureg, me_pmchain);
#ifdef DIAGNOSTIC
mmuseg->me_statp = NULL;
#endif
mmureg->me_cookie = rcookie;
mmureg->me_pmap = pmap_kernel();
mmureg->me_vreg = vr;
rp->rg_smeg = rcookie;
mmureg++;
rcookie++;
}
#endif
}
#if defined(SUN4_MMU3L)
if (!HASSUN4_MMU3L)
#endif
for (i = 1; i < nctx; i++)
prom_setcontext(i, (void *)p, scookie);
mmuq_insert_tail(&segm_locked, mmuseg);
#ifdef DIAGNOSTIC
mmuseg->me_statp = &pmap_stats.ps_npmeg_locked;
#endif
TAILQ_INSERT_TAIL(&pmap_kernel()->pm_seglist, mmuseg, me_pmchain);
pmap_stats.ps_npmeg_locked++;
mmuseg->me_cookie = scookie;
mmuseg->me_pmap = pmap_kernel();
mmuseg->me_vreg = vr;
mmuseg->me_vseg = vs % NSEGRG;
sp = &rp->rg_segmap[vs % NSEGRG];
sp->sg_pmeg = scookie;
npte = ++scookie < zseg ? NPTESG : lastpage;
sp->sg_npte = npte;
sp->sg_nwired = npte;
pmap_kernel()->pm_stats.resident_count += npte;
rp->rg_nsegmap += 1;
for (i = 0; i < npte; i++)
sp->sg_pte[i] = getpte4(p + i * NBPG) | PG_WIRED;
mmuseg++;
vs++;
if (scookie < zseg) {
p += NBPSG;
continue;
}
for (p += npte << PGSHIFT; npte < NPTESG; npte++, p += NBPG)
setpte4(p, 0);
#if defined(SUN4_MMU3L)
if (HASSUN4_MMU3L) {
for (i = rp->rg_nsegmap; i < NSEGRG; i++, p += NBPSG)
setsegmap(p, seginval);
for (i = 0; i < nctx; i++) {
setcontext4(i);
for (va = p;
va < (OPENPROM_STARTVADDR & ~(NBPRG - 1));
va += NBPRG)
setregmap(va, reginval);
}
} else
#endif
{
for (i = 0; i < nctx; i++) {
setcontext4(i);
for (va = p;
va < (OPENPROM_STARTVADDR & ~(NBPSG - 1));
va += NBPSG)
setsegmap(va, seginval);
}
}
break;
}
#if defined(SUN4_MMU3L)
if (HASSUN4_MMU3L)
for (rcookie = 0; rcookie < nregion; rcookie++) {
if (rcookie == startrcookie)
rcookie++;
mmureg = &mmuregions[rcookie];
mmureg->me_cookie = rcookie;
mmuq_insert_tail(®ion_freelist, mmureg);
#ifdef DIAGNOSTIC
mmuseg->me_statp = NULL;
#endif
}
#endif
for (scookie = 0; scookie < nsegment; scookie++) {
if (scookie == startscookie)
scookie = zseg;
mmuseg = &mmusegments[scookie];
mmuseg->me_cookie = scookie;
mmuq_insert_tail(&segm_freelist, mmuseg);
pmap_stats.ps_npmeg_free++;
#ifdef DIAGNOSTIC
mmuseg->me_statp = NULL;
#endif
}
for (i = 1; i < ncontext; i++) {
setcontext4(i);
if (HASSUN4_MMU3L)
for (p = 0, j = NUREG; --j >= 0; p += NBPRG)
setregmap(p, reginval);
else
for (p = 0, vr = 0; vr < NUREG; vr++) {
if (VA_INHOLE(p)) {
p = MMU_HOLE_END;
vr = VA_VREG(p);
}
for (j = NSEGRG; --j >= 0; p += NBPSG)
setsegmap(p, seginval);
}
}
setcontext4(0);
for (p = KERNBASE; p < (vaddr_t)trapbase; p += NBPG)
setpte4(p, getpte4(p) & ~PG_NC);
for (p = (vaddr_t)trapbase; p < (vaddr_t)etext; p += NBPG)
setpte4(p, getpte4(p) & ~(PG_NC|PG_W));
for (p = etext_gap_start; p < etext_gap_end; p += NBPG) {
rp = &pmap_kernel()->pm_regmap[VA_VREG(p)];
sp = &rp->rg_segmap[VA_VSEG(p)];
sp->sg_nwired--;
sp->sg_npte--;
pmap_kernel()->pm_stats.resident_count--;
sp->sg_pte[VA_VPG(p)] = 0;
setpte4(p, 0);
}
for (p = etext_gap_end; p < virtual_avail; p += NBPG)
setpte4(p, getpte4(p) & ~PG_NC);
cpus[0] = (struct cpu_info *)CPUINFO_VA;
}
#endif
#if defined(SUN4M) || defined(SUN4D)
static void
pmap_bootstrap4m(void *top)
{
int i, j;
vaddr_t p, q;
union ctxinfo *ci;
int reg, seg;
unsigned int ctxtblsize;
vaddr_t pagetables_start, pagetables_end;
paddr_t pagetables_start_pa;
vaddr_t va;
#if defined(MULTIPROCESSOR)
vsize_t off;
size_t cpuinfo_len = sizeof(struct cpu_info);
uint8_t *cpuinfo_data;
int align = PAGE_SIZE;
vaddr_t sva, cpuinfo_va;
vsize_t sz;
#endif
va2pa_offset = (vaddr_t)kernel_text - VA2PA(kernel_text);
ncontext = cpuinfo.mmu_ncontext;
#if defined(SUN4) || defined(SUN4C)
pmap_clear_modify_p = pmap_clear_modify4m;
pmap_clear_reference_p = pmap_clear_reference4m;
pmap_enter_p = pmap_enter4m;
pmap_extract_p = pmap_extract4m;
pmap_is_modified_p = pmap_is_modified4m;
pmap_is_referenced_p = pmap_is_referenced4m;
pmap_kenter_pa_p = pmap_kenter_pa4m;
pmap_kremove_p = pmap_kremove4m;
pmap_kprotect_p = pmap_kprotect4m;
pmap_page_protect_p = pmap_page_protect4m;
pmap_protect_p = pmap_protect4m;
pmap_rmk_p = pmap_rmk4m;
pmap_rmu_p = pmap_rmu4m;
#endif
p = (vaddr_t)top;
p = (p + NBPG - 1) & ~PGOFSET;
DPRINTF(PDB_INITLOUD, "initial p=%lx", p);
kernel_pmap_store.pm_refcount = 1;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
kernel_pmap_store.pm_regmap = kernel_regmap_store - NUREG;
#pragma GCC diagnostic pop
memset(kernel_regmap_store, 0, sizeof kernel_regmap_store);
memset(kernel_segmap_store, 0, sizeof kernel_segmap_store);
for (i = NKREG; --i >= 0;) {
kernel_regmap_store[i].rg_segmap =
&kernel_segmap_store[i * NSEGRG];
kernel_regmap_store[i].rg_seg_ptps = NULL;
for (j = NSEGRG; --j >= 0;)
kernel_segmap_store[i * NSEGRG + j].sg_pte = NULL;
}
pmap_kernel()->pm_reg_ptps = (int **)(q = p);
DPRINTF(PDB_INITLOUD, "kernel region pointer tables p=%lx", p);
p += sparc_ncpus * sizeof(int **);
memset((void *)q, 0, (u_int)p - (u_int)q);
pmap_kernel()->pm_reg_ptps_pa = (int *)(q = p);
DPRINTF(PDB_INITLOUD, "kernel region pointer tables pa p=%lx", p);
p += sparc_ncpus * sizeof(int *);
memset((void *)q, 0, (u_int)p - (u_int)q);
pmap_kernel()->pm_ctx = ctxinfo = ci = (union ctxinfo *)p;
DPRINTF(PDB_INITLOUD, "context administration p=%lx", p);
p += ncontext * sizeof *ci;
memset((void *)ci, 0, (u_int)p - (u_int)ci);
p = (p + NBPG - 1) & ~PGOFSET;
DPRINTF(PDB_INITLOUD, "align p=%lx", p);
pagetables_start = p;
pagetables_start_pa = PMAP_BOOTSTRAP_VA2PA(p);
ctxtblsize = uimax(ncontext,1024) * sizeof(int);
cpuinfo.ctx_tbl = (int *)roundup((u_int)p, ctxtblsize);
cpuinfo.ctx_tbl_pa = PMAP_BOOTSTRAP_VA2PA(cpuinfo.ctx_tbl);
p = (u_int)cpuinfo.ctx_tbl + ctxtblsize;
DPRINTF(PDB_INITLOUD, "post ctx table p=%lx", p);
#if defined(MULTIPROCESSOR)
pmap_kernel()->pm_cpuset = CPUSET_ALL;
#endif
p = (vaddr_t) roundup(p, SRMMU_L1SIZE * sizeof(u_int));
DPRINTF(PDB_INITLOUD, "roundup kernel_regtable_store p=%lx", p);
qzero((void *)p, SRMMU_L1SIZE * sizeof(u_int));
kernel_regtable_store = (u_int *)p;
p += SRMMU_L1SIZE * sizeof(u_int);
DPRINTF(PDB_INITLOUD, "L1 pages p=%lx", p);
p = (vaddr_t) roundup(p, SRMMU_L2SIZE * sizeof(u_int));
DPRINTF(PDB_INITLOUD, "roundup kernel_segtable_store p=%lx", p);
qzero((void *)p, (SRMMU_L2SIZE * sizeof(u_int)) * NKREG);
kernel_segtable_store = (u_int *)p;
p += (SRMMU_L2SIZE * sizeof(u_int)) * NKREG;
DPRINTF(PDB_INITLOUD, "L2 pages p=%lx", p);
p = (vaddr_t) roundup(p, SRMMU_L3SIZE * sizeof(u_int));
DPRINTF(PDB_INITLOUD, "roundup kernel_pagtable_store p=%lx", p);
qzero((void *)p, ((SRMMU_L3SIZE * sizeof(u_int)) * NSEGRG) * NKREG);
kernel_pagtable_store = (u_int *)p;
p += ((SRMMU_L3SIZE * sizeof(u_int)) * NSEGRG) * NKREG;
DPRINTF(PDB_INITLOUD, "L3 pages p=%lx", p);
p = (p + NBPG - 1) & ~PGOFSET;
DPRINTF(PDB_INITLOUD, "align p=%lx", p);
pagetables_end = p;
#if defined(MULTIPROCESSOR)
sz = sizeof(struct cpu_info);
if (sparc_ncpus > 1) {
if (CACHEINFO.c_totalsize > align) {
while (align <= CACHEINFO.c_totalsize)
align <<= 1;
align >>= 1;
}
sz = (sz + PAGE_SIZE - 1) & -PAGE_SIZE;
cpuinfo_len = sz + align - PAGE_SIZE;
DPRINTF(PDB_INITLOUD, "cpuinfo=%lx", p);
cpuinfo_data = (uint8_t *)p;
p += (cpuinfo_len * sparc_ncpus);
} else
cpuinfo_data = (uint8_t *)CPUINFO_VA;
#endif
DPRINTF(PDB_INITLOUD, "avail_start=%lx", p);
avail_start = PMAP_BOOTSTRAP_VA2PA(p);
pmap_kernel()->pm_reg_ptps[0] = (int *) kernel_regtable_store;
pmap_kernel()->pm_reg_ptps_pa[0] =
PMAP_BOOTSTRAP_VA2PA(kernel_regtable_store);
setpgt4m(&cpuinfo.ctx_tbl[0],
(pmap_kernel()->pm_reg_ptps_pa[0] >> SRMMU_PPNPASHIFT) | SRMMU_TEPTD);
for (reg = 0; reg < NKREG; reg++) {
struct regmap *rp;
void *kphyssegtbl;
rp = &pmap_kernel()->pm_regmap[reg + VA_VREG(KERNBASE)];
kphyssegtbl = (void *)
&kernel_segtable_store[reg * SRMMU_L2SIZE];
setpgt4m(&pmap_kernel()->pm_reg_ptps[0][reg + VA_VREG(KERNBASE)],
(PMAP_BOOTSTRAP_VA2PA(kphyssegtbl) >> SRMMU_PPNPASHIFT) |
SRMMU_TEPTD);
rp->rg_seg_ptps = (int *)kphyssegtbl;
for (seg = 0; seg < NSEGRG; seg++) {
struct segmap *sp;
void *kphyspagtbl;
rp->rg_nsegmap++;
sp = &rp->rg_segmap[seg];
kphyspagtbl = (void *)
&kernel_pagtable_store
[((reg * NSEGRG) + seg) * SRMMU_L3SIZE];
setpgt4m(&rp->rg_seg_ptps[seg],
(PMAP_BOOTSTRAP_VA2PA(kphyspagtbl) >> SRMMU_PPNPASHIFT) |
SRMMU_TEPTD);
sp->sg_pte = (int *) kphyspagtbl;
}
}
mmu_reservemon4m(&kernel_pmap_store);
q = p;
cpuinfo.vpage[0] = (void *)p, p += NBPG;
cpuinfo.vpage[1] = (void *)p, p += NBPG;
p = (vaddr_t)reserve_dumppages((void *)p);
for (i = 0; i < 2; i++) {
struct regmap *rp;
struct segmap *sp;
rp = &pmap_kernel()->pm_regmap[VA_VREG(cpuinfo.vpage[i])];
sp = &rp->rg_segmap[VA_VSEG(cpuinfo.vpage[i])];
cpuinfo.vpage_pte[i] =
&sp->sg_pte[VA_SUN4M_VPG(cpuinfo.vpage[i])];
}
#if !(defined(PROM_AT_F0) || defined(MSIIEP))
virtual_avail = p;
#elif defined(MSIIEP)
virtual_avail = (vaddr_t)0xf0800000;
#else
virtual_avail = (vaddr_t)0xf0080000;
#endif
virtual_end = VM_MAX_KERNEL_ADDRESS;
p = q;
ci->c_pmap = pmap_kernel();
ctx_freelist = ci + 1;
for (i = 1; i < ncontext; i++) {
ci++;
ci->c_nextfree = ci + 1;
}
ci->c_nextfree = NULL;
ctx_kick = 0;
ctx_kickdir = -1;
#ifdef DEBUG
if (p % NBPG != 0)
panic("pmap_bootstrap4m: p misaligned?!?");
if (KERNBASE % NBPRG != 0)
panic("pmap_bootstrap4m: KERNBASE not region-aligned");
#endif
for (q = KERNBASE; q < p; q += NBPG) {
struct regmap *rp;
struct segmap *sp;
int pte, *ptep;
rp = &pmap_kernel()->pm_regmap[VA_VREG(q)];
sp = &rp->rg_segmap[VA_VSEG(q)];
ptep = &sp->sg_pte[VA_VPG(q)];
if (q >= etext_gap_start && q < etext_gap_end) {
setpgt4m(ptep, 0);
continue;
}
pte = PMAP_BOOTSTRAP_VA2PA(q) >> SRMMU_PPNPASHIFT;
pte |= PPROT_N_RX | SRMMU_TEPTE;
if ((CACHEINFO.c_flags & CACHE_PAGETABLES) != 0 ||
q < pagetables_start || q >= pagetables_end)
pte |= SRMMU_PG_C;
if (q < (vaddr_t)trapbase || q >= (vaddr_t)etext)
pte |= PPROT_WRITE;
setpgt4m(ptep, pte);
pmap_kernel()->pm_stats.resident_count++;
}
if ((CACHEINFO.c_flags & CACHE_PAGETABLES) == 0) {
int size = pagetables_end - pagetables_start;
if (CACHEINFO.c_vactype != VAC_NONE) {
va = (vaddr_t)pagetables_start;
while (size > 0) {
cache_flush_page(va, 0);
va += NBPG;
size -= NBPG;
}
} else if (cpuinfo.pcache_flush_page != NULL) {
paddr_t pa = pagetables_start_pa;
while (size > 0) {
pcache_flush_page(pa, 0);
pa += NBPG;
size -= NBPG;
}
}
}
mmu_install_tables(&cpuinfo);
#if defined(MULTIPROCESSOR)
cpu_init_system();
for (i = 0; i < sparc_ncpus; i++) {
sva = (vaddr_t) (cpuinfo_data + (cpuinfo_len * i));
cpuinfo_va = sva +
(((CPUINFO_VA & (align - 1)) + align - sva) & (align - 1));
if (i == 0) {
for (off = 0, va = cpuinfo_va;
sparc_ncpus > 1 && off < sizeof(struct cpu_info);
va += NBPG, off += NBPG) {
paddr_t pa =
PMAP_BOOTSTRAP_VA2PA(CPUINFO_VA + off);
pmap_kremove(va, NBPG);
pmap_kenter_pa(va, pa,
VM_PROT_READ | VM_PROT_WRITE, 0);
}
} else
memset((void *)cpuinfo_va, 0, sizeof(struct cpu_info));
cpus[i] = (struct cpu_info *)cpuinfo_va;
cpus[i]->ci_self = cpus[i];
if (cpuinfo_va != sva) {
cpus[i]->ci_free_sva1 = sva;
cpus[i]->ci_free_eva1 = cpuinfo_va;
for (va = cpus[i]->ci_free_sva1;
va < cpus[i]->ci_free_eva1;
va += NBPG)
setpte4m(va, 0);
}
if (cpuinfo_va + sz != sva + cpuinfo_len) {
cpus[i]->ci_free_sva2 = cpuinfo_va + sz;
cpus[i]->ci_free_eva2 = sva + cpuinfo_len;
for (va = cpus[i]->ci_free_sva2;
va < cpus[i]->ci_free_eva2;
va += NBPG)
setpte4m(va, 0);
}
}
#else
cpus[0] = (struct cpu_info *)CPUINFO_VA;
#endif
pmap_update(pmap_kernel());
#ifdef DIAGNOSTIC
if (curcpu()->ci_self != cpus[0]) {
prom_printf("curcpu()->ci_self %p != cpus[0] %p\n", curcpu()->ci_self, cpus[0]);
panic("cpuinfo inconsistent");
}
#endif
}
static u_long prom_ctxreg;
void
mmu_install_tables(struct cpu_info *sc)
{
#ifdef DEBUG
prom_printf("pmap_bootstrap: installing kernel page tables...");
#endif
setcontext4m(0);
if (sc->mmu_enable != 0)
sc->mmu_enable();
tlb_flush_all_real();
prom_ctxreg = lda(SRMMU_CXTPTR, ASI_SRMMU);
sta(SRMMU_CXTPTR, ASI_SRMMU,
(sc->ctx_tbl_pa >> SRMMU_PPNPASHIFT) & ~0x3);
tlb_flush_all_real();
#ifdef DEBUG
prom_printf("done.\n");
#endif
}
void srmmu_restore_prom_ctx(void);
void
srmmu_restore_prom_ctx(void)
{
tlb_flush_all();
sta(SRMMU_CXTPTR, ASI_SRMMU, prom_ctxreg);
tlb_flush_all();
}
#endif
#if defined(MULTIPROCESSOR)
void
pmap_alloc_cpu(struct cpu_info *sc)
{
#if defined(SUN4M) || defined(SUN4D)
vaddr_t va;
paddr_t pa;
paddr_t alignment;
u_int *ctxtable, *regtable, *segtable, *pagtable;
u_int *ctxtable_pa, *regtable_pa, *segtable_pa, *pagtable_pa;
psize_t ctxsize, size;
int vr, vs, vpg;
struct regmap *rp;
struct segmap *sp;
struct pglist mlist;
int cachebit;
int pagesz = NBPG;
int i;
cachebit = (CACHEINFO.c_flags & CACHE_PAGETABLES) != 0;
ctxsize = (sc->mmu_ncontext * sizeof(int) + pagesz - 1) & -pagesz;
alignment = ctxsize;
size = ctxsize + pagesz;
if (uvm_pglistalloc(size, vm_first_phys, vm_first_phys+vm_num_phys,
alignment, 0, &mlist, 1, 0) != 0)
panic("pmap_alloc_cpu: no memory");
pa = VM_PAGE_TO_PHYS(TAILQ_FIRST(&mlist));
va = uvm_km_alloc(kernel_map, size, 0, UVM_KMF_VAONLY);
if (va == 0)
panic("pmap_alloc_cpu: no memory");
ctxtable = (u_int *)va;
regtable = (u_int *)(va + ctxsize);
segtable = regtable + SRMMU_L1SIZE;
pagtable = segtable + SRMMU_L2SIZE;
ctxtable_pa = (u_int *)pa;
regtable_pa = (u_int *)(pa + ctxsize);
segtable_pa = regtable_pa + SRMMU_L1SIZE;
pagtable_pa = segtable_pa + SRMMU_L2SIZE;
while (size != 0) {
pmap_kenter_pa(va, pa | (cachebit ? 0 : PMAP_NC),
VM_PROT_READ | VM_PROT_WRITE, 0);
va += pagesz;
pa += pagesz;
size -= pagesz;
}
pmap_update(pmap_kernel());
pmap_kernel()->pm_reg_ptps[sc->ci_cpuid] = regtable;
pmap_kernel()->pm_reg_ptps_pa[sc->ci_cpuid] = (paddr_t)regtable_pa;
vr = VA_VREG(CPUINFO_VA);
vs = VA_VSEG(CPUINFO_VA);
vpg = VA_VPG(CPUINFO_VA);
rp = &pmap_kernel()->pm_regmap[vr];
sp = &rp->rg_segmap[vs];
qcopy(pmap_kernel()->pm_reg_ptps[0], regtable,
SRMMU_L1SIZE * sizeof(int));
qcopy(rp->rg_seg_ptps, segtable, SRMMU_L2SIZE * sizeof(int));
qcopy(sp->sg_pte, pagtable, SRMMU_L3SIZE * sizeof(int));
setpgt4m(&ctxtable[0],
((u_long)regtable_pa >> SRMMU_PPNPASHIFT) | SRMMU_TEPTD);
setpgt4m(®table[vr],
((u_long)segtable_pa >> SRMMU_PPNPASHIFT) | SRMMU_TEPTD);
setpgt4m(&segtable[vs],
((u_long)pagtable_pa >> SRMMU_PPNPASHIFT) | SRMMU_TEPTD);
setpgt4m(&pagtable[vpg],
(VA2PA((void *)sc) >> SRMMU_PPNPASHIFT) |
(SRMMU_TEPTE | PPROT_N_RWX | SRMMU_PG_C));
sc->ctx_tbl = ctxtable;
sc->ctx_tbl_pa = (paddr_t)ctxtable_pa;
for (i = 0; i < 2; i++) {
rp = &pmap_kernel()->pm_regmap[VA_VREG(sc->vpage[i])];
sp = &rp->rg_segmap[VA_VSEG(sc->vpage[i])];
sc->vpage_pte[i] = &sp->sg_pte[VA_SUN4M_VPG(sc->vpage[i])];
}
#endif
}
#endif
void
pmap_init(void)
{
u_int sz;
if (PAGE_SIZE != NBPG)
panic("pmap_init: PAGE_SIZE!=NBPG");
vm_num_phys = vm_last_phys - vm_first_phys;
pool_init(&pv_pool, sizeof(struct pvlist), 0, 0, 0, "pvtable", NULL,
IPL_NONE);
sz = ALIGN(sizeof(struct pmap)) +
ALIGN(NUREG * sizeof(struct regmap)) +
sparc_ncpus * sizeof(int *) +
sparc_ncpus * sizeof(int);
pool_cache_bootstrap(&pmap_cache, sz, 0, 0, 0, "pmappl", NULL,
IPL_NONE, pmap_pmap_pool_ctor, pmap_pmap_pool_dtor, NULL);
sz = NSEGRG * sizeof (struct segmap);
pool_init(&segmap_pool, sz, 0, 0, 0, "segmap", NULL, IPL_NONE);
#if defined(SUN4M) || defined(SUN4D)
if (CPU_HAS_SRMMU) {
sz = SRMMU_L1SIZE * sizeof(int);
pool_init(&L1_pool, sz, sz, 0, 0, "L1 pagetable",
&pgt_page_allocator, IPL_NONE);
sz = SRMMU_L2SIZE * sizeof(int);
pool_init(&L23_pool, sz, sz, 0, 0, "L2/L3 pagetable",
&pgt_page_allocator, IPL_NONE);
}
#endif
#if defined(SUN4) || defined(SUN4C)
if (CPU_HAS_SUNMMU) {
sz = NPTESG * sizeof(int);
pool_init(&pte_pool, sz, 0, 0, 0, "ptemap", NULL,
IPL_NONE);
}
#endif
}
vaddr_t
pmap_map(vaddr_t va, paddr_t pa, paddr_t endpa, int prot)
{
int pgsize = PAGE_SIZE;
while (pa < endpa) {
pmap_kenter_pa(va, pa, prot, 0);
va += pgsize;
pa += pgsize;
}
pmap_update(pmap_kernel());
return (va);
}
static inline void
pmap_quiet_check(struct pmap *pm)
{
#ifdef DEBUG
int vs, vr;
if (CPU_HAS_SUNMMU) {
#if defined(SUN4_MMU3L)
if (TAILQ_FIRST(&pm->pm_reglist))
panic("pmap_destroy: region list not empty");
#endif
if (TAILQ_FIRST(&pm->pm_seglist))
panic("pmap_destroy: segment list not empty");
}
for (vr = 0; vr < NUREG; vr++) {
struct regmap *rp = &pm->pm_regmap[vr];
if (HASSUN4_MMU3L) {
if (rp->rg_smeg != reginval)
printf("pmap_chk: spurious smeg in "
"user region %d\n", vr);
}
if (CPU_HAS_SRMMU) {
int n;
#if defined(MULTIPROCESSOR)
for (n = 0; n < sparc_ncpus; n++)
#else
n = 0;
#endif
{
if (pmap_kernel()->pm_reg_ptps[n] == 0)
continue;
if (pm->pm_reg_ptps[n][vr] != SRMMU_TEINVALID)
printf("pmap_chk: spurious PTP in user "
"region %d on CPU %d\n", vr, n);
}
}
if (rp->rg_nsegmap != 0)
printf("pmap_chk: %d segments remain in "
"region %d\n", rp->rg_nsegmap, vr);
if (rp->rg_segmap != NULL) {
printf("pmap_chk: segments still "
"allocated in region %d\n", vr);
for (vs = 0; vs < NSEGRG; vs++) {
struct segmap *sp = &rp->rg_segmap[vs];
if (sp->sg_npte != 0)
printf("pmap_chk: %d ptes "
"remain in segment %d\n",
sp->sg_npte, vs);
if (sp->sg_pte != NULL) {
printf("pmap_chk: ptes still "
"allocated in segment %d\n", vs);
}
if (CPU_HAS_SUNMMU) {
if (sp->sg_pmeg != seginval)
printf("pmap_chk: pm %p(%d,%d) "
"spurious soft pmeg %d\n",
pm, vr, vs, sp->sg_pmeg);
}
}
}
if (pm->pm_ctx == NULL)
continue;
if (CPU_HAS_SUNMMU) {
int ctx;
if (mmu_has_hole && (vr >= 32 && vr < (256 - 32)))
continue;
ctx = getcontext4();
setcontext4(pm->pm_ctxnum);
for (vs = 0; vs < NSEGRG; vs++) {
vaddr_t va = VSTOVA(vr,vs);
int pmeg = getsegmap(va);
if (pmeg != seginval)
printf("pmap_chk: pm %p(%d,%d:%x): "
"spurious pmeg %d\n",
pm, vr, vs, (u_int)va, pmeg);
}
setcontext4(ctx);
}
}
if (pm->pm_stats.resident_count) {
printf("pmap_chk: res count %ld\n",
pm->pm_stats.resident_count);
}
if (pm->pm_stats.wired_count) {
printf("pmap_chk: wired count %ld\n",
pm->pm_stats.wired_count);
}
#endif
}
int
pmap_pmap_pool_ctor(void *arg, void *object, int flags)
{
struct pmap *pm = object;
u_long addr;
memset(pm, 0, sizeof *pm);
addr = (u_long)pm + ALIGN(sizeof(struct pmap));
pm->pm_regmap = (void *)addr;
addr += ALIGN(NUREG * sizeof(struct regmap));
pm->pm_reg_ptps = (int **)addr;
addr += sparc_ncpus * sizeof(int *);
pm->pm_reg_ptps_pa = (int *)addr;
qzero((void *)pm->pm_regmap, NUREG * sizeof(struct regmap));
if (CPU_HAS_SUNMMU) {
TAILQ_INIT(&pm->pm_seglist);
#if defined(SUN4_MMU3L)
TAILQ_INIT(&pm->pm_reglist);
if (HASSUN4_MMU3L) {
int i;
for (i = NUREG; --i >= 0;)
pm->pm_regmap[i].rg_smeg = reginval;
}
#endif
}
#if defined(SUN4M) || defined(SUN4D)
else {
int i, n;
#if defined(MULTIPROCESSOR)
for (n = 0; n < sparc_ncpus; n++)
#else
n = 0;
#endif
{
int *upt, *kpt;
#if defined(MULTIPROCESSOR)
if (pmap_kernel()->pm_reg_ptps[n] == 0)
continue;
#endif
upt = pool_get(&L1_pool, flags);
pm->pm_reg_ptps[n] = upt;
pm->pm_reg_ptps_pa[n] = VA2PA((char *)upt);
for (i = 0; i < NUREG; i++)
setpgt4m(upt++, SRMMU_TEINVALID);
kpt = &pmap_kernel()->pm_reg_ptps[n][VA_VREG(KERNBASE)];
for (i = 0; i < NKREG; i++)
setpgt4m(upt++, kpt[i]);
}
}
#endif
{static int x; if (x == 0) pool_setlowat(&pv_pool, 512), x = 1; }
return (0);
}
void
pmap_pmap_pool_dtor(void *arg, void *object)
{
struct pmap *pm = object;
union ctxinfo *c;
int s = splvm();
DPRINTF(PDB_DESTROY, "pmap_pmap_pool_dtor(%p)", pm);
if ((c = pm->pm_ctx) != NULL) {
mutex_spin_enter(&ctx_lock);
ctx_free(pm);
mutex_spin_exit(&ctx_lock);
}
#if defined(SUN4M) || defined(SUN4D)
if (CPU_HAS_SRMMU) {
int n;
#if defined(MULTIPROCESSOR)
for (n = 0; n < sparc_ncpus; n++)
#else
n = 0;
#endif
{
int *pt;
#if defined(MULTIPROCESSOR)
if (pmap_kernel()->pm_reg_ptps[n] == 0)
continue;
#endif
pt = pm->pm_reg_ptps[n];
pm->pm_reg_ptps[n] = NULL;
pm->pm_reg_ptps_pa[n] = 0;
pool_put(&L1_pool, pt);
}
}
#endif
splx(s);
}
struct pmap *
pmap_create(void)
{
struct pmap *pm;
pm = pool_cache_get(&pmap_cache, PR_WAITOK);
pm->pm_refcount = 1;
#if defined(MULTIPROCESSOR)
pm->pm_cpuset = 0;
#endif
if (CPU_HAS_SUNMMU) {
pm->pm_gap_start = 0;
pm->pm_gap_end = VA_VREG(VM_MAXUSER_ADDRESS);
}
DPRINTF(PDB_CREATE, "pmap_create[%d]: created %p", cpu_number(), pm);
pmap_quiet_check(pm);
return (pm);
}
void
pmap_destroy(struct pmap *pm)
{
DPRINTF(PDB_DESTROY, "pmap_destroy[%d](%p)", cpu_number(), pm);
membar_release();
if (atomic_dec_uint_nv(&pm->pm_refcount) == 0) {
membar_acquire();
pmap_quiet_check(pm);
pool_cache_put(&pmap_cache, pm);
}
}
void
pmap_reference(struct pmap *pm)
{
atomic_inc_uint(&pm->pm_refcount);
}
#if defined(SUN4) || defined(SUN4C)
static void
pgt_lvl23_remove4_4c(struct pmap *pm, struct regmap *rp, struct segmap *sp,
int vr, int vs)
{
vaddr_t va, tva;
int i, pmeg;
va = VSTOVA(vr,vs);
if ((pmeg = sp->sg_pmeg) != seginval) {
if (CTX_USABLE(pm,rp)) {
setcontext4(pm->pm_ctxnum);
setsegmap(va, seginval);
} else {
setcontext4(0);
if (HASSUN4_MMU3L && rp->rg_smeg != reginval) {
setregmap(0, rp->rg_smeg);
tva = vs << SGSHIFT;
setsegmap(tva, seginval);
}
}
if (!HASSUN4_MMU3L) {
if (pm == pmap_kernel()) {
for (i = ncontext; --i >= 0;) {
setcontext4(i);
setsegmap(va, seginval);
}
}
}
me_free(pm, pmeg);
sp->sg_pmeg = seginval;
}
if (pm != pmap_kernel()) {
pool_put(&pte_pool, sp->sg_pte);
sp->sg_pte = NULL;
}
if (rp->rg_nsegmap <= 0)
panic("pgt_rm: pm %p: nsegmap = %d\n", pm, rp->rg_nsegmap);
if (--rp->rg_nsegmap == 0) {
#if defined(SUN4_MMU3L)
if (HASSUN4_MMU3L) {
if (rp->rg_smeg != reginval) {
if (pm == pmap_kernel()) {
for (i = ncontext; --i >= 0;) {
setcontext4(i);
setregmap(va, reginval);
}
} else if (pm->pm_ctx) {
setcontext4(pm->pm_ctxnum);
setregmap(va, reginval);
}
region_free(pm, rp->rg_smeg);
rp->rg_smeg = reginval;
}
}
#endif
if (pm != pmap_kernel()) {
GAP_WIDEN(pm,vr);
pool_put(&segmap_pool, rp->rg_segmap);
rp->rg_segmap = NULL;
}
}
}
#endif
#if defined(SUN4M) || defined(SUN4D)
static void
pgt_lvl23_remove4m(struct pmap *pm, struct regmap *rp, struct segmap *sp,
int vr, int vs)
{
if (pm->pm_ctx)
tlb_flush_segment(VSTOVA(vr,vs), pm->pm_ctxnum,
PMAP_CPUSET(pm));
setpgt4m(&rp->rg_seg_ptps[vs], SRMMU_TEINVALID);
pool_put(&L23_pool, sp->sg_pte);
sp->sg_pte = NULL;
if (--rp->rg_nsegmap == 0) {
int n = 0;
if (pm->pm_ctx)
tlb_flush_region(VRTOVA(vr), pm->pm_ctxnum,
PMAP_CPUSET(pm));
#if defined(MULTIPROCESSOR)
for (; n < sparc_ncpus; n++) {
if ((cpus[n]->flags & CPUFLG_HATCHED) == 0)
continue;
#endif
setpgt4m(&pm->pm_reg_ptps[n][vr], SRMMU_TEINVALID);
#if defined(MULTIPROCESSOR)
}
#endif
pool_put(&segmap_pool, rp->rg_segmap);
rp->rg_segmap = NULL;
pool_put(&L23_pool, rp->rg_seg_ptps);
}
}
#endif
bool
pmap_remove_all(struct pmap *pm)
{
if (pm->pm_ctx == NULL)
return false;
#if defined(SUN4) || defined(SUN4C)
if (CPU_HAS_SUNMMU) {
int ctx = getcontext4();
setcontext4(pm->pm_ctxnum);
cache_flush_context(pm->pm_ctxnum);
setcontext4(ctx);
}
#endif
#if defined(SUN4M) || defined(SUN4D)
if (CPU_HAS_SRMMU) {
cache_flush_context(pm->pm_ctxnum);
}
#endif
pm->pm_flags |= PMAP_USERCACHECLEAN;
return false;
}
void
pmap_remove(struct pmap *pm, vaddr_t va, vaddr_t endva)
{
vaddr_t nva;
int vr, vs, s, ctx;
void (*rm)(struct pmap *, vaddr_t, vaddr_t, int, int);
DPRINTF(PDB_REMOVE, "pmap_remove[%d](%p, 0x%lx, 0x%lx)",
cpu_number(), pm, va, endva);
if (!CPU_HAS_SRMMU)
write_user_windows();
if (pm == pmap_kernel()) {
rm = pmap_rmk;
} else {
rm = pmap_rmu;
}
ctx = getcontext();
s = splvm();
PMAP_LOCK();
for (; va < endva; va = nva) {
vr = VA_VREG(va);
vs = VA_VSEG(va);
nva = VSTOVA(vr, vs + 1);
if (nva == 0 || nva > endva)
nva = endva;
if (pm->pm_regmap[vr].rg_nsegmap != 0)
(*rm)(pm, va, nva, vr, vs);
}
PMAP_UNLOCK();
splx(s);
setcontext(ctx);
}
#define PMAP_SFL_THRESHOLD 16
#if defined(SUN4) || defined(SUN4C)
void
pmap_rmk4_4c(struct pmap *pm, vaddr_t va, vaddr_t endva, int vr, int vs)
{
int pte, mmupte, *ptep, perpage, npg;
struct vm_page *pg;
int nleft, pmeg, inmmu;
struct regmap *rp;
struct segmap *sp;
rp = &pm->pm_regmap[vr];
sp = &rp->rg_segmap[vs];
if (rp->rg_nsegmap == 0)
return;
if ((nleft = sp->sg_npte) == 0)
return;
pmeg = sp->sg_pmeg;
inmmu = pmeg != seginval;
ptep = &sp->sg_pte[VA_VPG(va)];
npg = (endva - va) >> PGSHIFT;
if (!inmmu) {
perpage = 0;
} else if (npg > PMAP_SFL_THRESHOLD) {
perpage = 0;
cache_flush_segment(vr, vs, 0);
} else {
perpage = (CACHEINFO.c_vactype != VAC_NONE);
}
for (; va < endva; va += NBPG, ptep++) {
pte = *ptep;
mmupte = inmmu ? getpte4(va) : 0;
if ((pte & PG_V) == 0) {
#ifdef DIAGNOSTIC
if (inmmu && (mmupte & PG_V) != 0)
printf("rmk: inconsistent ptes va=%lx\n", va);
#endif
continue;
}
if ((pte & PG_TYPE) == PG_OBMEM) {
if (perpage && (mmupte & PG_NC) == 0)
cache_flush_page(va, 0);
if ((pg = pvhead4_4c(pte)) != NULL) {
if (inmmu)
VM_MDPAGE_PVHEAD(pg)->pv_flags |= MR4_4C(mmupte);
pv_unlink4_4c(pg, pm, va);
}
}
nleft--;
#ifdef DIAGNOSTIC
if (nleft < 0)
panic("pmap_rmk: too many PTEs in segment; "
"va 0x%lx; endva 0x%lx", va, endva);
#endif
if (pte & PG_WIRED) {
sp->sg_nwired--;
pm->pm_stats.wired_count--;
}
if (inmmu)
setpte4(va, 0);
*ptep = 0;
pm->pm_stats.resident_count--;
}
#ifdef DIAGNOSTIC
if (sp->sg_nwired > nleft || sp->sg_nwired < 0)
panic("pmap_rmk: pm %p, va %lx: nleft=%d, nwired=%d",
pm, va, nleft, sp->sg_nwired);
#endif
if ((sp->sg_npte = nleft) == 0)
pgt_lvl23_remove4_4c(pm, rp, sp, vr, vs);
else if (sp->sg_nwired == 0) {
if (sp->sg_pmeg != seginval)
mmu_pmeg_unlock(sp->sg_pmeg);
}
}
#endif
#if defined(SUN4M) || defined(SUN4D)
void
pmap_rmk4m(struct pmap *pm, vaddr_t va, vaddr_t endva, int vr, int vs)
{
int tpte, perpage, npg;
struct vm_page *pg;
struct regmap *rp;
struct segmap *sp;
rp = &pm->pm_regmap[vr];
sp = &rp->rg_segmap[vs];
if (rp->rg_nsegmap == 0)
return;
npg = (endva - va) >> PGSHIFT;
if (npg > PMAP_SFL_THRESHOLD) {
perpage = 0;
if (CACHEINFO.c_vactype != VAC_NONE)
cache_flush_segment(vr, vs, 0);
} else {
perpage = (CACHEINFO.c_vactype != VAC_NONE);
}
while (va < endva) {
tpte = sp->sg_pte[VA_SUN4M_VPG(va)];
if ((tpte & SRMMU_TETYPE) != SRMMU_TEPTE) {
#ifdef DEBUG
if ((pmapdebug & PDB_SANITYCHK) &&
(getpte4m(va) & SRMMU_TETYPE) == SRMMU_TEPTE)
panic("pmap_rmk: Spurious kTLB entry for 0x%lx",
va);
#endif
va += NBPG;
continue;
}
if ((tpte & SRMMU_PGTYPE) == PG_SUN4M_OBMEM) {
if (perpage && (tpte & SRMMU_PG_C))
cache_flush_page(va, 0);
if ((pg = pvhead4m(tpte)) != NULL) {
VM_MDPAGE_PVHEAD(pg)->pv_flags |= MR4M(tpte);
pv_unlink4m(pg, pm, va);
}
}
setpgt4m_va(va, &sp->sg_pte[VA_SUN4M_VPG(va)],
SRMMU_TEINVALID, 1, 0, CPUSET_ALL);
pm->pm_stats.resident_count--;
va += NBPG;
}
}
#endif
#if defined(SUN4) || defined(SUN4C)
void
pmap_rmu4_4c(struct pmap *pm, vaddr_t va, vaddr_t endva, int vr, int vs)
{
int *ptep, pteva, pte, perpage, npg;
struct vm_page *pg;
int nleft, pmeg, inmmu;
struct regmap *rp;
struct segmap *sp;
rp = &pm->pm_regmap[vr];
if (rp->rg_nsegmap == 0)
return;
sp = &rp->rg_segmap[vs];
if ((nleft = sp->sg_npte) == 0)
return;
pmeg = sp->sg_pmeg;
inmmu = pmeg != seginval;
if (!inmmu) {
perpage = 0;
pteva = 0;
} else if (CTX_USABLE(pm,rp)) {
npg = (endva - va) >> PGSHIFT;
setcontext4(pm->pm_ctxnum);
if ((pm->pm_flags & PMAP_USERCACHECLEAN) != 0)
perpage = 0;
else if (npg > PMAP_SFL_THRESHOLD) {
perpage = 0;
cache_flush_segment(vr, vs, pm->pm_ctxnum);
} else
perpage = (CACHEINFO.c_vactype != VAC_NONE);
pteva = va;
} else {
setcontext4(0);
if (HASSUN4_MMU3L)
setregmap(0, tregion);
setsegmap(0, pmeg);
pteva = VA_VPG(va) << PGSHIFT;
perpage = 0;
}
ptep = sp->sg_pte + VA_VPG(va);
for (; va < endva; ptep++, pteva += NBPG, va += NBPG) {
int mmupte;
pte = *ptep;
mmupte = inmmu ? getpte4(pteva) : 0;
if ((pte & PG_V) == 0) {
#ifdef DIAGNOSTIC
if (inmmu && (mmupte & PG_V) != 0)
printf("pmap_rmu: pte=%x, mmupte=%x\n",
pte, getpte4(pteva));
#endif
continue;
}
if ((pte & PG_TYPE) == PG_OBMEM) {
if (perpage && (mmupte & PG_NC) == 0)
cache_flush_page(va, pm->pm_ctxnum);
if ((pg = pvhead4_4c(pte)) != NULL) {
if (inmmu)
VM_MDPAGE_PVHEAD(pg)->pv_flags |= MR4_4C(mmupte);
pv_unlink4_4c(pg, pm, va);
}
}
nleft--;
#ifdef DIAGNOSTIC
if (nleft < 0)
panic("pmap_rmu: too many PTEs in segment; "
"va 0x%lx; endva 0x%lx", va, endva);
#endif
if (inmmu)
setpte4(pteva, 0);
if (pte & PG_WIRED) {
sp->sg_nwired--;
pm->pm_stats.wired_count--;
}
*ptep = 0;
pm->pm_stats.resident_count--;
}
#ifdef DIAGNOSTIC
if (sp->sg_nwired > nleft || sp->sg_nwired < 0)
panic("pmap_rmu: pm %p, va %lx: nleft=%d, nwired=%d",
pm, va, nleft, sp->sg_nwired);
#endif
if ((sp->sg_npte = nleft) == 0)
pgt_lvl23_remove4_4c(pm, rp, sp, vr, vs);
else if (sp->sg_nwired == 0) {
if (sp->sg_pmeg != seginval)
mmu_pmeg_unlock(sp->sg_pmeg);
}
}
#endif
#if defined(SUN4M) || defined(SUN4D)
void
pmap_rmu4m(struct pmap *pm, vaddr_t va, vaddr_t endva, int vr, int vs)
{
int *pte0, perpage, npg;
struct vm_page *pg;
int nleft;
struct regmap *rp;
struct segmap *sp;
rp = &pm->pm_regmap[vr];
if (rp->rg_nsegmap == 0)
return;
sp = &rp->rg_segmap[vs];
if ((nleft = sp->sg_npte) == 0)
return;
pte0 = sp->sg_pte;
if (pm->pm_ctx && (pm->pm_flags & PMAP_USERCACHECLEAN) == 0) {
if (CACHEINFO.c_vactype != VAC_NONE) {
npg = (endva - va) >> PGSHIFT;
if (npg > PMAP_SFL_THRESHOLD) {
perpage = 0;
cache_flush_segment(vr, vs, pm->pm_ctxnum);
} else
perpage = 1;
} else
perpage = 0;
} else {
perpage = 0;
}
for (; va < endva; va += NBPG) {
int tpte;
tpte = pte0[VA_SUN4M_VPG(va)];
if ((tpte & SRMMU_TETYPE) != SRMMU_TEPTE) {
#ifdef DEBUG
if ((pmapdebug & PDB_SANITYCHK) &&
pm->pm_ctx &&
(getpte4m(va) & SRMMU_TEPTE) == SRMMU_TEPTE)
panic("pmap_rmu: Spurious uTLB entry for 0x%lx",
va);
#endif
continue;
}
if ((tpte & SRMMU_PGTYPE) == PG_SUN4M_OBMEM) {
if (perpage && (tpte & SRMMU_PG_C))
cache_flush_page(va, pm->pm_ctxnum);
if ((pg = pvhead4m(tpte)) != NULL) {
VM_MDPAGE_PVHEAD(pg)->pv_flags |= MR4M(tpte);
pv_unlink4m(pg, pm, va);
}
}
nleft--;
#ifdef DIAGNOSTIC
if (nleft < 0)
panic("pmap_rmu: too many PTEs in segment; "
"va 0x%lx; endva 0x%lx", va, endva);
#endif
setpgt4m_va(va, &pte0[VA_SUN4M_VPG(va)], SRMMU_TEINVALID,
pm->pm_ctx != NULL, pm->pm_ctxnum, PMAP_CPUSET(pm));
pm->pm_stats.resident_count--;
if (sp->sg_wiremap & (1 << VA_SUN4M_VPG(va))) {
sp->sg_wiremap &= ~(1 << VA_SUN4M_VPG(va));
pm->pm_stats.wired_count--;
}
}
if ((sp->sg_npte = nleft) == 0)
pgt_lvl23_remove4m(pm, rp, sp, vr, vs);
}
#endif
#if defined(SUN4) || defined(SUN4C)
void
pmap_page_protect4_4c(struct vm_page *pg, vm_prot_t prot)
{
struct pvlist *pv, *npv;
struct pmap *pm;
vaddr_t va;
int vr, vs, pteva, pte, *ptep;
int flags, nleft, s, ctx;
struct regmap *rp;
struct segmap *sp;
#ifdef DEBUG
if ((pmapdebug & PDB_CHANGEPROT) ||
(pmapdebug & PDB_REMOVE && prot == VM_PROT_NONE))
printf("pmap_page_protect(0x%lx, 0x%x)\n",
VM_PAGE_TO_PHYS(pg), prot);
#endif
if (prot & VM_PROT_WRITE)
return;
write_user_windows();
if (prot & VM_PROT_READ) {
pv_changepte4_4c(pg, 0, PG_W);
return;
}
s = splvm();
pv = VM_MDPAGE_PVHEAD(pg);
if (pv->pv_pmap == NULL) {
splx(s);
return;
}
ctx = getcontext4();
flags = pv->pv_flags & ~(PV_NC|PV_ANC);
while (pv != NULL) {
pm = pv->pv_pmap;
va = pv->pv_va;
vr = VA_VREG(va);
vs = VA_VSEG(va);
rp = &pm->pm_regmap[vr];
sp = &rp->rg_segmap[vs];
if ((nleft = sp->sg_npte) <= 0)
panic("pmap_page_protect: empty vseg");
sp->sg_npte = --nleft;
ptep = &sp->sg_pte[VA_VPG(va)];
if (*ptep & PG_WIRED) {
sp->sg_nwired--;
pm->pm_stats.wired_count--;
}
if (sp->sg_pmeg != seginval) {
if (CTX_USABLE(pm,rp)) {
setcontext4(pm->pm_ctxnum);
pteva = va;
cache_flush_page(va, pm->pm_ctxnum);
} else {
setcontext4(0);
if (HASSUN4_MMU3L)
setregmap(0, tregion);
setsegmap(0, sp->sg_pmeg);
pteva = VA_VPG(va) << PGSHIFT;
}
pte = getpte4(pteva);
#ifdef DIAGNOSTIC
if ((pte & PG_V) == 0)
panic("pmap_page_protect !PG_V: pg %p "
"ctx %d, va 0x%lx, pte 0x%x",
pg, pm->pm_ctxnum, va, pte);
#endif
flags |= MR4_4C(pte);
setpte4(pteva, 0);
#ifdef DIAGNOSTIC
if (sp->sg_nwired > nleft || sp->sg_nwired < 0)
panic("pmap_page_protect: pm %p, va %lx: nleft=%d, nwired=%d",
pm, va, nleft, sp->sg_nwired);
#endif
if (sp->sg_nwired == 0)
mmu_pmeg_unlock(sp->sg_pmeg);
}
*ptep = 0;
pm->pm_stats.resident_count--;
if (nleft == 0)
pgt_lvl23_remove4_4c(pm, rp, sp, vr, vs);
npv = pv->pv_next;
if (pv != VM_MDPAGE_PVHEAD(pg))
pool_put(&pv_pool, pv);
pv = npv;
}
VM_MDPAGE_PVHEAD(pg)->pv_pmap = NULL;
VM_MDPAGE_PVHEAD(pg)->pv_next = NULL;
VM_MDPAGE_PVHEAD(pg)->pv_flags = flags;
setcontext4(ctx);
splx(s);
}
void
pmap_protect4_4c(struct pmap *pm, vaddr_t sva, vaddr_t eva, vm_prot_t prot)
{
int va, nva, vr, vs;
int s, ctx;
struct regmap *rp;
struct segmap *sp;
if ((prot & VM_PROT_READ) == 0) {
pmap_remove(pm, sva, eva);
return;
}
write_user_windows();
ctx = getcontext4();
s = splvm();
PMAP_LOCK();
for (va = sva; va < eva;) {
vr = VA_VREG(va);
vs = VA_VSEG(va);
rp = &pm->pm_regmap[vr];
nva = VSTOVA(vr,vs + 1);
if (nva > eva)
nva = eva;
if (rp->rg_nsegmap == 0) {
va = nva;
continue;
}
#ifdef DEBUG
if (rp->rg_segmap == NULL)
panic("pmap_protect: no segments");
#endif
sp = &rp->rg_segmap[vs];
if (sp->sg_npte == 0) {
va = nva;
continue;
}
#ifdef DEBUG
if (sp->sg_pte == NULL)
panic("pmap_protect: no pages");
#endif
if (sp->sg_pmeg == seginval) {
int *ptep = &sp->sg_pte[VA_VPG(va)];
for (; va < nva; va += NBPG)
*ptep++ &= ~PG_W;
} else {
if (CTX_USABLE(pm,rp)) {
int pte;
pmap_stats.ps_npg_prot_all +=
(nva - va) >> PGSHIFT;
setcontext4(pm->pm_ctxnum);
for (; va < nva; va += NBPG) {
pte = getpte4(va);
if ((pte & (PG_W|PG_TYPE)) ==
(PG_W|PG_OBMEM)) {
pmap_stats.ps_npg_prot_actual++;
cache_flush_page(va, pm->pm_ctxnum);
setpte4(va, pte & ~PG_W);
}
}
} else {
int pteva;
setcontext4(0);
if (HASSUN4_MMU3L)
setregmap(0, tregion);
setsegmap(0, sp->sg_pmeg);
pteva = VA_VPG(va) << PGSHIFT;
for (; va < nva; pteva += NBPG, va += NBPG)
setpte4(pteva, getpte4(pteva) & ~PG_W);
}
}
}
PMAP_UNLOCK();
splx(s);
setcontext4(ctx);
}
void
pmap_changeprot4_4c(struct pmap *pm, vaddr_t va, vm_prot_t prot, int flags)
{
int vr, vs, newprot, ctx, pte, *ptep;
int pmeg;
struct regmap *rp;
struct segmap *sp;
DPRINTF(PDB_CHANGEPROT, "pmap_changeprot(%p, 0x%lx, 0x%x, 0x%x)",
pm, va, prot, flags);
if (pm == pmap_kernel())
newprot = prot & VM_PROT_WRITE ? PG_S|PG_W : PG_S;
else
newprot = prot & VM_PROT_WRITE ? PG_W : 0;
vr = VA_VREG(va);
vs = VA_VSEG(va);
rp = &pm->pm_regmap[vr];
sp = &rp->rg_segmap[vs];
ptep = &sp->sg_pte[VA_VPG(va)];
pmap_stats.ps_changeprots++;
pte = *ptep;
if (pte & PG_WIRED && (flags & PMAP_WIRED) == 0) {
pte &= ~PG_WIRED;
sp->sg_nwired--;
pm->pm_stats.wired_count--;
} else if ((pte & PG_WIRED) == 0 && flags & PMAP_WIRED) {
pte |= PG_WIRED;
sp->sg_nwired++;
pm->pm_stats.wired_count++;
}
pte = (pte & ~PG_PROT) | newprot;
*ptep = pte;
if ((pmeg = sp->sg_pmeg) != seginval) {
ctx = getcontext4();
if (CTX_USABLE(pm,rp)) {
setcontext4(pm->pm_ctxnum);
pte = getpte4(va);
if ((pte & (PG_U|PG_NC|PG_TYPE)) == (PG_U|PG_OBMEM))
cache_flush_page(va, pm->pm_ctxnum);
} else {
setcontext4(0);
if (HASSUN4_MMU3L)
setregmap(0, tregion);
setsegmap(0, pmeg);
va = VA_VPG(va) << PGSHIFT;
pte = getpte4(va);
}
pte = (pte & ~PG_PROT) | newprot;
setpte4(va, pte);
setcontext4(ctx);
#ifdef DIAGNOSTIC
if (sp->sg_nwired > sp->sg_npte || sp->sg_nwired < 0)
panic("pmap_protect: pm %p, va %lx: nleft=%d, nwired=%d",
pm, va, sp->sg_npte, sp->sg_nwired);
#endif
if (sp->sg_nwired == 0)
mmu_pmeg_unlock(pmeg);
else
mmu_pmeg_lock(pmeg);
}
}
#endif
#if defined(SUN4M) || defined(SUN4D)
void
pmap_page_protect4m(struct vm_page *pg, vm_prot_t prot)
{
struct pvlist *pv, *npv;
struct pmap *pm;
vaddr_t va;
int vr, vs, tpte;
int flags, nleft, s;
struct regmap *rp;
struct segmap *sp;
#ifdef DEBUG
if ((pmapdebug & PDB_CHANGEPROT) ||
(pmapdebug & PDB_REMOVE && prot == VM_PROT_NONE))
printf("pmap_page_protect[%d](0x%lx, 0x%x)\n",
cpu_number(), VM_PAGE_TO_PHYS(pg), prot);
#endif
s = splvm();
PMAP_LOCK();
if (prot & VM_PROT_READ) {
pv_changepte4m(pg, 0, PPROT_WRITE);
goto out;
}
pv = VM_MDPAGE_PVHEAD(pg);
if (pv->pv_pmap == NULL)
goto out;
flags = pv->pv_flags & ~(PV_NC|PV_ANC);
while (pv != NULL) {
pm = pv->pv_pmap;
va = pv->pv_va;
vr = VA_VREG(va);
vs = VA_VSEG(va);
rp = &pm->pm_regmap[vr];
if (rp->rg_nsegmap == 0)
panic("pmap_remove_all: empty vreg");
sp = &rp->rg_segmap[vs];
nleft = sp->sg_npte;
if (pm != pmap_kernel()) {
if (nleft <= 0)
panic("pmap_page_protect: empty vseg");
sp->sg_npte = --nleft;
}
if (pm->pm_ctx) {
cache_flush_page(va, pm->pm_ctxnum);
}
tpte = sp->sg_pte[VA_SUN4M_VPG(va)];
setpgt4m_va(va, &sp->sg_pte[VA_SUN4M_VPG(va)], SRMMU_TEINVALID,
pm->pm_ctx != NULL, pm->pm_ctxnum, PMAP_CPUSET(pm));
pm->pm_stats.resident_count--;
if (sp->sg_wiremap & (1 << VA_SUN4M_VPG(va))) {
sp->sg_wiremap &= ~(1 << VA_SUN4M_VPG(va));
pm->pm_stats.wired_count--;
}
if ((tpte & SRMMU_TETYPE) != SRMMU_TEPTE)
panic("pmap_page_protect !PG_V: pg %p va %lx", pg, va);
flags |= MR4M(tpte);
if (pm != pmap_kernel() && nleft == 0)
pgt_lvl23_remove4m(pm, rp, sp, vr, vs);
npv = pv->pv_next;
if (pv != VM_MDPAGE_PVHEAD(pg))
pool_put(&pv_pool, pv);
pv = npv;
}
VM_MDPAGE_PVHEAD(pg)->pv_pmap = NULL;
VM_MDPAGE_PVHEAD(pg)->pv_next = NULL;
VM_MDPAGE_PVHEAD(pg)->pv_flags = flags;
out:
PMAP_UNLOCK();
splx(s);
}
void
pmap_protect4m(struct pmap *pm, vaddr_t sva, vaddr_t eva, vm_prot_t prot)
{
vaddr_t va, nva;
int s, vr, vs;
struct regmap *rp;
struct segmap *sp;
int newprot;
if ((prot & VM_PROT_READ) == 0) {
pmap_remove(pm, sva, eva);
return;
}
DPRINTF(PDB_CHANGEPROT,
"pmap_protect[%d][curpid %d, ctx %d,%d](%lx, %lx, %x)",
cpu_number(), curproc->p_pid, getcontext4m(),
pm->pm_ctx ? pm->pm_ctxnum : -1, sva, eva, prot);
newprot = pte_prot4m(pm, prot);
write_user_windows();
s = splvm();
PMAP_LOCK();
for (va = sva; va < eva;) {
vr = VA_VREG(va);
vs = VA_VSEG(va);
rp = &pm->pm_regmap[vr];
nva = VSTOVA(vr,vs + 1);
if (nva > eva)
nva = eva;
if (rp->rg_nsegmap == 0) {
va = nva;
continue;
}
sp = &rp->rg_segmap[vs];
if (pm != pmap_kernel() && sp->sg_npte == 0) {
va = nva;
continue;
}
pmap_stats.ps_npg_prot_all += (nva - va) >> PGSHIFT;
for (; va < nva; va += NBPG) {
int tpte, npte;
tpte = sp->sg_pte[VA_SUN4M_VPG(va)];
if ((tpte & SRMMU_PGTYPE) != PG_SUN4M_OBMEM)
continue;
if ((tpte & SRMMU_TETYPE) != SRMMU_TEPTE)
continue;
npte = (tpte & ~SRMMU_PROT_MASK) | newprot;
if (npte == tpte)
continue;
pmap_stats.ps_npg_prot_actual++;
if (pm->pm_ctx) {
cache_flush_page(va, pm->pm_ctxnum);
}
updatepte4m(va, &sp->sg_pte[VA_SUN4M_VPG(va)],
SRMMU_PROT_MASK, newprot, pm->pm_ctxnum,
PMAP_CPUSET(pm));
}
}
PMAP_UNLOCK();
splx(s);
}
void
pmap_changeprot4m(struct pmap *pm, vaddr_t va, vm_prot_t prot, int flags)
{
int pte, newprot;
struct regmap *rp;
struct segmap *sp;
bool owired;
DPRINTF(PDB_CHANGEPROT, "pmap_changeprot[%d](%p, 0x%lx, 0x%x, 0x%x)",
cpu_number(), pm, va, prot, flags);
newprot = pte_prot4m(pm, prot);
pmap_stats.ps_changeprots++;
rp = &pm->pm_regmap[VA_VREG(va)];
sp = &rp->rg_segmap[VA_VSEG(va)];
pte = sp->sg_pte[VA_SUN4M_VPG(va)];
owired = sp->sg_wiremap & (1 << VA_SUN4M_VPG(va));
if (owired) {
pm->pm_stats.wired_count--;
sp->sg_wiremap &= ~(1 << VA_SUN4M_VPG(va));
}
if (flags & PMAP_WIRED) {
pm->pm_stats.wired_count++;
sp->sg_wiremap |= (1 << VA_SUN4M_VPG(va));
}
if (pm->pm_ctx) {
if ((pte & (SRMMU_PG_C|SRMMU_PGTYPE)) ==
(SRMMU_PG_C|PG_SUN4M_OBMEM))
cache_flush_page(va, pm->pm_ctxnum);
}
setpgt4m_va(va, &sp->sg_pte[VA_SUN4M_VPG(va)],
(pte & ~SRMMU_PROT_MASK) | newprot,
pm->pm_ctx != NULL, pm->pm_ctxnum, PMAP_CPUSET(pm));
}
#endif
#if defined(SUN4) || defined(SUN4C)
int
pmap_enter4_4c(struct pmap *pm, vaddr_t va, paddr_t pa,
vm_prot_t prot, u_int flags)
{
struct vm_page *pg;
int pteproto, ctx;
int error;
if (VA_INHOLE(va)) {
#ifdef DEBUG
printf("pmap_enter: pm %p, va 0x%lx, pa 0x%lx: in MMU hole\n",
pm, va, pa);
#endif
return 0;
}
DPRINTF(PDB_ENTER, "pmap_enter(%p, 0x%lx, 0x%lx, 0x%x, 0x%x)",
pm, va, pa, prot, flags);
pg = PHYS_TO_VM_PAGE(pa);
pteproto = PG_V | PMAP_T2PTE_4(pa);
pa &= ~PMAP_TNC_4;
pteproto |= atop(pa) & PG_PFNUM;
if (prot & VM_PROT_WRITE)
pteproto |= PG_W;
if ((flags & PMAP_WIRED) != 0)
pteproto |= PG_WIRED;
if (flags & VM_PROT_ALL) {
pteproto |= PG_U;
if (flags & VM_PROT_WRITE) {
pteproto |= PG_M;
}
}
write_user_windows();
ctx = getcontext4();
if (pm == pmap_kernel())
error = pmap_enk4_4c(pm, va, prot, flags, pg, pteproto | PG_S);
else
error = pmap_enu4_4c(pm, va, prot, flags, pg, pteproto);
setcontext4(ctx);
return (error);
}
int
pmap_enk4_4c(struct pmap *pm, vaddr_t va, vm_prot_t prot, int flags,
struct vm_page *pg, int pteproto)
{
int vr, vs, pte, s, inmmu;
int *ptep;
struct regmap *rp;
struct segmap *sp;
int error = 0;
vr = VA_VREG(va);
vs = VA_VSEG(va);
rp = &pm->pm_regmap[vr];
sp = &rp->rg_segmap[vs];
ptep = &sp->sg_pte[VA_VPG(va)];
s = splvm();
#if defined(SUN4_MMU3L)
if (HASSUN4_MMU3L && rp->rg_smeg == reginval)
mmu_pagein_reg(pm, rp, va, vr, ®ion_locked);
#endif
inmmu = sp->sg_pmeg != seginval;
if ((pte = *ptep) & PG_V) {
if ((pte & (PG_PFNUM|PG_TYPE)) ==
(pteproto & (PG_PFNUM|PG_TYPE))) {
pmap_changeprot4_4c(pm, va, prot, flags);
splx(s);
return (0);
}
if ((pte & PG_TYPE) == PG_OBMEM) {
struct vm_page *opg;
if ((opg = pvhead4_4c(pte)) != NULL)
pv_unlink4_4c(opg, pm, va);
if (inmmu && (pte & PG_NC) == 0) {
setcontext4(0);
cache_flush_page(va, 0);
}
}
*ptep = 0;
if (inmmu)
setpte4(va, 0);
if (pte & PG_WIRED) {
sp->sg_nwired--;
pm->pm_stats.wired_count--;
}
pm->pm_stats.resident_count--;
} else {
if (sp->sg_npte++ == 0) {
#ifdef DIAGNOSTIC
int i; for (i = 0; i < NPTESG; i++) {
if (sp->sg_pte[i] == 0)
continue;
panic("pmap_enk: pm %p, va %lx: pte[%d] not empty\n",
pm, va, i);
}
#endif
rp->rg_nsegmap++;
}
}
if (pg != NULL && (error = pv_link4_4c(pg, pm, va, &pteproto)) != 0) {
if (--sp->sg_npte == 0)
pgt_lvl23_remove4_4c(pm, rp, sp, vr, vs);
if ((flags & PMAP_CANFAIL) != 0)
goto out;
panic("pmap_enter: cannot allocate PV entry");
}
*ptep = pteproto;
if (pteproto & PG_WIRED) {
sp->sg_nwired++;
pm->pm_stats.wired_count++;
}
pm->pm_stats.resident_count++;
#ifdef DIAGNOSTIC
if (sp->sg_nwired > sp->sg_npte || sp->sg_nwired < 0)
panic("pmap_enk: pm %p, va %lx: nleft=%d, nwired=%d",
pm, va, sp->sg_npte, sp->sg_nwired);
#endif
if (sp->sg_pmeg == seginval)
mmu_pagein_seg(pm, sp, va, vr, vs,
(pteproto & PG_WIRED) != 0 ? &segm_locked : &segm_lru);
else if ((pteproto & PG_WIRED) != 0)
mmu_pmeg_lock(sp->sg_pmeg);
setpte4(va, pteproto & ~PG_MBZ);
out:
splx(s);
return (error);
}
int
pmap_enu4_4c(struct pmap *pm, vaddr_t va, vm_prot_t prot, int flags,
struct vm_page *pg, int pteproto)
{
int vr, vs, *ptep, pte, pmeg, s;
int error = 0;
struct regmap *rp;
struct segmap *sp;
pm->pm_flags &= ~PMAP_USERCACHECLEAN;
vr = VA_VREG(va);
vs = VA_VSEG(va);
rp = &pm->pm_regmap[vr];
s = splvm();
GAP_SHRINK(pm,vr);
#ifdef DEBUG
if (pm->pm_gap_end < pm->pm_gap_start) {
printf("pmap_enu: gap_start 0x%x, gap_end 0x%x",
pm->pm_gap_start, pm->pm_gap_end);
panic("pmap_enu: gap botch");
}
#endif
if (rp->rg_segmap == NULL) {
int i;
int mflag = PR_NOWAIT;
rretry:
sp = (struct segmap *)pool_get(&segmap_pool, mflag);
if (sp == NULL) {
if ((flags & PMAP_CANFAIL) != 0) {
error = ENOMEM;
goto out;
}
mflag = PR_WAITOK;
goto rretry;
}
#ifdef DEBUG
if (rp->rg_segmap != NULL)
panic("pmap_enter: segment filled during sleep");
#endif
qzero((void *)sp, NSEGRG * sizeof (struct segmap));
rp->rg_segmap = sp;
rp->rg_nsegmap = 0;
for (i = NSEGRG; --i >= 0;)
sp++->sg_pmeg = seginval;
}
sp = &rp->rg_segmap[vs];
if ((ptep = sp->sg_pte) == NULL) {
int size = NPTESG * sizeof *ptep;
int mflag = PR_NOWAIT;
sretry:
ptep = (int *)pool_get(&pte_pool, mflag);
if (ptep == NULL) {
if ((flags & PMAP_CANFAIL) != 0) {
error = ENOMEM;
goto out;
}
mflag = PR_WAITOK;
goto sretry;
}
#ifdef DEBUG
if (sp->sg_pte != NULL)
panic("pmap_enter: pte filled during sleep");
if (sp->sg_pmeg != seginval)
panic("pmap_enter: new ptes, but not seginval");
#endif
qzero((void *)ptep, size);
sp->sg_pte = ptep;
sp->sg_npte = 1;
rp->rg_nsegmap++;
} else {
pte = ptep[VA_VPG(va)];
if (pte & PG_V) {
if ((pte & (PG_PFNUM|PG_TYPE)) ==
(pteproto & (PG_PFNUM|PG_TYPE))) {
pmap_changeprot4_4c(pm, va, prot, flags);
splx(s);
return (0);
}
#if 0
printf("%s[%d]: pmap_enu: changing existing "
"va(0x%lx)=>pa entry\n",
curproc->p_comm, curproc->p_pid, va);
#endif
if ((pte & PG_TYPE) == PG_OBMEM) {
struct vm_page *opg;
if ((opg = pvhead4_4c(pte)) != NULL)
pv_unlink4_4c(opg, pm, va);
if (CACHEINFO.c_vactype != VAC_NONE &&
(pmeg = sp->sg_pmeg) != seginval) {
if (CTX_USABLE(pm,rp)) {
setcontext4(pm->pm_ctxnum);
} else {
setcontext4(0);
if (HASSUN4_MMU3L)
setregmap(0, tregion);
setsegmap(0, pmeg);
}
cache_flush_page(va, pm->pm_ctxnum);
}
}
if (pte & PG_WIRED) {
sp->sg_nwired--;
pm->pm_stats.wired_count--;
}
pm->pm_stats.resident_count--;
ptep[VA_VPG(va)] = 0;
if (sp->sg_pmeg != seginval)
setpte4(va, 0);
} else {
sp->sg_npte++;
}
}
if (pg != NULL && (error = pv_link4_4c(pg, pm, va, &pteproto)) != 0) {
if (--sp->sg_npte == 0)
pgt_lvl23_remove4_4c(pm, rp, sp, vr, vs);
if ((flags & PMAP_CANFAIL) != 0)
goto out;
panic("pmap_enter: cannot allocate PV entry");
}
ptep += VA_VPG(va);
*ptep = pteproto;
if (pteproto & PG_WIRED) {
sp->sg_nwired++;
pm->pm_stats.wired_count++;
}
pm->pm_stats.resident_count++;
#ifdef DIAGNOSTIC
if (sp->sg_nwired > sp->sg_npte || sp->sg_nwired < 0)
panic("pmap_enu: pm %p, va %lx: nleft=%d, nwired=%d",
pm, va, sp->sg_npte, sp->sg_nwired);
#endif
if ((pmeg = sp->sg_pmeg) != seginval) {
if (CTX_USABLE(pm,rp))
setcontext4(pm->pm_ctxnum);
else {
setcontext4(0);
if (HASSUN4_MMU3L)
setregmap(0, tregion);
setsegmap(0, pmeg);
va = VA_VPG(va) << PGSHIFT;
}
setpte4(va, pteproto & ~PG_MBZ);
}
out:
splx(s);
return (error);
}
void
pmap_kenter_pa4_4c(vaddr_t va, paddr_t pa, vm_prot_t prot, u_int flags)
{
struct pmap *pm = pmap_kernel();
struct regmap *rp;
struct segmap *sp;
int vr, vs, s;
int *ptep, pteproto;
int lockit = 1;
pteproto = PG_S | PG_V | PMAP_T2PTE_4(pa);
pa &= ~PMAP_TNC_4;
pteproto |= atop(pa) & PG_PFNUM;
if (prot & VM_PROT_WRITE)
pteproto |= PG_W;
vr = VA_VREG(va);
vs = VA_VSEG(va);
rp = &pm->pm_regmap[vr];
sp = &rp->rg_segmap[vs];
ptep = &sp->sg_pte[VA_VPG(va)];
if (lockit) {
pteproto |= PG_WIRED;
sp->sg_nwired++;
}
KASSERT((*ptep & PG_V) == 0);
s = splvm();
#if defined(SUN4_MMU3L)
if (HASSUN4_MMU3L && rp->rg_smeg == reginval)
mmu_pagein_reg(pm, rp, va, vr, ®ion_locked);
#endif
if (sp->sg_npte++ == 0) {
#ifdef DIAGNOSTIC
int i; for (i = 0; i < NPTESG; i++) {
if (sp->sg_pte[i] == 0)
continue;
panic("pmap_enk: pm %p, va %lx: pte[%d] not empty\n",
pm, va, i);
}
#endif
rp->rg_nsegmap++;
}
*ptep = pteproto;
#ifdef DIAGNOSTIC
if (sp->sg_nwired > sp->sg_npte || sp->sg_nwired < 0)
panic("pmap_kenter: pm %p, va %lx: nleft=%d, nwired=%d",
pm, va, sp->sg_npte, sp->sg_nwired);
#endif
if (sp->sg_pmeg == seginval) {
mmu_pagein_seg(pm, sp, va, vr, vs,
lockit ? &segm_locked : &segm_lru);
} else if (lockit)
mmu_pmeg_lock(sp->sg_pmeg);
setpte4(va, pteproto & ~PG_MBZ);
splx(s);
}
#if notyet
void pmap_lockmmu(vaddr_t sva, size_t sz);
void
pmap_lockmmu(vaddr_t sva, size_t sz)
{
struct pmap *pm = pmap_kernel();
vaddr_t va, eva;
struct regmap *rp;
struct segmap *sp;
int vr, vs;
if (CPU_HAS_SRMMU)
return;
eva = sva + sz;
va = VA_ROUNDDOWNTOSEG(sva);
for (; va < eva; va += NBPSG) {
vr = VA_VREG(va);
vs = VA_VSEG(va);
rp = &pm->pm_regmap[vr];
sp = &rp->rg_segmap[vs];
KASSERT(sp->sg_npte != 0);
if (sp->sg_pmeg == seginval)
mmu_pagein_seg(pm, sp, va, vr, vs, &segm_locked);
else
mmu_pmeg_lock(sp->sg_pmeg);
}
}
#endif
void
pmap_kremove4_4c(vaddr_t va, vsize_t len)
{
struct pmap *pm = pmap_kernel();
struct regmap *rp;
struct segmap *sp;
vaddr_t nva, endva;
int pte, mmupte, *ptep, perpage, npg, inmmu;
int nleft, pmeg;
int vr, vs, s, ctx;
endva = va + len;
DPRINTF(PDB_REMOVE, "pmap_kremove(0x%lx, 0x%lx)", va, endva);
write_user_windows();
s = splvm();
ctx = getcontext();
PMAP_LOCK();
setcontext4(0);
for (; va < endva; va = nva) {
vr = VA_VREG(va);
vs = VA_VSEG(va);
nva = VSTOVA(vr, vs + 1);
if (nva == 0 || nva > endva)
nva = endva;
rp = &pm->pm_regmap[vr];
sp = &rp->rg_segmap[vs];
if (rp->rg_nsegmap == 0)
continue;
nleft = sp->sg_npte;
if (nleft == 0)
continue;
pmeg = sp->sg_pmeg;
inmmu = (pmeg != seginval);
ptep = &sp->sg_pte[VA_VPG(va)];
npg = (nva - va) >> PGSHIFT;
if (!inmmu) {
perpage = 0;
} else if (npg > PMAP_SFL_THRESHOLD) {
perpage = 0;
cache_flush_segment(vr, vs, 0);
} else {
perpage = (CACHEINFO.c_vactype != VAC_NONE);
}
for (; va < nva; va += NBPG, ptep++) {
pte = *ptep;
mmupte = inmmu ? getpte4(va) : 0;
if ((pte & PG_V) == 0) {
#ifdef DIAGNOSTIC
if (inmmu && (mmupte & PG_V) != 0)
printf("rmk: inconsistent ptes va=%lx\n", va);
#endif
continue;
}
if ((pte & PG_TYPE) == PG_OBMEM) {
if (perpage && (mmupte & PG_NC) == 0)
cache_flush_page(va, 0);
}
nleft--;
#ifdef DIAGNOSTIC
if (nleft < 0)
panic("pmap_kremove: too many PTEs in segment; "
"va 0x%lx; endva 0x%lx", va, endva);
#endif
if (pte & PG_WIRED)
sp->sg_nwired--;
if (inmmu)
setpte4(va, 0);
*ptep = 0;
}
#ifdef DIAGNOSTIC
if (sp->sg_nwired > nleft || sp->sg_nwired < 0)
panic("pmap_kremove: pm %p, va %lx: nleft=%d, nwired=%d",
pm, va, nleft, sp->sg_nwired);
#endif
if ((sp->sg_npte = nleft) == 0)
pgt_lvl23_remove4_4c(pm, rp, sp, vr, vs);
else if (sp->sg_nwired == 0) {
if (sp->sg_pmeg != seginval)
mmu_pmeg_unlock(sp->sg_pmeg);
}
}
PMAP_UNLOCK();
setcontext4(ctx);
splx(s);
}
void
pmap_kprotect4_4c(vaddr_t va, vsize_t size, vm_prot_t prot)
{
int pte, newprot, ctx;
size = roundup(size,NBPG);
newprot = prot & VM_PROT_WRITE ? PG_S|PG_W : PG_S;
ctx = getcontext4();
setcontext4(0);
while (size > 0) {
pte = getpte4(va);
if ((pte & (PG_NC|PG_TYPE)) == PG_OBMEM)
cache_flush_page(va, 0);
pte = (pte & ~PG_PROT) | newprot;
setpte4(va, pte);
va += NBPG;
size -= NBPG;
}
setcontext4(ctx);
}
#endif
#if defined(SUN4M) || defined(SUN4D)
int
pmap_enter4m(struct pmap *pm, vaddr_t va, paddr_t pa,
vm_prot_t prot, u_int flags)
{
struct vm_page *pg;
int pteproto;
int error;
DPRINTF(PDB_ENTER, "pmap_enter[curcpu %d, curpid %d, ctx %d,%d]"
"(%p, 0x%lx, 0x%lx, 0x%x, 0x%x)",
cpu_number(), curproc == NULL ? -1 : curproc->p_pid,
getcontext4m(), pm->pm_ctx == NULL ? -1 : pm->pm_ctxnum,
pm, va, pa, prot, flags);
pg = PHYS_TO_VM_PAGE(pa);
pteproto = (pa & PMAP_NC) == 0 ? SRMMU_PG_C : 0;
#ifdef DEBUG
if (pa & PMAP_TYPE_SRMMU) {
if (cpuinfo.cpu_type == CPUTYP_MS1)
panic("pmap_enter4m: attempt to use 36-bit iospace on"
" MicroSPARC");
}
#endif
pteproto |= SRMMU_TEPTE;
pteproto |= PMAP_T2PTE_SRMMU(pa);
pa &= ~PMAP_TNC_SRMMU;
pteproto |= (atop(pa) << SRMMU_PPNSHIFT);
pteproto |= pte_prot4m(pm, prot);
if (flags & VM_PROT_ALL) {
pteproto |= SRMMU_PG_R;
if (flags & VM_PROT_WRITE) {
pteproto |= SRMMU_PG_M;
}
}
if (pm == pmap_kernel())
error = pmap_enk4m(pm, va, prot, flags, pg, pteproto | PPROT_S);
else
error = pmap_enu4m(pm, va, prot, flags, pg, pteproto);
return (error);
}
int
pmap_enk4m(struct pmap *pm, vaddr_t va, vm_prot_t prot, int flags,
struct vm_page *pg, int pteproto)
{
int vr, vs, tpte, s;
struct regmap *rp;
struct segmap *sp;
int error = 0;
#ifdef DEBUG
if (va < KERNBASE)
panic("pmap_enk4m: can't enter va 0x%lx below KERNBASE", va);
#endif
vr = VA_VREG(va);
vs = VA_VSEG(va);
rp = &pm->pm_regmap[vr];
sp = &rp->rg_segmap[vs];
kpreempt_disable();
s = splvm();
PMAP_LOCK();
if (rp->rg_seg_ptps == NULL)
panic("pmap_enk4m: missing kernel region table for va 0x%lx",va);
tpte = sp->sg_pte[VA_SUN4M_VPG(va)];
if ((tpte & SRMMU_TETYPE) == SRMMU_TEPTE) {
if ((tpte & SRMMU_PPNMASK) == (pteproto & SRMMU_PPNMASK)) {
pmap_changeprot4m(pm, va, prot, flags);
error = 0;
goto out;
}
if ((tpte & SRMMU_PGTYPE) == PG_SUN4M_OBMEM) {
struct vm_page *opg;
#ifdef DEBUG
printf("pmap_enk4m: changing existing va=>pa entry: va 0x%lx, pteproto 0x%x, "
"oldpte 0x%x\n", va, pteproto, tpte);
#endif
if ((opg = pvhead4m(tpte)) != NULL)
pv_unlink4m(opg, pm, va);
if (tpte & SRMMU_PG_C) {
cache_flush_page(va, 0);
}
}
setpgt4m_va(va, &sp->sg_pte[VA_SUN4M_VPG(va)],
SRMMU_TEINVALID, pm->pm_ctx != NULL,
pm->pm_ctxnum, PMAP_CPUSET(pm));
pm->pm_stats.resident_count--;
}
if (pg != NULL && (error = pv_link4m(pg, pm, va, &pteproto)) != 0) {
if ((flags & PMAP_CANFAIL) != 0)
goto out;
panic("pmap_enter: cannot allocate PV entry");
}
setpgt4m(&sp->sg_pte[VA_SUN4M_VPG(va)], pteproto);
pm->pm_stats.resident_count++;
out:
PMAP_UNLOCK();
splx(s);
kpreempt_enable();
return (error);
}
int
pmap_enu4m(struct pmap *pm, vaddr_t va, vm_prot_t prot, int flags,
struct vm_page *pg, int pteproto)
{
int vr, vs, *pte, tpte, s;
int error = 0;
struct regmap *rp;
struct segmap *sp;
bool owired;
#ifdef DEBUG
if (KERNBASE < va)
panic("pmap_enu4m: can't enter va 0x%lx above KERNBASE", va);
#endif
pm->pm_flags &= ~PMAP_USERCACHECLEAN;
vr = VA_VREG(va);
vs = VA_VSEG(va);
rp = &pm->pm_regmap[vr];
s = splvm();
PMAP_LOCK();
if (rp->rg_segmap == NULL) {
int mflag = PR_NOWAIT;
rretry:
sp = (struct segmap *)pool_get(&segmap_pool, mflag);
if (sp == NULL) {
if ((flags & PMAP_CANFAIL) != 0) {
error = ENOMEM;
goto out;
}
mflag = PR_WAITOK;
goto rretry;
}
#ifdef DEBUG
if (rp->rg_segmap != NULL)
panic("pmap_enu4m: segment filled during sleep");
#endif
qzero((void *)sp, NSEGRG * sizeof (struct segmap));
rp->rg_segmap = sp;
rp->rg_nsegmap = 0;
rp->rg_seg_ptps = NULL;
}
if (rp->rg_seg_ptps == NULL) {
int i, *ptd;
int mflag = PR_NOWAIT;
sretry:
ptd = pool_get(&L23_pool, mflag);
if (ptd == NULL) {
if ((flags & PMAP_CANFAIL) != 0) {
error = ENOMEM;
goto out;
}
mflag = PR_WAITOK;
goto sretry;
}
rp->rg_seg_ptps = ptd;
for (i = 0; i < SRMMU_L2SIZE; i++)
setpgt4m(&ptd[i], SRMMU_TEINVALID);
#if defined(MULTIPROCESSOR)
for (i = 0; i < sparc_ncpus; i++)
#else
i = 0;
#endif
{
#if defined(MULTIPROCESSOR)
if ((cpus[i]->flags & CPUFLG_HATCHED) == 0)
continue;
#endif
setpgt4m(&pm->pm_reg_ptps[i][vr],
(VA2PA((void *)ptd) >> SRMMU_PPNPASHIFT) |
SRMMU_TEPTD);
}
}
sp = &rp->rg_segmap[vs];
owired = false;
if ((pte = sp->sg_pte) == NULL) {
int i;
int mflag = PR_NOWAIT;
pte = pool_get(&L23_pool, mflag);
if (pte == NULL) {
if ((flags & PMAP_CANFAIL) != 0) {
error = ENOMEM;
goto out;
}
panic("pmap_enter: cannot allocate PTE table");
}
sp->sg_pte = pte;
sp->sg_npte = 1;
rp->rg_nsegmap++;
for (i = 0; i < SRMMU_L3SIZE; i++)
setpgt4m(&pte[i], SRMMU_TEINVALID);
setpgt4m(&rp->rg_seg_ptps[vs],
(VA2PA((void *)pte) >> SRMMU_PPNPASHIFT) | SRMMU_TEPTD);
} else {
#ifdef DIAGNOSTIC
if (sp->sg_npte <= 0)
panic("pm %p: npte %d", pm, sp->sg_npte);
#endif
tpte = pte[VA_SUN4M_VPG(va)];
if ((tpte & SRMMU_TETYPE) == SRMMU_TEPTE) {
if ((tpte & SRMMU_PPNMASK) ==
(pteproto & SRMMU_PPNMASK)) {
pmap_changeprot4m(pm, va, prot, flags);
error = 0;
goto out;
}
DPRINTF(PDB_SWITCHMAP,
"%s[%d]: pmap_enu: changing existing "
"va 0x%x: pte 0x%x=>0x%x",
curproc->p_comm, curproc->p_pid,
(int)va, tpte, pteproto);
if ((tpte & SRMMU_PGTYPE) == PG_SUN4M_OBMEM) {
struct vm_page *opg;
if ((opg = pvhead4m(tpte)) != NULL) {
VM_MDPAGE_PVHEAD(opg)->pv_flags |=
MR4M(tpte);
pv_unlink4m(opg, pm, va);
}
if (pm->pm_ctx && (tpte & SRMMU_PG_C))
cache_flush_page(va, pm->pm_ctxnum);
}
setpgt4m_va(va, &sp->sg_pte[VA_SUN4M_VPG(va)],
SRMMU_TEINVALID, pm->pm_ctx != NULL,
pm->pm_ctxnum, PMAP_CPUSET(pm));
pm->pm_stats.resident_count--;
owired = sp->sg_wiremap & (1 << VA_SUN4M_VPG(va));
} else {
sp->sg_npte++;
}
}
if (pg != NULL && (error = pv_link4m(pg, pm, va, &pteproto)) != 0) {
if (--sp->sg_npte == 0)
pgt_lvl23_remove4m(pm, rp, sp, vr, vs);
if ((flags & PMAP_CANFAIL) != 0)
goto out;
panic("pmap_enter: cannot allocate PV entry");
}
setpgt4m(&sp->sg_pte[VA_SUN4M_VPG(va)], pteproto);
pm->pm_stats.resident_count++;
if (owired) {
pm->pm_stats.wired_count--;
sp->sg_wiremap &= ~(1 << VA_SUN4M_VPG(va));
}
if (flags & PMAP_WIRED) {
pm->pm_stats.wired_count++;
sp->sg_wiremap |= (1 << VA_SUN4M_VPG(va));
}
out:
PMAP_UNLOCK();
splx(s);
return (error);
}
void
pmap_kenter_pa4m(vaddr_t va, paddr_t pa, vm_prot_t prot, u_int flags)
{
struct pmap *pm = pmap_kernel();
struct regmap *rp;
struct segmap *sp;
int pteproto, vr, vs;
pteproto = (pa & PMAP_NC) == 0 ? SRMMU_PG_C : 0;
pteproto |= SRMMU_TEPTE | PPROT_S;
pteproto |= PMAP_T2PTE_SRMMU(pa);
pteproto |= (atop(pa & ~PMAP_TNC_SRMMU) << SRMMU_PPNSHIFT);
pteproto |= pte_kprot4m(prot);
vr = VA_VREG(va);
vs = VA_VSEG(va);
rp = &pm->pm_regmap[vr];
sp = &rp->rg_segmap[vs];
KASSERT((sp->sg_pte[VA_SUN4M_VPG(va)] & SRMMU_TETYPE) != SRMMU_TEPTE);
setpgt4m(&sp->sg_pte[VA_SUN4M_VPG(va)], pteproto);
}
void
pmap_kremove4m(vaddr_t va, vsize_t len)
{
struct pmap *pm = pmap_kernel();
struct regmap *rp;
struct segmap *sp;
vaddr_t endva, nva;
int vr, vs;
int tpte, perpage, npg, s;
kpreempt_disable();
s = splvm();
endva = va + len;
for (; va < endva; va = nva) {
vr = VA_VREG(va);
vs = VA_VSEG(va);
nva = VSTOVA(vr, vs + 1);
if (nva == 0 || nva > endva) {
nva = endva;
}
rp = &pm->pm_regmap[vr];
sp = &rp->rg_segmap[vs];
npg = (nva - va) >> PGSHIFT;
if (npg > PMAP_SFL_THRESHOLD) {
perpage = 0;
if (CACHEINFO.c_vactype != VAC_NONE) {
cache_flush_segment(vr, vs, 0);
}
} else {
perpage = (CACHEINFO.c_vactype != VAC_NONE);
}
for (; va < nva; va += NBPG) {
tpte = sp->sg_pte[VA_SUN4M_VPG(va)];
if ((tpte & SRMMU_TETYPE) != SRMMU_TEPTE)
continue;
if ((tpte & SRMMU_PGTYPE) == PG_SUN4M_OBMEM) {
if (perpage && (tpte & SRMMU_PG_C))
cache_flush_page(va, 0);
}
setpgt4m_va(va, &sp->sg_pte[VA_SUN4M_VPG(va)],
SRMMU_TEINVALID, 1, 0, CPUSET_ALL);
}
}
splx(s);
kpreempt_enable();
}
void
pmap_kprotect4m(vaddr_t va, vsize_t size, vm_prot_t prot)
{
struct pmap *pm = pmap_kernel();
int pte, newprot, s;
struct regmap *rp;
struct segmap *sp;
size = roundup(size,NBPG);
newprot = pte_kprot4m(prot);
kpreempt_disable();
s = splvm();
while (size > 0) {
rp = &pm->pm_regmap[VA_VREG(va)];
sp = &rp->rg_segmap[VA_VSEG(va)];
pte = sp->sg_pte[VA_SUN4M_VPG(va)];
if ((pte & (SRMMU_PG_C|SRMMU_PGTYPE)) ==
(SRMMU_PG_C|PG_SUN4M_OBMEM))
cache_flush_page(va, 0);
setpgt4m_va(va, &sp->sg_pte[VA_SUN4M_VPG(va)],
(pte & ~SRMMU_PROT_MASK) | newprot,
1, pm->pm_ctxnum, PMAP_CPUSET(pm));
va += NBPG;
size -= NBPG;
}
splx(s);
kpreempt_enable();
}
#endif
void
pmap_unwire(struct pmap *pm, vaddr_t va)
{
int vr, vs, *ptep;
struct regmap *rp;
struct segmap *sp;
bool owired;
kpreempt_disable();
vr = VA_VREG(va);
vs = VA_VSEG(va);
rp = &pm->pm_regmap[vr];
sp = &rp->rg_segmap[vs];
owired = false;
if (CPU_HAS_SUNMMU) {
ptep = &sp->sg_pte[VA_VPG(va)];
owired = *ptep & PG_WIRED;
*ptep &= ~PG_WIRED;
}
if (CPU_HAS_SRMMU) {
owired = sp->sg_wiremap & (1 << VA_SUN4M_VPG(va));
sp->sg_wiremap &= ~(1 << VA_SUN4M_VPG(va));
}
if (!owired) {
pmap_stats.ps_useless_changewire++;
kpreempt_enable();
return;
}
pm->pm_stats.wired_count--;
#if defined(SUN4) || defined(SUN4C)
if (CPU_HAS_SUNMMU && --sp->sg_nwired <= 0) {
#ifdef DIAGNOSTIC
if (sp->sg_nwired > sp->sg_npte || sp->sg_nwired < 0)
panic("pmap_unwire: pm %p, va %lx: nleft=%d, nwired=%d",
pm, va, sp->sg_npte, sp->sg_nwired);
#endif
if (sp->sg_pmeg != seginval)
mmu_pmeg_unlock(sp->sg_pmeg);
}
#endif
kpreempt_enable();
}
#if defined(SUN4) || defined(SUN4C)
bool
pmap_extract4_4c(struct pmap *pm, vaddr_t va, paddr_t *pap)
{
int vr, vs;
struct regmap *rp;
struct segmap *sp;
int pte, *ptep;
vr = VA_VREG(va);
vs = VA_VSEG(va);
rp = &pm->pm_regmap[vr];
if (rp->rg_segmap == NULL) {
DPRINTF(PDB_FOLLOW, "pmap_extract: invalid segment (%d)", vr);
return (false);
}
sp = &rp->rg_segmap[vs];
ptep = sp->sg_pte;
if (ptep == NULL) {
DPRINTF(PDB_FOLLOW, "pmap_extract: invalid segment");
return (false);
}
pte = ptep[VA_VPG(va)];
if ((pte & PG_V) == 0) {
DPRINTF(PDB_FOLLOW, "pmap_extract: invalid pte");
return (false);
}
pte &= PG_PFNUM;
if (pap != NULL)
*pap = (pte << PGSHIFT) | (va & PGOFSET);
return (true);
}
#endif
#if defined(SUN4M) || defined(SUN4D)
bool
pmap_extract4m(struct pmap *pm, vaddr_t va, paddr_t *pap)
{
struct regmap *rp;
struct segmap *sp;
int pte;
int vr, vs, s, v = false;
bool can_lock = lock_available;
vr = VA_VREG(va);
vs = VA_VSEG(va);
s = splvm();
if (pm != pmap_kernel()) {
PMAP_LOCK();
}
rp = &pm->pm_regmap[vr];
if (rp->rg_segmap == NULL) {
DPRINTF(PDB_FOLLOW, "pmap_extract: no segmap");
goto out;
}
sp = &rp->rg_segmap[vs];
if (sp->sg_pte == NULL) {
DPRINTF(PDB_FOLLOW, "pmap_extract: no ptes");
goto out;
}
pte = sp->sg_pte[VA_SUN4M_VPG(va)];
if ((pte & SRMMU_TETYPE) != SRMMU_TEPTE) {
DPRINTF(PDB_FOLLOW, "pmap_extract: invalid pte of type %d",
pte & SRMMU_TETYPE);
if (__predict_true(can_lock))
mutex_spin_enter(&demap_lock);
pte = sp->sg_pte[VA_SUN4M_VPG(va)];
if (__predict_true(can_lock))
mutex_spin_exit(&demap_lock);
if ((pte & SRMMU_TETYPE) != SRMMU_TEPTE)
goto out;
}
#ifdef DIAGNOSTIC
if (pm != pmap_kernel() && sp->sg_npte <= 0)
panic("pmap_extract: pm %p: npte = %d\n", pm, sp->sg_npte);
#endif
if (pap != NULL)
*pap = ptoa((pte & SRMMU_PPNMASK) >> SRMMU_PPNSHIFT) |
VA_OFF(va);
v = true;
out:
if (pm != pmap_kernel()) {
PMAP_UNLOCK();
}
splx(s);
return (v);
}
#endif
int pmap_copy_disabled=0;
void
pmap_copy(struct pmap *dst_pmap, struct pmap *src_pmap,
vaddr_t dst_addr, vsize_t len, vaddr_t src_addr)
{
#if notyet
struct regmap *rp;
struct segmap *sp;
if (pmap_copy_disabled)
return;
#ifdef DIAGNOSTIC
if (VA_OFF(src_addr) != 0)
printf("pmap_copy: addr not page aligned: 0x%lx\n", src_addr);
if ((len & (NBPG-1)) != 0)
printf("pmap_copy: length not page aligned: 0x%lx\n", len);
#endif
if (src_pmap == NULL)
return;
if (CPU_HAS_SRMMU) {
int i, npg, pte;
paddr_t pa;
npg = len >> PGSHIFT;
for (i = 0; i < npg; i++) {
if ((rp = src_pmap->pm_regmap) == NULL)
continue;
rp += VA_VREG(src_addr);
if ((sp = rp->rg_segmap) == NULL)
continue;
sp += VA_VSEG(src_addr);
if (sp->sg_npte == 0)
continue;
pte = sp->sg_pte[VA_SUN4M_VPG(src_addr)];
if ((pte & SRMMU_TETYPE) != SRMMU_TEPTE)
continue;
pa = ptoa((pte & SRMMU_PPNMASK) >> SRMMU_PPNSHIFT);
pmap_enter(dst_pmap, dst_addr,
pa,
(pte & PPROT_WRITE)
? (VM_PROT_WRITE | VM_PROT_READ)
: VM_PROT_READ,
0);
src_addr += NBPG;
dst_addr += NBPG;
}
pmap_update(dst_pmap);
}
#endif
}
#if defined(SUN4) || defined(SUN4C)
bool
pmap_clear_modify4_4c(struct vm_page *pg)
{
bool rv;
(void) pv_syncflags4_4c(pg);
rv = VM_MDPAGE_PVHEAD(pg)->pv_flags & PV_MOD;
VM_MDPAGE_PVHEAD(pg)->pv_flags &= ~PV_MOD;
return (rv);
}
bool
pmap_is_modified4_4c(struct vm_page *pg)
{
return (VM_MDPAGE_PVHEAD(pg)->pv_flags & PV_MOD ||
pv_syncflags4_4c(pg) & PV_MOD);
}
bool
pmap_clear_reference4_4c(struct vm_page *pg)
{
bool rv;
(void) pv_syncflags4_4c(pg);
rv = VM_MDPAGE_PVHEAD(pg)->pv_flags & PV_REF;
VM_MDPAGE_PVHEAD(pg)->pv_flags &= ~PV_REF;
return (rv);
}
bool
pmap_is_referenced4_4c(struct vm_page *pg)
{
return (VM_MDPAGE_PVHEAD(pg)->pv_flags & PV_REF ||
pv_syncflags4_4c(pg) & PV_REF);
}
#endif
#if defined(SUN4M) || defined(SUN4D)
bool
pmap_clear_modify4m(struct vm_page *pg)
{
bool rv;
(void) pv_syncflags4m(pg);
rv = VM_MDPAGE_PVHEAD(pg)->pv_flags & PV_MOD4M;
VM_MDPAGE_PVHEAD(pg)->pv_flags &= ~PV_MOD4M;
return (rv);
}
bool
pmap_is_modified4m(struct vm_page *pg)
{
return (VM_MDPAGE_PVHEAD(pg)->pv_flags & PV_MOD4M ||
pv_syncflags4m(pg) & PV_MOD4M);
}
bool
pmap_clear_reference4m(struct vm_page *pg)
{
bool rv;
(void) pv_syncflags4m(pg);
rv = VM_MDPAGE_PVHEAD(pg)->pv_flags & PV_REF4M;
VM_MDPAGE_PVHEAD(pg)->pv_flags &= ~PV_REF4M;
return (rv);
}
bool
pmap_is_referenced4m(struct vm_page *pg)
{
return (VM_MDPAGE_PVHEAD(pg)->pv_flags & PV_REF4M ||
pv_syncflags4m(pg) & PV_REF4M);
}
#endif
#if defined(SUN4) || defined(SUN4C)
void
pmap_zero_page4_4c(paddr_t pa)
{
struct vm_page *pg;
void *va;
int pte;
if ((pg = PHYS_TO_VM_PAGE(pa)) != NULL) {
pv_flushcache4_4c(pg);
}
pte = PG_V | PG_S | PG_W | PG_NC | (atop(pa) & PG_PFNUM);
va = cpuinfo.vpage[0];
setpte4(va, pte);
qzero(va, NBPG);
setpte4(va, 0);
}
void
pmap_copy_page4_4c(paddr_t src, paddr_t dst)
{
struct vm_page *pg;
char *sva, *dva;
int spte, dpte;
if ((pg = PHYS_TO_VM_PAGE(src)) != NULL) {
if (CACHEINFO.c_vactype == VAC_WRITEBACK)
pv_flushcache4_4c(pg);
}
spte = PG_V | PG_S | (atop(src) & PG_PFNUM);
if ((pg = PHYS_TO_VM_PAGE(dst)) != NULL) {
if (CACHEINFO.c_vactype != VAC_NONE)
pv_flushcache4_4c(pg);
}
dpte = PG_V | PG_S | PG_W | PG_NC | (atop(dst) & PG_PFNUM);
sva = cpuinfo.vpage[0];
dva = cpuinfo.vpage[1];
setpte4(sva, spte);
setpte4(dva, dpte);
qcopy(sva, dva, NBPG);
cache_flush_page((vaddr_t)sva, getcontext4());
setpte4(sva, 0);
setpte4(dva, 0);
}
#endif
#if defined(SUN4M) || defined(SUN4D)
void
pmap_zero_page4m(paddr_t pa)
{
struct vm_page *pg;
void *va;
int pte;
kpreempt_disable();
if ((pg = PHYS_TO_VM_PAGE(pa)) != NULL) {
if (CACHEINFO.c_vactype != VAC_NONE)
pv_flushcache4m(pg);
else
pcache_flush_page(pa, 1);
}
pte = SRMMU_TEPTE | PPROT_N_RWX | (pa >> SRMMU_PPNPASHIFT);
if (CACHEINFO.c_flags & CACHE_MANDATORY)
pte |= SRMMU_PG_C;
va = cpuinfo.vpage[0];
setpgt4m(cpuinfo.vpage_pte[0], pte);
qzero(va, NBPG);
sp_tlb_flush((int)va, 0, ASI_SRMMUFP_L3);
setpgt4m(cpuinfo.vpage_pte[0], SRMMU_TEINVALID);
kpreempt_enable();
}
void
pmap_zero_page_viking_mxcc(paddr_t pa)
{
u_int offset;
u_int stream_data_addr = MXCC_STREAM_DATA;
uint64_t v = (uint64_t)pa;
kpreempt_disable();
stda(stream_data_addr+0, ASI_CONTROL, 0);
stda(stream_data_addr+8, ASI_CONTROL, 0);
stda(stream_data_addr+16, ASI_CONTROL, 0);
stda(stream_data_addr+24, ASI_CONTROL, 0);
v |= MXCC_STREAM_C;
for (offset = 0; offset < NBPG; offset += MXCC_STREAM_BLKSZ) {
stda(MXCC_STREAM_DST, ASI_CONTROL, v | offset);
}
kpreempt_enable();
}
void
pmap_zero_page_hypersparc(paddr_t pa)
{
struct vm_page *pg;
void *va;
int pte;
int offset;
kpreempt_disable();
if ((pg = PHYS_TO_VM_PAGE(pa)) != NULL) {
if (CACHEINFO.c_vactype != VAC_NONE)
pv_flushcache4m(pg);
}
pte = SRMMU_TEPTE | SRMMU_PG_C | PPROT_N_RWX | (pa >> SRMMU_PPNPASHIFT);
va = cpuinfo.vpage[0];
setpgt4m(cpuinfo.vpage_pte[0], pte);
for (offset = 0; offset < NBPG; offset += 32) {
sta((char *)va + offset, ASI_BLOCKFILL, 0);
}
sp_tlb_flush((int)va, 0, ASI_SRMMUFP_L3);
setpgt4m(cpuinfo.vpage_pte[0], SRMMU_TEINVALID);
kpreempt_enable();
}
void
pmap_copy_page4m(paddr_t src, paddr_t dst)
{
struct vm_page *pg;
void *sva, *dva;
int spte, dpte;
kpreempt_disable();
if ((pg = PHYS_TO_VM_PAGE(src)) != NULL) {
if (CACHEINFO.c_vactype == VAC_WRITEBACK)
pv_flushcache4m(pg);
}
spte = SRMMU_TEPTE | SRMMU_PG_C | PPROT_N_RX |
(src >> SRMMU_PPNPASHIFT);
if ((pg = PHYS_TO_VM_PAGE(dst)) != NULL) {
if (CACHEINFO.c_vactype != VAC_NONE)
pv_flushcache4m(pg);
else
pcache_flush_page(dst, 1);
}
dpte = SRMMU_TEPTE | PPROT_N_RWX | (dst >> SRMMU_PPNPASHIFT);
if (CACHEINFO.c_flags & CACHE_MANDATORY)
dpte |= SRMMU_PG_C;
sva = cpuinfo.vpage[0];
dva = cpuinfo.vpage[1];
setpgt4m(cpuinfo.vpage_pte[0], spte);
setpgt4m(cpuinfo.vpage_pte[1], dpte);
qcopy(sva, dva, NBPG);
cpuinfo.sp_vcache_flush_page((vaddr_t)sva, getcontext4m());
sp_tlb_flush((int)sva, 0, ASI_SRMMUFP_L3);
setpgt4m(cpuinfo.vpage_pte[0], SRMMU_TEINVALID);
sp_tlb_flush((int)dva, 0, ASI_SRMMUFP_L3);
setpgt4m(cpuinfo.vpage_pte[1], SRMMU_TEINVALID);
kpreempt_enable();
}
void
pmap_copy_page_viking_mxcc(paddr_t src, paddr_t dst)
{
u_int offset;
uint64_t v1 = (uint64_t)src;
uint64_t v2 = (uint64_t)dst;
kpreempt_disable();
v1 |= MXCC_STREAM_C;
v2 |= MXCC_STREAM_C;
for (offset = 0; offset < NBPG; offset += MXCC_STREAM_BLKSZ) {
stda(MXCC_STREAM_SRC, ASI_CONTROL, v1 | offset);
stda(MXCC_STREAM_DST, ASI_CONTROL, v2 | offset);
}
kpreempt_enable();
}
void
pmap_copy_page_hypersparc(paddr_t src, paddr_t dst)
{
struct vm_page *pg;
void *sva, *dva;
int spte, dpte;
int offset;
kpreempt_disable();
if ((pg = PHYS_TO_VM_PAGE(src)) != NULL) {
if (CACHEINFO.c_vactype == VAC_WRITEBACK)
pv_flushcache4m(pg);
}
spte = SRMMU_TEPTE | SRMMU_PG_C | PPROT_N_RX |
(src >> SRMMU_PPNPASHIFT);
if ((pg = PHYS_TO_VM_PAGE(dst)) != NULL) {
if (CACHEINFO.c_vactype != VAC_NONE)
pv_flushcache4m(pg);
}
dpte = SRMMU_TEPTE | SRMMU_PG_C | PPROT_N_RWX |
(dst >> SRMMU_PPNPASHIFT);
sva = cpuinfo.vpage[0];
dva = cpuinfo.vpage[1];
setpgt4m(cpuinfo.vpage_pte[0], spte);
setpgt4m(cpuinfo.vpage_pte[1], dpte);
for (offset = 0; offset < NBPG; offset += 32) {
sta((char *)dva + offset, ASI_BLOCKCOPY, (char *)sva + offset);
}
sp_tlb_flush((int)sva, 0, ASI_SRMMUFP_L3);
setpgt4m(cpuinfo.vpage_pte[0], SRMMU_TEINVALID);
sp_tlb_flush((int)dva, 0, ASI_SRMMUFP_L3);
setpgt4m(cpuinfo.vpage_pte[1], SRMMU_TEINVALID);
kpreempt_enable();
}
#endif
void
kvm_uncache(char *va, int npages)
{
struct vm_page *pg;
int pte;
if (CPU_HAS_SRMMU) {
#if defined(SUN4M) || defined(SUN4D)
for (; --npages >= 0; va = (char *)va + NBPG) {
pte = getpte4m((vaddr_t) va);
if ((pte & SRMMU_TETYPE) != SRMMU_TEPTE)
panic("kvm_uncache: table entry not pte");
if ((pte & SRMMU_PGTYPE) == PG_SUN4M_OBMEM) {
if ((pg = pvhead4m(pte)) != NULL) {
pv_uncache(pg);
return;
}
cache_flush_page((vaddr_t)va, 0);
}
pte &= ~SRMMU_PG_C;
setpte4m((vaddr_t)va, pte);
}
#endif
} else {
#if defined(SUN4) || defined(SUN4C)
for (; --npages >= 0; va += NBPG) {
pte = getpte4(va);
if ((pte & PG_V) == 0)
panic("kvm_uncache !pg_v");
if ((pte & PG_TYPE) == PG_OBMEM) {
if ((pg = pvhead4_4c(pte)) != NULL) {
pv_uncache(pg);
return;
}
cache_flush_page((vaddr_t)va, 0);
}
pte |= PG_NC;
setpte4(va, pte);
}
#endif
}
}
#if 0
void
kvm_iocache(char *va, int npages)
{
#if defined(SUN4M)
if (CPU_ISSUN4M)
panic("kvm_iocache: 4m iocache not implemented");
#endif
#if defined(SUN4D)
if (CPU_ISSUN4D)
panic("kvm_iocache: 4d iocache not implemented");
#endif
#if defined(SUN4) || defined(SUN4C)
for (; --npages >= 0; va += NBPG) {
int pte = getpte4(va);
if ((pte & PG_V) == 0)
panic("kvm_iocache !pg_v");
pte |= PG_IOC;
setpte4(va, pte);
}
#endif
}
#endif
void
pmap_prefer(vaddr_t foff, vaddr_t *vap, size_t size, int td)
{
vaddr_t va = *vap;
long m;
m = CACHE_ALIAS_DIST;
if (m == 0)
return;
if (VA_INHOLE(va)) {
if (td)
va = MMU_HOLE_START - size;
else
va = MMU_HOLE_END;
}
va = (va & ~(m - 1)) | (foff & (m - 1));
if (td) {
if (va > *vap)
va -= m;
} else {
if (va < *vap)
va += m;
}
*vap = va;
}
void
pmap_redzone(void)
{
pmap_remove(pmap_kernel(), KERNBASE, KERNBASE+NBPG);
}
void
pmap_activate(struct lwp *l)
{
pmap_t pm = l->l_proc->p_vmspace->vm_map.pmap;
if (pm == pmap_kernel() || l != curlwp) {
return;
}
mutex_spin_enter(&ctx_lock);
if (pm->pm_ctx == NULL) {
ctx_alloc(pm);
} else {
setcontext(pm->pm_ctxnum);
}
PMAP_SET_CPUSET(pm, &cpuinfo);
mutex_spin_exit(&ctx_lock);
}
void
pmap_deactivate(struct lwp *l)
{
struct proc *p = l->l_proc;
pmap_t pm = p->p_vmspace->vm_map.pmap;
if (pm == pmap_kernel() || l != curlwp) {
return;
}
write_user_windows();
mutex_spin_enter(&ctx_lock);
if (pm->pm_ctx) {
(*cpuinfo.pure_vcache_flush)();
#if defined(SUN4M) || defined(SUN4D)
if (CPU_HAS_SRMMU)
sp_tlb_flush(0, pm->pm_ctxnum, ASI_SRMMUFP_L0);
#endif
}
PMAP_CLR_CPUSET(pm, &cpuinfo);
mutex_spin_exit(&ctx_lock);
}
#ifdef DEBUG
void
pm_check(char *s, struct pmap *pm)
{
if (pm == pmap_kernel())
pm_check_k(s, pm);
else
pm_check_u(s, pm);
}
void
pm_check_u(char *s, struct pmap *pm)
{
struct regmap *rp;
struct segmap *sp;
int cpu, n, vs, vr, j, m, *pte;
cpu = cpuinfo.ci_cpuid;
if (pm->pm_regmap == NULL)
panic("%s: CPU %d: CHK(pmap %p): no region mapping",
s, cpu, pm);
#if defined(SUN4M) || defined(SUN4D)
if (CPU_HAS_SRMMU &&
(pm->pm_reg_ptps[cpu] == NULL ||
pm->pm_reg_ptps_pa[cpu] != VA2PA((void *)pm->pm_reg_ptps[cpu])))
panic("%s: CPU %d: CHK(pmap %p): no SRMMU region table or bad pa: "
"tblva=%p, tblpa=0x%x",
s, cpu, pm, pm->pm_reg_ptps[cpu], pm->pm_reg_ptps_pa[cpu]);
if (CPU_HAS_SRMMU && pm->pm_ctx != NULL &&
(cpuinfo.ctx_tbl[pm->pm_ctxnum] != ((VA2PA((void *)pm->pm_reg_ptps[cpu])
>> SRMMU_PPNPASHIFT) |
SRMMU_TEPTD)))
panic("%s: CPU %d: CHK(pmap %p): SRMMU region table at 0x%x not installed "
"for context %d", s, cpu, pm, pm->pm_reg_ptps_pa[cpu], pm->pm_ctxnum);
#endif
for (vr = 0; vr < NUREG; vr++) {
rp = &pm->pm_regmap[vr];
if (rp->rg_nsegmap == 0)
continue;
if (rp->rg_segmap == NULL)
panic("%s: CPU %d: CHK(vr %d): nsegmap = %d; sp==NULL",
s, cpu, vr, rp->rg_nsegmap);
#if defined(SUN4M) || defined(SUN4D)
if (CPU_HAS_SRMMU && rp->rg_seg_ptps == NULL)
panic("%s: CPU %d: CHK(vr %d): nsegmap=%d; no SRMMU segment table",
s, cpu, vr, rp->rg_nsegmap);
if (CPU_HAS_SRMMU &&
pm->pm_reg_ptps[cpu][vr] != ((VA2PA((void *)rp->rg_seg_ptps) >>
SRMMU_PPNPASHIFT) | SRMMU_TEPTD))
panic("%s: CPU %d: CHK(vr %d): SRMMU segtbl not installed",
s, cpu, vr);
#endif
if ((unsigned int)rp < KERNBASE)
panic("%s: CPU %d: rp=%p", s, cpu, rp);
n = 0;
for (vs = 0; vs < NSEGRG; vs++) {
sp = &rp->rg_segmap[vs];
if ((unsigned int)sp < KERNBASE)
panic("%s: CPU %d: sp=%p", s, cpu, sp);
if (sp->sg_npte != 0) {
n++;
if (sp->sg_pte == NULL)
panic("%s: CPU %d: CHK(vr %d, vs %d): npte=%d, "
"pte=NULL", s, cpu, vr, vs, sp->sg_npte);
#if defined(SUN4M) || defined(SUN4D)
if (CPU_HAS_SRMMU &&
rp->rg_seg_ptps[vs] !=
((VA2PA((void *)sp->sg_pte)
>> SRMMU_PPNPASHIFT) |
SRMMU_TEPTD))
panic("%s: CPU %d: CHK(vr %d, vs %d): SRMMU page "
"table not installed correctly",
s, cpu, vr, vs);
#endif
pte=sp->sg_pte;
m = 0;
for (j=0; j<NPTESG; j++,pte++)
if ((CPU_HAS_SRMMU
?((*pte & SRMMU_TETYPE) == SRMMU_TEPTE)
:(*pte & PG_V)))
m++;
if (m != sp->sg_npte)
printf("%s: CPU %d: user CHK(vr %d, vs %d): "
"npte(%d) != # valid(%d)\n",
s, cpu, vr, vs, sp->sg_npte, m);
}
}
if (n != rp->rg_nsegmap)
panic("%s: CPU %d: CHK(vr %d): inconsistent "
"# of pte's: %d, should be %d",
s, cpu, vr, rp->rg_nsegmap, n);
}
return;
}
void
pm_check_k(char *s, struct pmap *pm)
{
struct regmap *rp;
int cpu, vr, vs, n;
cpu = cpu_number();
if (pm->pm_regmap == NULL)
panic("%s: CHK(pmap %p): no region mapping", s, pm);
#if defined(SUN4M) || defined(SUN4D)
if (CPU_HAS_SRMMU &&
(pm->pm_reg_ptps[cpu] == NULL ||
pm->pm_reg_ptps_pa[cpu] != VA2PA((void *)pm->pm_reg_ptps[cpu])))
panic("%s: CPU %d: CHK(pmap %p): no SRMMU region table or bad pa: tblva=%p, tblpa=0x%x",
s, cpu, pm, pm->pm_reg_ptps[cpu], pm->pm_reg_ptps_pa[cpu]);
if (CPU_HAS_SRMMU &&
(cpuinfo.ctx_tbl[0] != ((VA2PA((void *)pm->pm_reg_ptps[cpu]) >>
SRMMU_PPNPASHIFT) | SRMMU_TEPTD)))
panic("%s: CPU %d: CHK(pmap %p): SRMMU region table at 0x%x not installed "
"for context %d", s, cpu, pm, pm->pm_reg_ptps_pa[cpu], 0);
#endif
for (vr = NUREG; vr < NUREG+NKREG; vr++) {
rp = &pm->pm_regmap[vr];
if (rp->rg_segmap == NULL)
panic("%s: CPU %d: CHK(vr %d): nsegmap = %d; sp==NULL",
s, cpu, vr, rp->rg_nsegmap);
if (rp->rg_nsegmap == 0)
continue;
#if defined(SUN4M) || defined(SUN4D)
if (CPU_HAS_SRMMU && rp->rg_seg_ptps == NULL)
panic("%s: CPU %d: CHK(vr %d): nsegmap=%d; no SRMMU segment table",
s, cpu, vr, rp->rg_nsegmap);
if (CPU_HAS_SRMMU && vr != NUREG &&
pm->pm_reg_ptps[cpu][vr] != ((VA2PA((void *)rp->rg_seg_ptps) >>
SRMMU_PPNPASHIFT) | SRMMU_TEPTD))
panic("%s: CPU %d: CHK(vr %d): SRMMU segtbl not installed",
s, cpu, vr);
#endif
if (CPU_HAS_SRMMU) {
n = NSEGRG;
} else {
for (n = 0, vs = 0; vs < NSEGRG; vs++) {
if (rp->rg_segmap[vs].sg_npte)
n++;
}
}
if (n != rp->rg_nsegmap)
printf("%s: CPU %d: kernel CHK(vr %d): inconsistent "
"# of pte's: %d, should be %d\n",
s, cpu, vr, rp->rg_nsegmap, n);
}
return;
}
#endif
int
pmap_dumpsize(void)
{
int sz;
sz = ALIGN(sizeof(kcore_seg_t)) + ALIGN(sizeof(cpu_kcore_hdr_t));
sz += npmemarr * sizeof(phys_ram_seg_t);
sz += sizeof(kernel_segmap_store);
if (CPU_HAS_SUNMMU)
sz += (seginval + 1) * NPTESG * sizeof(int);
return btodb(sz + DEV_BSIZE - 1);
}
int
pmap_dumpmmu(int (*dump)(dev_t, daddr_t, void *, size_t),
daddr_t blkno)
{
kcore_seg_t *ksegp;
cpu_kcore_hdr_t *kcpup;
phys_ram_seg_t memseg;
int error = 0;
int i, memsegoffset, segmapoffset, pmegoffset;
int buffer[dbtob(1) / sizeof(int)];
int *bp, *ep;
#if defined(SUN4C) || defined(SUN4)
int pmeg;
#endif
#define EXPEDITE(p,n) do { \
int *sp = (int *)(p); \
int sz = (n); \
while (sz > 0) { \
*bp++ = *sp++; \
if (bp >= ep) { \
error = (*dump)(dumpdev, blkno, \
(void *)buffer, dbtob(1)); \
if (error != 0) \
return (error); \
++blkno; \
bp = buffer; \
} \
sz -= 4; \
} \
} while (0)
setcontext(0);
bp = buffer;
ep = &buffer[sizeof(buffer) / sizeof(buffer[0])];
ksegp = (kcore_seg_t *)bp;
CORE_SETMAGIC(*ksegp, KCORE_MAGIC, MID_MACHINE, CORE_CPU);
ksegp->c_size = dbtob(pmap_dumpsize()) - ALIGN(sizeof(kcore_seg_t));
kcpup = (cpu_kcore_hdr_t *)((int)bp + ALIGN(sizeof(kcore_seg_t)));
kcpup->cputype = cputyp;
kcpup->kernbase = KERNBASE;
kcpup->nmemseg = npmemarr;
kcpup->memsegoffset = memsegoffset = ALIGN(sizeof(cpu_kcore_hdr_t));
kcpup->nsegmap = NKREG*NSEGRG;
kcpup->segmapoffset = segmapoffset =
memsegoffset + npmemarr * sizeof(phys_ram_seg_t);
kcpup->npmeg = (CPU_HAS_SUNMMU) ? seginval + 1 : 0;
kcpup->pmegoffset = pmegoffset =
segmapoffset + kcpup->nsegmap * sizeof(struct segmap);
bp = (int *)((int)kcpup + ALIGN(sizeof(cpu_kcore_hdr_t)));
#if 0
while (bp != (int *)ALIGN(bp)) {
int dummy = 0;
EXPEDITE(&dummy, 4);
}
#endif
for (i = 0; i < npmemarr; i++) {
memseg.start = pmemarr[i].addr;
memseg.size = pmemarr[i].len;
EXPEDITE((void *)&memseg, sizeof(phys_ram_seg_t));
}
EXPEDITE(&kernel_segmap_store, sizeof(kernel_segmap_store));
if (CPU_HAS_SRMMU)
goto out;
#if defined(SUN4C) || defined(SUN4)
for (pmeg = 0; pmeg <= seginval; ++pmeg) {
int va = 0;
setsegmap(va, pmeg);
i = NPTESG;
do {
int pte = getpte4(va);
EXPEDITE(&pte, sizeof(pte));
va += NBPG;
} while (--i > 0);
}
setsegmap(0, seginval);
#endif
out:
if (bp != buffer)
error = (*dump)(dumpdev, blkno++, (void *)buffer, dbtob(1));
return (error);
}
void
pmap_writetext(unsigned char *dst, int ch)
{
int s, pte0, pte, ctx;
vaddr_t va;
s = splvm();
va = (unsigned long)dst & (~PGOFSET);
cache_flush(dst, 1);
ctx = getcontext();
setcontext(0);
#if defined(SUN4M) || defined(SUN4D)
if (CPU_HAS_SRMMU) {
pte0 = getpte4m(va);
if ((pte0 & SRMMU_TETYPE) != SRMMU_TEPTE) {
goto out;
}
pte = pte0 | PPROT_WRITE;
setpte4m(va, pte);
*dst = (unsigned char)ch;
setpte4m(va, pte0);
}
#endif
#if defined(SUN4) || defined(SUN4C)
if (CPU_ISSUN4C || CPU_ISSUN4) {
pte0 = getpte4(va);
if ((pte0 & PG_V) == 0) {
goto out;
}
pte = pte0 | PG_W;
setpte4(va, pte);
*dst = (unsigned char)ch;
setpte4(va, pte0);
}
#endif
cache_flush(dst, 1);
out:
setcontext(ctx);
splx(s);
}
#ifdef EXTREME_DEBUG
void debug_pagetables(void);
void print_fe_map(void);
static void test_region(int, int, int);
void
debug_pagetables(void)
{
struct promvec *promvec = romp;
int *regtbl;
int te;
int i;
printf("\nncontext=%d. ", ncontext);
printf("Context table is at va %p. Level 0 PTP: 0x%x\n",
cpuinfo.ctx_tbl, cpuinfo.ctx_tbl[0]);
printf("Context 0 region table is at va %p, pa 0x%x. Contents:\n",
pmap_kernel()->pm_reg_ptps[0], pmap_kernel()->pm_reg_ptps_pa[0]);
regtbl = pmap_kernel()->pm_reg_ptps[0];
printf("PROM vector is at %p\n", promvec);
printf("PROM reboot routine is at %p\n", promvec->pv_reboot);
printf("PROM abort routine is at %p\n", promvec->pv_abort);
printf("PROM halt routine is at %p\n", promvec->pv_halt);
printf("Testing region 0xfe: ");
test_region(0xfe,0,16*1024*1024);
printf("Testing region 0xff: ");
test_region(0xff,0,16*1024*1024);
printf("Testing kernel region 0x%x: ", VA_VREG(KERNBASE));
test_region(VA_VREG(KERNBASE), 4096, avail_start);
cngetc();
for (i = 0; i < SRMMU_L1SIZE; i++) {
te = regtbl[i];
if ((te & SRMMU_TETYPE) == SRMMU_TEINVALID)
continue;
printf("Region 0x%x: PTE=0x%x <%s> L2PA=0x%x kernL2VA=%p\n",
i, te, ((te & SRMMU_TETYPE) == SRMMU_TEPTE ? "pte" :
((te & SRMMU_TETYPE) == SRMMU_TEPTD ? "ptd" :
((te & SRMMU_TETYPE) == SRMMU_TEINVALID ?
"invalid" : "reserved"))),
(te & ~0x3) << SRMMU_PPNPASHIFT,
pmap_kernel()->pm_regmap[i].rg_seg_ptps);
}
printf("Press q to halt...\n");
if (cngetc()=='q')
callrom();
}
static u_int
VA2PAsw(int ctx, void *addr, int *pte)
{
int *curtbl;
int curpte;
#ifdef EXTREME_EXTREME_DEBUG
printf("Looking up addr 0x%x in context 0x%x\n",addr,ctx);
#endif
*pte = curpte = cpuinfo.ctx_tbl[ctx];
#ifdef EXTREME_EXTREME_DEBUG
printf("Got L0 pte 0x%x\n",pte);
#endif
if ((curpte & SRMMU_TETYPE) == SRMMU_TEPTE) {
return (((curpte & SRMMU_PPNMASK) << SRMMU_PPNPASHIFT) |
((u_int)addr & 0xffffffff));
}
if ((curpte & SRMMU_TETYPE) != SRMMU_TEPTD) {
printf("Bad context table entry 0x%x for context 0x%x\n",
curpte, ctx);
return 0;
}
curtbl = (int *)(((curpte & ~0x3) << 4) | KERNBASE);
*pte = curpte = curtbl[VA_VREG(addr)];
#ifdef EXTREME_EXTREME_DEBUG
printf("L1 table at 0x%x.\nGot L1 pte 0x%x\n",curtbl,curpte);
#endif
if ((curpte & SRMMU_TETYPE) == SRMMU_TEPTE)
return (((curpte & SRMMU_PPNMASK) << SRMMU_PPNPASHIFT) |
((u_int)addr & 0xffffff));
if ((curpte & SRMMU_TETYPE) != SRMMU_TEPTD) {
printf("Bad region table entry 0x%x for region 0x%x\n",
curpte, VA_VREG(addr));
return 0;
}
curtbl = (int *)(((curpte & ~0x3) << 4) | KERNBASE);
*pte = curpte = curtbl[VA_VSEG(addr)];
#ifdef EXTREME_EXTREME_DEBUG
printf("L2 table at 0x%x.\nGot L2 pte 0x%x\n",curtbl,curpte);
#endif
if ((curpte & SRMMU_TETYPE) == SRMMU_TEPTE)
return (((curpte & SRMMU_PPNMASK) << SRMMU_PPNPASHIFT) |
((u_int)addr & 0x3ffff));
if ((curpte & SRMMU_TETYPE) != SRMMU_TEPTD) {
printf("Bad segment table entry 0x%x for reg 0x%x, seg 0x%x\n",
curpte, VA_VREG(addr), VA_VSEG(addr));
return 0;
}
curtbl = (int *)(((curpte & ~0x3) << 4) | KERNBASE);
*pte = curpte = curtbl[VA_VPG(addr)];
#ifdef EXTREME_EXTREME_DEBUG
printf("L3 table at %p.\nGot L3 pte 0x%x\n", curtbl, curpte);
#endif
if ((curpte & SRMMU_TETYPE) == SRMMU_TEPTE)
return (((curpte & SRMMU_PPNMASK) << SRMMU_PPNPASHIFT) |
((u_int)addr & 0xfff));
else {
printf("Bad L3 pte 0x%x for reg 0x%x, seg 0x%x, pg 0x%x\n",
curpte, VA_VREG(addr), VA_VSEG(addr), VA_VPG(addr));
return 0;
}
printf("Bizarreness with address %p!\n", addr);
}
static void
test_region(int reg, int start, int stop)
{
int i;
int addr;
int pte;
int ptesw;
for (i = start; i < stop; i += NBPG) {
addr = (reg << RGSHIFT) | i;
pte = lda(((u_int)(addr)) | ASI_SRMMUFP_LN, ASI_SRMMUFP);
if (pte) {
if (VA2PA((void *)addr) != VA2PAsw(0, (void *)addr, &ptesw)) {
printf("Mismatch at address 0x%x.\n", addr);
if (cngetc() == 'q')
break;
}
if (reg == VA_VREG(KERNBASE))
continue;
if ((pte & SRMMU_PROT_MASK) != (ptesw & SRMMU_PROT_MASK)) {
printf("Mismatched protections at address "
"0x%x; pte=0x%x, ptesw=0x%x\n",
addr, pte, ptesw);
if (cngetc() == 'q')
break;
}
}
}
printf("done.\n");
}
void
print_fe_map(void)
{
u_int i, pte;
printf("map of region 0xfe:\n");
for (i = 0xfe000000; i < 0xff000000; i += 4096) {
if (((pte = getpte4m(i)) & SRMMU_TETYPE) != SRMMU_TEPTE)
continue;
printf("0x%x -> 0x%x%x (pte 0x%x)\n", i, pte >> 28,
(pte & ~0xff) << 4, pte);
}
printf("done\n");
}
#endif
#ifdef DDB
int pmap_dump(struct pmap *pm);
int
pmap_dump(struct pmap *pm)
{
int startvr, endvr, vr, vs, i, n;
struct regmap *rp;
struct segmap *sp;
if (pm == NULL)
pm = pmap_kernel();
if (pm == pmap_kernel()) {
startvr = NUREG;
endvr = 256;
} else {
startvr = 0;
endvr = NUREG;
}
for (vr = startvr; vr < endvr; vr++) {
rp = &pm->pm_regmap[vr];
if (rp->rg_nsegmap == 0)
continue;
printf("vr %d: %d segments", vr, rp->rg_nsegmap);
if (rp->rg_segmap == NULL) {
printf("[no segments]\n");
continue;
}
for (vs = 0; vs < NSEGRG; vs++) {
sp = &rp->rg_segmap[vs];
if (sp->sg_npte == 0)
continue;
if ((vs & 3) == 0)
printf("\n ");
printf(" %d: n %d w %d p %d,", vs,
sp->sg_npte, sp->sg_nwired, sp->sg_pmeg);
if (sp->sg_pte == NULL) {
printf("[no ptes]");
continue;
}
for (n = 0, i = 0; i < NPTESG; i++) {
if (CPU_HAS_SUNMMU && sp->sg_pte[i] & PG_WIRED)
n++;
if (CPU_HAS_SRMMU && sp->sg_wiremap & (1 << i))
n++;
}
if (n != sp->sg_nwired)
printf("[wired count %d]", n);
}
printf("\n");
}
return (0);
}
#endif