diff --git a/sys/arm64/arm64/identcpu.c b/sys/arm64/arm64/identcpu.c index bfbaad7a6483..c3544e9de9aa 100644 --- a/sys/arm64/arm64/identcpu.c +++ b/sys/arm64/arm64/identcpu.c @@ -350,7 +350,7 @@ static struct mrs_field id_aa64dfr0_fields[] = { MRS_FIELD(ID_AA64DFR0, PMSVer, false, MRS_EXACT, id_aa64dfr0_pmsver), MRS_FIELD(ID_AA64DFR0, CTX_CMPs, false, MRS_EXACT, id_aa64dfr0_ctx_cmps), - MRS_FIELD(ID_AA64DFR0, WRPs, false, MRS_EXACT, id_aa64dfr0_wrps), + MRS_FIELD(ID_AA64DFR0, WRPs, false, MRS_LOWER, id_aa64dfr0_wrps), MRS_FIELD(ID_AA64DFR0, BRPs, false, MRS_LOWER, id_aa64dfr0_brps), MRS_FIELD(ID_AA64DFR0, PMUVer, false, MRS_EXACT, id_aa64dfr0_pmuver), MRS_FIELD(ID_AA64DFR0, TraceVer, false, MRS_EXACT, diff --git a/sys/arm64/arm64/machdep.c b/sys/arm64/arm64/machdep.c index bf44dba19482..73b06beeba7e 100644 --- a/sys/arm64/arm64/machdep.c +++ b/sys/arm64/arm64/machdep.c @@ -321,8 +321,8 @@ int fill_dbregs(struct thread *td, struct dbreg *regs) { struct debug_monitor_state *monitor; - int count, i; - uint8_t debug_ver, nbkpts; + int i; + uint8_t debug_ver, nbkpts, nwtpts; memset(regs, 0, sizeof(*regs)); @@ -330,23 +330,30 @@ fill_dbregs(struct thread *td, struct dbreg *regs) &debug_ver); extract_user_id_field(ID_AA64DFR0_EL1, ID_AA64DFR0_BRPs_SHIFT, &nbkpts); + extract_user_id_field(ID_AA64DFR0_EL1, ID_AA64DFR0_WRPs_SHIFT, + &nwtpts); /* * The BRPs field contains the number of breakpoints - 1. Armv8-A * allows the hardware to provide 2-16 breakpoints so this won't - * overflow an 8 bit value. + * overflow an 8 bit value. The same applies to the WRPs field. */ - count = nbkpts + 1; + nbkpts++; + nwtpts++; - regs->db_info = debug_ver; - regs->db_info <<= 8; - regs->db_info |= count; + regs->db_debug_ver = debug_ver; + regs->db_nbkpts = nbkpts; + regs->db_nwtpts = nwtpts; monitor = &td->td_pcb->pcb_dbg_regs; if ((monitor->dbg_flags & DBGMON_ENABLED) != 0) { - for (i = 0; i < count; i++) { - regs->db_regs[i].dbr_addr = monitor->dbg_bvr[i]; - regs->db_regs[i].dbr_ctrl = monitor->dbg_bcr[i]; + for (i = 0; i < nbkpts; i++) { + regs->db_breakregs[i].dbr_addr = monitor->dbg_bvr[i]; + regs->db_breakregs[i].dbr_ctrl = monitor->dbg_bcr[i]; + } + for (i = 0; i < nwtpts; i++) { + regs->db_watchregs[i].dbw_addr = monitor->dbg_wvr[i]; + regs->db_watchregs[i].dbw_ctrl = monitor->dbg_wcr[i]; } } @@ -365,9 +372,10 @@ set_dbregs(struct thread *td, struct dbreg *regs) monitor = &td->td_pcb->pcb_dbg_regs; count = 0; monitor->dbg_enable_count = 0; + for (i = 0; i < DBG_BRP_MAX; i++) { - addr = regs->db_regs[i].dbr_addr; - ctrl = regs->db_regs[i].dbr_ctrl; + addr = regs->db_breakregs[i].dbr_addr; + ctrl = regs->db_breakregs[i].dbr_ctrl; /* Don't let the user set a breakpoint on a kernel address. */ if (addr >= VM_MAXUSER_ADDRESS) @@ -399,6 +407,45 @@ set_dbregs(struct thread *td, struct dbreg *regs) monitor->dbg_bvr[i] = addr; monitor->dbg_bcr[i] = ctrl; } + + for (i = 0; i < DBG_WRP_MAX; i++) { + addr = regs->db_watchregs[i].dbw_addr; + ctrl = regs->db_watchregs[i].dbw_ctrl; + + /* Don't let the user set a watchpoint on a kernel address. */ + if (addr >= VM_MAXUSER_ADDRESS) + return (EINVAL); + + /* + * Some control fields are ignored, and other bits reserved. + * Only unlinked watchpoints are supported. + */ + ctrl &= DBG_WCR_EN | DBG_WCR_PAC | DBG_WCR_LSC | DBG_WCR_BAS | + DBG_WCR_MASK; + + if ((ctrl & DBG_WCR_EN) != 0) { + /* Only target EL0. */ + if ((ctrl & DBG_WCR_PAC) != DBG_WCR_PAC_EL0) + return (EINVAL); + + /* Must set at least one of the load/store bits. */ + if ((ctrl & DBG_WCR_LSC) == 0) + return (EINVAL); + + /* + * When specifying the address range with BAS, the MASK + * field must be zero. + */ + if ((ctrl & DBG_WCR_BAS) != DBG_WCR_BAS_MASK && + (ctrl & DBG_WCR_MASK) != 0) + return (EINVAL); + + monitor->dbg_enable_count++; + } + monitor->dbg_wvr[i] = addr; + monitor->dbg_wcr[i] = ctrl; + } + if (monitor->dbg_enable_count > 0) monitor->dbg_flags |= DBGMON_ENABLED; diff --git a/sys/arm64/include/armreg.h b/sys/arm64/include/armreg.h index 70390d4ebf1e..af6e84e6c772 100644 --- a/sys/arm64/include/armreg.h +++ b/sys/arm64/include/armreg.h @@ -962,6 +962,28 @@ #define DBG_BCR_BT_SHIFT 20 #define DBG_BCR_BT (0xf << DBG_BCR_BT_SHIFT) +/* Debug Watchpoint Control Registers */ +#define DBG_WCR_EN 0x1 +#define DBG_WCR_PAC_SHIFT 1 +#define DBG_WCR_PAC (0x3 << DBG_WCR_PAC_SHIFT) +#define DBG_WCR_PAC_EL1 (0x1 << DBG_WCR_PAC_SHIFT) +#define DBG_WCR_PAC_EL0 (0x2 << DBG_WCR_PAC_SHIFT) +#define DBG_WCR_LSC_SHIFT 3 +#define DBG_WCR_LSC (0x3 << DBG_WCR_LSC_SHIFT) +#define DBG_WCR_BAS_SHIFT 5 +#define DBG_WCR_BAS (0xff << DBG_WCR_BAS_SHIFT) +#define DBG_WCR_BAS_MASK DBG_WCR_BAS +#define DBG_WCR_HMC_SHIFT 13 +#define DBG_WCR_HMC (0x1 << DBG_WCR_HMC_SHIFT) +#define DBG_WCR_SSC_SHIFT 14 +#define DBG_WCR_SSC (0x3 << DBG_WCR_SSC_SHIFT) +#define DBG_WCR_LBN_SHIFT 16 +#define DBG_WCR_LBN (0xf << DBG_WCR_LBN_SHIFT) +#define DBG_WCR_WT_SHIFT 20 +#define DBG_WCR_WT (0x1 << DBG_WCR_WT_SHIFT) +#define DBG_WCR_MASK_SHIFT 24 +#define DBG_WCR_MASK (0x1f << DBG_WCR_MASK_SHIFT) + /* Perfomance Monitoring Counters */ #define PMCR_E (1 << 0) /* Enable all counters */ #define PMCR_P (1 << 1) /* Reset all counters */ diff --git a/sys/arm64/include/reg.h b/sys/arm64/include/reg.h index aafe02b70925..9cfc5ea1d437 100644 --- a/sys/arm64/include/reg.h +++ b/sys/arm64/include/reg.h @@ -60,14 +60,21 @@ struct fpreg32 { }; struct dbreg { - uint32_t db_info; - uint32_t db_pad; + uint8_t db_debug_ver; + uint8_t db_nbkpts; + uint8_t db_nwtpts; + uint8_t db_pad[5]; struct { uint64_t dbr_addr; uint32_t dbr_ctrl; uint32_t dbr_pad; - } db_regs[16]; + } db_breakregs[16]; + struct { + uint64_t dbw_addr; + uint32_t dbw_ctrl; + uint32_t dbw_pad; + } db_watchregs[16]; }; struct dbreg32 {