]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - tools/elftosb/sbtool/sbtool.cpp
Unified codebase for TX28, TX48, TX51, TX53
[karo-tx-uboot.git] / tools / elftosb / sbtool / sbtool.cpp
1 /*
2  * File:        sbtool.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 <stdio.h>
15 #include "options.h"
16 #include "EncoreBootImage.h"
17 #include "smart_ptr.h"
18 #include "Logging.h"
19 #include "EncoreBootImageReader.h"
20 #include "format_string.h"
21
22 using namespace elftosb;
23
24 //! The tool's name.
25 const char k_toolName[] = "sbtool";
26
27 //! Current version number for the tool.
28 const char k_version[] = "1.1.4";
29
30 //! Copyright string.
31 const char k_copyright[] = "Copyright (c) 2006-2010 Freescale Semiconductor, Inc.\nAll rights reserved.";
32
33 //! Definition of command line options.
34 static const char * k_optionsDefinition[] = {
35         "?|help",
36         "v|version",
37         "k:key <file>",
38         "z|zero-key",
39         "x:extract",
40         "b|binary",
41         "d|debug",
42         "q|quiet",
43         "V|verbose",
44         NULL
45 };
46
47 //! Help string.
48 const char k_usageText[] = "\nOptions:\n\
49   -?/--help                    Show this help\n\
50   -v/--version                 Display tool version\n\
51   -k/--key <file>              Add OTP key used for decryption\n\
52   -z/--zero-key                Add default key of all zeroes\n\
53   -x/--extract <index>         Extract section number <index>\n\
54   -b/--binary                  Extract section data as binary\n\
55   -d/--debug                   Enable debug output\n\
56   -q/--quiet                   Output only warnings and errors\n\
57   -V/--verbose                 Print extra detailed log information\n\n";
58
59 //! An array of strings.
60 typedef std::vector<std::string> string_vector_t;
61
62 // prototypes
63 int main(int argc, char* argv[], char* envp[]);
64
65 /*!
66  * \brief Class that encapsulates the sbtool interface.
67  *
68  * A single global logger instance is created during object construction. It is
69  * never freed because we need it up to the last possible minute, when an
70  * exception could be thrown.
71  */
72 class sbtool
73 {
74 protected:
75         int m_argc;                                                     //!< Number of command line arguments.
76         char ** m_argv;                                         //!< String value for each command line argument.
77         StdoutLogger * m_logger;                        //!< Singleton logger instance.
78         string_vector_t m_keyFilePaths;         //!< Paths to OTP key files.
79         string_vector_t m_positionalArgs;       //!< Arguments coming after explicit options.
80         bool m_isVerbose;                                       //!< Whether the verbose flag was turned on.
81         bool m_useDefaultKey;                                   //!< Include a default (zero) crypto key.
82         bool m_doExtract;                                       //!< True if extract mode is on.
83         unsigned m_sectionIndex;                                //!< Index of section to extract.
84         bool m_extractBinary;                           //!< True if extraction output is binary, false for hex.
85         smart_ptr<EncoreBootImageReader> m_reader;      //!< Boot image reader object.
86         
87 public:
88         /*!
89          * Constructor.
90          *
91          * Creates the singleton logger instance.
92          */
93         sbtool(int argc, char * argv[])
94         :       m_argc(argc),
95                 m_argv(argv),
96                 m_logger(0),
97                 m_keyFilePaths(),
98                 m_positionalArgs(),
99                 m_isVerbose(false),
100                 m_useDefaultKey(false),
101                 m_doExtract(false),
102                 m_sectionIndex(0),
103                 m_extractBinary(false),
104                 m_reader()
105         {
106                 // create logger instance
107                 m_logger = new StdoutLogger();
108                 m_logger->setFilterLevel(Logger::INFO);
109                 Log::setLogger(m_logger);
110         }
111         
112         /*!
113          * Destructor.
114          */
115         ~sbtool()
116         {
117         }
118         
119         /*!
120          * Reads the command line options passed into the constructor.
121          *
122          * This method can return a return code to its caller, which will cause the
123          * tool to exit immediately with that return code value. Normally, though, it
124          * will return -1 to signal that the tool should continue to execute and
125          * all options were processed successfully.
126          *
127          * The Options class is used to parse command line options. See
128          * #k_optionsDefinition for the list of options and #k_usageText for the
129          * descriptive help for each option.
130          *
131          * \retval -1 The options were processed successfully. Let the tool run normally.
132          * \return A zero or positive result is a return code value that should be
133          *              returned from the tool as it exits immediately.
134          */
135         int processOptions()
136         {
137                 Options options(*m_argv, k_optionsDefinition);
138                 OptArgvIter iter(--m_argc, ++m_argv);
139                 
140                 // process command line options
141                 int optchar;
142                 const char * optarg;
143                 while (optchar = options(iter, optarg))
144                 {
145                         switch (optchar)
146                         {
147                                 case '?':
148                                         printUsage(options);
149                                         return 0;
150                                 
151                                 case 'v':
152                                         printf("%s %s\n%s\n", k_toolName, k_version, k_copyright);
153                                         return 0;
154                                         
155                                 case 'k':
156                                         m_keyFilePaths.push_back(optarg);
157                                         break;
158                                 
159                                 case 'z':
160                                         m_useDefaultKey = true;
161                                         break;
162                                 
163                                 case 'x':
164                                         m_doExtract = true;
165                                         m_sectionIndex = strtoul(optarg, NULL, 0);
166                                         break;
167                                 
168                                 case 'b':
169                                         m_extractBinary = true;
170                                         Log::getLogger()->setFilterLevel(Logger::WARNING);
171                                         break;
172                                         
173                                 case 'd':
174                                         Log::getLogger()->setFilterLevel(Logger::DEBUG);
175                                         break;
176                                         
177                                 case 'q':
178                                         Log::getLogger()->setFilterLevel(Logger::WARNING);
179                                         break;
180                                         
181                                 case 'V':
182                                         m_isVerbose = true;
183                                         break;
184                                 
185                                 default:
186                                         Log::log(Logger::ERROR, "error: unrecognized option\n\n");
187                                         printUsage(options);
188                                         return 1;
189                         }
190                 }
191                 
192                 // handle positional args
193                 if (iter.index() < m_argc)
194                 {
195 //                      Log::SetOutputLevel leveler(Logger::DEBUG);
196 //                      Log::log("positional args:\n");
197                         int i;
198                         for (i = iter.index(); i < m_argc; ++i)
199                         {
200 //                              Log::log("%d: %s\n", i - iter.index(), m_argv[i]);
201                                 m_positionalArgs.push_back(m_argv[i]);
202                         }
203                 }
204                 
205                 // all is well
206                 return -1;
207         }
208
209         /*!
210          * Prints help for the tool.
211          */
212         void printUsage(Options & options)
213         {
214                 options.usage(std::cout, "sb-file");
215                 printf(k_usageText, k_toolName);
216         }
217         
218         /*!
219          * Core of the tool. Calls processOptions() to handle command line options
220          * before performing the real work the tool does.
221          */
222         int run()
223         {
224                 try
225                 {
226                         // read command line options
227                         int result;
228                         if ((result = processOptions()) != -1)
229                         {
230                                 return result;
231                         }
232                         
233                         // set verbose logging
234                         setVerboseLogging();
235                         
236                         // make sure a file was provided
237                         if (m_positionalArgs.size() < 1)
238                         {
239                                 throw std::runtime_error("no sb file path was provided");
240                         }
241                         
242                         // read the boot image
243                         readBootImage();
244                 }
245                 catch (std::exception & e)
246                 {
247                         Log::log(Logger::ERROR, "error: %s\n", e.what());
248                         return 1;
249                 }
250                 catch (...)
251                 {
252                         Log::log(Logger::ERROR, "error: unexpected exception\n");
253                         return 1;
254                 }
255                 
256                 return 0;
257         }
258         
259         /*!
260          * \brief Turns on verbose logging.
261          */
262         void setVerboseLogging()
263         {
264                 if (m_isVerbose)
265                 {
266                         // verbose only affects the INFO and DEBUG filter levels
267                         // if the user has selected quiet mode, it overrides verbose
268                         switch (Log::getLogger()->getFilterLevel())
269                         {
270                                 case Logger::INFO:
271                                         Log::getLogger()->setFilterLevel(Logger::INFO2);
272                                         break;
273                                 case Logger::DEBUG:
274                                         Log::getLogger()->setFilterLevel(Logger::DEBUG2);
275                                         break;
276                         }
277                 }
278         }
279         
280         /*!
281          * \brief Opens and reads the boot image identified on the command line.
282          * \pre At least one position argument must be present.
283          */
284         void readBootImage()
285         {
286                 Log::SetOutputLevel infoLevel(Logger::INFO);
287                 
288                 // open the sb file stream
289                 std::ifstream sbStream(m_positionalArgs[0].c_str(), std::ios_base::binary | std::ios_base::in);
290                 if (!sbStream.is_open())
291                 {
292                         throw std::runtime_error("failed to open input file");
293                 }
294                 
295                 // create the boot image reader
296                 m_reader = new EncoreBootImageReader(sbStream);
297                 
298                 // read image header
299                 m_reader->readImageHeader();
300                 const EncoreBootImage::boot_image_header_t & header = m_reader->getHeader();
301                 if (header.m_majorVersion > 1)
302                 {
303                         throw std::runtime_error(format_string("boot image format version is too new (format version %d.%d)\n", header.m_majorVersion, header.m_minorVersion));
304                 }
305                 Log::log("---- Boot image header ----\n");
306                 dumpImageHeader(header);
307                 
308                 // compute SHA-1 over image header and test against the digest stored in the header
309                 sha1_digest_t computedDigest;
310                 m_reader->computeHeaderDigest(computedDigest);
311                 if (compareDigests(computedDigest, m_reader->getHeader().m_digest))
312                 {
313                         Log::log("Header digest is correct.\n");
314                 }
315                 else
316                 {
317                         Log::log(Logger::WARNING, "warning: stored SHA-1 header digest does not match the actual header digest\n");
318                         Log::log(Logger::WARNING, "\n---- Actual SHA-1 digest of image header ----\n");
319                         logHexArray(Logger::WARNING, (uint8_t *)&computedDigest, sizeof(computedDigest));
320                 }
321                 
322                 // read the section table
323                 m_reader->readSectionTable();
324                 const EncoreBootImageReader::section_array_t & sectionTable = m_reader->getSections();
325                 EncoreBootImageReader::section_array_t::const_iterator it = sectionTable.begin();
326                 Log::log("\n---- Section table ----\n");
327                 unsigned n = 0;
328                 for (; it != sectionTable.end(); ++it, ++n)
329                 {
330                         const EncoreBootImage::section_header_t & sectionHeader = *it;
331                         Log::log("Section %d:\n", n);
332                         dumpSectionHeader(sectionHeader);
333                 }
334                 
335                 // read the key dictionary
336                 // XXX need to support multiple keys, not just the first!
337                 if (m_reader->isEncrypted())
338                 {
339                         Log::log("\n---- Key dictionary ----\n");
340                         if (m_keyFilePaths.size() > 0 || m_useDefaultKey)
341                         {
342                                 if (m_keyFilePaths.size() > 0)
343                                 {
344                                         std::string & keyPath = m_keyFilePaths[0];
345                                         std::ifstream keyStream(keyPath.c_str(), std::ios_base::binary | std::ios_base::in);
346                                         if (!keyStream.is_open())
347                                         {
348                                                 Log::log(Logger::WARNING, "warning: unable to read key %s\n", keyPath.c_str());
349                                         }
350                                         AESKey<128> kek(keyStream);
351                                 
352                                         // search for this key in the key dictionary
353                                         if (!m_reader->readKeyDictionary(kek))
354                                         {
355                                                 throw std::runtime_error("the provided key is not valid for this encrypted boot image");
356                                         }
357                                         
358                                         Log::log("\nKey %s was found in key dictionary.\n", keyPath.c_str());
359                                 }
360                                 else
361                                 {
362                                         // default key of zero, overriden if -k was used
363                                         AESKey<128> defaultKek;
364                                 
365                                         // search for this key in the key dictionary
366                                         if (!m_reader->readKeyDictionary(defaultKek))
367                                         {
368                                                 throw std::runtime_error("the default key is not valid for this encrypted boot image");
369                                         }
370                                         
371                                         Log::log("\nDefault key was found in key dictionary.\n");
372                                 }
373                                 
374                                 // print out the DEK
375                                 AESKey<128> dek = m_reader->getKey();
376                                 std::stringstream dekStringStream(std::ios_base::in | std::ios_base::out);
377                                 dek.writeToStream(dekStringStream);
378                                 std::string dekString = dekStringStream.str();
379 //                              Log::log("\nData encryption key: %s\n", dekString.c_str());
380                                 Log::log("\nData encryption key:\n");
381                                 logHexArray(Logger::INFO, (const uint8_t *)&dek.getKey(), sizeof(AESKey<128>::key_t));
382                         }
383                         else
384                         {
385                                 throw std::runtime_error("the image is encrypted but no key was provided");
386                         }
387                 }
388                 
389                 // read the SHA-1 digest over the entire image. this is done after
390                 // reading the key dictionary because the digest is encrypted in
391                 // encrypted boot images.
392                 m_reader->readImageDigest();
393                 const sha1_digest_t & embeddedDigest = m_reader->getDigest();
394                 Log::log("\n---- SHA-1 digest of entire image ----\n");
395                 logHexArray(Logger::INFO, (const uint8_t *)&embeddedDigest, sizeof(embeddedDigest));
396                 
397                 // compute the digest over the entire image and compare
398                 m_reader->computeImageDigest(computedDigest);
399                 if (compareDigests(computedDigest, embeddedDigest))
400                 {
401                         Log::log("Image digest is correct.\n");
402                 }
403                 else
404                 {
405                         Log::log(Logger::WARNING, "warning: stored SHA-1 digest does not match the actual digest\n");
406                         Log::log(Logger::WARNING, "\n---- Actual SHA-1 digest of entire image ----\n");
407                         logHexArray(Logger::WARNING, (uint8_t *)&computedDigest, sizeof(computedDigest));
408                 }
409                 
410                 // read the boot tags
411                 m_reader->readBootTags();
412                 Log::log("\n---- Boot tags ----\n");
413                 unsigned block = header.m_firstBootTagBlock;
414                 const EncoreBootImageReader::boot_tag_array_t & tags = m_reader->getBootTags();
415                 EncoreBootImageReader::boot_tag_array_t::const_iterator tagIt = tags.begin();
416                 for (n = 0; tagIt != tags.end(); ++tagIt, ++n)
417                 {
418                         const EncoreBootImage::boot_command_t & command = *tagIt;
419                         Log::log("%04u: @ block %06u | id=0x%08x | length=%06u | flags=0x%08x\n", n, block, command.m_address, command.m_count, command.m_data);
420                                         
421                         if (command.m_data & EncoreBootImage::ROM_SECTION_BOOTABLE)
422                         {
423                                 Log::log("        0x1 = ROM_SECTION_BOOTABLE\n");
424                         }
425                         
426                         if (command.m_data & EncoreBootImage::ROM_SECTION_CLEARTEXT)
427                         {
428                                 Log::log("        0x2 = ROM_SECTION_CLEARTEXT\n");
429                         }
430                         
431                         block += command.m_count + 1;
432                 }
433         
434         // now read all of the sections
435                 Log::log(Logger::INFO2, "\n---- Sections ----\n");
436         for (n = 0; n < header.m_sectionCount; ++n)
437         {
438             EncoreBootImage::Section * section = m_reader->readSection(n);
439             section->debugPrint();
440                         
441                         // Check if this is the section the user wants to extract.
442                         if (m_doExtract && n == m_sectionIndex)
443                         {
444                                 extractSection(section);
445                         }
446         }
447         }
448         
449         //! \brief Dumps the contents of a section to stdout.
450         //!
451         //! If #m_extractBinary is true then the contents are written as
452         //! raw binary to stdout. Otherwise the data is formatted using
453         //! logHexArray().
454         void extractSection(EncoreBootImage::Section * section)
455         {
456                 // Allocate buffer to hold section data.
457                 unsigned blockCount = section->getBlockCount();
458                 unsigned dataLength = sizeOfCipherBlocks(blockCount);
459                 smart_array_ptr<uint8_t> buffer = new uint8_t[dataLength];
460                 cipher_block_t * data = reinterpret_cast<cipher_block_t *>(buffer.get());
461                 
462                 // Read section data into the buffer one block at a time.
463                 unsigned offset;
464                 for (offset = 0; offset < blockCount;)
465                 {
466                         unsigned blocksRead = section->getBlocks(offset, 1, data);
467                         offset += blocksRead;
468                         data += blocksRead;
469                 }
470                 
471                 // Print header.
472                 Log::log(Logger::INFO, "\nSection %d contents:\n", m_sectionIndex);
473                 
474                 // Now dump the extracted data to stdout.
475                 if (m_extractBinary)
476                 {
477                         if (fwrite(buffer.get(), 1, dataLength, stdout) != dataLength)
478                         {
479                                 throw std::runtime_error(format_string("failed to write data to stdout (%d)", ferror(stdout)));
480                         }
481                 }
482                 else
483                 {
484                         // Use the warning log level so the data will be visible even in quiet mode.
485                         logHexArray(Logger::WARNING, buffer, dataLength);
486                 }
487         }
488         
489         //! \brief Compares two SHA-1 digests and returns whether they are equal.
490         //! \retval true The two digests are equal.
491         //! \retval false The \a a and \a b digests are different from each other.
492         bool compareDigests(const sha1_digest_t & a, const sha1_digest_t & b)
493         {
494                 return memcmp(a, b, sizeof(sha1_digest_t)) == 0;
495         }
496         
497         /*
498         struct boot_image_header_t
499         {
500                 union
501                 {
502                         sha1_digest_t m_digest;         //!< SHA-1 digest of image header. Also used as the crypto IV.
503                         struct
504                         {
505                                 cipher_block_t m_iv;    //!< The first four bytes of the digest form the initialization vector.
506                                 uint8_t m_extra[4];             //!< The leftover top four bytes of the SHA-1 digest.
507                         };
508                 };
509                 uint8_t m_signature[4];                 //!< 'STMP', see #ROM_IMAGE_HEADER_SIGNATURE.
510                 uint16_t m_version;                             //!< Version of the boot image format, see #ROM_BOOT_IMAGE_VERSION.
511                 uint16_t m_flags;                               //!< Flags or options associated with the entire image.
512                 uint32_t m_imageBlocks;                 //!< Size of entire image in blocks.
513                 uint32_t m_firstBootTagBlock;   //!< Offset from start of file to the first boot tag, in blocks.
514                 section_id_t m_firstBootableSectionID;  //!< ID of section to start booting from.
515                 uint16_t m_keyCount;                    //!< Number of entries in DEK dictionary.
516                 uint16_t m_keyDictionaryBlock;  //!< Starting block number for the key dictionary.
517                 uint16_t m_headerBlocks;                //!< Size of this header, including this size word, in blocks.
518                 uint16_t m_sectionCount;                //!< Number of section headers in this table.
519                 uint16_t m_sectionHeaderSize;   //!< Size in blocks of a section header.
520                 uint8_t m_padding0[6];                  //!< Padding to align #m_timestamp to long word.
521                 uint64_t m_timestamp;                   //!< Timestamp when image was generated in microseconds since 1-1-2000.
522                 version_t m_productVersion;             //!< Product version.
523                 version_t m_componentVersion;   //!< Component version.
524                 uint16_t m_driveTag;
525                 uint8_t m_padding1[6];          //!< Padding to round up to next cipher block.
526         };
527         */
528         void dumpImageHeader(const EncoreBootImage::boot_image_header_t & header)
529         {
530                 version_t vers;
531
532                 Log::SetOutputLevel infoLevel(Logger::INFO);
533                 Log::log("Signature 1:           %c%c%c%c\n", header.m_signature[0], header.m_signature[1], header.m_signature[2], header.m_signature[3]);
534                 Log::log("Signature 2:           %c%c%c%c\n", header.m_signature2[0], header.m_signature2[1], header.m_signature2[2], header.m_signature2[3]);
535                 Log::log("Format version:        %d.%d\n", header.m_majorVersion, header.m_minorVersion);
536                 Log::log("Flags:                 0x%04x\n", header.m_flags);
537                 Log::log("Image blocks:          %u\n", header.m_imageBlocks);
538                 Log::log("First boot tag block:  %u\n", header.m_firstBootTagBlock);
539                 Log::log("First boot section ID: 0x%08x\n", header.m_firstBootableSectionID);
540                 Log::log("Key count:             %u\n", header.m_keyCount);
541                 Log::log("Key dictionary block:  %u\n", header.m_keyDictionaryBlock);
542                 Log::log("Header blocks:         %u\n", header.m_headerBlocks);
543                 Log::log("Section count:         %u\n", header.m_sectionCount);
544                 Log::log("Section header size:   %u\n", header.m_sectionHeaderSize);
545                 Log::log("Timestamp:             %llu\n", header.m_timestamp);
546                 vers = header.m_productVersion;
547                 vers.fixByteOrder();
548                 Log::log("Product version:       %x.%x.%x\n", vers.m_major, vers.m_minor, vers.m_revision);
549                 vers = header.m_componentVersion;
550                 vers.fixByteOrder();
551                 Log::log("Component version:     %x.%x.%x\n", vers.m_major, vers.m_minor, vers.m_revision);
552                 if (header.m_majorVersion == 1 && header.m_minorVersion >= 1)
553                 {
554                         Log::log("Drive tag:             0x%04x\n", header.m_driveTag);
555                 }
556                 Log::log("SHA-1 digest of header:\n");
557                 logHexArray(Logger::INFO, (uint8_t *)&header.m_digest, sizeof(header.m_digest));
558         }
559         
560         void dumpSectionHeader(const EncoreBootImage::section_header_t & header)
561         {
562                 Log::SetOutputLevel infoLevel(Logger::INFO);
563                 Log::log("    Identifier: 0x%x\n", header.m_tag);
564                 Log::log("    Offset:     %d block%s (%d bytes)\n", header.m_offset, header.m_offset!=1?"s":"", sizeOfCipherBlocks(header.m_offset));
565                 Log::log("    Length:     %d block%s (%d bytes)\n", header.m_length, header.m_length!=1?"s":"", sizeOfCipherBlocks(header.m_length));
566                 Log::log("    Flags:      0x%08x\n", header.m_flags);
567                 
568                 if (header.m_flags & EncoreBootImage::ROM_SECTION_BOOTABLE)
569                 {
570                         Log::log("                0x1 = ROM_SECTION_BOOTABLE\n");
571                 }
572                 
573                 if (header.m_flags & EncoreBootImage::ROM_SECTION_CLEARTEXT)
574                 {
575                         Log::log("                0x2 = ROM_SECTION_CLEARTEXT\n");
576                 }
577         }
578         
579         /*!
580          * \brief Log an array of bytes as hex.
581          */
582         void logHexArray(Logger::log_level_t level, const uint8_t * bytes, unsigned count)
583         {
584                 Log::SetOutputLevel leveler(level);
585
586                 unsigned i;
587                 for (i = 0; i < count; ++i, ++bytes)
588                 {
589                         if ((i % 16 == 0) && (i < count - 1))
590                         {
591                                 if (i != 0)
592                                 {
593                                         Log::log("\n");
594                                 }
595                                 Log::log("    0x%08x: ", i);
596                         }
597                         Log::log("%02x ", *bytes & 0xff);
598                 }
599                 
600                 Log::log("\n");
601         }
602
603 };
604
605 /*!
606  * Main application entry point. Creates an sbtool instance and lets it take over.
607  */
608 int main(int argc, char* argv[], char* envp[])
609 {
610         try
611         {
612                 return sbtool(argc, argv).run();
613         }
614         catch (...)
615         {
616                 Log::log(Logger::ERROR, "error: unexpected exception\n");
617                 return 1;
618         }
619
620         return 0;
621 }
622
623
624
625
626