[PATCH 0/5] bhyve: introduce NVRAM support

Roman Bogorodskiy (5): bhyve: conf: introduce nvramDir bhyve: generate NVRAM bhyve arguments bhyve: firmware: try to guess NVRAM settings bhyve: introduce bhyveDomainDefValidate() bhyve: support removing NVRAM on domain undefine src/bhyve/bhyve_command.c | 8 +- src/bhyve/bhyve_conf.c | 4 + src/bhyve/bhyve_domain.c | 43 ++++++ src/bhyve/bhyve_driver.c | 32 ++++- src/bhyve/bhyve_firmware.c | 119 ++++++++++++++-- src/bhyve/bhyve_process.c | 132 ++++++++++++++++++ src/bhyve/bhyve_process.h | 8 ++ src/bhyve/bhyve_utils.h | 2 + src/bhyve/meson.build | 5 + ...gv-uefi-nvram-template-and-source-set.args | 12 ++ ...-uefi-nvram-template-and-source-set.ldargs | 1 + ...rgv-uefi-nvram-template-and-source-set.xml | 24 ++++ ...bhyvexml2argv-uefi-nvram-template-set.args | 12 ++ ...yvexml2argv-uefi-nvram-template-set.ldargs | 1 + .../bhyvexml2argv-uefi-nvram-template-set.xml | 24 ++++ .../bhyvexml2argv-uefi-nvram.args | 12 ++ .../bhyvexml2argv-uefi-nvram.ldargs | 1 + .../bhyvexml2argv-uefi-nvram.xml | 24 ++++ tests/bhyvexml2argvtest.c | 5 + 19 files changed, 452 insertions(+), 17 deletions(-) create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-and-source-set.args create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-and-source-set.ldargs create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-and-source-set.xml create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-set.args create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-set.ldargs create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-set.xml create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram.args create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram.ldargs create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram.xml -- 2.49.0

As a preparation for NVRAM support, introduce nvramDir configuration item. Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- src/bhyve/bhyve_conf.c | 4 ++++ src/bhyve/bhyve_utils.h | 2 ++ src/bhyve/meson.build | 5 +++++ 3 files changed, 11 insertions(+) diff --git a/src/bhyve/bhyve_conf.c b/src/bhyve/bhyve_conf.c index 28981c90b7..f18b24f91d 100644 --- a/src/bhyve/bhyve_conf.c +++ b/src/bhyve/bhyve_conf.c @@ -57,6 +57,8 @@ virBhyveDriverConfigNew(void) return NULL; cfg->firmwareDir = g_strdup(DATADIR "/uefi-firmware"); + cfg->libDir = g_strdup_printf("%s/lib/libvirt/bhyve", LOCALSTATEDIR); + cfg->nvramDir = g_strdup_printf("%s/nvram", cfg->libDir); return cfg; } @@ -95,6 +97,8 @@ virBhyveDriverConfigDispose(void *obj) struct _virBhyveDriverConfig *cfg = obj; g_free(cfg->firmwareDir); + g_free(cfg->libDir); + g_free(cfg->nvramDir); } void diff --git a/src/bhyve/bhyve_utils.h b/src/bhyve/bhyve_utils.h index 6c6fd12dbe..9c9ea0a01a 100644 --- a/src/bhyve/bhyve_utils.h +++ b/src/bhyve/bhyve_utils.h @@ -39,6 +39,8 @@ struct _virBhyveDriverConfig { virObject parent; char *firmwareDir; + char *libDir; + char *nvramDir; }; G_DEFINE_AUTOPTR_CLEANUP_FUNC(virBhyveDriverConfig, virObjectUnref); diff --git a/src/bhyve/meson.build b/src/bhyve/meson.build index b3551477b7..11920d9c3e 100644 --- a/src/bhyve/meson.build +++ b/src/bhyve/meson.build @@ -64,4 +64,9 @@ if conf.has('WITH_BHYVE') virt_daemon_confs += { 'name': 'virtbhyved', } + + virt_install_dirs += [ + localstatedir / 'lib' / 'libvirt' / 'bhyve', + localstatedir / 'lib' / 'libvirt' / 'bhyve' / 'nvram', + ] endif -- 2.49.0

