qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Paolo Bonzini <pbonzini@redhat.com>
To: qemu-devel@nongnu.org
Cc: Isaku Yamahata <isaku.yamahata@intel.com>,
	Xiaoyao Li <xiaoyao.li@intel.com>,
	Gerd Hoffmann <kraxel@redhat.com>, Zhao Liu <zhao1.liu@intel.com>
Subject: [PULL 29/77] i386/tdvf: Introduce function to parse TDVF metadata
Date: Fri, 30 May 2025 09:11:59 +0200	[thread overview]
Message-ID: <20250530071250.2050910-30-pbonzini@redhat.com> (raw)
In-Reply-To: <20250530071250.2050910-1-pbonzini@redhat.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>
Link: https://lore.kernel.org/r/20250508150002.689633-18-xiaoyao.li@intel.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 include/hw/i386/tdvf.h |  38 +++++++++
 hw/i386/tdvf.c         | 188 +++++++++++++++++++++++++++++++++++++++++
 hw/i386/Kconfig        |   1 +
 hw/i386/meson.build    |   1 +
 4 files changed, 228 insertions(+)
 create mode 100644 include/hw/i386/tdvf.h
 create mode 100644 hw/i386/tdvf.c

diff --git a/include/hw/i386/tdvf.h b/include/hw/i386/tdvf.h
new file mode 100644
index 00000000000..7ebcac42a36
--- /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 */
diff --git a/hw/i386/tdvf.c b/hw/i386/tdvf.c
new file mode 100644
index 00000000000..e2d486946a0
--- /dev/null
+++ b/hw/i386/tdvf.c
@@ -0,0 +1,188 @@
+/*
+ * 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 */
+#define TDVF_ALIGNMENT              4096
+
+/*
+ * 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, TDVF_ALIGNMENT)) {
+        error_report("MemoryAddress 0x%lx not page aligned", entry->address);
+        return -1;
+    }
+    if (!QEMU_IS_ALIGNED(entry->size, TDVF_ALIGNMENT)) {
+        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/hw/i386/Kconfig b/hw/i386/Kconfig
index cce9521ba93..eb65bda6e07 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 10bdfde27c6..3bc1da2b6eb 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')
-- 
2.49.0



  parent reply	other threads:[~2025-05-30  7:15 UTC|newest]

Thread overview: 92+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-05-30  7:11 [PULL 00/77] i386, build system changes for 2025-05-30 Paolo Bonzini
2025-05-30  7:11 ` [PULL 01/77] meson: build target libraries with common dependencies Paolo Bonzini
2025-05-30  7:11 ` [PULL 02/77] hw/arm: remove explicit dependencies listed Paolo Bonzini
2025-06-02 13:59   ` Cédric Le Goater
2025-06-02 18:39     ` Pierrick Bouvier
2025-06-02 20:53       ` Nabih Estefan
2025-06-02 21:11         ` Cédric Le Goater
2025-06-02 22:37           ` Pierrick Bouvier
2025-06-02 23:40             ` Pierrick Bouvier
2025-05-30  7:11 ` [PULL 03/77] target/arm: " Paolo Bonzini
2025-05-30  7:11 ` [PULL 04/77] meson: apply target config for picking files from lib{system, user} Paolo Bonzini
2025-05-30  7:11 ` [PULL 05/77] meson: merge lib{system, user}_ss with {system, user}_ss Paolo Bonzini
2025-05-30  7:11 ` [PULL 06/77] meson: remove lib{system, user}_ss aliases Paolo Bonzini
2025-05-30  7:11 ` [PULL 07/77] meson: merge hw_common_arch in target_common_system_arch Paolo Bonzini
2025-05-30  7:11 ` [PULL 08/77] hw/timer/hpet: Reorganize register decoding Paolo Bonzini
2025-05-30  7:11 ` [PULL 09/77] rust: Fix Zhao's email address Paolo Bonzini
2025-05-30  7:11 ` [PULL 10/77] rust: Fix the typos in doc Paolo Bonzini
2025-05-30  7:11 ` [PULL 11/77] rust: hpet: rename hpet module to "device" Paolo Bonzini
2025-05-30  7:11 ` [PULL 12/77] target/i386/emulate: more lflags cleanups Paolo Bonzini
2025-05-30  7:11 ` [PULL 13/77] rocker: do not pollute the namespace Paolo Bonzini
2025-06-10  7:53   ` Philippe Mathieu-Daudé
2025-06-10  7:54     ` Philippe Mathieu-Daudé
2025-05-30  7:11 ` [PULL 14/77] i386: Introduce tdx-guest object Paolo Bonzini
2025-05-30  7:11 ` [PULL 15/77] i386/tdx: Implement tdx_kvm_type() for TDX Paolo Bonzini
2025-05-30  7:11 ` [PULL 16/77] i386/tdx: Implement tdx_kvm_init() to initialize TDX VM context Paolo Bonzini
2025-05-30  7:11 ` [PULL 17/77] i386/tdx: Get tdx_capabilities via KVM_TDX_CAPABILITIES Paolo Bonzini
2025-05-30  7:11 ` [PULL 18/77] i386/tdx: Introduce is_tdx_vm() helper and cache tdx_guest object Paolo Bonzini
2025-05-30  7:11 ` [PULL 19/77] kvm: Introduce kvm_arch_pre_create_vcpu() Paolo Bonzini
2025-05-30  7:11 ` [PULL 20/77] i386/tdx: Initialize TDX before creating TD vcpus Paolo Bonzini
2025-07-17  9:40   ` Peter Maydell
2025-07-17 10:10     ` Xiaoyao Li
2025-05-30  7:11 ` [PULL 21/77] i386/tdx: Add property sept-ve-disable for tdx-guest object Paolo Bonzini
2025-05-30  7:11 ` [PULL 22/77] i386/tdx: Make sept_ve_disable set by default Paolo Bonzini
2025-05-30  7:11 ` [PULL 23/77] i386/tdx: Wire CPU features up with attributes of TD guest Paolo Bonzini
2025-05-30  7:11 ` [PULL 24/77] i386/tdx: Validate TD attributes Paolo Bonzini
2025-05-30  7:11 ` [PULL 25/77] i386/tdx: Support user configurable mrconfigid/mrowner/mrownerconfig Paolo Bonzini
2025-05-30  7:11 ` [PULL 26/77] i386/tdx: Set APIC bus rate to match with what TDX module enforces Paolo Bonzini
2025-05-30  7:11 ` [PULL 27/77] i386/tdx: Implement user specified tsc frequency Paolo Bonzini
2025-05-30  7:11 ` [PULL 28/77] i386/tdx: load TDVF for TD guest Paolo Bonzini
2025-05-30  7:11 ` Paolo Bonzini [this message]
2025-05-30  7:12 ` [PULL 30/77] i386/tdx: Parse TDVF metadata for TDX VM Paolo Bonzini
2025-05-30  7:12 ` [PULL 31/77] i386/tdx: Don't initialize pc.rom for TDX VMs Paolo Bonzini
2025-05-30  7:12 ` [PULL 32/77] i386/tdx: Track mem_ptr for each firmware entry of TDVF Paolo Bonzini
2025-05-30  7:12 ` [PULL 33/77] i386/tdx: Track RAM entries for TDX VM Paolo Bonzini
2025-05-30  7:12 ` [PULL 34/77] headers: Add definitions from UEFI spec for volumes, resources, etc Paolo Bonzini
2025-05-30  7:12 ` [PULL 35/77] i386/tdx: Setup the TD HOB list Paolo Bonzini
2025-05-30  7:12 ` [PULL 36/77] i386/tdx: Add TDVF memory via KVM_TDX_INIT_MEM_REGION Paolo Bonzini
2025-05-30  7:12 ` [PULL 37/77] i386/tdx: Call KVM_TDX_INIT_VCPU to initialize TDX vcpu Paolo Bonzini
2025-05-30  7:12 ` [PULL 38/77] i386/tdx: Finalize TDX VM Paolo Bonzini
2025-05-30  7:12 ` [PULL 39/77] i386/tdx: Enable user exit on KVM_HC_MAP_GPA_RANGE Paolo Bonzini
2025-05-30  7:12 ` [PULL 40/77] i386/tdx: Handle KVM_SYSTEM_EVENT_TDX_FATAL Paolo Bonzini
2025-05-30  7:12 ` [PULL 41/77] i386/tdx: Wire TDX_REPORT_FATAL_ERROR with GuestPanic facility Paolo Bonzini
2025-05-30  7:12 ` [PULL 42/77] kvm: Check KVM_CAP_MAX_VCPUS at vm level Paolo Bonzini
2025-05-30  7:12 ` [PULL 43/77] i386/cpu: introduce x86_confidential_guest_cpu_instance_init() Paolo Bonzini
2025-05-30  7:12 ` [PULL 44/77] i386/tdx: implement tdx_cpu_instance_init() Paolo Bonzini
2025-05-30  7:12 ` [PULL 45/77] i386/cpu: Introduce enable_cpuid_0x1f to force exposing CPUID 0x1f Paolo Bonzini
2025-05-30  7:12 ` [PULL 46/77] i386/tdx: Force " Paolo Bonzini
2025-05-30  7:12 ` [PULL 47/77] i386/tdx: Set kvm_readonly_mem_enabled to false for TDX VM Paolo Bonzini
2025-05-30  7:12 ` [PULL 48/77] i386/tdx: Disable SMM for TDX VMs Paolo Bonzini
2025-05-30  7:12 ` [PULL 49/77] i386/tdx: Disable PIC " Paolo Bonzini
2025-05-30  7:12 ` [PULL 50/77] i386/tdx: Set and check kernel_irqchip mode for TDX Paolo Bonzini
2025-05-30  7:12 ` [PULL 51/77] i386/tdx: Don't synchronize guest tsc for TDs Paolo Bonzini
2025-05-30  7:12 ` [PULL 52/77] i386/tdx: Only configure MSR_IA32_UCODE_REV in kvm_init_msrs() " Paolo Bonzini
2025-05-30  7:12 ` [PULL 53/77] i386/apic: Skip kvm_apic_put() for TDX Paolo Bonzini
2025-05-30  7:12 ` [PULL 54/77] cpu: Don't set vcpu_dirty when guest_state_protected Paolo Bonzini
2025-05-30  7:12 ` [PULL 55/77] i386/cgs: Rename *mask_cpuid_features() to *adjust_cpuid_features() Paolo Bonzini
2025-05-30  7:12 ` [PULL 56/77] i386/tdx: Implement adjust_cpuid_features() for TDX Paolo Bonzini
2025-05-30  7:12 ` [PULL 57/77] i386/tdx: Add TDX fixed1 bits to supported CPUIDs Paolo Bonzini
2025-05-30  7:12 ` [PULL 58/77] i386/tdx: Add supported CPUID bits related to TD Attributes Paolo Bonzini
2025-05-30  7:12 ` [PULL 59/77] i386/tdx: Add supported CPUID bits relates to XFAM Paolo Bonzini
2025-07-10 12:27   ` Peter Maydell
2025-07-10 12:42     ` Xiaoyao Li
2025-07-10 12:57       ` Peter Maydell
2025-05-30  7:12 ` [PULL 60/77] i386/tdx: Add XFD to supported bit of TDX Paolo Bonzini
2025-05-30  7:12 ` [PULL 61/77] i386/tdx: Define supported KVM features for TDX Paolo Bonzini
2025-05-30  7:12 ` [PULL 62/77] i386/cgs: Introduce x86_confidential_guest_check_features() Paolo Bonzini
2025-05-30  7:12 ` [PULL 63/77] i386: Remove unused parameter "uint32_t bit" in feature_word_description() Paolo Bonzini
2025-05-30  7:12 ` [PULL 64/77] target/i386: Print CPUID subleaf info for unsupported feature Paolo Bonzini
2025-05-30  7:12 ` [PULL 65/77] i386/tdx: Fetch and validate CPUID of TD guest Paolo Bonzini
2025-05-30  7:12 ` [PULL 66/77] i386/tdx: Don't treat SYSCALL as unavailable Paolo Bonzini
2025-05-30  7:12 ` [PULL 67/77] i386/tdx: Make invtsc default on Paolo Bonzini
2025-05-30  7:12 ` [PULL 68/77] i386/tdx: Validate phys_bits against host value Paolo Bonzini
2025-05-30  7:12 ` [PULL 69/77] docs: Add TDX documentation Paolo Bonzini
2025-05-30  7:12 ` [PULL 70/77] rust: make declaration of dependent crates more consistent Paolo Bonzini
2025-05-30  7:12 ` [PULL 71/77] target/i386: Update EPYC CPU model for Cache property, RAS, SVM feature bits Paolo Bonzini
2025-05-30  7:12 ` [PULL 72/77] target/i386: Update EPYC-Rome " Paolo Bonzini
2025-05-30  7:12 ` [PULL 73/77] target/i386: Update EPYC-Milan " Paolo Bonzini
2025-05-30  7:12 ` [PULL 74/77] target/i386: Add couple of feature bits in CPUID_Fn80000021_EAX Paolo Bonzini
2025-05-30  7:12 ` [PULL 75/77] target/i386: Update EPYC-Genoa for Cache property, perfmon-v2, RAS and SVM feature bits Paolo Bonzini
2025-05-30  7:12 ` [PULL 76/77] target/i386: Add support for EPYC-Turin model Paolo Bonzini
2025-05-30  7:12 ` [PULL 77/77] target/i386/tcg/helper-tcg: fix file references in comments Paolo Bonzini
2025-05-31 23:05 ` [PULL 00/77] i386, build system changes for 2025-05-30 Stefan Hajnoczi

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=20250530071250.2050910-30-pbonzini@redhat.com \
    --to=pbonzini@redhat.com \
    --cc=isaku.yamahata@intel.com \
    --cc=kraxel@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=xiaoyao.li@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).