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(®ions, 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(®ions, 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(§ion, &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