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 <time.h> 35 #include <stdio.h> 36 #include <fcntl.h> 37 #include <libgen.h> 38 39 #include "fdt.hh" 40 #include "checking.hh" 41 42 using namespace dtc; 43 44 /** 45 * The current major version of the tool. 46 */ 47 int version_major = 0; 48 /** 49 * The current minor version of the tool. 50 */ 51 int version_minor = 4; 52 /** 53 * The current patch level of the tool. 54 */ 55 int version_patch = 0; 56 57 static void usage(const char* argv0) 58 { 59 fprintf(stderr, "Usage:\n" 60 "\t%s\t[-fhsv] [-b boot_cpu_id] [-d dependency_file]" 61 "[-E [no-]checker_name]\n" 62 "\t\t[-H phandle_format] [-I input_format]" 63 "[-O output_format]\n" 64 "\t\t[-o output_file] [-R entries] [-S bytes] [-p bytes]" 65 "[-V blob_version]\n" 66 "\t\t-W [no-]checker_name] input_file\n", basename(argv0)); 67 } 68 69 /** 70 * Prints the current version of this program.. 71 */ 72 static void version(const char* progname) 73 { 74 fprintf(stderr, "Version: %s %d.%d.%d\n", progname, version_major, 75 version_minor, version_patch); 76 } 77 78 using fdt::device_tree; 79 80 int 81 main(int argc, char **argv) 82 { 83 int ch; 84 int outfile = fileno(stdout); 85 const char *outfile_name = "-"; 86 const char *in_file = "-"; 87 FILE *depfile = 0; 88 bool debug_mode = false; 89 void (device_tree::*write_fn)(int) = &device_tree::write_binary; 90 void (device_tree::*read_fn)(const char*, FILE*) = 91 &device_tree::parse_dts; 92 uint32_t boot_cpu; 93 bool boot_cpu_specified = false; 94 bool keep_going = false; 95 bool sort = false; 96 clock_t c0 = clock(); 97 class device_tree tree; 98 fdt::checking::check_manager checks; 99 const char *options = "hqI:O:o:V:d:R:S:p:b:fisvH:W:E:D"; 100 101 // Don't forget to update the man page if any more options are added. 102 while ((ch = getopt(argc, argv, options)) != -1) 103 { 104 switch (ch) 105 { 106 case 'h': 107 usage(argv[0]); 108 return EXIT_SUCCESS; 109 case 'v': 110 version(argv[0]); 111 return EXIT_SUCCESS; 112 case 'I': 113 { 114 string arg = string(optarg); 115 if (arg == string("dtb")) 116 { 117 read_fn = &device_tree::parse_dtb; 118 } 119 else if (arg == string("dts")) 120 { 121 read_fn = &device_tree::parse_dts; 122 } 123 else 124 { 125 fprintf(stderr, "Unknown input format: %s\n", optarg); 126 return EXIT_FAILURE; 127 } 128 break; 129 } 130 case 'O': 131 { 132 string arg = string(optarg); 133 if (arg == string("dtb")) 134 { 135 write_fn = &device_tree::write_binary; 136 } 137 else if (arg == string("asm")) 138 { 139 write_fn = &device_tree::write_asm; 140 } 141 else if (arg == string("dts")) 142 { 143 write_fn = &device_tree::write_dts; 144 } 145 else 146 { 147 fprintf(stderr, "Unknown output format: %s\n", optarg); 148 return EXIT_FAILURE; 149 } 150 break; 151 } 152 case 'o': 153 { 154 outfile_name = optarg; 155 outfile = open(optarg, O_CREAT | O_TRUNC | O_WRONLY, 0666); 156 if (outfile == -1) 157 { 158 perror("Unable to open output file"); 159 return EXIT_FAILURE; 160 } 161 break; 162 } 163 case 'D': 164 debug_mode = true; 165 break; 166 case 'V': 167 if (string(optarg) != string("17")) 168 { 169 fprintf(stderr, "Unknown output format version: %s\n", optarg); 170 return EXIT_FAILURE; 171 } 172 break; 173 case 'd': 174 { 175 if (depfile != 0) 176 { 177 fclose(depfile); 178 } 179 if (string(optarg) == string("-")) 180 { 181 depfile = stdout; 182 } 183 else 184 { 185 depfile = fdopen(open(optarg, O_CREAT | O_TRUNC | O_WRONLY, 0666), "w"); 186 if (depfile == 0) 187 { 188 perror("Unable to open dependency file"); 189 return EXIT_FAILURE; 190 } 191 } 192 break; 193 } 194 case 'H': 195 { 196 string arg = string(optarg); 197 if (arg == string("both")) 198 { 199 tree.set_phandle_format(device_tree::BOTH); 200 } 201 else if (arg == string("epapr")) 202 { 203 tree.set_phandle_format(device_tree::EPAPR); 204 } 205 else if (arg == string("linux")) 206 { 207 tree.set_phandle_format(device_tree::LINUX); 208 } 209 else 210 { 211 fprintf(stderr, "Unknown phandle format: %s\n", optarg); 212 return EXIT_FAILURE; 213 } 214 break; 215 } 216 case 'b': 217 // Don't bother to check if strtoll fails, just 218 // use the 0 it returns. 219 boot_cpu = (uint32_t)strtoll(optarg, 0, 10); 220 boot_cpu_specified = true; 221 break; 222 case 'f': 223 keep_going = true; 224 break; 225 case 'W': 226 case 'E': 227 { 228 string arg = string(optarg); 229 if ((arg.size() > 3) && (strncmp(optarg, "no-", 3) == 0)) 230 { 231 arg = string(optarg+3); 232 if (!checks.disable_checker(arg)) 233 { 234 fprintf(stderr, "Checker %s either does not exist or is already disabled\n", optarg+3); 235 } 236 break; 237 } 238 if (!checks.enable_checker(arg)) 239 { 240 fprintf(stderr, "Checker %s either does not exist or is already enabled\n", optarg); 241 } 242 break; 243 } 244 case 's': 245 { 246 sort = true; 247 break; 248 } 249 case 'i': 250 { 251 tree.add_include_path(optarg); 252 break; 253 } 254 // Should quiet warnings, but for now is silently ignored. 255 case 'q': 256 break; 257 case 'R': 258 tree.set_empty_reserve_map_entries(strtoll(optarg, 0, 10)); 259 break; 260 case 'S': 261 tree.set_blob_minimum_size(strtoll(optarg, 0, 10)); 262 break; 263 case 'p': 264 tree.set_blob_padding(strtoll(optarg, 0, 10)); 265 break; 266 default: 267 fprintf(stderr, "Unknown option %c\n", ch); 268 return EXIT_FAILURE; 269 } 270 } 271 if (optind < argc) 272 { 273 in_file = argv[optind]; 274 } 275 if (depfile != 0) 276 { 277 fputs(outfile_name, depfile); 278 fputs(": ", depfile); 279 fputs(in_file, depfile); 280 } 281 clock_t c1 = clock(); 282 (tree.*read_fn)(in_file, depfile); 283 // Override the boot CPU found in the header, if we're loading from dtb 284 if (boot_cpu_specified) 285 { 286 tree.set_boot_cpu(boot_cpu); 287 } 288 if (sort) 289 { 290 tree.sort(); 291 } 292 if (depfile != 0) 293 { 294 putc('\n', depfile); 295 fclose(depfile); 296 } 297 if (!(tree.is_valid() || keep_going)) 298 { 299 fprintf(stderr, "Failed to parse tree. Unhappy face!\n"); 300 return EXIT_FAILURE; 301 } 302 clock_t c2 = clock(); 303 if (!(checks.run_checks(&tree, true) || keep_going)) 304 { 305 return EXIT_FAILURE; 306 } 307 clock_t c3 = clock(); 308 (tree.*write_fn)(outfile); 309 close(outfile); 310 clock_t c4 = clock(); 311 312 if (debug_mode) 313 { 314 struct rusage r; 315 316 getrusage(RUSAGE_SELF, &r); 317 fprintf(stderr, "Peak memory usage: %ld bytes\n", r.ru_maxrss); 318 fprintf(stderr, "Setup and option parsing took %f seconds\n", 319 ((double)(c1-c0))/CLOCKS_PER_SEC); 320 fprintf(stderr, "Parsing took %f seconds\n", 321 ((double)(c2-c1))/CLOCKS_PER_SEC); 322 fprintf(stderr, "Checking took %f seconds\n", 323 ((double)(c3-c2))/CLOCKS_PER_SEC); 324 fprintf(stderr, "Generating output took %f seconds\n", 325 ((double)(c4-c3))/CLOCKS_PER_SEC); 326 fprintf(stderr, "Total time: %f seconds\n", 327 ((double)(c4-c0))/CLOCKS_PER_SEC); 328 // This is not needed, but keeps valgrind quiet. 329 fclose(stdin); 330 } 331 return EXIT_SUCCESS; 332 } 333 334