162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * AMD Secure Encrypted Virtualization (SEV) guest driver interface 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2021 Advanced Micro Devices, Inc. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Brijesh Singh <brijesh.singh@amd.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/types.h> 1362306a36Sopenharmony_ci#include <linux/mutex.h> 1462306a36Sopenharmony_ci#include <linux/io.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/miscdevice.h> 1762306a36Sopenharmony_ci#include <linux/set_memory.h> 1862306a36Sopenharmony_ci#include <linux/fs.h> 1962306a36Sopenharmony_ci#include <crypto/aead.h> 2062306a36Sopenharmony_ci#include <linux/scatterlist.h> 2162306a36Sopenharmony_ci#include <linux/psp-sev.h> 2262306a36Sopenharmony_ci#include <uapi/linux/sev-guest.h> 2362306a36Sopenharmony_ci#include <uapi/linux/psp-sev.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <asm/svm.h> 2662306a36Sopenharmony_ci#include <asm/sev.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include "sev-guest.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define DEVICE_NAME "sev-guest" 3162306a36Sopenharmony_ci#define AAD_LEN 48 3262306a36Sopenharmony_ci#define MSG_HDR_VER 1 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define SNP_REQ_MAX_RETRY_DURATION (60*HZ) 3562306a36Sopenharmony_ci#define SNP_REQ_RETRY_DELAY (2*HZ) 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistruct snp_guest_crypto { 3862306a36Sopenharmony_ci struct crypto_aead *tfm; 3962306a36Sopenharmony_ci u8 *iv, *authtag; 4062306a36Sopenharmony_ci int iv_len, a_len; 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistruct snp_guest_dev { 4462306a36Sopenharmony_ci struct device *dev; 4562306a36Sopenharmony_ci struct miscdevice misc; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci void *certs_data; 4862306a36Sopenharmony_ci struct snp_guest_crypto *crypto; 4962306a36Sopenharmony_ci /* request and response are in unencrypted memory */ 5062306a36Sopenharmony_ci struct snp_guest_msg *request, *response; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci /* 5362306a36Sopenharmony_ci * Avoid information leakage by double-buffering shared messages 5462306a36Sopenharmony_ci * in fields that are in regular encrypted memory. 5562306a36Sopenharmony_ci */ 5662306a36Sopenharmony_ci struct snp_guest_msg secret_request, secret_response; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci struct snp_secrets_page_layout *layout; 5962306a36Sopenharmony_ci struct snp_req_data input; 6062306a36Sopenharmony_ci union { 6162306a36Sopenharmony_ci struct snp_report_req report; 6262306a36Sopenharmony_ci struct snp_derived_key_req derived_key; 6362306a36Sopenharmony_ci struct snp_ext_report_req ext_report; 6462306a36Sopenharmony_ci } req; 6562306a36Sopenharmony_ci u32 *os_area_msg_seqno; 6662306a36Sopenharmony_ci u8 *vmpck; 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic u32 vmpck_id; 7062306a36Sopenharmony_cimodule_param(vmpck_id, uint, 0444); 7162306a36Sopenharmony_ciMODULE_PARM_DESC(vmpck_id, "The VMPCK ID to use when communicating with the PSP."); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* Mutex to serialize the shared buffer access and command handling. */ 7462306a36Sopenharmony_cistatic DEFINE_MUTEX(snp_cmd_mutex); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic bool is_vmpck_empty(struct snp_guest_dev *snp_dev) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci char zero_key[VMPCK_KEY_LEN] = {0}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (snp_dev->vmpck) 8162306a36Sopenharmony_ci return !memcmp(snp_dev->vmpck, zero_key, VMPCK_KEY_LEN); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return true; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/* 8762306a36Sopenharmony_ci * If an error is received from the host or AMD Secure Processor (ASP) there 8862306a36Sopenharmony_ci * are two options. Either retry the exact same encrypted request or discontinue 8962306a36Sopenharmony_ci * using the VMPCK. 9062306a36Sopenharmony_ci * 9162306a36Sopenharmony_ci * This is because in the current encryption scheme GHCB v2 uses AES-GCM to 9262306a36Sopenharmony_ci * encrypt the requests. The IV for this scheme is the sequence number. GCM 9362306a36Sopenharmony_ci * cannot tolerate IV reuse. 9462306a36Sopenharmony_ci * 9562306a36Sopenharmony_ci * The ASP FW v1.51 only increments the sequence numbers on a successful 9662306a36Sopenharmony_ci * guest<->ASP back and forth and only accepts messages at its exact sequence 9762306a36Sopenharmony_ci * number. 9862306a36Sopenharmony_ci * 9962306a36Sopenharmony_ci * So if the sequence number were to be reused the encryption scheme is 10062306a36Sopenharmony_ci * vulnerable. If the sequence number were incremented for a fresh IV the ASP 10162306a36Sopenharmony_ci * will reject the request. 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_cistatic void snp_disable_vmpck(struct snp_guest_dev *snp_dev) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci dev_alert(snp_dev->dev, "Disabling vmpck_id %d to prevent IV reuse.\n", 10662306a36Sopenharmony_ci vmpck_id); 10762306a36Sopenharmony_ci memzero_explicit(snp_dev->vmpck, VMPCK_KEY_LEN); 10862306a36Sopenharmony_ci snp_dev->vmpck = NULL; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic inline u64 __snp_get_msg_seqno(struct snp_guest_dev *snp_dev) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci u64 count; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci lockdep_assert_held(&snp_cmd_mutex); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* Read the current message sequence counter from secrets pages */ 11862306a36Sopenharmony_ci count = *snp_dev->os_area_msg_seqno; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci return count + 1; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* Return a non-zero on success */ 12462306a36Sopenharmony_cistatic u64 snp_get_msg_seqno(struct snp_guest_dev *snp_dev) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci u64 count = __snp_get_msg_seqno(snp_dev); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* 12962306a36Sopenharmony_ci * The message sequence counter for the SNP guest request is a 64-bit 13062306a36Sopenharmony_ci * value but the version 2 of GHCB specification defines a 32-bit storage 13162306a36Sopenharmony_ci * for it. If the counter exceeds the 32-bit value then return zero. 13262306a36Sopenharmony_ci * The caller should check the return value, but if the caller happens to 13362306a36Sopenharmony_ci * not check the value and use it, then the firmware treats zero as an 13462306a36Sopenharmony_ci * invalid number and will fail the message request. 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_ci if (count >= UINT_MAX) { 13762306a36Sopenharmony_ci dev_err(snp_dev->dev, "request message sequence counter overflow\n"); 13862306a36Sopenharmony_ci return 0; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci return count; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic void snp_inc_msg_seqno(struct snp_guest_dev *snp_dev) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci /* 14762306a36Sopenharmony_ci * The counter is also incremented by the PSP, so increment it by 2 14862306a36Sopenharmony_ci * and save in secrets page. 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_ci *snp_dev->os_area_msg_seqno += 2; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic inline struct snp_guest_dev *to_snp_dev(struct file *file) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci struct miscdevice *dev = file->private_data; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci return container_of(dev, struct snp_guest_dev, misc); 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic struct snp_guest_crypto *init_crypto(struct snp_guest_dev *snp_dev, u8 *key, size_t keylen) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct snp_guest_crypto *crypto; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci crypto = kzalloc(sizeof(*crypto), GFP_KERNEL_ACCOUNT); 16562306a36Sopenharmony_ci if (!crypto) 16662306a36Sopenharmony_ci return NULL; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci crypto->tfm = crypto_alloc_aead("gcm(aes)", 0, 0); 16962306a36Sopenharmony_ci if (IS_ERR(crypto->tfm)) 17062306a36Sopenharmony_ci goto e_free; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (crypto_aead_setkey(crypto->tfm, key, keylen)) 17362306a36Sopenharmony_ci goto e_free_crypto; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci crypto->iv_len = crypto_aead_ivsize(crypto->tfm); 17662306a36Sopenharmony_ci crypto->iv = kmalloc(crypto->iv_len, GFP_KERNEL_ACCOUNT); 17762306a36Sopenharmony_ci if (!crypto->iv) 17862306a36Sopenharmony_ci goto e_free_crypto; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (crypto_aead_authsize(crypto->tfm) > MAX_AUTHTAG_LEN) { 18162306a36Sopenharmony_ci if (crypto_aead_setauthsize(crypto->tfm, MAX_AUTHTAG_LEN)) { 18262306a36Sopenharmony_ci dev_err(snp_dev->dev, "failed to set authsize to %d\n", MAX_AUTHTAG_LEN); 18362306a36Sopenharmony_ci goto e_free_iv; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci crypto->a_len = crypto_aead_authsize(crypto->tfm); 18862306a36Sopenharmony_ci crypto->authtag = kmalloc(crypto->a_len, GFP_KERNEL_ACCOUNT); 18962306a36Sopenharmony_ci if (!crypto->authtag) 19062306a36Sopenharmony_ci goto e_free_iv; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci return crypto; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cie_free_iv: 19562306a36Sopenharmony_ci kfree(crypto->iv); 19662306a36Sopenharmony_cie_free_crypto: 19762306a36Sopenharmony_ci crypto_free_aead(crypto->tfm); 19862306a36Sopenharmony_cie_free: 19962306a36Sopenharmony_ci kfree(crypto); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci return NULL; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic void deinit_crypto(struct snp_guest_crypto *crypto) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci crypto_free_aead(crypto->tfm); 20762306a36Sopenharmony_ci kfree(crypto->iv); 20862306a36Sopenharmony_ci kfree(crypto->authtag); 20962306a36Sopenharmony_ci kfree(crypto); 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic int enc_dec_message(struct snp_guest_crypto *crypto, struct snp_guest_msg *msg, 21362306a36Sopenharmony_ci u8 *src_buf, u8 *dst_buf, size_t len, bool enc) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci struct snp_guest_msg_hdr *hdr = &msg->hdr; 21662306a36Sopenharmony_ci struct scatterlist src[3], dst[3]; 21762306a36Sopenharmony_ci DECLARE_CRYPTO_WAIT(wait); 21862306a36Sopenharmony_ci struct aead_request *req; 21962306a36Sopenharmony_ci int ret; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci req = aead_request_alloc(crypto->tfm, GFP_KERNEL); 22262306a36Sopenharmony_ci if (!req) 22362306a36Sopenharmony_ci return -ENOMEM; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci /* 22662306a36Sopenharmony_ci * AEAD memory operations: 22762306a36Sopenharmony_ci * +------ AAD -------+------- DATA -----+---- AUTHTAG----+ 22862306a36Sopenharmony_ci * | msg header | plaintext | hdr->authtag | 22962306a36Sopenharmony_ci * | bytes 30h - 5Fh | or | | 23062306a36Sopenharmony_ci * | | cipher | | 23162306a36Sopenharmony_ci * +------------------+------------------+----------------+ 23262306a36Sopenharmony_ci */ 23362306a36Sopenharmony_ci sg_init_table(src, 3); 23462306a36Sopenharmony_ci sg_set_buf(&src[0], &hdr->algo, AAD_LEN); 23562306a36Sopenharmony_ci sg_set_buf(&src[1], src_buf, hdr->msg_sz); 23662306a36Sopenharmony_ci sg_set_buf(&src[2], hdr->authtag, crypto->a_len); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci sg_init_table(dst, 3); 23962306a36Sopenharmony_ci sg_set_buf(&dst[0], &hdr->algo, AAD_LEN); 24062306a36Sopenharmony_ci sg_set_buf(&dst[1], dst_buf, hdr->msg_sz); 24162306a36Sopenharmony_ci sg_set_buf(&dst[2], hdr->authtag, crypto->a_len); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci aead_request_set_ad(req, AAD_LEN); 24462306a36Sopenharmony_ci aead_request_set_tfm(req, crypto->tfm); 24562306a36Sopenharmony_ci aead_request_set_callback(req, 0, crypto_req_done, &wait); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci aead_request_set_crypt(req, src, dst, len, crypto->iv); 24862306a36Sopenharmony_ci ret = crypto_wait_req(enc ? crypto_aead_encrypt(req) : crypto_aead_decrypt(req), &wait); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci aead_request_free(req); 25162306a36Sopenharmony_ci return ret; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic int __enc_payload(struct snp_guest_dev *snp_dev, struct snp_guest_msg *msg, 25562306a36Sopenharmony_ci void *plaintext, size_t len) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci struct snp_guest_crypto *crypto = snp_dev->crypto; 25862306a36Sopenharmony_ci struct snp_guest_msg_hdr *hdr = &msg->hdr; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci memset(crypto->iv, 0, crypto->iv_len); 26162306a36Sopenharmony_ci memcpy(crypto->iv, &hdr->msg_seqno, sizeof(hdr->msg_seqno)); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci return enc_dec_message(crypto, msg, plaintext, msg->payload, len, true); 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic int dec_payload(struct snp_guest_dev *snp_dev, struct snp_guest_msg *msg, 26762306a36Sopenharmony_ci void *plaintext, size_t len) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci struct snp_guest_crypto *crypto = snp_dev->crypto; 27062306a36Sopenharmony_ci struct snp_guest_msg_hdr *hdr = &msg->hdr; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* Build IV with response buffer sequence number */ 27362306a36Sopenharmony_ci memset(crypto->iv, 0, crypto->iv_len); 27462306a36Sopenharmony_ci memcpy(crypto->iv, &hdr->msg_seqno, sizeof(hdr->msg_seqno)); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci return enc_dec_message(crypto, msg, msg->payload, plaintext, len, false); 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic int verify_and_dec_payload(struct snp_guest_dev *snp_dev, void *payload, u32 sz) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci struct snp_guest_crypto *crypto = snp_dev->crypto; 28262306a36Sopenharmony_ci struct snp_guest_msg *resp = &snp_dev->secret_response; 28362306a36Sopenharmony_ci struct snp_guest_msg *req = &snp_dev->secret_request; 28462306a36Sopenharmony_ci struct snp_guest_msg_hdr *req_hdr = &req->hdr; 28562306a36Sopenharmony_ci struct snp_guest_msg_hdr *resp_hdr = &resp->hdr; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci dev_dbg(snp_dev->dev, "response [seqno %lld type %d version %d sz %d]\n", 28862306a36Sopenharmony_ci resp_hdr->msg_seqno, resp_hdr->msg_type, resp_hdr->msg_version, resp_hdr->msg_sz); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* Copy response from shared memory to encrypted memory. */ 29162306a36Sopenharmony_ci memcpy(resp, snp_dev->response, sizeof(*resp)); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* Verify that the sequence counter is incremented by 1 */ 29462306a36Sopenharmony_ci if (unlikely(resp_hdr->msg_seqno != (req_hdr->msg_seqno + 1))) 29562306a36Sopenharmony_ci return -EBADMSG; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* Verify response message type and version number. */ 29862306a36Sopenharmony_ci if (resp_hdr->msg_type != (req_hdr->msg_type + 1) || 29962306a36Sopenharmony_ci resp_hdr->msg_version != req_hdr->msg_version) 30062306a36Sopenharmony_ci return -EBADMSG; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* 30362306a36Sopenharmony_ci * If the message size is greater than our buffer length then return 30462306a36Sopenharmony_ci * an error. 30562306a36Sopenharmony_ci */ 30662306a36Sopenharmony_ci if (unlikely((resp_hdr->msg_sz + crypto->a_len) > sz)) 30762306a36Sopenharmony_ci return -EBADMSG; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* Decrypt the payload */ 31062306a36Sopenharmony_ci return dec_payload(snp_dev, resp, payload, resp_hdr->msg_sz + crypto->a_len); 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic int enc_payload(struct snp_guest_dev *snp_dev, u64 seqno, int version, u8 type, 31462306a36Sopenharmony_ci void *payload, size_t sz) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci struct snp_guest_msg *req = &snp_dev->secret_request; 31762306a36Sopenharmony_ci struct snp_guest_msg_hdr *hdr = &req->hdr; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci memset(req, 0, sizeof(*req)); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci hdr->algo = SNP_AEAD_AES_256_GCM; 32262306a36Sopenharmony_ci hdr->hdr_version = MSG_HDR_VER; 32362306a36Sopenharmony_ci hdr->hdr_sz = sizeof(*hdr); 32462306a36Sopenharmony_ci hdr->msg_type = type; 32562306a36Sopenharmony_ci hdr->msg_version = version; 32662306a36Sopenharmony_ci hdr->msg_seqno = seqno; 32762306a36Sopenharmony_ci hdr->msg_vmpck = vmpck_id; 32862306a36Sopenharmony_ci hdr->msg_sz = sz; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* Verify the sequence number is non-zero */ 33162306a36Sopenharmony_ci if (!hdr->msg_seqno) 33262306a36Sopenharmony_ci return -ENOSR; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci dev_dbg(snp_dev->dev, "request [seqno %lld type %d version %d sz %d]\n", 33562306a36Sopenharmony_ci hdr->msg_seqno, hdr->msg_type, hdr->msg_version, hdr->msg_sz); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci return __enc_payload(snp_dev, req, payload, sz); 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic int __handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code, 34162306a36Sopenharmony_ci struct snp_guest_request_ioctl *rio) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci unsigned long req_start = jiffies; 34462306a36Sopenharmony_ci unsigned int override_npages = 0; 34562306a36Sopenharmony_ci u64 override_err = 0; 34662306a36Sopenharmony_ci int rc; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ciretry_request: 34962306a36Sopenharmony_ci /* 35062306a36Sopenharmony_ci * Call firmware to process the request. In this function the encrypted 35162306a36Sopenharmony_ci * message enters shared memory with the host. So after this call the 35262306a36Sopenharmony_ci * sequence number must be incremented or the VMPCK must be deleted to 35362306a36Sopenharmony_ci * prevent reuse of the IV. 35462306a36Sopenharmony_ci */ 35562306a36Sopenharmony_ci rc = snp_issue_guest_request(exit_code, &snp_dev->input, rio); 35662306a36Sopenharmony_ci switch (rc) { 35762306a36Sopenharmony_ci case -ENOSPC: 35862306a36Sopenharmony_ci /* 35962306a36Sopenharmony_ci * If the extended guest request fails due to having too 36062306a36Sopenharmony_ci * small of a certificate data buffer, retry the same 36162306a36Sopenharmony_ci * guest request without the extended data request in 36262306a36Sopenharmony_ci * order to increment the sequence number and thus avoid 36362306a36Sopenharmony_ci * IV reuse. 36462306a36Sopenharmony_ci */ 36562306a36Sopenharmony_ci override_npages = snp_dev->input.data_npages; 36662306a36Sopenharmony_ci exit_code = SVM_VMGEXIT_GUEST_REQUEST; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci /* 36962306a36Sopenharmony_ci * Override the error to inform callers the given extended 37062306a36Sopenharmony_ci * request buffer size was too small and give the caller the 37162306a36Sopenharmony_ci * required buffer size. 37262306a36Sopenharmony_ci */ 37362306a36Sopenharmony_ci override_err = SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* 37662306a36Sopenharmony_ci * If this call to the firmware succeeds, the sequence number can 37762306a36Sopenharmony_ci * be incremented allowing for continued use of the VMPCK. If 37862306a36Sopenharmony_ci * there is an error reflected in the return value, this value 37962306a36Sopenharmony_ci * is checked further down and the result will be the deletion 38062306a36Sopenharmony_ci * of the VMPCK and the error code being propagated back to the 38162306a36Sopenharmony_ci * user as an ioctl() return code. 38262306a36Sopenharmony_ci */ 38362306a36Sopenharmony_ci goto retry_request; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci /* 38662306a36Sopenharmony_ci * The host may return SNP_GUEST_VMM_ERR_BUSY if the request has been 38762306a36Sopenharmony_ci * throttled. Retry in the driver to avoid returning and reusing the 38862306a36Sopenharmony_ci * message sequence number on a different message. 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_ci case -EAGAIN: 39162306a36Sopenharmony_ci if (jiffies - req_start > SNP_REQ_MAX_RETRY_DURATION) { 39262306a36Sopenharmony_ci rc = -ETIMEDOUT; 39362306a36Sopenharmony_ci break; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci schedule_timeout_killable(SNP_REQ_RETRY_DELAY); 39662306a36Sopenharmony_ci goto retry_request; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci /* 40062306a36Sopenharmony_ci * Increment the message sequence number. There is no harm in doing 40162306a36Sopenharmony_ci * this now because decryption uses the value stored in the response 40262306a36Sopenharmony_ci * structure and any failure will wipe the VMPCK, preventing further 40362306a36Sopenharmony_ci * use anyway. 40462306a36Sopenharmony_ci */ 40562306a36Sopenharmony_ci snp_inc_msg_seqno(snp_dev); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if (override_err) { 40862306a36Sopenharmony_ci rio->exitinfo2 = override_err; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* 41162306a36Sopenharmony_ci * If an extended guest request was issued and the supplied certificate 41262306a36Sopenharmony_ci * buffer was not large enough, a standard guest request was issued to 41362306a36Sopenharmony_ci * prevent IV reuse. If the standard request was successful, return -EIO 41462306a36Sopenharmony_ci * back to the caller as would have originally been returned. 41562306a36Sopenharmony_ci */ 41662306a36Sopenharmony_ci if (!rc && override_err == SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN)) 41762306a36Sopenharmony_ci rc = -EIO; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci if (override_npages) 42162306a36Sopenharmony_ci snp_dev->input.data_npages = override_npages; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci return rc; 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic int handle_guest_request(struct snp_guest_dev *snp_dev, u64 exit_code, 42762306a36Sopenharmony_ci struct snp_guest_request_ioctl *rio, u8 type, 42862306a36Sopenharmony_ci void *req_buf, size_t req_sz, void *resp_buf, 42962306a36Sopenharmony_ci u32 resp_sz) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci u64 seqno; 43262306a36Sopenharmony_ci int rc; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci /* Get message sequence and verify that its a non-zero */ 43562306a36Sopenharmony_ci seqno = snp_get_msg_seqno(snp_dev); 43662306a36Sopenharmony_ci if (!seqno) 43762306a36Sopenharmony_ci return -EIO; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci /* Clear shared memory's response for the host to populate. */ 44062306a36Sopenharmony_ci memset(snp_dev->response, 0, sizeof(struct snp_guest_msg)); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* Encrypt the userspace provided payload in snp_dev->secret_request. */ 44362306a36Sopenharmony_ci rc = enc_payload(snp_dev, seqno, rio->msg_version, type, req_buf, req_sz); 44462306a36Sopenharmony_ci if (rc) 44562306a36Sopenharmony_ci return rc; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci /* 44862306a36Sopenharmony_ci * Write the fully encrypted request to the shared unencrypted 44962306a36Sopenharmony_ci * request page. 45062306a36Sopenharmony_ci */ 45162306a36Sopenharmony_ci memcpy(snp_dev->request, &snp_dev->secret_request, 45262306a36Sopenharmony_ci sizeof(snp_dev->secret_request)); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci rc = __handle_guest_request(snp_dev, exit_code, rio); 45562306a36Sopenharmony_ci if (rc) { 45662306a36Sopenharmony_ci if (rc == -EIO && 45762306a36Sopenharmony_ci rio->exitinfo2 == SNP_GUEST_VMM_ERR(SNP_GUEST_VMM_ERR_INVALID_LEN)) 45862306a36Sopenharmony_ci return rc; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci dev_alert(snp_dev->dev, 46162306a36Sopenharmony_ci "Detected error from ASP request. rc: %d, exitinfo2: 0x%llx\n", 46262306a36Sopenharmony_ci rc, rio->exitinfo2); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci snp_disable_vmpck(snp_dev); 46562306a36Sopenharmony_ci return rc; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci rc = verify_and_dec_payload(snp_dev, resp_buf, resp_sz); 46962306a36Sopenharmony_ci if (rc) { 47062306a36Sopenharmony_ci dev_alert(snp_dev->dev, "Detected unexpected decode failure from ASP. rc: %d\n", rc); 47162306a36Sopenharmony_ci snp_disable_vmpck(snp_dev); 47262306a36Sopenharmony_ci return rc; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci return 0; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic int get_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci struct snp_guest_crypto *crypto = snp_dev->crypto; 48162306a36Sopenharmony_ci struct snp_report_req *req = &snp_dev->req.report; 48262306a36Sopenharmony_ci struct snp_report_resp *resp; 48362306a36Sopenharmony_ci int rc, resp_len; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci lockdep_assert_held(&snp_cmd_mutex); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if (!arg->req_data || !arg->resp_data) 48862306a36Sopenharmony_ci return -EINVAL; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (copy_from_user(req, (void __user *)arg->req_data, sizeof(*req))) 49162306a36Sopenharmony_ci return -EFAULT; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci /* 49462306a36Sopenharmony_ci * The intermediate response buffer is used while decrypting the 49562306a36Sopenharmony_ci * response payload. Make sure that it has enough space to cover the 49662306a36Sopenharmony_ci * authtag. 49762306a36Sopenharmony_ci */ 49862306a36Sopenharmony_ci resp_len = sizeof(resp->data) + crypto->a_len; 49962306a36Sopenharmony_ci resp = kzalloc(resp_len, GFP_KERNEL_ACCOUNT); 50062306a36Sopenharmony_ci if (!resp) 50162306a36Sopenharmony_ci return -ENOMEM; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci rc = handle_guest_request(snp_dev, SVM_VMGEXIT_GUEST_REQUEST, arg, 50462306a36Sopenharmony_ci SNP_MSG_REPORT_REQ, req, sizeof(*req), resp->data, 50562306a36Sopenharmony_ci resp_len); 50662306a36Sopenharmony_ci if (rc) 50762306a36Sopenharmony_ci goto e_free; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci if (copy_to_user((void __user *)arg->resp_data, resp, sizeof(*resp))) 51062306a36Sopenharmony_ci rc = -EFAULT; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_cie_free: 51362306a36Sopenharmony_ci kfree(resp); 51462306a36Sopenharmony_ci return rc; 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic int get_derived_key(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci struct snp_derived_key_req *req = &snp_dev->req.derived_key; 52062306a36Sopenharmony_ci struct snp_guest_crypto *crypto = snp_dev->crypto; 52162306a36Sopenharmony_ci struct snp_derived_key_resp resp = {0}; 52262306a36Sopenharmony_ci int rc, resp_len; 52362306a36Sopenharmony_ci /* Response data is 64 bytes and max authsize for GCM is 16 bytes. */ 52462306a36Sopenharmony_ci u8 buf[64 + 16]; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci lockdep_assert_held(&snp_cmd_mutex); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci if (!arg->req_data || !arg->resp_data) 52962306a36Sopenharmony_ci return -EINVAL; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci /* 53262306a36Sopenharmony_ci * The intermediate response buffer is used while decrypting the 53362306a36Sopenharmony_ci * response payload. Make sure that it has enough space to cover the 53462306a36Sopenharmony_ci * authtag. 53562306a36Sopenharmony_ci */ 53662306a36Sopenharmony_ci resp_len = sizeof(resp.data) + crypto->a_len; 53762306a36Sopenharmony_ci if (sizeof(buf) < resp_len) 53862306a36Sopenharmony_ci return -ENOMEM; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci if (copy_from_user(req, (void __user *)arg->req_data, sizeof(*req))) 54162306a36Sopenharmony_ci return -EFAULT; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci rc = handle_guest_request(snp_dev, SVM_VMGEXIT_GUEST_REQUEST, arg, 54462306a36Sopenharmony_ci SNP_MSG_KEY_REQ, req, sizeof(*req), buf, resp_len); 54562306a36Sopenharmony_ci if (rc) 54662306a36Sopenharmony_ci return rc; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci memcpy(resp.data, buf, sizeof(resp.data)); 54962306a36Sopenharmony_ci if (copy_to_user((void __user *)arg->resp_data, &resp, sizeof(resp))) 55062306a36Sopenharmony_ci rc = -EFAULT; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci /* The response buffer contains the sensitive data, explicitly clear it. */ 55362306a36Sopenharmony_ci memzero_explicit(buf, sizeof(buf)); 55462306a36Sopenharmony_ci memzero_explicit(&resp, sizeof(resp)); 55562306a36Sopenharmony_ci return rc; 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cistatic int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_request_ioctl *arg) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci struct snp_ext_report_req *req = &snp_dev->req.ext_report; 56162306a36Sopenharmony_ci struct snp_guest_crypto *crypto = snp_dev->crypto; 56262306a36Sopenharmony_ci struct snp_report_resp *resp; 56362306a36Sopenharmony_ci int ret, npages = 0, resp_len; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci lockdep_assert_held(&snp_cmd_mutex); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci if (!arg->req_data || !arg->resp_data) 56862306a36Sopenharmony_ci return -EINVAL; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci if (copy_from_user(req, (void __user *)arg->req_data, sizeof(*req))) 57162306a36Sopenharmony_ci return -EFAULT; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci /* userspace does not want certificate data */ 57462306a36Sopenharmony_ci if (!req->certs_len || !req->certs_address) 57562306a36Sopenharmony_ci goto cmd; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci if (req->certs_len > SEV_FW_BLOB_MAX_SIZE || 57862306a36Sopenharmony_ci !IS_ALIGNED(req->certs_len, PAGE_SIZE)) 57962306a36Sopenharmony_ci return -EINVAL; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci if (!access_ok((const void __user *)req->certs_address, req->certs_len)) 58262306a36Sopenharmony_ci return -EFAULT; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci /* 58562306a36Sopenharmony_ci * Initialize the intermediate buffer with all zeros. This buffer 58662306a36Sopenharmony_ci * is used in the guest request message to get the certs blob from 58762306a36Sopenharmony_ci * the host. If host does not supply any certs in it, then copy 58862306a36Sopenharmony_ci * zeros to indicate that certificate data was not provided. 58962306a36Sopenharmony_ci */ 59062306a36Sopenharmony_ci memset(snp_dev->certs_data, 0, req->certs_len); 59162306a36Sopenharmony_ci npages = req->certs_len >> PAGE_SHIFT; 59262306a36Sopenharmony_cicmd: 59362306a36Sopenharmony_ci /* 59462306a36Sopenharmony_ci * The intermediate response buffer is used while decrypting the 59562306a36Sopenharmony_ci * response payload. Make sure that it has enough space to cover the 59662306a36Sopenharmony_ci * authtag. 59762306a36Sopenharmony_ci */ 59862306a36Sopenharmony_ci resp_len = sizeof(resp->data) + crypto->a_len; 59962306a36Sopenharmony_ci resp = kzalloc(resp_len, GFP_KERNEL_ACCOUNT); 60062306a36Sopenharmony_ci if (!resp) 60162306a36Sopenharmony_ci return -ENOMEM; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci snp_dev->input.data_npages = npages; 60462306a36Sopenharmony_ci ret = handle_guest_request(snp_dev, SVM_VMGEXIT_EXT_GUEST_REQUEST, arg, 60562306a36Sopenharmony_ci SNP_MSG_REPORT_REQ, &req->data, 60662306a36Sopenharmony_ci sizeof(req->data), resp->data, resp_len); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci /* If certs length is invalid then copy the returned length */ 60962306a36Sopenharmony_ci if (arg->vmm_error == SNP_GUEST_VMM_ERR_INVALID_LEN) { 61062306a36Sopenharmony_ci req->certs_len = snp_dev->input.data_npages << PAGE_SHIFT; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci if (copy_to_user((void __user *)arg->req_data, req, sizeof(*req))) 61362306a36Sopenharmony_ci ret = -EFAULT; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci if (ret) 61762306a36Sopenharmony_ci goto e_free; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci if (npages && 62062306a36Sopenharmony_ci copy_to_user((void __user *)req->certs_address, snp_dev->certs_data, 62162306a36Sopenharmony_ci req->certs_len)) { 62262306a36Sopenharmony_ci ret = -EFAULT; 62362306a36Sopenharmony_ci goto e_free; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci if (copy_to_user((void __user *)arg->resp_data, resp, sizeof(*resp))) 62762306a36Sopenharmony_ci ret = -EFAULT; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cie_free: 63062306a36Sopenharmony_ci kfree(resp); 63162306a36Sopenharmony_ci return ret; 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_cistatic long snp_guest_ioctl(struct file *file, unsigned int ioctl, unsigned long arg) 63562306a36Sopenharmony_ci{ 63662306a36Sopenharmony_ci struct snp_guest_dev *snp_dev = to_snp_dev(file); 63762306a36Sopenharmony_ci void __user *argp = (void __user *)arg; 63862306a36Sopenharmony_ci struct snp_guest_request_ioctl input; 63962306a36Sopenharmony_ci int ret = -ENOTTY; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci if (copy_from_user(&input, argp, sizeof(input))) 64262306a36Sopenharmony_ci return -EFAULT; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci input.exitinfo2 = 0xff; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci /* Message version must be non-zero */ 64762306a36Sopenharmony_ci if (!input.msg_version) 64862306a36Sopenharmony_ci return -EINVAL; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci mutex_lock(&snp_cmd_mutex); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci /* Check if the VMPCK is not empty */ 65362306a36Sopenharmony_ci if (is_vmpck_empty(snp_dev)) { 65462306a36Sopenharmony_ci dev_err_ratelimited(snp_dev->dev, "VMPCK is disabled\n"); 65562306a36Sopenharmony_ci mutex_unlock(&snp_cmd_mutex); 65662306a36Sopenharmony_ci return -ENOTTY; 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci switch (ioctl) { 66062306a36Sopenharmony_ci case SNP_GET_REPORT: 66162306a36Sopenharmony_ci ret = get_report(snp_dev, &input); 66262306a36Sopenharmony_ci break; 66362306a36Sopenharmony_ci case SNP_GET_DERIVED_KEY: 66462306a36Sopenharmony_ci ret = get_derived_key(snp_dev, &input); 66562306a36Sopenharmony_ci break; 66662306a36Sopenharmony_ci case SNP_GET_EXT_REPORT: 66762306a36Sopenharmony_ci ret = get_ext_report(snp_dev, &input); 66862306a36Sopenharmony_ci break; 66962306a36Sopenharmony_ci default: 67062306a36Sopenharmony_ci break; 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci mutex_unlock(&snp_cmd_mutex); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci if (input.exitinfo2 && copy_to_user(argp, &input, sizeof(input))) 67662306a36Sopenharmony_ci return -EFAULT; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci return ret; 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_cistatic void free_shared_pages(void *buf, size_t sz) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci unsigned int npages = PAGE_ALIGN(sz) >> PAGE_SHIFT; 68462306a36Sopenharmony_ci int ret; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci if (!buf) 68762306a36Sopenharmony_ci return; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci ret = set_memory_encrypted((unsigned long)buf, npages); 69062306a36Sopenharmony_ci if (ret) { 69162306a36Sopenharmony_ci WARN_ONCE(ret, "failed to restore encryption mask (leak it)\n"); 69262306a36Sopenharmony_ci return; 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci __free_pages(virt_to_page(buf), get_order(sz)); 69662306a36Sopenharmony_ci} 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_cistatic void *alloc_shared_pages(struct device *dev, size_t sz) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci unsigned int npages = PAGE_ALIGN(sz) >> PAGE_SHIFT; 70162306a36Sopenharmony_ci struct page *page; 70262306a36Sopenharmony_ci int ret; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci page = alloc_pages(GFP_KERNEL_ACCOUNT, get_order(sz)); 70562306a36Sopenharmony_ci if (!page) 70662306a36Sopenharmony_ci return NULL; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci ret = set_memory_decrypted((unsigned long)page_address(page), npages); 70962306a36Sopenharmony_ci if (ret) { 71062306a36Sopenharmony_ci dev_err(dev, "failed to mark page shared, ret=%d\n", ret); 71162306a36Sopenharmony_ci __free_pages(page, get_order(sz)); 71262306a36Sopenharmony_ci return NULL; 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci return page_address(page); 71662306a36Sopenharmony_ci} 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_cistatic const struct file_operations snp_guest_fops = { 71962306a36Sopenharmony_ci .owner = THIS_MODULE, 72062306a36Sopenharmony_ci .unlocked_ioctl = snp_guest_ioctl, 72162306a36Sopenharmony_ci}; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_cistatic u8 *get_vmpck(int id, struct snp_secrets_page_layout *layout, u32 **seqno) 72462306a36Sopenharmony_ci{ 72562306a36Sopenharmony_ci u8 *key = NULL; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci switch (id) { 72862306a36Sopenharmony_ci case 0: 72962306a36Sopenharmony_ci *seqno = &layout->os_area.msg_seqno_0; 73062306a36Sopenharmony_ci key = layout->vmpck0; 73162306a36Sopenharmony_ci break; 73262306a36Sopenharmony_ci case 1: 73362306a36Sopenharmony_ci *seqno = &layout->os_area.msg_seqno_1; 73462306a36Sopenharmony_ci key = layout->vmpck1; 73562306a36Sopenharmony_ci break; 73662306a36Sopenharmony_ci case 2: 73762306a36Sopenharmony_ci *seqno = &layout->os_area.msg_seqno_2; 73862306a36Sopenharmony_ci key = layout->vmpck2; 73962306a36Sopenharmony_ci break; 74062306a36Sopenharmony_ci case 3: 74162306a36Sopenharmony_ci *seqno = &layout->os_area.msg_seqno_3; 74262306a36Sopenharmony_ci key = layout->vmpck3; 74362306a36Sopenharmony_ci break; 74462306a36Sopenharmony_ci default: 74562306a36Sopenharmony_ci break; 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci return key; 74962306a36Sopenharmony_ci} 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_cistatic int __init sev_guest_probe(struct platform_device *pdev) 75262306a36Sopenharmony_ci{ 75362306a36Sopenharmony_ci struct snp_secrets_page_layout *layout; 75462306a36Sopenharmony_ci struct sev_guest_platform_data *data; 75562306a36Sopenharmony_ci struct device *dev = &pdev->dev; 75662306a36Sopenharmony_ci struct snp_guest_dev *snp_dev; 75762306a36Sopenharmony_ci struct miscdevice *misc; 75862306a36Sopenharmony_ci void __iomem *mapping; 75962306a36Sopenharmony_ci int ret; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP)) 76262306a36Sopenharmony_ci return -ENODEV; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci if (!dev->platform_data) 76562306a36Sopenharmony_ci return -ENODEV; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci data = (struct sev_guest_platform_data *)dev->platform_data; 76862306a36Sopenharmony_ci mapping = ioremap_encrypted(data->secrets_gpa, PAGE_SIZE); 76962306a36Sopenharmony_ci if (!mapping) 77062306a36Sopenharmony_ci return -ENODEV; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci layout = (__force void *)mapping; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci ret = -ENOMEM; 77562306a36Sopenharmony_ci snp_dev = devm_kzalloc(&pdev->dev, sizeof(struct snp_guest_dev), GFP_KERNEL); 77662306a36Sopenharmony_ci if (!snp_dev) 77762306a36Sopenharmony_ci goto e_unmap; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci ret = -EINVAL; 78062306a36Sopenharmony_ci snp_dev->vmpck = get_vmpck(vmpck_id, layout, &snp_dev->os_area_msg_seqno); 78162306a36Sopenharmony_ci if (!snp_dev->vmpck) { 78262306a36Sopenharmony_ci dev_err(dev, "invalid vmpck id %d\n", vmpck_id); 78362306a36Sopenharmony_ci goto e_unmap; 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci /* Verify that VMPCK is not zero. */ 78762306a36Sopenharmony_ci if (is_vmpck_empty(snp_dev)) { 78862306a36Sopenharmony_ci dev_err(dev, "vmpck id %d is null\n", vmpck_id); 78962306a36Sopenharmony_ci goto e_unmap; 79062306a36Sopenharmony_ci } 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci platform_set_drvdata(pdev, snp_dev); 79362306a36Sopenharmony_ci snp_dev->dev = dev; 79462306a36Sopenharmony_ci snp_dev->layout = layout; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci /* Allocate the shared page used for the request and response message. */ 79762306a36Sopenharmony_ci snp_dev->request = alloc_shared_pages(dev, sizeof(struct snp_guest_msg)); 79862306a36Sopenharmony_ci if (!snp_dev->request) 79962306a36Sopenharmony_ci goto e_unmap; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci snp_dev->response = alloc_shared_pages(dev, sizeof(struct snp_guest_msg)); 80262306a36Sopenharmony_ci if (!snp_dev->response) 80362306a36Sopenharmony_ci goto e_free_request; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci snp_dev->certs_data = alloc_shared_pages(dev, SEV_FW_BLOB_MAX_SIZE); 80662306a36Sopenharmony_ci if (!snp_dev->certs_data) 80762306a36Sopenharmony_ci goto e_free_response; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci ret = -EIO; 81062306a36Sopenharmony_ci snp_dev->crypto = init_crypto(snp_dev, snp_dev->vmpck, VMPCK_KEY_LEN); 81162306a36Sopenharmony_ci if (!snp_dev->crypto) 81262306a36Sopenharmony_ci goto e_free_cert_data; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci misc = &snp_dev->misc; 81562306a36Sopenharmony_ci misc->minor = MISC_DYNAMIC_MINOR; 81662306a36Sopenharmony_ci misc->name = DEVICE_NAME; 81762306a36Sopenharmony_ci misc->fops = &snp_guest_fops; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci /* initial the input address for guest request */ 82062306a36Sopenharmony_ci snp_dev->input.req_gpa = __pa(snp_dev->request); 82162306a36Sopenharmony_ci snp_dev->input.resp_gpa = __pa(snp_dev->response); 82262306a36Sopenharmony_ci snp_dev->input.data_gpa = __pa(snp_dev->certs_data); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci ret = misc_register(misc); 82562306a36Sopenharmony_ci if (ret) 82662306a36Sopenharmony_ci goto e_free_cert_data; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci dev_info(dev, "Initialized SEV guest driver (using vmpck_id %d)\n", vmpck_id); 82962306a36Sopenharmony_ci return 0; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_cie_free_cert_data: 83262306a36Sopenharmony_ci free_shared_pages(snp_dev->certs_data, SEV_FW_BLOB_MAX_SIZE); 83362306a36Sopenharmony_cie_free_response: 83462306a36Sopenharmony_ci free_shared_pages(snp_dev->response, sizeof(struct snp_guest_msg)); 83562306a36Sopenharmony_cie_free_request: 83662306a36Sopenharmony_ci free_shared_pages(snp_dev->request, sizeof(struct snp_guest_msg)); 83762306a36Sopenharmony_cie_unmap: 83862306a36Sopenharmony_ci iounmap(mapping); 83962306a36Sopenharmony_ci return ret; 84062306a36Sopenharmony_ci} 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_cistatic int __exit sev_guest_remove(struct platform_device *pdev) 84362306a36Sopenharmony_ci{ 84462306a36Sopenharmony_ci struct snp_guest_dev *snp_dev = platform_get_drvdata(pdev); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci free_shared_pages(snp_dev->certs_data, SEV_FW_BLOB_MAX_SIZE); 84762306a36Sopenharmony_ci free_shared_pages(snp_dev->response, sizeof(struct snp_guest_msg)); 84862306a36Sopenharmony_ci free_shared_pages(snp_dev->request, sizeof(struct snp_guest_msg)); 84962306a36Sopenharmony_ci deinit_crypto(snp_dev->crypto); 85062306a36Sopenharmony_ci misc_deregister(&snp_dev->misc); 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci return 0; 85362306a36Sopenharmony_ci} 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci/* 85662306a36Sopenharmony_ci * This driver is meant to be a common SEV guest interface driver and to 85762306a36Sopenharmony_ci * support any SEV guest API. As such, even though it has been introduced 85862306a36Sopenharmony_ci * with the SEV-SNP support, it is named "sev-guest". 85962306a36Sopenharmony_ci */ 86062306a36Sopenharmony_cistatic struct platform_driver sev_guest_driver = { 86162306a36Sopenharmony_ci .remove = __exit_p(sev_guest_remove), 86262306a36Sopenharmony_ci .driver = { 86362306a36Sopenharmony_ci .name = "sev-guest", 86462306a36Sopenharmony_ci }, 86562306a36Sopenharmony_ci}; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_cimodule_platform_driver_probe(sev_guest_driver, sev_guest_probe); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ciMODULE_AUTHOR("Brijesh Singh <brijesh.singh@amd.com>"); 87062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 87162306a36Sopenharmony_ciMODULE_VERSION("1.0.0"); 87262306a36Sopenharmony_ciMODULE_DESCRIPTION("AMD SEV Guest Driver"); 87362306a36Sopenharmony_ciMODULE_ALIAS("platform:sev-guest"); 874