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: + + 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;