]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - tools/elftosb/elftosb2/elftosb.cpp
merged tx6dl-devel into denx master branch
[karo-tx-uboot.git] / tools / elftosb / elftosb2 / elftosb.cpp
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