When SVM VM is up, KVM uses sev_issue_cmd_external_user() with an open
/dev/sev fd which ensures that the SVM initialization was done
correctly.
The only helper not following the scheme is
snp_guest_ext_guest_request()
which bypasses the fd check.
Change the SEV API to require passing a file.
Handle errors with care in the SNP Extended Guest Request handler
(snp_handle_ext_guest_request()) as there are actually 3 types of
errors:
- @rc: return code SEV device's sev_issue_cmd() which is int==int32;
- @err: a psp return code in sev_issue_cmd(), also int==int32 (probably
a mistake but kvm_sev_cmd::error uses __u32 for some time now);
- (added by this) @exitcode: GHCB's exit code sw_exit_info_2, uint64.
Use the right types, remove cast to int* and return ENOSPC from SEV
device for converting it to the GHCB's exit code
SNP_GUEST_REQ_INVALID_LEN==BIT(32).
Fixes: 17f1d0c995ac ("KVM: SVM: Provide support for SNP_GUEST_REQUEST
NAE event")
While at this, preserve the original error in snp_cleanup_guest_buf().
Signed-off-by: Alexey Kardashevskiy <aik@xxxxxxx>
---
This can easily be squashed into what it fixes.
The patch is made for
https://github.com/AMDESE/linux/commits/upmv10-host-snp-v7-rfc
---
include/linux/psp-sev.h | 62 +++++++++++---------
arch/x86/kvm/svm/sev.c | 50 +++++++++++-----
drivers/crypto/ccp/sev-dev.c | 11 ++--
3 files changed, 73 insertions(+), 50 deletions(-)
diff --git a/include/linux/psp-sev.h b/include/linux/psp-sev.h
index 970a9de0ed20..466b1a6e7d7b 100644
--- a/include/linux/psp-sev.h
+++ b/include/linux/psp-sev.h
@@ -848,6 +848,36 @@ int sev_platform_status(struct
sev_user_data_status *status, int *error);
int sev_issue_cmd_external_user(struct file *filep, unsigned int id,
void *data, int *error);
+/**
+ * sev_issue_cmd_external_user_cert - issue SEV command by other
driver with a file
+ * handle and return certificates set onto SEV device via
SNP_SET_EXT_CONFIG;
+ * intended for use by the SNP extended guest request command defined
+ * in the GHCB specification.
+ *
+ * @filep - SEV device file pointer
+ * @cmd - command to issue
+ * @data - command buffer
+ * @vaddr: address where the certificate blob need to be copied.
+ * @npages: number of pages for the certificate blob.
+ * If the specified page count is less than the certificate blob
size, then the
+ * required page count is returned with ENOSPC error code.
+ * If the specified page count is more than the certificate blob
size, then
+ * page count is updated to reflect the amount of valid data
copied in the
+ * vaddr.
+ *
+ * @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
+ * -%ENOSPC if the specified page count is too small
+ */
+int sev_issue_cmd_external_user_cert(struct file *filep, unsigned
int cmd, void *data,
+ unsigned long vaddr, unsigned long *npages, int
*error);
+
/**
* sev_guest_deactivate - perform SEV DEACTIVATE command
*
@@ -945,32 +975,6 @@ void snp_free_firmware_page(void *addr);
*/
void snp_mark_pages_offline(unsigned long pfn, unsigned int npages);
-/**
- * snp_guest_ext_guest_request - perform the SNP extended guest
request command
- * defined in the GHCB specification.
- *
- * @data: the input guest request structure
- * @vaddr: address where the certificate blob need to be copied.
- * @npages: number of pages for the certificate blob.
- * If the specified page count is less than the certificate blob
size, then the
- * required page count is returned with error code defined in the
GHCB spec.
- * If the specified page count is more than the certificate blob
size, then
- * page count is updated to reflect the amount of valid data
copied in the
- * vaddr.
- *
- * @sev_ret: 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 snp_guest_ext_guest_request(struct sev_data_snp_guest_request
*data,
- unsigned long vaddr, unsigned long *npages,
- unsigned long *error);
-
#else /* !CONFIG_CRYPTO_DEV_SP_PSP */
static inline int
@@ -1013,9 +1017,9 @@ static inline void
*snp_alloc_firmware_page(gfp_t mask)
static inline void snp_free_firmware_page(void *addr) { }
-static inline int snp_guest_ext_guest_request(struct
sev_data_snp_guest_request *data,
- unsigned long vaddr, unsigned long *n,
- unsigned long *error)
+static inline int sev_issue_cmd_external_user_cert(struct file
*filep, unsigned int cmd,
+ void *data, unsigned long vaddr,
+ unsigned long *npages, int *error)
{
return -ENODEV;
}
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index d0e58cffd1ed..b268c35efab4 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -394,6 +394,23 @@ static int sev_issue_cmd(struct kvm *kvm, int
id, void *data, int *error)
return __sev_issue_cmd(sev->fd, id, data, error);
}
+static int sev_issue_cmd_cert(struct kvm *kvm, int id, void *data,
+ unsigned long vaddr, unsigned long *npages, int
*error)
+{
+ struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
+ struct fd f;
+ int ret;
+
+ f = fdget(sev->fd);
+ if (!f.file)
+ return -EBADF;
+
+ ret = sev_issue_cmd_external_user_cert(f.file, id, data, vaddr,
npages, error);
+
+ fdput(f);
+ return ret;
+}
+
static int sev_launch_start(struct kvm *kvm, struct kvm_sev_cmd *argp)
{
struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
@@ -3587,11 +3604,11 @@ static void snp_cleanup_guest_buf(struct
sev_data_snp_guest_request *data, unsig
int ret;
ret = snp_page_reclaim(pfn);
- if (ret)
+ if (ret && (*rc == SEV_RET_SUCCESS))
*rc = SEV_RET_INVALID_ADDRESS;
ret = rmp_make_shared(pfn, PG_LEVEL_4K);
- if (ret)
+ if (ret && (*rc == SEV_RET_SUCCESS))
*rc = SEV_RET_INVALID_ADDRESS;
}