qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Xiaoyao Li <xiaoyao.li@intel.com>
To: "Paolo Bonzini" <pbonzini@redhat.com>,
	"Daniel P. Berrangé" <berrange@redhat.com>
Cc: "Michael S. Tsirkin" <mst@redhat.com>,
	"Markus Armbruster" <armbru@redhat.com>,
	"Francesco Lavra" <francescolavra.fl@gmail.com>,
	"Marcelo Tosatti" <mtosatti@redhat.com>,
	kvm@vger.kernel.org, qemu-devel@nongnu.org,
	"Philippe Mathieu-Daudé" <philmd@linaro.org>,
	"Zhao Liu" <zhao1.liu@intel.com>,
	"Rick Edgecombe" <rick.p.edgecombe@intel.com>,
	"Xiaoyao Li" <xiaoyao.li@intel.com>
Subject: [PATCH v9 17/55] i386/tdvf: Introduce function to parse TDVF metadata
Date: Thu,  8 May 2025 10:59:23 -0400	[thread overview]
Message-ID: <20250508150002.689633-18-xiaoyao.li@intel.com> (raw)
In-Reply-To: <20250508150002.689633-1-xiaoyao.li@intel.com>

From: Isaku Yamahata <isaku.yamahata@intel.com>

TDX VM needs to boot with its specialized firmware, Trusted Domain
Virtual Firmware (TDVF). QEMU needs to parse TDVF and map it in TD
guest memory prior to running the TDX VM.

A TDVF Metadata in TDVF image describes the structure of firmware.
QEMU refers to it to setup memory for TDVF. Introduce function
tdvf_parse_metadata() to parse the metadata from TDVF image and store
the info of each TDVF section.

TDX metadata is located by a TDX metadata offset block, which is a
GUID-ed structure. The data portion of the GUID structure contains
only an 4-byte field that is the offset of TDX metadata to the end
of firmware file.

Select X86_FW_OVMF when TDX is enable to leverage existing functions
to parse and search OVMF's GUID-ed structures.

Signed-off-by: Isaku Yamahata <isaku.yamahata@intel.com>
Co-developed-by: Xiaoyao Li <xiaoyao.li@intel.com>
Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
---
Changes in v9:
 - Add comment for TdvfSectionEntry and TdvfMetadata;

Changes in v8:
 - Drop the failure handling of memcpy() since it cannot fail;

Changes in v7:
 - Update license info to only use SPDX tag;
 - use g_autofree to avoid manually free;

Changes in v6:
 - Drop the the data endianness change for metadata->Length;

Changes in v1:
 - rename tdvf_parse_section_entry() to
   tdvf_parse_and_check_section_entry()

Changes in RFC v4:
 - rename TDX_METADATA_GUID to TDX_METADATA_OFFSET_GUID
---
 hw/i386/Kconfig        |   1 +
 hw/i386/meson.build    |   1 +
 hw/i386/tdvf.c         | 187 +++++++++++++++++++++++++++++++++++++++++
 include/hw/i386/tdvf.h |  38 +++++++++
 4 files changed, 227 insertions(+)
 create mode 100644 hw/i386/tdvf.c
 create mode 100644 include/hw/i386/tdvf.h

diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig
index cce9521ba934..eb65bda6e071 100644
--- a/hw/i386/Kconfig
+++ b/hw/i386/Kconfig
@@ -12,6 +12,7 @@ config SGX
 
 config TDX
     bool
+    select X86_FW_OVMF
     depends on KVM
 
 config PC
diff --git a/hw/i386/meson.build b/hw/i386/meson.build
index 10bdfde27c69..3bc1da2b6eb4 100644
--- a/hw/i386/meson.build
+++ b/hw/i386/meson.build
@@ -32,6 +32,7 @@ i386_ss.add(when: 'CONFIG_PC', if_true: files(
   'port92.c'))
 i386_ss.add(when: 'CONFIG_X86_FW_OVMF', if_true: files('pc_sysfw_ovmf.c'),
                                         if_false: files('pc_sysfw_ovmf-stubs.c'))
+i386_ss.add(when: 'CONFIG_TDX', if_true: files('tdvf.c'))
 
 subdir('kvm')
 subdir('xen')
