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