* [RFC PATCH 0/3] livepatch: arm64: add support for livepatch on arm64
@ 2015-12-14 12:49 Li Bin
2015-12-14 12:49 ` [RFC PATCH 1/3] livepatch: allow arch specific implementation Li Bin
` (4 more replies)
0 siblings, 5 replies; 9+ messages in thread
From: Li Bin @ 2015-12-14 12:49 UTC (permalink / raw)
To: linux-arm-kernel
This patchset depends on the on-going gcc feature "-fprolog-pad=N",
which will generate a pad of N nops at the beginning of each function.
Livepatch on arm64 can using the 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.
And this method will improve performance significantly compared with the
method based on ftrace, especially for the critical function being frequently
called.
Li Bin (3):
livepatch: allow arch specific implementation
livepatch: module: arm64: extract the relocation code for reuse
livepatch: arm64: add support for livepatch on arm64
Makefile | 7 +-
arch/arm64/Kconfig | 4 +
arch/arm64/include/asm/livepatch.h | 45 +++++
arch/arm64/include/asm/module.h | 3 +
arch/arm64/kernel/Makefile | 1 +
arch/arm64/kernel/livepatch.c | 123 ++++++++++++
arch/arm64/kernel/module.c | 360 ++++++++++++++++++------------------
kernel/livepatch/Kconfig | 10 +-
kernel/livepatch/core.c | 45 +++--
9 files changed, 403 insertions(+), 195 deletions(-)
create mode 100644 arch/arm64/include/asm/livepatch.h
create mode 100644 arch/arm64/kernel/livepatch.c
^ permalink raw reply [flat|nested] 9+ messages in thread* [RFC PATCH 1/3] livepatch: allow arch specific implementation 2015-12-14 12:49 [RFC PATCH 0/3] livepatch: arm64: add support for livepatch on arm64 Li Bin @ 2015-12-14 12:49 ` Li Bin 2015-12-14 12:49 ` [RFC PATCH 2/3] livepatch: module: arm64: extract the relocation code for reuse Li Bin ` (3 subsequent siblings) 4 siblings, 0 replies; 9+ messages in thread From: Li Bin @ 2015-12-14 12:49 UTC (permalink / raw) To: linux-arm-kernel Allow klp_enable_func/klp_disable_func be specific implementation for different arch. Signed-off-by: Li Bin <huawei.libin@huawei.com> --- kernel/livepatch/core.c | 45 +++++++++++++++++++++++++++++---------------- 1 files changed, 29 insertions(+), 16 deletions(-) diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c index db545cb..0a8cf96 100644 --- a/kernel/livepatch/core.c +++ b/kernel/livepatch/core.c @@ -350,15 +350,10 @@ unlock: rcu_read_unlock(); } -static void klp_disable_func(struct klp_func *func) +void __weak arch_klp_disable_func(struct klp_func *func) { struct klp_ops *ops; - if (WARN_ON(func->state != KLP_ENABLED)) - return; - if (WARN_ON(!func->old_addr)) - return; - ops = klp_find_ops(func->old_addr); if (WARN_ON(!ops)) return; @@ -373,21 +368,23 @@ static void klp_disable_func(struct klp_func *func) } else { list_del_rcu(&func->stack_node); } +} +static void klp_disable_func(struct klp_func *func) +{ + if (WARN_ON(func->state != KLP_ENABLED)) + return; + if (WARN_ON(!func->old_addr)) + return; + arch_klp_disable_func(func); func->state = KLP_DISABLED; } -static int klp_enable_func(struct klp_func *func) +int __weak arch_klp_enable_func(struct klp_func *func) { struct klp_ops *ops; int ret; - if (WARN_ON(!func->old_addr)) - return -EINVAL; - - if (WARN_ON(func->state != KLP_DISABLED)) - return -EINVAL; - ops = klp_find_ops(func->old_addr); if (!ops) { ops = kzalloc(sizeof(*ops), GFP_KERNEL); @@ -424,10 +421,7 @@ static int klp_enable_func(struct klp_func *func) list_add_rcu(&func->stack_node, &ops->func_stack); } - func->state = KLP_ENABLED; - return 0; - err: list_del_rcu(&func->stack_node); list_del(&ops->node); @@ -435,6 +429,25 @@ err: return ret; } +static int klp_enable_func(struct klp_func *func) +{ + int ret; + + if (WARN_ON(!func->old_addr)) + return -EINVAL; + + if (WARN_ON(func->state != KLP_DISABLED)) + return -EINVAL; + + ret = arch_klp_enable_func(func); + if (ret) + return ret; + + func->state = KLP_ENABLED; + + return 0; +} + static void klp_disable_object(struct klp_object *obj) { struct klp_func *func; -- 1.7.1 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [RFC PATCH 2/3] livepatch: module: arm64: extract the relocation code for reuse 2015-12-14 12:49 [RFC PATCH 0/3] livepatch: arm64: add support for livepatch on arm64 Li Bin 2015-12-14 12:49 ` [RFC PATCH 1/3] livepatch: allow arch specific implementation Li Bin @ 2015-12-14 12:49 ` Li Bin 2015-12-14 15:29 ` Josh Poimboeuf 2015-12-14 12:49 ` [RFC PATCH 3/3] livepatch: arm64: add support for livepatch on arm64 Li Bin ` (2 subsequent siblings) 4 siblings, 1 reply; 9+ messages in thread From: Li Bin @ 2015-12-14 12:49 UTC (permalink / raw) To: linux-arm-kernel Livepatch can reuse the relocation codes of module loader, this patch extract it. Signed-off-by: Li Bin <huawei.libin@huawei.com> --- arch/arm64/include/asm/module.h | 3 + arch/arm64/kernel/module.c | 360 ++++++++++++++++++++------------------- 2 files changed, 187 insertions(+), 176 deletions(-) diff --git a/arch/arm64/include/asm/module.h b/arch/arm64/include/asm/module.h index e80e232..78ac36e 100644 --- a/arch/arm64/include/asm/module.h +++ b/arch/arm64/include/asm/module.h @@ -20,4 +20,7 @@ #define MODULE_ARCH_VERMAGIC "aarch64" +extern int static_relocate(struct module *mod, unsigned long type, + void * loc, unsigned long value); + #endif /* __ASM_MODULE_H */ diff --git a/arch/arm64/kernel/module.c b/arch/arm64/kernel/module.c index f4bc779..6d1a1e3 100644 --- a/arch/arm64/kernel/module.c +++ b/arch/arm64/kernel/module.c @@ -203,6 +203,184 @@ static int reloc_insn_imm(enum aarch64_reloc_op op, void *place, u64 val, return 0; } +int static_relocate(struct module *me, unsigned long type, void *loc, + unsigned long val) +{ + int ovf = 0; + bool overflow_check = true; + /* Perform the static relocation. */ + switch (type) { + /* Null relocations. */ + case R_ARM_NONE: + case R_AARCH64_NONE: + ovf = 0; + break; + + /* Data relocations. */ + case R_AARCH64_ABS64: + overflow_check = false; + ovf = reloc_data(RELOC_OP_ABS, loc, val, 64); + break; + case R_AARCH64_ABS32: + ovf = reloc_data(RELOC_OP_ABS, loc, val, 32); + break; + case R_AARCH64_ABS16: + ovf = reloc_data(RELOC_OP_ABS, loc, val, 16); + break; + case R_AARCH64_PREL64: + overflow_check = false; + ovf = reloc_data(RELOC_OP_PREL, loc, val, 64); + break; + case R_AARCH64_PREL32: + ovf = reloc_data(RELOC_OP_PREL, loc, val, 32); + break; + case R_AARCH64_PREL16: + ovf = reloc_data(RELOC_OP_PREL, loc, val, 16); + break; + + /* MOVW instruction relocations. */ + case R_AARCH64_MOVW_UABS_G0_NC: + overflow_check = false; + case R_AARCH64_MOVW_UABS_G0: + ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0, + AARCH64_INSN_IMM_16); + break; + case R_AARCH64_MOVW_UABS_G1_NC: + overflow_check = false; + case R_AARCH64_MOVW_UABS_G1: + ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16, + AARCH64_INSN_IMM_16); + break; + case R_AARCH64_MOVW_UABS_G2_NC: + overflow_check = false; + case R_AARCH64_MOVW_UABS_G2: + ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32, + AARCH64_INSN_IMM_16); + break; + case R_AARCH64_MOVW_UABS_G3: + /* We're using the top bits so we can't overflow. */ + overflow_check = false; + ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 48, + AARCH64_INSN_IMM_16); + break; + case R_AARCH64_MOVW_SABS_G0: + ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0, + AARCH64_INSN_IMM_MOVNZ); + break; + case R_AARCH64_MOVW_SABS_G1: + ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16, + AARCH64_INSN_IMM_MOVNZ); + break; + case R_AARCH64_MOVW_SABS_G2: + ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32, + AARCH64_INSN_IMM_MOVNZ); + break; + case R_AARCH64_MOVW_PREL_G0_NC: + overflow_check = false; + ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0, + AARCH64_INSN_IMM_MOVK); + break; + case R_AARCH64_MOVW_PREL_G0: + ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0, + AARCH64_INSN_IMM_MOVNZ); + break; + case R_AARCH64_MOVW_PREL_G1_NC: + overflow_check = false; + ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16, + AARCH64_INSN_IMM_MOVK); + break; + case R_AARCH64_MOVW_PREL_G1: + ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16, + AARCH64_INSN_IMM_MOVNZ); + break; + case R_AARCH64_MOVW_PREL_G2_NC: + overflow_check = false; + ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32, + AARCH64_INSN_IMM_MOVK); + break; + case R_AARCH64_MOVW_PREL_G2: + ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32, + AARCH64_INSN_IMM_MOVNZ); + break; + case R_AARCH64_MOVW_PREL_G3: + /* We're using the top bits so we can't overflow. */ + overflow_check = false; + ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 48, + AARCH64_INSN_IMM_MOVNZ); + break; + + /* Immediate instruction relocations. */ + case R_AARCH64_LD_PREL_LO19: + ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 19, + AARCH64_INSN_IMM_19); + break; + case R_AARCH64_ADR_PREL_LO21: + ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 0, 21, + AARCH64_INSN_IMM_ADR); + break; +#ifndef CONFIG_ARM64_ERRATUM_843419 + case R_AARCH64_ADR_PREL_PG_HI21_NC: + overflow_check = false; + case R_AARCH64_ADR_PREL_PG_HI21: + ovf = reloc_insn_imm(RELOC_OP_PAGE, loc, val, 12, 21, + AARCH64_INSN_IMM_ADR); + break; +#endif + case R_AARCH64_ADD_ABS_LO12_NC: + case R_AARCH64_LDST8_ABS_LO12_NC: + overflow_check = false; + ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 0, 12, + AARCH64_INSN_IMM_12); + break; + case R_AARCH64_LDST16_ABS_LO12_NC: + overflow_check = false; + ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 1, 11, + AARCH64_INSN_IMM_12); + break; + case R_AARCH64_LDST32_ABS_LO12_NC: + overflow_check = false; + ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 2, 10, + AARCH64_INSN_IMM_12); + break; + case R_AARCH64_LDST64_ABS_LO12_NC: + overflow_check = false; + ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 3, 9, + AARCH64_INSN_IMM_12); + break; + case R_AARCH64_LDST128_ABS_LO12_NC: + overflow_check = false; + ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 4, 8, + AARCH64_INSN_IMM_12); + break; + case R_AARCH64_TSTBR14: + ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 14, + AARCH64_INSN_IMM_14); + break; + case R_AARCH64_CONDBR19: + ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 19, + AARCH64_INSN_IMM_19); + break; + case R_AARCH64_JUMP26: + case R_AARCH64_CALL26: + ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 26, + AARCH64_INSN_IMM_26); + break; + + default: + pr_err("module %s: unsupported RELA relocation: %lu\n", + me->name, type); + return -ENOEXEC; + } + + if (overflow_check && ovf == -ERANGE) { + pr_err("module %s: overflow in relocation type %lu val %lx\n", + me->name, type, val); + return -ENOEXEC; + } + + return 0; +} + int apply_relocate_add(Elf64_Shdr *sechdrs, const char *strtab, unsigned int symindex, @@ -210,12 +388,11 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, struct module *me) { unsigned int i; - int ovf; - bool overflow_check; Elf64_Sym *sym; void *loc; u64 val; Elf64_Rela *rel = (void *)sechdrs[relsec].sh_addr; + int type, ret; for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { /* loc corresponds to P in the AArch64 ELF document. */ @@ -229,184 +406,15 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, /* val corresponds to (S + A) in the AArch64 ELF document. */ val = sym->st_value + rel[i].r_addend; - /* Check for overflow by default. */ - overflow_check = true; - - /* Perform the static relocation. */ - switch (ELF64_R_TYPE(rel[i].r_info)) { - /* Null relocations. */ - case R_ARM_NONE: - case R_AARCH64_NONE: - ovf = 0; - break; - - /* Data relocations. */ - case R_AARCH64_ABS64: - overflow_check = false; - ovf = reloc_data(RELOC_OP_ABS, loc, val, 64); - break; - case R_AARCH64_ABS32: - ovf = reloc_data(RELOC_OP_ABS, loc, val, 32); - break; - case R_AARCH64_ABS16: - ovf = reloc_data(RELOC_OP_ABS, loc, val, 16); - break; - case R_AARCH64_PREL64: - overflow_check = false; - ovf = reloc_data(RELOC_OP_PREL, loc, val, 64); - break; - case R_AARCH64_PREL32: - ovf = reloc_data(RELOC_OP_PREL, loc, val, 32); - break; - case R_AARCH64_PREL16: - ovf = reloc_data(RELOC_OP_PREL, loc, val, 16); - break; - - /* MOVW instruction relocations. */ - case R_AARCH64_MOVW_UABS_G0_NC: - overflow_check = false; - case R_AARCH64_MOVW_UABS_G0: - ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0, - AARCH64_INSN_IMM_16); - break; - case R_AARCH64_MOVW_UABS_G1_NC: - overflow_check = false; - case R_AARCH64_MOVW_UABS_G1: - ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16, - AARCH64_INSN_IMM_16); - break; - case R_AARCH64_MOVW_UABS_G2_NC: - overflow_check = false; - case R_AARCH64_MOVW_UABS_G2: - ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32, - AARCH64_INSN_IMM_16); - break; - case R_AARCH64_MOVW_UABS_G3: - /* We're using the top bits so we can't overflow. */ - overflow_check = false; - ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 48, - AARCH64_INSN_IMM_16); - break; - case R_AARCH64_MOVW_SABS_G0: - ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0, - AARCH64_INSN_IMM_MOVNZ); - break; - case R_AARCH64_MOVW_SABS_G1: - ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16, - AARCH64_INSN_IMM_MOVNZ); - break; - case R_AARCH64_MOVW_SABS_G2: - ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32, - AARCH64_INSN_IMM_MOVNZ); - break; - case R_AARCH64_MOVW_PREL_G0_NC: - overflow_check = false; - ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0, - AARCH64_INSN_IMM_MOVK); - break; - case R_AARCH64_MOVW_PREL_G0: - ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0, - AARCH64_INSN_IMM_MOVNZ); - break; - case R_AARCH64_MOVW_PREL_G1_NC: - overflow_check = false; - ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16, - AARCH64_INSN_IMM_MOVK); - break; - case R_AARCH64_MOVW_PREL_G1: - ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16, - AARCH64_INSN_IMM_MOVNZ); - break; - case R_AARCH64_MOVW_PREL_G2_NC: - overflow_check = false; - ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32, - AARCH64_INSN_IMM_MOVK); - break; - case R_AARCH64_MOVW_PREL_G2: - ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32, - AARCH64_INSN_IMM_MOVNZ); - break; - case R_AARCH64_MOVW_PREL_G3: - /* We're using the top bits so we can't overflow. */ - overflow_check = false; - ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 48, - AARCH64_INSN_IMM_MOVNZ); - break; - - /* Immediate instruction relocations. */ - case R_AARCH64_LD_PREL_LO19: - ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 19, - AARCH64_INSN_IMM_19); - break; - case R_AARCH64_ADR_PREL_LO21: - ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 0, 21, - AARCH64_INSN_IMM_ADR); - break; -#ifndef CONFIG_ARM64_ERRATUM_843419 - case R_AARCH64_ADR_PREL_PG_HI21_NC: - overflow_check = false; - case R_AARCH64_ADR_PREL_PG_HI21: - ovf = reloc_insn_imm(RELOC_OP_PAGE, loc, val, 12, 21, - AARCH64_INSN_IMM_ADR); - break; -#endif - case R_AARCH64_ADD_ABS_LO12_NC: - case R_AARCH64_LDST8_ABS_LO12_NC: - overflow_check = false; - ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 0, 12, - AARCH64_INSN_IMM_12); - break; - case R_AARCH64_LDST16_ABS_LO12_NC: - overflow_check = false; - ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 1, 11, - AARCH64_INSN_IMM_12); - break; - case R_AARCH64_LDST32_ABS_LO12_NC: - overflow_check = false; - ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 2, 10, - AARCH64_INSN_IMM_12); - break; - case R_AARCH64_LDST64_ABS_LO12_NC: - overflow_check = false; - ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 3, 9, - AARCH64_INSN_IMM_12); - break; - case R_AARCH64_LDST128_ABS_LO12_NC: - overflow_check = false; - ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 4, 8, - AARCH64_INSN_IMM_12); - break; - case R_AARCH64_TSTBR14: - ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 14, - AARCH64_INSN_IMM_14); - break; - case R_AARCH64_CONDBR19: - ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 19, - AARCH64_INSN_IMM_19); - break; - case R_AARCH64_JUMP26: - case R_AARCH64_CALL26: - ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 26, - AARCH64_INSN_IMM_26); - break; - - default: - pr_err("module %s: unsupported RELA relocation: %llu\n", - me->name, ELF64_R_TYPE(rel[i].r_info)); - return -ENOEXEC; - } - - if (overflow_check && ovf == -ERANGE) - goto overflow; + type = ELF64_R_TYPE(rel[i].r_info); + /* Check for overflow by default. */ + ret = static_relocate(me, type, loc, val); + if (ret) + return ret; } return 0; - -overflow: - pr_err("module %s: overflow in relocation type %d val %Lx\n", - me->name, (int)ELF64_R_TYPE(rel[i].r_info), val); - return -ENOEXEC; } int module_finalize(const Elf_Ehdr *hdr, -- 1.7.1 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [RFC PATCH 2/3] livepatch: module: arm64: extract the relocation code for reuse 2015-12-14 12:49 ` [RFC PATCH 2/3] livepatch: module: arm64: extract the relocation code for reuse Li Bin @ 2015-12-14 15:29 ` Josh Poimboeuf 0 siblings, 0 replies; 9+ messages in thread From: Josh Poimboeuf @ 2015-12-14 15:29 UTC (permalink / raw) To: linux-arm-kernel On Mon, Dec 14, 2015 at 08:49:37PM +0800, Li Bin wrote: > Livepatch can reuse the relocation codes of module loader, this > patch extract it. > > Signed-off-by: Li Bin <huawei.libin@huawei.com> FYI, this patch may be obsoleted by Jessica Yu's patches which are still under discussion: [RFC PATCH v2 0/6] (mostly) Arch-independent livepatch > --- > arch/arm64/include/asm/module.h | 3 + > arch/arm64/kernel/module.c | 360 ++++++++++++++++++++------------------- > 2 files changed, 187 insertions(+), 176 deletions(-) > > diff --git a/arch/arm64/include/asm/module.h b/arch/arm64/include/asm/module.h > index e80e232..78ac36e 100644 > --- a/arch/arm64/include/asm/module.h > +++ b/arch/arm64/include/asm/module.h > @@ -20,4 +20,7 @@ > > #define MODULE_ARCH_VERMAGIC "aarch64" > > +extern int static_relocate(struct module *mod, unsigned long type, > + void * loc, unsigned long value); > + > #endif /* __ASM_MODULE_H */ > diff --git a/arch/arm64/kernel/module.c b/arch/arm64/kernel/module.c > index f4bc779..6d1a1e3 100644 > --- a/arch/arm64/kernel/module.c > +++ b/arch/arm64/kernel/module.c > @@ -203,6 +203,184 @@ static int reloc_insn_imm(enum aarch64_reloc_op op, void *place, u64 val, > return 0; > } > > +int static_relocate(struct module *me, unsigned long type, void *loc, > + unsigned long val) > +{ > + int ovf = 0; > + bool overflow_check = true; > + /* Perform the static relocation. */ > + switch (type) { > + /* Null relocations. */ > + case R_ARM_NONE: > + case R_AARCH64_NONE: > + ovf = 0; > + break; > + > + /* Data relocations. */ > + case R_AARCH64_ABS64: > + overflow_check = false; > + ovf = reloc_data(RELOC_OP_ABS, loc, val, 64); > + break; > + case R_AARCH64_ABS32: > + ovf = reloc_data(RELOC_OP_ABS, loc, val, 32); > + break; > + case R_AARCH64_ABS16: > + ovf = reloc_data(RELOC_OP_ABS, loc, val, 16); > + break; > + case R_AARCH64_PREL64: > + overflow_check = false; > + ovf = reloc_data(RELOC_OP_PREL, loc, val, 64); > + break; > + case R_AARCH64_PREL32: > + ovf = reloc_data(RELOC_OP_PREL, loc, val, 32); > + break; > + case R_AARCH64_PREL16: > + ovf = reloc_data(RELOC_OP_PREL, loc, val, 16); > + break; > + > + /* MOVW instruction relocations. */ > + case R_AARCH64_MOVW_UABS_G0_NC: > + overflow_check = false; > + case R_AARCH64_MOVW_UABS_G0: > + ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0, > + AARCH64_INSN_IMM_16); > + break; > + case R_AARCH64_MOVW_UABS_G1_NC: > + overflow_check = false; > + case R_AARCH64_MOVW_UABS_G1: > + ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16, > + AARCH64_INSN_IMM_16); > + break; > + case R_AARCH64_MOVW_UABS_G2_NC: > + overflow_check = false; > + case R_AARCH64_MOVW_UABS_G2: > + ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32, > + AARCH64_INSN_IMM_16); > + break; > + case R_AARCH64_MOVW_UABS_G3: > + /* We're using the top bits so we can't overflow. */ > + overflow_check = false; > + ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 48, > + AARCH64_INSN_IMM_16); > + break; > + case R_AARCH64_MOVW_SABS_G0: > + ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0, > + AARCH64_INSN_IMM_MOVNZ); > + break; > + case R_AARCH64_MOVW_SABS_G1: > + ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16, > + AARCH64_INSN_IMM_MOVNZ); > + break; > + case R_AARCH64_MOVW_SABS_G2: > + ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32, > + AARCH64_INSN_IMM_MOVNZ); > + break; > + case R_AARCH64_MOVW_PREL_G0_NC: > + overflow_check = false; > + ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0, > + AARCH64_INSN_IMM_MOVK); > + break; > + case R_AARCH64_MOVW_PREL_G0: > + ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0, > + AARCH64_INSN_IMM_MOVNZ); > + break; > + case R_AARCH64_MOVW_PREL_G1_NC: > + overflow_check = false; > + ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16, > + AARCH64_INSN_IMM_MOVK); > + break; > + case R_AARCH64_MOVW_PREL_G1: > + ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16, > + AARCH64_INSN_IMM_MOVNZ); > + break; > + case R_AARCH64_MOVW_PREL_G2_NC: > + overflow_check = false; > + ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32, > + AARCH64_INSN_IMM_MOVK); > + break; > + case R_AARCH64_MOVW_PREL_G2: > + ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32, > + AARCH64_INSN_IMM_MOVNZ); > + break; > + case R_AARCH64_MOVW_PREL_G3: > + /* We're using the top bits so we can't overflow. */ > + overflow_check = false; > + ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 48, > + AARCH64_INSN_IMM_MOVNZ); > + break; > + > + /* Immediate instruction relocations. */ > + case R_AARCH64_LD_PREL_LO19: > + ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 19, > + AARCH64_INSN_IMM_19); > + break; > + case R_AARCH64_ADR_PREL_LO21: > + ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 0, 21, > + AARCH64_INSN_IMM_ADR); > + break; > +#ifndef CONFIG_ARM64_ERRATUM_843419 > + case R_AARCH64_ADR_PREL_PG_HI21_NC: > + overflow_check = false; > + case R_AARCH64_ADR_PREL_PG_HI21: > + ovf = reloc_insn_imm(RELOC_OP_PAGE, loc, val, 12, 21, > + AARCH64_INSN_IMM_ADR); > + break; > +#endif > + case R_AARCH64_ADD_ABS_LO12_NC: > + case R_AARCH64_LDST8_ABS_LO12_NC: > + overflow_check = false; > + ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 0, 12, > + AARCH64_INSN_IMM_12); > + break; > + case R_AARCH64_LDST16_ABS_LO12_NC: > + overflow_check = false; > + ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 1, 11, > + AARCH64_INSN_IMM_12); > + break; > + case R_AARCH64_LDST32_ABS_LO12_NC: > + overflow_check = false; > + ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 2, 10, > + AARCH64_INSN_IMM_12); > + break; > + case R_AARCH64_LDST64_ABS_LO12_NC: > + overflow_check = false; > + ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 3, 9, > + AARCH64_INSN_IMM_12); > + break; > + case R_AARCH64_LDST128_ABS_LO12_NC: > + overflow_check = false; > + ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 4, 8, > + AARCH64_INSN_IMM_12); > + break; > + case R_AARCH64_TSTBR14: > + ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 14, > + AARCH64_INSN_IMM_14); > + break; > + case R_AARCH64_CONDBR19: > + ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 19, > + AARCH64_INSN_IMM_19); > + break; > + case R_AARCH64_JUMP26: > + case R_AARCH64_CALL26: > + ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 26, > + AARCH64_INSN_IMM_26); > + break; > + > + default: > + pr_err("module %s: unsupported RELA relocation: %lu\n", > + me->name, type); > + return -ENOEXEC; > + } > + > + if (overflow_check && ovf == -ERANGE) { > + pr_err("module %s: overflow in relocation type %lu val %lx\n", > + me->name, type, val); > + return -ENOEXEC; > + } > + > + return 0; > +} > + > int apply_relocate_add(Elf64_Shdr *sechdrs, > const char *strtab, > unsigned int symindex, > @@ -210,12 +388,11 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, > struct module *me) > { > unsigned int i; > - int ovf; > - bool overflow_check; > Elf64_Sym *sym; > void *loc; > u64 val; > Elf64_Rela *rel = (void *)sechdrs[relsec].sh_addr; > + int type, ret; > > for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { > /* loc corresponds to P in the AArch64 ELF document. */ > @@ -229,184 +406,15 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, > /* val corresponds to (S + A) in the AArch64 ELF document. */ > val = sym->st_value + rel[i].r_addend; > > - /* Check for overflow by default. */ > - overflow_check = true; > - > - /* Perform the static relocation. */ > - switch (ELF64_R_TYPE(rel[i].r_info)) { > - /* Null relocations. */ > - case R_ARM_NONE: > - case R_AARCH64_NONE: > - ovf = 0; > - break; > - > - /* Data relocations. */ > - case R_AARCH64_ABS64: > - overflow_check = false; > - ovf = reloc_data(RELOC_OP_ABS, loc, val, 64); > - break; > - case R_AARCH64_ABS32: > - ovf = reloc_data(RELOC_OP_ABS, loc, val, 32); > - break; > - case R_AARCH64_ABS16: > - ovf = reloc_data(RELOC_OP_ABS, loc, val, 16); > - break; > - case R_AARCH64_PREL64: > - overflow_check = false; > - ovf = reloc_data(RELOC_OP_PREL, loc, val, 64); > - break; > - case R_AARCH64_PREL32: > - ovf = reloc_data(RELOC_OP_PREL, loc, val, 32); > - break; > - case R_AARCH64_PREL16: > - ovf = reloc_data(RELOC_OP_PREL, loc, val, 16); > - break; > - > - /* MOVW instruction relocations. */ > - case R_AARCH64_MOVW_UABS_G0_NC: > - overflow_check = false; > - case R_AARCH64_MOVW_UABS_G0: > - ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0, > - AARCH64_INSN_IMM_16); > - break; > - case R_AARCH64_MOVW_UABS_G1_NC: > - overflow_check = false; > - case R_AARCH64_MOVW_UABS_G1: > - ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16, > - AARCH64_INSN_IMM_16); > - break; > - case R_AARCH64_MOVW_UABS_G2_NC: > - overflow_check = false; > - case R_AARCH64_MOVW_UABS_G2: > - ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32, > - AARCH64_INSN_IMM_16); > - break; > - case R_AARCH64_MOVW_UABS_G3: > - /* We're using the top bits so we can't overflow. */ > - overflow_check = false; > - ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 48, > - AARCH64_INSN_IMM_16); > - break; > - case R_AARCH64_MOVW_SABS_G0: > - ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0, > - AARCH64_INSN_IMM_MOVNZ); > - break; > - case R_AARCH64_MOVW_SABS_G1: > - ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16, > - AARCH64_INSN_IMM_MOVNZ); > - break; > - case R_AARCH64_MOVW_SABS_G2: > - ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32, > - AARCH64_INSN_IMM_MOVNZ); > - break; > - case R_AARCH64_MOVW_PREL_G0_NC: > - overflow_check = false; > - ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0, > - AARCH64_INSN_IMM_MOVK); > - break; > - case R_AARCH64_MOVW_PREL_G0: > - ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0, > - AARCH64_INSN_IMM_MOVNZ); > - break; > - case R_AARCH64_MOVW_PREL_G1_NC: > - overflow_check = false; > - ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16, > - AARCH64_INSN_IMM_MOVK); > - break; > - case R_AARCH64_MOVW_PREL_G1: > - ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16, > - AARCH64_INSN_IMM_MOVNZ); > - break; > - case R_AARCH64_MOVW_PREL_G2_NC: > - overflow_check = false; > - ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32, > - AARCH64_INSN_IMM_MOVK); > - break; > - case R_AARCH64_MOVW_PREL_G2: > - ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32, > - AARCH64_INSN_IMM_MOVNZ); > - break; > - case R_AARCH64_MOVW_PREL_G3: > - /* We're using the top bits so we can't overflow. */ > - overflow_check = false; > - ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 48, > - AARCH64_INSN_IMM_MOVNZ); > - break; > - > - /* Immediate instruction relocations. */ > - case R_AARCH64_LD_PREL_LO19: > - ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 19, > - AARCH64_INSN_IMM_19); > - break; > - case R_AARCH64_ADR_PREL_LO21: > - ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 0, 21, > - AARCH64_INSN_IMM_ADR); > - break; > -#ifndef CONFIG_ARM64_ERRATUM_843419 > - case R_AARCH64_ADR_PREL_PG_HI21_NC: > - overflow_check = false; > - case R_AARCH64_ADR_PREL_PG_HI21: > - ovf = reloc_insn_imm(RELOC_OP_PAGE, loc, val, 12, 21, > - AARCH64_INSN_IMM_ADR); > - break; > -#endif > - case R_AARCH64_ADD_ABS_LO12_NC: > - case R_AARCH64_LDST8_ABS_LO12_NC: > - overflow_check = false; > - ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 0, 12, > - AARCH64_INSN_IMM_12); > - break; > - case R_AARCH64_LDST16_ABS_LO12_NC: > - overflow_check = false; > - ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 1, 11, > - AARCH64_INSN_IMM_12); > - break; > - case R_AARCH64_LDST32_ABS_LO12_NC: > - overflow_check = false; > - ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 2, 10, > - AARCH64_INSN_IMM_12); > - break; > - case R_AARCH64_LDST64_ABS_LO12_NC: > - overflow_check = false; > - ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 3, 9, > - AARCH64_INSN_IMM_12); > - break; > - case R_AARCH64_LDST128_ABS_LO12_NC: > - overflow_check = false; > - ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 4, 8, > - AARCH64_INSN_IMM_12); > - break; > - case R_AARCH64_TSTBR14: > - ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 14, > - AARCH64_INSN_IMM_14); > - break; > - case R_AARCH64_CONDBR19: > - ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 19, > - AARCH64_INSN_IMM_19); > - break; > - case R_AARCH64_JUMP26: > - case R_AARCH64_CALL26: > - ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 26, > - AARCH64_INSN_IMM_26); > - break; > - > - default: > - pr_err("module %s: unsupported RELA relocation: %llu\n", > - me->name, ELF64_R_TYPE(rel[i].r_info)); > - return -ENOEXEC; > - } > - > - if (overflow_check && ovf == -ERANGE) > - goto overflow; > + type = ELF64_R_TYPE(rel[i].r_info); > > + /* Check for overflow by default. */ > + ret = static_relocate(me, type, loc, val); > + if (ret) > + return ret; > } > > return 0; > - > -overflow: > - pr_err("module %s: overflow in relocation type %d val %Lx\n", > - me->name, (int)ELF64_R_TYPE(rel[i].r_info), val); > - return -ENOEXEC; > } > > int module_finalize(const Elf_Ehdr *hdr, > -- > 1.7.1 > -- Josh ^ permalink raw reply [flat|nested] 9+ messages in thread
* [RFC PATCH 3/3] livepatch: arm64: add support for livepatch on arm64 2015-12-14 12:49 [RFC PATCH 0/3] livepatch: arm64: add support for livepatch on arm64 Li Bin 2015-12-14 12:49 ` [RFC PATCH 1/3] livepatch: allow arch specific implementation Li Bin 2015-12-14 12:49 ` [RFC PATCH 2/3] livepatch: module: arm64: extract the relocation code for reuse Li Bin @ 2015-12-14 12:49 ` Li Bin 2015-12-14 15:27 ` [RFC PATCH 0/3] " Josh Poimboeuf 2015-12-15 15:43 ` Petr Mladek 4 siblings, 0 replies; 9+ messages in thread From: Li Bin @ 2015-12-14 12:49 UTC (permalink / raw) To: linux-arm-kernel 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 diff --git a/Makefile b/Makefile index d644f6e..6171fb8 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 871f217..85e01b1 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -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" diff --git a/arch/arm64/include/asm/livepatch.h b/arch/arm64/include/asm/livepatch.h new file mode 100644 index 0000000..9bf6c0b --- /dev/null +++ b/arch/arm64/include/asm/livepatch.h @@ -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 */ diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 474691f..59a415d 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -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 diff --git a/arch/arm64/kernel/livepatch.c b/arch/arm64/kernel/livepatch.c new file mode 100644 index 0000000..a06f710 --- /dev/null +++ b/arch/arm64/kernel/livepatch.c @@ -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 diff --git a/kernel/livepatch/Kconfig b/kernel/livepatch/Kconfig index 0450225..287bb79 100644 --- a/kernel/livepatch/Kconfig +++ b/kernel/livepatch/Kconfig @@ -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 -- 1.7.1 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [RFC PATCH 0/3] livepatch: arm64: add support for livepatch on arm64 2015-12-14 12:49 [RFC PATCH 0/3] livepatch: arm64: add support for livepatch on arm64 Li Bin ` (2 preceding siblings ...) 2015-12-14 12:49 ` [RFC PATCH 3/3] livepatch: arm64: add support for livepatch on arm64 Li Bin @ 2015-12-14 15:27 ` Josh Poimboeuf 2015-12-15 15:43 ` Petr Mladek 4 siblings, 0 replies; 9+ messages in thread From: Josh Poimboeuf @ 2015-12-14 15:27 UTC (permalink / raw) To: linux-arm-kernel On Mon, Dec 14, 2015 at 08:49:35PM +0800, Li Bin wrote: > This patchset depends on the on-going gcc feature "-fprolog-pad=N", > which will generate a pad of N nops at the beginning of each function. > > Livepatch on arm64 can using the 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. > > And this method will improve performance significantly compared with the > method based on ftrace, especially for the critical function being frequently > called. I think the arch-specific code in this patch set would be greatly affected by the consistency model when it finally arrives (but that's currently gated by getting stacktool merged, which should be soon). So I think it would be a good idea to wait until then before doing something like this. Is it possible to use ftrace instead? That would be a much better short term solution. -- Josh ^ permalink raw reply [flat|nested] 9+ messages in thread
* [RFC PATCH 0/3] livepatch: arm64: add support for livepatch on arm64 2015-12-14 12:49 [RFC PATCH 0/3] livepatch: arm64: add support for livepatch on arm64 Li Bin ` (3 preceding siblings ...) 2015-12-14 15:27 ` [RFC PATCH 0/3] " Josh Poimboeuf @ 2015-12-15 15:43 ` Petr Mladek 2015-12-16 6:04 ` Li Bin 4 siblings, 1 reply; 9+ messages in thread From: Petr Mladek @ 2015-12-15 15:43 UTC (permalink / raw) To: linux-arm-kernel On Mon 2015-12-14 20:49:35, Li Bin wrote: > This patchset depends on the on-going gcc feature "-fprolog-pad=N", > which will generate a pad of N nops at the beginning of each function. > > Livepatch on arm64 can using the 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. > > And this method will improve performance significantly compared with the > method based on ftrace, especially for the critical function being frequently > called. It sounds like a great feature for ftrace. If the new prologue is usable for LivePatching, it should be usable to call the ftrace handler as well. If you teach ftrace to use the new prologue, you will not need all these crazy arch-specific hacks for LivePatching. Then both ftrace and livepatch will benefit from the feature. I suggest to read the ftrace documentation in Documentation/trace/, especially ftrace-design.txt. I have never ported ftrace to a new architecture. I guess that you need to teach scripts/recordmcount.c to find the new location. Also you might need to update arch/arm/kernel/ftrace.c. Also please make sure that ftrace supports DYNAMIC_FTRACE_WITH_REGS on Arm. It is harder but the right way to go. Your current patch set looks like a hack. Best Regards, Petr ^ permalink raw reply [flat|nested] 9+ messages in thread
* [RFC PATCH 0/3] livepatch: arm64: add support for livepatch on arm64 2015-12-15 15:43 ` Petr Mladek @ 2015-12-16 6:04 ` Li Bin 2015-12-17 12:36 ` Petr Mladek 0 siblings, 1 reply; 9+ messages in thread From: Li Bin @ 2015-12-16 6:04 UTC (permalink / raw) To: linux-arm-kernel on 2015/12/15 23:43, Petr Mladek wrote: > On Mon 2015-12-14 20:49:35, Li Bin wrote: >> This patchset depends on the on-going gcc feature "-fprolog-pad=N", >> which will generate a pad of N nops at the beginning of each function. >> >> Livepatch on arm64 can using the 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. >> >> And this method will improve performance significantly compared with the >> method based on ftrace, especially for the critical function being frequently >> called. > It sounds like a great feature for ftrace. If the new prologue is usable > for LivePatching, it should be usable to call the ftrace handler as > well. If you teach ftrace to use the new prologue, you will not need > all these crazy arch-specific hacks for LivePatching. Then both ftrace > and livepatch will benefit from the feature. > > I suggest to read the ftrace documentation in Documentation/trace/, > especially ftrace-design.txt. I have never ported ftrace to a new > architecture. I guess that you need to teach scripts/recordmcount.c > to find the new location. Also you might need to update > arch/arm/kernel/ftrace.c. Also please make sure that ftrace > supports DYNAMIC_FTRACE_WITH_REGS on Arm. Hi Petr, I had posted one method for livepatch implementaion on arm64 based on ftrace with DYNAMIC_FTRACE_WITH_REGS. https://lwn.net/Articles/646317/ This requires GCC changes to support it, and I propose a method that implement -mfentry feature which following some other arch such as x86/mips/s390 etc. This method is architecture-specific, and Maxim Kuvyrkov propose a new method for gcc that implement a target-independent option -fprolog-pad=N, which will generate a pad of N nops at the beginning of each function. https://gcc.gnu.org/ml/gcc/2015-05/msg00267.html And based on this, DYNAMIC_FTRACE_WITH_REGS feature can be implemented as mentioned in: http://lists.infradead.org/pipermail/linux-arm-kernel/2015-November/386815.html And in this patchset, I only concern the performance for livepatch on arm64, and I hope this also can be resolved by improving ftrace, and thanks to Steve that he pointed that he is working on a way to make ftrace a bit better for livepatch. Thanks, Li Bin > It is harder but the right way to go. Your current patch set > looks like a hack. > > Best Regards, > Petr > > . > ^ permalink raw reply [flat|nested] 9+ messages in thread
* [RFC PATCH 0/3] livepatch: arm64: add support for livepatch on arm64 2015-12-16 6:04 ` Li Bin @ 2015-12-17 12:36 ` Petr Mladek 0 siblings, 0 replies; 9+ messages in thread From: Petr Mladek @ 2015-12-17 12:36 UTC (permalink / raw) To: linux-arm-kernel On Wed 2015-12-16 14:04:33, Li Bin wrote: > > > on 2015/12/15 23:43, Petr Mladek wrote: > > On Mon 2015-12-14 20:49:35, Li Bin wrote: > >> This patchset depends on the on-going gcc feature "-fprolog-pad=N", > >> which will generate a pad of N nops at the beginning of each function. > >> > >> Livepatch on arm64 can using the 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. > >> > >> And this method will improve performance significantly compared with the > >> method based on ftrace, especially for the critical function being frequently > >> called. > > It sounds like a great feature for ftrace. If the new prologue is usable > > for LivePatching, it should be usable to call the ftrace handler as > > well. If you teach ftrace to use the new prologue, you will not need > > all these crazy arch-specific hacks for LivePatching. Then both ftrace > > and livepatch will benefit from the feature. > > > > I suggest to read the ftrace documentation in Documentation/trace/, > > especially ftrace-design.txt. I have never ported ftrace to a new > > architecture. I guess that you need to teach scripts/recordmcount.c > > to find the new location. Also you might need to update > > arch/arm/kernel/ftrace.c. Also please make sure that ftrace > > supports DYNAMIC_FTRACE_WITH_REGS on Arm. > > Hi Petr, > I had posted one method for livepatch implementaion on arm64 based on ftrace with > DYNAMIC_FTRACE_WITH_REGS. > https://lwn.net/Articles/646317/ I see. What are the plans with this patchset, please? > This requires GCC changes to support it, and I propose a method that implement -mfentry > feature which following some other arch such as x86/mips/s390 etc. This method is > architecture-specific, and Maxim Kuvyrkov propose a new method for gcc that implement > a target-independent option -fprolog-pad=N, which will generate a pad of N nops at the > beginning of each function. > https://gcc.gnu.org/ml/gcc/2015-05/msg00267.html > > And based on this, DYNAMIC_FTRACE_WITH_REGS feature can be implemented as > mentioned in: > http://lists.infradead.org/pipermail/linux-arm-kernel/2015-November/386815.html What will happen if DYNAMIC_FTRACE_WITH_REGS is implemented using -fprolog-pad=N, please? Will ftrace and livepatch try to modify the same location? Or do you plan to revert all these changes then? > And in this patchset, I only concern the performance for livepatch on arm64, and I > hope this also can be resolved by improving ftrace, and thanks to Steve that he pointed > that he is working on a way to make ftrace a bit better for livepatch. What is the exact performance optimization? Is it that you call the new functiion (from the patch) dirrectly instead of using the generic ftrace handler? This will break once ftrace start using this location. Also this will get more problematic once we have the consistency mode as pointed out by Josh. Please, note that there are currently three features that need to modify the function entry code: Ftrace, Kprobes, and LivePatch. They need to be coordinated. This is why also Kprobes are called via Ftrace when available. Your patch set looks like a temporary hack to me. I suggest you to concentrace on implemention DYNAMIC_FTRACE_WITH_REGS on Arm. If you are not happy with the ftrace performance, you might work on improving it. All three features: Ftrace, Kprobes, and LivePatch will benefit from it. Modyfying the code directly from LivePatch looks like a way to a maintenance hell. Best Regards, Petr ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2015-12-17 12:36 UTC | newest] Thread overview: 9+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2015-12-14 12:49 [RFC PATCH 0/3] livepatch: arm64: add support for livepatch on arm64 Li Bin 2015-12-14 12:49 ` [RFC PATCH 1/3] livepatch: allow arch specific implementation Li Bin 2015-12-14 12:49 ` [RFC PATCH 2/3] livepatch: module: arm64: extract the relocation code for reuse Li Bin 2015-12-14 15:29 ` Josh Poimboeuf 2015-12-14 12:49 ` [RFC PATCH 3/3] livepatch: arm64: add support for livepatch on arm64 Li Bin 2015-12-14 15:27 ` [RFC PATCH 0/3] " Josh Poimboeuf 2015-12-15 15:43 ` Petr Mladek 2015-12-16 6:04 ` Li Bin 2015-12-17 12:36 ` Petr Mladek
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.