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