Loading arch/arm64/kernel/livepatch.c +30 −17 Original line number Diff line number Diff line Loading @@ -358,6 +358,27 @@ long arch_klp_save_old_code(struct arch_klp_data *arch_data, void *old_func) return ret; } static int klp_patch_text(u32 *dst, const u32 *src, int len) { int i; int ret; if (len <= 0) return -EINVAL; /* skip breakpoint at first */ for (i = 1; i < len; i++) { ret = aarch64_insn_patch_text_nosync(dst + i, src[i]); if (ret) return ret; } /* * Avoid compile optimization, make sure that instructions * except first breakpoint has been patched. */ barrier(); return aarch64_insn_patch_text_nosync(dst, src[0]); } static int do_patch(unsigned long pc, unsigned long new_addr) { u32 insns[LJMP_INSN_SIZE]; Loading @@ -366,27 +387,23 @@ static int do_patch(unsigned long pc, unsigned long new_addr) if (offset_in_range(pc, new_addr, SZ_128M)) { insns[0] = aarch64_insn_gen_branch_imm(pc, new_addr, AARCH64_INSN_BRANCH_NOLINK); ret = aarch64_insn_patch_text_nosync((void *)pc, insns[0]); ret = klp_patch_text((u32 *)pc, insns, 1); if (ret) { pr_err("patch instruction small range failed, ret=%d\n", ret); return -EPERM; } } else { #ifdef CONFIG_ARM64_MODULE_PLTS int i; insns[0] = 0x92800010 | (((~new_addr) & 0xffff)) << 5; insns[1] = 0xf2a00010 | (((new_addr >> 16) & 0xffff)) << 5; insns[2] = 0xf2c00010 | (((new_addr >> 32) & 0xffff)) << 5; insns[3] = 0xd61f0200; for (i = 0; i < LJMP_INSN_SIZE; i++) { ret = aarch64_insn_patch_text_nosync(((u32 *)pc) + i, insns[i]); ret = klp_patch_text((u32 *)pc, insns, LJMP_INSN_SIZE); if (ret) { pr_err("patch instruction %d large range failed, ret=%d\n", i, ret); pr_err("patch instruction large range failed, ret=%d\n", ret); return -EPERM; } } #else /* * When offset from 'new_addr' to 'pc' is out of SZ_128M range but Loading Loading @@ -417,21 +434,17 @@ void arch_klp_unpatch_func(struct klp_func *func) struct klp_func_node *func_node; struct klp_func *next_func; unsigned long pc; int i; int ret; func_node = func->func_node; pc = (unsigned long)func_node->old_func; list_del_rcu(&func->stack_node); if (list_empty(&func_node->func_stack)) { for (i = 0; i < LJMP_INSN_SIZE; i++) { ret = aarch64_insn_patch_text_nosync(((u32 *)pc) + i, func_node->arch_data.old_insns[i]); ret = klp_patch_text((u32 *)pc, func_node->arch_data.old_insns, LJMP_INSN_SIZE); if (ret) { pr_err("restore instruction %d failed, ret=%d\n", i, ret); pr_err("restore instruction failed, ret=%d\n", ret); return; } } } else { next_func = list_first_or_null_rcu(&func_node->func_stack, struct klp_func, stack_node); Loading Loading
arch/arm64/kernel/livepatch.c +30 −17 Original line number Diff line number Diff line Loading @@ -358,6 +358,27 @@ long arch_klp_save_old_code(struct arch_klp_data *arch_data, void *old_func) return ret; } static int klp_patch_text(u32 *dst, const u32 *src, int len) { int i; int ret; if (len <= 0) return -EINVAL; /* skip breakpoint at first */ for (i = 1; i < len; i++) { ret = aarch64_insn_patch_text_nosync(dst + i, src[i]); if (ret) return ret; } /* * Avoid compile optimization, make sure that instructions * except first breakpoint has been patched. */ barrier(); return aarch64_insn_patch_text_nosync(dst, src[0]); } static int do_patch(unsigned long pc, unsigned long new_addr) { u32 insns[LJMP_INSN_SIZE]; Loading @@ -366,27 +387,23 @@ static int do_patch(unsigned long pc, unsigned long new_addr) if (offset_in_range(pc, new_addr, SZ_128M)) { insns[0] = aarch64_insn_gen_branch_imm(pc, new_addr, AARCH64_INSN_BRANCH_NOLINK); ret = aarch64_insn_patch_text_nosync((void *)pc, insns[0]); ret = klp_patch_text((u32 *)pc, insns, 1); if (ret) { pr_err("patch instruction small range failed, ret=%d\n", ret); return -EPERM; } } else { #ifdef CONFIG_ARM64_MODULE_PLTS int i; insns[0] = 0x92800010 | (((~new_addr) & 0xffff)) << 5; insns[1] = 0xf2a00010 | (((new_addr >> 16) & 0xffff)) << 5; insns[2] = 0xf2c00010 | (((new_addr >> 32) & 0xffff)) << 5; insns[3] = 0xd61f0200; for (i = 0; i < LJMP_INSN_SIZE; i++) { ret = aarch64_insn_patch_text_nosync(((u32 *)pc) + i, insns[i]); ret = klp_patch_text((u32 *)pc, insns, LJMP_INSN_SIZE); if (ret) { pr_err("patch instruction %d large range failed, ret=%d\n", i, ret); pr_err("patch instruction large range failed, ret=%d\n", ret); return -EPERM; } } #else /* * When offset from 'new_addr' to 'pc' is out of SZ_128M range but Loading Loading @@ -417,21 +434,17 @@ void arch_klp_unpatch_func(struct klp_func *func) struct klp_func_node *func_node; struct klp_func *next_func; unsigned long pc; int i; int ret; func_node = func->func_node; pc = (unsigned long)func_node->old_func; list_del_rcu(&func->stack_node); if (list_empty(&func_node->func_stack)) { for (i = 0; i < LJMP_INSN_SIZE; i++) { ret = aarch64_insn_patch_text_nosync(((u32 *)pc) + i, func_node->arch_data.old_insns[i]); ret = klp_patch_text((u32 *)pc, func_node->arch_data.old_insns, LJMP_INSN_SIZE); if (ret) { pr_err("restore instruction %d failed, ret=%d\n", i, ret); pr_err("restore instruction failed, ret=%d\n", ret); return; } } } else { next_func = list_first_or_null_rcu(&func_node->func_stack, struct klp_func, stack_node); Loading