Currently, bhyve bootrom specification looks like this: bhyve ... -l bootrom,/path/to/firmware.fd In addition to that, it supports specifying the VARS files using: -l bootrom,/path/to/firmware.fd,/path/to/my_domain_VARS.fd Update virBhyveProcessBuildBhyveCmd() to include the VARS file if NVRAM is specified in the domain XML. Additionally, support copying this file from the specified template. To do that, introduce the bhyveProcessPrepareHost() and related helpers. They are currently not doing anything but NVRAM preparations, but should be useful for other host-side related tasks in the future. Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- src/bhyve/bhyve_command.c | 8 ++- src/bhyve/bhyve_process.c | 132 ++++++++++++++++++++++++++++++++++++++ src/bhyve/bhyve_process.h | 8 +++ 3 files changed, 146 insertions(+), 2 deletions(-) diff --git a/src/bhyve/bhyve_command.c b/src/bhyve/bhyve_command.c index 44c66ea147..cd1ccf61f3 100644 --- a/src/bhyve/bhyve_command.c +++ b/src/bhyve/bhyve_command.c @@ -808,8 +808,12 @@ virBhyveProcessBuildBhyveCmd(struct _bhyveConn *driver, virDomainDef *def, if (def->os.bootloader == NULL && def->os.loader) { if ((bhyveDriverGetBhyveCaps(driver) & BHYVE_CAP_LPC_BOOTROM)) { - virCommandAddArg(cmd, "-l"); - virCommandAddArgFormat(cmd, "bootrom,%s", def->os.loader->path); + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; + virBufferAsprintf(&buf, "bootrom,%s", def->os.loader->path); + if (def->os.loader->nvram && def->os.loader->nvram->path) + virBufferAsprintf(&buf, ",%s", def->os.loader->nvram->path); + + virCommandAddArgList(cmd, "-l", virBufferContentAndReset(&buf), NULL); } else { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Installed bhyve binary does not support UEFI loader")); diff --git a/src/bhyve/bhyve_process.c b/src/bhyve/bhyve_process.c index a17994e2a0..5e77a9c4d6 100644 --- a/src/bhyve/bhyve_process.c +++ b/src/bhyve/bhyve_process.c @@ -1,6 +1,7 @@ /* * bhyve_process.c: bhyve process management * + * Copyright (C) 2006-2016 Red Hat, Inc. * Copyright (C) 2014 Roman Bogorodskiy * Copyright (C) 2025 The FreeBSD Foundation * @@ -275,6 +276,134 @@ bhyveProcessPrepareDomain(bhyveConn *driver, return 0; } + +struct bhyvePrepareNVRAMHelperData { + int srcFD; + const char *srcPath; +}; + + +static int +bhyvePrepareNVRAMHelper(int dstFD, + const char *dstPath, + const void *opaque) +{ + const struct bhyvePrepareNVRAMHelperData *data = opaque; + ssize_t r; + + do { + char buf[1024]; + + if ((r = saferead(data->srcFD, buf, sizeof(buf))) < 0) { + virReportSystemError(errno, + _("Unable to read from file '%1$s'"), + data->srcPath); + return -2; + } + + if (safewrite(dstFD, buf, r) < 0) { + virReportSystemError(errno, + _("Unable to write to file '%1$s'"), + dstPath); + return -1; + } + } while (r); + + return 0; +} + + +static int +bhyvePrepareNVRAMFile(bhyveConn *driver G_GNUC_UNUSED, + virDomainLoaderDef *loader) +{ + VIR_AUTOCLOSE srcFD = -1; + struct bhyvePrepareNVRAMHelperData data; + + if (virFileExists(loader->nvram->path)) + return 0; + + if (!loader->nvramTemplate) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("unable to find any master var store for loader: %1$s"), + loader->path); + return -1; + } + + /* If 'nvramTemplateFormat' is empty it means that it's a user-provided + * template which we couldn't verify. Assume the user knows what they're doing */ + if (loader->nvramTemplateFormat != VIR_STORAGE_FILE_NONE && + loader->nvram->format != loader->nvramTemplateFormat) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("conversion of the nvram template to another target format is not supported")); + return -1; + } + + if ((srcFD = virFileOpenAs(loader->nvramTemplate, O_RDONLY, + 0, -1, -1, 0)) < 0) { + virReportSystemError(-srcFD, + _("Failed to open file '%1$s'"), + loader->nvramTemplate); + return -1; + } + + data.srcFD = srcFD; + data.srcPath = loader->nvramTemplate; + + if (virFileRewrite(loader->nvram->path, + S_IRUSR | S_IWUSR, + 0, 0, + bhyvePrepareNVRAMHelper, + &data) < 0) { + return -1; + } + + return 0; +} + + +int +bhyvePrepareNVRAM(bhyveConn *driver, + virDomainDef *def) +{ + virDomainLoaderDef *loader = def->os.loader; + + if (!loader || !loader->nvram) + return 0; + + VIR_DEBUG("nvram='%s'", NULLSTR(loader->nvram->path)); + + switch (virStorageSourceGetActualType(loader->nvram)) { + case VIR_STORAGE_TYPE_FILE: + return bhyvePrepareNVRAMFile(driver, loader); + case VIR_STORAGE_TYPE_BLOCK: + case VIR_STORAGE_TYPE_DIR: + case VIR_STORAGE_TYPE_NETWORK: + case VIR_STORAGE_TYPE_VOLUME: + case VIR_STORAGE_TYPE_NVME: + case VIR_STORAGE_TYPE_VHOST_USER: + case VIR_STORAGE_TYPE_VHOST_VDPA: + case VIR_STORAGE_TYPE_LAST: + case VIR_STORAGE_TYPE_NONE: + break; + } + + return 0; +} + +int +bhyveProcessPrepareHost(bhyveConn *driver, + virDomainDef *def, + unsigned int flags) +{ + virCheckFlags(0, -1); + + if (bhyvePrepareNVRAM(driver, def) < 0) + return -1; + + return 0; +} + int virBhyveProcessStart(bhyveConn *driver, virConnectPtr conn, @@ -292,6 +421,9 @@ virBhyveProcessStart(bhyveConn *driver, if (bhyveProcessPrepareDomain(driver, vm, flags) < 0) return -1; + if (bhyveProcessPrepareHost(driver, vm->def, flags) < 0) + return -1; + return virBhyveProcessStartImpl(driver, vm, reason); } diff --git a/src/bhyve/bhyve_process.h b/src/bhyve/bhyve_process.h index e69db41fc2..5e0acc810c 100644 --- a/src/bhyve/bhyve_process.h +++ b/src/bhyve/bhyve_process.h @@ -23,10 +23,18 @@ #include "bhyve_utils.h" +int +bhyvePrepareNVRAM(bhyveConn *driver, + virDomainDef *def); + int bhyveProcessPrepareDomain(bhyveConn *driver, virDomainObj *vm, unsigned int flags); +int +bhyveProcessPrepareHost(bhyveConn *driver, + virDomainDef *def, + unsigned int flags); int virBhyveProcessStart(bhyveConn *driver, virConnectPtr conn, -- 2.49.0

Extend bhyveFirmwareFillDomain() so that when we find the default edk2 firmware, also look for its matching template file, and use it as a nvramTemplate if found. Extend bhyvexml2argvtest to verify various NVRAM configurations. Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- src/bhyve/bhyve_firmware.c | 119 +++++++++++++++--- ...gv-uefi-nvram-template-and-source-set.args | 12 ++ ...-uefi-nvram-template-and-source-set.ldargs | 1 + ...rgv-uefi-nvram-template-and-source-set.xml | 24 ++++ ...bhyvexml2argv-uefi-nvram-template-set.args | 12 ++ ...yvexml2argv-uefi-nvram-template-set.ldargs | 1 + .../bhyvexml2argv-uefi-nvram-template-set.xml | 24 ++++ .../bhyvexml2argv-uefi-nvram.args | 12 ++ .../bhyvexml2argv-uefi-nvram.ldargs | 1 + .../bhyvexml2argv-uefi-nvram.xml | 24 ++++ tests/bhyvexml2argvtest.c | 5 + 11 files changed, 221 insertions(+), 14 deletions(-) create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-and-source-set.args create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-and-source-set.ldargs create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-and-source-set.xml create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-set.args create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-set.ldargs create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-set.xml create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram.args create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram.ldargs create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram.xml diff --git a/src/bhyve/bhyve_firmware.c b/src/bhyve/bhyve_firmware.c index 8aaf05dc62..35a1097e5d 100644 --- a/src/bhyve/bhyve_firmware.c +++ b/src/bhyve/bhyve_firmware.c @@ -1,7 +1,9 @@ /* * bhyve_firmware.c: bhyve firmware management * + * Copyright (C) 2019 Red Hat, Inc. * Copyright (C) 2021 Roman Bogorodskiy + * Copyright (C) 2025 The FreeBSD Foundation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -25,6 +27,7 @@ #include "viralloc.h" #include "virlog.h" #include "virfile.h" +#include "virstring.h" #include "bhyve_conf.h" #include "bhyve_firmware.h" @@ -34,6 +37,76 @@ VIR_LOG_INIT("bhyve.bhyve_firmware"); #define BHYVE_DEFAULT_FIRMWARE "BHYVE_UEFI.fd" +#define BHYVE_DEFAULT_NVRAM_TEMPLATE "BHYVE_UEFI_VARS.fd" + +static void +bhyveFirmwareEnsureNVRAM(virDomainDef *def, + bhyveConn *driver) +{ + g_autoptr(virBhyveDriverConfig) cfg = virBhyveDriverGetConfig(driver); + virDomainLoaderDef *loader = def->os.loader; + const char *ext = NULL; + + if (!loader) + return; + + if (loader->type != VIR_DOMAIN_LOADER_TYPE_PFLASH) + return; + + if (loader->readonly != VIR_TRISTATE_BOOL_YES) + return; + + if (loader->stateless == VIR_TRISTATE_BOOL_YES) + return; + + /* If the NVRAM format hasn't been set yet, inherit the same as + * the loader */ + if (loader->nvram && !loader->nvram->format) + loader->nvram->format = loader->format; + + if (loader->nvram) { + /* Nothing to do if a proper NVRAM backend is already configured */ + if (!virStorageSourceIsEmpty(loader->nvram)) + return; + + /* otherwise we want to reset and re-populate the definition */ + virObjectUnref(loader->nvram); + } else { + /* Also do nothing if NVRAM is not requested */ + return; + } + + loader->nvram = virStorageSourceNew(); + loader->nvram->type = VIR_STORAGE_TYPE_FILE; + + /* The nvram template format should be always present but as a failsafe, + * duplicate the loader format if it is not available. */ + if (loader->nvramTemplateFormat > VIR_STORAGE_FILE_NONE) + loader->nvram->format = loader->nvramTemplateFormat; + else + loader->nvram->format = loader->format; + + /* The extension used by raw edk2 builds has historically + * been .fd, but more recent aarch64 builds have started + * using the .raw extension instead. + * + * If we're defining a new domain, we should try to match the + * extension for the file backing its NVRAM store with the + * one used by the template to keep things nice and + * consistent. + * + * If we're loading an existing domain, however, we need to + * stick with the .fd extension to ensure compatibility */ + if (loader->nvramTemplate && + virStringHasSuffix(loader->nvramTemplate, ".raw")) + ext = ".raw"; + else + ext = ".fd"; + + loader->nvram->path = g_strdup_printf("%s/%s_VARS%s", + cfg->nvramDir, def->name, + NULLSTR_EMPTY(ext)); +} int bhyveFirmwareFillDomain(bhyveConn *driver, @@ -42,25 +115,31 @@ bhyveFirmwareFillDomain(bhyveConn *driver, { g_autoptr(DIR) dir = NULL; g_autoptr(virBhyveDriverConfig) cfg = virBhyveDriverGetConfig(driver); + virDomainLoaderDef *loader = def->os.loader; const char *firmware_dir = cfg->firmwareDir; struct dirent *entry; g_autofree char *matching_firmware = NULL; + g_autofree char *matching_nvram_template = NULL; g_autofree char *first_found = NULL; virCheckFlags(0, -1); if (def->os.firmware == VIR_DOMAIN_OS_DEF_FIRMWARE_NONE) - return 0; + goto out; if (virDirOpenIfExists(&dir, firmware_dir) > 0) { while ((virDirRead(dir, &entry, firmware_dir)) > 0) { if (g_str_has_prefix(entry->d_name, ".")) continue; - if (STREQ(entry->d_name, BHYVE_DEFAULT_FIRMWARE)) { + if (!matching_firmware && + STREQ(entry->d_name, BHYVE_DEFAULT_FIRMWARE)) matching_firmware = g_strdup(entry->d_name); - break; - } + + if (!matching_nvram_template && + STREQ(entry->d_name, BHYVE_DEFAULT_NVRAM_TEMPLATE)) + matching_nvram_template = g_strdup(entry->d_name); + if (!first_found) first_found = g_strdup(entry->d_name); } @@ -77,25 +156,37 @@ bhyveFirmwareFillDomain(bhyveConn *driver, } } - if (!def->os.loader) - def->os.loader = virDomainLoaderDefNew(); + if (!loader) { + loader = virDomainLoaderDefNew(); + def->os.loader = loader; + } - if (!def->os.loader->format) - def->os.loader->format = VIR_STORAGE_FILE_RAW; + if (!loader->format) + loader->format = VIR_STORAGE_FILE_RAW; - if (def->os.loader->format != VIR_STORAGE_FILE_RAW) { + if (loader->format != VIR_STORAGE_FILE_RAW) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unsupported loader format '%1$s'"), - virStorageFileFormatTypeToString(def->os.loader->format)); + virStorageFileFormatTypeToString(loader->format)); return -1; } - def->os.loader->type = VIR_DOMAIN_LOADER_TYPE_PFLASH; - def->os.loader->readonly = VIR_TRISTATE_BOOL_YES; + if (!loader->nvramTemplate + && matching_firmware && matching_nvram_template) { + loader->nvramTemplate = g_build_filename(firmware_dir, + matching_firmware, + NULL); + } + + loader->type = VIR_DOMAIN_LOADER_TYPE_PFLASH; + loader->readonly = VIR_TRISTATE_BOOL_YES; + + VIR_FREE(loader->path); - VIR_FREE(def->os.loader->path); + loader->path = g_build_filename(firmware_dir, matching_firmware, NULL); - def->os.loader->path = g_build_filename(firmware_dir, matching_firmware, NULL); + out: + bhyveFirmwareEnsureNVRAM(def, driver); return 0; } diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-and-source-set.args b/tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-and-source-set.args new file mode 100644 index 0000000000..05a9e8df2c --- /dev/null +++ b/tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-and-source-set.args @@ -0,0 +1,12 @@ +bhyve \ +-c 1 \ +-m 214 \ +-u \ +-H \ +-P \ +-s 0:0,hostbridge \ +-l bootrom,/path/to/test.fd,/path/to/nvram/guest_VARS.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-uefi-nvram-template-and-source-set.ldargs b/tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-and-source-set.ldargs new file mode 100644 index 0000000000..421376db9e --- /dev/null +++ b/tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-and-source-set.ldargs @@ -0,0 +1 @@ +dummy diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-and-source-set.xml b/tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-and-source-set.xml new file mode 100644 index 0000000000..cdcbfd081c --- /dev/null +++ b/tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-and-source-set.xml @@ -0,0 +1,24 @@ +<domain type='bhyve'> + <name>bhyve</name> + <uuid>df3be7e7-a104-11e3-aeb0-50e5492bd3dc</uuid> + <memory>219136</memory> + <vcpu>1</vcpu> + <os> + <type>hvm</type> + <loader readonly="yes" type="pflash">/path/to/test.fd</loader> + <nvram template='/path/to/test_VARS.fd'>/path/to/nvram/guest_VARS.fd</nvram> + </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/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-set.args b/tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-set.args new file mode 100644 index 0000000000..0d367e99f1 --- /dev/null +++ b/tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-set.args @@ -0,0 +1,12 @@ +bhyve \ +-c 1 \ +-m 214 \ +-u \ +-H \ +-P \ +-s 0:0,hostbridge \ +-l bootrom,/path/to/test.fd,fakenvramdir/bhyve_VARS.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-uefi-nvram-template-set.ldargs b/tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-set.ldargs new file mode 100644 index 0000000000..421376db9e --- /dev/null +++ b/tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-set.ldargs @@ -0,0 +1 @@ +dummy diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-set.xml b/tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-set.xml new file mode 100644 index 0000000000..c05dd4ff95 --- /dev/null +++ b/tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-set.xml @@ -0,0 +1,24 @@ +<domain type='bhyve'> + <name>bhyve</name> + <uuid>df3be7e7-a104-11e3-aeb0-50e5492bd3dc</uuid> + <memory>219136</memory> + <vcpu>1</vcpu> + <os> + <type>hvm</type> + <loader readonly="yes" type="pflash">/path/to/test.fd</loader> + <nvram template='/path/to/test_VARS.fd'/> + </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/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram.args b/tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram.args new file mode 100644 index 0000000000..0d367e99f1 --- /dev/null +++ b/tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram.args @@ -0,0 +1,12 @@ +bhyve \ +-c 1 \ +-m 214 \ +-u \ +-H \ +-P \ +-s 0:0,hostbridge \ +-l bootrom,/path/to/test.fd,fakenvramdir/bhyve_VARS.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-uefi-nvram.ldargs b/tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram.ldargs new file mode 100644 index 0000000000..421376db9e --- /dev/null +++ b/tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram.ldargs @@ -0,0 +1 @@ +dummy diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram.xml b/tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram.xml new file mode 100644 index 0000000000..bec38ad65d --- /dev/null +++ b/tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram.xml @@ -0,0 +1,24 @@ +<domain type='bhyve'> + <name>bhyve</name> + <uuid>df3be7e7-a104-11e3-aeb0-50e5492bd3dc</uuid> + <memory>219136</memory> + <vcpu>1</vcpu> + <os> + <type>hvm</type> + <loader readonly="yes" type="pflash">/path/to/test.fd</loader> + <nvram/> + </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 3831f0c65d..58b404ca7d 100644 --- a/tests/bhyvexml2argvtest.c +++ b/tests/bhyvexml2argvtest.c @@ -148,6 +148,7 @@ mymain(void) { int ret = 0; g_autofree char *fakefirmwaredir = g_strdup("fakefirmwaredir"); + g_autofree char *fakenvramdir = g_strdup("fakenvramdir"); g_autofree char *fakefirmwareemptydir = g_strdup("fakefirmwareemptydir"); if ((driver.caps = virBhyveCapsBuild()) == NULL) @@ -163,6 +164,7 @@ mymain(void) return EXIT_FAILURE; driver.config->firmwareDir = fakefirmwaredir; + driver.config->nvramDir = fakenvramdir; # define DO_TEST_FULL(name, flags) \ do { \ @@ -218,6 +220,9 @@ mymain(void) DO_TEST("localtime"); DO_TEST("net-e1000"); DO_TEST("uefi"); + DO_TEST("uefi-nvram"); + DO_TEST("uefi-nvram-template-set"); + DO_TEST("uefi-nvram-template-and-source-set"); DO_TEST("vnc"); DO_TEST("vnc-vgaconf-on"); DO_TEST("vnc-vgaconf-off"); -- 2.49.0

Add the bhyveDomainDefValidate() validation which currently checks whether the requested NVRAM is supported. Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- src/bhyve/bhyve_domain.c | 43 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/bhyve/bhyve_domain.c b/src/bhyve/bhyve_domain.c index ca5176885a..3e18a462e4 100644 --- a/src/bhyve/bhyve_domain.c +++ b/src/bhyve/bhyve_domain.c @@ -267,11 +267,54 @@ bhyveDomainDeviceDefValidate(const virDomainDeviceDef *dev, return 0; } + +static int +bhyveDomainDefValidate(const virDomainDef *def, + void *opaque G_GNUC_UNUSED, + void *parseOpaque G_GNUC_UNUSED) +{ + virStorageSource *src = NULL; + + if (!def->os.loader) + return 0; + + if (!(src = def->os.loader->nvram)) + return 0; + + if (src->type != VIR_STORAGE_TYPE_FILE) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + "%s", + _("only 'file' type is supported with NVRAM")); + return -1; + } + + if (src->sliceStorage) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("slices are not supported with NVRAM")); + return -1; + } + + if (src->pr) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("persistent reservations are not supported with NVRAM")); + return -1; + } + + if (src->backingStore) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("backingStore is not supported with NVRAM")); + return -1; + } + + return 0; +} + virDomainDefParserConfig virBhyveDriverDomainDefParserConfig = { .devicesPostParseCallback = bhyveDomainDeviceDefPostParse, .domainPostParseCallback = bhyveDomainDefPostParse, .assignAddressesCallback = bhyveDomainDefAssignAddresses, .deviceValidateCallback = bhyveDomainDeviceDefValidate, + .domainValidateCallback = bhyveDomainDefValidate, .features = VIR_DOMAIN_DEF_FEATURE_FW_AUTOSELECT, }; -- 2.49.0

Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com> --- src/bhyve/bhyve_driver.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/bhyve/bhyve_driver.c b/src/bhyve/bhyve_driver.c index 684346952d..db7d440a97 100644 --- a/src/bhyve/bhyve_driver.c +++ b/src/bhyve/bhyve_driver.c @@ -533,9 +533,19 @@ bhyveDomainUndefineFlags(virDomainPtr domain, unsigned int flags) struct _bhyveConn *privconn = domain->conn->privateData; virObjectEvent *event = NULL; virDomainObj *vm; + g_autofree char *nvram_path = NULL; int ret = -1; - virCheckFlags(0, -1); + virCheckFlags(VIR_DOMAIN_UNDEFINE_NVRAM | + VIR_DOMAIN_UNDEFINE_KEEP_NVRAM, -1); + + if ((flags & VIR_DOMAIN_UNDEFINE_NVRAM) && + (flags & VIR_DOMAIN_UNDEFINE_KEEP_NVRAM)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("cannot both keep and delete nvram")); + return -1; + } + if (!(vm = bhyveDomObjFromDomain(domain))) goto cleanup; @@ -548,6 +558,26 @@ bhyveDomainUndefineFlags(virDomainPtr domain, unsigned int flags) goto cleanup; } + if (vm->def->os.loader && vm->def->os.loader->nvram && + virStorageSourceIsLocalStorage(vm->def->os.loader->nvram)) { + nvram_path = g_strdup(vm->def->os.loader->nvram->path); + } + + if (nvram_path && virFileExists(nvram_path)) { + if ((flags & VIR_DOMAIN_UNDEFINE_NVRAM)) { + if (unlink(nvram_path) < 0) { + virReportSystemError(errno, + _("failed to remove nvram: %1$s"), + nvram_path); + goto cleanup; + } + } else if (!(flags & VIR_DOMAIN_UNDEFINE_KEEP_NVRAM)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("cannot undefine domain with nvram")); + goto cleanup; + } + } + if (virDomainDeleteConfig(BHYVE_CONFIG_DIR, BHYVE_AUTOSTART_DIR, vm) < 0) -- 2.49.0

