acpi: Add support for reporting CrashLog in BERT table

Crash Data are collected and sent to the OS via the ACPI BERT.

BUG=None
TEST=Built, and BERT successfully generated in the crashLog flow.

Signed-off-by: Francois Toguo <francois.toguo.fotso@intel.com>
Change-Id: I00e390d735d61beac2e89a726e39119d9b06b3df
Signed-off-by: Nikunj A. Dadhania <nikunj.dadhania@intel.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/49799
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
diff --git a/src/acpi/acpi.c b/src/acpi/acpi.c
index 05ada08..fc13689 100644
--- a/src/acpi/acpi.c
+++ b/src/acpi/acpi.c
@@ -1331,6 +1331,14 @@
 	return lpi_desc->header.length;
 }
 
+/* BERT helpers */
+bool __weak acpi_is_boot_error_src_present(void)
+{
+	return false;
+}
+
+__weak void acpi_soc_fill_bert(acpi_bert_t *bert, void **region, size_t *length) {}
+
 unsigned long __weak fw_cfg_acpi_tables(unsigned long start)
 {
 	return 0;
@@ -1352,6 +1360,7 @@
 	acpi_tpm2_t *tpm2;
 	acpi_madt_t *madt;
 	acpi_lpit_t *lpit;
+	acpi_bert_t *bert;
 	struct device *dev;
 	unsigned long fw;
 	size_t slic_size, dsdt_size;
@@ -1573,6 +1582,20 @@
 
 	current = acpi_align_current(current);
 
+	if (acpi_is_boot_error_src_present()) {
+		void *region;
+		size_t size;
+		printk(BIOS_DEBUG, "ACPI:    * BERT\n");
+		bert = (acpi_bert_t *) current;
+		acpi_soc_fill_bert(bert, &region, &size);
+		acpi_write_bert(bert, (uintptr_t)region, size);
+		if (bert->header.length >= sizeof(acpi_bert_t)) {
+			current += bert->header.length;
+			acpi_add_table(rsdp, bert);
+		}
+		current = acpi_align_current(current);
+	}
+
 	printk(BIOS_DEBUG, "current = %lx\n", current);
 
 	for (dev = all_devices; dev; dev = dev->next) {
diff --git a/src/arch/x86/acpi_bert_storage.c b/src/arch/x86/acpi_bert_storage.c
index de56291..ec31917 100644
--- a/src/arch/x86/acpi_bert_storage.c
+++ b/src/arch/x86/acpi_bert_storage.c
@@ -106,6 +106,7 @@
 	entries = bert_entry_count(status);
 	entry = acpi_hest_generic_data_nth(status, entries);
 	status->data_length += size;
+	status->raw_data_length += size;
 	if (entry)
 		entry->data_length += size;
 }
@@ -174,6 +175,7 @@
 	entry->validation_bits |= ACPI_GENERROR_VALID_TIMESTAMP;
 
 	status->data_length += sizeof(*entry);
+	status->raw_data_length += sizeof(*entry);
 	bert_bump_entry_count(status);
 
 	return entry;
@@ -186,12 +188,52 @@
 		return sizeof(cper_proc_generic_error_section_t);
 	else if (!guidcmp(guid, &CPER_SEC_PROC_IA32X64_GUID))
 		return sizeof(cper_ia32x64_proc_error_section_t);
+	else if (!guidcmp(guid, &CPER_SEC_FW_ERR_REC_REF_GUID))
+		return sizeof(cper_fw_err_rec_section_t);
 	/* else if ... sizeof(structures not yet defined) */
 
 	printk(BIOS_ERR, "Error: Requested size of unrecognized CPER GUID\n");
 	return 0;
 }
 
