rminnichmerge57758a716 years agoHistory
346 lines · 6.0 KB · C
RawDownload
#include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h" #include "ureg.h" enum {	NKMap		= 64,			/* Overestimate? */	NKMtlb		= 3, 	Minutlb		= NTLB/2,		/* >= this for user processes */}; static Lock kmlock;static KMap kmpte[NKMap];static KMap* kmfree;static uintptr kmdma = KSEG1 + (NKMap<<PGSHIFT); static voidkmapdump(void){	int i;	KMap *k; 	DBG("kmfree %#p (%ld)\n", kmfree, kmfree == nil? -1: kmfree-kmpte);	DBG("kmtlblo %d %d kmactive %ld\n",		m->kmtlblo, m->kmtlbhi,		m->kmactive == nil? -1: m->kmactive-kmpte);	for(i = 0; i < NKMap; i++){		k = &kmpte[i];		//if(k->pa == 0)		//	continue;		DBG("kmpte%d: %#p %#p next %ld kmonmach %ld\n",			i, k->va, k->va, k->next == nil? -1: k->next-kmpte,			k->kmonmach[0]-kmpte);		USED(k);	}} voidkmapinit(void){	KMap *k; 	/*	 * Note: .(km|u)tlbhi are inclusive.	 */	m->kmtlbhi = m->utlbhi;	m->utlbhi -= NKMtlb;	m->kmtlblo = m->utlbhi+1;	m->kmtlbnext = m->kmtlblo; 	if(m->machno != 0)		return; 	lock(&kmlock);	kmfree = kmpte;	for(k = kmpte; k < &kmpte[NKMap-1]; k++)		k->next = k+1;	k->next = nil; 	unlock(&kmlock);} voidkunmap(KMap* k){	Mreg s; DBG("kunmap@%#p: index %ld km %#p %#p ref %d\n",    getcallerpc(&k), k-kmpte, k->va, k->pa, k->ref); 	s = splhi();	if(decref(k) == 0){		k->va = 0;		k->pa = 0;		k->sz = 0;		k->pg = nil; 		lock(&kmlock);		k->next = kmfree;		kmfree = k;		unlock(&kmlock);	}	splx(s);} static voidkmapinvalx(int clock){	int machno, i;	KMap *k, *next;	uchar *kmtlbx; 	if(m->kmactive == nil)		return; if(!clock) kmapdump();	stidput(0);	kmtlbx = m->kmtlbx;DBG("kmapinval: curpid %d *kmtlbx %d\n", pidget(), *kmtlbx);//this loop is from kmtlblo to kmtlbhi, surely?	for(i = 0; i < NTLB; i++, kmtlbx++){		if(*kmtlbx == 0)			continue;DBG("kmapinval: zap %d\n", i);		tlbwrx(i, 0, 0, 0);		*kmtlbx = 0;	}	stidput(pidget()); 	machno = m->machno;	for(k = m->kmactive; k != nil; k = next){DBG("kmapinval: kunmap km %#p %#p ref %d\n", k->va, k->pa, k->ref);		next = k->kmonmach[machno];		kunmap(k);	} 	m->kmactive = nil;	m->kmtlbnext = m->kmtlblo;} voidkmapinval(void){	/*	 * Called from hzclock().	 * Fix to have an 'archhzclock() routine.	 */	kmapinvalx(1);} KMap*kmap(Page* pg){	Mreg s;	int i, x;	KMap *k;	u32int hi, md; 	s = splhi();	lock(&kmlock); DBG("kmap: kmfree %#p\n", kmfree);	for(i = 0; kmfree == nil; i++){		unlock(&kmlock);		kmapinvalx(0);		lock(&kmlock);		if(kmfree == nil){			unlock(&kmlock);			splx(s);			delay(100);			splhi();			lock(&kmlock);			DBG("out of kmap %d\n", i);		}	} 	k = kmfree;	kmfree = k->next;DBG("kmap: use %ld, head now %ld\n", k-kmpte, kmfree == nil? -1: kmfree-kmpte); 	k->ref = 2;	k->kmonmach[m->machno] = m->kmactive;	m->kmactive = k;	k->pg = pg;	k->pc = getcallerpc(&pg); 	k->va = KSEG1 + ((k - kmpte)<<PGSHIFT);	/* munge k->sz eventually */	hi = TLBEPN(k->va)|TLB4K|TLBVALID;	/* munge k->sz eventually */	md = TLBRPN(pg->pa)/*|TLBERPN(pg->pa)*/;/* botch: pg->pa only 32b */	k->pa = md; 	if(stidget() != pidget()){		iprint("kmap mismatch: stid %d pid %d\n", stidget(), pidget());		stidput(pidget());	}	x = m->kmtlbnext;	tlbwrx(x, hi, md, TLBWL1|TLBU2|TLBM|TLBWR); 	m->kmtlbx[x] = 1;	if(x++ >= m->kmtlbhi)		x = m->kmtlblo;	m->kmtlbnext = x; 	unlock(&kmlock);	splx(s);DBG("kmap@%#p: pg %#p %#p km %#p %#p x %d\n",    k->pc, pg->va, pg->pa, k->va, k->pa, x); 	return k;} voidkmapfault(Ureg*, uintptr addr){	int x;	KMap *k, *f;  	x = (addr & ~KSEGM)/BY2PG;	if(x >= NKMap)		panic("kmapfault: %#p", addr); 	k = &kmpte[x];DBG("kmapfault@%#p: km %#p %#p x %d\n", addr, k->va, k->pa, m->kmtlbnext);	if(k->va == 0)		panic("kmapfault: unmapped %#p", addr); 	for(f = m->kmactive; f != nil; f = f->kmonmach[m->machno])		if(f == k)			break;	if(f == nil){		incref(k);		k->kmonmach[m->machno] = m->kmactive;		m->kmactive = k;	} 	x = m->kmtlbnext;	tlbwrx(x, TLBEPN(k->va)|TLB4K|TLBVALID, k->pa, TLBWL1|TLBU2|TLBM|TLBWR); 	m->kmtlbx[x] = 1;	if(x++ >= m->kmtlbhi)		x = m->kmtlblo;	m->kmtlbnext = x;} /* * return required size and alignment to map n bytes in a tlb entry */usizekmapsize(usize n){	int i;	usize size; 	size = 1*KiB;	for(i = 0; i < 10 && size < n; i++)		size <<= 2;	if(i >= 10)		return 0; 	/*	 * skip sizes not implemented by hardware	 */	if(i == 6 || i == 8)		size <<= 2; 	return size;} /* * map a physical addresses at pa to va, with the given attributes. * the virtual address must not be mapped already (but no check is made...). * if va is nil, map it at pa in virtual space. */void*kmapphys(uintptr va, u64int pa, unsigned nb, unsigned attr){	Mreg s;	int i;	Tlb *entry;	unsigned size;	u32int hi, md; 	if(va == 0)		va = pa;	/* simplest is to use a 1-1 map */	size = 1*KiB;	for(i = 0; i < 10 && size < nb; i++)		size <<= 2;	if(i >= 10)		return nil;	if(i == 6 || i == 8)		i++;	/* skip sizes not implemented by hardware */ 	hi = TLBEPN(va)|(i<<4)|TLBVALID;	md = TLBRPN(pa)|TLBERPN(pa); 	if(m->utlbhi <= Minutlb)		panic("kmapphys m->utlbhi %d", m->utlbhi); 	lock(&phystlblock);	if(phystlbhi >= nelem(phystlb)-1){		unlock(&phystlblock); 		return nil;	}	entry = &phystlb[phystlbhi++];	entry->hi = hi;	entry->md = md;	entry->lo = attr;	unlock(&phystlblock); 	s = splhi();	stidput(0);	tlbwrx(m->utlbhi, hi, md, attr);	m->utlbhi--;	stidput(pidget());	splx(s);//print("cpu%d: phystlhi %d utlbhi %d %#p %#llux %#ux %#ux %#ux\n",//    m->machno, phystlbhi-1, m->utlbhi+1, va, pa, hi, md, attr);	if(DBGFLG)		tlbdump("kmapphys"); 	return UINT2PTR(va);} voidkunmapphys(void* va){	/*	 * Stub for now.	 * How to synchronise the TLBs?	 */	USED(va);} void*kmapdma(usize n){	void *a;	uintptr pa, va; 	if((n = kmapsize(n)) == 0)		return nil;	if((a = mallocalign(n, n, 0, 0)) == nil)		return nil; 	dcflush(PTR2UINT(a), n); 	pa = PADDR(a);	lock(&kmlock);	kmdma = ROUNDUP(kmdma, n);	va = kmdma;	kmdma += n;	unlock(&kmlock);	if(va > KSEG1 + 0x10000000){		free(a);		return nil;	}//print("kmapdma: va %#p pa %#p n %lud kmdma %#p\n", va, pa, n, kmdma); 	return kmapphys(va, pa, n, TLBI|TLBG|TLBWR);}