Skip to content

Commit 1e0b23b

Browse files
nikunjadbp3tk0v
authored andcommitted
x86/sev: Relocate SNP guest messaging routines to common code
At present, the SEV guest driver exclusively handles SNP guest messaging. All routines for sending guest messages are embedded within it. To support Secure TSC, SEV-SNP guests must communicate with the AMD Security Processor during early boot. However, these guest messaging functions are not accessible during early boot since they are currently part of the guest driver. Hence, relocate the core SNP guest messaging functions to SEV common code and provide an API for sending SNP guest messages. No functional change, but just an export symbol added for snp_send_guest_request() and dropped the export symbol on snp_issue_guest_request() and made it static. Signed-off-by: Nikunj A Dadhania <[email protected]> Signed-off-by: Borislav Petkov (AMD) <[email protected]> Reviewed-by: Tom Lendacky <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent c552941 commit 1e0b23b

File tree

3 files changed

+298
-302
lines changed

3 files changed

+298
-302
lines changed

arch/x86/coco/sev/core.c

Lines changed: 291 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2509,8 +2509,8 @@ int snp_issue_svsm_attest_req(u64 call_id, struct svsm_call *call,
25092509
}
25102510
EXPORT_SYMBOL_GPL(snp_issue_svsm_attest_req);
25112511

2512-
int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_data *input,
2513-
struct snp_guest_request_ioctl *rio)
2512+
static int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_data *input,
2513+
struct snp_guest_request_ioctl *rio)
25142514
{
25152515
struct ghcb_state state;
25162516
struct es_em_ctxt ctxt;
@@ -2572,7 +2572,6 @@ int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_data *inpu
25722572

25732573
return ret;
25742574
}
2575-
EXPORT_SYMBOL_GPL(snp_issue_guest_request);
25762575

