[PATCH] bhyve: add <os firmware='efi'> support

Implement "<os firmware='efi'>" support for bhyve driver. As there are not really lot of options, try to find "BHYVE_UEFI.fd" firmware which is installed by the sysutils/uefi-edk2-bhyve FreeBSD port. If not found, just use the first found firmware in the firmwares directory (which is configurable via config file). Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- Not extremely happy about the LIBVIRT_BHYVE_FIRMWARE_DIR_OVERRIDE knob, but not sure how to test this otherwise. po/POTFILES.in | 1 + src/bhyve/bhyve_domain.c | 5 + src/bhyve/bhyve_firmware.c | 91 +++++++++++++++++++ src/bhyve/bhyve_firmware.h | 30 ++++++ src/bhyve/bhyve_process.c | 15 +++ src/bhyve/bhyve_process.h | 5 + src/bhyve/meson.build | 1 + .../bhyvexml2argv-firmware-efi.args | 11 +++ .../bhyvexml2argv-firmware-efi.ldargs | 1 + .../bhyvexml2argv-firmware-efi.xml | 22 +++++ tests/bhyvexml2argvtest.c | 83 ++++++++++++++--- 11 files changed, 254 insertions(+), 11 deletions(-) create mode 100644 src/bhyve/bhyve_firmware.c create mode 100644 src/bhyve/bhyve_firmware.h create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-firmware-efi.args create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-firmware-efi.ldargs create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-firmware-efi.xml diff --git a/po/POTFILES.in b/po/POTFILES.in index 80c5f145be..413783ee35 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -14,6 +14,7 @@ @SRCDIR@src/bhyve/bhyve_command.c @SRCDIR@src/bhyve/bhyve_domain.c @SRCDIR@src/bhyve/bhyve_driver.c +@SRCDIR@src/bhyve/bhyve_firmware.c @SRCDIR@src/bhyve/bhyve_monitor.c @SRCDIR@src/bhyve/bhyve_parse_command.c @SRCDIR@src/bhyve/bhyve_process.c diff --git a/src/bhyve/bhyve_domain.c b/src/bhyve/bhyve_domain.c index 8fbc554a0a..209e4d3905 100644 --- a/src/bhyve/bhyve_domain.c +++ b/src/bhyve/bhyve_domain.c @@ -64,6 +64,9 @@ bhyveDomainDefNeedsISAController(virDomainDefPtr def) if (def->os.bootloader == NULL && def->os.loader) return true; + if (def->os.firmware == VIR_DOMAIN_OS_DEF_FIRMWARE_EFI) + return true; + if (def->nserials || def->nconsoles) return true; @@ -230,6 +233,8 @@ virDomainDefParserConfig virBhyveDriverDomainDefParserConfig = { .domainPostParseCallback = bhyveDomainDefPostParse, .assignAddressesCallback = bhyveDomainDefAssignAddresses, .deviceValidateCallback = bhyveDomainDeviceDefValidate, + + .features = VIR_DOMAIN_DEF_FEATURE_FW_AUTOSELECT, }; static void diff --git a/src/bhyve/bhyve_firmware.c b/src/bhyve/bhyve_firmware.c new file mode 100644 index 0000000000..ccc3a5ffc8 --- /dev/null +++ b/src/bhyve/bhyve_firmware.c @@ -0,0 +1,91 @@ +/* + * bhyve_firmware.c: bhyve firmware management + * + * Copyright (C) 2021 Roman Bogorodskiy + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#include <config.h> +#include <dirent.h> + +#include "viralloc.h" +#include "virlog.h" +#include "virfile.h" +#include "bhyve_conf.h" +#include "bhyve_firmware.h" + +#define VIR_FROM_THIS VIR_FROM_BHYVE + +VIR_LOG_INIT("bhyve.bhyve_firmware"); + + +#define BHYVE_DEFAULT_FIRMWARE "BHYVE_UEFI.fd" + +int +bhyveFirmwareFillDomain(bhyveConnPtr driver, + virDomainDefPtr def, + unsigned int flags) +{ + g_autoptr(DIR) dir = NULL; + virBhyveDriverConfigPtr cfg = virBhyveDriverGetConfig(driver); + const char *firmware_dir_cfg = cfg->firmwareDir; + const char *firmware_dir_env = NULL, *firmware_dir = NULL; + struct dirent *entry; + char *matching_firmware = NULL; + char *first_found = NULL; + + virCheckFlags(0, -1); + + if (def->os.firmware == VIR_DOMAIN_OS_DEF_FIRMWARE_NONE) + return 0; + + if (virDirOpenIfExists(&dir, firmware_dir_cfg) > 0) { + while ((virDirRead(dir, &entry, firmware_dir)) > 0) { + if (STREQ(entry->d_name, BHYVE_DEFAULT_FIRMWARE)) { + matching_firmware = g_strdup(entry->d_name); + break; + } + if (!first_found) + first_found = g_strdup(entry->d_name); + } + } + + if (!matching_firmware) { + if (!first_found) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("no firmwares found in %s"), + firmware_dir_cfg); + return -1; + } else { + matching_firmware = first_found; + } + } + + if (!def->os.loader) + def->os.loader = g_new0(virDomainLoaderDef, 1); + + def->os.loader->type = VIR_DOMAIN_LOADER_TYPE_PFLASH; + def->os.loader->readonly = VIR_TRISTATE_BOOL_YES; + + VIR_FREE(def->os.loader->path); + + firmware_dir_env = g_getenv("LIBVIRT_BHYVE_FIRMWARE_DIR_OVERRIDE"); + firmware_dir = firmware_dir_env ? firmware_dir_env : firmware_dir_cfg; + def->os.loader->path = g_build_filename(firmware_dir, matching_firmware, NULL); + + return 0; +} diff --git a/src/bhyve/bhyve_firmware.h b/src/bhyve/bhyve_firmware.h new file mode 100644 index 0000000000..ae4bc98676 --- /dev/null +++ b/src/bhyve/bhyve_firmware.h @@ -0,0 +1,30 @@ +/* + * bhyve_firmware.h: bhyve firmware management + * + * Copyright (C) 2021 Roman Bogorodskiy + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#pragma once + +#include "domain_conf.h" +#include "bhyve_utils.h" + +int +bhyveFirmwareFillDomain(bhyveConnPtr driver, + virDomainDefPtr def, + unsigned int flags); diff --git a/src/bhyve/bhyve_process.c b/src/bhyve/bhyve_process.c index 060018bc70..0cfe69688c 100644 --- a/src/bhyve/bhyve_process.c +++ b/src/bhyve/bhyve_process.c @@ -33,6 +33,7 @@ #include "bhyve_device.h" #include "bhyve_driver.h" #include "bhyve_command.h" +#include "bhyve_firmware.h" #include "bhyve_monitor.h" #include "bhyve_process.h" #include "datatypes.h" @@ -251,6 +252,17 @@ virBhyveProcessStartImpl(bhyveConnPtr driver, return ret; } +int +bhyveProcessPrepareDomain(bhyveConnPtr driver, + virDomainObjPtr vm, + unsigned int flags) +{ + if (bhyveFirmwareFillDomain(driver, vm->def, flags) < 0) + return -1; + + return 0; +} + int virBhyveProcessStart(virConnectPtr conn, virDomainObjPtr vm, @@ -268,6 +280,9 @@ virBhyveProcessStart(virConnectPtr conn, conn, bhyveProcessAutoDestroy) < 0) return -1; + if (bhyveProcessPrepareDomain(driver, vm, flags) < 0) + return -1; + return virBhyveProcessStartImpl(driver, vm, reason); } diff --git a/src/bhyve/bhyve_process.h b/src/bhyve/bhyve_process.h index d7b4e0bd4e..133863c1e0 100644 --- a/src/bhyve/bhyve_process.h +++ b/src/bhyve/bhyve_process.h @@ -23,6 +23,11 @@ #include "bhyve_utils.h" +int +bhyveProcessPrepareDomain(bhyveConnPtr driver, + virDomainObjPtr vm, + unsigned int flags); + int virBhyveProcessStart(virConnectPtr conn, virDomainObjPtr vm, virDomainRunningReason reason, diff --git a/src/bhyve/meson.build b/src/bhyve/meson.build index 2b65eecf5e..b3551477b7 100644 --- a/src/bhyve/meson.build +++ b/src/bhyve/meson.build @@ -2,6 +2,7 @@ bhyve_sources = files( 'bhyve_capabilities.c', 'bhyve_command.c', 'bhyve_conf.c', + 'bhyve_firmware.c', 'bhyve_parse_command.c', 'bhyve_device.c', 'bhyve_domain.c', diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-firmware-efi.args b/tests/bhyvexml2argvdata/bhyvexml2argv-firmware-efi.args new file mode 100644 index 0000000000..40bc84ef27 --- /dev/null +++ b/tests/bhyvexml2argvdata/bhyvexml2argv-firmware-efi.args @@ -0,0 +1,11 @@ +/usr/sbin/bhyve \ +-c 1 \ +-m 214 \ +-u \ +-H \ +-P \ +-s 0:0,hostbridge \ +-l bootrom,test_firmware_dir/BHYVE_UEFI.fd \ +-s 1:0,lpc \ +-s 2:0,ahci,hd:/tmp/freebsd.img \ +-s 3:0,virtio-net,faketapdev,mac=52:54:00:00:00:00 bhyve diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-firmware-efi.ldargs b/tests/bhyvexml2argvdata/bhyvexml2argv-firmware-efi.ldargs new file mode 100644 index 0000000000..421376db9e --- /dev/null +++ b/tests/bhyvexml2argvdata/bhyvexml2argv-firmware-efi.ldargs @@ -0,0 +1 @@ +dummy diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-firmware-efi.xml b/tests/bhyvexml2argvdata/bhyvexml2argv-firmware-efi.xml new file mode 100644 index 0000000000..302326cb26 --- /dev/null +++ b/tests/bhyvexml2argvdata/bhyvexml2argv-firmware-efi.xml @@ -0,0 +1,22 @@ +<domain type='bhyve'> + <name>bhyve</name> + <uuid>df3be7e7-a104-11e3-aeb0-50e5492bd3dc</uuid> + <memory>219136</memory> + <vcpu>1</vcpu> + <os firmware='efi'> + <type>hvm</type> + </os> + <devices> + <disk type='file'> + <driver name='file' type='raw'/> + <source file='/tmp/freebsd.img'/> + <target dev='hda' bus='sata'/> + <address type='drive' controller='0' bus='0' target='2' unit='0'/> + </disk> + <interface type='bridge'> + <model type='virtio'/> + <source bridge="virbr0"/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </interface> + </devices> +</domain> diff --git a/tests/bhyvexml2argvtest.c b/tests/bhyvexml2argvtest.c index 197334f9c4..13b8e34c2b 100644 --- a/tests/bhyvexml2argvtest.c +++ b/tests/bhyvexml2argvtest.c @@ -7,17 +7,20 @@ # include "datatypes.h" # include "bhyve/bhyve_capabilities.h" +# include "bhyve/bhyve_conf.h" # include "bhyve/bhyve_domain.h" # include "bhyve/bhyve_utils.h" # include "bhyve/bhyve_command.h" +# include "bhyve/bhyve_process.h" # define VIR_FROM_THIS VIR_FROM_BHYVE static bhyveConn driver; typedef enum { - FLAG_EXPECT_FAILURE = 1 << 0, - FLAG_EXPECT_PARSE_ERROR = 1 << 1, + FLAG_EXPECT_FAILURE = 1 << 0, + FLAG_EXPECT_PARSE_ERROR = 1 << 1, + FLAG_EXPECT_PREPARE_ERROR = 1 << 2, } virBhyveXMLToArgvTestFlags; static int testCompareXMLToArgvFiles(const char *xml, @@ -29,7 +32,7 @@ static int testCompareXMLToArgvFiles(const char *xml, g_autofree char *actualargv = NULL; g_autofree char *actualld = NULL; g_autofree char *actualdm = NULL; - g_autoptr(virDomainDef) vmdef = NULL; + g_autoptr(virDomainObj) vm = NULL; g_autoptr(virCommand) cmd = NULL; g_autoptr(virCommand) ldcmd = NULL; g_autoptr(virConnect) conn = NULL; @@ -38,7 +41,10 @@ static int testCompareXMLToArgvFiles(const char *xml, if (!(conn = virGetConnect())) goto out; - if (!(vmdef = virDomainDefParseFile(xml, driver.xmlopt, + if (!(vm = virDomainObjNew(driver.xmlopt))) + return -1; + + if (!(vm->def = virDomainDefParseFile(xml, driver.xmlopt, NULL, VIR_DOMAIN_DEF_PARSE_INACTIVE))) { if (flags & FLAG_EXPECT_PARSE_ERROR) { ret = 0; @@ -54,11 +60,20 @@ static int testCompareXMLToArgvFiles(const char *xml, conn->privateData = &driver; - cmd = virBhyveProcessBuildBhyveCmd(&driver, vmdef, false); - if (vmdef->os.loader) + if (bhyveProcessPrepareDomain(&driver, vm, 0) < 0) { + if (flags & FLAG_EXPECT_PREPARE_ERROR) { + ret = 0; + VIR_TEST_DEBUG("Got expected error: %s", + virGetLastErrorMessage()); + } + goto out; + } + + cmd = virBhyveProcessBuildBhyveCmd(&driver, vm->def, false); + if (vm->def->os.loader) ldcmd = virCommandNew("dummy"); else - ldcmd = virBhyveProcessBuildLoadCmd(&driver, vmdef, "<device.map>", + ldcmd = virBhyveProcessBuildLoadCmd(&driver, vm->def, "<device.map>", &actualdm); if ((cmd == NULL) || (ldcmd == NULL)) { @@ -94,10 +109,10 @@ static int testCompareXMLToArgvFiles(const char *xml, ret = 0; out: - if (vmdef && - vmdef->ngraphics == 1 && - vmdef->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) - virPortAllocatorRelease(vmdef->graphics[0]->data.vnc.port); + if (vm && vm->def && + vm->def->ngraphics == 1 && + vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) + virPortAllocatorRelease(vm->def->graphics[0]->data.vnc.port); return ret; } @@ -128,10 +143,16 @@ testCompareXMLToArgvHelper(const void *data) return testCompareXMLToArgvFiles(xml, args, ldargs, dmargs, info->flags); } +# define FAKEFIRMWAREDIRTEMPLATE abs_builddir "/bhyvefakefirmwaredir-XXXXXX" + static int mymain(void) { int ret = 0; + g_autofree char *fakefirmwaredir = NULL; + g_autofree char *emptyfirmwaredir = NULL; + const char *firmwares[] = {"BHYVE_UEFI.fd", "BHYVE_UEFI_CSM.fd", "refind_x64.efi", NULL}; + size_t i; if ((driver.caps = virBhyveCapsBuild()) == NULL) return EXIT_FAILURE; @@ -142,6 +163,35 @@ mymain(void) if (!(driver.remotePorts = virPortAllocatorRangeNew("display", 5900, 65535))) return EXIT_FAILURE; + if (!(driver.config = virBhyveDriverConfigNew())) + return EXIT_FAILURE; + + fakefirmwaredir = g_strdup(FAKEFIRMWAREDIRTEMPLATE); + + if (!g_mkdtemp(fakefirmwaredir)) { + fprintf(stderr, "Cannot create fakefirmwaredir"); + abort(); + } + driver.config->firmwareDir = fakefirmwaredir; + + emptyfirmwaredir = g_strdup(FAKEFIRMWAREDIRTEMPLATE); + + if (!g_mkdtemp(emptyfirmwaredir)) { + fprintf(stderr, "Cannot create emptyfirmwaredir"); + abort(); + } + + i = 0; + while (firmwares[i]) { + g_autofree char *firmware_path = g_strdup_printf("%s/%s", fakefirmwaredir, firmwares[i++]); + + if (virFileTouch(firmware_path, 0600) < 0) { + fprintf(stderr, "Cannot create firmware file"); + abort(); + } + } + + g_setenv("LIBVIRT_BHYVE_FIRMWARE_DIR_OVERRIDE", "test_firmware_dir", TRUE); # define DO_TEST_FULL(name, flags) \ do { \ @@ -162,6 +212,9 @@ mymain(void) # define DO_TEST_PARSE_ERROR(name) \ DO_TEST_FULL(name, FLAG_EXPECT_PARSE_ERROR) +# define DO_TEST_PREPARE_ERROR(name) \ + DO_TEST_FULL(name, FLAG_EXPECT_PREPARE_ERROR) + driver.grubcaps = BHYVE_GRUB_CAP_CONSDEV; driver.bhyvecaps = BHYVE_CAP_RTC_UTC | BHYVE_CAP_AHCI32SLOT | \ BHYVE_CAP_NET_E1000 | BHYVE_CAP_LPC_BOOTROM | \ @@ -209,6 +262,10 @@ mymain(void) DO_TEST("sound"); DO_TEST("isa-controller"); DO_TEST_FAILURE("isa-multiple-controllers"); + DO_TEST("firmware-efi"); + g_unsetenv("LIBVIRT_BHYVE_FIRMWARE_DIR_OVERRIDE"); + driver.config->firmwareDir = emptyfirmwaredir; + DO_TEST_PREPARE_ERROR("firmware-efi"); DO_TEST("fs-9p"); DO_TEST("fs-9p-readonly"); DO_TEST_FAILURE("fs-9p-unsupported-type"); @@ -264,9 +321,13 @@ mymain(void) driver.bhyvecaps &= ~BHYVE_CAP_VNC_PASSWORD; DO_TEST_FAILURE("vnc-password"); + virFileDeleteTree(fakefirmwaredir); + virFileDeleteTree(emptyfirmwaredir); + virObjectUnref(driver.caps); virObjectUnref(driver.xmlopt); virPortAllocatorRangeFree(driver.remotePorts); + virObjectUnref(driver.config); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } -- 2.30.0

On 2/27/21 5:34 AM, Roman Bogorodskiy wrote:
Implement "<os firmware='efi'>" support for bhyve driver. As there are not really lot of options, try to find "BHYVE_UEFI.fd" firmware which is installed by the sysutils/uefi-edk2-bhyve FreeBSD port.
If not found, just use the first found firmware in the firmwares directory (which is configurable via config file).
Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- Not extremely happy about the LIBVIRT_BHYVE_FIRMWARE_DIR_OVERRIDE knob, but not sure how to test this otherwise.
po/POTFILES.in | 1 + src/bhyve/bhyve_domain.c | 5 + src/bhyve/bhyve_firmware.c | 91 +++++++++++++++++++ src/bhyve/bhyve_firmware.h | 30 ++++++ src/bhyve/bhyve_process.c | 15 +++ src/bhyve/bhyve_process.h | 5 + src/bhyve/meson.build | 1 + .../bhyvexml2argv-firmware-efi.args | 11 +++ .../bhyvexml2argv-firmware-efi.ldargs | 1 + .../bhyvexml2argv-firmware-efi.xml | 22 +++++ tests/bhyvexml2argvtest.c | 83 ++++++++++++++--- 11 files changed, 254 insertions(+), 11 deletions(-) create mode 100644 src/bhyve/bhyve_firmware.c create mode 100644 src/bhyve/bhyve_firmware.h create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-firmware-efi.args create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-firmware-efi.ldargs create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-firmware-efi.xml
diff --git a/po/POTFILES.in b/po/POTFILES.in index 80c5f145be..413783ee35 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -14,6 +14,7 @@ @SRCDIR@src/bhyve/bhyve_command.c @SRCDIR@src/bhyve/bhyve_domain.c @SRCDIR@src/bhyve/bhyve_driver.c +@SRCDIR@src/bhyve/bhyve_firmware.c @SRCDIR@src/bhyve/bhyve_monitor.c @SRCDIR@src/bhyve/bhyve_parse_command.c @SRCDIR@src/bhyve/bhyve_process.c diff --git a/src/bhyve/bhyve_domain.c b/src/bhyve/bhyve_domain.c index 8fbc554a0a..209e4d3905 100644 --- a/src/bhyve/bhyve_domain.c +++ b/src/bhyve/bhyve_domain.c @@ -64,6 +64,9 @@ bhyveDomainDefNeedsISAController(virDomainDefPtr def) if (def->os.bootloader == NULL && def->os.loader) return true;
+ if (def->os.firmware == VIR_DOMAIN_OS_DEF_FIRMWARE_EFI) + return true; + if (def->nserials || def->nconsoles) return true;
@@ -230,6 +233,8 @@ virDomainDefParserConfig virBhyveDriverDomainDefParserConfig = { .domainPostParseCallback = bhyveDomainDefPostParse, .assignAddressesCallback = bhyveDomainDefAssignAddresses, .deviceValidateCallback = bhyveDomainDeviceDefValidate, + + .features = VIR_DOMAIN_DEF_FEATURE_FW_AUTOSELECT, };
static void diff --git a/src/bhyve/bhyve_firmware.c b/src/bhyve/bhyve_firmware.c new file mode 100644 index 0000000000..ccc3a5ffc8 --- /dev/null +++ b/src/bhyve/bhyve_firmware.c @@ -0,0 +1,91 @@ +/* + * bhyve_firmware.c: bhyve firmware management + * + * Copyright (C) 2021 Roman Bogorodskiy + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#include <config.h> +#include <dirent.h> + +#include "viralloc.h" +#include "virlog.h" +#include "virfile.h" +#include "bhyve_conf.h" +#include "bhyve_firmware.h" + +#define VIR_FROM_THIS VIR_FROM_BHYVE + +VIR_LOG_INIT("bhyve.bhyve_firmware"); + + +#define BHYVE_DEFAULT_FIRMWARE "BHYVE_UEFI.fd" + +int +bhyveFirmwareFillDomain(bhyveConnPtr driver, + virDomainDefPtr def, + unsigned int flags) +{ + g_autoptr(DIR) dir = NULL; + virBhyveDriverConfigPtr cfg = virBhyveDriverGetConfig(driver);
virBhyveDriverGetConfig() returns a reference, thus needs to be coupled with virObjectUnref(cfg); otherwise .. [1]
+ const char *firmware_dir_cfg = cfg->firmwareDir; + const char *firmware_dir_env = NULL, *firmware_dir = NULL;
One variable per line, please. It turned out to be useful (although in this specific case it's not using virXXXPtr type so doesn't matter): https://listman.redhat.com/archives/libvir-list/2021-March/msg00542.html
+ struct dirent *entry; + char *matching_firmware = NULL; + char *first_found = NULL; + + virCheckFlags(0, -1);
1: .. @cfg is leaked here .. [2]
+ + if (def->os.firmware == VIR_DOMAIN_OS_DEF_FIRMWARE_NONE) + return 0;
2: .. or here. You get the idea. Alternatively, you can take inspiration from the QEMU driver, where we defined autoptr cleanup func and then use: g_autoptr(virQEMUDriverConfig) cfg = NULL;
+ + if (virDirOpenIfExists(&dir, firmware_dir_cfg) > 0) { + while ((virDirRead(dir, &entry, firmware_dir)) > 0) { + if (STREQ(entry->d_name, BHYVE_DEFAULT_FIRMWARE)) { + matching_firmware = g_strdup(entry->d_name); + break; + } + if (!first_found) + first_found = g_strdup(entry->d_name); + } + } + + if (!matching_firmware) { + if (!first_found) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("no firmwares found in %s"), + firmware_dir_cfg); + return -1; + } else { + matching_firmware = first_found;
3: ^^^
+ } + } + + if (!def->os.loader) + def->os.loader = g_new0(virDomainLoaderDef, 1); + + def->os.loader->type = VIR_DOMAIN_LOADER_TYPE_PFLASH; + def->os.loader->readonly = VIR_TRISTATE_BOOL_YES; + + VIR_FREE(def->os.loader->path); + + firmware_dir_env = g_getenv("LIBVIRT_BHYVE_FIRMWARE_DIR_OVERRIDE"); + firmware_dir = firmware_dir_env ? firmware_dir_env : firmware_dir_cfg; + def->os.loader->path = g_build_filename(firmware_dir, matching_firmware, NULL);
I don't think that g_build_filename() consumes @matching_firmware (even though GLib doesn't document this, looking at its source code - it sure doesn't). Therefore, @matching_firmware should be freed in the end. The same goes for @first_round - I can too find a path through this function which leaks it. For [3] I think you can use g_steal_pointer() to transfer the ownership.
+ + return 0; +}
diff --git a/tests/bhyvexml2argvtest.c b/tests/bhyvexml2argvtest.c index 197334f9c4..13b8e34c2b 100644 --- a/tests/bhyvexml2argvtest.c +++ b/tests/bhyvexml2argvtest.c
@@ -142,6 +163,35 @@ mymain(void) if (!(driver.remotePorts = virPortAllocatorRangeNew("display", 5900, 65535))) return EXIT_FAILURE;
+ if (!(driver.config = virBhyveDriverConfigNew())) + return EXIT_FAILURE; + + fakefirmwaredir = g_strdup(FAKEFIRMWAREDIRTEMPLATE); + + if (!g_mkdtemp(fakefirmwaredir)) { + fprintf(stderr, "Cannot create fakefirmwaredir"); + abort(); + } + driver.config->firmwareDir = fakefirmwaredir; + + emptyfirmwaredir = g_strdup(FAKEFIRMWAREDIRTEMPLATE); + + if (!g_mkdtemp(emptyfirmwaredir)) { + fprintf(stderr, "Cannot create emptyfirmwaredir"); + abort(); + } + + i = 0; + while (firmwares[i]) { + g_autofree char *firmware_path = g_strdup_printf("%s/%s", fakefirmwaredir, firmwares[i++]);
for() loop would be much more readable IMO.
+ + if (virFileTouch(firmware_path, 0600) < 0) { + fprintf(stderr, "Cannot create firmware file"); + abort(); + } + } + + g_setenv("LIBVIRT_BHYVE_FIRMWARE_DIR_OVERRIDE", "test_firmware_dir", TRUE);
Reviewed-by: Michal Privoznik <mprivozn@redhat.com> Michal

On Sat, Feb 27, 2021 at 08:34:08AM +0400, Roman Bogorodskiy wrote:
Implement "<os firmware='efi'>" support for bhyve driver. As there are not really lot of options, try to find "BHYVE_UEFI.fd" firmware which is installed by the sysutils/uefi-edk2-bhyve FreeBSD port.
If not found, just use the first found firmware in the firmwares directory (which is configurable via config file).
Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- Not extremely happy about the LIBVIRT_BHYVE_FIRMWARE_DIR_OVERRIDE knob, but not sure how to test this otherwise.
Agreed, that should not be part of the production code. You can use tests/bhyvexml2argvmock.c which is already used for that test where you would provide your custom implementation of opendir() system call. The implementation should check if it tries to access the default firmware dir and change it to location in our tests and all other paths simply pass to the real opendir(). Look into tests/virpcimock.c, but the addition for your use-case should look something like this: diff --git a/tests/bhyvexml2argvmock.c b/tests/bhyvexml2argvmock.c index 25b97f5e04..1c2b1f8876 100644 --- a/tests/bhyvexml2argvmock.c +++ b/tests/bhyvexml2argvmock.c @@ -4,10 +4,34 @@ #include "virstring.h" #include "virnetdev.h" #include "virnetdevtap.h" +#include "virmock.h" #include "internal.h" #define VIR_FROM_THIS VIR_FROM_BHYVE +#define DEFAULT_FIRMWARE_DIR_TEMPLATE DATADIR "/uefi-firmware" +#define FAKE_FIRMWARE_DIR_TEMPLATE abs_builddir "/bhyvefakefirmwaredir-XXXXXX" + +static int (*real_opendir)(const char *name); + +static void +init_syms(void) +{ + VIR_MOCK_REAL_INIT(opendir); +} + +DIR * +opendir(const char *path) +{ + init_syms(); + + if (STRPREFIX(path, DEFAULT_FIRMWARE_DIR_TEMPLATE)) { + return real_opendir(FAKE_FIRMWARE_DIR_TEMPLATE); + } else { + return real_opendir(path); + } +} + void virMacAddrGenerate(const unsigned char prefix[VIR_MAC_PREFIX_BUFLEN], virMacAddrPtr addr) { I did not test it :) Pavel
participants (3)
-
Michal Privoznik
-
Pavel Hrdina
-
Roman Bogorodskiy