Index: head/sys/arm64/arm64/db_trace.c =================================================================== --- head/sys/arm64/arm64/db_trace.c +++ head/sys/arm64/arm64/db_trace.c @@ -34,6 +34,7 @@ #include #include #include + #include #include #include @@ -53,15 +54,14 @@ 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 Index: head/sys/arm64/arm64/debug_monitor.c =================================================================== --- head/sys/arm64/arm64/debug_monitor.c +++ head/sys/arm64/arm64/debug_monitor.c @@ -43,8 +43,10 @@ #include #include +#ifdef DDB #include #include +#endif enum dbg_t { DBG_TYPE_BREAKPOINT = 0, @@ -53,8 +55,9 @@ 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 @@ 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 @@ 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 @@ 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 @@ * 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 @@ 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 @@ } } } +#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 @@ } 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 @@ 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 @@ 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 @@ * * 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); } Index: head/sys/arm64/arm64/exception.S =================================================================== --- head/sys/arm64/arm64/exception.S +++ head/sys/arm64/arm64/exception.S @@ -74,15 +74,17 @@ 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 @@ 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 */ Index: head/sys/arm64/arm64/mp_machdep.c =================================================================== --- head/sys/arm64/arm64/mp_machdep.c +++ head/sys/arm64/arm64/mp_machdep.c @@ -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 @@ #include #include +#include #include #include #ifdef VFP @@ -352,6 +354,10 @@ /* 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); Index: head/sys/arm64/include/debug_monitor.h =================================================================== --- head/sys/arm64/include/debug_monitor.h +++ head/sys/arm64/include/debug_monitor.h @@ -32,13 +32,20 @@ #ifndef _MACHINE_DEBUG_MONITOR_H_ #define _MACHINE_DEBUG_MONITOR_H_ -#ifdef DDB +#ifdef _KERNEL -#include +#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 @@ }; 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_ */ Index: head/sys/arm64/include/pcpu.h =================================================================== --- head/sys/arm64/include/pcpu.h +++ head/sys/arm64/include/pcpu.h @@ -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 */ \ Index: head/sys/conf/files.arm64 =================================================================== --- head/sys/conf/files.arm64 +++ head/sys/conf/files.arm64 @@ -136,7 +136,7 @@ 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