xref: /netbsd-src/external/bsd/elftosb/dist/common/EncoreBootImage.cpp (revision f49c4a3f9c7a5b33b2e1d7cf465e2accbb4280d0)
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 
EncoreBootImage()22 EncoreBootImage::EncoreBootImage()
23 :	m_headerFlags(0),
24 	m_productVersion(),
25 	m_componentVersion(),
26 	m_driveTag(0)
27 {
28 }
29 
~EncoreBootImage()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.
addSection(Section * newSection)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 
findSection(Section * section)61 EncoreBootImage::section_iterator_t EncoreBootImage::findSection(Section * section)
62 {
63 	return std::find(beginSection(), endSection(), section);
64 }
65 
setProductVersion(const version_t & version)66 void EncoreBootImage::setProductVersion(const version_t & version)
67 {
68 	m_productVersion = version;
69 }
70 
setComponentVersion(const version_t & version)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.
writeToStream(std::ostream & stream)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 *>(&sectionHeader), 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 *>(&sectionHeader), sizeof(sectionHeader));
152 				}
153 			}
154 
155 			// update SHA-1
156 			hash.Update(reinterpret_cast<uint8_t *>(&sectionHeader), 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 
prepareImageHeader(boot_image_header_t & header)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.
getTimestamp()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.
findFirstBootableSection()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.
getSectionOffset(Section * section)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.
getPadBlockCountForSection(Section * section,unsigned offset)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 
getImageSize()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 
debugPrint() const511 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.
createFromData(const cipher_block_t * blocks,unsigned count,unsigned * consumed)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.
calculateChecksum(const boot_command_t & header)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.
getBlockCount() const590 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.
getBlocks(unsigned offset,unsigned maxCount,cipher_block_t * data)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.
validateHeader(const boot_command_t * modelHeader,const boot_command_t * testHeader,unsigned whichFields)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.
initFromData(const cipher_block_t * blocks,unsigned count,unsigned * consumed)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.
fillCommandHeader(boot_command_t & header)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 
debugPrint() const722 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?
TagCommand(const Section & section)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.
initFromData(const cipher_block_t * blocks,unsigned count,unsigned * consumed)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.
fillCommandHeader(boot_command_t & header)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 
debugPrint() const772 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 //!
LoadCommand()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 
LoadCommand(uint32_t address,const uint8_t * data,uint32_t length)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.
initFromData(const cipher_block_t * blocks,unsigned count,unsigned * consumed)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.
fillCommandHeader(boot_command_t & header)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.
calculateCRC() const862 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.
setData(const uint8_t * data,uint32_t length)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.
getDataBlockCount() const897 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
getDataBlocks(unsigned offset,unsigned maxCount,cipher_block_t * data)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.
fillPadding()971 void EncoreBootImage::LoadCommand::fillPadding()
972 {
973 	RandomNumberGenerator rng;
974 	rng.generateBlock(m_padding, sizeof(m_padding));
975 }
976 
debugPrint() const977 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.
FillCommand()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.
initFromData(const cipher_block_t * blocks,unsigned count,unsigned * consumed)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 
fillCommandHeader(boot_command_t & header)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 //!
setPattern(uint8_t pattern)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 //!
setPattern(uint16_t pattern)1030 void EncoreBootImage::FillCommand::setPattern(uint16_t pattern)
1031 {
1032 	m_pattern = (pattern << 16) | pattern;
1033 }
1034 
setPattern(uint32_t pattern)1035 void EncoreBootImage::FillCommand::setPattern(uint32_t pattern)
1036 {
1037 	m_pattern = pattern;
1038 }
1039 
debugPrint() const1040 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.
initFromData(const cipher_block_t * blocks,unsigned count,unsigned * consumed)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 
fillCommandHeader(boot_command_t & header)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 
debugPrint() const1075 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.
initFromData(const cipher_block_t * blocks,unsigned count,unsigned * consumed)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 
fillCommandHeader(boot_command_t & header)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 
debugPrint() const1112 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 
debugPrint() const1117 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.
fillSectionHeader(section_header_t & header)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.
setAlignment(unsigned alignment)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.
getPadBlockCountForOffset(unsigned offset)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 
~BootSection()1173 EncoreBootImage::BootSection::~BootSection()
1174 {
1175 	deleteCommands();
1176 }
1177 
deleteCommands()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 //!
getBlockCount() const1190 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.
getBlocks(unsigned offset,unsigned maxCount,cipher_block_t * 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.
fillFromData(const cipher_block_t * blocks,unsigned count)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 
debugPrint() const1286 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 //!
setData(const uint8_t * data,unsigned length)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.
setDataNoCopy(const uint8_t * data,unsigned length)1309 void EncoreBootImage::DataSection::setDataNoCopy(const uint8_t * data, unsigned length)
1310 {
1311 	m_data = data;
1312 	m_length = length;
1313 }
1314 
getBlockCount() const1315 unsigned EncoreBootImage::DataSection::getBlockCount() const
1316 {
1317 	return numberOfCipherBlocks(m_length);
1318 }
1319 
getBlocks(unsigned offset,unsigned maxCount,cipher_block_t * data)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 
debugPrint() const1368 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