mbox series

[v3,00/23] hw/uefi: add uefi variable service

Message ID 20250211092324.965440-1-kraxel@redhat.com (mailing list archive)
Headers show
Series hw/uefi: add uefi variable service | expand

Message

Gerd Hoffmann Feb. 11, 2025, 9:22 a.m. UTC
This patch adds a virtual device to qemu which the uefi firmware can use
to store variables.  This moves the UEFI variable management from
privileged guest code (managing vars in pflash) to the host.  Main
advantage is that the need to have privilege separation in the guest
goes away.

On x86 privileged guest code runs in SMM.  It's supported by kvm, but
not liked much by various stakeholders in cloud space due to the
complexity SMM emulation brings.

On arm privileged guest code runs in el3 (aka secure world).  This is
not supported by kvm, which is unlikely to change anytime soon given
that even el2 support (nested virt) is being worked on for years and is
not yet in mainline.

The design idea is to reuse the request serialization protocol edk2 uses
for communication between SMM and non-SMM code, so large chunks of the
edk2 variable driver stack can be used unmodified.  Only the driver
which traps into SMM mode must be replaced by a driver which talks to
qemu instead.

A edk2 test branch can be found here (build with "-D QEMU_PV_VARS=TRUE").
https://github.com/kraxel/edk2/commits/devel/secure-boot-external-vars

The uefi-vars device re-implements the privileged edk2 protocols
(i.e. the code running in SMM mode).

v3 changes:
 - switch sysbus device variant to use the qemu platform bus.
 - misc minor changes.
v2 changes:
 - fully implement authenticated variables.
 - various cleanups and fixes.

enjoy & take care,
  Gerd

Gerd Hoffmann (23):
  hw/uefi: add include/hw/uefi/var-service-api.h
  hw/uefi: add include/hw/uefi/var-service-edk2.h
  hw/uefi: add include/hw/uefi/var-service.h
  hw/uefi: add var-service-guid.c
  hw/uefi: add var-service-utils.c
  hw/uefi: add var-service-vars.c
  hw/uefi: add var-service-auth.c
  hw/uefi: add var-service-policy.c
  hw/uefi: add var-service-core.c
  hw/uefi: add var-service-pkcs7.c
  hw/uefi: add var-service-pkcs7-stub.c
  hw/uefi: add var-service-siglist.c
  hw/uefi: add var-service-json.c + qapi for NV vars.
  hw/uefi: add trace-events
  hw/uefi: add UEFI_VARS to Kconfig
  hw/uefi: add to meson
  hw/uefi: add uefi-vars-sysbus device
  hw/uefi-vars-sysbus: qemu platform bus support
  hw/uefi-vars-sysbus: allow for arm virt
  hw/uefi: add uefi-vars-isa device
  hw/uefi-vars-isa: add acpi device
  docs: add uefi variable service documentation
  hw/uefi: add MAINTAINERS entry

 include/hw/uefi/var-service-api.h  |  43 ++
 include/hw/uefi/var-service-edk2.h | 227 +++++++++
 include/hw/uefi/var-service.h      | 186 ++++++++
 hw/arm/virt.c                      |   2 +
 hw/core/sysbus-fdt.c               |  24 +
 hw/uefi/var-service-auth.c         | 361 ++++++++++++++
 hw/uefi/var-service-core.c         | 237 ++++++++++
 hw/uefi/var-service-guid.c         |  99 ++++
 hw/uefi/var-service-isa.c          | 115 +++++
 hw/uefi/var-service-json.c         | 242 ++++++++++
 hw/uefi/var-service-pkcs7-stub.c   |  16 +
 hw/uefi/var-service-pkcs7.c        | 436 +++++++++++++++++
 hw/uefi/var-service-policy.c       | 370 +++++++++++++++
 hw/uefi/var-service-siglist.c      | 212 +++++++++
 hw/uefi/var-service-sysbus.c       |  90 ++++
 hw/uefi/var-service-utils.c        | 241 ++++++++++
 hw/uefi/var-service-vars.c         | 725 +++++++++++++++++++++++++++++
 MAINTAINERS                        |   6 +
 docs/devel/index-internals.rst     |   1 +
 docs/devel/uefi-vars.rst           |  66 +++
 hw/Kconfig                         |   1 +
 hw/meson.build                     |   1 +
 hw/uefi/Kconfig                    |   9 +
 hw/uefi/LIMITATIONS.md             |   7 +
 hw/uefi/meson.build                |  24 +
 hw/uefi/trace-events               |  17 +
 meson.build                        |   1 +
 qapi/meson.build                   |   1 +
 qapi/qapi-schema.json              |   1 +
 qapi/uefi.json                     |  45 ++
 30 files changed, 3806 insertions(+)
 create mode 100644 include/hw/uefi/var-service-api.h
 create mode 100644 include/hw/uefi/var-service-edk2.h
 create mode 100644 include/hw/uefi/var-service.h
 create mode 100644 hw/uefi/var-service-auth.c
 create mode 100644 hw/uefi/var-service-core.c
 create mode 100644 hw/uefi/var-service-guid.c
 create mode 100644 hw/uefi/var-service-isa.c
 create mode 100644 hw/uefi/var-service-json.c
 create mode 100644 hw/uefi/var-service-pkcs7-stub.c
 create mode 100644 hw/uefi/var-service-pkcs7.c
 create mode 100644 hw/uefi/var-service-policy.c
 create mode 100644 hw/uefi/var-service-siglist.c
 create mode 100644 hw/uefi/var-service-sysbus.c
 create mode 100644 hw/uefi/var-service-utils.c
 create mode 100644 hw/uefi/var-service-vars.c
 create mode 100644 docs/devel/uefi-vars.rst
 create mode 100644 hw/uefi/Kconfig
 create mode 100644 hw/uefi/LIMITATIONS.md
 create mode 100644 hw/uefi/meson.build
 create mode 100644 hw/uefi/trace-events
 create mode 100644 qapi/uefi.json

