170e6f7e2SDan Williams // SPDX-License-Identifier: GPL-2.0-only
270e6f7e2SDan Williams /* Copyright(c) 2023 Intel Corporation. All rights reserved. */
370e6f7e2SDan Williams
470e6f7e2SDan Williams #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
570e6f7e2SDan Williams
670e6f7e2SDan Williams #include <linux/tsm.h>
770e6f7e2SDan Williams #include <linux/err.h>
870e6f7e2SDan Williams #include <linux/slab.h>
970e6f7e2SDan Williams #include <linux/rwsem.h>
1070e6f7e2SDan Williams #include <linux/string.h>
1170e6f7e2SDan Williams #include <linux/module.h>
1270e6f7e2SDan Williams #include <linux/cleanup.h>
1370e6f7e2SDan Williams #include <linux/configfs.h>
1470e6f7e2SDan Williams
1570e6f7e2SDan Williams static struct tsm_provider {
1670e6f7e2SDan Williams const struct tsm_ops *ops;
1770e6f7e2SDan Williams void *data;
1870e6f7e2SDan Williams } provider;
1970e6f7e2SDan Williams static DECLARE_RWSEM(tsm_rwsem);
2070e6f7e2SDan Williams
2170e6f7e2SDan Williams /**
2270e6f7e2SDan Williams * DOC: Trusted Security Module (TSM) Attestation Report Interface
2370e6f7e2SDan Williams *
2470e6f7e2SDan Williams * The TSM report interface is a common provider of blobs that facilitate
2570e6f7e2SDan Williams * attestation of a TVM (confidential computing guest) by an attestation
2670e6f7e2SDan Williams * service. A TSM report combines a user-defined blob (likely a public-key with
2770e6f7e2SDan Williams * a nonce for a key-exchange protocol) with a signed attestation report. That
2870e6f7e2SDan Williams * combined blob is then used to obtain secrets provided by an agent that can
2970e6f7e2SDan Williams * validate the attestation report. The expectation is that this interface is
3070e6f7e2SDan Williams * invoked infrequently, however configfs allows for multiple agents to
3170e6f7e2SDan Williams * own their own report generation instances to generate reports as
3270e6f7e2SDan Williams * often as needed.
3370e6f7e2SDan Williams *
3470e6f7e2SDan Williams * The attestation report format is TSM provider specific, when / if a standard
3570e6f7e2SDan Williams * materializes that can be published instead of the vendor layout. Until then
3670e6f7e2SDan Williams * the 'provider' attribute indicates the format of 'outblob', and optionally
37*627dc671STom Lendacky * 'auxblob' and 'manifestblob'.
3870e6f7e2SDan Williams */
3970e6f7e2SDan Williams
4070e6f7e2SDan Williams struct tsm_report_state {
4170e6f7e2SDan Williams struct tsm_report report;
4270e6f7e2SDan Williams unsigned long write_generation;
4370e6f7e2SDan Williams unsigned long read_generation;
4470e6f7e2SDan Williams struct config_item cfg;
4570e6f7e2SDan Williams };
4670e6f7e2SDan Williams
4770e6f7e2SDan Williams enum tsm_data_select {
4870e6f7e2SDan Williams TSM_REPORT,
4970e6f7e2SDan Williams TSM_CERTS,
50*627dc671STom Lendacky TSM_MANIFEST,
5170e6f7e2SDan Williams };
5270e6f7e2SDan Williams
to_tsm_report(struct config_item * cfg)5370e6f7e2SDan Williams static struct tsm_report *to_tsm_report(struct config_item *cfg)
5470e6f7e2SDan Williams {
5570e6f7e2SDan Williams struct tsm_report_state *state =
5670e6f7e2SDan Williams container_of(cfg, struct tsm_report_state, cfg);
5770e6f7e2SDan Williams
5870e6f7e2SDan Williams return &state->report;
5970e6f7e2SDan Williams }
6070e6f7e2SDan Williams
to_state(struct tsm_report * report)6170e6f7e2SDan Williams static struct tsm_report_state *to_state(struct tsm_report *report)
6270e6f7e2SDan Williams {
6370e6f7e2SDan Williams return container_of(report, struct tsm_report_state, report);
6470e6f7e2SDan Williams }
6570e6f7e2SDan Williams
try_advance_write_generation(struct tsm_report * report)6670e6f7e2SDan Williams static int try_advance_write_generation(struct tsm_report *report)
6770e6f7e2SDan Williams {
6870e6f7e2SDan Williams struct tsm_report_state *state = to_state(report);
6970e6f7e2SDan Williams
7070e6f7e2SDan Williams lockdep_assert_held_write(&tsm_rwsem);
7170e6f7e2SDan Williams
7270e6f7e2SDan Williams /*
7370e6f7e2SDan Williams * Malicious or broken userspace has written enough times for
7470e6f7e2SDan Williams * read_generation == write_generation by modular arithmetic without an
7570e6f7e2SDan Williams * interim read. Stop accepting updates until the current report
7670e6f7e2SDan Williams * configuration is read.
7770e6f7e2SDan Williams */
7870e6f7e2SDan Williams if (state->write_generation == state->read_generation - 1)
7970e6f7e2SDan Williams return -EBUSY;
8070e6f7e2SDan Williams state->write_generation++;
8170e6f7e2SDan Williams return 0;
8270e6f7e2SDan Williams }
8370e6f7e2SDan Williams
tsm_report_privlevel_store(struct config_item * cfg,const char * buf,size_t len)8470e6f7e2SDan Williams static ssize_t tsm_report_privlevel_store(struct config_item *cfg,
8570e6f7e2SDan Williams const char *buf, size_t len)
8670e6f7e2SDan Williams {
8770e6f7e2SDan Williams struct tsm_report *report = to_tsm_report(cfg);
8870e6f7e2SDan Williams unsigned int val;
8970e6f7e2SDan Williams int rc;
9070e6f7e2SDan Williams
9170e6f7e2SDan Williams rc = kstrtouint(buf, 0, &val);
9270e6f7e2SDan Williams if (rc)
9370e6f7e2SDan Williams return rc;
9470e6f7e2SDan Williams
9570e6f7e2SDan Williams /*
9670e6f7e2SDan Williams * The valid privilege levels that a TSM might accept, if it accepts a
9770e6f7e2SDan Williams * privilege level setting at all, are a max of TSM_PRIVLEVEL_MAX (see
9870e6f7e2SDan Williams * SEV-SNP GHCB) and a minimum of a TSM selected floor value no less
9970e6f7e2SDan Williams * than 0.
10070e6f7e2SDan Williams */
10170e6f7e2SDan Williams if (provider.ops->privlevel_floor > val || val > TSM_PRIVLEVEL_MAX)
10270e6f7e2SDan Williams return -EINVAL;
10370e6f7e2SDan Williams
10470e6f7e2SDan Williams guard(rwsem_write)(&tsm_rwsem);
10570e6f7e2SDan Williams rc = try_advance_write_generation(report);
10670e6f7e2SDan Williams if (rc)
10770e6f7e2SDan Williams return rc;
10870e6f7e2SDan Williams report->desc.privlevel = val;
10970e6f7e2SDan Williams
11070e6f7e2SDan Williams return len;
11170e6f7e2SDan Williams }
11270e6f7e2SDan Williams CONFIGFS_ATTR_WO(tsm_report_, privlevel);
11370e6f7e2SDan Williams
tsm_report_privlevel_floor_show(struct config_item * cfg,char * buf)11470e6f7e2SDan Williams static ssize_t tsm_report_privlevel_floor_show(struct config_item *cfg,
11570e6f7e2SDan Williams char *buf)
11670e6f7e2SDan Williams {
11770e6f7e2SDan Williams guard(rwsem_read)(&tsm_rwsem);
11870e6f7e2SDan Williams return sysfs_emit(buf, "%u\n", provider.ops->privlevel_floor);
11970e6f7e2SDan Williams }
12070e6f7e2SDan Williams CONFIGFS_ATTR_RO(tsm_report_, privlevel_floor);
12170e6f7e2SDan Williams
tsm_report_service_provider_store(struct config_item * cfg,const char * buf,size_t len)122*627dc671STom Lendacky static ssize_t tsm_report_service_provider_store(struct config_item *cfg,
123*627dc671STom Lendacky const char *buf, size_t len)
124*627dc671STom Lendacky {
125*627dc671STom Lendacky struct tsm_report *report = to_tsm_report(cfg);
126*627dc671STom Lendacky size_t sp_len;
127*627dc671STom Lendacky char *sp;
128*627dc671STom Lendacky int rc;
129*627dc671STom Lendacky
130*627dc671STom Lendacky guard(rwsem_write)(&tsm_rwsem);
131*627dc671STom Lendacky rc = try_advance_write_generation(report);
132*627dc671STom Lendacky if (rc)
133*627dc671STom Lendacky return rc;
134*627dc671STom Lendacky
135*627dc671STom Lendacky sp_len = (buf[len - 1] != '\n') ? len : len - 1;
136*627dc671STom Lendacky
137*627dc671STom Lendacky sp = kstrndup(buf, sp_len, GFP_KERNEL);
138*627dc671STom Lendacky if (!sp)
139*627dc671STom Lendacky return -ENOMEM;
140*627dc671STom Lendacky kfree(report->desc.service_provider);
141*627dc671STom Lendacky
142*627dc671STom Lendacky report->desc.service_provider = sp;
143*627dc671STom Lendacky
144*627dc671STom Lendacky return len;
145*627dc671STom Lendacky }
146*627dc671STom Lendacky CONFIGFS_ATTR_WO(tsm_report_, service_provider);
147*627dc671STom Lendacky
tsm_report_service_guid_store(struct config_item * cfg,const char * buf,size_t len)148*627dc671STom Lendacky static ssize_t tsm_report_service_guid_store(struct config_item *cfg,
149*627dc671STom Lendacky const char *buf, size_t len)
150*627dc671STom Lendacky {
151*627dc671STom Lendacky struct tsm_report *report = to_tsm_report(cfg);
152*627dc671STom Lendacky int rc;
153*627dc671STom Lendacky
154*627dc671STom Lendacky guard(rwsem_write)(&tsm_rwsem);
155*627dc671STom Lendacky rc = try_advance_write_generation(report);
156*627dc671STom Lendacky if (rc)
157*627dc671STom Lendacky return rc;
158*627dc671STom Lendacky
159*627dc671STom Lendacky report->desc.service_guid = guid_null;
160*627dc671STom Lendacky
161*627dc671STom Lendacky rc = guid_parse(buf, &report->desc.service_guid);
162*627dc671STom Lendacky if (rc)
163*627dc671STom Lendacky return rc;
164*627dc671STom Lendacky
165*627dc671STom Lendacky return len;
166*627dc671STom Lendacky }
167*627dc671STom Lendacky CONFIGFS_ATTR_WO(tsm_report_, service_guid);
168*627dc671STom Lendacky
tsm_report_service_manifest_version_store(struct config_item * cfg,const char * buf,size_t len)169*627dc671STom Lendacky static ssize_t tsm_report_service_manifest_version_store(struct config_item *cfg,
170*627dc671STom Lendacky const char *buf, size_t len)
171*627dc671STom Lendacky {
172*627dc671STom Lendacky struct tsm_report *report = to_tsm_report(cfg);
173*627dc671STom Lendacky unsigned int val;
174*627dc671STom Lendacky int rc;
175*627dc671STom Lendacky
176*627dc671STom Lendacky rc = kstrtouint(buf, 0, &val);
177*627dc671STom Lendacky if (rc)
178*627dc671STom Lendacky return rc;
179*627dc671STom Lendacky
180*627dc671STom Lendacky guard(rwsem_write)(&tsm_rwsem);
181*627dc671STom Lendacky rc = try_advance_write_generation(report);
182*627dc671STom Lendacky if (rc)
183*627dc671STom Lendacky return rc;
184*627dc671STom Lendacky report->desc.service_manifest_version = val;
185*627dc671STom Lendacky
186*627dc671STom Lendacky return len;
187*627dc671STom Lendacky }
188*627dc671STom Lendacky CONFIGFS_ATTR_WO(tsm_report_, service_manifest_version);
189*627dc671STom Lendacky
tsm_report_inblob_write(struct config_item * cfg,const void * buf,size_t count)19070e6f7e2SDan Williams static ssize_t tsm_report_inblob_write(struct config_item *cfg,
19170e6f7e2SDan Williams const void *buf, size_t count)
19270e6f7e2SDan Williams {
19370e6f7e2SDan Williams struct tsm_report *report = to_tsm_report(cfg);
19470e6f7e2SDan Williams int rc;
19570e6f7e2SDan Williams
19670e6f7e2SDan Williams guard(rwsem_write)(&tsm_rwsem);
19770e6f7e2SDan Williams rc = try_advance_write_generation(report);
19870e6f7e2SDan Williams if (rc)
19970e6f7e2SDan Williams return rc;
20070e6f7e2SDan Williams
20170e6f7e2SDan Williams report->desc.inblob_len = count;
20270e6f7e2SDan Williams memcpy(report->desc.inblob, buf, count);
20370e6f7e2SDan Williams return count;
20470e6f7e2SDan Williams }
20570e6f7e2SDan Williams CONFIGFS_BIN_ATTR_WO(tsm_report_, inblob, NULL, TSM_INBLOB_MAX);
20670e6f7e2SDan Williams
tsm_report_generation_show(struct config_item * cfg,char * buf)20770e6f7e2SDan Williams static ssize_t tsm_report_generation_show(struct config_item *cfg, char *buf)
20870e6f7e2SDan Williams {
20970e6f7e2SDan Williams struct tsm_report *report = to_tsm_report(cfg);
21070e6f7e2SDan Williams struct tsm_report_state *state = to_state(report);
21170e6f7e2SDan Williams
21270e6f7e2SDan Williams guard(rwsem_read)(&tsm_rwsem);
21370e6f7e2SDan Williams return sysfs_emit(buf, "%lu\n", state->write_generation);
21470e6f7e2SDan Williams }
21570e6f7e2SDan Williams CONFIGFS_ATTR_RO(tsm_report_, generation);
21670e6f7e2SDan Williams
tsm_report_provider_show(struct config_item * cfg,char * buf)21770e6f7e2SDan Williams static ssize_t tsm_report_provider_show(struct config_item *cfg, char *buf)
21870e6f7e2SDan Williams {
21970e6f7e2SDan Williams guard(rwsem_read)(&tsm_rwsem);
22070e6f7e2SDan Williams return sysfs_emit(buf, "%s\n", provider.ops->name);
22170e6f7e2SDan Williams }
22270e6f7e2SDan Williams CONFIGFS_ATTR_RO(tsm_report_, provider);
22370e6f7e2SDan Williams
__read_report(struct tsm_report * report,void * buf,size_t count,enum tsm_data_select select)22470e6f7e2SDan Williams static ssize_t __read_report(struct tsm_report *report, void *buf, size_t count,
22570e6f7e2SDan Williams enum tsm_data_select select)
22670e6f7e2SDan Williams {
22770e6f7e2SDan Williams loff_t offset = 0;
22870e6f7e2SDan Williams ssize_t len;
22970e6f7e2SDan Williams u8 *out;
23070e6f7e2SDan Williams
23170e6f7e2SDan Williams if (select == TSM_REPORT) {
23270e6f7e2SDan Williams out = report->outblob;
23370e6f7e2SDan Williams len = report->outblob_len;
234*627dc671STom Lendacky } else if (select == TSM_MANIFEST) {
235*627dc671STom Lendacky out = report->manifestblob;
236*627dc671STom Lendacky len = report->manifestblob_len;
23770e6f7e2SDan Williams } else {
23870e6f7e2SDan Williams out = report->auxblob;
23970e6f7e2SDan Williams len = report->auxblob_len;
24070e6f7e2SDan Williams }
24170e6f7e2SDan Williams
24270e6f7e2SDan Williams /*
24370e6f7e2SDan Williams * Recall that a NULL @buf is configfs requesting the size of
24470e6f7e2SDan Williams * the buffer.
24570e6f7e2SDan Williams */
24670e6f7e2SDan Williams if (!buf)
24770e6f7e2SDan Williams return len;
24870e6f7e2SDan Williams return memory_read_from_buffer(buf, count, &offset, out, len);
24970e6f7e2SDan Williams }
25070e6f7e2SDan Williams
read_cached_report(struct tsm_report * report,void * buf,size_t count,enum tsm_data_select select)25170e6f7e2SDan Williams static ssize_t read_cached_report(struct tsm_report *report, void *buf,
25270e6f7e2SDan Williams size_t count, enum tsm_data_select select)
25370e6f7e2SDan Williams {
25470e6f7e2SDan Williams struct tsm_report_state *state = to_state(report);
25570e6f7e2SDan Williams
25670e6f7e2SDan Williams guard(rwsem_read)(&tsm_rwsem);
25770e6f7e2SDan Williams if (!report->desc.inblob_len)
25870e6f7e2SDan Williams return -EINVAL;
25970e6f7e2SDan Williams
26070e6f7e2SDan Williams /*
26170e6f7e2SDan Williams * A given TSM backend always fills in ->outblob regardless of
262*627dc671STom Lendacky * whether the report includes an auxblob/manifestblob or not.
26370e6f7e2SDan Williams */
26470e6f7e2SDan Williams if (!report->outblob ||
26570e6f7e2SDan Williams state->read_generation != state->write_generation)
26670e6f7e2SDan Williams return -EWOULDBLOCK;
26770e6f7e2SDan Williams
26870e6f7e2SDan Williams return __read_report(report, buf, count, select);
26970e6f7e2SDan Williams }
27070e6f7e2SDan Williams
tsm_report_read(struct tsm_report * report,void * buf,size_t count,enum tsm_data_select select)27170e6f7e2SDan Williams static ssize_t tsm_report_read(struct tsm_report *report, void *buf,
27270e6f7e2SDan Williams size_t count, enum tsm_data_select select)
27370e6f7e2SDan Williams {
27470e6f7e2SDan Williams struct tsm_report_state *state = to_state(report);
27570e6f7e2SDan Williams const struct tsm_ops *ops;
27670e6f7e2SDan Williams ssize_t rc;
27770e6f7e2SDan Williams
27870e6f7e2SDan Williams /* try to read from the existing report if present and valid... */
27970e6f7e2SDan Williams rc = read_cached_report(report, buf, count, select);
28070e6f7e2SDan Williams if (rc >= 0 || rc != -EWOULDBLOCK)
28170e6f7e2SDan Williams return rc;
28270e6f7e2SDan Williams
28370e6f7e2SDan Williams /* slow path, report may need to be regenerated... */
28470e6f7e2SDan Williams guard(rwsem_write)(&tsm_rwsem);
28570e6f7e2SDan Williams ops = provider.ops;
28670e6f7e2SDan Williams if (!ops)
28770e6f7e2SDan Williams return -ENOTTY;
28870e6f7e2SDan Williams if (!report->desc.inblob_len)
28970e6f7e2SDan Williams return -EINVAL;
29070e6f7e2SDan Williams
29170e6f7e2SDan Williams /* did another thread already generate this report? */
29270e6f7e2SDan Williams if (report->outblob &&
29370e6f7e2SDan Williams state->read_generation == state->write_generation)
29470e6f7e2SDan Williams goto out;
29570e6f7e2SDan Williams
29670e6f7e2SDan Williams kvfree(report->outblob);
29770e6f7e2SDan Williams kvfree(report->auxblob);
298*627dc671STom Lendacky kvfree(report->manifestblob);
29970e6f7e2SDan Williams report->outblob = NULL;
30070e6f7e2SDan Williams report->auxblob = NULL;
301*627dc671STom Lendacky report->manifestblob = NULL;
30270e6f7e2SDan Williams rc = ops->report_new(report, provider.data);
30370e6f7e2SDan Williams if (rc < 0)
30470e6f7e2SDan Williams return rc;
30570e6f7e2SDan Williams state->read_generation = state->write_generation;
30670e6f7e2SDan Williams out:
30770e6f7e2SDan Williams return __read_report(report, buf, count, select);
30870e6f7e2SDan Williams }
30970e6f7e2SDan Williams
tsm_report_outblob_read(struct config_item * cfg,void * buf,size_t count)31070e6f7e2SDan Williams static ssize_t tsm_report_outblob_read(struct config_item *cfg, void *buf,
31170e6f7e2SDan Williams size_t count)
31270e6f7e2SDan Williams {
31370e6f7e2SDan Williams struct tsm_report *report = to_tsm_report(cfg);
31470e6f7e2SDan Williams
31570e6f7e2SDan Williams return tsm_report_read(report, buf, count, TSM_REPORT);
31670e6f7e2SDan Williams }
31770e6f7e2SDan Williams CONFIGFS_BIN_ATTR_RO(tsm_report_, outblob, NULL, TSM_OUTBLOB_MAX);
31870e6f7e2SDan Williams
tsm_report_auxblob_read(struct config_item * cfg,void * buf,size_t count)31970e6f7e2SDan Williams static ssize_t tsm_report_auxblob_read(struct config_item *cfg, void *buf,
32070e6f7e2SDan Williams size_t count)
32170e6f7e2SDan Williams {
32270e6f7e2SDan Williams struct tsm_report *report = to_tsm_report(cfg);
32370e6f7e2SDan Williams
32470e6f7e2SDan Williams return tsm_report_read(report, buf, count, TSM_CERTS);
32570e6f7e2SDan Williams }
32670e6f7e2SDan Williams CONFIGFS_BIN_ATTR_RO(tsm_report_, auxblob, NULL, TSM_OUTBLOB_MAX);
32770e6f7e2SDan Williams
tsm_report_manifestblob_read(struct config_item * cfg,void * buf,size_t count)328*627dc671STom Lendacky static ssize_t tsm_report_manifestblob_read(struct config_item *cfg, void *buf,
329*627dc671STom Lendacky size_t count)
330*627dc671STom Lendacky {
331*627dc671STom Lendacky struct tsm_report *report = to_tsm_report(cfg);
332*627dc671STom Lendacky
333*627dc671STom Lendacky return tsm_report_read(report, buf, count, TSM_MANIFEST);
334*627dc671STom Lendacky }
335*627dc671STom Lendacky CONFIGFS_BIN_ATTR_RO(tsm_report_, manifestblob, NULL, TSM_OUTBLOB_MAX);
336*627dc671STom Lendacky
33770e6f7e2SDan Williams static struct configfs_attribute *tsm_report_attrs[] = {
33820dfee95STom Lendacky [TSM_REPORT_GENERATION] = &tsm_report_attr_generation,
33920dfee95STom Lendacky [TSM_REPORT_PROVIDER] = &tsm_report_attr_provider,
34020dfee95STom Lendacky [TSM_REPORT_PRIVLEVEL] = &tsm_report_attr_privlevel,
34120dfee95STom Lendacky [TSM_REPORT_PRIVLEVEL_FLOOR] = &tsm_report_attr_privlevel_floor,
342*627dc671STom Lendacky [TSM_REPORT_SERVICE_PROVIDER] = &tsm_report_attr_service_provider,
343*627dc671STom Lendacky [TSM_REPORT_SERVICE_GUID] = &tsm_report_attr_service_guid,
344*627dc671STom Lendacky [TSM_REPORT_SERVICE_MANIFEST_VER] = &tsm_report_attr_service_manifest_version,
34570e6f7e2SDan Williams NULL,
34670e6f7e2SDan Williams };
34770e6f7e2SDan Williams
34870e6f7e2SDan Williams static struct configfs_bin_attribute *tsm_report_bin_attrs[] = {
34920dfee95STom Lendacky [TSM_REPORT_INBLOB] = &tsm_report_attr_inblob,
35020dfee95STom Lendacky [TSM_REPORT_OUTBLOB] = &tsm_report_attr_outblob,
35120dfee95STom Lendacky [TSM_REPORT_AUXBLOB] = &tsm_report_attr_auxblob,
352*627dc671STom Lendacky [TSM_REPORT_MANIFESTBLOB] = &tsm_report_attr_manifestblob,
35370e6f7e2SDan Williams NULL,
35470e6f7e2SDan Williams };
35570e6f7e2SDan Williams
tsm_report_item_release(struct config_item * cfg)35670e6f7e2SDan Williams static void tsm_report_item_release(struct config_item *cfg)
35770e6f7e2SDan Williams {
35870e6f7e2SDan Williams struct tsm_report *report = to_tsm_report(cfg);
35970e6f7e2SDan Williams struct tsm_report_state *state = to_state(report);
36070e6f7e2SDan Williams
361*627dc671STom Lendacky kvfree(report->manifestblob);
36270e6f7e2SDan Williams kvfree(report->auxblob);
36370e6f7e2SDan Williams kvfree(report->outblob);
364*627dc671STom Lendacky kfree(report->desc.service_provider);
36570e6f7e2SDan Williams kfree(state);
36670e6f7e2SDan Williams }
36770e6f7e2SDan Williams
36870e6f7e2SDan Williams static struct configfs_item_operations tsm_report_item_ops = {
36970e6f7e2SDan Williams .release = tsm_report_item_release,
37070e6f7e2SDan Williams };
37170e6f7e2SDan Williams
tsm_report_is_visible(struct config_item * item,struct configfs_attribute * attr,int n)37220dfee95STom Lendacky static bool tsm_report_is_visible(struct config_item *item,
37320dfee95STom Lendacky struct configfs_attribute *attr, int n)
37420dfee95STom Lendacky {
37520dfee95STom Lendacky guard(rwsem_read)(&tsm_rwsem);
37620dfee95STom Lendacky if (!provider.ops)
37720dfee95STom Lendacky return false;
37820dfee95STom Lendacky
37920dfee95STom Lendacky if (!provider.ops->report_attr_visible)
38020dfee95STom Lendacky return true;
38120dfee95STom Lendacky
38220dfee95STom Lendacky return provider.ops->report_attr_visible(n);
38320dfee95STom Lendacky }
38420dfee95STom Lendacky
tsm_report_is_bin_visible(struct config_item * item,struct configfs_bin_attribute * attr,int n)38520dfee95STom Lendacky static bool tsm_report_is_bin_visible(struct config_item *item,
38620dfee95STom Lendacky struct configfs_bin_attribute *attr, int n)
38720dfee95STom Lendacky {
38820dfee95STom Lendacky guard(rwsem_read)(&tsm_rwsem);
38920dfee95STom Lendacky if (!provider.ops)
39020dfee95STom Lendacky return false;
39120dfee95STom Lendacky
39220dfee95STom Lendacky if (!provider.ops->report_bin_attr_visible)
39320dfee95STom Lendacky return true;
39420dfee95STom Lendacky
39520dfee95STom Lendacky return provider.ops->report_bin_attr_visible(n);
39620dfee95STom Lendacky }
39720dfee95STom Lendacky
39820dfee95STom Lendacky static struct configfs_group_operations tsm_report_attr_group_ops = {
39920dfee95STom Lendacky .is_visible = tsm_report_is_visible,
40020dfee95STom Lendacky .is_bin_visible = tsm_report_is_bin_visible,
40120dfee95STom Lendacky };
40220dfee95STom Lendacky
40320dfee95STom Lendacky static const struct config_item_type tsm_report_type = {
40470e6f7e2SDan Williams .ct_owner = THIS_MODULE,
40570e6f7e2SDan Williams .ct_bin_attrs = tsm_report_bin_attrs,
40670e6f7e2SDan Williams .ct_attrs = tsm_report_attrs,
40770e6f7e2SDan Williams .ct_item_ops = &tsm_report_item_ops,
40820dfee95STom Lendacky .ct_group_ops = &tsm_report_attr_group_ops,
40970e6f7e2SDan Williams };
41070e6f7e2SDan Williams
tsm_report_make_item(struct config_group * group,const char * name)41170e6f7e2SDan Williams static struct config_item *tsm_report_make_item(struct config_group *group,
41270e6f7e2SDan Williams const char *name)
41370e6f7e2SDan Williams {
41470e6f7e2SDan Williams struct tsm_report_state *state;
41570e6f7e2SDan Williams
41670e6f7e2SDan Williams guard(rwsem_read)(&tsm_rwsem);
41770e6f7e2SDan Williams if (!provider.ops)
41870e6f7e2SDan Williams return ERR_PTR(-ENXIO);
41970e6f7e2SDan Williams
42070e6f7e2SDan Williams state = kzalloc(sizeof(*state), GFP_KERNEL);
42170e6f7e2SDan Williams if (!state)
42270e6f7e2SDan Williams return ERR_PTR(-ENOMEM);
42370e6f7e2SDan Williams
42420dfee95STom Lendacky config_item_init_type_name(&state->cfg, name, &tsm_report_type);
42570e6f7e2SDan Williams return &state->cfg;
42670e6f7e2SDan Williams }
42770e6f7e2SDan Williams
42870e6f7e2SDan Williams static struct configfs_group_operations tsm_report_group_ops = {
42970e6f7e2SDan Williams .make_item = tsm_report_make_item,
43070e6f7e2SDan Williams };
43170e6f7e2SDan Williams
43270e6f7e2SDan Williams static const struct config_item_type tsm_reports_type = {
43370e6f7e2SDan Williams .ct_owner = THIS_MODULE,
43470e6f7e2SDan Williams .ct_group_ops = &tsm_report_group_ops,
43570e6f7e2SDan Williams };
43670e6f7e2SDan Williams
43770e6f7e2SDan Williams static const struct config_item_type tsm_root_group_type = {
43870e6f7e2SDan Williams .ct_owner = THIS_MODULE,
43970e6f7e2SDan Williams };
44070e6f7e2SDan Williams
44170e6f7e2SDan Williams static struct configfs_subsystem tsm_configfs = {
44270e6f7e2SDan Williams .su_group = {
44370e6f7e2SDan Williams .cg_item = {
44470e6f7e2SDan Williams .ci_namebuf = "tsm",
44570e6f7e2SDan Williams .ci_type = &tsm_root_group_type,
44670e6f7e2SDan Williams },
44770e6f7e2SDan Williams },
44870e6f7e2SDan Williams .su_mutex = __MUTEX_INITIALIZER(tsm_configfs.su_mutex),
44970e6f7e2SDan Williams };
45070e6f7e2SDan Williams
tsm_register(const struct tsm_ops * ops,void * priv)45120dfee95STom Lendacky int tsm_register(const struct tsm_ops *ops, void *priv)
45270e6f7e2SDan Williams {
45370e6f7e2SDan Williams const struct tsm_ops *conflict;
45470e6f7e2SDan Williams
45570e6f7e2SDan Williams guard(rwsem_write)(&tsm_rwsem);
45670e6f7e2SDan Williams conflict = provider.ops;
45770e6f7e2SDan Williams if (conflict) {
45870e6f7e2SDan Williams pr_err("\"%s\" ops already registered\n", conflict->name);
45970e6f7e2SDan Williams return -EBUSY;
46070e6f7e2SDan Williams }
46170e6f7e2SDan Williams
46270e6f7e2SDan Williams provider.ops = ops;
46370e6f7e2SDan Williams provider.data = priv;
46470e6f7e2SDan Williams return 0;
46570e6f7e2SDan Williams }
46670e6f7e2SDan Williams EXPORT_SYMBOL_GPL(tsm_register);
46770e6f7e2SDan Williams
tsm_unregister(const struct tsm_ops * ops)46870e6f7e2SDan Williams int tsm_unregister(const struct tsm_ops *ops)
46970e6f7e2SDan Williams {
47070e6f7e2SDan Williams guard(rwsem_write)(&tsm_rwsem);
47170e6f7e2SDan Williams if (ops != provider.ops)
47270e6f7e2SDan Williams return -EBUSY;
47370e6f7e2SDan Williams provider.ops = NULL;
47470e6f7e2SDan Williams provider.data = NULL;
47570e6f7e2SDan Williams return 0;
47670e6f7e2SDan Williams }
47770e6f7e2SDan Williams EXPORT_SYMBOL_GPL(tsm_unregister);
47870e6f7e2SDan Williams
47970e6f7e2SDan Williams static struct config_group *tsm_report_group;
48070e6f7e2SDan Williams
tsm_init(void)48170e6f7e2SDan Williams static int __init tsm_init(void)
48270e6f7e2SDan Williams {
48370e6f7e2SDan Williams struct config_group *root = &tsm_configfs.su_group;
48470e6f7e2SDan Williams struct config_group *tsm;
48570e6f7e2SDan Williams int rc;
48670e6f7e2SDan Williams
48770e6f7e2SDan Williams config_group_init(root);
48870e6f7e2SDan Williams rc = configfs_register_subsystem(&tsm_configfs);
48970e6f7e2SDan Williams if (rc)
49070e6f7e2SDan Williams return rc;
49170e6f7e2SDan Williams
49270e6f7e2SDan Williams tsm = configfs_register_default_group(root, "report",
49370e6f7e2SDan Williams &tsm_reports_type);
49470e6f7e2SDan Williams if (IS_ERR(tsm)) {
49570e6f7e2SDan Williams configfs_unregister_subsystem(&tsm_configfs);
49670e6f7e2SDan Williams return PTR_ERR(tsm);
49770e6f7e2SDan Williams }
49870e6f7e2SDan Williams tsm_report_group = tsm;
49970e6f7e2SDan Williams
50070e6f7e2SDan Williams return 0;
50170e6f7e2SDan Williams }
50270e6f7e2SDan Williams module_init(tsm_init);
50370e6f7e2SDan Williams
tsm_exit(void)50470e6f7e2SDan Williams static void __exit tsm_exit(void)
50570e6f7e2SDan Williams {
50670e6f7e2SDan Williams configfs_unregister_default_group(tsm_report_group);
50770e6f7e2SDan Williams configfs_unregister_subsystem(&tsm_configfs);
50870e6f7e2SDan Williams }
50970e6f7e2SDan Williams module_exit(tsm_exit);
51070e6f7e2SDan Williams
51170e6f7e2SDan Williams MODULE_LICENSE("GPL");
51270e6f7e2SDan Williams MODULE_DESCRIPTION("Provide Trusted Security Module attestation reports via configfs");
513