diff --git a/include/lib/cpus/aarch64/cpu_macros.S b/include/lib/cpus/aarch64/cpu_macros.S index 0d5505cbf..ebbe7d939 100644 --- a/include/lib/cpus/aarch64/cpu_macros.S +++ b/include/lib/cpus/aarch64/cpu_macros.S @@ -91,6 +91,27 @@ /* Insert list of functions */ fill_constants CPU_MAX_PWR_DWN_OPS, \_power_down_ops #endif + /* + * It is possible (although unlikely) that a cpu may have no errata in + * code. In that case the start label will not be defined. The list is + * intended to be used in a loop, so define it as zero-length for + * predictable behaviour. Since this macro is always called at the end + * of the cpu file (after all errata have been parsed) we can be sure + * that we are at the end of the list. Some cpus call declare_cpu_ops + * twice, so only do this once. + */ + .pushsection .rodata.errata_entries + .ifndef \_name\()_errata_list_start + \_name\()_errata_list_start: + .endif + .ifndef \_name\()_errata_list_end + \_name\()_errata_list_end: + .endif + .popsection + + /* and now put them in cpu_ops */ + .quad \_name\()_errata_list_start + .quad \_name\()_errata_list_end #if REPORT_ERRATA .ifndef \_name\()_cpu_str @@ -111,18 +132,20 @@ .popsection .endif + /* * Mandatory errata status printing function for CPUs of * this class. */ .quad \_name\()_errata_report + .quad \_name\()_cpu_str #ifdef IMAGE_BL31 /* Pointers to errata lock and reported flag */ .quad \_name\()_errata_lock .quad \_name\()_errata_reported -#endif -#endif +#endif /* IMAGE_BL31 */ +#endif /* REPORT_ERRATA */ #if defined(IMAGE_BL31) && CRASH_REPORTING .quad \_name\()_cpu_reg_dump @@ -148,6 +171,7 @@ \_extra1, \_extra2, \_extra3, 0, \_power_down_ops .endm +/* TODO can be deleted once all CPUs have been converted */ #if REPORT_ERRATA /* * Print status of a CPU errata @@ -230,4 +254,302 @@ b.eq \_label .endm + +/* + * Workaround wrappers for errata that apply at reset or runtime. Reset errata + * will be applied automatically + * + * _cpu: + * Name of cpu as given to declare_cpu_ops + * + * _cve: + * Whether erratum is a CVE. CVE year if yes, 0 otherwise + * + * _id: + * Erratum or CVE number. Please combine with previous field with ERRATUM + * or CVE macros + * + * _chosen: + * Compile time flag on whether the erratum is included + * + * _apply_at_reset: + * Whether the erratum should be automatically applied at reset + */ +.macro add_erratum_entry _cpu:req, _cve:req, _id:req, _chosen:req, _apply_at_reset:req + .pushsection .rodata.errata_entries + .align 3 + .ifndef \_cpu\()_errata_list_start + \_cpu\()_errata_list_start: + .endif + + /* check if unused and compile out if no references */ + .if \_apply_at_reset && \_chosen + .quad erratum_\_cpu\()_\_id\()_wa + .else + .quad 0 + .endif + /* TODO(errata ABI): this prevents all checker functions from + * being optimised away. Can be done away with unless the ABI + * needs them */ + .quad check_erratum_\_cpu\()_\_id + /* Will fit CVEs with up to 10 character in the ID field */ + .word \_id + .hword \_cve + .byte \_chosen + /* TODO(errata ABI): mitigated field for known but unmitigated + * errata */ + .byte 0x1 + .popsection +.endm + +.macro _workaround_start _cpu:req, _cve:req, _id:req, _chosen:req, _apply_at_reset:req + add_erratum_entry \_cpu, \_cve, \_id, \_chosen, \_apply_at_reset + + func erratum_\_cpu\()_\_id\()_wa + mov x8, x30 + + /* save rev_var for workarounds that might need it but don't + * restore to x0 because few will care */ + mov x7, x0 + bl check_erratum_\_cpu\()_\_id + cbz x0, erratum_\_cpu\()_\_id\()_skip +.endm + +.macro _workaround_end _cpu:req, _id:req + erratum_\_cpu\()_\_id\()_skip: + ret x8 + endfunc erratum_\_cpu\()_\_id\()_wa +.endm + +/******************************************************************************* + * Errata workaround wrappers + ******************************************************************************/ +/* + * Workaround wrappers for errata that apply at reset or runtime. Reset errata + * will be applied automatically + * + * _cpu: + * Name of cpu as given to declare_cpu_ops + * + * _cve: + * Whether erratum is a CVE. CVE year if yes, 0 otherwise + * + * _id: + * Erratum or CVE number. Please combine with previous field with ERRATUM + * or CVE macros + * + * _chosen: + * Compile time flag on whether the erratum is included + * + * in body: + * clobber x0 to x7 (please only use those) + * argument x7 - cpu_rev_var + * + * _wa clobbers: x0-x8 (PCS compliant) + */ +.macro workaround_reset_start _cpu:req, _cve:req, _id:req, _chosen:req + _workaround_start \_cpu, \_cve, \_id, \_chosen, 1 +.endm + +/* + * See `workaround_reset_start` for usage info. Additional arguments: + * + * _midr: + * Check if CPU's MIDR matches the CPU it's meant for. Must be specified + * for errata applied in generic code + */ +.macro workaround_runtime_start _cpu:req, _cve:req, _id:req, _chosen:req, _midr + /* + * Let errata specify if they need MIDR checking. Sadly, storing the + * MIDR in an .equ to retrieve automatically blows up as it stores some + * brackets in the symbol + */ + .ifnb \_midr + jump_if_cpu_midr \_midr, 1f + b erratum_\_cpu\()_\_id\()_skip + + 1: + .endif + _workaround_start \_cpu, \_cve, \_id, \_chosen, 0 +.endm + +/* + * Usage and arguments identical to `workaround_reset_start`. The _cve argument + * is kept here so the same #define can be used as that macro + */ +.macro workaround_reset_end _cpu:req, _cve:req, _id:req + _workaround_end \_cpu, \_id +.endm + +/* + * See `workaround_reset_start` for usage info. The _cve argument is kept here + * so the same #define can be used as that macro. Additional arguments: + * + * _no_isb: + * Optionally do not include the trailing isb. Please disable with the + * NO_ISB macro + */ +.macro workaround_runtime_end _cpu:req, _cve:req, _id:req, _no_isb + /* + * Runtime errata do not have a reset function to call the isb for them + * and missing the isb could be very problematic. It is also likely as + * they tend to be scattered in generic code. + */ + .ifb \_no_isb + isb + .endif + _workaround_end \_cpu, \_id +.endm + +/******************************************************************************* + * Errata workaround helpers + ******************************************************************************/ +/* + * Set a bit in a system register. Can set multiple bits but is limited by the + * way the ORR instruction encodes them. + * + * _reg: + * Register to write to + * + * _bit: + * Bit to set. Please use a descriptive #define + * + * _assert: + * Optionally whether to read back and assert that the bit has been + * written. Please disable with NO_ASSERT macro + * + * clobbers: x1 + */ +.macro sysreg_bit_set _reg:req, _bit:req, _assert=1 + mrs x1, \_reg + orr x1, x1, #\_bit + msr \_reg, x1 +.endm + +/* + * Apply erratum + * + * _cpu: + * Name of cpu as given to declare_cpu_ops + * + * _cve: + * Whether erratum is a CVE. CVE year if yes, 0 otherwise + * + * _id: + * Erratum or CVE number. Please combine with previous field with ERRATUM + * or CVE macros + * + * _chosen: + * Compile time flag on whether the erratum is included + * + * clobbers: x0-x9 (PCS compliant) + */ +.macro apply_erratum _cpu:req, _cve:req, _id:req, _chosen:req + .if \_chosen + mov x9, x30 + bl cpu_get_rev_var + bl erratum_\_cpu\()_\_id\()_wa + mov x30, x9 + + .endif +.endm + +/* + * Helpers to select which revisions errata apply to. Don't leave a link + * register as the cpu_rev_var_*** will call the ret and we can save on one. + * + * _cpu: + * Name of cpu as given to declare_cpu_ops + * + * _cve: + * Whether erratum is a CVE. CVE year if yes, 0 otherwise + * + * _id: + * Erratum or CVE number. Please combine with previous field with ERRATUM + * or CVE macros + * + * _rev_num: + * Revision to apply to + * + * in body: + * clobber: x0 to x4 + * argument: x0 - cpu_rev_var + */ +.macro check_erratum_ls _cpu:req, _cve:req, _id:req, _rev_num:req + func check_erratum_\_cpu\()_\_id + mov x1, #\_rev_num + b cpu_rev_var_ls + endfunc check_erratum_\_cpu\()_\_id +.endm + +.macro check_erratum_hs _cpu:req, _cve:req, _id:req, _rev_num:req + func check_erratum_\_cpu\()_\_id + mov x1, #\_rev_num + b cpu_rev_var_hs + endfunc check_erratum_\_cpu\()_\_id +.endm + +.macro check_erratum_range _cpu:req, _cve:req, _id:req, _rev_num_lo:req, _rev_num_hi:req + func check_erratum_\_cpu\()_\_id + mov x1, #\_rev_num_lo + mov x2, #\_rev_num_hi + b cpu_rev_var_range + endfunc check_erratum_\_cpu\()_\_id +.endm + +/******************************************************************************* + * CPU reset function wrapper + ******************************************************************************/ + +/* + * Wrapper to automatically apply all reset-time errata. Will end with an isb. + * + * _cpu: + * Name of cpu as given to declare_cpu_ops + * + * in body: + * clobber x8 to x14 + * argument x14 - cpu_rev_var + */ +.macro cpu_reset_func_start _cpu:req + func \_cpu\()_reset_func + mov x15, x30 + bl cpu_get_rev_var + mov x14, x0 + + /* short circuit the location to avoid searching the list */ + adrp x12, \_cpu\()_errata_list_start + add x12, x12, :lo12:\_cpu\()_errata_list_start + adrp x13, \_cpu\()_errata_list_end + add x13, x13, :lo12:\_cpu\()_errata_list_end + + errata_begin: + /* if head catches up with end of list, exit */ + cmp x12, x13 + b.eq errata_end + + ldr x10, [x12, #ERRATUM_WA_FUNC] + /* TODO(errata ABI): check mitigated and checker function fields + * for 0 */ + ldrb w11, [x12, #ERRATUM_CHOSEN] + + /* skip if not chosen */ + cbz x11, 1f + /* skip if runtime erratum */ + cbz x10, 1f + + /* put cpu revision in x0 and call workaround */ + mov x0, x14 + blr x10 + 1: + add x12, x12, #ERRATUM_ENTRY_SIZE + b errata_begin + errata_end: +.endm + +.macro cpu_reset_func_end _cpu:req + isb + ret x15 + endfunc \_cpu\()_reset_func +.endm #endif /* CPU_MACROS_S */ diff --git a/include/lib/cpus/cpu_ops.h b/include/lib/cpus/cpu_ops.h index 475ba91d9..8b36ff124 100644 --- a/include/lib/cpus/cpu_ops.h +++ b/include/lib/cpus/cpu_ops.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -53,9 +53,12 @@ #define CPU_PWR_DWN_OPS_SIZE 0 #endif /* defined(IMAGE_BL31) || defined(IMAGE_BL32) */ +#define CPU_ERRATA_LIST_START_SIZE CPU_WORD_SIZE +#define CPU_ERRATA_LIST_END_SIZE CPU_WORD_SIZE /* Fields required to print errata status */ #if REPORT_ERRATA #define CPU_ERRATA_FUNC_SIZE CPU_WORD_SIZE +#define CPU_CPU_STR_SIZE CPU_WORD_SIZE /* BL1 doesn't require mutual exclusion and printed flag. */ #if defined(IMAGE_BL31) || defined(IMAGE_BL32) #define CPU_ERRATA_LOCK_SIZE CPU_WORD_SIZE @@ -66,6 +69,7 @@ #endif /* defined(IMAGE_BL31) || defined(IMAGE_BL32) */ #else #define CPU_ERRATA_FUNC_SIZE 0 +#define CPU_CPU_STR_SIZE 0 #define CPU_ERRATA_LOCK_SIZE 0 #define CPU_ERRATA_PRINTED_SIZE 0 #endif /* REPORT_ERRATA */ @@ -92,8 +96,11 @@ #else #define CPU_PWR_DWN_OPS CPU_RESET_FUNC + CPU_RESET_FUNC_SIZE #endif /* __aarch64__ */ -#define CPU_ERRATA_FUNC CPU_PWR_DWN_OPS + CPU_PWR_DWN_OPS_SIZE -#define CPU_ERRATA_LOCK CPU_ERRATA_FUNC + CPU_ERRATA_FUNC_SIZE +#define CPU_ERRATA_LIST_START CPU_PWR_DWN_OPS + CPU_PWR_DWN_OPS_SIZE +#define CPU_ERRATA_LIST_END CPU_ERRATA_LIST_START + CPU_ERRATA_LIST_START_SIZE +#define CPU_ERRATA_FUNC CPU_ERRATA_LIST_END + CPU_ERRATA_LIST_END_SIZE +#define CPU_CPU_STR CPU_ERRATA_FUNC + CPU_ERRATA_FUNC_SIZE +#define CPU_ERRATA_LOCK CPU_CPU_STR + CPU_CPU_STR_SIZE #define CPU_ERRATA_PRINTED CPU_ERRATA_LOCK + CPU_ERRATA_LOCK_SIZE #if __aarch64__ #define CPU_REG_DUMP CPU_ERRATA_PRINTED + CPU_ERRATA_PRINTED_SIZE @@ -120,8 +127,11 @@ struct cpu_ops { #if (defined(IMAGE_BL31) || defined(IMAGE_BL32)) && CPU_MAX_PWR_DWN_OPS void (*pwr_dwn_ops[CPU_MAX_PWR_DWN_OPS])(void); #endif /* (defined(IMAGE_BL31) || defined(IMAGE_BL32)) && CPU_MAX_PWR_DWN_OPS */ + void *errata_list_start; + void *errata_list_end; #if REPORT_ERRATA void (*errata_func)(void); + char *cpu_str; #if defined(IMAGE_BL31) || defined(IMAGE_BL32) spinlock_t *errata_lock; unsigned int *errata_reported; diff --git a/include/lib/cpus/errata.h b/include/lib/cpus/errata.h index f9ab520fc..e8e2d6396 100644 --- a/include/lib/cpus/errata.h +++ b/include/lib/cpus/errata.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -7,11 +7,45 @@ #ifndef ERRATA_REPORT_H #define ERRATA_REPORT_H +#include + + +#define ERRATUM_WA_FUNC_SIZE CPU_WORD_SIZE +#define ERRATUM_CHECK_FUNC_SIZE CPU_WORD_SIZE +#define ERRATUM_ID_SIZE 4 +#define ERRATUM_CVE_SIZE 2 +#define ERRATUM_CHOSEN_SIZE 1 +#define ERRATUM_MITIGATED_SIZE 1 + +#define ERRATUM_WA_FUNC 0 +#define ERRATUM_CHECK_FUNC ERRATUM_WA_FUNC + ERRATUM_WA_FUNC_SIZE +#define ERRATUM_ID ERRATUM_CHECK_FUNC + ERRATUM_CHECK_FUNC_SIZE +#define ERRATUM_CVE ERRATUM_ID + ERRATUM_ID_SIZE +#define ERRATUM_CHOSEN ERRATUM_CVE + ERRATUM_CVE_SIZE +#define ERRATUM_MITIGATED ERRATUM_CHOSEN + ERRATUM_CHOSEN_SIZE +#define ERRATUM_ENTRY_SIZE ERRATUM_MITIGATED + ERRATUM_MITIGATED_SIZE + #ifndef __ASSEMBLER__ void print_errata_status(void); void errata_print_msg(unsigned int status, const char *cpu, const char *id); +#else + +/* + * errata framework macro helpers + * + * NOTE an erratum and CVE id could clash. However, both numbers are very large + * and the probablity is minuscule. Working around this makes code very + * complicated and extremely difficult to read so it is not considered. In the + * unlikely event that this does happen, prepending the CVE id with a 0 should + * resolve the conflict + */ +#define ERRATUM(id) 0, id +#define CVE(year, id) year, id +#define NO_ISB 1 +#define NO_ASSERT 0 + #endif /* __ASSEMBLER__ */ /* Errata status */