master
c 195 lines 5.7 KB
Raw
1 /*
2 * QEMU PC System Firmware (OVMF specific)
3 *
4 * Copyright (c) 2003-2004 Fabrice Bellard
5 * Copyright (c) 2011-2012 Intel Corporation
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 */
25
26 #include "qemu/osdep.h"
27 #include "qemu/error-report.h"
28 #include "hw/i386/pc.h"
29 #include "exec/target_page.h"
30 #include "target/i386/cpu.h"
31 #include "target/i386/sev.h"
32 #include "kvm/tdx.h"
33
34 #define OVMF_TABLE_FOOTER_GUID "96b582de-1fb2-45f7-baea-a366c55a082d"
35
36 static const int bytes_after_table_footer = 32;
37
38 static bool ovmf_flash_parsed;
39 static uint8_t *ovmf_table;
40 static int ovmf_table_len;
41
42 void pc_system_parse_ovmf_flash(uint8_t *flash_ptr, size_t flash_size)
43 {
44 uint8_t *ptr;
45 QemuUUID guid;
46 int tot_len;
47
48 /* should only be called once */
49 if (ovmf_flash_parsed) {
50 return;
51 }
52
53 ovmf_flash_parsed = true;
54
55 if (flash_size < TARGET_PAGE_SIZE) {
56 return;
57 }
58
59 /*
60 * if this is OVMF there will be a table footer
61 * guid 48 bytes before the end of the flash file
62 * (= 32 bytes after the table + 16 bytes the GUID itself).
63 * If it's not found, silently abort the flash parsing.
64 */
65 qemu_uuid_parse(OVMF_TABLE_FOOTER_GUID, &guid);
66 guid = qemu_uuid_bswap(guid); /* guids are LE */
67 ptr = flash_ptr + flash_size - (bytes_after_table_footer + sizeof(guid));
68 if (!qemu_uuid_is_equal((QemuUUID *)ptr, &guid)) {
69 return;
70 }
71
72 /* if found, just before is two byte table length */
73 ptr -= sizeof(uint16_t);
74 tot_len = le16_to_cpu(*(uint16_t *)ptr) - sizeof(guid) - sizeof(uint16_t);
75
76 if (tot_len < 0 || tot_len > (ptr - flash_ptr)) {
77 error_report("OVMF table has invalid size %d", tot_len);
78 return;
79 }
80
81 if (tot_len == 0) {
82 /* no entries in the OVMF table */
83 return;
84 }
85
86 ovmf_table = g_malloc(tot_len);
87 ovmf_table_len = tot_len;
88
89 /*
90 * ptr is the foot of the table, so copy it all to the newly
91 * allocated ovmf_table and then set the ovmf_table pointer
92 * to the table foot
93 */
94 memcpy(ovmf_table, ptr - tot_len, tot_len);
95 ovmf_table += tot_len;
96 }
97
98 /**
99 * pc_system_ovmf_table_find - Find the data associated with an entry in OVMF's
100 * reset vector GUIDed table.
101 *
102 * @entry: GUID string of the entry to lookup
103 * @data: Filled with a pointer to the entry's value (if not NULL)
104 * @data_len: Filled with the length of the entry's value (if not NULL). Pass
105 * NULL here if the length of data is known.
106 *
107 * Return: true if the entry was found in the OVMF table; false otherwise.
108 */
109 bool pc_system_ovmf_table_find(const char *entry, uint8_t **data,
110 int *data_len)
111 {
112 uint8_t *ptr = ovmf_table;
113 int tot_len = ovmf_table_len;
114 QemuUUID entry_guid;
115
116 assert(ovmf_flash_parsed);
117
118 if (qemu_uuid_parse(entry, &entry_guid) < 0) {
119 return false;
120 }
121
122 if (!ptr) {
123 return false;
124 }
125
126 entry_guid = qemu_uuid_bswap(entry_guid); /* guids are LE */
127 while (tot_len >= sizeof(QemuUUID) + sizeof(uint16_t)) {
128 int len;
129 QemuUUID *guid;
130
131 /*
132 * The data structure is
133 * arbitrary length data
134 * 2 byte length of entire entry
135 * 16 byte guid
136 */
137 guid = (QemuUUID *)(ptr - sizeof(QemuUUID));
138 len = le16_to_cpu(*(uint16_t *)(ptr - sizeof(QemuUUID) -
139 sizeof(uint16_t)));
140
141 /*
142 * just in case the table is corrupt, wouldn't want to spin in
143 * the zero case
144 */
145 if (len < sizeof(QemuUUID) + sizeof(uint16_t)) {
146 return false;
147 } else if (len > tot_len) {
148 return false;
149 }
150
151 ptr -= len;
152 tot_len -= len;
153 if (qemu_uuid_is_equal(guid, &entry_guid)) {
154 if (data) {
155 *data = ptr;
156 }
157 if (data_len) {
158 *data_len = len - sizeof(QemuUUID) - sizeof(uint16_t);
159 }
160 return true;
161 }
162 }
163 return false;
164 }
165
166 void x86_firmware_configure(hwaddr gpa, void *ptr, int size)
167 {
168 int ret;
169
170 /*
171 * OVMF places a GUIDed structures in the flash, so
172 * search for them
173 */
174 pc_system_parse_ovmf_flash(ptr, size);
175
176 if (sev_enabled()) {
177
178 /* Copy the SEV metadata table (if it exists) */
179 pc_system_parse_sev_metadata(ptr, size);
180
181 ret = sev_es_save_reset_vector(ptr, size);
182 if (ret) {
183 error_report("failed to locate and/or save reset vector");
184 exit(1);
185 }
186
187 sev_encrypt_flash(gpa, ptr, size, &error_fatal);
188 } else if (is_tdx_vm()) {
189 ret = tdx_parse_tdvf(ptr, size);
190 if (ret) {
191 error_report("failed to parse TDVF for TDX VM");
192 exit(1);
193 }
194 }
195 }