25772576
static struct platform_device sev_guest_device = {
25782577
.name = "sev-guest",
@@ -2838,3 +2837,292 @@ void snp_msg_free(struct snp_msg_desc *mdesc)
28382837
kfree(mdesc);
28392838
}
28402839
EXPORT_SYMBOL_GPL(snp_msg_free);
2840+
2841+
/* Mutex to serialize the shared buffer access and command handling. */
2842+
static DEFINE_MUTEX(snp_cmd_mutex);
2843+
2844+
/*
2845+
* If an error is received from the host or AMD Secure Processor (ASP) there
2846+
* are two options. Either retry the exact same encrypted request or discontinue
2847+
* using the VMPCK.
2848+
*
2849+
* This is because in the current encryption scheme GHCB v2 uses AES-GCM to
2850+
* encrypt the requests. The IV for this scheme is the sequence number. GCM
2851+
* cannot tolerate IV reuse.
2852+
*
2853+
* The ASP FW v1.51 only increments the sequence numbers on a successful
2854+
* guest<->ASP back and forth and only accepts messages at its exact sequence
2855+
* number.
2856+
*
2857+
* So if the sequence number were to be reused the encryption scheme is
2858+
* vulnerable. If the sequence number were incremented for a fresh IV the ASP
2859+
* will reject the request.
2860+
*/
2861+
static void snp_disable_vmpck(struct snp_msg_desc *mdesc)
2862+
{
2863+
pr_alert("Disabling VMPCK%d communication key to prevent IV reuse.\n",
2864+
mdesc->vmpck_id);
2865+
memzero_explicit(mdesc->vmpck, VMPCK_KEY_LEN);
2866+
mdesc->vmpck = NULL;
2867+
}
2868+
2869+
static inline u64 __snp_get_msg_seqno(struct snp_msg_desc *mdesc)
2870+
{
2871+
u64 count;
2872+
2873+
lockdep_assert_held(&snp_cmd_mutex);
2874+
2875+
/* Read the current message sequence counter from secrets pages */
2876+
count = *mdesc->os_area_msg_seqno;
2877+
2878+
return count + 1;
2879+
}
2880+
2881+
/* Return a non-zero on success */
2882+
static u64 snp_get_msg_seqno(struct snp_msg_desc *mdesc)
2883+
{
2884+
u64 count = __snp_get_msg_seqno(mdesc);
2885+
2886+
/*
2887+
* The message sequence counter for the SNP guest request is a 64-bit
2888+
* value but the version 2 of GHCB specification defines a 32-bit storage
2889+
* for it. If the counter exceeds the 32-bit value then return zero.
2890+
* The caller should check the return value, but if the caller happens to
2891+
* not check the value and use it, then the firmware treats zero as an
2892+
* invalid number and will fail the message request.
2893+
*/
2894+
if (count >= UINT_MAX) {
2895+
pr_err("request message sequence counter overflow\n");
2896+
return 0;
2897+
}
2898+
2899+
return count;
2900+
}
2901+
2902+
static void snp_inc_msg_seqno(struct snp_msg_desc *mdesc)
2903+
{
2904+
/*
2905+
* The counter is also incremented by the PSP, so increment it by 2
2906+
* and save in secrets page.
2907+
*/
2908+
*mdesc->os_area_msg_seqno += 2;
2909+
}
2910+
2911+
static int verify_and_dec_payload(struct snp_msg_desc *mdesc, struct snp_guest_req *req)
2912+
{
2913+
struct snp_guest_msg *resp_msg = &mdesc->secret_response;
2914+
struct snp_guest_msg *req_msg = &mdesc->secret_request;
2915+
struct snp_guest_msg_hdr *req_msg_hdr = &req_msg->hdr;
2916+
struct snp_guest_msg_hdr *resp_msg_hdr = &resp_msg->hdr;
2917+
struct aesgcm_ctx *ctx = mdesc->ctx;
2918+
u8 iv[GCM_AES_IV_SIZE] = {};
2919+
2920+
pr_debug("response [seqno %lld type %d version %d sz %d]\n",
2921+
resp_msg_hdr->msg_seqno, resp_msg_hdr->msg_type, resp_msg_hdr->msg_version,
2922+
resp_msg_hdr->msg_sz);
2923+
2924+
/* Copy response from shared memory to encrypted memory. */
2925+
memcpy(resp_msg, mdesc->response, sizeof(*resp_msg));
2926+
2927+
/* Verify that the sequence counter is incremented by 1 */
2928+
if (unlikely(resp_msg_hdr->msg_seqno != (req_msg_hdr->msg_seqno + 1)))
2929+
return -EBADMSG;
2930+
2931+
/* Verify response message type and version number. */
2932+
if (resp_msg_hdr->msg_type != (req_msg_hdr->msg_type + 1) ||
2933+
resp_msg_hdr->msg_version != req_msg_hdr->msg_version)
2934+
return -EBADMSG;
2935+
2936+
/*
2937+
* If the message size is greater than our buffer length then return
2938+
* an error.
2939+
*/
2940+
if (unlikely((resp_msg_hdr->msg_sz + ctx->authsize) > req->resp_sz))
2941+
return -EBADMSG;
2942+
2943+
/* Decrypt the payload */
2944+
memcpy(iv, &resp_msg_hdr->msg_seqno, min(sizeof(iv), sizeof(resp_msg_hdr->msg_seqno)));
2945+
if (!aesgcm_decrypt(ctx, req->resp_buf, resp_msg->payload, resp_msg_hdr->msg_sz,
2946+
&resp_msg_hdr->algo, AAD_LEN, iv, resp_msg_hdr->authtag))
2947+
return -EBADMSG;
2948+
2949+
return 0;
2950+
}
2951+
2952+
static int enc_payload(struct snp_msg_desc *mdesc, u64 seqno, struct snp_guest_req *req)
2953+
{
2954+
struct snp_guest_msg *msg = &mdesc->secret_request;
2955+
struct snp_guest_msg_hdr *hdr = &msg->hdr;
2956+
struct aesgcm_ctx *ctx = mdesc->ctx;
2957+
u8 iv[GCM_AES_IV_SIZE] = {};
2958+
2959+
memset(msg, 0, sizeof(*msg));
2960+
2961+
hdr->algo = SNP_AEAD_AES_256_GCM;
2962+
hdr->hdr_version = MSG_HDR_VER;
2963+
hdr->hdr_sz = sizeof(*hdr);
2964+
hdr->msg_type = req->msg_type;
2965+
hdr->msg_version = req->msg_version;
2966+
hdr->msg_seqno = seqno;
2967+
hdr->msg_vmpck = req->vmpck_id;
2968+
hdr->msg_sz = req->req_sz;
2969+
2970+
/* Verify the sequence number is non-zero */
2971+
if (!hdr->msg_seqno)
2972+
return -ENOSR;
2973+
2974+
pr_debug("request [seqno %lld type %d version %d sz %d]\n",
2975+
hdr->msg_seqno, hdr->msg_type, hdr->msg_version, hdr->msg_sz);
2976+
2977+
if (WARN_ON((req->req_sz + ctx->authsize) > sizeof(msg->payload)))
2978+
return -EBADMSG;
2979+
2980+
memcpy(iv, &hdr->msg_seqno, min(sizeof(iv), sizeof(hdr->msg_seqno)));
2981+
aesgcm_encrypt(ctx, msg->payload, req->req_buf, req->req_sz, &hdr->algo,
2982+
AAD_LEN, iv, hdr->authtag);
2983+
2984+
return 0;
2985+
}
2986+
2987+
static int __handle_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req,
2988+
struct snp_guest_request_ioctl *rio)
2989+
{
2990+
unsigned long req_start = jiffies;
2991+
unsigned int override_npages = 0;
2992+
u64 override_err = 0;
2993+
int rc;
2994+
2995+
retry_request:
2996+
/*
2997+
* Call firmware to process the request. In this function the encrypted
2998+
* message enters shared memory with the host. So after this call the
2999+
* sequence number must be incremented or the VMPCK must be deleted to
3000+
* prevent reuse of the IV.
3001+
*/
3002+
rc = snp_issue_guest_request(req, &mdesc->input, rio);
3003+
switch (rc) {
3004+
case -ENOSPC:
3005+
/*
3006+
* If the extended guest request fails due to having too
3007+
* small of a certificate data buffer, retry the same
3008+
* guest request without the extended data request in
3009+
* order to increment the sequence number and thus avoid
3010+
* IV reuse.
3011+
*/
3012+
override_npages = mdesc->input.data_npages;
3013+
req->exit_code = SVM_VMGEXIT_GUEST_REQUEST;
3014+
3015+
/*
3016+
* Override the error to inform callers the given extended
3017+
* request buffer size was too small and give the caller the
3018+
* required buffer size.
3019+
*/
3020+
override_err = SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN);
3021+
3022+
/*
3023+
* If this call to the firmware succeeds, the sequence number can
3024+
* be incremented allowing for continued use of the VMPCK. If
3025+
* there is an error reflected in the return value, this value
3026+
* is checked further down and the result will be the deletion
3027+
* of the VMPCK and the error code being propagated back to the
3028+
* user as an ioctl() return code.
3029+
*/
3030+
goto retry_request;
3031+
3032+
/*
3033+
* The host may return SNP_GUEST_VMM_ERR_BUSY if the request has been
3034+
* throttled. Retry in the driver to avoid returning and reusing the
3035+
* message sequence number on a different message.
3036+
*/
3037+
case -EAGAIN:
3038+
if (jiffies - req_start > SNP_REQ_MAX_RETRY_DURATION) {
3039+
rc = -ETIMEDOUT;
3040+
break;
3041+
}
3042+
schedule_timeout_killable(SNP_REQ_RETRY_DELAY);
3043+
goto retry_request;
3044+
}
3045+
3046+
/*
3047+
* Increment the message sequence number. There is no harm in doing
3048+
* this now because decryption uses the value stored in the response
3049+
* structure and any failure will wipe the VMPCK, preventing further
3050+
* use anyway.
3051+
*/
3052+
snp_inc_msg_seqno(mdesc);
3053+
3054+
if (override_err) {
3055+
rio->exitinfo2 = override_err;
3056+
3057+
/*
3058+
* If an extended guest request was issued and the supplied certificate
3059+
* buffer was not large enough, a standard guest request was issued to
3060+
* prevent IV reuse. If the standard request was successful, return -EIO
3061+
* back to the caller as would have originally been returned.
3062+
*/
3063+
if (!rc && override_err == SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN))
3064+
rc = -EIO;
3065+
}
3066+
3067+
if (override_npages)
3068+
mdesc->input.data_npages = override_npages;
3069+
3070+
return rc;
3071+
}
3072+
3073+
int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req,
3074+
struct snp_guest_request_ioctl *rio)
3075+
{
3076+
u64 seqno;
3077+
int rc;
3078+
3079+
guard(mutex)(&snp_cmd_mutex);
3080+
3081+
/* Check if the VMPCK is not empty */
3082+
if (!mdesc->vmpck || !memchr_inv(mdesc->vmpck, 0, VMPCK_KEY_LEN)) {
3083+
pr_err_ratelimited("VMPCK is disabled\n");
3084+
return -ENOTTY;
3085+
}
3086+
3087+
/* Get message sequence and verify that its a non-zero */
3088+
seqno = snp_get_msg_seqno(mdesc);
3089+
if (!seqno)
3090+
return -EIO;
3091+
3092+
/* Clear shared memory's response for the host to populate. */
3093+
memset(mdesc->response, 0, sizeof(struct snp_guest_msg));
3094+
3095+
/* Encrypt the userspace provided payload in mdesc->secret_request. */
3096+
rc = enc_payload(mdesc, seqno, req);
3097+
if (rc)
3098+
return rc;
3099+
3100+
/*
3101+
* Write the fully encrypted request to the shared unencrypted
3102+
* request page.
3103+
*/
3104+
memcpy(mdesc->request, &mdesc->secret_request, sizeof(mdesc->secret_request));
3105+
3106+
rc = __handle_guest_request(mdesc, req, rio);
3107+
if (rc) {
3108+
if (rc == -EIO &&
3109+
rio->exitinfo2 == SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN))
3110+
return rc;
3111+
3112+
pr_alert("Detected error from ASP request. rc: %d, exitinfo2: 0x%llx\n",
3113+
rc, rio->exitinfo2);
3114+
3115+
snp_disable_vmpck(mdesc);
3116+
return rc;
3117+
}
3118+
3119+
rc = verify_and_dec_payload(mdesc, req);
3120+
if (rc) {
3121+
pr_alert("Detected unexpected decode failure from ASP. rc: %d\n", rc);
3122+
snp_disable_vmpck(mdesc);
3123+
return rc;
3124+
}
3125+
3126+
return 0;
3127+
}
3128+
EXPORT_SYMBOL_GPL(snp_send_guest_request);

