1 /*
2  * Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 /* Runtime firmware routines to report errata status for the current CPU. */
8 
9 #include <assert.h>
10 #include <stdbool.h>
11 
12 #include <arch_helpers.h>
13 #include <common/debug.h>
14 #include <lib/cpus/cpu_ops.h>
15 #include <lib/cpus/errata.h>
16 #include <lib/el3_runtime/cpu_data.h>
17 #include <lib/spinlock.h>
18 
19 #ifdef IMAGE_BL1
20 # define BL_STRING	"BL1"
21 #elif defined(__aarch64__) && defined(IMAGE_BL31)
22 # define BL_STRING	"BL31"
23 #elif !defined(__aarch64__) && defined(IMAGE_BL32)
24 # define BL_STRING	"BL32"
25 #elif defined(IMAGE_BL2) && RESET_TO_BL2
26 # define BL_STRING "BL2"
27 #else
28 # error This image should not be printing errata status
29 #endif
30 
31 /* Errata format: BL stage, CPU, errata ID, message */
32 #define ERRATA_FORMAT	"%s: %s: CPU workaround for %s was %s\n"
33 
34 #define CVE_FORMAT	"%s: %s: CPU workaround for CVE %u_%u was %s\n"
35 #define ERRATUM_FORMAT	"%s: %s: CPU workaround for erratum %u was %s\n"
36 
37 
print_status(int status,char * cpu_str,uint16_t cve,uint32_t id)38 static __unused void print_status(int status, char *cpu_str, uint16_t cve, uint32_t id)
39 {
40 	if (status == ERRATA_MISSING) {
41 		if (cve) {
42 			WARN(CVE_FORMAT, BL_STRING, cpu_str, cve, id, "missing!");
43 		} else {
44 			WARN(ERRATUM_FORMAT, BL_STRING, cpu_str, id, "missing!");
45 		}
46 	} else if (status == ERRATA_APPLIES) {
47 		if (cve) {
48 			INFO(CVE_FORMAT, BL_STRING, cpu_str, cve, id, "applied");
49 		}  else {
50 			INFO(ERRATUM_FORMAT, BL_STRING, cpu_str, id, "applied");
51 		}
52 	} else {
53 		if (cve) {
54 			VERBOSE(CVE_FORMAT, BL_STRING, cpu_str, cve, id, "not applied");
55 		}  else {
56 			VERBOSE(ERRATUM_FORMAT, BL_STRING, cpu_str, id, "not applied");
57 		}
58 	}
59 }
60 
61 #if !REPORT_ERRATA
print_errata_status(void)62 void print_errata_status(void) {}
63 #else /* !REPORT_ERRATA */
64 /*
65  * New errata status message printer
66  * The order checking function is hidden behind the FEATURE_DETECTION flag to
67  * save space. This functionality is only useful on development and platform
68  * bringup builds, when FEATURE_DETECTION should be used anyway
69  */
generic_errata_report(void)70 void __unused generic_errata_report(void)
71 {
72 	struct cpu_ops *cpu_ops = get_cpu_ops_ptr();
73 	struct erratum_entry *entry = cpu_ops->errata_list_start;
74 	struct erratum_entry *end = cpu_ops->errata_list_end;
75 	long rev_var = cpu_get_rev_var();
76 #if FEATURE_DETECTION
77 	uint32_t last_erratum_id = 0;
78 	uint16_t last_cve_yr = 0;
79 	bool check_cve = false;
80 	bool failed = false;
81 #endif /* FEATURE_DETECTION */
82 
83 	for (; entry != end; entry += 1) {
84 		uint64_t status = entry->check_func(rev_var);
85 
86 		assert(entry->id != 0);
87 
88 		/*
89 		 * Errata workaround has not been compiled in. If the errata
90 		 * would have applied had it been compiled in, print its status
91 		 * as missing.
92 		 */
93 		if (status == ERRATA_APPLIES && entry->chosen == 0) {
94 			status = ERRATA_MISSING;
95 		}
96 
97 		print_status(status, cpu_ops->cpu_str, entry->cve, entry->id);
98 
99 #if FEATURE_DETECTION
100 		if (entry->cve) {
101 			if (last_cve_yr > entry->cve ||
102 			   (last_cve_yr == entry->cve && last_erratum_id >= entry->id)) {
103 				ERROR("CVE %u_%u was out of order!\n",
104 				      entry->cve, entry->id);
105 				failed = true;
106 			}
107 			check_cve = true;
108 			last_cve_yr = entry->cve;
109 		} else {
110 			if (last_erratum_id >= entry->id || check_cve) {
111 				ERROR("Erratum %u was out of order!\n",
112 				      entry->id);
113 				failed = true;
114 			}
115 		}
116 		last_erratum_id = entry->id;
117 #endif /* FEATURE_DETECTION */
118 	}
119 
120 #if FEATURE_DETECTION
121 	/*
122 	 * enforce errata and CVEs are in ascending order and that CVEs are
123 	 * after errata
124 	 */
125 	assert(!failed);
126 #endif /* FEATURE_DETECTION */
127 }
128 
129 /*
130  * Returns whether errata needs to be reported. Passed arguments are private to
131  * a CPU type.
132  */
errata_needs_reporting(spinlock_t * lock,uint32_t * reported)133 static __unused int errata_needs_reporting(spinlock_t *lock, uint32_t *reported)
134 {
135 	bool report_now;
136 
137 	/* If already reported, return false. */
138 	if (*reported != 0U)
139 		return 0;
140 
141 	/*
142 	 * Acquire lock. Determine whether status needs reporting, and then mark
143 	 * report status to true.
144 	 */
145 	spin_lock(lock);
146 	report_now = (*reported == 0U);
147 	if (report_now)
148 		*reported = 1;
149 	spin_unlock(lock);
150 
151 	return report_now;
152 }
153 
154 /*
155  * Function to print errata status for the calling CPU (and others of the same
156  * type). Must be called only:
157  *   - when MMU and data caches are enabled;
158  *   - after cpu_ops have been initialized in per-CPU data.
159  */
print_errata_status(void)160 void print_errata_status(void)
161 {
162 	struct cpu_ops *cpu_ops;
163 #ifdef IMAGE_BL1
164 	/*
165 	 * BL1 doesn't have per-CPU data. So retrieve the CPU operations
166 	 * directly.
167 	 */
168 	cpu_ops = get_cpu_ops_ptr();
169 
170 	if (cpu_ops->errata_func != NULL) {
171 		cpu_ops->errata_func();
172 	}
173 #else /* IMAGE_BL1 */
174 	cpu_ops = (void *) get_cpu_data(cpu_ops_ptr);
175 
176 	assert(cpu_ops != NULL);
177 
178 	if (cpu_ops->errata_func == NULL) {
179 		return;
180 	}
181 
182 	if (errata_needs_reporting(cpu_ops->errata_lock, cpu_ops->errata_reported)) {
183 		cpu_ops->errata_func();
184 	}
185 #endif /* IMAGE_BL1 */
186 }
187 
188 /*
189  * Old errata status message printer
190  * TODO: remove once all cpus have been converted to the new printing method
191  */
errata_print_msg(unsigned int status,const char * cpu,const char * id)192 void __unused errata_print_msg(unsigned int status, const char *cpu, const char *id)
193 {
194 	/* Errata status strings */
195 	static const char *const errata_status_str[] = {
196 		[ERRATA_NOT_APPLIES] = "not applied",
197 		[ERRATA_APPLIES] = "applied",
198 		[ERRATA_MISSING] = "missing!"
199 	};
200 	static const char *const __unused bl_str = BL_STRING;
201 	const char *msg __unused;
202 
203 
204 	assert(status < ARRAY_SIZE(errata_status_str));
205 	assert(cpu != NULL);
206 	assert(id != NULL);
207 
208 	msg = errata_status_str[status];
209 
210 	switch (status) {
211 	case ERRATA_NOT_APPLIES:
212 		VERBOSE(ERRATA_FORMAT, bl_str, cpu, id, msg);
213 		break;
214 
215 	case ERRATA_APPLIES:
216 		INFO(ERRATA_FORMAT, bl_str, cpu, id, msg);
217 		break;
218 
219 	case ERRATA_MISSING:
220 		WARN(ERRATA_FORMAT, bl_str, cpu, id, msg);
221 		break;
222 
223 	default:
224 		WARN(ERRATA_FORMAT, bl_str, cpu, id, "unknown");
225 		break;
226 	}
227 }
228 #endif /* !REPORT_ERRATA */
229