blob: 51fa9d0fbcebf2bcc96d628031f761fc85220395 [file] [log] [blame]
Furquan Shaikh796aeeb2021-09-16 22:02:01 -07001/* 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
15static 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
34struct 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
43static 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
52static 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
Furquan Shaikh796aeeb2021-09-16 22:02:01 -070067static 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
97static 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
124static 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
139static 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
167static 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
173static bool is_partition_code(const struct fpt_entry *e)
174{
175 return GET_FPT_ENTRY_TYPE(e->flags) == FPT_ENTRY_TYPE_CODE;
176}
177
178static 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
187static 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
204static 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
212static 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
222static 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
239static 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
256static 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
277static 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
304static 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
333static 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
342static 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
349int 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}