/ util / cbfstool / fmaptool.c
fmaptool.c
  1  /* fmaptool, CLI utility for converting plaintext fmd files into fmap blobs */
  2  /* SPDX-License-Identifier: GPL-2.0-only */
  3  
  4  #include "common.h"
  5  #include "cbfs_sections.h"
  6  #include "fmap_from_fmd.h"
  7  
  8  #include <stdio.h>
  9  #include <string.h>
 10  #include <unistd.h>
 11  
 12  #define STDIN_FILENAME_SENTINEL "-"
 13  
 14  #define HEADER_FMAP_OFFSET "FMAP_OFFSET"
 15  #define HEADER_FMAP_SIZE "FMAP_SIZE"
 16  
 17  /*
 18   * Macro name used in the generated C header file to provide list of section names that do not
 19   * have any sub-sections.
 20   */
 21  #define HEADER_FMAP_TERMINAL_SECTIONS "FMAP_TERMINAL_SECTIONS"
 22  
 23  enum fmaptool_return {
 24  	FMAPTOOL_EXIT_SUCCESS = 0,
 25  	FMAPTOOL_EXIT_BAD_ARGS,
 26  	FMAPTOOL_EXIT_BAD_INPUT_PATH,
 27  	FMAPTOOL_EXIT_BAD_OUTPUT_PATH,
 28  	FMAPTOOL_EXIT_FAILED_DESCRIPTOR,
 29  	FMAPTOOL_EXIT_MISSING_FMAP_SECTION,
 30  	FMAPTOOL_EXIT_MISSING_PRIMARY_CBFS,
 31  	FMAPTOOL_EXIT_FAILED_FMAP_CONVERSION,
 32  	FMAPTOOL_EXIT_UNKNOWN_FMAP_SIZE,
 33  	FMAPTOOL_EXIT_FAILED_WRITING_OUTPUT,
 34  	FMAPTOOL_EXIT_FAILED_WRITING_HEADER,
 35  };
 36  
 37  static void usage(const char *invoked_as)
 38  {
 39  	fputs("fmaptool: Compiler for fmd (flashmap descriptor) files\n",
 40  									stderr);
 41  	fputs("\nUSAGE:\n", stderr);
 42  	fprintf(stderr,
 43  		"\t%s [-h <header output file>] [-R <region output file>] <fmd input file> <binary output file>\n",
 44  								invoked_as);
 45  	fputs("\nMANDATORY ARGUMENTS:\n", stderr);
 46  	fprintf(stderr,
 47  		"<fmd input file> may be '%s' to read from standard input\n",
 48  						STDIN_FILENAME_SENTINEL);
 49  	fputs("<binary output file> must be a regular file\n", stderr);
 50  	fputs("\nOPTIONAL SWITCHES:\n", stderr);
 51  	fprintf(stderr,
 52  		"-h\tAlso produce a C header defining %s to the FMAP section's flash offset.\n",
 53  							HEADER_FMAP_OFFSET);
 54  	fprintf(stderr,
 55  		"-R\tAlso produce a text file listing the CBFS regions, comma separated.\n");
 56  	fputs("\nOUTPUT:\n", stderr);
 57  	fputs("A successful invocation prints a summary of work done to standard error, and a comma-separated list\n",
 58  									stderr);
 59  	fputs("of those sections that contain CBFSes, starting with the primary such section, to standard output.\n",
 60  									stderr);
 61  }
 62  
 63  static void list_cbfs_section_names(FILE *out)
 64  {
 65  	cbfs_section_iterator_t cbfs_it = cbfs_sections_iterator();
 66  	assert(cbfs_it);
 67  
 68  	bool subsequent = false;
 69  	while (cbfs_it) {
 70  		const char *cur_name =
 71  				cbfs_sections_iterator_deref(cbfs_it)->name;
 72  		if (cbfs_sections_iterator_advance(&cbfs_it) && subsequent)
 73  			fputc(',', out);
 74  		fputs(cur_name, out);
 75  		subsequent = true;
 76  	}
 77  	fputc('\n', out);
 78  }
 79  
 80  static void write_header_fmap_terminal_section_names(FILE *header,
 81  						     const struct flashmap_descriptor *root)
 82  {
 83  	assert(root);
 84  
 85  	if (root->list_len == 0) {
 86  		fprintf(header, "%s ", root->name);
 87  		return;
 88  	}
 89  
 90  	fmd_foreach_child(child, root)
 91  		write_header_fmap_terminal_section_names(header, child);
 92  }
 93  
 94  static void write_header_fmap_sections(FILE *header, const struct flashmap_descriptor *root,
 95  				       unsigned int offset)
 96  {
 97  	assert(root);
 98  	/*
 99  	 * The offset may only be unknown for the root node in a system where the flash isn't
100  	 * memory-mapped.
101  	 */
102  	if (!root->offset_known && offset != 0)
103  		return;
104  
105  	const unsigned int current_offset = offset + (root->offset_known ? root->offset : 0);
106  	fprintf(header, "#define FMAP_SECTION_%s_START %#x\n", root->name, current_offset);
107  
108  	if (!root->size_known)
109  		return;
110  
111  	fprintf(header, "#define FMAP_SECTION_%s_SIZE %#x\n", root->name, root->size);
112  
113  	fmd_foreach_child(child, root)
114  		write_header_fmap_sections(header, child, current_offset);
115  }
116  
117  static bool write_header(const char *out_fname,
118  			 const struct flashmap_descriptor *root,
119  			 const int fmap_size)
120  {
121  	assert(out_fname);
122  
123  	FILE *header = fopen(out_fname, "w");
124  	if (!header) {
125  		fprintf(stderr, "FATAL: Unable to open file '%s' for writing\n",
126  				out_fname);
127  		return false;
128  	}
129  
130  	unsigned fmap_offset =
131  			fmd_calc_absolute_offset(root, SECTION_NAME_FMAP);
132  	assert(fmap_offset != FMD_NOTFOUND);
133  
134  	fputs("#ifndef FMAPTOOL_GENERATED_HEADER_H_\n", header);
135  	fputs("#define FMAPTOOL_GENERATED_HEADER_H_\n\n", header);
136  	fprintf(header, "#define %s %#x\n", HEADER_FMAP_OFFSET, fmap_offset);
137  	fprintf(header, "#define %s %#x\n\n", HEADER_FMAP_SIZE, fmap_size);
138  
139  	fprintf(header, "#define %s \"", HEADER_FMAP_TERMINAL_SECTIONS);
140  	write_header_fmap_terminal_section_names(header, root);
141  	fprintf(header, "\"\n\n");
142  
143  	write_header_fmap_sections(header, root, 0);
144  	fputs("\n", header);
145  
146  	fputs("#endif\n", header);
147  
148  	fclose(header);
149  	return true;
150  }
151  
152  static void full_fmd_cleanup(struct flashmap_descriptor **victim)
153  {
154  	assert(victim);
155  
156  	cbfs_sections_cleanup();
157  	fmd_cleanup(*victim);
158  	*victim = NULL;
159  }
160  
161  int main(int argc, char **argv)
162  {
163  	struct {
164  		// Mandatory
165  		const char *fmd_filename;
166  		const char *fmap_filename;
167  
168  		// Optional
169  		const char *header_filename;
170  		const char *region_filename;
171  	} args = {NULL, NULL, NULL, NULL};
172  
173  	bool show_usage = false;
174  	int each_arg;
175  	while (!show_usage && (each_arg = getopt(argc, argv, ":h:R:")) != -1) {
176  		switch (each_arg) {
177  		case 'h':
178  			args.header_filename = optarg;
179  			break;
180  		case 'R':
181  			args.region_filename = optarg;
182  			break;
183  		case ':':
184  			fprintf(stderr, "-%c: Expected an accompanying value\n",
185  									optopt);
186  			show_usage = true;
187  			break;
188  		default:
189  			fprintf(stderr, "-%c: Unexpected command-line switch\n",
190  									optopt);
191  			show_usage = true;
192  		}
193  	}
194  
195  	if (show_usage || argc - optind != 2) {
196  		usage(argv[0]);
197  		return FMAPTOOL_EXIT_BAD_ARGS;
198  	}
199  	args.fmd_filename = argv[optind];
200  	args.fmap_filename = argv[optind + 1];
201  
202  	FILE *fmd_file = stdin;
203  	if (strcmp(args.fmd_filename, STDIN_FILENAME_SENTINEL) != 0) {
204  		fmd_file = fopen(args.fmd_filename, "r");
205  		if (!fmd_file) {
206  			fprintf(stderr, "FATAL: Unable to open file '%s'\n",
207  							args.fmd_filename);
208  			return FMAPTOOL_EXIT_BAD_INPUT_PATH;
209  		}
210  	}
211  
212  	struct flashmap_descriptor *descriptor = fmd_create(fmd_file);
213  	fclose(fmd_file);
214  	if (!descriptor) {
215  		fputs("FATAL: Failed while processing provided descriptor\n",
216  									stderr);
217  		full_fmd_cleanup(&descriptor);
218  		return FMAPTOOL_EXIT_FAILED_DESCRIPTOR;
219  	}
220  
221  	if (!fmd_find_node(descriptor, SECTION_NAME_FMAP)) {
222  		fprintf(stderr,
223  			"FATAL: Flashmap descriptor must have an '%s' section\n",
224  							SECTION_NAME_FMAP);
225  		full_fmd_cleanup(&descriptor);
226  		return FMAPTOOL_EXIT_MISSING_FMAP_SECTION;
227  	}
228  
229  	if (!cbfs_sections_primary_cbfs_accounted_for()) {
230  		fprintf(stderr,
231  			"FATAL: Flashmap descriptor must have a '%s' section that is annotated with '(%s)'\n",
232  						SECTION_NAME_PRIMARY_CBFS,
233  						SECTION_ANNOTATION_CBFS);
234  		full_fmd_cleanup(&descriptor);
235  		return FMAPTOOL_EXIT_MISSING_PRIMARY_CBFS;
236  	}
237  
238  	struct fmap *flashmap = fmap_from_fmd(descriptor);
239  	if (!flashmap) {
240  		fputs("FATAL: Failed while constructing FMAP section\n",
241  									stderr);
242  		full_fmd_cleanup(&descriptor);
243  		return FMAPTOOL_EXIT_FAILED_FMAP_CONVERSION;
244  	}
245  
246  	int size = fmap_size(flashmap);
247  	if (size < 0) {
248  		fputs("FATAL: Failed to determine FMAP section size\n",
249  									stderr);
250  		fmap_destroy(flashmap);
251  		full_fmd_cleanup(&descriptor);
252  		return FMAPTOOL_EXIT_UNKNOWN_FMAP_SIZE;
253  	}
254  
255  	FILE *fmap_file = fopen(args.fmap_filename, "wb");
256  	if (!fmap_file) {
257  		fprintf(stderr, "FATAL: Unable to open file '%s' for writing\n",
258  							args.fmap_filename);
259  		fmap_destroy(flashmap);
260  		full_fmd_cleanup(&descriptor);
261  		return FMAPTOOL_EXIT_BAD_OUTPUT_PATH;
262  	}
263  
264  	if (!fwrite(flashmap, size, 1, fmap_file)) {
265  		fputs("FATAL: Failed to write final FMAP to file\n", stderr);
266  		fclose(fmap_file);
267  		fmap_destroy(flashmap);
268  		full_fmd_cleanup(&descriptor);
269  		return FMAPTOOL_EXIT_FAILED_WRITING_OUTPUT;
270  	}
271  	fclose(fmap_file);
272  	fmap_destroy(flashmap);
273  
274  	if (args.header_filename &&
275  			!write_header(args.header_filename, descriptor, size)) {
276  		full_fmd_cleanup(&descriptor);
277  		return FMAPTOOL_EXIT_FAILED_WRITING_HEADER;
278  	}
279  
280  	fprintf(stderr, "SUCCESS: Wrote %d bytes to file '%s'%s\n", size,
281  							args.fmap_filename,
282  			args.header_filename ? " (and generated header)" : "");
283  	fputs("The sections containing CBFSes are: ", stderr);
284  	list_cbfs_section_names(stdout);
285  	if (args.region_filename) {
286  		FILE *region_file = fopen(args.region_filename, "w");
287  		if (region_file == NULL)
288  			return FMAPTOOL_EXIT_FAILED_WRITING_OUTPUT;
289  
290  		list_cbfs_section_names(region_file);
291  		fclose(region_file);
292  	}
293  
294  	full_fmd_cleanup(&descriptor);
295  	return FMAPTOOL_EXIT_SUCCESS;
296  }