On 5/9/25 19:11, Roman Bogorodskiy wrote:
Roman Bogorodskiy (5): bhyve: conf: introduce nvramDir bhyve: generate NVRAM bhyve arguments bhyve: firmware: try to guess NVRAM settings bhyve: introduce bhyveDomainDefValidate() bhyve: support removing NVRAM on domain undefine
src/bhyve/bhyve_command.c | 8 +- src/bhyve/bhyve_conf.c | 4 + src/bhyve/bhyve_domain.c | 43 ++++++ src/bhyve/bhyve_driver.c | 32 ++++- src/bhyve/bhyve_firmware.c | 119 ++++++++++++++-- src/bhyve/bhyve_process.c | 132 ++++++++++++++++++ src/bhyve/bhyve_process.h | 8 ++ src/bhyve/bhyve_utils.h | 2 + src/bhyve/meson.build | 5 + ...gv-uefi-nvram-template-and-source-set.args | 12 ++ ...-uefi-nvram-template-and-source-set.ldargs | 1 + ...rgv-uefi-nvram-template-and-source-set.xml | 24 ++++ ...bhyvexml2argv-uefi-nvram-template-set.args | 12 ++ ...yvexml2argv-uefi-nvram-template-set.ldargs | 1 + .../bhyvexml2argv-uefi-nvram-template-set.xml | 24 ++++ .../bhyvexml2argv-uefi-nvram.args | 12 ++ .../bhyvexml2argv-uefi-nvram.ldargs | 1 + .../bhyvexml2argv-uefi-nvram.xml | 24 ++++ tests/bhyvexml2argvtest.c | 5 + 19 files changed, 452 insertions(+), 17 deletions(-) create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-and-source-set.args create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-and-source-set.ldargs create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-and-source-set.xml create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-set.args create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-set.ldargs create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-set.xml create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram.args create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram.ldargs create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram.xml
Reviewed-by: Michal Privoznik <mprivozn@redhat.com> Michal

