/* * 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(section); if (opSection) { processOperationSection(opSection, image); continue; } BinaryDataSection *dataSection = dynamic_cast(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(m_options->getOption(kFlagsOption)); if (intValue) { image->setFlags(static_cast(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(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(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(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(op); if (progOp) { processProgramOperation(progOp, newSection); continue; } KeywrapOperation *wrapOp = dynamic_cast(op); if (wrapOp) { processKeywrapOperation(wrapOp, newSection); continue; } EncryptOperation *encOp = dynamic_cast(op); if (encOp) { processEncryptOperation(encOp, newSection); continue; } LoadOperation *loadOp = dynamic_cast(op); if (loadOp) { processLoadOperation(loadOp, newSection); continue; } ExecuteOperation *execOp = dynamic_cast(op); if (execOp) { processExecuteOperation(execOp, newSection); continue; } BootModeOperation *modeOp = dynamic_cast(op); if (modeOp) { processBootModeOperation(modeOp, newSection); continue; } FlashEraseOperation *eraseOp = dynamic_cast(op); if (eraseOp) { processFlashEraseOperation(eraseOp, newSection); continue; } ResetOperation *resetOp = dynamic_cast(op); if (resetOp) { processResetOperation(resetOp, newSection); continue; } MemEnableOperation *enableOp = dynamic_cast(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(range.m_end) - static_cast(range.m_begin); if (rangeDiff <= 0) { continue; } unsigned rangeLength = static_cast(rangeDiff); // handle a pattern segment as a special case to create a fill command DataSource::PatternSegment *patternSegment = dynamic_cast(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(u32PatternValue); command.setPattern(u16PatternValue); break; } case kByteSize: { uint8_t u8PatternValue = static_cast(u32PatternValue); command.setPattern(u8PatternValue); } } } void EncoreBootImageGenerator::processExecuteOperation(ExecuteOperation *op, EncoreBootImage::BootSection *section) { DataTarget *target = op->getTarget(); uint32_t arg = static_cast(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(op); FlashEraseAllUnsecureOperation *eraseAllUnsecOp = dynamic_cast(op); FlashEraseAllExternalOperation *eraseAllExtOp = dynamic_cast(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(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(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); }