xref: /linux/drivers/virt/coco/tsm.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
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