1321936Shselasky/*-
2321936Shselasky * Copyright (c) 2013 David Chisnall
3321936Shselasky * All rights reserved.
4321936Shselasky *
5321936Shselasky * This software was developed by SRI International and the University of
6321936Shselasky * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
7321936Shselasky * ("CTSRD"), as part of the DARPA CRASH research programme.
8321936Shselasky *
9321936Shselasky * Redistribution and use in source and binary forms, with or without
10321936Shselasky * modification, are permitted provided that the following conditions
11321936Shselasky * are met:
12321936Shselasky * 1. Redistributions of source code must retain the above copyright
13321936Shselasky *    notice, this list of conditions and the following disclaimer.
14321936Shselasky * 2. Redistributions in binary form must reproduce the above copyright
15321936Shselasky *    notice, this list of conditions and the following disclaimer in the
16321936Shselasky *    documentation and/or other materials provided with the distribution.
17321936Shselasky *
18321936Shselasky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19321936Shselasky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20321936Shselasky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21321936Shselasky * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22321936Shselasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23321936Shselasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24321936Shselasky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25321936Shselasky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26321936Shselasky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27321936Shselasky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28321936Shselasky * SUCH DAMAGE.
29321936Shselasky *
30321936Shselasky * $FreeBSD$
31321936Shselasky */
32321936Shselasky
33321936Shselasky#include <sys/resource.h>
34321936Shselasky#include <fcntl.h>
35321936Shselasky#include <libgen.h>
36321936Shselasky#include <limits.h>
37321936Shselasky#include <stdio.h>
38321936Shselasky#include <stdlib.h>
39321936Shselasky#include <time.h>
40321936Shselasky#include <unistd.h>
41321936Shselasky
42321936Shselasky
43321936Shselasky#include "fdt.hh"
44321936Shselasky#include "checking.hh"
45321936Shselasky
46321936Shselaskyusing namespace dtc;
47321936Shselasky
48321936Shselasky/**
49321936Shselasky * The current major version of the tool.
50321936Shselasky */
51321936Shselaskyint version_major = 0;
52321936Shselasky/**
53321936Shselasky * The current minor version of the tool.
54321936Shselasky */
55321936Shselaskyint version_minor = 4;
56321936Shselasky/**
57321936Shselasky * The current patch level of the tool.
58321936Shselasky */
59321936Shselaskyint version_patch = 0;
60321936Shselasky
61321936Shselaskystatic void usage(const char* argv0)
62321936Shselasky{
63321936Shselasky	fprintf(stderr, "Usage:\n"
64321936Shselasky		"\t%s\t[-fhsv] [-b boot_cpu_id] [-d dependency_file]"
65321936Shselasky			"[-E [no-]checker_name]\n"
66321936Shselasky		"\t\t[-H phandle_format] [-I input_format]"
67321936Shselasky			"[-O output_format]\n"
68321936Shselasky		"\t\t[-o output_file] [-R entries] [-S bytes] [-p bytes]"
69321936Shselasky			"[-V blob_version]\n"
70321936Shselasky		"\t\t-W [no-]checker_name] input_file\n", basename(argv0));
71321936Shselasky}
72321936Shselasky
73321936Shselasky/**
74321936Shselasky * Prints the current version of this program..
75321936Shselasky */
76321936Shselaskystatic void version(const char* progname)
77321936Shselasky{
78321936Shselasky	fprintf(stderr, "Version: %s %d.%d.%d\n", progname, version_major,
79321936Shselasky			version_minor, version_patch);
80321936Shselasky}
81321936Shselasky
82321936Shselaskyusing fdt::device_tree;
83321936Shselasky
84321936Shselaskyint
85321936Shselaskymain(int argc, char **argv)
86321936Shselasky{
87321936Shselasky	int ch;
88321936Shselasky	int outfile = fileno(stdout);
89321936Shselasky	const char *outfile_name = "-";
90321936Shselasky	const char *in_file = "-";
91321936Shselasky	FILE *depfile = 0;
92	bool debug_mode = false;
93	void (device_tree::*write_fn)(int) = &device_tree::write_binary;
94	void (device_tree::*read_fn)(const char*, FILE*) =
95		&device_tree::parse_dts;
96	uint32_t boot_cpu;
97	bool boot_cpu_specified = false;
98	bool keep_going = false;
99	bool sort = false;
100	clock_t c0 = clock();
101	class device_tree tree;
102	fdt::checking::check_manager checks;
103	const char *options = "hqI:O:o:V:d:R:S:p:b:fisvH:W:E:DP:";
104
105	// Don't forget to update the man page if any more options are added.
106	while ((ch = getopt(argc, argv, options)) != -1)
107	{
108		switch (ch)
109		{
110		case 'h':
111			usage(argv[0]);
112			return EXIT_SUCCESS;
113		case 'v':
114			version(argv[0]);
115			return EXIT_SUCCESS;
116		case 'I':
117		{
118			string arg = string(optarg);
119			if (arg == string("dtb"))
120			{
121				read_fn = &device_tree::parse_dtb;
122			}
123			else if (arg == string("dts"))
124			{
125				read_fn = &device_tree::parse_dts;
126			}
127			else
128			{
129				fprintf(stderr, "Unknown input format: %s\n", optarg);
130				return EXIT_FAILURE;
131			}
132			break;
133		}
134		case 'O':
135		{
136			string arg = string(optarg);
137			if (arg == string("dtb"))
138			{
139				write_fn = &device_tree::write_binary;
140			}
141			else if (arg == string("asm"))
142			{
143				write_fn = &device_tree::write_asm;
144			}
145			else if (arg == string("dts"))
146			{
147				write_fn = &device_tree::write_dts;
148			}
149			else
150			{
151				fprintf(stderr, "Unknown output format: %s\n", optarg);
152				return EXIT_FAILURE;
153			}
154			break;
155		}
156		case 'o':
157		{
158			outfile_name = optarg;
159			outfile = open(optarg, O_CREAT | O_TRUNC | O_WRONLY, 0666);
160			if (outfile == -1)
161			{
162				perror("Unable to open output file");
163				return EXIT_FAILURE;
164			}
165			break;
166		}
167		case 'D':
168			debug_mode = true;
169			break;
170		case 'V':
171			if (string(optarg) != string("17"))
172			{
173				fprintf(stderr, "Unknown output format version: %s\n", optarg);
174				return EXIT_FAILURE;
175			}
176			break;
177		case 'd':
178		{
179			if (depfile != 0)
180			{
181				fclose(depfile);
182			}
183			if (string(optarg) == string("-"))
184			{
185				depfile = stdout;
186			}
187			else
188			{
189				depfile = fdopen(open(optarg, O_CREAT | O_TRUNC | O_WRONLY, 0666), "w");
190				if (depfile == 0)
191				{
192					perror("Unable to open dependency file");
193					return EXIT_FAILURE;
194				}
195			}
196			break;
197		}
198		case 'H':
199		{
200			string arg = string(optarg);
201			if (arg == string("both"))
202			{
203				tree.set_phandle_format(device_tree::BOTH);
204			}
205			else if (arg == string("epapr"))
206			{
207				tree.set_phandle_format(device_tree::EPAPR);
208			}
209			else if (arg == string("linux"))
210			{
211				tree.set_phandle_format(device_tree::LINUX);
212			}
213			else
214			{
215				fprintf(stderr, "Unknown phandle format: %s\n", optarg);
216				return EXIT_FAILURE;
217			}
218			break;
219		}
220		case 'b':
221			// Don't bother to check if strtoll fails, just
222			// use the 0 it returns.
223			boot_cpu = (uint32_t)strtoll(optarg, 0, 10);
224			boot_cpu_specified = true;
225			break;
226		case 'f':
227			keep_going = true;
228			break;
229		case 'W':
230		case 'E':
231		{
232			string arg = string(optarg);
233			if ((arg.size() > 3) && (strncmp(optarg, "no-", 3) == 0))
234			{
235				arg = string(optarg+3);
236				if (!checks.disable_checker(arg))
237				{
238					fprintf(stderr, "Checker %s either does not exist or is already disabled\n", optarg+3);
239				}
240				break;
241			}
242			if (!checks.enable_checker(arg))
243			{
244				fprintf(stderr, "Checker %s either does not exist or is already enabled\n", optarg);
245			}
246			break;
247		}
248		case 's':
249		{
250			sort = true;
251			break;
252		}
253		case 'i':
254		{
255			tree.add_include_path(optarg);
256			break;
257		}
258		// Should quiet warnings, but for now is silently ignored.
259		case 'q':
260			break;
261		case 'R':
262			tree.set_empty_reserve_map_entries(strtoll(optarg, 0, 10));
263			break;
264		case 'S':
265			tree.set_blob_minimum_size(strtoll(optarg, 0, 10));
266			break;
267		case 'p':
268			tree.set_blob_padding(strtoll(optarg, 0, 10));
269			break;
270		case 'P':
271			if (!tree.parse_define(optarg))
272			{
273				fprintf(stderr, "Invalid predefine value %s\n",
274				        optarg);
275			}
276			break;
277		default:
278			fprintf(stderr, "Unknown option %c\n", ch);
279			return EXIT_FAILURE;
280		}
281	}
282	if (optind < argc)
283	{
284		in_file = argv[optind];
285	}
286	if (depfile != 0)
287	{
288		fputs(outfile_name, depfile);
289		fputs(": ", depfile);
290		fputs(in_file, depfile);
291	}
292	clock_t c1 = clock();
293	(tree.*read_fn)(in_file, depfile);
294	// Override the boot CPU found in the header, if we're loading from dtb
295	if (boot_cpu_specified)
296	{
297		tree.set_boot_cpu(boot_cpu);
298	}
299	if (sort)
300	{
301		tree.sort();
302	}
303	if (depfile != 0)
304	{
305		putc('\n', depfile);
306		fclose(depfile);
307	}
308	if (!(tree.is_valid() || keep_going))
309	{
310		fprintf(stderr, "Failed to parse tree.  Unhappy face!\n");
311		return EXIT_FAILURE;
312	}
313	clock_t c2 = clock();
314	if (!(checks.run_checks(&tree, true) || keep_going))
315	{
316		return EXIT_FAILURE;
317	}
318	clock_t c3 = clock();
319	(tree.*write_fn)(outfile);
320	close(outfile);
321	clock_t c4 = clock();
322
323	if (debug_mode)
324	{
325		struct rusage r;
326
327		getrusage(RUSAGE_SELF, &r);
328		fprintf(stderr, "Peak memory usage: %ld bytes\n", r.ru_maxrss);
329		fprintf(stderr, "Setup and option parsing took %f seconds\n",
330				((double)(c1-c0))/CLOCKS_PER_SEC);
331		fprintf(stderr, "Parsing took %f seconds\n",
332				((double)(c2-c1))/CLOCKS_PER_SEC);
333		fprintf(stderr, "Checking took %f seconds\n",
334				((double)(c3-c2))/CLOCKS_PER_SEC);
335		fprintf(stderr, "Generating output took %f seconds\n",
336				((double)(c4-c3))/CLOCKS_PER_SEC);
337		fprintf(stderr, "Total time: %f seconds\n",
338				((double)(c4-c0))/CLOCKS_PER_SEC);
339		// This is not needed, but keeps valgrind quiet.
340		fclose(stdin);
341	}
342	return EXIT_SUCCESS;
343}
344
345