Comments

Ard Biesheuvel Feb. 13, 2025, 9:41 a.m. UTC | #1
On Tue, 11 Feb 2025 at 10:23, Gerd Hoffmann <kraxel@redhat.com> wrote:
>
> This patch adds a virtual device to qemu which the uefi firmware can use
> to store variables.  This moves the UEFI variable management from
> privileged guest code (managing vars in pflash) to the host.  Main
> advantage is that the need to have privilege separation in the guest
> goes away.
>
> On x86 privileged guest code runs in SMM.  It's supported by kvm, but
> not liked much by various stakeholders in cloud space due to the
> complexity SMM emulation brings.
>
> On arm privileged guest code runs in el3 (aka secure world).  This is
> not supported by kvm, which is unlikely to change anytime soon given
> that even el2 support (nested virt) is being worked on for years and is
> not yet in mainline.
>

The secure counterpart of this would never execute at EL3 on ARM, but
at secure EL1 (or potentially at secure EL2 on more recent CPUs). But
the general point that this is difficult to virtualize stands; I've
contemplated doing something similar to SMM emulation using non-secure
EL1 in a separate VM to provide an execution context that could those
the secure EL1 payload (using standalone MM) but I never found the
time to work on this.

> The design idea is to reuse the request serialization protocol edk2 uses
> for communication between SMM and non-SMM code, so large chunks of the
> edk2 variable driver stack can be used unmodified.  Only the driver
> which traps into SMM mode must be replaced by a driver which talks to
> qemu instead.
>

I like this approach, but I will note that these protocols are not
standardized: it is basically an EDK2 implementation detail, but this
is fine, given that this targets firmware that is based on EDK2 (or
its derivatives).

Using a single shared communication buffer makes it feasible to
paravirtualize this even under confidential compute scenarios (where
the buffer needs special shared mapping semantics), and I think this
might be useful, even if in principle, the VMM is untrusted in such
scenarios. Paravirtualizing the individual variable services directly
creates a problem here, given that the firmware cannot share mappings
of arbitrary arguments passed via pointers.

For the record, I've already acked the OVMF counterpart of this, and
I've started working on adding support for this to my minimal EFI for
mach-virt [0], which is another scenario (i.e., minimal EFI compatible
firmware for micro VMs) where having this complexity in the VMM is
preferred.


[0] https://github.com/ardbiesheuvel/efilite



