lkml.org 
[lkml]   [2022]   [Aug]   [25]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v4 4/4] x86/sev: Add SNP-specific unaccepted memory support
    Date
    Add SNP-specific hooks to the unaccepted memory support in the boot
    path (__accept_memory()) and the core kernel (accept_memory()) in order
    to support booting SNP guests when unaccepted memory is present. Without
    this support, SNP guests will fail to boot and/or panic() when unaccepted
    memory is present in the EFI memory map.

    The process of accepting memory under SNP involves invoking the hypervisor
    to perform a page state change for the page to private memory and then
    issuing a PVALIDATE instruction to accept the page.

    Since the boot path and the core kernel paths perform similar operations,
    move the pvalidate_pages() and vmgexit_psc() functions into sev-shared.c
    to avoid code duplication.

    Create the new header file arch/x86/boot/compressed/sev.h because adding
    the function declaration to any of the existing SEV related header files
    pulls in too many other header files, causing the build to fail.

    Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
    ---
    arch/x86/Kconfig | 1 +
    arch/x86/boot/compressed/mem.c | 3 +
    arch/x86/boot/compressed/sev.c | 54 ++++++++++++++-
    arch/x86/boot/compressed/sev.h | 23 +++++++
    arch/x86/include/asm/sev.h | 3 +
    arch/x86/kernel/sev-shared.c | 104 +++++++++++++++++++++++++++++
    arch/x86/kernel/sev.c | 112 ++++----------------------------
    arch/x86/mm/unaccepted_memory.c | 4 ++
    8 files changed, 205 insertions(+), 99 deletions(-)
    create mode 100644 arch/x86/boot/compressed/sev.h

    diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
    index 34146ecc5bdd..0ad53c3533c2 100644
    --- a/arch/x86/Kconfig
    +++ b/arch/x86/Kconfig
    @@ -1553,6 +1553,7 @@ config AMD_MEM_ENCRYPT
    select INSTRUCTION_DECODER
    select ARCH_HAS_CC_PLATFORM
    select X86_MEM_ENCRYPT
    + select UNACCEPTED_MEMORY
    help
    Say yes to enable support for the encryption of system memory.
    This requires an AMD processor that supports Secure Memory
    diff --git a/arch/x86/boot/compressed/mem.c b/arch/x86/boot/compressed/mem.c
    index 48e36e640da1..3e19dc0da0d7 100644
    --- a/arch/x86/boot/compressed/mem.c
    +++ b/arch/x86/boot/compressed/mem.c
    @@ -6,6 +6,7 @@
    #include "find.h"
    #include "math.h"
    #include "tdx.h"
    +#include "sev.h"
    #include <asm/shared/tdx.h>

    #define PMD_SHIFT 21
    @@ -39,6 +40,8 @@ static inline void __accept_memory(phys_addr_t start, phys_addr_t end)
    /* Platform-specific memory-acceptance call goes here */
    if (is_tdx_guest())
    tdx_accept_memory(start, end);
    + else if (sev_snp_enabled())
    + snp_accept_memory(start, end);
    else
    error("Cannot accept memory: unknown platform\n");
    }
    diff --git a/arch/x86/boot/compressed/sev.c b/arch/x86/boot/compressed/sev.c
    index 730c4677e9db..22da65c96b47 100644
    --- a/arch/x86/boot/compressed/sev.c
    +++ b/arch/x86/boot/compressed/sev.c
    @@ -115,7 +115,7 @@ static enum es_result vc_read_mem(struct es_em_ctxt *ctxt,
    /* Include code for early handlers */
    #include "../../kernel/sev-shared.c"

    -static inline bool sev_snp_enabled(void)
    +bool sev_snp_enabled(void)
    {
    return sev_status & MSR_AMD64_SEV_SNP_ENABLED;
    }
    @@ -181,6 +181,58 @@ static bool early_setup_ghcb(void)
    return true;
    }

    +static phys_addr_t __snp_accept_memory(struct snp_psc_desc *desc,
    + phys_addr_t pa, phys_addr_t pa_end)
    +{
    + struct psc_hdr *hdr;
    + struct psc_entry *e;
    + unsigned int i;
    +
    + hdr = &desc->hdr;
    + memset(hdr, 0, sizeof(*hdr));
    +
    + e = desc->entries;
    +
    + i = 0;
    + while (pa < pa_end && i < VMGEXIT_PSC_MAX_ENTRY) {
    + hdr->end_entry = i;
    +
    + e->gfn = pa >> PAGE_SHIFT;
    + e->operation = SNP_PAGE_STATE_PRIVATE;
    + if (IS_ALIGNED(pa, PMD_PAGE_SIZE) && (pa_end - pa) >= PMD_PAGE_SIZE) {
    + e->pagesize = RMP_PG_SIZE_2M;
    + pa += PMD_PAGE_SIZE;
    + } else {
    + e->pagesize = RMP_PG_SIZE_4K;
    + pa += PAGE_SIZE;
    + }
    +
    + e++;
    + i++;
    + }
    +
    + if (vmgexit_psc(boot_ghcb, desc))
    + sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PSC);
    +
    + pvalidate_pages(desc);
    +
    + return pa;
    +}
    +
    +void snp_accept_memory(phys_addr_t start, phys_addr_t end)
    +{
    + struct snp_psc_desc desc = {};
    + unsigned int i;
    + phys_addr_t pa;
    +
    + if (!boot_ghcb && !early_setup_ghcb())
    + sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PSC);
    +
    + pa = start;
    + while (pa < end)
    + pa = __snp_accept_memory(&desc, pa, end);
    +}
    +
    void sev_es_shutdown_ghcb(void)
    {
    if (!boot_ghcb)
    diff --git a/arch/x86/boot/compressed/sev.h b/arch/x86/boot/compressed/sev.h
    new file mode 100644
    index 000000000000..fc725a981b09
    --- /dev/null
    +++ b/arch/x86/boot/compressed/sev.h
    @@ -0,0 +1,23 @@
    +/* SPDX-License-Identifier: GPL-2.0 */
    +/*
    + * AMD SEV header for early boot related functions.
    + *
    + * Author: Tom Lendacky <thomas.lendacky@amd.com>
    + */
    +
    +#ifndef BOOT_COMPRESSED_SEV_H
    +#define BOOT_COMPRESSED_SEV_H
    +
    +#ifdef CONFIG_AMD_MEM_ENCRYPT
    +
    +bool sev_snp_enabled(void);
    +void snp_accept_memory(phys_addr_t start, phys_addr_t end);
    +
    +#else
    +
    +static inline bool sev_snp_enabled(void) { return false; }
    +static inline void snp_accept_memory(phys_addr_t start, phys_addr_t end) { }
    +
    +#endif
    +
    +#endif
    diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
    index 0007ab04ac5f..9297aab0c79e 100644
    --- a/arch/x86/include/asm/sev.h
    +++ b/arch/x86/include/asm/sev.h
    @@ -206,6 +206,7 @@ void snp_set_wakeup_secondary_cpu(void);
    bool snp_init(struct boot_params *bp);
    void snp_abort(void);
    int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, unsigned long *fw_err);
    +void snp_accept_memory(phys_addr_t start, phys_addr_t end);
    #else
    static inline void sev_es_ist_enter(struct pt_regs *regs) { }
    static inline void sev_es_ist_exit(void) { }
    @@ -230,6 +231,8 @@ static inline int snp_issue_guest_request(u64 exit_code, struct snp_req_data *in
    {
    return -ENOTTY;
    }
    +
    +static inline void snp_accept_memory(phys_addr_t start, phys_addr_t end) { }
    #endif

    #endif
    diff --git a/arch/x86/kernel/sev-shared.c b/arch/x86/kernel/sev-shared.c
    index b478edf43bec..7ac7857da2b8 100644
    --- a/arch/x86/kernel/sev-shared.c
    +++ b/arch/x86/kernel/sev-shared.c
    @@ -12,6 +12,9 @@
    #ifndef __BOOT_COMPRESSED
    #define error(v) pr_err(v)
    #define has_cpuflag(f) boot_cpu_has(f)
    +#else
    +#undef WARN
    +#define WARN(condition...)
    #endif

    /* I/O parameters for CPUID-related helpers */
    @@ -998,3 +1001,104 @@ static void __init setup_cpuid_table(const struct cc_blob_sev_info *cc_info)
    cpuid_ext_range_max = fn->eax;
    }
    }
    +
    +static void pvalidate_pages(struct snp_psc_desc *desc)
    +{
    + struct psc_entry *e;
    + unsigned long vaddr;
    + unsigned int size;
    + unsigned int i;
    + bool validate;
    + int rc;
    +
    + for (i = 0; i <= desc->hdr.end_entry; i++) {
    + e = &desc->entries[i];
    +
    + vaddr = (unsigned long)pfn_to_kaddr(e->gfn);
    + size = e->pagesize ? RMP_PG_SIZE_2M : RMP_PG_SIZE_4K;
    + validate = (e->operation == SNP_PAGE_STATE_PRIVATE) ? true : false;
    +
    + rc = pvalidate(vaddr, size, validate);
    + if (rc == PVALIDATE_FAIL_SIZEMISMATCH && size == RMP_PG_SIZE_2M) {
    + unsigned long vaddr_end = vaddr + PMD_PAGE_SIZE;
    +
    + for (; vaddr < vaddr_end; vaddr += PAGE_SIZE) {
    + rc = pvalidate(vaddr, RMP_PG_SIZE_4K, validate);
    + if (rc)
    + break;
    + }
    + }
    +
    + if (rc) {
    + WARN(1, "Failed to validate address 0x%lx ret %d", vaddr, rc);
    + sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PVALIDATE);
    + }
    + }
    +}
    +
    +static int vmgexit_psc(struct ghcb *ghcb, struct snp_psc_desc *desc)
    +{
    + int cur_entry, end_entry, ret = 0;
    + struct snp_psc_desc *data;
    + struct es_em_ctxt ctxt;
    +
    + vc_ghcb_invalidate(ghcb);
    +
    + /* Copy the input desc into GHCB shared buffer */
    + data = (struct snp_psc_desc *)ghcb->shared_buffer;
    + memcpy(ghcb->shared_buffer, desc, min_t(int, GHCB_SHARED_BUF_SIZE, sizeof(*desc)));
    +
    + /*
    + * As per the GHCB specification, the hypervisor can resume the guest
    + * before processing all the entries. Check whether all the entries
    + * are processed. If not, then keep retrying. Note, the hypervisor
    + * will update the data memory directly to indicate the status, so
    + * reference the data->hdr everywhere.
    + *
    + * The strategy here is to wait for the hypervisor to change the page
    + * state in the RMP table before guest accesses the memory pages. If the
    + * page state change was not successful, then later memory access will
    + * result in a crash.
    + */
    + cur_entry = data->hdr.cur_entry;
    + end_entry = data->hdr.end_entry;
    +
    + while (data->hdr.cur_entry <= data->hdr.end_entry) {
    + ghcb_set_sw_scratch(ghcb, (u64)__pa(data));
    +
    + /* This will advance the shared buffer data points to. */
    + ret = sev_es_ghcb_hv_call(ghcb, true, &ctxt, SVM_VMGEXIT_PSC, 0, 0);
    +
    + /*
    + * Page State Change VMGEXIT can pass error code through
    + * exit_info_2.
    + */
    + if (ret || ghcb->save.sw_exit_info_2) {
    + WARN(1, "SNP: PSC failed ret=%d exit_info_2=%llx\n",
    + ret, ghcb->save.sw_exit_info_2);
    + ret = 1;
    + goto out;
    + }
    +
    + /* Verify that reserved bit is not set */
    + if (data->hdr.reserved) {
    + WARN(1, "Reserved bit is set in the PSC header\n");
    + ret = 1;
    + goto out;
    + }
    +
    + /*
    + * Sanity check that entry processing is not going backwards.
    + * This will happen only if hypervisor is tricking us.
    + */
    + if (data->hdr.end_entry > end_entry || cur_entry > data->hdr.cur_entry) {
    + WARN(1, "SNP: PSC processing going backward, end_entry %d (got %d) cur_entry %d (got %d)\n",
    + end_entry, data->hdr.end_entry, cur_entry, data->hdr.cur_entry);
    + ret = 1;
    + goto out;
    + }
    + }
    +
    +out:
    + return ret;
    +}
    diff --git a/arch/x86/kernel/sev.c b/arch/x86/kernel/sev.c
    index a744f7f2e72b..abdf431622ea 100644
    --- a/arch/x86/kernel/sev.c
    +++ b/arch/x86/kernel/sev.c
    @@ -655,38 +655,6 @@ static u64 __init get_jump_table_addr(void)
    return ret;
    }

    -static void pvalidate_pages(struct snp_psc_desc *desc)
    -{
    - struct psc_entry *e;
    - unsigned long vaddr;
    - unsigned int size;
    - unsigned int i;
    - bool validate;
    - int rc;
    -
    - for (i = 0; i <= desc->hdr.end_entry; i++) {
    - e = &desc->entries[i];
    -
    - vaddr = (unsigned long)pfn_to_kaddr(e->gfn);
    - size = e->pagesize ? RMP_PG_SIZE_2M : RMP_PG_SIZE_4K;
    - validate = (e->operation == SNP_PAGE_STATE_PRIVATE) ? true : false;
    -
    - rc = pvalidate(vaddr, size, validate);
    - if (rc == PVALIDATE_FAIL_SIZEMISMATCH && size == RMP_PG_SIZE_2M) {
    - unsigned long vaddr_end = vaddr + PMD_PAGE_SIZE;
    -
    - for (; vaddr < vaddr_end; vaddr += PAGE_SIZE) {
    - rc = pvalidate(vaddr, RMP_PG_SIZE_4K, validate);
    - if (rc)
    - break;
    - }
    - }
    -
    - if (WARN(rc, "Failed to validate address 0x%lx ret %d", vaddr, rc))
    - sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PVALIDATE);
    - }
    -}
    -
    static void early_set_pages_state(unsigned long vaddr, unsigned long paddr,
    unsigned int npages, enum psc_op op)
    {
    @@ -782,72 +750,6 @@ void __init snp_prep_memory(unsigned long paddr, unsigned int sz, enum psc_op op
    WARN(1, "invalid memory op %d\n", op);
    }

    -static int vmgexit_psc(struct ghcb *ghcb, struct snp_psc_desc *desc)
    -{
    - int cur_entry, end_entry, ret = 0;
    - struct snp_psc_desc *data;
    - struct es_em_ctxt ctxt;
    -
    - vc_ghcb_invalidate(ghcb);
    -
    - /* Copy the input desc into GHCB shared buffer */
    - data = (struct snp_psc_desc *)ghcb->shared_buffer;
    - memcpy(ghcb->shared_buffer, desc, min_t(int, GHCB_SHARED_BUF_SIZE, sizeof(*desc)));
    -
    - /*
    - * As per the GHCB specification, the hypervisor can resume the guest
    - * before processing all the entries. Check whether all the entries
    - * are processed. If not, then keep retrying. Note, the hypervisor
    - * will update the data memory directly to indicate the status, so
    - * reference the data->hdr everywhere.
    - *
    - * The strategy here is to wait for the hypervisor to change the page
    - * state in the RMP table before guest accesses the memory pages. If the
    - * page state change was not successful, then later memory access will
    - * result in a crash.
    - */
    - cur_entry = data->hdr.cur_entry;
    - end_entry = data->hdr.end_entry;
    -
    - while (data->hdr.cur_entry <= data->hdr.end_entry) {
    - ghcb_set_sw_scratch(ghcb, (u64)__pa(data));
    -
    - /* This will advance the shared buffer data points to. */
    - ret = sev_es_ghcb_hv_call(ghcb, true, &ctxt, SVM_VMGEXIT_PSC, 0, 0);
    -
    - /*
    - * Page State Change VMGEXIT can pass error code through
    - * exit_info_2.
    - */
    - if (WARN(ret || ghcb->save.sw_exit_info_2,
    - "SNP: PSC failed ret=%d exit_info_2=%llx\n",
    - ret, ghcb->save.sw_exit_info_2)) {
    - ret = 1;
    - goto out;
    - }
    -
    - /* Verify that reserved bit is not set */
    - if (WARN(data->hdr.reserved, "Reserved bit is set in the PSC header\n")) {
    - ret = 1;
    - goto out;
    - }
    -
    - /*
    - * Sanity check that entry processing is not going backwards.
    - * This will happen only if hypervisor is tricking us.
    - */
    - if (WARN(data->hdr.end_entry > end_entry || cur_entry > data->hdr.cur_entry,
    -"SNP: PSC processing going backward, end_entry %d (got %d) cur_entry %d (got %d)\n",
    - end_entry, data->hdr.end_entry, cur_entry, data->hdr.cur_entry)) {
    - ret = 1;
    - goto out;
    - }
    - }
    -
    -out:
    - return ret;
    -}
    -
    static unsigned long __set_pages_state(struct snp_psc_desc *data, unsigned long vaddr,
    unsigned long vaddr_end, int op)
    {
    @@ -952,6 +854,20 @@ void snp_set_memory_private(unsigned long vaddr, unsigned int npages)
    set_pages_state(vaddr, npages, SNP_PAGE_STATE_PRIVATE);
    }

    +void snp_accept_memory(phys_addr_t start, phys_addr_t end)
    +{
    + unsigned long vaddr;
    + unsigned int npages;
    +
    + if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP))
    + return;
    +
    + vaddr = (unsigned long)__va(start);
    + npages = (end - start) >> PAGE_SHIFT;
    +
    + set_pages_state(vaddr, npages, SNP_PAGE_STATE_PRIVATE);
    +}
    +
    static int snp_set_vmsa(void *va, bool vmsa)
    {
    u64 attrs;
    diff --git a/arch/x86/mm/unaccepted_memory.c b/arch/x86/mm/unaccepted_memory.c
    index 9ec2304272dc..b86ad6a8ddf5 100644
    --- a/arch/x86/mm/unaccepted_memory.c
    +++ b/arch/x86/mm/unaccepted_memory.c
    @@ -9,6 +9,7 @@
    #include <asm/setup.h>
    #include <asm/shared/tdx.h>
    #include <asm/unaccepted_memory.h>
    +#include <asm/sev.h>

    /* Protects unaccepted memory bitmap */
    static DEFINE_SPINLOCK(unaccepted_memory_lock);
    @@ -66,6 +67,9 @@ void accept_memory(phys_addr_t start, phys_addr_t end)
    if (cpu_feature_enabled(X86_FEATURE_TDX_GUEST)) {
    tdx_accept_memory(range_start * PMD_SIZE,
    range_end * PMD_SIZE);
    + } else if (cc_platform_has(CC_ATTR_GUEST_SEV_SNP)) {
    + snp_accept_memory(range_start * PMD_SIZE,
    + range_end * PMD_SIZE);
    } else {
    panic("Cannot accept memory: unknown platform\n");
    }
    --
    2.37.2
    \
     
     \ /
      Last update: 2022-08-25 16:26    [W:2.918 / U:0.048 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site