Skip to content

Commit 0144e3b

Browse files
deeglazebp3tk0v
authored andcommitted
x86/sev: Change snp_guest_issue_request()'s fw_err argument
The GHCB specification declares that the firmware error value for a guest request will be stored in the lower 32 bits of EXIT_INFO_2. The upper 32 bits are for the VMM's own error code. The fw_err argument to snp_guest_issue_request() is thus a misnomer, and callers will need access to all 64 bits. The type of unsigned long also causes problems, since sw_exit_info2 is u64 (unsigned long long) vs the argument's unsigned long*. Change this type for issuing the guest request. Pass the ioctl command struct's error field directly instead of in a local variable, since an incomplete guest request may not set the error code, and uninitialized stack memory would be written back to user space. The firmware might not even be called, so bookend the call with the no firmware call error and clear the error. Since the "fw_err" field is really exitinfo2 split into the upper bits' vmm error code and lower bits' firmware error code, convert the 64 bit value to a union. [ bp: - Massage commit message - adjust code - Fix a build issue as Reported-by: kernel test robot <lkp@intel.com> Link: https://lore.kernel.org/oe-kbuild-all/202303070609.vX6wp2Af-lkp@intel.com - print exitinfo2 in hex Tom: - Correct -EIO exit case. ] Signed-off-by: Dionna Glaze <dionnaglaze@google.com> Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com> Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de> Link: https://lore.kernel.org/r/20230214164638.1189804-5-dionnaglaze@google.com Link: https://lore.kernel.org/r/20230307192449.24732-12-bp@alien8.de
1 parent 9650061 commit 0144e3b

6 files changed

Lines changed: 83 additions & 56 deletions

File tree

Documentation/virt/coco/sev-guest.rst

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@ along with a description:
3737
the return value. General error numbers (-ENOMEM, -EINVAL)
3838
are not detailed, but errors with specific meanings are.
3939

40-
The guest ioctl should be issued on a file descriptor of the /dev/sev-guest device.
41-
The ioctl accepts struct snp_user_guest_request. The input and output structure is
42-
specified through the req_data and resp_data field respectively. If the ioctl fails
43-
to execute due to a firmware error, then fw_err code will be set. Otherwise, fw_err
44-
will be set to 0x00000000ffffffff, i.e., the lower 32-bits are -1.
40+
The guest ioctl should be issued on a file descriptor of the /dev/sev-guest
41+
device. The ioctl accepts struct snp_user_guest_request. The input and
42+
output structure is specified through the req_data and resp_data field
43+
respectively. If the ioctl fails to execute due to a firmware error, then
44+
the fw_error code will be set, otherwise fw_error will be set to -1.
4545

4646
The firmware checks that the message sequence counter is one greater than
4747
the guests message sequence counter. If guest driver fails to increment message
@@ -57,8 +57,14 @@ counter (e.g. counter overflow), then -EIO will be returned.
5757
__u64 req_data;
5858
__u64 resp_data;
5959

60-
/* firmware error code on failure (see psp-sev.h) */
61-
__u64 fw_err;
60+
/* bits[63:32]: VMM error code, bits[31:0] firmware error code (see psp-sev.h) */
61+
union {
62+
__u64 exitinfo2;
63+
struct {
64+
__u32 fw_error;
65+
__u32 vmm_error;
66+
};
67+
};
6268
};
6369

6470
2.1 SNP_GET_REPORT

arch/x86/include/asm/sev-common.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,6 @@ struct snp_psc_desc {
128128
struct psc_entry entries[VMGEXIT_PSC_MAX_ENTRY];
129129
} __packed;
130130

131-
/* Guest message request error codes */
132-
#define SNP_GUEST_REQ_INVALID_LEN BIT_ULL(32)
133-
#define SNP_GUEST_REQ_ERR_BUSY BIT_ULL(33)
134-
135131
#define GHCB_MSR_TERM_REQ 0x100
136132
#define GHCB_MSR_TERM_REASON_SET_POS 12
137133
#define GHCB_MSR_TERM_REASON_SET_MASK 0xf

arch/x86/include/asm/sev.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#define __ASM_ENCRYPTED_STATE_H
1010

1111
#include <linux/types.h>
12+
#include <linux/sev-guest.h>
13+
1214
#include <asm/insn.h>
1315
#include <asm/sev-common.h>
1416
#include <asm/bootparam.h>
@@ -185,6 +187,9 @@ static inline int pvalidate(unsigned long vaddr, bool rmp_psize, bool validate)
185187

