diff mbox series

[kvm-unit-tests,02/13] x86: AMD SEV-ES: Setup #VC exception handler for AMD SEV-ES

Message ID 20220120125122.4633-3-varad.gautam@suse.com (mailing list archive)
State New, archived
Headers show
Series Add #VC exception handling for AMD SEV-ES | expand

Commit Message

Varad Gautam Jan. 20, 2022, 12:51 p.m. UTC
AMD SEV-ES defines a new guest exception that gets triggered on
some vmexits to allow the guest to control what state gets shared
with the host. kvm-unit-tests currently relies on UEFI to provide
this #VC exception handler.

Switch the tests to install their own #VC handler on early bootup
to process these exits, just after GHCB has been mapped.

If --amdsev-efi-vc is passed during ./configure, the tests will
continue using the UEFI #VC handler.

Signed-off-by: Varad Gautam <varad.gautam@suse.com>
---
 Makefile             |  3 +++
 configure            | 16 ++++++++++++++++
 lib/x86/amd_sev.c    |  3 ++-
 lib/x86/amd_sev.h    |  1 +
 lib/x86/amd_sev_vc.c | 14 ++++++++++++++
 lib/x86/desc.c       | 15 +++++++++++++++
 lib/x86/desc.h       |  1 +
 lib/x86/setup.c      | 10 ++++++++++
 x86/Makefile.common  |  1 +
 9 files changed, 63 insertions(+), 1 deletion(-)
 create mode 100644 lib/x86/amd_sev_vc.c

Comments

