17515f45cSDan Williams // SPDX-License-Identifier: GPL-2.0-only
27515f45cSDan Williams /* Copyright(c) 2023 Intel Corporation. All rights reserved. */
37515f45cSDan Williams
47515f45cSDan Williams #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
57515f45cSDan Williams
67515f45cSDan Williams #include <linux/tsm.h>
77515f45cSDan Williams #include <linux/err.h>
87515f45cSDan Williams #include <linux/slab.h>
97515f45cSDan Williams #include <linux/rwsem.h>
107515f45cSDan Williams #include <linux/string.h>
117515f45cSDan Williams #include <linux/module.h>
127515f45cSDan Williams #include <linux/cleanup.h>
137515f45cSDan Williams #include <linux/configfs.h>
147515f45cSDan Williams
157515f45cSDan Williams static struct tsm_provider {
167515f45cSDan Williams const struct tsm_report_ops *ops;
177515f45cSDan Williams void *data;
18*a0372b38SDan Williams atomic_t count;
197515f45cSDan Williams } provider;
207515f45cSDan Williams static DECLARE_RWSEM(tsm_rwsem);
217515f45cSDan Williams
227515f45cSDan Williams /**
237515f45cSDan Williams * DOC: Trusted Security Module (TSM) Attestation Report Interface
247515f45cSDan Williams *
257515f45cSDan Williams * The TSM report interface is a common provider of blobs that facilitate
267515f45cSDan Williams * attestation of a TVM (confidential computing guest) by an attestation
277515f45cSDan Williams * service. A TSM report combines a user-defined blob (likely a public-key with
287515f45cSDan Williams * a nonce for a key-exchange protocol) with a signed attestation report. That
297515f45cSDan Williams * combined blob is then used to obtain secrets provided by an agent that can
307515f45cSDan Williams * validate the attestation report. The expectation is that this interface is
317515f45cSDan Williams * invoked infrequently, however configfs allows for multiple agents to
327515f45cSDan Williams * own their own report generation instances to generate reports as
337515f45cSDan Williams * often as needed.
347515f45cSDan Williams *
357515f45cSDan Williams * The attestation report format is TSM provider specific, when / if a standard
367515f45cSDan Williams * materializes that can be published instead of the vendor layout. Until then
377515f45cSDan Williams * the 'provider' attribute indicates the format of 'outblob', and optionally
387515f45cSDan Williams * 'auxblob' and 'manifestblob'.
397515f45cSDan Williams */
407515f45cSDan Williams
417515f45cSDan Williams struct tsm_report_state {
427515f45cSDan Williams struct tsm_report report;
437515f45cSDan Williams unsigned long write_generation;
447515f45cSDan Williams unsigned long read_generation;
457515f45cSDan Williams struct config_item cfg;
467515f45cSDan Williams };
477515f45cSDan Williams
487515f45cSDan Williams enum tsm_data_select {
497515f45cSDan Williams TSM_REPORT,
507515f45cSDan Williams TSM_CERTS,
517515f45cSDan Williams TSM_MANIFEST,
527515f45cSDan Williams };
537515f45cSDan Williams
to_tsm_report(struct config_item * cfg)547515f45cSDan Williams static struct tsm_report *to_tsm_report(struct config_item *cfg)
557515f45cSDan Williams {
567515f45cSDan Williams struct tsm_report_state *state =
577515f45cSDan Williams container_of(cfg, struct tsm_report_state, cfg);
587515f45cSDan Williams
597515f45cSDan Williams return &state->report;
607515f45cSDan Williams }
617515f45cSDan Williams
to_state(struct tsm_report * report)627515f45cSDan Williams static struct tsm_report_state *to_state(struct tsm_report *report)
637515f45cSDan Williams {
647515f45cSDan Williams return container_of(report, struct tsm_report_state, report);
657515f45cSDan Williams }
667515f45cSDan Williams
try_advance_write_generation(struct tsm_report * report)677515f45cSDan Williams static int try_advance_write_generation(struct tsm_report *report)
687515f45cSDan Williams {
697515f45cSDan Williams struct tsm_report_state *state = to_state(report);
707515f45cSDan Williams
717515f45cSDan Williams lockdep_assert_held_write(&tsm_rwsem);
727515f45cSDan Williams
737515f45cSDan Williams /*
747515f45cSDan Williams * Malicious or broken userspace has written enough times for
757515f45cSDan Williams * read_generation == write_generation by modular arithmetic without an
767515f45cSDan Williams * interim read. Stop accepting updates until the current report
777515f45cSDan Williams * configuration is read.
787515f45cSDan Williams */
797515f45cSDan Williams if (state->write_generation == state->read_generation - 1)
807515f45cSDan Williams return -EBUSY;
817515f45cSDan Williams state->write_generation++;
827515f45cSDan Williams return 0;
837515f45cSDan Williams }
847515f45cSDan Williams
tsm_report_privlevel_store(struct config_item * cfg,const char * buf,size_t len)857515f45cSDan Williams static ssize_t tsm_report_privlevel_store(struct config_item *cfg,
867515f45cSDan Williams const char *buf, size_t len)
877515f45cSDan Williams {
887515f45cSDan Williams struct tsm_report *report = to_tsm_report(cfg);
897515f45cSDan Williams unsigned int val;
907515f45cSDan Williams int rc;
917515f45cSDan Williams
927515f45cSDan Williams rc = kstrtouint(buf, 0, &val);
937515f45cSDan Williams if (rc)
947515f45cSDan Williams return rc;
957515f45cSDan Williams
96*a0372b38SDan Williams guard(rwsem_write)(&tsm_rwsem);
97*a0372b38SDan Williams if (!provider.ops)
98*a0372b38SDan Williams return -ENXIO;
99*a0372b38SDan Williams
1007515f45cSDan Williams /*
1017515f45cSDan Williams * The valid privilege levels that a TSM might accept, if it accepts a
1027515f45cSDan Williams * privilege level setting at all, are a max of TSM_PRIVLEVEL_MAX (see
1037515f45cSDan Williams * SEV-SNP GHCB) and a minimum of a TSM selected floor value no less
1047515f45cSDan Williams * than 0.
1057515f45cSDan Williams */
1067515f45cSDan Williams if (provider.ops->privlevel_floor > val || val > TSM_REPORT_PRIVLEVEL_MAX)
1077515f45cSDan Williams return -EINVAL;
1087515f45cSDan Williams
1097515f45cSDan Williams rc = try_advance_write_generation(report);
1107515f45cSDan Williams if (rc)
1117515f45cSDan Williams return rc;
1127515f45cSDan Williams report->desc.privlevel = val;
1137515f45cSDan Williams
1147515f45cSDan Williams return len;
1157515f45cSDan Williams }
1167515f45cSDan Williams CONFIGFS_ATTR_WO(tsm_report_, privlevel);
1177515f45cSDan Williams
tsm_report_privlevel_floor_show(struct config_item * cfg,char * buf)1187515f45cSDan Williams static ssize_t tsm_report_privlevel_floor_show(struct config_item *cfg,
1197515f45cSDan Williams char *buf)
1207515f45cSDan Williams {
1217515f45cSDan Williams guard(rwsem_read)(&tsm_rwsem);
122*a0372b38SDan Williams
123*a0372b38SDan Williams if (!provider.ops)
124*a0372b38SDan Williams return -ENXIO;
125*a0372b38SDan Williams
1267515f45cSDan Williams return sysfs_emit(buf, "%u\n", provider.ops->privlevel_floor);
1277515f45cSDan Williams }
1287515f45cSDan Williams CONFIGFS_ATTR_RO(tsm_report_, privlevel_floor);
1297515f45cSDan Williams
tsm_report_service_provider_store(struct config_item * cfg,const char * buf,size_t len)1307515f45cSDan Williams static ssize_t tsm_report_service_provider_store(struct config_item *cfg,
1317515f45cSDan Williams const char *buf, size_t len)
1327515f45cSDan Williams {
1337515f45cSDan Williams struct tsm_report *report = to_tsm_report(cfg);
1347515f45cSDan Williams size_t sp_len;
1357515f45cSDan Williams char *sp;
1367515f45cSDan Williams int rc;
1377515f45cSDan Williams
1387515f45cSDan Williams guard(rwsem_write)(&tsm_rwsem);
1397515f45cSDan Williams rc = try_advance_write_generation(report);
1407515f45cSDan Williams if (rc)
1417515f45cSDan Williams return rc;
1427515f45cSDan Williams
1437515f45cSDan Williams sp_len = (buf[len - 1] != '\n') ? len : len - 1;
1447515f45cSDan Williams
1457515f45cSDan Williams sp = kstrndup(buf, sp_len, GFP_KERNEL);
1467515f45cSDan Williams if (!sp)
1477515f45cSDan Williams return -ENOMEM;
1487515f45cSDan Williams kfree(report->desc.service_provider);
1497515f45cSDan Williams
1507515f45cSDan Williams report->desc.service_provider = sp;
1517515f45cSDan Williams
1527515f45cSDan Williams return len;
1537515f45cSDan Williams }
1547515f45cSDan Williams CONFIGFS_ATTR_WO(tsm_report_, service_provider);
1557515f45cSDan Williams
tsm_report_service_guid_store(struct config_item * cfg,const char * buf,size_t len)1567515f45cSDan Williams static ssize_t tsm_report_service_guid_store(struct config_item *cfg,
1577515f45cSDan Williams const char *buf, size_t len)
1587515f45cSDan Williams {
1597515f45cSDan Williams struct tsm_report *report = to_tsm_report(cfg);
1607515f45cSDan Williams int rc;
1617515f45cSDan Williams
1627515f45cSDan Williams guard(rwsem_write)(&tsm_rwsem);
1637515f45cSDan Williams rc = try_advance_write_generation(report);
1647515f45cSDan Williams if (rc)
1657515f45cSDan Williams return rc;
1667515f45cSDan Williams
1677515f45cSDan Williams report->desc.service_guid = guid_null;
1687515f45cSDan Williams
1697515f45cSDan Williams rc = guid_parse(buf, &report->desc.service_guid);
1707515f45cSDan Williams if (rc)
1717515f45cSDan Williams return rc;
1727515f45cSDan Williams
1737515f45cSDan Williams return len;
1747515f45cSDan Williams }
1757515f45cSDan Williams CONFIGFS_ATTR_WO(tsm_report_, service_guid);
1767515f45cSDan Williams
tsm_report_service_manifest_version_store(struct config_item * cfg,const char * buf,size_t len)1777515f45cSDan Williams static ssize_t tsm_report_service_manifest_version_store(struct config_item *cfg,
1787515f45cSDan Williams const char *buf, size_t len)
1797515f45cSDan Williams {
1807515f45cSDan Williams struct tsm_report *report = to_tsm_report(cfg);
1817515f45cSDan Williams unsigned int val;
1827515f45cSDan Williams int rc;
1837515f45cSDan Williams
1847515f45cSDan Williams rc = kstrtouint(buf, 0, &val);
1857515f45cSDan Williams if (rc)
1867515f45cSDan Williams return rc;
1877515f45cSDan Williams
1887515f45cSDan Williams guard(rwsem_write)(&tsm_rwsem);
1897515f45cSDan Williams rc = try_advance_write_generation(report);
1907515f45cSDan Williams if (rc)
1917515f45cSDan Williams return rc;
1927515f45cSDan Williams report->desc.service_manifest_version = val;
1937515f45cSDan Williams
1947515f45cSDan Williams return len;
1957515f45cSDan Williams }
1967515f45cSDan Williams CONFIGFS_ATTR_WO(tsm_report_, service_manifest_version);
1977515f45cSDan Williams
tsm_report_inblob_write(struct config_item * cfg,const void * buf,size_t count)1987515f45cSDan Williams static ssize_t tsm_report_inblob_write(struct config_item *cfg,
1997515f45cSDan Williams const void *buf, size_t count)
2007515f45cSDan Williams {
2017515f45cSDan Williams struct tsm_report *report = to_tsm_report(cfg);
2027515f45cSDan Williams int rc;
2037515f45cSDan Williams
2047515f45cSDan Williams guard(rwsem_write)(&tsm_rwsem);
2057515f45cSDan Williams rc = try_advance_write_generation(report);
2067515f45cSDan Williams if (rc)
2077515f45cSDan Williams return rc;
2087515f45cSDan Williams
2097515f45cSDan Williams report->desc.inblob_len = count;
2107515f45cSDan Williams memcpy(report->desc.inblob, buf, count);
2117515f45cSDan Williams return count;
2127515f45cSDan Williams }
2137515f45cSDan Williams CONFIGFS_BIN_ATTR_WO(tsm_report_, inblob, NULL, TSM_REPORT_INBLOB_MAX);
2147515f45cSDan Williams
tsm_report_generation_show(struct config_item * cfg,char * buf)2157515f45cSDan Williams static ssize_t tsm_report_generation_show(struct config_item *cfg, char *buf)
2167515f45cSDan Williams {
2177515f45cSDan Williams struct tsm_report *report = to_tsm_report(cfg);
2187515f45cSDan Williams struct tsm_report_state *state = to_state(report);
2197515f45cSDan Williams
2207515f45cSDan Williams guard(rwsem_read)(&tsm_rwsem);
2217515f45cSDan Williams return sysfs_emit(buf, "%lu\n", state->write_generation);
2227515f45cSDan Williams }
2237515f45cSDan Williams CONFIGFS_ATTR_RO(tsm_report_, generation);
2247515f45cSDan Williams
tsm_report_provider_show(struct config_item * cfg,char * buf)2257515f45cSDan Williams static ssize_t tsm_report_provider_show(struct config_item *cfg, char *buf)
2267515f45cSDan Williams {
2277515f45cSDan Williams guard(rwsem_read)(&tsm_rwsem);
228*a0372b38SDan Williams if (!provider.ops)
229*a0372b38SDan Williams return -ENXIO;
230*a0372b38SDan Williams
2317515f45cSDan Williams return sysfs_emit(buf, "%s\n", provider.ops->name);
2327515f45cSDan Williams }
2337515f45cSDan Williams CONFIGFS_ATTR_RO(tsm_report_, provider);
2347515f45cSDan Williams
__read_report(struct tsm_report * report,void * buf,size_t count,enum tsm_data_select select)2357515f45cSDan Williams static ssize_t __read_report(struct tsm_report *report, void *buf, size_t count,
2367515f45cSDan Williams enum tsm_data_select select)
2377515f45cSDan Williams {
2387515f45cSDan Williams loff_t offset = 0;
2397515f45cSDan Williams ssize_t len;
2407515f45cSDan Williams u8 *out;
2417515f45cSDan Williams
2427515f45cSDan Williams if (select == TSM_REPORT) {
2437515f45cSDan Williams out = report->outblob;
2447515f45cSDan Williams len = report->outblob_len;
2457515f45cSDan Williams } else if (select == TSM_MANIFEST) {
2467515f45cSDan Williams out = report->manifestblob;
2477515f45cSDan Williams len = report->manifestblob_len;
2487515f45cSDan Williams } else {
2497515f45cSDan Williams out = report->auxblob;
2507515f45cSDan Williams len = report->auxblob_len;
2517515f45cSDan Williams }
2527515f45cSDan Williams
2537515f45cSDan Williams /*
2547515f45cSDan Williams * Recall that a NULL @buf is configfs requesting the size of
2557515f45cSDan Williams * the buffer.
2567515f45cSDan Williams */
2577515f45cSDan Williams if (!buf)
2587515f45cSDan Williams return len;
2597515f45cSDan Williams return memory_read_from_buffer(buf, count, &offset, out, len);
2607515f45cSDan Williams }
2617515f45cSDan Williams
read_cached_report(struct tsm_report * report,void * buf,size_t count,enum tsm_data_select select)2627515f45cSDan Williams static ssize_t read_cached_report(struct tsm_report *report, void *buf,
2637515f45cSDan Williams size_t count, enum tsm_data_select select)
2647515f45cSDan Williams {
2657515f45cSDan Williams struct tsm_report_state *state = to_state(report);
2667515f45cSDan Williams
2677515f45cSDan Williams guard(rwsem_read)(&tsm_rwsem);
2687515f45cSDan Williams if (!report->desc.inblob_len)
2697515f45cSDan Williams return -EINVAL;
2707515f45cSDan Williams
2717515f45cSDan Williams /*
2727515f45cSDan Williams * A given TSM backend always fills in ->outblob regardless of
2737515f45cSDan Williams * whether the report includes an auxblob/manifestblob or not.
2747515f45cSDan Williams */
2757515f45cSDan Williams if (!report->outblob ||
2767515f45cSDan Williams state->read_generation != state->write_generation)
2777515f45cSDan Williams return -EWOULDBLOCK;
2787515f45cSDan Williams
2797515f45cSDan Williams return __read_report(report, buf, count, select);
2807515f45cSDan Williams }
2817515f45cSDan Williams
tsm_report_read(struct tsm_report * report,void * buf,size_t count,enum tsm_data_select select)2827515f45cSDan Williams static ssize_t tsm_report_read(struct tsm_report *report, void *buf,
2837515f45cSDan Williams size_t count, enum tsm_data_select select)
2847515f45cSDan Williams {
2857515f45cSDan Williams struct tsm_report_state *state = to_state(report);
2867515f45cSDan Williams const struct tsm_report_ops *ops;
2877515f45cSDan Williams ssize_t rc;
2887515f45cSDan Williams
2897515f45cSDan Williams /* try to read from the existing report if present and valid... */
2907515f45cSDan Williams rc = read_cached_report(report, buf, count, select);
2917515f45cSDan Williams if (rc >= 0 || rc != -EWOULDBLOCK)
2927515f45cSDan Williams return rc;
2937515f45cSDan Williams
2947515f45cSDan Williams /* slow path, report may need to be regenerated... */
2957515f45cSDan Williams guard(rwsem_write)(&tsm_rwsem);
2967515f45cSDan Williams ops = provider.ops;
2977515f45cSDan Williams if (!ops)
298*a0372b38SDan Williams return -ENXIO;
2997515f45cSDan Williams if (!report->desc.inblob_len)
3007515f45cSDan Williams return -EINVAL;
3017515f45cSDan Williams
3027515f45cSDan Williams /* did another thread already generate this report? */
3037515f45cSDan Williams if (report->outblob &&
3047515f45cSDan Williams state->read_generation == state->write_generation)
3057515f45cSDan Williams goto out;
3067515f45cSDan Williams
3077515f45cSDan Williams kvfree(report->outblob);
3087515f45cSDan Williams kvfree(report->auxblob);
3097515f45cSDan Williams kvfree(report->manifestblob);
3107515f45cSDan Williams report->outblob = NULL;
3117515f45cSDan Williams report->auxblob = NULL;
3127515f45cSDan Williams report->manifestblob = NULL;
3137515f45cSDan Williams rc = ops->report_new(report, provider.data);
3147515f45cSDan Williams if (rc < 0)
3157515f45cSDan Williams return rc;
3167515f45cSDan Williams state->read_generation = state->write_generation;
3177515f45cSDan Williams out:
3187515f45cSDan Williams return __read_report(report, buf, count, select);
3197515f45cSDan Williams }
3207515f45cSDan Williams
tsm_report_outblob_read(struct config_item * cfg,void * buf,size_t count)3217515f45cSDan Williams static ssize_t tsm_report_outblob_read(struct config_item *cfg, void *buf,
3227515f45cSDan Williams size_t count)
3237515f45cSDan Williams {
3247515f45cSDan Williams struct tsm_report *report = to_tsm_report(cfg);
3257515f45cSDan Williams
3267515f45cSDan Williams return tsm_report_read(report, buf, count, TSM_REPORT);
3277515f45cSDan Williams }
3287515f45cSDan Williams CONFIGFS_BIN_ATTR_RO(tsm_report_, outblob, NULL, TSM_REPORT_OUTBLOB_MAX);
3297515f45cSDan Williams
tsm_report_auxblob_read(struct config_item * cfg,void * buf,size_t count)3307515f45cSDan Williams static ssize_t tsm_report_auxblob_read(struct config_item *cfg, void *buf,
3317515f45cSDan Williams size_t count)
3327515f45cSDan Williams {
3337515f45cSDan Williams struct tsm_report *report = to_tsm_report(cfg);
3347515f45cSDan Williams
3357515f45cSDan Williams return tsm_report_read(report, buf, count, TSM_CERTS);
3367515f45cSDan Williams }
3377515f45cSDan Williams CONFIGFS_BIN_ATTR_RO(tsm_report_, auxblob, NULL, TSM_REPORT_OUTBLOB_MAX);
3387515f45cSDan Williams
tsm_report_manifestblob_read(struct config_item * cfg,void * buf,size_t count)3397515f45cSDan Williams static ssize_t tsm_report_manifestblob_read(struct config_item *cfg, void *buf,
3407515f45cSDan Williams size_t count)
3417515f45cSDan Williams {
3427515f45cSDan Williams struct tsm_report *report = to_tsm_report(cfg);
3437515f45cSDan Williams
3447515f45cSDan Williams return tsm_report_read(report, buf, count, TSM_MANIFEST);
3457515f45cSDan Williams }
3467515f45cSDan Williams CONFIGFS_BIN_ATTR_RO(tsm_report_, manifestblob, NULL, TSM_REPORT_OUTBLOB_MAX);
3477515f45cSDan Williams
3487515f45cSDan Williams static struct configfs_attribute *tsm_report_attrs[] = {
3497515f45cSDan Williams [TSM_REPORT_GENERATION] = &tsm_report_attr_generation,
3507515f45cSDan Williams [TSM_REPORT_PROVIDER] = &tsm_report_attr_provider,
3517515f45cSDan Williams [TSM_REPORT_PRIVLEVEL] = &tsm_report_attr_privlevel,
3527515f45cSDan Williams [TSM_REPORT_PRIVLEVEL_FLOOR] = &tsm_report_attr_privlevel_floor,
3537515f45cSDan Williams [TSM_REPORT_SERVICE_PROVIDER] = &tsm_report_attr_service_provider,
3547515f45cSDan Williams [TSM_REPORT_SERVICE_GUID] = &tsm_report_attr_service_guid,
3557515f45cSDan Williams [TSM_REPORT_SERVICE_MANIFEST_VER] = &tsm_report_attr_service_manifest_version,
3567515f45cSDan Williams NULL,
3577515f45cSDan Williams };
3587515f45cSDan Williams
3597515f45cSDan Williams static struct configfs_bin_attribute *tsm_report_bin_attrs[] = {
3607515f45cSDan Williams [TSM_REPORT_INBLOB] = &tsm_report_attr_inblob,
3617515f45cSDan Williams [TSM_REPORT_OUTBLOB] = &tsm_report_attr_outblob,
3627515f45cSDan Williams [TSM_REPORT_AUXBLOB] = &tsm_report_attr_auxblob,
3637515f45cSDan Williams [TSM_REPORT_MANIFESTBLOB] = &tsm_report_attr_manifestblob,
3647515f45cSDan Williams NULL,
3657515f45cSDan Williams };
3667515f45cSDan Williams
tsm_report_item_release(struct config_item * cfg)3677515f45cSDan Williams static void tsm_report_item_release(struct config_item *cfg)
3687515f45cSDan Williams {
3697515f45cSDan Williams struct tsm_report *report = to_tsm_report(cfg);
3707515f45cSDan Williams struct tsm_report_state *state = to_state(report);
3717515f45cSDan Williams
3727515f45cSDan Williams kvfree(report->manifestblob);
3737515f45cSDan Williams kvfree(report->auxblob);
3747515f45cSDan Williams kvfree(report->outblob);
3757515f45cSDan Williams kfree(report->desc.service_provider);
3767515f45cSDan Williams kfree(state);
3777515f45cSDan Williams }
3787515f45cSDan Williams
3797515f45cSDan Williams static struct configfs_item_operations tsm_report_item_ops = {
3807515f45cSDan Williams .release = tsm_report_item_release,
3817515f45cSDan Williams };
3827515f45cSDan Williams
tsm_report_is_visible(struct config_item * item,struct configfs_attribute * attr,int n)3837515f45cSDan Williams static bool tsm_report_is_visible(struct config_item *item,
3847515f45cSDan Williams struct configfs_attribute *attr, int n)
3857515f45cSDan Williams {
3867515f45cSDan Williams guard(rwsem_read)(&tsm_rwsem);
3877515f45cSDan Williams if (!provider.ops)
3887515f45cSDan Williams return false;
3897515f45cSDan Williams
3907515f45cSDan Williams if (!provider.ops->report_attr_visible)
3917515f45cSDan Williams return true;
3927515f45cSDan Williams
3937515f45cSDan Williams return provider.ops->report_attr_visible(n);
3947515f45cSDan Williams }
3957515f45cSDan Williams
tsm_report_is_bin_visible(struct config_item * item,struct configfs_bin_attribute * attr,int n)3967515f45cSDan Williams static bool tsm_report_is_bin_visible(struct config_item *item,
3977515f45cSDan Williams struct configfs_bin_attribute *attr, int n)
3987515f45cSDan Williams {
3997515f45cSDan Williams guard(rwsem_read)(&tsm_rwsem);
4007515f45cSDan Williams if (!provider.ops)
4017515f45cSDan Williams return false;
4027515f45cSDan Williams
4037515f45cSDan Williams if (!provider.ops->report_bin_attr_visible)
4047515f45cSDan Williams return true;
4057515f45cSDan Williams
4067515f45cSDan Williams return provider.ops->report_bin_attr_visible(n);
4077515f45cSDan Williams }
4087515f45cSDan Williams
4097515f45cSDan Williams static struct configfs_group_operations tsm_report_attr_group_ops = {
4107515f45cSDan Williams .is_visible = tsm_report_is_visible,
4117515f45cSDan Williams .is_bin_visible = tsm_report_is_bin_visible,
4127515f45cSDan Williams };
4137515f45cSDan Williams
4147515f45cSDan Williams static const struct config_item_type tsm_report_type = {
4157515f45cSDan Williams .ct_owner = THIS_MODULE,
4167515f45cSDan Williams .ct_bin_attrs = tsm_report_bin_attrs,
4177515f45cSDan Williams .ct_attrs = tsm_report_attrs,
4187515f45cSDan Williams .ct_item_ops = &tsm_report_item_ops,
4197515f45cSDan Williams .ct_group_ops = &tsm_report_attr_group_ops,
4207515f45cSDan Williams };
4217515f45cSDan Williams
tsm_report_make_item(struct config_group * group,const char * name)4227515f45cSDan Williams static struct config_item *tsm_report_make_item(struct config_group *group,
4237515f45cSDan Williams const char *name)
4247515f45cSDan Williams {
4257515f45cSDan Williams struct tsm_report_state *state;
4267515f45cSDan Williams
4277515f45cSDan Williams guard(rwsem_read)(&tsm_rwsem);
4287515f45cSDan Williams if (!provider.ops)
4297515f45cSDan Williams return ERR_PTR(-ENXIO);
4307515f45cSDan Williams
4317515f45cSDan Williams state = kzalloc(sizeof(*state), GFP_KERNEL);
4327515f45cSDan Williams if (!state)
4337515f45cSDan Williams return ERR_PTR(-ENOMEM);
4347515f45cSDan Williams
435*a0372b38SDan Williams atomic_inc(&provider.count);
4367515f45cSDan Williams config_item_init_type_name(&state->cfg, name, &tsm_report_type);
4377515f45cSDan Williams return &state->cfg;
4387515f45cSDan Williams }
4397515f45cSDan Williams
tsm_report_drop_item(struct config_group * group,struct config_item * item)440*a0372b38SDan Williams static void tsm_report_drop_item(struct config_group *group, struct config_item *item)
441*a0372b38SDan Williams {
442*a0372b38SDan Williams config_item_put(item);
443*a0372b38SDan Williams atomic_dec(&provider.count);
444*a0372b38SDan Williams }
445*a0372b38SDan Williams
4467515f45cSDan Williams static struct configfs_group_operations tsm_report_group_ops = {
4477515f45cSDan Williams .make_item = tsm_report_make_item,
448*a0372b38SDan Williams .drop_item = tsm_report_drop_item,
4497515f45cSDan Williams };
4507515f45cSDan Williams
4517515f45cSDan Williams static const struct config_item_type tsm_reports_type = {
4527515f45cSDan Williams .ct_owner = THIS_MODULE,
4537515f45cSDan Williams .ct_group_ops = &tsm_report_group_ops,
4547515f45cSDan Williams };
4557515f45cSDan Williams
4567515f45cSDan Williams static const struct config_item_type tsm_root_group_type = {
4577515f45cSDan Williams .ct_owner = THIS_MODULE,
4587515f45cSDan Williams };
4597515f45cSDan Williams
4607515f45cSDan Williams static struct configfs_subsystem tsm_configfs = {
4617515f45cSDan Williams .su_group = {
4627515f45cSDan Williams .cg_item = {
4637515f45cSDan Williams .ci_namebuf = "tsm",
4647515f45cSDan Williams .ci_type = &tsm_root_group_type,
4657515f45cSDan Williams },
4667515f45cSDan Williams },
4677515f45cSDan Williams .su_mutex = __MUTEX_INITIALIZER(tsm_configfs.su_mutex),
4687515f45cSDan Williams };
4697515f45cSDan Williams
tsm_report_register(const struct tsm_report_ops * ops,void * priv)4707515f45cSDan Williams int tsm_report_register(const struct tsm_report_ops *ops, void *priv)
4717515f45cSDan Williams {
4727515f45cSDan Williams const struct tsm_report_ops *conflict;
4737515f45cSDan Williams
4747515f45cSDan Williams guard(rwsem_write)(&tsm_rwsem);
4757515f45cSDan Williams conflict = provider.ops;
4767515f45cSDan Williams if (conflict) {
4777515f45cSDan Williams pr_err("\"%s\" ops already registered\n", conflict->name);
4787515f45cSDan Williams return -EBUSY;
4797515f45cSDan Williams }
4807515f45cSDan Williams
481*a0372b38SDan Williams if (atomic_read(&provider.count)) {
482*a0372b38SDan Williams pr_err("configfs/tsm/report not empty\n");
483*a0372b38SDan Williams return -EBUSY;
484*a0372b38SDan Williams }
485*a0372b38SDan Williams
4867515f45cSDan Williams provider.ops = ops;
4877515f45cSDan Williams provider.data = priv;
4887515f45cSDan Williams return 0;
4897515f45cSDan Williams }
4907515f45cSDan Williams EXPORT_SYMBOL_GPL(tsm_report_register);
4917515f45cSDan Williams
tsm_report_unregister(const struct tsm_report_ops * ops)4927515f45cSDan Williams int tsm_report_unregister(const struct tsm_report_ops *ops)
4937515f45cSDan Williams {
4947515f45cSDan Williams guard(rwsem_write)(&tsm_rwsem);
4957515f45cSDan Williams if (ops != provider.ops)
4967515f45cSDan Williams return -EBUSY;
497*a0372b38SDan Williams if (atomic_read(&provider.count))
498*a0372b38SDan Williams pr_warn("\"%s\" unregistered with items present in configfs/tsm/report\n",
499*a0372b38SDan Williams provider.ops->name);
5007515f45cSDan Williams provider.ops = NULL;
5017515f45cSDan Williams provider.data = NULL;
5027515f45cSDan Williams return 0;
5037515f45cSDan Williams }
5047515f45cSDan Williams EXPORT_SYMBOL_GPL(tsm_report_unregister);
5057515f45cSDan Williams
5067515f45cSDan Williams static struct config_group *tsm_report_group;
5077515f45cSDan Williams
tsm_report_init(void)5087515f45cSDan Williams static int __init tsm_report_init(void)
5097515f45cSDan Williams {
5107515f45cSDan Williams struct config_group *root = &tsm_configfs.su_group;
5117515f45cSDan Williams struct config_group *tsm;
5127515f45cSDan Williams int rc;
5137515f45cSDan Williams
5147515f45cSDan Williams config_group_init(root);
5157515f45cSDan Williams rc = configfs_register_subsystem(&tsm_configfs);
5167515f45cSDan Williams if (rc)
5177515f45cSDan Williams return rc;
5187515f45cSDan Williams
5197515f45cSDan Williams tsm = configfs_register_default_group(root, "report",
5207515f45cSDan Williams &tsm_reports_type);
5217515f45cSDan Williams if (IS_ERR(tsm)) {
5227515f45cSDan Williams configfs_unregister_subsystem(&tsm_configfs);
5237515f45cSDan Williams return PTR_ERR(tsm);
5247515f45cSDan Williams }
5257515f45cSDan Williams tsm_report_group = tsm;
5267515f45cSDan Williams
5277515f45cSDan Williams return 0;
5287515f45cSDan Williams }
5297515f45cSDan Williams module_init(tsm_report_init);
5307515f45cSDan Williams
tsm_report_exit(void)5317515f45cSDan Williams static void __exit tsm_report_exit(void)
5327515f45cSDan Williams {
5337515f45cSDan Williams configfs_unregister_default_group(tsm_report_group);
5347515f45cSDan Williams configfs_unregister_subsystem(&tsm_configfs);
5357515f45cSDan Williams }
5367515f45cSDan Williams module_exit(tsm_report_exit);
5377515f45cSDan Williams
5387515f45cSDan Williams MODULE_LICENSE("GPL");
5397515f45cSDan Williams MODULE_DESCRIPTION("Provide Trusted Security Module attestation reports via configfs");
540