/ util / cbfstool / cse_fpt.c
cse_fpt.c
  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  
 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  
 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  
 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  
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  
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  
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  
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  
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  
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  
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  
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  
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  
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  
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  
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  
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  
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  }