summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell King (Oracle) <rmk+kernel@armlinux.org.uk>2022-06-17 13:25:15 +0100
committerRussell King (Oracle) <rmk+kernel@armlinux.org.uk>2023-10-30 12:56:15 +0000
commit0b0a9f1236df10a6e4539872da60ad7932904201 (patch)
tree257d3182b69d66f26668bcf2c6feb89298d0ce3e
parent9ccc4b641f545790622f8fe2423989db463cdae8 (diff)
arm64: text replication: add test module
Add a module to allow kernel text replication to be tested; this exposes some data in procfs which can be used to verify that: (a) we're using different page tables in TTBR1 on CPUs in different NUMA nodes (b) that CPUs in different NUMA nodes are indeed accessing different copies of the kernel Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
-rw-r--r--arch/arm64/Kconfig8
-rw-r--r--arch/arm64/include/asm/ktext.h2
-rw-r--r--arch/arm64/mm/Makefile1
-rw-r--r--arch/arm64/mm/ktext-test.c93
-rw-r--r--arch/arm64/mm/ktext.c15
5 files changed, 119 insertions, 0 deletions
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 82b10e9e72a8..636cdc7bbf13 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -1454,6 +1454,14 @@ config REPLICATE_KTEXT_DEFAULT
Determine whether kernel text replication is enabled at boot by
default.
+config TEST_KTEXT_REPLICATE
+ tristate "Kernel text replication testing module"
+ depends on REPLICATE_KTEXT
+ help
+ Enable building of a test module for kernel text replication.
+
+ If unsure, say N.
+
source "kernel/Kconfig.hz"
config ARCH_SPARSEMEM_ENABLE
diff --git a/arch/arm64/include/asm/ktext.h b/arch/arm64/include/asm/ktext.h
index 2927e5672ace..741b9f75c63e 100644
--- a/arch/arm64/include/asm/ktext.h
+++ b/arch/arm64/include/asm/ktext.h
@@ -19,6 +19,8 @@ void ktext_replication_set_swapper_pgd(pgd_t *pgdp, pgd_t pgd);
void ktext_replication_init_tramp(void);
void create_kernel_nid_map(pgd_t *pgdp, void *ktext);
+extern const char ktext_nid[32];
+
#else
static inline void ktext_replication_init(void)
diff --git a/arch/arm64/mm/Makefile b/arch/arm64/mm/Makefile
index 41e705027c57..482ba013c390 100644
--- a/arch/arm64/mm/Makefile
+++ b/arch/arm64/mm/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_KASAN) += kasan_init.o
KASAN_SANITIZE_kasan_init.o := n
obj-$(CONFIG_REPLICATE_KTEXT) += ktext.o
+obj-$(CONFIG_TEST_KTEXT_REPLICATE) += ktext-test.o
diff --git a/arch/arm64/mm/ktext-test.c b/arch/arm64/mm/ktext-test.c
new file mode 100644
index 000000000000..f397b2fe2e0b
--- /dev/null
+++ b/arch/arm64/mm/ktext-test.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/seq_file.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+
+#include <asm/sysreg.h>
+
+static int ttbr1_show(struct seq_file *m, void *v)
+{
+ unsigned long ttbr;
+ int cpu;
+
+ preempt_disable();
+ cpu = smp_processor_id();
+ ttbr = read_sysreg(ttbr1_el1);
+ preempt_enable();
+
+ seq_printf(m, "CPU%u: TTBR1 0x%08lx\n", cpu, ttbr);
+
+ return 0;
+}
+
+static int ttbr1_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ttbr1_show, NULL);
+}
+
+static const struct proc_ops ttbr1_fops = {
+ .proc_open = ttbr1_open,
+ .proc_read = seq_read,
+ .proc_lseek = seq_lseek,
+ .proc_release = single_release,
+};
+
+extern const char ktext_nid[32];
+
+static int nid_show(struct seq_file *m, void *v)
+{
+ int cpu;
+
+ preempt_disable();
+ cpu = smp_processor_id();
+ seq_printf(m, "CPU%u: nid %s\n",
+ cpu, ktext_nid);
+ preempt_enable();
+
+
+ return 0;
+}
+
+static int nid_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, nid_show, NULL);
+}
+
+static const struct proc_ops nid_fops = {
+ .proc_open = nid_open,
+ .proc_read = seq_read,
+ .proc_lseek = seq_lseek,
+ .proc_release = single_release,
+};
+
+static int ttbr1_init(void)
+{
+ struct proc_dir_entry *dir;
+
+ dir = proc_mkdir("ktext", NULL);
+ if (!dir)
+ return -ENOMEM;
+
+ if (!proc_create("ttbr1", S_IRUSR, dir, &ttbr1_fops) ||
+ !proc_create("text_nid", S_IRUSR, dir, &nid_fops)) {
+ remove_proc_subtree("ktext", NULL);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+module_init(ttbr1_init);
+
+static void ttbr1_fin(void)
+{
+ remove_proc_subtree("ktext", NULL);
+}
+module_exit(ttbr1_fin);
+
+MODULE_AUTHOR("Russell King");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm64/mm/ktext.c b/arch/arm64/mm/ktext.c
index 4ab04f531c81..d29497c37da9 100644
--- a/arch/arm64/mm/ktext.c
+++ b/arch/arm64/mm/ktext.c
@@ -22,6 +22,11 @@ struct pgtables *pgtables[MAX_NUMNODES] = {
static void *kernel_texts[MAX_NUMNODES];
+#if IS_ENABLED(CONFIG_TEST_KTEXT_REPLICATE)
+const char ktext_nid[32] = "0";
+EXPORT_SYMBOL_GPL(ktext_nid);
+#endif
+
static pgd_t *__swapper_pg_dir_node(int nid)
{
return pgtables[nid]->swapper_pg_dir;
@@ -131,6 +136,9 @@ early_param("ktext", parse_ktext);
void __init ktext_replication_init(void)
{
size_t size = __end_rodata - _stext;
+#if IS_ENABLED(CONFIG_TEST_KTEXT_REPLICATE)
+ size_t kt_nid = ktext_nid - _stext;
+#endif
int kidx = pgd_index((phys_addr_t)KERNEL_START);
int nid;
@@ -159,6 +167,13 @@ void __init ktext_replication_init(void)
/* Allocate and copy initial kernel text for this node */
kernel_texts[nid] = memblock_alloc_node(size, PAGE_SIZE, nid);
memcpy(kernel_texts[nid], _stext, size);
+
+#if IS_ENABLED(CONFIG_TEST_KTEXT_REPLICATE)
+ /* Update the node ID in each copy of the kernel text/rodata */
+ snprintf(kernel_texts[nid] + kt_nid, sizeof(kernel_texts[nid]),
+ "%u", nid);
+#endif
+
caches_clean_inval_pou((u64)kernel_texts[nid],
(u64)kernel_texts[nid] + size);