xref: /plan9-contrib/sys/src/9k/k10/mmu.c (revision 38151b0b66407dd6bdaaa567048e0f2778fb0149)
19ef1f84bSDavid du Colombier #include "u.h"
29ef1f84bSDavid du Colombier #include "../port/lib.h"
39ef1f84bSDavid du Colombier #include "mem.h"
49ef1f84bSDavid du Colombier #include "dat.h"
59ef1f84bSDavid du Colombier #include "fns.h"
69ef1f84bSDavid du Colombier 
79ef1f84bSDavid du Colombier #include "amd64.h"
89ef1f84bSDavid du Colombier 
99ef1f84bSDavid du Colombier #define ALIGNED(p, a)	(!(((uintptr)(p)) & ((a)-1)))
109ef1f84bSDavid du Colombier 
119ef1f84bSDavid du Colombier #define PDMAP		(0xffffffffff800000ull)
129ef1f84bSDavid du Colombier #define PDPX(v)		PTLX((v), 2)
139ef1f84bSDavid du Colombier #define PDX(v)		PTLX((v), 1)
149ef1f84bSDavid du Colombier #define PTX(v)		PTLX((v), 0)
159ef1f84bSDavid du Colombier 
169ef1f84bSDavid du Colombier #define VMAP		(0xffffffffe0000000ull)
179ef1f84bSDavid du Colombier #define VMAPSZ		(256*MiB)
189ef1f84bSDavid du Colombier 
199ef1f84bSDavid du Colombier #define KSEG1PML4	(0xffff000000000000ull\
209ef1f84bSDavid du Colombier 			|(PTLX(KSEG1, 3)<<(((3)*PTSHFT)+PGSHFT))\
219ef1f84bSDavid du Colombier 			|(PTLX(KSEG1, 3)<<(((2)*PTSHFT)+PGSHFT))\
229ef1f84bSDavid du Colombier 			|(PTLX(KSEG1, 3)<<(((1)*PTSHFT)+PGSHFT))\
239ef1f84bSDavid du Colombier 			|(PTLX(KSEG1, 3)<<(((0)*PTSHFT)+PGSHFT)))
249ef1f84bSDavid du Colombier 
259ef1f84bSDavid du Colombier #define KSEG1PTP(va, l)	((0xffff000000000000ull\
269ef1f84bSDavid du Colombier 			|(KSEG1PML4<<((3-(l))*PTSHFT))\
279ef1f84bSDavid du Colombier 			|(((va) & 0xffffffffffffull)>>(((l)+1)*PTSHFT))\
289ef1f84bSDavid du Colombier 			& ~0xfffull))
299ef1f84bSDavid du Colombier 
309ef1f84bSDavid du Colombier static Lock vmaplock;
319ef1f84bSDavid du Colombier static Page mach0pml4;
329ef1f84bSDavid du Colombier 
339ef1f84bSDavid du Colombier void
mmuflushtlb(u64int)349ef1f84bSDavid du Colombier mmuflushtlb(u64int)
359ef1f84bSDavid du Colombier {
369ef1f84bSDavid du Colombier 	if(m->pml4->daddr){
379ef1f84bSDavid du Colombier 		memset(UINT2PTR(m->pml4->va), 0, m->pml4->daddr*sizeof(PTE));
389ef1f84bSDavid du Colombier 		m->pml4->daddr = 0;
399ef1f84bSDavid du Colombier 	}
409ef1f84bSDavid du Colombier 	cr3put(m->pml4->pa);
419ef1f84bSDavid du Colombier }
429ef1f84bSDavid du Colombier 
439ef1f84bSDavid du Colombier void
mmuflush(void)449ef1f84bSDavid du Colombier mmuflush(void)
459ef1f84bSDavid du Colombier {
469ef1f84bSDavid du Colombier 	int s;
479ef1f84bSDavid du Colombier 
489ef1f84bSDavid du Colombier 	s = splhi();
499ef1f84bSDavid du Colombier 	up->newtlb = 1;
509ef1f84bSDavid du Colombier 	mmuswitch(up);
519ef1f84bSDavid du Colombier 	splx(s);
529ef1f84bSDavid du Colombier }
539ef1f84bSDavid du Colombier 
549ef1f84bSDavid du Colombier static void
mmuptpfree(Proc * proc,int release)559ef1f84bSDavid du Colombier mmuptpfree(Proc* proc, int release)
569ef1f84bSDavid du Colombier {
579ef1f84bSDavid du Colombier 	int l;
589ef1f84bSDavid du Colombier 	PTE *pte;
599ef1f84bSDavid du Colombier 	Page **last, *page;
609ef1f84bSDavid du Colombier 
619ef1f84bSDavid du Colombier 	/*
629ef1f84bSDavid du Colombier 	 * To do here:
639ef1f84bSDavid du Colombier 	 *	coalesce the clean and release functionality
649ef1f84bSDavid du Colombier 	 *	(it's either one or the other, and no need for
659ef1f84bSDavid du Colombier 	 *	wakeup in mmurelease as not using the palloc pool);
669ef1f84bSDavid du Colombier 	 *	0-based levels, not 1-based, for consistency;
679ef1f84bSDavid du Colombier 	 *	fix memset level for 2MiB pages;
689ef1f84bSDavid du Colombier 	 *	use a dedicated datastructure rather than Page?
699ef1f84bSDavid du Colombier 	 */
709ef1f84bSDavid du Colombier 	for(l = 1; l < 4; l++){
719ef1f84bSDavid du Colombier 		last = &proc->mmuptp[l];
729ef1f84bSDavid du Colombier 		if(*last == nil)
739ef1f84bSDavid du Colombier 			continue;
749ef1f84bSDavid du Colombier 		for(page = *last; page != nil; page = page->next){
759ef1f84bSDavid du Colombier 			if(!release){
769ef1f84bSDavid du Colombier 				if(l == 1)
779ef1f84bSDavid du Colombier 					memset(UINT2PTR(page->va), 0, PTSZ);
789ef1f84bSDavid du Colombier 				pte = UINT2PTR(page->prev->va);
799ef1f84bSDavid du Colombier 				pte[page->daddr] = 0;
809ef1f84bSDavid du Colombier 			}
819ef1f84bSDavid du Colombier 			last = &page->next;
829ef1f84bSDavid du Colombier 		}
839ef1f84bSDavid du Colombier 		*last = proc->mmuptp[0];
849ef1f84bSDavid du Colombier 		proc->mmuptp[0] = proc->mmuptp[l];
859ef1f84bSDavid du Colombier 		proc->mmuptp[l] = nil;
869ef1f84bSDavid du Colombier 	}
879ef1f84bSDavid du Colombier 
889ef1f84bSDavid du Colombier 	m->pml4->daddr = 0;
899ef1f84bSDavid du Colombier }
909ef1f84bSDavid du Colombier 
919ef1f84bSDavid du Colombier static Page*
mmuptpalloc(void)929ef1f84bSDavid du Colombier mmuptpalloc(void)
939ef1f84bSDavid du Colombier {
949ef1f84bSDavid du Colombier 	Page *page;
95*38151b0bSDavid du Colombier 	uintmem pa;
96*38151b0bSDavid du Colombier 	int color;
979ef1f84bSDavid du Colombier 
989ef1f84bSDavid du Colombier 	/*
999ef1f84bSDavid du Colombier 	 * Do not really need a whole Page structure,
1009ef1f84bSDavid du Colombier 	 * but it makes testing this out a lot easier.
1019ef1f84bSDavid du Colombier 	 * Could keep a cache and free excess.
1029ef1f84bSDavid du Colombier 	 */
1039ef1f84bSDavid du Colombier 	if((page = malloc(sizeof(Page))) == nil){
1049ef1f84bSDavid du Colombier 		print("mmuptpalloc Page\n");
1059ef1f84bSDavid du Colombier 
1069ef1f84bSDavid du Colombier 		return nil;
1079ef1f84bSDavid du Colombier 	}
108*38151b0bSDavid du Colombier 	color = NOCOLOR;
109*38151b0bSDavid du Colombier 	if((pa = physalloc(PTSZ, &color, page)) == 0){
110*38151b0bSDavid du Colombier 		print("mmuptpalloc pa\n");
1119ef1f84bSDavid du Colombier 		free(page);
1129ef1f84bSDavid du Colombier 
1139ef1f84bSDavid du Colombier 		return nil;
1149ef1f84bSDavid du Colombier 	}
1159ef1f84bSDavid du Colombier 
116*38151b0bSDavid du Colombier 	page->va = PTR2UINT(KADDR(pa));
117*38151b0bSDavid du Colombier 	page->pa = pa;
1189ef1f84bSDavid du Colombier 	page->ref = 1;
119*38151b0bSDavid du Colombier 	page->color = color;
120*38151b0bSDavid du Colombier 	memset(UINT2PTR(page->va), 0, PTSZ);
1219ef1f84bSDavid du Colombier 
1229ef1f84bSDavid du Colombier 	return page;
1239ef1f84bSDavid du Colombier }
1249ef1f84bSDavid du Colombier 
1259ef1f84bSDavid du Colombier void
mmuswitch(Proc * proc)1269ef1f84bSDavid du Colombier mmuswitch(Proc* proc)
1279ef1f84bSDavid du Colombier {
1289ef1f84bSDavid du Colombier 	PTE *pte;
1299ef1f84bSDavid du Colombier 	Page *page;
1309ef1f84bSDavid du Colombier 
1319ef1f84bSDavid du Colombier 	if(proc->newtlb){
1329ef1f84bSDavid du Colombier 		mmuptpfree(proc, 0);
1339ef1f84bSDavid du Colombier 		proc->newtlb = 0;
1349ef1f84bSDavid du Colombier 	}
1359ef1f84bSDavid du Colombier 
1369ef1f84bSDavid du Colombier 	if(m->pml4->daddr){
1379ef1f84bSDavid du Colombier 		memset(UINT2PTR(m->pml4->va), 0, m->pml4->daddr*sizeof(PTE));
1389ef1f84bSDavid du Colombier 		m->pml4->daddr = 0;
1399ef1f84bSDavid du Colombier 	}
1409ef1f84bSDavid du Colombier 
1419ef1f84bSDavid du Colombier 	pte = UINT2PTR(m->pml4->va);
1429ef1f84bSDavid du Colombier 	for(page = proc->mmuptp[3]; page != nil; page = page->next){
1439ef1f84bSDavid du Colombier 		pte[page->daddr] = PPN(page->pa)|PteU|PteRW|PteP;
1449ef1f84bSDavid du Colombier 		if(page->daddr >= m->pml4->daddr)
1459ef1f84bSDavid du Colombier 			m->pml4->daddr = page->daddr+1;
1469ef1f84bSDavid du Colombier 		page->prev = m->pml4;
1479ef1f84bSDavid du Colombier 	}
1489ef1f84bSDavid du Colombier 
1499ef1f84bSDavid du Colombier 	tssrsp0(STACKALIGN(PTR2UINT(proc->kstack+KSTACK)));
1509ef1f84bSDavid du Colombier 	cr3put(m->pml4->pa);
1519ef1f84bSDavid du Colombier }
1529ef1f84bSDavid du Colombier 
1539ef1f84bSDavid du Colombier void
mmurelease(Proc * proc)1549ef1f84bSDavid du Colombier mmurelease(Proc* proc)
1559ef1f84bSDavid du Colombier {
1569ef1f84bSDavid du Colombier 	Page *page, *next;
1579ef1f84bSDavid du Colombier 
1589ef1f84bSDavid du Colombier 	/*
1599ef1f84bSDavid du Colombier 	 * See comments in mmuptpfree above.
1609ef1f84bSDavid du Colombier 	 */
1619ef1f84bSDavid du Colombier 	mmuptpfree(proc, 1);
1629ef1f84bSDavid du Colombier 
1639ef1f84bSDavid du Colombier 	for(page = proc->mmuptp[0]; page != nil; page = next){
1649ef1f84bSDavid du Colombier 		next = page->next;
1659ef1f84bSDavid du Colombier 		if(--page->ref)
1669ef1f84bSDavid du Colombier 			panic("mmurelease: page->ref %d\n", page->ref);
167*38151b0bSDavid du Colombier 		physfree(page->pa, PTSZ);
1689ef1f84bSDavid du Colombier 		free(page);
1699ef1f84bSDavid du Colombier 	}
1709ef1f84bSDavid du Colombier 	if(proc->mmuptp[0] && palloc.r.p)
1719ef1f84bSDavid du Colombier 		wakeup(&palloc.r);
1729ef1f84bSDavid du Colombier 	proc->mmuptp[0] = nil;
1739ef1f84bSDavid du Colombier 
1749ef1f84bSDavid du Colombier 	tssrsp0(STACKALIGN(m->stack+MACHSTKSZ));
1759ef1f84bSDavid du Colombier 	cr3put(m->pml4->pa);
1769ef1f84bSDavid du Colombier }
1779ef1f84bSDavid du Colombier 
1789ef1f84bSDavid du Colombier static PTE*
mmuptpget(uintptr va,int level)1799ef1f84bSDavid du Colombier mmuptpget(uintptr va, int level)
1809ef1f84bSDavid du Colombier {
1819ef1f84bSDavid du Colombier 	return (PTE*)KSEG1PTP(va, level);
1829ef1f84bSDavid du Colombier }
1839ef1f84bSDavid du Colombier 
1849ef1f84bSDavid du Colombier void
mmuput(uintptr va,uintmem pa,Page *)1859ef1f84bSDavid du Colombier mmuput(uintptr va, uintmem pa, Page*)
1869ef1f84bSDavid du Colombier {
1879ef1f84bSDavid du Colombier 	Mpl pl;
1889ef1f84bSDavid du Colombier 	int l, x;
1899ef1f84bSDavid du Colombier 	PTE *pte, *ptp;
1909ef1f84bSDavid du Colombier 	Page *page, *prev;
1919ef1f84bSDavid du Colombier 
1929ef1f84bSDavid du Colombier 	pte = nil;
1939ef1f84bSDavid du Colombier 	pl = splhi();
1949ef1f84bSDavid du Colombier 	prev = m->pml4;
1959ef1f84bSDavid du Colombier 	for(l = 3; l >= 0; l--){
1969ef1f84bSDavid du Colombier 		ptp = mmuptpget(va, l);
1979ef1f84bSDavid du Colombier 		x = PTLX(va, l);
1989ef1f84bSDavid du Colombier 		pte = &ptp[x];
1999ef1f84bSDavid du Colombier 		for(page = up->mmuptp[l]; page != nil; page = page->next){
2009ef1f84bSDavid du Colombier 			if(page->prev == prev && page->daddr == x)
2019ef1f84bSDavid du Colombier 				break;
2029ef1f84bSDavid du Colombier 		}
2039ef1f84bSDavid du Colombier 		if(page == nil){
204fcc23d6fSDavid du Colombier 			if(up->mmuptp[0] == nil)
2059ef1f84bSDavid du Colombier 				page = mmuptpalloc();
2069ef1f84bSDavid du Colombier 			else {
2079ef1f84bSDavid du Colombier 				page = up->mmuptp[0];
2089ef1f84bSDavid du Colombier 				up->mmuptp[0] = page->next;
2099ef1f84bSDavid du Colombier 			}
2109ef1f84bSDavid du Colombier 			page->daddr = x;
2119ef1f84bSDavid du Colombier 			page->next = up->mmuptp[l];
2129ef1f84bSDavid du Colombier 			up->mmuptp[l] = page;
2139ef1f84bSDavid du Colombier 			page->prev = prev;
2149ef1f84bSDavid du Colombier 			*pte = PPN(page->pa)|PteU|PteRW|PteP;
2159ef1f84bSDavid du Colombier 			if(l == 3 && x >= m->pml4->daddr)
2169ef1f84bSDavid du Colombier 				m->pml4->daddr = x+1;
2179ef1f84bSDavid du Colombier 		}
2189ef1f84bSDavid du Colombier 		prev = page;
2199ef1f84bSDavid du Colombier 	}
2209ef1f84bSDavid du Colombier 
2219ef1f84bSDavid du Colombier 	*pte = pa|PteU;
2229ef1f84bSDavid du Colombier //if(pa & PteRW)
2239ef1f84bSDavid du Colombier //  *pte |= PteNX;
2249ef1f84bSDavid du Colombier 	splx(pl);
2259ef1f84bSDavid du Colombier 
2269ef1f84bSDavid du Colombier 	invlpg(va);			/* only if old entry valid? */
2279ef1f84bSDavid du Colombier }
2289ef1f84bSDavid du Colombier 
2299ef1f84bSDavid du Colombier static PTE
pdeget(uintptr va)2309ef1f84bSDavid du Colombier pdeget(uintptr va)
2319ef1f84bSDavid du Colombier {
2329ef1f84bSDavid du Colombier 	PTE *pdp;
2339ef1f84bSDavid du Colombier 
2349ef1f84bSDavid du Colombier 	if(va < 0xffffffffc0000000ull)
2359ef1f84bSDavid du Colombier 		panic("pdeget(%#p)", va);
2369ef1f84bSDavid du Colombier 
2379ef1f84bSDavid du Colombier 	pdp = (PTE*)(PDMAP+PDX(PDMAP)*4096);
2389ef1f84bSDavid du Colombier 
2399ef1f84bSDavid du Colombier 	return pdp[PDX(va)];
2409ef1f84bSDavid du Colombier }
2419ef1f84bSDavid du Colombier 
2429ef1f84bSDavid du Colombier /*
2439ef1f84bSDavid du Colombier  * Add kernel mappings for pa -> va for a section of size bytes.
2449ef1f84bSDavid du Colombier  * Called only after the va range is known to be unoccupied.
2459ef1f84bSDavid du Colombier  */
2469ef1f84bSDavid du Colombier static int
pdmap(uintmem pa,int attr,uintptr va,usize size)2479ef1f84bSDavid du Colombier pdmap(uintmem pa, int attr, uintptr va, usize size)
2489ef1f84bSDavid du Colombier {
2499ef1f84bSDavid du Colombier 	uintmem pae;
2509ef1f84bSDavid du Colombier 	PTE *pd, *pde, *pt, *pte;
251*38151b0bSDavid du Colombier 	uintmem pdpa;
252*38151b0bSDavid du Colombier 	int pdx, pgsz, color;
2539ef1f84bSDavid du Colombier 
2549ef1f84bSDavid du Colombier 	pd = (PTE*)(PDMAP+PDX(PDMAP)*4096);
2559ef1f84bSDavid du Colombier 
2569ef1f84bSDavid du Colombier 	for(pae = pa + size; pa < pae; pa += pgsz){
2579ef1f84bSDavid du Colombier 		pdx = PDX(va);
2589ef1f84bSDavid du Colombier 		pde = &pd[pdx];
2599ef1f84bSDavid du Colombier 
2609ef1f84bSDavid du Colombier 		/*
2619ef1f84bSDavid du Colombier 		 * Check if it can be mapped using a big page,
2629ef1f84bSDavid du Colombier 		 * i.e. is big enough and starts on a suitable boundary.
2639ef1f84bSDavid du Colombier 		 * Assume processor can do it.
2649ef1f84bSDavid du Colombier 		 */
2659ef1f84bSDavid du Colombier 		if(ALIGNED(pa, PGLSZ(1)) && ALIGNED(va, PGLSZ(1)) && (pae-pa) >= PGLSZ(1)){
2669ef1f84bSDavid du Colombier 			assert(*pde == 0);
2679ef1f84bSDavid du Colombier 			*pde = pa|attr|PtePS|PteP;
2689ef1f84bSDavid du Colombier 			pgsz = PGLSZ(1);
2699ef1f84bSDavid du Colombier 		}
2709ef1f84bSDavid du Colombier 		else{
271*38151b0bSDavid du Colombier 			pt = (PTE*)(PDMAP+pdx*PTSZ);
2729ef1f84bSDavid du Colombier 			if(*pde == 0){
273*38151b0bSDavid du Colombier 				color = NOCOLOR;
274*38151b0bSDavid du Colombier 				pdpa = physalloc(PTSZ, &color, nil);
275*38151b0bSDavid du Colombier 				if(pdpa == 0)
276*38151b0bSDavid du Colombier 					panic("pdmap");
277*38151b0bSDavid du Colombier 				*pde = pdpa|PteRW|PteP;
278*38151b0bSDavid du Colombier 				memset(pt, 0, PTSZ);
2799ef1f84bSDavid du Colombier 			}
2809ef1f84bSDavid du Colombier 
2819ef1f84bSDavid du Colombier 			pte = &pt[PTX(va)];
2829ef1f84bSDavid du Colombier 			assert(!(*pte & PteP));
2839ef1f84bSDavid du Colombier 			*pte = pa|attr|PteP;
2849ef1f84bSDavid du Colombier 			pgsz = PGLSZ(0);
2859ef1f84bSDavid du Colombier 		}
2869ef1f84bSDavid du Colombier 		va += pgsz;
2879ef1f84bSDavid du Colombier 	}
2889ef1f84bSDavid du Colombier 
2899ef1f84bSDavid du Colombier 	return 0;
2909ef1f84bSDavid du Colombier }
2919ef1f84bSDavid du Colombier 
2929ef1f84bSDavid du Colombier static int
findhole(PTE * a,int n,int count)2939ef1f84bSDavid du Colombier findhole(PTE* a, int n, int count)
2949ef1f84bSDavid du Colombier {
2959ef1f84bSDavid du Colombier 	int have, i;
2969ef1f84bSDavid du Colombier 
2979ef1f84bSDavid du Colombier 	have = 0;
2989ef1f84bSDavid du Colombier 	for(i = 0; i < n; i++){
2999ef1f84bSDavid du Colombier 		if(a[i] == 0)
3009ef1f84bSDavid du Colombier 			have++;
3019ef1f84bSDavid du Colombier 		else
3029ef1f84bSDavid du Colombier 			have = 0;
3039ef1f84bSDavid du Colombier 		if(have >= count)
3049ef1f84bSDavid du Colombier 			return i+1 - have;
3059ef1f84bSDavid du Colombier 	}
3069ef1f84bSDavid du Colombier 
3079ef1f84bSDavid du Colombier 	return -1;
3089ef1f84bSDavid du Colombier }
3099ef1f84bSDavid du Colombier 
3109ef1f84bSDavid du Colombier /*
3119ef1f84bSDavid du Colombier  * Look for free space in the vmap.
3129ef1f84bSDavid du Colombier  */
3139ef1f84bSDavid du Colombier static uintptr
vmapalloc(usize size)3149ef1f84bSDavid du Colombier vmapalloc(usize size)
3159ef1f84bSDavid du Colombier {
3169ef1f84bSDavid du Colombier 	int i, n, o;
3179ef1f84bSDavid du Colombier 	PTE *pd, *pt;
3189ef1f84bSDavid du Colombier 	int pdsz, ptsz;
3199ef1f84bSDavid du Colombier 
3209ef1f84bSDavid du Colombier 	pd = (PTE*)(PDMAP+PDX(PDMAP)*4096);
3219ef1f84bSDavid du Colombier 	pd += PDX(VMAP);
3229ef1f84bSDavid du Colombier 	pdsz = VMAPSZ/PGLSZ(1);
3239ef1f84bSDavid du Colombier 
3249ef1f84bSDavid du Colombier 	/*
3259ef1f84bSDavid du Colombier 	 * Look directly in the PD entries if the size is
3269ef1f84bSDavid du Colombier 	 * larger than the range mapped by a single entry.
3279ef1f84bSDavid du Colombier 	 */
3289ef1f84bSDavid du Colombier 	if(size >= PGLSZ(1)){
3299ef1f84bSDavid du Colombier 		n = HOWMANY(size, PGLSZ(1));
3309ef1f84bSDavid du Colombier 		if((o = findhole(pd, pdsz, n)) != -1)
3319ef1f84bSDavid du Colombier 			return VMAP + o*PGLSZ(1);
3329ef1f84bSDavid du Colombier 		return 0;
3339ef1f84bSDavid du Colombier 	}
3349ef1f84bSDavid du Colombier 
3359ef1f84bSDavid du Colombier 	/*
3369ef1f84bSDavid du Colombier 	 * Size is smaller than that mapped by a single PD entry.
3379ef1f84bSDavid du Colombier 	 * Look for an already mapped PT page that has room.
3389ef1f84bSDavid du Colombier 	 */
3399ef1f84bSDavid du Colombier 	n = HOWMANY(size, PGLSZ(0));
3409ef1f84bSDavid du Colombier 	ptsz = PGLSZ(0)/sizeof(PTE);
3419ef1f84bSDavid du Colombier 	for(i = 0; i < pdsz; i++){
3429ef1f84bSDavid du Colombier 		if(!(pd[i] & PteP) || (pd[i] & PtePS))
3439ef1f84bSDavid du Colombier 			continue;
3449ef1f84bSDavid du Colombier 
3459ef1f84bSDavid du Colombier 		pt = (PTE*)(PDMAP+(PDX(VMAP)+i)*4096);
3469ef1f84bSDavid du Colombier 		if((o = findhole(pt, ptsz, n)) != -1)
3479ef1f84bSDavid du Colombier 			return VMAP + i*PGLSZ(1) + o*PGLSZ(0);
3489ef1f84bSDavid du Colombier 	}
3499ef1f84bSDavid du Colombier 
3509ef1f84bSDavid du Colombier 	/*
3519ef1f84bSDavid du Colombier 	 * Nothing suitable, start using a new PD entry.
3529ef1f84bSDavid du Colombier 	 */
3539ef1f84bSDavid du Colombier 	if((o = findhole(pd, pdsz, 1)) != -1)
3549ef1f84bSDavid du Colombier 		return VMAP + o*PGLSZ(1);
3559ef1f84bSDavid du Colombier 
3569ef1f84bSDavid du Colombier 	return 0;
3579ef1f84bSDavid du Colombier }
3589ef1f84bSDavid du Colombier 
3599ef1f84bSDavid du Colombier void*
vmap(uintmem pa,usize size)3609ef1f84bSDavid du Colombier vmap(uintmem pa, usize size)
3619ef1f84bSDavid du Colombier {
3629ef1f84bSDavid du Colombier 	uintptr va;
3639ef1f84bSDavid du Colombier 	usize o, sz;
3649ef1f84bSDavid du Colombier 
3659ef1f84bSDavid du Colombier 	DBG("vmap(%#P, %lud)\n", pa, size);
3669ef1f84bSDavid du Colombier 
3679ef1f84bSDavid du Colombier 	if(m->machno != 0)
3689ef1f84bSDavid du Colombier 		panic("vmap");
3699ef1f84bSDavid du Colombier 
3709ef1f84bSDavid du Colombier 	/*
3719ef1f84bSDavid du Colombier 	 * This is incomplete; the checks are not comprehensive
3729ef1f84bSDavid du Colombier 	 * enough.
3739ef1f84bSDavid du Colombier 	 * Sometimes the request is for an already-mapped piece
3749ef1f84bSDavid du Colombier 	 * of low memory, in which case just return a good value
3759ef1f84bSDavid du Colombier 	 * and hope that a corresponding vunmap of the address
3769ef1f84bSDavid du Colombier 	 * will have the same address.
3779ef1f84bSDavid du Colombier 	 * To do this properly will require keeping track of the
3789ef1f84bSDavid du Colombier 	 * mappings; perhaps something like kmap, but kmap probably
3799ef1f84bSDavid du Colombier 	 * can't be used early enough for some of the uses.
3809ef1f84bSDavid du Colombier 	 */
3819ef1f84bSDavid du Colombier 	if(pa+size < 1ull*MiB)
3829ef1f84bSDavid du Colombier 		return KADDR(pa);
3839ef1f84bSDavid du Colombier 	if(pa < 1ull*MiB)
3849ef1f84bSDavid du Colombier 		return nil;
3859ef1f84bSDavid du Colombier 
3869ef1f84bSDavid du Colombier 	/*
3879ef1f84bSDavid du Colombier 	 * Might be asking for less than a page.
3889ef1f84bSDavid du Colombier 	 * This should have a smaller granularity if
3899ef1f84bSDavid du Colombier 	 * the page size is large.
3909ef1f84bSDavid du Colombier 	 */
3919ef1f84bSDavid du Colombier 	o = pa & ((1<<PGSHFT)-1);
3929ef1f84bSDavid du Colombier 	pa -= o;
3939ef1f84bSDavid du Colombier 	sz = ROUNDUP(size+o, PGSZ);
3949ef1f84bSDavid du Colombier 
3959ef1f84bSDavid du Colombier 	if(pa == 0){
3969ef1f84bSDavid du Colombier 		DBG("vmap(0, %lud) pc=%#p\n", size, getcallerpc(&pa));
3979ef1f84bSDavid du Colombier 		return nil;
3989ef1f84bSDavid du Colombier 	}
3999ef1f84bSDavid du Colombier 	ilock(&vmaplock);
4009ef1f84bSDavid du Colombier 	if((va = vmapalloc(sz)) == 0 || pdmap(pa, PtePCD|PteRW, va, sz) < 0){
4019ef1f84bSDavid du Colombier 		iunlock(&vmaplock);
4029ef1f84bSDavid du Colombier 		return nil;
4039ef1f84bSDavid du Colombier 	}
4049ef1f84bSDavid du Colombier 	iunlock(&vmaplock);
4059ef1f84bSDavid du Colombier 
4069ef1f84bSDavid du Colombier 	DBG("vmap(%#P, %lud) => %#p\n", pa+o, size, va+o);
4079ef1f84bSDavid du Colombier 
4089ef1f84bSDavid du Colombier 	return UINT2PTR(va + o);
4099ef1f84bSDavid du Colombier }
4109ef1f84bSDavid du Colombier 
4119ef1f84bSDavid du Colombier void
vunmap(void * v,usize size)4129ef1f84bSDavid du Colombier vunmap(void* v, usize size)
4139ef1f84bSDavid du Colombier {
4149ef1f84bSDavid du Colombier 	uintptr va;
4159ef1f84bSDavid du Colombier 
4169ef1f84bSDavid du Colombier 	DBG("vunmap(%#p, %lud)\n", v, size);
4179ef1f84bSDavid du Colombier 
4189ef1f84bSDavid du Colombier 	if(m->machno != 0)
4199ef1f84bSDavid du Colombier 		panic("vunmap");
4209ef1f84bSDavid du Colombier 
4219ef1f84bSDavid du Colombier 	/*
4229ef1f84bSDavid du Colombier 	 * See the comments above in vmap.
4239ef1f84bSDavid du Colombier 	 */
4249ef1f84bSDavid du Colombier 	va = PTR2UINT(v);
4259ef1f84bSDavid du Colombier 	if(va >= KZERO && va+size < KZERO+1ull*MiB)
4269ef1f84bSDavid du Colombier 		return;
4279ef1f84bSDavid du Colombier 
4289ef1f84bSDavid du Colombier 	/*
4299ef1f84bSDavid du Colombier 	 * Here will have to deal with releasing any
4309ef1f84bSDavid du Colombier 	 * resources used for the allocation (e.g. page table
4319ef1f84bSDavid du Colombier 	 * pages).
4329ef1f84bSDavid du Colombier 	 */
4339ef1f84bSDavid du Colombier 	DBG("vunmap(%#p, %lud)\n", v, size);
4349ef1f84bSDavid du Colombier }
4359ef1f84bSDavid du Colombier 
4369ef1f84bSDavid du Colombier int
mmuwalk(uintptr va,int level,PTE ** ret,u64int (* alloc)(usize))4379ef1f84bSDavid du Colombier mmuwalk(uintptr va, int level, PTE** ret, u64int (*alloc)(usize))
4389ef1f84bSDavid du Colombier {
4399ef1f84bSDavid du Colombier //alloc and pa - uintmem or PTE or what?
4409ef1f84bSDavid du Colombier 	int l;
4419ef1f84bSDavid du Colombier 	Mpl pl;
4429ef1f84bSDavid du Colombier 	uintptr pa;
4439ef1f84bSDavid du Colombier 	PTE *pte, *ptp;
4449ef1f84bSDavid du Colombier 
4459ef1f84bSDavid du Colombier 	DBG("mmuwalk%d: va %#p level %d\n", m->machno, va, level);
4469ef1f84bSDavid du Colombier 	pte = nil;
4479ef1f84bSDavid du Colombier 	pl = splhi();
4489ef1f84bSDavid du Colombier 	for(l = 3; l >= 0; l--){
4499ef1f84bSDavid du Colombier 		ptp = mmuptpget(va, l);
4509ef1f84bSDavid du Colombier 		pte = &ptp[PTLX(va, l)];
4519ef1f84bSDavid du Colombier 		if(l == level)
4529ef1f84bSDavid du Colombier 			break;
4539ef1f84bSDavid du Colombier 		if(!(*pte & PteP)){
4549ef1f84bSDavid du Colombier 			if(alloc == nil)
4559ef1f84bSDavid du Colombier 				break;
4569ef1f84bSDavid du Colombier 			pa = alloc(PTSZ);
4579ef1f84bSDavid du Colombier 			if(pa == ~0)
4589ef1f84bSDavid du Colombier 				return -1;
4599ef1f84bSDavid du Colombier if(pa & 0xfffull) print("mmuwalk pa %#llux\n", pa);
4609ef1f84bSDavid du Colombier 			*pte = pa|PteRW|PteP;
4619ef1f84bSDavid du Colombier 			if((ptp = mmuptpget(va, l-1)) == nil)
4629ef1f84bSDavid du Colombier 				panic("mmuwalk: mmuptpget(%#p, %d)\n", va, l-1);
4639ef1f84bSDavid du Colombier 			memset(ptp, 0, PTSZ);
4649ef1f84bSDavid du Colombier 		}
4659ef1f84bSDavid du Colombier 		else if(*pte & PtePS)
4669ef1f84bSDavid du Colombier 			break;
4679ef1f84bSDavid du Colombier 	}
4689ef1f84bSDavid du Colombier 	*ret = pte;
4699ef1f84bSDavid du Colombier 	splx(pl);
4709ef1f84bSDavid du Colombier 
4719ef1f84bSDavid du Colombier 	return l;
4729ef1f84bSDavid du Colombier }
4739ef1f84bSDavid du Colombier 
4749ef1f84bSDavid du Colombier u64int
mmuphysaddr(uintptr va)4759ef1f84bSDavid du Colombier mmuphysaddr(uintptr va)
4769ef1f84bSDavid du Colombier {
4779ef1f84bSDavid du Colombier 	int l;
4789ef1f84bSDavid du Colombier 	PTE *pte;
4799ef1f84bSDavid du Colombier 	u64int mask, pa;
4809ef1f84bSDavid du Colombier 
4819ef1f84bSDavid du Colombier 	/*
4829ef1f84bSDavid du Colombier 	 * Given a VA, find the PA.
4839ef1f84bSDavid du Colombier 	 * This is probably not the right interface,
4849ef1f84bSDavid du Colombier 	 * but will do as an experiment. Usual
4859ef1f84bSDavid du Colombier 	 * question, should va be void* or uintptr?
4869ef1f84bSDavid du Colombier 	 */
4879ef1f84bSDavid du Colombier 	l = mmuwalk(va, 0, &pte, nil);
4889ef1f84bSDavid du Colombier 	DBG("mmuphysaddr: va %#p l %d\n", va, l);
4899ef1f84bSDavid du Colombier 	if(l < 0)
4909ef1f84bSDavid du Colombier 		return ~0;
4919ef1f84bSDavid du Colombier 
4929ef1f84bSDavid du Colombier 	mask = (1ull<<(((l)*PTSHFT)+PGSHFT))-1;
4939ef1f84bSDavid du Colombier 	pa = (*pte & ~mask) + (va & mask);
4949ef1f84bSDavid du Colombier 
4959ef1f84bSDavid du Colombier 	DBG("mmuphysaddr: l %d va %#p pa %#llux\n", l, va, pa);
4969ef1f84bSDavid du Colombier 
4979ef1f84bSDavid du Colombier 	return pa;
4989ef1f84bSDavid du Colombier }
4999ef1f84bSDavid du Colombier 
5009ef1f84bSDavid du Colombier void
mmuinit(void)5019ef1f84bSDavid du Colombier mmuinit(void)
5029ef1f84bSDavid du Colombier {
5039ef1f84bSDavid du Colombier 	int l;
5049ef1f84bSDavid du Colombier 	uchar *p;
5059ef1f84bSDavid du Colombier 	PTE *pte;
5069ef1f84bSDavid du Colombier 	Page *page;
5079ef1f84bSDavid du Colombier 	uintptr pml4;
5089ef1f84bSDavid du Colombier 	u64int o, pa, r, sz;
5099ef1f84bSDavid du Colombier 
5109ef1f84bSDavid du Colombier 	archmmu();
5119ef1f84bSDavid du Colombier 	DBG("mach%d: %#p npgsz %d\n", m->machno, m, m->npgsz);
5129ef1f84bSDavid du Colombier 	if(m->machno != 0){
5139ef1f84bSDavid du Colombier 		/*
5149ef1f84bSDavid du Colombier 		 * GAK: Has to go when each mach is using
5159ef1f84bSDavid du Colombier 		 * its own page table
5169ef1f84bSDavid du Colombier 		 */
5179ef1f84bSDavid du Colombier 		p = UINT2PTR(m->stack);
5189ef1f84bSDavid du Colombier 		p += MACHSTKSZ;
5199ef1f84bSDavid du Colombier 		memmove(p, UINT2PTR(mach0pml4.va), PTSZ);
5209ef1f84bSDavid du Colombier 		m->pml4 = &m->pml4kludge;
5219ef1f84bSDavid du Colombier 		m->pml4->va = PTR2UINT(p);
5229ef1f84bSDavid du Colombier 		m->pml4->pa = PADDR(p);
5239ef1f84bSDavid du Colombier 		m->pml4->daddr = mach0pml4.daddr;	/* # of user mappings in pml4 */
5249ef1f84bSDavid du Colombier 		if(m->pml4->daddr){
5259ef1f84bSDavid du Colombier 			memset(p, 0, m->pml4->daddr*sizeof(PTE));
5269ef1f84bSDavid du Colombier 			m->pml4->daddr = 0;
5279ef1f84bSDavid du Colombier 		}
5289ef1f84bSDavid du Colombier pte = (PTE*)p;
5299ef1f84bSDavid du Colombier pte[PTLX(KSEG1PML4, 3)] = m->pml4->pa|PteRW|PteP;
5309ef1f84bSDavid du Colombier 
5319ef1f84bSDavid du Colombier 		r = rdmsr(Efer);
5329ef1f84bSDavid du Colombier 		r |= Nxe;
5339ef1f84bSDavid du Colombier 		wrmsr(Efer, r);
5349ef1f84bSDavid du Colombier 		cr3put(m->pml4->pa);
535fdf34543SDavid du Colombier 		DBG("mach%d: %#p pml4 %#p\n", m->machno, m, m->pml4);
5369ef1f84bSDavid du Colombier 		return;
5379ef1f84bSDavid du Colombier 	}
5389ef1f84bSDavid du Colombier 
5399ef1f84bSDavid du Colombier 	page = &mach0pml4;
5409ef1f84bSDavid du Colombier 	page->pa = cr3get();
5419ef1f84bSDavid du Colombier 	page->va = PTR2UINT(sys->pml4);
5429ef1f84bSDavid du Colombier 
5439ef1f84bSDavid du Colombier 	m->pml4 = page;
5449ef1f84bSDavid du Colombier 
5459ef1f84bSDavid du Colombier 	r = rdmsr(Efer);
5469ef1f84bSDavid du Colombier 	r |= Nxe;
5479ef1f84bSDavid du Colombier 	wrmsr(Efer, r);
5489ef1f84bSDavid du Colombier 
5499ef1f84bSDavid du Colombier 	/*
5509ef1f84bSDavid du Colombier 	 * Set up the various kernel memory allocator limits:
5519ef1f84bSDavid du Colombier 	 * pmstart/pmend bound the unused physical memory;
5529ef1f84bSDavid du Colombier 	 * vmstart/vmend bound the total possible virtual memory
5539ef1f84bSDavid du Colombier 	 * used by the kernel;
5549ef1f84bSDavid du Colombier 	 * vmunused is the highest virtual address currently mapped
5559ef1f84bSDavid du Colombier 	 * and used by the kernel;
5569ef1f84bSDavid du Colombier 	 * vmunmapped is the highest virtual address currently
5579ef1f84bSDavid du Colombier 	 * mapped by the kernel.
5589ef1f84bSDavid du Colombier 	 * Vmunused can be bumped up to vmunmapped before more
5599ef1f84bSDavid du Colombier 	 * physical memory needs to be allocated and mapped.
5609ef1f84bSDavid du Colombier 	 *
5619ef1f84bSDavid du Colombier 	 * This is set up here so meminit can map appropriately.
5629ef1f84bSDavid du Colombier 	 */
5639ef1f84bSDavid du Colombier 	o = sys->pmstart;
5649ef1f84bSDavid du Colombier 	sz = ROUNDUP(o, 4*MiB) - o;
5659ef1f84bSDavid du Colombier 	pa = asmalloc(0, sz, 1, 0);
5669ef1f84bSDavid du Colombier 	if(pa != o)
5679ef1f84bSDavid du Colombier 		panic("mmuinit: pa %#llux memstart %#llux\n", pa, o);
5689ef1f84bSDavid du Colombier 	sys->pmstart += sz;
5699ef1f84bSDavid du Colombier 
5709ef1f84bSDavid du Colombier 	sys->vmstart = KSEG0;
5719ef1f84bSDavid du Colombier 	sys->vmunused = sys->vmstart + ROUNDUP(o, 4*KiB);
5729ef1f84bSDavid du Colombier 	sys->vmunmapped = sys->vmstart + o + sz;
5739ef1f84bSDavid du Colombier 	sys->vmend = sys->vmstart + TMFM;
5749ef1f84bSDavid du Colombier 
5759ef1f84bSDavid du Colombier 	print("mmuinit: vmstart %#p vmunused %#p vmunmapped %#p vmend %#p\n",
5769ef1f84bSDavid du Colombier 		sys->vmstart, sys->vmunused, sys->vmunmapped, sys->vmend);
5779ef1f84bSDavid du Colombier 
5789ef1f84bSDavid du Colombier 	/*
5799ef1f84bSDavid du Colombier 	 * Set up the map for PD entry access by inserting
5809ef1f84bSDavid du Colombier 	 * the relevant PDP entry into the PD. It's equivalent
5819ef1f84bSDavid du Colombier 	 * to PADDR(sys->pd)|PteRW|PteP.
5829ef1f84bSDavid du Colombier 	 *
5839ef1f84bSDavid du Colombier 	 * Change code that uses this to use the KSEG1PML4
5849ef1f84bSDavid du Colombier 	 * map below.
5859ef1f84bSDavid du Colombier 	 */
5869ef1f84bSDavid du Colombier 	sys->pd[PDX(PDMAP)] = sys->pdp[PDPX(PDMAP)] & ~(PteD|PteA);
5879ef1f84bSDavid du Colombier 	print("sys->pd %#p %#p\n", sys->pd[PDX(PDMAP)], sys->pdp[PDPX(PDMAP)]);
5889ef1f84bSDavid du Colombier 
5899ef1f84bSDavid du Colombier 	assert((pdeget(PDMAP) & ~(PteD|PteA)) == (PADDR(sys->pd)|PteRW|PteP));
5909ef1f84bSDavid du Colombier 
5919ef1f84bSDavid du Colombier 	/*
5929ef1f84bSDavid du Colombier 	 * Set up the map for PTE access by inserting
5939ef1f84bSDavid du Colombier 	 * the relevant PML4 into itself.
5949ef1f84bSDavid du Colombier 	 * Note: outwith level 0, PteG is MBZ on AMD processors,
5959ef1f84bSDavid du Colombier 	 * is 'Reserved' on Intel processors, and the behaviour
5969ef1f84bSDavid du Colombier 	 * can be different.
5979ef1f84bSDavid du Colombier 	 */
5989ef1f84bSDavid du Colombier 	pml4 = cr3get();
5999ef1f84bSDavid du Colombier 	sys->pml4[PTLX(KSEG1PML4, 3)] = pml4|PteRW|PteP;
6009ef1f84bSDavid du Colombier 	cr3put(m->pml4->pa);
6019ef1f84bSDavid du Colombier 
6029ef1f84bSDavid du Colombier 	if((l = mmuwalk(KZERO, 3, &pte, nil)) >= 0)
6039ef1f84bSDavid du Colombier 		print("l %d %#p %llux\n", l, pte, *pte);
6049ef1f84bSDavid du Colombier 	if((l = mmuwalk(KZERO, 2, &pte, nil)) >= 0)
6059ef1f84bSDavid du Colombier 		print("l %d %#p %llux\n", l, pte, *pte);
6069ef1f84bSDavid du Colombier 	if((l = mmuwalk(KZERO, 1, &pte, nil)) >= 0)
6079ef1f84bSDavid du Colombier 		print("l %d %#p %llux\n", l, pte, *pte);
6089ef1f84bSDavid du Colombier 	if((l = mmuwalk(KZERO, 0, &pte, nil)) >= 0)
6099ef1f84bSDavid du Colombier 		print("l %d %#p %llux\n", l, pte, *pte);
6109ef1f84bSDavid du Colombier 
6119ef1f84bSDavid du Colombier 	mmuphysaddr(PTR2UINT(end));
6129ef1f84bSDavid du Colombier }
6131f646f47SDavid du Colombier 
6140d74731bSDavid du Colombier void
mmucachectl(Page * p,uint why)6150d74731bSDavid du Colombier mmucachectl(Page *p, uint why)
6160d74731bSDavid du Colombier {
6170d74731bSDavid du Colombier 	if(!pagedout(p))
6180d74731bSDavid du Colombier 		memset(p->cachectl, why, sizeof(p->cachectl));
6190d74731bSDavid du Colombier }
6200d74731bSDavid du Colombier 
6211f646f47SDavid du Colombier /*
6221f646f47SDavid du Colombier  * Double-check the user MMU.
6231f646f47SDavid du Colombier  * Error checking only.
6241f646f47SDavid du Colombier  */
6251f646f47SDavid du Colombier void
checkmmu(uintptr va,uintmem pa)6261f646f47SDavid du Colombier checkmmu(uintptr va, uintmem pa)
6271f646f47SDavid du Colombier {
6281f646f47SDavid du Colombier 	uintmem mpa;
6291f646f47SDavid du Colombier 
6301f646f47SDavid du Colombier 	mpa = mmuphysaddr(va);
6311f646f47SDavid du Colombier 	if(mpa != ~(uintmem)0 && mpa != pa)
6321f646f47SDavid du Colombier 		print("%d %s: va=%#p pa=%#P mmupa=%#P\n",
6331f646f47SDavid du Colombier 			up->pid, up->text, va, pa, mpa);
6341f646f47SDavid du Colombier }
635