[PATCH 08/29] DONOTMERGE: qemu_firmware: Support extended syntax for ROM firmware descriptors

Andrea Bolognani via Devel posted 29 patches 1 week, 3 days ago
[PATCH 08/29] DONOTMERGE: qemu_firmware: Support extended syntax for ROM firmware descriptors
Posted by Andrea Bolognani via Devel 1 week, 3 days ago
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 <abologna@redhat.com>
---
 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