diff --git a/hw/i386/tdvf.c b/hw/i386/tdvf.c
new file mode 100644
index 000000000000..6b1c6aede1e7
--- /dev/null
+++ b/hw/i386/tdvf.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2025 Intel Corporation
+ * Author: Isaku Yamahata <isaku.yamahata at gmail.com>
+ *                        <isaku.yamahata at intel.com>
+ *         Xiaoyao Li <xiaoyao.li@intel.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+
+#include "hw/i386/pc.h"
+#include "hw/i386/tdvf.h"
+#include "system/kvm.h"
+
+#define TDX_METADATA_OFFSET_GUID    "e47a6535-984a-4798-865e-4685a7bf8ec2"
+#define TDX_METADATA_VERSION        1
+#define TDVF_SIGNATURE              0x46564454 /* TDVF as little endian */
+
+/*
+ * the raw structs read from TDVF keeps the name convention in
+ * TDVF Design Guide spec.
+ */
+typedef struct {
+    uint32_t DataOffset;
+    uint32_t RawDataSize;
+    uint64_t MemoryAddress;
+    uint64_t MemoryDataSize;
+    uint32_t Type;
+    uint32_t Attributes;
+} TdvfSectionEntry;
+
+typedef struct {
+    uint32_t Signature;
+    uint32_t Length;
+    uint32_t Version;
+    uint32_t NumberOfSectionEntries;
+    TdvfSectionEntry SectionEntries[];
+} TdvfMetadata;
+
+struct tdx_metadata_offset {
+    uint32_t offset;
+};
+
+static TdvfMetadata *tdvf_get_metadata(void *flash_ptr, int size)
+{
+    TdvfMetadata *metadata;
+    uint32_t offset = 0;
+    uint8_t *data;
+
+    if ((uint32_t) size != size) {
+        return NULL;
+    }
+
+    if (pc_system_ovmf_table_find(TDX_METADATA_OFFSET_GUID, &data, NULL)) {
+        offset = size - le32_to_cpu(((struct tdx_metadata_offset *)data)->offset);
+
+        if (offset + sizeof(*metadata) > size) {
+            return NULL;
+        }
+    } else {
+        error_report("Cannot find TDX_METADATA_OFFSET_GUID");
+        return NULL;
+    }
+
+    metadata = flash_ptr + offset;
+
+    /* Finally, verify the signature to determine if this is a TDVF image. */
+    metadata->Signature = le32_to_cpu(metadata->Signature);
+    if (metadata->Signature != TDVF_SIGNATURE) {
+        error_report("Invalid TDVF signature in metadata!");
+        return NULL;
+    }
+
+    /* Sanity check that the TDVF doesn't overlap its own metadata. */
+    metadata->Length = le32_to_cpu(metadata->Length);
+    if (offset + metadata->Length > size) {
+        return NULL;
+    }
+
+    /* Only version 1 is supported/defined. */
+    metadata->Version = le32_to_cpu(metadata->Version);
+    if (metadata->Version != TDX_METADATA_VERSION) {
+        return NULL;
+    }
+
+    return metadata;
+}
+
+static int tdvf_parse_and_check_section_entry(const TdvfSectionEntry *src,
+                                              TdxFirmwareEntry *entry)
+{
+    entry->data_offset = le32_to_cpu(src->DataOffset);
+    entry->data_len = le32_to_cpu(src->RawDataSize);
+    entry->address = le64_to_cpu(src->MemoryAddress);
+    entry->size = le64_to_cpu(src->MemoryDataSize);
+    entry->type = le32_to_cpu(src->Type);
+    entry->attributes = le32_to_cpu(src->Attributes);
+
+    /* sanity check */
+    if (entry->size < entry->data_len) {
+        error_report("Broken metadata RawDataSize 0x%x MemoryDataSize 0x%lx",
+                     entry->data_len, entry->size);
+        return -1;
+    }
+    if (!QEMU_IS_ALIGNED(entry->address, TARGET_PAGE_SIZE)) {
+        error_report("MemoryAddress 0x%lx not page aligned", entry->address);
+        return -1;
+    }
+    if (!QEMU_IS_ALIGNED(entry->size, TARGET_PAGE_SIZE)) {
+        error_report("MemoryDataSize 0x%lx not page aligned", entry->size);
+        return -1;
+    }
+
+    switch (entry->type) {
+    case TDVF_SECTION_TYPE_BFV:
+    case TDVF_SECTION_TYPE_CFV:
+        /* The sections that must be copied from firmware image to TD memory */
+        if (entry->data_len == 0) {
+            error_report("%d section with RawDataSize == 0", entry->type);
+            return -1;
+        }
+        break;
+    case TDVF_SECTION_TYPE_TD_HOB:
+    case TDVF_SECTION_TYPE_TEMP_MEM:
+        /* The sections that no need to be copied from firmware image */
+        if (entry->data_len != 0) {
+            error_report("%d section with RawDataSize 0x%x != 0",
+                         entry->type, entry->data_len);
+            return -1;
+        }
+        break;
+    default:
+        error_report("TDVF contains unsupported section type %d", entry->type);
+        return -1;
+    }
+
+    return 0;
+}
+
+int tdvf_parse_metadata(TdxFirmware *fw, void *flash_ptr, int size)
+{
+    g_autofree TdvfSectionEntry *sections = NULL;
+    TdvfMetadata *metadata;
+    ssize_t entries_size;
+    int i;
+
+    metadata = tdvf_get_metadata(flash_ptr, size);
+    if (!metadata) {
+        return -EINVAL;
+    }
+
+    /* load and parse metadata entries */
+    fw->nr_entries = le32_to_cpu(metadata->NumberOfSectionEntries);
+    if (fw->nr_entries < 2) {
+        error_report("Invalid number of fw entries (%u) in TDVF Metadata",
+                     fw->nr_entries);
+        return -EINVAL;
+    }
+
+    entries_size = fw->nr_entries * sizeof(TdvfSectionEntry);
+    if (metadata->Length != sizeof(*metadata) + entries_size) {
+        error_report("TDVF metadata len (0x%x) mismatch, expected (0x%x)",
+                     metadata->Length,
+                     (uint32_t)(sizeof(*metadata) + entries_size));
+        return -EINVAL;
+    }
+
+    fw->entries = g_new(TdxFirmwareEntry, fw->nr_entries);
+    sections = g_new(TdvfSectionEntry, fw->nr_entries);
+
+    memcpy(sections, (void *)metadata + sizeof(*metadata), entries_size);
+
+    for (i = 0; i < fw->nr_entries; i++) {
+        if (tdvf_parse_and_check_section_entry(&sections[i], &fw->entries[i])) {
+            goto err;
+        }
+    }
+
+    return 0;
+
+err:
+    fw->entries = 0;
+    g_free(fw->entries);
+    return -EINVAL;
+}
diff --git a/include/hw/i386/tdvf.h b/include/hw/i386/tdvf.h
new file mode 100644
index 000000000000..7ebcac42a36c
--- /dev/null
+++ b/include/hw/i386/tdvf.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2025 Intel Corporation
+ * Author: Isaku Yamahata <isaku.yamahata at gmail.com>
+ *                        <isaku.yamahata at intel.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_I386_TDVF_H
+#define HW_I386_TDVF_H
+
+#include "qemu/osdep.h"
+
+#define TDVF_SECTION_TYPE_BFV               0
+#define TDVF_SECTION_TYPE_CFV               1
+#define TDVF_SECTION_TYPE_TD_HOB            2
+#define TDVF_SECTION_TYPE_TEMP_MEM          3
+
+#define TDVF_SECTION_ATTRIBUTES_MR_EXTEND   (1U << 0)
+#define TDVF_SECTION_ATTRIBUTES_PAGE_AUG    (1U << 1)
+
+typedef struct TdxFirmwareEntry {
+    uint32_t data_offset;
+    uint32_t data_len;
+    uint64_t address;
+    uint64_t size;
+    uint32_t type;
+    uint32_t attributes;
+} TdxFirmwareEntry;
+
+typedef struct TdxFirmware {
+    uint32_t nr_entries;
+    TdxFirmwareEntry *entries;
+} TdxFirmware;
+
+int tdvf_parse_metadata(TdxFirmware *fw, void *flash_ptr, int size);
+
+#endif /* HW_I386_TDVF_H */
-- 
2.43.0



  parent reply	other threads:[~2025-05-08 15:09 UTC|newest]

