/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* $Source: src/sbefw/core/sbemain.C $ */ /* */ /* OpenPOWER sbe Project */ /* */ /* Contributors Listed Below - COPYRIGHT 2015,2018 */ /* [+] 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 */ /* @file: ppe/sbe/sbefw/sbemain.C * * @brief This file does the following * - SBE Application Main entry point * - PK initialization * - Thread initialization * - Semaphore initialization * - IRQ setup * - Scheduling of the threads and * - Starting of the control loop code flow * */ #include "sbeexeintf.H" #include "sbetrace.H" #include "sberegaccess.H" #include "sbestates.H" #include "fapi2.H" // For target init #include "sbeutil.H" // For getting SBE_TO_NEST_FREQ_FACTOR #include "sbeglobals.H" #include "p9_misc_scom_addresses.H" // Max defines for Semaphores static uint32_t MAX_SEMAPHORE_COUNT = 3; // As periodic timer expire in 24 hours count should never // be more than 1. static uint32_t MAX_PERIODIC_TIMER_SEMAPHORE_COUNT = 1; extern "C" { // These variables are declared in linker script to keep track of // global constructor pointer functions and sbss section. extern void (*ctor_start_address)() __attribute__ ((section (".rodata"))); extern void (*ctor_end_address)() __attribute__ ((section (".rodata"))); extern uint64_t _sbss_start __attribute__ ((section (".sbss"))); extern uint64_t _sbss_end __attribute__ ((section (".sbss"))); // This function will be used to do any C++ handling required before doing // any main job. Call to this function should get generated by // compiler. // TODO via RTC 152070 // We are also initialising sbss section to zero this function. // Though it does not do any harm as of now, it is better if we use loader // or linker script to zero init sbss section. This way we will be future // garded if pk boot uses some static/global data initialised to // false in future. void __eabi() { do { SBE_GLOBAL->isHreset = SBE::isHreset(); if (SBE_GLOBAL->isHreset) { // skip constructors break; } // Initialise sbss section uint64_t *startAddr = &_sbss_start; while ( startAddr != &_sbss_end ) { *startAddr = 0; startAddr++; } // Call global constructors void(**ctors)() = &ctor_start_address; while( ctors != &ctor_end_address) { (*ctors)(); ctors++; } } while (false); } } // end extern "C" //////////////////////////////////////////////////////////////// // @brief sbeInitSems - Create the necessary semaphores // // @return PK_OK - Success // PK_INVALID_SEMAPHORE_AT_CREATE - Invalid PkSemaphore // PK_INVALID_ARGUMENT_SEMAPHORE - max_count is non-zero // and less than the initial_count //////////////////////////////////////////////////////////////// uint32_t sbeInitSems(void) { SBE_ENTER("sbeInitSems"); int l_rc = PK_OK; do { l_rc = pk_semaphore_create(&SBE_GLOBAL->sbeSemCmdRecv, 0, MAX_SEMAPHORE_COUNT); if (l_rc) { break; } l_rc = pk_semaphore_create(&SBE_GLOBAL->sbeSemCmdProcess, 0, MAX_SEMAPHORE_COUNT); if (l_rc) { break; } l_rc = pk_semaphore_create(&SBE_GLOBAL->sbeSemAsyncProcess, 0, MAX_PERIODIC_TIMER_SEMAPHORE_COUNT); if (l_rc) { break; } } while (false); if (l_rc) { SBE_ERROR ("pk_semaphore_create, rc=[%d]", l_rc); } return l_rc; } //////////////////////////////////////////////////////////////// // @brief createAndResumeThreadHelper // - Create and resume the given thread // // @param[in/out] io_thread A pointer to an PkThread structure to initialize // @param[in] i_thread_routine The subroutine that implements the thread // @param[in/out] io_arg Private data to be passed as the argument to the // thread routine when it begins execution // @param[in] i_stack The stack space of the thread // @param[in] i_stack_size The size of the stack in bytes // @param[in] i_priority The initial priority of the thread // // @return PK_OK Successfully created and resumed the thread // // @return PK_INVALID_THREAD_AT_CREATE io_thread is null // @return PK_INVALID_ARGUMENT_THREAD1 i_thread_routine is null // @return PK_INVALID_ARGUMENT_THREAD2 i_priority is invalid // @return PK_INVALID_ARGUMENT_THREAD3 the stack area wraps around // the end of memory. // @return PK_STACK_OVERFLOW The stack area at thread creation // is smaller than the min safe size // @return PK_INVALID_THREAD_AT_RESUME1 io_thread is null (unlikely) // @return PK_INVALID_THREAD_AT_RESUME2 The thread is not active, // i.e. has completed or been deleted, // @return PK_PRIORITY_IN_USE_AT_RESUME Another thread is already // mapped at the priority of the thread //////////////////////////////////////////////////////////////// uint32_t createAndResumeThreadHelper(PkThread *io_pThread, PkThreadRoutine i_thread_routine, void *io_pArg, PkAddress i_stack, size_t i_stack_size, sbeThreadPriorities i_priority) { int l_rc = PK_OK; // Thread creation l_rc = pk_thread_create(io_pThread, i_thread_routine, io_pArg, i_stack, i_stack_size, (PkThreadPriority)i_priority); if(l_rc == PK_OK) { // resume the thread once created l_rc = pk_thread_resume(io_pThread); } // Check for errors creating or resuming the thread if(l_rc != PK_OK) { SBE_ERROR ("Failure creating/resuming thread, rc=[%d]", l_rc); } return l_rc; } //////////////////////////////////////////////////////////////// // @brief sbeInitThreads // Create the resume all the firmware threads // // @return See createAndResumeThreadHelper for more details //////////////////////////////////////////////////////////////// int sbeInitThreads(void) { // Locals uint32_t l_rc = PK_OK; do { // Initialize Command receiver thread l_rc = createAndResumeThreadHelper(&SBE_GLOBAL->sbeCommandReceiver_thread, sbeCommandReceiver_routine, (void *)0, (PkAddress)sbeCommandReceiver_stack, SBE_THREAD_CMD_RECV_STACK_SIZE, THREAD_PRIORITY_5); if (l_rc) { break; } // Initialize Synchronous Command Processor thread l_rc = createAndResumeThreadHelper(&SBE_GLOBAL->sbeSyncCommandProcessor_thread, sbeSyncCommandProcessor_routine, (void *)0, (PkAddress)sbeSyncCommandProcessor_stack, SBE_THREAD_SYNC_CMD_PROC_STACK_SIZE, THREAD_PRIORITY_6); if (l_rc) { break; } // Initialize Asynchronous Command Processor thread l_rc = createAndResumeThreadHelper(&SBE_GLOBAL->sbeAsyncCommandProcessor_thread, sbeAsyncCommandProcessor_routine, (void *)0, (PkAddress)sbeAsyncCommandProcessor_stack, SBE_THREAD_ASYNC_CMD_PROC_STACK_SIZE, THREAD_PRIORITY_7); if (l_rc) { break; } } while (false); // If there are any errors initializing the threads if( l_rc ) { SBE_ERROR ("Error Initializing a thread, rc=[%d]", l_rc); } return l_rc; } //////////////////////////////////////////////////////////////// // @brief - main : SBE Application main //////////////////////////////////////////////////////////////// uint32_t main(int argc, char **argv) { #define SBE_FUNC "main " SBE_ENTER(SBE_FUNC); int l_rc = 0; // backup i2c mode register uint32_t reg_address = PU_MODE_REGISTER_B; PPE_LVD( reg_address, SBE_GLOBAL->i2cModeRegister); // @TODO via RTC : 128818 // Explore on reclaiming the stack // used by this Initialization code do { // initializes kernel data - // stack, threads, timebase, timers, etc. l_rc = pk_initialize((PkAddress)sbe_Kernel_NCInt_stack, SBE_NONCRITICAL_STACK_SIZE, 0, SBE_GLOBAL->sbefreq ); if (l_rc) { break; } SBE_INFO("Completed PK init"); // Initialize the semaphores l_rc = sbeInitSems(); if (l_rc) { break; } // Initialize SBE control loop threads l_rc = sbeInitThreads(); if (l_rc) { break; } // Setup SBE PPE IRQs l_rc = sbeIRQSetup(); if (l_rc) { break; } if (!SBE_GLOBAL->isHreset) { // TODO via RTC 126146. // Check if we should call plat_TargetsInit in some other thread. // We may want to keep only PK init in main and can move // plat init to some other thread. Only if this is required by more // than one thread and there can be some race condition, we will // keep it here before starting other threads. fapi2::ReturnCode fapiRc = fapi2::plat_TargetsInit(); if( fapiRc != fapi2::FAPI2_RC_SUCCESS ) { SBE_ERROR(SBE_FUNC"plat_TargetsInit failed"); (void)SbeRegAccess::theSbeRegAccess(). stateTransition(SBE_FAILURE_EVENT); // Hard Reset SBE to recover break; } fapiRc = fapi2::plat_AttrInit(); if(fapiRc != fapi2::FAPI2_RC_SUCCESS) { SBE_ERROR(SBE_FUNC"plat_AttrInit failed"); (void)SbeRegAccess::theSbeRegAccess(). stateTransition(SBE_FAILURE_EVENT); // Hard Reset SBE to recover break; } if(SbeRegAccess::theSbeRegAccess().init()) { SBE_ERROR(SBE_FUNC"Failed to initialize SbeRegAccess."); // init failure could mean the below will fail too, but attempt it // anyway (void)SbeRegAccess::theSbeRegAccess().stateTransition( SBE_FAILURE_EVENT); // Hard Reset SBE to recover break; } if(SBE::isSimicsRunning()) { SBE_INFO("SBE is running on simics"); } } // Start running the highest priority thread. // This function never returns pk_start_threads(); } while (false); SBE_EXIT(SBE_FUNC); return l_rc; }