mbox series

[v4,0/7] Move initializing SEV/SNP functionality to KVM

Message ID cover.1739997129.git.ashish.kalra@amd.com
Headers show
Series Move initializing SEV/SNP functionality to KVM | expand

Message

Ashish Kalra Feb. 19, 2025, 8:52 p.m. UTC
From: Ashish Kalra <ashish.kalra@amd.com>

Remove initializing SEV/SNP functionality from PSP driver and instead add
support to KVM to explicitly initialize the PSP if KVM wants to use
SEV/SNP functionality.

This removes SEV/SNP initialization at PSP module probe time and does
on-demand SEV/SNP initialization when KVM really wants to use 
SEV/SNP functionality. This will allow running legacy non-confidential
VMs without initializating SEV functionality. 

This will assist in adding SNP CipherTextHiding support and SEV firmware
hotloading support in KVM without sharing SEV ASID management and SNP
guest context support between PSP driver and KVM and keeping all that
support only in KVM.

To support SEV firmware hotloading, SEV Shutdown will be done explicitly
prior to DOWNLOAD_FIRMWARE_EX and SEV INIT post it to work with the
requirement of SEV to be in UNINIT state for DOWNLOAD_FIRMWARE_EX.
NOTE: SEV firmware hotloading will only be supported if there are no
active SEV/SEV-ES guests. 

v4:
- Rebase on linux-next which has the fix for SNP broken with kvm_amd
module built-in.
- Fix commit logs.
- Add explicit SEV/SNP initialization and shutdown error logs instead
of using a common exit point.
- Move SEV/SNP shutdown error logs from callers into __sev_platform_shutdown_locked()
and __sev_snp_shutdown_locked().
- Make sure that we continue to support both the probe field and psp_init_on_probe
module parameter for PSP module to support SEV INIT_EX.
- Add reviewed-by's.

v3:
- Move back to do both SNP and SEV platform initialization at KVM module
load time instead of SEV initialization on demand at SEV/SEV-ES VM launch
to prevent breaking QEMU which has a check for SEV to be initialized 
prior to launching SEV/SEV-ES VMs. 
- As both SNP and SEV platform initialization and shutdown is now done at
KVM module load and unload time remove patches for separate SEV and SNP
platform initialization and shutdown.

v2:
- Added support for separate SEV and SNP platform initalization, while
SNP platform initialization is done at KVM module load time, SEV 
platform initialization is done on demand at SEV/SEV-ES VM launch.
- Added support for separate SEV and SNP platform shutdown, both 
SEV and SNP shutdown done at KVM module unload time, only SEV
shutdown down when all SEV/SEV-ES VMs have been destroyed, this
allows SEV firmware hotloading support anytime during system lifetime.
- Updated commit messages for couple of patches in the series with
reference to the feedback received on v1 patches.

Ashish Kalra (7):
  crypto: ccp: Move dev_info/err messages for SEV/SNP init and shutdown
  crypto: ccp: Ensure implicit SEV/SNP init and shutdown in ioctls
  crypto: ccp: Reset TMR size at SNP Shutdown
  crypto: ccp: Register SNP panic notifier only if SNP is enabled
  crypto: ccp: Add new SEV/SNP platform shutdown API
  KVM: SVM: Add support to initialize SEV/SNP functionality in KVM
  crypto: ccp: Move SEV/SNP Platform initialization to KVM

 arch/x86/kvm/svm/sev.c       |  15 +++
 drivers/crypto/ccp/sev-dev.c | 219 ++++++++++++++++++++++++-----------
 include/linux/psp-sev.h      |   3 +
 3 files changed, 171 insertions(+), 66 deletions(-)

Comments

Dionna Amalie Glaze Feb. 20, 2025, 4:47 p.m. UTC | #1
On Wed, Feb 19, 2025 at 12:53 PM Ashish Kalra <Ashish.Kalra@amd.com> wrote:
>
> From: Ashish Kalra <ashish.kalra@amd.com>
>
> When SEV-SNP is enabled the TMR needs to be 2MB aligned and 2MB sized,
> ensure that TMR size is reset back to default when SNP is shutdown as
> SNP initialization and shutdown as part of some SNP ioctls may leave
> TMR size modified and cause subsequent SEV only initialization to fail.
>
> Signed-off-by: Ashish Kalra <ashish.kalra@amd.com>

