1993229b6Sjkunz /*
2993229b6Sjkunz * File: EncoreBootImage.cpp
3993229b6Sjkunz *
4993229b6Sjkunz * Copyright (c) Freescale Semiconductor, Inc. All rights reserved.
5993229b6Sjkunz * See included license file for license details.
6993229b6Sjkunz */
7993229b6Sjkunz
8993229b6Sjkunz #include "EncoreBootImage.h"
9993229b6Sjkunz #include <stdexcept>
10993229b6Sjkunz #include <algorithm>
11993229b6Sjkunz #include <time.h>
12993229b6Sjkunz #include "crc.h"
13993229b6Sjkunz #include "SHA1.h"
14993229b6Sjkunz #include "Random.h"
15993229b6Sjkunz #include "rijndael.h"
16993229b6Sjkunz #include "RijndaelCBCMAC.h"
17993229b6Sjkunz #include "Logging.h"
18993229b6Sjkunz #include "EndianUtilities.h"
19993229b6Sjkunz
20993229b6Sjkunz using namespace elftosb;
21993229b6Sjkunz
EncoreBootImage()22993229b6Sjkunz EncoreBootImage::EncoreBootImage()
23993229b6Sjkunz : m_headerFlags(0),
24993229b6Sjkunz m_productVersion(),
25993229b6Sjkunz m_componentVersion(),
26993229b6Sjkunz m_driveTag(0)
27993229b6Sjkunz {
28993229b6Sjkunz }
29993229b6Sjkunz
~EncoreBootImage()30993229b6Sjkunz EncoreBootImage::~EncoreBootImage()
31993229b6Sjkunz {
32993229b6Sjkunz // dispose of all sections
33993229b6Sjkunz section_iterator_t it = beginSection();
34993229b6Sjkunz for (; it != endSection(); ++it)
35993229b6Sjkunz {
36993229b6Sjkunz delete *it;
37993229b6Sjkunz }
38993229b6Sjkunz }
39993229b6Sjkunz
40993229b6Sjkunz //! \exception std::runtime_error Raised if \a newSection has the same tag as a previously
41993229b6Sjkunz //! added section.
addSection(Section * newSection)42993229b6Sjkunz void EncoreBootImage::addSection(Section * newSection)
43993229b6Sjkunz {
44993229b6Sjkunz // check for another section with this tag
45993229b6Sjkunz section_iterator_t it = beginSection();
46993229b6Sjkunz for (; it != endSection(); ++it)
47993229b6Sjkunz {
48993229b6Sjkunz if ((*it)->getIdentifier() == newSection->getIdentifier())
49993229b6Sjkunz {
50993229b6Sjkunz throw std::runtime_error("new section with non-unique tag");
51993229b6Sjkunz }
52993229b6Sjkunz }
53993229b6Sjkunz
54993229b6Sjkunz // no conflicting section tags, so add it
55993229b6Sjkunz m_sections.push_back(newSection);
56993229b6Sjkunz
57993229b6Sjkunz // tell the image who owns it now
58993229b6Sjkunz newSection->setImage(this);
59993229b6Sjkunz }
60993229b6Sjkunz
findSection(Section * section)61993229b6Sjkunz EncoreBootImage::section_iterator_t EncoreBootImage::findSection(Section * section)
62993229b6Sjkunz {
63993229b6Sjkunz return std::find(beginSection(), endSection(), section);
64993229b6Sjkunz }
65993229b6Sjkunz
setProductVersion(const version_t & version)66993229b6Sjkunz void EncoreBootImage::setProductVersion(const version_t & version)
67993229b6Sjkunz {
68993229b6Sjkunz m_productVersion = version;
69993229b6Sjkunz }
70993229b6Sjkunz
setComponentVersion(const version_t & version)71993229b6Sjkunz void EncoreBootImage::setComponentVersion(const version_t & version)
72993229b6Sjkunz {
73993229b6Sjkunz m_componentVersion = version;
74993229b6Sjkunz }
75993229b6Sjkunz
76993229b6Sjkunz //! \todo Optimize writing section data. Right now it only writes one block at a
77993229b6Sjkunz //! time, which is of course quite slow (in relative terms).
78993229b6Sjkunz //! \todo Refactor this into several different methods for writing each region
79993229b6Sjkunz //! of the image. Use a context structure to keep track of shared data between
80993229b6Sjkunz //! each of the methods.
81993229b6Sjkunz //! \todo Refactor the section and boot tag writing code to only have a single
82993229b6Sjkunz //! copy of the block writing and encryption loop.
writeToStream(std::ostream & stream)83993229b6Sjkunz void EncoreBootImage::writeToStream(std::ostream & stream)
84993229b6Sjkunz {
85993229b6Sjkunz // always generate the session key or DEK even if image is unencrypted
86993229b6Sjkunz m_sessionKey.randomize();
87993229b6Sjkunz
88993229b6Sjkunz // prepare to compute CBC-MACs with each KEK
89993229b6Sjkunz unsigned i;
90993229b6Sjkunz smart_array_ptr<RijndaelCBCMAC> macs(0);
91993229b6Sjkunz if (isEncrypted())
92993229b6Sjkunz {
93993229b6Sjkunz macs = new RijndaelCBCMAC[m_keys.size()];
94993229b6Sjkunz for (i=0; i < m_keys.size(); ++i)
95993229b6Sjkunz {
96993229b6Sjkunz RijndaelCBCMAC mac(m_keys[i]);
97993229b6Sjkunz (macs.get())[i] = mac;
98993229b6Sjkunz }
99993229b6Sjkunz }
100993229b6Sjkunz
101993229b6Sjkunz // prepare to compute SHA-1 digest over entire image
102993229b6Sjkunz CSHA1 hash;
103993229b6Sjkunz hash.Reset();
104993229b6Sjkunz
105993229b6Sjkunz // count of total blocks written to the file
106993229b6Sjkunz unsigned fileBlocksWritten = 0;
107993229b6Sjkunz
108993229b6Sjkunz // we need some pieces of the header down below
109993229b6Sjkunz boot_image_header_t imageHeader;
110993229b6Sjkunz prepareImageHeader(imageHeader);
111993229b6Sjkunz
112993229b6Sjkunz // write plaintext header
113993229b6Sjkunz {
114993229b6Sjkunz // write header
115993229b6Sjkunz assert(sizeOfPaddingForCipherBlocks(sizeof(boot_image_header_t)) == 0);
116993229b6Sjkunz stream.write(reinterpret_cast<char *>(&imageHeader), sizeof(imageHeader));
117993229b6Sjkunz fileBlocksWritten += numberOfCipherBlocks(sizeof(imageHeader));
118993229b6Sjkunz
119993229b6Sjkunz // update CBC-MAC over image header
120993229b6Sjkunz if (isEncrypted())
121993229b6Sjkunz {
122993229b6Sjkunz for (i=0; i < m_keys.size(); ++i)
123993229b6Sjkunz {
124993229b6Sjkunz (macs.get())[i].update(reinterpret_cast<uint8_t *>(&imageHeader), sizeof(imageHeader));
125993229b6Sjkunz }
126993229b6Sjkunz }
127993229b6Sjkunz
128993229b6Sjkunz // update SHA-1
129993229b6Sjkunz hash.Update(reinterpret_cast<uint8_t *>(&imageHeader), sizeof(imageHeader));
130993229b6Sjkunz }
131993229b6Sjkunz
132993229b6Sjkunz // write plaintext section table
133993229b6Sjkunz {
134993229b6Sjkunz section_iterator_t it = beginSection();
135993229b6Sjkunz for (; it != endSection(); ++it)
136993229b6Sjkunz {
137993229b6Sjkunz Section * section = *it;
138993229b6Sjkunz
139993229b6Sjkunz // write header for this section
140993229b6Sjkunz assert(sizeOfPaddingForCipherBlocks(sizeof(section_header_t)) == 0);
141993229b6Sjkunz section_header_t sectionHeader;
142993229b6Sjkunz section->fillSectionHeader(sectionHeader);
143993229b6Sjkunz stream.write(reinterpret_cast<char *>(§ionHeader), sizeof(sectionHeader));
144993229b6Sjkunz fileBlocksWritten += numberOfCipherBlocks(sizeof(sectionHeader));
145993229b6Sjkunz
146993229b6Sjkunz // update CBC-MAC over this entry
147993229b6Sjkunz if (isEncrypted())
148993229b6Sjkunz {
149993229b6Sjkunz for (i=0; i < m_keys.size(); ++i)
150993229b6Sjkunz {
151993229b6Sjkunz (macs.get())[i].update(reinterpret_cast<uint8_t *>(§ionHeader), sizeof(sectionHeader));
152993229b6Sjkunz }
153993229b6Sjkunz }
154993229b6Sjkunz
155993229b6Sjkunz // update SHA-1
156993229b6Sjkunz hash.Update(reinterpret_cast<uint8_t *>(§ionHeader), sizeof(sectionHeader));
157993229b6Sjkunz }
158993229b6Sjkunz }
159993229b6Sjkunz
160993229b6Sjkunz // finished with the CBC-MAC
161993229b6Sjkunz if (isEncrypted())
162993229b6Sjkunz {
163993229b6Sjkunz for (i=0; i < m_keys.size(); ++i)
164993229b6Sjkunz {
165993229b6Sjkunz (macs.get())[i].finalize();
166993229b6Sjkunz }
167993229b6Sjkunz }
168993229b6Sjkunz
169993229b6Sjkunz // write key dictionary
170993229b6Sjkunz if (isEncrypted())
171993229b6Sjkunz {
172993229b6Sjkunz key_iterator_t it = beginKeys();
173993229b6Sjkunz for (i=0; it != endKeys(); ++it, ++i)
174993229b6Sjkunz {
175993229b6Sjkunz // write CBC-MAC result for this key, then update SHA-1
176993229b6Sjkunz RijndaelCBCMAC & mac = (macs.get())[i];
177993229b6Sjkunz const RijndaelCBCMAC::block_t & macResult = mac.getMAC();
178993229b6Sjkunz stream.write(reinterpret_cast<const char *>(&macResult), sizeof(RijndaelCBCMAC::block_t));
179993229b6Sjkunz hash.Update(reinterpret_cast<const uint8_t *>(&macResult), sizeof(RijndaelCBCMAC::block_t));
180993229b6Sjkunz fileBlocksWritten++;
181993229b6Sjkunz
182993229b6Sjkunz // encrypt DEK with this key, write it out, and update image digest
183993229b6Sjkunz Rijndael cipher;
184993229b6Sjkunz cipher.init(Rijndael::CBC, Rijndael::Encrypt, *it, Rijndael::Key16Bytes, imageHeader.m_iv);
185993229b6Sjkunz AESKey<128>::key_t wrappedSessionKey;
186993229b6Sjkunz cipher.blockEncrypt(m_sessionKey, sizeof(AESKey<128>::key_t) * 8, wrappedSessionKey);
187993229b6Sjkunz stream.write(reinterpret_cast<char *>(&wrappedSessionKey), sizeof(wrappedSessionKey));
188993229b6Sjkunz hash.Update(reinterpret_cast<uint8_t *>(&wrappedSessionKey), sizeof(wrappedSessionKey));
189993229b6Sjkunz fileBlocksWritten++;
190993229b6Sjkunz }
191993229b6Sjkunz }
192993229b6Sjkunz
193993229b6Sjkunz // write sections and boot tags
194993229b6Sjkunz {
195993229b6Sjkunz section_iterator_t it = beginSection();
196993229b6Sjkunz for (; it != endSection(); ++it)
197993229b6Sjkunz {
198993229b6Sjkunz section_iterator_t itCopy = it;
199993229b6Sjkunz bool isLastSection = (++itCopy == endSection());
200993229b6Sjkunz
201993229b6Sjkunz Section * section = *it;
202993229b6Sjkunz cipher_block_t block;
203993229b6Sjkunz unsigned blockCount = section->getBlockCount();
204993229b6Sjkunz unsigned blocksWritten = 0;
205993229b6Sjkunz
206993229b6Sjkunz Rijndael cipher;
207993229b6Sjkunz cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, imageHeader.m_iv);
208993229b6Sjkunz
209993229b6Sjkunz // Compute the number of padding blocks needed to align the section. This first
210993229b6Sjkunz // call to getPadBlockCountForOffset() passes an offset that excludes
211993229b6Sjkunz // the boot tag for this section.
212993229b6Sjkunz unsigned paddingBlocks = getPadBlockCountForSection(section, fileBlocksWritten);
213993229b6Sjkunz
214993229b6Sjkunz // Insert nop commands as padding to align the start of the section, if
215993229b6Sjkunz // the section has special alignment requirements.
216993229b6Sjkunz NopCommand nop;
217993229b6Sjkunz while (paddingBlocks--)
218993229b6Sjkunz {
219993229b6Sjkunz blockCount = nop.getBlockCount();
220993229b6Sjkunz blocksWritten = 0;
221993229b6Sjkunz while (blocksWritten < blockCount)
222993229b6Sjkunz {
223993229b6Sjkunz nop.getBlocks(blocksWritten, 1, &block);
224993229b6Sjkunz
225993229b6Sjkunz if (isEncrypted())
226993229b6Sjkunz {
227993229b6Sjkunz // re-init after encrypt to update IV
228993229b6Sjkunz cipher.blockEncrypt(block, sizeof(cipher_block_t) * 8, block);
229993229b6Sjkunz cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, block);
230993229b6Sjkunz }
231993229b6Sjkunz
232993229b6Sjkunz stream.write(reinterpret_cast<char *>(&block), sizeof(cipher_block_t));
233993229b6Sjkunz hash.Update(reinterpret_cast<uint8_t *>(&block), sizeof(cipher_block_t));
234993229b6Sjkunz
235993229b6Sjkunz blocksWritten++;
236993229b6Sjkunz fileBlocksWritten++;
237993229b6Sjkunz }
238993229b6Sjkunz }
239993229b6Sjkunz
240993229b6Sjkunz // reinit cipher for boot tag
241993229b6Sjkunz cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, imageHeader.m_iv);
242993229b6Sjkunz
243993229b6Sjkunz // write boot tag
244993229b6Sjkunz TagCommand tag(*section);
245993229b6Sjkunz tag.setLast(isLastSection);
246993229b6Sjkunz if (!isLastSection)
247993229b6Sjkunz {
248993229b6Sjkunz // If this isn't the last section, the tag needs to include any
249993229b6Sjkunz // padding for the next section in its length, otherwise the ROM
250993229b6Sjkunz // won't be able to find the next section's boot tag.
251993229b6Sjkunz unsigned nextSectionOffset = fileBlocksWritten + section->getBlockCount() + 1;
252993229b6Sjkunz tag.setSectionLength(section->getBlockCount() + getPadBlockCountForSection(*itCopy, nextSectionOffset));
253993229b6Sjkunz }
254993229b6Sjkunz blockCount = tag.getBlockCount();
255993229b6Sjkunz blocksWritten = 0;
256993229b6Sjkunz while (blocksWritten < blockCount)
257993229b6Sjkunz {
258993229b6Sjkunz tag.getBlocks(blocksWritten, 1, &block);
259993229b6Sjkunz
260993229b6Sjkunz if (isEncrypted())
261993229b6Sjkunz {
262993229b6Sjkunz // re-init after encrypt to update IV
263993229b6Sjkunz cipher.blockEncrypt(block, sizeof(cipher_block_t) * 8, block);
264993229b6Sjkunz cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, block);
265993229b6Sjkunz }
266993229b6Sjkunz
267993229b6Sjkunz stream.write(reinterpret_cast<char *>(&block), sizeof(cipher_block_t));
268993229b6Sjkunz hash.Update(reinterpret_cast<uint8_t *>(&block), sizeof(cipher_block_t));
269993229b6Sjkunz
270993229b6Sjkunz blocksWritten++;
271993229b6Sjkunz fileBlocksWritten++;
272993229b6Sjkunz }
273993229b6Sjkunz
274993229b6Sjkunz // reinit cipher for section data
275993229b6Sjkunz cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, imageHeader.m_iv);
276993229b6Sjkunz
277993229b6Sjkunz // write section data
278993229b6Sjkunz blockCount = section->getBlockCount();
279993229b6Sjkunz blocksWritten = 0;
280993229b6Sjkunz while (blocksWritten < blockCount)
281993229b6Sjkunz {
282993229b6Sjkunz section->getBlocks(blocksWritten, 1, &block);
283993229b6Sjkunz
284993229b6Sjkunz // Only encrypt the section contents if the entire boot image is encrypted
285993229b6Sjkunz // and the section doesn't have the "leave unencrypted" flag set. Even if the
286993229b6Sjkunz // section is unencrypted the boot tag will remain encrypted.
287993229b6Sjkunz if (isEncrypted() && !section->getLeaveUnencrypted())
288993229b6Sjkunz {
289993229b6Sjkunz // re-init after encrypt to update IV
290993229b6Sjkunz cipher.blockEncrypt(block, sizeof(cipher_block_t) * 8, block);
291993229b6Sjkunz cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, block);
292993229b6Sjkunz }
293993229b6Sjkunz
294993229b6Sjkunz stream.write(reinterpret_cast<char *>(&block), sizeof(cipher_block_t));
295993229b6Sjkunz hash.Update(reinterpret_cast<uint8_t *>(&block), sizeof(cipher_block_t));
296993229b6Sjkunz
297993229b6Sjkunz blocksWritten++;
298993229b6Sjkunz fileBlocksWritten++;
299993229b6Sjkunz }
300993229b6Sjkunz }
301993229b6Sjkunz }
302993229b6Sjkunz
303993229b6Sjkunz // write SHA-1 digest over entire image
304993229b6Sjkunz {
305993229b6Sjkunz // allocate enough room for digest and bytes to pad out to the next cipher block
306993229b6Sjkunz const unsigned padBytes = sizeOfPaddingForCipherBlocks(sizeof(sha1_digest_t));
307993229b6Sjkunz unsigned digestBlocksSize = sizeof(sha1_digest_t) + padBytes;
308993229b6Sjkunz smart_array_ptr<uint8_t> digestBlocks = new uint8_t[digestBlocksSize];
309993229b6Sjkunz hash.Final();
310993229b6Sjkunz hash.GetHash(digestBlocks.get());
311993229b6Sjkunz
312993229b6Sjkunz // set the pad bytes to random values
313993229b6Sjkunz RandomNumberGenerator rng;
314993229b6Sjkunz rng.generateBlock(&(digestBlocks.get())[sizeof(sha1_digest_t)], padBytes);
315993229b6Sjkunz
316993229b6Sjkunz // encrypt with session key
317993229b6Sjkunz if (isEncrypted())
318993229b6Sjkunz {
319993229b6Sjkunz Rijndael cipher;
320993229b6Sjkunz cipher.init(Rijndael::CBC, Rijndael::Encrypt, m_sessionKey, Rijndael::Key16Bytes, imageHeader.m_iv);
321993229b6Sjkunz cipher.blockEncrypt(digestBlocks.get(), digestBlocksSize * 8, digestBlocks.get());
322993229b6Sjkunz }
323993229b6Sjkunz
324993229b6Sjkunz // write to the stream
325993229b6Sjkunz stream.write(reinterpret_cast<char *>(digestBlocks.get()), digestBlocksSize);
326993229b6Sjkunz }
327993229b6Sjkunz }
328993229b6Sjkunz
prepareImageHeader(boot_image_header_t & header)329993229b6Sjkunz void EncoreBootImage::prepareImageHeader(boot_image_header_t & header)
330993229b6Sjkunz {
331993229b6Sjkunz // get identifier for the first bootable section
332993229b6Sjkunz Section * firstBootSection = findFirstBootableSection();
333993229b6Sjkunz section_id_t firstBootSectionID = 0;
334993229b6Sjkunz if (firstBootSection)
335993229b6Sjkunz {
336993229b6Sjkunz firstBootSectionID = firstBootSection->getIdentifier();
337993229b6Sjkunz }
338993229b6Sjkunz
339993229b6Sjkunz // fill in header fields
340993229b6Sjkunz header.m_signature[0] = 'S';
341993229b6Sjkunz header.m_signature[1] = 'T';
342993229b6Sjkunz header.m_signature[2] = 'M';
343993229b6Sjkunz header.m_signature[3] = 'P';
344993229b6Sjkunz header.m_majorVersion = ROM_BOOT_IMAGE_MAJOR_VERSION;
345993229b6Sjkunz header.m_minorVersion = ROM_BOOT_IMAGE_MINOR_VERSION;
346993229b6Sjkunz header.m_flags = ENDIAN_HOST_TO_LITTLE_U16(m_headerFlags);
347993229b6Sjkunz header.m_imageBlocks = ENDIAN_HOST_TO_LITTLE_U32(getImageSize());
348993229b6Sjkunz header.m_firstBootableSectionID = ENDIAN_HOST_TO_LITTLE_U32(firstBootSectionID);
349993229b6Sjkunz header.m_keyCount = ENDIAN_HOST_TO_LITTLE_U16((uint16_t)m_keys.size());
350993229b6Sjkunz header.m_headerBlocks = ENDIAN_HOST_TO_LITTLE_U16((uint16_t)numberOfCipherBlocks(sizeof(header)));
351993229b6Sjkunz header.m_sectionCount = ENDIAN_HOST_TO_LITTLE_U16((uint16_t)m_sections.size());
352993229b6Sjkunz header.m_sectionHeaderSize = ENDIAN_HOST_TO_LITTLE_U16((uint16_t)numberOfCipherBlocks(sizeof(section_header_t)));
353993229b6Sjkunz header.m_signature2[0] = 's';
354993229b6Sjkunz header.m_signature2[1] = 'g';
355993229b6Sjkunz header.m_signature2[2] = 't';
356993229b6Sjkunz header.m_signature2[3] = 'l';
357993229b6Sjkunz header.m_timestamp = ENDIAN_HOST_TO_LITTLE_U64(getTimestamp());
358993229b6Sjkunz header.m_driveTag = m_driveTag;
359993229b6Sjkunz
360993229b6Sjkunz // Prepare version fields by converting them to the correct byte order.
361993229b6Sjkunz header.m_productVersion = m_productVersion;
362993229b6Sjkunz header.m_componentVersion = m_componentVersion;
363993229b6Sjkunz header.m_productVersion.fixByteOrder();
364993229b6Sjkunz header.m_componentVersion.fixByteOrder();
365993229b6Sjkunz
366993229b6Sjkunz // the fields are dependant on others
367993229b6Sjkunz header.m_keyDictionaryBlock = ENDIAN_HOST_TO_LITTLE_U16(header.m_headerBlocks + header.m_sectionCount * header.m_sectionHeaderSize);
368993229b6Sjkunz header.m_firstBootTagBlock = ENDIAN_HOST_TO_LITTLE_U32(header.m_keyDictionaryBlock + header.m_keyCount * 2);
369993229b6Sjkunz
370993229b6Sjkunz // generate random pad bytes
371993229b6Sjkunz RandomNumberGenerator rng;
372993229b6Sjkunz rng.generateBlock(header.m_padding0, sizeof(header.m_padding0));
373993229b6Sjkunz rng.generateBlock(header.m_padding1, sizeof(header.m_padding1));
374993229b6Sjkunz
375993229b6Sjkunz // compute SHA-1 digest over the image header
376993229b6Sjkunz uint8_t * message = reinterpret_cast<uint8_t *>(&header.m_signature);
377993229b6Sjkunz uint32_t length = static_cast<uint32_t>(sizeof(header) - sizeof(header.m_digest)); // include padding
378993229b6Sjkunz
379993229b6Sjkunz CSHA1 hash;
380993229b6Sjkunz hash.Reset();
381993229b6Sjkunz hash.Update(message, length);
382993229b6Sjkunz hash.Final();
383993229b6Sjkunz hash.GetHash(header.m_digest);
384993229b6Sjkunz }
385993229b6Sjkunz
386993229b6Sjkunz //! Returns the number of microseconds since 00:00 1-1-2000. In actuality, the timestamp
387993229b6Sjkunz //! is only accurate to seconds, and is simply extended out to microseconds.
388993229b6Sjkunz //!
389993229b6Sjkunz //! \todo Use the operating system's low-level functions to get a true microsecond
390993229b6Sjkunz //! timestamp, instead of faking it like we do now.
391993229b6Sjkunz //! \bug The timestamp might be off an hour.
getTimestamp()392993229b6Sjkunz uint64_t EncoreBootImage::getTimestamp()
393993229b6Sjkunz {
394*f49c4a3fShans #if defined(WIN32) || defined(__CYGWIN__) || defined(__sun)
395993229b6Sjkunz struct tm epoch = { 0, 0, 0, 1, 0, 100, 0, 0 }; // 00:00 1-1-2000
396993229b6Sjkunz #else
397993229b6Sjkunz struct tm epoch = { 0, 0, 0, 1, 0, 100, 0, 0, 1, 0, NULL }; // 00:00 1-1-2000
398993229b6Sjkunz #endif
399993229b6Sjkunz time_t epochTime = mktime(&epoch);
400993229b6Sjkunz time_t now = time(NULL);
401993229b6Sjkunz now -= epochTime;
402993229b6Sjkunz uint64_t microNow = uint64_t(now) * 1000000; // convert to microseconds
403993229b6Sjkunz return microNow;
404993229b6Sjkunz }
405993229b6Sjkunz
406993229b6Sjkunz //! Scans the section list looking for the first section which has
407993229b6Sjkunz //! the #ROM_SECTION_BOOTABLE flag set on it.
findFirstBootableSection()408993229b6Sjkunz EncoreBootImage::Section * EncoreBootImage::findFirstBootableSection()
409993229b6Sjkunz {
410993229b6Sjkunz section_iterator_t it = beginSection();
411993229b6Sjkunz for (; it != endSection(); ++it)
412993229b6Sjkunz {
413993229b6Sjkunz if ((*it)->getFlags() & ROM_SECTION_BOOTABLE)
414993229b6Sjkunz {
415993229b6Sjkunz return *it;
416993229b6Sjkunz }
417993229b6Sjkunz }
418993229b6Sjkunz
419993229b6Sjkunz // no bootable sections were found
420993229b6Sjkunz return NULL;
421993229b6Sjkunz }
422993229b6Sjkunz
423993229b6Sjkunz //! The boot tag for \a section is taken into account, thus making the
424993229b6Sjkunz //! result offset point to the first block of the actual section data.
425993229b6Sjkunz //!
426993229b6Sjkunz //! \note The offset will only be valid if all encryption keys and all
427993229b6Sjkunz //! sections have already been added to the image.
getSectionOffset(Section * section)428993229b6Sjkunz uint32_t EncoreBootImage::getSectionOffset(Section * section)
429993229b6Sjkunz {
430993229b6Sjkunz // start with boot image headers
431993229b6Sjkunz uint32_t offset = numberOfCipherBlocks(sizeof(boot_image_header_t)); // header
432993229b6Sjkunz offset += numberOfCipherBlocks(sizeof(section_header_t)) * sectionCount(); // section table
433993229b6Sjkunz offset += 2 * keyCount(); // key dictiontary
434993229b6Sjkunz
435993229b6Sjkunz // add up sections before this one
436993229b6Sjkunz section_iterator_t it = beginSection();
437993229b6Sjkunz for (; it != endSection() && *it != section; ++it)
438993229b6Sjkunz {
439993229b6Sjkunz Section * thisSection = *it;
440993229b6Sjkunz
441993229b6Sjkunz // insert padding for section alignment
442993229b6Sjkunz offset += getPadBlockCountForSection(thisSection, offset);
443993229b6Sjkunz
444993229b6Sjkunz // add one for boot tag associated with this section
445993229b6Sjkunz offset++;
446993229b6Sjkunz
447993229b6Sjkunz // now add the section's contents
448993229b6Sjkunz offset += thisSection->getBlockCount();
449993229b6Sjkunz }
450993229b6Sjkunz
451993229b6Sjkunz // and add padding for this section
452993229b6Sjkunz offset += getPadBlockCountForSection(section, offset);
453993229b6Sjkunz
454993229b6Sjkunz // skip over this section's boot tag
455993229b6Sjkunz offset++;
456993229b6Sjkunz
457993229b6Sjkunz return offset;
458993229b6Sjkunz }
459993229b6Sjkunz
460993229b6Sjkunz //! Computes the number of blocks of padding required to align \a section while
461993229b6Sjkunz //! taking into account the boot tag that gets inserted before the section contents.
getPadBlockCountForSection(Section * section,unsigned offset)462993229b6Sjkunz unsigned EncoreBootImage::getPadBlockCountForSection(Section * section, unsigned offset)
463993229b6Sjkunz {
464993229b6Sjkunz // Compute the number of padding blocks needed to align the section. This first
465993229b6Sjkunz // call to getPadBlockCountForOffset() passes an offset that excludes
466993229b6Sjkunz // the boot tag for this section.
467993229b6Sjkunz unsigned paddingBlocks = section->getPadBlockCountForOffset(offset);
468993229b6Sjkunz
469993229b6Sjkunz // If the pad count comes back as 0 then we need to try again with an offset that
470993229b6Sjkunz // includes the boot tag. This is all because we're aligning the section contents
471993229b6Sjkunz // start and not the section's boot tag.
472993229b6Sjkunz if (paddingBlocks == 0)
473993229b6Sjkunz {
474993229b6Sjkunz paddingBlocks = section->getPadBlockCountForOffset(offset + 1);
475993229b6Sjkunz }
476993229b6Sjkunz // Otherwise if we get a nonzero pad amount then we need to subtract the block
477993229b6Sjkunz // for the section's boot tag from the pad count.
478993229b6Sjkunz else
479993229b6Sjkunz {
480993229b6Sjkunz paddingBlocks--;
481993229b6Sjkunz }
482993229b6Sjkunz
483993229b6Sjkunz return paddingBlocks;
484993229b6Sjkunz }
485993229b6Sjkunz
getImageSize()486993229b6Sjkunz uint32_t EncoreBootImage::getImageSize()
487993229b6Sjkunz {
488993229b6Sjkunz // determine to total size of the image
489993229b6Sjkunz const uint32_t headerBlocks = numberOfCipherBlocks(sizeof(boot_image_header_t));
490993229b6Sjkunz const uint32_t sectionHeaderSize = numberOfCipherBlocks(sizeof(section_header_t));
491993229b6Sjkunz uint32_t imageBlocks = headerBlocks;
492993229b6Sjkunz imageBlocks += sectionHeaderSize * m_sections.size(); // section table
493993229b6Sjkunz imageBlocks += 2 * m_keys.size(); // key dict
494993229b6Sjkunz
495993229b6Sjkunz // add in each section's size
496993229b6Sjkunz section_iterator_t it = beginSection();
497993229b6Sjkunz for (; it != endSection(); ++it)
498993229b6Sjkunz {
499993229b6Sjkunz // add in this section's size, padding to align it, and its boot tag
500993229b6Sjkunz imageBlocks += getPadBlockCountForSection(*it, imageBlocks);
501993229b6Sjkunz imageBlocks += (*it)->getBlockCount();
502993229b6Sjkunz imageBlocks++;
503993229b6Sjkunz }
504993229b6Sjkunz
505993229b6Sjkunz // image MAC
506993229b6Sjkunz imageBlocks += 2;
507993229b6Sjkunz
508993229b6Sjkunz return imageBlocks;
509993229b6Sjkunz }
510993229b6Sjkunz
debugPrint() const511993229b6Sjkunz void EncoreBootImage::debugPrint() const
512993229b6Sjkunz {
513993229b6Sjkunz const_section_iterator_t it = beginSection();
514993229b6Sjkunz for (; it != endSection(); ++it)
515993229b6Sjkunz {
516993229b6Sjkunz const Section * section = *it;
517993229b6Sjkunz section->debugPrint();
518993229b6Sjkunz }
519993229b6Sjkunz }
520993229b6Sjkunz
521993229b6Sjkunz //! \param blocks Pointer to the raw data blocks.
522993229b6Sjkunz //! \param count Number of blocks pointed to by \a blocks.
523993229b6Sjkunz //! \param[out] consumed On exit, this points to the number of cipher blocks that were occupied
524993229b6Sjkunz //! by the command. Should be at least 1 for every command. This must not be NULL
525993229b6Sjkunz //! on entry!
526993229b6Sjkunz //!
527993229b6Sjkunz //! \return A new boot command instance.
528993229b6Sjkunz //! \retval NULL The boot command pointed to by \a blocks was not recognized as a known
529993229b6Sjkunz //! command type.
530993229b6Sjkunz //!
531993229b6Sjkunz //! \exception std::runtime_error This exception indicates that a command was recognized
532993229b6Sjkunz //! but contained invalid data. Compare this to a NULL result which indicates that
533993229b6Sjkunz //! no command was recognized at all.
createFromData(const cipher_block_t * blocks,unsigned count,unsigned * consumed)534993229b6Sjkunz EncoreBootImage::BootCommand * EncoreBootImage::BootCommand::createFromData(const cipher_block_t * blocks, unsigned count, unsigned * consumed)
535993229b6Sjkunz {
536993229b6Sjkunz const boot_command_t * header = reinterpret_cast<const boot_command_t *>(blocks);
537993229b6Sjkunz BootCommand * command = NULL;
538993229b6Sjkunz
539993229b6Sjkunz switch (header->m_tag)
540993229b6Sjkunz {
541993229b6Sjkunz case ROM_NOP_CMD:
542993229b6Sjkunz command = new NopCommand();
543993229b6Sjkunz break;
544993229b6Sjkunz case ROM_TAG_CMD:
545993229b6Sjkunz command = new TagCommand();
546993229b6Sjkunz break;
547993229b6Sjkunz case ROM_LOAD_CMD:
548993229b6Sjkunz command = new LoadCommand();
549993229b6Sjkunz break;
550993229b6Sjkunz case ROM_FILL_CMD:
551993229b6Sjkunz command = new FillCommand();
552993229b6Sjkunz break;
553993229b6Sjkunz case ROM_MODE_CMD:
554993229b6Sjkunz command = new ModeCommand();
555993229b6Sjkunz break;
556993229b6Sjkunz case ROM_JUMP_CMD:
557993229b6Sjkunz command = new JumpCommand();
558993229b6Sjkunz break;
559993229b6Sjkunz case ROM_CALL_CMD:
560993229b6Sjkunz command = new CallCommand();
561993229b6Sjkunz break;
562993229b6Sjkunz }
563993229b6Sjkunz
564993229b6Sjkunz if (command)
565993229b6Sjkunz {
566993229b6Sjkunz command->initFromData(blocks, count, consumed);
567993229b6Sjkunz }
568993229b6Sjkunz return command;
569993229b6Sjkunz }
570993229b6Sjkunz
571993229b6Sjkunz //! The checksum algorithm is totally straightforward, except that the
572993229b6Sjkunz //! initial checksum byte value is set to 0x5a instead of 0.
calculateChecksum(const boot_command_t & header)573993229b6Sjkunz uint8_t EncoreBootImage::BootCommand::calculateChecksum(const boot_command_t & header)
574993229b6Sjkunz {
575993229b6Sjkunz const uint8_t * bytes = reinterpret_cast<const uint8_t *>(&header);
576993229b6Sjkunz uint8_t checksum = 0x5a;
577993229b6Sjkunz int i;
578993229b6Sjkunz
579993229b6Sjkunz // start at one to skip checksum field
580993229b6Sjkunz for (i = 1; i < sizeof(header); ++i)
581993229b6Sjkunz {
582993229b6Sjkunz checksum += bytes[i];
583993229b6Sjkunz }
584993229b6Sjkunz
585993229b6Sjkunz return checksum;
586993229b6Sjkunz }
587993229b6Sjkunz
588993229b6Sjkunz //! The default implementation returns 0, indicating that no blocks are
589993229b6Sjkunz //! available.
getBlockCount() const590993229b6Sjkunz unsigned EncoreBootImage::BootCommand::getBlockCount() const
591993229b6Sjkunz {
592993229b6Sjkunz return 1 + getDataBlockCount();
593993229b6Sjkunz }
594993229b6Sjkunz
595993229b6Sjkunz //! Up to \a maxCount cipher blocks are copied into the buffer pointed to by
596993229b6Sjkunz //! the \a data argument. The index of the first block to copy is
597993229b6Sjkunz //! held in the \a offset argument.
598993229b6Sjkunz //!
599993229b6Sjkunz //! \param offset Starting block number to copy. Zero means the first available block.
600993229b6Sjkunz //! \param maxCount Up to this number of blocks may be copied into \a data. Must be 1 or greater.
601993229b6Sjkunz //! \param data Buffer for outgoing cipher blocks. Must have enough room to hold
602993229b6Sjkunz //! \a maxCount blocks.
603993229b6Sjkunz //!
604993229b6Sjkunz //! \return The number of cipher blocks copied into \a data.
605993229b6Sjkunz //! \retval 0 No more blocks are available and nothing was written to \a data.
606993229b6Sjkunz //!
607993229b6Sjkunz //! \exception std::out_of_range If \a offset is invalid.
getBlocks(unsigned offset,unsigned maxCount,cipher_block_t * data)608993229b6Sjkunz unsigned EncoreBootImage::BootCommand::getBlocks(unsigned offset, unsigned maxCount, cipher_block_t * data)
609993229b6Sjkunz {
610993229b6Sjkunz assert(data);
611993229b6Sjkunz assert(maxCount >= 1);
612993229b6Sjkunz
613993229b6Sjkunz // check for valid offset
614993229b6Sjkunz if (offset >= getBlockCount())
615993229b6Sjkunz {
616993229b6Sjkunz throw std::out_of_range("invalid offset");
617993229b6Sjkunz }
618993229b6Sjkunz
619993229b6Sjkunz // handle the command header block separately
620993229b6Sjkunz if (offset == 0)
621993229b6Sjkunz {
622993229b6Sjkunz assert(sizeof(boot_command_t) == sizeof(cipher_block_t));
623993229b6Sjkunz
624993229b6Sjkunz boot_command_t header;
625993229b6Sjkunz fillCommandHeader(header);
626993229b6Sjkunz memcpy(data, &header, sizeof(header));
627993229b6Sjkunz
628993229b6Sjkunz return 1;
629993229b6Sjkunz }
630993229b6Sjkunz
631993229b6Sjkunz // handle any data blocks
632993229b6Sjkunz return getDataBlocks(offset - 1, maxCount, data);
633993229b6Sjkunz }
634993229b6Sjkunz
635993229b6Sjkunz //! The checksum field of \a testHeader is always computed and checked against itself.
636993229b6Sjkunz //! All other fields are compared to the corresponding value set in \a modelHeader
637993229b6Sjkunz //! if the appropriate flag is set in \a whichFields. For example, the m_address fields
638993229b6Sjkunz //! in \a testHeader and \a modelHeader are compared when the CMD_ADDRESS_FIELD bit
639993229b6Sjkunz //! is set in \a whichFields. An exception is thrown if any comparison fails.
640993229b6Sjkunz //!
641993229b6Sjkunz //! \param modelHeader The baseline header to compare against. Only those fields that
642993229b6Sjkunz //! have corresponding bits set in \a whichFields need to be set.
643993229b6Sjkunz //! \param testHeader The actual command header which is being validated.
644993229b6Sjkunz //! \param whichFields A bitfield used to determine which fields of the boot command
645993229b6Sjkunz //! header are compared. Possible values are:
646993229b6Sjkunz //! - CMD_TAG_FIELD
647993229b6Sjkunz //! - CMD_FLAGS_FIELD
648993229b6Sjkunz //! - CMD_ADDRESS_FIELD
649993229b6Sjkunz //! - CMD_COUNT_FIELD
650993229b6Sjkunz //! - CMD_DATA_FIELD
651993229b6Sjkunz //!
652993229b6Sjkunz //! \exception std::runtime_error Thrown if any requested validation fails.
validateHeader(const boot_command_t * modelHeader,const boot_command_t * testHeader,unsigned whichFields)653993229b6Sjkunz void EncoreBootImage::BootCommand::validateHeader(const boot_command_t * modelHeader, const boot_command_t * testHeader, unsigned whichFields)
654993229b6Sjkunz {
655993229b6Sjkunz // compare all the fields that were requested
656993229b6Sjkunz if ((whichFields & CMD_TAG_FIELD) && (testHeader->m_tag != modelHeader->m_tag))
657993229b6Sjkunz {
658993229b6Sjkunz throw std::runtime_error("invalid tag field");
659993229b6Sjkunz }
660993229b6Sjkunz
661993229b6Sjkunz if ((whichFields & CMD_FLAGS_FIELD) && (testHeader->m_flags != modelHeader->m_flags))
662993229b6Sjkunz {
663993229b6Sjkunz throw std::runtime_error("invalid flags field");
664993229b6Sjkunz }
665993229b6Sjkunz
666993229b6Sjkunz if ((whichFields & CMD_ADDRESS_FIELD) && (testHeader->m_address != modelHeader->m_address))
667993229b6Sjkunz {
668993229b6Sjkunz throw std::runtime_error("invalid address field");
669993229b6Sjkunz }
670993229b6Sjkunz
671993229b6Sjkunz if ((whichFields & CMD_COUNT_FIELD) && (testHeader->m_count != modelHeader->m_count))
672993229b6Sjkunz {
673993229b6Sjkunz throw std::runtime_error("invalid count field");
674993229b6Sjkunz }
675993229b6Sjkunz
676993229b6Sjkunz if ((whichFields & CMD_DATA_FIELD) && (testHeader->m_data != modelHeader->m_data))
677993229b6Sjkunz {
678993229b6Sjkunz throw std::runtime_error("invalid data field");
679993229b6Sjkunz }
680993229b6Sjkunz
681993229b6Sjkunz // calculate checksum
682993229b6Sjkunz uint8_t testChecksum = calculateChecksum(*testHeader);
683993229b6Sjkunz if (testChecksum != testHeader->m_checksum)
684993229b6Sjkunz {
685993229b6Sjkunz throw std::runtime_error("invalid checksum");
686993229b6Sjkunz }
687993229b6Sjkunz }
688993229b6Sjkunz
689993229b6Sjkunz //! Since the NOP command has no data, this method just validates the command header.
690993229b6Sjkunz //! All fields except the checksum are expected to be set to 0.
691993229b6Sjkunz //!
692993229b6Sjkunz //! \param blocks Pointer to the raw data blocks.
693993229b6Sjkunz //! \param count Number of blocks pointed to by \a blocks.
694993229b6Sjkunz //! \param[out] consumed On exit, this points to the number of cipher blocks that were occupied
695993229b6Sjkunz //! by the command. Should be at least 1 for every command. This must not be NULL
696993229b6Sjkunz //! on entry!
697993229b6Sjkunz //!
698993229b6Sjkunz //! \exception std::runtime_error Thrown if header fields are invalid.
initFromData(const cipher_block_t * blocks,unsigned count,unsigned * consumed)699993229b6Sjkunz void EncoreBootImage::NopCommand::initFromData(const cipher_block_t * blocks, unsigned count, unsigned * consumed)
700993229b6Sjkunz {
701993229b6Sjkunz const boot_command_t model = { 0, ROM_NOP_CMD, 0, 0, 0, 0 };
702993229b6Sjkunz const boot_command_t * header = reinterpret_cast<const boot_command_t *>(blocks);
703993229b6Sjkunz validateHeader(&model, header, CMD_TAG_FIELD | CMD_FLAGS_FIELD | CMD_ADDRESS_FIELD | CMD_COUNT_FIELD | CMD_DATA_FIELD);
704993229b6Sjkunz
705993229b6Sjkunz *consumed = 1;
706993229b6Sjkunz }
707993229b6Sjkunz
708993229b6Sjkunz //! All fields of the boot command header structure are set to 0, except
709993229b6Sjkunz //! for the checksum. This includes the tag field since the tag value for
710993229b6Sjkunz //! the #ROM_NOP_CMD is zero. And since all fields are zeroes the checksum
711993229b6Sjkunz //! remains the initial checksum value of 0x5a.
fillCommandHeader(boot_command_t & header)712993229b6Sjkunz void EncoreBootImage::NopCommand::fillCommandHeader(boot_command_t & header)
713993229b6Sjkunz {
714993229b6Sjkunz header.m_tag = getTag();
715993229b6Sjkunz header.m_flags = 0;
716993229b6Sjkunz header.m_address = 0;
717993229b6Sjkunz header.m_count = 0;
718993229b6Sjkunz header.m_data = 0;
719993229b6Sjkunz header.m_checksum = calculateChecksum(header); // do this last
720993229b6Sjkunz }
721993229b6Sjkunz
debugPrint() const722993229b6Sjkunz void EncoreBootImage::NopCommand::debugPrint() const
723993229b6Sjkunz {
724993229b6Sjkunz Log::log(Logger::INFO2, "\tNOOP\n");
725993229b6Sjkunz }
726993229b6Sjkunz
727993229b6Sjkunz //! The identifier, length, and flags fields are taken from \a section.
728993229b6Sjkunz //!
729993229b6Sjkunz //! \todo How does length get set correctly if the length is supposed to include
730993229b6Sjkunz //! this command?
TagCommand(const Section & section)731993229b6Sjkunz EncoreBootImage::TagCommand::TagCommand(const Section & section)
732993229b6Sjkunz {
733993229b6Sjkunz m_sectionIdentifier = section.getIdentifier();
734993229b6Sjkunz m_sectionLength = section.getBlockCount();
735993229b6Sjkunz m_sectionFlags = section.getFlags();
736993229b6Sjkunz }
737993229b6Sjkunz
738993229b6Sjkunz //! \param blocks Pointer to the raw data blocks.
739993229b6Sjkunz //! \param count Number of blocks pointed to by \a blocks.
740993229b6Sjkunz //! \param[out] consumed On exit, this points to the number of cipher blocks that were occupied
741993229b6Sjkunz //! by the command. Should be at least 1 for every command. This must not be NULL
742993229b6Sjkunz //! on entry!
743993229b6Sjkunz //!
744993229b6Sjkunz //! \exception std::runtime_error Thrown if header fields are invalid.
initFromData(const cipher_block_t * blocks,unsigned count,unsigned * consumed)745993229b6Sjkunz void EncoreBootImage::TagCommand::initFromData(const cipher_block_t * blocks, unsigned count, unsigned * consumed)
746993229b6Sjkunz {
747993229b6Sjkunz const boot_command_t model = { 0, ROM_TAG_CMD, 0, 0, 0, 0 };
748993229b6Sjkunz const boot_command_t * header = reinterpret_cast<const boot_command_t *>(blocks);
749993229b6Sjkunz validateHeader(&model, header, CMD_TAG_FIELD);
750993229b6Sjkunz
751993229b6Sjkunz // read fields from header
752993229b6Sjkunz m_isLast = (ENDIAN_LITTLE_TO_HOST_U16(header->m_flags) & ROM_LAST_TAG) != 0;
753993229b6Sjkunz m_sectionIdentifier = ENDIAN_LITTLE_TO_HOST_U32(header->m_address);
754993229b6Sjkunz m_sectionLength = ENDIAN_LITTLE_TO_HOST_U32(header->m_count);
755993229b6Sjkunz m_sectionFlags = ENDIAN_LITTLE_TO_HOST_U32(header->m_data);
756993229b6Sjkunz
757993229b6Sjkunz *consumed = 1;
758993229b6Sjkunz }
759993229b6Sjkunz
760993229b6Sjkunz //! This method currently assumes that the next tag command will come immediately
761993229b6Sjkunz //! after the data for this section.
fillCommandHeader(boot_command_t & header)762993229b6Sjkunz void EncoreBootImage::TagCommand::fillCommandHeader(boot_command_t & header)
763993229b6Sjkunz {
764993229b6Sjkunz header.m_tag = getTag();
765993229b6Sjkunz header.m_flags = ENDIAN_HOST_TO_LITTLE_U16(m_isLast ? ROM_LAST_TAG : 0);
766993229b6Sjkunz header.m_address = ENDIAN_HOST_TO_LITTLE_U32(m_sectionIdentifier);
767993229b6Sjkunz header.m_count = ENDIAN_HOST_TO_LITTLE_U32(m_sectionLength);
768993229b6Sjkunz header.m_data = ENDIAN_HOST_TO_LITTLE_U32(m_sectionFlags);
769993229b6Sjkunz header.m_checksum = calculateChecksum(header); // do this last
770993229b6Sjkunz }
771993229b6Sjkunz
debugPrint() const772993229b6Sjkunz void EncoreBootImage::TagCommand::debugPrint() const
773993229b6Sjkunz {
774993229b6Sjkunz Log::log(Logger::INFO2, " BTAG | sec=0x%08x | cnt=0x%08x | flg=0x%08x\n", m_sectionIdentifier, m_sectionLength, m_sectionFlags);
775993229b6Sjkunz }
776993229b6Sjkunz
777993229b6Sjkunz //! All fields are set to zero.
778993229b6Sjkunz //!
LoadCommand()779993229b6Sjkunz EncoreBootImage::LoadCommand::LoadCommand()
780993229b6Sjkunz : BootCommand(), m_data(), m_padCount(0), m_length(0), m_address(0), m_loadDCD(false)
781993229b6Sjkunz {
782993229b6Sjkunz fillPadding();
783993229b6Sjkunz }
784993229b6Sjkunz
LoadCommand(uint32_t address,const uint8_t * data,uint32_t length)785993229b6Sjkunz EncoreBootImage::LoadCommand::LoadCommand(uint32_t address, const uint8_t * data, uint32_t length)
786993229b6Sjkunz : BootCommand(), m_data(), m_padCount(0), m_length(0), m_address(address), m_loadDCD(false)
787993229b6Sjkunz {
788993229b6Sjkunz fillPadding();
789993229b6Sjkunz setData(data, length);
790993229b6Sjkunz }
791993229b6Sjkunz
792993229b6Sjkunz //! \param blocks Pointer to the raw data blocks.
793993229b6Sjkunz //! \param count Number of blocks pointed to by \a blocks.
794993229b6Sjkunz //! \param[out] consumed On exit, this points to the number of cipher blocks that were occupied
795993229b6Sjkunz //! by the command. Should be at least 1 for every command. This must not be NULL
796993229b6Sjkunz //! on entry!
797993229b6Sjkunz //!
798993229b6Sjkunz //! \exception std::runtime_error This exception is thrown if the actual CRC of the load
799993229b6Sjkunz //! data does not match the CRC stored in the command header. Also thrown if the
800993229b6Sjkunz //! \a count parameter is less than the number of data blocks needed for the length
801993229b6Sjkunz //! specified in the command header or if header fields are invalid.
initFromData(const cipher_block_t * blocks,unsigned count,unsigned * consumed)802993229b6Sjkunz void EncoreBootImage::LoadCommand::initFromData(const cipher_block_t * blocks, unsigned count, unsigned * consumed)
803993229b6Sjkunz {
804993229b6Sjkunz // check static fields
805993229b6Sjkunz const boot_command_t model = { 0, ROM_LOAD_CMD, 0, 0, 0, 0 };
806993229b6Sjkunz const boot_command_t * header = reinterpret_cast<const boot_command_t *>(blocks);
807993229b6Sjkunz validateHeader(&model, header, CMD_TAG_FIELD);
808993229b6Sjkunz
809993229b6Sjkunz // read fields from header
810993229b6Sjkunz m_address = ENDIAN_LITTLE_TO_HOST_U32(header->m_address);
811993229b6Sjkunz m_length = ENDIAN_LITTLE_TO_HOST_U32(header->m_count);
812993229b6Sjkunz unsigned crc = ENDIAN_LITTLE_TO_HOST_U32(header->m_data);
813993229b6Sjkunz unsigned dataBlockCount = numberOfCipherBlocks(m_length);
814993229b6Sjkunz m_padCount = sizeOfPaddingForCipherBlocks(dataBlockCount);
815993229b6Sjkunz m_loadDCD = (ENDIAN_LITTLE_TO_HOST_U16(header->m_flags) & ROM_LOAD_DCD) != 0;
816993229b6Sjkunz
817993229b6Sjkunz // make sure there are enough blocks
818993229b6Sjkunz if (count - 1 < dataBlockCount)
819993229b6Sjkunz {
820993229b6Sjkunz throw std::runtime_error("not enough cipher blocks for load data");
821993229b6Sjkunz }
822993229b6Sjkunz
823993229b6Sjkunz // copy data
824993229b6Sjkunz setData(reinterpret_cast<const uint8_t *>(blocks + 1), m_length);
825993229b6Sjkunz
826993229b6Sjkunz // copy padding
827993229b6Sjkunz if (m_padCount)
828993229b6Sjkunz {
829993229b6Sjkunz const uint8_t * firstPadByte = reinterpret_cast<const uint8_t *> (blocks + (1 + dataBlockCount)) - m_padCount;
830993229b6Sjkunz memcpy(m_padding, firstPadByte, m_padCount);
831993229b6Sjkunz }
832993229b6Sjkunz
833993229b6Sjkunz // check CRC
834993229b6Sjkunz uint32_t actualCRC = calculateCRC();
835993229b6Sjkunz if (actualCRC != crc)
836993229b6Sjkunz {
837993229b6Sjkunz throw std::runtime_error("load data failed CRC check");
838993229b6Sjkunz }
839993229b6Sjkunz
840993229b6Sjkunz *consumed = 1 + dataBlockCount;
841993229b6Sjkunz }
842993229b6Sjkunz
843993229b6Sjkunz //! The only thing unique in the load command header is the
844993229b6Sjkunz //! #elftosb::EncoreBootImage::boot_command_t::m_data. It contains a CRC-32 over the
845993229b6Sjkunz //! load data, plus any bytes of padding in the last data cipher block.
fillCommandHeader(boot_command_t & header)846993229b6Sjkunz void EncoreBootImage::LoadCommand::fillCommandHeader(boot_command_t & header)
847993229b6Sjkunz {
848993229b6Sjkunz header.m_tag = getTag();
849993229b6Sjkunz header.m_flags = ENDIAN_HOST_TO_LITTLE_U16(m_loadDCD ? ROM_LOAD_DCD : 0);
850993229b6Sjkunz header.m_address = ENDIAN_HOST_TO_LITTLE_U32(m_address);
851993229b6Sjkunz header.m_count = ENDIAN_HOST_TO_LITTLE_U32(m_length);
852993229b6Sjkunz header.m_data = ENDIAN_HOST_TO_LITTLE_U32(calculateCRC());
853993229b6Sjkunz
854993229b6Sjkunz // do this last
855993229b6Sjkunz header.m_checksum = calculateChecksum(header);
856993229b6Sjkunz }
857993229b6Sjkunz
858993229b6Sjkunz //! A CRC-32 is calculated over the load data, including any pad bytes
859993229b6Sjkunz //! that are required in the last data cipher block. Including the
860993229b6Sjkunz //! pad bytes in the CRC makes it vastly easier for the ROM to calculate
861993229b6Sjkunz //! the CRC for validation.
calculateCRC() const862993229b6Sjkunz uint32_t EncoreBootImage::LoadCommand::calculateCRC() const
863993229b6Sjkunz {
864993229b6Sjkunz uint32_t result;
865993229b6Sjkunz CRC32 crc;
866993229b6Sjkunz crc.update(m_data, m_length);
867993229b6Sjkunz if (m_padCount)
868993229b6Sjkunz {
869993229b6Sjkunz // include random padding in the CRC
870993229b6Sjkunz crc.update(m_padding, m_padCount);
871993229b6Sjkunz }
872993229b6Sjkunz crc.truncatedFinal(reinterpret_cast<uint8_t*>(&result), sizeof(result));
873993229b6Sjkunz
874993229b6Sjkunz return result;
875993229b6Sjkunz }
876993229b6Sjkunz
877993229b6Sjkunz //! A local copy of the load data is made. This copy will be disposed of when this object
878993229b6Sjkunz //! is destroyed. This means the caller is free to deallocate \a data after this call
879993229b6Sjkunz //! returns. It also means the caller can pass a pointer into the middle of a buffer for
880993229b6Sjkunz //! \a data and not worry about ownership issues.
setData(const uint8_t * data,uint32_t length)881993229b6Sjkunz void EncoreBootImage::LoadCommand::setData(const uint8_t * data, uint32_t length)
882993229b6Sjkunz {
883993229b6Sjkunz assert(data);
884993229b6Sjkunz assert(length);
885993229b6Sjkunz
886993229b6Sjkunz uint8_t * dataCopy = new uint8_t[length];
887993229b6Sjkunz memcpy(dataCopy, data, length);
888993229b6Sjkunz
889993229b6Sjkunz m_data = dataCopy;
890993229b6Sjkunz m_length = length;
891993229b6Sjkunz
892993229b6Sjkunz m_padCount = sizeOfPaddingForCipherBlocks(m_length);
893993229b6Sjkunz }
894993229b6Sjkunz
895993229b6Sjkunz //! \return The number of cipher blocks required to hold the load data,
896993229b6Sjkunz //! rounded up as necessary.
getDataBlockCount() const897993229b6Sjkunz unsigned EncoreBootImage::LoadCommand::getDataBlockCount() const
898993229b6Sjkunz {
899993229b6Sjkunz // round up to the next cipher block
900993229b6Sjkunz return numberOfCipherBlocks(m_length);
901993229b6Sjkunz }
902993229b6Sjkunz
903993229b6Sjkunz //! Up to \a maxCount data blocks are copied into the buffer pointed to by
904993229b6Sjkunz //! the \a data argument. This is only a request for \a maxCount blocks.
905993229b6Sjkunz //! A return value of 0 indicates that no more blocks are available. The
906993229b6Sjkunz //! index of the first block to copy is held in the \a offset argument.
907993229b6Sjkunz //! If there are pad bytes needed to fill out the last data block, they
908993229b6Sjkunz //! will be filled with random data in order to add to the "whiteness" of
909993229b6Sjkunz //! the data on both sides of encryption.
910993229b6Sjkunz //!
911993229b6Sjkunz //! \param offset Starting block number to copy. Zero means the first available block.
912993229b6Sjkunz //! \param maxCount Up to this number of blocks may be copied into \a data. Must be 1 or greater.
913993229b6Sjkunz //! \param data Buffer for outgoing data blocks. Must have enough room to hold
914993229b6Sjkunz //! \a maxCount blocks.
915993229b6Sjkunz //!
916993229b6Sjkunz //! \return The number of data blocks copied into \a data.
917993229b6Sjkunz //! \retval 0 No more blocks are available and nothing was written to \a data.
918993229b6Sjkunz //!
919993229b6Sjkunz //! \exception std::out_of_range Thrown when offset is invalid.
920993229b6Sjkunz //!
921993229b6Sjkunz //! \todo fill pad bytes with random bytes
getDataBlocks(unsigned offset,unsigned maxCount,cipher_block_t * data)922993229b6Sjkunz unsigned EncoreBootImage::LoadCommand::getDataBlocks(unsigned offset, unsigned maxCount, cipher_block_t * data)
923993229b6Sjkunz {
924993229b6Sjkunz assert(data);
925993229b6Sjkunz assert(maxCount != 0);
926993229b6Sjkunz
927993229b6Sjkunz uint32_t blockCount = getDataBlockCount();
928993229b6Sjkunz
929993229b6Sjkunz // check offset
930993229b6Sjkunz if (offset >= blockCount)
931993229b6Sjkunz {
932993229b6Sjkunz throw std::out_of_range("invalid offset");
933993229b6Sjkunz }
934993229b6Sjkunz
935993229b6Sjkunz // figure out how many blocks to return
936993229b6Sjkunz unsigned resultBlocks = blockCount - offset;
937993229b6Sjkunz if (resultBlocks > maxCount)
938993229b6Sjkunz {
939993229b6Sjkunz resultBlocks = maxCount;
940993229b6Sjkunz
941993229b6Sjkunz // exclude last block if there is padding
942993229b6Sjkunz if (m_padCount && (offset != blockCount - 1) && (offset + resultBlocks == blockCount))
943993229b6Sjkunz {
944993229b6Sjkunz resultBlocks--;
945993229b6Sjkunz }
946993229b6Sjkunz }
947993229b6Sjkunz
948993229b6Sjkunz // if there are pad bytes, handle the last block specially
949993229b6Sjkunz if (m_padCount && offset == blockCount - 1)
950993229b6Sjkunz {
951993229b6Sjkunz // copy the remainder of the load data into the first part of the result block
952993229b6Sjkunz unsigned remainderLength = sizeof(cipher_block_t) - m_padCount;
953993229b6Sjkunz memcpy(data, &m_data[sizeof(cipher_block_t) * offset], remainderLength);
954993229b6Sjkunz
955993229b6Sjkunz // copy pad bytes we previously generated into the last part of the result block
956993229b6Sjkunz // data is a cipher block pointer, so indexing is done on cipher block
957993229b6Sjkunz // boundaries, thus we need a byte pointer to index properly
958993229b6Sjkunz uint8_t * bytePtr = reinterpret_cast<uint8_t*>(data);
959993229b6Sjkunz memcpy(bytePtr + remainderLength, &m_padding, m_padCount);
960993229b6Sjkunz }
961993229b6Sjkunz else
962993229b6Sjkunz {
963993229b6Sjkunz memcpy(data, &m_data[sizeof(cipher_block_t) * offset], sizeof(cipher_block_t) * resultBlocks);
964993229b6Sjkunz }
965993229b6Sjkunz
966993229b6Sjkunz return resultBlocks;
967993229b6Sjkunz }
968993229b6Sjkunz
969993229b6Sjkunz //! Fills #m_padding with random bytes that may be used to fill up the last data
970993229b6Sjkunz //! cipher block.
fillPadding()971993229b6Sjkunz void EncoreBootImage::LoadCommand::fillPadding()
972993229b6Sjkunz {
973993229b6Sjkunz RandomNumberGenerator rng;
974993229b6Sjkunz rng.generateBlock(m_padding, sizeof(m_padding));
975993229b6Sjkunz }
976993229b6Sjkunz
debugPrint() const977993229b6Sjkunz void EncoreBootImage::LoadCommand::debugPrint() const
978993229b6Sjkunz {
979993229b6Sjkunz 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);
980993229b6Sjkunz }
981993229b6Sjkunz
982993229b6Sjkunz //! The pattern, address, and count are all initialized to zero, and the pattern
983993229b6Sjkunz //! size is set to a word.
FillCommand()984993229b6Sjkunz EncoreBootImage::FillCommand::FillCommand()
985993229b6Sjkunz : BootCommand(), m_address(0), m_count(0), m_pattern(0)
986993229b6Sjkunz {
987993229b6Sjkunz }
988993229b6Sjkunz
989993229b6Sjkunz //! \param blocks Pointer to the raw data blocks.
990993229b6Sjkunz //! \param count Number of blocks pointed to by \a blocks.
991993229b6Sjkunz //! \param[out] consumed On exit, this points to the number of cipher blocks that were occupied
992993229b6Sjkunz //! by the command. Should be at least 1 for every command. This must not be NULL
993993229b6Sjkunz //! on entry!
994993229b6Sjkunz //!
995993229b6Sjkunz //! \exception std::runtime_error Thrown if header fields are invalid.
initFromData(const cipher_block_t * blocks,unsigned count,unsigned * consumed)996993229b6Sjkunz void EncoreBootImage::FillCommand::initFromData(const cipher_block_t * blocks, unsigned count, unsigned * consumed)
997993229b6Sjkunz {
998993229b6Sjkunz // check static fields
999993229b6Sjkunz const boot_command_t model = { 0, ROM_FILL_CMD, 0, 0, 0, 0 };
1000993229b6Sjkunz const boot_command_t * header = reinterpret_cast<const boot_command_t *>(blocks);
1001993229b6Sjkunz validateHeader(&model, header, CMD_TAG_FIELD | CMD_FLAGS_FIELD);
1002993229b6Sjkunz
1003993229b6Sjkunz // read fields from header
1004993229b6Sjkunz m_address = ENDIAN_LITTLE_TO_HOST_U32(header->m_address);
1005993229b6Sjkunz m_count = ENDIAN_LITTLE_TO_HOST_U32(header->m_count);
1006993229b6Sjkunz m_pattern = ENDIAN_LITTLE_TO_HOST_U32(header->m_data);
1007993229b6Sjkunz
1008993229b6Sjkunz *consumed = 1;
1009993229b6Sjkunz }
1010993229b6Sjkunz
fillCommandHeader(boot_command_t & header)1011993229b6Sjkunz void EncoreBootImage::FillCommand::fillCommandHeader(boot_command_t & header)
1012993229b6Sjkunz {
1013993229b6Sjkunz header.m_tag = getTag();
1014993229b6Sjkunz header.m_flags = 0;
1015993229b6Sjkunz header.m_address = ENDIAN_HOST_TO_LITTLE_U32(m_address);
1016993229b6Sjkunz header.m_count = ENDIAN_HOST_TO_LITTLE_U32(m_count);
1017993229b6Sjkunz header.m_data = ENDIAN_HOST_TO_LITTLE_U32(m_pattern);
1018993229b6Sjkunz header.m_checksum = calculateChecksum(header); // do this last
1019993229b6Sjkunz }
1020993229b6Sjkunz
1021993229b6Sjkunz //! Extends the pattern across 32 bits.
1022993229b6Sjkunz //!
setPattern(uint8_t pattern)1023993229b6Sjkunz void EncoreBootImage::FillCommand::setPattern(uint8_t pattern)
1024993229b6Sjkunz {
1025993229b6Sjkunz m_pattern = (pattern << 24) | (pattern << 16) | (pattern << 8) | pattern;
1026993229b6Sjkunz }
1027993229b6Sjkunz
1028993229b6Sjkunz //! Extends the pattern across 32 bits.
1029993229b6Sjkunz //!
setPattern(uint16_t pattern)1030993229b6Sjkunz void EncoreBootImage::FillCommand::setPattern(uint16_t pattern)
1031993229b6Sjkunz {
1032993229b6Sjkunz m_pattern = (pattern << 16) | pattern;
1033993229b6Sjkunz }
1034993229b6Sjkunz
setPattern(uint32_t pattern)1035993229b6Sjkunz void EncoreBootImage::FillCommand::setPattern(uint32_t pattern)
1036993229b6Sjkunz {
1037993229b6Sjkunz m_pattern = pattern;
1038993229b6Sjkunz }
1039993229b6Sjkunz
debugPrint() const1040993229b6Sjkunz void EncoreBootImage::FillCommand::debugPrint() const
1041993229b6Sjkunz {
1042993229b6Sjkunz Log::log(Logger::INFO2, " FILL | adr=0x%08x | len=0x%08x | ptn=0x%08x\n", m_address, m_count, m_pattern);
1043993229b6Sjkunz }
1044993229b6Sjkunz
1045993229b6Sjkunz //! \param blocks Pointer to the raw data blocks.
1046993229b6Sjkunz //! \param count Number of blocks pointed to by \a blocks.
1047993229b6Sjkunz //! \param[out] consumed On exit, this points to the number of cipher blocks that were occupied
1048993229b6Sjkunz //! by the command. Should be at least 1 for every command. This must not be NULL
1049993229b6Sjkunz //! on entry!
1050993229b6Sjkunz //!
1051993229b6Sjkunz //! \exception std::runtime_error Thrown if header fields are invalid.
initFromData(const cipher_block_t * blocks,unsigned count,unsigned * consumed)1052993229b6Sjkunz void EncoreBootImage::ModeCommand::initFromData(const cipher_block_t * blocks, unsigned count, unsigned * consumed)
1053993229b6Sjkunz {
1054993229b6Sjkunz // check static fields
1055993229b6Sjkunz const boot_command_t model = { 0, ROM_MODE_CMD, 0, 0, 0, 0 };
1056993229b6Sjkunz const boot_command_t * header = reinterpret_cast<const boot_command_t *>(blocks);
1057993229b6Sjkunz validateHeader(&model, header, CMD_TAG_FIELD | CMD_FLAGS_FIELD | CMD_ADDRESS_FIELD | CMD_COUNT_FIELD);
1058993229b6Sjkunz
1059993229b6Sjkunz // read fields from header
1060993229b6Sjkunz m_mode = ENDIAN_LITTLE_TO_HOST_U32(header->m_data);
1061993229b6Sjkunz
1062993229b6Sjkunz *consumed = 1;
1063993229b6Sjkunz }
1064993229b6Sjkunz
fillCommandHeader(boot_command_t & header)1065993229b6Sjkunz void EncoreBootImage::ModeCommand::fillCommandHeader(boot_command_t & header)
1066993229b6Sjkunz {
1067993229b6Sjkunz header.m_tag = getTag();
1068993229b6Sjkunz header.m_flags = 0;
1069993229b6Sjkunz header.m_address = 0;
1070993229b6Sjkunz header.m_count = 0;
1071993229b6Sjkunz header.m_data = ENDIAN_HOST_TO_LITTLE_U32(m_mode);
1072993229b6Sjkunz header.m_checksum = calculateChecksum(header); // do this last
1073993229b6Sjkunz }
1074993229b6Sjkunz
debugPrint() const1075993229b6Sjkunz void EncoreBootImage::ModeCommand::debugPrint() const
1076993229b6Sjkunz {
1077993229b6Sjkunz Log::log(Logger::INFO2, " MODE | mod=0x%08x\n", m_mode);
1078993229b6Sjkunz }
1079993229b6Sjkunz
1080993229b6Sjkunz //! \param blocks Pointer to the raw data blocks.
1081993229b6Sjkunz //! \param count Number of blocks pointed to by \a blocks.
1082993229b6Sjkunz //! \param[out] consumed On exit, this points to the number of cipher blocks that were occupied
1083993229b6Sjkunz //! by the command. Should be at least 1 for every command. This must not be NULL
1084993229b6Sjkunz //! on entry!
1085993229b6Sjkunz //!
1086993229b6Sjkunz //! \exception std::runtime_error Thrown if header fields are invalid.
initFromData(const cipher_block_t * blocks,unsigned count,unsigned * consumed)1087993229b6Sjkunz void EncoreBootImage::JumpCommand::initFromData(const cipher_block_t * blocks, unsigned count, unsigned * consumed)
1088993229b6Sjkunz {
1089993229b6Sjkunz // check static fields
1090993229b6Sjkunz const boot_command_t model = { 0, getTag(), 0, 0, 0, 0 };
1091993229b6Sjkunz const boot_command_t * header = reinterpret_cast<const boot_command_t *>(blocks);
1092993229b6Sjkunz validateHeader(&model, header, CMD_TAG_FIELD | CMD_COUNT_FIELD);
1093993229b6Sjkunz
1094993229b6Sjkunz // read fields from header
1095993229b6Sjkunz m_address = ENDIAN_LITTLE_TO_HOST_U32(header->m_address);
1096993229b6Sjkunz m_argument = ENDIAN_LITTLE_TO_HOST_U32(header->m_data);
1097993229b6Sjkunz m_isHAB = (ENDIAN_LITTLE_TO_HOST_U16(header->m_flags) & ROM_HAB_EXEC) != 0;
1098993229b6Sjkunz
1099993229b6Sjkunz *consumed = 1;
1100993229b6Sjkunz }
1101993229b6Sjkunz
fillCommandHeader(boot_command_t & header)1102993229b6Sjkunz void EncoreBootImage::JumpCommand::fillCommandHeader(boot_command_t & header)
1103993229b6Sjkunz {
1104993229b6Sjkunz header.m_tag = getTag();
1105993229b6Sjkunz header.m_flags = ENDIAN_HOST_TO_LITTLE_U16(m_isHAB ? ROM_HAB_EXEC : 0);
1106993229b6Sjkunz header.m_address = ENDIAN_HOST_TO_LITTLE_U32(m_address);
1107993229b6Sjkunz header.m_count = 0;
1108993229b6Sjkunz header.m_data = ENDIAN_HOST_TO_LITTLE_U32(m_argument);
1109993229b6Sjkunz header.m_checksum = calculateChecksum(header); // do this last
1110993229b6Sjkunz }
1111993229b6Sjkunz
debugPrint() const1112993229b6Sjkunz void EncoreBootImage::JumpCommand::debugPrint() const
1113993229b6Sjkunz {
1114993229b6Sjkunz 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);
1115993229b6Sjkunz }
1116993229b6Sjkunz
debugPrint() const1117993229b6Sjkunz void EncoreBootImage::CallCommand::debugPrint() const
1118993229b6Sjkunz {
1119993229b6Sjkunz 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);
1120993229b6Sjkunz }
1121993229b6Sjkunz
1122993229b6Sjkunz //! Only if the section has been assigned a boot image owner object will this
1123993229b6Sjkunz //! method be able to fill in the #section_header_t::m_offset field. If no
1124993229b6Sjkunz //! boot image has been set the offset will be set to 0.
fillSectionHeader(section_header_t & header)1125993229b6Sjkunz void EncoreBootImage::Section::fillSectionHeader(section_header_t & header)
1126993229b6Sjkunz {
1127993229b6Sjkunz header.m_tag = getIdentifier();
1128993229b6Sjkunz header.m_offset = 0;
1129993229b6Sjkunz header.m_length = ENDIAN_HOST_TO_LITTLE_U32(getBlockCount());
1130993229b6Sjkunz header.m_flags = ENDIAN_HOST_TO_LITTLE_U32(getFlags());
1131993229b6Sjkunz
1132993229b6Sjkunz // if we're attached to an image, we can compute our real offset
1133993229b6Sjkunz if (m_image)
1134993229b6Sjkunz {
1135993229b6Sjkunz header.m_offset = ENDIAN_HOST_TO_LITTLE_U32(m_image->getSectionOffset(this));
1136993229b6Sjkunz }
1137993229b6Sjkunz }
1138993229b6Sjkunz
1139993229b6Sjkunz //! The alignment will never be less than 16, since that is the size of the
1140993229b6Sjkunz //! cipher block which is the basic unit of the boot image format. If an
1141993229b6Sjkunz //! alignment less than 16 is set it will be ignored.
1142993229b6Sjkunz //!
1143993229b6Sjkunz //! \param alignment Alignment in bytes for this section. Must be a power of two.
1144993229b6Sjkunz //! Ignored if less than 16.
setAlignment(unsigned alignment)1145993229b6Sjkunz void EncoreBootImage::Section::setAlignment(unsigned alignment)
1146993229b6Sjkunz {
1147993229b6Sjkunz if (alignment > BOOT_IMAGE_MINIMUM_SECTION_ALIGNMENT)
1148993229b6Sjkunz {
1149993229b6Sjkunz m_alignment = alignment;
1150993229b6Sjkunz }
1151993229b6Sjkunz }
1152993229b6Sjkunz
1153993229b6Sjkunz //! This method calculates the number of padding blocks that need to be inserted
1154993229b6Sjkunz //! from a given offset for the section to be properly aligned. The value returned
1155993229b6Sjkunz //! is the number of padding blocks that should be inserted starting just after
1156993229b6Sjkunz //! \a offset to align the first cipher block of the section contents. The section's
1157993229b6Sjkunz //! boot tag is \i not taken into account by this method, so the caller must
1158993229b6Sjkunz //! deal with that herself.
1159993229b6Sjkunz //!
1160993229b6Sjkunz //! \param offset Start offset in cipher blocks (not bytes).
1161993229b6Sjkunz //!
1162993229b6Sjkunz //! \return A number of cipher blocks of padding to insert.
getPadBlockCountForOffset(unsigned offset)1163993229b6Sjkunz unsigned EncoreBootImage::Section::getPadBlockCountForOffset(unsigned offset)
1164993229b6Sjkunz {
1165993229b6Sjkunz // convert alignment from byte to block alignment
1166993229b6Sjkunz unsigned blockAlignment = m_alignment >> 4;
1167993229b6Sjkunz
1168993229b6Sjkunz unsigned nextAlignmentOffset = (offset + blockAlignment - 1) / blockAlignment * blockAlignment;
1169993229b6Sjkunz
1170993229b6Sjkunz return nextAlignmentOffset - offset;
1171993229b6Sjkunz }
1172993229b6Sjkunz
~BootSection()1173993229b6Sjkunz EncoreBootImage::BootSection::~BootSection()
1174993229b6Sjkunz {
1175993229b6Sjkunz deleteCommands();
1176993229b6Sjkunz }
1177993229b6Sjkunz
deleteCommands()1178993229b6Sjkunz void EncoreBootImage::BootSection::deleteCommands()
1179993229b6Sjkunz {
1180993229b6Sjkunz // dispose of all sections
1181993229b6Sjkunz iterator_t it = begin();
1182993229b6Sjkunz for (; it != end(); ++it)
1183993229b6Sjkunz {
1184993229b6Sjkunz delete *it;
1185993229b6Sjkunz }
1186993229b6Sjkunz }
1187993229b6Sjkunz
1188993229b6Sjkunz //! Always returns at least 1 for the required tag command.
1189993229b6Sjkunz //!
getBlockCount() const1190993229b6Sjkunz unsigned EncoreBootImage::BootSection::getBlockCount() const
1191993229b6Sjkunz {
1192993229b6Sjkunz unsigned count = 0;
1193993229b6Sjkunz
1194993229b6Sjkunz const_iterator_t it = begin();
1195993229b6Sjkunz for (; it != end(); ++it)
1196993229b6Sjkunz {
1197993229b6Sjkunz count += (*it)->getBlockCount();
1198993229b6Sjkunz }
1199993229b6Sjkunz
1200993229b6Sjkunz return count;
1201993229b6Sjkunz }
1202993229b6Sjkunz
1203993229b6Sjkunz //! Up to \a maxCount cipher blocks are copied into the buffer pointed to by
1204993229b6Sjkunz //! the \a data argument. A return value of 0 indicates that
1205993229b6Sjkunz //! no more blocks are available. The index of the first block to copy is
1206993229b6Sjkunz //! held in the \a offset argument.
1207993229b6Sjkunz //!
1208993229b6Sjkunz //! \param offset Starting block number to copy. Zero means the first available block.
1209993229b6Sjkunz //! \param maxCount Up to this number of blocks may be copied into \a data.
1210993229b6Sjkunz //! \param data Buffer for outgoing cipher blocks. Must have enough room to hold
1211993229b6Sjkunz //! \a maxCount blocks.
1212993229b6Sjkunz //!
1213993229b6Sjkunz //! \return The number of cipher blocks copied into \a data.
1214993229b6Sjkunz //! \retval 0 No more blocks are available and nothing was written to \a data.
getBlocks(unsigned offset,unsigned maxCount,cipher_block_t * data)1215993229b6Sjkunz unsigned EncoreBootImage::BootSection::getBlocks(unsigned offset, unsigned maxCount, cipher_block_t * data)
1216993229b6Sjkunz {
1217993229b6Sjkunz assert(data);
1218993229b6Sjkunz assert(maxCount >= 1);
1219993229b6Sjkunz
1220993229b6Sjkunz unsigned currentOffset = 0;
1221993229b6Sjkunz unsigned readCount = maxCount;
1222993229b6Sjkunz
1223993229b6Sjkunz iterator_t it = begin();
1224993229b6Sjkunz for (; it != end(); ++it)
1225993229b6Sjkunz {
1226993229b6Sjkunz BootCommand * command = *it;
1227993229b6Sjkunz unsigned commandBlocks = command->getBlockCount();
1228993229b6Sjkunz
1229993229b6Sjkunz // this should never be false!
1230993229b6Sjkunz assert(offset >= currentOffset);
1231993229b6Sjkunz
1232993229b6Sjkunz // skip forward until we hit the requested offset
1233993229b6Sjkunz if (offset >= currentOffset + commandBlocks)
1234993229b6Sjkunz {
1235993229b6Sjkunz currentOffset += commandBlocks;
1236993229b6Sjkunz continue;
1237993229b6Sjkunz }
1238993229b6Sjkunz
1239993229b6Sjkunz // read from this command
1240993229b6Sjkunz unsigned commandOffset = offset - currentOffset;
1241993229b6Sjkunz unsigned commandRemaining = commandBlocks - commandOffset;
1242993229b6Sjkunz if (readCount > commandRemaining)
1243993229b6Sjkunz {
1244993229b6Sjkunz readCount = commandRemaining;
1245993229b6Sjkunz }
1246993229b6Sjkunz return command->getBlocks(commandOffset, readCount, data);
1247993229b6Sjkunz }
1248993229b6Sjkunz
1249993229b6Sjkunz return 0;
1250993229b6Sjkunz }
1251993229b6Sjkunz
1252993229b6Sjkunz //! The entire contents of the section must be in memory, pointed to by \a blocks.
1253993229b6Sjkunz //! Any commands that had previously been added to the section are disposed of.
1254993229b6Sjkunz //!
1255993229b6Sjkunz //! \param blocks Pointer to the section contents.
1256993229b6Sjkunz //! \param count Number of blocks pointed to by \a blocks.
1257993229b6Sjkunz //!
1258993229b6Sjkunz //! \exception std::runtime_error Thrown if a boot command cannot be created from
1259993229b6Sjkunz //! the cipher block stream.
fillFromData(const cipher_block_t * blocks,unsigned count)1260993229b6Sjkunz void EncoreBootImage::BootSection::fillFromData(const cipher_block_t * blocks, unsigned count)
1261993229b6Sjkunz {
1262993229b6Sjkunz // start with an empty slate
1263993229b6Sjkunz deleteCommands();
1264993229b6Sjkunz
1265993229b6Sjkunz const cipher_block_t * currentBlock = blocks;
1266993229b6Sjkunz unsigned remaining = count;
1267993229b6Sjkunz while (remaining)
1268993229b6Sjkunz {
1269993229b6Sjkunz // try to create a command from the next cipher block. the number of
1270993229b6Sjkunz // blocks the command used up is returned in consumed.
1271993229b6Sjkunz unsigned consumed;
1272993229b6Sjkunz BootCommand * command = BootCommand::createFromData(currentBlock, remaining, &consumed);
1273993229b6Sjkunz if (!command)
1274993229b6Sjkunz {
1275993229b6Sjkunz throw std::runtime_error("invalid boot section data");
1276993229b6Sjkunz }
1277993229b6Sjkunz
1278993229b6Sjkunz addCommand(command);
1279993229b6Sjkunz
1280993229b6Sjkunz // update loop counters
1281993229b6Sjkunz remaining -= consumed;
1282993229b6Sjkunz currentBlock += consumed;
1283993229b6Sjkunz }
1284993229b6Sjkunz }
1285993229b6Sjkunz
debugPrint() const1286993229b6Sjkunz void EncoreBootImage::BootSection::debugPrint() const
1287993229b6Sjkunz {
1288993229b6Sjkunz Log::log(Logger::INFO2, "Boot Section 0x%08x:\n", m_identifier);
1289993229b6Sjkunz
1290993229b6Sjkunz const_iterator_t it = begin();
1291993229b6Sjkunz for (; it != end(); ++it)
1292993229b6Sjkunz {
1293993229b6Sjkunz const BootCommand * command = *it;
1294993229b6Sjkunz command->debugPrint();
1295993229b6Sjkunz }
1296993229b6Sjkunz }
1297993229b6Sjkunz
1298993229b6Sjkunz //! A copy is made of \a data. Any previously assigned data is disposed of.
1299993229b6Sjkunz //!
setData(const uint8_t * data,unsigned length)1300993229b6Sjkunz void EncoreBootImage::DataSection::setData(const uint8_t * data, unsigned length)
1301993229b6Sjkunz {
1302993229b6Sjkunz m_data = new uint8_t[length];
1303993229b6Sjkunz memcpy(m_data.get(), data, length);
1304993229b6Sjkunz m_length = length;
1305993229b6Sjkunz }
1306993229b6Sjkunz
1307993229b6Sjkunz //! The section takes ownership of \a data and will dispose of it using the
1308993229b6Sjkunz //! array delete operator upon its destruction.
setDataNoCopy(const uint8_t * data,unsigned length)1309993229b6Sjkunz void EncoreBootImage::DataSection::setDataNoCopy(const uint8_t * data, unsigned length)
1310993229b6Sjkunz {
1311993229b6Sjkunz m_data = data;
1312993229b6Sjkunz m_length = length;
1313993229b6Sjkunz }
1314993229b6Sjkunz
getBlockCount() const1315993229b6Sjkunz unsigned EncoreBootImage::DataSection::getBlockCount() const
1316993229b6Sjkunz {
1317993229b6Sjkunz return numberOfCipherBlocks(m_length);
1318993229b6Sjkunz }
1319993229b6Sjkunz
getBlocks(unsigned offset,unsigned maxCount,cipher_block_t * data)1320993229b6Sjkunz unsigned EncoreBootImage::DataSection::getBlocks(unsigned offset, unsigned maxCount, cipher_block_t * data)
1321993229b6Sjkunz {
1322993229b6Sjkunz assert(data);
1323993229b6Sjkunz assert(maxCount != 0);
1324993229b6Sjkunz
1325993229b6Sjkunz unsigned blockCount = getBlockCount();
1326993229b6Sjkunz unsigned padCount = sizeOfPaddingForCipherBlocks(m_length);
1327993229b6Sjkunz
1328993229b6Sjkunz // check offset
1329993229b6Sjkunz if (offset >= blockCount)
1330993229b6Sjkunz {
1331993229b6Sjkunz throw std::out_of_range("invalid offset");
1332993229b6Sjkunz }
1333993229b6Sjkunz
1334993229b6Sjkunz // figure out how many blocks to return
1335993229b6Sjkunz unsigned resultBlocks = blockCount - offset;
1336993229b6Sjkunz if (resultBlocks > maxCount)
1337993229b6Sjkunz {
1338993229b6Sjkunz resultBlocks = maxCount;
1339993229b6Sjkunz
1340993229b6Sjkunz // exclude last block if there is padding
1341993229b6Sjkunz if (padCount && (offset != blockCount - 1) && (offset + resultBlocks == blockCount))
1342993229b6Sjkunz {
1343993229b6Sjkunz resultBlocks--;
1344993229b6Sjkunz }
1345993229b6Sjkunz }
1346993229b6Sjkunz
1347993229b6Sjkunz // if there are pad bytes, handle the last block specially
1348993229b6Sjkunz if (padCount && offset == blockCount - 1)
1349993229b6Sjkunz {
1350993229b6Sjkunz // copy the remainder of the load data into the first part of the result block
1351993229b6Sjkunz unsigned remainderLength = sizeof(cipher_block_t) - padCount;
1352993229b6Sjkunz memcpy(data, &m_data[sizeOfCipherBlocks(offset)], remainderLength);
1353993229b6Sjkunz
1354993229b6Sjkunz // set pad bytes to zeroes.
1355993229b6Sjkunz // data is a cipher block pointer, so indexing is done on cipher block
1356993229b6Sjkunz // boundaries, thus we need a byte pointer to index properly
1357993229b6Sjkunz uint8_t * bytePtr = reinterpret_cast<uint8_t*>(data);
1358993229b6Sjkunz memset(bytePtr + remainderLength, 0, padCount);
1359993229b6Sjkunz }
1360993229b6Sjkunz else
1361993229b6Sjkunz {
1362993229b6Sjkunz memcpy(data, &m_data[sizeOfCipherBlocks(offset)], sizeOfCipherBlocks(resultBlocks));
1363993229b6Sjkunz }
1364993229b6Sjkunz
1365993229b6Sjkunz return resultBlocks;
1366993229b6Sjkunz }
1367993229b6Sjkunz
debugPrint() const1368993229b6Sjkunz void EncoreBootImage::DataSection::debugPrint() const
1369993229b6Sjkunz {
1370993229b6Sjkunz Log::log(Logger::INFO2, "Data Section 0x%08x: (%d bytes, %d blocks)\n", m_identifier, m_length, getBlockCount());
1371993229b6Sjkunz }
1372993229b6Sjkunz
1373