Re: [PATCH v7 13/45] x86/sev: Check the vmpl level

Borislav Petkov
2021-12-06T18:26:00Z

On Wed, Nov 10, 2021 at 04:06:59PM -0600, Brijesh Singh wrote:
> Virtual Machine Privilege Level (VMPL) is an optional feature in the
> SEV-SNP architecture, which allows a guest VM to divide its address space
> into four levels. The level can be used to provide the hardware isolated
> abstraction layers with a VM.

That sentence needs improving.

> The VMPL0 is the highest privilege, and
> VMPL3 is the least privilege. Certain operations must be done by the VMPL0
> software, such as:
>
> * Validate or invalidate memory range (PVALIDATE instruction)
> * Allocate VMSA page (RMPADJUST instruction when VMSA=1)
>
> The initial SEV-SNP support assumes that the guest kernel is running on

assumes? I think it is "requires".

> VMPL0. Let's add a check to make sure that kernel is running at VMPL0

s/Let's //

> before continuing the boot. There is no easy method to query the current
> VMPL level, so use the RMPADJUST instruction to determine whether its

"... whether the guest is running at VMPL0."

> booted at the VMPL0.
>
> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
> ---
> arch/x86/boot/compressed/sev.c | 34 ++++++++++++++++++++++++++++---
> arch/x86/include/asm/sev-common.h | 1 +
> arch/x86/include/asm/sev.h | 16 +++++++++++++++
> 3 files changed, 48 insertions(+), 3 deletions(-)
>
> diff --git a/arch/x86/boot/compressed/sev.c b/arch/x86/boot/compressed/sev.c
> index e525fa74a551..21feb7f4f76f 100644
> --- a/arch/x86/boot/compressed/sev.c
> +++ b/arch/x86/boot/compressed/sev.c
> @@ -124,6 +124,29 @@ static inline bool sev_snp_enabled(void)
> return sev_status & MSR_AMD64_SEV_SNP_ENABLED;
> }
>
> +static bool is_vmpl0(void)
> +{
> + u64 attrs;
> + int err;
> +
> + /*
> + * There is no straightforward way to query the current VMPL level. The
> + * simplest method is to use the RMPADJUST instruction to change a page
> + * permission to a VMPL level-1, and if the guest kernel is launched at
> + * a level <= 1, then RMPADJUST instruction will return an error.
> + */

So I was wondering what this is changing because if the change you do is
relevant, you'd have to undo it.

But looking at RMPADJUST, TARGET_PERM_MASK is 0 for target VMPL1 so
you're basically clearing all permissions for boot_ghcb_page on VMPL1.
Which is fine currently as we do only VMPL0 but pls write that out
explicitly what you're doing here and why it is ok to use RMPADJUST
without having to restore any changes it has done to the RMP table.

> + attrs = 1;
> +
> + /*
> + * Any page-aligned virtual address is sufficient to test the VMPL level.
> + * The boot_ghcb_page is page aligned memory, so lets use for the test.
> + */
> + if (rmpadjust((unsigned long)&boot_ghcb_page, RMP_PG_SIZE_4K, attrs))
> + return false;
> +
> + return true;
> +}
> +
> static bool do_early_sev_setup(void)
> {
> if (!sev_es_negotiate_protocol())
> @@ -132,10 +155,15 @@ static bool do_early_sev_setup(void)
> /*
> * SNP is supported in v2 of the GHCB spec which mandates support for HV
> * features. If SEV-SNP is enabled, then check if the hypervisor supports
> - * the SEV-SNP features.
> + * the SEV-SNP features and is launched at VMPL-0 level.

"VMPL0" - no hyphen - like in the APM. Below too.

> */
> - if (sev_snp_enabled() && !(sev_hv_features & GHCB_HV_FT_SNP))
> - sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
> + if (sev_snp_enabled()) {
> + if (!(sev_hv_features & GHCB_HV_FT_SNP))
> + sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
> +
> + if (!is_vmpl0())
> + sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_NOT_VMPL0);
> + }
>
> if (set_page_decrypted((unsigned long)&boot_ghcb_page))
> return false;

--
Regards/Gruss,
Boris.