From owner-svn-src-head@freebsd.org Thu Jan 28 12:44:00 2016 Return-Path: Delivered-To: svn-src-head@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 5E127A71B99; Thu, 28 Jan 2016 12:44:00 +0000 (UTC) (envelope-from zbb@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 2092F1BE4; Thu, 28 Jan 2016 12:44:00 +0000 (UTC) (envelope-from zbb@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id u0SChx4f069220; Thu, 28 Jan 2016 12:43:59 GMT (envelope-from zbb@FreeBSD.org) Received: (from zbb@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id u0SChwn4069214; Thu, 28 Jan 2016 12:43:58 GMT (envelope-from zbb@FreeBSD.org) Message-Id: <201601281243.u0SChwn4069214@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: zbb set sender to zbb@FreeBSD.org using -f From: Zbigniew Bodek Date: Thu, 28 Jan 2016 12:43:58 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r294987 - in head/sys/arm: arm include X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 28 Jan 2016 12:44:00 -0000 Author: zbb Date: Thu Jan 28 12:43:58 2016 New Revision: 294987 URL: https://svnweb.freebsd.org/changeset/base/294987 Log: SMP support for ARMv6/v7 HW watchpoints Use per-CPU structure to store HW watchpoints registers state for each CPU present in the system. Those registers will be restored upon wake up from the STOP state if requested by the debug_monitor code. The method is similar to the one introduced to AMD64. We store all possible 16 registers for HW watchpoints (maximum allowed by the architecture). HW breakpoints are not maintained since they are used for single stepping only. Pointed out by: kib Reviewed by: wma No strong objections from: kib Submitted by: Zbigniew Bodek Obtained from: Semihalf Sponsored by: Juniper Networks Inc. Differential Revision: https://reviews.freebsd.org/D4338 Modified: head/sys/arm/arm/debug_monitor.c head/sys/arm/arm/mp_machdep.c head/sys/arm/include/debug_monitor.h head/sys/arm/include/pcpu.h head/sys/arm/include/reg.h Modified: head/sys/arm/arm/debug_monitor.c ============================================================================== --- head/sys/arm/arm/debug_monitor.c Thu Jan 28 12:25:27 2016 (r294986) +++ head/sys/arm/arm/debug_monitor.c Thu Jan 28 12:43:58 2016 (r294987) @@ -35,14 +35,17 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include +#include #include #include #include #include #include #include +#include #include #include @@ -80,7 +83,7 @@ static boolean_t dbg_ossr; /* OS Save an static uint32_t dbg_watchpoint_num; static uint32_t dbg_breakpoint_num; -static int dbg_ref_count_mme[MAXCPU]; /* Times monitor mode was enabled */ +static int dbg_ref_count_mme; /* Times monitor mode was enabled */ /* ID_DFR0 - Debug Feature Register 0 */ #define ID_DFR0_CP_DEBUG_M_SHIFT 0 @@ -542,11 +545,9 @@ dbg_enable_monitor(void) { uint32_t dbg_dscr; - /* Already enabled? Just increment reference counter and return */ - if (dbg_monitor_is_enabled()) { - dbg_ref_count_mme[PCPU_GET(cpuid)]++; + /* Already enabled? Just return */ + if (dbg_monitor_is_enabled()) return (0); - } dbg_dscr = cp14_dbgdscrint_get(); @@ -565,10 +566,8 @@ dbg_enable_monitor(void) isb(); /* Verify that Monitor mode is set */ - if (dbg_monitor_is_enabled()) { - dbg_ref_count_mme[PCPU_GET(cpuid)]++; + if (dbg_monitor_is_enabled()) return (0); - } return (ENXIO); } @@ -581,9 +580,6 @@ dbg_disable_monitor(void) if (!dbg_monitor_is_enabled()) return (0); - if (--dbg_ref_count_mme[PCPU_GET(cpuid)] > 0) - return (0); - dbg_dscr = cp14_dbgdscrint_get(); switch (dbg_model) { case ID_DFR0_CP_DEBUG_M_V6: @@ -607,11 +603,13 @@ dbg_disable_monitor(void) static int dbg_setup_xpoint(struct dbg_wb_conf *conf) { + struct pcpu *pcpu; + struct dbreg *d; const char *typestr; uint32_t cr_size, cr_priv, cr_access; uint32_t reg_ctrl, reg_addr, ctrl, addr; boolean_t is_bkpt; - u_int cpuid; + u_int cpuid, cpu; u_int i; int err; @@ -705,20 +703,52 @@ dbg_setup_xpoint(struct dbg_wb_conf *con dbg_wb_write_reg(reg_addr, i, addr); dbg_wb_write_reg(reg_ctrl, i, ctrl); - return (dbg_enable_monitor()); + err = dbg_enable_monitor(); + if (err != 0) + return (err); + + /* Increment monitor enable counter */ + dbg_ref_count_mme++; + + /* + * Save watchpoint settings for all CPUs. + * We don't need to do the same with breakpoints since HW breakpoints + * are only used to perform single stepping. + */ + if (!is_bkpt) { + CPU_FOREACH(cpu) { + pcpu = pcpu_find(cpu); + /* Fill out the settings for watchpoint */ + d = (struct dbreg *)pcpu->pc_dbreg; + d->dbg_wvr[i] = addr; + d->dbg_wcr[i] = ctrl; + /* Skip update command for the current CPU */ + if (cpu != cpuid) + pcpu->pc_dbreg_cmd = PC_DBREG_CMD_LOAD; + } + } + /* Ensure all data is written before waking other CPUs */ + atomic_thread_fence_rel(); + + return (0); } static int dbg_remove_xpoint(struct dbg_wb_conf *conf) { + struct pcpu *pcpu; + struct dbreg *d; uint32_t reg_ctrl, reg_addr, addr; - u_int cpuid; + boolean_t is_bkpt; + u_int cpuid, cpu; u_int i; int err; if (!dbg_capable) return (ENXIO); + is_bkpt = (conf->type == DBG_TYPE_BREAKPOINT); + cpuid = PCPU_GET(cpuid); if (!dbg_ready[cpuid]) { err = dbg_reset_state(); @@ -729,7 +759,7 @@ dbg_remove_xpoint(struct dbg_wb_conf *co addr = conf->address; - if (conf->type == DBG_TYPE_BREAKPOINT) { + if (is_bkpt) { i = conf->slot; reg_ctrl = DBG_REG_BASE_BCR; reg_addr = DBG_REG_BASE_BVR; @@ -746,7 +776,40 @@ dbg_remove_xpoint(struct dbg_wb_conf *co dbg_wb_write_reg(reg_ctrl, i, 0); dbg_wb_write_reg(reg_addr, i, 0); - return (dbg_disable_monitor()); + /* Decrement monitor enable counter */ + dbg_ref_count_mme--; + if (dbg_ref_count_mme < 0) + dbg_ref_count_mme = 0; + + atomic_thread_fence_rel(); + + if (dbg_ref_count_mme == 0) { + err = dbg_disable_monitor(); + if (err != 0) + return (err); + } + + /* + * Save watchpoint settings for all CPUs. + * We don't need to do the same with breakpoints since HW breakpoints + * are only used to perform single stepping. + */ + if (!is_bkpt) { + CPU_FOREACH(cpu) { + pcpu = pcpu_find(cpu); + /* Fill out the settings for watchpoint */ + d = (struct dbreg *)pcpu->pc_dbreg; + d->dbg_wvr[i] = 0; + d->dbg_wcr[i] = 0; + /* Skip update command for the current CPU */ + if (cpu != cpuid) + pcpu->pc_dbreg_cmd = PC_DBREG_CMD_LOAD; + } + /* Ensure all data is written before waking other CPUs */ + atomic_thread_fence_rel(); + } + + return (0); } static __inline uint32_t @@ -941,3 +1004,67 @@ dbg_monitor_init(void) db_printf("HW Breakpoints/Watchpoints not enabled on CPU%d\n", PCPU_GET(cpuid)); } + +CTASSERT(sizeof(struct dbreg) == sizeof(((struct pcpu *)NULL)->pc_dbreg)); + +void +dbg_resume_dbreg(void) +{ + struct dbreg *d; + u_int cpuid; + u_int i; + int err; + + /* + * This flag is set on the primary CPU + * and its meaning is valid for other CPUs too. + */ + if (!dbg_capable) + return; + + atomic_thread_fence_acq(); + + switch (PCPU_GET(dbreg_cmd)) { + case PC_DBREG_CMD_LOAD: + d = (struct dbreg *)PCPU_PTR(dbreg); + cpuid = PCPU_GET(cpuid); + + /* Reset Debug Architecture State if not done earlier */ + if (!dbg_ready[cpuid]) { + err = dbg_reset_state(); + if (err != 0) { + /* + * Something is very wrong. + * WPs/BPs will not work correctly in this CPU. + */ + panic("%s: Failed to reset Debug Architecture " + "state on CPU%d", __func__, cpuid); + } + dbg_ready[cpuid] = TRUE; + } + + /* Restore watchpoints */ + for (i = 0; i < dbg_watchpoint_num; i++) { + dbg_wb_write_reg(DBG_REG_BASE_WVR, i, d->dbg_wvr[i]); + dbg_wb_write_reg(DBG_REG_BASE_WCR, i, d->dbg_wcr[i]); + } + + if ((dbg_ref_count_mme > 0) && !dbg_monitor_is_enabled()) { + err = dbg_enable_monitor(); + if (err != 0) { + panic("%s: Failed to enable Debug Monitor " + "on CPU%d", __func__, cpuid); + } + } + if ((dbg_ref_count_mme == 0) && dbg_monitor_is_enabled()) { + err = dbg_disable_monitor(); + if (err != 0) { + panic("%s: Failed to disable Debug Monitor " + "on CPU%d", __func__, cpuid); + } + } + + PCPU_SET(dbreg_cmd, PC_DBREG_CMD_NONE); + break; + } +} Modified: head/sys/arm/arm/mp_machdep.c ============================================================================== --- head/sys/arm/arm/mp_machdep.c Thu Jan 28 12:25:27 2016 (r294986) +++ head/sys/arm/arm/mp_machdep.c Thu Jan 28 12:43:58 2016 (r294987) @@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -303,6 +304,9 @@ ipi_stop(void *dummy __unused) CPU_CLR_ATOMIC(cpu, &started_cpus); CPU_CLR_ATOMIC(cpu, &stopped_cpus); +#ifdef DDB + dbg_resume_dbreg(); +#endif CTR0(KTR_SMP, "IPI_STOP (restart)"); } @@ -405,6 +409,9 @@ ipi_handler(void *arg) CPU_CLR_ATOMIC(cpu, &started_cpus); CPU_CLR_ATOMIC(cpu, &stopped_cpus); +#ifdef DDB + dbg_resume_dbreg(); +#endif CTR0(KTR_SMP, "IPI_STOP (restart)"); break; case IPI_PREEMPT: Modified: head/sys/arm/include/debug_monitor.h ============================================================================== --- head/sys/arm/include/debug_monitor.h Thu Jan 28 12:25:27 2016 (r294986) +++ head/sys/arm/include/debug_monitor.h Thu Jan 28 12:43:58 2016 (r294987) @@ -48,6 +48,7 @@ void dbg_monitor_init(void); void dbg_show_watchpoint(void); int dbg_setup_watchpoint(db_expr_t, db_expr_t, enum dbg_access_t); int dbg_remove_watchpoint(db_expr_t, db_expr_t); +void dbg_resume_dbreg(void); #else /* __ARM_ARCH >= 6 */ static __inline void dbg_show_watchpoint(void) @@ -68,6 +69,11 @@ static __inline void dbg_monitor_init(void) { } + +static __inline void +dbg_resume_dbreg(void) +{ +} #endif /* __ARM_ARCH < 6 */ #else /* DDB */ Modified: head/sys/arm/include/pcpu.h ============================================================================== --- head/sys/arm/include/pcpu.h Thu Jan 28 12:25:27 2016 (r294986) +++ head/sys/arm/include/pcpu.h Thu Jan 28 12:43:58 2016 (r294987) @@ -49,7 +49,9 @@ struct vmspace; struct pmap *pc_curpmap; \ vm_offset_t pc_qmap_addr; \ void *pc_qmap_pte; \ - char __pad[133] + unsigned int pc_dbreg[32]; \ + int pc_dbreg_cmd; \ + char __pad[1] #else #define PCPU_MD_FIELDS \ vm_offset_t qmap_addr; \ @@ -59,6 +61,9 @@ struct vmspace; #ifdef _KERNEL +#define PC_DBREG_CMD_NONE 0 +#define PC_DBREG_CMD_LOAD 1 + struct pcb; struct pcpu; Modified: head/sys/arm/include/reg.h ============================================================================== --- head/sys/arm/include/reg.h Thu Jan 28 12:25:27 2016 (r294986) +++ head/sys/arm/include/reg.h Thu Jan 28 12:43:58 2016 (r294987) @@ -19,7 +19,9 @@ struct fpreg { }; struct dbreg { - unsigned int dr[8]; /* debug registers */ +#define ARM_WR_MAX 16 /* Maximum number of watchpoint registers */ + unsigned int dbg_wcr[ARM_WR_MAX]; /* Watchpoint Control Registers */ + unsigned int dbg_wvr[ARM_WR_MAX]; /* Watchpoint Value Registers */ }; #ifdef _KERNEL