[Concept,1/7] acpi: qfw: Add FPDT support for QEMU builds

Message ID 20251029061657.1456910-2-sjg@u-boot.org
State New
Headers
Series efi: Minor improvements to QEMU and build scripts |

Commit Message

Simon Glass Oct. 29, 2025, 6:16 a.m. UTC
  From: Simon Glass <sjg@chromium.org>

QEMU creates ACPI tables but doesn't include FPDT (Firmware Performance
Data Table). Add FPDT generation in qfw_acpi.c following the same
pattern as BGRT.

Move the acpi_write_fpdt() function from acpi_table.c to acpi_extra.c so
that is available even when CONFIG_ACPIGEN is disabled.

This allows QEMU x86_64 builds to provide firmware boot timing
information to the operating system.

Disable this for qemu-riscv64_smode_acpi as it is near the code-size
limit.

Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <sjg@chromium.org>
---

 drivers/qfw/qfw_acpi.c | 10 +++++
 lib/acpi/acpi_extra.c  | 92 ++++++++++++++++++++++++++++++++++++++++++
 lib/acpi/acpi_table.c  | 90 -----------------------------------------
 3 files changed, 102 insertions(+), 90 deletions(-)
  

Patch

diff --git a/drivers/qfw/qfw_acpi.c b/drivers/qfw/qfw_acpi.c
index 916a59be7fb..7f7dba73155 100644
--- a/drivers/qfw/qfw_acpi.c
+++ b/drivers/qfw/qfw_acpi.c
@@ -8,6 +8,7 @@ 
 
 #include <abuf.h>
 #include <bloblist.h>
+#include <bootstage.h>
 #include <errno.h>
 #include <malloc.h>
 #include <mapmem.h>
@@ -256,6 +257,15 @@  out:
 		return addr;
 	}
 
+	if (!IS_ENABLED(CONFIG_TARGET_QEMU_VIRT)) {
+		ret = acpi_write_fpdt(ctx,
+				bootstage_get_time(BOOTSTAGE_ID_START_UBOOT_F));
+		if (ret) {
+			printf("error: failed to write FPDT (err=%dE)\n", ret);
+			return addr;
+		}
+	}
+
 	return addr;
 }
 
diff --git a/lib/acpi/acpi_extra.c b/lib/acpi/acpi_extra.c
index 709f64305b5..09b8a251ca2 100644
--- a/lib/acpi/acpi_extra.c
+++ b/lib/acpi/acpi_extra.c
@@ -7,9 +7,11 @@ 
 
 #define LOG_CATEGORY	LOGC_ACPI
 
+#include <bootstage.h>
 #include <dm.h>
 #include <efi_loader.h>
 #include <mapmem.h>
+#include <tables_csum.h>
 #include <video.h>
 #include <acpi/acpi_table.h>
 
@@ -87,3 +89,93 @@  int acpi_write_bgrt(struct acpi_ctx *ctx)
 
 	return 0;
 }
