提交 3dda889a 编写于 作者: M Michal Privoznik

conf: Add firmware blob configuration

QEMU has -fw_cfg which allows users to tweak how firmware
configures itself and/or provide new configuration blobs.
Introduce new <sysinfo/> type "fwcfg" that will hold these
new blobs.

It's possible to either specify new value as a string or
provide a filename which contents then serve as the value.
Signed-off-by: NMichal Privoznik <mprivozn@redhat.com>
Reviewed-by: NDaniel P. Berrangé <berrange@redhat.com>
上级 b44898dd
......@@ -479,6 +479,10 @@
&lt;entry&gt;otherappname:more arbitrary data&lt;/entry&gt;
&lt;/oemStrings&gt;
&lt;/sysinfo&gt;
&lt;sysinfo type='fwcfg'&gt;
&lt;entry name='opt/com.example/name'&gt;example value&lt;/entry&gt;
&lt;entry name='opt/com.coreos/config' file='/tmp/provision.ign'/&gt;
&lt;/sysinfo&gt;
...</pre>
<p>
......@@ -593,6 +597,34 @@
</dd>
</dl>
</dd>
<dt><code>fwcfg</code></dt>
<dd>
Some hypervisors provide unified way to tweak how firmware configures
itself, or may contain tables to be installed for the guest OS, for
instance boot order, ACPI, SMBIOS, etc. It even allows users to define
their own config blobs. In case of QEMU, these then appear under domain's
sysfs, under <code>/sys/firmware/qemu_fw_cfg</code>. Note, that these
values apply regardless the &lt;smbios/&gt; mode under &lt;os/&gt;.
<span class="since">Since 6.5.0</span>
<pre>
&lt;smbios type='fwcfg'&gt;
&lt;entry name='opt/com.example/name'&gt;example value&lt;/entry&gt;
&lt;entry name='opt/com.coreos/config' file='/tmp/provision.ign'/&gt;
&lt;/smbios&gt;
</pre>
The <code>smbios</code> element can have multiple <code>entry</code>
child elements. Each element then has mandatory <code>name</code>
attribute, which defines the name of the blob and must begin with
<code>"opt/"</code> and to avoid clashing with other names is advised to
be in form <code>"opt/$RFQDN/$name"</code> where <code>$RFQDN</code> is a
reverse fully qualified domain name you control.
Then, the element can either contain the value (to set the blob value
directly), or <code>file</code> attribute (to set the blob value from
the file).
</dd>
</dl>
<h3><a id="elementsCPUAllocation">CPU Allocation</a></h3>
......
......@@ -46,9 +46,9 @@
<optional>
<ref name="cpu"/>
</optional>
<optional>
<zeroOrMore>
<ref name="sysinfo"/>
</optional>
</zeroOrMore>
<ref name="os"/>
<ref name="clock"/>
<ref name="resources"/>
......@@ -5511,68 +5511,95 @@
-->
<define name="sysinfo">
<element name="sysinfo">
<attribute name="type">
<value>smbios</value>
</attribute>
<interleave>
<optional>
<element name="bios">
<oneOrMore>
<element name="entry">
<attribute name="name">
<ref name="sysinfo-bios-name"/>
</attribute>
<ref name="sysinfo-value"/>
<choice>
<group>
<attribute name="type">
<value>smbios</value>
</attribute>
<interleave>
<optional>
<element name="bios">
<oneOrMore>
<element name="entry">
<attribute name="name">
<ref name="sysinfo-bios-name"/>
</attribute>
<ref name="sysinfo-value"/>
</element>
</oneOrMore>
</element>
</oneOrMore>
</element>
</optional>
<optional>
<element name="system">
<oneOrMore>
<element name="entry">
<attribute name="name">
<ref name="sysinfo-system-name"/>
</attribute>
<ref name="sysinfo-value"/>
</optional>
<optional>
<element name="system">
<oneOrMore>
<element name="entry">
<attribute name="name">
<ref name="sysinfo-system-name"/>
</attribute>
<ref name="sysinfo-value"/>
</element>
</oneOrMore>
</element>
</oneOrMore>
</element>
</optional>
<zeroOrMore>
<element name="baseBoard">
<oneOrMore>
<element name="entry">
<attribute name="name">
<ref name="sysinfo-baseBoard-name"/>
</attribute>
<ref name="sysinfo-value"/>
</optional>
<zeroOrMore>
<element name="baseBoard">
<oneOrMore>
<element name="entry">
<attribute name="name">
<ref name="sysinfo-baseBoard-name"/>
</attribute>
<ref name="sysinfo-value"/>
</element>
</oneOrMore>
</element>
</oneOrMore>
</element>
</zeroOrMore>
<optional>
<element name="chassis">
<oneOrMore>
<element name="entry">
<attribute name="name">
<ref name="sysinfo-chassis-name"/>
</attribute>
<ref name="sysinfo-value"/>
</zeroOrMore>
<optional>
<element name="chassis">
<oneOrMore>
<element name="entry">
<attribute name="name">
<ref name="sysinfo-chassis-name"/>
</attribute>
<ref name="sysinfo-value"/>
</element>
</oneOrMore>
</element>
</oneOrMore>
</element>
</optional>
<optional>
<element name="oemStrings">
<oneOrMore>
<element name="entry">
<ref name="sysinfo-value"/>
</optional>
<optional>
<element name="oemStrings">
<oneOrMore>
<element name="entry">
<ref name="sysinfo-value"/>
</element>
</oneOrMore>
</element>
</oneOrMore>
</element>
</optional>
</interleave>
</optional>
</interleave>
</group>
<group>
<attribute name="type">
<value>fwcfg</value>
</attribute>
<zeroOrMore>
<element name="entry">
<attribute name="name">
<data type="string"/>
</attribute>
<choice>
<group>
<attribute name="file">
<data type="string"/>
</attribute>
<empty/>
</group>
<group>
<ref name="sysinfo-value"/>
</group>
</choice>
</element>
</zeroOrMore>
</group>
</choice>
</element>
</define>
......
......@@ -3551,7 +3551,9 @@ void virDomainDefFree(virDomainDefPtr def)
virDomainNumaFree(def->numa);
virSysinfoDefFree(def->sysinfo);
for (i = 0; i < def->nsysinfo; i++)
virSysinfoDefFree(def->sysinfo[i]);
VIR_FREE(def->sysinfo);
virDomainRedirFilterDefFree(def->redirfilter);
......@@ -15708,16 +15710,115 @@ virSysinfoChassisParseXML(xmlNodePtr node,
}
static int
virSysinfoParseSMBIOSDef(virSysinfoDefPtr def,
xmlXPathContextPtr ctxt,
unsigned char *domUUID,
bool uuid_generated)
{
xmlNodePtr tmpnode;
/* Extract BIOS related metadata */
if ((tmpnode = virXPathNode("./bios[1]", ctxt)) != NULL) {
if (virSysinfoBIOSParseXML(tmpnode, ctxt, &def->bios) < 0)
return -1;
}
/* Extract system related metadata */
if ((tmpnode = virXPathNode("./system[1]", ctxt)) != NULL) {
if (virSysinfoSystemParseXML(tmpnode, ctxt, &def->system,
domUUID, uuid_generated) < 0)
return -1;
}
/* Extract system base board metadata */
if (virSysinfoBaseBoardParseXML(ctxt, &def->baseBoard, &def->nbaseBoard) < 0)
return -1;
/* Extract chassis related metadata */
if ((tmpnode = virXPathNode("./chassis[1]", ctxt)) != NULL) {
if (virSysinfoChassisParseXML(tmpnode, ctxt, &def->chassis) < 0)
return -1;
}
/* Extract system related metadata */
if ((tmpnode = virXPathNode("./oemStrings[1]", ctxt)) != NULL) {
if (virSysinfoOEMStringsParseXML(tmpnode, ctxt, &def->oemStrings) < 0)
return -1;
}
return 0;
}
static int
virSysinfoParseFWCfgDef(virSysinfoDefPtr def,
xmlNodePtr node,
xmlXPathContextPtr ctxt)
{
VIR_XPATH_NODE_AUTORESTORE(ctxt);
g_autofree xmlNodePtr *nodes = NULL;
int n;
size_t i;
ctxt->node = node;
if ((n = virXPathNodeSet("./entry", ctxt, &nodes)) < 0)
return -1;
if (n == 0)
return 0;
def->fw_cfgs = g_new0(virSysinfoFWCfgDef, n);
for (i = 0; i < n; i++) {
g_autofree char *name = NULL;
g_autofree char *value = NULL;
g_autofree char *file = NULL;
g_autofree char *sanitizedFile = NULL;
if (!(name = virXMLPropString(nodes[i], "name"))) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("Firmware entry is missing 'name' attribute"));
return -1;
}
value = virXMLNodeContentString(nodes[i]);
file = virXMLPropString(nodes[i], "file");
if (virStringIsEmpty(value))
VIR_FREE(value);
if (!value && !file) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("Firmware entry must have either value or "
"'file' attribute"));
return -1;
}
if (file)
sanitizedFile = virFileSanitizePath(file);
def->fw_cfgs[i].name = g_steal_pointer(&name);
def->fw_cfgs[i].value = g_steal_pointer(&value);
def->fw_cfgs[i].file = g_steal_pointer(&sanitizedFile);
def->nfw_cfgs++;
}
return 0;
}
static virSysinfoDefPtr
virSysinfoParseXML(xmlNodePtr node,
xmlXPathContextPtr ctxt,
unsigned char *domUUID,
bool uuid_generated)
xmlXPathContextPtr ctxt,
unsigned char *domUUID,
bool uuid_generated)
{
VIR_XPATH_NODE_AUTORESTORE(ctxt);
virSysinfoDefPtr def;
xmlNodePtr tmpnode;
g_autofree char *type = NULL;
g_autofree char *typeStr = NULL;
int type;
ctxt->node = node;
......@@ -15730,45 +15831,32 @@ virSysinfoParseXML(xmlNodePtr node,
if (VIR_ALLOC(def) < 0)
return NULL;
type = virXMLPropString(node, "type");
if (type == NULL) {
typeStr = virXMLPropString(node, "type");
if (typeStr == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("sysinfo must contain a type attribute"));
goto error;
}
if ((def->type = virSysinfoTypeFromString(type)) < 0) {
if ((type = virSysinfoTypeFromString(typeStr)) < 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unknown sysinfo type '%s'"), type);
_("unknown sysinfo type '%s'"), typeStr);
goto error;
}
def->type = type;
/* Extract BIOS related metadata */
if ((tmpnode = virXPathNode("./bios[1]", ctxt)) != NULL) {
if (virSysinfoBIOSParseXML(tmpnode, ctxt, &def->bios) < 0)
goto error;
}
/* Extract system related metadata */
if ((tmpnode = virXPathNode("./system[1]", ctxt)) != NULL) {
if (virSysinfoSystemParseXML(tmpnode, ctxt, &def->system,
domUUID, uuid_generated) < 0)
switch (def->type) {
case VIR_SYSINFO_SMBIOS:
if (virSysinfoParseSMBIOSDef(def, ctxt, domUUID, uuid_generated) < 0)
goto error;
}
/* Extract system base board metadata */
if (virSysinfoBaseBoardParseXML(ctxt, &def->baseBoard, &def->nbaseBoard) < 0)
goto error;
break;
/* Extract chassis related metadata */
if ((tmpnode = virXPathNode("./chassis[1]", ctxt)) != NULL) {
if (virSysinfoChassisParseXML(tmpnode, ctxt, &def->chassis) < 0)
case VIR_SYSINFO_FWCFG:
if (virSysinfoParseFWCfgDef(def, node, ctxt) < 0)
goto error;
}
break;
/* Extract system related metadata */
if ((tmpnode = virXPathNode("./oemStrings[1]", ctxt)) != NULL) {
if (virSysinfoOEMStringsParseXML(tmpnode, ctxt, &def->oemStrings) < 0)
goto error;
case VIR_SYSINFO_LAST:
break;
}
return def;
......@@ -22173,6 +22261,7 @@ virDomainDefParseXML(xmlDocPtr xml,
def->idmap.ngidmap = n;
}
VIR_FREE(nodes);
if ((def->idmap.uidmap && !def->idmap.gidmap) ||
(!def->idmap.uidmap && def->idmap.gidmap)) {
......@@ -22181,13 +22270,21 @@ virDomainDefParseXML(xmlDocPtr xml,
goto error;
}
if ((node = virXPathNode("./sysinfo[1]", ctxt)) != NULL) {
def->sysinfo = virSysinfoParseXML(node, ctxt,
def->uuid, uuid_generated);
if ((n = virXPathNodeSet("./sysinfo", ctxt, &nodes)) < 0)
goto error;
def->sysinfo = g_new0(virSysinfoDefPtr, n);
if (def->sysinfo == NULL)
for (i = 0; i < n; i++) {
virSysinfoDefPtr sysinfo = virSysinfoParseXML(nodes[i], ctxt,
def->uuid, uuid_generated);
if (!sysinfo)
goto error;
def->sysinfo[def->nsysinfo++] = sysinfo;
}
VIR_FREE(nodes);
if ((tmp = virXPathString("string(./os/smbios/@mode)", ctxt))) {
int mode;
......@@ -24072,8 +24169,16 @@ virDomainDefCheckABIStabilityFlags(virDomainDefPtr src,
if (!virCPUDefIsEqual(src->cpu, dst->cpu, true))
goto error;
if (!virSysinfoIsEqual(src->sysinfo, dst->sysinfo))
goto error;
if (src->nsysinfo != dst->nsysinfo) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Target domain count of sysinfo does not match source"));
goto error;
}
for (i = 0; i < src->nsysinfo; i++) {
if (!virSysinfoIsEqual(src->sysinfo[i], dst->sysinfo[i]))
goto error;
}
if (src->ndisks != dst->ndisks) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
......@@ -29507,8 +29612,8 @@ virDomainDefFormatInternalSetRootName(virDomainDefPtr def,
if (def->resource)
virDomainResourceDefFormat(buf, def->resource);
if (def->sysinfo)
ignore_value(virSysinfoFormat(buf, def->sysinfo));
for (i = 0; i < def->nsysinfo; i++)
virSysinfoFormat(buf, def->sysinfo[i]);
if (def->os.bootloader) {
virBufferEscapeString(buf, "<bootloader>%s</bootloader>\n",
......@@ -2624,13 +2624,15 @@ struct _virDomainDef {
size_t npanics;
virDomainPanicDefPtr *panics;
size_t nsysinfo;
virSysinfoDefPtr *sysinfo;
/* Only 1 */
virDomainWatchdogDefPtr watchdog;
virDomainMemballoonDefPtr memballoon;
virDomainNVRAMDefPtr nvram;
virDomainTPMDefPtr tpm;
virCPUDefPtr cpu;
virSysinfoDefPtr sysinfo;
virDomainRedirFilterDefPtr redirfilter;
virDomainIOMMUDefPtr iommu;
virDomainVsockDefPtr vsock;
......
......@@ -5736,13 +5736,19 @@ qemuBuildSmbiosCommandLine(virCommandPtr cmd,
/* Host and guest uuid must differ, by definition of UUID. */
skip_uuid = true;
} else if (def->os.smbios_mode == VIR_DOMAIN_SMBIOS_SYSINFO) {
if (def->sysinfo == NULL) {
for (i = 0; i < def->nsysinfo; i++) {
if (def->sysinfo[i]->type == VIR_SYSINFO_SMBIOS) {
source = def->sysinfo[i];
break;
}
}
if (!source) {
virReportError(VIR_ERR_XML_ERROR,
_("Domain '%s' sysinfo are not available"),
def->name);
return -1;
}
source = def->sysinfo;
/* domain_conf guaranteed that system_uuid matches guest uuid. */
}
if (source != NULL) {
......
......@@ -43,6 +43,7 @@ VIR_LOG_INIT("util.sysinfo");
VIR_ENUM_IMPL(virSysinfo,
VIR_SYSINFO_LAST,
"smbios",
"fwcfg"
);
static const char *sysinfoSysinfo = "/proc/sysinfo";
......@@ -1513,6 +1514,40 @@ virSysinfoOEMStringsFormat(virBufferPtr buf, virSysinfoOEMStringsDefPtr def)
virBufferAddLit(buf, "</oemStrings>\n");
}
static void
virSysinfoFormatSMBIOS(virBufferPtr buf,
virSysinfoDefPtr def)
{
virSysinfoBIOSFormat(buf, def->bios);
virSysinfoSystemFormat(buf, def->system);
virSysinfoBaseBoardFormat(buf, def->baseBoard, def->nbaseBoard);
virSysinfoChassisFormat(buf, def->chassis);
virSysinfoProcessorFormat(buf, def);
virSysinfoMemoryFormat(buf, def);
virSysinfoOEMStringsFormat(buf, def->oemStrings);
}
static void
virSysinfoFormatFWCfg(virBufferPtr buf,
virSysinfoDefPtr def)
{
size_t i;
for (i = 0; i < def->nfw_cfgs; i++) {
const virSysinfoFWCfgDef *f = &def->fw_cfgs[i];
virBufferAsprintf(buf, "<entry name='%s'", f->name);
if (f->file)
virBufferEscapeString(buf, " file='%s'/>\n", f->file);
else
virBufferEscapeString(buf, ">%s</entry>\n", f->value);
}
}
/**
* virSysinfoFormat:
* @buf: buffer to append output to (may use auto-indentation)
......@@ -1535,13 +1570,16 @@ virSysinfoFormat(virBufferPtr buf, virSysinfoDefPtr def)
return -1;
}
virSysinfoBIOSFormat(&childrenBuf, def->bios);
virSysinfoSystemFormat(&childrenBuf, def->system);
virSysinfoBaseBoardFormat(&childrenBuf, def->baseBoard, def->nbaseBoard);
virSysinfoChassisFormat(&childrenBuf, def->chassis);
virSysinfoProcessorFormat(&childrenBuf, def);
virSysinfoMemoryFormat(&childrenBuf, def);
virSysinfoOEMStringsFormat(&childrenBuf, def->oemStrings);
switch (def->type) {
case VIR_SYSINFO_SMBIOS:
virSysinfoFormatSMBIOS(&childrenBuf, def);
break;
case VIR_SYSINFO_FWCFG:
virSysinfoFormatFWCfg(&childrenBuf, def);
break;
case VIR_SYSINFO_LAST:
break;
}
virBufferAsprintf(&attrBuf, " type='%s'", type);
......
......@@ -27,6 +27,7 @@
typedef enum {
VIR_SYSINFO_SMBIOS,
VIR_SYSINFO_FWCFG,
VIR_SYSINFO_LAST
} virSysinfoType;
......@@ -112,11 +113,20 @@ struct _virSysinfoOEMStringsDef {
char **values;
};
typedef struct _virSysinfoFWCfgDef virSysinfoFWCfgDef;
typedef virSysinfoFWCfgDef *virSysinfoFWCfgDefPtr;
struct _virSysinfoFWCfgDef {
char *name;
char *value;
char *file;
};
typedef struct _virSysinfoDef virSysinfoDef;
typedef virSysinfoDef *virSysinfoDefPtr;
struct _virSysinfoDef {
int type;
virSysinfoType type;
/* The following members are valid for type == VIR_SYSINFO_SMBIOS */
virSysinfoBIOSDefPtr bios;
virSysinfoSystemDefPtr system;
......@@ -132,6 +142,10 @@ struct _virSysinfoDef {
virSysinfoMemoryDefPtr memory;
virSysinfoOEMStringsDefPtr oemStrings;
/* The following members are valid for type == VIR_SYSINFO_FWCFG */
size_t nfw_cfgs;
virSysinfoFWCfgDefPtr fw_cfgs;
};
virSysinfoDefPtr virSysinfoRead(void);
......
<domain type='qemu'>
<name>QEMUGuest1</name>
<uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
<memory unit='KiB'>219100</memory>
<currentMemory unit='KiB'>219100</currentMemory>
<vcpu placement='static'>1</vcpu>
<sysinfo type='smbios'>
<bios>
<entry name='vendor'>LENOVO</entry>
<entry name='version'>6FET82WW (3.12 )</entry>
</bios>
<system>
<entry name='manufacturer'>Fedora</entry>
<entry name='product'>Virt-Manager</entry>
<entry name='version'>0.8.2-3.fc14</entry>
<entry name='serial'>32dfcb37-5af1-552b-357c-be8c3aa38310</entry>
<entry name='uuid'>c7a5fdbd-edaf-9455-926a-d65c16db1809</entry>
<entry name='sku'>1234567890</entry>
<entry name='family'>Red Hat</entry>
</system>
<baseBoard>
<entry name='manufacturer'>Lenovo</entry>
<entry name='product'>20BE0061MC</entry>
<entry name='version'>0B98401 Pro</entry>
<entry name='serial'>W1KS427111E</entry>
<entry name='location'>Not Available</entry>
</baseBoard>
</sysinfo>
<sysinfo type='fwcfg'>
<entry name='opt/com.example/name'>example value</entry>
<entry name='opt/com.coreos/config' file='/tmp/provision.ign'/>
</sysinfo>
<os>
<type arch='i686' machine='pc'>hvm</type>
<boot dev='hd'/>
<smbios mode='sysinfo'/>
</os>
<clock offset='utc'/>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>destroy</on_crash>
<devices>
<emulator>/usr/bin/qemu-system-i386</emulator>
<disk type='block' device='disk'>
<driver name='qemu' type='raw'/>
<source dev='/dev/HostVG/QEMUGuest1'/>
<target dev='hda' bus='ide'/>
<address type='drive' controller='0' bus='0' target='0' unit='0'/>
</disk>
<controller type='ide' index='0'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
</controller>
<controller type='usb' index='0'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
</controller>
<controller type='pci' index='0' model='pci-root'/>
<input type='mouse' bus='ps2'/>
<input type='keyboard' bus='ps2'/>
<memballoon model='virtio'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
</memballoon>
</devices>
</domain>
../qemuxml2argvdata/smbios-type-fwcfg.xml
\ No newline at end of file
......@@ -1125,6 +1125,7 @@ mymain(void)
DO_TEST("shmem-plain-doorbell", NONE);
DO_TEST("smbios", NONE);
DO_TEST("smbios-multiple-type2", NONE);
DO_TEST("smbios-type-fwcfg", NONE);
DO_TEST_CAPS_LATEST("os-firmware-bios");
DO_TEST_CAPS_LATEST("os-firmware-efi");
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册