--- /dev/null
+/*
+ * File: elftosb.cpp
+ *
+ * Copyright (c) Freescale Semiconductor, Inc. All rights reserved.
+ * See included license file for license details.
+ */
+
+#include "stdafx.h"
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <stdlib.h>
+#include <stdexcept>
+#include "ConversionController.h"
+#include "options.h"
+#include "Version.h"
+#include "EncoreBootImage.h"
+#include "smart_ptr.h"
+#include "Logging.h"
+#include "EncoreBootImageGenerator.h"
+#include "SearchPath.h"
+#include "format_string.h"
+
+//! An array of strings.
+typedef std::vector<std::string> string_vector_t;
+
+//! The tool's name.
+const char k_toolName[] = "elftosb";
+
+//! Current version number for the tool.
+const char k_version[] = "2.6.1";
+
+//! Copyright string.
+const char k_copyright[] = "Copyright (c) 2004-2010 Freescale Semiconductor, Inc.\nAll rights reserved.";
+
+static const char * k_optionsDefinition[] = {
+ "?|help",
+ "v|version",
+ "f:chip-family <family>",
+ "c:command <file>",
+ "o:output <file>",
+ "P:product <version>",
+ "C:component <version>",
+ "k:key <file>",
+ "z|zero-key",
+ "D:define <const>",
+ "O:option <option>",
+ "d|debug",
+ "q|quiet",
+ "V|verbose",
+ "p:search-path <path>",
+ NULL
+};
+
+//! Help string.
+const char k_usageText[] = "\nOptions:\n\
+ -?/--help Show this help\n\
+ -v/--version Display tool version\n\
+ -f/--chip-family <family> Select the chip family (default is 37xx)\n\
+ -c/--command <file> Use this command file\n\
+ -o/--output <file> Write output to this file\n\
+ -p/--search-path <path> Add a search path used to find input files\n\
+ -P/--product <version Set product version\n\
+ -C/--component <version> Set component version\n\
+ -k/--key <file> Add OTP key, enable encryption\n\
+ -z/--zero-key Add default key of all zeroes\n\
+ -D/--define <const>=<int> Define or override a constant value\n\
+ -O/--option <name>=<value> Set or override a processing option\n\
+ -d/--debug Enable debug output\n\
+ -q/--quiet Output only warnings and errors\n\
+ -V/--verbose Print extra detailed log information\n\n";
+
+// prototypes
+int main(int argc, char* argv[], char* envp[]);
+
+/*!
+ * \brief Class that encapsulates the elftosb tool.
+ *
+ * A single global logger instance is created during object construction. It is
+ * never freed because we need it up to the last possible minute, when an
+ * exception could be thrown.
+ */
+class elftosbTool
+{
+protected:
+ //! Supported chip families.
+ enum chip_family_t
+ {
+ k37xxFamily, //!< 37xx series.
+ kMX28Family, //!< Catskills series.
+ };
+
+ /*!
+ * \brief A structure describing an entry in the table of chip family names.
+ */
+ struct FamilyNameTableEntry
+ {
+ const char * const name;
+ chip_family_t family;
+ };
+
+ //! \brief Table that maps from family name strings to chip family constants.
+ static const FamilyNameTableEntry kFamilyNameTable[];
+
+ int m_argc; //!< Number of command line arguments.
+ char ** m_argv; //!< String value for each command line argument.
+ StdoutLogger * m_logger; //!< Singleton logger instance.
+ string_vector_t m_keyFilePaths; //!< Paths to OTP key files.
+ string_vector_t m_positionalArgs; //!< Arguments coming after explicit options.
+ bool m_isVerbose; //!< Whether the verbose flag was turned on.
+ bool m_useDefaultKey; //!< Include a default (zero) crypto key.
+ const char * m_commandFilePath; //!< Path to the elftosb command file.
+ const char * m_outputFilePath; //!< Path to the output .sb file.
+ const char * m_searchPath; //!< Optional search path for input files.
+ elftosb::version_t m_productVersion; //!< Product version specified on command line.
+ elftosb::version_t m_componentVersion; //!< Component version specified on command line.
+ bool m_productVersionSpecified; //!< True if the product version was specified on the command line.
+ bool m_componentVersionSpecified; //!< True if the component version was specified on the command line.
+ chip_family_t m_family; //!< Chip family that the output file is formatted for.
+ elftosb::ConversionController m_controller; //!< Our conversion controller instance.
+
+public:
+ /*!
+ * Constructor.
+ *
+ * Creates the singleton logger instance.
+ */
+ elftosbTool(int argc, char * argv[])
+ : m_argc(argc),
+ m_argv(argv),
+ m_logger(0),
+ m_keyFilePaths(),
+ m_positionalArgs(),
+ m_isVerbose(false),
+ m_useDefaultKey(false),
+ m_commandFilePath(NULL),
+ m_outputFilePath(NULL),
+ m_searchPath(NULL),
+ m_productVersion(),
+ m_componentVersion(),
+ m_productVersionSpecified(false),
+ m_componentVersionSpecified(false),
+ m_family(k37xxFamily),
+ m_controller()
+ {
+ // create logger instance
+ m_logger = new StdoutLogger();
+ m_logger->setFilterLevel(Logger::INFO);
+ Log::setLogger(m_logger);
+ }
+
+ /*!
+ * Destructor.
+ */
+ ~elftosbTool()
+ {
+ }
+
+ /*!
+ * \brief Searches the family name table.
+ *
+ * \retval true The \a name was found in the table, and \a family is valid.
+ * \retval false No matching family name was found. The \a family argument is not modified.
+ */
+ bool lookupFamilyName(const char * name, chip_family_t * family)
+ {
+ // Create a local read-write copy of the argument string.
+ std::string familyName(name);
+
+ // Convert the argument string to lower case for case-insensitive comparison.
+ for (int n=0; n < familyName.length(); n++)
+ {
+ familyName[n] = tolower(familyName[n]);
+ }
+
+ // Exit the loop if we hit the NULL terminator entry.
+ const FamilyNameTableEntry * entry = &kFamilyNameTable[0];
+ for (; entry->name; entry++)
+ {
+ // Compare lowercased name with the table entry.
+ if (familyName == entry->name)
+ {
+ *family = entry->family;
+ return true;
+ }
+ }
+
+ // Failed to find a matching name.
+ return false;
+ }
+
+ /*!
+ * Reads the command line options passed into the constructor.
+ *
+ * This method can return a return code to its caller, which will cause the
+ * tool to exit immediately with that return code value. Normally, though, it
+ * will return -1 to signal that the tool should continue to execute and
+ * all options were processed successfully.
+ *
+ * The Options class is used to parse command line options. See
+ * #k_optionsDefinition for the list of options and #k_usageText for the
+ * descriptive help for each option.
+ *
+ * \retval -1 The options were processed successfully. Let the tool run normally.
+ * \return A zero or positive result is a return code value that should be
+ * returned from the tool as it exits immediately.
+ */
+ int processOptions()
+ {
+ Options options(*m_argv, k_optionsDefinition);
+ OptArgvIter iter(--m_argc, ++m_argv);
+
+ // process command line options
+ int optchar;
+ const char * optarg;
+ while (optchar = options(iter, optarg))
+ {
+ switch (optchar)
+ {
+ case '?':
+ printUsage(options);
+ return 0;
+
+ case 'v':
+ printf("%s %s\n%s\n", k_toolName, k_version, k_copyright);
+ return 0;
+
+ case 'f':
+ if (!lookupFamilyName(optarg, &m_family))
+ {
+ Log::log(Logger::ERROR, "error: unknown chip family '%s'\n", optarg);
+ printUsage(options);
+ return 0;
+ }
+ break;
+
+ case 'c':
+ m_commandFilePath = optarg;
+ break;
+
+ case 'o':
+ m_outputFilePath = optarg;
+ break;
+
+ case 'P':
+ m_productVersion.set(optarg);
+ m_productVersionSpecified = true;
+ break;
+
+ case 'C':
+ m_componentVersion.set(optarg);
+ m_componentVersionSpecified = true;
+ break;
+
+ case 'k':
+ m_keyFilePaths.push_back(optarg);
+ break;
+
+ case 'z':
+ m_useDefaultKey = true;
+ break;
+
+ case 'D':
+ overrideVariable(optarg);
+ break;
+
+ case 'O':
+ overrideOption(optarg);
+ break;
+
+ case 'd':
+ Log::getLogger()->setFilterLevel(Logger::DEBUG);
+ break;
+
+ case 'q':
+ Log::getLogger()->setFilterLevel(Logger::WARNING);
+ break;
+
+ case 'V':
+ m_isVerbose = true;
+ break;
+
+ case 'p':
+ {
+ std::string newSearchPath(optarg);
+ PathSearcher::getGlobalSearcher().addSearchPath(newSearchPath);
+ break;
+ }
+
+ default:
+ Log::log(Logger::ERROR, "error: unrecognized option\n\n");
+ printUsage(options);
+ return 0;
+ }
+ }
+
+ // handle positional args
+ if (iter.index() < m_argc)
+ {
+ Log::SetOutputLevel leveler(Logger::DEBUG);
+ Log::log("positional args:\n");
+ int i;
+ for (i = iter.index(); i < m_argc; ++i)
+ {
+ Log::log("%d: %s\n", i - iter.index(), m_argv[i]);
+ m_positionalArgs.push_back(m_argv[i]);
+ }
+ }
+
+ // all is well
+ return -1;
+ }
+
+ /*!
+ * Prints help for the tool.
+ */
+ void printUsage(Options & options)
+ {
+ options.usage(std::cout, "files...");
+ printf(k_usageText, k_toolName);
+ }
+
+ /*!
+ * \brief Core of the tool.
+ *
+ * Calls processOptions() to handle command line options before performing the
+ * real work the tool does.
+ */
+ int run()
+ {
+ try
+ {
+ // read command line options
+ int result;
+ if ((result = processOptions()) != -1)
+ {
+ return result;
+ }
+
+ // set verbose logging
+ setVerboseLogging();
+
+ // check argument values
+ checkArguments();
+
+ // set up the controller
+ m_controller.setCommandFilePath(m_commandFilePath);
+
+ // add external paths to controller
+ string_vector_t::iterator it = m_positionalArgs.begin();
+ for (; it != m_positionalArgs.end(); ++it)
+ {
+ m_controller.addExternalFilePath(*it);
+ }
+
+ // run conversion
+ convert();
+ }
+ catch (std::exception & e)
+ {
+ Log::log(Logger::ERROR, "error: %s\n", e.what());
+ return 1;
+ }
+ catch (...)
+ {
+ Log::log(Logger::ERROR, "error: unexpected exception\n");
+ return 1;
+ }
+
+ return 0;
+ }
+
+ /*!
+ * \brief Validate arguments that can be checked.
+ * \exception std::runtime_error Thrown if an argument value fails to pass validation.
+ */
+ void checkArguments()
+ {
+ if (m_commandFilePath == NULL)
+ {
+ throw std::runtime_error("no command file was specified");
+ }
+ if (m_outputFilePath == NULL)
+ {
+ throw std::runtime_error("no output file was specified");
+ }
+ }
+
+ /*!
+ * \brief Turns on verbose logging.
+ */
+ void setVerboseLogging()
+ {
+ if (m_isVerbose)
+ {
+ // verbose only affects the INFO and DEBUG filter levels
+ // if the user has selected quiet mode, it overrides verbose
+ switch (Log::getLogger()->getFilterLevel())
+ {
+ case Logger::INFO:
+ Log::getLogger()->setFilterLevel(Logger::INFO2);
+ break;
+ case Logger::DEBUG:
+ Log::getLogger()->setFilterLevel(Logger::DEBUG2);
+ break;
+ }
+ }
+ }
+
+ /*!
+ * \brief Returns the integer value for a string.
+ *
+ * Metric multiplier prefixes are supported.
+ */
+ uint32_t parseIntValue(const char * value)
+ {
+ // Accept 'true'/'yes' and 'false'/'no' as integer values.
+ if ((strcmp(value, "true") == 0) || (strcmp(value, "yes") == 0))
+ {
+ return 1;
+ }
+ else if ((strcmp(value, "false") == 0) || (strcmp(value, "no") == 0))
+ {
+ return 0;
+ }
+
+ uint32_t intValue = strtoul(value, NULL, 0);
+ unsigned multiplier;
+ switch (value[strlen(value) - 1])
+ {
+ case 'G':
+ multiplier = 1024 * 1024 * 1024;
+ break;
+ case 'M':
+ multiplier = 1024 * 1024;
+ break;
+ case 'K':
+ multiplier = 1024;
+ break;
+ default:
+ multiplier = 1;
+ }
+ intValue *= multiplier;
+ return intValue;
+ }
+
+ /*!
+ * \brief Parses the -D option to override a constant value.
+ */
+ void overrideVariable(const char * optarg)
+ {
+ // split optarg into two strings
+ std::string constName(optarg);
+ int i;
+ for (i=0; i < strlen(optarg); ++i)
+ {
+ if (optarg[i] == '=')
+ {
+ constName.resize(i++);
+ break;
+ }
+ }
+
+ uint32_t constValue = parseIntValue(&optarg[i]);
+
+ elftosb::EvalContext & context = m_controller.getEvalContext();
+ context.setVariable(constName, constValue);
+ context.lockVariable(constName);
+ }
+
+ /*!
+ * \brief
+ */
+ void overrideOption(const char * optarg)
+ {
+ // split optarg into two strings
+ std::string optionName(optarg);
+ int i;
+ for (i=0; i < strlen(optarg); ++i)
+ {
+ if (optarg[i] == '=')
+ {
+ optionName.resize(i++);
+ break;
+ }
+ }
+
+ // handle quotes for option value
+ const char * valuePtr = &optarg[i];
+ bool isString = false;
+ int len;
+ if (valuePtr[0] == '"')
+ {
+ // remember that the value is a string and get rid of the opening quote
+ isString = true;
+ valuePtr++;
+
+ // remove trailing quote if present
+ len = strlen(valuePtr);
+ if (valuePtr[len] == '"')
+ {
+ len--;
+ }
+ }
+
+ elftosb::Value * value;
+ if (isString)
+ {
+ std::string stringValue(valuePtr);
+ stringValue.resize(len); // remove trailing quote
+ value = new elftosb::StringValue(stringValue);
+ }
+ else
+ {
+ value = new elftosb::IntegerValue(parseIntValue(valuePtr));
+ }
+
+ // Set and lock the option in the controller
+ m_controller.setOption(optionName, value);
+ m_controller.lockOption(optionName);
+ }
+
+ /*!
+ * \brief Do the conversion.
+ * \exception std::runtime_error This exception is thrown if the conversion controller does
+ * not produce a boot image, or if the output file cannot be opened. Other errors
+ * internal to the conversion controller may also produce this exception.
+ */
+ void convert()
+ {
+ // create a generator for the chosen chip family
+ smart_ptr<elftosb::BootImageGenerator> generator;
+ switch (m_family)
+ {
+ case k37xxFamily:
+ generator = new elftosb::EncoreBootImageGenerator;
+ elftosb::g_enableHABSupport = false;
+ break;
+
+ case kMX28Family:
+ generator = new elftosb::EncoreBootImageGenerator;
+ elftosb::g_enableHABSupport = true;
+ break;
+ }
+
+ // process input and get a boot image
+ m_controller.run();
+ smart_ptr<elftosb::BootImage> image = m_controller.generateOutput(generator);
+ if (!image)
+ {
+ throw std::runtime_error("failed to produce output!");
+ }
+
+ // set version numbers if they were provided on the command line
+ if (m_productVersionSpecified)
+ {
+ image->setProductVersion(m_productVersion);
+ }
+ if (m_componentVersionSpecified)
+ {
+ image->setComponentVersion(m_componentVersion);
+ }
+
+ // special handling for each family
+ switch (m_family)
+ {
+ case k37xxFamily:
+ case kMX28Family:
+ {
+ // add OTP keys
+ elftosb::EncoreBootImage * encoreImage = dynamic_cast<elftosb::EncoreBootImage*>(image.get());
+ if (encoreImage)
+ {
+ // add keys
+ addCryptoKeys(encoreImage);
+
+ // print debug image
+ encoreImage->debugPrint();
+ }
+ break;
+ }
+ }
+
+ // write output
+ std::ofstream outputStream(m_outputFilePath, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);
+ if (outputStream.is_open())
+ {
+ image->writeToStream(outputStream);
+ }
+ else
+ {
+ throw std::runtime_error(format_string("could not open output file %s", m_outputFilePath));
+ }
+ }
+
+ /*!
+ * \brief
+ */
+ void addCryptoKeys(elftosb::EncoreBootImage * encoreImage)
+ {
+ string_vector_t::iterator it = m_keyFilePaths.begin();
+ for (; it != m_keyFilePaths.end(); ++it)
+ {
+ std::string & keyPath = *it;
+
+ std::string actualPath;
+ bool found = PathSearcher::getGlobalSearcher().search(keyPath, PathSearcher::kFindFile, true, actualPath);
+ if (!found)
+ {
+ throw std::runtime_error(format_string("unable to find key file %s\n", keyPath.c_str()));
+ }
+
+ std::ifstream keyStream(actualPath.c_str(), std::ios_base::in);
+ if (!keyStream.is_open())
+ {
+ throw std::runtime_error(format_string("unable to read key file %s\n", keyPath.c_str()));
+ }
+ keyStream.seekg(0);
+
+ try
+ {
+ // read as many keys as possible from the stream
+ while (true)
+ {
+ AESKey<128> key(keyStream);
+ encoreImage->addKey(key);
+
+ // dump key bytes
+ dumpKey(key);
+ }
+ }
+ catch (...)
+ {
+ // ignore the exception -- there are just no more keys in the stream
+ }
+ }
+
+ // add the default key of all zero bytes if requested
+ if (m_useDefaultKey)
+ {
+ AESKey<128> defaultKey;
+ encoreImage->addKey(defaultKey);
+ }
+ }
+
+ /*!
+ * \brief Write the value of each byte of the \a key to the log.
+ */
+ void dumpKey(const AESKey<128> & key)
+ {
+ // dump key bytes
+ Log::log(Logger::DEBUG, "key bytes: ");
+ AESKey<128>::key_t the_key;
+ key.getKey(&the_key);
+ int q;
+ for (q=0; q<16; q++)
+ {
+ Log::log(Logger::DEBUG, "%02x ", the_key[q]);
+ }
+ Log::log(Logger::DEBUG, "\n");
+ }
+
+};
+
+const elftosbTool::FamilyNameTableEntry elftosbTool::kFamilyNameTable[] =
+ {
+ { "37xx", k37xxFamily },
+ { "377x", k37xxFamily },
+ { "378x", k37xxFamily },
+ { "mx23", k37xxFamily },
+ { "imx23", k37xxFamily },
+ { "i.mx23", k37xxFamily },
+ { "mx28", kMX28Family },
+ { "imx28", kMX28Family },
+ { "i.mx28", kMX28Family },
+
+ // Null terminator entry.
+ { NULL, k37xxFamily }
+ };
+
+/*!
+ * Main application entry point. Creates an sbtool instance and lets it take over.
+ */
+int main(int argc, char* argv[], char* envp[])
+{
+ try
+ {
+ return elftosbTool(argc, argv).run();
+ }
+ catch (...)
+ {
+ Log::log(Logger::ERROR, "error: unexpected exception\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+