drivers/intel/fsp2_0: Add provision to extract FSP Performance Data

This patch enriches coreboot FSP2.0 driver to extract the FSP timestamp
from FPDT (Firmware Performance Data Table) and display right after
FSP-S exits (from `fsp_silicon_init()` function), based on SoC user
selects the required `DISPLAY_FSP_TIMESTAMPS` config.

The prerequisite to this implementation is to have FSP binary built with
`PcdFspPerformanceEnable` PCD set to `TRUE` to allow FSP to populate
the FPDT HOB.

BUG=b:216635831
TEST=Able to dump FSP performance data with DISPLAY_FSP_TIMESTAMPS
Kconfig selected and met the FSP prerequisites.
+--------------------------------------------------+
|------ FSP Performance Timestamp Table Dump ------|
+--------------------------------------------------+
| Perf-ID	Timestamp(ms)	       String/GUID |
+--------------------------------------------------+
    0	          460253    SEC/52c05b14-0b98-496c-bc3b04b50211d680
   50	          460263    PEI/52c05b14-0b98-496c-bc3b04b50211d680
   40	          460274    PreMem/52c05b14-0b98-496c-bc3b04b50211d680
    1	          495803    9b3ada4f-ae56-4c24-8deaf03b7558ae50
    2	          508959    9b3ada4f-ae56-4c24-8deaf03b7558ae50
    1	          515253    6141e486-7543-4f1a-a579ff532ed78e75
    2	          525453    6141e486-7543-4f1a-a579ff532ed78e75
    1	          532059    baeb5bee-5b33-480a-8ab7b29c85e7ceab
    2	          546806    baeb5bee-5b33-480a-8ab7b29c85e7ceab
    1	          553302    1b04374d-fa9c-420f-ac62fee6d45e8443
    2	          563859    1b04374d-fa9c-420f-ac62fee6d45e8443
    1	          569955    88c17e54-ebfe-4531-a992581029f58126
    2	          575753    88c17e54-ebfe-4531-a992581029f58126
    1	          582099    a8499e65-a6f6-48b0-96db45c266030d83
 50f0	          599599    unknown name/3112356f-cc77-4e82-86d53e25ee8192a4
 50f1	          716649    unknown name/3112356f-cc77-4e82-86d53e25ee8192a4
    2	          728507    a8499e65-a6f6-48b0-96db45c266030d83
    1	          734755    9e1cc850-6731-4848-87526673c7005eee
....

Signed-off-by: Subrata Banik <subratabanik@google.com>
Change-Id: Ia1b7f6b98bafeec0afe843f0f78c99c2f34f50b3
Reviewed-on: https://review.coreboot.org/c/coreboot/+/62942
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Kangheui Won <khwon@chromium.org>
Reviewed-by: Eric Lai <eric_lai@quanta.corp-partner.google.com>
Reviewed-by: Michael Niewöhner <foss@mniewoehner.de>
diff --git a/src/drivers/intel/fsp2_0/Kconfig b/src/drivers/intel/fsp2_0/Kconfig
index 0823aa3..1eccd61 100644
--- a/src/drivers/intel/fsp2_0/Kconfig
+++ b/src/drivers/intel/fsp2_0/Kconfig
@@ -359,4 +359,14 @@
 	  This option allows to create `Debug Event Handler` to print FSP debug messages
 	  to output device using coreboot native implementation.
 
+config DISPLAY_FSP_TIMESTAMPS
+	bool "Display FSP Timestamps"
+	default n
+	help
+	  Select this config to retrieve FSP timestamps from Firmware Performance Data Table
+	  (FPDT) and display from ramstage after FSP-S is executed.
+
+	  To be able to use this, FSP has to be compiled with `PcdFspPerformanceEnable` set to
+	  `TRUE`.
+
 endif
diff --git a/src/drivers/intel/fsp2_0/Makefile.inc b/src/drivers/intel/fsp2_0/Makefile.inc
index 076ab2d..f5641ac 100644
--- a/src/drivers/intel/fsp2_0/Makefile.inc
+++ b/src/drivers/intel/fsp2_0/Makefile.inc
@@ -19,6 +19,7 @@
 ramstage-y += debug.c
 ramstage-$(CONFIG_FSP_USES_CB_DEBUG_EVENT_HANDLER) += fsp_debug_event.c
 ramstage-$(CONFIG_USE_INTEL_FSP_MP_INIT) += fsp_mpinit.c
+ramstage-$(CONFIG_DISPLAY_FSP_TIMESTAMPS) += fsp_timestamp.c
 ramstage-$(CONFIG_RUN_FSP_GOP) += graphics.c
 ramstage-y += hand_off_block.c
 ramstage-$(CONFIG_DISPLAY_FSP_HEADER) += header_display.c
