diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index 949e2fc1b4..a0b3632b7e 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -128,7 +128,7 @@
...
-<os>
+<os firmware='uefi'>
<type>hvm</type>
<loader readonly='yes' secure='no' type='rom'>/usr/lib/xen/boot/hvmloader</loader>
<nvram template='/usr/share/OVMF/OVMF_VARS.fd'>/var/lib/libvirt/nvram/guest_VARS.fd</nvram>
@@ -141,6 +141,26 @@
...
+ firmware
+ - The
firmware
attribute allows management
+ applications to automatically fill <loader/>
+ and <nvram/>
elements and possibly enable
+ some features required by selected firmware. Accepted values are
+ bios
and efi
.
+ The selection process scans for files describing installed
+ firmware images in specified location and uses the most specific
+ one which fulfils domain requirements. The locations in order of
+ preference (from generic to most specific one) are:
+
+ /usr/share/qemu/firmware
+ /etc/qemu/firmware
+ $XDG_CONFIG_HOME/qemu/firmware
+
+ For more information refer to firmware metadata specification as
+ described in docs/interop/firmware.json
in QEMU
+ repository. Regular users do not need to bother.
+ Since 5.2.0 (QEMU and KVM only)
+
type
- The content of the
type
element specifies the
type of operating system to be booted in the virtual machine.
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index c8d63c4912..87ba9daeda 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -256,6 +256,14 @@
+
+
+
+ bios
+ efi
+
+
+
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 28f4ecdced..778c386ee2 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -1079,6 +1079,13 @@ VIR_ENUM_IMPL(virDomainHPTResizing,
"required",
);
+VIR_ENUM_IMPL(virDomainOsDefFirmware,
+ VIR_DOMAIN_OS_DEF_FIRMWARE_LAST,
+ "none",
+ "bios",
+ "efi",
+);
+
/* Internal mapping: subset of block job types that can be present in
* XML (remaining types are not two-phase). */
VIR_ENUM_DECL(virDomainBlockJob);
@@ -6608,14 +6615,23 @@ virDomainDefMemtuneValidate(const virDomainDef *def)
static int
-virDomainDefOSValidate(const virDomainDef *def)
+virDomainDefOSValidate(const virDomainDef *def,
+ virDomainXMLOptionPtr xmlopt)
{
if (!def->os.loader)
return 0;
- if (!def->os.loader->path) {
+ if (def->os.firmware &&
+ !(xmlopt->config.features & VIR_DOMAIN_DEF_FEATURE_FW_AUTOSELECT)) {
virReportError(VIR_ERR_XML_DETAIL, "%s",
- _("no loader path specified"));
+ _("firmware auto selection not implemented for this driver"));
+ return -1;
+ }
+
+ if (!def->os.loader->path &&
+ def->os.firmware == VIR_DOMAIN_OS_DEF_FIRMWARE_NONE) {
+ virReportError(VIR_ERR_XML_DETAIL, "%s",
+ _("no loader path specified and firmware auto selection disabled"));
return -1;
}
@@ -6624,7 +6640,8 @@ virDomainDefOSValidate(const virDomainDef *def)
static int
-virDomainDefValidateInternal(const virDomainDef *def)
+virDomainDefValidateInternal(const virDomainDef *def,
+ virDomainXMLOptionPtr xmlopt)
{
if (virDomainDefCheckDuplicateDiskInfo(def) < 0)
return -1;
@@ -6661,7 +6678,7 @@ virDomainDefValidateInternal(const virDomainDef *def)
if (virDomainDefMemtuneValidate(def) < 0)
return -1;
- if (virDomainDefOSValidate(def) < 0)
+ if (virDomainDefOSValidate(def, xmlopt) < 0)
return -1;
return 0;
@@ -6712,7 +6729,7 @@ virDomainDefValidate(virDomainDefPtr def,
&data) < 0)
return -1;
- if (virDomainDefValidateInternal(def) < 0)
+ if (virDomainDefValidateInternal(def, xmlopt) < 0)
return -1;
return 0;
@@ -18267,19 +18284,22 @@ virDomainDefMaybeAddHostdevSCSIcontroller(virDomainDefPtr def)
static int
virDomainLoaderDefParseXML(xmlNodePtr node,
- virDomainLoaderDefPtr loader)
+ virDomainLoaderDefPtr loader,
+ bool fwAutoSelect)
{
VIR_AUTOFREE(char *) readonly_str = NULL;
VIR_AUTOFREE(char *) secure_str = NULL;
VIR_AUTOFREE(char *) type_str = NULL;
- readonly_str = virXMLPropString(node, "readonly");
secure_str = virXMLPropString(node, "secure");
- type_str = virXMLPropString(node, "type");
- loader->path = (char *) xmlNodeGetContent(node);
- if (STREQ_NULLABLE(loader->path, ""))
- VIR_FREE(loader->path);
+ if (!fwAutoSelect) {
+ readonly_str = virXMLPropString(node, "readonly");
+ type_str = virXMLPropString(node, "type");
+ loader->path = (char *) xmlNodeGetContent(node);
+ if (STREQ_NULLABLE(loader->path, ""))
+ VIR_FREE(loader->path);
+ }
if (readonly_str &&
(loader->readonly = virTristateBoolTypeFromString(readonly_str)) <= 0) {
@@ -18674,6 +18694,7 @@ virDomainDefParseBootOptions(virDomainDefPtr def,
def->os.type == VIR_DOMAIN_OSTYPE_XENPVH ||
def->os.type == VIR_DOMAIN_OSTYPE_HVM ||
def->os.type == VIR_DOMAIN_OSTYPE_UML) {
+ VIR_AUTOFREE(char *) firmware = NULL;
xmlNodePtr loader_node;
def->os.kernel = virXPathString("string(./os/kernel[1])", ctxt);
@@ -18681,15 +18702,35 @@ virDomainDefParseBootOptions(virDomainDefPtr def,
def->os.cmdline = virXPathString("string(./os/cmdline[1])", ctxt);
def->os.dtb = virXPathString("string(./os/dtb[1])", ctxt);
def->os.root = virXPathString("string(./os/root[1])", ctxt);
+
+ if (def->os.type == VIR_DOMAIN_OSTYPE_HVM &&
+ (firmware = virXPathString("string(./os/@firmware)", ctxt))) {
+ int fw = virDomainOsDefFirmwareTypeFromString(firmware);
+
+ if (fw <= 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("unknown firmware value %s"),
+ firmware);
+ return -1;
+ }
+
+ def->os.firmware = fw;
+ }
+
if ((loader_node = virXPathNode("./os/loader[1]", ctxt))) {
+ const bool fwAutoSelect = def->os.firmware != VIR_DOMAIN_OS_DEF_FIRMWARE_NONE;
+
if (VIR_ALLOC(def->os.loader) < 0)
return -1;
- if (virDomainLoaderDefParseXML(loader_node, def->os.loader) < 0)
+ if (virDomainLoaderDefParseXML(loader_node,
+ def->os.loader,
+ fwAutoSelect) < 0)
return -1;
def->os.loader->nvram = virXPathString("string(./os/nvram[1])", ctxt);
- def->os.loader->templt = virXPathString("string(./os/nvram[1]/@template)", ctxt);
+ if (!fwAutoSelect)
+ def->os.loader->templt = virXPathString("string(./os/nvram[1]/@template)", ctxt);
}
}
@@ -28078,7 +28119,11 @@ virDomainDefFormatInternal(virDomainDefPtr def,
def->os.bootloaderArgs);
}
- virBufferAddLit(buf, "\n");
+ virBufferAddLit(buf, "os.firmware)
+ virBufferAsprintf(buf, " firmware='%s'",
+ virDomainOsDefFirmwareTypeToString(def->os.firmware));
+ virBufferAddLit(buf, ">\n");
virBufferAdjustIndent(buf, 2);
virBufferAddLit(buf, "os.arch)
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index ed5d00c399..fe9d4fb81a 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -2005,10 +2005,21 @@ struct _virDomainOSEnv {
char *value;
};
+typedef enum {
+ VIR_DOMAIN_OS_DEF_FIRMWARE_NONE = 0,
+ VIR_DOMAIN_OS_DEF_FIRMWARE_BIOS,
+ VIR_DOMAIN_OS_DEF_FIRMWARE_EFI,
+
+ VIR_DOMAIN_OS_DEF_FIRMWARE_LAST
+} virDomainOsDefFirmware;
+
+VIR_ENUM_DECL(virDomainOsDefFirmware);
+
typedef struct _virDomainOSDef virDomainOSDef;
typedef virDomainOSDef *virDomainOSDefPtr;
struct _virDomainOSDef {
int type;
+ virDomainOsDefFirmware firmware;
virArch arch;
char *machine;
size_t nBootDevs;
@@ -2729,6 +2740,7 @@ typedef enum {
VIR_DOMAIN_DEF_FEATURE_INDIVIDUAL_VCPUS = (1 << 4),
VIR_DOMAIN_DEF_FEATURE_USER_ALIAS = (1 << 5),
VIR_DOMAIN_DEF_FEATURE_NO_BOOT_ORDER = (1 << 6),
+ VIR_DOMAIN_DEF_FEATURE_FW_AUTOSELECT = (1 << 7),
} virDomainDefFeatures;
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 86f639a9c1..960a97cf1d 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -510,6 +510,8 @@ virDomainObjTaint;
virDomainObjUpdateModificationImpact;
virDomainObjWait;
virDomainObjWaitUntil;
+virDomainOsDefFirmwareTypeFromString;
+virDomainOsDefFirmwareTypeToString;
virDomainOSTypeFromString;
virDomainOSTypeToString;
virDomainParseMemory;