summaryrefslogtreecommitdiff
path: root/drivers/virt/coco/tsm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/virt/coco/tsm.c')
-rw-r--r--drivers/virt/coco/tsm.c425
1 files changed, 0 insertions, 425 deletions
diff --git a/drivers/virt/coco/tsm.c b/drivers/virt/coco/tsm.c
deleted file mode 100644
index d1c2db83a8ca..000000000000
--- a/drivers/virt/coco/tsm.c
+++ /dev/null
@@ -1,425 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright(c) 2023 Intel Corporation. All rights reserved. */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/tsm.h>
-#include <linux/err.h>
-#include <linux/slab.h>
-#include <linux/rwsem.h>
-#include <linux/string.h>
-#include <linux/module.h>
-#include <linux/cleanup.h>
-#include <linux/configfs.h>
-
-static struct tsm_provider {
- const struct tsm_ops *ops;
- const struct config_item_type *type;
- void *data;
-} provider;
-static DECLARE_RWSEM(tsm_rwsem);
-
-/**
- * DOC: Trusted Security Module (TSM) Attestation Report Interface
- *
- * The TSM report interface is a common provider of blobs that facilitate
- * attestation of a TVM (confidential computing guest) by an attestation
- * service. A TSM report combines a user-defined blob (likely a public-key with
- * a nonce for a key-exchange protocol) with a signed attestation report. That
- * combined blob is then used to obtain secrets provided by an agent that can
- * validate the attestation report. The expectation is that this interface is
- * invoked infrequently, however configfs allows for multiple agents to
- * own their own report generation instances to generate reports as
- * often as needed.
- *
- * The attestation report format is TSM provider specific, when / if a standard
- * materializes that can be published instead of the vendor layout. Until then
- * the 'provider' attribute indicates the format of 'outblob', and optionally
- * 'auxblob'.
- */
-
-struct tsm_report_state {
- struct tsm_report report;
- unsigned long write_generation;
- unsigned long read_generation;
- struct config_item cfg;
-};
-
-enum tsm_data_select {
- TSM_REPORT,
- TSM_CERTS,
-};
-
-static struct tsm_report *to_tsm_report(struct config_item *cfg)
-{
- struct tsm_report_state *state =
- container_of(cfg, struct tsm_report_state, cfg);
-
- return &state->report;
-}
-
-static struct tsm_report_state *to_state(struct tsm_report *report)
-{
- return container_of(report, struct tsm_report_state, report);
-}
-
-static int try_advance_write_generation(struct tsm_report *report)
-{
- struct tsm_report_state *state = to_state(report);
-
- lockdep_assert_held_write(&tsm_rwsem);
-
- /*
- * Malicious or broken userspace has written enough times for
- * read_generation == write_generation by modular arithmetic without an
- * interim read. Stop accepting updates until the current report
- * configuration is read.
- */
- if (state->write_generation == state->read_generation - 1)
- return -EBUSY;
- state->write_generation++;
- return 0;
-}
-
-static ssize_t tsm_report_privlevel_store(struct config_item *cfg,
- const char *buf, size_t len)
-{
- struct tsm_report *report = to_tsm_report(cfg);
- unsigned int val;
- int rc;
-
- rc = kstrtouint(buf, 0, &val);
- if (rc)
- return rc;
-
- /*
- * The valid privilege levels that a TSM might accept, if it accepts a
- * privilege level setting at all, are a max of TSM_PRIVLEVEL_MAX (see
- * SEV-SNP GHCB) and a minimum of a TSM selected floor value no less
- * than 0.
- */
- if (provider.ops->privlevel_floor > val || val > TSM_PRIVLEVEL_MAX)
- return -EINVAL;
-
- guard(rwsem_write)(&tsm_rwsem);
- rc = try_advance_write_generation(report);
- if (rc)
- return rc;
- report->desc.privlevel = val;
-
- return len;
-}
-CONFIGFS_ATTR_WO(tsm_report_, privlevel);
-
-static ssize_t tsm_report_privlevel_floor_show(struct config_item *cfg,
- char *buf)
-{
- guard(rwsem_read)(&tsm_rwsem);
- return sysfs_emit(buf, "%u\n", provider.ops->privlevel_floor);
-}
-CONFIGFS_ATTR_RO(tsm_report_, privlevel_floor);
-
-static ssize_t tsm_report_inblob_write(struct config_item *cfg,
- const void *buf, size_t count)
-{
- struct tsm_report *report = to_tsm_report(cfg);
- int rc;
-
- guard(rwsem_write)(&tsm_rwsem);
- rc = try_advance_write_generation(report);
- if (rc)
- return rc;
-
- report->desc.inblob_len = count;
- memcpy(report->desc.inblob, buf, count);
- return count;
-}
-CONFIGFS_BIN_ATTR_WO(tsm_report_, inblob, NULL, TSM_INBLOB_MAX);
-
-static ssize_t tsm_report_generation_show(struct config_item *cfg, char *buf)
-{
- struct tsm_report *report = to_tsm_report(cfg);
- struct tsm_report_state *state = to_state(report);
-
- guard(rwsem_read)(&tsm_rwsem);
- return sysfs_emit(buf, "%lu\n", state->write_generation);
-}
-CONFIGFS_ATTR_RO(tsm_report_, generation);
-
-static ssize_t tsm_report_provider_show(struct config_item *cfg, char *buf)
-{
- guard(rwsem_read)(&tsm_rwsem);
- return sysfs_emit(buf, "%s\n", provider.ops->name);
-}
-CONFIGFS_ATTR_RO(tsm_report_, provider);
-
-static ssize_t __read_report(struct tsm_report *report, void *buf, size_t count,
- enum tsm_data_select select)
-{
- loff_t offset = 0;
- ssize_t len;
- u8 *out;
-
- if (select == TSM_REPORT) {
- out = report->outblob;
- len = report->outblob_len;
- } else {
- out = report->auxblob;
- len = report->auxblob_len;
- }
-
- /*
- * Recall that a NULL @buf is configfs requesting the size of
- * the buffer.
- */
- if (!buf)
- return len;
- return memory_read_from_buffer(buf, count, &offset, out, len);
-}
-
-static ssize_t read_cached_report(struct tsm_report *report, void *buf,
- size_t count, enum tsm_data_select select)
-{
- struct tsm_report_state *state = to_state(report);
-
- guard(rwsem_read)(&tsm_rwsem);
- if (!report->desc.inblob_len)
- return -EINVAL;
-
- /*
- * A given TSM backend always fills in ->outblob regardless of
- * whether the report includes an auxblob or not.
- */
- if (!report->outblob ||
- state->read_generation != state->write_generation)
- return -EWOULDBLOCK;
-
- return __read_report(report, buf, count, select);
-}
-
-static ssize_t tsm_report_read(struct tsm_report *report, void *buf,
- size_t count, enum tsm_data_select select)
-{
- struct tsm_report_state *state = to_state(report);
- const struct tsm_ops *ops;
- ssize_t rc;
-
- /* try to read from the existing report if present and valid... */
- rc = read_cached_report(report, buf, count, select);
- if (rc >= 0 || rc != -EWOULDBLOCK)
- return rc;
-
- /* slow path, report may need to be regenerated... */
- guard(rwsem_write)(&tsm_rwsem);
- ops = provider.ops;
- if (!ops)
- return -ENOTTY;
- if (!report->desc.inblob_len)
- return -EINVAL;
-
- /* did another thread already generate this report? */
- if (report->outblob &&
- state->read_generation == state->write_generation)
- goto out;
-
- kvfree(report->outblob);
- kvfree(report->auxblob);
- report->outblob = NULL;
- report->auxblob = NULL;
- rc = ops->report_new(report, provider.data);
- if (rc < 0)
- return rc;
- state->read_generation = state->write_generation;
-out:
- return __read_report(report, buf, count, select);
-}
-
-static ssize_t tsm_report_outblob_read(struct config_item *cfg, void *buf,
- size_t count)
-{
- struct tsm_report *report = to_tsm_report(cfg);
-
- return tsm_report_read(report, buf, count, TSM_REPORT);
-}
-CONFIGFS_BIN_ATTR_RO(tsm_report_, outblob, NULL, TSM_OUTBLOB_MAX);
-
-static ssize_t tsm_report_auxblob_read(struct config_item *cfg, void *buf,
- size_t count)
-{
- struct tsm_report *report = to_tsm_report(cfg);
-
- return tsm_report_read(report, buf, count, TSM_CERTS);
-}
-CONFIGFS_BIN_ATTR_RO(tsm_report_, auxblob, NULL, TSM_OUTBLOB_MAX);
-
-#define TSM_DEFAULT_ATTRS() \
- &tsm_report_attr_generation, \
- &tsm_report_attr_provider
-
-static struct configfs_attribute *tsm_report_attrs[] = {
- TSM_DEFAULT_ATTRS(),
- NULL,
-};
-
-static struct configfs_attribute *tsm_report_extra_attrs[] = {
- TSM_DEFAULT_ATTRS(),
- &tsm_report_attr_privlevel,
- &tsm_report_attr_privlevel_floor,
- NULL,
-};
-
-#define TSM_DEFAULT_BIN_ATTRS() \
- &tsm_report_attr_inblob, \
- &tsm_report_attr_outblob
-
-static struct configfs_bin_attribute *tsm_report_bin_attrs[] = {
- TSM_DEFAULT_BIN_ATTRS(),
- NULL,
-};
-
-static struct configfs_bin_attribute *tsm_report_bin_extra_attrs[] = {
- TSM_DEFAULT_BIN_ATTRS(),
- &tsm_report_attr_auxblob,
- NULL,
-};
-
-static void tsm_report_item_release(struct config_item *cfg)
-{
- struct tsm_report *report = to_tsm_report(cfg);
- struct tsm_report_state *state = to_state(report);
-
- kvfree(report->auxblob);
- kvfree(report->outblob);
- kfree(state);
-}
-
-static struct configfs_item_operations tsm_report_item_ops = {
- .release = tsm_report_item_release,
-};
-
-const struct config_item_type tsm_report_default_type = {
- .ct_owner = THIS_MODULE,
- .ct_bin_attrs = tsm_report_bin_attrs,
- .ct_attrs = tsm_report_attrs,
- .ct_item_ops = &tsm_report_item_ops,
-};
-EXPORT_SYMBOL_GPL(tsm_report_default_type);
-
-const struct config_item_type tsm_report_extra_type = {
- .ct_owner = THIS_MODULE,
- .ct_bin_attrs = tsm_report_bin_extra_attrs,
- .ct_attrs = tsm_report_extra_attrs,
- .ct_item_ops = &tsm_report_item_ops,
-};
-EXPORT_SYMBOL_GPL(tsm_report_extra_type);
-
-static struct config_item *tsm_report_make_item(struct config_group *group,
- const char *name)
-{
- struct tsm_report_state *state;
-
- guard(rwsem_read)(&tsm_rwsem);
- if (!provider.ops)
- return ERR_PTR(-ENXIO);
-
- state = kzalloc(sizeof(*state), GFP_KERNEL);
- if (!state)
- return ERR_PTR(-ENOMEM);
-
- config_item_init_type_name(&state->cfg, name, provider.type);
- return &state->cfg;
-}
-
-static struct configfs_group_operations tsm_report_group_ops = {
- .make_item = tsm_report_make_item,
-};
-
-static const struct config_item_type tsm_reports_type = {
- .ct_owner = THIS_MODULE,
- .ct_group_ops = &tsm_report_group_ops,
-};
-
-static const struct config_item_type tsm_root_group_type = {
- .ct_owner = THIS_MODULE,
-};
-
-static struct configfs_subsystem tsm_configfs = {
- .su_group = {
- .cg_item = {
- .ci_namebuf = "tsm",
- .ci_type = &tsm_root_group_type,
- },
- },
- .su_mutex = __MUTEX_INITIALIZER(tsm_configfs.su_mutex),
-};
-
-int tsm_register(const struct tsm_ops *ops, void *priv,
- const struct config_item_type *type)
-{
- const struct tsm_ops *conflict;
-
- if (!type)
- type = &tsm_report_default_type;
- if (!(type == &tsm_report_default_type || type == &tsm_report_extra_type))
- return -EINVAL;
-
- guard(rwsem_write)(&tsm_rwsem);
- conflict = provider.ops;
- if (conflict) {
- pr_err("\"%s\" ops already registered\n", conflict->name);
- return -EBUSY;
- }
-
- provider.ops = ops;
- provider.data = priv;
- provider.type = type;
- return 0;
-}
-EXPORT_SYMBOL_GPL(tsm_register);
-
-int tsm_unregister(const struct tsm_ops *ops)
-{
- guard(rwsem_write)(&tsm_rwsem);
- if (ops != provider.ops)
- return -EBUSY;
- provider.ops = NULL;
- provider.data = NULL;
- provider.type = NULL;
- return 0;
-}
-EXPORT_SYMBOL_GPL(tsm_unregister);
-
-static struct config_group *tsm_report_group;
-
-static int __init tsm_init(void)
-{
- struct config_group *root = &tsm_configfs.su_group;
- struct config_group *tsm;
- int rc;
-
- config_group_init(root);
- rc = configfs_register_subsystem(&tsm_configfs);
- if (rc)
- return rc;
-
- tsm = configfs_register_default_group(root, "report",
- &tsm_reports_type);
- if (IS_ERR(tsm)) {
- configfs_unregister_subsystem(&tsm_configfs);
- return PTR_ERR(tsm);
- }
- tsm_report_group = tsm;
-
- return 0;
-}
-module_init(tsm_init);
-
-static void __exit tsm_exit(void)
-{
- configfs_unregister_default_group(tsm_report_group);
- configfs_unregister_subsystem(&tsm_configfs);
-}
-module_exit(tsm_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Provide Trusted Security Module attestation reports via configfs");