brintos

brintos / linux-shallow public Read only

0
0
Text · 7.8 KiB · d7db6c8 Raw
353 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * TDX guest user interface driver4 *5 * Copyright (C) 2022 Intel Corporation6 */7 8#include <linux/kernel.h>9#include <linux/miscdevice.h>10#include <linux/mm.h>11#include <linux/module.h>12#include <linux/mod_devicetable.h>13#include <linux/string.h>14#include <linux/uaccess.h>15#include <linux/set_memory.h>16#include <linux/io.h>17#include <linux/delay.h>18#include <linux/tsm.h>19#include <linux/sizes.h>20 21#include <uapi/linux/tdx-guest.h>22 23#include <asm/cpu_device_id.h>24#include <asm/tdx.h>25 26/*27 * Intel's SGX QE implementation generally uses Quote size less28 * than 8K (2K Quote data + ~5K of certificate blob).29 */30#define GET_QUOTE_BUF_SIZE		SZ_8K31 32#define GET_QUOTE_CMD_VER		133 34/* TDX GetQuote status codes */35#define GET_QUOTE_SUCCESS		036#define GET_QUOTE_IN_FLIGHT		0xffffffffffffffff37 38/* struct tdx_quote_buf: Format of Quote request buffer.39 * @version: Quote format version, filled by TD.40 * @status: Status code of Quote request, filled by VMM.41 * @in_len: Length of TDREPORT, filled by TD.42 * @out_len: Length of Quote data, filled by VMM.43 * @data: Quote data on output or TDREPORT on input.44 *45 * More details of Quote request buffer can be found in TDX46 * Guest-Host Communication Interface (GHCI) for Intel TDX 1.0,47 * section titled "TDG.VP.VMCALL<GetQuote>"48 */49struct tdx_quote_buf {50	u64 version;51	u64 status;52	u32 in_len;53	u32 out_len;54	u8 data[];55};56 57/* Quote data buffer */58static void *quote_data;59 60/* Lock to streamline quote requests */61static DEFINE_MUTEX(quote_lock);62 63/*64 * GetQuote request timeout in seconds. Expect that 30 seconds65 * is enough time for QE to respond to any Quote requests.66 */67static u32 getquote_timeout = 30;68 69static long tdx_get_report0(struct tdx_report_req __user *req)70{71	u8 *reportdata, *tdreport;72	long ret;73 74	reportdata = kmalloc(TDX_REPORTDATA_LEN, GFP_KERNEL);75	if (!reportdata)76		return -ENOMEM;77 78	tdreport = kzalloc(TDX_REPORT_LEN, GFP_KERNEL);79	if (!tdreport) {80		ret = -ENOMEM;81		goto out;82	}83 84	if (copy_from_user(reportdata, req->reportdata, TDX_REPORTDATA_LEN)) {85		ret = -EFAULT;86		goto out;87	}88 89	/* Generate TDREPORT0 using "TDG.MR.REPORT" TDCALL */90	ret = tdx_mcall_get_report0(reportdata, tdreport);91	if (ret)92		goto out;93 94	if (copy_to_user(req->tdreport, tdreport, TDX_REPORT_LEN))95		ret = -EFAULT;96 97out:98	kfree(reportdata);99	kfree(tdreport);100 101	return ret;102}103 104static void free_quote_buf(void *buf)105{106	size_t len = PAGE_ALIGN(GET_QUOTE_BUF_SIZE);107	unsigned int count = len >> PAGE_SHIFT;108 109	if (set_memory_encrypted((unsigned long)buf, count)) {110		pr_err("Failed to restore encryption mask for Quote buffer, leak it\n");111		return;112	}113 114	free_pages_exact(buf, len);115}116 117static void *alloc_quote_buf(void)118{119	size_t len = PAGE_ALIGN(GET_QUOTE_BUF_SIZE);120	unsigned int count = len >> PAGE_SHIFT;121	void *addr;122 123	addr = alloc_pages_exact(len, GFP_KERNEL | __GFP_ZERO);124	if (!addr)125		return NULL;126 127	if (set_memory_decrypted((unsigned long)addr, count)) {128		free_pages_exact(addr, len);129		return NULL;130	}131 132	return addr;133}134 135/*136 * wait_for_quote_completion() - Wait for Quote request completion137 * @quote_buf: Address of Quote buffer.138 * @timeout: Timeout in seconds to wait for the Quote generation.139 *140 * As per TDX GHCI v1.0 specification, sec titled "TDG.VP.VMCALL<GetQuote>",141 * the status field in the Quote buffer will be set to GET_QUOTE_IN_FLIGHT142 * while VMM processes the GetQuote request, and will change it to success143 * or error code after processing is complete. So wait till the status144 * changes from GET_QUOTE_IN_FLIGHT or the request being timed out.145 */146static int wait_for_quote_completion(struct tdx_quote_buf *quote_buf, u32 timeout)147{148	int i = 0;149 150	/*151	 * Quote requests usually take a few seconds to complete, so waking up152	 * once per second to recheck the status is fine for this use case.153	 */154	while (quote_buf->status == GET_QUOTE_IN_FLIGHT && i++ < timeout) {155		if (msleep_interruptible(MSEC_PER_SEC))156			return -EINTR;157	}158 159	return (i == timeout) ? -ETIMEDOUT : 0;160}161 162static int tdx_report_new(struct tsm_report *report, void *data)163{164	u8 *buf, *reportdata = NULL, *tdreport = NULL;165	struct tdx_quote_buf *quote_buf = quote_data;166	struct tsm_desc *desc = &report->desc;167	int ret;168	u64 err;169 170	/* TODO: switch to guard(mutex_intr) */171	if (mutex_lock_interruptible(&quote_lock))172		return -EINTR;173 174	/*175	 * If the previous request is timedout or interrupted, and the176	 * Quote buf status is still in GET_QUOTE_IN_FLIGHT (owned by177	 * VMM), don't permit any new request.178	 */179	if (quote_buf->status == GET_QUOTE_IN_FLIGHT) {180		ret = -EBUSY;181		goto done;182	}183 184	if (desc->inblob_len != TDX_REPORTDATA_LEN) {185		ret = -EINVAL;186		goto done;187	}188 189	reportdata = kmalloc(TDX_REPORTDATA_LEN, GFP_KERNEL);190	if (!reportdata) {191		ret = -ENOMEM;192		goto done;193	}194 195	tdreport = kzalloc(TDX_REPORT_LEN, GFP_KERNEL);196	if (!tdreport) {197		ret = -ENOMEM;198		goto done;199	}200 201	memcpy(reportdata, desc->inblob, desc->inblob_len);202 203	/* Generate TDREPORT0 using "TDG.MR.REPORT" TDCALL */204	ret = tdx_mcall_get_report0(reportdata, tdreport);205	if (ret) {206		pr_err("GetReport call failed\n");207		goto done;208	}209 210	memset(quote_data, 0, GET_QUOTE_BUF_SIZE);211 212	/* Update Quote buffer header */213	quote_buf->version = GET_QUOTE_CMD_VER;214	quote_buf->in_len = TDX_REPORT_LEN;215 216	memcpy(quote_buf->data, tdreport, TDX_REPORT_LEN);217 218	err = tdx_hcall_get_quote(quote_data, GET_QUOTE_BUF_SIZE);219	if (err) {220		pr_err("GetQuote hypercall failed, status:%llx\n", err);221		ret = -EIO;222		goto done;223	}224 225	ret = wait_for_quote_completion(quote_buf, getquote_timeout);226	if (ret) {227		pr_err("GetQuote request timedout\n");228		goto done;229	}230 231	buf = kvmemdup(quote_buf->data, quote_buf->out_len, GFP_KERNEL);232	if (!buf) {233		ret = -ENOMEM;234		goto done;235	}236 237	report->outblob = buf;238	report->outblob_len = quote_buf->out_len;239 240	/*241	 * TODO: parse the PEM-formatted cert chain out of the quote buffer when242	 * provided243	 */244done:245	mutex_unlock(&quote_lock);246	kfree(reportdata);247	kfree(tdreport);248 249	return ret;250}251 252static bool tdx_report_attr_visible(int n)253{254	switch (n) {255	case TSM_REPORT_GENERATION:256	case TSM_REPORT_PROVIDER:257		return true;258	}259 260	return false;261}262 263static bool tdx_report_bin_attr_visible(int n)264{265	switch (n) {266	case TSM_REPORT_INBLOB:267	case TSM_REPORT_OUTBLOB:268		return true;269	}270 271	return false;272}273 274static long tdx_guest_ioctl(struct file *file, unsigned int cmd,275			    unsigned long arg)276{277	switch (cmd) {278	case TDX_CMD_GET_REPORT0:279		return tdx_get_report0((struct tdx_report_req __user *)arg);280	default:281		return -ENOTTY;282	}283}284 285static const struct file_operations tdx_guest_fops = {286	.owner = THIS_MODULE,287	.unlocked_ioctl = tdx_guest_ioctl,288};289 290static struct miscdevice tdx_misc_dev = {291	.name = KBUILD_MODNAME,292	.minor = MISC_DYNAMIC_MINOR,293	.fops = &tdx_guest_fops,294};295 296static const struct x86_cpu_id tdx_guest_ids[] = {297	X86_MATCH_FEATURE(X86_FEATURE_TDX_GUEST, NULL),298	{}299};300MODULE_DEVICE_TABLE(x86cpu, tdx_guest_ids);301 302static const struct tsm_ops tdx_tsm_ops = {303	.name = KBUILD_MODNAME,304	.report_new = tdx_report_new,305	.report_attr_visible = tdx_report_attr_visible,306	.report_bin_attr_visible = tdx_report_bin_attr_visible,307};308 309static int __init tdx_guest_init(void)310{311	int ret;312 313	if (!x86_match_cpu(tdx_guest_ids))314		return -ENODEV;315 316	ret = misc_register(&tdx_misc_dev);317	if (ret)318		return ret;319 320	quote_data = alloc_quote_buf();321	if (!quote_data) {322		pr_err("Failed to allocate Quote buffer\n");323		ret = -ENOMEM;324		goto free_misc;325	}326 327	ret = tsm_register(&tdx_tsm_ops, NULL);328	if (ret)329		goto free_quote;330 331	return 0;332 333free_quote:334	free_quote_buf(quote_data);335free_misc:336	misc_deregister(&tdx_misc_dev);337 338	return ret;339}340module_init(tdx_guest_init);341 342static void __exit tdx_guest_exit(void)343{344	tsm_unregister(&tdx_tsm_ops);345	free_quote_buf(quote_data);346	misc_deregister(&tdx_misc_dev);347}348module_exit(tdx_guest_exit);349 350MODULE_AUTHOR("Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>");351MODULE_DESCRIPTION("TDX Guest Driver");352MODULE_LICENSE("GPL");353