From patchwork Thu Oct 23 09:42:54 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 637 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1761212631; bh=QIxu9zplHX1egjuLyF+nw8AITqLny90OGPc904iYxVk=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=k6sbbhoCdEgBZhYVGCPGnA7JskVMBPyuZi2cEkrVS2NeyQdAXt/MwvUj0jBs0WHcw yxiWA3vpBM2Xqk+WhJLA4tzp+Tkww3zji6HXkTOBJsYVhnpQPQKldZZFbNdsU5rwni Ly2OgZcXlKsvXZ6S5HH47Ak3JSns6hoRTw3lZS3aozWdZd37ZwmbeEkfM0VM5L+N9j 0puL4EnPl6+9N21beeW2KLOKIKSICVoJgDch8GclBNAZVwUnI4yDqAUqBkh8heloYP 0iJTtkI/TrQOde/I2wLShToI0zGLdj2USVvZd/aWqGIFE4FghFh4hgMziLTBbuFdJ/ U8Hk83id1TEwQ== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id EA2C4682B1 for ; Thu, 23 Oct 2025 03:43:51 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id afOUpdT9LJQC for ; Thu, 23 Oct 2025 03:43:51 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1761212630; bh=QIxu9zplHX1egjuLyF+nw8AITqLny90OGPc904iYxVk=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=RWNFiOUx//1I1yEWQvG7Pm2of/r91kop3Rzm2HbJ/WARlkhYLG1H2YEzPLtfcJH85 5fJp1dyiQB5/8tEdvvCqYfyZLQFKkzoYKP6lgLvwhf/EGSFycGL30tMSbbQZZVWik8 jU5/rufSZpfwlrf0NBnYuD/brDFxbS4NpgxPto5bpvgk6J8d2casmL3e42BNRo1yUy 3p7ITadi1zalGvApkQnbOPU1Qei4kdJWKqEaNPxYTpjL8ez+19/x71E+vNb9kboPyL ui1R02z8HuFfs3s6PGBQ3eM95BZOFvRg+4VHJ5StDTM2wz/RhNRK4G89H9qfDCfzNZ harppmGGLi4ag== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id DF4AA682B2 for ; Thu, 23 Oct 2025 03:43:50 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1761212626; bh=RWMljvImtCQBuMrLwLHOTS8/4extInyZQgKCesZBZC4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=HSqlR0j9/i/Ek6pGo9loB+fl9eq0Zlwly5bFdz++ZAZ3Gvcl3wDrgNk0G/dal/S5K eMXabnb8scrozzUBBw3eK1mM393b/c/5D2JWXOV8lELq1KEJsqnMGbdRwuG2NAxsDN SKCVTxLmAck99/GqdLyEjUKi/wlONXAWhKo3q/Q1RgUx95s/ptoLCOG0r1om9z3Ehz 3ACp69boQeRM5ga3a19LGafCgGmRuSUiTR+MqPpalVPmRsMzUwF5WVsMLmVrsrVDEw F4SZD8d5BvSbF5naB70174Ulk67vq2WA/KuZa+MMNb6N04/865v0sGgrkC6g7H2tvx gbsHfZxeVtCMQ== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id CFF3F68195; Thu, 23 Oct 2025 03:43:46 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id 9eN7-WJgVTrY; Thu, 23 Oct 2025 03:43:46 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1761212624; bh=DXydtS9ZE0KGYbKszHuU57d9u3kMse18/K0DvQq4tN4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=IsvPT7oi0x0WBfV28x6ZvY+xiecD5qHYjn9TWg4zsmlo3q/PEUtJwomYiKZlijutG OYje7H++y4wfgGYWPLJqX/dDGKdkfLvgXz/U6tvFKVIXyjwYAXukBbGwjN3FkmqAfd FkiytAGpWHzLlNuozNior3nr7SuXkY5i5ue8D3zelIpZNvQWlgdpM5IFGaTASqUtI8 8rhAvWascHqkdfw1GUrcscVd3Rejj6NYLyKe1BmrcRVWHR+GVE6xcvPgPxSeDnIF3C /8KZ50fhWOU/JPA+F+wFUkmzaivoDCvItbeiXozQN6/YBgnUo8Phomrf7LVGiEGkLt XnmCWOAiQiGcg== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 3D98A6822E; Thu, 23 Oct 2025 03:43:44 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Thu, 23 Oct 2025 03:42:54 -0600 Message-ID: <20251023094308.3406453-6-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20251023094308.3406453-1-sjg@u-boot.org> References: <20251023094308.3406453-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: PLAJ5MPEXTHOYOAWM6OHF7P5442RIBW4 X-Message-ID-Hash: PLAJ5MPEXTHOYOAWM6OHF7P5442RIBW4 X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Heinrich Schuchardt , Simon Glass , Claude X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 05/11] acpi: Add support for Firmware Performance Data Table List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass Add ACPI FPDT support to report firmware boot timing information to the OS. The FPDT table contains timing data for firmware phases from reset through OS handoff. Add some functions to enable generation of this table. Co-developed-by: Claude Signed-off-by: Simon Glass --- include/acpi/acpi_table.h | 76 +++++++++++++++++++++++++++++++++++++++ lib/acpi/acpi_table.c | 75 ++++++++++++++++++++++++++++++++++++++ test/dm/acpi.c | 73 +++++++++++++++++++++++++++++++++++++ 3 files changed, 224 insertions(+) diff --git a/include/acpi/acpi_table.h b/include/acpi/acpi_table.h index 850cb8db816..8459fa60c25 100644 --- a/include/acpi/acpi_table.h +++ b/include/acpi/acpi_table.h @@ -760,6 +760,49 @@ struct __packed acpi_bgrt { u32 offset_y; }; +/** + * struct acpi_fpdt - Firmware Performance Data Table (FPDT) header + * + * See ACPI Spec v6.5 section 5.2.24 for details + */ +struct acpi_fpdt { + struct acpi_table_header header; +}; + +/* FPDT Performance Record Types */ +#define FPDT_REC_BOOT 0 + +/* FPDT Performance Record Header */ +struct acpi_fpdt_hdr { + u16 type; + u8 length; + u8 revision; +} __packed; + +/** + * struct acpi_fpdt_boot - Firmware Basic Boot Performance Record + * + * This record describes the boot performance from power-on to OS handoff. + * All timing values are in microseconds since system reset. + * + * @hdr: Record header + * @reserved: Reserved, must be zero + * @reset_end: Timer value at start of firmware (microseconds) + * @loader_start: Start of OS loader load (microseconds) + * @loader_exec: Start of OS loader execution (microseconds) + * @ebs_entry: Entry to ExitBootServices (microseconds) + * @ebs_exit: Exit from ExitBootServices (microseconds) + */ +struct acpi_fpdt_boot { + struct acpi_fpdt_hdr hdr; + u32 reserved; + u64 reset_end; + u64 loader_start; + u64 loader_exec; + u64 ebs_entry; + u64 ebs_exit; +} __packed; + /* Types for PPTT */ #define ACPI_PPTT_TYPE_PROC 0 #define ACPI_PPTT_TYPE_CACHE 1 @@ -952,6 +995,7 @@ enum acpi_tables { ACPITAB_ECDT, ACPITAB_FACS, ACPITAB_FADT, + ACPITAB_FPDT, ACPITAB_GTDT, ACPITAB_HEST, ACPITAB_HPET, @@ -1307,6 +1351,38 @@ void *acpi_get_end(void); */ int acpi_write_bgrt(struct acpi_ctx *ctx); +/** + * acpi_write_fpdt() - Write a Firmware Performance Data Table (FPDT) + * + * This creates an FPDT table with firmware boot timing information + * + * @ctx: ACPI context + * @uboot_start: U-Boot start time in microseconds + * Return: 0 if OK, -ve on error + */ +int acpi_write_fpdt(struct acpi_ctx *ctx, u64 uboot_start); + +/** + * acpi_get_fpdt_boot() - Get pointer to FPDT boot performance record + * + * This allows the caller to update the boot performance timing fields + * after the FPDT table has been created. After updating, call + * acpi_fix_fpdt_checksum() to recalculate the table checksum. + * + * Return: pointer to boot performance record, or NULL if not found + */ +struct acpi_fpdt_boot *acpi_get_fpdt_boot(void); + +/** + * acpi_fix_fpdt_checksum() - Recalculate FPDT table checksum + * + * Call this after updating the boot performance record to fix the + * table checksum. + * + * Return: 0 if OK, -ENOENT if FPDT table not found + */ +int acpi_fix_fpdt_checksum(void); + #endif /* !__ACPI__*/ #include diff --git a/lib/acpi/acpi_table.c b/lib/acpi/acpi_table.c index f2dadd792bd..31d8d314314 100644 --- a/lib/acpi/acpi_table.c +++ b/lib/acpi/acpi_table.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -106,6 +107,8 @@ int acpi_get_table_revision(enum acpi_tables table) return 1; case ACPITAB_GTDT: /* ACPI 6.2: 2, ACPI 6.3: 3 */ return 2; + case ACPITAB_FPDT: /* ACPI 6.4+: 1 */ + return 1; default: return -EINVAL; } @@ -710,3 +713,75 @@ 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; +} diff --git a/test/dm/acpi.c b/test/dm/acpi.c index a274671de02..1409c333eb1 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -961,3 +962,75 @@ static int dm_test_acpi_bgrt(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_bgrt, UTF_SCAN_FDT); + +/* Test ACPI FPDT (Firmware Performance Data Table) generation */ +static int dm_test_acpi_fpdt(struct unit_test_state *uts) +{ + struct acpi_fpdt_boot *rec; + struct acpi_fpdt *fpdt; + struct acpi_ctx ctx; + ulong addr, time; + void *buf; + + addr = 0; + buf = map_sysmem(addr, BUF_SIZE); + + /* Set up context with base tables (RSDP, RSDT, XSDT) */ + ut_assertok(setup_ctx_and_base_tables(uts, &ctx, addr)); + + /* Save where the FPDT will be written */ + fpdt = ctx.current; + + /* Write the FPDT table with test U-Boot start time */ + time = timer_get_boot_us(); + ut_assertok(acpi_write_fpdt(&ctx, 1234)); + + /* Verify the FPDT was written at the saved location */ + ut_asserteq_mem("FPDT", fpdt->header.signature, ACPI_NAME_LEN); + ut_asserteq(1, fpdt->header.revision); + ut_asserteq(sizeof(struct acpi_fpdt) + sizeof(struct acpi_fpdt_boot), + fpdt->header.length); + + /* Verify the boot performance record */ + rec = (struct acpi_fpdt_boot *)(fpdt + 1); + ut_asserteq(FPDT_REC_BOOT, rec->hdr.type); + ut_asserteq(sizeof(struct acpi_fpdt_boot), rec->hdr.length); + ut_asserteq(2, rec->hdr.revision); + + ut_asserteq(1234, rec->reset_end); + ut_assert(rec->loader_start != 0); + ut_assert(rec->loader_exec != 0); + ut_assert(rec->ebs_entry != 0); + ut_assert(rec->ebs_exit != 0); + + /* Verify checksum is valid */ + ut_asserteq(0, table_compute_checksum(fpdt, fpdt->header.length)); + + /* Get pointer to boot record and verify it matches */ + rec = acpi_get_fpdt_boot(); + ut_assertnonnull(rec); + ut_asserteq_ptr(rec, (struct acpi_fpdt_boot *)(fpdt + 1)); + ut_asserteq(1234, rec->reset_end); + + /* Update a timing field */ + rec->ebs_entry = 123; + rec->ebs_exit = 456; + + /* Checksum should now be invalid */ + ut_assert(table_compute_checksum(fpdt, fpdt->header.length) != 0); + + /* Fix the checksum */ + ut_assertok(acpi_fix_fpdt_checksum()); + + /* Checksum should now be valid again */ + ut_asserteq(0, table_compute_checksum(fpdt, fpdt->header.length)); + + /* Verify the updated values are still there */ + ut_asserteq(123, rec->ebs_entry); + ut_asserteq(456, rec->ebs_exit); + + unmap_sysmem(buf); + + return 0; +} +DM_TEST(dm_test_acpi_fpdt, 0);