]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
openrisc: Add text patching API support
authorchenmiao <chenmiao.ku@gmail.com>
Fri, 5 Sep 2025 18:12:55 +0000 (18:12 +0000)
committerStafford Horne <shorne@gmail.com>
Thu, 11 Sep 2025 10:27:59 +0000 (11:27 +0100)
Add text patching api's to use in subsequent jump_label implementation.  We use
a new fixmap FIX_TEXT_POKE0 entry to temporarily override MMU mappings to allow
read only text pages to be written to.

Previously, __set_fix was marked with __init as it was only used during the
EARLYCON stage. Now that TEXT_POKE mappings require post-init usage
(e.g., FIX_TEXT_POKE0), keeping __init would cause runtime bugs whenset_fixmap
accesses invalid memory. Thus, we remove the __init flag to ensure __set_fix
remains valid beyond initialization.

A new function patch_insn_write is exposed to allow single instruction patching.

Link: https://lore.kernel.org/openrisc/aJIC8o1WmVHol9RY@antec/T/#t
Signed-off-by: chenmiao <chenmiao.ku@gmail.com>
Signed-off-by: Stafford Horne <shorne@gmail.com>
arch/openrisc/include/asm/Kbuild
arch/openrisc/include/asm/fixmap.h
arch/openrisc/include/asm/insn-def.h [new file with mode: 0644]
arch/openrisc/include/asm/text-patching.h [new file with mode: 0644]
arch/openrisc/kernel/Makefile
arch/openrisc/kernel/patching.c [new file with mode: 0644]
arch/openrisc/mm/init.c

index 2b1a6b00cdac0a0adae4868e82afeda2b727b55e..cef49d60d74c0f46f01cf46cc35e1e52404185f3 100644 (file)
@@ -9,4 +9,3 @@ generic-y += spinlock.h
 generic-y += qrwlock_types.h
 generic-y += qrwlock.h
 generic-y += user.h
-generic-y += text-patching.h
index aaa6a26a3e92151200ce53215120f675808e55f0..74000215064d0c540329e3ffed65cf9ebf39d588 100644 (file)
@@ -28,6 +28,7 @@
 
 enum fixed_addresses {
        FIX_EARLYCON_MEM_BASE,
+       FIX_TEXT_POKE0,
        __end_of_fixed_addresses
 };
 
diff --git a/arch/openrisc/include/asm/insn-def.h b/arch/openrisc/include/asm/insn-def.h
new file mode 100644 (file)
index 0000000..e28a9a9
--- /dev/null
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2025 Chen Miao
+ */
+
+#ifndef __ASM_OPENRISC_INSN_DEF_H
+#define __ASM_OPENRISC_INSN_DEF_H
+
+/* or1k instructions are always 32 bits. */
+#define        OPENRISC_INSN_SIZE              4
+
+#endif /* __ASM_OPENRISC_INSN_DEF_H */
diff --git a/arch/openrisc/include/asm/text-patching.h b/arch/openrisc/include/asm/text-patching.h
new file mode 100644 (file)
index 0000000..d19098d
--- /dev/null
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2025 Chen Miao
+ */
+
+#ifndef _ASM_OPENRISC_PATCHING_H
+#define _ASM_OPENRISC_PATCHING_H
+
+#include <linux/types.h>
+
+int patch_insn_write(void *addr, u32 insn);
+
+#endif /* _ASM_OPENRISC_PATCHING_H */
index 58e6a1b525b7b1bfed82150bd2614432805a94ac..f0957ce16d6b31e2ee3d6da15bc7097dfdfdf30d 100644 (file)
@@ -13,5 +13,6 @@ obj-$(CONFIG_SMP)             += smp.o sync-timer.o
 obj-$(CONFIG_STACKTRACE)       += stacktrace.o
 obj-$(CONFIG_MODULES)          += module.o
 obj-$(CONFIG_OF)               += prom.o
+obj-y  += patching.o
 
 clean:
diff --git a/arch/openrisc/kernel/patching.c b/arch/openrisc/kernel/patching.c
new file mode 100644 (file)
index 0000000..d186172
--- /dev/null
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2020 SiFive
+ * Copyright (C) 2025 Chen Miao
+ */
+
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+
+#include <asm/insn-def.h>
+#include <asm/cacheflush.h>
+#include <asm/page.h>
+#include <asm/fixmap.h>
+#include <asm/text-patching.h>
+#include <asm/sections.h>
+
+static DEFINE_RAW_SPINLOCK(patch_lock);
+
+static __always_inline void *patch_map(void *addr, int fixmap)
+{
+       uintptr_t uaddr = (uintptr_t) addr;
+       phys_addr_t phys;
+
+       if (core_kernel_text(uaddr)) {
+               phys = __pa_symbol(addr);
+       } else {
+               struct page *page = vmalloc_to_page(addr);
+               BUG_ON(!page);
+               phys = page_to_phys(page) + offset_in_page(addr);
+       }
+
+       return (void *)set_fixmap_offset(fixmap, phys);
+}
+
+static void patch_unmap(int fixmap)
+{
+       clear_fixmap(fixmap);
+}
+
+static int __patch_insn_write(void *addr, u32 insn)
+{
+       void *waddr = addr;
+       unsigned long flags = 0;
+       int ret;
+
+       raw_spin_lock_irqsave(&patch_lock, flags);
+
+       waddr = patch_map(addr, FIX_TEXT_POKE0);
+
+       ret = copy_to_kernel_nofault(waddr, &insn, OPENRISC_INSN_SIZE);
+       local_icache_range_inv((unsigned long)waddr,
+                              (unsigned long)waddr + OPENRISC_INSN_SIZE);
+
+       patch_unmap(FIX_TEXT_POKE0);
+
+       raw_spin_unlock_irqrestore(&patch_lock, flags);
+
+       return ret;
+}
+
+/*
+ * patch_insn_write - Write a single instruction to a specified memory location
+ * This API provides a single-instruction patching, primarily used for runtime
+ * code modification.
+ * By the way, the insn size must be 4 bytes.
+ */
+int patch_insn_write(void *addr, u32 insn)
+{
+       u32 *tp = addr;
+       int ret;
+
+       if ((uintptr_t) tp & 0x3)
+               return -EINVAL;
+
+       ret = __patch_insn_write(tp, insn);
+
+       return ret;
+}
index e4904ca6f0a088fe44de19aa78e642257c5e65c1..9382d9a0ec788843b3969282bb81f6a4465c2bb2 100644 (file)
@@ -226,7 +226,11 @@ static int __init map_page(unsigned long va, phys_addr_t pa, pgprot_t prot)
        return 0;
 }
 
-void __init __set_fixmap(enum fixed_addresses idx,
+/*
+ * __set_fix must now support both EARLYCON and TEXT_POKE mappings,
+ * which are used at different stages of kernel execution.
+ */
+void __set_fixmap(enum fixed_addresses idx,
                         phys_addr_t phys, pgprot_t prot)
 {
        unsigned long address = __fix_to_virt(idx);