xref: /freebsd/sys/arm64/arm64/cpu_feat.c (revision 5e690f1e12ce8699f16019854dfffd1857a801d8)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2024 Arm Ltd
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/param.h>
29 #include <sys/systm.h>
30 #include <sys/kernel.h>
31 
32 #include <machine/cpu.h>
33 #include <machine/cpu_feat.h>
34 
35 SYSCTL_NODE(_hw, OID_AUTO, feat, CTLFLAG_RD, 0, "CPU features/errata");
36 
37 /* TODO: Make this a list if we ever grow a callback other than smccc_errata */
38 static cpu_feat_errata_check_fn cpu_feat_check_cb = NULL;
39 
40 void
enable_cpu_feat(uint32_t stage)41 enable_cpu_feat(uint32_t stage)
42 {
43 	char tunable[32];
44 	struct cpu_feat **featp, *feat;
45 	uint32_t midr;
46 	u_int errata_count, *errata_list;
47 	cpu_feat_errata errata_status;
48 	cpu_feat_en check_status;
49 	bool val;
50 
51 	MPASS((stage & ~CPU_FEAT_STAGE_MASK) == 0);
52 
53 	midr = get_midr();
54 	SET_FOREACH(featp, cpu_feat_set) {
55 		feat = *featp;
56 
57 		/* Read any tunable the user may have set */
58 		if (stage == CPU_FEAT_EARLY_BOOT && PCPU_GET(cpuid) == 0) {
59 			snprintf(tunable, sizeof(tunable), "hw.feat.%s",
60 			    feat->feat_name);
61 			if (TUNABLE_BOOL_FETCH(tunable, &val)) {
62 				if (val) {
63 					feat->feat_flags |=
64 					    CPU_FEAT_USER_ENABLED;
65 				} else {
66 					feat->feat_flags |=
67 					    CPU_FEAT_USER_DISABLED;
68 				}
69 			}
70 		}
71 
72 		/* Run the enablement code at the correct stage of boot */
73 		if ((feat->feat_flags & CPU_FEAT_STAGE_MASK) != stage)
74 			continue;
75 
76 		/* If the feature is system wide run on a single CPU */
77 		if ((feat->feat_flags & CPU_FEAT_SCOPE_MASK)==CPU_FEAT_SYSTEM &&
78 		    PCPU_GET(cpuid) != 0)
79 			continue;
80 
81 		if (feat->feat_check != NULL) {
82 			check_status = feat->feat_check(feat, midr);
83 		} else {
84 			check_status = FEAT_DEFAULT_ENABLE;
85 		}
86 		/* Ignore features that are not present */
87 		if (check_status == FEAT_ALWAYS_DISABLE)
88 			goto next;
89 
90 		/* The user disabled the feature */
91 		if ((feat->feat_flags & CPU_FEAT_USER_DISABLED) != 0)
92 			goto next;
93 
94 		/*
95 		 * The feature was disabled by default and the user
96 		 * didn't enable it then skip.
97 		 */
98 		if (check_status == FEAT_DEFAULT_DISABLE &&
99 		    (feat->feat_flags & CPU_FEAT_USER_ENABLED) == 0)
100 			goto next;
101 
102 		/*
103 		 * Check if the feature has any errata that may need a
104 		 * workaround applied (or it is to install the workaround for
105 		 * known errata.
106 		 */
107 		errata_status = ERRATA_NONE;
108 		errata_list = NULL;
109 		errata_count = 0;
110 		if (feat->feat_has_errata != NULL) {
111 			if (feat->feat_has_errata(feat, midr, &errata_list,
112 			    &errata_count)) {
113 				/* Assume we are affected */
114 				errata_status = ERRATA_AFFECTED;
115 			}
116 		}
117 
118 		if (errata_status == ERRATA_AFFECTED &&
119 		    cpu_feat_check_cb != NULL) {
120 			for (int i = 0; i < errata_count; i++) {
121 				cpu_feat_errata new_status;
122 
123 				/* Check if affected by this erratum */
124 				new_status = cpu_feat_check_cb(feat,
125 				    errata_list[i]);
126 				if (new_status != ERRATA_UNKNOWN) {
127 					errata_status = new_status;
128 					errata_list = &errata_list[i];
129 					errata_count = 1;
130 					break;
131 				}
132 			}
133 		}
134 
135 		/* Shouldn't be possible */
136 		MPASS(errata_status != ERRATA_UNKNOWN);
137 
138 		if (feat->feat_enable(feat, errata_status, errata_list,
139 		    errata_count))
140 			feat->feat_enabled = true;
141 
142 next:
143 		if (!feat->feat_enabled && feat->feat_disabled != NULL)
144 			feat->feat_disabled(feat);
145 	}
146 }
147 
148 static void
enable_cpu_feat_after_dev(void * dummy __unused)149 enable_cpu_feat_after_dev(void *dummy __unused)
150 {
151 	MPASS(PCPU_GET(cpuid) == 0);
152 	enable_cpu_feat(CPU_FEAT_AFTER_DEV);
153 }
154 SYSINIT(enable_cpu_feat_after_dev, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE,
155     enable_cpu_feat_after_dev, NULL);
156 
157 void
cpu_feat_register_errata_check(cpu_feat_errata_check_fn cb)158 cpu_feat_register_errata_check(cpu_feat_errata_check_fn cb)
159 {
160 	MPASS(cpu_feat_check_cb == NULL);
161 	cpu_feat_check_cb = cb;
162 }
163