#include <sys/atomic.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/msgbuf.h>
#include <sys/pool.h>
#include <sys/exec.h>
#include <sys/core.h>
#include <sys/kcore.h>
#include <uvm/uvm.h>
#include <machine/pcb.h>
#include <machine/sparc64.h>
#include <machine/ctlreg.h>
#include <machine/hypervisor.h>
#include <machine/openfirm.h>
#include <machine/kcore.h>
#include <machine/pte.h>
#include <sparc64/sparc64/cache.h>
#ifdef DDB
#include <machine/db_machdep.h>
#include <ddb/db_output.h>
#define db_enter() __asm volatile("ta 1; nop")
#else
#define db_enter()
#endif
#define MEG (1<<20)
#define KB (1<<10)
paddr_t cpu0paddr;
extern int64_t pseg_get(struct pmap*, vaddr_t addr);
extern int pseg_set(struct pmap*, vaddr_t addr, int64_t tte, paddr_t spare);
extern void pmap_zero_phys(paddr_t pa);
extern void pmap_copy_phys(paddr_t src, paddr_t dst);
#define PV_ALIAS 0x1LL
#define PV_REF 0x2LL
#define PV_MOD 0x4LL
#define PV_MASK (0x03fLL)
#define PV_VAMASK (~(NBPG - 1))
#define PV_MATCH(pv,va) (!((((pv)->pv_va) ^ (va)) & PV_VAMASK))
#define PV_SETVA(pv,va) ((pv)->pv_va = (((va) & PV_VAMASK) | (((pv)->pv_va) & PV_MASK)))
static struct pool pv_pool;
static struct pool pmap_pool;
pv_entry_t pmap_remove_pv(struct pmap *pm, vaddr_t va, paddr_t pa);
pv_entry_t pmap_enter_pv(struct pmap *pm, pv_entry_t, vaddr_t va, paddr_t pa);
void pmap_page_cache(struct pmap *pm, paddr_t pa, int mode);
void pmap_bootstrap_cpu(paddr_t);
void pmap_release(struct pmap *);
pv_entry_t pa_to_pvh(paddr_t);
pv_entry_t
pa_to_pvh(paddr_t pa)
{
struct vm_page *pg;
pg = PHYS_TO_VM_PAGE(pa);
return pg ? &pg->mdpage.pvent : NULL;
}
static __inline u_int
pmap_tte2flags(u_int64_t tte)
{
if (CPU_ISSUN4V)
return (((tte & SUN4V_TLB_ACCESS) ? PV_REF : 0) |
((tte & SUN4V_TLB_MODIFY) ? PV_MOD : 0));
else
return (((tte & SUN4U_TLB_ACCESS) ? PV_REF : 0) |
((tte & SUN4U_TLB_MODIFY) ? PV_MOD : 0));
}
pte_t *tsb_dmmu;
pte_t *tsb_immu;
int tsbsize;
#define TSBENTS (512 << tsbsize)
#define TSBSIZE (TSBENTS * 16)
#define TSB_TAG_INVALID (~0LL << 48)
#define TSB_DATA(g,sz,pa,priv,write,cache,aliased,valid,ie) \
(CPU_ISSUN4V ?\
SUN4V_TSB_DATA(g,sz,pa,priv,write,cache,aliased,valid,ie) : \
SUN4U_TSB_DATA(g,sz,pa,priv,write,cache,aliased,valid,ie))
#define TLB_V SUN4U_TLB_V
#define TLB_NFO (CPU_ISSUN4V ? SUN4V_TLB_NFO : SUN4U_TLB_NFO)
#define TLB_PA_MASK SUN4U_TLB_PA_MASK
#define TLB_TSB_LOCK (CPU_ISSUN4V ? SUN4V_TLB_TSB_LOCK : SUN4U_TLB_TSB_LOCK)
#ifdef SUN4V
struct tsb_desc *tsb_desc;
#endif
struct pmap kernel_pmap_;
vaddr_t ktext;
paddr_t ktextp;
vaddr_t ektext;
paddr_t ektextp;
vaddr_t kdata;
paddr_t kdatap;
vaddr_t ekdata;
paddr_t ekdatap;
static struct mem_region memlist[8];
vaddr_t vmmap;
struct mem_region *mem, *avail, *orig;
int memsize;
static int memh = 0, vmemh = 0;
static int ptelookup_va(vaddr_t va);
static __inline void
tsb_invalidate(int ctx, vaddr_t va)
{
int i;
int64_t tag;
i = ptelookup_va(va);
tag = TSB_TAG(0, ctx, va);
if (tsb_dmmu[i].tag == tag)
atomic_cas_ulong((volatile unsigned long *)&tsb_dmmu[i].tag,
tag, TSB_TAG_INVALID);
if (tsb_immu[i].tag == tag)
atomic_cas_ulong((volatile unsigned long *)&tsb_immu[i].tag,
tag, TSB_TAG_INVALID);
}
struct prom_map *prom_map;
int prom_map_size;
#ifdef DEBUG
#define PDB_BOOT 0x20000
#define PDB_BOOT1 0x40000
int pmapdebug = 0;
#define BDPRINTF(n, f) if (pmapdebug & (n)) prom_printf f
#else
#define BDPRINTF(n, f)
#endif
paddr_t *ctxbusy;
int numctx;
#define CTXENTRY (sizeof(paddr_t))
#define CTXSIZE (numctx * CTXENTRY)
struct mutex ctxmtx = MUTEX_INITIALIZER(IPL_HIGH);
int pmap_get_page(paddr_t *, const char *, struct pmap *);
void pmap_free_page(paddr_t, struct pmap *);
const struct page_size_map page_size_map[] = {
{ (4*1024*1024-1) & ~(8*1024-1), PGSZ_4M },
{ (512*1024-1) & ~(8*1024-1), PGSZ_512K },
{ (64*1024-1) & ~(8*1024-1), PGSZ_64K },
{ (8*1024-1) & ~(8*1024-1), PGSZ_8K },
{ 0, 0 }
};
static void
pmap_enter_kpage(vaddr_t va, int64_t data)
{
paddr_t newp;
newp = 0;
while (pseg_set(pmap_kernel(), va, data, newp) == 1) {
newp = 0;
if (!pmap_get_page(&newp, NULL, pmap_kernel())) {
prom_printf("pmap_enter_kpage: out of pages\n");
panic("pmap_enter_kpage");
}
BDPRINTF(PDB_BOOT1,
("pseg_set: pm=%p va=%p data=%lx newp %lx\r\n",
pmap_kernel(), va, (long)data, (long)newp));
}
}
#ifdef DEBUG
void
pmap_bootdebug(void)
{
int chosen;
char *cp;
char buf[128];
chosen = OF_finddevice("/chosen");
OF_getprop(chosen, "bootargs", buf, sizeof(buf));
cp = buf;
while (*cp != '-')
if (*cp++ == '\0')
return;
for (;;)
switch (*++cp) {
case '\0':
return;
case 'V':
pmapdebug |= PDB_BOOT|PDB_BOOT1;
break;
case 'D':
pmapdebug |= PDB_BOOT1;
break;
}
}
#endif
void
pmap_bootstrap(u_long kernelstart, u_long kernelend, u_int maxctx, u_int numcpus)
{
extern int data_start[], end[];
extern int msgbufmapped;
struct mem_region *mp, *mp1;
int msgbufsiz;
int pcnt;
size_t s, sz;
int i, j;
int64_t data;
vaddr_t va;
u_int64_t phys_msgbuf;
paddr_t newkp;
vaddr_t newkv, firstaddr, intstk;
vsize_t kdsize, ktsize;
#ifdef DEBUG
pmap_bootdebug();
#endif
BDPRINTF(PDB_BOOT, ("Entered pmap_bootstrap.\r\n"));
uvmexp.pagesize = NBPG;
uvm_setpagesize();
if ((vmemh = OF_finddevice("/virtual-memory")) == -1) {
prom_printf("no virtual-memory?");
OF_exit();
}
bzero((caddr_t)memlist, sizeof(memlist));
if (OF_getprop(vmemh, "available", memlist, sizeof(memlist)) <= 0) {
prom_printf("no vmemory avail?");
OF_exit();
}
#ifdef DEBUG
if (pmapdebug & PDB_BOOT) {
prom_printf("Available virtual memory:\r\n");
for (mp = memlist; mp->size; mp++) {
prom_printf("memlist start %p size %lx\r\n",
(void *)(u_long)mp->start,
(u_long)mp->size);
}
prom_printf("End of available virtual memory\r\n");
}
#endif
msgbufp = (struct msgbuf *)(vaddr_t)MSGBUF_VA;
msgbufsiz = 4*NBPG ;
BDPRINTF(PDB_BOOT, ("Trying to allocate msgbuf at %lx, size %lx\r\n",
(long)msgbufp, (long)msgbufsiz));
if ((long)msgbufp !=
(long)(phys_msgbuf = prom_claim_virt((vaddr_t)msgbufp, msgbufsiz)))
prom_printf(
"cannot get msgbuf VA, msgbufp=%p, phys_msgbuf=%lx\r\n",
(void *)msgbufp, (long)phys_msgbuf);
phys_msgbuf = prom_get_msgbuf(msgbufsiz, MMU_PAGE_ALIGN);
BDPRINTF(PDB_BOOT,
("We should have the memory at %lx, let's map it in\r\n",
phys_msgbuf));
if (prom_map_phys(phys_msgbuf, msgbufsiz, (vaddr_t)msgbufp,
-1) == -1)
prom_printf("Failed to map msgbuf\r\n");
else
BDPRINTF(PDB_BOOT, ("msgbuf mapped at %p\r\n",
(void *)msgbufp));
msgbufmapped = 1;
initmsgbuf((caddr_t)msgbufp, msgbufsiz);
BDPRINTF(PDB_BOOT, ("translating kernelstart %p\r\n",
(void *)kernelstart));
ktext = kernelstart;
ktextp = prom_vtop(kernelstart);
kdata = (vaddr_t)data_start;
kdatap = prom_vtop(kdata);
ekdata = (vaddr_t)end;
for (mp1 = mp = memlist; mp->size; mp++) {
if (mp->start >= ekdata && (mp1->start < ekdata ||
mp1->start > mp->start))
mp1 = mp;
}
if (mp1->start < kdata)
prom_printf("Kernel at end of vmem???\r\n");
BDPRINTF(PDB_BOOT1,
("Kernel data is mapped at %lx, next free seg: %lx, %lx\r\n",
(long)kdata, (u_long)mp1->start, (u_long)mp1->size));
firstaddr = (ekdata + 07) & ~ 07;
ekdata += 100*KB;
if (ekdata < mp1->start)
ekdata = mp1->start;
#define valloc(name, type, num) (name) = (type *)firstaddr; firstaddr += (num)
kdsize = round_page(ekdata - kdata);
BDPRINTF(PDB_BOOT1, ("Kernel data size is %lx\r\n", (long)kdsize));
if ((kdatap & (4*MEG-1)) == 0) {
psize_t szdiff = (4*MEG - kdsize) & (4*MEG - 1);
BDPRINTF(PDB_BOOT1, ("Need to extend dseg by %lx\r\n",
(long)szdiff));
if (szdiff) {
newkp = kdatap + kdsize;
newkv = kdata + kdsize;
if (newkp != prom_claim_phys(newkp, szdiff)) {
prom_printf("pmap_bootstrap: could not claim "
"physical dseg extension "
"at %lx size %lx\r\n",
newkp, szdiff);
goto remap_data;
}
if (prom_claim_virt(newkv, szdiff) != newkv)
prom_printf("pmap_bootstrap: could not claim "
"virtual dseg extension "
"at size %lx\r\n", newkv, szdiff);
prom_map_phys(newkp, szdiff, newkv, -1);
}
} else {
psize_t sz;
remap_data:
sz = (kdsize + 4*MEG - 1) & ~(4*MEG-1);
BDPRINTF(PDB_BOOT1,
("Allocating new %lx kernel data at 4MB boundary\r\n",
(u_long)sz));
if ((newkp = prom_alloc_phys(sz, 4*MEG)) == (paddr_t)-1 ) {
prom_printf("Cannot allocate new kernel\r\n");
OF_exit();
}
BDPRINTF(PDB_BOOT1, ("Allocating new va for buffer at %llx\r\n",
(u_int64_t)newkp));
if ((newkv = (vaddr_t)prom_alloc_virt(sz, 8)) ==
(vaddr_t)-1) {
prom_printf("Cannot allocate new kernel va\r\n");
OF_exit();
}
BDPRINTF(PDB_BOOT1, ("Mapping in buffer %llx at %llx\r\n",
(u_int64_t)newkp, (u_int64_t)newkv));
prom_map_phys(newkp, sz, (vaddr_t)newkv, -1);
BDPRINTF(PDB_BOOT1, ("Copying %ld bytes kernel data...",
kdsize));
bzero((void *)newkv, sz);
bcopy((void *)kdata, (void *)newkv, kdsize);
BDPRINTF(PDB_BOOT1, ("done. Swapping maps..unmap new\r\n"));
prom_unmap_virt((vaddr_t)newkv, sz);
BDPRINTF(PDB_BOOT, ("remap old "));
#if 0
prom_unmap_virt((vaddr_t)kdatap, kdsize);
#endif
prom_map_phys(newkp, sz, kdata, -1);
BDPRINTF(PDB_BOOT1, ("free old\r\n"));
prom_free_phys(kdatap, kdsize);
kdatap = newkp;
BDPRINTF(PDB_BOOT1,
("pmap_bootstrap: firstaddr is %lx virt (%lx phys)"
"avail for kernel\r\n", (u_long)firstaddr,
(u_long)prom_vtop(firstaddr)));
}
BDPRINTF(PDB_BOOT, ("pmap_bootstrap: getting phys installed\r\n"));
if ((memh = OF_finddevice("/memory")) == -1) {
prom_printf("no memory?");
OF_exit();
}
memsize = OF_getproplen(memh, "reg") + 2 * sizeof(struct mem_region);
valloc(mem, struct mem_region, memsize);
bzero((caddr_t)mem, memsize);
if (OF_getprop(memh, "reg", mem, memsize) <= 0) {
prom_printf("no memory installed?");
OF_exit();
}
#ifdef DEBUG
if (pmapdebug & PDB_BOOT1) {
prom_printf("Installed physical memory:\r\n");
for (mp = mem; mp->size; mp++) {
prom_printf("memlist start %lx size %lx\r\n",
(u_long)mp->start, (u_long)mp->size);
}
}
#endif
BDPRINTF(PDB_BOOT1, ("Calculating physmem:"));
for (mp = mem; mp->size; mp++)
physmem += atop(mp->size);
BDPRINTF(PDB_BOOT1, (" result %x or %d pages\r\n",
(int)physmem, (int)physmem));
tsbsize = 0;
#ifdef SMALL_KERNEL
while ((physmem >> tsbsize) > atop(64 * MEG) && tsbsize < 2)
#else
while ((physmem >> tsbsize) > atop(64 * MEG) && tsbsize < 7)
#endif
tsbsize++;
sz = OF_getproplen(vmemh, "translations");
valloc(prom_map, struct prom_map, sz);
if (OF_getprop(vmemh, "translations", (void *)prom_map, sz) <= 0) {
prom_printf("no translations installed?");
OF_exit();
}
prom_map_size = sz / sizeof(struct prom_map);
#ifdef DEBUG
if (pmapdebug & PDB_BOOT) {
prom_printf("Prom xlations:\r\n");
for (i = 0; i < prom_map_size; i++) {
prom_printf("start %016lx size %016lx tte %016lx\r\n",
(u_long)prom_map[i].vstart,
(u_long)prom_map[i].vsize,
(u_long)prom_map[i].tte);
}
prom_printf("End of prom xlations\r\n");
}
#endif
ktsize = 0;
for (i = 0; i < prom_map_size; i++)
if (prom_map[i].vstart == ktext + ktsize)
ktsize += prom_map[i].vsize;
if (ktsize == 0)
panic("No kernel text segment!");
ektext = ktext + ktsize;
if (ktextp & (4*MEG-1)) {
BDPRINTF(PDB_BOOT1,
("Allocating new %lx kernel text at 4MB boundary\r\n",
(u_long)ktsize));
if ((newkp = prom_alloc_phys(ktsize, 4*MEG)) == 0 ) {
prom_printf("Cannot allocate new kernel text\r\n");
OF_exit();
}
BDPRINTF(PDB_BOOT1, ("Allocating new va for buffer at %llx\r\n",
(u_int64_t)newkp));
if ((newkv = (vaddr_t)prom_alloc_virt(ktsize, 8)) ==
(vaddr_t)-1) {
prom_printf("Cannot allocate new kernel text va\r\n");
OF_exit();
}
BDPRINTF(PDB_BOOT1, ("Mapping in buffer %lx at %lx\r\n",
(u_long)newkp, (u_long)newkv));
prom_map_phys(newkp, ktsize, (vaddr_t)newkv, -1);
BDPRINTF(PDB_BOOT1, ("Copying %ld bytes kernel text...",
ktsize));
bcopy((void *)ktext, (void *)newkv,
ktsize);
BDPRINTF(PDB_BOOT1, ("done. Swapping maps..unmap new\r\n"));
prom_unmap_virt((vaddr_t)newkv, 4*MEG);
BDPRINTF(PDB_BOOT, ("remap old "));
#if 0
prom_unmap_virt((vaddr_t)ktextp, ktsize);
#endif
prom_map_phys(newkp, ktsize, ktext, -1);
BDPRINTF(PDB_BOOT1, ("free old\r\n"));
prom_free_phys(ktextp, ktsize);
ktextp = newkp;
BDPRINTF(PDB_BOOT1,
("pmap_bootstrap: firstaddr is %lx virt (%lx phys)"
"avail for kernel\r\n", (u_long)firstaddr,
(u_long)prom_vtop(firstaddr)));
if (OF_getprop(vmemh, "translations", (void *)prom_map, sz) <=
0) {
prom_printf("no translations installed?");
OF_exit();
}
#ifdef DEBUG
if (pmapdebug & PDB_BOOT) {
prom_printf("New prom xlations:\r\n");
for (i = 0; i < prom_map_size; i++) {
prom_printf("start %016lx size %016lx tte %016lx\r\n",
(u_long)prom_map[i].vstart,
(u_long)prom_map[i].vsize,
(u_long)prom_map[i].tte);
}
prom_printf("End of prom xlations\r\n");
}
#endif
}
ektextp = ktextp + ktsize;
for(i = 0; i < prom_map_size; i++) {
if (prom_map[i].vstart >= kdata &&
prom_map[i].vstart <= firstaddr) {
prom_map[i].vstart = 0;
prom_map[i].vsize = 0;
}
if (prom_map[i].vstart >= ktext &&
prom_map[i].vstart <= ektext) {
prom_map[i].vstart = 0;
prom_map[i].vsize = 0;
}
for(j = i; j < prom_map_size; j++) {
if (prom_map[j].vstart >= kdata &&
prom_map[j].vstart <= firstaddr)
continue;
if (prom_map[j].vstart >= ktext &&
prom_map[j].vstart <= ektext)
continue;
if (prom_map[j].vstart > prom_map[i].vstart) {
struct prom_map tmp;
tmp = prom_map[i];
prom_map[i] = prom_map[j];
prom_map[j] = tmp;
}
}
}
#ifdef DEBUG
if (pmapdebug & PDB_BOOT) {
prom_printf("Prom xlations:\r\n");
for (i = 0; i < prom_map_size; i++) {
prom_printf("start %016lx size %016lx tte %016lx\r\n",
(u_long)prom_map[i].vstart,
(u_long)prom_map[i].vsize,
(u_long)prom_map[i].tte);
}
prom_printf("End of prom xlations\r\n");
}
#endif
if ((cpu0paddr = prom_alloc_phys(numcpus * 8*NBPG, 8*NBPG)) == 0 ) {
prom_printf("Cannot allocate new cpu_info\r\n");
OF_exit();
}
sz = OF_getproplen(memh, "available") + sizeof(struct mem_region);
valloc(orig, struct mem_region, sz);
bzero((caddr_t)orig, sz);
if (OF_getprop(memh, "available", orig, sz) <= 0) {
prom_printf("no available RAM?");
OF_exit();
}
#ifdef DEBUG
if (pmapdebug & PDB_BOOT1) {
prom_printf("Available physical memory:\r\n");
for (mp = orig; mp->size; mp++) {
prom_printf("memlist start %lx size %lx\r\n",
(u_long)mp->start, (u_long)mp->size);
}
prom_printf("End of available physical memory\r\n");
}
#endif
valloc(avail, struct mem_region, sz);
bzero((caddr_t)avail, sz);
for (pcnt = 0, mp = orig, mp1 = avail; (mp1->size = mp->size);
mp++, mp1++) {
mp1->start = mp->start;
pcnt++;
}
numctx = maxctx;
valloc(ctxbusy, paddr_t, CTXSIZE);
bzero((caddr_t)ctxbusy, CTXSIZE);
BDPRINTF(PDB_BOOT1, ("firstaddr before TSB=%lx\r\n",
(u_long)firstaddr));
firstaddr = ((firstaddr + TSBSIZE - 1) & ~(TSBSIZE-1));
#ifdef DEBUG
i = (firstaddr + (NBPG-1)) & ~(NBPG-1);
if ((int)firstaddr < i) {
prom_printf("TSB alloc fixup failed\r\n");
prom_printf("frobbed i, firstaddr before TSB=%x, %lx\r\n",
(int)i, (u_long)firstaddr);
panic("TSB alloc");
OF_exit();
}
#endif
BDPRINTF(PDB_BOOT, ("frobbed i, firstaddr before TSB=%x, %lx\r\n",
(int)i, (u_long)firstaddr));
valloc(tsb_dmmu, pte_t, TSBSIZE);
bzero(tsb_dmmu, TSBSIZE);
valloc(tsb_immu, pte_t, TSBSIZE);
bzero(tsb_immu, TSBSIZE);
BDPRINTF(PDB_BOOT1, ("firstaddr after TSB=%lx\r\n", (u_long)firstaddr));
BDPRINTF(PDB_BOOT1, ("TSB allocated at %p size %08x\r\n", (void *)tsb_dmmu,
(int)TSBSIZE));
#ifdef SUN4V
if (CPU_ISSUN4V) {
valloc(tsb_desc, struct tsb_desc, sizeof(struct tsb_desc));
bzero(tsb_desc, sizeof(struct tsb_desc));
tsb_desc->td_idxpgsz = 0;
tsb_desc->td_assoc = 1;
tsb_desc->td_size = TSBENTS;
tsb_desc->td_ctxidx = -1;
tsb_desc->td_pgsz = 0xf;
tsb_desc->td_pa = (paddr_t)tsb_dmmu + kdatap - kdata;
}
#endif
BDPRINTF(PDB_BOOT1, ("firstaddr after pmap=%08lx\r\n",
(u_long)firstaddr));
BDPRINTF(PDB_BOOT, ("kernel virtual size %08lx - %08lx\r\n",
(u_long)kernelstart, (u_long)firstaddr));
kdata = kdata & ~PGOFSET;
ekdata = firstaddr;
ekdata = (ekdata + PGOFSET) & ~PGOFSET;
BDPRINTF(PDB_BOOT1, ("kernel virtual size %08lx - %08lx\r\n",
(u_long)kernelstart, (u_long)kernelend));
ekdatap = ekdata - kdata + kdatap;
if(ekdatap > (kdatap + 4*MEG)) {
prom_printf("Kernel size exceeds 4MB\r\n");
}
#ifdef DEBUG
if (pmapdebug & PDB_BOOT1) {
prom_printf("Available %lx physical memory before cleanup:\r\n",
(u_long)avail);
for (mp = avail; mp->size; mp++) {
prom_printf("memlist start %lx size %lx\r\n",
(u_long)mp->start,
(u_long)mp->size);
}
prom_printf("End of available physical memory before cleanup\r\n");
prom_printf("kernel physical text size %08lx - %08lx\r\n",
(u_long)ktextp, (u_long)ektextp);
prom_printf("kernel physical data size %08lx - %08lx\r\n",
(u_long)kdatap, (u_long)ekdatap);
}
#endif
for (i = 0; i < pcnt; i++) {
for (j = i; j < pcnt; j++) {
if (avail[j].start < avail[i].start) {
struct mem_region tmp;
tmp = avail[i];
avail[i] = avail[j];
avail[j] = tmp;
}
}
}
if (avail->start == 0) {
avail->start += NBPG;
avail->size -= NBPG;
}
for (mp = avail; mp->size; mp++) {
s = mp->start + mp->size;
if (mp->start < kdatap && s > roundup(ekdatap, 4*MEG)) {
avail[pcnt].start = roundup(ekdatap, 4*MEG);
avail[pcnt++].size = s - kdatap;
mp->size = kdatap - mp->start;
}
if (mp->start >= kdatap &&
mp->start < roundup(ekdatap, 4*MEG)) {
s = ekdatap - mp->start;
if (mp->size > s)
mp->size -= s;
else
mp->size = 0;
mp->start = roundup(ekdatap, 4*MEG);
}
s = mp->start + mp->size;
if (s > kdatap && s < roundup(ekdatap, 4*MEG))
mp->size -= s - kdatap;
s = mp->start % NBPG;
if (mp->size >= s) {
mp->size -= s;
mp->start += s;
}
mp->size -= mp->size % NBPG;
if (mp->size == 0) {
bcopy(mp + 1, mp,
(pcnt - (mp - avail)) * sizeof *mp);
pcnt--;
mp--;
continue;
}
s = mp->start;
sz = mp->size;
for (mp1 = avail; mp1 < mp; mp1++)
if (s < mp1->start)
break;
if (mp1 < mp) {
bcopy(mp1, mp1 + 1, (char *)mp - (char *)mp1);
mp1->start = s;
mp1->size = sz;
}
uvm_page_physload(
atop(mp->start),
atop(mp->start+mp->size),
atop(mp->start),
atop(mp->start+mp->size), 0);
}
#if 0
prom_unmap_virt((vaddr_t)ekdata, roundup(ekdata, 4*MEG) - ekdata);
if (ekdatap < roundup(kdatap, 4*MEG))) {
uvm_page_physload(atop(ekdatap),
atop(roundup(ekdatap, (4*MEG))),
atop(ekdatap),
atop(roundup(ekdatap, (4*MEG))), 0);
}
#endif
#ifdef DEBUG
if (pmapdebug & PDB_BOOT) {
prom_printf("Available physical memory after cleanup:\r\n");
for (mp = avail; mp->size; mp++) {
prom_printf("avail start %lx size %lx\r\n",
(long)mp->start, (long)mp->size);
}
prom_printf("End of available physical memory after cleanup\r\n");
}
#endif
mtx_init(&pmap_kernel()->pm_mtx, IPL_VM);
pmap_kernel()->pm_refs = 1;
pmap_kernel()->pm_ctx = 0;
{
paddr_t newp;
do {
pmap_get_page(&newp, NULL, pmap_kernel());
} while (!newp);
pmap_kernel()->pm_segs=(int64_t *)(u_long)newp;
pmap_kernel()->pm_physaddr = newp;
((paddr_t*)ctxbusy)[0] = pmap_kernel()->pm_physaddr;
}
BDPRINTF(PDB_BOOT, ("pmap_kernel()->pm_physaddr = %lx\r\n",
(long)pmap_kernel()->pm_physaddr));
#ifdef DEBUG
BDPRINTF(PDB_BOOT1, ("Calling consinit()\r\n"));
if (pmapdebug & PDB_BOOT1) consinit();
BDPRINTF(PDB_BOOT1, ("Inserting mesgbuf into pmap_kernel()\r\n"));
#endif
va = (vaddr_t)msgbufp;
prom_map_phys(phys_msgbuf, msgbufsiz, (vaddr_t)msgbufp, -1);
while (msgbufsiz) {
data = TSB_DATA(0 ,
PGSZ_8K,
phys_msgbuf,
1 ,
1 ,
1 ,
0 ,
1 ,
0 );
pmap_enter_kpage(va, data);
va += PAGE_SIZE;
msgbufsiz -= PAGE_SIZE;
phys_msgbuf += PAGE_SIZE;
}
BDPRINTF(PDB_BOOT1, ("Done inserting mesgbuf into pmap_kernel()\r\n"));
BDPRINTF(PDB_BOOT1, ("Inserting PROM mappings into pmap_kernel()\r\n"));
data = (CPU_ISSUN4V ? SUN4V_TLB_EXEC : SUN4U_TLB_EXEC);
for (i = 0; i < prom_map_size; i++) {
if (prom_map[i].vstart && ((prom_map[i].vstart>>32) == 0)) {
for (j = 0; j < prom_map[i].vsize; j += NBPG) {
int k;
uint64_t tte;
for (k = 0; page_size_map[k].mask; k++) {
if (((prom_map[i].vstart |
prom_map[i].tte) &
page_size_map[k].mask) == 0 &&
page_size_map[k].mask <
prom_map[i].vsize)
break;
}
tte = prom_map[i].tte;
if (CPU_ISSUN4V)
tte &= ~SUN4V_TLB_SOFT_MASK;
else
tte &= ~(SUN4U_TLB_SOFT2_MASK |
SUN4U_TLB_SOFT_MASK);
pmap_enter_kpage(prom_map[i].vstart + j,
(tte + j) | data | page_size_map[k].code);
}
}
}
BDPRINTF(PDB_BOOT1, ("Done inserting PROM mappings into pmap_kernel()\r\n"));
vmmap = (vaddr_t)roundup(ekdata, 4*MEG);
vmmap += NBPG;
{
extern vaddr_t u0[2];
extern struct pcb *proc0paddr;
extern void main(void);
paddr_t pa;
u0[0] = vmmap;
u0[1] = vmmap + 2*USPACE;
BDPRINTF(PDB_BOOT1,
("Inserting stack 0 into pmap_kernel() at %p\r\n",
vmmap));
while (vmmap < u0[1]) {
int64_t data;
pmap_get_page(&pa, NULL, pmap_kernel());
prom_map_phys(pa, NBPG, vmmap, -1);
data = TSB_DATA(0 ,
PGSZ_8K,
pa,
1 ,
1 ,
1 ,
0 ,
1 ,
0 );
pmap_enter_kpage(vmmap, data);
vmmap += NBPG;
}
BDPRINTF(PDB_BOOT1,
("Done inserting stack 0 into pmap_kernel()\r\n"));
#ifdef DIAGNOSTIC
vmmap += NBPG;
#endif
intstk = vmmap = roundup(vmmap, 64*KB);
cpus = (struct cpu_info *)(intstk + CPUINFO_VA - INTSTACK);
BDPRINTF(PDB_BOOT1,
("Inserting cpu_info into pmap_kernel() at %p\r\n",
cpus));
pa = cpu0paddr;
prom_map_phys(pa, 64*KB, vmmap, -1);
prom_map_phys(pa, 64*KB, CPUINFO_VA, -1);
for (i=0; i<8; i++) {
int64_t data;
data = TSB_DATA(0 ,
PGSZ_8K,
pa,
1 ,
1 ,
1 ,
0 ,
1 ,
0 );
pmap_enter_kpage(vmmap, data);
vmmap += NBPG;
pa += NBPG;
}
BDPRINTF(PDB_BOOT1, ("Initializing cpu_info\r\n"));
bzero((void *)intstk, 8*NBPG);
cpus->ci_self = cpus;
cpus->ci_next = NULL;
cpus->ci_curproc = &proc0;
cpus->ci_cpcb = (struct pcb *)u0[0];
cpus->ci_cpcbpaddr = pseg_get(pmap_kernel(), u0[0]) &
TLB_PA_MASK;
cpus->ci_upaid = cpu_myid();
cpus->ci_cpuid = 0;
cpus->ci_flags = CPUF_RUNNING;
cpus->ci_fpproc = NULL;
cpus->ci_spinup = main;
cpus->ci_initstack = (void *)u0[1];
cpus->ci_paddr = cpu0paddr;
#ifdef SUN4V
cpus->ci_mmfsa = cpu0paddr;
#endif
proc0paddr = cpus->ci_cpcb;
cpu0paddr += 64 * KB;
BDPRINTF(PDB_BOOT1,
("Done inserting cpu_info into pmap_kernel()\r\n"));
}
vmmap = (vaddr_t)reserve_dumppages((caddr_t)(u_long)vmmap);
BDPRINTF(PDB_BOOT1, ("Finished pmap_bootstrap()\r\n"));
pmap_bootstrap_cpu(cpus->ci_paddr);
}
void sun4u_bootstrap_cpu(paddr_t);
void sun4v_bootstrap_cpu(paddr_t);
void
pmap_bootstrap_cpu(paddr_t intstack)
{
if (CPU_ISSUN4V)
sun4v_bootstrap_cpu(intstack);
else
sun4u_bootstrap_cpu(intstack);
}
extern void sun4u_set_tsbs(void);
void
sun4u_bootstrap_cpu(paddr_t intstack)
{
u_int64_t data;
paddr_t pa;
vaddr_t va;
int index;
int impl;
impl = (getver() & VER_IMPL) >> VER_IMPL_SHIFT;
index = 15;
for (va = ktext, pa = ktextp; va < ektext; va += 4*MEG, pa += 4*MEG) {
data = SUN4U_TSB_DATA(0, PGSZ_4M, pa, 1, 0, 1, 0, 1, 0);
data |= SUN4U_TLB_L;
prom_itlb_load(index, data, va);
prom_dtlb_load(index, data, va);
index--;
}
for (va = kdata, pa = kdatap; va < ekdata; va += 4*MEG, pa += 4*MEG) {
data = SUN4U_TSB_DATA(0, PGSZ_4M, pa, 1, 1, 1, 0, 1, 0);
data |= SUN4U_TLB_L;
prom_dtlb_load(index, data, va);
index--;
}
#ifdef MULTIPROCESSOR
if (impl >= IMPL_OLYMPUS_C && impl <= IMPL_JUPITER) {
pa = intstack + (CPUINFO_VA - INTSTACK);
pa += offsetof(struct cpu_info, ci_self);
va = ldxa(pa, ASI_PHYS_CACHED);
stxa(0x00, ASI_SCRATCH, va);
if ((CPU_JUPITERID % 2) == 1)
index--;
data = SUN4U_TSB_DATA(0, PGSZ_64K, intstack, 1, 1, 1, 0, 1, 0);
data |= SUN4U_TLB_L;
prom_dtlb_load(index, data, va - (CPUINFO_VA - INTSTACK));
sun4u_set_tsbs();
return;
}
#endif
#ifdef MULTIPROCESSOR
pa = intstack + (CPUINFO_VA - INTSTACK);
pa += offsetof(struct cpu_info, ci_self);
va = ldxa(pa, ASI_PHYS_CACHED);
data = SUN4U_TSB_DATA(0, PGSZ_64K, intstack, 1, 1, 1, 0, 1, 0);
data |= SUN4U_TLB_L;
prom_dtlb_load(index, data, va - (CPUINFO_VA - INTSTACK));
index--;
#endif
data = SUN4U_TSB_DATA(0, PGSZ_64K, intstack, 1, 1, 1, 0, 1, 0);
data |= SUN4U_TLB_L;
prom_dtlb_load(index, data, INTSTACK);
sun4u_set_tsbs();
}
void
sun4v_bootstrap_cpu(paddr_t intstack)
{
#ifdef SUN4V
u_int64_t data;
paddr_t pa;
vaddr_t va;
int err;
for (va = ktext, pa = ktextp; va < ektext; va += 4*MEG, pa += 4*MEG) {
data = SUN4V_TSB_DATA(0, PGSZ_4M, pa, 1, 0, 1, 0, 1, 0);
data |= SUN4V_TLB_X;
err = hv_mmu_map_perm_addr(va, data, MAP_ITLB|MAP_DTLB);
if (err != H_EOK)
prom_printf("err: %d\r\n", err);
}
for (va = kdata, pa = kdatap; va < ekdata; va += 4*MEG, pa += 4*MEG) {
data = SUN4V_TSB_DATA(0, PGSZ_4M, pa, 1, 1, 1, 0, 1, 0);
err = hv_mmu_map_perm_addr(va, data, MAP_DTLB);
if (err != H_EOK)
prom_printf("err: %d\r\n", err);
}
#ifndef MULTIPROCESSOR
data = SUN4V_TSB_DATA(0, PGSZ_64K, intstack, 1, 1, 1, 0, 1, 0);
err = hv_mmu_map_perm_addr(INTSTACK, data, MAP_DTLB);
if (err != H_EOK)
prom_printf("err: %d\r\n", err);
#else
pa = intstack + (CPUINFO_VA - INTSTACK);
pa += offsetof(struct cpu_info, ci_self);
stxa(0x00, ASI_SCRATCHPAD, ldxa(pa, ASI_PHYS_CACHED));
#endif
stxa(0x10, ASI_SCRATCHPAD, intstack + (CPUINFO_VA - INTSTACK));
err = hv_mmu_tsb_ctx0(1, (paddr_t)tsb_desc + kdatap - kdata);
if (err != H_EOK)
prom_printf("err: %d\r\n", err);
err = hv_mmu_tsb_ctxnon0(1, (paddr_t)tsb_desc + kdatap - kdata);
if (err != H_EOK)
prom_printf("err: %d\r\n", err);
#endif
}
void
pmap_init(void)
{
BDPRINTF(PDB_BOOT1, ("pmap_init()\r\n"));
if (PAGE_SIZE != NBPG)
panic("pmap_init: CLSIZE!=1");
pool_init(&pv_pool, sizeof(struct pv_entry), 0, IPL_VM, 0,
"pv_entry", NULL);
pool_init(&pmap_pool, sizeof(struct pmap), 0, IPL_NONE, 0,
"pmappl", NULL);
}
#define VM_MAXPHYS_ADDRESS ((vaddr_t)0x0000040000000000L)
static vaddr_t kbreak;
void
pmap_virtual_space(vaddr_t *start, vaddr_t *end)
{
if (vmmap < VM_MAXPHYS_ADDRESS)
vmmap = VM_MAXPHYS_ADDRESS;
*start = kbreak = (vaddr_t)(vmmap + 2*NBPG);
*end = VM_MAX_KERNEL_ADDRESS;
BDPRINTF(PDB_BOOT1, ("pmap_virtual_space: %x-%x\r\n", *start, *end));
}
vaddr_t
pmap_growkernel(vaddr_t maxkvaddr)
{
paddr_t pg;
struct pmap *pm = pmap_kernel();
if (maxkvaddr >= VM_MAX_KERNEL_ADDRESS) {
printf("WARNING: cannot extend kernel pmap beyond %p to %p\n",
(void *)VM_MAX_KERNEL_ADDRESS, (void *)maxkvaddr);
return (kbreak);
}
for (kbreak &= (-1<<PDSHIFT); kbreak < maxkvaddr;
kbreak += (1<<PDSHIFT)) {
if (pseg_get(pm, kbreak))
continue;
pg = 0;
while (pseg_set(pm, kbreak, 0, pg) == 1) {
pg = 0;
pmap_get_page(&pg, "growk", pm);
}
}
return (kbreak);
}
struct pmap *
pmap_create(void)
{
struct pmap *pm;
pm = pool_get(&pmap_pool, PR_WAITOK | PR_ZERO);
mtx_init(&pm->pm_mtx, IPL_VM);
pm->pm_refs = 1;
pmap_get_page(&pm->pm_physaddr, "pmap_create", pm);
pm->pm_segs = (int64_t *)(u_long)pm->pm_physaddr;
ctx_alloc(pm);
return (pm);
}
void
pmap_reference(struct pmap *pm)
{
atomic_inc_int(&pm->pm_refs);
}
void
pmap_destroy(struct pmap *pm)
{
if (atomic_dec_int_nv(&pm->pm_refs) == 0) {
pmap_release(pm);
pool_put(&pmap_pool, pm);
}
}
void
pmap_release(struct pmap *pm)
{
int i, j, k;
paddr_t *pdir, *ptbl, tmp;
#ifdef DIAGNOSTIC
if(pm == pmap_kernel())
panic("pmap_release: releasing pmap_kernel()");
#endif
mtx_enter(&pm->pm_mtx);
for(i=0; i<STSZ; i++) {
paddr_t psegentp = (paddr_t)(u_long)&pm->pm_segs[i];
if((pdir = (paddr_t *)(u_long)ldxa((vaddr_t)psegentp,
ASI_PHYS_CACHED))) {
for (k=0; k<PDSZ; k++) {
paddr_t pdirentp = (paddr_t)(u_long)&pdir[k];
if ((ptbl = (paddr_t *)(u_long)ldxa(
(vaddr_t)pdirentp, ASI_PHYS_CACHED))) {
for (j=0; j<PTSZ; j++) {
int64_t data;
paddr_t pa;
pv_entry_t pv;
data = ldxa((vaddr_t)&ptbl[j],
ASI_PHYS_CACHED);
if (!(data & TLB_V))
continue;
pa = data & TLB_PA_MASK;
pv = pa_to_pvh(pa);
if (pv != NULL) {
printf("pmap_release: pm=%p page %llx still in use\n", pm,
(unsigned long long)(((u_int64_t)i<<STSHIFT)|((u_int64_t)k<<PDSHIFT)|((u_int64_t)j<<PTSHIFT)));
db_enter();
}
}
stxa(pdirentp, ASI_PHYS_CACHED, 0);
pmap_free_page((paddr_t)ptbl, pm);
}
}
stxa(psegentp, ASI_PHYS_CACHED, 0);
pmap_free_page((paddr_t)pdir, pm);
}
}
tmp = (paddr_t)(u_long)pm->pm_segs;
pm->pm_segs = NULL;
pmap_free_page(tmp, pm);
mtx_leave(&pm->pm_mtx);
ctx_free(pm);
}
void
pmap_zero_page(struct vm_page *pg)
{
pmap_zero_phys(VM_PAGE_TO_PHYS(pg));
}
void
pmap_copy_page(struct vm_page *srcpg, struct vm_page *dstpg)
{
paddr_t src = VM_PAGE_TO_PHYS(srcpg);
paddr_t dst = VM_PAGE_TO_PHYS(dstpg);
pmap_copy_phys(src, dst);
}
void
pmap_activate(struct proc *p)
{
struct pmap *pmap = p->p_vmspace->vm_map.pmap;
int s;
s = splvm();
if (p == curproc) {
write_user_windows();
if (pmap->pm_ctx == 0)
ctx_alloc(pmap);
if (CPU_ISSUN4V)
stxa(CTX_SECONDARY, ASI_MMU_CONTEXTID, pmap->pm_ctx);
else
stxa(CTX_SECONDARY, ASI_DMMU, pmap->pm_ctx);
}
splx(s);
}
void
pmap_deactivate(struct proc *p)
{
}
void
pmap_purge(struct proc *p)
{
write_user_windows();
}
void
pmap_kenter_pa(vaddr_t va, paddr_t pa, vm_prot_t prot)
{
struct pmap *pm = pmap_kernel();
pte_t tte;
KDASSERT(va < INTSTACK || va > EINTSTACK);
KDASSERT(va < kdata || va > ekdata);
#ifdef DIAGNOSTIC
if (pa & (PMAP_NVC|PMAP_NC|PMAP_LITTLE))
panic("%s: illegal cache flags 0x%lx", __func__, pa);
#endif
tte.tag = TSB_TAG(0, pm->pm_ctx,va);
if (CPU_ISSUN4V) {
tte.data = SUN4V_TSB_DATA(0, PGSZ_8K, pa, 1 ,
(PROT_WRITE & prot), 1, 0, 1, 0);
if (prot & PROT_WRITE)
tte.data |= SUN4V_TLB_REAL_W|SUN4V_TLB_W;
if (prot & PROT_EXEC)
tte.data |= SUN4V_TLB_EXEC;
tte.data |= SUN4V_TLB_TSB_LOCK;
} else {
tte.data = SUN4U_TSB_DATA(0, PGSZ_8K, pa, 1 ,
(PROT_WRITE & prot), 1, 0, 1, 0);
if (prot & PROT_WRITE)
tte.data |= SUN4U_TLB_REAL_W|SUN4U_TLB_W;
if (prot & PROT_EXEC)
tte.data |= SUN4U_TLB_EXEC;
if (prot == PROT_EXEC)
tte.data |= SUN4U_TLB_EXEC_ONLY;
tte.data |= SUN4U_TLB_TSB_LOCK;
}
KDASSERT((tte.data & TLB_NFO) == 0);
if (pseg_set(pm, va, tte.data, 0) != 0)
panic("%s: no pseg", __func__);
dcache_flush_page(pa);
}
void
pmap_kremove(vaddr_t va, vsize_t size)
{
struct pmap *pm = pmap_kernel();
KDASSERT(va < INTSTACK || va > EINTSTACK);
KDASSERT(va < kdata || va > ekdata);
while (size >= NBPG) {
#ifdef DIAGNOSTIC
if (pm == pmap_kernel() &&
(va >= ktext && va < roundup(ekdata, 4*MEG)))
panic("%s: va=0x%lx in locked TLB", __func__, va);
#endif
if (pseg_get(pm, va)) {
if (pseg_set(pm, va, 0, 0)) {
printf("pmap_kremove: gotten pseg empty!\n");
db_enter();
}
tsb_invalidate(pm->pm_ctx, va);
tlb_flush_pte(va, pm->pm_ctx);
}
va += NBPG;
size -= NBPG;
}
}
int
pmap_enter(struct pmap *pm, vaddr_t va, paddr_t pa, vm_prot_t prot, int flags)
{
pte_t tte;
paddr_t pg;
int aliased = 0;
pv_entry_t pv, npv;
int size = 0;
boolean_t wired = (flags & PMAP_WIRED) != 0;
KDASSERT(pm != pmap_kernel() || va < INTSTACK || va > EINTSTACK);
KDASSERT(pm != pmap_kernel() || va < kdata || va > ekdata);
npv = pool_get(&pv_pool, PR_NOWAIT);
if (npv == NULL && (flags & PMAP_CANFAIL))
return (ENOMEM);
mtx_enter(&pm->pm_mtx);
tte.data = pseg_get(pm, va);
if (tte.data & TLB_V) {
mtx_leave(&pm->pm_mtx);
pmap_remove(pm, va, va + NBPG-1);
mtx_enter(&pm->pm_mtx);
tte.data = pseg_get(pm, va);
}
pv = pa_to_pvh(pa);
if (pv != NULL) {
struct vm_page *pg = PHYS_TO_VM_PAGE(pa);
mtx_enter(&pg->mdpage.pvmtx);
aliased = (pv->pv_va & PV_ALIAS);
#ifdef DIAGNOSTIC
if ((flags & PROT_MASK) & ~prot)
panic("pmap_enter: access_type exceeds prot");
#endif
if (flags & PROT_MASK)
pv->pv_va |= PV_REF;
if (flags & PROT_WRITE)
pv->pv_va |= PV_MOD;
pv->pv_va |= pmap_tte2flags(tte.data);
mtx_leave(&pg->mdpage.pvmtx);
} else {
aliased = 0;
}
if (pa & PMAP_NVC)
aliased = 1;
if (CPU_ISSUN4V) {
tte.data = SUN4V_TSB_DATA(0, size, pa, pm == pmap_kernel(),
(flags & PROT_WRITE), (!(pa & PMAP_NC)),
aliased, 1, (pa & PMAP_LITTLE));
if (prot & PROT_WRITE)
tte.data |= SUN4V_TLB_REAL_W;
if (prot & PROT_EXEC)
tte.data |= SUN4V_TLB_EXEC;
if (wired)
tte.data |= SUN4V_TLB_TSB_LOCK;
} else {
tte.data = SUN4U_TSB_DATA(0, size, pa, pm == pmap_kernel(),
(flags & PROT_WRITE), (!(pa & PMAP_NC)),
aliased, 1, (pa & PMAP_LITTLE));
if (prot & PROT_WRITE)
tte.data |= SUN4U_TLB_REAL_W;
if (prot & PROT_EXEC)
tte.data |= SUN4U_TLB_EXEC;
if (prot == PROT_EXEC)
tte.data |= SUN4U_TLB_EXEC_ONLY;
if (wired)
tte.data |= SUN4U_TLB_TSB_LOCK;
}
KDASSERT((tte.data & TLB_NFO) == 0);
pg = 0;
while (pseg_set(pm, va, tte.data, pg) == 1) {
pg = 0;
if (!pmap_get_page(&pg, NULL, pm)) {
if ((flags & PMAP_CANFAIL) == 0)
panic("pmap_enter: no memory");
mtx_leave(&pm->pm_mtx);
if (npv != NULL)
pool_put(&pv_pool, npv);
return (ENOMEM);
}
}
if (pv != NULL)
npv = pmap_enter_pv(pm, npv, va, pa);
atomic_inc_long(&pm->pm_stats.resident_count);
mtx_leave(&pm->pm_mtx);
if (pm->pm_ctx || pm == pmap_kernel()) {
tsb_invalidate(pm->pm_ctx, va);
tlb_flush_pte(va, pm->pm_ctx);
}
dcache_flush_page(pa);
if (npv != NULL)
pool_put(&pv_pool, npv);
return 0;
}
void
pmap_remove(struct pmap *pm, vaddr_t va, vaddr_t endva)
{
pv_entry_t pv, freepvs = NULL;
int flush = 0;
int64_t data;
vaddr_t flushva = va;
KDASSERT(pm != pmap_kernel() || endva < INTSTACK || va > EINTSTACK);
KDASSERT(pm != pmap_kernel() || endva < kdata || va > ekdata);
mtx_enter(&pm->pm_mtx);
while (va < endva) {
#ifdef DIAGNOSTIC
if (pm == pmap_kernel() && va >= ktext &&
va < roundup(ekdata, 4*MEG))
panic("pmap_remove: va=%08x in locked TLB", (u_int)va);
#endif
if ((data = pseg_get(pm, va)) && (data & TLB_V) != 0) {
paddr_t entry;
flush |= 1;
entry = (data & TLB_PA_MASK);
pv = pa_to_pvh(entry);
if (pv != NULL) {
pv = pmap_remove_pv(pm, va, entry);
if (pv != NULL) {
pv->pv_next = freepvs;
freepvs = pv;
}
}
if (pseg_set(pm, va, 0, 0)) {
printf("pmap_remove: gotten pseg empty!\n");
db_enter();
}
atomic_dec_long(&pm->pm_stats.resident_count);
if (!pm->pm_ctx && pm != pmap_kernel())
continue;
tsb_invalidate(pm->pm_ctx, va);
tlb_flush_pte(va, pm->pm_ctx);
}
va += NBPG;
}
mtx_leave(&pm->pm_mtx);
while ((pv = freepvs) != NULL) {
freepvs = pv->pv_next;
pool_put(&pv_pool, pv);
}
if (flush)
cache_flush_virt(flushva, endva - flushva);
}
void
pmap_protect(struct pmap *pm, vaddr_t sva, vaddr_t eva, vm_prot_t prot)
{
paddr_t pa;
pv_entry_t pv;
int64_t data;
KDASSERT(pm != pmap_kernel() || eva < INTSTACK || sva > EINTSTACK);
KDASSERT(pm != pmap_kernel() || eva < kdata || sva > ekdata);
if ((prot & (PROT_WRITE | PROT_EXEC)) == (PROT_WRITE | PROT_EXEC))
return;
if (prot == PROT_NONE) {
pmap_remove(pm, sva, eva);
return;
}
mtx_enter(&pm->pm_mtx);
sva = sva & ~PGOFSET;
while (sva < eva) {
if (pm == pmap_kernel() && sva >= ktext &&
sva < roundup(ekdata, 4*MEG)) {
prom_printf("pmap_protect: va=%08x in locked TLB\r\n", sva);
OF_enter();
mtx_leave(&pm->pm_mtx);
return;
}
if (((data = pseg_get(pm, sva))&TLB_V) ) {
pa = data & TLB_PA_MASK;
pv = pa_to_pvh(pa);
if (pv != NULL) {
struct vm_page *pg = PHYS_TO_VM_PAGE(pa);
mtx_enter(&pg->mdpage.pvmtx);
pv->pv_va |= pmap_tte2flags(data);
mtx_leave(&pg->mdpage.pvmtx);
}
if (CPU_ISSUN4V) {
if ((prot & PROT_WRITE) == 0)
data &= ~(SUN4V_TLB_W|SUN4V_TLB_REAL_W);
if ((prot & PROT_EXEC) == 0)
data &= ~(SUN4V_TLB_EXEC);
} else {
if ((prot & PROT_WRITE) == 0)
data &= ~(SUN4U_TLB_W|SUN4U_TLB_REAL_W);
if ((prot & PROT_EXEC) == 0)
data &= ~(SUN4U_TLB_EXEC | SUN4U_TLB_EXEC_ONLY);
}
KDASSERT((data & TLB_NFO) == 0);
if (pseg_set(pm, sva, data, 0)) {
printf("pmap_protect: gotten pseg empty!\n");
db_enter();
}
if (!pm->pm_ctx && pm != pmap_kernel())
continue;
tsb_invalidate(pm->pm_ctx, sva);
tlb_flush_pte(sva, pm->pm_ctx);
}
sva += NBPG;
}
mtx_leave(&pm->pm_mtx);
}
boolean_t
pmap_extract(struct pmap *pm, vaddr_t va, paddr_t *pap)
{
paddr_t pa;
if (pm == pmap_kernel()) {
if (va >= kdata && va < roundup(ekdata, 4*MEG)) {
pa = (paddr_t)(kdatap - kdata + va);
} else if (va >= ktext && va < ektext) {
pa = (paddr_t)(ktextp - ktext + va);
} else if (va >= INTSTACK && va < EINTSTACK) {
pa = curcpu()->ci_paddr + va - INTSTACK;
} else {
goto check_pseg;
}
} else {
check_pseg:
mtx_enter(&pm->pm_mtx);
pa = pseg_get(pm, va) & TLB_PA_MASK;
mtx_leave(&pm->pm_mtx);
if (pa == 0)
return FALSE;
pa |= va & PAGE_MASK;
}
if (pap != NULL)
*pap = pa;
return TRUE;
}
int
pmap_dumpsize(void)
{
int sz;
sz = ALIGN(sizeof(kcore_seg_t)) + ALIGN(sizeof(cpu_kcore_hdr_t));
sz += memsize * sizeof(phys_ram_seg_t);
return btodb(sz + DEV_BSIZE - 1);
}
int
pmap_dumpmmu(int (*dump)(dev_t, daddr_t, caddr_t, size_t), daddr_t blkno)
{
kcore_seg_t *kseg;
cpu_kcore_hdr_t *kcpu;
phys_ram_seg_t memseg;
register int error = 0;
register int i, memsegoffset;
int buffer[dbtob(1) / sizeof(int)];
int *bp, *ep;
#define EXPEDITE(p,n) do { \
int *sp = (int *)(p); \
int sz = (n); \
while (sz > 0) { \
*bp++ = *sp++; \
if (bp >= ep) { \
error = (*dump)(dumpdev, blkno, \
(caddr_t)buffer, dbtob(1)); \
if (error != 0) \
return (error); \
++blkno; \
bp = buffer; \
} \
sz -= 4; \
} \
} while (0)
bp = buffer;
ep = &buffer[sizeof(buffer) / sizeof(buffer[0])];
kseg = (kcore_seg_t *)bp;
CORE_SETMAGIC(*kseg, KCORE_MAGIC, MID_MACHINE, CORE_CPU);
kseg->c_size = dbtob(pmap_dumpsize()) - ALIGN(sizeof(kcore_seg_t));
kcpu = (cpu_kcore_hdr_t *)((long)bp + ALIGN(sizeof(kcore_seg_t)));
kcpu->cputype = CPU_SUN4U;
kcpu->kernbase = (u_int64_t)KERNBASE;
kcpu->cpubase = (u_int64_t)CPUINFO_VA;
kcpu->ktextbase = (u_int64_t)ktext;
kcpu->ktextp = (u_int64_t)ktextp;
kcpu->ktextsz = (u_int64_t)(roundup(ektextp, 4*MEG) - ktextp);
kcpu->kdatabase = (u_int64_t)kdata;
kcpu->kdatap = (u_int64_t)kdatap;
kcpu->kdatasz = (u_int64_t)(roundup(ekdatap, 4*MEG) - kdatap);
kcpu->nmemseg = memsize;
kcpu->memsegoffset = memsegoffset = ALIGN(sizeof(cpu_kcore_hdr_t));
kcpu->nsegmap = STSZ;
kcpu->segmapoffset = (u_int64_t)pmap_kernel()->pm_physaddr;
bp = (int *)((long)kcpu + ALIGN(sizeof(cpu_kcore_hdr_t)));
for (i = 0; i < memsize; i++) {
memseg.start = mem[i].start;
memseg.size = mem[i].size;
EXPEDITE(&memseg, sizeof(phys_ram_seg_t));
}
if (bp != buffer)
error = (*dump)(dumpdev, blkno++, (caddr_t)buffer, dbtob(1));
return (error);
}
int
pmap_pa_exists(paddr_t pa)
{
struct mem_region *mp;
for (mp = mem; mp->size && mp->start <= pa; mp++)
if (mp->start <= pa && mp->start + mp->size >= pa)
return 1;
return 0;
}
#ifdef NOTYET
int64 GenerateTSBPointer(
int64 va,
PointerType type,
int64 TSBBase,
Boolean split,
int TSBSize)
{
int64 vaPortion;
int64 TSBBaseMask;
int64 splitMask;
TSBBaseMask = 0xffffffffffffe000 <<
(split? (TSBsize + 1) : TSBsize);
vaPortion = (va >> ((type == 8K_POINTER)? 9: 12)) &
0xfffffffffffffff0;
if (split) {
splitMask = 1 << (13 + TSBsize);
if (type == 8K_POINTER)
vaPortion &= ~splitMask;
else
vaPortion |= splitMask;
}
return (TSBBase & TSBBaseMask) | (vaPortion & ~TSBBaseMask);
}
#endif
int
ptelookup_va(vaddr_t va)
{
long tsbptr;
#define TSBBASEMASK (0xffffffffffffe000LL<<tsbsize)
tsbptr = (((va >> 9) & 0xfffffffffffffff0LL) & ~TSBBASEMASK );
return (tsbptr/sizeof(pte_t));
}
boolean_t
pmap_clear_modify(struct vm_page *pg)
{
paddr_t pa = VM_PAGE_TO_PHYS(pg);
int changed = 0;
pv_entry_t pv;
mtx_enter(&pg->mdpage.pvmtx);
pv = pa_to_pvh(pa);
if (pv->pv_va & PV_MOD) {
changed |= 1;
pv->pv_va &= ~PV_MOD;
}
if (pv->pv_pmap != NULL) {
for (; pv; pv = pv->pv_next) {
int64_t data;
data = pseg_get(pv->pv_pmap, pv->pv_va & PV_VAMASK);
if (CPU_ISSUN4V) {
if (data & (SUN4V_TLB_MODIFY))
changed |= 1;
data &= ~(SUN4V_TLB_MODIFY|SUN4V_TLB_W);
} else {
if (data & (SUN4U_TLB_MODIFY))
changed |= 1;
data &= ~(SUN4U_TLB_MODIFY|SUN4U_TLB_W);
}
KDASSERT((data & TLB_NFO) == 0);
if (pseg_set(pv->pv_pmap, pv->pv_va & PV_VAMASK, data, 0)) {
printf("pmap_clear_modify: gotten pseg empty!\n");
db_enter();
}
if (pv->pv_pmap->pm_ctx || pv->pv_pmap == pmap_kernel()) {
tsb_invalidate(pv->pv_pmap->pm_ctx,
(pv->pv_va & PV_VAMASK));
tlb_flush_pte((pv->pv_va & PV_VAMASK),
pv->pv_pmap->pm_ctx);
}
if (pv->pv_va & PV_MOD) {
changed |= 1;
pv->pv_va &= ~PV_MOD;
}
dcache_flush_page(pa);
}
}
mtx_leave(&pg->mdpage.pvmtx);
return (changed);
}
boolean_t
pmap_clear_reference(struct vm_page *pg)
{
paddr_t pa = VM_PAGE_TO_PHYS(pg);
int changed = 0;
pv_entry_t pv;
mtx_enter(&pg->mdpage.pvmtx);
pv = pa_to_pvh(pa);
if (pv->pv_va & PV_REF) {
changed = 1;
pv->pv_va &= ~PV_REF;
}
if (pv->pv_pmap != NULL) {
for (; pv; pv = pv->pv_next) {
int64_t data;
data = pseg_get(pv->pv_pmap, pv->pv_va & PV_VAMASK);
if (CPU_ISSUN4V) {
if (data & SUN4V_TLB_ACCESS)
changed = 1;
data &= ~SUN4V_TLB_ACCESS;
} else {
if (data & SUN4U_TLB_ACCESS)
changed = 1;
data &= ~SUN4U_TLB_ACCESS;
}
KDASSERT((data & TLB_NFO) == 0);
if (pseg_set(pv->pv_pmap, pv->pv_va & PV_VAMASK, data, 0)) {
printf("pmap_clear_reference: gotten pseg empty!\n");
db_enter();
}
if (pv->pv_pmap->pm_ctx || pv->pv_pmap == pmap_kernel()) {
tsb_invalidate(pv->pv_pmap->pm_ctx,
(pv->pv_va & PV_VAMASK));
}
if (pv->pv_va & PV_REF) {
changed = 1;
pv->pv_va &= ~PV_REF;
}
}
}
dcache_flush_page(VM_PAGE_TO_PHYS(pg));
mtx_leave(&pg->mdpage.pvmtx);
return (changed);
}
boolean_t
pmap_is_modified(struct vm_page *pg)
{
pv_entry_t pv, npv;
int mod = 0;
mtx_enter(&pg->mdpage.pvmtx);
pv = &pg->mdpage.pvent;
if (pv->pv_va & PV_MOD)
mod = 1;
if (!mod && (pv->pv_pmap != NULL)) {
for (npv = pv; mod == 0 && npv && npv->pv_pmap; npv = npv->pv_next) {
int64_t data;
data = pseg_get(npv->pv_pmap, npv->pv_va & PV_VAMASK);
if (pmap_tte2flags(data) & PV_MOD)
mod = 1;
if (npv->pv_va & PV_MOD) {
mod = 1;
npv->pv_va &= ~PV_MOD;
}
}
}
if (mod)
pv->pv_va |= PV_MOD;
mtx_leave(&pg->mdpage.pvmtx);
return (mod);
}
boolean_t
pmap_is_referenced(struct vm_page *pg)
{
pv_entry_t pv, npv;
int ref = 0;
mtx_enter(&pg->mdpage.pvmtx);
pv = &pg->mdpage.pvent;
if (pv->pv_va & PV_REF)
ref = 1;
if (!ref && (pv->pv_pmap != NULL)) {
for (npv = pv; npv; npv = npv->pv_next) {
int64_t data;
data = pseg_get(npv->pv_pmap, npv->pv_va & PV_VAMASK);
if (pmap_tte2flags(data) & PV_REF)
ref = 1;
if (npv->pv_va & PV_REF) {
ref = 1;
npv->pv_va &= ~PV_REF;
}
}
}
if (ref)
pv->pv_va |= PV_REF;
mtx_leave(&pg->mdpage.pvmtx);
return (ref);
}
void
pmap_unwire(struct pmap *pmap, vaddr_t va)
{
int64_t data;
if (pmap == NULL)
return;
if (pmap == pmap_kernel() && va >= ktext &&
va < roundup(ekdata, 4*MEG)) {
prom_printf("pmap_unwire: va=%08x in locked TLB\r\n", va);
OF_enter();
return;
}
mtx_enter(&pmap->pm_mtx);
data = pseg_get(pmap, va & PV_VAMASK);
if (CPU_ISSUN4V)
data &= ~SUN4V_TLB_TSB_LOCK;
else
data &= ~SUN4U_TLB_TSB_LOCK;
if (pseg_set(pmap, va & PV_VAMASK, data, 0)) {
printf("pmap_unwire: gotten pseg empty!\n");
db_enter();
}
mtx_leave(&pmap->pm_mtx);
}
void
pmap_page_protect(struct vm_page *pg, vm_prot_t prot)
{
paddr_t pa = VM_PAGE_TO_PHYS(pg);
pv_entry_t pv, freepvs = NULL;
int64_t data, clear, set;
if (prot & PROT_WRITE)
return;
if (prot & (PROT_READ | PROT_EXEC)) {
set = TLB_V;
if (CPU_ISSUN4V) {
clear = SUN4V_TLB_REAL_W|SUN4V_TLB_W;
if (PROT_EXEC & prot)
set |= SUN4V_TLB_EXEC;
else
clear |= SUN4V_TLB_EXEC;
} else {
clear = SUN4U_TLB_REAL_W|SUN4U_TLB_W;
if (PROT_EXEC & prot)
set |= SUN4U_TLB_EXEC;
else
clear |= SUN4U_TLB_EXEC;
if (PROT_EXEC == prot)
set |= SUN4U_TLB_EXEC_ONLY;
else
clear |= SUN4U_TLB_EXEC_ONLY;
}
pv = pa_to_pvh(pa);
mtx_enter(&pg->mdpage.pvmtx);
if (pv->pv_pmap != NULL) {
for (; pv; pv = pv->pv_next) {
data = pseg_get(pv->pv_pmap, pv->pv_va & PV_VAMASK);
pv->pv_va |= pmap_tte2flags(data);
data &= ~(clear);
data |= (set);
KDASSERT((data & TLB_NFO) == 0);
if (pseg_set(pv->pv_pmap, pv->pv_va & PV_VAMASK, data, 0)) {
printf("pmap_page_protect: gotten pseg empty!\n");
db_enter();
}
if (pv->pv_pmap->pm_ctx || pv->pv_pmap == pmap_kernel()) {
tsb_invalidate(pv->pv_pmap->pm_ctx,
(pv->pv_va & PV_VAMASK));
tlb_flush_pte(pv->pv_va & PV_VAMASK, pv->pv_pmap->pm_ctx);
}
}
}
mtx_leave(&pg->mdpage.pvmtx);
} else {
pv_entry_t firstpv;
firstpv = pa_to_pvh(pa);
mtx_enter(&pg->mdpage.pvmtx);
while ((pv = firstpv->pv_next) != NULL) {
data = pseg_get(pv->pv_pmap, pv->pv_va & PV_VAMASK);
firstpv->pv_va |= pmap_tte2flags(data);
if (pseg_set(pv->pv_pmap, pv->pv_va & PV_VAMASK, 0, 0)) {
printf("pmap_page_protect: gotten pseg empty!\n");
db_enter();
}
if (pv->pv_pmap->pm_ctx || pv->pv_pmap == pmap_kernel()) {
tsb_invalidate(pv->pv_pmap->pm_ctx,
(pv->pv_va & PV_VAMASK));
tlb_flush_pte(pv->pv_va & PV_VAMASK, pv->pv_pmap->pm_ctx);
}
atomic_dec_long(&pv->pv_pmap->pm_stats.resident_count);
firstpv->pv_next = pv->pv_next;
pv->pv_next = freepvs;
freepvs = pv;
}
pv = firstpv;
if (pv->pv_pmap != NULL) {
data = pseg_get(pv->pv_pmap, pv->pv_va & PV_VAMASK);
pv->pv_va |= pmap_tte2flags(data);
if (pseg_set(pv->pv_pmap, pv->pv_va & PV_VAMASK, 0, 0)) {
printf("pmap_page_protect: gotten pseg empty!\n");
db_enter();
}
if (pv->pv_pmap->pm_ctx || pv->pv_pmap == pmap_kernel()) {
tsb_invalidate(pv->pv_pmap->pm_ctx,
(pv->pv_va & PV_VAMASK));
tlb_flush_pte(pv->pv_va & PV_VAMASK,
pv->pv_pmap->pm_ctx);
}
atomic_dec_long(&pv->pv_pmap->pm_stats.resident_count);
KASSERT(pv->pv_next == NULL);
pv->pv_pmap = NULL;
}
dcache_flush_page(pa);
mtx_leave(&pg->mdpage.pvmtx);
while ((pv = freepvs) != NULL) {
freepvs = pv->pv_next;
pool_put(&pv_pool, pv);
}
}
}
int
ctx_alloc(struct pmap *pm)
{
int cnum;
static int next = 0;
if (pm == pmap_kernel()) {
#ifdef DIAGNOSTIC
printf("ctx_alloc: kernel pmap!\n");
#endif
return (0);
}
mtx_enter(&ctxmtx);
cnum = next;
do {
if (cnum >= numctx - 2)
cnum = 0;
} while (ctxbusy[++cnum] != 0 && cnum != next);
if (cnum==0) cnum++;
if (ctxbusy[cnum]) {
#ifdef notyet
int i;
for (i = 0; i < TSBENTS; i++) {
if (TSB_TAG_CTX(tsb_dmmu[i].tag) == cnum)
tsb_dmmu[i].tag = TSB_TAG_INVALID;
if (TSB_TAG_CTX(tsb_immu[i].tag) == cnum)
tsb_immu[i].tag = TSB_TAG_INVALID;
}
tlb_flush_ctx(cnum);
#else
panic("%s: stealing context", __func__);
#endif
}
ctxbusy[cnum] = pm->pm_physaddr;
next = cnum;
mtx_leave(&ctxmtx);
pm->pm_ctx = cnum;
return cnum;
}
void
ctx_free(struct pmap *pm)
{
int oldctx;
#ifdef DIAGNOSTIC
u_int64_t tag, data;
int i;
#endif
oldctx = pm->pm_ctx;
if (oldctx == 0)
panic("ctx_free: freeing kernel context");
#ifdef DIAGNOSTIC
if (ctxbusy[oldctx] == 0)
printf("ctx_free: freeing free context %d\n", oldctx);
if (ctxbusy[oldctx] != pm->pm_physaddr) {
printf("ctx_free: freeing someone else's context\n "
"ctxbusy[%d] = %p, pm(%p)->pm_ctx = %p\n",
oldctx, (void *)(u_long)ctxbusy[oldctx], pm,
(void *)(u_long)pm->pm_physaddr);
db_enter();
}
for (i = 0; i < TSBENTS; i++) {
tag = READ_ONCE(tsb_dmmu[i].tag);
if (TSB_TAG_CTX(tag) == oldctx) {
data = READ_ONCE(tsb_dmmu[i].data);
atomic_cas_ulong((unsigned long *)&tsb_dmmu[i].tag,
tag, TSB_TAG_INVALID);
printf("%s: context %d still active in DMMU TSB\n",
__func__, oldctx);
printf("%s: tag 0x%016llx data 0x%016llx\n",
__func__, tag, data);
}
tag = READ_ONCE(tsb_immu[i].tag);
if (TSB_TAG_CTX(tag) == oldctx) {
data = READ_ONCE(tsb_immu[i].data);
atomic_cas_ulong((unsigned long *)&tsb_immu[i].tag,
tag, TSB_TAG_INVALID);
printf("%s: context %d still active in IMMU TSB\n",
__func__, oldctx);
printf("%s: tag 0x%016llx data 0x%016llx\n",
__func__, tag, data);
}
}
#endif
mtx_enter(&ctxmtx);
ctxbusy[oldctx] = 0;
mtx_leave(&ctxmtx);
}
pv_entry_t
pmap_enter_pv(struct pmap *pmap, pv_entry_t npv, vaddr_t va, paddr_t pa)
{
struct vm_page *pg = PHYS_TO_VM_PAGE(pa);
pv_entry_t pv = &pg->mdpage.pvent;
mtx_enter(&pg->mdpage.pvmtx);
if (pv->pv_pmap == NULL) {
PV_SETVA(pv, va);
pv->pv_pmap = pmap;
pv->pv_next = NULL;
mtx_leave(&pg->mdpage.pvmtx);
return (npv);
}
if (npv == NULL)
panic("%s: no pv entries available", __func__);
if (!(pv->pv_va & PV_ALIAS)) {
if ((pv->pv_va ^ va) & VA_ALIAS_MASK) {
pv->pv_va |= PV_ALIAS;
pmap_page_cache(pmap, pa, 0);
}
}
npv->pv_va = va & PV_VAMASK;
npv->pv_pmap = pmap;
npv->pv_next = pv->pv_next;
pv->pv_next = npv;
mtx_leave(&pg->mdpage.pvmtx);
return (NULL);
}
pv_entry_t
pmap_remove_pv(struct pmap *pmap, vaddr_t va, paddr_t pa)
{
pv_entry_t pv, opv, npv = NULL;
struct vm_page *pg = PHYS_TO_VM_PAGE(pa);
int64_t data = 0LL;
int alias;
opv = pv = &pg->mdpage.pvent;
mtx_enter(&pg->mdpage.pvmtx);
if (pmap == pv->pv_pmap && PV_MATCH(pv, va)) {
data = pseg_get(pv->pv_pmap, pv->pv_va & PV_VAMASK);
npv = pv->pv_next;
if (npv) {
pv->pv_va = (pv->pv_va & PV_MASK) | npv->pv_va;
pv->pv_next = npv->pv_next;
pv->pv_pmap = npv->pv_pmap;
} else {
pv->pv_pmap = NULL;
pv->pv_next = NULL;
pv->pv_va &= (PV_REF|PV_MOD);
}
} else {
for (npv = pv->pv_next; npv; pv = npv, npv = npv->pv_next) {
if (pmap == npv->pv_pmap && PV_MATCH(npv, va))
goto found;
}
mtx_leave(&pg->mdpage.pvmtx);
return (NULL);
found:
pv->pv_next = npv->pv_next;
data = pseg_get(npv->pv_pmap, npv->pv_va & PV_VAMASK);
}
opv->pv_va |= pmap_tte2flags(data);
if (opv->pv_va & PV_ALIAS) {
alias = 0;
for (pv = opv; pv; pv = pv->pv_next) {
if ((pv->pv_va ^ opv->pv_va) & VA_ALIAS_MASK) {
alias = 1;
break;
}
}
if (alias == 0) {
opv->pv_va &= ~PV_ALIAS;
pmap_page_cache(pmap, pa, 1);
}
}
mtx_leave(&pg->mdpage.pvmtx);
return (npv);
}
void
pmap_page_cache(struct pmap *pm, paddr_t pa, int mode)
{
pv_entry_t pv;
struct vm_page *pg = PHYS_TO_VM_PAGE(pa);
if (CPU_ISSUN4US || CPU_ISSUN4V)
return;
pv = &pg->mdpage.pvent;
if (pv == NULL)
return;
MUTEX_ASSERT_LOCKED(&pg->mdpage.pvmtx);
while (pv) {
vaddr_t va;
va = (pv->pv_va & PV_VAMASK);
if (mode) {
if (pseg_set(pv->pv_pmap, va,
pseg_get(pv->pv_pmap, va) | SUN4U_TLB_CV, 0)) {
printf("pmap_page_cache: aliased pseg empty!\n");
db_enter();
}
} else {
if (pseg_set(pv->pv_pmap, va,
pseg_get(pv->pv_pmap, va) & ~SUN4U_TLB_CV, 0)) {
printf("pmap_page_cache: aliased pseg empty!\n");
db_enter();
}
}
if (pv->pv_pmap->pm_ctx || pv->pv_pmap == pmap_kernel()) {
tsb_invalidate(pv->pv_pmap->pm_ctx, va);
tlb_flush_pte(va, pv->pv_pmap->pm_ctx);
}
pv = pv->pv_next;
}
}
int
pmap_get_page(paddr_t *pa, const char *wait, struct pmap *pm)
{
int reserve = pm == pmap_kernel() ? UVM_PGA_USERESERVE : 0;
if (uvm.page_init_done) {
struct vm_page *pg;
while ((pg = uvm_pagealloc(NULL, 0, NULL,
UVM_PGA_ZERO|reserve)) == NULL) {
if (wait == NULL)
return 0;
uvm_wait(wait);
}
pg->wire_count++;
atomic_clearbits_int(&pg->pg_flags, PG_BUSY);
*pa = VM_PAGE_TO_PHYS(pg);
} else {
uvm_page_physget(pa);
prom_claim_phys(*pa, PAGE_SIZE);
pmap_zero_phys(*pa);
}
return (1);
}
void
pmap_free_page(paddr_t pa, struct pmap *pm)
{
struct vm_page *pg = PHYS_TO_VM_PAGE(pa);
pg->wire_count = 0;
uvm_pagefree(pg);
}
void
pmap_remove_holes(struct vmspace *vm)
{
vaddr_t shole, ehole;
struct vm_map *map = &vm->vm_map;
shole = 1L << (HOLESHIFT - 1);
ehole = -1L << (HOLESHIFT - 1);
shole = ulmax(vm_map_min(map), shole);
ehole = ulmin(vm_map_max(map), ehole);
if (ehole <= shole)
return;
(void)uvm_map(map, &shole, ehole - shole, NULL, UVM_UNKNOWN_OFFSET, 0,
UVM_MAPFLAG(PROT_NONE, PROT_NONE, MAP_INHERIT_SHARE, MADV_RANDOM,
UVM_FLAG_NOMERGE | UVM_FLAG_HOLE | UVM_FLAG_FIXED));
}
#ifdef DDB
void
db_dump_pv(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
{
struct pv_entry *pv;
if (!have_addr) {
db_printf("Need addr for pv\n");
return;
}
for (pv = pa_to_pvh(addr); pv; pv = pv->pv_next)
db_printf("pv@%p: next=%p pmap=%p va=0x%llx\n",
pv, pv->pv_next, pv->pv_pmap,
(unsigned long long)pv->pv_va);
}
#endif
int
pmap_copyinsn(pmap_t pmap, vaddr_t va, uint32_t *insn)
{
paddr_t pa;
if (pmap == pmap_kernel())
return EINVAL;
mtx_enter(&pmap->pm_mtx);
pa = pseg_get(pmap, va) & TLB_PA_MASK;
if (pa != 0)
*insn = lduwa(pa | (va & PAGE_MASK), ASI_PHYS_CACHED);
mtx_leave(&pmap->pm_mtx);
return pa == 0 ? EFAULT : 0;
}