1 /* Copyright 2023 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 #include <inttypes.h>
11
12 #include "flash_helpers.h"
13 #include "fmap.h"
14 #include "futility.h"
15 #include "updater.h"
16
17 #ifdef USE_FLASHROM
18
get_ro_range(struct updater_config * cfg,uint32_t * start,uint32_t * len)19 static int get_ro_range(struct updater_config *cfg,
20 uint32_t *start, uint32_t *len)
21 {
22 int ret = 0;
23
24 /* Read fmap */
25 const char *const regions[] = {FMAP_RO_FMAP};
26 if (flashrom_read_image(&cfg->image_current, regions,
27 ARRAY_SIZE(regions), cfg->verbosity + 1))
28 return -1;
29
30 FmapAreaHeader *wp_ro = NULL;
31 uint8_t *r = fmap_find_by_name(cfg->image_current.data,
32 cfg->image_current.size,
33 NULL, FMAP_RO, &wp_ro);
34 if (!r || !wp_ro) {
35 ERROR("Could not find WP_RO in the FMAP\n");
36 ret = -1;
37 goto err;
38 }
39
40 *start = wp_ro->area_offset;
41 *len = wp_ro->area_size;
42 VB2_DEBUG("start=0x%08" PRIx32 ", length=0x%08" PRIx32 "\n", *start, *len);
43
44 err:
45 free(cfg->image_current.data);
46 cfg->image_current.data = NULL;
47 cfg->image_current.size = 0;
48
49 return ret;
50 }
51
print_flash_size(struct updater_config * cfg)52 static int print_flash_size(struct updater_config *cfg)
53 {
54 uint32_t flash_size;
55 if (flashrom_get_size(cfg->image.programmer, &flash_size,
56 cfg->verbosity + 1)) {
57 ERROR("%s failed.\n", __func__);
58 return -1;
59 }
60
61 printf("Flash size: 0x%08" PRIx32 "\n", flash_size);
62 return 0;
63 }
64
print_flash_info(struct updater_config * cfg)65 static int print_flash_info(struct updater_config *cfg)
66 {
67 char *vendor;
68 char *name;
69 uint32_t vid;
70 uint32_t pid;
71 uint32_t flash_size;
72 if (flashrom_get_info(cfg->image.programmer,
73 &vendor, &name,
74 &vid, &pid,
75 &flash_size,
76 cfg->verbosity + 1)) {
77 ERROR("%s failed.\n", __func__);
78 return -1;
79 }
80
81 printf("Flash vendor: %s\n", vendor);
82 free(vendor);
83 printf("Flash name: %s\n", name);
84 free(name);
85 const uint64_t vidpid = (uint64_t) vid << 32 | pid;
86 printf("Flash vid-pid: 0x%" PRIx64 "\n", vidpid);
87 printf("Flash size: 0x%08" PRIx32 "\n", flash_size);
88
89 /* Get WP_RO region start and length from image */
90 uint32_t ro_start, ro_len;
91 if (get_ro_range(cfg, &ro_start, &ro_len))
92 return -1;
93 printf("Expected WP SR configuration by FW image: (start = 0x%08" PRIx32
94 ", length = 0x%08" PRIx32 ")\n", ro_start, ro_len);
95
96 return 0;
97 }
98
print_wp_status(struct updater_config * cfg,bool ignore_hw)99 static int print_wp_status(struct updater_config *cfg, bool ignore_hw)
100 {
101 /* Get WP_RO region start and length from image */
102 uint32_t ro_start, ro_len;
103 if (get_ro_range(cfg, &ro_start, &ro_len))
104 return -1;
105
106 /* Get current WP region and mode from SPI flash */
107 bool wp_mode;
108 uint32_t wp_start, wp_len;
109 if (flashrom_get_wp(cfg->image.programmer, &wp_mode,
110 &wp_start, &wp_len, cfg->verbosity + 1)) {
111 ERROR("Failed to get WP status\n");
112 return -1;
113 }
114
115 /* A 1 implies HWWP is enabled. */
116 int hwwp = ignore_hw ? 1 : dut_get_property(DUT_PROP_WP_HW, cfg);
117
118 /* SWWP could be disabled, enabled, or misconfigured. */
119 bool is_swwp_disabled = !wp_mode && wp_start == 0 && wp_len == 0;
120 bool is_swwp_enabled = wp_mode && wp_start == ro_start && wp_len == ro_len;
121 if (!is_swwp_disabled && !is_swwp_enabled)
122 WARN("SWWP misconfigured (srp = %d, start = 0x%08" PRIx32
123 ", length = 0x%08" PRIx32 "), WP_RO start = 0x%08" PRIx32
124 ", length = 0x%08" PRIx32 "\n", wp_mode, wp_start, wp_len, ro_start,
125 ro_len);
126
127 if (!hwwp || is_swwp_disabled) {
128 if (!ignore_hw && !is_swwp_disabled) {
129 WARN("HWWP disabled. Result does not reflect the SWWP status.\n");
130 WARN("Use --ignore-hw instead to see the SWWP status specifically.\n");
131 }
132 printf("WP status: disabled\n");
133 } else if (is_swwp_enabled) {
134 printf("WP status: enabled\n");
135 } else {
136 printf("WP status: misconfigured\n");
137 }
138
139 return 0;
140 }
141
142
set_flash_wp(struct updater_config * cfg,bool enable)143 static int set_flash_wp(struct updater_config *cfg, bool enable)
144 {
145 uint32_t wp_start = 0;
146 uint32_t wp_len = 0;
147
148 if (enable) {
149 /* Use the WP_RO region as the protection range */
150 if (get_ro_range(cfg, &wp_start, &wp_len))
151 return -1;
152 }
153
154 if (flashrom_set_wp(cfg->image.programmer, enable,
155 wp_start, wp_len, cfg->verbosity + 1)) {
156 ERROR("Failed to modify WP configuration.\n");
157 return -1;
158 }
159
160 printf("%s WP\n", enable ? "Enabled" : "Disabled");
161
162 return 0;
163 }
164
165 /* Command line options */
166 static struct option const long_opts[] = {
167 SHARED_FLASH_ARGS_LONGOPTS
168 /* name has_arg *flag val */
169 {"help", 0, NULL, 'h'},
170 {"wp-status", 0, NULL, 's'},
171 {"ignore-hw", 0, NULL, 'o'},
172 {"wp-enable", 0, NULL, 'e'},
173 {"wp-disable", 0, NULL, 'd'},
174 {"flash-info", 0, NULL, 'i'},
175 {"flash-size", 0, NULL, 'z'},
176 {"verbose", 0, NULL, 'v'},
177 {NULL, 0, NULL, 0},
178 };
179
180 static const char *const short_opts = "hsoedizv" SHARED_FLASH_ARGS_SHORTOPTS;
181
print_help(int argc,char * argv[])182 static void print_help(int argc, char *argv[])
183 {
184 printf("\n"
185 "Allows for the management of AP SPI flash configuration.\n"
186 "\n"
187 "Usage: " MYNAME " %s [OPTIONS] \n"
188 "\n"
189 "-s, --wp-status \tGet the current flash WP state.\n"
190 " --wp-status \tGet the current HW and SW WP state.\n"
191 "-o, [--ignore-hw] \tGet SW WP state only.\n"
192 "-e, --wp-enable \tEnable protection for the RO image section.\n"
193 "-d, --wp-disable \tDisable all write protection.\n"
194 "-i, --flash-info \tGet flash info.\n"
195 "-z, --flash-size \tGet flash size.\n"
196 "-v, --verbose \tPrint verbose messages\n"
197 "\n"
198 SHARED_FLASH_ARGS_HELP,
199 argv[0]);
200 }
201
do_flash(int argc,char * argv[])202 static int do_flash(int argc, char *argv[])
203 {
204 struct updater_config *cfg = NULL;
205 struct updater_config_arguments args = {0};
206 bool enable_wp = false;
207 bool disable_wp = false;
208 bool get_wp_status = false;
209 bool ignore_hw_wp = false;
210 bool get_size = false;
211 bool get_info = false;
212 int ret = 0;
213
214 opterr = 0;
215 int i;
216 while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) {
217 if (handle_flash_argument(&args, i, optarg))
218 continue;
219 switch (i) {
220 case 'h':
221 print_help(argc, argv);
222 return 0;
223 case 's':
224 get_wp_status = true;
225 break;
226 case 'o':
227 ignore_hw_wp = true;
228 break;
229 case 'e':
230 enable_wp = true;
231 break;
232 case 'd':
233 disable_wp = true;
234 break;
235 case 'i':
236 get_info = true;
237 break;
238 case 'z':
239 get_size = true;
240 break;
241 case 'v':
242 args.verbosity++;
243 break;
244 case '?':
245 if (optopt)
246 ERROR("Unrecognized option: -%c\n", optopt);
247 else if (argv[optind - 1])
248 ERROR("Unrecognized option (possibly '%s')\n",
249 argv[optind - 1]);
250 else
251 ERROR("Unrecognized option.\n");
252 return 1;
253 default:
254 ERROR("Failed parsing options.\n");
255 return 1;
256 }
257 }
258 if (optind < argc) {
259 ERROR("Unexpected arguments.\n");
260 return 1;
261 }
262
263 if (!get_size && !get_info && !enable_wp && !disable_wp && !get_wp_status) {
264 print_help(argc, argv);
265 return 0;
266 }
267
268 if (!get_wp_status && ignore_hw_wp) {
269 ERROR("--ignore-hw must be used with --wp-status.\n");
270 return 1;
271 }
272
273 if (enable_wp && disable_wp) {
274 ERROR("--wp-enable and --wp-disable cannot be used together.\n");
275 return 1;
276 }
277
278 if (setup_flash(&cfg, &args)) {
279 ERROR("While preparing flash\n");
280 return 1;
281 }
282
283 if (get_info)
284 ret = print_flash_info(cfg);
285
286 if (!ret && get_size)
287 ret = print_flash_size(cfg);
288
289 if (!ret && enable_wp)
290 ret = set_flash_wp(cfg, true);
291
292 if (!ret && disable_wp)
293 ret = set_flash_wp(cfg, false);
294
295 if (!ret && get_wp_status)
296 ret = print_wp_status(cfg, ignore_hw_wp);
297
298 teardown_flash(cfg);
299 return ret;
300 }
301 #define CMD_HELP_STR "Manage AP SPI flash properties and writeprotect configuration"
302
303 #else /* USE_FLASHROM */
304
do_flash(int argc,char * argv[])305 static int do_flash(int argc, char *argv[])
306 {
307 FATAL(MYNAME " was built without flashrom support, `flash` command unavailable!\n");
308 return -1;
309 }
310 #define CMD_HELP_STR "Manage AP SPI flash properties and writeprotect configuration (unavailable in this build)"
311
312 #endif /* !USE_FLASHROM */
313
314 DECLARE_FUTIL_COMMAND(flash, do_flash, VBOOT_VERSION_ALL, CMD_HELP_STR);
315