The legacy syntax can only describe stateless firmware builds,
while the extended one can additionally describe split builds
where an NVRAM file is used for variable storage.
The extended syntax is basically identical to the one that we
already have to support for flash firmware descriptors. It's a
strict superset of the legacy one, so we can always store the
former one internally and not worry about the differences past
the parse phase. Some of the output files for qemufirmwaretest
are updated as a consequence of this, but the changes are not
semantically meaningful.
DONOTMERGE: The extended syntax has not been accepted into the
official spec yet.
Signed-off-by: Andrea Bolognani <[email protected]>
---
src/qemu/qemu_firmware.c | 130 ++++++++++++++++--
...tdx.json => 50-edk2-ovmf-x64-microvm.json} | 12 +-
.../firmware/60-edk2-ovmf-x64-inteltdx.json | 6 +-
.../out/usr/share/qemu/firmware/91-bios.json | 33 +++++
4 files changed, 166 insertions(+), 15 deletions(-)
copy
tests/qemufirmwaredata/out/usr/share/qemu/firmware/{60-edk2-ovmf-x64-inteltdx.json
=> 50-edk2-ovmf-x64-microvm.json} (56%)
create mode 100644
tests/qemufirmwaredata/out/usr/share/qemu/firmware/91-bios.json
diff --git a/src/qemu/qemu_firmware.c b/src/qemu/qemu_firmware.c
index 5f5550424f..bf6090fac0 100644
--- a/src/qemu/qemu_firmware.c
+++ b/src/qemu/qemu_firmware.c
@@ -93,9 +93,26 @@ struct _qemuFirmwareMappingFlash {
};
+typedef enum {
+ QEMU_FIRMWARE_MEMORY_MODE_SPLIT,
+ QEMU_FIRMWARE_MEMORY_MODE_STATELESS,
+
+ QEMU_FIRMWARE_MEMORY_MODE_LAST,
+} qemuFirmwareMemoryMode;
+
+VIR_ENUM_DECL(qemuFirmwareMemoryMode);
+VIR_ENUM_IMPL(qemuFirmwareMemoryMode,
+ QEMU_FIRMWARE_MEMORY_MODE_LAST,
+ "split",
+ "stateless",
+);
+
+
typedef struct _qemuFirmwareMappingMemory qemuFirmwareMappingMemory;
struct _qemuFirmwareMappingMemory {
+ qemuFirmwareMemoryMode mode;
qemuFirmwareFile executable;
+ qemuFirmwareFile nvram_template;
};
@@ -218,6 +235,7 @@ static void
qemuFirmwareMappingMemoryFreeContent(qemuFirmwareMappingMemory *memory)
{
qemuFirmwareFileFreeContent(&memory->executable);
+ qemuFirmwareFileFreeContent(&memory->nvram_template);
}
@@ -405,15 +423,90 @@ qemuFirmwareMappingMemoryParse(const char *path,
virJSONValue *doc,
qemuFirmwareMappingMemory *memory)
{
- const char *filename;
-
- if (!(filename = virJSONValueObjectGetString(doc, "filename"))) {
- VIR_DEBUG("missing 'filename' in '%s'", path);
+ virJSONValue *mode;
+ virJSONValue *executable;
+ virJSONValue *nvram_template;
+ virJSONValue *filename;
+
+ mode = virJSONValueObjectGet(doc, "mode");
+ executable = virJSONValueObjectGet(doc, "executable");
+ nvram_template = virJSONValueObjectGet(doc, "nvram-template");
+ filename = virJSONValueObjectGet(doc, "filename");
+
+ /* Firmware descriptors can use either the legacy syntax (filename) or
+ * the extended one (mode, executable and optionally nvram-template),
+ * but mixing and matching the two is not allowed. Malformed firmware
+ * descriptors will be ignored */
+ if (!executable && !filename) {
+ VIR_DEBUG("Must have one of 'executable' and 'filename' in '%s'",
path);
+ return -1;
+ }
+ if (executable && filename) {
+ VIR_DEBUG("Cannot have both 'executable' and 'filename' in '%s'",
path);
return -1;
}
+ if (executable && !mode) {
+ VIR_DEBUG("Must have 'mode' with 'executable' in '%s'", path);
+ return -1;
+ }
+ if (filename && mode) {
+ VIR_DEBUG("Cannot have 'mode' with 'filename' in '%s'", path);
+ return -1;
+ }
+ if (filename && nvram_template) {
+ VIR_DEBUG("Cannot have 'nvram_template' with 'filename' in '%s'",
path);
+ return -1;
+ }
+
+ if (mode) {
+ const char *modestr;
+ int modeval;
+
+ modestr = virJSONValueGetString(mode);
+ if (!modestr) {
+ VIR_DEBUG("Value of 'mode' is not a string in '%s'", path);
+ return -1;
+ }
- memory->executable.filename = g_strdup(filename);
- memory->executable.format = g_strdup("raw");
+ modeval = qemuFirmwareMemoryModeTypeFromString(modestr);
+ if (modeval < 0) {
+ VIR_DEBUG("Unrecognized value '%s' for 'mode' in '%s'", modestr,
path);
+ return -1;
+ }
+
+ memory->mode = modeval;
+ } else {
+ /* Default for legacy syntax */
+ memory->mode = QEMU_FIRMWARE_MEMORY_MODE_STATELESS;
+ }
+
+ if (executable) {
+ if (qemuFirmwareFileParse(path, executable, &memory->executable) < 0)
+ return -1;
+ }
+
+ if (memory->mode == QEMU_FIRMWARE_MEMORY_MODE_SPLIT) {
+ if (!nvram_template) {
+ VIR_DEBUG("Missing mandatory 'nvram-template' for mode=split in
'%s'", path);
+ return -1;
+ }
+
+ if (qemuFirmwareFileParse(path, nvram_template,
&memory->nvram_template) < 0)
+ return -1;
+ }
+
+ if (filename) {
+ const char *filenameval;
+
+ filenameval = virJSONValueGetString(filename);
+ if (!filenameval) {
+ VIR_DEBUG("Value of 'filename' is not a string in '%s'", path);
+ return -1;
+ }
+
+ memory->executable.filename = g_strdup(filenameval);
+ memory->executable.format = g_strdup("raw");
+ }
return 0;
}
@@ -697,11 +790,32 @@ static int
qemuFirmwareMappingMemoryFormat(virJSONValue *mapping,
qemuFirmwareMappingMemory *memory)
{
+ g_autoptr(virJSONValue) executable = NULL;
+ g_autoptr(virJSONValue) nvram_template = NULL;
+
if (virJSONValueObjectAppendString(mapping,
- "filename",
- memory->executable.filename) < 0)
+ "mode",
+
qemuFirmwareMemoryModeTypeToString(memory->mode)) < 0)
+ return -1;
+
+ if (!(executable = qemuFirmwareFileFormat(memory->executable)))
+ return -1;
+
+ if (virJSONValueObjectAppend(mapping,
+ "executable",
+ &executable) < 0)
return -1;
+ if (memory->mode == QEMU_FIRMWARE_MEMORY_MODE_SPLIT) {
+ if (!(nvram_template = qemuFirmwareFileFormat(memory->nvram_template)))
+ return -1;
+
+ if (virJSONValueObjectAppend(mapping,
+ "nvram-template",
+ &nvram_template) < 0)
+ return -1;
+ }
+
return 0;
}
diff --git
a/tests/qemufirmwaredata/out/usr/share/qemu/firmware/60-edk2-ovmf-x64-inteltdx.json
b/tests/qemufirmwaredata/out/usr/share/qemu/firmware/50-edk2-ovmf-x64-microvm.json
similarity index 56%
copy from
tests/qemufirmwaredata/out/usr/share/qemu/firmware/60-edk2-ovmf-x64-inteltdx.json
copy to
tests/qemufirmwaredata/out/usr/share/qemu/firmware/50-edk2-ovmf-x64-microvm.json
index 2630b57b05..ebe1c2db55 100644
---
a/tests/qemufirmwaredata/out/usr/share/qemu/firmware/60-edk2-ovmf-x64-inteltdx.json
+++
b/tests/qemufirmwaredata/out/usr/share/qemu/firmware/50-edk2-ovmf-x64-microvm.json
@@ -4,20 +4,20 @@
],
"mapping": {
"device": "memory",
- "filename": "/usr/share/edk2/ovmf/OVMF.inteltdx.secboot.fd"
+ "mode": "stateless",
+ "executable": {
+ "filename": "/usr/share/edk2/ovmf/MICROVM.fd",
+ "format": "raw"
+ }
},
"targets": [
{
"architecture": "x86_64",
"machines": [
- "pc-q35-*"
+ "microvm"
]
}
],
"features": [
- "enrolled-keys",
- "intel-tdx",
- "secure-boot",
- "verbose-dynamic"
]
}
diff --git
a/tests/qemufirmwaredata/out/usr/share/qemu/firmware/60-edk2-ovmf-x64-inteltdx.json
b/tests/qemufirmwaredata/out/usr/share/qemu/firmware/60-edk2-ovmf-x64-inteltdx.json
index 2630b57b05..68a1cb4ee0 100644
---
a/tests/qemufirmwaredata/out/usr/share/qemu/firmware/60-edk2-ovmf-x64-inteltdx.json
+++
b/tests/qemufirmwaredata/out/usr/share/qemu/firmware/60-edk2-ovmf-x64-inteltdx.json
@@ -4,7 +4,11 @@
],
"mapping": {
"device": "memory",
- "filename": "/usr/share/edk2/ovmf/OVMF.inteltdx.secboot.fd"
+ "mode": "stateless",
+ "executable": {
+ "filename": "/usr/share/edk2/ovmf/OVMF.inteltdx.secboot.fd",
+ "format": "raw"
+ }
},
"targets": [
{
diff --git a/tests/qemufirmwaredata/out/usr/share/qemu/firmware/91-bios.json
b/tests/qemufirmwaredata/out/usr/share/qemu/firmware/91-bios.json
new file mode 100644
index 0000000000..10a22969f4
--- /dev/null
+++ b/tests/qemufirmwaredata/out/usr/share/qemu/firmware/91-bios.json
@@ -0,0 +1,33 @@
+{
+ "interface-types": [
+ "bios"
+ ],
+ "mapping": {
+ "device": "memory",
+ "mode": "stateless",
+ "executable": {
+ "filename": "/usr/share/seabios/bios-256k.bin",
+ "format": "raw"
+ }
+ },
+ "targets": [
+ {
+ "architecture": "i386",
+ "machines": [
+ "pc-i440fx-*",
+ "pc-q35-*"
+ ]
+ },
+ {
+ "architecture": "x86_64",
+ "machines": [
+ "pc-i440fx-*",
+ "pc-q35-*"
+ ]
+ }
+ ],
+ "features": [
+ "acpi-s3",
+ "acpi-s4"
+ ]
+}
--
2.52.0