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(§ions[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