Author: andrew
Date: Wed Oct 30 10:51:24 2019
New Revision: 354175
URL: https://svnweb.freebsd.org/changeset/base/354175

Log:
  Update the debug monitor handling to work after userspace has started
  
  The debug monitor register state is now stored in a struct and updated
  when required. Currently there is only a kernel state, however a
  per-process state will be added in a future change.
  
  Sponsored by: DARPA, AFRL
  Differential Revision:        https://reviews.freebsd.org/D22128

Modified:
  head/sys/arm64/arm64/db_trace.c
  head/sys/arm64/arm64/debug_monitor.c
  head/sys/arm64/arm64/exception.S
  head/sys/arm64/arm64/mp_machdep.c
  head/sys/arm64/include/debug_monitor.h
  head/sys/arm64/include/pcpu.h
  head/sys/conf/files.arm64

Modified: head/sys/arm64/arm64/db_trace.c
==============================================================================
--- head/sys/arm64/arm64/db_trace.c     Wed Oct 30 10:42:52 2019        
(r354174)
+++ head/sys/arm64/arm64/db_trace.c     Wed Oct 30 10:51:24 2019        
(r354175)
@@ -34,6 +34,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/param.h>
 #include <sys/proc.h>
 #include <sys/kdb.h>
+
 #include <machine/pcb.h>
 #include <ddb/ddb.h>
 #include <ddb/db_sym.h>
@@ -53,15 +54,14 @@ int
 db_md_clr_watchpoint(db_expr_t addr, db_expr_t size)
 {
 
-       return (dbg_remove_watchpoint(addr, size, DBG_FROM_EL1));
+       return (dbg_remove_watchpoint(NULL, addr, size));
 }
 
 int
 db_md_set_watchpoint(db_expr_t addr, db_expr_t size)
 {
 
-       return (dbg_setup_watchpoint(addr, size, DBG_FROM_EL1,
-           HW_BREAKPOINT_RW));
+       return (dbg_setup_watchpoint(NULL, addr, size, HW_BREAKPOINT_RW));
 }
 
 static void

Modified: head/sys/arm64/arm64/debug_monitor.c
==============================================================================
--- head/sys/arm64/arm64/debug_monitor.c        Wed Oct 30 10:42:52 2019        
(r354174)
+++ head/sys/arm64/arm64/debug_monitor.c        Wed Oct 30 10:51:24 2019        
(r354175)
@@ -43,8 +43,10 @@ __FBSDID("$FreeBSD$");
 #include <machine/debug_monitor.h>
 #include <machine/kdb.h>
 
+#ifdef DDB
 #include <ddb/ddb.h>
 #include <ddb/db_sym.h>
+#endif
 
 enum dbg_t {
        DBG_TYPE_BREAKPOINT = 0,
@@ -53,8 +55,9 @@ enum dbg_t {
 
 static int dbg_watchpoint_num;
 static int dbg_breakpoint_num;
-static int dbg_ref_count_mde[MAXCPU];
-static int dbg_ref_count_kde[MAXCPU];
+static struct debug_monitor_state kernel_monitor = {
+       .dbg_flags = DBGMON_KERNEL
+};
 
 /* Watchpoints/breakpoints control register bitfields */
 #define DBG_WATCH_CTRL_LEN_1           (0x1 << 5)
@@ -138,6 +141,7 @@ static int dbg_ref_count_kde[MAXCPU];
        WRITE_WB_REG_CASE(reg, 14, offset, val);        \
        WRITE_WB_REG_CASE(reg, 15, offset, val)
 
+#ifdef DDB
 static uint64_t
 dbg_wb_read_reg(int reg, int n)
 {
@@ -149,11 +153,12 @@ dbg_wb_read_reg(int reg, int n)
        SWITCH_CASES_READ_WB_REG(DBG_WB_BVR, DBG_REG_BASE_BVR, val);
        SWITCH_CASES_READ_WB_REG(DBG_WB_BCR, DBG_REG_BASE_BCR, val);
        default:
-               db_printf("trying to read from wrong debug register %d\n", n);
+               printf("trying to read from wrong debug register %d\n", n);
        }
 
        return val;
 }
+#endif /* DDB */
 
 static void
 dbg_wb_write_reg(int reg, int n, uint64_t val)
@@ -164,17 +169,19 @@ dbg_wb_write_reg(int reg, int n, uint64_t val)
        SWITCH_CASES_WRITE_WB_REG(DBG_WB_BVR, DBG_REG_BASE_BVR, val);
        SWITCH_CASES_WRITE_WB_REG(DBG_WB_BCR, DBG_REG_BASE_BCR, val);
        default:
-               db_printf("trying to write to wrong debug register %d\n", n);
+               printf("trying to write to wrong debug register %d\n", n);
+               return;
        }
        isb();
 }
 