Thread overview: 63+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-05-08 14:59 [PATCH v9 00/55] QEMU TDX support Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 01/55] *** HACK *** linux-headers: Update headers to pull in TDX API changes Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 02/55] i386: Introduce tdx-guest object Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 03/55] i386/tdx: Implement tdx_kvm_type() for TDX Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 04/55] i386/tdx: Implement tdx_kvm_init() to initialize TDX VM context Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 05/55] i386/tdx: Get tdx_capabilities via KVM_TDX_CAPABILITIES Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 06/55] i386/tdx: Introduce is_tdx_vm() helper and cache tdx_guest object Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 07/55] kvm: Introduce kvm_arch_pre_create_vcpu() Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 08/55] i386/tdx: Initialize TDX before creating TD vcpus Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 09/55] i386/tdx: Add property sept-ve-disable for tdx-guest object Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 10/55] i386/tdx: Make sept_ve_disable set by default Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 11/55] i386/tdx: Wire CPU features up with attributes of TD guest Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 12/55] i386/tdx: Validate TD attributes Xiaoyao Li
2025-05-08 15:50   ` Daniel P. Berrangé
2025-05-08 14:59 ` [PATCH v9 13/55] i386/tdx: Support user configurable mrconfigid/mrowner/mrownerconfig Xiaoyao Li
2025-05-08 15:55   ` Daniel P. Berrangé
2025-05-09  6:21     ` Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 14/55] i386/tdx: Set APIC bus rate to match with what TDX module enforces Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 15/55] i386/tdx: Implement user specified tsc frequency Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 16/55] i386/tdx: load TDVF for TD guest Xiaoyao Li
2025-05-08 14:59 ` Xiaoyao Li [this message]
2025-05-08 14:59 ` [PATCH v9 18/55] i386/tdx: Parse TDVF metadata for TDX VM Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 19/55] i386/tdx: Don't initialize pc.rom for TDX VMs Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 20/55] i386/tdx: Track mem_ptr for each firmware entry of TDVF Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 21/55] i386/tdx: Track RAM entries for TDX VM Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 22/55] headers: Add definitions from UEFI spec for volumes, resources, etc Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 23/55] i386/tdx: Setup the TD HOB list Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 24/55] i386/tdx: Add TDVF memory via KVM_TDX_INIT_MEM_REGION Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 25/55] i386/tdx: Call KVM_TDX_INIT_VCPU to initialize TDX vcpu Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 26/55] i386/tdx: Finalize TDX VM Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 27/55] i386/tdx: Enable user exit on KVM_HC_MAP_GPA_RANGE Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 28/55] i386/tdx: Handle KVM_SYSTEM_EVENT_TDX_FATAL Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 29/55] i386/tdx: Wire TDX_REPORT_FATAL_ERROR with GuestPanic facility Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 30/55] kvm: Check KVM_CAP_MAX_VCPUS at vm level Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 31/55] i386/cpu: introduce x86_confidential_guest_cpu_instance_init() Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 32/55] i386/tdx: implement tdx_cpu_instance_init() Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 33/55] i386/cpu: Introduce enable_cpuid_0x1f to force exposing CPUID 0x1f Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 34/55] i386/tdx: Force " Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 35/55] i386/tdx: Set kvm_readonly_mem_enabled to false for TDX VM Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 36/55] i386/tdx: Disable SMM for TDX VMs Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 37/55] i386/tdx: Disable PIC " Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 38/55] i386/tdx: Set and check kernel_irqchip mode for TDX Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 39/55] i386/tdx: Don't synchronize guest tsc for TDs Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 40/55] i386/tdx: Only configure MSR_IA32_UCODE_REV in kvm_init_msrs() " Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 41/55] i386/apic: Skip kvm_apic_put() for TDX Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 42/55] cpu: Don't set vcpu_dirty when guest_state_protected Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 43/55] i386/cgs: Rename *mask_cpuid_features() to *adjust_cpuid_features() Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 44/55] i386/tdx: Implement adjust_cpuid_features() for TDX Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 45/55] i386/tdx: Add TDX fixed1 bits to supported CPUIDs Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 46/55] i386/tdx: Add supported CPUID bits related to TD Attributes Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 47/55] i386/tdx: Add supported CPUID bits relates to XFAM Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 48/55] i386/tdx: Add XFD to supported bit of TDX Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 49/55] i386/tdx: Define supported KVM features for TDX Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 50/55] i386/cgs: Introduce x86_confidential_guest_check_features() Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 51/55] i386/tdx: Fetch and validate CPUID of TD guest Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 52/55] i386/tdx: Don't treat SYSCALL as unavailable Xiaoyao Li
2025-05-08 14:59 ` [PATCH v9 53/55] i386/tdx: Make invtsc default on Xiaoyao Li
2025-05-08 15:00 ` [PATCH v9 54/55] i386/tdx: Validate phys_bits against host value Xiaoyao Li
2025-05-08 15:00 ` [PATCH v9 55/55] docs: Add TDX documentation Xiaoyao Li
2025-05-26 16:12 ` [PATCH v9 00/55] QEMU TDX support Paolo Bonzini
2025-05-27  1:30   ` Xiaoyao Li
2025-05-27  4:27     ` Markus Armbruster
2025-05-27  7:46       ` Xiaoyao Li

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250508150002.689633-18-xiaoyao.li@intel.com \
    --to=xiaoyao.li@intel.com \
    --cc=armbru@redhat.com \
    --cc=berrange@redhat.com \
    --cc=francescolavra.fl@gmail.com \
    --cc=kvm@vger.kernel.org \
    --cc=mst@redhat.com \
    --cc=mtosatti@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=philmd@linaro.org \
    --cc=qemu-devel@nongnu.org \
    --cc=rick.p.edgecombe@intel.com \
    --cc=zhao1.liu@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).