git: 9front

Download patch

ref: dcd6920bda655b7c1bf8245db3a60589afe848ab
parent: 2e6fb00af4fae7afd8d6bc9767b89ff0da5670dd
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sat Nov 1 22:09:51 EDT 2025

pc64: make vmap() work beyond 1TB (thanks noam)

In some qemu configurations, PCI bars are mapped beyond
1TB physical, which exceeds our VMAPSIZE offset map.

When this happens, allocate use some unbacked physical
addresses (UPA) that will fit into VMAP instead.

This means pmap() now needs to be more carefull and
take the physical address alignment into account when
picking 4KB or 2MB pages.

Thanks to noam for testing.

--- a/sys/src/9/pc64/mmu.c
+++ b/sys/src/9/pc64/mmu.c
@@ -355,7 +355,7 @@
 	uintptr *pte, *ptee, flags;
 	int z, l;
 
-	if(size <= 0 || va < VMAP)
+	if(size <= 0 || va < VMAP || (va % BY2PG) != 0)
 		panic("pmap: pa=%#p va=%#p size=%lld", pa, va, size);
 	flags = pa;
 	pa = PPN(pa);
@@ -364,23 +364,32 @@
 	if(va >= KZERO)
 		flags |= PTEGLOBAL;
 	while(size > 0){
-		if(size >= PGLSZ(1) && (va % PGLSZ(1)) == 0)
-			flags |= PTESIZE;
-		l = (flags & PTESIZE) != 0;
-		z = PGLSZ(l);
-		pte = mmuwalk(m->pml4, va, l, 1);
+		l = 0;
+		pte = nil;
+		if(size >= PGLSZ(1) && ((va|pa) % PGLSZ(1)) == 0
+		&& (pte = mmuwalk(m->pml4, va, l, 0)) == nil)
+			l++;
+		if(pte == nil)
+			pte = mmuwalk(m->pml4, va, l, 1);
 		if(pte == nil){
-			pte = mmuwalk(m->pml4, va, ++l, 0);
-			if(pte && (*pte & PTESIZE)){
-				flags |= PTESIZE;
-				z = va & (PGLSZ(l)-1);
-				va -= z;
-				pa -= z;
-				size += z;
-				continue;
+			if(l == 0 && ((va^pa) % PGLSZ(1)) == 0){
+				pte = mmuwalk(m->pml4, va, ++l, 0);
+				if(pte && (*pte & PTESIZE)){
+					z = va & (PGLSZ(1)-1);
+					va -= z;
+					pa -= z;
+					size += z;
+					goto Create;
+				}
 			}
 			panic("pmap: pa=%#p va=%#p size=%lld", pa, va, size);
 		}
+Create:
+		z = PGLSZ(l);
+		if(l == 0)
+			flags &= ~PTESIZE;
+		else
+			flags |= PTESIZE;
 		ptee = pte + ptecount(va, l);
 		while(size > 0 && pte < ptee){
 			*pte++ = pa | flags;
@@ -607,11 +616,11 @@
 	uintptr va;
 	int o;
 
-	if(pa < BY2PG || size <= 0 || -pa < size || pa+size > VMAPSIZE){
+	if(pa < BY2PG || size <= 0 || -pa < size){
+Nope:
 		print("vmap pa=%llux size=%lld pc=%#p\n", pa, size, getcallerpc(&pa));
 		return nil;
 	}
-	va = pa+VMAP;
 
 	/*
 	 * might be asking for less than a page.
@@ -618,8 +627,17 @@
 	 */
 	o = pa & (BY2PG-1);
 	pa -= o;
-	va -= o;
 	size += o;
+
+	if(pa+size > VMAPSIZE){
+		va = upaalloc(-1ULL, size, PGLSZ(size>=PGLSZ(1)&&(pa%PGLSZ(1))==0));
+		if(va == -1ULL)
+			goto Nope;
+		va += VMAP;
+		punmap(va, size);
+	} else {
+		va = VMAP+pa;
+	}
 	pmap(pa | PTEUNCACHED|PTEWRITE|PTENOEXEC|PTEVALID, va, size);
 	return (void*)(va+o);
 }
--