Branch data Line data Source code
1 : : // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
2 : : /*
3 : : * Assemble a FFS Image (no, not that FFS, this FFS)
4 : : *
5 : : * Copyright 2013-2019 IBM Corp.
6 : : */
7 : :
8 : : #include <ctype.h>
9 : : #include <errno.h>
10 : : #include <fcntl.h>
11 : : #include <getopt.h>
12 : : #include <limits.h>
13 : : #include <stdio.h>
14 : : #include <stdlib.h>
15 : : #include <string.h>
16 : : #include <stdint.h>
17 : : #include <stdbool.h>
18 : : #include <sys/stat.h>
19 : : #include <sys/mman.h>
20 : : #include <unistd.h>
21 : :
22 : : #include <libflash/libflash.h>
23 : : #include <libflash/libffs.h>
24 : : #include <libflash/blocklevel.h>
25 : : #include <libflash/ecc.h>
26 : : #include <common/arch_flash.h>
27 : :
28 : : /*
29 : : * Flags:
30 : : * - E: ECC for this part
31 : : */
32 : :
33 : : /*
34 : : * TODO FIXME
35 : : * Max line theoretical max size:
36 : : * - name: 15 chars = 15
37 : : * - base: 0xffffffff = 10
38 : : * - size: 0xffffffff = 10
39 : : * - flag: E = 1
40 : : *
41 : : * 36 + 3 separators = 39
42 : : * Plus \n 40
43 : : * Lets do 50.
44 : : */
45 : : #define MAX_LINE (PATH_MAX+255)
46 : : #define MAX_TOCS 10
47 : : #define SEPARATOR ','
48 : :
49 : : /* Full version number (possibly includes gitid). */
50 : : extern const char version[];
51 : :
52 : 56 : static int read_u32(const char *input, uint32_t *val)
53 : : {
54 : 56 : char *endptr;
55 : 56 : *val = strtoul(input, &endptr, 0);
56 : 56 : return (*endptr == SEPARATOR) ? 0 : 1;
57 : : }
58 : :
59 : 168 : static const char *advance_line(const char *input)
60 : : {
61 : 168 : char *pos = strchr(input, SEPARATOR);
62 : 168 : if (!pos)
63 : : return NULL;
64 : 168 : return pos + 1;
65 : : }
66 : :
67 : 0 : static struct ffs_hdr *parse_toc(const char *line, uint32_t block_size,
68 : : uint32_t block_count)
69 : : {
70 : 0 : struct ffs_entry_user user;
71 : 0 : struct ffs_entry *ent;
72 : 0 : struct ffs_hdr *hdr;
73 : 0 : uint32_t tbase;
74 : 0 : int rc;
75 : :
76 : 0 : if (read_u32(line, &tbase)) {
77 : 0 : fprintf(stderr, "Couldn't parse TOC base address\n");
78 : 0 : return NULL;
79 : : }
80 : :
81 : 0 : line = advance_line(line);
82 : 0 : if (!line) {
83 : 0 : fprintf(stderr, "Couldn't find TOC flags\n");
84 : 0 : return NULL;
85 : : }
86 : :
87 : 0 : rc = ffs_string_to_entry_user(line, strlen(line), &user);
88 : 0 : if (rc) {
89 : 0 : fprintf(stderr, "Couldn't parse TOC flags\n");
90 : 0 : return NULL;
91 : : }
92 : :
93 : 0 : rc = ffs_entry_new("part", tbase, 0, &ent);
94 : 0 : if (rc) {
95 : 0 : fprintf(stderr, "Couldn't make entry for TOC@0x%08x\n", tbase);
96 : 0 : return NULL;
97 : : }
98 : :
99 : 0 : rc = ffs_entry_user_set(ent, &user);
100 : 0 : if (rc) {
101 : 0 : fprintf(stderr, "Invalid TOC flag\n");
102 : 0 : ffs_entry_put(ent);
103 : 0 : return NULL;
104 : : }
105 : :
106 : 0 : rc = ffs_hdr_new(block_size, block_count, &ent, &hdr);
107 : 0 : if (rc) {
108 : 0 : hdr = NULL;
109 : 0 : fprintf(stderr, "Couldn't make header for TOC@0x%08x\n", tbase);
110 : : }
111 : :
112 : 0 : ffs_entry_put(ent);
113 : 0 : return hdr;
114 : : }
115 : :
116 : 28 : static int parse_entry(struct blocklevel_device *bl,
117 : : struct ffs_hdr **tocs, const char *line, bool allow_empty)
118 : : {
119 : 28 : char name[FFS_PART_NAME_MAX + 2] = { 0 };
120 : 28 : struct ffs_entry_user user = { 0 };
121 : 28 : uint32_t pbase, psize, pactual, i;
122 : 28 : struct ffs_entry *new_entry;
123 : 28 : struct stat data_stat;
124 : 28 : const char *filename;
125 : 28 : bool added = false;
126 : 28 : uint8_t *data_ptr, ecc = 0;
127 : 28 : int data_fd, rc;
128 : 28 : char *pos;
129 : :
130 : 28 : memcpy(name, line, FFS_PART_NAME_MAX + 1);
131 : 28 : pos = strchr(name, SEPARATOR);
132 : : /* There is discussion to be had as to if we should bail here */
133 : 28 : if (!pos) {
134 : 0 : fprintf(stderr, "WARNING: Long partition name will get truncated to '%s'\n",
135 : : name);
136 : 0 : name[FFS_PART_NAME_MAX] = '\0';
137 : : } else {
138 : 28 : *pos = '\0';
139 : : }
140 : :
141 : 28 : line = advance_line(line);
142 : 28 : if (!line || read_u32(line, &pbase)) {
143 : 0 : fprintf(stderr, "Couldn't parse '%s' partition base address\n",
144 : : name);
145 : 0 : return -1;
146 : : }
147 : :
148 : 28 : line = advance_line(line);
149 : 28 : if (!line || read_u32(line, &psize)) {
150 : 0 : fprintf(stderr, "Couldn't parse '%s' partition length\n",
151 : : name);
152 : 0 : return -1;
153 : : }
154 : :
155 : 28 : line = advance_line(line);
156 : 28 : if (!line || !advance_line(line)) {
157 : 0 : fprintf(stderr, "Couldn't find '%s' partition flags\n",
158 : : name);
159 : 0 : return -1;
160 : : }
161 : :
162 : 28 : rc = ffs_string_to_entry_user(line, advance_line(line) - 1 - line, &user);
163 : 28 : if (rc) {
164 : 0 : fprintf(stderr, "Couldn't parse '%s' partition flags\n",
165 : : name);
166 : 0 : return -1;
167 : : }
168 : 28 : line = advance_line(line);
169 : : /* Already checked return value */
170 : :
171 : 28 : rc = ffs_entry_new(name, pbase, psize, &new_entry);
172 : 28 : if (rc) {
173 : 0 : fprintf(stderr, "Invalid entry '%s' 0x%08x for 0x%08x\n",
174 : : name, pbase, psize);
175 : 0 : return -1;
176 : : }
177 : :
178 : 28 : rc = ffs_entry_user_set(new_entry, &user);
179 : 28 : if (rc) {
180 : 0 : fprintf(stderr, "Couldn't set '%s' partition flags\n",
181 : : name);
182 : 0 : ffs_entry_put(new_entry);
183 : 0 : return -1;
184 : : }
185 : :
186 : 28 : if (has_flag(new_entry, FFS_MISCFLAGS_BACKUP)) {
187 : 1 : rc = ffs_entry_set_act_size(new_entry, 0);
188 : 1 : if (rc) {
189 : 0 : fprintf(stderr, "Couldn't set '%s' partition actual size\n",
190 : : name);
191 : 0 : ffs_entry_put(new_entry);
192 : 0 : return -1;
193 : : }
194 : : }
195 : :
196 : 28 : if (!advance_line(line)) {
197 : 0 : fprintf(stderr, "Missing TOC field for '%s' partition\n",
198 : : name);
199 : 0 : ffs_entry_put(new_entry);
200 : 0 : return -1;
201 : : }
202 : :
203 : 28 : while (*line != SEPARATOR) {
204 : 0 : int toc = *(line++);
205 : :
206 : 0 : if (!isdigit(toc)) {
207 : 0 : fprintf(stderr, "Bad TOC value %d (%c) for '%s' partition\n",
208 : : toc, toc, name);
209 : 0 : ffs_entry_put(new_entry);
210 : 0 : return -1;
211 : : }
212 : 0 : toc -= '0';
213 : 0 : if (!tocs[toc]) {
214 : 0 : fprintf(stderr, "No TOC with ID %d for '%s' partition\n",
215 : : toc, name);
216 : 0 : ffs_entry_put(new_entry);
217 : 0 : return -1;
218 : : }
219 : 0 : rc = ffs_entry_add(tocs[toc], new_entry);
220 : 0 : if (rc) {
221 : 0 : fprintf(stderr, "Couldn't add '%s' partition to TOC %d: %d\n",
222 : : name, toc, rc);
223 : 0 : ffs_entry_put(new_entry);
224 : 0 : return rc;
225 : : }
226 : : added = true;
227 : : }
228 : 28 : if (!added) {
229 : : /*
230 : : * They didn't specify a TOC in the TOC field, use
231 : : * TOC@0 as the default
232 : : */
233 : 28 : rc = ffs_entry_add(tocs[0], new_entry);
234 : 28 : if (rc) {
235 : 0 : fprintf(stderr, "Couldn't add '%s' partition to default TOC: %d\n",
236 : : name, rc);
237 : 0 : ffs_entry_put(new_entry);
238 : 0 : return rc;
239 : : }
240 : : }
241 : 28 : ffs_entry_put(new_entry);
242 : :
243 : 56 : if (*line != '\0' && *(line + 1) != '\0') {
244 : 28 : size_t data_len;
245 : :
246 : 28 : filename = line + 1;
247 : :
248 : : /*
249 : : * Support flashing already ecc'd data as this is the case
250 : : * for POWER8 SBE image binary.
251 : : */
252 : 28 : if (has_ecc(new_entry) && !strstr(filename, ".ecc"))
253 : 20 : blocklevel_ecc_protect(bl, pbase, psize);
254 : :
255 : 28 : data_fd = open(filename, O_RDONLY);
256 : 28 : if (data_fd == -1) {
257 : 0 : fprintf(stderr, "Couldn't open file '%s' for '%s' partition "
258 : : "(%m)\n", filename, name);
259 : 0 : return -1;
260 : : }
261 : :
262 : 28 : if (fstat(data_fd, &data_stat) == -1) {
263 : 0 : fprintf(stderr, "Couldn't stat file '%s' for '%s' partition "
264 : : "(%m)\n", filename, name);
265 : 0 : close(data_fd);
266 : 0 : return -1;
267 : : }
268 : :
269 : 28 : data_ptr = calloc(1, psize);
270 : 28 : if (!data_ptr) {
271 : : return -1;
272 : : }
273 : :
274 : 28 : pactual = data_stat.st_size;
275 : :
276 : : /*
277 : : * There's two char device inputs we care about: /dev/zero and
278 : : * /dev/urandom. Both have a stat.st_size of zero so read in
279 : : * a full partition worth, accounting for ECC overhead.
280 : : */
281 : 28 : if (!pactual && S_ISCHR(data_stat.st_mode)) {
282 : 28 : pactual = psize;
283 : :
284 : 28 : if (has_ecc(new_entry)) {
285 : 20 : pactual = ecc_buffer_size_minus_ecc(pactual);
286 : :
287 : : /* ECC input size needs to be a multiple of 8 */
288 : 20 : pactual = pactual & ~0x7;
289 : : }
290 : : }
291 : : /*
292 : : * Sanity check that the file isn't too large for
293 : : * partition
294 : : */
295 : 28 : if (has_ecc(new_entry) && !strstr(filename, ".ecc"))
296 : 20 : psize = ecc_buffer_size_minus_ecc(psize);
297 : 28 : if (pactual > psize) {
298 : 0 : fprintf(stderr, "File '%s' for partition '%s' is too large,"
299 : : " %u > %u\n",
300 : : filename, name, pactual, psize);
301 : 0 : close(data_fd);
302 : 0 : return -1;
303 : : }
304 : :
305 : 56 : for (data_len = 0; data_len < pactual; data_len += rc) {
306 : 28 : rc = read(data_fd, &data_ptr[data_len], pactual - data_len);
307 : 28 : if (rc == -1) {
308 : 0 : fprintf(stderr, "error reading from '%s'", filename);
309 : 0 : exit(1);
310 : : }
311 : : }
312 : :
313 : 28 : rc = blocklevel_write(bl, pbase, data_ptr, pactual);
314 : 28 : if (rc) {
315 : 0 : fprintf(stderr, "Couldn't write file '%s' for '%s' partition to PNOR "
316 : : "(%m)\n", filename, name);
317 : 0 : exit(1);
318 : : }
319 : :
320 : 28 : free(data_ptr);
321 : 28 : close(data_fd);
322 : : } else {
323 : 0 : if (!allow_empty) {
324 : 0 : fprintf(stderr, "Filename missing for partition %s!\n",
325 : : name);
326 : 0 : return -1;
327 : : }
328 : 0 : if (has_ecc(new_entry)) {
329 : 0 : i = pbase + 8;
330 : 0 : while (i < pbase + psize) {
331 : 0 : rc = blocklevel_write(bl, i, &ecc, sizeof(ecc));
332 : 0 : if (rc) {
333 : 0 : fprintf(stderr, "\nError setting ECC byte at 0x%08x\n",
334 : : i);
335 : 0 : return rc;
336 : : }
337 : 0 : i += 9;
338 : : }
339 : : }
340 : :
341 : : }
342 : :
343 : : return 0;
344 : : }
345 : :
346 : 0 : static void print_version(void)
347 : : {
348 : 0 : printf("Open-Power FFS format tool %s\n", version);
349 : : }
350 : :
351 : 0 : static void print_help(const char *pname)
352 : : {
353 : 0 : print_version();
354 : 0 : printf("Usage: %s [options] -e -s size -c num -i layout_file -p pnor_file ...\n\n", pname);
355 : 0 : printf(" Options:\n");
356 : 0 : printf("\t-e, --allow_empty\n");
357 : 0 : printf("\t\tCreate partition as blank if not specified (sets ECC if flag set)\n\n");
358 : 0 : printf("\t-s, --block_size=size\n");
359 : 0 : printf("\t\tSize (in hex with leading 0x) of the blocks on the flash in bytes\n\n");
360 : 0 : printf("\t-c, --block_count=num\n");
361 : 0 : printf("\t\tNumber of blocks on the flash\n\n");
362 : 0 : printf("\t-i, --input=file\n");
363 : 0 : printf("\t\tFile containing the required partition data\n\n");
364 : 0 : printf("\t-p, --pnor=file\n");
365 : 0 : printf("\t\tOutput file to write data\n\n");
366 : 0 : }
367 : :
368 : 6 : int main(int argc, char *argv[])
369 : : {
370 : 6 : static char line[MAX_LINE];
371 : :
372 : 6 : char *pnor = NULL, *input = NULL;
373 : 6 : bool toc_created = false, bad_input = false, allow_empty = false;
374 : 6 : uint32_t block_size = 0, block_count = 0;
375 : 6 : struct ffs_hdr *tocs[MAX_TOCS] = { 0 };
376 : 6 : struct blocklevel_device *bl = NULL;
377 : 6 : const char *pname = argv[0];
378 : 30 : int line_number, rc, i;
379 : 30 : FILE *in_file;
380 : :
381 : 54 : while(1) {
382 : 30 : struct option long_opts[] = {
383 : : {"allow_empty", no_argument, NULL, 'e'},
384 : : {"block_count", required_argument, NULL, 'c'},
385 : : {"block_size", required_argument, NULL, 's'},
386 : : {"debug", no_argument, NULL, 'g'},
387 : : {"input", required_argument, NULL, 'i'},
388 : : {"pnor", required_argument, NULL, 'p'},
389 : : {NULL, 0, 0, 0}
390 : : };
391 : 30 : int c, oidx = 0;
392 : :
393 : 30 : c = getopt_long(argc, argv, "+:ec:gi:p:s:", long_opts, &oidx);
394 : 30 : if (c == EOF)
395 : : break;
396 : 24 : switch(c) {
397 : : case 'e':
398 : : allow_empty = true;
399 : : break;
400 : 6 : case 'c':
401 : 6 : block_count = strtoul(optarg, NULL, 0);
402 : 6 : break;
403 : 0 : case 'g':
404 : 0 : libflash_debug = true;
405 : 0 : break;
406 : 6 : case 'i':
407 : 6 : free(input);
408 : 6 : input = strdup(optarg);
409 : 6 : if (!input)
410 : 0 : fprintf(stderr, "Out of memory!\n");
411 : : break;
412 : 6 : case 'p':
413 : 6 : free(pnor);
414 : 6 : pnor = strdup(optarg);
415 : 6 : if (!pnor)
416 : 24 : fprintf(stderr, "Out of memory!\n");
417 : : break;
418 : 6 : case 's':
419 : 6 : block_size = strtoul(optarg, NULL, 0);
420 : 6 : break;
421 : 0 : case ':':
422 : 0 : fprintf(stderr, "Unrecognised option \"%s\" to '%c'\n",
423 : : optarg, optopt);
424 : 0 : bad_input = true;
425 : 0 : break;
426 : 0 : case '?':
427 : 0 : fprintf(stderr, "Unrecognised option '%c'\n", optopt);
428 : 0 : bad_input = true;
429 : 0 : break;
430 : 0 : default:
431 : 0 : fprintf(stderr , "Encountered unknown error parsing options\n");
432 : 0 : bad_input = true;
433 : : }
434 : : }
435 : :
436 : 6 : if (bad_input || !block_size || !block_count || !input || !pnor) {
437 : 0 : print_help(pname);
438 : 0 : return 1;
439 : : }
440 : :
441 : 6 : in_file = fopen(input, "r");
442 : 6 : if (!in_file) {
443 : 0 : fprintf(stderr, "Couldn't open your input file %s: %m\n", input);
444 : 0 : return 2;
445 : : }
446 : :
447 : : /*
448 : : * TODO: This won't create the file.
449 : : * We should do this
450 : : */
451 : 6 : rc = arch_flash_init(&bl, pnor, true);
452 : 6 : if (rc) {
453 : 0 : fprintf(stderr, "Couldn't initialise architecture flash structures\n");
454 : 0 : fclose(in_file);
455 : 0 : return 3;
456 : : }
457 : :
458 : : /*
459 : : * 'Erase' the file, make it all 0xFF
460 : : * TODO: Add sparse option and don't do this.
461 : : */
462 : 6 : rc = blocklevel_erase(bl, 0, block_size * block_count);
463 : 6 : if (rc) {
464 : 0 : fprintf(stderr, "Couldn't erase '%s' pnor file\n", pnor);
465 : 0 : fclose(in_file);
466 : 0 : return 4;
467 : : }
468 : :
469 : : line_number = 0;
470 : 34 : while (fgets(line, MAX_LINE, in_file) != NULL) {
471 : 28 : line_number++;
472 : :
473 : : /* Inline comments in input file */
474 : 28 : if (line[0] == '#')
475 : 0 : continue;
476 : :
477 : 28 : if (line[strlen(line) - 1] == '\n')
478 : 28 : line[strlen(line) - 1] = '\0';
479 : :
480 : 28 : if (line[0] == '@') {
481 : 0 : int toc_num = line[1];
482 : 0 : rc = 5;
483 : :
484 : 0 : if (!isdigit(toc_num)) {
485 : 0 : fprintf(stderr, "Invalid TOC ID %d (%c)\n",
486 : : toc_num, toc_num);
487 : 0 : goto parse_out;
488 : : }
489 : :
490 : 0 : toc_num -= '0';
491 : :
492 : 0 : if (line[2] != SEPARATOR) {
493 : 0 : fprintf(stderr, "TOC ID too long\n");
494 : 0 : goto parse_out;
495 : : }
496 : :
497 : 0 : if (tocs[toc_num]) {
498 : 0 : fprintf(stderr, "Duplicate TOC ID %d\n", toc_num);
499 : 0 : goto parse_out;
500 : : }
501 : :
502 : 0 : tocs[toc_num] = parse_toc(&line[3], block_size, block_count);
503 : 0 : if (!tocs[toc_num])
504 : 0 : goto parse_out;
505 : : toc_created = true;
506 : : } else {
507 : 28 : if (!toc_created) {
508 : 6 : fprintf(stderr, "WARNING: Attempting to parse a partition line without any TOCs created.\n");
509 : 6 : fprintf(stderr, " Generating a default TOC at zero\n");
510 : 6 : rc = ffs_hdr_new(block_size, block_count, NULL, &tocs[0]);
511 : 6 : if (rc) {
512 : 0 : rc = 7;
513 : 0 : fprintf(stderr, "Couldn't generate a default TOC at zero\n");
514 : 0 : goto parse_out;
515 : : }
516 : : toc_created = true;
517 : : }
518 : 28 : rc = parse_entry(bl, tocs, line, allow_empty);
519 : 28 : if (rc) {
520 : 0 : rc = 6;
521 : 0 : goto parse_out;
522 : : }
523 : : }
524 : : }
525 : :
526 : 66 : for(i = 0; i < MAX_TOCS; i++) {
527 : 60 : if (tocs[i]) {
528 : 6 : rc = ffs_hdr_finalise(bl, tocs[i]);
529 : 6 : if (rc) {
530 : 0 : rc = 7;
531 : 0 : fprintf(stderr, "Failed to write out TOC values\n");
532 : : break;
533 : : }
534 : : }
535 : : }
536 : :
537 : 6 : parse_out:
538 : 6 : if (rc == 5 || rc == 6)
539 : 0 : fprintf(stderr, "Failed to parse input file '%s' at line %d\n",
540 : : input, line_number);
541 : 6 : arch_flash_close(bl, pnor);
542 : 6 : fclose(in_file);
543 : 72 : for(i = 0; i < MAX_TOCS; i++)
544 : 60 : ffs_hdr_free(tocs[i]);
545 : 6 : free(input);
546 : 6 : free(pnor);
547 : 6 : return rc;
548 : : }
|