Michal Prívozník wrote:
On 5/9/25 19:11, Roman Bogorodskiy wrote:
Roman Bogorodskiy (5): bhyve: conf: introduce nvramDir bhyve: generate NVRAM bhyve arguments bhyve: firmware: try to guess NVRAM settings bhyve: introduce bhyveDomainDefValidate() bhyve: support removing NVRAM on domain undefine
src/bhyve/bhyve_command.c | 8 +- src/bhyve/bhyve_conf.c | 4 + src/bhyve/bhyve_domain.c | 43 ++++++ src/bhyve/bhyve_driver.c | 32 ++++- src/bhyve/bhyve_firmware.c | 119 ++++++++++++++-- src/bhyve/bhyve_process.c | 132 ++++++++++++++++++ src/bhyve/bhyve_process.h | 8 ++ src/bhyve/bhyve_utils.h | 2 + src/bhyve/meson.build | 5 + ...gv-uefi-nvram-template-and-source-set.args | 12 ++ ...-uefi-nvram-template-and-source-set.ldargs | 1 + ...rgv-uefi-nvram-template-and-source-set.xml | 24 ++++ ...bhyvexml2argv-uefi-nvram-template-set.args | 12 ++ ...yvexml2argv-uefi-nvram-template-set.ldargs | 1 + .../bhyvexml2argv-uefi-nvram-template-set.xml | 24 ++++ .../bhyvexml2argv-uefi-nvram.args | 12 ++ .../bhyvexml2argv-uefi-nvram.ldargs | 1 + .../bhyvexml2argv-uefi-nvram.xml | 24 ++++ tests/bhyvexml2argvtest.c | 5 + 19 files changed, 452 insertions(+), 17 deletions(-) create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-and-source-set.args create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-and-source-set.ldargs create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-and-source-set.xml create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-set.args create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-set.ldargs create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram-template-set.xml create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram.args create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram.ldargs create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-uefi-nvram.xml
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
Thanks, I've pushed the changes. I've changed one line before pushing though, due to a bug I noticed in my final round of pre-push testing. The changed line is: +++ b/src/bhyve/bhyve_firmware.c @@ -174,7 +174,7 @@ bhyveFirmwareFillDomain(bhyveConn *driver, if (!loader->nvramTemplate && matching_firmware && matching_nvram_template) { loader->nvramTemplate = g_build_filename(firmware_dir, - matching_firmware, + matching_nvram_template, NULL); } For the cases when the template is not specified, so we assign the proper template path instead of a firmware path.
Michal
participants (2)
-
Michal Prívozník
-
Roman Bogorodskiy