blob: ad5e7f37b7b7cc0562d5547bf99fe5af7c2fc3bd [file] [log] [blame]
Patrick Georgiac959032020-05-05 22:49:26 +02001/* SPDX-License-Identifier: GPL-2.0-or-later */
Lee Leahy1b39f172017-01-05 17:59:38 -08002
Lee Leahy1b39f172017-01-05 17:59:38 -08003#include <assert.h>
4#include <commonlib/endian.h>
Elyes HAOUAS361a9352019-12-18 21:26:33 +01005#include <commonlib/helpers.h>
Lee Leahy1b39f172017-01-05 17:59:38 -08006#include <console/console.h>
7#include <delay.h>
Nico Huber0f2dd1e2017-08-01 14:02:40 +02008#include <device/i2c_simple.h>
Lee Leahy1b39f172017-01-05 17:59:38 -08009#include <endian.h>
10#include <lib.h>
Philipp Deppenwiesed88fb362017-10-18 20:26:18 +020011#include <security/tpm/tis.h>
Lee Leahy1b39f172017-01-05 17:59:38 -080012#include <timer.h>
Elyes HAOUASede8dd02019-06-23 06:57:53 +020013#include <types.h>
Lee Leahy1b39f172017-01-05 17:59:38 -080014
Sergii Dmytruk3e5cefc2022-11-01 00:48:43 +020015#include "tpm.h"
16
Lee Leahy1b39f172017-01-05 17:59:38 -080017#define RECV_TIMEOUT (1 * 1000) /* 1 second */
18#define XMIT_TIMEOUT (1 * 1000) /* 1 second */
19#define SLEEP_DURATION 1000 /* microseconds */
20
21struct tpm_output_header {
22 uint16_t tag;
23 uint32_t length;
24 uint32_t return_code;
Stefan Reinauer6a001132017-07-13 02:20:27 +020025} __packed;
Lee Leahy1b39f172017-01-05 17:59:38 -080026
Sergii Dmytruk963f7b92022-10-29 20:42:28 +030027static tpm_result_t i2c_tis_sendrecv(const uint8_t *sendbuf, size_t sbuf_size,
28 uint8_t *recvbuf, size_t *rbuf_len)
Lee Leahy1b39f172017-01-05 17:59:38 -080029{
30 size_t hdr_bytes;
31 struct tpm_output_header *header;
32 size_t max_recv_bytes;
33 size_t recv_bytes;
34 int status;
35 struct stopwatch sw;
36
37 ASSERT(sbuf_size >= 10);
Julius Wernercd49cce2019-03-05 16:53:33 -080038 if (CONFIG(DRIVER_TPM_DISPLAY_TIS_BYTES)) {
Lee Leahy1b39f172017-01-05 17:59:38 -080039 /* Display the TPM command */
40 if (sbuf_size >= 10)
41 printk(BIOS_DEBUG, "TPM Command: 0x%08x\n",
42 read_at_be32(sendbuf, sizeof(uint16_t)
43 + sizeof(uint32_t)));
44 hexdump(sendbuf, sbuf_size);
45 }
46
47 /* Send the command to the TPM */
48 stopwatch_init_msecs_expire(&sw, XMIT_TIMEOUT);
49 while (1) {
50 status = i2c_write_raw(CONFIG_DRIVER_TPM_I2C_BUS,
51 CONFIG_DRIVER_TPM_I2C_ADDR, (uint8_t *)sendbuf,
52 sbuf_size);
53 if ((status < 0) && (!stopwatch_expired(&sw)))
54 continue;
Jon Murphyd7b8dc92023-09-05 11:36:43 -060055 if (status < 0) {
56 printk(BIOS_ERR, "I2C write error: %d\n", status);
57 return TPM_CB_COMMUNICATION_ERROR;
58 }
Lee Leahy1b39f172017-01-05 17:59:38 -080059 break;
60 }
61
62 /* Read the TPM response header */
63 max_recv_bytes = *rbuf_len;
64 ASSERT(max_recv_bytes >= sizeof(*header));
65 hdr_bytes = sizeof(*header);
66 header = (struct tpm_output_header *)recvbuf;
67 stopwatch_init_msecs_expire(&sw, RECV_TIMEOUT);
68 do {
69 status = i2c_read_raw(CONFIG_DRIVER_TPM_I2C_BUS,
70 CONFIG_DRIVER_TPM_I2C_ADDR, recvbuf, hdr_bytes);
71 if (status > 0)
72 break;
73 udelay(SLEEP_DURATION);
74 } while (!stopwatch_expired(&sw));
75 if (status != sizeof(*header))
Jon Murphyd7b8dc92023-09-05 11:36:43 -060076 return TPM_CB_COMMUNICATION_ERROR;
Lee Leahy1b39f172017-01-05 17:59:38 -080077
78 /* Determine the number of bytes remaining */
Elyes HAOUAS361a9352019-12-18 21:26:33 +010079 recv_bytes = MIN(be32_to_cpu(*(uint32_t *)&header->length),
Lee Leahy1b39f172017-01-05 17:59:38 -080080 max_recv_bytes);
81
82 /* Determine if there is additional response data */
83 if (recv_bytes > hdr_bytes) {
84 /* Display the TPM response */
Julius Wernercd49cce2019-03-05 16:53:33 -080085 if (CONFIG(DRIVER_TPM_DISPLAY_TIS_BYTES))
Lee Leahy1b39f172017-01-05 17:59:38 -080086 hexdump(recvbuf, hdr_bytes);
87
88 /* Read the full TPM response */
89 status = i2c_read_raw(CONFIG_DRIVER_TPM_I2C_BUS,
90 CONFIG_DRIVER_TPM_I2C_ADDR, recvbuf, recv_bytes);
Jon Murphyd7b8dc92023-09-05 11:36:43 -060091 if (status < 0) {
92 printk(BIOS_ERR, "I2C read error: %d\n", status);
93 return TPM_CB_COMMUNICATION_ERROR;
94 }
Lee Leahy1b39f172017-01-05 17:59:38 -080095 }
96
97 /* Return the number of bytes received */
98 *rbuf_len = status;
99
100 /* Display the TPM response */
Julius Wernercd49cce2019-03-05 16:53:33 -0800101 if (CONFIG(DRIVER_TPM_DISPLAY_TIS_BYTES)) {
Lee Leahy1b39f172017-01-05 17:59:38 -0800102 printk(BIOS_DEBUG, "TPM Response: 0x%08x\n",
103 read_at_be32(recvbuf, sizeof(uint16_t)
104 + sizeof(uint32_t)));
105 hexdump(recvbuf, *rbuf_len);
106 }
107
108 /* Successful transfer */
Jon Murphyd7b8dc92023-09-05 11:36:43 -0600109 return TPM_SUCCESS;
Lee Leahy1b39f172017-01-05 17:59:38 -0800110}
Sergii Dmytruk963f7b92022-10-29 20:42:28 +0300111
Sergii Dmytruk3e5cefc2022-11-01 00:48:43 +0200112tis_sendrecv_fn i2c_tis_probe(enum tpm_family *family)
Sergii Dmytruk963f7b92022-10-29 20:42:28 +0300113{
Sergii Dmytrukfebf9b92022-10-31 15:30:15 +0200114 /*
115 * Can't query version or really anything as the device doesn't support
116 * much through this interface (can't specify address on accesses).
117 *
118 * Hence the assumption is that whatever TPM version is enabled at
119 * compile-time defines what the device supports. The check is written
120 * in a way to give TPM 1 preference even if support for both versions
121 * is compiled in.
122 */
123 if (family != NULL)
124 *family = CONFIG(TPM1) ? TPM_1 : TPM_2;
125
Sergii Dmytruk963f7b92022-10-29 20:42:28 +0300126 return &i2c_tis_sendrecv;
127}