diff --git a/src/qemu/Makefile.inc.am b/src/qemu/Makefile.inc.am
index 0b2bc074c082882e5c62a938004e6f4a392a498b..8d10e1085a368a5e273198db98b53871d4183b74 100644
--- a/src/qemu/Makefile.inc.am
+++ b/src/qemu/Makefile.inc.am
@@ -21,6 +21,8 @@ QEMU_DRIVER_SOURCES = \
qemu/qemu_cgroup.h \
qemu/qemu_extdevice.c \
qemu/qemu_extdevice.h \
+ qemu/qemu_firmware.c \
+ qemu/qemu_firmware.h \
qemu/qemu_hostdev.c \
qemu/qemu_hostdev.h \
qemu/qemu_hotplug.c \
diff --git a/src/qemu/qemu_firmware.c b/src/qemu/qemu_firmware.c
new file mode 100644
index 0000000000000000000000000000000000000000..8f718ee2a61ca6aa748eac87205b3ef71d4743e8
--- /dev/null
+++ b/src/qemu/qemu_firmware.c
@@ -0,0 +1,901 @@
+/*
+ * qemu_firmware.c: QEMU firmware
+ *
+ * Copyright (C) 2019 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * .
+ */
+
+#include
+
+#include "qemu_firmware.h"
+#include "qemu_capabilities.h"
+#include "virarch.h"
+#include "virfile.h"
+#include "virjson.h"
+#include "virlog.h"
+#include "virstring.h"
+
+#define VIR_FROM_THIS VIR_FROM_QEMU
+
+VIR_LOG_INIT("qemu.qemu_firmware");
+
+
+typedef enum {
+ QEMU_FIRMWARE_OS_INTERFACE_NONE = 0,
+ QEMU_FIRMWARE_OS_INTERFACE_BIOS,
+ QEMU_FIRMWARE_OS_INTERFACE_OPENFIRMWARE,
+ QEMU_FIRMWARE_OS_INTERFACE_UBOOT,
+ QEMU_FIRMWARE_OS_INTERFACE_UEFI,
+
+ QEMU_FIRMWARE_OS_INTERFACE_LAST
+} qemuFirmwareOSInterface;
+
+VIR_ENUM_DECL(qemuFirmwareOSInterface);
+VIR_ENUM_IMPL(qemuFirmwareOSInterface,
+ QEMU_FIRMWARE_OS_INTERFACE_LAST,
+ "",
+ "bios",
+ "openfirmware",
+ "uboot",
+ "uefi",
+);
+
+
+typedef struct _qemuFirmwareFlashFile qemuFirmwareFlashFile;
+typedef qemuFirmwareFlashFile *qemuFirmwareFlashFilePtr;
+struct _qemuFirmwareFlashFile {
+ char *filename;
+ char *format;
+};
+
+
+typedef struct _qemuFirmwareMappingFlash qemuFirmwareMappingFlash;
+typedef qemuFirmwareMappingFlash *qemuFirmwareMappingFlashPtr;
+struct _qemuFirmwareMappingFlash {
+ qemuFirmwareFlashFile executable;
+ qemuFirmwareFlashFile nvram_template;
+};
+
+
+typedef struct _qemuFirmwareMappingKernel qemuFirmwareMappingKernel;
+typedef qemuFirmwareMappingKernel *qemuFirmwareMappingKernelPtr;
+struct _qemuFirmwareMappingKernel {
+ char *filename;
+};
+
+
+typedef struct _qemuFirmwareMappingMemory qemuFirmwareMappingMemory;
+typedef qemuFirmwareMappingMemory *qemuFirmwareMappingMemoryPtr;
+struct _qemuFirmwareMappingMemory {
+ char *filename;
+};
+
+
+typedef enum {
+ QEMU_FIRMWARE_DEVICE_NONE = 0,
+ QEMU_FIRMWARE_DEVICE_FLASH,
+ QEMU_FIRMWARE_DEVICE_KERNEL,
+ QEMU_FIRMWARE_DEVICE_MEMORY,
+
+ QEMU_FIRMWARE_DEVICE_LAST
+} qemuFirmwareDevice;
+
+VIR_ENUM_DECL(qemuFirmwareDevice);
+VIR_ENUM_IMPL(qemuFirmwareDevice,
+ QEMU_FIRMWARE_DEVICE_LAST,
+ "",
+ "flash",
+ "kernel",
+ "memory",
+);
+
+
+typedef struct _qemuFirmwareMapping qemuFirmwareMapping;
+typedef qemuFirmwareMapping *qemuFirmwareMappingPtr;
+struct _qemuFirmwareMapping {
+ qemuFirmwareDevice device;
+
+ union {
+ qemuFirmwareMappingFlash flash;
+ qemuFirmwareMappingKernel kernel;
+ qemuFirmwareMappingMemory memory;
+ } data;
+};
+
+
+typedef struct _qemuFirmwareTarget qemuFirmwareTarget;
+typedef qemuFirmwareTarget *qemuFirmwareTargetPtr;
+struct _qemuFirmwareTarget {
+ virArch architecture;
+ size_t nmachines;
+ char **machines;
+};
+
+
+typedef enum {
+ QEMU_FIRMWARE_FEATURE_NONE = 0,
+ QEMU_FIRMWARE_FEATURE_ACPI_S3,
+ QEMU_FIRMWARE_FEATURE_ACPI_S4,
+ QEMU_FIRMWARE_FEATURE_AMD_SEV,
+ QEMU_FIRMWARE_FEATURE_ENROLLED_KEYS,
+ QEMU_FIRMWARE_FEATURE_REQUIRES_SMM,
+ QEMU_FIRMWARE_FEATURE_SECURE_BOOT,
+ QEMU_FIRMWARE_FEATURE_VERBOSE_DYNAMIC,
+ QEMU_FIRMWARE_FEATURE_VERBOSE_STATIC,
+
+ QEMU_FIRMWARE_FEATURE_LAST
+} qemuFirmwareFeature;
+
+VIR_ENUM_DECL(qemuFirmwareFeature);
+VIR_ENUM_IMPL(qemuFirmwareFeature,
+ QEMU_FIRMWARE_FEATURE_LAST,
+ "",
+ "acpi-s3",
+ "acpi-s4",
+ "amd-sev",
+ "enrolled-keys",
+ "requires-smm",
+ "secure-boot",
+ "verbose-dynamic",
+ "verbose-static"
+);
+
+
+struct _qemuFirmware {
+ /* Description intentionally not parsed. */
+
+ size_t ninterfaces;
+ qemuFirmwareOSInterface *interfaces;
+
+ qemuFirmwareMapping mapping;
+
+ size_t ntargets;
+ qemuFirmwareTargetPtr *targets;
+
+ size_t nfeatures;
+ qemuFirmwareFeature *features;
+
+ /* Tags intentionally not parsed. */
+};
+
+
+static void
+qemuFirmwareOSInterfaceFree(qemuFirmwareOSInterface *interfaces)
+{
+ VIR_FREE(interfaces);
+}
+
+
+VIR_DEFINE_AUTOPTR_FUNC(qemuFirmwareOSInterface, qemuFirmwareOSInterfaceFree);
+
+
+static void
+qemuFirmwareFlashFileFree(qemuFirmwareFlashFile flash)
+{
+ VIR_FREE(flash.filename);
+ VIR_FREE(flash.format);
+}
+
+
+static void
+qemuFirmwareMappingFlashFree(qemuFirmwareMappingFlash flash)
+{
+ qemuFirmwareFlashFileFree(flash.executable);
+ qemuFirmwareFlashFileFree(flash.nvram_template);
+}
+
+
+static void
+qemuFirmwareMappingKernelFree(qemuFirmwareMappingKernel kernel)
+{
+ VIR_FREE(kernel.filename);
+}
+
+
+static void
+qemuFirmwareMappingMemoryFree(qemuFirmwareMappingMemory memory)
+{
+ VIR_FREE(memory.filename);
+}
+
+
+static void
+qemuFirmwareMappingFree(qemuFirmwareMapping mapping)
+{
+ switch (mapping.device) {
+ case QEMU_FIRMWARE_DEVICE_FLASH:
+ qemuFirmwareMappingFlashFree(mapping.data.flash);
+ break;
+ case QEMU_FIRMWARE_DEVICE_KERNEL:
+ qemuFirmwareMappingKernelFree(mapping.data.kernel);
+ break;
+ case QEMU_FIRMWARE_DEVICE_MEMORY:
+ qemuFirmwareMappingMemoryFree(mapping.data.memory);
+ break;
+ case QEMU_FIRMWARE_DEVICE_NONE:
+ case QEMU_FIRMWARE_DEVICE_LAST:
+ break;
+ }
+}
+
+
+static void
+qemuFirmwareTargetFree(qemuFirmwareTargetPtr target)
+{
+ if (!target)
+ return;
+
+ virStringListFreeCount(target->machines, target->nmachines);
+
+ VIR_FREE(target);
+}
+
+
+VIR_DEFINE_AUTOPTR_FUNC(qemuFirmwareTarget, qemuFirmwareTargetFree);
+
+
+static void
+qemuFirmwareFeatureFree(qemuFirmwareFeature *features)
+{
+ VIR_FREE(features);
+}
+
+
+VIR_DEFINE_AUTOPTR_FUNC(qemuFirmwareFeature, qemuFirmwareFeatureFree);
+
+
+void
+qemuFirmwareFree(qemuFirmwarePtr fw)
+{
+ size_t i;
+
+ if (!fw)
+ return;
+
+ qemuFirmwareOSInterfaceFree(fw->interfaces);
+ qemuFirmwareMappingFree(fw->mapping);
+ for (i = 0; i < fw->ntargets; i++)
+ qemuFirmwareTargetFree(fw->targets[i]);
+ VIR_FREE(fw->targets);
+ qemuFirmwareFeatureFree(fw->features);
+
+ VIR_FREE(fw);
+}
+
+
+static int
+qemuFirmwareInterfaceParse(const char *path,
+ virJSONValuePtr doc,
+ qemuFirmwarePtr fw)
+{
+ virJSONValuePtr interfacesJSON;
+ VIR_AUTOPTR(qemuFirmwareOSInterface) interfaces = NULL;
+ VIR_AUTOCLEAN(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+ size_t ninterfaces;
+ size_t i;
+
+ if (!(interfacesJSON = virJSONValueObjectGetArray(doc, "interface-types"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to get interface-types from '%s'"),
+ path);
+ return -1;
+ }
+
+ ninterfaces = virJSONValueArraySize(interfacesJSON);
+
+ if (VIR_ALLOC_N(interfaces, ninterfaces) < 0)
+ return -1;
+
+ for (i = 0; i < ninterfaces; i++) {
+ virJSONValuePtr item = virJSONValueArrayGet(interfacesJSON, i);
+ const char *tmpStr = virJSONValueGetString(item);
+ int tmp;
+
+ if ((tmp = qemuFirmwareOSInterfaceTypeFromString(tmpStr)) <= 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unknown interface type: '%s'"),
+ tmpStr);
+ return -1;
+ }
+
+ virBufferAsprintf(&buf, " %s", tmpStr);
+ interfaces[i] = tmp;
+ }
+
+ VIR_DEBUG("firmware description path '%s' supported interfaces: %s",
+ path, NULLSTR_MINUS(virBufferCurrentContent(&buf)));
+
+ VIR_STEAL_PTR(fw->interfaces, interfaces);
+ fw->ninterfaces = ninterfaces;
+ return 0;
+}
+
+
+static int
+qemuFirmwareFlashFileParse(const char *path,
+ virJSONValuePtr doc,
+ qemuFirmwareFlashFilePtr flash)
+{
+ const char *filename;
+ const char *format;
+
+ if (!(filename = virJSONValueObjectGetString(doc, "filename"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("missing 'filename' in '%s'"),
+ path);
+ return -1;
+ }
+
+ if (VIR_STRDUP(flash->filename, filename) < 0)
+ return -1;
+
+ if (!(format = virJSONValueObjectGetString(doc, "format"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("missing 'format' in '%s'"),
+ path);
+ return -1;
+ }
+
+ if (VIR_STRDUP(flash->format, format) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+static int
+qemuFirmwareMappingFlashParse(const char *path,
+ virJSONValuePtr doc,
+ qemuFirmwareMappingFlashPtr flash)
+{
+ virJSONValuePtr executable;
+ virJSONValuePtr nvram_template;
+
+ if (!(executable = virJSONValueObjectGet(doc, "executable"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("missing 'executable' in '%s'"),
+ path);
+ return -1;
+ }
+
+ 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 (qemuFirmwareFlashFileParse(path, nvram_template, &flash->nvram_template) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+static int
+qemuFirmwareMappingKernelParse(const char *path,
+ virJSONValuePtr doc,
+ qemuFirmwareMappingKernelPtr kernel)
+{
+ const char *filename;
+
+ if (!(filename = virJSONValueObjectGetString(doc, "filename"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("missing 'filename' in '%s'"),
+ path);
+ }
+
+ if (VIR_STRDUP(kernel->filename, filename) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+static int
+qemuFirmwareMappingMemoryParse(const char *path,
+ virJSONValuePtr doc,
+ qemuFirmwareMappingMemoryPtr memory)
+{
+ const char *filename;
+
+ if (!(filename = virJSONValueObjectGetString(doc, "filename"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("missing 'filename' in '%s'"),
+ path);
+ }
+
+ if (VIR_STRDUP(memory->filename, filename) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+static int
+qemuFirmwareMappingParse(const char *path,
+ virJSONValuePtr doc,
+ qemuFirmwarePtr fw)
+{
+ virJSONValuePtr mapping = virJSONValueObjectGet(doc, "mapping");
+ const char *deviceStr;
+ int tmp;
+
+ if (!(deviceStr = virJSONValueObjectGetString(mapping, "device"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("missing device type in '%s'"),
+ path);
+ return -1;
+ }
+
+ if ((tmp = qemuFirmwareDeviceTypeFromString(deviceStr)) <= 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unknown device type in '%s'"),
+ path);
+ return -1;
+ }
+
+ fw->mapping.device = tmp;
+
+ switch (fw->mapping.device) {
+ case QEMU_FIRMWARE_DEVICE_FLASH:
+ if (qemuFirmwareMappingFlashParse(path, mapping, &fw->mapping.data.flash) < 0)
+ return -1;
+ break;
+ case QEMU_FIRMWARE_DEVICE_KERNEL:
+ if (qemuFirmwareMappingKernelParse(path, mapping, &fw->mapping.data.kernel) < 0)
+ return -1;
+ break;
+ case QEMU_FIRMWARE_DEVICE_MEMORY:
+ if (qemuFirmwareMappingMemoryParse(path, mapping, &fw->mapping.data.memory) < 0)
+ return -1;
+ break;
+
+ case QEMU_FIRMWARE_DEVICE_NONE:
+ case QEMU_FIRMWARE_DEVICE_LAST:
+ break;
+ }
+
+ return 0;
+}
+
+
+static int
+qemuFirmwareTargetParse(const char *path,
+ virJSONValuePtr doc,
+ qemuFirmwarePtr fw)
+{
+ virJSONValuePtr targetsJSON;
+ qemuFirmwareTargetPtr *targets = NULL;
+ size_t ntargets;
+ size_t i;
+ int ret = -1;
+
+ if (!(targetsJSON = virJSONValueObjectGetArray(doc, "targets"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to get targets from '%s'"),
+ path);
+ return -1;
+ }
+
+ ntargets = virJSONValueArraySize(targetsJSON);
+
+ if (VIR_ALLOC_N(targets, ntargets) < 0)
+ return -1;
+
+ for (i = 0; i < ntargets; i++) {
+ virJSONValuePtr item = virJSONValueArrayGet(targetsJSON, i);
+ virJSONValuePtr machines;
+ VIR_AUTOPTR(qemuFirmwareTarget) t = NULL;
+ const char *architectureStr = NULL;
+ size_t nmachines;
+ size_t j;
+
+ if (VIR_ALLOC(t) < 0)
+ goto cleanup;
+
+ if (!(architectureStr = virJSONValueObjectGetString(item, "architecture"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("missing 'architecture' in '%s'"),
+ path);
+ goto cleanup;
+ }
+
+ if ((t->architecture = virQEMUCapsArchFromString(architectureStr)) == VIR_ARCH_NONE) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unknown architecture '%s'"),
+ architectureStr);
+ goto cleanup;
+ }
+
+ if (!(machines = virJSONValueObjectGetArray(item, "machines"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("missing 'machines' in '%s'"),
+ path);
+ goto cleanup;
+ }
+
+ nmachines = virJSONValueArraySize(machines);
+
+ if (VIR_ALLOC_N(t->machines, nmachines) < 0)
+ goto cleanup;
+
+ for (j = 0; j < nmachines; j++) {
+ virJSONValuePtr machine = virJSONValueArrayGet(machines, j);
+ VIR_AUTOFREE(char *) machineStr = NULL;
+
+ if (VIR_STRDUP(machineStr, virJSONValueGetString(machine)) < 0)
+ goto cleanup;
+
+ VIR_APPEND_ELEMENT_INPLACE(t->machines, t->nmachines, machineStr);
+ }
+
+ VIR_STEAL_PTR(targets[i], t);
+ }
+
+ VIR_STEAL_PTR(fw->targets, targets);
+ fw->ntargets = ntargets;
+ ntargets = 0;
+ ret = 0;
+
+ cleanup:
+ for (i = 0; i < ntargets; i++)
+ qemuFirmwareTargetFree(targets[i]);
+ VIR_FREE(targets);
+ return ret;
+}
+
+
+static int
+qemuFirmwareFeatureParse(const char *path,
+ virJSONValuePtr doc,
+ qemuFirmwarePtr fw)
+{
+ virJSONValuePtr featuresJSON;
+ VIR_AUTOPTR(qemuFirmwareFeature) features = NULL;
+ size_t nfeatures;
+ size_t i;
+
+ if (!(featuresJSON = virJSONValueObjectGetArray(doc, "features"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("failed to get features from '%s'"),
+ path);
+ return -1;
+ }
+
+ nfeatures = virJSONValueArraySize(featuresJSON);
+
+ if (VIR_ALLOC_N(features, nfeatures) < 0)
+ return -1;
+
+ fw->nfeatures = nfeatures;
+
+ for (i = 0; i < nfeatures; i++) {
+ virJSONValuePtr item = virJSONValueArrayGet(featuresJSON, i);
+ const char *tmpStr = virJSONValueGetString(item);
+ int tmp;
+
+ if ((tmp = qemuFirmwareFeatureTypeFromString(tmpStr)) <= 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unknown feature %s"),
+ tmpStr);
+ return -1;
+ }
+
+ features[i] = tmp;
+ }
+
+ VIR_STEAL_PTR(fw->features, features);
+ fw->nfeatures = nfeatures;
+ return 0;
+}
+
+
+/* 1MiB should be enough for everybody (TM) */
+#define DOCUMENT_SIZE (1024 * 1024)
+
+qemuFirmwarePtr
+qemuFirmwareParse(const char *path)
+{
+ VIR_AUTOFREE(char *) cont = NULL;
+ VIR_AUTOPTR(virJSONValue) doc = NULL;
+ VIR_AUTOPTR(qemuFirmware) fw = NULL;
+ qemuFirmwarePtr ret = NULL;
+
+ if (virFileReadAll(path, DOCUMENT_SIZE, &cont) < 0)
+ return NULL;
+
+ if (!(doc = virJSONValueFromString(cont))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unable to parse json file '%s'"),
+ path);
+ return NULL;
+ }
+
+ if (VIR_ALLOC(fw) < 0)
+ return NULL;
+
+ if (qemuFirmwareInterfaceParse(path, doc, fw) < 0)
+ return NULL;
+
+ if (qemuFirmwareMappingParse(path, doc, fw) < 0)
+ return NULL;
+
+ if (qemuFirmwareTargetParse(path, doc, fw) < 0)
+ return NULL;
+
+ if (qemuFirmwareFeatureParse(path, doc, fw) < 0)
+ return NULL;
+
+ VIR_STEAL_PTR(ret, fw);
+ return ret;
+}
+
+
+static int
+qemuFirmwareInterfaceFormat(virJSONValuePtr doc,
+ qemuFirmwarePtr fw)
+{
+ VIR_AUTOPTR(virJSONValue) interfacesJSON = NULL;
+ size_t i;
+
+ if (!(interfacesJSON = virJSONValueNewArray()))
+ return -1;
+
+ for (i = 0; i < fw->ninterfaces; i++) {
+ if (virJSONValueArrayAppendString(interfacesJSON,
+ qemuFirmwareOSInterfaceTypeToString(fw->interfaces[i])) < 0)
+ return -1;
+ }
+
+ if (virJSONValueObjectAppend(doc,
+ "interface-types",
+ interfacesJSON) < 0)
+ return -1;
+
+ interfacesJSON = NULL;
+ return 0;
+}
+
+
+static virJSONValuePtr
+qemuFirmwareFlashFileFormat(qemuFirmwareFlashFile flash)
+{
+ VIR_AUTOPTR(virJSONValue) json = NULL;
+ virJSONValuePtr ret;
+
+ if (!(json = virJSONValueNewObject()))
+ return NULL;
+
+ if (virJSONValueObjectAppendString(json,
+ "filename",
+ flash.filename) < 0)
+ return NULL;
+
+ if (virJSONValueObjectAppendString(json,
+ "format",
+ flash.format) < 0)
+ return NULL;
+
+ VIR_STEAL_PTR(ret, json);
+ return ret;
+}
+
+
+static int
+qemuFirmwareMappingFlashFormat(virJSONValuePtr mapping,
+ qemuFirmwareMappingFlashPtr flash)
+{
+ VIR_AUTOPTR(virJSONValue) executable = NULL;
+ VIR_AUTOPTR(virJSONValue) nvram_template = NULL;
+
+ if (!(executable = qemuFirmwareFlashFileFormat(flash->executable)))
+ return -1;
+
+ if (!(nvram_template = qemuFirmwareFlashFileFormat(flash->nvram_template)))
+ return -1;
+
+ if (virJSONValueObjectAppend(mapping,
+ "executable",
+ executable) < 0)
+ return -1;
+
+ executable = NULL;
+
+ if (virJSONValueObjectAppend(mapping,
+ "nvram-template",
+ nvram_template) < 0)
+ return -1;
+
+ nvram_template = NULL;
+
+ return 0;
+}
+
+
+static int
+qemuFirmwareMappingKernelFormat(virJSONValuePtr mapping,
+ qemuFirmwareMappingKernelPtr kernel)
+{
+ if (virJSONValueObjectAppendString(mapping,
+ "filename",
+ kernel->filename) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+static int
+qemuFirmwareMappingMemoryFormat(virJSONValuePtr mapping,
+ qemuFirmwareMappingMemoryPtr memory)
+{
+ if (virJSONValueObjectAppendString(mapping,
+ "filename",
+ memory->filename) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+static int
+qemuFirmwareMappingFormat(virJSONValuePtr doc,
+ qemuFirmwarePtr fw)
+{
+ VIR_AUTOPTR(virJSONValue) mapping = NULL;
+
+ if (!(mapping = virJSONValueNewObject()))
+ return -1;
+
+ if (virJSONValueObjectAppendString(mapping,
+ "device",
+ qemuFirmwareDeviceTypeToString(fw->mapping.device)) < 0)
+ return -1;
+
+ switch (fw->mapping.device) {
+ case QEMU_FIRMWARE_DEVICE_FLASH:
+ if (qemuFirmwareMappingFlashFormat(mapping, &fw->mapping.data.flash) < 0)
+ return -1;
+ break;
+ case QEMU_FIRMWARE_DEVICE_KERNEL:
+ if (qemuFirmwareMappingKernelFormat(mapping, &fw->mapping.data.kernel) < 0)
+ return -1;
+ break;
+ case QEMU_FIRMWARE_DEVICE_MEMORY:
+ if (qemuFirmwareMappingMemoryFormat(mapping, &fw->mapping.data.memory) < 0)
+ return -1;
+ break;
+
+ case QEMU_FIRMWARE_DEVICE_NONE:
+ case QEMU_FIRMWARE_DEVICE_LAST:
+ break;
+ }
+
+ if (virJSONValueObjectAppend(doc, "mapping", mapping) < 0)
+ return -1;
+
+ mapping = NULL;
+ return 0;
+}
+
+
+static int
+qemuFirmwareTargetFormat(virJSONValuePtr doc,
+ qemuFirmwarePtr fw)
+{
+ VIR_AUTOPTR(virJSONValue) targetsJSON = NULL;
+ size_t i;
+
+ if (!(targetsJSON = virJSONValueNewArray()))
+ return -1;
+
+ for (i = 0; i < fw->ntargets; i++) {
+ qemuFirmwareTargetPtr t = fw->targets[i];
+ VIR_AUTOPTR(virJSONValue) target = NULL;
+ VIR_AUTOPTR(virJSONValue) machines = NULL;
+ size_t j;
+
+ if (!(target = virJSONValueNewObject()))
+ return -1;
+
+ if (virJSONValueObjectAppendString(target,
+ "architecture",
+ virQEMUCapsArchToString(t->architecture)) < 0)
+ return -1;
+
+ if (!(machines = virJSONValueNewArray()))
+ return -1;
+
+ for (j = 0; j < t->nmachines; j++) {
+ if (virJSONValueArrayAppendString(machines,
+ t->machines[j]) < 0)
+ return -1;
+ }
+
+ if (virJSONValueObjectAppend(target, "machines", machines) < 0)
+ return -1;
+
+ machines = NULL;
+
+ if (virJSONValueArrayAppend(targetsJSON, target) < 0)
+ return -1;
+
+ target = NULL;
+ }
+
+ if (virJSONValueObjectAppend(doc, "targets", targetsJSON) < 0)
+ return -1;
+
+ targetsJSON = NULL;
+ return 0;
+}
+
+
+static int
+qemuFirmwareFeatureFormat(virJSONValuePtr doc,
+ qemuFirmwarePtr fw)
+{
+ VIR_AUTOPTR(virJSONValue) featuresJSON = NULL;
+ size_t i;
+
+ if (!(featuresJSON = virJSONValueNewArray()))
+ return -1;
+
+ for (i = 0; i < fw->nfeatures; i++) {
+ if (virJSONValueArrayAppendString(featuresJSON,
+ qemuFirmwareFeatureTypeToString(fw->features[i])) < 0)
+ return -1;
+ }
+
+ if (virJSONValueObjectAppend(doc,
+ "features",
+ featuresJSON) < 0)
+ return -1;
+
+ featuresJSON = NULL;
+ return 0;
+}
+
+
+char *
+qemuFirmwareFormat(qemuFirmwarePtr fw)
+{
+ VIR_AUTOPTR(virJSONValue) doc = NULL;
+
+ if (!fw)
+ return NULL;
+
+ if (!(doc = virJSONValueNewObject()))
+ return NULL;
+
+ if (qemuFirmwareInterfaceFormat(doc, fw) < 0)
+ return NULL;
+
+ if (qemuFirmwareMappingFormat(doc, fw) < 0)
+ return NULL;
+
+ if (qemuFirmwareTargetFormat(doc, fw) < 0)
+ return NULL;
+
+ if (qemuFirmwareFeatureFormat(doc, fw) < 0)
+ return NULL;
+
+ return virJSONValueToString(doc, true);
+}
diff --git a/src/qemu/qemu_firmware.h b/src/qemu/qemu_firmware.h
new file mode 100644
index 0000000000000000000000000000000000000000..952615d42b651b0e1d45aa630ca3619640d3e1e9
--- /dev/null
+++ b/src/qemu/qemu_firmware.h
@@ -0,0 +1,40 @@
+/*
+ * qemu_firmware.h: QEMU firmware
+ *
+ * Copyright (C) 2019 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * .
+ */
+
+#ifndef LIBVIRT_QEMU_FIRMWARE_H
+# define LIBVIRT_QEMU_FIRMWARE_H
+
+# include "viralloc.h"
+
+typedef struct _qemuFirmware qemuFirmware;
+typedef qemuFirmware *qemuFirmwarePtr;
+
+void
+qemuFirmwareFree(qemuFirmwarePtr fw);
+
+VIR_DEFINE_AUTOPTR_FUNC(qemuFirmware, qemuFirmwareFree);
+
+qemuFirmwarePtr
+qemuFirmwareParse(const char *path);
+
+char *
+qemuFirmwareFormat(qemuFirmwarePtr fw);
+
+#endif /* LIBVIRT_QEMU_FIRMWARE_H */