1 /* 2 * File: EncoreBootImage.cpp 3 * 4 * Copyright (c) Freescale Semiconductor, Inc. All rights reserved. 5 * See included license file for license details. 6 */ 7 8 #include "EncoreBootImage.h" 9 #include <stdexcept> 10 #include <algorithm> 11 #include <time.h> 12 #include "crc.h" 13 #include "SHA1.h" 14 #include "Random.h" 15 #include "rijndael.h" 16 #include "RijndaelCBCMAC.h" 17 #include "Logging.h" 18 #include "EndianUtilities.h" 19 20 using namespace elftosb; 21 22 EncoreBootImage::EncoreBootImage() 23 : m_headerFlags(0), 24 m_productVersion(), 25 m_componentVersion(), 26 m_driveTag(0) 27 { 28 } 29 30 EncoreBootImage::~EncoreBootImage() 31 { 32 // dispose of all sections 33 section_iterator_t it = beginSection(); 34 for (; it != endSection(); ++it) 35 { 36 delete *it; 37 } 38 } 39 40 //! \exception std::runtime_error Raised if \a newSection has the same tag as a previously 41 //! added section. 42 void EncoreBootImage::addSection(Section * newSection) 43 { 44 // check for another section with this tag 45 section_iterator_t it = beginSection(); 46 for (; it != endSection(); ++it) 47 { 48 if ((*it)->getIdentifier() == newSection->getIdentifier()) 49 { 50 throw std::runtime_error("new section with non-unique tag"); 51 } 52 } 53 54 // no conflicting section tags, so add it 55 m_sections.push_back(newSection); 56 57 // tell the image who owns it now 58 newSection->setImage(this); 59 } 60 61 EncoreBootImage::section_iterator_t EncoreBootImage::findSection(Section * section) 62 { 63 return std::find(beginSection(), endSection(), section); 64 } 65 66 void EncoreBootImage::setProductVersion(const version_t & version) 67 { 68 m_productVersion = version; 69 } 70 71 void EncoreBootImage::setComponentVersion(const version_t & version) 72 { 73 m_componentVersion = version; 74 } 75 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) 84 { 85 // always generate the session key or DEK even if image is unencrypted 86 m_sessionKey.randomize(); 87 88 // prepare to compute CBC-MACs with each KEK 89 unsigned i; 90 smart_array_ptr<RijndaelCBCMAC> macs(0); 91 if (isEncrypted()) 92 { 93 macs = new RijndaelCBCMAC[m_keys.size()]; 94 for (i=0; i < m_keys.size(); ++i) 95 { 96 RijndaelCBCMAC mac(m_keys[i]); 97 (macs.get())[i] = mac; 98 } 99 } 100 101 // prepare to compute SHA-1 digest over entire image 102 CSHA1 hash; 103 hash.Reset(); 104 105 // count of total blocks written to the file 106 unsigned fileBlocksWritten = 0; 107 108 // we need some pieces of the header down below 109 boot_image_header_t imageHeader; 110 prepareImageHeader(imageHeader); 111 112 // write plaintext header 113 { 114 // write header 115 assert(sizeOfPaddingForCipherBlocks(sizeof(boot_image_header_t)) == 0); 116 stream.write(reinterpret_cast<char *>(&imageHeader), sizeof(imageHeader)); 117 fileBlocksWritten += numberOfCipherBlocks(sizeof(imageHeader)); 118 119 // update CBC-MAC over image header 120 if (isEncrypted()) 121 { 122 for (i=0; i < m_keys.size(); ++i) 123 { 124 (macs.get())[i].update(reinterpret_cast<uint8_t *>(&imageHeader), sizeof(imageHeader)); 125 } 126 } 127 128 // update SHA-1 129 hash.Update(reinterpret_cast<uint8_t *>(&imageHeader), sizeof(imageHeader)); 130 } 131 132 // write plaintext section table 133 { 134 section_iterator_t it = beginSection(); 135 for (; it != endSection(); ++it) 136 { 137 Section * section = *it; 138 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)); 145 146 // update CBC-MAC over this entry 147 if (isEncrypted()) 148 { 149 for (i=0; i < m_keys.size(); ++i) 150 { 151 (macs.get())[i].update(reinterpret_cast<uint8_t *>(§ionHeader), sizeof(sectionHeader)); 152 } 153 } 154 155 // update SHA-1 156 hash.Update(reinterpret_cast<uint8_t *>(§ionHeader), sizeof(sectionHeader)); 157 } 158 } 159 160 // finished with the CBC-MAC 161 if (isEncrypted()) 162 { 163 for (i=0; i < m_keys.size(); ++i) 164 { 165 (macs.get())[i].finalize(); 166 } 167 } 168 169 // write key dictionary 170 if (isEncrypted()) 171 { 172 key_iterator_t it = beginKeys(); 173 for (i=0; it != endKeys(); ++it, ++i) 174 { 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)); 180 fileBlocksWritten++; 181 182 // encrypt DEK with this key, write it out, and update image digest 183 Rijndael cipher; 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)); 189 fileBlocksWritten++; 190 } 191 } 192 193 // write sections and boot tags 194 { 195 section_iterator_t it = beginSection(); 196 for (; it != endSection(); ++it) 197 { 198 section_iterator_t itCopy = it; 199 bool isLastSection = (++itCopy == endSection()); 200 201 Section * section = *it; 202 cipher_block_t block; 203 unsigned blockCount = section->getBlockCount(); 204 unsigned blocksWritten = 0; 205 206 Rijndael cipher; 207 cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, imageHeader.m_iv); 208 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); 213 214 // Insert nop commands as padding to align the start of the section, if 215 // the section has special alignment requirements. 216 NopCommand nop; 217 while (paddingBlocks--) 218 { 219 blockCount = nop.getBlockCount(); 220 blocksWritten = 0; 221 while (blocksWritten < blockCount) 222 { 223 nop.getBlocks(blocksWritten, 1, &block); 224 225 if (isEncrypted()) 226 { 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); 230 } 231 232 stream.write(reinterpret_cast<char *>(&block), sizeof(cipher_block_t)); 233 hash.Update(reinterpret_cast<uint8_t *>(&block), sizeof(cipher_block_t)); 234 235 blocksWritten++; 236 fileBlocksWritten++; 237 } 238 } 239 240 // reinit cipher for boot tag 241 cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, imageHeader.m_iv); 242 243 // write boot tag 244 TagCommand tag(*section); 245 tag.setLast(isLastSection); 246 if (!isLastSection) 247 { 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)); 253 } 254 blockCount = tag.getBlockCount(); 255 blocksWritten = 0; 256 while (blocksWritten < blockCount) 257 { 258 tag.getBlocks(blocksWritten, 1, &block); 259 260 if (isEncrypted()) 261 { 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); 265 } 266 267 stream.write(reinterpret_cast<char *>(&block), sizeof(cipher_block_t)); 268 hash.Update(reinterpret_cast<uint8_t *>(&block), sizeof(cipher_block_t)); 269 270 blocksWritten++; 271 fileBlocksWritten++; 272 } 273 274 // reinit cipher for section data 275 cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, imageHeader.m_iv); 276 277 // write section data 278 blockCount = section->getBlockCount(); 279 blocksWritten = 0; 280 while (blocksWritten < blockCount) 281 { 282 section->getBlocks(blocksWritten, 1, &block); 283 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()) 288 { 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); 292 } 293 294 stream.write(reinterpret_cast<char *>(&block), sizeof(cipher_block_t)); 295 hash.Update(reinterpret_cast<uint8_t *>(&block), sizeof(cipher_block_t)); 296 297 blocksWritten++; 298 fileBlocksWritten++; 299 } 300 } 301 } 302 303 // write SHA-1 digest over entire image 304 { 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]; 309 hash.Final(); 310 hash.GetHash(digestBlocks.get()); 311 312 // set the pad bytes to random values 313 RandomNumberGenerator rng; 314 rng.generateBlock(&(digestBlocks.get())[sizeof(sha1_digest_t)], padBytes); 315 316 // encrypt with session key 317 if (isEncrypted()) 318 { 319 Rijndael cipher; 320 cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, imageHeader.m_iv); 321 cipher.blockEncrypt(digestBlocks.get(), digestBlocksSize * 8, digestBlocks.get()); 322 } 323 324 // write to the stream 325 stream.write(reinterpret_cast<char *>(digestBlocks.get()), digestBlocksSize); 326 } 327 } 328 329 void EncoreBootImage::prepareImageHeader(boot_image_header_t & header) 330 { 331 // get identifier for the first bootable section 332 Section * firstBootSection = findFirstBootableSection(); 333 section_id_t firstBootSectionID = 0; 334 if (firstBootSection) 335 { 336 firstBootSectionID = firstBootSection->getIdentifier(); 337 } 338 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; 359 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(); 365 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); 369 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)); 374 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 378 379 CSHA1 hash; 380 hash.Reset(); 381 hash.Update(message, length); 382 hash.Final(); 383 hash.GetHash(header.m_digest); 384 } 385 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. 388 //! 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() 393 { 394 #if defined(WIN32) || defined(__CYGWIN__) || defined(__sun) 395 struct tm epoch = { 0, 0, 0, 1, 0, 100, 0, 0 }; // 00:00 1-1-2000 396 #else 397 struct tm epoch = { 0, 0, 0, 1, 0, 100, 0, 0, 1, 0, NULL }; // 00:00 1-1-2000 398 #endif 399 time_t epochTime = mktime(&epoch); 400 time_t now = time(NULL); 401 now -= epochTime; 402 uint64_t microNow = uint64_t(now) * 1000000; // convert to microseconds 403 return microNow; 404 } 405 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() 409 { 410 section_iterator_t it = beginSection(); 411 for (; it != endSection(); ++it) 412 { 413 if ((*it)->getFlags() & ROM_SECTION_BOOTABLE) 414 { 415 return *it; 416 } 417 } 418 419 // no bootable sections were found 420 return NULL; 421 } 422 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. 425 //! 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) 429 { 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 434 435 // add up sections before this one 436 section_iterator_t it = beginSection(); 437 for (; it != endSection() && *it != section; ++it) 438 { 439 Section * thisSection = *it; 440 441 // insert padding for section alignment 442 offset += getPadBlockCountForSection(thisSection, offset); 443 444 // add one for boot tag associated with this section 445 offset++; 446 447 // now add the section's contents 448 offset += thisSection->getBlockCount(); 449 } 450 451 // and add padding for this section 452 offset += getPadBlockCountForSection(section, offset); 453 454 // skip over this section's boot tag 455 offset++; 456 457 return offset; 458 } 459 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) 463 { 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); 468 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) 473 { 474 paddingBlocks = section->getPadBlockCountForOffset(offset + 1); 475 } 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. 478 else 479 { 480 paddingBlocks--; 481 } 482 483 return paddingBlocks; 484 } 485 486 uint32_t EncoreBootImage::getImageSize() 487 { 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 494 495 // add in each section's size 496 section_iterator_t it = beginSection(); 497 for (; it != endSection(); ++it) 498 { 499 // add in this section's size, padding to align it, and its boot tag 500 imageBlocks += getPadBlockCountForSection(*it, imageBlocks); 501 imageBlocks += (*it)->getBlockCount(); 502 imageBlocks++; 503 } 504 505 // image MAC 506 imageBlocks += 2; 507 508 return imageBlocks; 509 } 510 511 void EncoreBootImage::debugPrint() const 512 { 513 const_section_iterator_t it = beginSection(); 514 for (; it != endSection(); ++it) 515 { 516 const Section * section = *it; 517 section->debugPrint(); 518 } 519 } 520 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 525 //! on entry! 526 //! 527 //! \return A new boot command instance. 528 //! \retval NULL The boot command pointed to by \a blocks was not recognized as a known 529 //! command type. 530 //! 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) 535 { 536 const boot_command_t * header = reinterpret_cast<const boot_command_t *>(blocks); 537 BootCommand * command = NULL; 538 539 switch (header->m_tag) 540 { 541 case ROM_NOP_CMD: 542 command = new NopCommand(); 543 break; 544 case ROM_TAG_CMD: 545 command = new TagCommand(); 546 break; 547 case ROM_LOAD_CMD: 548 command = new LoadCommand(); 549 break; 550 case ROM_FILL_CMD: 551 command = new FillCommand(); 552 break; 553 case ROM_MODE_CMD: 554 command = new ModeCommand(); 555 break; 556 case ROM_JUMP_CMD: 557 command = new JumpCommand(); 558 break; 559 case ROM_CALL_CMD: 560 command = new CallCommand(); 561 break; 562 } 563 564 if (command) 565 { 566 command->initFromData(blocks, count, consumed); 567 } 568 return command; 569 } 570 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) 574 { 575 const uint8_t * bytes = reinterpret_cast<const uint8_t *>(&header); 576 uint8_t checksum = 0x5a; 577 int i; 578 579 // start at one to skip checksum field 580 for (i = 1; i < sizeof(header); ++i) 581 { 582 checksum += bytes[i]; 583 } 584 585 return checksum; 586 } 587 588 //! The default implementation returns 0, indicating that no blocks are 589 //! available. 590 unsigned EncoreBootImage::BootCommand::getBlockCount() const 591 { 592 return 1 + getDataBlockCount(); 593 } 594 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. 598 //! 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. 603 //! 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. 606 //! 607 //! \exception std::out_of_range If \a offset is invalid. 608 unsigned EncoreBootImage::BootCommand::getBlocks(unsigned offset, unsigned maxCount, cipher_block_t * data) 609 { 610 assert(data); 611 assert(maxCount >= 1); 612 613 // check for valid offset 614 if (offset >= getBlockCount()) 615 { 616 throw std::out_of_range("invalid offset"); 617 } 618 619 // handle the command header block separately 620 if (offset == 0) 621 { 622 assert(sizeof(boot_command_t) == sizeof(cipher_block_t)); 623 624 boot_command_t header; 625 fillCommandHeader(header); 626 memcpy(data, &header, sizeof(header)); 627 628 return 1; 629 } 630 631 // handle any data blocks 632 return getDataBlocks(offset - 1, maxCount, data); 633 } 634 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. 640 //! 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: 646 //! - CMD_TAG_FIELD 647 //! - CMD_FLAGS_FIELD 648 //! - CMD_ADDRESS_FIELD 649 //! - CMD_COUNT_FIELD 650 //! - CMD_DATA_FIELD 651 //! 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) 654 { 655 // compare all the fields that were requested 656 if ((whichFields & CMD_TAG_FIELD) && (testHeader->m_tag != modelHeader->m_tag)) 657 { 658 throw std::runtime_error("invalid tag field"); 659 } 660 661 if ((whichFields & CMD_FLAGS_FIELD) && (testHeader->m_flags != modelHeader->m_flags)) 662 { 663 throw std::runtime_error("invalid flags field"); 664 } 665 666 if ((whichFields & CMD_ADDRESS_FIELD) && (testHeader->m_address != modelHeader->m_address)) 667 { 668 throw std::runtime_error("invalid address field"); 669 } 670 671 if ((whichFields & CMD_COUNT_FIELD) && (testHeader->m_count != modelHeader->m_count)) 672 { 673 throw std::runtime_error("invalid count field"); 674 } 675 676 if ((whichFields & CMD_DATA_FIELD) && (testHeader->m_data != modelHeader->m_data)) 677 { 678 throw std::runtime_error("invalid data field"); 679 } 680 681 // calculate checksum 682 uint8_t testChecksum = calculateChecksum(*testHeader); 683 if (testChecksum != testHeader->m_checksum) 684 { 685 throw std::runtime_error("invalid checksum"); 686 } 687 } 688 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. 691 //! 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 696 //! on entry! 697 //! 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) 700 { 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); 704 705 *consumed = 1; 706 } 707 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) 713 { 714 header.m_tag = getTag(); 715 header.m_flags = 0; 716 header.m_address = 0; 717 header.m_count = 0; 718 header.m_data = 0; 719 header.m_checksum = calculateChecksum(header); // do this last 720 } 721 722 void EncoreBootImage::NopCommand::debugPrint() const 723 { 724 Log::log(Logger::INFO2, "\tNOOP\n"); 725 } 726 727 //! The identifier, length, and flags fields are taken from \a section. 728 //! 729 //! \todo How does length get set correctly if the length is supposed to include 730 //! this command? 731 EncoreBootImage::TagCommand::TagCommand(const Section & section) 732 { 733 m_sectionIdentifier = section.getIdentifier(); 734 m_sectionLength = section.getBlockCount(); 735 m_sectionFlags = section.getFlags(); 736 } 737 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 742 //! on entry! 743 //! 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) 746 { 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); 750 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); 756 757 *consumed = 1; 758 } 759 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) 763 { 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 770 } 771 772 void EncoreBootImage::TagCommand::debugPrint() const 773 { 774 Log::log(Logger::INFO2, " BTAG | sec=0x%08x | cnt=0x%08x | flg=0x%08x\n", m_sectionIdentifier, m_sectionLength, m_sectionFlags); 775 } 776 777 //! All fields are set to zero. 778 //! 779 EncoreBootImage::LoadCommand::LoadCommand() 780 : BootCommand(), m_data(), m_padCount(0), m_length(0), m_address(0), m_loadDCD(false) 781 { 782 fillPadding(); 783 } 784 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) 787 { 788 fillPadding(); 789 setData(data, length); 790 } 791 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 796 //! on entry! 797 //! 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) 803 { 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); 808 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; 816 817 // make sure there are enough blocks 818 if (count - 1 < dataBlockCount) 819 { 820 throw std::runtime_error("not enough cipher blocks for load data"); 821 } 822 823 // copy data 824 setData(reinterpret_cast<const uint8_t *>(blocks + 1), m_length); 825 826 // copy padding 827 if (m_padCount) 828 { 829 const uint8_t * firstPadByte = reinterpret_cast<const uint8_t *> (blocks + (1 + dataBlockCount)) - m_padCount; 830 memcpy(m_padding, firstPadByte, m_padCount); 831 } 832 833 // check CRC 834 uint32_t actualCRC = calculateCRC(); 835 if (actualCRC != crc) 836 { 837 throw std::runtime_error("load data failed CRC check"); 838 } 839 840 *consumed = 1 + dataBlockCount; 841 } 842 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) 847 { 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()); 853 854 // do this last 855 header.m_checksum = calculateChecksum(header); 856 } 857 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 863 { 864 uint32_t result; 865 CRC32 crc; 866 crc.update(m_data, m_length); 867 if (m_padCount) 868 { 869 // include random padding in the CRC 870 crc.update(m_padding, m_padCount); 871 } 872 crc.truncatedFinal(reinterpret_cast<uint8_t*>(&result), sizeof(result)); 873 874 return result; 875 } 876 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) 882 { 883 assert(data); 884 assert(length); 885 886 uint8_t * dataCopy = new uint8_t[length]; 887 memcpy(dataCopy, data, length); 888 889 m_data = dataCopy; 890 m_length = length; 891 892 m_padCount = sizeOfPaddingForCipherBlocks(m_length); 893 } 894 895 //! \return The number of cipher blocks required to hold the load data, 896 //! rounded up as necessary. 897 unsigned EncoreBootImage::LoadCommand::getDataBlockCount() const 898 { 899 // round up to the next cipher block 900 return numberOfCipherBlocks(m_length); 901 } 902 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. 910 //! 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. 915 //! 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. 918 //! 919 //! \exception std::out_of_range Thrown when offset is invalid. 920 //! 921 //! \todo fill pad bytes with random bytes 922 unsigned EncoreBootImage::LoadCommand::getDataBlocks(unsigned offset, unsigned maxCount, cipher_block_t * data) 923 { 924 assert(data); 925 assert(maxCount != 0); 926 927 uint32_t blockCount = getDataBlockCount(); 928 929 // check offset 930 if (offset >= blockCount) 931 { 932 throw std::out_of_range("invalid offset"); 933 } 934 935 // figure out how many blocks to return 936 unsigned resultBlocks = blockCount - offset; 937 if (resultBlocks > maxCount) 938 { 939 resultBlocks = maxCount; 940 941 // exclude last block if there is padding 942 if (m_padCount && (offset != blockCount - 1) && (offset + resultBlocks == blockCount)) 943 { 944 resultBlocks--; 945 } 946 } 947 948 // if there are pad bytes, handle the last block specially 949 if (m_padCount && offset == blockCount - 1) 950 { 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); 954 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); 960 } 961 else 962 { 963 memcpy(data, &m_data[sizeof(cipher_block_t) * offset], sizeof(cipher_block_t) * resultBlocks); 964 } 965 966 return resultBlocks; 967 } 968 969 //! Fills #m_padding with random bytes that may be used to fill up the last data 970 //! cipher block. 971 void EncoreBootImage::LoadCommand::fillPadding() 972 { 973 RandomNumberGenerator rng; 974 rng.generateBlock(m_padding, sizeof(m_padding)); 975 } 976 977 void EncoreBootImage::LoadCommand::debugPrint() const 978 { 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); 980 } 981 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) 986 { 987 } 988 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 993 //! on entry! 994 //! 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) 997 { 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); 1002 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); 1007 1008 *consumed = 1; 1009 } 1010 1011 void EncoreBootImage::FillCommand::fillCommandHeader(boot_command_t & header) 1012 { 1013 header.m_tag = getTag(); 1014 header.m_flags = 0; 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 1019 } 1020 1021 //! Extends the pattern across 32 bits. 1022 //! 1023 void EncoreBootImage::FillCommand::setPattern(uint8_t pattern) 1024 { 1025 m_pattern = (pattern << 24) | (pattern << 16) | (pattern << 8) | pattern; 1026 } 1027 1028 //! Extends the pattern across 32 bits. 1029 //! 1030 void EncoreBootImage::FillCommand::setPattern(uint16_t pattern) 1031 { 1032 m_pattern = (pattern << 16) | pattern; 1033 } 1034 1035 void EncoreBootImage::FillCommand::setPattern(uint32_t pattern) 1036 { 1037 m_pattern = pattern; 1038 } 1039 1040 void EncoreBootImage::FillCommand::debugPrint() const 1041 { 1042 Log::log(Logger::INFO2, " FILL | adr=0x%08x | len=0x%08x | ptn=0x%08x\n", m_address, m_count, m_pattern); 1043 } 1044 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 1049 //! on entry! 1050 //! 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) 1053 { 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); 1058 1059 // read fields from header 1060 m_mode = ENDIAN_LITTLE_TO_HOST_U32(header->m_data); 1061 1062 *consumed = 1; 1063 } 1064 1065 void EncoreBootImage::ModeCommand::fillCommandHeader(boot_command_t & header) 1066 { 1067 header.m_tag = getTag(); 1068 header.m_flags = 0; 1069 header.m_address = 0; 1070 header.m_count = 0; 1071 header.m_data = ENDIAN_HOST_TO_LITTLE_U32(m_mode); 1072 header.m_checksum = calculateChecksum(header); // do this last 1073 } 1074 1075 void EncoreBootImage::ModeCommand::debugPrint() const 1076 { 1077 Log::log(Logger::INFO2, " MODE | mod=0x%08x\n", m_mode); 1078 } 1079 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 1084 //! on entry! 1085 //! 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) 1088 { 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); 1093 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; 1098 1099 *consumed = 1; 1100 } 1101 1102 void EncoreBootImage::JumpCommand::fillCommandHeader(boot_command_t & header) 1103 { 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); 1107 header.m_count = 0; 1108 header.m_data = ENDIAN_HOST_TO_LITTLE_U32(m_argument); 1109 header.m_checksum = calculateChecksum(header); // do this last 1110 } 1111 1112 void EncoreBootImage::JumpCommand::debugPrint() const 1113 { 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); 1115 } 1116 1117 void EncoreBootImage::CallCommand::debugPrint() const 1118 { 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); 1120 } 1121 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) 1126 { 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()); 1131 1132 // if we're attached to an image, we can compute our real offset 1133 if (m_image) 1134 { 1135 header.m_offset = ENDIAN_HOST_TO_LITTLE_U32(m_image->getSectionOffset(this)); 1136 } 1137 } 1138 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. 1142 //! 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) 1146 { 1147 if (alignment > BOOT_IMAGE_MINIMUM_SECTION_ALIGNMENT) 1148 { 1149 m_alignment = alignment; 1150 } 1151 } 1152 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. 1159 //! 1160 //! \param offset Start offset in cipher blocks (not bytes). 1161 //! 1162 //! \return A number of cipher blocks of padding to insert. 1163 unsigned EncoreBootImage::Section::getPadBlockCountForOffset(unsigned offset) 1164 { 1165 // convert alignment from byte to block alignment 1166 unsigned blockAlignment = m_alignment >> 4; 1167 1168 unsigned nextAlignmentOffset = (offset + blockAlignment - 1) / blockAlignment * blockAlignment; 1169 1170 return nextAlignmentOffset - offset; 1171 } 1172 1173 EncoreBootImage::BootSection::~BootSection() 1174 { 1175 deleteCommands(); 1176 } 1177 1178 void EncoreBootImage::BootSection::deleteCommands() 1179 { 1180 // dispose of all sections 1181 iterator_t it = begin(); 1182 for (; it != end(); ++it) 1183 { 1184 delete *it; 1185 } 1186 } 1187 1188 //! Always returns at least 1 for the required tag command. 1189 //! 1190 unsigned EncoreBootImage::BootSection::getBlockCount() const 1191 { 1192 unsigned count = 0; 1193 1194 const_iterator_t it = begin(); 1195 for (; it != end(); ++it) 1196 { 1197 count += (*it)->getBlockCount(); 1198 } 1199 1200 return count; 1201 } 1202 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. 1207 //! 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. 1212 //! 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) 1216 { 1217 assert(data); 1218 assert(maxCount >= 1); 1219 1220 unsigned currentOffset = 0; 1221 unsigned readCount = maxCount; 1222 1223 iterator_t it = begin(); 1224 for (; it != end(); ++it) 1225 { 1226 BootCommand * command = *it; 1227 unsigned commandBlocks = command->getBlockCount(); 1228 1229 // this should never be false! 1230 assert(offset >= currentOffset); 1231 1232 // skip forward until we hit the requested offset 1233 if (offset >= currentOffset + commandBlocks) 1234 { 1235 currentOffset += commandBlocks; 1236 continue; 1237 } 1238 1239 // read from this command 1240 unsigned commandOffset = offset - currentOffset; 1241 unsigned commandRemaining = commandBlocks - commandOffset; 1242 if (readCount > commandRemaining) 1243 { 1244 readCount = commandRemaining; 1245 } 1246 return command->getBlocks(commandOffset, readCount, data); 1247 } 1248 1249 return 0; 1250 } 1251 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. 1254 //! 1255 //! \param blocks Pointer to the section contents. 1256 //! \param count Number of blocks pointed to by \a blocks. 1257 //! 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) 1261 { 1262 // start with an empty slate 1263 deleteCommands(); 1264 1265 const cipher_block_t * currentBlock = blocks; 1266 unsigned remaining = count; 1267 while (remaining) 1268 { 1269 // try to create a command from the next cipher block. the number of 1270 // blocks the command used up is returned in consumed. 1271 unsigned consumed; 1272 BootCommand * command = BootCommand::createFromData(currentBlock, remaining, &consumed); 1273 if (!command) 1274 { 1275 throw std::runtime_error("invalid boot section data"); 1276 } 1277 1278 addCommand(command); 1279 1280 // update loop counters 1281 remaining -= consumed; 1282 currentBlock += consumed; 1283 } 1284 } 1285 1286 void EncoreBootImage::BootSection::debugPrint() const 1287 { 1288 Log::log(Logger::INFO2, "Boot Section 0x%08x:\n", m_identifier); 1289 1290 const_iterator_t it = begin(); 1291 for (; it != end(); ++it) 1292 { 1293 const BootCommand * command = *it; 1294 command->debugPrint(); 1295 } 1296 } 1297 1298 //! A copy is made of \a data. Any previously assigned data is disposed of. 1299 //! 1300 void EncoreBootImage::DataSection::setData(const uint8_t * data, unsigned length) 1301 { 1302 m_data = new uint8_t[length]; 1303 memcpy(m_data.get(), data, length); 1304 m_length = length; 1305 } 1306 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) 1310 { 1311 m_data = data; 1312 m_length = length; 1313 } 1314 1315 unsigned EncoreBootImage::DataSection::getBlockCount() const 1316 { 1317 return numberOfCipherBlocks(m_length); 1318 } 1319 1320 unsigned EncoreBootImage::DataSection::getBlocks(unsigned offset, unsigned maxCount, cipher_block_t * data) 1321 { 1322 assert(data); 1323 assert(maxCount != 0); 1324 1325 unsigned blockCount = getBlockCount(); 1326 unsigned padCount = sizeOfPaddingForCipherBlocks(m_length); 1327 1328 // check offset 1329 if (offset >= blockCount) 1330 { 1331 throw std::out_of_range("invalid offset"); 1332 } 1333 1334 // figure out how many blocks to return 1335 unsigned resultBlocks = blockCount - offset; 1336 if (resultBlocks > maxCount) 1337 { 1338 resultBlocks = maxCount; 1339 1340 // exclude last block if there is padding 1341 if (padCount && (offset != blockCount - 1) && (offset + resultBlocks == blockCount)) 1342 { 1343 resultBlocks--; 1344 } 1345 } 1346 1347 // if there are pad bytes, handle the last block specially 1348 if (padCount && offset == blockCount - 1) 1349 { 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); 1353 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); 1359 } 1360 else 1361 { 1362 memcpy(data, &m_data[sizeOfCipherBlocks(offset)], sizeOfCipherBlocks(resultBlocks)); 1363 } 1364 1365 return resultBlocks; 1366 } 1367 1368 void EncoreBootImage::DataSection::debugPrint() const 1369 { 1370 Log::log(Logger::INFO2, "Data Section 0x%08x: (%d bytes, %d blocks)\n", m_identifier, m_length, getBlockCount()); 1371 } 1372 1373