[libvirt] [PATCH 0/5] qemu: Use FW descriptors to report FW image paths

It feels a bit odd to report a built in list of FW images when we have FW descriptor files. Especially, when some weird architectures are concerned. For instance, OVMF_CODE.fd is reported even for non-x86_64/non-i386 arches, like ppc. But if FW descriptor files are taken into the picture then no OVMF_CODE.fd is ever reported. One can argue, that these patches are not necessary, because the whole point of FW descriptor files is that users do not have to bother with paths to FW images. And that is true. However, the whole ecosystem of FW descriptor files allows sys admins and regular users to write their own FW descriptor files and thus reporting what paths libvirt found might come handy when writing those descriptors. Michal Prívozník (5): virfirmware: Expose and define autoptr for virFirmwareFree qemu_firmware: Document qemuFirmwareGetSupported qemu_firmware: Extend qemuFirmwareGetSupported to return FW paths qemufirmwaretest: Test FW path getting through qemuFirmwareGetSupported() qemu: Use FW descriptors to report FW image paths src/libvirt_private.syms | 1 + src/qemu/qemu_capabilities.c | 19 +++++++-- src/qemu/qemu_firmware.c | 81 +++++++++++++++++++++++++++++++++++- src/qemu/qemu_firmware.h | 5 ++- src/util/virfirmware.c | 2 +- src/util/virfirmware.h | 5 +++ tests/qemufirmwaretest.c | 78 ++++++++++++++++++++++++++++++---- 7 files changed, 177 insertions(+), 14 deletions(-) -- 2.21.0

This function frees a _virFirmware struct. So far, it doesn't need to be called from outside of the module, but this will change shortly. In the light of recent VIR_DEFINE_AUTOPTR_FUNC() additions, do the same to virFirmwareFree(). Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/libvirt_private.syms | 1 + src/util/virfirmware.c | 2 +- src/util/virfirmware.h | 5 +++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index c323f679b3..c66161496e 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2036,6 +2036,7 @@ virFirewallDZoneExists; # util/virfirmware.h +virFirmwareFree; virFirmwareFreeList; virFirmwareParse; virFirmwareParseList; diff --git a/src/util/virfirmware.c b/src/util/virfirmware.c index f41e000447..b4747bd346 100644 --- a/src/util/virfirmware.c +++ b/src/util/virfirmware.c @@ -31,7 +31,7 @@ VIR_LOG_INIT("util.firmware"); -static void +void virFirmwareFree(virFirmwarePtr firmware) { if (!firmware) diff --git a/src/util/virfirmware.h b/src/util/virfirmware.h index ed59f34102..30bcd21fa4 100644 --- a/src/util/virfirmware.h +++ b/src/util/virfirmware.h @@ -31,6 +31,11 @@ struct _virFirmware { }; +void +virFirmwareFree(virFirmwarePtr firmware); + +VIR_DEFINE_AUTOPTR_FUNC(virFirmware, virFirmwareFree); + void virFirmwareFreeList(virFirmwarePtr *firmwares, size_t nfirmwares); -- 2.21.0