186188
return rc;
187189
}
190+
191+
struct snp_guest_request_ioctl;
192+
188193
void setup_ghcb(void);
189194
void __init early_snp_set_memory_private(unsigned long vaddr, unsigned long paddr,
190195
unsigned int npages);
@@ -196,7 +201,7 @@ void snp_set_memory_private(unsigned long vaddr, unsigned int npages);
196201
void snp_set_wakeup_secondary_cpu(void);
197202
bool snp_init(struct boot_params *bp);
198203
void __init __noreturn snp_abort(void);
199-
int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, unsigned long *fw_err);
204+
int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct snp_guest_request_ioctl *rio);
200205
#else
201206
static inline void sev_es_ist_enter(struct pt_regs *regs) { }
202207
static inline void sev_es_ist_exit(void) { }
@@ -216,8 +221,7 @@ static inline void snp_set_memory_private(unsigned long vaddr, unsigned int npag
216221
static inline void snp_set_wakeup_secondary_cpu(void) { }
217222
static inline bool snp_init(struct boot_params *bp) { return false; }
218223
static inline void snp_abort(void) { }
219-
static inline int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input,
220-
unsigned long *fw_err)
224+
static inline int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct snp_guest_request_ioctl *rio)
221225
{
222226
return -ENOTTY;
223227
}

arch/x86/kernel/sev.c

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#include <linux/efi.h>
2323
#include <linux/platform_device.h>
2424
#include <linux/io.h>
25+
#include <linux/psp-sev.h>
26+
#include <uapi/linux/sev-guest.h>
2527

2628
#include <asm/cpu_entry_area.h>
2729
#include <asm/stacktrace.h>
@@ -2175,16 +2177,15 @@ static int __init init_sev_config(char *str)
21752177
}
21762178
__setup("sev=", init_sev_config);
21772179

2178-
int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, unsigned long *fw_err)
2180+
int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct snp_guest_request_ioctl *rio)
21792181
{
21802182
struct ghcb_state state;
21812183
struct es_em_ctxt ctxt;
21822184
unsigned long flags;
21832185
struct ghcb *ghcb;
21842186
int ret;
21852187

2186-
if (!fw_err)
2187-
return -EINVAL;
2188+
rio->exitinfo2 = SEV_RET_NO_FW_CALL;
21882189

21892190
/*
21902191
* __sev_get_ghcb() needs to run with IRQs disabled because it is using
@@ -2209,16 +2210,16 @@ int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, unsigned
22092210
if (ret)
22102211
goto e_put;
22112212

2212-
*fw_err = ghcb->save.sw_exit_info_2;
2213-
switch (*fw_err) {
2213+
rio->exitinfo2 = ghcb->save.sw_exit_info_2;
2214+
switch (rio->exitinfo2) {
22142215
case 0:
22152216
break;
22162217

2217-
case SNP_GUEST_REQ_ERR_BUSY:
2218+
case SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_BUSY):
22182219
ret = -EAGAIN;
22192220
break;
22202221

2221-
case SNP_GUEST_REQ_INVALID_LEN:
2222+
case SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN):
22222223
/* Number of expected pages are returned in RBX */
22232224
if (exit_code == SVM_VMGEXIT_EXT_GUEST_REQUEST) {
22242225
input->data_npages = ghcb_get_rbx(ghcb);

drivers/virt/coco/sev-guest/sev-guest.c

Lines changed: 39 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -332,11 +332,12 @@ static int enc_payload(struct snp_guest_dev *snp_dev, u64 seqno, int version, u8
332332
return __enc_payload(snp_dev, req, payload, sz);
333333
}
334334

335-
static int __handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code, __u64 *fw_err)
335+
static int __handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
336+
struct snp_guest_request_ioctl *rio)
336337
{
337-
unsigned long err = 0xff, override_err = 0;
338338
unsigned long req_start = jiffies;
339339
unsigned int override_npages = 0;
340+
u64 override_err = 0;
340341
int rc;
341342

342343
retry_request:
@@ -346,7 +347,7 @@ static int __handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
346347
* sequence number must be incremented or the VMPCK must be deleted to
347348
* prevent reuse of the IV.
348349
*/
349-
rc = snp_issue_guest_request(exit_code, &snp_dev->input, &err);
350+
rc = snp_issue_guest_request(exit_code, &snp_dev->input, rio);
350351
switch (rc) {
351352
case -ENOSPC:
352353
/*
@@ -364,7 +365,7 @@ static int __handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
364365
* request buffer size was too small and give the caller the
365366
* required buffer size.
366367
*/
367-
override_err = SNP_GUEST_REQ_INVALID_LEN;
368+
override_err = SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN);
368369

369370
/*
370371
* If this call to the firmware succeeds, the sequence number can
@@ -377,7 +378,7 @@ static int __handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
377378
goto retry_request;
378379

379380
/*
380-
* The host may return SNP_GUEST_REQ_ERR_EBUSY if the request has been
381+
* The host may return SNP_GUEST_VMM_ERR_BUSY if the request has been
381382
* throttled. Retry in the driver to avoid returning and reusing the
382383
* message sequence number on a different message.
383384
*/
@@ -398,27 +399,29 @@ static int __handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
398399
*/
399400
snp_inc_msg_seqno(snp_dev);
400401

401-
if (fw_err)
402-
*fw_err = override_err ?: err;
402+
if (override_err) {
403+
rio->exitinfo2 = override_err;
404+
405+
/*
406+
* If an extended guest request was issued and the supplied certificate
407+
* buffer was not large enough, a standard guest request was issued to
408+
* prevent IV reuse. If the standard request was successful, return -EIO
409+
* back to the caller as would have originally been returned.
410+
*/
411+
if (!rc && override_err == SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN))
412+
rc = -EIO;
413+
}
403414

