Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 25 Feb 2021 21:32:17 GMT
From:      Mitchell Horne <mhorne@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org
Subject:   git: f1847ed16ebd - releng/13.0 - arm64: extend struct db_reg to include watchpoint registers
Message-ID:  <202102252132.11PLWHKU033529@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch releng/13.0 has been updated by mhorne:

URL: https://cgit.FreeBSD.org/src/commit/?id=f1847ed16ebd16d91766934ff7c28e887fbd2846

commit f1847ed16ebd16d91766934ff7c28e887fbd2846
Author:     Mitchell Horne <mhorne@FreeBSD.org>
AuthorDate: 2021-01-28 17:49:47 +0000
Commit:     Mitchell Horne <mhorne@FreeBSD.org>
CommitDate: 2021-02-25 21:31:31 +0000

    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)
    Sponsored by:   The FreeBSD Foundation
    Approved by:    re (gjb)
    
    (cherry picked from commit f2583be110ca3a5b32f0993f1464a5c69151c62f)
    (cherry picked from commit 4786c8582c569a7245dadbdbb4638d1667c10d62)
---
 sys/arm64/arm64/identcpu.c |  2 +-
 sys/arm64/arm64/machdep.c  | 71 ++++++++++++++++++++++++++++++++++++++--------
 sys/arm64/include/armreg.h | 22 ++++++++++++++
 sys/arm64/include/reg.h    | 13 +++++++--
 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 66cd8591c7ab..81cea5431017 100644
--- a/sys/arm64/include/armreg.h
+++ b/sys/arm64/include/armreg.h
@@ -961,6 +961,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 {



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202102252132.11PLWHKU033529>