arch/x86/include/asm/sev.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ struct snp_req_data {
125125
#define AAD_LEN 48
126126
#define MSG_HDR_VER 1
127127

128+
#define SNP_REQ_MAX_RETRY_DURATION (60*HZ)
129+
#define SNP_REQ_RETRY_DELAY (2*HZ)
130+
128131
/* See SNP spec SNP_GUEST_REQUEST section for the structure */
129132
enum msg_type {
130133
SNP_MSG_TYPE_INVALID = 0,
@@ -443,8 +446,6 @@ void snp_set_wakeup_secondary_cpu(void);
443446
bool snp_init(struct boot_params *bp);
444447
void __noreturn snp_abort(void);
445448
void snp_dmi_setup(void);
446-
int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_data *input,
447-
struct snp_guest_request_ioctl *rio);
448449
int snp_issue_svsm_attest_req(u64 call_id, struct svsm_call *call, struct svsm_attest_call *input);
449450
void snp_accept_memory(phys_addr_t start, phys_addr_t end);
450451
u64 snp_get_unsupported_features(u64 status);
@@ -459,6 +460,8 @@ void snp_kexec_begin(void);
459460
int snp_msg_init(struct snp_msg_desc *mdesc, int vmpck_id);
460461
struct snp_msg_desc *snp_msg_alloc(void);
461462
void snp_msg_free(struct snp_msg_desc *mdesc);
463+
int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req,
464+
struct snp_guest_request_ioctl *rio);
462465

