[libvirt PATCH] qemu: support parsing firmware descriptor flash 'mode' for optional NVRAM

Daniel P. Berrangé berrange at redhat.com
Wed Feb 23 12:37:08 UTC 2022


Ping.

On Thu, Feb 17, 2022 at 11:54:07AM +0000, Daniel P. Berrangé wrote:
> Currently the 'nvram_template' entry is mandatory when parsing the
> firmware descriptor based on flash. QEMU is extending the firmware
> descriptor spec to make the 'nvram_template' optional, depending
> on the value of a new 'mode' field:
> 
>   - "split"
>       * "executable" contains read-only CODE
>       * "nvram_template" contains read-write VARS
> 
>   - "combined"
>       * "executable" contains read-write CODE and VARs
>       * "nvram_template" not present, as the "executable"
>         is effectively the template on its own
> 
>   - "stateless"
>       * "executable" contains read-only CODE and VARs
>       * "nvram_template" not present
> 
> In the latter case, the guest OS can write vars but the firmware will
> make no attempt to persist them, so any changes will be lost at
> poweroff.
> 
> For now we parse this new 'mode' but discard any firmware which is not
> 'mode=split' when matching for a domain. This is the minimum required
> to have libvirt not break when seeing the new firmware descriptors.
> Future changes will support the new modes.
> 
> In the tests we have a mixture of files with and without the mode
> attribute.
> 
> Signed-off-by: Daniel P. Berrangé <berrange at redhat.com>
> ---
>  src/qemu/qemu_firmware.c                      | 79 ++++++++++++++++---
>  .../share/qemu/firmware/50-ovmf-sb-keys.json  | 33 ++++++++
>  .../out/usr/share/qemu/firmware/61-ovmf.json  | 31 ++++++++
>  .../out/usr/share/qemu/firmware/70-aavmf.json | 28 +++++++
>  .../qemu/firmware/45-ovmf-sev-stateless.json  | 31 ++++++++
>  .../qemu/firmware/55-ovmf-sb-combined.json    | 33 ++++++++
>  .../usr/share/qemu/firmware/60-ovmf-sb.json   |  1 +
>  tests/qemufirmwaretest.c                      | 31 ++++++--
>  8 files changed, 246 insertions(+), 21 deletions(-)
>  create mode 100644 tests/qemufirmwaredata/out/usr/share/qemu/firmware/50-ovmf-sb-keys.json
>  create mode 100644 tests/qemufirmwaredata/out/usr/share/qemu/firmware/61-ovmf.json
>  create mode 100644 tests/qemufirmwaredata/out/usr/share/qemu/firmware/70-aavmf.json
>  create mode 100644 tests/qemufirmwaredata/usr/share/qemu/firmware/45-ovmf-sev-stateless.json
>  create mode 100644 tests/qemufirmwaredata/usr/share/qemu/firmware/55-ovmf-sb-combined.json
> 
> diff --git a/src/qemu/qemu_firmware.c b/src/qemu/qemu_firmware.c
> index ff364996b8..e403ee98e4 100644
> --- a/src/qemu/qemu_firmware.c
> +++ b/src/qemu/qemu_firmware.c
> @@ -59,6 +59,22 @@ VIR_ENUM_IMPL(qemuFirmwareOSInterface,
>  );
>  
>  
> +typedef enum {
> +    QEMU_FIRMWARE_FLASH_MODE_SPLIT,
> +    QEMU_FIRMWARE_FLASH_MODE_COMBINED,
> +    QEMU_FIRMWARE_FLASH_MODE_STATELESS,
> +
> +    QEMU_FIRMWARE_FLASH_MODE_LAST,
> +} qemuFirmwareFlashMode;
> +
> +VIR_ENUM_DECL(qemuFirmwareFlashMode);
> +VIR_ENUM_IMPL(qemuFirmwareFlashMode,
> +              QEMU_FIRMWARE_FLASH_MODE_LAST,
> +              "split",
> +              "combined",
> +              "stateless",
> +);
> +
>  typedef struct _qemuFirmwareFlashFile qemuFirmwareFlashFile;
>  struct _qemuFirmwareFlashFile {
>      char *filename;
> @@ -68,6 +84,7 @@ struct _qemuFirmwareFlashFile {
>  
>  typedef struct _qemuFirmwareMappingFlash qemuFirmwareMappingFlash;
>  struct _qemuFirmwareMappingFlash {
> +    qemuFirmwareFlashMode mode;
>      qemuFirmwareFlashFile executable;
>      qemuFirmwareFlashFile nvram_template;
>  };
> @@ -359,9 +376,31 @@ qemuFirmwareMappingFlashParse(const char *path,
>                                virJSONValue *doc,
>                                qemuFirmwareMappingFlash *flash)
>  {
> +    virJSONValue *mode;
>      virJSONValue *executable;
>      virJSONValue *nvram_template;
>  
> +    if (!(mode = virJSONValueObjectGet(doc, "mode"))) {
> +        /* Historical default */
> +        flash->mode = QEMU_FIRMWARE_FLASH_MODE_SPLIT;
> +    } else {
> +        const char *modestr = virJSONValueGetString(mode);
> +        int modeval;
> +        if (!modestr) {
> +            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> +                           _("Firmware flash mode value was malformed"));
> +            return -1;
> +        }
> +        modeval = qemuFirmwareFlashModeTypeFromString(modestr);
> +        if (modeval < 0) {
> +            virReportError(VIR_ERR_INTERNAL_ERROR,
> +                           _("Firmware flash mode value '%s' unexpected"),
> +                           modestr);
> +            return -1;
> +        }
> +        flash->mode = modeval;
> +    }
> +
>      if (!(executable = virJSONValueObjectGet(doc, "executable"))) {
>          virReportError(VIR_ERR_INTERNAL_ERROR,
>                         _("missing 'executable' in '%s'"),
> @@ -372,15 +411,17 @@ qemuFirmwareMappingFlashParse(const char *path,
>      if (qemuFirmwareFlashFileParse(path, executable, &flash->executable) < 0)
>          return -1;
>  
> -    if (!(nvram_template = virJSONValueObjectGet(doc, "nvram-template"))) {
> -        virReportError(VIR_ERR_INTERNAL_ERROR,
> -                       _("missing 'nvram-template' in '%s'"),
> -                       path);
> -        return -1;
> -    }
> +    if (flash->mode == QEMU_FIRMWARE_FLASH_MODE_SPLIT) {
> +        if (!(nvram_template = virJSONValueObjectGet(doc, "nvram-template"))) {
> +            virReportError(VIR_ERR_INTERNAL_ERROR,
> +                           _("missing 'nvram-template' in '%s'"),
> +                           path);
> +            return -1;
> +        }
>  
> -    if (qemuFirmwareFlashFileParse(path, nvram_template, &flash->nvram_template) < 0)
> -        return -1;
> +        if (qemuFirmwareFlashFileParse(path, nvram_template, &flash->nvram_template) < 0)
> +            return -1;
> +    }
>  
>      return 0;
>  }
> @@ -693,10 +734,12 @@ qemuFirmwareMappingFlashFormat(virJSONValue *mapping,
>      g_autoptr(virJSONValue) executable = NULL;
>      g_autoptr(virJSONValue) nvram_template = NULL;
>  
> -    if (!(executable = qemuFirmwareFlashFileFormat(flash->executable)))
> +    if (virJSONValueObjectAppendString(mapping,
> +                                       "mode",
> +                                       qemuFirmwareFlashModeTypeToString(flash->mode)) < 0)
>          return -1;
>  
> -    if (!(nvram_template = qemuFirmwareFlashFileFormat(flash->nvram_template)))
> +    if (!(executable = qemuFirmwareFlashFileFormat(flash->executable)))
>          return -1;
>  
>      if (virJSONValueObjectAppend(mapping,
> @@ -704,11 +747,15 @@ qemuFirmwareMappingFlashFormat(virJSONValue *mapping,
>                                   &executable) < 0)
>          return -1;
>  
> +    if (flash->mode == QEMU_FIRMWARE_FLASH_MODE_SPLIT) {
> +        if (!(nvram_template = qemuFirmwareFlashFileFormat(flash->nvram_template)))
> +            return -1;
>  
> -    if (virJSONValueObjectAppend(mapping,
> +        if (virJSONValueObjectAppend(mapping,
>                                   "nvram-template",
> -                                 &nvram_template) < 0)
> -        return -1;
> +                                     &nvram_template) < 0)
> +            return -1;
> +    }
>  
>      return 0;
>  }
> @@ -1070,6 +1117,12 @@ qemuFirmwareMatchDomain(const virDomainDef *def,
>          return false;
>      }
>  
> +    if (fw->mapping.device == QEMU_FIRMWARE_DEVICE_FLASH &&
> +        fw->mapping.data.flash.mode != QEMU_FIRMWARE_FLASH_MODE_SPLIT) {
> +        VIR_DEBUG("Discarding loader without split flash");
> +        return false;
> +    }
> +
>      if (def->sec) {
>          switch ((virDomainLaunchSecurity) def->sec->sectype) {
>          case VIR_DOMAIN_LAUNCH_SECURITY_SEV:
> 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
> new file mode 100644
> index 0000000000..c251682cd9
> --- /dev/null
> +++ b/tests/qemufirmwaredata/out/usr/share/qemu/firmware/50-ovmf-sb-keys.json
> @@ -0,0 +1,33 @@
> +{
> +    "interface-types": [
> +        "uefi"
> +    ],
> +    "mapping": {
> +        "device": "flash",
> +        "mode": "split",
> +        "executable": {
> +            "filename": "/usr/share/OVMF/OVMF_CODE.secboot.fd",
> +            "format": "raw"
> +        },
> +        "nvram-template": {
> +            "filename": "/usr/share/OVMF/OVMF_VARS.secboot.fd",
> +            "format": "raw"
> +        }
> +    },
> +    "targets": [
> +        {
> +            "architecture": "x86_64",
> +            "machines": [
> +                "pc-q35-*"
> +            ]
> +        }
> +    ],
> +    "features": [
> +        "acpi-s3",
> +        "amd-sev",
> +        "enrolled-keys",
> +        "requires-smm",
> +        "secure-boot",
> +        "verbose-dynamic"
> +    ]
> +}
> diff --git a/tests/qemufirmwaredata/out/usr/share/qemu/firmware/61-ovmf.json b/tests/qemufirmwaredata/out/usr/share/qemu/firmware/61-ovmf.json
> new file mode 100644
> index 0000000000..2a9aa23efb
> --- /dev/null
> +++ b/tests/qemufirmwaredata/out/usr/share/qemu/firmware/61-ovmf.json
> @@ -0,0 +1,31 @@
> +{
> +    "interface-types": [
> +        "uefi"
> +    ],
> +    "mapping": {
> +        "device": "flash",
> +        "mode": "split",
> +        "executable": {
> +            "filename": "/usr/share/OVMF/OVMF_CODE.fd",
> +            "format": "raw"
> +        },
> +        "nvram-template": {
> +            "filename": "/usr/share/OVMF/OVMF_VARS.fd",
> +            "format": "raw"
> +        }
> +    },
> +    "targets": [
> +        {
> +            "architecture": "x86_64",
> +            "machines": [
> +                "pc-i440fx-*",
> +                "pc-q35-*"
> +            ]
> +        }
> +    ],
> +    "features": [
> +        "acpi-s3",
> +        "amd-sev",
> +        "verbose-dynamic"
> +    ]
> +}
> diff --git a/tests/qemufirmwaredata/out/usr/share/qemu/firmware/70-aavmf.json b/tests/qemufirmwaredata/out/usr/share/qemu/firmware/70-aavmf.json
> new file mode 100644
> index 0000000000..9bd5ac2868
> --- /dev/null
> +++ b/tests/qemufirmwaredata/out/usr/share/qemu/firmware/70-aavmf.json
> @@ -0,0 +1,28 @@
> +{
> +    "interface-types": [
> +        "uefi"
> +    ],
> +    "mapping": {
> +        "device": "flash",
> +        "mode": "split",
> +        "executable": {
> +            "filename": "/usr/share/AAVMF/AAVMF_CODE.fd",
> +            "format": "raw"
> +        },
> +        "nvram-template": {
> +            "filename": "/usr/share/AAVMF/AAVMF_VARS.fd",
> +            "format": "raw"
> +        }
> +    },
> +    "targets": [
> +        {
> +            "architecture": "aarch64",
> +            "machines": [
> +                "virt-*"
> +            ]
> +        }
> +    ],
> +    "features": [
> +
> +    ]
> +}
> 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
> new file mode 100644
> index 0000000000..5a619f3ab0
> --- /dev/null
> +++ b/tests/qemufirmwaredata/usr/share/qemu/firmware/45-ovmf-sev-stateless.json
> @@ -0,0 +1,31 @@
> +{
> +    "description": "OVMF for x86_64, with SEV, without SB, without SMM, with NO varstore",
> +    "interface-types": [
> +        "uefi"
> +    ],
> +    "mapping": {
> +        "device": "flash",
> +        "mode": "stateless",
> +        "executable": {
> +            "filename": "/usr/share/OVMF/OVMF.sev.fd",
> +            "format": "raw"
> +        }
> +    },
> +    "targets": [
> +        {
> +            "architecture": "x86_64",
> +            "machines": [
> +                "pc-q35-*"
> +            ]
> +        }
> +    ],
> +    "features": [
> +        "acpi-s3",
> +        "amd-sev",
> +        "amd-sev-es",
> +        "verbose-dynamic"
> +    ],
> +    "tags": [
> +
> +    ]
> +}
> 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
> new file mode 100644
> index 0000000000..eb3332e4ab
> --- /dev/null
> +++ b/tests/qemufirmwaredata/usr/share/qemu/firmware/55-ovmf-sb-combined.json
> @@ -0,0 +1,33 @@
> +{
> +    "description": "OVMF with SB+SMM, SB enabled, MS certs enrolled",
> +    "interface-types": [
> +        "uefi"
> +    ],
> +    "mapping": {
> +        "device": "flash",
> +	"mode": "combined",
> +        "executable": {
> +            "filename": "/usr/share/OVMF/OVMF.secboot.fd",
> +            "format": "raw"
> +        }
> +    },
> +    "targets": [
> +        {
> +            "architecture": "x86_64",
> +            "machines": [
> +                "pc-q35-*"
> +            ]
> +        }
> +    ],
> +    "features": [
> +        "acpi-s3",
> +        "amd-sev",
> +        "enrolled-keys",
> +        "requires-smm",
> +        "secure-boot",
> +        "verbose-dynamic"
> +    ],
> +    "tags": [
> +
> +    ]
> +}
> diff --git a/tests/qemufirmwaredata/usr/share/qemu/firmware/60-ovmf-sb.json b/tests/qemufirmwaredata/usr/share/qemu/firmware/60-ovmf-sb.json
> index 5e8a94ae78..a5273a5e8b 100644
> --- a/tests/qemufirmwaredata/usr/share/qemu/firmware/60-ovmf-sb.json
> +++ b/tests/qemufirmwaredata/usr/share/qemu/firmware/60-ovmf-sb.json
> @@ -5,6 +5,7 @@
>      ],
>      "mapping": {
>          "device": "flash",
> +        "mode": "split",
>          "executable": {
>              "filename": "/usr/share/OVMF/OVMF_CODE.secboot.fd",
>              "format": "raw"
> diff --git a/tests/qemufirmwaretest.c b/tests/qemufirmwaretest.c
> index cad4b6d383..fc3416b2ae 100644
> --- a/tests/qemufirmwaretest.c
> +++ b/tests/qemufirmwaretest.c
> @@ -17,22 +17,31 @@ static int
>  testParseFormatFW(const void *opaque)
>  {
>      const char *filename = opaque;
> -    g_autofree char *path = NULL;
> +    g_autofree char *inpath = NULL;
> +    g_autofree char *outpath = NULL;
>      g_autoptr(qemuFirmware) fw = NULL;
> -    g_autofree char *buf = NULL;
>      g_autoptr(virJSONValue) json = NULL;
>      g_autofree char *expected = NULL;
>      g_autofree char *actual = NULL;
> +    g_autofree char *buf = NULL;
>  
> -    path = g_strdup_printf("%s/qemufirmwaredata/%s", abs_srcdir, filename);
> +    inpath = g_strdup_printf("%s/qemufirmwaredata/%s", abs_srcdir, filename);
> +    outpath = g_strdup_printf("%s/qemufirmwaredata/out/%s", abs_srcdir, filename);
>  
> -    if (!(fw = qemuFirmwareParse(path)))
> +    if (!(fw = qemuFirmwareParse(inpath)))
>          return -1;
>  
> -    if (virFileReadAll(path,
> -                       1024 * 1024, /* 1MiB */
> -                       &buf) < 0)
> -        return -1;
> +    if (virFileExists(outpath)) {
> +        if (virFileReadAll(outpath,
> +                           1024 * 1024, /* 1MiB */
> +                           &buf) < 0)
> +            return -1;
> +    } else {
> +        if (virFileReadAll(inpath,
> +                           1024 * 1024, /* 1MiB */
> +                           &buf) < 0)
> +            return -1;
> +    }
>  
>      if (!(json = virJSONValueFromString(buf)))
>          return -1;
> @@ -60,7 +69,9 @@ testFWPrecedence(const void *opaque G_GNUC_UNUSED)
>      const char *expected[] = {
>          PREFIX "/share/qemu/firmware/40-bios.json",
>          SYSCONFDIR "/qemu/firmware/40-ovmf-sb-keys.json",
> +        PREFIX "/share/qemu/firmware/45-ovmf-sev-stateless.json",
>          PREFIX "/share/qemu/firmware/50-ovmf-sb-keys.json",
> +        PREFIX "/share/qemu/firmware/55-ovmf-sb-combined.json",
>          PREFIX "/share/qemu/firmware/61-ovmf.json",
>          PREFIX "/share/qemu/firmware/70-aavmf.json",
>          NULL
> @@ -218,7 +229,9 @@ mymain(void)
>      } while (0)
>  
>      DO_PARSE_TEST("usr/share/qemu/firmware/40-bios.json");
> +    DO_PARSE_TEST("usr/share/qemu/firmware/45-ovmf-sev-stateless.json");
>      DO_PARSE_TEST("usr/share/qemu/firmware/50-ovmf-sb-keys.json");
> +    DO_PARSE_TEST("usr/share/qemu/firmware/55-ovmf-sb-combined.json");
>      DO_PARSE_TEST("usr/share/qemu/firmware/60-ovmf-sb.json");
>      DO_PARSE_TEST("usr/share/qemu/firmware/61-ovmf.json");
>      DO_PARSE_TEST("usr/share/qemu/firmware/70-aavmf.json");
> @@ -250,6 +263,8 @@ mymain(void)
>      DO_SUPPORTED_TEST("pc-q35-3.1", VIR_ARCH_X86_64, true,
>                        "/usr/share/seabios/bios-256k.bin:NULL:"
>                        "/usr/share/OVMF/OVMF_CODE.secboot.fd:/usr/share/OVMF/OVMF_VARS.secboot.fd:"
> +                      "/usr/share/OVMF/OVMF.sev.fd:NULL:"
> +                      "/usr/share/OVMF/OVMF.secboot.fd:NULL:"
>                        "/usr/share/OVMF/OVMF_CODE.fd:/usr/share/OVMF/OVMF_VARS.fd",
>                        VIR_DOMAIN_OS_DEF_FIRMWARE_BIOS,
>                        VIR_DOMAIN_OS_DEF_FIRMWARE_EFI);
> -- 
> 2.34.1
> 

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|




More information about the libvir-list mailing list