2 * File: EncoreBootImage.cpp
4 * Copyright (c) Freescale Semiconductor, Inc. All rights reserved.
5 * See included license file for license details.
8 #include "EncoreBootImage.h"
16 #include "RijndaelCBCMAC.h"
18 #include "EndianUtilities.h"
20 using namespace elftosb;
22 EncoreBootImage::EncoreBootImage()
30 EncoreBootImage::~EncoreBootImage()
32 // dispose of all sections
33 section_iterator_t it = beginSection();
34 for (; it != endSection(); ++it)
40 //! \exception std::runtime_error Raised if \a newSection has the same tag as a previously
42 void EncoreBootImage::addSection(Section * newSection)
44 // check for another section with this tag
45 section_iterator_t it = beginSection();
46 for (; it != endSection(); ++it)
48 if ((*it)->getIdentifier() == newSection->getIdentifier())
50 throw std::runtime_error("new section with non-unique tag");
54 // no conflicting section tags, so add it
55 m_sections.push_back(newSection);
57 // tell the image who owns it now
58 newSection->setImage(this);
61 EncoreBootImage::section_iterator_t EncoreBootImage::findSection(Section * section)
63 return std::find(beginSection(), endSection(), section);
66 void EncoreBootImage::setProductVersion(const version_t & version)
68 m_productVersion = version;
71 void EncoreBootImage::setComponentVersion(const version_t & version)
73 m_componentVersion = version;
76 //! \todo Optimize writing section data. Right now it only writes one block at a
77 //! time, which is of course quite slow (in relative terms).
78 //! \todo Refactor this into several different methods for writing each region
79 //! of the image. Use a context structure to keep track of shared data between
80 //! each of the methods.
81 //! \todo Refactor the section and boot tag writing code to only have a single
82 //! copy of the block writing and encryption loop.
83 void EncoreBootImage::writeToStream(std::ostream & stream)
85 // always generate the session key or DEK even if image is unencrypted
86 m_sessionKey.randomize();
88 // prepare to compute CBC-MACs with each KEK
90 smart_array_ptr<RijndaelCBCMAC> macs(0);
93 macs = new RijndaelCBCMAC[m_keys.size()];
94 for (i=0; i < m_keys.size(); ++i)
96 RijndaelCBCMAC mac(m_keys[i]);
97 (macs.get())[i] = mac;
101 // prepare to compute SHA-1 digest over entire image
105 // count of total blocks written to the file
106 unsigned fileBlocksWritten = 0;
108 // we need some pieces of the header down below
109 boot_image_header_t imageHeader;
110 prepareImageHeader(imageHeader);
112 // write plaintext header
115 assert(sizeOfPaddingForCipherBlocks(sizeof(boot_image_header_t)) == 0);
116 stream.write(reinterpret_cast<char *>(&imageHeader), sizeof(imageHeader));
117 fileBlocksWritten += numberOfCipherBlocks(sizeof(imageHeader));
119 // update CBC-MAC over image header
122 for (i=0; i < m_keys.size(); ++i)
124 (macs.get())[i].update(reinterpret_cast<uint8_t *>(&imageHeader), sizeof(imageHeader));
129 hash.Update(reinterpret_cast<uint8_t *>(&imageHeader), sizeof(imageHeader));
132 // write plaintext section table
134 section_iterator_t it = beginSection();
135 for (; it != endSection(); ++it)
137 Section * section = *it;
139 // write header for this section
140 assert(sizeOfPaddingForCipherBlocks(sizeof(section_header_t)) == 0);
141 section_header_t sectionHeader;
142 section->fillSectionHeader(sectionHeader);
143 stream.write(reinterpret_cast<char *>(§ionHeader), sizeof(sectionHeader));
144 fileBlocksWritten += numberOfCipherBlocks(sizeof(sectionHeader));
146 // update CBC-MAC over this entry
149 for (i=0; i < m_keys.size(); ++i)
151 (macs.get())[i].update(reinterpret_cast<uint8_t *>(§ionHeader), sizeof(sectionHeader));
156 hash.Update(reinterpret_cast<uint8_t *>(§ionHeader), sizeof(sectionHeader));
160 // finished with the CBC-MAC
163 for (i=0; i < m_keys.size(); ++i)
165 (macs.get())[i].finalize();
169 // write key dictionary
172 key_iterator_t it = beginKeys();
173 for (i=0; it != endKeys(); ++it, ++i)
175 // write CBC-MAC result for this key, then update SHA-1
176 RijndaelCBCMAC & mac = (macs.get())[i];
177 const RijndaelCBCMAC::block_t & macResult = mac.getMAC();
178 stream.write(reinterpret_cast<const char *>(&macResult), sizeof(RijndaelCBCMAC::block_t));
179 hash.Update(reinterpret_cast<const uint8_t *>(&macResult), sizeof(RijndaelCBCMAC::block_t));
182 // encrypt DEK with this key, write it out, and update image digest
184 cipher.init(Rijndael::CBC, Rijndael::Encrypt, *it, Rijndael::Key16Bytes, imageHeader.m_iv);
185 AESKey<128>::key_t wrappedSessionKey;
186 cipher.blockEncrypt(m_sessionKey, sizeof(AESKey<128>::key_t) * 8, wrappedSessionKey);
187 stream.write(reinterpret_cast<char *>(&wrappedSessionKey), sizeof(wrappedSessionKey));
188 hash.Update(reinterpret_cast<uint8_t *>(&wrappedSessionKey), sizeof(wrappedSessionKey));
193 // write sections and boot tags
195 section_iterator_t it = beginSection();
196 for (; it != endSection(); ++it)
198 section_iterator_t itCopy = it;
199 bool isLastSection = (++itCopy == endSection());
201 Section * section = *it;
202 cipher_block_t block;
203 unsigned blockCount = section->getBlockCount();
204 unsigned blocksWritten = 0;
207 cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, imageHeader.m_iv);
209 // Compute the number of padding blocks needed to align the section. This first
210 // call to getPadBlockCountForOffset() passes an offset that excludes
211 // the boot tag for this section.
212 unsigned paddingBlocks = getPadBlockCountForSection(section, fileBlocksWritten);
214 // Insert nop commands as padding to align the start of the section, if
215 // the section has special alignment requirements.
217 while (paddingBlocks--)
219 blockCount = nop.getBlockCount();
221 while (blocksWritten < blockCount)
223 nop.getBlocks(blocksWritten, 1, &block);
227 // re-init after encrypt to update IV
228 cipher.blockEncrypt(block, sizeof(cipher_block_t) * 8, block);
229 cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, block);
232 stream.write(reinterpret_cast<char *>(&block), sizeof(cipher_block_t));
233 hash.Update(reinterpret_cast<uint8_t *>(&block), sizeof(cipher_block_t));
240 // reinit cipher for boot tag
241 cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, imageHeader.m_iv);
244 TagCommand tag(*section);
245 tag.setLast(isLastSection);
248 // If this isn't the last section, the tag needs to include any
249 // padding for the next section in its length, otherwise the ROM
250 // won't be able to find the next section's boot tag.
251 unsigned nextSectionOffset = fileBlocksWritten + section->getBlockCount() + 1;
252 tag.setSectionLength(section->getBlockCount() + getPadBlockCountForSection(*itCopy, nextSectionOffset));
254 blockCount = tag.getBlockCount();
256 while (blocksWritten < blockCount)
258 tag.getBlocks(blocksWritten, 1, &block);
262 // re-init after encrypt to update IV
263 cipher.blockEncrypt(block, sizeof(cipher_block_t) * 8, block);
264 cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, block);
267 stream.write(reinterpret_cast<char *>(&block), sizeof(cipher_block_t));
268 hash.Update(reinterpret_cast<uint8_t *>(&block), sizeof(cipher_block_t));
274 // reinit cipher for section data
275 cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, imageHeader.m_iv);
277 // write section data
278 blockCount = section->getBlockCount();
280 while (blocksWritten < blockCount)
282 section->getBlocks(blocksWritten, 1, &block);
284 // Only encrypt the section contents if the entire boot image is encrypted
285 // and the section doesn't have the "leave unencrypted" flag set. Even if the
286 // section is unencrypted the boot tag will remain encrypted.
287 if (isEncrypted() && !section->getLeaveUnencrypted())
289 // re-init after encrypt to update IV
290 cipher.blockEncrypt(block, sizeof(cipher_block_t) * 8, block);
291 cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, block);
294 stream.write(reinterpret_cast<char *>(&block), sizeof(cipher_block_t));
295 hash.Update(reinterpret_cast<uint8_t *>(&block), sizeof(cipher_block_t));
303 // write SHA-1 digest over entire image
305 // allocate enough room for digest and bytes to pad out to the next cipher block
306 const unsigned padBytes = sizeOfPaddingForCipherBlocks(sizeof(sha1_digest_t));
307 unsigned digestBlocksSize = sizeof(sha1_digest_t) + padBytes;
308 smart_array_ptr<uint8_t> digestBlocks = new uint8_t[digestBlocksSize];
310 hash.GetHash(digestBlocks.get());
312 // set the pad bytes to random values
313 RandomNumberGenerator rng;
314 rng.generateBlock(&(digestBlocks.get())[sizeof(sha1_digest_t)], padBytes);
316 // encrypt with session key
320 cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, imageHeader.m_iv);
321 cipher.blockEncrypt(digestBlocks.get(), digestBlocksSize * 8, digestBlocks.get());
324 // write to the stream
325 stream.write(reinterpret_cast<char *>(digestBlocks.get()), digestBlocksSize);
329 void EncoreBootImage::prepareImageHeader(boot_image_header_t & header)
331 // get identifier for the first bootable section
332 Section * firstBootSection = findFirstBootableSection();
333 section_id_t firstBootSectionID = 0;
334 if (firstBootSection)
336 firstBootSectionID = firstBootSection->getIdentifier();
339 // fill in header fields
340 header.m_signature[0] = 'S';
341 header.m_signature[1] = 'T';
342 header.m_signature[2] = 'M';
343 header.m_signature[3] = 'P';
344 header.m_majorVersion = ROM_BOOT_IMAGE_MAJOR_VERSION;
345 header.m_minorVersion = ROM_BOOT_IMAGE_MINOR_VERSION;
346 header.m_flags = ENDIAN_HOST_TO_LITTLE_U16(m_headerFlags);
347 header.m_imageBlocks = ENDIAN_HOST_TO_LITTLE_U32(getImageSize());
348 header.m_firstBootableSectionID = ENDIAN_HOST_TO_LITTLE_U32(firstBootSectionID);
349 header.m_keyCount = ENDIAN_HOST_TO_LITTLE_U16((uint16_t)m_keys.size());
350 header.m_headerBlocks = ENDIAN_HOST_TO_LITTLE_U16((uint16_t)numberOfCipherBlocks(sizeof(header)));
351 header.m_sectionCount = ENDIAN_HOST_TO_LITTLE_U16((uint16_t)m_sections.size());
352 header.m_sectionHeaderSize = ENDIAN_HOST_TO_LITTLE_U16((uint16_t)numberOfCipherBlocks(sizeof(section_header_t)));
353 header.m_signature2[0] = 's';
354 header.m_signature2[1] = 'g';
355 header.m_signature2[2] = 't';
356 header.m_signature2[3] = 'l';
357 header.m_timestamp = ENDIAN_HOST_TO_LITTLE_U64(getTimestamp());
358 header.m_driveTag = m_driveTag;
360 // Prepare version fields by converting them to the correct byte order.
361 header.m_productVersion = m_productVersion;
362 header.m_componentVersion = m_componentVersion;
363 header.m_productVersion.fixByteOrder();
364 header.m_componentVersion.fixByteOrder();
366 // the fields are dependant on others
367 header.m_keyDictionaryBlock = ENDIAN_HOST_TO_LITTLE_U16(header.m_headerBlocks + header.m_sectionCount * header.m_sectionHeaderSize);
368 header.m_firstBootTagBlock = ENDIAN_HOST_TO_LITTLE_U32(header.m_keyDictionaryBlock + header.m_keyCount * 2);
370 // generate random pad bytes
371 RandomNumberGenerator rng;
372 rng.generateBlock(header.m_padding0, sizeof(header.m_padding0));
373 rng.generateBlock(header.m_padding1, sizeof(header.m_padding1));
375 // compute SHA-1 digest over the image header
376 uint8_t * message = reinterpret_cast<uint8_t *>(&header.m_signature);
377 uint32_t length = static_cast<uint32_t>(sizeof(header) - sizeof(header.m_digest)); // include padding
381 hash.Update(message, length);
383 hash.GetHash(header.m_digest);
386 //! Returns the number of microseconds since 00:00 1-1-2000. In actuality, the timestamp
387 //! is only accurate to seconds, and is simply extended out to microseconds.
389 //! \todo Use the operating system's low-level functions to get a true microsecond
390 //! timestamp, instead of faking it like we do now.
391 //! \bug The timestamp might be off an hour.
392 uint64_t EncoreBootImage::getTimestamp()
395 struct tm epoch = { 0, 0, 0, 1, 0, 100, 0, 0 }; // 00:00 1-1-2000
397 struct tm epoch = { 0, 0, 0, 1, 0, 100, 0, 0, 1, 0, NULL }; // 00:00 1-1-2000
399 time_t epochTime = mktime(&epoch);
400 time_t now = time(NULL);
402 uint64_t microNow = uint64_t(now) * 1000000; // convert to microseconds
406 //! Scans the section list looking for the first section which has
407 //! the #ROM_SECTION_BOOTABLE flag set on it.
408 EncoreBootImage::Section * EncoreBootImage::findFirstBootableSection()
410 section_iterator_t it = beginSection();
411 for (; it != endSection(); ++it)
413 if ((*it)->getFlags() & ROM_SECTION_BOOTABLE)
419 // no bootable sections were found
423 //! The boot tag for \a section is taken into account, thus making the
424 //! result offset point to the first block of the actual section data.
426 //! \note The offset will only be valid if all encryption keys and all
427 //! sections have already been added to the image.
428 uint32_t EncoreBootImage::getSectionOffset(Section * section)
430 // start with boot image headers
431 uint32_t offset = numberOfCipherBlocks(sizeof(boot_image_header_t)); // header
432 offset += numberOfCipherBlocks(sizeof(section_header_t)) * sectionCount(); // section table
433 offset += 2 * keyCount(); // key dictiontary
435 // add up sections before this one
436 section_iterator_t it = beginSection();
437 for (; it != endSection() && *it != section; ++it)
439 Section * thisSection = *it;
441 // insert padding for section alignment
442 offset += getPadBlockCountForSection(thisSection, offset);
444 // add one for boot tag associated with this section
447 // now add the section's contents
448 offset += thisSection->getBlockCount();
451 // and add padding for this section
452 offset += getPadBlockCountForSection(section, offset);
454 // skip over this section's boot tag
460 //! Computes the number of blocks of padding required to align \a section while
461 //! taking into account the boot tag that gets inserted before the section contents.
462 unsigned EncoreBootImage::getPadBlockCountForSection(Section * section, unsigned offset)
464 // Compute the number of padding blocks needed to align the section. This first
465 // call to getPadBlockCountForOffset() passes an offset that excludes
466 // the boot tag for this section.
467 unsigned paddingBlocks = section->getPadBlockCountForOffset(offset);
469 // If the pad count comes back as 0 then we need to try again with an offset that
470 // includes the boot tag. This is all because we're aligning the section contents
471 // start and not the section's boot tag.
472 if (paddingBlocks == 0)
474 paddingBlocks = section->getPadBlockCountForOffset(offset + 1);
476 // Otherwise if we get a nonzero pad amount then we need to subtract the block
477 // for the section's boot tag from the pad count.
483 return paddingBlocks;
486 uint32_t EncoreBootImage::getImageSize()
488 // determine to total size of the image
489 const uint32_t headerBlocks = numberOfCipherBlocks(sizeof(boot_image_header_t));
490 const uint32_t sectionHeaderSize = numberOfCipherBlocks(sizeof(section_header_t));
491 uint32_t imageBlocks = headerBlocks;
492 imageBlocks += sectionHeaderSize * m_sections.size(); // section table
493 imageBlocks += 2 * m_keys.size(); // key dict
495 // add in each section's size
496 section_iterator_t it = beginSection();
497 for (; it != endSection(); ++it)
499 // add in this section's size, padding to align it, and its boot tag
500 imageBlocks += getPadBlockCountForSection(*it, imageBlocks);
501 imageBlocks += (*it)->getBlockCount();
511 void EncoreBootImage::debugPrint() const
513 const_section_iterator_t it = beginSection();
514 for (; it != endSection(); ++it)
516 const Section * section = *it;
517 section->debugPrint();
521 //! \param blocks Pointer to the raw data blocks.
522 //! \param count Number of blocks pointed to by \a blocks.
523 //! \param[out] consumed On exit, this points to the number of cipher blocks that were occupied
524 //! by the command. Should be at least 1 for every command. This must not be NULL
527 //! \return A new boot command instance.
528 //! \retval NULL The boot command pointed to by \a blocks was not recognized as a known
531 //! \exception std::runtime_error This exception indicates that a command was recognized
532 //! but contained invalid data. Compare this to a NULL result which indicates that
533 //! no command was recognized at all.
534 EncoreBootImage::BootCommand * EncoreBootImage::BootCommand::createFromData(const cipher_block_t * blocks, unsigned count, unsigned * consumed)
536 const boot_command_t * header = reinterpret_cast<const boot_command_t *>(blocks);
537 BootCommand * command = NULL;
539 switch (header->m_tag)
542 command = new NopCommand();
545 command = new TagCommand();
548 command = new LoadCommand();
551 command = new FillCommand();
554 command = new ModeCommand();
557 command = new JumpCommand();
560 command = new CallCommand();
566 command->initFromData(blocks, count, consumed);
571 //! The checksum algorithm is totally straightforward, except that the
572 //! initial checksum byte value is set to 0x5a instead of 0.
573 uint8_t EncoreBootImage::BootCommand::calculateChecksum(const boot_command_t & header)
575 const uint8_t * bytes = reinterpret_cast<const uint8_t *>(&header);
576 uint8_t checksum = 0x5a;
579 // start at one to skip checksum field
580 for (i = 1; i < sizeof(header); ++i)
582 checksum += bytes[i];
588 //! The default implementation returns 0, indicating that no blocks are
590 unsigned EncoreBootImage::BootCommand::getBlockCount() const
592 return 1 + getDataBlockCount();
595 //! Up to \a maxCount cipher blocks are copied into the buffer pointed to by
596 //! the \a data argument. The index of the first block to copy is
597 //! held in the \a offset argument.
599 //! \param offset Starting block number to copy. Zero means the first available block.
600 //! \param maxCount Up to this number of blocks may be copied into \a data. Must be 1 or greater.
601 //! \param data Buffer for outgoing cipher blocks. Must have enough room to hold
602 //! \a maxCount blocks.
604 //! \return The number of cipher blocks copied into \a data.
605 //! \retval 0 No more blocks are available and nothing was written to \a data.
607 //! \exception std::out_of_range If \a offset is invalid.
608 unsigned EncoreBootImage::BootCommand::getBlocks(unsigned offset, unsigned maxCount, cipher_block_t * data)
611 assert(maxCount >= 1);
613 // check for valid offset
614 if (offset >= getBlockCount())
616 throw std::out_of_range("invalid offset");
619 // handle the command header block separately
622 assert(sizeof(boot_command_t) == sizeof(cipher_block_t));
624 boot_command_t header;
625 fillCommandHeader(header);
626 memcpy(data, &header, sizeof(header));
631 // handle any data blocks
632 return getDataBlocks(offset - 1, maxCount, data);
635 //! The checksum field of \a testHeader is always computed and checked against itself.
636 //! All other fields are compared to the corresponding value set in \a modelHeader
637 //! if the appropriate flag is set in \a whichFields. For example, the m_address fields
638 //! in \a testHeader and \a modelHeader are compared when the CMD_ADDRESS_FIELD bit
639 //! is set in \a whichFields. An exception is thrown if any comparison fails.
641 //! \param modelHeader The baseline header to compare against. Only those fields that
642 //! have corresponding bits set in \a whichFields need to be set.
643 //! \param testHeader The actual command header which is being validated.
644 //! \param whichFields A bitfield used to determine which fields of the boot command
645 //! header are compared. Possible values are:
647 //! - CMD_FLAGS_FIELD
648 //! - CMD_ADDRESS_FIELD
649 //! - CMD_COUNT_FIELD
652 //! \exception std::runtime_error Thrown if any requested validation fails.
653 void EncoreBootImage::BootCommand::validateHeader(const boot_command_t * modelHeader, const boot_command_t * testHeader, unsigned whichFields)
655 // compare all the fields that were requested
656 if ((whichFields & CMD_TAG_FIELD) && (testHeader->m_tag != modelHeader->m_tag))
658 throw std::runtime_error("invalid tag field");
661 if ((whichFields & CMD_FLAGS_FIELD) && (testHeader->m_flags != modelHeader->m_flags))
663 throw std::runtime_error("invalid flags field");
666 if ((whichFields & CMD_ADDRESS_FIELD) && (testHeader->m_address != modelHeader->m_address))
668 throw std::runtime_error("invalid address field");
671 if ((whichFields & CMD_COUNT_FIELD) && (testHeader->m_count != modelHeader->m_count))
673 throw std::runtime_error("invalid count field");
676 if ((whichFields & CMD_DATA_FIELD) && (testHeader->m_data != modelHeader->m_data))
678 throw std::runtime_error("invalid data field");
681 // calculate checksum
682 uint8_t testChecksum = calculateChecksum(*testHeader);
683 if (testChecksum != testHeader->m_checksum)
685 throw std::runtime_error("invalid checksum");
689 //! Since the NOP command has no data, this method just validates the command header.
690 //! All fields except the checksum are expected to be set to 0.
692 //! \param blocks Pointer to the raw data blocks.
693 //! \param count Number of blocks pointed to by \a blocks.
694 //! \param[out] consumed On exit, this points to the number of cipher blocks that were occupied
695 //! by the command. Should be at least 1 for every command. This must not be NULL
698 //! \exception std::runtime_error Thrown if header fields are invalid.
699 void EncoreBootImage::NopCommand::initFromData(const cipher_block_t * blocks, unsigned count, unsigned * consumed)
701 const boot_command_t model = { 0, ROM_NOP_CMD, 0, 0, 0, 0 };
702 const boot_command_t * header = reinterpret_cast<const boot_command_t *>(blocks);
703 validateHeader(&model, header, CMD_TAG_FIELD | CMD_FLAGS_FIELD | CMD_ADDRESS_FIELD | CMD_COUNT_FIELD | CMD_DATA_FIELD);
708 //! All fields of the boot command header structure are set to 0, except
709 //! for the checksum. This includes the tag field since the tag value for
710 //! the #ROM_NOP_CMD is zero. And since all fields are zeroes the checksum
711 //! remains the initial checksum value of 0x5a.
712 void EncoreBootImage::NopCommand::fillCommandHeader(boot_command_t & header)
714 header.m_tag = getTag();
716 header.m_address = 0;
719 header.m_checksum = calculateChecksum(header); // do this last
722 void EncoreBootImage::NopCommand::debugPrint() const
724 Log::log(Logger::INFO2, "\tNOOP\n");
727 //! The identifier, length, and flags fields are taken from \a section.
729 //! \todo How does length get set correctly if the length is supposed to include
731 EncoreBootImage::TagCommand::TagCommand(const Section & section)
733 m_sectionIdentifier = section.getIdentifier();
734 m_sectionLength = section.getBlockCount();
735 m_sectionFlags = section.getFlags();
738 //! \param blocks Pointer to the raw data blocks.
739 //! \param count Number of blocks pointed to by \a blocks.
740 //! \param[out] consumed On exit, this points to the number of cipher blocks that were occupied
741 //! by the command. Should be at least 1 for every command. This must not be NULL
744 //! \exception std::runtime_error Thrown if header fields are invalid.
745 void EncoreBootImage::TagCommand::initFromData(const cipher_block_t * blocks, unsigned count, unsigned * consumed)
747 const boot_command_t model = { 0, ROM_TAG_CMD, 0, 0, 0, 0 };
748 const boot_command_t * header = reinterpret_cast<const boot_command_t *>(blocks);
749 validateHeader(&model, header, CMD_TAG_FIELD);
751 // read fields from header
752 m_isLast = (ENDIAN_LITTLE_TO_HOST_U16(header->m_flags) & ROM_LAST_TAG) != 0;
753 m_sectionIdentifier = ENDIAN_LITTLE_TO_HOST_U32(header->m_address);
754 m_sectionLength = ENDIAN_LITTLE_TO_HOST_U32(header->m_count);
755 m_sectionFlags = ENDIAN_LITTLE_TO_HOST_U32(header->m_data);
760 //! This method currently assumes that the next tag command will come immediately
761 //! after the data for this section.
762 void EncoreBootImage::TagCommand::fillCommandHeader(boot_command_t & header)
764 header.m_tag = getTag();
765 header.m_flags = ENDIAN_HOST_TO_LITTLE_U16(m_isLast ? ROM_LAST_TAG : 0);
766 header.m_address = ENDIAN_HOST_TO_LITTLE_U32(m_sectionIdentifier);
767 header.m_count = ENDIAN_HOST_TO_LITTLE_U32(m_sectionLength);
768 header.m_data = ENDIAN_HOST_TO_LITTLE_U32(m_sectionFlags);
769 header.m_checksum = calculateChecksum(header); // do this last
772 void EncoreBootImage::TagCommand::debugPrint() const
774 Log::log(Logger::INFO2, " BTAG | sec=0x%08x | cnt=0x%08x | flg=0x%08x\n", m_sectionIdentifier, m_sectionLength, m_sectionFlags);
777 //! All fields are set to zero.
779 EncoreBootImage::LoadCommand::LoadCommand()
780 : BootCommand(), m_data(), m_padCount(0), m_length(0), m_address(0), m_loadDCD(false)
785 EncoreBootImage::LoadCommand::LoadCommand(uint32_t address, const uint8_t * data, uint32_t length)
786 : BootCommand(), m_data(), m_padCount(0), m_length(0), m_address(address), m_loadDCD(false)
789 setData(data, length);
792 //! \param blocks Pointer to the raw data blocks.
793 //! \param count Number of blocks pointed to by \a blocks.
794 //! \param[out] consumed On exit, this points to the number of cipher blocks that were occupied
795 //! by the command. Should be at least 1 for every command. This must not be NULL
798 //! \exception std::runtime_error This exception is thrown if the actual CRC of the load
799 //! data does not match the CRC stored in the command header. Also thrown if the
800 //! \a count parameter is less than the number of data blocks needed for the length
801 //! specified in the command header or if header fields are invalid.
802 void EncoreBootImage::LoadCommand::initFromData(const cipher_block_t * blocks, unsigned count, unsigned * consumed)
804 // check static fields
805 const boot_command_t model = { 0, ROM_LOAD_CMD, 0, 0, 0, 0 };
806 const boot_command_t * header = reinterpret_cast<const boot_command_t *>(blocks);
807 validateHeader(&model, header, CMD_TAG_FIELD);
809 // read fields from header
810 m_address = ENDIAN_LITTLE_TO_HOST_U32(header->m_address);
811 m_length = ENDIAN_LITTLE_TO_HOST_U32(header->m_count);
812 unsigned crc = ENDIAN_LITTLE_TO_HOST_U32(header->m_data);
813 unsigned dataBlockCount = numberOfCipherBlocks(m_length);
814 m_padCount = sizeOfPaddingForCipherBlocks(dataBlockCount);
815 m_loadDCD = (ENDIAN_LITTLE_TO_HOST_U16(header->m_flags) & ROM_LOAD_DCD) != 0;
817 // make sure there are enough blocks
818 if (count - 1 < dataBlockCount)
820 throw std::runtime_error("not enough cipher blocks for load data");
824 setData(reinterpret_cast<const uint8_t *>(blocks + 1), m_length);
829 const uint8_t * firstPadByte = reinterpret_cast<const uint8_t *> (blocks + (1 + dataBlockCount)) - m_padCount;
830 memcpy(m_padding, firstPadByte, m_padCount);
834 uint32_t actualCRC = calculateCRC();
835 if (actualCRC != crc)
837 throw std::runtime_error("load data failed CRC check");
840 *consumed = 1 + dataBlockCount;
843 //! The only thing unique in the load command header is the
844 //! #elftosb::EncoreBootImage::boot_command_t::m_data. It contains a CRC-32 over the
845 //! load data, plus any bytes of padding in the last data cipher block.
846 void EncoreBootImage::LoadCommand::fillCommandHeader(boot_command_t & header)
848 header.m_tag = getTag();
849 header.m_flags = ENDIAN_HOST_TO_LITTLE_U16(m_loadDCD ? ROM_LOAD_DCD : 0);
850 header.m_address = ENDIAN_HOST_TO_LITTLE_U32(m_address);
851 header.m_count = ENDIAN_HOST_TO_LITTLE_U32(m_length);
852 header.m_data = ENDIAN_HOST_TO_LITTLE_U32(calculateCRC());
855 header.m_checksum = calculateChecksum(header);
858 //! A CRC-32 is calculated over the load data, including any pad bytes
859 //! that are required in the last data cipher block. Including the
860 //! pad bytes in the CRC makes it vastly easier for the ROM to calculate
861 //! the CRC for validation.
862 uint32_t EncoreBootImage::LoadCommand::calculateCRC() const
866 crc.update(m_data, m_length);
869 // include random padding in the CRC
870 crc.update(m_padding, m_padCount);
872 crc.truncatedFinal(reinterpret_cast<uint8_t*>(&result), sizeof(result));
877 //! A local copy of the load data is made. This copy will be disposed of when this object
878 //! is destroyed. This means the caller is free to deallocate \a data after this call
879 //! returns. It also means the caller can pass a pointer into the middle of a buffer for
880 //! \a data and not worry about ownership issues.
881 void EncoreBootImage::LoadCommand::setData(const uint8_t * data, uint32_t length)
886 uint8_t * dataCopy = new uint8_t[length];
887 memcpy(dataCopy, data, length);
892 m_padCount = sizeOfPaddingForCipherBlocks(m_length);
895 //! \return The number of cipher blocks required to hold the load data,
896 //! rounded up as necessary.
897 unsigned EncoreBootImage::LoadCommand::getDataBlockCount() const
899 // round up to the next cipher block
900 return numberOfCipherBlocks(m_length);
903 //! Up to \a maxCount data blocks are copied into the buffer pointed to by
904 //! the \a data argument. This is only a request for \a maxCount blocks.
905 //! A return value of 0 indicates that no more blocks are available. The
906 //! index of the first block to copy is held in the \a offset argument.
907 //! If there are pad bytes needed to fill out the last data block, they
908 //! will be filled with random data in order to add to the "whiteness" of
909 //! the data on both sides of encryption.
911 //! \param offset Starting block number to copy. Zero means the first available block.
912 //! \param maxCount Up to this number of blocks may be copied into \a data. Must be 1 or greater.
913 //! \param data Buffer for outgoing data blocks. Must have enough room to hold
914 //! \a maxCount blocks.
916 //! \return The number of data blocks copied into \a data.
917 //! \retval 0 No more blocks are available and nothing was written to \a data.
919 //! \exception std::out_of_range Thrown when offset is invalid.
921 //! \todo fill pad bytes with random bytes
922 unsigned EncoreBootImage::LoadCommand::getDataBlocks(unsigned offset, unsigned maxCount, cipher_block_t * data)
925 assert(maxCount != 0);
927 uint32_t blockCount = getDataBlockCount();
930 if (offset >= blockCount)
932 throw std::out_of_range("invalid offset");
935 // figure out how many blocks to return
936 unsigned resultBlocks = blockCount - offset;
937 if (resultBlocks > maxCount)
939 resultBlocks = maxCount;
941 // exclude last block if there is padding
942 if (m_padCount && (offset != blockCount - 1) && (offset + resultBlocks == blockCount))
948 // if there are pad bytes, handle the last block specially
949 if (m_padCount && offset == blockCount - 1)
951 // copy the remainder of the load data into the first part of the result block
952 unsigned remainderLength = sizeof(cipher_block_t) - m_padCount;
953 memcpy(data, &m_data[sizeof(cipher_block_t) * offset], remainderLength);
955 // copy pad bytes we previously generated into the last part of the result block
956 // data is a cipher block pointer, so indexing is done on cipher block
957 // boundaries, thus we need a byte pointer to index properly
958 uint8_t * bytePtr = reinterpret_cast<uint8_t*>(data);
959 memcpy(bytePtr + remainderLength, &m_padding, m_padCount);
963 memcpy(data, &m_data[sizeof(cipher_block_t) * offset], sizeof(cipher_block_t) * resultBlocks);
969 //! Fills #m_padding with random bytes that may be used to fill up the last data
971 void EncoreBootImage::LoadCommand::fillPadding()
973 RandomNumberGenerator rng;
974 rng.generateBlock(m_padding, sizeof(m_padding));
977 void EncoreBootImage::LoadCommand::debugPrint() const
979 Log::log(Logger::INFO2, " LOAD | adr=0x%08x | len=0x%08x | crc=0x%08x | flg=0x%08x\n", m_address, m_length, calculateCRC(), m_loadDCD ? ROM_LOAD_DCD : 0);
982 //! The pattern, address, and count are all initialized to zero, and the pattern
983 //! size is set to a word.
984 EncoreBootImage::FillCommand::FillCommand()
985 : BootCommand(), m_address(0), m_count(0), m_pattern(0)
989 //! \param blocks Pointer to the raw data blocks.
990 //! \param count Number of blocks pointed to by \a blocks.
991 //! \param[out] consumed On exit, this points to the number of cipher blocks that were occupied
992 //! by the command. Should be at least 1 for every command. This must not be NULL
995 //! \exception std::runtime_error Thrown if header fields are invalid.
996 void EncoreBootImage::FillCommand::initFromData(const cipher_block_t * blocks, unsigned count, unsigned * consumed)
998 // check static fields
999 const boot_command_t model = { 0, ROM_FILL_CMD, 0, 0, 0, 0 };
1000 const boot_command_t * header = reinterpret_cast<const boot_command_t *>(blocks);
1001 validateHeader(&model, header, CMD_TAG_FIELD | CMD_FLAGS_FIELD);
1003 // read fields from header
1004 m_address = ENDIAN_LITTLE_TO_HOST_U32(header->m_address);
1005 m_count = ENDIAN_LITTLE_TO_HOST_U32(header->m_count);
1006 m_pattern = ENDIAN_LITTLE_TO_HOST_U32(header->m_data);
1011 void EncoreBootImage::FillCommand::fillCommandHeader(boot_command_t & header)
1013 header.m_tag = getTag();
1015 header.m_address = ENDIAN_HOST_TO_LITTLE_U32(m_address);
1016 header.m_count = ENDIAN_HOST_TO_LITTLE_U32(m_count);
1017 header.m_data = ENDIAN_HOST_TO_LITTLE_U32(m_pattern);
1018 header.m_checksum = calculateChecksum(header); // do this last
1021 //! Extends the pattern across 32 bits.
1023 void EncoreBootImage::FillCommand::setPattern(uint8_t pattern)
1025 m_pattern = (pattern << 24) | (pattern << 16) | (pattern << 8) | pattern;
1028 //! Extends the pattern across 32 bits.
1030 void EncoreBootImage::FillCommand::setPattern(uint16_t pattern)
1032 m_pattern = (pattern << 16) | pattern;
1035 void EncoreBootImage::FillCommand::setPattern(uint32_t pattern)
1037 m_pattern = pattern;
1040 void EncoreBootImage::FillCommand::debugPrint() const
1042 Log::log(Logger::INFO2, " FILL | adr=0x%08x | len=0x%08x | ptn=0x%08x\n", m_address, m_count, m_pattern);
1045 //! \param blocks Pointer to the raw data blocks.
1046 //! \param count Number of blocks pointed to by \a blocks.
1047 //! \param[out] consumed On exit, this points to the number of cipher blocks that were occupied
1048 //! by the command. Should be at least 1 for every command. This must not be NULL
1051 //! \exception std::runtime_error Thrown if header fields are invalid.
1052 void EncoreBootImage::ModeCommand::initFromData(const cipher_block_t * blocks, unsigned count, unsigned * consumed)
1054 // check static fields
1055 const boot_command_t model = { 0, ROM_MODE_CMD, 0, 0, 0, 0 };
1056 const boot_command_t * header = reinterpret_cast<const boot_command_t *>(blocks);
1057 validateHeader(&model, header, CMD_TAG_FIELD | CMD_FLAGS_FIELD | CMD_ADDRESS_FIELD | CMD_COUNT_FIELD);
1059 // read fields from header
1060 m_mode = ENDIAN_LITTLE_TO_HOST_U32(header->m_data);
1065 void EncoreBootImage::ModeCommand::fillCommandHeader(boot_command_t & header)
1067 header.m_tag = getTag();
1069 header.m_address = 0;
1071 header.m_data = ENDIAN_HOST_TO_LITTLE_U32(m_mode);
1072 header.m_checksum = calculateChecksum(header); // do this last
1075 void EncoreBootImage::ModeCommand::debugPrint() const
1077 Log::log(Logger::INFO2, " MODE | mod=0x%08x\n", m_mode);
1080 //! \param blocks Pointer to the raw data blocks.
1081 //! \param count Number of blocks pointed to by \a blocks.
1082 //! \param[out] consumed On exit, this points to the number of cipher blocks that were occupied
1083 //! by the command. Should be at least 1 for every command. This must not be NULL
1086 //! \exception std::runtime_error Thrown if header fields are invalid.
1087 void EncoreBootImage::JumpCommand::initFromData(const cipher_block_t * blocks, unsigned count, unsigned * consumed)
1089 // check static fields
1090 const boot_command_t model = { 0, getTag(), 0, 0, 0, 0 };
1091 const boot_command_t * header = reinterpret_cast<const boot_command_t *>(blocks);
1092 validateHeader(&model, header, CMD_TAG_FIELD | CMD_COUNT_FIELD);
1094 // read fields from header
1095 m_address = ENDIAN_LITTLE_TO_HOST_U32(header->m_address);
1096 m_argument = ENDIAN_LITTLE_TO_HOST_U32(header->m_data);
1097 m_isHAB = (ENDIAN_LITTLE_TO_HOST_U16(header->m_flags) & ROM_HAB_EXEC) != 0;
1102 void EncoreBootImage::JumpCommand::fillCommandHeader(boot_command_t & header)
1104 header.m_tag = getTag();
1105 header.m_flags = ENDIAN_HOST_TO_LITTLE_U16(m_isHAB ? ROM_HAB_EXEC : 0);
1106 header.m_address = ENDIAN_HOST_TO_LITTLE_U32(m_address);
1108 header.m_data = ENDIAN_HOST_TO_LITTLE_U32(m_argument);
1109 header.m_checksum = calculateChecksum(header); // do this last
1112 void EncoreBootImage::JumpCommand::debugPrint() const
1114 Log::log(Logger::INFO2, " JUMP | adr=0x%08x | arg=0x%08x | flg=0x%08x\n", m_address, m_argument, m_isHAB ? ROM_HAB_EXEC : 0);
1117 void EncoreBootImage::CallCommand::debugPrint() const
1119 Log::log(Logger::INFO2, " CALL | adr=0x%08x | arg=0x%08x | flg=0x%08x\n", m_address, m_argument, m_isHAB ? ROM_HAB_EXEC : 0);
1122 //! Only if the section has been assigned a boot image owner object will this
1123 //! method be able to fill in the #section_header_t::m_offset field. If no
1124 //! boot image has been set the offset will be set to 0.
1125 void EncoreBootImage::Section::fillSectionHeader(section_header_t & header)
1127 header.m_tag = getIdentifier();
1128 header.m_offset = 0;
1129 header.m_length = ENDIAN_HOST_TO_LITTLE_U32(getBlockCount());
1130 header.m_flags = ENDIAN_HOST_TO_LITTLE_U32(getFlags());
1132 // if we're attached to an image, we can compute our real offset
1135 header.m_offset = ENDIAN_HOST_TO_LITTLE_U32(m_image->getSectionOffset(this));
1139 //! The alignment will never be less than 16, since that is the size of the
1140 //! cipher block which is the basic unit of the boot image format. If an
1141 //! alignment less than 16 is set it will be ignored.
1143 //! \param alignment Alignment in bytes for this section. Must be a power of two.
1144 //! Ignored if less than 16.
1145 void EncoreBootImage::Section::setAlignment(unsigned alignment)
1147 if (alignment > BOOT_IMAGE_MINIMUM_SECTION_ALIGNMENT)
1149 m_alignment = alignment;
1153 //! This method calculates the number of padding blocks that need to be inserted
1154 //! from a given offset for the section to be properly aligned. The value returned
1155 //! is the number of padding blocks that should be inserted starting just after
1156 //! \a offset to align the first cipher block of the section contents. The section's
1157 //! boot tag is \i not taken into account by this method, so the caller must
1158 //! deal with that herself.
1160 //! \param offset Start offset in cipher blocks (not bytes).
1162 //! \return A number of cipher blocks of padding to insert.
1163 unsigned EncoreBootImage::Section::getPadBlockCountForOffset(unsigned offset)
1165 // convert alignment from byte to block alignment
1166 unsigned blockAlignment = m_alignment >> 4;
1168 unsigned nextAlignmentOffset = (offset + blockAlignment - 1) / blockAlignment * blockAlignment;
1170 return nextAlignmentOffset - offset;
1173 EncoreBootImage::BootSection::~BootSection()
1178 void EncoreBootImage::BootSection::deleteCommands()
1180 // dispose of all sections
1181 iterator_t it = begin();
1182 for (; it != end(); ++it)
1188 //! Always returns at least 1 for the required tag command.
1190 unsigned EncoreBootImage::BootSection::getBlockCount() const
1194 const_iterator_t it = begin();
1195 for (; it != end(); ++it)
1197 count += (*it)->getBlockCount();
1203 //! Up to \a maxCount cipher blocks are copied into the buffer pointed to by
1204 //! the \a data argument. A return value of 0 indicates that
1205 //! no more blocks are available. The index of the first block to copy is
1206 //! held in the \a offset argument.
1208 //! \param offset Starting block number to copy. Zero means the first available block.
1209 //! \param maxCount Up to this number of blocks may be copied into \a data.
1210 //! \param data Buffer for outgoing cipher blocks. Must have enough room to hold
1211 //! \a maxCount blocks.
1213 //! \return The number of cipher blocks copied into \a data.
1214 //! \retval 0 No more blocks are available and nothing was written to \a data.
1215 unsigned EncoreBootImage::BootSection::getBlocks(unsigned offset, unsigned maxCount, cipher_block_t * data)
1218 assert(maxCount >= 1);
1220 unsigned currentOffset = 0;
1221 unsigned readCount = maxCount;
1223 iterator_t it = begin();
1224 for (; it != end(); ++it)
1226 BootCommand * command = *it;
1227 unsigned commandBlocks = command->getBlockCount();
1229 // this should never be false!
1230 assert(offset >= currentOffset);
1232 // skip forward until we hit the requested offset
1233 if (offset >= currentOffset + commandBlocks)
1235 currentOffset += commandBlocks;
1239 // read from this command
1240 unsigned commandOffset = offset - currentOffset;
1241 unsigned commandRemaining = commandBlocks - commandOffset;
1242 if (readCount > commandRemaining)
1244 readCount = commandRemaining;
1246 return command->getBlocks(commandOffset, readCount, data);
1252 //! The entire contents of the section must be in memory, pointed to by \a blocks.
1253 //! Any commands that had previously been added to the section are disposed of.
1255 //! \param blocks Pointer to the section contents.
1256 //! \param count Number of blocks pointed to by \a blocks.
1258 //! \exception std::runtime_error Thrown if a boot command cannot be created from
1259 //! the cipher block stream.
1260 void EncoreBootImage::BootSection::fillFromData(const cipher_block_t * blocks, unsigned count)
1262 // start with an empty slate
1265 const cipher_block_t * currentBlock = blocks;
1266 unsigned remaining = count;
1269 // try to create a command from the next cipher block. the number of
1270 // blocks the command used up is returned in consumed.
1272 BootCommand * command = BootCommand::createFromData(currentBlock, remaining, &consumed);
1275 throw std::runtime_error("invalid boot section data");
1278 addCommand(command);
1280 // update loop counters
1281 remaining -= consumed;
1282 currentBlock += consumed;
1286 void EncoreBootImage::BootSection::debugPrint() const
1288 Log::log(Logger::INFO2, "Boot Section 0x%08x:\n", m_identifier);
1290 const_iterator_t it = begin();
1291 for (; it != end(); ++it)
1293 const BootCommand * command = *it;
1294 command->debugPrint();
1298 //! A copy is made of \a data. Any previously assigned data is disposed of.
1300 void EncoreBootImage::DataSection::setData(const uint8_t * data, unsigned length)
1302 m_data = new uint8_t[length];
1303 memcpy(m_data.get(), data, length);
1307 //! The section takes ownership of \a data and will dispose of it using the
1308 //! array delete operator upon its destruction.
1309 void EncoreBootImage::DataSection::setDataNoCopy(const uint8_t * data, unsigned length)
1315 unsigned EncoreBootImage::DataSection::getBlockCount() const
1317 return numberOfCipherBlocks(m_length);
1320 unsigned EncoreBootImage::DataSection::getBlocks(unsigned offset, unsigned maxCount, cipher_block_t * data)
1323 assert(maxCount != 0);
1325 unsigned blockCount = getBlockCount();
1326 unsigned padCount = sizeOfPaddingForCipherBlocks(m_length);
1329 if (offset >= blockCount)
1331 throw std::out_of_range("invalid offset");
1334 // figure out how many blocks to return
1335 unsigned resultBlocks = blockCount - offset;
1336 if (resultBlocks > maxCount)
1338 resultBlocks = maxCount;
1340 // exclude last block if there is padding
1341 if (padCount && (offset != blockCount - 1) && (offset + resultBlocks == blockCount))
1347 // if there are pad bytes, handle the last block specially
1348 if (padCount && offset == blockCount - 1)
1350 // copy the remainder of the load data into the first part of the result block
1351 unsigned remainderLength = sizeof(cipher_block_t) - padCount;
1352 memcpy(data, &m_data[sizeOfCipherBlocks(offset)], remainderLength);
1354 // set pad bytes to zeroes.
1355 // data is a cipher block pointer, so indexing is done on cipher block
1356 // boundaries, thus we need a byte pointer to index properly
1357 uint8_t * bytePtr = reinterpret_cast<uint8_t*>(data);
1358 memset(bytePtr + remainderLength, 0, padCount);
1362 memcpy(data, &m_data[sizeOfCipherBlocks(offset)], sizeOfCipherBlocks(resultBlocks));
1365 return resultBlocks;
1368 void EncoreBootImage::DataSection::debugPrint() const
1370 Log::log(Logger::INFO2, "Data Section 0x%08x: (%d bytes, %d blocks)\n", m_identifier, m_length, getBlockCount());