4 * Copyright (c) Freescale Semiconductor, Inc. All rights reserved.
5 * See included license file for license details.
14 #include "ConversionController.h"
17 #include "EncoreBootImage.h"
18 #include "smart_ptr.h"
20 #include "EncoreBootImageGenerator.h"
21 #include "SearchPath.h"
22 #include "format_string.h"
24 //! An array of strings.
25 typedef std::vector<std::string> string_vector_t;
28 const char k_toolName[] = "elftosb";
30 //! Current version number for the tool.
31 const char k_version[] = "2.6.1";
34 const char k_copyright[] = "Copyright (c) 2004-2010 Freescale Semiconductor, Inc.\nAll rights reserved.";
36 static const char * k_optionsDefinition[] = {
39 "f:chip-family <family>",
42 "P:product <version>",
43 "C:component <version>",
51 "p:search-path <path>",
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";
74 int main(int argc, char* argv[], char* envp[]);
77 * \brief Class that encapsulates the elftosb tool.
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.
86 //! Supported chip families.
89 k37xxFamily, //!< 37xx series.
90 kMX28Family, //!< Catskills series.
94 * \brief A structure describing an entry in the table of chip family names.
96 struct FamilyNameTableEntry
98 const char * const name;
102 //! \brief Table that maps from family name strings to chip family constants.
103 static const FamilyNameTableEntry kFamilyNameTable[];
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.
126 * Creates the singleton logger instance.
128 elftosbTool(int argc, char * argv[])
135 m_useDefaultKey(false),
136 m_commandFilePath(NULL),
137 m_outputFilePath(NULL),
140 m_componentVersion(),
141 m_productVersionSpecified(false),
142 m_componentVersionSpecified(false),
143 m_family(k37xxFamily),
146 // create logger instance
147 m_logger = new StdoutLogger();
148 m_logger->setFilterLevel(Logger::INFO);
149 Log::setLogger(m_logger);
160 * \brief Searches the family name table.
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.
165 bool lookupFamilyName(const char * name, chip_family_t * family)
167 // Create a local read-write copy of the argument string.
168 std::string familyName(name);
170 // Convert the argument string to lower case for case-insensitive comparison.
171 for (int n=0; n < familyName.length(); n++)
173 familyName[n] = tolower(familyName[n]);
176 // Exit the loop if we hit the NULL terminator entry.
177 const FamilyNameTableEntry * entry = &kFamilyNameTable[0];
178 for (; entry->name; entry++)
180 // Compare lowercased name with the table entry.
181 if (familyName == entry->name)
183 *family = entry->family;
188 // Failed to find a matching name.
193 * Reads the command line options passed into the constructor.
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.
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.
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.
210 Options options(*m_argv, k_optionsDefinition);
211 OptArgvIter iter(--m_argc, ++m_argv);
213 // process command line options
216 while (optchar = options(iter, optarg))
225 printf("%s %s\n%s\n", k_toolName, k_version, k_copyright);
229 if (!lookupFamilyName(optarg, &m_family))
231 Log::log(Logger::ERROR, "error: unknown chip family '%s'\n", optarg);
238 m_commandFilePath = optarg;
242 m_outputFilePath = optarg;
246 m_productVersion.set(optarg);
247 m_productVersionSpecified = true;
251 m_componentVersion.set(optarg);
252 m_componentVersionSpecified = true;
256 m_keyFilePaths.push_back(optarg);
260 m_useDefaultKey = true;
264 overrideVariable(optarg);
268 overrideOption(optarg);
272 Log::getLogger()->setFilterLevel(Logger::DEBUG);
276 Log::getLogger()->setFilterLevel(Logger::WARNING);
285 std::string newSearchPath(optarg);
286 PathSearcher::getGlobalSearcher().addSearchPath(newSearchPath);
291 Log::log(Logger::ERROR, "error: unrecognized option\n\n");
297 // handle positional args
298 if (iter.index() < m_argc)
300 Log::SetOutputLevel leveler(Logger::DEBUG);
301 Log::log("positional args:\n");
303 for (i = iter.index(); i < m_argc; ++i)
305 Log::log("%d: %s\n", i - iter.index(), m_argv[i]);
306 m_positionalArgs.push_back(m_argv[i]);
315 * Prints help for the tool.
317 void printUsage(Options & options)
319 options.usage(std::cout, "files...");
320 printf(k_usageText, k_toolName);
324 * \brief Core of the tool.
326 * Calls processOptions() to handle command line options before performing the
327 * real work the tool does.
333 // read command line options
335 if ((result = processOptions()) != -1)
340 // set verbose logging
343 // check argument values
346 // set up the controller
347 m_controller.setCommandFilePath(m_commandFilePath);
349 // add external paths to controller
350 string_vector_t::iterator it = m_positionalArgs.begin();
351 for (; it != m_positionalArgs.end(); ++it)
353 m_controller.addExternalFilePath(*it);
359 catch (std::exception & e)
361 Log::log(Logger::ERROR, "error: %s\n", e.what());
366 Log::log(Logger::ERROR, "error: unexpected exception\n");
374 * \brief Validate arguments that can be checked.
375 * \exception std::runtime_error Thrown if an argument value fails to pass validation.
377 void checkArguments()
379 if (m_commandFilePath == NULL)
381 throw std::runtime_error("no command file was specified");
383 if (m_outputFilePath == NULL)
385 throw std::runtime_error("no output file was specified");
390 * \brief Turns on verbose logging.
392 void setVerboseLogging()
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())
401 Log::getLogger()->setFilterLevel(Logger::INFO2);
404 Log::getLogger()->setFilterLevel(Logger::DEBUG2);
411 * \brief Returns the integer value for a string.
413 * Metric multiplier prefixes are supported.
415 uint32_t parseIntValue(const char * value)
417 // Accept 'true'/'yes' and 'false'/'no' as integer values.
418 if ((strcmp(value, "true") == 0) || (strcmp(value, "yes") == 0))
422 else if ((strcmp(value, "false") == 0) || (strcmp(value, "no") == 0))
427 uint32_t intValue = strtoul(value, NULL, 0);
429 switch (value[strlen(value) - 1])
432 multiplier = 1024 * 1024 * 1024;
435 multiplier = 1024 * 1024;
443 intValue *= multiplier;
448 * \brief Parses the -D option to override a constant value.
450 void overrideVariable(const char * optarg)
452 // split optarg into two strings
453 std::string constName(optarg);
455 for (i=0; i < strlen(optarg); ++i)
457 if (optarg[i] == '=')
459 constName.resize(i++);
464 uint32_t constValue = parseIntValue(&optarg[i]);
466 elftosb::EvalContext & context = m_controller.getEvalContext();
467 context.setVariable(constName, constValue);
468 context.lockVariable(constName);
474 void overrideOption(const char * optarg)
476 // split optarg into two strings
477 std::string optionName(optarg);
479 for (i=0; i < strlen(optarg); ++i)
481 if (optarg[i] == '=')
483 optionName.resize(i++);
488 // handle quotes for option value
489 const char * valuePtr = &optarg[i];
490 bool isString = false;
492 if (valuePtr[0] == '"')
494 // remember that the value is a string and get rid of the opening quote
498 // remove trailing quote if present
499 len = strlen(valuePtr);
500 if (valuePtr[len] == '"')
506 elftosb::Value * value;
509 std::string stringValue(valuePtr);
510 stringValue.resize(len); // remove trailing quote
511 value = new elftosb::StringValue(stringValue);
515 value = new elftosb::IntegerValue(parseIntValue(valuePtr));
518 // Set and lock the option in the controller
519 m_controller.setOption(optionName, value);
520 m_controller.lockOption(optionName);
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.
531 // create a generator for the chosen chip family
532 smart_ptr<elftosb::BootImageGenerator> generator;
536 generator = new elftosb::EncoreBootImageGenerator;
537 elftosb::g_enableHABSupport = false;
541 generator = new elftosb::EncoreBootImageGenerator;
542 elftosb::g_enableHABSupport = true;
546 // process input and get a boot image
548 smart_ptr<elftosb::BootImage> image = m_controller.generateOutput(generator);
551 throw std::runtime_error("failed to produce output!");
554 // set version numbers if they were provided on the command line
555 if (m_productVersionSpecified)
557 image->setProductVersion(m_productVersion);
559 if (m_componentVersionSpecified)
561 image->setComponentVersion(m_componentVersion);
564 // special handling for each family
571 elftosb::EncoreBootImage * encoreImage = dynamic_cast<elftosb::EncoreBootImage*>(image.get());
575 addCryptoKeys(encoreImage);
578 encoreImage->debugPrint();
585 std::ofstream outputStream(m_outputFilePath, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);
586 if (outputStream.is_open())
588 image->writeToStream(outputStream);
592 throw std::runtime_error(format_string("could not open output file %s", m_outputFilePath));
599 void addCryptoKeys(elftosb::EncoreBootImage * encoreImage)
601 string_vector_t::iterator it = m_keyFilePaths.begin();
602 for (; it != m_keyFilePaths.end(); ++it)
604 std::string & keyPath = *it;
606 std::string actualPath;
607 bool found = PathSearcher::getGlobalSearcher().search(keyPath, PathSearcher::kFindFile, true, actualPath);
610 throw std::runtime_error(format_string("unable to find key file %s\n", keyPath.c_str()));
613 std::ifstream keyStream(actualPath.c_str(), std::ios_base::in);
614 if (!keyStream.is_open())
616 throw std::runtime_error(format_string("unable to read key file %s\n", keyPath.c_str()));
622 // read as many keys as possible from the stream
625 AESKey<128> key(keyStream);
626 encoreImage->addKey(key);
634 // ignore the exception -- there are just no more keys in the stream
638 // add the default key of all zero bytes if requested
641 AESKey<128> defaultKey;
642 encoreImage->addKey(defaultKey);
647 * \brief Write the value of each byte of the \a key to the log.
649 void dumpKey(const AESKey<128> & key)
652 Log::log(Logger::DEBUG, "key bytes: ");
653 AESKey<128>::key_t the_key;
654 key.getKey(&the_key);
658 Log::log(Logger::DEBUG, "%02x ", the_key[q]);
660 Log::log(Logger::DEBUG, "\n");
665 const elftosbTool::FamilyNameTableEntry elftosbTool::kFamilyNameTable[] =
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 },
677 // Null terminator entry.
678 { NULL, k37xxFamily }
682 * Main application entry point. Creates an sbtool instance and lets it take over.
684 int main(int argc, char* argv[], char* envp[])
688 return elftosbTool(argc, argv).run();
692 Log::log(Logger::ERROR, "error: unexpected exception\n");