2019-02-20 00:35:43 +08:00
|
|
|
#include <config.h>
|
|
|
|
|
2019-04-04 21:52:53 +08:00
|
|
|
#include <inttypes.h>
|
|
|
|
|
2019-02-20 00:35:43 +08:00
|
|
|
#include "testutils.h"
|
2019-02-21 22:24:00 +08:00
|
|
|
#include "virfilewrapper.h"
|
2019-02-20 00:35:43 +08:00
|
|
|
#include "qemu/qemu_firmware.h"
|
2019-02-21 22:24:00 +08:00
|
|
|
#include "configmake.h"
|
2019-02-20 00:35:43 +08:00
|
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_QEMU
|
|
|
|
|
|
|
|
/* A very basic test. Parse given JSON firmware description into
|
|
|
|
* an internal structure, format it back and compare with the
|
|
|
|
* contents of the file (minus some keys that are not parsed).
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
testParseFormatFW(const void *opaque)
|
|
|
|
{
|
|
|
|
const char *filename = opaque;
|
2019-10-15 21:16:31 +08:00
|
|
|
g_autofree char *path = NULL;
|
2019-10-15 20:47:50 +08:00
|
|
|
g_autoptr(qemuFirmware) fw = NULL;
|
2019-10-15 21:16:31 +08:00
|
|
|
g_autofree char *buf = NULL;
|
2019-10-15 20:47:50 +08:00
|
|
|
g_autoptr(virJSONValue) json = NULL;
|
2019-10-15 21:16:31 +08:00
|
|
|
g_autofree char *expected = NULL;
|
|
|
|
g_autofree char *actual = NULL;
|
2019-02-20 00:35:43 +08:00
|
|
|
|
2019-10-22 21:26:14 +08:00
|
|
|
path = g_strdup_printf("%s/qemufirmwaredata/%s", abs_srcdir, filename);
|
2019-02-20 00:35:43 +08:00
|
|
|
|
|
|
|
if (!(fw = qemuFirmwareParse(path)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (virFileReadAll(path,
|
|
|
|
1024 * 1024, /* 1MiB */
|
|
|
|
&buf) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (!(json = virJSONValueFromString(buf)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Description and tags are not parsed. */
|
|
|
|
if (virJSONValueObjectRemoveKey(json, "description", NULL) < 0 ||
|
|
|
|
virJSONValueObjectRemoveKey(json, "tags", NULL) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (!(expected = virJSONValueToString(json, true)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (!(actual = qemuFirmwareFormat(fw)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return virTestCompareToString(expected, actual);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-02-21 22:24:00 +08:00
|
|
|
static int
|
2019-10-14 20:45:03 +08:00
|
|
|
testFWPrecedence(const void *opaque G_GNUC_UNUSED)
|
2019-02-21 22:24:00 +08:00
|
|
|
{
|
2019-10-15 21:16:31 +08:00
|
|
|
g_autofree char *fakehome = NULL;
|
2019-02-21 22:24:00 +08:00
|
|
|
VIR_AUTOSTRINGLIST fwList = NULL;
|
|
|
|
size_t nfwList;
|
|
|
|
size_t i;
|
|
|
|
const char *expected[] = {
|
|
|
|
PREFIX "/share/qemu/firmware/40-bios.json",
|
|
|
|
SYSCONFDIR "/qemu/firmware/40-ovmf-sb-keys.json",
|
|
|
|
PREFIX "/share/qemu/firmware/50-ovmf-sb-keys.json",
|
|
|
|
PREFIX "/share/qemu/firmware/61-ovmf.json",
|
|
|
|
PREFIX "/share/qemu/firmware/70-aavmf.json",
|
|
|
|
};
|
2019-10-15 19:55:26 +08:00
|
|
|
const size_t nexpected = G_N_ELEMENTS(expected);
|
2019-02-21 22:24:00 +08:00
|
|
|
|
2019-10-20 19:49:46 +08:00
|
|
|
fakehome = g_strdup(abs_srcdir "/qemufirmwaredata/home/user/.config");
|
2019-02-21 22:24:00 +08:00
|
|
|
|
|
|
|
setenv("XDG_CONFIG_HOME", fakehome, 1);
|
|
|
|
|
|
|
|
if (qemuFirmwareFetchConfigs(&fwList, false) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (!fwList) {
|
|
|
|
fprintf(stderr, "Expected a non-NULL result, but got a NULL result\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
nfwList = virStringListLength((const char **)fwList);
|
|
|
|
|
2019-03-26 15:57:33 +08:00
|
|
|
for (i = 0; i < MAX(nfwList, nexpected); i++) {
|
|
|
|
const char *e = i < nexpected ? expected[i] : NULL;
|
|
|
|
const char *f = i < nfwList ? fwList[i] : NULL;
|
|
|
|
|
|
|
|
if (STRNEQ_NULLABLE(e, f)) {
|
2019-02-21 22:24:00 +08:00
|
|
|
fprintf(stderr,
|
|
|
|
"Unexpected path (i=%zu). Expected %s got %s \n",
|
2019-03-26 15:57:33 +08:00
|
|
|
i, NULLSTR(e), NULLSTR(f));
|
2019-02-21 22:24:00 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-04-04 21:52:53 +08:00
|
|
|
struct supportedData {
|
|
|
|
const char *machine;
|
|
|
|
virArch arch;
|
|
|
|
bool secure;
|
2019-08-05 22:11:26 +08:00
|
|
|
const char *fwlist;
|
2019-04-04 21:52:53 +08:00
|
|
|
unsigned int *interfaces;
|
|
|
|
size_t ninterfaces;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
testSupportedFW(const void *opaque)
|
|
|
|
{
|
|
|
|
const struct supportedData *data = opaque;
|
|
|
|
uint64_t actualInterfaces;
|
|
|
|
uint64_t expectedInterfaces = 0;
|
|
|
|
bool actualSecure;
|
2019-08-05 22:11:26 +08:00
|
|
|
virFirmwarePtr *expFWs = NULL;
|
|
|
|
size_t nexpFWs = 0;
|
|
|
|
virFirmwarePtr *actFWs = NULL;
|
|
|
|
size_t nactFWs = 0;
|
2019-04-04 21:52:53 +08:00
|
|
|
size_t i;
|
2019-08-05 22:11:26 +08:00
|
|
|
int ret = -1;
|
2019-04-04 21:52:53 +08:00
|
|
|
|
|
|
|
for (i = 0; i < data->ninterfaces; i++)
|
|
|
|
expectedInterfaces |= 1ULL << data->interfaces[i];
|
|
|
|
|
2019-08-05 22:11:26 +08:00
|
|
|
if (virFirmwareParseList(data->fwlist, &expFWs, &nexpFWs) < 0) {
|
|
|
|
fprintf(stderr, "Unable to parse list of expected FW paths\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* virFirmwareParseList() expects to see pairs of paths: ${FW}:${NVRAM}.
|
|
|
|
* Well, some images don't have a NVRAM store. In that case NULL was passed:
|
|
|
|
* ${FW}:NULL. Now iterate over expected firmwares and fix this. */
|
|
|
|
for (i = 0; i < nexpFWs; i++) {
|
|
|
|
virFirmwarePtr tmp = expFWs[i];
|
|
|
|
|
|
|
|
if (STREQ(tmp->nvram, "NULL"))
|
|
|
|
VIR_FREE(tmp->nvram);
|
|
|
|
}
|
|
|
|
|
2019-04-04 21:52:53 +08:00
|
|
|
if (qemuFirmwareGetSupported(data->machine, data->arch, false,
|
2019-08-05 22:11:26 +08:00
|
|
|
&actualInterfaces, &actualSecure, &actFWs, &nactFWs) < 0) {
|
2019-04-04 21:52:53 +08:00
|
|
|
fprintf(stderr, "Unable to get list of supported interfaces\n");
|
2019-08-05 22:11:26 +08:00
|
|
|
goto cleanup;
|
2019-04-04 21:52:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (actualInterfaces != expectedInterfaces) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Mismatch in supported interfaces. "
|
|
|
|
"Expected 0x%" PRIx64 " got 0x%" PRIx64 "\n",
|
|
|
|
expectedInterfaces, actualInterfaces);
|
2019-08-05 22:11:26 +08:00
|
|
|
goto cleanup;
|
2019-04-04 21:52:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (actualSecure != data->secure) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Mismatch in SMM requirement/support. "
|
|
|
|
"Expected %d got %d\n",
|
|
|
|
data->secure, actualSecure);
|
2019-08-05 22:11:26 +08:00
|
|
|
goto cleanup;
|
2019-04-04 21:52:53 +08:00
|
|
|
}
|
|
|
|
|
2019-08-05 22:11:26 +08:00
|
|
|
for (i = 0; i < nactFWs; i++) {
|
|
|
|
virFirmwarePtr actFW = actFWs[i];
|
|
|
|
virFirmwarePtr expFW = NULL;
|
|
|
|
|
|
|
|
if (i >= nexpFWs) {
|
|
|
|
fprintf(stderr, "Unexpected FW image: %s NVRAM: %s\n",
|
|
|
|
actFW->name, NULLSTR(actFW->nvram));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
expFW = expFWs[i];
|
|
|
|
|
|
|
|
if (STRNEQ(actFW->name, expFW->name) ||
|
|
|
|
STRNEQ_NULLABLE(actFW->nvram, expFW->nvram)) {
|
|
|
|
fprintf(stderr, "Unexpected FW image: %s NVRAM: %s\n"
|
|
|
|
"Expected: %s NVRAM: %s\n",
|
|
|
|
actFW->name, NULLSTR(actFW->nvram),
|
|
|
|
expFW->name, NULLSTR(expFW->nvram));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i < nexpFWs) {
|
|
|
|
fprintf(stderr, "Expected FW image: %s NVRAM: %s got nothing\n",
|
|
|
|
expFWs[i]->name, NULLSTR(expFWs[i]->nvram));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
|
|
virFirmwareFreeList(actFWs, nactFWs);
|
|
|
|
virFirmwareFreeList(expFWs, nexpFWs);
|
|
|
|
return ret;
|
2019-04-04 21:52:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-02-20 00:35:43 +08:00
|
|
|
static int
|
|
|
|
mymain(void)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
2019-02-21 22:24:00 +08:00
|
|
|
virFileWrapperAddPrefix(SYSCONFDIR "/qemu/firmware",
|
|
|
|
abs_srcdir "/qemufirmwaredata/etc/qemu/firmware");
|
|
|
|
virFileWrapperAddPrefix(PREFIX "/share/qemu/firmware",
|
|
|
|
abs_srcdir "/qemufirmwaredata/usr/share/qemu/firmware");
|
|
|
|
virFileWrapperAddPrefix("/home/user/.config/qemu/firmware",
|
|
|
|
abs_srcdir "/qemufirmwaredata/home/user/.config/qemu/firmware");
|
|
|
|
|
2019-02-20 00:35:43 +08:00
|
|
|
#define DO_PARSE_TEST(filename) \
|
|
|
|
do { \
|
|
|
|
if (virTestRun("QEMU FW " filename, \
|
|
|
|
testParseFormatFW, filename) < 0) \
|
|
|
|
ret = -1; \
|
|
|
|
} while (0)
|
|
|
|
|
2019-02-21 22:24:00 +08:00
|
|
|
DO_PARSE_TEST("usr/share/qemu/firmware/40-bios.json");
|
|
|
|
DO_PARSE_TEST("usr/share/qemu/firmware/50-ovmf-sb-keys.json");
|
|
|
|
DO_PARSE_TEST("usr/share/qemu/firmware/60-ovmf-sb.json");
|
|
|
|
DO_PARSE_TEST("usr/share/qemu/firmware/61-ovmf.json");
|
|
|
|
DO_PARSE_TEST("usr/share/qemu/firmware/70-aavmf.json");
|
|
|
|
|
|
|
|
if (virTestRun("QEMU FW precedence test", testFWPrecedence, NULL) < 0)
|
|
|
|
ret = -1;
|
2019-02-20 00:35:43 +08:00
|
|
|
|
2019-08-05 22:11:26 +08:00
|
|
|
/* The @fwlist contains pairs of ${FW}:${NVRAM}. If there's
|
|
|
|
* no NVRAM expected pass literal "NULL" and test fixes that
|
|
|
|
* later. */
|
|
|
|
#define DO_SUPPORTED_TEST(machine, arch, secure, fwlist, ...) \
|
2019-04-04 21:52:53 +08:00
|
|
|
do { \
|
|
|
|
unsigned int interfaces[] = {__VA_ARGS__}; \
|
2019-08-05 22:11:26 +08:00
|
|
|
struct supportedData data = {machine, arch, secure, fwlist, \
|
2019-10-15 19:55:26 +08:00
|
|
|
interfaces, G_N_ELEMENTS(interfaces)}; \
|
2019-04-04 21:52:53 +08:00
|
|
|
if (virTestRun("QEMU FW SUPPORTED " machine " " #arch, \
|
|
|
|
testSupportedFW, &data) < 0) \
|
|
|
|
ret = -1; \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
DO_SUPPORTED_TEST("pc-i440fx-3.1", VIR_ARCH_X86_64, false,
|
2019-08-05 22:11:26 +08:00
|
|
|
"/usr/share/seabios/bios-256k.bin:NULL:"
|
|
|
|
"/usr/share/OVMF/OVMF_CODE.fd:/usr/share/OVMF/OVMF_VARS.fd",
|
2019-04-04 21:52:53 +08:00
|
|
|
VIR_DOMAIN_OS_DEF_FIRMWARE_BIOS,
|
|
|
|
VIR_DOMAIN_OS_DEF_FIRMWARE_EFI);
|
|
|
|
DO_SUPPORTED_TEST("pc-i440fx-3.1", VIR_ARCH_I686, false,
|
2019-08-05 22:11:26 +08:00
|
|
|
"/usr/share/seabios/bios-256k.bin:NULL",
|
2019-04-04 21:52:53 +08:00
|
|
|
VIR_DOMAIN_OS_DEF_FIRMWARE_BIOS);
|
|
|
|
DO_SUPPORTED_TEST("pc-q35-3.1", VIR_ARCH_X86_64, true,
|
2019-08-05 22:11:26 +08:00
|
|
|
"/usr/share/seabios/bios-256k.bin:NULL:"
|
|
|
|
"/usr/share/OVMF/OVMF_CODE.secboot.fd:/usr/share/OVMF/OVMF_VARS.secboot.fd:"
|
|
|
|
"/usr/share/OVMF/OVMF_CODE.fd:/usr/share/OVMF/OVMF_VARS.fd",
|
2019-04-04 21:52:53 +08:00
|
|
|
VIR_DOMAIN_OS_DEF_FIRMWARE_BIOS,
|
|
|
|
VIR_DOMAIN_OS_DEF_FIRMWARE_EFI);
|
|
|
|
DO_SUPPORTED_TEST("pc-q35-3.1", VIR_ARCH_I686, false,
|
2019-08-05 22:11:26 +08:00
|
|
|
"/usr/share/seabios/bios-256k.bin:NULL",
|
2019-04-04 21:52:53 +08:00
|
|
|
VIR_DOMAIN_OS_DEF_FIRMWARE_BIOS);
|
|
|
|
DO_SUPPORTED_TEST("virt-3.1", VIR_ARCH_AARCH64, false,
|
2019-08-05 22:11:26 +08:00
|
|
|
"/usr/share/AAVMF/AAVMF_CODE.fd:/usr/share/AAVMF/AAVMF_VARS.fd",
|
2019-04-04 21:52:53 +08:00
|
|
|
VIR_DOMAIN_OS_DEF_FIRMWARE_EFI);
|
|
|
|
|
2019-03-13 16:56:47 +08:00
|
|
|
virFileWrapperClearPrefixes();
|
|
|
|
|
2019-02-20 00:35:43 +08:00
|
|
|
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
VIR_TEST_MAIN(mymain)
|