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