Francois Toguo Fotso has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/49258 )
Change subject: This change implements CrashLog for intel TGL ......................................................................
This change implements CrashLog for intel TGL
Signed-off-by: Francois Toguo francois.toguo.fotso@intel.com Change-Id: I2a4422ca5a61e2dac71cf46ca22ed793891ef85a --- M src/acpi/acpi.c M src/commonlib/include/commonlib/cbmem_id.h M src/include/acpi/acpi.h M src/include/cper.h M src/soc/intel/common/Makefile.inc A src/soc/intel/common/acpi_bert_storage.c A src/soc/intel/common/block/include/intelblocks/bert_storage.h M src/soc/intel/common/block/include/intelblocks/pmclib.h M src/soc/intel/common/block/pmc/pmclib.c M src/soc/intel/common/block/systemagent/memmap.c M src/soc/intel/tigerlake/Makefile.inc M src/soc/intel/tigerlake/acpi.c M src/soc/intel/tigerlake/chipset.cb A src/soc/intel/tigerlake/crashlog_lib.c A src/soc/intel/tigerlake/include/soc/crashlog_def.h A src/soc/intel/tigerlake/include/soc/crashlog_lib.h M src/soc/intel/tigerlake/include/soc/iomap.h M src/soc/intel/tigerlake/include/soc/pci_devs.h M src/soc/intel/tigerlake/romstage/fsp_params.c M src/vendorcode/intel/fsp/fsp2_0/tigerlake/FspmUpd.h 20 files changed, 1,605 insertions(+), 5 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/58/49258/1
diff --git a/src/acpi/acpi.c b/src/acpi/acpi.c index a16800b..c8c8934 100644 --- a/src/acpi/acpi.c +++ b/src/acpi/acpi.c @@ -1293,6 +1293,29 @@ acpi_checksum((void *) fadt, header->length); }
+/* Boot Error Record Table creation */ +void acpi_create_bert(acpi_bert_t *bert) +{ + acpi_header_t *header = &(bert->header); + if (!header) + return; + + memset((void *) bert, 0, sizeof(acpi_bert_t)); + + memcpy(header->signature, "BERT", 4); + header->length = sizeof(acpi_bert_t); + header->revision = get_acpi_table_revision(BERT); + memcpy(header->oem_id, OEM_ID, 6); + memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8); + memcpy(header->asl_compiler_id, ASLC, 4); + header->asl_compiler_revision = asl_revision; + + acpi_soc_fill_bert(bert); + + /* (Re)calculate length and checksum. */ + header->checksum = acpi_checksum((void *)header, header->length); +} + unsigned long __weak fw_cfg_acpi_tables(unsigned long start) { return 0; @@ -1313,6 +1336,7 @@ acpi_tcpa_t *tcpa; acpi_tpm2_t *tpm2; acpi_madt_t *madt; + acpi_bert_t *bert; struct device *dev; unsigned long fw; size_t slic_size, dsdt_size; @@ -1517,6 +1541,16 @@ } current = acpi_align_current(current);
+ if (acpi_is_boot_error_src_present()) { + printk(BIOS_DEBUG, "ACPI: * BERT\n"); + bert = (acpi_bert_t *) current; + acpi_create_bert(bert); + if (bert->header.length >= sizeof(acpi_bert_t)) { + current += bert->header.length; + acpi_add_table(rsdp, bert); + } + } + printk(BIOS_DEBUG, "current = %lx\n", current);
for (dev = all_devices; dev; dev = dev->next) { 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 1c364a0..724eefd 100644 --- a/src/include/acpi/acpi.h +++ b/src/include/acpi/acpi.h @@ -1025,6 +1025,11 @@ unsigned long acpi_create_hest_error_source(acpi_hest_t *hest, acpi_hest_esd_t *esd, u16 type, void *data, u16 len);
+/* For crashlog. */ +void acpi_create_bert(acpi_bert_t *bert); +bool acpi_is_boot_error_src_present(void); +void acpi_soc_fill_bert(acpi_bert_t *bert); + /* 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..bce262f 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; +} __packed 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/Makefile.inc b/src/soc/intel/common/Makefile.inc index 9993bce..962902f 100644 --- a/src/soc/intel/common/Makefile.inc +++ b/src/soc/intel/common/Makefile.inc @@ -20,6 +20,7 @@ ramstage-$(CONFIG_SOC_INTEL_COMMON_ACPI_WAKE_SOURCE) += acpi_wake_source.c ramstage-y += vbt.c ramstage-$(CONFIG_SOC_INTEL_COMMON_NHLT) += nhlt.c +ramstage-y += acpi_bert_storage.c
bootblock-$(CONFIG_TPM_CR50) += tpm_tis.c verstage-$(CONFIG_TPM_CR50) += tpm_tis.c diff --git a/src/soc/intel/common/acpi_bert_storage.c b/src/soc/intel/common/acpi_bert_storage.c new file mode 100644 index 0000000..af65098 --- /dev/null +++ b/src/soc/intel/common/acpi_bert_storage.c @@ -0,0 +1,378 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <cbmem.h> +#include <console/console.h> +#include <cpu/x86/name.h> +#include <cpu/x86/msr.h> +#include <cpu/x86/lapic.h> +#include <acpi/acpi.h> +#include <intelblocks/bert_storage.h> +#include <string.h> + +/* BERT region management: Allow the chipset to determine the specific + * location of the BERT region. We find that base and size, then manage + * the allocation of error information within it. + * + * Use simple static variables for managing the BERT region. This is a thin + * implementation; it is only created and consumed by coreboot, and only in + * a single stage, and we don't want its information to survive reboot or + * resume cycles. If the requirements change, consider using IMD to help + * manage the space. + */ +static int bert_region_broken; +static void *bert_region_base; +static size_t bert_region_size; +static size_t bert_region_used; + +/* Calculate the remaining space in the BERT region. This knowledge may help + * the caller prioritize the information to store. + */ +size_t bert_storage_remaining(void) +{ + return bert_region_broken ? 0 : bert_region_size - bert_region_used; +} + +int bert_errors_present(void) +{ + return bert_region_broken ? 0 : !!bert_region_used; +} + +void bert_errors_region(void **start, size_t *size) +{ + if (bert_region_broken) { + *start = NULL; + *size = 0; + return; + } + + /* No metadata, etc. with our region, so this is easy */ + *start = bert_region_base; + *size = bert_region_used; +} + +static void *bert_allocate_storage(size_t size) +{ + size_t alloc; + + if (bert_region_broken) + return NULL; + if (bert_region_used + size > bert_region_size) + return NULL; + + alloc = bert_region_used; + bert_region_used += size; + + return (void *)((u8 *)bert_region_base + alloc); +} + +/* Generic Error Status: Each Status represents a unique error event within + * the BERT errors region. Each event may have multiple errors associated + * with it. + */ + +/* Find the nth (1-based) Generic Data Structure attached to an Error Status */ +static void *acpi_hest_generic_data_nth( + acpi_generic_error_status_t *status, int num) +{ + acpi_hest_generic_data_v300_t *ptr; + size_t struct_size; + + if (!num || num > bert_entry_count(status)) + return NULL; + + ptr = (acpi_hest_generic_data_v300_t *)(status + 1); + while (--num) { + if (ptr->revision == HEST_GENERIC_ENTRY_V300) + struct_size = sizeof(acpi_hest_generic_data_v300_t); + else + struct_size = sizeof(acpi_hest_generic_data_t); + ptr = (acpi_hest_generic_data_v300_t *)( + (u8 *)ptr + + ptr->data_length + + struct_size); + } + return ptr; +} + +/* Update data_length for this Error Status, and final Data Entry it contains */ +static void revise_error_sizes(acpi_generic_error_status_t *status, size_t size) +{ + acpi_hest_generic_data_v300_t *entry; + int entries; + + if (!status) + return; + + entries = bert_entry_count(status); + entry = acpi_hest_generic_data_nth(status, entries); + status->data_length += size; + if (entry) + entry->data_length += size; + +} + +/* Create space for a new BERT Generic Error Status Block, by finding the next + * available slot and moving the ending location. There is nothing to designate + * this as another Generic Error Status Block (e.g. no signature); only that it + * is within the BERT region. + * + * It is up to the caller to correctly fill the information, including status + * and error severity, and to update/maintain data offsets and lengths as + * entries are added. + */ +static acpi_generic_error_status_t *new_bert_status(void) +{ + acpi_generic_error_status_t *status; + + status = bert_allocate_storage(sizeof(*status)); + + if (!status) { + printk(BIOS_ERR, "Error: New BERT error entry would exceed available region\n"); + return NULL; + } + + revise_error_sizes(status, sizeof(*status)); + status->error_severity = ACPI_GENERROR_SEV_NONE; + return status; +} + +/* Generic Error Data: Each Generic Error Status may contain zero or more + * Generic Error Data structures. The data structures describe particular + * error(s) associated with an event. The definition for the structure is + * found in the ACPI spec, however the data types and any accompanying data + * definitions are in the Common Platform Error Record appendix of the UEFI + * spec. + */ + +/* Create space for a new BERT Generic Data Entry. Update the count and + * data length in the parent Generic Error Status Block. Version 0x300 of + * the structure is used, and the timestamp is filled and marked precise + * (i.e. assumed close enough for reporting). + * + * It is up to the caller to fill the Section Type field and add the Common + * Platform Error Record type data as appropriate. In addition, the caller + * should update the error severity, and may optionally add FRU information + * or override any existing information. + */ +static acpi_hest_generic_data_v300_t *new_generic_error_entry( + acpi_generic_error_status_t *status) +{ + acpi_hest_generic_data_v300_t *entry; + + if (bert_entry_count(status) == GENERIC_ERR_STS_ENTRY_COUNT_MAX) { + printk(BIOS_ERR, "Error: New BERT error would exceed maximum entries\n"); + return NULL; + } + + entry = bert_allocate_storage(sizeof(*entry)); + if (!entry) { + printk(BIOS_ERR, "Error: New BERT error entry would exceed available region\n"); + return NULL; + } + + entry->revision = HEST_GENERIC_ENTRY_V300; + + entry->timestamp = cper_timestamp(CPER_TIMESTAMP_PRECISE); + entry->validation_bits |= ACPI_GENERROR_VALID_TIMESTAMP; + + status->data_length += sizeof(*entry); + bert_bump_entry_count(status); + + return entry; +} + +/* Find the size of a CPER error section w/o any add-ons */ +static size_t sizeof_error_section(guid_t *guid) +{ + 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; +} + +/* 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 + * of the error section. + */ +acpi_hest_generic_data_v300_t *bert_append_error_datasection( + acpi_generic_error_status_t *status, guid_t *guid) +{ + acpi_hest_generic_data_v300_t *entry; + void *sect; + size_t sect_size; + + sect_size = sizeof_error_section(guid); + if (!sect_size) + return NULL; /* Don't allocate structure if bad GUID passed */ + + if (sizeof(*entry) + sect_size > bert_storage_remaining()) + return NULL; + + entry = new_generic_error_entry(status); + if (!entry) + return NULL; + + /* error section immediately follows the Generic Error Data Entry */ + sect = bert_allocate_storage(sect_size); + if (!sect) + return NULL; + + revise_error_sizes(status, sect_size); + + guidcpy(&entry->section_type, guid); + return entry; +} + +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 = 2; /* SOC Firmware error recort Type 2 */ + fw_err->revision = 2; + fw_err->record_id = 0; + guidcpy(&fw_err->record_guid, &FW_ERR_RECORD_ID_CRASHLOG_GUID); + + return entry; +} + +static const char * const generic_error_types[] = { + "PROCESSOR_GENERIC", + "PROCESSOR_SPECIFIC_X86", + "PROCESSOR_SPECIFIC_ARM", + "PLATFORM_MEMORY", + "PLATFORM_MEMORY2", + "PCIE", + "FW_ERROR_RECORD", + "PCI_PCIX_BUS", + "PCI_DEVICE", + "DMAR_GENERIC", + "DIRECTED_IO_DMAR", + "IOMMU_DMAR", + "UNRECOGNIZED" +}; + +static const char *generic_error_name(guid_t *guid) +{ + if (!guidcmp(guid, &CPER_SEC_PROC_GENERIC_GUID)) + return generic_error_types[0]; + if (!guidcmp(guid, &CPER_SEC_PROC_IA32X64_GUID)) + return generic_error_types[1]; + if (!guidcmp(guid, &CPER_SEC_PROC_ARM_GUID)) + return generic_error_types[2]; + if (!guidcmp(guid, &CPER_SEC_PLATFORM_MEM_GUID)) + return generic_error_types[3]; + if (!guidcmp(guid, &CPER_SEC_PLATFORM_MEM2_GUID)) + return generic_error_types[4]; + if (!guidcmp(guid, &CPER_SEC_PCIE_GUID)) + return generic_error_types[5]; + if (!guidcmp(guid, &CPER_SEC_FW_ERR_REC_REF_GUID)) + return generic_error_types[6]; + if (!guidcmp(guid, &CPER_SEC_PCI_X_BUS_GUID)) + return generic_error_types[7]; + if (!guidcmp(guid, &CPER_SEC_PCI_DEV_GUID)) + return generic_error_types[8]; + if (!guidcmp(guid, &CPER_SEC_DMAR_GENERIC_GUID)) + return generic_error_types[9]; + if (!guidcmp(guid, &CPER_SEC_DMAR_VT_GUID)) + return generic_error_types[10]; + if (!guidcmp(guid, &CPER_SEC_DMAR_IOMMU_GUID)) + return generic_error_types[11]; + return generic_error_types[12]; +} + +/* 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. + */ +acpi_generic_error_status_t *bert_new_event(guid_t *guid) +{ + size_t size; + acpi_generic_error_status_t *status; + acpi_hest_generic_data_v300_t *entry, *r; + + size = sizeof(*status); + size += sizeof(*entry); + size += sizeof_error_section(guid); + + if (size > bert_storage_remaining()) { + printk(BIOS_ERR, "Error: Not enough BERT region space to add event for type %s\n", + generic_error_name(guid)); + return NULL; + } + + status = new_bert_status(); + if (!status) + return NULL; + + 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; + + if (r) + return status; + return NULL; +} + + +/* The region must be in memory marked as reserved. If not implemented, + * skip generating the information in the region. + */ +__weak void bert_reserved_region(void **start, size_t *size) +{ + printk(BIOS_ERR, "Error: %s not implemented. BERT region generation disabled\n", + __func__); + *start = NULL; + *size = 0; +} + +static void bert_storage_setup(int unused) +{ + /* Always start with a blank bert region. Make sure nothing is + * maintained across reboots or resumes. + */ + bert_region_broken = 0; + bert_region_used = 0; + + bert_reserved_region(&bert_region_base, &bert_region_size); + + if (!bert_region_base || !bert_region_size) { + printk(BIOS_ERR, "Bug: Can't find/add BERT storage area\n"); + bert_region_broken = 1; + return; + } + + memset(bert_region_base, 0, bert_region_size); +} + +RAMSTAGE_CBMEM_INIT_HOOK(bert_storage_setup) diff --git a/src/soc/intel/common/block/include/intelblocks/bert_storage.h b/src/soc/intel/common/block/include/intelblocks/bert_storage.h new file mode 100644 index 0000000..e222fa9 --- /dev/null +++ b/src/soc/intel/common/block/include/intelblocks/bert_storage.h @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _BERT_STORAGE_H_ +#define _BERT_STORAGE_H_ + +#include <stdint.h> +#include <acpi/acpi.h> + +/* Items in the BERT region + * + * * Each item begins with a Generic Error Status Block + * * Zero or more Generic Error Data Entries follow, and + * are associated with the Status Block + * * Each Generic Error Data Entry must be a certain type, + * as defined in the UEFI CPER appendix + * * Each type may allow zero or more additional sets of + * data, e.g. error descriptions, or processor contexts. + * + * In the example layout below, there are three BERT region + * entries. The first two are a single error. The third + * has two errors, with one providing a variable amount + * of additional information. + * + * +====================================================================+ + * | Generic Error | Generic Error | Platform Memory Error | + * | Status | Data Entry | | + * |====================================================================| + * | Generic Error | Generic Error | Generic Processor Error | + * | Status | Data Entry | | + * |====================================================================| + * | Generic Error | Generic Error | IA32/X64 Processor Error | + * | Status | Data Entry | +----------------------------+ + * | | | | Error Check Data | + * | | | +----------------------------+ + * | | | | MSR Context | + * | | | +----------------------------+ + * | | | | X64 Registers Context | + * | +-----------------+----+----------------------------+ + * | | Generic Error | PCI Express Error | + * | | Data Entry | | + * +--------------------------------------------------------------------+ + */ + +/* Get implementation-specific reserved area for generating BERT info */ +void bert_reserved_region(void **start, size_t *size); + +/* Get the region where BERT error structures have been constructed for + * generating the ACPI table + */ +void bert_errors_region(void **start, size_t *size); + +/* Get amount of available storage left for error info */ +size_t bert_storage_remaining(void); +/* Find if errors were added, a BERT region is present, and ACPI table needed */ +int bert_errors_present(void); + +/* Get the number of entries associated with status */ +static inline size_t bert_entry_count(acpi_generic_error_status_t *status) +{ + return (status->block_status & GENERIC_ERR_STS_ENTRY_COUNT_MASK) + >> GENERIC_ERR_STS_ENTRY_COUNT_SHIFT; +} + +/* Increment the number of entries this status describes */ +static inline void bert_bump_entry_count(acpi_generic_error_status_t *status) +{ + int count; + + count = bert_entry_count(status) + 1; + status->block_status &= ~GENERIC_ERR_STS_ENTRY_COUNT_MASK; + status->block_status |= count << GENERIC_ERR_STS_ENTRY_COUNT_SHIFT; +} + +/* Find the address of the first Generic Data structure from its status entry */ +static inline acpi_hest_generic_data_v300_t *acpi_hest_generic_data3( + acpi_generic_error_status_t *status) +{ + return (acpi_hest_generic_data_v300_t *) + ((u8 *)status + sizeof(*status)); +} + +/* Find the address of a Generic Data structure's CPER error record section */ +#define section_of_acpientry(A, B) ((typeof(A))((u8 *)(B) + sizeof(*(B)))) + +/* 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 + * of the error section. + */ +acpi_hest_generic_data_v300_t *bert_append_error_datasection( + acpi_generic_error_status_t *status, guid_t *guid); + +/* Helper to append an ACPI Generic Error Data Entry plus a CPER IA32/X64 + * Processor Error Section. As many fields are populated as possible for the + * caller. + */ + +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. + */ +acpi_generic_error_status_t *bert_new_event(guid_t *guid); + +#endif /* _BERT_STORAGE_H_ */ diff --git a/src/soc/intel/common/block/include/intelblocks/pmclib.h b/src/soc/intel/common/block/include/intelblocks/pmclib.h index ecc8166..f7e052b 100644 --- a/src/soc/intel/common/block/include/intelblocks/pmclib.h +++ b/src/soc/intel/common/block/include/intelblocks/pmclib.h @@ -235,4 +235,11 @@ /* API to set ACPI mode */ void pmc_set_acpi_mode(void);
+/* + * Send PMC IPC command + */ + +int pmc_cl_send_ipc_cmd(uint32_t cmd, uint32_t sub, uint8_t *in, uint32_t inlen, + uint32_t *out, uint32_t outlen); + #endif /* SOC_INTEL_COMMON_BLOCK_PMCLIB_H */ diff --git a/src/soc/intel/common/block/pmc/pmclib.c b/src/soc/intel/common/block/pmc/pmclib.c index 09af749..32db779 100644 --- a/src/soc/intel/common/block/pmc/pmclib.c +++ b/src/soc/intel/common/block/pmc/pmclib.c @@ -17,6 +17,27 @@ #include <string.h> #include <timer.h>
+#define PMC_IPC_WBUF0 0x80 +#define PMC_IPC_RBUF0 0x90 +#define PMC_IPC_CMD 0x0 +#define PMC_IPC_STS 0x4 +#define PMC_IPC_USBC_CMD_ID 0xA7 +#define PMC_IPC_STS_BUSY (1 << 0) +#define PMC_IPC_STS_ERR (1 << 1) +#define PMC_IPC_CMD_SIZE 16 +#define PMC_IPC_CMD_SUBCMD 12 +#define PMC_IPC_XFER_TIMEOUT 1000 /* max 1s*/ + +/* Anything that's not success is <0. Provided solely for readability, as these +* constants are not used outside this file. +*/ +enum errors { + SUCCESS = 0, + E_TIMEOUT = -1, + E_HW_ERROR = -2, + E_ARGUMENT = -3, +}; + static struct chipset_power_state power_state;
/* List of Minimum Assertion durations in microseconds */ @@ -717,3 +738,62 @@ apm_control(APM_CNT_ACPI_DISABLE); } } + +static int check_ips_sts(uintptr_t pmcbase) +{ + struct stopwatch sw; + uint32_t ipcsts; + + stopwatch_init_msecs_expire(&sw, PMC_IPC_XFER_TIMEOUT); + do { + ipcsts = read32((void *)pmcbase + PMC_IPC_STS); + if (!(ipcsts & PMC_IPC_STS_BUSY)) + return SUCCESS; + } while (!(stopwatch_expired(&sw))); + + return E_TIMEOUT; +} + +int pmc_cl_send_ipc_cmd(uint32_t cmd, + uint32_t sub, + uint8_t *in, + uint32_t inlen, + uint32_t *out, + uint32_t outlen) +{ + uintptr_t pmcbase; + int i; + uint32_t wbuf[4] = { 0 }; + + if (inlen > 16) + return 0; + + /* Read PMC base address from soc. This is implemented in soc */ + pmcbase = soc_read_pmc_base(); + memcpy(wbuf, in, inlen); + + /* Program the Write Buffer with the data */ + for (i = 0; i < inlen; i++) { + write32((void *)pmcbase + PMC_IPC_WBUF0 + 4*i, + wbuf[i]); + } + + /* Program the command register with command and size */ + write32((void *)pmcbase + PMC_IPC_CMD, (inlen << PMC_IPC_CMD_SIZE) | + (sub << PMC_IPC_CMD_SUBCMD) | cmd); + + if (check_ips_sts(pmcbase)) { + printk(BIOS_ERR, "PMC IPC command failed\n"); + return -1; + } + + if (out) { + /* get the pmc response */ + for (i = 0; i < outlen; i++) { + out[i] = read32((u32 *)(pmcbase + PMC_IPC_RBUF0 + 4*i)); + printk(BIOS_INFO, "Read %x from PMC BUF: %x\n", out[i], + PMC_IPC_RBUF0 + 4*i); + } + } + return 0; +} diff --git a/src/soc/intel/common/block/systemagent/memmap.c b/src/soc/intel/common/block/systemagent/memmap.c index 86ca4e1..3447661 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 <intelblocks/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() */ diff --git a/src/soc/intel/tigerlake/Makefile.inc b/src/soc/intel/tigerlake/Makefile.inc index 4a41812..d87758f 100644 --- a/src/soc/intel/tigerlake/Makefile.inc +++ b/src/soc/intel/tigerlake/Makefile.inc @@ -46,6 +46,7 @@ ramstage-y += systemagent.c ramstage-y += me.c ramstage-y += xhci.c +ramstage-y += crashlog_lib.c
smm-y += gpio.c smm-y += p2sb.c diff --git a/src/soc/intel/tigerlake/acpi.c b/src/soc/intel/tigerlake/acpi.c index c7c5446..c4e11ba 100644 --- a/src/soc/intel/tigerlake/acpi.c +++ b/src/soc/intel/tigerlake/acpi.c @@ -13,6 +13,9 @@ #include <intelblocks/cpulib.h> #include <intelblocks/pmclib.h> #include <intelblocks/acpi.h> +#include <intelblocks/bert_storage.h> +#include <soc/crashlog_def.h> +#include <soc/crashlog_lib.h> #include <soc/cpu.h> #include <soc/iomap.h> #include <soc/nvs.h> @@ -368,3 +371,79 @@ { return acpigen_soc_gpio_op("\_SB.PCI0.CTXS", gpio_num); } + +void acpi_soc_fill_bert(acpi_bert_t *bert) +{ + acpi_generic_error_status_t *status = NULL; + size_t cpu_record_size, pmc_record_size; + void *cl_data = NULL; + + if (!crashlog_get_total_data_size()) { + printk(BIOS_ERR, "Error: No crashlog record present\n"); + return; + } + + status = bert_new_event(&CPER_SEC_FW_ERR_REC_REF_GUID); + if (!status) { + printk(BIOS_ERR, "Error: unable to allocate GSB\n"); + return; + } + + if (crashlog_get_total_data_size() > bert_storage_remaining()) { + printk(BIOS_ERR, "Error: Crashlog entry would exceed " + "available region\n"); + return; + } + + cpu_record_size = crashlog_get_cpu_record_size(); + if (cpu_record_size) { + cl_data = new_cper_fw_error_crashlog(status, cpu_record_size); + if (!cl_data) { + printk(BIOS_ERR, "Error: Crashlog CPU entry(size %lu) would exceed available region\n", + cpu_record_size); + return; + } + printk(BIOS_DEBUG, "cl_data %p, cpu_record_size %lu\n", + cl_data, cpu_record_size); + crashlog_fill_cpu_records(cl_data); + } + + pmc_record_size = crashlog_get_pmc_record_size(); + if (pmc_record_size) { + /* Allocate new FW ERR structure in case CPU crashlog is present */ + if (cpu_record_size && !bert_append_fw_err(status)) { + printk(BIOS_ERR, "Error: Crashlog PMC entry would " + "exceed available region\n"); + return; + } + + cl_data = new_cper_fw_error_crashlog(status, pmc_record_size); + if (!cl_data) { + printk(BIOS_ERR, "Error: Crashlog PMC entry(size %lu) would exceed available region\n", + pmc_record_size); + return; + } + printk(BIOS_DEBUG, "cl_data %p, pmc_record_size %lu\n", + cl_data, pmc_record_size); + crashlog_fill_pmc_records(cl_data); + } + + bert->region_length = status->data_length; + bert->error_region = (uintptr_t)status; + +} + +bool acpi_is_boot_error_src_present(void) +{ + if (!discover_crashlog()) { + printk(BIOS_DEBUG, "Crashlog discovery result: crashlog not found\n"); + return false; + } + + collect_pmc_and_cpu_crashlog_from_srams(); + + /* Size of valid collected data can differ size in dicovery tables */ + u32 crashlog_size = crashlog_get_total_data_size(); + + return (crashlog_size > 0) ? true : false; +} diff --git a/src/soc/intel/tigerlake/chipset.cb b/src/soc/intel/tigerlake/chipset.cb index d4bc76c..d007464 100644 --- a/src/soc/intel/tigerlake/chipset.cb +++ b/src/soc/intel/tigerlake/chipset.cb @@ -32,7 +32,7 @@ end device pci 08.0 alias gna off end device pci 09.0 alias npk off end - device pci 0a.0 alias crashlog off end + device pci 0a.0 alias crashlog on end device pci 0d.0 alias north_xhci off chip drivers/usb/acpi register "type" = "UPC_TYPE_HUB" diff --git a/src/soc/intel/tigerlake/crashlog_lib.c b/src/soc/intel/tigerlake/crashlog_lib.c new file mode 100644 index 0000000..5e8915e --- /dev/null +++ b/src/soc/intel/tigerlake/crashlog_lib.c @@ -0,0 +1,632 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* + * This file is part of the coreboot project. + * + * Copyright 2021 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include <console/console.h> +#include <cbmem.h> +#include <device/pci_ops.h> +#include <device/pci_def.h> +#include <device/mmio.h> +#include <delay.h> +#include <string.h> +#include <soc/crashlog_lib.h> +#include <intelblocks/pmclib.h> +#include <intelblocks/bert_storage.h> +#include <soc/crashlog_def.h> +#include <soc/iomap.h> +#include <soc/pci_devs.h> + +/* global crashLog info */ +static bool m_pmc_crashLog_support = false; +static bool m_pmc_crashLog_present = false; +static bool m_cpu_crashLog_support = false; +static bool m_cpu_crashLog_present = false; +static u32 m_pmc_crashLog_size = 0; +static u32 m_cpu_crashLog_size = 0; +static u32 cpu_crash_version = 0; +static bool clear_pmc_sram = true; + +static pmc_ipc_discovery_buf_t discovery_buf; +static pmc_crashlog_desc_table_t descriptor_table; +static tel_crashlog_devsc_cap_t cpu_cl_devsc_cap; +static cpu_crashlog_discovery_table_t cpu_cl_disc_tab; + + +int pmc_cl_get_descriptor_table(u32 desc_table_addr) +{ + int total_data_size = 0; + descriptor_table.numb_regions = read32((u32 *)desc_table_addr); + printk(BIOS_DEBUG, "CL PMC desc table: numb of regions is 0x%x at addr 0x%x\n", + descriptor_table.numb_regions, desc_table_addr); + for (int i = 0; i < descriptor_table.numb_regions; i++) { + desc_table_addr += 4; + descriptor_table.regions[i].data = read32((u32 *)(desc_table_addr)); + total_data_size += descriptor_table.regions[i].bits.size * sizeof(u32); + printk(BIOS_DEBUG, "CL PMC desc table: region 0x%x has size 0x%x at offset 0x%x\n", + i, descriptor_table.regions[i].bits.size, + descriptor_table.regions[i].bits.offset); + if (i > 255) { + printk(BIOS_ERR, "More than 255 regions in PMC crahlog descriptor tale"); + break; + } + } + return total_data_size; +} + + +bool pmc_cl_discovery(void) +{ + u8 wr_buf[4]; + u32 rd_buf[4]; + int pmc_rslt = 0; + u32 tmp_bar_addr = 0, desc_table_addr = 0; + + memset(&wr_buf, 0, sizeof(wr_buf)); + memset(&rd_buf, 0, sizeof(rd_buf)); + + pmc_rslt = pmc_cl_send_ipc_cmd(PMC_IPC_CMD_CRASHLOG, + PMC_IPC_CMD_ID_CRASHLOG_DISCOVERY, + (u8 *)&wr_buf, 4, (u32 *)rd_buf, 2); + + if (!pmc_rslt) { + discovery_buf.val_64_bits = ((u64)rd_buf[1] << 32) | rd_buf[0]; + } else { + printk(BIOS_ERR, "IPC command to PMC returned unexpected: 0x%x .\n", pmc_rslt); + return false; + } + + if (discovery_buf.bits.supported != 1) { + printk(BIOS_DEBUG, "PCH crashlog feature not supported .\n"); + m_pmc_crashLog_support = false; + return false; + } + m_pmc_crashLog_support = true; + + /* Program BAR 0 and enable command register memory space decoding */ + tmp_bar_addr = SPI_BASE_ADDRESS; + pci_write_config32(PCH_DEV_SRAM, PCI_BASE_ADDRESS_0, tmp_bar_addr); + pci_or_config16(PCH_DEV_SRAM, PCI_COMMAND, PCI_COMMAND_MEMORY); + + if (discovery_buf.bits.discov_mechanism == 1) { + /* discovery mode */ + if (discovery_buf.bits.base_offset & BIT_31_SET) { + printk(BIOS_DEBUG, "PCH discovery to be used is disabled .\n"); + m_pmc_crashLog_present = false; + m_pmc_crashLog_size = 0; + return false; + } + desc_table_addr = tmp_bar_addr + discovery_buf.bits.desc_tabl_offset; + m_pmc_crashLog_size = pmc_cl_get_descriptor_table(desc_table_addr); + printk(BIOS_DEBUG, "PMC crashLog size in discovery mode : 0x%X .\n", + m_pmc_crashLog_size); + } else { + /* legacy mode */ + if (discovery_buf.bits.dis) { + printk(BIOS_DEBUG, "PCH crashlog is disabled in legacy mode.\n"); + m_pmc_crashLog_present = false; + return false; + } + m_pmc_crashLog_size = (discovery_buf.bits.size != 0) ? + (discovery_buf.bits.size * sizeof(u32)) : 0xC00; + printk(BIOS_DEBUG, "PMC crashlog size in legacy mode = 0x%x .\n", + m_pmc_crashLog_size); + } + m_pmc_crashLog_present = true; + + return true; +} + + +int pmc_cl_disable(void) +{ + u8 wr_buf[4]; + memset(&wr_buf, 0, sizeof(wr_buf)); + return pmc_cl_send_ipc_cmd(PMC_IPC_CMD_CRASHLOG, PMC_IPC_CMD_ID_CRASHLOG_DISABLE, + (u8 *)&wr_buf, 4, NULL, 0); +} + +int pmc_cl_re_arm_after_reset(void) +{ + u8 wr_buf[4]; + memset(&wr_buf, 0, sizeof(wr_buf)); + printk(BIOS_DEBUG, "Crashlog: PMC re-arm initiated.\n"); + return pmc_cl_send_ipc_cmd(PMC_IPC_CMD_CRASHLOG, + PMC_IPC_CMD_ID_CRASHLOG_RE_ARM_ON_RESET, (u8 *)&wr_buf, + 4, NULL, 0); +} + + +/* Sends PMC IPC to clear CrashLog from PMC SSRAM area */ +int pmc_cl_clear(void) +{ + u8 wr_buf[4]; + memset(&wr_buf, 0, sizeof(wr_buf)); + printk(BIOS_DEBUG, "Crashlog PMC erase initiated.\n"); + return pmc_cl_send_ipc_cmd(PMC_IPC_CMD_CRASHLOG, PMC_IPC_CMD_ID_CRASHLOG_ERASE, + (u8 *)&wr_buf, 4, NULL, 0); +} + +/* Sends PMC IPC to populate CrashLog on all reboot. +The SSRAM area will be cleared on G3 by PMC automatically */ + +int pmc_cl_en_gen_on_all_reboot(void) +{ + u8 wr_buf[4]; + memset(&wr_buf, 0, sizeof(wr_buf)); + return pmc_cl_send_ipc_cmd(PMC_IPC_CMD_CRASHLOG, PMC_IPC_CMD_ID_CRASHLOG_ON_RESET, + (u8 *)&wr_buf, 4, NULL, 0); +} + +int cpu_cl_poll_mailbox_ready(u32 cl_mailbox_addr) +{ + cpu_crashlog_mailbox_t cl_mailbox_interface; + u16 stall_cnt = 0; + + do { + cl_mailbox_interface.data = read32((u32 *)cl_mailbox_addr); + udelay(CPU_CRASHLOG_MAILBOX_WAIT_STALL); + stall_cnt++; + } while ((cl_mailbox_interface.fields.busy == 1) + && stall_cnt < CPU_CRASHLOG_MAILBOX_WAIT_TIMEOUT); + + if ((cl_mailbox_interface.fields.busy == 1) + && (stall_cnt >= CPU_CRASHLOG_MAILBOX_WAIT_TIMEOUT)) { + printk(BIOS_ERR, "CPU crashlog mailbox timed out.\n"); + return 0; + } + + return 1; +} + +int cpu_cl_mailbox_cmd(u8 cmd, u8 param) +{ + cpu_crashlog_mailbox_t cl_mailbox_intf; + u32 cl_base_addr; + + memset(&cl_mailbox_intf, 0, sizeof(cpu_crashlog_mailbox_t)); + + cl_base_addr = cpu_cl_get_bar_addr(&cpu_cl_devsc_cap); + + cl_mailbox_intf.fields.command = cmd; + cl_mailbox_intf.fields.param = param; + cl_mailbox_intf.fields.busy = 1; + + write32((u32 *)(cl_base_addr + CRASHLOG_MAILBOX_INTF_ADDRESS), + cl_mailbox_intf.data); + + cpu_cl_poll_mailbox_ready(cl_base_addr + CRASHLOG_MAILBOX_INTF_ADDRESS); + + return 1; +} + +bool cpu_cl_get_capability(tel_crashlog_devsc_cap_t *cl_devsc_cap) +{ + + cl_devsc_cap->cap_data.data = pci_read_config32(SA_DEV_TMT, + TEL_DVSEC_OFFSET + TEL_DVSEC_PCIE_CAP_ID); + if (cl_devsc_cap->cap_data.fields.pcie_cap_id != TELEMETRY_EXTENDED_CAP_ID) { + printk(BIOS_DEBUG, "Read ID for Telemetry: 0x%x differs from expected: 0x%x .\n", + cl_devsc_cap->cap_data.fields.pcie_cap_id, TELEMETRY_EXTENDED_CAP_ID); + return false; + } + + /* walk through the entries until crashLog entry */ + cl_devsc_cap->devsc_data.data_32[1] = pci_read_config32(SA_DEV_TMT, TEL_DVSEV_ID); + int new_offset = 0; + while (cl_devsc_cap->devsc_data.fields.devsc_id != CRASHLOG_DVSEC_ID) { + if (cl_devsc_cap->cap_data.fields.next_cap_offset == 0 + || cl_devsc_cap->cap_data.fields.next_cap_offset == 0xFFFF) { + printk(BIOS_DEBUG, "Read invalid pcie_cap_id value: : 0x%x .\n", + cl_devsc_cap->cap_data.fields.pcie_cap_id); + return false; + } + new_offset = cl_devsc_cap->cap_data.fields.next_cap_offset; + cl_devsc_cap->cap_data.data = pci_read_config32(SA_DEV_TMT, + new_offset + TEL_DVSEC_PCIE_CAP_ID); + cl_devsc_cap->devsc_data.data_32[1] = pci_read_config32(SA_DEV_TMT, + new_offset + TEL_DVSEV_ID); + } + cpu_crash_version = cl_devsc_cap->devsc_data.fields.devsc_ver; + cl_devsc_cap->discovery_data.data = pci_read_config32(SA_DEV_TMT, new_offset + + TEL_DVSEV_DISCOVERY_TABLE_OFFSET); + + return true; +} + +u32 cpu_cl_get_bar_addr(tel_crashlog_devsc_cap_t *cl_devsc_cap) +{ + u32 base_addr = 0; + if (cpu_cl_devsc_cap.discovery_data.fields.t_bir_q == TEL_DVSEC_TBIR_BAR0) + base_addr = pci_read_config32(SA_DEV_TMT, TEL_CFG_BAR0) & 0xFFFFFFFFF0; + else if (cpu_cl_devsc_cap.discovery_data.fields.t_bir_q == TEL_DVSEC_TBIR_BAR1) + base_addr = pci_read_config32(SA_DEV_TMT, TEL_CFG_BAR1) & 0xFFFFFFFFF0; + else + printk(BIOS_ERR, "Invalid TEL_CFG_BAR value %d:\n", + cpu_cl_devsc_cap.discovery_data.fields.t_bir_q); + + + return base_addr; +} + +int cpu_cl_clear_data(void) +{ + return cpu_cl_mailbox_cmd(CPU_CRASHLOG_CMD_CLEAR, 0); +} + + +bool cpu_cl_get_discovery_table(void) +{ + u32 bar_addr = 0, disc_tab_addr = 0; + bar_addr = cpu_cl_get_bar_addr(&cpu_cl_devsc_cap); + disc_tab_addr = bar_addr + + cpu_cl_devsc_cap.discovery_data.fields.discovery_table_offset; + memset(&cpu_cl_disc_tab, 0, sizeof(cpu_crashlog_discovery_table_t)); + + cpu_cl_disc_tab.header.data = ((u64)read32((u32 *)disc_tab_addr) + + ((u64)read32((u32 *)(disc_tab_addr + 4)) << 32)); + + cpu_cl_disc_tab.cmd_mailbox.data = read32((u32 *)(disc_tab_addr + 8)); + cpu_cl_disc_tab.mailbox_data = read32((u32 *)(disc_tab_addr + 12)); + + printk(BIOS_DEBUG, "cpu_crashlog_discovery_table buffer count: 0x%x\n", + cpu_cl_disc_tab.header.fields.count); + + if (cpu_cl_disc_tab.header.fields.guid != CPU_CRASHLOG_DISC_TAB_GUID_VALID) { + printk(BIOS_ERR, "Invalid CPU crashlog discovery table GUID, expected = 0x%X ," + "actual = 0x%X\n", CPU_CRASHLOG_DISC_TAB_GUID_VALID, + cpu_cl_disc_tab.header.fields.guid); + return false; + } + + int cur_offset = 0; + for (int i = 0; i < cpu_cl_disc_tab.header.fields.count ; i++) { + cur_offset = 16 + 8*i; + cpu_cl_disc_tab.buffers[i].data = ((u64)read32((u32 *)(disc_tab_addr + + cur_offset)) + ((u64)read32((u32 *) + (disc_tab_addr + cur_offset + 4)) << 32)); + printk(BIOS_DEBUG, "cpu_crashlog_discovery_table buffer: 0x%x size:" + "0x%x offset: 0x%x\n", i, cpu_cl_disc_tab.buffers[i].fields.size, + cpu_cl_disc_tab.buffers[i].fields.offset); + m_cpu_crashLog_size += cpu_cl_disc_tab.buffers[i].fields.size * sizeof(u32); + } + + m_cpu_crashLog_present = m_cpu_crashLog_size ? true : false; + + return true; +} + +bool cpu_cl_discovery(void) +{ + memset(&cpu_cl_devsc_cap, 0, sizeof(tel_crashlog_devsc_cap_t)); + + if (!cpu_cl_get_capability(&cpu_cl_devsc_cap)) { + printk(BIOS_ERR, "CPU crashlog capability not found.\n"); + m_cpu_crashLog_support = false; + return false; + } + m_cpu_crashLog_support = true; + + /* Program BAR address and enable command register memory space decoding */ + u32 tmp_bar_addr = PCH_PWRM_BASE_ADDRESS; + printk(BIOS_DEBUG, "tmp_bar_addr: 0x%X .\n", tmp_bar_addr); + + if (cpu_cl_devsc_cap.discovery_data.fields.t_bir_q == TEL_DVSEC_TBIR_BAR0) { + pci_write_config32(SA_DEV_TMT, TEL_CFG_BAR0, tmp_bar_addr); + } else if (cpu_cl_devsc_cap.discovery_data.fields.t_bir_q == TEL_DVSEC_TBIR_BAR1) { + pci_write_config32(SA_DEV_TMT, TEL_CFG_BAR1, tmp_bar_addr); + } else { + printk(BIOS_DEBUG, "invalid discovery data t_bir_q: 0x%x\n", + cpu_cl_devsc_cap.discovery_data.fields.t_bir_q); + return false; + } + pci_or_config16(SA_DEV_TMT, PCI_COMMAND, PCI_COMMAND_MEMORY); + + if (!cpu_cl_get_discovery_table()) { + printk(BIOS_ERR, "CPU crashlog discovery table not valid.\n"); + m_cpu_crashLog_present = false; + return false; + } + m_cpu_crashLog_present = true; + + return true; +} + + +bool discover_crashlog(void) +{ + bool cpu_cl_discovered = false, pmc_cl_discovered = false; + + memset(&discovery_buf, 0, sizeof(pmc_ipc_discovery_buf_t)); + memset(&descriptor_table, 0, sizeof(pmc_crashlog_desc_table_t)); + memset(&cpu_cl_devsc_cap, 0, sizeof(tel_crashlog_devsc_cap_t)); + + /* PCH crashLog discovery */ + pmc_cl_discovered = pmc_cl_discovery(); + + /* CPU crashLog discovery */ + cpu_cl_discovered = cpu_cl_discovery(); + + return (cpu_cl_discovered || pmc_cl_discovered); +} + +int crashlog_get_total_data_size(void) +{ + return m_pmc_crashLog_size + m_cpu_crashLog_size; +} + + +bool cl_copy_data_from_sram(u32 src_bar, + u32 offset, + u32 size, + u32 *dest_addr, + u32 buffer_index, + bool pmc_sram) +{ + u32 src_addr = src_bar + offset; + if (src_addr == 0) { + printk(BIOS_ERR, "Invalid addr 0x%x and offset 0x%x for %s .\n", + src_bar, offset, __func__); + return false; + } + + u32 data = read32((u32 *)src_addr); + /*PMC: copy if 1st DWORD in buffer is not zero and its 31st bit is not set */ + if (pmc_sram && !(data && !(data & BIT_31_SET))) { + printk(BIOS_DEBUG, "Invalid data 0x%x at offset 0x%x from addr 0x%x" + " of PMC SRAM.\n", data, offset, src_bar); + return false; + } + /*CPU: don't copy if 1st DWORD in first buffer is zero */ + if (!pmc_sram && !data && (buffer_index == 0)) { + printk(BIOS_DEBUG, "Invalid data 0x%x at offset 0x%x from addr 0x%x" + " of telemetry SRAM.\n", data, offset, src_bar); + return false; + } + + u32 copied = 0; + while (copied < size) { + /* DW by DW copy: byte access to PMC SRAM not allowed */ + *dest_addr = read32((u32 *)src_addr); + dest_addr++; + src_addr += 4; + copied++; + } + return true; +} + +void pmc_cl_get_sram_data(void) +{ + u32 *dest = NULL; + u32 tmp_bar_addr = SPI_BASE_ADDRESS; + + /*PMC SSRAM MMIO access */ + if (pci_read_config16(PCH_DEV_SRAM, PCI_VENDOR_ID) == 0xFFFF) { + printk(BIOS_ERR, "PMC SSRAM PCI device is disabled.\n"); + return; + } + + if (discovery_buf.bits.supported != 1) { + printk(BIOS_DEBUG, "PCH crashlog feature not supported.\n"); + goto pmc_send_re_arm_after_reset; + } + + /* Get the size of data to copy */ + if (discovery_buf.bits.discov_mechanism == 1) { + if (discovery_buf.bits.base_offset & BIT_31_SET) { + printk(BIOS_DEBUG, "PCH discovery to be used is disabled.\n"); + goto pmc_send_re_arm_after_reset; + } + printk(BIOS_DEBUG, "PMC crashLog size in discovery mode : 0x%X\n", + m_pmc_crashLog_size); + } else { + if (discovery_buf.bits.dis) { + printk(BIOS_DEBUG, "PCH crashlog is disabled in legacy mode.\n"); + goto pmc_sram_exit; + } + m_pmc_crashLog_size = (discovery_buf.bits.size != 0) ? + discovery_buf.bits.size : 0xC00; + printk(BIOS_DEBUG, "PMC crashLog size in legacy mode : 0x%X\n", + m_pmc_crashLog_size); + } + + /* allocate mem for the record to be copied */ + unsigned long pmc_cl_cbmem_addr; + + pmc_cl_cbmem_addr = (unsigned long) cbmem_add(CBMEM_ID_PMC_CRASHLOG, + m_pmc_crashLog_size); + if (!pmc_cl_cbmem_addr) { + printk(BIOS_ERR, "Unable to allocate CBMEM PMC crashLog entry.\n"); + goto pmc_sram_exit; + } + memset((void *)pmc_cl_cbmem_addr, 0, m_pmc_crashLog_size); + dest = (u32 *)(uintptr_t) pmc_cl_cbmem_addr; + bool pmc_sram = true; + if (discovery_buf.bits.discov_mechanism == 1) { + for (int i = 0; i < descriptor_table.numb_regions; i++) { + if (cl_copy_data_from_sram(tmp_bar_addr, + descriptor_table.regions[i].bits.offset, + descriptor_table.regions[i].bits.size, + dest, + i, + pmc_sram)) { + dest = (u32 *)((u32)dest + + (descriptor_table.regions[i].bits.size + * sizeof(u32))); + } else { + m_pmc_crashLog_size -= descriptor_table.regions[i].bits.size * + sizeof(u32); + printk(BIOS_DEBUG, "discover mode PMC crashlog size adjusted" + " to: 0x%x\n", m_pmc_crashLog_size); + } + } + } else { + if (!cl_copy_data_from_sram(tmp_bar_addr, + discovery_buf.bits.base_offset, + discovery_buf.bits.size, + dest, + 0, + pmc_sram)) { + m_pmc_crashLog_size -= discovery_buf.bits.size * sizeof(u32); + printk(BIOS_DEBUG, "legacy mode PMC crashlog size adjusted to: 0x%x\n", + m_pmc_crashLog_size); + } + } + +pmc_send_re_arm_after_reset: + /* when bit 7 of discov cmd resp is set -> bit 2 of size field */ + if (discovery_buf.bits.size & BIT_2_SET) + pmc_cl_re_arm_after_reset(); + + /* Clear the SSRAM region after copying the error log */ + if (clear_pmc_sram) + pmc_cl_clear(); + + pmc_sram_exit: + /* disable PMC SSRAM MMIO */ + pci_update_config16(PCH_DEV_SRAM, PCI_COMMAND, PCI_COMMAND_MEMORY | PCI_COMMAND_IO, 0); + pci_write_config32(PCH_DEV_SRAM, PCI_BASE_ADDRESS_0, 0); + +} + +void cpu_cl_get_telemtry_sram_data(void) +{ + u32 tmp_bar_addr = 0; + u32 *dest = NULL; + + if (m_cpu_crashLog_size < 1) { + printk(BIOS_DEBUG, "%s: no data to collect .\n", __func__); + goto tel_sram_exit; + } + + printk(BIOS_DEBUG, "CPU crash data size: 0x%X bytes in 0x%X region(s) .\n", + m_cpu_crashLog_size, cpu_cl_disc_tab.header.fields.count); + + /* allocate memory buffers for CPU crashog data to be copied */ + unsigned long cpu_crashlog_cbmem_addr; + cpu_crashlog_cbmem_addr = (unsigned long) cbmem_add(CBMEM_ID_CPU_CRASHLOG, + m_cpu_crashLog_size); + if (!cpu_crashlog_cbmem_addr) { + printk(BIOS_ERR, "Failed to add CPU main crashLog entries to CBMEM.\n"); + goto tel_sram_exit; + } + + memset((void *) cpu_crashlog_cbmem_addr, 0, m_cpu_crashLog_size); + tmp_bar_addr = cpu_cl_get_bar_addr(&cpu_cl_devsc_cap); + dest = (u32 *)(uintptr_t) cpu_crashlog_cbmem_addr; + bool pmc_sram = false; + cpu_crashlog_buffer_info_t buff_info; + + for (int i = 0 ; i < cpu_cl_disc_tab.header.fields.count ; i++) { + buff_info = cpu_cl_disc_tab.buffers[i]; + + if (cl_copy_data_from_sram(tmp_bar_addr, + cpu_cl_disc_tab.buffers[i].fields.offset, + cpu_cl_disc_tab.buffers[i].fields.size, + dest, + i, + pmc_sram)) { + dest = (u32 *)((u32)dest + + (cpu_cl_disc_tab.buffers[i].fields.size * sizeof(u32))); + } else { + m_cpu_crashLog_size -= cpu_cl_disc_tab.buffers[i].fields.size + * sizeof(u32); + + /* for CPU skip all buffers if the 1st one is not valid */ + if (i == 0) { + m_cpu_crashLog_size = 0; + break; + } + } + } + + /* clear telemetry SRAM region */ + cpu_cl_clear_data(); + +tel_sram_exit: + /* Disable telemetry SRAM MMIO */ + pci_update_config16(SA_DEV_TMT, PCI_COMMAND, PCI_COMMAND_MEMORY | PCI_COMMAND_IO, 0); + if (cpu_cl_devsc_cap.discovery_data.fields.t_bir_q == TEL_DVSEC_TBIR_BAR0) { + pci_write_config32(SA_DEV_TMT, TEL_CFG_BAR0, 0); + } else if (cpu_cl_devsc_cap.discovery_data.fields.t_bir_q == TEL_DVSEC_TBIR_BAR1) { + pci_write_config32(SA_DEV_TMT, TEL_CFG_BAR1, 0); + } else { + printk(BIOS_DEBUG, "invalid discovery data t_bir_q: 0x%x .\n", + cpu_cl_devsc_cap.discovery_data.fields.t_bir_q); + } + +} + +void collect_pmc_and_cpu_crashlog_from_srams(void) +{ + if (m_pmc_crashLog_support && m_pmc_crashLog_present) { + pmc_cl_en_gen_on_all_reboot(); + printk(BIOS_DEBUG, "Crashlog collection enabled on every reboot.\n"); + pmc_cl_get_sram_data(); + } else { + printk(BIOS_DEBUG, "Skipping PMC crashLog collection. Data not present.\n"); + } + + printk(BIOS_DEBUG, "m_cpu_crashLog_size : 0x%X bytes\n", m_cpu_crashLog_size); + if (m_cpu_crashLog_support && m_cpu_crashLog_present && (m_cpu_crashLog_size > 0)) { + printk(BIOS_DEBUG, "CPU crashLog present.\n"); + cpu_cl_get_telemtry_sram_data(); + + } else { + printk(BIOS_DEBUG, "Skipping CPU crashLog collection. Data not present.\n"); + } +} + +int crashlog_get_cpu_record_size(void) +{ + return m_cpu_crashLog_size; +} + +int crashlog_get_pmc_record_size(void) +{ + return m_pmc_crashLog_size; +} + +bool crashlog_fill_cpu_records(void *cl_record) +{ + void *cl_src_addr = NULL; + + if (!m_cpu_crashLog_present || m_cpu_crashLog_size == 0) { + printk(BIOS_DEBUG, "CPU crashLog not present, skipping.\n"); + return false; + } + + printk(BIOS_DEBUG, "CPU crash data collection.\n"); + cl_src_addr = cbmem_find(CBMEM_ID_CPU_CRASHLOG); + memcpy(cl_record, cl_src_addr, m_cpu_crashLog_size); + + return true; +} + +bool crashlog_fill_pmc_records(void *cl_record) +{ + void *cl_src_addr = NULL; + + if (!m_pmc_crashLog_present || m_pmc_crashLog_size == 0) { + printk(BIOS_DEBUG, "PMC crashLog not present, skipping.\n"); + return false; + } + + printk(BIOS_DEBUG, "PMC crash data collection.\n"); + cl_src_addr = cbmem_find(CBMEM_ID_PMC_CRASHLOG); + memcpy(cl_record, cl_src_addr, m_pmc_crashLog_size); + + return true; +} diff --git a/src/soc/intel/tigerlake/include/soc/crashlog_def.h b/src/soc/intel/tigerlake/include/soc/crashlog_def.h new file mode 100644 index 0000000..0f586ef --- /dev/null +++ b/src/soc/intel/tigerlake/include/soc/crashlog_def.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* + * This file is part of the coreboot project. + * + * Copyright 2021 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hop that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +#ifndef _SOC_CRASHLOG_DEF_H_ +#define _SOC_CRASHLOG_DEF_H_ + +/* PMC CrashLog Command */ +#define PMC_IPC_CMD_CRASHLOG 0xA6 +#define PMC_IPC_CMD_ID_CRASHLOG_DISCOVERY 0x01 +#define PMC_IPC_CMD_ID_CRASHLOG_DISABLE 0x02 +#define PMC_IPC_CMD_ID_CRASHLOG_ERASE 0x04 +#define PMC_IPC_CMD_ID_CRASHLOG_ON_RESET 0x05 +#define PMC_IPC_CMD_ID_CRASHLOG_RE_ARM_ON_RESET 0x06 +#define BIT_2_SET 0x04 +#define BIT_31_SET 0x80000000 + +/* DVSEC capability Registers */ +#define TEL_DVSEC_OFFSET 0x100 +#define TEL_DVSEC_PCIE_CAP_ID 0x0 +#define TEL_DVSEC_NEXT_CAP 0x2 +#define TEL_DVSEV_ID 0x8 +#define TEL_DVSEV_DISCOVERY_TABLE_OFFSET 0xC +#define TELEMETRY_EXTENDED_CAP_ID 0x23 +#define CRASHLOG_DVSEC_ID 0x04 +#define TEL_DVSEC_TBIR_BAR0 0 +#define TEL_DVSEC_TBIR_BAR1 1 + +/* CPU CrashLog MMIO Registers */ +#define CRASHLOG_MAILBOX_INTF_ADDRESS 0x6038 +#define CRASHLOG_POINTER_SIZE_FIELD_OFFSET 0x04 + +/* CPU CrashLog Mailbox commands */ +#define CPU_CRASHLOG_CMD_DISABLE 0 +#define CPU_CRASHLOG_CMD_CLEAR 2 +#define CPU_CRASHLOG_MAILBOX_WAIT_STALL 1 +#define CPU_CRASHLOG_MAILBOX_WAIT_TIMEOUT 1000 +#define CPU_CRASHLOG_DISC_TAB_GUID_VALID 0x1600 + +#define CRASHLOG_SIZE_DEBUG_PURPOSE 0x640 + +#endif /* _SOC_CRASHLOG_DEF_H_ */ diff --git a/src/soc/intel/tigerlake/include/soc/crashlog_lib.h b/src/soc/intel/tigerlake/include/soc/crashlog_lib.h new file mode 100644 index 0000000..deeca83 --- /dev/null +++ b/src/soc/intel/tigerlake/include/soc/crashlog_lib.h @@ -0,0 +1,173 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* + * This file is part of the coreboot project. + * + * Copyright 2021 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _SOC_COMMON_BLOCK_CRASHLOG_LIB_H_ +#define _SOC_COMMON_BLOCK_CRASHLIB_LIB_H_ + +#include <arch/io.h> +#include <acpi/acpi.h> +#include <fsp/util.h> + +/* PMC crashlog discovery structs */ +typedef union { + struct { + u16 offset; + u16 size; + } bits; + u32 data; +} __packed pmc_crashlog_discov_region_t; + +typedef struct { + u32 numb_regions; + pmc_crashlog_discov_region_t regions[256]; +} __packed pmc_crashlog_desc_table_t; + +typedef union { + struct { + u32 supported :1; + u32 dis :1; + u32 discov_mechanism :2; + u32 size :12; + u32 base_offset :16; /* Start offset of CrashLog in PMC SSRAM */ + u32 rsv :16; + u32 desc_tabl_offset :16; /* start offset of descriptor table */ + } bits; + u64 val_64_bits; +} __packed pmc_ipc_discovery_buf_t; + + +/* CPU/TELEMETRY crashlog discovery structs */ + +typedef union { + struct { + u32 pcie_cap_id :16; + u32 cap_ver :4; + u32 next_cap_offset :12; + } fields; + u32 data; +} __packed cap_data_t; + +typedef union { + struct { + u64 devsc_ven_id :16; + u64 devsc_ver :4; + u64 devsc_len :12; + u64 devsc_id :16; + u64 num_entries :8; /*Numb of telemetry aggregators in lookup table. */ + u64 entries_size :8; /* Entry Size in DWORDS */ + } fields; + u64 data_64; + u32 data_32[2]; +} __packed devsc_data_t; + +typedef union { + struct { + u32 t_bir_q :3; /* tBIR, The BAR to be used */ + u32 discovery_table_offset :29; + } fields; + u32 data; +} __packed discovery_data_t; + +typedef struct { + cap_data_t cap_data; + devsc_data_t devsc_data; + discovery_data_t discovery_data; +} __packed tel_crashlog_devsc_cap_t; + +typedef union { + struct { + u64 access_type :4; + u64 crash_type :4; + u64 count :8; + u64 reserved :16; + u64 guid :32; + } fields; + u64 data; +} __packed cpu_crashlog_header_t; + + +/* Structures for CPU CrashLog mailbox interface */ +typedef union { + struct { + u32 command :8; + u32 param :8; + u32 reserved :15; + u32 busy :1; + } fields; + u32 data; +} __packed cpu_crashlog_mailbox_t; + +typedef union { + struct { + u32 offset :32; + u32 size :16; + u32 reserved :16; + } fields; + u64 data; +} __packed cpu_crashlog_buffer_info_t; + +typedef struct { + cpu_crashlog_header_t header; + cpu_crashlog_mailbox_t cmd_mailbox; + u32 mailbox_data; + cpu_crashlog_buffer_info_t buffers[256]; +} __packed cpu_crashlog_discovery_table_t; + + +static const EFI_GUID FW_ERR_SECTION_GUID = { + 0x81212a96, 0x09ed, 0x4996, + { 0x94, 0x71, 0x8d, 0x72, 0x9c, 0x8e, 0x69, 0xed } +}; + +int pmc_cl_get_descriptor_table(u32 desc_table_addr); +bool pmc_cl_discovery(void); +int pmc_cl_disable(void); +int pmc_cl_clear(void); +int pmc_cl_re_arm_after_reset(void); +int pmc_cl_en_gen_on_all_reboot(void); +void pmc_cl_get_sram_data(void); + +bool discover_crashlog(void); +int crashlog_get_total_data_size(void); +bool cl_copy_data_from_sram(u32 src_bar, + u32 offset, + u32 size, + u32 *dest_addr, + u32 buffer_index, + bool pmc_sram); + +int cpu_cl_poll_mailbox_ready(u32 cl_mailbox_addr); +int cpu_cl_mailbox_cmd(u8 cmd, u8 param); +bool cpu_cl_get_capability(tel_crashlog_devsc_cap_t *cl_devsc_cap); +u32 cpu_cl_get_bar_addr(tel_crashlog_devsc_cap_t *cl_devsc_cap); +int cpu_cl_clear_data(void); +void cpu_cl_get_header(u32 base_addr, + cpu_crashlog_header_t *cl_header_buff); +bool cpu_cl_disable(void); +bool cpu_cl_get_discovery_table(void); +bool cpu_cl_discovery(void); + +void cpu_cl_get_telemtry_sram_data(void); +void collect_pmc_and_cpu_crashlog_from_srams(void); + +int crashlog_get_cpu_record_size(void); +int crashlog_get_pmc_record_size(void); +bool crashlog_fill_cpu_records(void *cl_record); +bool crashlog_fill_pmc_records(void *cl_record); + +#endif /* _SOC_COMMON_BLOCK_CRASHLOG_LIB_H_ */ diff --git a/src/soc/intel/tigerlake/include/soc/iomap.h b/src/soc/intel/tigerlake/include/soc/iomap.h index 6fa29d3..47d2551 100644 --- a/src/soc/intel/tigerlake/include/soc/iomap.h +++ b/src/soc/intel/tigerlake/include/soc/iomap.h @@ -102,4 +102,8 @@ #define P2SB_BAR CONFIG_PCR_BASE_ADDRESS #define P2SB_SIZE (16 * MiB)
+/* PCI Configuration Space Registers for telemetry */ +#define TEL_CFG_BAR0 0x10 +#define TEL_CFG_BAR1 0x14 + #endif diff --git a/src/soc/intel/tigerlake/include/soc/pci_devs.h b/src/soc/intel/tigerlake/include/soc/pci_devs.h index ac0498f..4737fc7 100644 --- a/src/soc/intel/tigerlake/include/soc/pci_devs.h +++ b/src/soc/intel/tigerlake/include/soc/pci_devs.h @@ -5,21 +5,24 @@
#include <device/pci_def.h>
+#define _SA_DEVFN(slot) PCI_DEVFN(SA_DEV_SLOT_ ## slot, 0) #define _PCH_DEVFN(slot, func) PCI_DEVFN(PCH_DEV_SLOT_ ## slot, func)
#if !defined(__SIMPLE_DEVICE__) #include <device/device.h> +#define _SA_DEV(slot) pcidev_path_on_root(_SA_DEVFN(slot)) #define _PCH_DEV(slot, func) pcidev_path_on_root_debug(_PCH_DEVFN(slot, func), __func__) #else +#define _SA_DEV(slot) PCI_DEV(0, SA_DEV_SLOT_ ## slot, 0) #define _PCH_DEV(slot, func) PCI_DEV(0, PCH_DEV_SLOT_ ## slot, func) #endif
/* System Agent Devices */
#define SA_DEV_SLOT_ROOT 0x00 -#define SA_DEVFN_ROOT PCI_DEVFN(SA_DEV_SLOT_ROOT, 0) +#define SA_DEVFN_ROOT PCI_DEVFN(SA_DEV_SLOT_ROOT, 0) #if defined(__SIMPLE_DEVICE__) -#define SA_DEV_ROOT PCI_DEV(0, SA_DEV_SLOT_ROOT, 0) +#define SA_DEV_ROOT PCI_DEV(0, SA_DEV_SLOT_ROOT, 0) #endif
#define SA_DEV_SLOT_IGD 0x02 @@ -49,6 +52,10 @@ #define SA_DEV_TBT2 PCI_DEV(0, SA_DEV_SLOT_TBT, 2) #define SA_DEV_TBT3 PCI_DEV(0, SA_DEV_SLOT_TBT, 3)
+#define SA_DEV_SLOT_TMT 0x0A +#define SA_DEVFN_TMT _SA_DEVFN(TMT) +#define SA_DEV_TMT _SA_DEV(TMT) + #define SA_DEV_SLOT_TCSS 0x0d #define NUM_TCSS_DMA_FUNCTIONS 2 #define SA_DEVFN_TCSS_DMA(x) PCI_DEVFN(SA_DEV_SLOT_TCSS, ((x) + 2)) diff --git a/src/soc/intel/tigerlake/romstage/fsp_params.c b/src/soc/intel/tigerlake/romstage/fsp_params.c index dc9caee..1709367 100644 --- a/src/soc/intel/tigerlake/romstage/fsp_params.c +++ b/src/soc/intel/tigerlake/romstage/fsp_params.c @@ -214,6 +214,10 @@
/* Change TmeEnable UPD value according to INTEL_TME Kconfig */ m_cfg->TmeEnable = CONFIG(INTEL_TME); + + /* crashLog config */ + m_cfg->CpuCrashLogDevice = 1; + m_cfg->CpuCrashLogEnable = 1; }
void platform_fsp_memory_init_params_cb(FSPM_UPD *mupd, uint32_t version) diff --git a/src/vendorcode/intel/fsp/fsp2_0/tigerlake/FspmUpd.h b/src/vendorcode/intel/fsp/fsp2_0/tigerlake/FspmUpd.h index 35cc43b..1b3c859 100644 --- a/src/vendorcode/intel/fsp/fsp2_0/tigerlake/FspmUpd.h +++ b/src/vendorcode/intel/fsp/fsp2_0/tigerlake/FspmUpd.h @@ -72,9 +72,11 @@ **/ UINT8 EnableAbove4GBMmio;
-/** Offset 0x004B - Reserved +/** Offset 0x004B - Enable/Disable CrashLog Device 10 + Enable(Default): Enable CPU CrashLog Device 10, Disable: Disable CPU CrashLog + $EN_DIS **/ - UINT8 Reserved0; + UINT8 CpuCrashLogDevice;
/** Offset 0x004C - MemorySpdPtr00 **/