git: 76384bd10fdb - main - powerpc64: fix OFWFB with Radix MMU

From: Leandro Lupori <luporl_at_FreeBSD.org>
Date: Thu, 14 Oct 2021 13:46:23 UTC
The branch main has been updated by luporl:

URL: https://cgit.FreeBSD.org/src/commit/?id=76384bd10fdbb97be2803d969905f15a84255d6a

commit 76384bd10fdbb97be2803d969905f15a84255d6a
Author:     Leandro Lupori <luporl@FreeBSD.org>
AuthorDate: 2021-10-14 13:39:52 +0000
Commit:     Leandro Lupori <luporl@FreeBSD.org>
CommitDate: 2021-10-14 13:39:52 +0000

    powerpc64: fix OFWFB with Radix MMU
    
    Current implementation of Radix MMU doesn't support mapping
    arbitrary virtual addresses, such as the ones generated by
    "direct mapping" I/O addresses. This caused the system to hang, when
    early I/O addresses, such as those used by OpenFirmware Frame Buffer,
    were remapped after the MMU was up.
    
    To avoid having to modify mmu_radix_kenter_attr just to support this
    use case, this change makes early I/O map use virtual addresses from
    KVA area instead (similar to what mmu_radix_mapdev_attr does), as
    these can be safely remapped later.
    
    Reviewed by:            alfredo (earlier version), jhibbits (in irc)
    MFC after:              2 weeks
    Sponsored by:           Instituto de Pesquisas Eldorado (eldorado.org.br)
    Differential Revision:  https://reviews.freebsd.org/D31232
---
 sys/powerpc/aim/aim_machdep.c | 42 +++++++++++++++++++++++++++++++++---------
 sys/powerpc/aim/mmu_radix.c   |  8 +-------
 sys/powerpc/include/pmap.h    |  3 +++
 sys/powerpc/powerpc/machdep.c |  9 +++++++++
 4 files changed, 46 insertions(+), 16 deletions(-)

diff --git a/sys/powerpc/aim/aim_machdep.c b/sys/powerpc/aim/aim_machdep.c
index d582489d9f7e..914296440104 100644
--- a/sys/powerpc/aim/aim_machdep.c
+++ b/sys/powerpc/aim/aim_machdep.c
@@ -476,14 +476,9 @@ aim_cpu_init(vm_offset_t toc)
 	 * in case the platform module had a better idea of what we
 	 * should do.
 	 */
-	if (cpu_features2 & PPC_FEATURE2_ARCH_3_00) {
-		radix_mmu = 0;
-		TUNABLE_INT_FETCH("radix_mmu", &radix_mmu);
-		if (radix_mmu)
-			pmap_mmu_install(MMU_TYPE_RADIX, BUS_PROBE_GENERIC);
-		else
-			pmap_mmu_install(MMU_TYPE_G5, BUS_PROBE_GENERIC);
-	} else if (cpu_features & PPC_FEATURE_64)
+	if (radix_mmu)
+		pmap_mmu_install(MMU_TYPE_RADIX, BUS_PROBE_GENERIC);
+	else if (cpu_features & PPC_FEATURE_64)
 		pmap_mmu_install(MMU_TYPE_G5, BUS_PROBE_GENERIC);
 	else
 		pmap_mmu_install(MMU_TYPE_OEA, BUS_PROBE_GENERIC);
@@ -586,6 +581,25 @@ va_to_vsid(pmap_t pm, vm_offset_t va)
 
 #endif
 
