Skip to content

Commit 1ca5614

Browse files
codomaniabp3tk0v
authored andcommitted
crypto: ccp: Add support to initialize the AMD-SP for SEV-SNP
Before SNP VMs can be launched, the platform must be appropriately configured and initialized via the SNP_INIT command. 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 pages in 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. Note that SNP_INIT(_EX) must not be executed while non-SEV guests are executing, otherwise it is possible that the system could reset or hang. The psp_init_on_probe module parameter was added for SEV/SEV-ES support and the init_ex_path module parameter to allow for time for the necessary file system to be mounted/available. SNP_INIT(_EX) does not use the file associated with init_ex_path. So, to avoid running into issues where SNP_INIT(_EX) is called while there are other running guests, issue it during module probe regardless of the psp_init_on_probe setting, but maintain the previous deferrable handling for SEV/SEV-ES initialization. [ mdr: Squash in psp_init_on_probe changes from Tom, reduce proliferation of 'probe' function parameter where possible. bp: Fix 32-bit allmodconfig build. ] Signed-off-by: Brijesh Singh <[email protected]> Co-developed-by: Ashish Kalra <[email protected]> Signed-off-by: Ashish Kalra <[email protected]> Co-developed-by: Jarkko Sakkinen <[email protected]> Signed-off-by: Jarkko Sakkinen <[email protected]> Signed-off-by: Tom Lendacky <[email protected]> Signed-off-by: Michael Roth <[email protected]> Signed-off-by: Borislav Petkov (AMD) <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 3a45dc2 commit 1ca5614

File tree

4 files changed

+282
-24
lines changed

4 files changed

+282
-24
lines changed

arch/x86/kvm/svm/sev.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ static void sev_unbind_asid(struct kvm *kvm, unsigned int handle)
246246
static int sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp)
247247
{
248248
struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
249+
struct sev_platform_init_args init_args = {0};
249250
int asid, ret;
250251

251252
if (kvm->created_vcpus)
@@ -262,7 +263,8 @@ static int sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp)
262263
goto e_no_asid;
263264
sev->asid = asid;
264265

265-
ret = sev_platform_init(&argp->error);
266+
init_args.probe = false;
267+
ret = sev_platform_init(&init_args);
266268
if (ret)
267269
goto e_free;
268270

@@ -274,6 +276,7 @@ static int sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp)
274276
return 0;
275277

276278
e_free:
279+
argp->error = init_args.error;
277280
sev_asid_free(sev);
278281
sev->asid = 0;
279282
e_no_asid:

drivers/crypto/ccp/sev-dev.c

Lines changed: 260 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
#include <asm/smp.h>
3131
#include <asm/cacheflush.h>
32+
#include <asm/e820/types.h>
3233

3334
#include "psp-dev.h"
3435
#include "sev-dev.h"
@@ -37,6 +38,10 @@
3738
#define SEV_FW_FILE "amd/sev.fw"
3839
#define SEV_FW_NAME_SIZE 64
3940

41+
/* Minimum firmware version required for the SEV-SNP support */
42+
#define SNP_MIN_API_MAJOR 1
43+
#define SNP_MIN_API_MINOR 51
44+
4045
static DEFINE_MUTEX(sev_cmd_mutex);
4146
static struct sev_misc_dev *misc_dev;
4247

@@ -80,6 +85,13 @@ static void *sev_es_tmr;
8085
#define NV_LENGTH (32 * 1024)
8186
static void *sev_init_ex_buffer;
8287

88+
/*
89+
* SEV_DATA_RANGE_LIST:
90+
* Array containing range of pages that firmware transitions to HV-fixed
91+
* page state.
92+
*/
93+
struct sev_data_range_list *snp_range_list;
94+
8395
static inline bool sev_version_greater_or_equal(u8 maj, u8 min)
8496
{
8597
struct sev_device *sev = psp_master->sev_data;
@@ -480,20 +492,163 @@ static inline int __sev_do_init_locked(int *psp_ret)
480492
return __sev_init_locked(psp_ret);
481493
}
482494