> A edk2 test branch can be found here (build with "-D QEMU_PV_VARS=TRUE").
> https://github.com/kraxel/edk2/commits/devel/secure-boot-external-vars
>
> The uefi-vars device re-implements the privileged edk2 protocols
> (i.e. the code running in SMM mode).
>
> v3 changes:
>  - switch sysbus device variant to use the qemu platform bus.
>  - misc minor changes.
> v2 changes:
>  - fully implement authenticated variables.
>  - various cleanups and fixes.
>
> enjoy & take care,
>   Gerd
>
> Gerd Hoffmann (23):
>   hw/uefi: add include/hw/uefi/var-service-api.h
>   hw/uefi: add include/hw/uefi/var-service-edk2.h
>   hw/uefi: add include/hw/uefi/var-service.h
>   hw/uefi: add var-service-guid.c
>   hw/uefi: add var-service-utils.c
>   hw/uefi: add var-service-vars.c
>   hw/uefi: add var-service-auth.c
>   hw/uefi: add var-service-policy.c
>   hw/uefi: add var-service-core.c
>   hw/uefi: add var-service-pkcs7.c
>   hw/uefi: add var-service-pkcs7-stub.c
>   hw/uefi: add var-service-siglist.c
>   hw/uefi: add var-service-json.c + qapi for NV vars.
>   hw/uefi: add trace-events
>   hw/uefi: add UEFI_VARS to Kconfig
>   hw/uefi: add to meson
>   hw/uefi: add uefi-vars-sysbus device
>   hw/uefi-vars-sysbus: qemu platform bus support
>   hw/uefi-vars-sysbus: allow for arm virt
>   hw/uefi: add uefi-vars-isa device
>   hw/uefi-vars-isa: add acpi device
>   docs: add uefi variable service documentation
>   hw/uefi: add MAINTAINERS entry
>
>  include/hw/uefi/var-service-api.h  |  43 ++
>  include/hw/uefi/var-service-edk2.h | 227 +++++++++
>  include/hw/uefi/var-service.h      | 186 ++++++++
>  hw/arm/virt.c                      |   2 +
>  hw/core/sysbus-fdt.c               |  24 +
>  hw/uefi/var-service-auth.c         | 361 ++++++++++++++
>  hw/uefi/var-service-core.c         | 237 ++++++++++
>  hw/uefi/var-service-guid.c         |  99 ++++
>  hw/uefi/var-service-isa.c          | 115 +++++
>  hw/uefi/var-service-json.c         | 242 ++++++++++
>  hw/uefi/var-service-pkcs7-stub.c   |  16 +
>  hw/uefi/var-service-pkcs7.c        | 436 +++++++++++++++++
>  hw/uefi/var-service-policy.c       | 370 +++++++++++++++
>  hw/uefi/var-service-siglist.c      | 212 +++++++++
>  hw/uefi/var-service-sysbus.c       |  90 ++++
>  hw/uefi/var-service-utils.c        | 241 ++++++++++
>  hw/uefi/var-service-vars.c         | 725 +++++++++++++++++++++++++++++
>  MAINTAINERS                        |   6 +
>  docs/devel/index-internals.rst     |   1 +
>  docs/devel/uefi-vars.rst           |  66 +++
>  hw/Kconfig                         |   1 +
>  hw/meson.build                     |   1 +
>  hw/uefi/Kconfig                    |   9 +
>  hw/uefi/LIMITATIONS.md             |   7 +
>  hw/uefi/meson.build                |  24 +
>  hw/uefi/trace-events               |  17 +
>  meson.build                        |   1 +
>  qapi/meson.build                   |   1 +
>  qapi/qapi-schema.json              |   1 +
>  qapi/uefi.json                     |  45 ++
>  30 files changed, 3806 insertions(+)
>  create mode 100644 include/hw/uefi/var-service-api.h
>  create mode 100644 include/hw/uefi/var-service-edk2.h
>  create mode 100644 include/hw/uefi/var-service.h
>  create mode 100644 hw/uefi/var-service-auth.c
>  create mode 100644 hw/uefi/var-service-core.c
>  create mode 100644 hw/uefi/var-service-guid.c
>  create mode 100644 hw/uefi/var-service-isa.c
>  create mode 100644 hw/uefi/var-service-json.c
>  create mode 100644 hw/uefi/var-service-pkcs7-stub.c
>  create mode 100644 hw/uefi/var-service-pkcs7.c
>  create mode 100644 hw/uefi/var-service-policy.c
>  create mode 100644 hw/uefi/var-service-siglist.c
>  create mode 100644 hw/uefi/var-service-sysbus.c
>  create mode 100644 hw/uefi/var-service-utils.c
>  create mode 100644 hw/uefi/var-service-vars.c
>  create mode 100644 docs/devel/uefi-vars.rst
>  create mode 100644 hw/uefi/Kconfig
>  create mode 100644 hw/uefi/LIMITATIONS.md
>  create mode 100644 hw/uefi/meson.build
>  create mode 100644 hw/uefi/trace-events
>  create mode 100644 qapi/uefi.json
>
> --
> 2.48.1
>
Alexander Graf Feb. 13, 2025, 10:11 a.m. UTC | #2
On 13.02.25 10:41, Ard Biesheuvel wrote:
> On Tue, 11 Feb 2025 at 10:23, Gerd Hoffmann <kraxel@redhat.com> wrote:
>> This patch adds a virtual device to qemu which the uefi firmware can use
>> to store variables.  This moves the UEFI variable management from
>> privileged guest code (managing vars in pflash) to the host.  Main
>> advantage is that the need to have privilege separation in the guest
>> goes away.
>>
>> On x86 privileged guest code runs in SMM.  It's supported by kvm, but
>> not liked much by various stakeholders in cloud space due to the
>> complexity SMM emulation brings.
>>
>> On arm privileged guest code runs in el3 (aka secure world).  This is
>> not supported by kvm, which is unlikely to change anytime soon given
>> that even el2 support (nested virt) is being worked on for years and is
>> not yet in mainline.
>>
> The secure counterpart of this would never execute at EL3 on ARM, but
> at secure EL1 (or potentially at secure EL2 on more recent CPUs). But
> the general point that this is difficult to virtualize stands; I've
> contemplated doing something similar to SMM emulation using non-secure
> EL1 in a separate VM to provide an execution context that could those
> the secure EL1 payload (using standalone MM) but I never found the
> time to work on this.