Acked-by: Dionna Glaze <dionnaglaze@google.com>

> ---
>  drivers/crypto/ccp/sev-dev.c | 3 +++
>  1 file changed, 3 insertions(+)
>
> diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c
> index b06f43eb18f7..be8a84ce24c7 100644
> --- a/drivers/crypto/ccp/sev-dev.c
> +++ b/drivers/crypto/ccp/sev-dev.c
> @@ -1751,6 +1751,9 @@ static int __sev_snp_shutdown_locked(int *error, bool panic)
>         sev->snp_initialized = false;
>         dev_dbg(sev->dev, "SEV-SNP firmware shutdown\n");
>
> +       /* Reset TMR size back to default */
> +       sev_es_tmr_size = SEV_TMR_SIZE;
> +
>         return ret;
>  }
>
> --
> 2.34.1
>
Tom Lendacky Feb. 20, 2025, 6:27 p.m. UTC | #2
On 2/19/25 14:52, Ashish Kalra wrote:
> From: Ashish Kalra <ashish.kalra@amd.com>
> 
> Move dev_info and dev_err messages related to SEV/SNP initialization
> and shutdown into __sev_platform_init_locked(), __sev_snp_init_locked()
> and __sev_platform_shutdown_locked(), __sev_snp_shutdown_locked() so
> that they don't need to be issued from callers.
> 
> This allows both _sev_platform_init_locked() and various SEV/SNP ioctls
> to call __sev_platform_init_locked(), __sev_snp_init_locked() and
> __sev_platform_shutdown_locked(), __sev_snp_shutdown_locked() for
> implicit SEV/SNP initialization and shutdown without additionally
> printing any errors/success messages.
> 
> Signed-off-by: Ashish Kalra <ashish.kalra@amd.com>
> ---
>  drivers/crypto/ccp/sev-dev.c | 39 +++++++++++++++++++++++++++---------
>  1 file changed, 30 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c
> index 2e87ca0e292a..8f5c474b9d1c 100644
> --- a/drivers/crypto/ccp/sev-dev.c
> +++ b/drivers/crypto/ccp/sev-dev.c
> @@ -1176,21 +1176,30 @@ static int __sev_snp_init_locked(int *error)
>  	wbinvd_on_all_cpus();
>  
>  	rc = __sev_do_cmd_locked(cmd, arg, error);
> -	if (rc)
> +	if (rc) {
> +		dev_err(sev->dev, "SEV-SNP: failed to INIT rc %d, error %#x\n",
> +			rc, *error);

How about doing:

	dev_err(sev->dev, "SEV-SNP: %s failed rc %d, error %#x\n",
		cmd == SEV_CMD_SNP_INIT_EX ? "SNP_INIT_EX" : "SNP_INIT",
		rc, *error);

>  		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)
> +	if (rc) {
> +		dev_err(sev->dev, "SEV-SNP: SNP_DF_FLUSH failed rc %d, error %#x\n",
> +			rc, *error);
>  		return rc;
> +	}
>  
>  	sev->snp_initialized = true;
>  	dev_dbg(sev->dev, "SEV-SNP firmware initialized\n");
>  
> +	dev_info(sev->dev, "SEV-SNP API:%d.%d build:%d\n", sev->api_major,
> +		 sev->api_minor, sev->build);
> +
>  	sev_es_tmr_size = SNP_TMR_SIZE;
>  
> -	return rc;
> +	return 0;
>  }
>  
>  static void __sev_platform_init_handle_tmr(struct sev_device *sev)
> @@ -1267,8 +1276,10 @@ static int __sev_platform_init_locked(int *error)
>  	__sev_platform_init_handle_tmr(sev);
>  
>  	rc = __sev_platform_init_handle_init_ex_path(sev);
> -	if (rc)
> +	if (rc) {
> +		dev_err(sev->dev, "SEV: handle_init_ex_path failed, rc %d\n", rc);
>  		return rc;
> +	}

Messages should be issued in __sev_platform_init_handle_init_ex_path().
The only non-zero rc value that doesn't cause a message would come from
sev_read_init_ex_file() when sev_init_ex_buffer is NULL, but
sev_read_init_ex_file() isn't called if the allocation for that buffer
fails. So I don't think this message is necessary. But double-check me
on that.