483-
static int __sev_platform_init_locked(int *error)
495+
static void snp_set_hsave_pa(void *arg)
496+
{
497+
wrmsrl(MSR_VM_HSAVE_PA, 0);
498+
}
499+
500+
static int snp_filter_reserved_mem_regions(struct resource *rs, void *arg)
501+
{
502+
struct sev_data_range_list *range_list = arg;
503+
struct sev_data_range *range = &range_list->ranges[range_list->num_elements];
504+
size_t size;
505+
506+
/*
507+
* Ensure the list of HV_FIXED pages that will be passed to firmware
508+
* do not exceed the page-sized argument buffer.
509+
*/
510+
if ((range_list->num_elements * sizeof(struct sev_data_range) +
511+
sizeof(struct sev_data_range_list)) > PAGE_SIZE)
512+
return -E2BIG;
513+
514+
switch (rs->desc) {
515+
case E820_TYPE_RESERVED:
516+
case E820_TYPE_PMEM:
517+
case E820_TYPE_ACPI:
518+
range->base = rs->start & PAGE_MASK;
519+
size = PAGE_ALIGN((rs->end + 1) - rs->start);
520+
range->page_count = size >> PAGE_SHIFT;
521+
range_list->num_elements++;
522+
break;
523+
default:
524+
break;
525+
}
526+
527+
return 0;
528+
}
529+
530+
static int __sev_snp_init_locked(int *error)
484531
{
485-
int rc = 0, psp_ret = SEV_RET_NO_FW_CALL;
486532
struct psp_device *psp = psp_master;
533+
struct sev_data_snp_init_ex data;
487534
struct sev_device *sev;
535+
void *arg = &data;
536+
int cmd, rc = 0;
488537

489-
if (!psp || !psp->sev_data)
538+
if (!cpu_feature_enabled(X86_FEATURE_SEV_SNP))
490539
return -ENODEV;
491540

492541
sev = psp->sev_data;
493542

543+
if (sev->snp_initialized)
544+
return 0;
545+
546+
if (!sev_version_greater_or_equal(SNP_MIN_API_MAJOR, SNP_MIN_API_MINOR)) {
547+
dev_dbg(sev->dev, "SEV-SNP support requires firmware version >= %d:%d\n",
548+
SNP_MIN_API_MAJOR, SNP_MIN_API_MINOR);
549+
return 0;
550+
}
551+
552+
/* SNP_INIT requires MSR_VM_HSAVE_PA to be cleared on all CPUs. */
553+
on_each_cpu(snp_set_hsave_pa, NULL, 1);
554+
555+
/*
556+
* Starting in SNP firmware v1.52, the SNP_INIT_EX command takes a list
557+
* of system physical address ranges to convert into HV-fixed page
558+
* states during the RMP initialization. For instance, the memory that
559+
* UEFI reserves should be included in the that list. This allows system
560+
* components that occasionally write to memory (e.g. logging to UEFI
561+
* reserved regions) to not fail due to RMP initialization and SNP
562+
* enablement.
563+
*
564+
*/
565+
if (sev_version_greater_or_equal(SNP_MIN_API_MAJOR, 52)) {
566+
/*
567+
* Firmware checks that the pages containing the ranges enumerated
568+
* in the RANGES structure are either in the default page state or in the
569+
* firmware page state.
570+
*/
571+
snp_range_list = kzalloc(PAGE_SIZE, GFP_KERNEL);
572+
if (!snp_range_list) {
573+
dev_err(sev->dev,
574+
"SEV: SNP_INIT_EX range list memory allocation failed\n");
575+
return -ENOMEM;
576+
}
577+
578+
/*
579+
* Retrieve all reserved memory regions from the e820 memory map
580+
* to be setup as HV-fixed pages.
581+
*/
582+
rc = walk_iomem_res_desc(IORES_DESC_NONE, IORESOURCE_MEM, 0, ~0,
583+
snp_range_list, snp_filter_reserved_mem_regions);
584+
if (rc) {
585+
dev_err(sev->dev,
586+
"SEV: SNP_INIT_EX walk_iomem_res_desc failed rc = %d\n", rc);
587+
return rc;
588+
}
589+
590+
memset(&data, 0, sizeof(data));
591+
data.init_rmp = 1;
592+
data.list_paddr_en = 1;
593+
data.list_paddr = __psp_pa(snp_range_list);
594+
cmd = SEV_CMD_SNP_INIT_EX;
595+
} else {
596+
cmd = SEV_CMD_SNP_INIT;
597+
arg = NULL;
598+
}
599+
600+
/*
601+
* The following sequence must be issued before launching the first SNP
602+
* guest to ensure all dirty cache lines are flushed, including from
603+
* updates to the RMP table itself via the RMPUPDATE instruction:
604+
*
605+
* - WBINVD on all running CPUs
606+
* - SEV_CMD_SNP_INIT[_EX] firmware command
607+
* - WBINVD on all running CPUs
608+
* - SEV_CMD_SNP_DF_FLUSH firmware command
609+
*/
610+
wbinvd_on_all_cpus();
611+
612+
rc = __sev_do_cmd_locked(cmd, arg, error);
613+
if (rc)
614+
return rc;
615+
616+
/* Prepare for first SNP guest launch after INIT. */
617+
wbinvd_on_all_cpus();
618+
rc = __sev_do_cmd_locked(SEV_CMD_SNP_DF_FLUSH, NULL, error);
619+
if (rc)
620+
return rc;
621+
622+
sev->snp_initialized = true;
623+
dev_dbg(sev->dev, "SEV-SNP firmware initialized\n");
624+
625+
return rc;
626+
}
627+
628+
static int __sev_platform_init_locked(int *error)
629+
{
630+
int rc, psp_ret = SEV_RET_NO_FW_CALL;
631+
struct sev_device *sev;
632+
633+
if (!psp_master || !psp_master->sev_data)
634+
return -ENODEV;
635+
636+
sev = psp_master->sev_data;
637+
494638
if (sev->state == SEV_STATE_INIT)
495639
return 0;
496640

641+
if (!sev_es_tmr) {
642+
/* Obtain the TMR memory area for SEV-ES use */
643+
sev_es_tmr = sev_fw_alloc(SEV_ES_TMR_SIZE);
644+
if (sev_es_tmr)
645+
/* Must flush the cache before giving it to the firmware */
646+
clflush_cache_range(sev_es_tmr, SEV_ES_TMR_SIZE);
647+
else
648+
dev_warn(sev->dev,
649+
"SEV: TMR allocation failed, SEV-ES support unavailable\n");
650+
}
651+
497652
if (sev_init_ex_buffer) {
498653
rc = sev_read_init_ex_file();
499654
if (rc)
@@ -536,12 +691,46 @@ static int __sev_platform_init_locked(int *error)
536691
return 0;
537692
}
538693

539-
int sev_platform_init(int *error)
694+
static int _sev_platform_init_locked(struct sev_platform_init_args *args)
695+
{
696+
struct sev_device *sev;
697+
int rc;
698+
699+
if (!psp_master || !psp_master->sev_data)
700+
return -ENODEV;
701+
702+
sev = psp_master->sev_data;
703+
704+
if (sev->state == SEV_STATE_INIT)
705+
return 0;
706+
707+
/*
708+
* Legacy guests cannot be running while SNP_INIT(_EX) is executing,
709+
* so perform SEV-SNP initialization at probe time.
710+
*/
711+
rc = __sev_snp_init_locked(&args->error);
712+
if (rc && rc != -ENODEV) {
713+
/*
714+
* Don't abort the probe if SNP INIT failed,
715+
* continue to initialize the legacy SEV firmware.
716+
*/
717+
dev_err(sev->dev, "SEV-SNP: failed to INIT rc %d, error %#x\n",
718+
rc, args->error);
719+
}
720+
721+
/* Defer legacy SEV/SEV-ES support if allowed by caller/module. */
722+
if (args->probe && !psp_init_on_probe)
723+
return 0;
724+
725+
return __sev_platform_init_locked(&args->error);
726+
}
727+
728+
int sev_platform_init(struct sev_platform_init_args *args)
540729
{
541730
int rc;
542731

543732
mutex_lock(&sev_cmd_mutex);
544-
rc = __sev_platform_init_locked(error);
733+
rc = _sev_platform_init_locked(args);
545734
mutex_unlock(&sev_cmd_mutex);
546735

547736
return rc;
@@ -852,6 +1041,55 @@ static int sev_update_firmware(struct device *dev)
8521041
return ret;
8531042
}
8541043

1044+
static int __sev_snp_shutdown_locked(int *error)
1045+
{
1046+
struct sev_device *sev = psp_master->sev_data;
1047+
struct sev_data_snp_shutdown_ex data;
1048+
int ret;
1049+
1050+
if (!sev->snp_initialized)
1051+
return 0;
1052+
1053+
memset(&data, 0, sizeof(data));
1054+
data.len = sizeof(data);
1055+
data.iommu_snp_shutdown = 1;
1056+
1057+
wbinvd_on_all_cpus();
1058+
1059+
ret = __sev_do_cmd_locked(SEV_CMD_SNP_SHUTDOWN_EX, &data, error);
1060+
/* SHUTDOWN may require DF_FLUSH */
1061+
if (*error == SEV_RET_DFFLUSH_REQUIRED) {
1062+
ret = __sev_do_cmd_locked(SEV_CMD_SNP_DF_FLUSH, NULL, NULL);
1063+
if (ret) {
1064+
dev_err(sev->dev, "SEV-SNP DF_FLUSH failed\n");
1065+
return ret;
1066+
}
1067+
/* reissue the shutdown command */
1068+
ret = __sev_do_cmd_locked(SEV_CMD_SNP_SHUTDOWN_EX, &data,
1069+
error);
1070+
}
1071+
if (ret) {
1072+
dev_err(sev->dev, "SEV-SNP firmware shutdown failed\n");
1073+
return ret;
1074+
}
1075+
1076+
sev->snp_initialized = false;
1077+
dev_dbg(sev->dev, "SEV-SNP firmware shutdown\n");
1078+
1079+
return ret;
1080+
}
1081+
1082+
static int sev_snp_shutdown(int *error)
1083+
{
1084+
int rc;
1085+
1086+
mutex_lock(&sev_cmd_mutex);
1087+
rc = __sev_snp_shutdown_locked(error);
1088+
mutex_unlock(&sev_cmd_mutex);
1089+
1090+
return rc;
1091+
}
1092+
8551093
static int sev_ioctl_do_pek_import(struct sev_issue_cmd *argp, bool writable)
8561094
{
8571095
struct sev_device *sev = psp_master->sev_data;
@@ -1299,6 +1537,8 @@ int sev_dev_init(struct psp_device *psp)
12991537

13001538
static void sev_firmware_shutdown(struct sev_device *sev)
13011539
{
1540+
int error;
1541+
13021542
sev_platform_shutdown(NULL);
13031543

13041544
if (sev_es_tmr) {
@@ -1315,6 +1555,13 @@ static void sev_firmware_shutdown(struct sev_device *sev)
13151555
get_order(NV_LENGTH));
13161556
sev_init_ex_buffer = NULL;
13171557
}
1558+
1559+
if (snp_range_list) {
1560+
kfree(snp_range_list);
1561+
snp_range_list = NULL;
1562+
}
1563+
1564+
sev_snp_shutdown(&error);
13181565
}
13191566

13201567
void sev_dev_destroy(struct psp_device *psp)
@@ -1345,7 +1592,8 @@ EXPORT_SYMBOL_GPL(sev_issue_cmd_external_user);
13451592
void sev_pci_init(void)
13461593
{
13471594
struct sev_device *sev = psp_master->sev_data;
1348-
int error, rc;
1595+
struct sev_platform_init_args args = {0};
1596+
int rc;
13491597

13501598
if (!sev)
13511599
return;
@@ -1370,23 +1618,15 @@ void sev_pci_init(void)
13701618
}
13711619
}
13721620

1373-
/* Obtain the TMR memory area for SEV-ES use */
1374-
sev_es_tmr = sev_fw_alloc(SEV_ES_TMR_SIZE);
1375-
if (sev_es_tmr)
1376-
/* Must flush the cache before giving it to the firmware */
1377-
clflush_cache_range(sev_es_tmr, SEV_ES_TMR_SIZE);
1378-
else
1379-
dev_warn(sev->dev,
1380-
"SEV: TMR allocation failed, SEV-ES support unavailable\n");
1381-
1382-
if (!psp_init_on_probe)
1383-
return;
1384-
13851621
/* Initialize the platform */
1386-
rc = sev_platform_init(&error);
1622+
args.probe = true;
1623+
rc = sev_platform_init(&args);
13871624
if (rc)
13881625
dev_err(sev->dev, "SEV: failed to INIT error %#x, rc %d\n",
1389-
error, rc);
1626+
args.error, rc);
1627+
1628+
dev_info(sev->dev, "SEV%s API:%d.%d build:%d\n", sev->snp_initialized ?
1629+
"-SNP" : "", sev->api_major, sev->api_minor, sev->build);
13901630

13911631
return;
13921632

0 commit comments

Comments
 (0)