[PATCH 2/2] docs/interop/firmware: Introduce extended syntax for FirmwareMappingMemory

Andrea Bolognani posted 2 patches 1 week, 2 days ago
Maintainers: "Philippe Mathieu-Daudé" <philmd@linaro.org>, "Daniel P. Berrangé" <berrange@redhat.com>, Kashyap Chamarthy <kchamart@redhat.com>
[PATCH 2/2] docs/interop/firmware: Introduce extended syntax for FirmwareMappingMemory
Posted by Andrea Bolognani 1 week, 2 days ago
The new syntax allows describing firmwares that are loaded as
ROMs but also support NVRAM storage. This is the case for edk2
builds that are set up to use the uefi-vars QEMU device, and
whose descriptors would advertise the 'host-uefi-vars' feature.

The extended syntax intentionally mirrors FirmwareMappingFlash
since it needs to cover the exact same scenarios, which are a
strict superset of the ones FirmwareMappingMemory supports
today.

Unfortunately there doesn't seem to be a way to make the legacy
syntax and the extended one coexist within the boundaries of
the QAPI type system, at least not in a semantically-meaningful
fashion.

Given that the specification, despite being maintained in a
machine-readable format, is really intended to be used by the
humans authoring firmware descriptors or writing code that
parses them, and that dealing with the additional complexity on
the consumer side is in practice very easy (as confirmed by
implementing support for it in libvirt), the decision was made
to favor alignment with FirmwareMappingFlash over QAPI purity,
and to bridge the gap with extensive inline documentation.

Signed-off-by: Andrea Bolognani <abologna@redhat.com>
---
 docs/interop/firmware.json | 186 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 181 insertions(+), 5 deletions(-)

diff --git a/docs/interop/firmware.json b/docs/interop/firmware.json
index ef9b976a34..686964ec83 100644
--- a/docs/interop/firmware.json
+++ b/docs/interop/firmware.json
@@ -369,21 +369,197 @@
 { 'struct' : 'FirmwareMappingKernel',
   'data'   : { 'filename' : 'str' } }
 
+##
+# @FirmwareMemoryExecutableFormat:
+#
+# Formats that are supported for firmware executables.
+#
+# @raw: Raw disk image format.
+#
+# Since: 11.0
+##
+{ 'enum': 'FirmwareMemoryExecutableFormat',
+  'data': [ 'raw' ] }
+
+##
+# @FirmwareMemoryNvramFormat:
+#
+# Formats that are supported for firmware NVRAM files.
+#
+# @json: JSON format.
+#
+# Since: 11.0
+##
+{ 'enum': 'FirmwareMemoryNvramFormat',
+  'data': [ 'json' ] }
+
+##
+# @FirmwareMemoryExecutableFile:
+#
+# Describes the firmware executable.
+#
+# @filename: Filename on the host filesystem where the executable can
+#     be found.
+#
+# @format: Block format of the file pointed to by @filename.
+#
+# Since: 11.0
+##
+{ 'struct' : 'FirmwareMemoryExecutableFile',
+  'data'   : { 'filename' : 'str',
+               'format'   : 'FirmwareMemoryExecutableFormat' } }
+
+##
+# @FirmwareMemoryNvramTemplateFile:
+#
+# Describes the NVRAM template to be used together with the
+# corresponding firmware executable.
+#
+# @filename: Filename on the host filesystem where the NVRAM template
+#     can be found.
+#
+# @format: Block format of the file pointed to by @filename.
+#
+# Since: 11.0
+##
+{ 'struct' : 'FirmwareMemoryNvramTemplateFile',
+  'data'   : { 'filename' : 'str',
+               'format'   : 'FirmwareMemoryNvramFormat' } }
+
+##
+# @FirmwareMemoryMode:
+#
+# Describes how the firmware build handles variable persistence.
+#
+# @split: the executable file contains code while the NVRAM template
+#     provides variable storage.  The executable can be shared
+#     between multiple guests.  The NVRAM template must be cloned for
+#     each new guest and configured read-write.
+#
+# @stateless: the executable file contains code and variable storage
+#     is not persisted.  The executable can be shared between
+#     multiple guests.  No NVRAM template will be specified.
+#
+# Since: 11.0
+##
+{ 'enum' : 'FirmwareMemoryMode',
+  'data' : [ 'split', 'stateless' ] }
+
 ##
 # @FirmwareMappingMemory:
 #
 # Describes loading and mapping properties for the firmware
 # executable, when @FirmwareDevice is @memory.
 #
