Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

nios2: do not introduce conflicting mappings when flushing tlb entries

The NIOS2 hardware does not support conflicting mappings for the same
virtual address (see Nios II Processor Reference Guide from 2023.08.28):

"The operating system software is responsible for guaranteeing that
multiple TLB entries do not map the same virtual address. The hardware
behavior is undefined when multiple entries map the same virtual
address."

When flushing tlb-entries, the kernel may violate this invariant for
virtual addresses related to PID 0 as flushing is currently implemented
by assigning physical address, pid and flags to 0.

A small example:

Before flushing TLB mappings for pid:0x42:
dump tlb-entries for line=0xd (addr 000d0000):
-- way:09 vpn:0x0006d000 phys:0x01145000 pid:0x00 flags:rw--c
...
-- way:0d vpn:0x0006d000 phys:0x02020000 pid:0x42 flags:rw--c

After flushing TLB mappings for pid:0x42:
dump tlb-entries for line=0xd (addr 000d0000):
-- way:09 vpn:0x0006d000 phys:0x01145000 pid:0x00 flags:rw--c
...
-- way:0d vpn:0x0006d000 phys:0x00000000 pid:0x00 flags:-----

As functions such as replace_tlb_one_pid operate on the assumption of
unique mappings, this can cause repeated pagefaults for a single
address that are not covered by the existing spurious pagefault handling.

This commit fixes this issue by keeping the pid field of the entries
when flushing. That way, no conflicting mappings are introduced as the
pair of <address,pid> is now kept unique:

Fixed example after flushing TLB mappings for pid:0x42:
dump tlb-entries for line=0xd (addr 000d0000):
-- way:09 vpn:0x0006d000 phys:0x01145000 pid:0x00 flags:rw--c
...
-- way:0d vpn:0x0006d000 phys:0x00000000 pid:0x42 flags:-----

When flushing the complete tlb/initialising all entries, the way is
used as a substitute mmu pid value for the "invalid" entries.

Signed-off-by: Simon Schuster <schuster.simon@siemens-energy.com>
Signed-off-by: Andreas Oetken <andreas.oetken@siemens-energy.com>
Signed-off-by: Dinh Nguyen <dinguyen@kernel.org>

authored by

Simon Schuster and committed by
Dinh Nguyen
83ab5983 2d8a3179

+11 -7
+11 -7
arch/nios2/mm/tlb.c
··· 144 144 if (((pteaddr >> 2) & 0xfffff) != (addr >> PAGE_SHIFT)) 145 145 continue; 146 146 147 + tlbmisc = RDCTL(CTL_TLBMISC); 147 148 pr_debug("Flush entry by writing way=%dl pid=%ld\n", 148 - way, (pid_misc >> TLBMISC_PID_SHIFT)); 149 + way, ((tlbmisc >> TLBMISC_PID_SHIFT) & TLBMISC_PID_MASK)); 149 150 150 - tlbmisc = TLBMISC_WE | (way << TLBMISC_WAY_SHIFT); 151 + tlbmisc = TLBMISC_WE | (way << TLBMISC_WAY_SHIFT) | (tlbmisc & TLBMISC_PID); 151 152 WRCTL(CTL_TLBMISC, tlbmisc); 152 153 WRCTL(CTL_PTEADDR, pteaddr_invalid(addr)); 153 154 WRCTL(CTL_TLBACC, 0); ··· 238 237 if (pid != mmu_pid) 239 238 continue; 240 239 241 - tlbmisc = TLBMISC_WE | (way << TLBMISC_WAY_SHIFT); 240 + tlbmisc = TLBMISC_WE | (way << TLBMISC_WAY_SHIFT) | 241 + (pid << TLBMISC_PID_SHIFT); 242 242 WRCTL(CTL_TLBMISC, tlbmisc); 243 243 WRCTL(CTL_TLBACC, 0); 244 244 } ··· 274 272 /* remember pid/way until we return */ 275 273 get_misc_and_pid(&org_misc, &pid_misc); 276 274 277 - /* Start at way 0, way is auto-incremented after each TLBACC write */ 278 - WRCTL(CTL_TLBMISC, TLBMISC_WE); 279 - 280 275 /* Map each TLB entry to physcal address 0 with no-access and a 281 276 bad ptbase */ 282 277 for (line = 0; line < cpuinfo.tlb_num_lines; line++) { 283 278 WRCTL(CTL_PTEADDR, pteaddr_invalid(addr)); 284 - for (way = 0; way < cpuinfo.tlb_num_ways; way++) 279 + for (way = 0; way < cpuinfo.tlb_num_ways; way++) { 280 + // Code such as replace_tlb_one_pid assumes that no duplicate entries exist 281 + // for a single address across ways, so also use way as a dummy PID 282 + WRCTL(CTL_TLBMISC, TLBMISC_WE | (way << TLBMISC_WAY_SHIFT) | 283 + (way << TLBMISC_PID_SHIFT)); 285 284 WRCTL(CTL_TLBACC, 0); 285 + } 286 286 287 287 addr += PAGE_SIZE; 288 288 }