/* * File: ConversionController.cpp * * Copyright (c) Freescale Semiconductor, Inc. All rights reserved. * See included license file for license details. */ #include "ConversionController.h" #include #include "EvalContext.h" #include "ElftosbErrors.h" #include "GlobMatcher.h" #include "ExcludesListMatcher.h" #include "BootImageGenerator.h" #include "EncoreBootImageGenerator.h" #include "Logging.h" #include "OptionDictionary.h" #include "format_string.h" #include "SearchPath.h" #include "DataSourceImager.h" #include "IVTDataSource.h" #include "Keyblob.h" #include //! Set to 1 to cause the ConversionController to print information about //! the values that it processes (options, constants, etc.). #define PRINT_VALUES 1 using namespace elftosb; // Define the parser function prototype; extern int yyparse(ElftosbLexer *lexer, CommandFileASTNode **resultAST); bool elftosb::g_enableHABSupport = false; ConversionController::ConversionController() : OptionDictionary() , m_commandFilePath() , m_ast() , m_defaultSource(0) , m_keywrapKeyblob(0) , m_encryptKeyblob(0) { m_context.setSourceFileManager(this); } ConversionController::~ConversionController() { // clean up sources source_map_t::iterator it = m_sources.begin(); for (; it != m_sources.end(); ++it) { if (it->second) { delete it->second; } } } void ConversionController::setCommandFilePath(const std::string &path) { m_commandFilePath = new std::string(path); } //! The paths provided to this method are added to an array and accessed with the //! "extern(N)" notation in the command file. So the path provided in the third //! call to addExternalFilePath() will be found with N=2 in the source definition. void ConversionController::addExternalFilePath(const std::string &path) { m_externPaths.push_back(path); } bool ConversionController::hasSourceFile(const std::string &name) { return m_sources.find(name) != m_sources.end(); } SourceFile *ConversionController::getSourceFile(const std::string &name) { if (!hasSourceFile(name)) { return NULL; } return m_sources[name]; } SourceFile *ConversionController::getDefaultSourceFile() { return m_defaultSource; } //! These steps are executed while running this method: //! - The command file is parsed into an abstract syntax tree. //! - The list of options is extracted. //! - Constant expressions are evaluated. //! - The list of source files is extracted and source file objects created. //! - Section definitions are extracted. //! //! This method does not produce any output. It processes the input files and //! builds a representation of the output in memory. Use the generateOutput() method //! to produce a BootImage object after this method returns. //! //! \note This method is \e not reentrant. And in fact, the whole class is not designed //! to be reentrant. //! //! \exception std::runtime_error Any number of problems will cause this exception to //! be thrown. //! //! \see parseCommandFile() //! \see processOptions() //! \see processConstants() //! \see processSources() //! \see processSections() void ConversionController::run() { #if PRINT_VALUES Log::SetOutputLevel debugLevel(Logger::DEBUG2); #endif parseCommandFile(); assert(m_ast); ListASTNode *blocks = m_ast->getBlocks(); if (!blocks) { throw std::runtime_error("command file has no blocks"); } ListASTNode::iterator it = blocks->begin(); for (; it != blocks->end(); ++it) { ASTNode *node = *it; // Handle an options block. OptionsBlockASTNode *options = dynamic_cast(node); if (options) { processOptions(options->getOptions()); continue; } // Handle a constants block. ConstantsBlockASTNode *constants = dynamic_cast(node); if (constants) { processConstants(constants->getConstants()); continue; } // Handle a sources block. SourcesBlockASTNode *sources = dynamic_cast(node); if (sources) { processSources(sources->getSources()); } // Handle a keyblob block. KeyblobBlockASTNode *keyblob = dynamic_cast(node); if (keyblob) { processKeyblob(keyblob->getKeyblobNumberExpr(), keyblob->getEntries()); } } processSections(m_ast->getSections()); } //! Opens the command file and runs it through the lexer and parser. The resulting //! abstract syntax tree is held in the m_ast member variable. After parsing, the //! command file is closed. //! //! \exception std::runtime_error Several problems will cause this exception to be //! raised, including an unspecified command file path or an error opening the //! file. void ConversionController::parseCommandFile() { if (!m_commandFilePath) { throw std::runtime_error("no command file path was provided"); } // Search for command file std::string actualPath; bool found = PathSearcher::getGlobalSearcher().search(*m_commandFilePath, PathSearcher::kFindFile, true, actualPath); if (!found) { throw runtime_error(format_string("unable to find command file %s\n", m_commandFilePath->c_str())); } // open command file std::ifstream commandFile(actualPath.c_str(), ios_base::in | ios_base::binary); if (!commandFile.is_open()) { throw std::runtime_error("could not open command file"); } try { // create lexer instance ElftosbLexer lexer(commandFile); // testLexer(lexer); CommandFileASTNode *ast = NULL; int result = yyparse(&lexer, &ast); m_ast = ast; // check results if (result || !m_ast) { throw std::runtime_error("failed to parse command file"); } // dump AST // m_ast->printTree(0); // close command file commandFile.close(); } catch (...) { // close command file commandFile.close(); // rethrow exception throw; } } //! Iterates over the option definition AST nodes. elftosb::Value objects are created for //! each option value and added to the option dictionary. //! //! \exception std::runtime_error Various errors will cause this exception to be thrown. These //! include AST nodes being an unexpected type or expression not evaluating to integers. void ConversionController::processOptions(ListASTNode *options) { if (!options) { return; } ListASTNode::iterator it = options->begin(); for (; it != options->end(); ++it) { std::string ident; Value *value = convertAssignmentNodeToValue(*it, ident); // check if this option has already been set if (hasOption(ident)) { throw semantic_error(format_string("line %d: option already set", (*it)->getFirstLine())); } // now save the option value in our map if (value) { setOption(ident, value); } } } //! Scans the constant definition AST nodes, evaluates expression nodes by calling their //! elftosb::ExprASTNode::reduce() method, and updates the evaluation context member so //! those constant values can be used in other expressions. //! //! \exception std::runtime_error Various errors will cause this exception to be thrown. These //! include AST nodes being an unexpected type or expression not evaluating to integers. void ConversionController::processConstants(ListASTNode *constants) { if (!constants) { return; } ListASTNode::iterator it = constants->begin(); for (; it != constants->end(); ++it) { std::string ident; Value *value = convertAssignmentNodeToValue(*it, ident); SizedIntegerValue *intValue = dynamic_cast(value); if (!intValue) { throw semantic_error(format_string("line %d: constant value is an invalid type", (*it)->getFirstLine())); } //#if PRINT_VALUES // Log::log("constant "); // printIntConstExpr(ident, intValue); //#endif // record this constant's value in the evaluation context m_context.setVariable(ident, intValue->getValue(), intValue->getWordSize()); } } //! \exception std::runtime_error Various errors will cause this exception to be thrown. These //! include AST nodes being an unexpected type or expression not evaluating to integers. //! //! \todo Handle freeing of dict if an exception occurs. void ConversionController::processSources(ListASTNode *sources) { if (!sources) { return; } ListASTNode::iterator it = sources->begin(); for (; it != sources->end(); ++it) { SourceDefASTNode *node = dynamic_cast(*it); if (!node) { throw semantic_error( format_string("line %d: source definition node is an unexpected type", node->getFirstLine())); } // get source name and check if it has already been defined std::string *name = node->getName(); if (m_sources.find(*name) != m_sources.end()) { // can't define a source multiple times throw semantic_error(format_string("line %d: source already defined", node->getFirstLine())); } // convert attributes into an option dict OptionDictionary *dict = new OptionDictionary(this); ListASTNode *attrsNode = node->getAttributes(); if (attrsNode) { ListASTNode::iterator attrIt = attrsNode->begin(); for (; attrIt != attrsNode->end(); ++attrIt) { std::string ident; Value *value = convertAssignmentNodeToValue(*attrIt, ident); dict->setOption(ident, value); } } // figure out which type of source definition this is PathSourceDefASTNode *pathNode = dynamic_cast(node); ExternSourceDefASTNode *externNode = dynamic_cast(node); SourceFile *file = NULL; if (pathNode) { // explicit path std::string *path = pathNode->getPath(); #if PRINT_VALUES Log::log("source %s => path(%s)\n", name->c_str(), path->c_str()); #endif try { file = SourceFile::openFile(*path); } catch (...) { // file doesn't exist Log::log(Logger::INFO2, "failed to open source file: %s (ignoring for now)\n", path->c_str()); m_failedSources.push_back(*name); } } else if (externNode) { // externally provided path ExprASTNode *expr = externNode->getSourceNumberExpr()->reduce(m_context); IntConstExprASTNode *intConst = dynamic_cast(expr); if (!intConst) { throw semantic_error( format_string("line %d: expression didn't evaluate to an integer", expr->getFirstLine())); } uint32_t externalFileNumber = static_cast(intConst->getValue()); // make sure the extern number is valid if (externalFileNumber < m_externPaths.size()) { #if PRINT_VALUES Log::log("source %s => extern(%d=%s)\n", name->c_str(), externalFileNumber, m_externPaths[externalFileNumber].c_str()); #endif try { file = SourceFile::openFile(m_externPaths[externalFileNumber]); } catch (...) { Log::log(Logger::INFO2, "failed to open source file: %s (ignoring for now)\n", m_externPaths[externalFileNumber].c_str()); m_failedSources.push_back(*name); } } } else { throw semantic_error( format_string("line %d: unexpected source definition node type", node->getFirstLine())); } if (file) { // set options file->setOptions(dict); // stick the file object in the source map m_sources[*name] = file; } } } //! Iterates over the keyblob definition AST nodes. //! //! \exception std::runtime_error Various errors will cause this exception to be thrown. These //! include AST nodes being an unexpected type or expression not evaluating to integers. void ConversionController::processKeyblob(ExprASTNode *idExpr, ListASTNode *entries) { if (!entries) { return; } ExprASTNode *expr = idExpr->reduce(m_context); IntConstExprASTNode *intConst = dynamic_cast(expr); if (!intConst) { throw semantic_error( format_string("line %d: ID expression didn't evaluate to an integer", expr->getFirstLine())); } Keyblob *keyblob = new Keyblob(intConst->getValue()); try { // Process the entries. ListASTNode::iterator it = entries->begin(); for (; it != entries->end(); ++it) { KeyblobEntryASTNode *node = dynamic_cast(*it); if (!node) { throw semantic_error(format_string("line %d: keyblob entry is unexpected type", node->getFirstLine())); } ListASTNode *fields = node->getFieldAssignments(); // fixme who deletes this? OptionDictionary *optionsDict = new OptionDictionary(this); if (fields) { // Process the fields of this entry (each one is an assignement node). ListASTNode::iterator fieldIt = fields->begin(); for (; fieldIt != fields->end(); ++fieldIt) { std::string ident; Value *value = convertAssignmentNodeToValue(*fieldIt, ident); optionsDict->setOption(ident, value); } } // Add the options dictionary to the keyblob object. // An empty options dictionary indicates a blank keyblob entry. keyblob->addOptions(optionsDict); } } catch (...) { if (keyblob) { delete keyblob; } throw; } // Save the keyblob in the list of keyblobs maintained by the ConversionController. m_keyblobs.push_back(keyblob); } void ConversionController::processSections(ListASTNode *sections) { if (!sections) { Log::log(Logger::WARNING, "warning: no sections were defined in command file"); return; } ListASTNode::iterator it = sections->begin(); for (; it != sections->end(); ++it) { SectionContentsASTNode *node = dynamic_cast(*it); if (!node) { throw semantic_error(format_string("line %d: section definition is unexpected type", node->getFirstLine())); } // evaluate section number ExprASTNode *idExpr = node->getSectionNumberExpr()->reduce(m_context); IntConstExprASTNode *idConst = dynamic_cast(idExpr); if (!idConst) { throw semantic_error( format_string("line %d: section number did not evaluate to an integer", idExpr->getFirstLine())); } uint32_t sectionID = idConst->getValue(); // Create options context for this section. The options context has the // conversion controller as its parent context so it will inherit global options. // The context will be set in the section after the section is created below. OptionDictionary *optionsDict = new OptionDictionary(this); ListASTNode *attrsNode = node->getOptions(); if (attrsNode) { ListASTNode::iterator attrIt = attrsNode->begin(); for (; attrIt != attrsNode->end(); ++attrIt) { std::string ident; Value *value = convertAssignmentNodeToValue(*attrIt, ident); optionsDict->setOption(ident, value); } } // Now create the actual section object based on its type. OutputSection *outputSection = NULL; BootableSectionContentsASTNode *bootableSection; DataSectionContentsASTNode *dataSection; if ((bootableSection = dynamic_cast(node))) { // process statements into a sequence of operations ListASTNode *statements = bootableSection->getStatements(); OperationSequence *sequence = convertStatementList(statements); #if 0 Log::log("section ID = %d\n", sectionID); statements->printTree(0); Log::log("sequence has %d operations\n", sequence->getCount()); OperationSequence::iterator_t it = sequence->begin(); for (; it != sequence->end(); ++it) { Operation * op = *it; Log::log("op = %p\n", op); } #endif // create the output section and add it to the list OperationSequenceSection *opSection = new OperationSequenceSection(sectionID); opSection->setOptions(optionsDict); opSection->getSequence() += sequence; outputSection = opSection; } else if ((dataSection = dynamic_cast(node))) { outputSection = convertDataSection(dataSection, sectionID, optionsDict); } else { throw semantic_error(format_string("line %d: unexpected section contents type", node->getFirstLine())); } if (outputSection) { m_outputSections.push_back(outputSection); } } } //! Creates an instance of BinaryDataSection from the AST node passed in the //! \a dataSection parameter. The section-specific options for this node will //! have already been converted into an OptionDictionary, the one passed in //! the \a optionsDict parameter. //! //! The \a dataSection node will have as its contents one of the AST node //! classes that represents a source of data. The member function //! createSourceFromNode() is used to convert this AST node into an //! instance of a DataSource subclass. Then the method imageDataSource() //! converts the segments of the DataSource into a raw binary buffer that //! becomes the contents of the BinaryDataSection this is returned. //! //! \param dataSection The AST node for the data section. //! \param sectionID Unique tag value the user has assigned to this section. //! \param optionsDict Options that apply only to this section. This dictionary //! will be assigned as the options dictionary for the resulting section //! object. Its parent is the conversion controller itself. //! \return An instance of BinaryDataSection. Its contents are a contiguous //! binary representation of the contents of \a dataSection. OutputSection *ConversionController::convertDataSection(DataSectionContentsASTNode *dataSection, uint32_t sectionID, OptionDictionary *optionsDict) { // Create a data source from the section contents AST node. ASTNode *contents = dataSection->getContents(); DataSource *dataSource = createSourceFromNode(contents); // Convert the data source to a raw buffer. DataSourceImager imager; imager.addDataSource(dataSource); // Then make a data section from the buffer. BinaryDataSection *resultSection = new BinaryDataSection(sectionID); resultSection->setOptions(optionsDict); if (imager.getLength()) { resultSection->setData(imager.getData(), imager.getLength()); } return resultSection; } //! @param node The AST node instance for the assignment expression. //! @param[out] ident Upon exit this string will be set the the left hand side of the //! assignment expression, the identifier name. //! //! @return An object that is a subclass of Value is returned. The specific subclass will //! depend on the type of the right hand side of the assignment expression whose AST //! node was provided in the @a node argument. //! //! @exception semantic_error Thrown for any error where an AST node is an unexpected type. //! This may be the @a node argument itself, if it is not an AssignmentASTNode. Or it //! may be an unexpected type for either the right or left hand side of the assignment. //! The message for the exception will contain a description of the error. Value *ConversionController::convertAssignmentNodeToValue(ASTNode *node, std::string &ident) { Value *resultValue = NULL; // each item of the options list should be an assignment node AssignmentASTNode *assignmentNode = dynamic_cast(node); if (!node) { throw semantic_error(format_string("line %d: node is wrong type", assignmentNode->getFirstLine())); } // save the left hand side (the identifier) into ident ident = *assignmentNode->getIdent(); // get the right hand side and convert it to a Value instance ASTNode *valueNode = assignmentNode->getValue(); StringConstASTNode *str; ExprASTNode *expr; if ((str = dynamic_cast(valueNode))) { // the option value is a string constant resultValue = new StringValue(str->getString()); //#if PRINT_VALUES // Log::log("option %s => \'%s\'\n", ident->c_str(), str->getString()->c_str()); //#endif } else if ((expr = dynamic_cast(valueNode))) { ExprASTNode *reducedExpr = expr->reduce(m_context); IntConstExprASTNode *intConst = dynamic_cast(reducedExpr); if (!intConst) { throw semantic_error( format_string("line %d: expression didn't evaluate to an integer", expr->getFirstLine())); } //#if PRINT_VALUES // Log::log("option "); // printIntConstExpr(*ident, intConst); //#endif resultValue = new SizedIntegerValue(intConst->getValue(), intConst->getSize()); } else { throw semantic_error( format_string("line %d: right hand side node is an unexpected type", valueNode->getFirstLine())); } return resultValue; } //! Builds up a sequence of Operation objects that are equivalent to the //! statements in the \a statements list. The statement list is simply iterated //! over and the results of convertOneStatement() are used to build up //! the final result sequence. //! //! \see convertOneStatement() OperationSequence *ConversionController::convertStatementList(ListASTNode *statements) { OperationSequence *resultSequence = new OperationSequence(); ListASTNode::iterator it = statements->begin(); for (; it != statements->end(); ++it) { StatementASTNode *statement = dynamic_cast(*it); if (!statement) { throw semantic_error(format_string("line %d: statement node is unexpected type", (*it)->getFirstLine())); } // convert this statement and append it to the result OperationSequence *sequence = convertOneStatement(statement); if (sequence) { *resultSequence += sequence; } } return resultSequence; } //! Uses C++ RTTI to identify the particular subclass of StatementASTNode that //! the \a statement argument matches. Then the appropriate conversion method //! is called. //! //! \see convertLoadStatement() //! \see convertCallStatement() //! \see convertFromStatement() OperationSequence *ConversionController::convertOneStatement(StatementASTNode *statement) { // see if it's a load statement LoadStatementASTNode *load = dynamic_cast(statement); if (load) { return convertLoadStatement(load); } // see if it's a call statement CallStatementASTNode *call = dynamic_cast(statement); if (call) { return convertCallStatement(call); } // see if it's a from statement FromStatementASTNode *from = dynamic_cast(statement); if (from) { return convertFromStatement(from); } // see if it's a mode statement ModeStatementASTNode *mode = dynamic_cast(statement); if (mode) { return convertModeStatement(mode); } // see if it's an if statement IfStatementASTNode *ifStmt = dynamic_cast(statement); if (ifStmt) { return convertIfStatement(ifStmt); } // see if it's a message statement MessageStatementASTNode *messageStmt = dynamic_cast(statement); if (messageStmt) { // Message statements don't produce operation sequences. handleMessageStatement(messageStmt); return NULL; } // see if it's an erase statement EraseStatementASTNode *eraseStmt = dynamic_cast(statement); if (eraseStmt) { return convertEraseStatement(eraseStmt); } // see if it's a reset statement ResetStatementASTNode *resetStmt = dynamic_cast(statement); if (resetStmt) { return convertResetStatement(resetStmt); } // see if it's a reset statement MemEnableStatementASTNode *enableStmt = dynamic_cast(statement); if (enableStmt) { return convertMemEnableStatement(enableStmt); } // see if it's a keywrap statement KeywrapStatementASTNode *wrapStmt = dynamic_cast(statement); if (wrapStmt) { return convertKeywrapStatement(wrapStmt); } // see if it's an encrypt statement EncryptStatementASTNode *encStmt = dynamic_cast(statement); if (encStmt) { return convertEncryptStatement(encStmt); } // didn't match any of the expected statement types throw semantic_error(format_string("line %d: unexpected statement type", statement->getFirstLine())); return NULL; } //! Possible load data node types: //! - StringConstASTNode //! - ExprASTNode //! - SourceASTNode //! - SectionMatchListASTNode //! //! Possible load target node types: //! - SymbolASTNode //! - NaturalLocationASTNode //! - AddressRangeASTNode OperationSequence *ConversionController::convertLoadStatement(LoadStatementASTNode *statement) { LoadOperation *op = NULL; ProgramOperation *progOp = NULL; KeywrapOperation *keywrapOp = NULL; EncryptOperation *encryptOp = NULL; bool isDCD = false; try { // Evaluate load option. ASTNode *loadOption = statement->getLoadOption(); if (loadOption) { StringConstASTNode *strNode = dynamic_cast(loadOption); if (!strNode) { throw semantic_error( format_string("line %d: load option did not evaluate to a string", statement->getFirstLine())); } std::string *optStr = strNode->getString(); if (optStr->compare("dcd") == 0) { if (!elftosb::g_enableHABSupport) { throw semantic_error(format_string("line %d: HAB features not supported with the selected familiy", statement->getFirstLine())); } isDCD = true; } else if (optStr->compare("ifr") == 0) { progOp = new ProgramOperation(EncoreBootImage::ROM_MEM_SPACE_IFR); op = progOp; } else { throw semantic_error( format_string("line %d: invalid memory option %s", statement->getFirstLine(), optStr)); } } // Create load operation and set source and target. if (!op) { if (m_keywrapKeyblob) { // This is a keyblob load. keywrapOp = new KeywrapOperation(m_keywrapKeyblob); op = keywrapOp; } else if (m_encryptKeyblob) { // This is a encrypt load. encryptOp = new EncryptOperation(m_encryptKeyblob); op = encryptOp; } else { op = new LoadOperation(); } } op->setSource(createSourceFromNode(statement->getData())); op->setTarget(createTargetFromNode(statement->getTarget())); op->setDCDLoad(isDCD); // Append the correct operation subclass to the operation sequence. OperationSequence *opSeq = new OperationSequence(); if (progOp) { opSeq->append(progOp); } else if (keywrapOp) { opSeq->append(keywrapOp); } else if (encryptOp) { opSeq->append(encryptOp); } else { opSeq->append(op); } return opSeq; } catch (...) { if (op) { delete op; } throw; } } //! Possible call target node types: //! - SymbolASTNode //! - ExprASTNode //! //! Possible call argument node types: //! - ExprASTNode //! - NULL OperationSequence *ConversionController::convertCallStatement(CallStatementASTNode *statement) { ExecuteOperation *op = NULL; try { // create operation from AST nodes op = new ExecuteOperation(); bool isHAB = statement->isHAB(); op->setTarget(createTargetFromNode(statement->getTarget())); // set argument value, which defaults to 0 if no expression was provided uint32_t arg = 0; ASTNode *argNode = statement->getArgument(); if (argNode) { ExprASTNode *argExprNode = dynamic_cast(argNode); if (!argExprNode) { throw semantic_error( format_string("line %d: call argument is unexpected type", argNode->getFirstLine())); } argExprNode = argExprNode->reduce(m_context); IntConstExprASTNode *intNode = dynamic_cast(argExprNode); if (!intNode) { throw semantic_error(format_string("line %d: call argument did not evaluate to an integer", argExprNode->getFirstLine())); } arg = intNode->getValue(); } op->setArgument(arg); // set call type switch (statement->getCallType()) { case CallStatementASTNode::kCallType: op->setExecuteType(ExecuteOperation::kCall); break; case CallStatementASTNode::kJumpType: op->setExecuteType(ExecuteOperation::kJump); break; } // Set the HAB mode flag. op->setIsHAB(isHAB); // Set the stack pointer if present. ASTNode *spNode = statement->getStackPointer(); if (spNode) { ExprASTNode *spExprNode = dynamic_cast(spNode); if (!spExprNode) { throw semantic_error( format_string("line %d: stack pointer argument is unexpected type", argNode->getFirstLine())); } spExprNode = spExprNode->reduce(m_context); IntConstExprASTNode *intNode = dynamic_cast(spExprNode); if (!intNode) { throw semantic_error(format_string("line %d: stack pointer argument did not evaluate to an integer", spExprNode->getFirstLine())); } op->setStackPointer(intNode->getValue()); op->setIsStackPointerSet(true); } return new OperationSequence(op); } catch (...) { // delete op and rethrow exception if (op) { delete op; } throw; } } //! First this method sets the default source to the source identified in //! the from statement. Then the statements within the from block are //! processed recursively by calling convertStatementList(). The resulting //! operation sequence is returned. OperationSequence *ConversionController::convertFromStatement(FromStatementASTNode *statement) { if (m_defaultSource) { throw semantic_error(format_string("line %d: from statements cannot be nested", statement->getFirstLine())); } // look up source file instance std::string *fromSourceName = statement->getSourceName(); assert(fromSourceName); // make sure it's a valid source name source_map_t::iterator sourceIt = m_sources.find(*fromSourceName); if (sourceIt == m_sources.end()) { throw semantic_error(format_string("line %d: bad source name", statement->getFirstLine())); } // set default source m_defaultSource = sourceIt->second; assert(m_defaultSource); // get statements inside the from block ListASTNode *fromStatements = statement->getStatements(); assert(fromStatements); // produce resulting operation sequence OperationSequence *result = convertStatementList(fromStatements); // restore default source to NULL m_defaultSource = NULL; return result; } //! First this method sets the keyblob to the keyblob number identified in //! the keywrap statement. Then the statements within the keywrap block are //! processed recursively by calling convertStatementList(). The resulting //! operation sequence is returned. OperationSequence *ConversionController::convertKeywrapStatement(KeywrapStatementASTNode *statement) { if (m_keywrapKeyblob) { throw semantic_error(format_string("line %d: keywrap statements cannot be nested", statement->getFirstLine())); } // Convert keyblob number expression to an integer. ExprASTNode *kbExprNode = statement->getKeyblobNumberExpr()->reduce(m_context); IntConstExprASTNode *intNode = dynamic_cast(kbExprNode); if (!intNode) { throw semantic_error( format_string("line %d: keyblob number did not evaluate to an integer", statement->getFirstLine())); } // Look up keyblob. Keyblob *blob = m_keyblobs.at(intNode->getValue()); if (!blob) { throw semantic_error(format_string("line %d: keyblob specification not found", statement->getFirstLine())); } m_keywrapKeyblob = blob; // get statements inside the from block ListASTNode *keywrapStatements = statement->getStatements(); assert(keywrapStatements); // produce resulting operation sequence OperationSequence *result = convertStatementList(keywrapStatements); // restore active keyblob to NULL m_keywrapKeyblob = NULL; return result; } //! First this method sets the keyblob to the keyblob number identified in //! the encrypt statement. Then the statements within the encrypt block are //! processed recursively by calling convertStatementList(). The resulting //! operation sequence is returned. OperationSequence *ConversionController::convertEncryptStatement(EncryptStatementASTNode *statement) { if (m_encryptKeyblob) { throw semantic_error(format_string("line %d: encrypt statements cannot be nested", statement->getFirstLine())); } // Convert keyblob number expression to an integer. ExprASTNode *kbExprNode = statement->getKeyblobNumberExpr()->reduce(m_context); IntConstExprASTNode *intNode = dynamic_cast(kbExprNode); if (!intNode) { throw semantic_error( format_string("line %d: keyblob number did not evaluate to an integer", statement->getFirstLine())); } // Look up keyblob. Keyblob *blob = m_keyblobs.at(intNode->getValue()); if (!blob) { throw semantic_error(format_string("line %d: keyblob specification not found", statement->getFirstLine())); } m_encryptKeyblob = blob; // get statements inside the from block ListASTNode *encryptStatements = statement->getStatements(); assert(encryptStatements); // produce resulting operation sequence OperationSequence *result = convertStatementList(encryptStatements); // restore active keyblob to NULL m_encryptKeyblob = NULL; return result; } //! Evaluates the expression to get the new boot mode value. Then creates a //! BootModeOperation object and returns an OperationSequence containing it. //! //! \exception elftosb::semantic_error Thrown if a semantic problem is found with //! the boot mode expression. OperationSequence *ConversionController::convertModeStatement(ModeStatementASTNode *statement) { BootModeOperation *op = NULL; try { op = new BootModeOperation(); // evaluate the boot mode expression ExprASTNode *modeExprNode = statement->getModeExpr(); if (!modeExprNode) { throw semantic_error( format_string("line %d: mode statement has invalid boot mode expression", statement->getFirstLine())); } modeExprNode = modeExprNode->reduce(m_context); IntConstExprASTNode *intNode = dynamic_cast(modeExprNode); if (!intNode) { throw semantic_error( format_string("line %d: boot mode did not evaluate to an integer", statement->getFirstLine())); } op->setBootMode(intNode->getValue()); return new OperationSequence(op); } catch (...) { if (op) { delete op; } // rethrow exception throw; } } //! Creates a Reset Operation object and returns an OperationSequence containing it. OperationSequence *ConversionController::convertResetStatement(ResetStatementASTNode *statement) { return new OperationSequence(new ResetOperation); } //! Else branches, including else-if, are handled recursively, so there is a limit //! on the number of them based on the stack size. //! //! \return Returns the operation sequence for the branch of the if statement that //! evaluated to true. If the statement did not have an else branch and the //! condition expression evaluated to false, then NULL will be returned. //! //! \todo Handle else branches without recursion. OperationSequence *ConversionController::convertIfStatement(IfStatementASTNode *statement) { // Get the if's conditional expression. ExprASTNode *conditionalExpr = statement->getConditionExpr(); if (!conditionalExpr) { throw semantic_error( format_string("line %d: missing or invalid conditional expression", statement->getFirstLine())); } // Reduce the conditional to a single integer. conditionalExpr = conditionalExpr->reduce(m_context); IntConstExprASTNode *intNode = dynamic_cast(conditionalExpr); if (!intNode) { throw semantic_error(format_string( "line %d: if statement conditional expression did not evaluate to an integer", statement->getFirstLine())); } // Decide which statements to further process by the conditional's boolean value. if (intNode->getValue() && statement->getIfStatements()) { return convertStatementList(statement->getIfStatements()); } else if (statement->getElseStatements()) { return convertStatementList(statement->getElseStatements()); } else { // No else branch and the conditional was false, so there are no operations to return. return NULL; } } OperationSequence *ConversionController::convertEraseStatement(EraseStatementASTNode *statement) { // Handle erase all first. if (statement->getEraseAll()) { // Check for erase all memory ID. ASTNode *memOption = statement->getMemOption(); if (!memOption) { // Flash erase all internal. return new OperationSequence(new FlashEraseAllOperation); } else { StringConstASTNode *strNode = dynamic_cast(memOption); if (!strNode) { throw semantic_error( format_string("line %d: memory option did not evaluate to a string", statement->getFirstLine())); } std::string *optStr = strNode->getString(); if (optStr->compare("internal") == 0) { // Flash erase all internal. return new OperationSequence(new FlashEraseAllOperation); } else if (optStr->compare("qspi") == 0) { FlashEraseAllExternalOperation *op = new FlashEraseAllExternalOperation; op->setMemoryId(EncoreBootImage::ROM_MEM_ID_QSPI); return new OperationSequence(op); } else { throw semantic_error( format_string("line %d: invalid memory option %s", statement->getFirstLine(), optStr)); } } } // Handle erase all unsecure. if (statement->getEraseAllUnsecure()) { return new OperationSequence(new FlashEraseAllUnsecureOperation); } AddressRangeASTNode *addressNode = dynamic_cast(statement->getRangeExpr()); if (!addressNode) { throw semantic_error("unexpected erase range node type"); } // evaluate begin address ExprASTNode *beginExpr = dynamic_cast(addressNode->getBegin()); if (!beginExpr) { throw semantic_error("address range must always have a beginning expression"); } IntConstExprASTNode *beginIntExpr = dynamic_cast(beginExpr->reduce(m_context)); if (!beginIntExpr) { throw semantic_error("address range begin did not evaluate to an integer"); } uint32_t beginAddress = static_cast(beginIntExpr->getValue()); // evaluate end address ExprASTNode *endExpr = dynamic_cast(addressNode->getEnd()); uint32_t endAddress = 0; bool hasEndAddress = false; if (endExpr) { IntConstExprASTNode *endIntExpr = dynamic_cast(endExpr->reduce(m_context)); if (!endIntExpr) { throw semantic_error("address range end did not evaluate to an integer"); } endAddress = static_cast(endIntExpr->getValue()); hasEndAddress = true; } if (endAddress < beginAddress) { Log::log(Logger::WARNING, "warning: line %d: end address of address range is before start address\n", addressNode->getFirstLine()); } // create target FlashEraseOperation *op = new FlashEraseOperation; if (hasEndAddress) { op->setRange(beginAddress, endAddress - beginAddress); } else { op->setRange(beginAddress, sizeof(uint32_t)); } return new OperationSequence(op); } OperationSequence *ConversionController::convertMemEnableStatement(MemEnableStatementASTNode *statement) { // Get memory controller ID. int memId; ASTNode *memOption = statement->getMemOption(); if (!memOption) { throw semantic_error(format_string("line %d: memory ID is required", statement->getFirstLine())); } else { StringConstASTNode *strNode = dynamic_cast(memOption); if (!strNode) { throw semantic_error( format_string("line %d: memory option ID did not evaluate to an string", statement->getFirstLine())); } std::string *optStr = strNode->getString(); if (optStr->compare("qspi") == 0) { memId = 1; } else { throw semantic_error(format_string("line %d: invalid memory option %s", statement->getFirstLine(), optStr)); } } AddressRangeASTNode *addressNode = dynamic_cast(statement->getRangeExpr()); if (!addressNode) { throw semantic_error("unexpected erase range node type"); } // evaluate begin address ExprASTNode *beginExpr = dynamic_cast(addressNode->getBegin()); if (!beginExpr) { throw semantic_error("address range must always have a beginning expression"); } IntConstExprASTNode *beginIntExpr = dynamic_cast(beginExpr->reduce(m_context)); if (!beginIntExpr) { throw semantic_error("address range begin did not evaluate to an integer"); } uint32_t beginAddress = static_cast(beginIntExpr->getValue()); // evaluate end address ExprASTNode *endExpr = dynamic_cast(addressNode->getEnd()); uint32_t endAddress = 0; bool hasEndAddress = false; if (endExpr) { IntConstExprASTNode *endIntExpr = dynamic_cast(endExpr->reduce(m_context)); if (!endIntExpr) { throw semantic_error("address range end did not evaluate to an integer"); } endAddress = static_cast(endIntExpr->getValue()); hasEndAddress = true; if (endAddress < beginAddress) { Log::log(Logger::WARNING, "warning: line %d: end address of address range is before start address\n", addressNode->getFirstLine()); } } // create target MemEnableOperation *op = new MemEnableOperation; if (hasEndAddress) { op->setRange(beginAddress, endAddress - beginAddress); } else { op->setRange(beginAddress, sizeof(uint32_t)); } op->setMemControllerId(memId); return new OperationSequence(op); } //! Message statements are executed immediately, by this method. They are //! not converted into an abstract operation. All messages are passed through //! substituteVariables() before being output. //! //! \param statement The message statement AST node object. void ConversionController::handleMessageStatement(MessageStatementASTNode *statement) { string *message = statement->getMessage(); if (!message) { throw runtime_error("message statement had no message"); } smart_ptr finalMessage = substituteVariables(message); switch (statement->getType()) { case MessageStatementASTNode::kInfo: Log::log(Logger::INFO, "%s\n", finalMessage->c_str()); break; case MessageStatementASTNode::kWarning: Log::log(Logger::WARNING, "warning: %s\n", finalMessage->c_str()); break; case MessageStatementASTNode::kError: throw runtime_error(*finalMessage); break; } } //! Performs shell-like variable substitution on the string passed into it. //! Both sources and constants can be substituted. Sources will be replaced //! with their path and constants with their integer value. The syntax allows //! for some simple formatting for constants. //! //! The syntax is mostly standard. A substitution begins with a dollar-sign //! and is followed by the source or constant name in parentheses. For instance, //! "$(mysource)" or "$(myconst)". The parentheses are always required. //! //! Constant names can be prefixed by a single formatting character followed //! by a colon. The only formatting characters currently supported are 'd' for //! decimal and 'x' for hex. For example, "$(x:myconst)" will be replaced with //! the value of the constant named "myconst" formatted as hexadecimal. The //! default is decimal, so the 'd' formatting character isn't really ever //! needed. //! //! \param message The string to perform substitution on. //! \return Returns a newly allocated std::string object that has all //! substitutions replaced with the associated value. The caller is //! responsible for freeing the string object using the delete operator. std::string *ConversionController::substituteVariables(const std::string *message) { string *result = new string(); unsigned i; int state = 0; string name; for (i = 0; i < message->size(); ++i) { char c = (*message)[i]; switch (state) { case 0: if (c == '$') { state = 1; } else { (*result) += c; } break; case 1: if (c == '(') { state = 2; } else { // Wasn't a variable substitution, so revert to initial state after // inserting the original characters. (*result) += '$'; (*result) += c; state = 0; } break; case 2: if (c == ')') { // Try the name as a source name first. if (m_sources.find(name) != m_sources.end()) { (*result) += m_sources[name]->getPath(); } // Otherwise try it as a variable. else { // Select format. const char *fmt = "%d"; if (name[1] == ':' && (name[0] == 'd' || name[0] == 'x')) { if (name[0] == 'x') { fmt = "0x%x"; } // Delete the format characters. name.erase(0, 2); } // Now insert the formatted variable if it exists. if (m_context.isVariableDefined(name)) { (*result) += format_string(fmt, m_context.getVariableValue(name)); } } // Switch back to initial state and clear name. state = 0; name.clear(); } else { // Just keep building up the variable name. name += c; } break; } } return result; } //! //! \param generator The generator to use. BootImage *ConversionController::generateOutput(BootImageGenerator *generator) { // set the generator's option context generator->setOptionContext(this); // add output sections to the generator in sequence section_vector_t::iterator it = m_outputSections.begin(); for (; it != m_outputSections.end(); ++it) { generator->addOutputSection(*it); } // and produce the output BootImage *image = generator->generate(); // Log::log("boot image = %p\n", image); return image; } //! Takes an AST node that is one of the following subclasses and creates the corresponding //! type of DataSource object from it. //! - StringConstASTNode //! - ExprASTNode //! - SourceASTNode //! - SectionASTNode //! - SectionMatchListASTNode //! - BlobConstASTNode //! - IVTConstASTNode //! //! \exception elftosb::semantic_error Thrown if a semantic problem is found with //! the data node. //! \exception std::runtime_error Thrown if an error occurs that shouldn't be possible //! based on the grammar. DataSource *ConversionController::createSourceFromNode(ASTNode *dataNode) { assert(dataNode); DataSource *source = NULL; StringConstASTNode *stringNode; BlobConstASTNode *blobNode; ExprASTNode *exprNode; SourceASTNode *sourceNode; SectionASTNode *sectionNode; SectionMatchListASTNode *matchListNode; IVTConstASTNode *ivtNode; if ((stringNode = dynamic_cast(dataNode))) { // create a data source with the string contents std::string *stringData = stringNode->getString(); const uint8_t *stringContents = reinterpret_cast(stringData->c_str()); source = new UnmappedDataSource(stringContents, static_cast(stringData->size())); } else if ((blobNode = dynamic_cast(dataNode))) { // create a data source with the raw binary data Blob *blob = blobNode->getBlob(); source = new UnmappedDataSource(blob->getData(), blob->getLength()); } else if ((exprNode = dynamic_cast(dataNode))) { // reduce the expression first exprNode = exprNode->reduce(m_context); IntConstExprASTNode *intNode = dynamic_cast(exprNode); if (!intNode) { throw semantic_error("load pattern expression did not evaluate to an integer"); } SizedIntegerValue intValue(intNode->getValue(), intNode->getSize()); source = new PatternSource(intValue); } else if ((sourceNode = dynamic_cast(dataNode))) { // load the entire source contents SourceFile *sourceFile = getSourceFromName(sourceNode->getSourceName(), sourceNode->getFirstLine()); source = sourceFile->createDataSource(); } else if ((sectionNode = dynamic_cast(dataNode))) { // load some subset of the source SourceFile *sourceFile = getSourceFromName(sectionNode->getSourceName(), sectionNode->getFirstLine()); if (!sourceFile->supportsNamedSections()) { throw semantic_error( format_string("line %d: source does not support sections", sectionNode->getFirstLine())); } // create data source from the section name std::string *sectionName = sectionNode->getSectionName(); GlobMatcher globber(*sectionName); source = sourceFile->createDataSource(globber); if (!source) { throw semantic_error(format_string("line %d: no sections match the pattern", sectionNode->getFirstLine())); } } else if ((matchListNode = dynamic_cast(dataNode))) { SourceFile *sourceFile = getSourceFromName(matchListNode->getSourceName(), matchListNode->getFirstLine()); if (!sourceFile->supportsNamedSections()) { throw semantic_error( format_string("line %d: source type does not support sections", matchListNode->getFirstLine())); } // create string matcher ExcludesListMatcher matcher; // add each pattern to the matcher ListASTNode *matchList = matchListNode->getSections(); ListASTNode::iterator it = matchList->begin(); for (; it != matchList->end(); ++it) { ASTNode *node = *it; sectionNode = dynamic_cast(node); if (!sectionNode) { throw std::runtime_error( format_string("line %d: unexpected node type in section pattern list", (*it)->getFirstLine())); } bool isInclude = sectionNode->getAction() == SectionASTNode::kInclude; matcher.addPattern(isInclude, *(sectionNode->getSectionName())); } // create data source from the section match list source = sourceFile->createDataSource(matcher); if (!source) { throw semantic_error( format_string("line %d: no sections match the section pattern list", matchListNode->getFirstLine())); } } else if ((ivtNode = dynamic_cast(dataNode))) { source = createIVTDataSource(ivtNode); } else { throw semantic_error(format_string("line %d: unexpected load data node type", dataNode->getFirstLine())); } return source; } DataSource *ConversionController::createIVTDataSource(IVTConstASTNode *ivtNode) { IVTDataSource *source = new IVTDataSource; // Iterate over the assignment statements in the IVT definition. ListASTNode *fieldList = ivtNode->getFieldAssignments(); if (fieldList) { ListASTNode::iterator it = fieldList->begin(); for (; it != fieldList->end(); ++it) { AssignmentASTNode *assignmentNode = dynamic_cast(*it); if (!assignmentNode) { throw std::runtime_error( format_string("line %d: unexpected node type in IVT definition", (*it)->getFirstLine())); } // Get the IVT field name. std::string *fieldName = assignmentNode->getIdent(); // Reduce the field expression and get the integer result. ASTNode *valueNode = assignmentNode->getValue(); ExprASTNode *valueExpr = dynamic_cast(valueNode); if (!valueExpr) { throw semantic_error("IVT field must have a valid expression"); } IntConstExprASTNode *valueIntExpr = dynamic_cast(valueExpr->reduce(m_context)); if (!valueIntExpr) { throw semantic_error(format_string("line %d: IVT field '%s' does not evaluate to an integer", valueNode->getFirstLine(), fieldName->c_str())); } uint32_t value = static_cast(valueIntExpr->getValue()); // Set the field in the IVT data source. if (!source->setFieldByName(*fieldName, value)) { throw semantic_error(format_string("line %d: unknown IVT field '%s'", assignmentNode->getFirstLine(), fieldName->c_str())); } } } return source; } //! Takes an AST node subclass and returns an appropriate DataTarget object that contains //! the same information. Supported AST node types are: //! - SymbolASTNode //! - NaturalLocationASTNode //! - AddressRangeASTNode //! //! \exception elftosb::semantic_error Thrown if a semantic problem is found with //! the target node. DataTarget *ConversionController::createTargetFromNode(ASTNode *targetNode) { assert(targetNode); DataTarget *target = NULL; SymbolASTNode *symbolNode; NaturalLocationASTNode *naturalNode; AddressRangeASTNode *addressNode; if ((symbolNode = dynamic_cast(targetNode))) { SourceFile *sourceFile = getSourceFromName(symbolNode->getSource(), symbolNode->getFirstLine()); std::string *symbolName = symbolNode->getSymbolName(); // symbol name is optional if (symbolName) { if (!sourceFile->supportsNamedSymbols()) { throw std::runtime_error( format_string("line %d: source does not support symbols", symbolNode->getFirstLine())); } target = sourceFile->createDataTargetForSymbol(*symbolName); if (!target) { throw std::runtime_error( format_string("line %d: source does not have a symbol with that name", symbolNode->getFirstLine())); } } else { // no symbol name was specified so use entry point target = sourceFile->createDataTargetForEntryPoint(); if (!target) { throw std::runtime_error( format_string("line %d: source does not have an entry point", symbolNode->getFirstLine())); } } } else if ((naturalNode = dynamic_cast(targetNode))) { // the target is the source's natural location target = new NaturalDataTarget(); } else if ((addressNode = dynamic_cast(targetNode))) { // evaluate begin address ExprASTNode *beginExpr = dynamic_cast(addressNode->getBegin()); if (!beginExpr) { throw semantic_error("address range must always have a beginning expression"); } IntConstExprASTNode *beginIntExpr = dynamic_cast(beginExpr->reduce(m_context)); if (!beginIntExpr) { throw semantic_error("address range begin did not evaluate to an integer"); } uint32_t beginAddress = static_cast(beginIntExpr->getValue()); // evaluate end address ExprASTNode *endExpr = dynamic_cast(addressNode->getEnd()); uint32_t endAddress = 0; bool hasEndAddress = false; if (endExpr) { IntConstExprASTNode *endIntExpr = dynamic_cast(endExpr->reduce(m_context)); if (!endIntExpr) { throw semantic_error("address range end did not evaluate to an integer"); } endAddress = static_cast(endIntExpr->getValue()); hasEndAddress = true; } // create target if (hasEndAddress) { target = new ConstantDataTarget(beginAddress, endAddress); } else { target = new ConstantDataTarget(beginAddress); } } else { throw semantic_error("unexpected load target node type"); } return target; } //! \param sourceName Pointer to string containing the name of the source to look up. //! May be NULL, in which case the default source is used. //! \param line The line number on which the source name was located. //! //! \result A source file object that was previously created in the processSources() //! stage. //! //! \exception std::runtime_error Thrown if the source name is invalid, or if it //! was NULL and there is no default source (i.e., we're not inside a from //! statement). SourceFile *ConversionController::getSourceFromName(std::string *sourceName, int line) { SourceFile *sourceFile = NULL; if (sourceName) { // look up source in map source_map_t::iterator it = m_sources.find(*sourceName); if (it == m_sources.end()) { source_name_vector_t::const_iterator findIt = std::find( m_failedSources.begin(), m_failedSources.end(), *sourceName); if (findIt != m_failedSources.end()) { throw semantic_error(format_string("line %d: error opening source '%s'", line, sourceName->c_str())); } else { throw semantic_error(format_string("line %d: invalid source name '%s'", line, sourceName->c_str())); } } sourceFile = it->second; } else { // no name provided - use default source sourceFile = m_defaultSource; if (!sourceFile) { throw semantic_error(format_string("line %d: source required but no default source is available", line)); } } // open the file if it hasn't already been if (!sourceFile->isOpen()) { sourceFile->open(); } return sourceFile; } //! Exercises the lexer by printing out the value of every token produced by the //! lexer. It is assumed that the lexer object has already be configured to read //! from some input file. The method will return when the lexer has exhausted all //! tokens, or an error occurs. void ConversionController::testLexer(ElftosbLexer &lexer) { // test lexer while (1) { YYSTYPE value; int lexresult = lexer.yylex(); if (lexresult == 0) break; lexer.getSymbolValue(&value); Log::log("%d -> int:%d, ast:%p", lexresult, value.m_int, value.m_str, value.m_ast); if (lexresult == TOK_IDENT || lexresult == TOK_SOURCE_NAME || lexresult == TOK_STRING_LITERAL) { if (value.m_str) { Log::log(", str:%s\n", value.m_str->c_str()); } else { Log::log("str:NULL\n"); } } else { Log::log("\n"); } } } //! Prints out the value of an integer constant expression AST node. Also prints //! the name of the identifier associated with that node, as well as the integer //! size. void ConversionController::printIntConstExpr(const std::string &ident, IntConstExprASTNode *expr) { // print constant value char sizeChar; switch (expr->getSize()) { case kWordSize: sizeChar = 'w'; break; case kHalfWordSize: sizeChar = 'h'; break; case kByteSize: sizeChar = 'b'; break; } Log::log("%s => %d:%c\n", ident.c_str(), expr->getValue(), sizeChar); }