]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
MIPS: mm: tlb-r4k: Uniquify TLB entries on init
authorJiaxun Yang <jiaxun.yang@flygoat.com>
Sat, 7 Jun 2025 12:43:56 +0000 (13:43 +0100)
committerThomas Bogendoerfer <tsbogend@alpha.franken.de>
Wed, 16 Jul 2025 18:01:07 +0000 (20:01 +0200)
Hardware or bootloader will initialize TLB entries to any value, which
may collide with kernel's UNIQUE_ENTRYHI value. On MIPS microAptiv/M5150
family of cores this will trigger machine check exception and cause boot
failure. On M5150 simulation this could happen 7 times out of 1000 boots.

Replace local_flush_tlb_all() with r4k_tlb_uniquify() which probes each
TLB ENTRIHI unique value for collisions before it's written, and in case
of collision try a different ASID.

Cc: stable@kernel.org
Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
arch/mips/mm/tlb-r4k.c

index 76f3b9c0a9f0ce60c42e4a9ea8025e1283678bd1..347126dc010dd59904820d9d9e34cdeeb011832f 100644 (file)
@@ -508,6 +508,60 @@ static int __init set_ntlb(char *str)
 
 __setup("ntlb=", set_ntlb);
 
+/* Initialise all TLB entries with unique values */
+static void r4k_tlb_uniquify(void)
+{
+       int entry = num_wired_entries();
+
+       htw_stop();
+       write_c0_entrylo0(0);
+       write_c0_entrylo1(0);
+
+       while (entry < current_cpu_data.tlbsize) {
+               unsigned long asid_mask = cpu_asid_mask(&current_cpu_data);
+               unsigned long asid = 0;
+               int idx;
+
+               /* Skip wired MMID to make ginvt_mmid work */
+               if (cpu_has_mmid)
+                       asid = MMID_KERNEL_WIRED + 1;
+
+               /* Check for match before using UNIQUE_ENTRYHI */
+               do {
+                       if (cpu_has_mmid) {
+                               write_c0_memorymapid(asid);
+                               write_c0_entryhi(UNIQUE_ENTRYHI(entry));
+                       } else {
+                               write_c0_entryhi(UNIQUE_ENTRYHI(entry) | asid);
+                       }
+                       mtc0_tlbw_hazard();
+                       tlb_probe();
+                       tlb_probe_hazard();
+                       idx = read_c0_index();
+                       /* No match or match is on current entry */
+                       if (idx < 0 || idx == entry)
+                               break;
+                       /*
+                        * If we hit a match, we need to try again with
+                        * a different ASID.
+                        */
+                       asid++;
+               } while (asid < asid_mask);
+
+               if (idx >= 0 && idx != entry)
+                       panic("Unable to uniquify TLB entry %d", idx);
+
+               write_c0_index(entry);
+               mtc0_tlbw_hazard();
+               tlb_write_indexed();
+               entry++;
+       }
+
+       tlbw_use_hazard();
+       htw_start();
+       flush_micro_tlb();
+}
+
 /*
  * Configure TLB (for init or after a CPU has been powered off).
  */
@@ -547,7 +601,7 @@ static void r4k_tlb_configure(void)
        temp_tlb_entry = current_cpu_data.tlbsize - 1;
 
        /* From this point on the ARC firmware is dead.  */
-       local_flush_tlb_all();
+       r4k_tlb_uniquify();
 
        /* Did I tell you that ARC SUCKS?  */
 }