Marc Orr Jan. 30, 2022, 8:36 p.m. UTC | #1
On Thu, Jan 20, 2022 at 4:52 AM Varad Gautam <varad.gautam@suse.com> wrote:
>
> AMD SEV-ES defines a new guest exception that gets triggered on
> some vmexits to allow the guest to control what state gets shared
> with the host. kvm-unit-tests currently relies on UEFI to provide
> this #VC exception handler.
>
> Switch the tests to install their own #VC handler on early bootup
> to process these exits, just after GHCB has been mapped.
>
> If --amdsev-efi-vc is passed during ./configure, the tests will
> continue using the UEFI #VC handler.
>
> Signed-off-by: Varad Gautam <varad.gautam@suse.com>
> ---
>  Makefile             |  3 +++
>  configure            | 16 ++++++++++++++++
>  lib/x86/amd_sev.c    |  3 ++-
>  lib/x86/amd_sev.h    |  1 +
>  lib/x86/amd_sev_vc.c | 14 ++++++++++++++
>  lib/x86/desc.c       | 15 +++++++++++++++
>  lib/x86/desc.h       |  1 +
>  lib/x86/setup.c      | 10 ++++++++++
>  x86/Makefile.common  |  1 +
>  9 files changed, 63 insertions(+), 1 deletion(-)
>  create mode 100644 lib/x86/amd_sev_vc.c
>
> diff --git a/Makefile b/Makefile
> index 4f4ad23..94a0162 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -46,6 +46,9 @@ else
>  $(error Cannot build $(ARCH_NAME) tests as EFI apps)
>  endif
>  EFI_CFLAGS := -DTARGET_EFI
> +ifeq ($(AMDSEV_EFI_VC),y)
> +EFI_CFLAGS += -DAMDSEV_EFI_VC
> +endif
>  # The following CFLAGS and LDFLAGS come from:
>  #   - GNU-EFI/Makefile.defaults
>  #   - GNU-EFI/apps/Makefile
> diff --git a/configure b/configure
> index 41372ef..c687d9f 100755
> --- a/configure
> +++ b/configure
> @@ -29,6 +29,7 @@ host_key_document=
>  page_size=
>  earlycon=
>  target_efi=
> +amdsev_efi_vc=
>
>  usage() {
>      cat <<-EOF
> @@ -71,6 +72,8 @@ usage() {
>                                    Specify a PL011 compatible UART at address ADDR. Supported
>                                    register stride is 32 bit only.
>             --target-efi           Boot and run from UEFI
> +           --amdsev-efi-vc        Use UEFI-provided #VC handlers on AMD SEV/ES. Requires
> +                                  --target-efi.

What's the rationale to make the built-in #VC handler default? Note:
I'm not actually saying I have an opinion, one way or the other. But I
think the commit description (and cover letter) should explain why we
prefer what we're doing.

>  EOF
>      exit 1
>  }
> @@ -138,6 +141,9 @@ while [[ "$1" = -* ]]; do
>         --target-efi)
>             target_efi=y
>             ;;
> +       --amdsev-efi-vc)
> +           amdsev_efi_vc=y
> +           ;;
>         --help)
>             usage
>             ;;
> @@ -197,8 +203,17 @@ elif [ "$processor" = "arm" ]; then
>      processor="cortex-a15"
>  fi
>
> +if [ "$amdsev_efi_vc" ] && [ "$arch" != "x86_64" ]; then
> +    echo "--amdsev-efi-vc requires arch x86_64."
> +    usage
> +fi
> +
>  if [ "$arch" = "i386" ] || [ "$arch" = "x86_64" ]; then
>      testdir=x86
> +    if [ "$amdsev_efi_vc" ] && [ -z "$target_efi" ]; then
> +        echo "--amdsev-efi-vc requires --target-efi."
> +        usage
> +    fi
>  elif [ "$arch" = "arm" ] || [ "$arch" = "arm64" ]; then
>      testdir=arm
>      if [ "$target" = "qemu" ]; then
> @@ -356,6 +371,7 @@ WA_DIVIDE=$wa_divide
>  GENPROTIMG=${GENPROTIMG-genprotimg}
>  HOST_KEY_DOCUMENT=$host_key_document
>  TARGET_EFI=$target_efi
> +AMDSEV_EFI_VC=$amdsev_efi_vc
>  EOF
>  if [ "$arch" = "arm" ] || [ "$arch" = "arm64" ]; then
>      echo "TARGET=$target" >> config.mak
> diff --git a/lib/x86/amd_sev.c b/lib/x86/amd_sev.c
> index 6672214..bde126b 100644
> --- a/lib/x86/amd_sev.c
> +++ b/lib/x86/amd_sev.c
> @@ -14,6 +14,7 @@
>  #include "x86/vm.h"
>
>  static unsigned short amd_sev_c_bit_pos;
> +phys_addr_t ghcb_addr;
>
>  bool amd_sev_enabled(void)
>  {
> @@ -126,7 +127,7 @@ void setup_ghcb_pte(pgd_t *page_table)
>          * function searches GHCB's L1 pte, creates corresponding L1 ptes if not
>          * found, and unsets the c-bit of GHCB's L1 pte.
>          */
> -       phys_addr_t ghcb_addr, ghcb_base_addr;
> +       phys_addr_t ghcb_base_addr;
>         pteval_t *pte;
>
>         /* Read the current GHCB page addr */
> diff --git a/lib/x86/amd_sev.h b/lib/x86/amd_sev.h
> index 6a10f84..afbacf3 100644
> --- a/lib/x86/amd_sev.h
> +++ b/lib/x86/amd_sev.h
> @@ -54,6 +54,7 @@ efi_status_t setup_amd_sev(void);
>  bool amd_sev_es_enabled(void);
>  efi_status_t setup_amd_sev_es(void);
>  void setup_ghcb_pte(pgd_t *page_table);
> +void handle_sev_es_vc(struct ex_regs *regs);
>
>  unsigned long long get_amd_sev_c_bit_mask(void);
>  unsigned long long get_amd_sev_addr_upperbound(void);
> diff --git a/lib/x86/amd_sev_vc.c b/lib/x86/amd_sev_vc.c
> new file mode 100644
> index 0000000..8226121
> --- /dev/null
> +++ b/lib/x86/amd_sev_vc.c
> @@ -0,0 +1,14 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +#include "amd_sev.h"
> +
> +extern phys_addr_t ghcb_addr;
> +
> +void handle_sev_es_vc(struct ex_regs *regs)
> +{
> +       struct ghcb *ghcb = (struct ghcb *) ghcb_addr;
> +       if (!ghcb) {
> +               /* TODO: kill guest */
> +               return;
> +       }
> +}
> diff --git a/lib/x86/desc.c b/lib/x86/desc.c
> index 16b7256..73aa866 100644
> --- a/lib/x86/desc.c
> +++ b/lib/x86/desc.c
> @@ -3,6 +3,9 @@
>  #include "processor.h"
>  #include <setjmp.h>
>  #include "apic-defs.h"
> +#ifdef TARGET_EFI
> +#include "amd_sev.h"
> +#endif
>
>  /* Boot-related data structures */
>
> @@ -228,6 +231,9 @@ EX_E(ac, 17);
>  EX(mc, 18);
>  EX(xm, 19);
>  EX_E(cp, 21);
> +#ifdef TARGET_EFI
> +EX_E(vc, 29);
> +#endif
>
>  asm (".pushsection .text \n\t"
>       "__handle_exception: \n\t"
> @@ -293,6 +299,15 @@ void setup_idt(void)
>      handle_exception(13, check_exception_table);
>  }
>
> +void setup_amd_sev_es_vc(void)
> +{
> +       if (!amd_sev_es_enabled())
> +               return;
> +
> +       set_idt_entry(29, &vc_fault, 0);
> +       handle_exception(29, handle_sev_es_vc);
> +}
> +
>  unsigned exception_vector(void)
>  {
>      unsigned char vector;
> diff --git a/lib/x86/desc.h b/lib/x86/desc.h
> index b65539e..4fcbf9f 100644
> --- a/lib/x86/desc.h
> +++ b/lib/x86/desc.h
> @@ -220,6 +220,7 @@ void set_intr_alt_stack(int e, void *fn);
>  void print_current_tss_info(void);
>  handler handle_exception(u8 v, handler fn);
>  void unhandled_exception(struct ex_regs *regs, bool cpu);
> +void setup_amd_sev_es_vc(void);
>
>  bool test_for_exception(unsigned int ex, void (*trigger_func)(void *data),
>                         void *data);
> diff --git a/lib/x86/setup.c b/lib/x86/setup.c
> index bbd3468..6013602 100644
> --- a/lib/x86/setup.c
> +++ b/lib/x86/setup.c
> @@ -327,6 +327,16 @@ efi_status_t setup_efi(efi_bootinfo_t *efi_bootinfo)
>         smp_init();
>         setup_page_table();
>
> +#ifndef AMDSEV_EFI_VC
> +       if (amd_sev_es_enabled()) {

This is a tautology. `setup_amd_sev_es_vc()` returns immediately upon
`!amd_sev_es_enabled()`. Therefore, I think we should remove the check
here.

> +               /*
> +                * Switch away from the UEFI-installed #VC handler.
> +                * GHCB has already been mapped at this point.
> +                */
> +               setup_amd_sev_es_vc();
> +       }
> +#endif /* AMDSEV_EFI_VC */
> +
>         return EFI_SUCCESS;
>  }
>
> diff --git a/x86/Makefile.common b/x86/Makefile.common
> index 984444e..65d16e7 100644
> --- a/x86/Makefile.common
> +++ b/x86/Makefile.common
> @@ -24,6 +24,7 @@ cflatobjs += lib/x86/fault_test.o
>  cflatobjs += lib/x86/delay.o
>  ifeq ($(TARGET_EFI),y)
>  cflatobjs += lib/x86/amd_sev.o
> +cflatobjs += lib/x86/amd_sev_vc.o
>  cflatobjs += lib/efi.o
>  cflatobjs += x86/efi/reloc_x86_64.o
>  endif
> --
> 2.32.0
>

Thank you for adding this patch to the series!

I left a comment above asking about the default #VC handler, above.
And I'd like to expand on that question with my thoughts here.

As far as I understand, mostly from offline conversations with Sean, a
big motivation for the built-in #VC handler is that in the (very) long
term, people will build a guest firmware without a #VC handler. In
other words, eventually (I assume this is many years down the road),
the guest firmware will be enlightened enough to avoid NAEs
altogether, and instead explicitly call VMGEXIT explicitly, rather
than implicitly after triggering a #VC exception. To me, this all
sounds very far (e.g., 5-10 years) out. And thus, I still question the
value in adding the built-in #VC handler with this as the
justification now. However, it's clear from prior discussions that
this is the direction preferred by the community. So I'm not pushing
back. I'm just making sure we're all on the same page here. Please let
me know if I'm mis-understanding this rationale or missing any reasons
for why folks want a built-in #VC handler. And again, I think the
cover letter should be updated with all the reasons why we're doing
what we're doing.

Where I think this work could be useful in the shorter term is
testing. We have three pieces of code to test:
1. Host-side VMGEXIT handler.
2. Guest-side firmware #VC handler.
3. Guest-side kernel #VC handler.

Technically, I guess there are multiple #VC handlers within each
guest-side component, depending on the boot stage. But let's consider
the steady state handlers that operate after initial guest-side boot
stages for now.

Using the UEFI's builtin #VC handler tests:
- Guest-side firmware #VC handler + host-side VMGEXIT handler.

Notably, it does _not_ test the guest-side kernel #VC handler. Looking
ahead in the series, I can see that the code for the new builtin
kvm-unit-tests #VC handler is basically copied from the Linux kernel
#VC handler. Therefore, we could use this work to test:
- Guest-side kernel #VC handler + host-side VMGEXIT handler.

The difficulty with using this approach to test the guest-side #VC
handler is that we now have two copies of the code that can get out of
sync: one in the kernel and a 2nd in kvm-unit-tests. However, maybe
this is OK. And maybe we can even try to ameliorate this issue with
some scripts to compare the two codes or some policy that when we
update the Linux kernel proper we send a follow-up patch to
kvm-unit-tests.

Another approach to test the guest-side kernel #VC handler would be to
add tests to run from within a Linux guest. E.g., maybe Linux
selftests could work here. We actually have something like this
internally, But the way it was written would make it non-trivial to
upstream.

Thoughts?

Tested-by: Marc Orr <marcorr@google.com>
Joerg Roedel Feb. 4, 2022, 10:55 a.m. UTC | #2
Hi Marc,

On Sun, Jan 30, 2022 at 12:36:48PM -0800, Marc Orr wrote:
> Please let me know if I'm mis-understanding this rationale or missing
> any reasons for why folks want a built-in #VC handler.

There are a couple of reasons which come all come down to one goal:
Robustnes of the kvm-unit-tests.

If kvm-unit-tests continue to use the firmware #VC handler after
ExitBootServices there needs to be a contract between the test
framework and the firmware about:

	1) Page-table layout - The page table needs to map the firmware
	   and the shared GHCB used by the firmware.

	2) The firmware is required to keep its #VC handler in the
	   current IDT for kvm-unit-tests to find it and copy the #VC
	   entry into its own IDT.

	3) The firmware #VC handler might use state which is not
	   available anymore after ExitBootServices.

	4) If the firmware uses the kvm-unit-test GHCB after
	   ExitBootServices, it has the get the GHCB address from the
	   GHCB MSR, requiring an identity mapping.
	   Moreover it requires to keep the address of the GHCB in the
	   MSR at all times where a #VC could happen. This could be a
	   problem when we start to add SEV-ES specific tests to the
	   unit-tests, explcitily testing the MSR protocol.

