render / rpms / libvirt

Forked from rpms/libvirt 9 months ago
Clone
44173a
From 3cde498c98be902fc8fe87c895dfeaaa95352b38 Mon Sep 17 00:00:00 2001
44173a
Message-Id: <3cde498c98be902fc8fe87c895dfeaaa95352b38@dist-git>
44173a
From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com>
44173a
Date: Thu, 3 Feb 2022 13:43:18 +0000
44173a
Subject: [PATCH] qemu: support firmware descriptor flash 'mode' for optional
44173a
 NVRAM
44173a
MIME-Version: 1.0
44173a
Content-Type: text/plain; charset=UTF-8
44173a
Content-Transfer-Encoding: 8bit
44173a
44173a
Currently the 'nvram_template' entry is mandatory when parsing the
44173a
firmware descriptor based on flash. QEMU is extending the firmware
44173a
descriptor spec to make the 'nvram_template' optional, depending
44173a
on the value of a new 'mode' field:
44173a
44173a
  - "split"
44173a
      * "executable" contains read-only CODE
44173a
      * "nvram_template" contains read-write VARS
44173a
44173a
  - "combined"
44173a
      * "executable" contains read-write CODE and VARs
44173a
      * "nvram_template" not present
44173a
44173a
  - "stateless"
44173a
      * "executable" contains read-only CODE and VARs
44173a
      * "nvram_template" not present
44173a
44173a
In the latter case, the guest OS can write vars but the
44173a
firmware will make no attempt to persist them, so any changes
44173a
will be lost at poweroff.
44173a
44173a
For now we parse this new 'mode' but discard any firmware
44173a
which is not 'mode=split' when matching for a domain.
44173a
44173a
In the tests we have a mixture of files with and without the
44173a
mode attribute.
44173a
44173a
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
44173a
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
44173a
(cherry picked from commit 32b9d8b0ae00669555f01f91ee11612a636c4b69)
44173a
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2057769
44173a
---
44173a
 src/qemu/qemu_firmware.c                      | 79 ++++++++++++++++---
44173a
 .../share/qemu/firmware/50-ovmf-sb-keys.json  | 33 ++++++++
44173a
 .../out/usr/share/qemu/firmware/61-ovmf.json  | 31 ++++++++
44173a
 .../out/usr/share/qemu/firmware/70-aavmf.json | 28 +++++++
44173a
 .../qemu/firmware/45-ovmf-sev-stateless.json  | 31 ++++++++
44173a
 .../qemu/firmware/55-ovmf-sb-combined.json    | 33 ++++++++
44173a
 .../usr/share/qemu/firmware/60-ovmf-sb.json   |  1 +
44173a
 tests/qemufirmwaretest.c                      | 31 ++++++--
44173a
 8 files changed, 246 insertions(+), 21 deletions(-)
44173a
 create mode 100644 tests/qemufirmwaredata/out/usr/share/qemu/firmware/50-ovmf-sb-keys.json
44173a
 create mode 100644 tests/qemufirmwaredata/out/usr/share/qemu/firmware/61-ovmf.json
44173a
 create mode 100644 tests/qemufirmwaredata/out/usr/share/qemu/firmware/70-aavmf.json
44173a
 create mode 100644 tests/qemufirmwaredata/usr/share/qemu/firmware/45-ovmf-sev-stateless.json
44173a
 create mode 100644 tests/qemufirmwaredata/usr/share/qemu/firmware/55-ovmf-sb-combined.json
44173a
44173a
diff --git a/src/qemu/qemu_firmware.c b/src/qemu/qemu_firmware.c
44173a
index 529ab8d68e..7911d45aa0 100644
44173a
--- a/src/qemu/qemu_firmware.c
44173a
+++ b/src/qemu/qemu_firmware.c
44173a
@@ -59,6 +59,22 @@ VIR_ENUM_IMPL(qemuFirmwareOSInterface,
44173a
 );
44173a
 
44173a
 
