blob: e0c849378f1f3af12f70b9b8dc7e6faaf5157700 [file] [log] [blame]
Stefan Tauner9b32de92014-08-08 23:52:33 +00001/*
2 * This file is part of the flashrom project.
3 *
4 * Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
5 * Copyright (C) 2009 Carl-Daniel Hailfinger
6 * Copyright (C) 2011-2014 Stefan Tauner
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
Stefan Tauner9b32de92014-08-08 23:52:33 +000017 */
18
Nico Huberd39c7d62023-02-11 00:53:08 +010019#include <stdio.h>
Stefan Tauner9b32de92014-08-08 23:52:33 +000020#include <stdlib.h>
21#include <string.h>
Nico Huber85c2cf82024-11-02 13:47:06 +010022#include <getopt.h>
Nico Huber0da839b2023-02-11 01:40:07 +010023#include <sys/stat.h>
Nico Huber34e783a2023-02-11 00:30:27 +010024
Stefan Tauner9b32de92014-08-08 23:52:33 +000025#include "flash.h"
Nico Huber34e783a2023-02-11 00:30:27 +010026#include "cli.h"
27
Nico Huberd91822a2023-02-11 00:43:54 +010028int cli_check_filename(const char *const filename, const char *const type)
29{
30 if (!filename || (filename[0] == '\0')) {
31 fprintf(stderr, "Error: No %s file specified.\n", type);
32 return 1;
33 }
34 /* Not an error, but maybe the user intended to specify a CLI option instead of a file name. */
35 if (filename[0] == '-' && filename[1] != '\0')
36 fprintf(stderr, "Warning: Supplied %s file name starts with -\n", type);
37 return 0;
38}
39
Nico Huberd39c7d62023-02-11 00:53:08 +010040/* Ensure a file is open by means of fstat */
41static bool cli_check_file(FILE *file)
42{
43 struct stat statbuf;
44
45 if (fstat(fileno(file), &statbuf) < 0)
46 return false;
47 return true;
48}
49
50int cli_init(void)
51{
52 /*
53 * Safety-guard against a user who has (mistakenly) closed
54 * stdout or stderr before exec'ing flashprog. We disable
55 * logging in this case to prevent writing log data to a flash
56 * chip when a flash device gets opened with fd 1 or 2.
57 */
58 if (cli_check_file(stdout) && cli_check_file(stderr)) {
59 flashprog_set_log_callback((flashprog_log_callback *)&flashprog_print_cb);
60 }
61
62 print_version();
63 print_banner();
64
65 return flashprog_init(/* perform_selfcheck => */1);
66}
67
Nico Hubere05e3342024-11-14 14:08:56 +010068int cli_parse_log_args(struct log_args *const args, const int opt, const char *const opt_arg)
Nico Huberdf6ce9f2023-02-11 16:16:04 +010069{
70 switch (opt) {
71 case OPTION_VERBOSE:
72 args->screen_level++;
73 if (args->screen_level > args->logfile_level)
74 args->logfile_level = args->screen_level;
75 break;
76 case OPTION_LOGFILE:
Nico Hubere05e3342024-11-14 14:08:56 +010077 if (cli_check_filename(opt_arg, "log"))
Nico Huberdf6ce9f2023-02-11 16:16:04 +010078 return 1;
79
80 if (args->logfile) {
81 fprintf(stderr, "Warning: -o/--output specified multiple times.\n");
82 free(args->logfile);
83 }
84
Nico Hubere05e3342024-11-14 14:08:56 +010085 args->logfile = strdup(opt_arg);
Nico Huberdf6ce9f2023-02-11 16:16:04 +010086 if (!args->logfile) {
87 fprintf(stderr, "Out of memory!\n");
88 return 2;
89 }
90 break;
91 }
92
93 return 0;
94}
95
Nico Hubere05e3342024-11-14 14:08:56 +010096int cli_parse_flash_args(struct flash_args *const args, const int opt, const char *const opt_arg)
Nico Huber34e783a2023-02-11 00:30:27 +010097{
98 switch (opt) {
99 case OPTION_PROGRAMMER:
100 if (args->prog_name) {
101 fprintf(stderr,
102 "Error: --programmer specified more than once. You can separate multiple\n"
103 "arguments for a programmer with ','. Please see the man page for details.\n");
104 return 1;
105 }
Nico Hubere05e3342024-11-14 14:08:56 +0100106 const char *const colon = strchr(opt_arg, ':');
Nico Huber34e783a2023-02-11 00:30:27 +0100107 if (colon) {
Nico Hubere05e3342024-11-14 14:08:56 +0100108 args->prog_name = strndup(opt_arg, colon - opt_arg);
Nico Huber34e783a2023-02-11 00:30:27 +0100109 args->prog_args = strdup(colon + 1);
110 } else {
Nico Hubere05e3342024-11-14 14:08:56 +0100111 args->prog_name = strdup(opt_arg);
Nico Huber34e783a2023-02-11 00:30:27 +0100112 }
113 if (!args->prog_name || (colon && !args->prog_args)) {
114 fprintf(stderr, "Out of memory!\n");
115 return 2;
116 }
117 break;
118 case OPTION_CHIP:
119 if (args->chip) {
120 fprintf(stderr, "Error: --chip specified more than once.\n");
121 return 1;
122 }
Nico Hubere05e3342024-11-14 14:08:56 +0100123 args->chip = strdup(opt_arg);
Nico Huber34e783a2023-02-11 00:30:27 +0100124 if (!args->chip) {
125 fprintf(stderr, "Out of memory!\n");
126 return 2;
127 }
128 break;
129 }
130
131 return 0;
132}
Stefan Tauner9b32de92014-08-08 23:52:33 +0000133
Nico Hubere05e3342024-11-14 14:08:56 +0100134int cli_parse_layout_args(struct layout_args *const args, const int opt, const char *const opt_arg)
Nico Huberd91822a2023-02-11 00:43:54 +0100135{
136 if (args->layoutfile || args->ifd || args->fmap || args->fmapfile) {
137 fprintf(stderr, "Error: Only one layout source may be specified.\n");
138 return 1;
139 }
140
141 switch (opt) {
142 case OPTION_LAYOUT:
Nico Hubere05e3342024-11-14 14:08:56 +0100143 if (cli_check_filename(opt_arg, "layout"))
Nico Huberd91822a2023-02-11 00:43:54 +0100144 return 1;
145
Nico Hubere05e3342024-11-14 14:08:56 +0100146 args->layoutfile = strdup(opt_arg);
Nico Huberd91822a2023-02-11 00:43:54 +0100147 if (!args->layoutfile) {
148 fprintf(stderr, "Out of memory!\n");
149 return 2;
150 }
151 break;
152 case OPTION_IFD:
153 args->ifd = true;
154 break;
155 case OPTION_FMAP:
156 args->fmap = true;
157 break;
158 case OPTION_FMAP_FILE:
Nico Hubere05e3342024-11-14 14:08:56 +0100159 if (cli_check_filename(opt_arg, "fmap"))
Nico Huberd91822a2023-02-11 00:43:54 +0100160 return 1;
161
Nico Hubere05e3342024-11-14 14:08:56 +0100162 args->fmapfile = strdup(opt_arg);
Nico Huberd91822a2023-02-11 00:43:54 +0100163 if (!args->fmapfile) {
164 fprintf(stderr, "Out of memory!\n");
165 return 2;
166 }
167 break;
168 }
169
170 return 0;
171}
172
Nico Huber0da839b2023-02-11 01:40:07 +0100173int cli_process_layout_args(struct flashprog_layout **const layout,
174 struct flashprog_flashctx *const flash,
175 const struct layout_args *const args)
176{
177 *layout = NULL;
178
179 if (args->layoutfile) {
180 if (layout_from_file(layout, args->layoutfile))
181 return 1;
182 } else if (args->ifd) {
183 if (flashprog_layout_read_from_ifd(layout, flash, NULL, 0))
184 return 1;
185 } else if (args->fmap) {
186 if (flashprog_layout_read_fmap_from_rom(layout, flash, 0, flashprog_flash_getsize(flash)))
187 return 1;
188 } else if (args->fmapfile) {
189 struct stat s;
190 if (stat(args->fmapfile, &s) != 0) {
191 msg_gerr("Failed to stat fmapfile \"%s\"\n", args->fmapfile);
192 return 1;
193 }
194
195 size_t fmapfile_size = s.st_size;
196 uint8_t *fmapfile_buffer = malloc(fmapfile_size);
197 if (!fmapfile_buffer) {
198 fprintf(stderr, "Out of memory!\n");
199 return 1;
200 }
201
202 if (read_buf_from_file(fmapfile_buffer, fmapfile_size, args->fmapfile)) {
203 free(fmapfile_buffer);
204 return 1;
205 }
206
207 if (flashprog_layout_read_fmap_from_buffer(layout, flash, fmapfile_buffer, fmapfile_size)) {
208 free(fmapfile_buffer);
209 return 1;
210 }
211 free(fmapfile_buffer);
212 }
213
214 return 0;
215}
216
Nico Huber85c2cf82024-11-02 13:47:06 +0100217/* Note: Changes global `optind` from <getopt.h>. */
218int getopt_command(const int argc, char *const argv[], const struct opt_command *const opts)
219{
220 if (optind >= argc || argv[optind][0] == '-')
221 return -1;
222
223 unsigned int i;
224 for (i = 0; opts[i].name; ++i) {
225 if (!strcmp(argv[optind], opts[i].name)) {
226 ++optind;
227 return opts[i].val;
228 }
229 }
230 return -1;
231}
232
Nico Huber8f7122c2023-02-11 18:28:33 +0100233void print_generic_options(const bool layout_options)
Nico Huber24c09772024-11-02 13:46:21 +0100234{
235 fprintf(stderr, "\n"
236 "Where generic <options> are\n"
237 " -p | --programmer <name>[:<params>] specify the programmer device. One of\n");
238 list_programmers_linebreak(12, 80, 0);
239 fprintf(stderr, "\n"
240 " -c | --chip <chipname> probe only for specified flash chip\n"
241 " -V | --verbose more verbose output\n"
242 " -o | --output <logfile> log output to <logfile>\n"
243 " -h | --help print help text\n");
Nico Huber8f7122c2023-02-11 18:28:33 +0100244
245 if (!layout_options)
246 return;
247 fprintf(stderr, "\n"
248 "and layout <options> are\n"
249 " -l | --layout <layoutfile> read ROM layout from <layoutfile>\n"
250 " --fmap-file <fmapfile> read ROM layout from fmap in <fmapfile>\n"
251 " --fmap read ROM layout from fmap embedded in ROM\n"
252 " --ifd read layout from an Intel Flash Descriptor\n");
Nico Huber24c09772024-11-02 13:46:21 +0100253}
254
Stefan Tauner9b32de92014-08-08 23:52:33 +0000255void print_chip_support_status(const struct flashchip *chip)
256{
257 if (chip->feature_bits & FEATURE_OTP) {
Nico Huberc3b02dc2023-08-12 01:13:45 +0200258 msg_cdbg("This chip may contain one-time programmable memory. flashprog cannot read\n"
Stefan Tauner9b32de92014-08-08 23:52:33 +0000259 "and may never be able to write it, hence it may not be able to completely\n"
260 "clone the contents of this chip (see man page for details).\n");
261 }
262
263 if ((chip->tested.erase == NA) && (chip->tested.write == NA)) {
264 msg_cdbg("This chip's main memory can not be erased/written by design.\n");
265 }
266
267 if ((chip->tested.probe == BAD) || (chip->tested.probe == NT) ||
268 (chip->tested.read == BAD) || (chip->tested.read == NT) ||
269 (chip->tested.erase == BAD) || (chip->tested.erase == NT) ||
Nico Huberbb4f3b02022-12-30 14:28:06 +0100270 (chip->tested.write == BAD) || (chip->tested.write == NT)) {
Stefan Tauner9b32de92014-08-08 23:52:33 +0000271 msg_cinfo("===\n");
272 if ((chip->tested.probe == BAD) ||
273 (chip->tested.read == BAD) ||
274 (chip->tested.erase == BAD) ||
Nico Huberbb4f3b02022-12-30 14:28:06 +0100275 (chip->tested.write == BAD)) {
Stefan Tauner9b32de92014-08-08 23:52:33 +0000276 msg_cinfo("This flash part has status NOT WORKING for operations:");
277 if (chip->tested.probe == BAD)
278 msg_cinfo(" PROBE");
279 if (chip->tested.read == BAD)
280 msg_cinfo(" READ");
281 if (chip->tested.erase == BAD)
282 msg_cinfo(" ERASE");
283 if (chip->tested.write == BAD)
284 msg_cinfo(" WRITE");
285 msg_cinfo("\n");
286 }
287 if ((chip->tested.probe == NT) ||
288 (chip->tested.read == NT) ||
289 (chip->tested.erase == NT) ||
Nico Huberbb4f3b02022-12-30 14:28:06 +0100290 (chip->tested.write == NT)) {
Stefan Tauner9b32de92014-08-08 23:52:33 +0000291 msg_cinfo("This flash part has status UNTESTED for operations:");
292 if (chip->tested.probe == NT)
293 msg_cinfo(" PROBE");
294 if (chip->tested.read == NT)
295 msg_cinfo(" READ");
296 if (chip->tested.erase == NT)
297 msg_cinfo(" ERASE");
298 if (chip->tested.write == NT)
299 msg_cinfo(" WRITE");
300 msg_cinfo("\n");
301 }
302 msg_cinfo("The test status of this chip may have been updated in the latest development\n"
Nico Huberc3b02dc2023-08-12 01:13:45 +0200303 "version of flashprog. If you are running the latest development version,\n"
304 "please email a report to flashprog@flashprog.org if any of the above\n"
Nico Huberac90af62022-12-18 00:22:47 +0000305 "operations work correctly for you with this flash chip. Please include the\n"
Nico Huberc3b02dc2023-08-12 01:13:45 +0200306 "flashprog log file for all operations you tested (see the man page for details),\n"
Nico Huberac90af62022-12-18 00:22:47 +0000307 "and mention which mainboard or programmer you tested in the subject line.\n"
Stefan Tauner9b32de92014-08-08 23:52:33 +0000308 "Thanks for your help!\n");
309 }
310}