1 | /* $NetBSD: tpm.c,v 1.11 2014/07/25 08:10:37 dholland Exp $ */ |
2 | /* |
3 | * Copyright (c) 2008, 2009 Michael Shalayeff |
4 | * Copyright (c) 2009, 2010 Hans-Jörg Höxer |
5 | * All rights reserved. |
6 | * |
7 | * Permission to use, copy, modify, and distribute this software for any |
8 | * purpose with or without fee is hereby granted, provided that the above |
9 | * copyright notice and this permission notice appear in all copies. |
10 | * |
11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
15 | * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN |
16 | * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
17 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
18 | */ |
19 | |
20 | #include <sys/cdefs.h> |
21 | __KERNEL_RCSID(0, "$NetBSD: tpm.c,v 1.11 2014/07/25 08:10:37 dholland Exp $" ); |
22 | |
23 | #if 0 |
24 | #define TPM_DEBUG |
25 | #define aprint_debug_dev aprint_error_dev |
26 | #endif |
27 | |
28 | #include <sys/param.h> |
29 | #include <sys/systm.h> |
30 | #include <sys/kernel.h> |
31 | #include <sys/malloc.h> |
32 | #include <sys/proc.h> |
33 | #include <sys/device.h> |
34 | #include <sys/conf.h> |
35 | #include <sys/bus.h> |
36 | #include <sys/pmf.h> |
37 | |
38 | #include <dev/ic/tpmreg.h> |
39 | #include <dev/ic/tpmvar.h> |
40 | |
41 | /* Set when enabling legacy interface in host bridge. */ |
42 | int tpm_enabled; |
43 | |
44 | const struct { |
45 | uint32_t devid; |
46 | char name[32]; |
47 | int flags; |
48 | #define TPM_DEV_NOINTS 0x0001 |
49 | } tpm_devs[] = { |
50 | { 0x000615d1, "IFX SLD 9630 TT 1.1" , 0 }, |
51 | { 0x000b15d1, "IFX SLB 9635 TT 1.2" , 0 }, |
52 | { 0x100214e4, "Broadcom BCM0102" , TPM_DEV_NOINTS }, |
53 | { 0x00fe1050, "WEC WPCT200" , 0 }, |
54 | { 0x687119fa, "SNS SSX35" , 0 }, |
55 | { 0x2e4d5453, "STM ST19WP18" , 0 }, |
56 | { 0x32021114, "ATML 97SC3203" , TPM_DEV_NOINTS }, |
57 | { 0x10408086, "INTEL INTC0102" , 0 }, |
58 | { 0, "" , TPM_DEV_NOINTS }, |
59 | }; |
60 | |
61 | int tpm_tis12_irqinit(struct tpm_softc *, int, int); |
62 | |
63 | int tpm_waitfor_poll(struct tpm_softc *, uint8_t, int, void *); |
64 | int tpm_waitfor_int(struct tpm_softc *, uint8_t, int, void *, int); |
65 | int tpm_waitfor(struct tpm_softc *, uint8_t, int, void *); |
66 | int tpm_request_locality(struct tpm_softc *, int); |
67 | int tpm_getburst(struct tpm_softc *); |
68 | uint8_t tpm_status(struct tpm_softc *); |
69 | int tpm_tmotohz(int); |
70 | |
71 | static dev_type_open(tpmopen); |
72 | static dev_type_close(tpmclose); |
73 | static dev_type_read(tpmread); |
74 | static dev_type_read(tpmwrite); |
75 | static dev_type_ioctl(tpmioctl); |
76 | |
77 | extern struct cfdriver tpm_cd; |
78 | #define TPMUNIT(a) minor(a) |
79 | |
80 | const struct cdevsw tpm_cdevsw = { |
81 | .d_open = tpmopen, |
82 | .d_close = tpmclose, |
83 | .d_read = tpmread, |
84 | .d_write = tpmwrite, |
85 | .d_ioctl = tpmioctl, |
86 | .d_stop = nostop, |
87 | .d_tty = notty, |
88 | .d_poll = nopoll, |
89 | .d_mmap = nommap, |
90 | .d_kqfilter = nokqfilter, |
91 | .d_discard = nodiscard, |
92 | .d_flag = D_OTHER, |
93 | }; |
94 | |
95 | /* Probe TPM using TIS 1.2 interface. */ |
96 | int |
97 | tpm_tis12_probe(bus_space_tag_t bt, bus_space_handle_t bh) |
98 | { |
99 | uint32_t r; |
100 | uint8_t save, reg; |
101 | |
102 | r = bus_space_read_4(bt, bh, TPM_INTF_CAPABILITIES); |
103 | if (r == 0xffffffff) |
104 | return 0; |
105 | |
106 | #ifdef TPM_DEBUG |
107 | char buf[128]; |
108 | snprintb(buf, sizeof(buf), TPM_CAPBITS, r); |
109 | printf("%s: caps=%s\n" , __func__, buf); |
110 | #endif |
111 | if ((r & TPM_CAPSREQ) != TPM_CAPSREQ || |
112 | !(r & (TPM_INTF_INT_EDGE_RISING | TPM_INTF_INT_LEVEL_LOW))) { |
113 | #ifdef TPM_DEBUG |
114 | printf("%s: caps too low (caps=%s)\n" , __func__, buf); |
115 | #endif |
116 | return 0; |
117 | } |
118 | |
119 | save = bus_space_read_1(bt, bh, TPM_ACCESS); |
120 | bus_space_write_1(bt, bh, TPM_ACCESS, TPM_ACCESS_REQUEST_USE); |
121 | reg = bus_space_read_1(bt, bh, TPM_ACCESS); |
122 | if ((reg & TPM_ACCESS_VALID) && (reg & TPM_ACCESS_ACTIVE_LOCALITY) && |
123 | bus_space_read_4(bt, bh, TPM_ID) != 0xffffffff) |
124 | return 1; |
125 | |
126 | bus_space_write_1(bt, bh, TPM_ACCESS, save); |
127 | return 0; |
128 | } |
129 | |
130 | /* |
131 | * Setup interrupt vector if one is provided and interrupts are know to |
132 | * work on that particular chip. |
133 | */ |
134 | int |
135 | tpm_tis12_irqinit(struct tpm_softc *sc, int irq, int idx) |
136 | { |
137 | uint32_t r; |
138 | |
139 | if ((irq == -1) || (tpm_devs[idx].flags & TPM_DEV_NOINTS)) { |
140 | sc->sc_vector = -1; |
141 | return 0; |
142 | } |
143 | |
144 | /* Ack and disable all interrupts. */ |
145 | r = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE); |
146 | bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE, |
147 | r & ~TPM_GLOBAL_INT_ENABLE); |
148 | bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INT_STATUS, |
149 | bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INT_STATUS)); |
150 | #ifdef TPM_DEBUG |
151 | char buf[128]; |
152 | snprintb(buf, sizeof(buf), TPM_INTERRUPT_ENABLE_BITS, r); |
153 | aprint_debug_dev(sc->sc_dev, "%s: before ien %s\n" , __func__, buf); |
154 | #endif |
155 | |
156 | /* Program interrupt vector. */ |
157 | bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_INT_VECTOR, irq); |
158 | sc->sc_vector = irq; |
159 | |
160 | /* Program interrupt type. */ |
161 | r &= ~(TPM_INT_EDGE_RISING|TPM_INT_EDGE_FALLING|TPM_INT_LEVEL_HIGH| |
162 | TPM_INT_LEVEL_LOW); |
163 | r |= TPM_GLOBAL_INT_ENABLE|TPM_CMD_READY_INT|TPM_LOCALITY_CHANGE_INT| |
164 | TPM_STS_VALID_INT|TPM_DATA_AVAIL_INT; |
165 | if (sc->sc_capabilities & TPM_INTF_INT_EDGE_RISING) |
166 | r |= TPM_INT_EDGE_RISING; |
167 | else if (sc->sc_capabilities & TPM_INTF_INT_EDGE_FALLING) |
168 | r |= TPM_INT_EDGE_FALLING; |
169 | else if (sc->sc_capabilities & TPM_INTF_INT_LEVEL_HIGH) |
170 | r |= TPM_INT_LEVEL_HIGH; |
171 | else |
172 | r |= TPM_INT_LEVEL_LOW; |
173 | |
174 | bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE, r); |
175 | #ifdef TPM_DEBUG |
176 | snprintb(buf, sizeof(buf), TPM_INTERRUPT_ENABLE_BITS, r); |
177 | aprint_debug_dev(sc->sc_dev, "%s: after ien %s\n" , __func__, buf); |
178 | #endif |
179 | |
180 | return 0; |
181 | } |
182 | |
183 | /* Setup TPM using TIS 1.2 interface. */ |
184 | int |
185 | tpm_tis12_init(struct tpm_softc *sc, int irq, const char *name) |
186 | { |
187 | uint32_t r; |
188 | int i; |
189 | |
190 | r = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INTF_CAPABILITIES); |
191 | #ifdef TPM_DEBUG |
192 | char cbuf[128]; |
193 | snprintb(cbuf, sizeof(cbuf), TPM_CAPBITS, r); |
194 | aprint_debug_dev(sc->sc_dev, "%s: caps=%s " , __func__, cbuf); |
195 | #endif |
196 | if ((r & TPM_CAPSREQ) != TPM_CAPSREQ || |
197 | !(r & (TPM_INTF_INT_EDGE_RISING | TPM_INTF_INT_LEVEL_LOW))) { |
198 | char buf[128]; |
199 | snprintb(buf, sizeof(buf), TPM_CAPBITS, r); |
200 | aprint_error_dev(sc->sc_dev, "capabilities too low (caps=%s)\n" , |
201 | buf); |
202 | return 1; |
203 | } |
204 | sc->sc_capabilities = r; |
205 | |
206 | sc->sc_devid = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_ID); |
207 | sc->sc_rev = bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_REV); |
208 | |
209 | for (i = 0; tpm_devs[i].devid; i++) |
210 | if (tpm_devs[i].devid == sc->sc_devid) |
211 | break; |
212 | |
213 | if (tpm_devs[i].devid) |
214 | aprint_normal(": %s rev 0x%x\n" , |
215 | tpm_devs[i].name, sc->sc_rev); |
216 | else |
217 | aprint_normal(": device 0x%08x rev 0x%x\n" , |
218 | sc->sc_devid, sc->sc_rev); |
219 | |
220 | if (tpm_tis12_irqinit(sc, irq, i)) |
221 | return 1; |
222 | |
223 | if (tpm_request_locality(sc, 0)) |
224 | return 1; |
225 | |
226 | /* Abort whatever it thought it was doing. */ |
227 | bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_STS, TPM_STS_CMD_READY); |
228 | |
229 | return 0; |
230 | } |
231 | |
232 | int |
233 | tpm_request_locality(struct tpm_softc *sc, int l) |
234 | { |
235 | uint32_t r; |
236 | int to, rv; |
237 | |
238 | if (l != 0) |
239 | return EINVAL; |
240 | |
241 | if ((bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_ACCESS) & |
242 | (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) == |
243 | (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) |
244 | return 0; |
245 | |
246 | bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_ACCESS, |
247 | TPM_ACCESS_REQUEST_USE); |
248 | |
249 | to = tpm_tmotohz(TPM_ACCESS_TMO); |
250 | |
251 | while ((r = bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_ACCESS) & |
252 | (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) != |
253 | (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY) && to--) { |
254 | rv = tsleep(sc->sc_init, PRIBIO | PCATCH, "tpm_locality" , 1); |
255 | if (rv && rv != EWOULDBLOCK) { |
256 | #ifdef TPM_DEBUG |
257 | aprint_debug_dev(sc->sc_dev, "%s: interrupted %d\n" , |
258 | __func__, rv); |
259 | #endif |
260 | return rv; |
261 | } |
262 | } |
263 | |
264 | if ((r & (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) != |
265 | (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) { |
266 | #ifdef TPM_DEBUG |
267 | char buf[128]; |
268 | snprintb(buf, sizeof(buf), TPM_ACCESS_BITS, r); |
269 | aprint_debug_dev(sc->sc_dev, "%s: access %s\n" , __func__, buf); |
270 | #endif |
271 | return EBUSY; |
272 | } |
273 | |
274 | return 0; |
275 | } |
276 | |
277 | int |
278 | tpm_getburst(struct tpm_softc *sc) |
279 | { |
280 | int burst, to, rv; |
281 | |
282 | to = tpm_tmotohz(TPM_BURST_TMO); |
283 | |
284 | burst = 0; |
285 | while (burst == 0 && to--) { |
286 | /* |
287 | * Burst count has to be read from bits 8 to 23 without |
288 | * touching any other bits, eg. the actual status bits 0 |
289 | * to 7. |
290 | */ |
291 | burst = bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_STS + 1); |
292 | burst |= bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_STS + 2) |
293 | << 8; |
294 | #ifdef TPM_DEBUG |
295 | aprint_debug_dev(sc->sc_dev, "%s: read %d\n" , __func__, burst); |
296 | #endif |
297 | if (burst) |
298 | return burst; |
299 | |
300 | rv = tsleep(sc, PRIBIO | PCATCH, "tpm_getburst" , 1); |
301 | if (rv && rv != EWOULDBLOCK) { |
302 | return 0; |
303 | } |
304 | } |
305 | |
306 | return 0; |
307 | } |
308 | |
309 | uint8_t |
310 | tpm_status(struct tpm_softc *sc) |
311 | { |
312 | return bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_STS) & TPM_STS_MASK; |
313 | } |
314 | |
315 | int |
316 | tpm_tmotohz(int tmo) |
317 | { |
318 | struct timeval tv; |
319 | |
320 | tv.tv_sec = tmo / 1000; |
321 | tv.tv_usec = 1000 * (tmo % 1000); |
322 | |
323 | return tvtohz(&tv); |
324 | } |
325 | |
326 | /* Save TPM state on suspend. */ |
327 | bool |
328 | tpm_suspend(device_t dev, const pmf_qual_t *qual) |
329 | { |
330 | struct tpm_softc *sc = device_private(dev); |
331 | static const uint8_t command[] = { |
332 | 0, 193, /* TPM_TAG_RQU_COMMAND */ |
333 | 0, 0, 0, 10, /* Length in bytes */ |
334 | 0, 0, 0, 156 /* TPM_ORD_SaveStates */ |
335 | }; |
336 | uint8_t scratch[sizeof(command)]; |
337 | |
338 | /* |
339 | * Power down: We have to issue the SaveStates command. |
340 | */ |
341 | (*sc->sc_write)(sc, &command, sizeof(command)); |
342 | (*sc->sc_read)(sc, &scratch, sizeof(scratch), NULL, TPM_HDRSIZE); |
343 | #ifdef TPM_DEBUG |
344 | aprint_debug_dev(sc->sc_dev, "%s: power down\n" , __func__); |
345 | #endif |
346 | return true; |
347 | } |
348 | |
349 | /* |
350 | * Handle resume event. Actually nothing to do as the BIOS is supposed |
351 | * to restore the previously saved state. |
352 | */ |
353 | bool |
354 | tpm_resume(device_t dev, const pmf_qual_t *qual) |
355 | { |
356 | #ifdef TPM_DEBUG |
357 | struct tpm_softc *sc = device_private(dev); |
358 | aprint_debug_dev(sc->sc_dev, "%s: resume\n" , __func__); |
359 | #endif |
360 | return true; |
361 | } |
362 | |
363 | /* Wait for given status bits using polling. */ |
364 | int |
365 | tpm_waitfor_poll(struct tpm_softc *sc, uint8_t mask, int tmo, void *c) |
366 | { |
367 | int rv; |
368 | |
369 | /* |
370 | * Poll until either the requested condition or a time out is |
371 | * met. |
372 | */ |
373 | while (((sc->sc_stat = tpm_status(sc)) & mask) != mask && tmo--) { |
374 | rv = tsleep(c, PRIBIO | PCATCH, "tpm_poll" , 1); |
375 | if (rv && rv != EWOULDBLOCK) { |
376 | #ifdef TPM_DEBUG |
377 | aprint_debug_dev(sc->sc_dev, |
378 | "%s: interrupted %d\n" , __func__, rv); |
379 | #endif |
380 | return rv; |
381 | } |
382 | } |
383 | |
384 | return 0; |
385 | } |
386 | |
387 | /* Wait for given status bits using interrupts. */ |
388 | int |
389 | tpm_waitfor_int(struct tpm_softc *sc, uint8_t mask, int tmo, void *c, |
390 | int inttype) |
391 | { |
392 | int rv, to; |
393 | |
394 | /* Poll and return when condition is already met. */ |
395 | sc->sc_stat = tpm_status(sc); |
396 | if ((sc->sc_stat & mask) == mask) |
397 | return 0; |
398 | |
399 | /* |
400 | * Enable interrupt on tpm chip. Note that interrupts on our |
401 | * level (SPL_TTY) are disabled (see tpm{read,write} et al) and |
402 | * will not be delivered to the cpu until we call tsleep(9) below. |
403 | */ |
404 | bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE, |
405 | bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE) | |
406 | inttype); |
407 | bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE, |
408 | bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE) | |
409 | TPM_GLOBAL_INT_ENABLE); |
410 | |
411 | /* |
412 | * Poll once more to remedy the race between previous polling |
413 | * and enabling interrupts on the tpm chip. |
414 | */ |
415 | sc->sc_stat = tpm_status(sc); |
416 | if ((sc->sc_stat & mask) == mask) { |
417 | rv = 0; |
418 | goto out; |
419 | } |
420 | |
421 | to = tpm_tmotohz(tmo); |
422 | #ifdef TPM_DEBUG |
423 | aprint_debug_dev(sc->sc_dev, |
424 | "%s: sleeping for %d ticks on %p\n" , __func__, to, c); |
425 | #endif |
426 | /* |
427 | * tsleep(9) enables interrupts on the cpu and returns after |
428 | * wake up with interrupts disabled again. Note that interrupts |
429 | * generated by the tpm chip while being at SPL_TTY are not lost |
430 | * but held and delivered as soon as the cpu goes below SPL_TTY. |
431 | */ |
432 | rv = tsleep(c, PRIBIO | PCATCH, "tpm_wait" , to); |
433 | |
434 | sc->sc_stat = tpm_status(sc); |
435 | #ifdef TPM_DEBUG |
436 | char buf[128]; |
437 | snprintb(buf, sizeof(buf), TPM_STS_BITS, sc->sc_stat); |
438 | aprint_debug_dev(sc->sc_dev, |
439 | "%s: woke up with rv %d stat %s\n" , __func__, rv, buf); |
440 | #endif |
441 | if ((sc->sc_stat & mask) == mask) |
442 | rv = 0; |
443 | |
444 | /* Disable interrupts on tpm chip again. */ |
445 | out: bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE, |
446 | bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE) & |
447 | ~TPM_GLOBAL_INT_ENABLE); |
448 | bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE, |
449 | bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INTERRUPT_ENABLE) & |
450 | ~inttype); |
451 | |
452 | return rv; |
453 | } |
454 | |
455 | /* |
456 | * Wait on given status bits, uses interrupts where possible, otherwise polls. |
457 | */ |
458 | int |
459 | tpm_waitfor(struct tpm_softc *sc, uint8_t b0, int tmo, void *c) |
460 | { |
461 | uint8_t b; |
462 | int re, to, rv; |
463 | |
464 | #ifdef TPM_DEBUG |
465 | char buf[128]; |
466 | snprintb(buf, sizeof(buf), TPM_STS_BITS, sc->sc_stat); |
467 | aprint_debug_dev(sc->sc_dev, "%s: b0 %s\n" , __func__, buf); |
468 | #endif |
469 | |
470 | /* |
471 | * If possible, use interrupts, otherwise poll. |
472 | * |
473 | * We use interrupts for TPM_STS_VALID and TPM_STS_DATA_AVAIL (if |
474 | * the tpm chips supports them) as waiting for those can take |
475 | * really long. The other TPM_STS* are not needed very often |
476 | * so we do not support them. |
477 | */ |
478 | if (sc->sc_vector != -1) { |
479 | b = b0; |
480 | |
481 | /* |
482 | * Wait for data ready. This interrupt only occures |
483 | * when both TPM_STS_VALID and TPM_STS_DATA_AVAIL are asserted. |
484 | * Thus we don't have to bother with TPM_STS_VALID |
485 | * separately and can just return. |
486 | * |
487 | * This only holds for interrupts! When using polling |
488 | * both flags have to be waited for, see below. |
489 | */ |
490 | if ((b & TPM_STS_DATA_AVAIL) && (sc->sc_capabilities & |
491 | TPM_INTF_DATA_AVAIL_INT)) |
492 | return tpm_waitfor_int(sc, b, tmo, c, |
493 | TPM_DATA_AVAIL_INT); |
494 | |
495 | /* Wait for status valid bit. */ |
496 | if ((b & TPM_STS_VALID) && (sc->sc_capabilities & |
497 | TPM_INTF_STS_VALID_INT)) { |
498 | rv = tpm_waitfor_int(sc, b, tmo, c, TPM_STS_VALID_INT); |
499 | if (rv != 0) |
500 | return rv; |
501 | else |
502 | b = b0 & ~TPM_STS_VALID; |
503 | } |
504 | |
505 | /* |
506 | * When all flags are taken care of, return. Otherwise |
507 | * use polling for eg. TPM_STS_CMD_READY. |
508 | */ |
509 | if (b == 0) |
510 | return 0; |
511 | } |
512 | |
513 | re = 3; |
514 | restart: |
515 | /* |
516 | * If requested wait for TPM_STS_VALID before dealing with |
517 | * any other flag. Eg. when both TPM_STS_DATA_AVAIL and TPM_STS_VALID |
518 | * are requested, wait for the latter first. |
519 | */ |
520 | b = b0; |
521 | if (b0 & TPM_STS_VALID) |
522 | b = TPM_STS_VALID; |
523 | |
524 | to = tpm_tmotohz(tmo); |
525 | again: |
526 | if ((rv = tpm_waitfor_poll(sc, b, to, c)) != 0) |
527 | return rv; |
528 | |
529 | if ((b & sc->sc_stat) == TPM_STS_VALID) { |
530 | /* Now wait for other flags. */ |
531 | b = b0 & ~TPM_STS_VALID; |
532 | to++; |
533 | goto again; |
534 | } |
535 | |
536 | if ((sc->sc_stat & b) != b) { |
537 | #ifdef TPM_DEBUG |
538 | char bbuf[128], cbuf[128]; |
539 | snprintb(bbuf, sizeof(bbuf), TPM_STS_BITS, b); |
540 | snprintb(cbuf, sizeof(cbuf), TPM_STS_BITS, sc->sc_stat); |
541 | aprint_debug_dev(sc->sc_dev, |
542 | "%s: timeout: stat=%s b=%s\n" , __func__, cbuf, bbuf); |
543 | #endif |
544 | if (re-- && (b0 & TPM_STS_VALID)) { |
545 | bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_STS, |
546 | TPM_STS_RESP_RETRY); |
547 | goto restart; |
548 | } |
549 | return EIO; |
550 | } |
551 | |
552 | return 0; |
553 | } |
554 | |
555 | /* Start transaction. */ |
556 | int |
557 | tpm_tis12_start(struct tpm_softc *sc, int flag) |
558 | { |
559 | int rv; |
560 | |
561 | if (flag == UIO_READ) { |
562 | rv = tpm_waitfor(sc, TPM_STS_DATA_AVAIL | TPM_STS_VALID, |
563 | TPM_READ_TMO, sc->sc_read); |
564 | return rv; |
565 | } |
566 | |
567 | /* Own our (0th) locality. */ |
568 | if ((rv = tpm_request_locality(sc, 0)) != 0) |
569 | return rv; |
570 | |
571 | sc->sc_stat = tpm_status(sc); |
572 | if (sc->sc_stat & TPM_STS_CMD_READY) { |
573 | #ifdef TPM_DEBUG |
574 | char buf[128]; |
575 | snprintb(buf, sizeof(buf), TPM_STS_BITS, sc->sc_stat); |
576 | aprint_debug_dev(sc->sc_dev, "%s: UIO_WRITE status %s\n" , |
577 | __func__, buf); |
578 | #endif |
579 | return 0; |
580 | } |
581 | |
582 | #ifdef TPM_DEBUG |
583 | aprint_debug_dev(sc->sc_dev, |
584 | "%s: UIO_WRITE readying chip\n" , __func__); |
585 | #endif |
586 | |
587 | /* Abort previous and restart. */ |
588 | bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_STS, TPM_STS_CMD_READY); |
589 | if ((rv = tpm_waitfor(sc, TPM_STS_CMD_READY, TPM_READY_TMO, |
590 | sc->sc_write))) { |
591 | #ifdef TPM_DEBUG |
592 | aprint_debug_dev(sc->sc_dev, |
593 | "%s: UIO_WRITE readying failed %d\n" , __func__, rv); |
594 | #endif |
595 | return rv; |
596 | } |
597 | |
598 | #ifdef TPM_DEBUG |
599 | aprint_debug_dev(sc->sc_dev, |
600 | "%s: UIO_WRITE readying done\n" , __func__); |
601 | #endif |
602 | |
603 | return 0; |
604 | } |
605 | |
606 | int |
607 | tpm_tis12_read(struct tpm_softc *sc, void *buf, size_t len, size_t *count, |
608 | int flags) |
609 | { |
610 | uint8_t *p = buf; |
611 | size_t cnt; |
612 | int rv, n, bcnt; |
613 | |
614 | #ifdef TPM_DEBUG |
615 | aprint_debug_dev(sc->sc_dev, "%s: len %zu\n" , __func__, len); |
616 | #endif |
617 | cnt = 0; |
618 | while (len > 0) { |
619 | if ((rv = tpm_waitfor(sc, TPM_STS_DATA_AVAIL | TPM_STS_VALID, |
620 | TPM_READ_TMO, sc->sc_read))) |
621 | return rv; |
622 | |
623 | bcnt = tpm_getburst(sc); |
624 | n = MIN(len, bcnt); |
625 | #ifdef TPM_DEBUG |
626 | aprint_debug_dev(sc->sc_dev, |
627 | "%s: fetching %d, burst is %d\n" , __func__, n, bcnt); |
628 | #endif |
629 | for (; n--; len--) { |
630 | *p++ = bus_space_read_1(sc->sc_bt, sc->sc_bh, TPM_DATA); |
631 | cnt++; |
632 | } |
633 | |
634 | if ((flags & TPM_PARAM_SIZE) == 0 && cnt >= 6) |
635 | break; |
636 | } |
637 | #ifdef TPM_DEBUG |
638 | aprint_debug_dev(sc->sc_dev, |
639 | "%s: read %zu bytes, len %zu\n" , __func__, cnt, len); |
640 | #endif |
641 | |
642 | if (count) |
643 | *count = cnt; |
644 | |
645 | return 0; |
646 | } |
647 | |
648 | int |
649 | tpm_tis12_write(struct tpm_softc *sc, const void *buf, size_t len) |
650 | { |
651 | const uint8_t *p = buf; |
652 | size_t cnt; |
653 | int rv, r; |
654 | |
655 | #ifdef TPM_DEBUG |
656 | aprint_debug_dev(sc->sc_dev, |
657 | "%s: sc %p buf %p len %zu\n" , __func__, sc, buf, len); |
658 | #endif |
659 | if (len == 0) |
660 | return 0; |
661 | |
662 | if ((rv = tpm_request_locality(sc, 0)) != 0) |
663 | return rv; |
664 | |
665 | cnt = 0; |
666 | while (cnt < len - 1) { |
667 | for (r = tpm_getburst(sc); r > 0 && cnt < len - 1; r--) { |
668 | bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_DATA, *p++); |
669 | cnt++; |
670 | } |
671 | if ((rv = tpm_waitfor(sc, TPM_STS_VALID, TPM_READ_TMO, sc))) { |
672 | #ifdef TPM_DEBUG |
673 | aprint_debug_dev(sc->sc_dev, |
674 | "%s: failed burst rv %d\n" , __func__, rv); |
675 | #endif |
676 | return rv; |
677 | } |
678 | sc->sc_stat = tpm_status(sc); |
679 | if (!(sc->sc_stat & TPM_STS_DATA_EXPECT)) { |
680 | #ifdef TPM_DEBUG |
681 | char sbuf[128]; |
682 | snprintb(sbuf, sizeof(sbuf), TPM_STS_BITS, sc->sc_stat); |
683 | aprint_debug_dev(sc->sc_dev, |
684 | "%s: failed rv %d stat=%s\n" , __func__, rv, sbuf); |
685 | #endif |
686 | return EIO; |
687 | } |
688 | } |
689 | |
690 | bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_DATA, *p++); |
691 | cnt++; |
692 | |
693 | if ((rv = tpm_waitfor(sc, TPM_STS_VALID, TPM_READ_TMO, sc))) { |
694 | #ifdef TPM_DEBUG |
695 | aprint_debug_dev(sc->sc_dev, "%s: failed last byte rv %d\n" , |
696 | __func__, rv); |
697 | #endif |
698 | return rv; |
699 | } |
700 | if ((sc->sc_stat & TPM_STS_DATA_EXPECT) != 0) { |
701 | #ifdef TPM_DEBUG |
702 | char sbuf[128]; |
703 | snprintb(sbuf, sizeof(sbuf), TPM_STS_BITS, sc->sc_stat); |
704 | aprint_debug_dev(sc->sc_dev, |
705 | "%s: failed rv %d stat=%s\n" , __func__, rv, sbuf); |
706 | #endif |
707 | return EIO; |
708 | } |
709 | |
710 | #ifdef TPM_DEBUG |
711 | aprint_debug_dev(sc->sc_dev, "%s: wrote %zu byte\n" , __func__, cnt); |
712 | #endif |
713 | |
714 | return 0; |
715 | } |
716 | |
717 | /* Finish transaction. */ |
718 | int |
719 | tpm_tis12_end(struct tpm_softc *sc, int flag, int err) |
720 | { |
721 | int rv = 0; |
722 | |
723 | if (flag == UIO_READ) { |
724 | if ((rv = tpm_waitfor(sc, TPM_STS_VALID, TPM_READ_TMO, |
725 | sc->sc_read))) |
726 | return rv; |
727 | |
728 | /* Still more data? */ |
729 | sc->sc_stat = tpm_status(sc); |
730 | if (!err && ((sc->sc_stat & TPM_STS_DATA_AVAIL) |
731 | == TPM_STS_DATA_AVAIL)) { |
732 | #ifdef TPM_DEBUG |
733 | char buf[128]; |
734 | snprintb(buf, sizeof(buf), TPM_STS_BITS, sc->sc_stat); |
735 | aprint_debug_dev(sc->sc_dev, |
736 | "%s: read failed stat=%s\n" , __func__, buf); |
737 | #endif |
738 | rv = EIO; |
739 | } |
740 | |
741 | bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_STS, |
742 | TPM_STS_CMD_READY); |
743 | |
744 | /* Release our (0th) locality. */ |
745 | bus_space_write_1(sc->sc_bt, sc->sc_bh,TPM_ACCESS, |
746 | TPM_ACCESS_ACTIVE_LOCALITY); |
747 | } else { |
748 | /* Hungry for more? */ |
749 | sc->sc_stat = tpm_status(sc); |
750 | if (!err && (sc->sc_stat & TPM_STS_DATA_EXPECT)) { |
751 | #ifdef TPM_DEBUG |
752 | char buf[128]; |
753 | snprintb(buf, sizeof(buf), TPM_STS_BITS, sc->sc_stat); |
754 | aprint_debug_dev(sc->sc_dev, |
755 | "%s: write failed stat=%s\n" , __func__, buf); |
756 | #endif |
757 | rv = EIO; |
758 | } |
759 | |
760 | bus_space_write_1(sc->sc_bt, sc->sc_bh, TPM_STS, |
761 | err ? TPM_STS_CMD_READY : TPM_STS_GO); |
762 | } |
763 | |
764 | return rv; |
765 | } |
766 | |
767 | int |
768 | tpm_intr(void *v) |
769 | { |
770 | struct tpm_softc *sc = v; |
771 | uint32_t r; |
772 | #ifdef TPM_DEBUG |
773 | static int cnt = 0; |
774 | #endif |
775 | |
776 | r = bus_space_read_4(sc->sc_bt, sc->sc_bh, TPM_INT_STATUS); |
777 | #ifdef TPM_DEBUG |
778 | if (r != 0) { |
779 | char buf[128]; |
780 | snprintb(buf, sizeof(buf), TPM_INTERRUPT_ENABLE_BITS, r); |
781 | aprint_debug_dev(sc->sc_dev, "%s: int=%s (%d)\n" , __func__, |
782 | buf, cnt); |
783 | } else |
784 | cnt++; |
785 | #endif |
786 | if (!(r & (TPM_CMD_READY_INT | TPM_LOCALITY_CHANGE_INT | |
787 | TPM_STS_VALID_INT | TPM_DATA_AVAIL_INT))) |
788 | #ifdef __FreeBSD__ |
789 | return; |
790 | #else |
791 | return 0; |
792 | #endif |
793 | if (r & TPM_STS_VALID_INT) |
794 | wakeup(sc); |
795 | |
796 | if (r & TPM_CMD_READY_INT) |
797 | wakeup(sc->sc_write); |
798 | |
799 | if (r & TPM_DATA_AVAIL_INT) |
800 | wakeup(sc->sc_read); |
801 | |
802 | if (r & TPM_LOCALITY_CHANGE_INT) |
803 | wakeup(sc->sc_init); |
804 | |
805 | bus_space_write_4(sc->sc_bt, sc->sc_bh, TPM_INT_STATUS, r); |
806 | |
807 | return 1; |
808 | } |
809 | |
810 | /* Read single byte using legacy interface. */ |
811 | static inline uint8_t |
812 | tpm_legacy_in(bus_space_tag_t iot, bus_space_handle_t ioh, int reg) |
813 | { |
814 | bus_space_write_1(iot, ioh, 0, reg); |
815 | return bus_space_read_1(iot, ioh, 1); |
816 | } |
817 | |
818 | /* Probe for TPM using legacy interface. */ |
819 | int |
820 | tpm_legacy_probe(bus_space_tag_t iot, bus_addr_t iobase) |
821 | { |
822 | bus_space_handle_t ioh; |
823 | uint8_t r, v; |
824 | int i, rv = 0; |
825 | char id[8]; |
826 | |
827 | if (!tpm_enabled || iobase == -1) |
828 | return 0; |
829 | |
830 | if (bus_space_map(iot, iobase, 2, 0, &ioh)) |
831 | return 0; |
832 | |
833 | v = bus_space_read_1(iot, ioh, 0); |
834 | if (v == 0xff) { |
835 | bus_space_unmap(iot, ioh, 2); |
836 | return 0; |
837 | } |
838 | r = bus_space_read_1(iot, ioh, 1); |
839 | |
840 | for (i = sizeof(id); i--; ) |
841 | id[i] = tpm_legacy_in(iot, ioh, TPM_ID + i); |
842 | |
843 | #ifdef TPM_DEBUG |
844 | printf("tpm_legacy_probe %.4s %d.%d.%d.%d\n" , |
845 | &id[4], id[0], id[1], id[2], id[3]); |
846 | #endif |
847 | /* |
848 | * The only chips using the legacy interface we are aware of are |
849 | * by Atmel. For other chips more signature would have to be added. |
850 | */ |
851 | if (!bcmp(&id[4], "ATML" , 4)) |
852 | rv = 1; |
853 | |
854 | if (!rv) { |
855 | bus_space_write_1(iot, ioh, r, 1); |
856 | bus_space_write_1(iot, ioh, v, 0); |
857 | } |
858 | bus_space_unmap(iot, ioh, 2); |
859 | |
860 | return rv; |
861 | } |
862 | |
863 | /* Setup TPM using legacy interface. */ |
864 | int |
865 | tpm_legacy_init(struct tpm_softc *sc, int irq, const char *name) |
866 | { |
867 | char id[8]; |
868 | int i; |
869 | |
870 | if ((i = bus_space_map(sc->sc_batm, tpm_enabled, 2, 0, &sc->sc_bahm))) { |
871 | aprint_debug_dev(sc->sc_dev, "cannot map tpm registers (%d)\n" , |
872 | i); |
873 | tpm_enabled = 0; |
874 | return 1; |
875 | } |
876 | |
877 | for (i = sizeof(id); i--; ) |
878 | id[i] = tpm_legacy_in(sc->sc_bt, sc->sc_bh, TPM_ID + i); |
879 | |
880 | aprint_debug_dev(sc->sc_dev, "%.4s %d.%d @0x%x\n" , &id[4], id[0], |
881 | id[1], tpm_enabled); |
882 | tpm_enabled = 0; |
883 | |
884 | return 0; |
885 | } |
886 | |
887 | /* Start transaction. */ |
888 | int |
889 | tpm_legacy_start(struct tpm_softc *sc, int flag) |
890 | { |
891 | struct timeval tv; |
892 | uint8_t bits, r; |
893 | int to, rv; |
894 | |
895 | bits = flag == UIO_READ ? TPM_LEGACY_DA : 0; |
896 | tv.tv_sec = TPM_LEGACY_TMO; |
897 | tv.tv_usec = 0; |
898 | to = tvtohz(&tv) / TPM_LEGACY_SLEEP; |
899 | while (((r = bus_space_read_1(sc->sc_batm, sc->sc_bahm, 1)) & |
900 | (TPM_LEGACY_BUSY|bits)) != bits && to--) { |
901 | rv = tsleep(sc, PRIBIO | PCATCH, "legacy_tpm_start" , |
902 | TPM_LEGACY_SLEEP); |
903 | if (rv && rv != EWOULDBLOCK) |
904 | return rv; |
905 | } |
906 | |
907 | #if defined(TPM_DEBUG) && !defined(__FreeBSD__) |
908 | char buf[128]; |
909 | snprintb(buf, sizeof(buf), TPM_LEGACY_BITS, r); |
910 | aprint_debug_dev(sc->sc_dev, "%s: bits %s\n" , device_xname(sc->sc_dev), |
911 | buf); |
912 | #endif |
913 | if ((r & (TPM_LEGACY_BUSY|bits)) != bits) |
914 | return EIO; |
915 | |
916 | return 0; |
917 | } |
918 | |
919 | int |
920 | tpm_legacy_read(struct tpm_softc *sc, void *buf, size_t len, size_t *count, |
921 | int flags) |
922 | { |
923 | uint8_t *p; |
924 | size_t cnt; |
925 | int to, rv; |
926 | |
927 | cnt = rv = 0; |
928 | for (p = buf; !rv && len > 0; len--) { |
929 | for (to = 1000; |
930 | !(bus_space_read_1(sc->sc_batm, sc->sc_bahm, 1) & |
931 | TPM_LEGACY_DA); DELAY(1)) |
932 | if (!to--) |
933 | return EIO; |
934 | |
935 | DELAY(TPM_LEGACY_DELAY); |
936 | *p++ = bus_space_read_1(sc->sc_batm, sc->sc_bahm, 0); |
937 | cnt++; |
938 | } |
939 | |
940 | *count = cnt; |
941 | return 0; |
942 | } |
943 | |
944 | int |
945 | tpm_legacy_write(struct tpm_softc *sc, const void *buf, size_t len) |
946 | { |
947 | const uint8_t *p; |
948 | size_t n; |
949 | |
950 | for (p = buf, n = len; n--; DELAY(TPM_LEGACY_DELAY)) { |
951 | if (!n && len != TPM_BUFSIZ) { |
952 | bus_space_write_1(sc->sc_batm, sc->sc_bahm, 1, |
953 | TPM_LEGACY_LAST); |
954 | DELAY(TPM_LEGACY_DELAY); |
955 | } |
956 | bus_space_write_1(sc->sc_batm, sc->sc_bahm, 0, *p++); |
957 | } |
958 | |
959 | return 0; |
960 | } |
961 | |
962 | /* Finish transaction. */ |
963 | int |
964 | tpm_legacy_end(struct tpm_softc *sc, int flag, int rv) |
965 | { |
966 | struct timeval tv; |
967 | uint8_t r; |
968 | int to; |
969 | |
970 | if (rv || flag == UIO_READ) |
971 | bus_space_write_1(sc->sc_batm, sc->sc_bahm, 1, TPM_LEGACY_ABRT); |
972 | else { |
973 | tv.tv_sec = TPM_LEGACY_TMO; |
974 | tv.tv_usec = 0; |
975 | to = tvtohz(&tv) / TPM_LEGACY_SLEEP; |
976 | while(((r = bus_space_read_1(sc->sc_batm, sc->sc_bahm, 1)) & |
977 | TPM_LEGACY_BUSY) && to--) { |
978 | rv = tsleep(sc, PRIBIO | PCATCH, "legacy_tpm_end" , |
979 | TPM_LEGACY_SLEEP); |
980 | if (rv && rv != EWOULDBLOCK) |
981 | return rv; |
982 | } |
983 | |
984 | #if defined(TPM_DEBUG) && !defined(__FreeBSD__) |
985 | char buf[128]; |
986 | snprintb(buf, sizeof(buf), TPM_LEGACY_BITS, r); |
987 | aprint_debug_dev(sc->sc_dev, "%s: bits %s\n" , |
988 | device_xname(sc->sc_dev), buf); |
989 | #endif |
990 | if (r & TPM_LEGACY_BUSY) |
991 | return EIO; |
992 | |
993 | if (r & TPM_LEGACY_RE) |
994 | return EIO; /* XXX Retry the loop? */ |
995 | } |
996 | |
997 | return rv; |
998 | } |
999 | |
1000 | int |
1001 | tpmopen(dev_t dev, int flag, int mode, struct lwp *l) |
1002 | { |
1003 | struct tpm_softc *sc = device_lookup_private(&tpm_cd, TPMUNIT(dev)); |
1004 | |
1005 | if (!sc) |
1006 | return ENXIO; |
1007 | |
1008 | if (sc->sc_flags & TPM_OPEN) |
1009 | return EBUSY; |
1010 | |
1011 | sc->sc_flags |= TPM_OPEN; |
1012 | |
1013 | return 0; |
1014 | } |
1015 | |
1016 | int |
1017 | tpmclose(dev_t dev, int flag, int mode, struct lwp *l) |
1018 | { |
1019 | struct tpm_softc *sc = device_lookup_private(&tpm_cd, TPMUNIT(dev)); |
1020 | |
1021 | if (!sc) |
1022 | return ENXIO; |
1023 | |
1024 | if (!(sc->sc_flags & TPM_OPEN)) |
1025 | return EINVAL; |
1026 | |
1027 | sc->sc_flags &= ~TPM_OPEN; |
1028 | |
1029 | return 0; |
1030 | } |
1031 | |
1032 | int |
1033 | tpmread(dev_t dev, struct uio *uio, int flags) |
1034 | { |
1035 | struct tpm_softc *sc = device_lookup_private(&tpm_cd, TPMUNIT(dev)); |
1036 | uint8_t buf[TPM_BUFSIZ], *p; |
1037 | size_t cnt, len, n; |
1038 | int rv, s; |
1039 | |
1040 | if (!sc) |
1041 | return ENXIO; |
1042 | |
1043 | s = spltty(); |
1044 | if ((rv = (*sc->sc_start)(sc, UIO_READ))) |
1045 | goto out; |
1046 | |
1047 | #ifdef TPM_DEBUG |
1048 | aprint_debug_dev(sc->sc_dev, "%s: getting header\n" , __func__); |
1049 | #endif |
1050 | if ((rv = (*sc->sc_read)(sc, buf, TPM_HDRSIZE, &cnt, 0))) { |
1051 | (*sc->sc_end)(sc, UIO_READ, rv); |
1052 | goto out; |
1053 | } |
1054 | |
1055 | len = (buf[2] << 24) | (buf[3] << 16) | (buf[4] << 8) | buf[5]; |
1056 | #ifdef TPM_DEBUG |
1057 | aprint_debug_dev(sc->sc_dev, "%s: len %zu, io count %zu\n" , __func__, |
1058 | len, uio->uio_resid); |
1059 | #endif |
1060 | if (len > uio->uio_resid) { |
1061 | rv = EIO; |
1062 | (*sc->sc_end)(sc, UIO_READ, rv); |
1063 | #ifdef TPM_DEBUG |
1064 | aprint_debug_dev(sc->sc_dev, |
1065 | "%s: bad residual io count 0x%zx\n" , __func__, |
1066 | uio->uio_resid); |
1067 | #endif |
1068 | goto out; |
1069 | } |
1070 | |
1071 | /* Copy out header. */ |
1072 | if ((rv = uiomove(buf, cnt, uio))) { |
1073 | #ifdef TPM_DEBUG |
1074 | aprint_debug_dev(sc->sc_dev, |
1075 | "%s: uiomove failed %d\n" , __func__, rv); |
1076 | #endif |
1077 | (*sc->sc_end)(sc, UIO_READ, rv); |
1078 | goto out; |
1079 | } |
1080 | |
1081 | /* Get remaining part of the answer (if anything is left). */ |
1082 | for (len -= cnt, p = buf, n = sizeof(buf); len > 0; p = buf, len -= n, |
1083 | n = sizeof(buf)) { |
1084 | n = MIN(n, len); |
1085 | #ifdef TPM_DEBUG |
1086 | aprint_debug_dev(sc->sc_dev, "%s: n %zu len %zu\n" , __func__, |
1087 | n, len); |
1088 | #endif |
1089 | if ((rv = (*sc->sc_read)(sc, p, n, NULL, TPM_PARAM_SIZE))) { |
1090 | (*sc->sc_end)(sc, UIO_READ, rv); |
1091 | goto out; |
1092 | } |
1093 | p += n; |
1094 | if ((rv = uiomove(buf, p - buf, uio))) { |
1095 | #ifdef TPM_DEBUG |
1096 | aprint_debug_dev(sc->sc_dev, |
1097 | "%s: uiomove failed %d\n" , __func__, rv); |
1098 | #endif |
1099 | (*sc->sc_end)(sc, UIO_READ, rv); |
1100 | goto out; |
1101 | } |
1102 | } |
1103 | |
1104 | rv = (*sc->sc_end)(sc, UIO_READ, rv); |
1105 | out: |
1106 | splx(s); |
1107 | return rv; |
1108 | } |
1109 | |
1110 | int |
1111 | tpmwrite(dev_t dev, struct uio *uio, int flags) |
1112 | { |
1113 | struct tpm_softc *sc = device_lookup_private(&tpm_cd, TPMUNIT(dev)); |
1114 | uint8_t buf[TPM_BUFSIZ]; |
1115 | int n, rv, s; |
1116 | |
1117 | if (!sc) |
1118 | return ENXIO; |
1119 | |
1120 | s = spltty(); |
1121 | |
1122 | #ifdef TPM_DEBUG |
1123 | aprint_debug_dev(sc->sc_dev, "%s: io count %zu\n" , __func__, |
1124 | uio->uio_resid); |
1125 | #endif |
1126 | |
1127 | n = MIN(sizeof(buf), uio->uio_resid); |
1128 | if ((rv = uiomove(buf, n, uio))) { |
1129 | #ifdef TPM_DEBUG |
1130 | aprint_debug_dev(sc->sc_dev, |
1131 | "%s: uiomove failed %d\n" , __func__, rv); |
1132 | #endif |
1133 | splx(s); |
1134 | return rv; |
1135 | } |
1136 | |
1137 | if ((rv = (*sc->sc_start)(sc, UIO_WRITE))) { |
1138 | splx(s); |
1139 | return rv; |
1140 | } |
1141 | |
1142 | if ((rv = (*sc->sc_write)(sc, buf, n))) { |
1143 | splx(s); |
1144 | return rv; |
1145 | } |
1146 | |
1147 | rv = (*sc->sc_end)(sc, UIO_WRITE, rv); |
1148 | splx(s); |
1149 | return rv; |
1150 | } |
1151 | |
1152 | int |
1153 | tpmioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l) |
1154 | { |
1155 | return ENOTTY; |
1156 | } |
1157 | |