From 2d8a3179ea035f9341b6a73e5ba4029fc67e983d Mon Sep 17 00:00:00 2001 From: Simon Schuster Date: Thu, 27 Mar 2025 14:54:22 +0100 Subject: nios2: force update_mmu_cache on spurious tlb-permission--related pagefaults NIOS2 uses a software-managed TLB for virtual address translation. To flush a cache line, the original mapping is replaced by one to physical address 0x0 with no permissions (rwx mapped to 0) set. This can lead to TLB-permission--related traps when such a nominally flushed entry is encountered as a mapping for an otherwise valid virtual address within a process (e.g. due to an MMU-PID-namespace rollover that previously flushed the complete TLB including entries of existing, running processes). The default ptep_set_access_flags implementation from mm/pgtable-generic.c only forces a TLB-update when the page-table entry has changed within the page table: /* * [...] We return whether the PTE actually changed, which in turn * instructs the caller to do things like update__mmu_cache. [...] */ int ptep_set_access_flags(struct vm_area_struct *vma, unsigned long address, pte_t *ptep, pte_t entry, int dirty) { int changed = !pte_same(*ptep, entry); if (changed) { set_pte_at(vma->vm_mm, address, ptep, entry); flush_tlb_fix_spurious_fault(vma, address); } return changed; } However, no cross-referencing with the TLB-state occurs, so the flushing-induced pseudo entries that are responsible for the pagefault in the first place are never pre-empted from TLB on this code path. This commit fixes this behaviour by always requesting a TLB-update in this part of the pagefault handling, fixing spurious page-faults on the way. The handling is a straightforward port of the logic from the MIPS architecture via an arch-specific ptep_set_access_flags function ported from arch/mips/include/asm/pgtable.h. Signed-off-by: Simon Schuster Signed-off-by: Andreas Oetken Signed-off-by: Dinh Nguyen --- arch/nios2/include/asm/pgtable.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'arch') diff --git a/arch/nios2/include/asm/pgtable.h b/arch/nios2/include/asm/pgtable.h index eab87c6beacb..e5d64c84aadf 100644 --- a/arch/nios2/include/asm/pgtable.h +++ b/arch/nios2/include/asm/pgtable.h @@ -291,4 +291,20 @@ void update_mmu_cache_range(struct vm_fault *vmf, struct vm_area_struct *vma, #define update_mmu_cache(vma, addr, ptep) \ update_mmu_cache_range(NULL, vma, addr, ptep, 1) +static inline int pte_same(pte_t pte_a, pte_t pte_b); + +#define __HAVE_ARCH_PTEP_SET_ACCESS_FLAGS +static inline int ptep_set_access_flags(struct vm_area_struct *vma, + unsigned long address, pte_t *ptep, + pte_t entry, int dirty) +{ + if (!pte_same(*ptep, entry)) + set_ptes(vma->vm_mm, address, ptep, entry, 1); + /* + * update_mmu_cache will unconditionally execute, handling both + * the case that the PTE changed and the spurious fault case. + */ + return true; +} + #endif /* _ASM_NIOS2_PGTABLE_H */ -- cgit v1.2.3-59-g8ed1b From 83ab598362fb3a30cf2789f7482878084ff8efaa Mon Sep 17 00:00:00 2001 From: Simon Schuster Date: Thu, 27 Mar 2025 14:56:24 +0100 Subject: 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 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 Signed-off-by: Andreas Oetken Signed-off-by: Dinh Nguyen --- arch/nios2/mm/tlb.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'arch') diff --git a/arch/nios2/mm/tlb.c b/arch/nios2/mm/tlb.c index f90ac35f05f3..a9cbe20f9e79 100644 --- a/arch/nios2/mm/tlb.c +++ b/arch/nios2/mm/tlb.c @@ -144,10 +144,11 @@ static void flush_tlb_one(unsigned long addr) if (((pteaddr >> 2) & 0xfffff) != (addr >> PAGE_SHIFT)) continue; + tlbmisc = RDCTL(CTL_TLBMISC); pr_debug("Flush entry by writing way=%dl pid=%ld\n", - way, (pid_misc >> TLBMISC_PID_SHIFT)); + way, ((tlbmisc >> TLBMISC_PID_SHIFT) & TLBMISC_PID_MASK)); - tlbmisc = TLBMISC_WE | (way << TLBMISC_WAY_SHIFT); + tlbmisc = TLBMISC_WE | (way << TLBMISC_WAY_SHIFT) | (tlbmisc & TLBMISC_PID); WRCTL(CTL_TLBMISC, tlbmisc); WRCTL(CTL_PTEADDR, pteaddr_invalid(addr)); WRCTL(CTL_TLBACC, 0); @@ -237,7 +238,8 @@ void flush_tlb_pid(unsigned long mmu_pid) if (pid != mmu_pid) continue; - tlbmisc = TLBMISC_WE | (way << TLBMISC_WAY_SHIFT); + tlbmisc = TLBMISC_WE | (way << TLBMISC_WAY_SHIFT) | + (pid << TLBMISC_PID_SHIFT); WRCTL(CTL_TLBMISC, tlbmisc); WRCTL(CTL_TLBACC, 0); } @@ -272,15 +274,17 @@ void flush_tlb_all(void) /* remember pid/way until we return */ get_misc_and_pid(&org_misc, &pid_misc); - /* Start at way 0, way is auto-incremented after each TLBACC write */ - WRCTL(CTL_TLBMISC, TLBMISC_WE); - /* Map each TLB entry to physcal address 0 with no-access and a bad ptbase */ for (line = 0; line < cpuinfo.tlb_num_lines; line++) { WRCTL(CTL_PTEADDR, pteaddr_invalid(addr)); - for (way = 0; way < cpuinfo.tlb_num_ways; way++) + for (way = 0; way < cpuinfo.tlb_num_ways; way++) { + // Code such as replace_tlb_one_pid assumes that no duplicate entries exist + // for a single address across ways, so also use way as a dummy PID + WRCTL(CTL_TLBMISC, TLBMISC_WE | (way << TLBMISC_WAY_SHIFT) | + (way << TLBMISC_PID_SHIFT)); WRCTL(CTL_TLBACC, 0); + } addr += PAGE_SIZE; } -- cgit v1.2.3-59-g8ed1b From aa264d9511aa5befa28cf8d9f32fce78fcf1a773 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Wed, 16 Apr 2025 21:09:07 +0200 Subject: nios2: Replace strcpy() with strscpy() and simplify setup_cpuinfo() strcpy() is deprecated; use strscpy() instead. Since the destination buffer has a fixed length, strscpy() automatically determines its size using sizeof() when the size argument is omitted. This makes the explicit size argument unnecessary - remove it. Now, combine both if-else branches using strscpy() and the same buffer into a single statement to simplify the code. No functional changes intended. Link: https://github.com/KSPP/linux/issues/88 Cc: linux-hardening@vger.kernel.org Signed-off-by: Thorsten Blum Reviewed-by: Kees Cook Signed-off-by: Dinh Nguyen --- arch/nios2/kernel/cpuinfo.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'arch') diff --git a/arch/nios2/kernel/cpuinfo.c b/arch/nios2/kernel/cpuinfo.c index 7b1e8f9128e9..55882feb6249 100644 --- a/arch/nios2/kernel/cpuinfo.c +++ b/arch/nios2/kernel/cpuinfo.c @@ -46,10 +46,7 @@ void __init setup_cpuinfo(void) cpuinfo.cpu_clock_freq = fcpu(cpu, "clock-frequency"); str = of_get_property(cpu, "altr,implementation", &len); - if (str) - strscpy(cpuinfo.cpu_impl, str, sizeof(cpuinfo.cpu_impl)); - else - strcpy(cpuinfo.cpu_impl, ""); + strscpy(cpuinfo.cpu_impl, str ?: ""); cpuinfo.has_div = of_property_read_bool(cpu, "altr,has-div"); cpuinfo.has_mul = of_property_read_bool(cpu, "altr,has-mul"); -- cgit v1.2.3-59-g8ed1b