Subject: [PATCH] powerpc/64s: ISAv3 initialize MMU registers before setting partition table

From: Nicholas Piggin <[email-masked]>
Date: Fri, 1 Dec 2017 14:14:48 +1000

kexec can leave MMU registers set, PIDR in particular, when booting the
new kernel. The boot sequence does not zero PIDR ever, and it only gets
changed as CPUs first switch to userspace processes. This can leave a
window where speculative accesses from a CPU to quadrant 0 can pick up
translations in the partition table that may be set up for processes
running on other CPUs. These cached translations will not be involved in
the invalidation protocol, so we can end with stable TLB and PWC.

This can cause the kernel to hang in infinite page fault loops (usually
in schedule_tail, which is usually the first quardant 0 access to a new
PID.

Fix this by zeroing out PIDR before setting PTCR.

Signed-off-by: Nicholas Piggin <[email-masked]>
---
 arch/powerpc/mm/hash_utils_64.c |  6 ++++++
 arch/powerpc/mm/pgtable-radix.c | 15 +++++++++++++++
 2 files changed, 21 insertions(+)

diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c
index 655a5a9a183d..9176e1dda8e0 100644
--- a/arch/powerpc/mm/hash_utils_64.c
+++ b/arch/powerpc/mm/hash_utils_64.c
@@ -1029,6 +1029,9 @@ void __init hash__early_init_mmu(void)
 	pci_io_base = ISA_IO_BASE;
 #endif
 
+	if (cpu_has_feature(CPU_FTR_ARCH_300))
+		mtspr(SPRN_PID, 0);
+
 	/* Select appropriate backend */
 	if (firmware_has_feature(FW_FEATURE_PS3_LV1))
 		ps3_early_mm_init();
@@ -1055,6 +1058,9 @@ void __init hash__early_init_mmu(void)
 void hash__early_init_mmu_secondary(void)
 {
 	/* Initialize hash table for that CPU */
+	if (cpu_has_feature(CPU_FTR_ARCH_300))
+		mtspr(SPRN_PID, 0);
+
 	if (!firmware_has_feature(FW_FEATURE_LPAR)) {
 
 		if (cpu_has_feature(CPU_FTR_POWER9_DD1))
diff --git a/arch/powerpc/mm/pgtable-radix.c b/arch/powerpc/mm/pgtable-radix.c
index cfbbee941a76..88430acf4b85 100644
--- a/arch/powerpc/mm/pgtable-radix.c
+++ b/arch/powerpc/mm/pgtable-radix.c
@@ -563,12 +563,25 @@ void __init radix__early_init_mmu(void)
 	__pte_frag_nr = H_PTE_FRAG_NR;
 	__pte_frag_size_shift = H_PTE_FRAG_SIZE_SHIFT;
 
+	/*
+	 * kexec kernels can boot into the new kernel with PIDR non-zero.
+	 *
+	 * PIDR should be zeroed before we set the partition table,
+	 * to ensure no speculative TLB fill goes off to non-zero PIDs.
+	 * This shouldn't be a problem in real-mode, but I have not
+	 * found definitively in the ISA.
+	 *
+	 * We do need to ensure all CPUs have PID=0 when the MMU
+	 * is turned on.
+	 */
+	mtspr(SPRN_PID, 0);
 	if (!firmware_has_feature(FW_FEATURE_LPAR)) {
 		radix_init_native();
 		if (cpu_has_feature(CPU_FTR_POWER9_DD1))
 			update_hid_for_radix();
 		lpcr = mfspr(SPRN_LPCR);
 		mtspr(SPRN_LPCR, lpcr | LPCR_UPRT | LPCR_HR);
+		mtspr(SPRN_LPID, 0);
 		radix_init_partition_table();
 		radix_init_amor();
 	} else {
@@ -587,6 +600,7 @@ void radix__early_init_mmu_secondary(void)
 	/*
 	 * update partition table control register and UPRT
 	 */
+	mtspr(SPRN_PID, 0);
 	if (!firmware_has_feature(FW_FEATURE_LPAR)) {
 
 		if (cpu_has_feature(CPU_FTR_POWER9_DD1))
@@ -595,6 +609,7 @@ void radix__early_init_mmu_secondary(void)
 		lpcr = mfspr(SPRN_LPCR);
 		mtspr(SPRN_LPCR, lpcr | LPCR_UPRT | LPCR_HR);
 
+		mtspr(SPRN_LPID, 0);
 		mtspr(SPRN_PTCR,
 		      __pa(partition_tb) | (PATB_SIZE_SHIFT - 12));
 		radix_init_amor();
-- 
2.15.0