blob: 8816d55759fdf7bba66537bbf9efe2434afe6a49 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Simon Glassed3f5a32013-11-10 10:27:05 -07002/*
3 * Copyright (c) 2013 Google, Inc
Simon Glassed3f5a32013-11-10 10:27:05 -07004 */
5
6#include <common.h>
Simon Glass21baf152015-08-22 18:31:35 -06007#include <dm.h>
Miquel Raynald677bfe2018-05-15 11:57:06 +02008#include <tpm-v1.h>
Simon Glassed3f5a32013-11-10 10:27:05 -07009#include <asm/state.h>
10#include <asm/unaligned.h>
11#include <linux/crc8.h>
12
13/* TPM NVRAM location indices. */
14#define FIRMWARE_NV_INDEX 0x1007
15#define KERNEL_NV_INDEX 0x1008
16
17#define NV_DATA_PUBLIC_PERMISSIONS_OFFSET 60
18
19/* Kernel TPM space - KERNEL_NV_INDEX, locked with physical presence */
20#define ROLLBACK_SPACE_KERNEL_VERSION 2
21#define ROLLBACK_SPACE_KERNEL_UID 0x4752574C /* 'GRWL' */
22
23struct rollback_space_kernel {
24 /* Struct version, for backwards compatibility */
25 uint8_t struct_version;
26 /* Unique ID to detect space redefinition */
27 uint32_t uid;
28 /* Kernel versions */
29 uint32_t kernel_versions;
30 /* Reserved for future expansion */
31 uint8_t reserved[3];
32 /* Checksum (v2 and later only) */
33 uint8_t crc8;
34} __packed rollback_space_kernel;
35
36/*
37 * These numbers derive from adding the sizes of command fields as shown in
38 * the TPM commands manual.
39 */
40#define TPM_REQUEST_HEADER_LENGTH 10
41#define TPM_RESPONSE_HEADER_LENGTH 10
42
43/* These are the different non-volatile spaces that we emulate */
44enum {
45 NV_GLOBAL_LOCK,
46 NV_SEQ_FIRMWARE,
47 NV_SEQ_KERNEL,
48 NV_SEQ_COUNT,
49};
50
51/* Size of each non-volatile space */
52#define NV_DATA_SIZE 0x20
53
54/*
55 * Information about our TPM emulation. This is preserved in the sandbox
56 * state file if enabled.
57 */
58static struct tpm_state {
59 uint8_t nvdata[NV_SEQ_COUNT][NV_DATA_SIZE];
Simon Glass21baf152015-08-22 18:31:35 -060060} g_state;
Simon Glassed3f5a32013-11-10 10:27:05 -070061
62/**
63 * sandbox_tpm_read_state() - read the sandbox EC state from the state file
64 *
65 * If data is available, then blob and node will provide access to it. If
66 * not this function sets up an empty TPM.
67 *
68 * @blob: Pointer to device tree blob, or NULL if no data to read
69 * @node: Node offset to read from
70 */
71static int sandbox_tpm_read_state(const void *blob, int node)
72{
73 const char *prop;
74 int len;
75 int i;
76
77 if (!blob)
78 return 0;
79
80 for (i = 0; i < NV_SEQ_COUNT; i++) {
81 char prop_name[20];
82
83 sprintf(prop_name, "nvdata%d", i);
84 prop = fdt_getprop(blob, node, prop_name, &len);
85 if (prop && len == NV_DATA_SIZE)
Simon Glass21baf152015-08-22 18:31:35 -060086 memcpy(g_state.nvdata[i], prop, NV_DATA_SIZE);
Simon Glassed3f5a32013-11-10 10:27:05 -070087 }
88
89 return 0;
90}
91
92/**
93 * cros_ec_write_state() - Write out our state to the state file
94 *
95 * The caller will ensure that there is a node ready for the state. The node
96 * may already contain the old state, in which case it is overridden.
97 *
98 * @blob: Device tree blob holding state
99 * @node: Node to write our state into
100 */
101static int sandbox_tpm_write_state(void *blob, int node)
102{
103 int i;
104
105 /*
106 * We are guaranteed enough space to write basic properties.
107 * We could use fdt_add_subnode() to put each set of data in its
108 * own node - perhaps useful if we add access informaiton to each.
109 */
110 for (i = 0; i < NV_SEQ_COUNT; i++) {
111 char prop_name[20];
112
113 sprintf(prop_name, "nvdata%d", i);
Simon Glass21baf152015-08-22 18:31:35 -0600114 fdt_setprop(blob, node, prop_name, g_state.nvdata[i],
Simon Glassed3f5a32013-11-10 10:27:05 -0700115 NV_DATA_SIZE);
116 }
117
118 return 0;
119}
120
121SANDBOX_STATE_IO(sandbox_tpm, "google,sandbox-tpm", sandbox_tpm_read_state,
122 sandbox_tpm_write_state);
123
124static int index_to_seq(uint32_t index)
125{
126 switch (index) {
127 case FIRMWARE_NV_INDEX:
128 return NV_SEQ_FIRMWARE;
129 case KERNEL_NV_INDEX:
130 return NV_SEQ_KERNEL;
131 case 0:
132 return NV_GLOBAL_LOCK;
133 }
134
135 printf("Invalid nv index %#x\n", index);
136 return -1;
137}
138
Simon Glass21baf152015-08-22 18:31:35 -0600139static int sandbox_tpm_xfer(struct udevice *dev, const uint8_t *sendbuf,
140 size_t send_size, uint8_t *recvbuf,
141 size_t *recv_len)
Simon Glassed3f5a32013-11-10 10:27:05 -0700142{
Simon Glass21baf152015-08-22 18:31:35 -0600143 struct tpm_state *tpm = dev_get_priv(dev);
Simon Glassed3f5a32013-11-10 10:27:05 -0700144 uint32_t code, index, length, type;
145 uint8_t *data;
146 int seq;
147
148 code = get_unaligned_be32(sendbuf + sizeof(uint16_t) +
149 sizeof(uint32_t));
150 printf("tpm: %zd bytes, recv_len %zd, cmd = %x\n", send_size,
151 *recv_len, code);
152 print_buffer(0, sendbuf, 1, send_size, 0);
153 switch (code) {
154 case 0x65: /* get flags */
155 type = get_unaligned_be32(sendbuf + 14);
156 switch (type) {
157 case 4:
158 index = get_unaligned_be32(sendbuf + 18);
159 printf("Get flags index %#02x\n", index);
160 *recv_len = 22;
161 memset(recvbuf, '\0', *recv_len);
162 put_unaligned_be32(22, recvbuf +
163 TPM_RESPONSE_HEADER_LENGTH);
164 data = recvbuf + TPM_RESPONSE_HEADER_LENGTH +
165 sizeof(uint32_t);
166 switch (index) {
167 case FIRMWARE_NV_INDEX:
168 break;
169 case KERNEL_NV_INDEX:
170 /* TPM_NV_PER_PPWRITE */
171 put_unaligned_be32(1, data +
172 NV_DATA_PUBLIC_PERMISSIONS_OFFSET);
173 break;
174 }
175 break;
176 case 0x11: /* TPM_CAP_NV_INDEX */
177 index = get_unaligned_be32(sendbuf + 18);
178 printf("Get cap nv index %#02x\n", index);
179 put_unaligned_be32(22, recvbuf +
180 TPM_RESPONSE_HEADER_LENGTH);
181 break;
182 default:
183 printf(" ** Unknown 0x65 command type %#02x\n",
184 type);
185 return -1;
186 }
187 break;
188 case 0xcd: /* nvwrite */
189 index = get_unaligned_be32(sendbuf + 10);
190 length = get_unaligned_be32(sendbuf + 18);
191 seq = index_to_seq(index);
192 if (seq < 0)
193 return -1;
194 printf("tpm: nvwrite index=%#02x, len=%#02x\n", index, length);
Che-Liang Chiou2c30af82013-11-10 10:27:08 -0700195 memcpy(&tpm->nvdata[seq], sendbuf + 22, length);
Simon Glassed3f5a32013-11-10 10:27:05 -0700196 *recv_len = 12;
197 memset(recvbuf, '\0', *recv_len);
198 break;
199 case 0xcf: /* nvread */
200 index = get_unaligned_be32(sendbuf + 10);
201 length = get_unaligned_be32(sendbuf + 18);
202 seq = index_to_seq(index);
203 if (seq < 0)
204 return -1;
205 printf("tpm: nvread index=%#02x, len=%#02x\n", index, length);
206 *recv_len = TPM_RESPONSE_HEADER_LENGTH + sizeof(uint32_t) +
207 length;
208 memset(recvbuf, '\0', *recv_len);
209 put_unaligned_be32(length, recvbuf +
210 TPM_RESPONSE_HEADER_LENGTH);
211 if (seq == NV_SEQ_KERNEL) {
212 struct rollback_space_kernel rsk;
213
214 data = recvbuf + TPM_RESPONSE_HEADER_LENGTH +
215 sizeof(uint32_t);
Tom Rini7e019da2016-04-12 15:11:22 -0400216 memset(&rsk, 0, sizeof(struct rollback_space_kernel));
Simon Glassed3f5a32013-11-10 10:27:05 -0700217 rsk.struct_version = 2;
218 rsk.uid = ROLLBACK_SPACE_KERNEL_UID;
Stefan Roese456ecd02016-04-08 15:56:29 +0200219 rsk.crc8 = crc8(0, (unsigned char *)&rsk,
Simon Glassed3f5a32013-11-10 10:27:05 -0700220 offsetof(struct rollback_space_kernel,
221 crc8));
222 memcpy(data, &rsk, sizeof(rsk));
223 } else {
224 memcpy(recvbuf + TPM_RESPONSE_HEADER_LENGTH +
225 sizeof(uint32_t), &tpm->nvdata[seq], length);
226 }
227 break;
228 case 0x14: /* tpm extend */
229 case 0x15: /* pcr read */
230 case 0x5d: /* force clear */
231 case 0x6f: /* physical enable */
232 case 0x72: /* physical set deactivated */
233 case 0x99: /* startup */
234 case 0x4000000a: /* assert physical presence */
235 *recv_len = 12;
236 memset(recvbuf, '\0', *recv_len);
237 break;
238 default:
239 printf("Unknown tpm command %02x\n", code);
240 return -1;
241 }
242
243 return 0;
244}
245
Simon Glass21baf152015-08-22 18:31:35 -0600246static int sandbox_tpm_get_desc(struct udevice *dev, char *buf, int size)
Simon Glassed3f5a32013-11-10 10:27:05 -0700247{
Simon Glass21baf152015-08-22 18:31:35 -0600248 if (size < 15)
249 return -ENOSPC;
250
251 return snprintf(buf, size, "sandbox TPM");
252}
253
254static int sandbox_tpm_probe(struct udevice *dev)
255{
256 struct tpm_state *tpm = dev_get_priv(dev);
257
258 memcpy(tpm, &g_state, sizeof(*tpm));
259
Simon Glassed3f5a32013-11-10 10:27:05 -0700260 return 0;
261}
262
Simon Glass21baf152015-08-22 18:31:35 -0600263static int sandbox_tpm_open(struct udevice *dev)
Simon Glassed3f5a32013-11-10 10:27:05 -0700264{
Simon Glassed3f5a32013-11-10 10:27:05 -0700265 return 0;
266}
267
Simon Glass21baf152015-08-22 18:31:35 -0600268static int sandbox_tpm_close(struct udevice *dev)
Simon Glassed3f5a32013-11-10 10:27:05 -0700269{
Simon Glassed3f5a32013-11-10 10:27:05 -0700270 return 0;
271}
Simon Glass21baf152015-08-22 18:31:35 -0600272
273static const struct tpm_ops sandbox_tpm_ops = {
274 .open = sandbox_tpm_open,
275 .close = sandbox_tpm_close,
276 .get_desc = sandbox_tpm_get_desc,
277 .xfer = sandbox_tpm_xfer,
278};
279
280static const struct udevice_id sandbox_tpm_ids[] = {
281 { .compatible = "google,sandbox-tpm" },
282 { }
283};
284
285U_BOOT_DRIVER(sandbox_tpm) = {
286 .name = "sandbox_tpm",
287 .id = UCLASS_TPM,
288 .of_match = sandbox_tpm_ids,
289 .ops = &sandbox_tpm_ops,
290 .probe = sandbox_tpm_probe,
291 .priv_auto_alloc_size = sizeof(struct tpm_state),
292};