1 /*
2 * Copyright 2018 The ChromiumOS Authors
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6
7 #include <errno.h>
8 #include <stdbool.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12
13 #include "config.h"
14 #include "desc_parser.h"
15 #include "gsctool.h"
16 #include "tpm_vendor_cmds.h"
17 #include "verify_ro.h"
18
19 /* Index of the matching hash variant. */
20 static ssize_t matching_variant;
21
22 /*
23 * Print out passed in buffer contents in hex, 16 bytes per line, each line
24 * starting with the base address value.
25 *
26 * If the passed in base address is not aligned at 16 byte boundary, skip
27 * positions in the dump line so that the address is displayed rounded down to
28 * the closest lower 16 byte boundary.
29 *
30 * For instance passing base of 0x4007 and size of 20 will result in a
31 * printout like:
32 *
33 * 004000 e0 00 00 00 00 66 c7 05 04
34 * 004010 80 06 e0 06 00 66 c7 05 20 90 06
35 *
36 * If title is nonzero - print out the string it points to before printing
37 * out buffer contents.
38 */
print_buffer_aligned(const char * title,uint32_t base,size_t size,const void * data)39 static void print_buffer_aligned(const char *title, uint32_t base, size_t size,
40 const void *data)
41 {
42 const uint8_t *bytes = data;
43 size_t i;
44 uint8_t alignment;
45
46 /*
47 * Calculate how many characters we need to skip in the first dump
48 * line.
49 */
50 alignment = base % 16;
51 if (alignment) {
52 size += alignment;
53 base &= ~0xf;
54 }
55
56 if (title)
57 printf("%s\n", title);
58
59 /* Let's print data space separated 16 bytes per line. */
60 for (i = 0; i < size; i++) {
61 if (!(i % 16))
62 printf("\n%06zx", base + i);
63
64 if (i < alignment)
65 printf(" ");
66 else
67 printf(" %02x", bytes[i - alignment]);
68 }
69 }
70
71 /* Change the DUT spihash range to the new_type value. */
set_new_range(struct transfer_descriptor * td,enum range_type_t new_type)72 static int set_new_range(struct transfer_descriptor *td,
73 enum range_type_t new_type)
74 {
75 uint32_t rv;
76 struct vendor_cc_spi_hash_request req;
77
78 memset(&req, 0, sizeof(req));
79
80 /* Need to send command to change spihash mode. */
81 switch (new_type) {
82 case AP_RANGE:
83 req.subcmd = SPI_HASH_SUBCMD_AP;
84 break;
85 case EC_RANGE:
86 req.subcmd = SPI_HASH_SUBCMD_EC;
87 break;
88 case EC_GANG_RANGE:
89 req.subcmd = SPI_HASH_SUBCMD_EC;
90 req.flags = SPI_HASH_FLAG_EC_GANG;
91 break;
92 default: /* Should never happen. */
93 return -EINVAL;
94 }
95
96 rv = send_vendor_command(td, VENDOR_CC_SPI_HASH, &req, sizeof(req), 0,
97 NULL);
98
99 if (!rv)
100 return 0;
101
102 if (rv == VENDOR_RC_IN_PROGRESS) {
103 /* This will exit() on error. */
104 poll_for_pp(td, VENDOR_CC_SPI_HASH, SPI_HASH_PP_POLL);
105 } else {
106 fprintf(stderr, "%s: failed setting range type %d, error %d\n",
107 __func__, new_type, rv);
108 return -EINVAL;
109 }
110
111 return 0;
112 }
113
114 /*
115 * Verify a dump descriptor hash section defined by 'range'. The passed in by
116 * pointer structure req has the range offset and size already initialized.
117 *
118 * Make sure that matching hashes are at the same index in the hash variants
119 * arrays within the same board section.
120 */
verify_hash_section(struct transfer_descriptor * td,struct vendor_cc_spi_hash_request * req,struct addr_range * range)121 static int verify_hash_section(struct transfer_descriptor *td,
122 struct vendor_cc_spi_hash_request *req,
123 struct addr_range *range)
124 {
125 size_t i;
126 uint8_t response[sizeof(range->variants->expected_result)];
127 size_t response_size;
128 int rv;
129
130 /* First retrieve hash from the DUT. */
131 response_size = sizeof(response);
132 req->subcmd = SPI_HASH_SUBCMD_SHA256;
133 rv = send_vendor_command(td, VENDOR_CC_SPI_HASH, req, sizeof(*req),
134 response, &response_size);
135
136 if (rv) {
137 fprintf(stderr,
138 "%s: failed retrieving hash at %x, tpm error %d\n",
139 __func__, req->offset, rv);
140 return -EINVAL;
141 }
142
143 if (response_size != sizeof(response)) {
144 fprintf(stderr, "got %zd bytes in response for range %x:%x\n",
145 response_size, req->offset, req->size);
146 return -EINVAL;
147 }
148
149 if (matching_variant < 0) {
150 /* This is the first hash range to be processed. */
151 struct result_node *variant = range->variants;
152
153 for (i = 0; i < range->variant_count; i++) {
154 if (!memcmp(variant->expected_result, response,
155 response_size)) {
156 matching_variant = i;
157 return 0;
158 }
159 variant++;
160 }
161
162 fprintf(stderr, "no matching hash found for range %x:%x\n",
163 req->offset, req->size);
164 return -EINVAL;
165 }
166
167 if (!memcmp(range->variants[matching_variant].expected_result, response,
168 response_size))
169 return 0;
170
171 fprintf(stderr, "hash mismatch for range %x:%x\n", req->offset,
172 req->size);
173
174 return -EINVAL;
175 }
176
177 /*
178 * Dump DUT's memory in the range defined by contents of the passed in req
179 * structure.
180 *
181 * The Cr50 SPI hash dump vendor command implementation limits size of the
182 * dump to 32, so in case the caller requests more than 32 bytes retrieve them
183 * in 32 byte blocks.
184 *
185 * If base address of the range is not aligned at 16, retrieve smaller
186 * quantity such that the following transactions retrieve block starting at
187 * aligned addresses, this makes for a better looking hex dump.
188 */
dump_range(struct transfer_descriptor * td,struct vendor_cc_spi_hash_request * req)189 static int dump_range(struct transfer_descriptor *td,
190 struct vendor_cc_spi_hash_request *req)
191 {
192 size_t remaining_size = req->size;
193 size_t response_size;
194 /* Max size of a single shot is 32 bytes. */
195 const size_t max_transfer = 32;
196 uint8_t response[max_transfer];
197
198 req->subcmd = SPI_HASH_SUBCMD_DUMP;
199 while (remaining_size) {
200 size_t shot_size = max_transfer;
201 uint8_t alignment;
202 uint32_t rv;
203
204 alignment = req->offset % 16;
205
206 if (alignment && ((alignment + remaining_size) > max_transfer))
207 /* first line should be truncated. */
208 shot_size = max_transfer - alignment;
209 else if (shot_size > remaining_size)
210 shot_size = remaining_size;
211
212 req->size = shot_size;
213 response_size = shot_size;
214 rv = send_vendor_command(td, VENDOR_CC_SPI_HASH, req,
215 sizeof(*req), response,
216 &response_size);
217 if (rv) {
218 fprintf(stderr,
219 "%s: failed getting dump contents at %x\n",
220 __func__, req->offset);
221 return -EINVAL;
222 }
223
224 if (response_size != shot_size) {
225 fprintf(stderr,
226 "%s: dump error: got %zd bytes, expected %zd\n",
227 __func__, response_size, shot_size);
228 return -EINVAL;
229 }
230
231 print_buffer_aligned(NULL, req->offset, shot_size, response);
232 remaining_size -= shot_size;
233 req->offset += shot_size;
234 }
235 printf("\n");
236
237 return 0;
238 }
239
240 /*
241 * Iterate through sections of a board descriptor database, retrieving hashes
242 * or straight memory blocks as defined by description sections.
243 */
process_descriptor_sections(struct transfer_descriptor * td)244 static int process_descriptor_sections(struct transfer_descriptor *td)
245 {
246 struct vendor_cc_spi_hash_request req;
247 int rv;
248 struct addr_range *range;
249 enum range_type_t current_range = NOT_A_RANGE;
250
251 do {
252 /*
253 * Retrieve next range descriptor section from the descriptor
254 * database. The function below is guaranteed to set range to
255 * NULL on any error.
256 */
257 rv = parser_get_next_range(&range);
258 if (rv) {
259 /*
260 * ENODATA means all board's sections have been
261 * processed.
262 */
263 if (rv == -ENODATA)
264 rv = 0;
265 break;
266 }
267
268 if (current_range != range->range_type) {
269 rv = set_new_range(td, range->range_type);
270 if (rv)
271 break;
272 }
273
274 memset(&req, 0, sizeof(req));
275 req.offset = range->base_addr;
276 req.size = range->range_size;
277
278 if (range->variant_count)
279 rv = verify_hash_section(td, &req, range);
280 else
281 rv = dump_range(td, &req);
282
283 free(range);
284 range = NULL;
285 } while (!rv);
286
287 if (range)
288 free(range);
289
290 return rv;
291 }
292
verify_ro(struct transfer_descriptor * td,const char * desc_file_name,bool show_machine_output)293 int verify_ro(struct transfer_descriptor *td, const char *desc_file_name,
294 bool show_machine_output)
295 {
296 /* First find out board ID of the target. */
297 struct board_id bid;
298 char rlz_code[sizeof(bid.type) + 1];
299 int section_count = 0;
300 int rv = 0;
301
302 /*
303 * Find out what Board ID is the device we are talking to. This
304 * function calls exit() on any error.
305 */
306 process_bid(td, bid_get, &bid, show_machine_output);
307
308 if (bid.type != ~bid.type_inv) {
309 fprintf(stderr, "Inconsistent board ID: %08x != ~%08x\n",
310 bid.type, bid.type_inv);
311 return -EINVAL;
312 }
313
314 /*
315 * Convert bid from int to asciiz so that it could be used for
316 * strcmp() on the descriptor file section headers.
317 */
318 memcpy(rlz_code, &bid.type, sizeof(rlz_code) - 1);
319 rlz_code[sizeof(rlz_code) - 1] = '\0';
320
321 while (!parser_find_board(desc_file_name, rlz_code)) {
322 /*
323 * Each board section might have different index of the
324 * matching hash variant.
325 */
326 matching_variant = -1;
327
328 section_count++;
329 rv = process_descriptor_sections(td);
330 if (rv)
331 break;
332 }
333
334 if (section_count != 2) {
335 printf("Found wrong number of sections (%d) for board ID %s\n",
336 section_count, rlz_code);
337 rv = -EINVAL;
338 } else if (!rv) {
339 /*
340 * Check was successful, send command to exit verification
341 * mode.
342 */
343 struct vendor_cc_spi_hash_request req;
344
345 memset(&req, 0, sizeof(req));
346 req.subcmd = SPI_HASH_SUBCMD_DISABLE;
347 rv = send_vendor_command(td, VENDOR_CC_SPI_HASH, &req,
348 sizeof(req), 0, NULL);
349 if (rv) {
350 fprintf(stderr, "%s: spi hash disable TPM error %d\n",
351 __func__, rv);
352 rv = -EINVAL;
353 }
354 }
355
356 parser_done();
357 return rv;
358 }
359