diff options
author | Jaymes Wilks <mjwilks@us.ibm.com> | 2016-10-17 12:15:40 -0500 |
---|---|---|
committer | Daniel M. Crowell <dcrowell@us.ibm.com> | 2016-11-14 17:17:33 -0500 |
commit | 16263a641c48773091dd60b55e28ad77ca5a8574 (patch) | |
tree | 97120f76deb4132a1a1b7ceba8701318c5663a68 /src/usr/pnor/spnorrp.C | |
parent | a904e156364a8f0fd5f6bc2b7094f79cf77da1b2 (diff) | |
download | talos-hostboot-16263a641c48773091dd60b55e28ad77ca5a8574.tar.gz talos-hostboot-16263a641c48773091dd60b55e28ad77ca5a8574.zip |
Secure PNOR Resource Provider port from p8
Adds a Secure PNOR Resource Provider (SPNORRP) layer on top of the
original PNORRP to handle verification of secured PNOR sections.
Change-Id: Iff25abf599f3c850197c6e6d23ff03e5edf945bb
RTC:163078
Reviewed-on: http://ralgit01.raleigh.ibm.com/gerrit1/31588
Tested-by: Jenkins Server <pfd-jenkins+hostboot@us.ibm.com>
Tested-by: FSP CI Jenkins <fsp-CI-jenkins+hostboot@us.ibm.com>
Reviewed-by: Stephen M. Cprek <smcprek@us.ibm.com>
Reviewed-by: Michael Baiocchi <mbaiocch@us.ibm.com>
Reviewed-by: Daniel M. Crowell <dcrowell@us.ibm.com>
Diffstat (limited to 'src/usr/pnor/spnorrp.C')
-rw-r--r-- | src/usr/pnor/spnorrp.C | 948 |
1 files changed, 948 insertions, 0 deletions
diff --git a/src/usr/pnor/spnorrp.C b/src/usr/pnor/spnorrp.C new file mode 100644 index 000000000..71de27578 --- /dev/null +++ b/src/usr/pnor/spnorrp.C @@ -0,0 +1,948 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/pnor/spnorrp.C $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2016 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#include "pnorrp.H" +#include "spnorrp.H" +#include <pnor/pnor_reasoncodes.H> +#include <initservice/taskargs.H> +#include <initservice/initserviceif.H> +#include <sys/msg.h> +#include <trace/interface.H> +#include <errl/errlmanager.H> +#include <sys/mm.h> +#include <errno.h> +#include <util/align.H> +#include <config.h> +#include "pnor_common.H" +#include <console/consoleif.H> +#include <secureboot/service.H> +#include <secureboot/containerheader.H> +#include <secureboot/trustedbootif.H> +#include <secureboot/header.H> + +extern trace_desc_t* g_trac_pnor; + +// used to uniquley identify the secure PNOR RP message queue +const char* SPNORRP_MSG_Q = "spnorrpq"; + +// Easy macro replace for unit testing +//#define TRACUCOMP(args...) TRACFCOMP(args) +#define TRACUCOMP(args...) + +namespace PNOR +{ + //used by secure message queue for load/unload of secure PNOR sections + enum secure_msg_type + { + MSG_LOAD_SECTION = 0x02, + MSG_UNLOAD_SECTION = 0x03, + }; +}; + + +using namespace PNOR; + +/******************** + Helper Methods + ********************/ + +/** + * @brief Static function wrapper to pass into task_create + */ +void* secure_wait_for_message( void* unused ) +{ + TRACUCOMP(g_trac_pnor, "wait_for_message> " ); + Singleton<SPnorRP>::instance().waitForMessage(); + return NULL; +} + + +/******************** + Private/Protected Methods + ********************/ + +/** + * @brief Constructor + */ +SPnorRP::SPnorRP() +: +iv_msgQ(NULL) +,iv_startupRC(0) +{ + TRACFCOMP(g_trac_pnor, "SPnorRP::SPnorRP> " ); + // setup everything in a separate function + initDaemon(); + + TRACFCOMP(g_trac_pnor, "< SPnorRP::PnorRP : Startup Errors=%X ", iv_startupRC ); +} + +/** + * @brief Destructor + */ +SPnorRP::~SPnorRP() +{ + TRACFCOMP(g_trac_pnor, "SPnorRP::~SPnorRP> " ); + + // delete the message queue we created + if( iv_msgQ ) + { + msg_q_destroy( iv_msgQ ); + } + + // clean up the load records + for(std::map<SectionId, LoadRecord*>::iterator + i = iv_loadedSections.begin(); + i != iv_loadedSections.end(); + ++i) + { + LoadRecord* l_rec = (*i).second; + delete l_rec; + } + + TRACFCOMP(g_trac_pnor, "< SPnorRP::~SPnorRP" ); +} + +/** + * @brief A wrapper for mm_alloc_block that encapsulates error log creation. + */ +errlHndl_t SPnorRP::allocBlock(msg_q_t i_mq, void* i_va, uint64_t i_size) const +{ + errlHndl_t l_errhdl = NULL; + int rc = mm_alloc_block(i_mq, i_va, i_size ); + if( rc ) + { + TRACFCOMP( g_trac_pnor,"SPnorRP::allocBlock> Error " + "with mm_alloc_block at address 0x%.16llX : rc=%d", i_va, rc ); + /*@ + * @errortype + * @severity ERRL_SEV_CRITICAL_SYS_TERM + * @moduleid PNOR::MOD_SPNORRP_ALLOCATE_BLOCK + * @reasoncode PNOR::RC_EXTERNAL_ERROR + * @userdata1 Requested Address + * @userdata2 rc from mm_alloc_block + * @devdesc SPnorRP::initDaemon> Error from mm_alloc_block + * @custdesc A problem occurred while initializing secure PNOR + */ + l_errhdl = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_CRITICAL_SYS_TERM, + PNOR::MOD_SPNORRP_ALLOCATE_BLOCK, + PNOR::RC_EXTERNAL_ERROR, + TO_UINT64(reinterpret_cast<uint64_t>(i_va)), + TO_UINT64(rc), + true); //Add HB SW Callout + l_errhdl->collectTrace(PNOR_COMP_NAME); + l_errhdl->collectTrace(SECURE_COMP_NAME); + } + return l_errhdl; +} + +/** + * @brief A wrapper for mm_set_permission that adds error log creation. + */ +errlHndl_t SPnorRP::setPermission(void* i_va, uint64_t i_size, + uint64_t i_accessType) const +{ + errlHndl_t l_errhdl = NULL; + int rc = mm_set_permission(reinterpret_cast<void*>(i_va), i_size, + i_accessType); + if ( rc ) + { + TRACFCOMP( g_trac_pnor, "SPnorRP::setPermission> Error " + "with mm_set_permission at address 0x%.16llX : rc=%d",i_va, rc ); + /*@ + * @errortype + * @severity ERRL_SEV_CRITICAL_SYS_TERM + * @moduleid PNOR::MOD_SPNORRP_SET_PERMISSION + * @reasoncode PNOR::RC_EXTERNAL_ERROR + * @userdata1 Requested Address + * @userdata2 rc from mm_set_permission + * @devdesc Could not set permissions of the + * given PNOR section + * @custdesc A problem occurred while initializing secure PNOR + */ + l_errhdl = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_CRITICAL_SYS_TERM, + PNOR::MOD_SPNORRP_SET_PERMISSION, + PNOR::RC_EXTERNAL_ERROR, + TO_UINT64(reinterpret_cast<uint64_t>(i_va)), + TO_UINT64(rc), + true); // Add HB SW Callout + l_errhdl->collectTrace(PNOR_COMP_NAME); + l_errhdl->collectTrace(SECURE_COMP_NAME); + } + return l_errhdl; +} + +/** + * @brief Initialize the daemon + */ +void SPnorRP::initDaemon() +{ + TRACFCOMP(g_trac_pnor, "SPnorRP::initDaemon> " ); + errlHndl_t l_errhdl = NULL; + + do + { + // create a message queue for secure space + iv_msgQ = msg_q_create(); + + // register it so that it can be discovered by loadSecureSection() + int rc = msg_q_register(iv_msgQ, SPNORRP_MSG_Q); + + assert(rc == 0); + + // create a Block for temp space + l_errhdl = allocBlock( NULL, reinterpret_cast<void*>(TEMP_VADDR), + PNOR_SIZE ); + if( l_errhdl ) + { + break; + } + + // set permissions for temp space + l_errhdl = setPermission(reinterpret_cast<void*>(TEMP_VADDR), + PNOR_SIZE, WRITABLE | ALLOCATE_FROM_ZERO); + if ( l_errhdl ) + { + break; + } + + // create a block for secure space + l_errhdl = allocBlock( iv_msgQ, reinterpret_cast<void*>(SBASE_VADDR), + PNOR_SIZE ); + if( l_errhdl ) + { + break; + } + + // set permissions for secure space + l_errhdl = setPermission( reinterpret_cast<void*>(SBASE_VADDR), + PNOR_SIZE, NO_ACCESS); + if ( l_errhdl ) + { + break; + } + + // start task to wait on the queue + task_create( secure_wait_for_message, NULL ); + + } while(0); + + if( l_errhdl ) + { + iv_startupRC = l_errhdl->reasonCode(); + errlCommit(l_errhdl,PNOR_COMP_ID); + } + + TRACUCOMP(g_trac_pnor, "< SPnorRP::initDaemon" ); +} + +/** + * @brief Load secure sections into temporary address space and verify them + */ +void SPnorRP::verifySections(LoadRecord* o_rec, SectionId i_id) +{ + SectionInfo_t l_info; + errlHndl_t l_errhdl = NULL; + bool failedVerify = false; + + do { + l_errhdl = getSectionInfo(i_id, l_info); + + if (l_errhdl) + { + TRACFCOMP(g_trac_pnor, + "< SPnorrRP::verifySections - getSectionInfo failed"); + + break; + } + TRACDCOMP(g_trac_pnor,"SPnorRP::verifySections getSectionInfo" + " succeeded for sec = %s", l_info.name); + + l_info.vaddr -= PAGESIZE; // back up a page to expose the secure header + l_info.size += PAGESIZE; // add a page to size to account for the header + + // it's a coding error if l_info.vaddr is not in secure space + assert(l_info.vaddr >= SBASE_VADDR, "Virtual address for section %s is" + " not in secure space. Bad ptr=0x%X", l_info.name, l_info.vaddr); + + // Note: A pointer to virtual memory in one PNOR space can be converted + // to a pointer to any of the other two PNOR spaces and visa versa. + // These are unsecured space, temp space, and secured space. They are + // evenly separated by VMM_VADDR_SPNOR_DELTA and in the above order. + // l_info.vaddr points to secure space, so we subtract a delta value + // from it to calculate its corresponding address in temp space. + uint8_t* l_tempAddr = reinterpret_cast<uint8_t*>(l_info.vaddr) + - VMM_VADDR_SPNOR_DELTA; + + // calcluate unsecured address from temp address + uint8_t* l_unsecuredAddr = l_tempAddr - VMM_VADDR_SPNOR_DELTA; + + TRACFCOMP(g_trac_pnor,"SPnorRP::verifySections section start address " + "in temp space is 0x%.16llX, " + "section start address in unsecured space is 0x%.16llX, " + "l_info.size = 0x%.16llX, " + "payload size = 0x%.16llX, ", + l_tempAddr, l_unsecuredAddr, l_info.size, + l_info.secureProtectedPayloadSize); + + TRACDBIN(g_trac_pnor,"SPnorRP::verifySections unsecured mem now: ", + l_unsecuredAddr, 128); + + TRACDCOMP(g_trac_pnor,"SPnorRP::verifySections about to do memcpy"); + + // copy from unsecured PNOR space to temp PNOR space + memcpy(l_tempAddr, l_unsecuredAddr, l_info.secureProtectedPayloadSize + + PAGESIZE); // plus header size + + SECUREBOOT::ContainerHeader l_conHdr(l_tempAddr); + size_t l_totalContainerSize = l_conHdr.totalContainerSize(); + + TRACDCOMP(g_trac_pnor, "SPnorRP::verifySections " + "Total container size = 0x%.16llX", l_totalContainerSize); + + assert(l_totalContainerSize >= PAGESIZE + + + l_info.secureProtectedPayloadSize, + "For section %s, total container size (%d) was less than header " + "size (4096) + payload text size (%d)", + l_info.name, + l_totalContainerSize, + l_info.secureProtectedPayloadSize); + + assert(l_info.size >= l_totalContainerSize, + "For section %s, logical section size (%d) was less than total " + "container size (%d)", + l_info.name, + l_info.size, + l_totalContainerSize); + + TRACDCOMP(g_trac_pnor,"SPnorRP::verifySections did memcpy"); + TRACDBIN(g_trac_pnor,"SPnorRP::verifySections temp mem now: ", + l_tempAddr, 128); + + // store secure space pointer in load record + o_rec->secAddr = reinterpret_cast<uint8_t*>(l_info.vaddr) + PAGESIZE; + + TRACFCOMP(g_trac_pnor,"section start address in secure space is " + "0x%.16llX",o_rec->secAddr); + + // verify while in temp space + if (SECUREBOOT::enabled()) + { + l_errhdl = SECUREBOOT::verifyContainer(l_tempAddr); + if (l_errhdl) + { + TRACFCOMP(g_trac_pnor, ERR_MRK"SPnorrRP::verifySections - section " + "with id 0x%08X failed verifyContainer", i_id); + failedVerify = true; + break; + } + + l_errhdl = miscSectionVerification(l_tempAddr, i_id); + if (l_errhdl) + { + TRACFCOMP(g_trac_pnor, ERR_MRK"SPnorrRP::verifySections - section " + " with id 0x%08X failed miscSectionVerification", i_id); + failedVerify = true; + break; + } + } + + // verification succeeded + + // parse container header now that it is verified + // store the payload text size in the section load record + // Note: the text size we get back is now trusted + o_rec->textSize = l_conHdr.payloadTextSize(); + l_totalContainerSize = l_conHdr.totalContainerSize(); + + assert(o_rec->textSize == l_info.secureProtectedPayloadSize); + + // pcr extension of PNOR hash + l_errhdl = TRUSTEDBOOT::extendPnorSectionHash(l_conHdr, + (l_tempAddr + PAGESIZE), + i_id); + if(l_errhdl) + { + TRACFCOMP(g_trac_pnor,"SPnorRP::verifySections extendPnorSectionHash failed"); + break; + } + + // remove secure header page in temp space + mm_remove_pages(RELEASE, l_tempAddr, PAGESIZE); + + // keep track of info size in load record + o_rec->infoSize = l_totalContainerSize - PAGESIZE; + + // set permissions on the secured pages to writable + l_errhdl = setPermission(o_rec->secAddr, o_rec->textSize, WRITABLE); + if(l_errhdl) + { + TRACFCOMP(g_trac_pnor,"SPnorRP::verifySections set permissions " + "failed on text section"); + break; + } + + // set permissions on the unsecured pages to write tracked so that any + // unprotected payload pages with dirty writes can flow back to PNOR. + uint64_t unprotectedPayloadSize = l_totalContainerSize + - PAGESIZE - o_rec->textSize; + if (unprotectedPayloadSize) // only write track a non-zero range + { + TRACFCOMP(g_trac_pnor,INFO_MRK " SPnorRP::verifySections " + "creating unprotected area (%d bytes) for section %s", + unprotectedPayloadSize, + l_info.name); + + // Split the mod math out of the assert as the trace would not + // display otherwise. + bool l_onPageBoundary = !(o_rec->textSize % PAGESIZE); + assert( l_onPageBoundary, "For section %s, payloadTextSize does " + "not fall on a page boundary and there is an unprotected " + "payload", + l_info.name); + + l_errhdl = setPermission(o_rec->secAddr + o_rec->textSize, + unprotectedPayloadSize, + WRITABLE | WRITE_TRACKED); + if(l_errhdl) + { + TRACFCOMP(g_trac_pnor,"SPnorRP::verifySections set permissions " + "failed on data section"); + break; + } + + // Register the write tracked memory range to be flushed on + // shutdown. + INITSERVICE::registerBlock(o_rec->secAddr + o_rec->textSize, + unprotectedPayloadSize, SPNOR_PRIORITY); + } + else + { + TRACFCOMP(g_trac_pnor,INFO_MRK " SPnorRP::verifySections not " + "creating unprotected area for section %s", + l_info.name); + } + TRACFCOMP(g_trac_pnor,"SPnorRP::verifySections set permissions " + "and register block complete"); + } while(0); + + if( l_errhdl ) + { + uint32_t l_errPlid = l_errhdl->plid(); + iv_startupRC = l_errhdl->reasonCode(); + TRACFCOMP(g_trac_pnor,ERR_MRK"SPnorRP::verifySections there was an error"); + if (failedVerify) + { + TRACFCOMP(g_trac_pnor,ERR_MRK"SPnorRP::verifySections failed verify"); + SECUREBOOT::handleSecurebootFailure(l_errhdl); + } + else + { + errlCommit(l_errhdl,PNOR_COMP_ID); + INITSERVICE::doShutdown(l_errPlid); + } + } +} + + + +/** + * @brief Message receiver for secure space + */ +void SPnorRP::waitForMessage() +{ + TRACFCOMP(g_trac_pnor, "SPnorRP::waitForMessage>" ); + + errlHndl_t l_errhdl = NULL; + msg_t* message = NULL; + uint8_t* user_addr = NULL; + uint8_t* eff_addr = NULL; + int rc = 0; + uint64_t status_rc = 0; + LoadRecord l_rec; + l_rec.secAddr = NULL; + l_rec.textSize = 0; + l_rec.infoSize = 0; + + while(1) + { + status_rc = 0; + TRACUCOMP(g_trac_pnor, "SPnorRP::waitForMessage> waiting for message" ); + message = msg_wait( iv_msgQ ); + if( message ) + { + // data[0] = virtual address requested + // data[1] = address to place contents + eff_addr = reinterpret_cast<uint8_t*>(message->data[0]); + user_addr = reinterpret_cast<uint8_t*>(message->data[1]); + + switch(message->type) + { + case( MSG_MM_RP_READ ): + { + uint64_t delta = VMM_VADDR_SPNOR_DELTA; + + // check and see if our cached section information + // is obsolete and if so, change it + if ( (eff_addr < l_rec.secAddr) || + (eff_addr >= (l_rec.secAddr + l_rec.infoSize)) + ) + { + bool l_found = false; + // recalculate addresses + for(std::map<SectionId, LoadRecord*>::const_iterator + i = iv_loadedSections.begin(); + i != iv_loadedSections.end(); + ++i) + { + LoadRecord* l_record = (*i).second; + if ( (eff_addr >= l_record->secAddr) && + (eff_addr < (l_record->secAddr + + l_record->infoSize)) + ) + { + l_rec = *l_record; + l_found = true; + break; + } + } + if (!l_found) + { + TRACFCOMP( g_trac_pnor, + "SPnorRP::waitforMessage - address %p " + "out of bounds", eff_addr); + // not much we can do here unfortunately + // if we get here it is a coding mistake + status_rc = -EFAULT; + break; + } + + } + + TRACDCOMP( g_trac_pnor, "SPnorRP::waitForMessage got a" + " request to read from secure space - " + "message : user_addr=%p, eff_addr=%p, msgtype=%d, " + "textSize=0x%.16llX secAddr0x%.16llX", user_addr, + eff_addr, message->type, l_rec.textSize, + l_rec.secAddr); + + // determine the source of the data depending on + // whether it is part of the secure payload. + // by the way, this if could be removed to make this + // purely arithmetic + if (eff_addr >= (l_rec.secAddr + l_rec.textSize)) + { + delta += VMM_VADDR_SPNOR_DELTA; + } + TRACDCOMP( g_trac_pnor, "SPnorRP::waitForMessage " + "source address: %p", eff_addr - delta); + + // depending on the value of delta, memcpy from either + // temp space or unsecured pnor space over to secure + // pnor space + memcpy(user_addr, eff_addr - delta, PAGESIZE); + // if the page came from temp space then free up + // the temp page now that we're done with it + if (eff_addr < (l_rec.secAddr + l_rec.textSize)) + { + mm_remove_pages(RELEASE, eff_addr - delta, + PAGESIZE); + } + } + break; + + case( MSG_MM_RP_WRITE ): + { + TRACDCOMP( g_trac_pnor, "SPnorRP::waitForMessage " + "writing to an unsecured area of section."); + + // calculate unsecured address (in PnorRP) by + // subtracting two deltas from secure address + uint8_t* dest = eff_addr - VMM_VADDR_SPNOR_DELTA + - VMM_VADDR_SPNOR_DELTA; + memcpy(dest, user_addr, PAGESIZE); + } + break; + + case( PNOR::MSG_LOAD_SECTION ): + { + SectionId l_id = + static_cast<SectionId>(message->data[0]); + if (iv_loadedSections[l_id] == NULL) + { + LoadRecord* l_record = new LoadRecord; + verifySections(l_record,l_id); + iv_loadedSections[l_id] = l_record; + + // cache the record to use fields later as hints + l_rec = *l_record; + } + } + break; + + default: + TRACFCOMP( g_trac_pnor, ERR_MRK"SPnorRP::waitForMessage> " + "Unrecognized message type : user_addr=%p, eff_addr=%p," + " msgtype=%d", user_addr, eff_addr, message->type ); + /*@ + * @errortype + * @severity ERRL_SEV_CRITICAL_SYS_TERM + * @moduleid PNOR::MOD_SPNORRP_WAITFORMESSAGE + * @reasoncode PNOR::RC_INVALID_MESSAGE_TYPE + * @userdata1 Message type + * @userdata2 Requested Virtual Address + * @devdesc PnorRP::waitForMessage> Unrecognized + * message type + * @custdesc A problem occurred while accessing + * the boot flash. + */ + l_errhdl = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_CRITICAL_SYS_TERM, + PNOR::MOD_SPNORRP_WAITFORMESSAGE, + PNOR::RC_INVALID_MESSAGE_TYPE, + TO_UINT64(message->type), + reinterpret_cast<uint64_t>(eff_addr), + true /*Add HB SW Callout*/); + l_errhdl->collectTrace(PNOR_COMP_NAME); + l_errhdl->collectTrace(SECURE_COMP_NAME); + status_rc = -EINVAL; /* Invalid argument */ + } + + assert(msg_is_async(message) == false); + + if( l_errhdl ) + { + errlCommit(l_errhdl,PNOR_COMP_ID); + } + + /* Expected Response: + * data[0] = virtual address requested or + * section id (if load message) + * data[1] = rc (0 or negative errno value) + * extra_data = Specific reason code. + */ + message->data[1] = status_rc; + message->extra_data = 0; + rc = msg_respond( iv_msgQ, message ); + if( rc ) + { + TRACFCOMP(g_trac_pnor, + "SPnorRP::waitForMessage> Error from msg_respond, " + "giving up : rc=%d", rc ); + break; + } + } + assert(message != NULL); + } + + TRACFCOMP(g_trac_pnor, "< SPnorRP::waitForMessage" ); +} + +/** + * @brief Loads requested PNOR section to secure virtual address space + */ +errlHndl_t PNOR::loadSecureSection(const SectionId i_section) +{ + // Send message to secure provider to load the section + errlHndl_t err = NULL; + + if (!isSecureSection(i_section)) + { + TRACFCOMP(g_trac_pnor,ERR_MRK"PNOR::loadSecureSection> called on " + "unsecured section"); + // TODO securebootp9 remove below temporary code after all of the + // sections in the below if condition have been fully ported and added + // to isSecureSection. + // start temporary code + if (i_section == PNOR::HB_EXT_CODE || + i_section == PNOR::HB_DATA || + i_section == PNOR::SBE_IPL || + i_section == PNOR::CENTAUR_SBE || + i_section == PNOR::PAYLOAD || + i_section == PNOR::OCC || + i_section == PNOR::HB_RUNTIME) + { + // For now, ignore the attempt to load this section securely. + // Returning from the middle of a function is excusable because + // it keeps the temp code in one place, making it easier to remove. + return NULL; + } + // end temporary code + // TODO securebootp9 revisit this assert code and replace with error log + // code if it is deemed that this assert could happen in the field + assert(false,"PNOR::loadSection> section %i is not a secure section", + i_section); + } + + msg_q_t spnorQ = msg_q_resolve(SPNORRP_MSG_Q); + + assert(spnorQ != NULL); + + msg_t* msg = msg_allocate(); + + assert(msg != NULL); + + msg->type = PNOR::MSG_LOAD_SECTION; + msg->data[0] = static_cast<uint64_t>(i_section); + int rc = msg_sendrecv(spnorQ, msg); + + TRACFCOMP(g_trac_pnor, "loadSecureSection i_section = %i",i_section); + + // TODO securebootp9 - Need to be able to receive an error from the + // message handler. Also, message handler should police whether the request + // is for a secure section or not and throw an error accordingly. + //if (0 == rc) + //{ + // err = reinterpret_cast<errlHndl_t>(msg->data[1]); + //} + //else remove the if clause below at some point + if (rc != 0) + { + + TRACFCOMP(g_trac_pnor,ERR_MRK"PNOR::loadSecureSection> Error from msg_sendrecv rc=%d", + rc ); + /* @errorlog + * @severity ERRL_SEV_CRITICAL_SYS_TERM + * @moduleid MOD_PNORRP_LOADSECURESECTION + * @reasoncode RC_EXTERNAL_ERROR + * @userdata1 returncode from msg_sendrecv() + * @userdata2[0:31] SPNOR message type [LOAD | UNLOAD] + * @userdata2[32:63] Section ID + * @devdesc Could not load/unload section. + * @custdesc Security failure: unable to securely load + * requested firmware. + */ + err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_CRITICAL_SYS_TERM, + MOD_PNORRP_LOADSECURESECTION, + RC_EXTERNAL_ERROR, + rc, + TWO_UINT32_TO_UINT64(PNOR::MSG_LOAD_SECTION, + i_section), + true /* Add HB Software Callout */); + err->collectTrace(PNOR_COMP_NAME); + } + msg_free(msg); + + return err; +} + +/** + * @brief Flushes any applicable pending writes and unloads requested PNOR + * section from secure virtual address space + */ +errlHndl_t PNOR::unloadSecureSection(const SectionId i_section) +{ + // @TODO RTC 156118 + // Replace with call to secure provider to unload the section + errlHndl_t pError=NULL; + return pError; +} + +errlHndl_t SPnorRP::miscSectionVerification(const uint8_t *i_vaddr, + SectionId i_secId) const +{ + errlHndl_t l_errl = NULL; + assert(i_vaddr != NULL); + + TRACFCOMP(g_trac_pnor, "SPnorRP::miscSectionVerification section=%d", i_secId); + +// TODO securebootp9 - remove the following #if 0 and address issues +#if 0 + // Do any additional verification needed for a specific PNOR section + switch (i_secId) { + case HB_EXT_CODE: + // Compare HBB and HBI versions. Pass the vaddr of HBI's hash page + // table by skipping past the container header. + l_errl = baseExtVersCheck((i_vaddr + PAGESIZE)); + break; + case SBKT: + // Ensure the SBKT partition has a valid key transition container + // Add PAGESIZE to skip outer container + l_errl = keyTransitionCheck((i_vaddr + PAGESIZE)); + break; + default: + break; + } +#endif + return l_errl; +} + +errlHndl_t SPnorRP::baseExtVersCheck(const uint8_t *i_vaddr) const +{ + errlHndl_t l_errl = NULL; + assert(i_vaddr != NULL); + + // Check if measured and build time hashes of HBB sw signatures match. + // Query the HBB header + const void* l_pHbbHeader = NULL; + SECUREBOOT::baseHeader().getHeader(l_pHbbHeader); + // Fatal code bug if either address is NULL + assert(l_pHbbHeader!=NULL,"ERORR: Cached header address is NULL"); + // Build a container header object from the raw header + SECUREBOOT::ContainerHeader l_hbbContainerHeader(l_pHbbHeader); + + // Calculate hash of HBB's sw signatures + SHA512_t l_hashSwSigs = {0}; + + SECUREBOOT::hashBlob(l_hbbContainerHeader.sw_sigs(), + l_hbbContainerHeader.totalSwKeysSize(), + l_hashSwSigs); + + // Get build time hash of HBB's sw signatures. The hash of HBB's sw + // signatures are stored in the first entry (SALT) of HBI's hash page + // table. The first entry of HBI's hash pagle starts immediately after the + // header so we can simply cast the vaddr to get our first entry (SALT) + // Note: It is expected that i_vaddr points to the start of HBI's hash + // page table. + const PAGE_TABLE_ENTRY_t* l_hashPageTableSaltEntry = + reinterpret_cast<const PAGE_TABLE_ENTRY_t*>(i_vaddr); + + // Throw an error if the hashes do not match to the truncated length + // of a hash page table entry. + if ( memcmp(l_hashSwSigs, l_hashPageTableSaltEntry, + HASH_PAGE_TABLE_ENTRY_SIZE) != 0 ) + { + TRACFCOMP(g_trac_pnor, ERR_MRK"SPnorRP::baseExtVersCheck Hostboot Base and Extended image mismatch"); + TRACFBIN(g_trac_pnor,"SPnorRP::baseExtVersCheck Measured sw key hash", + l_hashSwSigs, HASH_PAGE_TABLE_ENTRY_SIZE); + TRACFBIN(g_trac_pnor,"SPnorRP::baseExtVersCheck HBI's hash page table salt entry", + l_hashPageTableSaltEntry, HASH_PAGE_TABLE_ENTRY_SIZE); + + // Memcpy needed for measured hash to avoid gcc error: dereferencing + // type-punned pointer will break strict-aliasing rules + uint64_t l_measuredHash = 0; + memcpy(&l_measuredHash, l_hashSwSigs, sizeof(l_measuredHash)); + /*@ errorlog + * @severity ERRL_SEV_CRITICAL_SYS_TERM + * @moduleid MOD_SPNORRP_BASE_EXT_VER_CHK + * @reasoncode RC_BASE_EXT_MISMATCH + * @userdata1 First 8 bytes of hash of measured SW signatures + * @userdata2 First 8 bytes of hash of stored SW signatures in + * hash page table + * @devdesc Hostboot Base and Extend code do not match versions. + * @custdesc Firmware level mismatch. + */ + l_errl = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_CRITICAL_SYS_TERM, + MOD_SPNORRP_BASE_EXT_VER_CHK, + RC_BASE_EXT_MISMATCH, + l_measuredHash, + TO_UINT64(*reinterpret_cast<const uint64_t*>(l_hashPageTableSaltEntry))); + l_errl->collectTrace(PNOR_COMP_NAME); + l_errl->collectTrace(SECURE_COMP_NAME); + } + + return l_errl; +} + +errlHndl_t SPnorRP::keyTransitionCheck(const uint8_t *i_vaddr) const +{ + errlHndl_t l_errl = NULL; + assert(i_vaddr != NULL); + + do { + // Check if the header flags have the key transition bit set + SECUREBOOT::ContainerHeader l_nestedConHdr(i_vaddr); + if (!l_nestedConHdr.sb_flags()->hw_key_transition) + { + TRACFCOMP( g_trac_pnor, ERR_MRK"SPnorRP::keyTransitionCheck() - Key transition flag not set"); + /*@ + * @errortype + * @severity ERRL_SEV_CRITICAL_SYS_TERM + * @moduleid MOD_SPNORRP_KEY_TRAN_CHK + * @reasoncode RC_KEY_TRAN_FLAG_UNSET + * @userdata1 0 + * @userdata2 0 + * @devdesc Key transition flag not set in nested SBKT container containing new hw keys + * @custdesc Secureboot key transition failure + */ + l_errl = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_CRITICAL_SYS_TERM, + MOD_SPNORRP_KEY_TRAN_CHK, + RC_KEY_TRAN_FLAG_UNSET, + 0, + 0, + true /*Add HB Software Callout*/ ); + l_errl->collectTrace(PNOR_COMP_NAME); + l_errl->collectTrace(SECURE_COMP_NAME); + break; + } + +// TODO securebootp9 - remove the following #if 0 and address issues +#if 1 + // Validate nested container is properly signed using new hw keys + l_errl = SECUREBOOT::verifyContainer(const_cast<uint8_t*>(i_vaddr), + l_nestedConHdr.hwKeyHash()); + if (l_errl) + { + TRACFCOMP( g_trac_pnor, ERR_MRK"SPnorRP::keyTransitionCheck() - failed verifyContainer"); + break; + } +#endif + }while(0); + + return l_errl; +} + +bool PNOR::cmpSecurebootMagicNumber(const uint8_t* i_vaddr) +{ + return memcmp(&MAGIC_NUMBER, i_vaddr, sizeof(MAGIC_NUMBER)) == 0; +} + +errlHndl_t PNOR::hasSecurebootMagicNumber(const SectionId i_section, + bool &o_valid) +{ + errlHndl_t l_errl = NULL; + SectionInfo_t l_info; + + // Force to false + o_valid = false; + + // This will not work for HBB + assert(i_section != HB_BASE_CODE, "hasSecurebootMagicNumber() does not work for HBB section"); + + bool isSecure = PNOR::isSecureSection(i_section); + do { + l_errl = getSectionInfo(i_section, l_info); + if (l_errl) + { + TRACFCOMP(g_trac_pnor, ERR_MRK"PNOR::hasSecurebootMagicNumber(): - getSectionInfo failed"); + break; + } + + // Use PNOR vaddr + if(isSecure) + { + // back up a page to expose the secure header + l_info.vaddr = l_info.vaddr - VMM_VADDR_SPNOR_DELTA + - VMM_VADDR_SPNOR_DELTA + - PAGESIZE; + } + o_valid = cmpSecurebootMagicNumber(reinterpret_cast<uint8_t*> + (l_info.vaddr)); + }while(0); + + return l_errl; +} + |