+void *new_cper_fw_error_crashlog(acpi_generic_error_status_t *status, size_t cl_size)
+{
+	void *cl_data = bert_allocate_storage(cl_size);
+	if (!cl_data) {
+		printk(BIOS_ERR, "Error: Crashlog entry (size %lu) would exceed available region\n",
+			cl_size);
+		return NULL;
+	}
+
+	revise_error_sizes(status, cl_size);
+
+	return cl_data;
+}
+
+/* Helper to append an ACPI Generic Error Data Entry per crashlog data */
+acpi_hest_generic_data_v300_t *bert_append_fw_err(acpi_generic_error_status_t *status)
+{
+	acpi_hest_generic_data_v300_t *entry;
+	cper_fw_err_rec_section_t *fw_err;
+
+	entry = bert_append_error_datasection(status, &CPER_SEC_FW_ERR_REC_REF_GUID);
+	if (!entry)
+		return NULL;
+
+	status->block_status |= GENERIC_ERR_STS_UNCORRECTABLE_VALID;
+	status->error_severity = ACPI_GENERROR_SEV_FATAL;
+	entry->error_severity = ACPI_GENERROR_SEV_FATAL;
+
+	fw_err = section_of_acpientry(fw_err, entry);
+
+	fw_err->record_type = CRASHLOG_RECORD_TYPE;
+	fw_err->revision = CRASHLOG_FW_ERR_REV;
+	fw_err->record_id = 0;
+	guidcpy(&fw_err->record_guid, &FW_ERR_RECORD_ID_CRASHLOG_GUID);
+
+	return entry;
+}
+
 /* Append a new ACPI Generic Error Data Entry plus CPER Error Section to an
  * existing ACPI Generic Error Status Block.  The caller is responsible for
  * the setting the status and entry severity, as well as populating all fields
@@ -486,10 +528,14 @@
 	if (!status)
 		return NULL;
 
+	status->raw_data_length = sizeof(*status);
+
 	if (!guidcmp(guid, &CPER_SEC_PROC_GENERIC_GUID))
 		r = bert_append_genproc(status);
 	else if (!guidcmp(guid, &CPER_SEC_PROC_GENERIC_GUID))
 		r = bert_append_ia32x64(status);
+	if (!guidcmp(guid, &CPER_SEC_FW_ERR_REC_REF_GUID))
+		r = bert_append_fw_err(status);
 	/* else if other types not implemented */
 	else
 		r = NULL;
diff --git a/src/arch/x86/include/arch/bert_storage.h b/src/arch/x86/include/arch/bert_storage.h
index 060e1a4..0d373e1 100644
--- a/src/arch/x86/include/arch/bert_storage.h
+++ b/src/arch/x86/include/arch/bert_storage.h
@@ -41,6 +41,9 @@
  * +--------------------------------------------------------------------+
  */
 
+#define CRASHLOG_RECORD_TYPE	0x2
+#define CRASHLOG_FW_ERR_REV	0x2
+
 /* Get implementation-specific reserved area for generating BERT info */
 void bert_reserved_region(void **start, size_t *size);
 
@@ -120,6 +123,9 @@
 acpi_hest_generic_data_v300_t *bert_append_ia32x64(
 					acpi_generic_error_status_t *status);
 
+void *new_cper_fw_error_crashlog(acpi_generic_error_status_t *status, size_t cl_size);
+acpi_hest_generic_data_v300_t *bert_append_fw_err(acpi_generic_error_status_t *status);
+
 /* Add a new event to the BERT region.  An event consists of an ACPI Error
  * Status Block, a Generic Error Data Entry, and an associated CPER Error
  * Section.
diff --git a/src/commonlib/include/commonlib/cbmem_id.h b/src/commonlib/include/commonlib/cbmem_id.h
index f58d7b1..ae644de 100644
--- a/src/commonlib/include/commonlib/cbmem_id.h
+++ b/src/commonlib/include/commonlib/cbmem_id.h
@@ -4,6 +4,7 @@
 #define _CBMEM_ID_H_
 
 #define CBMEM_ID_ACPI		0x41435049
+#define CBMEM_ID_ACPI_BERT      0x42455254
 #define CBMEM_ID_ACPI_GNVS	0x474e5653
 #define CBMEM_ID_ACPI_UCSI	0x55435349
 #define CBMEM_ID_AFTER_CAR	0xc4787a93
@@ -14,6 +15,7 @@
 #define CBMEM_ID_CBTABLE_FWD	0x43425443
 #define CBMEM_ID_CB_EARLY_DRAM	0x4544524D
 #define CBMEM_ID_CONSOLE	0x434f4e53
+#define CBMEM_ID_CPU_CRASHLOG	0x4350555f
 #define CBMEM_ID_COVERAGE	0x47434f56
 #define CBMEM_ID_EHCI_DEBUG	0xe4c1deb9
 #define CBMEM_ID_ELOG		0x454c4f47
@@ -31,6 +33,7 @@
 #define CBMEM_ID_MMC_STATUS	0x4d4d4353
 #define CBMEM_ID_MPTABLE	0x534d5054
 #define CBMEM_ID_MRCDATA	0x4d524344
+#define CBMEM_ID_PMC_CRASHLOG	0x504d435f
 #define CBMEM_ID_VAR_MRCDATA	0x4d524345
 #define CBMEM_ID_MTC		0xcb31d31c
 #define CBMEM_ID_NONE		0x00000000
@@ -76,6 +79,7 @@
 
 #define CBMEM_ID_TO_NAME_TABLE				 \
 	{ CBMEM_ID_ACPI,		"ACPI       " }, \
+	{ CBMEM_ID_ACPI_BERT,		"ACPI BERT  " }, \
 	{ CBMEM_ID_ACPI_GNVS,		"ACPI GNVS  " }, \
 	{ CBMEM_ID_ACPI_UCSI,		"ACPI UCSI  " }, \
 	{ CBMEM_ID_AGESA_RUNTIME,	"AGESA RSVD " }, \
@@ -87,6 +91,7 @@
 	{ CBMEM_ID_CB_EARLY_DRAM,	"EARLY DRAM USAGE" }, \
 	{ CBMEM_ID_CONSOLE,		"CONSOLE    " }, \
 	{ CBMEM_ID_COVERAGE,		"COVERAGE   " }, \
+	{ CBMEM_ID_CPU_CRASHLOG,	"CPU CRASHLOG"}, \
 	{ CBMEM_ID_EHCI_DEBUG,		"USBDEBUG   " }, \
 	{ CBMEM_ID_ELOG,		"ELOG       " }, \
 	{ CBMEM_ID_FREESPACE,		"FREE SPACE " }, \
@@ -101,6 +106,7 @@
 	{ CBMEM_ID_MMC_STATUS,		"MMC STATUS " }, \
 	{ CBMEM_ID_MPTABLE,		"SMP TABLE  " }, \
 	{ CBMEM_ID_MRCDATA,		"MRC DATA   " }, \
+	{ CBMEM_ID_PMC_CRASHLOG,	"PMC CRASHLOG"}, \
 	{ CBMEM_ID_VAR_MRCDATA,		"VARMRC DATA" }, \
 	{ CBMEM_ID_MTC,			"MTC        " }, \
 	{ CBMEM_ID_PIRQ,		"IRQ TABLE  " }, \
diff --git a/src/include/acpi/acpi.h b/src/include/acpi/acpi.h
index fa24902..058c0d0 100644
--- a/src/include/acpi/acpi.h
+++ b/src/include/acpi/acpi.h
@@ -1075,6 +1075,10 @@
 void acpi_create_lpit(acpi_lpit_t *lpit);
 unsigned long acpi_create_lpi_desc_ncst(acpi_lpi_desc_ncst_t *lpi_desc, uint16_t uid);
 
+/* For crashlog. */
+bool acpi_is_boot_error_src_present(void);
+void acpi_soc_fill_bert(acpi_bert_t *bert, void **region, size_t *length);
+
 /* For ACPI S3 support. */
 void __noreturn acpi_resume(void *wake_vec);
 void mainboard_suspend_resume(void);
