diff --git a/sys/arm64/arm64/db_trace.c b/sys/arm64/arm64/db_trace.c --- a/sys/arm64/arm64/db_trace.c +++ b/sys/arm64/arm64/db_trace.c @@ -47,6 +47,13 @@ #define FRAME_SERROR 3 #define FRAME_UNHANDLED 4 +void +db_md_list_breakpoints(void) +{ + + dbg_show_breakpoint(); +} + void db_md_list_watchpoints(void) { diff --git a/sys/arm64/arm64/debug_monitor.c b/sys/arm64/arm64/debug_monitor.c --- a/sys/arm64/arm64/debug_monitor.c +++ b/sys/arm64/arm64/debug_monitor.c @@ -44,7 +44,9 @@ #include #ifdef DDB +#include #include +#include #include #endif @@ -59,6 +61,10 @@ .dbg_flags = DBGMON_KERNEL }; +static int dbg_setup_breakpoint(struct debug_monitor_state *monitor, + vm_offset_t addr); +static int dbg_remove_breakpoint(struct debug_monitor_state *monitor, + vm_offset_t addr); static int dbg_setup_watchpoint(struct debug_monitor_state *, vm_offset_t, vm_size_t, enum dbg_access_t); static int dbg_remove_watchpoint(struct debug_monitor_state *, vm_offset_t, @@ -185,6 +191,18 @@ } #if defined(DDB) || defined(GDB) +int +kdb_cpu_set_breakpoint(vm_offset_t addr) +{ + return (dbg_setup_breakpoint(NULL, addr)); +} + +int +kdb_cpu_clr_breakpoint(vm_offset_t addr) +{ + return (dbg_remove_breakpoint(NULL, addr)); +} + void kdb_cpu_set_singlestep(void) { @@ -272,6 +290,33 @@ #endif /* DDB || GDB */ #ifdef DDB +void +dbg_show_breakpoint(void) +{ + db_breakpoint_t bkpt; + uint32_t bcr; + uint64_t addr; + int i; + + db_printf("\nhardware breakpoints:\n"); + db_printf(" break status count address symbol\n"); + db_printf(" ----- -------- ----- ------------------ ------------------\n"); + for (i = 0; i < dbg_breakpoint_num; i++) { + bcr = dbg_wb_read_reg(DBG_REG_BASE_BCR, i); + if ((bcr & DBG_WB_CTRL_E) != 0) { + addr = dbg_wb_read_reg(DBG_REG_BASE_BVR, i); + bkpt = db_find_breakpoint_here(addr); + db_printf(" %-5d %-8s %-5d 0x%16lx ", + i, "enabled", bkpt == NULL ? -1 : bkpt->count, + addr); + db_printsym((db_addr_t)addr, DB_STGY_ANY); + db_printf("\n"); + } else { + db_printf(" %-5d disabled\n", i); + } + } +} + static const char * dbg_watchtype_str(uint32_t type) { @@ -394,6 +439,60 @@ return (-1); } +static int +dbg_setup_breakpoint(struct debug_monitor_state *monitor, vm_offset_t addr) +{ + uint64_t bcr_priv; + u_int i; + + if (monitor == NULL) + monitor = &kernel_monitor; + + i = dbg_find_free_slot(monitor, DBG_TYPE_BREAKPOINT); + if (i == -1) { + printf("Can not find slot for breakpoint, max %d" + " breakpoints supported\n", dbg_breakpoint_num); + return (EBUSY); + } + + if ((monitor->dbg_flags & DBGMON_KERNEL) == 0) + bcr_priv = DBG_WB_CTRL_EL0; + else + bcr_priv = DBG_WB_CTRL_EL1; + + monitor->dbg_bvr[i] = addr; + monitor->dbg_bcr[i] = (0xf << 5) | bcr_priv | DBG_WB_CTRL_E; + monitor->dbg_enable_count++; + monitor->dbg_flags |= DBGMON_ENABLED; + dbg_register_sync(monitor); + + return (0); +} + +static int +dbg_remove_breakpoint(struct debug_monitor_state *monitor, vm_offset_t addr) +{ + u_int i; + + if (monitor == NULL) + monitor = &kernel_monitor; + + i = dbg_find_slot(monitor, DBG_TYPE_BREAKPOINT, addr); + if (i == -1) { + printf("Can not find breakpoint for address 0%lx\n", addr); + return (i); + } + + monitor->dbg_bvr[i] = 0; + monitor->dbg_bcr[i] = 0; + monitor->dbg_enable_count--; + if (monitor->dbg_enable_count == 0) + monitor->dbg_flags &= ~DBGMON_ENABLED; + dbg_register_sync(monitor); + + return (0); +} + static int dbg_setup_watchpoint(struct debug_monitor_state *monitor, vm_offset_t addr, vm_size_t size, enum dbg_access_t access) diff --git a/sys/arm64/arm64/gdb_machdep.c b/sys/arm64/arm64/gdb_machdep.c --- a/sys/arm64/arm64/gdb_machdep.c +++ b/sys/arm64/arm64/gdb_machdep.c @@ -121,9 +121,16 @@ gdb_cpu_stop_reason(int type, int code __unused) { - if (type == EXCP_WATCHPT_EL1) { + switch (type) { + case EXCP_WATCHPT_EL1: gdb_tx_str("watch:"); gdb_tx_varhex((uintmax_t)READ_SPECIALREG(far_el1)); gdb_tx_char(';'); + break; + case EXCP_BRKPT_EL1: + gdb_tx_str("hwbreak:;"); + break; + default: + break; } } diff --git a/sys/arm64/include/db_machdep.h b/sys/arm64/include/db_machdep.h --- a/sys/arm64/include/db_machdep.h +++ b/sys/arm64/include/db_machdep.h @@ -40,6 +40,9 @@ #define T_SINGLESTEP (EXCP_SOFTSTP_EL1) #define T_WATCHPOINT (EXCP_WATCHPT_EL1) +#define HAS_HW_BREAKPOINT +#define NHBREAKPOINTS 16 + typedef vm_offset_t db_addr_t; typedef long db_expr_t; diff --git a/sys/arm64/include/debug_monitor.h b/sys/arm64/include/debug_monitor.h --- a/sys/arm64/include/debug_monitor.h +++ b/sys/arm64/include/debug_monitor.h @@ -56,6 +56,7 @@ void dbg_register_sync(struct debug_monitor_state *); #ifdef DDB +void dbg_show_breakpoint(void); void dbg_show_watchpoint(void); #endif diff --git a/sys/arm64/include/kdb.h b/sys/arm64/include/kdb.h --- a/sys/arm64/include/kdb.h +++ b/sys/arm64/include/kdb.h @@ -37,6 +37,8 @@ void kdb_cpu_clear_singlestep(void); void kdb_cpu_set_singlestep(void); +int kdb_cpu_set_breakpoint(vm_offset_t addr); +int kdb_cpu_clr_breakpoint(vm_offset_t addr); int kdb_cpu_set_watchpoint(vm_offset_t addr, size_t size, int access); int kdb_cpu_clr_watchpoint(vm_offset_t addr, size_t size);