+void
+pmap_early_io_map_init(void)
+{
+	if ((cpu_features2 & PPC_FEATURE2_ARCH_3_00) == 0)
+		radix_mmu = 0;
+	else
+		TUNABLE_INT_FETCH("radix_mmu", &radix_mmu);
+
+	/*
+	 * When using Radix, set the start and end of kva early, to be able to
+	 * use KVAs on pmap_early_io_map and avoid issues when remapping them
+	 * later.
+	 */
+	if (radix_mmu) {
+		virtual_avail = VM_MIN_KERNEL_ADDRESS;
+		virtual_end = VM_MAX_SAFE_KERNEL_ADDRESS;
+	}
+}
+
 /*
  * These functions need to provide addresses that both (a) work in real mode
  * (or whatever mode/circumstances the kernel is in in early boot (now)) and
@@ -601,10 +615,20 @@ pmap_early_io_map(vm_paddr_t pa, vm_size_t size)
 	 * If we have the MMU up in early boot, assume it is 1:1. Otherwise,
 	 * try to get the address in a memory region compatible with the
 	 * direct map for efficiency later.
+	 * Except for Radix MMU, for which current implementation doesn't
+	 * support mapping arbitrary virtual addresses, such as the ones
+	 * generated by "direct mapping" I/O addresses. In this case, use
+	 * addresses from KVA area.
 	 */
 	if (mfmsr() & PSL_DR)
 		return (pa);
-	else
+	else if (radix_mmu) {
+		vm_offset_t va;
+
+		va = virtual_avail;
+		virtual_avail += round_page(size + pa - trunc_page(pa));
+		return (va);
+	} else
 		return (DMAP_BASE_ADDRESS + pa);
 }
 
diff --git a/sys/powerpc/aim/mmu_radix.c b/sys/powerpc/aim/mmu_radix.c
index 50c658e58a4a..ca1a4d2f4797 100644
--- a/sys/powerpc/aim/mmu_radix.c
+++ b/sys/powerpc/aim/mmu_radix.c
@@ -907,7 +907,7 @@ kvtopte(vm_offset_t va)
 	pt_entry_t *l3e;
 
 	l3e = pmap_pml3e(kernel_pmap, va);
-	if ((be64toh(*l3e) & RPTE_VALID) == 0)
+	if (l3e == NULL || (be64toh(*l3e) & RPTE_VALID) == 0)
 		return (NULL);
 	return (pmap_l3e_to_pte(l3e, va));
 }
@@ -2071,12 +2071,6 @@ mmu_radix_late_bootstrap(vm_offset_t start, vm_offset_t end)
 	for (i = 0; phys_avail[i + 2] != 0; i += 2)
 		Maxmem = MAX(Maxmem, powerpc_btop(phys_avail[i + 1]));
 
-	/*
-	 * Set the start and end of kva.
-	 */
-	virtual_avail = VM_MIN_KERNEL_ADDRESS;
-	virtual_end = VM_MAX_SAFE_KERNEL_ADDRESS;
-
 	/*
 	 * Remap any early IO mappings (console framebuffer, etc.)
 	 */
diff --git a/sys/powerpc/include/pmap.h b/sys/powerpc/include/pmap.h
index 2f1886a27093..d14398750080 100644
--- a/sys/powerpc/include/pmap.h
+++ b/sys/powerpc/include/pmap.h
@@ -340,6 +340,9 @@ extern	int pmap_bootstrapped;
 extern	int radix_mmu;
 extern	int superpages_enabled;
 
+#ifdef AIM
+void pmap_early_io_map_init(void);
+#endif
 vm_offset_t pmap_early_io_map(vm_paddr_t pa, vm_size_t size);
 void pmap_early_io_unmap(vm_offset_t va, vm_size_t size);
 void pmap_track_page(pmap_t pmap, vm_offset_t va);
diff --git a/sys/powerpc/powerpc/machdep.c b/sys/powerpc/powerpc/machdep.c
index 622af17b3305..2dc9a3e9d549 100644
--- a/sys/powerpc/powerpc/machdep.c
+++ b/sys/powerpc/powerpc/machdep.c
@@ -419,6 +419,15 @@ powerpc_init(vm_offset_t fdt, vm_offset_t toc, vm_offset_t ofentry, void *mdp,
 	if (ofw_bootargs)
 		ofw_parse_bootargs();
 
+#ifdef AIM
+	/*
+	 * Early I/O map needs to be initialized before console, in order to
+	 * map frame buffers properly, and after boot args have been parsed,
+	 * to handle tunables properly.
+	 */
+	pmap_early_io_map_init();
+#endif
+
 	/*
 	 * Initialize the console before printing anything.
 	 */