-# @filename: Identifies the firmware executable.  The firmware
-#     executable may be shared by multiple virtual machine
-#     definitions.  The corresponding QEMU command line option is
-#     "-bios @filename".
+# Two syntaxes are possible: a legacy one, which can only describe
+# stateless firmware builds, and an extended one, which can
+# additionally describe firmware builds that support variable
+# storage via the "uefi-vars" device appropriate for the
+# architecture.
+#
+# The two syntaxes are mutually exclusive. In particular:
+#
+# - only one of @filename and @executable can be specified;
+#
+# - if @filename is specified, @mode must be omitted and its value
+#   is assumed to be @stateless.  If @executable is used instead,
+#   the value for @mode must be provided explicitly;
+#
+# - @nvram-template can only be specified together with @executable,
+#   and in this case the value of @mode must be @split.
+#
+# Based on these rules,
+#
+# ::
+#
+#     {
+#         "mapping: {
+#             "device": "memory",
+#             "filename": "/path/to/firmware.bin"
+#         }
+#     }
+#
+# and
+#
+# ::
+#
+#     {
+#         "mapping": {
+#             "device": "memory",
+#             "mode": "stateless",
+#             "executable": {
+#                 "filename": "/path/to/firmware.bin",
+#                 "format": "raw"
+#             }
+#         }
+#     }
+#
+# are completely equivalent, whereas
+#
+# ::
+#
+#     {
+#         "mapping": {
+#             "device": "memory",
+#             "mode": "split",
+#             "executable": {
+#                 "filename": "/path/to/firmware.bin",
+#                 "format": "raw"
+#             },
+#             "nvram-template": {
+#                 "filename": "/path/to/variables.json",
+#                 "format": "json"
+#             }
+#         }
+#     }
+#
+# can only be described using the extended syntax.
+#
+# @mode: Describes how the firmware build handles variable storage.
+#     Must be present when @executable is used and absent when
+#     @filename is used; in the latter scenario, its value will be
+#     assumed to be @stateless.  Since: 11.0
+#
+# @executable: Describes the firmware excutable.  The corresponding
+#     QEMU command line option is "-bios @executable.@filename".
+#     Since: 11.0
+#
+# @nvram-template: Describes the NVRAM template compatible with
+#     @executable, when @mode is set to @split, otherwise it should
+#     not be present.  Management software instantiates an individual
+#     copy -- a specific NVRAM file -- from @nvram-template.@filename
+#     for each new virtual machine definition created.
+#     @nvram-template.@filename itself is never mapped into virtual
+#     machines, only individual copies of it are.  An NVRAM file is
+#     typically used for persistently storing the non-volatile UEFI
+#     variables of a virtual machine definition.  The corresponding
+#     QEMU command line option is
+#
+#     ::
+#
+#         -device uefi-vars-x64,jsonfile=FILENAME_OF_PRIVATE_NVRAM_FILE
+#
+#     on x86_64 and
+#
+#     ::
+#
+#         -device uefi-vars-sysbus,jsonfile=FILENAME_OF_PRIVATE_NVRAM_FILE
+#
+#     on other architectures (aarch64, riscv64, loongarch64).
+#     Since: 11.0
+#
+# @filename: Legacy syntax that can only describe @stateless firmware
+#     builds.  The corresponding QEMU command line option is "-bios
+#     @filename".  If present, none of the other attributes (@mode,
+#     @executable, @template) can be present.
 #
 # Since: 3.0
 ##
 { 'struct' : 'FirmwareMappingMemory',
-  'data'   : { 'filename' : 'str' } }
+  'data'   : { '*mode'           : 'FirmwareMemoryMode',
+               '*executable'     : 'FirmwareMemoryExecutableFile',
+               '*nvram-template' : 'FirmwareMemoryNvramTemplateFile',
+               '*filename'       : 'str' } }
 
 ##
 # @FirmwareMappingIgvm:
-- 
2.52.0
Re: [PATCH 2/2] docs/interop/firmware: Introduce extended syntax for FirmwareMappingMemory
Posted by Daniel P. Berrangé 1 day, 18 hours ago
On Mon, Dec 29, 2025 at 12:26:49AM +0100, Andrea Bolognani wrote:
> The new syntax allows describing firmwares that are loaded as
> ROMs but also support NVRAM storage. This is the case for edk2
> builds that are set up to use the uefi-vars QEMU device, and
> whose descriptors would advertise the 'host-uefi-vars' feature.