It is easy to violate this implicit protocol and breaking kvm-unit-tests
just by a new version of OVMF being used. I think that is not a very
robust approach and a separate #VC handler in the unit-test framework
makes sense even now.

Regards,
Marc Orr Feb. 4, 2022, 3:57 p.m. UTC | #3
On Fri, Feb 4, 2022 at 2:55 AM Joerg Roedel <jroedel@suse.de> wrote:
>
> Hi Marc,
>
> On Sun, Jan 30, 2022 at 12:36:48PM -0800, Marc Orr wrote:
> > Please let me know if I'm mis-understanding this rationale or missing
> > any reasons for why folks want a built-in #VC handler.
>
> There are a couple of reasons which come all come down to one goal:
> Robustnes of the kvm-unit-tests.
>
> If kvm-unit-tests continue to use the firmware #VC handler after
> ExitBootServices there needs to be a contract between the test
> framework and the firmware about:
>
>         1) Page-table layout - The page table needs to map the firmware
>            and the shared GHCB used by the firmware.
>
>         2) The firmware is required to keep its #VC handler in the
>            current IDT for kvm-unit-tests to find it and copy the #VC
>            entry into its own IDT.

Yeah. I think we already resolved these first two issues in the
initial patch set.

>         3) The firmware #VC handler might use state which is not
>            available anymore after ExitBootServices.

