xref: /aosp_15_r20/external/gsc-utils/extra/usb_updater/verify_ro.c (revision 4f2df630800bdcf1d4f0decf95d8a1cb87344f5f)
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