MIPS: Add support for CONFIG_DEBUG_VIRTUAL
authorFlorian Fainelli <f.fainelli@gmail.com>
Wed, 31 Mar 2021 03:22:07 +0000 (20:22 -0700)
committerThomas Bogendoerfer <tsbogend@alpha.franken.de>
Tue, 6 Apr 2021 12:34:02 +0000 (14:34 +0200)
Provide hooks to intercept bad usages of virt_to_phys() and
__pa_symbol() throughout the kernel. To make this possible, we need to
rename the current implement of virt_to_phys() into
__virt_to_phys_nodebug() and wrap it around depending on
CONFIG_DEBUG_VIRTUAL.

A similar thing is needed for __pa_symbol() which is now aliased to
__phys_addr_symbol() whose implementation is either the direct return of
RELOC_HIDE or goes through the debug version.

Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
arch/mips/Kconfig
arch/mips/include/asm/io.h
arch/mips/include/asm/page.h
arch/mips/kernel/vdso.c
arch/mips/mm/Makefile
arch/mips/mm/physaddr.c [new file with mode: 0644]

index b72458215d20f126c5d1f81c78f784dab5515be3..7d509191168b54c39682d87a2ab282ba8a67a693 100644 (file)
@@ -4,6 +4,7 @@ config MIPS
        default y
        select ARCH_32BIT_OFF_T if !64BIT
        select ARCH_BINFMT_ELF_STATE if MIPS_FP_SUPPORT
+       select ARCH_HAS_DEBUG_VIRTUAL if !64BIT
        select ARCH_HAS_FORTIFY_SOURCE
        select ARCH_HAS_KCOV
        select ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE if !EVA
index 78537aa2350062f3678f8ae62b91d002884c15a3..2c138450ad3b58a38e070c97a702ff45f93c3e51 100644 (file)
@@ -100,11 +100,23 @@ static inline void set_io_port_base(unsigned long base)
  *     almost all conceivable cases a device driver should not be using
  *     this function
  */
-static inline unsigned long virt_to_phys(volatile const void *address)
+static inline unsigned long __virt_to_phys_nodebug(volatile const void *address)
 {
        return __pa(address);
 }
 
+#ifdef CONFIG_DEBUG_VIRTUAL
+extern phys_addr_t __virt_to_phys(volatile const void *x);
+#else
+#define __virt_to_phys(x)      __virt_to_phys_nodebug(x)
+#endif
+
+#define virt_to_phys virt_to_phys
+static inline phys_addr_t virt_to_phys(const volatile void *x)
+{
+       return __virt_to_phys(x);
+}
+
 /*
  *     phys_to_virt    -       map physical address to virtual
  *     @address: address to remap
index 65acab9c41f9a83269188ccdac43cfd69e3eb183..195ff4e9771f462219c9cb54557e3cc4978d15a8 100644 (file)
@@ -210,9 +210,16 @@ static inline unsigned long ___pa(unsigned long x)
  * also affect MIPS so we keep this one until GCC 3.x has been retired
  * before we can apply https://patchwork.linux-mips.org/patch/1541/
  */
+#define __pa_symbol_nodebug(x) __pa(RELOC_HIDE((unsigned long)(x), 0))
+
+#ifdef CONFIG_DEBUG_VIRTUAL
+extern phys_addr_t __phys_addr_symbol(unsigned long x);
+#else
+#define __phys_addr_symbol(x)  __pa_symbol_nodebug(x)
+#endif
 
 #ifndef __pa_symbol
-#define __pa_symbol(x) __pa(RELOC_HIDE((unsigned long)(x), 0))
+#define __pa_symbol(x)         __phys_addr_symbol((unsigned long)(x))
 #endif
 
 #define pfn_to_kaddr(pfn)      __va((pfn) << PAGE_SHIFT)
index 7d0b91ad25810b8843f8216d530fce132eb4a3c9..3d0cf471f2fe1eeaace55057e138b6b8dcddf033 100644 (file)
@@ -90,7 +90,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
 {
        struct mips_vdso_image *image = current->thread.abi->vdso;
        struct mm_struct *mm = current->mm;
-       unsigned long gic_size, vvar_size, size, base, data_addr, vdso_addr, gic_pfn;
+       unsigned long gic_size, vvar_size, size, base, data_addr, vdso_addr, gic_pfn, gic_base;
        struct vm_area_struct *vma;
        int ret;
 
@@ -158,7 +158,8 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
 
        /* Map GIC user page. */
        if (gic_size) {
-               gic_pfn = virt_to_phys(mips_gic_base + MIPS_GIC_USER_OFS) >> PAGE_SHIFT;
+               gic_base = (unsigned long)mips_gic_base + MIPS_GIC_USER_OFS;
+               gic_pfn = virt_to_phys((void *)gic_base) >> PAGE_SHIFT;
 
                ret = io_remap_pfn_range(vma, base, gic_pfn, gic_size,
                                         pgprot_noncached(vma->vm_page_prot));
index 865926a3777573b0960ae551d7176c194c9ebe47..fa1f729e070005a92a2872422b4cc4eb16ad3f86 100644 (file)
@@ -40,3 +40,5 @@ obj-$(CONFIG_R5000_CPU_SCACHE)        += sc-r5k.o
 obj-$(CONFIG_RM7000_CPU_SCACHE) += sc-rm7k.o
 obj-$(CONFIG_MIPS_CPU_SCACHE)  += sc-mips.o
 obj-$(CONFIG_SCACHE_DEBUGFS)   += sc-debugfs.o
+
+obj-$(CONFIG_DEBUG_VIRTUAL)    += physaddr.o
diff --git a/arch/mips/mm/physaddr.c b/arch/mips/mm/physaddr.c
new file mode 100644 (file)
index 0000000..a1ced5e
--- /dev/null
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/bug.h>
+#include <linux/export.h>
+#include <linux/types.h>
+#include <linux/mmdebug.h>
+#include <linux/mm.h>
+
+#include <asm/sections.h>
+#include <asm/io.h>
+#include <asm/page.h>
+#include <asm/dma.h>
+
+static inline bool __debug_virt_addr_valid(unsigned long x)
+{
+       /* high_memory does not get immediately defined, and there
+        * are early callers of __pa() against PAGE_OFFSET
+        */
+       if (!high_memory && x >= PAGE_OFFSET)
+               return true;
+
+       if (high_memory && x >= PAGE_OFFSET && x < (unsigned long)high_memory)
+               return true;
+
+       /*
+        * MAX_DMA_ADDRESS is a virtual address that may not correspond to an
+        * actual physical address. Enough code relies on
+        * virt_to_phys(MAX_DMA_ADDRESS) that we just need to work around it
+        * and always return true.
+        */
+       if (x == MAX_DMA_ADDRESS)
+               return true;
+
+       return false;
+}
+
+phys_addr_t __virt_to_phys(volatile const void *x)
+{
+       WARN(!__debug_virt_addr_valid((unsigned long)x),
+            "virt_to_phys used for non-linear address: %pK (%pS)\n",
+            x, x);
+
+       return __virt_to_phys_nodebug(x);
+}
+EXPORT_SYMBOL(__virt_to_phys);
+
+phys_addr_t __phys_addr_symbol(unsigned long x)
+{
+       /* This is bounds checking against the kernel image only.
+        * __pa_symbol should only be used on kernel symbol addresses.
+        */
+       VIRTUAL_BUG_ON(x < (unsigned long)_text ||
+                      x > (unsigned long)_end);
+
+       return __pa_symbol_nodebug(x);
+}
+EXPORT_SYMBOL(__phys_addr_symbol);