xref: /aosp_15_r20/external/coreboot/util/cbfstool/fmaptool.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
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 
usage(const char * invoked_as)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 
list_cbfs_section_names(FILE * out)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 
write_header_fmap_terminal_section_names(FILE * header,const struct flashmap_descriptor * root)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 
write_header_fmap_sections(FILE * header,const struct flashmap_descriptor * root,unsigned int offset)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 
write_header(const char * out_fname,const struct flashmap_descriptor * root,const int fmap_size)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 
full_fmd_cleanup(struct flashmap_descriptor ** victim)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 
main(int argc,char ** argv)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 }
297