plan 9 kernel history: overview | file list | diff list

1999/0512/alphapc/mmu.c (diff list | history)

alphapc/mmu.c on 1999/0415
1999/0415    
#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 
1999/0511    
upamalloc(ulong pa, int size, int align) 
1999/0415    
{ 
1999/0511    
	void *va; 
 
	/* 
1999/0512    
	 * Viability hack. Only for PCI frambuffers. 
1999/0511    
	 */ 
1999/0512    
	if(pa == 0) 
		return 0; 
1999/0511    
	USED(align); 
	va = kmapv(((uvlong)0x88<<32LL)|pa, size); 
	if(va == nil) 
		return 0; 
	return PADDR(va); 
1999/0415    
} 
 
void 
upafree(...) 
{ 
	panic("upafree"); 
} 


source code copyright © 1990-2005 Lucent Technologies; see license
Plan 9 distribution
comments to russ cox (rsc@swtch.com)