404415
if (override_npages)
405416
snp_dev->input.data_npages = override_npages;
406417

407-
/*
408-
* If an extended guest request was issued and the supplied certificate
409-
* buffer was not large enough, a standard guest request was issued to
410-
* prevent IV reuse. If the standard request was successful, return -EIO
411-
* back to the caller as would have originally been returned.
412-
*/
413-
if (!rc && override_err == SNP_GUEST_REQ_INVALID_LEN)
414-
return -EIO;
415-
416418
return rc;
417419
}
418420

419-
static int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code, int msg_ver,
420-
u8 type, void *req_buf, size_t req_sz, void *resp_buf,
421-
u32 resp_sz, __u64 *fw_err)
421+
static int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code,
422+
struct snp_guest_request_ioctl *rio, u8 type,
423+
void *req_buf, size_t req_sz, void *resp_buf,
424+
u32 resp_sz)
422425
{
423426
u64 seqno;
424427
int rc;
@@ -432,7 +435,7 @@ static int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code, in
432435
memset(snp_dev->response, 0, sizeof(struct snp_guest_msg));
433436

434437
/* Encrypt the userspace provided payload in snp_dev->secret_request. */
435-
rc = enc_payload(snp_dev, seqno, msg_ver, type, req_buf, req_sz);
438+
rc = enc_payload(snp_dev, seqno, rio->msg_version, type, req_buf, req_sz);
436439
if (rc)
437440
return rc;
438441

@@ -443,12 +446,16 @@ static int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code, in
443446
memcpy(snp_dev->request, &snp_dev->secret_request,
444447
sizeof(snp_dev->secret_request));
445448

446-
rc = __handle_guest_request(snp_dev, exit_code, fw_err);
449+
rc = __handle_guest_request(snp_dev, exit_code, rio);
447450
if (rc) {
448-
if (rc == -EIO && *fw_err == SNP_GUEST_REQ_INVALID_LEN)
451+
if (rc == -EIO &&
452+
rio->exitinfo2 == SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN))
449453
return rc;
450454

451-
dev_alert(snp_dev->dev, "Detected error from ASP request. rc: %d, fw_err: %llu\n", rc, *fw_err);
455+
dev_alert(snp_dev->dev,
456+
"Detected error from ASP request. rc: %d, exitinfo2: 0x%llx\n",
457+
rc, rio->exitinfo2);
458+
452459
snp_disable_vmpck(snp_dev);
453460
return rc;
454461
}
@@ -488,9 +495,9 @@ static int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_io
488495
if (!resp)
489496
return -ENOMEM;
490497

491-
rc = handle_guest_request(snp_dev, SVM_VMGEXIT_GUEST_REQUEST, arg->msg_version,
498+
rc = handle_guest_request(snp_dev, SVM_VMGEXIT_GUEST_REQUEST, arg,
492499
SNP_MSG_REPORT_REQ, &req, sizeof(req), resp->data,
493-
resp_len, &arg->fw_err);
500+
resp_len);
494501
if (rc)
495502
goto e_free;
496503

