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 }