git: a090372f38f1 - main - arm64: Decode CTR_EL0 via a table

From: Andrew Turner <andrew_at_FreeBSD.org>
Date: Mon, 21 Oct 2024 12:24:21 UTC
The branch main has been updated by andrew:

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

commit a090372f38f1cc136323599f92d0acecfca5b40e
Author:     Andrew Turner <andrew@FreeBSD.org>
AuthorDate: 2024-10-18 09:17:08 +0000
Commit:     Andrew Turner <andrew@FreeBSD.org>
CommitDate: 2024-10-21 12:23:15 +0000

    arm64: Decode CTR_EL0 via a table
    
    Use the same method to decode CTR_EL0 as for the ID registers. This
    will allow us to create a common view over all CPUs.
    
    This will also allow us to create a common view for userspace and the
    kernel if we detect a difference on some CPUs, or to handle errata.
    
    Reviewed by:    imp
    Sponsored by:   Arm Ltd
    Differential Revision:  https://reviews.freebsd.org/D47124
---
 sys/arm64/arm64/identcpu.c | 118 ++++++++++++++++++++++++++++++---------------
 sys/arm64/include/armreg.h |  11 +++++
 2 files changed, 91 insertions(+), 38 deletions(-)

diff --git a/sys/arm64/arm64/identcpu.c b/sys/arm64/arm64/identcpu.c
index 126df01edfa3..7c5fdc04fdc7 100644
--- a/sys/arm64/arm64/identcpu.c
+++ b/sys/arm64/arm64/identcpu.c
@@ -337,6 +337,28 @@ struct mrs_field_value {
 	MRS_FIELD_VALUE(14ul<< _reg ## _ ## _field ## _SHIFT, "15 "_desc "s"), \
 	MRS_FIELD_VALUE(15ul<< _reg ## _ ## _field ## _SHIFT, "16 "_desc "s")
 
+/*
+ * Used for printing I/D cache line sizes & CWG/ERG, as 0 is a special case
+ * in some cases the decoded string needs to be passed in.
+ */
+#define	MRS_FIELD_VALUE_CACHE(_reg, _field, _0desc, _desc)		\
+	MRS_FIELD_VALUE(0ul << _reg ## _ ## _field ## _SHIFT, _0desc), \
+	MRS_FIELD_VALUE(1ul << _reg ## _ ## _field ## _SHIFT, "8 "   _desc), \
+	MRS_FIELD_VALUE(2ul << _reg ## _ ## _field ## _SHIFT, "16 "  _desc), \
+	MRS_FIELD_VALUE(3ul << _reg ## _ ## _field ## _SHIFT, "32 "  _desc), \
+	MRS_FIELD_VALUE(4ul << _reg ## _ ## _field ## _SHIFT, "64 "  _desc), \
+	MRS_FIELD_VALUE(5ul << _reg ## _ ## _field ## _SHIFT, "128 " _desc), \
+	MRS_FIELD_VALUE(6ul << _reg ## _ ## _field ## _SHIFT, "256 " _desc), \
+	MRS_FIELD_VALUE(7ul << _reg ## _ ## _field ## _SHIFT, "512 " _desc), \
+	MRS_FIELD_VALUE(8ul << _reg ## _ ## _field ## _SHIFT, "1k "  _desc), \
+	MRS_FIELD_VALUE(9ul << _reg ## _ ## _field ## _SHIFT, "2k "  _desc), \
+	MRS_FIELD_VALUE(10ul<< _reg ## _ ## _field ## _SHIFT, "4k "  _desc), \
+	MRS_FIELD_VALUE(11ul<< _reg ## _ ## _field ## _SHIFT, "8k "  _desc), \
+	MRS_FIELD_VALUE(12ul<< _reg ## _ ## _field ## _SHIFT, "16k " _desc), \
+	MRS_FIELD_VALUE(13ul<< _reg ## _ ## _field ## _SHIFT, "32k " _desc), \
+	MRS_FIELD_VALUE(14ul<< _reg ## _ ## _field ## _SHIFT, "64k " _desc), \
+	MRS_FIELD_VALUE(15ul<< _reg ## _ ## _field ## _SHIFT, "128k "_desc)
+
 #define	MRS_FIELD_VALUE_END	{ .desc = NULL }
 
 struct mrs_field_hwcap {
@@ -393,6 +415,62 @@ struct mrs_field {
 
 #define	MRS_FIELD_END	{ .type = MRS_INVALID, }
 
+/* CTR_EL0 */
+static const struct mrs_field_value ctr_dic[] = {
+	MRS_FIELD_VALUE_NONE_IMPL(CTR, DIC, NONE, IMPL),
+	MRS_FIELD_VALUE_END,
+};
+
+static const struct mrs_field_value ctr_idc[] = {
+	MRS_FIELD_VALUE_NONE_IMPL(CTR, IDC, NONE, IMPL),
+	MRS_FIELD_VALUE_END,
+};
+
+static const struct mrs_field_value ctr_cwg[] = {
+	MRS_FIELD_VALUE_CACHE(CTR, CWG, "Unknown CWG",
+	    "byte CWG"),
+	MRS_FIELD_VALUE_END,
+};
+
+static const struct mrs_field_value ctr_erg[] = {
+	MRS_FIELD_VALUE_CACHE(CTR, ERG, "Unknown ERG",
+	    "byte ERG"),
+	MRS_FIELD_VALUE_END,
+};
+
+static const struct mrs_field_value ctr_dline[] = {
+	MRS_FIELD_VALUE_CACHE(CTR, DLINE, "4 byte D-cacheline",
+	    "byte D-cacheline"),
+	MRS_FIELD_VALUE_END,
+};
+
+static const struct mrs_field_value ctr_l1ip[] = {
+	MRS_FIELD_VALUE(CTR_L1IP_VIPT, "VIPT I-cache"),
+	MRS_FIELD_VALUE(CTR_L1IP_PIPT, "PIPT I-cache"),
+	MRS_FIELD_VALUE_END,
+};
+
+static const struct mrs_field_value ctr_iline[] = {
+	MRS_FIELD_VALUE_CACHE(CTR, ILINE, "4 byte I-cacheline",
+	    "byte I-cacheline"),
+	MRS_FIELD_VALUE_END,
+};
+
+static const struct mrs_field ctr_fields[] = {
+	/* Bit 31 is RES1 */
+	MRS_FIELD_RES1(1, 31),
+	MRS_FIELD(CTR, DIC, false, MRS_LOWER, MRS_USERSPACE, ctr_dic),
+	MRS_FIELD(CTR, IDC, false, MRS_LOWER, MRS_USERSPACE, ctr_idc),
+	MRS_FIELD(CTR, CWG, false, MRS_HIGHER_OR_ZERO, MRS_USERSPACE, ctr_cwg),
+	MRS_FIELD(CTR, ERG, false, MRS_HIGHER_OR_ZERO, MRS_USERSPACE, ctr_erg),
+	MRS_FIELD(CTR, DLINE, false, MRS_LOWER, MRS_USERSPACE, ctr_dline),
+	/* If the ICache types are different report the safe option */
+	MRS_FIELD(CTR, L1IP, false, MRS_EXACT_IF_DIFFERENT |
+	    MRS_SAFE(CTR_L1IP_VIPT >> CTR_L1IP_SHIFT), MRS_USERSPACE,
+	    ctr_l1ip),
+	MRS_FIELD(CTR, ILINE, false, MRS_LOWER, MRS_USERSPACE, ctr_iline),
+	MRS_FIELD_END,
+};
 
 /* ID_AA64AFR0_EL1 */
 static const struct mrs_field id_aa64afr0_fields[] = {
@@ -2497,40 +2575,6 @@ parse_cpu_features_hwcap32(void)
 }
 #endif /* COMPAT_FREEBSD32 */
 
-static void
-print_ctr_fields(struct sbuf *sb, uint64_t reg, const void *arg __unused)
-{
-
-	sbuf_printf(sb, "%u byte D-cacheline,", CTR_DLINE_SIZE(reg));
-	sbuf_printf(sb, "%u byte I-cacheline,", CTR_ILINE_SIZE(reg));
-	reg &= ~(CTR_DLINE_MASK | CTR_ILINE_MASK);
-
-	switch(CTR_L1IP_VAL(reg)) {
-	case CTR_L1IP_VIPT:
-		sbuf_printf(sb, "VIPT");
-		break;
-	case CTR_L1IP_PIPT:
-		sbuf_printf(sb, "PIPT");
-		break;
-	}
-	sbuf_printf(sb, " ICache,");
-	reg &= ~CTR_L1IP_MASK;
-
-	sbuf_printf(sb, "%d byte ERG,", CTR_ERG_SIZE(reg));
-	sbuf_printf(sb, "%d byte CWG", CTR_CWG_SIZE(reg));
-	reg &= ~(CTR_ERG_MASK | CTR_CWG_MASK);
-
-	if (CTR_IDC_VAL(reg) != 0)
-		sbuf_printf(sb, ",IDC");
-	if (CTR_DIC_VAL(reg) != 0)
-		sbuf_printf(sb, ",DIC");
-	reg &= ~(CTR_IDC_MASK | CTR_DIC_MASK);
-	reg &= ~CTR_RES1;
-
-	if (reg != 0)
-		sbuf_printf(sb, ",%lx", reg);
-}
-
 static void
 print_register(struct sbuf *sb, const char *reg_name, uint64_t reg,
     void (*print_fields)(struct sbuf *, uint64_t, const void *),
@@ -2754,10 +2798,8 @@ print_cpu_features(u_int cpu, struct cpu_desc *desc,
     (prev_desc == NULL || desc->_reg != prev_desc->_reg)
 
 	/* Cache Type Register */
-	if (SHOULD_PRINT_REG(ctr)) {
-		print_register(sb, "Cache Type",
-		    desc->ctr, print_ctr_fields, NULL);
-	}
+	if (SHOULD_PRINT_REG(ctr))
+		print_id_register(sb, "Cache Type", desc->ctr, ctr_fields);
 
 	/* AArch64 Instruction Set Attribute Register 0 */
 	if (SHOULD_PRINT_REG(id_aa64isar0))
diff --git a/sys/arm64/include/armreg.h b/sys/arm64/include/armreg.h
index 7d2fffb28779..e26f9859947e 100644
--- a/sys/arm64/include/armreg.h
+++ b/sys/arm64/include/armreg.h
@@ -368,29 +368,40 @@
 #define	CTR_TminLine_MASK	(UL(0x3f) << CTR_TminLine_SHIFT)
 #define	CTR_TminLine_VAL(reg)	((reg) & CTR_TminLine_MASK)
 #define	CTR_DIC_SHIFT		29
+#define	CTR_DIC_WIDTH		1
 #define	CTR_DIC_MASK		(0x1 << CTR_DIC_SHIFT)
 #define	CTR_DIC_VAL(reg)	((reg) & CTR_DIC_MASK)
+#define	 CTR_DIC_NONE		(0x0 << CTR_DIC_SHIFT)
+#define	 CTR_DIC_IMPL		(0x1 << CTR_DIC_SHIFT)
 #define	CTR_IDC_SHIFT		28
+#define	CTR_IDC_WIDTH		1
 #define	CTR_IDC_MASK		(0x1 << CTR_IDC_SHIFT)
 #define	CTR_IDC_VAL(reg)	((reg) & CTR_IDC_MASK)
+#define	 CTR_IDC_NONE		(0x0 << CTR_IDC_SHIFT)
+#define	 CTR_IDC_IMPL		(0x1 << CTR_IDC_SHIFT)
 #define	CTR_CWG_SHIFT		24
+#define	CTR_CWG_WIDTH		4
 #define	CTR_CWG_MASK		(0xf << CTR_CWG_SHIFT)
 #define	CTR_CWG_VAL(reg)	((reg) & CTR_CWG_MASK)
 #define	CTR_CWG_SIZE(reg)	(4 << (CTR_CWG_VAL(reg) >> CTR_CWG_SHIFT))
 #define	CTR_ERG_SHIFT		20
+#define	CTR_ERG_WIDTH		4
 #define	CTR_ERG_MASK		(0xf << CTR_ERG_SHIFT)
 #define	CTR_ERG_VAL(reg)	((reg) & CTR_ERG_MASK)
 #define	CTR_ERG_SIZE(reg)	(4 << (CTR_ERG_VAL(reg) >> CTR_ERG_SHIFT))
 #define	CTR_DLINE_SHIFT		16
+#define	CTR_DLINE_WIDTH		4
 #define	CTR_DLINE_MASK		(0xf << CTR_DLINE_SHIFT)
 #define	CTR_DLINE_VAL(reg)	((reg) & CTR_DLINE_MASK)
 #define	CTR_DLINE_SIZE(reg)	(4 << (CTR_DLINE_VAL(reg) >> CTR_DLINE_SHIFT))
 #define	CTR_L1IP_SHIFT		14
+#define	CTR_L1IP_WIDTH		2
 #define	CTR_L1IP_MASK		(0x3 << CTR_L1IP_SHIFT)
 #define	CTR_L1IP_VAL(reg)	((reg) & CTR_L1IP_MASK)
 #define	 CTR_L1IP_VIPT		(2 << CTR_L1IP_SHIFT)
 #define	 CTR_L1IP_PIPT		(3 << CTR_L1IP_SHIFT)
 #define	CTR_ILINE_SHIFT		0
+#define	CTR_ILINE_WIDTH		4
 #define	CTR_ILINE_MASK		(0xf << CTR_ILINE_SHIFT)
 #define	CTR_ILINE_VAL(reg)	((reg) & CTR_ILINE_MASK)
 #define	CTR_ILINE_SIZE(reg)	(4 << (CTR_ILINE_VAL(reg) >> CTR_ILINE_SHIFT))