xref: /aosp_15_r20/external/coreboot/util/cbfstool/cse_fpt.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /* CSE FPT tool */
3 
4 #include <commonlib/endian.h>
5 #include <getopt.h>
6 #include <errno.h>
7 #include <stdlib.h>
8 #include <sys/stat.h>
9 #include <sys/types.h>
10 #include <unistd.h>
11 
12 #include "common.h"
13 #include "cse_fpt.h"
14 
15 static struct params {
16 	const char *output_dir;
17 	const char *partition_name;
18 
19 	struct fpt_hdr_ops *hdr_ops;
20 } params;
21 
22 #define FPT_ENTRY_TYPE_MASK		0x7f
23 #define FPT_ENTRY_TYPE_SHIFT		0
24 #define GET_FPT_ENTRY_TYPE(x)		(((x) >> FPT_ENTRY_TYPE_SHIFT) & FPT_ENTRY_TYPE_MASK)
25 #define FPT_ENTRY_TYPE_CODE		0x0
26 #define FPT_ENTRY_TYPE_DATA		0x1
27 
28 #define FPT_ENTRY_VALID_MASK		0xff
29 #define FPT_ENTRY_VALID_SHIFT		24
30 #define GET_FPT_ENTRY_VALID(x)		(((x) >> FPT_ENTRY_VALID_SHIFT) & FPT_ENTRY_VALID_MASK)
31 #define FPT_ENTRY_INVALID		0xff
32 #define FPT_ENTRY_VALID			0x0
33 
34 struct fpt_entry {
35 	uint8_t name[4];		/* ASCII short name */
36 	uint8_t rsvd1[4];
37 	uint32_t offset;		/* Offset in bytes from start of FPT binary */
38 	uint32_t length;		/* Size in bytes */
39 	uint8_t rsvd2[12];
40 	uint32_t flags;
41 } __packed;
42 
43 static struct fpt {
44 	struct buffer input_buff;
45 
46 	const struct fpt_hdr_ops *hdr_ops;
47 
48 	fpt_hdr_ptr hdr;
49 	struct fpt_entry *entries;
50 } fpt;
51 
usage(const char * name)52 static void usage(const char *name)
53 {
54 	printf("%s: Utility for CSE FPT\n\n"
55 		"USAGE:\n"
56 		" %s FILE COMMAND\n\n"
57 		"COMMANDs:\n"
58 		" print\n"
59 		" dump [-o OUTPUT_DIR] [-n NAME]\n"
60 		"\nOPTIONS:\n"
61 		" -o OUTPUT_DIR : Directory to dump the partition files in\n"
62 		" -n NAME       : Name of partition to dump\n"
63 		"\n",
64 		name, name);
65 }
66 
get_fpt_buff(struct buffer * input_buff,struct buffer * fpt_buff)67 static int get_fpt_buff(struct buffer *input_buff, struct buffer *fpt_buff)
68 {
69 	/*
70 	 * FPT marker is typically at offset 0x10 in the released CSE binary. Check at offset
71 	 * 0x10 first and if that fails fall back to checking offset 0.
72 	 */
73 	const size_t fpt_offsets[] = { 0x10, 0 };
74 	size_t i;
75 
76 	for (i = 0; i < ARRAY_SIZE(fpt_offsets); i++) {
77 		if (buffer_size(input_buff) < (strlen(FPT_MARKER) + fpt_offsets[i]))
78 			continue;
79 
80 		const uint8_t *data = buffer_get(input_buff);
81 
82 		if (!memcmp(data + fpt_offsets[i], FPT_MARKER, strlen(FPT_MARKER)))
83 			break;
84 	}
85 
86 	if (i == ARRAY_SIZE(fpt_offsets)) {
87 		ERROR("Could not locate FPT at known offsets.\n");
88 		return -1;
89 	}
90 
91 	buffer_clone(fpt_buff, input_buff);
92 	buffer_seek(fpt_buff, fpt_offsets[i]);
93 
94 	return 0;
95 }
96 
read_fpt_entries(struct buffer * buff)97 static int read_fpt_entries(struct buffer *buff)
98 {
99 	size_t i;
100 	struct fpt_entry *e;
101 	const size_t entries = fpt.hdr_ops->get_entry_count(fpt.hdr);
102 	const size_t fpt_entries_size = sizeof(struct fpt_entry) * entries;
103 
104 	if (buffer_size(buff) < fpt_entries_size) {
105 		ERROR("Not enough bytes(actual=0x%zx, expected=0x%zx) for FPT entries!\n",
106 		      buffer_size(buff), fpt_entries_size);
107 		return -1;
108 	}
109 
110 	e = fpt.entries = malloc(fpt_entries_size);
111 
112 	for (i = 0; i < entries; i++, e++) {
113 		READ_MEMBER(buff, e->name);
114 		READ_MEMBER(buff, e->rsvd1);
115 		READ_MEMBER(buff, e->offset);
116 		READ_MEMBER(buff, e->length);
117 		READ_MEMBER(buff, e->rsvd2);
118 		READ_MEMBER(buff, e->flags);
119 	}
120 
121 	return 0;
122 }
123 
get_fpt_hdr_ops(struct buffer * buff)124 static const struct fpt_hdr_ops *get_fpt_hdr_ops(struct buffer *buff)
125 {
126 	static const struct fpt_hdr_ops *hdr_ops[] = {
127 		&fpt_hdr_20_ops,
128 		&fpt_hdr_21_ops,
129 	};
130 
131 	for (size_t i = 0; i < ARRAY_SIZE(hdr_ops); i++) {
132 		if (hdr_ops[i]->match_version(buff))
133 			return hdr_ops[i];
134 	}
135 
136 	return NULL;
137 }
138 
fpt_parse(const char * image_name)139 static int fpt_parse(const char *image_name)
140 {
141 	struct buffer *input_buff = &fpt.input_buff;
142 	struct buffer fpt_buff;
143 
144 	if (buffer_from_file(input_buff, image_name)) {
145 		ERROR("Failed to read input file %s\n", image_name);
146 		return -1;
147 	}
148 
149 	if (get_fpt_buff(input_buff, &fpt_buff))
150 		return -1;
151 
152 	fpt.hdr_ops = get_fpt_hdr_ops(&fpt_buff);
153 	if (fpt.hdr_ops == NULL) {
154 		ERROR("FPT header format not supported!\n");
155 		return -1;
156 	}
157 
158 	fpt.hdr = fpt.hdr_ops->read(&fpt_buff);
159 	if (!fpt.hdr) {
160 		ERROR("Unable to read FPT header!\n");
161 		return -1;
162 	}
163 
164 	return read_fpt_entries(&fpt_buff);
165 }
166 
is_partition_valid(const struct fpt_entry * e)167 static bool is_partition_valid(const struct fpt_entry *e)
168 {
169 	return e->offset != 0 && e->length != 0 &&
170 		GET_FPT_ENTRY_VALID(e->flags) != FPT_ENTRY_INVALID;
171 }
172 
is_partition_code(const struct fpt_entry * e)173 static bool is_partition_code(const struct fpt_entry *e)
174 {
175 	return GET_FPT_ENTRY_TYPE(e->flags) == FPT_ENTRY_TYPE_CODE;
176 }
177 
print_fpt_entry(const struct fpt_entry * e)178 static void print_fpt_entry(const struct fpt_entry *e)
179 {
180 	printf("%-25s0x%-23x0x%-23x%c,%c (0x%.8x)\n",
181 	       e->name, e->offset, e->length,
182 	       is_partition_code(e) ? 'C' : 'D',
183 	       is_partition_valid(e) ? 'V' : 'I',
184 	       e->flags);
185 }
186 
print_fpt_entries(const struct fpt_entry * e,size_t count)187 static void print_fpt_entries(const struct fpt_entry *e, size_t count)
188 {
189 	printf("\n * FPT entries\n");
190 
191 	printf("%-25s%-25s%-25s%-25s\n", "Name", "Offset", "Size", "Flags");
192 
193 	printf("=============================================================="
194 		"===============================\n");
195 
196 	for (size_t i = 0; i < count; i++)
197 		print_fpt_entry(&e[i]);
198 
199 	printf("=============================================================="
200 	       "================================\n");
201 	printf("Flags: I=invalid, V=valid, C=code, D=data\n");
202 }
203 
partition_name_match(const struct fpt_entry * e,const char * name)204 static bool partition_name_match(const struct fpt_entry *e, const char *name)
205 {
206 	if (!name)
207 		return false;
208 
209 	return !memcmp(e->name, name, sizeof(e->name));
210 }
211 
get_partition_entry(const char * name)212 static const struct fpt_entry *get_partition_entry(const char *name)
213 {
214 	for (size_t i = 0; i < fpt.hdr_ops->get_entry_count(fpt.hdr); i++) {
215 		if (partition_name_match(&fpt.entries[i], name))
216 			return &fpt.entries[i];
217 	}
218 
219 	return NULL;
220 }
221 
cmd_print(void)222 static int cmd_print(void)
223 {
224 	if (params.partition_name == NULL) {
225 		fpt.hdr_ops->print(fpt.hdr);
226 		print_fpt_entries(fpt.entries, fpt.hdr_ops->get_entry_count(fpt.hdr));
227 	} else {
228 		const struct fpt_entry *e = get_partition_entry(params.partition_name);
229 		if (e)
230 			print_fpt_entry(e);
231 		else {
232 			ERROR("Partition %s not found!\n", params.partition_name);
233 			return -1;
234 		}
235 	}
236 	return 0;
237 }
238 
should_dump_partition(const struct fpt_entry * e)239 static bool should_dump_partition(const struct fpt_entry *e)
240 {
241 	if (!is_partition_valid(e)) {
242 		if (partition_name_match(e, params.partition_name)) {
243 			ERROR("Invalid partition requested to be dumped!\n");
244 			exit(-1);
245 		}
246 		return false;
247 	}
248 
249 	/* Dump all partitions if no name provided. */
250 	if (params.partition_name == NULL)
251 		return true;
252 
253 	return partition_name_match(e, params.partition_name);
254 }
255 
get_file_path(const struct fpt_entry * e)256 static char *get_file_path(const struct fpt_entry *e)
257 {
258 	size_t filename_len = sizeof(e->name) + 1;
259 	char *filepath;
260 
261 	/* output_dir name followed by '/' */
262 	if (params.output_dir)
263 		filename_len += strlen(params.output_dir) + 1;
264 
265 	filepath = malloc(filename_len);
266 	if (!filepath)
267 		return NULL;
268 
269 	snprintf(filepath, filename_len, "%s%s%s",
270 			params.output_dir ? : "",
271 			params.output_dir ? "/" : "",
272 			e->name);
273 
274 	return filepath;
275 }
276 
write_partition_to_file(const struct fpt_entry * e)277 static int write_partition_to_file(const struct fpt_entry *e)
278 {
279 	size_t end_offset = e->offset + e->length - 1;
280 	struct buffer part_buffer;
281 	char *filepath;
282 
283 	if (end_offset > buffer_size(&fpt.input_buff)) {
284 		ERROR("Offset out of bounds for the partition!\n");
285 		return -1;
286 	}
287 
288 	filepath = get_file_path(e);
289 	if (!filepath) {
290 		ERROR("Failed to allocate space for filepath!\n");
291 		return -1;
292 	}
293 
294 	printf("Dumping %.4s in %s\n", e->name, filepath);
295 
296 	buffer_splice(&part_buffer, &fpt.input_buff, e->offset, e->length);
297 	buffer_write_file(&part_buffer, filepath);
298 
299 	free(filepath);
300 
301 	return 0;
302 }
303 
cmd_dump(void)304 static int cmd_dump(void)
305 {
306 	size_t i;
307 	bool found = false;
308 	struct stat sb;
309 
310 	if (params.output_dir && (stat(params.output_dir, &sb) == -1)) {
311 		ERROR("Failed to stat %s: %s\n", params.output_dir, strerror(errno));
312 		return -1;
313 	}
314 
315 	for (i = 0; i < fpt.hdr_ops->get_entry_count(fpt.hdr); i++) {
316 		if (!should_dump_partition(&fpt.entries[i]))
317 			continue;
318 		found = true;
319 		if (write_partition_to_file(&fpt.entries[i]))
320 			return -1;
321 	}
322 
323 	if (found == false) {
324 		if (params.partition_name)
325 			ERROR("%s not found!\n", params.partition_name);
326 		ERROR("No partitions dumped!\n");
327 		return -1;
328 	}
329 
330 	return 0;
331 }
332 
333 static struct command {
334 	const char *name;
335 	const char *optstring;
336 	int (*function)(void);
337 } commands[] = {
338 	{ "print", "n:?", cmd_print },
339 	{ "dump", "n:o:?", cmd_dump },
340 };
341 
342 static struct option long_options[] = {
343 	{"help",		required_argument,	0,	'h'},
344 	{"partition_name",	required_argument,	0,	'n'},
345 	{"output_dir",		required_argument,	0,	'o'},
346 	{NULL,			0,			0,	0 }
347 };
348 
main(int argc,char ** argv)349 int main(int argc, char **argv)
350 {
351 	if (argc < 3) {
352 		ERROR("Incorrect number of args(%d)!\n", argc);
353 		usage(argv[0]);
354 		return 1;
355 	}
356 
357 	const char *prog_name = argv[0];
358 	const char *image_name = argv[1];
359 	const char *cmd = argv[2];
360 	size_t i;
361 
362 	for (i = 0; i < ARRAY_SIZE(commands); i++) {
363 		if (strcmp(cmd, commands[i].name))
364 			continue;
365 
366 		int c;
367 		int option_index;
368 
369 		while (1) {
370 			c = getopt_long(argc, argv, commands[i].optstring,
371 					long_options, &option_index);
372 
373 			if (c == -1)
374 				break;
375 
376 			if (strchr(commands[i].optstring, c) == NULL) {
377 				ERROR("Invalid option '%c'\n", c);
378 				usage(prog_name);
379 				return 1;
380 			}
381 
382 			switch (c) {
383 			case 'o':
384 				params.output_dir = optarg;
385 				break;
386 			case 'n':
387 				params.partition_name = optarg;
388 				break;
389 			case 'h':
390 			case '?':
391 			default:
392 				usage(prog_name);
393 				return 1;
394 			}
395 		}
396 
397 		break;
398 	}
399 
400 	if (i == ARRAY_SIZE(commands)) {
401 		ERROR("No command match %s\n", cmd);
402 		usage(prog_name);
403 		return 1;
404 	}
405 
406 	if (fpt_parse(image_name))
407 		return 1;
408 
409 	return commands[i].function();
410 }
411