The idea is that using this attribute users enable libvirt to
automagically select firmware image for their domain. For
instance:
<os firmware='efi'>
<type arch='x86_64' machine='pc-q35-4.0'>hvm</type>
<loader secure='no'/>
</os>
<os firmware='bios'>
<type arch='x86_64' machine='pc-q35-4.0'>hvm</type>
</os>
(The automagic of selecting firmware image will be described in
later commits.)
Accepted values are 'bios' and 'efi' to let libvirt select
corresponding type of firmware.
I know it is a good sign to introduce xml2xml test case when
changing XML config parser but that will have to come later.
Firmware auto selection is not enabled for any driver just yet so
any xml2xml test would fail right away.
Signed-off-by: Michal Privoznik <mprivozn(a)redhat.com>
---
docs/formatdomain.html.in | 22 +++++++++-
docs/schemas/domaincommon.rng | 8 ++++
src/conf/domain_conf.c | 75 ++++++++++++++++++++++++++++-------
src/conf/domain_conf.h | 12 ++++++
src/libvirt_private.syms | 2 +
5 files changed, 103 insertions(+), 16 deletions(-)
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index b848e535e6..45f7af0e8b 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -128,7 +128,7 @@
<pre>
...
-<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 @@
...</pre>
<dl>
+ <dt><code>firmware</code></dt>
+ <dd>The <code>firmware</code> attribute allows management
+ applications to automatically fill
<code><loader/></code>
+ and <code><nvram/></code> elements and possibly enable
+ some features required by selected firmware. Accepted values are
+ <code>bios</code> and <code>efi</code>.<br/>
+ 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:
+ <ul>
+ <li><code>/usr/share/qemu/firmware</code></li>
+ <li><code>/etc/qemu/firmware</code></li>
+ <li><code>$XDG_CONFIG_HOME/qemu/firmware</code></li>
+ </ul>
+ For more information refer to firmware metadata specification as
+ described in <code>docs/interop/firmware.json</code> in QEMU
+ repository. Regular users do not need to bother.
+ <span class="since">Since 5.2.0 (QEMU and KVM only)</span>
+ </dd>
<dt><code>type</code></dt>
<dd>The content of the <code>type</code> 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 bebe39de76..7b858f2685 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -256,6 +256,14 @@
</optional>
<element name="os">
<interleave>
+ <optional>
+ <attribute name="firmware">
+ <choice>
+ <value>bios</value>
+ <value>efi</value>
+ </choice>
+ </attribute>
+ </optional>
<ref name="ostypehvm"/>
<optional>
<element name="loader">
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index b436b91c66..0ca923b505 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -1033,6 +1033,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
* <mirror> XML (remaining types are not two-phase). */
VIR_ENUM_DECL(virDomainBlockJob);
@@ -6582,14 +6589,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;
}
@@ -6598,7 +6614,8 @@ virDomainDefOSValidate(const virDomainDef *def)
static int
-virDomainDefValidateInternal(const virDomainDef *def)
+virDomainDefValidateInternal(const virDomainDef *def,
+ virDomainXMLOptionPtr xmlopt)
{
if (virDomainDefCheckDuplicateDiskInfo(def) < 0)
return -1;
@@ -6635,7 +6652,7 @@ virDomainDefValidateInternal(const virDomainDef *def)
if (virDomainDefMemtuneValidate(def) < 0)
return -1;
- if (virDomainDefOSValidate(def) < 0)
+ if (virDomainDefOSValidate(def, xmlopt) < 0)
return -1;
return 0;
@@ -6686,7 +6703,7 @@ virDomainDefValidate(virDomainDefPtr def,
&data) < 0)
return -1;
- if (virDomainDefValidateInternal(def) < 0)
+ if (virDomainDefValidateInternal(def, xmlopt) < 0)
return -1;
return 0;
@@ -18692,20 +18709,23 @@ virDomainDefMaybeAddHostdevSCSIcontroller(virDomainDefPtr def)
static int
virDomainLoaderDefParseXML(xmlNodePtr node,
- virDomainLoaderDefPtr loader)
+ virDomainLoaderDefPtr loader,
+ bool fwAutoSelect)
{
int ret = -1;
char *readonly_str = NULL;
char *secure_str = NULL;
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) {
@@ -19120,6 +19140,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);
@@ -19127,15 +19148,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);
+ goto error;
+ }
+
+ 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)
goto error;
- if (virDomainLoaderDefParseXML(loader_node, def->os.loader) < 0)
+ if (virDomainLoaderDefParseXML(loader_node,
+ def->os.loader,
+ fwAutoSelect) < 0)
goto error;
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);
}
}
@@ -28459,7 +28500,11 @@ virDomainDefFormatInternal(virDomainDefPtr def,
def->os.bootloaderArgs);
}
- virBufferAddLit(buf, "<os>\n");
+ virBufferAddLit(buf, "<os");
+ if (def->os.firmware)
+ virBufferAsprintf(buf, " firmware='%s'",
+ virDomainOsDefFirmwareTypeToString(def->os.firmware));
+ virBufferAddLit(buf, ">\n");
virBufferAdjustIndent(buf, 2);
virBufferAddLit(buf, "<type");
if (def->os.arch)
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 4e8c02b5e3..c1e7845cf8 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -1950,10 +1950,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;
@@ -2670,6 +2681,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 038a744981..fdd67a7fde 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -504,6 +504,8 @@ virDomainObjTaint;
virDomainObjUpdateModificationImpact;
virDomainObjWait;
virDomainObjWaitUntil;
+virDomainOsDefFirmwareTypeFromString;
+virDomainOsDefFirmwareTypeToString;
virDomainOSTypeFromString;
virDomainOSTypeToString;
virDomainParseMemory;
--
2.19.2