>  
>  	rc = __sev_do_init_locked(&psp_ret);
>  	if (rc && psp_ret == SEV_RET_SECURE_DATA_INVALID) {
> @@ -1287,16 +1298,22 @@ static int __sev_platform_init_locked(int *error)
>  	if (error)
>  		*error = psp_ret;
>  
> -	if (rc)
> +	if (rc) {
> +		dev_err(sev->dev, "SEV: failed to INIT error %#x, rc %d\n",
> +			psp_ret, rc);

Similar to the SNP INIT comment above, how about:

	dev_err(sev->dev, "SEV: %s failed %#x, rc %d\n",
		sev_init_ex_buffer ? "INIT_EX" : "INIT", psp_ret, rc);

>  		return rc;
> +	}
>  
>  	sev->state = SEV_STATE_INIT;
>  
>  	/* Prepare for first SEV guest launch after INIT */
>  	wbinvd_on_all_cpus();
>  	rc = __sev_do_cmd_locked(SEV_CMD_DF_FLUSH, NULL, error);
> -	if (rc)
> +	if (rc) {
> +		dev_err(sev->dev, "SEV: DF_FLUSH failed %#x, rc %d\n",
> +			*error, rc);
>  		return rc;
> +	}
>  
>  	dev_dbg(sev->dev, "SEV firmware initialized\n");
>  
> @@ -1367,8 +1384,11 @@ static int __sev_platform_shutdown_locked(int *error)
>  		return 0;
>  
>  	ret = __sev_do_cmd_locked(SEV_CMD_SHUTDOWN, NULL, error);
> -	if (ret)
> +	if (ret) {
> +		dev_err(sev->dev, "SEV: failed to SHUTDOWN error %#x, rc %d\n",
> +			*error, ret);
>  		return ret;
> +	}
>  
>  	sev->state = SEV_STATE_UNINIT;
>  	dev_dbg(sev->dev, "SEV firmware shutdown\n");
> @@ -1684,7 +1704,7 @@ static int __sev_snp_shutdown_locked(int *error, bool panic)
>  	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");
> +			dev_err(sev->dev, "SEV-SNP DF_FLUSH failed, ret = %d\n", ret);

Should provide as much info as possible, so create a local int variable
that you can pass into __sev_do_cmd_locked() and output that in the
failure message.

(I should go through this file later and make all the message formats
consistent.)

Thanks,
Tom

>  			return ret;
>  		}
>  		/* reissue the shutdown command */
> @@ -1692,7 +1712,8 @@ static int __sev_snp_shutdown_locked(int *error, bool panic)
>  					  error);
>  	}
>  	if (ret) {
> -		dev_err(sev->dev, "SEV-SNP firmware shutdown failed\n");
> +		dev_err(sev->dev, "SEV-SNP firmware shutdown failed, rc %d, error %#x\n",
> +			ret, *error);
>  		return ret;
>  	}
>
Tom Lendacky Feb. 20, 2025, 7:21 p.m. UTC | #3
On 2/19/25 14:54, Ashish Kalra wrote:
> From: Ashish Kalra <ashish.kalra@amd.com>
> 
> Add new API interface to do SEV/SNP platform shutdown when KVM module
> is unloaded.

Just a nit below if you have to respin. Otherwise:

Reviewed-by: Tom Lendacky <thomas.lendacky@amd.com>

