@@ -303,9 +303,10 @@ static int enc_payload(struct snp_guest_dev *snp_dev, u64 seqno, int version, u8
static int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code, int msg_ver,
u8 type, void *req_buf, size_t req_sz, void *resp_buf,
- u32 resp_sz, __u64 *fw_err)
+ u32 resp_sz, __u64 *exitinfo2)
{
- unsigned long err;
+ unsigned long _exitinfo2 = 0;
+ unsigned int vmm_err;
u64 seqno;
int rc;
@@ -322,9 +323,20 @@ static int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code, in
return rc;
/* Call firmware to process the request */
- rc = snp_issue_guest_request(exit_code, &snp_dev->input, &err);
- if (fw_err)
- *fw_err = err;
+ rc = snp_issue_guest_request(exit_code, &snp_dev->input, &_exitinfo2);
+ if (exitinfo2)
+ *exitinfo2 = _exitinfo2;
+
+ vmm_err = _exitinfo2 >> SNP_GUEST_VMM_ERR_SHIFT;
+ /* The host may return EBUSY if the request has been throttled. */
+ if (vmm_err == SNP_GUEST_VMM_ERR_BUSY)
+ return -EAGAIN;
+
+ if (vmm_err && vmm_err != SNP_GUEST_VMM_ERR_INVALID_LEN) {
+ pr_err("sev-guest: host returned unknown error code: %d\n",
+ vmm_err);
+ return -EINVAL;
+ }
if (rc)
return rc;
@@ -378,7 +390,7 @@ static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_io
rc = handle_guest_request(snp_dev, SVM_VMGEXIT_GUEST_REQUEST, arg->msg_version,
SNP_MSG_REPORT_REQ, &req, sizeof(req), resp->data,
- resp_len, &arg->fw_err);
+ resp_len, &arg->exitinfo2);
if (rc)
goto e_free;
@@ -418,7 +430,7 @@ static int get_derived_key(struct snp_guest_dev *snp_dev, struct snp_guest_reque
rc = handle_guest_request(snp_dev, SVM_VMGEXIT_GUEST_REQUEST, arg->msg_version,
SNP_MSG_KEY_REQ, &req, sizeof(req), buf, resp_len,
- &arg->fw_err);
+ &arg->exitinfo2);
if (rc)
return rc;
@@ -480,10 +492,10 @@ static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_reques
snp_dev->input.data_npages = npages;
ret = handle_guest_request(snp_dev, SVM_VMGEXIT_EXT_GUEST_REQUEST, arg->msg_version,
SNP_MSG_REPORT_REQ, &req.data,
- sizeof(req.data), resp->data, resp_len, &arg->fw_err);
+ sizeof(req.data), resp->data, resp_len, &arg->exitinfo2);
/* If certs length is invalid then copy the returned length */
- if (arg->fw_err == SNP_GUEST_REQ_INVALID_LEN) {
+ if (arg->vmm_error == SNP_GUEST_VMM_ERR_INVALID_LEN) {
req.certs_len = snp_dev->input.data_npages << PAGE_SHIFT;
if (copy_to_user((void __user *)arg->req_data, &req, sizeof(req)))
@@ -518,7 +530,7 @@ static long snp_guest_ioctl(struct file *file, unsigned int ioctl, unsigned long
if (copy_from_user(&input, argp, sizeof(input)))
return -EFAULT;
- input.fw_err = 0xff;
+ input.exitinfo2 = 0xff;
/* Message version must be non-zero */
if (!input.msg_version)
@@ -549,7 +561,7 @@ static long snp_guest_ioctl(struct file *file, unsigned int ioctl, unsigned long
mutex_unlock(&snp_cmd_mutex);
- if (input.fw_err && copy_to_user(argp, &input, sizeof(input)))
+ if (input.exitinfo2 && copy_to_user(argp, &input, sizeof(input)))
return -EFAULT;
return ret;
@@ -52,8 +52,15 @@ struct snp_guest_request_ioctl {
__u64 req_data;
__u64 resp_data;
- /* firmware error code on failure (see psp-sev.h) */
- __u64 fw_err;
+ /* bits[63:32]: VMM error code, bits[31:0] firmware error code (see psp-sev.h) */
+ union {
+ __u64 exitinfo2;
+ __u64 fw_err; /* Name deprecated in favor of others */
+ struct {
+ __u32 fw_error;
+ __u32 vmm_error;
+ };
+ };
};
struct snp_ext_report_req {
@@ -77,4 +84,9 @@ struct snp_ext_report_req {
/* Get SNP extended report as defined in the GHCB specification version 2. */
#define SNP_GET_EXT_REPORT _IOWR(SNP_GUEST_REQ_IOC_TYPE, 0x2, struct snp_guest_request_ioctl)
+
+#define SNP_GUEST_VMM_ERR_SHIFT 32
+#define SNP_GUEST_VMM_ERR_INVALID_LEN 1
+#define SNP_GUEST_VMM_ERR_BUSY 2
+
#endif /* __UAPI_LINUX_SEV_GUEST_H_ */