I find it a bit of a stretch to be referring to the host-uefi-vars
feature as being "NVRAM storage". IIUC, it is effectively a command
/ response RPC service over a paravirtualized device.

> The extended syntax intentionally mirrors FirmwareMappingFlash
> since it needs to cover the exact same scenarios, which are a
> strict superset of the ones FirmwareMappingMemory supports
> today.

IMHO this is a mistake as it isn't really the same as the
NVRAM for the flash mapping.


> +##
> +# @FirmwareMemoryExecutableFormat:
> +#
> +# Formats that are supported for firmware executables.
> +#
> +# @raw: Raw disk image format.
> +#
> +# Since: 11.0
> +##
> +{ 'enum': 'FirmwareMemoryExecutableFormat',
> +  'data': [ 'raw' ] }
> +
> +##
> +# @FirmwareMemoryNvramFormat:
> +#
> +# Formats that are supported for firmware NVRAM files.
> +#
> +# @json: JSON format.
> +#
> +# Since: 11.0
> +##
> +{ 'enum': 'FirmwareMemoryNvramFormat',
> +  'data': [ 'json' ] }

These two are both examples of where the analogy to the flash
mapping falls down.

The executable format in flash mapping was required because
the binaries are provided via the block backend config (-blockdev).
The list of formats conceptually would expand to anything that
is possible with -blockdev, but we artificially restricted the
schema to just raw & qcow2 since other options were somewhat
unplausible.

Similarly the flash mapping nvram again wanted a format because
it was again describing something to be used with -blockdev.

Neither of these two scenarios applies, and while we could squint
and say using "raw" is acceptable for FirmwareMemoryExecutableFormat,
using "json" is definitely not a fit as it has nothing todo with
-blockdev.


> +##
> +# @FirmwareMemoryMode:
> +#
> +# Describes how the firmware build handles variable persistence.
> +#
> +# @split: the executable file contains code while the NVRAM template
> +#     provides variable storage.  The executable can be shared
> +#     between multiple guests.  The NVRAM template must be cloned for
> +#     each new guest and configured read-write.
> +#
> +# @stateless: the executable file contains code and variable storage
> +#     is not persisted.  The executable can be shared between
> +#     multiple guests.  No NVRAM template will be specified.
> +#
> +# Since: 11.0
> +##
> +{ 'enum' : 'FirmwareMemoryMode',
> +  'data' : [ 'split', 'stateless' ] }

This feels very wierd talking about NVRAM again when no such concept
exists with -bios, even when the UEFI var service is present.

The 'split' concept doesn't really match what the UEFI var service
provides IMHO. 


> +# The two syntaxes are mutually exclusive. In particular:
> +#
> +# - only one of @filename and @executable can be specified;
> +#
> +# - if @filename is specified, @mode must be omitted and its value
> +#   is assumed to be @stateless.  If @executable is used instead,
> +#   the value for @mode must be provided explicitly;
> +#
> +# - @nvram-template can only be specified together with @executable,
> +#   and in this case the value of @mode must be @split.
> +#
> +# Based on these rules,
> +#
> +# ::
> +#
> +#     {
> +#         "mapping: {
> +#             "device": "memory",
> +#             "filename": "/path/to/firmware.bin"
> +#         }
> +#     }
> +#
> +# and
> +#
> +# ::
> +#
> +#     {
> +#         "mapping": {
> +#             "device": "memory",
> +#             "mode": "stateless",
> +#             "executable": {
> +#                 "filename": "/path/to/firmware.bin",
> +#                 "format": "raw"
> +#             }
> +#         }
> +#     }
> +#
> +# are completely equivalent, whereas
> +#
> +# ::
> +#
> +#     {
> +#         "mapping": {
> +#             "device": "memory",
> +#             "mode": "split",
> +#             "executable": {
> +#                 "filename": "/path/to/firmware.bin",
> +#                 "format": "raw"
> +#             },
> +#             "nvram-template": {
> +#                 "filename": "/path/to/variables.json",
> +#                 "format": "json"
> +#             }
> +#         }
> +#     }
> +#
> +# can only be described using the extended syntax.
> +#
> +# @mode: Describes how the firmware build handles variable storage.
> +#     Must be present when @executable is used and absent when
> +#     @filename is used; in the latter scenario, its value will be
> +#     assumed to be @stateless.  Since: 11.0
> +#
> +# @executable: Describes the firmware excutable.  The corresponding
> +#     QEMU command line option is "-bios @executable.@filename".
> +#     Since: 11.0
> +#
> +# @nvram-template: Describes the NVRAM template compatible with
> +#     @executable, when @mode is set to @split, otherwise it should
> +#     not be present.  Management software instantiates an individual
> +#     copy -- a specific NVRAM file -- from @nvram-template.@filename
> +#     for each new virtual machine definition created.
> +#     @nvram-template.@filename itself is never mapped into virtual
> +#     machines, only individual copies of it are.  An NVRAM file is
> +#     typically used for persistently storing the non-volatile UEFI
> +#     variables of a virtual machine definition.  The corresponding
> +#     QEMU command line option is
> +#
> +#     ::
> +#
> +#         -device uefi-vars-x64,jsonfile=FILENAME_OF_PRIVATE_NVRAM_FILE
> +#
> +#     on x86_64 and
> +#
> +#     ::
> +#
> +#         -device uefi-vars-sysbus,jsonfile=FILENAME_OF_PRIVATE_NVRAM_FILE
> +#
> +#     on other architectures (aarch64, riscv64, loongarch64).
> +#     Since: 11.0
> +#
> +# @filename: Legacy syntax that can only describe @stateless firmware
> +#     builds.  The corresponding QEMU command line option is "-bios
> +#     @filename".  If present, none of the other attributes (@mode,
> +#     @executable, @template) can be present.
>  #
>  # Since: 3.0
>  ##
>  { 'struct' : 'FirmwareMappingMemory',
> -  'data'   : { 'filename' : 'str' } }
> +  'data'   : { '*mode'           : 'FirmwareMemoryMode',
> +               '*executable'     : 'FirmwareMemoryExecutableFile',
> +               '*nvram-template' : 'FirmwareMemoryNvramTemplateFile',
> +               '*filename'       : 'str' } }

