tpm20.c revision 346721
1/*-
2 * Copyright (c) 2018 Stormshield.
3 * Copyright (c) 2018 Semihalf.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
19 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
23 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
24 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: stable/11/sys/dev/tpm/tpm20.c 346721 2019-04-26 01:02:24Z mw $");
30
31#include "tpm20.h"
32
33MALLOC_DECLARE(M_TPM20);
34MALLOC_DEFINE(M_TPM20, "tpm_buffer", "buffer for tpm 2.0 driver");
35
36static void tpm20_discard_buffer(void *arg);
37static int tpm20_save_state(device_t dev, bool suspend);
38
39static d_open_t		tpm20_open;
40static d_close_t	tpm20_close;
41static d_read_t		tpm20_read;
42static d_write_t	tpm20_write;
43static d_ioctl_t	tpm20_ioctl;
44
45static struct cdevsw tpm20_cdevsw = {
46	.d_version = D_VERSION,
47	.d_open = tpm20_open,
48	.d_close = tpm20_close,
49	.d_read = tpm20_read,
50	.d_write = tpm20_write,
51	.d_ioctl = tpm20_ioctl,
52	.d_name = "tpm20",
53};
54
55int
56tpm20_read(struct cdev *dev, struct uio *uio, int flags)
57{
58	struct tpm_sc *sc;
59	size_t bytes_to_transfer;
60	int result = 0;
61
62	sc = (struct tpm_sc *)dev->si_drv1;
63
64	callout_stop(&sc->discard_buffer_callout);
65	sx_xlock(&sc->dev_lock);
66
67	bytes_to_transfer = MIN(sc->pending_data_length, uio->uio_resid);
68	if (bytes_to_transfer > 0) {
69		result = uiomove((caddr_t) sc->buf, bytes_to_transfer, uio);
70		memset(sc->buf, 0, TPM_BUFSIZE);
71		sc->pending_data_length = 0;
72		cv_signal(&sc->buf_cv);
73	} else {
74		result = ETIMEDOUT;
75	}
76
77	sx_xunlock(&sc->dev_lock);
78
79	return (result);
80}
81
82int
83tpm20_write(struct cdev *dev, struct uio *uio, int flags)
84{
85	struct tpm_sc *sc;
86	size_t byte_count;
87	int result = 0;
88
89	sc = (struct tpm_sc *)dev->si_drv1;
90
91	byte_count = uio->uio_resid;
92	if (byte_count < TPM_HEADER_SIZE) {
93		device_printf(sc->dev,
94		    "Requested transfer is too small\n");
95		return (EINVAL);
96	}
97
98	if (byte_count > TPM_BUFSIZE) {
99		device_printf(sc->dev,
100		    "Requested transfer is too large\n");
101		return (E2BIG);
102	}
103
104	sx_xlock(&sc->dev_lock);
105
106	while (sc->pending_data_length != 0)
107		cv_wait(&sc->buf_cv, &sc->dev_lock);
108
109	result = uiomove(sc->buf, byte_count, uio);
110	if (result != 0) {
111		sx_xunlock(&sc->dev_lock);
112		return (result);
113	}
114
115	result = sc->transmit(sc, byte_count);
116
117	if (result == 0)
118		callout_reset(&sc->discard_buffer_callout,
119		    TPM_READ_TIMEOUT / tick, tpm20_discard_buffer, sc);
120
121	sx_xunlock(&sc->dev_lock);
122	return (result);
123}
124
125static void tpm20_discard_buffer(void *arg)
126{
127	struct tpm_sc *sc;
128
129	sc = (struct tpm_sc *)arg;
130	if (callout_pending(&sc->discard_buffer_callout))
131		return;
132
133	sx_xlock(&sc->dev_lock);
134
135	memset(sc->buf, 0, TPM_BUFSIZE);
136	sc->pending_data_length = 0;
137
138	cv_signal(&sc->buf_cv);
139	sx_xunlock(&sc->dev_lock);
140
141	device_printf(sc->dev,
142	    "User failed to read buffer in time\n");
143}
144
145int
146tpm20_open(struct cdev *dev, int flag, int mode, struct thread *td)
147{
148
149	return (0);
150}
151
152int
153tpm20_close(struct cdev *dev, int flag, int mode, struct thread *td)
154{
155
156	return (0);
157}
158
159
160int
161tpm20_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
162    int flags, struct thread *td)
163{
164
165	return (ENOTTY);
166}
167
168int
169tpm20_init(struct tpm_sc *sc)
170{
171	struct make_dev_args args;
172	int result;
173
174	sc->buf = malloc(TPM_BUFSIZE, M_TPM20, M_WAITOK);
175	sx_init(&sc->dev_lock, "TPM driver lock");
176	cv_init(&sc->buf_cv, "TPM buffer cv");
177	callout_init(&sc->discard_buffer_callout, 1);
178	sc->pending_data_length = 0;
179
180	make_dev_args_init(&args);
181	args.mda_devsw = &tpm20_cdevsw;
182	args.mda_uid = UID_ROOT;
183	args.mda_gid = GID_WHEEL;
184	args.mda_mode = TPM_CDEV_PERM_FLAG;
185	args.mda_si_drv1 = sc;
186	result = make_dev_s(&args, &sc->sc_cdev, TPM_CDEV_NAME);
187	if (result != 0)
188		tpm20_release(sc);
189
190	return (result);
191
192}
193
194void
195tpm20_release(struct tpm_sc *sc)
196{
197
198	if (sc->buf != NULL)
199		free(sc->buf, M_TPM20);
200
201	sx_destroy(&sc->dev_lock);
202	cv_destroy(&sc->buf_cv);
203	if (sc->sc_cdev != NULL)
204		destroy_dev(sc->sc_cdev);
205}
206
207
208int
209tpm20_suspend(device_t dev)
210{
211	return (tpm20_save_state(dev, true));
212}
213
214int
215tpm20_shutdown(device_t dev)
216{
217	return (tpm20_save_state(dev, false));
218}
219
220static int
221tpm20_save_state(device_t dev, bool suspend)
222{
223	struct tpm_sc *sc;
224	uint8_t save_cmd[] = {
225		0x80, 0x01, /* TPM_ST_NO_SESSIONS tag*/
226		0x00, 0x00, 0x00, 0x0C, /* cmd length */
227		0x00, 0x00, 0x01, 0x45, 0x00, 0x00 /* cmd TPM_CC_Shutdown */
228	};
229
230	sc = device_get_softc(dev);
231
232	/*
233	 * Inform the TPM whether we are going to suspend or reboot/shutdown.
234	 */
235	if (suspend)
236		save_cmd[11] = 1;	/* TPM_SU_STATE */
237
238	if (sc == NULL || sc->buf == NULL)
239		return (0);
240
241	sx_xlock(&sc->dev_lock);
242
243	memcpy(sc->buf, save_cmd, sizeof(save_cmd));
244	sc->transmit(sc, sizeof(save_cmd));
245
246	sx_xunlock(&sc->dev_lock);
247
248	return (0);
249}
250
251int32_t
252tpm20_get_timeout(uint32_t command)
253{
254	int32_t timeout;
255
256	switch (command) {
257		case TPM_CC_CreatePrimary:
258		case TPM_CC_Create:
259		case TPM_CC_CreateLoaded:
260			timeout = TPM_TIMEOUT_LONG;
261			break;
262		case TPM_CC_SequenceComplete:
263		case TPM_CC_Startup:
264		case TPM_CC_SequenceUpdate:
265		case TPM_CC_GetCapability:
266		case TPM_CC_PCR_Extend:
267		case TPM_CC_EventSequenceComplete:
268		case TPM_CC_HashSequenceStart:
269			timeout = TPM_TIMEOUT_C;
270			break;
271		default:
272			timeout = TPM_TIMEOUT_B;
273			break;
274	}
275	return timeout;
276}
277