[Concept,1/7] acpi: qfw: Add FPDT support for QEMU builds
Commit Message
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(-)
@@ -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;
}
@@ -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();
+ }
+}
@@ -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