Message ID | 20221214194056.161492-26-michael.roth@amd.com (mailing list archive) |
---|---|
State | Not Applicable |
Delegated to: | Herbert Xu |
Headers | show |
Series | Add AMD Secure Nested Paging (SEV-SNP) Hypervisor Support | expand |
On Wed, Dec 14, 2022 at 01:40:17PM -0600, Michael Roth wrote: > From: Brijesh Singh <brijesh.singh@amd.com> > > Before SNP VMs can be launched, the platform must be appropriately > configured and initialized. Platform initialization is accomplished via > the SNP_INIT command. Make sure to do a WBINVD and issue DF_FLUSH > command to prepare for the first SNP guest launch after INIT. > > During the execution of SNP_INIT command, the firmware configures > and enables SNP security policy enforcement in many system components. > Some system components write to regions of memory reserved by early > x86 firmware (e.g. UEFI). Other system components write to regions > provided by the operation system, hypervisor, or x86 firmware. > Such system components can only write to HV-fixed pages or Default > pages. They will error when attempting to write to other page states > after SNP_INIT enables their SNP enforcement. > > Starting in SNP firmware v1.52, the SNP_INIT_EX command takes a list of > system physical address ranges to convert into the HV-fixed page states > during the RMP initialization. If INIT_RMP is 1, hypervisors should > provide all system physical address ranges that the hypervisor will > never assign to a guest until the next RMP re-initialization. > For instance, the memory that UEFI reserves should be included in the > range list. This allows system components that occasionally write to > memory (e.g. logging to UEFI reserved regions) to not fail due to > RMP initialization and SNP enablement. > > Co-developed-by: Ashish Kalra <ashish.kalra@amd.com> > Signed-off-by: Ashish Kalra <ashish.kalra@amd.com> > Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> > Signed-off-by: Michael Roth <michael.roth@amd.com> > --- > drivers/crypto/ccp/sev-dev.c | 225 +++++++++++++++++++++++++++++++++++ > drivers/crypto/ccp/sev-dev.h | 2 + > include/linux/psp-sev.h | 17 +++ > 3 files changed, 244 insertions(+) > > diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c > index 9d84720a41d7..af20420bd6c2 100644 > --- a/drivers/crypto/ccp/sev-dev.c > +++ b/drivers/crypto/ccp/sev-dev.c > @@ -26,6 +26,7 @@ > #include <linux/fs_struct.h> > > #include <asm/smp.h> > +#include <asm/e820/types.h> > > #include "psp-dev.h" > #include "sev-dev.h" > @@ -34,6 +35,10 @@ > #define SEV_FW_FILE "amd/sev.fw" > #define SEV_FW_NAME_SIZE 64 > > +/* Minimum firmware version required for the SEV-SNP support */ > +#define SNP_MIN_API_MAJOR 1 > +#define SNP_MIN_API_MINOR 51 > + > static DEFINE_MUTEX(sev_cmd_mutex); > static struct sev_misc_dev *misc_dev; > > @@ -76,6 +81,13 @@ static void *sev_es_tmr; > #define NV_LENGTH (32 * 1024) > static void *sev_init_ex_buffer; > > +/* > + * SEV_DATA_RANGE_LIST: > + * Array containing range of pages that firmware transitions to HV-fixed > + * page state. > + */ > +struct sev_data_range_list *snp_range_list; > + > static inline bool sev_version_greater_or_equal(u8 maj, u8 min) > { > struct sev_device *sev = psp_master->sev_data; > @@ -830,6 +842,186 @@ 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 snp_filter_reserved_mem_regions(struct resource *rs, void *arg) > +{ > + struct sev_data_range_list *range_list = arg; > + struct sev_data_range *range = &range_list->ranges[range_list->num_elements]; > + size_t size; > + > + if ((range_list->num_elements * sizeof(struct sev_data_range) + > + sizeof(struct sev_data_range_list)) > PAGE_SIZE) > + return -E2BIG; > + > + switch (rs->desc) { > + case E820_TYPE_RESERVED: > + case E820_TYPE_PMEM: > + case E820_TYPE_ACPI: > + range->base = rs->start & PAGE_MASK; > + size = (rs->end + 1) - rs->start; > + range->page_count = size >> PAGE_SHIFT; > + range_list->num_elements++; > + break; > + default: > + break; > + } > + > + return 0; > +} > + > +static int __sev_snp_init_locked(int *error) > +{ > + struct psp_device *psp = psp_master; > + struct sev_data_snp_init_ex data; > + struct sev_device *sev; > + int rc = 0; > + > + if (!psp || !psp->sev_data) > + return -ENODEV; > + > + sev = psp->sev_data; > + > + if (sev->snp_initialized) > + return 0; Shouldn't this follow this check: if (sev->state == SEV_STATE_INIT) { /* debug printk about possible incorrect call order */ return -ENODEV; } It is game over for SNP, if SEV_CMD_INIT{_EX} got first, which means that this should not proceed. BR, Jarkko
On Wed, Dec 14, 2022 at 01:40:17PM -0600, Michael Roth wrote: > + /* > + * If boot CPU supports SNP, then first attempt to initialize > + * the SNP firmware. > + */ > + if (cpu_feature_enabled(X86_FEATURE_SEV_SNP)) { > + if (!sev_version_greater_or_equal(SNP_MIN_API_MAJOR, SNP_MIN_API_MINOR)) { > + dev_err(sev->dev, "SEV-SNP support requires firmware version >= %d:%d\n", > + SNP_MIN_API_MAJOR, SNP_MIN_API_MINOR); > + } else { > + rc = sev_snp_init(&error, true); > + if (rc) { > + /* > + * Don't abort the probe if SNP INIT failed, > + * continue to initialize the legacy SEV firmware. > + */ > + dev_err(sev->dev, "SEV-SNP: failed to INIT error %#x\n", error); > + } > + } > + } I think this is not right as there is a dep between sev init and this, and there is about a dozen of call sites already __sev_platform_init_locked(). Instead there should be __sev_snp_init_locked() that would be called as part of __sev_platform_init_locked() flow. Also TMR allocation should be moved inside __sev_platform_init_locked, given that it needs to be marked into RMP after SNP init. BR, Jarkko
Hello Jarkko, On 12/31/2022 9:32 AM, Jarkko Sakkinen wrote: > On Wed, Dec 14, 2022 at 01:40:17PM -0600, Michael Roth wrote: >> From: Brijesh Singh <brijesh.singh@amd.com> >> >> Before SNP VMs can be launched, the platform must be appropriately >> configured and initialized. Platform initialization is accomplished via >> the SNP_INIT command. Make sure to do a WBINVD and issue DF_FLUSH >> command to prepare for the first SNP guest launch after INIT. >> >> During the execution of SNP_INIT command, the firmware configures >> and enables SNP security policy enforcement in many system components. >> Some system components write to regions of memory reserved by early >> x86 firmware (e.g. UEFI). Other system components write to regions >> provided by the operation system, hypervisor, or x86 firmware. >> Such system components can only write to HV-fixed pages or Default >> pages. They will error when attempting to write to other page states >> after SNP_INIT enables their SNP enforcement. >> >> Starting in SNP firmware v1.52, the SNP_INIT_EX command takes a list of >> system physical address ranges to convert into the HV-fixed page states >> during the RMP initialization. If INIT_RMP is 1, hypervisors should >> provide all system physical address ranges that the hypervisor will >> never assign to a guest until the next RMP re-initialization. >> For instance, the memory that UEFI reserves should be included in the >> range list. This allows system components that occasionally write to >> memory (e.g. logging to UEFI reserved regions) to not fail due to >> RMP initialization and SNP enablement. >> >> Co-developed-by: Ashish Kalra <ashish.kalra@amd.com> >> Signed-off-by: Ashish Kalra <ashish.kalra@amd.com> >> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> >> Signed-off-by: Michael Roth <michael.roth@amd.com> >> --- >> drivers/crypto/ccp/sev-dev.c | 225 +++++++++++++++++++++++++++++++++++ >> drivers/crypto/ccp/sev-dev.h | 2 + >> include/linux/psp-sev.h | 17 +++ >> 3 files changed, 244 insertions(+) >> >> diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c >> index 9d84720a41d7..af20420bd6c2 100644 >> --- a/drivers/crypto/ccp/sev-dev.c >> +++ b/drivers/crypto/ccp/sev-dev.c >> @@ -26,6 +26,7 @@ >> #include <linux/fs_struct.h> >> >> #include <asm/smp.h> >> +#include <asm/e820/types.h> >> >> #include "psp-dev.h" >> #include "sev-dev.h" >> @@ -34,6 +35,10 @@ >> #define SEV_FW_FILE "amd/sev.fw" >> #define SEV_FW_NAME_SIZE 64 >> >> +/* Minimum firmware version required for the SEV-SNP support */ >> +#define SNP_MIN_API_MAJOR 1 >> +#define SNP_MIN_API_MINOR 51 >> + >> static DEFINE_MUTEX(sev_cmd_mutex); >> static struct sev_misc_dev *misc_dev; >> >> @@ -76,6 +81,13 @@ static void *sev_es_tmr; >> #define NV_LENGTH (32 * 1024) >> static void *sev_init_ex_buffer; >> >> +/* >> + * SEV_DATA_RANGE_LIST: >> + * Array containing range of pages that firmware transitions to HV-fixed >> + * page state. >> + */ >> +struct sev_data_range_list *snp_range_list; >> + >> static inline bool sev_version_greater_or_equal(u8 maj, u8 min) >> { >> struct sev_device *sev = psp_master->sev_data; >> @@ -830,6 +842,186 @@ 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 snp_filter_reserved_mem_regions(struct resource *rs, void *arg) >> +{ >> + struct sev_data_range_list *range_list = arg; >> + struct sev_data_range *range = &range_list->ranges[range_list->num_elements]; >> + size_t size; >> + >> + if ((range_list->num_elements * sizeof(struct sev_data_range) + >> + sizeof(struct sev_data_range_list)) > PAGE_SIZE) >> + return -E2BIG; >> + >> + switch (rs->desc) { >> + case E820_TYPE_RESERVED: >> + case E820_TYPE_PMEM: >> + case E820_TYPE_ACPI: >> + range->base = rs->start & PAGE_MASK; >> + size = (rs->end + 1) - rs->start; >> + range->page_count = size >> PAGE_SHIFT; >> + range_list->num_elements++; >> + break; >> + default: >> + break; >> + } >> + >> + return 0; >> +} >> + >> +static int __sev_snp_init_locked(int *error) >> +{ >> + struct psp_device *psp = psp_master; >> + struct sev_data_snp_init_ex data; >> + struct sev_device *sev; >> + int rc = 0; >> + >> + if (!psp || !psp->sev_data) >> + return -ENODEV; >> + >> + sev = psp->sev_data; >> + >> + if (sev->snp_initialized) >> + return 0; > > Shouldn't this follow this check: > > if (sev->state == SEV_STATE_INIT) { > /* debug printk about possible incorrect call order */ > return -ENODEV; > } > > It is game over for SNP, if SEV_CMD_INIT{_EX} got first, which means that > this should not proceed. But, how will SEV_CMD_INIT_EX happen before as sev_pci_init() which is invoked during CCP module load/initialization, will first try to do sev_snp_init() if SNP is supported, before it invokes sev_platform_init() to do SEV firmware initialization ? Thanks, Ashish
Hello Jarkko, On 1/4/2023 6:12 AM, Jarkko Sakkinen wrote: > On Wed, Dec 14, 2022 at 01:40:17PM -0600, Michael Roth wrote: >> + /* >> + * If boot CPU supports SNP, then first attempt to initialize >> + * the SNP firmware. >> + */ >> + if (cpu_feature_enabled(X86_FEATURE_SEV_SNP)) { >> + if (!sev_version_greater_or_equal(SNP_MIN_API_MAJOR, SNP_MIN_API_MINOR)) { >> + dev_err(sev->dev, "SEV-SNP support requires firmware version >= %d:%d\n", >> + SNP_MIN_API_MAJOR, SNP_MIN_API_MINOR); >> + } else { >> + rc = sev_snp_init(&error, true); >> + if (rc) { >> + /* >> + * Don't abort the probe if SNP INIT failed, >> + * continue to initialize the legacy SEV firmware. >> + */ >> + dev_err(sev->dev, "SEV-SNP: failed to INIT error %#x\n", error); >> + } >> + } >> + } > > I think this is not right as there is a dep between sev init and this, > and there is about a dozen of call sites already __sev_platform_init_locked(). > sev_init ? As this is invoked during CCP module load/initialization, shouldn't this get invoked before any other call sites invoking __sev_platform_init_locked() ? Thanks, Ashish > Instead there should be __sev_snp_init_locked() that would be called as > part of __sev_platform_init_locked() flow. > > Also TMR allocation should be moved inside __sev_platform_init_locked, > given that it needs to be marked into RMP after SNP init. > > BR, Jarkko >
On Thu, Jan 05, 2023 at 04:40:29PM -0600, Kalra, Ashish wrote: > Hello Jarkko, > > On 12/31/2022 9:32 AM, Jarkko Sakkinen wrote: > > On Wed, Dec 14, 2022 at 01:40:17PM -0600, Michael Roth wrote: > > > From: Brijesh Singh <brijesh.singh@amd.com> > > > > > > Before SNP VMs can be launched, the platform must be appropriately > > > configured and initialized. Platform initialization is accomplished via > > > the SNP_INIT command. Make sure to do a WBINVD and issue DF_FLUSH > > > command to prepare for the first SNP guest launch after INIT. > > > > > > During the execution of SNP_INIT command, the firmware configures > > > and enables SNP security policy enforcement in many system components. > > > Some system components write to regions of memory reserved by early > > > x86 firmware (e.g. UEFI). Other system components write to regions > > > provided by the operation system, hypervisor, or x86 firmware. > > > Such system components can only write to HV-fixed pages or Default > > > pages. They will error when attempting to write to other page states > > > after SNP_INIT enables their SNP enforcement. > > > > > > Starting in SNP firmware v1.52, the SNP_INIT_EX command takes a list of > > > system physical address ranges to convert into the HV-fixed page states > > > during the RMP initialization. If INIT_RMP is 1, hypervisors should > > > provide all system physical address ranges that the hypervisor will > > > never assign to a guest until the next RMP re-initialization. > > > For instance, the memory that UEFI reserves should be included in the > > > range list. This allows system components that occasionally write to > > > memory (e.g. logging to UEFI reserved regions) to not fail due to > > > RMP initialization and SNP enablement. > > > > > > Co-developed-by: Ashish Kalra <ashish.kalra@amd.com> > > > Signed-off-by: Ashish Kalra <ashish.kalra@amd.com> > > > Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> > > > Signed-off-by: Michael Roth <michael.roth@amd.com> > > > --- > > > drivers/crypto/ccp/sev-dev.c | 225 +++++++++++++++++++++++++++++++++++ > > > drivers/crypto/ccp/sev-dev.h | 2 + > > > include/linux/psp-sev.h | 17 +++ > > > 3 files changed, 244 insertions(+) > > > > > > diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c > > > index 9d84720a41d7..af20420bd6c2 100644 > > > --- a/drivers/crypto/ccp/sev-dev.c > > > +++ b/drivers/crypto/ccp/sev-dev.c > > > @@ -26,6 +26,7 @@ > > > #include <linux/fs_struct.h> > > > #include <asm/smp.h> > > > +#include <asm/e820/types.h> > > > #include "psp-dev.h" > > > #include "sev-dev.h" > > > @@ -34,6 +35,10 @@ > > > #define SEV_FW_FILE "amd/sev.fw" > > > #define SEV_FW_NAME_SIZE 64 > > > +/* Minimum firmware version required for the SEV-SNP support */ > > > +#define SNP_MIN_API_MAJOR 1 > > > +#define SNP_MIN_API_MINOR 51 > > > + > > > static DEFINE_MUTEX(sev_cmd_mutex); > > > static struct sev_misc_dev *misc_dev; > > > @@ -76,6 +81,13 @@ static void *sev_es_tmr; > > > #define NV_LENGTH (32 * 1024) > > > static void *sev_init_ex_buffer; > > > +/* > > > + * SEV_DATA_RANGE_LIST: > > > + * Array containing range of pages that firmware transitions to HV-fixed > > > + * page state. > > > + */ > > > +struct sev_data_range_list *snp_range_list; > > > + > > > static inline bool sev_version_greater_or_equal(u8 maj, u8 min) > > > { > > > struct sev_device *sev = psp_master->sev_data; > > > @@ -830,6 +842,186 @@ 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 snp_filter_reserved_mem_regions(struct resource *rs, void *arg) > > > +{ > > > + struct sev_data_range_list *range_list = arg; > > > + struct sev_data_range *range = &range_list->ranges[range_list->num_elements]; > > > + size_t size; > > > + > > > + if ((range_list->num_elements * sizeof(struct sev_data_range) + > > > + sizeof(struct sev_data_range_list)) > PAGE_SIZE) > > > + return -E2BIG; > > > + > > > + switch (rs->desc) { > > > + case E820_TYPE_RESERVED: > > > + case E820_TYPE_PMEM: > > > + case E820_TYPE_ACPI: > > > + range->base = rs->start & PAGE_MASK; > > > + size = (rs->end + 1) - rs->start; > > > + range->page_count = size >> PAGE_SHIFT; > > > + range_list->num_elements++; > > > + break; > > > + default: > > > + break; > > > + } > > > + > > > + return 0; > > > +} > > > + > > > +static int __sev_snp_init_locked(int *error) > > > +{ > > > + struct psp_device *psp = psp_master; > > > + struct sev_data_snp_init_ex data; > > > + struct sev_device *sev; > > > + int rc = 0; > > > + > > > + if (!psp || !psp->sev_data) > > > + return -ENODEV; > > > + > > > + sev = psp->sev_data; > > > + > > > + if (sev->snp_initialized) > > > + return 0; > > > > Shouldn't this follow this check: > > > > if (sev->state == SEV_STATE_INIT) { > > /* debug printk about possible incorrect call order */ > > return -ENODEV; > > } > > > > It is game over for SNP, if SEV_CMD_INIT{_EX} got first, which means that > > this should not proceed. > > > But, how will SEV_CMD_INIT_EX happen before as sev_pci_init() which is > invoked during CCP module load/initialization, will first try to do > sev_snp_init() if SNP is supported, before it invokes sev_platform_init() to > do SEV firmware initialization ? Because the symbol is exported outside the driver to be called by other subsystems, you need to have a santiy check for the call order, as it is a hardware constraint. Otherwise, any unconsidered change in either side could unknowingily break the kernel. Alternatively, you could choose not to export sev_snp_init(). It is supported by the fact that the call in sev_guest_init() is does nothing useful (for the reasons you already wrote). BR, Jarkko
On Thu, Jan 05, 2023 at 04:54:23PM -0600, Kalra, Ashish wrote: > Hello Jarkko, > > On 1/4/2023 6:12 AM, Jarkko Sakkinen wrote: > > On Wed, Dec 14, 2022 at 01:40:17PM -0600, Michael Roth wrote: > > > + /* > > > + * If boot CPU supports SNP, then first attempt to initialize > > > + * the SNP firmware. > > > + */ > > > + if (cpu_feature_enabled(X86_FEATURE_SEV_SNP)) { > > > + if (!sev_version_greater_or_equal(SNP_MIN_API_MAJOR, SNP_MIN_API_MINOR)) { > > > + dev_err(sev->dev, "SEV-SNP support requires firmware version >= %d:%d\n", > > > + SNP_MIN_API_MAJOR, SNP_MIN_API_MINOR); > > > + } else { > > > + rc = sev_snp_init(&error, true); > > > + if (rc) { > > > + /* > > > + * Don't abort the probe if SNP INIT failed, > > > + * continue to initialize the legacy SEV firmware. > > > + */ > > > + dev_err(sev->dev, "SEV-SNP: failed to INIT error %#x\n", error); > > > + } > > > + } > > > + } > > > > I think this is not right as there is a dep between sev init and this, > > and there is about a dozen of call sites already __sev_platform_init_locked(). > > > > sev_init ? > > As this is invoked during CCP module load/initialization, shouldn't this get > invoked before any other call sites invoking __sev_platform_init_locked() ? Then it should not be exported because this the only working call site. However, the benefit of __sev_platform_init_locked() addressing SNP init is that psp_init_on_probe can also postpone SNP init without possibility to any side effects (other call sites than sev_guest_init()). BR, Jarkko
diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c index 9d84720a41d7..af20420bd6c2 100644 --- a/drivers/crypto/ccp/sev-dev.c +++ b/drivers/crypto/ccp/sev-dev.c @@ -26,6 +26,7 @@ #include <linux/fs_struct.h> #include <asm/smp.h> +#include <asm/e820/types.h> #include "psp-dev.h" #include "sev-dev.h" @@ -34,6 +35,10 @@ #define SEV_FW_FILE "amd/sev.fw" #define SEV_FW_NAME_SIZE 64 +/* Minimum firmware version required for the SEV-SNP support */ +#define SNP_MIN_API_MAJOR 1 +#define SNP_MIN_API_MINOR 51 + static DEFINE_MUTEX(sev_cmd_mutex); static struct sev_misc_dev *misc_dev; @@ -76,6 +81,13 @@ static void *sev_es_tmr; #define NV_LENGTH (32 * 1024) static void *sev_init_ex_buffer; +/* + * SEV_DATA_RANGE_LIST: + * Array containing range of pages that firmware transitions to HV-fixed + * page state. + */ +struct sev_data_range_list *snp_range_list; + static inline bool sev_version_greater_or_equal(u8 maj, u8 min) { struct sev_device *sev = psp_master->sev_data; @@ -830,6 +842,186 @@ 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 snp_filter_reserved_mem_regions(struct resource *rs, void *arg) +{ + struct sev_data_range_list *range_list = arg; + struct sev_data_range *range = &range_list->ranges[range_list->num_elements]; + size_t size; + + if ((range_list->num_elements * sizeof(struct sev_data_range) + + sizeof(struct sev_data_range_list)) > PAGE_SIZE) + return -E2BIG; + + switch (rs->desc) { + case E820_TYPE_RESERVED: + case E820_TYPE_PMEM: + case E820_TYPE_ACPI: + range->base = rs->start & PAGE_MASK; + size = (rs->end + 1) - rs->start; + range->page_count = size >> PAGE_SHIFT; + range_list->num_elements++; + break; + default: + break; + } + + return 0; +} + +static int __sev_snp_init_locked(int *error) +{ + struct psp_device *psp = psp_master; + struct sev_data_snp_init_ex data; + struct sev_device *sev; + int rc = 0; + + if (!psp || !psp->sev_data) + return -ENODEV; + + sev = psp->sev_data; + + if (sev->snp_initialized) + return 0; + + /* + * The 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); + + /* + * Starting in SNP firmware v1.52, the SNP_INIT_EX command takes a list of + * system physical address ranges to convert into the HV-fixed page states + * during the RMP initialization. For instance, the memory that UEFI + * reserves should be included in the range list. This allows system + * components that occasionally write to memory (e.g. logging to UEFI + * reserved regions) to not fail due to RMP initialization and SNP enablement. + */ + if (sev_version_greater_or_equal(SNP_MIN_API_MAJOR, 52)) { + /* + * Firmware checks that the pages containing the ranges enumerated + * in the RANGES structure are either in the Default page state or in the + * firmware page state. + */ + snp_range_list = sev_fw_alloc(PAGE_SIZE); + if (!snp_range_list) { + dev_err(sev->dev, + "SEV: SNP_INIT_EX range list memory allocation failed\n"); + return -ENOMEM; + } + + memset(snp_range_list, 0, PAGE_SIZE); + + /* + * Retrieve all reserved memory regions setup by UEFI from the e820 memory map + * to be setup as HV-fixed pages. + */ + + rc = walk_iomem_res_desc(IORES_DESC_NONE, IORESOURCE_MEM, 0, ~0, + snp_range_list, snp_filter_reserved_mem_regions); + if (rc) { + dev_err(sev->dev, + "SEV: SNP_INIT_EX walk_iomem_res_desc failed rc = %d\n", rc); + return rc; + } + + memset(&data, 0, sizeof(data)); + data.init_rmp = 1; + data.list_paddr_en = 1; + data.list_paddr = __pa(snp_range_list); + + rc = __sev_do_cmd_locked(SEV_CMD_SNP_INIT_EX, &data, error); + if (rc) + return rc; + } else { + rc = __sev_do_cmd_locked(SEV_CMD_SNP_INIT, NULL, error); + if (rc) + return rc; + } + + /* Prepare for first SNP guest launch after INIT */ + wbinvd_on_all_cpus(); + rc = __sev_do_cmd_locked(SEV_CMD_SNP_DF_FLUSH, NULL, error); + if (rc) + return rc; + + sev->snp_initialized = true; + dev_dbg(sev->dev, "SEV-SNP firmware initialized\n"); + + return rc; +} + +int sev_snp_init(int *error, bool init_on_probe) +{ + int rc; + + if (!cpu_feature_enabled(X86_FEATURE_SEV_SNP)) + return -ENODEV; + + if (init_on_probe && !psp_init_on_probe) + return 0; + + 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; + struct sev_data_snp_shutdown_ex data; + int ret; + + if (!sev->snp_initialized) + return 0; + + memset(&data, 0, sizeof(data)); + data.length = sizeof(data); + data.iommu_snp_shutdown = 1; + + wbinvd_on_all_cpus(); + +retry: + ret = __sev_do_cmd_locked(SEV_CMD_SNP_SHUTDOWN_EX, &data, error); + /* SHUTDOWN may require DF_FLUSH */ + if (*error == SEV_RET_DFFLUSH_REQUIRED) { + ret = __sev_do_cmd_locked(SEV_CMD_SNP_DF_FLUSH, NULL, NULL); + if (ret) { + dev_err(sev->dev, "SEV-SNP DF_FLUSH failed\n"); + return ret; + } + goto retry; + } + if (ret) { + dev_err(sev->dev, "SEV-SNP firmware shutdown failed\n"); + return ret; + } + + sev->snp_initialized = false; + 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(error); + 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; @@ -1270,6 +1462,8 @@ int sev_dev_init(struct psp_device *psp) static void sev_firmware_shutdown(struct sev_device *sev) { + int error; + sev_platform_shutdown(NULL); if (sev_es_tmr) { @@ -1286,6 +1480,14 @@ static void sev_firmware_shutdown(struct sev_device *sev) get_order(NV_LENGTH)); sev_init_ex_buffer = NULL; } + + if (snp_range_list) { + free_pages((unsigned long)snp_range_list, + get_order(PAGE_SIZE)); + snp_range_list = NULL; + } + + sev_snp_shutdown(&error); } void sev_dev_destroy(struct psp_device *psp) @@ -1341,6 +1543,26 @@ void sev_pci_init(void) } } + /* + * If boot CPU supports SNP, then first attempt to initialize + * the SNP firmware. + */ + if (cpu_feature_enabled(X86_FEATURE_SEV_SNP)) { + if (!sev_version_greater_or_equal(SNP_MIN_API_MAJOR, SNP_MIN_API_MINOR)) { + dev_err(sev->dev, "SEV-SNP support requires firmware version >= %d:%d\n", + SNP_MIN_API_MAJOR, SNP_MIN_API_MINOR); + } else { + rc = sev_snp_init(&error, true); + if (rc) { + /* + * Don't abort the probe if SNP INIT failed, + * continue to initialize the legacy SEV firmware. + */ + dev_err(sev->dev, "SEV-SNP: failed to INIT error %#x\n", error); + } + } + } + /* Obtain the TMR memory area for SEV-ES use */ sev_es_tmr = sev_fw_alloc(SEV_ES_TMR_SIZE); if (!sev_es_tmr) @@ -1356,6 +1578,9 @@ void sev_pci_init(void) dev_err(sev->dev, "SEV: failed to INIT error %#x, rc %d\n", error, rc); + dev_info(sev->dev, "SEV%s API:%d.%d build:%d\n", sev->snp_initialized ? + "-SNP" : "", sev->api_major, sev->api_minor, sev->build); + return; err: diff --git a/drivers/crypto/ccp/sev-dev.h b/drivers/crypto/ccp/sev-dev.h index 666c21eb81ab..34767657beb5 100644 --- a/drivers/crypto/ccp/sev-dev.h +++ b/drivers/crypto/ccp/sev-dev.h @@ -52,6 +52,8 @@ struct sev_device { u8 build; void *cmd_buf; + + bool snp_initialized; }; int sev_dev_init(struct psp_device *psp); diff --git a/include/linux/psp-sev.h b/include/linux/psp-sev.h index 31b045e1926f..8cfe92e82743 100644 --- a/include/linux/psp-sev.h +++ b/include/linux/psp-sev.h @@ -794,6 +794,21 @@ struct sev_data_snp_shutdown_ex { */ int sev_platform_init(int *error); +/** + * sev_snp_init - perform SEV SNP_INIT command + * + * @error: SEV command return code + * @init_on_probe: indicates if called during module probe/init + * + * 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, bool init_on_probe); + /** * sev_platform_status - perform SEV PLATFORM_STATUS command * @@ -901,6 +916,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, bool init_on_probe) { return -ENODEV; } + static inline int sev_guest_deactivate(struct sev_data_deactivate *data, int *error) { return -ENODEV; }