IMHO we could add merely a new "uefi-vars-service" such that it looks
like this:

     {
         "mapping: {
             "device": "memory",
             "filename": "/path/to/firmware.bin",
	     "uefi-vars-service": {
	       "template": "/path/to/firmware.json",
	     }
         }
     }

With 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 :|
Re: [PATCH 2/2] docs/interop/firmware: Introduce extended syntax for FirmwareMappingMemory
Posted by Gerd Hoffmann 23 hours ago
  Hi,

> IMHO we could add merely a new "uefi-vars-service" such that it looks
> like this:
> 
>      {
>          "mapping: {
>              "device": "memory",
>              "filename": "/path/to/firmware.bin",
> 	     "uefi-vars-service": {
> 	       "template": "/path/to/firmware.json",
> 	     }
>          }
>      }

I'm wondering whenever it makes sense to put this into the firmware
description at all?  The reason this is done for flash images is that
code and vars can not be matched freely.  This is simply not the case
with host-managed variable stores.

Maybe have separate json files describing the variable store template
only, and expect libvirt searching for one in case the firmware
descriptor has the 'host-uefi-vars' feature flag set?

Many fields of the firmware descriptor format continue to make sense
in that case.  Description obviously.  Architecture too (the dbx
revocation list is arch specific, so x86 / arm need different
templates).  Also some features, specifically 'enrolled-keys'.

For the template filename we could use mapping->device = 'hostvars' with
mapping->filenname (somewhat quirky), or simply replace mapping with
something else.

I think this also fits better with the long-term plan that libvirt can
generate distro-specific variable stores based on libosinfo information
instead of using some template file.

take care,
  Gerd
Re: [PATCH 2/2] docs/interop/firmware: Introduce extended syntax for FirmwareMappingMemory
Posted by Daniel P. Berrangé 22 hours ago
On Tue, Jan 06, 2026 at 11:13:33AM +0100, Gerd Hoffmann wrote:
>   Hi,
> 
> > IMHO we could add merely a new "uefi-vars-service" such that it looks
> > like this:
> > 
> >      {
> >          "mapping: {
> >              "device": "memory",
> >              "filename": "/path/to/firmware.bin",
> > 	     "uefi-vars-service": {
> > 	       "template": "/path/to/firmware.json",
> > 	     }
> >          }
> >      }
> 
> I'm wondering whenever it makes sense to put this into the firmware
> description at all?  The reason this is done for flash images is that
> code and vars can not be matched freely.  This is simply not the case
> with host-managed variable stores.

