xref: /freebsd/sys/dev/tpm/tpm20.c (revision 0957b409)
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$");
30 
31 #include "tpm20.h"
32 
33 MALLOC_DECLARE(M_TPM20);
34 MALLOC_DEFINE(M_TPM20, "tpm_buffer", "buffer for tpm 2.0 driver");
35 
36 static void tpm20_discard_buffer(void *arg);
37 static int tpm20_save_state(device_t dev, bool suspend);
38 
39 static d_open_t		tpm20_open;
40 static d_close_t	tpm20_close;
41 static d_read_t		tpm20_read;
42 static d_write_t	tpm20_write;
43 static d_ioctl_t	tpm20_ioctl;
44 
45 static 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 
55 int
56 tpm20_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 
82 int
83 tpm20_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 
125 static 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 
145 int
146 tpm20_open(struct cdev *dev, int flag, int mode, struct thread *td)
147 {
148 
149 	return (0);
150 }
151 
152 int
153 tpm20_close(struct cdev *dev, int flag, int mode, struct thread *td)
154 {
155 
156 	return (0);
157 }
158 
159 
160 int
161 tpm20_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
162     int flags, struct thread *td)
163 {
164 
165 	return (ENOTTY);
166 }
167 
168 int
169 tpm20_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 
194 void
195 tpm20_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 
208 int
209 tpm20_suspend(device_t dev)
210 {
211 	return (tpm20_save_state(dev, true));
212 }
213 
214 int
215 tpm20_shutdown(device_t dev)
216 {
217 	return (tpm20_save_state(dev, false));
218 }
219 
220 static int
221 tpm20_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 
251 int32_t
252 tpm20_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