Of all the issues listed, this one seems the most serious.

>         4) If the firmware uses the kvm-unit-test GHCB after
>            ExitBootServices, it has the get the GHCB address from the
>            GHCB MSR, requiring an identity mapping.
>            Moreover it requires to keep the address of the GHCB in the
>            MSR at all times where a #VC could happen. This could be a
>            problem when we start to add SEV-ES specific tests to the
>            unit-tests, explcitily testing the MSR protocol.

Ack. I'd think we could require tests to save/restore the GHCB MSR.

> It is easy to violate this implicit protocol and breaking kvm-unit-tests
> just by a new version of OVMF being used. I think that is not a very
> robust approach and a separate #VC handler in the unit-test framework
> makes sense even now.

Thanks for the explanation! I hope we can keep the UEFI #VC handler
working, because like I mentioned, I think this work can be used to
test that code inside of UEFI. But I guess time will tell.

Of all the points listed above, I think point #3 is the most
concerning. The others seem like they can be managed.

Nonetheless, based on this explanation plus prior mailing list
discussions, it is clear that the preference is to make the built-in
#VC handler the default. My only request to Varad is to update the
cover letter/patch descriptions with a summary of this discussion.
Also, it might be worth adding a comment in the configure script
mentioning that the built-in #VC handler is the default due to
robustness and future-proofing concerns.

Regarding code review and testing, I can help with the following:
- Compare the patches being pulled into kvm-unit-tests to what's in
the Linux kernel and add my Reviewed-by tags if I don't see any
meaningful discrepancies.
- Test the entire series on Google's setup, which doesn't use QEMU and
add my Tested-by tag accordingly. My previous Tested-by tags were on
individual patches. I have not yet tested the entire series.

Please let me know if this is useful. If not, I wouldn't spend the time :-).

> Regards,
>
> --
> Jörg Rödel
> jroedel@suse.de
>
> SUSE Software Solutions Germany GmbH
> Maxfeldstr. 5
> 90409 Nürnberg
> Germany
>
> (HRB 36809, AG Nürnberg)
> Geschäftsführer: Ivo Totev
>
Sean Christopherson Feb. 4, 2022, 4:30 p.m. UTC | #4
On Fri, Feb 04, 2022, Marc Orr wrote:
> On Fri, Feb 4, 2022 at 2:55 AM Joerg Roedel <jroedel@suse.de> wrote:
> >         3) The firmware #VC handler might use state which is not
> >            available anymore after ExitBootServices.
> 
> Of all the issues listed, this one seems the most serious.
> 
> >         4) If the firmware uses the kvm-unit-test GHCB after
> >            ExitBootServices, it has the get the GHCB address from the
> >            GHCB MSR, requiring an identity mapping.
> >            Moreover it requires to keep the address of the GHCB in the
> >            MSR at all times where a #VC could happen. This could be a
> >            problem when we start to add SEV-ES specific tests to the
> >            unit-tests, explcitily testing the MSR protocol.
> 
> Ack. I'd think we could require tests to save/restore the GHCB MSR.
> 
> > It is easy to violate this implicit protocol and breaking kvm-unit-tests
> > just by a new version of OVMF being used. I think that is not a very
> > robust approach and a separate #VC handler in the unit-test framework
> > makes sense even now.
> 
> Thanks for the explanation! I hope we can keep the UEFI #VC handler
> working, because like I mentioned, I think this work can be used to
> test that code inside of UEFI. But I guess time will tell.
> 
> Of all the points listed above, I think point #3 is the most
> concerning. The others seem like they can be managed.

  5) Debug.  I don't want to have to reverse engineer assembly code to understand
     why a #VC handler isn't doing what I expect, or to a debug the exchanges
     between guest and host.


On Thu, Jan 20, 2022 at 4:52 AM Varad Gautam <varad.gautam@suse.com> wrote:
> If --amdsev-efi-vc is passed during ./configure, the tests will
> continue using the UEFI #VC handler.

