#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.186 2026/07/06 16:08:13 thorpej Exp $");
#include "opt_ddb.h"
#include "opt_kgdb.h"
#include "opt_pmap_debug.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/kmem.h>
#include <sys/pool.h>
#include <sys/queue.h>
#include <sys/kcore.h>
#include <sys/atomic.h>
#include <uvm/uvm.h>
#include <machine/cpu.h>
#include <machine/dvma.h>
#include <machine/fcode.h>
#include <machine/idprom.h>
#include <machine/kcore.h>
#include <machine/mon.h>
#include <machine/pmap.h>
#include <machine/pte.h>
#include <machine/vmparam.h>
#include <m68k/cacheops.h>
#include <m68k/seglist.h>
#include <sun3/sun3/control.h>
#include <sun3/sun3/machdep.h>
#include <sun3/sun3/obmem.h>
#ifdef DDB
#include <ddb/db_output.h>
#else
#define db_printf printf
#endif
#if (PMAP_OBIO << PG_MOD_SHIFT) != PGT_OBIO
#error "PMAP_XXX definitions don't match pte.h!"
#endif
#define PMAP_TYPE PMAP_VME32
#define DVMA_MAP_END (DVMA_MAP_BASE + DVMA_MAP_AVAIL)
#define NUSEG (KERNBASE3 / NBSG)
#define NKSEG (NSEGMAP - NUSEG)
#define VA_SEGNUM(x) ((u_int)(x) >> SEGSHIFT)
#define MEM_BITS (PG_TYPE | PA_PGNUM(0xF8000000))
#define IS_MAIN_MEM(pte) (((pte) & MEM_BITS) == 0)
#define PA_DEV_MASK (0xF8000000 | PMAP_TYPE)
#define PA_IS_DEV(pa) ((pa) & PA_DEV_MASK)
#define BADALIAS(a1, a2) (((int)(a1) ^ (int)(a2)) & SEGOFSET)
#define PMD_ENTER 1
#define PMD_LINK 2
#define PMD_PROTECT 4
#define PMD_SWITCH 8
#define PMD_COW 0x10
#define PMD_MODBIT 0x20
#define PMD_REFBIT 0x40
#define PMD_WIRING 0x80
#define PMD_CONTEXT 0x100
#define PMD_CREATE 0x200
#define PMD_SEGMAP 0x400
#define PMD_SETPTE 0x800
#define PMD_FAULT 0x1000
#define PMD_KMAP 0x2000
#define PMD_REMOVE PMD_ENTER
#define PMD_UNLINK PMD_LINK
#ifdef PMAP_DEBUG
int pmap_debug = 0;
int pmap_db_watchva = -1;
int pmap_db_watchpmeg = -1;
#endif
vaddr_t virtual_avail, virtual_end;
paddr_t avail_start, avail_end;
static vaddr_t hole_start, hole_size;
static vaddr_t temp_seg_va;
vaddr_t tmp_vpages[2] = {
SUN3_MONSHORTSEG,
SUN3_MONSHORTSEG + PAGE_SIZE };
int tmp_vpages_inuse;
static int pmap_version = 1;
static struct pmap kernel_pmap_store;
struct pmap *const kernel_pmap_ptr = &kernel_pmap_store;
#define kernel_pmap (kernel_pmap_ptr)
static u_char kernel_segmap[NSEGMAP];
struct pool pmap_pmap_pool;
struct pmap_stats {
int ps_enter_firstpv;
int ps_enter_secondpv;
int ps_unlink_pvfirst;
int ps_unlink_pvsearch;
int ps_pmeg_faultin;
int ps_changeprots;
int ps_changewire;
int ps_npg_prot_all;
int ps_npg_prot_actual;
int ps_vac_uncached;
int ps_vac_recached;
} pmap_stats;
#ifdef PMAP_DEBUG
#define CHECK_SPL() do { \
if ((getsr() & PSL_IPL) < PSL_IPL4) \
panic("pmap: bad spl, line %d", __LINE__); \
} while (0)
#else
#define CHECK_SPL() (void)0
#endif
int pv_initialized = 0;
struct pv_entry {
struct pv_entry *pv_next;
pmap_t pv_pmap;
vaddr_t pv_va;
};
typedef struct pv_entry *pv_entry_t;
static struct pv_entry **pv_head_tbl;
static struct pv_entry *pv_free_list;
static u_char *pv_flags_tbl;
#define PV_SHIFT 24
#define PV_VALID 0x80
#define PV_WRITE 0x40
#define PV_SYSTEM 0x20
#define PV_NC 0x10
#define PV_PERM 0xF0
#define PV_TYPE 0x0C
#define PV_REF 0x02
#define PV_MOD 0x01
struct context_state {
TAILQ_ENTRY(context_state) context_link;
int context_num;
struct pmap *context_upmap;
};
typedef struct context_state *context_t;
#define INVALID_CONTEXT -1
#define EMPTY_CONTEXT 0
#define FIRST_CONTEXT 1
#define has_context(pmap) ((pmap)->pm_ctxnum != EMPTY_CONTEXT)
TAILQ_HEAD(context_tailq, context_state)
context_free_queue, context_active_queue;
static struct context_state context_array[NCONTEXT];
#define PMEGQ_FREE 0
#define PMEGQ_INACTIVE 1
#define PMEGQ_ACTIVE 2
#define PMEGQ_KERNEL 3
#define PMEGQ_NONE 4
struct pmeg_state {
TAILQ_ENTRY(pmeg_state) pmeg_link;
int pmeg_index;
pmap_t pmeg_owner;
int pmeg_version;
vaddr_t pmeg_va;
int pmeg_wired;
int pmeg_reserved;
int pmeg_vpages;
int pmeg_qstate;
};
typedef struct pmeg_state *pmeg_t;
#define PMEG_INVAL (NPMEG-1)
#define PMEG_NULL (pmeg_t) NULL
TAILQ_HEAD(pmeg_tailq, pmeg_state)
pmeg_free_queue, pmeg_inactive_queue,
pmeg_active_queue, pmeg_kernel_queue;
static struct pmeg_state pmeg_array[NPMEG];
static int get_pte_pmeg(int, int);
static void set_pte_pmeg(int, int, int);
static void context_allocate(pmap_t);
static void context_free(pmap_t);
static void context_init(void);
static void pmeg_init(void);
static void pmeg_reserve(int);
static pmeg_t pmeg_allocate(pmap_t, vaddr_t);
static void pmeg_mon_init(vaddr_t, vaddr_t, int);
static void pmeg_release(pmeg_t);
static void pmeg_free(pmeg_t);
static pmeg_t pmeg_cache(pmap_t, vaddr_t);
static void pmeg_set_wiring(pmeg_t, vaddr_t, int);
static int pv_link (pmap_t, int, vaddr_t);
static void pv_unlink(pmap_t, int, vaddr_t);
static void pv_remove_all(paddr_t);
static void pv_changepte(paddr_t, int, int);
static u_int pv_syncflags(pv_entry_t);
static void pv_init(void);
static void pmeg_clean(pmeg_t);
static void pmeg_clean_free(void);
static void pmap_common_init(pmap_t);
static void pmap_kernel_init(pmap_t);
static void pmap_user_init(pmap_t);
static void pmap_enter_kernel(vaddr_t, int, bool);
static void pmap_enter_user(pmap_t, vaddr_t, int, bool);
static void pmap_protect1(pmap_t, vaddr_t, vaddr_t);
static void pmap_protect_mmu(pmap_t, vaddr_t, vaddr_t);
static void pmap_protect_noctx(pmap_t, vaddr_t, vaddr_t);
static void pmap_remove1(pmap_t, vaddr_t, vaddr_t);
static void pmap_remove_mmu(pmap_t, vaddr_t, vaddr_t);
static void pmap_remove_noctx(pmap_t, vaddr_t, vaddr_t);
static int pmap_fault_reload(struct pmap *, vaddr_t, int);
void _pmap_switch(pmap_t);
#ifdef PMAP_DEBUG
void pmap_print(pmap_t);
void pv_print(paddr_t);
void pmeg_print(pmeg_t);
static void pmeg_verify_empty(vaddr_t);
#endif
void pmap_pinit(pmap_t);
void pmap_release(pmap_t);
static inline pmap_t
current_pmap(void)
{
struct vmspace *vm;
struct vm_map *map;
pmap_t pmap;
vm = curproc->p_vmspace;
map = &vm->vm_map;
pmap = vm_map_pmap(map);
return (pmap);
}
static inline struct pv_entry **
pa_to_pvhead(paddr_t pa)
{
int idx;
idx = PA_PGNUM(pa);
#ifdef DIAGNOSTIC
if (PA_IS_DEV(pa) || (idx >= physmem))
panic("pmap:pa_to_pvhead: bad pa=0x%lx", pa);
#endif
return (&pv_head_tbl[idx]);
}
static inline u_char *
pa_to_pvflags(paddr_t pa)
{
int idx;
idx = PA_PGNUM(pa);
#ifdef DIAGNOSTIC
if (PA_IS_DEV(pa) || (idx >= physmem))
panic("pmap:pa_to_pvflags: bad pa=0x%lx", pa);
#endif
return (&pv_flags_tbl[idx]);
}
static inline void
save_modref_bits(int pte)
{
u_char *pv_flags;
pv_flags = pa_to_pvflags(PG_PA(pte));
*pv_flags |= ((pte & PG_MODREF) >> PV_SHIFT);
}
static inline pmeg_t
pmeg_p(int sme)
{
#ifdef DIAGNOSTIC
if (sme < 0 || sme >= SEGINV)
panic("pmeg_p: bad sme");
#endif
return &pmeg_array[sme];
}
#define is_pmeg_wired(pmegp) (pmegp->pmeg_wired != 0)
static void
pmeg_set_wiring(pmeg_t pmegp, vaddr_t va, int flag)
{
int idx, mask;
idx = VA_PTE_NUM(va);
mask = 1 << idx;
if (flag)
pmegp->pmeg_wired |= mask;
else
pmegp->pmeg_wired &= ~mask;
}
static void
context_init(void)
{
int i;
TAILQ_INIT(&context_free_queue);
TAILQ_INIT(&context_active_queue);
context_array[0].context_upmap = kernel_pmap;
for (i = 1; i < NCONTEXT; i++) {
context_array[i].context_num = i;
context_array[i].context_upmap = NULL;
TAILQ_INSERT_TAIL(&context_free_queue, &context_array[i],
context_link);
#ifdef PMAP_DEBUG
if (pmap_debug & PMD_CONTEXT)
printf("context_init: sizeof(context_array[0])=%d\n",
sizeof(context_array[0]));
#endif
}
}
static void
context_allocate(pmap_t pmap)
{
context_t context;
CHECK_SPL();
#ifdef DIAGNOSTIC
if (pmap == kernel_pmap)
panic("context_allocate: kernel_pmap");
if (has_context(pmap))
panic("pmap: pmap already has context allocated to it");
#endif
context = TAILQ_FIRST(&context_free_queue);
if (context == NULL) {
context = TAILQ_FIRST(&context_active_queue);
if (context == NULL)
panic("pmap: no contexts left?");
#ifdef PMAP_DEBUG
if (pmap_debug & PMD_CONTEXT)
printf("context_allocate: steal ctx %d from pmap %p\n",
context->context_num, context->context_upmap);
#endif
context_free(context->context_upmap);
context = TAILQ_FIRST(&context_free_queue);
}
TAILQ_REMOVE(&context_free_queue, context, context_link);
#ifdef DIAGNOSTIC
if (context->context_upmap != NULL)
panic("pmap: context in use???");
#endif
context->context_upmap = pmap;
pmap->pm_ctxnum = context->context_num;
TAILQ_INSERT_TAIL(&context_active_queue, context, context_link);
}
static void
context_free(pmap_t pmap)
{
int saved_ctxnum, ctxnum;
int i, sme;
context_t contextp;
vaddr_t va;
CHECK_SPL();
ctxnum = pmap->pm_ctxnum;
if (ctxnum < FIRST_CONTEXT || ctxnum >= NCONTEXT)
panic("pmap: context_free ctxnum");
contextp = &context_array[ctxnum];
saved_ctxnum = get_context();
set_context(ctxnum);
#ifdef HAVECACHE
if (cache_size)
cache_flush_context();
#endif
for (i = 0, va = 0; i < NUSEG; i++, va += NBSG) {
#if !defined(PMAP_DEBUG)
if (pmap->pm_segmap[i] == SEGINV)
continue;
#endif
sme = get_segmap(va);
if (sme == SEGINV)
continue;
#ifdef PMAP_DEBUG
if (pmap_debug & PMD_SEGMAP)
printf("pmap: set_segmap ctx=%d v=0x%lx old=0x%x "
"new=ff (cf)\n", ctxnum, va, sme);
#endif
#ifdef DIAGNOSTIC
if (sme != pmap->pm_segmap[i])
panic("context_free: unknown sme at va=0x%lx", va);
#endif
set_segmap(va, SEGINV);
pmeg_release(pmeg_p(sme));
}
set_context(saved_ctxnum);
TAILQ_REMOVE(&context_active_queue, contextp, context_link);
pmap->pm_ctxnum = EMPTY_CONTEXT;
contextp->context_upmap = NULL;
TAILQ_INSERT_TAIL(&context_free_queue, contextp, context_link);
}
static void
pmeg_init(void)
{
int x;
TAILQ_INIT(&pmeg_free_queue);
TAILQ_INIT(&pmeg_inactive_queue);
TAILQ_INIT(&pmeg_active_queue);
TAILQ_INIT(&pmeg_kernel_queue);
memset(pmeg_array, 0, NPMEG*sizeof(struct pmeg_state));
for (x = 0; x < NPMEG; x++) {
TAILQ_INSERT_TAIL(&pmeg_free_queue, &pmeg_array[x], pmeg_link);
pmeg_array[x].pmeg_qstate = PMEGQ_FREE;
pmeg_array[x].pmeg_index = x;
}
pmeg_reserve(SEGINV);
}
void
pmeg_reserve(int sme)
{
pmeg_t pmegp;
pmegp = &pmeg_array[sme];
if (pmegp->pmeg_reserved) {
mon_printf("pmeg_reserve: already reserved\n");
sunmon_abort();
}
if (pmegp->pmeg_owner) {
mon_printf("pmeg_reserve: already owned\n");
sunmon_abort();
}
pmegp->pmeg_owner = kernel_pmap;
pmegp->pmeg_reserved++;
TAILQ_REMOVE(&pmeg_free_queue, pmegp, pmeg_link);
pmegp->pmeg_qstate = PMEGQ_NONE;
}
static void
pmeg_mon_init(vaddr_t sva, vaddr_t eva, int keep)
{
vaddr_t pgva, endseg;
int pte, valid;
unsigned char sme;
#ifdef PMAP_DEBUG
if (pmap_debug & PMD_SEGMAP)
mon_printf("pmeg_mon_init(0x%x, 0x%x, %d)\n",
sva, eva, keep);
#endif
sva &= ~(NBSG - 1);
while (sva < eva) {
sme = get_segmap(sva);
if (sme != SEGINV) {
valid = 0;
endseg = sva + NBSG;
for (pgva = sva; pgva < endseg; pgva += PAGE_SIZE) {
pte = get_pte(pgva);
if (pte & PG_VALID) {
valid++;
}
}
#ifdef PMAP_DEBUG
if (pmap_debug & PMD_SEGMAP)
mon_printf(" sva=0x%x seg=0x%x valid=%d\n",
sva, sme, valid);
#endif
if (keep && valid)
pmeg_reserve(sme);
else
set_segmap(sva, SEGINV);
}
sva += NBSG;
}
}
static void
pmeg_clean(pmeg_t pmegp)
{
int sme;
vaddr_t va;
sme = get_segmap(0);
if (sme != SEGINV)
panic("pmeg_clean");
sme = pmegp->pmeg_index;
set_segmap(0, sme);
for (va = 0; va < NBSG; va += PAGE_SIZE)
set_pte(va, PG_INVAL);
set_segmap(0, SEGINV);
}
static void
pmeg_clean_free(void)
{
pmeg_t pmegp, pmegp_first;
pmegp = TAILQ_FIRST(&pmeg_free_queue);
if (pmegp == NULL)
panic("pmap: no free pmegs available to clean");
pmegp_first = NULL;
for (;;) {
pmegp = TAILQ_FIRST(&pmeg_free_queue);
TAILQ_REMOVE(&pmeg_free_queue, pmegp, pmeg_link);
pmegp->pmeg_qstate = PMEGQ_NONE;
pmeg_clean(pmegp);
pmegp->pmeg_qstate = PMEGQ_FREE;
TAILQ_INSERT_TAIL(&pmeg_free_queue, pmegp, pmeg_link);
if (pmegp == pmegp_first)
break;
if (pmegp_first == NULL)
pmegp_first = pmegp;
}
}
static pmeg_t
pmeg_allocate(pmap_t pmap, vaddr_t va)
{
pmeg_t pmegp;
CHECK_SPL();
#ifdef DIAGNOSTIC
if (va & SEGOFSET) {
panic("pmap:pmeg_allocate: va=0x%lx", va);
}
#endif
pmegp = TAILQ_FIRST(&pmeg_free_queue);
if (!pmegp) {
pmegp = TAILQ_FIRST(&pmeg_inactive_queue);
if (!pmegp) {
pmegp = TAILQ_FIRST(&pmeg_active_queue);
}
if (!pmegp) {
panic("pmeg_allocate: failed");
}
pmap_remove1(pmegp->pmeg_owner,
pmegp->pmeg_va,
pmegp->pmeg_va + NBSG);
}
pmegp = TAILQ_FIRST(&pmeg_free_queue);
#ifdef DIAGNOSTIC
if (pmegp == NULL)
panic("pmeg_allocagte: still none free?");
if ((pmegp->pmeg_qstate != PMEGQ_FREE) ||
(pmegp->pmeg_index == SEGINV) ||
(pmegp->pmeg_vpages))
panic("pmeg_allocate: bad pmegp=%p", pmegp);
#endif
#ifdef PMAP_DEBUG
if (pmegp->pmeg_index == pmap_db_watchpmeg) {
db_printf("pmeg_allocate: watch pmegp=%p\n", pmegp);
Debugger();
}
#endif
TAILQ_REMOVE(&pmeg_free_queue, pmegp, pmeg_link);
pmegp->pmeg_owner = pmap;
pmegp->pmeg_version = pmap->pm_version;
pmegp->pmeg_va = va;
pmegp->pmeg_wired = 0;
pmegp->pmeg_reserved = 0;
pmegp->pmeg_vpages = 0;
if (pmap == kernel_pmap) {
TAILQ_INSERT_TAIL(&pmeg_kernel_queue, pmegp, pmeg_link);
pmegp->pmeg_qstate = PMEGQ_KERNEL;
} else {
TAILQ_INSERT_TAIL(&pmeg_active_queue, pmegp, pmeg_link);
pmegp->pmeg_qstate = PMEGQ_ACTIVE;
}
return pmegp;
}
static void
pmeg_release(pmeg_t pmegp)
{
CHECK_SPL();
#ifdef DIAGNOSTIC
if ((pmegp->pmeg_owner == kernel_pmap) ||
(pmegp->pmeg_qstate != PMEGQ_ACTIVE))
panic("pmeg_release: bad pmeg=%p", pmegp);
#endif
TAILQ_REMOVE(&pmeg_active_queue, pmegp, pmeg_link);
pmegp->pmeg_qstate = PMEGQ_INACTIVE;
TAILQ_INSERT_TAIL(&pmeg_inactive_queue, pmegp, pmeg_link);
}
static void
pmeg_free(pmeg_t pmegp)
{
CHECK_SPL();
#ifdef DIAGNOSTIC
if (pmegp->pmeg_vpages != 0)
panic("pmeg_free: vpages");
#endif
switch (pmegp->pmeg_qstate) {
case PMEGQ_ACTIVE:
TAILQ_REMOVE(&pmeg_active_queue, pmegp, pmeg_link);
break;
case PMEGQ_INACTIVE:
TAILQ_REMOVE(&pmeg_inactive_queue, pmegp, pmeg_link);
break;
case PMEGQ_KERNEL:
TAILQ_REMOVE(&pmeg_kernel_queue, pmegp, pmeg_link);
break;
default:
panic("pmeg_free: releasing bad pmeg");
break;
}
#ifdef PMAP_DEBUG
if (pmegp->pmeg_index == pmap_db_watchpmeg) {
db_printf("pmeg_free: watch pmeg 0x%x\n",
pmegp->pmeg_index);
Debugger();
}
#endif
pmegp->pmeg_owner = NULL;
pmegp->pmeg_qstate = PMEGQ_FREE;
TAILQ_INSERT_TAIL(&pmeg_free_queue, pmegp, pmeg_link);
}
static pmeg_t
pmeg_cache(pmap_t pmap, vaddr_t va)
{
int sme, segnum;
pmeg_t pmegp;
CHECK_SPL();
#ifdef DIAGNOSTIC
if (pmap == kernel_pmap)
panic("pmeg_cache: kernel_pmap");
if (va & SEGOFSET) {
panic("pmap:pmeg_cache: va=0x%lx", va);
}
#endif
if (pmap->pm_segmap == NULL)
return PMEG_NULL;
segnum = VA_SEGNUM(va);
if (segnum > NUSEG)
return PMEG_NULL;
sme = pmap->pm_segmap[segnum];
if (sme == SEGINV)
return PMEG_NULL;
pmegp = pmeg_p(sme);
#ifdef PMAP_DEBUG
if (pmegp->pmeg_index == pmap_db_watchpmeg) {
db_printf("pmeg_cache: watch pmeg 0x%x\n", pmegp->pmeg_index);
Debugger();
}
#endif
if ((pmegp->pmeg_owner != pmap) ||
(pmegp->pmeg_version != pmap->pm_version) ||
(pmegp->pmeg_va != va))
{
#ifdef PMAP_DEBUG
db_printf("pmap:pmeg_cache: invalid pmeg: sme=0x%x\n", sme);
pmeg_print(pmegp);
Debugger();
#endif
pmap->pm_segmap[segnum] = SEGINV;
return PMEG_NULL;
}
#ifdef DIAGNOSTIC
if (pmegp->pmeg_qstate != PMEGQ_INACTIVE)
panic("pmeg_cache: pmeg was taken: %p", pmegp);
#endif
TAILQ_REMOVE(&pmeg_inactive_queue, pmegp, pmeg_link);
pmegp->pmeg_qstate = PMEGQ_ACTIVE;
TAILQ_INSERT_TAIL(&pmeg_active_queue, pmegp, pmeg_link);
return pmegp;
}
#ifdef PMAP_DEBUG
static void
pmeg_verify_empty(vaddr_t va)
{
vaddr_t eva;
int pte;
for (eva = va + NBSG; va < eva; va += PAGE_SIZE) {
pte = get_pte(va);
if (pte & PG_VALID)
panic("pmeg_verify_empty");
}
}
#endif
static void
pv_init(void)
{
int npp, nvp, sz;
pv_entry_t pv;
char *p;
sz = 0;
npp = ALIGN(physmem);
sz += (npp * sizeof(*pv_flags_tbl));
sz += (npp * sizeof(*pv_head_tbl));
nvp = NPMEG * NPAGSEG;
sz += (nvp * sizeof(*pv_free_list));
sz = m68k_round_page(sz);
p = (char *)uvm_km_alloc(kernel_map, sz, 0, UVM_KMF_WIRED);
if (p == NULL)
panic("pmap:pv_init: alloc failed");
memset(p, 0, sz);
pv_flags_tbl = (void *) p;
p += (npp * sizeof(*pv_flags_tbl));
pv_head_tbl = (void*) p;
p += (npp * sizeof(*pv_head_tbl));
pv_free_list = (void *)p;
p += (nvp * sizeof(*pv_free_list));
for (pv = pv_free_list; (char *)pv < p; pv++)
pv->pv_next = &pv[1];
pv[-1].pv_next = 0;
pv_initialized++;
}
static void
pv_changepte(paddr_t pa, int set_bits, int clear_bits)
{
pv_entry_t *head, pv;
u_char *pv_flags;
pmap_t pmap;
vaddr_t va;
int pte, sme;
int saved_ctx;
bool in_ctx;
u_int flags;
pv_flags = pa_to_pvflags(pa);
head = pa_to_pvhead(pa);
if (*head == NULL)
return;
#ifdef DIAGNOSTIC
if (clear_bits & ~(PG_WRITE | PG_NC | PG_REF | PG_MOD))
panic("pv_changepte: clear=0x%x", clear_bits);
#endif
flags = 0;
saved_ctx = get_context();
for (pv = *head; pv != NULL; pv = pv->pv_next) {
pmap = pv->pv_pmap;
va = pv->pv_va;
#ifdef DIAGNOSTIC
if (pmap->pm_segmap == NULL)
panic("pv_changepte: null segmap");
#endif
in_ctx = false;
sme = SEGINV;
if (pmap == kernel_pmap)
in_ctx = true;
else if (has_context(pmap)) {
set_context(pmap->pm_ctxnum);
sme = get_segmap(va);
if (sme != SEGINV)
in_ctx = true;
}
if (in_ctx == true) {
#ifdef HAVECACHE
if (cache_size)
cache_flush_page(va);
#endif
pte = get_pte(va);
} else {
sme = pmap->pm_segmap[VA_SEGNUM(va)];
#ifdef DIAGNOSTIC
if (sme == SEGINV)
panic("pv_changepte: SEGINV");
#endif
pte = get_pte_pmeg(sme, VA_PTE_NUM(va));
}
#ifdef DIAGNOSTIC
if ((pte & PG_VALID) == 0)
panic("pv_changepte: not PG_VALID at va=0x%lx", va);
#endif
if (pte & PG_MODREF) {
flags |= (pte & PG_MODREF);
pte &= ~PG_MODREF;
}
pte |= set_bits;
pte &= ~clear_bits;
if (in_ctx == true) {
set_pte(va, pte);
} else {
set_pte_pmeg(sme, VA_PTE_NUM(va), pte);
}
}
set_context(saved_ctx);
*pv_flags |= (flags >> PV_SHIFT);
}
static u_int
pv_syncflags(pv_entry_t pv)
{
pmap_t pmap;
vaddr_t va;
int pte, sme;
int saved_ctx;
bool in_ctx;
u_int flags;
if (pv == NULL)
return (0);
flags = 0;
saved_ctx = get_context();
for (; pv != NULL; pv = pv->pv_next) {
pmap = pv->pv_pmap;
va = pv->pv_va;
sme = SEGINV;
#ifdef DIAGNOSTIC
if (pmap->pm_segmap == NULL)
panic("pv_syncflags: null segmap");
#endif
in_ctx = false;
if (pmap == kernel_pmap)
in_ctx = true;
else if (has_context(pmap)) {
set_context(pmap->pm_ctxnum);
sme = get_segmap(va);
if (sme != SEGINV)
in_ctx = true;
}
if (in_ctx == true) {
#ifdef HAVECACHE
if (cache_size)
cache_flush_page(va);
#endif
pte = get_pte(va);
} else {
sme = pmap->pm_segmap[VA_SEGNUM(va)];
#ifdef DIAGNOSTIC
if (sme == SEGINV)
panic("pv_syncflags: SEGINV");
#endif
pte = get_pte_pmeg(sme, VA_PTE_NUM(va));
}
#ifdef DIAGNOSTIC
if ((pte & PG_VALID) == 0)
panic("pv_syncflags: not PG_VALID at va=0x%lx", va);
#endif
if (pte & PG_MODREF) {
flags |= (pte & PG_MODREF);
pte &= ~PG_MODREF;
}
if (in_ctx == true) {
set_pte(va, pte);
} else {
set_pte_pmeg(sme, VA_PTE_NUM(va), pte);
}
}
set_context(saved_ctx);
return (flags >> PV_SHIFT);
}
static void
pv_remove_all(paddr_t pa)
{
pv_entry_t *head, pv;
pmap_t pmap;
vaddr_t va;
CHECK_SPL();
#ifdef PMAP_DEBUG
if (pmap_debug & PMD_REMOVE)
printf("pv_remove_all(0x%lx)\n", pa);
#endif
head = pa_to_pvhead(pa);
while ((pv = *head) != NULL) {
pmap = pv->pv_pmap;
va = pv->pv_va;
pmap_remove1(pmap, va, va + PAGE_SIZE);
#ifdef PMAP_DEBUG
if (pv == *head) {
db_printf("pv_remove_all: "
"head unchanged for pa=0x%lx\n", pa);
Debugger();
}
#endif
}
}
static int
pv_link(pmap_t pmap, int pte, vaddr_t va)
{
paddr_t pa;
pv_entry_t *head, pv;
u_char *pv_flags;
int flags;
if (!pv_initialized)
return 0;
CHECK_SPL();
pa = PG_PA(pte);
#ifdef PMAP_DEBUG
if ((pmap_debug & PMD_LINK) || (va == pmap_db_watchva)) {
printf("pv_link(%p, 0x%x, 0x%lx)\n", pmap, pte, va);
}
#endif
pv_flags = pa_to_pvflags(pa);
head = pa_to_pvhead(pa);
#ifdef DIAGNOSTIC
for (pv = *head; pv != NULL; pv = pv->pv_next) {
if ((pv->pv_pmap == pmap) && (pv->pv_va == va))
panic("pv_link: duplicate entry for PA=0x%lx", pa);
}
#endif
flags = (pte & (PG_NC | PG_MODREF)) >> PV_SHIFT;
*pv_flags |= flags;
#ifdef HAVECACHE
if ((*pv_flags & PV_NC) == 0) {
for (pv = *head; pv != NULL; pv = pv->pv_next) {
if (BADALIAS(va, pv->pv_va)) {
*pv_flags |= PV_NC;
pv_changepte(pa, PG_NC, 0);
pmap_stats.ps_vac_uncached++;
break;
}
}
}
#endif
pv = pv_free_list;
if (pv == NULL)
panic("pv_link: pv_alloc");
pv_free_list = pv->pv_next;
pv->pv_next = 0;
pv->pv_pmap = pmap;
pv->pv_va = va;
pv->pv_next = *head;
*head = pv;
return (*pv_flags & PV_NC);
}
static void
pv_unlink(pmap_t pmap, int pte, vaddr_t va)
{
paddr_t pa;
pv_entry_t *head, *ppv, pv;
u_char *pv_flags;
CHECK_SPL();
pa = PG_PA(pte);
#ifdef PMAP_DEBUG
if ((pmap_debug & PMD_LINK) || (va == pmap_db_watchva)) {
printf("pv_unlink(%p, 0x%x, 0x%lx)\n", pmap, pte, va);
}
#endif
pv_flags = pa_to_pvflags(pa);
head = pa_to_pvhead(pa);
ppv = head;
pv = *ppv;
while (pv) {
if ((pv->pv_pmap == pmap) && (pv->pv_va == va))
goto found;
ppv = &pv->pv_next;
pv = pv->pv_next;
}
#ifdef PMAP_DEBUG
db_printf("pv_unlink: not found (pa=0x%lx,va=0x%lx)\n", pa, va);
Debugger();
#endif
return;
found:
*ppv = pv->pv_next;
pv->pv_pmap = NULL;
pv->pv_va = 0;
pv->pv_next = pv_free_list;
pv_free_list = pv;
pv = NULL;
if ((*pv_flags & PV_NC) == 0)
return;
if ((pv = *head) == NULL)
return;
va = pv->pv_va;
for (pv = pv->pv_next; pv != NULL; pv = pv->pv_next) {
if (va >= DVMA_MAP_BASE)
return;
if (BADALIAS(va, pv->pv_va))
return;
}
*pv_flags &= ~PV_NC;
pv_changepte(pa, 0, PG_NC);
pmap_stats.ps_vac_recached++;
}
void
pmap_common_init(pmap_t pmap)
{
memset(pmap, 0, sizeof(struct pmap));
pmap->pm_refcount = 1;
pmap->pm_version = pmap_version++;
pmap->pm_ctxnum = EMPTY_CONTEXT;
}
paddr_t
pmap_bootstrap(vaddr_t nextva)
{
struct sunromvec *rvec;
vaddr_t va, eva;
int i, pte, sme;
extern char etext[];
extern void *msgbufaddr;
paddr_t nextpa;
nextva = m68k_round_page(nextva);
rvec = romVectorPtr;
virtual_avail = sun3_round_seg(nextva);
virtual_end = VM_MAX_KERNEL_ADDRESS;
phys_seg_list[0].ps_start = 0;
nextpa = nextva - KERNBASE3;
if (rvec->romvecVersion < 1) {
mon_printf("Warning: ancient PROM version=%d\n",
rvec->romvecVersion);
phys_seg_list[0].ps_end = *rvec->memorySize - (2*PAGE_SIZE);
} else {
phys_seg_list[0].ps_end = *rvec->memoryAvail;
}
physmem = (btoc(phys_seg_list[0].ps_end) + 0xF) & ~0xF;
if (cpu_machine_id == ID_SUN3_50) {
hole_start = m68k_trunc_page(OBMEM_BW50_ADDR);
hole_size = m68k_round_page(OBMEM_BW2_SIZE);
if (nextpa > hole_start) {
mon_printf("kernel too large for Sun3/50\n");
sunmon_abort();
}
phys_seg_list[1].ps_start = hole_start + hole_size;
phys_seg_list[1].ps_end = phys_seg_list[0].ps_end;
phys_seg_list[0].ps_end = hole_start;
}
for (va = nextva; va < virtual_avail; va += PAGE_SIZE)
set_pte(va, PG_INVAL);
pmeg_init();
for (va = 0; va < KERNBASE3; va += NBSG)
set_segmap(va, SEGINV);
for ( ; va < virtual_avail; va += NBSG) {
sme = get_segmap(va);
if (sme == SEGINV) {
mon_printf("kernel text/data/bss not mapped\n");
sunmon_abort();
}
pmeg_reserve(sme);
}
for ( ; va < virtual_end; va += NBSG)
set_segmap(va, SEGINV);
pmeg_mon_init(SUN3_MONSTART, SUN3_MONEND, true);
pmeg_mon_init(SUN3_MONEND, SUN3_MONSHORTSEG, false);
va = SUN3_MONSHORTSEG;
eva = SUN3_MONSHORTPAGE;
sme = get_segmap(va);
pmeg_reserve(sme);
for ( ; va < eva; va += PAGE_SIZE)
set_pte(va, PG_INVAL);
va = KERNBASE3;
msgbufaddr = (void *)va;
pte = get_pte(va);
pte |= (PG_SYSTEM | PG_WRITE | PG_NC);
set_pte(va, pte);
va += PAGE_SIZE;
pte = get_pte(va);
pte &= ~(PG_NC);
pte |= (PG_SYSTEM | PG_WRITE);
set_pte(va, pte);
va += PAGE_SIZE;
eva = m68k_trunc_page(etext);
while (va < eva) {
pte = get_pte(va);
if ((pte & (PG_VALID|PG_TYPE)) != PG_VALID) {
mon_printf("invalid page at 0x%x\n", va);
}
pte &= ~(PG_WRITE|PG_NC);
pte |= (PG_SYSTEM);
set_pte(va, pte);
va += PAGE_SIZE;
}
while (va < nextva) {
pte = get_pte(va);
if ((pte & (PG_VALID|PG_TYPE)) != PG_VALID) {
mon_printf("invalid page at 0x%x\n", va);
}
pte &= ~(PG_NC);
pte |= (PG_SYSTEM | PG_WRITE);
set_pte(va, pte);
va += PAGE_SIZE;
}
#ifdef DIAGNOSTIC
if ((getsfc() != FC_CONTROL) || (getdfc() != FC_CONTROL)) {
mon_printf("pmap_bootstrap: bad dfc or sfc\n");
sunmon_abort();
}
if (get_context() != 0) {
mon_printf("pmap_bootstrap: not in context zero?\n");
sunmon_abort();
}
#endif
for (va = 0; va < (vaddr_t) (NBSG * NSEGMAP); va += NBSG) {
sme = get_segmap(va);
for (i = 1; i < NCONTEXT; i++) {
(*rvec->setcxsegmap)(i, va, sme);
}
}
temp_seg_va = virtual_avail;
virtual_avail += NBSG;
#ifdef DIAGNOSTIC
if (temp_seg_va & SEGOFSET) {
mon_printf("pmap_bootstrap: temp_seg_va\n");
sunmon_abort();
}
#endif
uvmexp.pagesize = PAGE_SIZE;
uvm_md_init();
pmap_common_init(kernel_pmap);
pmap_kernel_init(kernel_pmap);
context_init();
pmeg_clean_free();
return nextpa;
}
void
pmap_kernel_init(pmap_t pmap)
{
vaddr_t va;
int i, sme;
for (i=0, va=0; i < NSEGMAP; i++, va+=NBSG) {
sme = get_segmap(va);
kernel_segmap[i] = sme;
}
pmap->pm_segmap = kernel_segmap;
}
void
pmap_virtual_space(vaddr_t *v_start, vaddr_t *v_end)
{
*v_start = virtual_avail;
*v_end = virtual_end;
}
void
pmap_init(void)
{
pv_init();
pool_init(&pmap_pmap_pool, sizeof(struct pmap), 0, 0, 0, "pmappl",
&pool_allocator_nointr, IPL_NONE);
}
vaddr_t
pmap_map(vaddr_t va, paddr_t pa, paddr_t endpa, int prot)
{
int sz;
sz = endpa - pa;
do {
pmap_enter(kernel_pmap, va, pa, prot, 0);
va += PAGE_SIZE;
pa += PAGE_SIZE;
sz -= PAGE_SIZE;
} while (sz > 0);
pmap_update(kernel_pmap);
return(va);
}
void
pmap_user_init(pmap_t pmap)
{
int i;
pmap->pm_segmap = kmem_alloc(sizeof(char)*NUSEG, KM_SLEEP);
for (i = 0; i < NUSEG; i++) {
pmap->pm_segmap[i] = SEGINV;
}
}
pmap_t
pmap_create(void)
{
pmap_t pmap;
pmap = pool_get(&pmap_pmap_pool, PR_WAITOK);
pmap_pinit(pmap);
return pmap;
}
void
pmap_release(struct pmap *pmap)
{
int s;
s = splvm();
if (pmap == kernel_pmap)
panic("pmap_release: kernel_pmap!");
if (has_context(pmap)) {
#ifdef PMAP_DEBUG
if (pmap_debug & PMD_CONTEXT)
printf("pmap_release(%p): free ctx %d\n",
pmap, pmap->pm_ctxnum);
#endif
context_free(pmap);
}
kmem_free(pmap->pm_segmap, sizeof(char)*NUSEG);
pmap->pm_segmap = NULL;
splx(s);
}
void
pmap_destroy(pmap_t pmap)
{
int count;
#ifdef PMAP_DEBUG
if (pmap_debug & PMD_CREATE)
printf("pmap_destroy(%p)\n", pmap);
#endif
if (pmap == kernel_pmap)
panic("pmap_destroy: kernel_pmap!");
count = atomic_dec_uint_nv(&pmap->pm_refcount);
if (count == 0) {
pmap_release(pmap);
pool_put(&pmap_pmap_pool, pmap);
}
}
void
pmap_reference(pmap_t pmap)
{
atomic_inc_uint(&pmap->pm_refcount);
}
int
pmap_enter(pmap_t pmap, vaddr_t va, paddr_t pa, vm_prot_t prot, u_int flags)
{
int new_pte, s;
bool wired = (flags & PMAP_WIRED) != 0;
#ifdef PMAP_DEBUG
if ((pmap_debug & PMD_ENTER) ||
(va == pmap_db_watchva))
printf("pmap_enter(%p, 0x%lx, 0x%lx, 0x%x, 0x%x)\n",
pmap, va, pa, prot, wired);
#endif
new_pte = (pa & PMAP_SPEC) << PG_MOD_SHIFT;
new_pte |= PG_VALID;
if (prot & VM_PROT_WRITE)
new_pte |= PG_WRITE;
if (flags & VM_PROT_ALL) {
new_pte |= PG_REF;
if (flags & VM_PROT_WRITE) {
new_pte |= PG_MOD;
}
}
new_pte |= PA_PGNUM(pa);
s = splvm();
if (pmap == kernel_pmap) {
new_pte |= PG_SYSTEM;
pmap_enter_kernel(va, new_pte, wired);
} else {
pmap_enter_user(pmap, va, new_pte, wired);
}
splx(s);
return 0;
}
static void
pmap_enter_kernel(vaddr_t pgva, int new_pte, bool wired)
{
pmap_t pmap = kernel_pmap;
pmeg_t pmegp;
int do_pv, old_pte, sme;
vaddr_t segva;
#ifdef DIAGNOSTIC
if ((pgva < virtual_avail) || (pgva >= DVMA_MAP_END))
panic("pmap_enter_kernel: bad va=0x%lx", pgva);
if ((new_pte & (PG_VALID | PG_SYSTEM)) != (PG_VALID | PG_SYSTEM))
panic("pmap_enter_kernel: bad pte");
#endif
if (pgva >= DVMA_MAP_BASE) {
new_pte |= PG_NC;
}
segva = sun3_trunc_seg(pgva);
do_pv = true;
sme = get_segmap(segva);
if (sme != SEGINV) {
pmegp = pmeg_p(sme);
#ifdef DIAGNOSTIC
if (sme != pmap->pm_segmap[VA_SEGNUM(segva)])
panic("pmap_enter_kernel: wrong sme at VA=0x%lx",
segva);
if (pmegp->pmeg_owner != pmap)
panic("pmap_enter_kernel: MMU has bad pmeg 0x%x", sme);
#endif
} else {
pmegp = pmeg_allocate(pmap, segva);
sme = pmegp->pmeg_index;
pmap->pm_segmap[VA_SEGNUM(segva)] = sme;
set_segmap_allctx(segva, sme);
#ifdef PMAP_DEBUG
pmeg_verify_empty(segva);
if (pmap_debug & PMD_SEGMAP) {
printf("pmap: set_segmap pmap=%p va=0x%lx sme=0x%x "
"(ek)\n", pmap, segva, sme);
}
#endif
old_pte = 0;
goto add_pte;
}
old_pte = get_pte(pgva);
if ((old_pte & PG_VALID) == 0)
goto add_pte;
#ifdef HAVECACHE
if (cache_size) {
cache_flush_page(pgva);
old_pte = get_pte(pgva);
}
#endif
pmegp->pmeg_vpages--;
if (!IS_MAIN_MEM(old_pte)) {
goto add_pte;
}
save_modref_bits(old_pte);
if ((old_pte & (PG_TYPE|PG_FRAME)) == (new_pte & (PG_TYPE|PG_FRAME))) {
do_pv = false;
new_pte |= (old_pte & PG_NC);
goto add_pte;
}
pv_unlink(pmap, old_pte, pgva);
add_pte:
pmeg_set_wiring(pmegp, pgva, wired);
if (!IS_MAIN_MEM(new_pte)) {
new_pte |= PG_NC;
do_pv = false;
}
if (do_pv == true) {
if (pv_link(pmap, new_pte, pgva) & PV_NC)
new_pte |= PG_NC;
}
#ifdef PMAP_DEBUG
if ((pmap_debug & PMD_SETPTE) || (pgva == pmap_db_watchva)) {
printf("pmap: set_pte pmap=%p va=0x%lx old=0x%x new=0x%x "
"(ek)\n", pmap, pgva, old_pte, new_pte);
}
#endif
set_pte(pgva, new_pte);
pmegp->pmeg_vpages++;
}
static void
pmap_enter_user(pmap_t pmap, vaddr_t pgva, int new_pte, bool wired)
{
int do_pv, old_pte, sme;
vaddr_t segva;
pmeg_t pmegp;
#ifdef DIAGNOSTIC
if (pgva >= VM_MAXUSER_ADDRESS)
panic("pmap_enter_user: bad va=0x%lx", pgva);
if ((new_pte & (PG_VALID | PG_SYSTEM)) != PG_VALID)
panic("pmap_enter_user: bad pte");
#endif
#ifdef PMAP_DEBUG
if (wired && (pmap_debug & PMD_WIRING)) {
db_printf("pmap_enter_user: attempt to wire user page, "
"ignored\n");
Debugger();
}
#endif
if (pmap != current_pmap()) {
#ifdef PMAP_DEBUG
db_printf("pmap_enter_user: not curlwp\n");
Debugger();
#endif
return;
}
segva = sun3_trunc_seg(pgva);
do_pv = true;
if (!has_context(pmap)) {
context_allocate(pmap);
#ifdef PMAP_DEBUG
if (pmap_debug & PMD_CONTEXT)
printf("pmap_enter(%p) got context %d\n",
pmap, pmap->pm_ctxnum);
#endif
set_context(pmap->pm_ctxnum);
} else {
#ifdef PMAP_DEBUG
if (pmap->pm_ctxnum != get_context()) {
db_printf("pmap_enter_user: wrong context\n");
Debugger();
set_context(pmap->pm_ctxnum);
}
#endif
}
sme = get_segmap(segva);
if (sme != SEGINV) {
pmegp = pmeg_p(sme);
#ifdef DIAGNOSTIC
if (sme != pmap->pm_segmap[VA_SEGNUM(segva)])
panic("pmap_enter_user: wrong sme at VA=0x%lx", segva);
if (pmegp->pmeg_owner != pmap)
panic("pmap_enter_user: MMU has bad pmeg 0x%x", sme);
#endif
} else {
pmegp = pmeg_cache(pmap, segva);
if (pmegp) {
sme = pmegp->pmeg_index;
set_segmap(segva, sme);
} else {
pmegp = pmeg_allocate(pmap, segva);
sme = pmegp->pmeg_index;
pmap->pm_segmap[VA_SEGNUM(segva)] = sme;
set_segmap(segva, sme);
#ifdef PMAP_DEBUG
pmeg_verify_empty(segva);
#endif
}
#ifdef PMAP_DEBUG
if (pmap_debug & PMD_SEGMAP) {
printf("pmap: set_segmap pmap=%p va=0x%lx sme=0x%x "
"(eu)\n", pmap, segva, sme);
}
#endif
}
old_pte = get_pte(pgva);
if ((old_pte & PG_VALID) == 0)
goto add_pte;
#ifdef HAVECACHE
if (cache_size) {
cache_flush_page(pgva);
old_pte = get_pte(pgva);
}
#endif
pmegp->pmeg_vpages--;
if (!IS_MAIN_MEM(old_pte)) {
goto add_pte;
}
save_modref_bits(old_pte);
if ((old_pte & (PG_TYPE|PG_FRAME)) == (new_pte & (PG_TYPE|PG_FRAME))) {
do_pv = false;
new_pte |= (old_pte & PG_NC);
goto add_pte;
}
pv_unlink(pmap, old_pte, pgva);
add_pte:
if (!IS_MAIN_MEM(new_pte)) {
new_pte |= PG_NC;
do_pv = false;
}
if (do_pv == true) {
if (pv_link(pmap, new_pte, pgva) & PV_NC)
new_pte |= PG_NC;
}
#ifdef PMAP_DEBUG
if ((pmap_debug & PMD_SETPTE) || (pgva == pmap_db_watchva)) {
printf("pmap: set_pte pmap=%p va=0x%lx old=0x%x new=0x%x "
"(eu)\n", pmap, pgva, old_pte, new_pte);
}
#endif
set_pte(pgva, new_pte);
pmegp->pmeg_vpages++;
}
void
pmap_kenter_pa(vaddr_t va, paddr_t pa, vm_prot_t prot, u_int flags)
{
int new_pte, s;
pmap_t pmap = kernel_pmap;
pmeg_t pmegp;
int sme;
vaddr_t segva;
#ifdef PMAP_DEBUG
if ((pmap_debug & PMD_ENTER) ||
(va == pmap_db_watchva))
printf("pmap_kenter_pa(0x%lx, 0x%lx, 0x%x)\n",
va, pa, prot);
#endif
new_pte = (pa & PMAP_SPEC) << PG_MOD_SHIFT;
new_pte |= PG_SYSTEM|PG_VALID;
if (prot & VM_PROT_WRITE)
new_pte |= PG_WRITE;
new_pte |= PA_PGNUM(pa);
#ifdef DIAGNOSTIC
if ((va < virtual_avail) || (va >= DVMA_MAP_END))
panic("pmap_kenter_pa: bad va=0x%lx", va);
#endif
if (va >= DVMA_MAP_BASE) {
new_pte |= PG_NC;
}
segva = sun3_trunc_seg(va);
s = splvm();
sme = get_segmap(segva);
if (sme != SEGINV) {
KASSERT((get_pte(va) & PG_VALID) == 0);
pmegp = pmeg_p(sme);
#ifdef DIAGNOSTIC
if (sme != pmap->pm_segmap[VA_SEGNUM(segva)])
panic("pmap_kenter_pa: wrong sme at VA=0x%lx", segva);
if (pmegp->pmeg_owner != pmap)
panic("pmap_kenter_pa: MMU has bad pmeg 0x%x", sme);
#endif
} else {
pmegp = pmeg_allocate(pmap, segva);
sme = pmegp->pmeg_index;
pmap->pm_segmap[VA_SEGNUM(segva)] = sme;
set_segmap_allctx(segva, sme);
#ifdef PMAP_DEBUG
pmeg_verify_empty(segva);
if (pmap_debug & PMD_SEGMAP) {
printf("pmap: set_segmap pmap=%p va=0x%lx sme=0x%x "
"(ek)\n", pmap, segva, sme);
}
#endif
}
pmeg_set_wiring(pmegp, va, true);
if (!IS_MAIN_MEM(new_pte)) {
new_pte |= PG_NC;
}
#ifdef PMAP_DEBUG
if ((pmap_debug & PMD_SETPTE) || (va == pmap_db_watchva)) {
printf("pmap: set_pte pmap=%p va=0x%lx new=0x%x "
"(ek)\n", pmap, va, new_pte);
}
#endif
set_pte(va, new_pte);
pmegp->pmeg_vpages++;
splx(s);
}
void
pmap_kremove(vaddr_t va, vsize_t len)
{
pmap_t pmap = kernel_pmap;
vaddr_t eva, neva, pgva, segva, segnum;
int pte, sme;
pmeg_t pmegp;
int flush_by_page = 0;
int s;
s = splvm();
segnum = VA_SEGNUM(va);
for (eva = va + len; va < eva; va = neva, segnum++) {
neva = sun3_trunc_seg(va) + NBSG;
if (neva > eva) {
neva = eva;
}
if (pmap->pm_segmap[segnum] == SEGINV) {
continue;
}
segva = sun3_trunc_seg(va);
sme = get_segmap(segva);
pmegp = pmeg_p(sme);
#ifdef HAVECACHE
if (cache_size) {
if (cache_size < (eva - va)) {
cache_flush_segment(segva);
} else {
flush_by_page = 1;
}
}
#endif
for (pgva = va; pgva < neva; pgva += PAGE_SIZE) {
pte = get_pte(pgva);
if (pte & PG_VALID) {
if (flush_by_page) {
#ifdef HAVECACHE
cache_flush_page(pgva);
pte = get_pte(pgva);
#endif
}
#ifdef PMAP_DEBUG
if ((pmap_debug & PMD_SETPTE) ||
(pgva == pmap_db_watchva)) {
printf("pmap: set_pte pmap=%p va=0x%lx"
" old=0x%x new=0x%x (rrmmu)\n",
pmap, pgva, pte, PG_INVAL);
}
#endif
set_pte(pgva, PG_INVAL);
KASSERT(pmegp->pmeg_vpages > 0);
pmegp->pmeg_vpages--;
}
}
KASSERT(pmegp->pmeg_vpages >= 0);
if (pmegp->pmeg_vpages == 0) {
#ifdef PMAP_DEBUG
if (is_pmeg_wired(pmegp)) {
if (pmap_debug & PMD_WIRING) {
db_printf("pmap: removing wired "
"pmeg: %p\n", pmegp);
Debugger();
}
}
if (pmap_debug & PMD_SEGMAP) {
printf("pmap: set_segmap ctx=%d v=0x%lx "
"old=0x%x new=ff (rm)\n",
pmap->pm_ctxnum, segva,
pmegp->pmeg_index);
}
pmeg_verify_empty(segva);
#endif
set_segmap_allctx(segva, SEGINV);
pmap->pm_segmap[VA_SEGNUM(segva)] = SEGINV;
pmeg_free(pmegp);
}
}
splx(s);
}
int
pmap_fault(struct vm_map *map, vaddr_t va, vm_prot_t ftype)
{
pmap_t pmap;
int rv;
pmap = vm_map_pmap(map);
if (map == kernel_map) {
if (va < virtual_avail) {
return EACCES;
}
} else {
if (pmap_fault_reload(pmap, va, ftype))
return 0;
}
rv = uvm_fault(map, va, ftype);
#ifdef PMAP_DEBUG
if (pmap_debug & PMD_FAULT) {
printf("pmap_fault(%p, 0x%lx, 0x%x) -> 0x%x\n",
map, va, ftype, rv);
}
#endif
return (rv);
}
int
pmap_fault_reload(pmap_t pmap, vaddr_t pgva, vm_prot_t ftype)
{
int rv, s, pte, chkpte, sme;
vaddr_t segva;
pmeg_t pmegp;
if (pgva >= VM_MAXUSER_ADDRESS)
return (0);
if (pmap->pm_segmap == NULL) {
#ifdef PMAP_DEBUG
db_printf("pmap_fault_reload: null segmap\n");
Debugger();
#endif
return (0);
}
if (pmap->pm_segmap[VA_SEGNUM(pgva)] == SEGINV)
return (0);
segva = sun3_trunc_seg(pgva);
chkpte = PG_VALID;
if (ftype & VM_PROT_WRITE)
chkpte |= PG_WRITE;
rv = 0;
s = splvm();
if (!has_context(pmap)) {
context_allocate(pmap);
#ifdef PMAP_DEBUG
if (pmap_debug & PMD_CONTEXT)
printf("pmap_fault(%p) got context %d\n",
pmap, pmap->pm_ctxnum);
#endif
set_context(pmap->pm_ctxnum);
} else {
#ifdef PMAP_DEBUG
if (pmap->pm_ctxnum != get_context()) {
db_printf("pmap_fault_reload: wrong context\n");
Debugger();
set_context(pmap->pm_ctxnum);
}
#endif
}
sme = get_segmap(segva);
if (sme == SEGINV) {
pmegp = pmeg_cache(pmap, segva);
if (pmegp) {
pmap_stats.ps_pmeg_faultin++;
sme = pmegp->pmeg_index;
set_segmap(segva, sme);
pte = get_pte(pgva);
if (pte & chkpte)
rv = 1;
}
}
splx(s);
return (rv);
}
bool
pmap_clear_modify(struct vm_page *pg)
{
paddr_t pa = VM_PAGE_TO_PHYS(pg);
pv_entry_t *head;
u_char *pv_flags;
int s;
bool rv;
pv_flags = pa_to_pvflags(pa);
head = pa_to_pvhead(pa);
s = splvm();
*pv_flags |= pv_syncflags(*head);
rv = *pv_flags & PV_MOD;
*pv_flags &= ~PV_MOD;
splx(s);
return rv;
}
bool
pmap_is_modified(struct vm_page *pg)
{
paddr_t pa = VM_PAGE_TO_PHYS(pg);
pv_entry_t *head;
u_char *pv_flags;
int s;
bool rv;
pv_flags = pa_to_pvflags(pa);
head = pa_to_pvhead(pa);
s = splvm();
if ((*pv_flags & PV_MOD) == 0)
*pv_flags |= pv_syncflags(*head);
rv = (*pv_flags & PV_MOD);
splx(s);
return (rv);
}
bool
pmap_clear_reference(struct vm_page *pg)
{
paddr_t pa = VM_PAGE_TO_PHYS(pg);
pv_entry_t *head;
u_char *pv_flags;
int s;
bool rv;
pv_flags = pa_to_pvflags(pa);
head = pa_to_pvhead(pa);
s = splvm();
*pv_flags |= pv_syncflags(*head);
rv = *pv_flags & PV_REF;
*pv_flags &= ~PV_REF;
splx(s);
return rv;
}
bool
pmap_is_referenced(struct vm_page *pg)
{
paddr_t pa = VM_PAGE_TO_PHYS(pg);
pv_entry_t *head;
u_char *pv_flags;
int s;
bool rv;
pv_flags = pa_to_pvflags(pa);
head = pa_to_pvhead(pa);
s = splvm();
if ((*pv_flags & PV_REF) == 0)
*pv_flags |= pv_syncflags(*head);
rv = (*pv_flags & PV_REF);
splx(s);
return (rv);
}
void
_pmap_switch(pmap_t pmap)
{
set_context(pmap->pm_ctxnum);
ICIA();
}
void
pmap_activate(struct lwp *l)
{
pmap_t pmap = l->l_proc->p_vmspace->vm_map.pmap;
if (l->l_proc == curproc) {
_pmap_switch(pmap);
}
}
void
pmap_deactivate(struct lwp *l)
{
}
void
pmap_unwire(pmap_t pmap, vaddr_t va)
{
int s, sme;
int wiremask, ptenum;
pmeg_t pmegp;
#ifdef PMAP_DEBUG
if (pmap_debug & PMD_WIRING)
printf("pmap_unwire(pmap=%p, va=0x%lx)\n",
pmap, va);
#endif
if (pmap != kernel_pmap) {
#ifdef PMAP_DEBUG
if (pmap_debug & PMD_WIRING) {
db_printf(" (user pmap -- ignored)\n");
Debugger();
}
#endif
return;
}
ptenum = VA_PTE_NUM(va);
wiremask = 1 << ptenum;
s = splvm();
sme = get_segmap(va);
pmegp = pmeg_p(sme);
pmegp->pmeg_wired &= ~wiremask;
splx(s);
}
void
pmap_copy(pmap_t dst_pmap, pmap_t src_pmap, vaddr_t dst_addr, vsize_t len,
vaddr_t src_addr)
{
}
bool
pmap_extract(pmap_t pmap, vaddr_t va, paddr_t *pap)
{
int s, sme, segnum, ptenum, pte;
paddr_t pa;
pte = 0;
s = splvm();
if (pmap == kernel_pmap) {
sme = get_segmap(va);
if (sme != SEGINV)
pte = get_pte(va);
} else {
segnum = VA_SEGNUM(va);
sme = pmap->pm_segmap[segnum];
if (sme != SEGINV) {
ptenum = VA_PTE_NUM(va);
pte = get_pte_pmeg(sme, ptenum);
}
}
splx(s);
if ((pte & PG_VALID) == 0) {
#ifdef PMAP_DEBUG
db_printf("pmap_extract: invalid va=0x%lx\n", va);
Debugger();
#endif
return (false);
}
pa = PG_PA(pte);
#ifdef DIAGNOSTIC
if (pte & PG_TYPE) {
panic("pmap_extract: not main mem, va=0x%lx", va);
}
#endif
if (pap != NULL)
*pap = pa;
return (true);
}
paddr_t
pmap_phys_address(paddr_t addr)
{
return addr;
}
void
pmap_page_protect(struct vm_page *pg, vm_prot_t prot)
{
paddr_t pa = VM_PAGE_TO_PHYS(pg);
int s;
s = splvm();
#ifdef PMAP_DEBUG
if (pmap_debug & PMD_PROTECT)
printf("pmap_page_protect(0x%lx, 0x%x)\n", pa, prot);
#endif
switch (prot) {
case VM_PROT_ALL:
break;
case VM_PROT_READ:
case VM_PROT_READ|VM_PROT_EXECUTE:
pv_changepte(pa, 0, PG_WRITE);
break;
default:
pv_remove_all(pa);
break;
}
splx(s);
}
void
pmap_pinit(pmap_t pmap)
{
pmap_common_init(pmap);
pmap_user_init(pmap);
}
void
pmap_protect(pmap_t pmap, vaddr_t sva, vaddr_t eva, vm_prot_t prot)
{
vaddr_t va, neva;
int segnum;
if (prot & VM_PROT_WRITE)
return;
if ((prot & VM_PROT_READ) == 0) {
pmap_remove(pmap, sva, eva);
return;
}
#ifdef PMAP_DEBUG
if ((pmap_debug & PMD_PROTECT) ||
((sva <= pmap_db_watchva && eva > pmap_db_watchva)))
printf("pmap_protect(%p, 0x%lx, 0x%lx)\n", pmap, sva, eva);
#endif
KASSERT((pmap == kernel_pmap) ?
sva >= virtual_avail && eva < DVMA_MAP_END :
eva <= VM_MAXUSER_ADDRESS);
va = sva;
segnum = VA_SEGNUM(va);
while (va < eva) {
neva = sun3_trunc_seg(va) + NBSG;
if (neva > eva)
neva = eva;
if (pmap->pm_segmap[segnum] != SEGINV)
pmap_protect1(pmap, va, neva);
va = neva;
segnum++;
}
}
void
pmap_protect1(pmap_t pmap, vaddr_t sva, vaddr_t eva)
{
int old_ctx, s, sme;
bool in_ctx;
s = splvm();
#ifdef DIAGNOSTIC
if (sun3_trunc_seg(sva) != sun3_trunc_seg(eva-1))
panic("pmap_protect1: bad range!");
#endif
if (pmap == kernel_pmap) {
sme = get_segmap(sva);
if (sme != SEGINV)
pmap_protect_mmu(pmap, sva, eva);
goto out;
}
old_ctx = INVALID_CONTEXT;
in_ctx = false;
if (has_context(pmap)) {
old_ctx = get_context();
set_context(pmap->pm_ctxnum);
sme = get_segmap(sva);
if (sme != SEGINV)
in_ctx = true;
}
if (in_ctx == true)
pmap_protect_mmu(pmap, sva, eva);
else
pmap_protect_noctx(pmap, sva, eva);
if (old_ctx != INVALID_CONTEXT) {
set_context(old_ctx);
}
out:
splx(s);
}
void
pmap_protect_mmu(pmap_t pmap, vaddr_t sva, vaddr_t eva)
{
vaddr_t pgva;
int pte;
int flush_by_page = 0;
#if defined(HAVECACHE) || defined(DIAGNOSTIC)
vaddr_t segva = sun3_trunc_seg(sva);
#endif
CHECK_SPL();
#ifdef DIAGNOSTIC
if (pmap != kernel_pmap) {
if (pmap->pm_ctxnum != get_context())
panic("pmap_protect_mmu: wrong context");
}
int sme = get_segmap(segva);
if (sme == SEGINV)
panic("pmap_protect_mmu: SEGINV");
if (pmap->pm_segmap && (pmap->pm_segmap[VA_SEGNUM(segva)] != sme))
panic("pmap_protect_mmu: incorrect sme, va=0x%lx", segva);
pmeg_t pmegp = pmeg_p(sme);
if ((pmegp->pmeg_va != segva) ||
(pmegp->pmeg_owner != pmap) ||
(pmegp->pmeg_version != pmap->pm_version))
{
panic("pmap_protect_mmu: bad pmeg=%p", pmegp);
}
if (pmegp->pmeg_vpages < 0)
panic("pmap_protect_mmu: npages corrupted");
if (pmegp->pmeg_vpages == 0)
panic("pmap_protect_mmu: no valid pages?");
#endif
#ifdef HAVECACHE
if (cache_size) {
if (cache_size < (eva - sva)) {
cache_flush_segment(segva);
} else {
flush_by_page = 1;
}
}
#endif
for (pgva = sva; pgva < eva; pgva += PAGE_SIZE) {
pte = get_pte(pgva);
if (pte & PG_VALID) {
if (flush_by_page) {
#ifdef HAVECACHE
cache_flush_page(pgva);
pte = get_pte(pgva);
#endif
}
if (IS_MAIN_MEM(pte)) {
save_modref_bits(pte);
}
pte &= ~(PG_WRITE | PG_MODREF);
set_pte(pgva, pte);
}
}
}
void
pmap_protect_noctx(pmap_t pmap, vaddr_t sva, vaddr_t eva)
{
int old_ctx, pte, sme, segnum;
vaddr_t pgva, segva;
#ifdef DIAGNOSTIC
if (pmap == kernel_pmap)
panic("pmap_protect_noctx: kernel_pmap");
if (pmap->pm_segmap == NULL)
panic("pmap_protect_noctx: null segmap");
#endif
segva = sun3_trunc_seg(sva);
segnum = VA_SEGNUM(segva);
sme = pmap->pm_segmap[segnum];
if (sme == SEGINV)
return;
old_ctx = get_context();
set_context(EMPTY_CONTEXT);
set_segmap(segva, sme);
for (pgva = sva; pgva < eva; pgva += PAGE_SIZE) {
pte = get_pte(pgva);
if (pte & PG_VALID) {
if (IS_MAIN_MEM(pte)) {
save_modref_bits(pte);
}
pte &= ~(PG_WRITE | PG_MODREF);
set_pte(pgva, pte);
}
}
set_segmap(segva, SEGINV);
set_context(old_ctx);
}
void
pmap_remove(pmap_t pmap, vaddr_t sva, vaddr_t eva)
{
vaddr_t va, neva;
int segnum;
#ifdef PMAP_DEBUG
if ((pmap_debug & PMD_REMOVE) ||
((sva <= pmap_db_watchva && eva > pmap_db_watchva)))
printf("pmap_remove(%p, 0x%lx, 0x%lx)\n", pmap, sva, eva);
#endif
KASSERT((pmap == kernel_pmap) ?
sva >= virtual_avail && eva < DVMA_MAP_END :
eva <= VM_MAXUSER_ADDRESS);
va = sva;
segnum = VA_SEGNUM(va);
while (va < eva) {
neva = sun3_trunc_seg(va) + NBSG;
if (neva > eva)
neva = eva;
if (pmap->pm_segmap[segnum] != SEGINV)
pmap_remove1(pmap, va, neva);
va = neva;
segnum++;
}
}
void
pmap_remove1(pmap_t pmap, vaddr_t sva, vaddr_t eva)
{
int old_ctx, s, sme;
bool in_ctx;
s = splvm();
#ifdef DIAGNOSTIC
if (sun3_trunc_seg(sva) != sun3_trunc_seg(eva-1))
panic("pmap_remove1: bad range!");
#endif
if (pmap == kernel_pmap) {
sme = get_segmap(sva);
if (sme != SEGINV)
pmap_remove_mmu(pmap, sva, eva);
goto out;
}
old_ctx = INVALID_CONTEXT;
in_ctx = false;
if (has_context(pmap)) {
old_ctx = get_context();
set_context(pmap->pm_ctxnum);
sme = get_segmap(sva);
if (sme != SEGINV)
in_ctx = true;
}
if (in_ctx == true)
pmap_remove_mmu(pmap, sva, eva);
else
pmap_remove_noctx(pmap, sva, eva);
if (old_ctx != INVALID_CONTEXT) {
set_context(old_ctx);
}
out:
splx(s);
}
void
pmap_remove_mmu(pmap_t pmap, vaddr_t sva, vaddr_t eva)
{
pmeg_t pmegp;
vaddr_t pgva, segva;
int pte, sme;
int flush_by_page = 0;
CHECK_SPL();
#ifdef DIAGNOSTIC
if (pmap != kernel_pmap) {
if (pmap->pm_ctxnum != get_context())
panic("pmap_remove_mmu: wrong context");
}
#endif
segva = sun3_trunc_seg(sva);
sme = get_segmap(segva);
#ifdef DIAGNOSTIC
if (sme == SEGINV)
panic("pmap_remove_mmu: SEGINV");
if (pmap->pm_segmap && (pmap->pm_segmap[VA_SEGNUM(segva)] != sme))
panic("pmap_remove_mmu: incorrect sme, va=0x%lx", segva);
#endif
pmegp = pmeg_p(sme);
#ifdef DIAGNOSTIC
if ((pmegp->pmeg_va != segva) ||
(pmegp->pmeg_owner != pmap) ||
(pmegp->pmeg_version != pmap->pm_version))
{
panic("pmap_remove_mmu: bad pmeg=%p", pmegp);
}
if (pmegp->pmeg_vpages < 0)
panic("pmap_remove_mmu: npages corrupted");
if (pmegp->pmeg_vpages == 0)
panic("pmap_remove_mmu: no valid pages?");
#endif
#ifdef HAVECACHE
if (cache_size) {
if (cache_size < (eva - sva)) {
cache_flush_segment(segva);
} else {
flush_by_page = 1;
}
}
#endif
for (pgva = sva; pgva < eva; pgva += PAGE_SIZE) {
pte = get_pte(pgva);
if (pte & PG_VALID) {
if (flush_by_page) {
#ifdef HAVECACHE
cache_flush_page(pgva);
pte = get_pte(pgva);
#endif
}
if (IS_MAIN_MEM(pte)) {
save_modref_bits(pte);
pv_unlink(pmap, pte, pgva);
}
#ifdef PMAP_DEBUG
if ((pmap_debug & PMD_SETPTE) ||
(pgva == pmap_db_watchva)) {
printf("pmap: set_pte pmap=%p va=0x%lx"
" old=0x%x new=0x%x (rrmmu)\n",
pmap, pgva, pte, PG_INVAL);
}
#endif
set_pte(pgva, PG_INVAL);
KASSERT(pmegp->pmeg_vpages > 0);
pmegp->pmeg_vpages--;
}
}
KASSERT(pmegp->pmeg_vpages >= 0);
if (pmegp->pmeg_vpages == 0) {
#ifdef PMAP_DEBUG
if (is_pmeg_wired(pmegp)) {
if (pmap_debug & PMD_WIRING) {
db_printf("pmap: removing wired pmeg: %p\n",
pmegp);
Debugger();
}
}
if (pmap_debug & PMD_SEGMAP) {
printf("pmap: set_segmap ctx=%d v=0x%lx old=0x%x "
"new=ff (rm)\n",
pmap->pm_ctxnum, segva, pmegp->pmeg_index);
}
pmeg_verify_empty(segva);
#endif
if (kernel_pmap == pmap) {
set_segmap_allctx(segva, SEGINV);
} else {
set_segmap(segva, SEGINV);
}
pmap->pm_segmap[VA_SEGNUM(segva)] = SEGINV;
pmeg_free(pmegp);
}
}
void
pmap_remove_noctx(pmap_t pmap, vaddr_t sva, vaddr_t eva)
{
pmeg_t pmegp;
int old_ctx, pte, sme, segnum;
vaddr_t pgva, segva;
CHECK_SPL();
#ifdef DIAGNOSTIC
if (pmap == kernel_pmap)
panic("pmap_remove_noctx: kernel_pmap");
if (pmap->pm_segmap == NULL)
panic("pmap_remove_noctx: null segmap");
#endif
segva = sun3_trunc_seg(sva);
segnum = VA_SEGNUM(segva);
sme = pmap->pm_segmap[segnum];
if (sme == SEGINV)
return;
pmegp = pmeg_p(sme);
old_ctx = get_context();
set_context(EMPTY_CONTEXT);
set_segmap(segva, sme);
for (pgva = sva; pgva < eva; pgva += PAGE_SIZE) {
pte = get_pte(pgva);
if (pte & PG_VALID) {
if (IS_MAIN_MEM(pte)) {
save_modref_bits(pte);
pv_unlink(pmap, pte, pgva);
}
#ifdef PMAP_DEBUG
if ((pmap_debug & PMD_SETPTE) ||
(pgva == pmap_db_watchva)) {
printf("pmap: set_pte pmap=%p va=0x%lx"
" old=0x%x new=0x%x (rrncx)\n",
pmap, pgva, pte, PG_INVAL);
}
#endif
set_pte(pgva, PG_INVAL);
KASSERT(pmegp->pmeg_vpages > 0);
pmegp->pmeg_vpages--;
}
}
set_segmap(segva, SEGINV);
set_context(old_ctx);
KASSERT(pmegp->pmeg_vpages >= 0);
if (pmegp->pmeg_vpages == 0) {
if (is_pmeg_wired(pmegp)) {
#ifdef PMAP_DEBUG
if (pmap_debug & PMD_WIRING) {
db_printf("pmap: removing wired pmeg: %p\n",
pmegp);
Debugger();
}
#endif
}
pmap->pm_segmap[segnum] = SEGINV;
pmeg_free(pmegp);
}
}
segsz_t
pmap_resident_pages(pmap_t pmap)
{
int i, sme, pages;
pmeg_t pmeg;
if (pmap->pm_segmap == 0)
return (0);
pages = 0;
for (i = 0; i < NUSEG; i++) {
sme = pmap->pm_segmap[i];
if (sme != SEGINV) {
pmeg = pmeg_p(sme);
pages += pmeg->pmeg_vpages;
}
}
return (pages);
}
segsz_t
pmap_wired_pages(pmap_t pmap)
{
int i, mask, sme, pages;
pmeg_t pmeg;
if (pmap->pm_segmap == 0)
return (0);
pages = 0;
for (i = 0; i < NUSEG; i++) {
sme = pmap->pm_segmap[i];
if (sme != SEGINV) {
pmeg = pmeg_p(sme);
mask = 0x8000;
do {
if (pmeg->pmeg_wired & mask)
pages++;
mask = (mask >> 1);
} while (mask);
}
}
return (pages);
}
void
pmap_copy_page(paddr_t src, paddr_t dst)
{
int pte;
int s;
s = splvm();
#ifdef PMAP_DEBUG
if (pmap_debug & PMD_COW)
printf("pmap_copy_page: 0x%lx -> 0x%lx\n", src, dst);
#endif
#ifdef DIAGNOSTIC
if (tmp_vpages_inuse)
panic("pmap_copy_page: vpages inuse");
tmp_vpages_inuse++;
#endif
pte = PG_PERM | PA_PGNUM(src);
set_pte(tmp_vpages[0], pte);
pte = PG_PERM | PA_PGNUM(dst);
set_pte(tmp_vpages[1], pte);
copypage((char *) tmp_vpages[0], (char *) tmp_vpages[1]);
set_pte(tmp_vpages[0], PG_INVAL);
set_pte(tmp_vpages[0], PG_INVAL);
#ifdef DIAGNOSTIC
tmp_vpages_inuse--;
#endif
splx(s);
}
void
pmap_zero_page(paddr_t pa)
{
int pte;
int s;
s = splvm();
#ifdef PMAP_DEBUG
if (pmap_debug & PMD_COW)
printf("pmap_zero_page: 0x%lx\n", pa);
#endif
#ifdef DIAGNOSTIC
if (tmp_vpages_inuse)
panic("pmap_zero_page: vpages inuse");
tmp_vpages_inuse++;
#endif
pte = PG_PERM | PA_PGNUM(pa);
set_pte(tmp_vpages[0], pte);
zeropage((char *) tmp_vpages[0]);
set_pte(tmp_vpages[0], PG_INVAL);
#ifdef DIAGNOSTIC
tmp_vpages_inuse--;
#endif
splx(s);
}
void
pmap_prefer(vaddr_t fo, vaddr_t *va, int td)
{
long d;
d = fo - *va;
d &= SEGOFSET;
if (d == 0) {
return;
}
if (td) {
*va -= SEGOFSET + 1;
}
*va += d;
}
void
pmap_kcore_hdr(struct sun3_kcore_hdr *sh)
{
vaddr_t va;
u_char *cp, *ep;
sh->segshift = SEGSHIFT;
sh->pg_frame = PG_FRAME;
sh->pg_valid = PG_VALID;
va = KERNBASE3;
cp = sh->ksegmap;
ep = cp + sizeof(sh->ksegmap);
do {
*cp = get_segmap(va);
va += NBSG;
cp++;
} while (cp < ep);
}
void
pmap_get_pagemap(int *pt, int off)
{
vaddr_t va, va_end;
int sme, sme_end;
sme = (off >> 6);
sme_end = sme + 128;
va_end = temp_seg_va + NBSG;
do {
set_segmap(temp_seg_va, sme);
va = temp_seg_va;
do {
*pt++ = get_pte(va);
va += PAGE_SIZE;
} while (va < va_end);
sme++;
} while (sme < sme_end);
set_segmap(temp_seg_va, SEGINV);
}
#ifdef DIAGNOSTIC
static int temp_seg_inuse;
#endif
static int
get_pte_pmeg(int pmeg_num, int page_num)
{
vaddr_t va;
int pte;
CHECK_SPL();
#ifdef DIAGNOSTIC
if (temp_seg_inuse)
panic("get_pte_pmeg: temp_seg_inuse");
temp_seg_inuse++;
#endif
va = temp_seg_va;
set_segmap(temp_seg_va, pmeg_num);
va += PAGE_SIZE*page_num;
pte = get_pte(va);
set_segmap(temp_seg_va, SEGINV);
#ifdef DIAGNOSTIC
temp_seg_inuse--;
#endif
return pte;
}
static void
set_pte_pmeg(int pmeg_num, int page_num, int pte)
{
vaddr_t va;
CHECK_SPL();
#ifdef DIAGNOSTIC
if (temp_seg_inuse)
panic("set_pte_pmeg: temp_seg_inuse");
temp_seg_inuse++;
#endif
va = temp_seg_va;
set_segmap(temp_seg_va, pmeg_num);
va += PAGE_SIZE*page_num;
set_pte(va, pte);
set_segmap(temp_seg_va, SEGINV);
#ifdef DIAGNOSTIC
temp_seg_inuse--;
#endif
}
void
pmap_procwr(struct proc *p, vaddr_t va, size_t len)
{
(void)cachectl1(0x80000004, va, len, p);
}
#if defined(DDB) || defined(KGDB)
bool
pmap_db_write_text_enter(vaddr_t pgva, struct pmap_db_write_text_context *ctx)
{
int oldpte, tmppte;
#ifdef HAVECACHE
if (cache_size) {
cache_flush_page(pgva);
}
#endif
oldpte = get_pte(pgva);
if ((oldpte & PG_VALID) == 0) {
return false;
}
tmppte = oldpte;
tmppte |= (PG_WRITE | PG_NC);
set_pte(pgva, tmppte);
ctx->pgva = pgva;
ctx->opte = oldpte;
return true;
}
void
pmap_db_write_text_exit(struct pmap_db_write_text_context *ctx)
{
set_pte(ctx->pgva, ctx->opte);
}
#endif
#ifdef PMAP_DEBUG
void
pmap_print(pmap_t pmap)
{
db_printf(" pm_ctxnum=%d\n", pmap->pm_ctxnum);
db_printf(" pm_version=0x%x\n", pmap->pm_version);
db_printf(" pm_segmap=%p\n", pmap->pm_segmap);
}
void
pmeg_print(pmeg_t pmegp)
{
db_printf("link_next=%p link_prev=%p\n",
TAILQ_NEXT(pmegp, pmeg_link),
TAILQ_PREV(pmegp, pmeg_tailq, pmeg_link));
db_printf("index=0x%x owner=%p own_vers=0x%x\n",
pmegp->pmeg_index, pmegp->pmeg_owner, pmegp->pmeg_version);
db_printf("va=0x%lx wired=0x%x reserved=0x%x vpgs=0x%x qstate=0x%x\n",
pmegp->pmeg_va, pmegp->pmeg_wired,
pmegp->pmeg_reserved, pmegp->pmeg_vpages,
pmegp->pmeg_qstate);
}
void
pv_print(paddr_t pa)
{
pv_entry_t pv;
int idx;
idx = PA_PGNUM(pa);
if (idx >= physmem) {
db_printf("bad address\n");
return;
}
db_printf("pa=0x%lx, flags=0x%x\n",
pa, pv_flags_tbl[idx]);
pv = pv_head_tbl[idx];
while (pv) {
db_printf(" pv_entry %p pmap %p va 0x%lx next %p\n",
pv, pv->pv_pmap, pv->pv_va, pv->pv_next);
pv = pv->pv_next;
}
}
#endif