Sounds very similar to what Ilias built a few years ago?

https://lore.kernel.org/all/20200511085205.GD73895@apalos.home/T/

Which reminds me: How similar is the protocol in this patch set to the 
one implemented in U-Boot? No need to reinvent the wheel over and over 
again.

>> The design idea is to reuse the request serialization protocol edk2 uses
>> for communication between SMM and non-SMM code, so large chunks of the
>> edk2 variable driver stack can be used unmodified.  Only the driver
>> which traps into SMM mode must be replaced by a driver which talks to
>> qemu instead.
>>
> I like this approach, but I will note that these protocols are not
> standardized: it is basically an EDK2 implementation detail, but this
> is fine, given that this targets firmware that is based on EDK2 (or
> its derivatives).
>
> Using a single shared communication buffer makes it feasible to
> paravirtualize this even under confidential compute scenarios (where
> the buffer needs special shared mapping semantics), and I think this
> might be useful, even if in principle, the VMM is untrusted in such
> scenarios. Paravirtualizing the individual variable services directly
> creates a problem here, given that the firmware cannot share mappings
> of arbitrary arguments passed via pointers.
>
> For the record, I've already acked the OVMF counterpart of this, and
> I've started working on adding support for this to my minimal EFI for
> mach-virt [0], which is another scenario (i.e., minimal EFI compatible
> firmware for micro VMs) where having this complexity in the VMM is
> preferred.


Amazing! :)