> 
> Reviewed-by: Dionna Glaze <dionnaglaze@google.com>
> Signed-off-by: Ashish Kalra <ashish.kalra@amd.com>
> ---
>  drivers/crypto/ccp/sev-dev.c | 13 +++++++++++++
>  include/linux/psp-sev.h      |  3 +++
>  2 files changed, 16 insertions(+)
> 
> diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c
> index 582304638319..f0f3e6d29200 100644
> --- a/drivers/crypto/ccp/sev-dev.c
> +++ b/drivers/crypto/ccp/sev-dev.c
> @@ -2445,6 +2445,19 @@ static void sev_firmware_shutdown(struct sev_device *sev)
>  	mutex_unlock(&sev_cmd_mutex);
>  }
>  
> +void sev_platform_shutdown(void)
> +{
> +	struct sev_device *sev;
> +
> +	if (!psp_master || !psp_master->sev_data)
> +		return;
> +
> +	sev = psp_master->sev_data;
> +
> +	sev_firmware_shutdown(sev);

	sev_firmware_shutdown(psp->master->sev_data);

and then you can get rid of the sev variable.

Thanks,
Tom

> +}
> +EXPORT_SYMBOL_GPL(sev_platform_shutdown);
> +
>  void sev_dev_destroy(struct psp_device *psp)
>  {
>  	struct sev_device *sev = psp->sev_data;
> diff --git a/include/linux/psp-sev.h b/include/linux/psp-sev.h
> index f3cad182d4ef..0b3a36bdaa90 100644
> --- a/include/linux/psp-sev.h
> +++ b/include/linux/psp-sev.h
> @@ -954,6 +954,7 @@ int sev_do_cmd(int cmd, void *data, int *psp_ret);
>  void *psp_copy_user_blob(u64 uaddr, u32 len);
>  void *snp_alloc_firmware_page(gfp_t mask);
>  void snp_free_firmware_page(void *addr);
> +void sev_platform_shutdown(void);
>  
>  #else	/* !CONFIG_CRYPTO_DEV_SP_PSP */
>  
> @@ -988,6 +989,8 @@ static inline void *snp_alloc_firmware_page(gfp_t mask)
>  
>  static inline void snp_free_firmware_page(void *addr) { }
>  
> +static inline void sev_platform_shutdown(void) { }
> +
>  #endif	/* CONFIG_CRYPTO_DEV_SP_PSP */
>  
>  #endif	/* __PSP_SEV_H__ */
Tom Lendacky Feb. 20, 2025, 8:03 p.m. UTC | #4
On 2/19/25 14:55, Ashish Kalra wrote:
> From: Ashish Kalra <ashish.kalra@amd.com>
> 
> SNP initialization is forced during PSP driver probe purely because SNP
> can't be initialized if VMs are running.  But the only in-tree user of
> SEV/SNP functionality is KVM, and KVM depends on PSP driver for the same.
> Forcing SEV/SNP initialization because a hypervisor could be running
> legacy non-confidential VMs make no sense.
> 
> This patch removes SEV/SNP initialization from the PSP driver probe
> time and moves the requirement to initialize SEV/SNP functionality
> to KVM if it wants to use SEV/SNP.
> 
> Suggested-by: Sean Christopherson <seanjc@google.com>
> Reviewed-by: Alexey Kardashevskiy <aik@amd.com>
> Signed-off-by: Ashish Kalra <ashish.kalra@amd.com>
> ---
>  drivers/crypto/ccp/sev-dev.c | 25 +------------------------
>  1 file changed, 1 insertion(+), 24 deletions(-)
> 
> diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c
> index f0f3e6d29200..99a663dbc2b6 100644
> --- a/drivers/crypto/ccp/sev-dev.c
> +++ b/drivers/crypto/ccp/sev-dev.c
> @@ -1346,18 +1346,13 @@ static int _sev_platform_init_locked(struct sev_platform_init_args *args)
>  	if (sev->state == SEV_STATE_INIT)
>  		return 0;
>  
> -	/*
> -	 * Legacy guests cannot be running while SNP_INIT(_EX) is executing,
> -	 * so perform SEV-SNP initialization at probe time.
> -	 */
>  	rc = __sev_snp_init_locked(&args->error);
>  	if (rc && rc != -ENODEV) {
>  		/*
>  		 * 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 rc %d, error %#x\n",
> -			rc, args->error);
> +		dev_err(sev->dev, "SEV-SNP: failed to INIT, continue SEV INIT\n");

Please don't remove the error information.

>  	}
>  
>  	/* Defer legacy SEV/SEV-ES support if allowed by caller/module. */
> @@ -2505,9 +2500,7 @@ EXPORT_SYMBOL_GPL(sev_issue_cmd_external_user);
>  void sev_pci_init(void)
>  {
>  	struct sev_device *sev = psp_master->sev_data;
> -	struct sev_platform_init_args args = {0};
>  	u8 api_major, api_minor, build;
> -	int rc;
>  
>  	if (!sev)
>  		return;
> @@ -2530,16 +2523,6 @@ void sev_pci_init(void)
>  			 api_major, api_minor, build,
>  			 sev->api_major, sev->api_minor, sev->build);
>  
> -	/* Initialize the platform */
> -	args.probe = true;
> -	rc = sev_platform_init(&args);
> -	if (rc)
> -		dev_err(sev->dev, "SEV: failed to INIT error %#x, rc %d\n",
> -			args.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:
> @@ -2550,10 +2533,4 @@ void sev_pci_init(void)
>  
>  void sev_pci_exit(void)
>  {
> -	struct sev_device *sev = psp_master->sev_data;
> -
> -	if (!sev)
> -		return;
> -
> -	sev_firmware_shutdown(sev);

Should this remain? If there's a bug in KVM that somehow skips the
shutdown call, then SEV will remain initialized. I think the path is
safe to call a second time.

Thanks,
Tom


>  }
Ashish Kalra Feb. 20, 2025, 8:23 p.m. UTC | #5
Hello Tom,

On 2/20/2025 2:03 PM, Tom Lendacky wrote:
> On 2/19/25 14:55, Ashish Kalra wrote:
>> From: Ashish Kalra <ashish.kalra@amd.com>
>>
>> SNP initialization is forced during PSP driver probe purely because SNP
>> can't be initialized if VMs are running.  But the only in-tree user of
>> SEV/SNP functionality is KVM, and KVM depends on PSP driver for the same.
>> Forcing SEV/SNP initialization because a hypervisor could be running
>> legacy non-confidential VMs make no sense.
>>
>> This patch removes SEV/SNP initialization from the PSP driver probe
>> time and moves the requirement to initialize SEV/SNP functionality
>> to KVM if it wants to use SEV/SNP.
>>
>> Suggested-by: Sean Christopherson <seanjc@google.com>
>> Reviewed-by: Alexey Kardashevskiy <aik@amd.com>
>> Signed-off-by: Ashish Kalra <ashish.kalra@amd.com>
>> ---
>>  drivers/crypto/ccp/sev-dev.c | 25 +------------------------
>>  1 file changed, 1 insertion(+), 24 deletions(-)
>>
>> diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c
>> index f0f3e6d29200..99a663dbc2b6 100644
>> --- a/drivers/crypto/ccp/sev-dev.c
>> +++ b/drivers/crypto/ccp/sev-dev.c
>> @@ -1346,18 +1346,13 @@ static int _sev_platform_init_locked(struct sev_platform_init_args *args)
>>  	if (sev->state == SEV_STATE_INIT)
>>  		return 0;
>>  
>> -	/*
>> -	 * Legacy guests cannot be running while SNP_INIT(_EX) is executing,
>> -	 * so perform SEV-SNP initialization at probe time.
>> -	 */
>>  	rc = __sev_snp_init_locked(&args->error);
>>  	if (rc && rc != -ENODEV) {
>>  		/*
>>  		 * 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 rc %d, error %#x\n",
>> -			rc, args->error);
>> +		dev_err(sev->dev, "SEV-SNP: failed to INIT, continue SEV INIT\n");
> 
> Please don't remove the error information.
> 

The error(s) are already being printed in __sev_snp_init_locked() otherwise the same
error will be printed twice, hence removing it here.

>>  	}
>>  
>>  	/* Defer legacy SEV/SEV-ES support if allowed by caller/module. */
>> @@ -2505,9 +2500,7 @@ EXPORT_SYMBOL_GPL(sev_issue_cmd_external_user);
>>  void sev_pci_init(void)
>>  {
>>  	struct sev_device *sev = psp_master->sev_data;
>> -	struct sev_platform_init_args args = {0};
>>  	u8 api_major, api_minor, build;
>> -	int rc;
>>  
>>  	if (!sev)
>>  		return;
>> @@ -2530,16 +2523,6 @@ void sev_pci_init(void)
>>  			 api_major, api_minor, build,
>>  			 sev->api_major, sev->api_minor, sev->build);
>>  
>> -	/* Initialize the platform */
>> -	args.probe = true;
>> -	rc = sev_platform_init(&args);
>> -	if (rc)
>> -		dev_err(sev->dev, "SEV: failed to INIT error %#x, rc %d\n",
>> -			args.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:
>> @@ -2550,10 +2533,4 @@ void sev_pci_init(void)
>>  
>>  void sev_pci_exit(void)
>>  {
>> -	struct sev_device *sev = psp_master->sev_data;
>> -
>> -	if (!sev)
>> -		return;
>> -
>> -	sev_firmware_shutdown(sev);
> 
> Should this remain? If there's a bug in KVM that somehow skips the
> shutdown call, then SEV will remain initialized. I think the path is
> safe to call a second time.

Ok.

Thanks,
Ashish