Why bother?  I would prefer we ditch the UEFI #VC handler entirely and not give
users the option to using anything but the built-in handler.  What do we gain
other than complexity?
Joerg Roedel Feb. 4, 2022, 5:15 p.m. UTC | #5
Hi Marc,

On Fri, Feb 04, 2022 at 07:57:39AM -0800, Marc Orr wrote:
> Regarding code review and testing, I can help with the following:
> - Compare the patches being pulled into kvm-unit-tests to what's in
> the Linux kernel and add my Reviewed-by tags if I don't see any
> meaningful discrepancies.
> - Test the entire series on Google's setup, which doesn't use QEMU and
> add my Tested-by tag accordingly. My previous Tested-by tags were on
> individual patches. I have not yet tested the entire series.
> 
> Please let me know if this is useful. If not, I wouldn't spend the time :-).

I think it is definitly useful to run this in Googles environment too
to get it tested and possible bugs ruled out. That can only help the
upstream integration of these patches :)

Varad discussed an idea with me today where the core VC handling code
could be put into a library and used by the kernel and unit-tests and
possibly others as well. The has the benefit that the kvm-unit-tests
would also test the kernels VC handler paths. But it is probably
something for the future.

Regards,
Marc Orr Feb. 4, 2022, 8:09 p.m. UTC | #6
On Fri, Feb 4, 2022 at 8:30 AM Sean Christopherson <seanjc@google.com> wrote:
>
> On Fri, Feb 04, 2022, Marc Orr wrote:
> > On Fri, Feb 4, 2022 at 2:55 AM Joerg Roedel <jroedel@suse.de> wrote:
> > >         3) The firmware #VC handler might use state which is not
> > >            available anymore after ExitBootServices.
> >
> > Of all the issues listed, this one seems the most serious.
> >
> > >         4) If the firmware uses the kvm-unit-test GHCB after
> > >            ExitBootServices, it has the get the GHCB address from the
> > >            GHCB MSR, requiring an identity mapping.
> > >            Moreover it requires to keep the address of the GHCB in the
> > >            MSR at all times where a #VC could happen. This could be a
> > >            problem when we start to add SEV-ES specific tests to the
> > >            unit-tests, explcitily testing the MSR protocol.
> >
> > Ack. I'd think we could require tests to save/restore the GHCB MSR.
> >
> > > It is easy to violate this implicit protocol and breaking kvm-unit-tests
> > > just by a new version of OVMF being used. I think that is not a very
> > > robust approach and a separate #VC handler in the unit-test framework
> > > makes sense even now.
> >
> > Thanks for the explanation! I hope we can keep the UEFI #VC handler
> > working, because like I mentioned, I think this work can be used to
> > test that code inside of UEFI. But I guess time will tell.
> >
> > Of all the points listed above, I think point #3 is the most
> > concerning. The others seem like they can be managed.
>
>   5) Debug.  I don't want to have to reverse engineer assembly code to understand
>      why a #VC handler isn't doing what I expect, or to a debug the exchanges
>      between guest and host.

Ack. But this can also be viewed as a benefit. Because the bug is
probably something that should be followed up and fixed inside of
UEFI.

And that's my end goal. Can we reuse this work to test the #VC handler
in the UEFI?

This shouldn't be onerous. Because the #VC should follow the APM and
GHCB specs. And both UEFI and kvm-unit-tests #VC handlers should be
coded to those specs. If they're not, then one of them has a bug.

> On Thu, Jan 20, 2022 at 4:52 AM Varad Gautam <varad.gautam@suse.com> wrote:
> > If --amdsev-efi-vc is passed during ./configure, the tests will
> > continue using the UEFI #VC handler.
>
> Why bother?  I would prefer we ditch the UEFI #VC handler entirely and not give
> users the option to using anything but the built-in handler.  What do we gain
> other than complexity?

See above. If we keep the ability to run off the UEFI #VC handler then
we can get continuous testing running inside of Google to verify the
UEFI used to launch every SEV VM on Google cloud.
Marc Orr Feb. 4, 2022, 8:12 p.m. UTC | #7
On Fri, Feb 4, 2022 at 9:15 AM Joerg Roedel <jroedel@suse.de> wrote:
>
> Hi Marc,
>
> On Fri, Feb 04, 2022 at 07:57:39AM -0800, Marc Orr wrote:
> > Regarding code review and testing, I can help with the following:
> > - Compare the patches being pulled into kvm-unit-tests to what's in
> > the Linux kernel and add my Reviewed-by tags if I don't see any
> > meaningful discrepancies.
> > - Test the entire series on Google's setup, which doesn't use QEMU and
> > add my Tested-by tag accordingly. My previous Tested-by tags were on
> > individual patches. I have not yet tested the entire series.
> >
> > Please let me know if this is useful. If not, I wouldn't spend the time :-).
>
> I think it is definitly useful to run this in Googles environment too
> to get it tested and possible bugs ruled out. That can only help the
> upstream integration of these patches :)

SGTM. Thank you for the feedback.

> Varad discussed an idea with me today where the core VC handling code
> could be put into a library and used by the kernel and unit-tests and
> possibly others as well. The has the benefit that the kvm-unit-tests
> would also test the kernels VC handler paths. But it is probably
> something for the future.