@@ -528,9 +535,8 @@ static int get_derived_key(struct snp_guest_dev *snp_dev, struct snp_guest_reque
528535
if (copy_from_user(&req, (void __user *)arg->req_data, sizeof(req)))
529536
return -EFAULT;
530537

531-
rc = handle_guest_request(snp_dev, SVM_VMGEXIT_GUEST_REQUEST, arg->msg_version,
532-
SNP_MSG_KEY_REQ, &req, sizeof(req), buf, resp_len,
533-
&arg->fw_err);
538+
rc = handle_guest_request(snp_dev, SVM_VMGEXIT_GUEST_REQUEST, arg,
539+
SNP_MSG_KEY_REQ, &req, sizeof(req), buf, resp_len);
534540
if (rc)
535541
return rc;
536542

@@ -590,12 +596,12 @@ static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_reques
590596
return -ENOMEM;
591597

592598
snp_dev->input.data_npages = npages;
593-
ret = handle_guest_request(snp_dev, SVM_VMGEXIT_EXT_GUEST_REQUEST, arg->msg_version,
599+
ret = handle_guest_request(snp_dev, SVM_VMGEXIT_EXT_GUEST_REQUEST, arg,
594600
SNP_MSG_REPORT_REQ, &req.data,
595-
sizeof(req.data), resp->data, resp_len, &arg->fw_err);
601+
sizeof(req.data), resp->data, resp_len);
596602

597603
/* If certs length is invalid then copy the returned length */
598-
if (arg->fw_err == SNP_GUEST_REQ_INVALID_LEN) {
604+
if (arg->vmm_error == SNP_GUEST_VMM_ERR_INVALID_LEN) {
599605
req.certs_len = snp_dev->input.data_npages << PAGE_SHIFT;
600606

601607
if (copy_to_user((void __user *)arg->req_data, &req, sizeof(req)))
@@ -630,7 +636,7 @@ static long snp_guest_ioctl(struct file *file, unsigned int ioctl, unsigned long
630636
if (copy_from_user(&input, argp, sizeof(input)))
631637
return -EFAULT;
632638

633-
input.fw_err = 0xff;
639+
input.exitinfo2 = 0xff;
634640

635641
/* Message version must be non-zero */
636642
if (!input.msg_version)
@@ -661,7 +667,7 @@ static long snp_guest_ioctl(struct file *file, unsigned int ioctl, unsigned long
661667

662668
mutex_unlock(&snp_cmd_mutex);
663669

664-
if (input.fw_err && copy_to_user(argp, &input, sizeof(input)))
670+
if (input.exitinfo2 && copy_to_user(argp, &input, sizeof(input)))
665671
return -EFAULT;
666672

667673
return ret;

include/uapi/linux/sev-guest.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,14 @@ struct snp_guest_request_ioctl {
5252
__u64 req_data;
5353
__u64 resp_data;
5454

55-
/* firmware error code on failure (see psp-sev.h) */
56-
__u64 fw_err;
55+
/* bits[63:32]: VMM error code, bits[31:0] firmware error code (see psp-sev.h) */
56+
union {
57+
__u64 exitinfo2;
58+
struct {
59+
__u32 fw_error;
60+
__u32 vmm_error;
61+
};
62+
};
5763
};
5864

5965
struct snp_ext_report_req {
@@ -77,4 +83,12 @@ struct snp_ext_report_req {
7783
/* Get SNP extended report as defined in the GHCB specification version 2. */
7884
#define SNP_GET_EXT_REPORT _IOWR(SNP_GUEST_REQ_IOC_TYPE, 0x2, struct snp_guest_request_ioctl)
7985

86+
/* Guest message request EXIT_INFO_2 constants */
87+
#define SNP_GUEST_FW_ERR_MASK GENMASK_ULL(31, 0)
88+
#define SNP_GUEST_VMM_ERR_SHIFT 32
89+
#define SNP_GUEST_VMM_ERR(x) (((u64)x) << SNP_GUEST_VMM_ERR_SHIFT)
90+
91+
#define SNP_GUEST_VMM_ERR_INVALID_LEN 1
92+
#define SNP_GUEST_VMM_ERR_BUSY 2
93+
8094
#endif /* __UAPI_LINUX_SEV_GUEST_H_ */

0 commit comments

Comments
 (0)