From patchwork Fri Jun 3 03:26:21 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Long X-Patchwork-Id: 9151605 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 089B56074E for ; Fri, 3 Jun 2016 03:29:33 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id EC41F28328 for ; Fri, 3 Jun 2016 03:29:32 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id E0A8F2832D; Fri, 3 Jun 2016 03:29:32 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.1 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_MED,T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 077C728328 for ; Fri, 3 Jun 2016 03:29:32 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1b8fm9-0001w7-3I; Fri, 03 Jun 2016 03:27:57 +0000 Received: from mail-qt0-x230.google.com ([2607:f8b0:400d:c0d::230]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1b8flF-0001Gg-R1 for linux-arm-kernel@lists.infradead.org; Fri, 03 Jun 2016 03:27:06 +0000 Received: by mail-qt0-x230.google.com with SMTP id q45so8497175qtq.1 for ; Thu, 02 Jun 2016 20:26:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=fdsjJq61iz+59IDSNM2FxJjAYa76wUiEIwAxS++YP48=; b=TwaPYipsBB1Dpb3yiSmg4ynCx32Zhq3DaGTm+1clLFXx3EI1IS1vtJLDSsmgdVDQL/ yHGw4LUI4siNFpN6+nFIG/0n74NaOalI72ps9TeF5yC+JszRvexoEyS/SSxI7NSgp+ry hCX2l8/PXkwf3rGEc5F36paQtB5MVddqhFJEQ= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=fdsjJq61iz+59IDSNM2FxJjAYa76wUiEIwAxS++YP48=; b=dT1qMcQswMLzTEJHbQQg6TwPPK5UN/SZBIlS6A07xTQ+KYaRBQzEo5GuejwqzxXFum YJR/eZRm+nEfkCbYAxMCJFxaOVbokhtvEjANCHA4oA21B6HjnrAfjaNeZ0iRa4GRWdtO tfR0KA3xxCHcfB4fYrwDtHZKuuvkbgT8PkEZXrmmlQWUqDxFHW5z+j1TEOs+B+jYGnfi oMtKZYDW0JVCSXrp2hiQdkIIzoi2f8XC/41xXcSkK57aH5lj0Ep6PGqLQ424QPEH96Bo Ov9lC8icyX01jEhWYJ6sQd9ttTfY3YHnHuoktmbql1Gsz+axGmwHLTqbkFyfCwx3XF5E oCsg== X-Gm-Message-State: ALyK8tK+MwOkei8r6GS3k11Sq/habi7nwIMiw+kcDP80Um+ioGPf+BanDSKsoJxGTwVsCRuA X-Received: by 10.200.39.80 with SMTP id h16mr1265516qth.41.1464924400530; Thu, 02 Jun 2016 20:26:40 -0700 (PDT) Received: from localhost.localdomain (pool-72-71-243-181.cncdnh.fast00.myfairpoint.net. [72.71.243.181]) by smtp.googlemail.com with ESMTPSA id 30sm359841qgx.19.2016.06.02.20.26.38 (version=TLSv1/SSLv3 cipher=OTHER); Thu, 02 Jun 2016 20:26:40 -0700 (PDT) From: David Long To: Catalin Marinas , Huang Shijie , James Morse , Marc Zyngier , Pratyush Anand , Sandeepa Prabhu , Will Deacon , William Cohen , linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Steve Capper , Masami Hiramatsu , Li Bin Subject: [PATCH v13 07/10] arm64: kprobes instruction simulation support Date: Thu, 2 Jun 2016 23:26:21 -0400 Message-Id: <1464924384-15269-8-git-send-email-dave.long@linaro.org> X-Mailer: git-send-email 2.5.0 In-Reply-To: <1464924384-15269-1-git-send-email-dave.long@linaro.org> References: <1464924384-15269-1-git-send-email-dave.long@linaro.org> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20160602_202702_253072_A0283E06 X-CRM114-Status: GOOD ( 26.86 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Jisheng Zhang , Mark Rutland , Daniel Thompson , Vladimir Murzin , Petr Mladek , Ard Biesheuvel , Jens Wiklander , Robin Murphy , Mark Brown , Suzuki K Poulose , Dave P Martin , Andrey Ryabinin , yalin wang , Yang Shi , Zi Shen Lim , John Blackwood , Andrew Morton , =?UTF-8?q?Alex=20Benn=C3=A9e?= , Adam Buchbinder , Christoffer Dall MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP From: Sandeepa Prabhu Kprobes needs simulation of instructions that cannot be stepped from a different memory location, e.g.: those instructions that uses PC-relative addressing. In simulation, the behaviour of the instruction is implemented using a copy of pt_regs. The following instruction categories are simulated: - All branching instructions(conditional, register, and immediate) - Literal access instructions(load-literal, adr/adrp) Conditional execution is limited to branching instructions in ARM v8. If conditions at PSTATE do not match the condition fields of opcode, the instruction is effectively NOP. Thanks to Will Cohen for assorted suggested changes. Signed-off-by: Sandeepa Prabhu Signed-off-by: William Cohen Signed-off-by: David A. Long --- arch/arm64/include/asm/insn.h | 1 + arch/arm64/include/asm/probes.h | 5 +- arch/arm64/kernel/Makefile | 3 +- arch/arm64/kernel/insn.c | 1 + arch/arm64/kernel/kprobes-arm64.c | 32 ++++- arch/arm64/kernel/kprobes.c | 53 ++++++-- arch/arm64/kernel/probes-simulate-insn.c | 218 +++++++++++++++++++++++++++++++ arch/arm64/kernel/probes-simulate-insn.h | 28 ++++ 8 files changed, 325 insertions(+), 16 deletions(-) create mode 100644 arch/arm64/kernel/probes-simulate-insn.c create mode 100644 arch/arm64/kernel/probes-simulate-insn.h diff --git a/arch/arm64/include/asm/insn.h b/arch/arm64/include/asm/insn.h index be2d2b9..1339f24 100644 --- a/arch/arm64/include/asm/insn.h +++ b/arch/arm64/include/asm/insn.h @@ -411,6 +411,7 @@ u32 aarch32_insn_mcr_extract_crm(u32 insn); typedef bool (pstate_check_t)(unsigned long); extern pstate_check_t * const opcode_condition_checks[16]; + #endif /* __ASSEMBLY__ */ #endif /* __ASM_INSN_H */ diff --git a/arch/arm64/include/asm/probes.h b/arch/arm64/include/asm/probes.h index c5fcbe6..d524f7d 100644 --- a/arch/arm64/include/asm/probes.h +++ b/arch/arm64/include/asm/probes.h @@ -15,11 +15,12 @@ #ifndef _ARM_PROBES_H #define _ARM_PROBES_H +#include + struct kprobe; struct arch_specific_insn; typedef u32 kprobe_opcode_t; -typedef unsigned long (kprobes_pstate_check_t)(unsigned long); typedef void (kprobes_handler_t) (u32 opcode, long addr, struct pt_regs *); enum pc_restore_type { @@ -35,7 +36,7 @@ struct kprobe_pc_restore { /* architecture specific copy of original instruction */ struct arch_specific_insn { kprobe_opcode_t *insn; - kprobes_pstate_check_t *pstate_cc; + pstate_check_t *pstate_cc; kprobes_handler_t *handler; /* restore address after step xol */ struct kprobe_pc_restore restore; diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index d78ed62..46724a1 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -37,7 +37,8 @@ arm64-obj-$(CONFIG_CPU_PM) += sleep.o suspend.o arm64-obj-$(CONFIG_CPU_IDLE) += cpuidle.o arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.o arm64-obj-$(CONFIG_KGDB) += kgdb.o -arm64-obj-$(CONFIG_KPROBES) += kprobes.o kprobes-arm64.o +arm64-obj-$(CONFIG_KPROBES) += kprobes.o kprobes-arm64.o \ + probes-simulate-insn.o arm64-obj-$(CONFIG_EFI) += efi.o efi-entry.stub.o arm64-obj-$(CONFIG_PCI) += pci.o arm64-obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o diff --git a/arch/arm64/kernel/insn.c b/arch/arm64/kernel/insn.c index 4585b72..f949e4f 100644 --- a/arch/arm64/kernel/insn.c +++ b/arch/arm64/kernel/insn.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #define AARCH64_INSN_SF_BIT BIT(31) diff --git a/arch/arm64/kernel/kprobes-arm64.c b/arch/arm64/kernel/kprobes-arm64.c index 0af5d94..28b9c5b 100644 --- a/arch/arm64/kernel/kprobes-arm64.c +++ b/arch/arm64/kernel/kprobes-arm64.c @@ -21,6 +21,7 @@ #include #include "kprobes-arm64.h" +#include "probes-simulate-insn.h" static bool __kprobes aarch64_insn_is_steppable(u32 insn) { @@ -85,8 +86,37 @@ arm_probe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi) */ if (aarch64_insn_is_steppable(insn)) return INSN_GOOD; - else + + if (aarch64_insn_is_bcond(insn)) { + asi->handler = simulate_b_cond; + } else if (aarch64_insn_is_cbz(insn) || + aarch64_insn_is_cbnz(insn)) { + asi->handler = simulate_cbz_cbnz; + } else if (aarch64_insn_is_tbz(insn) || + aarch64_insn_is_tbnz(insn)) { + asi->handler = simulate_tbz_tbnz; + } else if (aarch64_insn_is_adr_adrp(insn)) { + asi->handler = simulate_adr_adrp; + } else if (aarch64_insn_is_b(insn) || + aarch64_insn_is_bl(insn)) { + asi->handler = simulate_b_bl; + } else if (aarch64_insn_is_br(insn) || + aarch64_insn_is_blr(insn) || + aarch64_insn_is_ret(insn)) { + asi->handler = simulate_br_blr_ret; + } else if (aarch64_insn_is_ldr_lit(insn)) { + asi->handler = simulate_ldr_literal; + } else if (aarch64_insn_is_ldrsw_lit(insn)) { + asi->handler = simulate_ldrsw_literal; + } else { + /* + * Instruction cannot be stepped out-of-line and we don't + * (yet) simulate it. + */ return INSN_REJECTED; + } + + return INSN_GOOD_NO_SLOT; } static bool __kprobes diff --git a/arch/arm64/kernel/kprobes.c b/arch/arm64/kernel/kprobes.c index ba43623..9d0ad47 100644 --- a/arch/arm64/kernel/kprobes.c +++ b/arch/arm64/kernel/kprobes.c @@ -45,6 +45,9 @@ void jprobe_return_break(void); DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); +static void __kprobes +post_kprobe_handler(struct kprobe_ctlblk *, struct pt_regs *); + static void __kprobes arch_prepare_ss_slot(struct kprobe *p) { /* prepare insn slot */ @@ -62,6 +65,24 @@ static void __kprobes arch_prepare_ss_slot(struct kprobe *p) p->ainsn.restore.type = RESTORE_PC; } +static void __kprobes arch_prepare_simulate(struct kprobe *p) +{ + /* This instructions is not executed xol. No need to adjust the PC */ + p->ainsn.restore.addr = 0; + p->ainsn.restore.type = NO_RESTORE; +} + +static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs) +{ + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + + if (p->ainsn.handler) + p->ainsn.handler((u32)p->opcode, (long)p->addr, regs); + + /* single step simulated, now go for post processing */ + post_kprobe_handler(kcb, regs); +} + int __kprobes arch_prepare_kprobe(struct kprobe *p) { unsigned long probe_addr = (unsigned long)p->addr; @@ -86,7 +107,8 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p) return -EINVAL; case INSN_GOOD_NO_SLOT: /* insn need simulation */ - return -EINVAL; + p->ainsn.insn = NULL; + break; case INSN_GOOD: /* instruction uses slot */ p->ainsn.insn = get_insn_slot(); @@ -96,7 +118,10 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p) }; /* prepare the instruction */ - arch_prepare_ss_slot(p); + if (p->ainsn.insn) + arch_prepare_ss_slot(p); + else + arch_prepare_simulate(p); return 0; } @@ -222,20 +247,24 @@ static void __kprobes setup_singlestep(struct kprobe *p, kcb->kprobe_status = KPROBE_HIT_SS; } - BUG_ON(!p->ainsn.insn); - /* prepare for single stepping */ - slot = (unsigned long)p->ainsn.insn; + if (p->ainsn.insn) { + /* prepare for single stepping */ + slot = (unsigned long)p->ainsn.insn; - set_ss_context(kcb, slot); /* mark pending ss */ + set_ss_context(kcb, slot); /* mark pending ss */ - if (kcb->kprobe_status == KPROBE_REENTER) - spsr_set_debug_flag(regs, 0); + if (kcb->kprobe_status == KPROBE_REENTER) + spsr_set_debug_flag(regs, 0); - /* IRQs and single stepping do not mix well. */ - kprobes_save_local_irqflag(kcb, regs); - kernel_enable_single_step(regs); - instruction_pointer(regs) = slot; + /* IRQs and single stepping do not mix well. */ + kprobes_save_local_irqflag(kcb, regs); + kernel_enable_single_step(regs); + instruction_pointer(regs) = slot; + } else { + /* insn simulation */ + arch_simulate_insn(p, regs); + } } static int __kprobes reenter_kprobe(struct kprobe *p, diff --git a/arch/arm64/kernel/probes-simulate-insn.c b/arch/arm64/kernel/probes-simulate-insn.c new file mode 100644 index 0000000..726caaa --- /dev/null +++ b/arch/arm64/kernel/probes-simulate-insn.c @@ -0,0 +1,218 @@ +/* + * arch/arm64/kernel/probes-simulate-insn.c + * + * Copyright (C) 2013 Linaro Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + */ + +#include +#include +#include + +#include "probes-simulate-insn.h" + +#define sign_extend(x, signbit) \ + ((x) | (0 - ((x) & (1 << (signbit))))) + +#define bbl_displacement(insn) \ + sign_extend(((insn) & 0x3ffffff) << 2, 27) + +#define bcond_displacement(insn) \ + sign_extend(((insn >> 5) & 0x7ffff) << 2, 20) + +#define cbz_displacement(insn) \ + sign_extend(((insn >> 5) & 0x7ffff) << 2, 20) + +#define tbz_displacement(insn) \ + sign_extend(((insn >> 5) & 0x3fff) << 2, 15) + +#define ldr_displacement(insn) \ + sign_extend(((insn >> 5) & 0x7ffff) << 2, 20) + +static inline void set_x_reg(struct pt_regs *regs, int reg, u64 val) +{ + if (reg < 31) + regs->regs[reg] = val; +} + +static inline void set_w_reg(struct pt_regs *regs, int reg, u64 val) +{ + if (reg < 31) + regs->regs[reg] = lower_32_bits(val); +} + +static inline u64 get_x_reg(struct pt_regs *regs, int reg) +{ + if (reg < 31) + return regs->regs[reg]; + else + return 0; +} + +static inline u32 get_w_reg(struct pt_regs *regs, int reg) +{ + if (reg < 31) + return lower_32_bits(regs->regs[reg]); + else + return 0; +} + +static bool __kprobes check_cbz(u32 opcode, struct pt_regs *regs) +{ + int xn = opcode & 0x1f; + + return (opcode & (1 << 31)) ? + (get_x_reg(regs, xn) == 0) : (get_w_reg(regs, xn) == 0); +} + +static bool __kprobes check_cbnz(u32 opcode, struct pt_regs *regs) +{ + int xn = opcode & 0x1f; + + return (opcode & (1 << 31)) ? + (get_x_reg(regs, xn) != 0) : (get_w_reg(regs, xn) != 0); +} + +static bool __kprobes check_tbz(u32 opcode, struct pt_regs *regs) +{ + int xn = opcode & 0x1f; + int bit_pos = ((opcode & (1 << 31)) >> 26) | ((opcode >> 19) & 0x1f); + + return ((get_x_reg(regs, xn) >> bit_pos) & 0x1) == 0; +} + +static bool __kprobes check_tbnz(u32 opcode, struct pt_regs *regs) +{ + int xn = opcode & 0x1f; + int bit_pos = ((opcode & (1 << 31)) >> 26) | ((opcode >> 19) & 0x1f); + + return ((get_x_reg(regs, xn) >> bit_pos) & 0x1) != 0; +} + +/* + * instruction simulation functions + */ +void __kprobes +simulate_adr_adrp(u32 opcode, long addr, struct pt_regs *regs) +{ + long imm, xn, val; + + xn = opcode & 0x1f; + imm = ((opcode >> 3) & 0x1ffffc) | ((opcode >> 29) & 0x3); + imm = sign_extend(imm, 20); + if (opcode & 0x80000000) + val = (imm<<12) + (addr & 0xfffffffffffff000); + else + val = imm + addr; + + set_x_reg(regs, xn, val); + + instruction_pointer(regs) += 4; +} + +void __kprobes +simulate_b_bl(u32 opcode, long addr, struct pt_regs *regs) +{ + int disp = bbl_displacement(opcode); + + /* Link register is x30 */ + if (opcode & (1 << 31)) + set_x_reg(regs, 30, addr + 4); + + instruction_pointer(regs) = addr + disp; +} + +void __kprobes +simulate_b_cond(u32 opcode, long addr, struct pt_regs *regs) +{ + int disp = 4; + + if (opcode_condition_checks[opcode & 0xf](regs->pstate & 0xffffffff)) + disp = bcond_displacement(opcode); + + instruction_pointer(regs) = addr + disp; +} + +void __kprobes +simulate_br_blr_ret(u32 opcode, long addr, struct pt_regs *regs) +{ + int xn = (opcode >> 5) & 0x1f; + + /* update pc first in case we're doing a "blr lr" */ + instruction_pointer(regs) = get_x_reg(regs, xn); + + /* Link register is x30 */ + if (((opcode >> 21) & 0x3) == 1) + set_x_reg(regs, 30, addr + 4); +} + +void __kprobes +simulate_cbz_cbnz(u32 opcode, long addr, struct pt_regs *regs) +{ + int disp = 4; + + if (opcode & (1 << 24)) { + if (check_cbnz(opcode, regs)) + disp = cbz_displacement(opcode); + } else { + if (check_cbz(opcode, regs)) + disp = cbz_displacement(opcode); + } + instruction_pointer(regs) = addr + disp; +} + +void __kprobes +simulate_tbz_tbnz(u32 opcode, long addr, struct pt_regs *regs) +{ + int disp = 4; + + if (opcode & (1 << 24)) { + if (check_tbnz(opcode, regs)) + disp = tbz_displacement(opcode); + } else { + if (check_tbz(opcode, regs)) + disp = tbz_displacement(opcode); + } + instruction_pointer(regs) = addr + disp; +} + +void __kprobes +simulate_ldr_literal(u32 opcode, long addr, struct pt_regs *regs) +{ + u64 *load_addr; + int xn = opcode & 0x1f; + int disp; + + disp = ldr_displacement(opcode); + load_addr = (u64 *) (addr + disp); + + if (opcode & (1 << 30)) /* x0-x30 */ + set_x_reg(regs, xn, *load_addr); + else /* w0-w30 */ + set_w_reg(regs, xn, *load_addr); + + instruction_pointer(regs) += 4; +} + +void __kprobes +simulate_ldrsw_literal(u32 opcode, long addr, struct pt_regs *regs) +{ + s32 *load_addr; + int xn = opcode & 0x1f; + int disp; + + disp = ldr_displacement(opcode); + load_addr = (s32 *) (addr + disp); + + set_x_reg(regs, xn, *load_addr); + + instruction_pointer(regs) += 4; +} diff --git a/arch/arm64/kernel/probes-simulate-insn.h b/arch/arm64/kernel/probes-simulate-insn.h new file mode 100644 index 0000000..d6bb9a5 --- /dev/null +++ b/arch/arm64/kernel/probes-simulate-insn.h @@ -0,0 +1,28 @@ +/* + * arch/arm64/kernel/probes-simulate-insn.h + * + * Copyright (C) 2013 Linaro Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + */ + +#ifndef _ARM_KERNEL_PROBES_SIMULATE_INSN_H +#define _ARM_KERNEL_PROBES_SIMULATE_INSN_H + +void simulate_adr_adrp(u32 opcode, long addr, struct pt_regs *regs); +void simulate_b_bl(u32 opcode, long addr, struct pt_regs *regs); +void simulate_b_cond(u32 opcode, long addr, struct pt_regs *regs); +void simulate_br_blr_ret(u32 opcode, long addr, struct pt_regs *regs); +void simulate_cbz_cbnz(u32 opcode, long addr, struct pt_regs *regs); +void simulate_tbz_tbnz(u32 opcode, long addr, struct pt_regs *regs); +void simulate_ldr_literal(u32 opcode, long addr, struct pt_regs *regs); +void simulate_ldrsw_literal(u32 opcode, long addr, struct pt_regs *regs); + +#endif /* _ARM_KERNEL_PROBES_SIMULATE_INSN_H */