git: 3c25261ea29c - stable/15 - arm64: Read the CPU feature tunables once

From: Andrew Turner <andrew_at_FreeBSD.org>
Date: Mon, 22 Sep 2025 15:20:21 UTC
The branch stable/15 has been updated by andrew:

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

commit 3c25261ea29c7b3c32556db2927a95b7c7acecee
Author:     Andrew Turner <andrew@FreeBSD.org>
AuthorDate: 2025-09-19 10:05:47 +0000
Commit:     Andrew Turner <andrew@FreeBSD.org>
CommitDate: 2025-09-20 01:00:53 +0000

    arm64: Read the CPU feature tunables once
    
    Rather than reading the CPU tunables each time we enable a feature
    just read them once on boot. Reading them early on non-boot CPUs
    causes issues where they try to allocate memory before the core is set
    up to do so. As they don't change it is safe to cache the state of
    them.
    
    Sponsored by:   Arm Ltd
    Differential Revision:  https://reviews.freebsd.org/D52578
    
    (cherry picked from commit 5e0e2565288f3d1f1b4223d8bb53c7d70e0aa92c)
---
 sys/arm64/arm64/cpu_feat.c   | 35 +++++++++++++++++++++++++----------
 sys/arm64/include/cpu_feat.h |  3 +++
 2 files changed, 28 insertions(+), 10 deletions(-)

diff --git a/sys/arm64/arm64/cpu_feat.c b/sys/arm64/arm64/cpu_feat.c
index 0ffcdf2c9ed1..fb065ea463cf 100644
--- a/sys/arm64/arm64/cpu_feat.c
+++ b/sys/arm64/arm64/cpu_feat.c
@@ -54,6 +54,21 @@ enable_cpu_feat(uint32_t stage)
 	SET_FOREACH(featp, cpu_feat_set) {
 		feat = *featp;
 
+		/* Read any tunable the user may have set */
+		if (stage == CPU_FEAT_EARLY_BOOT && PCPU_GET(cpuid) == 0) {
+			snprintf(tunable, sizeof(tunable), "hw.feat.%s",
+			    feat->feat_name);
+			if (TUNABLE_BOOL_FETCH(tunable, &val)) {
+				if (val) {
+					feat->feat_flags |=
+					    CPU_FEAT_USER_ENABLED;
+				} else {
+					feat->feat_flags |=
+					    CPU_FEAT_USER_DISABLED;
+				}
+			}
+		}
+
 		/* Run the enablement code at the correct stage of boot */
 		if ((feat->feat_flags & CPU_FEAT_STAGE_MASK) != stage)
 			continue;
@@ -71,17 +86,17 @@ enable_cpu_feat(uint32_t stage)
 		if (check_status == FEAT_ALWAYS_DISABLE)
 			goto next;
 
-		snprintf(tunable, sizeof(tunable), "hw.feat.%s",
-		    feat->feat_name);
-		if (TUNABLE_BOOL_FETCH(tunable, &val)) {
-			/* Is the feature disabled by the tunable? */
-			if (!val)
-				goto next;
-			/* If enabled by the tunable then enable it */
-		} else if (check_status == FEAT_DEFAULT_DISABLE) {
-			/* No tunable set and disabled by default */
+		/* The user disabled the feature */
+		if ((feat->feat_flags & CPU_FEAT_USER_DISABLED) != 0)
+			goto next;
+
+		/*
+		 * The feature was disabled by default and the user
+		 * didn't enable it then skip.
+		 */
+		if (check_status == FEAT_DEFAULT_DISABLE &&
+		    (feat->feat_flags & CPU_FEAT_USER_ENABLED) == 0)
 			goto next;
-		}
 
 		/*
 		 * Check if the feature has any errata that may need a
diff --git a/sys/arm64/include/cpu_feat.h b/sys/arm64/include/cpu_feat.h
index 20c743a7e507..6a311d4000bb 100644
--- a/sys/arm64/include/cpu_feat.h
+++ b/sys/arm64/include/cpu_feat.h
@@ -73,6 +73,9 @@ typedef enum {
 #define	CPU_FEAT_PER_CPU	0x00000000
 #define	CPU_FEAT_SYSTEM		0x00000010
 
+#define	CPU_FEAT_USER_ENABLED	0x40000000
+#define	CPU_FEAT_USER_DISABLED	0x80000000
+
 struct cpu_feat;
 
 typedef cpu_feat_en (cpu_feat_check)(const struct cpu_feat *, u_int);