#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.201 2023/12/22 19:14:57 thorpej Exp $");
#include "opt_cputype.h"
#include "opt_ddb.h"
#include "opt_lockdebug.h"
#include "opt_modular.h"
#include "opt_multiprocessor.h"
#include "opt_pipe.h"
#include <sys/param.h>
#include <sys/atomic.h>
#include <sys/buf.h>
#include <sys/cpu.h>
#include <sys/device.h>
#include <sys/extent.h>
#include <sys/kmem.h>
#include <sys/mutex.h>
#include <sys/proc.h>
#include <sys/syncobj.h>
#include <sys/systm.h>
#include <uvm/uvm.h>
#include <uvm/uvm_physseg.h>
#ifdef PMAPDEBUG
#include <dev/cons.h>
#endif
#include <machine/macros.h>
#include <machine/rpb.h>
#include <machine/scb.h>
#include <machine/sid.h>
#include "qd.h"
void qdearly(void);
#define PROTSHIFT 27
#define PROT_KW (PG_KW >> PROTSHIFT)
#define PROT_KR (PG_KR >> PROTSHIFT)
#define PROT_RW (PG_RW >> PROTSHIFT)
#define PROT_RO (PG_RO >> PROTSHIFT)
#define PROT_URKW (PG_URKW >> PROTSHIFT)
uintptr_t scratch;
#define SCRATCHPAGES 4
static struct pmap kernel_pmap_store;
struct pmap *const kernel_pmap_ptr = &kernel_pmap_store;
struct pte *Sysmap;
struct pv_entry *pv_table;
u_int pventries;
u_int pvinuse;
vaddr_t iospace;
vaddr_t ptemapstart, ptemapend;
struct extent *ptemap;
#define PTMAPSZ EXTENT_FIXED_STORAGE_SIZE(100)
char ptmapstorage[PTMAPSZ];
extern void *msgbufaddr;
#define IOSPACE_P(p) (((u_long)(p) & 0xe0000000) != 0)
#define NPTEPROCSPC 0x1000
#define NPTEPG 0x80
#define PPTESZ sizeof(struct pte)
#define NOVADDR 0xffffffff
#define NPTEPERREG 0x200000
#define SEGTYPE(x) (((unsigned int)(x)) >> 30)
#define P0SEG 0
#define P1SEG 1
#define SYSSEG 2
static inline void
pmap_decrement_stats(struct pmap *pm, bool wired)
{
pm->pm_stats.resident_count--;
if (wired)
pm->pm_stats.wired_count--;
}
static inline void
mapin8(int *ptep, long pte)
{
ptep[0] = pte;
ptep[1] = pte+1;
ptep[2] = pte+2;
ptep[3] = pte+3;
ptep[4] = pte+4;
ptep[5] = pte+5;
ptep[6] = pte+6;
ptep[7] = pte+7;
}
static inline int
ptpinuse(void *pte)
{
int *pve = (int *)vax_trunc_page(pte);
int i;
for (i = 0; i < NPTEPG; i += 8)
if (pve[i] != 0)
return 1;
return 0;
}
#ifdef PMAPDEBUG
#define PMDEBUG(x) if (startpmapdebug)printf x
#else
#define PMDEBUG(x)
#endif
#if defined(MULTIPROCESSOR) || defined(LOCKDEBUG)
static kmutex_t pmap_lock;
#define PMAP_LOCK mutex_spin_enter(&pmap_lock);
#define PMAP_UNLOCK mutex_spin_exit(&pmap_lock);
#else
#define PMAP_LOCK
#define PMAP_UNLOCK
#endif
#ifdef PMAPDEBUG
int startpmapdebug = 0;
#endif
paddr_t avail_start, avail_end;
vaddr_t virtual_avail, virtual_end;
struct pv_entry *get_pventry(void);
void free_pventry(struct pv_entry *);
void more_pventries(void);
vaddr_t get_ptp(void);
void free_ptp(paddr_t);
static vsize_t
calc_kvmsize(vsize_t usrptsize)
{
vsize_t kvmsize, bufsz;
kmeminit_nkmempages();
kvmsize = avail_end;
kvmsize += (usrptsize * sizeof(struct pte));
kvmsize += (USPACE * maxproc);
kvmsize += nkmempages * PAGE_SIZE;
kvmsize += (IOSPSZ * VAX_NBPG);
kvmsize += (pager_map_size + MAXBSIZE);
kvmsize += (physmem * sizeof(struct vm_anon));
kvmsize += avail_end;
bufsz = buf_memcalc();
buf_setvalimit(bufsz);
kvmsize += bufsz;
kvmsize += (UBC_NWINS << UBC_WINSHIFT);
kvmsize += NCARGS;
#if VAX46 || VAX48 || VAX49 || VAX53 || VAXANY
kvmsize += VM_PHYS_SIZE;
#endif
#if VAX46 || VAX49
kvmsize += 0x800000;
#endif
#ifdef MODULAR
#define MAXLKMSIZ 0x100000
kvmsize += MAXLKMSIZ;
#endif
#ifndef SWAPSIZE
#define SWAPSIZE (200*1024*1024)
#endif
kvmsize += ((SWAPSIZE/PAGE_SIZE)*sizeof(struct vm_anon));
#ifndef PIPE_SOCKETPAIR
kvmsize += PIPE_DIRECT_CHUNK*10;
#endif
kvmsize = round_page(kvmsize);
return kvmsize;
}
void
pmap_bootstrap(void)
{
struct pcb * const pcb = lwp_getpcb(&lwp0);
struct pmap * const pmap = pmap_kernel();
struct cpu_info *ci;
extern unsigned int etext;
unsigned int sysptsize, i;
vsize_t kvmsize, usrptsize;
vaddr_t istack;
uvmexp.pagesize = NBPG;
uvm_md_init();
physmem = btoc(avail_end);
usrptsize = (1024*1024*1024)/VAX_NBPG;
if (vax_btop(usrptsize)* PPTESZ > avail_end/20)
usrptsize = (avail_end/(20 * PPTESZ)) * VAX_NBPG;
kvmsize = calc_kvmsize(usrptsize);
#define S0SPACE (1*1024*1024*1024)
if (kvmsize > S0SPACE)
kvmsize = S0SPACE;
sysptsize = kvmsize >> VAX_PGSHIFT;
virtual_avail = avail_end + KERNBASE;
virtual_end = KERNBASE + sysptsize * VAX_NBPG;
memset(Sysmap, 0, sysptsize * 4);
pmap_map(KERNBASE, 0, avail_end, VM_PROT_READ|VM_PROT_WRITE);
for (i = 0; i < ((unsigned)&etext ^ KERNBASE) >> VAX_PGSHIFT; i++)
Sysmap[i].pg_prot = PROT_URKW;
mtpr((unsigned)Sysmap - KERNBASE, PR_SBR);
istack = (uintptr_t)Sysmap + round_page(sysptsize * 4);
mtpr(istack + USPACE, PR_ISP);
kvtopte(istack)->pg_v = 0;
scratch = istack + USPACE;
pv_table = (struct pv_entry *)(scratch + SCRATCHPAGES * VAX_NBPG);
avail_start = (vaddr_t)pv_table + (round_page(avail_end >> PGSHIFT)) *
sizeof(struct pv_entry) - KERNBASE;
avail_end -= MSGBUFSIZE;
msgbufaddr = (void *)(avail_end + KERNBASE);
memset((void *)istack, 0, (avail_start + KERNBASE) - istack);
#if NQD > 0
qdearly();
#endif
MAPVIRT(ptemapstart, vax_btoc(usrptsize * sizeof(struct pte)));
ptemapend = virtual_avail;
MAPVIRT(iospace, IOSPSZ);
avail_start = scb_init(avail_start);
*(struct rpb *)0 = *(struct rpb *)(uvm_lwp_getuarea(&lwp0) + REDZONEADDR);
if (dep_call->cpu_steal_pages)
(*dep_call->cpu_steal_pages)();
avail_start = round_page(avail_start);
virtual_avail = round_page(virtual_avail);
virtual_end = trunc_page(virtual_end);
#if 0
cninit();
printf("Sysmap %p, istack %lx, scratch %lx\n",Sysmap,ci->ci_istack,scratch);
printf("etext %p, kvmsize %lx\n", &etext, kvmsize);
printf("SYSPTSIZE %x usrptsize %lx\n",
sysptsize, usrptsize * sizeof(struct pte));
printf("pv_table %p, ptemapstart %lx ptemapend %lx\n",
pv_table, ptemapstart, ptemapend);
printf("avail_start %lx, avail_end %lx\n",avail_start,avail_end);
printf("virtual_avail %lx,virtual_end %lx\n",
virtual_avail, virtual_end);
printf("startpmapdebug %p\n",&startpmapdebug);
#endif
pmap->pm_p1br = (struct pte *)KERNBASE;
pmap->pm_p0br = (struct pte *)KERNBASE;
pmap->pm_p1lr = NPTEPERREG;
pmap->pm_p0lr = 0;
pmap->pm_stats.wired_count = pmap->pm_stats.resident_count = 0;
pmap->pm_count = 1;
pcb->P1BR = pmap->pm_p1br;
pcb->P0BR = pmap->pm_p0br;
pcb->P1LR = pmap->pm_p1lr;
pcb->P0LR = pmap->pm_p0lr|AST_PCB;
pcb->pcb_pm = pmap;
pcb->pcb_pmnext = pmap->pm_pcbs;
pmap->pm_pcbs = pcb;
mtpr((uintptr_t)pcb->P1BR, PR_P1BR);
mtpr((uintptr_t)pcb->P0BR, PR_P0BR);
mtpr(pcb->P1LR, PR_P1LR);
mtpr(pcb->P0LR, PR_P0LR);
pcb->SSP = (uintptr_t)&lwp0;
mtpr(pcb->SSP, PR_SSP);
ci = (struct cpu_info *) scratch;
lwp0.l_cpu = ci;
ci->ci_istack = istack;
memset(ci, 0, sizeof(*ci));
#if defined(MULTIPROCESSOR)
ci->ci_curlwp = &lwp0;
ci->ci_flags = CI_MASTERCPU|CI_RUNNING;
SIMPLEQ_FIRST(&cpus) = ci;
#endif
#if defined(MULTIPROCESSOR) || defined(LOCKDEBUG)
mutex_init(&pmap_lock, MUTEX_DEFAULT, IPL_VM);
#endif
uvm_page_physload(avail_start >> PGSHIFT, avail_end >> PGSHIFT,
avail_start >> PGSHIFT, avail_end >> PGSHIFT,
VM_FREELIST_DEFAULT);
mtpr(sysptsize, PR_SLR);
rpb.sbr = mfpr(PR_SBR);
rpb.slr = mfpr(PR_SLR);
rpb.wait = 0;
mtpr(1, PR_MAPEN);
}
void
pmap_virtual_space(vaddr_t *vstartp, vaddr_t *vendp)
{
*vstartp = virtual_avail;
*vendp = virtual_end;
}
vaddr_t
pmap_steal_memory(vsize_t size, vaddr_t *vstartp, vaddr_t *vendp)
{
vaddr_t v;
int npgs;
uvm_physseg_t bank;
PMDEBUG(("pmap_steal_memory: size 0x%lx start %p end %p\n",
size, vstartp, vendp));
size = round_page(size);
npgs = btoc(size);
#ifdef DIAGNOSTIC
if (uvm.page_init_done == true)
panic("pmap_steal_memory: called _after_ bootstrap");
#endif
bank = uvm_physseg_get_first();
v = (uvm_physseg_get_start(bank) << PGSHIFT) | KERNBASE;
uvm_physseg_unplug(uvm_physseg_get_start(bank), npgs);
memset((void *)v, 0, size);
return v;
}
void
pmap_init(void)
{
ptemap = extent_create("ptemap", ptemapstart, ptemapend,
ptmapstorage, PTMAPSZ, EX_NOCOALESCE);
if (ptemap == NULL)
panic("pmap_init");
}
static u_long
pmap_extwrap(vsize_t nsize)
{
int res;
u_long rv;
for (;;) {
res = extent_alloc(ptemap, nsize, PAGE_SIZE, 0,
EX_WAITOK|EX_MALLOCOK, &rv);
if (res == EAGAIN)
return 0;
if (res == 0)
return rv;
}
}
static void
rmpage(pmap_t pm, int *br)
{
struct pv_entry *pv, *pl, *pf;
vaddr_t vaddr;
int found = 0;
if (pm == pmap_kernel())
vaddr = (br - (int *)Sysmap) * VAX_NBPG + 0x80000000;
else if ((br >= (int *)pm->pm_p0br) &&
(br < ((int *)pm->pm_p0br + pm->pm_p0lr)))
vaddr = (br - (int *)pm->pm_p0br) * VAX_NBPG;
else
vaddr = (br - (int *)pm->pm_p1br) * VAX_NBPG + 0x40000000;
if (IOSPACE_P((br[0] & PG_FRAME) << VAX_PGSHIFT))
return;
pv = pv_table + ((br[0] & PG_FRAME) >> LTOHPS);
if (((br[0] & PG_PROT) == PG_RW) &&
((pv->pv_attr & PG_M) != PG_M))
pv->pv_attr |= br[0]|br[1]|br[2]|br[3]|br[4]|br[5]|br[6]|br[7];
pmap_decrement_stats(pm, (br[0] & PG_W) != 0);
if (pv->pv_pmap == pm && pv->pv_vaddr == vaddr) {
pv->pv_vaddr = NOVADDR;
pv->pv_pmap = 0;
found++;
} else
for (pl = pv; pl->pv_next; pl = pl->pv_next) {
if (pl->pv_next->pv_pmap != pm ||
pl->pv_next->pv_vaddr != vaddr)
continue;
pf = pl->pv_next;
pl->pv_next = pl->pv_next->pv_next;
free_pventry(pf);
found++;
break;
}
if (found == 0)
panic("rmpage: pm %p br %p", pm, br);
}
static void
update_pcbs(struct pmap *pm)
{
struct pcb *pcb;
for (pcb = pm->pm_pcbs; pcb != NULL; pcb = pcb->pcb_pmnext) {
KASSERT(pcb->pcb_pm == pm);
pcb->P0BR = pm->pm_p0br;
pcb->P0LR = pm->pm_p0lr | (pcb->P0LR & AST_MASK);
pcb->P1BR = pm->pm_p1br;
pcb->P1LR = pm->pm_p1lr;
}
if (pm == curproc->p_vmspace->vm_map.pmap) {
mtpr((uintptr_t)pm->pm_p0br, PR_P0BR);
mtpr(pm->pm_p0lr, PR_P0LR);
mtpr((uintptr_t)pm->pm_p1br, PR_P1BR);
mtpr(pm->pm_p1lr, PR_P1LR);
}
#if defined(MULTIPROCESSOR) && defined(notyet)
cpu_send_ipi(IPI_DEST_ALL, IPI_NEWPTE);
#endif
}
static vaddr_t
getpage(void)
{
struct vm_page *pg;
pg = uvm_pagealloc(NULL, 0, NULL, UVM_PGA_ZERO);
if (pg == NULL)
return 0;
return (VM_PAGE_TO_PHYS(pg)|KERNBASE);
}
#if 0
static void
freepage(vaddr_t v)
{
paddr_t paddr = (kvtopte(v)->pg_pfn << VAX_PGSHIFT);
uvm_pagefree(PHYS_TO_VM_PAGE(paddr));
}
#endif
static void
rmspace(struct pmap *pm)
{
int lr, i, j, *br, *ptpp;
if (pm->pm_p0lr == 0 && pm->pm_p1lr == NPTEPERREG)
return;
lr = pm->pm_p0lr/NPTEPG;
for (i = 0; i < lr; i++) {
ptpp = (int *)kvtopte(&pm->pm_p0br[i*NPTEPG]);
if (*ptpp == 0)
continue;
br = (int *)&pm->pm_p0br[i*NPTEPG];
for (j = 0; j < NPTEPG; j+=LTOHPN) {
if (br[j] == 0)
continue;
rmpage(pm, &br[j]);
}
free_ptp((((struct pte *)ptpp)->pg_pfn << VAX_PGSHIFT));
*ptpp = 0;
mtpr((vaddr_t)br, PR_TBIS);
}
lr = pm->pm_p1lr/NPTEPG;
for (i = lr; i < NPTEPERREG/NPTEPG; i++) {
ptpp = (int *)kvtopte(&pm->pm_p1br[i*NPTEPG]);
if (*ptpp == 0)
continue;
br = (int *)&pm->pm_p1br[i*NPTEPG];
for (j = 0; j < NPTEPG; j+=LTOHPN) {
if (br[j] == 0)
continue;
rmpage(pm, &br[j]);
}
free_ptp((((struct pte *)ptpp)->pg_pfn << VAX_PGSHIFT));
*ptpp = 0;
mtpr((vaddr_t)br, PR_TBIS);
}
if (pm->pm_p0lr != 0)
extent_free(ptemap, (u_long)pm->pm_p0br,
pm->pm_p0lr * PPTESZ, EX_WAITOK);
if (pm->pm_p1lr != NPTEPERREG)
extent_free(ptemap, (u_long)pm->pm_p1ap,
(NPTEPERREG - pm->pm_p1lr) * PPTESZ, EX_WAITOK);
pm->pm_p0br = pm->pm_p1br = (struct pte *)KERNBASE;
pm->pm_p0lr = 0;
pm->pm_p1lr = NPTEPERREG;
pm->pm_p1ap = NULL;
update_pcbs(pm);
}
static inline bool
pmap_vax_swappable(struct lwp *l, struct pmap *pm)
{
if (l->l_flag & (LW_SYSTEM | LW_WEXIT))
return false;
if (l->l_proc->p_vmspace->vm_map.pmap == pm)
return false;
if ((l->l_pflag & LP_RUNNING) != 0)
return false;
if (l->l_class != SCHED_OTHER)
return false;
if (l->l_syncobj == &rw_syncobj || l->l_syncobj == &mutex_syncobj)
return false;
if (l->l_proc->p_stat != SACTIVE && l->l_proc->p_stat != SSTOP)
return false;
return true;
}
static int
pmap_rmproc(struct pmap *pm)
{
struct pmap *ppm;
struct lwp *l;
struct lwp *outl, *outl2;
int outpri, outpri2;
int didswap = 0;
extern int maxslp;
outl = outl2 = NULL;
outpri = outpri2 = 0;
mutex_enter(&proc_lock);
LIST_FOREACH(l, &alllwp, l_list) {
if (!pmap_vax_swappable(l, pm))
continue;
ppm = l->l_proc->p_vmspace->vm_map.pmap;
if (ppm->pm_p0lr == 0 && ppm->pm_p1lr == NPTEPERREG)
continue;
switch (l->l_stat) {
case LSRUN:
case LSONPROC:
if (l->l_swtime > outpri2) {
outl2 = l;
outpri2 = l->l_swtime;
}
continue;
case LSSLEEP:
case LSSTOP:
if (l->l_slptime >= maxslp) {
rmspace(l->l_proc->p_vmspace->vm_map.pmap);
didswap++;
} else if (l->l_slptime > outpri) {
outl = l;
outpri = l->l_slptime;
}
continue;
}
}
mutex_exit(&proc_lock);
if (didswap == 0) {
if ((l = outl) == NULL)
l = outl2;
if (l) {
rmspace(l->l_proc->p_vmspace->vm_map.pmap);
didswap++;
}
}
return didswap;
}
static vaddr_t
pmap_getusrptes(pmap_t pm, vsize_t nsize)
{
u_long rv;
#ifdef DEBUG
if (nsize & PAGE_MASK)
panic("pmap_getusrptes: bad size %lx", nsize);
#endif
while (((rv = pmap_extwrap(nsize)) == 0) && (pmap_rmproc(pm) != 0))
;
return rv;
}
static void
rmptep(struct pte *pte)
{
int *ptpp = (int *)kvtopte(pte);
#ifdef DEBUG
{ int i, *ptr = (int *)vax_trunc_page(pte);
for (i = 0; i < NPTEPG; i++)
if (ptr[i] != 0)
panic("rmptep: ptr[%d] != 0", i);
}
#endif
free_ptp((((struct pte *)ptpp)->pg_pfn << VAX_PGSHIFT));
*ptpp = 0;
}
static int
grow_p0(struct pmap *pm, int reqlen)
{
vaddr_t nptespc;
char *from, *to;
int srclen, dstlen;
int inuse, len, p0lr;
u_long p0br;
PMDEBUG(("grow_p0: pmap %p reqlen %d\n", pm, reqlen));
p0lr = pm->pm_p0lr;
inuse = p0lr != 0;
len = round_page((reqlen+1) * PPTESZ);
PMAP_UNLOCK;
nptespc = pmap_getusrptes(pm, len);
PMAP_LOCK;
if (nptespc == 0)
return 0;
srclen = vax_btop(p0lr * PPTESZ) * PPTESZ;
dstlen = vax_btoc(len)*PPTESZ;
from = (char *)kvtopte(pm->pm_p0br);
to = (char *)kvtopte(nptespc);
PMDEBUG(("grow_p0: from %p to %p src %d dst %d\n",
from, to, srclen, dstlen));
if (inuse)
memcpy(to, from, srclen);
memset(to+srclen, 0, dstlen-srclen);
p0br = (u_long)pm->pm_p0br;
pm->pm_p0br = (struct pte *)nptespc;
pm->pm_p0lr = (len/PPTESZ);
update_pcbs(pm);
if (inuse)
extent_free(ptemap, p0br, p0lr*PPTESZ, EX_WAITOK);
return 1;
}
static int
grow_p1(struct pmap *pm, int len)
{
vaddr_t nptespc, optespc;
int nlen, olen;
PMDEBUG(("grow_p1: pm %p len %x\n", pm, len));
nlen = (NPTEPERREG*PPTESZ) - trunc_page(len * PPTESZ);
PMAP_UNLOCK;
nptespc = pmap_getusrptes(pm, nlen);
PMAP_LOCK;
if (nptespc == 0)
return 0;
olen = (NPTEPERREG*PPTESZ) - (pm->pm_p1lr * PPTESZ);
optespc = (vaddr_t)pm->pm_p1ap;
memset(kvtopte(nptespc), 0, vax_btop(nlen-olen) * PPTESZ);
if (optespc)
memcpy(kvtopte(nptespc+nlen-olen), kvtopte(optespc),
vax_btop(olen) * PPTESZ);
pm->pm_p1ap = (struct pte *)nptespc;
pm->pm_p1br = (struct pte *)(nptespc+nlen-(NPTEPERREG*PPTESZ));
pm->pm_p1lr = NPTEPERREG - nlen/PPTESZ;
update_pcbs(pm);
if (optespc)
extent_free(ptemap, optespc, olen, EX_WAITOK);
return 1;
}
static void
pmap_pinit(pmap_t pmap)
{
pmap->pm_p0br = (struct pte *)KERNBASE;
pmap->pm_p1br = (struct pte *)KERNBASE;
pmap->pm_p0lr = 0;
pmap->pm_p1lr = NPTEPERREG;
pmap->pm_p1ap = NULL;
PMDEBUG(("pmap_pinit(%p): p0br=%p p0lr=0x%lx p1br=%p p1lr=0x%lx\n",
pmap, pmap->pm_p0br, pmap->pm_p0lr, pmap->pm_p1br, pmap->pm_p1lr));
pmap->pm_count = 1;
pmap->pm_stats.resident_count = pmap->pm_stats.wired_count = 0;
}
struct pmap *
pmap_create(void)
{
struct pmap *pmap;
pmap = kmem_zalloc(sizeof(*pmap), KM_SLEEP);
pmap_pinit(pmap);
return pmap;
}
static void
pmap_release(struct pmap *pmap)
{
#ifdef DEBUG
vaddr_t saddr, eaddr;
#endif
PMDEBUG(("pmap_release: pmap %p\n",pmap));
if (pmap->pm_p0br == 0)
return;
#ifdef DEBUG
#if 0
for (i = 0; i < NPTEPROCSPC; i++)
if (pmap->pm_pref[i])
panic("pmap_release: refcnt %d index %d",
pmap->pm_pref[i], i);
#endif
saddr = (vaddr_t)pmap->pm_p0br;
eaddr = saddr + pmap->pm_p0lr * PPTESZ;
for (; saddr < eaddr; saddr += PAGE_SIZE)
if (kvtopte(saddr)->pg_pfn)
panic("pmap_release: P0 page mapped");
saddr = (vaddr_t)pmap->pm_p1br + pmap->pm_p1lr * PPTESZ;
eaddr = KERNBASE;
for (; saddr < eaddr; saddr += PAGE_SIZE)
if (kvtopte(saddr)->pg_pfn)
panic("pmap_release: P1 page mapped");
#endif
if (pmap->pm_p0lr != 0)
extent_free(ptemap, (u_long)pmap->pm_p0br,
pmap->pm_p0lr * PPTESZ, EX_WAITOK);
if (pmap->pm_p1lr != NPTEPERREG)
extent_free(ptemap, (u_long)pmap->pm_p1ap,
(NPTEPERREG - pmap->pm_p1lr) * PPTESZ, EX_WAITOK);
}
void
pmap_destroy(pmap_t pmap)
{
PMDEBUG(("pmap_destroy: pmap %p\n",pmap));
if (atomic_dec_uint_nv(&pmap->pm_count) == 0) {
#ifdef DIAGNOSTIC
if (pmap->pm_pcbs)
panic("pmap_destroy used pmap");
#endif
pmap_release(pmap);
kmem_free(pmap, sizeof(*pmap));
}
}
static struct pte *
vaddrtopte(const struct pv_entry *pv)
{
struct pmap *pm;
if (pv->pv_pmap == NULL || pv->pv_vaddr == NOVADDR)
return NULL;
if (pv->pv_vaddr & KERNBASE)
return &Sysmap[(pv->pv_vaddr & ~KERNBASE) >> VAX_PGSHIFT];
pm = pv->pv_pmap;
if (pv->pv_vaddr & 0x40000000)
return &pm->pm_p1br[vax_btop(pv->pv_vaddr & ~0x40000000)];
else
return &pm->pm_p0br[vax_btop(pv->pv_vaddr)];
}
void
pmap_kenter_pa(vaddr_t va, paddr_t pa, vm_prot_t prot, u_int flags)
{
int *ptp, opte;
ptp = (int *)kvtopte(va);
PMDEBUG(("pmap_kenter_pa: va: %lx, pa %lx, prot %x ptp %p\n",
va, pa, prot, ptp));
opte = ptp[0];
ptp[0] = PG_V | ((prot & VM_PROT_WRITE)? PG_KW : PG_KR) |
PG_PFNUM(pa) | PG_SREF;
ptp[1] = ptp[0] + 1;
ptp[2] = ptp[0] + 2;
ptp[3] = ptp[0] + 3;
ptp[4] = ptp[0] + 4;
ptp[5] = ptp[0] + 5;
ptp[6] = ptp[0] + 6;
ptp[7] = ptp[0] + 7;
if (opte & PG_V) {
#if defined(MULTIPROCESSOR)
cpu_send_ipi(IPI_DEST_ALL, IPI_TBIA);
#endif
mtpr(0, PR_TBIA);
}
}
void
pmap_kremove(vaddr_t va, vsize_t len)
{
struct pte *pte;
#ifdef PMAPDEBUG
int i;
#endif
PMDEBUG(("pmap_kremove: va: %lx, len %lx, ptp %p\n",
va, len, kvtopte(va)));
pte = kvtopte(va);
#ifdef PMAPDEBUG
len >>= PGSHIFT;
for (i = 0; i < len; i++) {
if (pte->pg_pfn == 0)
continue;
if (pte->pg_sref == 0)
panic("pmap_kremove");
memset(pte, 0, LTOHPN * sizeof(struct pte));
pte += LTOHPN;
}
#else
len >>= VAX_PGSHIFT;
memset(pte, 0, len * sizeof(struct pte));
#endif
#if defined(MULTIPROCESSOR)
cpu_send_ipi(IPI_DEST_ALL, IPI_TBIA);
#endif
mtpr(0, PR_TBIA);
}
int
pmap_enter(pmap_t pmap, vaddr_t v, paddr_t p, vm_prot_t prot, u_int flags)
{
struct pv_entry *pv, *tmp;
int newpte, oldpte;
int *pteptr;
int *ptpptr;
PMDEBUG(("pmap_enter: pmap %p v %lx p %lx prot %x wired %d access %x\n",
pmap, v, p, prot, (flags & PMAP_WIRED) != 0, flags & VM_PROT_ALL));
PMAP_LOCK;
switch (SEGTYPE(v)) {
case SYSSEG:
pteptr = ((int *)Sysmap) + vax_btop(v - KERNBASE);
newpte = (prot & VM_PROT_WRITE ? PG_KW : PG_KR);
break;
case P0SEG:
if (vax_btop(v) >= pmap->pm_p0lr)
if (grow_p0(pmap, vax_btop(v)) == 0)
goto growfail;
pteptr = (int *)pmap->pm_p0br + vax_btop(v);
newpte = (prot & VM_PROT_WRITE ? PG_RW : PG_RO);
break;
case P1SEG:
if (vax_btop(v - 0x40000000) < pmap->pm_p1lr)
if (grow_p1(pmap, vax_btop(v - 0x40000000)) == 0)
goto growfail;
pteptr = (int *)pmap->pm_p1br + vax_btop(v - 0x40000000);
newpte = (prot & VM_PROT_WRITE ? PG_RW : PG_RO);
break;
default:
panic("bad seg");
}
newpte |= vax_btop(p);
if (SEGTYPE(v) != SYSSEG) {
ptpptr = (int *)kvtopte(pteptr);
if (*ptpptr == 0) {
paddr_t phys;
phys = get_ptp();
if (phys == 0) {
PMAP_UNLOCK;
if ((flags & PMAP_CANFAIL) != 0)
return ENOMEM;
panic("pmap_enter: out of memory");
}
*ptpptr = PG_V | PG_KW | PG_PFNUM(phys);
}
}
if (IOSPACE_P(p)) {
mapin8(pteptr, newpte);
PMAP_UNLOCK;
return 0;
}
if (flags & PMAP_WIRED)
newpte |= PG_W;
oldpte = *pteptr & ~(PG_V|PG_M);
pv = pv_table + (p >> PGSHIFT);
if (newpte == (oldpte | PG_W)) {
*pteptr |= PG_W;
pmap->pm_stats.wired_count++;
PMAP_UNLOCK;
return 0;
}
if (newpte == oldpte) {
PMAP_UNLOCK;
return 0;
}
if ((newpte & PG_FRAME) == (oldpte & PG_FRAME)) {
pmap_decrement_stats(pmap, (oldpte & PG_W) != 0);
} else {
if (oldpte & PG_FRAME) {
if ((oldpte & PG_SREF) == 0)
rmpage(pmap, pteptr);
else {
PMAP_UNLOCK;
panic("pmap_enter on PG_SREF page");
}
}
if (pv->pv_pmap == NULL) {
pv->pv_vaddr = v;
pv->pv_pmap = pmap;
} else {
tmp = get_pventry();
tmp->pv_vaddr = v;
tmp->pv_pmap = pmap;
tmp->pv_next = pv->pv_next;
pv->pv_next = tmp;
}
}
pmap->pm_stats.resident_count++;
if ((flags & PMAP_WIRED) != 0)
pmap->pm_stats.wired_count++;
if (flags & (VM_PROT_READ|VM_PROT_WRITE)) {
pv->pv_attr |= PG_V;
newpte |= PG_V;
}
if (flags & VM_PROT_WRITE)
pv->pv_attr |= PG_M;
if (flags & PMAP_WIRED)
newpte |= PG_V;
mapin8(pteptr, newpte);
if (pventries < 10)
more_pventries();
PMAP_UNLOCK;
mtpr(0, PR_TBIA);
return 0;
growfail:
PMAP_UNLOCK;
if (flags & PMAP_CANFAIL)
return ENOMEM;
panic("usrptmap space leakage");
}
vaddr_t
pmap_map(vaddr_t virtual, paddr_t pstart, paddr_t pend, int prot)
{
vaddr_t count;
int *pentry;
PMDEBUG(("pmap_map: virt %lx, pstart %lx, pend %lx, Sysmap %p\n",
virtual, pstart, pend, Sysmap));
pstart &= 0x7fffffffUL;
pend &= 0x7fffffffUL;
virtual &= 0x7fffffffUL;
pentry = &((int *)Sysmap)[virtual >> VAX_PGSHIFT];
for (count = pstart; count < pend; count += VAX_NBPG) {
*pentry++ = (count >> VAX_PGSHIFT)|PG_V|
(prot & VM_PROT_WRITE ? PG_KW : PG_KR);
}
return virtual + (count - pstart) + KERNBASE;
}
#if 0
bool
pmap_extract(pmap_t pmap, vaddr_t va, paddr_t *pap)
{
paddr_t pa = 0;
int *pte, sva;
PMDEBUG(("pmap_extract: pmap %p, va %lx\n",pmap, va));
if (va & KERNBASE) {
pa = kvtophys(va);
if (pap)
*pap = pa;
if (pa)
return (true);
return (false);
}
sva = PG_PFNUM(va);
if (va < 0x40000000) {
if (sva > pmap->pm_p0lr)
return false;
pte = (int *)pmap->pm_p0br;
} else {
if (sva < pmap->pm_p1lr)
return false;
pte = (int *)pmap->pm_p1br;
}
if (kvtopte(&pte[sva])->pg_pfn) {
if (pap)
*pap = (pte[sva] & PG_FRAME) << VAX_PGSHIFT;
return (true);
}
return (false);
}
#endif
void
pmap_protect_long(pmap_t pmap, vaddr_t start, vaddr_t end, vm_prot_t prot)
{
struct pte *pt, *pts, *ptd;
int pr, lr;
PMDEBUG(("pmap_protect: pmap %p, start %lx, end %lx, prot %x\n",
pmap, start, end,prot));
PMAP_LOCK;
switch (SEGTYPE(start)) {
case SYSSEG:
pt = Sysmap;
#ifdef DIAGNOSTIC
if (((end & 0x3fffffff) >> VAX_PGSHIFT) > mfpr(PR_SLR))
panic("pmap_protect: outside SLR: %lx", end);
#endif
start &= ~KERNBASE;
end &= ~KERNBASE;
pr = (prot & VM_PROT_WRITE ? PROT_KW : PROT_KR);
break;
case P1SEG:
if (vax_btop(end - 0x40000000) <= pmap->pm_p1lr) {
PMAP_UNLOCK;
return;
}
if (vax_btop(start - 0x40000000) < pmap->pm_p1lr)
start = pmap->pm_p1lr * VAX_NBPG;
pt = pmap->pm_p1br;
start &= 0x3fffffff;
end = (end == KERNBASE ? end >> 1 : end & 0x3fffffff);
pr = (prot & VM_PROT_WRITE ? PROT_RW : PROT_RO);
break;
case P0SEG:
lr = pmap->pm_p0lr;
if (vax_btop(start) > lr) {
PMAP_UNLOCK;
return;
}
if (vax_btop(end) > lr)
end = lr * VAX_NBPG;
pt = pmap->pm_p0br;
pr = (prot & VM_PROT_WRITE ? PROT_RW : PROT_RO);
break;
default:
panic("unsupported segtype: %d", SEGTYPE(start));
}
pts = &pt[start >> VAX_PGSHIFT];
ptd = &pt[end >> VAX_PGSHIFT];
#ifdef DEBUG
if (((int)pts - (int)pt) & 7)
panic("pmap_remove: pts not even");
if (((int)ptd - (int)pt) & 7)
panic("pmap_remove: ptd not even");
#endif
while (pts < ptd) {
if (kvtopte(pts)->pg_pfn && *(int *)pts) {
if (prot == VM_PROT_NONE) {
if ((*(int *)pts & PG_SREF) == 0)
rmpage(pmap, (u_int *)pts);
#ifdef DEBUG
else {
PMAP_UNLOCK;
panic("pmap_remove PG_SREF page");
}
#endif
memset(pts, 0, sizeof(struct pte) * LTOHPN);
if (pt != Sysmap) {
if (ptpinuse(pts) == 0)
rmptep(pts);
}
} else {
pts[0].pg_prot = pr;
pts[1].pg_prot = pr;
pts[2].pg_prot = pr;
pts[3].pg_prot = pr;
pts[4].pg_prot = pr;
pts[5].pg_prot = pr;
pts[6].pg_prot = pr;
pts[7].pg_prot = pr;
}
}
pts += LTOHPN;
}
PMAP_UNLOCK;
#ifdef MULTIPROCESSOR
cpu_send_ipi(IPI_DEST_ALL, IPI_TBIA);
#endif
mtpr(0, PR_TBIA);
}
int pmap_simulref(int bits, int addr);
int
pmap_simulref(int bits, int addr)
{
u_int *pte;
struct pv_entry *pv;
paddr_t pa;
PMDEBUG(("pmap_simulref: bits %x addr %x\n", bits, addr));
#ifdef DEBUG
if (bits & 1)
panic("pte trans len");
#endif
addr &= ~PGOFSET;
if (addr >= 0) {
if ((addr << 1) < 0)
pte = (u_int *)mfpr(PR_P1BR);
else
pte = (u_int *)mfpr(PR_P0BR);
pte += PG_PFNUM(addr);
if (bits & 2) {
pte = (u_int *)kvtopte(vax_trunc_page(pte));
if (pte[0] == 0)
return 1;
panic("pmap_simulref");
pa = (u_int)pte & ~KERNBASE;
} else
pa = Sysmap[PG_PFNUM(pte)].pg_pfn << VAX_PGSHIFT;
} else {
pte = (u_int *)kvtopte(addr);
pa = (u_int)pte & ~KERNBASE;
}
pte[0] |= PG_V;
pte[1] |= PG_V;
pte[2] |= PG_V;
pte[3] |= PG_V;
pte[4] |= PG_V;
pte[5] |= PG_V;
pte[6] |= PG_V;
pte[7] |= PG_V;
if (!IOSPACE_P(pa)) {
PMAP_LOCK;
pv = pv_table + (pa >> PGSHIFT);
pv->pv_attr |= PG_V;
if (bits & 4)
pv->pv_attr |= PG_M;
PMAP_UNLOCK;
}
return 0;
}
bool
pmap_clear_reference(struct vm_page *pg)
{
struct pv_entry *pv = pmap_pg_to_pv(pg);
struct pte *pte;
bool ref;
PMDEBUG(("pmap_clear_reference: pv_entry %p\n", pv));
PMAP_LOCK;
ref = ISSET(pv->pv_attr, PG_V);
CLR(pv->pv_attr, PG_V);
if (pv->pv_pmap != NULL) do {
pte = vaddrtopte(pv);
if (pte[0].pg_w == 0) {
pte[0].pg_v = 0; pte[1].pg_v = 0;
pte[2].pg_v = 0; pte[3].pg_v = 0;
pte[4].pg_v = 0; pte[5].pg_v = 0;
pte[6].pg_v = 0; pte[7].pg_v = 0;
}
} while ((pv = pv->pv_next) != NULL);
PMAP_UNLOCK;
#ifdef MULTIPROCESSOR
cpu_send_ipi(IPI_DEST_ALL, IPI_TBIA);
#endif
mtpr(0, PR_TBIA);
return ref;
}
bool
pmap_is_modified(struct vm_page *pg)
{
struct pv_entry *pv = pmap_pg_to_pv(pg);
bool rv;
PMDEBUG(("pmap_is_modified: pv_entry %p ", pv));
PMAP_LOCK;
rv = ISSET(pv->pv_attr, PG_M);
if (rv == false && pv->pv_pmap != NULL) do {
const struct pte * const pte = vaddrtopte(pv);
if (pte[0].pg_m | pte[1].pg_m | pte[2].pg_m | pte[3].pg_m
| pte[4].pg_m | pte[5].pg_m | pte[6].pg_m | pte[7].pg_m) {
rv = true;
SET(pv->pv_attr, PG_M);
break;
}
} while ((pv = pv->pv_next) != NULL);
PMAP_UNLOCK;
return rv;
}
bool
pmap_clear_modify(struct vm_page *pg)
{
struct pv_entry *pv = pmap_pg_to_pv(pg);
bool rv = false;
PMDEBUG(("pmap_clear_modify: pv_entry %p\n", pv));
PMAP_LOCK;
rv = ISSET(pv->pv_attr, PG_M);
CLR(pv->pv_attr, PG_M);
if (pv->pv_pmap != NULL) do {
struct pte * const pte = vaddrtopte(pv);
if (pte[0].pg_m | pte[1].pg_m | pte[2].pg_m | pte[3].pg_m |
pte[4].pg_m | pte[5].pg_m | pte[6].pg_m | pte[7].pg_m) {
rv = true;
}
pte[0].pg_m = pte[1].pg_m = pte[2].pg_m = pte[3].pg_m = 0;
pte[4].pg_m = pte[5].pg_m = pte[6].pg_m = pte[7].pg_m = 0;
} while ((pv = pv->pv_next) != NULL);
PMAP_UNLOCK;
return rv;
}
void
pmap_page_protect_long(struct pv_entry *pv, vm_prot_t prot)
{
struct pte *pt;
struct pv_entry *opv, *pl;
int *g;
PMDEBUG(("pmap_page_protect: pv %p, prot %x\n", pv, prot));
if (prot == VM_PROT_ALL)
return;
PMAP_LOCK;
if (prot == VM_PROT_NONE) {
g = (int *)vaddrtopte(pv);
if (g) {
pmap_decrement_stats(pv->pv_pmap, (g[0] & PG_W) != 0);
if ((pv->pv_attr & (PG_V|PG_M)) != (PG_V|PG_M))
pv->pv_attr |=
g[0]|g[1]|g[2]|g[3]|g[4]|g[5]|g[6]|g[7];
memset(g, 0, sizeof(struct pte) * LTOHPN);
if (pv->pv_pmap != pmap_kernel()) {
if (ptpinuse(g) == 0)
rmptep((void *)g);
}
pv->pv_vaddr = NOVADDR;
pv->pv_pmap = NULL;
}
pl = pv->pv_next;
pv->pv_pmap = 0;
pv->pv_next = 0;
while (pl) {
g = (int *)vaddrtopte(pl);
pmap_decrement_stats(pl->pv_pmap, (g[0] & PG_W) != 0);
if ((pv->pv_attr & (PG_V|PG_M)) != (PG_V|PG_M))
pv->pv_attr |=
g[0]|g[1]|g[2]|g[3]|g[4]|g[5]|g[6]|g[7];
memset(g, 0, sizeof(struct pte) * LTOHPN);
if (pl->pv_pmap != pmap_kernel()) {
if (ptpinuse(g) == 0)
rmptep((void *)g);
}
opv = pl;
pl = pl->pv_next;
free_pventry(opv);
}
} else {
do {
int pr;
pt = vaddrtopte(pv);
if (pt == 0)
continue;
pr = ((vaddr_t)pt < ptemapstart ? PROT_KR : PROT_RO);
pt[0].pg_prot = pr; pt[1].pg_prot = pr;
pt[2].pg_prot = pr; pt[3].pg_prot = pr;
pt[4].pg_prot = pr; pt[5].pg_prot = pr;
pt[6].pg_prot = pr; pt[7].pg_prot = pr;
} while ((pv = pv->pv_next));
}
PMAP_UNLOCK;
#ifdef MULTIPROCESSOR
cpu_send_ipi(IPI_DEST_ALL, IPI_TBIA);
#endif
mtpr(0, PR_TBIA);
}
static void
pmap_remove_pcb(struct pmap *pm, struct pcb *thispcb)
{
struct pcb *pcb, **pcbp;
for (pcbp = &pm->pm_pcbs;
(pcb = *pcbp) != NULL;
pcbp = &pcb->pcb_pmnext) {
#ifdef DIAGNOSTIC
if (pcb->pcb_pm != pm)
panic("pmap_remove_pcb: pcb %p (pm %p) not owned by pmap %p",
pcb, pcb->pcb_pm, pm);
#endif
if (pcb == thispcb) {
*pcbp = pcb->pcb_pmnext;
thispcb->pcb_pm = NULL;
return;
}
}
#ifdef DIAGNOSTIC
panic("pmap_remove_pcb: pmap %p: pcb %p not in list", pm, thispcb);
#endif
}
void
pmap_activate(struct lwp *l)
{
struct pcb * const pcb = lwp_getpcb(l);
struct pmap * const pmap = l->l_proc->p_vmspace->vm_map.pmap;
PMDEBUG(("pmap_activate: l %p\n", l));
pcb->P0BR = pmap->pm_p0br;
pcb->P0LR = pmap->pm_p0lr|AST_PCB;
pcb->P1BR = pmap->pm_p1br;
pcb->P1LR = pmap->pm_p1lr;
if (pcb->pcb_pm != pmap) {
if (pcb->pcb_pm != NULL)
pmap_remove_pcb(pcb->pcb_pm, pcb);
pcb->pcb_pmnext = pmap->pm_pcbs;
pmap->pm_pcbs = pcb;
pcb->pcb_pm = pmap;
}
if (l == curlwp) {
mtpr((uintptr_t)pmap->pm_p0br, PR_P0BR);
mtpr(pmap->pm_p0lr|AST_PCB, PR_P0LR);
mtpr((uintptr_t)pmap->pm_p1br, PR_P1BR);
mtpr(pmap->pm_p1lr, PR_P1LR);
mtpr(0, PR_TBIA);
}
}
void
pmap_deactivate(struct lwp *l)
{
struct pcb * const pcb = lwp_getpcb(l);
struct pmap * const pmap = l->l_proc->p_vmspace->vm_map.pmap;
PMDEBUG(("pmap_deactivate: l %p\n", l));
if (pcb->pcb_pm == NULL)
return;
#ifdef DIAGNOSTIC
if (pcb->pcb_pm != pmap)
panic("pmap_deactivate: lwp %p pcb %p not owned by pmap %p",
l, pcb, pmap);
#endif
pmap_remove_pcb(pmap, pcb);
}
void
pmap_unwire(pmap_t pmap, vaddr_t v)
{
int *pte;
PMDEBUG(("pmap_unwire: pmap %p v %lx\n", pmap, v));
PMAP_LOCK;
if (v & KERNBASE) {
pte = (int *)kvtopte(v);
} else {
if (v < 0x40000000)
pte = (int *)&pmap->pm_p0br[PG_PFNUM(v)];
else
pte = (int *)&pmap->pm_p1br[PG_PFNUM(v)];
}
pte[0] &= ~PG_W;
pmap->pm_stats.wired_count--;
PMAP_UNLOCK;
}
struct pv_entry *pv_list;
struct pv_entry *
get_pventry(void)
{
struct pv_entry *tmp;
if (pventries == 0)
panic("get_pventry");
tmp = pv_list;
pv_list = tmp->pv_next;
pventries--;
pvinuse++;
return tmp;
}
void
free_pventry(struct pv_entry *pv)
{
pv->pv_next = pv_list;
pv_list = pv;
pventries++;
pvinuse--;
}
void
more_pventries(void)
{
struct pv_entry *pv;
int i, count;
pv = (struct pv_entry *)getpage();
if (pv == NULL)
return;
count = PAGE_SIZE/sizeof(struct pv_entry);
for (i = 0; i < count - 1; i++)
pv[i].pv_next = &pv[i + 1];
pv[count - 1].pv_next = pv_list;
pv_list = pv;
pventries += count;
}
static int *ptpp;
vaddr_t
get_ptp(void)
{
int *a;
if ((a = ptpp)) {
ptpp = (int *)*ptpp;
memset(a, 0, VAX_NBPG);
return (vaddr_t)a;
}
a = (int *)getpage();
if (a != NULL) {
a[128] = (int)&a[256];
a[256] = (int)&a[384];
a[384] = (int)&a[512];
a[512] = (int)&a[640];
a[640] = (int)&a[768];
a[768] = (int)&a[896];
a[896] = (int)ptpp;
ptpp = &a[128];
}
return (vaddr_t)a;
}
void
free_ptp(paddr_t v)
{
v |= KERNBASE;
*(int *)v = (int)ptpp;
ptpp = (int *)v;
}