Alex
Ard Biesheuvel Feb. 13, 2025, 10:13 a.m. UTC | #3
On Thu, 13 Feb 2025 at 11:11, Alexander Graf <graf@amazon.com> wrote:
>
>
> On 13.02.25 10:41, Ard Biesheuvel wrote:
> > On Tue, 11 Feb 2025 at 10:23, Gerd Hoffmann <kraxel@redhat.com> wrote:
> >> This patch adds a virtual device to qemu which the uefi firmware can use
> >> to store variables.  This moves the UEFI variable management from
> >> privileged guest code (managing vars in pflash) to the host.  Main
> >> advantage is that the need to have privilege separation in the guest
> >> goes away.
> >>
> >> On x86 privileged guest code runs in SMM.  It's supported by kvm, but
> >> not liked much by various stakeholders in cloud space due to the
> >> complexity SMM emulation brings.
> >>
> >> On arm privileged guest code runs in el3 (aka secure world).  This is
> >> not supported by kvm, which is unlikely to change anytime soon given
> >> that even el2 support (nested virt) is being worked on for years and is
> >> not yet in mainline.
> >>
> > The secure counterpart of this would never execute at EL3 on ARM, but
> > at secure EL1 (or potentially at secure EL2 on more recent CPUs). But
> > the general point that this is difficult to virtualize stands; I've
> > contemplated doing something similar to SMM emulation using non-secure
> > EL1 in a separate VM to provide an execution context that could those
> > the secure EL1 payload (using standalone MM) but I never found the
> > time to work on this.
>
>
> Sounds very similar to what Ilias built a few years ago?
>
> https://lore.kernel.org/all/20200511085205.GD73895@apalos.home/T/
>
> Which reminds me: How similar is the protocol in this patch set to the
> one implemented in U-Boot? No need to reinvent the wheel over and over
> again.
>

Identical afaik
Ilias Apalodimas Feb. 20, 2025, 12:43 p.m. UTC | #4
Hi Alex, Ard, Gerd,

Thanks for roping me in,

On Thu, 13 Feb 2025 at 12:13, Ard Biesheuvel <ardb@kernel.org> wrote:
>
> On Thu, 13 Feb 2025 at 11:11, Alexander Graf <graf@amazon.com> wrote:
> >
> >
> > On 13.02.25 10:41, Ard Biesheuvel wrote:
> > > On Tue, 11 Feb 2025 at 10:23, Gerd Hoffmann <kraxel@redhat.com> wrote:
> > >> This patch adds a virtual device to qemu which the uefi firmware can use
> > >> to store variables.  This moves the UEFI variable management from
> > >> privileged guest code (managing vars in pflash) to the host.  Main
> > >> advantage is that the need to have privilege separation in the guest
> > >> goes away.
> > >>
> > >> On x86 privileged guest code runs in SMM.  It's supported by kvm, but
> > >> not liked much by various stakeholders in cloud space due to the
> > >> complexity SMM emulation brings.
> > >>
> > >> On arm privileged guest code runs in el3 (aka secure world).  This is
> > >> not supported by kvm, which is unlikely to change anytime soon given
> > >> that even el2 support (nested virt) is being worked on for years and is
> > >> not yet in mainline.
> > >>
> > > The secure counterpart of this would never execute at EL3 on ARM, but
> > > at secure EL1 (or potentially at secure EL2 on more recent CPUs). But
> > > the general point that this is difficult to virtualize stands; I've
> > > contemplated doing something similar to SMM emulation using non-secure
> > > EL1 in a separate VM to provide an execution context that could those
> > > the secure EL1 payload (using standalone MM) but I never found the
> > > time to work on this.
> >
> >
> > Sounds very similar to what Ilias built a few years ago?
> >
> > https://lore.kernel.org/all/20200511085205.GD73895@apalos.home/T/
> >
> > Which reminds me: How similar is the protocol in this patch set to the
> > one implemented in U-Boot? No need to reinvent the wheel over and over
> > again.
> >
>
> Identical afaik

I don't know what I can do to help here but I'll explain what we have
in case we can figure something out .
The idea is very close indeed and in fact it works on QEMU with some
hacks for arm(7/8). [0]. Since QEMU doesn't have an RPMB emulation I
am providing one in software in U-Boot. That's obviously useless in
real use usecases, since the memory backend disappears when we leave
the firmware, but still useful for testing.

I also have a blog explaining the arm specific bits here [1].

The TL;DR is that we set up everything StMM needs inside OP-TEE and
execute it in S-EL1. For storage, we have a 'special' StMM driver that
sends requests to OP-TEE and uses its RPMB support to write sensitive
data on the device.

[0] https://git.linaro.org/people/ilias.apalodimas/efi_optee_variables.git/
[1] https://old.linaro.org/blog/protected-uefi-variables-with-u-boot/

Let me know if you need anything else

Cheers
/Ilias