aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/virt/coco
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2025-05-12 22:12:44 -0700
committerDan Williams <dan.j.williams@intel.com>2025-05-12 22:12:44 -0700
commit15ff5d0e90bb044b94d9a4ac57276829861a954d (patch)
tree28bcf60949db629b0b772f6eebf026714dc38fe9 /drivers/virt/coco
parentMerge branch 'for-6.16/tsm' into tsm-next (diff)
parentvirt: tdx-guest: Transition to scoped_cond_guard for mutex operations (diff)
downloadlinux-15ff5d0e90bb044b94d9a4ac57276829861a954d.tar.gz
linux-15ff5d0e90bb044b94d9a4ac57276829861a954d.zip
Merge branch 'for-6.16/tsm-mr' into tsm-next
Merge measurement-register infrastructure for v6.16. Resolve conflicts with the establishment of drivers/virt/coco/guest/ for cross-vendor common TSM functionality. Address a mis-merge with a fixup from Lukas: Link: http://lore.kernel.org/20250509134031.70559-1-lukas.bulwahn@redhat.com
Diffstat (limited to 'drivers/virt/coco')
-rw-r--r--drivers/virt/coco/Makefile2
-rw-r--r--drivers/virt/coco/guest/Kconfig10
-rw-r--r--drivers/virt/coco/guest/Makefile1
-rw-r--r--drivers/virt/coco/guest/tsm-mr.c251
-rw-r--r--drivers/virt/coco/tdx-guest/Kconfig1
-rw-r--r--drivers/virt/coco/tdx-guest/tdx-guest.c251
6 files changed, 435 insertions, 81 deletions
diff --git a/drivers/virt/coco/Makefile b/drivers/virt/coco/Makefile
index 885c9ef4e9fc..f918bbb61737 100644
--- a/drivers/virt/coco/Makefile
+++ b/drivers/virt/coco/Makefile
@@ -7,4 +7,4 @@ obj-$(CONFIG_ARM_PKVM_GUEST) += pkvm-guest/
obj-$(CONFIG_SEV_GUEST) += sev-guest/
obj-$(CONFIG_INTEL_TDX_GUEST) += tdx-guest/
obj-$(CONFIG_ARM_CCA_GUEST) += arm-cca-guest/
-obj-$(CONFIG_TSM_REPORTS) += guest/
+obj-$(CONFIG_TSM_GUEST) += guest/
diff --git a/drivers/virt/coco/guest/Kconfig b/drivers/virt/coco/guest/Kconfig
index ed9bafbdd854..3d5e1d05bf34 100644
--- a/drivers/virt/coco/guest/Kconfig
+++ b/drivers/virt/coco/guest/Kconfig
@@ -2,6 +2,16 @@
#
# Confidential computing shared guest collateral
#
+config TSM_GUEST
+ bool
+
config TSM_REPORTS
+ select TSM_GUEST
select CONFIGFS_FS
tristate
+
+config TSM_MEASUREMENTS
+ select TSM_GUEST
+ select CRYPTO_HASH_INFO
+ select CRYPTO
+ bool
diff --git a/drivers/virt/coco/guest/Makefile b/drivers/virt/coco/guest/Makefile
index b3b217af77cf..9ec4860bd213 100644
--- a/drivers/virt/coco/guest/Makefile
+++ b/drivers/virt/coco/guest/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_TSM_REPORTS) += tsm_report.o
tsm_report-y := report.o
+obj-$(CONFIG_TSM_MEASUREMENTS) += tsm-mr.o
diff --git a/drivers/virt/coco/guest/tsm-mr.c b/drivers/virt/coco/guest/tsm-mr.c
new file mode 100644
index 000000000000..1f0c43a516fb
--- /dev/null
+++ b/drivers/virt/coco/guest/tsm-mr.c
@@ -0,0 +1,251 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright(c) 2024-2025 Intel Corporation. All rights reserved. */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/tsm_mr.h>
+
+/*
+ * struct tm_context - contains everything necessary to implement sysfs
+ * attributes for MRs.
+ * @rwsem: protects the MR cache from concurrent access.
+ * @agrp: contains all MR attributes created by tsm_mr_create_attribute_group().
+ * @tm: input to tsm_mr_create_attribute_group() containing MR definitions/ops.
+ * @in_sync: %true if MR cache is up-to-date.
+ * @mrs: array of &struct bin_attribute, one for each MR.
+ *
+ * This internal structure contains everything needed to implement
+ * tm_digest_read() and tm_digest_write().
+ *
+ * Given tm->refresh() is potentially expensive, tm_digest_read() caches MR
+ * values and calls tm->refresh() only when necessary. Only live MRs (i.e., with
+ * %TSM_MR_F_LIVE set) can trigger tm->refresh(), while others are assumed to
+ * retain their values from the last tm->write(). @in_sync tracks if there have
+ * been tm->write() calls since the last tm->refresh(). That is, tm->refresh()
+ * will be called only when a live MR is being read and the cache is stale
+ * (@in_sync is %false).
+ *
+ * tm_digest_write() sets @in_sync to %false and calls tm->write(), whose
+ * semantics is arch and MR specific. Most (if not all) writable MRs support the
+ * extension semantics (i.e., tm->write() extends the input buffer into the MR).
+ */
+struct tm_context {
+ struct rw_semaphore rwsem;
+ struct attribute_group agrp;
+ const struct tsm_measurements *tm;
+ bool in_sync;
+ struct bin_attribute mrs[];
+};
+
+static ssize_t tm_digest_read(struct file *filp, struct kobject *kobj,
+ const struct bin_attribute *attr, char *buffer,
+ loff_t off, size_t count)
+{
+ struct tm_context *ctx;
+ const struct tsm_measurement_register *mr;
+ int rc;
+
+ ctx = attr->private;
+ rc = down_read_interruptible(&ctx->rwsem);
+ if (rc)
+ return rc;
+
+ mr = &ctx->tm->mrs[attr - ctx->mrs];
+
+ /*
+ * @ctx->in_sync indicates if the MR cache is stale. It is a global
+ * instead of a per-MR flag for simplicity, as most (if not all) archs
+ * allow reading all MRs in oneshot.
+ *
+ * ctx->refresh() is necessary only for LIVE MRs, while others retain
+ * their values from their respective last ctx->write().
+ */
+ if ((mr->mr_flags & TSM_MR_F_LIVE) && !ctx->in_sync) {
+ up_read(&ctx->rwsem);
+
+ rc = down_write_killable(&ctx->rwsem);
+ if (rc)
+ return rc;
+
+ if (!ctx->in_sync) {
+ rc = ctx->tm->refresh(ctx->tm);
+ ctx->in_sync = !rc;
+ trace_tsm_mr_refresh(mr, rc);
+ }
+
+ downgrade_write(&ctx->rwsem);
+ }
+
+ memcpy(buffer, mr->mr_value + off, count);
+ trace_tsm_mr_read(mr);
+
+ up_read(&ctx->rwsem);
+ return rc ?: count;
+}
+
+static ssize_t tm_digest_write(struct file *filp, struct kobject *kobj,
+ const struct bin_attribute *attr, char *buffer,
+ loff_t off, size_t count)
+{
+ struct tm_context *ctx;
+ const struct tsm_measurement_register *mr;
+ ssize_t rc;
+
+ /* partial writes are not supported */
+ if (off != 0 || count != attr->size)
+ return -EINVAL;
+
+ ctx = attr->private;
+ mr = &ctx->tm->mrs[attr - ctx->mrs];
+
+ rc = down_write_killable(&ctx->rwsem);
+ if (rc)
+ return rc;
+
+ rc = ctx->tm->write(ctx->tm, mr, buffer);
+
+ /* mark MR cache stale */
+ if (!rc) {
+ ctx->in_sync = false;
+ trace_tsm_mr_write(mr, buffer);
+ }
+
+ up_write(&ctx->rwsem);
+ return rc ?: count;
+}
+
+/**
+ * tsm_mr_create_attribute_group() - creates an attribute group for measurement
+ * registers (MRs)
+ * @tm: pointer to &struct tsm_measurements containing the MR definitions.
+ *
+ * This function creates attributes corresponding to the MR definitions
+ * provided by @tm->mrs.
+ *
+ * The created attributes will reference @tm and its members. The caller must
+ * not free @tm until after tsm_mr_free_attribute_group() is called.
+ *
+ * Context: Process context. May sleep due to memory allocation.
+ *
+ * Return:
+ * * On success, the pointer to a an attribute group is returned; otherwise
+ * * %-EINVAL - Invalid MR definitions.
+ * * %-ENOMEM - Out of memory.
+ */
+const struct attribute_group *
+tsm_mr_create_attribute_group(const struct tsm_measurements *tm)
+{
+ size_t nlen;
+
+ if (!tm || !tm->mrs)
+ return ERR_PTR(-EINVAL);
+
+ /* aggregated length of all MR names */
+ nlen = 0;
+ for (size_t i = 0; i < tm->nr_mrs; ++i) {
+ if ((tm->mrs[i].mr_flags & TSM_MR_F_LIVE) && !tm->refresh)
+ return ERR_PTR(-EINVAL);
+
+ if ((tm->mrs[i].mr_flags & TSM_MR_F_WRITABLE) && !tm->write)
+ return ERR_PTR(-EINVAL);
+
+ if (!tm->mrs[i].mr_name)
+ return ERR_PTR(-EINVAL);
+
+ if (tm->mrs[i].mr_flags & TSM_MR_F_NOHASH)
+ continue;
+
+ if (tm->mrs[i].mr_hash >= HASH_ALGO__LAST)
+ return ERR_PTR(-EINVAL);
+
+ /* MR sysfs attribute names have the form of MRNAME:HASH */
+ nlen += strlen(tm->mrs[i].mr_name) + 1 +
+ strlen(hash_algo_name[tm->mrs[i].mr_hash]) + 1;
+ }
+
+ /*
+ * @attrs and the MR name strings are combined into a single allocation
+ * so that we don't have to free MR names one-by-one in
+ * tsm_mr_free_attribute_group()
+ */
+ const struct bin_attribute * const *attrs __free(kfree) =
+ kzalloc(sizeof(*attrs) * (tm->nr_mrs + 1) + nlen, GFP_KERNEL);
+ struct tm_context *ctx __free(kfree) =
+ kzalloc(struct_size(ctx, mrs, tm->nr_mrs), GFP_KERNEL);
+ char *name, *end;
+
+ if (!ctx || !attrs)
+ return ERR_PTR(-ENOMEM);
+
+ /* @attrs is followed immediately by MR name strings */
+ name = (char *)&attrs[tm->nr_mrs + 1];
+ end = name + nlen;
+
+ for (size_t i = 0; i < tm->nr_mrs; ++i) {
+ /* break const for init */
+ struct bin_attribute **bas = (struct bin_attribute **)attrs;
+
+ bas[i] = &ctx->mrs[i];
+ sysfs_bin_attr_init(bas[i]);
+
+ if (tm->mrs[i].mr_flags & TSM_MR_F_NOHASH)
+ bas[i]->attr.name = tm->mrs[i].mr_name;
+ else if (name < end) {
+ bas[i]->attr.name = name;
+ name += snprintf(name, end - name, "%s:%s",
+ tm->mrs[i].mr_name,
+ hash_algo_name[tm->mrs[i].mr_hash]);
+ ++name;
+ } else
+ return ERR_PTR(-EINVAL);
+
+ /* check for duplicated MR definitions */
+ for (size_t j = 0; j < i; ++j)
+ if (!strcmp(bas[i]->attr.name, bas[j]->attr.name))
+ return ERR_PTR(-EINVAL);
+
+ if (tm->mrs[i].mr_flags & TSM_MR_F_READABLE) {
+ bas[i]->attr.mode |= 0444;
+ bas[i]->read_new = tm_digest_read;
+ }
+
+ if (tm->mrs[i].mr_flags & TSM_MR_F_WRITABLE) {
+ bas[i]->attr.mode |= 0200;
+ bas[i]->write_new = tm_digest_write;
+ }
+
+ bas[i]->size = tm->mrs[i].mr_size;
+ bas[i]->private = ctx;
+ }
+
+ if (name != end)
+ return ERR_PTR(-EINVAL);
+
+ init_rwsem(&ctx->rwsem);
+ ctx->agrp.name = "measurements";
+ ctx->agrp.bin_attrs_new = no_free_ptr(attrs);
+ ctx->tm = tm;
+ return &no_free_ptr(ctx)->agrp;
+}
+EXPORT_SYMBOL_GPL(tsm_mr_create_attribute_group);
+
+/**
+ * tsm_mr_free_attribute_group() - frees the attribute group returned by
+ * tsm_mr_create_attribute_group()
+ * @attr_grp: attribute group returned by tsm_mr_create_attribute_group()
+ *
+ * Context: Process context.
+ */
+void tsm_mr_free_attribute_group(const struct attribute_group *attr_grp)
+{
+ if (!IS_ERR_OR_NULL(attr_grp)) {
+ kfree(attr_grp->bin_attrs);
+ kfree(container_of(attr_grp, struct tm_context, agrp));
+ }
+}
+EXPORT_SYMBOL_GPL(tsm_mr_free_attribute_group);
diff --git a/drivers/virt/coco/tdx-guest/Kconfig b/drivers/virt/coco/tdx-guest/Kconfig
index 22dd59e19431..dbbdc14383b1 100644
--- a/drivers/virt/coco/tdx-guest/Kconfig
+++ b/drivers/virt/coco/tdx-guest/Kconfig
@@ -2,6 +2,7 @@ config TDX_GUEST_DRIVER
tristate "TDX Guest driver"
depends on INTEL_TDX_GUEST
select TSM_REPORTS
+ select TSM_MEASUREMENTS
help
The driver provides userspace interface to communicate with
the TDX module to request the TDX guest details like attestation
diff --git a/drivers/virt/coco/tdx-guest/tdx-guest.c b/drivers/virt/coco/tdx-guest/tdx-guest.c
index bd043838ab2e..4e239ec960c9 100644
--- a/drivers/virt/coco/tdx-guest/tdx-guest.c
+++ b/drivers/virt/coco/tdx-guest/tdx-guest.c
@@ -5,6 +5,8 @@
* Copyright (C) 2022 Intel Corporation
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
@@ -15,14 +17,146 @@
#include <linux/set_memory.h>
#include <linux/io.h>
#include <linux/delay.h>
+#include <linux/sockptr.h>
#include <linux/tsm.h>
-#include <linux/sizes.h>
+#include <linux/tsm-mr.h>
#include <uapi/linux/tdx-guest.h>
#include <asm/cpu_device_id.h>
#include <asm/tdx.h>
+/* TDREPORT buffer */
+static u8 *tdx_report_buf;
+
+/* Lock to serialize TDG.MR.REPORT and TDG.MR.RTMR.EXTEND TDCALLs */
+static DEFINE_MUTEX(mr_lock);
+
+/* TDREPORT fields */
+enum {
+ TDREPORT_reportdata = 128,
+ TDREPORT_tee_tcb_info = 256,
+ TDREPORT_tdinfo = TDREPORT_tee_tcb_info + 256,
+ TDREPORT_attributes = TDREPORT_tdinfo,
+ TDREPORT_xfam = TDREPORT_attributes + sizeof(u64),
+ TDREPORT_mrtd = TDREPORT_xfam + sizeof(u64),
+ TDREPORT_mrconfigid = TDREPORT_mrtd + SHA384_DIGEST_SIZE,
+ TDREPORT_mrowner = TDREPORT_mrconfigid + SHA384_DIGEST_SIZE,
+ TDREPORT_mrownerconfig = TDREPORT_mrowner + SHA384_DIGEST_SIZE,
+ TDREPORT_rtmr0 = TDREPORT_mrownerconfig + SHA384_DIGEST_SIZE,
+ TDREPORT_rtmr1 = TDREPORT_rtmr0 + SHA384_DIGEST_SIZE,
+ TDREPORT_rtmr2 = TDREPORT_rtmr1 + SHA384_DIGEST_SIZE,
+ TDREPORT_rtmr3 = TDREPORT_rtmr2 + SHA384_DIGEST_SIZE,
+ TDREPORT_servtd_hash = TDREPORT_rtmr3 + SHA384_DIGEST_SIZE,
+};
+
+static int tdx_do_report(sockptr_t data, sockptr_t tdreport)
+{
+ scoped_cond_guard(mutex_intr, return -EINTR, &mr_lock) {
+ u8 *reportdata = tdx_report_buf + TDREPORT_reportdata;
+ int ret;
+
+ if (!sockptr_is_null(data) &&
+ copy_from_sockptr(reportdata, data, TDX_REPORTDATA_LEN))
+ return -EFAULT;
+
+ ret = tdx_mcall_get_report0(reportdata, tdx_report_buf);
+ if (WARN_ONCE(ret, "tdx_mcall_get_report0() failed: %d", ret))
+ return ret;
+
+ if (!sockptr_is_null(tdreport) &&
+ copy_to_sockptr(tdreport, tdx_report_buf, TDX_REPORT_LEN))
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static int tdx_do_extend(u8 mr_ind, const u8 *data)
+{
+ scoped_cond_guard(mutex_intr, return -EINTR, &mr_lock) {
+ /*
+ * TDX requires @extend_buf to be 64-byte aligned.
+ * It's safe to use REPORTDATA buffer for that purpose because
+ * tdx_mr_report/extend_lock() are mutually exclusive.
+ */
+ u8 *extend_buf = tdx_report_buf + TDREPORT_reportdata;
+ int ret;
+
+ memcpy(extend_buf, data, SHA384_DIGEST_SIZE);
+
+ ret = tdx_mcall_extend_rtmr(mr_ind, extend_buf);
+ if (WARN_ONCE(ret, "tdx_mcall_extend_rtmr(%u) failed: %d", mr_ind, ret))
+ return ret;
+ }
+ return 0;
+}
+
+#define TDX_MR_(r) .mr_value = (void *)TDREPORT_##r, TSM_MR_(r, SHA384)
+static struct tsm_measurement_register tdx_mrs[] = {
+ { TDX_MR_(rtmr0) | TSM_MR_F_RTMR },
+ { TDX_MR_(rtmr1) | TSM_MR_F_RTMR },
+ { TDX_MR_(rtmr2) | TSM_MR_F_RTMR },
+ { TDX_MR_(rtmr3) | TSM_MR_F_RTMR },
+ { TDX_MR_(mrtd) },
+ { TDX_MR_(mrconfigid) | TSM_MR_F_NOHASH },
+ { TDX_MR_(mrowner) | TSM_MR_F_NOHASH },
+ { TDX_MR_(mrownerconfig) | TSM_MR_F_NOHASH },
+};
+#undef TDX_MR_
+
+static int tdx_mr_refresh(const struct tsm_measurements *tm)
+{
+ return tdx_do_report(KERNEL_SOCKPTR(NULL), KERNEL_SOCKPTR(NULL));
+}
+
+static int tdx_mr_extend(const struct tsm_measurements *tm,
+ const struct tsm_measurement_register *mr,
+ const u8 *data)
+{
+ return tdx_do_extend(mr - tm->mrs, data);
+}
+
+static struct tsm_measurements tdx_measurements = {
+ .mrs = tdx_mrs,
+ .nr_mrs = ARRAY_SIZE(tdx_mrs),
+ .refresh = tdx_mr_refresh,
+ .write = tdx_mr_extend,
+};
+
+static const struct attribute_group *tdx_mr_init(void)
+{
+ const struct attribute_group *g;
+ int rc;
+
+ u8 *buf __free(kfree) = kzalloc(TDX_REPORT_LEN, GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ tdx_report_buf = buf;
+ rc = tdx_mr_refresh(&tdx_measurements);
+ if (rc)
+ return ERR_PTR(rc);
+
+ /*
+ * @mr_value was initialized with the offset only, while the base
+ * address is being added here.
+ */
+ for (size_t i = 0; i < ARRAY_SIZE(tdx_mrs); ++i)
+ *(long *)&tdx_mrs[i].mr_value += (long)buf;
+
+ g = tsm_mr_create_attribute_group(&tdx_measurements);
+ if (!IS_ERR(g))
+ tdx_report_buf = no_free_ptr(buf);
+
+ return g;
+}
+
+static void tdx_mr_deinit(const struct attribute_group *mr_grp)
+{
+ tsm_mr_free_attribute_group(mr_grp);
+ kfree(tdx_report_buf);
+}
+
/*
* Intel's SGX QE implementation generally uses Quote size less
* than 8K (2K Quote data + ~5K of certificate blob).
@@ -68,37 +202,8 @@ static u32 getquote_timeout = 30;
static long tdx_get_report0(struct tdx_report_req __user *req)
{
- u8 *reportdata, *tdreport;
- long ret;
-
- reportdata = kmalloc(TDX_REPORTDATA_LEN, GFP_KERNEL);
- if (!reportdata)
- return -ENOMEM;
-
- tdreport = kzalloc(TDX_REPORT_LEN, GFP_KERNEL);
- if (!tdreport) {
- ret = -ENOMEM;
- goto out;
- }
-
- if (copy_from_user(reportdata, req->reportdata, TDX_REPORTDATA_LEN)) {
- ret = -EFAULT;
- goto out;
- }
-
- /* Generate TDREPORT0 using "TDG.MR.REPORT" TDCALL */
- ret = tdx_mcall_get_report0(reportdata, tdreport);
- if (ret)
- goto out;
-
- if (copy_to_user(req->tdreport, tdreport, TDX_REPORT_LEN))
- ret = -EFAULT;
-
-out:
- kfree(reportdata);
- kfree(tdreport);
-
- return ret;
+ return tdx_do_report(USER_SOCKPTR(req->reportdata),
+ USER_SOCKPTR(req->tdreport));
}
static void free_quote_buf(void *buf)
@@ -157,53 +262,24 @@ static int wait_for_quote_completion(struct tdx_quote_buf *quote_buf, u32 timeou
return (i == timeout) ? -ETIMEDOUT : 0;
}
-static int tdx_report_new(struct tsm_report *report, void *data)
+static int tdx_report_new_locked(struct tsm_report *report, void *data)
{
- u8 *buf, *reportdata = NULL, *tdreport = NULL;
+ u8 *buf;
struct tdx_quote_buf *quote_buf = quote_data;
struct tsm_report_desc *desc = &report->desc;
int ret;
u64 err;
- /* TODO: switch to guard(mutex_intr) */
- if (mutex_lock_interruptible(&quote_lock))
- return -EINTR;
-
/*
* If the previous request is timedout or interrupted, and the
* Quote buf status is still in GET_QUOTE_IN_FLIGHT (owned by
* VMM), don't permit any new request.
*/
- if (quote_buf->status == GET_QUOTE_IN_FLIGHT) {
- ret = -EBUSY;
- goto done;
- }
-
- if (desc->inblob_len != TDX_REPORTDATA_LEN) {
- ret = -EINVAL;
- goto done;
- }
-
- reportdata = kmalloc(TDX_REPORTDATA_LEN, GFP_KERNEL);
- if (!reportdata) {
- ret = -ENOMEM;
- goto done;
- }
+ if (quote_buf->status == GET_QUOTE_IN_FLIGHT)
+ return -EBUSY;
- tdreport = kzalloc(TDX_REPORT_LEN, GFP_KERNEL);
- if (!tdreport) {
- ret = -ENOMEM;
- goto done;
- }
-
- memcpy(reportdata, desc->inblob, desc->inblob_len);
-
- /* Generate TDREPORT0 using "TDG.MR.REPORT" TDCALL */
- ret = tdx_mcall_get_report0(reportdata, tdreport);
- if (ret) {
- pr_err("GetReport call failed\n");
- goto done;
- }
+ if (desc->inblob_len != TDX_REPORTDATA_LEN)
+ return -EINVAL;
memset(quote_data, 0, GET_QUOTE_BUF_SIZE);
@@ -211,26 +287,26 @@ static int tdx_report_new(struct tsm_report *report, void *data)
quote_buf->version = GET_QUOTE_CMD_VER;
quote_buf->in_len = TDX_REPORT_LEN;
- memcpy(quote_buf->data, tdreport, TDX_REPORT_LEN);
+ ret = tdx_do_report(KERNEL_SOCKPTR(desc->inblob),
+ KERNEL_SOCKPTR(quote_buf->data));
+ if (ret)
+ return ret;
err = tdx_hcall_get_quote(quote_data, GET_QUOTE_BUF_SIZE);
if (err) {
pr_err("GetQuote hypercall failed, status:%llx\n", err);
- ret = -EIO;
- goto done;
+ return -EIO;
}
ret = wait_for_quote_completion(quote_buf, getquote_timeout);
if (ret) {
pr_err("GetQuote request timedout\n");
- goto done;
+ return ret;
}
buf = kvmemdup(quote_buf->data, quote_buf->out_len, GFP_KERNEL);
- if (!buf) {
- ret = -ENOMEM;
- goto done;
- }
+ if (!buf)
+ return -ENOMEM;
report->outblob = buf;
report->outblob_len = quote_buf->out_len;
@@ -239,14 +315,16 @@ static int tdx_report_new(struct tsm_report *report, void *data)
* TODO: parse the PEM-formatted cert chain out of the quote buffer when
* provided
*/
-done:
- mutex_unlock(&quote_lock);
- kfree(reportdata);
- kfree(tdreport);
return ret;
}
+static int tdx_report_new(struct tsm_report *report, void *data)
+{
+ scoped_cond_guard(mutex_intr, return -EINTR, &quote_lock)
+ return tdx_report_new_locked(report, data);
+}
+
static bool tdx_report_attr_visible(int n)
{
switch (n) {
@@ -285,10 +363,16 @@ static const struct file_operations tdx_guest_fops = {
.unlocked_ioctl = tdx_guest_ioctl,
};
+static const struct attribute_group *tdx_attr_groups[] = {
+ NULL, /* measurements */
+ NULL
+};
+
static struct miscdevice tdx_misc_dev = {
.name = KBUILD_MODNAME,
.minor = MISC_DYNAMIC_MINOR,
.fops = &tdx_guest_fops,
+ .groups = tdx_attr_groups,
};
static const struct x86_cpu_id tdx_guest_ids[] = {
@@ -311,9 +395,13 @@ static int __init tdx_guest_init(void)
if (!x86_match_cpu(tdx_guest_ids))
return -ENODEV;
+ tdx_attr_groups[0] = tdx_mr_init();
+ if (IS_ERR(tdx_attr_groups[0]))
+ return PTR_ERR(tdx_attr_groups[0]);
+
ret = misc_register(&tdx_misc_dev);
if (ret)
- return ret;
+ goto deinit_mr;
quote_data = alloc_quote_buf();
if (!quote_data) {
@@ -332,6 +420,8 @@ free_quote:
free_quote_buf(quote_data);
free_misc:
misc_deregister(&tdx_misc_dev);
+deinit_mr:
+ tdx_mr_deinit(tdx_attr_groups[0]);
return ret;
}
@@ -342,6 +432,7 @@ static void __exit tdx_guest_exit(void)
tsm_report_unregister(&tdx_tsm_ops);
free_quote_buf(quote_data);
misc_deregister(&tdx_misc_dev);
+ tdx_mr_deinit(tdx_attr_groups[0]);
}
module_exit(tdx_guest_exit);