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