44173a
+typedef enum {
44173a
+    QEMU_FIRMWARE_FLASH_MODE_SPLIT,
44173a
+    QEMU_FIRMWARE_FLASH_MODE_COMBINED,
44173a
+    QEMU_FIRMWARE_FLASH_MODE_STATELESS,
44173a
+
44173a
+    QEMU_FIRMWARE_FLASH_MODE_LAST,
44173a
+} qemuFirmwareFlashMode;
44173a
+
44173a
+VIR_ENUM_DECL(qemuFirmwareFlashMode);
44173a
+VIR_ENUM_IMPL(qemuFirmwareFlashMode,
44173a
+              QEMU_FIRMWARE_FLASH_MODE_LAST,
44173a
+              "split",
44173a
+              "combined",
44173a
+              "stateless",
44173a
+);
44173a
+
44173a
 typedef struct _qemuFirmwareFlashFile qemuFirmwareFlashFile;
44173a
 struct _qemuFirmwareFlashFile {
44173a
     char *filename;
44173a
@@ -68,6 +84,7 @@ struct _qemuFirmwareFlashFile {
44173a
 
44173a
 typedef struct _qemuFirmwareMappingFlash qemuFirmwareMappingFlash;
44173a
 struct _qemuFirmwareMappingFlash {
44173a
+    qemuFirmwareFlashMode mode;
44173a
     qemuFirmwareFlashFile executable;
44173a
     qemuFirmwareFlashFile nvram_template;
44173a
 };
44173a
@@ -359,9 +376,31 @@ qemuFirmwareMappingFlashParse(const char *path,
44173a
                               virJSONValue *doc,
44173a
                               qemuFirmwareMappingFlash *flash)
44173a
 {
44173a
+    virJSONValue *mode;
44173a
     virJSONValue *executable;
44173a
     virJSONValue *nvram_template;
44173a
 
44173a
+    if (!(mode = virJSONValueObjectGet(doc, "mode"))) {
44173a
+        /* Historical default */
44173a
+        flash->mode = QEMU_FIRMWARE_FLASH_MODE_SPLIT;
44173a
+    } else {
44173a
+        const char *modestr = virJSONValueGetString(mode);
44173a
+        int modeval;
44173a
+        if (!modestr) {
44173a
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
44173a
+                           _("Firmware flash mode value was malformed"));
44173a
+            return -1;
44173a
+        }
44173a
+        modeval = qemuFirmwareFlashModeTypeFromString(modestr);
44173a
+        if (modeval < 0) {
44173a
+            virReportError(VIR_ERR_INTERNAL_ERROR,
44173a
+                           _("Firmware flash mode value '%s' unexpected"),
44173a
+                           modestr);
44173a
+            return -1;
44173a
+        }
44173a
+        flash->mode = modeval;
44173a
+    }
44173a
+
44173a
     if (!(executable = virJSONValueObjectGet(doc, "executable"))) {
44173a
         virReportError(VIR_ERR_INTERNAL_ERROR,
44173a
                        _("missing 'executable' in '%s'"),
44173a
@@ -372,15 +411,17 @@ qemuFirmwareMappingFlashParse(const char *path,
44173a
     if (qemuFirmwareFlashFileParse(path, executable, &flash->executable) < 0)
44173a
         return -1;
44173a
 
44173a
-    if (!(nvram_template = virJSONValueObjectGet(doc, "nvram-template"))) {
44173a
-        virReportError(VIR_ERR_INTERNAL_ERROR,
44173a
-                       _("missing 'nvram-template' in '%s'"),
44173a
-                       path);
44173a
-        return -1;
44173a
-    }
44173a
+    if (flash->mode == QEMU_FIRMWARE_FLASH_MODE_SPLIT) {
44173a
+        if (!(nvram_template = virJSONValueObjectGet(doc, "nvram-template"))) {
44173a
+            virReportError(VIR_ERR_INTERNAL_ERROR,
44173a
+                           _("missing 'nvram-template' in '%s'"),
44173a
+                           path);
44173a
+            return -1;
44173a
+        }
44173a
 
44173a
-    if (qemuFirmwareFlashFileParse(path, nvram_template, &flash->nvram_template) < 0)
44173a
-        return -1;
44173a
+        if (qemuFirmwareFlashFileParse(path, nvram_template, &flash->nvram_template) < 0)
44173a
+            return -1;
44173a
+    }
44173a
 
44173a
     return 0;
44173a
 }
44173a
@@ -693,10 +734,12 @@ qemuFirmwareMappingFlashFormat(virJSONValue *mapping,
44173a
     g_autoptr(virJSONValue) executable = NULL;
44173a
     g_autoptr(virJSONValue) nvram_template = NULL;
44173a
 
44173a
-    if (!(executable = qemuFirmwareFlashFileFormat(flash->executable)))
44173a
+    if (virJSONValueObjectAppendString(mapping,
44173a
+                                       "mode",
44173a
+                                       qemuFirmwareFlashModeTypeToString(flash->mode)) < 0)
44173a
         return -1;
44173a
 
44173a
-    if (!(nvram_template = qemuFirmwareFlashFileFormat(flash->nvram_template)))
44173a
+    if (!(executable = qemuFirmwareFlashFileFormat(flash->executable)))
44173a
         return -1;
44173a
 
44173a
     if (virJSONValueObjectAppend(mapping,
44173a
@@ -704,11 +747,15 @@ qemuFirmwareMappingFlashFormat(virJSONValue *mapping,
44173a
                                  &executable) < 0)
44173a
         return -1;
44173a
 
44173a
+    if (flash->mode == QEMU_FIRMWARE_FLASH_MODE_SPLIT) {
44173a
+        if (!(nvram_template = qemuFirmwareFlashFileFormat(flash->nvram_template)))
44173a
+            return -1;
44173a
 
44173a
-    if (virJSONValueObjectAppend(mapping,
44173a
+        if (virJSONValueObjectAppend(mapping,
44173a
                                  "nvram-template",
44173a
-                                 &nvram_template) < 0)
44173a
-        return -1;
44173a
+                                     &nvram_template) < 0)
44173a
+            return -1;
44173a
+    }
44173a
 
44173a
     return 0;
44173a
 }
44173a
@@ -1053,6 +1100,12 @@ qemuFirmwareMatchDomain(const virDomainDef *def,
44173a
         return false;
44173a
     }
44173a
 
44173a
+    if (fw->mapping.device == QEMU_FIRMWARE_DEVICE_FLASH &&
44173a
+        fw->mapping.data.flash.mode != QEMU_FIRMWARE_FLASH_MODE_SPLIT) {
44173a
+        VIR_DEBUG("Discarding loader without split flash");
44173a
+        return false;
44173a
+    }
44173a
+
44173a
     if (def->sec) {
44173a
         switch ((virDomainLaunchSecurity) def->sec->sectype) {
44173a
         case VIR_DOMAIN_LAUNCH_SECURITY_SEV:
44173a
diff --git a/tests/qemufirmwaredata/out/usr/share/qemu/firmware/50-ovmf-sb-keys.json b/tests/qemufirmwaredata/out/usr/share/qemu/firmware/50-ovmf-sb-keys.json
44173a
new file mode 100644
44173a
index 0000000000..c251682cd9
44173a
--- /dev/null
44173a
+++ b/tests/qemufirmwaredata/out/usr/share/qemu/firmware/50-ovmf-sb-keys.json
44173a
@@ -0,0 +1,33 @@
44173a
+{
44173a
+    "interface-types": [
44173a
+        "uefi"
44173a
+    ],
44173a
+    "mapping": {
44173a
+        "device": "flash",
44173a
+        "mode": "split",
44173a
+        "executable": {
44173a
+            "filename": "/usr/share/OVMF/OVMF_CODE.secboot.fd",
44173a
+            "format": "raw"
44173a
+        },
44173a
+        "nvram-template": {
44173a
+            "filename": "/usr/share/OVMF/OVMF_VARS.secboot.fd",
44173a
+            "format": "raw"
44173a
+        }
44173a
+    },
44173a
+    "targets": [
44173a
+        {
44173a
+            "architecture": "x86_64",
44173a
+            "machines": [
44173a
+                "pc-q35-*"
44173a
+            ]
44173a
+        }
44173a
+    ],
44173a
+    "features": [
44173a
+        "acpi-s3",
44173a
+        "amd-sev",
44173a
+        "enrolled-keys",
44173a
+        "requires-smm",
44173a
+        "secure-boot",
44173a
+        "verbose-dynamic"
44173a
+    ]
44173a
+}
44173a
diff --git a/tests/qemufirmwaredata/out/usr/share/qemu/firmware/61-ovmf.json b/tests/qemufirmwaredata/out/usr/share/qemu/firmware/61-ovmf.json
44173a
new file mode 100644
44173a
index 0000000000..2a9aa23efb
44173a
--- /dev/null
44173a
+++ b/tests/qemufirmwaredata/out/usr/share/qemu/firmware/61-ovmf.json
44173a
@@ -0,0 +1,31 @@
44173a
+{
44173a
+    "interface-types": [
44173a
+        "uefi"
44173a
+    ],
44173a
+    "mapping": {
44173a
+        "device": "flash",
44173a
+        "mode": "split",
44173a
+        "executable": {
44173a
+            "filename": "/usr/share/OVMF/OVMF_CODE.fd",
44173a
+            "format": "raw"
44173a
+        },
44173a
+        "nvram-template": {
44173a
+            "filename": "/usr/share/OVMF/OVMF_VARS.fd",
44173a
+            "format": "raw"
44173a
+        }
44173a
+    },
44173a
+    "targets": [
44173a
+        {
44173a
+            "architecture": "x86_64",
44173a
+            "machines": [
44173a
+                "pc-i440fx-*",
44173a
+                "pc-q35-*"
44173a
+            ]
44173a
+        }
44173a
+    ],
44173a
+    "features": [
44173a
+        "acpi-s3",
44173a
+        "amd-sev",
44173a
+        "verbose-dynamic"
44173a
+    ]
44173a
+}
44173a
diff --git a/tests/qemufirmwaredata/out/usr/share/qemu/firmware/70-aavmf.json b/tests/qemufirmwaredata/out/usr/share/qemu/firmware/70-aavmf.json
44173a
new file mode 100644
44173a
index 0000000000..9bd5ac2868
44173a
--- /dev/null
44173a
+++ b/tests/qemufirmwaredata/out/usr/share/qemu/firmware/70-aavmf.json
44173a
@@ -0,0 +1,28 @@
44173a
+{
44173a
+    "interface-types": [
44173a
+        "uefi"
44173a
+    ],
44173a
+    "mapping": {
44173a
+        "device": "flash",
44173a
+        "mode": "split",
44173a
+        "executable": {
44173a
+            "filename": "/usr/share/AAVMF/AAVMF_CODE.fd",
44173a
+            "format": "raw"
44173a
+        },
44173a
+        "nvram-template": {
44173a
+            "filename": "/usr/share/AAVMF/AAVMF_VARS.fd",
44173a
+            "format": "raw"
44173a
+        }
44173a
+    },
44173a
+    "targets": [
44173a
+        {
44173a
+            "architecture": "aarch64",
44173a
+            "machines": [
44173a
+                "virt-*"
44173a
+            ]
44173a
+        }
44173a
+    ],
44173a
+    "features": [
44173a
+
44173a
+    ]
44173a
+}
44173a
diff --git a/tests/qemufirmwaredata/usr/share/qemu/firmware/45-ovmf-sev-stateless.json b/tests/qemufirmwaredata/usr/share/qemu/firmware/45-ovmf-sev-stateless.json
44173a
new file mode 100644
44173a
index 0000000000..5a619f3ab0
44173a
--- /dev/null
44173a
+++ b/tests/qemufirmwaredata/usr/share/qemu/firmware/45-ovmf-sev-stateless.json
44173a
@@ -0,0 +1,31 @@
44173a
+{
44173a
+    "description": "OVMF for x86_64, with SEV, without SB, without SMM, with NO varstore",
44173a
+    "interface-types": [
44173a
+        "uefi"
44173a
+    ],
44173a
+    "mapping": {
44173a
+        "device": "flash",
44173a
+        "mode": "stateless",
44173a
+        "executable": {
44173a
+            "filename": "/usr/share/OVMF/OVMF.sev.fd",
44173a
+            "format": "raw"
44173a
+        }
44173a
+    },
44173a
+    "targets": [
44173a
+        {
44173a
+            "architecture": "x86_64",
44173a
+            "machines": [
44173a
+                "pc-q35-*"
44173a
+            ]
44173a
+        }
44173a
+    ],
44173a
+    "features": [
44173a
+        "acpi-s3",
44173a
+        "amd-sev",
44173a
+        "amd-sev-es",
44173a
+        "verbose-dynamic"
44173a
+    ],
44173a
+    "tags": [
44173a
+
44173a
+    ]
44173a
+}
44173a
diff --git a/tests/qemufirmwaredata/usr/share/qemu/firmware/55-ovmf-sb-combined.json b/tests/qemufirmwaredata/usr/share/qemu/firmware/55-ovmf-sb-combined.json
44173a
new file mode 100644
44173a
index 0000000000..eb3332e4ab
44173a
--- /dev/null
44173a
+++ b/tests/qemufirmwaredata/usr/share/qemu/firmware/55-ovmf-sb-combined.json
44173a
@@ -0,0 +1,33 @@
44173a
+{
44173a
+    "description": "OVMF with SB+SMM, SB enabled, MS certs enrolled",
44173a
+    "interface-types": [
44173a
+        "uefi"
44173a
+    ],
44173a
+    "mapping": {
44173a
+        "device": "flash",
44173a
+	"mode": "combined",
44173a
+        "executable": {
44173a
+            "filename": "/usr/share/OVMF/OVMF.secboot.fd",
44173a
+            "format": "raw"
44173a
+        }
44173a
+    },
44173a
+    "targets": [
44173a
+        {
44173a
+            "architecture": "x86_64",
44173a
+            "machines": [
44173a
+                "pc-q35-*"
44173a
+            ]
44173a
+        }
44173a
+    ],
44173a
+    "features": [
44173a
+        "acpi-s3",
44173a
+        "amd-sev",
44173a
+        "enrolled-keys",
44173a
+        "requires-smm",
44173a
+        "secure-boot",
44173a
+        "verbose-dynamic"
44173a
+    ],
44173a
+    "tags": [
44173a
+
44173a
+    ]
44173a
+}
44173a
diff --git a/tests/qemufirmwaredata/usr/share/qemu/firmware/60-ovmf-sb.json b/tests/qemufirmwaredata/usr/share/qemu/firmware/60-ovmf-sb.json
44173a
index 5e8a94ae78..a5273a5e8b 100644
44173a
--- a/tests/qemufirmwaredata/usr/share/qemu/firmware/60-ovmf-sb.json
44173a
+++ b/tests/qemufirmwaredata/usr/share/qemu/firmware/60-ovmf-sb.json
44173a
@@ -5,6 +5,7 @@
44173a
     ],
44173a
     "mapping": {
44173a
         "device": "flash",
44173a
+        "mode": "split",
44173a
         "executable": {
44173a
             "filename": "/usr/share/OVMF/OVMF_CODE.secboot.fd",
44173a
             "format": "raw"
44173a
diff --git a/tests/qemufirmwaretest.c b/tests/qemufirmwaretest.c
44173a
index cad4b6d383..fc3416b2ae 100644
44173a
--- a/tests/qemufirmwaretest.c
44173a
+++ b/tests/qemufirmwaretest.c
44173a
@@ -17,22 +17,31 @@ static int
44173a
 testParseFormatFW(const void *opaque)
44173a
 {
44173a
     const char *filename = opaque;
44173a
-    g_autofree char *path = NULL;
44173a
+    g_autofree char *inpath = NULL;
44173a
+    g_autofree char *outpath = NULL;
44173a
     g_autoptr(qemuFirmware) fw = NULL;
44173a
-    g_autofree char *buf = NULL;
44173a
     g_autoptr(virJSONValue) json = NULL;
44173a
     g_autofree char *expected = NULL;
44173a
     g_autofree char *actual = NULL;
44173a
+    g_autofree char *buf = NULL;
44173a
 
44173a
-    path = g_strdup_printf("%s/qemufirmwaredata/%s", abs_srcdir, filename);
44173a
+    inpath = g_strdup_printf("%s/qemufirmwaredata/%s", abs_srcdir, filename);
44173a
+    outpath = g_strdup_printf("%s/qemufirmwaredata/out/%s", abs_srcdir, filename);
44173a
 
44173a
-    if (!(fw = qemuFirmwareParse(path)))
44173a
+    if (!(fw = qemuFirmwareParse(inpath)))
44173a
         return -1;
44173a
 
44173a
-    if (virFileReadAll(path,
44173a
-                       1024 * 1024, /* 1MiB */
44173a
-                       &buf) < 0)
44173a
-        return -1;
44173a
+    if (virFileExists(outpath)) {
44173a
+        if (virFileReadAll(outpath,
44173a
+                           1024 * 1024, /* 1MiB */
44173a
+                           &buf) < 0)
44173a
+            return -1;
44173a
+    } else {
44173a
+        if (virFileReadAll(inpath,
44173a
+                           1024 * 1024, /* 1MiB */
44173a
+                           &buf) < 0)
44173a
+            return -1;
44173a
+    }
44173a
 
44173a
     if (!(json = virJSONValueFromString(buf)))
44173a
         return -1;
44173a
@@ -60,7 +69,9 @@ testFWPrecedence(const void *opaque G_GNUC_UNUSED)
44173a
     const char *expected[] = {
44173a
         PREFIX "/share/qemu/firmware/40-bios.json",
44173a
         SYSCONFDIR "/qemu/firmware/40-ovmf-sb-keys.json",
44173a
+        PREFIX "/share/qemu/firmware/45-ovmf-sev-stateless.json",
44173a
         PREFIX "/share/qemu/firmware/50-ovmf-sb-keys.json",
44173a
+        PREFIX "/share/qemu/firmware/55-ovmf-sb-combined.json",
44173a
         PREFIX "/share/qemu/firmware/61-ovmf.json",
44173a
         PREFIX "/share/qemu/firmware/70-aavmf.json",
44173a
         NULL
44173a
@@ -218,7 +229,9 @@ mymain(void)
44173a
     } while (0)
44173a
 
44173a
     DO_PARSE_TEST("usr/share/qemu/firmware/40-bios.json");
44173a
+    DO_PARSE_TEST("usr/share/qemu/firmware/45-ovmf-sev-stateless.json");
44173a
     DO_PARSE_TEST("usr/share/qemu/firmware/50-ovmf-sb-keys.json");
44173a
+    DO_PARSE_TEST("usr/share/qemu/firmware/55-ovmf-sb-combined.json");
44173a
     DO_PARSE_TEST("usr/share/qemu/firmware/60-ovmf-sb.json");
44173a
     DO_PARSE_TEST("usr/share/qemu/firmware/61-ovmf.json");
44173a
     DO_PARSE_TEST("usr/share/qemu/firmware/70-aavmf.json");
44173a
@@ -250,6 +263,8 @@ mymain(void)
44173a
     DO_SUPPORTED_TEST("pc-q35-3.1", VIR_ARCH_X86_64, true,
44173a
                       "/usr/share/seabios/bios-256k.bin:NULL:"
44173a
                       "/usr/share/OVMF/OVMF_CODE.secboot.fd:/usr/share/OVMF/OVMF_VARS.secboot.fd:"
44173a
+                      "/usr/share/OVMF/OVMF.sev.fd:NULL:"
44173a
+                      "/usr/share/OVMF/OVMF.secboot.fd:NULL:"
44173a
                       "/usr/share/OVMF/OVMF_CODE.fd:/usr/share/OVMF/OVMF_VARS.fd",
44173a
                       VIR_DOMAIN_OS_DEF_FIRMWARE_BIOS,
44173a
                       VIR_DOMAIN_OS_DEF_FIRMWARE_EFI);
44173a
-- 
44173a
2.35.1
44173a