<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>
...</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.
"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);
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;
}
static int
-virDomainDefValidateInternal(const virDomainDef *def)
+virDomainDefValidateInternal(const virDomainDef *def,
+ virDomainXMLOptionPtr xmlopt)
{
if (virDomainDefCheckDuplicateDiskInfo(def) < 0)
return -1;
if (virDomainDefMemtuneValidate(def) < 0)
return -1;
- if (virDomainDefOSValidate(def) < 0)
+ if (virDomainDefOSValidate(def, xmlopt) < 0)
return -1;
return 0;
&data) < 0)
return -1;
- if (virDomainDefValidateInternal(def) < 0)
+ if (virDomainDefValidateInternal(def, xmlopt) < 0)
return -1;
return 0;
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) {
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);
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);
}
}
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)