]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
ed3f5a30 SG |
2 | /* |
3 | * Copyright (c) 2013 Google, Inc | |
ed3f5a30 SG |
4 | */ |
5 | ||
4e4bf944 | 6 | #include <display_options.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); | |
7f350a95 | 143 | data = recvbuf + TPM_HDR_LEN + sizeof(uint32_t); |
ed3f5a30 SG |
144 | switch (index) { |
145 | case FIRMWARE_NV_INDEX: | |
146 | break; | |
147 | case KERNEL_NV_INDEX: | |
ef8a2500 | 148 | handle_cap_flag_space(&data, index); |
7f350a95 | 149 | *recv_len = data - recvbuf; |
ef8a2500 SG |
150 | break; |
151 | case TPM_CAP_FLAG_PERMANENT: { | |
152 | struct tpm_permanent_flags *pflags; | |
153 | ||
154 | pflags = (struct tpm_permanent_flags *)data; | |
155 | memset(pflags, '\0', sizeof(*pflags)); | |
156 | put_unaligned_be32(TPM_TAG_PERMANENT_FLAGS, | |
157 | &pflags->tag); | |
158 | *recv_len = TPM_HEADER_SIZE + 4 + | |
159 | sizeof(*pflags); | |
ed3f5a30 SG |
160 | break; |
161 | } | |
ef8a2500 SG |
162 | default: |
163 | printf(" ** Unknown flags index %x\n", index); | |
164 | return -ENOSYS; | |
165 | } | |
7f350a95 | 166 | put_unaligned_be32(*recv_len, recvbuf + TPM_HDR_LEN); |
ed3f5a30 | 167 | break; |
998af319 | 168 | case TPM_CAP_NV_INDEX: |
ed3f5a30 SG |
169 | index = get_unaligned_be32(sendbuf + 18); |
170 | printf("Get cap nv index %#02x\n", index); | |
7f350a95 | 171 | put_unaligned_be32(22, recvbuf + TPM_HDR_LEN); |
ed3f5a30 SG |
172 | break; |
173 | default: | |
174 | printf(" ** Unknown 0x65 command type %#02x\n", | |
175 | type); | |
998af319 | 176 | return -ENOSYS; |
ed3f5a30 SG |
177 | } |
178 | break; | |
998af319 | 179 | case TPM_CMD_NV_WRITE_VALUE: |
ed3f5a30 SG |
180 | index = get_unaligned_be32(sendbuf + 10); |
181 | length = get_unaligned_be32(sendbuf + 18); | |
974c98f2 | 182 | seq = sb_tpm_index_to_seq(index); |
ed3f5a30 | 183 | if (seq < 0) |
998af319 | 184 | return -EINVAL; |
ed3f5a30 | 185 | printf("tpm: nvwrite index=%#02x, len=%#02x\n", index, length); |
974c98f2 | 186 | sb_tpm_write_data(tpm->nvdata, seq, sendbuf, 22, length); |
ed3f5a30 | 187 | break; |
998af319 | 188 | case TPM_CMD_NV_READ_VALUE: /* nvread */ |
ed3f5a30 SG |
189 | index = get_unaligned_be32(sendbuf + 10); |
190 | length = get_unaligned_be32(sendbuf + 18); | |
974c98f2 | 191 | seq = sb_tpm_index_to_seq(index); |
ed3f5a30 | 192 | if (seq < 0) |
998af319 | 193 | return -EINVAL; |
ef8a2500 SG |
194 | printf("tpm: nvread index=%#02x, len=%#02x, seq=%#02x\n", index, |
195 | length, seq); | |
974c98f2 | 196 | *recv_len = TPM_HDR_LEN + sizeof(uint32_t) + length; |
ed3f5a30 | 197 | memset(recvbuf, '\0', *recv_len); |
974c98f2 SG |
198 | put_unaligned_be32(length, recvbuf + TPM_HDR_LEN); |
199 | sb_tpm_read_data(tpm->nvdata, seq, recvbuf, TPM_HDR_LEN + 4, | |
200 | length); | |
ed3f5a30 | 201 | break; |
ef8a2500 SG |
202 | case TPM_CMD_EXTEND: |
203 | *recv_len = 30; | |
204 | memset(recvbuf, '\0', *recv_len); | |
205 | break; | |
206 | case TPM_CMD_NV_DEFINE_SPACE: | |
f9143c12 SG |
207 | index = get_unaligned_be32(sendbuf + 12); |
208 | length = get_unaligned_be32(sendbuf + 77); | |
209 | seq = sb_tpm_index_to_seq(index); | |
210 | if (seq < 0) | |
211 | return -EINVAL; | |
212 | printf("tpm: define_space index=%#02x, len=%#02x, seq=%#02x\n", | |
213 | index, length, seq); | |
214 | sb_tpm_define_data(tpm->nvdata, seq, length); | |
215 | *recv_len = 12; | |
216 | memset(recvbuf, '\0', *recv_len); | |
217 | break; | |
ed3f5a30 SG |
218 | case 0x15: /* pcr read */ |
219 | case 0x5d: /* force clear */ | |
220 | case 0x6f: /* physical enable */ | |
221 | case 0x72: /* physical set deactivated */ | |
222 | case 0x99: /* startup */ | |
ef8a2500 | 223 | case 0x50: /* self test full */ |
ed3f5a30 SG |
224 | case 0x4000000a: /* assert physical presence */ |
225 | *recv_len = 12; | |
226 | memset(recvbuf, '\0', *recv_len); | |
227 | break; | |
228 | default: | |
229 | printf("Unknown tpm command %02x\n", code); | |
998af319 | 230 | return -ENOSYS; |
ed3f5a30 | 231 | } |
04488c4d SG |
232 | #ifdef DEBUG |
233 | printf("tpm: rx recv_len %zd\n", *recv_len); | |
234 | print_buffer(0, recvbuf, 1, *recv_len, 0); | |
235 | #endif | |
ed3f5a30 SG |
236 | |
237 | return 0; | |
238 | } | |
239 | ||
21baf15b | 240 | static int sandbox_tpm_get_desc(struct udevice *dev, char *buf, int size) |
ed3f5a30 | 241 | { |
21baf15b SG |
242 | if (size < 15) |
243 | return -ENOSPC; | |
244 | ||
245 | return snprintf(buf, size, "sandbox TPM"); | |
246 | } | |
247 | ||
248 | static int sandbox_tpm_probe(struct udevice *dev) | |
249 | { | |
250 | struct tpm_state *tpm = dev_get_priv(dev); | |
251 | ||
1db235a1 SG |
252 | if (s_state.valid) |
253 | memcpy(tpm, &s_state, sizeof(*tpm)); | |
254 | g_state = tpm; | |
21baf15b | 255 | |
ed3f5a30 SG |
256 | return 0; |
257 | } | |
258 | ||
21baf15b | 259 | static int sandbox_tpm_open(struct udevice *dev) |
ed3f5a30 | 260 | { |
ed3f5a30 SG |
261 | return 0; |
262 | } | |
263 | ||
21baf15b | 264 | static int sandbox_tpm_close(struct udevice *dev) |
ed3f5a30 | 265 | { |
ed3f5a30 SG |
266 | return 0; |
267 | } | |
21baf15b SG |
268 | |
269 | static const struct tpm_ops sandbox_tpm_ops = { | |
270 | .open = sandbox_tpm_open, | |
271 | .close = sandbox_tpm_close, | |
272 | .get_desc = sandbox_tpm_get_desc, | |
273 | .xfer = sandbox_tpm_xfer, | |
274 | }; | |
275 | ||
276 | static const struct udevice_id sandbox_tpm_ids[] = { | |
277 | { .compatible = "google,sandbox-tpm" }, | |
278 | { } | |
279 | }; | |
280 | ||
e3e2470f WL |
281 | U_BOOT_DRIVER(google_sandbox_tpm) = { |
282 | .name = "google_sandbox_tpm", | |
21baf15b SG |
283 | .id = UCLASS_TPM, |
284 | .of_match = sandbox_tpm_ids, | |
285 | .ops = &sandbox_tpm_ops, | |
286 | .probe = sandbox_tpm_probe, | |
41575d8e | 287 | .priv_auto = sizeof(struct tpm_state), |
21baf15b | 288 | }; |