]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
ed3f5a30 SG |
2 | /* |
3 | * Copyright (c) 2013 Google, Inc | |
ed3f5a30 SG |
4 | */ |
5 | ||
6 | #include <common.h> | |
21baf15b | 7 | #include <dm.h> |
d677bfe2 | 8 | #include <tpm-v1.h> |
ed3f5a30 SG |
9 | #include <asm/state.h> |
10 | #include <asm/unaligned.h> | |
c3a4d1c3 | 11 | #include <u-boot/crc.h> |
974c98f2 | 12 | #include "sandbox_common.h" |
ed3f5a30 SG |
13 | |
14 | #define NV_DATA_PUBLIC_PERMISSIONS_OFFSET 60 | |
15 | ||
ed3f5a30 SG |
16 | /* |
17 | * Information about our TPM emulation. This is preserved in the sandbox | |
18 | * state file if enabled. | |
19 | */ | |
20 | static struct tpm_state { | |
ef8a2500 SG |
21 | bool valid; |
22 | struct nvdata_state nvdata[NV_SEQ_COUNT]; | |
1db235a1 | 23 | } s_state, *g_state; |
ed3f5a30 SG |
24 | |
25 | /** | |
26 | * sandbox_tpm_read_state() - read the sandbox EC state from the state file | |
27 | * | |
28 | * If data is available, then blob and node will provide access to it. If | |
29 | * not this function sets up an empty TPM. | |
30 | * | |
31 | * @blob: Pointer to device tree blob, or NULL if no data to read | |
32 | * @node: Node offset to read from | |
33 | */ | |
34 | static int sandbox_tpm_read_state(const void *blob, int node) | |
35 | { | |
1db235a1 | 36 | struct tpm_state *state = &s_state; |
ed3f5a30 SG |
37 | const char *prop; |
38 | int len; | |
39 | int i; | |
40 | ||
41 | if (!blob) | |
42 | return 0; | |
43 | ||
44 | for (i = 0; i < NV_SEQ_COUNT; i++) { | |
1db235a1 | 45 | struct nvdata_state *nvd = &state->nvdata[i]; |
ed3f5a30 SG |
46 | char prop_name[20]; |
47 | ||
48 | sprintf(prop_name, "nvdata%d", i); | |
49 | prop = fdt_getprop(blob, node, prop_name, &len); | |
1db235a1 SG |
50 | if (len >= NV_DATA_SIZE) |
51 | return log_msg_ret("nvd", -E2BIG); | |
52 | if (prop) { | |
53 | memcpy(nvd->data, prop, len); | |
54 | nvd->length = len; | |
55 | nvd->present = true; | |
ef8a2500 | 56 | } |
ed3f5a30 | 57 | } |
1db235a1 SG |
58 | |
59 | s_state.valid = true; | |
ed3f5a30 SG |
60 | |
61 | return 0; | |
62 | } | |
63 | ||
64 | /** | |
1db235a1 | 65 | * sandbox_tpm_write_state() - Write out our state to the state file |
ed3f5a30 SG |
66 | * |
67 | * The caller will ensure that there is a node ready for the state. The node | |
68 | * may already contain the old state, in which case it is overridden. | |
69 | * | |
70 | * @blob: Device tree blob holding state | |
71 | * @node: Node to write our state into | |
72 | */ | |
73 | static int sandbox_tpm_write_state(void *blob, int node) | |
74 | { | |
1db235a1 | 75 | const struct tpm_state *state = g_state; |
ed3f5a30 SG |
76 | int i; |
77 | ||
1db235a1 SG |
78 | if (!state) |
79 | return 0; | |
80 | ||
ed3f5a30 SG |
81 | /* |
82 | * We are guaranteed enough space to write basic properties. | |
83 | * We could use fdt_add_subnode() to put each set of data in its | |
84 | * own node - perhaps useful if we add access informaiton to each. | |
85 | */ | |
86 | for (i = 0; i < NV_SEQ_COUNT; i++) { | |
1db235a1 | 87 | const struct nvdata_state *nvd = &state->nvdata[i]; |
ed3f5a30 SG |
88 | char prop_name[20]; |
89 | ||
1db235a1 SG |
90 | if (nvd->present) { |
91 | snprintf(prop_name, sizeof(prop_name), "nvdata%d", i); | |
92 | fdt_setprop(blob, node, prop_name, nvd->data, | |
93 | nvd->length); | |
ef8a2500 | 94 | } |
ed3f5a30 SG |
95 | } |
96 | ||
97 | return 0; | |
98 | } | |
99 | ||
100 | SANDBOX_STATE_IO(sandbox_tpm, "google,sandbox-tpm", sandbox_tpm_read_state, | |
101 | sandbox_tpm_write_state); | |
102 | ||
ef8a2500 SG |
103 | static void handle_cap_flag_space(u8 **datap, uint index) |
104 | { | |
105 | struct tpm_nv_data_public pub; | |
106 | ||
107 | /* TPM_NV_PER_PPWRITE */ | |
108 | memset(&pub, '\0', sizeof(pub)); | |
109 | pub.nv_index = __cpu_to_be32(index); | |
110 | pub.pcr_info_read.pcr_selection.size_of_select = __cpu_to_be16( | |
111 | sizeof(pub.pcr_info_read.pcr_selection.pcr_select)); | |
112 | pub.permission.attributes = __cpu_to_be32(1); | |
113 | pub.pcr_info_write = pub.pcr_info_read; | |
114 | memcpy(*datap, &pub, sizeof(pub)); | |
115 | *datap += sizeof(pub); | |
116 | } | |
117 | ||
21baf15b SG |
118 | static int sandbox_tpm_xfer(struct udevice *dev, const uint8_t *sendbuf, |
119 | size_t send_size, uint8_t *recvbuf, | |
120 | size_t *recv_len) | |
ed3f5a30 | 121 | { |
21baf15b | 122 | struct tpm_state *tpm = dev_get_priv(dev); |
ed3f5a30 SG |
123 | uint32_t code, index, length, type; |
124 | uint8_t *data; | |
125 | int seq; | |
126 | ||
127 | code = get_unaligned_be32(sendbuf + sizeof(uint16_t) + | |
128 | sizeof(uint32_t)); | |
04488c4d | 129 | #ifdef DEBUG |
ed3f5a30 SG |
130 | printf("tpm: %zd bytes, recv_len %zd, cmd = %x\n", send_size, |
131 | *recv_len, code); | |
132 | print_buffer(0, sendbuf, 1, send_size, 0); | |
04488c4d | 133 | #endif |
ed3f5a30 | 134 | switch (code) { |
998af319 | 135 | case TPM_CMD_GET_CAPABILITY: |
ed3f5a30 SG |
136 | type = get_unaligned_be32(sendbuf + 14); |
137 | switch (type) { | |
998af319 | 138 | case TPM_CAP_FLAG: |
ed3f5a30 SG |
139 | index = get_unaligned_be32(sendbuf + 18); |
140 | printf("Get flags index %#02x\n", index); | |
141 | *recv_len = 22; | |
142 | memset(recvbuf, '\0', *recv_len); | |
ed3f5a30 SG |
143 | data = recvbuf + TPM_RESPONSE_HEADER_LENGTH + |
144 | sizeof(uint32_t); | |
145 | switch (index) { | |
146 | case FIRMWARE_NV_INDEX: | |
147 | break; | |
148 | case KERNEL_NV_INDEX: | |
ef8a2500 SG |
149 | handle_cap_flag_space(&data, index); |
150 | *recv_len = data - recvbuf - | |
151 | TPM_RESPONSE_HEADER_LENGTH - | |
152 | sizeof(uint32_t); | |
153 | break; | |
154 | case TPM_CAP_FLAG_PERMANENT: { | |
155 | struct tpm_permanent_flags *pflags; | |
156 | ||
157 | pflags = (struct tpm_permanent_flags *)data; | |
158 | memset(pflags, '\0', sizeof(*pflags)); | |
159 | put_unaligned_be32(TPM_TAG_PERMANENT_FLAGS, | |
160 | &pflags->tag); | |
161 | *recv_len = TPM_HEADER_SIZE + 4 + | |
162 | sizeof(*pflags); | |
ed3f5a30 SG |
163 | break; |
164 | } | |
ef8a2500 SG |
165 | default: |
166 | printf(" ** Unknown flags index %x\n", index); | |
167 | return -ENOSYS; | |
168 | } | |
169 | put_unaligned_be32(*recv_len, | |
170 | recvbuf + | |
171 | TPM_RESPONSE_HEADER_LENGTH); | |
ed3f5a30 | 172 | break; |
998af319 | 173 | case TPM_CAP_NV_INDEX: |
ed3f5a30 SG |
174 | index = get_unaligned_be32(sendbuf + 18); |
175 | printf("Get cap nv index %#02x\n", index); | |
176 | put_unaligned_be32(22, recvbuf + | |
177 | TPM_RESPONSE_HEADER_LENGTH); | |
178 | break; | |
179 | default: | |
180 | printf(" ** Unknown 0x65 command type %#02x\n", | |
181 | type); | |
998af319 | 182 | return -ENOSYS; |
ed3f5a30 SG |
183 | } |
184 | break; | |
998af319 | 185 | case TPM_CMD_NV_WRITE_VALUE: |
ed3f5a30 SG |
186 | index = get_unaligned_be32(sendbuf + 10); |
187 | length = get_unaligned_be32(sendbuf + 18); | |
974c98f2 | 188 | seq = sb_tpm_index_to_seq(index); |
ed3f5a30 | 189 | if (seq < 0) |
998af319 | 190 | return -EINVAL; |
ed3f5a30 | 191 | printf("tpm: nvwrite index=%#02x, len=%#02x\n", index, length); |
974c98f2 | 192 | sb_tpm_write_data(tpm->nvdata, seq, sendbuf, 22, length); |
ed3f5a30 | 193 | break; |
998af319 | 194 | case TPM_CMD_NV_READ_VALUE: /* nvread */ |
ed3f5a30 SG |
195 | index = get_unaligned_be32(sendbuf + 10); |
196 | length = get_unaligned_be32(sendbuf + 18); | |
974c98f2 | 197 | seq = sb_tpm_index_to_seq(index); |
ed3f5a30 | 198 | if (seq < 0) |
998af319 | 199 | return -EINVAL; |
ef8a2500 SG |
200 | printf("tpm: nvread index=%#02x, len=%#02x, seq=%#02x\n", index, |
201 | length, seq); | |
974c98f2 | 202 | *recv_len = TPM_HDR_LEN + sizeof(uint32_t) + length; |
ed3f5a30 | 203 | memset(recvbuf, '\0', *recv_len); |
974c98f2 SG |
204 | put_unaligned_be32(length, recvbuf + TPM_HDR_LEN); |
205 | sb_tpm_read_data(tpm->nvdata, seq, recvbuf, TPM_HDR_LEN + 4, | |
206 | length); | |
ed3f5a30 | 207 | break; |
ef8a2500 SG |
208 | case TPM_CMD_EXTEND: |
209 | *recv_len = 30; | |
210 | memset(recvbuf, '\0', *recv_len); | |
211 | break; | |
212 | case TPM_CMD_NV_DEFINE_SPACE: | |
ed3f5a30 SG |
213 | case 0x15: /* pcr read */ |
214 | case 0x5d: /* force clear */ | |
215 | case 0x6f: /* physical enable */ | |
216 | case 0x72: /* physical set deactivated */ | |
217 | case 0x99: /* startup */ | |
ef8a2500 | 218 | case 0x50: /* self test full */ |
ed3f5a30 SG |
219 | case 0x4000000a: /* assert physical presence */ |
220 | *recv_len = 12; | |
221 | memset(recvbuf, '\0', *recv_len); | |
222 | break; | |
223 | default: | |
224 | printf("Unknown tpm command %02x\n", code); | |
998af319 | 225 | return -ENOSYS; |
ed3f5a30 | 226 | } |
04488c4d SG |
227 | #ifdef DEBUG |
228 | printf("tpm: rx recv_len %zd\n", *recv_len); | |
229 | print_buffer(0, recvbuf, 1, *recv_len, 0); | |
230 | #endif | |
ed3f5a30 SG |
231 | |
232 | return 0; | |
233 | } | |
234 | ||
21baf15b | 235 | static int sandbox_tpm_get_desc(struct udevice *dev, char *buf, int size) |
ed3f5a30 | 236 | { |
21baf15b SG |
237 | if (size < 15) |
238 | return -ENOSPC; | |
239 | ||
240 | return snprintf(buf, size, "sandbox TPM"); | |
241 | } | |
242 | ||
243 | static int sandbox_tpm_probe(struct udevice *dev) | |
244 | { | |
245 | struct tpm_state *tpm = dev_get_priv(dev); | |
246 | ||
1db235a1 SG |
247 | if (s_state.valid) |
248 | memcpy(tpm, &s_state, sizeof(*tpm)); | |
249 | g_state = tpm; | |
21baf15b | 250 | |
ed3f5a30 SG |
251 | return 0; |
252 | } | |
253 | ||
21baf15b | 254 | static int sandbox_tpm_open(struct udevice *dev) |
ed3f5a30 | 255 | { |
ed3f5a30 SG |
256 | return 0; |
257 | } | |
258 | ||
21baf15b | 259 | static int sandbox_tpm_close(struct udevice *dev) |
ed3f5a30 | 260 | { |
ed3f5a30 SG |
261 | return 0; |
262 | } | |
21baf15b SG |
263 | |
264 | static const struct tpm_ops sandbox_tpm_ops = { | |
265 | .open = sandbox_tpm_open, | |
266 | .close = sandbox_tpm_close, | |
267 | .get_desc = sandbox_tpm_get_desc, | |
268 | .xfer = sandbox_tpm_xfer, | |
269 | }; | |
270 | ||
271 | static const struct udevice_id sandbox_tpm_ids[] = { | |
272 | { .compatible = "google,sandbox-tpm" }, | |
273 | { } | |
274 | }; | |
275 | ||
e3e2470f WL |
276 | U_BOOT_DRIVER(google_sandbox_tpm) = { |
277 | .name = "google_sandbox_tpm", | |
21baf15b SG |
278 | .id = UCLASS_TPM, |
279 | .of_match = sandbox_tpm_ids, | |
280 | .ops = &sandbox_tpm_ops, | |
281 | .probe = sandbox_tpm_probe, | |
41575d8e | 282 | .priv_auto = sizeof(struct tpm_state), |
21baf15b | 283 | }; |