Yes, I like this idea a lot. This is basically what our internal test
framework does. So if we can eventually convert this code to be a #VC
as a library, that can be used from kvm-unit-tests, Linux kernel,
UEFI, etc., that would be great.
Sean Christopherson Feb. 7, 2022, 9:11 p.m. UTC | #8
On Fri, Feb 04, 2022, Marc Orr wrote:
> On Fri, Feb 4, 2022 at 8:30 AM Sean Christopherson <seanjc@google.com> wrote:
> >
> > On Fri, Feb 04, 2022, Marc Orr wrote:
> > > On Fri, Feb 4, 2022 at 2:55 AM Joerg Roedel <jroedel@suse.de> wrote:
> > > >         3) The firmware #VC handler might use state which is not
> > > >            available anymore after ExitBootServices.
> > >
> > > Of all the issues listed, this one seems the most serious.
> > >
> > > >         4) If the firmware uses the kvm-unit-test GHCB after
> > > >            ExitBootServices, it has the get the GHCB address from the
> > > >            GHCB MSR, requiring an identity mapping.
> > > >            Moreover it requires to keep the address of the GHCB in the
> > > >            MSR at all times where a #VC could happen. This could be a
> > > >            problem when we start to add SEV-ES specific tests to the
> > > >            unit-tests, explcitily testing the MSR protocol.
> > >
> > > Ack. I'd think we could require tests to save/restore the GHCB MSR.
> > >
> > > > It is easy to violate this implicit protocol and breaking kvm-unit-tests
> > > > just by a new version of OVMF being used. I think that is not a very
> > > > robust approach and a separate #VC handler in the unit-test framework
> > > > makes sense even now.
> > >
> > > Thanks for the explanation! I hope we can keep the UEFI #VC handler
> > > working, because like I mentioned, I think this work can be used to
> > > test that code inside of UEFI. But I guess time will tell.
> > >
> > > Of all the points listed above, I think point #3 is the most
> > > concerning. The others seem like they can be managed.
> >
> >   5) Debug.  I don't want to have to reverse engineer assembly code to understand
> >      why a #VC handler isn't doing what I expect, or to a debug the exchanges
> >      between guest and host.
> 
> Ack. But this can also be viewed as a benefit. Because the bug is
> probably something that should be followed up and fixed inside of
> UEFI.

But how would we know it's a bug?  E.g. IMO, UEFI should be enlightened to _never_
take a #VC, at which point its #VC handle can be changed to panic and using such a
UEIF would cause KUT to fail.

> And that's my end goal. Can we reuse this work to test the #VC handler
> in the UEFI?
> 
> This shouldn't be onerous. Because the #VC should follow the APM and
> GHCB specs. And both UEFI and kvm-unit-tests #VC handlers should be
> coded to those specs. If they're not, then one of them has a bug.
> 
> > On Thu, Jan 20, 2022 at 4:52 AM Varad Gautam <varad.gautam@suse.com> wrote:
> > > If --amdsev-efi-vc is passed during ./configure, the tests will
> > > continue using the UEFI #VC handler.
> >
> > Why bother?  I would prefer we ditch the UEFI #VC handler entirely and not give
> > users the option to using anything but the built-in handler.  What do we gain
> > other than complexity?
> 
> See above. If we keep the ability to run off the UEFI #VC handler then
> we can get continuous testing running inside of Google to verify the
> UEFI used to launch every SEV VM on Google cloud.

I'm not super opposed to the idea, but I really do think that taking a #VC in
guest UEFI is a bug in and of itself.
Marc Orr Feb. 8, 2022, 1:58 a.m. UTC | #9
On Mon, Feb 7, 2022 at 1:11 PM Sean Christopherson <seanjc@google.com> wrote:
>
> On Fri, Feb 04, 2022, Marc Orr wrote:
> > On Fri, Feb 4, 2022 at 8:30 AM Sean Christopherson <seanjc@google.com> wrote:
> > >
> > > On Fri, Feb 04, 2022, Marc Orr wrote:
> > > > On Fri, Feb 4, 2022 at 2:55 AM Joerg Roedel <jroedel@suse.de> wrote:
> > > > >         3) The firmware #VC handler might use state which is not
> > > > >            available anymore after ExitBootServices.
> > > >
> > > > Of all the issues listed, this one seems the most serious.
> > > >
> > > > >         4) If the firmware uses the kvm-unit-test GHCB after
> > > > >            ExitBootServices, it has the get the GHCB address from the
> > > > >            GHCB MSR, requiring an identity mapping.
> > > > >            Moreover it requires to keep the address of the GHCB in the
> > > > >            MSR at all times where a #VC could happen. This could be a
> > > > >            problem when we start to add SEV-ES specific tests to the
> > > > >            unit-tests, explcitily testing the MSR protocol.
> > > >
> > > > Ack. I'd think we could require tests to save/restore the GHCB MSR.
> > > >
> > > > > It is easy to violate this implicit protocol and breaking kvm-unit-tests
> > > > > just by a new version of OVMF being used. I think that is not a very
> > > > > robust approach and a separate #VC handler in the unit-test framework
> > > > > makes sense even now.
> > > >
> > > > Thanks for the explanation! I hope we can keep the UEFI #VC handler
> > > > working, because like I mentioned, I think this work can be used to
> > > > test that code inside of UEFI. But I guess time will tell.
> > > >
> > > > Of all the points listed above, I think point #3 is the most
> > > > concerning. The others seem like they can be managed.
> > >
> > >   5) Debug.  I don't want to have to reverse engineer assembly code to understand
> > >      why a #VC handler isn't doing what I expect, or to a debug the exchanges
> > >      between guest and host.
> >
> > Ack. But this can also be viewed as a benefit. Because the bug is
> > probably something that should be followed up and fixed inside of
> > UEFI.
>
> But how would we know it's a bug?  E.g. IMO, UEFI should be enlightened to _never_
> take a #VC, at which point its #VC handle can be changed to panic and using such a
> UEIF would cause KUT to fail.

