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 #define PRINT_STATUS_DISPATCH(status, ...) \ 38 do { \ 39 assert(status <= ERRATA_MISSING); \ 40 switch (status) { \ 41 case ERRATA_NOT_APPLIES: \ 42 VERBOSE(__VA_ARGS__, "not applied"); \ 43 break; \ 44 case ERRATA_APPLIES: \ 45 INFO(__VA_ARGS__, "applied"); \ 46 break; \ 47 case ERRATA_MISSING: \ 48 WARN(__VA_ARGS__, "missing!"); \ 49 break; \ 50 } \ 51 } while (0) 52 53 54 #if !REPORT_ERRATA 55 void print_errata_status(void) {} 56 #else /* !REPORT_ERRATA */ 57 /* New errata status message printer */ 58 void __unused generic_errata_report(void) 59 { 60 struct cpu_ops *cpu_ops = get_cpu_ops_ptr(); 61 struct erratum_entry *entry = cpu_ops->errata_list_start; 62 struct erratum_entry *end = cpu_ops->errata_list_end; 63 long rev_var = cpu_get_rev_var(); 64 uint32_t last_erratum_id = 0; 65 uint16_t last_cve_yr = 0; 66 bool check_cve = false; 67 /* unused because assert goes away on release */ 68 bool failed __unused = false; 69 70 for (; entry != end; entry += 1) { 71 uint64_t status = entry->check_func(rev_var); 72 73 assert(entry->id != 0); 74 75 /* 76 * Errata workaround has not been compiled in. If the errata 77 * would have applied had it been compiled in, print its status 78 * as missing. 79 */ 80 if (status == ERRATA_APPLIES && entry->chosen == 0) { 81 status = ERRATA_MISSING; 82 } 83 84 if (entry->cve) { 85 PRINT_STATUS_DISPATCH(status, CVE_FORMAT, BL_STRING, 86 cpu_ops->cpu_str, entry->cve, entry->id); 87 88 if (last_cve_yr > entry->cve || 89 (last_cve_yr == entry->cve && last_erratum_id >= entry->id)) { 90 ERROR("CVE %u_%u was out of order!\n", 91 entry->cve, entry->id); 92 failed = true; 93 } 94 check_cve = true; 95 last_cve_yr = entry->cve; 96 } else { 97 PRINT_STATUS_DISPATCH(status, ERRATUM_FORMAT, BL_STRING, 98 cpu_ops->cpu_str, entry->id); 99 100 if (last_erratum_id >= entry->id || check_cve) { 101 ERROR("Erratum %u was out of order!\n", 102 entry->id); 103 failed = true; 104 } 105 } 106 last_erratum_id = entry->id; 107 } 108 109 /* 110 * enforce errata and CVEs are in ascending order and that CVEs are 111 * after errata 112 */ 113 assert(!failed); 114 } 115 116 /* 117 * Returns whether errata needs to be reported. Passed arguments are private to 118 * a CPU type. 119 */ 120 static __unused int errata_needs_reporting(spinlock_t *lock, uint32_t *reported) 121 { 122 bool report_now; 123 124 /* If already reported, return false. */ 125 if (*reported != 0U) 126 return 0; 127 128 /* 129 * Acquire lock. Determine whether status needs reporting, and then mark 130 * report status to true. 131 */ 132 spin_lock(lock); 133 report_now = (*reported == 0U); 134 if (report_now) 135 *reported = 1; 136 spin_unlock(lock); 137 138 return report_now; 139 } 140 141 /* 142 * Function to print errata status for the calling CPU (and others of the same 143 * type). Must be called only: 144 * - when MMU and data caches are enabled; 145 * - after cpu_ops have been initialized in per-CPU data. 146 */ 147 void print_errata_status(void) 148 { 149 struct cpu_ops *cpu_ops; 150 #ifdef IMAGE_BL1 151 /* 152 * BL1 doesn't have per-CPU data. So retrieve the CPU operations 153 * directly. 154 */ 155 cpu_ops = get_cpu_ops_ptr(); 156 157 if (cpu_ops->errata_func != NULL) { 158 cpu_ops->errata_func(); 159 } 160 #else /* IMAGE_BL1 */ 161 cpu_ops = (void *) get_cpu_data(cpu_ops_ptr); 162 163 assert(cpu_ops != NULL); 164 165 if (cpu_ops->errata_func == NULL) { 166 return; 167 } 168 169 if (errata_needs_reporting(cpu_ops->errata_lock, cpu_ops->errata_reported)) { 170 cpu_ops->errata_func(); 171 } 172 #endif /* IMAGE_BL1 */ 173 } 174 175 /* 176 * Old errata status message printer 177 * TODO: remove once all cpus have been converted to the new printing method 178 */ 179 void __unused errata_print_msg(unsigned int status, const char *cpu, const char *id) 180 { 181 /* Errata status strings */ 182 static const char *const errata_status_str[] = { 183 [ERRATA_NOT_APPLIES] = "not applied", 184 [ERRATA_APPLIES] = "applied", 185 [ERRATA_MISSING] = "missing!" 186 }; 187 static const char *const __unused bl_str = BL_STRING; 188 const char *msg __unused; 189 190 191 assert(status < ARRAY_SIZE(errata_status_str)); 192 assert(cpu != NULL); 193 assert(id != NULL); 194 195 msg = errata_status_str[status]; 196 197 switch (status) { 198 case ERRATA_NOT_APPLIES: 199 VERBOSE(ERRATA_FORMAT, bl_str, cpu, id, msg); 200 break; 201 202 case ERRATA_APPLIES: 203 INFO(ERRATA_FORMAT, bl_str, cpu, id, msg); 204 break; 205 206 case ERRATA_MISSING: 207 WARN(ERRATA_FORMAT, bl_str, cpu, id, msg); 208 break; 209 210 default: 211 WARN(ERRATA_FORMAT, bl_str, cpu, id, "unknown"); 212 break; 213 } 214 } 215 #endif /* !REPORT_ERRATA */ 216