diff --git a/src/qemu/qemu_firmware.c b/src/qemu/qemu_firmware.c index 59dc76fec4..51223faadf 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);