@@ -727,8 +727,11 @@ export CC_FLAGS_FTRACE
ifdef CONFIG_HAVE_FENTRY
CC_USING_FENTRY := $(call cc-option, -mfentry -DCC_USING_FENTRY)
endif
-KBUILD_CFLAGS += $(CC_FLAGS_FTRACE) $(CC_USING_FENTRY)
-KBUILD_AFLAGS += $(CC_USING_FENTRY)
+ifdef CONFIG_HAVE_PROLOG_PAD
+CC_USING_PROLOG_PAD := $(call cc-option, -fprolog-pad=1 -DCC_USING_PROLOG_PAD)
+endif
+KBUILD_CFLAGS += $(CC_FLAGS_FTRACE) $(CC_USING_FENTRY) $(CC_USING_PROLOG_PAD)
+KBUILD_AFLAGS += $(CC_USING_FENTRY) $(CC_USING_PROLOG_PAD)
ifdef CONFIG_DYNAMIC_FTRACE
ifdef CONFIG_HAVE_C_RECORDMCOUNT
BUILD_C_RECORDMCOUNT := y
@@ -66,6 +66,7 @@ config ARM64
select HAVE_DYNAMIC_FTRACE
select HAVE_EFFICIENT_UNALIGNED_ACCESS
select HAVE_FTRACE_MCOUNT_RECORD
+ select HAVE_PROLOG_PAD
select HAVE_FUNCTION_TRACER
select HAVE_FUNCTION_GRAPH_TRACER
select HAVE_GENERIC_DMA_COHERENT
@@ -92,6 +93,7 @@ config ARM64
select SPARSE_IRQ
select SYSCTL_EXCEPTION_TRACE
select HAVE_CONTEXT_TRACKING
+ select HAVE_LIVEPATCH
help
ARM 64-bit (AArch64) Linux support.
@@ -183,6 +185,8 @@ source "init/Kconfig"
source "kernel/Kconfig.freezer"
+source "kernel/livepatch/Kconfig"
+
source "arch/arm64/Kconfig.platforms"
menu "Bus support"
new file mode 100644
@@ -0,0 +1,45 @@
+/*
+ * livepatch.h - arm64-specific Kernel Live Patching Core
+ *
+ * Copyright (C) 2015 Li Bin <huawei.libin@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _ASM_ARM64_LIVEPATCH_H
+#define _ASM_ARM64_LIVEPATCH_H
+
+#include <linux/module.h>
+#include <linux/ftrace.h>
+
+#ifdef CONFIG_LIVEPATCH
+static inline int klp_check_compiler_support(void)
+{
+#ifndef CC_USING_PROLOG_PAD
+ return 1;
+#endif
+ return 0;
+}
+extern int klp_write_module_reloc(struct module *mod, unsigned long type,
+ unsigned long loc, unsigned long value);
+
+static inline void klp_arch_set_pc(struct pt_regs *regs, unsigned long pc)
+{
+ BUG();
+}
+#else
+#error Live patching support is disabled; check CONFIG_LIVEPATCH
+#endif
+
+#endif /* _ASM_ARM64_LIVEPATCH_H */
@@ -29,6 +29,7 @@ arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \
sys_compat.o entry32.o \
../../arm/kernel/opcodes.o
arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o
+arm64-obj-$(CONFIG_LIVEPATCH) += livepatch.o
arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o
arm64-obj-$(CONFIG_PERF_EVENTS) += perf_regs.o perf_callchain.o
arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o
new file mode 100644
@@ -0,0 +1,123 @@
+/*
+ * livepatch.c - arm64-specific Kernel Live Patching Core
+ *
+ * Copyright (C) 2015 Li Bin <huawei.libin@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/livepatch.h>
+#include <linux/slab.h>
+#include <asm/livepatch.h>
+#include <asm/insn.h>
+
+struct klp_func_node {
+ struct list_head node;
+ struct list_head func_stack;
+ unsigned long old_addr;
+};
+
+static LIST_HEAD(klp_func_list);
+
+/**
+ * klp_write_module_reloc() - write a relocation in a module
+ * @mod: module in which the section to be modified is found
+ * @type: ELF relocation type (see asm/elf.h)
+ * @loc: address that the relocation should be written to
+ * @value: relocation value (sym address + addend)
+ *
+ * This function writes a relocation to the specified location for
+ * a particular module.
+ */
+int klp_write_module_reloc(struct module *mod, unsigned long type,
+ unsigned long loc, unsigned long value)
+{
+ /* Perform the static relocation. */
+ return static_relocate(mod, type, (void *)loc, value);
+}
+
+#ifdef CC_USING_PROLOG_PAD
+static struct klp_func_node *klp_find_func_node(unsigned long old_addr)
+{
+ struct klp_func_node *func_node;
+
+ list_for_each_entry(func_node, &klp_func_list, node) {
+ if (func_node->old_addr == old_addr)
+ return func_node;
+ }
+
+ return NULL;
+}
+
+void arch_klp_disable_func(struct klp_func *func)
+{
+ struct klp_func_node *func_node;
+ struct klp_func *next_func;
+ unsigned long pc, new_addr;
+ u32 insn;
+
+ func_node = klp_find_func_node(func->old_addr);
+ pc = func_node->old_addr;
+ if (list_is_singular(&func_node->func_stack)) {
+ list_del_rcu(&func->stack_node);
+ list_del(&func_node->node);
+ kfree(func_node);
+
+ insn = aarch64_insn_gen_nop();
+ aarch64_insn_patch_text_nosync((void *)pc, insn);
+ } else {
+ list_del_rcu(&func->stack_node);
+ next_func = list_first_or_null_rcu(&func_node->func_stack,
+ struct klp_func, stack_node);
+
+ new_addr = (unsigned long)next_func->new_func;
+ insn = aarch64_insn_gen_branch_imm(pc, new_addr,
+ AARCH64_INSN_BRANCH_NOLINK);
+
+ aarch64_insn_patch_text_nosync((void *)pc, insn);
+ }
+}
+
+int arch_klp_enable_func(struct klp_func *func)
+{
+ struct klp_func_node *func_node;
+ unsigned long pc, new_addr;
+ u32 insn;
+
+ func_node = klp_find_func_node(func->old_addr);
+ if (!func_node) {
+ func_node = kzalloc(sizeof(*func_node), GFP_KERNEL);
+ if (!func_node)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&func_node->func_stack);
+ func_node->old_addr = func->old_addr;
+ list_add(&func_node->node, &klp_func_list);
+ }
+
+ list_add_rcu(&func->stack_node, &func_node->func_stack);
+
+ pc = func->old_addr;
+ new_addr = (unsigned long)func->new_func;
+ insn = aarch64_insn_gen_branch_imm(pc, new_addr,
+ AARCH64_INSN_BRANCH_NOLINK);
+
+ if (aarch64_insn_patch_text_nosync((void *)pc, insn))
+ return -EPERM;
+
+ return 0;
+}
+#endif
@@ -1,3 +1,11 @@
+config HAVE_PROLOG_PAD
+ bool
+
+config PROLOG_PAD
+ bool
+ depends on HAVE_PROLOG_PAD
+ default y
+
config HAVE_LIVEPATCH
bool
help
@@ -5,7 +13,7 @@ config HAVE_LIVEPATCH
config LIVEPATCH
bool "Kernel Live Patching"
- depends on DYNAMIC_FTRACE_WITH_REGS
+ depends on DYNAMIC_FTRACE_WITH_REGS || PROLOG_PAD
depends on MODULES
depends on SYSFS
depends on KALLSYMS_ALL
This patch add support for livepach on arm64 based on the gcc -fprolog-pad feature (that always placing one nop at the beginning of the function). And when enable/disable func patching, just modify the pad code to nop or branch. And that NOP and B instruction are both safe instructions on arm64 which called "concurrent modification and execution of instructions", that can be executed by one thread of execution as they are being modified by another thread of execution without requiring explicit synchronization. Signed-off-by: Li Bin <huawei.libin@huawei.com> --- Makefile | 7 ++- arch/arm64/Kconfig | 4 + arch/arm64/include/asm/livepatch.h | 45 +++++++++++++ arch/arm64/kernel/Makefile | 1 + arch/arm64/kernel/livepatch.c | 123 ++++++++++++++++++++++++++++++++++++ kernel/livepatch/Kconfig | 10 +++- 6 files changed, 187 insertions(+), 3 deletions(-) create mode 100644 arch/arm64/include/asm/livepatch.h create mode 100644 arch/arm64/kernel/livepatch.c