From patchwork Mon Mar 21 08:37:50 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: He Kuang X-Patchwork-Id: 8630391 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 7DF2E9F36E for ; Mon, 21 Mar 2016 08:46:08 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 84CCC201F4 for ; Mon, 21 Mar 2016 08:46:07 +0000 (UTC) Received: from bombadil.infradead.org (unknown [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 63B792026D for ; Mon, 21 Mar 2016 08:46:06 +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 1ahvNa-0008Hp-Pp; Mon, 21 Mar 2016 08:40:02 +0000 Received: from casper.infradead.org ([2001:770:15f::2]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1ahvNQ-0008Fi-F4 for linux-arm-kernel@bombadil.infradead.org; Mon, 21 Mar 2016 08:39:52 +0000 Received: from [58.251.152.52] (helo=szxga04-in.huawei.com) by casper.infradead.org with esmtps (Exim 4.85 #2 (Red Hat Linux)) id 1ahvNM-0007id-8d for linux-arm-kernel@lists.infradead.org; Mon, 21 Mar 2016 08:39:50 +0000 Received: from 172.24.1.46 (EHLO lggeml427-hub.china.huawei.com) ([172.24.1.46]) by szxrg04-dlp.huawei.com (MOS 4.4.6-GA FastPath queued) with ESMTP id AUH28987; Mon, 21 Mar 2016 16:24:39 +0800 (CST) Received: from euler.hulk-profiling (10.107.193.250) by lggeml427-hub.china.huawei.com (10.72.61.79) with Microsoft SMTP Server id 14.3.235.1; Mon, 21 Mar 2016 16:37:55 +0800 From: He Kuang To: , , , , , , , , , Subject: [PATCH 2/2] arm64: Fix watchpoint recursion when single-step is wrongly triggered in irq Date: Mon, 21 Mar 2016 08:37:50 +0000 Message-ID: <1458549470-124791-2-git-send-email-hekuang@huawei.com> X-Mailer: git-send-email 1.8.3.4 In-Reply-To: <1458549470-124791-1-git-send-email-hekuang@huawei.com> References: <1458549470-124791-1-git-send-email-hekuang@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.107.193.250] X-CFilter-Loop: Reflected X-Mirapoint-Virus-RAPID-Raw: score=unknown(0), refid=str=0001.0A090203.56EFB2EE.009B, ss=1, re=0.000, recu=0.000, reip=0.000, cl=1, cld=1, fgs=0, ip=0.0.0.0, so=2014-11-16 11:51:01, dmn=2013-03-21 17:37:32 X-Mirapoint-Loop-Id: e65d97c4b8ab12b3bd37d2cd3cee79c8 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20160321_083950_172729_95EF5168 X-CRM114-Status: GOOD ( 20.12 ) X-Spam-Score: -1.1 (-) 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: wangnan0@huawei.com, hekuang@huawei.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-3.4 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RDNS_NONE,UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP On arm64, watchpoint handler enables single-step to bypass the next instruction for not recursive enter. If an irq is triggered right after the watchpoint, a single-step will be wrongly triggered in irq handler, which causes the watchpoint address not stepped over and system hang. Problem can be found at the following URL: "http://thread.gmane.org/gmane.linux.kernel/2167918" This patch pushes watchpoint status and disables single step if it is triggered in irq handler and restores them back after irq is handled. Signed-off-by: Wang Nan Signed-off-by: He Kuang --- arch/arm64/include/asm/debug-monitors.h | 9 +++++++ arch/arm64/kernel/debug-monitors.c | 13 ++++++++++ arch/arm64/kernel/entry.S | 6 +++++ arch/arm64/kernel/hw_breakpoint.c | 44 +++++++++++++++++++++++++++++++-- 4 files changed, 70 insertions(+), 2 deletions(-) diff --git a/arch/arm64/include/asm/debug-monitors.h b/arch/arm64/include/asm/debug-monitors.h index b5902e8..fe6939e 100644 --- a/arch/arm64/include/asm/debug-monitors.h +++ b/arch/arm64/include/asm/debug-monitors.h @@ -133,7 +133,10 @@ int kernel_active_single_step(void); #ifdef CONFIG_HAVE_HW_BREAKPOINT int reinstall_suspended_bps(struct pt_regs *regs); u64 signal_single_step_enable_bps(void); +u64 irq_single_step_enable_bps(void); + void signal_reinstall_single_step(u64 pstate); +void irq_reinstall_single_step(struct pt_regs *regs); #else static inline int reinstall_suspended_bps(struct pt_regs *regs) { @@ -145,7 +148,13 @@ static inline u64 signal_single_step_enable_bps(void) return 0; } +static inline u64 irq_single_step_enable_bps(void) +{ + return 0; +} + static inline void signal_reinstall_single_step(u64 pstate) { } +static inline void irq_reinstall_single_step(struct pt_regs *regs) { } #endif int aarch32_break_handler(struct pt_regs *regs); diff --git a/arch/arm64/kernel/debug-monitors.c b/arch/arm64/kernel/debug-monitors.c index c536c9e..fab1faa 100644 --- a/arch/arm64/kernel/debug-monitors.c +++ b/arch/arm64/kernel/debug-monitors.c @@ -245,9 +245,22 @@ static void send_user_sigtrap(int si_code) force_sig_info(SIGTRAP, &info, current); } +extern unsigned long el1_irq_ss_entry[]; + static int single_step_handler(unsigned long addr, unsigned int esr, struct pt_regs *regs) { + void *pc = (void *)instruction_pointer(regs); + + if (pc == &el1_irq_ss_entry) { + struct pt_regs *irq_regs = (struct pt_regs *)(regs->sp); + + irq_regs->pstate |= irq_single_step_enable_bps(); + kernel_disable_single_step(); + + return 0; + } + /* * If we are stepping a pending breakpoint, call the hw_breakpoint * handler first. diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 1f7f5a2..836d98e 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -402,12 +402,18 @@ ENDPROC(el1_sync) el1_irq: kernel_entry 1 enable_dbg + .global el1_irq_ss_entry +el1_irq_ss_entry: #ifdef CONFIG_TRACE_IRQFLAGS bl trace_hardirqs_off #endif get_thread_info tsk irq_handler +#ifdef CONFIG_HAVE_HW_BREAKPOINT + mov x0, sp + bl irq_reinstall_single_step +#endif #ifdef CONFIG_PREEMPT ldr w24, [tsk, #TI_PREEMPT] // get preempt count diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c index 18fd3d3..0cf13ee 100644 --- a/arch/arm64/kernel/hw_breakpoint.c +++ b/arch/arm64/kernel/hw_breakpoint.c @@ -540,11 +540,12 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp) * exception level at the register level. * This is used when single-stepping after a breakpoint exception. */ -static void toggle_bp_registers(int reg, enum dbg_active_el el, int enable) +static bool toggle_bp_registers(int reg, enum dbg_active_el el, int enable) { int i, max_slots, privilege; u32 ctrl; struct perf_event **slots; + bool origin_state = false; switch (reg) { case AARCH64_DBG_REG_BCR: @@ -556,7 +557,7 @@ static void toggle_bp_registers(int reg, enum dbg_active_el el, int enable) max_slots = core_num_wrps; break; default: - return; + return false; } for (i = 0; i < max_slots; ++i) { @@ -568,12 +569,16 @@ static void toggle_bp_registers(int reg, enum dbg_active_el el, int enable) continue; ctrl = read_wb_reg(reg, i); + if (ctrl & 0x1) + origin_state = true; if (enable) ctrl |= 0x1; else ctrl &= ~0x1; write_wb_reg(reg, i, ctrl); } + + return origin_state; } /* @@ -982,6 +987,41 @@ u64 signal_single_step_enable_bps(void) return retval; } +u64 irq_single_step_enable_bps(void) +{ + u64 retval = 0; + + if (!toggle_bp_registers(AARCH64_DBG_REG_WCR, DBG_ACTIVE_EL1, 1)) + retval |= PSR_LINUX_HW_WP_SS; + + if (!toggle_bp_registers(AARCH64_DBG_REG_BCR, DBG_ACTIVE_EL1, 1)) + retval |= PSR_LINUX_HW_BP_SS; + + return retval; +} + +void irq_reinstall_single_step(struct pt_regs *regs) +{ + u64 pstate = regs->pstate; + + if (likely(!(regs->pstate & PSR_LINUX_HW_SS))) + return; + + if (!user_mode(regs)) { + if (pstate & PSR_LINUX_HW_BP_SS) + toggle_bp_registers(AARCH64_DBG_REG_BCR, + DBG_ACTIVE_EL1, 0); + if (pstate & PSR_LINUX_HW_WP_SS) + toggle_bp_registers(AARCH64_DBG_REG_WCR, + DBG_ACTIVE_EL1, 0); + + if (!kernel_active_single_step()) { + asm volatile ("msr daifset, #8\n"); + kernel_enable_single_step(regs); + } + } +} + void signal_reinstall_single_step(u64 pstate) { struct debug_info *debug_info = ¤t->thread.debug;