diff options
Diffstat (limited to 'src/usr/initservice/istepdispatcher/istepdispatcher.C')
-rw-r--r-- | src/usr/initservice/istepdispatcher/istepdispatcher.C | 546 |
1 files changed, 482 insertions, 64 deletions
diff --git a/src/usr/initservice/istepdispatcher/istepdispatcher.C b/src/usr/initservice/istepdispatcher/istepdispatcher.C index 5dfb5a53e..002cc59d4 100644 --- a/src/usr/initservice/istepdispatcher/istepdispatcher.C +++ b/src/usr/initservice/istepdispatcher/istepdispatcher.C @@ -24,7 +24,9 @@ /** * @file istepdispatcher.C * - * IStep Dispatcher interface. Launched from Extended Initialization Service + * IStep Dispatcher code. Launched from Extended Initialization Service + * + * PNOR Driver and Trace should be available by the time this is launched. * */ @@ -36,12 +38,18 @@ #include <stdio.h> #include <string.h> -#include <sys/task.h> // tid_t, task_create, etc -#include <trace/interface.H> // trace support -#include <errl/errlentry.H> // errlHndl_t +#include <sys/task.h> // tid_t, task_create, etc +#include <sys/time.h> // nanosleep +#include <trace/interface.H> // trace support +#include <errl/errlentry.H> // errlHndl_t +#include <devicefw/userif.H> // targeting +#include <sys/mmio.h> // mmio_scratch_read() #include "istepdispatcher.H" -#include "isteplist.H" + +#include "splesscommon.H" + +#include <isteps/isteplist.H> namespace INITSERVICE @@ -53,102 +61,502 @@ namespace INITSERVICE extern trace_desc_t *g_trac_initsvc; /** - * @brief set up _start() task entry procedure using the macro + * @enum + * SPLess Task return codes + * + * task return codes for SPless single step + * @note future errors will be passed from task_create() and task_exec() + * and should be documented in errno.h + * + */ +enum { + SPLESS_TASKRC_INVALID_ISTEP = -3, // invalid istep or substep + SPLESS_TASKRC_LAUNCH_FAIL = -4, // failed to launch the task + SPLESS_TASKRC_RETURNED_ERRLOG = -5, // istep returned an errorlog + SPLESS_TASKRC_TERMINATED = -6, // terminated the polling loop +}; + +/** + * @note SPLess PAUSE - These two constants are used in a nanosleep() call + * below to sleep between polls of the StatusReg. Typically this will + * be about 100 ms - the actual value will be determined empirically. + * + * @debug simics "feature" - set polling time to 1 sec for demo + * + */ + +const uint64_t SINGLESTEP_PAUSE_S = 1; +const uint64_t SINGLESTEP_PAUSE_NS = 100000000; + + +/** + * @brief set up _start() task entry procedure using the macro in taskargs.H */ TASK_ENTRY_MACRO( IStepDispatcher::getTheInstance().init ); -IStepDispatcher::IStepDispatcher() -: iv_istepmodeflag(false), - iv_nextistep( 0 ), - iv_cancontinueflag(false) +const TaskInfo *IStepDispatcher::findTaskInfo( const uint16_t i_IStep, + const uint16_t i_SubStep ) const { + const TaskInfo *l_pistep = NULL; + + TRACDCOMP( g_trac_initsvc, + "g_isteps[%d].numitems = 0x%x", + i_IStep, + g_isteps[i_IStep].numitems ); + + // apply filters + do + { + + // check input range - IStep + if ( i_IStep >= MAX_ISTEPS ) + { + TRACDCOMP( g_trac_initsvc, + "IStep 0x%x out of range.", + i_IStep ); + break; // break out with l_pistep set to NULL + } + + // check input range - ISubStep + if ( i_SubStep >= g_isteps[i_IStep].numitems ) + { + TRACDCOMP( g_trac_initsvc, + "IStep 0x%x Substep 0x%x out of range.", + i_IStep, + i_SubStep ); + break; // break out with l_pistep set to NULL + } + + // check for end of list. + if ( g_isteps[i_IStep].pti[i_SubStep].taskflags.task_type + == END_TASK_LIST ) + { + TRACDCOMP( g_trac_initsvc, + "IStep 0x%x SubSStep 0x%x task_type==END_TASK_LIST.", + i_IStep, + i_SubStep ); + break; + } + + // looks good, send it back to the caller + TRACDCOMP( g_trac_initsvc, + "Found TaskInfo 0x%x 0x%x", + i_IStep, + i_SubStep ); + l_pistep = &( g_isteps[i_IStep].pti[i_SubStep] ); + } while ( 0 ); + + return l_pistep; } -IStepDispatcher::~IStepDispatcher() +void IStepDispatcher::init( void * io_ptr ) { + TRACFCOMP( g_trac_initsvc, + ENTER_MRK "starting IStepDispatcher, io_ptr=%p ", + io_ptr ); + + if ( getIStepMode() ) + { + TRACFCOMP( g_trac_initsvc, + "IStep single-step" ); + // IStep single-step + singleStepISteps( io_ptr ); + } + else + { + TRACFCOMP( g_trac_initsvc, + "IStep run all" ); + // Run all the ISteps sequentially + runAllISteps( io_ptr ); + } // endelse + + + TRACFCOMP( g_trac_initsvc, + EXIT_MRK "IStepDispatcher finished."); } -IStepDispatcher& IStepDispatcher::getTheInstance() +bool IStepDispatcher::getIStepMode( ) const { - return Singleton<IStepDispatcher>::instance(); + + return iv_istepmodeflag; +} + + +/** + * @todo revisit this when PNOR Driver is implemented + * + */ +void IStepDispatcher::initIStepMode( ) +{ + uint64_t l_readData = 0; + + l_readData = mmio_scratch_read( MMIO_SCRATCH_IPLSTEP_CONFIG ); + + TRACDCOMP( g_trac_initsvc, + "SCOM ScratchPad read, Offset 0x%x, Data 0x%llx", + MMIO_SCRATCH_IPLSTEP_CONFIG, + l_readData ); + + // check for IStep Mode signature + if ( l_readData == ISTEP_MODE_SIGNATURE ) + { + iv_istepmodeflag = true; + } + else + { + iv_istepmodeflag = false; + } + } -void IStepDispatcher::init( void * ptr ) +void IStepDispatcher::singleStepISteps( void * io_ptr ) const { - errlHndl_t errl = NULL; - uint64_t nextIStep = 0; - const TaskInfo *pistep = NULL; - TaskArgs::TaskArgs args; + errlHndl_t l_errl = NULL; + TaskArgs::TaskArgs l_args; + const TaskInfo *l_pistep = NULL; + bool l_gobit = false; + uint16_t l_nextIStep = 0; + uint16_t l_nextSubstep = 0; + uint16_t l_taskStatus = 0; + uint16_t l_istepStatus = 0; + uint32_t l_progresscode = 0; + uint64_t l_isteprc = 0; + + TRACFCOMP( g_trac_initsvc, "Start IStep single-step.\n" ); + + // initialize command reg + SPLESSCMD::write( false, // go bit is false + 0, // istep = 0 + 0 ); // substep = 0 + + SPLESSSTS::write( false, // running bit + true, // ready bit + 0, // istep running + 0, // substep running + 0, // task status + 0 ); // istep status + + // + // @note Start the polling loop. + // Currently this has no exit, user should reset the machine and/or + // load the payload, which will wipe HostBoot from memory. + // Loop forever, unless something Very Bad happens. + // + while( 1 ) + { + // read command reg, updates l_gobit, l_nextIStep, l_nextSubstep + SPLESSCMD::read( l_gobit, + l_nextIStep, + l_nextSubstep ); - TRACFCOMP( g_trac_initsvc, - ENTER_MRK "starting IStepDispatcher, ptr=%p. &args=%p", - ptr, &args ); + // process any commands + if ( l_gobit ) + { + TRACDCOMP( g_trac_initsvc, + "gobit turned on, istep=0x%x, substep=0x%x", + l_nextIStep, + l_nextSubstep ); + + // look up istep+substep + l_pistep = findTaskInfo( l_nextIStep, + l_nextSubstep ); + if ( l_pistep == NULL ) + { + // no istep TaskInfo returned, update status & drop to end. + TRACFCOMP( g_trac_initsvc, + "Invalid IStep 0x%x / substep 0x%x, try again.", + l_nextIStep, + l_nextSubstep ); + SPLESSSTS::write( false, + true, + l_nextIStep, + l_nextSubstep, + SPLESS_TASKRC_INVALID_ISTEP, + 0 ); + } + else + { + // set running bit, fill in istep and substep + SPLESSSTS::write( true, // set running bit + true, // ready bit + l_nextIStep, // running istep + l_nextSubstep, // running substep + 0, // task status (=0) + 0 ); // istep status(=0) + + /** + * @todo placeholder - set progress code before starting + * This will not be finalized until the progress code driver + * is designed and implemented. + */ + l_progresscode = ( (l_nextIStep<<16) | l_nextSubstep ); + InitService::getTheInstance().setProgressCode( l_progresscode ); + + // launch the istep + TRACDCOMP( g_trac_initsvc, + "execute Istep=0x%x / Substep=0x%x", + l_nextIStep, + l_nextSubStep ); + + // clear status, etc for the next istep + l_taskStatus = 0; + l_istepStatus = 0; + l_args.clear(); + + // launch the istep + l_errl = InitService::getTheInstance().executeFn( l_pistep, + &l_args ); + // filter errors returning from executeFn + if ( l_errl ) + { + // handle an errorlog from the parent. This means the + // launch failed, set the task Status to Bad. + // no need to process child info, thus the else. + // set the taskStatus to LAUNCH_FAIL; this will fall + // out the bottom and be written to SPLESS Status + l_taskStatus = SPLESS_TASKRC_LAUNCH_FAIL; + TRACFCOMP( g_trac_initsvc, + "ERROR 0x%x: function launch FAIL", + l_taskStatus ); + errlCommit( l_errl ); + } + else + { + // process information returned from the IStep. + // make local copies of the info; this has a secondary + // effect of clearing the errorlog pointer inside + // the TaskArgs struct. + l_isteprc = l_args.getReturnCode(); // local copy + l_errl = l_args.getErrorLog(); // local copy + + TRACDCOMP( g_trac_initsvc, + "IStep TaskArgs return 0x%llx, errlog=%p", + l_isteprc, + l_errl ); + + // check for child errorlog + if ( l_errl ) + { + // tell the user that the IStep returned an errorlog + l_taskStatus = SPLESS_TASKRC_RETURNED_ERRLOG; + // go ahead and commit the child errorlog + errlCommit( l_errl); + } + + // massage the return code from the IStep - + // If the istep did not set an errorcode, + // then we report 0 + if ( l_isteprc == TASKARGS_UNDEFINED64 ) + { + l_istepStatus = 0; + } + else + { + // truncate IStep return status to 16 bits. + l_isteprc &= 0x000000000000ffff; + l_istepStatus = static_cast<uint16_t>(l_isteprc); + } + + } // end else parent errlog + + // l_taskStatus and l_istepStatus should be set correctly now, + // send it to the user. + // clear runningbit, report status + TRACDCOMP( g_trac_initsvc, + "Write IStep Status: istep=0x%x, substep=0x%x, taskstatus=0x%x, istepstatus=0x%x", + l_nextIStep, + l_nextSubstep, + l_taskStatus, + l_istepStatus ); + SPLESSSTS::write( false, // clear running bit + true, // ready bit + l_nextIStep, // running istep + l_nextSubstep, // running substep + l_taskStatus, // task status + l_istepStatus // istepStatus + ); + + SPLESSCMD::setgobit( false ); // clear gobit + } // end else l_pistep + } // endif gobit + + // sleep, and wait for user to give us something else to do. + nanosleep( SINGLESTEP_PAUSE_S, SINGLESTEP_PAUSE_NS ); + } // endwhile + + + // @note + // Fell out of loop, clear sts reg and turn off readybit + // Currently this will never be reached. Later there may be + // a reason to break out of the loop, if this happens we want to + // disable the ready bit so the user knows. + SPLESSSTS::write( false, + false, + 0, + 0, + SPLESS_TASKRC_TERMINATED, + 0 + ); + + // all errorlogs should have been committed in the loop, we should + // not have any errorlogs still set. + if ( l_errl ) + { + // if we do then commit it and stop here. + errlCommit( l_errl ); + assert(0); + } + +} - for ( nextIStep=0; - nextIStep<INITSERVICE::MAX_ISTEPS; - nextIStep++ ) + +void IStepDispatcher::runAllISteps( void * io_ptr ) const +{ + errlHndl_t l_errl = NULL; + uint16_t l_IStep = 0; + uint16_t l_SubStep = 0; + const TaskInfo *l_pistep = NULL; + TaskArgs::TaskArgs l_args; + uint64_t l_progresscode = 0; + uint64_t l_isteprc = 0; + + for ( l_IStep=0; + l_IStep<INITSERVICE::MAX_ISTEPS; + l_IStep++ ) { - pistep = &(g_isteps[nextIStep]); - if ( pistep->taskflags.task_type == END_TASK_LIST ) + for ( l_SubStep=0; + l_SubStep < INITSERVICE::MAX_SUBSTEPS; + l_SubStep++) { TRACDCOMP( g_trac_initsvc, - "End of IStep list.\n" ); - break; - } + "Find IStep=%d, SubStep=%d", + l_IStep, + l_SubStep ); + l_pistep = findTaskInfo( l_IStep, + l_SubStep ); + if ( l_pistep == NULL ) + { - args.clear(); // clear the args struct for the next istep - errl = InitService::getTheInstance().executeFn( pistep, - &args ); + TRACDCOMP( g_trac_initsvc, + "End of ISubStep list." ); + break; // break out of inner for loop + } - // handle an errorlog from the parent, if it exists - InitService::getTheInstance().reportError( errl ); + // @todo placeholder until progress codes are defined and + // progress code driver is implemented. + l_progresscode = ( (l_IStep<<16) | l_SubStep ); + InitService::getTheInstance().setProgressCode( l_progresscode ); - /** - * @todo call getCanContinueProcedure when we have some ISteps that - * require them. - */ + l_args.clear(); - if ( args.getReturnCode() != TASKARGS_UNDEFINED64 ) - { TRACFCOMP( g_trac_initsvc, - ERR_MRK "IStep TaskArgs returned 0x%llx, errlog=%p", - args.getReturnCode(), - args.getErrorLog() - ); + "Run IStep 0x%x / Substep 0x%x", + l_IStep, + l_SubStep ); + + l_errl = InitService::getTheInstance().executeFn( l_pistep, + &l_args ); + if ( l_errl ) + { + // Handle an errorlog from the parent, if it exists + // an error here is probably fatal, it means we can't + // launch the task or it doesn't exist. In either + // case we should exit the loop. + // Can't commit the errorlog here because we are using it + // as a flag to exit the loop. + break; + } + + // make local copies of the status returned from the istep. + // note that this also removes the errorlog from the TaskArgs + // struct - it is read-once. See taskargs.H for details + l_isteprc = l_args.getReturnCode(); + l_errl = l_args.getErrorLog(); + + TRACDCOMP( g_trac_initsvc, + "IStep TaskArgs returned 0x%llx, errlog=%p", + l_isteprc, + l_errl ); + if ( l_errl ) + { + // if we have an errorlog, break out of the inner loop + // and handle it. + break; + } + else + { + // Check child results for a valid nonzero return code. + // If we have one, and no errorlog, then we create an + // errorlog here. + if ( ( l_isteprc != TASKARGS_UNDEFINED64 ) + && ( l_isteprc != 0 ) + ) + { + TRACFCOMP( g_trac_initsvc, + "istep returned 0x%llx, no errlog", + l_isteprc ); + + /*@ errorlog tag + * @errortype ERRL_SEV_CRITICAL_SYS_TERM + * @moduleid see task list + * @reasoncode ISTEP_FAILED_NO_ERRLOG + * @userdata1 returncode from istep + * @userdata2 0 + * + * @devdesc The Istep returned with an error, + * but there was no errorlog posted + * from the IStep. + */ + l_errl = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_CRITICAL_SYS_TERM, + l_pistep->taskflags.module_id, + INITSERVICE::ISTEP_FAILED_NO_ERRLOG, + l_isteprc, + 0 ); + // drop out of inner loop with errlog set. + break; + } // end if ( ) + } + } // endfor l_SubStep + + if ( l_errl ) + { + // something left an error log in the inner loop, breakout + break; } + } // endfor l_IStep - // report an error from the child, if it exists. - reportIStepErrorLog( pistep, args.getErrorLog() ); - } // endfor + /** + * @todo detect Can-Continue condition here and reset/rerun ISteps. + * For now this is pushed to a later sprint. + */ - TRACFCOMP( g_trac_initsvc, - EXIT_MRK "IStepDispatcher finished."); + // Post any errorlogs. If one exists, stop here. Otherwise, return + // to caller. + if ( l_errl ) + { + TRACFCOMP( g_trac_initsvc, + "ERROR: istep=0x%x, substep=0x%x, committing errorlog %p", + l_IStep, + l_SubStep, + l_errl ); + errlCommit( l_errl ); + assert( 0 ); + } } -/** - * @note IStep Mode is hardwired to false for now - * - * @todo revisit this when PNOR Driver is implemented - */ -bool IStepDispatcher::getIStepMode( ) const -{ - - return false; -} - - bool IStepDispatcher::getCanContinueProcedure( const TaskInfo &i_failingIStep, errlHndl_t &i_failingError, @@ -159,11 +567,21 @@ bool IStepDispatcher::getCanContinueProcedure( const TaskInfo &i_failingIStep, return false; } -void IStepDispatcher::reportIStepErrorLog( const TaskInfo *i_failingIStep, - errlHndl_t io_errl ) + +IStepDispatcher& IStepDispatcher::getTheInstance() +{ + return Singleton<IStepDispatcher>::instance(); +} + + +IStepDispatcher::IStepDispatcher() { + initIStepMode(); // set up internal flag - InitService::getTheInstance().reportError( io_errl ); } + +IStepDispatcher::~IStepDispatcher() +{ } + } // namespace |