xref: /netbsd-src/external/bsd/elftosb/dist/elftosb2/elftosb.cpp (revision 616728154accb2da0d5a0126e1280ca29f8ba569)
1 /*
2  * File:	elftosb.cpp
3  *
4  * Copyright (c) Freescale Semiconductor, Inc. All rights reserved.
5  * See included license file for license details.
6  */
7 
8 #include "stdafx.h"
9 #include <iostream>
10 #include <fstream>
11 #include <sstream>
12 #include <stdlib.h>
13 #include <stdexcept>
14 #include "ConversionController.h"
15 #include "options.h"
16 #include "Version.h"
17 #include "EncoreBootImage.h"
18 #include "smart_ptr.h"
19 #include "Logging.h"
20 #include "EncoreBootImageGenerator.h"
21 #include "SearchPath.h"
22 #include "format_string.h"
23 
24 //! An array of strings.
25 typedef std::vector<std::string> string_vector_t;
26 
27 //! The tool's name.
28 const char k_toolName[] = "elftosb";
29 
30 //! Current version number for the tool.
31 const char k_version[] = "2.6.1";
32 
33 //! Copyright string.
34 const char k_copyright[] = "Copyright (c) 2004-2010 Freescale Semiconductor, Inc.\nAll rights reserved.";
35 
36 static const char * k_optionsDefinition[] = {
37 	"?|help",
38 	"v|version",
39 	"f:chip-family <family>",
40 	"c:command <file>",
41 	"o:output <file>",
42 	"P:product <version>",
43 	"C:component <version>",
44 	"k:key <file>",
45 	"z|zero-key",
46 	"D:define <const>",
47 	"O:option <option>",
48 	"d|debug",
49 	"q|quiet",
50 	"V|verbose",
51 	"p:search-path <path>",
52 	NULL
53 };
54 
55 //! Help string.
56 const char k_usageText[] = "\nOptions:\n\
57   -?/--help                    Show this help\n\
58   -v/--version                 Display tool version\n\
59   -f/--chip-family <family>    Select the chip family (default is 37xx)\n\
60   -c/--command <file>          Use this command file\n\
61   -o/--output <file>           Write output to this file\n\
62   -p/--search-path <path>      Add a search path used to find input files\n\
63   -P/--product <version        Set product version\n\
64   -C/--component <version>     Set component version\n\
65   -k/--key <file>              Add OTP key, enable encryption\n\
66   -z/--zero-key                Add default key of all zeroes\n\
67   -D/--define <const>=<int>    Define or override a constant value\n\
68   -O/--option <name>=<value>   Set or override a processing option\n\
69   -d/--debug                   Enable debug output\n\
70   -q/--quiet                   Output only warnings and errors\n\
71   -V/--verbose                 Print extra detailed log information\n\n";
72 
73 // prototypes
74 int main(int argc, char* argv[], char* envp[]);
75 
76 /*!
77  * \brief Class that encapsulates the elftosb tool.
78  *
79  * A single global logger instance is created during object construction. It is
80  * never freed because we need it up to the last possible minute, when an
81  * exception could be thrown.
82  */
83 class elftosbTool
84 {
85 protected:
86 	//! Supported chip families.
87 	enum chip_family_t
88 	{
89 		k37xxFamily,	//!< 37xx series.
90 		kMX28Family,	//!< Catskills series.
91 	};
92 
93 	/*!
94 	 * \brief A structure describing an entry in the table of chip family names.
95 	 */
96 	struct FamilyNameTableEntry
97 	{
98 		const char * const name;
99 		chip_family_t family;
100 	};
101 
102 	//! \brief Table that maps from family name strings to chip family constants.
103 	static const FamilyNameTableEntry kFamilyNameTable[];
104 
105 	int m_argc;							//!< Number of command line arguments.
106 	char ** m_argv;						//!< String value for each command line argument.
107 	StdoutLogger * m_logger;			//!< Singleton logger instance.
108 	string_vector_t m_keyFilePaths;		//!< Paths to OTP key files.
109 	string_vector_t m_positionalArgs;	//!< Arguments coming after explicit options.
110 	bool m_isVerbose;					//!< Whether the verbose flag was turned on.
111 	bool m_useDefaultKey;					//!< Include a default (zero) crypto key.
112 	const char * m_commandFilePath;		//!< Path to the elftosb command file.
113 	const char * m_outputFilePath;		//!< Path to the output .sb file.
114 	const char * m_searchPath;			//!< Optional search path for input files.
115 	elftosb::version_t m_productVersion;	//!< Product version specified on command line.
116 	elftosb::version_t m_componentVersion;	//!< Component version specified on command line.
117 	bool m_productVersionSpecified;		//!< True if the product version was specified on the command line.
118 	bool m_componentVersionSpecified;		//!< True if the component version was specified on the command line.
119 	chip_family_t m_family;				//!< Chip family that the output file is formatted for.
120 	elftosb::ConversionController m_controller;	//!< Our conversion controller instance.
121 
122 public:
123 	/*!
124 	 * Constructor.
125 	 *
126 	 * Creates the singleton logger instance.
127 	 */
elftosbTool(int argc,char * argv[])128 	elftosbTool(int argc, char * argv[])
129 	:	m_argc(argc),
130 		m_argv(argv),
131 		m_logger(0),
132 		m_keyFilePaths(),
133 		m_positionalArgs(),
134 		m_isVerbose(false),
135 		m_useDefaultKey(false),
136 		m_commandFilePath(NULL),
137 		m_outputFilePath(NULL),
138 		m_searchPath(NULL),
139 		m_productVersion(),
140 		m_componentVersion(),
141 		m_productVersionSpecified(false),
142 		m_componentVersionSpecified(false),
143 		m_family(k37xxFamily),
144 		m_controller()
145 	{
146 		// create logger instance
147 		m_logger = new StdoutLogger();
148 		m_logger->setFilterLevel(Logger::INFO);
149 		Log::setLogger(m_logger);
150 	}
151 
152 	/*!
153 	 * Destructor.
154 	 */
~elftosbTool()155 	~elftosbTool()
156 	{
157 	}
158 
159 	/*!
160 	 * \brief Searches the family name table.
161 	 *
162 	 * \retval true The \a name was found in the table, and \a family is valid.
163 	 * \retval false No matching family name was found. The \a family argument is not modified.
164 	 */
lookupFamilyName(const char * name,chip_family_t * family)165 	bool lookupFamilyName(const char * name, chip_family_t * family)
166 	{
167 		// Create a local read-write copy of the argument string.
168 		std::string familyName(name);
169 
170 		// Convert the argument string to lower case for case-insensitive comparison.
171 		for (int n=0; n < familyName.length(); n++)
172 		{
173 			familyName[n] = tolower(familyName[n]);
174 		}
175 
176         // Exit the loop if we hit the NULL terminator entry.
177 		const FamilyNameTableEntry * entry = &kFamilyNameTable[0];
178 		for (; entry->name; entry++)
179 		{
180 			// Compare lowercased name with the table entry.
181 			if (familyName == entry->name)
182 			{
183 				*family = entry->family;
184 				return true;
185 			}
186 		}
187 
188 		// Failed to find a matching name.
189 		return false;
190 	}
191 
192 	/*!
193 	 * Reads the command line options passed into the constructor.
194 	 *
195 	 * This method can return a return code to its caller, which will cause the
196 	 * tool to exit immediately with that return code value. Normally, though, it
197 	 * will return -1 to signal that the tool should continue to execute and
198 	 * all options were processed successfully.
199 	 *
200 	 * The Options class is used to parse command line options. See
201 	 * #k_optionsDefinition for the list of options and #k_usageText for the
202 	 * descriptive help for each option.
203 	 *
204 	 * \retval -1 The options were processed successfully. Let the tool run normally.
205 	 * \return A zero or positive result is a return code value that should be
206 	 *		returned from the tool as it exits immediately.
207 	 */
processOptions()208 	int processOptions()
209 	{
210 		Options options(*m_argv, k_optionsDefinition);
211 		OptArgvIter iter(--m_argc, ++m_argv);
212 
213 		// process command line options
214 		int optchar;
215 		const char * optarg;
216 		while (optchar = options(iter, optarg))
217 		{
218 			switch (optchar)
219 			{
220 				case '?':
221 					printUsage(options);
222 					return 0;
223 
224 				case 'v':
225 					printf("%s %s\n%s\n", k_toolName, k_version, k_copyright);
226 					return 0;
227 
228 				case 'f':
229 					if (!lookupFamilyName(optarg, &m_family))
230 					{
231 						Log::log(Logger::ERROR, "error: unknown chip family '%s'\n", optarg);
232 						printUsage(options);
233 						return 0;
234 					}
235 					break;
236 
237 				case 'c':
238 					m_commandFilePath = optarg;
239 					break;
240 
241 				case 'o':
242 					m_outputFilePath = optarg;
243 					break;
244 
245 				case 'P':
246 					m_productVersion.set(optarg);
247 					m_productVersionSpecified = true;
248 					break;
249 
250 				case 'C':
251 					m_componentVersion.set(optarg);
252 					m_componentVersionSpecified = true;
253 					break;
254 
255 				case 'k':
256 					m_keyFilePaths.push_back(optarg);
257 					break;
258 
259 				case 'z':
260 					m_useDefaultKey = true;
261 					break;
262 
263 				case 'D':
264 					overrideVariable(optarg);
265 					break;
266 
267 				case 'O':
268 					overrideOption(optarg);
269 					break;
270 
271 				case 'd':
272 					Log::getLogger()->setFilterLevel(Logger::DEBUG);
273 					break;
274 
275 				case 'q':
276 					Log::getLogger()->setFilterLevel(Logger::WARNING);
277 					break;
278 
279 				case 'V':
280 					m_isVerbose = true;
281 					break;
282 
283 				case 'p':
284 				{
285 					std::string newSearchPath(optarg);
286 					PathSearcher::getGlobalSearcher().addSearchPath(newSearchPath);
287 					break;
288 				}
289 
290 				default:
291 					Log::log(Logger::ERROR, "error: unrecognized option\n\n");
292 					printUsage(options);
293 					return 0;
294 			}
295 		}
296 
297 		// handle positional args
298 		if (iter.index() < m_argc)
299 		{
300 			Log::SetOutputLevel leveler(Logger::DEBUG);
301 			Log::log("positional args:\n");
302 			int i;
303 			for (i = iter.index(); i < m_argc; ++i)
304 			{
305 				Log::log("%d: %s\n", i - iter.index(), m_argv[i]);
306 				m_positionalArgs.push_back(m_argv[i]);
307 			}
308 		}
309 
310 		// all is well
311 		return -1;
312 	}
313 
314 	/*!
315 	 * Prints help for the tool.
316 	 */
printUsage(Options & options)317 	void printUsage(Options & options)
318 	{
319 		options.usage(std::cout, "files...");
320 		printf("%s", k_usageText);
321 	}
322 
323 	/*!
324 	 * \brief Core of the tool.
325 	 *
326 	 * Calls processOptions() to handle command line options before performing the
327 	 * real work the tool does.
328 	 */
run()329 	int run()
330 	{
331 		try
332 		{
333 			// read command line options
334 			int result;
335 			if ((result = processOptions()) != -1)
336 			{
337 				return result;
338 			}
339 
340 			// set verbose logging
341 			setVerboseLogging();
342 
343 			// check argument values
344 			checkArguments();
345 
346 			// set up the controller
347 			m_controller.setCommandFilePath(m_commandFilePath);
348 
349 			// add external paths to controller
350 			string_vector_t::iterator it = m_positionalArgs.begin();
351 			for (; it != m_positionalArgs.end(); ++it)
352 			{
353 				m_controller.addExternalFilePath(*it);
354 			}
355 
356 			// run conversion
357 			convert();
358 		}
359 		catch (std::exception & e)
360 		{
361 			Log::log(Logger::ERROR, "error: %s\n", e.what());
362 			return 1;
363 		}
364 		catch (...)
365 		{
366 			Log::log(Logger::ERROR, "error: unexpected exception\n");
367 			return 1;
368 		}
369 
370 		return 0;
371 	}
372 
373 	/*!
374 	 * \brief Validate arguments that can be checked.
375 	 * \exception std::runtime_error Thrown if an argument value fails to pass validation.
376 	 */
checkArguments()377 	void checkArguments()
378 	{
379 		if (m_commandFilePath == NULL)
380 		{
381 			throw std::runtime_error("no command file was specified");
382 		}
383 		if (m_outputFilePath == NULL)
384 		{
385 			throw std::runtime_error("no output file was specified");
386 		}
387 	}
388 
389 	/*!
390 	 * \brief Turns on verbose logging.
391 	 */
setVerboseLogging()392 	void setVerboseLogging()
393 	{
394 		if (m_isVerbose)
395 		{
396 			// verbose only affects the INFO and DEBUG filter levels
397 			// if the user has selected quiet mode, it overrides verbose
398 			switch (Log::getLogger()->getFilterLevel())
399 			{
400 				case Logger::INFO:
401 					Log::getLogger()->setFilterLevel(Logger::INFO2);
402 					break;
403 				case Logger::DEBUG:
404 					Log::getLogger()->setFilterLevel(Logger::DEBUG2);
405 					break;
406 			}
407 		}
408 	}
409 
410 	/*!
411 	 * \brief Returns the integer value for a string.
412 	 *
413 	 * Metric multiplier prefixes are supported.
414 	 */
parseIntValue(const char * value)415 	uint32_t parseIntValue(const char * value)
416 	{
417 		// Accept 'true'/'yes' and 'false'/'no' as integer values.
418 		if ((strcmp(value, "true") == 0) || (strcmp(value, "yes") == 0))
419 		{
420 			return 1;
421 		}
422 		else if ((strcmp(value, "false") == 0) || (strcmp(value, "no") == 0))
423 		{
424 			return 0;
425 		}
426 
427 		uint32_t intValue = strtoul(value, NULL, 0);
428 		unsigned multiplier;
429 		switch (value[strlen(value) - 1])
430 		{
431 			case 'G':
432 				multiplier = 1024 * 1024 * 1024;
433 				break;
434 			case 'M':
435 				multiplier = 1024 * 1024;
436 				break;
437 			case 'K':
438 				multiplier = 1024;
439 				break;
440 			default:
441 				multiplier = 1;
442 		}
443 		intValue *= multiplier;
444 		return intValue;
445 	}
446 
447 	/*!
448 	 * \brief Parses the -D option to override a constant value.
449 	 */
overrideVariable(const char * optarg)450 	void overrideVariable(const char * optarg)
451 	{
452 		// split optarg into two strings
453 		std::string constName(optarg);
454 		int i;
455 		for (i=0; i < strlen(optarg); ++i)
456 		{
457 			if (optarg[i] == '=')
458 			{
459 				constName.resize(i++);
460 				break;
461 			}
462 		}
463 
464 		uint32_t constValue = parseIntValue(&optarg[i]);
465 
466 		elftosb::EvalContext & context = m_controller.getEvalContext();
467 		context.setVariable(constName, constValue);
468 		context.lockVariable(constName);
469 	}
470 
471 	/*!
472 	 * \brief
473 	 */
overrideOption(const char * optarg)474 	void overrideOption(const char * optarg)
475 	{
476 		// split optarg into two strings
477 		std::string optionName(optarg);
478 		int i;
479 		for (i=0; i < strlen(optarg); ++i)
480 		{
481 			if (optarg[i] == '=')
482 			{
483 				optionName.resize(i++);
484 				break;
485 			}
486 		}
487 
488 		// handle quotes for option value
489 		const char * valuePtr = &optarg[i];
490 		bool isString = false;
491 		int len;
492 		if (valuePtr[0] == '"')
493 		{
494 			// remember that the value is a string and get rid of the opening quote
495 			isString = true;
496 			valuePtr++;
497 
498 			// remove trailing quote if present
499 			len = strlen(valuePtr);
500 			if (valuePtr[len] == '"')
501 			{
502 				len--;
503 			}
504 		}
505 
506 		elftosb::Value * value;
507 		if (isString)
508 		{
509 			std::string stringValue(valuePtr);
510 			stringValue.resize(len);	// remove trailing quote
511 			value = new elftosb::StringValue(stringValue);
512 		}
513 		else
514 		{
515 			value = new elftosb::IntegerValue(parseIntValue(valuePtr));
516 		}
517 
518 		// Set and lock the option in the controller
519 		m_controller.setOption(optionName, value);
520 		m_controller.lockOption(optionName);
521 	}
522 
523 	/*!
524 	 * \brief Do the conversion.
525 	 * \exception std::runtime_error This exception is thrown if the conversion controller does
526 	 *		not produce a boot image, or if the output file cannot be opened. Other errors
527 	 *		internal to the conversion controller may also produce this exception.
528 	 */
convert()529 	void convert()
530 	{
531 		// create a generator for the chosen chip family
532 		smart_ptr<elftosb::BootImageGenerator> generator;
533 		switch (m_family)
534 		{
535 			case k37xxFamily:
536 				generator = new elftosb::EncoreBootImageGenerator;
537 				elftosb::g_enableHABSupport = false;
538 				break;
539 
540 			case kMX28Family:
541 				generator = new elftosb::EncoreBootImageGenerator;
542 				elftosb::g_enableHABSupport = true;
543 				break;
544 		}
545 
546 		// process input and get a boot image
547 		m_controller.run();
548 		smart_ptr<elftosb::BootImage> image = m_controller.generateOutput(generator);
549 		if (!image)
550 		{
551 			throw std::runtime_error("failed to produce output!");
552 		}
553 
554 		// set version numbers if they were provided on the command line
555 		if (m_productVersionSpecified)
556 		{
557 			image->setProductVersion(m_productVersion);
558 		}
559 		if (m_componentVersionSpecified)
560 		{
561 			image->setComponentVersion(m_componentVersion);
562 		}
563 
564 		// special handling for each family
565 		switch (m_family)
566 		{
567 			case k37xxFamily:
568 			case kMX28Family:
569 			{
570 				// add OTP keys
571 				elftosb::EncoreBootImage * encoreImage = dynamic_cast<elftosb::EncoreBootImage*>(image.get());
572 				if (encoreImage)
573 				{
574 					// add keys
575 					addCryptoKeys(encoreImage);
576 
577 					// print debug image
578 					encoreImage->debugPrint();
579 				}
580 				break;
581 			}
582 		}
583 
584 		// write output
585 		std::ofstream outputStream(m_outputFilePath, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);
586 		if (outputStream.is_open())
587 		{
588 			image->writeToStream(outputStream);
589 		}
590 		else
591 		{
592 			throw std::runtime_error(format_string("could not open output file %s", m_outputFilePath));
593 		}
594 	}
595 
596 	/*!
597 	 * \brief
598 	 */
addCryptoKeys(elftosb::EncoreBootImage * encoreImage)599 	void addCryptoKeys(elftosb::EncoreBootImage * encoreImage)
600 	{
601 		string_vector_t::iterator it = m_keyFilePaths.begin();
602 		for (; it != m_keyFilePaths.end(); ++it)
603 		{
604 			std::string & keyPath = *it;
605 
606 			std::string actualPath;
607 			bool found = PathSearcher::getGlobalSearcher().search(keyPath, PathSearcher::kFindFile, true, actualPath);
608 			if (!found)
609 			{
610 				throw std::runtime_error(format_string("unable to find key file %s\n", keyPath.c_str()));
611 			}
612 
613 			std::ifstream keyStream(actualPath.c_str(), std::ios_base::in);
614 			if (!keyStream.is_open())
615 			{
616 				throw std::runtime_error(format_string("unable to read key file %s\n", keyPath.c_str()));
617 			}
618 			keyStream.seekg(0);
619 
620 			try
621 			{
622 				// read as many keys as possible from the stream
623 				while (true)
624 				{
625 					AESKey<128> key(keyStream);
626 					encoreImage->addKey(key);
627 
628 					// dump key bytes
629 					dumpKey(key);
630 				}
631 			}
632 			catch (...)
633 			{
634 				// ignore the exception -- there are just no more keys in the stream
635 			}
636 		}
637 
638 		// add the default key of all zero bytes if requested
639 		if (m_useDefaultKey)
640 		{
641 			AESKey<128> defaultKey;
642 			encoreImage->addKey(defaultKey);
643 		}
644 	}
645 
646 	/*!
647 	 * \brief Write the value of each byte of the \a key to the log.
648 	 */
dumpKey(const AESKey<128> & key)649 	void dumpKey(const AESKey<128> & key)
650 	{
651 		// dump key bytes
652 		Log::log(Logger::DEBUG, "key bytes: ");
653 		AESKey<128>::key_t the_key;
654 		key.getKey(&the_key);
655 		int q;
656 		for (q=0; q<16; q++)
657 		{
658 			Log::log(Logger::DEBUG, "%02x ", the_key[q]);
659 		}
660 		Log::log(Logger::DEBUG, "\n");
661 	}
662 
663 };
664 
665 const elftosbTool::FamilyNameTableEntry elftosbTool::kFamilyNameTable[] =
666 	{
667 		{ "37xx", k37xxFamily },
668 		{ "377x", k37xxFamily },
669 		{ "378x", k37xxFamily },
670 		{ "mx23", k37xxFamily },
671 		{ "imx23", k37xxFamily },
672 		{ "i.mx23", k37xxFamily },
673 		{ "mx28", kMX28Family },
674 		{ "imx28", kMX28Family },
675 		{ "i.mx28", kMX28Family },
676 
677 		// Null terminator entry.
678 		{ NULL, k37xxFamily }
679 	};
680 
681 /*!
682  * Main application entry point. Creates an sbtool instance and lets it take over.
683  */
main(int argc,char * argv[],char * envp[])684 int main(int argc, char* argv[], char* envp[])
685 {
686 	try
687 	{
688 		return elftosbTool(argc, argv).run();
689 	}
690 	catch (...)
691 	{
692 		Log::log(Logger::ERROR, "error: unexpected exception\n");
693 		return 1;
694 	}
695 
696 	return 0;
697 }
698 
699 
700 
701