True, though our schema of course allows us to list the exact same
json file against multiple firmware binaries. The more significant
problem is probably in the opposite direction - there are can
arbitrarily many json vars files that are valid for use with any
single firmware binary. This would require us to have multiple
firmware descriptors pointing to the same binary, each with a
differnt json file. Essentially the descriptors need to define
the combinatorial expansion of firmware + json files, and this
feels like it is getting a bit silly.

> Maybe have separate json files describing the variable store template
> only, and expect libvirt searching for one in case the firmware
> descriptor has the 'host-uefi-vars' feature flag set?

Or just have the host-uefi-vars feature flag alone, and then libvirt
can just invoke virt-firmware in whatever way it needs to create the
json templates and not worry about providing any pre-defined json
files.  All the default microsoft CAs are ultimately embedded in
virt-firmware and spat out when given the right args.

If libvirt doesn't want to run  virt-firmware every time, libvirt
could cache some common templates itself.

> Many fields of the firmware descriptor format continue to make sense
> in that case.  Description obviously.  Architecture too (the dbx
> revocation list is arch specific, so x86 / arm need different
> templates).  Also some features, specifically 'enrolled-keys'.
> 
> For the template filename we could use mapping->device = 'hostvars' with
> mapping->filenname (somewhat quirky), or simply replace mapping with
> something else.
> 
> I think this also fits better with the long-term plan that libvirt can
> generate distro-specific variable stores based on libosinfo information
> instead of using some template file.

With 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 :|
Re: [PATCH 2/2] docs/interop/firmware: Introduce extended syntax for FirmwareMappingMemory
Posted by Gerd Hoffmann 21 hours ago
  Hi,

> > Maybe have separate json files describing the variable store template
> > only, and expect libvirt searching for one in case the firmware
> > descriptor has the 'host-uefi-vars' feature flag set?
> 
> Or just have the host-uefi-vars feature flag alone, and then libvirt
> can just invoke virt-firmware in whatever way it needs to create the
> json templates and not worry about providing any pre-defined json
> files.  All the default microsoft CAs are ultimately embedded in
> virt-firmware and spat out when given the right args.

Makes sense too, given that the long-term plan is to do that anyway to
allow distro-specific varstore setups.  So doing that right from the
start, only without the certificate configuration part which comes
later, looks like good approach too.

BTW: virt-firmware-rs.rpm has a utility (virt-fw-vars-setup) which can
do exactly that without pulling in python as dependency.

Question is how we enable/disable secure boot then ... 

One option would be to special-case the enrolled-keys feature for the
host-uefi-vars.  A little bit hackish, but with the advantage that the
switch would be transparent for libvirt users, existing xml syntax
continues to work.

Another option would be to add xml syntax for varstore setup, which will
be needed anyway in the future for the custom certificate setup.

take care,
  Gerd
Re: [PATCH 2/2] docs/interop/firmware: Introduce extended syntax for FirmwareMappingMemory
Posted by Daniel P. Berrangé 21 hours ago
On Tue, Jan 06, 2026 at 12:28:38PM +0100, Gerd Hoffmann wrote:
>   Hi,
> 
> > > Maybe have separate json files describing the variable store template
> > > only, and expect libvirt searching for one in case the firmware
> > > descriptor has the 'host-uefi-vars' feature flag set?
> > 
> > Or just have the host-uefi-vars feature flag alone, and then libvirt
> > can just invoke virt-firmware in whatever way it needs to create the
> > json templates and not worry about providing any pre-defined json
> > files.  All the default microsoft CAs are ultimately embedded in
> > virt-firmware and spat out when given the right args.
> 
> Makes sense too, given that the long-term plan is to do that anyway to
> allow distro-specific varstore setups.  So doing that right from the
> start, only without the certificate configuration part which comes
> later, looks like good approach too.
> 
> BTW: virt-firmware-rs.rpm has a utility (virt-fw-vars-setup) which can
> do exactly that without pulling in python as dependency.
> 
> Question is how we enable/disable secure boot then ... 
> 
> One option would be to special-case the enrolled-keys feature for the
> host-uefi-vars.  A little bit hackish, but with the advantage that the
> switch would be transparent for libvirt users, existing xml syntax
> continues to work.

Currently the libvirt XML features are matched to firmware descriptor
features, but that's just an internal implementation detail for libvirt.
We can have our impl match XML features to virt-fw-var-setup flags
instead where appropriate for the firmware.

> 
> Another option would be to add xml syntax for varstore setup, which will
> be needed anyway in the future for the custom certificate setup.
> 
> take care,
>   Gerd
> 

With 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 :|