This function is going to get some new arguments. Document the current ones for clarity. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_firmware.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/qemu/qemu_firmware.c b/src/qemu/qemu_firmware.c index bf29b10b9a..8fbe8952ba 100644 --- a/src/qemu/qemu_firmware.c +++ b/src/qemu/qemu_firmware.c @@ -1413,6 +1413,25 @@ qemuFirmwareFillDomain(virQEMUDriverPtr driver, } +/** + * qemuFirmwareGetSupported: + * @machine: machine type + * @arch: architecture + * @privileged: whether running as privileged user + * @supported: returned bitmap of supported interfaces + * @secure: true if at least one secure boot enabled FW was found + * + * Parse all FW descriptors (depending whether running as @privileged this may + * or may not include user's $HOME) and for given combination of @machine and + * @arch extract information to be later reported in domain capabilities. + * The @supported contains a bitmap of found interfaces (and ORed values of 1 + * << VIR_DOMAIN_OS_DEF_FIRMWARE_*). Then, @supported is true if at least one + * FW descriptor signalizes secure boot (although, this is checked against SMM + * rather than SECURE_BOOT because reasons). + * + * Returns: 0 on success, + * -1 otherwise. + */ int qemuFirmwareGetSupported(const char *machine, virArch arch, -- 2.21.0

The qemuFirmwareGetSupported() function is called from qemu driver to generate domain capabilities XML based on FW descriptor files. However, the function currently reports only some features from domcapabilities XML and not actual FW image paths. The paths reported in the domcapabilities XML still from from pre-FW descriptor era and therefore the XML might be a bit confusing. For instance, it may say that secure boot is supported but secboot enabled FW is not in the listed FW image paths. To resolve this problem, change qemuFirmwareGetSupported() so that it also returns a list of FW images (we have the list anyway). Luckily, we already have a structure to represent a FW image - virFirmware. Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1733940 Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_capabilities.c | 3 +- src/qemu/qemu_firmware.c | 62 +++++++++++++++++++++++++++++++++++- src/qemu/qemu_firmware.h | 5 ++- tests/qemufirmwaretest.c | 2 +- 4 files changed, 68 insertions(+), 4 deletions(-) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 2388f145af..db1fc31cd9 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -5165,7 +5165,8 @@ virQEMUCapsFillDomainOSCaps(virDomainCapsOSPtr os, os->supported = VIR_TRISTATE_BOOL_YES; os->firmware.report = true; - if (qemuFirmwareGetSupported(machine, arch, privileged, &autoFirmwares, &secure) < 0) + if (qemuFirmwareGetSupported(machine, arch, privileged, + &autoFirmwares, &secure, NULL, NULL) < 0) return -1; if (autoFirmwares & (1ULL << VIR_DOMAIN_OS_DEF_FIRMWARE_BIOS)) diff --git a/src/qemu/qemu_firmware.c b/src/qemu/qemu_firmware.c index 8fbe8952ba..9ba38477cc 100644 --- a/src/qemu/qemu_firmware.c +++ b/src/qemu/qemu_firmware.c @@ -1420,6 +1420,8 @@ qemuFirmwareFillDomain(virQEMUDriverPtr driver, * @privileged: whether running as privileged user * @supported: returned bitmap of supported interfaces * @secure: true if at least one secure boot enabled FW was found + * @fws: (optional) list of found firmwares + * @nfws: (optional) number of members in @fws * * Parse all FW descriptors (depending whether running as @privileged this may * or may not include user's $HOME) and for given combination of @machine and @@ -1429,6 +1431,15 @@ qemuFirmwareFillDomain(virQEMUDriverPtr driver, * FW descriptor signalizes secure boot (although, this is checked against SMM * rather than SECURE_BOOT because reasons). * + * If @fws and @nfws are not NULL, then @fws is allocated (must be freed by + * caller when no longer needed) and contains list of firmwares found in form + * of virFirmware. This can be useful if caller wants to know the paths to + * firmware images (e.g. to present them in domain capabilities XML). + * Moreover, to allow the caller distinguish between no FW descriptors found + * and no matching FW descriptors found (nfws == 0 in both cases), the @fws is + * going to be allocated in case of the latter anyway (with no real content + * though). + * * Returns: 0 on success, * -1 otherwise. */ @@ -1437,7 +1448,9 @@ qemuFirmwareGetSupported(const char *machine, virArch arch, bool privileged, uint64_t *supported, - bool *secure) + bool *secure, + virFirmwarePtr **fws, + size_t *nfws) { qemuFirmwarePtr *firmwares = NULL; ssize_t nfirmwares = 0; @@ -1446,12 +1459,21 @@ qemuFirmwareGetSupported(const char *machine, *supported = VIR_DOMAIN_OS_DEF_FIRMWARE_NONE; *secure = false; + if (fws) { + *fws = NULL; + *nfws = 0; + } + if ((nfirmwares = qemuFirmwareFetchParsedConfigs(privileged, &firmwares, NULL)) < 0) return -1; for (i = 0; i < nfirmwares; i++) { qemuFirmwarePtr fw = firmwares[i]; + const qemuFirmwareMappingFlash *flash = &fw->mapping.data.flash; + const qemuFirmwareMappingMemory *memory = &fw->mapping.data.memory; + const char *fwpath = NULL; + const char *nvrampath = NULL; size_t j; if (!qemuFirmwareMatchesMachineArch(fw, machine, arch)) @@ -1491,8 +1513,46 @@ qemuFirmwareGetSupported(const char *machine, break; } } + + switch (fw->mapping.device) { + case QEMU_FIRMWARE_DEVICE_FLASH: + fwpath = flash->executable.filename; + nvrampath = flash->nvram_template.filename; + break; + + case QEMU_FIRMWARE_DEVICE_MEMORY: + fwpath = memory->filename; + break; + + case QEMU_FIRMWARE_DEVICE_KERNEL: + case QEMU_FIRMWARE_DEVICE_NONE: + case QEMU_FIRMWARE_DEVICE_LAST: + break; + } + + if (fws && fwpath) { + VIR_AUTOPTR(virFirmware) tmp = NULL; + + /* Append only unique pairs. */ + for (j = 0; j < *nfws; j++) { + if (STREQ((*fws)[j]->name, fwpath) && + STREQ_NULLABLE((*fws)[j]->nvram, nvrampath)) + break; + } + + if (j == *nfws && + (VIR_ALLOC(tmp) < 0 || + VIR_STRDUP(tmp->name, fwpath) < 0 || + VIR_STRDUP(tmp->nvram, nvrampath) < 0 || + VIR_APPEND_ELEMENT(*fws, *nfws, tmp) < 0)) + return -1; + } } + if (fws && !*fws && nfirmwares && + VIR_REALLOC_N(*fws, 0) < 0) + return -1; + for (i = 0; i < nfirmwares; i++) qemuFirmwareFree(firmwares[i]); VIR_FREE(firmwares); diff --git a/src/qemu/qemu_firmware.h b/src/qemu/qemu_firmware.h index 6a3b6061f4..28e8322ec9 100644 --- a/src/qemu/qemu_firmware.h +++ b/src/qemu/qemu_firmware.h @@ -24,6 +24,7 @@ #include "qemu_conf.h" #include "virautoclean.h" #include "virarch.h" +#include "virfirmware.h" typedef struct _qemuFirmware qemuFirmware; typedef qemuFirmware *qemuFirmwarePtr; @@ -53,6 +54,8 @@ qemuFirmwareGetSupported(const char *machine, virArch arch, bool privileged, uint64_t *supported, - bool *secure); + bool *secure, + virFirmwarePtr **fws, + size_t *nfws); verify(VIR_DOMAIN_OS_DEF_FIRMWARE_LAST <= 64); diff --git a/tests/qemufirmwaretest.c b/tests/qemufirmwaretest.c index 2228359a7b..bab23f696e 100644 --- a/tests/qemufirmwaretest.c +++ b/tests/qemufirmwaretest.c @@ -123,7 +123,7 @@ testSupportedFW(const void *opaque) expectedInterfaces |= 1ULL << data->interfaces[i]; if (qemuFirmwareGetSupported(data->machine, data->arch, false, - &actualInterfaces, &actualSecure) < 0) { + &actualInterfaces, &actualSecure, NULL, NULL) < 0) { fprintf(stderr, "Unable to get list of supported interfaces\n"); return -1; } -- 2.21.0

On 8/5/19 12:14 PM, Michal Privoznik wrote:
The qemuFirmwareGetSupported() function is called from qemu driver to generate domain capabilities XML based on FW descriptor files. However, the function currently reports only some features from domcapabilities XML and not actual FW image paths. The paths reported in the domcapabilities XML still from from pre-FW
s/still from from/are still from/ - Cole

There is one hack hidden here, but since this is in a test, it's okay. In order to get a list of expected firmwares in virFirmwarePtr form I'm using virFirmwareParseList(). But usually, in real life scenario, this function is used only to parse a list of UEFI images which have NVRAM split out. In other words, this function expects ${FW}:${NVRAM} pairs. But in this test, we also want to allow just a single path: ${FW} because some reported firmwares are just a BIOS image really. To avoid writing some parser function, let's just pass "NULL" as ${NVRAM} and fix the result later. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- tests/qemufirmwaretest.c | 78 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 71 insertions(+), 7 deletions(-) diff --git a/tests/qemufirmwaretest.c b/tests/qemufirmwaretest.c index bab23f696e..653476fdc1 100644 --- a/tests/qemufirmwaretest.c +++ b/tests/qemufirmwaretest.c @@ -105,6 +105,7 @@ struct supportedData { const char *machine; virArch arch; bool secure; + const char *fwlist; unsigned int *interfaces; size_t ninterfaces; }; @@ -117,15 +118,35 @@ testSupportedFW(const void *opaque) uint64_t actualInterfaces; uint64_t expectedInterfaces = 0; bool actualSecure; + virFirmwarePtr *expFWs = NULL; + size_t nexpFWs = 0; + virFirmwarePtr *actFWs = NULL; + size_t nactFWs = 0; size_t i; + int ret = -1; for (i = 0; i < data->ninterfaces; i++) expectedInterfaces |= 1ULL << data->interfaces[i]; + 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); + } + if (qemuFirmwareGetSupported(data->machine, data->arch, false, - &actualInterfaces, &actualSecure, NULL, NULL) < 0) { + &actualInterfaces, &actualSecure, &actFWs, &nactFWs) < 0) { fprintf(stderr, "Unable to get list of supported interfaces\n"); - return -1; + goto cleanup; } if (actualInterfaces != expectedInterfaces) { @@ -133,7 +154,7 @@ testSupportedFW(const void *opaque) "Mismatch in supported interfaces. " "Expected 0x%" PRIx64 " got 0x%" PRIx64 "\n", expectedInterfaces, actualInterfaces); - return -1; + goto cleanup; } if (actualSecure != data->secure) { @@ -141,10 +162,42 @@ testSupportedFW(const void *opaque) "Mismatch in SMM requirement/support. " "Expected %d got %d\n", data->secure, actualSecure); - return -1; + goto cleanup; } - return 0; + 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; } @@ -176,10 +229,13 @@ mymain(void) if (virTestRun("QEMU FW precedence test", testFWPrecedence, NULL) < 0) ret = -1; -#define DO_SUPPORTED_TEST(machine, arch, secure, ...) \ + /* 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, ...) \ do { \ unsigned int interfaces[] = {__VA_ARGS__}; \ - struct supportedData data = {machine, arch, secure, \ + struct supportedData data = {machine, arch, secure, fwlist, \ interfaces, ARRAY_CARDINALITY(interfaces)}; \ if (virTestRun("QEMU FW SUPPORTED " machine " " #arch, \ testSupportedFW, &data) < 0) \ @@ -187,16 +243,24 @@ mymain(void) } while (0) DO_SUPPORTED_TEST("pc-i440fx-3.1", VIR_ARCH_X86_64, false, + "/usr/share/seabios/bios-256k.bin:NULL:" + "/usr/share/OVMF/OVMF_CODE.fd:/usr/share/OVMF/OVMF_VARS.fd", VIR_DOMAIN_OS_DEF_FIRMWARE_BIOS, VIR_DOMAIN_OS_DEF_FIRMWARE_EFI); DO_SUPPORTED_TEST("pc-i440fx-3.1", VIR_ARCH_I686, false, + "/usr/share/seabios/bios-256k.bin:NULL", VIR_DOMAIN_OS_DEF_FIRMWARE_BIOS); DO_SUPPORTED_TEST("pc-q35-3.1", VIR_ARCH_X86_64, true, + "/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", VIR_DOMAIN_OS_DEF_FIRMWARE_BIOS, VIR_DOMAIN_OS_DEF_FIRMWARE_EFI); DO_SUPPORTED_TEST("pc-q35-3.1", VIR_ARCH_I686, false, + "/usr/share/seabios/bios-256k.bin:NULL", VIR_DOMAIN_OS_DEF_FIRMWARE_BIOS); DO_SUPPORTED_TEST("virt-3.1", VIR_ARCH_AARCH64, false, + "/usr/share/AAVMF/AAVMF_CODE.fd:/usr/share/AAVMF/AAVMF_VARS.fd", VIR_DOMAIN_OS_DEF_FIRMWARE_EFI); virFileWrapperClearPrefixes(); -- 2.21.0

Now that we have qemuFirmwareGetSupported() so that it also returns a list of FW image paths, we can use it to report them in domain capabilities instead of the old time default list. Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1733940 Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_capabilities.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index db1fc31cd9..318cfcebf3 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -5161,12 +5161,16 @@ virQEMUCapsFillDomainOSCaps(virDomainCapsOSPtr os, virDomainCapsLoaderPtr capsLoader = &os->loader; uint64_t autoFirmwares = 0; bool secure = false; + virFirmwarePtr *firmwaresAlt = NULL; + size_t nfirmwaresAlt = 0; + int ret = -1; os->supported = VIR_TRISTATE_BOOL_YES; os->firmware.report = true; if (qemuFirmwareGetSupported(machine, arch, privileged, - &autoFirmwares, &secure, NULL, NULL) < 0) + &autoFirmwares, &secure, + &firmwaresAlt, &nfirmwaresAlt) < 0) return -1; if (autoFirmwares & (1ULL << VIR_DOMAIN_OS_DEF_FIRMWARE_BIOS)) @@ -5174,9 +5178,15 @@ virQEMUCapsFillDomainOSCaps(virDomainCapsOSPtr os, if (autoFirmwares & (1ULL << VIR_DOMAIN_OS_DEF_FIRMWARE_EFI)) VIR_DOMAIN_CAPS_ENUM_SET(os->firmware, VIR_DOMAIN_OS_DEF_FIRMWARE_EFI); - if (virQEMUCapsFillDomainLoaderCaps(capsLoader, secure, firmwares, nfirmwares) < 0) - return -1; - return 0; + if (virQEMUCapsFillDomainLoaderCaps(capsLoader, secure, + firmwaresAlt ? firmwaresAlt : firmwares, + firmwaresAlt ? nfirmwaresAlt : nfirmwares) < 0) + goto cleanup; + + ret = 0; + cleanup: + virFirmwareFreeList(firmwaresAlt, nfirmwaresAlt); + return ret; } -- 2.21.0

On 8/5/19 12:14 PM, Michal Privoznik wrote:
It feels a bit odd to report a built in list of FW images when we have FW descriptor files. Especially, when some weird architectures are concerned. For instance, OVMF_CODE.fd is reported even for non-x86_64/non-i386 arches, like ppc. But if FW descriptor files are taken into the picture then no OVMF_CODE.fd is ever reported.
One can argue, that these patches are not necessary, because the whole point of FW descriptor files is that users do not have to bother with paths to FW images. And that is true. However, the whole ecosystem of FW descriptor files allows sys admins and regular users to write their own FW descriptor files and thus reporting what paths libvirt found might come handy when writing those descriptors.
Michal Prívozník (5): virfirmware: Expose and define autoptr for virFirmwareFree qemu_firmware: Document qemuFirmwareGetSupported qemu_firmware: Extend qemuFirmwareGetSupported to return FW paths qemufirmwaretest: Test FW path getting through qemuFirmwareGetSupported() qemu: Use FW descriptors to report FW image paths
src/libvirt_private.syms | 1 + src/qemu/qemu_capabilities.c | 19 +++++++-- src/qemu/qemu_firmware.c | 81 +++++++++++++++++++++++++++++++++++- src/qemu/qemu_firmware.h | 5 ++- src/util/virfirmware.c | 2 +- src/util/virfirmware.h | 5 +++ tests/qemufirmwaretest.c | 78 ++++++++++++++++++++++++++++++---- 7 files changed, 177 insertions(+), 14 deletions(-)
Reviewed-by: Cole Robinson <crobinso@redhat.com> One problem with the output though, but I think it can be fixed as a follow on. $ sudo ./tools/virsh domcapabilities --machine q35 ... <loader supported='yes'> <value>/usr/share/edk2/ovmf/OVMF_CODE.secboot.fd</value> <value>/usr/share/edk2/ovmf/OVMF_CODE.secboot.fd</value> <value>/usr/share/edk2/ovmf/OVMF_CODE.fd</value> <enum name='type'> <value>rom</value> <value>pflash</value> </enum> <enum name='readonly'> <value>yes</value> <value>no</value> </enum> <enum name='secure'> <value>yes</value> <value>no</value> </enum> </loader> Notice the double secboot listing. This is on f31 with stock packages. Probably due to 50-edk2-ovmf-x64-sb.json and 40-edk2-ovmf-x64-sb-enrolled.json using the same firmware path. I guess dupes should be filtered out? Thanks, Cole

On Mon, Aug 05, 2019 at 06:14:20PM +0200, Michal Privoznik wrote: [...]
Michal Prívozník (5): virfirmware: Expose and define autoptr for virFirmwareFree qemu_firmware: Document qemuFirmwareGetSupported qemu_firmware: Extend qemuFirmwareGetSupported to return FW paths qemufirmwaretest: Test FW path getting through qemuFirmwareGetSupported() qemu: Use FW descriptors to report FW image paths
[...] Tested-by: Kashyap Chamarthy <kchamart@redhat.com> I've just tested this patchset on Fedora 30. (I too can reproduce the behaviour Cole saw - duplicate 'secboot' binaries.) Build libvirt with this: $> git describe v5.7.0-107-gb6e6d35f3f Stop the system libvirt daemons: $> systemctl stop libvirtd virtlockd virtlogd Start the daemons built from Git: $> sudo ./run src/virtlockd & $> sudo ./run src/virtlogd & $> sudo ./run src/libvirtd & Make sure your EDK2/OVMF RPM has the 'secboot' binaries/VARS files: $> rpm -q edk2-ovmf edk2-ovmf-20190501stable-3.fc30.noarch $> rpm -ql edk2-ovmf | grep secboot /usr/share/OVMF/OVMF_CODE.secboot.fd /usr/share/OVMF/OVMF_VARS.secboot.fd /usr/share/edk2/ovmf/OVMF_CODE.secboot.fd /usr/share/edk2/ovmf/OVMF_VARS.secboot.fd (The top two files are a symlink to the bottom two.) Before invoking domCapabilities API, ensure the relevant firmware descriptor files for x86_64 have the secboot binary listed: $> grep CODE.secboot /usr/share/qemu/firmware/40-edk2-ovmf-x64-sb-enrolled.json /usr/share/qemu/firmware/50-edk2-ovmf-x64-sb.json /usr/share/qemu/firmware/40-edk2-ovmf-x64-sb-enrolled.json: "filename": "/usr/share/edk2/ovmf/OVMF_CODE.secboot.fd", /usr/share/qemu/firmware/50-edk2-ovmf-x64-sb.json: "filename": "/usr/share/edk2/ovmf/OVMF_CODE.secboot.fd", Now run 'domcapabilities' (with 'q35'): $> sudo tools/virsh domcapabilities --machine q35 --arch x86_64 [...] <os supported='yes'> <enum name='firmware'> <value>bios</value> <value>efi</value> </enum> <loader supported='yes'> <value>/usr/share/edk2/ovmf/OVMF_CODE.secboot.fd</value> <value>/usr/share/edk2/ovmf/OVMF_CODE.secboot.fd</value> <value>/usr/share/edk2/ovmf/OVMF_CODE.fd</value> <value>/usr/share/edk2.git/ovmf-x64/OVMF_CODE-pure-efi.fd</value> <value>/usr/share/edk2.git/ovmf-x64/OVMF_CODE-with-csm.fd</value> <enum name='type'> <value>rom</value> <value>pflash</value> </enum> <enum name='readonly'> <value>yes</value> <value>no</value> </enum> <enum name='secure'> <value>yes</value> <value>no</value> </enum> </loader> </os> [...] -- /kashyap

On 9/13/19 12:25 PM, Kashyap Chamarthy wrote:
On Mon, Aug 05, 2019 at 06:14:20PM +0200, Michal Privoznik wrote:
[...]
Michal Prívozník (5): virfirmware: Expose and define autoptr for virFirmwareFree qemu_firmware: Document qemuFirmwareGetSupported qemu_firmware: Extend qemuFirmwareGetSupported to return FW paths qemufirmwaretest: Test FW path getting through qemuFirmwareGetSupported() qemu: Use FW descriptors to report FW image paths
[...]
Tested-by: Kashyap Chamarthy <kchamart@redhat.com>
Thanks, but I've pushed it yesterday and thus I can no longer add you to the commit messages. Sorry.
I've just tested this patchset on Fedora 30. (I too can reproduce the behaviour Cole saw - duplicate 'secboot' binaries.)
Build libvirt with this:
$> git describe v5.7.0-107-gb6e6d35f3f
Stop the system libvirt daemons:
$> systemctl stop libvirtd virtlockd virtlogd
Start the daemons built from Git:
$> sudo ./run src/virtlockd & $> sudo ./run src/virtlogd & $> sudo ./run src/libvirtd &
Make sure your EDK2/OVMF RPM has the 'secboot' binaries/VARS files:
$> rpm -q edk2-ovmf edk2-ovmf-20190501stable-3.fc30.noarch
$> rpm -ql edk2-ovmf | grep secboot /usr/share/OVMF/OVMF_CODE.secboot.fd /usr/share/OVMF/OVMF_VARS.secboot.fd /usr/share/edk2/ovmf/OVMF_CODE.secboot.fd /usr/share/edk2/ovmf/OVMF_VARS.secboot.fd
(The top two files are a symlink to the bottom two.)
Before invoking domCapabilities API, ensure the relevant firmware descriptor files for x86_64 have the secboot binary listed:
$> grep CODE.secboot /usr/share/qemu/firmware/40-edk2-ovmf-x64-sb-enrolled.json /usr/share/qemu/firmware/50-edk2-ovmf-x64-sb.json /usr/share/qemu/firmware/40-edk2-ovmf-x64-sb-enrolled.json: "filename": "/usr/share/edk2/ovmf/OVMF_CODE.secboot.fd", /usr/share/qemu/firmware/50-edk2-ovmf-x64-sb.json: "filename": "/usr/share/edk2/ovmf/OVMF_CODE.secboot.fd",
Yeah, I've posted a patch for that: https://www.redhat.com/archives/libvir-list/2019-September/msg00485.html The reason is that (I suspect) 40-edk2-ovmf-x64-sb-enrolled.json and 50-edk2-ovmf-x64-sb.json have the same _CODE but different _VARS. Therefore, in qemuFirmwareGetSupported() (which is called when constructing domcaps) we effectivelly see two different firmwares: https://libvirt.org/git/?p=libvirt.git;a=blob;f=src/qemu/qemu_firmware.c;h=f... Therefore, they are appended onto @fws list which is then passed to virQEMUCapsFillDomainLoaderCaps() in virQEMUCapsFillDomainOSCaps() which doesn't deduplicate them. But the same thing can happen even without FW descriptors: ./configure --with-loader-nvram="/dev/null:X:/dev/null:Y" (a very dumb config, but serves the point) which declares [{CODE = "/dev/null", NVRAM = "X"}, {CODE = "/dev/null", NVRAM "Y"}] pair. In both cases the FW image is the same and therefore it would be printed twice. To test this with latest git just revert e9d51a221c1871 because now libvirt picks FW descriptors and thus configure arg (or correspondinf qemu.conf knob) is ignrored. Michal
participants (3)
-
Cole Robinson
-
Kashyap Chamarthy
-
Michal Privoznik