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