Yeah. If we end up with enlightened UEFIs that skip handling some/all
#VC exceptions, then using the UEFI #VC handler from kvm-unit-tests
doesn't make any sense :-).

> > And that's my end goal. Can we reuse this work to test the #VC handler
> > in the UEFI?
> >
> > This shouldn't be onerous. Because the #VC should follow the APM and
> > GHCB specs. And both UEFI and kvm-unit-tests #VC handlers should be
> > coded to those specs. If they're not, then one of them has a bug.
> >
> > > On Thu, Jan 20, 2022 at 4:52 AM Varad Gautam <varad.gautam@suse.com> wrote:
> > > > If --amdsev-efi-vc is passed during ./configure, the tests will
> > > > continue using the UEFI #VC handler.
> > >
> > > Why bother?  I would prefer we ditch the UEFI #VC handler entirely and not give
> > > users the option to using anything but the built-in handler.  What do we gain
> > > other than complexity?
> >
> > See above. If we keep the ability to run off the UEFI #VC handler then
> > we can get continuous testing running inside of Google to verify the
> > UEFI used to launch every SEV VM on Google cloud.
>
> I'm not super opposed to the idea, but I really do think that taking a #VC in
> guest UEFI is a bug in and of itself.

Understood.
diff mbox series

Patch

diff --git a/Makefile b/Makefile
index 4f4ad23..94a0162 100644
--- a/Makefile
+++ b/Makefile
@@ -46,6 +46,9 @@  else
 $(error Cannot build $(ARCH_NAME) tests as EFI apps)
 endif
 EFI_CFLAGS := -DTARGET_EFI
+ifeq ($(AMDSEV_EFI_VC),y)
+EFI_CFLAGS += -DAMDSEV_EFI_VC
+endif
 # The following CFLAGS and LDFLAGS come from:
 #   - GNU-EFI/Makefile.defaults
 #   - GNU-EFI/apps/Makefile
diff --git a/configure b/configure
index 41372ef..c687d9f 100755
--- a/configure
+++ b/configure
@@ -29,6 +29,7 @@  host_key_document=
 page_size=
 earlycon=
 target_efi=
+amdsev_efi_vc=
 
 usage() {
     cat <<-EOF
@@ -71,6 +72,8 @@  usage() {
 	                           Specify a PL011 compatible UART at address ADDR. Supported
 	                           register stride is 32 bit only.
 	    --target-efi           Boot and run from UEFI
+	    --amdsev-efi-vc        Use UEFI-provided #VC handlers on AMD SEV/ES. Requires
+	                           --target-efi.
 EOF
     exit 1
 }
@@ -138,6 +141,9 @@  while [[ "$1" = -* ]]; do
 	--target-efi)
 	    target_efi=y
 	    ;;
+	--amdsev-efi-vc)
+	    amdsev_efi_vc=y
+	    ;;
 	--help)
 	    usage
 	    ;;
@@ -197,8 +203,17 @@  elif [ "$processor" = "arm" ]; then
     processor="cortex-a15"
 fi
 
+if [ "$amdsev_efi_vc" ] && [ "$arch" != "x86_64" ]; then
+    echo "--amdsev-efi-vc requires arch x86_64."
+    usage
+fi
+
 if [ "$arch" = "i386" ] || [ "$arch" = "x86_64" ]; then
     testdir=x86
+    if [ "$amdsev_efi_vc" ] && [ -z "$target_efi" ]; then
+        echo "--amdsev-efi-vc requires --target-efi."
+        usage
+    fi
 elif [ "$arch" = "arm" ] || [ "$arch" = "arm64" ]; then
     testdir=arm
     if [ "$target" = "qemu" ]; then
@@ -356,6 +371,7 @@  WA_DIVIDE=$wa_divide
 GENPROTIMG=${GENPROTIMG-genprotimg}
 HOST_KEY_DOCUMENT=$host_key_document
 TARGET_EFI=$target_efi
+AMDSEV_EFI_VC=$amdsev_efi_vc
 EOF
 if [ "$arch" = "arm" ] || [ "$arch" = "arm64" ]; then
     echo "TARGET=$target" >> config.mak
