mbox series

[v2,00/21] hw/uefi: add uefi variable service

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

Message

Gerd Hoffmann Jan. 7, 2025, 3:33 p.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_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).

v2 changes:
 - fully implement authenticated variables.
 - various cleanups and fixes.

enjoy & take care,
  Gerd

Gerd Hoffmann (21):
  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: add uefi-vars-isa device
  hw/arm: add uefi variable support to virt machine type
  docs: add uefi variable service documentation
  hw/uefi: add MAINTAINERS entry

 include/hw/arm/virt.h              |   2 +
 include/hw/uefi/var-service-api.h  |  40 ++
 include/hw/uefi/var-service-edk2.h | 227 +++++++++
 include/hw/uefi/var-service.h      | 186 ++++++++
 hw/arm/virt.c                      |  41 ++
 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          |  91 ++++
 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, 3796 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

Daniel P. Berrangé Jan. 7, 2025, 3:41 p.m. UTC | #1
On Tue, Jan 07, 2025 at 04:33:27PM +0100, Gerd Hoffmann 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 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.

In the coconut-svsm project there's a likely need for coconut to
provide a UEFI variable store, since we can't store plain text
variables in host context for confidential VMs.

Am I right in thinking that this design approach could be reused
in coconut context with coconut providing the equivalent backend
service, and EDK2 using the same driver to talk to either QEMU
or Coconut's service  ?


With regards,
Daniel
Gerd Hoffmann Jan. 7, 2025, 3:51 p.m. UTC | #2
Hi,

> > 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.
> 
> In the coconut-svsm project there's a likely need for coconut to
> provide a UEFI variable store, since we can't store plain text
> variables in host context for confidential VMs.
> 
> Am I right in thinking that this design approach could be reused
> in coconut context with coconut providing the equivalent backend
> service, and EDK2 using the same driver to talk to either QEMU
> or Coconut's service  ?

Yes, that is the idea.  Right now the edk2 driver has two modes, one
talking to the isa device and one talking to the sysbus device.  Adding
a third mode which uses a svsm protocol should be easy.

Writing the efi variable service for coconut is the more challenging
item here.  /me plans to look into that later this year.

take care,
  Gerd
Marc-André Lureau Jan. 8, 2025, 11:53 a.m. UTC | #3
Hi

On Tue, Jan 7, 2025 at 7:34 PM 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 design idea is to reuse the request serialization protocol edk2 uses

I suppose this is a stable protocol. (some parts are set by the UEFI
spec probably)

There doesn't seem to be a defined way to query either side version or
capability, I suppose this could be added later assuming an initial
behaviour/magic etc.

> 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_VARS=TRUE").
> https://github.com/kraxel/edk2/commits/devel/secure-boot-external-vars
>

ok, perhaps it would be nice to have some basic unit tests in qemu
too. Almost none of this new code is exercised by the qemu tests yet.

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

Typically the kind of new code that I wish would be in Rust. But I
suppose it is too early yet, and you came to the same conclusion.
Probably a good candidate for rewrite though!

>
> v2 changes:
>  - fully implement authenticated variables.
>  - various cleanups and fixes.
>
> enjoy & take care,
>   Gerd
>
> Gerd Hoffmann (21):
>   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: add uefi-vars-isa device
>   hw/arm: add uefi variable support to virt machine type
>   docs: add uefi variable service documentation
>   hw/uefi: add MAINTAINERS entry
>
>  include/hw/arm/virt.h              |   2 +
>  include/hw/uefi/var-service-api.h  |  40 ++
>  include/hw/uefi/var-service-edk2.h | 227 +++++++++
>  include/hw/uefi/var-service.h      | 186 ++++++++
>  hw/arm/virt.c                      |  41 ++
>  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          |  91 ++++
>  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, 3796 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.47.1
>
Daniel P. Berrangé Jan. 8, 2025, 12:24 p.m. UTC | #4
On Wed, Jan 08, 2025 at 03:53:21PM +0400, Marc-André Lureau wrote:
> Hi
> 
> On Tue, Jan 7, 2025 at 7:34 PM 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 design idea is to reuse the request serialization protocol edk2 uses
> 
> I suppose this is a stable protocol. (some parts are set by the UEFI
> spec probably)
> 
> There doesn't seem to be a defined way to query either side version or
> capability, I suppose this could be added later assuming an initial
> behaviour/magic etc.
> 
> > 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_VARS=TRUE").
> > https://github.com/kraxel/edk2/commits/devel/secure-boot-external-vars
> >
> 
> ok, perhaps it would be nice to have some basic unit tests in qemu
> too. Almost none of this new code is exercised by the qemu tests yet.
> 
> > The uefi-vars device re-implements the privileged edk2 protocols
> > (i.e. the code running in SMM mode).
> 
> Typically the kind of new code that I wish would be in Rust. But I
> suppose it is too early yet, and you came to the same conclusion.
> Probably a good candidate for rewrite though!

