text: /*
   * 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);
  }
  