diff --git a/src/drivers/intel/fsp2_0/fsp_timestamp.c b/src/drivers/intel/fsp2_0/fsp_timestamp.c
new file mode 100644
index 0000000..da14db9
--- /dev/null
+++ b/src/drivers/intel/fsp2_0/fsp_timestamp.c
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <commonlib/bsd/compiler.h>
+#include <console/console.h>
+#include <fsp/util.h>
+#include <lib.h>
+
+#define TIMESTAMP_MS(x) ((x) / 1000ull)
+
+static const uint8_t fpdt_guid[16] = {
+	0xfd, 0x7b, 0x38, 0x3b, 0xbc, 0x7a, 0xf2, 0x4c,
+	0xa0, 0xca, 0xb6, 0xa1, 0x6c, 0x1b, 0x1b, 0x25,
+};
+
+enum fpdt_record_type {
+	FPDT_GUID_EVENT		= 0x1010,
+	FPDT_STRING_EVENT	= 0x1011,
+};
+
+struct perf_record_hdr {
+	uint16_t	type;
+	uint8_t		length;
+	uint8_t		revision;
+} __packed;
+
+struct generic_event_record {
+	struct perf_record_hdr	header;
+	uint16_t		progress_id;
+	uint32_t		apic_id;
+	uint64_t		timestamp;
+	uint8_t			guid[16];
+	uint8_t			string[0];
+} __packed;
+
+/*
+ * Performance Hob:
+ *   GUID - fpdt_guid;
+ *   Data - FPDT_PEI_EXT_PERF_HEADER one or more FPDT records
+*/
+struct fpdt_pei_ext_perf_header {
+	uint32_t	table_size;
+	uint32_t	load_image_count;
+	uint32_t	hob_is_full;
+} __packed;
+
+static void print_guid_record(const struct generic_event_record *rec)
+{
+	printk(BIOS_INFO, "%5x\t%16llu\t\t", rec->progress_id, TIMESTAMP_MS(rec->timestamp));
+	fsp_print_guid(rec->guid);
+	printk(BIOS_INFO, "\n");
+}
+
+static void print_string_record(const struct generic_event_record *rec)
+{
+	size_t str_len = rec->header.length - offsetof(struct generic_event_record, string);
+	printk(BIOS_INFO, "%5x\t%16llu\t\t%*s/",
+	       rec->progress_id, TIMESTAMP_MS(rec->timestamp), (int)str_len, rec->string);
+	fsp_print_guid(rec->guid);
+	printk(BIOS_INFO, "\n");
+}
+
+static void print_fsp_perf_timestamp(const struct generic_event_record *rec)
+{
+	switch (rec->header.type) {
+	case FPDT_GUID_EVENT:
+		print_guid_record(rec);
+		break;
+	case FPDT_STRING_EVENT:
+		print_string_record(rec);
+		break;
+	default:
+		printk(BIOS_INFO, "Unhandled Event Type 0x%x\n", rec->header.type);
+		break;
+	}
+}
+
+static void print_fsp_timestamp_header(void)
+{
+	printk(BIOS_INFO, "+---------------------------------------------------+\n");
+	printk(BIOS_INFO, "|------ FSP Performance Timestamp Table Dump -------|\n");
+	printk(BIOS_INFO, "+---------------------------------------------------+\n");
+	printk(BIOS_INFO, "| Perf-ID\tTimestamp(ms)\t\tString/GUID |\n");
+	printk(BIOS_INFO, "+---------------------------------------------------+\n");
+}
+
+void fsp_display_timestamp(void)
+{
+	size_t size;
+	const struct fpdt_pei_ext_perf_header *hdr = fsp_find_extension_hob_by_guid(fpdt_guid,
+							 &size);
+
+	if (!hdr || !size) {
+		printk(BIOS_INFO, "FPDT Extended Firmware Performance HOB Not Found!\n"
+		"Check if PcdFspPerformanceEnable is set to `TRUE` inside FSP package\n");
+		return;
+	}
+
+	const struct generic_event_record *rec = (const struct generic_event_record *)(
+				(uint8_t *)hdr + sizeof(struct fpdt_pei_ext_perf_header));
+
+	print_fsp_timestamp_header();
+	for (size_t i = 0; i < hdr->table_size;) {
+		print_fsp_perf_timestamp(rec);
+
+		i += rec->header.length;
+		rec = (const struct generic_event_record *)((uint8_t *)rec +
+				 rec->header.length);
+	}
+}
diff --git a/src/drivers/intel/fsp2_0/include/fsp/util.h b/src/drivers/intel/fsp2_0/include/fsp/util.h
index 2687d0d..96dc472 100644
--- a/src/drivers/intel/fsp2_0/include/fsp/util.h
+++ b/src/drivers/intel/fsp2_0/include/fsp/util.h
@@ -106,6 +106,8 @@
 extern const uint8_t fsp_nv_storage_guid[16];
 extern const uint8_t fsp_reserved_memory_guid[16];
 
+/* Function to extract the FSP timestamp from FPDT Hob and display */
+void fsp_display_timestamp(void);
 const void *fsp_get_hob_list(void);
 void *fsp_get_hob_list_ptr(void);
 const void *fsp_find_extension_hob_by_guid(const uint8_t *guid, size_t *size);
diff --git a/src/drivers/intel/fsp2_0/silicon_init.c b/src/drivers/intel/fsp2_0/silicon_init.c
index 05cea11..263ea3b 100644
--- a/src/drivers/intel/fsp2_0/silicon_init.c
+++ b/src/drivers/intel/fsp2_0/silicon_init.c
@@ -244,6 +244,9 @@
 	timestamp_add_now(TS_FSP_SILICON_INIT_LOAD);
 	fsps_load();
 	do_silicon_init(&fsps_hdr);
+
+	if (CONFIG(DISPLAY_FSP_TIMESTAMPS))
+		fsp_display_timestamp();
 }
 
 __weak void soc_load_logo(FSPS_UPD *supd) { }