feat(cpus): add a way to automatically report errata

Using the errata framework per-cpu data structure, errata can all be
reported automatically through a single standard errata reporter which
can replace the cpu-specific ones.

This reporter can also enforce the ordering requirement of errata.

Signed-off-by: Boyan Karatotev <boyan.karatotev@arm.com>
Change-Id: I7d2d5ac5bcb9d21aed0d560d7d23919a323ffdab
diff --git a/include/lib/cpus/aarch64/cpu_macros.S b/include/lib/cpus/aarch64/cpu_macros.S
index ebbe7d9..724624c 100644
--- a/include/lib/cpus/aarch64/cpu_macros.S
+++ b/include/lib/cpus/aarch64/cpu_macros.S
@@ -552,4 +552,24 @@
 		ret	x15
 	endfunc \_cpu\()_reset_func
 .endm
+
+/*
+ * Maintain compatibility with the old scheme of each cpu has its own reporting.
+ * TODO remove entirely once all cpus have been converted. This includes the
+ * cpu_ops entry, as print_errata_status can call this directly for all cpus
+ */
+.macro errata_report_shim _cpu:req
+	#if REPORT_ERRATA
+	func \_cpu\()_errata_report
+		/* normal stack frame for pretty debugging */
+		stp	x29, x30, [sp, #-16]!
+		mov	x29, sp
+
+		bl	generic_errata_report
+
+		ldp	x29, x30, [sp], #16
+		ret
+	endfunc \_cpu\()_errata_report
+	#endif
+.endm
 #endif /* CPU_MACROS_S */
diff --git a/include/lib/cpus/errata.h b/include/lib/cpus/errata.h
index e8e2d63..f8f9555 100644
--- a/include/lib/cpus/errata.h
+++ b/include/lib/cpus/errata.h
@@ -26,10 +26,29 @@
 #define ERRATUM_ENTRY_SIZE	ERRATUM_MITIGATED + ERRATUM_MITIGATED_SIZE
 
 #ifndef __ASSEMBLER__
+#include <lib/cassert.h>
 
 void print_errata_status(void);
 void errata_print_msg(unsigned int status, const char *cpu, const char *id);
 
+/*
+ * NOTE that this structure will be different on AArch32 and AArch64. The
+ * uintptr_t will reflect the change and the alignment will be correct in both.
+ */
+struct erratum_entry {
+	uintptr_t (*wa_func)(uint64_t cpu_rev);
+	uintptr_t (*check_func)(uint64_t cpu_rev);
+	/* Will fit CVEs with up to 10 character in the ID field */
+	uint32_t id;
+	/* Denote CVEs with their year or errata with 0 */
+	uint16_t cve;
+	uint8_t chosen;
+	/* TODO(errata ABI): placeholder for the mitigated field */
+	uint8_t _mitigated;
+} __packed;
+
+CASSERT(sizeof(struct erratum_entry) == ERRATUM_ENTRY_SIZE,
+	assert_erratum_entry_asm_c_different_sizes);
 #else
 
 /*
diff --git a/lib/cpus/errata_report.c b/lib/cpus/errata_report.c
index 8c04328..a37ba81 100644
--- a/lib/cpus/errata_report.c
+++ b/lib/cpus/errata_report.c
@@ -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
  */
@@ -31,9 +31,88 @@
 /* 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"
+
+#define PRINT_STATUS_DISPATCH(status, ...)					\
+	do {									\
+		assert(status <= ERRATA_MISSING);				\
+		switch (status) {						\
+		case ERRATA_NOT_APPLIES:					\
+			VERBOSE(__VA_ARGS__, "not applied");			\
+			break;							\
+		case ERRATA_APPLIES:						\
+			INFO(__VA_ARGS__, "applied");				\
+			break;							\
+		case ERRATA_MISSING:						\
+			WARN(__VA_ARGS__, "missing!");				\
+			break;							\
+		}								\
+	} while (0)
+
+
 #if !REPORT_ERRATA
 void print_errata_status(void) {}
 #else /* !REPORT_ERRATA */
+/* New errata status message printer */
+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();
+	uint32_t last_erratum_id = 0;
+	uint16_t last_cve_yr = 0;
+	bool check_cve = false;
+	/* unused because assert goes away on release */
+	bool failed __unused = false;
+
+	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;
+		}
+
+		if (entry->cve) {
+			PRINT_STATUS_DISPATCH(status, CVE_FORMAT, BL_STRING,
+				cpu_ops->cpu_str, entry->cve, entry->id);
+
+			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 {
+			PRINT_STATUS_DISPATCH(status, ERRATUM_FORMAT, BL_STRING,
+				cpu_ops->cpu_str, entry->id);
+
+			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;
+	}
+
+	/*
+	 * enforce errata and CVEs are in ascending order and that CVEs are
+	 * after errata
+	 */
+	assert(!failed);
+}
+
 /*
  * Returns whether errata needs to be reported. Passed arguments are private to
  * a CPU type.
@@ -94,14 +173,10 @@
 }
 
 /*
- * Print errata status message.
- *
- * Unknown: WARN
- * Missing: WARN
- * Applied: INFO
- * Not applied: VERBOSE
+ * Old errata status message printer
+ * TODO: remove once all cpus have been converted to the new printing method
  */
-void errata_print_msg(unsigned int status, const char *cpu, const char *id)
+void __unused errata_print_msg(unsigned int status, const char *cpu, const char *id)
 {
 	/* Errata status strings */
 	static const char *const errata_status_str[] = {