acpi: qfw: Add FPDT support for QEMU builds
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>
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user