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>
182 lines
4.1 KiB
C
182 lines
4.1 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Generation of tables for particular device types
|
|
*
|
|
* Copyright 2025 Simon Glass <sjg@chromium.org>
|
|
*/
|
|
|
|
#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>
|
|
|
|
int acpi_write_bgrt(struct acpi_ctx *ctx)
|
|
{
|
|
struct udevice *dev;
|
|
struct acpi_bgrt *bgrt;
|
|
efi_status_t eret;
|
|
void *logo, *buf;
|
|
bool have_video;
|
|
int size;
|
|
|
|
/* If video is available, use the screen size to centre the logo */
|
|
have_video = !uclass_first_device_err(UCLASS_VIDEO, &dev);
|
|
|
|
if (!IS_ENABLED(CONFIG_VIDEO))
|
|
return -ENOENT;
|
|
|
|
logo = video_image_get(bgrt, &size);
|
|
|
|
/* If there's no logo data, there's nothing to report */
|
|
if (!logo)
|
|
return -ENOENT;
|
|
|
|
bgrt = ctx->current;
|
|
ctx->tab_start = ctx->current;
|
|
memset(bgrt, '\0', sizeof(struct acpi_table_iort));
|
|
|
|
acpi_fill_header(&bgrt->header, "BGRT");
|
|
bgrt->version = 1;
|
|
|
|
/* Status: Bit 0 (Displayed) = 1, Bits 1-2 (Orientation) = 0 */
|
|
bgrt->status = 1;
|
|
|
|
/* Image Type: 0 = Bitmap */
|
|
bgrt->image_type = 0;
|
|
|
|
/* Mark space used for tables */
|
|
eret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, size, &buf);
|
|
if (eret)
|
|
return -ENOMEM;
|
|
memcpy(buf, logo, size);
|
|
|
|
/* The physical address of the in-memory logo bitmap */
|
|
bgrt->addr = nomap_to_sysmem(buf);
|
|
|
|
/* Calculate offsets to center the logo on the screen */
|
|
bgrt->offset_x = 0;
|
|
bgrt->offset_y = 0;
|
|
|
|
/*
|
|
* centering is disabled for now, since it seems to be done by the
|
|
* startup code
|
|
*/
|
|
if (0 && IS_ENABLED(CONFIG_VIDEO) && have_video) {
|
|
struct video_priv *priv = dev_get_uclass_priv(dev);
|
|
ulong width, height;
|
|
uint bpix;
|
|
|
|
video_bmp_get_info(logo, &width, &height, &bpix);
|
|
|
|
if (priv->xsize > width)
|
|
bgrt->offset_x = (priv->xsize - width) / 2;
|
|
if (priv->ysize > height)
|
|
bgrt->offset_y = (priv->ysize - height) / 2;
|
|
}
|
|
acpi_inc(ctx, sizeof(*bgrt));
|
|
|
|
/* Calculate length and checksum */
|
|
bgrt->header.length = (ulong)ctx->current - (ulong)bgrt;
|
|
acpi_update_checksum(&bgrt->header);
|
|
log_debug("BGRT at %p length %x logo copied to bs-data at %p\n", bgrt,
|
|
bgrt->header.length, buf);
|
|
acpi_add_table(ctx, bgrt);
|
|
|
|
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();
|
|
}
|
|
}
|