1 1.1 jkunz /* 2 1.1 jkunz * File: EncoreBootImage.cpp 3 1.1 jkunz * 4 1.1 jkunz * Copyright (c) Freescale Semiconductor, Inc. All rights reserved. 5 1.1 jkunz * See included license file for license details. 6 1.1 jkunz */ 7 1.1 jkunz 8 1.1 jkunz #include "EncoreBootImage.h" 9 1.1 jkunz #include <stdexcept> 10 1.1 jkunz #include <algorithm> 11 1.1 jkunz #include <time.h> 12 1.1 jkunz #include "crc.h" 13 1.1 jkunz #include "SHA1.h" 14 1.1 jkunz #include "Random.h" 15 1.1 jkunz #include "rijndael.h" 16 1.1 jkunz #include "RijndaelCBCMAC.h" 17 1.1 jkunz #include "Logging.h" 18 1.1 jkunz #include "EndianUtilities.h" 19 1.1 jkunz 20 1.1 jkunz using namespace elftosb; 21 1.1 jkunz 22 1.1 jkunz EncoreBootImage::EncoreBootImage() 23 1.1 jkunz : m_headerFlags(0), 24 1.1 jkunz m_productVersion(), 25 1.1 jkunz m_componentVersion(), 26 1.1 jkunz m_driveTag(0) 27 1.1 jkunz { 28 1.1 jkunz } 29 1.1 jkunz 30 1.1 jkunz EncoreBootImage::~EncoreBootImage() 31 1.1 jkunz { 32 1.1 jkunz // dispose of all sections 33 1.1 jkunz section_iterator_t it = beginSection(); 34 1.1 jkunz for (; it != endSection(); ++it) 35 1.1 jkunz { 36 1.1 jkunz delete *it; 37 1.1 jkunz } 38 1.1 jkunz } 39 1.1 jkunz 40 1.1 jkunz //! \exception std::runtime_error Raised if \a newSection has the same tag as a previously 41 1.1 jkunz //! added section. 42 1.1 jkunz void EncoreBootImage::addSection(Section * newSection) 43 1.1 jkunz { 44 1.1 jkunz // check for another section with this tag 45 1.1 jkunz section_iterator_t it = beginSection(); 46 1.1 jkunz for (; it != endSection(); ++it) 47 1.1 jkunz { 48 1.1 jkunz if ((*it)->getIdentifier() == newSection->getIdentifier()) 49 1.1 jkunz { 50 1.1 jkunz throw std::runtime_error("new section with non-unique tag"); 51 1.1 jkunz } 52 1.1 jkunz } 53 1.1 jkunz 54 1.1 jkunz // no conflicting section tags, so add it 55 1.1 jkunz m_sections.push_back(newSection); 56 1.1 jkunz 57 1.1 jkunz // tell the image who owns it now 58 1.1 jkunz newSection->setImage(this); 59 1.1 jkunz } 60 1.1 jkunz 61 1.1 jkunz EncoreBootImage::section_iterator_t EncoreBootImage::findSection(Section * section) 62 1.1 jkunz { 63 1.1 jkunz return std::find(beginSection(), endSection(), section); 64 1.1 jkunz } 65 1.1 jkunz 66 1.1 jkunz void EncoreBootImage::setProductVersion(const version_t & version) 67 1.1 jkunz { 68 1.1 jkunz m_productVersion = version; 69 1.1 jkunz } 70 1.1 jkunz 71 1.1 jkunz void EncoreBootImage::setComponentVersion(const version_t & version) 72 1.1 jkunz { 73 1.1 jkunz m_componentVersion = version; 74 1.1 jkunz } 75 1.1 jkunz 76 1.1 jkunz //! \todo Optimize writing section data. Right now it only writes one block at a 77 1.1 jkunz //! time, which is of course quite slow (in relative terms). 78 1.1 jkunz //! \todo Refactor this into several different methods for writing each region 79 1.1 jkunz //! of the image. Use a context structure to keep track of shared data between 80 1.1 jkunz //! each of the methods. 81 1.1 jkunz //! \todo Refactor the section and boot tag writing code to only have a single 82 1.1 jkunz //! copy of the block writing and encryption loop. 83 1.1 jkunz void EncoreBootImage::writeToStream(std::ostream & stream) 84 1.1 jkunz { 85 1.1 jkunz // always generate the session key or DEK even if image is unencrypted 86 1.1 jkunz m_sessionKey.randomize(); 87 1.1 jkunz 88 1.1 jkunz // prepare to compute CBC-MACs with each KEK 89 1.1 jkunz unsigned i; 90 1.1 jkunz smart_array_ptr<RijndaelCBCMAC> macs(0); 91 1.1 jkunz if (isEncrypted()) 92 1.1 jkunz { 93 1.1 jkunz macs = new RijndaelCBCMAC[m_keys.size()]; 94 1.1 jkunz for (i=0; i < m_keys.size(); ++i) 95 1.1 jkunz { 96 1.1 jkunz RijndaelCBCMAC mac(m_keys[i]); 97 1.1 jkunz (macs.get())[i] = mac; 98 1.1 jkunz } 99 1.1 jkunz } 100 1.1 jkunz 101 1.1 jkunz // prepare to compute SHA-1 digest over entire image 102 1.1 jkunz CSHA1 hash; 103 1.1 jkunz hash.Reset(); 104 1.1 jkunz 105 1.1 jkunz // count of total blocks written to the file 106 1.1 jkunz unsigned fileBlocksWritten = 0; 107 1.1 jkunz 108 1.1 jkunz // we need some pieces of the header down below 109 1.1 jkunz boot_image_header_t imageHeader; 110 1.1 jkunz prepareImageHeader(imageHeader); 111 1.1 jkunz 112 1.1 jkunz // write plaintext header 113 1.1 jkunz { 114 1.1 jkunz // write header 115 1.1 jkunz assert(sizeOfPaddingForCipherBlocks(sizeof(boot_image_header_t)) == 0); 116 1.1 jkunz stream.write(reinterpret_cast<char *>(&imageHeader), sizeof(imageHeader)); 117 1.1 jkunz fileBlocksWritten += numberOfCipherBlocks(sizeof(imageHeader)); 118 1.1 jkunz 119 1.1 jkunz // update CBC-MAC over image header 120 1.1 jkunz if (isEncrypted()) 121 1.1 jkunz { 122 1.1 jkunz for (i=0; i < m_keys.size(); ++i) 123 1.1 jkunz { 124 1.1 jkunz (macs.get())[i].update(reinterpret_cast<uint8_t *>(&imageHeader), sizeof(imageHeader)); 125 1.1 jkunz } 126 1.1 jkunz } 127 1.1 jkunz 128 1.1 jkunz // update SHA-1 129 1.1 jkunz hash.Update(reinterpret_cast<uint8_t *>(&imageHeader), sizeof(imageHeader)); 130 1.1 jkunz } 131 1.1 jkunz 132 1.1 jkunz // write plaintext section table 133 1.1 jkunz { 134 1.1 jkunz section_iterator_t it = beginSection(); 135 1.1 jkunz for (; it != endSection(); ++it) 136 1.1 jkunz { 137 1.1 jkunz Section * section = *it; 138 1.1 jkunz 139 1.1 jkunz // write header for this section 140 1.1 jkunz assert(sizeOfPaddingForCipherBlocks(sizeof(section_header_t)) == 0); 141 1.1 jkunz section_header_t sectionHeader; 142 1.1 jkunz section->fillSectionHeader(sectionHeader); 143 1.1 jkunz stream.write(reinterpret_cast<char *>(§ionHeader), sizeof(sectionHeader)); 144 1.1 jkunz fileBlocksWritten += numberOfCipherBlocks(sizeof(sectionHeader)); 145 1.1 jkunz 146 1.1 jkunz // update CBC-MAC over this entry 147 1.1 jkunz if (isEncrypted()) 148 1.1 jkunz { 149 1.1 jkunz for (i=0; i < m_keys.size(); ++i) 150 1.1 jkunz { 151 1.1 jkunz (macs.get())[i].update(reinterpret_cast<uint8_t *>(§ionHeader), sizeof(sectionHeader)); 152 1.1 jkunz } 153 1.1 jkunz } 154 1.1 jkunz 155 1.1 jkunz // update SHA-1 156 1.1 jkunz hash.Update(reinterpret_cast<uint8_t *>(§ionHeader), sizeof(sectionHeader)); 157 1.1 jkunz } 158 1.1 jkunz } 159 1.1 jkunz 160 1.1 jkunz // finished with the CBC-MAC 161 1.1 jkunz if (isEncrypted()) 162 1.1 jkunz { 163 1.1 jkunz for (i=0; i < m_keys.size(); ++i) 164 1.1 jkunz { 165 1.1 jkunz (macs.get())[i].finalize(); 166 1.1 jkunz } 167 1.1 jkunz } 168 1.1 jkunz 169 1.1 jkunz // write key dictionary 170 1.1 jkunz if (isEncrypted()) 171 1.1 jkunz { 172 1.1 jkunz key_iterator_t it = beginKeys(); 173 1.1 jkunz for (i=0; it != endKeys(); ++it, ++i) 174 1.1 jkunz { 175 1.1 jkunz // write CBC-MAC result for this key, then update SHA-1 176 1.1 jkunz RijndaelCBCMAC & mac = (macs.get())[i]; 177 1.1 jkunz const RijndaelCBCMAC::block_t & macResult = mac.getMAC(); 178 1.1 jkunz stream.write(reinterpret_cast<const char *>(&macResult), sizeof(RijndaelCBCMAC::block_t)); 179 1.1 jkunz hash.Update(reinterpret_cast<const uint8_t *>(&macResult), sizeof(RijndaelCBCMAC::block_t)); 180 1.1 jkunz fileBlocksWritten++; 181 1.1 jkunz 182 1.1 jkunz // encrypt DEK with this key, write it out, and update image digest 183 1.1 jkunz Rijndael cipher; 184 1.1 jkunz cipher.init(Rijndael::CBC, Rijndael::Encrypt, *it, Rijndael::Key16Bytes, imageHeader.m_iv); 185 1.1 jkunz AESKey<128>::key_t wrappedSessionKey; 186 1.1 jkunz cipher.blockEncrypt(m_sessionKey, sizeof(AESKey<128>::key_t) * 8, wrappedSessionKey); 187 1.1 jkunz stream.write(reinterpret_cast<char *>(&wrappedSessionKey), sizeof(wrappedSessionKey)); 188 1.1 jkunz hash.Update(reinterpret_cast<uint8_t *>(&wrappedSessionKey), sizeof(wrappedSessionKey)); 189 1.1 jkunz fileBlocksWritten++; 190 1.1 jkunz } 191 1.1 jkunz } 192 1.1 jkunz 193 1.1 jkunz // write sections and boot tags 194 1.1 jkunz { 195 1.1 jkunz section_iterator_t it = beginSection(); 196 1.1 jkunz for (; it != endSection(); ++it) 197 1.1 jkunz { 198 1.1 jkunz section_iterator_t itCopy = it; 199 1.1 jkunz bool isLastSection = (++itCopy == endSection()); 200 1.1 jkunz 201 1.1 jkunz Section * section = *it; 202 1.1 jkunz cipher_block_t block; 203 1.1 jkunz unsigned blockCount = section->getBlockCount(); 204 1.1 jkunz unsigned blocksWritten = 0; 205 1.1 jkunz 206 1.1 jkunz Rijndael cipher; 207 1.1 jkunz cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, imageHeader.m_iv); 208 1.1 jkunz 209 1.1 jkunz // Compute the number of padding blocks needed to align the section. This first 210 1.1 jkunz // call to getPadBlockCountForOffset() passes an offset that excludes 211 1.1 jkunz // the boot tag for this section. 212 1.1 jkunz unsigned paddingBlocks = getPadBlockCountForSection(section, fileBlocksWritten); 213 1.1 jkunz 214 1.1 jkunz // Insert nop commands as padding to align the start of the section, if 215 1.1 jkunz // the section has special alignment requirements. 216 1.1 jkunz NopCommand nop; 217 1.1 jkunz while (paddingBlocks--) 218 1.1 jkunz { 219 1.1 jkunz blockCount = nop.getBlockCount(); 220 1.1 jkunz blocksWritten = 0; 221 1.1 jkunz while (blocksWritten < blockCount) 222 1.1 jkunz { 223 1.1 jkunz nop.getBlocks(blocksWritten, 1, &block); 224 1.1 jkunz 225 1.1 jkunz if (isEncrypted()) 226 1.1 jkunz { 227 1.1 jkunz // re-init after encrypt to update IV 228 1.1 jkunz cipher.blockEncrypt(block, sizeof(cipher_block_t) * 8, block); 229 1.1 jkunz cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, block); 230 1.1 jkunz } 231 1.1 jkunz 232 1.1 jkunz stream.write(reinterpret_cast<char *>(&block), sizeof(cipher_block_t)); 233 1.1 jkunz hash.Update(reinterpret_cast<uint8_t *>(&block), sizeof(cipher_block_t)); 234 1.1 jkunz 235 1.1 jkunz blocksWritten++; 236 1.1 jkunz fileBlocksWritten++; 237 1.1 jkunz } 238 1.1 jkunz } 239 1.1 jkunz 240 1.1 jkunz // reinit cipher for boot tag 241 1.1 jkunz cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, imageHeader.m_iv); 242 1.1 jkunz 243 1.1 jkunz // write boot tag 244 1.1 jkunz TagCommand tag(*section); 245 1.1 jkunz tag.setLast(isLastSection); 246 1.1 jkunz if (!isLastSection) 247 1.1 jkunz { 248 1.1 jkunz // If this isn't the last section, the tag needs to include any 249 1.1 jkunz // padding for the next section in its length, otherwise the ROM 250 1.1 jkunz // won't be able to find the next section's boot tag. 251 1.1 jkunz unsigned nextSectionOffset = fileBlocksWritten + section->getBlockCount() + 1; 252 1.1 jkunz tag.setSectionLength(section->getBlockCount() + getPadBlockCountForSection(*itCopy, nextSectionOffset)); 253 1.1 jkunz } 254 1.1 jkunz blockCount = tag.getBlockCount(); 255 1.1 jkunz blocksWritten = 0; 256 1.1 jkunz while (blocksWritten < blockCount) 257 1.1 jkunz { 258 1.1 jkunz tag.getBlocks(blocksWritten, 1, &block); 259 1.1 jkunz 260 1.1 jkunz if (isEncrypted()) 261 1.1 jkunz { 262 1.1 jkunz // re-init after encrypt to update IV 263 1.1 jkunz cipher.blockEncrypt(block, sizeof(cipher_block_t) * 8, block); 264 1.1 jkunz cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, block); 265 1.1 jkunz } 266 1.1 jkunz 267 1.1 jkunz stream.write(reinterpret_cast<char *>(&block), sizeof(cipher_block_t)); 268 1.1 jkunz hash.Update(reinterpret_cast<uint8_t *>(&block), sizeof(cipher_block_t)); 269 1.1 jkunz 270 1.1 jkunz blocksWritten++; 271 1.1 jkunz fileBlocksWritten++; 272 1.1 jkunz } 273 1.1 jkunz 274 1.1 jkunz // reinit cipher for section data 275 1.1 jkunz cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, imageHeader.m_iv); 276 1.1 jkunz 277 1.1 jkunz // write section data 278 1.1 jkunz blockCount = section->getBlockCount(); 279 1.1 jkunz blocksWritten = 0; 280 1.1 jkunz while (blocksWritten < blockCount) 281 1.1 jkunz { 282 1.1 jkunz section->getBlocks(blocksWritten, 1, &block); 283 1.1 jkunz 284 1.1 jkunz // Only encrypt the section contents if the entire boot image is encrypted 285 1.1 jkunz // and the section doesn't have the "leave unencrypted" flag set. Even if the 286 1.1 jkunz // section is unencrypted the boot tag will remain encrypted. 287 1.1 jkunz if (isEncrypted() && !section->getLeaveUnencrypted()) 288 1.1 jkunz { 289 1.1 jkunz // re-init after encrypt to update IV 290 1.1 jkunz cipher.blockEncrypt(block, sizeof(cipher_block_t) * 8, block); 291 1.1 jkunz cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, block); 292 1.1 jkunz } 293 1.1 jkunz 294 1.1 jkunz stream.write(reinterpret_cast<char *>(&block), sizeof(cipher_block_t)); 295 1.1 jkunz hash.Update(reinterpret_cast<uint8_t *>(&block), sizeof(cipher_block_t)); 296 1.1 jkunz 297 1.1 jkunz blocksWritten++; 298 1.1 jkunz fileBlocksWritten++; 299 1.1 jkunz } 300 1.1 jkunz } 301 1.1 jkunz } 302 1.1 jkunz 303 1.1 jkunz // write SHA-1 digest over entire image 304 1.1 jkunz { 305 1.1 jkunz // allocate enough room for digest and bytes to pad out to the next cipher block 306 1.1 jkunz const unsigned padBytes = sizeOfPaddingForCipherBlocks(sizeof(sha1_digest_t)); 307 1.1 jkunz unsigned digestBlocksSize = sizeof(sha1_digest_t) + padBytes; 308 1.1 jkunz smart_array_ptr<uint8_t> digestBlocks = new uint8_t[digestBlocksSize]; 309 1.1 jkunz hash.Final(); 310 1.1 jkunz hash.GetHash(digestBlocks.get()); 311 1.1 jkunz 312 1.1 jkunz // set the pad bytes to random values 313 1.1 jkunz RandomNumberGenerator rng; 314 1.1 jkunz rng.generateBlock(&(digestBlocks.get())[sizeof(sha1_digest_t)], padBytes); 315 1.1 jkunz 316 1.1 jkunz // encrypt with session key 317 1.1 jkunz if (isEncrypted()) 318 1.1 jkunz { 319 1.1 jkunz Rijndael cipher; 320 1.1 jkunz cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, imageHeader.m_iv); 321 1.1 jkunz cipher.blockEncrypt(digestBlocks.get(), digestBlocksSize * 8, digestBlocks.get()); 322 1.1 jkunz } 323 1.1 jkunz 324 1.1 jkunz // write to the stream 325 1.1 jkunz stream.write(reinterpret_cast<char *>(digestBlocks.get()), digestBlocksSize); 326 1.1 jkunz } 327 1.1 jkunz } 328 1.1 jkunz 329 1.1 jkunz void EncoreBootImage::prepareImageHeader(boot_image_header_t & header) 330 1.1 jkunz { 331 1.1 jkunz // get identifier for the first bootable section 332 1.1 jkunz Section * firstBootSection = findFirstBootableSection(); 333 1.1 jkunz section_id_t firstBootSectionID = 0; 334 1.1 jkunz if (firstBootSection) 335 1.1 jkunz { 336 1.1 jkunz firstBootSectionID = firstBootSection->getIdentifier(); 337 1.1 jkunz } 338 1.1 jkunz 339 1.1 jkunz // fill in header fields 340 1.1 jkunz header.m_signature[0] = 'S'; 341 1.1 jkunz header.m_signature[1] = 'T'; 342 1.1 jkunz header.m_signature[2] = 'M'; 343 1.1 jkunz header.m_signature[3] = 'P'; 344 1.1 jkunz header.m_majorVersion = ROM_BOOT_IMAGE_MAJOR_VERSION; 345 1.1 jkunz header.m_minorVersion = ROM_BOOT_IMAGE_MINOR_VERSION; 346 1.1 jkunz header.m_flags = ENDIAN_HOST_TO_LITTLE_U16(m_headerFlags); 347 1.1 jkunz header.m_imageBlocks = ENDIAN_HOST_TO_LITTLE_U32(getImageSize()); 348 1.1 jkunz header.m_firstBootableSectionID = ENDIAN_HOST_TO_LITTLE_U32(firstBootSectionID); 349 1.1 jkunz header.m_keyCount = ENDIAN_HOST_TO_LITTLE_U16((uint16_t)m_keys.size()); 350 1.1 jkunz header.m_headerBlocks = ENDIAN_HOST_TO_LITTLE_U16((uint16_t)numberOfCipherBlocks(sizeof(header))); 351 1.1 jkunz header.m_sectionCount = ENDIAN_HOST_TO_LITTLE_U16((uint16_t)m_sections.size()); 352 1.1 jkunz header.m_sectionHeaderSize = ENDIAN_HOST_TO_LITTLE_U16((uint16_t)numberOfCipherBlocks(sizeof(section_header_t))); 353 1.1 jkunz header.m_signature2[0] = 's'; 354 1.1 jkunz header.m_signature2[1] = 'g'; 355 1.1 jkunz header.m_signature2[2] = 't'; 356 1.1 jkunz header.m_signature2[3] = 'l'; 357 1.1 jkunz header.m_timestamp = ENDIAN_HOST_TO_LITTLE_U64(getTimestamp()); 358 1.1 jkunz header.m_driveTag = m_driveTag; 359 1.1 jkunz 360 1.1 jkunz // Prepare version fields by converting them to the correct byte order. 361 1.1 jkunz header.m_productVersion = m_productVersion; 362 1.1 jkunz header.m_componentVersion = m_componentVersion; 363 1.1 jkunz header.m_productVersion.fixByteOrder(); 364 1.1 jkunz header.m_componentVersion.fixByteOrder(); 365 1.1 jkunz 366 1.1 jkunz // the fields are dependant on others 367 1.1 jkunz header.m_keyDictionaryBlock = ENDIAN_HOST_TO_LITTLE_U16(header.m_headerBlocks + header.m_sectionCount * header.m_sectionHeaderSize); 368 1.1 jkunz header.m_firstBootTagBlock = ENDIAN_HOST_TO_LITTLE_U32(header.m_keyDictionaryBlock + header.m_keyCount * 2); 369 1.1 jkunz 370 1.1 jkunz // generate random pad bytes 371 1.1 jkunz RandomNumberGenerator rng; 372 1.1 jkunz rng.generateBlock(header.m_padding0, sizeof(header.m_padding0)); 373 1.1 jkunz rng.generateBlock(header.m_padding1, sizeof(header.m_padding1)); 374 1.1 jkunz 375 1.1 jkunz // compute SHA-1 digest over the image header 376 1.1 jkunz uint8_t * message = reinterpret_cast<uint8_t *>(&header.m_signature); 377 1.1 jkunz uint32_t length = static_cast<uint32_t>(sizeof(header) - sizeof(header.m_digest)); // include padding 378 1.1 jkunz 379 1.1 jkunz CSHA1 hash; 380 1.1 jkunz hash.Reset(); 381 1.1 jkunz hash.Update(message, length); 382 1.1 jkunz hash.Final(); 383 1.1 jkunz hash.GetHash(header.m_digest); 384 1.1 jkunz } 385 1.1 jkunz 386 1.1 jkunz //! Returns the number of microseconds since 00:00 1-1-2000. In actuality, the timestamp 387 1.1 jkunz //! is only accurate to seconds, and is simply extended out to microseconds. 388 1.1 jkunz //! 389 1.1 jkunz //! \todo Use the operating system's low-level functions to get a true microsecond 390 1.1 jkunz //! timestamp, instead of faking it like we do now. 391 1.1 jkunz //! \bug The timestamp might be off an hour. 392 1.1 jkunz uint64_t EncoreBootImage::getTimestamp() 393 1.1 jkunz { 394 1.3 hans #if defined(WIN32) || defined(__CYGWIN__) || defined(__sun) 395 1.1 jkunz struct tm epoch = { 0, 0, 0, 1, 0, 100, 0, 0 }; // 00:00 1-1-2000 396 1.1 jkunz #else 397 1.1 jkunz struct tm epoch = { 0, 0, 0, 1, 0, 100, 0, 0, 1, 0, NULL }; // 00:00 1-1-2000 398 1.1 jkunz #endif 399 1.1 jkunz time_t epochTime = mktime(&epoch); 400 1.1 jkunz time_t now = time(NULL); 401 1.1 jkunz now -= epochTime; 402 1.1 jkunz uint64_t microNow = uint64_t(now) * 1000000; // convert to microseconds 403 1.1 jkunz return microNow; 404 1.1 jkunz } 405 1.1 jkunz 406 1.1 jkunz //! Scans the section list looking for the first section which has 407 1.1 jkunz //! the #ROM_SECTION_BOOTABLE flag set on it. 408 1.1 jkunz EncoreBootImage::Section * EncoreBootImage::findFirstBootableSection() 409 1.1 jkunz { 410 1.1 jkunz section_iterator_t it = beginSection(); 411 1.1 jkunz for (; it != endSection(); ++it) 412 1.1 jkunz { 413 1.1 jkunz if ((*it)->getFlags() & ROM_SECTION_BOOTABLE) 414 1.1 jkunz { 415 1.1 jkunz return *it; 416 1.1 jkunz } 417 1.1 jkunz } 418 1.1 jkunz 419 1.1 jkunz // no bootable sections were found 420 1.1 jkunz return NULL; 421 1.1 jkunz } 422 1.1 jkunz 423 1.1 jkunz //! The boot tag for \a section is taken into account, thus making the 424 1.1 jkunz //! result offset point to the first block of the actual section data. 425 1.1 jkunz //! 426 1.1 jkunz //! \note The offset will only be valid if all encryption keys and all 427 1.1 jkunz //! sections have already been added to the image. 428 1.1 jkunz uint32_t EncoreBootImage::getSectionOffset(Section * section) 429 1.1 jkunz { 430 1.1 jkunz // start with boot image headers 431 1.1 jkunz uint32_t offset = numberOfCipherBlocks(sizeof(boot_image_header_t)); // header 432 1.1 jkunz offset += numberOfCipherBlocks(sizeof(section_header_t)) * sectionCount(); // section table 433 1.1 jkunz offset += 2 * keyCount(); // key dictiontary 434 1.1 jkunz 435 1.1 jkunz // add up sections before this one 436 1.1 jkunz section_iterator_t it = beginSection(); 437 1.1 jkunz for (; it != endSection() && *it != section; ++it) 438 1.1 jkunz { 439 1.1 jkunz Section * thisSection = *it; 440 1.1 jkunz 441 1.1 jkunz // insert padding for section alignment 442 1.1 jkunz offset += getPadBlockCountForSection(thisSection, offset); 443 1.1 jkunz 444 1.1 jkunz // add one for boot tag associated with this section 445 1.1 jkunz offset++; 446 1.1 jkunz 447 1.1 jkunz // now add the section's contents 448 1.1 jkunz offset += thisSection->getBlockCount(); 449 1.1 jkunz } 450 1.1 jkunz 451 1.1 jkunz // and add padding for this section 452 1.1 jkunz offset += getPadBlockCountForSection(section, offset); 453 1.1 jkunz 454 1.1 jkunz // skip over this section's boot tag 455 1.1 jkunz offset++; 456 1.1 jkunz 457 1.1 jkunz return offset; 458 1.1 jkunz } 459 1.1 jkunz 460 1.1 jkunz //! Computes the number of blocks of padding required to align \a section while 461 1.1 jkunz //! taking into account the boot tag that gets inserted before the section contents. 462 1.1 jkunz unsigned EncoreBootImage::getPadBlockCountForSection(Section * section, unsigned offset) 463 1.1 jkunz { 464 1.1 jkunz // Compute the number of padding blocks needed to align the section. This first 465 1.1 jkunz // call to getPadBlockCountForOffset() passes an offset that excludes 466 1.1 jkunz // the boot tag for this section. 467 1.1 jkunz unsigned paddingBlocks = section->getPadBlockCountForOffset(offset); 468 1.1 jkunz 469 1.1 jkunz // If the pad count comes back as 0 then we need to try again with an offset that 470 1.1 jkunz // includes the boot tag. This is all because we're aligning the section contents 471 1.1 jkunz // start and not the section's boot tag. 472 1.1 jkunz if (paddingBlocks == 0) 473 1.1 jkunz { 474 1.1 jkunz paddingBlocks = section->getPadBlockCountForOffset(offset + 1); 475 1.1 jkunz } 476 1.1 jkunz // Otherwise if we get a nonzero pad amount then we need to subtract the block 477 1.1 jkunz // for the section's boot tag from the pad count. 478 1.1 jkunz else 479 1.1 jkunz { 480 1.1 jkunz paddingBlocks--; 481 1.1 jkunz } 482 1.1 jkunz 483 1.1 jkunz return paddingBlocks; 484 1.1 jkunz } 485 1.1 jkunz 486 1.1 jkunz uint32_t EncoreBootImage::getImageSize() 487 1.1 jkunz { 488 1.1 jkunz // determine to total size of the image 489 1.1 jkunz const uint32_t headerBlocks = numberOfCipherBlocks(sizeof(boot_image_header_t)); 490 1.1 jkunz const uint32_t sectionHeaderSize = numberOfCipherBlocks(sizeof(section_header_t)); 491 1.1 jkunz uint32_t imageBlocks = headerBlocks; 492 1.1 jkunz imageBlocks += sectionHeaderSize * m_sections.size(); // section table 493 1.1 jkunz imageBlocks += 2 * m_keys.size(); // key dict 494 1.1 jkunz 495 1.1 jkunz // add in each section's size 496 1.1 jkunz section_iterator_t it = beginSection(); 497 1.1 jkunz for (; it != endSection(); ++it) 498 1.1 jkunz { 499 1.1 jkunz // add in this section's size, padding to align it, and its boot tag 500 1.1 jkunz imageBlocks += getPadBlockCountForSection(*it, imageBlocks); 501 1.1 jkunz imageBlocks += (*it)->getBlockCount(); 502 1.1 jkunz imageBlocks++; 503 1.1 jkunz } 504 1.1 jkunz 505 1.1 jkunz // image MAC 506 1.1 jkunz imageBlocks += 2; 507 1.1 jkunz 508 1.1 jkunz return imageBlocks; 509 1.1 jkunz } 510 1.1 jkunz 511 1.1 jkunz void EncoreBootImage::debugPrint() const 512 1.1 jkunz { 513 1.1 jkunz const_section_iterator_t it = beginSection(); 514 1.1 jkunz for (; it != endSection(); ++it) 515 1.1 jkunz { 516 1.1 jkunz const Section * section = *it; 517 1.1 jkunz section->debugPrint(); 518 1.1 jkunz } 519 1.1 jkunz } 520 1.1 jkunz 521 1.1 jkunz //! \param blocks Pointer to the raw data blocks. 522 1.1 jkunz //! \param count Number of blocks pointed to by \a blocks. 523 1.1 jkunz //! \param[out] consumed On exit, this points to the number of cipher blocks that were occupied 524 1.1 jkunz //! by the command. Should be at least 1 for every command. This must not be NULL 525 1.1 jkunz //! on entry! 526 1.1 jkunz //! 527 1.1 jkunz //! \return A new boot command instance. 528 1.1 jkunz //! \retval NULL The boot command pointed to by \a blocks was not recognized as a known 529 1.1 jkunz //! command type. 530 1.1 jkunz //! 531 1.1 jkunz //! \exception std::runtime_error This exception indicates that a command was recognized 532 1.1 jkunz //! but contained invalid data. Compare this to a NULL result which indicates that 533 1.1 jkunz //! no command was recognized at all. 534 1.1 jkunz EncoreBootImage::BootCommand * EncoreBootImage::BootCommand::createFromData(const cipher_block_t * blocks, unsigned count, unsigned * consumed) 535 1.1 jkunz { 536 1.1 jkunz const boot_command_t * header = reinterpret_cast<const boot_command_t *>(blocks); 537 1.1 jkunz BootCommand * command = NULL; 538 1.1 jkunz 539 1.1 jkunz switch (header->m_tag) 540 1.1 jkunz { 541 1.1 jkunz case ROM_NOP_CMD: 542 1.1 jkunz command = new NopCommand(); 543 1.1 jkunz break; 544 1.1 jkunz case ROM_TAG_CMD: 545 1.1 jkunz command = new TagCommand(); 546 1.1 jkunz break; 547 1.1 jkunz case ROM_LOAD_CMD: 548 1.1 jkunz command = new LoadCommand(); 549 1.1 jkunz break; 550 1.1 jkunz case ROM_FILL_CMD: 551 1.1 jkunz command = new FillCommand(); 552 1.1 jkunz break; 553 1.1 jkunz case ROM_MODE_CMD: 554 1.1 jkunz command = new ModeCommand(); 555 1.1 jkunz break; 556 1.1 jkunz case ROM_JUMP_CMD: 557 1.1 jkunz command = new JumpCommand(); 558 1.1 jkunz break; 559 1.1 jkunz case ROM_CALL_CMD: 560 1.1 jkunz command = new CallCommand(); 561 1.1 jkunz break; 562 1.1 jkunz } 563 1.1 jkunz 564 1.1 jkunz if (command) 565 1.1 jkunz { 566 1.1 jkunz command->initFromData(blocks, count, consumed); 567 1.1 jkunz } 568 1.1 jkunz return command; 569 1.1 jkunz } 570 1.1 jkunz 571 1.1 jkunz //! The checksum algorithm is totally straightforward, except that the 572 1.1 jkunz //! initial checksum byte value is set to 0x5a instead of 0. 573 1.1 jkunz uint8_t EncoreBootImage::BootCommand::calculateChecksum(const boot_command_t & header) 574 1.1 jkunz { 575 1.1 jkunz const uint8_t * bytes = reinterpret_cast<const uint8_t *>(&header); 576 1.1 jkunz uint8_t checksum = 0x5a; 577 1.1 jkunz int i; 578 1.1 jkunz 579 1.1 jkunz // start at one to skip checksum field 580 1.1 jkunz for (i = 1; i < sizeof(header); ++i) 581 1.1 jkunz { 582 1.1 jkunz checksum += bytes[i]; 583 1.1 jkunz } 584 1.1 jkunz 585 1.1 jkunz return checksum; 586 1.1 jkunz } 587 1.1 jkunz 588 1.1 jkunz //! The default implementation returns 0, indicating that no blocks are 589 1.1 jkunz //! available. 590 1.1 jkunz unsigned EncoreBootImage::BootCommand::getBlockCount() const 591 1.1 jkunz { 592 1.1 jkunz return 1 + getDataBlockCount(); 593 1.1 jkunz } 594 1.1 jkunz 595 1.1 jkunz //! Up to \a maxCount cipher blocks are copied into the buffer pointed to by 596 1.1 jkunz //! the \a data argument. The index of the first block to copy is 597 1.1 jkunz //! held in the \a offset argument. 598 1.1 jkunz //! 599 1.1 jkunz //! \param offset Starting block number to copy. Zero means the first available block. 600 1.1 jkunz //! \param maxCount Up to this number of blocks may be copied into \a data. Must be 1 or greater. 601 1.1 jkunz //! \param data Buffer for outgoing cipher blocks. Must have enough room to hold 602 1.1 jkunz //! \a maxCount blocks. 603 1.1 jkunz //! 604 1.1 jkunz //! \return The number of cipher blocks copied into \a data. 605 1.1 jkunz //! \retval 0 No more blocks are available and nothing was written to \a data. 606 1.1 jkunz //! 607 1.1 jkunz //! \exception std::out_of_range If \a offset is invalid. 608 1.1 jkunz unsigned EncoreBootImage::BootCommand::getBlocks(unsigned offset, unsigned maxCount, cipher_block_t * data) 609 1.1 jkunz { 610 1.1 jkunz assert(data); 611 1.1 jkunz assert(maxCount >= 1); 612 1.1 jkunz 613 1.1 jkunz // check for valid offset 614 1.1 jkunz if (offset >= getBlockCount()) 615 1.1 jkunz { 616 1.1 jkunz throw std::out_of_range("invalid offset"); 617 1.1 jkunz } 618 1.1 jkunz 619 1.1 jkunz // handle the command header block separately 620 1.1 jkunz if (offset == 0) 621 1.1 jkunz { 622 1.1 jkunz assert(sizeof(boot_command_t) == sizeof(cipher_block_t)); 623 1.1 jkunz 624 1.1 jkunz boot_command_t header; 625 1.1 jkunz fillCommandHeader(header); 626 1.1 jkunz memcpy(data, &header, sizeof(header)); 627 1.1 jkunz 628 1.1 jkunz return 1; 629 1.1 jkunz } 630 1.1 jkunz 631 1.1 jkunz // handle any data blocks 632 1.1 jkunz return getDataBlocks(offset - 1, maxCount, data); 633 1.1 jkunz } 634 1.1 jkunz 635 1.1 jkunz //! The checksum field of \a testHeader is always computed and checked against itself. 636 1.1 jkunz //! All other fields are compared to the corresponding value set in \a modelHeader 637 1.1 jkunz //! if the appropriate flag is set in \a whichFields. For example, the m_address fields 638 1.1 jkunz //! in \a testHeader and \a modelHeader are compared when the CMD_ADDRESS_FIELD bit 639 1.1 jkunz //! is set in \a whichFields. An exception is thrown if any comparison fails. 640 1.1 jkunz //! 641 1.1 jkunz //! \param modelHeader The baseline header to compare against. Only those fields that 642 1.1 jkunz //! have corresponding bits set in \a whichFields need to be set. 643 1.1 jkunz //! \param testHeader The actual command header which is being validated. 644 1.1 jkunz //! \param whichFields A bitfield used to determine which fields of the boot command 645 1.1 jkunz //! header are compared. Possible values are: 646 1.1 jkunz //! - CMD_TAG_FIELD 647 1.1 jkunz //! - CMD_FLAGS_FIELD 648 1.1 jkunz //! - CMD_ADDRESS_FIELD 649 1.1 jkunz //! - CMD_COUNT_FIELD 650 1.1 jkunz //! - CMD_DATA_FIELD 651 1.1 jkunz //! 652 1.1 jkunz //! \exception std::runtime_error Thrown if any requested validation fails. 653 1.1 jkunz void EncoreBootImage::BootCommand::validateHeader(const boot_command_t * modelHeader, const boot_command_t * testHeader, unsigned whichFields) 654 1.1 jkunz { 655 1.1 jkunz // compare all the fields that were requested 656 1.1 jkunz if ((whichFields & CMD_TAG_FIELD) && (testHeader->m_tag != modelHeader->m_tag)) 657 1.1 jkunz { 658 1.1 jkunz throw std::runtime_error("invalid tag field"); 659 1.1 jkunz } 660 1.1 jkunz 661 1.1 jkunz if ((whichFields & CMD_FLAGS_FIELD) && (testHeader->m_flags != modelHeader->m_flags)) 662 1.1 jkunz { 663 1.1 jkunz throw std::runtime_error("invalid flags field"); 664 1.1 jkunz } 665 1.1 jkunz 666 1.1 jkunz if ((whichFields & CMD_ADDRESS_FIELD) && (testHeader->m_address != modelHeader->m_address)) 667 1.1 jkunz { 668 1.1 jkunz throw std::runtime_error("invalid address field"); 669 1.1 jkunz } 670 1.1 jkunz 671 1.1 jkunz if ((whichFields & CMD_COUNT_FIELD) && (testHeader->m_count != modelHeader->m_count)) 672 1.1 jkunz { 673 1.1 jkunz throw std::runtime_error("invalid count field"); 674 1.1 jkunz } 675 1.1 jkunz 676 1.1 jkunz if ((whichFields & CMD_DATA_FIELD) && (testHeader->m_data != modelHeader->m_data)) 677 1.1 jkunz { 678 1.1 jkunz throw std::runtime_error("invalid data field"); 679 1.1 jkunz } 680 1.1 jkunz 681 1.1 jkunz // calculate checksum 682 1.1 jkunz uint8_t testChecksum = calculateChecksum(*testHeader); 683 1.1 jkunz if (testChecksum != testHeader->m_checksum) 684 1.1 jkunz { 685 1.1 jkunz throw std::runtime_error("invalid checksum"); 686 1.1 jkunz } 687 1.1 jkunz } 688 1.1 jkunz 689 1.1 jkunz //! Since the NOP command has no data, this method just validates the command header. 690 1.1 jkunz //! All fields except the checksum are expected to be set to 0. 691 1.1 jkunz //! 692 1.1 jkunz //! \param blocks Pointer to the raw data blocks. 693 1.1 jkunz //! \param count Number of blocks pointed to by \a blocks. 694 1.1 jkunz //! \param[out] consumed On exit, this points to the number of cipher blocks that were occupied 695 1.1 jkunz //! by the command. Should be at least 1 for every command. This must not be NULL 696 1.1 jkunz //! on entry! 697 1.1 jkunz //! 698 1.1 jkunz //! \exception std::runtime_error Thrown if header fields are invalid. 699 1.1 jkunz void EncoreBootImage::NopCommand::initFromData(const cipher_block_t * blocks, unsigned count, unsigned * consumed) 700 1.1 jkunz { 701 1.1 jkunz const boot_command_t model = { 0, ROM_NOP_CMD, 0, 0, 0, 0 }; 702 1.1 jkunz const boot_command_t * header = reinterpret_cast<const boot_command_t *>(blocks); 703 1.1 jkunz validateHeader(&model, header, CMD_TAG_FIELD | CMD_FLAGS_FIELD | CMD_ADDRESS_FIELD | CMD_COUNT_FIELD | CMD_DATA_FIELD); 704 1.1 jkunz 705 1.1 jkunz *consumed = 1; 706 1.1 jkunz } 707 1.1 jkunz 708 1.1 jkunz //! All fields of the boot command header structure are set to 0, except 709 1.1 jkunz //! for the checksum. This includes the tag field since the tag value for 710 1.1 jkunz //! the #ROM_NOP_CMD is zero. And since all fields are zeroes the checksum 711 1.1 jkunz //! remains the initial checksum value of 0x5a. 712 1.1 jkunz void EncoreBootImage::NopCommand::fillCommandHeader(boot_command_t & header) 713 1.1 jkunz { 714 1.1 jkunz header.m_tag = getTag(); 715 1.1 jkunz header.m_flags = 0; 716 1.1 jkunz header.m_address = 0; 717 1.1 jkunz header.m_count = 0; 718 1.1 jkunz header.m_data = 0; 719 1.1 jkunz header.m_checksum = calculateChecksum(header); // do this last 720 1.1 jkunz } 721 1.1 jkunz 722 1.1 jkunz void EncoreBootImage::NopCommand::debugPrint() const 723 1.1 jkunz { 724 1.1 jkunz Log::log(Logger::INFO2, "\tNOOP\n"); 725 1.1 jkunz } 726 1.1 jkunz 727 1.1 jkunz //! The identifier, length, and flags fields are taken from \a section. 728 1.1 jkunz //! 729 1.1 jkunz //! \todo How does length get set correctly if the length is supposed to include 730 1.1 jkunz //! this command? 731 1.1 jkunz EncoreBootImage::TagCommand::TagCommand(const Section & section) 732 1.1 jkunz { 733 1.1 jkunz m_sectionIdentifier = section.getIdentifier(); 734 1.1 jkunz m_sectionLength = section.getBlockCount(); 735 1.1 jkunz m_sectionFlags = section.getFlags(); 736 1.1 jkunz } 737 1.1 jkunz 738 1.1 jkunz //! \param blocks Pointer to the raw data blocks. 739 1.1 jkunz //! \param count Number of blocks pointed to by \a blocks. 740 1.1 jkunz //! \param[out] consumed On exit, this points to the number of cipher blocks that were occupied 741 1.1 jkunz //! by the command. Should be at least 1 for every command. This must not be NULL 742 1.1 jkunz //! on entry! 743 1.1 jkunz //! 744 1.1 jkunz //! \exception std::runtime_error Thrown if header fields are invalid. 745 1.1 jkunz void EncoreBootImage::TagCommand::initFromData(const cipher_block_t * blocks, unsigned count, unsigned * consumed) 746 1.1 jkunz { 747 1.1 jkunz const boot_command_t model = { 0, ROM_TAG_CMD, 0, 0, 0, 0 }; 748 1.1 jkunz const boot_command_t * header = reinterpret_cast<const boot_command_t *>(blocks); 749 1.1 jkunz validateHeader(&model, header, CMD_TAG_FIELD); 750 1.1 jkunz 751 1.1 jkunz // read fields from header 752 1.1 jkunz m_isLast = (ENDIAN_LITTLE_TO_HOST_U16(header->m_flags) & ROM_LAST_TAG) != 0; 753 1.1 jkunz m_sectionIdentifier = ENDIAN_LITTLE_TO_HOST_U32(header->m_address); 754 1.1 jkunz m_sectionLength = ENDIAN_LITTLE_TO_HOST_U32(header->m_count); 755 1.1 jkunz m_sectionFlags = ENDIAN_LITTLE_TO_HOST_U32(header->m_data); 756 1.1 jkunz 757 1.1 jkunz *consumed = 1; 758 1.1 jkunz } 759 1.1 jkunz 760 1.1 jkunz //! This method currently assumes that the next tag command will come immediately 761 1.1 jkunz //! after the data for this section. 762 1.1 jkunz void EncoreBootImage::TagCommand::fillCommandHeader(boot_command_t & header) 763 1.1 jkunz { 764 1.1 jkunz header.m_tag = getTag(); 765 1.1 jkunz header.m_flags = ENDIAN_HOST_TO_LITTLE_U16(m_isLast ? ROM_LAST_TAG : 0); 766 1.1 jkunz header.m_address = ENDIAN_HOST_TO_LITTLE_U32(m_sectionIdentifier); 767 1.1 jkunz header.m_count = ENDIAN_HOST_TO_LITTLE_U32(m_sectionLength); 768 1.1 jkunz header.m_data = ENDIAN_HOST_TO_LITTLE_U32(m_sectionFlags); 769 1.1 jkunz header.m_checksum = calculateChecksum(header); // do this last 770 1.1 jkunz } 771 1.1 jkunz 772 1.1 jkunz void EncoreBootImage::TagCommand::debugPrint() const 773 1.1 jkunz { 774 1.1 jkunz Log::log(Logger::INFO2, " BTAG | sec=0x%08x | cnt=0x%08x | flg=0x%08x\n", m_sectionIdentifier, m_sectionLength, m_sectionFlags); 775 1.1 jkunz } 776 1.1 jkunz 777 1.1 jkunz //! All fields are set to zero. 778 1.1 jkunz //! 779 1.1 jkunz EncoreBootImage::LoadCommand::LoadCommand() 780 1.1 jkunz : BootCommand(), m_data(), m_padCount(0), m_length(0), m_address(0), m_loadDCD(false) 781 1.1 jkunz { 782 1.1 jkunz fillPadding(); 783 1.1 jkunz } 784 1.1 jkunz 785 1.1 jkunz EncoreBootImage::LoadCommand::LoadCommand(uint32_t address, const uint8_t * data, uint32_t length) 786 1.1 jkunz : BootCommand(), m_data(), m_padCount(0), m_length(0), m_address(address), m_loadDCD(false) 787 1.1 jkunz { 788 1.1 jkunz fillPadding(); 789 1.1 jkunz setData(data, length); 790 1.1 jkunz } 791 1.1 jkunz 792 1.1 jkunz //! \param blocks Pointer to the raw data blocks. 793 1.1 jkunz //! \param count Number of blocks pointed to by \a blocks. 794 1.1 jkunz //! \param[out] consumed On exit, this points to the number of cipher blocks that were occupied 795 1.1 jkunz //! by the command. Should be at least 1 for every command. This must not be NULL 796 1.1 jkunz //! on entry! 797 1.1 jkunz //! 798 1.1 jkunz //! \exception std::runtime_error This exception is thrown if the actual CRC of the load 799 1.1 jkunz //! data does not match the CRC stored in the command header. Also thrown if the 800 1.1 jkunz //! \a count parameter is less than the number of data blocks needed for the length 801 1.1 jkunz //! specified in the command header or if header fields are invalid. 802 1.1 jkunz void EncoreBootImage::LoadCommand::initFromData(const cipher_block_t * blocks, unsigned count, unsigned * consumed) 803 1.1 jkunz { 804 1.1 jkunz // check static fields 805 1.1 jkunz const boot_command_t model = { 0, ROM_LOAD_CMD, 0, 0, 0, 0 }; 806 1.1 jkunz const boot_command_t * header = reinterpret_cast<const boot_command_t *>(blocks); 807 1.1 jkunz validateHeader(&model, header, CMD_TAG_FIELD); 808 1.1 jkunz 809 1.1 jkunz // read fields from header 810 1.1 jkunz m_address = ENDIAN_LITTLE_TO_HOST_U32(header->m_address); 811 1.1 jkunz m_length = ENDIAN_LITTLE_TO_HOST_U32(header->m_count); 812 1.1 jkunz unsigned crc = ENDIAN_LITTLE_TO_HOST_U32(header->m_data); 813 1.1 jkunz unsigned dataBlockCount = numberOfCipherBlocks(m_length); 814 1.1 jkunz m_padCount = sizeOfPaddingForCipherBlocks(dataBlockCount); 815 1.1 jkunz m_loadDCD = (ENDIAN_LITTLE_TO_HOST_U16(header->m_flags) & ROM_LOAD_DCD) != 0; 816 1.1 jkunz 817 1.1 jkunz // make sure there are enough blocks 818 1.1 jkunz if (count - 1 < dataBlockCount) 819 1.1 jkunz { 820 1.1 jkunz throw std::runtime_error("not enough cipher blocks for load data"); 821 1.1 jkunz } 822 1.1 jkunz 823 1.1 jkunz // copy data 824 1.1 jkunz setData(reinterpret_cast<const uint8_t *>(blocks + 1), m_length); 825 1.1 jkunz 826 1.1 jkunz // copy padding 827 1.1 jkunz if (m_padCount) 828 1.1 jkunz { 829 1.1 jkunz const uint8_t * firstPadByte = reinterpret_cast<const uint8_t *> (blocks + (1 + dataBlockCount)) - m_padCount; 830 1.1 jkunz memcpy(m_padding, firstPadByte, m_padCount); 831 1.1 jkunz } 832 1.1 jkunz 833 1.1 jkunz // check CRC 834 1.1 jkunz uint32_t actualCRC = calculateCRC(); 835 1.1 jkunz if (actualCRC != crc) 836 1.1 jkunz { 837 1.1 jkunz throw std::runtime_error("load data failed CRC check"); 838 1.1 jkunz } 839 1.1 jkunz 840 1.1 jkunz *consumed = 1 + dataBlockCount; 841 1.1 jkunz } 842 1.1 jkunz 843 1.1 jkunz //! The only thing unique in the load command header is the 844 1.1 jkunz //! #elftosb::EncoreBootImage::boot_command_t::m_data. It contains a CRC-32 over the 845 1.1 jkunz //! load data, plus any bytes of padding in the last data cipher block. 846 1.1 jkunz void EncoreBootImage::LoadCommand::fillCommandHeader(boot_command_t & header) 847 1.1 jkunz { 848 1.1 jkunz header.m_tag = getTag(); 849 1.1 jkunz header.m_flags = ENDIAN_HOST_TO_LITTLE_U16(m_loadDCD ? ROM_LOAD_DCD : 0); 850 1.1 jkunz header.m_address = ENDIAN_HOST_TO_LITTLE_U32(m_address); 851 1.1 jkunz header.m_count = ENDIAN_HOST_TO_LITTLE_U32(m_length); 852 1.1 jkunz header.m_data = ENDIAN_HOST_TO_LITTLE_U32(calculateCRC()); 853 1.1 jkunz 854 1.1 jkunz // do this last 855 1.1 jkunz header.m_checksum = calculateChecksum(header); 856 1.1 jkunz } 857 1.1 jkunz 858 1.1 jkunz //! A CRC-32 is calculated over the load data, including any pad bytes 859 1.1 jkunz //! that are required in the last data cipher block. Including the 860 1.1 jkunz //! pad bytes in the CRC makes it vastly easier for the ROM to calculate 861 1.1 jkunz //! the CRC for validation. 862 1.1 jkunz uint32_t EncoreBootImage::LoadCommand::calculateCRC() const 863 1.1 jkunz { 864 1.1 jkunz uint32_t result; 865 1.1 jkunz CRC32 crc; 866 1.1 jkunz crc.update(m_data, m_length); 867 1.1 jkunz if (m_padCount) 868 1.1 jkunz { 869 1.1 jkunz // include random padding in the CRC 870 1.1 jkunz crc.update(m_padding, m_padCount); 871 1.1 jkunz } 872 1.1 jkunz crc.truncatedFinal(reinterpret_cast<uint8_t*>(&result), sizeof(result)); 873 1.1 jkunz 874 1.1 jkunz return result; 875 1.1 jkunz } 876 1.1 jkunz 877 1.1 jkunz //! A local copy of the load data is made. This copy will be disposed of when this object 878 1.1 jkunz //! is destroyed. This means the caller is free to deallocate \a data after this call 879 1.1 jkunz //! returns. It also means the caller can pass a pointer into the middle of a buffer for 880 1.1 jkunz //! \a data and not worry about ownership issues. 881 1.1 jkunz void EncoreBootImage::LoadCommand::setData(const uint8_t * data, uint32_t length) 882 1.1 jkunz { 883 1.1 jkunz assert(data); 884 1.1 jkunz assert(length); 885 1.1 jkunz 886 1.1 jkunz uint8_t * dataCopy = new uint8_t[length]; 887 1.1 jkunz memcpy(dataCopy, data, length); 888 1.1 jkunz 889 1.1 jkunz m_data = dataCopy; 890 1.1 jkunz m_length = length; 891 1.1 jkunz 892 1.1 jkunz m_padCount = sizeOfPaddingForCipherBlocks(m_length); 893 1.1 jkunz } 894 1.1 jkunz 895 1.1 jkunz //! \return The number of cipher blocks required to hold the load data, 896 1.1 jkunz //! rounded up as necessary. 897 1.1 jkunz unsigned EncoreBootImage::LoadCommand::getDataBlockCount() const 898 1.1 jkunz { 899 1.1 jkunz // round up to the next cipher block 900 1.1 jkunz return numberOfCipherBlocks(m_length); 901 1.1 jkunz } 902 1.1 jkunz 903 1.1 jkunz //! Up to \a maxCount data blocks are copied into the buffer pointed to by 904 1.1 jkunz //! the \a data argument. This is only a request for \a maxCount blocks. 905 1.1 jkunz //! A return value of 0 indicates that no more blocks are available. The 906 1.1 jkunz //! index of the first block to copy is held in the \a offset argument. 907 1.1 jkunz //! If there are pad bytes needed to fill out the last data block, they 908 1.1 jkunz //! will be filled with random data in order to add to the "whiteness" of 909 1.1 jkunz //! the data on both sides of encryption. 910 1.1 jkunz //! 911 1.1 jkunz //! \param offset Starting block number to copy. Zero means the first available block. 912 1.1 jkunz //! \param maxCount Up to this number of blocks may be copied into \a data. Must be 1 or greater. 913 1.1 jkunz //! \param data Buffer for outgoing data blocks. Must have enough room to hold 914 1.1 jkunz //! \a maxCount blocks. 915 1.1 jkunz //! 916 1.1 jkunz //! \return The number of data blocks copied into \a data. 917 1.1 jkunz //! \retval 0 No more blocks are available and nothing was written to \a data. 918 1.1 jkunz //! 919 1.1 jkunz //! \exception std::out_of_range Thrown when offset is invalid. 920 1.1 jkunz //! 921 1.1 jkunz //! \todo fill pad bytes with random bytes 922 1.1 jkunz unsigned EncoreBootImage::LoadCommand::getDataBlocks(unsigned offset, unsigned maxCount, cipher_block_t * data) 923 1.1 jkunz { 924 1.1 jkunz assert(data); 925 1.1 jkunz assert(maxCount != 0); 926 1.1 jkunz 927 1.1 jkunz uint32_t blockCount = getDataBlockCount(); 928 1.1 jkunz 929 1.1 jkunz // check offset 930 1.1 jkunz if (offset >= blockCount) 931 1.1 jkunz { 932 1.1 jkunz throw std::out_of_range("invalid offset"); 933 1.1 jkunz } 934 1.1 jkunz 935 1.1 jkunz // figure out how many blocks to return 936 1.1 jkunz unsigned resultBlocks = blockCount - offset; 937 1.1 jkunz if (resultBlocks > maxCount) 938 1.1 jkunz { 939 1.1 jkunz resultBlocks = maxCount; 940 1.1 jkunz 941 1.1 jkunz // exclude last block if there is padding 942 1.1 jkunz if (m_padCount && (offset != blockCount - 1) && (offset + resultBlocks == blockCount)) 943 1.1 jkunz { 944 1.1 jkunz resultBlocks--; 945 1.1 jkunz } 946 1.1 jkunz } 947 1.1 jkunz 948 1.1 jkunz // if there are pad bytes, handle the last block specially 949 1.1 jkunz if (m_padCount && offset == blockCount - 1) 950 1.1 jkunz { 951 1.1 jkunz // copy the remainder of the load data into the first part of the result block 952 1.1 jkunz unsigned remainderLength = sizeof(cipher_block_t) - m_padCount; 953 1.1 jkunz memcpy(data, &m_data[sizeof(cipher_block_t) * offset], remainderLength); 954 1.1 jkunz 955 1.1 jkunz // copy pad bytes we previously generated into the last part of the result block 956 1.1 jkunz // data is a cipher block pointer, so indexing is done on cipher block 957 1.1 jkunz // boundaries, thus we need a byte pointer to index properly 958 1.1 jkunz uint8_t * bytePtr = reinterpret_cast<uint8_t*>(data); 959 1.1 jkunz memcpy(bytePtr + remainderLength, &m_padding, m_padCount); 960 1.1 jkunz } 961 1.1 jkunz else 962 1.1 jkunz { 963 1.1 jkunz memcpy(data, &m_data[sizeof(cipher_block_t) * offset], sizeof(cipher_block_t) * resultBlocks); 964 1.1 jkunz } 965 1.1 jkunz 966 1.1 jkunz return resultBlocks; 967 1.1 jkunz } 968 1.1 jkunz 969 1.1 jkunz //! Fills #m_padding with random bytes that may be used to fill up the last data 970 1.1 jkunz //! cipher block. 971 1.1 jkunz void EncoreBootImage::LoadCommand::fillPadding() 972 1.1 jkunz { 973 1.1 jkunz RandomNumberGenerator rng; 974 1.1 jkunz rng.generateBlock(m_padding, sizeof(m_padding)); 975 1.1 jkunz } 976 1.1 jkunz 977 1.1 jkunz void EncoreBootImage::LoadCommand::debugPrint() const 978 1.1 jkunz { 979 1.1 jkunz 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 1.1 jkunz } 981 1.1 jkunz 982 1.1 jkunz //! The pattern, address, and count are all initialized to zero, and the pattern 983 1.1 jkunz //! size is set to a word. 984 1.1 jkunz EncoreBootImage::FillCommand::FillCommand() 985 1.1 jkunz : BootCommand(), m_address(0), m_count(0), m_pattern(0) 986 1.1 jkunz { 987 1.1 jkunz } 988 1.1 jkunz 989 1.1 jkunz //! \param blocks Pointer to the raw data blocks. 990 1.1 jkunz //! \param count Number of blocks pointed to by \a blocks. 991 1.1 jkunz //! \param[out] consumed On exit, this points to the number of cipher blocks that were occupied 992 1.1 jkunz //! by the command. Should be at least 1 for every command. This must not be NULL 993 1.1 jkunz //! on entry! 994 1.1 jkunz //! 995 1.1 jkunz //! \exception std::runtime_error Thrown if header fields are invalid. 996 1.1 jkunz void EncoreBootImage::FillCommand::initFromData(const cipher_block_t * blocks, unsigned count, unsigned * consumed) 997 1.1 jkunz { 998 1.1 jkunz // check static fields 999 1.1 jkunz const boot_command_t model = { 0, ROM_FILL_CMD, 0, 0, 0, 0 }; 1000 1.1 jkunz const boot_command_t * header = reinterpret_cast<const boot_command_t *>(blocks); 1001 1.1 jkunz validateHeader(&model, header, CMD_TAG_FIELD | CMD_FLAGS_FIELD); 1002 1.1 jkunz 1003 1.1 jkunz // read fields from header 1004 1.1 jkunz m_address = ENDIAN_LITTLE_TO_HOST_U32(header->m_address); 1005 1.1 jkunz m_count = ENDIAN_LITTLE_TO_HOST_U32(header->m_count); 1006 1.1 jkunz m_pattern = ENDIAN_LITTLE_TO_HOST_U32(header->m_data); 1007 1.1 jkunz 1008 1.1 jkunz *consumed = 1; 1009 1.1 jkunz } 1010 1.1 jkunz 1011 1.1 jkunz void EncoreBootImage::FillCommand::fillCommandHeader(boot_command_t & header) 1012 1.1 jkunz { 1013 1.1 jkunz header.m_tag = getTag(); 1014 1.1 jkunz header.m_flags = 0; 1015 1.1 jkunz header.m_address = ENDIAN_HOST_TO_LITTLE_U32(m_address); 1016 1.1 jkunz header.m_count = ENDIAN_HOST_TO_LITTLE_U32(m_count); 1017 1.1 jkunz header.m_data = ENDIAN_HOST_TO_LITTLE_U32(m_pattern); 1018 1.1 jkunz header.m_checksum = calculateChecksum(header); // do this last 1019 1.1 jkunz } 1020 1.1 jkunz 1021 1.1 jkunz //! Extends the pattern across 32 bits. 1022 1.1 jkunz //! 1023 1.1 jkunz void EncoreBootImage::FillCommand::setPattern(uint8_t pattern) 1024 1.1 jkunz { 1025 1.1 jkunz m_pattern = (pattern << 24) | (pattern << 16) | (pattern << 8) | pattern; 1026 1.1 jkunz } 1027 1.1 jkunz 1028 1.1 jkunz //! Extends the pattern across 32 bits. 1029 1.1 jkunz //! 1030 1.1 jkunz void EncoreBootImage::FillCommand::setPattern(uint16_t pattern) 1031 1.1 jkunz { 1032 1.1 jkunz m_pattern = (pattern << 16) | pattern; 1033 1.1 jkunz } 1034 1.1 jkunz 1035 1.1 jkunz void EncoreBootImage::FillCommand::setPattern(uint32_t pattern) 1036 1.1 jkunz { 1037 1.1 jkunz m_pattern = pattern; 1038 1.1 jkunz } 1039 1.1 jkunz 1040 1.1 jkunz void EncoreBootImage::FillCommand::debugPrint() const 1041 1.1 jkunz { 1042 1.1 jkunz Log::log(Logger::INFO2, " FILL | adr=0x%08x | len=0x%08x | ptn=0x%08x\n", m_address, m_count, m_pattern); 1043 1.1 jkunz } 1044 1.1 jkunz 1045 1.1 jkunz //! \param blocks Pointer to the raw data blocks. 1046 1.1 jkunz //! \param count Number of blocks pointed to by \a blocks. 1047 1.1 jkunz //! \param[out] consumed On exit, this points to the number of cipher blocks that were occupied 1048 1.1 jkunz //! by the command. Should be at least 1 for every command. This must not be NULL 1049 1.1 jkunz //! on entry! 1050 1.1 jkunz //! 1051 1.1 jkunz //! \exception std::runtime_error Thrown if header fields are invalid. 1052 1.1 jkunz void EncoreBootImage::ModeCommand::initFromData(const cipher_block_t * blocks, unsigned count, unsigned * consumed) 1053 1.1 jkunz { 1054 1.1 jkunz // check static fields 1055 1.1 jkunz const boot_command_t model = { 0, ROM_MODE_CMD, 0, 0, 0, 0 }; 1056 1.1 jkunz const boot_command_t * header = reinterpret_cast<const boot_command_t *>(blocks); 1057 1.1 jkunz validateHeader(&model, header, CMD_TAG_FIELD | CMD_FLAGS_FIELD | CMD_ADDRESS_FIELD | CMD_COUNT_FIELD); 1058 1.1 jkunz 1059 1.1 jkunz // read fields from header 1060 1.1 jkunz m_mode = ENDIAN_LITTLE_TO_HOST_U32(header->m_data); 1061 1.1 jkunz 1062 1.1 jkunz *consumed = 1; 1063 1.1 jkunz } 1064 1.1 jkunz 1065 1.1 jkunz void EncoreBootImage::ModeCommand::fillCommandHeader(boot_command_t & header) 1066 1.1 jkunz { 1067 1.1 jkunz header.m_tag = getTag(); 1068 1.1 jkunz header.m_flags = 0; 1069 1.1 jkunz header.m_address = 0; 1070 1.1 jkunz header.m_count = 0; 1071 1.1 jkunz header.m_data = ENDIAN_HOST_TO_LITTLE_U32(m_mode); 1072 1.1 jkunz header.m_checksum = calculateChecksum(header); // do this last 1073 1.1 jkunz } 1074 1.1 jkunz 1075 1.1 jkunz void EncoreBootImage::ModeCommand::debugPrint() const 1076 1.1 jkunz { 1077 1.1 jkunz Log::log(Logger::INFO2, " MODE | mod=0x%08x\n", m_mode); 1078 1.1 jkunz } 1079 1.1 jkunz 1080 1.1 jkunz //! \param blocks Pointer to the raw data blocks. 1081 1.1 jkunz //! \param count Number of blocks pointed to by \a blocks. 1082 1.1 jkunz //! \param[out] consumed On exit, this points to the number of cipher blocks that were occupied 1083 1.1 jkunz //! by the command. Should be at least 1 for every command. This must not be NULL 1084 1.1 jkunz //! on entry! 1085 1.1 jkunz //! 1086 1.1 jkunz //! \exception std::runtime_error Thrown if header fields are invalid. 1087 1.1 jkunz void EncoreBootImage::JumpCommand::initFromData(const cipher_block_t * blocks, unsigned count, unsigned * consumed) 1088 1.1 jkunz { 1089 1.1 jkunz // check static fields 1090 1.1 jkunz const boot_command_t model = { 0, getTag(), 0, 0, 0, 0 }; 1091 1.1 jkunz const boot_command_t * header = reinterpret_cast<const boot_command_t *>(blocks); 1092 1.1 jkunz validateHeader(&model, header, CMD_TAG_FIELD | CMD_COUNT_FIELD); 1093 1.1 jkunz 1094 1.1 jkunz // read fields from header 1095 1.1 jkunz m_address = ENDIAN_LITTLE_TO_HOST_U32(header->m_address); 1096 1.1 jkunz m_argument = ENDIAN_LITTLE_TO_HOST_U32(header->m_data); 1097 1.1 jkunz m_isHAB = (ENDIAN_LITTLE_TO_HOST_U16(header->m_flags) & ROM_HAB_EXEC) != 0; 1098 1.1 jkunz 1099 1.1 jkunz *consumed = 1; 1100 1.1 jkunz } 1101 1.1 jkunz 1102 1.1 jkunz void EncoreBootImage::JumpCommand::fillCommandHeader(boot_command_t & header) 1103 1.1 jkunz { 1104 1.1 jkunz header.m_tag = getTag(); 1105 1.1 jkunz header.m_flags = ENDIAN_HOST_TO_LITTLE_U16(m_isHAB ? ROM_HAB_EXEC : 0); 1106 1.1 jkunz header.m_address = ENDIAN_HOST_TO_LITTLE_U32(m_address); 1107 1.1 jkunz header.m_count = 0; 1108 1.1 jkunz header.m_data = ENDIAN_HOST_TO_LITTLE_U32(m_argument); 1109 1.1 jkunz header.m_checksum = calculateChecksum(header); // do this last 1110 1.1 jkunz } 1111 1.1 jkunz 1112 1.1 jkunz void EncoreBootImage::JumpCommand::debugPrint() const 1113 1.1 jkunz { 1114 1.1 jkunz 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 1.1 jkunz } 1116 1.1 jkunz 1117 1.1 jkunz void EncoreBootImage::CallCommand::debugPrint() const 1118 1.1 jkunz { 1119 1.1 jkunz 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 1.1 jkunz } 1121 1.1 jkunz 1122 1.1 jkunz //! Only if the section has been assigned a boot image owner object will this 1123 1.1 jkunz //! method be able to fill in the #section_header_t::m_offset field. If no 1124 1.1 jkunz //! boot image has been set the offset will be set to 0. 1125 1.1 jkunz void EncoreBootImage::Section::fillSectionHeader(section_header_t & header) 1126 1.1 jkunz { 1127 1.1 jkunz header.m_tag = getIdentifier(); 1128 1.1 jkunz header.m_offset = 0; 1129 1.1 jkunz header.m_length = ENDIAN_HOST_TO_LITTLE_U32(getBlockCount()); 1130 1.1 jkunz header.m_flags = ENDIAN_HOST_TO_LITTLE_U32(getFlags()); 1131 1.1 jkunz 1132 1.1 jkunz // if we're attached to an image, we can compute our real offset 1133 1.1 jkunz if (m_image) 1134 1.1 jkunz { 1135 1.1 jkunz header.m_offset = ENDIAN_HOST_TO_LITTLE_U32(m_image->getSectionOffset(this)); 1136 1.1 jkunz } 1137 1.1 jkunz } 1138 1.1 jkunz 1139 1.1 jkunz //! The alignment will never be less than 16, since that is the size of the 1140 1.1 jkunz //! cipher block which is the basic unit of the boot image format. If an 1141 1.1 jkunz //! alignment less than 16 is set it will be ignored. 1142 1.1 jkunz //! 1143 1.1 jkunz //! \param alignment Alignment in bytes for this section. Must be a power of two. 1144 1.1 jkunz //! Ignored if less than 16. 1145 1.1 jkunz void EncoreBootImage::Section::setAlignment(unsigned alignment) 1146 1.1 jkunz { 1147 1.1 jkunz if (alignment > BOOT_IMAGE_MINIMUM_SECTION_ALIGNMENT) 1148 1.1 jkunz { 1149 1.1 jkunz m_alignment = alignment; 1150 1.1 jkunz } 1151 1.1 jkunz } 1152 1.1 jkunz 1153 1.1 jkunz //! This method calculates the number of padding blocks that need to be inserted 1154 1.1 jkunz //! from a given offset for the section to be properly aligned. The value returned 1155 1.1 jkunz //! is the number of padding blocks that should be inserted starting just after 1156 1.1 jkunz //! \a offset to align the first cipher block of the section contents. The section's 1157 1.1 jkunz //! boot tag is \i not taken into account by this method, so the caller must 1158 1.1 jkunz //! deal with that herself. 1159 1.1 jkunz //! 1160 1.1 jkunz //! \param offset Start offset in cipher blocks (not bytes). 1161 1.1 jkunz //! 1162 1.1 jkunz //! \return A number of cipher blocks of padding to insert. 1163 1.1 jkunz unsigned EncoreBootImage::Section::getPadBlockCountForOffset(unsigned offset) 1164 1.1 jkunz { 1165 1.1 jkunz // convert alignment from byte to block alignment 1166 1.1 jkunz unsigned blockAlignment = m_alignment >> 4; 1167 1.1 jkunz 1168 1.1 jkunz unsigned nextAlignmentOffset = (offset + blockAlignment - 1) / blockAlignment * blockAlignment; 1169 1.1 jkunz 1170 1.1 jkunz return nextAlignmentOffset - offset; 1171 1.1 jkunz } 1172 1.1 jkunz 1173 1.1 jkunz EncoreBootImage::BootSection::~BootSection() 1174 1.1 jkunz { 1175 1.1 jkunz deleteCommands(); 1176 1.1 jkunz } 1177 1.1 jkunz 1178 1.1 jkunz void EncoreBootImage::BootSection::deleteCommands() 1179 1.1 jkunz { 1180 1.1 jkunz // dispose of all sections 1181 1.1 jkunz iterator_t it = begin(); 1182 1.1 jkunz for (; it != end(); ++it) 1183 1.1 jkunz { 1184 1.1 jkunz delete *it; 1185 1.1 jkunz } 1186 1.1 jkunz } 1187 1.1 jkunz 1188 1.1 jkunz //! Always returns at least 1 for the required tag command. 1189 1.1 jkunz //! 1190 1.1 jkunz unsigned EncoreBootImage::BootSection::getBlockCount() const 1191 1.1 jkunz { 1192 1.1 jkunz unsigned count = 0; 1193 1.1 jkunz 1194 1.1 jkunz const_iterator_t it = begin(); 1195 1.1 jkunz for (; it != end(); ++it) 1196 1.1 jkunz { 1197 1.1 jkunz count += (*it)->getBlockCount(); 1198 1.1 jkunz } 1199 1.1 jkunz 1200 1.1 jkunz return count; 1201 1.1 jkunz } 1202 1.1 jkunz 1203 1.1 jkunz //! Up to \a maxCount cipher blocks are copied into the buffer pointed to by 1204 1.1 jkunz //! the \a data argument. A return value of 0 indicates that 1205 1.1 jkunz //! no more blocks are available. The index of the first block to copy is 1206 1.1 jkunz //! held in the \a offset argument. 1207 1.1 jkunz //! 1208 1.1 jkunz //! \param offset Starting block number to copy. Zero means the first available block. 1209 1.1 jkunz //! \param maxCount Up to this number of blocks may be copied into \a data. 1210 1.1 jkunz //! \param data Buffer for outgoing cipher blocks. Must have enough room to hold 1211 1.1 jkunz //! \a maxCount blocks. 1212 1.1 jkunz //! 1213 1.1 jkunz //! \return The number of cipher blocks copied into \a data. 1214 1.1 jkunz //! \retval 0 No more blocks are available and nothing was written to \a data. 1215 1.1 jkunz unsigned EncoreBootImage::BootSection::getBlocks(unsigned offset, unsigned maxCount, cipher_block_t * data) 1216 1.1 jkunz { 1217 1.1 jkunz assert(data); 1218 1.1 jkunz assert(maxCount >= 1); 1219 1.1 jkunz 1220 1.1 jkunz unsigned currentOffset = 0; 1221 1.1 jkunz unsigned readCount = maxCount; 1222 1.1 jkunz 1223 1.1 jkunz iterator_t it = begin(); 1224 1.1 jkunz for (; it != end(); ++it) 1225 1.1 jkunz { 1226 1.1 jkunz BootCommand * command = *it; 1227 1.1 jkunz unsigned commandBlocks = command->getBlockCount(); 1228 1.1 jkunz 1229 1.1 jkunz // this should never be false! 1230 1.1 jkunz assert(offset >= currentOffset); 1231 1.1 jkunz 1232 1.1 jkunz // skip forward until we hit the requested offset 1233 1.1 jkunz if (offset >= currentOffset + commandBlocks) 1234 1.1 jkunz { 1235 1.1 jkunz currentOffset += commandBlocks; 1236 1.1 jkunz continue; 1237 1.1 jkunz } 1238 1.1 jkunz 1239 1.1 jkunz // read from this command 1240 1.1 jkunz unsigned commandOffset = offset - currentOffset; 1241 1.1 jkunz unsigned commandRemaining = commandBlocks - commandOffset; 1242 1.1 jkunz if (readCount > commandRemaining) 1243 1.1 jkunz { 1244 1.1 jkunz readCount = commandRemaining; 1245 1.1 jkunz } 1246 1.1 jkunz return command->getBlocks(commandOffset, readCount, data); 1247 1.1 jkunz } 1248 1.1 jkunz 1249 1.1 jkunz return 0; 1250 1.1 jkunz } 1251 1.1 jkunz 1252 1.1 jkunz //! The entire contents of the section must be in memory, pointed to by \a blocks. 1253 1.1 jkunz //! Any commands that had previously been added to the section are disposed of. 1254 1.1 jkunz //! 1255 1.1 jkunz //! \param blocks Pointer to the section contents. 1256 1.1 jkunz //! \param count Number of blocks pointed to by \a blocks. 1257 1.1 jkunz //! 1258 1.1 jkunz //! \exception std::runtime_error Thrown if a boot command cannot be created from 1259 1.1 jkunz //! the cipher block stream. 1260 1.1 jkunz void EncoreBootImage::BootSection::fillFromData(const cipher_block_t * blocks, unsigned count) 1261 1.1 jkunz { 1262 1.1 jkunz // start with an empty slate 1263 1.1 jkunz deleteCommands(); 1264 1.1 jkunz 1265 1.1 jkunz const cipher_block_t * currentBlock = blocks; 1266 1.1 jkunz unsigned remaining = count; 1267 1.1 jkunz while (remaining) 1268 1.1 jkunz { 1269 1.1 jkunz // try to create a command from the next cipher block. the number of 1270 1.1 jkunz // blocks the command used up is returned in consumed. 1271 1.1 jkunz unsigned consumed; 1272 1.1 jkunz BootCommand * command = BootCommand::createFromData(currentBlock, remaining, &consumed); 1273 1.1 jkunz if (!command) 1274 1.1 jkunz { 1275 1.1 jkunz throw std::runtime_error("invalid boot section data"); 1276 1.1 jkunz } 1277 1.1 jkunz 1278 1.1 jkunz addCommand(command); 1279 1.1 jkunz 1280 1.1 jkunz // update loop counters 1281 1.1 jkunz remaining -= consumed; 1282 1.1 jkunz currentBlock += consumed; 1283 1.1 jkunz } 1284 1.1 jkunz } 1285 1.1 jkunz 1286 1.1 jkunz void EncoreBootImage::BootSection::debugPrint() const 1287 1.1 jkunz { 1288 1.1 jkunz Log::log(Logger::INFO2, "Boot Section 0x%08x:\n", m_identifier); 1289 1.1 jkunz 1290 1.1 jkunz const_iterator_t it = begin(); 1291 1.1 jkunz for (; it != end(); ++it) 1292 1.1 jkunz { 1293 1.1 jkunz const BootCommand * command = *it; 1294 1.1 jkunz command->debugPrint(); 1295 1.1 jkunz } 1296 1.1 jkunz } 1297 1.1 jkunz 1298 1.1 jkunz //! A copy is made of \a data. Any previously assigned data is disposed of. 1299 1.1 jkunz //! 1300 1.1 jkunz void EncoreBootImage::DataSection::setData(const uint8_t * data, unsigned length) 1301 1.1 jkunz { 1302 1.1 jkunz m_data = new uint8_t[length]; 1303 1.1 jkunz memcpy(m_data.get(), data, length); 1304 1.1 jkunz m_length = length; 1305 1.1 jkunz } 1306 1.1 jkunz 1307 1.1 jkunz //! The section takes ownership of \a data and will dispose of it using the 1308 1.1 jkunz //! array delete operator upon its destruction. 1309 1.1 jkunz void EncoreBootImage::DataSection::setDataNoCopy(const uint8_t * data, unsigned length) 1310 1.1 jkunz { 1311 1.1 jkunz m_data = data; 1312 1.1 jkunz m_length = length; 1313 1.1 jkunz } 1314 1.1 jkunz 1315 1.1 jkunz unsigned EncoreBootImage::DataSection::getBlockCount() const 1316 1.1 jkunz { 1317 1.1 jkunz return numberOfCipherBlocks(m_length); 1318 1.1 jkunz } 1319 1.1 jkunz 1320 1.1 jkunz unsigned EncoreBootImage::DataSection::getBlocks(unsigned offset, unsigned maxCount, cipher_block_t * data) 1321 1.1 jkunz { 1322 1.1 jkunz assert(data); 1323 1.1 jkunz assert(maxCount != 0); 1324 1.1 jkunz 1325 1.1 jkunz unsigned blockCount = getBlockCount(); 1326 1.1 jkunz unsigned padCount = sizeOfPaddingForCipherBlocks(m_length); 1327 1.1 jkunz 1328 1.1 jkunz // check offset 1329 1.1 jkunz if (offset >= blockCount) 1330 1.1 jkunz { 1331 1.1 jkunz throw std::out_of_range("invalid offset"); 1332 1.1 jkunz } 1333 1.1 jkunz 1334 1.1 jkunz // figure out how many blocks to return 1335 1.1 jkunz unsigned resultBlocks = blockCount - offset; 1336 1.1 jkunz if (resultBlocks > maxCount) 1337 1.1 jkunz { 1338 1.1 jkunz resultBlocks = maxCount; 1339 1.1 jkunz 1340 1.1 jkunz // exclude last block if there is padding 1341 1.1 jkunz if (padCount && (offset != blockCount - 1) && (offset + resultBlocks == blockCount)) 1342 1.1 jkunz { 1343 1.1 jkunz resultBlocks--; 1344 1.1 jkunz } 1345 1.1 jkunz } 1346 1.1 jkunz 1347 1.1 jkunz // if there are pad bytes, handle the last block specially 1348 1.1 jkunz if (padCount && offset == blockCount - 1) 1349 1.1 jkunz { 1350 1.1 jkunz // copy the remainder of the load data into the first part of the result block 1351 1.1 jkunz unsigned remainderLength = sizeof(cipher_block_t) - padCount; 1352 1.1 jkunz memcpy(data, &m_data[sizeOfCipherBlocks(offset)], remainderLength); 1353 1.1 jkunz 1354 1.1 jkunz // set pad bytes to zeroes. 1355 1.1 jkunz // data is a cipher block pointer, so indexing is done on cipher block 1356 1.1 jkunz // boundaries, thus we need a byte pointer to index properly 1357 1.1 jkunz uint8_t * bytePtr = reinterpret_cast<uint8_t*>(data); 1358 1.1 jkunz memset(bytePtr + remainderLength, 0, padCount); 1359 1.1 jkunz } 1360 1.1 jkunz else 1361 1.1 jkunz { 1362 1.1 jkunz memcpy(data, &m_data[sizeOfCipherBlocks(offset)], sizeOfCipherBlocks(resultBlocks)); 1363 1.1 jkunz } 1364 1.1 jkunz 1365 1.1 jkunz return resultBlocks; 1366 1.1 jkunz } 1367 1.1 jkunz 1368 1.1 jkunz void EncoreBootImage::DataSection::debugPrint() const 1369 1.1 jkunz { 1370 1.1 jkunz Log::log(Logger::INFO2, "Data Section 0x%08x: (%d bytes, %d blocks)\n", m_identifier, m_length, getBlockCount()); 1371 1.1 jkunz } 1372 1.1 jkunz 1373