463466
#else /* !CONFIG_AMD_MEM_ENCRYPT */
464467

@@ -482,11 +485,6 @@ static inline void snp_set_wakeup_secondary_cpu(void) { }
482485
static inline bool snp_init(struct boot_params *bp) { return false; }
483486
static inline void snp_abort(void) { }
484487
static inline void snp_dmi_setup(void) { }
485-
static inline int snp_issue_guest_request(struct snp_guest_req *req, struct snp_req_data *input,
486-
struct snp_guest_request_ioctl *rio)
487-
{
488-
return -ENOTTY;
489-
}
490488
static inline int snp_issue_svsm_attest_req(u64 call_id, struct svsm_call *call, struct svsm_attest_call *input)
491489
{
492490
return -ENOTTY;
@@ -503,6 +501,8 @@ static inline void snp_kexec_begin(void) { }
503501
static inline int snp_msg_init(struct snp_msg_desc *mdesc, int vmpck_id) { return -1; }
504502
static inline struct snp_msg_desc *snp_msg_alloc(void) { return NULL; }
505503
static inline void snp_msg_free(struct snp_msg_desc *mdesc) { }
504+
static inline int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req,
505+
struct snp_guest_request_ioctl *rio) { return -ENODEV; }
506506

507507
#endif /* CONFIG_AMD_MEM_ENCRYPT */
508508

0 commit comments

Comments
 (0)