aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/arm64/arm64/identcpu.c2
-rw-r--r--sys/arm64/arm64/machdep.c71
-rw-r--r--sys/arm64/include/armreg.h22
-rw-r--r--sys/arm64/include/reg.h13
4 files changed, 92 insertions, 16 deletions
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 {