#include "opt_ddb.h"
#include "opt_kgdb.h"
#include "opt_m68k_arch.h"
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: pmap_motorola.c,v 1.110 2026/07/06 14:33:55 thorpej Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/pool.h>
#include <sys/cpu.h>
#ifndef __HAVE_M68K_BROKEN_RMC
#include <sys/atomic.h>
#endif
#include <machine/pcb.h>
#include <uvm/uvm.h>
#include <uvm/uvm_physseg.h>
#include <m68k/cacheops.h>
#include <m68k/mmu.h>
#if !defined(M68K_MMU_MOTOROLA) && !defined(M68K_MMU_HP)
#error Hit the road, Jack...
#endif
#ifdef DEBUG
#define PDB_FOLLOW 0x0001
#define PDB_INIT 0x0002
#define PDB_ENTER 0x0004
#define PDB_REMOVE 0x0008
#define PDB_CREATE 0x0010
#define PDB_PTPAGE 0x0020
#define PDB_CACHE 0x0040
#define PDB_BITS 0x0080
#define PDB_COLLECT 0x0100
#define PDB_PROTECT 0x0200
#define PDB_SEGTAB 0x0400
#define PDB_MULTIMAP 0x0800
#define PDB_PARANOIA 0x2000
#define PDB_WIRING 0x4000
#define PDB_PVDUMP 0x8000
int debugmap = 0;
int pmapdebug = PDB_PARANOIA;
#define PMAP_DPRINTF(l, x) if (pmapdebug & (l)) printf x
#else
#define PMAP_DPRINTF(l, x)
#endif
#if defined(M68040) || defined(M68060)
#define pmap_ste1(m, v) \
(&((m)->pm_stab[(vaddr_t)(v) >> SG4_SHIFT1]))
#define pmap_ste2(m, v) \
(&((m)->pm_stab[(st_entry_t *)(*(u_int *)pmap_ste1(m, v) & SG4_ADDR1) \
- (m)->pm_stpa + (((v) & SG4_MASK2) >> SG4_SHIFT2)]))
#if defined(M68020) || defined(M68030)
#define pmap_ste(m, v) \
(&((m)->pm_stab[(vaddr_t)(v) \
>> (mmutype == MMU_68040 ? SG4_SHIFT1 : SG_ISHIFT)]))
#define pmap_ste_v(m, v) \
(mmutype == MMU_68040 \
? ((*pmap_ste1(m, v) & SG_V) && \
(*pmap_ste2(m, v) & SG_V)) \
: (*pmap_ste(m, v) & SG_V))
#else
#define pmap_ste(m, v) \
(&((m)->pm_stab[(vaddr_t)(v) >> SG4_SHIFT1]))
#define pmap_ste_v(m, v) \
((*pmap_ste1(m, v) & SG_V) && (*pmap_ste2(m, v) & SG_V))
#endif
#else
#define pmap_ste(m, v) (&((m)->pm_stab[(vaddr_t)(v) >> SG_ISHIFT]))
#define pmap_ste_v(m, v) (*pmap_ste(m, v) & SG_V)
#endif
#define pmap_pte(m, v) (&((m)->pm_ptab[(vaddr_t)(v) >> PG_SHIFT]))
#define pmap_pte_pa(pte) (*(pte) & PG_FRAME)
#define pmap_pte_w(pte) (*(pte) & PG_W)
#define pmap_pte_ci(pte) (*(pte) & PG_CI)
#define pmap_pte_m(pte) (*(pte) & PG_M)
#define pmap_pte_u(pte) (*(pte) & PG_U)
#define pmap_pte_prot(pte) (*(pte) & PG_PROT)
#define pmap_pte_v(pte) (*(pte) & PG_V)
#define pmap_pte_set_w(pte, v) \
if (v) *(pte) |= PG_W; else *(pte) &= ~PG_W
#define pmap_pte_set_prot(pte, v) \
if (v) *(pte) |= PG_PROT; else *(pte) &= ~PG_PROT
#define pmap_pte_w_chg(pte, nw) ((nw) ^ pmap_pte_w(pte))
#define pmap_pte_prot_chg(pte, np) ((np) ^ pmap_pte_prot(pte))
#define pte_prot(m, p) (protection_codes[p])
static u_int protection_codes[8];
struct kpt_page {
struct kpt_page *kpt_next;
vaddr_t kpt_va;
paddr_t kpt_pa;
};
struct kpt_page *kpt_free_list, *kpt_used_list;
struct kpt_page *kpt_pages;
paddr_t Sysseg_pa;
st_entry_t *Sysseg;
pt_entry_t *Sysmap, *Sysptmap;
st_entry_t *Segtabzero, *Segtabzeropa;
#ifndef VM_KERNEL_PT_PAGES
#define VM_KERNEL_PT_PAGES ((vsize_t)2)
#endif
vsize_t Sysptsize = VM_KERNEL_PT_PAGES;
static struct pmap kernel_pmap_store;
struct pmap *const kernel_pmap_ptr = &kernel_pmap_store;
struct vm_map *st_map, *pt_map;
struct vm_map st_map_store, pt_map_store;
vaddr_t lwp0uarea;
paddr_t avail_start;
paddr_t avail_end;
vaddr_t virtual_avail;
vaddr_t virtual_end;
int page_cnt;
bool pmap_initialized = false;
vaddr_t m68k_uptbase = M68K_PTBASE;
vaddr_t kernel_virtual_max = SYSMAP_VA;
struct pv_header {
struct pv_entry pvh_first;
uint32_t pvh_attrs;
};
#define PVH_CI 0x10
#define PVH_PTPAGE 0x20
struct pv_header *pv_table;
TAILQ_HEAD(pv_page_list, pv_page) pv_page_freelist;
int pv_nfree;
#ifdef M68K_EC_VAC
static u_int pmap_aliasmask;
#endif
#if defined(M68040) || defined(M68060)
u_int protostfree;
#endif
pt_entry_t *caddr1_pte;
pt_entry_t *caddr2_pte;
struct pool pmap_pmap_pool;
struct pool pmap_pv_pool;
#define pmap_alloc_pv() pool_get(&pmap_pv_pool, PR_NOWAIT)
#define pmap_free_pv(pv) pool_put(&pmap_pv_pool, (pv))
#define PAGE_IS_MANAGED(pa) (pmap_initialized && uvm_pageismanaged(pa))
static inline struct pv_header *
pa_to_pvh(paddr_t pa)
{
uvm_physseg_t bank = 0;
psize_t pg = 0;
bank = uvm_physseg_find(atop((pa)), &pg);
return &uvm_physseg_get_pmseg(bank)->pvheader[pg];
}
void pmap_remove_mapping(pmap_t, vaddr_t, pt_entry_t *, int,
struct pv_entry **);
bool pmap_testbit(paddr_t, int);
bool pmap_changebit(paddr_t, pt_entry_t, pt_entry_t);
int pmap_enter_ptpage(pmap_t, vaddr_t, bool);
void pmap_ptpage_addref(vaddr_t);
int pmap_ptpage_delref(vaddr_t);
void pmap_pinit(pmap_t);
void pmap_release(pmap_t);
#ifdef DEBUG
void pmap_pvdump(paddr_t);
void pmap_check_wiring(const char *, vaddr_t);
#endif
#define PRM_TFLUSH 0x01
#define PRM_CFLUSH 0x02
#define PRM_KEEPPTPAGE 0x04
#define active_pmap(pm) \
((pm) == pmap_kernel() || (pm) == curproc->p_vmspace->vm_map.pmap)
#define active_user_pmap(pm) \
(curproc && \
(pm) != pmap_kernel() && (pm) == curproc->p_vmspace->vm_map.pmap)
static void (*pmap_load_urp_func)(paddr_t);
static inline void
pmap_load_urp(paddr_t urp)
{
(*pmap_load_urp_func)(urp);
}
#ifdef M68K_EC_VAC
void
pmap_init_vac(size_t vacsize)
{
KASSERT(pmap_aliasmask == 0);
KASSERT(powerof2(vacsize));
pmap_aliasmask = vacsize - 1;
}
#endif
__CTASSERT(PAGE_SIZE != 0);
void *
pmap_bootstrap2(void)
{
uvmexp.pagesize = PAGE_SIZE;
uvm_md_init();
protection_codes[VM_PROT_NONE|VM_PROT_NONE|VM_PROT_NONE] = 0;
protection_codes[VM_PROT_READ|VM_PROT_NONE|VM_PROT_NONE] = PG_RO;
protection_codes[VM_PROT_READ|VM_PROT_NONE|VM_PROT_EXECUTE] = PG_RO;
protection_codes[VM_PROT_NONE|VM_PROT_NONE|VM_PROT_EXECUTE] = PG_RO;
protection_codes[VM_PROT_NONE|VM_PROT_WRITE|VM_PROT_NONE] = PG_RW;
protection_codes[VM_PROT_NONE|VM_PROT_WRITE|VM_PROT_EXECUTE] = PG_RW;
protection_codes[VM_PROT_READ|VM_PROT_WRITE|VM_PROT_NONE] = PG_RW;
protection_codes[VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE] = PG_RW;
pmap_kernel()->pm_stpa = (st_entry_t *)Sysseg_pa;
pmap_kernel()->pm_stab = Sysseg;
pmap_kernel()->pm_ptab = Sysmap;
#if defined(M68040) || defined(M68060)
if (mmutype == MMU_68040)
pmap_kernel()->pm_stfree = protostfree;
#endif
pmap_kernel()->pm_count = 1;
memset((void *)lwp0uarea, 0, USPACE);
uvm_lwp_setuarea(&lwp0, lwp0uarea);
curlwp = &lwp0;
curpcb = lwp_getpcb(&lwp0);
struct trapframe *tf = (struct trapframe *)(lwp0uarea + USPACE) - 1;
tf->tf_sr = PSL_USER;
lwp0.l_md.md_regs = (int *)tf;
setsfc(FC_USERD);
setdfc(FC_USERD);
return tf;
}
void
pmap_virtual_space(vaddr_t *vstartp, vaddr_t *vendp)
{
*vstartp = virtual_avail;
*vendp = virtual_end;
}
void
pmap_init(void)
{
vaddr_t addr, addr2;
vsize_t s;
struct pv_header *pvh;
int rv;
int npages;
uvm_physseg_t bank;
PMAP_DPRINTF(PDB_FOLLOW, ("pmap_init()\n"));
caddr1_pte = pmap_pte(pmap_kernel(), CADDR1);
caddr2_pte = pmap_pte(pmap_kernel(), CADDR2);
PMAP_DPRINTF(PDB_INIT,
("pmap_init: Sysseg %p, Sysmap %p, Sysptmap %p\n",
Sysseg, Sysmap, Sysptmap));
PMAP_DPRINTF(PDB_INIT,
(" pstart %lx, pend %lx, vstart %lx, vend %lx\n",
avail_start, avail_end, virtual_avail, virtual_end));
for (page_cnt = 0, bank = uvm_physseg_get_first();
uvm_physseg_valid_p(bank);
bank = uvm_physseg_get_next(bank))
page_cnt += uvm_physseg_get_end(bank) - uvm_physseg_get_start(bank);
s = M68K_STSIZE;
s += page_cnt * sizeof(struct pv_header);
s = round_page(s);
addr = uvm_km_alloc(kernel_map, s, 0, UVM_KMF_WIRED | UVM_KMF_ZERO);
if (addr == 0)
panic("pmap_init: can't allocate data structures");
Segtabzero = (st_entry_t *)addr;
(void)pmap_extract(pmap_kernel(), addr,
(paddr_t *)(void *)&Segtabzeropa);
addr += M68K_STSIZE;
pv_table = (struct pv_header *) addr;
addr += page_cnt * sizeof(struct pv_header);
PMAP_DPRINTF(PDB_INIT, ("pmap_init: %lx bytes: page_cnt %x s0 %p(%p) "
"tbl %p\n",
s, page_cnt, Segtabzero, Segtabzeropa,
pv_table));
pvh = pv_table;
for (bank = uvm_physseg_get_first();
uvm_physseg_valid_p(bank);
bank = uvm_physseg_get_next(bank)) {
npages = uvm_physseg_get_end(bank) - uvm_physseg_get_start(bank);
uvm_physseg_get_pmseg(bank)->pvheader = pvh;
pvh += npages;
}
npages = uimin(atop(M68K_MAX_KPTSIZE), maxproc+16);
s = ptoa(npages) + round_page(npages * sizeof(struct kpt_page));
addr = 0;
rv = uvm_map(kernel_map, &addr, s, NULL, UVM_UNKNOWN_OFFSET, 0,
UVM_MAPFLAG(UVM_PROT_NONE, UVM_PROT_NONE, UVM_INH_NONE,
UVM_ADV_RANDOM, UVM_FLAG_NOMERGE));
if (rv != 0 || (addr + s) >= (vaddr_t)Sysmap)
panic("pmap_init: kernel PT too small");
uvm_unmap(kernel_map, addr, addr + s);
addr = uvm_km_alloc(kernel_map, s, 0, UVM_KMF_WIRED | UVM_KMF_ZERO);
if (addr == 0)
panic("pmap_init: cannot allocate KPT free list");
s = ptoa(npages);
addr2 = addr + s;
kpt_pages = &((struct kpt_page *)addr2)[npages];
kpt_free_list = NULL;
do {
addr2 -= PAGE_SIZE;
(--kpt_pages)->kpt_next = kpt_free_list;
kpt_free_list = kpt_pages;
kpt_pages->kpt_va = addr2;
(void) pmap_extract(pmap_kernel(), addr2,
(paddr_t *)&kpt_pages->kpt_pa);
} while (addr != addr2);
PMAP_DPRINTF(PDB_INIT, ("pmap_init: KPT: %ld pages from %lx to %lx\n",
atop(s), addr, addr + s));
s = maxproc * M68K_STSIZE;
st_map = uvm_km_suballoc(kernel_map, &addr, &addr2, s, 0, false,
&st_map_store);
addr = m68k_uptbase;
if ((M68K_PTMAXSIZE / M68K_MAX_PTSIZE) < maxproc) {
s = M68K_PTMAXSIZE;
maxproc = (M68K_PTMAXSIZE / M68K_MAX_PTSIZE);
} else
s = (maxproc * M68K_MAX_PTSIZE);
pt_map = uvm_km_suballoc(kernel_map, &addr, &addr2, s, 0,
true, &pt_map_store);
#if defined(M68040) || defined(M68060)
if (mmutype == MMU_68040) {
protostfree = ~l2tobm(0);
for (rv = MAXUL2SIZE; rv < sizeof(protostfree)*NBBY; rv++)
protostfree &= ~l2tobm(rv);
}
#endif
pool_init(&pmap_pmap_pool, sizeof(struct pmap), 0, 0, 0, "pmappl",
&pool_allocator_nointr, IPL_NONE);
pool_init(&pmap_pv_pool, sizeof(struct pv_entry), 0, 0, 0, "pvpl",
&pool_allocator_meta, IPL_NONE);
#ifdef M68060
#if defined(M68020) || defined(M68030) || defined(M68040)
if (cputype == CPU_68060)
#endif
{
struct kpt_page *kptp = kpt_free_list;
paddr_t paddr;
while (kptp) {
pmap_changebit(kptp->kpt_pa, PG_CI,
(pt_entry_t)~PG_CCB);
kptp = kptp->kpt_next;
}
paddr = (paddr_t)Segtabzeropa;
while (paddr < (paddr_t)Segtabzeropa + M68K_STSIZE) {
pmap_changebit(paddr, PG_CI,
(pt_entry_t)~PG_CCB);
paddr += PAGE_SIZE;
}
DCIS();
}
#endif
switch (cputype) {
#if defined(M68020)
case CPU_68020:
#ifdef M68K_MMU_MOTOROLA
if (mmutype == MMU_68851) {
protorp[0] = MMU51_CRP_BITS;
pmap_load_urp_func = mmu_load_urp51;
}
#endif
#ifdef M68K_MMU_HP
if (mmutype == MMU_HP) {
pmap_load_urp_func = mmu_load_urp20hp;
}
#endif
break;
#endif
#if defined(M68030)
case CPU_68030:
protorp[0] = MMU51_CRP_BITS;
pmap_load_urp_func = mmu_load_urp51;
break;
#endif
#if defined(M68040)
case CPU_68040:
pmap_load_urp_func = mmu_load_urp40;
break;
#endif
#if defined(M68060)
case CPU_68060:
pmap_load_urp_func = mmu_load_urp60;
break;
#endif
default:
break;
}
if (pmap_load_urp_func == NULL) {
panic("pmap_init: No mmu_load_*() for cpu=%d mmu=%d",
cputype, mmutype);
}
pmap_initialized = true;
}
pmap_t
pmap_create(void)
{
struct pmap *pmap;
PMAP_DPRINTF(PDB_FOLLOW|PDB_CREATE,
("pmap_create()\n"));
pmap = pool_get(&pmap_pmap_pool, PR_WAITOK);
memset(pmap, 0, sizeof(*pmap));
pmap_pinit(pmap);
return pmap;
}
void
pmap_pinit(struct pmap *pmap)
{
PMAP_DPRINTF(PDB_FOLLOW|PDB_CREATE,
("pmap_pinit(%p)\n", pmap));
pmap->pm_stab = Segtabzero;
pmap->pm_stpa = Segtabzeropa;
#if defined(M68040) || defined(M68060)
#if defined(M68020) || defined(M68030)
if (mmutype == MMU_68040)
#endif
pmap->pm_stfree = protostfree;
#endif
pmap->pm_count = 1;
}
void
pmap_destroy(pmap_t pmap)
{
int count;
PMAP_DPRINTF(PDB_FOLLOW, ("pmap_destroy(%p)\n", pmap));
#ifdef __HAVE_M68K_BROKEN_RMC
count = --pmap->pm_count;
#else
count = atomic_dec_uint_nv(&pmap->pm_count);
#endif
if (count == 0) {
pmap_release(pmap);
pool_put(&pmap_pmap_pool, pmap);
}
}
void
pmap_release(pmap_t pmap)
{
PMAP_DPRINTF(PDB_FOLLOW, ("pmap_release(%p)\n", pmap));
#ifdef notdef
if (pmap->pm_count != 1)
panic("pmap_release count");
#endif
if (pmap->pm_ptab) {
pmap_remove(pmap_kernel(), (vaddr_t)pmap->pm_ptab,
(vaddr_t)pmap->pm_ptab + M68K_MAX_PTSIZE);
uvm_km_pgremove((vaddr_t)pmap->pm_ptab,
(vaddr_t)pmap->pm_ptab + M68K_MAX_PTSIZE);
uvm_km_free(pt_map, (vaddr_t)pmap->pm_ptab,
M68K_MAX_PTSIZE, UVM_KMF_VAONLY);
}
KASSERT(pmap->pm_stab == Segtabzero);
}
void
pmap_reference(pmap_t pmap)
{
PMAP_DPRINTF(PDB_FOLLOW, ("pmap_reference(%p)\n", pmap));
#ifdef __HAVE_M68K_BROKEN_RMC
pmap->pm_count++;
#else
atomic_inc_uint(&pmap->pm_count);
#endif
}
void
pmap_activate(struct lwp *l)
{
pmap_t pmap = l->l_proc->p_vmspace->vm_map.pmap;
PMAP_DPRINTF(PDB_FOLLOW|PDB_SEGTAB,
("pmap_activate(%p)\n", l));
KASSERT(l == curlwp);
if (pmap != pmap_kernel()) {
pmap_load_urp((paddr_t)pmap->pm_stpa);
}
}
void
pmap_deactivate(struct lwp *l)
{
}
void
pmap_remove(pmap_t pmap, vaddr_t sva, vaddr_t eva)
{
vaddr_t nssva;
pt_entry_t *pte;
int flags;
#ifdef M68K_EC_VAC
bool firstpage = true, needcflush = false;
#endif
PMAP_DPRINTF(PDB_FOLLOW|PDB_REMOVE|PDB_PROTECT,
("pmap_remove(%p, %lx, %lx)\n", pmap, sva, eva));
flags = active_pmap(pmap) ? PRM_TFLUSH : 0;
while (sva < eva) {
nssva = m68k_trunc_seg(sva) + NBSEG;
if (nssva == 0 || nssva > eva)
nssva = eva;
pte = pmap_pte(pmap, sva);
while (sva < nssva) {
if (!pmap_ste_v(pmap, sva)) {
sva = nssva;
break;
}
if (pmap_pte_v(pte)) {
#ifdef M68K_EC_VAC
if (pmap_aliasmask) {
if (firstpage) {
DCIS();
}
if (!needcflush && !pmap_pte_ci(pte))
needcflush = true;
}
firstpage = false;
#endif
pmap_remove_mapping(pmap, sva, pte, flags, NULL);
}
pte++;
sva += PAGE_SIZE;
}
}
#ifdef M68K_EC_VAC
if (firstpage)
return;
if (pmap_aliasmask && !active_user_pmap(pmap))
needcflush = false;
if (needcflush) {
if (pmap == pmap_kernel()) {
DCIS();
} else {
DCIU();
}
}
#endif
}
void
pmap_page_protect(struct vm_page *pg, vm_prot_t prot)
{
paddr_t pa = VM_PAGE_TO_PHYS(pg);
struct pv_header *pvh;
struct pv_entry *pv;
pt_entry_t *pte;
int s;
#ifdef DEBUG
if ((pmapdebug & (PDB_FOLLOW|PDB_PROTECT)) ||
(prot == VM_PROT_NONE && (pmapdebug & PDB_REMOVE)))
printf("pmap_page_protect(%p, %x)\n", pg, prot);
#endif
switch (prot) {
case VM_PROT_READ|VM_PROT_WRITE:
case VM_PROT_ALL:
return;
case VM_PROT_READ:
case VM_PROT_READ|VM_PROT_EXECUTE:
pmap_changebit(pa, PG_RO, ~0);
return;
default:
break;
}
pvh = pa_to_pvh(pa);
pv = &pvh->pvh_first;
s = splvm();
while (pv->pv_pmap != NULL) {
pte = pmap_pte(pv->pv_pmap, pv->pv_va);
#ifdef DEBUG
if (!pmap_ste_v(pv->pv_pmap, pv->pv_va) ||
pmap_pte_pa(pte) != pa)
panic("pmap_page_protect: bad mapping");
#endif
pmap_remove_mapping(pv->pv_pmap, pv->pv_va,
pte, PRM_TFLUSH|PRM_CFLUSH, NULL);
}
splx(s);
}
void
pmap_protect(pmap_t pmap, vaddr_t sva, vaddr_t eva, vm_prot_t prot)
{
vaddr_t nssva;
pt_entry_t *pte;
bool firstpage __unused, needtflush;
int isro;
PMAP_DPRINTF(PDB_FOLLOW|PDB_PROTECT,
("pmap_protect(%p, %lx, %lx, %x)\n",
pmap, sva, eva, prot));
#ifdef PMAPSTATS
protect_stats.calls++;
#endif
if ((prot & VM_PROT_READ) == VM_PROT_NONE) {
pmap_remove(pmap, sva, eva);
return;
}
isro = pte_prot(pmap, prot);
needtflush = active_pmap(pmap);
firstpage = true;
while (sva < eva) {
nssva = m68k_trunc_seg(sva) + NBSEG;
if (nssva == 0 || nssva > eva)
nssva = eva;
if (!pmap_ste_v(pmap, sva)) {
sva = nssva;
continue;
}
pte = pmap_pte(pmap, sva);
while (sva < nssva) {
if (pmap_pte_v(pte) && pmap_pte_prot_chg(pte, isro)) {
#ifdef M68K_EC_VAC
if (firstpage && pmap_aliasmask)
DCIS();
#endif
#if defined(M68040) || defined(M68060)
#if defined(M68020) || defined(M68030)
if (isro && mmutype == MMU_68040)
#else
if (isro)
#endif
{
paddr_t pa = pmap_pte_pa(pte);
DCFP(pa);
ICPP(pa);
}
#endif
pmap_pte_set_prot(pte, isro);
if (needtflush)
TBIS(sva);
firstpage = false;
}
pte++;
sva += PAGE_SIZE;
}
}
}
int
pmap_enter(pmap_t pmap, vaddr_t va, paddr_t pa, vm_prot_t prot, u_int flags)
{
pt_entry_t *pte;
struct pv_entry *opv = NULL;
int npte;
paddr_t opa;
bool cacheable = true;
bool checkpv = true;
bool wired = (flags & PMAP_WIRED) != 0;
bool can_fail = (flags & PMAP_CANFAIL) != 0;
PMAP_DPRINTF(PDB_FOLLOW|PDB_ENTER,
("pmap_enter(%p, %lx, %lx, %x, %x)\n",
pmap, va, pa, prot, wired));
#ifdef DIAGNOSTIC
if (pmap == pmap_kernel() &&
(va == (vaddr_t)CADDR1 || va == (vaddr_t)CADDR2))
panic("pmap_enter: used for CADDR1 or CADDR2");
#endif
if (pmap->pm_ptab == NULL) {
pmap->pm_ptab = (pt_entry_t *)
uvm_km_alloc(pt_map, M68K_MAX_PTSIZE, 0,
UVM_KMF_VAONLY |
(can_fail ? UVM_KMF_NOWAIT : UVM_KMF_WAITVA));
if (pmap->pm_ptab == NULL)
return ENOMEM;
}
if (!pmap_ste_v(pmap, va)) {
int err = pmap_enter_ptpage(pmap, va, can_fail);
if (err)
return err;
}
pa = m68k_trunc_page(pa);
pte = pmap_pte(pmap, va);
opa = pmap_pte_pa(pte);
PMAP_DPRINTF(PDB_ENTER, ("enter: pte %p, *pte %x\n", pte, *pte));
if (opa == pa) {
if (pmap_pte_w_chg(pte, wired ? PG_W : 0)) {
PMAP_DPRINTF(PDB_ENTER,
("enter: wiring change -> %x\n", wired));
if (wired)
pmap->pm_stats.wired_count++;
else
pmap->pm_stats.wired_count--;
}
checkpv = false;
if (pmap_pte_ci(pte))
cacheable = false;
goto validate;
}
if (opa) {
PMAP_DPRINTF(PDB_ENTER,
("enter: removing old mapping %lx\n", va));
pmap_remove_mapping(pmap, va, pte,
PRM_TFLUSH|PRM_CFLUSH|PRM_KEEPPTPAGE, &opv);
}
if (pmap != pmap_kernel())
pmap_ptpage_addref(trunc_page((vaddr_t)pte));
if (PAGE_IS_MANAGED(pa)) {
struct pv_header *pvh;
struct pv_entry *pv, *npv;
int s;
pvh = pa_to_pvh(pa);
pv = &pvh->pvh_first;
s = splvm();
PMAP_DPRINTF(PDB_ENTER,
("enter: pv at %p: %lx/%p/%p\n",
pv, pv->pv_va, pv->pv_pmap, pv->pv_next));
if (pv->pv_pmap == NULL) {
pv->pv_va = va;
pv->pv_pmap = pmap;
pv->pv_next = NULL;
pv->pv_ptste = NULL;
pv->pv_ptpmap = NULL;
pvh->pvh_attrs = 0;
}
else {
#ifdef DEBUG
for (npv = pv; npv; npv = npv->pv_next)
if (pmap == npv->pv_pmap && va == npv->pv_va)
panic("pmap_enter: already in pv_tab");
#endif
if (opv != NULL) {
npv = opv;
opv = NULL;
} else {
npv = pmap_alloc_pv();
}
KASSERT(npv != NULL);
npv->pv_va = va;
npv->pv_pmap = pmap;
npv->pv_next = pv->pv_next;
npv->pv_ptste = NULL;
npv->pv_ptpmap = NULL;
pv->pv_next = npv;
#ifdef M68K_EC_VAC
if (pmap_aliasmask) {
if (pvh->pvh_attrs & PVH_CI) {
PMAP_DPRINTF(PDB_CACHE,
("enter: pa %lx already CI'ed\n",
pa));
checkpv = cacheable = false;
} else if (npv->pv_next ||
((pmap == pv->pv_pmap ||
pmap == pmap_kernel() ||
pv->pv_pmap == pmap_kernel()) &&
((pv->pv_va & pmap_aliasmask) !=
(va & pmap_aliasmask)))) {
PMAP_DPRINTF(PDB_CACHE,
("enter: pa %lx CI'ing all\n",
pa));
cacheable = false;
pvh->pvh_attrs |= PVH_CI;
}
}
#endif
}
#ifdef DIAGNOSTIC
if ((flags & VM_PROT_ALL) & ~prot)
panic("pmap_enter: access_type exceeds prot");
#endif
if (flags & VM_PROT_WRITE)
pvh->pvh_attrs |= (PG_U|PG_M);
else if (flags & VM_PROT_ALL)
pvh->pvh_attrs |= PG_U;
splx(s);
}
else if (pmap_initialized) {
checkpv = cacheable = false;
}
pmap->pm_stats.resident_count++;
if (wired)
pmap->pm_stats.wired_count++;
validate:
#ifdef M68K_EC_VAC
if (pmap_aliasmask)
DCIS();
#endif
npte = pa | pte_prot(pmap, prot) | (*pte & (PG_M|PG_U)) | PG_V;
if (wired)
npte |= PG_W;
if (!checkpv && !cacheable)
#if defined(M68040) || defined(M68060)
#if defined(M68020) || defined(M68030)
npte |= (mmutype == MMU_68040 ? PG_CIN : PG_CI);
#else
npte |= PG_CIN;
#endif
#else
npte |= PG_CI;
#endif
#if defined(M68040) || defined(M68060)
#if defined(M68020) || defined(M68030)
else if (mmutype == MMU_68040 && (npte & (PG_PROT|PG_CI)) == PG_RW)
#else
else if ((npte & (PG_PROT|PG_CI)) == PG_RW)
#endif
npte |= PG_CCB;
#endif
PMAP_DPRINTF(PDB_ENTER, ("enter: new pte value %x\n", npte));
wired = ((*pte ^ npte) == PG_W);
#if defined(M68040) || defined(M68060)
#if defined(M68020) || defined(M68030)
if (mmutype == MMU_68040 && !wired)
#else
if (!wired)
#endif
{
DCFP(pa);
ICPP(pa);
}
#endif
*pte = npte;
if (!wired && active_pmap(pmap))
TBIS(va);
#ifdef M68K_EC_VAC
if (checkpv && !cacheable) {
pmap_changebit(pa, PG_CI, ~0);
DCIA();
#ifdef DEBUG
if ((pmapdebug & (PDB_CACHE|PDB_PVDUMP)) ==
(PDB_CACHE|PDB_PVDUMP))
pmap_pvdump(pa);
#endif
}
#endif
#ifdef DEBUG
if ((pmapdebug & PDB_WIRING) && pmap != pmap_kernel())
pmap_check_wiring("enter", trunc_page((vaddr_t)pte));
#endif
if (opv != NULL)
pmap_free_pv(opv);
return 0;
}
void
pmap_kenter_pa(vaddr_t va, paddr_t pa, vm_prot_t prot, u_int flags)
{
pmap_t pmap = pmap_kernel();
pt_entry_t *pte;
int s, npte;
PMAP_DPRINTF(PDB_FOLLOW|PDB_ENTER,
("pmap_kenter_pa(%lx, %lx, %x)\n", va, pa, prot));
if (!pmap_ste_v(pmap, va)) {
s = splvm();
pmap_enter_ptpage(pmap, va, false);
splx(s);
}
pa = m68k_trunc_page(pa);
pte = pmap_pte(pmap, va);
PMAP_DPRINTF(PDB_ENTER, ("enter: pte %p, *pte %x\n", pte, *pte));
KASSERT(!pmap_pte_v(pte));
pmap->pm_stats.resident_count++;
pmap->pm_stats.wired_count++;
npte = pa | pte_prot(pmap, prot) | PG_V | PG_W;
if (flags & PMAP_NOCACHE) {
npte |= PG_CI;
}
#if defined(M68040) || defined(M68060)
#if defined(M68020) || defined(M68030)
if (mmutype == MMU_68040 && (npte & (PG_PROT|PG_CI)) == PG_RW)
#else
if ((npte & (PG_PROT|PG_CI)) == PG_RW)
#endif
npte |= PG_CCB;
if (mmutype == MMU_68040) {
DCFP(pa);
ICPP(pa);
}
#endif
*pte = npte;
TBIS(va);
}
void
pmap_kremove(vaddr_t va, vsize_t size)
{
pmap_t pmap = pmap_kernel();
pt_entry_t *pte;
vaddr_t nssva;
vaddr_t eva = va + size;
#ifdef M68K_EC_VAC
bool firstpage, needcflush;
#endif
PMAP_DPRINTF(PDB_FOLLOW|PDB_REMOVE|PDB_PROTECT,
("pmap_kremove(%lx, %lx)\n", va, size));
#ifdef M68K_EC_VAC
firstpage = true;
needcflush = false;
#endif
while (va < eva) {
nssva = m68k_trunc_seg(va) + NBSEG;
if (nssva == 0 || nssva > eva)
nssva = eva;
if (!pmap_ste_v(pmap, va)) {
va = nssva;
continue;
}
pte = pmap_pte(pmap, va);
while (va < nssva) {
if (!pmap_pte_v(pte)) {
pte++;
va += PAGE_SIZE;
continue;
}
#ifdef M68K_EC_VAC
if (pmap_aliasmask) {
if (firstpage) {
DCIS();
firstpage = false;
}
needcflush = true;
}
#endif
pmap->pm_stats.wired_count--;
pmap->pm_stats.resident_count--;
*pte = PG_NV;
TBIS(va);
pte++;
va += PAGE_SIZE;
}
}
#ifdef M68K_EC_VAC
if (pmap_aliasmask && !active_user_pmap(pmap))
needcflush = false;
if (needcflush) {
if (pmap == pmap_kernel()) {
DCIS();
} else {
DCIU();
}
}
#endif
}
void
pmap_unwire(pmap_t pmap, vaddr_t va)
{
pt_entry_t *pte;
PMAP_DPRINTF(PDB_FOLLOW,
("pmap_unwire(%p, %lx)\n", pmap, va));
pte = pmap_pte(pmap, va);
if (pmap_pte_w_chg(pte, 0)) {
pmap_pte_set_w(pte, false);
pmap->pm_stats.wired_count--;
}
}
bool
pmap_extract(pmap_t pmap, vaddr_t va, paddr_t *pap)
{
paddr_t pa;
u_int pte;
PMAP_DPRINTF(PDB_FOLLOW,
("pmap_extract(%p, %lx) -> ", pmap, va));
if (pmap_ste_v(pmap, va)) {
pte = *(u_int *)pmap_pte(pmap, va);
if (pte) {
pa = (pte & PG_FRAME) | (va & ~PG_FRAME);
if (pap != NULL)
*pap = pa;
#ifdef DEBUG
if (pmapdebug & PDB_FOLLOW)
printf("%lx\n", pa);
#endif
return true;
}
}
#ifdef DEBUG
if (pmapdebug & PDB_FOLLOW)
printf("failed\n");
#endif
return false;
}
paddr_t
vtophys(vaddr_t va)
{
paddr_t pa;
if (pmap_extract(pmap_kernel(), va, &pa))
return pa;
KASSERT(0);
return (paddr_t) -1;
}
void
pmap_copy(pmap_t dst_pmap, pmap_t src_pmap, vaddr_t dst_addr, vsize_t len,
vaddr_t src_addr)
{
PMAP_DPRINTF(PDB_FOLLOW,
("pmap_copy(%p, %p, %lx, %lx, %lx)\n",
dst_pmap, src_pmap, dst_addr, len, src_addr));
}
static inline void
pmap_collect1(pmap_t pmap, paddr_t startpa, paddr_t endpa)
{
paddr_t pa;
struct pv_header *pvh;
struct pv_entry *pv;
pt_entry_t *pte;
paddr_t kpa;
#ifdef DEBUG
st_entry_t *ste;
int opmapdebug = 0;
#endif
for (pa = startpa; pa < endpa; pa += PAGE_SIZE) {
struct kpt_page *kpt, **pkpt;
pvh = pa_to_pvh(pa);
pv = &pvh->pvh_first;
if (pv->pv_pmap != pmap_kernel() ||
!(pvh->pvh_attrs & PVH_PTPAGE))
continue;
do {
if (pv->pv_ptste && pv->pv_ptpmap == pmap_kernel())
break;
} while ((pv = pv->pv_next));
if (pv == NULL)
continue;
#ifdef DEBUG
if (pv->pv_va < (vaddr_t)Sysmap ||
pv->pv_va >= (vaddr_t)Sysmap + M68K_MAX_PTSIZE) {
printf("collect: kernel PT VA out of range\n");
pmap_pvdump(pa);
continue;
}
#endif
pte = (pt_entry_t *)(pv->pv_va + PAGE_SIZE);
while (--pte >= (pt_entry_t *)pv->pv_va && *pte == PG_NV)
;
if (pte >= (pt_entry_t *)pv->pv_va)
continue;
#ifdef DEBUG
if (pmapdebug & (PDB_PTPAGE|PDB_COLLECT)) {
printf("collect: freeing KPT page at %lx (ste %x@%p)\n",
pv->pv_va, *pv->pv_ptste, pv->pv_ptste);
opmapdebug = pmapdebug;
pmapdebug |= PDB_PTPAGE;
}
ste = pv->pv_ptste;
#endif
if (!pmap_extract(pmap, pv->pv_va, &kpa)) {
printf("collect: freeing KPT page at %lx (ste %x@%p)\n",
pv->pv_va, *pv->pv_ptste, pv->pv_ptste);
panic("pmap_collect: mapping not found");
}
pmap_remove_mapping(pmap, pv->pv_va, NULL,
PRM_TFLUSH|PRM_CFLUSH, NULL);
for (pkpt = &kpt_used_list, kpt = *pkpt;
kpt != NULL;
pkpt = &kpt->kpt_next, kpt = *pkpt)
if (kpt->kpt_pa == kpa)
break;
#ifdef DEBUG
if (kpt == NULL)
panic("pmap_collect: lost a KPT page");
if (pmapdebug & (PDB_PTPAGE|PDB_COLLECT))
printf("collect: %lx (%lx) to free list\n",
kpt->kpt_va, kpa);
#endif
*pkpt = kpt->kpt_next;
kpt->kpt_next = kpt_free_list;
kpt_free_list = kpt;
#ifdef DEBUG
if (pmapdebug & (PDB_PTPAGE|PDB_COLLECT))
pmapdebug = opmapdebug;
if (*ste != SG_NV)
printf("collect: kernel STE at %p still valid (%x)\n",
ste, *ste);
ste = &Sysptmap[ste - pmap_ste(pmap_kernel(), 0)];
if (*ste != SG_NV)
printf("collect: kernel PTmap at %p still valid (%x)\n",
ste, *ste);
#endif
}
}
static void
pmap_collect(void)
{
int s;
uvm_physseg_t bank;
s = splvm();
for (bank = uvm_physseg_get_first();
uvm_physseg_valid_p(bank);
bank = uvm_physseg_get_next(bank)) {
pmap_collect1(pmap_kernel(), ptoa(uvm_physseg_get_start(bank)),
ptoa(uvm_physseg_get_end(bank)));
}
splx(s);
}
void
pmap_zero_page(paddr_t phys)
{
int npte;
PMAP_DPRINTF(PDB_FOLLOW, ("pmap_zero_page(%lx)\n", phys));
npte = phys | PG_V;
#ifdef M68K_EC_VAC
if (pmap_aliasmask) {
npte |= PG_CI;
}
#endif
#if defined(M68040) || defined(M68060)
#if defined(M68020) || defined(M68030)
if (mmutype == MMU_68040)
#endif
{
npte |= PG_CCB;
}
#endif
*caddr1_pte = npte;
TBIS((vaddr_t)CADDR1);
zeropage(CADDR1);
#ifdef DEBUG
*caddr1_pte = PG_NV;
TBIS((vaddr_t)CADDR1);
#endif
}
void
pmap_copy_page(paddr_t src, paddr_t dst)
{
pt_entry_t npte1, npte2;
PMAP_DPRINTF(PDB_FOLLOW, ("pmap_copy_page(%lx, %lx)\n", src, dst));
npte1 = src | PG_RO | PG_V;
npte2 = dst | PG_V;
#ifdef M68K_EC_VAC
if (pmap_aliasmask) {
npte1 |= PG_CI;
npte2 |= PG_CI;
}
#endif
#if defined(M68040) || defined(M68060)
#if defined(M68020) || defined(M68030)
if (mmutype == MMU_68040)
#endif
{
npte1 |= PG_CCB;
npte2 |= PG_CCB;
}
#endif
*caddr1_pte = npte1;
TBIS((vaddr_t)CADDR1);
*caddr2_pte = npte2;
TBIS((vaddr_t)CADDR2);
copypage(CADDR1, CADDR2);
#ifdef DEBUG
*caddr1_pte = PG_NV;
TBIS((vaddr_t)CADDR1);
*caddr2_pte = PG_NV;
TBIS((vaddr_t)CADDR2);
#endif
}
bool
pmap_clear_modify(struct vm_page *pg)
{
paddr_t pa = VM_PAGE_TO_PHYS(pg);
PMAP_DPRINTF(PDB_FOLLOW, ("pmap_clear_modify(%p)\n", pg));
return pmap_changebit(pa, 0, (pt_entry_t)~PG_M);
}
bool
pmap_clear_reference(struct vm_page *pg)
{
paddr_t pa = VM_PAGE_TO_PHYS(pg);
PMAP_DPRINTF(PDB_FOLLOW, ("pmap_clear_reference(%p)\n", pg));
return pmap_changebit(pa, 0, (pt_entry_t)~PG_U);
}
bool
pmap_is_referenced(struct vm_page *pg)
{
paddr_t pa = VM_PAGE_TO_PHYS(pg);
return pmap_testbit(pa, PG_U);
}
bool
pmap_is_modified(struct vm_page *pg)
{
paddr_t pa = VM_PAGE_TO_PHYS(pg);
return pmap_testbit(pa, PG_M);
}
paddr_t
pmap_phys_address(paddr_t ppn)
{
return m68k_ptob(ppn);
}
#ifdef M68K_EC_VAC
void
pmap_prefer(vaddr_t foff, vaddr_t *vap)
{
vaddr_t va;
vsize_t d;
#ifdef M68K_MMU_MOTOROLA
if (pmap_aliasmask)
#endif
{
va = *vap;
d = foff - va;
d &= pmap_aliasmask;
*vap = va + d;
}
}
#endif
void
pmap_remove_mapping(pmap_t pmap, vaddr_t va, pt_entry_t *pte, int flags,
struct pv_entry **opvp)
{
paddr_t pa;
struct pv_header *pvh;
struct pv_entry *pv, *npv, *opv = NULL;
struct pmap *ptpmap;
st_entry_t *ste;
int s, bits;
#ifdef DEBUG
pt_entry_t opte;
#endif
PMAP_DPRINTF(PDB_FOLLOW|PDB_REMOVE|PDB_PROTECT,
("pmap_remove_mapping(%p, %lx, %p, %x, %p)\n",
pmap, va, pte, flags, opvp));
if (pte == NULL) {
pte = pmap_pte(pmap, va);
if (*pte == PG_NV)
return;
}
#ifdef M68K_EC_VAC
if (pmap_aliasmask && (flags & PRM_CFLUSH)) {
DCIS();
if (active_user_pmap(pmap) && !pmap_pte_ci(pte)) {
DCIU();
}
}
#endif
pa = pmap_pte_pa(pte);
#ifdef DEBUG
opte = *pte;
#endif
if (pmap_pte_w(pte))
pmap->pm_stats.wired_count--;
pmap->pm_stats.resident_count--;
#if defined(M68040) || defined(M68060)
#if defined(M68020) || defined(M68030)
if (mmutype == MMU_68040)
#endif
if ((flags & PRM_CFLUSH)) {
DCFP(pa);
ICPP(pa);
}
#endif
PMAP_DPRINTF(PDB_REMOVE, ("remove: invalidating pte at %p\n", pte));
bits = *pte & (PG_U|PG_M);
*pte = PG_NV;
if ((flags & PRM_TFLUSH) && active_pmap(pmap))
TBIS(va);
if (pmap != pmap_kernel()) {
vaddr_t ptpva = trunc_page((vaddr_t)pte);
int refs = pmap_ptpage_delref(ptpva);
#ifdef DEBUG
if (pmapdebug & PDB_WIRING)
pmap_check_wiring("remove", ptpva);
#endif
if (refs == 0 && (flags & PRM_KEEPPTPAGE) == 0) {
#ifdef DIAGNOSTIC
struct pv_header *ptppvh;
struct pv_entry *ptppv;
#endif
paddr_t ptppa;
ptppa = pmap_pte_pa(pmap_pte(pmap_kernel(), ptpva));
#ifdef DIAGNOSTIC
if (PAGE_IS_MANAGED(ptppa) == 0)
panic("pmap_remove_mapping: unmanaged PT page");
ptppvh = pa_to_pvh(ptppa);
ptppv = &ptppvh->pvh_first;
if (ptppv->pv_ptste == NULL)
panic("pmap_remove_mapping: ptste == NULL");
if (ptppv->pv_pmap != pmap_kernel() ||
ptppv->pv_va != ptpva ||
ptppv->pv_next != NULL)
panic("pmap_remove_mapping: "
"bad PT page pmap %p, va 0x%lx, next %p",
ptppv->pv_pmap, ptppv->pv_va,
ptppv->pv_next);
#endif
pmap_remove_mapping(pmap_kernel(), ptpva,
NULL, PRM_TFLUSH|PRM_CFLUSH, NULL);
rw_enter(uvm_kernel_object->vmobjlock, RW_WRITER);
uvm_pagefree(PHYS_TO_VM_PAGE(ptppa));
rw_exit(uvm_kernel_object->vmobjlock);
PMAP_DPRINTF(PDB_REMOVE|PDB_PTPAGE,
("remove: PT page 0x%lx (0x%lx) freed\n",
ptpva, ptppa));
}
}
if (PAGE_IS_MANAGED(pa) == 0)
return;
pvh = pa_to_pvh(pa);
pv = &pvh->pvh_first;
ste = NULL;
s = splvm();
if (pmap == pv->pv_pmap && va == pv->pv_va) {
ste = pv->pv_ptste;
ptpmap = pv->pv_ptpmap;
npv = pv->pv_next;
if (npv) {
*pv = *npv;
opv = npv;
} else
pv->pv_pmap = NULL;
} else {
for (npv = pv->pv_next; npv; npv = npv->pv_next) {
if (pmap == npv->pv_pmap && va == npv->pv_va)
break;
pv = npv;
}
#ifdef DEBUG
if (npv == NULL)
panic("pmap_remove: PA not in pv_tab");
#endif
ste = npv->pv_ptste;
ptpmap = npv->pv_ptpmap;
pv->pv_next = npv->pv_next;
opv = npv;
pvh = pa_to_pvh(pa);
pv = &pvh->pvh_first;
}
#ifdef M68K_EC_VAC
if (pmap_aliasmask &&
pv->pv_pmap && pv->pv_next == NULL && (pvh->pvh_attrs & PVH_CI)) {
PMAP_DPRINTF(PDB_CACHE,
("remove: clearing CI for pa %lx\n", pa));
pvh->pvh_attrs &= ~PVH_CI;
pmap_changebit(pa, 0, (pt_entry_t)~PG_CI);
#ifdef DEBUG
if ((pmapdebug & (PDB_CACHE|PDB_PVDUMP)) ==
(PDB_CACHE|PDB_PVDUMP))
pmap_pvdump(pa);
#endif
}
#endif
if (ste) {
PMAP_DPRINTF(PDB_REMOVE|PDB_PTPAGE,
("remove: ste was %x@%p pte was %x@%p\n",
*ste, ste, opte, pmap_pte(pmap, va)));
#if defined(M68040) || defined(M68060)
#if defined(M68020) || defined(M68030)
if (mmutype == MMU_68040)
#endif
{
st_entry_t *este = &ste[NPTEPG/SG4_LEV3SIZE];
while (ste < este)
*ste++ = SG_NV;
#ifdef DEBUG
ste -= NPTEPG/SG4_LEV3SIZE;
#endif
}
#if defined(M68020) || defined(M68030)
else
#endif
#endif
#if defined(M68020) || defined(M68030)
*ste = SG_NV;
#endif
if (ptpmap != pmap_kernel()) {
PMAP_DPRINTF(PDB_REMOVE|PDB_SEGTAB,
("remove: stab %p, refcnt %d\n",
ptpmap->pm_stab, ptpmap->pm_sref - 1));
#ifdef DEBUG
if ((pmapdebug & PDB_PARANOIA) &&
ptpmap->pm_stab !=
(st_entry_t *)trunc_page((vaddr_t)ste))
panic("remove: bogus ste");
#endif
if (--(ptpmap->pm_sref) == 0) {
PMAP_DPRINTF(PDB_REMOVE|PDB_SEGTAB,
("remove: free stab %p\n",
ptpmap->pm_stab));
uvm_km_free(st_map, (vaddr_t)ptpmap->pm_stab,
M68K_STSIZE, UVM_KMF_WIRED);
ptpmap->pm_stab = Segtabzero;
ptpmap->pm_stpa = Segtabzeropa;
#if defined(M68040) || defined(M68060)
#if defined(M68020) || defined(M68030)
if (mmutype == MMU_68040)
#endif
ptpmap->pm_stfree = protostfree;
#endif
if (active_user_pmap(ptpmap)) {
pmap_load_urp((paddr_t)ptpmap->pm_stpa);
}
}
}
pvh->pvh_attrs &= ~PVH_PTPAGE;
ptpmap->pm_ptpages--;
}
pvh->pvh_attrs |= bits;
splx(s);
if (opvp != NULL)
*opvp = opv;
else if (opv != NULL)
pmap_free_pv(opv);
}
bool
pmap_testbit(paddr_t pa, int bit)
{
struct pv_header *pvh;
struct pv_entry *pv;
pt_entry_t *pte;
int s;
pvh = pa_to_pvh(pa);
pv = &pvh->pvh_first;
s = splvm();
if (pvh->pvh_attrs & bit) {
splx(s);
return true;
}
#ifdef M68K_EC_VAC
if (pmap_aliasmask && (bit & (PG_U|PG_M)))
DCIS();
#endif
if (pv->pv_pmap != NULL) {
for (; pv; pv = pv->pv_next) {
pte = pmap_pte(pv->pv_pmap, pv->pv_va);
if (*pte & bit) {
pvh->pvh_attrs |= bit;
splx(s);
return true;
}
}
}
splx(s);
return false;
}
bool
pmap_changebit(paddr_t pa, pt_entry_t set, pt_entry_t mask)
{
struct pv_header *pvh;
struct pv_entry *pv;
pt_entry_t *pte, npte;
vaddr_t va;
int s;
#if defined(M68K_EC_VAC) || defined(M68040) || defined(M68060)
bool firstpage = true;
#endif
bool r;
PMAP_DPRINTF(PDB_BITS,
("pmap_changebit(%lx, %x, %x)\n", pa, set, mask));
pvh = pa_to_pvh(pa);
pv = &pvh->pvh_first;
s = splvm();
r = (pvh->pvh_attrs & ~mask) != 0;
pvh->pvh_attrs &= mask;
if (pv->pv_pmap != NULL) {
#ifdef DEBUG
int toflush = 0;
#endif
for (; pv; pv = pv->pv_next) {
#ifdef DEBUG
toflush |= (pv->pv_pmap == pmap_kernel()) ? 2 : 1;
#endif
va = pv->pv_va;
pte = pmap_pte(pv->pv_pmap, va);
#ifdef M68K_EC_VAC
if (firstpage && pmap_aliasmask) {
firstpage = false;
DCIS();
}
#endif
npte = (*pte | set) & mask;
if (*pte != npte) {
r = true;
#if defined(M68040) || defined(M68060)
if (firstpage &&
#if defined(M68020) || defined(M68030)
(mmutype == MMU_68040) &&
#endif
((set == PG_RO) ||
(set & PG_CMASK) ||
(mask & PG_CMASK) == 0)) {
firstpage = false;
DCFP(pa);
ICPP(pa);
}
#endif
*pte = npte;
if (active_pmap(pv->pv_pmap))
TBIS(va);
}
}
}
splx(s);
return r;
}
int
pmap_enter_ptpage(pmap_t pmap, vaddr_t va, bool can_fail)
{
paddr_t ptpa;
struct vm_page *pg;
struct pv_header *pvh;
struct pv_entry *pv;
st_entry_t *ste;
int s;
PMAP_DPRINTF(PDB_FOLLOW|PDB_ENTER|PDB_PTPAGE,
("pmap_enter_ptpage: pmap %p, va %lx\n", pmap, va));
if (pmap->pm_stab == Segtabzero) {
pmap->pm_stab = (st_entry_t *)
uvm_km_alloc(st_map, M68K_STSIZE, 0,
UVM_KMF_WIRED | UVM_KMF_ZERO |
(can_fail ? UVM_KMF_NOWAIT : 0));
if (pmap->pm_stab == NULL) {
pmap->pm_stab = Segtabzero;
return ENOMEM;
}
(void) pmap_extract(pmap_kernel(), (vaddr_t)pmap->pm_stab,
(paddr_t *)&pmap->pm_stpa);
#if defined(M68040) || defined(M68060)
#if defined(M68020) || defined(M68030)
if (mmutype == MMU_68040)
#endif
{
pt_entry_t *pte;
pte = pmap_pte(pmap_kernel(), pmap->pm_stab);
*pte = (*pte & ~PG_CMASK) | PG_CI;
pmap->pm_stfree = protostfree;
}
#endif
if (active_user_pmap(pmap)) {
pmap_load_urp((paddr_t)pmap->pm_stpa);
}
PMAP_DPRINTF(PDB_ENTER|PDB_PTPAGE|PDB_SEGTAB,
("enter: pmap %p stab %p(%p)\n",
pmap, pmap->pm_stab, pmap->pm_stpa));
}
ste = pmap_ste(pmap, va);
#if defined(M68040) || defined(M68060)
#if defined(M68020) || defined(M68030)
if (mmutype == MMU_68040)
#endif
{
if (*ste == SG_NV) {
int ix;
void *addr;
ix = bmtol2(pmap->pm_stfree);
if (ix == -1)
panic("enter: out of address space");
pmap->pm_stfree &= ~l2tobm(ix);
addr = (void *)&pmap->pm_stab[ix*SG4_LEV2SIZE];
memset(addr, 0, SG4_LEV2SIZE*sizeof(st_entry_t));
addr = (void *)&pmap->pm_stpa[ix*SG4_LEV2SIZE];
*ste = (u_int)addr | SG_RW | SG_U | SG_V;
PMAP_DPRINTF(PDB_ENTER|PDB_PTPAGE|PDB_SEGTAB,
("enter: alloc ste2 %d(%p)\n", ix, addr));
}
ste = pmap_ste2(pmap, va);
ste = (st_entry_t *)((int)ste & ~(PAGE_SIZE/SG4_LEV3SIZE-1));
PMAP_DPRINTF(PDB_ENTER|PDB_PTPAGE|PDB_SEGTAB,
("enter: ste2 %p (%p)\n", pmap_ste2(pmap, va), ste));
}
#endif
va = trunc_page((vaddr_t)pmap_pte(pmap, va));
if (pmap == pmap_kernel()) {
struct kpt_page *kpt;
s = splvm();
if ((kpt = kpt_free_list) == NULL) {
PMAP_DPRINTF(PDB_COLLECT,
("enter: no KPT pages, collecting...\n"));
pmap_collect();
if ((kpt = kpt_free_list) == NULL)
panic("pmap_enter_ptpage: can't get KPT page");
}
kpt_free_list = kpt->kpt_next;
kpt->kpt_next = kpt_used_list;
kpt_used_list = kpt;
ptpa = kpt->kpt_pa;
memset((void *)kpt->kpt_va, 0, PAGE_SIZE);
pmap_enter(pmap, va, ptpa, VM_PROT_READ | VM_PROT_WRITE,
VM_PROT_READ | VM_PROT_WRITE | PMAP_WIRED);
pmap_update(pmap);
#ifdef DEBUG
if (pmapdebug & (PDB_ENTER|PDB_PTPAGE)) {
int ix = pmap_ste(pmap, va) - pmap_ste(pmap, 0);
printf("enter: add &Sysptmap[%d]: %x (KPT page %lx)\n",
ix, Sysptmap[ix], kpt->kpt_va);
}
#endif
splx(s);
} else {
pmap->pm_sref++;
PMAP_DPRINTF(PDB_ENTER|PDB_PTPAGE,
("enter: about to alloc UPT pg at %lx\n", va));
rw_enter(uvm_kernel_object->vmobjlock, RW_WRITER);
while ((pg = uvm_pagealloc(uvm_kernel_object,
va - vm_map_min(kernel_map),
NULL, UVM_PGA_ZERO)) == NULL) {
rw_exit(uvm_kernel_object->vmobjlock);
if (can_fail) {
pmap->pm_sref--;
return ENOMEM;
}
uvm_wait("ptpage");
rw_enter(uvm_kernel_object->vmobjlock, RW_WRITER);
}
rw_exit(uvm_kernel_object->vmobjlock);
pg->flags &= ~(PG_BUSY|PG_FAKE);
UVM_PAGE_OWN(pg, NULL);
ptpa = VM_PAGE_TO_PHYS(pg);
pmap_enter(pmap_kernel(), va, ptpa,
VM_PROT_READ | VM_PROT_WRITE,
VM_PROT_READ | VM_PROT_WRITE | PMAP_WIRED);
pmap_update(pmap_kernel());
}
#if defined(M68040) || defined(M68060)
#if defined(M68020) || defined(M68030)
if (mmutype == MMU_68040)
#endif
{
#ifdef DEBUG
pt_entry_t *pte = pmap_pte(pmap_kernel(), va);
if ((pmapdebug & PDB_PARANOIA) && (*pte & PG_CCB) == 0)
printf("%s PT no CCB: kva=%lx ptpa=%lx pte@%p=%x\n",
pmap == pmap_kernel() ? "Kernel" : "User",
va, ptpa, pte, *pte);
#endif
if (pmap_changebit(ptpa, PG_CI, (pt_entry_t)~PG_CCB))
DCIS();
}
#endif
pvh = pa_to_pvh(ptpa);
s = splvm();
if (pvh) {
pv = &pvh->pvh_first;
pvh->pvh_attrs |= PVH_PTPAGE;
do {
if (pv->pv_pmap == pmap_kernel() && pv->pv_va == va)
break;
} while ((pv = pv->pv_next));
} else {
pv = NULL;
}
#ifdef DEBUG
if (pv == NULL)
panic("pmap_enter_ptpage: PT page not entered");
#endif
pv->pv_ptste = ste;
pv->pv_ptpmap = pmap;
PMAP_DPRINTF(PDB_ENTER|PDB_PTPAGE,
("enter: new PT page at PA %lx, ste at %p\n", ptpa, ste));
#if defined(M68040) || defined(M68060)
#if defined(M68020) || defined(M68030)
if (mmutype == MMU_68040)
#endif
{
st_entry_t *este;
for (este = &ste[NPTEPG/SG4_LEV3SIZE]; ste < este; ste++) {
*ste = ptpa | SG_U | SG_RW | SG_V;
ptpa += SG4_LEV3SIZE * sizeof(st_entry_t);
}
}
#if defined(M68020) || defined(M68030)
else
*ste = (ptpa & SG_FRAME) | SG_RW | SG_V;
#endif
#else
*ste = (ptpa & SG_FRAME) | SG_RW | SG_V;
#endif
if (pmap != pmap_kernel()) {
PMAP_DPRINTF(PDB_ENTER|PDB_PTPAGE|PDB_SEGTAB,
("enter: stab %p refcnt %d\n",
pmap->pm_stab, pmap->pm_sref));
}
if (pmap == pmap_kernel())
TBIAS();
else
TBIAU();
pmap->pm_ptpages++;
splx(s);
return 0;
}
void
pmap_ptpage_addref(vaddr_t ptpva)
{
struct vm_page *pg;
rw_enter(uvm_kernel_object->vmobjlock, RW_WRITER);
pg = uvm_pagelookup(uvm_kernel_object, ptpva - vm_map_min(kernel_map));
pg->wire_count++;
PMAP_DPRINTF(PDB_ENTER|PDB_PTPAGE|PDB_SEGTAB,
("ptpage addref: pg %p now %d\n",
pg, pg->wire_count));
rw_exit(uvm_kernel_object->vmobjlock);
}
int
pmap_ptpage_delref(vaddr_t ptpva)
{
struct vm_page *pg;
int rv;
rw_enter(uvm_kernel_object->vmobjlock, RW_WRITER);
pg = uvm_pagelookup(uvm_kernel_object, ptpva - vm_map_min(kernel_map));
rv = --pg->wire_count;
PMAP_DPRINTF(PDB_ENTER|PDB_PTPAGE|PDB_SEGTAB,
("ptpage delref: pg %p now %d\n",
pg, pg->wire_count));
rw_exit(uvm_kernel_object->vmobjlock);
return rv;
}
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)
{
volatile pt_entry_t *pte;
pt_entry_t oldpte, tmppte;
#ifdef M68K_MMU_HP
if (ectype == EC_VIRT)
DCIS();
#endif
pte = kvtopte(pgva);
oldpte = *pte;
if ((oldpte & DT51_PAGE) == 0) {
return false;
}
tmppte = (oldpte & ~PTE51_WP) | PTE51_CI;
*pte = tmppte;
TBIS(pgva);
ctx->pgva = pgva;
ctx->ptep = pte;
ctx->opte = oldpte;
return true;
}
void
pmap_db_write_text_exit(struct pmap_db_write_text_context *ctx)
{
*ctx->ptep = ctx->opte;
TBIS(ctx->pgva);
}
#endif
void
_pmap_set_page_cacheable(pmap_t pmap, vaddr_t va)
{
if (!pmap_ste_v(pmap, va))
return;
#if defined(M68040) || defined(M68060)
#if defined(M68020) || defined(M68030)
if (mmutype == MMU_68040) {
#endif
if (pmap_changebit(pmap_pte_pa(pmap_pte(pmap, va)), PG_CCB,
(pt_entry_t)~PG_CI))
DCIS();
#if defined(M68020) || defined(M68030)
} else
pmap_changebit(pmap_pte_pa(pmap_pte(pmap, va)), 0,
(pt_entry_t)~PG_CI);
#endif
#else
pmap_changebit(pmap_pte_pa(pmap_pte(pmap, va)), 0,
(pt_entry_t)~PG_CI);
#endif
}
void
_pmap_set_page_cacheinhibit(pmap_t pmap, vaddr_t va)
{
if (!pmap_ste_v(pmap, va))
return;
#if defined(M68040) || defined(M68060)
#if defined(M68020) || defined(M68030)
if (mmutype == MMU_68040) {
#endif
if (pmap_changebit(pmap_pte_pa(pmap_pte(pmap, va)), PG_CI,
(pt_entry_t)~PG_CCB))
DCIS();
#if defined(M68020) || defined(M68030)
} else
pmap_changebit(pmap_pte_pa(pmap_pte(pmap, va)), PG_CI, ~0);
#endif
#else
pmap_changebit(pmap_pte_pa(pmap_pte(pmap, va)), PG_CI, ~0);
#endif
}
int
_pmap_page_is_cacheable(pmap_t pmap, vaddr_t va)
{
if (!pmap_ste_v(pmap, va))
return 0;
return (pmap_pte_ci(pmap_pte(pmap, va)) == 0) ? 1 : 0;
}
vaddr_t kernel_reloc_offset;
phys_ram_seg_t *
pmap_init_kcore_hdr(cpu_kcore_hdr_t *h)
{
struct m68k_kcore_hdr *m = &h->un._m68k;
extern char end[];
memset(h, 0, sizeof(*h));
strcpy(h->name, machine);
h->page_size = PAGE_SIZE;
h->kernbase = KERNBASE;
m->mmutype = mmutype;
m->sg_v = SG_V;
m->sg_frame = SG_FRAME;
m->sg_ishift = SG_ISHIFT;
m->sg_pmask = SG_PMASK;
m->sg40_shift1 = SG4_SHIFT1;
m->sg40_mask2 = SG4_MASK2;
m->sg40_shift2 = SG4_SHIFT2;
m->sg40_mask3 = SG4_MASK3;
m->sg40_shift3 = SG4_SHIFT3;
m->sg40_addr1 = SG4_ADDR1;
m->sg40_addr2 = SG4_ADDR2;
m->pg_v = PG_V;
m->pg_frame = PG_FRAME;
m->sysseg_pa = Sysseg_pa;
m->reloc = kernel_reloc_offset;
m->relocend = (uint32_t)end;
return m->ram_segs;
}
#ifdef DEBUG
void
pmap_pvdump(paddr_t pa)
{
struct pv_header *pvh;
struct pv_entry *pv;
printf("pa %lx", pa);
pvh = pa_to_pvh(pa);
for (pv = &pvh->pvh_first; pv; pv = pv->pv_next)
printf(" -> pmap %p, va %lx, ptste %p, ptpmap %p",
pv->pv_pmap, pv->pv_va, pv->pv_ptste, pv->pv_ptpmap);
printf("\n");
}
void
pmap_check_wiring(const char *str, vaddr_t va)
{
pt_entry_t *pte;
paddr_t pa;
struct vm_page *pg;
int count;
if (!pmap_ste_v(pmap_kernel(), va) ||
!pmap_pte_v(pmap_pte(pmap_kernel(), va)))
return;
pa = pmap_pte_pa(pmap_pte(pmap_kernel(), va));
pg = PHYS_TO_VM_PAGE(pa);
if (pg->wire_count > PAGE_SIZE / sizeof(pt_entry_t)) {
panic("*%s*: 0x%lx: wire count %d", str, va, pg->wire_count);
}
count = 0;
for (pte = (pt_entry_t *)va; pte < (pt_entry_t *)(va + PAGE_SIZE);
pte++)
if (*pte)
count++;
if (pg->wire_count != count)
panic("*%s*: 0x%lx: w%d/a%d",
str, va, pg->wire_count, count);
}
#endif
void
physaccess(void *vaddr, void *paddr, int size, int prot)
{
pt_entry_t *pte;
u_int page;
pte = kvtopte(vaddr);
page = (u_int)paddr & PG_FRAME;
for (size = btoc(size); size; size--) {
*pte++ = PG_V | prot | page;
page += PAGE_SIZE;
}
TBIAS();
}
void
physunaccess(void *vaddr, int size)
{
pt_entry_t *pte;
pte = kvtopte(vaddr);
for (size = btoc(size); size; size--)
*pte++ = PG_NV;
TBIAS();
}
int
kvtop(void *addr)
{
return (int)vtophys((vaddr_t)addr);
}
bool
pmap_pa_has_static_mapping(paddr_t pa, size_t len, vm_prot_t prot,
vaddr_t *vap, int *flagsp)
{
#if defined(M68030) || defined(M68040) || defined(M68060)
if (cputype >= CPU_68030 &&
mmu_range_is_tt(pa, len, prot, flagsp)) {
*vap = pa;
return true;
}
#endif
const struct pmap_bootmap *pmbm = machine_bootmap;
paddr_t lastpg = m68k_btop(pa + (len - 1));
paddr_t firstpg = m68k_btop(pa);
paddr_t tfirst, tlast;
vaddr_t tblva;
bool need_write = !!(prot & UVM_PROT_WRITE);
for (; pmbm->pmbm_vaddr != (vaddr_t)-1; pmbm++) {
if (pmbm->pmbm_flags & (PMBM_F_VAONLY|PMBM_F_KEEPOUT)) {
continue;
}
if (pmbm->pmbm_flags & PMBM_F_FIXEDVA) {
tblva = pmbm->pmbm_vaddr;
} else {
tblva = *pmbm->pmbm_vaddr_ptr;
}
tfirst = m68k_btop(pmbm->pmbm_paddr);
tlast = m68k_btop(pmbm->pmbm_paddr + (pmbm->pmbm_size - 1));
if (firstpg >= tfirst && lastpg <= tlast) {
if (need_write &&
(pmbm->pmbm_flags & PMBM_F_RO) != 0) {
return false;
}
*vap = tblva + (pa - pmbm->pmbm_paddr);
if (pmbm->pmbm_flags & PMBM_F_CI) {
*flagsp = PMAP_NOCACHE;
} else {
*flagsp = 0;
}
return true;
}
}
return false;
}
bool
pmap_va_is_static_mapping(vaddr_t va, size_t len)
{
#if defined(M68030) || defined(M68040) || defined(M68060)
int map_flags;
if (cputype >= CPU_68030 &&
(mmu_range_is_tt(va, len, UVM_PROT_READ, &map_flags) ||
mmu_range_is_tt(va, len, UVM_PROT_WRITE, &map_flags) ||
mmu_range_is_tt(va, len, UVM_PROT_EXEC, &map_flags))) {
return true;
}
#endif
const struct pmap_bootmap *pmbm = machine_bootmap;
vaddr_t lastpg = m68k_btop(va + (len - 1));
vaddr_t firstpg = m68k_btop(va);
vaddr_t tblva, tfirst, tlast;
for (; pmbm->pmbm_vaddr != (vaddr_t)-1; pmbm++) {
if (pmbm->pmbm_flags & (PMBM_F_VAONLY|PMBM_F_KEEPOUT)) {
continue;
}
if (pmbm->pmbm_flags & PMBM_F_FIXEDVA) {
tblva = pmbm->pmbm_vaddr;
} else {
tblva = *pmbm->pmbm_vaddr_ptr;
}
tfirst = m68k_btop(tblva);
tlast = m68k_btop(tblva + (pmbm->pmbm_size - 1));
if (firstpg >= tfirst && lastpg <= tlast) {
return true;
}
}
return false;
}