aboutsummaryrefslogtreecommitdiff
path: root/sys/arm64
diff options
context:
space:
mode:
authorMitchell Horne <mhorne@FreeBSD.org>2021-01-28 17:49:47 +0000
committerMitchell Horne <mhorne@FreeBSD.org>2021-02-17 16:05:00 +0000
commitf2583be110ca3a5b32f0993f1464a5c69151c62f (patch)
tree01821c3c4a8422d2aad6cba998e2ea9c85a329dd /sys/arm64
parentbd012c71592323d957b409bb5e0cf7940729650e (diff)
downloadsrc-f2583be110ca3a5b32f0993f1464a5c69151c62f.tar.gz
src-f2583be110ca3a5b32f0993f1464a5c69151c62f.zip
arm64: extend struct db_reg to include watchpoint registers
The motivation is to provide access to these registers from userspace via ptrace(2) requests PT_GETDBREGS and PT_SETDBREGS. This change breaks the ABI of these particular requests, but is justified by the fact that the intended consumers (debuggers) have not been taught to use them yet. Making this change now enables active upstream work on lldb to begin using this interface, and take advantage of the hardware debugging registers available on the platform. PR: 252860 Reported by: Michał Górny (mgorny@gentoo.org) Reviewed by: andrew, markj (earlier version) Tested by: Michał Górny (mgorny@gentoo.org) MFC after: 1 week Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D28415
Diffstat (limited to 'sys/arm64')
-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 {