From patchwork Sun Oct 14 19:23:13 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rabin Vincent X-Patchwork-Id: 1591661 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) by patchwork1.kernel.org (Postfix) with ESMTP id A690B40135 for ; Sun, 14 Oct 2012 19:27:43 +0000 (UTC) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1TNToZ-0006at-9Y; Sun, 14 Oct 2012 19:25:31 +0000 Received: from mail-lb0-f177.google.com ([209.85.217.177]) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1TNTmz-0005wU-H6 for linux-arm-kernel@lists.infradead.org; Sun, 14 Oct 2012 19:23:57 +0000 Received: by mail-lb0-f177.google.com with SMTP id gi11so2942888lbb.36 for ; Sun, 14 Oct 2012 12:23:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=sender:from:to:cc:subject:date:message-id:x-mailer:in-reply-to :references; bh=u88M3gCxQWpb3QRHOC51yxfZoaQ5CR4ECrPG9QLm20w=; b=BCl3UqKO4l0VV2H94FkuOmjBw2dXjTUCZfmNURqrpEfKVZu5gHM9fz+5+PQcSCqAln ckIFMNJBRa9ItguB6MUkUvUhX1OU2+rvWxSg0ShB/s/rO8pEiNpwo/XO3kUdRF/1752O G1IopgjDpcXbOV9UfrCHdRZrJeLlhDyrhhK/LSwGraz9AUn/wqcjmw7GINv7ghXOLDZS ArmYjvVW8r0nx29W+MgMPaRcVVEbmWgF821U/R8iy8ejAiwieuwe7uNHFbXH2nsZtP5x zwteg+9x4iLniVtPM7ir4eOuQrGVkFJZFlzFieknvrsM21j8FZn4eRmd2RS9H5bfS8zY zGNw== Received: by 10.152.146.67 with SMTP id ta3mr8199249lab.23.1350242633118; Sun, 14 Oct 2012 12:23:53 -0700 (PDT) Received: from localhost.localdomain (c83-254-195-218.bredband.comhem.se. [83.254.195.218]) by mx.google.com with ESMTPS id ef4sm556036lbb.13.2012.10.14.12.23.51 (version=TLSv1/SSLv3 cipher=OTHER); Sun, 14 Oct 2012 12:23:52 -0700 (PDT) From: Rabin Vincent To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org Subject: [PATCH 9/9] ARM: add uprobes support Date: Sun, 14 Oct 2012 21:23:13 +0200 Message-Id: <1350242593-17761-9-git-send-email-rabin@rab.in> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1350242593-17761-1-git-send-email-rabin@rab.in> References: <1350242593-17761-1-git-send-email-rabin@rab.in> X-Spam-Note: CRM114 invocation failed X-Spam-Score: -2.6 (--) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-2.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at http://www.dnswl.org/, low trust [209.85.217.177 listed in list.dnswl.org] 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider (rabin.vincent[at]gmail.com) -0.0 SPF_PASS SPF: sender matches SPF record -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature Cc: Peter Zijlstra , Rabin Vincent , Srikar Dronamraju , oleg@redhat.com X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: linux-arm-kernel-bounces@lists.infradead.org Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org Add basic uprobes support for ARM. perf probe --exec and SystemTap's userspace probing work. The ARM kprobes test code has also been run in a userspace harness to test the uprobe instruction decoding. Caveats: - Thumb is not supported - XOL abort/trap handling is not implemented Signed-off-by: Rabin Vincent --- arch/arm/Kconfig | 4 + arch/arm/include/asm/ptrace.h | 6 ++ arch/arm/include/asm/thread_info.h | 5 +- arch/arm/include/asm/uprobes.h | 34 +++++++ arch/arm/kernel/Makefile | 1 + arch/arm/kernel/signal.c | 4 + arch/arm/kernel/uprobes.c | 191 ++++++++++++++++++++++++++++++++++++ 7 files changed, 244 insertions(+), 1 deletion(-) create mode 100644 arch/arm/include/asm/uprobes.h create mode 100644 arch/arm/kernel/uprobes.c diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 272c3a1..2191b61d 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -168,6 +168,10 @@ config ZONE_DMA config NEED_DMA_MAP_STATE def_bool y +config ARCH_SUPPORTS_UPROBES + depends on KPROBES + def_bool y + config ARCH_HAS_DMA_SET_COHERENT_MASK bool diff --git a/arch/arm/include/asm/ptrace.h b/arch/arm/include/asm/ptrace.h index 142d6ae..297936a 100644 --- a/arch/arm/include/asm/ptrace.h +++ b/arch/arm/include/asm/ptrace.h @@ -197,6 +197,12 @@ static inline long regs_return_value(struct pt_regs *regs) #define instruction_pointer(regs) (regs)->ARM_pc +static inline void instruction_pointer_set(struct pt_regs *regs, + unsigned long val) +{ + instruction_pointer(regs) = val; +} + #ifdef CONFIG_SMP extern unsigned long profile_pc(struct pt_regs *regs); #else diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h index 8477b4c..7bedaee 100644 --- a/arch/arm/include/asm/thread_info.h +++ b/arch/arm/include/asm/thread_info.h @@ -148,6 +148,7 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *, #define TIF_SIGPENDING 0 #define TIF_NEED_RESCHED 1 #define TIF_NOTIFY_RESUME 2 /* callback before returning to user */ +#define TIF_UPROBE 7 #define TIF_SYSCALL_TRACE 8 #define TIF_SYSCALL_AUDIT 9 #define TIF_SYSCALL_TRACEPOINT 10 @@ -160,6 +161,7 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *, #define _TIF_SIGPENDING (1 << TIF_SIGPENDING) #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) #define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) +#define _TIF_UPROBE (1 << TIF_UPROBE) #define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) #define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT) #define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT) @@ -172,7 +174,8 @@ extern int vfp_restore_user_hwstate(struct user_vfp __user *, /* * Change these and you break ASM code in entry-common.S */ -#define _TIF_WORK_MASK (_TIF_NEED_RESCHED | _TIF_SIGPENDING | _TIF_NOTIFY_RESUME) +#define _TIF_WORK_MASK (_TIF_NEED_RESCHED | _TIF_SIGPENDING | \ + _TIF_NOTIFY_RESUME | _TIF_UPROBE) #endif /* __KERNEL__ */ #endif /* __ASM_ARM_THREAD_INFO_H */ diff --git a/arch/arm/include/asm/uprobes.h b/arch/arm/include/asm/uprobes.h new file mode 100644 index 0000000..fa4b81e --- /dev/null +++ b/arch/arm/include/asm/uprobes.h @@ -0,0 +1,34 @@ +#ifndef _ASM_UPROBES_H +#define _ASM_UPROBES_H + +#include + +typedef u32 uprobe_opcode_t; + +#define MAX_UINSN_BYTES 4 +#define UPROBE_XOL_SLOT_BYTES 64 + +#define UPROBE_SWBP_INSN 0x07f001f9 +#define UPROBE_SS_INSN 0x07f001fa +#define UPROBE_SWBP_INSN_SIZE 4 + +struct arch_uprobe_task { + u32 backup; +}; + +struct arch_uprobe { + u8 insn[MAX_UINSN_BYTES]; + uprobe_opcode_t modinsn; + uprobe_opcode_t bpinsn; + bool simulate; + u32 pcreg; + void (*prehandler)(struct arch_uprobe *auprobe, + struct arch_uprobe_task *autask, + struct pt_regs *regs); + void (*posthandler)(struct arch_uprobe *auprobe, + struct arch_uprobe_task *autask, + struct pt_regs *regs); + struct arch_specific_insn asi; +}; + +#endif diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 5bbec7b..a39f634 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o insn.o obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o insn.o obj-$(CONFIG_JUMP_LABEL) += jump_label.o insn.o patch.o obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o +obj-$(CONFIG_UPROBES) += uprobes.o uprobes-arm.o kprobes-arm.o obj-$(CONFIG_KPROBES) += kprobes.o kprobes-common.o patch.o ifdef CONFIG_THUMB2_KERNEL obj-$(CONFIG_KPROBES) += kprobes-thumb.o diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 56f72d2..3b1e88e 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -655,6 +656,9 @@ do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall) return restart; } syscall = 0; + } else if (thread_flags & _TIF_UPROBE) { + clear_thread_flag(TIF_UPROBE); + uprobe_notify_resume(regs); } else { clear_thread_flag(TIF_NOTIFY_RESUME); tracehook_notify_resume(regs); diff --git a/arch/arm/kernel/uprobes.c b/arch/arm/kernel/uprobes.c new file mode 100644 index 0000000..f25a4af --- /dev/null +++ b/arch/arm/kernel/uprobes.c @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2012 Rabin Vincent + * + * 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. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "kprobes.h" + +bool is_swbp_insn(uprobe_opcode_t *insn) +{ + return (__mem_to_opcode_arm(*insn) & 0x0fffffff) == UPROBE_SWBP_INSN; +} + +bool arch_uprobe_ignore(struct arch_uprobe *auprobe, struct pt_regs *regs) +{ + if (!auprobe->asi.insn_check_cc(regs->ARM_cpsr)) { + regs->ARM_pc += 4; + return true; + } + + return false; +} + +bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) +{ + struct kprobe kp; + + if (!auprobe->simulate) + return false; + + kp.addr = (void *) regs->ARM_pc; + kp.opcode = __mem_to_opcode_arm(*(unsigned int *) auprobe->insn); + kp.ainsn.insn_handler = auprobe->asi.insn_handler; + + auprobe->asi.insn_singlestep(&kp, regs); + + return true; +} + +int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, + unsigned long addr) +{ + unsigned int insn; + unsigned int bpinsn; + enum kprobe_insn ret; + + /* Thumb not yet support */ + if (addr & 0x3) + return -EINVAL; + + insn = __mem_to_opcode_arm(*(unsigned int *)auprobe->insn); + auprobe->modinsn = insn; + + ret = arm_kprobe_decode_insn(insn, &auprobe->asi, true); + switch (ret) { + case INSN_REJECTED: + return -EINVAL; + + case INSN_GOOD_NO_SLOT: + auprobe->simulate = true; + break; + + case INSN_GOOD: + default: + break; + } + + bpinsn = UPROBE_SWBP_INSN; + if (insn >= 0xe0000000) + bpinsn |= 0xe0000000; /* Unconditional instruction */ + else + bpinsn |= insn & 0xf0000000; /* Copy condition from insn */ + + auprobe->bpinsn = bpinsn; + + return 0; +} + +void arch_uprobe_write_opcode(struct arch_uprobe *auprobe, void *vaddr, + uprobe_opcode_t opcode) +{ + unsigned long *addr = vaddr; + + if (opcode == UPROBE_SWBP_INSN) + opcode = __opcode_to_mem_arm(auprobe->bpinsn); + + *addr = opcode; +} + +void arch_uprobe_xol_copy(struct arch_uprobe *auprobe, void *vaddr) +{ + unsigned long *addr = vaddr; + + addr[0] = __opcode_to_mem_arm(auprobe->modinsn); + addr[1] = __opcode_to_mem_arm(0xe0000000 | UPROBE_SS_INSN); +} + +int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) +{ + struct uprobe_task *utask = current->utask; + + if (auprobe->prehandler) + auprobe->prehandler(auprobe, &utask->autask, regs); + + regs->ARM_pc = utask->xol_vaddr; + + return 0; +} + +int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) +{ + struct uprobe_task *utask = current->utask; + + regs->ARM_pc = utask->vaddr + 4; + + if (auprobe->posthandler) + auprobe->posthandler(auprobe, &utask->autask, regs); + + return 0; +} + +bool arch_uprobe_xol_was_trapped(struct task_struct *t) +{ + /* TODO: implement */ + return false; +} + +void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) +{ + /* TODO: implement */ +} + +int arch_uprobe_exception_notify(struct notifier_block *self, + unsigned long val, void *data) +{ + return NOTIFY_DONE; +} + +static int uprobe_trap_handler(struct pt_regs *regs, unsigned int instr) +{ + unsigned long flags; + + local_irq_save(flags); + if ((instr & 0x0fffffff) == UPROBE_SWBP_INSN) + uprobe_pre_sstep_notifier(regs); + else + uprobe_post_sstep_notifier(regs); + local_irq_restore(flags); + + return 0; +} + +unsigned long uprobe_get_swbp_addr(struct pt_regs *regs) +{ + return instruction_pointer(regs); +} + +static struct undef_hook uprobes_arm_break_hook = { + .instr_mask = 0x0fffffff, + .instr_val = UPROBE_SWBP_INSN, + .cpsr_mask = MODE_MASK, + .cpsr_val = USR_MODE, + .fn = uprobe_trap_handler, +}; + +static struct undef_hook uprobes_arm_ss_hook = { + .instr_mask = 0x0fffffff, + .instr_val = UPROBE_SS_INSN, + .cpsr_mask = MODE_MASK, + .cpsr_val = USR_MODE, + .fn = uprobe_trap_handler, +}; + +int arch_uprobes_init(void) +{ + register_undef_hook(&uprobes_arm_break_hook); + register_undef_hook(&uprobes_arm_ss_hook); + + return 0; +}