xref: /qemu/hw/i386/tdvf.c (revision f8a113701dd2d28f3bedb216e59125ddcb77fd05)
1 /*
2  * Copyright (c) 2025 Intel Corporation
3  * Author: Isaku Yamahata <isaku.yamahata at gmail.com>
4  *                        <isaku.yamahata at intel.com>
5  *         Xiaoyao Li <xiaoyao.li@intel.com>
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  */
9 
10 #include "qemu/osdep.h"
11 #include "qemu/error-report.h"
12 
13 #include "hw/i386/pc.h"
14 #include "hw/i386/tdvf.h"
15 #include "system/kvm.h"
16 
17 #define TDX_METADATA_OFFSET_GUID    "e47a6535-984a-4798-865e-4685a7bf8ec2"
18 #define TDX_METADATA_VERSION        1
19 #define TDVF_SIGNATURE              0x46564454 /* TDVF as little endian */
20 #define TDVF_ALIGNMENT              4096
21 
22 /*
23  * the raw structs read from TDVF keeps the name convention in
24  * TDVF Design Guide spec.
25  */
26 typedef struct {
27     uint32_t DataOffset;
28     uint32_t RawDataSize;
29     uint64_t MemoryAddress;
30     uint64_t MemoryDataSize;
31     uint32_t Type;
32     uint32_t Attributes;
33 } TdvfSectionEntry;
34 
35 typedef struct {
36     uint32_t Signature;
37     uint32_t Length;
38     uint32_t Version;
39     uint32_t NumberOfSectionEntries;
40     TdvfSectionEntry SectionEntries[];
41 } TdvfMetadata;
42 
43 struct tdx_metadata_offset {
44     uint32_t offset;
45 };
46 
tdvf_get_metadata(void * flash_ptr,int size)47 static TdvfMetadata *tdvf_get_metadata(void *flash_ptr, int size)
48 {
49     TdvfMetadata *metadata;
50     uint32_t offset = 0;
51     uint8_t *data;
52 
53     if ((uint32_t) size != size) {
54         return NULL;
55     }
56 
57     if (pc_system_ovmf_table_find(TDX_METADATA_OFFSET_GUID, &data, NULL)) {
58         offset = size - le32_to_cpu(((struct tdx_metadata_offset *)data)->offset);
59 
60         if (offset + sizeof(*metadata) > size) {
61             return NULL;
62         }
63     } else {
64         error_report("Cannot find TDX_METADATA_OFFSET_GUID");
65         return NULL;
66     }
67 
68     metadata = flash_ptr + offset;
69 
70     /* Finally, verify the signature to determine if this is a TDVF image. */
71     metadata->Signature = le32_to_cpu(metadata->Signature);
72     if (metadata->Signature != TDVF_SIGNATURE) {
73         error_report("Invalid TDVF signature in metadata!");
74         return NULL;
75     }
76 
77     /* Sanity check that the TDVF doesn't overlap its own metadata. */
78     metadata->Length = le32_to_cpu(metadata->Length);
79     if (offset + metadata->Length > size) {
80         return NULL;
81     }
82 
83     /* Only version 1 is supported/defined. */
84     metadata->Version = le32_to_cpu(metadata->Version);
85     if (metadata->Version != TDX_METADATA_VERSION) {
86         return NULL;
87     }
88 
89     return metadata;
90 }
91 
tdvf_parse_and_check_section_entry(const TdvfSectionEntry * src,TdxFirmwareEntry * entry)92 static int tdvf_parse_and_check_section_entry(const TdvfSectionEntry *src,
93                                               TdxFirmwareEntry *entry)
94 {
95     entry->data_offset = le32_to_cpu(src->DataOffset);
96     entry->data_len = le32_to_cpu(src->RawDataSize);
97     entry->address = le64_to_cpu(src->MemoryAddress);
98     entry->size = le64_to_cpu(src->MemoryDataSize);
99     entry->type = le32_to_cpu(src->Type);
100     entry->attributes = le32_to_cpu(src->Attributes);
101 
102     /* sanity check */
103     if (entry->size < entry->data_len) {
104         error_report("Broken metadata RawDataSize 0x%x MemoryDataSize 0x%"PRIx64,
105                      entry->data_len, entry->size);
106         return -1;
107     }
108     if (!QEMU_IS_ALIGNED(entry->address, TDVF_ALIGNMENT)) {
109         error_report("MemoryAddress 0x%"PRIx64" not page aligned", entry->address);
110         return -1;
111     }
112     if (!QEMU_IS_ALIGNED(entry->size, TDVF_ALIGNMENT)) {
113         error_report("MemoryDataSize 0x%"PRIx64" not page aligned", entry->size);
114         return -1;
115     }
116 
117     switch (entry->type) {
118     case TDVF_SECTION_TYPE_BFV:
119     case TDVF_SECTION_TYPE_CFV:
120         /* The sections that must be copied from firmware image to TD memory */
121         if (entry->data_len == 0) {
122             error_report("%d section with RawDataSize == 0", entry->type);
123             return -1;
124         }
125         break;
126     case TDVF_SECTION_TYPE_TD_HOB:
127     case TDVF_SECTION_TYPE_TEMP_MEM:
128         /* The sections that no need to be copied from firmware image */
129         if (entry->data_len != 0) {
130             error_report("%d section with RawDataSize 0x%x != 0",
131                          entry->type, entry->data_len);
132             return -1;
133         }
134         break;
135     default:
136         error_report("TDVF contains unsupported section type %d", entry->type);
137         return -1;
138     }
139 
140     return 0;
141 }
142 
tdvf_parse_metadata(TdxFirmware * fw,void * flash_ptr,int size)143 int tdvf_parse_metadata(TdxFirmware *fw, void *flash_ptr, int size)
144 {
145     g_autofree TdvfSectionEntry *sections = NULL;
146     TdvfMetadata *metadata;
147     ssize_t entries_size;
148     int i;
149 
150     metadata = tdvf_get_metadata(flash_ptr, size);
151     if (!metadata) {
152         return -EINVAL;
153     }
154 
155     /* load and parse metadata entries */
156     fw->nr_entries = le32_to_cpu(metadata->NumberOfSectionEntries);
157     if (fw->nr_entries < 2) {
158         error_report("Invalid number of fw entries (%u) in TDVF Metadata",
159                      fw->nr_entries);
160         return -EINVAL;
161     }
162 
163     entries_size = fw->nr_entries * sizeof(TdvfSectionEntry);
164     if (metadata->Length != sizeof(*metadata) + entries_size) {
165         error_report("TDVF metadata len (0x%x) mismatch, expected (0x%x)",
166                      metadata->Length,
167                      (uint32_t)(sizeof(*metadata) + entries_size));
168         return -EINVAL;
169     }
170 
171     fw->entries = g_new(TdxFirmwareEntry, fw->nr_entries);
172     sections = g_new(TdvfSectionEntry, fw->nr_entries);
173 
174     memcpy(sections, (void *)metadata + sizeof(*metadata), entries_size);
175 
176     for (i = 0; i < fw->nr_entries; i++) {
177         if (tdvf_parse_and_check_section_entry(&sections[i], &fw->entries[i])) {
178             goto err;
179         }
180     }
181 
182     fw->mem_ptr = flash_ptr;
183     return 0;
184 
185 err:
186     fw->entries = 0;
187     g_free(fw->entries);
188     return -EINVAL;
189 }
190