+#ifdef DDB
 void
 kdb_cpu_set_singlestep(void)
 {
 
        kdb_frame->tf_spsr |= DBG_SPSR_SS;
-       WRITE_SPECIALREG(MDSCR_EL1, READ_SPECIALREG(MDSCR_EL1) |
+       WRITE_SPECIALREG(mdscr_el1, READ_SPECIALREG(mdscr_el1) |
            DBG_MDSCR_SS | DBG_MDSCR_KDE);
 
        /*
@@ -182,9 +189,9 @@ kdb_cpu_set_singlestep(void)
         * over watched instruction will trigger break exception instead of
         * single-step exception and locks CPU on that instruction for ever.
         */
-       if (dbg_ref_count_mde[PCPU_GET(cpuid)] > 0) {
-               WRITE_SPECIALREG(MDSCR_EL1,
-                   READ_SPECIALREG(MDSCR_EL1) & ~DBG_MDSCR_MDE);
+       if ((kernel_monitor.dbg_flags & DBGMON_ENABLED) != 0) {
+               WRITE_SPECIALREG(mdscr_el1,
+                   READ_SPECIALREG(mdscr_el1) & ~DBG_MDSCR_MDE);
        }
 }
 
@@ -192,18 +199,18 @@ void
 kdb_cpu_clear_singlestep(void)
 {
 
-       WRITE_SPECIALREG(MDSCR_EL1, READ_SPECIALREG(MDSCR_EL1) &
+       WRITE_SPECIALREG(mdscr_el1, READ_SPECIALREG(mdscr_el1) &
            ~(DBG_MDSCR_SS | DBG_MDSCR_KDE));
 
        /* Restore breakpoints and watchpoints */
-       if (dbg_ref_count_mde[PCPU_GET(cpuid)] > 0) {
-               WRITE_SPECIALREG(MDSCR_EL1,
-                   READ_SPECIALREG(MDSCR_EL1) | DBG_MDSCR_MDE);
-       }
+       if ((kernel_monitor.dbg_flags & DBGMON_ENABLED) != 0) {
+               WRITE_SPECIALREG(mdscr_el1,
+                   READ_SPECIALREG(mdscr_el1) | DBG_MDSCR_MDE);
 
-       if (dbg_ref_count_kde[PCPU_GET(cpuid)] > 0) {
-               WRITE_SPECIALREG(MDSCR_EL1,
-                   READ_SPECIALREG(MDSCR_EL1) | DBG_MDSCR_KDE);
+               if ((kernel_monitor.dbg_flags & DBGMON_KERNEL) != 0) {
+                       WRITE_SPECIALREG(mdscr_el1,
+                           READ_SPECIALREG(mdscr_el1) | DBG_MDSCR_KDE);
+               }
        }
 }
 
@@ -267,30 +274,30 @@ dbg_show_watchpoint(void)
                }
        }
 }
+#endif /* DDB */
 
-
 static int
-dbg_find_free_slot(enum dbg_t type)
+dbg_find_free_slot(struct debug_monitor_state *monitor, enum dbg_t type)
 {
-       u_int max, reg, i;
+       uint64_t *reg;
+       u_int max, i;
 
        switch(type) {
        case DBG_TYPE_BREAKPOINT:
                max = dbg_breakpoint_num;
-               reg = DBG_REG_BASE_BCR;
-
+               reg = monitor->dbg_bcr;
                break;
        case DBG_TYPE_WATCHPOINT:
                max = dbg_watchpoint_num;
-               reg = DBG_REG_BASE_WCR;
+               reg = monitor->dbg_wcr;
                break;
        default:
-               db_printf("Unsupported debug type\n");
+               printf("Unsupported debug type\n");
                return (i);
        }
 
        for (i = 0; i < max; i++) {
-               if ((dbg_wb_read_reg(reg, i) & DBG_WB_CTRL_E) == 0)
+               if ((reg[i] & DBG_WB_CTRL_E) == 0)
                        return (i);
        }
 
@@ -298,81 +305,50 @@ dbg_find_free_slot(enum dbg_t type)
 }
 
 static int
-dbg_find_slot(enum dbg_t type, db_expr_t addr)
+dbg_find_slot(struct debug_monitor_state *monitor, enum dbg_t type,
+    vm_offset_t addr)
 {
-       u_int max, reg_addr, reg_ctrl, i;
+       uint64_t *reg_addr, *reg_ctrl;
+       u_int max, i;
 
        switch(type) {
        case DBG_TYPE_BREAKPOINT:
                max = dbg_breakpoint_num;
-               reg_addr = DBG_REG_BASE_BVR;
-               reg_ctrl = DBG_REG_BASE_BCR;
+               reg_addr = monitor->dbg_bvr;
+               reg_ctrl = monitor->dbg_bcr;
                break;
        case DBG_TYPE_WATCHPOINT:
                max = dbg_watchpoint_num;
-               reg_addr = DBG_REG_BASE_WVR;
-               reg_ctrl = DBG_REG_BASE_WCR;
+               reg_addr = monitor->dbg_wvr;
+               reg_ctrl = monitor->dbg_wcr;
                break;
        default:
-               db_printf("Unsupported debug type\n");
+               printf("Unsupported debug type\n");
                return (i);
        }
 
        for (i = 0; i < max; i++) {
-               if ((dbg_wb_read_reg(reg_addr, i) == addr) &&
-                   ((dbg_wb_read_reg(reg_ctrl, i) & DBG_WB_CTRL_E) != 0))
+               if (reg_addr[i] == addr &&
+                   (reg_ctrl[i] & DBG_WB_CTRL_E) != 0)
                        return (i);
        }
 
        return (-1);
 }
 
-static void
-dbg_enable_monitor(enum dbg_el_t el)
-{
-       uint64_t reg_mdcr = 0;
-
-       /*
-        * There is no need to have debug monitor on permanently, thus we are
-        * refcounting and turn it on only if any of CPU is going to use that.
-        */
-       if (atomic_fetchadd_int(&dbg_ref_count_mde[PCPU_GET(cpuid)], 1) == 0)
-               reg_mdcr = DBG_MDSCR_MDE;
-
-       if ((el == DBG_FROM_EL1) &&
-           atomic_fetchadd_int(&dbg_ref_count_kde[PCPU_GET(cpuid)], 1) == 0)
-               reg_mdcr |= DBG_MDSCR_KDE;
-
-       if (reg_mdcr)
-               WRITE_SPECIALREG(MDSCR_EL1, READ_SPECIALREG(MDSCR_EL1) | 
reg_mdcr);
-}
-
-static void
-dbg_disable_monitor(enum dbg_el_t el)
-{
-       uint64_t reg_mdcr = 0;
-
-       if (atomic_fetchadd_int(&dbg_ref_count_mde[PCPU_GET(cpuid)], -1) == 1)
-               reg_mdcr = DBG_MDSCR_MDE;
-
-       if ((el == DBG_FROM_EL1) &&
-           atomic_fetchadd_int(&dbg_ref_count_kde[PCPU_GET(cpuid)], -1) == 1)
-               reg_mdcr |= DBG_MDSCR_KDE;
-
-       if (reg_mdcr)
-               WRITE_SPECIALREG(MDSCR_EL1, READ_SPECIALREG(MDSCR_EL1) & 
~reg_mdcr);
-}
-
 int
-dbg_setup_watchpoint(db_expr_t addr, db_expr_t size, enum dbg_el_t el,
-    enum dbg_access_t access)
+dbg_setup_watchpoint(struct debug_monitor_state *monitor, vm_offset_t addr,
+    vm_size_t size, enum dbg_access_t access)
 {
        uint64_t wcr_size, wcr_priv, wcr_access;
        u_int i;
 
-       i = dbg_find_free_slot(DBG_TYPE_WATCHPOINT);
+       if (monitor == NULL)
+               monitor = &kernel_monitor;
+
+       i = dbg_find_free_slot(monitor, DBG_TYPE_WATCHPOINT);
        if (i == -1) {
-               db_printf("Can not find slot for watchpoint, max %d"
+               printf("Can not find slot for watchpoint, max %d"
                    " watchpoints supported\n", dbg_watchpoint_num);
                return (i);
        }
@@ -391,21 +367,14 @@ dbg_setup_watchpoint(db_expr_t addr, db_expr_t size, e
                wcr_size = DBG_WATCH_CTRL_LEN_8;
                break;
        default:
-               db_printf("Unsupported address size for watchpoint\n");
+               printf("Unsupported address size for watchpoint\n");
                return (-1);
        }
 
-       switch(el) {
-       case DBG_FROM_EL0:
+       if ((monitor->dbg_flags & DBGMON_KERNEL) == 0)
                wcr_priv = DBG_WB_CTRL_EL0;
-               break;
-       case DBG_FROM_EL1:
+       else
                wcr_priv = DBG_WB_CTRL_EL1;
-               break;
-       default:
-               db_printf("Unsupported exception level for watchpoint\n");
-               return (-1);
-       }
 
        switch(access) {
        case HW_BREAKPOINT_X:
@@ -421,34 +390,79 @@ dbg_setup_watchpoint(db_expr_t addr, db_expr_t size, e
                wcr_access = DBG_WATCH_CTRL_LOAD | DBG_WATCH_CTRL_STORE;
                break;
        default:
-               db_printf("Unsupported exception level for watchpoint\n");
+               printf("Unsupported exception level for watchpoint\n");
                return (-1);
        }
 
-       dbg_wb_write_reg(DBG_REG_BASE_WVR, i, addr);
-       dbg_wb_write_reg(DBG_REG_BASE_WCR, i, wcr_size | wcr_access | wcr_priv |
-           DBG_WB_CTRL_E);
-       dbg_enable_monitor(el);
+       monitor->dbg_wvr[i] = addr;
+       monitor->dbg_wcr[i] = wcr_size | wcr_access | wcr_priv | DBG_WB_CTRL_E;
+       monitor->dbg_enable_count++;
+       monitor->dbg_flags |= DBGMON_ENABLED;
+
+       dbg_register_sync(monitor);
        return (0);
 }
 
 int
-dbg_remove_watchpoint(db_expr_t addr, db_expr_t size, enum dbg_el_t el)
+dbg_remove_watchpoint(struct debug_monitor_state *monitor, vm_offset_t addr,
+    vm_size_t size)
 {
        u_int i;
 
-       i = dbg_find_slot(DBG_TYPE_WATCHPOINT, addr);
+       if (monitor == NULL)
+               monitor = &kernel_monitor;
+
+       i = dbg_find_slot(monitor, DBG_TYPE_WATCHPOINT, addr);
        if (i == -1) {
-               db_printf("Can not find watchpoint for address 0%lx\n", addr);
+               printf("Can not find watchpoint for address 0%lx\n", addr);
                return (i);
        }
 
-       dbg_wb_write_reg(DBG_REG_BASE_WCR, i, 0);
-       dbg_disable_monitor(el);
+       monitor->dbg_wvr[i] = 0;
+       monitor->dbg_wcr[i] = 0;
+       monitor->dbg_enable_count--;
+       if (monitor->dbg_enable_count == 0)
+               monitor->dbg_flags &= ~DBGMON_ENABLED;
+
+       dbg_register_sync(monitor);
        return (0);
 }
 
 void
+dbg_register_sync(struct debug_monitor_state *monitor)
+{
+       uint64_t mdscr;
+       int i;
+
+       if (monitor == NULL)
+               monitor = &kernel_monitor;
+
+       mdscr = READ_SPECIALREG(mdscr_el1);
+       if ((monitor->dbg_flags & DBGMON_ENABLED) == 0) {
+               mdscr &= ~(DBG_MDSCR_MDE | DBG_MDSCR_KDE);
+       } else {
+               for (i = 0; i < dbg_breakpoint_num; i++) {
+                       dbg_wb_write_reg(DBG_REG_BASE_BCR, i,
+                           monitor->dbg_bcr[i]);
+                       dbg_wb_write_reg(DBG_REG_BASE_BVR, i,
+                           monitor->dbg_bvr[i]);
+               }
+
+               for (i = 0; i < dbg_watchpoint_num; i++) {
+                       dbg_wb_write_reg(DBG_REG_BASE_WCR, i,
+                           monitor->dbg_wcr[i]);
+                       dbg_wb_write_reg(DBG_REG_BASE_WVR, i,
+                           monitor->dbg_wvr[i]);
+               }
+               mdscr |= DBG_MDSCR_MDE;
+               if ((monitor->dbg_flags & DBGMON_KERNEL) == DBGMON_KERNEL)
+                       mdscr |= DBG_MDSCR_KDE;
+       }
+       WRITE_SPECIALREG(mdscr_el1, mdscr);
+       isb();
+}
+
+void
 dbg_monitor_init(void)
 {
        u_int i;
@@ -471,12 +485,12 @@ dbg_monitor_init(void)
         *
         * Reset all breakpoints and watchpoints.
         */
-       for (i = 0; i < dbg_watchpoint_num; ++i) {
+       for (i = 0; i < dbg_watchpoint_num; i++) {
                dbg_wb_write_reg(DBG_REG_BASE_WCR, i, 0);
                dbg_wb_write_reg(DBG_REG_BASE_WVR, i, 0);
        }
 
-       for (i = 0; i < dbg_breakpoint_num; ++i) {
+       for (i = 0; i < dbg_breakpoint_num; i++) {
                dbg_wb_write_reg(DBG_REG_BASE_BCR, i, 0);
                dbg_wb_write_reg(DBG_REG_BASE_BVR, i, 0);
        }

Modified: head/sys/arm64/arm64/exception.S
==============================================================================
--- head/sys/arm64/arm64/exception.S    Wed Oct 30 10:42:52 2019        
(r354174)
+++ head/sys/arm64/arm64/exception.S    Wed Oct 30 10:51:24 2019        
(r354175)
@@ -74,15 +74,17 @@ __FBSDID("$FreeBSD$");
        blr     x1
 1:
 .endif
+       msr     daifclr, #8     /* Enable the debug exception */
 .endm
 
 .macro restore_registers el
 .if \el == 1
-       msr     daifset, #2
        /*
-        * Disable interrupts, x18 may change in the interrupt exception
-        * handler.  For EL0 exceptions, do_ast already did this.
+        * Disable interrupts and debug exceptions, x18 may change in the
+        * interrupt exception handler.  For EL0 exceptions, do_ast already
+        * did this.
         */
+       msr     daifset, #10
 .endif
 .if \el == 0
        /* Remove the SSBD (CVE-2018-3639) workaround if needed */
@@ -136,7 +138,7 @@ __FBSDID("$FreeBSD$");
        bic     x19, x19, #PSR_I
 1:
        /* Disable interrupts */
-       msr     daifset, #2
+       msr     daifset, #10
 
        /* Read the current thread flags */
        ldr     x1, [x18, #PC_CURTHREAD]        /* Load curthread */

Modified: head/sys/arm64/arm64/mp_machdep.c
==============================================================================
--- head/sys/arm64/arm64/mp_machdep.c   Wed Oct 30 10:42:52 2019        
(r354174)
+++ head/sys/arm64/arm64/mp_machdep.c   Wed Oct 30 10:51:24 2019        
(r354175)
@@ -29,6 +29,7 @@
  */
 
 #include "opt_acpi.h"
+#include "opt_ddb.h"
 #include "opt_kstack_pages.h"
 #include "opt_platform.h"
 
@@ -55,6 +56,7 @@ __FBSDID("$FreeBSD$");
 #include <vm/vm_kern.h>
 
 #include <machine/machdep.h>
+#include <machine/debug_monitor.h>
 #include <machine/intr.h>
 #include <machine/smp.h>
 #ifdef VFP
@@ -352,6 +354,10 @@ ipi_stop(void *dummy __unused)
        /* Wait for restart */
        while (!CPU_ISSET(cpu, &started_cpus))
                cpu_spinwait();
+
+#ifdef DDB
+       dbg_register_sync(NULL);
+#endif
 
        CPU_CLR_ATOMIC(cpu, &started_cpus);
        CPU_CLR_ATOMIC(cpu, &stopped_cpus);

Modified: head/sys/arm64/include/debug_monitor.h
==============================================================================
--- head/sys/arm64/include/debug_monitor.h      Wed Oct 30 10:42:52 2019        
(r354174)
+++ head/sys/arm64/include/debug_monitor.h      Wed Oct 30 10:51:24 2019        
(r354175)
@@ -32,13 +32,20 @@
 #ifndef _MACHINE_DEBUG_MONITOR_H_
 #define        _MACHINE_DEBUG_MONITOR_H_
 
-#ifdef DDB
+#ifdef _KERNEL
 
-#include <machine/db_machdep.h>
+#define        DBG_BRP_MAX     16
+#define        DBG_WRP_MAX     16
 
-enum dbg_el_t {
-       DBG_FROM_EL0 = 0,
-       DBG_FROM_EL1 = 1,
+struct debug_monitor_state {
+       uint32_t        dbg_enable_count;
+       uint32_t        dbg_flags;
+#define        DBGMON_ENABLED          (1 << 0)
+#define        DBGMON_KERNEL           (1 << 1)
+       uint64_t        dbg_bcr[DBG_BRP_MAX];
+       uint64_t        dbg_bvr[DBG_BRP_MAX];
+       uint64_t        dbg_wcr[DBG_WRP_MAX];
+       uint64_t        dbg_wvr[DBG_WRP_MAX];
 };
 
 enum dbg_access_t {
@@ -49,15 +56,15 @@ enum dbg_access_t {
 };
 
 void dbg_monitor_init(void);
+void dbg_register_sync(struct debug_monitor_state *);
+int dbg_setup_watchpoint(struct debug_monitor_state *, vm_offset_t, vm_size_t,
+    enum dbg_access_t);
+int dbg_remove_watchpoint(struct debug_monitor_state *, vm_offset_t, 
vm_size_t);
+
+#ifdef DDB
 void dbg_show_watchpoint(void);
-int dbg_setup_watchpoint(db_expr_t addr, db_expr_t size, enum dbg_el_t el,
-    enum dbg_access_t access);
-int dbg_remove_watchpoint(db_expr_t addr, db_expr_t size, enum dbg_el_t el);
-#else
-static __inline void
-dbg_monitor_init(void)
-{
-}
 #endif
+
+#endif /* _KERNEL */
 
 #endif /* _MACHINE_DEBUG_MONITOR_H_ */

Modified: head/sys/arm64/include/pcpu.h
==============================================================================
--- head/sys/arm64/include/pcpu.h       Wed Oct 30 10:42:52 2019        
(r354174)
+++ head/sys/arm64/include/pcpu.h       Wed Oct 30 10:51:24 2019        
(r354175)
@@ -37,6 +37,7 @@
 
 typedef int (*pcpu_bp_harden)(void);
 typedef int (*pcpu_ssbd)(int);
+struct debug_monitor_state;
 
 #define        PCPU_MD_FIELDS                                                  
\
        u_int   pc_acpi_id;     /* ACPI CPU id */               \

Modified: head/sys/conf/files.arm64
==============================================================================
--- head/sys/conf/files.arm64   Wed Oct 30 10:42:52 2019        (r354174)
+++ head/sys/conf/files.arm64   Wed Oct 30 10:51:24 2019        (r354175)
@@ -136,7 +136,7 @@ arm64/arm64/cpufunc_asm.S   standard
 arm64/arm64/db_disasm.c                optional        ddb
 arm64/arm64/db_interface.c     optional        ddb
 arm64/arm64/db_trace.c         optional        ddb
-arm64/arm64/debug_monitor.c    optional        ddb
+arm64/arm64/debug_monitor.c    standard
 arm64/arm64/disassem.c         optional        ddb
 arm64/arm64/dump_machdep.c     standard
 arm64/arm64/efirt_machdep.c    optional        efirt
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to