xref: /aosp_15_r20/external/vboot_reference/futility/cmd_flash_util.c (revision 8617a60d3594060b7ecbd21bc622a7c14f3cf2bc)
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