xref: /aosp_15_r20/external/vboot_reference/futility/cmd_read.c (revision 8617a60d3594060b7ecbd21bc622a7c14f3cf2bc)
1*8617a60dSAndroid Build Coastguard Worker /* Copyright 2022 The ChromiumOS Authors
2*8617a60dSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
3*8617a60dSAndroid Build Coastguard Worker  * found in the LICENSE file.
4*8617a60dSAndroid Build Coastguard Worker  */
5*8617a60dSAndroid Build Coastguard Worker 
6*8617a60dSAndroid Build Coastguard Worker #include <assert.h>
7*8617a60dSAndroid Build Coastguard Worker #include <stdio.h>
8*8617a60dSAndroid Build Coastguard Worker #include <stdlib.h>
9*8617a60dSAndroid Build Coastguard Worker #include <getopt.h>
10*8617a60dSAndroid Build Coastguard Worker 
11*8617a60dSAndroid Build Coastguard Worker #include "flash_helpers.h"
12*8617a60dSAndroid Build Coastguard Worker #include "futility.h"
13*8617a60dSAndroid Build Coastguard Worker #include "updater.h"
14*8617a60dSAndroid Build Coastguard Worker 
15*8617a60dSAndroid Build Coastguard Worker #ifdef USE_FLASHROM
16*8617a60dSAndroid Build Coastguard Worker 
17*8617a60dSAndroid Build Coastguard Worker /* Command line options */
18*8617a60dSAndroid Build Coastguard Worker static struct option const long_opts[] = {
19*8617a60dSAndroid Build Coastguard Worker 	SHARED_FLASH_ARGS_LONGOPTS
20*8617a60dSAndroid Build Coastguard Worker 	/* name  has_arg *flag val */
21*8617a60dSAndroid Build Coastguard Worker 	{"help", 0, NULL, 'h'},
22*8617a60dSAndroid Build Coastguard Worker 	{"debug", 0, NULL, 'd'},
23*8617a60dSAndroid Build Coastguard Worker 	{"region", 1, NULL, 'r'},
24*8617a60dSAndroid Build Coastguard Worker 	{"split-output", 0, NULL, 's'},
25*8617a60dSAndroid Build Coastguard Worker 	{"verbose", 0, NULL, 'v'},
26*8617a60dSAndroid Build Coastguard Worker 	{NULL, 0, NULL, 0},
27*8617a60dSAndroid Build Coastguard Worker };
28*8617a60dSAndroid Build Coastguard Worker 
29*8617a60dSAndroid Build Coastguard Worker static const char *const short_opts = "hdsr:v" SHARED_FLASH_ARGS_SHORTOPTS;
30*8617a60dSAndroid Build Coastguard Worker 
print_help(int argc,char * argv[])31*8617a60dSAndroid Build Coastguard Worker static void print_help(int argc, char *argv[])
32*8617a60dSAndroid Build Coastguard Worker {
33*8617a60dSAndroid Build Coastguard Worker 	printf("\n"
34*8617a60dSAndroid Build Coastguard Worker 	       "Usage:  " MYNAME " %s [OPTIONS] FILE\n"
35*8617a60dSAndroid Build Coastguard Worker 	       "\n"
36*8617a60dSAndroid Build Coastguard Worker 	       "Reads AP firmware to the FILE\n"
37*8617a60dSAndroid Build Coastguard Worker 	       "-d, --debug            \tPrint debugging messages\n"
38*8617a60dSAndroid Build Coastguard Worker 	       "-r, --region=REGIONS   \tComma delimited regions to read (optional)\n"
39*8617a60dSAndroid Build Coastguard Worker 	       "-v, --verbose          \tPrint verbose messages\n"
40*8617a60dSAndroid Build Coastguard Worker 	       "-s, --split-output     \tOutput each comma delimited regions to own {FILE}_{region_name} (optional)\n"
41*8617a60dSAndroid Build Coastguard Worker 	       SHARED_FLASH_ARGS_HELP,
42*8617a60dSAndroid Build Coastguard Worker 	       argv[0]);
43*8617a60dSAndroid Build Coastguard Worker }
44*8617a60dSAndroid Build Coastguard Worker 
append_to_str_array(const char *** arr,const char * str,size_t * len)45*8617a60dSAndroid Build Coastguard Worker static int append_to_str_array(const char ***arr, const char *str, size_t *len)
46*8617a60dSAndroid Build Coastguard Worker {
47*8617a60dSAndroid Build Coastguard Worker 	const char **expanded_arr = realloc(*arr, sizeof(char *) * ((*len) + 1));
48*8617a60dSAndroid Build Coastguard Worker 	if (!expanded_arr) {
49*8617a60dSAndroid Build Coastguard Worker 		WARN("Failed to allocate memory for string array");
50*8617a60dSAndroid Build Coastguard Worker 		return -1;
51*8617a60dSAndroid Build Coastguard Worker 	}
52*8617a60dSAndroid Build Coastguard Worker 
53*8617a60dSAndroid Build Coastguard Worker 	*arr = expanded_arr;
54*8617a60dSAndroid Build Coastguard Worker 	(*arr)[*len] = str;
55*8617a60dSAndroid Build Coastguard Worker 	(*len)++;
56*8617a60dSAndroid Build Coastguard Worker 	return 0;
57*8617a60dSAndroid Build Coastguard Worker }
58*8617a60dSAndroid Build Coastguard Worker 
parse_region_string(const char *** regions,char * str,size_t * rlen)59*8617a60dSAndroid Build Coastguard Worker static int parse_region_string(const char ***regions, char *str, size_t *rlen)
60*8617a60dSAndroid Build Coastguard Worker {
61*8617a60dSAndroid Build Coastguard Worker 	if (!str)
62*8617a60dSAndroid Build Coastguard Worker 		return -1; /* no regions to parse. */
63*8617a60dSAndroid Build Coastguard Worker 
64*8617a60dSAndroid Build Coastguard Worker 	char *savedptr;
65*8617a60dSAndroid Build Coastguard Worker 	const char *delim = ",";
66*8617a60dSAndroid Build Coastguard Worker 	char *region = strtok_r(str, delim, &savedptr);
67*8617a60dSAndroid Build Coastguard Worker 
68*8617a60dSAndroid Build Coastguard Worker 	while (region) {
69*8617a60dSAndroid Build Coastguard Worker 		if (append_to_str_array(regions, region, rlen))
70*8617a60dSAndroid Build Coastguard Worker 			return -1;
71*8617a60dSAndroid Build Coastguard Worker 		region = strtok_r(NULL, delim, &savedptr);
72*8617a60dSAndroid Build Coastguard Worker 	}
73*8617a60dSAndroid Build Coastguard Worker 
74*8617a60dSAndroid Build Coastguard Worker 	return 0;
75*8617a60dSAndroid Build Coastguard Worker }
76*8617a60dSAndroid Build Coastguard Worker 
read_flash_regions_to_file(struct updater_config * cfg,const char * path,char * str,bool do_split)77*8617a60dSAndroid Build Coastguard Worker static int read_flash_regions_to_file(struct updater_config *cfg,
78*8617a60dSAndroid Build Coastguard Worker 				      const char *path, char *str,
79*8617a60dSAndroid Build Coastguard Worker 				      bool do_split)
80*8617a60dSAndroid Build Coastguard Worker {
81*8617a60dSAndroid Build Coastguard Worker 	int ret = 0;
82*8617a60dSAndroid Build Coastguard Worker 	size_t rlen = 0;
83*8617a60dSAndroid Build Coastguard Worker 	const char **regions = NULL;
84*8617a60dSAndroid Build Coastguard Worker 
85*8617a60dSAndroid Build Coastguard Worker 	if (parse_region_string(&regions, str, &rlen) || !regions || !rlen) {
86*8617a60dSAndroid Build Coastguard Worker 		WARN("No parsable regions to process.\n");
87*8617a60dSAndroid Build Coastguard Worker 		ret = -1;
88*8617a60dSAndroid Build Coastguard Worker 		goto out_free;
89*8617a60dSAndroid Build Coastguard Worker 	}
90*8617a60dSAndroid Build Coastguard Worker 
91*8617a60dSAndroid Build Coastguard Worker 	/* Need to read the FMAP to find regions if we are going to write each
92*8617a60dSAndroid Build Coastguard Worker 	 * region to a separate file. */
93*8617a60dSAndroid Build Coastguard Worker 	if (do_split) {
94*8617a60dSAndroid Build Coastguard Worker 		if (append_to_str_array(&regions, FMAP_RO_FMAP, &rlen)) {
95*8617a60dSAndroid Build Coastguard Worker 			ret = -1;
96*8617a60dSAndroid Build Coastguard Worker 			goto out_free;
97*8617a60dSAndroid Build Coastguard Worker 		}
98*8617a60dSAndroid Build Coastguard Worker 	}
99*8617a60dSAndroid Build Coastguard Worker 
100*8617a60dSAndroid Build Coastguard Worker 	/* Read only the specified regions */
101*8617a60dSAndroid Build Coastguard Worker 	if (flashrom_read_image(&cfg->image_current, regions,
102*8617a60dSAndroid Build Coastguard Worker 				rlen, cfg->verbosity + 1)) {
103*8617a60dSAndroid Build Coastguard Worker 		ret = -1;
104*8617a60dSAndroid Build Coastguard Worker 		goto out_free;
105*8617a60dSAndroid Build Coastguard Worker 	}
106*8617a60dSAndroid Build Coastguard Worker 
107*8617a60dSAndroid Build Coastguard Worker 	if (!do_split) {
108*8617a60dSAndroid Build Coastguard Worker 		if (write_to_file("Wrote AP firmware region to", path,
109*8617a60dSAndroid Build Coastguard Worker 				  cfg->image_current.data,
110*8617a60dSAndroid Build Coastguard Worker 				  cfg->image_current.size)) {
111*8617a60dSAndroid Build Coastguard Worker 			return -1;
112*8617a60dSAndroid Build Coastguard Worker 		}
113*8617a60dSAndroid Build Coastguard Worker 		return 0;
114*8617a60dSAndroid Build Coastguard Worker 	}
115*8617a60dSAndroid Build Coastguard Worker 
116*8617a60dSAndroid Build Coastguard Worker 	/*
117*8617a60dSAndroid Build Coastguard Worker 	 * The last element of regions is FMAP_RO_FMAP, stop at (rlen-1) to
118*8617a60dSAndroid Build Coastguard Worker 	 * only process the user-specified regions.
119*8617a60dSAndroid Build Coastguard Worker 	 */
120*8617a60dSAndroid Build Coastguard Worker 	for (size_t i = 0; i < rlen - 1; i++) {
121*8617a60dSAndroid Build Coastguard Worker 		const char *region = regions[i];
122*8617a60dSAndroid Build Coastguard Worker 
123*8617a60dSAndroid Build Coastguard Worker 		struct firmware_section section;
124*8617a60dSAndroid Build Coastguard Worker 		if (find_firmware_section(&section, &cfg->image_current, region)) {
125*8617a60dSAndroid Build Coastguard Worker 			ERROR("Region '%s' not found in image.\n", region);
126*8617a60dSAndroid Build Coastguard Worker 			ret = -1;
127*8617a60dSAndroid Build Coastguard Worker 			goto out_free;
128*8617a60dSAndroid Build Coastguard Worker 		}
129*8617a60dSAndroid Build Coastguard Worker 		const char *separator = "_";
130*8617a60dSAndroid Build Coastguard Worker 		const size_t fpath_sz = strlen(path) +
131*8617a60dSAndroid Build Coastguard Worker 					strlen(separator) +
132*8617a60dSAndroid Build Coastguard Worker 					strlen(region) +
133*8617a60dSAndroid Build Coastguard Worker 					1; /* +1 for null termination. */
134*8617a60dSAndroid Build Coastguard Worker 		char *fpath = calloc(1, fpath_sz);
135*8617a60dSAndroid Build Coastguard Worker 		if (!fpath) {
136*8617a60dSAndroid Build Coastguard Worker 			ret = -1;
137*8617a60dSAndroid Build Coastguard Worker 			goto out_free;
138*8617a60dSAndroid Build Coastguard Worker 		}
139*8617a60dSAndroid Build Coastguard Worker 		snprintf(fpath, fpath_sz, "%s%s%s", path, separator, region);
140*8617a60dSAndroid Build Coastguard Worker 		if (write_to_file("Wrote AP firmware region to",
141*8617a60dSAndroid Build Coastguard Worker 				  fpath, section.data, section.size)) {
142*8617a60dSAndroid Build Coastguard Worker 			free(fpath);
143*8617a60dSAndroid Build Coastguard Worker 			ret = -1;
144*8617a60dSAndroid Build Coastguard Worker 			goto out_free;
145*8617a60dSAndroid Build Coastguard Worker 		}
146*8617a60dSAndroid Build Coastguard Worker 		free(fpath);
147*8617a60dSAndroid Build Coastguard Worker 	}
148*8617a60dSAndroid Build Coastguard Worker 
149*8617a60dSAndroid Build Coastguard Worker out_free:
150*8617a60dSAndroid Build Coastguard Worker 	free(regions);
151*8617a60dSAndroid Build Coastguard Worker 	return ret;
152*8617a60dSAndroid Build Coastguard Worker }
153*8617a60dSAndroid Build Coastguard Worker 
do_read(int argc,char * argv[])154*8617a60dSAndroid Build Coastguard Worker static int do_read(int argc, char *argv[])
155*8617a60dSAndroid Build Coastguard Worker {
156*8617a60dSAndroid Build Coastguard Worker 	struct updater_config *cfg = NULL;
157*8617a60dSAndroid Build Coastguard Worker 	struct updater_config_arguments args = {0};
158*8617a60dSAndroid Build Coastguard Worker 	int i, errorcnt = 0;
159*8617a60dSAndroid Build Coastguard Worker 	char *regions = NULL;
160*8617a60dSAndroid Build Coastguard Worker 	bool do_split = false;
161*8617a60dSAndroid Build Coastguard Worker 
162*8617a60dSAndroid Build Coastguard Worker 	opterr = 0;
163*8617a60dSAndroid Build Coastguard Worker 	while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) {
164*8617a60dSAndroid Build Coastguard Worker 		if (handle_flash_argument(&args, i, optarg))
165*8617a60dSAndroid Build Coastguard Worker 			continue;
166*8617a60dSAndroid Build Coastguard Worker 		switch (i) {
167*8617a60dSAndroid Build Coastguard Worker 		case 'h':
168*8617a60dSAndroid Build Coastguard Worker 			print_help(argc, argv);
169*8617a60dSAndroid Build Coastguard Worker 			return 0;
170*8617a60dSAndroid Build Coastguard Worker 		case 'd':
171*8617a60dSAndroid Build Coastguard Worker 			debugging_enabled = 1;
172*8617a60dSAndroid Build Coastguard Worker 			args.verbosity++;
173*8617a60dSAndroid Build Coastguard Worker 			break;
174*8617a60dSAndroid Build Coastguard Worker 		case 'r':
175*8617a60dSAndroid Build Coastguard Worker 			regions = strdup(optarg);
176*8617a60dSAndroid Build Coastguard Worker 			if (!regions) {
177*8617a60dSAndroid Build Coastguard Worker 				ERROR("strdup() returned NULL\n");
178*8617a60dSAndroid Build Coastguard Worker 				return 1;
179*8617a60dSAndroid Build Coastguard Worker 			}
180*8617a60dSAndroid Build Coastguard Worker 			break;
181*8617a60dSAndroid Build Coastguard Worker 		case 's':
182*8617a60dSAndroid Build Coastguard Worker 			do_split = true;
183*8617a60dSAndroid Build Coastguard Worker 			break;
184*8617a60dSAndroid Build Coastguard Worker 		case 'v':
185*8617a60dSAndroid Build Coastguard Worker 			args.verbosity++;
186*8617a60dSAndroid Build Coastguard Worker 			break;
187*8617a60dSAndroid Build Coastguard Worker 		case '?':
188*8617a60dSAndroid Build Coastguard Worker 			errorcnt++;
189*8617a60dSAndroid Build Coastguard Worker 			if (optopt)
190*8617a60dSAndroid Build Coastguard Worker 				ERROR("Unrecognized option: -%c\n", optopt);
191*8617a60dSAndroid Build Coastguard Worker 			else if (argv[optind - 1])
192*8617a60dSAndroid Build Coastguard Worker 				ERROR("Unrecognized option (possibly '%s')\n",
193*8617a60dSAndroid Build Coastguard Worker 				      argv[optind - 1]);
194*8617a60dSAndroid Build Coastguard Worker 			else
195*8617a60dSAndroid Build Coastguard Worker 				ERROR("Unrecognized option.\n");
196*8617a60dSAndroid Build Coastguard Worker 			break;
197*8617a60dSAndroid Build Coastguard Worker 		default:
198*8617a60dSAndroid Build Coastguard Worker 			errorcnt++;
199*8617a60dSAndroid Build Coastguard Worker 			ERROR("Failed parsing options.\n");
200*8617a60dSAndroid Build Coastguard Worker 		}
201*8617a60dSAndroid Build Coastguard Worker 	}
202*8617a60dSAndroid Build Coastguard Worker 	if (argc - optind < 1) {
203*8617a60dSAndroid Build Coastguard Worker 		ERROR("Missing output filename\n");
204*8617a60dSAndroid Build Coastguard Worker 		print_help(argc, argv);
205*8617a60dSAndroid Build Coastguard Worker 		return 1;
206*8617a60dSAndroid Build Coastguard Worker 	}
207*8617a60dSAndroid Build Coastguard Worker 	const char *output_file_name = argv[optind++];
208*8617a60dSAndroid Build Coastguard Worker 	if (optind < argc) {
209*8617a60dSAndroid Build Coastguard Worker 		ERROR("Unexpected arguments.\n");
210*8617a60dSAndroid Build Coastguard Worker 		print_help(argc, argv);
211*8617a60dSAndroid Build Coastguard Worker 		return 1;
212*8617a60dSAndroid Build Coastguard Worker 	}
213*8617a60dSAndroid Build Coastguard Worker 	if (do_split && !regions) {
214*8617a60dSAndroid Build Coastguard Worker 		ERROR("Cannot split read of regions without list of regions.\n");
215*8617a60dSAndroid Build Coastguard Worker 		print_help(argc, argv);
216*8617a60dSAndroid Build Coastguard Worker 		return 1;
217*8617a60dSAndroid Build Coastguard Worker 	}
218*8617a60dSAndroid Build Coastguard Worker 
219*8617a60dSAndroid Build Coastguard Worker 	if (setup_flash(&cfg, &args)) {
220*8617a60dSAndroid Build Coastguard Worker 		ERROR("While preparing flash\n");
221*8617a60dSAndroid Build Coastguard Worker 		return 1;
222*8617a60dSAndroid Build Coastguard Worker 	}
223*8617a60dSAndroid Build Coastguard Worker 
224*8617a60dSAndroid Build Coastguard Worker 	if (!regions) {
225*8617a60dSAndroid Build Coastguard Worker 		/* full image read. */
226*8617a60dSAndroid Build Coastguard Worker 		int r = load_system_firmware(cfg, &cfg->image_current);
227*8617a60dSAndroid Build Coastguard Worker 		/*
228*8617a60dSAndroid Build Coastguard Worker 		 * Ignore a parse error as we still want to write the file
229*8617a60dSAndroid Build Coastguard Worker 		 * out in that case
230*8617a60dSAndroid Build Coastguard Worker 		 */
231*8617a60dSAndroid Build Coastguard Worker 		if (r && r != IMAGE_PARSE_FAILURE) {
232*8617a60dSAndroid Build Coastguard Worker 			errorcnt++;
233*8617a60dSAndroid Build Coastguard Worker 			goto err;
234*8617a60dSAndroid Build Coastguard Worker 		}
235*8617a60dSAndroid Build Coastguard Worker 		if (write_to_file("Wrote AP firmware to", output_file_name,
236*8617a60dSAndroid Build Coastguard Worker 				  cfg->image_current.data,
237*8617a60dSAndroid Build Coastguard Worker 				  cfg->image_current.size)) {
238*8617a60dSAndroid Build Coastguard Worker 			errorcnt++;
239*8617a60dSAndroid Build Coastguard Worker 			goto err;
240*8617a60dSAndroid Build Coastguard Worker 		}
241*8617a60dSAndroid Build Coastguard Worker 	} else {
242*8617a60dSAndroid Build Coastguard Worker 		if (read_flash_regions_to_file(cfg, output_file_name, regions, do_split))
243*8617a60dSAndroid Build Coastguard Worker 			errorcnt++;
244*8617a60dSAndroid Build Coastguard Worker 		free(regions);
245*8617a60dSAndroid Build Coastguard Worker 	}
246*8617a60dSAndroid Build Coastguard Worker 
247*8617a60dSAndroid Build Coastguard Worker err:
248*8617a60dSAndroid Build Coastguard Worker 	teardown_flash(cfg);
249*8617a60dSAndroid Build Coastguard Worker 	return !!errorcnt;
250*8617a60dSAndroid Build Coastguard Worker }
251*8617a60dSAndroid Build Coastguard Worker #define CMD_HELP_STR "Read AP firmware"
252*8617a60dSAndroid Build Coastguard Worker 
253*8617a60dSAndroid Build Coastguard Worker #else /* USE_FLASHROM */
254*8617a60dSAndroid Build Coastguard Worker 
do_read(int argc,char * argv[])255*8617a60dSAndroid Build Coastguard Worker static int do_read(int argc, char *argv[])
256*8617a60dSAndroid Build Coastguard Worker {
257*8617a60dSAndroid Build Coastguard Worker 	FATAL(MYNAME " was built without flashrom support, `read` command unavailable!\n");
258*8617a60dSAndroid Build Coastguard Worker 	return -1;
259*8617a60dSAndroid Build Coastguard Worker }
260*8617a60dSAndroid Build Coastguard Worker #define CMD_HELP_STR "Read system firmware (unavailable in this build)"
261*8617a60dSAndroid Build Coastguard Worker 
262*8617a60dSAndroid Build Coastguard Worker #endif /* !USE_FLASHROM */
263*8617a60dSAndroid Build Coastguard Worker 
264*8617a60dSAndroid Build Coastguard Worker DECLARE_FUTIL_COMMAND(read, do_read, VBOOT_VERSION_ALL, CMD_HELP_STR);
265