+
+int acpi_write_fpdt(struct acpi_ctx *ctx, u64 uboot_start)
+{
+	struct acpi_fpdt *fpdt;
+	struct acpi_fpdt_boot *rec;
+	struct acpi_table_header *header;
+	u64 current_time;
+	int size;
+
+	fpdt = ctx->current;
+	header = &fpdt->header;
+
+	/* Calculate total size: FPDT header + boot performance record */
+	size = sizeof(struct acpi_fpdt) + sizeof(struct acpi_fpdt_boot);
+
+	memset(fpdt, '\0', size);
+
+	/* Fill out FPDT header */
+	acpi_fill_header(header, "FPDT");
+	header->length = size;
+	header->revision = 1;  /* ACPI 6.4+: 1 */
+
+	/* Add boot performance record right after FPDT header */
+	rec = (struct acpi_fpdt_boot *)(fpdt + 1);
+
+	/* Fill in record header */
+	rec->hdr.type = FPDT_REC_BOOT;
+	rec->hdr.length = sizeof(struct acpi_fpdt_boot);
+	rec->hdr.revision = 2;  /* FPDT Boot Performance Record revision */
+
+	/* Fill in timing data */
+	current_time = timer_get_boot_us();
+	rec->reset_end = uboot_start;
+	rec->loader_start = current_time;
+	rec->loader_exec = current_time;
+	rec->ebs_entry = current_time;
+	rec->ebs_exit = current_time;
+
+	header->checksum = table_compute_checksum(fpdt, header->length);
+
+	acpi_inc_align(ctx, size);
+	acpi_add_table(ctx, fpdt);
+
+	return 0;
+}
+
+struct acpi_fpdt_boot *acpi_get_fpdt_boot(void)
+{
+	struct acpi_table_header *header;
+	struct acpi_fpdt *fpdt;
+
+	header = acpi_find_table("FPDT");
+	if (!header)
+		return NULL;
+
+	fpdt = (struct acpi_fpdt *)header;
+	return (struct acpi_fpdt_boot *)(fpdt + 1);
+}
+
+int acpi_fix_fpdt_checksum(void)
+{
+	struct acpi_table_header *header;
+
+	header = acpi_find_table("FPDT");
+	if (!header)
+		return -ENOENT;
+
+	header->checksum = 0;
+	header->checksum = table_compute_checksum(header, header->length);
+
+	return 0;
+}
+
+void acpi_final_fpdt(void)
+{
+	struct acpi_fpdt_boot *fpdt;
+
+	if (IS_ENABLED(CONFIG_TARGET_QEMU_VIRT))
+		return;
+
+	fpdt = acpi_get_fpdt_boot();
+	if (fpdt) {
+		u64 time;
+
+		time = timer_get_boot_us();
+		fpdt->ebs_entry = time;
+		fpdt->ebs_exit = time;
+		acpi_fix_fpdt_checksum();
+	}
+}
diff --git a/lib/acpi/acpi_table.c b/lib/acpi/acpi_table.c
index 99251176d09..94e6647b666 100644
--- a/lib/acpi/acpi_table.c
+++ b/lib/acpi/acpi_table.c
@@ -715,96 +715,6 @@  static int acpi_create_bgrt(struct acpi_ctx *ctx,
 ACPI_WRITER(6bgrt, "BGRT", acpi_create_bgrt, 0);
 #endif
 
-int acpi_write_fpdt(struct acpi_ctx *ctx, u64 uboot_start)
-{
-	struct acpi_fpdt *fpdt;
-	struct acpi_fpdt_boot *rec;
-	struct acpi_table_header *header;
-	u64 current_time;
-	int size;
-
-	fpdt = ctx->current;
-	header = &fpdt->header;
-
-	/* Calculate total size: FPDT header + boot performance record */
-	size = sizeof(struct acpi_fpdt) + sizeof(struct acpi_fpdt_boot);
-
-	memset(fpdt, '\0', size);
-
-	/* Fill out FPDT header */
-	acpi_fill_header(header, "FPDT");
-	header->length = size;
-	header->revision = acpi_get_table_revision(ACPITAB_FPDT);
-
-	/* Add boot performance record right after FPDT header */
-	rec = (struct acpi_fpdt_boot *)(fpdt + 1);
-
-	/* Fill in record header */
-	rec->hdr.type = FPDT_REC_BOOT;
-	rec->hdr.length = sizeof(struct acpi_fpdt_boot);
-	rec->hdr.revision = 2;  /* FPDT Boot Performance Record revision */
-
-	/* Fill in timing data */
-	current_time = timer_get_boot_us();
-	rec->reset_end = uboot_start;
-	rec->loader_start = current_time;
-	rec->loader_exec = current_time;
-	rec->ebs_entry = current_time;
-	rec->ebs_exit = current_time;
-
-	header->checksum = table_compute_checksum(fpdt, header->length);
-
-	acpi_inc_align(ctx, size);
-	acpi_add_table(ctx, fpdt);
-
-	return 0;
-}
-
-struct acpi_fpdt_boot *acpi_get_fpdt_boot(void)
-{
-	struct acpi_table_header *header;
-	struct acpi_fpdt *fpdt;
-
-	header = acpi_find_table("FPDT");
-	if (!header)
-		return NULL;
-
-	fpdt = (struct acpi_fpdt *)header;
-	return (struct acpi_fpdt_boot *)(fpdt + 1);
-}
-
-int acpi_fix_fpdt_checksum(void)
-{
-	struct acpi_table_header *header;
-
-	header = acpi_find_table("FPDT");
-	if (!header)
-		return -ENOENT;
-
-	header->checksum = 0;
-	header->checksum = table_compute_checksum(header, header->length);
-
-	return 0;
-}
-
-void acpi_final_fpdt(void)
-{
-	struct acpi_fpdt_boot *fpdt;
-
-	if (IS_ENABLED(CONFIG_TARGET_QEMU_VIRT))
-		return;
-
-	fpdt = acpi_get_fpdt_boot();
-	if (fpdt) {
-		u64 time;
-
-		time = timer_get_boot_us();
-		fpdt->ebs_entry = time;
-		fpdt->ebs_exit = time;
-		acpi_fix_fpdt_checksum();
-	}
-}
-
 /* this board lacks the bootstage timer */
 #ifndef CONFIG_TARGET_QEMU_VIRT