1245803Stheraven/*-
2245803Stheraven * Copyright (c) 2013 David Chisnall
3245803Stheraven * All rights reserved.
4245803Stheraven *
5245803Stheraven * This software was developed by SRI International and the University of
6245803Stheraven * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
7245803Stheraven * ("CTSRD"), as part of the DARPA CRASH research programme.
8245803Stheraven *
9245803Stheraven * Redistribution and use in source and binary forms, with or without
10245803Stheraven * modification, are permitted provided that the following conditions
11245803Stheraven * are met:
12245803Stheraven * 1. Redistributions of source code must retain the above copyright
13245803Stheraven *    notice, this list of conditions and the following disclaimer.
14245803Stheraven * 2. Redistributions in binary form must reproduce the above copyright
15245803Stheraven *    notice, this list of conditions and the following disclaimer in the
16245803Stheraven *    documentation and/or other materials provided with the distribution.
17245803Stheraven *
18245803Stheraven * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19245803Stheraven * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20245803Stheraven * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21245803Stheraven * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22245803Stheraven * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23245803Stheraven * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24245803Stheraven * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25245803Stheraven * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26245803Stheraven * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27245803Stheraven * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28245803Stheraven * SUCH DAMAGE.
29245803Stheraven *
30245803Stheraven * $FreeBSD$
31245803Stheraven */
32245803Stheraven
33245803Stheraven#include <sys/resource.h>
34245803Stheraven#include <fcntl.h>
35245803Stheraven#include <libgen.h>
36245839Stheraven#include <limits.h>
37245839Stheraven#include <stdio.h>
38245839Stheraven#include <stdlib.h>
39245839Stheraven#include <time.h>
40245839Stheraven#include <unistd.h>
41245803Stheraven
42245839Stheraven
43245803Stheraven#include "fdt.hh"
44245803Stheraven#include "checking.hh"
45245803Stheraven
46245803Stheravenusing namespace dtc;
47245803Stheraven
48245803Stheraven/**
49245803Stheraven * The current major version of the tool.
50245803Stheraven */
51245803Stheravenint version_major = 0;
52245803Stheraven/**
53245803Stheraven * The current minor version of the tool.
54245803Stheraven */
55245803Stheravenint version_minor = 4;
56245803Stheraven/**
57245803Stheraven * The current patch level of the tool.
58245803Stheraven */
59245803Stheravenint version_patch = 0;
60245803Stheraven
61245803Stheravenstatic void usage(const char* argv0)
62245803Stheraven{
63245803Stheraven	fprintf(stderr, "Usage:\n"
64245803Stheraven		"\t%s\t[-fhsv] [-b boot_cpu_id] [-d dependency_file]"
65245803Stheraven			"[-E [no-]checker_name]\n"
66245803Stheraven		"\t\t[-H phandle_format] [-I input_format]"
67245803Stheraven			"[-O output_format]\n"
68245803Stheraven		"\t\t[-o output_file] [-R entries] [-S bytes] [-p bytes]"
69245803Stheraven			"[-V blob_version]\n"
70245803Stheraven		"\t\t-W [no-]checker_name] input_file\n", basename(argv0));
71245803Stheraven}
72245803Stheraven
73245803Stheraven/**
74245803Stheraven * Prints the current version of this program..
75245803Stheraven */
76245803Stheravenstatic void version(const char* progname)
77245803Stheraven{
78245803Stheraven	fprintf(stderr, "Version: %s %d.%d.%d\n", progname, version_major,
79245803Stheraven			version_minor, version_patch);
80245803Stheraven}
81245803Stheraven
82245803Stheravenusing fdt::device_tree;
83245803Stheraven
84245803Stheravenint
85245803Stheravenmain(int argc, char **argv)
86245803Stheraven{
87245803Stheraven	int ch;
88245803Stheraven	int outfile = fileno(stdout);
89245803Stheraven	const char *outfile_name = "-";
90245803Stheraven	const char *in_file = "-";
91245803Stheraven	FILE *depfile = 0;
92245803Stheraven	bool debug_mode = false;
93245803Stheraven	void (device_tree::*write_fn)(int) = &device_tree::write_binary;
94245803Stheraven	void (device_tree::*read_fn)(const char*, FILE*) =
95245803Stheraven		&device_tree::parse_dts;
96245803Stheraven	uint32_t boot_cpu;
97245803Stheraven	bool boot_cpu_specified = false;
98245803Stheraven	bool keep_going = false;
99245803Stheraven	bool sort = false;
100245803Stheraven	clock_t c0 = clock();
101245803Stheraven	class device_tree tree;
102245803Stheraven	fdt::checking::check_manager checks;
103254522Stheraven	const char *options = "hqI:O:o:V:d:R:S:p:b:fisvH:W:E:DP:";
104245803Stheraven
105245803Stheraven	// Don't forget to update the man page if any more options are added.
106245803Stheraven	while ((ch = getopt(argc, argv, options)) != -1)
107245803Stheraven	{
108245803Stheraven		switch (ch)
109245803Stheraven		{
110245803Stheraven		case 'h':
111245803Stheraven			usage(argv[0]);
112245803Stheraven			return EXIT_SUCCESS;
113245803Stheraven		case 'v':
114245803Stheraven			version(argv[0]);
115245803Stheraven			return EXIT_SUCCESS;
116245803Stheraven		case 'I':
117245803Stheraven		{
118245803Stheraven			string arg = string(optarg);
119245803Stheraven			if (arg == string("dtb"))
120245803Stheraven			{
121245803Stheraven				read_fn = &device_tree::parse_dtb;
122245803Stheraven			}
123245803Stheraven			else if (arg == string("dts"))
124245803Stheraven			{
125245803Stheraven				read_fn = &device_tree::parse_dts;
126245803Stheraven			}
127245803Stheraven			else
128245803Stheraven			{
129245803Stheraven				fprintf(stderr, "Unknown input format: %s\n", optarg);
130245803Stheraven				return EXIT_FAILURE;
131245803Stheraven			}
132245803Stheraven			break;
133245803Stheraven		}
134245803Stheraven		case 'O':
135245803Stheraven		{
136245803Stheraven			string arg = string(optarg);
137245803Stheraven			if (arg == string("dtb"))
138245803Stheraven			{
139245803Stheraven				write_fn = &device_tree::write_binary;
140245803Stheraven			}
141245803Stheraven			else if (arg == string("asm"))
142245803Stheraven			{
143245803Stheraven				write_fn = &device_tree::write_asm;
144245803Stheraven			}
145245803Stheraven			else if (arg == string("dts"))
146245803Stheraven			{
147245803Stheraven				write_fn = &device_tree::write_dts;
148245803Stheraven			}
149245803Stheraven			else
150245803Stheraven			{
151245803Stheraven				fprintf(stderr, "Unknown output format: %s\n", optarg);
152245803Stheraven				return EXIT_FAILURE;
153245803Stheraven			}
154245803Stheraven			break;
155245803Stheraven		}
156245803Stheraven		case 'o':
157245803Stheraven		{
158245803Stheraven			outfile_name = optarg;
159245803Stheraven			outfile = open(optarg, O_CREAT | O_TRUNC | O_WRONLY, 0666);
160245803Stheraven			if (outfile == -1)
161245803Stheraven			{
162245803Stheraven				perror("Unable to open output file");
163245803Stheraven				return EXIT_FAILURE;
164245803Stheraven			}
165245803Stheraven			break;
166245803Stheraven		}
167245803Stheraven		case 'D':
168245803Stheraven			debug_mode = true;
169245803Stheraven			break;
170245803Stheraven		case 'V':
171245807Stheraven			if (string(optarg) != string("17"))
172245803Stheraven			{
173245803Stheraven				fprintf(stderr, "Unknown output format version: %s\n", optarg);
174245803Stheraven				return EXIT_FAILURE;
175245803Stheraven			}
176245803Stheraven			break;
177245803Stheraven		case 'd':
178245803Stheraven		{
179245803Stheraven			if (depfile != 0)
180245803Stheraven			{
181245803Stheraven				fclose(depfile);
182245803Stheraven			}
183245803Stheraven			if (string(optarg) == string("-"))
184245803Stheraven			{
185245803Stheraven				depfile = stdout;
186245803Stheraven			}
187245803Stheraven			else
188245803Stheraven			{
189245803Stheraven				depfile = fdopen(open(optarg, O_CREAT | O_TRUNC | O_WRONLY, 0666), "w");
190245803Stheraven				if (depfile == 0)
191245803Stheraven				{
192245803Stheraven					perror("Unable to open dependency file");
193245803Stheraven					return EXIT_FAILURE;
194245803Stheraven				}
195245803Stheraven			}
196245803Stheraven			break;
197245803Stheraven		}
198245803Stheraven		case 'H':
199245803Stheraven		{
200245803Stheraven			string arg = string(optarg);
201245803Stheraven			if (arg == string("both"))
202245803Stheraven			{
203245803Stheraven				tree.set_phandle_format(device_tree::BOTH);
204245803Stheraven			}
205245803Stheraven			else if (arg == string("epapr"))
206245803Stheraven			{
207245803Stheraven				tree.set_phandle_format(device_tree::EPAPR);
208245803Stheraven			}
209245803Stheraven			else if (arg == string("linux"))
210245803Stheraven			{
211245803Stheraven				tree.set_phandle_format(device_tree::LINUX);
212245803Stheraven			}
213245803Stheraven			else
214245803Stheraven			{
215245803Stheraven				fprintf(stderr, "Unknown phandle format: %s\n", optarg);
216245803Stheraven				return EXIT_FAILURE;
217245803Stheraven			}
218245803Stheraven			break;
219245803Stheraven		}
220245803Stheraven		case 'b':
221245803Stheraven			// Don't bother to check if strtoll fails, just
222245803Stheraven			// use the 0 it returns.
223245803Stheraven			boot_cpu = (uint32_t)strtoll(optarg, 0, 10);
224245803Stheraven			boot_cpu_specified = true;
225245803Stheraven			break;
226245803Stheraven		case 'f':
227245803Stheraven			keep_going = true;
228245803Stheraven			break;
229245803Stheraven		case 'W':
230245803Stheraven		case 'E':
231245803Stheraven		{
232245803Stheraven			string arg = string(optarg);
233245803Stheraven			if ((arg.size() > 3) && (strncmp(optarg, "no-", 3) == 0))
234245803Stheraven			{
235245803Stheraven				arg = string(optarg+3);
236245803Stheraven				if (!checks.disable_checker(arg))
237245803Stheraven				{
238245803Stheraven					fprintf(stderr, "Checker %s either does not exist or is already disabled\n", optarg+3);
239245803Stheraven				}
240245803Stheraven				break;
241245803Stheraven			}
242245803Stheraven			if (!checks.enable_checker(arg))
243245803Stheraven			{
244245803Stheraven				fprintf(stderr, "Checker %s either does not exist or is already enabled\n", optarg);
245245803Stheraven			}
246245803Stheraven			break;
247245803Stheraven		}
248245803Stheraven		case 's':
249245803Stheraven		{
250245803Stheraven			sort = true;
251245803Stheraven			break;
252245803Stheraven		}
253245803Stheraven		case 'i':
254245803Stheraven		{
255245803Stheraven			tree.add_include_path(optarg);
256245803Stheraven			break;
257245803Stheraven		}
258245803Stheraven		// Should quiet warnings, but for now is silently ignored.
259245803Stheraven		case 'q':
260245803Stheraven			break;
261245803Stheraven		case 'R':
262245803Stheraven			tree.set_empty_reserve_map_entries(strtoll(optarg, 0, 10));
263245803Stheraven			break;
264245803Stheraven		case 'S':
265245803Stheraven			tree.set_blob_minimum_size(strtoll(optarg, 0, 10));
266245803Stheraven			break;
267245803Stheraven		case 'p':
268245803Stheraven			tree.set_blob_padding(strtoll(optarg, 0, 10));
269245803Stheraven			break;
270254522Stheraven		case 'P':
271254522Stheraven			if (!tree.parse_define(optarg))
272254522Stheraven			{
273254522Stheraven				fprintf(stderr, "Invalid predefine value %s\n",
274254522Stheraven				        optarg);
275254522Stheraven			}
276254522Stheraven			break;
277245803Stheraven		default:
278245803Stheraven			fprintf(stderr, "Unknown option %c\n", ch);
279245803Stheraven			return EXIT_FAILURE;
280245803Stheraven		}
281245803Stheraven	}
282245803Stheraven	if (optind < argc)
283245803Stheraven	{
284245803Stheraven		in_file = argv[optind];
285245803Stheraven	}
286245803Stheraven	if (depfile != 0)
287245803Stheraven	{
288245803Stheraven		fputs(outfile_name, depfile);
289245803Stheraven		fputs(": ", depfile);
290245803Stheraven		fputs(in_file, depfile);
291245803Stheraven	}
292245803Stheraven	clock_t c1 = clock();
293245803Stheraven	(tree.*read_fn)(in_file, depfile);
294245803Stheraven	// Override the boot CPU found in the header, if we're loading from dtb
295245803Stheraven	if (boot_cpu_specified)
296245803Stheraven	{
297245803Stheraven		tree.set_boot_cpu(boot_cpu);
298245803Stheraven	}
299245803Stheraven	if (sort)
300245803Stheraven	{
301245803Stheraven		tree.sort();
302245803Stheraven	}
303245803Stheraven	if (depfile != 0)
304245803Stheraven	{
305245803Stheraven		putc('\n', depfile);
306245803Stheraven		fclose(depfile);
307245803Stheraven	}
308245803Stheraven	if (!(tree.is_valid() || keep_going))
309245803Stheraven	{
310245803Stheraven		fprintf(stderr, "Failed to parse tree.  Unhappy face!\n");
311245803Stheraven		return EXIT_FAILURE;
312245803Stheraven	}
313245803Stheraven	clock_t c2 = clock();
314245803Stheraven	if (!(checks.run_checks(&tree, true) || keep_going))
315245803Stheraven	{
316245803Stheraven		return EXIT_FAILURE;
317245803Stheraven	}
318245803Stheraven	clock_t c3 = clock();
319245803Stheraven	(tree.*write_fn)(outfile);
320245803Stheraven	close(outfile);
321245803Stheraven	clock_t c4 = clock();
322245803Stheraven
323245803Stheraven	if (debug_mode)
324245803Stheraven	{
325245803Stheraven		struct rusage r;
326245803Stheraven
327245803Stheraven		getrusage(RUSAGE_SELF, &r);
328245803Stheraven		fprintf(stderr, "Peak memory usage: %ld bytes\n", r.ru_maxrss);
329245803Stheraven		fprintf(stderr, "Setup and option parsing took %f seconds\n",
330245803Stheraven				((double)(c1-c0))/CLOCKS_PER_SEC);
331245803Stheraven		fprintf(stderr, "Parsing took %f seconds\n",
332245803Stheraven				((double)(c2-c1))/CLOCKS_PER_SEC);
333245803Stheraven		fprintf(stderr, "Checking took %f seconds\n",
334245803Stheraven				((double)(c3-c2))/CLOCKS_PER_SEC);
335245803Stheraven		fprintf(stderr, "Generating output took %f seconds\n",
336245803Stheraven				((double)(c4-c3))/CLOCKS_PER_SEC);
337245803Stheraven		fprintf(stderr, "Total time: %f seconds\n",
338245803Stheraven				((double)(c4-c0))/CLOCKS_PER_SEC);
339245803Stheraven		// This is not needed, but keeps valgrind quiet.
340245803Stheraven		fclose(stdin);
341245803Stheraven	}
342245803Stheraven	return EXIT_SUCCESS;
343245803Stheraven}
344245803Stheraven
345