Home | History | Annotate | Line # | Download | only in elftosb2
elftosb.cpp revision 1.1.4.2
      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 	 */
    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 	 */
    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 	 */
    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 	 */
    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 	 */
    317 	void printUsage(Options & options)
    318 	{
    319 		options.usage(std::cout, "files...");
    320 		printf(k_usageText, k_toolName);
    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 	 */
    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 	 */
    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 	 */
    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 	 */
    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 	 */
    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 	 */
    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 	 */
    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 	 */
    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 	 */
    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  */
    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