summaryrefslogtreecommitdiff
path: root/lib/cpus/errata_report.c
blob: 4e9bdfc566143f902082e7cc610e8c1cd96bedd8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
/*
 * Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

/* Runtime firmware routines to report errata status for the current CPU. */

#include <assert.h>
#include <stdbool.h>

#include <arch_helpers.h>
#include <common/debug.h>
#include <lib/cpus/cpu_ops.h>
#include <lib/cpus/errata.h>
#include <lib/el3_runtime/cpu_data.h>
#include <lib/spinlock.h>

#ifdef IMAGE_BL1
# define BL_STRING	"BL1"
#elif defined(__aarch64__) && defined(IMAGE_BL31)
# define BL_STRING	"BL31"
#elif !defined(__aarch64__) && defined(IMAGE_BL32)
# define BL_STRING	"BL32"
#elif defined(IMAGE_BL2) && RESET_TO_BL2
# define BL_STRING "BL2"
#else
# error This image should not be printing errata status
#endif

/* Errata format: BL stage, CPU, errata ID, message */
#define ERRATA_FORMAT	"%s: %s: CPU workaround for %s was %s\n"

#define CVE_FORMAT	"%s: %s: CPU workaround for CVE %u_%u was %s\n"
#define ERRATUM_FORMAT	"%s: %s: CPU workaround for erratum %u was %s\n"


static __unused void print_status(int status, char *cpu_str, uint16_t cve, uint32_t id)
{
	if (status == ERRATA_MISSING) {
		if (cve) {
			WARN(CVE_FORMAT, BL_STRING, cpu_str, cve, id, "missing!");
		} else {
			WARN(ERRATUM_FORMAT, BL_STRING, cpu_str, id, "missing!");
		}
	} else if (status == ERRATA_APPLIES) {
		if (cve) {
			INFO(CVE_FORMAT, BL_STRING, cpu_str, cve, id, "applied");
		}  else {
			INFO(ERRATUM_FORMAT, BL_STRING, cpu_str, id, "applied");
		}
	} else {
		if (cve) {
			VERBOSE(CVE_FORMAT, BL_STRING, cpu_str, cve, id, "not applied");
		}  else {
			VERBOSE(ERRATUM_FORMAT, BL_STRING, cpu_str, id, "not applied");
		}
	}
}

#if !REPORT_ERRATA
void print_errata_status(void) {}
#else /* !REPORT_ERRATA */
/*
 * New errata status message printer
 * The order checking function is hidden behind the FEATURE_DETECTION flag to
 * save space. This functionality is only useful on development and platform
 * bringup builds, when FEATURE_DETECTION should be used anyway
 */
void __unused generic_errata_report(void)
{
	struct cpu_ops *cpu_ops = get_cpu_ops_ptr();
	struct erratum_entry *entry = cpu_ops->errata_list_start;
	struct erratum_entry *end = cpu_ops->errata_list_end;
	long rev_var = cpu_get_rev_var();
#if FEATURE_DETECTION
	uint32_t last_erratum_id = 0;
	uint16_t last_cve_yr = 0;
	bool check_cve = false;
	bool failed = false;
#endif /* FEATURE_DETECTION */

	for (; entry != end; entry += 1) {
		uint64_t status = entry->check_func(rev_var);

		assert(entry->id != 0);

		/*
		 * Errata workaround has not been compiled in. If the errata
		 * would have applied had it been compiled in, print its status
		 * as missing.
		 */
		if (status == ERRATA_APPLIES && entry->chosen == 0) {
			status = ERRATA_MISSING;
		}

		print_status(status, cpu_ops->cpu_str, entry->cve, entry->id);

#if FEATURE_DETECTION
		if (entry->cve) {
			if (last_cve_yr > entry->cve ||
			   (last_cve_yr == entry->cve && last_erratum_id >= entry->id)) {
				ERROR("CVE %u_%u was out of order!\n",
				      entry->cve, entry->id);
				failed = true;
			}
			check_cve = true;
			last_cve_yr = entry->cve;
		} else {
			if (last_erratum_id >= entry->id || check_cve) {
				ERROR("Erratum %u was out of order!\n",
				      entry->id);
				failed = true;
			}
		}
		last_erratum_id = entry->id;
#endif /* FEATURE_DETECTION */
	}

#if FEATURE_DETECTION
	/*
	 * enforce errata and CVEs are in ascending order and that CVEs are
	 * after errata
	 */
	assert(!failed);
#endif /* FEATURE_DETECTION */
}

/*
 * Returns whether errata needs to be reported. Passed arguments are private to
 * a CPU type.
 */
static __unused int errata_needs_reporting(spinlock_t *lock, uint32_t *reported)
{
	bool report_now;

	/* If already reported, return false. */
	if (*reported != 0U)
		return 0;

	/*
	 * Acquire lock. Determine whether status needs reporting, and then mark
	 * report status to true.
	 */
	spin_lock(lock);
	report_now = (*reported == 0U);
	if (report_now)
		*reported = 1;
	spin_unlock(lock);

	return report_now;
}

/*
 * Function to print errata status for the calling CPU (and others of the same
 * type). Must be called only:
 *   - when MMU and data caches are enabled;
 *   - after cpu_ops have been initialized in per-CPU data.
 */
void print_errata_status(void)
{
	struct cpu_ops *cpu_ops;
#ifdef IMAGE_BL1
	/*
	 * BL1 doesn't have per-CPU data. So retrieve the CPU operations
	 * directly.
	 */
	cpu_ops = get_cpu_ops_ptr();

	if (cpu_ops->errata_func != NULL) {
		cpu_ops->errata_func();
	}
#else /* IMAGE_BL1 */
	cpu_ops = (void *) get_cpu_data(cpu_ops_ptr);

	assert(cpu_ops != NULL);

	if (cpu_ops->errata_func == NULL) {
		return;
	}

	if (errata_needs_reporting(cpu_ops->errata_lock, cpu_ops->errata_reported)) {
		cpu_ops->errata_func();
	}
#endif /* IMAGE_BL1 */
}

/*
 * Old errata status message printer
 * TODO: remove once all cpus have been converted to the new printing method
 */
void __unused errata_print_msg(unsigned int status, const char *cpu, const char *id)
{
	/* Errata status strings */
	static const char *const errata_status_str[] = {
		[ERRATA_NOT_APPLIES] = "not applied",
		[ERRATA_APPLIES] = "applied",
		[ERRATA_MISSING] = "missing!"
	};
	static const char *const __unused bl_str = BL_STRING;
	const char *msg __unused;


	assert(status < ARRAY_SIZE(errata_status_str));
	assert(cpu != NULL);
	assert(id != NULL);

	msg = errata_status_str[status];

	switch (status) {
	case ERRATA_NOT_APPLIES:
		VERBOSE(ERRATA_FORMAT, bl_str, cpu, id, msg);
		break;

	case ERRATA_APPLIES:
		INFO(ERRATA_FORMAT, bl_str, cpu, id, msg);
		break;

	case ERRATA_MISSING:
		WARN(ERRATA_FORMAT, bl_str, cpu, id, msg);
		break;

	default:
		WARN(ERRATA_FORMAT, bl_str, cpu, id, "unknown");
		break;
	}
}
#endif /* !REPORT_ERRATA */