summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell King (Oracle) <rmk+kernel@armlinux.org.uk>2022-05-24 21:11:13 +0100
committerRussell King (Oracle) <rmk+kernel@armlinux.org.uk>2022-08-10 15:33:49 +0100
commitb8557d9117093ebe17320d36dadd47fc742820c9 (patch)
treee494687bdf7a41319f818818ef4b46e34ad75fd5
parent62f0b42355e0b5f73456ce8df0e926749d8fdf7e (diff)
arm64: text replication: add node text patching
Add support for text patching on our replicated texts. Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-rw-r--r--arch/arm64/include/asm/ktext.h12
-rw-r--r--arch/arm64/kernel/alternative.c2
-rw-r--r--arch/arm64/kernel/patching.c7
-rw-r--r--arch/arm64/mm/ktext.c58
4 files changed, 78 insertions, 1 deletions
diff --git a/arch/arm64/include/asm/ktext.h b/arch/arm64/include/asm/ktext.h
index 1a5f7452a3bf..289e11289c06 100644
--- a/arch/arm64/include/asm/ktext.h
+++ b/arch/arm64/include/asm/ktext.h
@@ -5,9 +5,13 @@
#ifndef ASM_KTEXT_H
#define ASM_KTEXT_H
+#include <linux/kprobes.h>
+
#ifdef CONFIG_REPLICATE_KTEXT
void ktext_replication_init(void);
+void __kprobes ktext_replication_patch(u32 *tp, __le32 insn);
+void ktext_replication_patch_alternative(__le32 *src, int nr_inst);
#else
@@ -15,6 +19,14 @@ static inline void ktext_replication_init(void)
{
}
+static inline void __kprobes ktext_replication_patch(u32 *tp, __le32 insn)
+{
+}
+
+static inline void ktext_replication_patch_alternative(__le32 *src, int nr_inst)
+{
+}
+
#endif
#endif
diff --git a/arch/arm64/kernel/alternative.c b/arch/arm64/kernel/alternative.c
index 7bbf5104b7b7..b4bf3c24668c 100644
--- a/arch/arm64/kernel/alternative.c
+++ b/arch/arm64/kernel/alternative.c
@@ -14,6 +14,7 @@
#include <asm/alternative.h>
#include <asm/cpufeature.h>
#include <asm/insn.h>
+#include <asm/ktext.h>
#include <asm/sections.h>
#include <linux/stop_machine.h>
@@ -170,6 +171,7 @@ static void __nocfi __apply_alternatives(struct alt_region *region, bool is_modu
alt_cb(alt, origptr, updptr, nr_inst);
if (!is_module) {
+ ktext_replication_patch_alternative(updptr, nr_inst);
clean_dcache_range_nopatch((u64)origptr,
(u64)(origptr + nr_inst));
}
diff --git a/arch/arm64/kernel/patching.c b/arch/arm64/kernel/patching.c
index 33e0fabc0b79..0805440aa3c1 100644
--- a/arch/arm64/kernel/patching.c
+++ b/arch/arm64/kernel/patching.c
@@ -10,6 +10,7 @@
#include <asm/fixmap.h>
#include <asm/insn.h>
#include <asm/kprobes.h>
+#include <asm/ktext.h>
#include <asm/patching.h>
#include <asm/sections.h>
@@ -98,9 +99,13 @@ int __kprobes aarch64_insn_patch_text_nosync(void *addr, u32 insn)
return -EINVAL;
ret = aarch64_insn_write(tp, insn);
- if (ret == 0)
+ if (ret == 0) {
+ /* Also patch the other nodes */
+ ktext_replication_patch(tp, cpu_to_le32(insn));
+
caches_clean_inval_pou((uintptr_t)tp,
(uintptr_t)tp + AARCH64_INSN_SIZE);
+ }
return ret;
}
diff --git a/arch/arm64/mm/ktext.c b/arch/arm64/mm/ktext.c
index 7e808bd6004f..3fb502170bf1 100644
--- a/arch/arm64/mm/ktext.c
+++ b/arch/arm64/mm/ktext.c
@@ -3,7 +3,9 @@
* Copyright (C) 2022, Oracle and/or its affiliates.
*/
+#include <linux/kallsyms.h>
#include <linux/kernel.h>
+#include <linux/mm.h>
#include <linux/numa.h>
#include <linux/pgtable.h>
#include <linux/string.h>
@@ -14,6 +16,62 @@
static void *kernel_texts[MAX_NUMNODES];
+void __kprobes ktext_replication_patch(u32 *tp, __le32 insn)
+{
+ unsigned long offset;
+ int nid, this_nid;
+ __le32 *p;
+
+ if (!is_kernel_text((unsigned long)tp))
+ return;
+
+ offset = (unsigned long)tp - (unsigned long)_stext;
+
+ this_nid = numa_node_id();
+ if (this_nid) {
+ /* The cache maintenance by aarch64_insn_patch_text_nosync()
+ * will occur on this node. We need it to occur on node 0.
+ */
+ p = (void *)lm_alias(_stext) + offset;
+ caches_clean_inval_pou((u64)p, (u64)p + AARCH64_INSN_SIZE);
+ }
+
+ for_each_node(nid) {
+ if (!kernel_texts[nid])
+ continue;
+
+ p = kernel_texts[nid] + offset;
+ WRITE_ONCE(*p, insn);
+ caches_clean_inval_pou((u64)p, (u64)p + AARCH64_INSN_SIZE);
+ }
+}
+
+/* Copy the patched alternative from the node0 image to the other
+ * modes. src is the node 0 linear-mapping address.
+ */
+void ktext_replication_patch_alternative(__le32 *src, int nr_inst)
+{
+ unsigned long offset;
+ size_t size;
+ int nid;
+ __le32 *p;
+
+ offset = (unsigned long)src - (unsigned long)lm_alias(_stext);
+ if (WARN_ON_ONCE(offset >= _etext - _stext))
+ return;
+
+ size = AARCH64_INSN_SIZE * nr_inst;
+
+ for_each_node(nid) {
+ if (!kernel_texts[nid])
+ continue;
+
+ p = kernel_texts[nid] + offset;
+ memcpy(p, src, size);
+ clean_dcache_range_nopatch((u64)p, (u64)p + size);
+ }
+}
+
/* Allocate memory for the replicated kernel texts. */
void ktext_replication_init(void)
{