#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "/sys/src/boot/alphapc/conf.h"
static uvlong origlvl1; /* physical address */
static uvlong klvl2; /* physical, as created by boot loader */
static uchar *nextio; /* next virtual address to be allocated by kmapv */
extern Bootconf *bootconf;
#define LVL2OFF(v) ((((long)(v))>>(2*PGSHIFT-3))&(PTE2PG-1))
#define LVL3OFF(v) ((((long)(v))>>(PGSHIFT))&(PTE2PG-1))
static void
setptb(ulong pa)
{
m->ptbr = (uvlong)pa>>PGSHIFT;
swpctx(m);
}
void
mmuinit(void)
{
uvlong *plvl2;
/* set PCB to new one in mach structure before stomping on old one */
m->usp = 0;
m->fen = 1;
m->ptbr = bootconf->pcb->ptbr;
origlvl1 = (m->ptbr << PGSHIFT);
setpcb(m);
plvl2 = (uvlong*) (KZERO|origlvl1|(BY2PG-8));
klvl2 = (*plvl2 >> 32)<<PGSHIFT;
nextio = (uchar*) (KZERO|bootconf->maxphys);
}
/*
* Called splhi, not in Running state
*/
void
mmuswitch(Proc *p)
{
Page *pg;
uvlong *lvl2;
if(p->newtlb){
/*
* newtlb set means that they are inconsistent
* with the segment.c data structures.
*
* bin the current 3rd level page tables and
* the pointers to them in the 2nd level page.
* pg->daddr is used by putmmu to save the offset into
* the 2nd level page.
*/
if(p->mmutop && p->mmuused){
lvl2 = (uvlong*)p->mmulvl2->va;
for(pg = p->mmuused; pg->next; pg = pg->next)
lvl2[pg->daddr] = 0;
lvl2[pg->daddr] = 0;
pg->next = p->mmufree;
p->mmufree = p->mmuused;
p->mmuused = 0;
}
p->newtlb = 0;
}
/* tell processor about new page table and flush cached entries */
if(p->mmutop == 0)
setptb(origlvl1);
else
setptb(p->mmutop->pa);
tlbflush(-1, 0);
icflush();
}
/*
* give all page table pages back to the free pool. This is called in sched()
* with palloc locked.
*/
void
mmurelease(Proc *p)
{
Page *pg;
Page *next;
if(canlock(&palloc))
panic("mmurelease");
/* point to protoype page map */
setptb(origlvl1);
icflush();
/* give away page table pages */
for(pg = p->mmuused; pg; pg = next){
next = pg->next;
pg->next = p->mmufree;
p->mmufree = pg;
}
p->mmuused = 0;
if(p->mmutop) {
p->mmutop->next = p->mmufree;
p->mmufree = p->mmutop;
p->mmutop = 0;
}
if(p->mmulvl2) {
p->mmulvl2->next = p->mmufree;
p->mmufree = p->mmulvl2;
p->mmulvl2 = 0;
}
for(pg = p->mmufree; pg; pg = next){
next = pg->next;
if(--pg->ref)
panic("mmurelease: pg->ref %d\n", pg->ref);
pagechainhead(pg);
}
if(p->mmufree && palloc.r.p)
wakeup(&palloc.r);
p->mmufree = 0;
}
void
mmunewtop(void)
{
Page *top, *lvl2;
uvlong *ppte;
top = newpage(1, 0, 0);
top->va = VA(kmap(top));
lvl2 = newpage(1, 0, 0);
lvl2->va = VA(kmap(lvl2));
ppte = (uvlong *)top->va;
ppte[0] = PTEPFN(lvl2->pa) | PTEKVALID;
ppte[PTE2PG-2] = PTEPFN(top->pa) | PTEKVALID;
ppte[PTE2PG-1] = PTEPFN(klvl2) | PTEKVALID;
up->mmutop = top;
up->mmulvl2 = lvl2;
setptb(top->pa);
tlbflush(-1, 0);
icflush();
}
void
putmmu(ulong va, ulong pa, Page *pg)
{
int lvl2off;
uvlong *lvl2, *pt;
int s;
if(up->mmutop == 0)
mmunewtop();
lvl2 = (uvlong*)up->mmulvl2->va;
lvl2off = LVL2OFF(va);
/*
* if bottom level page table missing, allocate one
* and point the top level page at it.
*/
s = splhi();
if(lvl2[lvl2off] == 0){
if(up->mmufree == 0){
spllo();
pg = newpage(1, 0, 0);
pg->va = VA(kmap(pg));
splhi();
} else {
pg = up->mmufree;
up->mmufree = pg->next;
memset((void*)pg->va, 0, BY2PG);
}
lvl2[lvl2off] = PTEPFN(pg->pa) | PTEVALID;
pg->daddr = lvl2off;
pg->next = up->mmuused;
up->mmuused = pg;
}
/*
* put in new mmu entry
*/
pt = (uvlong*)(((lvl2[lvl2off] >> 32)<<PGSHIFT)|KZERO);
pt[LVL3OFF(va)] = FIXPTE(pa);
/* flush cached mmu entries */
tlbflush(3, va);
icflush();
splx(s);
}
void *
kmapv(uvlong pa, int size)
{
void *va, *new;
int lvl2off, i, npage, offset;
uvlong *lvl2, *pt;
offset = pa&(BY2PG-1);
npage = ((size+offset+BY2PG-1)>>PGSHIFT);
va = nextio+offset;
lvl2 = (uvlong*)(KZERO|klvl2);
for (i = 0; i < npage; i++) {
lvl2off = LVL2OFF(nextio);
if (lvl2[lvl2off] == 0) {
new = xspanalloc(BY2PG, BY2PG, 0);
memset(new, 0, BY2PG);
lvl2[lvl2off] = PTEPFN(PADDR(new)) | PTEKVALID | PTEASM;
}
pt = (uvlong*)(((lvl2[lvl2off] >> 32)<<PGSHIFT)|KZERO);
pt[LVL3OFF(nextio)] = PTEPFN(pa) | PTEKVALID | PTEASM;
nextio += BY2PG;
pa += BY2PG;
}
return va;
}
void
flushmmu(void)
{
int s;
s = splhi();
up->newtlb = 1;
mmuswitch(up);
splx(s);
}
ulong
upamalloc(ulong pa, int size, int align)
{
void *va;
/*
* Viability hack. Only for PCI frambuffers.
*/
if(pa == 0)
return 0;
USED(align);
va = kmapv(((uvlong)0x88<<32LL)|pa, size);
if(va == nil)
return 0;
return PADDR(va);
}
void
upafree(ulong, int)
{
print("upafree: virtual mapping not freed\n");
}