1 /*- 2 * Copyright (c) 2013 David Chisnall 3 * All rights reserved. 4 * 5 * This software was developed by SRI International and the University of 6 * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) 7 * ("CTSRD"), as part of the DARPA CRASH research programme. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 * 30 * $FreeBSD$ 31 */ 32 33 #include <sys/resource.h> 34 #include <fcntl.h> 35 #include <libgen.h> 36 #include <limits.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <time.h> 40 #include <unistd.h> 41 42 43 #include "fdt.hh" 44 #include "checking.hh" 45 46 using namespace dtc; 47 48 /** 49 * The current major version of the tool. 50 */ 51 int version_major = 0; 52 /** 53 * The current minor version of the tool. 54 */ 55 int version_minor = 4; 56 /** 57 * The current patch level of the tool. 58 */ 59 int version_patch = 0; 60 61 static void usage(const char* argv0) 62 { 63 fprintf(stderr, "Usage:\n" 64 "\t%s\t[-fhsv] [-b boot_cpu_id] [-d dependency_file]" 65 "[-E [no-]checker_name]\n" 66 "\t\t[-H phandle_format] [-I input_format]" 67 "[-O output_format]\n" 68 "\t\t[-o output_file] [-R entries] [-S bytes] [-p bytes]" 69 "[-V blob_version]\n" 70 "\t\t-W [no-]checker_name] input_file\n", basename(argv0)); 71 } 72 73 /** 74 * Prints the current version of this program.. 75 */ 76 static void version(const char* progname) 77 { 78 fprintf(stderr, "Version: %s %d.%d.%d\n", progname, version_major, 79 version_minor, version_patch); 80 } 81 82 using fdt::device_tree; 83 84 int 85 main(int argc, char **argv) 86 { 87 int ch; 88 int outfile = fileno(stdout); 89 const char *outfile_name = "-"; 90 const char *in_file = "-"; 91 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:D"; 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 default: 271 fprintf(stderr, "Unknown option %c\n", ch); 272 return EXIT_FAILURE; 273 } 274 } 275 if (optind < argc) 276 { 277 in_file = argv[optind]; 278 } 279 if (depfile != 0) 280 { 281 fputs(outfile_name, depfile); 282 fputs(": ", depfile); 283 fputs(in_file, depfile); 284 } 285 clock_t c1 = clock(); 286 (tree.*read_fn)(in_file, depfile); 287 // Override the boot CPU found in the header, if we're loading from dtb 288 if (boot_cpu_specified) 289 { 290 tree.set_boot_cpu(boot_cpu); 291 } 292 if (sort) 293 { 294 tree.sort(); 295 } 296 if (depfile != 0) 297 { 298 putc('\n', depfile); 299 fclose(depfile); 300 } 301 if (!(tree.is_valid() || keep_going)) 302 { 303 fprintf(stderr, "Failed to parse tree. Unhappy face!\n"); 304 return EXIT_FAILURE; 305 } 306 clock_t c2 = clock(); 307 if (!(checks.run_checks(&tree, true) || keep_going)) 308 { 309 return EXIT_FAILURE; 310 } 311 clock_t c3 = clock(); 312 (tree.*write_fn)(outfile); 313 close(outfile); 314 clock_t c4 = clock(); 315 316 if (debug_mode) 317 { 318 struct rusage r; 319 320 getrusage(RUSAGE_SELF, &r); 321 fprintf(stderr, "Peak memory usage: %ld bytes\n", r.ru_maxrss); 322 fprintf(stderr, "Setup and option parsing took %f seconds\n", 323 ((double)(c1-c0))/CLOCKS_PER_SEC); 324 fprintf(stderr, "Parsing took %f seconds\n", 325 ((double)(c2-c1))/CLOCKS_PER_SEC); 326 fprintf(stderr, "Checking took %f seconds\n", 327 ((double)(c3-c2))/CLOCKS_PER_SEC); 328 fprintf(stderr, "Generating output took %f seconds\n", 329 ((double)(c4-c3))/CLOCKS_PER_SEC); 330 fprintf(stderr, "Total time: %f seconds\n", 331 ((double)(c4-c0))/CLOCKS_PER_SEC); 332 // This is not needed, but keeps valgrind quiet. 333 fclose(stdin); 334 } 335 return EXIT_SUCCESS; 336 } 337 338