Skip to content

Commit 9f048fa

Browse files
maciej-w-rozyckitsbogend
authored andcommitted
MIPS: mm: Prevent a TLB shutdown on initial uniquification
Depending on the particular CPU implementation a TLB shutdown may occur if multiple matching entries are detected upon the execution of a TLBP or the TLBWI/TLBWR instructions. Given that we don't know what entries we have been handed we need to be very careful with the initial TLB setup and avoid all these instructions. Therefore read all the TLB entries one by one with the TLBR instruction, bypassing the content addressing logic, and truncate any large pages in place so as to avoid a case in the second step where an incoming entry for a large page at a lower address overlaps with a replacement entry chosen at another index. Then preinitialize the TLB using addresses outside our usual unique range and avoiding clashes with any entries received, before making the usual call to local_flush_tlb_all(). This fixes (at least) R4x00 cores if TLBP hits multiple matching TLB entries (SGI IP22 PROM for examples sets up all TLBs to the same virtual address). Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk> Fixes: 35ad7e1 ("MIPS: mm: tlb-r4k: Uniquify TLB entries on init") Cc: stable@vger.kernel.org Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Tested-by: Jiaxun Yang <jiaxun.yang@flygoat.com> # Boston I6400, M5150 sim Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
1 parent 09782e7 commit 9f048fa

File tree

1 file changed

+63
-37
lines changed

1 file changed

+63
-37
lines changed

arch/mips/mm/tlb-r4k.c

Lines changed: 63 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <linux/mm.h>
1616
#include <linux/hugetlb.h>
1717
#include <linux/export.h>
18+
#include <linux/sort.h>
1819

1920
#include <asm/cpu.h>
2021
#include <asm/cpu-type.h>
@@ -508,54 +509,78 @@ static int __init set_ntlb(char *str)
508509

509510
__setup("ntlb=", set_ntlb);
510511

511-
/* Initialise all TLB entries with unique values */
512+
513+
/* Comparison function for EntryHi VPN fields. */
514+
static int r4k_vpn_cmp(const void *a, const void *b)
515+
{
516+
long v = *(unsigned long *)a - *(unsigned long *)b;
517+
int s = sizeof(long) > sizeof(int) ? sizeof(long) * 8 - 1: 0;
518+
return s ? (v != 0) | v >> s : v;
519+
}
520+
521+
/*
522+
* Initialise all TLB entries with unique values that do not clash with
523+
* what we have been handed over and what we'll be using ourselves.
524+
*/
512525
static void r4k_tlb_uniquify(void)
513526
{
514-
int entry = num_wired_entries();
527+
unsigned long tlb_vpns[1 << MIPS_CONF1_TLBS_SIZE];
528+
int tlbsize = current_cpu_data.tlbsize;
529+
int start = num_wired_entries();
530+
unsigned long vpn_mask;
531+
int cnt, ent, idx, i;
532+
533+
vpn_mask = GENMASK(cpu_vmbits - 1, 13);
534+
vpn_mask |= IS_ENABLED(CONFIG_64BIT) ? 3ULL << 62 : 1 << 31;
515535

516536
htw_stop();
537+
538+
for (i = start, cnt = 0; i < tlbsize; i++, cnt++) {
539+
unsigned long vpn;
540+
541+
write_c0_index(i);
542+
mtc0_tlbr_hazard();
543+
tlb_read();
544+
tlb_read_hazard();
545+
vpn = read_c0_entryhi();
546+
vpn &= vpn_mask & PAGE_MASK;
547+
tlb_vpns[cnt] = vpn;
548+
549+
/* Prevent any large pages from overlapping regular ones. */
550+
write_c0_pagemask(read_c0_pagemask() & PM_DEFAULT_MASK);
551+
mtc0_tlbw_hazard();
552+
tlb_write_indexed();
553+
tlbw_use_hazard();
554+
}
555+
556+
sort(tlb_vpns, cnt, sizeof(tlb_vpns[0]), r4k_vpn_cmp, NULL);
557+
558+
write_c0_pagemask(PM_DEFAULT_MASK);
517559
write_c0_entrylo0(0);
518560
write_c0_entrylo1(0);
519561

520-
while (entry < current_cpu_data.tlbsize) {
521-
unsigned long asid_mask = cpu_asid_mask(&current_cpu_data);
522-
unsigned long asid = 0;
523-
int idx;
562+
idx = 0;
563+
ent = tlbsize;
564+
for (i = start; i < tlbsize; i++)
565+
while (1) {
566+
unsigned long entryhi, vpn;
524567

525-
/* Skip wired MMID to make ginvt_mmid work */
526-
if (cpu_has_mmid)
527-
asid = MMID_KERNEL_WIRED + 1;
568+
entryhi = UNIQUE_ENTRYHI(ent);
569+
vpn = entryhi & vpn_mask & PAGE_MASK;
528570

529-
/* Check for match before using UNIQUE_ENTRYHI */
530-
do {
531-
if (cpu_has_mmid) {
532-
write_c0_memorymapid(asid);
533-
write_c0_entryhi(UNIQUE_ENTRYHI(entry));
571+
if (idx >= cnt || vpn < tlb_vpns[idx]) {
572+
write_c0_entryhi(entryhi);
573+
write_c0_index(i);
574+
mtc0_tlbw_hazard();
575+
tlb_write_indexed();
576+
ent++;
577+
break;
578+
} else if (vpn == tlb_vpns[idx]) {
579+
ent++;
534580
} else {
535-
write_c0_entryhi(UNIQUE_ENTRYHI(entry) | asid);
581+
idx++;
536582
}
537-
mtc0_tlbw_hazard();
538-
tlb_probe();
539-
tlb_probe_hazard();
540-
idx = read_c0_index();
541-
/* No match or match is on current entry */
542-
if (idx < 0 || idx == entry)
543-
break;
544-
/*
545-
* If we hit a match, we need to try again with
546-
* a different ASID.
547-
*/
548-
asid++;
549-
} while (asid < asid_mask);
550-
551-
if (idx >= 0 && idx != entry)
552-
panic("Unable to uniquify TLB entry %d", idx);
553-
554-
write_c0_index(entry);
555-
mtc0_tlbw_hazard();
556-
tlb_write_indexed();
557-
entry++;
558-
}
583+
}
559584

560585
tlbw_use_hazard();
561586
htw_start();
@@ -602,6 +627,7 @@ static void r4k_tlb_configure(void)
602627

603628
/* From this point on the ARC firmware is dead. */
604629
r4k_tlb_uniquify();
630+
local_flush_tlb_all();
605631

606632
/* Did I tell you that ARC SUCKS? */
607633
}

0 commit comments

Comments
 (0)