lkml.org 
[lkml]   [2021]   [Mar]   [24]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[RFC Part2 PATCH 09/30] crypto: ccp: Add support to initialize the AMD-SP for SEV-SNP
    Date
    Before SNP VMs can be launched, the platform must be appropriately
    configured and initialized. Platform initialization is accomplished via
    the SNP_INIT command.

    Cc: Thomas Gleixner <tglx@linutronix.de>
    Cc: Ingo Molnar <mingo@redhat.com>
    Cc: Borislav Petkov <bp@alien8.de>
    Cc: Joerg Roedel <jroedel@suse.de>
    Cc: "H. Peter Anvin" <hpa@zytor.com>
    Cc: Tony Luck <tony.luck@intel.com>
    Cc: Dave Hansen <dave.hansen@intel.com>
    Cc: "Peter Zijlstra (Intel)" <peterz@infradead.org>
    Cc: Paolo Bonzini <pbonzini@redhat.com>
    Cc: Tom Lendacky <thomas.lendacky@amd.com>
    Cc: David Rientjes <rientjes@google.com>
    Cc: Sean Christopherson <seanjc@google.com>
    Cc: x86@kernel.org
    Cc: kvm@vger.kernel.org
    Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
    ---
    drivers/crypto/ccp/sev-dev.c | 164 ++++++++++++++++++++++++++++++++++-
    drivers/crypto/ccp/sev-dev.h | 2 +
    include/linux/psp-sev.h | 16 ++++
    3 files changed, 179 insertions(+), 3 deletions(-)

    diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c
    index 8a9fd843ad9e..c983a8b040c3 100644
    --- a/drivers/crypto/ccp/sev-dev.c
    +++ b/drivers/crypto/ccp/sev-dev.c
    @@ -21,8 +21,10 @@
    #include <linux/ccp.h>
    #include <linux/firmware.h>
    #include <linux/gfp.h>
    +#include <linux/mem_encrypt.h>

    #include <asm/smp.h>
    +#include <asm/sev-snp.h>

    #include "psp-dev.h"
    #include "sev-dev.h"
    @@ -30,6 +32,7 @@
    #define DEVICE_NAME "sev"
    #define SEV_FW_FILE "amd/sev.fw"
    #define SEV_FW_NAME_SIZE 64
    +#define __sme_page_pa(x) __sme_set(page_to_pfn(x) << PAGE_SHIFT)

    static DEFINE_MUTEX(sev_cmd_mutex);
    static struct sev_misc_dev *misc_dev;
    @@ -574,6 +577,93 @@ static int sev_update_firmware(struct device *dev)
    return ret;
    }

    +static void snp_set_hsave_pa(void *arg)
    +{
    + wrmsrl(MSR_VM_HSAVE_PA, 0);
    +}
    +
    +static int __sev_snp_init_locked(int *error)
    +{
    + struct psp_device *psp = psp_master;
    + struct sev_device *sev;
    + int rc = 0;
    +
    + if (!psp || !psp->sev_data)
    + return -ENODEV;
    +
    + sev = psp->sev_data;
    +
    + if (sev->snp_inited && sev->state >= SEV_STATE_INIT)
    + return 0;
    +
    + if (!snp_key_active()) {
    + dev_notice(sev->dev, "SNP is not enabled\n");
    + return -ENODEV;
    + }
    +
    + /* SNP_INIT requires the MSR_VM_HSAVE_PA must be set to 0h across all cores. */
    + on_each_cpu(snp_set_hsave_pa, NULL, 1);
    +
    + /* Prepare for first SEV guest launch after INIT */
    + wbinvd_on_all_cpus();
    +
    + /* Issue the SNP_INIT firmware command. */
    + rc = __sev_do_cmd_locked(SEV_CMD_SNP_INIT, NULL, error);
    + if (rc)
    + return rc;
    +
    + sev->snp_inited = true;
    + sev->state = SEV_STATE_INIT;
    + dev_dbg(sev->dev, "SEV-SNP firmware initialized\n");
    +
    + return rc;
    +}
    +
    +int sev_snp_init(int *error)
    +{
    + int rc;
    +
    + mutex_lock(&sev_cmd_mutex);
    + rc = __sev_snp_init_locked(error);
    + mutex_unlock(&sev_cmd_mutex);
    +
    + return rc;
    +}
    +EXPORT_SYMBOL_GPL(sev_snp_init);
    +
    +static int __sev_snp_shutdown_locked(int *error)
    +{
    + struct sev_device *sev = psp_master->sev_data;
    + int ret;
    +
    + ret = __sev_do_cmd_locked(SEV_CMD_SNP_SHUTDOWN, NULL, error);
    + if (ret)
    + return ret;
    +
    + wbinvd_on_all_cpus();
    +
    + ret = __sev_do_cmd_locked(SEV_CMD_SNP_DF_FLUSH, NULL, error);
    + if (ret)
    + dev_err(sev->dev, "SEV-SNP firmware DF_FLUSH failed\n");
    +
    + sev->snp_inited = false;
    + sev->state = SEV_STATE_UNINIT;
    + dev_dbg(sev->dev, "SEV-SNP firmware shutdown\n");
    +
    + return ret;
    +}
    +
    +static int sev_snp_shutdown(int *error)
    +{
    + int rc;
    +
    + mutex_lock(&sev_cmd_mutex);
    + rc = __sev_snp_shutdown_locked(NULL);
    + mutex_unlock(&sev_cmd_mutex);
    +
    + return rc;
    +}
    +
    static int sev_ioctl_do_pek_import(struct sev_issue_cmd *argp, bool writable)
    {
    struct sev_device *sev = psp_master->sev_data;
    @@ -1043,6 +1133,42 @@ int sev_issue_cmd_external_user(struct file *filep, unsigned int cmd,
    }
    EXPORT_SYMBOL_GPL(sev_issue_cmd_external_user);

    +static int sev_snp_firmware_state(void)
    +{
    + struct sev_data_snp_platform_status_buf *buf = NULL;
    + struct page *status_page = NULL;
    + int state = SEV_STATE_UNINIT;
    + int rc, error;
    +
    + status_page = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO);
    + if (!status_page)
    + return -ENOMEM;
    +
    + buf = kzalloc(sizeof(*buf), GFP_KERNEL_ACCOUNT);
    + if (!buf) {
    + __free_page(status_page);
    + return -ENOMEM;
    + }
    +
    + buf->status_paddr = __sme_page_pa(status_page);
    + rc = sev_do_cmd(SEV_CMD_SNP_PLATFORM_STATUS, buf, &error);
    +
    + /*
    + * The status buffer is allocated as a hypervisor page. As per the SEV spec,
    + * if the firmware is in INIT state then status buffer must be either a
    + * the firmware page or the default page. Since our status buffer is in
    + * the hypervisor page, so, if firmware is in INIT state then we should
    + * fail with INVALID_PAGE_STATE.
    + */
    + if (rc && error == SEV_RET_INVALID_PAGE_STATE)
    + state = SEV_STATE_INIT;
    +
    + kfree(buf);
    + __free_page(status_page);
    +
    + return state;
    +}
    +
    void sev_pci_init(void)
    {
    struct sev_device *sev = psp_master->sev_data;
    @@ -1052,8 +1178,20 @@ void sev_pci_init(void)
    if (!sev)
    return;

    + /* Check if the SEV feature is enabled */
    + if (!boot_cpu_has(X86_FEATURE_SEV)) {
    + dev_err(sev->dev, "SEV: Memory Encryption is disabled by the BIOS\n");
    + return;
    + }
    +
    psp_timeout = psp_probe_timeout;

    + /* check if SNP firmware is in INIT state, If so shutdown */
    + if (boot_cpu_has(X86_FEATURE_SEV_SNP)) {
    + if (sev_snp_firmware_state() == SEV_STATE_INIT)
    + sev_snp_shutdown(NULL);
    + }
    +
    if (sev_get_api_version())
    goto err;

    @@ -1086,6 +1224,21 @@ void sev_pci_init(void)
    "SEV: TMR allocation failed, SEV-ES support unavailable\n");
    }

    + /*
    + * If boot CPU supports the SNP, then let first attempt to initialize
    + * the SNP firmware.
    + */
    + if (boot_cpu_has(X86_FEATURE_SEV_SNP)) {
    + rc = sev_snp_init(&error);
    + if (rc) {
    + /*
    + * If we failed to INIT SNP then don't abort the probe.
    + * Continue to initialize the legacy SEV firmware.
    + */
    + dev_err(sev->dev, "SEV-SNP: failed to INIT error %#x\n", error);
    + }
    + }
    +
    /* Initialize the platform */
    rc = sev_platform_init(&error);
    if (rc && (error == SEV_RET_SECURE_DATA_INVALID)) {
    @@ -1105,8 +1258,8 @@ void sev_pci_init(void)
    return;
    }

    - dev_info(sev->dev, "SEV API:%d.%d build:%d\n", sev->api_major,
    - sev->api_minor, sev->build);
    + dev_info(sev->dev, "SEV%s API:%d.%d build:%d\n", sev->snp_inited ?
    + "-SNP" : "", sev->api_major, sev->api_minor, sev->build);

    return;

    @@ -1119,7 +1272,12 @@ void sev_pci_exit(void)
    if (!psp_master->sev_data)
    return;

    - sev_platform_shutdown(NULL);
    + if (boot_cpu_has(X86_FEATURE_SEV))
    + sev_platform_shutdown(NULL);
    +
    + if (boot_cpu_has(X86_FEATURE_SEV_SNP))
    + sev_snp_shutdown(NULL);
    +

    if (sev_es_tmr) {
    /* The TMR area was encrypted, flush it from the cache */
    diff --git a/drivers/crypto/ccp/sev-dev.h b/drivers/crypto/ccp/sev-dev.h
    index dd5c4fe82914..18b116a817ff 100644
    --- a/drivers/crypto/ccp/sev-dev.h
    +++ b/drivers/crypto/ccp/sev-dev.h
    @@ -52,6 +52,8 @@ struct sev_device {
    u8 api_major;
    u8 api_minor;
    u8 build;
    +
    + bool snp_inited;
    };

    int sev_dev_init(struct psp_device *psp);
    diff --git a/include/linux/psp-sev.h b/include/linux/psp-sev.h
    index df89f0207099..ec45c18c3b0a 100644
    --- a/include/linux/psp-sev.h
    +++ b/include/linux/psp-sev.h
    @@ -709,6 +709,20 @@ struct sev_data_snp_guest_request {
    */
    int sev_platform_init(int *error);

    +/**
    + * sev_snp_init - perform SEV SNP_INIT command
    + *
    + * @error: SEV command return code
    + *
    + * Returns:
    + * 0 if the SEV successfully processed the command
    + * -%ENODEV if the SEV device is not available
    + * -%ENOTSUPP if the SEV does not support SEV
    + * -%ETIMEDOUT if the SEV command timed out
    + * -%EIO if the SEV returned a non-zero return code
    + */
    +int sev_snp_init(int *error);
    +
    /**
    * sev_platform_status - perform SEV PLATFORM_STATUS command
    *
    @@ -816,6 +830,8 @@ sev_platform_status(struct sev_user_data_status *status, int *error) { return -E

    static inline int sev_platform_init(int *error) { return -ENODEV; }

    +static inline int sev_snp_init(int *error) { return -ENODEV; }
    +
    static inline int
    sev_guest_deactivate(struct sev_data_deactivate *data, int *error) { return -ENODEV; }

    --
    2.17.1
    \
     
     \ /
      Last update: 2021-03-24 18:06    [W:3.250 / U:0.260 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site