Files
bootloader/apps/elftosb/elftosb2/EncoreBootImageGenerator.cpp
László Monda e6c1fce5b4 Add KBOOT.
2016-08-10 01:45:15 +02:00

582 lines
19 KiB
C++

/*
* File: EncoreBootImageGenerator.cpp
*
* Copyright (c) Freescale Semiconductor, Inc. All rights reserved.
* See included license file for license details.
*/
#include "EncoreBootImageGenerator.h"
#include "Logging.h"
#include "Keyblob.h"
#include "OptionContext.h"
#define kFlagsOption "flags"
#define kSectionFlagsOption "sectionFlags"
#define kProductVersionOption "productVersion"
#define kComponentVersionOption "componentVersion"
#define kAlignmentOption "alignment"
#define kCleartextOption "cleartext"
using namespace elftosb;
BootImage *EncoreBootImageGenerator::generate()
{
EncoreBootImage *image = new EncoreBootImage();
// process each output section
section_vector_t::iterator it = m_sections.begin();
for (; it != m_sections.end(); ++it)
{
OutputSection *section = *it;
OperationSequenceSection *opSection = dynamic_cast<OperationSequenceSection *>(section);
if (opSection)
{
processOperationSection(opSection, image);
continue;
}
BinaryDataSection *dataSection = dynamic_cast<BinaryDataSection *>(section);
if (dataSection)
{
processDataSection(dataSection, image);
continue;
}
Log::log(Logger::WARNING, "warning: unexpected output section type\n");
}
// handle global options that affect the image
processOptions(image);
return image;
}
void EncoreBootImageGenerator::processOptions(EncoreBootImage *image)
{
// bail if no option context was set
if (!m_options)
{
return;
}
if (m_options->hasOption(kFlagsOption))
{
const IntegerValue *intValue = dynamic_cast<const IntegerValue *>(m_options->getOption(kFlagsOption));
if (intValue)
{
image->setFlags(static_cast<uint16_t>(intValue->getValue()));
}
else
{
Log::log(Logger::WARNING, "warning: flags option is an unexpected type\n");
}
}
// handle common options
processVersionOptions(image);
processDriveTagOption(image);
}
void EncoreBootImageGenerator::processSectionOptions(EncoreBootImage::Section *imageSection,
OutputSection *modelSection)
{
// Get options context for this output section.
const OptionContext *context = modelSection->getOptions();
if (!context)
{
return;
}
// Check for and handle "sectionFlags" option.
if (context->hasOption(kSectionFlagsOption))
{
const Value *value = context->getOption(kSectionFlagsOption);
const IntegerValue *intValue = dynamic_cast<const IntegerValue *>(value);
if (intValue)
{
// set explicit flags for this section
imageSection->setFlags(intValue->getValue());
}
else
{
Log::log(Logger::WARNING, "warning: sectionFlags option is an unexpected type\n");
}
}
// Check for and handle "alignment" option.
if (context->hasOption(kAlignmentOption))
{
const Value *value = context->getOption(kAlignmentOption);
const IntegerValue *intValue = dynamic_cast<const IntegerValue *>(value);
if (intValue)
{
// verify alignment value
if (intValue->getValue() < EncoreBootImage::BOOT_IMAGE_MINIMUM_SECTION_ALIGNMENT)
{
Log::log(Logger::WARNING, "warning: alignment option value must be 16 or greater\n");
}
imageSection->setAlignment(intValue->getValue());
}
else
{
Log::log(Logger::WARNING, "warning: alignment option is an unexpected type\n");
}
}
// Check for and handle "cleartext" option.
if (context->hasOption(kCleartextOption))
{
const Value *value = context->getOption(kCleartextOption);
const IntegerValue *intValue = dynamic_cast<const IntegerValue *>(value);
if (intValue)
{
bool leaveUnencrypted = intValue->getValue() != 0;
imageSection->setLeaveUnencrypted(leaveUnencrypted);
}
else
{
Log::log(Logger::WARNING, "warning: cleartext option is an unexpected type\n");
}
}
}
void EncoreBootImageGenerator::processOperationSection(OperationSequenceSection *section, EncoreBootImage *image)
{
EncoreBootImage::BootSection *newSection = new EncoreBootImage::BootSection(section->getIdentifier());
OperationSequence &sequence = section->getSequence();
OperationSequence::iterator_t it = sequence.begin();
for (; it != sequence.end(); ++it)
{
Operation *op = *it;
ProgramOperation *progOp = dynamic_cast<ProgramOperation *>(op);
if (progOp)
{
processProgramOperation(progOp, newSection);
continue;
}
KeywrapOperation *wrapOp = dynamic_cast<KeywrapOperation *>(op);
if (wrapOp)
{
processKeywrapOperation(wrapOp, newSection);
continue;
}
EncryptOperation *encOp = dynamic_cast<EncryptOperation *>(op);
if (encOp)
{
processEncryptOperation(encOp, newSection);
continue;
}
LoadOperation *loadOp = dynamic_cast<LoadOperation *>(op);
if (loadOp)
{
processLoadOperation(loadOp, newSection);
continue;
}
ExecuteOperation *execOp = dynamic_cast<ExecuteOperation *>(op);
if (execOp)
{
processExecuteOperation(execOp, newSection);
continue;
}
BootModeOperation *modeOp = dynamic_cast<BootModeOperation *>(op);
if (modeOp)
{
processBootModeOperation(modeOp, newSection);
continue;
}
FlashEraseOperation *eraseOp = dynamic_cast<FlashEraseOperation *>(op);
if (eraseOp)
{
processFlashEraseOperation(eraseOp, newSection);
continue;
}
ResetOperation *resetOp = dynamic_cast<ResetOperation *>(op);
if (resetOp)
{
processResetOperation(resetOp, newSection);
continue;
}
MemEnableOperation *enableOp = dynamic_cast<MemEnableOperation *>(op);
if (enableOp)
{
processMemEnableOperation(enableOp, newSection);
continue;
}
Log::log(Logger::WARNING, "warning: unexpected operation type\n");
}
// Deal with options that apply to sections.
processSectionOptions(newSection, section);
// add the boot section to the image
image->addSection(newSection);
}
void EncoreBootImageGenerator::processLoadOperation(LoadOperation *op, EncoreBootImage::BootSection *section)
{
DataSource *source = op->getSource();
DataTarget *target = op->getTarget();
// other sources get handled the same way
unsigned segmentCount = source->getSegmentCount();
unsigned index = 0;
for (; index < segmentCount; ++index)
{
DataSource::Segment *segment = source->getSegmentAt(index);
DataTarget::AddressRange range = target->getRangeForSegment(*source, *segment);
// Check for negative and zero length range.
int64_t rangeDiff = static_cast<int64_t>(range.m_end) - static_cast<int64_t>(range.m_begin);
if (rangeDiff <= 0)
{
continue;
}
unsigned rangeLength = static_cast<uint32_t>(rangeDiff);
// handle a pattern segment as a special case to create a fill command
DataSource::PatternSegment *patternSegment = dynamic_cast<DataSource::PatternSegment *>(segment);
if (patternSegment)
{
SizedIntegerValue &pattern = patternSegment->getPattern();
EncoreBootImage::FillCommand *command = new EncoreBootImage::FillCommand();
command->setAddress(range.m_begin);
command->setFillCount(rangeLength);
setFillPatternFromValue(*command, pattern);
section->addCommand(command);
continue;
}
unsigned allocLength = rangeLength;
if (m_encryptKeyBlob)
{
// Align allocation size to quadspi block
const unsigned align = Keyblob::k_qspiAlignlength;
unsigned remainder = rangeLength % align;
allocLength = remainder ? rangeLength + (align - remainder) : rangeLength;
}
// get the data from the segment
uint8_t *data = new uint8_t[allocLength];
memset(data, 0, allocLength);
segment->getData(0, rangeLength, data);
// Encrypt for OTFAD if keyblob provided.
if (m_encryptKeyBlob && m_encryptKeyBlob->encryptMatchingRange(range.m_begin, allocLength, data))
{
rangeLength = allocLength;
}
// create the boot command
EncoreBootImage::LoadCommand *command = new EncoreBootImage::LoadCommand();
command->setData(data, rangeLength); // Makes a copy of the data buffer.
command->setLoadAddress(range.m_begin);
command->setDCD(op->isDCDLoad());
section->addCommand(command);
// Free the segment buffer.
delete[] data;
}
}
void EncoreBootImageGenerator::setFillPatternFromValue(EncoreBootImage::FillCommand &command,
SizedIntegerValue &pattern)
{
uint32_t u32PatternValue = pattern.getValue() & pattern.getWordSizeMask();
switch (pattern.getWordSize())
{
case kWordSize:
{
command.setPattern(u32PatternValue);
break;
}
case kHalfWordSize:
{
uint16_t u16PatternValue = static_cast<uint16_t>(u32PatternValue);
command.setPattern(u16PatternValue);
break;
}
case kByteSize:
{
uint8_t u8PatternValue = static_cast<uint8_t>(u32PatternValue);
command.setPattern(u8PatternValue);
}
}
}
void EncoreBootImageGenerator::processExecuteOperation(ExecuteOperation *op, EncoreBootImage::BootSection *section)
{
DataTarget *target = op->getTarget();
uint32_t arg = static_cast<uint32_t>(op->getArgument());
EncoreBootImage::JumpCommand *command;
switch (op->getExecuteType())
{
case ExecuteOperation::kJump:
command = new EncoreBootImage::JumpCommand();
break;
case ExecuteOperation::kCall:
command = new EncoreBootImage::CallCommand();
break;
}
command->setAddress(target->getBeginAddress());
command->setArgument(arg);
command->setIsHAB(op->isHAB());
if (op->isStackPonterSet())
{
command->setStackPointer(op->getStackPointer());
command->setIsStackPointerSet(true);
}
section->addCommand(command);
}
void EncoreBootImageGenerator::processBootModeOperation(BootModeOperation *op, EncoreBootImage::BootSection *section)
{
EncoreBootImage::ModeCommand *command = new EncoreBootImage::ModeCommand();
command->setBootMode(op->getBootMode());
section->addCommand(command);
}
void EncoreBootImageGenerator::processResetOperation(ResetOperation *op, EncoreBootImage::BootSection *section)
{
EncoreBootImage::ResetCommand *command = new EncoreBootImage::ResetCommand();
section->addCommand(command);
}
void EncoreBootImageGenerator::processFlashEraseOperation(FlashEraseOperation *op,
EncoreBootImage::BootSection *section)
{
EncoreBootImage::EraseCommand *command = new EncoreBootImage::EraseCommand();
// Set command options based on whether this is an erase all operation, erase all unsecure operation, erase all with
// mem controller operation, or a regular erase.
FlashEraseAllOperation *eraseAllOp = dynamic_cast<FlashEraseAllOperation *>(op);
FlashEraseAllUnsecureOperation *eraseAllUnsecOp = dynamic_cast<FlashEraseAllUnsecureOperation *>(op);
FlashEraseAllExternalOperation *eraseAllExtOp = dynamic_cast<FlashEraseAllExternalOperation *>(op);
if (eraseAllOp)
{
command->setEraseAll(true);
}
else if (eraseAllUnsecOp)
{
command->setEraseAllUnsecure(true);
}
else if (eraseAllExtOp)
{
command->setEraseAll(true);
command->setMemoryId(eraseAllExtOp->getMemoryId());
}
else
{
uint32_t start;
uint32_t count;
op->getRange(&start, &count);
command->setAddressRange(start, count);
}
section->addCommand(command);
}
void EncoreBootImageGenerator::processMemEnableOperation(MemEnableOperation *op, EncoreBootImage::BootSection *section)
{
EncoreBootImage::MemEnableCommand *command = new EncoreBootImage::MemEnableCommand();
uint32_t start;
uint32_t count;
op->getRange(&start, &count);
command->setAddressRange(start, count);
command->setMemControllerId(op->getMemControllerId());
section->addCommand(command);
}
void EncoreBootImageGenerator::processProgramOperation(ProgramOperation *op, EncoreBootImage::BootSection *section)
{
DataSource *source = op->getSource();
DataTarget *target = op->getTarget();
unsigned segmentCount = source->getSegmentCount();
if (!segmentCount)
{
return;
}
if (segmentCount != 1)
{
Log::log(Logger::WARNING, "warning: program operation using first segment only\n");
}
DataSource::Segment *segment = source->getSegmentAt(0);
DataTarget::AddressRange range = target->getRangeForSegment(*source, *segment);
unsigned rangeLength = range.m_end - range.m_begin;
if ((rangeLength != 4) && (rangeLength != 8))
{
Log::log(Logger::WARNING, "warning: program operation requires 4 or 8 byte segment\n");
return;
}
// Handle a pattern segment as a special case.
DataSource::PatternSegment *patternSegment = dynamic_cast<DataSource::PatternSegment *>(segment);
if (patternSegment)
{
// Only 4 byte pattern is supported.
if (rangeLength != 4)
{
Log::log(Logger::WARNING, "warning: program operation requires a 4 byte pattern segment\n");
return;
}
SizedIntegerValue &pattern = patternSegment->getPattern();
// Create a fill command to convert the pattern to data.
EncoreBootImage::FillCommand *command = new EncoreBootImage::FillCommand();
setFillPatternFromValue(*command, pattern);
EncoreBootImage::ProgramCommand *progCmd = new EncoreBootImage::ProgramCommand();
progCmd->setIsEightByte(false);
progCmd->setMemSpace(op->getMemorySpace());
progCmd->setIndex(range.m_begin);
progCmd->setDataWord1(command->getPattern());
section->addCommand(progCmd);
delete command;
}
else
{
EncoreBootImage::ProgramCommand *command = new EncoreBootImage::ProgramCommand();
command->setIsEightByte((rangeLength == 8));
command->setMemSpace(op->getMemorySpace());
command->setIndex(range.m_begin);
// get first word from the segment
uint8_t *data = new uint8_t[8];
segment->getData(0, 4, data);
command->setDataWord1(*(uint32_t *)data);
if (command->isEightByte())
{
// get second word from the segment
segment->getData(0, 8, data);
command->setDataWord2(*(uint32_t *)(data + 4));
}
section->addCommand(command);
// Free the segment buffer.
delete[] data;
}
}
void EncoreBootImageGenerator::processKeywrapOperation(KeywrapOperation *op, EncoreBootImage::BootSection *section)
{
DataSource *source = op->getSource();
DataTarget *target = op->getTarget();
unsigned segmentCount = source->getSegmentCount();
if (!segmentCount)
{
return;
}
if (segmentCount != 1)
{
Log::log(Logger::WARNING, "warning: keywrap operation using first segment only\n");
}
DataSource::Segment *segment = source->getSegmentAt(0);
DataTarget::AddressRange range = target->getRangeForSegment(*source, *segment);
unsigned rangeLength = range.m_end - range.m_begin;
const uint32_t kekSize = Keyblob::getKekSizeBytes();
if (rangeLength != kekSize)
{
Log::log(Logger::WARNING, "warning: keywrap operation requires %d byte key segment\n", kekSize);
return;
}
// Handle a pattern segment as a special case.
DataSource::PatternSegment *patternSegment = dynamic_cast<DataSource::PatternSegment *>(segment);
if (patternSegment)
{
Log::log(Logger::WARNING, "warning: keywrap operation does not support pattern segment\n");
return;
}
else
{
// Get key data (kek) from source.
uint8_t *kek = new uint8_t[kekSize];
segment->getData(0, kekSize, kek);
Log::log(Logger::INFO2, "creating wrapped keyblob\n");
Keyblob *keyblob = op->getKeyblob();
assert(keyblob);
uint32_t byteCount = 0;
uint8_t *data = keyblob->createWrappedKeyblobData(kek, &byteCount);
delete[] kek;
// Create new data source with wrapped key blob.
source = new UnmappedDataSource(data, byteCount);
segment = source->getSegmentAt(0);
// Create an address range, possibly modified by target.
range = target->getRangeForSegment(*source, *segment);
unsigned length = range.m_end - range.m_begin;
delete source;
// Create load command with wrapped keyblob as source.
EncoreBootImage::LoadCommand *command = new EncoreBootImage::LoadCommand();
command->setData(data, length); // Makes a copy of the data buffer.
command->setLoadAddress(range.m_begin);
command->setDCD(false);
delete[] data;
section->addCommand(command);
}
}
void EncoreBootImageGenerator::processEncryptOperation(EncryptOperation *op, EncoreBootImage::BootSection *section)
{
m_encryptKeyBlob = op->getKeyblob();
assert(m_encryptKeyBlob);
// Load operation will notice non-null keyblob pointer and encrypt accordingly.
processLoadOperation(op, section);
m_encryptKeyBlob = NULL;
}
void EncoreBootImageGenerator::processDataSection(BinaryDataSection *section, EncoreBootImage *image)
{
EncoreBootImage::DataSection *dataSection = new EncoreBootImage::DataSection(section->getIdentifier());
dataSection->setData(section->getData(), section->getLength());
// Handle alignment option.
processSectionOptions(dataSection, section);
image->addSection(dataSection);
}