1 /*
2 * Copyright (c) 2017-2025, 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_%04u 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 applicable");
55 } else {
56 VERBOSE(ERRATUM_FORMAT, BL_STRING, cpu_str, id, "not applicable");
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 */
generic_errata_report(void)67 void generic_errata_report(void)
68 {
69 struct cpu_ops *cpu_ops = get_cpu_ops_ptr();
70 struct erratum_entry *entry = cpu_ops->errata_list_start;
71 struct erratum_entry *end = cpu_ops->errata_list_end;
72 long rev_var = cpu_get_rev_var();
73
74 for (; entry != end; entry += 1) {
75 uint64_t status = entry->check_func(rev_var);
76
77 assert(entry->id != 0);
78
79 /*
80 * Errata workaround has not been compiled in. If the errata
81 * would have applied had it been compiled in, print its status
82 * as missing.
83 */
84 if (status == ERRATA_APPLIES && entry->chosen == 0) {
85 status = ERRATA_MISSING;
86 }
87
88 print_status(status, cpu_ops->cpu_str, entry->cve, entry->id);
89 }
90 }
91
92 /*
93 * Returns whether errata needs to be reported. Passed arguments are private to
94 * a CPU type.
95 */
errata_needs_reporting(spinlock_t * lock,uint32_t * reported)96 static __unused int errata_needs_reporting(spinlock_t *lock, uint32_t *reported)
97 {
98 bool report_now;
99
100 /* If already reported, return false. */
101 if (*reported != 0U)
102 return 0;
103
104 /*
105 * Acquire lock. Determine whether status needs reporting, and then mark
106 * report status to true.
107 */
108 spin_lock(lock);
109 report_now = (*reported == 0U);
110 if (report_now)
111 *reported = 1;
112 spin_unlock(lock);
113
114 return report_now;
115 }
116
117 /*
118 * Function to print errata status for the calling CPU (and others of the same
119 * type). Must be called only:
120 * - when MMU and data caches are enabled;
121 * - after cpu_ops have been initialized in per-CPU data.
122 */
print_errata_status(void)123 void print_errata_status(void)
124 {
125 #ifdef IMAGE_BL1
126 generic_errata_report();
127 #else /* IMAGE_BL1 */
128 struct cpu_ops *cpu_ops = (void *) get_cpu_data(cpu_ops_ptr);
129
130 assert(cpu_ops != NULL);
131
132 if (errata_needs_reporting(cpu_ops->errata_lock, cpu_ops->errata_reported)) {
133 generic_errata_report();
134 }
135 #endif /* IMAGE_BL1 */
136 }
137 #endif /* !REPORT_ERRATA */
138