diff --git a/lib/x86/amd_sev.c b/lib/x86/amd_sev.c
index 6672214..bde126b 100644
--- a/lib/x86/amd_sev.c
+++ b/lib/x86/amd_sev.c
@@ -14,6 +14,7 @@ 
 #include "x86/vm.h"
 
 static unsigned short amd_sev_c_bit_pos;
+phys_addr_t ghcb_addr;
 
 bool amd_sev_enabled(void)
 {
@@ -126,7 +127,7 @@  void setup_ghcb_pte(pgd_t *page_table)
 	 * function searches GHCB's L1 pte, creates corresponding L1 ptes if not
 	 * found, and unsets the c-bit of GHCB's L1 pte.
 	 */
-	phys_addr_t ghcb_addr, ghcb_base_addr;
+	phys_addr_t ghcb_base_addr;
 	pteval_t *pte;
 
 	/* Read the current GHCB page addr */
diff --git a/lib/x86/amd_sev.h b/lib/x86/amd_sev.h
index 6a10f84..afbacf3 100644
--- a/lib/x86/amd_sev.h
+++ b/lib/x86/amd_sev.h
@@ -54,6 +54,7 @@  efi_status_t setup_amd_sev(void);
 bool amd_sev_es_enabled(void);
 efi_status_t setup_amd_sev_es(void);
 void setup_ghcb_pte(pgd_t *page_table);
+void handle_sev_es_vc(struct ex_regs *regs);
 
 unsigned long long get_amd_sev_c_bit_mask(void);
 unsigned long long get_amd_sev_addr_upperbound(void);
diff --git a/lib/x86/amd_sev_vc.c b/lib/x86/amd_sev_vc.c
new file mode 100644
index 0000000..8226121
--- /dev/null
+++ b/lib/x86/amd_sev_vc.c
@@ -0,0 +1,14 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#include "amd_sev.h"
+
+extern phys_addr_t ghcb_addr;
+
+void handle_sev_es_vc(struct ex_regs *regs)
+{
+	struct ghcb *ghcb = (struct ghcb *) ghcb_addr;
+	if (!ghcb) {
+		/* TODO: kill guest */
+		return;
+	}
+}
diff --git a/lib/x86/desc.c b/lib/x86/desc.c
index 16b7256..73aa866 100644
--- a/lib/x86/desc.c
+++ b/lib/x86/desc.c
@@ -3,6 +3,9 @@ 
 #include "processor.h"
 #include <setjmp.h>
 #include "apic-defs.h"
+#ifdef TARGET_EFI
+#include "amd_sev.h"
+#endif
 
 /* Boot-related data structures */
 
@@ -228,6 +231,9 @@  EX_E(ac, 17);
 EX(mc, 18);
 EX(xm, 19);
 EX_E(cp, 21);
+#ifdef TARGET_EFI
+EX_E(vc, 29);
+#endif
 
 asm (".pushsection .text \n\t"
      "__handle_exception: \n\t"
@@ -293,6 +299,15 @@  void setup_idt(void)
     handle_exception(13, check_exception_table);
 }
 
+void setup_amd_sev_es_vc(void)
+{
+	if (!amd_sev_es_enabled())
+		return;
+
+	set_idt_entry(29, &vc_fault, 0);
+	handle_exception(29, handle_sev_es_vc);
+}
+
 unsigned exception_vector(void)
 {
     unsigned char vector;
diff --git a/lib/x86/desc.h b/lib/x86/desc.h
index b65539e..4fcbf9f 100644
--- a/lib/x86/desc.h
+++ b/lib/x86/desc.h
@@ -220,6 +220,7 @@  void set_intr_alt_stack(int e, void *fn);
 void print_current_tss_info(void);
 handler handle_exception(u8 v, handler fn);
 void unhandled_exception(struct ex_regs *regs, bool cpu);
+void setup_amd_sev_es_vc(void);
 
 bool test_for_exception(unsigned int ex, void (*trigger_func)(void *data),
 			void *data);
diff --git a/lib/x86/setup.c b/lib/x86/setup.c
index bbd3468..6013602 100644
--- a/lib/x86/setup.c
+++ b/lib/x86/setup.c
@@ -327,6 +327,16 @@  efi_status_t setup_efi(efi_bootinfo_t *efi_bootinfo)
 	smp_init();
 	setup_page_table();
 
+#ifndef AMDSEV_EFI_VC
+	if (amd_sev_es_enabled()) {
+		/*
+		 * Switch away from the UEFI-installed #VC handler.
+		 * GHCB has already been mapped at this point.
+		 */
+		setup_amd_sev_es_vc();
+	}
+#endif /* AMDSEV_EFI_VC */
+
 	return EFI_SUCCESS;
 }
 
diff --git a/x86/Makefile.common b/x86/Makefile.common
index 984444e..65d16e7 100644
--- a/x86/Makefile.common
+++ b/x86/Makefile.common
@@ -24,6 +24,7 @@  cflatobjs += lib/x86/fault_test.o
 cflatobjs += lib/x86/delay.o
 ifeq ($(TARGET_EFI),y)
 cflatobjs += lib/x86/amd_sev.o
+cflatobjs += lib/x86/amd_sev_vc.o
 cflatobjs += lib/efi.o
 cflatobjs += x86/efi/reloc_x86_64.o
 endif