bhyve: improve loader handling on arm64

Bhyve on arm64 does not have the bhyveload(8) tool.
That means that it cannot be used as a default if the loader is not
configured for the domain.

To prevent users from getting unusable configurations, handle loader
configuration on arm64 like that:

 - if loader is specified in the domain XML, just use it
 - if not specified, try to check whether the default uboot loader
   is available on the system. In case it is, set is as the loader,
   otherwise fail with the error.

Additionally, the loader could be configured in bhyve.conf.
By default, it uses the loader installed by the
sysutils/u-boot-bhyve-arm64 port or a corresponding package.

Signed-off-by: Roman Bogorodskiy <bogorodskiy@gmail.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
This commit is contained in:
Roman Bogorodskiy
2026-02-12 19:18:35 +01:00
parent e23fd0b7fd
commit 79b05df9f8
15 changed files with 76 additions and 16 deletions
+5
View File
@@ -6,6 +6,11 @@
# to the directory that sysutils/bhyve-firmware installs files into.
#firmware_dir = "/usr/local/share/uefi-firmware"
# Path to the U-Boot loader for the arm64 guests.
# By default it's pointing to the loader installed
# by the sysutils/u-boot-bhyve-arm64 port
#uboot_path = "/usr/local/share/u-boot/u-boot-bhyve-arm64/u-boot.bin"
# Set timeout for the bhyveload(8) command. This might be necessary
# because in case of errors bhyveload(8) drops to an interactive
# loader and hangs indefinitely. These timeout values are passed
+8
View File
@@ -61,6 +61,8 @@ virBhyveDriverConfigNew(void)
cfg->libDir = g_strdup_printf("%s/lib/libvirt/bhyve", LOCALSTATEDIR);
cfg->nvramDir = g_strdup_printf("%s/nvram", cfg->libDir);
cfg->ubootPath = g_strdup(DATADIR "/u-boot/u-boot-bhyve-arm64/u-boot.bin");
cfg->bhyveloadTimeout = 300;
cfg->bhyveloadTimeoutKill = 15;
@@ -85,6 +87,10 @@ virBhyveLoadDriverConfig(struct _virBhyveDriverConfig *cfg,
&cfg->firmwareDir) < 0)
return -1;
if (virConfGetValueString(conf, "uboot_path",
&cfg->ubootPath) < 0)
return -1;
if (virConfGetValueInt(conf, "bhyveload_timeout",
&cfg->bhyveloadTimeout) < 0)
return -1;
@@ -111,6 +117,8 @@ virBhyveDriverConfigDispose(void *obj)
g_free(cfg->firmwareDir);
g_free(cfg->libDir);
g_free(cfg->nvramDir);
g_free(cfg->ubootPath);
}
void
+23
View File
@@ -27,6 +27,7 @@
#include "bhyve_domain.h"
#include "bhyve_capabilities.h"
#include "viralloc.h"
#include "virfile.h"
#include "virlog.h"
#include "virutil.h"
@@ -112,6 +113,28 @@ bhyveDomainDefPostParse(virDomainDef *def,
!(bhyveDriverGetBhyveCaps(driver) & BHYVE_CAP_RTC_UTC))
def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME;
/* bhyve/arm64 does not provide the bhyveload(8) tool,
* so if the loader is not specified and we cannot fall back to the
* default one, then this results in an unusable configuration. */
if (ARCH_IS_ARM(def->os.arch)) {
if (def->os.loader == NULL) {
g_autoptr(virBhyveDriverConfig) cfg = virBhyveDriverGetConfig(driver);
char *uboot_path = cfg->ubootPath;
if (virFileExists(uboot_path)) {
def->os.loader = virDomainLoaderDefNew();
def->os.loader->path = g_strdup(uboot_path);
def->os.loader->readonly = true;
def->os.loader->type = VIR_DOMAIN_LOADER_TYPE_PFLASH;
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("loader is not specified and the default loader (%1$s) not found"),
uboot_path);
return -1;
}
}
}
return 0;
}
+2
View File
@@ -42,6 +42,8 @@ struct _virBhyveDriverConfig {
char *libDir;
char *nvramDir;
char *ubootPath;
int bhyveloadTimeout;
int bhyveloadTimeoutKill;
};
+2 -1
View File
@@ -25,9 +25,10 @@ module Libvirtd_bhyve =
let log_entry = str_entry "firmware_dir"
let bhyveload_timeout = int_entry "bhyveload_timeout"
let bhyveload_timeout_kill = int_entry "bhyveload_timeout_kill"
let uboot_path = str_entry "uboot_path"
(* Each entry in the config is one of the following three ... *)
let entry = log_entry | bhyveload_timeout | bhyveload_timeout_kill
let entry = log_entry | bhyveload_timeout | bhyveload_timeout_kill | uboot_path
let comment = [ label "#comment" . del /#[ \t]*/ "# " . store /([^ \t\n][^\n]*)?/ . del /\n/ "\n" ]
let empty = [ label "#empty" . eol ]
+1
View File
@@ -3,5 +3,6 @@ module Test_libvirtd_bhyve =
test Libvirtd_bhyve.lns get conf =
{ "firmware_dir" = "/usr/local/share/uefi-firmware" }
{ "uboot_path" = "/usr/local/share/u-boot/u-boot-bhyve-arm64/u-boot.bin" }
{ "bhyveload_timeout" = "300" }
{ "bhyveload_timeout_kill" = "15" }
@@ -2,6 +2,7 @@ bhyve \
-c 1 \
-m 214 \
-s 0:0,hostbridge \
-o bootrom=fakeubootpath/u-boot.bin \
-s 3:0,virtio-net,faketapdev,mac=52:54:00:b9:94:02 \
-s 2:0,virtio-blk,/tmp/freebsd.img \
bhyve
@@ -1,7 +1 @@
timeout \
--foreground \
--verbose \
-k 20s 300s bhyveload \
-m 214 \
-d /tmp/freebsd.img \
bhyve
dummy
@@ -2,6 +2,7 @@ bhyve \
-c 1 \
-m 214 \
-s 0:0,hostbridge \
-o bootrom=fakeubootpath/u-boot.bin \
-s 3:0,virtio-net,faketapdev,mac=52:54:00:b9:94:02 \
-s 2:0,virtio-blk,/tmp/freebsd.img \
-o console=/dev/nmdm0A \
@@ -1,7 +1 @@
timeout \
--foreground \
--verbose \
-k 20s 300s bhyveload \
-m 214 \
-d /tmp/freebsd.img \
bhyve
dummy
+19 -1
View File
@@ -2,7 +2,9 @@
#include <dirent.h>
#include "configmake.h"
#include "viralloc.h"
#include "virfile.h"
#include "virstring.h"
#include "virnetdev.h"
#include "virnetdevtap.h"
@@ -14,11 +16,16 @@
#define VIR_FROM_THIS VIR_FROM_BHYVE
static DIR * (*real_opendir)(const char *name);
static bool (*real_virFileExists)(const char *path);
static void
init_syms(void)
{
VIR_MOCK_REAL_INIT(opendir);
if (!real_opendir)
VIR_MOCK_REAL_INIT(opendir);
if (!real_virFileExists)
VIR_MOCK_REAL_INIT(virFileExists);
}
#define FAKEFIRMWAREDIR abs_srcdir "/bhyvefirmwaredata/three_firmwares"
@@ -97,3 +104,14 @@ virCPUProbeHost(virArch arch)
{
return testUtilsHostCpusGetDefForArch(arch);
}
bool
virFileExists(const char *path)
{
init_syms();
if (STREQ(path, "fakeubootpath/u-boot.bin"))
return true;
return real_virFileExists(path);
}
+3
View File
@@ -151,6 +151,7 @@ mymain(void)
g_autofree char *fakefirmwaredir = g_strdup("fakefirmwaredir");
g_autofree char *fakenvramdir = g_strdup("fakenvramdir");
g_autofree char *fakefirmwareemptydir = g_strdup("fakefirmwareemptydir");
g_autofree char *fakeubootpath = g_strdup("fakeubootpath/u-boot.bin");
if ((driver.caps = virBhyveCapsBuild()) == NULL)
return EXIT_FAILURE;
@@ -166,8 +167,10 @@ mymain(void)
VIR_FREE(driver.config->firmwareDir);
VIR_FREE(driver.config->nvramDir);
VIR_FREE(driver.config->ubootPath);
driver.config->firmwareDir = g_steal_pointer(&fakefirmwaredir);
driver.config->nvramDir = g_steal_pointer(&fakenvramdir);
driver.config->ubootPath = g_steal_pointer(&fakeubootpath);
driver.config->bhyveloadTimeout = 0;
driver.config->bhyveloadTimeoutKill = 0;
@@ -6,6 +6,7 @@
<vcpu placement='static'>1</vcpu>
<os>
<type arch='aarch64'>hvm</type>
<loader readonly='yes' type='pflash'>fakeubootpath/u-boot.bin</loader>
<boot dev='hd'/>
</os>
<clock offset='localtime'/>
@@ -6,6 +6,7 @@
<vcpu placement='static'>1</vcpu>
<os>
<type arch='aarch64'>hvm</type>
<loader readonly='yes' type='pflash'>fakeubootpath/u-boot.bin</loader>
<boot dev='hd'/>
</os>
<clock offset='localtime'/>
+7
View File
@@ -5,6 +5,7 @@
#ifdef WITH_BHYVE
# include "bhyve/bhyve_capabilities.h"
# include "bhyve/bhyve_conf.h"
# include "bhyve/bhyve_domain.h"
# include "bhyve/bhyve_utils.h"
@@ -55,6 +56,7 @@ testCompareXMLToXMLHelper(const void *data)
static int
mymain(void)
{
g_autofree char *fakeubootpath = g_strdup("fakeubootpath/u-boot.bin");
int ret = 0;
if ((driver.caps = virBhyveCapsBuild()) == NULL)
@@ -63,6 +65,11 @@ mymain(void)
if ((driver.xmlopt = virBhyveDriverCreateXMLConf(&driver)) == NULL)
return EXIT_FAILURE;
if (!(driver.config = virBhyveDriverConfigNew()))
return EXIT_FAILURE;
driver.config->ubootPath = fakeubootpath;
# define DO_TEST_FULL(name, flags) \
do { \
const struct testInfo info = {name, (flags)}; \