1 #include <config.h>
2
3 #include <inttypes.h>
4
5 #include "testutils.h"
6 #include "virfilewrapper.h"
7 #include "qemu/qemu_firmware.h"
8 #include "configmake.h"
9
10 #define VIR_FROM_THIS VIR_FROM_QEMU
11
12 /* A very basic test. Parse given JSON firmware description into
13 * an internal structure, format it back and compare with the
14 * contents of the file (minus some keys that are not parsed).
15 */
16 static int
testParseFormatFW(const void * opaque)17 testParseFormatFW(const void *opaque)
18 {
19 const char *filename = opaque;
20 g_autofree char *path = NULL;
21 g_autoptr(qemuFirmware) fw = NULL;
22 g_autofree char *buf = NULL;
23 g_autoptr(virJSONValue) json = NULL;
24 g_autofree char *expected = NULL;
25 g_autofree char *actual = NULL;
26
27 path = g_strdup_printf("%s/qemufirmwaredata/%s", abs_srcdir, filename);
28
29 if (!(fw = qemuFirmwareParse(path)))
30 return -1;
31
32 if (virFileReadAll(path,
33 1024 * 1024, /* 1MiB */
34 &buf) < 0)
35 return -1;
36
37 if (!(json = virJSONValueFromString(buf)))
38 return -1;
39
40 /* Description and tags are not parsed. */
41 if (virJSONValueObjectRemoveKey(json, "description", NULL) < 0 ||
42 virJSONValueObjectRemoveKey(json, "tags", NULL) < 0)
43 return -1;
44
45 if (!(expected = virJSONValueToString(json, true)))
46 return -1;
47
48 if (!(actual = qemuFirmwareFormat(fw)))
49 return -1;
50
51 return virTestCompareToString(expected, actual);
52 }
53
54
55 static int
testFWPrecedence(const void * opaque G_GNUC_UNUSED)56 testFWPrecedence(const void *opaque G_GNUC_UNUSED)
57 {
58 g_autofree char *fakehome = NULL;
59 g_auto(GStrv) fwList = NULL;
60 const char *expected[] = {
61 PREFIX "/share/qemu/firmware/40-bios.json",
62 SYSCONFDIR "/qemu/firmware/40-ovmf-sb-keys.json",
63 PREFIX "/share/qemu/firmware/50-ovmf-sb-keys.json",
64 PREFIX "/share/qemu/firmware/61-ovmf.json",
65 PREFIX "/share/qemu/firmware/70-aavmf.json",
66 NULL
67 };
68 const char **e;
69 GStrv f;
70
71 fakehome = g_strdup(abs_srcdir "/qemufirmwaredata/home/user/.config");
72
73 g_setenv("XDG_CONFIG_HOME", fakehome, TRUE);
74
75 if (qemuFirmwareFetchConfigs(&fwList, false) < 0)
76 return -1;
77
78 if (!fwList) {
79 fprintf(stderr, "Expected a non-NULL result, but got a NULL result\n");
80 return -1;
81 }
82
83 for (e = expected, f = fwList; *f || *e;) {
84 if (STRNEQ_NULLABLE(*f, *e)) {
85 fprintf(stderr,
86 "Unexpected path. Expected %s got %s \n",
87 NULLSTR(*e), NULLSTR(*f));
88 return -1;
89 }
90
91 if (*f)
92 f++;
93 if (*e)
94 e++;
95 }
96
97 return 0;
98 }
99
100
101 struct supportedData {
102 const char *machine;
103 virArch arch;
104 bool secure;
105 const char *fwlist;
106 unsigned int *interfaces;
107 size_t ninterfaces;
108 };
109
110
111 static int
testSupportedFW(const void * opaque)112 testSupportedFW(const void *opaque)
113 {
114 const struct supportedData *data = opaque;
115 uint64_t actualInterfaces;
116 uint64_t expectedInterfaces = 0;
117 bool actualSecure;
118 virFirmware **expFWs = NULL;
119 size_t nexpFWs = 0;
120 virFirmware **actFWs = NULL;
121 size_t nactFWs = 0;
122 size_t i;
123 int ret = -1;
124
125 for (i = 0; i < data->ninterfaces; i++)
126 expectedInterfaces |= 1ULL << data->interfaces[i];
127
128 if (virFirmwareParseList(data->fwlist, &expFWs, &nexpFWs) < 0) {
129 fprintf(stderr, "Unable to parse list of expected FW paths\n");
130 return -1;
131 }
132
133 /* virFirmwareParseList() expects to see pairs of paths: ${FW}:${NVRAM}.
134 * Well, some images don't have a NVRAM store. In that case NULL was passed:
135 * ${FW}:NULL. Now iterate over expected firmwares and fix this. */
136 for (i = 0; i < nexpFWs; i++) {
137 virFirmware *tmp = expFWs[i];
138
139 if (STREQ(tmp->nvram, "NULL"))
140 VIR_FREE(tmp->nvram);
141 }
142
143 if (qemuFirmwareGetSupported(data->machine, data->arch, false,
144 &actualInterfaces, &actualSecure, &actFWs, &nactFWs) < 0) {
145 fprintf(stderr, "Unable to get list of supported interfaces\n");
146 goto cleanup;
147 }
148
149 if (actualInterfaces != expectedInterfaces) {
150 fprintf(stderr,
151 "Mismatch in supported interfaces. "
152 "Expected 0x%" PRIx64 " got 0x%" PRIx64 "\n",
153 expectedInterfaces, actualInterfaces);
154 goto cleanup;
155 }
156
157 if (actualSecure != data->secure) {
158 fprintf(stderr,
159 "Mismatch in SMM requirement/support. "
160 "Expected %d got %d\n",
161 data->secure, actualSecure);
162 goto cleanup;
163 }
164
165 for (i = 0; i < nactFWs; i++) {
166 virFirmware *actFW = actFWs[i];
167 virFirmware *expFW = NULL;
168
169 if (i >= nexpFWs) {
170 fprintf(stderr, "Unexpected FW image: %s NVRAM: %s\n",
171 actFW->name, NULLSTR(actFW->nvram));
172 goto cleanup;
173 }
174
175 expFW = expFWs[i];
176
177 if (STRNEQ(actFW->name, expFW->name) ||
178 STRNEQ_NULLABLE(actFW->nvram, expFW->nvram)) {
179 fprintf(stderr, "Unexpected FW image: %s NVRAM: %s\n"
180 "Expected: %s NVRAM: %s\n",
181 actFW->name, NULLSTR(actFW->nvram),
182 expFW->name, NULLSTR(expFW->nvram));
183 goto cleanup;
184 }
185 }
186
187 if (i < nexpFWs) {
188 fprintf(stderr, "Expected FW image: %s NVRAM: %s got nothing\n",
189 expFWs[i]->name, NULLSTR(expFWs[i]->nvram));
190 goto cleanup;
191 }
192
193 ret = 0;
194 cleanup:
195 virFirmwareFreeList(actFWs, nactFWs);
196 virFirmwareFreeList(expFWs, nexpFWs);
197 return ret;
198 }
199
200
201 static int
mymain(void)202 mymain(void)
203 {
204 int ret = 0;
205
206 virFileWrapperAddPrefix(SYSCONFDIR "/qemu/firmware",
207 abs_srcdir "/qemufirmwaredata/etc/qemu/firmware");
208 virFileWrapperAddPrefix(PREFIX "/share/qemu/firmware",
209 abs_srcdir "/qemufirmwaredata/usr/share/qemu/firmware");
210 virFileWrapperAddPrefix("/home/user/.config/qemu/firmware",
211 abs_srcdir "/qemufirmwaredata/home/user/.config/qemu/firmware");
212
213 #define DO_PARSE_TEST(filename) \
214 do { \
215 if (virTestRun("QEMU FW " filename, \
216 testParseFormatFW, filename) < 0) \
217 ret = -1; \
218 } while (0)
219
220 DO_PARSE_TEST("usr/share/qemu/firmware/40-bios.json");
221 DO_PARSE_TEST("usr/share/qemu/firmware/50-ovmf-sb-keys.json");
222 DO_PARSE_TEST("usr/share/qemu/firmware/60-ovmf-sb.json");
223 DO_PARSE_TEST("usr/share/qemu/firmware/61-ovmf.json");
224 DO_PARSE_TEST("usr/share/qemu/firmware/70-aavmf.json");
225
226 if (virTestRun("QEMU FW precedence test", testFWPrecedence, NULL) < 0)
227 ret = -1;
228
229 /* The @fwlist contains pairs of ${FW}:${NVRAM}. If there's
230 * no NVRAM expected pass literal "NULL" and test fixes that
231 * later. */
232 #define DO_SUPPORTED_TEST(machine, arch, secure, fwlist, ...) \
233 do { \
234 unsigned int interfaces[] = {__VA_ARGS__}; \
235 struct supportedData data = {machine, arch, secure, fwlist, \
236 interfaces, G_N_ELEMENTS(interfaces)}; \
237 if (virTestRun("QEMU FW SUPPORTED " machine " " #arch, \
238 testSupportedFW, &data) < 0) \
239 ret = -1; \
240 } while (0)
241
242 DO_SUPPORTED_TEST("pc-i440fx-3.1", VIR_ARCH_X86_64, false,
243 "/usr/share/seabios/bios-256k.bin:NULL:"
244 "/usr/share/OVMF/OVMF_CODE.fd:/usr/share/OVMF/OVMF_VARS.fd",
245 VIR_DOMAIN_OS_DEF_FIRMWARE_BIOS,
246 VIR_DOMAIN_OS_DEF_FIRMWARE_EFI);
247 DO_SUPPORTED_TEST("pc-i440fx-3.1", VIR_ARCH_I686, false,
248 "/usr/share/seabios/bios-256k.bin:NULL",
249 VIR_DOMAIN_OS_DEF_FIRMWARE_BIOS);
250 DO_SUPPORTED_TEST("pc-q35-3.1", VIR_ARCH_X86_64, true,
251 "/usr/share/seabios/bios-256k.bin:NULL:"
252 "/usr/share/OVMF/OVMF_CODE.secboot.fd:/usr/share/OVMF/OVMF_VARS.secboot.fd:"
253 "/usr/share/OVMF/OVMF_CODE.fd:/usr/share/OVMF/OVMF_VARS.fd",
254 VIR_DOMAIN_OS_DEF_FIRMWARE_BIOS,
255 VIR_DOMAIN_OS_DEF_FIRMWARE_EFI);
256 DO_SUPPORTED_TEST("pc-q35-3.1", VIR_ARCH_I686, false,
257 "/usr/share/seabios/bios-256k.bin:NULL",
258 VIR_DOMAIN_OS_DEF_FIRMWARE_BIOS);
259 DO_SUPPORTED_TEST("virt-3.1", VIR_ARCH_AARCH64, false,
260 "/usr/share/AAVMF/AAVMF_CODE.fd:/usr/share/AAVMF/AAVMF_VARS.fd",
261 VIR_DOMAIN_OS_DEF_FIRMWARE_EFI);
262
263 virFileWrapperClearPrefixes();
264
265 return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
266 }
267
268
269 VIR_TEST_MAIN(mymain)
270