diff --git a/src/include/cper.h b/src/include/cper.h
index b6d182e..020ac41 100644
--- a/src/include/cper.h
+++ b/src/include/cper.h
@@ -370,6 +370,19 @@
 	u16 tr;
 } cper_ia32x64_ctx_x64state_t;
 
+#define FW_ERR_RECORD_ID_CRASHLOG_GUID				\
+	GUID_INIT(0x8f87f311, 0xc998, 0x4d9e,			\
+		0xa0, 0xc4, 0x60, 0x65, 0x51, 0x8c, 0x4f, 0x6d)
+
+/* Firmware Error Record Reference, UEFI v2.8 sec N.2.10  */
+typedef struct cper_fw_err_rec_section {
+	u8 record_type;
+	u8 revision;
+	u8 reserved[6];
+	u64 record_id;
+	guid_t record_guid;
+} cper_fw_err_rec_section_t;
+
 static inline cper_timestamp_t cper_timestamp(int precise)
 {
 	cper_timestamp_t ts;
diff --git a/src/soc/intel/common/block/systemagent/memmap.c b/src/soc/intel/common/block/systemagent/memmap.c
index 86ca4e1..ce842e8 100644
--- a/src/soc/intel/common/block/systemagent/memmap.c
+++ b/src/soc/intel/common/block/systemagent/memmap.c
@@ -7,6 +7,7 @@
 #include <cpu/x86/smm.h>
 #include <intelblocks/fast_spi.h>
 #include <intelblocks/systemagent.h>
+#include <arch/bert_storage.h>
 #include <types.h>
 
 /*
@@ -49,12 +50,22 @@
  * +---------------------------+ 0
  */
 
+#define BERT_REGION_MAX_SIZE 0x10000
+
 void smm_region(uintptr_t *start, size_t *size)
 {
 	*start = sa_get_tseg_base();
 	*size = sa_get_tseg_size();
 }
 
+void bert_reserved_region(void **start, size_t *size)
+{
+	*start = cbmem_add(CBMEM_ID_ACPI_BERT, BERT_REGION_MAX_SIZE);
+	*size = BERT_REGION_MAX_SIZE;
+
+	printk(BIOS_DEBUG, "Reserving BERT start %lx, size %lx\n", (uintptr_t)*start, *size);
+}
+
 void fill_postcar_frame(struct postcar_frame *pcf)
 {
 	/* FSP does not seem to bother w.r.t. alignment when asked to place cbmem_top() */