Perhaps too early for the device impl, but I would have thought
the general var-service code could be done in rust today. It does
not have all that much interaction with other parts of the QEMU
codebase & thus wouldn't be building on the moving target of the
QOM/Device abstractions. It would also be the prime part that
could be shared with coconut-svsm too.

> 
> >
> > v2 changes:
> >  - fully implement authenticated variables.
> >  - various cleanups and fixes.
> >
> > enjoy & take care,
> >   Gerd
> >
> > Gerd Hoffmann (21):
> >   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: add uefi-vars-isa device
> >   hw/arm: add uefi variable support to virt machine type
> >   docs: add uefi variable service documentation
> >   hw/uefi: add MAINTAINERS entry
> >
> >  include/hw/arm/virt.h              |   2 +
> >  include/hw/uefi/var-service-api.h  |  40 ++
> >  include/hw/uefi/var-service-edk2.h | 227 +++++++++
> >  include/hw/uefi/var-service.h      | 186 ++++++++
> >  hw/arm/virt.c                      |  41 ++
> >  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          |  91 ++++
> >  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, 3796 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.47.1
> >
> 

With regards,
Daniel
Gerd Hoffmann Jan. 8, 2025, 1:45 p.m. UTC | #5
Hi,

> > Typically the kind of new code that I wish would be in Rust. But I
> > suppose it is too early yet, and you came to the same conclusion.
> > Probably a good candidate for rewrite though!
> 
> Perhaps too early for the device impl, but I would have thought
> the general var-service code could be done in rust today. It does
> not have all that much interaction with other parts of the QEMU
> codebase & thus wouldn't be building on the moving target of the
> QOM/Device abstractions. It would also be the prime part that
> could be shared with coconut-svsm too.

That remains to be seen.  The svsm code will indeed most likely be
written in rust.  There are a number of noteworthy differences though:

 * It's a totally different environment (no_std).
 * Persistent storage for the vars will be different.
 * Crypto code will be different (probably openssl because the vTPM
   needs that too, possibly native rust crypto).

So not fully sure the code sharing part will actually work out ...

I plan to keep this in mind when designing the svsm code though, and
then revisit things once I have some actual rust code for this and the
rust support in qemu is more mature.

take care,
  Gerd
Gerd Hoffmann Jan. 8, 2025, 2:02 p.m. UTC | #6
Hi,

> > The design idea is to reuse the request serialization protocol edk2 uses
> 
> I suppose this is a stable protocol. (some parts are set by the UEFI
> spec probably)

Partly yes, partly this is edk2-internal stuff.  In theory there is some
freedom to change the internal parts, in practice I have not seen this
changing in incompatible ways to far.

> There doesn't seem to be a defined way to query either side version or
> capability, I suppose this could be added later assuming an initial
> behaviour/magic etc.

There is a 'magic' device register, so should the need arise there is
the option to define a new magic cookie for incompatible changes.

> > A edk2 test branch can be found here (build with "-D QEMU_VARS=TRUE").
> > https://github.com/kraxel/edk2/commits/devel/secure-boot-external-vars
> 
> ok, perhaps it would be nice to have some basic unit tests in qemu
> too. Almost none of this new code is exercised by the qemu tests yet.

I have some unit tests, they are using edk2 though.  So having unit
tests right from the start is somewhat difficult.  I don't feel like
re-implementing the guest side of the serialization protocol for the
qemu unit tests.

Once the edk2 changes have landed in a edk2 stable tag and qemu bundled
firmware has been updated it should be relatively easy to add those
tests to qemu.  With updated edk2 firmware we can also add some
end-to-end testing such as booting a fedora cloud image with secure boot
turned on.

take care,
  Gerd