From 17068b4c1991806e5bf1512df406c9cb5e217599 Mon Sep 17 00:00:00 2001 From: "Gerardo E. Cruz-Ortiz" <59618057+astrogeco@users.noreply.github.com> Date: Wed, 7 Apr 2021 21:19:29 -0400 Subject: [PATCH] CaelumReview-CFE40, Add ES files --- .gitignore | 8 + modules/core_api/fsw/inc/cfe_es.h | 1635 +++++++++++++ .../core_api/fsw/inc/cfe_es_api_typedefs.h | 193 ++ .../core_api/fsw/inc/cfe_es_core_internal.h | 136 ++ .../core_api/fsw/inc/cfe_es_extern_typedefs.h | 573 +++++ .../fsw/inc/cfe_es_erlog_typedef.h | 98 + .../fsw/inc/cfe_es_perfdata_typedef.h | 75 + .../fsw/inc/cfe_es_resetdata_typedef.h | 99 + modules/es/CMakeLists.txt | 39 + modules/es/fsw/inc/cfe_es_events.h | 1451 +++++++++++ modules/es/fsw/inc/cfe_es_msg.h | 1570 ++++++++++++ modules/es/fsw/src/cfe_es_api.c | 2136 +++++++++++++++++ modules/es/fsw/src/cfe_es_apps.c | 1819 ++++++++++++++ modules/es/fsw/src/cfe_es_apps.h | 303 +++ modules/es/fsw/src/cfe_es_backgroundtask.c | 230 ++ modules/es/fsw/src/cfe_es_cds.c | 931 +++++++ modules/es/fsw/src/cfe_es_cds.h | 641 +++++ modules/es/fsw/src/cfe_es_cds_mempool.c | 371 +++ modules/es/fsw/src/cfe_es_cds_mempool.h | 78 + modules/es/fsw/src/cfe_es_erlog.c | 430 ++++ modules/es/fsw/src/cfe_es_generic_pool.c | 676 ++++++ modules/es/fsw/src/cfe_es_generic_pool.h | 283 +++ modules/es/fsw/src/cfe_es_global.h | 239 ++ modules/es/fsw/src/cfe_es_log.h | 361 +++ modules/es/fsw/src/cfe_es_mempool.c | 672 ++++++ modules/es/fsw/src/cfe_es_mempool.h | 200 ++ modules/es/fsw/src/cfe_es_module_all.h | 57 + modules/es/fsw/src/cfe_es_objtab.c | 137 ++ modules/es/fsw/src/cfe_es_perf.c | 724 ++++++ modules/es/fsw/src/cfe_es_perf.h | 135 ++ modules/es/fsw/src/cfe_es_resource.c | 408 ++++ modules/es/fsw/src/cfe_es_resource.h | 552 +++++ modules/es/fsw/src/cfe_es_start.c | 954 ++++++++ modules/es/fsw/src/cfe_es_start.h | 94 + modules/es/fsw/src/cfe_es_syslog.c | 556 +++++ modules/es/fsw/src/cfe_es_task.c | 2077 ++++++++++++++++ modules/es/fsw/src/cfe_es_task.h | 129 + modules/es/fsw/src/cfe_es_verify.h | 337 +++ 38 files changed, 21407 insertions(+) create mode 100644 modules/core_api/fsw/inc/cfe_es.h create mode 100644 modules/core_api/fsw/inc/cfe_es_api_typedefs.h create mode 100644 modules/core_api/fsw/inc/cfe_es_core_internal.h create mode 100644 modules/core_api/fsw/inc/cfe_es_extern_typedefs.h create mode 100644 modules/core_private/fsw/inc/cfe_es_erlog_typedef.h create mode 100644 modules/core_private/fsw/inc/cfe_es_perfdata_typedef.h create mode 100644 modules/core_private/fsw/inc/cfe_es_resetdata_typedef.h create mode 100644 modules/es/CMakeLists.txt create mode 100644 modules/es/fsw/inc/cfe_es_events.h create mode 100644 modules/es/fsw/inc/cfe_es_msg.h create mode 100644 modules/es/fsw/src/cfe_es_api.c create mode 100644 modules/es/fsw/src/cfe_es_apps.c create mode 100644 modules/es/fsw/src/cfe_es_apps.h create mode 100644 modules/es/fsw/src/cfe_es_backgroundtask.c create mode 100644 modules/es/fsw/src/cfe_es_cds.c create mode 100644 modules/es/fsw/src/cfe_es_cds.h create mode 100644 modules/es/fsw/src/cfe_es_cds_mempool.c create mode 100644 modules/es/fsw/src/cfe_es_cds_mempool.h create mode 100644 modules/es/fsw/src/cfe_es_erlog.c create mode 100644 modules/es/fsw/src/cfe_es_generic_pool.c create mode 100644 modules/es/fsw/src/cfe_es_generic_pool.h create mode 100644 modules/es/fsw/src/cfe_es_global.h create mode 100644 modules/es/fsw/src/cfe_es_log.h create mode 100644 modules/es/fsw/src/cfe_es_mempool.c create mode 100644 modules/es/fsw/src/cfe_es_mempool.h create mode 100644 modules/es/fsw/src/cfe_es_module_all.h create mode 100644 modules/es/fsw/src/cfe_es_objtab.c create mode 100644 modules/es/fsw/src/cfe_es_perf.c create mode 100644 modules/es/fsw/src/cfe_es_perf.h create mode 100644 modules/es/fsw/src/cfe_es_resource.c create mode 100644 modules/es/fsw/src/cfe_es_resource.h create mode 100644 modules/es/fsw/src/cfe_es_start.c create mode 100644 modules/es/fsw/src/cfe_es_start.h create mode 100644 modules/es/fsw/src/cfe_es_syslog.c create mode 100644 modules/es/fsw/src/cfe_es_task.c create mode 100644 modules/es/fsw/src/cfe_es_task.h create mode 100644 modules/es/fsw/src/cfe_es_verify.h diff --git a/.gitignore b/.gitignore index 5a17b9313..637b047dc 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,11 @@ !modules/core_api/fsw/inc/cfe_endian.h !modules/core_api/fsw/inc/cfe_error.h !modules/core_api/fsw/inc/cfe_version.h + +# ES +!modules/core_api/fsw/inc/cfe_es* +!modules/core_private/fsw/inc/cfe_es* + +!modules/es/fsw/** + +!modules/es/CMakeLists.txt diff --git a/modules/core_api/fsw/inc/cfe_es.h b/modules/core_api/fsw/inc/cfe_es.h new file mode 100644 index 000000000..3af8c2f6d --- /dev/null +++ b/modules/core_api/fsw/inc/cfe_es.h @@ -0,0 +1,1635 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/** + * @file + * + * Purpose: + * Unit specification for Executive Services library functions and macros. + * + * References: + * Flight Software Branch C Coding Standard Version 1.0a + * cFE Flight Software Application Developers Guide + * + * Notes: + * + */ + +#ifndef CFE_ES_H +#define CFE_ES_H + +/* +** Includes +*/ +#include "common_types.h" +#include "cfe_error.h" +#include "cfe_es_api_typedefs.h" + +/* +** The OS_PRINTF macro may be defined by OSAL to enable +** printf-style argument checking. If using a version of OSAL +** that does not define this then define it as a no-op. +*/ +#ifndef OS_PRINTF +#define OS_PRINTF(m, n) +#endif + +/* +** Macro Definitions +*/ + +#define CFE_ES_DBIT(x) (1L << (x)) /* Places a one at bit positions 0 thru 31 */ +#define CFE_ES_DTEST(i, x) (((i)&CFE_ES_DBIT(x)) != 0) /* true iff bit x of i is set */ +#define CFE_ES_TEST_LONG_MASK(m, s) \ + (CFE_ES_DTEST(m[(s) / 32], (s) % 32)) /* Test a bit within an array of 32-bit integers. */ + +/*****************************************************************************/ +/* +** Exported Functions +*/ + +/*****************************************************************************/ + +/** @defgroup CFEAPIESResourceID cFE Resource ID APIs + * @{ + */ + +/** + * @brief Obtain an index value correlating to an ES Application ID + * + * This calculates a zero based integer value that may be used for indexing + * into a local resource table/array. + * + * Index values are only guaranteed to be unique for resources of the same + * type. For instance, the indices corresponding to two [valid] application + * IDs will never overlap, but the index of an application and a library ID + * may be the same. Furthermore, indices may be reused if a resource is + * deleted and re-created. + * + * @note There is no inverse of this function - indices cannot be converted + * back to the original AppID value. The caller should retain the original ID + * for future use. + * + * @param[in] AppID Application ID to convert + * @param[out] Idx Buffer where the calculated index will be stored + * + * @return Execution status, see @ref CFEReturnCodes + * @retval #CFE_SUCCESS @copybrief CFE_SUCCESS + * @retval #CFE_ES_ERR_RESOURCEID_NOT_VALID @copybrief CFE_ES_ERR_RESOURCEID_NOT_VALID + */ +CFE_Status_t CFE_ES_AppID_ToIndex(CFE_ES_AppId_t AppID, uint32 *Idx); + +/** + * @brief Obtain an index value correlating to an ES Library ID + * + * This calculates a zero based integer value that may be used for indexing + * into a local resource table/array. + * + * Index values are only guaranteed to be unique for resources of the same + * type. For instance, the indices corresponding to two [valid] Library + * IDs will never overlap, but the index of an Library and a library ID + * may be the same. Furthermore, indices may be reused if a resource is + * deleted and re-created. + * + * @note There is no inverse of this function - indices cannot be converted + * back to the original LibID value. The caller should retain the original ID + * for future use. + * + * @param[in] LibID Library ID to convert + * @param[out] Idx Buffer where the calculated index will be stored + * + * @return Execution status, see @ref CFEReturnCodes + * @retval #CFE_SUCCESS @copybrief CFE_SUCCESS + * @retval #CFE_ES_ERR_RESOURCEID_NOT_VALID @copybrief CFE_ES_ERR_RESOURCEID_NOT_VALID + */ +int32 CFE_ES_LibID_ToIndex(CFE_ES_LibId_t LibID, uint32 *Idx); + +/** + * @brief Obtain an index value correlating to an ES Task ID + * + * This calculates a zero based integer value that may be used for indexing + * into a local resource table/array. + * + * Index values are only guaranteed to be unique for resources of the same + * type. For instance, the indices corresponding to two [valid] Task + * IDs will never overlap, but the index of an Task and a library ID + * may be the same. Furthermore, indices may be reused if a resource is + * deleted and re-created. + * + * @note There is no inverse of this function - indices cannot be converted + * back to the original TaskID value. The caller should retain the original ID + * for future use. + * + * @param[in] TaskID Task ID to convert + * @param[out] Idx Buffer where the calculated index will be stored + * + * @return Execution status, see @ref CFEReturnCodes + * @retval #CFE_SUCCESS @copybrief CFE_SUCCESS + * @retval #CFE_ES_ERR_RESOURCEID_NOT_VALID @copybrief CFE_ES_ERR_RESOURCEID_NOT_VALID + */ +CFE_Status_t CFE_ES_TaskID_ToIndex(CFE_ES_TaskId_t TaskID, uint32 *Idx); + +/** + * @brief Obtain an index value correlating to an ES Counter ID + * + * This calculates a zero based integer value that may be used for indexing + * into a local resource table/array. + * + * Index values are only guaranteed to be unique for resources of the same + * type. For instance, the indices corresponding to two [valid] Counter + * IDs will never overlap, but the index of an Counter and a library ID + * may be the same. Furthermore, indices may be reused if a resource is + * deleted and re-created. + * + * @note There is no inverse of this function - indices cannot be converted + * back to the original CounterID value. The caller should retain the original ID + * for future use. + * + * @param[in] CounterID Counter ID to convert + * @param[out] Idx Buffer where the calculated index will be stored + * + * @return Execution status, see @ref CFEReturnCodes + * @retval #CFE_SUCCESS @copybrief CFE_SUCCESS + * @retval #CFE_ES_ERR_RESOURCEID_NOT_VALID @copybrief CFE_ES_ERR_RESOURCEID_NOT_VALID + */ +CFE_Status_t CFE_ES_CounterID_ToIndex(CFE_ES_CounterId_t CounterID, uint32 *Idx); + +/** @} */ + +/*****************************************************************************/ + +/** @defgroup CFEAPIESEntryExit cFE Entry/Exit APIs + * @{ + */ + +/*****************************************************************************/ +/** +** \brief cFE Main Entry Point used by Board Support Package to start cFE +** +** \par Description +** cFE main entry point. This is the entry point into the cFE software. +** It is called only by the Board Support Package software. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[in] StartType Identifies whether this was a #CFE_PSP_RST_TYPE_POWERON or #CFE_PSP_RST_TYPE_PROCESSOR. +** +** \param[in] StartSubtype Specifies, in more detail, what caused the \c StartType identified above. +** See #CFE_PSP_RST_SUBTYPE_POWER_CYCLE for possible examples. +** +** \param[in] ModeId Identifies the source of the Boot as determined by the BSP. +** +** \param[in] StartFilePath Identifies the startup file to use to initialize the cFE apps. +** +** \sa #CFE_ES_ResetCFE +** +******************************************************************************/ +void CFE_ES_Main(uint32 StartType, uint32 StartSubtype, uint32 ModeId, const char *StartFilePath); + +/*****************************************************************************/ +/** +** \brief Reset the cFE Core and all cFE Applications +** +** \par Description +** This API causes an immediate reset of the cFE Kernel and all cFE Applications. +** The caller can specify whether the reset should clear all memory (#CFE_PSP_RST_TYPE_POWERON) +** or try to retain volatile memory areas (#CFE_PSP_RST_TYPE_PROCESSOR). +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[in] ResetType Identifies the type of reset desired. Allowable settings are: +** \arg #CFE_PSP_RST_TYPE_POWERON - Causes all memory to be cleared +** \arg #CFE_PSP_RST_TYPE_PROCESSOR - Attempts to retain volatile disk, critical data store +** and user reserved memory. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** \retval #CFE_ES_NOT_IMPLEMENTED \copybrief CFE_ES_NOT_IMPLEMENTED +** +** \sa #CFE_ES_Main +** +******************************************************************************/ +CFE_Status_t CFE_ES_ResetCFE(uint32 ResetType); +/**@}*/ + +/** @defgroup CFEAPIESAppControl cFE Application Control APIs + * @{ + */ + +/*****************************************************************************/ +/** +** \brief Restart a single cFE Application +** +** \par Description +** This API causes a cFE Application to be unloaded and restarted +** from the same file name as the last start. +** +** \par Assumptions, External Events, and Notes: +** The filename is checked for existance prior to load. A missing file +** will be reported and the reload operation will be aborted prior +** to unloading the app. +** +** Goes through the standard CFE_ES_CleanUpApp which unloads, +** then attempts a load using the original file name. +** +** In the event that an application cannot be reloaded due to a +** missing file or any other load issue, the application may no longer be +** restarted or reloaded when given a valid load file (the app has been +** deleted and no longer exists). To recover, the application +** may be started by loading the application via the ES_STARTAPP +** command (#CFE_ES_START_APP_CC). +** +** \param[in] AppID Identifies the application to be reset. +** +** \return Execution status, see \ref CFEReturnCodes +** +** \sa #CFE_ES_ReloadApp, #CFE_ES_DeleteApp +** +******************************************************************************/ +CFE_Status_t CFE_ES_RestartApp(CFE_ES_AppId_t AppID); + +/*****************************************************************************/ +/** +** \brief Reload a single cFE Application +** +** \par Description +** This API causes a cFE Application to be stopped and restarted from +** the specified file. +** +** \par Assumptions, External Events, and Notes: +** The filename is checked for existance prior to load. A missing file +** will be reported and the reload operation will be aborted prior +** to unloading the app. +** +** Goes through the standard CFE_ES_CleanUpApp which unloads, +** then attempts a load using the specified file name. +** +** In the event that an application cannot be reloaded due to +** a corrupt file, the application may no longer be reloaded when given a valid +** load file (it has been deleted and no longer exists). To recover, the +** application may be started by loading the application via the ES_STARTAPP +** command (#CFE_ES_START_APP_CC). +** +** \param[in] AppID Identifies the application to be reset. +** +** \param[in] AppFileName Identifies the new file to start. +** +** \return Execution status, see \ref CFEReturnCodes +** +** \sa #CFE_ES_RestartApp, #CFE_ES_DeleteApp, #CFE_ES_START_APP_CC +** +******************************************************************************/ +CFE_Status_t CFE_ES_ReloadApp(CFE_ES_AppId_t AppID, const char *AppFileName); + +/*****************************************************************************/ +/** +** \brief Delete a cFE Application +** +** \par Description +** This API causes a cFE Application to be stopped deleted. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[in] AppID Identifies the application to be reset. +** +** \return Execution status, see \ref CFEReturnCodes +** +** \sa #CFE_ES_RestartApp, #CFE_ES_ReloadApp +** +******************************************************************************/ +CFE_Status_t CFE_ES_DeleteApp(CFE_ES_AppId_t AppID); +/**@}*/ + +/** @defgroup CFEAPIESAppBehavior cFE Application Behavior APIs + * @{ + */ + +/*****************************************************************************/ +/** +** \brief Exit a cFE Application +** +** \par Description +** This API is the "Exit Point" for the cFE application +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[in] ExitStatus Acceptable values are: \arg #CFE_ES_RunStatus_APP_EXIT - \copybrief CFE_ES_RunStatus_APP_EXIT +** \arg #CFE_ES_RunStatus_APP_ERROR - \copybrief CFE_ES_RunStatus_APP_ERROR +** \arg #CFE_ES_RunStatus_CORE_APP_INIT_ERROR - \copybrief CFE_ES_RunStatus_CORE_APP_INIT_ERROR +** \arg #CFE_ES_RunStatus_CORE_APP_RUNTIME_ERROR - \copybrief CFE_ES_RunStatus_CORE_APP_RUNTIME_ERROR +** +** +** \sa #CFE_ES_RunLoop +** +******************************************************************************/ +void CFE_ES_ExitApp(uint32 ExitStatus); + +/*****************************************************************************/ +/** +** \brief Check for Exit, Restart, or Reload commands +** +** \par Description +** This is the API that allows an app to check for exit requests from +** the system. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[in] ExitStatus A pointer to a variable containing the Application's +** desired run status. Acceptable values are: +** \arg #CFE_ES_RunStatus_APP_RUN - \copybrief CFE_ES_RunStatus_APP_RUN +** \arg #CFE_ES_RunStatus_APP_EXIT - \copybrief CFE_ES_RunStatus_APP_EXIT +** \arg #CFE_ES_RunStatus_APP_ERROR - \copybrief CFE_ES_RunStatus_APP_ERROR +** +** \return Boolean indicating application should continue running +** \retval true Application should continue running +** \retval false Application should not continue running +** +** \sa #CFE_ES_ExitApp +** +******************************************************************************/ +bool CFE_ES_RunLoop(uint32 *ExitStatus); + +/*****************************************************************************/ +/** +** \brief Allow an Application to Wait for a minimum global system state +** +** \par Description +** This is the API that allows an app to wait for the rest of the apps +** to complete a given stage of initialization before continuing. +** +** This gives finer grained control than #CFE_ES_WaitForStartupSync +** +** \par Assumptions, External Events, and Notes: +** This API assumes that the caller has also been initialized sufficiently +** to satisfy the global system state it is waiting for, and the apps own +** state will be updated accordingly. +** +** \param[in] TimeOutMilliseconds The timeout value in Milliseconds. +** This parameter must be at least 1000. Lower values +** will be rounded up. There is not an option to +** wait indefinitely to avoid hanging a critical +** application because a non-critical app did not start. +** +** \param[in] MinSystemState Determine the state of the App +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS State successfully achieved +** \retval #CFE_ES_OPERATION_TIMED_OUT Timeout was reached +** +** \sa #CFE_ES_RunLoop +** +******************************************************************************/ +CFE_Status_t CFE_ES_WaitForSystemState(uint32 MinSystemState, uint32 TimeOutMilliseconds); + +/*****************************************************************************/ +/** +** \brief Allow an Application to Wait for the "OPERATIONAL" global system state +** +** \par Description +** This is the API that allows an app to wait for the rest of the apps +** to complete their entire initialization before continuing. It is most +** useful for applications such as Health and Safety or the Scheduler that need +** to wait until applications exist and are running before sending out +** packets to them. +** +** This is a specialized wrapper for CFE_ES_WaitForSystemState for compatibility +** with applications using this API. +** +** \par Assumptions, External Events, and Notes: +** This API should only be called as the last item of an Apps initialization. +** In addition, this API should only be called by an App that is started +** from the ES Startup file. It should not be used by an App that is +** started after the system is running. ( Although it will cause no harm ) +** +** \param[in] TimeOutMilliseconds The timeout value in Milliseconds. +** This parameter must be at least 1000. Lower values +** will be rounded up. There is not an option to +** wait indefinitely to avoid hanging a critical +** application because a non-critical app did not start. +** +** \sa #CFE_ES_RunLoop +** +******************************************************************************/ +void CFE_ES_WaitForStartupSync(uint32 TimeOutMilliseconds); + +/*****************************************************************************/ +/** +** \ingroup CFEAPIESAppBehavior +** \brief Increments the execution counter for the calling task +** +** \par Description +** This routine increments the execution counter that is stored for +** the calling task. It can be called from cFE Application main tasks, child +** tasks, or cFE Core application main tasks. Normally, the call is not +** necessary from a cFE Application, since the CFE_ES_RunLoop call increments +** the counter for the Application. +** +** \par Assumptions, External Events, and Notes: +** NOTE: This API is not needed for Appplications that call the CFE_ES_RunLoop call. +** +** \sa #CFE_ES_RunLoop +** +******************************************************************************/ +void CFE_ES_IncrementTaskCounter(void); +/**@}*/ + +/** @defgroup CFEAPIESInfo cFE Information APIs + * @{ + */ + +/*****************************************************************************/ +/** +** \brief Return the most recent Reset Type +** +** \par Description +** Provides the caller with codes that identifies the type of Reset +** the processor most recently underwent. The caller can also obtain +** information on what caused the reset by supplying a pointer to a +** variable that will be filled with the Reset Sub-Type. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[in, out] ResetSubtypePtr Pointer to \c uint32 type variable in which the Reset Sub-Type will be stored. +** The caller can set this pointer to NULL if the Sub-Type is of no interest. \n +** ResetSubtypePtr If the provided pointer was not \c NULL, the Reset Sub-Type is +** stored at the given address. For a list of possible Sub-Type values, see \link +** #CFE_PSP_RST_SUBTYPE_POWER_CYCLE "Reset Sub-Types" \endlink. +** +** \return Processor reset type +** \retval #CFE_PSP_RST_TYPE_POWERON \copybrief CFE_PSP_RST_TYPE_POWERON +** \retval #CFE_PSP_RST_TYPE_PROCESSOR \copybrief CFE_PSP_RST_TYPE_PROCESSOR +** +** \sa #CFE_ES_GetAppID, #CFE_ES_GetAppIDByName, #CFE_ES_GetAppName, #CFE_ES_GetTaskInfo +** +******************************************************************************/ +int32 CFE_ES_GetResetType(uint32 *ResetSubtypePtr); + +/*****************************************************************************/ +/** +** \brief Get an Application ID for the calling Application +** +** \par Description +** This routine retrieves the cFE Application ID for the calling Application. +** +** \par Assumptions, External Events, and Notes: +** NOTE: \b All tasks associated with the Application would return the same Application ID. +** +** \param[out] AppIdPtr Pointer to variable that is to receive the Application's ID. +** *AppIdPtr will be set to the application ID of the calling Application. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_ERR_RESOURCEID_NOT_VALID \copybrief CFE_ES_ERR_RESOURCEID_NOT_VALID +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_GetResetType, #CFE_ES_GetAppIDByName, #CFE_ES_GetAppName, #CFE_ES_GetTaskInfo +** +******************************************************************************/ +CFE_Status_t CFE_ES_GetAppID(CFE_ES_AppId_t *AppIdPtr); + +/*****************************************************************************/ +/** +** \brief Get the task ID of the calling context +** +** \par Description +** This retrieves the current task context from OSAL +** +** \par Assumptions, External Events, and Notes: +** Applications which desire to call other CFE ES services such as +** CFE_ES_TaskGetInfo() should use this API rather than getting the ID +** from OSAL directly via OS_TaskGetId(). +** +** \param[out] TaskIdPtr Pointer to variable that is to receive the ID. +** Will be set to the ID of the calling task. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_ERR_RESOURCEID_NOT_VALID \copybrief CFE_ES_ERR_RESOURCEID_NOT_VALID +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +******************************************************************************/ +CFE_Status_t CFE_ES_GetTaskID(CFE_ES_TaskId_t *TaskIdPtr); + +/*****************************************************************************/ +/** +** \brief Get an Application ID associated with a specified Application name +** +** \par Description +** This routine retrieves the cFE Application ID associated with a +** specified Application name. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[out] AppIdPtr Pointer to variable that is to receive the Application's ID. +** \param[in] AppName Pointer to null terminated character string containing an Application name. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_ERR_NAME_NOT_FOUND \copybrief CFE_ES_ERR_NAME_NOT_FOUND +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_GetAppID, #CFE_ES_GetAppName, #CFE_ES_GetAppInfo +** +******************************************************************************/ +CFE_Status_t CFE_ES_GetAppIDByName(CFE_ES_AppId_t *AppIdPtr, const char *AppName); + +/*****************************************************************************/ +/** +** \brief Get a Library ID associated with a specified Library name +** +** \par Description +** This routine retrieves the cFE Library ID associated with a +** specified Library name. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[out] LibIdPtr Pointer to variable that is to receive the Library's ID. +** \param[in] LibName Pointer to null terminated character string containing a Library name. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_ERR_NAME_NOT_FOUND \copybrief CFE_ES_ERR_NAME_NOT_FOUND +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_GetLibName +** +******************************************************************************/ +CFE_Status_t CFE_ES_GetLibIDByName(CFE_ES_LibId_t *LibIdPtr, const char *LibName); + +/*****************************************************************************/ +/** +** \brief Get an Application name for a specified Application ID +** +** \par Description +** This routine retrieves the cFE Application name associated with a +** specified Application ID. +** +** \par Assumptions, External Events, and Notes: +** In the case of a failure (#CFE_ES_ERR_RESOURCEID_NOT_VALID), an empty string is returned. +** +** \param[out] AppName Pointer to a character array of at least \c BufferLength in size that will +** be filled with the appropriate Application name. +** +** \param[in] AppId Application ID of Application whose name is being requested. +** +** \param[in] BufferLength The maximum number of characters, including the null terminator, that can be put +** into the \c AppName buffer. This routine will truncate the name to this length, +** if necessary. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_ERR_RESOURCEID_NOT_VALID \copybrief CFE_ES_ERR_RESOURCEID_NOT_VALID +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_GetAppID, #CFE_ES_GetAppIDByName, #CFE_ES_GetAppInfo +** +******************************************************************************/ +CFE_Status_t CFE_ES_GetAppName(char *AppName, CFE_ES_AppId_t AppId, size_t BufferLength); + +/*****************************************************************************/ +/** +** \brief Get a Library name for a specified Library ID +** +** \par Description +** This routine retrieves the cFE Library name associated with a +** specified Library ID. +** +** \par Assumptions, External Events, and Notes: +** In the case of a failure (#CFE_ES_ERR_RESOURCEID_NOT_VALID), an empty string is returned. +** +** \param[out] LibName Pointer to a character array of at least \c BufferLength in size that will +** be filled with the Library name. +** +** \param[in] LibId Library ID of Library whose name is being requested. +** +** \param[in] BufferLength The maximum number of characters, including the null terminator, that can be put +** into the \c LibName buffer. This routine will truncate the name to this length, +** if necessary. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_ERR_RESOURCEID_NOT_VALID \copybrief CFE_ES_ERR_RESOURCEID_NOT_VALID +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_GetLibIDByName +** +******************************************************************************/ +CFE_Status_t CFE_ES_GetLibName(char *LibName, CFE_ES_LibId_t LibId, size_t BufferLength); + +/*****************************************************************************/ +/** +** \brief Get Application Information given a specified App ID +** +** \par Description +** This routine retrieves the information about an App associated with a +** specified App ID. The information includes all of the information ES +** maintains for an application ( documented in the CFE_ES_AppInfo_t type ) +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[out] AppInfo Pointer to a structure that will be filled with +** resource name and memory addresses information. +** \param[in] AppId ID of application to obtain information about +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_ERR_RESOURCEID_NOT_VALID \copybrief CFE_ES_ERR_RESOURCEID_NOT_VALID +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_GetAppID, #CFE_ES_GetAppIDByName, #CFE_ES_GetAppName +** +******************************************************************************/ +CFE_Status_t CFE_ES_GetAppInfo(CFE_ES_AppInfo_t *AppInfo, CFE_ES_AppId_t AppId); + +/*****************************************************************************/ +/** +** \brief Get Task Information given a specified Task ID +** +** \par Description +** This routine retrieves the information about a Task associated with a +** specified Task ID. The information includes Task Name, and Parent/Creator +** Application ID. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[out] TaskInfo Pointer to a \c CFE_ES_TaskInfo_t structure that holds the specific +** task information. *TaskInfo is the filled out \c CFE_ES_TaskInfo_t structure containing +** the Task Name, Parent App Name, Parent App ID among other fields. +** +** \param[in] TaskId Application ID of Application whose name is being requested. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_ERR_RESOURCEID_NOT_VALID \copybrief CFE_ES_ERR_RESOURCEID_NOT_VALID +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_GetTaskID, #CFE_ES_GetTaskIDByName, #CFE_ES_GetTaskName +** +******************************************************************************/ +CFE_Status_t CFE_ES_GetTaskInfo(CFE_ES_TaskInfo_t *TaskInfo, CFE_ES_TaskId_t TaskId); + +/*****************************************************************************/ +/** +** \brief Get Library Information given a specified Resource ID +** +** \par Description +** This routine retrieves the information about a Library +** associated with a specified ID. The information includes all of the +** information ES maintains for this resource type ( documented in +** the CFE_ES_AppInfo_t type ). +** +** This shares the same output structure as CFE_ES_GetAppInfo, such that +** informational commands can be executed against either applications or +** libraries. When applied to a library, the task information in the +** structure will be omitted, as libraries do not have tasks associated. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[out] LibInfo Pointer to a structure that will be filled with +** resource name and memory addresses information. +** \param[in] LibId ID of application to obtain information about +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_ERR_RESOURCEID_NOT_VALID \copybrief CFE_ES_ERR_RESOURCEID_NOT_VALID +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_GetLibIDByName, #CFE_ES_GetLibName +** +******************************************************************************/ +int32 CFE_ES_GetLibInfo(CFE_ES_AppInfo_t *LibInfo, CFE_ES_LibId_t LibId); + +/*****************************************************************************/ +/** +** \brief Get Information given a specified Resource ID +** +** \par Description +** This routine retrieves the information about an Application or Library +** associated with a specified ID. +** +** This is a wrapper API that in turn calls either CFE_ES_GetAppInfo or +** CFE_ES_GetLibInfo if passed an AppId or LibId, respectively. +** +** This allows commands originally targeted to operate on AppIDs to be +** easily ported to operate on either Libraries or Applications, where +** relevant. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[out] ModuleInfo Pointer to a structure that will be filled with +** resource name and memory addresses information. +** \param[in] ResourceId ID of application or library to obtain information about +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_ERR_RESOURCEID_NOT_VALID \copybrief CFE_ES_ERR_RESOURCEID_NOT_VALID +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_GetLibInfo, #CFE_ES_GetAppInfo +** +******************************************************************************/ +int32 CFE_ES_GetModuleInfo(CFE_ES_AppInfo_t *ModuleInfo, CFE_ResourceId_t ResourceId); + +/**@}*/ + +/** @defgroup CFEAPIESChildTask cFE Child Task APIs + * @{ + */ + +/*****************************************************************************/ +/** +** \brief Creates a new task under an existing Application +** +** \par Description +** This routine creates a new task (a separate execution thread) owned by the calling Application. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[in, out] TaskIdPtr A pointer to a variable that will be filled in with the new task's ID. *TaskIdPtr is +** the Task ID of the newly created child task. +** +** \param[in] TaskName A pointer to a string containing the desired name of the new task. +** This can be up to #OS_MAX_API_NAME characters, including the trailing null. +** +** \param[in] FunctionPtr A pointer to the function that will be spawned as a new task. This function +** must have the following signature: uint32 function(void). Input parameters +** for the new task are not supported. +** +** \param[in] StackPtr A pointer to the location where the child task's stack pointer should start. +** NOTE: Not all underlying operating systems support this parameter. +** The CFE_ES_TASK_STACK_ALLOCATE constant may be passed to indicate that the +** stack should be dynamically allocated. +** +** \param[in] StackSize The number of bytes to allocate for the new task's stack. +** +** \param[in] Priority The priority for the new task. Lower numbers are higher priority, with 0 being +** the highest priority. Applications cannot create tasks with a higher priority +** (lower number) than their own priority. +** +** \param[in] Flags Reserved for future expansion. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_ERR_CHILD_TASK_CREATE \copybrief CFE_ES_ERR_CHILD_TASK_CREATE +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_DeleteChildTask, #CFE_ES_ExitChildTask +** +******************************************************************************/ +CFE_Status_t CFE_ES_CreateChildTask(CFE_ES_TaskId_t *TaskIdPtr, const char *TaskName, + CFE_ES_ChildTaskMainFuncPtr_t FunctionPtr, CFE_ES_StackPointer_t StackPtr, + size_t StackSize, CFE_ES_TaskPriority_Atom_t Priority, uint32 Flags); + +/*****************************************************************************/ +/** +** \brief Get a Task ID associated with a specified Task name +** +** \par Description +** This routine retrieves the cFE Task ID associated with a +** specified Task name. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[out] TaskIdPtr Pointer to variable that is to receive the Task's ID. +** \param[in] TaskName Pointer to null terminated character string containing an Task name. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_ERR_NAME_NOT_FOUND \copybrief CFE_ES_ERR_NAME_NOT_FOUND +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_GetTaskName +** +******************************************************************************/ +CFE_Status_t CFE_ES_GetTaskIDByName(CFE_ES_TaskId_t *TaskIdPtr, const char *TaskName); + +/*****************************************************************************/ +/** +** \brief Get a Task name for a specified Task ID +** +** \par Description +** This routine retrieves the cFE Task name associated with a +** specified Task ID. +** +** \par Assumptions, External Events, and Notes: +** In the case of a failure (#CFE_ES_ERR_RESOURCEID_NOT_VALID), an empty string is returned. +** +** \param[out] TaskName Pointer to a character array of at least \c BufferLength in size that will +** be filled with the Task name. +** +** \param[in] TaskId Task ID of Task whose name is being requested. +** +** \param[in] BufferLength The maximum number of characters, including the null terminator, that can be put +** into the \c TaskName buffer. This routine will truncate the name to this length, +** if necessary. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_ERR_RESOURCEID_NOT_VALID \copybrief CFE_ES_ERR_RESOURCEID_NOT_VALID +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_GetTaskIDByName +** +******************************************************************************/ +CFE_Status_t CFE_ES_GetTaskName(char *TaskName, CFE_ES_TaskId_t TaskId, size_t BufferLength); + +/*****************************************************************************/ +/** +** \brief Deletes a task under an existing Application +** +** \par Description +** This routine deletes a task under an Application specified by the \c TaskId obtained +** when the child task was created using the #CFE_ES_CreateChildTask API. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[in] TaskId The task ID previously obtained when the Child Task was created with the +*#CFE_ES_CreateChildTask API. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_NOT_IMPLEMENTED \copybrief CFE_ES_NOT_IMPLEMENTED +** +** \sa #CFE_ES_CreateChildTask, #CFE_ES_ExitChildTask +** +******************************************************************************/ +CFE_Status_t CFE_ES_DeleteChildTask(CFE_ES_TaskId_t TaskId); + +/*****************************************************************************/ +/** +** \brief Exits a child task +** +** \par Description +** This routine allows the current executing child task to exit and +** be deleted by ES. +** +** \par Assumptions, External Events, and Notes: +** This function cannot be called from an Application's Main Task. +** +** \note This function does not return a value, but if it does return +** at all, it is assumed that the Task was either unregistered or +** this function was called from a cFE Application's main task. +** +** \sa #CFE_ES_CreateChildTask, #CFE_ES_DeleteChildTask +** +******************************************************************************/ +void CFE_ES_ExitChildTask(void); +/**@}*/ + +/** @defgroup CFEAPIESMisc cFE Miscellaneous APIs + * @{ + */ + +/*****************************************************************************/ +/** +** \brief Wakes up the CFE background task +** +** \par Description +** Normally the ES background task wakes up at a periodic interval. +** Whenever new background work is added, this can be used to wake the task early, +** which may reduce the delay between adding the job and the job getting processed. +** +** \par Assumptions, External Events, and Notes: +** Note the amount of work that the background task will perform is pro-rated +** based on the amount of time elapsed since the last wakeup. Waking the task +** early will not cause the background task to do more work than it otherwise +** would - it just reduces the delay before work starts initially. +** +******************************************************************************/ +void CFE_ES_BackgroundWakeup(void); + +/*****************************************************************************/ +/** +** \brief Write a string to the cFE System Log +** +** \par Description +** This routine writes a formatted string to the cFE system log. This +** can be used to record very low-level errors that can't be reported +** using the Event Services. This function is used in place of printf +** for flight software. It should be used for significant startup events, +** critical errors, and conditionally compiled debug software. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[in] SpecStringPtr The format string for the log message. +** This is similar to the format string for a printf() call. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_ERR_SYS_LOG_FULL \copybrief CFE_ES_ERR_SYS_LOG_FULL +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +******************************************************************************/ +CFE_Status_t CFE_ES_WriteToSysLog(const char *SpecStringPtr, ...) OS_PRINTF(1, 2); + +/*****************************************************************************/ +/** +** \brief Calculate a CRC on a block of memory +** +** \par Description +** This routine calculates a cyclic redundancy check (CRC) on a block of memory. The CRC algorithm +** used is determined by the last parameter. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[in] DataPtr Pointer to the base of the memory block. +** +** \param[in] DataLength The number of bytes in the memory block. +** +** \param[in] InputCRC A starting value for use in the CRC calculation. This parameter +** allows the user to calculate the CRC of non-contiguous blocks as +** a single value. Nominally, the user should set this value to zero. +** +** \param[in] TypeCRC One of the following CRC algorithm selections: +** \arg \c CFE_MISSION_ES_CRC_8 - (Not currently implemented) +** \arg \c CFE_MISSION_ES_CRC_16 - CRC-16/ARC
+** Polynomial: 0x8005
+** Initialization: 0x0000
+** Reflect Input/Output: true
+** XorOut: 0x0000 +** \arg \c CFE_MISSION_ES_CRC_32 - (not currently implemented) +** +** \return The result of the CRC calculation on the specified memory block, or error code \ref CFEReturnCodes +** +******************************************************************************/ +uint32 CFE_ES_CalculateCRC(const void *DataPtr, size_t DataLength, uint32 InputCRC, uint32 TypeCRC); + +/*****************************************************************************/ +/** +** \ingroup CFEAPIESMisc +** \brief Notification that an asynchronous event was detected by the underlying OS/PSP +** +** \par Description +** This hook routine is called from the PSP when an exception or +** other asynchronous system event occurs +** +** \par Assumptions, External Events, and Notes: +** The PSP must guarantee that this function is only invoked from a +** context which may use OSAL primitives. In general this means that +** it shouldn't be _directly_ invoked from an ISR/signal context. +** +******************************************************************************/ +void CFE_ES_ProcessAsyncEvent(void); + +/**@}*/ + +/** @defgroup CFEAPIESCritData cFE Critical Data Store APIs + * @{ + */ + +/*****************************************************************************/ +/** +** \brief Reserve space (or re-obtain previously reserved space) in the Critical Data Store (CDS) +** +** \par Description +** This routine allocates a block of memory in the Critical Data Store and associates it with +** the calling Application. The memory can survive an Application restart as well as a Processor Reset. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[in, out] HandlePtr Pointer Application's variable that will contain the CDS Memory Block Handle. +** HandlePtr is the handle of the CDS block that can be used in +** #CFE_ES_CopyToCDS and #CFE_ES_RestoreFromCDS. +** +** \param[in] BlockSize The number of bytes needed in the CDS. +** +** \param[in] Name A pointer to a character string containing an application +** unique name of #CFE_MISSION_ES_CDS_MAX_NAME_LENGTH characters or less. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS The memory block was successfully created in the CDS. +** \retval #CFE_ES_NOT_IMPLEMENTED The processor does not support a Critical Data Store. +** \retval #CFE_ES_CDS_ALREADY_EXISTS \copybrief CFE_ES_CDS_ALREADY_EXISTS +** \retval #CFE_ES_CDS_INVALID_SIZE \copybrief CFE_ES_CDS_INVALID_SIZE +** \retval #CFE_ES_CDS_INVALID_NAME \copybrief CFE_ES_CDS_INVALID_NAME +** \retval #CFE_ES_CDS_REGISTRY_FULL \copybrief CFE_ES_CDS_REGISTRY_FULL +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_CopyToCDS, #CFE_ES_RestoreFromCDS +** +******************************************************************************/ +CFE_Status_t CFE_ES_RegisterCDS(CFE_ES_CDSHandle_t *HandlePtr, size_t BlockSize, const char *Name); + +/*****************************************************************************/ +/** +** \brief Get a CDS Block ID associated with a specified CDS Block name +** +** \par Description +** This routine retrieves the CDS Block ID associated with a +** specified CDS Block name. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[out] BlockIdPtr Pointer to variable that is to receive the CDS Block ID. +** \param[in] BlockName Pointer to null terminated character string containing a CDS Block name. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_ERR_NAME_NOT_FOUND \copybrief CFE_ES_ERR_NAME_NOT_FOUND +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_GetCDSBlockName +** +******************************************************************************/ +CFE_Status_t CFE_ES_GetCDSBlockIDByName(CFE_ES_CDSHandle_t *BlockIdPtr, const char *BlockName); + +/*****************************************************************************/ +/** +** \brief Get a Block name for a specified Block ID +** +** \par Description +** This routine retrieves the cFE Block name associated with a +** specified Block ID. +** +** \par Assumptions, External Events, and Notes: +** In the case of a failure (#CFE_ES_ERR_RESOURCEID_NOT_VALID), an empty string is returned. +** +** \param[out] BlockName Pointer to a character array of at least \c BufferLength in size that will +** be filled with the CDS Block name. +** +** \param[in] BlockId Block ID/Handle of CDS registry entry whose name is being requested. +** +** \param[in] BufferLength The maximum number of characters, including the null terminator, that can be put +** into the \c BlockName buffer. This routine will truncate the name to this length, +** if necessary. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_ERR_RESOURCEID_NOT_VALID \copybrief CFE_ES_ERR_RESOURCEID_NOT_VALID +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_GetCDSBlockIDByName +** +******************************************************************************/ +CFE_Status_t CFE_ES_GetCDSBlockName(char *BlockName, CFE_ES_CDSHandle_t BlockId, size_t BufferLength); + +/*****************************************************************************/ +/** +** \brief Save a block of data in the Critical Data Store (CDS) +** +** \par Description +** This routine copies a specified block of memory into the Critical Data Store that +** had been previously registered via #CFE_ES_RegisterCDS. The block of memory to be +** copied must be at least as big as the size specified when registering the CDS. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[in] Handle The handle of the CDS block that was previously obtained from #CFE_ES_RegisterCDS. +** +** \param[in] DataToCopy A Pointer to the block of memory to be copied into the CDS. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_ERR_RESOURCEID_NOT_VALID \copybrief CFE_ES_ERR_RESOURCEID_NOT_VALID +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_RegisterCDS, #CFE_ES_RestoreFromCDS +** +*/ +CFE_Status_t CFE_ES_CopyToCDS(CFE_ES_CDSHandle_t Handle, void *DataToCopy); + +/*****************************************************************************/ +/** +** \brief Recover a block of data from the Critical Data Store (CDS) +** +** \par Description +** This routine copies data from the Critical Data Store identified with the \c Handle into +** the area of memory pointed to by the \c RestoreToMemory pointer. The area of memory to +** be copied into must be at least as big as the size specified when registering the CDS. +** The recovery will indicate an error if the data integrity check maintained by the CDS +** indicates the contents of the CDS have changed. However, the contents will still be +** copied into the specified area of memory. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[in] Handle The handle of the CDS block that was previously obtained from #CFE_ES_RegisterCDS. +** +** \param[in, out] RestoreToMemory A Pointer to the block of memory that is to be restored with the contents of +** the CDS. *RestoreToMemory is the contents of the specified CDS. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_ERR_RESOURCEID_NOT_VALID \copybrief CFE_ES_ERR_RESOURCEID_NOT_VALID +** \retval #CFE_ES_CDS_BLOCK_CRC_ERR \copybrief CFE_ES_CDS_BLOCK_CRC_ERR +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_RegisterCDS, #CFE_ES_CopyToCDS +** +*/ +CFE_Status_t CFE_ES_RestoreFromCDS(void *RestoreToMemory, CFE_ES_CDSHandle_t Handle); +/**@}*/ + +/** @defgroup CFEAPIESMemManage cFE Memory Manager APIs + * @{ + */ + +/*****************************************************************************/ +/** +** \brief Initializes a memory pool created by an application without using a semaphore during processing. +** +** \par Description +** This routine initializes a pool of memory supplied by the calling application. When a memory pool +** created by this routine is processed, no mutex handling is performed. +** +** \par Assumptions, External Events, and Notes: +** -# The size of the pool must be an integral number of 32-bit words +** -# The start address of the pool must be 32-bit aligned +** -# 168 bytes are used for internal bookkeeping, therefore, they will not be available for allocation. +** +** \param[in, out] PoolID A pointer to the variable the caller wishes to have the memory pool handle kept in. +** PoolID is the memory pool handle. +** +** \param[in] MemPtr A Pointer to the pool of memory created by the calling application. This address must +** be on a 32-bit boundary. +** +** \param[in] Size The size of the pool of memory. Note that this must be an integral number of 32 bit words. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_PoolCreate, #CFE_ES_PoolCreateEx, #CFE_ES_GetPoolBuf, #CFE_ES_PutPoolBuf, #CFE_ES_GetMemPoolStats +** +******************************************************************************/ +CFE_Status_t CFE_ES_PoolCreateNoSem(CFE_ES_MemHandle_t *PoolID, uint8 *MemPtr, size_t Size); + +/*****************************************************************************/ +/** +** \brief Initializes a memory pool created by an application while using a semaphore during processing. +** +** \par Description +** This routine initializes a pool of memory supplied by the calling application. When a memory pool +** created by this routine is processed, mutex handling will be performed. +** +** \par Assumptions, External Events, and Notes: +** -# The size of the pool must be an integral number of 32-bit words +** -# The start address of the pool must be 32-bit aligned +** -# 168 bytes are used for internal bookkeeping, therefore, they will not be available for allocation. +** +** \param[in, out] PoolID A pointer to the variable the caller wishes to have the memory pool handle kept in. +** PoolID is the memory pool handle. +** +** \param[in] MemPtr A Pointer to the pool of memory created by the calling application. This address must +** be on a 32-bit boundary. +** +** \param[in] Size The size of the pool of memory. Note that this must be an integral number of 32 bit words. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_PoolCreateNoSem, #CFE_ES_PoolCreateEx, #CFE_ES_GetPoolBuf, #CFE_ES_PutPoolBuf, #CFE_ES_GetMemPoolStats +** +******************************************************************************/ +CFE_Status_t CFE_ES_PoolCreate(CFE_ES_MemHandle_t *PoolID, uint8 *MemPtr, size_t Size); + +/*****************************************************************************/ +/** +** \brief Initializes a memory pool created by an application with application specified block sizes. +** +** \par Description +** This routine initializes a pool of memory supplied by the calling application. +** +** \par Assumptions, External Events, and Notes: +** -# The size of the pool must be an integral number of 32-bit words +** -# The start address of the pool must be 32-bit aligned +** -# 168 bytes are used for internal bookkeeping, therefore, they will not be available for allocation. +** +** \param[in, out] PoolID A pointer to the variable the caller wishes to have the memory pool handle kept in. +** PoolID is the memory pool handle. +** +** \param[in] MemPtr A Pointer to the pool of memory created by the calling application. This address must +** be on a 32-bit boundary. +** +** \param[in] Size The size of the pool of memory. Note that this must be an integral number of 32 bit +** words. +** +** \param[in] NumBlockSizes The number of different block sizes specified in the \c BlockSizes array. If set equal to +** zero or if greater than 17, then default block sizes are used. +** +** \param[in] BlockSizes Pointer to an array of sizes to be used instead of the default block sizes specified by +** #CFE_PLATFORM_ES_MEM_BLOCK_SIZE_01 through #CFE_PLATFORM_ES_MAX_BLOCK_SIZE. If the +** pointer is equal to NULL, the default block sizes are used. +** +** \param[in] UseMutex Flag indicating whether the new memory pool will be processing with mutex handling or +** not. Valid parameter values are #CFE_ES_USE_MUTEX and #CFE_ES_NO_MUTEX +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_PoolCreate, #CFE_ES_PoolCreateNoSem, #CFE_ES_GetPoolBuf, #CFE_ES_PutPoolBuf, #CFE_ES_GetMemPoolStats +** +******************************************************************************/ +CFE_Status_t CFE_ES_PoolCreateEx(CFE_ES_MemHandle_t *PoolID, uint8 *MemPtr, size_t Size, uint16 NumBlockSizes, + const size_t *BlockSizes, bool UseMutex); + +/*****************************************************************************/ +/** +** \brief Deletes a memory pool that was previously created +** +** \par Description +** This routine removes the pool ID and frees the global table +** entry for future re-use. +** +** \par Assumptions, External Events, and Notes: +** All buffers associated with the pool become invalid after this call. +** The application should ensure that buffers/references to the +** pool are returned before deleting the pool. +** +** \param[in] PoolID The ID of the pool to delete +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_ERR_RESOURCEID_NOT_VALID \copybrief CFE_ES_ERR_RESOURCEID_NOT_VALID +** +** \sa #CFE_ES_PoolCreate, #CFE_ES_PoolCreateNoSem, #CFE_ES_GetPoolBuf, #CFE_ES_PutPoolBuf, #CFE_ES_GetMemPoolStats +** +******************************************************************************/ +int32 CFE_ES_PoolDelete(CFE_ES_MemHandle_t PoolID); + +/*****************************************************************************/ +/** +** \brief Gets a buffer from the memory pool created by #CFE_ES_PoolCreate or #CFE_ES_PoolCreateNoSem +** +** \par Description +** This routine obtains a block of memory from the memory pool supplied by the calling application. +** +** \par Assumptions, External Events, and Notes: +** -# The size allocated from the memory pool is, at a minimum, 12 bytes more than requested. +** +** \param[in, out] BufPtr A pointer to the Application's pointer in which will be stored the address of the +** allocated memory buffer. *BufPtr is the address of the requested buffer. +** +** \param[in] PoolID The handle to the memory pool as returned by #CFE_ES_PoolCreate or #CFE_ES_PoolCreateNoSem. +** +** \param[in] Size The size of the buffer requested. NOTE: The size allocated may be larger. +** +** \return Bytes Allocated, or error code \ref CFEReturnCodes +** \retval #CFE_ES_ERR_RESOURCEID_NOT_VALID \copybrief CFE_ES_ERR_RESOURCEID_NOT_VALID +** \retval #CFE_ES_ERR_MEM_BLOCK_SIZE \copybrief CFE_ES_ERR_MEM_BLOCK_SIZE +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_PoolCreate, #CFE_ES_PoolCreateNoSem, #CFE_ES_PoolCreateEx, #CFE_ES_PutPoolBuf, #CFE_ES_GetMemPoolStats, +*#CFE_ES_GetPoolBufInfo +** +******************************************************************************/ +int32 CFE_ES_GetPoolBuf(CFE_ES_MemPoolBuf_t *BufPtr, CFE_ES_MemHandle_t PoolID, size_t Size); + +/*****************************************************************************/ +/** +** \brief Gets info on a buffer previously allocated via #CFE_ES_GetPoolBuf +** +** \par Description +** This routine gets info on a buffer in the memory pool. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[in] PoolID The handle to the memory pool as returned by #CFE_ES_PoolCreate or #CFE_ES_PoolCreateNoSem. +** +** \param[in] BufPtr A pointer to the memory buffer to provide status for. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_ERR_RESOURCEID_NOT_VALID \copybrief CFE_ES_ERR_RESOURCEID_NOT_VALID +** \retval #CFE_ES_BUFFER_NOT_IN_POOL \copybrief CFE_ES_BUFFER_NOT_IN_POOL +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_PoolCreate, #CFE_ES_PoolCreateNoSem, #CFE_ES_PoolCreateEx, #CFE_ES_GetPoolBuf, #CFE_ES_GetMemPoolStats, +*#CFE_ES_PutPoolBuf +** +******************************************************************************/ +CFE_Status_t CFE_ES_GetPoolBufInfo(CFE_ES_MemHandle_t PoolID, CFE_ES_MemPoolBuf_t BufPtr); + +/*****************************************************************************/ +/** +** \brief Releases a buffer from the memory pool that was previously allocated via #CFE_ES_GetPoolBuf +** +** \par Description +** This routine releases a buffer back into the memory pool. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[in] PoolID The handle to the memory pool as returned by #CFE_ES_PoolCreate or #CFE_ES_PoolCreateNoSem. +** +** \param[in] BufPtr A pointer to the memory buffer to be released. +** +** \return Bytes released, or error code \ref CFEReturnCodes +** \retval #CFE_ES_ERR_RESOURCEID_NOT_VALID \copybrief CFE_ES_ERR_RESOURCEID_NOT_VALID +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_PoolCreate, #CFE_ES_PoolCreateNoSem, #CFE_ES_PoolCreateEx, #CFE_ES_GetPoolBuf, #CFE_ES_GetMemPoolStats, +*#CFE_ES_GetPoolBufInfo +** +******************************************************************************/ +int32 CFE_ES_PutPoolBuf(CFE_ES_MemHandle_t PoolID, CFE_ES_MemPoolBuf_t BufPtr); + +/*****************************************************************************/ +/** +** \brief Extracts the statistics maintained by the memory pool software +** +** \par Description +** This routine fills the #CFE_ES_MemPoolStats_t data structure with the statistics +** maintained by the memory pool software. These statistics can then be telemetered +** by the calling Application. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[in, out] BufPtr Pointer to #CFE_ES_MemPoolStats_t data structure to be +** filled with memory statistics. *BufPtr is the Memory Pool Statistics stored in given +** data structure. +** +** \param[in] Handle The handle to the memory pool whose statistics are desired. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_ERR_RESOURCEID_NOT_VALID \copybrief CFE_ES_ERR_RESOURCEID_NOT_VALID +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_PoolCreate, #CFE_ES_PoolCreateNoSem, #CFE_ES_PoolCreateEx, #CFE_ES_GetPoolBuf, #CFE_ES_PutPoolBuf +** +******************************************************************************/ +CFE_Status_t CFE_ES_GetMemPoolStats(CFE_ES_MemPoolStats_t *BufPtr, CFE_ES_MemHandle_t Handle); +/**@}*/ + +/** @defgroup CFEAPIESPerfMon cFE Performance Monitor APIs + * @{ + */ + +/*****************************************************************************/ +/** +** \brief Entry marker for use with Software Performance Analysis Tool. +** +** \par Description +** This macro logs the entry or start event/marker for the specified +** entry \c id. This macro, in conjunction with the #CFE_ES_PerfLogExit, +** is used by the Software Performance Analysis tool (see section 5.15). +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[in] id Identifier of the specific event or marker. +** +** \sa #CFE_ES_PerfLogExit, #CFE_ES_PerfLogAdd +** +******************************************************************************/ +#define CFE_ES_PerfLogEntry(id) (CFE_ES_PerfLogAdd(id, 0)) + +/*****************************************************************************/ +/** +** \brief Exit marker for use with Software Performance Analysis Tool. +** +** \par Description +** This macro logs the exit or end event/marker for the specified +** entry \c id. This macro, in conjunction with the #CFE_ES_PerfLogEntry, +** is used by the Software Performance Analysis tool (see section 5.15). +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[in] id Identifier of the specific event or marker. +** +** \sa #CFE_ES_PerfLogEntry, #CFE_ES_PerfLogAdd +** +******************************************************************************/ +#define CFE_ES_PerfLogExit(id) (CFE_ES_PerfLogAdd(id, 1)) + +/*****************************************************************************/ +/** +** \brief Function called by #CFE_ES_PerfLogEntry and #CFE_ES_PerfLogExit macros +** +** \par Description +** This function logs the entry and exit marker for the specified +** \c id. This function is used by the Software Performance Analysis +** tool (see section 5.15). +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[in] Marker Identifier of the specific event or marker. +** \param[in] EntryExit Used to specify Entry(0) or Exit(1) +** +** \sa #CFE_ES_PerfLogEntry, #CFE_ES_PerfLogExit +** +******************************************************************************/ +void CFE_ES_PerfLogAdd(uint32 Marker, uint32 EntryExit); +/**@}*/ + +/** @defgroup CFEAPIESGenCount cFE Generic Counter APIs + * @{ + */ + +/*****************************************************************************/ +/** +** \brief Register a generic counter +** +** \par Description +** This routine registers a generic counter. +** +** \par Assumptions, External Events, and Notes: +** None. +** +** \param[in] *CounterName The Name of the generic counter. +** +** \param[out] *CounterIdPtr The Counter Id of the newly created counter. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_IncrementGenCounter, #CFE_ES_DeleteGenCounter, #CFE_ES_SetGenCount, #CFE_ES_GetGenCount, +*#CFE_ES_GetGenCounterIDByName +** +******************************************************************************/ +CFE_Status_t CFE_ES_RegisterGenCounter(CFE_ES_CounterId_t *CounterIdPtr, const char *CounterName); + +/*****************************************************************************/ +/** +** \brief Delete a generic counter +** +** \par Description +** This routine deletes a previously registered generic counter. +** +** \par Assumptions, External Events, and Notes: +** None. +** +** \param[in] CounterId The Counter Id of the newly created counter. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_IncrementGenCounter, #CFE_ES_RegisterGenCounter, #CFE_ES_SetGenCount, #CFE_ES_GetGenCount, +*#CFE_ES_GetGenCounterIDByName +** +******************************************************************************/ +CFE_Status_t CFE_ES_DeleteGenCounter(CFE_ES_CounterId_t CounterId); + +/*****************************************************************************/ +/** +** \brief Increments the specified generic counter +** +** \par Description +** This routine increments the specified generic counter. +** +** \par Assumptions, External Events, and Notes: +** None. +** +** \param[in] CounterId The Counter to be incremented. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_RegisterGenCounter, #CFE_ES_DeleteGenCounter, #CFE_ES_SetGenCount, #CFE_ES_GetGenCount, +*#CFE_ES_GetGenCounterIDByName +** +******************************************************************************/ +CFE_Status_t CFE_ES_IncrementGenCounter(CFE_ES_CounterId_t CounterId); + +/*****************************************************************************/ +/** +** \brief Set the specified generic counter +** +** \par Description +** This routine sets the specified generic counter to the specified value. +** +** \par Assumptions, External Events, and Notes: +** None. +** +** \param[in] CounterId The Counter to be set. +** +** \param[in] Count The new value of the Counter. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_RegisterGenCounter, #CFE_ES_DeleteGenCounter, #CFE_ES_IncrementGenCounter, #CFE_ES_GetGenCount, +*#CFE_ES_GetGenCounterIDByName +** +******************************************************************************/ +CFE_Status_t CFE_ES_SetGenCount(CFE_ES_CounterId_t CounterId, uint32 Count); + +/*****************************************************************************/ +/** +** \brief Get the specified generic counter count +** +** \par Description +** This routine gets the value of a generic counter. +** +** \par Assumptions, External Events, and Notes: +** None. +** +** \param[in] CounterId The Counter to get the value from. +** +** \param[in] *Count The value of the Counter. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_RegisterGenCounter, #CFE_ES_DeleteGenCounter, #CFE_ES_SetGenCount, #CFE_ES_IncrementGenCounter, +*#CFE_ES_GetGenCounterIDByName +** +******************************************************************************/ +CFE_Status_t CFE_ES_GetGenCount(CFE_ES_CounterId_t CounterId, uint32 *Count); + +/*****************************************************************************/ +/** +** \brief Get the Id associated with a generic counter name +** +** \par Description +** This routine gets the Counter Id for a generic counter specified by name. +** +** \par Assumptions, External Events, and Notes: +** None. +** +** \param[out] CounterIdPtr Pointer to variable that is to receive the Counter's ID. +** \param[in] CounterName Pointer to null terminated character string containing a Counter name. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_ERR_NAME_NOT_FOUND \copybrief CFE_ES_ERR_NAME_NOT_FOUND +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_GetGenCounterName +** +******************************************************************************/ +CFE_Status_t CFE_ES_GetGenCounterIDByName(CFE_ES_CounterId_t *CounterIdPtr, const char *CounterName); + +/*****************************************************************************/ +/** +** \brief Get a Counter name for a specified Counter ID +** +** \par Description +** This routine retrieves the cFE Counter name associated with a +** specified Counter ID. +** +** \par Assumptions, External Events, and Notes: +** In the case of a failure (#CFE_ES_ERR_RESOURCEID_NOT_VALID), an empty string is returned. +** +** \param[out] CounterName Pointer to a character array of at least \c BufferLength in size that will +** be filled with the Counter name. +** +** \param[in] CounterId ID of Counter whose name is being requested. +** +** \param[in] BufferLength The maximum number of characters, including the null terminator, that can be put +** into the \c CounterName buffer. This routine will truncate the name to this length, +** if necessary. +** +** \return Execution status, see \ref CFEReturnCodes +** \retval #CFE_SUCCESS \copybrief CFE_SUCCESS +** \retval #CFE_ES_ERR_RESOURCEID_NOT_VALID \copybrief CFE_ES_ERR_RESOURCEID_NOT_VALID +** \retval #CFE_ES_BAD_ARGUMENT \copybrief CFE_ES_BAD_ARGUMENT +** +** \sa #CFE_ES_GetGenCounterIDByName +** +******************************************************************************/ +CFE_Status_t CFE_ES_GetGenCounterName(char *CounterName, CFE_ES_CounterId_t CounterId, size_t BufferLength); + +/**@}*/ + +#endif /* CFE_ES_H */ diff --git a/modules/core_api/fsw/inc/cfe_es_api_typedefs.h b/modules/core_api/fsw/inc/cfe_es_api_typedefs.h new file mode 100644 index 000000000..d8edef745 --- /dev/null +++ b/modules/core_api/fsw/inc/cfe_es_api_typedefs.h @@ -0,0 +1,193 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/** + * @file + * + * Purpose: + * Unit specification for Executive Services library functions and macros. + * + * References: + * Flight Software Branch C Coding Standard Version 1.0a + * cFE Flight Software Application Developers Guide + * + * Notes: + * + */ + +#ifndef CFE_ES_API_TYPEDEFS_H +#define CFE_ES_API_TYPEDEFS_H + +/* +** Includes +*/ +#include "common_types.h" +#include "cfe_es_extern_typedefs.h" + +/* +** Note about reset type and subtypes: +** +** These values come from the PSP so the actual definition of these enumerations +** was moved to the PSP header file . +** +** In the future the Electronic Data sheets (EDS) for PSP/ES +** will define the exact values to use in telemetry messages. +*/ + +/* +** Reset types +*/ +/** \name Reset Type extensions */ +/** \{ */ +#define CFE_ES_APP_RESTART CFE_PSP_RST_TYPE_MAX /**< Application only was reset (extend the PSP enumeration here) */ +/** \} */ + +/*****************************************************************************/ +/* +** Type Definitions +*/ + +/* +** Entry Function Prototypes +*/ +typedef void (*CFE_ES_TaskEntryFuncPtr_t)(void); /**< \brief Required Prototype of Task Main Functions */ +typedef int32 (*CFE_ES_LibraryEntryFuncPtr_t)( + CFE_ES_LibId_t LibId); /**< \brief Required Prototype of Library Initialization Functions */ + +/** + * \brief Compatible typedef for ES child task entry point. + * + * All ES task functions (main + child) use the same entry point type. + */ +typedef CFE_ES_TaskEntryFuncPtr_t CFE_ES_ChildTaskMainFuncPtr_t; + +/** + * @brief Type for the stack pointer of tasks. + * + * This type is used in the CFE ES task API. + */ +typedef void *CFE_ES_StackPointer_t; /* aka osal_stackptr_t in proposed OSAL change */ + +/** + * \brief Pool Alignement + * + * Union that can be used for minimum memory alignment of ES memory pools on the target. + * It contains the longest native data types such that the alignment of this structure + * should reflect the largest possible alignment requirements for any data on this processor. + */ +typedef union CFE_ES_PoolAlign +{ + void *Ptr; /**< \brief Aligned pointer */ + /* note -- native types (int/double) are intentional here */ + long long int LongInt; /**< \brief Aligned Long Integer */ + long double LongDouble; /**< \brief Aligned Long Double */ +} CFE_ES_PoolAlign_t; + +/** + * \brief Static Pool Type + * + * A macro to help instantiate static memory pools that are correctly aligned. + * This resolves to a union type that contains a member called "Data" that will + * be correctly aligned to be a memory pool and sized according to the argument. + */ +#define CFE_ES_STATIC_POOL_TYPE(size) \ + union \ + { \ + CFE_ES_PoolAlign_t Align; \ + uint8 Data[size]; \ + } + +/** + * @brief Pointer type used for memory pool API + * + * This is used in the Get/Put API calls to refer to a pool buffer. + * + * This pointer is expected to be type cast to the real object + * type after getting a new buffer. Using void* allows this + * type conversion to occur easily. + * + * @note Older versions of CFE implemented the API using a uint32*, + * which required explicit type casting everywhere it was called. + * Although the API type is now void* to make usage easier, the + * pool buffers are aligned to machine requirements - typically 64 bits. + */ +typedef void *CFE_ES_MemPoolBuf_t; + +/** + * @brief Conversion macro to create buffer pointer from another type + * + * In cases where the actual buffer pointer is computed, this macro + * aids in converting the computed address (typically an OSAL "cpuaddr" + * type) into a buffer pointer. + * + * @note Any address calculation needs to take machine alignment + * requirements into account. + */ +#define CFE_ES_MEMPOOLBUF_C(x) ((CFE_ES_MemPoolBuf_t)(x)) + +/** \name Conversions for ES resource IDs */ +/** \{ */ + +/* + * Conversion macros for each ES resource ID subtype + * + * These accept a generic/non-specific CFE_ResourceId_t value + * and convert it to the corresponding resource-specific type. + * + * These should only be used when with the resource ID constants, + * or where the code has confirmed or is determining the generic + * identifier does correspond to a resource of that type. + */ +#define CFE_ES_APPID_C(val) ((CFE_ES_AppId_t)CFE_RESOURCEID_WRAP(val)) +#define CFE_ES_TASKID_C(val) ((CFE_ES_TaskId_t)CFE_RESOURCEID_WRAP(val)) +#define CFE_ES_LIBID_C(val) ((CFE_ES_LibId_t)CFE_RESOURCEID_WRAP(val)) +#define CFE_ES_COUNTERID_C(val) ((CFE_ES_CounterId_t)CFE_RESOURCEID_WRAP(val)) +#define CFE_ES_MEMHANDLE_C(val) ((CFE_ES_MemHandle_t)CFE_RESOURCEID_WRAP(val)) +#define CFE_ES_CDSHANDLE_C(val) ((CFE_ES_CDSHandle_t)CFE_RESOURCEID_WRAP(val)) + +/** \} */ + +/** \name Type-specific initalizers for "undefined" resource IDs */ +/** \{ */ + +#define CFE_ES_APPID_UNDEFINED CFE_ES_APPID_C(CFE_RESOURCEID_UNDEFINED) +#define CFE_ES_TASKID_UNDEFINED CFE_ES_TASKID_C(CFE_RESOURCEID_UNDEFINED) +#define CFE_ES_LIBID_UNDEFINED CFE_ES_LIBID_C(CFE_RESOURCEID_UNDEFINED) +#define CFE_ES_COUNTERID_UNDEFINED CFE_ES_COUNTERID_C(CFE_RESOURCEID_UNDEFINED) +#define CFE_ES_MEMHANDLE_UNDEFINED CFE_ES_MEMHANDLE_C(CFE_RESOURCEID_UNDEFINED) +#define CFE_ES_CDS_BAD_HANDLE CFE_ES_CDSHANDLE_C(CFE_RESOURCEID_UNDEFINED) +/** \} */ + +/** \name Task Stack Constants */ +/** \{ */ + +/** + * \brief Indicates that the stack for the child task should be dynamically allocated. + * + * This value may be supplied as the Stack Pointer argument to CFE_ES_ChildTaskCreate() + * to indicate that the stack should be dynamically allocated. + */ +#define CFE_ES_TASK_STACK_ALLOCATE NULL /* aka OS_TASK_STACK_ALLOCATE in proposed OSAL change */ +/** \} */ + +#define CFE_ES_NO_MUTEX false /**< \brief Indicates that the memory pool selection will not use a semaphore */ +#define CFE_ES_USE_MUTEX true /**< \brief Indicates that the memory pool selection will use a semaphore */ + +#endif /* CFE_ES_API_TYPEDEFS_H */ diff --git a/modules/core_api/fsw/inc/cfe_es_core_internal.h b/modules/core_api/fsw/inc/cfe_es_core_internal.h new file mode 100644 index 000000000..e9e38a4b3 --- /dev/null +++ b/modules/core_api/fsw/inc/cfe_es_core_internal.h @@ -0,0 +1,136 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/** + * @file + * + * Purpose: + * Unit specification for Executive Services library functions and macros. + * + * References: + * Flight Software Branch C Coding Standard Version 1.0a + * cFE Flight Software Application Developers Guide + * + * Notes: + * + */ + +#ifndef CFE_ES_CORE_INTERNAL_H +#define CFE_ES_CORE_INTERNAL_H + +#include "common_types.h" +#include "cfe_es_extern_typedefs.h" + +/* + * The internal APIs prototyped within this block are only intended to be invoked from + * other CFE core apps. They still need to be prototyped in the shared header such that + * they can be called from other core modules, but applications should not call these. + */ + +/** @defgroup CFEAPIESCoreInternal cFE Internal Executive Service APIs, internal to CFE core + * @{ + */ + +/*****************************************************************************/ +/** +** \brief Entry Point for cFE Core Application +** +** \par Description +** This is the entry point to the cFE ES Core Application. +** +** \par Assumptions, External Events, and Notes: +** None +** +******************************************************************************/ +extern void CFE_ES_TaskMain(void); + +/*****************************************************************************/ +/** +** \brief Initializes the cFE core module API Library +** +** \par Description +** Initializes the cFE core module API Library +** +** \par Assumptions, External Events, and Notes: +** -# This function MUST be called before any module API's are called. +** +******************************************************************************/ +extern int32 CFE_ES_CDS_EarlyInit(void); + +/*****************************************************************************/ +/** +** \brief Reserve space (or re-obtain previously reserved space) in the Critical Data Store (CDS) +** +** \par Description +** This routine is identical to #CFE_ES_RegisterCDS except it identifies the contents +** of the CDS as a critical table. This is crucial because a critical table CDS must +** only be deleted by cFE Table Services, not via an ES delete CDS command. Otherwise, +** Table Services may be out of sync with the contents of the CDS. +** +** \par Assumptions, External Events, and Notes: +** -# This function assumes input parameters are error free and have met size/value restrictions. +** -# The calling function is responsible for issuing any event messages associated with errors. +** +** \param[in, out] HandlePtr Pointer Application's variable that will contain the CDS Memory Block Handle. +** HandlePtr is the handle of the CDS block that can be used in #CFE_ES_CopyToCDS and +** #CFE_ES_RestoreFromCDS. +** +** \param[in] UserBlockSize The number of bytes needed in the CDS. +** +** \param[in] Name Pointer to character string containing the Application's local name for +** the CDS. +** +** \param[in] CriticalTbl Indicates whether the CDS is to be used as a Critical Table or not +** +** \return See return codes for #CFE_ES_RegisterCDS +** +******************************************************************************/ +int32 CFE_ES_RegisterCDSEx(CFE_ES_CDSHandle_t *HandlePtr, size_t UserBlockSize, const char *Name, bool CriticalTbl); + +/*****************************************************************************/ +/** +** \brief Deletes the specified CDS from the CDS Registry and frees CDS Memory +** +** \par Description +** Removes the record of the specified CDS from the CDS Registry and +** frees the associated CDS memory for future use. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[in] CDSName - Pointer to character string containing complete +** CDS Name (of the format "AppName.CDSName"). +** +** \param[in] CalledByTblServices - Flag that identifies whether the CDS is supposed to +** be a Critical Table Image or not. +** +** \return #CFE_SUCCESS \copydoc CFE_SUCCESS +** \return #CFE_ES_CDS_WRONG_TYPE_ERR \copydoc CFE_ES_CDS_WRONG_TYPE_ERR +** \return #CFE_ES_CDS_OWNER_ACTIVE_ERR \copydoc CFE_ES_CDS_OWNER_ACTIVE_ERR +** \return #CFE_ES_ERR_NAME_NOT_FOUND \copydoc CFE_ES_ERR_NAME_NOT_FOUND +** \return Any of the return values from CFE_ES_UpdateCDSRegistry +** \return Any of the return values from CFE_ES_GenPoolPutBlock +** +******************************************************************************/ +int32 CFE_ES_DeleteCDS(const char *CDSName, bool CalledByTblServices); + +/**@}*/ + +#endif /* CFE_ES_CORE_INTERNAL_H */ diff --git a/modules/core_api/fsw/inc/cfe_es_extern_typedefs.h b/modules/core_api/fsw/inc/cfe_es_extern_typedefs.h new file mode 100644 index 000000000..b0c70194b --- /dev/null +++ b/modules/core_api/fsw/inc/cfe_es_extern_typedefs.h @@ -0,0 +1,573 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/** + * @file + * + * Declarations and prototypes for cfe_es_extern_typedefs module + */ + +#ifndef CFE_ES_EXTERN_TYPEDEFS_H +#define CFE_ES_EXTERN_TYPEDEFS_H + +/* This header may be generated from an EDS file, + * tools are available and the feature is enabled */ +#ifdef CFE_EDS_ENABLED_BUILD + +/* Use the EDS generated version of these types */ +#include "cfe_es_eds_typedefs.h" + +#else +/* Use the local definitions of these types */ + +#include "common_types.h" +#include "cfe_resourceid_typedef.h" +#include "cfe_mission_cfg.h" + +/** + * @brief Label definitions associated with CFE_ES_LogMode_Enum_t + */ +enum CFE_ES_LogMode +{ + + /** + * @brief Overwrite Log Mode + */ + CFE_ES_LogMode_OVERWRITE = 0, + + /** + * @brief Discard Log Mode + */ + CFE_ES_LogMode_DISCARD = 1 +}; + +/** + * @brief Identifies handling of log messages after storage is filled + * + * @sa enum CFE_ES_LogMode + */ +typedef uint8 CFE_ES_LogMode_Enum_t; + +/** + * @brief Label definitions associated with CFE_ES_ExceptionAction_Enum_t + */ +enum CFE_ES_ExceptionAction +{ + + /** + * @brief Restart application if exception occurs + */ + CFE_ES_ExceptionAction_RESTART_APP = 0, + + /** + * @brief Restart processor if exception occurs + */ + CFE_ES_ExceptionAction_PROC_RESTART = 1 +}; + +/** + * @brief Identifies action to take if exception occurs + * + * @sa enum CFE_ES_ExceptionAction + */ +typedef uint8 CFE_ES_ExceptionAction_Enum_t; + +/** + * @brief Label definitions associated with CFE_ES_AppType_Enum_t + */ +enum CFE_ES_AppType +{ + + /** + * @brief CFE core application + */ + CFE_ES_AppType_CORE = 1, + + /** + * @brief CFE external application + */ + CFE_ES_AppType_EXTERNAL = 2, + + /** + * @brief CFE library + */ + CFE_ES_AppType_LIBRARY = 3 +}; + +/** + * @brief Identifies type of CFE application + * + * @sa enum CFE_ES_AppType + */ +typedef uint8 CFE_ES_AppType_Enum_t; + +/** + * @brief Label definitions associated with CFE_ES_RunStatus_Enum_t + */ +enum CFE_ES_RunStatus +{ + /** + * @brief Reserved value, should not be used + */ + CFE_ES_RunStatus_UNDEFINED = 0, + + /** + * @brief Indicates that the Application should continue to run + */ + CFE_ES_RunStatus_APP_RUN = 1, + + /** + * @brief Indicates that the Application wants to exit normally + */ + CFE_ES_RunStatus_APP_EXIT = 2, + + /** + * @brief Indicates that the Application is quitting with an error + */ + CFE_ES_RunStatus_APP_ERROR = 3, + + /** + * @brief The cFE App caused an exception + */ + CFE_ES_RunStatus_SYS_EXCEPTION = 4, + + /** + * @brief The system is requesting a restart of the cFE App + */ + CFE_ES_RunStatus_SYS_RESTART = 5, + + /** + * @brief The system is requesting a reload of the cFE App + */ + CFE_ES_RunStatus_SYS_RELOAD = 6, + + /** + * @brief The system is requesting that the cFE App is stopped + */ + CFE_ES_RunStatus_SYS_DELETE = 7, + + /** + * @brief Indicates that the Core Application could not Init + */ + CFE_ES_RunStatus_CORE_APP_INIT_ERROR = 8, + + /** + * @brief Indicates that the Core Application had a runtime failure + */ + CFE_ES_RunStatus_CORE_APP_RUNTIME_ERROR = 9, + + /** + * @brief Reserved value, marker for the maximum state + */ + CFE_ES_RunStatus_MAX + +}; + +/** + * @brief Run Status and Exit Status identifiers + * + * @sa enum CFE_ES_RunStatus + */ +typedef uint32 CFE_ES_RunStatus_Enum_t; + +/** + * @brief Label definitions associated with CFE_ES_SystemState_Enum_t + */ +enum CFE_ES_SystemState +{ + + /** + * @brief reserved + */ + CFE_ES_SystemState_UNDEFINED = 0, + + /** + * @brief single threaded mode while setting up CFE itself + */ + CFE_ES_SystemState_EARLY_INIT = 1, + + /** + * @brief core apps (CFE_ES_ObjectTable) are starting (multi-threaded) + */ + CFE_ES_SystemState_CORE_STARTUP = 2, + + /** + * @brief core is ready, starting other external apps/libraries (if any) + */ + CFE_ES_SystemState_CORE_READY = 3, + + /** + * @brief startup apps have all completed their early init, but not necessarily operational yet + */ + CFE_ES_SystemState_APPS_INIT = 4, + + /** + * @brief normal operation mode; all apps are RUNNING + */ + CFE_ES_SystemState_OPERATIONAL = 5, + + /** + * @brief reserved for future use, all apps would be STOPPED + */ + CFE_ES_SystemState_SHUTDOWN = 6, + + /** + * @brief Reserved value, marker for the maximum state + */ + CFE_ES_SystemState_MAX +}; + +/** + * @brief The overall cFE System State + * + * These values are used with the #CFE_ES_WaitForSystemState API call to synchronize application startup. + * + * @note These are defined in order so that relational comparisons e.g. if (STATEA < STATEB) are possible + * + * @sa enum CFE_ES_SystemState + */ +typedef uint32 CFE_ES_SystemState_Enum_t; + +/** + * @brief Label definitions associated with CFE_ES_LogEntryType_Enum_t + */ +enum CFE_ES_LogEntryType +{ + + /** + * @brief Log entry from a core subsystem + */ + CFE_ES_LogEntryType_CORE = 1, + + /** + * @brief Log entry from an application + */ + CFE_ES_LogEntryType_APPLICATION = 2 +}; + +/** + * @brief Type of entry in the Error and Reset (ER) Log + * + * @sa enum CFE_ES_LogEntryType + */ +typedef uint8 CFE_ES_LogEntryType_Enum_t; + +/** + * @brief Label definitions associated with CFE_ES_AppState_Enum_t + */ +enum CFE_ES_AppState +{ + + /** + * @brief Initial state before app thread is started + */ + CFE_ES_AppState_UNDEFINED = 0, + + /** + * @brief App thread has started, app performing early initialization of its own data + */ + CFE_ES_AppState_EARLY_INIT = 1, + + /** + * @brief Early/Local initialization is complete. First sync point. + */ + CFE_ES_AppState_LATE_INIT = 2, + + /** + * @brief All initialization is complete. Second sync point. + */ + CFE_ES_AppState_RUNNING = 3, + + /** + * @brief Application is waiting on a Restart/Reload/Delete request + */ + CFE_ES_AppState_WAITING = 4, + + /** + * @brief Application is stopped + */ + CFE_ES_AppState_STOPPED = 5, + + /** + * @brief Reserved entry, marker for the maximum state + */ + CFE_ES_AppState_MAX +}; + +/** + * @brief Application Run State + * + * The normal progression of APP states: + * UNDEFINED -> EARLY_INIT -> LATE_INIT -> RUNNING -> WAITING -> STOPPED + * + * @note These are defined in order so that relational comparisons e.g. if (STATEA < STATEB) are possible + * + * @sa enum CFE_ES_AppState + */ +typedef uint32 CFE_ES_AppState_Enum_t; + +/** + * @brief A type for Application IDs + * + * This is the type that is used for any API accepting or returning an App ID + */ +typedef CFE_RESOURCEID_BASE_TYPE CFE_ES_AppId_t; + +/** + * @brief A type for Task IDs + * + * This is the type that is used for any API accepting or returning a Task ID + */ +typedef CFE_RESOURCEID_BASE_TYPE CFE_ES_TaskId_t; + +/** + * @brief A type for Library IDs + * + * This is the type that is used for any API accepting or returning a Lib ID + */ +typedef CFE_RESOURCEID_BASE_TYPE CFE_ES_LibId_t; + +/** + * @brief A type for Counter IDs + * + * This is the type that is used for any API accepting or returning an Counter ID + */ +typedef CFE_RESOURCEID_BASE_TYPE CFE_ES_CounterId_t; + +/** + * @brief Memory Handle type + * + * Data type used to hold Handles of Memory Pools + * created via CFE_ES_PoolCreate and CFE_ES_PoolCreateNoSem + */ +typedef CFE_RESOURCEID_BASE_TYPE CFE_ES_MemHandle_t; + +/** + * @brief CDS Handle type + * + * Data type used to hold Handles of Critical Data Stores. See #CFE_ES_RegisterCDS + */ +typedef CFE_RESOURCEID_BASE_TYPE CFE_ES_CDSHandle_t; + +/** + * @brief Type used for task priority in CFE ES as + * including the commands/telemetry messages. + * + * @note the valid range is only 0-255 (same as OSAL) but + * a wider type is used for backward compatibility + * in binary formats of messages. + */ +typedef uint16 CFE_ES_TaskPriority_Atom_t; + +/** + * @brief Type used for memory sizes and offsets in commands and telemetry + * + * For backward compatibility with existing CFE code this should be uint32, + * but all telemetry information will be limited to 4GB in size as a result. + * + * On 64-bit platforms this can be a 64-bit value which will allow larger + * memory objects, but this will break compatibility with existing control + * systems, and may also change the alignment/padding of messages. + * + * In either case this must be an unsigned type. + */ +typedef uint32 CFE_ES_MemOffset_t; + +/* + * A converter macro to use when initializing an CFE_ES_MemOffset_t + * from an integer value of a different type. + */ +#define CFE_ES_MEMOFFSET_C(x) ((CFE_ES_MemOffset_t)(x)) + +/** + * @brief Type used for memory addresses in command and telemetry messages + * + * For backward compatibility with existing CFE code this should be uint32, + * but if running on a 64-bit platform, addresses in telemetry will be + * truncated to 32 bits and therefore will not be valid. + * + * On 64-bit platforms this can be a 64-bit address which will allow the + * full memory address in commands and telemetry, but this will break + * compatibility with existing control systems, and may also change + * the alignment/padding of messages. + * + * In either case this must be an unsigned type. + * + * FSW code should access this value via the macros provided, which + * converts to the native "cpuaddr" type provided by OSAL. This macro + * provides independence between the message representation and local + * representation of a memory address. + */ +typedef uint32 CFE_ES_MemAddress_t; + +/* + * A converter macro to use when initializing an CFE_ES_MemAddress_t + * from a pointer value of a different type. + * + * @note on a 64 bit platform, this macro will truncate the address such + * that it will fit into a 32-bit telemetry field. Obviously, the resulting + * value is no longer usable as a memory address after this. + */ +#define CFE_ES_MEMADDRESS_C(x) ((CFE_ES_MemAddress_t)((cpuaddr)(x)&0xFFFFFFFF)) + +/* + * Data Sructures shared between API and Message (CMD/TLM) interfaces + */ + +/** + * \brief Application Information + * + * Structure that is used to provide information about an app. + * It is primarily used for the QueryOne and QueryAll Commands. + * + * While this structure is primarily intended for Application info, + * it can also represent Library information where only a subset of + * the information applies. + */ +typedef struct CFE_ES_AppInfo +{ + CFE_ResourceId_t ResourceId; /**< \cfetlmmnemonic \ES_APP_ID + \brief Application or Library ID for this resource */ + uint32 Type; /**< \cfetlmmnemonic \ES_APPTYPE + \brief The type of App: CORE or EXTERNAL */ + + char Name[CFE_MISSION_MAX_API_LEN]; /**< \cfetlmmnemonic \ES_APPNAME + \brief The Registered Name of the Application */ + char EntryPoint[CFE_MISSION_MAX_API_LEN]; /**< \cfetlmmnemonic \ES_APPENTRYPT + \brief The Entry Point label for the Application */ + char FileName[CFE_MISSION_MAX_PATH_LEN]; /**< \cfetlmmnemonic \ES_APPFILENAME + \brief The Filename of the file containing the Application */ + + CFE_ES_MemOffset_t StackSize; /**< \cfetlmmnemonic \ES_STACKSIZE + \brief The Stack Size of the Application */ + uint32 AddressesAreValid; /**< \cfetlmmnemonic \ES_ADDRVALID + \brief Indicates that the Code, Data, and BSS addresses/sizes are valid */ + CFE_ES_MemAddress_t CodeAddress; /**< \cfetlmmnemonic \ES_CODEADDR + \brief The Address of the Application Code Segment*/ + CFE_ES_MemOffset_t CodeSize; /**< \cfetlmmnemonic \ES_CODESIZE + \brief The Code Size of the Application */ + CFE_ES_MemAddress_t DataAddress; /**< \cfetlmmnemonic \ES_DATAADDR + \brief The Address of the Application Data Segment*/ + CFE_ES_MemOffset_t DataSize; /**< \cfetlmmnemonic \ES_DATASIZE + \brief The Data Size of the Application */ + CFE_ES_MemAddress_t BSSAddress; /**< \cfetlmmnemonic \ES_BSSADDR + \brief The Address of the Application BSS Segment*/ + CFE_ES_MemOffset_t BSSSize; /**< \cfetlmmnemonic \ES_BSSSIZE + \brief The BSS Size of the Application */ + CFE_ES_MemAddress_t StartAddress; /**< \cfetlmmnemonic \ES_STARTADDR + \brief The Start Address of the Application */ + CFE_ES_ExceptionAction_Enum_t ExceptionAction; /**< \cfetlmmnemonic \ES_EXCEPTNACTN + \brief What should occur if Application has an exception + (Restart Application OR Restart Processor) */ + CFE_ES_TaskPriority_Atom_t Priority; /**< \cfetlmmnemonic \ES_PRIORITY + \brief The Priority of the Application */ + CFE_ES_TaskId_t MainTaskId; /**< \cfetlmmnemonic \ES_MAINTASKID + \brief The Application's Main Task ID */ + uint32 ExecutionCounter; /**< \cfetlmmnemonic \ES_MAINTASKEXECNT + \brief The Application's Main Task Execution Counter */ + char MainTaskName[CFE_MISSION_MAX_API_LEN]; /**< \cfetlmmnemonic \ES_MAINTASKNAME + \brief The Application's Main Task ID */ + uint32 NumOfChildTasks; /**< \cfetlmmnemonic \ES_CHILDTASKS + \brief Number of Child tasks for an App */ + +} CFE_ES_AppInfo_t; + +/** + * \brief Task Information + * + * Structure that is used to provide information about a task. It is primarily + * used for the Query All Tasks (#CFE_ES_QUERY_ALL_TASKS_CC) command. + * + * \note There is not currently a telemetry message directly containing this + * data structure, but it does define the format of the data file generated + * by the Query All Tasks command. Therefore it should be considered + * part of the overall telemetry interface. + */ +typedef struct CFE_ES_TaskInfo +{ + CFE_ES_TaskId_t TaskId; /**< \brief Task Id */ + uint32 ExecutionCounter; /**< \brief Task Execution Counter */ + char TaskName[CFE_MISSION_MAX_API_LEN]; /**< \brief Task Name */ + CFE_ES_AppId_t AppId; /**< \brief Parent Application ID */ + char AppName[CFE_MISSION_MAX_API_LEN]; /**< \brief Parent Application Name */ + CFE_ES_MemOffset_t StackSize; /**< Size of task stack */ + CFE_ES_TaskPriority_Atom_t Priority; /**< Priority of task */ + uint8 Spare[2]; /**< Spare bytes for alignment */ +} CFE_ES_TaskInfo_t; + +/** + * \brief CDS Register Dump Record + * + * Structure that is used to provide information about a critical data store. + * It is primarily used for the Dump CDS registry (#CFE_ES_DUMP_CDS_REGISTRY_CC) + * command. + * + * \note There is not currently a telemetry message directly containing this + * data structure, but it does define the format of the data file generated + * by the Dump CDS registry command. Therefore it should be considered + * part of the overall telemetry interface. + */ +typedef struct CFE_ES_CDSRegDumpRec +{ + CFE_ES_CDSHandle_t Handle; /**< \brief Handle of CDS */ + CFE_ES_MemOffset_t Size; /**< \brief Size, in bytes, of the CDS memory block */ + bool Table; /**< \brief Flag that indicates whether CDS contains a Critical Table */ + char Name[CFE_MISSION_ES_CDS_MAX_FULL_NAME_LEN]; /**< \brief Processor Unique Name of CDS */ + uint8 ByteAlignSpare[3]; /**< \brief Spare bytes to ensure structure size is multiple of 4 bytes */ +} CFE_ES_CDSRegDumpRec_t; + +/** + * \brief Block statistics + * + * Sub-Structure that is used to provide information about a specific + * block size/bucket within a memory pool. + */ +typedef struct CFE_ES_BlockStats +{ + CFE_ES_MemOffset_t BlockSize; /**< \brief Number of bytes in each of these blocks */ + uint32 NumCreated; /**< \brief Number of Memory Blocks of this size created */ + uint32 NumFree; /**< \brief Number of Memory Blocks of this size that are free */ +} CFE_ES_BlockStats_t; + +/** + * \brief Memory Pool Statistics + * + * Structure that is used to provide information about a memory + * pool. Used by the Memory Pool Stats telemetry message. + * + * \sa #CFE_ES_SEND_MEM_POOL_STATS_CC + */ +typedef struct CFE_ES_MemPoolStats +{ + CFE_ES_MemOffset_t PoolSize; /**< \cfetlmmnemonic \ES_POOLSIZE + \brief Size of Memory Pool (in bytes) */ + uint32 NumBlocksRequested; /**< \cfetlmmnemonic \ES_BLKSREQ + \brief Number of times a memory block has been allocated */ + uint32 CheckErrCtr; /**< \cfetlmmnemonic \ES_BLKERRCTR + \brief Number of errors detected when freeing a memory block */ + CFE_ES_MemOffset_t NumFreeBytes; /**< \cfetlmmnemonic \ES_FREEBYTES + \brief Number of bytes never allocated to a block */ + CFE_ES_BlockStats_t BlockStats[CFE_MISSION_ES_POOL_MAX_BUCKETS]; /**< \cfetlmmnemonic \ES_BLKSTATS + \brief Contains stats on each block size */ +} CFE_ES_MemPoolStats_t; + +#endif /* CFE_EDS_ENABLED_BUILD */ + +#endif /* CFE_ES_EXTERN_TYPEDEFS_H */ diff --git a/modules/core_private/fsw/inc/cfe_es_erlog_typedef.h b/modules/core_private/fsw/inc/cfe_es_erlog_typedef.h new file mode 100644 index 000000000..ee1810e77 --- /dev/null +++ b/modules/core_private/fsw/inc/cfe_es_erlog_typedef.h @@ -0,0 +1,98 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/** + * @file + * + * Definition of the CFE_ES_ERLog structure type. + * This was moved into its own header file since it is referenced by multiple CFE core apps. + */ + +#ifndef CFE_ES_ERLOG_TYPEDEF_H +#define CFE_ES_ERLOG_TYPEDEF_H + +#include "common_types.h" +#include "cfe_platform_cfg.h" + +#include "cfe_time_extern_typedefs.h" /* Needed for CFE_TIME_SysTime_t */ +#include "cfe_es_extern_typedefs.h" /* Needed for CFE_ES_AppId_t */ + +#define CFE_ES_ERLOG_DESCRIPTION_MAX_LENGTH 80 + +/* +** Debug variables type +*/ +typedef struct +{ + uint32 DebugFlag; + uint32 WatchdogWriteFlag; + uint32 PrintfEnabledFlag; + uint32 LastAppId; + +} CFE_ES_DebugVariables_t; + +/* +** Exception and Reset Log Base Structure +** +** This is the common data structure that is stored in RAM and log files +*/ +typedef struct +{ + uint32 LogEntryType; /* What type of log entry */ + uint32 ResetType; /* Main cause for the reset */ + uint32 ResetSubtype; /* The sub-type for the reset */ + uint32 BootSource; /* The boot source */ + uint32 ProcessorResetCount; /* The number of processor resets */ + uint32 MaxProcessorResetCount; /* The maximum number before a Power On */ + CFE_ES_DebugVariables_t DebugVars; /* ES Debug variables */ + CFE_TIME_SysTime_t TimeCode; /* Time code */ + char Description[CFE_ES_ERLOG_DESCRIPTION_MAX_LENGTH]; /* The ascii data for the event */ +} CFE_ES_ERLog_BaseInfo_t; + +/* +** Exception and Reset Log File Structure +** +** This is the "export" data structure that gets written to a log file +** It is intended to be binary-compatible with the historical definition of this +** structure, to work with existing tools that may read log files. +** +** Note that "AppID" really belongs in the base info, but it is kept here +** for backward compatibility. +*/ +typedef struct +{ + CFE_ES_ERLog_BaseInfo_t BaseInfo; /* basic info about the event */ + uint32 ContextSize; /* Indicates the context data is valid */ + uint32 AppID; /* The application ID */ + uint8 Context[CFE_PLATFORM_ES_ER_LOG_MAX_CONTEXT_SIZE]; /* cpu context */ +} CFE_ES_ERLog_FileEntry_t; + +/* +** Exception and Reset Log Metadata Structure +** This is stored in ES RAM, not _directly_ written to ER log files. +*/ +typedef struct +{ + CFE_ES_ERLog_BaseInfo_t BaseInfo; /**< Core Log Data */ + CFE_ES_AppId_t AppID; /* The application ID */ + uint32 PspContextId; /**< Reference to context information stored in PSP */ +} CFE_ES_ERLog_MetaData_t; + +#endif /* CFE_ES_ERLOG_TYPEDEF_H */ diff --git a/modules/core_private/fsw/inc/cfe_es_perfdata_typedef.h b/modules/core_private/fsw/inc/cfe_es_perfdata_typedef.h new file mode 100644 index 000000000..35d635c8f --- /dev/null +++ b/modules/core_private/fsw/inc/cfe_es_perfdata_typedef.h @@ -0,0 +1,75 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/** + * @file + * + * Definition of CFE_ES_PerfData_t structure type + */ + +#ifndef CFE_ES_PERFDATA_TYPEDEF_H +#define CFE_ES_PERFDATA_TYPEDEF_H + +#include "common_types.h" +#include "cfe_mission_cfg.h" /* Required for CFE_MISSION_ES_PERF_MAX_IDS */ +#include "cfe_platform_cfg.h" /* Required for CFE_PLATFORM_ES_PERF_DATA_BUFFER_SIZE */ + +#define CFE_ES_PERF_32BIT_WORDS_IN_MASK ((CFE_MISSION_ES_PERF_MAX_IDS) / 32) + +typedef struct +{ + uint32 Data; + uint32 TimerUpper32; /* TBU - timebase register */ + uint32 TimerLower32; /* TBL - timebase register */ +} CFE_ES_PerfDataEntry_t; + +typedef struct +{ + uint8 Version; + uint8 Endian; + uint8 Spare[2]; + uint32 TimerTicksPerSecond; + uint32 TimerLow32Rollover; + /* + * The "State" member is marked volatile to help + * ensure that an optimizing compiler does not rearrange + * or eliminate reads/writes of this value. It is read + * outside of any locking to determine whether or not + * the performance log function is enabled. + */ + volatile uint32 State; + uint32 Mode; + uint32 TriggerCount; + uint32 DataStart; + uint32 DataEnd; + uint32 DataCount; + uint32 InvalidMarkerReported; + uint32 FilterTriggerMaskSize; + uint32 FilterMask[CFE_ES_PERF_32BIT_WORDS_IN_MASK]; + uint32 TriggerMask[CFE_ES_PERF_32BIT_WORDS_IN_MASK]; +} CFE_ES_PerfMetaData_t; + +typedef struct +{ + CFE_ES_PerfMetaData_t MetaData; + CFE_ES_PerfDataEntry_t DataBuffer[CFE_PLATFORM_ES_PERF_DATA_BUFFER_SIZE]; +} CFE_ES_PerfData_t; + +#endif /* CFE_ES_PERFDATA_TYPEDEF_H */ diff --git a/modules/core_private/fsw/inc/cfe_es_resetdata_typedef.h b/modules/core_private/fsw/inc/cfe_es_resetdata_typedef.h new file mode 100644 index 000000000..5549c2890 --- /dev/null +++ b/modules/core_private/fsw/inc/cfe_es_resetdata_typedef.h @@ -0,0 +1,99 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/** + * @file + * + * Definition of the CFE_ES_ResetData structure type. + * This was moved into its own header file since it is referenced by multiple CFE core apps. + */ + +#ifndef CFE_ES_RESETDATA_TYPEDEF_H +#define CFE_ES_RESETDATA_TYPEDEF_H + +#include "common_types.h" + +#include "cfe_time_resetvars_typedef.h" /* Required for CFE_TIME_ResetVars_t definition */ +#include "cfe_es_erlog_typedef.h" /* Required for CFE_ES_ERLog_t definition */ +#include "cfe_es_perfdata_typedef.h" /* Required for CFE_ES_PerfData_t definition */ +#include "cfe_evs_log_typedef.h" /* Required for CFE_EVS_Log_t definition */ +#include "cfe_platform_cfg.h" /* CFE_PLATFORM_ES_ER_LOG_ENTRIES, CFE_PLATFORM_ES_SYSTEM_LOG_SIZE */ + +/* +** Reset Variables type +*/ +typedef struct +{ + uint32 ResetType; + uint32 ResetSubtype; + uint32 BootSource; + uint32 ES_CausedReset; + uint32 ProcessorResetCount; + uint32 MaxProcessorResetCount; +} CFE_ES_ResetVariables_t; + +/* +** Executive Services Global Reset Data type +** This is the special memory area for ES that is preserved +** on a processor reset. +*/ +typedef struct +{ + /* + ** Exception and Reset log declaration + */ + CFE_ES_ERLog_MetaData_t ERLog[CFE_PLATFORM_ES_ER_LOG_ENTRIES]; + uint32 ERLogIndex; + uint32 ERLogEntries; + uint32 LastAppId; + + /* + ** System Log declaration + */ + char SystemLog[CFE_PLATFORM_ES_SYSTEM_LOG_SIZE]; + size_t SystemLogWriteIdx; + size_t SystemLogEndIdx; + uint32 SystemLogMode; + uint32 SystemLogEntryNum; + + /* + ** Performance Data + */ + CFE_ES_PerfData_t Perf; + + /* + ** Reset Variables + */ + CFE_ES_ResetVariables_t ResetVars; + + /* + ** Time variables that are + ** preserved on a processor reset + */ + CFE_TIME_ResetVars_t TimeResetVars; + + /* + ** EVS Log and associated variables. This needs to be preserved on a processor reset. + */ + CFE_EVS_Log_t EVS_Log; + +} CFE_ES_ResetData_t; + +#endif /* CFE_ES_RESETDATA_TYPEDEF_H */ diff --git a/modules/es/CMakeLists.txt b/modules/es/CMakeLists.txt new file mode 100644 index 000000000..0e8ebe701 --- /dev/null +++ b/modules/es/CMakeLists.txt @@ -0,0 +1,39 @@ +################################################################## +# +# cFE Executive Services (ES) module CMake build recipe +# +################################################################## + +project(CFE_ES C) + +# Executive services source files +set(es_SOURCES + fsw/src/cfe_es_api.c + fsw/src/cfe_es_apps.c + fsw/src/cfe_es_backgroundtask.c + fsw/src/cfe_es_cds.c + fsw/src/cfe_es_cds_mempool.c + fsw/src/cfe_es_erlog.c + fsw/src/cfe_es_generic_pool.c + fsw/src/cfe_es_mempool.c + fsw/src/cfe_es_objtab.c + fsw/src/cfe_es_perf.c + fsw/src/cfe_es_resource.c + fsw/src/cfe_es_start.c + fsw/src/cfe_es_syslog.c + fsw/src/cfe_es_task.c +) +add_library(es STATIC ${es_SOURCES}) + +target_include_directories(es PUBLIC fsw/inc) +target_link_libraries(es PRIVATE core_private) + +# Add unit test coverage subdirectory +if (ENABLE_UNIT_TESTS) + add_subdirectory(ut-coverage) +endif (ENABLE_UNIT_TESTS) + +cfs_app_check_intf(${DEP} + cfe_es_msg.h + cfe_es_events.h +) diff --git a/modules/es/fsw/inc/cfe_es_events.h b/modules/es/fsw/inc/cfe_es_events.h new file mode 100644 index 000000000..5024fa38b --- /dev/null +++ b/modules/es/fsw/inc/cfe_es_events.h @@ -0,0 +1,1451 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/** + * @file + * + * Purpose: + * cFE Executive Services (ES) Event IDs + * + * References: + * Flight Software Branch C Coding Standard Version 1.0a + * cFE Flight Software Application Developers Guide + * + * Notes: + * + */ + +#ifndef CFE_ES_EVENTS_H +#define CFE_ES_EVENTS_H + +/* ************************** +** ****** Maximum EID. ****** +** ************************** +** The EID's below may not necessarily be in order, so it can be difficult to +** determine what the next EID is to use. When you add EID's, start with MAX_EID + 1 +** and when you're done adding, set this to the highest EID you used. It may +** be worthwhile to, on occasion, re-number the EID's to put them back in order. +*/ +#define CFE_ES_MAX_EID 92 + +/* +** ES task event message ID's. +*/ + +/** \brief 'cFE ES Initialized' +** \event 'cFE ES Initialized' +** +** \par Type: INFORMATION +** +** \par Cause: +** +** This event message is always automatically issued when the Executive Services +** Task completes its Initialization. +**/ +#define CFE_ES_INIT_INF_EID 1 /* start up message "informational" */ + +/** \brief 'cFE Version \%d.\%d.\%d chksm \%d, OSAL Version \%d.\%d' +** \event 'cFE Version \%d.\%d.\%d chksm \%d, OSAL Version \%d.\%d' +** +** \par Type: INFORMATION +** +** \par Cause: +** +** This event message is always automatically issued when the Executive Services +** Task completes its Initialization. +** +** The \c Version field identifies the tagged version for the cFE Build, the \c chksm field +** provides the 16-bit checksum of the cFE Build and the \c OSAL \c Version field identifies +** the version of the OS Abstraction Layer on which this particular version of the cFE was built. +**/ +#define CFE_ES_INITSTATS_INF_EID 2 + +/** \brief 'No-op command' +** \event 'No-op command' +** +** \par Type: INFORMATION +** +** \par Cause: +** +** This event message is always automatically issued in response +** to a cFE Executive Services \link #CFE_ES_NOOP_CC NO-OP command \endlink +**/ +#define CFE_ES_NOOP_INF_EID 3 /* processed command "informational" */ + +/** \brief 'Reset Counters command' +** \event 'Reset Counters command' +** +** \par Type: INFORMATION +** +** \par Cause: +** +** This event message is always automatically issued in response +** to a cFE Executive Services \link #CFE_ES_RESET_COUNTERS_CC Reset Counters command \endlink +**/ +#define CFE_ES_RESET_INF_EID 4 + +/** \brief 'Started \%s from \%s, AppID = \%d' +** \event 'Started \%s from \%s, AppID = \%d' +** +** \par Type: INFORMATION +** +** \par Cause: +** +** This event message is automatically issued upon successful completion of +** a cFE Executive Services \link #CFE_ES_START_APP_CC Start Application command \endlink +** +** The first \c 's' string identifies the name of the started Application, the +** second \c 's' string identifies the filename from which the Application was +** loaded and the \c AppId field specifies the Application ID assigned to the +** newly started Application by the cFE Executive Services. +**/ +#define CFE_ES_START_INF_EID 6 + +/** \brief 'Stop Application \%s Initiated.' +** \event 'Stop Application \%s Initiated.' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This event message is issued upon successful processing of the +** cFE Executive Services \link #CFE_ES_STOP_APP_CC Stop Application command \endlink +** Note that when this event is displayed, the Application is not deleted. ES has +** accepted the request to delete the application, and it will be deleted after the app exits +** it's main loop, or times out. +** +** The \c 's' field identifies the name of the Application that will be stopped. +**/ +#define CFE_ES_STOP_DBG_EID 7 + +/** \brief 'Stop Application \%s Completed.' +** \event 'Stop Application \%s Completed.' +** +** \par Type: INFORMATION +** +** \par Cause: +** +** This event message is issued when the cFE finishes deleting the cFE Application +** That was started when the \link #CFE_ES_STOP_APP_CC Stop Application command \endlink +** was issued. +** +** The \c 's' field identifies the name of the Application that was stopped. +*/ +#define CFE_ES_STOP_INF_EID 8 + +/** \brief 'Restart Application \%s Initiated.' +** \event 'Restart Application \%s Initiated.' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This event message is issued upon successful processing of the +** cFE Executive Services \link #CFE_ES_RESTART_APP_CC Restart Application command \endlink +** Note that when this event is displayed, the Application is not restarted. ES has +** accepted the request to restart the application, and it will be restarted after the app exits +** it's main loop, or times out. +** +** The \c 's' field identifies the name of the Application that will be restarted. +**/ +#define CFE_ES_RESTART_APP_DBG_EID 9 + +/** \brief 'Restart Application \%s Completed, AppID=%lu' +** \event 'Restart Application \%s Completed, AppID=%lu' +** +** \par Type: INFORMATION +** +** \par Cause: +** +** This event message is issued when the cFE finishes Restarting the cFE Application +** That was started when the \link #CFE_ES_RESTART_APP_CC Restart Application command \endlink +** was issued. +** +** The \c 's' field identifies the name of the Application that was restarted, and +** the %lu field identifies the new Application ID +*/ +#define CFE_ES_RESTART_APP_INF_EID 10 + +/** \brief 'Reload Application \%s Initiated.' +** \event 'Reload Application \%s Initiated.' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This event message is issued upon successful processing of the +** cFE Executive Services \link #CFE_ES_RELOAD_APP_CC Reload Application command \endlink +** Note that when this event is displayed, the Application is not reloaded. ES has +** accepted the request to reload the application, and it will be reloaded after the app exits +** it's main loop, or times out. +** +** The \c 's' field identifies the name of the Application that will be reloaded. +**/ +#define CFE_ES_RELOAD_APP_DBG_EID 11 + +/** \brief 'Reload Application \%s Completed, AppID=%lu' +** \event 'Reload Application \%s Completed, AppID=%lu' +** +** \par Type: INFORMATION +** +** \par Cause: +** +** This event message is issued when the cFE finishes Reloading the cFE Application +** That was started when the \link #CFE_ES_RELOAD_APP_CC Restart Application command \endlink +** was issued. +** +** The \c 's' field identifies the name of the Application that was reloaded, and +** the %lu field identifies the new Application ID +*/ +#define CFE_ES_RELOAD_APP_INF_EID 12 + +/** \brief 'Exit Application \%s Completed.' +** \event 'Exit Application \%s Completed.' +** +** \par Type: INFORMATION +** +** \par Cause: +** +** This event message is issued when the cFE finishes exiting/cleaning up an +** application that called the CFE_ES_ExitApp API with the CFE_ES_RunStatus_APP_EXIT parameter. +** When an App calls this API, the request is recorded and the Executive Services App will +** actually delete cFE Application before issuing this event message. +** +** The \c 's' field identifies the name of the Application that was exited. +*/ +#define CFE_ES_EXIT_APP_INF_EID 13 + +/** \brief 'Exit Application \%s Completed.' +** \event 'Exit Application \%s Completed.' +** +** \par Type: INFORMATION +** +** \par Cause: +** +** This event message is issued when the cFE finishes exiting/cleaning up an +** application that called the CFE_ES_ExitApp API with an ERROR condition. +** When an App calls this API, with the CFE_ES_RunStatus_APP_ERROR parameter, it indicates +** that the Application exited due to an error condition. The details of the +** error that occurred should be given by the Application through an event message, +** System Log entry, or both. +** The request is recorded and the Executive Services App will actually delete +** cFE Application before issuing this event message. +** +** The \c 's' field identifies the name of the Application that was exited. +*/ +#define CFE_ES_ERREXIT_APP_INF_EID 14 + +/** \brief 'Sent \%s application data' +** \event 'Sent \%s application data' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This event message is issued upon successful completion of the +** cFE Executive Services \link #CFE_ES_QUERY_ONE_CC Query One Application command \endlink +** +** The \c 's' field identifies the name of the Application whose Executive Services +** Application information has been telemetered. +**/ +#define CFE_ES_ONE_APP_EID 15 + +/** \brief 'App Info file written to \%s, Entries=\%d, FileSize=\%d' +** \event 'App Info file written to \%s, Entries=\%d, FileSize=\%d' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This event message is issued upon successful completion of the cFE Executive +** Services \link #CFE_ES_QUERY_ALL_CC Query All Applications command \endlink +** +** The \c 's' field identifies the name of the file to which all Executive Services Application +** data has been written. The \c Entries field identifies, in decimal, the number of Applications +** whose data was written and the \c FileSize field gives the total number of bytes written to the +** file. +**/ +#define CFE_ES_ALL_APPS_EID 16 + +/** \brief 'Cleared Executive Services log data' +** \event 'Cleared Executive Services log data' +** +** \par Type: INFORMATION +** +** \par Cause: +** +** This event message is generated upon successful completion of the cFE Executive +** Services \link #CFE_ES_CLEAR_SYSLOG_CC Clear System Log command \endlink +**/ +#define CFE_ES_SYSLOG1_INF_EID 17 + +/** \brief '\%s written:Size=\%d,Entries=\%d' +** \event '\%s written:Size=\%d,Entries=\%d' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This event message is generated when the System Log has been successfully written +** to a file after receiving the cFE Executive Services \link #CFE_ES_CLEAR_SYSLOG_CC Write Executive +** Services System Log command \endlink +** +** The \c 's' field identifies the name of the file written to, the \c Size field specifies, in decimal, +** the number of bytes written to the file and the \c Entries field identifies the number of System Log +** messages that were written. +**/ +#define CFE_ES_SYSLOG2_EID 18 + +/** \brief 'Cleared mode log data' +** \event 'Cleared mode log data' +** +** \par Type: INFORMATION +** +** \par Cause: +** +** This event message is generated upon successful completion of the cFE Executive +** Services \link #CFE_ES_CLEAR_ER_LOG_CC Clear Exception Reset Log command \endlink +**/ +#define CFE_ES_ERLOG1_INF_EID 19 + +/** \brief '\%s written:Size=\%d' +** \event '\%s written:Size=\%d' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This event message is generated when the Exception Reset Log has been successfully written +** to a file after receiving the cFE Executive Services \link #CFE_ES_WRITE_ER_LOG_CC Write Executive +** Services Exception Reset Log command \endlink +** +** The \c 's' field identifies the name of the file written to and the \c Size field specifies, in decimal, +** the number of bytes written to the file. +**/ +#define CFE_ES_ERLOG2_EID 20 + +/** \brief 'Invalid command pipe message ID: 0x\%X' +** \event 'Invalid command pipe message ID: 0x\%X' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated when a message has arrived on +** the cFE Executive Services Application's Message Pipe that has a +** Message ID that is neither #CFE_ES_SEND_HK_MID or #CFE_ES_CMD_MID. +** Most likely, the cFE Software Bus routing table has become corrupt +** and is sending messages targeted for other Applications to the cFE +** Executive Services Application. +** +** The \c ID field in the event message identifies +** the message ID (in hex) that was found in the message. +**/ +#define CFE_ES_MID_ERR_EID 21 /* invalid command packet "error" */ + +/** \brief 'Invalid ground command code: ID = 0x\%X, CC = \%d' +** \event 'Invalid ground command code: ID = 0x\%X, CC = \%d' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated when a message with the #CFE_ES_CMD_MID +** message ID has arrived but whose Command Code is not one of the command +** codes specified in \link #CFE_ES_NOOP_CC cfe_es.h \endlink. This +** problem is most likely to occur when: +** -# A Message ID meant for another Application became corrupted and was +** set equal to #CFE_ES_CMD_MID. +** -# The Command Code field in the Message became corrupted. +** -# The command database at the ground station has been corrupted. +** +** The \c ID field in the event message specifies the Message ID (in hex) and the +** \c CC field specifies the Command Code (in decimal) found in the message. +**/ +#define CFE_ES_CC1_ERR_EID 22 + +/** \brief 'Invalid cmd length: ID = 0x\%X, CC = \%d, Exp Len = \%d, Len = \%d' +** \event 'Invalid cmd length: ID = 0x\%X, CC = \%d, Exp Len = \%d, Len = \%d' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated when a message with the #CFE_ES_CMD_MID +** message ID has arrived but whose packet length does not match the expected +** length for the specified command code. +** +** The \c ID field in the event message specifies the Message ID (in hex), the \c CC field +** specifies the Command Code (in decimal), the \c Exp Len field specified the Expected +** Length (in decimal ), and \c Len specifies the message Length (in decimal) +** found in the message. +**/ +#define CFE_ES_LEN_ERR_EID 23 + +/** \brief 'Invalid cFE restart type \%d' +** \event 'Invalid cFE restart type \%d' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is issued when the cFE Executive Services receives a +** \link #CFE_ES_RESTART_CC cFE Restart Command \endlink whose parameter +** identifying the restart type is not equal to either #CFE_PSP_RST_TYPE_PROCESSOR +** or #CFE_PSP_RST_TYPE_POWERON. +** +** The 'd' field identifies the numeric, in decimal, of the restart type found +** in the received cFE Restart Command Packet. +**/ +#define CFE_ES_BOOT_ERR_EID 24 /* command specific "error" */ + +/** \brief 'Failed to start \%s from \%s, RC = \%08X' +** \event 'Failed to start \%s from \%s, RC = \%08X' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated for an error encountered in response +** to an Executive Services \link #CFE_ES_START_APP_CC Start Application Command \endlink. +** +** This message is a general failure when the command passes the parameter validation, but +** fails when a call to CFE_ES_AppCreate is called. +** +** The \c 's' term identifies the name of the Application that was attempted to start. +** The second \c 's' field specifies the file from which the Application was loaded. +** The \c 'X' field is the return code returned by the CFE_ES_AppCreate. +**/ +#define CFE_ES_START_ERR_EID 26 + +/** \brief 'CFE_ES_StartAppCmd: invalid filename: \%s' +** \event 'CFE_ES_StartAppCmd: invalid filename: \%s' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated for an error encountered in response +** to an Executive Services \link #CFE_ES_START_APP_CC Start Application Command \endlink. +** +** This message reports a command failure when the Start Appplication Command is given +** an invalid filename. ( Either NULL or too short to be a valid cFE file name ). +** +** The \c 's' term identifies the invalid filename that was sent with the command. +**/ +#define CFE_ES_START_INVALID_FILENAME_ERR_EID 27 + +/** \brief 'CFE_ES_StartAppCmd: App Entry Point is NULL.' +** \event 'CFE_ES_StartAppCmd: App Entry Point is NULL.' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated for an error encountered in response +** to an Executive Services \link #CFE_ES_START_APP_CC Start Application Command \endlink. +** +** This message reports a command failure when the Start Appplication Command is given +** a NULL Application Entry Point parameter. The command must contain an application entry +** point string. ( Example: "SC_AppMain" ). +** +**/ +#define CFE_ES_START_INVALID_ENTRY_POINT_ERR_EID 28 + +/** \brief 'CFE_ES_StartAppCmd: App Name is NULL.' +** \event 'CFE_ES_StartAppCmd: App Name is NULL.' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated for an error encountered in response +** to an Executive Services \link #CFE_ES_START_APP_CC Start Application Command \endlink. +** +** This message reports a command failure when the Start Appplication Command is given +** a NULL Application Name parameter. The command must contain an application name string. +**/ +#define CFE_ES_START_NULL_APP_NAME_ERR_EID 29 + +/** \brief 'CFE_ES_StartAppCmd: Priority is too large: \%d.' +** \event 'CFE_ES_StartAppCmd: Priority is too large: \%d.' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated for an error encountered in response +** to an Executive Services \link #CFE_ES_START_APP_CC Start Application Command \endlink. +** +** This message reports a command failure when the Application priority greater than the +** maximum priority for a Task defined by the OS Abstraction Layer ( 256 ). +** +** The \c 'd' term identifies the priority that was given in the command. +**/ +#define CFE_ES_START_PRIORITY_ERR_EID 31 + +/** \brief 'CFE_ES_StartAppCmd: Invalid Exception Action: \%d.' +** \event 'CFE_ES_StartAppCmd: Invalid Exception Action: \%d.' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated for an error encountered in response +** to an Executive Services \link #CFE_ES_START_APP_CC Start Application Command \endlink. +** +** This message reports a command failure when the Application Exception Action parameter is +** invalid. The valid options for this parameter are: 0 = Application will restart on an exception +** 1 = Application cause a processor restart on +** exception. +** +** The \c 'd' term identifies the Exception Action parameter that was given in the command. +**/ +#define CFE_ES_START_EXC_ACTION_ERR_EID 32 + +/** \brief 'Exit Application \%s on Error Failed: CleanUpApp Error 0x\%08X.' +** \event 'Exit Application \%s on Error Failed: CleanUpApp Error 0x\%08X.' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated when ES is completing the processing of the +** CFE_ES_ExitApp API call with the CFE_ES_RunStatus_APP_ERROR parameter and the call to CFE_ES_CleanUpApp fails. +** At this point the Application will likely be stopped or deleted, but it may be in an unknown state. +** +** The \c 's' field identifies the name of the Application which was attempted to be +** reloaded and the \c RC field identifies the error code, in hex, that will identify +** the precise reason for the failure. +**/ +#define CFE_ES_ERREXIT_APP_ERR_EID 33 + +/** \brief 'Stop Application \%s Failed, RC = 0x\%08X' +** \event 'Stop Application \%s Failed, RC = 0x\%08X' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated upon receipt of an Executive Services +** \link #CFE_ES_STOP_APP_CC Stop Application Command \endlink which fails. +** +** The \c 's' field identifies the name of the Application which was attempted to be +** stopped and the \c rc field identifies the error code, in hex, that may identify +** the precise reason for the failure. +**/ +#define CFE_ES_STOP_ERR1_EID 35 + +/** \brief 'Stop Application \%s, GetAppIDByName failed. RC = 0x\%08X.' +** \event 'Stop Application \%s, GetAppIDByName failed. RC = 0x\%08X.' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated upon receipt of an Executive Services +** \link #CFE_ES_STOP_APP_CC Stop Application Command \endlink which fails. This message +** is for a specific failure when the call to CFE_ES_GetAppIDByName fails. The application +** will not be deleted at this point. +** +** The \c 's' field identifies the name of the Application which was attempted to be +** stopped and the \c RC field identifies the error code, in hex, that will identify +** the precise reason for the failure. +**/ +#define CFE_ES_STOP_ERR2_EID 36 + +/* +** "Stop Application \%s Failed: CleanUpApp Error 0x\%08X." +*/ + +/** \brief 'Stop Application \%s Failed: CleanUpApp Error 0x\%08X.' +** \event 'Stop Application \%s Failed: CleanUpApp Error 0x\%08X.' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated upon receipt of an Executive Services +** \link #CFE_ES_STOP_APP_CC Stop Application Command \endlink which fails. This message +** is for a specific failure when the call to CFE_ES_GetAppIDByName fails. The application +** will not be deleted at this point. +** +** The \c 's' field identifies the name of the Application which was attempted to be +** stopped and the \c RC field identifies the error code, in hex, that will identify +** the precise reason for the failure. +**/ +#define CFE_ES_STOP_ERR3_EID 37 + +/** \brief 'Restart Application \%s Failed, RC = 0x\%08X' +** \event 'Restart Application \%s Failed, RC = 0x\%08X' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated when an Executive Services \link #CFE_ES_RESTART_APP_CC Restart Application +** Command \endlink fails. +** +** The \c 's' field identifies the name of the Application which was attempted to be +** reset and the \c rc field identifies the error code, in hex, that may identify +** the precise reason for the failure. +**/ +#define CFE_ES_RESTART_APP_ERR1_EID 38 + +/** \brief 'Restart Application \%s, GetAppIDByName failed. RC = 0x\%08X.' +** \event 'Restart Application \%s, GetAppIDByName failed. RC = 0x\%08X.' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated upon receipt of an Executive Services +** \link #CFE_ES_RESTART_APP_CC Restart Application Command \endlink which fails. This message +** is for a specific failure when the call to CFE_ES_GetAppIDByName fails. The application +** will not be restarted at this point. +** +** The \c 's' field identifies the name of the Application which was attempted to be +** restarted and the \c RC field identifies the error code, in hex, that will identify +** the precise reason for the failure. +**/ +#define CFE_ES_RESTART_APP_ERR2_EID 39 + +/* +** "Restart Application \%s Failed: AppCreate Error 0x\%08X." +*/ + +/** \brief 'Restart Application \%s Failed: AppCreate Error 0x\%08X.' +** \event 'Restart Application \%s Failed: AppCreate Error 0x\%08X.' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated upon receipt of an Executive Services +** \link #CFE_ES_RESTART_APP_CC Restart Application Command \endlink which fails. This message +** is for a specific failure when the call to CFE_ES_AppCreate fails. The application +** will not be restarted at this point. +** +** The \c 's' field identifies the name of the Application which was attempted to be +** restarted and the \c RC field identifies the error code, in hex, that will identify +** the precise reason for the failure. +**/ +#define CFE_ES_RESTART_APP_ERR3_EID 40 + +/** \brief 'Restart Application \%s Failed: CleanUpApp Error 0x\%08X.' +** \event 'Restart Application \%s Failed: CleanUpApp Error 0x\%08X.' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated upon receipt of an Executive Services +** \link #CFE_ES_RESTART_APP_CC Restart Application Command \endlink which fails. This message +** is for a specific failure when the call to CFE_ES_CleanUpApp fails. The application +** will not be restarted at this point, but will likely be deleted or in an unknown state. +** +** The \c 's' field identifies the name of the Application which was attempted to be +** restarted and the \c RC field identifies the error code, in hex, that will identify +** the precise reason for the failure. +**/ +#define CFE_ES_RESTART_APP_ERR4_EID 41 + +/** \brief 'Failed to reload Application \%s, rc = \%08X' +** \event 'Failed to reload Application \%s, rc = \%08X' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated when an Executive Services \link #CFE_ES_RELOAD_APP_CC Reload Application +** Command \endlink fails. +** +** The \c 's' field identifies the name of the Application which was attempted to be +** reloaded and the \c rc field identifies the error code, in hex, that may identify +** the precise reason for the failure. +**/ +#define CFE_ES_RELOAD_APP_ERR1_EID 42 + +/** \brief 'Reload Application \%s, GetAppIDByName failed. RC = 0x\%08X.' +** \event 'Reload Application \%s, GetAppIDByName failed. RC = 0x\%08X.' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated upon receipt of an Executive Services +** \link #CFE_ES_RELOAD_APP_CC Reload Application Command \endlink which fails. This message +** is for a specific failure when the call to CFE_ES_GetAppIDByName fails. The application +** will not be reloaded at this point. +** +** The \c 's' field identifies the name of the Application which was attempted to be +** reloaded and the \c RC field identifies the error code, in hex, that will identify +** the precise reason for the failure. +**/ +#define CFE_ES_RELOAD_APP_ERR2_EID 43 + +/** \brief 'Reload Application \%s Failed: AppCreate Error 0x\%08X.' +** \event 'Reload Application \%s Failed: AppCreate Error 0x\%08X.' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated upon receipt of an Executive Services +** \link #CFE_ES_RELOAD_APP_CC Reload Application Command \endlink which fails. This message +** is for a specific failure when the call to CFE_ES_AppCreate fails. The application +** will not be reloaded at this point. +** +** The \c 's' field identifies the name of the Application which was attempted to be +** reloaded and the \c RC field identifies the error code, in hex, that will identify +** the precise reason for the failure. +**/ +#define CFE_ES_RELOAD_APP_ERR3_EID 44 + +/** \brief 'Reload Application \%s Failed: CleanUpApp Error 0x\%08X.' +** \event 'Reload Application \%s Failed: CleanUpApp Error 0x\%08X.' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated upon receipt of an Executive Services +** \link #CFE_ES_RELOAD_APP_CC Reload Application Command \endlink which fails. This message +** is for a specific failure when the call to CFE_ES_CleanUpApp fails. The application +** will not be reloaded at this point, and will likely be deleted or in an unknown state. +** +** The \c 's' field identifies the name of the Application which was attempted to be +** reloaded and the \c RC field identifies the error code, in hex, that will identify +** the precise reason for the failure. +**/ +#define CFE_ES_RELOAD_APP_ERR4_EID 45 + +/** \brief 'Exit Application \%s Failed: CleanUpApp Error 0x\%08X.' +** \event 'Exit Application \%s Failed: CleanUpApp Error 0x\%08X.' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated when ES is completing the processing of the +** CFE_ES_ExitApp API call and the call to CFE_ES_CleanUpApp fails. At this point the Application will +** likely be stopped or deleted, but it may be in an unknown state. +** +** The \c 's' field identifies the name of the Application which was attempted to be +** reloaded and the \c RC field identifies the error code, in hex, that will identify +** the precise reason for the failure. +**/ +#define CFE_ES_EXIT_APP_ERR_EID 46 + +/** \brief 'ES_ProcControlReq: Invalid State (EXCEPTION) Application \%s.' +** \event 'ES_ProcControlReq: Invalid State (EXCEPTION) Application \%s.' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated when ES is processing it's internal Application table and encounters +** an App with the EXCEPTION state. Because exceptions are supposed to be processed immediately, this is +** an invalid state and should not happen. It may indicate some sort of memory corruption or other problem. +**/ +#define CFE_ES_PCR_ERR1_EID 47 + +/* +** "CFE_ES_CleanUpApp: Unknown State ( \%d ) Application \%s." +*/ +/** \brief 'ES_ProcControlReq: Unknown State ( \%d ) Application \%s.' +** \event 'ES_ProcControlReq: Unknown State ( \%d ) Application \%s.' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated when ES is processing it's internal Application table and encounters +** an App with an unknown state. If this message occurs, it might be an indication of a memory corruption +** or other problem. +**/ +#define CFE_ES_PCR_ERR2_EID 48 + +/** \brief 'Failed to send \%s application data, RC = \%08X' +** \event 'Failed to send \%s application data, RC = \%08X' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated when an Executive Services \link #CFE_ES_QUERY_ONE_CC Request Application +** Data Command \endlink failed. +** +** The \c 's' field identifies the name of the Application whose data was attempted +** to be telemetered and the \c rc field identifies the error code, in hex, that may identify +** the precise reason for the failure. +**/ +#define CFE_ES_ONE_ERR_EID 49 + +/** \brief 'Failed to send \%s application data: GetAppIDByName Failed, RC = 0x\%08X' +** \event 'Failed to send \%s application data: GetAppIDByName Failed, RC = 0x\%08X' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated when an Executive Services \link #CFE_ES_QUERY_ONE_CC Request Application +** Data Command \endlink failed. +** +** The \c 's' field identifies the name of the Application whose data was attempted +** to be telemetered and the \c rc field identifies the error code, in hex, that may identify +** the precise reason for the failure. +**/ +#define CFE_ES_ONE_APPID_ERR_EID 50 + +/** \brief 'Failed to write App Info file, OS_OpenCreate returned \%d' +** \event 'Failed to write App Info file, OS_OpenCreate returned \%d' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated when an Executive Services \link #CFE_ES_QUERY_ALL_CC Dump Application +** Data Command \endlink fails to create the dump file. +** +** The \c 'd' parameter identifies, in decimal, the error code returned by #OS_OpenCreate when the attempt was made +** to create the file. +**/ +#define CFE_ES_OSCREATE_ERR_EID 51 + +/** \brief 'Failed to write App Info file, WriteHdr rtnd \%08X, exp \%d' +** \event 'Failed to write App Info file, WriteHdr rtnd \%08X, exp \%d' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated when an Executive Services \link #CFE_ES_QUERY_ALL_CC Dump Application +** Data Command \endlink fails while writing the cFE Standard File Header. +** +** The \c rtnd field contains the error code returned by the #CFE_FS_WriteHeader API. Nominally, the +** returned result should have been equal to the \c exp field (i.e. - sizeof(#CFE_FS_Header_t)). +**/ +#define CFE_ES_WRHDR_ERR_EID 52 + +/** \brief 'Failed to write App Info file, Task write RC = 0x\%08X, exp \%d' +** \event 'Failed to write App Info file, Task write RC = 0x\%08X, exp \%d' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated whenever an Executive Services \link #CFE_ES_QUERY_ALL_CC Dump Application +** Data Command \endlink fails while writing Application data to the specified file. +** +** The \c rtnd field contains, in hex, the error code returned from the #OS_write API. The expected return +** value is identified, in decimal, in the \c exp field. +**/ +#define CFE_ES_TASKWR_ERR_EID 53 + +/** \brief 'Error creating file \%s, stat=0x\%x' +** \event 'Error creating file \%s, stat=0x\%x' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated when an Executive Services \link #CFE_ES_WRITE_SYSLOG_CC Dump System Log +** Command \endlink fails while attempting to create the specified file. +** +** The \c 's' field identifies the name of the file that was attempted to be created and the \c stat field +** specifies, in hex, the error code returned by the #OS_OpenCreate API. +**/ +#define CFE_ES_SYSLOG2_ERR_EID 55 + +/** \brief 'Error creating file \%s, stat=0x\%x' +** \event 'Error creating file \%s, stat=0x\%x' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated when an Executive Services \link #CFE_ES_WRITE_ER_LOG_CC Dump Exception Reset Log +** Command \endlink fails while attempting to create the specified file. +** +** The \c 's' field identifies the name of the file that was attempted to be created and the \c stat field +** specifies, in hex, the error code returned by the #OS_OpenCreate API. +**/ +#define CFE_ES_ERLOG2_ERR_EID 56 + +/** \brief 'Start collecting performance data command, trigger mode = %d' +** \event 'Start collecting performance data command, trigger mode = %d' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This event message is generated in response to receiving an Executive Services +** \link #CFE_ES_START_PERF_DATA_CC Start Performance Analyzer Data Collection Command \endlink +** +** The \c 'd' field identifies the requested trigger mode as defined by CFE_ES_PerfMode_t. +**/ +#define CFE_ES_PERF_STARTCMD_EID 57 + +/** \brief 'Cannot start collecting performance data,perf data write in progress' +** \event 'Cannot start collecting performance data,perf data write in progress' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated in response to receiving an Executive Services +** \link #CFE_ES_START_PERF_DATA_CC Start Performance Analyzer Data Collection Command \endlink +**/ +#define CFE_ES_PERF_STARTCMD_ERR_EID 58 + +/** \brief 'Cannot start collecting performance data, trigger mode (%d) out of range (%d to %d)' +** \event 'Cannot start collecting performance data, trigger mode (%d) out of range (%d to %d)' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated when an Executive Services \link #CFE_ES_START_PERF_DATA_CC +** Start Performance Analyzer Data Collection Command \endlink command is received with a bad +** value for the requested trigger mode. +** +** The first \c 'd' field identifies the received trigger mode value as defined by CFE_ES_PerfMode_t. +** The second and third \c 'd' fields specify the valid range of values for the trigger mode. +**/ +#define CFE_ES_PERF_STARTCMD_TRIG_ERR_EID 59 + +/** \brief 'Perf Stop Cmd Rcvd,\%s will write \%d entries.\%dmS dly every \%d entries' +** \event 'Perf Stop Cmd Rcvd,\%s will write \%d entries.\%dmS dly every \%d entries' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This event message is generated upon receipt of a successful Performance Data Stop +** Command after receiving the cFE Executive Services \link #CFE_ES_STOP_PERF_DATA_CC Stop +** Performance Analyzer Data Collection Command \endlink +** +** The \c 's' field identifies the name of the file write task that has begun execution. +** The first \c 'd' identifies the total number of performance entries(in decimal) that will be written to the file. +** A performance data entry is defined by an unsigned 32 bit data point and an unsigned 64 bit time stamp. +** The second \c 'd' identifies the millisecond delay between writes and the +** third \c 'd' identifies the number of entries written (in decimal) between delays. +**/ +#define CFE_ES_PERF_STOPCMD_EID 60 + +/** \brief 'Stop performance data cmd ignored,perf data write in progress' +** \event 'Stop performance data cmd ignored,perf data write in progress' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated upon receipt of an unsuccessful Performance Data Stop +** Command after receiving the cFE Executive Services \link #CFE_ES_STOP_PERF_DATA_CC Stop +** Performance Analyzer Data Collection Command \endlink +** +**/ +#define CFE_ES_PERF_STOPCMD_ERR2_EID 62 + +/** \brief 'Set Performance Filter Mask command' +** \event 'Set Performance Filter Mask command' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This event message is generated in response to receiving an Executive Services +** \link #CFE_ES_SET_PERF_FILTER_MASK_CC Set Performance Analyzer Filter Mask Command \endlink. +** +**/ +#define CFE_ES_PERF_FILTMSKCMD_EID 63 + +/** \brief 'Performance Filter Mask Cmd Error,Index(%u)out of range(%u)' +** \event 'Performance Filter Mask Cmd Error,Index(%u)out of range(%u)' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated in response to receiving an Executive Services +** \link #CFE_ES_SET_PERF_FILTER_MASK_CC Set Performance Analyzer Filter Mask Command \endlink. +** +**/ +#define CFE_ES_PERF_FILTMSKERR_EID 64 + +/** \brief 'Set Performance Trigger Mask command' +** \event 'Set Performance Trigger Mask command' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This event message is generated in response to receiving an Executive Services +** \link #CFE_ES_SET_PERF_TRIGGER_MASK_CC Set Performance Analyzer Trigger Mask Command \endlink. +** +**/ +#define CFE_ES_PERF_TRIGMSKCMD_EID 65 + +/** \brief 'Performance Trigger Mask Cmd Error,Index(%u)out of range(%u)' +** \event 'Performance Trigger Mask Cmd Error,Index(%u)out of range(%u)' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated in response to receiving an Executive Services +** \link #CFE_ES_SET_PERF_TRIGGER_MASK_CC Set Performance Analyzer Trigger Mask Command \endlink. +** +**/ +#define CFE_ES_PERF_TRIGMSKERR_EID 66 + +/** \brief 'Error creating file \%s, stat=\%d' +** \event 'Error creating file \%s, stat=\%d' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated when an Executive Services +** \link #CFE_ES_STOP_PERF_DATA_CC Stop Performance Analyzer Data Collection Command \endlink +** fails to create the associated logic analyzer dump file. +** +** The \c 's' field identifies the name of the file that was attempted to be created and the \c stat field +** specifies, in decimal, the error code returned by the #OS_OpenCreate API. +**/ +#define CFE_ES_PERF_LOG_ERR_EID 67 + +/** \brief '\%s written:Size=\%d,EntryCount=\%d' +** \event '\%s written:Size=\%d,EntryCount=\%d' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This event message is generated when the Performance Log has been successfully written +** to a file after receiving the cFE Executive Services \link #CFE_ES_STOP_PERF_DATA_CC Stop +** Performance Analyzer Data Collection Command \endlink +** +** The \c 's' field identifies the name of the file written to, the \c Size field specifies, in decimal, +** the number of bytes written to the file and the \c EntryCount field identifies the number of data +** entries that were written. +**/ +#define CFE_ES_PERF_DATAWRITTEN_EID 68 + +/** \brief '\%s Failed to Register CDS '\%s', Status=0x\%08X' +** \event '\%s Failed to Register CDS '\%s', Status=0x\%08X' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated whenever an Application calls the #CFE_ES_RegisterCDS API and +** fails to successfully create the desired CDS. +** +** The first \c 's' field identifies the name of the Application which made the API call, the second +** \c 's' field specifies the name of the CDS as requested by the Application and the \c Status field +** provides the error code which identifies in more detail the nature of the failure (See return codes +** for the #CFE_ES_RegisterCDS API). +**/ +#define CFE_ES_CDS_REGISTER_ERR_EID 69 + +/** \brief 'Set OverWriteSysLog Command Received with Mode setting = \%d' +** \event 'Set OverWriteSysLog Command Received with Mode setting = \%d' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This event message is generated upon successful completion of an Executive Services \link +*#CFE_ES_OVER_WRITE_SYSLOG_CC +** Set System Log Overwrite Mode Command \endlink. +** +** The \c setting field identifies the newly chosen Overwrite Mode and should be equal to either +** #CFE_ES_LogMode_OVERWRITE or #CFE_ES_LogMode_DISCARD. +**/ +#define CFE_ES_SYSLOGMODE_EID 70 + +/** \brief 'Set OverWriteSysLog Command: Invalid Mode setting = \%d' +** \event 'Set OverWriteSysLog Command: Invalid Mode setting = \%d' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated upon unsuccessful completion of an Executive Services \link +*#CFE_ES_OVER_WRITE_SYSLOG_CC +** Set System Log Overwrite Mode Command \endlink. +** +** The \c setting field identifies the illegal Overwrite Mode found in the command message. The mode +** must be either #CFE_ES_LogMode_OVERWRITE (0) or #CFE_ES_LogMode_DISCARD (1). +**/ +#define CFE_ES_ERR_SYSLOGMODE_EID 71 + +/** \brief 'Reset Processor Reset Count to Zero' +** \event 'Reset Processor Reset Count to Zero' +** +** \par Type: INFORMATION +** +** \par Cause: +** +** This event message is always generated in response to the Executive Services +** \link #CFE_ES_RESET_PR_COUNT_CC Set Processor Reset Counter to Zero Command \endlink. +**/ +#define CFE_ES_RESET_PR_COUNT_EID 72 + +/** \brief 'Maximum Processor Reset Count set to: \%d' +** \event 'Maximum Processor Reset Count set to: \%d' +** +** \par Type: INFORMATION +** +** \par Cause: +** +** This event message is always generated in response to the Executive Services +** \link #CFE_ES_RESET_PR_COUNT_CC Set Maximum Processor Reset Limit Command \endlink. +** +** The \c 'd' field identifies, in decimal, the number of Processor Resets that will need +** to occur before a Power-On Reset is automatically performed. +**/ +#define CFE_ES_SET_MAX_PR_COUNT_EID 73 + +/** \brief 'File write,byte cnt err,file \%s,request=\%d,actual=\%d' +** \event 'File write,byte cnt err,file \%s,request=\%d,actual=\%d' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated in response to any command requesting information to be written +** to a file and whose data is not completely written to the specified file. +** +** The \c file field identifies the filename of the file to which the data failed to write completely, +** the \c request field specifies, in decimal, the number of bytes that were attempted to be written and +** the \c actual field indicates, in decimal, the actual number of bytes written to the file. +**/ +#define CFE_ES_FILEWRITE_ERR_EID 74 + +/** \brief 'Error while deleting '\%s' from CDS, See SysLog.(Err=0x\%08X)' +** \event 'Error while deleting '\%s' from CDS, See SysLog.(Err=0x\%08X)' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated when an Executive Services +** \link #CFE_ES_DELETE_CDS_CC Delete CDS Command \endlink +** fails to cleanly remove the specified CDS. +** +** The \c 's' field identifies the name of the CDS that was attempted to be deleted the \c Err field +** specifies, in hex, the error code. +**/ +#define CFE_ES_CDS_DELETE_ERR_EID 76 + +/** \brief 'Unable to locate '\%s' in CDS Registry' +** \event 'Unable to locate '\%s' in CDS Registry' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated when an Executive Services +** \link #CFE_ES_DELETE_CDS_CC Delete CDS Command \endlink +** specifies a name for a CDS that cannot be found in the CDS Registry. +** +** The \c 's' field identifies the name of the CDS that was attempted to be deleted. +**/ +#define CFE_ES_CDS_NAME_ERR_EID 77 + +/** \brief 'Successfully removed '\%s' from CDS' +** \event 'Successfully removed '\%s' from CDS' +** +** \par Type: INFORMATION +** +** \par Cause: +** +** This event message is generated when an Executive Services +** \link #CFE_ES_DELETE_CDS_CC Delete CDS Command \endlink +** is successfully completed. +** +** The \c 's' field identifies the name of the CDS that was deleted. +**/ +#define CFE_ES_CDS_DELETED_INFO_EID 78 + +/** \brief 'CDS '\%s' is a Critical Table CDS. Must be deleted via TBL Command' +** \event 'CDS '\%s' is a Critical Table CDS. Must be deleted via TBL Command' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated when an Executive Services +** \link #CFE_ES_DELETE_CDS_CC Delete CDS Command \endlink +** specifies a name for a CDS that is a Critical Table image. +** Critical Table images can only be deleted via a Table Services +** command (#CFE_TBL_DELETE_CDS_CC). +** +** The \c 's' field identifies the name of the CDS that was attempted to be deleted. +**/ +#define CFE_ES_CDS_DELETE_TBL_ERR_EID 79 + +/** \brief 'CDS '\%s' not deleted because owning app is active' +** \event 'CDS '\%s' not deleted because owning app is active' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated when an Executive Services +** \link #CFE_ES_DELETE_CDS_CC Delete CDS Command \endlink +** specifies a name for a CDS whose prefix name identifies +** an application that is still registered in the system. +** CDSs can only be deleted when their owning applications +** have been removed from the system. +** +** The \c 's' field identifies the name of the CDS that was attempted to be deleted. +**/ +#define CFE_ES_CDS_OWNER_ACTIVE_EID 80 + +/** \brief 'Successfully telemetered memory pool stats for 0x\%08X' +** \event 'Successfully telemetered memory pool stats for 0x\%08X' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This event message is generated following successful execution of the +** \link #CFE_ES_SEND_MEM_POOL_STATS_CC Telemeter Memory Statistics Command \endlink. +**/ +#define CFE_ES_TLM_POOL_STATS_INFO_EID 81 + +/** \brief 'Cannot telemeter memory pool stats. Illegal Handle (0x\%08X)' +** \event 'Cannot telemeter memory pool stats. Illegal Handle (0x\%08X)' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated when an Executive Services +** \link #CFE_ES_SEND_MEM_POOL_STATS_CC Telemeter Memory Statistics Command \endlink +** specifies a memory pool handle that is invalid. A handle is determined to +** be invalid when any of the following are true: +** -# The handle does not contain a value that is an integral multiple of 4 +** -# The handle does not specify a valid area of memory +** -# The handle does not point to an area of memory that contains the handle itself +** -# The handle does not point to an area of memory whose Size field is an integral multiple of 4 +** -# The handle does not point to an area of memory whose End field is equal to the Start plus the Size +** +** The \c '08X' field identifies the handle that was found in the command. +**/ +#define CFE_ES_INVALID_POOL_HANDLE_ERR_EID 82 + +/** \brief 'Successfully dumped CDS Registry to '\%s':Size=\%d,Entries=\%d' +** \event 'Successfully dumped CDS Registry to '\%s':Size=\%d,Entries=\%d' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This event message is generated when an Executive Services +** \link #CFE_ES_DUMP_CDS_REGISTRY_CC Dump Critical Data Store Registry Command \endlink +** is successfully executed. The specified file should have been created +** and contains the CDS Registry Entries. +** +** The \c 's' field identifies the CDS Registry Dump Filename. +** The first \c 'd' field specifies the size of the file (in bytes) +** The second \c 'd' field specifies the number of CDS Registry Records that were written +**/ +#define CFE_ES_CDS_REG_DUMP_INF_EID 83 + +/** \brief 'Error writing CDS Registry to '\%s', Status=0x\%08X' +** \event 'Error writing CDS Registry to '\%s', Status=0x\%08X' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated when an Executive Services +** \link #CFE_ES_DUMP_CDS_REGISTRY_CC Dump Critical Data Store Registry Command \endlink +** was being performed and it encountered a filesystem write error while writing +** a CDS Registry record. +** +** The \c 's' field identifies the CDS Registry Dump Filename. +** The \c '08X' field identifies the error code returned from #OS_write that caused the command to abort. +**/ +#define CFE_ES_CDS_DUMP_ERR_EID 84 + +/** \brief 'Error writing cFE File Header to '\%s', Status=0x\%08X' +** \event 'Error writing cFE File Header to '\%s', Status=0x\%08X' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated when an Executive Services +** \link #CFE_ES_DUMP_CDS_REGISTRY_CC Dump Critical Data Store Registry Command \endlink +** command successfully created the CDS Dump File onboard but encountered an error +** while writing the standard cFE File Header to the file. +** +** The \c 's' field identifies the CDS Registry Dump Filename. +** The \c '08X' field identifies error code returned by the API #CFE_FS_WriteHeader. +**/ +#define CFE_ES_WRITE_CFE_HDR_ERR_EID 85 + +/** \brief 'Error creating CDS dump file '\%s', Status=0x\%08X' +** \event 'Error creating CDS dump file '\%s', Status=0x\%08X' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated when an Executive Services +** \link #CFE_ES_DUMP_CDS_REGISTRY_CC Dump Critical Data Store Registry Command \endlink +** is unable to create the specified file on the onboard filesystem. +** +** The \c 's' field identifies the CDS Registry Dump Filename. +** The \c '08X' field identifies error code returned by the API #OS_OpenCreate. +**/ +#define CFE_ES_CREATING_CDS_DUMP_ERR_EID 86 + +/** \brief 'Task Info file written to \%s, Entries=\%d, FileSize=\%d' +** \event 'Task Info file written to \%s, Entries=\%d, FileSize=\%d' +** +** \par Type: DEBUG +** +** \par Cause: +** +** This event message is issued upon successful completion of the cFE Executive +** Services \link #CFE_ES_QUERY_ALL_TASKS_CC Query All Tasks command \endlink +** +** The \c 's' field identifies the name of the file to which all Executive Services Task +** data has been written. The \c Entries field identifies, in decimal, the number of Tasks +** whose data was written and the \c FileSize field gives the total number of bytes written to the +** file. +**/ +#define CFE_ES_TASKINFO_EID 87 + +/** \brief 'Failed to write Task Info file, OS_OpenCreate returned \%d' +** \event 'Failed to write Task Info file, OS_OpenCreate returned \%d' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated when an Executive Services \link #CFE_ES_QUERY_ALL_TASKS_CC Dump Task +** Data Command \endlink fails to create the dump file. +** +** The \c 'd' parameter identifies, in decimal, the error code returned by #OS_OpenCreate when the attempt was made +** to create the file. +**/ +#define CFE_ES_TASKINFO_OSCREATE_ERR_EID 88 + +/** \brief 'Failed to write Task Info file, WriteHdr rtnd \%08X, exp \%d' +** \event 'Failed to write Task Info file, WriteHdr rtnd \%08X, exp \%d' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated when an Executive Services \link #CFE_ES_QUERY_ALL_TASKS_CC Dump Task +** Data Command \endlink fails while writing the cFE Standard File Header. +** +** The \c rtnd field contains the error code returned by the #CFE_FS_WriteHeader API. Nominally, the +** returned result should have been equal to the \c exp field (i.e. - sizeof(#CFE_FS_Header_t)). +**/ +#define CFE_ES_TASKINFO_WRHDR_ERR_EID 89 + +/** \brief 'Failed to write Task Info file, Task write RC = 0x\%08X, exp \%d' +** \event 'Failed to write Task Info file, Task write RC = 0x\%08X, exp \%d' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated whenever an Executive Services \link #CFE_ES_QUERY_ALL_TASKS_CC Dump Tasks +** Data Command \endlink fails while writing Tasks data to the specified file. +** +** The \c rtnd field contains, in hex, the error code returned from the #OS_write API. The expected return +** value is identified, in decimal, in the \c exp field. +**/ +#define CFE_ES_TASKINFO_WR_ERR_EID 90 + +/** \brief 'Version Info: %s, %s' +** \event 'Version Info: %s, %s' +** +** \par Type: INFORMATION +** +** \par Cause: +** +** This event message is always automatically issued when the Executive Services +** Task completes its Initialization and as part of the Noop command +** +** A separate version info event will be generated for every module which is statically +** linked into the CFE core executable (e.g. OSAL, PSP, MSG, SBR, etc). +** +** The version information reported in this event is derived from the source revision +** control system at build time, as opposed to manually-assigned semantic version numbers. +** It is intendended to uniquely identify the actual source code that is currently running, +** to the extent this is possible. +** +** The \c Mission version information also identifies the build configuration name, if available. +**/ +#define CFE_ES_VERSION_INF_EID 91 + +/** \brief 'Build %s by %s@%s, config %s' +** \event 'Build %s by %s@%s, config %s' +** +** \par Type: INFORMATION +** +** \par Cause: +** +** This event message is always automatically issued when the Executive Services +** Task completes its Initialization, and as part of the Noop command. +** +** The \c Build field identifies the build date, time, hostname and user identifier of +** the build host machine for the current running binary. The first string is the +** build date/time, and the second string is formatted as "user@hostname" +** +** This additionally reports the configuration name that was selected by the user, +** which may affect various platform/mission limits. +** +** By default, if not specified/overridden, the default values of these variables will be: +** BUILDDATE ==> the output of "date +%Y%m%d%H%M" +** HOSTNAME ==> the output of "hostname" +** USER ==> the output of "whoami" +** +** The values can be overridden by setting an environment variable with the names above +** to the value desired for the field when running "make". +**/ +#define CFE_ES_BUILD_INF_EID 92 + +/** \brief 'Error log write to file \%s already in progress' +** \event 'Error log write to file \%s already in progress' +** +** \par Type: ERROR +** +** \par Cause: +** +** This event message is generated when an Executive Services \link #CFE_ES_WRITE_ER_LOG_CC Dump Exception Reset Log +** Command \endlink is received before a previously-issued command has finished executing +** +**/ +#define CFE_ES_ERLOG_PENDING_ERR_EID 93 + +#endif /* CFE_ES_EVENTS_H */ diff --git a/modules/es/fsw/inc/cfe_es_msg.h b/modules/es/fsw/inc/cfe_es_msg.h new file mode 100644 index 000000000..a52965c4c --- /dev/null +++ b/modules/es/fsw/inc/cfe_es_msg.h @@ -0,0 +1,1570 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/** + * @file + * + * Purpose: + * cFE Executive Services (ES) Command and Telemetry packet definition file. + * + * References: + * Flight Software Branch C Coding Standard Version 1.0a + * cFE Flight Software Application Developers Guide + * + * Notes: + * + */ + +#ifndef CFE_ES_MSG_H +#define CFE_ES_MSG_H + +/* +** Includes +*/ +#include "common_types.h" /* Basic data types */ +#include "cfe_msg_hdr.h" /* for header definitions */ +#include "cfe_es_extern_typedefs.h" + +/* +** ES task command packet command codes +*/ +/** \name Executive Services Command Codes */ +/** \{ */ +/** \cfeescmd Executive Services No-Op +** +** \par Description +** This command performs no other function than to increment the +** command execution counter. The command may be used to verify +** general aliveness of the Executive Services task. +** +** \cfecmdmnemonic \ES_NOOP +** +** \par Command Structure +** #CFE_ES_NoopCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with the +** following telemetry: +** - \b \c \ES_CMDPC - command execution counter will +** increment +** - The #CFE_ES_NOOP_INF_EID informational event message will +** be generated +** +** \par Error Conditions +** This command may fail for the following reason(s): +** - The command packet length is incorrect +** +** Evidence of failure may be found in the following telemetry: +** - \b \c \ES_CMDEC - command error counter will increment +** - the #CFE_ES_LEN_ERR_EID error event message will be generated +** +** \par Criticality +** None +** +** \sa +*/ +#define CFE_ES_NOOP_CC 0 + +/** \cfeescmd Executive Services Reset Counters +** +** \par Description +** This command resets the following counters within the Executive +** Services housekeeping telemetry: +** - Command Execution Counter +** - Command Error Counter +** +** \cfecmdmnemonic \ES_RESETCTRS +** +** \par Command Structure +** #CFE_ES_ResetCountersCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with +** the following telemetry: +** - \b \c \ES_CMDPC - command execution counter will +** increment +** - The #CFE_ES_RESET_INF_EID informational event message will be +** generated +** +** \par Error Conditions +** This command may fail for the following reason(s): +** - The command packet length is incorrect +** +** Evidence of failure may be found in the following telemetry: +** - \b \c \ES_CMDEC - command error counter will increment +** - the #CFE_ES_LEN_ERR_EID error event message will be generated +** +** \par Criticality +** This command is not inherently dangerous. However, it is +** possible for ground systems and on-board safing procedures +** to be designed such that they react to changes in the counter +** values that are reset by this command. +** +** \sa #CFE_ES_RESET_PR_COUNT_CC +*/ +#define CFE_ES_RESET_COUNTERS_CC 1 + +/** \cfeescmd Executive Services Processor / Power-On Reset +** +** \par Description +** This command restarts the cFE in one of two modes. The Power-On Reset +** will cause the cFE to restart as though the power were first applied +** to the processor. The Processor Reset will attempt to retain the contents +** of the volatile disk and the contents of the Critical Data Store. +** NOTE: If a requested Processor Reset should cause the +** Processor Reset Counter (\b \c \ES_PROCRESETCNT) +** to exceed OR EQUAL the limit #CFE_PLATFORM_ES_MAX_PROCESSOR_RESETS (which is reported in +** housekeeping telemetry as \b \c \ES_MAXPROCRESETS), +** the command is \b AUTOMATICALLY upgraded to a Power-On Reset. +** +** \cfecmdmnemonic \ES_RESET +** +** \par Command Structure +** #CFE_ES_RestartCmd_t +** +** \par Command Verification +** Successful execution of this command (as a Processor Reset) +** may be verified with the following telemetry: +** - \b \c \ES_PROCRESETCNT - processor reset counter will increment +** - New entries in the Exception Reset Log and System Log can be found
+** NOTE: Verification of a Power-On Reset is shown through the loss of +** data nominally retained through a Processor Reset
+** NOTE: Since the reset of the processor resets the command execution +** counter (\b \c \ES_CMDPC), this counter \b CANNOT be used to verify +** command execution. +** +** \par Error Conditions +** This command may fail for the following reason(s): +** - The command packet length is incorrect +** - The \link #CFE_ES_RestartCmd_Payload_t Restart Type \endlink was +** not a recognized value. +** +** Evidence of failure may be found in the following telemetry: +** - \b \c \ES_CMDEC - command error counter will increment +** - the #CFE_ES_BOOT_ERR_EID error event message will be generated +** +** \par Criticality +** This command is, by definition, dangerous. Significant loss of +** data will occur. All processes and the cFE itself will be stopped +** and restarted. With the Power-On reset option, all data on the +** volatile disk and the contents of the Critical Data Store will be +** lost. +** +** \sa #CFE_ES_RESET_PR_COUNT_CC, #CFE_ES_SET_MAX_PR_COUNT_CC +*/ +#define CFE_ES_RESTART_CC 2 + +/** \cfeescmd Load and Start an Application +** +** \par Description +** This command starts the specified application with the +** specified start address, stack size, etc options. +** +** \cfecmdmnemonic \ES_STARTAPP +** +** \par Command Structure +** #CFE_ES_StartAppCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with +** the following telemetry: +** - \b \c \ES_CMDPC - command execution counter will +** increment +** - The #CFE_ES_START_INF_EID informational event message will be +** generated +** +** \par Error Conditions +** This command may fail for the following reason(s): +** - The command packet length is incorrect +** - The specified application filename string is either a NULL string +** or less than four characters in length +** - The specified application entry point is a NULL string +** - The specified application name is a NULL string +** - The specified stack size is less than #CFE_PLATFORM_ES_DEFAULT_STACK_SIZE +** - The specified priority is greater than MAX_PRIORITY (as defined in osapi.c) +** - The specified exception action is neither #CFE_ES_ExceptionAction_RESTART_APP (0) or +** #CFE_ES_ExceptionAction_PROC_RESTART (1) +** - The Operating System was unable to load the specified application file +** +** Evidence of failure may be found in the following telemetry: +** - \b \c \ES_CMDEC - command error counter will increment +** - A command specific error event message is issued for all error +** cases +** +** \par Criticality +** This command is not inherently dangerous although system resources +** could be taxed beyond their limits with the starting of erroneous +** or invalid applications. +** +** \sa #CFE_ES_STOP_APP_CC, #CFE_ES_RESTART_APP_CC, #CFE_ES_RELOAD_APP_CC +*/ +#define CFE_ES_START_APP_CC 4 + +/** \cfeescmd Stop and Unload Application +** +** \par Description +** This command halts and removes the specified Application +** from the system. \b NOTE: This command should never be used +** on the Command Ingest application. This would prevent further +** commands from entering the system. If Command Ingest needs to +** be stopped and restarted, use #CFE_ES_RESTART_APP_CC or +** #CFE_ES_RELOAD_APP_CC. +** +** \cfecmdmnemonic \ES_STOPAPP +** +** \par Command Structure +** #CFE_ES_StopAppCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with +** the following telemetry: +** - \b \c \ES_CMDPC - command execution counter will +** increment +** - The #CFE_ES_STOP_DBG_EID debug event message will be +** generated. NOTE: This event message only identifies that the +** stop has been started, not that is has completed. +** - Once the stop has successfully completed, the list of Applications +** and Tasks created in response to the \b \c \ES_WRITEAPPINFO2FILE, +** \b \c \ES_WRITETASKINFO2FILE should no longer contain the +** specified application. +** +** \par Error Conditions +** This command may fail for the following reason(s): +** - The command packet length is incorrect +** - The specified application name is not recognized as an active application +** - The specified application is one of the cFE's Core applications (ES, EVS, SB, TBL, TIME) +** +** Evidence of failure may be found in the following telemetry: +** - \b \c \ES_CMDEC - command error counter will increment +** - A command specific error event message is issued for all error +** cases +** - Additional information on the reason for command failure may be found +** in the System Log +** +** \par Criticality +** This command is not inherently dangerous, however the removal of certain +** applications (e.g. - Spacecraft Attitude and Control) may have a detrimental effect +** on the spacecraft. +** +** \sa #CFE_ES_START_APP_CC, #CFE_ES_RESTART_APP_CC, #CFE_ES_RELOAD_APP_CC +*/ +#define CFE_ES_STOP_APP_CC 5 + +/** \cfeescmd Stops, Unloads, Loads using the previous File name, and Restarts an Application +** +** \par Description +** This command halts and removes the specified Application +** from the system. Then it immediately loads the Application from +** the same filename last used to start. This command is +** especially useful for restarting a Command Ingest Application +** since once it has been stopped, no further commands can come in +** to restart it. +** +** \cfecmdmnemonic \ES_RESTARTAPP +** +** \par Command Structure +** #CFE_ES_RestartAppCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with +** the following telemetry: +** - \b \c \ES_CMDPC - command execution counter will +** increment +** - The #CFE_ES_RESTART_APP_DBG_EID debug event message will be +** generated. NOTE: This event message only identifies that the +** act of stopping the application has begun, not that is has completed. +** +** \par Error Conditions +** This command may fail for the following reason(s): +** - The command packet length is incorrect +** - The original file is missing +** - The specified application name is not recognized as an active application +** - The specified application is one of the cFE's Core applications (ES, EVS, SB, TBL, TIME) +** +** Evidence of failure may be found in the following telemetry: +** - \b \c \ES_CMDEC - command error counter will increment +** - A command specific error event message is issued for all error +** cases +** - Additional information on the reason for command failure may be found +** in the System Log +** +** \par Criticality +** This command is not inherently dangerous, however the restarting of certain +** applications (e.g. - Spacecraft Attitude and Control) may have a detrimental effect +** on the spacecraft. +** +** \sa #CFE_ES_START_APP_CC, #CFE_ES_STOP_APP_CC, #CFE_ES_RELOAD_APP_CC +*/ +#define CFE_ES_RESTART_APP_CC 6 + +/** \cfeescmd Stops, Unloads, Loads from the command specfied File and Restarts an Application +** +** \par Description +** This command halts and removes the specified Application +** from the system. Then it immediately loads the Application from +** the command specified file and restarts it. This command is +** especially useful for restarting a Command Ingest Application +** since once it has been stopped, no further commands can come in +** to restart it. +** +** \cfecmdmnemonic \ES_RELOADAPP +** +** \par Command Structure +** #CFE_ES_ReloadAppCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with +** the following telemetry: +** - \b \c \ES_CMDPC - command execution counter will +** increment +** - The #CFE_ES_RELOAD_APP_DBG_EID debug event message will be +** generated. NOTE: This event message only identifies that the +** act of stopping the application has begun, not that is has completed. +** +** \par Error Conditions +** This command may fail for the following reason(s): +** - The command packet length is incorrect +** - The reload file is missing +** - The specified application name is not recognized as an active application +** - The specified application is one of the cFE's Core applications (ES, EVS, SB, TBL, TIME) +** +** Evidence of failure may be found in the following telemetry: +** - \b \c \ES_CMDEC - command error counter will increment +** - A command specific error event message is issued for all error +** cases +** - Additional information on the reason for command failure may be found +** in the System Log +** +** \par Criticality +** This command is not inherently dangerous, however the restarting of certain +** applications (e.g. - Spacecraft Attitude and Control) may have a detrimental effect +** on the spacecraft. +** +** \sa #CFE_ES_START_APP_CC, #CFE_ES_STOP_APP_CC, #CFE_ES_RESTART_APP_CC +*/ +#define CFE_ES_RELOAD_APP_CC 7 + +/** \cfeescmd Request Executive Services Information on a Specified Application +** +** \par Description +** This command takes the information kept by Executive Services on the +** specified application and telemeters it to the ground. +** +** \cfecmdmnemonic \ES_QUERYAPP +** +** \par Command Structure +** #CFE_ES_QueryOneCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with +** the following telemetry: +** - \b \c \ES_CMDPC - command execution counter will +** increment +** - The #CFE_ES_ONE_APP_EID debug event message will be +** generated. NOTE: This event message only identifies that the +** act of stopping the application has begun, not that is has completed. +** - Receipt of the #CFE_ES_OneAppTlm_t telemetry packet +** +** \par Error Conditions +** This command may fail for the following reason(s): +** - The command packet length is incorrect +** - The specified application name is not recognized as an active application +** +** Evidence of failure may be found in the following telemetry: +** - \b \c \ES_CMDEC - command error counter will increment +** - A command specific error event message is issued for all error +** cases +** +** \par Criticality +** None +** +** \sa #CFE_ES_QUERY_ALL_CC, #CFE_ES_QUERY_ALL_TASKS_CC +*/ +#define CFE_ES_QUERY_ONE_CC 8 + +/** \cfeescmd Writes all Executive Services Information on All Applications to a File +** +** \par Description +** This command takes the information kept by Executive Services on all of the +** registered applications and writes it to the specified file. +** +** \cfecmdmnemonic \ES_WRITEAPPINFO2FILE +** +** \par Command Structure +** #CFE_ES_QueryAllCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with +** the following telemetry: +** - \b \c \ES_CMDPC - command execution counter will +** increment +** - The #CFE_ES_ALL_APPS_EID debug event message will be +** generated. +** - The file specified in the command (or the default specified +** by the #CFE_PLATFORM_ES_DEFAULT_APP_LOG_FILE configuration parameter) will be +** updated with the lastest information. +** +** \par Error Conditions +** This command may fail for the following reason(s): +** - The command packet length is incorrect +** - An Error occurs while trying to write to the file +** +** Evidence of failure may be found in the following telemetry: +** - \b \c \ES_CMDEC - command error counter will increment +** - A command specific error event message is issued for all error +** cases +** +** \par Criticality +** This command is not inherently dangerous. It will create a new +** file in the file system (or overwrite an existing one) and could, +** if performed repeatedly without sufficient file management by the +** operator, fill the file system. +** +** \sa #CFE_ES_QUERY_ONE_CC, #CFE_ES_QUERY_ALL_TASKS_CC +*/ +#define CFE_ES_QUERY_ALL_CC 9 + +/** \cfeescmd Clear Executive Services System Log +** +** \par Description +** This command clears the contents of the Executive Services System Log. +** +** \cfecmdmnemonic \ES_CLEARSYSLOG +** +** \par Command Structure +** #CFE_ES_ClearSysLogCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with +** the following telemetry: +** - \b \c \ES_CMDPC - command execution counter will +** increment +** - The #CFE_ES_SYSLOG1_INF_EID informational event message will be +** generated. +** - \b \c \ES_SYSLOGBYTEUSED - System Log Bytes Used will go to zero +** - \b \c \ES_SYSLOGENTRIES - Number of System Log Entries will go to zero +** +** \par Error Conditions +** This command may fail for the following reason(s): +** - The command packet length is incorrect +** +** Evidence of failure may be found in the following telemetry: +** - \b \c \ES_CMDEC - command error counter will increment +** - A command specific error event message is issued for all error +** cases +** +** \par Criticality +** This command is not dangerous. However, any previously logged data +** will be lost. +** +** \sa #CFE_ES_WRITE_SYSLOG_CC, #CFE_ES_CLEAR_ER_LOG_CC, #CFE_ES_WRITE_ER_LOG_CC, +** #CFE_ES_OVER_WRITE_SYSLOG_CC +*/ +#define CFE_ES_CLEAR_SYSLOG_CC 10 + +/** \cfeescmd Writes contents of Executive Services System Log to a File +** +** \par Description +** This command causes the contents of the Executive Services System Log +** to be written to a log file. +** +** \cfecmdmnemonic \ES_WRITESYSLOG2FILE +** +** \par Command Structure +** #CFE_ES_WriteSysLogCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with +** the following telemetry: +** - \b \c \ES_CMDPC - command execution counter will +** increment +** - The #CFE_ES_SYSLOG2_EID debug event message will be +** generated. +** - The file specified in the command (or the default specified +** by the #CFE_PLATFORM_ES_DEFAULT_SYSLOG_FILE configuration parameter) will be +** updated with the lastest information. +** +** \par Error Conditions +** This command may fail for the following reason(s): +** - The command packet length is incorrect +** - An Error occurs while trying to write to the file +** +** Evidence of failure may be found in the following telemetry: +** - \b \c \ES_CMDEC - command error counter will increment +** - A command specific error event message is issued for all error +** cases +** +** \par Criticality +** This command is not inherently dangerous. It will create a new +** file in the file system (or overwrite an existing one) and could, +** if performed repeatedly without sufficient file management by the +** operator, fill the file system. +** +** \sa #CFE_ES_CLEAR_SYSLOG_CC, #CFE_ES_CLEAR_ER_LOG_CC, #CFE_ES_WRITE_ER_LOG_CC, +** #CFE_ES_OVER_WRITE_SYSLOG_CC +*/ +#define CFE_ES_WRITE_SYSLOG_CC 11 + +/** \cfeescmd Clears the contents of the Exeception and Reset Log +** +** \par Description +** This command causes the contents of the Executive Services Exception +** and Reset Log to be cleared. +** +** \cfecmdmnemonic \ES_CLEARERLOG +** +** \par Command Structure +** #CFE_ES_ClearERLogCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with +** the following telemetry: +** - \b \c \ES_CMDPC - command execution counter will +** increment +** - The #CFE_ES_ERLOG1_INF_EID informational event message will be +** generated. +** - \b \c \ES_ERLOGINDEX - Index into Exception Reset Log goes to zero +** +** \par Error Conditions +** This command may fail for the following reason(s): +** - The command packet length is incorrect +** +** Evidence of failure may be found in the following telemetry: +** - \b \c \ES_CMDEC - command error counter will increment +** - A command specific error event message is issued for all error +** cases +** +** \par Criticality +** This command is not dangerous. However, any previously logged data +** will be lost. +** +** \sa #CFE_ES_CLEAR_SYSLOG_CC, #CFE_ES_WRITE_SYSLOG_CC, #CFE_ES_WRITE_ER_LOG_CC +*/ +#define CFE_ES_CLEAR_ER_LOG_CC 12 + +/** \cfeescmd Writes Exeception and Reset Log to a File +** +** \par Description +** This command causes the contents of the Executive Services Exception +** and Reset Log to be written to the specified file. +** +** \cfecmdmnemonic \ES_WRITEERLOG2FILE +** +** \par Command Structure +** #CFE_ES_WriteERLogCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with +** the following telemetry: +** - \b \c \ES_CMDPC - command execution counter will +** increment +** - The #CFE_ES_ERLOG2_EID debug event message will be +** generated. +** - The file specified in the command (or the default specified +** by the #CFE_PLATFORM_ES_DEFAULT_ER_LOG_FILE configuration parameter) will be +** updated with the lastest information. +** +** \par Error Conditions +** This command may fail for the following reason(s): +** - The command packet length is incorrect +** - An Error occurs while trying to write to the file +** +** Evidence of failure may be found in the following telemetry: +** - \b \c \ES_CMDEC - command error counter will increment +** - A command specific error event message is issued for all error +** cases +** +** \par Criticality +** This command is not inherently dangerous. It will create a new +** file in the file system (or overwrite an existing one) and could, +** if performed repeatedly without sufficient file management by the +** operator, fill the file system. +** +** \sa #CFE_ES_CLEAR_SYSLOG_CC, #CFE_ES_WRITE_SYSLOG_CC, #CFE_ES_CLEAR_ER_LOG_CC +*/ +#define CFE_ES_WRITE_ER_LOG_CC 13 + +/** \cfeescmd Start Performance Analyzer +** +** \par Description +** This command causes the Performance Analyzer to begin collecting data using the specified trigger mode. +** +** \cfecmdmnemonic \ES_STARTLADATA +** +** \par Command Structure +** #CFE_ES_StartPerfDataCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with +** the following telemetry: +** - \b \c \ES_CMDPC - command execution counter will +** increment +** - \b \c \ES_PERFSTATE - Current performance analyzer state will change to +** either WAITING FOR TRIGGER or, if conditions are appropriate fast enough, +** TRIGGERED. +** - \b \c \ES_PERFMODE - Performance Analyzer Mode will change to the commanded trigger mode (TRIGGER START, +** TRIGGER CENTER, or TRIGGER END). +** - \b \c \ES_PERFTRIGCNT - Performance Trigger Count will go to zero +** - \b \c \ES_PERFDATASTART - Data Start Index will go to zero +** - \b \c \ES_PERFDATAEND - Data End Index will go to zero +** - \b \c \ES_PERFDATACNT - Performance Data Counter will go to zero +** - The #CFE_ES_PERF_STARTCMD_EID debug event message will be +** generated. +** +** \par Error Conditions +** This command may fail for the following reason(s): +** - The command packet length is incorrect +** - A previous #CFE_ES_STOP_PERF_DATA_CC command has not completely finished. +** - An invalid trigger mode is requested. +** +** Evidence of failure may be found in the following telemetry: +** - \b \c \ES_CMDEC - command error counter will increment +** - A command specific error event message is issued for all error +** cases +** +** \par Criticality +** This command is not inherently dangerous but may cause a small increase in CPU +** utilization as the performance analyzer data is collected. +** +** \sa #CFE_ES_STOP_PERF_DATA_CC, #CFE_ES_SET_PERF_FILTER_MASK_CC, #CFE_ES_SET_PERF_TRIGGER_MASK_CC +*/ +#define CFE_ES_START_PERF_DATA_CC 14 + +/** \cfeescmd Stop Performance Analyzer +** +** \par Description +** This command stops the Performance Analyzer from collecting any more data. +** +** \cfecmdmnemonic \ES_STOPLADATA +** +** \par Command Structure +** #CFE_ES_StopPerfDataCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with +** the following telemetry: +** - \b \c \ES_CMDPC - command execution counter will +** increment +** - \b \c \ES_PERFSTATE - Current performance analyzer state will change to +** IDLE. +** - The #CFE_ES_PERF_STOPCMD_EID debug event message will be +** generated. +** - The file specified in the command (or the default specified +** by the #CFE_PLATFORM_ES_DEFAULT_PERF_DUMP_FILENAME configuration parameter) will be +** updated with the lastest information. +** +** \par Error Conditions +** This command may fail for the following reason(s): +** - The command packet length is incorrect +** - A previous Stop Performance Analyzer command is still in process +** - An error occurred while spawning the child task responsible for +** dumping the Performance Analyzer data to a file +** +** Evidence of failure may be found in the following telemetry: +** - \b \c \ES_CMDEC - command error counter will increment +** - A command specific error event message is issued for all error +** cases +** +** \par Criticality +** This command is not inherently dangerous. An additional low priority child +** task will be spawned, however, to dump the performance analyzer data to a file. +** +** \sa #CFE_ES_START_PERF_DATA_CC, #CFE_ES_SET_PERF_FILTER_MASK_CC, #CFE_ES_SET_PERF_TRIGGER_MASK_CC +*/ +#define CFE_ES_STOP_PERF_DATA_CC 15 + +/** \cfeescmd Set Performance Analyzer's Filter Masks +** +** \par Description +** This command sets the Performance Analyzer's Filter Masks. +** +** \cfecmdmnemonic \ES_LAFILTERMASK +** +** \par Command Structure +** #CFE_ES_SetPerfFilterMaskCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with +** the following telemetry: +** - \b \c \ES_CMDPC - command execution counter will +** increment +** - \b \c \ES_PERFFLTRMASK - the current performance filter mask +** value(s) should reflect the commanded value +** - The #CFE_ES_PERF_FILTMSKCMD_EID debug event message will be +** generated. +** +** \par Error Conditions +** This command may fail for the following reason(s): +** - The command packet length is incorrect +** - The Filter Mask ID number is out of range +** +** Evidence of failure may be found in the following telemetry: +** - \b \c \ES_CMDEC - command error counter will increment +** - A command specific error event message is issued for all error +** cases +** +** \par Criticality +** Changing the filter masks may cause a small change in the Performance +** Analyzer's CPU utilization. +** +** \sa #CFE_ES_START_PERF_DATA_CC, #CFE_ES_STOP_PERF_DATA_CC, #CFE_ES_SET_PERF_TRIGGER_MASK_CC +*/ +#define CFE_ES_SET_PERF_FILTER_MASK_CC 16 + +/** \cfeescmd Set Performance Analyzer's Trigger Masks +** +** \par Description +** This command sets the Performance Analyzer's Trigger Masks. +** +** \cfecmdmnemonic \ES_LATRIGGERMASK +** +** \par Command Structure +** #CFE_ES_SetPerfTriggerMaskCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with +** the following telemetry: +** - \b \c \ES_CMDPC - command execution counter will +** increment +** - \b \c \ES_PERFTRIGMASK - the current performance trigger mask +** value(s) should reflect the commanded value +** - The #CFE_ES_PERF_TRIGMSKCMD_EID debug event message will be +** generated. +** +** \par Error Conditions +** This command may fail for the following reason(s): +** - The command packet length is incorrect +** - The Trigger Mask ID number is out of range +** +** Evidence of failure may be found in the following telemetry: +** - \b \c \ES_CMDEC - command error counter will increment +** - A command specific error event message is issued for all error +** cases +** +** \par Criticality +** Changing the trigger masks may cause a small change in the Performance +** Analyzer's CPU utilization. +** +** \sa #CFE_ES_START_PERF_DATA_CC, #CFE_ES_STOP_PERF_DATA_CC, #CFE_ES_SET_PERF_FILTER_MASK_CC +*/ +#define CFE_ES_SET_PERF_TRIGGER_MASK_CC 17 + +/** \cfeescmd Set Executive Services System Log Mode to Discard/Overwrite +** +** \par Description +** This command allows the user to configure the Executive Services +** to either discard new System Log messages when it is full or to +** overwrite the oldest messages. +** +** \cfecmdmnemonic \ES_OVERWRITESYSLOGMODE +** +** \par Command Structure +** #CFE_ES_OverWriteSysLogCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with +** the following telemetry: +** - \b \c \ES_CMDPC - command execution counter will +** increment +** - \b \c \ES_SYSLOGMODE - Current System Log Mode should reflect +** the commanded value +** - The #CFE_ES_SYSLOGMODE_EID debug event message will be +** generated. +** +** \par Error Conditions +** This command may fail for the following reason(s): +** - The command packet length is incorrect +** - The desired mode is neither #CFE_ES_LogMode_OVERWRITE or #CFE_ES_LogMode_DISCARD +** +** Evidence of failure may be found in the following telemetry: +** - \b \c \ES_CMDEC - command error counter will increment +** - A command specific error event message is issued for all error +** cases +** +** \par Criticality +** None. (It should be noted that "Overwrite" mode would allow a message +** identifying the cause of a problem to be lost by a subsequent flood of +** additional messages). +** +** \sa #CFE_ES_CLEAR_SYSLOG_CC, #CFE_ES_WRITE_SYSLOG_CC +*/ +#define CFE_ES_OVER_WRITE_SYSLOG_CC 18 + +/** \cfeescmd Resets the Processor Reset Counter to Zero +** +** \par Description +** This command allows the user to reset the Processor Reset Counter to zero. +** The Processor Reset Counter counts the number of Processor Resets that +** have occurred so as to identify when a Processor Reset should automatically +** be upgraded to a full Power-On Reset. +** +** \cfecmdmnemonic \ES_RESETPRCNT +** +** \par Command Structure +** #CFE_ES_ResetPRCountCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with +** the following telemetry: +** - \b \c \ES_CMDPC - command execution counter will +** increment +** - \b \c \ES_PROCRESETCNT - Current number of processor resets will go to zero +** - The #CFE_ES_RESET_PR_COUNT_EID informational event message will be +** generated. +** +** \par Error Conditions +** This command may fail for the following reason(s): +** - The command packet length is incorrect +** +** Evidence of failure may be found in the following telemetry: +** - \b \c \ES_CMDEC - command error counter will increment +** - A command specific error event message is issued for all error +** cases +** +** \par Criticality +** This command is not critical. The only impact would be that the system +** would have to have more processor resets before an automatic power-on reset +** occurred. +** +** \sa #CFE_ES_SET_MAX_PR_COUNT_CC, #CFE_ES_RESET_COUNTERS_CC +*/ +#define CFE_ES_RESET_PR_COUNT_CC 19 + +/** \cfeescmd Configure the Maximum Number of Processor Resets before a Power-On Reset +** +** \par Description +** This command allows the user to specify the number of Processor Resets that +** are allowed before the next Processor Reset is upgraded to a Power-On Reset. +** +** \cfecmdmnemonic \ES_SETMAXPRCNT +** +** \par Command Structure +** #CFE_ES_SetMaxPRCountCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with +** the following telemetry: +** - \b \c \ES_CMDPC - command execution counter will +** increment +** - \b \c \ES_MAXPROCRESETS - Current maximum number of processor resets +** before an automatic power-on reset will go to the command specified value. +** - The #CFE_ES_SET_MAX_PR_COUNT_EID informational event message will be +** generated. +** +** \par Error Conditions +** This command may fail for the following reason(s): +** - The command packet length is incorrect +** +** Evidence of failure may be found in the following telemetry: +** - \b \c \ES_CMDEC - command error counter will increment +** - A command specific error event message is issued for all error +** cases +** +** \par Criticality +** If the operator were to set the Maximum Processor Reset Count to too high a value, +** the processor would require an inordinate number of consecutive processor resets +** before an automatic power-on reset would occur. This could potentially leave the +** spacecraft without any control for a significant amount of time if a processor reset +** fails to clear a problem. +** +** \sa #CFE_ES_RESET_PR_COUNT_CC +*/ +#define CFE_ES_SET_MAX_PR_COUNT_CC 20 + +/** \cfeescmd Delete Critical Data Store +** +** \par Description +** This command allows the user to delete a Critical Data Store that was created +** by an Application that is now no longer executing. +** +** \cfecmdmnemonic \ES_DELETECDS +** +** \par Command Structure +** #CFE_ES_DeleteCDSCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with +** the following telemetry: +** - \b \c \ES_CMDPC - command execution counter will +** increment +** - The #CFE_ES_CDS_DELETED_INFO_EID informational event message will be +** generated. +** - The specified CDS should no longer appear in a CDS Registry dump generated +** upon receipt of the #CFE_ES_DUMP_CDS_REGISTRY_CC command +** +** \par Error Conditions +** This command may fail for the following reason(s): +** - The command packet length is incorrect +** - The specified CDS is the CDS portion of a Critical Table. See #CFE_TBL_DELETE_CDS_CC. +** - The specified CDS is not found in the CDS Registry +** - The specified CDS is associated with an Application that is still active +** - An error occurred while accessing the CDS memory (see the System Log for more details) +** +** Evidence of failure may be found in the following telemetry: +** - \b \c \ES_CMDEC - command error counter will increment +** - A command specific error event message is issued for all error +** cases +** +** \par Criticality +** This command is not critical because it is not possible to delete a CDS that is +** associated with an active application. However, deleting a CDS does eliminate +** any "history" that an application may be wishing to keep. +** +** \sa #CFE_ES_DUMP_CDS_REGISTRY_CC, #CFE_TBL_DELETE_CDS_CC +*/ +#define CFE_ES_DELETE_CDS_CC 21 + +/** \cfeescmd Telemeter Memory Pool Statistics +** +** \par Description +** This command allows the user to obtain a snapshot of the statistics maintained +** for a specified memory pool. +** +** \cfecmdmnemonic \ES_TLMPOOLSTATS +** +** \par Command Structure +** #CFE_ES_SendMemPoolStatsCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with +** the following telemetry: +** - \b \c \ES_CMDPC - command execution counter will +** increment +** - The #CFE_ES_TLM_POOL_STATS_INFO_EID debug event message will be +** generated. +** - The \link #CFE_ES_MemStatsTlm_t Memory Pool Statistics Telemetry Packet \endlink +** is produced +** +** \par Error Conditions +** This command may fail for the following reason(s): +** - The command packet length is incorrect +** - The specified handle is not associated with a known memory pool +** - The specified handle caused a processor exception because it improperly +** identified a segment of memory +** +** Evidence of failure may be found in the following telemetry: +** - \b \c \ES_CMDEC - command error counter will increment +** - A command specific error event message is issued for all error +** cases +** +** \par Criticality +** An incorrect Memory Pool Handle value can cause a system crash. +** Extreme care should be taken to ensure the memory handle value +** used in the command is correct. +** +** \sa +*/ +#define CFE_ES_SEND_MEM_POOL_STATS_CC 22 + +/** \cfeescmd Dump Critical Data Store Registry to a File +** +** \par Description +** This command allows the user to dump the Critical Data Store +** Registry to an onboard file. +** +** \cfecmdmnemonic \ES_DUMPCDSREG +** +** \par Command Structure +** #CFE_ES_DumpCDSRegistryCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with +** the following telemetry: +** - \b \c \ES_CMDPC - command execution counter will +** increment +** - The #CFE_ES_CDS_REG_DUMP_INF_EID debug event message will be +** generated. +** - The file specified in the command (or the default specified +** by the #CFE_PLATFORM_ES_DEFAULT_CDS_REG_DUMP_FILE configuration parameter) will be +** updated with the lastest information. +** +** \par Error Conditions +** This command may fail for the following reason(s): +** - Error occurred while trying to create the dump file +** +** Evidence of failure may be found in the following telemetry: +** - \b \c \ES_CMDEC - command error counter will increment +** - A command specific error event message is issued for all error +** cases +** +** \par Criticality +** This command is not inherently dangerous. It will create a new +** file in the file system (or overwrite an existing one) and could, +** if performed repeatedly without sufficient file management by the +** operator, fill the file system. +** +** \sa #CFE_ES_DELETE_CDS_CC, #CFE_TBL_DELETE_CDS_CC +*/ +#define CFE_ES_DUMP_CDS_REGISTRY_CC 23 + +/** \cfeescmd Writes a list of All Executive Services Tasks to a File +** +** \par Description +** This command takes the information kept by Executive Services on all of the +** registered tasks and writes it to the specified file. +** +** \cfecmdmnemonic \ES_WRITETASKINFO2FILE +** +** \par Command Structure +** #CFE_ES_QueryAllTasksCmd_t +** +** \par Command Verification +** Successful execution of this command may be verified with +** the following telemetry: +** - \b \c \ES_CMDPC - command execution counter will +** increment +** - The #CFE_ES_TASKINFO_EID debug event message will be +** generated. +** - The file specified in the command (or the default specified +** by the #CFE_PLATFORM_ES_DEFAULT_TASK_LOG_FILE configuration parameter) will be +** updated with the lastest information. +** +** \par Error Conditions +** This command may fail for the following reason(s): +** - The command packet length is incorrect +** - An Error occurs while trying to write to the file +** +** Evidence of failure may be found in the following telemetry: +** - \b \c \ES_CMDEC - command error counter will increment +** - A command specific error event message is issued for all error +** cases +** +** \par Criticality +** This command is not inherently dangerous. It will create a new +** file in the file system (or overwrite an existing one) and could, +** if performed repeatedly without sufficient file management by the +** operator, fill the file system. +** +** \sa #CFE_ES_QUERY_ALL_CC, CFE_ES_QUERY_ONE_CC +*/ +#define CFE_ES_QUERY_ALL_TASKS_CC 24 + +/** \} */ + +/*************************************************************************/ +/********************************/ +/* Command Message Data Formats */ +/********************************/ +/** +** \brief Generic "no arguments" command +** +** This command structure is used for commands that do not have any parameters. +** This includes: +** -# The Housekeeping Request Message +** -# The No-Op Command (For details, see #CFE_ES_NOOP_CC) +** -# The Reset Counters Command (For details, see #CFE_ES_RESET_COUNTERS_CC) +*/ +typedef struct CFE_ES_NoArgsCmd +{ + CFE_MSG_CommandHeader_t CmdHeader; /**< \brief Command header */ +} CFE_ES_NoArgsCmd_t; + +/* + * The following commands all share the "NoArgs" format + * + * They are each given their own type name matching the command name, which + * allows them to change independently in the future without changing the prototype + * of the handler function. + */ +typedef CFE_ES_NoArgsCmd_t CFE_ES_NoopCmd_t; +typedef CFE_ES_NoArgsCmd_t CFE_ES_ResetCountersCmd_t; +typedef CFE_ES_NoArgsCmd_t CFE_ES_ClearSysLogCmd_t; +typedef CFE_ES_NoArgsCmd_t CFE_ES_ClearERLogCmd_t; +typedef CFE_ES_NoArgsCmd_t CFE_ES_ResetPRCountCmd_t; + +/** +** \brief Restart cFE Command Payload +** +** For command details, see #CFE_ES_RESTART_CC +** +**/ +typedef struct CFE_ES_RestartCmd_Payload +{ + uint16 RestartType; /**< \brief #CFE_PSP_RST_TYPE_PROCESSOR=Processor Reset + or #CFE_PSP_RST_TYPE_POWERON=Power-On Reset */ +} CFE_ES_RestartCmd_Payload_t; + +/** + * \brief Restart cFE Command + */ +typedef struct CFE_ES_RestartCmd +{ + CFE_MSG_CommandHeader_t CmdHeader; /**< \brief Command header */ + CFE_ES_RestartCmd_Payload_t Payload; /**< \brief Command payload */ +} CFE_ES_RestartCmd_t; + +/** +** \brief Generic file name command payload +** +** This format is shared by several executive services commands. +** For command details, see #CFE_ES_QUERY_ALL_CC, #CFE_ES_QUERY_ALL_TASKS_CC, +** #CFE_ES_WRITE_SYSLOG_CC, and #CFE_ES_WRITE_ER_LOG_CC +** +**/ +typedef struct CFE_ES_FileNameCmd_Payload +{ + char FileName[CFE_MISSION_MAX_PATH_LEN]; /**< \brief ASCII text string containing full path and + filename of file in which Application data is to be dumped */ +} CFE_ES_FileNameCmd_Payload_t; + +/** + * \brief Generic file name command + */ +typedef struct CFE_ES_FileNameCmd +{ + CFE_MSG_CommandHeader_t CmdHeader; /**< \brief Command header */ + CFE_ES_FileNameCmd_Payload_t Payload; /**< \brief Command payload */ +} CFE_ES_FileNameCmd_t; + +/* + * Unique typedefs for each of the commands that utilize the FileNameCmd + * packet format + */ +typedef CFE_ES_FileNameCmd_t CFE_ES_QueryAllCmd_t; +typedef CFE_ES_FileNameCmd_t CFE_ES_QueryAllTasksCmd_t; +typedef CFE_ES_FileNameCmd_t CFE_ES_WriteSysLogCmd_t; +typedef CFE_ES_FileNameCmd_t CFE_ES_WriteERLogCmd_t; + +/** +** \brief Overwrite/Discard System Log Configuration Command Payload +** +** For command details, see #CFE_ES_OVER_WRITE_SYSLOG_CC +** +**/ +typedef struct CFE_ES_OverWriteSysLogCmd_Payload +{ + uint32 Mode; /**< \brief #CFE_ES_LogMode_DISCARD=Throw away most recent messages, + #CFE_ES_LogMode_OVERWRITE=Overwrite oldest with most recent */ + +} CFE_ES_OverWriteSysLogCmd_Payload_t; + +/** + * \brief Overwrite/Discard System Log Configuration Command Payload + */ +typedef struct CFE_ES_OverWriteSysLogCmd +{ + CFE_MSG_CommandHeader_t CmdHeader; /**< \brief Command header */ + CFE_ES_OverWriteSysLogCmd_Payload_t Payload; /**< \brief Command payload */ +} CFE_ES_OverWriteSysLogCmd_t; + +/** +** \brief Start Application Command Payload +** +** For command details, see #CFE_ES_START_APP_CC +** +**/ +typedef struct CFE_ES_StartAppCmd_Payload +{ + char Application[CFE_MISSION_MAX_API_LEN]; /**< \brief Name of Application to be started */ + char AppEntryPoint[CFE_MISSION_MAX_API_LEN]; /**< \brief Symbolic name of Application's entry point */ + char AppFileName[CFE_MISSION_MAX_PATH_LEN]; /**< \brief Full path and filename of Application's + executable image */ + + CFE_ES_MemOffset_t StackSize; /**< \brief Desired stack size for the new application */ + + CFE_ES_ExceptionAction_Enum_t ExceptionAction; /**< \brief #CFE_ES_ExceptionAction_RESTART_APP=On exception, + restart Application, + #CFE_ES_ExceptionAction_PROC_RESTART=On exception, + perform a Processor Reset */ + CFE_ES_TaskPriority_Atom_t Priority; /**< \brief The new Applications runtime priority. */ + +} CFE_ES_StartAppCmd_Payload_t; + +/** + * \brief Start Application Command + */ +typedef struct CFE_ES_StartApp +{ + CFE_MSG_CommandHeader_t CmdHeader; /**< \brief Command header */ + CFE_ES_StartAppCmd_Payload_t Payload; /**< \brief Command payload */ +} CFE_ES_StartAppCmd_t; + +/** +** \brief Generic application name command payload +** +** For command details, see #CFE_ES_STOP_APP_CC, #CFE_ES_RESTART_APP_CC, #CFE_ES_QUERY_ONE_CC +** +**/ +typedef struct CFE_ES_AppNameCmd_Payload +{ + char Application[CFE_MISSION_MAX_API_LEN]; /**< \brief ASCII text string containing Application Name */ +} CFE_ES_AppNameCmd_Payload_t; + +/** + * \brief Generic application name command + */ +typedef struct CFE_ES_AppNameCmd +{ + CFE_MSG_CommandHeader_t CmdHeader; /**< \brief Command header */ + CFE_ES_AppNameCmd_Payload_t Payload; /**< \brief Command payload */ +} CFE_ES_AppNameCmd_t; + +/* + * Like NoArgsCmd, this message definition is shared by multiple commands. + * Create a separate typedef for each one so they can all evolve independently + * without affecting the prototype. + */ +typedef CFE_ES_AppNameCmd_t CFE_ES_StopAppCmd_t; +typedef CFE_ES_AppNameCmd_t CFE_ES_RestartAppCmd_t; +typedef CFE_ES_AppNameCmd_t CFE_ES_QueryOneCmd_t; + +/** +** \brief Reload Application Command Payload +** +** For command details, see #CFE_ES_RELOAD_APP_CC +** +**/ +typedef struct CFE_ES_AppReloadCmd_Payload +{ + char Application[CFE_MISSION_MAX_API_LEN]; /**< \brief ASCII text string containing Application Name */ + char AppFileName[CFE_MISSION_MAX_PATH_LEN]; /**< \brief Full path and filename of Application's + executable image */ +} CFE_ES_AppReloadCmd_Payload_t; + +/** + * \brief Reload Application Command + */ +typedef struct CFE_ES_ReloadAppCmd +{ + CFE_MSG_CommandHeader_t CmdHeader; /**< \brief Command header */ + CFE_ES_AppReloadCmd_Payload_t Payload; /**< \brief Command payload */ +} CFE_ES_ReloadAppCmd_t; + +/** +** \brief Set Maximum Processor Reset Count Command Payload +** +** For command details, see #CFE_ES_SET_MAX_PR_COUNT_CC +** +**/ +typedef struct CFE_ES_SetMaxPRCountCmd_Payload +{ + uint16 MaxPRCount; /**< \brief New maximum number of Processor Resets before + an automatic Power-On Reset is performed */ +} CFE_ES_SetMaxPRCountCmd_Payload_t; + +/** + * \brief Set Maximum Processor Reset Count Command + */ +typedef struct CFE_ES_SetMaxPRCountCmd +{ + CFE_MSG_CommandHeader_t CmdHeader; /**< \brief Command header */ + CFE_ES_SetMaxPRCountCmd_Payload_t Payload; /**< \brief Command payload */ +} CFE_ES_SetMaxPRCountCmd_t; + +/** +** \brief Delete Critical Data Store Command Payload +** +** For command details, see #CFE_ES_DELETE_CDS_CC +** +**/ +typedef struct CFE_ES_DeleteCDSCmd_Payload +{ + char + CdsName[CFE_MISSION_ES_CDS_MAX_FULL_NAME_LEN]; /**< \brief ASCII text string containing name of CDS to delete */ + +} CFE_ES_DeleteCDSCmd_Payload_t; + +/** + * \brief Delete Critical Data Store Command + */ +typedef struct CFE_ES_DeleteCDSCmd +{ + CFE_MSG_CommandHeader_t CmdHeader; /**< \brief Command header */ + CFE_ES_DeleteCDSCmd_Payload_t Payload; /**< \brief Command payload */ +} CFE_ES_DeleteCDSCmd_t; + +/** +** \brief Start Performance Analyzer Command Payload +** +** For command details, see #CFE_ES_START_PERF_DATA_CC +** +**/ +typedef struct CFE_ES_StartPerfCmd_Payload +{ + uint32 TriggerMode; /**< \brief Desired trigger position (Start, Center, End) */ +} CFE_ES_StartPerfCmd_Payload_t; + +/** + * \brief Start Performance Analyzer Command + */ +typedef struct CFE_ES_StartPerfDataCmd +{ + CFE_MSG_CommandHeader_t CmdHeader; /**< \brief Command header */ + CFE_ES_StartPerfCmd_Payload_t Payload; /**< \brief Command payload */ +} CFE_ES_StartPerfDataCmd_t; + +/** +** \brief Stop Performance Analyzer Command Payload +** +** For command details, see #CFE_ES_STOP_PERF_DATA_CC +** +**/ +typedef struct CFE_ES_StopPerfCmd_Payload +{ + char DataFileName[CFE_MISSION_MAX_PATH_LEN]; /**< \brief ASCII text string of full path and filename + of file Performance Analyzer data is to be written */ +} CFE_ES_StopPerfCmd_Payload_t; + +/** + * \brief Stop Performance Analyzer Command + */ +typedef struct CFE_ES_StopPerfDataCmd +{ + CFE_MSG_CommandHeader_t CmdHeader; /**< \brief Command header */ + CFE_ES_StopPerfCmd_Payload_t Payload; /**< \brief Command payload */ +} CFE_ES_StopPerfDataCmd_t; + +/** +** \brief Set Performance Analyzer Filter Mask Command Payload +** +** For command details, see #CFE_ES_SET_PERF_FILTER_MASK_CC +** +**/ +typedef struct CFE_ES_SetPerfFilterMaskCmd_Payload +{ + uint32 FilterMaskNum; /**< \brief Index into array of Filter Masks */ + uint32 FilterMask; /**< \brief New Mask for specified entry in array of Filter Masks */ + +} CFE_ES_SetPerfFilterMaskCmd_Payload_t; + +/** + * \brief Set Performance Analyzer Filter Mask Command + */ +typedef struct CFE_ES_SetPerfFilterMaskCmd +{ + CFE_MSG_CommandHeader_t CmdHeader; /**< \brief Command header */ + CFE_ES_SetPerfFilterMaskCmd_Payload_t Payload; /**< \brief Command payload */ +} CFE_ES_SetPerfFilterMaskCmd_t; + +/** +** \brief Set Performance Analyzer Trigger Mask Command Payload +** +** For command details, see #CFE_ES_SET_PERF_TRIGGER_MASK_CC +** +**/ +typedef struct CFE_ES_SetPerfTrigMaskCmd_Payload +{ + uint32 TriggerMaskNum; /**< \brief Index into array of Trigger Masks */ + uint32 TriggerMask; /**< \brief New Mask for specified entry in array of Trigger Masks */ + +} CFE_ES_SetPerfTrigMaskCmd_Payload_t; + +/** + * \brief Set Performance Analyzer Trigger Mask Command + */ +typedef struct CFE_ES_SetPerfTriggerMaskCmd +{ + CFE_MSG_CommandHeader_t CmdHeader; /**< \brief Command header */ + CFE_ES_SetPerfTrigMaskCmd_Payload_t Payload; /**< \brief Command payload */ +} CFE_ES_SetPerfTriggerMaskCmd_t; + +/** +** \brief Send Memory Pool Statistics Command Payload +** +** For command details, see #CFE_ES_SEND_MEM_POOL_STATS_CC +** +**/ +typedef struct CFE_ES_SendMemPoolStatsCmd_Payload +{ + char Application[CFE_MISSION_MAX_API_LEN]; /**< \brief - RESERVED - should be all zeroes */ + CFE_ES_MemHandle_t PoolHandle; /**< \brief Handle of Pool whose statistics are to be telemetered */ + +} CFE_ES_SendMemPoolStatsCmd_Payload_t; + +/** + * \brief Send Memory Pool Statistics Command + */ +typedef struct CFE_ES_SendMemPoolStatsCmd +{ + CFE_MSG_CommandHeader_t CmdHeader; /**< \brief Command header */ + CFE_ES_SendMemPoolStatsCmd_Payload_t Payload; /**< \brief Command payload */ +} CFE_ES_SendMemPoolStatsCmd_t; + +/** +** \brief Dump CDS Registry Command Payload +** +** For command details, see #CFE_ES_DUMP_CDS_REGISTRY_CC +** +**/ +typedef struct CFE_ES_DumpCDSRegistryCmd_Payload +{ + char DumpFilename[CFE_MISSION_MAX_PATH_LEN]; /**< \brief ASCII text string of full path and filename + of file CDS Registry is to be written */ +} CFE_ES_DumpCDSRegistryCmd_Payload_t; + +/** + * \brief Dump CDS Registry Command + */ +typedef struct CFE_ES_DumpCDSRegistryCmd +{ + CFE_MSG_CommandHeader_t CmdHeader; /**< \brief Command header */ + CFE_ES_DumpCDSRegistryCmd_Payload_t Payload; /**< \brief Command payload */ +} CFE_ES_DumpCDSRegistryCmd_t; + +/*************************************************************************/ + +/************************************/ +/* Telemetry Interface Data Formats */ +/************************************/ + +/**********************************/ +/* Telemetry Message Data Formats */ +/**********************************/ +/** +** \cfeestlm Single Application Information Packet +**/ +typedef struct CFE_ES_OneAppTlm_Payload +{ + CFE_ES_AppInfo_t AppInfo; /**< \brief For more information, see #CFE_ES_AppInfo_t */ + +} CFE_ES_OneAppTlm_Payload_t; + +typedef struct CFE_ES_OneAppTlm +{ + CFE_MSG_TelemetryHeader_t TlmHeader; /**< \brief Telemetry header */ + CFE_ES_OneAppTlm_Payload_t Payload; /**< \brief Telemetry payload */ +} CFE_ES_OneAppTlm_t; + +/** +** \cfeestlm Memory Pool Statistics Packet +**/ +typedef struct CFE_ES_PoolStatsTlm_Payload +{ + CFE_ES_MemHandle_t PoolHandle; /**< \cfetlmmnemonic \ES_POOLHANDLE + \brief Handle of memory pool whose stats are being telemetered */ + CFE_ES_MemPoolStats_t PoolStats; /**< \brief For more info, see #CFE_ES_MemPoolStats_t */ +} CFE_ES_PoolStatsTlm_Payload_t; + +typedef struct CFE_ES_MemStatsTlm +{ + CFE_MSG_TelemetryHeader_t TlmHeader; /**< \brief Telemetry header */ + CFE_ES_PoolStatsTlm_Payload_t Payload; /**< \brief Telemetry payload */ +} CFE_ES_MemStatsTlm_t; + +/*************************************************************************/ + +/** +** \cfeestlm Executive Services Housekeeping Packet +**/ +typedef struct CFE_ES_HousekeepingTlm_Payload +{ + uint8 CommandCounter; /**< \cfetlmmnemonic \ES_CMDPC + \brief The ES Application Command Counter */ + uint8 CommandErrorCounter; /**< \cfetlmmnemonic \ES_CMDEC + \brief The ES Application Command Error Counter */ + + uint16 CFECoreChecksum; /**< \cfetlmmnemonic \ES_CKSUM + \brief Checksum of cFE Core Code */ + uint8 CFEMajorVersion; /**< \cfetlmmnemonic \ES_CFEMAJORVER + \brief Major Version Number of cFE */ + uint8 CFEMinorVersion; /**< \cfetlmmnemonic \ES_CFEMINORVER + \brief Minor Version Number of cFE */ + uint8 CFERevision; /**< \cfetlmmnemonic \ES_CFEREVISION + \brief Sub-Minor Version Number of cFE */ + uint8 CFEMissionRevision; /**< \cfetlmmnemonic \ES_CFEMISSIONREV + \brief Mission Version Number of cFE */ + uint8 OSALMajorVersion; /**< \cfetlmmnemonic \ES_OSMAJORVER + \brief OS Abstraction Layer Major Version Number */ + uint8 OSALMinorVersion; /**< \cfetlmmnemonic \ES_OSMINORVER + \brief OS Abstraction Layer Minor Version Number */ + uint8 OSALRevision; /**< \cfetlmmnemonic \ES_OSREVISION + \brief OS Abstraction Layer Revision Number */ + uint8 OSALMissionRevision; /**< \cfetlmmnemonic \ES_OSMISSIONREV + \brief OS Abstraction Layer MissionRevision Number */ + + uint8 PSPMajorVersion; /**< \cfetlmmnemonic \ES_PSPMAJORVER + \brief Platform Support Package Major Version Number */ + uint8 PSPMinorVersion; /**< \cfetlmmnemonic \ES_PSPMINORVER + \brief Platform Support Package Minor Version Number */ + uint8 PSPRevision; /**< \cfetlmmnemonic \ES_PSPREVISION + \brief Platform Support Package Revision Number */ + uint8 PSPMissionRevision; /**< \cfetlmmnemonic \ES_PSPMISSIONREV + \brief Platform Support Package MissionRevision Number */ + + CFE_ES_MemOffset_t SysLogBytesUsed; /**< \cfetlmmnemonic \ES_SYSLOGBYTEUSED + \brief Total number of bytes used in system log */ + CFE_ES_MemOffset_t SysLogSize; /**< \cfetlmmnemonic \ES_SYSLOGSIZE + \brief Total size of the system log */ + uint32 SysLogEntries; /**< \cfetlmmnemonic \ES_SYSLOGENTRIES + \brief Number of entries in the system log */ + uint32 SysLogMode; /**< \cfetlmmnemonic \ES_SYSLOGMODE + \brief Write/Overwrite Mode */ + + uint32 ERLogIndex; /**< \cfetlmmnemonic \ES_ERLOGINDEX + \brief Current index of the ER Log (wraps around) */ + uint32 ERLogEntries; /**< \cfetlmmnemonic \ES_ERLOGENTRIES + \brief Number of entries made in the ER Log since the power on */ + + uint32 RegisteredCoreApps; /**< \cfetlmmnemonic \ES_REGCOREAPPS + \brief Number of Applications registered with ES */ + uint32 RegisteredExternalApps; /**< \cfetlmmnemonic \ES_REGEXTAPPS + \brief Number of Applications registered with ES */ + uint32 RegisteredTasks; /**< \cfetlmmnemonic \ES_REGTASKS + \brief Number of Tasks ( main AND child tasks ) registered with ES */ + uint32 RegisteredLibs; /**< \cfetlmmnemonic \ES_REGLIBS + \brief Number of Libraries registered with ES */ + + uint32 ResetType; /**< \cfetlmmnemonic \ES_RESETTYPE + \brief Reset type ( PROCESSOR or POWERON ) */ + uint32 ResetSubtype; /**< \cfetlmmnemonic \ES_RESETSUBTYPE + \brief Reset Sub Type */ + uint32 ProcessorResets; /**< \cfetlmmnemonic \ES_PROCRESETCNT + \brief Number of processor resets since last power on */ + uint32 MaxProcessorResets; /**< \cfetlmmnemonic \ES_MAXPROCRESETS + \brief Max processor resets before a power on is done */ + uint32 BootSource; /**< \cfetlmmnemonic \ES_BOOTSOURCE + \brief Boot source ( as provided from BSP ) */ + + uint32 PerfState; /**< \cfetlmmnemonic \ES_PERFSTATE + \brief Current state of Performance Analyzer */ + uint32 PerfMode; /**< \cfetlmmnemonic \ES_PERFMODE + \brief Current mode of Performance Analyzer */ + uint32 PerfTriggerCount; /**< \cfetlmmnemonic \ES_PERFTRIGCNT + \brief Number of Times Perfomance Analyzer has Triggered */ + uint32 PerfFilterMask[CFE_MISSION_ES_PERF_MAX_IDS / 32]; /**< \cfetlmmnemonic \ES_PERFFLTRMASK + \brief Current Setting of Performance Analyzer Filter Masks */ + uint32 + PerfTriggerMask[CFE_MISSION_ES_PERF_MAX_IDS / 32]; /**< \cfetlmmnemonic \ES_PERFTRIGMASK + \brief Current Setting of Performance Analyzer Trigger Masks */ + uint32 PerfDataStart; /**< \cfetlmmnemonic \ES_PERFDATASTART + \brief Identifies First Stored Entry in Performance Analyzer Log */ + uint32 PerfDataEnd; /**< \cfetlmmnemonic \ES_PERFDATAEND + \brief Identifies Last Stored Entry in Performance Analyzer Log */ + uint32 PerfDataCount; /**< \cfetlmmnemonic \ES_PERFDATACNT + \brief Number of Entries Put Into the Performance Analyzer Log */ + uint32 + PerfDataToWrite; /**< \cfetlmmnemonic \ES_PERFDATA2WRITE + \brief Number of Performance Analyzer Log Entries Left to be Written to Log Dump File */ + CFE_ES_MemOffset_t HeapBytesFree; /**< \cfetlmmnemonic \ES_HEAPBYTESFREE + \brief Number of free bytes remaining in the OS heap */ + CFE_ES_MemOffset_t HeapBlocksFree; /**< \cfetlmmnemonic \ES_HEAPBLKSFREE + \brief Number of free blocks remaining in the OS heap */ + CFE_ES_MemOffset_t HeapMaxBlockSize; /**< \cfetlmmnemonic \ES_HEAPMAXBLK + \brief Number of bytes in the largest free block */ +} CFE_ES_HousekeepingTlm_Payload_t; + +typedef struct CFE_ES_HousekeepingTlm +{ + CFE_MSG_TelemetryHeader_t TlmHeader; /**< \brief Telemetry header */ + CFE_ES_HousekeepingTlm_Payload_t Payload; /**< \brief Telemetry payload */ + +} CFE_ES_HousekeepingTlm_t; + +#endif /* CFE_ES_MSG_H */ diff --git a/modules/es/fsw/src/cfe_es_api.c b/modules/es/fsw/src/cfe_es_api.c new file mode 100644 index 000000000..3ac9f1ff0 --- /dev/null +++ b/modules/es/fsw/src/cfe_es_api.c @@ -0,0 +1,2136 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/* +** File: +** cfe_es_api.c +** +** Purpose: +** This file implements the cFE Executive Services API functions. +** +** References: +** Flight Software Branch C Coding Standard Version 1.0a +** cFE Flight Software Application Developers Guide +** +** Notes: +** +*/ + +/* +** Required header files. +*/ +#include "cfe_es_module_all.h" + +#include +#include +#include + +/* +** Function: CFE_ES_GetResetType - See API and header file for details +*/ +int32 CFE_ES_GetResetType(uint32 *ResetSubtypePtr) +{ + if (ResetSubtypePtr != NULL) + { + *ResetSubtypePtr = CFE_ES_Global.ResetDataPtr->ResetVars.ResetSubtype; + } + + return (CFE_ES_Global.ResetDataPtr->ResetVars.ResetType); + +} /* End of CFE_ES_GetResetType() */ + +/* +** Function: CFE_ES_ResetCFE - See API and header file for details +*/ +int32 CFE_ES_ResetCFE(uint32 ResetType) +{ + int32 ReturnCode; + + if (ResetType == CFE_PSP_RST_TYPE_PROCESSOR) + { + /* + ** Increment the processor reset count + */ + CFE_ES_Global.ResetDataPtr->ResetVars.ProcessorResetCount++; + + /* + ** Before doing a Processor reset, check to see + ** if the maximum number has been exceeded + */ + if (CFE_ES_Global.ResetDataPtr->ResetVars.ProcessorResetCount > + CFE_ES_Global.ResetDataPtr->ResetVars.MaxProcessorResetCount) + { + CFE_ES_WriteToSysLog("POWER ON RESET due to max proc resets (Commanded).\n"); + + /* + ** Log the reset in the ER Log. The log will be wiped out, but it's good to have + ** the entry just in case something fails. + */ + CFE_ES_WriteToERLog(CFE_ES_LogEntryType_CORE, CFE_PSP_RST_TYPE_POWERON, CFE_PSP_RST_SUBTYPE_RESET_COMMAND, + "POWER ON RESET due to max proc resets (Commanded)."); + /* + ** Call the BSP reset routine + */ + CFE_PSP_Restart(CFE_PSP_RST_TYPE_POWERON); + } + else + { + CFE_ES_WriteToSysLog("PROCESSOR RESET called from CFE_ES_ResetCFE (Commanded).\n"); + + /* + ** Update the reset variables + */ + CFE_ES_Global.ResetDataPtr->ResetVars.ES_CausedReset = true; + + /* + ** Log the reset in the ER Log + */ + CFE_ES_WriteToERLog(CFE_ES_LogEntryType_CORE, CFE_PSP_RST_TYPE_PROCESSOR, CFE_PSP_RST_SUBTYPE_RESET_COMMAND, + "PROCESSOR RESET called from CFE_ES_ResetCFE (Commanded)."); + /* + ** Call the BSP reset routine + */ + CFE_PSP_Restart(CFE_PSP_RST_TYPE_PROCESSOR); + + } /* end if */ + + /* + ** If the BSP routine is not implemented, + ** it will return. + */ + ReturnCode = CFE_ES_NOT_IMPLEMENTED; + } + else if (ResetType == CFE_PSP_RST_TYPE_POWERON) + { + CFE_ES_WriteToSysLog("POWERON RESET called from CFE_ES_ResetCFE (Commanded).\n"); + + /* + ** Log the reset in the ER Log. The log will be wiped out, but it's good to have + ** the entry just in case something fails. + */ + CFE_ES_WriteToERLog(CFE_ES_LogEntryType_CORE, CFE_PSP_RST_TYPE_POWERON, CFE_PSP_RST_SUBTYPE_RESET_COMMAND, + "POWERON RESET called from CFE_ES_ResetCFE (Commanded)."); + + /* + ** Call the BSP reset routine + */ + CFE_PSP_Restart(CFE_PSP_RST_TYPE_POWERON); + + /* + ** If the BSP routine is not implemented, + ** it will return. + */ + ReturnCode = CFE_ES_NOT_IMPLEMENTED; + } + else + { + CFE_ES_WriteToSysLog("ES ResetCFE: Invalid Reset Type: %d.\n", (int)ResetType); + ReturnCode = CFE_ES_BAD_ARGUMENT; + } + + return (ReturnCode); + +} /* End of CFE_ES_ResetCFE() */ + +/* +** Function: CFE_ES_RestartApp - See API and header file for details +*/ +int32 CFE_ES_RestartApp(CFE_ES_AppId_t AppID) +{ + int32 ReturnCode = CFE_SUCCESS; + os_fstat_t FileStatus; + CFE_ES_AppRecord_t *AppRecPtr; + + AppRecPtr = CFE_ES_LocateAppRecordByID(AppID); + if (AppRecPtr != NULL) + { + + CFE_ES_LockSharedData(__func__, __LINE__); + + /* + ** Check to see if the App is an external cFE App. + */ + if (AppRecPtr->Type == CFE_ES_AppType_CORE) + { + CFE_ES_SysLogWrite_Unsync("CFE_ES_RestartApp: Cannot Restart a CORE Application: %s.\n", + CFE_ES_AppRecordGetName(AppRecPtr)); + ReturnCode = CFE_ES_ERR_RESOURCEID_NOT_VALID; + } + else if (AppRecPtr->AppState != CFE_ES_AppState_RUNNING) + { + CFE_ES_SysLogWrite_Unsync("CFE_ES_RestartApp: Cannot Restart Application %s, It is not running.\n", + CFE_ES_AppRecordGetName(AppRecPtr)); + ReturnCode = CFE_ES_ERR_RESOURCEID_NOT_VALID; + } + else + { + /* + ** Check to see if the file exists + */ + if (OS_stat(AppRecPtr->StartParams.BasicInfo.FileName, &FileStatus) == OS_SUCCESS) + { + CFE_ES_SysLogWrite_Unsync("CFE_ES_RestartApp: Restart Application %s Initiated\n", + CFE_ES_AppRecordGetName(AppRecPtr)); + AppRecPtr->ControlReq.AppControlRequest = CFE_ES_RunStatus_SYS_RESTART; + } + else + { + CFE_ES_SysLogWrite_Unsync("CFE_ES_RestartApp: Cannot Restart Application %s, File %s does not exist.\n", + CFE_ES_AppRecordGetName(AppRecPtr), + AppRecPtr->StartParams.BasicInfo.FileName); + ReturnCode = CFE_ES_FILE_IO_ERR; + } + } + + CFE_ES_UnlockSharedData(__func__, __LINE__); + } + else /* App ID is not valid */ + { + ReturnCode = CFE_ES_ERR_RESOURCEID_NOT_VALID; + + CFE_ES_WriteToSysLog("CFE_ES_RestartApp: Invalid Application ID received, AppID = %lu\n", + CFE_RESOURCEID_TO_ULONG(AppID)); + + } /* end if */ + + return (ReturnCode); + +} /* End of CFE_ES_RestartApp() */ + +/* +** Function: CFE_ES_ReloadApp - See API and header file for details +*/ +int32 CFE_ES_ReloadApp(CFE_ES_AppId_t AppID, const char *AppFileName) +{ + int32 ReturnCode = CFE_SUCCESS; + os_fstat_t FileStatus; + CFE_ES_AppRecord_t *AppRecPtr = CFE_ES_LocateAppRecordByID(AppID); + + if (AppRecPtr == NULL) + { + return CFE_ES_ERR_RESOURCEID_NOT_VALID; + } + + CFE_ES_LockSharedData(__func__, __LINE__); + + /* + ** Check to see if the App is an external cFE App. + */ + if (AppRecPtr->Type == CFE_ES_AppType_CORE) + { + CFE_ES_SysLogWrite_Unsync("CFE_ES_DeleteApp: Cannot Reload a CORE Application: %s.\n", + CFE_ES_AppRecordGetName(AppRecPtr)); + ReturnCode = CFE_ES_ERR_RESOURCEID_NOT_VALID; + } + else if (AppRecPtr->AppState != CFE_ES_AppState_RUNNING) + { + CFE_ES_SysLogWrite_Unsync("CFE_ES_ReloadApp: Cannot Reload Application %s, It is not running.\n", + CFE_ES_AppRecordGetName(AppRecPtr)); + ReturnCode = CFE_ES_ERR_RESOURCEID_NOT_VALID; + } + else + { + /* + ** Check to see if the file exists + */ + if (OS_stat(AppFileName, &FileStatus) == OS_SUCCESS) + { + CFE_ES_SysLogWrite_Unsync("CFE_ES_ReloadApp: Reload Application %s Initiated. New filename = %s\n", + CFE_ES_AppRecordGetName(AppRecPtr), AppFileName); + strncpy(AppRecPtr->StartParams.BasicInfo.FileName, AppFileName, + sizeof(AppRecPtr->StartParams.BasicInfo.FileName) - 1); + AppRecPtr->StartParams.BasicInfo.FileName[sizeof(AppRecPtr->StartParams.BasicInfo.FileName) - 1] = 0; + AppRecPtr->ControlReq.AppControlRequest = CFE_ES_RunStatus_SYS_RELOAD; + } + else + { + CFE_ES_SysLogWrite_Unsync("CFE_ES_ReloadApp: Cannot Reload Application %s, File %s does not exist.\n", + CFE_ES_AppRecordGetName(AppRecPtr), AppFileName); + ReturnCode = CFE_ES_FILE_IO_ERR; + } + } + + CFE_ES_UnlockSharedData(__func__, __LINE__); + + return (ReturnCode); + +} /* End of CFE_ES_ReloadApp() */ + +/* +** Function: CFE_ES_DeleteApp - See API and header file for details +*/ +int32 CFE_ES_DeleteApp(CFE_ES_AppId_t AppID) +{ + int32 ReturnCode = CFE_SUCCESS; + CFE_ES_AppRecord_t *AppRecPtr = CFE_ES_LocateAppRecordByID(AppID); + + if (AppRecPtr == NULL) + { + return CFE_ES_ERR_RESOURCEID_NOT_VALID; + } + + CFE_ES_LockSharedData(__func__, __LINE__); + + /* + ** Check to see if the App is an external cFE App. + */ + if (AppRecPtr->Type == CFE_ES_AppType_CORE) + { + CFE_ES_SysLogWrite_Unsync("CFE_ES_DeleteApp: Cannot Delete a CORE Application: %s.\n", + CFE_ES_AppRecordGetName(AppRecPtr)); + ReturnCode = CFE_ES_ERR_RESOURCEID_NOT_VALID; + } + else if (AppRecPtr->AppState != CFE_ES_AppState_RUNNING) + { + CFE_ES_SysLogWrite_Unsync("CFE_ES_DeleteApp: Cannot Delete Application %s, It is not running.\n", + CFE_ES_AppRecordGetName(AppRecPtr)); + ReturnCode = CFE_ES_ERR_RESOURCEID_NOT_VALID; + } + else + { + CFE_ES_SysLogWrite_Unsync("CFE_ES_DeleteApp: Delete Application %s Initiated\n", + CFE_ES_AppRecordGetName(AppRecPtr)); + AppRecPtr->ControlReq.AppControlRequest = CFE_ES_RunStatus_SYS_DELETE; + } + + CFE_ES_UnlockSharedData(__func__, __LINE__); + + return (ReturnCode); + +} /* End of CFE_ES_DeleteApp() */ + +/* +** Function: CFE_ES_ExitApp - See API and header file for details +*/ +void CFE_ES_ExitApp(uint32 ExitStatus) +{ + int32 ReturnCode; + CFE_ES_AppRecord_t *AppRecPtr; + + CFE_ES_LockSharedData(__func__, __LINE__); + + /* + * This should only be called with a valid ExitStatus, anything else is invalid + * and indicates a bug in the caller. + */ + + if (ExitStatus == CFE_ES_RunStatus_UNDEFINED || ExitStatus >= CFE_ES_RunStatus_MAX) + { + CFE_ES_SysLogWrite_Unsync("CFE_ES_ExitApp: Called with invalid status (%u).\n", (unsigned int)ExitStatus); + + /* revert to the ERROR status */ + ExitStatus = CFE_ES_RunStatus_APP_ERROR; + } + + AppRecPtr = CFE_ES_GetAppRecordByContext(); + if (AppRecPtr != NULL) + { + /* + * Set the status in the global table. + * + * The passed-in status should only be stored if there was no already-pending + * request from a ground command or other source, such as an exception, etc. + * + * If a control request is already pending, it is assumed that this exit is + * part of an orderly shutdown caused by that request, and therefore it + * should not be overwritten here. + */ + if (AppRecPtr->ControlReq.AppControlRequest == CFE_ES_RunStatus_APP_RUN) + { + AppRecPtr->ControlReq.AppControlRequest = ExitStatus; + } + + /* + ** Check to see if the App is an external cFE App. + */ + if (AppRecPtr->Type == CFE_ES_AppType_CORE) + { + /* + ** A core app should only call this function with one of two ExitStatus codes. + */ + if (ExitStatus == CFE_ES_RunStatus_CORE_APP_INIT_ERROR) + { + CFE_ES_SysLogWrite_Unsync("CFE_ES_ExitApp: CORE Application %s Had an Init Error.\n", + CFE_ES_AppRecordGetName(AppRecPtr)); + + /* + ** Unlock the ES Shared data before calling ResetCFE + */ + CFE_ES_UnlockSharedData(__func__, __LINE__); + + /* + ** Do a Processor Reset the cFE + */ + ReturnCode = CFE_ES_ResetCFE(CFE_PSP_RST_TYPE_PROCESSOR); + + /* + ** The CFE_ES_ResetCFE function does not normally return, + ** but it may return during unit testing. If it does, + ** log the return code (even if it claims CFE_SUCCESS). + */ + CFE_ES_WriteToSysLog("CFE_ES_ExitApp: CORE Application Init Error Processor Reset, RC = 0x%08X\n", + (unsigned int)ReturnCode); + + return; + } + else if (ExitStatus == CFE_ES_RunStatus_CORE_APP_RUNTIME_ERROR) + { + CFE_ES_SysLogWrite_Unsync("CFE_ES_ExitApp: CORE Application %s Had a Runtime Error.\n", + CFE_ES_AppRecordGetName(AppRecPtr)); + + /* + ** Unlock the ES Shared data before killing the main task + */ + CFE_ES_UnlockSharedData(__func__, __LINE__); + + /* + ** Exit this task + */ + OS_TaskExit(); + + /* + ** Code will not return, except under unit test + */ + return; + } + else + { + CFE_ES_SysLogWrite_Unsync("CFE_ES_ExitApp, Cannot Exit CORE Application %s\n", + CFE_ES_AppRecordGetName(AppRecPtr)); + } + } + else /* It is an external App */ + { + + CFE_ES_SysLogWrite_Unsync("Application %s called CFE_ES_ExitApp\n", CFE_ES_AppRecordGetName(AppRecPtr)); + + AppRecPtr->AppState = CFE_ES_AppState_STOPPED; + + /* + ** Unlock the ES Shared data before suspending the app + */ + CFE_ES_UnlockSharedData(__func__, __LINE__); + + /* + ** Suspend the Application until ES kills it. + ** It might be better to have a way of suspending the app in the OS + */ + while (1) + { + OS_TaskDelay(500); + } + + } /* end if */ + + } /* end if ReturnCode == CFE_SUCCESS */ + + CFE_ES_UnlockSharedData(__func__, __LINE__); + +} /* End of CFE_ES_ExitApp() */ + +/* +** Function: CFE_ES_RunLoop - See API and header file for details +*/ +bool CFE_ES_RunLoop(uint32 *RunStatus) +{ + bool ReturnCode; + CFE_ES_AppRecord_t *AppRecPtr; + + /* + * call CFE_ES_IncrementTaskCounter() so this is + * recorded as task activity for outgoing telemetry. + * + * This will update the counter for whatever task context + * is calling this API, which is expected to be the main + * task of the app. This can be done outside of any lock + * because each task has its own counter which is only updated + * by itself. + */ + CFE_ES_IncrementTaskCounter(); + + /* + * This API should generally only be called with the status as CFE_ES_RunStatus_APP_RUN. + * Anything else gets an immediate "false" return which should cause the caller to + * break out of its main loop. There is no need to take the lock or do any other + * accounting in that case. + * + * Note that the RunStatus really doesn't add much value here, so this also allows + * this function to be called with NULL, with the possibility of phasing this out + * entirely. + */ + if (RunStatus != NULL && *RunStatus != CFE_ES_RunStatus_APP_RUN) + { + return false; + } + + CFE_ES_LockSharedData(__func__, __LINE__); + + /* + ** Get App Record + */ + AppRecPtr = CFE_ES_GetAppRecordByContext(); + if (AppRecPtr != NULL) + { + /* + ** App state must be RUNNING (no-op if already set to running) + */ + if (AppRecPtr->AppState < CFE_ES_AppState_RUNNING) + { + AppRecPtr->AppState = CFE_ES_AppState_RUNNING; + } + + /* + * Check if the control request is also set to "RUN" + * Anything else should also return false, so the the loop will exit. + */ + if (AppRecPtr->ControlReq.AppControlRequest == CFE_ES_RunStatus_APP_RUN) + { + ReturnCode = true; + } + else + { + /* + * Just in case, also output the status, just in case the app looks at this. + */ + if (RunStatus != NULL) + { + *RunStatus = AppRecPtr->ControlReq.AppControlRequest; + } + ReturnCode = false; + } + } + else + { + /* + * Cannot do anything without the AppID + */ + CFE_ES_SysLogWrite_Unsync("CFE_ES_RunLoop Error: Cannot get AppID for the caller\n"); + ReturnCode = false; + + } /* end if Status == CFE_SUCCESS */ + + CFE_ES_UnlockSharedData(__func__, __LINE__); + + return (ReturnCode); + +} /* End of CFE_ES_RunLoop() */ + +/* +** Function: CFE_ES_WaitForSystemState - See API and header file for details +*/ +int32 CFE_ES_WaitForSystemState(uint32 MinSystemState, uint32 TimeOutMilliseconds) +{ + int32 Status = CFE_SUCCESS; + CFE_ES_AppRecord_t *AppRecPtr; + uint32 RequiredAppState; + uint32 WaitTime; + uint32 WaitRemaining; + + /* + * Calling app is assumed to have completed its own initialization up to the point + * it is waiting for. + * + * Determine the implicit app state based on the system state it is indicating + */ + CFE_ES_LockSharedData(__func__, __LINE__); + + /* + ** Get App Record + */ + AppRecPtr = CFE_ES_GetAppRecordByContext(); + if (AppRecPtr != NULL) + { + RequiredAppState = CFE_ES_AppState_EARLY_INIT; + /* + * If a core app waits for anything above "CORE_READY" then it is assumed to be RUNNING + * + * External apps have additional finer-grained sync: + * - SYSTEM_STATE_APPS_INIT requires that all apps are at least up to LATE_INIT + * - SYSTEM_STATE_OPERATIONAL requires that all apps are RUNNING + * - SYSTEM_STATE_SHUTDOWN requires that all apps are STOPPED (in concept anyway) + */ + if (AppRecPtr->Type == CFE_ES_AppType_CORE) + { + if (MinSystemState >= CFE_ES_SystemState_CORE_READY) + { + RequiredAppState = CFE_ES_AppState_RUNNING; + } + } + else if (MinSystemState >= CFE_ES_SystemState_SHUTDOWN) + { + RequiredAppState = CFE_ES_AppState_STOPPED; + } + else if (MinSystemState >= CFE_ES_SystemState_OPERATIONAL) + { + RequiredAppState = CFE_ES_AppState_RUNNING; + } + else if (MinSystemState >= CFE_ES_SystemState_APPS_INIT) + { + RequiredAppState = CFE_ES_AppState_LATE_INIT; + } + + /* + * NOTE -- a call to "CFE_ES_WaitForSystemState()" implies that the calling app MUST also + * be in the requisite state. This is hooked into here to avoid needing to update all existing + * apps to add an explicit state change call, but it makes sense because if this was not done an app could + * be waiting for itself (which will always time out). + */ + if (AppRecPtr->AppState < RequiredAppState) + { + AppRecPtr->AppState = RequiredAppState; + } + } + CFE_ES_UnlockSharedData(__func__, __LINE__); + + /* + * Do the actual delay loop. + * + * This is only dependent on the main (startup) task updating the global variable + * to be at least the state requested. + */ + WaitRemaining = TimeOutMilliseconds; + while (CFE_ES_Global.SystemState < MinSystemState) + { + /* TBD: Very Crude timing here, but not sure if it matters, + * as this is only done during startup, not real work */ + if (WaitRemaining > CFE_PLATFORM_ES_STARTUP_SYNC_POLL_MSEC) + { + WaitTime = CFE_PLATFORM_ES_STARTUP_SYNC_POLL_MSEC; + } + else if (WaitRemaining > 0) + { + WaitTime = WaitRemaining; + } + else + { + Status = CFE_ES_OPERATION_TIMED_OUT; + break; + } + + OS_TaskDelay(WaitTime); + WaitRemaining -= WaitTime; + } + + return Status; + +} /* End of CFE_ES_WaitForSystemState() */ + +/* +** Function: CFE_ES_WaitForStartupSync - See API and header file for details +*/ +void CFE_ES_WaitForStartupSync(uint32 TimeOutMilliseconds) +{ + CFE_ES_WaitForSystemState(CFE_ES_SystemState_OPERATIONAL, TimeOutMilliseconds); +} + +/* +** Function: CFE_ES_GetAppIDByName - See API and header file for details +*/ +int32 CFE_ES_GetAppIDByName(CFE_ES_AppId_t *AppIdPtr, const char *AppName) +{ + CFE_ES_AppRecord_t *AppRecPtr; + int32 Result; + + if (AppName == NULL || AppIdPtr == NULL) + { + return CFE_ES_BAD_ARGUMENT; + } + + CFE_ES_LockSharedData(__func__, __LINE__); + + AppRecPtr = CFE_ES_LocateAppRecordByName(AppName); + if (AppRecPtr == NULL) + { + /* + * ensure the output value is set to a safe value, + * in case the does not check the return code. + */ + Result = CFE_ES_ERR_NAME_NOT_FOUND; + *AppIdPtr = CFE_ES_APPID_UNDEFINED; + } + else + { + Result = CFE_SUCCESS; + *AppIdPtr = CFE_ES_AppRecordGetID(AppRecPtr); + } + + CFE_ES_UnlockSharedData(__func__, __LINE__); + + return (Result); + +} /* End of CFE_ES_GetAppIDByName() */ + +/* +** Function: CFE_ES_GetLibIDByName - See API and header file for details +*/ +int32 CFE_ES_GetLibIDByName(CFE_ES_LibId_t *LibIdPtr, const char *LibName) +{ + CFE_ES_LibRecord_t *LibRecPtr; + int32 Result; + + if (LibName == NULL || LibIdPtr == NULL) + { + return CFE_ES_BAD_ARGUMENT; + } + + CFE_ES_LockSharedData(__func__, __LINE__); + + LibRecPtr = CFE_ES_LocateLibRecordByName(LibName); + if (LibRecPtr == NULL) + { + /* + * ensure the output value is set to a safe value, + * in case the does not check the return code. + */ + Result = CFE_ES_ERR_NAME_NOT_FOUND; + *LibIdPtr = CFE_ES_LIBID_UNDEFINED; + } + else + { + Result = CFE_SUCCESS; + *LibIdPtr = CFE_ES_LibRecordGetID(LibRecPtr); + } + + CFE_ES_UnlockSharedData(__func__, __LINE__); + + return (Result); +} /* End of CFE_ES_GetLibIDByName() */ + +/* +** Function: CFE_ES_GetTaskIDByName - See API and header file for details +*/ +CFE_Status_t CFE_ES_GetTaskIDByName(CFE_ES_TaskId_t *TaskIdPtr, const char *TaskName) +{ + osal_id_t OsalId; + int32 Status; + CFE_Status_t Result; + + if (TaskName == NULL || TaskIdPtr == NULL) + { + return CFE_ES_BAD_ARGUMENT; + } + + /* For tasks IDs, defer to OSAL for name lookup */ + Status = OS_TaskGetIdByName(&OsalId, TaskName); + if (Status == OS_SUCCESS) + { + Result = CFE_SUCCESS; + *TaskIdPtr = CFE_ES_TaskId_FromOSAL(OsalId); + } + else + { + Result = CFE_ES_ERR_NAME_NOT_FOUND; + *TaskIdPtr = CFE_ES_TASKID_UNDEFINED; + } + + return (Result); +} /* End of CFE_ES_GetTaskIDByName() */ + +/* +** Function: CFE_ES_GetAppID - See API and header file for details +*/ +int32 CFE_ES_GetAppID(CFE_ES_AppId_t *AppIdPtr) +{ + CFE_ES_AppRecord_t *AppRecPtr; + int32 Result; + + if (AppIdPtr == NULL) + { + return CFE_ES_BAD_ARGUMENT; + } + + CFE_ES_LockSharedData(__func__, __LINE__); + + AppRecPtr = CFE_ES_GetAppRecordByContext(); + + if (AppRecPtr != NULL) + { + *AppIdPtr = CFE_ES_AppRecordGetID(AppRecPtr); + Result = CFE_SUCCESS; + } + else + { + *AppIdPtr = CFE_ES_APPID_UNDEFINED; + Result = CFE_ES_ERR_RESOURCEID_NOT_VALID; + } + + CFE_ES_UnlockSharedData(__func__, __LINE__); + + return (Result); + +} /* End of CFE_ES_GetAppID() */ + +/* +** Function: CFE_ES_GetTaskID - See API and header file for details +*/ +int32 CFE_ES_GetTaskID(CFE_ES_TaskId_t *TaskIdPtr) +{ + int32 Result; + CFE_ES_TaskRecord_t *TaskRecPtr; + + if (TaskIdPtr == NULL) + { + return CFE_ES_BAD_ARGUMENT; + } + + CFE_ES_LockSharedData(__func__, __LINE__); + TaskRecPtr = CFE_ES_GetTaskRecordByContext(); + if (TaskRecPtr == NULL) + { + *TaskIdPtr = CFE_ES_TASKID_UNDEFINED; + Result = CFE_ES_ERR_RESOURCEID_NOT_VALID; + } + else + { + *TaskIdPtr = CFE_ES_TaskRecordGetID(TaskRecPtr); + Result = CFE_SUCCESS; + } + CFE_ES_UnlockSharedData(__func__, __LINE__); + return Result; +} + +/* +** Function: CFE_ES_GetAppName - See API and header file for details +*/ +int32 CFE_ES_GetAppName(char *AppName, CFE_ES_AppId_t AppId, size_t BufferLength) +{ + int32 Result; + CFE_ES_AppRecord_t *AppRecPtr; + + if (BufferLength == 0 || AppName == NULL) + { + return CFE_ES_BAD_ARGUMENT; + } + + /* + ** Get App Record + */ + AppRecPtr = CFE_ES_LocateAppRecordByID(AppId); + + CFE_ES_LockSharedData(__func__, __LINE__); + + /* + * confirm that the app record is a match, + * which must be done while locked. + */ + if (CFE_ES_AppRecordIsMatch(AppRecPtr, AppId)) + { + strncpy(AppName, CFE_ES_AppRecordGetName(AppRecPtr), BufferLength - 1); + AppName[BufferLength - 1] = '\0'; + Result = CFE_SUCCESS; + } + else + { + AppName[0] = 0; + Result = CFE_ES_ERR_RESOURCEID_NOT_VALID; + } + + CFE_ES_UnlockSharedData(__func__, __LINE__); + + return (Result); + +} /* End of CFE_ES_GetAppName() */ + +/* +** Function: CFE_ES_GetLibName - See API and header file for details +*/ +int32 CFE_ES_GetLibName(char *LibName, CFE_ES_LibId_t LibId, size_t BufferLength) +{ + int32 Result; + CFE_ES_LibRecord_t *LibRecPtr; + + if (BufferLength == 0 || LibName == NULL) + { + return CFE_ES_BAD_ARGUMENT; + } + + /* + ** Get Lib Record + */ + LibRecPtr = CFE_ES_LocateLibRecordByID(LibId); + + CFE_ES_LockSharedData(__func__, __LINE__); + + /* + * confirm that the Lib record is a match, + * which must be done while locked. + */ + if (CFE_ES_LibRecordIsMatch(LibRecPtr, LibId)) + { + strncpy(LibName, CFE_ES_LibRecordGetName(LibRecPtr), BufferLength - 1); + LibName[BufferLength - 1] = '\0'; + Result = CFE_SUCCESS; + } + else + { + LibName[0] = 0; + Result = CFE_ES_ERR_RESOURCEID_NOT_VALID; + } + + CFE_ES_UnlockSharedData(__func__, __LINE__); + + return (Result); + +} /* End of CFE_ES_GetLibName() */ + +/* +** Function: CFE_ES_GetTaskName - See API and header file for details +*/ +int32 CFE_ES_GetTaskName(char *TaskName, CFE_ES_TaskId_t TaskId, size_t BufferLength) +{ + int32 Result; + osal_id_t OsalId; + + if (BufferLength == 0 || TaskName == NULL) + { + return CFE_ES_BAD_ARGUMENT; + } + + if (!CFE_RESOURCEID_TEST_DEFINED(TaskId)) + { + return CFE_ES_ERR_RESOURCEID_NOT_VALID; + } + + /* + * Query OSAL to get the task name + */ + OsalId = CFE_ES_TaskId_ToOSAL(TaskId); + Result = OS_GetResourceName(OsalId, TaskName, BufferLength); + + if (Result != OS_SUCCESS) + { + return CFE_ES_ERR_RESOURCEID_NOT_VALID; + } + + return CFE_SUCCESS; + +} /* End of CFE_ES_GetTaskName() */ + +/* +** Function: CFE_ES_GetAppInfo - See API and header file for details +*/ +int32 CFE_ES_GetAppInfo(CFE_ES_AppInfo_t *AppInfo, CFE_ES_AppId_t AppId) +{ + CFE_ES_AppRecord_t * AppRecPtr; + CFE_ES_TaskRecord_t *TaskRecPtr; + int32 Status; + osal_id_t ModuleId; + uint32 i; + + if (AppInfo == NULL) + { + CFE_ES_WriteToSysLog("CFE_ES_GetAppInfo: Invalid Parameter ( Null Pointer )\n"); + return CFE_ES_BAD_ARGUMENT; + } + + memset(AppInfo, 0, sizeof(*AppInfo)); + ModuleId = OS_OBJECT_ID_UNDEFINED; + + AppRecPtr = CFE_ES_LocateAppRecordByID(AppId); + + CFE_ES_LockSharedData(__func__, __LINE__); + + if (!CFE_ES_AppRecordIsMatch(AppRecPtr, AppId)) + { + /* + * Log a message if called with an invalid ID. + */ + CFE_ES_WriteToSysLog("CFE_ES_GetAppInfo: App ID not active: %lu\n", CFE_RESOURCEID_TO_ULONG(AppId)); + + Status = CFE_ES_ERR_RESOURCEID_NOT_VALID; + } + else + { + AppInfo->ResourceId = CFE_RESOURCEID_UNWRAP(AppId); /* make into a generic resource ID */ + AppInfo->Type = AppRecPtr->Type; + + strncpy(AppInfo->Name, CFE_ES_AppRecordGetName(AppRecPtr), sizeof(AppInfo->Name) - 1); + + CFE_ES_CopyModuleBasicInfo(&AppRecPtr->StartParams.BasicInfo, AppInfo); + CFE_ES_CopyModuleStatusInfo(&AppRecPtr->LoadStatus, AppInfo); + + AppInfo->ExceptionAction = AppRecPtr->StartParams.ExceptionAction; + AppInfo->MainTaskId = AppRecPtr->MainTaskId; + + ModuleId = AppRecPtr->LoadStatus.ModuleId; + + /* + ** Calculate the number of child tasks + */ + AppInfo->NumOfChildTasks = 0; + TaskRecPtr = CFE_ES_Global.TaskTable; + for (i = 0; i < OS_MAX_TASKS; i++) + { + if (CFE_ES_TaskRecordIsUsed(TaskRecPtr) && CFE_RESOURCEID_TEST_EQUAL(TaskRecPtr->AppId, AppId)) + { + if (CFE_RESOURCEID_TEST_EQUAL(CFE_ES_TaskRecordGetID(TaskRecPtr), AppInfo->MainTaskId)) + { + /* This is the main task - capture its name and execution count */ + AppInfo->ExecutionCounter = TaskRecPtr->ExecutionCounter; + strncpy(AppInfo->MainTaskName, TaskRecPtr->TaskName, sizeof(AppInfo->MainTaskName) - 1); + AppInfo->MainTaskName[sizeof(AppInfo->MainTaskName) - 1] = '\0'; + + AppInfo->StackSize = TaskRecPtr->StartParams.StackSize; + AppInfo->Priority = TaskRecPtr->StartParams.Priority; + } + else + { + /* This is a child task, no extra info, just increment count */ + ++AppInfo->NumOfChildTasks; + } + } + ++TaskRecPtr; + } + + Status = CFE_SUCCESS; + } + + CFE_ES_UnlockSharedData(__func__, __LINE__); + + /* + ** Get the address information from the OSAL + */ + if (Status == CFE_SUCCESS) + { + CFE_ES_CopyModuleAddressInfo(ModuleId, AppInfo); + } + + return Status; +} + +/* +** Function: CFE_ES_GetLibInfo - See API and header file for details +*/ +int32 CFE_ES_GetLibInfo(CFE_ES_AppInfo_t *LibInfo, CFE_ES_LibId_t LibId) +{ + int32 Status; + CFE_ES_LibRecord_t *LibRecPtr; + osal_id_t ModuleId; + + if (LibInfo == NULL) + { + CFE_ES_WriteToSysLog("CFE_ES_GetLibInfo: Invalid Parameter ( Null Pointer )\n"); + return CFE_ES_BAD_ARGUMENT; + } + + memset(LibInfo, 0, sizeof(*LibInfo)); + ModuleId = OS_OBJECT_ID_UNDEFINED; + + LibRecPtr = CFE_ES_LocateLibRecordByID(LibId); + + CFE_ES_LockSharedData(__func__, __LINE__); + + if (!CFE_ES_LibRecordIsMatch(LibRecPtr, LibId)) + { + /* + * Log a message if called with an invalid ID. + */ + CFE_ES_SysLogWrite_Unsync("CFE_ES_GetLibInfo: Lib ID not active: %lu\n", CFE_RESOURCEID_TO_ULONG(LibId)); + + Status = CFE_ES_ERR_RESOURCEID_NOT_VALID; + } + else + { + LibInfo->ResourceId = CFE_RESOURCEID_UNWRAP(LibId); /* make into generic ID */ + LibInfo->Type = CFE_ES_AppType_LIBRARY; + + strncpy(LibInfo->Name, CFE_ES_LibRecordGetName(LibRecPtr), sizeof(LibInfo->Name) - 1); + + CFE_ES_CopyModuleBasicInfo(&LibRecPtr->LoadParams, LibInfo); + CFE_ES_CopyModuleStatusInfo(&LibRecPtr->LoadStatus, LibInfo); + + ModuleId = LibRecPtr->LoadStatus.ModuleId; + + Status = CFE_SUCCESS; + } + + CFE_ES_UnlockSharedData(__func__, __LINE__); + + /* + ** Get the address information from the OSAL + */ + if (Status == CFE_SUCCESS) + { + CFE_ES_CopyModuleAddressInfo(ModuleId, LibInfo); + } + + return Status; +} + +/* +** Function: CFE_ES_GetModuleInfo - See API and header file for details +*/ +int32 CFE_ES_GetModuleInfo(CFE_ES_AppInfo_t *ModuleInfo, CFE_ResourceId_t ResourceId) +{ + int32 Status; + + switch (CFE_ResourceId_GetBase(ResourceId)) + { + case CFE_ES_APPID_BASE: + Status = CFE_ES_GetAppInfo(ModuleInfo, CFE_ES_APPID_C(ResourceId)); + break; + case CFE_ES_LIBID_BASE: + Status = CFE_ES_GetLibInfo(ModuleInfo, CFE_ES_LIBID_C(ResourceId)); + break; + default: + /* + * Log a message if called with an invalid ID. + */ + CFE_ES_WriteToSysLog("CFE_ES_GetModuleInfo: Resource ID not valid: %lu\n", + CFE_ResourceId_ToInteger(ResourceId)); + Status = CFE_ES_ERR_RESOURCEID_NOT_VALID; + break; + } + + return (Status); + +} /* End of CFE_ES_GetModuleInfo() */ + +/* +** Function: CFE_ES_GetTaskInfo - See API and header file for details +*/ +int32 CFE_ES_GetTaskInfo(CFE_ES_TaskInfo_t *TaskInfo, CFE_ES_TaskId_t TaskId) +{ + CFE_ES_TaskRecord_t *TaskRecPtr; + CFE_ES_AppRecord_t * AppRecPtr; + int32 Status; + + if (TaskInfo == NULL) + { + CFE_ES_WriteToSysLog("CFE_ES_GetTaskInfo: Invalid Parameter ( Null Pointer )\n"); + return CFE_ES_BAD_ARGUMENT; + } + + memset(TaskInfo, 0, sizeof(*TaskInfo)); + + TaskRecPtr = CFE_ES_LocateTaskRecordByID(TaskId); + + CFE_ES_LockSharedData(__func__, __LINE__); + + if (!CFE_ES_TaskRecordIsMatch(TaskRecPtr, TaskId)) + { + /* task ID is bad */ + Status = CFE_ES_ERR_RESOURCEID_NOT_VALID; + CFE_ES_SysLogWrite_Unsync("CFE_ES_GetTaskInfo: Task ID Not Active: %lu\n", CFE_RESOURCEID_TO_ULONG(TaskId)); + } + else + { + + /* + ** Get the Application ID and Task Name + */ + TaskInfo->AppId = TaskRecPtr->AppId; + strncpy(TaskInfo->TaskName, CFE_ES_TaskRecordGetName(TaskRecPtr), sizeof(TaskInfo->TaskName) - 1); + TaskInfo->TaskName[sizeof(TaskInfo->TaskName) - 1] = '\0'; + + /* + ** Store away the Task ID ( for the QueryAllTasks Cmd ) + */ + TaskInfo->TaskId = CFE_ES_TaskRecordGetID(TaskRecPtr); + + /* + ** Get the other stats for the task + */ + TaskInfo->ExecutionCounter = TaskRecPtr->ExecutionCounter; + TaskInfo->StackSize = TaskRecPtr->StartParams.StackSize; + TaskInfo->Priority = TaskRecPtr->StartParams.Priority; + + /* + ** Get the Application Details + */ + AppRecPtr = CFE_ES_LocateAppRecordByID(TaskRecPtr->AppId); + if (CFE_ES_AppRecordIsMatch(AppRecPtr, TaskRecPtr->AppId)) + { + strncpy(TaskInfo->AppName, CFE_ES_AppRecordGetName(AppRecPtr), sizeof(TaskInfo->AppName) - 1); + TaskInfo->AppName[sizeof(TaskInfo->AppName) - 1] = '\0'; + Status = CFE_SUCCESS; + } + else + { + /* task ID was OK but parent app ID is bad */ + Status = CFE_ES_ERR_RESOURCEID_NOT_VALID; + } + } + + CFE_ES_UnlockSharedData(__func__, __LINE__); + + return (Status); + +} /* End of CFE_ES_GetTaskInfo() */ + +/* +** Function: CFE_ES_CreateChildTask - See API and header file for details +*/ +int32 CFE_ES_CreateChildTask(CFE_ES_TaskId_t *TaskIdPtr, const char *TaskName, + CFE_ES_ChildTaskMainFuncPtr_t FunctionPtr, CFE_ES_StackPointer_t StackPtr, + size_t StackSize, CFE_ES_TaskPriority_Atom_t Priority, uint32 Flags) +{ + int32 ReturnCode; + CFE_ES_AppRecord_t * AppRecPtr; + CFE_ES_AppId_t ParentAppId; + CFE_ES_TaskId_t SelfTaskId; + CFE_ES_TaskStartParams_t Params; + + ParentAppId = CFE_ES_APPID_UNDEFINED; + + memset(&Params, 0, sizeof(Params)); + Params.Priority = Priority; + Params.StackSize = StackSize; + + /* + ** Validate some of the arguments + */ + if (TaskIdPtr == NULL) + { + if (TaskName == NULL) + { + CFE_ES_WriteToSysLog("CFE_ES_CreateChildTask: Task Id and Name Pointer Parameters are NULL.\n"); + ReturnCode = CFE_ES_BAD_ARGUMENT; + } + else + { + CFE_ES_WriteToSysLog("CFE_ES_CreateChildTask: Task Id Pointer Parameter is NULL for Task '%s'.\n", + TaskName); + ReturnCode = CFE_ES_BAD_ARGUMENT; + } + } + else if (TaskName == NULL) + { + CFE_ES_WriteToSysLog("CFE_ES_CreateChildTask: TaskName Parameter is NULL\n"); + ReturnCode = CFE_ES_BAD_ARGUMENT; + } + else if (FunctionPtr == NULL) + { + CFE_ES_WriteToSysLog("CFE_ES_CreateChildTask: Function Pointer Parameter is NULL for Task '%s'\n", TaskName); + ReturnCode = CFE_ES_BAD_ARGUMENT; + } + else + { + /* + ** First, Make sure the Calling Task is a cFE Main task. + ** TaskID must be the same as the Parent Task ID. + */ + SelfTaskId = CFE_ES_TaskId_FromOSAL(OS_TaskGetId()); + + CFE_ES_LockSharedData(__func__, __LINE__); + + /* + ** Get the App Record of the calling Application + */ + AppRecPtr = CFE_ES_GetAppRecordByContext(); + if (AppRecPtr == NULL) + { + CFE_ES_SysLogWrite_Unsync("CFE_ES_CreateChildTask: Invalid calling context when creating Task '%s'\n", + TaskName); + ReturnCode = CFE_ES_ERR_RESOURCEID_NOT_VALID; + } + else if (!CFE_RESOURCEID_TEST_EQUAL(SelfTaskId, AppRecPtr->MainTaskId)) + { + CFE_ES_SysLogWrite_Unsync("CFE_ES_CreateChildTask: Error: Cannot call from a Child Task (for Task '%s').\n", + TaskName); + ReturnCode = CFE_ES_ERR_CHILD_TASK_CREATE; + } + else + { + ParentAppId = CFE_ES_AppRecordGetID(AppRecPtr); + ReturnCode = CFE_SUCCESS; + } /* end If AppID is valid */ + + CFE_ES_UnlockSharedData(__func__, __LINE__); + + } /* end if parameter checking */ + + /* + ** Step 2: Create the new task if the parameter validation succeeded + */ + if (ReturnCode == CFE_SUCCESS) + { + ReturnCode = CFE_ES_StartAppTask(TaskIdPtr, TaskName, FunctionPtr, &Params, ParentAppId); + } + + return (ReturnCode); + +} /* End of CFE_ES_CreateChildTask() */ + +/* +** Function: CFE_ES_IncrementTaskCounter - See API and header file for details +*/ +void CFE_ES_IncrementTaskCounter(void) +{ + CFE_ES_TaskRecord_t *TaskRecPtr; + CFE_ES_TaskId_t TaskID; + + /* + * Note this locates a task record but intentionally does _not_ + * lock the global data structure. Locking is avoided for + * efficiency reasons. + * + * As tasks can only increment their own counters, there is no risk + * of concurrent access to the same counter. + * + * Because the global data is not locked, only minimal validation + * is performed. + */ + TaskID = CFE_ES_TaskId_FromOSAL(OS_TaskGetId()); + TaskRecPtr = CFE_ES_LocateTaskRecordByID(TaskID); + if (TaskRecPtr != NULL) + { + TaskRecPtr->ExecutionCounter++; + } + +} /* End of CFE_ES_IncrementTaskCounter() */ + +/* +** Function: CFE_ES_DeleteChildTask - See API and header file for details +*/ +int32 CFE_ES_DeleteChildTask(CFE_ES_TaskId_t TaskId) +{ + CFE_ES_TaskRecord_t *TaskRecPtr; + CFE_ES_AppRecord_t * AppRecPtr; + uint32 i; + bool TaskIsMain; + int32 ReturnCode = CFE_SUCCESS; + int32 OSReturnCode; + osal_id_t OsalId; + + /* + ** Make sure the task ID is within range + */ + TaskRecPtr = CFE_ES_LocateTaskRecordByID(TaskId); + if (TaskRecPtr != NULL) + { + CFE_ES_LockSharedData(__func__, __LINE__); + + /* + ** Make sure the task is active/valid + */ + if (CFE_ES_TaskRecordIsMatch(TaskRecPtr, TaskId)) + { + /* + ** Search for this task ID in the ES App Table to make sure + ** it is not a cFE App Main Task + */ + TaskIsMain = false; + AppRecPtr = CFE_ES_Global.AppTable; + for (i = 0; i < CFE_PLATFORM_ES_MAX_APPLICATIONS; i++) + { + if (CFE_ES_AppRecordIsUsed(AppRecPtr)) + { + if (CFE_RESOURCEID_TEST_EQUAL(AppRecPtr->MainTaskId, TaskId)) + { + /* + ** Error, the task Id is an App Main Task ID + */ + TaskIsMain = true; + break; + } /* end if */ + } /* end if */ + ++AppRecPtr; + } /* end for */ + + if (TaskIsMain == false) + { + /* + ** Can delete the Task + */ + OsalId = CFE_ES_TaskId_ToOSAL(TaskId); + OSReturnCode = OS_TaskDelete(OsalId); + if (OSReturnCode == OS_SUCCESS) + { + /* + ** Invalidate the task table entry + */ + CFE_ES_TaskRecordSetFree(TaskRecPtr); + CFE_ES_Global.RegisteredTasks--; + + /* + ** Report the task delete + */ + CFE_ES_SysLogWrite_Unsync("CFE_ES_DeleteChildTask Task %lu Deleted\n", + CFE_RESOURCEID_TO_ULONG(TaskId)); + ReturnCode = CFE_SUCCESS; + } + else + { + CFE_ES_SysLogWrite_Unsync( + "CFE_ES_DeleteChildTask Error: Error Calling OS_TaskDelete: Task %lu, RC = 0x%08X\n", + CFE_RESOURCEID_TO_ULONG(TaskId), (unsigned int)OSReturnCode); + ReturnCode = CFE_ES_ERR_CHILD_TASK_DELETE; + } + } + else + { + /* + ** Error: The task is a cFE Application Main task + */ + CFE_ES_SysLogWrite_Unsync("CFE_ES_DeleteChildTask Error: Task %lu is a cFE Main Task.\n", + CFE_RESOURCEID_TO_ULONG(TaskId)); + ReturnCode = CFE_ES_ERR_CHILD_TASK_DELETE_MAIN_TASK; + } /* end if TaskMain == false */ + } + else + { + /* + ** Task ID is not in use, so it is invalid + */ + CFE_ES_SysLogWrite_Unsync("CFE_ES_DeleteChildTask Error: Task ID is not active: %lu\n", + CFE_RESOURCEID_TO_ULONG(TaskId)); + ReturnCode = CFE_ES_ERR_RESOURCEID_NOT_VALID; + + } /* end if */ + + CFE_ES_UnlockSharedData(__func__, __LINE__); + } + else + { + /* + ** Task ID is invalid ( too large ) + */ + CFE_ES_WriteToSysLog("CFE_ES_DeleteChildTask Error: Invalid Task ID: %lu\n", CFE_RESOURCEID_TO_ULONG(TaskId)); + ReturnCode = CFE_ES_ERR_RESOURCEID_NOT_VALID; + } + return (ReturnCode); + +} /* End of CFE_ES_DeleteTask() */ + +/* +** Function: CFE_ES_ExitChildTask +** +** Purpose: Stop execution of a child task. +** +*/ +void CFE_ES_ExitChildTask(void) +{ + CFE_ES_AppRecord_t * AppRecPtr; + CFE_ES_TaskRecord_t *TaskRecPtr; + + CFE_ES_LockSharedData(__func__, __LINE__); + + /* + ** Check to see if this is being called from a cFE Application's + ** main task. + */ + TaskRecPtr = CFE_ES_GetTaskRecordByContext(); + if (TaskRecPtr != NULL) + { + AppRecPtr = CFE_ES_LocateAppRecordByID(TaskRecPtr->AppId); + + if (CFE_ES_AppRecordIsMatch(AppRecPtr, TaskRecPtr->AppId) && + !CFE_ES_TaskRecordIsMatch(TaskRecPtr, AppRecPtr->MainTaskId)) + { + /* + ** Invalidate the task table entry + */ + CFE_ES_TaskRecordSetFree(TaskRecPtr); + CFE_ES_Global.RegisteredTasks--; + + CFE_ES_UnlockSharedData(__func__, __LINE__); + + /* + ** Call the OS AL routine + */ + OS_TaskExit(); + /* + ** Does not return from OS_TaskExit, except under unit test + */ + return; + } + else + { + CFE_ES_SysLogWrite_Unsync("CFE_ES_ExitChildTask Error: Cannot Call from a cFE App Main Task. ID = %lu\n", + CFE_RESOURCEID_TO_ULONG(CFE_ES_TaskRecordGetID(TaskRecPtr))); + } + } + else + { + CFE_ES_SysLogWrite_Unsync("CFE_ES_ExitChildTask called from invalid task context\n"); + } /* end if GetAppId */ + + CFE_ES_UnlockSharedData(__func__, __LINE__); + +} /* End of CFE_ES_ExitChildTask() */ + +/* +** Function: CFE_ES_WriteToSysLog - See API and header file for details +*/ +int32 CFE_ES_WriteToSysLog(const char *SpecStringPtr, ...) +{ + char TmpString[CFE_ES_MAX_SYSLOG_MSG_SIZE]; + int32 ReturnCode; + va_list ArgPtr; + + if (SpecStringPtr == NULL) + { + return CFE_ES_BAD_ARGUMENT; + } + + va_start(ArgPtr, SpecStringPtr); + CFE_ES_SysLog_vsnprintf(TmpString, sizeof(TmpString), SpecStringPtr, ArgPtr); + va_end(ArgPtr); + + /* + * Append to the syslog buffer, which must be done while locked. + * Only one thread can actively write into the buffer at time. + */ + CFE_ES_LockSharedData(__func__, __LINE__); + ReturnCode = CFE_ES_SysLogAppend_Unsync(TmpString); + CFE_ES_UnlockSharedData(__func__, __LINE__); + + /* Output the entry to the console */ + OS_printf("%s", TmpString); + + return (ReturnCode); + +} /* End of CFE_ES_WriteToSysLog() */ + +/* +** Function: CFE_ES_CalculateCRC - See API and header file for details +*/ +uint32 CFE_ES_CalculateCRC(const void *DataPtr, size_t DataLength, uint32 InputCRC, uint32 TypeCRC) +{ + uint32 i; + int16 Index; + int16 Crc = 0; + const uint8 *BufPtr; + uint8 ByteValue; + + static const uint16 CrcTable[256] = { + + 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, + 0xC481, 0x0440, 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, 0x0A00, 0xCAC1, 0xCB81, 0x0B40, + 0xC901, 0x09C0, 0x0880, 0xC841, 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, 0x1E00, 0xDEC1, + 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, + 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, + 0xF281, 0x3240, 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, 0x3C00, 0xFCC1, 0xFD81, 0x3D40, + 0xFF01, 0x3FC0, 0x3E80, 0xFE41, 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, 0x2800, 0xE8C1, + 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, + 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, + 0x2080, 0xE041, 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, 0x6600, 0xA6C1, 0xA781, 0x6740, + 0xA501, 0x65C0, 0x6480, 0xA441, 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, 0xAA01, 0x6AC0, + 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, + 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, + 0xB681, 0x7640, 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, 0x5000, 0x90C1, 0x9181, 0x5140, + 0x9301, 0x53C0, 0x5280, 0x9241, 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, 0x9C01, 0x5CC0, + 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, + 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, + 0x4C80, 0x8C41, 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, 0x8201, 0x42C0, 0x4380, 0x8341, + 0x4100, 0x81C1, 0x8081, 0x4040 + + }; + + if (DataPtr == NULL || DataLength == 0) + { + return InputCRC; + } + + switch (TypeCRC) + { + case CFE_MISSION_ES_CRC_32: + CFE_ES_WriteToSysLog("CFE ES Calculate CRC32 not Implemented\n"); + break; + + case CFE_MISSION_ES_CRC_16: + Crc = (int16)(0xFFFF & InputCRC); + BufPtr = (const uint8 *)DataPtr; + + for (i = 0; i < DataLength; i++, BufPtr++) + { + /* + * It is assumed that the supplied buffer is in a + * directly-accessible memory space that does not + * require special logic to access + */ + ByteValue = *BufPtr; + Index = ((Crc ^ ByteValue) & 0x00FF); + Crc = ((Crc >> 8) & 0x00FF) ^ CrcTable[Index]; + } + break; + + case CFE_MISSION_ES_CRC_8: + CFE_ES_WriteToSysLog("CFE ES Calculate CRC8 not Implemented\n"); + break; + + default: + break; + } + return (Crc); + +} /* End of CFE_ES_CalculateCRC() */ + +/* +** Function: CFE_ES_RegisterCDS +** +** Purpose: Allocate a data block for a Critical Data Store. +** +*/ +int32 CFE_ES_RegisterCDS(CFE_ES_CDSHandle_t *CDSHandlePtr, size_t BlockSize, const char *Name) +{ + int32 Status; + size_t NameLen; + CFE_ES_AppId_t ThisAppId; + + char AppName[OS_MAX_API_NAME] = {"UNKNOWN"}; + char CDSName[CFE_MISSION_ES_CDS_MAX_FULL_NAME_LEN] = {""}; + + /* Check to make sure calling application is legit */ + Status = CFE_ES_GetAppID(&ThisAppId); + + if (CDSHandlePtr == NULL || Name == NULL) + { + CFE_ES_WriteToSysLog("CFE_ES_RegisterCDS:-Failed invalid arguments\n"); + return CFE_ES_BAD_ARGUMENT; + } + + /* Initialize output to safe value, in case this fails */ + *CDSHandlePtr = CFE_ES_CDS_BAD_HANDLE; + + if (Status != CFE_SUCCESS) /* Application ID was invalid */ + { + CFE_ES_WriteToSysLog("CFE_CDS:Register-Bad AppId context\n"); + } + else if (!CFE_ES_Global.CDSIsAvailable) + { + CFE_ES_WriteToSysLog("CFE_CDS:Register-CDS not available\n"); + Status = CFE_ES_NOT_IMPLEMENTED; + } + else + { + /* Make sure specified CDS name is not too long or too short */ + NameLen = strlen(Name); + if ((NameLen > CFE_MISSION_ES_CDS_MAX_NAME_LENGTH) || (NameLen == 0)) + { + Status = CFE_ES_CDS_INVALID_NAME; + + /* Perform a buffer overrun safe copy of name for debug log message */ + + strncpy(CDSName, Name, sizeof(CDSName) - 1); + CDSName[sizeof(CDSName) - 1] = '\0'; + CFE_ES_WriteToSysLog("CFE_CDS:Register-CDS Name (%s) is too long\n", CDSName); + } + else + { + /* Modify specified name to be processor specific name */ + /* of the form "AppName.Name" */ + CFE_ES_FormCDSName(CDSName, Name, ThisAppId); + + /* Create CDS and designate it as NOT being a Critical Table */ + Status = CFE_ES_RegisterCDSEx(CDSHandlePtr, BlockSize, CDSName, false); + + /* If size is unacceptable, log it */ + if (Status == CFE_ES_CDS_INVALID_SIZE) + { + CFE_ES_WriteToSysLog("CFE_CDS:Register-CDS %s has invalid size (%lu)\n", Name, + (unsigned long)BlockSize); + } + } + } + + /* On Error conditions, notify ground of screw up */ + if (Status < 0) + { + /* Translate AppID of caller into App Name */ + CFE_ES_GetAppName(AppName, ThisAppId, sizeof(AppName)); + + CFE_EVS_SendEventWithAppID(CFE_ES_CDS_REGISTER_ERR_EID, CFE_EVS_EventType_ERROR, ThisAppId, + "%s Failed to Register CDS '%s', Status=0x%08X", AppName, Name, + (unsigned int)Status); + } + + return Status; +} /* End of CFE_ES_RegisterCDS */ + +/* + * Function: CFE_ES_GetCDSBlockIDByName + * + * Purpose: Obtain CDS Block ID from name + * See full API description in header file + */ +CFE_Status_t CFE_ES_GetCDSBlockIDByName(CFE_ES_CDSHandle_t *BlockIdPtr, const char *BlockName) +{ + CFE_Status_t Status; + CFE_ES_CDS_RegRec_t *RegRecPtr; + + if (BlockName == NULL || BlockIdPtr == NULL) + { + return CFE_ES_BAD_ARGUMENT; + } + if (!CFE_ES_Global.CDSIsAvailable) + { + return CFE_ES_NOT_IMPLEMENTED; + } + + CFE_ES_LockCDS(); + + RegRecPtr = CFE_ES_LocateCDSBlockRecordByName(BlockName); + + if (RegRecPtr != NULL) + { + *BlockIdPtr = CFE_ES_CDSBlockRecordGetID(RegRecPtr); + Status = CFE_SUCCESS; + } + else + { + *BlockIdPtr = CFE_ES_CDS_BAD_HANDLE; + Status = CFE_ES_ERR_NAME_NOT_FOUND; + } + + CFE_ES_UnlockCDS(); + + return Status; +} + +/* + * Function: CFE_ES_GetCDSBlockName + * + * Purpose: Obtain CDS Block Name from ID + * See full API description in header file + */ +CFE_Status_t CFE_ES_GetCDSBlockName(char *BlockName, CFE_ES_CDSHandle_t BlockId, size_t BufferLength) +{ + CFE_Status_t Status; + CFE_ES_CDS_RegRec_t *RegRecPtr; + + if (BufferLength == 0 || BlockName == NULL) + { + return CFE_ES_BAD_ARGUMENT; + } + if (!CFE_ES_Global.CDSIsAvailable) + { + return CFE_ES_NOT_IMPLEMENTED; + } + + RegRecPtr = CFE_ES_LocateCDSBlockRecordByID(BlockId); + + CFE_ES_LockCDS(); + + if (CFE_ES_CDSBlockRecordIsMatch(RegRecPtr, BlockId)) + { + strncpy(BlockName, RegRecPtr->Name, BufferLength - 1); + BlockName[BufferLength - 1] = 0; + Status = CFE_SUCCESS; + } + else + { + BlockName[0] = 0; + Status = CFE_ES_ERR_RESOURCEID_NOT_VALID; + } + + CFE_ES_UnlockCDS(); + + return Status; +} + +/* +** Function: CFE_ES_CopyToCDS +** +** Purpose: Copies a data block to a Critical Data Store. +** +*/ +int32 CFE_ES_CopyToCDS(CFE_ES_CDSHandle_t Handle, void *DataToCopy) +{ + if (DataToCopy == NULL) + { + return CFE_ES_BAD_ARGUMENT; + } + + return CFE_ES_CDSBlockWrite(Handle, DataToCopy); +} /* End of CFE_ES_CopyToCDS() */ + +/* +** Function: CFE_ES_RestoreFromCDS +** +** Purpose: Restores a data block from a Critical Data Store. +** +*/ +int32 CFE_ES_RestoreFromCDS(void *RestoreToMemory, CFE_ES_CDSHandle_t Handle) +{ + if (RestoreToMemory == NULL) + { + return CFE_ES_BAD_ARGUMENT; + } + + return CFE_ES_CDSBlockRead(RestoreToMemory, Handle); +} /* End of CFE_ES_RestoreFromCDS() */ + +/* +** Function: CFE_ES_RegisterGenCounter +** +** Purpose: Allocates a generic counter resource and assigns ID +*/ +int32 CFE_ES_RegisterGenCounter(CFE_ES_CounterId_t *CounterIdPtr, const char *CounterName) +{ + CFE_ES_GenCounterRecord_t *CountRecPtr; + CFE_ResourceId_t PendingResourceId; + int32 Status; + + if (CounterName == NULL || CounterIdPtr == NULL) + { + return CFE_ES_BAD_ARGUMENT; + } + + if (strlen(CounterName) >= sizeof(CountRecPtr->CounterName)) + { + return CFE_ES_BAD_ARGUMENT; + } + + CFE_ES_LockSharedData(__func__, __LINE__); + + /* + * Check for an existing entry with the same name. + */ + CountRecPtr = CFE_ES_LocateCounterRecordByName(CounterName); + if (CountRecPtr != NULL) + { + CFE_ES_SysLogWrite_Unsync("ES Startup: Duplicate Counter name '%s'\n", CounterName); + Status = CFE_ES_ERR_DUPLICATE_NAME; + PendingResourceId = CFE_RESOURCEID_UNDEFINED; + } + else + { + /* scan for a free slot */ + PendingResourceId = CFE_ResourceId_FindNext(CFE_ES_Global.LastCounterId, CFE_PLATFORM_ES_MAX_GEN_COUNTERS, + CFE_ES_CheckCounterIdSlotUsed); + CountRecPtr = CFE_ES_LocateCounterRecordByID(CFE_ES_COUNTERID_C(PendingResourceId)); + + if (CountRecPtr == NULL) + { + CFE_ES_SysLogWrite_Unsync("ES Startup: No free Counter slots available\n"); + Status = CFE_ES_NO_RESOURCE_IDS_AVAILABLE; + } + else + { + strncpy(CountRecPtr->CounterName, CounterName, sizeof(CountRecPtr->CounterName) - 1); + CountRecPtr->CounterName[sizeof(CountRecPtr->CounterName) - 1] = '\0'; + CountRecPtr->Counter = 0; + CFE_ES_CounterRecordSetUsed(CountRecPtr, PendingResourceId); + CFE_ES_Global.LastCounterId = PendingResourceId; + Status = CFE_SUCCESS; + } + } + + CFE_ES_UnlockSharedData(__func__, __LINE__); + + *CounterIdPtr = CFE_ES_COUNTERID_C(PendingResourceId); + return Status; +} + +/* +** Function: CFE_ES_DeleteGenCounter +** +** Purpose: Delete a Generic Counter. +** +*/ +int32 CFE_ES_DeleteGenCounter(CFE_ES_CounterId_t CounterId) +{ + CFE_ES_GenCounterRecord_t *CountRecPtr; + int32 Status = CFE_ES_BAD_ARGUMENT; + + CountRecPtr = CFE_ES_LocateCounterRecordByID(CounterId); + if (CountRecPtr != NULL) + { + CFE_ES_LockSharedData(__func__, __LINE__); + if (CFE_ES_CounterRecordIsMatch(CountRecPtr, CounterId)) + { + CountRecPtr->Counter = 0; + CFE_ES_CounterRecordSetFree(CountRecPtr); + Status = CFE_SUCCESS; + } + CFE_ES_UnlockSharedData(__func__, __LINE__); + } + + return Status; + +} /* End of CFE_ES_DeleteGenCounter() */ + +/* +** Function: CFE_ES_IncrementGenCounter +** +** Purpose: Increment a Generic Counter. +** +*/ +int32 CFE_ES_IncrementGenCounter(CFE_ES_CounterId_t CounterId) +{ + int32 Status = CFE_ES_BAD_ARGUMENT; + CFE_ES_GenCounterRecord_t *CountRecPtr; + + CountRecPtr = CFE_ES_LocateCounterRecordByID(CounterId); + if (CFE_ES_CounterRecordIsMatch(CountRecPtr, CounterId)) + { + ++CountRecPtr->Counter; + Status = CFE_SUCCESS; + } + return Status; + +} /* End of CFE_ES_IncrementGenCounter() */ + +/* +** Function: CFE_ES_SetGenCount +** +** Purpose: Sets a Generic Counter's count. +** +*/ +int32 CFE_ES_SetGenCount(CFE_ES_CounterId_t CounterId, uint32 Count) +{ + int32 Status = CFE_ES_BAD_ARGUMENT; + CFE_ES_GenCounterRecord_t *CountRecPtr; + + CountRecPtr = CFE_ES_LocateCounterRecordByID(CounterId); + if (CFE_ES_CounterRecordIsMatch(CountRecPtr, CounterId)) + { + CountRecPtr->Counter = Count; + Status = CFE_SUCCESS; + } + return Status; +} /* End of CFE_ES_SetGenCount() */ + +/* +** Function: CFE_ES_GetGenCount +** +** Purpose: Gets the value of a Generic Counter. +** +*/ +int32 CFE_ES_GetGenCount(CFE_ES_CounterId_t CounterId, uint32 *Count) +{ + int32 Status = CFE_ES_BAD_ARGUMENT; + CFE_ES_GenCounterRecord_t *CountRecPtr; + + CountRecPtr = CFE_ES_LocateCounterRecordByID(CounterId); + if (CFE_ES_CounterRecordIsMatch(CountRecPtr, CounterId) && Count != NULL) + { + *Count = CountRecPtr->Counter; + Status = CFE_SUCCESS; + } + return Status; +} /* End of CFE_ES_GetGenCount() */ + +int32 CFE_ES_GetGenCounterIDByName(CFE_ES_CounterId_t *CounterIdPtr, const char *CounterName) +{ + CFE_ES_GenCounterRecord_t *CounterRecPtr; + int32 Result; + + if (CounterName == NULL || CounterIdPtr == NULL) + { + return CFE_ES_BAD_ARGUMENT; + } + + /* + ** Search the ES Generic Counter table for a counter with a matching name. + */ + CFE_ES_LockSharedData(__func__, __LINE__); + + CounterRecPtr = CFE_ES_LocateCounterRecordByName(CounterName); + if (CounterRecPtr == NULL) + { + /* + * ensure the output value is set to a safe value, + * in case the does not check the return code. + */ + Result = CFE_ES_ERR_NAME_NOT_FOUND; + *CounterIdPtr = CFE_ES_COUNTERID_UNDEFINED; + } + else + { + Result = CFE_SUCCESS; + *CounterIdPtr = CFE_ES_CounterRecordGetID(CounterRecPtr); + } + + CFE_ES_UnlockSharedData(__func__, __LINE__); + + return (Result); + +} /* End of CFE_ES_GetGenCounterIDByName() */ + +/* + * Function: CFE_ES_GetGenCounterName + * + * Purpose: Obtain Counter Name from ID + * See full API description in header file + */ +CFE_Status_t CFE_ES_GetGenCounterName(char *CounterName, CFE_ES_CounterId_t CounterId, size_t BufferLength) +{ + CFE_ES_GenCounterRecord_t *CountRecPtr; + CFE_Status_t Status; + + if (BufferLength == 0 || CounterName == NULL) + { + return CFE_ES_BAD_ARGUMENT; + } + + CountRecPtr = CFE_ES_LocateCounterRecordByID(CounterId); + + CFE_ES_LockSharedData(__func__, __LINE__); + + if (CFE_ES_CounterRecordIsMatch(CountRecPtr, CounterId)) + { + strncpy(CounterName, CFE_ES_CounterRecordGetName(CountRecPtr), BufferLength - 1); + CounterName[BufferLength - 1] = 0; + Status = CFE_SUCCESS; + } + else + { + CounterName[0] = 0; + Status = CFE_ES_ERR_RESOURCEID_NOT_VALID; + } + + CFE_ES_UnlockSharedData(__func__, __LINE__); + + return Status; +} + +/* + * A conversion function to obtain an index value correlating to an AppID + * This is a zero based value that can be used for indexing into a table. + */ +int32 CFE_ES_AppID_ToIndex(CFE_ES_AppId_t AppID, uint32 *Idx) +{ + return CFE_ResourceId_ToIndex(CFE_RESOURCEID_UNWRAP(AppID), CFE_ES_APPID_BASE, CFE_PLATFORM_ES_MAX_APPLICATIONS, + Idx); +} + +/* + * A conversion function to obtain an index value correlating to a LibID + * This is a zero based value that can be used for indexing into a table. + */ +int32 CFE_ES_LibID_ToIndex(CFE_ES_LibId_t LibId, uint32 *Idx) +{ + return CFE_ResourceId_ToIndex(CFE_RESOURCEID_UNWRAP(LibId), CFE_ES_LIBID_BASE, CFE_PLATFORM_ES_MAX_LIBRARIES, Idx); +} + +/* + * A conversion function to obtain an index value correlating to an TaskID + * This is a zero based value that can be used for indexing into a table. + * + * Task IDs come from OSAL, so this is currently a wrapper around the OSAL converter. + * This is an alias for consistency with the ES AppID paradigm. + */ +int32 CFE_ES_TaskID_ToIndex(CFE_ES_TaskId_t TaskID, uint32 *Idx) +{ + osal_id_t OsalID; + osal_index_t OsalIndex; + + if (!CFE_RESOURCEID_TEST_DEFINED(TaskID)) + { + return CFE_ES_ERR_RESOURCEID_NOT_VALID; + } + + OsalID = CFE_ES_TaskId_ToOSAL(TaskID); + if (OS_ObjectIdToArrayIndex(OS_OBJECT_TYPE_OS_TASK, OsalID, &OsalIndex) != OS_SUCCESS) + { + return CFE_ES_ERR_RESOURCEID_NOT_VALID; + } + + *Idx = OsalIndex; + + return CFE_SUCCESS; +} + +/* + * A conversion function to obtain an index value correlating to a CounterID + * This is a zero based value that can be used for indexing into a table. + */ +int32 CFE_ES_CounterID_ToIndex(CFE_ES_CounterId_t CounterId, uint32 *Idx) +{ + return CFE_ResourceId_ToIndex(CFE_RESOURCEID_UNWRAP(CounterId), CFE_ES_COUNTID_BASE, + CFE_PLATFORM_ES_MAX_GEN_COUNTERS, Idx); +} + +/*************************************************************************************** +** Private API functions +*/ + +/****************************************************************************** +** Function: CFE_ES_LockSharedData() +** +** Purpose: +** ES internal function to take the Shared Data Mutex and handle +** error conditions. +** +** Arguments: +** FunctionName - the name of the function containing the code that generated the error. +** LineNumber - the file line number of the code that generated the error. +** +** Return: +** None +*/ +void CFE_ES_LockSharedData(const char *FunctionName, int32 LineNumber) +{ + int32 Status; + + Status = OS_MutSemTake(CFE_ES_Global.SharedDataMutex); + if (Status != OS_SUCCESS) + { + /* + * NOTE: this is going to write into a buffer that itself + * is _supposed_ to be protected by this same mutex. + */ + CFE_ES_SysLogWrite_Unsync("ES SharedData Mutex Take Err Stat=0x%x,Func=%s,Line=%d\n", (unsigned int)Status, + FunctionName, (int)LineNumber); + + } /* end if */ + + return; + +} /* end CFE_ES_LockSharedData */ + +/****************************************************************************** +** Function: CFE_ES_UnlockSharedData() +** +** Purpose: +** ES internal function to Release the shared data mutex and handle error +** conditions. +** +** Arguments: +** FunctionName - the name of the function containing the code that generated the error. +** LineNumber - the file line number of the code that generated the error. +** +** Return: +** None +*/ +void CFE_ES_UnlockSharedData(const char *FunctionName, int32 LineNumber) +{ + int32 Status; + + Status = OS_MutSemGive(CFE_ES_Global.SharedDataMutex); + if (Status != OS_SUCCESS) + { + /* + * NOTE: this is going to write into a buffer that itself + * is _supposed_ to be protected by this same mutex. + */ + CFE_ES_SysLogWrite_Unsync("ES SharedData Mutex Give Err Stat=0x%x,Func=%s,Line=%d\n", (unsigned int)Status, + FunctionName, (int)LineNumber); + + } /* end if */ + + return; + +} /* end CFE_ES_UnlockSharedData */ + +/****************************************************************************** +** Function: CFE_ES_ProcessAsyncEvent() +** +** Purpose: +** Called by the PSP to notify CFE ES that an asynchronous event occurred. +*/ +void CFE_ES_ProcessAsyncEvent(void) +{ + /* This just wakes up the background task to log/handle the event. */ + CFE_ES_BackgroundWakeup(); +} diff --git a/modules/es/fsw/src/cfe_es_apps.c b/modules/es/fsw/src/cfe_es_apps.c new file mode 100644 index 000000000..aed7353c8 --- /dev/null +++ b/modules/es/fsw/src/cfe_es_apps.c @@ -0,0 +1,1819 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/* +** File: +** cfe_es_apps.c +** +** Purpose: +** This file contains functions for starting cFE applications from a filesystem. +** +** References: +** Flight Software Branch C Coding Standard Version 1.0a +** cFE Flight Software Application Developers Guide +** +** Notes: +** +*/ + +/* +** Includes +*/ +#include "cfe_es_module_all.h" + +#include "cfe_evs_core_internal.h" +#include "cfe_sb_core_internal.h" +#include "cfe_tbl_core_internal.h" +#include "cfe_time_core_internal.h" + +#include +#include /* memset() */ +#include + +/* +** Defines +*/ +#define ES_START_BUFF_SIZE 128 + +/* +** +** Global Variables +** +*/ + +/* +**************************************************************************** +** Functions +*************************************************************************** +*/ + +/* +** Name: +** CFE_ES_StartApplications +** +** Purpose: +** This routine loads/starts cFE applications. +** +*/ +void CFE_ES_StartApplications(uint32 ResetType, const char *StartFilePath) +{ + char ES_AppLoadBuffer[ES_START_BUFF_SIZE]; /* A buffer of for a line in a file */ + char ScriptFileName[OS_MAX_PATH_LEN]; + const char *TokenList[CFE_ES_STARTSCRIPT_MAX_TOKENS_PER_LINE]; + uint32 NumTokens; + uint32 BuffLen; /* Length of the current buffer */ + osal_id_t AppFile = OS_OBJECT_ID_UNDEFINED; + int32 Status; + char c; + bool LineTooLong = false; + bool FileOpened = false; + + /* + ** Get the ES startup script filename. + ** If this is a Processor Reset, try to open the file in the volatile disk first. + */ + if (ResetType == CFE_PSP_RST_TYPE_PROCESSOR) + { + /* + ** First Attempt to parse as file in the volatile disk (temp area). + */ + Status = CFE_FS_ParseInputFileName(ScriptFileName, CFE_PLATFORM_ES_VOLATILE_STARTUP_FILE, + sizeof(ScriptFileName), CFE_FS_FileCategory_TEMP); + + if (Status == CFE_SUCCESS) + { + Status = OS_OpenCreate(&AppFile, ScriptFileName, OS_FILE_FLAG_NONE, OS_READ_ONLY); + } + + if (Status >= 0) + { + CFE_ES_WriteToSysLog("ES Startup: Opened ES App Startup file: %s\n", ScriptFileName); + FileOpened = true; + } + else + { + CFE_ES_WriteToSysLog("ES Startup: Cannot Open Volatile Startup file, Trying Nonvolatile.\n"); + } + + } /* end if */ + + /* + ** This if block covers two cases: A Power on reset, and a Processor reset when + ** the startup file on the volatile file system could not be opened. + */ + if (FileOpened == false) + { + /* + ** Try to Open the file passed in to the cFE start. + */ + Status = CFE_FS_ParseInputFileName(ScriptFileName, StartFilePath, sizeof(ScriptFileName), + CFE_FS_FileCategory_SCRIPT); + + if (Status == CFE_SUCCESS) + { + Status = OS_OpenCreate(&AppFile, ScriptFileName, OS_FILE_FLAG_NONE, OS_READ_ONLY); + } + + if (Status >= 0) + { + CFE_ES_WriteToSysLog("ES Startup: Opened ES App Startup file: %s\n", ScriptFileName); + FileOpened = true; + } + else + { + CFE_ES_WriteToSysLog("ES Startup: Error, Can't Open ES App Startup file: %s EC = 0x%08X\n", StartFilePath, + (unsigned int)Status); + } + } + + /* + ** If the file is opened in either the Nonvolatile or the Volatile disk, process it. + */ + if (FileOpened == true) + { + memset(ES_AppLoadBuffer, 0x0, ES_START_BUFF_SIZE); + BuffLen = 0; + NumTokens = 0; + TokenList[0] = ES_AppLoadBuffer; + + /* + ** Parse the lines from the file. If it has an error + ** or reaches EOF, then abort the loop. + */ + while (1) + { + Status = OS_read(AppFile, &c, 1); + if (Status < 0) + { + CFE_ES_WriteToSysLog("ES Startup: Error Reading Startup file. EC = 0x%08X\n", (unsigned int)Status); + break; + } + else if (Status == 0) + { + /* + ** EOF Reached + */ + break; + } + else if (c != '!') + { + if (c <= ' ') + { + /* + ** Skip all white space in the file + */ + ; + } + else if (c == ',') + { + /* + ** replace the field delimiter with a null + ** This is used to separate the tokens + */ + if (BuffLen < ES_START_BUFF_SIZE) + { + ES_AppLoadBuffer[BuffLen] = 0; + } + else + { + LineTooLong = true; + } + BuffLen++; + + if (NumTokens < (CFE_ES_STARTSCRIPT_MAX_TOKENS_PER_LINE - 1)) + { + /* + * NOTE: pointer never deferenced unless "LineTooLong" is false. + */ + ++NumTokens; + TokenList[NumTokens] = &ES_AppLoadBuffer[BuffLen]; + } + } + else if (c != ';') + { + /* + ** Regular data gets copied in + */ + if (BuffLen < ES_START_BUFF_SIZE) + { + ES_AppLoadBuffer[BuffLen] = c; + } + else + { + LineTooLong = true; + } + BuffLen++; + } + else + { + if (LineTooLong == true) + { + /* + ** The was too big for the buffer + */ + CFE_ES_WriteToSysLog("ES Startup: ES Startup File Line is too long: %u bytes.\n", + (unsigned int)BuffLen); + LineTooLong = false; + } + else + { + /* + ** Send the line to the file parser + ** Ensure termination of the last token and send it along + */ + ES_AppLoadBuffer[BuffLen] = 0; + CFE_ES_ParseFileEntry(TokenList, 1 + NumTokens); + } + BuffLen = 0; + NumTokens = 0; + } + } + else + { + /* + ** break when EOF character '!' is reached + */ + break; + } + } + /* + ** close the file + */ + OS_close(AppFile); + } +} + +/* +**--------------------------------------------------------------------------------------- +** Name: CFE_ES_ParseFileEntry +** +** Purpose: This function parses the startup file line for an individual +** cFE application. +**--------------------------------------------------------------------------------------- +*/ +int32 CFE_ES_ParseFileEntry(const char **TokenList, uint32 NumTokens) +{ + const char * ModuleName; + const char * EntryType; + unsigned long ParsedValue; + union + { + CFE_ES_AppId_t AppId; + CFE_ES_LibId_t LibId; + } IdBuf; + int32 Status; + CFE_ES_AppStartParams_t ParamBuf; + + /* + ** Check to see if the correct number of items were parsed + */ + if (NumTokens < 8) + { + CFE_ES_WriteToSysLog("ES Startup: Invalid ES Startup file entry: %u\n", (unsigned int)NumTokens); + return CFE_ES_BAD_ARGUMENT; + } + + /* Get pointers to specific tokens that are simple strings used as-is */ + EntryType = TokenList[0]; + ModuleName = TokenList[3]; + + /* + * Other tokens will need to be scrubbed/converted. + * Both Libraries and Apps use File Name (1) and Symbol Name (2) fields so copy those now + */ + memset(&ParamBuf, 0, sizeof(ParamBuf)); + Status = CFE_FS_ParseInputFileName(ParamBuf.BasicInfo.FileName, TokenList[1], sizeof(ParamBuf.BasicInfo.FileName), + CFE_FS_FileCategory_DYNAMIC_MODULE); + if (Status != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("ES Startup: Invalid ES Startup script file name: %s\n", TokenList[1]); + return Status; + } + + strncpy(ParamBuf.BasicInfo.InitSymbolName, TokenList[2], sizeof(ParamBuf.BasicInfo.InitSymbolName) - 1); + + if (strcmp(EntryType, "CFE_APP") == 0) + { + CFE_ES_WriteToSysLog("ES Startup: Loading file: %s, APP: %s\n", ParamBuf.BasicInfo.FileName, ModuleName); + + /* + * Priority and Exception action have limited ranges, which is checked here + * Task priority cannot be bigger than OS_MAX_TASK_PRIORITY + */ + ParsedValue = strtoul(TokenList[4], NULL, 0); + if (ParsedValue > OS_MAX_TASK_PRIORITY) + { + ParamBuf.MainTaskInfo.Priority = OS_MAX_TASK_PRIORITY; + } + else + { + /* convert parsed value to correct type */ + ParamBuf.MainTaskInfo.Priority = (CFE_ES_TaskPriority_Atom_t)ParsedValue; + } + + /* No specific upper/lower limit for stack size - will pass value through */ + ParamBuf.MainTaskInfo.StackSize = strtoul(TokenList[5], NULL, 0); + + /* + ** Validate Some parameters + ** Exception action should be 0 ( Restart App ) or + ** 1 ( Processor reset ). If it's non-zero, assume it means + ** reset CPU. + */ + ParsedValue = strtoul(TokenList[7], NULL, 0); + if (ParsedValue > CFE_ES_ExceptionAction_RESTART_APP) + { + ParamBuf.ExceptionAction = CFE_ES_ExceptionAction_PROC_RESTART; + } + else + { + /* convert parsed value to correct type */ + ParamBuf.ExceptionAction = (CFE_ES_ExceptionAction_Enum_t)ParsedValue; + } + + /* + ** Now create the application + */ + Status = CFE_ES_AppCreate(&IdBuf.AppId, ModuleName, &ParamBuf); + } + else if (strcmp(EntryType, "CFE_LIB") == 0) + { + CFE_ES_WriteToSysLog("ES Startup: Loading shared library: %s\n", ParamBuf.BasicInfo.FileName); + + /* + ** Now load the library + */ + Status = CFE_ES_LoadLibrary(&IdBuf.LibId, ModuleName, &ParamBuf.BasicInfo); + } + else + { + CFE_ES_WriteToSysLog("ES Startup: Unexpected EntryType %s in startup file.\n", EntryType); + Status = CFE_ES_ERR_APP_CREATE; + } + + return (Status); +} + +/* +**------------------------------------------------------------------------------------- +** Name: CFE_ES_LoadModule +** +** Helper function to load + configure (but not start) a new app/lib module +** +** Loads the module file via OSAL and stores all relevant info in the table entry as necessary. +** +**------------------------------------------------------------------------------------- +*/ +int32 CFE_ES_LoadModule(CFE_ResourceId_t ParentResourceId, const char *ModuleName, + const CFE_ES_ModuleLoadParams_t *LoadParams, CFE_ES_ModuleLoadStatus_t *LoadStatus) +{ + osal_id_t ModuleId; + cpuaddr InitSymbolAddress; + int32 ReturnCode; + int32 StatusCode; + uint32 LoadFlags; + + LoadFlags = 0; + InitSymbolAddress = 0; + ReturnCode = CFE_SUCCESS; + + if (LoadParams->FileName[0] != 0) + { + switch (CFE_ResourceId_GetBase(ParentResourceId)) + { + case CFE_ES_APPID_BASE: + /* + * Apps should not typically have symbols exposed to other apps. + * + * Keeping symbols local/private may help ensure this module is unloadable + * in the future, depending on underlying OS/loader implementation. + */ + LoadFlags |= OS_MODULE_FLAG_LOCAL_SYMBOLS; + break; + case CFE_ES_LIBID_BASE: + /* + * Libraries need to have their symbols exposed to other apps. + * + * Note on some OS/loader implementations this may make it so the module + * cannot be unloaded, if there is no way to ensure that symbols + * are not being referenced. CFE does not currently support unloading + * of libraries for this reason, among others. + */ + LoadFlags |= OS_MODULE_FLAG_GLOBAL_SYMBOLS; + break; + default: + break; + } + + /* + * Load the module via OSAL. + */ + StatusCode = OS_ModuleLoad(&ModuleId, ModuleName, LoadParams->FileName, LoadFlags); + + if (StatusCode != OS_SUCCESS) + { + CFE_ES_WriteToSysLog("ES Startup: Could not load file:%s. EC = 0x%08X\n", LoadParams->FileName, + (unsigned int)StatusCode); + ModuleId = OS_OBJECT_ID_UNDEFINED; + ReturnCode = CFE_STATUS_EXTERNAL_RESOURCE_FAIL; + } + } + else + { + ModuleId = OS_OBJECT_ID_UNDEFINED; + } + + /* + * If the Load was OK, then lookup the address of the entry point + */ + if (ReturnCode == CFE_SUCCESS && LoadParams->InitSymbolName[0] != 0) + { + StatusCode = OS_ModuleSymbolLookup(ModuleId, &InitSymbolAddress, LoadParams->InitSymbolName); + if (StatusCode != OS_SUCCESS) + { + CFE_ES_WriteToSysLog("ES Startup: Could not find symbol:%s. EC = 0x%08X\n", LoadParams->InitSymbolName, + (unsigned int)StatusCode); + ReturnCode = CFE_STATUS_EXTERNAL_RESOURCE_FAIL; + } + } + + if (ReturnCode == CFE_SUCCESS) + { + /* store the data in the app record after successful load+lookup */ + LoadStatus->ModuleId = ModuleId; + LoadStatus->InitSymbolAddress = InitSymbolAddress; + } + else if (OS_ObjectIdDefined(ModuleId)) + { + /* If the module had been successfully loaded, then unload it, + * so that it does not consume resources */ + StatusCode = OS_ModuleUnload(ModuleId); + if (StatusCode != OS_SUCCESS) /* There's not much we can do except notify */ + { + CFE_ES_WriteToSysLog("ES Startup: Failed to unload: %s. EC = 0x%08X\n", ModuleName, + (unsigned int)StatusCode); + } + } + + return ReturnCode; +} + +/* +**------------------------------------------------------------------------------------- +** Name: CFE_ES_GetTaskFunction +** +** Helper function to act as the intermediate entry point of an app +** This is to support starting apps before having a fully completed entry in the +** global app table. The app startup will delay until the app creation is completed +** and verified, then the actual entry point will be determined. +** +**------------------------------------------------------------------------------------- +*/ +int32 CFE_ES_GetTaskFunction(CFE_ES_TaskEntryFuncPtr_t *FuncPtr) +{ + CFE_ES_TaskRecord_t * TaskRecPtr; + CFE_ES_TaskEntryFuncPtr_t EntryFunc; + int32 ReturnCode; + int32 Timeout; + + /* + * Use the same timeout as was used for the startup script itself. + */ + ReturnCode = CFE_ES_ERR_APP_REGISTER; + Timeout = CFE_PLATFORM_ES_STARTUP_SCRIPT_TIMEOUT_MSEC; + EntryFunc = NULL; + + while (true) + { + OS_TaskDelay(CFE_PLATFORM_ES_STARTUP_SYNC_POLL_MSEC); + + CFE_ES_LockSharedData(__func__, __LINE__); + TaskRecPtr = CFE_ES_GetTaskRecordByContext(); + if (TaskRecPtr != NULL) + { + EntryFunc = TaskRecPtr->EntryFunc; + if (CFE_RESOURCEID_TEST_DEFINED(TaskRecPtr->AppId) && EntryFunc != 0) + { + ReturnCode = CFE_SUCCESS; + } + } + CFE_ES_UnlockSharedData(__func__, __LINE__); + + if (ReturnCode == CFE_SUCCESS || Timeout <= 0) + { + /* end of loop condition */ + break; + } + + Timeout -= CFE_PLATFORM_ES_STARTUP_SYNC_POLL_MSEC; + } + + /* output function address to caller */ + if (FuncPtr != NULL) + { + *FuncPtr = EntryFunc; + } + + return (ReturnCode); +} + +/* +**------------------------------------------------------------------------------------- +** Name: CFE_ES_TaskEntryPoint +** +** Helper function to act as the intermediate entry point of an app +** This is to support starting apps before having a fully completed entry in the +** global app table. The app startup will delay until the app creation is completed +** and verified, then the actual entry point will be determined. +** +**------------------------------------------------------------------------------------- +*/ +void CFE_ES_TaskEntryPoint(void) +{ + CFE_ES_TaskEntryFuncPtr_t RealEntryFunc; + + if (CFE_ES_GetTaskFunction(&RealEntryFunc) == CFE_SUCCESS && RealEntryFunc != NULL) + { + /* + * Set the default exception environment, which should + * be done serialized (i.e. only one task at a time should + * call into CFE_PSP_SetDefaultExceptionEnvironment). + */ + CFE_ES_LockSharedData(__func__, __LINE__); + CFE_PSP_SetDefaultExceptionEnvironment(); + CFE_ES_UnlockSharedData(__func__, __LINE__); + + /* + * Call the actual task entry function + */ + (*RealEntryFunc)(); + } +} + +/* +**------------------------------------------------------------------------------------- +** Name: CFE_ES_StartMainTask +** +** Helper function to start (but not load) a new app/lib module +** +** Note that OSAL does not separate the action of creating and start a task, providing +** only OS_TaskCreate which does both. But there is a potential race condition if +** the real task code starts and calls any function that depends on having an AppID +** context before its fully registered in the global app table. +** +** Therefore this calls a dedicated CFE_ES_AppEntryPoint which then will wait until +** the task is fully registered in the global, before calling the actual app entry point. +** +**------------------------------------------------------------------------------------- +*/ +int32 CFE_ES_StartAppTask(CFE_ES_TaskId_t *TaskIdPtr, const char *TaskName, CFE_ES_TaskEntryFuncPtr_t EntryFunc, + const CFE_ES_TaskStartParams_t *Params, CFE_ES_AppId_t ParentAppId) +{ + CFE_ES_TaskRecord_t *TaskRecPtr; + osal_id_t OsalTaskId; + CFE_ES_TaskId_t LocalTaskId; + int32 StatusCode; + int32 ReturnCode; + + /* + * Create the primary task for the newly loaded task + */ + StatusCode = OS_TaskCreate(&OsalTaskId, /* task id */ + TaskName, /* task name matches app name for main task */ + CFE_ES_TaskEntryPoint, /* task function pointer */ + OSAL_TASK_STACK_ALLOCATE, /* stack pointer (allocate) */ + Params->StackSize, /* stack size */ + Params->Priority, /* task priority */ + OS_FP_ENABLED); /* task options */ + + CFE_ES_LockSharedData(__func__, __LINE__); + + if (StatusCode == OS_SUCCESS) + { + /* + * As this is a newly-created task, this shouldn't fail. + * The entry is not (yet) matching the task ID - it will be + * initialized here. + */ + LocalTaskId = CFE_ES_TaskId_FromOSAL(OsalTaskId); + TaskRecPtr = CFE_ES_LocateTaskRecordByID(LocalTaskId); + if (CFE_ES_TaskRecordIsUsed(TaskRecPtr)) + { + CFE_ES_SysLogWrite_Unsync("ES Startup: Error: ES_TaskTable slot for ID %lx in use at task creation!\n", + OS_ObjectIdToInteger(OsalTaskId)); + } + + /* + * Clear any other/stale data that might be in the entry, + * and reset all fields to the correct value. + */ + memset(TaskRecPtr, 0, sizeof(*TaskRecPtr)); + + TaskRecPtr->AppId = ParentAppId; + TaskRecPtr->EntryFunc = EntryFunc; + TaskRecPtr->StartParams = *Params; + + strncpy(TaskRecPtr->TaskName, TaskName, sizeof(TaskRecPtr->TaskName) - 1); + TaskRecPtr->TaskName[sizeof(TaskRecPtr->TaskName) - 1] = 0; + + CFE_ES_TaskRecordSetUsed(TaskRecPtr, CFE_RESOURCEID_UNWRAP(LocalTaskId)); + + /* + * Increment the registered Task count. + */ + CFE_ES_Global.RegisteredTasks++; + ReturnCode = CFE_SUCCESS; + *TaskIdPtr = CFE_ES_TaskRecordGetID(TaskRecPtr); + } + else + { + CFE_ES_SysLogWrite_Unsync("ES Startup: AppCreate Error: TaskCreate %s Failed. EC = 0x%08X!\n", TaskName, + (unsigned int)StatusCode); + ReturnCode = CFE_STATUS_EXTERNAL_RESOURCE_FAIL; + *TaskIdPtr = CFE_ES_TASKID_UNDEFINED; + } + + CFE_ES_UnlockSharedData(__func__, __LINE__); + + return ReturnCode; +} + +/* +**--------------------------------------------------------------------------------------- +** Name: ES_AppCreate +** +** Purpose: This function loads and creates a cFE Application. +** This function can be called from the ES startup code when it +** loads the cFE Applications from the disk using the startup script, or it +** can be called when the ES Start Application command is executed. +** +**--------------------------------------------------------------------------------------- +*/ +int32 CFE_ES_AppCreate(CFE_ES_AppId_t *ApplicationIdPtr, const char *AppName, const CFE_ES_AppStartParams_t *Params) +{ + CFE_Status_t Status; + CFE_ES_AppRecord_t *AppRecPtr; + CFE_ResourceId_t PendingResourceId = CFE_RESOURCEID_UNDEFINED; + + /* + * The AppName must not be NULL + */ + if (AppName == NULL || Params == NULL) + { + return CFE_ES_BAD_ARGUMENT; + } + + /* Confirm name will fit inside the record */ + if (memchr(AppName, 0, sizeof(AppRecPtr->AppName)) == NULL) + { + return CFE_ES_BAD_ARGUMENT; + } + + /* + ** Allocate an ES_AppTable entry + */ + CFE_ES_LockSharedData(__func__, __LINE__); + + /* + ** Find an ES AppTable entry, and set to RESERVED + ** + ** In this state, the entry is no longer free, but also will not pass the + ** validation test. So this function effectively has exclusive access + ** without holding the global lock. + ** + ** IMPORTANT: it must set the ID to something else before leaving + ** this function or else the resource will be leaked. After this + ** point, execution must proceed to the end of the function to + ** guarantee that the entry is either completed or freed. + */ + + /* + * Check for an existing entry with the same name. + * Also check for a matching Library name. + * (Apps and libraries should be uniquely named) + */ + AppRecPtr = CFE_ES_LocateAppRecordByName(AppName); + if (AppRecPtr != NULL) + { + CFE_ES_SysLogWrite_Unsync("ES Startup: Duplicate app name '%s'\n", AppName); + Status = CFE_ES_ERR_DUPLICATE_NAME; + } + else + { + /* scan for a free slot */ + PendingResourceId = CFE_ResourceId_FindNext(CFE_ES_Global.LastAppId, CFE_PLATFORM_ES_MAX_APPLICATIONS, + CFE_ES_CheckAppIdSlotUsed); + AppRecPtr = CFE_ES_LocateAppRecordByID(CFE_ES_APPID_C(PendingResourceId)); + + if (AppRecPtr == NULL) + { + CFE_ES_SysLogWrite_Unsync("ES Startup: No free application slots available\n"); + Status = CFE_ES_NO_RESOURCE_IDS_AVAILABLE; + } + else + { + /* Fully clear the entry, just in case of stale data */ + memset(AppRecPtr, 0, sizeof(*AppRecPtr)); + + /* Store the app name from passed-in value */ + strncpy(AppRecPtr->AppName, AppName, sizeof(AppRecPtr->AppName) - 1); + + AppRecPtr->Type = CFE_ES_AppType_EXTERNAL; + + /* + * Fill out the parameters in the StartParams sub-structure + * + * This contains all relevant info, including file name, entry point, + * main task info, etc. which is required to start the app now + * or in a future restart/reload request. + */ + AppRecPtr->StartParams = *Params; + + /* + * Fill out the Task State info + */ + AppRecPtr->ControlReq.AppControlRequest = CFE_ES_RunStatus_APP_RUN; + AppRecPtr->ControlReq.AppTimerMsec = 0; + + CFE_ES_AppRecordSetUsed(AppRecPtr, CFE_RESOURCEID_RESERVED); + CFE_ES_Global.LastAppId = PendingResourceId; + Status = CFE_SUCCESS; + } + } + + CFE_ES_UnlockSharedData(__func__, __LINE__); + + /* + * If ID allocation was not successful, return now. + * A message regarding the issue should have already been logged + */ + if (Status != CFE_SUCCESS) + { + return Status; + } + + /* + * Load the module based on StartParams configured above. + */ + Status = CFE_ES_LoadModule(PendingResourceId, AppName, &AppRecPtr->StartParams.BasicInfo, &AppRecPtr->LoadStatus); + + /* + * If the Load was OK, then complete the initialization + */ + if (Status == CFE_SUCCESS) + { + Status = + CFE_ES_StartAppTask(&AppRecPtr->MainTaskId, /* Task ID (output) stored in App Record as main task */ + AppName, /* Main Task name matches app name */ + (CFE_ES_TaskEntryFuncPtr_t) + AppRecPtr->LoadStatus.InitSymbolAddress, /* Init Symbol is main task entry point */ + &AppRecPtr->StartParams.MainTaskInfo, /* Main task parameters */ + CFE_ES_APPID_C(PendingResourceId)); /* Parent App ID */ + } + + /* + * Finalize data in the app table entry, which must be done under lock. + * This transitions the entry from being RESERVED to the real ID. + */ + CFE_ES_LockSharedData(__func__, __LINE__); + + if (Status == CFE_SUCCESS) + { + /* + * important - set the ID to its proper value + * which turns this into a real/valid table entry + */ + CFE_ES_AppRecordSetUsed(AppRecPtr, PendingResourceId); + + /* + ** Increment the registered App counter. + */ + CFE_ES_Global.RegisteredExternalApps++; + } + else + { + /* + * Set the table entry back to free + */ + CFE_ES_AppRecordSetFree(AppRecPtr); + PendingResourceId = CFE_RESOURCEID_UNDEFINED; + } + + CFE_ES_UnlockSharedData(__func__, __LINE__); + + *ApplicationIdPtr = CFE_ES_APPID_C(PendingResourceId); + + return Status; + +} /* End Function */ + +/* +**--------------------------------------------------------------------------------------- +** Name: CFE_ES_LoadLibrary +** +** Purpose: This function loads and initializes a cFE Shared Library. +** +**--------------------------------------------------------------------------------------- +*/ +int32 CFE_ES_LoadLibrary(CFE_ES_LibId_t *LibraryIdPtr, const char *LibName, const CFE_ES_ModuleLoadParams_t *Params) +{ + CFE_ES_LibraryEntryFuncPtr_t FunctionPointer; + CFE_ES_LibRecord_t * LibSlotPtr; + int32 Status; + CFE_ResourceId_t PendingResourceId; + + /* + * The LibName must not be NULL + */ + if (LibName == NULL || Params == NULL) + { + return CFE_ES_BAD_ARGUMENT; + } + + /* Confirm name will fit inside the record */ + if (memchr(LibName, 0, sizeof(LibSlotPtr->LibName)) == NULL) + { + return CFE_ES_BAD_ARGUMENT; + } + + /* + ** Allocate an ES_LibTable entry + */ + FunctionPointer = NULL; + PendingResourceId = CFE_RESOURCEID_UNDEFINED; + + /* + ** Find an ES AppTable entry, and set to RESERVED + ** + ** In this state, the entry is no longer free, but also will not pass the + ** validation test. So this function effectively has exclusive access + ** without holding the global lock. + ** + ** IMPORTANT: it must set the ID to something else before leaving + ** this function or else the resource will be leaked. After this + ** point, execution must proceed to the end of the function to + ** guarantee that the entry is either completed or freed. + */ + CFE_ES_LockSharedData(__func__, __LINE__); + + /* + * Check for an existing entry with the same name. + * Also check for a matching Library name. + * (Libs and libraries should be uniquely named) + */ + LibSlotPtr = CFE_ES_LocateLibRecordByName(LibName); + if (LibSlotPtr != NULL || CFE_ES_LocateAppRecordByName(LibName) != NULL) + { + CFE_ES_SysLogWrite_Unsync("ES Startup: Duplicate Lib name '%s'\n", LibName); + if (LibSlotPtr != NULL) + { + PendingResourceId = CFE_RESOURCEID_UNWRAP(CFE_ES_LibRecordGetID(LibSlotPtr)); + } + Status = CFE_ES_ERR_DUPLICATE_NAME; + } + else + { + /* scan for a free slot */ + PendingResourceId = + CFE_ResourceId_FindNext(CFE_ES_Global.LastLibId, CFE_PLATFORM_ES_MAX_LIBRARIES, CFE_ES_CheckLibIdSlotUsed); + LibSlotPtr = CFE_ES_LocateLibRecordByID(CFE_ES_LIBID_C(PendingResourceId)); + + if (LibSlotPtr == NULL) + { + CFE_ES_SysLogWrite_Unsync("ES Startup: No free library slots available\n"); + Status = CFE_ES_NO_RESOURCE_IDS_AVAILABLE; + } + else + { + /* Fully clear the entry, just in case of stale data */ + memset(LibSlotPtr, 0, sizeof(*LibSlotPtr)); + + /* + * Fill out the parameters in the AppStartParams sub-structure + */ + strncpy(LibSlotPtr->LibName, LibName, sizeof(LibSlotPtr->LibName) - 1); + LibSlotPtr->LibName[sizeof(LibSlotPtr->LibName) - 1] = '\0'; + LibSlotPtr->LoadParams = *Params; + + CFE_ES_LibRecordSetUsed(LibSlotPtr, CFE_RESOURCEID_RESERVED); + CFE_ES_Global.LastLibId = PendingResourceId; + Status = CFE_SUCCESS; + } + } + + CFE_ES_UnlockSharedData(__func__, __LINE__); + + /* + * If any off-nominal condition exists, skip the rest of this logic. + * (Log message already written) + */ + if (Status != CFE_SUCCESS) + { + *LibraryIdPtr = CFE_ES_LIBID_C(PendingResourceId); + return Status; + } + + /* + * Load the module based on StartParams configured above. + */ + Status = CFE_ES_LoadModule(PendingResourceId, LibName, &LibSlotPtr->LoadParams, &LibSlotPtr->LoadStatus); + if (Status == CFE_SUCCESS) + { + FunctionPointer = (CFE_ES_LibraryEntryFuncPtr_t)LibSlotPtr->LoadStatus.InitSymbolAddress; + if (FunctionPointer != NULL) + { + Status = (*FunctionPointer)(CFE_ES_LIBID_C(PendingResourceId)); + if (Status != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("ES Startup: Load Shared Library Init Error = 0x%08x\n", (unsigned int)Status); + } + } + } + + /* + * Finalize data in the app table entry, which must be done under lock. + * This transitions the entry from being RESERVED to the real type, + * either MAIN_TASK (success) or returning to INVALID (failure). + */ + CFE_ES_LockSharedData(__func__, __LINE__); + + if (Status == CFE_SUCCESS) + { + /* + * important - set the ID to its proper value + * which turns this into a real/valid table entry + */ + CFE_ES_LibRecordSetUsed(LibSlotPtr, PendingResourceId); + + /* + * Increment the registered Lib counter. + */ + CFE_ES_Global.RegisteredLibs++; + } + else + { + CFE_ES_LibRecordSetFree(LibSlotPtr); + PendingResourceId = CFE_RESOURCEID_UNDEFINED; + } + + CFE_ES_UnlockSharedData(__func__, __LINE__); + + *LibraryIdPtr = CFE_ES_LIBID_C(PendingResourceId); + + return (Status); + +} /* End Function */ + +/* +**--------------------------------------------------------------------------------------- +** Name: CFE_ES_RunAppTableScan +** +** Purpose: This function scans the ES Application table and acts on the changes +** in application states. This is where the external cFE Applications are +** restarted, reloaded, or deleted. +**--------------------------------------------------------------------------------------- +*/ +bool CFE_ES_RunAppTableScan(uint32 ElapsedTime, void *Arg) +{ + CFE_ES_AppTableScanState_t *State = (CFE_ES_AppTableScanState_t *)Arg; + uint32 i; + CFE_ES_AppRecord_t * AppPtr; + CFE_ES_AppId_t AppTimeoutList[CFE_PLATFORM_ES_MAX_APPLICATIONS]; + uint32 NumAppTimeouts; + + if (State->PendingAppStateChanges == 0) + { + /* + * If the command count changes, then a scan becomes due immediately. + */ + if (State->LastScanCommandCount == CFE_ES_Global.TaskData.CommandCounter && + State->BackgroundScanTimer > ElapsedTime) + { + /* no action at this time, background scan is not due yet */ + State->BackgroundScanTimer -= ElapsedTime; + return false; + } + } + + /* + * Every time a scan is initiated (for any reason) + * reset the background scan timer to the full value, + * and take a snapshot of the the command counter. + */ + NumAppTimeouts = 0; + State->BackgroundScanTimer = CFE_PLATFORM_ES_APP_SCAN_RATE; + State->LastScanCommandCount = CFE_ES_Global.TaskData.CommandCounter; + State->PendingAppStateChanges = 0; + + /* + * Scan needs to be done with the table locked, + * as these state changes need to be done atomically + * with respect to other tasks that also access/update + * the state. + */ + CFE_ES_LockSharedData(__func__, __LINE__); + + /* + ** Scan the ES Application table. Skip entries that are: + ** - Not in use, or + ** - cFE Core apps, or + ** - Currently running + */ + AppPtr = CFE_ES_Global.AppTable; + for (i = 0; i < CFE_PLATFORM_ES_MAX_APPLICATIONS; i++) + { + if (CFE_ES_AppRecordIsUsed(AppPtr) && AppPtr->Type == CFE_ES_AppType_EXTERNAL) + { + if (AppPtr->AppState > CFE_ES_AppState_RUNNING) + { + /* + * Increment the "pending" counter which reflects + * the number of apps that are in some phase of clean up. + */ + ++State->PendingAppStateChanges; + + /* + * Decrement the wait timer, if active. + * When the timeout value becomes zero, take the action to delete/restart/reload the app + */ + if (AppPtr->ControlReq.AppTimerMsec > ElapsedTime) + { + AppPtr->ControlReq.AppTimerMsec -= ElapsedTime; + } + else + { + AppPtr->ControlReq.AppTimerMsec = 0; + + /* Add it to the list to be processed later */ + AppTimeoutList[NumAppTimeouts] = CFE_ES_AppRecordGetID(AppPtr); + ++NumAppTimeouts; + } + } + else if (AppPtr->AppState == CFE_ES_AppState_RUNNING && + AppPtr->ControlReq.AppControlRequest > CFE_ES_RunStatus_APP_RUN) + { + /* this happens after a command arrives to restart/reload/delete an app */ + /* switch to WAITING state, and set the timer for transition */ + AppPtr->AppState = CFE_ES_AppState_WAITING; + AppPtr->ControlReq.AppTimerMsec = CFE_PLATFORM_ES_APP_KILL_TIMEOUT * CFE_PLATFORM_ES_APP_SCAN_RATE; + } + + } /* end if */ + + ++AppPtr; + + } /* end for loop */ + + CFE_ES_UnlockSharedData(__func__, __LINE__); + + /* + * Now invoke the CFE_ES_ProcessControlRequest() routine for any app + * which has reached that point. + */ + for (i = 0; i < NumAppTimeouts; i++) + { + /* + * Call CFE_ES_ProcessControlRequest() with a reference to + * the _copies_ of the app record details. (This avoids + * needing to access the global records outside of the lock). + */ + CFE_ES_ProcessControlRequest(AppTimeoutList[i]); + } + + /* + * This state machine is considered active if there are any + * pending app state changes. Returning "true" will cause this job + * to be called from the background task at a faster interval. + */ + return (State->PendingAppStateChanges != 0); + +} /* End Function */ + +/* +**--------------------------------------------------------------------------------------- +** Name: CFE_ES_ProcessControlRequest +** +** Purpose: This function will perform the requested control action for an application. +**--------------------------------------------------------------------------------------- +*/ +void CFE_ES_ProcessControlRequest(CFE_ES_AppId_t AppId) +{ + CFE_ES_AppRecord_t * AppRecPtr; + uint32 PendingControlReq; + CFE_ES_AppStartParams_t RestartParams; + char OrigAppName[OS_MAX_API_NAME]; + CFE_Status_t CleanupStatus; + CFE_Status_t StartupStatus; + CFE_ES_AppId_t NewAppId; + const char * ReqName; + char MessageDetail[48]; + uint16 EventID; + CFE_EVS_EventType_Enum_t EventType; + + /* Init/clear all local state variables */ + ReqName = NULL; + MessageDetail[0] = 0; + EventID = 0; + EventType = 0; + StartupStatus = CFE_SUCCESS; + PendingControlReq = 0; + NewAppId = CFE_ES_APPID_UNDEFINED; + OrigAppName[0] = 0; + memset(&RestartParams, 0, sizeof(RestartParams)); + + AppRecPtr = CFE_ES_LocateAppRecordByID(AppId); + + /* + * Take a local snapshot of the important app record data + * This way it becomes private and can be accessed without + * concerns about other threads/tasks, even after the global + * data records are eventually cleared. + */ + CFE_ES_LockSharedData(__func__, __LINE__); + + if (CFE_ES_AppRecordIsMatch(AppRecPtr, AppId)) + { + PendingControlReq = AppRecPtr->ControlReq.AppControlRequest; + strncpy(OrigAppName, AppRecPtr->AppName, sizeof(OrigAppName) - 1); + OrigAppName[sizeof(OrigAppName) - 1] = 0; + + /* If a restart was requested, copy the parameters to re-use in new app */ + if (PendingControlReq == CFE_ES_RunStatus_SYS_RESTART || PendingControlReq == CFE_ES_RunStatus_SYS_RELOAD) + { + RestartParams = AppRecPtr->StartParams; + } + } + + CFE_ES_UnlockSharedData(__func__, __LINE__); + + /* + * All control requests start by deleting the app/task and + * all associated resources. + * + * The reload/restart requests will start it again, and it gets + * a new appID. For other requests it just leaves it deleted. + * + * Note that Cleanup can fail for a variety of reasons, including + * situations where e.g. a task ID had become stale because the task + * already exited itself. In most cases these are minor errors and + * reflect problems with the consistency of the old app record. + * + * Even when this happens the cleanup should still do its best effort + * to release all relevant global data entries. So it should not + * prevent starting the new app, if a restart/reload is indicated. + */ + CleanupStatus = CFE_ES_CleanUpApp(AppId); + + /* + * Attempt to restart the app if the request indicated to do so, + * regardless of the CleanupStatus. + */ + if (PendingControlReq == CFE_ES_RunStatus_SYS_RESTART || PendingControlReq == CFE_ES_RunStatus_SYS_RELOAD) + { + StartupStatus = CFE_ES_AppCreate(&NewAppId, OrigAppName, &RestartParams); + } + + /* + * Determine the event ID associated with the control request, + * which indicates the success/failure of the operation and + * any other relevant detail. + * + * Note that the specific event ID that gets generated is the only + * other difference between all these control request types. + */ + switch (PendingControlReq) + { + case CFE_ES_RunStatus_APP_EXIT: + ReqName = "Exit"; + if (CleanupStatus != CFE_SUCCESS) + { + /* error event for this request */ + EventID = CFE_ES_EXIT_APP_ERR_EID; + } + else + { + /* success event for this request */ + EventID = CFE_ES_EXIT_APP_INF_EID; + } + break; + + case CFE_ES_RunStatus_APP_ERROR: + ReqName = "Exit"; + if (CleanupStatus != CFE_SUCCESS) + { + /* error event for this request */ + EventID = CFE_ES_ERREXIT_APP_ERR_EID; + } + else + { + /* success event for this request */ + EventID = CFE_ES_ERREXIT_APP_INF_EID; + } + break; + + case CFE_ES_RunStatus_SYS_DELETE: + ReqName = "Stop"; + if (CleanupStatus != CFE_SUCCESS) + { + /* error event for this request */ + EventID = CFE_ES_STOP_ERR3_EID; + } + else + { + /* success event for this request */ + EventID = CFE_ES_STOP_INF_EID; + } + break; + + case CFE_ES_RunStatus_SYS_RESTART: + ReqName = "Restart"; + if (CleanupStatus != CFE_SUCCESS) + { + /* error event for this request */ + EventID = CFE_ES_RESTART_APP_ERR4_EID; + } + else if (StartupStatus != CFE_SUCCESS) + { + /* error event for this request */ + EventID = CFE_ES_RESTART_APP_ERR3_EID; + } + else + { + /* success event for this request */ + EventID = CFE_ES_RESTART_APP_INF_EID; + } + break; + + case CFE_ES_RunStatus_SYS_RELOAD: + ReqName = "Reload"; + if (CleanupStatus != CFE_SUCCESS) + { + /* error event for this request */ + EventID = CFE_ES_RELOAD_APP_ERR4_EID; + } + else if (StartupStatus != CFE_SUCCESS) + { + /* error event for this request */ + EventID = CFE_ES_RELOAD_APP_ERR3_EID; + } + else + { + /* success event for this request */ + EventID = CFE_ES_RELOAD_APP_INF_EID; + } + break; + + /* + * These two cases below should never occur so they are always + * reported as errors, but the CFE_ES_CleanUpApp() should hopefully + * have fixed it either way. + */ + case CFE_ES_RunStatus_SYS_EXCEPTION: + ReqName = "ES_ProcControlReq: Invalid State"; + EventID = CFE_ES_PCR_ERR1_EID; + snprintf(MessageDetail, sizeof(MessageDetail), "EXCEPTION"); + break; + + default: + ReqName = "ES_ProcControlReq: Unknown State"; + EventID = CFE_ES_PCR_ERR2_EID; + snprintf(MessageDetail, sizeof(MessageDetail), "( %lu )", (unsigned long)PendingControlReq); + break; + } + + if (MessageDetail[0] != 0) + { + /* Detail message already set, assume it is an error event */ + EventType = CFE_EVS_EventType_ERROR; + } + else if (StartupStatus != CFE_SUCCESS) + { + /* Make detail message for event containing startup error code */ + EventType = CFE_EVS_EventType_ERROR; + snprintf(MessageDetail, sizeof(MessageDetail), "Failed: AppCreate Error 0x%08X.", (unsigned int)StartupStatus); + } + else if (CleanupStatus != CFE_SUCCESS) + { + /* Make detail message for event containing cleanup error code */ + EventType = CFE_EVS_EventType_ERROR; + snprintf(MessageDetail, sizeof(MessageDetail), "Failed: CleanUpApp Error 0x%08X.", (unsigned int)CleanupStatus); + } + else if (CFE_RESOURCEID_TEST_DEFINED(NewAppId)) + { + /* Record success message for event where app is restarted */ + EventType = CFE_EVS_EventType_INFORMATION; + snprintf(MessageDetail, sizeof(MessageDetail), "Completed, AppID=%lu", CFE_RESOURCEID_TO_ULONG(NewAppId)); + } + else + { + /* Record success message for event */ + EventType = CFE_EVS_EventType_INFORMATION; + snprintf(MessageDetail, sizeof(MessageDetail), "Completed."); + } + + CFE_EVS_SendEvent(EventID, EventType, "%s Application %s %s", ReqName, OrigAppName, MessageDetail); + +} /* End Function */ + +/* +**--------------------------------------------------------------------------------------- +** Name: CFE_ES_CleanUpApp +** +** Purpose: Delete an application by cleaning up all of it's resources. +**--------------------------------------------------------------------------------------- +*/ +int32 CFE_ES_CleanUpApp(CFE_ES_AppId_t AppId) +{ + uint32 i; + int32 Status; + int32 ReturnCode; + CFE_ES_TaskId_t TaskList[OS_MAX_TASKS]; + CFE_ES_MemHandle_t PoolList[CFE_PLATFORM_ES_MAX_MEMORY_POOLS]; + osal_id_t ModuleId; + uint32 NumTasks; + uint32 NumPools; + CFE_ES_AppRecord_t * AppRecPtr; + CFE_ES_TaskRecord_t * TaskRecPtr; + CFE_ES_MemPoolRecord_t *MemPoolRecPtr; + + NumTasks = 0; + NumPools = 0; + ModuleId = OS_OBJECT_ID_UNDEFINED; + ReturnCode = CFE_SUCCESS; + + AppRecPtr = CFE_ES_LocateAppRecordByID(AppId); + + /* + * Collect a list of resources previously owned by this app, which + * must be done while the global data is locked. + */ + CFE_ES_LockSharedData(__func__, __LINE__); + + if (CFE_ES_AppRecordIsMatch(AppRecPtr, AppId)) + { + if (AppRecPtr->Type == CFE_ES_AppType_EXTERNAL) + { + CFE_ES_Global.RegisteredExternalApps--; + + /* + * Get the Module ID, if it was an external app + * + * (this will be OS_OBJECT_ID_UNDEFINED if it was not loaded dynamically) + */ + ModuleId = AppRecPtr->LoadStatus.ModuleId; + } + + /* + * Collect all tasks associated with this app + */ + TaskRecPtr = CFE_ES_Global.TaskTable; + for (i = 0; i < OS_MAX_TASKS; i++) + { + if (CFE_ES_TaskRecordIsUsed(TaskRecPtr) && CFE_RESOURCEID_TEST_EQUAL(TaskRecPtr->AppId, AppId)) + { + TaskList[NumTasks] = CFE_ES_TaskRecordGetID(TaskRecPtr); + + /* Store the main task ID at index 0 (swap with whatever was there) */ + if (CFE_RESOURCEID_TEST_EQUAL(TaskList[NumTasks], AppRecPtr->MainTaskId) && NumTasks != 0) + { + TaskList[NumTasks] = TaskList[0]; + TaskList[0] = AppRecPtr->MainTaskId; + } + + /* Mark record for removal */ + CFE_ES_TaskRecordSetUsed(TaskRecPtr, CFE_RESOURCEID_RESERVED); + ++NumTasks; + } + + ++TaskRecPtr; + } /* end for */ + + CFE_ES_Global.RegisteredTasks -= NumTasks; + + /* + * Collect memory pools associated with this app + */ + MemPoolRecPtr = CFE_ES_Global.MemPoolTable; + for (i = 0; i < CFE_PLATFORM_ES_MAX_MEMORY_POOLS; i++) + { + if (CFE_ES_MemPoolRecordIsUsed(MemPoolRecPtr) && + CFE_RESOURCEID_TEST_EQUAL(MemPoolRecPtr->OwnerAppID, AppId)) + { + PoolList[NumPools] = CFE_ES_MemPoolRecordGetID(MemPoolRecPtr); + ++NumPools; + } + + ++MemPoolRecPtr; + } /* end for */ + + /* + * Set the record to RESERVED. + * + * This prevents reallocation of this slot while the remainder + * of resources are freed. + */ + CFE_ES_AppRecordSetUsed(AppRecPtr, CFE_RESOURCEID_RESERVED); + } + else + { + CFE_ES_SysLogWrite_Unsync("CFE_ES_CleanUpApp: AppID %lu is not valid for deletion\n", + CFE_RESOURCEID_TO_ULONG(AppId)); + ReturnCode = CFE_ES_APP_CLEANUP_ERR; + } + + CFE_ES_UnlockSharedData(__func__, __LINE__); + + if (ReturnCode != CFE_SUCCESS) + { + return ReturnCode; + } + + /* + * Now actually delete all the resources associated with the task. + * + * Most of this involves calling into other subsystems, so it is + * done while the ES global data is UNLOCKED to avoid holding more + * than one lock at a time. + */ + + /* + ** Call the Table Clean up function + */ + CFE_TBL_CleanUpApp(AppId); + + /* + ** Call the Software Bus clean up function + */ + CFE_SB_CleanUpApp(AppId); + + /* + ** Call the TIME Clean up function + */ + CFE_TIME_CleanUpApp(AppId); + + /* + ** Call the EVS Clean up function + */ + Status = CFE_EVS_CleanUpApp(AppId); + if (Status != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("CFE_ES_CleanUpApp: Call to CFE_EVS_CleanUpApp returned Error: 0x%08X\n", + (unsigned int)Status); + ReturnCode = CFE_ES_APP_CLEANUP_ERR; + } + + /* + * Delete all tasks. + * + * Note that the main task is always positioned at index 0 in this list. + * + * This iterates the list in reverse order, such that the child + * tasks are deleted first (in any order) and main task is deleted last. + */ + i = NumTasks; + while (i > 0) + { + --i; + Status = CFE_ES_CleanupTaskResources(TaskList[i]); + if (Status != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("CFE_ES_CleanUpApp: CleanUpTaskResources for Task ID:%lu returned Error: 0x%08X\n", + CFE_RESOURCEID_TO_ULONG(TaskList[i]), (unsigned int)Status); + ReturnCode = CFE_ES_APP_CLEANUP_ERR; + } + } + + /* + * Delete all mem pools. + */ + for (i = 0; i < NumPools; ++i) + { + Status = CFE_ES_PoolDelete(PoolList[i]); + if (Status != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("CFE_ES_MemPoolCleanupApp: delete pool %lu returned Error: 0x%08X\n", + CFE_RESOURCEID_TO_ULONG(PoolList[i]), (unsigned int)Status); + ReturnCode = CFE_ES_APP_CLEANUP_ERR; + } + } + + /* + ** Unload the module, if applicable + */ + if (OS_ObjectIdDefined(ModuleId)) + { + /* + ** Unload the module only if it is an external app + */ + Status = OS_ModuleUnload(ModuleId); + if (Status != OS_SUCCESS) + { + CFE_ES_WriteToSysLog("CFE_ES_CleanUpApp: Module (ID:0x%08lX) Unload failed. RC=0x%08X\n", + OS_ObjectIdToInteger(ModuleId), (unsigned int)Status); + ReturnCode = CFE_ES_APP_CLEANUP_ERR; + } + } + + /* + * Finally, re-acquire the ES lock and set all + * table entries free for re-use. + */ + CFE_ES_LockSharedData(__func__, __LINE__); + + /* + * Free all task records. + */ + for (i = 0; i < NumTasks; ++i) + { + TaskRecPtr = CFE_ES_LocateTaskRecordByID(TaskList[i]); + if (CFE_ES_TaskRecordIsMatch(TaskRecPtr, CFE_ES_TASKID_C(CFE_RESOURCEID_RESERVED))) + { + CFE_ES_TaskRecordSetFree(TaskRecPtr); + } + } + + /* + * Now finally delete the record and allow re-use of the slot. + */ + if (CFE_ES_AppRecordIsMatch(AppRecPtr, CFE_ES_APPID_C(CFE_RESOURCEID_RESERVED))) + { + CFE_ES_AppRecordSetFree(AppRecPtr); + } + + CFE_ES_UnlockSharedData(__func__, __LINE__); + + return (ReturnCode); + +} /* end function */ + +/* + * Simple state structure used when cleaning up objects associated with tasks + * + * This is used locally by CFE_ES_CleanupTaskResources + */ +typedef struct +{ + uint32 ErrorFlag; + uint32 FoundObjects; + uint32 PrevFoundObjects; + uint32 DeletedObjects; + int32 OverallStatus; +} CFE_ES_CleanupState_t; + +/* +**--------------------------------------------------------------------------------------- +** Name: CFE_ES_CleanupObjectCallback +** +** Purpose: Helper function clean up all objects. +** +** NOTE: This is called while holding the ES global lock +**--------------------------------------------------------------------------------------- +*/ +void CFE_ES_CleanupObjectCallback(osal_id_t ObjectId, void *arg) +{ + CFE_ES_CleanupState_t *CleanState; + int32 Status; + osal_objtype_t ObjType; + bool ObjIsValid; + + CleanState = (CFE_ES_CleanupState_t *)arg; + ObjIsValid = true; + + ObjType = OS_IdentifyObject(ObjectId); + switch (ObjType) + { + case OS_OBJECT_TYPE_OS_TASK: + Status = OS_TaskDelete(ObjectId); + break; + case OS_OBJECT_TYPE_OS_QUEUE: + Status = OS_QueueDelete(ObjectId); + break; + case OS_OBJECT_TYPE_OS_BINSEM: + Status = OS_BinSemDelete(ObjectId); + break; + case OS_OBJECT_TYPE_OS_COUNTSEM: + Status = OS_CountSemDelete(ObjectId); + break; + case OS_OBJECT_TYPE_OS_MUTEX: + Status = OS_MutSemDelete(ObjectId); + break; + case OS_OBJECT_TYPE_OS_TIMECB: + Status = OS_TimerDelete(ObjectId); + break; + case OS_OBJECT_TYPE_OS_STREAM: + Status = OS_close(ObjectId); + break; + case OS_OBJECT_TYPE_OS_MODULE: + Status = OS_ModuleUnload(ObjectId); + break; + default: + ObjIsValid = false; + Status = OS_ERROR; + break; + } + + if (ObjIsValid) + { + ++CleanState->FoundObjects; + if (Status == OS_SUCCESS) + { + ++CleanState->DeletedObjects; + } + else + { + CFE_ES_SysLogWrite_Unsync("Call to OSAL Delete Object (ID:%lu) failed. RC=0x%08X\n", + OS_ObjectIdToInteger(ObjectId), (unsigned int)Status); + if (CleanState->OverallStatus == CFE_SUCCESS) + { + /* + * Translate any OS failures into the appropriate CFE_ES return codes + * (Some object types have special return codes, depending on what type + * of object failed to delete) + */ + switch (ObjType) + { + case OS_OBJECT_TYPE_OS_TASK: + CleanState->OverallStatus = CFE_ES_ERR_CHILD_TASK_DELETE; + break; + case OS_OBJECT_TYPE_OS_QUEUE: + CleanState->OverallStatus = CFE_ES_QUEUE_DELETE_ERR; + break; + case OS_OBJECT_TYPE_OS_BINSEM: + CleanState->OverallStatus = CFE_ES_BIN_SEM_DELETE_ERR; + break; + case OS_OBJECT_TYPE_OS_COUNTSEM: + CleanState->OverallStatus = CFE_ES_COUNT_SEM_DELETE_ERR; + break; + case OS_OBJECT_TYPE_OS_MUTEX: + CleanState->OverallStatus = CFE_ES_MUT_SEM_DELETE_ERR; + break; + case OS_OBJECT_TYPE_OS_TIMECB: + CleanState->OverallStatus = CFE_ES_TIMER_DELETE_ERR; + break; + default: + /* generic failure */ + CleanState->OverallStatus = CFE_ES_APP_CLEANUP_ERR; + break; + } + } + } + } +} + +/* +**--------------------------------------------------------------------------------------- +** Name: CFE_ES_CleanupTaskResources +** +** Purpose: Clean up the OS resources associated with an individual Task +** Note: This is called when the ES global is UNLOCKED - so it should not touch +** any ES global data structures. It should only clean up at the OSAL level. +**--------------------------------------------------------------------------------------- +*/ +int32 CFE_ES_CleanupTaskResources(CFE_ES_TaskId_t TaskId) +{ + CFE_ES_CleanupState_t CleanState; + int32 Result; + osal_id_t OsalId; + + /* Get the Task ID for calling OSAL APIs (convert type) */ + OsalId = CFE_ES_TaskId_ToOSAL(TaskId); + + /* + ** Delete all OSAL resources that belong to this task + */ + memset(&CleanState, 0, sizeof(CleanState)); + --CleanState.PrevFoundObjects; + while (1) + { + OS_ForEachObject(OsalId, CFE_ES_CleanupObjectCallback, &CleanState); + if (CleanState.FoundObjects == 0 || CleanState.ErrorFlag != 0) + { + break; + } + /* + * The number of found objects should show a downward trend, + * if not, then stop and do not loop here forever. (This can + * happen when using the UT stub functions, or if an object + * cannot be fully deleted successfully). + */ + CleanState.ErrorFlag = + (CleanState.DeletedObjects == 0 || CleanState.FoundObjects >= CleanState.PrevFoundObjects); + CleanState.PrevFoundObjects = CleanState.FoundObjects; + CleanState.FoundObjects = 0; + CleanState.DeletedObjects = 0; + } + + /* + ** Delete the task itself + ** + ** Note, if the task self exited, then the ID becomes invalid. + ** In this case the OS_ERR_INVALID_ID status is returned, but + ** that is OK, there is nothing else needed to do. + */ + Result = OS_TaskDelete(OsalId); + if (Result == OS_SUCCESS || Result == OS_ERR_INVALID_ID) + { + Result = CleanState.OverallStatus; + if (Result == CFE_SUCCESS && CleanState.FoundObjects > 0) + { + /* Objects leftover after cleanup -- resource leak */ + Result = CFE_ES_APP_CLEANUP_ERR; + } + } + else + { + Result = CFE_ES_TASK_DELETE_ERR; + } + + return (Result); +} + +/* +**--------------------------------------------------------------------------------------- +** Name: CFE_ES_CopyModuleBasicInfo +** +** Purpose: Populate the cFE_ES_AppInfo structure with the data for an app. +** +** This internal function does not log any errors/events. The caller is expected +** to check the return code and log any relevant errors based on the context. +**--------------------------------------------------------------------------------------- +*/ +void CFE_ES_CopyModuleBasicInfo(const CFE_ES_ModuleLoadParams_t *ParamsPtr, CFE_ES_AppInfo_t *AppInfoPtr) +{ + strncpy(AppInfoPtr->EntryPoint, ParamsPtr->InitSymbolName, sizeof(AppInfoPtr->EntryPoint) - 1); + AppInfoPtr->EntryPoint[sizeof(AppInfoPtr->EntryPoint) - 1] = '\0'; + + strncpy(AppInfoPtr->FileName, ParamsPtr->FileName, sizeof(AppInfoPtr->FileName) - 1); + AppInfoPtr->FileName[sizeof(AppInfoPtr->FileName) - 1] = '\0'; +} + +/* +**--------------------------------------------------------------------------------------- +** Name: CFE_ES_CopyModuleStatusInfo +** +** Purpose: Populate the cFE_ES_AppInfo structure with the data for an app. +** +** This internal function does not log any errors/events. The caller is expected +** to check the return code and log any relevant errors based on the context. +**--------------------------------------------------------------------------------------- +*/ +void CFE_ES_CopyModuleStatusInfo(const CFE_ES_ModuleLoadStatus_t *StatusPtr, CFE_ES_AppInfo_t *AppInfoPtr) +{ + AppInfoPtr->StartAddress = CFE_ES_MEMADDRESS_C(StatusPtr->InitSymbolAddress); +} + +/* +**--------------------------------------------------------------------------------------- +** Name: CFE_ES_CopyModuleAddressInfo +** +** Purpose: Populate the cFE_ES_AppInfo structure with the data for an app. +** +** This internal function does not log any errors/events. The caller is expected +** to check the return code and log any relevant errors based on the context. +**--------------------------------------------------------------------------------------- +*/ +void CFE_ES_CopyModuleAddressInfo(osal_id_t ModuleId, CFE_ES_AppInfo_t *AppInfoPtr) +{ + OS_module_prop_t ModuleInfo; + int32 ReturnCode; + + ReturnCode = OS_ModuleInfo(ModuleId, &ModuleInfo); + if (ReturnCode == OS_SUCCESS) + { + AppInfoPtr->AddressesAreValid = + (sizeof(ModuleInfo.addr.code_address) <= sizeof(AppInfoPtr->CodeAddress)) && ModuleInfo.addr.valid; + } + else + { + AppInfoPtr->AddressesAreValid = false; + memset(&ModuleInfo, 0, sizeof(ModuleInfo)); + } + + /* + * Convert the internal size and address to the telemetry format. + * (The telemetry format may be a different bitwidth than the native processor) + */ + AppInfoPtr->CodeAddress = CFE_ES_MEMADDRESS_C(ModuleInfo.addr.code_address); + AppInfoPtr->CodeSize = CFE_ES_MEMOFFSET_C(ModuleInfo.addr.code_size); + AppInfoPtr->DataAddress = CFE_ES_MEMADDRESS_C(ModuleInfo.addr.data_address); + AppInfoPtr->DataSize = CFE_ES_MEMOFFSET_C(ModuleInfo.addr.data_size); + AppInfoPtr->BSSAddress = CFE_ES_MEMADDRESS_C(ModuleInfo.addr.bss_address); + AppInfoPtr->BSSSize = CFE_ES_MEMOFFSET_C(ModuleInfo.addr.bss_size); +} diff --git a/modules/es/fsw/src/cfe_es_apps.h b/modules/es/fsw/src/cfe_es_apps.h new file mode 100644 index 000000000..3b8e3c56f --- /dev/null +++ b/modules/es/fsw/src/cfe_es_apps.h @@ -0,0 +1,303 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/** + * @file + * + * Purpose: + * This file contains the Internal interface for the cFE Application control functions of ES. + * These functions and data structures manage the Applications and Child tasks in the cFE. + * + * References: + * Flight Software Branch C Coding Standard Version 1.0a + * cFE Flight Software Application Developers Guide + * + */ + +#ifndef CFE_ES_APPS_H +#define CFE_ES_APPS_H + +/* +** Include Files +*/ +#include "common_types.h" + +#include "cfe_es_api_typedefs.h" +#include "cfe_fs_api_typedefs.h" + +/* +** Macro Definitions +*/ +#define CFE_ES_STARTSCRIPT_MAX_TOKENS_PER_LINE 8 + +/* +** Type Definitions +*/ + +/* +** CFE_ES_AppState_t is a structure of information for External cFE Apps. +** This information is used to control/alter the state of External Apps. +** The fields in this structure are not needed or used for the cFE Core Apps. +*/ +typedef struct +{ + uint32 AppControlRequest; /* What the App should be doing next */ + int32 AppTimerMsec; /* Countdown timer for killing an app, in milliseconds */ + +} CFE_ES_ControlReq_t; + +/* +** CFE_ES_ModuleLoadParams_t contains the information used when a module +** (library or app) load request initially processed in the system. It captures +** the fundamental information - the name, the file to load, its entry point. +** It contains information directly provided by the user, not runtime status or +** other derived information. +** +** This information should remain fairly constant after initial allocation, even +** if the application is restarted for some reason. The major exception is the +** ReloadApp command, which can change the FileName. +*/ +typedef struct +{ + char InitSymbolName[OS_MAX_API_NAME]; + char FileName[OS_MAX_PATH_LEN]; + +} CFE_ES_ModuleLoadParams_t; + +/* +** CFE_ES_ModuleLoadStatus_t is a structure of information used when a module +** (library or app) is actually loaded in the system. It captures the +** runtime information - the module ID and starting address. +** +** This information may change if the module is reloaded. +*/ +typedef struct +{ + osal_id_t ModuleId; + cpuaddr InitSymbolAddress; + +} CFE_ES_ModuleLoadStatus_t; + +/* +** CFE_ES_TaskStartParams_t contains basic details about a CFE task +** +** This information needs to be specified when starting a task and is +** stored as part of the task record for future reference. +*/ +typedef struct +{ + size_t StackSize; + CFE_ES_TaskPriority_Atom_t Priority; + +} CFE_ES_TaskStartParams_t; + +/* +** CFE_ES_AppStartParams_t contains basic details about a CFE app. +** +** This is an extension of the CFE_ES_ModuleLoadParams_t which adds information +** about the main task and exception action. It is only used for apps, as libraries +** do not have a task associated. +*/ +typedef struct +{ + /* + * Basic (static) information about the module + */ + CFE_ES_ModuleLoadParams_t BasicInfo; + + CFE_ES_TaskStartParams_t MainTaskInfo; + CFE_ES_ExceptionAction_Enum_t ExceptionAction; + +} CFE_ES_AppStartParams_t; + +/* +** CFE_ES_AppRecord_t is an internal structure used to keep track of +** CFE Applications that are active in the system. +*/ +typedef struct +{ + CFE_ES_AppId_t AppId; /* The actual AppID of this entry, or undefined */ + char AppName[OS_MAX_API_NAME]; /* The name of the app */ + CFE_ES_AppState_Enum_t AppState; /* Is the app running, or stopped, or waiting? */ + CFE_ES_AppType_Enum_t Type; /* The type of App: CORE or EXTERNAL */ + CFE_ES_AppStartParams_t StartParams; /* The start parameters for an App */ + CFE_ES_ModuleLoadStatus_t LoadStatus; /* Runtime module information */ + CFE_ES_ControlReq_t ControlReq; /* The Control Request Record for External cFE Apps */ + CFE_ES_TaskId_t MainTaskId; /* The Application's Main Task ID */ + +} CFE_ES_AppRecord_t; + +/* +** CFE_ES_TaskRecord_t is an internal structure used to keep track of +** CFE Tasks that are active in the system. +*/ +typedef struct +{ + CFE_ES_TaskId_t TaskId; /* The actual TaskID of this entry, or undefined */ + char TaskName[OS_MAX_API_NAME]; /* Task Name */ + CFE_ES_AppId_t AppId; /* The parent Application's App ID */ + CFE_ES_TaskStartParams_t StartParams; /* The start parameters for the task */ + CFE_ES_TaskEntryFuncPtr_t EntryFunc; /* Task entry function */ + uint32 ExecutionCounter; /* The execution counter for the task */ + +} CFE_ES_TaskRecord_t; + +/* +** CFE_ES_LibRecord_t is an internal structure used to keep track of +** CFE Shared Libraries that are loaded in the system. +*/ +typedef struct +{ + CFE_ES_LibId_t LibId; /* The actual LibID of this entry, or undefined */ + char LibName[OS_MAX_API_NAME]; /* Library Name */ + CFE_ES_ModuleLoadParams_t LoadParams; /* Basic (static) information about the module */ + CFE_ES_ModuleLoadStatus_t LoadStatus; /* Runtime information about the module */ + +} CFE_ES_LibRecord_t; + +/* +** CFE_ES_AppTableScanState_t is an internal structure used to keep state of +** the background app table scan/cleanup process +*/ +typedef struct +{ + uint32 PendingAppStateChanges; + uint32 BackgroundScanTimer; + uint8 LastScanCommandCount; +} CFE_ES_AppTableScanState_t; + +/*****************************************************************************/ +/* +** Function prototypes +*/ + +/* +** Internal function start applications based on the startup script +*/ +void CFE_ES_StartApplications(uint32 ResetType, const char *StartFilePath); + +/* +** Internal function to parse/execute a line of the cFE application startup 'script' +*/ +int32 CFE_ES_ParseFileEntry(const char **TokenList, uint32 NumTokens); + +/* +** Internal function to load a module (app or library) +** This only loads the code and looks up relevent runtime information. +** It does not start any tasks. +*/ +int32 CFE_ES_LoadModule(CFE_ResourceId_t ParentResourceId, const char *ModuleName, + const CFE_ES_ModuleLoadParams_t *LoadParams, CFE_ES_ModuleLoadStatus_t *LoadStatus); + +/* +** Internal function to determine the entry point of an app. +** If the app isn't fully registered in the global app table, +** then this delays until the app is completely configured and the entry point is +** confirmed to be valid. +*/ +int32 CFE_ES_GetTaskFunction(CFE_ES_TaskEntryFuncPtr_t *FuncPtr); + +/* +** Intermediate entry point of all tasks. Determines the actual +** entry point from the global data structures. +*/ +void CFE_ES_TaskEntryPoint(void); + +/* +** Internal function to start a task associated with an app. +*/ +int32 CFE_ES_StartAppTask(CFE_ES_TaskId_t *TaskIdPtr, const char *TaskName, CFE_ES_TaskEntryFuncPtr_t EntryFunc, + const CFE_ES_TaskStartParams_t *Params, CFE_ES_AppId_t ParentAppId); + +/* +** Internal function to create/start a new cFE app +** based on the parameters passed in +*/ +int32 CFE_ES_AppCreate(CFE_ES_AppId_t *ApplicationIdPtr, const char *AppName, const CFE_ES_AppStartParams_t *Params); + +/* +** Internal function to load a a new cFE shared Library +*/ +int32 CFE_ES_LoadLibrary(CFE_ES_LibId_t *LibraryIdPtr, const char *LibName, const CFE_ES_ModuleLoadParams_t *Params); + +/* +** Scan the Application Table for actions to take +*/ +bool CFE_ES_RunAppTableScan(uint32 ElapsedTime, void *Arg); + +/* +** Scan for new exceptions stored in the PSP +*/ +bool CFE_ES_RunExceptionScan(uint32 ElapsedTime, void *Arg); + +/* + * Background file write data getter for ER log entry + */ +bool CFE_ES_BackgroundERLogFileDataGetter(void *Meta, uint32 RecordNum, void **Buffer, size_t *BufSize); + +/* + * Background file write event handler for ER log entry + */ +void CFE_ES_BackgroundERLogFileEventHandler(void *Meta, CFE_FS_FileWriteEvent_t Event, int32 Status, uint32 RecordNum, + size_t BlockSize, size_t Position); + +/* +** Perform the requested control action for an application +*/ +void CFE_ES_ProcessControlRequest(CFE_ES_AppId_t AppId); + +/* +** Clean up all app resources and delete it +*/ +int32 CFE_ES_CleanUpApp(CFE_ES_AppId_t AppId); + +/* +** Clean up all Task resources and detete the task +*/ +int32 CFE_ES_CleanupTaskResources(CFE_ES_TaskId_t TaskId); + +/* +**--------------------------------------------------------------------------------------- +** Name: CFE_ES_CopyModuleBasicInfo +** +** Purpose: Populate the cFE_ES_AppInfo structure from the CFE_ES_ModuleLoadParams_t data +**--------------------------------------------------------------------------------------- +*/ +void CFE_ES_CopyModuleBasicInfo(const CFE_ES_ModuleLoadParams_t *ParamsPtr, CFE_ES_AppInfo_t *AppInfoPtr); + +/* +**--------------------------------------------------------------------------------------- +** Name: CFE_ES_CopyModuleStatusInfo +** +** Purpose: Populate the cFE_ES_AppInfo structure from the CFE_ES_ModuleLoadStatus_t data +**--------------------------------------------------------------------------------------- +*/ +void CFE_ES_CopyModuleStatusInfo(const CFE_ES_ModuleLoadStatus_t *StatusPtr, CFE_ES_AppInfo_t *AppInfoPtr); + +/* +**--------------------------------------------------------------------------------------- +** Name: CFE_ES_CopyModuleAddressInfo +** +** Purpose: Populate the cFE_ES_AppInfo structure with address information from OSAL. +**--------------------------------------------------------------------------------------- +*/ +void CFE_ES_CopyModuleAddressInfo(osal_id_t ModuleId, CFE_ES_AppInfo_t *AppInfoPtr); + +#endif /* CFE_ES_APPS_H */ diff --git a/modules/es/fsw/src/cfe_es_backgroundtask.c b/modules/es/fsw/src/cfe_es_backgroundtask.c new file mode 100644 index 000000000..046c1545c --- /dev/null +++ b/modules/es/fsw/src/cfe_es_backgroundtask.c @@ -0,0 +1,230 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/* +** File: cfe_es_backgroundtask.c +** +** Purpose: This file contains the implementation of the ES "background task" +** +** This task sits idle most of the time, but is woken by the ES application +** for various maintenance duties that may take time to execute, such as +** writing status/log files. +** +*/ + +/* +** Include Section +*/ + +#include + +#include "cfe_es_module_all.h" + +#define CFE_ES_BACKGROUND_SEM_NAME "ES_BG_SEM" +#define CFE_ES_BACKGROUND_CHILD_NAME "ES_BG_TASK" +#define CFE_ES_BACKGROUND_CHILD_STACK_PTR CFE_ES_TASK_STACK_ALLOCATE +#define CFE_ES_BACKGROUND_CHILD_STACK_SIZE CFE_PLATFORM_ES_PERF_CHILD_STACK_SIZE +#define CFE_ES_BACKGROUND_CHILD_PRIORITY CFE_PLATFORM_ES_PERF_CHILD_PRIORITY +#define CFE_ES_BACKGROUND_CHILD_FLAGS 0 +#define CFE_ES_BACKGROUND_MAX_IDLE_DELAY 30000 /* 30 seconds */ + +typedef struct +{ + bool (*RunFunc)(uint32 ElapsedTime, void *Arg); + void * JobArg; + uint32 ActivePeriod; /**< max wait/delay time between calls when job is active */ + uint32 IdlePeriod; /**< max wait/delay time between calls when job is idle */ +} CFE_ES_BackgroundJobEntry_t; + +/* + * List of "background jobs" + * + * This is just a list of functions to periodically call from the context of the background task, + * and can be added/extended as needed. + * + * Each Job function returns a boolean, and should return "true" if it is active, or "false" if it is idle. + * + * This uses "cooperative multitasking" -- the function should do some limited work, then return to the + * background task. It will be called again after a delay period to do more work. + */ +const CFE_ES_BackgroundJobEntry_t CFE_ES_BACKGROUND_JOB_TABLE[] = { + {/* ES app table background scan */ + .RunFunc = CFE_ES_RunAppTableScan, + .JobArg = &CFE_ES_Global.BackgroundAppScanState, + .ActivePeriod = CFE_PLATFORM_ES_APP_SCAN_RATE / 4, + .IdlePeriod = CFE_PLATFORM_ES_APP_SCAN_RATE}, + {/* Performance Log Data Dump to file */ + .RunFunc = CFE_ES_RunPerfLogDump, + .JobArg = &CFE_ES_Global.BackgroundPerfDumpState, + .ActivePeriod = CFE_PLATFORM_ES_PERF_CHILD_MS_DELAY, + .IdlePeriod = CFE_PLATFORM_ES_PERF_CHILD_MS_DELAY * 1000}, + {/* Check for exceptions stored in the PSP */ + .RunFunc = CFE_ES_RunExceptionScan, + .JobArg = NULL, + .ActivePeriod = CFE_PLATFORM_ES_APP_SCAN_RATE, + .IdlePeriod = CFE_PLATFORM_ES_APP_SCAN_RATE}, + {/* Call FS to handle background file writes */ + .RunFunc = CFE_FS_RunBackgroundFileDump, + .JobArg = NULL, + .ActivePeriod = CFE_PLATFORM_ES_APP_SCAN_RATE, + .IdlePeriod = CFE_PLATFORM_ES_APP_SCAN_RATE}}; + +#define CFE_ES_BACKGROUND_NUM_JOBS (sizeof(CFE_ES_BACKGROUND_JOB_TABLE) / sizeof(CFE_ES_BACKGROUND_JOB_TABLE[0])) + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* Name: CFE_ES_BackgroundTask */ +/* */ +/* Purpose: A helper task for low priority routines that may take time to */ +/* execute, such as writing log files. */ +/* */ +/* Assumptions and Notes: This is started from the ES initialization, and */ +/* pends on a semaphore until a work request comes in. This is intended to */ +/* avoid the need to create a child task "on demand" when work items arrive, */ +/* which is a form of dynamic allocation. */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +void CFE_ES_BackgroundTask(void) +{ + int32 status; + uint32 JobTotal; + uint32 NumJobsRunning; + uint32 NextDelay; + uint32 ElapsedTime; + OS_time_t CurrTime; + OS_time_t LastTime; + const CFE_ES_BackgroundJobEntry_t *JobPtr; + + CFE_PSP_GetTime(&LastTime); + + while (true) + { + /* + * compute the elapsed time (difference) between last + * execution and now, in milliseconds. + */ + CFE_PSP_GetTime(&CurrTime); + ElapsedTime = OS_TimeGetTotalMilliseconds(OS_TimeSubtract(CurrTime, LastTime)); + LastTime = CurrTime; + + NextDelay = CFE_ES_BACKGROUND_MAX_IDLE_DELAY; /* default; will be adjusted based on active jobs */ + JobPtr = CFE_ES_BACKGROUND_JOB_TABLE; + JobTotal = CFE_ES_BACKGROUND_NUM_JOBS; + NumJobsRunning = 0; + + while (JobTotal > 0) + { + /* + * call the background job - + * if it returns "true" that means it is active, + * if it returns "false" that means it is idle + */ + if (JobPtr->RunFunc != NULL && JobPtr->RunFunc(ElapsedTime, JobPtr->JobArg)) + { + ++NumJobsRunning; + + if (JobPtr->ActivePeriod != 0 && NextDelay > JobPtr->ActivePeriod) + { + /* next delay is based on this active job wait time */ + NextDelay = JobPtr->ActivePeriod; + } + } + else if (JobPtr->IdlePeriod != 0 && NextDelay > JobPtr->IdlePeriod) + { + /* next delay is based on this idle job wait time */ + NextDelay = JobPtr->IdlePeriod; + } + --JobTotal; + ++JobPtr; + } + + CFE_ES_Global.BackgroundTask.NumJobsRunning = NumJobsRunning; + + status = OS_BinSemTimedWait(CFE_ES_Global.BackgroundTask.WorkSem, NextDelay); + if (status != OS_SUCCESS && status != OS_SEM_TIMEOUT) + { + /* should never occur */ + CFE_ES_WriteToSysLog("CFE_ES: Failed to take background sem: %08lx\n", (unsigned long)status); + break; + } + } +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* Name: CFE_ES_BackgroundInit */ +/* */ +/* Purpose: Initialize the background task */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +int32 CFE_ES_BackgroundInit(void) +{ + int32 status; + + status = OS_BinSemCreate(&CFE_ES_Global.BackgroundTask.WorkSem, CFE_ES_BACKGROUND_SEM_NAME, 0, 0); + if (status != OS_SUCCESS) + { + CFE_ES_WriteToSysLog("CFE_ES: Failed to create background sem: %08lx\n", (unsigned long)status); + return status; + } + + /* Spawn a task to write the performance data to a file */ + status = CFE_ES_CreateChildTask(&CFE_ES_Global.BackgroundTask.TaskID, CFE_ES_BACKGROUND_CHILD_NAME, + CFE_ES_BackgroundTask, CFE_ES_BACKGROUND_CHILD_STACK_PTR, + CFE_ES_BACKGROUND_CHILD_STACK_SIZE, CFE_ES_BACKGROUND_CHILD_PRIORITY, + CFE_ES_BACKGROUND_CHILD_FLAGS); + + if (status != OS_SUCCESS) + { + CFE_ES_WriteToSysLog("CFE_ES: Failed to create background task: %08lx\n", (unsigned long)status); + return status; + } + + return CFE_SUCCESS; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* Name: CFE_ES_BackgroundCleanup */ +/* */ +/* Purpose: Exit/Stop the background task */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +void CFE_ES_BackgroundCleanup(void) +{ + CFE_ES_DeleteChildTask(CFE_ES_Global.BackgroundTask.TaskID); + OS_BinSemDelete(CFE_ES_Global.BackgroundTask.WorkSem); + + CFE_ES_Global.BackgroundTask.TaskID = CFE_ES_TASKID_UNDEFINED; + CFE_ES_Global.BackgroundTask.WorkSem = OS_OBJECT_ID_UNDEFINED; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* Name: CFE_ES_BackgroundWakeup */ +/* */ +/* Purpose: Wake up the background task */ +/* Notifies the background task to perform an extra poll for new work */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +void CFE_ES_BackgroundWakeup(void) +{ + /* wake up the background task by giving the sem. + * This is "informational" and not strictly required, + * but it will make the task immediately wake up and check for new + * work if it was idle. */ + OS_BinSemGive(CFE_ES_Global.BackgroundTask.WorkSem); +} diff --git a/modules/es/fsw/src/cfe_es_cds.c b/modules/es/fsw/src/cfe_es_cds.c new file mode 100644 index 000000000..9e6d58603 --- /dev/null +++ b/modules/es/fsw/src/cfe_es_cds.c @@ -0,0 +1,931 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/* +** File: +** cfe_es_cds.c +** +** Purpose: +** This file implements the cFE Executive Services Critical Data Store functions. +** +** References: +** Flight Software Branch C Coding Standard Version 1.0a +** cFE Flight Software Application Developers Guide +** +** Notes: +** +** Modification History: +** +*/ + +/* +** Required header files. +*/ +#include "cfe_es_module_all.h" + +#include +#include +#include + +/*****************************************************************************/ +/* + * CFE_ES_CDS_EarlyInit + * + * NOTE: For complete prolog information, see 'cfe_es_cds.h' + */ +/*****************************************************************************/ +int32 CFE_ES_CDS_EarlyInit(void) +{ + CFE_ES_CDS_Instance_t *CDS = &CFE_ES_Global.CDSVars; + uint32 PlatformSize; + size_t MinRequiredSize; + int32 Status; + + CFE_ES_Global.CDSIsAvailable = false; + + /* Create CDS general access mutex */ + Status = OS_MutSemCreate(&CDS->GenMutex, CFE_ES_CDS_MUT_REG_NAME, CFE_ES_CDS_MUT_REG_VALUE); + if (Status != OS_SUCCESS) + { + CFE_ES_SysLogWrite_Unsync("CFE_ES_CDS_EarlyInit: Failed to create mutex with error %d\n", (int)Status); + return CFE_STATUS_EXTERNAL_RESOURCE_FAIL; + } + + CDS->LastCDSBlockId = CFE_ResourceId_FromInteger(CFE_ES_CDSBLOCKID_BASE); + + /* Get CDS size from PSP. Note that the PSP interface + * uses "uint32" for size here. */ + Status = CFE_PSP_GetCDSSize(&PlatformSize); + if (Status != CFE_PSP_SUCCESS) + { + /* Error getting the size of the CDS from the BSP */ + CFE_ES_WriteToSysLog("CFE_CDS:EarlyInit-Unable to obtain CDS Size from BSP (Err=0x%08X)\n", + (unsigned int)Status); + return Status; + } + + /* Always truncate the size to the nearest 4 byte boundary */ + CDS->TotalSize = PlatformSize & 0xfffffffc; + + /* Compute the minimum size required for the CDS with the current configuration of the cFE */ + MinRequiredSize = CDS_RESERVED_MIN_SIZE; + MinRequiredSize += CFE_ES_CDSReqdMinSize(CFE_PLATFORM_ES_CDS_MAX_NUM_ENTRIES); /* Max # of Min Sized Blocks */ + + if (CDS->TotalSize < MinRequiredSize) + { + CFE_ES_WriteToSysLog("CFE_CDS:EarlyInit-CDS Size (%lu) less than required (%lu)\n", + (unsigned long)CDS->TotalSize, (unsigned long)MinRequiredSize); + Status = CFE_SUCCESS; + } + else + { + CDS->DataSize = CDS->TotalSize; + CDS->DataSize -= CDS_RESERVED_MIN_SIZE; + + /* If the size was obtained successfully and meets the minimum requirements, then check its contents */ + Status = CFE_ES_ValidateCDS(); + + if (Status == CFE_SUCCESS) + { + /* If a valid CDS was found, rebuild the memory pool */ + Status = CFE_ES_RebuildCDS(); + } + + /* If the CDS is accessible but invalid, then create a new one */ + if (Status == CFE_ES_CDS_INVALID) + { + /* First wipe the entire CDS area */ + Status = CFE_ES_ClearCDS(); + + if (Status == CFE_SUCCESS) + { + Status = CFE_ES_InitCDSSignatures(); + } + + if (Status == CFE_SUCCESS) + { + /* Initialize the variables for managing the CDS Memory Pool */ + Status = CFE_ES_CreateCDSPool(CDS->DataSize, CDS_POOL_OFFSET); + } + + if (Status == CFE_SUCCESS) + { + Status = CFE_ES_InitCDSRegistry(); + } + } + + if (Status != CFE_SUCCESS) + { + /* Unrecoverable error while reading the CDS */ + CFE_ES_WriteToSysLog("CFE_CDS:EarlyInit-error validating/initializing CDS (0x%08lX)\n", + (unsigned long)Status); + } + else + { + /* Set the CDS Overall flag to be present/valid */ + CFE_ES_Global.CDSIsAvailable = true; + } + } + + return Status; + +} /* End of CFE_ES_CDS_EarlyInit() */ + +/*******************************************************************/ +/* + * CFE_ES_LocateCDSBlockRecordByID + * + * NOTE: For complete prolog information, see 'cfe_es_cds.h' + */ +/*******************************************************************/ +int32 CFE_ES_CDSHandle_ToIndex(CFE_ES_CDSHandle_t BlockID, uint32 *Idx) +{ + return CFE_ResourceId_ToIndex(CFE_RESOURCEID_UNWRAP(BlockID), CFE_ES_CDSBLOCKID_BASE, + CFE_PLATFORM_ES_CDS_MAX_NUM_ENTRIES, Idx); +} + +/*--------------------------------------------------------------------------------------- + * Function: CFE_ES_CheckCDSHandleSlotUsed + * + * Purpose: Helper function, Aids in allocating a new ID by checking if + * a given ID is available. Must be called while locked. + *--------------------------------------------------------------------------------------- + */ +bool CFE_ES_CheckCDSHandleSlotUsed(CFE_ResourceId_t CheckId) +{ + CFE_ES_CDS_RegRec_t *CDSRegRecPtr; + /* + * Note - The pointer here should never be NULL because the ID should always be + * within the expected range, but if it ever is NULL, this should return true + * such that the caller will _not_ attempt to use the record. + */ + CDSRegRecPtr = CFE_ES_LocateCDSBlockRecordByID(CFE_ES_CDSHANDLE_C(CheckId)); + return (CDSRegRecPtr == NULL || CFE_ES_CDSBlockRecordIsUsed(CDSRegRecPtr)); +} + +/*******************************************************************/ +/* + * CFE_ES_LocateCDSBlockRecordByID + * + * NOTE: For complete prolog information, see 'cfe_es_cds.h' + */ +/*******************************************************************/ +CFE_ES_CDS_RegRec_t *CFE_ES_LocateCDSBlockRecordByID(CFE_ES_CDSHandle_t BlockID) +{ + CFE_ES_CDS_Instance_t *CDS = &CFE_ES_Global.CDSVars; + CFE_ES_CDS_RegRec_t * CDSRegRecPtr; + uint32 Idx; + + if (CFE_ES_CDSHandle_ToIndex(BlockID, &Idx) == CFE_SUCCESS) + { + CDSRegRecPtr = &CDS->Registry[Idx]; + } + else + { + CDSRegRecPtr = NULL; + } + + return CDSRegRecPtr; +} + +/*******************************************************************/ +/* + * CFE_ES_CacheRead() + * + * NOTE: For complete prolog information, see 'cfe_es_cds.h' + */ +/*******************************************************************/ +int32 CFE_ES_CDS_CacheFetch(CFE_ES_CDS_AccessCache_t *Cache, size_t Offset, size_t Size) +{ + int32 Status; + + if (Size > 0 && Size <= sizeof(Cache->Data)) + { + Cache->AccessStatus = CFE_PSP_ReadFromCDS(&Cache->Data, Offset, Size); + + if (Cache->AccessStatus == CFE_PSP_SUCCESS) + { + Cache->Offset = Offset; + Cache->Size = Size; + Status = CFE_SUCCESS; + } + else + { + Status = CFE_ES_CDS_ACCESS_ERROR; + } + } + else + { + Status = CFE_ES_CDS_INVALID_SIZE; + } + + return Status; +} + +/*******************************************************************/ +/* + * CFE_ES_CDS_CacheFlush() + * + * NOTE: For complete prolog information, see 'cfe_es_cds.h' + */ +/*******************************************************************/ +int32 CFE_ES_CDS_CacheFlush(CFE_ES_CDS_AccessCache_t *Cache) +{ + int32 Status; + + if (Cache->Size > 0 && Cache->Size <= sizeof(Cache->Data)) + { + Cache->AccessStatus = CFE_PSP_WriteToCDS(&Cache->Data, Cache->Offset, Cache->Size); + + if (Cache->AccessStatus == CFE_PSP_SUCCESS) + { + Status = CFE_SUCCESS; + } + else + { + Status = CFE_ES_CDS_ACCESS_ERROR; + } + } + else + { + Status = CFE_ES_CDS_INVALID_SIZE; + } + + return Status; +} + +/*******************************************************************/ +/* + * CFE_ES_CDS_CachePreload() + * + * NOTE: For complete prolog information, see 'cfe_es_cds.h' + */ +/*******************************************************************/ +int32 CFE_ES_CDS_CachePreload(CFE_ES_CDS_AccessCache_t *Cache, const void *Source, size_t Offset, size_t Size) +{ + int32 Status; + + if (Size > 0 && Size <= sizeof(Cache->Data)) + { + if (Source == NULL) + { + /* just zero it out */ + memset(&Cache->Data, 0, Size); + } + else if (Source != &Cache->Data) + { + /* copy from the user-supplied preload data */ + memcpy(&Cache->Data, Source, Size); + } + Cache->Size = Size; + Cache->Offset = Offset; + Status = CFE_SUCCESS; + } + else + { + Status = CFE_ES_CDS_INVALID_SIZE; + } + + return Status; +} + +/******************************************************************* +** +** CFE_ES_RegisterCDSEx +** +** NOTE: For complete prolog information, see 'cfe_es_cds.h' +********************************************************************/ + +int32 CFE_ES_RegisterCDSEx(CFE_ES_CDSHandle_t *HandlePtr, size_t UserBlockSize, const char *Name, bool CriticalTbl) +{ + CFE_ES_CDS_Instance_t *CDS = &CFE_ES_Global.CDSVars; + int32 Status; + int32 RegUpdateStatus; + CFE_ES_CDS_RegRec_t * RegRecPtr; + size_t BlockOffset; + size_t OldBlockSize; + size_t NewBlockSize; + CFE_ResourceId_t PendingBlockId; + bool IsNewEntry; + bool IsNewOffset; + + Status = CFE_SUCCESS; + RegUpdateStatus = CFE_SUCCESS; + IsNewEntry = false; + IsNewOffset = false; + + if (UserBlockSize == 0 || UserBlockSize > CDS_ABS_MAX_BLOCK_SIZE) + { + /* Block size is not supportable */ + return CFE_ES_CDS_INVALID_SIZE; + } + + /* Lock Registry for update. This prevents two applications from */ + /* trying to register CDSs at the same location at the same time */ + CFE_ES_LockCDS(); + + /* + * Check for an existing entry with the same name. + */ + RegRecPtr = CFE_ES_LocateCDSBlockRecordByName(Name); + if (RegRecPtr != NULL) + { + /* in CDS a duplicate name is not necessarily an error, we + * may reuse/resize the existing entry */ + PendingBlockId = CFE_RESOURCEID_UNWRAP(CFE_ES_CDSBlockRecordGetID(RegRecPtr)); + } + else + { + /* scan for a free slot */ + PendingBlockId = CFE_ResourceId_FindNext(CDS->LastCDSBlockId, CFE_PLATFORM_ES_CDS_MAX_NUM_ENTRIES, + CFE_ES_CheckCDSHandleSlotUsed); + RegRecPtr = CFE_ES_LocateCDSBlockRecordByID(CFE_ES_CDSHANDLE_C(PendingBlockId)); + + if (RegRecPtr != NULL) + { + /* Fully clear the entry, just in case of stale data */ + memset(RegRecPtr, 0, sizeof(*RegRecPtr)); + CDS->LastCDSBlockId = PendingBlockId; + IsNewEntry = true; + Status = CFE_SUCCESS; + } + else + { + Status = CFE_ES_NO_RESOURCE_IDS_AVAILABLE; + PendingBlockId = CFE_RESOURCEID_UNDEFINED; + } + } + + if (RegRecPtr != NULL) + { + /* Account for the extra header which will be added */ + NewBlockSize = UserBlockSize; + NewBlockSize += sizeof(CFE_ES_CDS_BlockHeader_t); + + /* If a reallocation is needed, the old block may need to be freed first */ + if (Status == CFE_SUCCESS && RegRecPtr->BlockOffset != 0 && NewBlockSize != RegRecPtr->BlockSize) + { + /* If the new size is different, the old CDS must be deleted first */ + Status = CFE_ES_GenPoolPutBlock(&CDS->Pool, &OldBlockSize, RegRecPtr->BlockOffset); + + /* + * Note because CDS puts a signature at the very beginning of the memory, + * valid data offsets are never zero. + */ + if (Status == CFE_SUCCESS) + { + RegRecPtr->BlockOffset = 0; + RegRecPtr->BlockSize = 0; + } + } + + /* If a new allocation is needed, do it now */ + if (Status == CFE_SUCCESS && RegRecPtr->BlockOffset == 0) + { + /* Allocate the block for the CDS */ + Status = CFE_ES_GenPoolGetBlock(&CDS->Pool, &BlockOffset, NewBlockSize); + if (Status == CFE_SUCCESS) + { + /* Save the size of the CDS */ + RegRecPtr->BlockOffset = BlockOffset; + RegRecPtr->BlockSize = NewBlockSize; + IsNewOffset = true; + } + } + + if (Status == CFE_SUCCESS && IsNewEntry) + { + /* Save flag indicating whether it is a Critical Table or not */ + RegRecPtr->Table = CriticalTbl; + + /* Save CDS Name in Registry */ + strncpy(RegRecPtr->Name, Name, sizeof(RegRecPtr->Name) - 1); + RegRecPtr->Name[sizeof(RegRecPtr->Name) - 1] = 0; + CFE_ES_CDSBlockRecordSetUsed(RegRecPtr, PendingBlockId); + } + + if (Status == CFE_SUCCESS && (IsNewOffset || IsNewEntry)) + { + /* If we succeeded at creating a CDS, save updated registry in the CDS */ + RegUpdateStatus = CFE_ES_UpdateCDSRegistry(); + } + } + + /* Unlock Registry for update */ + CFE_ES_UnlockCDS(); + + /* Log any failures AFTER releasing the lock */ + if (RegUpdateStatus != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("CFE_CDS:RegCDS-Failed to update CDS Registry (Stat=0x%08X)\n", + (unsigned int)RegUpdateStatus); + + /* + * Return failure only if this was the primary error, + * do not overwrite a preexisting error. + */ + if (Status == CFE_SUCCESS) + { + Status = RegUpdateStatus; + } + } + + if (Status == CFE_SUCCESS && !IsNewOffset) + { + /* + * For backward compatibility, return the + * special non-success success code when + * reallocating an existing CDS. + * + * Note this intentionally needs to return CFE_SUCCESS + * when reusing an exiting entry but changing the size. + */ + Status = CFE_ES_CDS_ALREADY_EXISTS; + } + + *HandlePtr = CFE_ES_CDSHANDLE_C(PendingBlockId); + + return (Status); + +} /* End of CFE_ES_RegisterCDSEx() */ + +/******************************************************************* +** +** CFE_ES_ValidateCDS +** +** NOTE: For complete prolog information, see prototype above +********************************************************************/ + +int32 CFE_ES_ValidateCDS(void) +{ + CFE_ES_CDS_Instance_t *CDS = &CFE_ES_Global.CDSVars; + size_t TrailerOffset; + const size_t SIG_CDS_SIZE = {CFE_ES_CDS_SIGNATURE_LEN}; + int32 Status; + + /* Perform 2 checks to validate the CDS Memory Pool */ + /* First, determine if the first validity check field is correct */ + Status = CFE_ES_CDS_CacheFetch(&CDS->Cache, CDS_SIG_BEGIN_OFFSET, SIG_CDS_SIZE); + if (Status != CFE_PSP_SUCCESS) + { + CFE_ES_WriteToSysLog("CFE_CDS:Validate-1st ReadFromCDS Failed. Status=0x%X\n", (unsigned int)Status); + return Status; + } + + if (strncmp(CDS->Cache.Data.Sig, CFE_ES_CDS_SIGNATURE_BEGIN, CFE_ES_CDS_SIGNATURE_LEN) != 0) + { + /* Beginning Validity Field failed */ + return CFE_ES_CDS_INVALID; + } + + TrailerOffset = CDS->TotalSize; + TrailerOffset -= sizeof(CFE_ES_CDS_PersistentTrailer_t); + + Status = CFE_ES_CDS_CacheFetch(&CDS->Cache, TrailerOffset, SIG_CDS_SIZE); + if (Status != CFE_PSP_SUCCESS) + { + /* BSP reported an error reading from CDS */ + CFE_ES_WriteToSysLog("CFE_CDS:Validate-2nd ReadFromCDS Failed. Status=0x%X\n", (unsigned int)Status); + return Status; + } + + if (strncmp(CDS->Cache.Data.Sig, CFE_ES_CDS_SIGNATURE_END, CFE_ES_CDS_SIGNATURE_LEN) != 0) + { + /* Ending Validity Field failed */ + return CFE_ES_CDS_INVALID; + } + + /* All sanity checks passed */ + return CFE_SUCCESS; +} /* End of CFE_ES_ValidateCDS() */ + +/******************************************************************* +** +** CFE_ES_ClearCDS +** +** NOTE: For complete prolog information, see prototype above +********************************************************************/ + +int32 CFE_ES_ClearCDS(void) +{ + CFE_ES_CDS_Instance_t *CDS = &CFE_ES_Global.CDSVars; + size_t RemainSize; + int32 Status; + + /* Clear the CDS to ensure everything is gone */ + /* Create a block of zeros to write to the CDS */ + Status = CFE_ES_CDS_CachePreload(&CDS->Cache, NULL, 0, sizeof(CDS->Cache.Data.Zero)); + + /* While there is space to write another block of zeros, then do so */ + while (CDS->Cache.Offset < CDS->TotalSize) + { + RemainSize = CDS->TotalSize - CDS->Cache.Offset; + if (RemainSize < sizeof(CDS->Cache.Data.Zero)) + { + /* partial size */ + CDS->Cache.Size = RemainSize; + } + Status = CFE_ES_CDS_CacheFlush(&CDS->Cache); + if (Status != CFE_SUCCESS) + { + break; + } + + CDS->Cache.Offset += CDS->Cache.Size; + } + + if (Status != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("CFE_CDS:Init-Clear CDS failed @ Offset=%lu Status=0x%08X\n", + (unsigned long)CDS->Cache.Offset, (unsigned int)CDS->Cache.AccessStatus); + } + + return Status; +} + +/******************************************************************* +** +** CFE_ES_InitCDSSignatures +** +** NOTE: For complete prolog information, see prototype above +********************************************************************/ + +int32 CFE_ES_InitCDSSignatures(void) +{ + CFE_ES_CDS_Instance_t *CDS = &CFE_ES_Global.CDSVars; + size_t SigOffset; + int32 Status; + + /* Initialize the Validity Check strings */ + SigOffset = 0; + CFE_ES_CDS_CachePreload(&CDS->Cache, CFE_ES_CDS_SIGNATURE_BEGIN, SigOffset, CFE_ES_CDS_SIGNATURE_LEN); + Status = CFE_ES_CDS_CacheFlush(&CDS->Cache); + if (Status != CFE_SUCCESS) + { + /* BSP reported an error writing to CDS */ + CFE_ES_WriteToSysLog("CFE_CDS:Init-'_CDSBeg_' write failed. Status=0x%08X\n", + (unsigned int)CDS->Cache.AccessStatus); + return Status; + } + + SigOffset = CDS->TotalSize; + SigOffset -= sizeof(CFE_ES_CDS_PersistentTrailer_t); + + CFE_ES_CDS_CachePreload(&CDS->Cache, CFE_ES_CDS_SIGNATURE_END, SigOffset, CFE_ES_CDS_SIGNATURE_LEN); + Status = CFE_ES_CDS_CacheFlush(&CDS->Cache); + if (Status != CFE_PSP_SUCCESS) + { + CFE_ES_WriteToSysLog("CFE_CDS:Init-'_CDSEnd_' write failed. Status=0x%08X\n", + (unsigned int)CDS->Cache.AccessStatus); + return Status; + } + + return Status; +} /* End of CFE_ES_InitCDSSignatures() */ + +/******************************************************************* +** +** CFE_ES_InitCDSRegistry +** +** NOTE: For complete prolog information, see prototype above +********************************************************************/ + +int32 CFE_ES_InitCDSRegistry(void) +{ + CFE_ES_CDS_Instance_t *CDS = &CFE_ES_Global.CDSVars; + int32 Status; + uint32 RegSize; + + /* Initialize the local CDS Registry */ + RegSize = CFE_PLATFORM_ES_CDS_MAX_NUM_ENTRIES; + CFE_ES_CDS_CachePreload(&CDS->Cache, &RegSize, CDS_REG_SIZE_OFFSET, sizeof(RegSize)); + /* Copy the number of registry entries to the CDS */ + Status = CFE_ES_CDS_CacheFlush(&CDS->Cache); + if (Status == CFE_SUCCESS) + { + memset(CDS->Registry, 0, sizeof(CDS->Registry)); + + Status = CFE_ES_UpdateCDSRegistry(); + } + else + { + CFE_ES_WriteToSysLog("CFE_CDS:InitReg-Failed to write Reg Size. Status=0x%08X\n", + (unsigned int)CDS->Cache.AccessStatus); + } + + return Status; +} /* End of CFE_ES_InitCDSRegistry() */ + +/******************************************************************* +** +** CFE_ES_UpdateCDSRegistry +** +** NOTE: For complete prolog information, see 'cfe_es_cds.h' +********************************************************************/ + +int32 CFE_ES_UpdateCDSRegistry(void) +{ + CFE_ES_CDS_Instance_t *CDS = &CFE_ES_Global.CDSVars; + int32 Status; + + /* Copy the contents of the local registry to the CDS */ + Status = CFE_PSP_WriteToCDS(CDS->Registry, CDS_REG_OFFSET, sizeof(CDS->Registry)); + + if (Status != CFE_PSP_SUCCESS) + { + CFE_ES_WriteToSysLog("CFE_CDS:UpdateReg-Failed to write CDS Registry. Status=0x%08X\n", (unsigned int)Status); + Status = CFE_ES_CDS_ACCESS_ERROR; + } + + return Status; +} + +/******************************************************************* +** +** CFE_ES_FormCDSName +** +** NOTE: For complete prolog information, see 'cfe_es_cds.h' +********************************************************************/ + +void CFE_ES_FormCDSName(char *FullCDSName, const char *CDSName, CFE_ES_AppId_t ThisAppId) +{ + char AppName[OS_MAX_API_NAME]; + + CFE_ES_GetAppName(AppName, ThisAppId, sizeof(AppName)); + + /* Ensure that AppName is null terminated */ + AppName[OS_MAX_API_NAME - 1] = '\0'; + + /* Complete formation of processor specific table name */ + sprintf(FullCDSName, "%s.%s", AppName, CDSName); + + return; +} /* End of CFE_ES_FormCDSName() */ + +/******************************************************************* +** +** CFE_ES_LockCDSRegistry +** +** NOTE: For complete prolog information, see 'cfe_es_cds.h' +********************************************************************/ + +int32 CFE_ES_LockCDS(void) +{ + CFE_ES_CDS_Instance_t *CDS = &CFE_ES_Global.CDSVars; + int32 Status; + + Status = OS_MutSemTake(CDS->GenMutex); + + /* Convert to CFE return code */ + if (Status == OS_SUCCESS) + { + Status = CFE_SUCCESS; + } + else + { + Status = CFE_STATUS_EXTERNAL_RESOURCE_FAIL; + } + + return Status; + +} /* End of CFE_ES_LockCDSRegistry() */ + +/******************************************************************* +** +** CFE_ES_UnlockCDSRegistry +** +** NOTE: For complete prolog information, see 'cfe_es_cds.h' +********************************************************************/ + +int32 CFE_ES_UnlockCDS(void) +{ + CFE_ES_CDS_Instance_t *CDS = &CFE_ES_Global.CDSVars; + int32 Status; + + Status = OS_MutSemGive(CDS->GenMutex); + + /* Convert to CFE return code */ + if (Status == OS_SUCCESS) + { + Status = CFE_SUCCESS; + } + else + { + Status = CFE_STATUS_EXTERNAL_RESOURCE_FAIL; + } + + return Status; + +} /* End of CFE_ES_UnlockCDSRegistry() */ + +/******************************************************************* +** +** CFE_ES_LocateCDSBlockRecordByName +** +** NOTE: For complete prolog information, see 'cfe_es_cds.h' +********************************************************************/ + +CFE_ES_CDS_RegRec_t *CFE_ES_LocateCDSBlockRecordByName(const char *CDSName) +{ + CFE_ES_CDS_Instance_t *CDS = &CFE_ES_Global.CDSVars; + CFE_ES_CDS_RegRec_t * CDSRegRecPtr; + uint32 NumReg; + + CDSRegRecPtr = CDS->Registry; + NumReg = CFE_PLATFORM_ES_CDS_MAX_NUM_ENTRIES; + while (true) + { + if (NumReg == 0) + { + CDSRegRecPtr = NULL; /* not found */ + break; + } + + if (CFE_ES_CDSBlockRecordIsUsed(CDSRegRecPtr)) + { + /* Perform a case sensitive name comparison */ + if (strcmp(CDSName, CDSRegRecPtr->Name) == 0) + { + /* If the names match, then stop */ + break; + } + } + + ++CDSRegRecPtr; + --NumReg; + } + + return CDSRegRecPtr; +} /* End of CFE_ES_LocateCDSBlockRecordByName() */ + +/******************************************************************* +** +** CFE_ES_RebuildCDS +** +** NOTE: For complete prolog information, see above +********************************************************************/ + +int32 CFE_ES_RebuildCDS(void) +{ + CFE_ES_CDS_Instance_t *CDS = &CFE_ES_Global.CDSVars; + int32 Status; + + /* First, determine if the CDS registry stored in the CDS is smaller or equal */ + /* in size to the CDS registry we are currently configured for */ + /* Copy the number of registry entries to the CDS */ + Status = CFE_ES_CDS_CacheFetch(&CDS->Cache, CDS_REG_SIZE_OFFSET, sizeof(CDS->Cache.Data.RegistrySize)); + if (Status != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("CFE_CDS:Rebuild-PSP Error reading Registry size (%lx)\n", + (unsigned long)CDS->Cache.AccessStatus); + return CFE_ES_CDS_INVALID; + } + + if (CDS->Cache.Data.RegistrySize != CFE_PLATFORM_ES_CDS_MAX_NUM_ENTRIES) + { + /* Registry in CDS is incompatible size to recover */ + CFE_ES_WriteToSysLog("CFE_CDS:Rebuild-Registry in CDS incorrect size (%lu)\n", + (unsigned long)CDS->Cache.Data.RegistrySize); + return CFE_ES_CDS_INVALID; + } + + Status = CFE_PSP_ReadFromCDS(&CDS->Registry, CDS_REG_OFFSET, sizeof(CDS->Registry)); + + if (Status == CFE_PSP_SUCCESS) + { + /* Scan the memory pool and identify the created but currently unused memory blocks */ + Status = CFE_ES_RebuildCDSPool(CDS->DataSize, CDS_POOL_OFFSET); + } + else + { + /* Registry in CDS is unreadable */ + CFE_ES_WriteToSysLog("CFE_CDS:Rebuild-Registry in CDS is unreadable, PSP error %lx\n", (unsigned long)Status); + Status = CFE_ES_CDS_INVALID; + } + + return Status; +} + +/******************************************************************* +** +** CFE_ES_DeleteCDS +** +** NOTE: For complete prolog information, see 'cfe_es_cds.h' +********************************************************************/ + +int32 CFE_ES_DeleteCDS(const char *CDSName, bool CalledByTblServices) +{ + CFE_ES_CDS_Instance_t *CDS = &CFE_ES_Global.CDSVars; + int32 Status; + CFE_ES_CDS_RegRec_t * RegRecPtr; + char OwnerName[OS_MAX_API_NAME]; + CFE_ES_AppId_t AppId; + uint32 i; + char LogMessage[CFE_ES_MAX_SYSLOG_MSG_SIZE]; + size_t OldBlockSize; + + LogMessage[0] = 0; + + /* Lock Registry for update. This prevents two applications from */ + /* trying to change the CDS registry at the same time */ + CFE_ES_LockCDS(); + + /* Find CDS name in registry */ + RegRecPtr = CFE_ES_LocateCDSBlockRecordByName(CDSName); + + /* Check to see if CDS is already in the registry */ + if (RegRecPtr != NULL) + { + /* Critical tables are not allowed to be deleted via an ES Command. */ + /* They must be deleted by a Table Services Command */ + if (RegRecPtr->Table != CalledByTblServices) + { + Status = CFE_ES_CDS_WRONG_TYPE_ERR; + } + else + { + /* Check to see if the owning application is still active */ + /* First, extract the owning application name */ + i = 0; + while ((i < (OS_MAX_API_NAME - 1) && (RegRecPtr->Name[i] != '.'))) + { + OwnerName[i] = RegRecPtr->Name[i]; + i++; + } + + /* Null terminate the application name */ + OwnerName[i] = '\0'; + + /* Check to see if the Application Name is in the Registered Apps list */ + Status = CFE_ES_GetAppIDByName(&AppId, OwnerName); + + /* If we can't find the name, then things are good */ + if (Status != CFE_SUCCESS) + { + /* Free the registry entry and the CDS memory block associated with it */ + Status = CFE_ES_GenPoolPutBlock(&CDS->Pool, &OldBlockSize, RegRecPtr->BlockOffset); + + /* Report any errors incurred while freeing the CDS Memory Block */ + if (Status != CFE_SUCCESS) + { + CFE_ES_SysLog_snprintf( + LogMessage, sizeof(LogMessage), + "CFE_ES:DeleteCDS-Failed to free CDS Mem Block (Handle=0x%08lX)(Stat=0x%08X)\n", + (unsigned long)RegRecPtr->BlockOffset, (unsigned int)Status); + } + else + { + /* Remove entry from the CDS Registry */ + CFE_ES_CDSBlockRecordSetFree(RegRecPtr); + + Status = CFE_ES_UpdateCDSRegistry(); + + if (Status != CFE_SUCCESS) + { + CFE_ES_SysLog_snprintf(LogMessage, sizeof(LogMessage), + "CFE_ES:DeleteCDS-Failed to update CDS Registry (Stat=0x%08X)\n", + (unsigned int)Status); + } + } + } + else + { + Status = CFE_ES_CDS_OWNER_ACTIVE_ERR; + } + } + } + else /* Error - CDS not in registry */ + { + Status = CFE_ES_ERR_NAME_NOT_FOUND; + } + + /* Unlock Registry for future updates */ + CFE_ES_UnlockCDS(); + + /* Output the message to syslog once the CDS registry resource is unlocked */ + if (LogMessage[0] != 0) + { + CFE_ES_SYSLOG_APPEND(LogMessage); + } + + return Status; +} /* End of CFE_ES_DeleteCDS() */ + +/* end of file */ diff --git a/modules/es/fsw/src/cfe_es_cds.h b/modules/es/fsw/src/cfe_es_cds.h new file mode 100644 index 000000000..0a952a58a --- /dev/null +++ b/modules/es/fsw/src/cfe_es_cds.h @@ -0,0 +1,641 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/** + * @file + * + * Purpose: + * This file contains the Internal interface for the cFE Critical Data Store functions. + * These functions and data structures manage the Critical Data Store in the cFE. + * + * References: + * Flight Software Branch C Coding Standard Version 1.0a + * cFE Flight Software Application Developers Guide + * + * Notes: + * + */ + +#ifndef CFE_ES_CDS_H +#define CFE_ES_CDS_H + +/* +** Include Files +*/ +#include "common_types.h" +#include "cfe_es_generic_pool.h" + +/* +** Macro Definitions +*/ + +/** \name Registry Mutex Definitions */ +/** \{ */ +#define CFE_ES_CDS_MUT_REG_NAME "CDS_MUTEX" /**< \brief Name of Mutex controlling CDS Access */ +#define CFE_ES_CDS_MUT_REG_VALUE 0 /**< \brief Initial Value of CDS Access Mutex */ +/** \} */ + +/** \name Registry Signature Definitions */ +/** \{ */ +#define CFE_ES_CDS_SIGNATURE_LEN 8 /**< \brief Length of CDS signature field. */ +#define CFE_ES_CDS_SIGNATURE_BEGIN "_CDSBeg_" /**< \brief Fixed signature at beginning of CDS */ +#define CFE_ES_CDS_SIGNATURE_END "_CDSEnd_" /**< \brief Fixed signature at end of CDS */ +/** \} */ + +/* + * Space in CDS should be aligned to a multiple of uint32 + * These helper macros round up to a whole number of words + */ +#define CDS_SIZE_TO_U32WORDS(x) (((x) + 3) / sizeof(uint32)) +#define CDS_RESERVE_SPACE(name, size) uint32 name[CDS_SIZE_TO_U32WORDS(size)] + +/* Define offset addresses for CDS data segments */ +#define CDS_SIG_BEGIN_OFFSET offsetof(CFE_ES_CDS_PersistentHeader_t, SignatureBegin) +#define CDS_REG_SIZE_OFFSET offsetof(CFE_ES_CDS_PersistentHeader_t, RegistrySize) +#define CDS_REG_OFFSET offsetof(CFE_ES_CDS_PersistentHeader_t, RegistryContent) +#define CDS_POOL_OFFSET sizeof(CFE_ES_CDS_PersistentHeader_t) + +/* + * Absolute Minimum CDS size conceivably supportable by the implementation. + * This is the space required for the basic signatures and registry information. + * It is not possible to create a CDS with a storage area smaller than this. + */ +#define CDS_RESERVED_MIN_SIZE sizeof(CFE_ES_CDS_PersistentHeader_t) + sizeof(CFE_ES_CDS_PersistentTrailer_t) + +/* + * Absolute Maximum Block size conceivably supportable by the implementation. + * User-defined platform limits (in cfe_platform_cfg.h) may be lower, + * but this is a hard limit to avoid overflow of a 32 bit integer. + * + * This ensures the size is safe for a PSP that uses 32 bit CDS offsets. + * (It is not anticipated that a CDS would need to exceed this size) + */ +#define CDS_ABS_MAX_BLOCK_SIZE ((size_t)(1 << 30) - sizeof(CFE_ES_CDS_BlockHeader_t)) + +/* +** Type Definitions +*/ + +/** + * The structure cached in RAM for each block within the CDS non-volatile memory + * This has the basic runtime info without having to go to CDS. + */ +typedef struct +{ + /* + * Note that the block size and offset stored here are for the + * total block size. The CDS code adds is own extra metadata + * which has a CRC, and therefore the actual user data size is + * less than this. + */ + CFE_ES_CDSHandle_t BlockID; /**< Abstract ID associated with this CDS block */ + size_t BlockOffset; /**< Start offset of the block in CDS memory */ + size_t BlockSize; /**< Size, in bytes, of the CDS memory block */ + char Name[CFE_MISSION_ES_CDS_MAX_FULL_NAME_LEN]; + bool Table; /**< \brief Flag that indicates whether CDS contains a Critical Table */ +} CFE_ES_CDS_RegRec_t; + +typedef struct CFE_ES_CDSBlockHeader +{ + uint32 Crc; /**< CRC of content */ +} CFE_ES_CDS_BlockHeader_t; + +/* + * A generic buffer to hold the various objects that need + * to be cached in RAM from the CDS non-volatile storage. + */ +typedef union CFE_ES_CDS_AccessCacheData +{ + char Sig[CFE_ES_CDS_SIGNATURE_LEN]; /**< A signature field (beginning or end) */ + uint32 RegistrySize; /**< Registry Size Field */ + uint32 Zero[4]; /**< Used when clearing CDS content */ + CFE_ES_GenPoolBD_t Desc; /**< A generic block descriptor */ + CFE_ES_CDS_BlockHeader_t BlockHeader; /**< A user block header */ + CFE_ES_CDS_RegRec_t RegEntry; /**< A registry entry */ +} CFE_ES_CDS_AccessCacheData_t; + +typedef struct CFE_ES_CDS_AccessCache +{ + CFE_ES_CDS_AccessCacheData_t Data; /**< Cached data (varies in size) */ + size_t Offset; /**< The offset where Data is cached from */ + size_t Size; /**< The size of cached Data */ + int32 AccessStatus; /**< The PSP status of the last read/write from CDS memory */ +} CFE_ES_CDS_AccessCache_t; + +/** + * Instance data associated with a CDS + * + * Currently there is just one global CDS instance (i.e. a singleton) + * stored in the CFE_ES_Global structure. + */ +typedef struct +{ + /* + * The generic pool structure + * This must be the first entry in this structure. + */ + CFE_ES_GenPoolRecord_t Pool; + + /* + * Cache of last accessed data block + * Because CDS memory is not memory mapped, this serves + * as temporary holding location for data being actively accessed. + */ + CFE_ES_CDS_AccessCache_t Cache; + + osal_id_t GenMutex; /**< \brief Mutex that controls access to CDS and registry */ + size_t TotalSize; /**< \brief Total size of the CDS as reported by BSP */ + size_t DataSize; /**< \brief Size of actual user data pool */ + CFE_ResourceId_t LastCDSBlockId; /**< \brief Last issued CDS block ID */ + CFE_ES_CDS_RegRec_t Registry[CFE_PLATFORM_ES_CDS_MAX_NUM_ENTRIES]; /**< \brief CDS Registry (Local Copy) */ +} CFE_ES_CDS_Instance_t; + +/* + * structs representing the intended layout of data + * in the actual CDS/PSP-provided non-volatile memory + * + * All blocks should be multiples of uint32 + * + * NOTE: these aren't necessarily instantiated in RAM, + * just in CDS. Mainly interested in the size of these + * elements, and offset of the various members within. + */ +typedef struct CFE_ES_CDS_PersistentHeader +{ + CDS_RESERVE_SPACE(SignatureBegin, CFE_ES_CDS_SIGNATURE_LEN); + CDS_RESERVE_SPACE(RegistrySize, sizeof(uint32)); + CDS_RESERVE_SPACE(RegistryContent, CFE_PLATFORM_ES_CDS_MAX_NUM_ENTRIES * sizeof(CFE_ES_CDS_RegRec_t)); +} CFE_ES_CDS_PersistentHeader_t; + +typedef struct CFE_ES_CDS_PersistentTrailer +{ + CDS_RESERVE_SPACE(SignatureEnd, CFE_ES_CDS_SIGNATURE_LEN); +} CFE_ES_CDS_PersistentTrailer_t; + +/*****************************************************************************/ +/* +** Function prototypes +*/ + +/** + * @brief Fetch data from the non-volatile storage and store in RAM cache + * + * This fetches a data segment from the PSP and loads it into the + * local CDS cache buffer. The content can be accessed via the + * "Data" member inside the cache structure. + * + * Only one thread can use CDS cache at a given time, so the CDS access + * control mutex must be obtained before calling this function. + * + * @param[inout] Cache the global CDS cache buffer + * @param[in] Offset the CDS offset to fetch + * @param[in] Size the CDS data size to fetch + * @returns #CFE_SUCCESS on success, or appropriate error code. + */ +int32 CFE_ES_CDS_CacheFetch(CFE_ES_CDS_AccessCache_t *Cache, size_t Offset, size_t Size); + +/** + * @brief Write data from the RAM cache back to non-volatile storage + * + * This stores a data segment from the cache into the PSP for + * permanent storage. Data should be loaded into the cache + * prior to invoking this function, either via CFE_ES_CDS_CacheFetch() + * or CFE_ES_CDS_CachePreload(). + * + * Only one thread can use CDS cache at a given time, so the CDS access + * control mutex must be obtained before calling this function. + * + * @param[inout] Cache the global CDS cache buffer + * @returns #CFE_SUCCESS on success, or appropriate error code. + */ +int32 CFE_ES_CDS_CacheFlush(CFE_ES_CDS_AccessCache_t *Cache); + +/** + * @brief Preload the cache data from a local buffer + * + * This loads the CDS cache directly from a provided object/buffer to + * prepare for writing to PSP. The data can then be committed to PSP + * at a later time using CFE_ES_CDS_CacheFlush(). + * + * If Source is NULL, then the cache data will be initialized to zero. + * + * If Source refers to the cache buffer, then no copying will take place, because + * source and destination are the same. No copy is performed, and the data will be + * unchanged. In this mode only the size and offset are updated. + * + * Only one thread can use CDS cache at a given time, so the CDS access + * control mutex must be obtained before calling this function. + * + * @param[inout] Cache the global CDS cache buffer + * @param[in] Source the local object to load into cache + * @param[in] Offset the CDS offset to fetch + * @param[in] Size the CDS data size to fetch + * @returns #CFE_SUCCESS on success, or appropriate error code. + */ +int32 CFE_ES_CDS_CachePreload(CFE_ES_CDS_AccessCache_t *Cache, const void *Source, size_t Offset, size_t Size); + +/** + * @brief Get the registry array index correlating with a CDS block ID + * + * Calculates the array position/index of the CDS registry entry for + * the given block ID. + * + * @param[in] BlockID the ID/handle of the CDS block to retrieve + * @param[out] Idx Output buffer to store the index + * @returns #CFE_SUCCESS if conversion successful. @copydoc CFE_SUCCESS + * #CFE_ES_ERR_RESOURCEID_NOT_VALID if block ID is outside valid range + */ +int32 CFE_ES_CDSHandle_ToIndex(CFE_ES_CDSHandle_t BlockID, uint32 *Idx); + +/** + * @brief Get a registry record within the CDS, given a block ID/handle + * + * Retrieves a pointer to the registry record associated with a CDS block ID/handle + * Returns NULL if the handle is outside the valid range + * + * @note This only does the lookup, it does not validate that the handle + * actually matches the returned record. The caller should lock the CDS and + * confirm that the record is a match to the expected ID before using it. + * + * @param[in] BlockID the ID/handle of the CDS block to retrieve + * @returns Pointer to registry record, or NULL if ID/handle invalid. + */ +CFE_ES_CDS_RegRec_t *CFE_ES_LocateCDSBlockRecordByID(CFE_ES_CDSHandle_t BlockID); + +/** + * @brief Check if a Memory Pool record is in use or free/empty + * + * This routine checks if the Pool table entry is in use or if it is free + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * @param[in] CDSBlockRecPtr pointer to Pool table entry + * @returns true if the entry is in use/configured, or false if it is free/empty + */ +static inline bool CFE_ES_CDSBlockRecordIsUsed(const CFE_ES_CDS_RegRec_t *CDSBlockRecPtr) +{ + return CFE_RESOURCEID_TEST_DEFINED(CDSBlockRecPtr->BlockID); +} + +/** + * @brief Get the ID value from a Memory Pool table entry + * + * This routine converts the table entry back to an abstract ID. + * + * @param[in] CDSBlockRecPtr pointer to Pool table entry + * @returns BlockID of entry + */ +static inline CFE_ES_CDSHandle_t CFE_ES_CDSBlockRecordGetID(const CFE_ES_CDS_RegRec_t *CDSBlockRecPtr) +{ + return (CDSBlockRecPtr->BlockID); +} + +/** + * @brief Marks a Memory Pool table entry as used (not free) + * + * This sets the internal field(s) within this entry, and marks + * it as being associated with the given Pool ID. + * + * @param[in] CDSBlockRecPtr pointer to Pool table entry + * @param[in] PendingId the Pool ID of this entry + */ +static inline void CFE_ES_CDSBlockRecordSetUsed(CFE_ES_CDS_RegRec_t *CDSBlockRecPtr, CFE_ResourceId_t PendingId) +{ + CDSBlockRecPtr->BlockID = CFE_ES_CDSHANDLE_C(PendingId); +} + +/** + * @brief Set a Memory Pool record table entry free (not used) + * + * This clears the internal field(s) within this entry, and allows the + * memory to be re-used in the future. + * + * @param[in] CDSBlockRecPtr pointer to Pool table entry + */ +static inline void CFE_ES_CDSBlockRecordSetFree(CFE_ES_CDS_RegRec_t *CDSBlockRecPtr) +{ + CDSBlockRecPtr->BlockID = CFE_ES_CDS_BAD_HANDLE; +} + +/** + * @brief Check if a CDS block record is a match for the given BlockID + * + * This routine confirms that the previously-located record is valid + * and matches the expected block ID. + * + * As this dereferences fields within the record, CDS access mutex must be + * locked prior to invoking this function. + * + * @param[in] CDSBlockRecPtr pointer to registry table entry + * @param[in] BlockID expected block ID + * @returns true if the entry matches the given block ID + */ +static inline bool CFE_ES_CDSBlockRecordIsMatch(const CFE_ES_CDS_RegRec_t *CDSBlockRecPtr, CFE_ES_CDSHandle_t BlockID) +{ + return (CDSBlockRecPtr != NULL && CFE_RESOURCEID_TEST_EQUAL(CDSBlockRecPtr->BlockID, BlockID)); +} + +/** + * @brief Gets the data size from a given registry record + * + * This computes the usable data size of the CDS registry entry + * + * As this dereferences fields within the record, CDS access mutex must be + * locked prior to invoking this function. + * + * @note CDS entries include an extra header in addition to the data, + * which contains error checking information. Therefore the usable data + * size is less than the raw block size. + * + * @param[in] CDSBlockRecPtr pointer to registry table entry + * @returns Usable size of the CDS + */ +static inline size_t CFE_ES_CDSBlockRecordGetUserSize(const CFE_ES_CDS_RegRec_t *CDSBlockRecPtr) +{ + return (CDSBlockRecPtr->BlockSize - sizeof(CFE_ES_CDS_BlockHeader_t)); +} + +/** + * @brief Check if a CDS Block ID table slot is used + * + * Checks if a table slot is available for a potential new ID + * This is a helper function intended to be used with + * CFE_ResourceId_FindNext() for allocating new IDs + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * @param[in] CheckId pending/candidate Block ID to check + * @returns true if the table slot for the ID is occupied, false if available + */ +bool CFE_ES_CheckCDSHandleSlotUsed(CFE_ResourceId_t CheckId); + +/*****************************************************************************/ +/** +** \brief Initializes CDS data constructs +** +** \par Description +** Locates and validates any pre-existing CDS memory or initializes the +** memory as a fresh CDS. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \par SysLog Messages +** +** \return None +** +******************************************************************************/ +int32 CFE_ES_CDS_EarlyInit(void); + +/*****************************************************************************/ +/** +** \brief Determines whether a CDS currently exists +** +** \par Description +** Reads a set of bytes from the beginning and end of the CDS memory +** area and determines if a fixed pattern is present, thus determining +** whether the CDS still likely contains valid data or not. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \return #CFE_SUCCESS \copydoc CFE_SUCCESS +** \return #CFE_ES_CDS_INVALID \copydoc CFE_ES_CDS_INVALID +** \return Any of the return values from #CFE_PSP_ReadFromCDS +** +******************************************************************************/ +int32 CFE_ES_ValidateCDS(void); + +/*****************************************************************************/ +/** +** \brief Initializes the CDS Registry +** +** \par Description +** Initializes the data structure used to keep track of CDS blocks and +** who they belong to. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \retval #CFE_SUCCESS \copydoc CFE_SUCCESS +** +******************************************************************************/ +int32 CFE_ES_InitCDSRegistry(void); + +/*****************************************************************************/ +/** +** \brief Rebuilds memory pool for CDS and recovers existing registry +** +** \par Description +** Scans memory for existing CDS and initializes memory pool and registry +** settings accordingly +** +** \par Assumptions, External Events, and Notes: +** -# Assumes the validity of the CDS has already been determined +** +** \return #CFE_SUCCESS \copydoc CFE_SUCCESS +** \return Any of the return values from #CFE_PSP_ReadFromCDS +** +******************************************************************************/ +int32 CFE_ES_RebuildCDS(void); + +/*****************************************************************************/ +/** +** \brief Copies the local version of the CDS Registry to the actual CDS +** +** \par Description +** Copies the local working copy of the CDS Registry to the CDS. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \return #CFE_SUCCESS \copydoc CFE_SUCCESS +** \return Any of the return values from #CFE_PSP_WriteToCDS +** +******************************************************************************/ +int32 CFE_ES_UpdateCDSRegistry(void); + +/*****************************************************************************/ +/** +** \brief Creates a Full CDS name from application name and CDS name +** +** \par Description +** Takes a given CDS Name and combines it with the calling +** Application's name to make a processor specific name of the +** form: "AppName.CDSName" +** +** \par Assumptions, External Events, and Notes: +** Note: AppName portion will be truncated to OS_MAX_API_NAME. +** +** \param[in, out] FullCDSName pointer to character buffer of #CFE_MISSION_ES_CDS_MAX_FULL_NAME_LEN size +** that will be filled with the processor specific CDS Name. *FullCDSName is the processor +** specific CDS Name of the form "AppName.CDSName". +** +** \param[in] CDSName pointer to character string containing the Application's local name for +** the CDS. +** +** \param[in] ThisAppId the Application ID of the Application making the call. +** +******************************************************************************/ +void CFE_ES_FormCDSName(char *FullCDSName, const char *CDSName, CFE_ES_AppId_t ThisAppId); + +/*****************************************************************************/ +/** +** \brief Returns the Registry Record for the specified CDS Name +** +** \par Description +** Locates given CDS Name in the CDS Registry and +** returns the appropriate Registry Index. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[in] CDSName - Pointer to character string containing complete +** CDS Name (of the format "AppName.CDSName"). +** +** \retval NULL if not found, Non null entry pointer on success +** +******************************************************************************/ +CFE_ES_CDS_RegRec_t *CFE_ES_LocateCDSBlockRecordByName(const char *CDSName); + +/*****************************************************************************/ +/** +** \brief Locks access to the CDS +** +** \par Description +** Locks the CDS to prevent multiple tasks/threads +** from modifying it at once. +** +** This lock covers both the registry and the data access. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \retval #CFE_SUCCESS \copydoc CFE_SUCCESS +******************************************************************************/ +int32 CFE_ES_LockCDS(void); + +/*****************************************************************************/ +/** +** \brief Unlocks access to the CDS +** +** \par Description +** Unlocks CDS to allow other tasks/threads to +** modify the CDS contents. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \retval #CFE_SUCCESS \copydoc CFE_SUCCESS +** +******************************************************************************/ +int32 CFE_ES_UnlockCDS(void); + +/*****************************************************************************/ +/** +** \brief Rebuilds memory pool for CDS and recovers existing registry +** +** \par Description +** Scans memory for existing CDS and initializes memory pool and registry +** settings accordingly +** +** \par Assumptions, External Events, and Notes: +** -# Assumes the validity of the CDS has already been determined +** +** \return #CFE_SUCCESS \copydoc CFE_SUCCESS +** \return Any of the return values from #CFE_PSP_ReadFromCDS +** +******************************************************************************/ +int32 CFE_ES_RebuildCDS(void); + +/*****************************************************************************/ +/** +** \brief Initializes the CDS Registry +** +** \par Description +** Initializes the data structure used to keep track of CDS blocks and +** who they belong to. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \retval #CFE_SUCCESS \copydoc CFE_SUCCESS +** +******************************************************************************/ +int32 CFE_ES_InitCDSRegistry(void); + +/*****************************************************************************/ +/** +** \brief Determines whether a CDS currently exists +** +** \par Description +** Reads a set of bytes from the beginning and end of the CDS memory +** area and determines if a fixed pattern is present, thus determining +** whether the CDS still likely contains valid data or not. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \return #CFE_SUCCESS \copydoc CFE_SUCCESS +** \return #CFE_ES_CDS_INVALID \copydoc CFE_ES_CDS_INVALID +** \return Any of the return values from #CFE_PSP_ReadFromCDS +** +******************************************************************************/ +int32 CFE_ES_ValidateCDS(void); + +/*****************************************************************************/ +/** +** \brief Clears the contents of the CDS +** +** \par Description +** Writes zeros to the entire CDS storage area +** +** This prevents any stale data that may exist in the +** memory area from being potentially interpreted as valid +** +** \par Assumptions, External Events, and Notes: +** None +** +** \return #CFE_SUCCESS \copydoc CFE_SUCCESS +** \return Any of the return values from #CFE_ES_CDS_CacheFlush +** +******************************************************************************/ +int32 CFE_ES_ClearCDS(void); + +/*****************************************************************************/ +/** +** \brief Initializes the signatures of the CDS area +** +** \par Description +** Stores a fixed pattern at the beginning and end of the CDS memory +** to tag it for future verification following a reset. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \return #CFE_SUCCESS \copydoc CFE_SUCCESS +** \return Any of the return values from #CFE_ES_CDS_CacheFlush +** +******************************************************************************/ +int32 CFE_ES_InitCDSSignatures(void); + +#endif /* CFE_ES_CDS_H */ diff --git a/modules/es/fsw/src/cfe_es_cds_mempool.c b/modules/es/fsw/src/cfe_es_cds_mempool.c new file mode 100644 index 000000000..4f02386fc --- /dev/null +++ b/modules/es/fsw/src/cfe_es_cds_mempool.c @@ -0,0 +1,371 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/* +** File: +** cfe_es_cds_mempool.c +** +** Purpose: +** Set of services for management of the CDS discrete sized memory pools. +** +** References: +** Flight Software Branch C Coding Standard Version 1.0a +** cFE Flight Software Application Developers Guide +** +*/ + +/* +** Includes +*/ +#include +#include +#include + +#include "cfe_es_module_all.h" + +/*****************************************************************************/ +/* +** Type Definitions +*/ + +/*****************************************************************************/ +/* +** File Global Data +*/ + +const size_t CFE_ES_CDSMemPoolDefSize[CFE_ES_CDS_NUM_BLOCK_SIZES] = { + CFE_PLATFORM_ES_CDS_MAX_BLOCK_SIZE, CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_16, CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_15, + CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_14, CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_13, CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_12, + CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_11, CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_10, CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_09, + CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_08, CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_07, CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_06, + CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_05, CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_04, CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_03, + CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_02, CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_01}; + +/*****************************************************************************/ +/* +** Functions +*/ + +/* +** CFE_ES_CDS_PoolRetrieve will obtain a block descriptor from CDS storage. +** +** This is a bridge between the generic pool implementation and the CDS cache. +*/ +int32 CFE_ES_CDS_PoolRetrieve(CFE_ES_GenPoolRecord_t *GenPoolRecPtr, size_t Offset, CFE_ES_GenPoolBD_t **BdPtr) +{ + CFE_ES_CDS_Instance_t *CDS = (CFE_ES_CDS_Instance_t *)GenPoolRecPtr; + + *BdPtr = &CDS->Cache.Data.Desc; + + return CFE_ES_CDS_CacheFetch(&CDS->Cache, Offset, sizeof(CFE_ES_GenPoolBD_t)); +} + +/* +** CFE_ES_CDS_PoolCommit will write a block descriptor to CDS storage. +** +** This is a bridge between the generic pool implementation and the CDS cache. +*/ +int32 CFE_ES_CDS_PoolCommit(CFE_ES_GenPoolRecord_t *GenPoolRecPtr, size_t Offset, const CFE_ES_GenPoolBD_t *BdPtr) +{ + CFE_ES_CDS_Instance_t *CDS = (CFE_ES_CDS_Instance_t *)GenPoolRecPtr; + + CFE_ES_CDS_CachePreload(&CDS->Cache, BdPtr, Offset, sizeof(CFE_ES_GenPoolBD_t)); + + return CFE_ES_CDS_CacheFlush(&CDS->Cache); +} + +/* +** CFE_ES_CreateCDSPool will initialize a pre-allocated memory pool. +** +** NOTE: +** This function is only ever called during "Early Init" phase, +** where it is not possible to have contention writing into the syslog. +** Therefore the use of CFE_ES_SysLogWrite_Unsync() is acceptable +*/ +int32 CFE_ES_CreateCDSPool(size_t CDSPoolSize, size_t StartOffset) +{ + CFE_ES_CDS_Instance_t *CDS = &CFE_ES_Global.CDSVars; + int32 Status; + size_t SizeCheck; + size_t ActualSize; + + SizeCheck = CFE_ES_GenPoolCalcMinSize(CFE_ES_CDS_NUM_BLOCK_SIZES, CFE_ES_CDSMemPoolDefSize, 1); + ActualSize = CDSPoolSize; + + if (ActualSize < SizeCheck) + { + /* Must be able make Pool verification, block descriptor and at least one of the smallest blocks */ + CFE_ES_SysLogWrite_Unsync("CFE_ES:CreateCDSPool-Pool size(%lu) too small for one CDS Block, need >=%lu\n", + (unsigned long)ActualSize, (unsigned long)SizeCheck); + return CFE_ES_CDS_INVALID_SIZE; + } + + Status = CFE_ES_GenPoolInitialize(&CDS->Pool, StartOffset, /* starting offset */ + ActualSize, /* total size */ + 4, /* alignment */ + CFE_ES_CDS_NUM_BLOCK_SIZES, CFE_ES_CDSMemPoolDefSize, CFE_ES_CDS_PoolRetrieve, + CFE_ES_CDS_PoolCommit); + + return Status; +} + +/* +** Function: +** CFE_ES_RebuildCDSPool +** +** Purpose: +** +** NOTE: +** This function is only ever called during "Early Init" phase, +** where it is not possible to have contention writing into the syslog. +** Therefore the use of CFE_ES_SysLogWrite_Unsync() is acceptable +*/ +int32 CFE_ES_RebuildCDSPool(size_t CDSPoolSize, size_t StartOffset) +{ + CFE_ES_CDS_Instance_t *CDS = &CFE_ES_Global.CDSVars; + int32 Status; + + /* + * Start by creating the pool in a clean state, as it would be in a non-rebuild. + */ + Status = CFE_ES_CreateCDSPool(CDSPoolSize, StartOffset); + if (Status != CFE_SUCCESS) + { + return Status; + } + + /* Now walk through the CDS memory and attempt to recover existing CDS blocks */ + Status = CFE_ES_GenPoolRebuild(&CDS->Pool); + + if (Status != CFE_SUCCESS) + { + CFE_ES_SysLogWrite_Unsync("CFE_ES:RebuildCDS-Err rebuilding CDS (Stat=0x%08x)\n", (unsigned int)Status); + Status = CFE_ES_CDS_ACCESS_ERROR; + } + + return Status; +} + +/* +** Function: +** CFE_ES_CDSBlockWrite +** +** Purpose: +** +*/ +int32 CFE_ES_CDSBlockWrite(CFE_ES_CDSHandle_t Handle, const void *DataToWrite) +{ + CFE_ES_CDS_Instance_t *CDS = &CFE_ES_Global.CDSVars; + char LogMessage[CFE_ES_MAX_SYSLOG_MSG_SIZE]; + int32 Status; + size_t BlockSize; + size_t UserDataSize; + size_t UserDataOffset; + CFE_ES_CDS_RegRec_t * CDSRegRecPtr; + + /* Ensure the the log message is an empty string in case it is never written to */ + LogMessage[0] = 0; + + CDSRegRecPtr = CFE_ES_LocateCDSBlockRecordByID(Handle); + + /* + * A CDS block ID must be accessed by only one thread at a time. + * Checking the validity of the block requires access to the registry. + */ + CFE_ES_LockCDS(); + + if (CFE_ES_CDSBlockRecordIsMatch(CDSRegRecPtr, Handle)) + { + /* + * Getting the buffer size via this function retrieves it from the + * internal descriptor, and validates the descriptor as part of the operation. + * This should always agree with the size in the registry for this block. + */ + Status = CFE_ES_GenPoolGetBlockSize(&CDS->Pool, &BlockSize, CDSRegRecPtr->BlockOffset); + if (Status != CFE_SUCCESS) + { + CFE_ES_SysLog_snprintf(LogMessage, sizeof(LogMessage), + "CFE_ES:CDSBlkWrite-Invalid Handle or Block Descriptor.\n"); + } + else if (BlockSize <= sizeof(CFE_ES_CDS_BlockHeader_t) || BlockSize != CDSRegRecPtr->BlockSize) + { + CFE_ES_SysLog_snprintf(LogMessage, sizeof(LogMessage), + "CFE_ES:CDSBlkWrite-Block size %lu invalid, expected %lu\n", + (unsigned long)BlockSize, (unsigned long)CDSRegRecPtr->BlockSize); + Status = CFE_ES_CDS_INVALID_SIZE; + } + else + { + UserDataSize = CDSRegRecPtr->BlockSize; + UserDataSize -= sizeof(CFE_ES_CDS_BlockHeader_t); + UserDataOffset = CDSRegRecPtr->BlockOffset; + UserDataOffset += sizeof(CFE_ES_CDS_BlockHeader_t); + + CDS->Cache.Data.BlockHeader.Crc = + CFE_ES_CalculateCRC(DataToWrite, UserDataSize, 0, CFE_MISSION_ES_DEFAULT_CRC); + CDS->Cache.Offset = CDSRegRecPtr->BlockOffset; + CDS->Cache.Size = sizeof(CFE_ES_CDS_BlockHeader_t); + + /* Write the new block descriptor for the data coming from the Application */ + Status = CFE_ES_CDS_CacheFlush(&CDS->Cache); + if (Status != CFE_SUCCESS) + { + CFE_ES_SysLog_snprintf( + LogMessage, sizeof(LogMessage), + "CFE_ES:CDSBlkWrite-Err writing header data to CDS (Stat=0x%08x) @Offset=0x%08lx\n", + (unsigned int)CDS->Cache.AccessStatus, (unsigned long)CDSRegRecPtr->BlockOffset); + } + else + { + Status = CFE_PSP_WriteToCDS(DataToWrite, UserDataOffset, UserDataSize); + if (Status != CFE_PSP_SUCCESS) + { + CFE_ES_SysLog_snprintf( + LogMessage, sizeof(LogMessage), + "CFE_ES:CDSBlkWrite-Err writing user data to CDS (Stat=0x%08x) @Offset=0x%08lx\n", + (unsigned int)Status, (unsigned long)UserDataOffset); + } + } + } + } + else + { + Status = CFE_ES_ERR_RESOURCEID_NOT_VALID; + } + + CFE_ES_UnlockCDS(); + + /* Do the actual syslog if something went wrong */ + if (LogMessage[0] != 0) + { + CFE_ES_SYSLOG_APPEND(LogMessage); + } + + return Status; +} + +/* +** Function: +** CFE_ES_CDSBlockRead +** +** Purpose: +** +*/ +int32 CFE_ES_CDSBlockRead(void *DataRead, CFE_ES_CDSHandle_t Handle) +{ + CFE_ES_CDS_Instance_t *CDS = &CFE_ES_Global.CDSVars; + char LogMessage[CFE_ES_MAX_SYSLOG_MSG_SIZE]; + int32 Status; + uint32 CrcOfCDSData; + size_t BlockSize; + size_t UserDataSize; + size_t UserDataOffset; + CFE_ES_CDS_RegRec_t * CDSRegRecPtr; + + /* Validate the handle before doing anything */ + LogMessage[0] = 0; + + CDSRegRecPtr = CFE_ES_LocateCDSBlockRecordByID(Handle); + + /* + * A CDS block ID must be accessed by only one thread at a time. + * Checking the validity of the block requires access to the registry. + */ + CFE_ES_LockCDS(); + + if (CFE_ES_CDSBlockRecordIsMatch(CDSRegRecPtr, Handle)) + { + /* + * Getting the buffer size via this function retrieves it from the + * internal descriptor, and validates the descriptor as part of the operation. + * This should always agree with the size in the registry for this block. + */ + Status = CFE_ES_GenPoolGetBlockSize(&CDS->Pool, &BlockSize, CDSRegRecPtr->BlockOffset); + if (Status == CFE_SUCCESS) + { + if (BlockSize <= sizeof(CFE_ES_CDS_BlockHeader_t) || BlockSize != CDSRegRecPtr->BlockSize) + { + Status = CFE_ES_CDS_INVALID_SIZE; + } + else + { + UserDataSize = CDSRegRecPtr->BlockSize; + UserDataSize -= sizeof(CFE_ES_CDS_BlockHeader_t); + UserDataOffset = CDSRegRecPtr->BlockOffset; + UserDataOffset += sizeof(CFE_ES_CDS_BlockHeader_t); + + /* Read the header */ + Status = + CFE_ES_CDS_CacheFetch(&CDS->Cache, CDSRegRecPtr->BlockOffset, sizeof(CFE_ES_CDS_BlockHeader_t)); + + if (Status == CFE_SUCCESS) + { + /* Read the data block */ + Status = CFE_PSP_ReadFromCDS(DataRead, UserDataOffset, UserDataSize); + if (Status == CFE_PSP_SUCCESS) + { + /* Compute the CRC for the data read from the CDS and determine if the data is still valid */ + CrcOfCDSData = CFE_ES_CalculateCRC(DataRead, UserDataSize, 0, CFE_MISSION_ES_DEFAULT_CRC); + + /* If the CRCs do not match, report an error */ + if (CrcOfCDSData != CDS->Cache.Data.BlockHeader.Crc) + { + Status = CFE_ES_CDS_BLOCK_CRC_ERR; + } + else + { + Status = CFE_SUCCESS; + } + } + } + } + } + } + else + { + Status = CFE_ES_ERR_RESOURCEID_NOT_VALID; + } + + CFE_ES_UnlockCDS(); + + /* Do the actual syslog if something went wrong */ + if (LogMessage[0] != 0) + { + CFE_ES_SYSLOG_APPEND(LogMessage); + } + + return Status; +} + +/* +** Function: +** CFE_ES_CDSReqdMinSize +** +** Purpose: +** +*/ +size_t CFE_ES_CDSReqdMinSize(uint32 MaxNumBlocksToSupport) +{ + size_t ReqSize; + + ReqSize = CFE_ES_GenPoolCalcMinSize(CFE_ES_CDS_NUM_BLOCK_SIZES, CFE_ES_CDSMemPoolDefSize, MaxNumBlocksToSupport); + + return ReqSize; +} diff --git a/modules/es/fsw/src/cfe_es_cds_mempool.h b/modules/es/fsw/src/cfe_es_cds_mempool.h new file mode 100644 index 000000000..696fca5d1 --- /dev/null +++ b/modules/es/fsw/src/cfe_es_cds_mempool.h @@ -0,0 +1,78 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/** + * @file + * + * Purpose: + * This file contains the Internal interface for the cFE Critical Data Store + * memory pool functions. + * + * References: + * Flight Software Branch C Coding Standard Version 1.0a + * cFE Flight Software Application Developers Guide + * + * Notes: + * + */ + +#ifndef CFE_ES_CDS_MEMPOOL_H +#define CFE_ES_CDS_MEMPOOL_H + +/* +** Include Files +*/ +#include "cfe_es_cds.h" + +/* +** Macro Definitions +*/ +#define CFE_ES_CDS_NUM_BLOCK_SIZES 17 + +/*****************************************************************************/ +/* +** Function prototypes +*/ + +/*****************************************************************************/ +/** +** \brief Creates a CDS memory pool from scratch +** +** \par Description +** Creates a memory pool of the specified size starting at the specified +** offset into the CDS memory. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \return #CFE_SUCCESS \copydoc CFE_SUCCESS +** +******************************************************************************/ +int32 CFE_ES_CreateCDSPool(size_t CDSPoolSize, size_t StartOffset); + +int32 CFE_ES_RebuildCDSPool(size_t CDSPoolSize, size_t StartOffset); + +int32 CFE_ES_CDSBlockWrite(CFE_ES_CDSHandle_t Handle, const void *DataToWrite); + +int32 CFE_ES_CDSBlockRead(void *DataRead, CFE_ES_CDSHandle_t Handle); + +size_t CFE_ES_CDSReqdMinSize(uint32 MaxNumBlocksToSupport); + +#endif /* CFE_ES_CDS_MEMPOOL_H */ diff --git a/modules/es/fsw/src/cfe_es_erlog.c b/modules/es/fsw/src/cfe_es_erlog.c new file mode 100644 index 000000000..7370df0bd --- /dev/null +++ b/modules/es/fsw/src/cfe_es_erlog.c @@ -0,0 +1,430 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/* +** File: +** cfe_es_erlog.c +** +** Purpose: +** This file implements the cFE Executive Services Exception and Reset Log functions. +** +** References: +** Flight Software Branch C Coding Standard Version 1.0a +** cFE Flight Software Application Developers Guide +** +** Notes: +** +** Modification History: +** +*/ + +/* +** Required header files. +*/ +#include "cfe_es_module_all.h" + +#include +#include +#include + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* +** Function: CFE_ES_WriteToERLogWithContext +** +** Purpose: Create an entry in the ES Exception and Reset Log. +** This log API accepts extra context information (AppID and ContextID) +** and is used when the app/task invoking this API is not the same app +** as where the event occurred. +** +*/ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +int32 CFE_ES_WriteToERLogWithContext(CFE_ES_LogEntryType_Enum_t EntryType, uint32 ResetType, uint32 ResetSubtype, + const char *Description, CFE_ES_AppId_t AppId, uint32 PspContextId) +{ + uint32 LogIdx; + CFE_ES_ERLog_MetaData_t *EntryPtr; + CFE_TIME_SysTime_t PendingTime; + + /* + * Snapshot the time before locking (different subsystem) + */ + PendingTime = CFE_TIME_GetTime(); + + /* + * Ensure that description string is not NULL. + */ + if (Description == NULL) + { + Description = "No Description String Given."; + } + + /* + * This routine needs to lock in case it is called + * from concurrent threads + */ + CFE_ES_LockSharedData(__func__, __LINE__); + + /* + ** Try to clean up an invalid ER log index variable. + */ + if (CFE_ES_Global.ResetDataPtr->ERLogIndex >= CFE_PLATFORM_ES_ER_LOG_ENTRIES) + { + CFE_ES_Global.ResetDataPtr->ERLogIndex = 0; + } + LogIdx = CFE_ES_Global.ResetDataPtr->ERLogIndex; + + /* + ** Now that the Local Index variable is set, increment the index for the next entry. + */ + CFE_ES_Global.ResetDataPtr->ERLogIndex++; + if (CFE_ES_Global.ResetDataPtr->ERLogIndex >= CFE_PLATFORM_ES_ER_LOG_ENTRIES) + { + CFE_ES_Global.ResetDataPtr->ERLogIndex = 0; + } + + /* + ** Clear out the log entry we are about to use. + */ + EntryPtr = &CFE_ES_Global.ResetDataPtr->ERLog[LogIdx]; + memset(EntryPtr, 0, sizeof(*EntryPtr)); + + /* + ** Fill out the log fields + */ + EntryPtr->BaseInfo.LogEntryType = EntryType; + EntryPtr->BaseInfo.ResetType = ResetType; + EntryPtr->BaseInfo.ResetSubtype = ResetSubtype; + EntryPtr->BaseInfo.BootSource = CFE_ES_Global.ResetDataPtr->ResetVars.BootSource; + EntryPtr->BaseInfo.ProcessorResetCount = CFE_ES_Global.ResetDataPtr->ResetVars.ProcessorResetCount; + EntryPtr->BaseInfo.MaxProcessorResetCount = CFE_ES_Global.ResetDataPtr->ResetVars.MaxProcessorResetCount; + + /* + ** Copy the ES Reset variables to the log (before they are modified by the log entry). + */ + memcpy(&EntryPtr->BaseInfo.DebugVars, &CFE_ES_Global.DebugVars, sizeof(EntryPtr->BaseInfo.DebugVars)); + + /* + ** Time Stamp the log entry with the system time + */ + EntryPtr->BaseInfo.TimeCode = PendingTime; + + /* + ** Copy the Description string to the log. + */ + strncpy(EntryPtr->BaseInfo.Description, Description, sizeof(EntryPtr->BaseInfo.Description) - 1); + EntryPtr->BaseInfo.Description[sizeof(EntryPtr->BaseInfo.Description) - 1] = '\0'; + + /* + * Store the context info (if any) + */ + EntryPtr->AppID = AppId; + EntryPtr->PspContextId = PspContextId; + + /* + ** Increment the number of ER log entries made + */ + CFE_ES_Global.ResetDataPtr->ERLogEntries++; + + /* + * Shared data update is complete + */ + CFE_ES_UnlockSharedData(__func__, __LINE__); + + return (CFE_SUCCESS); + +} /* End of CFE_ES_WriteToERLogWithContext() */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* +** Function: CFE_ES_WriteToERLog +** +** Purpose: Create an entry in the ES Exception and Reset Log. +** This log API is simplified for cases which do not have a separate context +** +*/ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +int32 CFE_ES_WriteToERLog(CFE_ES_LogEntryType_Enum_t EntryType, uint32 ResetType, uint32 ResetSubtype, + const char *Description) +{ + /* passing 0xFFFFFFFF as the appid avoids confusion with actual appid 0 */ + return CFE_ES_WriteToERLogWithContext(EntryType, ResetType, ResetSubtype, Description, CFE_ES_APPID_UNDEFINED, + CFE_ES_ERLOG_NO_CONTEXT); + +} /* End of CFE_ES_WriteToERLog() */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* Function: CFE_ES_BackgroundERLogFileDataGetter() */ +/* */ +/* Purpose: */ +/* Gets a single record from exception & reset log to write to a file. */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +bool CFE_ES_BackgroundERLogFileDataGetter(void *Meta, uint32 RecordNum, void **Buffer, size_t *BufSize) +{ + CFE_ES_BackgroundLogDumpGlobal_t *BgFilePtr; + CFE_ES_ERLog_FileEntry_t * FileBufferPtr; + CFE_ES_ERLog_MetaData_t * EntryPtr; + int32 PspStatus; + + BgFilePtr = (CFE_ES_BackgroundLogDumpGlobal_t *)Meta; + FileBufferPtr = &BgFilePtr->EntryBuffer; + + if (RecordNum < CFE_PLATFORM_ES_ER_LOG_ENTRIES) + { + EntryPtr = &CFE_ES_Global.ResetDataPtr->ERLog[RecordNum]; + + /* First wipe the buffer before re-use */ + memset(FileBufferPtr, 0, sizeof(*FileBufferPtr)); + + CFE_ES_LockSharedData(__func__, __LINE__); + + /* The basic info comes directly from the ES log */ + FileBufferPtr->BaseInfo = EntryPtr->BaseInfo; + + /* + * The context info, if available, comes from the PSP. + * This returns the actual size of the context info, or <0 on error. + */ + PspStatus = CFE_PSP_Exception_CopyContext(EntryPtr->PspContextId, &FileBufferPtr->Context, + sizeof(FileBufferPtr->Context)); + if (PspStatus > 0) + { + FileBufferPtr->ContextSize = PspStatus; + } + else + { + /* + * errors here are OK - just means there is no context available. + * Record a size of 0 in the log file. + */ + FileBufferPtr->ContextSize = 0; + } + + CFE_ES_UnlockSharedData(__func__, __LINE__); + + /* + * Export data to caller for actual write + */ + *Buffer = FileBufferPtr; + *BufSize = sizeof(*FileBufferPtr); + } + else + { + *Buffer = NULL; + *BufSize = 0; + } + + /* Check for EOF (last entry) */ + return (RecordNum >= (CFE_PLATFORM_ES_ER_LOG_ENTRIES - 1)); +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* Function: CFE_ER_BackgroundERLogFileEventHandler() */ +/* */ +/* Purpose: */ +/* Report events during writing exception & reset log to a file. */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +void CFE_ES_BackgroundERLogFileEventHandler(void *Meta, CFE_FS_FileWriteEvent_t Event, int32 Status, uint32 RecordNum, + size_t BlockSize, size_t Position) +{ + CFE_ES_BackgroundLogDumpGlobal_t *BgFilePtr; + + BgFilePtr = (CFE_ES_BackgroundLogDumpGlobal_t *)Meta; + + /* + * Note that this runs in the context of ES background task (file writer background job) + * It does NOT run in the context of the CFE_TBL app task. + * + * Events should use CFE_EVS_SendEventWithAppID() rather than CFE_EVS_SendEvent() + * to get proper association with TBL task. + */ + switch (Event) + { + case CFE_FS_FileWriteEvent_COMPLETE: + CFE_EVS_SendEvent(CFE_ES_ERLOG2_EID, CFE_EVS_EventType_DEBUG, "%s written:Size=%lu", + BgFilePtr->FileWrite.FileName, (unsigned long)Position); + break; + + case CFE_FS_FileWriteEvent_HEADER_WRITE_ERROR: + case CFE_FS_FileWriteEvent_RECORD_WRITE_ERROR: + CFE_EVS_SendEvent(CFE_ES_FILEWRITE_ERR_EID, CFE_EVS_EventType_ERROR, + "File write,byte cnt err,file %s,request=%u,actual=%u", BgFilePtr->FileWrite.FileName, + (int)BlockSize, (int)Status); + break; + + case CFE_FS_FileWriteEvent_CREATE_ERROR: + CFE_EVS_SendEvent(CFE_ES_ERLOG2_ERR_EID, CFE_EVS_EventType_ERROR, "Error creating file %s, RC = %d", + BgFilePtr->FileWrite.FileName, (int)Status); + break; + + default: + /* unhandled event - ignore */ + break; + } +} + +/* +**--------------------------------------------------------------------------------------- +** Name: CFE_ES_RunExceptionScan +** +** Purpose: This function pools the PSP to check if any exceptions have been logged +** since the last background cycle. If an exception is present, retreive +** the details, add it to the ER log, and trigger the action (e.g. app restart). +**--------------------------------------------------------------------------------------- +*/ +bool CFE_ES_RunExceptionScan(uint32 ElapsedTime, void *Arg) +{ + int32 Status; + uint32 PspContextId; + char ReasonString[CFE_ES_ERLOG_DESCRIPTION_MAX_LENGTH]; + CFE_ES_TaskInfo_t EsTaskInfo; + osal_id_t ExceptionTaskID; + uint32 ResetType; + CFE_ES_LogEntryType_Enum_t LogType; + CFE_ES_AppRecord_t * AppRecPtr; + + if (CFE_PSP_Exception_GetCount() == 0) + { + /* no exceptions pending, nothing to do */ + return false; + } + + /* + * Note a reset type of 0 is not defined by the PSP - + * the real values are all nonzero + */ + ResetType = 0; + memset(&EsTaskInfo, 0, sizeof(EsTaskInfo)); + Status = CFE_PSP_Exception_GetSummary(&PspContextId, &ExceptionTaskID, ReasonString, sizeof(ReasonString)); + if (Status != CFE_PSP_SUCCESS) + { + /* reason string is not available - populate with something for the log */ + snprintf(ReasonString, sizeof(ReasonString), "Unknown - CFE_PSP_ExceptionGetSummary() error %ld", (long)Status); + PspContextId = 0; + ExceptionTaskID = OS_OBJECT_ID_UNDEFINED; + } /* end if */ + + /* + * Note that writes to the ES ER log actually do not get propagated to the debug console. + * so by writing to SysLog here it becomes visible in both places. + */ + CFE_ES_WriteToSysLog("ExceptionID 0x%lx in TaskID %lu: %s\n", (unsigned long)PspContextId, + OS_ObjectIdToInteger(ExceptionTaskID), ReasonString); + + /* + * If task ID is 0, this means it was a system level exception and + * not associated with a specific task. + * + * Otherwise, if it was related to a task, determine the associated AppID + * so the exception action can be checked. + */ + if (OS_ObjectIdDefined(ExceptionTaskID)) + { + Status = CFE_ES_GetTaskInfo(&EsTaskInfo, CFE_ES_TaskId_FromOSAL(ExceptionTaskID)); + + /* + * The App ID was found, now see if the ExceptionAction is set for a reset + */ + if (Status == CFE_SUCCESS) + { + AppRecPtr = CFE_ES_LocateAppRecordByID(EsTaskInfo.AppId); + CFE_ES_LockSharedData(__func__, __LINE__); + if (CFE_ES_AppRecordIsMatch(AppRecPtr, EsTaskInfo.AppId) && + AppRecPtr->StartParams.ExceptionAction == CFE_ES_ExceptionAction_RESTART_APP) + { + /* + * Log the Application reset + */ + ResetType = CFE_ES_APP_RESTART; + } + CFE_ES_UnlockSharedData(__func__, __LINE__); + } + } + + do + { + /* + * If no disposition is identified yet, then trigger a PSP reset. + * Need to determine if a processor or poweron reset is needed. + */ + if (ResetType == 0) + { + if (CFE_ES_Global.ResetDataPtr->ResetVars.ProcessorResetCount >= + CFE_ES_Global.ResetDataPtr->ResetVars.MaxProcessorResetCount) + { + CFE_ES_WriteToSysLog("Maximum Processor Reset count reached (%u)", + (unsigned int)CFE_ES_Global.ResetDataPtr->ResetVars.MaxProcessorResetCount); + + ResetType = CFE_PSP_RST_TYPE_POWERON; + } + else + { + CFE_ES_WriteToSysLog("Processor Reset count not reached (%u/%u)", + (unsigned int)CFE_ES_Global.ResetDataPtr->ResetVars.ProcessorResetCount, + (unsigned int)CFE_ES_Global.ResetDataPtr->ResetVars.MaxProcessorResetCount); + + /* + ** Update the reset variables + */ + CFE_ES_Global.ResetDataPtr->ResetVars.ProcessorResetCount++; + CFE_ES_Global.ResetDataPtr->ResetVars.ES_CausedReset = true; + + ResetType = CFE_PSP_RST_TYPE_PROCESSOR; + } + } + + if (ResetType == CFE_ES_APP_RESTART) + { + LogType = CFE_ES_LogEntryType_APPLICATION; + } + else + { + LogType = CFE_ES_LogEntryType_CORE; + } + + CFE_ES_WriteToERLogWithContext(LogType, ResetType, CFE_PSP_RST_SUBTYPE_EXCEPTION, ReasonString, + EsTaskInfo.AppId, PspContextId); + + if (ResetType == CFE_ES_APP_RESTART) + { + /* + * Restart the App. This call is just a request + * to ES, but the request could fail. If that happens, + * proceed to a processor reset. + */ + Status = CFE_ES_RestartApp(EsTaskInfo.AppId); + if (Status != CFE_SUCCESS) + { + ResetType = 0; + snprintf(ReasonString, sizeof(ReasonString), "App Restart Failed"); + } + } + else + { + /* normally this will not return */ + CFE_PSP_Restart(ResetType); + } + } while (ResetType == 0); + + return true; /* returning true because there was an exception to deal with */ +} + +/* end of file */ diff --git a/modules/es/fsw/src/cfe_es_generic_pool.c b/modules/es/fsw/src/cfe_es_generic_pool.c new file mode 100644 index 000000000..742c4da43 --- /dev/null +++ b/modules/es/fsw/src/cfe_es_generic_pool.c @@ -0,0 +1,676 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/* +** File: +** cfe_es_generic_pool.c +** +** Purpose: +** Set of services for management of discrete sized memory pools. +** +** References: +** Flight Software Branch C Coding Standard Version 1.0a +** cFE Flight Software Application Developers Guide +** +*/ + +/* +** Includes +*/ +#include "cfe_es_module_all.h" + +#include +#include +#include + +/*****************************************************************************/ +/* +** Functions +*/ + +/* +**--------------------------------------------------------------------------------------- +** Name: CFE_ES_GenPoolFindBucket +** +** Local Helper function to find the appropriate bucket given a requested block size +**--------------------------------------------------------------------------------------- +*/ +uint16 CFE_ES_GenPoolFindBucket(CFE_ES_GenPoolRecord_t *PoolRecPtr, size_t ReqSize) +{ + uint16 Index; + + for (Index = 0; Index < PoolRecPtr->NumBuckets; ++Index) + { + if (ReqSize <= PoolRecPtr->Buckets[Index].BlockSize) + { + /* it fits - stop here */ + break; + } + } + + /* + * Invert output such that if a bucket wasn't found, this + * will return 0. A valid bucket ID will be nonzero. + */ + return (PoolRecPtr->NumBuckets - Index); +} + +/* +**--------------------------------------------------------------------------------------- +** Name: CFE_ES_GenPoolGetBucketState +** +** Local Helper function to obtain the structure associated with a given bucket ID. +**--------------------------------------------------------------------------------------- +*/ +CFE_ES_GenPoolBucket_t *CFE_ES_GenPoolGetBucketState(CFE_ES_GenPoolRecord_t *PoolRecPtr, uint16 BucketId) +{ + uint16 Index; + + Index = PoolRecPtr->NumBuckets - BucketId; + if (Index >= PoolRecPtr->NumBuckets) + { + return NULL; + } + + return &PoolRecPtr->Buckets[Index]; +} + +/* +**--------------------------------------------------------------------------------------- +** Name: CFE_ES_GenPoolRecyclePoolBlock +** +** Local helper function to find and re-allocate a previously returned block +**--------------------------------------------------------------------------------------- +*/ +int32 CFE_ES_GenPoolRecyclePoolBlock(CFE_ES_GenPoolRecord_t *PoolRecPtr, uint16 BucketId, size_t NewSize, + size_t *BlockOffsetPtr) +{ + CFE_ES_GenPoolBucket_t *BucketPtr; + size_t DescOffset; + size_t BlockOffset; + size_t NextOffset; + CFE_ES_GenPoolBD_t * BdPtr; + uint16 RecycleBucketId; + int32 Status; + + BucketPtr = CFE_ES_GenPoolGetBucketState(PoolRecPtr, BucketId); + if (BucketPtr == NULL || BucketPtr->RecycleCount == BucketPtr->ReleaseCount || BucketPtr->FirstOffset == 0) + { + /* no buffers in pool to recycle */ + return CFE_ES_BUFFER_NOT_IN_POOL; + } + + BlockOffset = BucketPtr->FirstOffset; + DescOffset = BlockOffset - CFE_ES_GENERIC_POOL_DESCRIPTOR_SIZE; + Status = PoolRecPtr->Retrieve(PoolRecPtr, DescOffset, &BdPtr); + if (Status == CFE_SUCCESS) + { + RecycleBucketId = BdPtr->Allocated - CFE_ES_MEMORY_DEALLOCATED; + if (BdPtr->CheckBits != CFE_ES_CHECK_PATTERN || RecycleBucketId != BucketId) + { + /* sanity check failed - possible pool corruption? */ + Status = CFE_ES_BUFFER_NOT_IN_POOL; + } + else + { + /* + * Get it off the top on the list + */ + NextOffset = BdPtr->NextOffset; + + BdPtr->Allocated = CFE_ES_MEMORY_ALLOCATED + BucketId; /* Flag memory block as allocated */ + BdPtr->ActualSize = NewSize; + BdPtr->NextOffset = 0; + + Status = PoolRecPtr->Commit(PoolRecPtr, DescOffset, BdPtr); + if (Status == CFE_SUCCESS) + { + *BlockOffsetPtr = BlockOffset; + BucketPtr->FirstOffset = NextOffset; + ++BucketPtr->RecycleCount; + } + } + } + + return Status; +} + +/* +**--------------------------------------------------------------------------------------- +** Name: CFE_ES_GenPoolCreatePoolBlock +** +** Local helper function to create a new block of the given size +**--------------------------------------------------------------------------------------- +*/ +int32 CFE_ES_GenPoolCreatePoolBlock(CFE_ES_GenPoolRecord_t *PoolRecPtr, uint16 BucketId, size_t NewSize, + size_t *BlockOffsetPtr) +{ + CFE_ES_GenPoolBucket_t *BucketPtr; + size_t DescOffset; + size_t BlockOffset; + size_t NextTailPosition; + CFE_ES_GenPoolBD_t * BdPtr; + int32 Status; + + BucketPtr = CFE_ES_GenPoolGetBucketState(PoolRecPtr, BucketId); + if (BucketPtr == NULL) + { + /* no buffers in pool to create */ + return CFE_ES_BUFFER_NOT_IN_POOL; + } + + /* + * Determine the offsets of the new user block, + * which must be aligned according to the AlignMask member. + * + * Note - just pre-calculating offsets here, nothing is committed yet. + */ + BlockOffset = PoolRecPtr->TailPosition + CFE_ES_GENERIC_POOL_DESCRIPTOR_SIZE; + BlockOffset += PoolRecPtr->AlignMask; + BlockOffset &= ~PoolRecPtr->AlignMask; + + DescOffset = BlockOffset - CFE_ES_GENERIC_POOL_DESCRIPTOR_SIZE; + NextTailPosition = BlockOffset + BucketPtr->BlockSize; + + /* + * Check if there is enough space remaining in the pool -- the + * proposed start address plus the block size must not exceed the pool end. + */ + if (NextTailPosition > PoolRecPtr->PoolMaxOffset) + { + /* can't fit in remaining mem */ + return CFE_ES_ERR_MEM_BLOCK_SIZE; + } + + /* + * Now commit the new block + */ + Status = PoolRecPtr->Retrieve(PoolRecPtr, DescOffset, &BdPtr); + if (Status == CFE_SUCCESS) + { + BdPtr->CheckBits = CFE_ES_CHECK_PATTERN; + BdPtr->Allocated = CFE_ES_MEMORY_ALLOCATED + BucketId; /* Flag memory block as allocated */ + BdPtr->ActualSize = NewSize; + BdPtr->NextOffset = 0; + + Status = PoolRecPtr->Commit(PoolRecPtr, DescOffset, BdPtr); + if (Status == CFE_SUCCESS) + { + /* + ** adjust pool current pointer and other record keeping + */ + PoolRecPtr->TailPosition = NextTailPosition; + ++BucketPtr->AllocationCount; + ++PoolRecPtr->AllocationCount; + + *BlockOffsetPtr = BlockOffset; + } + } + + return Status; +} + +/* +**--------------------------------------------------------------------------------------- +** Name: CFE_ES_GenPoolInitialize +** +** ES Internal API - See Prototype for full API description +**--------------------------------------------------------------------------------------- +*/ +int32 CFE_ES_GenPoolInitialize(CFE_ES_GenPoolRecord_t *PoolRecPtr, size_t StartOffset, size_t PoolSize, + size_t AlignSize, uint16 NumBlockSizes, const size_t *BlockSizeList, + CFE_ES_PoolRetrieve_Func_t RetrieveFunc, CFE_ES_PoolCommit_Func_t CommitFunc) +{ + cpuaddr AlignMask; + uint32 i; + uint32 j; + CFE_ES_GenPoolBucket_t *BucketPtr; + + /* + * Note - being an internal/non-public API this does not need to + * check the directly-supplied arguments, it is assumed they are already + * sanity checked. + */ + memset(PoolRecPtr, 0, sizeof(*PoolRecPtr)); + + /* + * Convert alignment to a bit mask. + * This sets all LSBs if the passed in value was not actually a power of 2. + */ + if (AlignSize <= 1) + { + AlignMask = 0; + } + else + { + AlignMask = AlignSize - 1; + AlignMask |= AlignMask >> 1; + AlignMask |= AlignMask >> 2; + AlignMask |= AlignMask >> 4; + AlignMask |= AlignMask >> 8; + AlignMask |= AlignMask >> 16; + } + + /* complete initialization of pool record entry */ + PoolRecPtr->AlignMask = AlignMask; + PoolRecPtr->PoolTotalSize = PoolSize; + PoolRecPtr->PoolMaxOffset = PoolSize + StartOffset; + PoolRecPtr->NumBuckets = NumBlockSizes; + PoolRecPtr->Retrieve = RetrieveFunc; + PoolRecPtr->Commit = CommitFunc; + PoolRecPtr->TailPosition = StartOffset; + + /* initially copy all block sizes */ + BucketPtr = PoolRecPtr->Buckets; + for (i = 0; i < NumBlockSizes; ++i) + { + BucketPtr->BlockSize = BlockSizeList[i]; + ++BucketPtr; + } + + /* Sort by block size - a simple bubble sort - + * this does not run often and the list is relatively small. */ + do + { + j = 0; + BucketPtr = PoolRecPtr->Buckets; + for (i = 1; i < NumBlockSizes; ++i) + { + if (BucketPtr[0].BlockSize > BucketPtr[1].BlockSize) + { + /* swap */ + BucketPtr[0].BlockSize ^= BucketPtr[1].BlockSize; + BucketPtr[1].BlockSize ^= BucketPtr[0].BlockSize; + BucketPtr[0].BlockSize ^= BucketPtr[1].BlockSize; + ++j; + } + ++BucketPtr; + } + } while (j > 0); + + /* + * Additional sanity check - after sorting the list, + * confirm that the smallest block size (first entry) + * is not zero. + */ + if (PoolRecPtr->Buckets[0].BlockSize == 0) + { + return CFE_ES_ERR_MEM_BLOCK_SIZE; + } + + return CFE_SUCCESS; +} + +/* +**--------------------------------------------------------------------------------------- +** Name: CFE_ES_GenPoolCalcMinSize +** +** ES Internal API - See Prototype for full API description +**--------------------------------------------------------------------------------------- +*/ +size_t CFE_ES_GenPoolCalcMinSize(uint16 NumBlockSizes, const size_t *BlockSizeList, uint32 NumBlocks) +{ + uint16 BucketId; + size_t MinBlockSize; + + MinBlockSize = 0; + + if (NumBlockSizes > 0) + { + MinBlockSize = BlockSizeList[0]; + for (BucketId = 1; BucketId < NumBlockSizes; ++BucketId) + { + if (BlockSizeList[BucketId] < MinBlockSize) + { + MinBlockSize = BlockSizeList[BucketId]; + } + } + } + + MinBlockSize += sizeof(CFE_ES_GenPoolBD_t); + + return (NumBlocks * MinBlockSize); +} + +/* +**--------------------------------------------------------------------------------------- +** Name: CFE_ES_GenPoolGetBlock +** +** ES Internal API - See Prototype for full API description +**--------------------------------------------------------------------------------------- +*/ +int32 CFE_ES_GenPoolGetBlock(CFE_ES_GenPoolRecord_t *PoolRecPtr, size_t *BlockOffsetPtr, size_t ReqSize) +{ + int32 Status; + uint16 BucketId; + + /* Find the bucket which can accommodate the requested size. */ + BucketId = CFE_ES_GenPoolFindBucket(PoolRecPtr, ReqSize); + if (BucketId == 0) + { + CFE_ES_WriteToSysLog("CFE_ES:getPoolBlock err:size(%lu) > max(%lu).\n", (unsigned long)ReqSize, + (unsigned long)PoolRecPtr->Buckets[PoolRecPtr->NumBuckets - 1].BlockSize); + return (CFE_ES_ERR_MEM_BLOCK_SIZE); + } + + /* first attempt to recycle any buffers from the same bucket that were freed */ + Status = CFE_ES_GenPoolRecyclePoolBlock(PoolRecPtr, BucketId, ReqSize, BlockOffsetPtr); + if (Status != CFE_SUCCESS) + { + /* recycling not available - try making a new one instead */ + Status = CFE_ES_GenPoolCreatePoolBlock(PoolRecPtr, BucketId, ReqSize, BlockOffsetPtr); + } + + return (Status); +} + +/* +**--------------------------------------------------------------------------------------- +** Name: CFE_ES_GenPoolGetBlockSize +** +** ES Internal API - See Prototype for full API description +**--------------------------------------------------------------------------------------- +*/ +int32 CFE_ES_GenPoolGetBlockSize(CFE_ES_GenPoolRecord_t *PoolRecPtr, size_t *BlockSizePtr, size_t BlockOffset) +{ + size_t DescOffset; + CFE_ES_GenPoolBucket_t *BucketPtr; + CFE_ES_GenPoolBD_t * BdPtr; + int32 Status; + uint16 BucketId; + + if (BlockOffset >= PoolRecPtr->TailPosition || BlockOffset < CFE_ES_GENERIC_POOL_DESCRIPTOR_SIZE) + { + /* outside the bounds of the pool */ + return CFE_ES_BUFFER_NOT_IN_POOL; + } + + DescOffset = BlockOffset - CFE_ES_GENERIC_POOL_DESCRIPTOR_SIZE; + + Status = PoolRecPtr->Retrieve(PoolRecPtr, DescOffset, &BdPtr); + if (Status == CFE_SUCCESS) + { + BucketId = BdPtr->Allocated - CFE_ES_MEMORY_ALLOCATED; + BucketPtr = CFE_ES_GenPoolGetBucketState(PoolRecPtr, BucketId); + + if (BdPtr->CheckBits != CFE_ES_CHECK_PATTERN || BucketPtr == NULL || BdPtr->ActualSize == 0 || + BucketPtr->BlockSize < BdPtr->ActualSize) + { + /* This does not appear to be a valid data buffer */ + Status = CFE_ES_POOL_BLOCK_INVALID; + } + else + { + *BlockSizePtr = BdPtr->ActualSize; + } + } + + return Status; +} + +/* +**--------------------------------------------------------------------------------------- +** Name: CFE_ES_GenPoolPutBlock +** +** ES Internal API - See Prototype for full API description +**--------------------------------------------------------------------------------------- +*/ +int32 CFE_ES_GenPoolPutBlock(CFE_ES_GenPoolRecord_t *PoolRecPtr, size_t *BlockSizePtr, size_t BlockOffset) +{ + size_t DescOffset; + CFE_ES_GenPoolBucket_t *BucketPtr; + CFE_ES_GenPoolBD_t * BdPtr; + int32 Status; + uint16 BucketId; + + if (BlockOffset >= PoolRecPtr->TailPosition || BlockOffset < CFE_ES_GENERIC_POOL_DESCRIPTOR_SIZE) + { + /* outside the bounds of the pool */ + return CFE_ES_BUFFER_NOT_IN_POOL; + } + + DescOffset = BlockOffset - CFE_ES_GENERIC_POOL_DESCRIPTOR_SIZE; + + Status = PoolRecPtr->Retrieve(PoolRecPtr, DescOffset, &BdPtr); + if (Status == CFE_SUCCESS) + { + BucketId = BdPtr->Allocated - CFE_ES_MEMORY_ALLOCATED; + BucketPtr = CFE_ES_GenPoolGetBucketState(PoolRecPtr, BucketId); + + if (BdPtr->CheckBits != CFE_ES_CHECK_PATTERN || BucketPtr == NULL || BdPtr->ActualSize == 0 || + BucketPtr->BlockSize < BdPtr->ActualSize) + { + /* This does not appear to be a valid data buffer */ + ++PoolRecPtr->ValidationErrorCount; + Status = CFE_ES_POOL_BLOCK_INVALID; + } + else + { + BdPtr->Allocated = CFE_ES_MEMORY_DEALLOCATED + BucketId; + BdPtr->NextOffset = BucketPtr->FirstOffset; + *BlockSizePtr = BdPtr->ActualSize; + + Status = PoolRecPtr->Commit(PoolRecPtr, DescOffset, BdPtr); + if (Status == CFE_SUCCESS) + { + BucketPtr->FirstOffset = BlockOffset; + ++BucketPtr->ReleaseCount; + } + } + } + + return Status; +} + +/* +**--------------------------------------------------------------------------------------- +** Name: CFE_ES_GenPoolRebuild +** +** ES Internal API - See Prototype for full API description +**--------------------------------------------------------------------------------------- +*/ +int32 CFE_ES_GenPoolRebuild(CFE_ES_GenPoolRecord_t *PoolRecPtr) +{ + int32 Status; + size_t DescOffset; + size_t BlockOffset; + CFE_ES_GenPoolBucket_t *BucketPtr; + CFE_ES_GenPoolBD_t * BdPtr; + uint16 BucketId; + bool IsDeallocatedBlock; + + Status = CFE_SUCCESS; + + /* Scan the pool to find blocks that were created and freed */ + while (true) + { + IsDeallocatedBlock = false; + BucketId = 0; + BucketPtr = NULL; + + /* + * Determine the offsets of the next user block, + * which must be aligned according to the AlignMask member. + */ + BlockOffset = PoolRecPtr->TailPosition + CFE_ES_GENERIC_POOL_DESCRIPTOR_SIZE; + BlockOffset += PoolRecPtr->AlignMask; + BlockOffset &= ~PoolRecPtr->AlignMask; + + if (BlockOffset > PoolRecPtr->PoolMaxOffset) + { + /* End of pool reached, stop now */ + break; + } + + DescOffset = BlockOffset - CFE_ES_GENERIC_POOL_DESCRIPTOR_SIZE; + Status = PoolRecPtr->Retrieve(PoolRecPtr, DescOffset, &BdPtr); + if (Status != CFE_SUCCESS) + { + /* Failed to read descriptor */ + break; + } + + /* + * If the CheckBits indicate the block was in use, + * then do further inspection to find the block size + * and allocated/deallocated status. + */ + if (BdPtr->CheckBits == CFE_ES_CHECK_PATTERN) + { + /* Test if block is deallocated */ + BucketId = BdPtr->Allocated - CFE_ES_MEMORY_DEALLOCATED; + BucketPtr = CFE_ES_GenPoolGetBucketState(PoolRecPtr, BucketId); + if (BucketPtr != 0) + { + IsDeallocatedBlock = true; + } + else + { + /* + * Test if block is allocated. + * In this case there is nothing more to do, just + * get the size and skip the block. + */ + BucketId = BdPtr->Allocated - CFE_ES_MEMORY_ALLOCATED; + BucketPtr = CFE_ES_GenPoolGetBucketState(PoolRecPtr, BucketId); + } + } + + /* + * Sanity check that the actual size is less than the bucket size - + * it always should be, as long as the pool was created with the same + * set of bucket sizes. + */ + if (BucketPtr == NULL || BucketPtr->BlockSize < BdPtr->ActualSize) + { + /* Not a valid block signature - stop recovery now */ + break; + } + + PoolRecPtr->TailPosition = BlockOffset + BucketPtr->BlockSize; + + BucketPtr = CFE_ES_GenPoolGetBucketState(PoolRecPtr, BucketId); + ++BucketPtr->AllocationCount; + ++PoolRecPtr->AllocationCount; + + /* + * If it was a deallocated block, then add it to the local + * pool linked list structure and rewrite the descriptor. + */ + if (IsDeallocatedBlock) + { + ++BucketPtr->ReleaseCount; + BdPtr->NextOffset = BucketPtr->FirstOffset; + BucketPtr->FirstOffset = BlockOffset; + Status = PoolRecPtr->Commit(PoolRecPtr, DescOffset, BdPtr); + if (Status != CFE_SUCCESS) + { + break; + } + } + } + + return Status; +} + +/* +**--------------------------------------------------------------------------------------- +** Name: CFE_ES_GenPoolValidateState +** +** ES Internal API - See Prototype for full API description +**--------------------------------------------------------------------------------------- +*/ +bool CFE_ES_GenPoolValidateState(const CFE_ES_GenPoolRecord_t *PoolRecPtr) +{ + return (PoolRecPtr->PoolTotalSize > 0 && PoolRecPtr->TailPosition <= PoolRecPtr->PoolMaxOffset && + PoolRecPtr->NumBuckets > 0 && PoolRecPtr->NumBuckets <= CFE_PLATFORM_ES_POOL_MAX_BUCKETS); +} + +/* +**--------------------------------------------------------------------------------------- +** Name: CFE_ES_GenPoolGetUsage +** +** ES Internal API - See Prototype for full API description +**--------------------------------------------------------------------------------------- +*/ +void CFE_ES_GenPoolGetUsage(CFE_ES_GenPoolRecord_t *PoolRecPtr, CFE_ES_MemOffset_t *FreeSizeBuf, + CFE_ES_MemOffset_t *TotalSizeBuf) +{ + if (TotalSizeBuf != NULL) + { + *TotalSizeBuf = CFE_ES_MEMOFFSET_C(PoolRecPtr->PoolTotalSize); + } + if (FreeSizeBuf != NULL) + { + *FreeSizeBuf = CFE_ES_MEMOFFSET_C(PoolRecPtr->PoolMaxOffset - PoolRecPtr->TailPosition); + } +} + +/* +**--------------------------------------------------------------------------------------- +** Name: CFE_ES_GenPoolGetCounts +** +** ES Internal API - See Prototype for full API description +**--------------------------------------------------------------------------------------- +*/ +void CFE_ES_GenPoolGetCounts(CFE_ES_GenPoolRecord_t *PoolRecPtr, uint16 *NumBucketsBuf, uint32 *AllocCountBuf, + uint32 *ValidationErrorCountBuf) +{ + if (NumBucketsBuf != NULL) + { + *NumBucketsBuf = PoolRecPtr->NumBuckets; + } + if (AllocCountBuf != NULL) + { + *AllocCountBuf = PoolRecPtr->AllocationCount; + } + if (ValidationErrorCountBuf != NULL) + { + *ValidationErrorCountBuf = PoolRecPtr->ValidationErrorCount; + } +} + +/* +**--------------------------------------------------------------------------------------- +** Name: CFE_ES_GenPoolGetFreeSize +** +** ES Internal API - See Prototype for full API description +**--------------------------------------------------------------------------------------- +*/ +void CFE_ES_GenPoolGetBucketUsage(CFE_ES_GenPoolRecord_t *PoolRecPtr, uint16 BucketId, + CFE_ES_BlockStats_t *BlockStatsBuf) +{ + const CFE_ES_GenPoolBucket_t * BucketPtr; + static const CFE_ES_GenPoolBucket_t ZeroBucket = {0}; + + BucketPtr = CFE_ES_GenPoolGetBucketState(PoolRecPtr, BucketId); + if (BucketPtr == NULL) + { + /* bucket ID is not valid */ + BucketPtr = &ZeroBucket; + } + + if (BlockStatsBuf != NULL) + { + BlockStatsBuf->NumCreated = BucketPtr->AllocationCount; + BlockStatsBuf->BlockSize = CFE_ES_MEMOFFSET_C(BucketPtr->BlockSize); + BlockStatsBuf->NumFree = BucketPtr->ReleaseCount - BucketPtr->RecycleCount; + } +} diff --git a/modules/es/fsw/src/cfe_es_generic_pool.h b/modules/es/fsw/src/cfe_es_generic_pool.h new file mode 100644 index 000000000..6021da2c1 --- /dev/null +++ b/modules/es/fsw/src/cfe_es_generic_pool.h @@ -0,0 +1,283 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/** + * @file + * + * Purpose: + * This file contains the Internal interface for the cFE Critical Data Store + * memory pool functions. + * + * References: + * Flight Software Branch C Coding Standard Version 1.0a + * cFE Flight Software Application Developers Guide + * + * Notes: + * + */ + +#ifndef CFE_ES_GENERIC_POOL_H +#define CFE_ES_GENERIC_POOL_H + +/* +** Include Files +*/ +#include "common_types.h" + +/* +** Macro Definitions +*/ +#define CFE_ES_CHECK_PATTERN ((uint16)0x5a5a) +#define CFE_ES_MEMORY_ALLOCATED ((uint16)0xaaaa) +#define CFE_ES_MEMORY_DEALLOCATED ((uint16)0xdddd) + +#define CFE_ES_GENERIC_POOL_DESCRIPTOR_SIZE \ + sizeof(CFE_ES_GenPoolBD_t) /* amount of space to reserve with every allocation */ + +/* +** Type Definitions +*/ + +typedef struct CFE_ES_GenPoolBD +{ + uint16 CheckBits; /**< Set to a fixed bit pattern after init */ + uint16 Allocated; /**< Set to a bit pattern depending on allocation state */ + size_t ActualSize; /**< The actual requested size of the block */ + size_t NextOffset; /**< The offset of the next descriptor in the free stack */ +} CFE_ES_GenPoolBD_t; + +typedef struct CFE_ES_GenPoolBucket +{ + size_t BlockSize; + size_t FirstOffset; /**< Top of the "free stack" of buffers which have been returned */ + uint32 AllocationCount; /**< Total number of buffers of this block size that exist (initial get) */ + uint32 ReleaseCount; /**< Total number of buffers that have been released (put back) */ + uint32 RecycleCount; /**< Total number of buffers that have been recycled (get after put) */ +} CFE_ES_GenPoolBucket_t; + +/* + * Forward struct typedef so it can be used in retrieve/commit prototype + */ +typedef struct CFE_ES_GenPoolRecord CFE_ES_GenPoolRecord_t; + +/** + * \brief Function to retrieve a buffer descriptor from the pool storage + * + * The generic pool implementation does not assume that buffers can be + * directly accessed as memory. This routine obtains a reference to + * the descriptor data. On memory mapped pools it may output a direct + * pointer to the data instead of copying it. + */ +typedef int32 (*CFE_ES_PoolRetrieve_Func_t)(CFE_ES_GenPoolRecord_t *PoolRecPtr, size_t Offset, + CFE_ES_GenPoolBD_t **BdPtr); + +/** + * \brief Function to commit a buffer descriptor to the pool storage + * + * The generic pool implementation does not assume that buffers can be + * directly accessed as memory. This routine writes data back to pool + * storage. It may be a no-op for memory mapped pools. + */ +typedef int32 (*CFE_ES_PoolCommit_Func_t)(CFE_ES_GenPoolRecord_t *PoolRecPtr, size_t Offset, + const CFE_ES_GenPoolBD_t *BdPtr); + +/** + * \brief Generic Memory Pool Type + */ +struct CFE_ES_GenPoolRecord +{ + size_t PoolTotalSize; /**< Total size of the pool area, in bytes */ + size_t PoolMaxOffset; /**< End offset (position) of the pool */ + size_t AlignMask; /**< Alignment mask applied to all new allocations */ + size_t TailPosition; /**< Current high watermark of the pool, end of last allocation */ + + CFE_ES_PoolRetrieve_Func_t Retrieve; /**< Function to access a buffer descriptor in the pool storage */ + CFE_ES_PoolCommit_Func_t Commit; /**< Function to commit a buffer descriptor to the pool storage */ + + uint32 AllocationCount; /**< Total number of block allocations of any size */ + uint32 ValidationErrorCount; /**< Count of validation errors */ + + uint16 NumBuckets; /**< Number of entries in the "Buckets" array that are valid */ + CFE_ES_GenPoolBucket_t Buckets[CFE_PLATFORM_ES_POOL_MAX_BUCKETS]; /**< Bucket States */ +}; + +/*****************************************************************************/ +/* +** Function prototypes +*/ + +/** + * \brief Initialize a generic pool structure + * + * Resets the pool to its initial state, given the size + * and alignment specifications. + * + * \param[out] PoolRecPtr Pointer to pool structure + * \param[in] StartOffset Initial starting location of pool + * \param[in] PoolSize Size of pool (beyond start offset) + * \param[in] AlignSize Required Alignment of blocks + * \param[in] NumBlockSizes Number of entries in the BlockSizeList + * \param[in] BlockSizeList Size of pool blocks + * \param[in] RetrieveFunc Function to retrieve buffer descriptors + * \param[in] CommitFunc Function to commit buffer descriptors + * + * \return #CFE_SUCCESS, or error code \ref CFEReturnCodes + */ +int32 CFE_ES_GenPoolInitialize(CFE_ES_GenPoolRecord_t *PoolRecPtr, size_t StartOffset, size_t PoolSize, + size_t AlignSize, uint16 NumBlockSizes, const size_t *BlockSizeList, + CFE_ES_PoolRetrieve_Func_t RetrieveFunc, CFE_ES_PoolCommit_Func_t CommitFunc); + +/** + * \brief Gets a block from the pool + * + * This may recycle a previously returned block or allocate + * a new block, depending on availability. + * + * \param[inout] PoolRecPtr Pointer to pool structure + * \param[out] BlockOffsetPtr Location to output new block offset + * \param[in] ReqSize Size of block requested + * + * \return #CFE_SUCCESS, or error code \ref CFEReturnCodes + */ +int32 CFE_ES_GenPoolGetBlock(CFE_ES_GenPoolRecord_t *PoolRecPtr, size_t *BlockOffsetPtr, size_t ReqSize); + +/** + * \brief Returns a block to the pool + * + * This marks the previously allocated block as deallocated, + * and allows it to be recycled on a future get request. + * + * \param[inout] PoolRecPtr Pointer to pool structure + * \param[out] BlockSizePtr Location to output original allocation size + * \param[in] BlockOffset Offset of data block + * + * \return #CFE_SUCCESS, or error code \ref CFEReturnCodes + */ +int32 CFE_ES_GenPoolPutBlock(CFE_ES_GenPoolRecord_t *PoolRecPtr, size_t *BlockSizePtr, size_t BlockOffset); + +/** + * \brief Rebuild list of free blocks in pool + * + * If pools are stored in a nonvolatile memory area, then it is + * possible to resume pool operation from a previously initialized + * pool. This function attempts to restore the state of the pool + * by scanning for allocated and deallocated block markers. + * + * Before using this function, one should call CFE_ES_GenPoolInitialize() + * to first configure the basic pool structure and block size list. + * + * This function will then attempt to recreate the internal free lists + * based on descriptors/signatures already existing in the memory area. + * + * \param[inout] PoolRecPtr Pointer to pool structure + * + * \return #CFE_SUCCESS, or error code \ref CFEReturnCodes + */ +int32 CFE_ES_GenPoolRebuild(CFE_ES_GenPoolRecord_t *PoolRecPtr); + +/** + * \brief Get size of pool block + * + * Given a previously allocated block, look up its descriptor information + * and return the actual size. + * + * \param[inout] PoolRecPtr Pointer to pool structure + * \param[out] BlockSizePtr Location to output original allocation size + * \param[in] BlockOffset Offset of data block + * + * \return #CFE_SUCCESS, or error code \ref CFEReturnCodes + */ +int32 CFE_ES_GenPoolGetBlockSize(CFE_ES_GenPoolRecord_t *PoolRecPtr, size_t *BlockSizePtr, size_t BlockOffset); + +/** + * \brief Validate a pool structure + * + * Perform basic sanity checks on the pool internal data. + * + * \param[in] PoolRecPtr Pointer to pool structure + * + * \return #CFE_SUCCESS, or error code \ref CFEReturnCodes + */ +bool CFE_ES_GenPoolValidateState(const CFE_ES_GenPoolRecord_t *PoolRecPtr); + +/** + * \brief Query basic usage of the pool structure + * + * Obtain basic pool usage info for telemetry/statistics reporting. + * + * \param[in] PoolRecPtr Pointer to pool structure + * \param[out] FreeSizeBuf Buffer to store free size + * \param[out] TotalSizeBuf Buffer to store total size + * + * \note This function is intended for telemetry purposes, so it + * uses the message size type (CFE_ES_MemOffset_t) rather + * than size_t, to be compatible with the type used in telemetry + * messages. + */ +void CFE_ES_GenPoolGetUsage(CFE_ES_GenPoolRecord_t *PoolRecPtr, CFE_ES_MemOffset_t *FreeSizeBuf, + CFE_ES_MemOffset_t *TotalSizeBuf); + +/** + * \brief Query counters associated with the pool structure + * + * Obtain pool counters for telemetry/statistics reporting. + * + * \param[in] PoolRecPtr Pointer to pool structure + * \param[out] NumBucketsBuf Buffer to store bucket count + * \param[out] AllocCountBuf Buffer to store allocation count + * \param[out] ValidationErrorCountBuf Buffer to store validation error count + */ +void CFE_ES_GenPoolGetCounts(CFE_ES_GenPoolRecord_t *PoolRecPtr, uint16 *NumBucketsBuf, uint32 *AllocCountBuf, + uint32 *ValidationErrorCountBuf); + +/** + * \brief Query bucket-specific usage of the pool structure + * + * Obtain pool per-bucket stats for telemetry/statistics reporting. + * + * If the bucket number is not valid, this sets all output values to zero. + * + * \param[in] PoolRecPtr Pointer to pool structure + * \param[in] BucketId Bucket number (non-zero) + * \param[out] BlockStatsBuf Buffer to store block stats + */ +void CFE_ES_GenPoolGetBucketUsage(CFE_ES_GenPoolRecord_t *PoolRecPtr, uint16 BucketId, + CFE_ES_BlockStats_t *BlockStatsBuf); + +/** + * \brief Calculate the pool size required for the specified number of blocks + * + * Given a block size list, determine the amount of bytes required to allocate + * the requested number of minimally-sized blocks, including descriptor overhead. + * + * \note This is intended only as a sanity check on pool sizes, and does not + * guarantee the ability to actually allocate buffers in a real pool. In particular, + * alignment is not factored into the this size calculation, and this may require + * some additional overhead. + * + * \param[in] NumBlockSizes Number of entries in BlockSizeList + * \param[in] BlockSizeList Size of pool blocks + * \param[in] NumBlocks Number of blocks + * + * \return Minimum size required for requested number of blocks. + */ +size_t CFE_ES_GenPoolCalcMinSize(uint16 NumBlockSizes, const size_t *BlockSizeList, uint32 NumBlocks); + +#endif /* CFE_ES_GENERIC_POOL_H */ diff --git a/modules/es/fsw/src/cfe_es_global.h b/modules/es/fsw/src/cfe_es_global.h new file mode 100644 index 000000000..fa5cdd001 --- /dev/null +++ b/modules/es/fsw/src/cfe_es_global.h @@ -0,0 +1,239 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/** + * @file + * + * Purpose: + * This file contains the ES global data definitions. + * + * References: + * Flight Software Branch C Coding Standard Version 1.0a + * cFE Flight Software Application Developers Guide + * + */ + +#ifndef CFE_ES_GLOBAL_H +#define CFE_ES_GLOBAL_H + +/* +** Includes +*/ +#include "common_types.h" +#include "cfe_es_msg.h" +#include "cfe_es_api_typedefs.h" + +#include "cfe_es_erlog_typedef.h" +#include "cfe_es_resetdata_typedef.h" +#include "cfe_es_cds.h" + +#include /* for sig_atomic_t */ + +/* +** Typedefs +*/ + +/* +** CFE_ES_GenCounterRecord_t is an internal structure used to keep track of +** Generic Counters that are active in the system. +*/ +typedef struct +{ + CFE_ES_CounterId_t CounterId; /**< The actual counter ID of this entry, or undefined */ + uint32 Counter; + char CounterName[OS_MAX_API_NAME]; /* Counter Name */ +} CFE_ES_GenCounterRecord_t; + +/* + * Encapsulates the state of the ES background task + */ +typedef struct +{ + CFE_ES_TaskId_t TaskID; /**< ES ID of the background task */ + osal_id_t WorkSem; /**< Semaphore that is given whenever background work is pending */ + uint32 NumJobsRunning; /**< Current Number of active jobs (updated by background task) */ +} CFE_ES_BackgroundTaskState_t; + +/* + * Background log dump state structure + * + * This structure is stored in global memory and keeps the state + * of the log dump from one iteration to the next. + * + * NOTE: This is used for log structures which are expected to be small + * enough so such that it is not necessary to throttle the file write or + * spread it over time. + * + * Therefore, the only thing necessary to be stored is whether there + * is a pending write request, and the data file name. + * + * Larger log files, such as the Perf log, must implement a state machine + * with a dedicated state data structure. + */ +typedef struct +{ + CFE_FS_FileWriteMetaData_t FileWrite; /**< FS state data - must be first */ + CFE_ES_ERLog_FileEntry_t EntryBuffer; /**< Temp holding area for record to write */ +} CFE_ES_BackgroundLogDumpGlobal_t; + +/* +** Type definition (ES task global data) +*/ +typedef struct +{ + /* + ** ES Task command interface counters + */ + uint8 CommandCounter; + uint8 CommandErrorCounter; + + /* + ** ES Task housekeeping telemetry + */ + CFE_ES_HousekeepingTlm_t HkPacket; + + /* + ** Single application telemetry + */ + CFE_ES_OneAppTlm_t OneAppPacket; + + /* + ** Memory statistics telemetry + */ + CFE_ES_MemStatsTlm_t MemStatsPacket; + + /* + ** ES Task operational data (not reported in housekeeping) + */ + CFE_SB_PipeId_t CmdPipe; + +} CFE_ES_TaskData_t; + +/* +** Executive Services Global Memory Data +** This is the regular global data that is not preserved on a +** processor reset. +*/ +typedef struct +{ + /* + ** Debug Variables + */ + CFE_ES_DebugVariables_t DebugVars; + + /* + ** Shared Data Semaphore + */ + osal_id_t SharedDataMutex; + + /* + ** Performance Data Mutex + */ + osal_id_t PerfDataMutex; + + /* + ** Startup Sync + */ + volatile sig_atomic_t SystemState; + + /* + ** ES Task Table + */ + uint32 RegisteredTasks; + CFE_ES_TaskRecord_t TaskTable[OS_MAX_TASKS]; + + /* + ** ES App Table + */ + uint32 RegisteredCoreApps; + uint32 RegisteredExternalApps; + CFE_ResourceId_t LastAppId; + CFE_ES_AppRecord_t AppTable[CFE_PLATFORM_ES_MAX_APPLICATIONS]; + + /* + ** ES Shared Library Table + */ + uint32 RegisteredLibs; + CFE_ResourceId_t LastLibId; + CFE_ES_LibRecord_t LibTable[CFE_PLATFORM_ES_MAX_LIBRARIES]; + + /* + ** ES Generic Counters Table + */ + CFE_ResourceId_t LastCounterId; + CFE_ES_GenCounterRecord_t CounterTable[CFE_PLATFORM_ES_MAX_GEN_COUNTERS]; + + /* + ** Critical Data Store Management Variables + */ + CFE_ES_CDS_Instance_t CDSVars; + bool CDSIsAvailable; /**< \brief Whether or not the CDS service is active/valid */ + + /* + * Background task for handling long-running, non real time tasks + * such as maintenance, file writes, and other items. + */ + CFE_ES_BackgroundTaskState_t BackgroundTask; + + /* + ** Memory Pools + */ + CFE_ResourceId_t LastMemPoolId; + CFE_ES_MemPoolRecord_t MemPoolTable[CFE_PLATFORM_ES_MAX_MEMORY_POOLS]; + + /* + ** ES Task initialization data (not reported in housekeeping) + */ + CFE_ES_BackgroundLogDumpGlobal_t BackgroundERLogDumpState; + + /* + * Persistent state data associated with performance log data file writes + */ + CFE_ES_PerfDumpGlobal_t BackgroundPerfDumpState; + + /* + * Persistent state data associated with background app table scans + */ + CFE_ES_AppTableScanState_t BackgroundAppScanState; + + /* + * Task global data (formerly a separate global). + */ + CFE_ES_TaskData_t TaskData; + + /* + * Pointer to the Reset data that is preserved on a processor reset + */ + CFE_ES_ResetData_t *ResetDataPtr; + +} CFE_ES_Global_t; + +/* +** The Executive Services Global Data declaration +*/ +extern CFE_ES_Global_t CFE_ES_Global; + +/* +** Functions used to lock/unlock shared data +*/ +extern void CFE_ES_LockSharedData(const char *FunctionName, int32 LineNumber); +extern void CFE_ES_UnlockSharedData(const char *FunctionName, int32 LineNumber); + +#endif /* CFE_ES_GLOBAL_H */ diff --git a/modules/es/fsw/src/cfe_es_log.h b/modules/es/fsw/src/cfe_es_log.h new file mode 100644 index 000000000..cac9267c7 --- /dev/null +++ b/modules/es/fsw/src/cfe_es_log.h @@ -0,0 +1,361 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/** + * @file + * + * Purpose: + * This file contains definitions needed for the cFE ES Logs. The + * logs include the Mode Transition log, the System Log, and the + * Performance log. + * + * References: + * Flight Software Branch C Coding Standard Version 1.0a + * cFE Flight Software Application Developers Guide + * + * Notes: + * + */ + +#ifndef CFE_ES_LOG_H +#define CFE_ES_LOG_H + +/* +** Include Files +*/ +#include "common_types.h" +#include "cfe_es_api_typedefs.h" +#include "cfe_time_api_typedefs.h" + +#include /* required for "va_list" */ + +/* +** Macro Definitions +*/ + +/** + * Buffer size for system log messages + * + * This is based on the EVS maximum event message size, plus a time stamp + * and required extra formatting characters. + * + * Two extra characters are necessary: + * - for the space between the timestamp and the message in the system log + * - to enforce a newline character at the end of the string + * + * note that a null terminator byte is accounted for in "CFE_TIME_PRINTED_STRING_SIZE" + */ +#define CFE_ES_MAX_SYSLOG_MSG_SIZE (CFE_MISSION_EVS_MAX_MESSAGE_LENGTH + CFE_TIME_PRINTED_STRING_SIZE + 2) + +/** + * Size of the syslog "dump buffer" + * + * This is a temporary buffer that serves as a holding place for syslog data as + * it is being dumped to a file on disk. Since disks are comparatively slow and + * access to the syslog buffer must be synchronized, copying to a temporary buffer + * first significantly decreases the amount of time that the syslog is locked after + * a file dump is requested. + * + * This buffer also reflects the SysLog "burst size" that is guaranteed to be + * safe for concurrent writes and reads/dump operations. If applications Log more than + * this amount of data in less time than it takes to write this amount of data to disk, + * then some log messages may be corrupt or lost in the output file. + * + * @note If contention occurs where applications would overwrite logs that are still + * being "read" by a dump process, the realtime applications are given preference and + * therefore NOT blocked. Design preference is given to applications over the absolute + * integrity of the dump file. + */ +#define CFE_ES_SYSLOG_READ_BUFFER_SIZE (3 * CFE_ES_MAX_SYSLOG_MSG_SIZE) + +/** + * \brief Self-synchronized macro to call CFE_ES_SysLogAppend_Unsync + * + * Calls CFE_ES_SysLogAppend_Unsync() with appropriate synchronization. + * It will acquire the shared data lock and release it after appending the log. + * + * This is implemented as a macro such that the "__func__" and "__LINE__" directives + * will reflect the actual place that the append was done, rather than where this + * wrapper was defined. + * + * \sa CFE_ES_SysLogAppend_Unsync() + */ +#define CFE_ES_SYSLOG_APPEND(LogString) \ + { \ + CFE_ES_LockSharedData(__func__, __LINE__); \ + CFE_ES_SysLogAppend_Unsync(LogString); \ + CFE_ES_UnlockSharedData(__func__, __LINE__); \ + OS_printf("%s", LogString); \ + } + +/** + * \brief Indicates no context information Error Logs + * + * For use with the CFE_ES_WriteToERLog() function when no context + * information is available. + */ +#define CFE_ES_ERLOG_NO_CONTEXT (0) + +/* +** Type Definitions +*/ + +/** + * \brief Buffer structure for reading data out of the SysLog + * + * Access to the syslog must be synchronized, so it is not possible to + * directly access the contents. This structure keeps the state of + * read operations such that the syslog can be read in segments. + * + * @sa CFE_ES_SysLogReadData(), CFE_ES_SysLogReadStart_Unsync() + */ +typedef struct +{ + size_t SizeLeft; /**< Total amount of unread syslog data */ + size_t BlockSize; /**< Size of content currently in the "Data" member */ + size_t EndIdx; /**< End of the syslog buffer at the time reading started */ + size_t LastOffset; /**< Current Read Position */ + + char Data[CFE_ES_SYSLOG_READ_BUFFER_SIZE]; /**< Actual syslog content */ +} CFE_ES_SysLogReadBuffer_t; + +/* +** Function prototypes +*/ + +/* +** System log management +** +** NOTE: CFE_ES_WriteToSysLog() is a public routine in cfe_es.h, it is not prototyped here +*/ + +/** + * \brief Clear system log + * + * This discards the entire system log buffer and resets internal index values + * + * \note This function requires external thread synchronization + */ +void CFE_ES_SysLogClear_Unsync(void); + +/** + * \brief Begin reading the system log + * + * This a helper function is intended to assist with the "Write" command to dump + * the contents of the syslog to a disk file. This locates the oldest complete + * log message currently contained in the buffer. + * + * The oldest log message may be overwritten when any application calls + * CFE_ES_WriteToSysLog() if set to OVERWRITE mode. + * + * This function only locates the first message, it does not actually copy any + * data to the supplied buffer. The CFE_ES_SysLogReadData() should be called + * to read log data. + * + * \param Buffer A local buffer which will be initialized to the start of the log buffer + * + * \note This function requires external thread synchronization + * \sa CFE_ES_SysLogReadData() + */ +void CFE_ES_SysLogReadStart_Unsync(CFE_ES_SysLogReadBuffer_t *Buffer); + +/** + * \brief Write a printf-style formatted string to the system log + * + * This is a drop-in replacement for the existing CFE_ES_WriteToSysLog() API + * that does _not_ perform any synchronization or locking. It is intended for + * logging from within the ES subsystem where the appropriate lock is + * already held for other reasons. + * + * \note This function requires external thread synchronization + */ +int32 CFE_ES_SysLogWrite_Unsync(const char *SpecStringPtr, ...); + +/** + * \brief Append a complete pre-formatted string to the ES SysLog + * + * The new message will be copied to the current write location in the + * system log buffer. If there is not sufficient space to completely store + * the message, then the behavior depends on the "LogMode" setting. + * + * If "LogMode" is set to DISCARD, then the message will be truncated + * to fit in the available space, or completely discarded if no space exists. + * + * If "LogMode" is set to OVERWRITE, then the oldest message(s) in the + * system log will be overwritten with this new message. + * + * \param LogString Message to append + * + * \note This function requires external thread synchronization + * \sa CFE_ES_SysLogSetMode() + */ +int32 CFE_ES_SysLogAppend_Unsync(const char *LogString); + +/** + * \brief Read data from the system log buffer into the local buffer + * + * Prior to calling this function, the buffer structure should be initialized + * using CFE_ES_SysLogReadStart_Unsync() + * + * This copies the data from the syslog memory space into the local buffer, starting + * from the end of the previously read data. To read the complete system log, + * this function should be called repeatedly until the "BlockSize" member in the + * returned buffer is returned as zero, indicating there is no more data in the syslog. + * + * There is no specific external synchronization requirement on this function, since + * copies of the relevant log indices are kept in the buffer structure itself. However, + * if system log data is overwritten between calls to this function, it may result in + * undefined data being returned to the caller. + * + * Therefore, in cases where it is critically important to read log message data, the + * lock should be held for the entire procedure (initialization through complete read). + * However this may have significant realtime implications, so it is not the required + * mode of operation. + * + * \param Buffer A local buffer which will be filled with data from the log buffer + */ +void CFE_ES_SysLogReadData(CFE_ES_SysLogReadBuffer_t *Buffer); + +/** + * \brief Sets the operating mode of the system log buffer + * + * The operating mode of the system log controls its behavior once filled to the point + * where additional messages can no longer be stored. + * + * If "Mode" is set to DISCARD, then the message will be truncated + * to fit in the available space, or completely discarded if no space exists. + * + * If "Mode" is set to OVERWRITE, then the oldest message(s) in the + * system log will be overwritten with this new message. + * + * \note Switching from OVERWRITE to DISCARD mode may take effect immediately, as the + * setting only takes effect when the buffer "wrap-point" is reached at the end. + * + * \param Mode The desired operating mode + * \return CFE_SUCCESS if set successfully + */ +int32 CFE_ES_SysLogSetMode(CFE_ES_LogMode_Enum_t Mode); + +/** + * \brief Format a message intended for output to the system log + * + * This function prepares a complete message for passing into CFE_ES_SysLogAppend_Unsync(), + * based on the given vsnprintf-style specification string and argument list. + * + * The message is prefixed with a time stamp based on the current time, followed by the + * caller-specified string. An ending newline and terminating null character are both + * ensured on the output string. + * + * To account for the timestamp, newline, and terminating null character, the supplied buffer + * must be greater than (CFE_TIME_PRINTED_STRING_SIZE+2) to get a useful output. Any user-specified + * output string will be truncated to fit into the remaining space. + * + * \param Buffer User supplied buffer to output formatted sting into + * \param BufferSize Size of "Buffer" parameter. Should be greater than (CFE_TIME_PRINTED_STRING_SIZE+2) + * \param SpecStringPtr Printf-style format string + * \param ArgPtr Variable argument list as obtained by va_start() in the caller + * + * \sa CFE_ES_SysLogAppend_Unsync() + */ +void CFE_ES_SysLog_vsnprintf(char *Buffer, size_t BufferSize, const char *SpecStringPtr, va_list ArgPtr); + +/** + * \brief Format a message intended for output to the system log + * + * Identical to the CFE_ES_SysLog_vsnprintf() call but with a variable argument set, + * for use in functions that need to directly handle a log message string. + * + * Similar in definition to the "snprintf()" C library call. + * + * \param Buffer User supplied buffer to output formatted sting into + * \param BufferSize Size of "Buffer" parameter. Should be greater than (CFE_TIME_PRINTED_STRING_SIZE+2) + * \param SpecStringPtr Printf-style format string + * + * \sa CFE_ES_SysLogAppend_Unsync() + */ +void CFE_ES_SysLog_snprintf(char *Buffer, size_t BufferSize, const char *SpecStringPtr, ...) OS_PRINTF(3, 4); + +/** + * \brief Write the contents of the syslog to a disk file + * + * Writes the current contents of the syslog buffer to a file specified + * by the Filename parameter. The log messages will be written to the file + * in the same order in which they were written into the syslog buffer. + * + * A snapshot of the log indices is taken at the beginning of the writing + * process. Additional log entries added after this (e.g. from applications + * calling CFE_ES_WriteToSysLog() after starting a syslog dump) will not be + * included in the dump file. + * + * Note that preference is given to the realtime application threads over + * any pending log read activities, such as a dumping to a file. The design + * of this function can tolerate a limited level of logging activity while + * the dump is in progress without any negative side effects. However, a significant + * "flood" of log messages may corrupt the output file, by overwriting older data + * before it has actually been written. + * + * \param Filename Output file to write + * \return CFE_SUCCESS if successful, or an appropriate error code from cfe_error.h + * + * \sa CFE_ES_SYSLOG_READ_BUFFER_SIZE + * + */ +int32 CFE_ES_SysLogDump(const char *Filename); + +/* +** Processor Performance log management +*/ +int32 CFE_ES_PerfLogClear(void); +void CFE_ES_PerfLogDump(void); + +/* +** Exception and Reset Log API +*/ + +/** + * \brief Create an entry in the ES Exception and Reset Log. + * + * The exception and reset log is used to track significant system-level events and anomalies + * for later analysis. + * + * \param EntryType Whether the event is relevant to the CORE or an APPLICATION (#CFE_ES_LogEntryType_Enum_t) + * \param ResetType The type of the last reset + * \param ResetSubtype The subtype of the last reset + * \param Description A summary of the event + * + * \return CFE_SUCCESS if successful, or an appropriate error code from cfe_error.h + */ +int32 CFE_ES_WriteToERLog(CFE_ES_LogEntryType_Enum_t EntryType, uint32 ResetType, uint32 ResetSubtype, + const char *Description); + +/** + * \copydoc CFE_ES_WriteToERLog() + * + * This log API accepts extra context information (AppID and ContextID) + * and is used when the app/task invoking this API is not the same app + * as where the event occurred. + * + * \param AppId The Application ID associated with the task that caused the exception + * \param PspContextId Identifier of extended context info stored in the PSP (if available) + */ +int32 CFE_ES_WriteToERLogWithContext(CFE_ES_LogEntryType_Enum_t EntryType, uint32 ResetType, uint32 ResetSubtype, + const char *Description, CFE_ES_AppId_t AppId, uint32 PspContextId); + +#endif /* CFE_ES_LOG_H */ diff --git a/modules/es/fsw/src/cfe_es_mempool.c b/modules/es/fsw/src/cfe_es_mempool.c new file mode 100644 index 000000000..6a7107547 --- /dev/null +++ b/modules/es/fsw/src/cfe_es_mempool.c @@ -0,0 +1,672 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/* +** File: +** cfe_es_mempool.c +** +** Purpose: +** Set of services for management of discrete sized memory pools. +** +** References: +** Flight Software Branch C Coding Standard Version 1.0a +** cFE Flight Software Application Developers Guide +** +*/ + +/* +** Includes +*/ +#include "cfe_es_module_all.h" + +#include +#include +#include + +/** + * Macro that determines the native alignment requirement of a specific type + * + * By getting the offset of the structure after following a single char, + * this effectively gets how much padding the compiler added, which in turn reveals its + * minimum alignment requirement. (C99 is lacking a standardized "alignof" operator, + * and this is intended to substitute). + */ +#define ALIGN_OF(type) \ + ((cpuaddr) & ((struct { \ + char Byte; \ + type Align; \ + } *)0) \ + ->Align) + +/*****************************************************************************/ +/* +** Type Definitions +*/ + +const size_t CFE_ES_MemPoolDefSize[CFE_PLATFORM_ES_POOL_MAX_BUCKETS] = { + CFE_PLATFORM_ES_MAX_BLOCK_SIZE, CFE_PLATFORM_ES_MEM_BLOCK_SIZE_16, CFE_PLATFORM_ES_MEM_BLOCK_SIZE_15, + CFE_PLATFORM_ES_MEM_BLOCK_SIZE_14, CFE_PLATFORM_ES_MEM_BLOCK_SIZE_13, CFE_PLATFORM_ES_MEM_BLOCK_SIZE_12, + CFE_PLATFORM_ES_MEM_BLOCK_SIZE_11, CFE_PLATFORM_ES_MEM_BLOCK_SIZE_10, CFE_PLATFORM_ES_MEM_BLOCK_SIZE_09, + CFE_PLATFORM_ES_MEM_BLOCK_SIZE_08, CFE_PLATFORM_ES_MEM_BLOCK_SIZE_07, CFE_PLATFORM_ES_MEM_BLOCK_SIZE_06, + CFE_PLATFORM_ES_MEM_BLOCK_SIZE_05, CFE_PLATFORM_ES_MEM_BLOCK_SIZE_04, CFE_PLATFORM_ES_MEM_BLOCK_SIZE_03, + CFE_PLATFORM_ES_MEM_BLOCK_SIZE_02, CFE_PLATFORM_ES_MEM_BLOCK_SIZE_01}; + +/*****************************************************************************/ +/* +** Functions +*/ + +int32 CFE_ES_MemPoolDirectRetrieve(CFE_ES_GenPoolRecord_t *PoolRecPtr, size_t Offset, CFE_ES_GenPoolBD_t **BdPtr) +{ + cpuaddr DataAddress; + CFE_ES_MemPoolRecord_t *MemPoolRecPtr = (CFE_ES_MemPoolRecord_t *)PoolRecPtr; + + DataAddress = MemPoolRecPtr->BaseAddr + Offset; + *BdPtr = (CFE_ES_GenPoolBD_t *)DataAddress; + + return CFE_SUCCESS; +} + +int32 CFE_ES_MemPoolDirectCommit(CFE_ES_GenPoolRecord_t *PoolRecPtr, size_t Offset, const CFE_ES_GenPoolBD_t *BdPtr) +{ + return CFE_SUCCESS; +} + +int32 CFE_ES_MemPoolID_ToIndex(CFE_ES_MemHandle_t PoolID, uint32 *Idx) +{ + return CFE_ResourceId_ToIndex(CFE_RESOURCEID_UNWRAP(PoolID), CFE_ES_POOLID_BASE, CFE_PLATFORM_ES_MAX_MEMORY_POOLS, + Idx); +} + +/*--------------------------------------------------------------------------------------- + * Function: CFE_ES_CheckMemPoolSlotUsed + * + * Purpose: Helper function, Aids in allocating a new ID by checking if + * a given table slot is available. Must be called while locked. + *--------------------------------------------------------------------------------------- + */ +bool CFE_ES_CheckMemPoolSlotUsed(CFE_ResourceId_t CheckId) +{ + CFE_ES_MemPoolRecord_t *MemPoolRecPtr; + /* + * Note - The pointer here should never be NULL because the ID should always be + * within the expected range, but if it ever is NULL, this should return true + * such that the caller will _not_ attempt to use the record. + */ + MemPoolRecPtr = CFE_ES_LocateMemPoolRecordByID(CFE_ES_MEMHANDLE_C(CheckId)); + return (MemPoolRecPtr == NULL || CFE_ES_MemPoolRecordIsUsed(MemPoolRecPtr)); +} + +CFE_ES_MemPoolRecord_t *CFE_ES_LocateMemPoolRecordByID(CFE_ES_MemHandle_t PoolID) +{ + CFE_ES_MemPoolRecord_t *MemPoolRecPtr; + uint32 Idx; + + if (CFE_ES_MemPoolID_ToIndex(PoolID, &Idx) == CFE_SUCCESS) + { + MemPoolRecPtr = &CFE_ES_Global.MemPoolTable[Idx]; + } + else + { + MemPoolRecPtr = NULL; + } + + return MemPoolRecPtr; +} + +/* +** CFE_ES_PoolCreateNoSem will initialize a pre-allocated memory pool without using a mutex. +*/ +int32 CFE_ES_PoolCreateNoSem(CFE_ES_MemHandle_t *PoolID, uint8 *MemPtr, size_t Size) +{ + return CFE_ES_PoolCreateEx(PoolID, MemPtr, Size, CFE_PLATFORM_ES_POOL_MAX_BUCKETS, &CFE_ES_MemPoolDefSize[0], + CFE_ES_NO_MUTEX); +} + +/* +** CFE_ES_PoolCreate will initialize a pre-allocated memory pool while using a mutex. +*/ +int32 CFE_ES_PoolCreate(CFE_ES_MemHandle_t *PoolID, uint8 *MemPtr, size_t Size) +{ + return CFE_ES_PoolCreateEx(PoolID, MemPtr, Size, CFE_PLATFORM_ES_POOL_MAX_BUCKETS, &CFE_ES_MemPoolDefSize[0], + CFE_ES_USE_MUTEX); +} + +int32 CFE_ES_PoolCreateEx(CFE_ES_MemHandle_t *PoolID, uint8 *MemPtr, size_t Size, uint16 NumBlockSizes, + const size_t *BlockSizes, bool UseMutex) +{ + int32 Status; + CFE_ResourceId_t PendingID; + CFE_ES_MemPoolRecord_t *PoolRecPtr; + size_t Alignment; + size_t MinimumSize; + char MutexName[OS_MAX_API_NAME]; + + /* Sanity Check inputs */ + if (MemPtr == NULL || PoolID == NULL) + { + return CFE_ES_BAD_ARGUMENT; + } + + /* If too many sizes are specified, return an error */ + if (NumBlockSizes > CFE_PLATFORM_ES_POOL_MAX_BUCKETS) + { + CFE_ES_WriteToSysLog("CFE_ES:poolCreate Num Block Sizes (%d) greater than max (%d)\n", (int)NumBlockSizes, + CFE_PLATFORM_ES_POOL_MAX_BUCKETS); + return (CFE_ES_BAD_ARGUMENT); + } + + /* + * Use default block sizes if not specified + */ + if (BlockSizes == NULL) + { + BlockSizes = CFE_ES_MemPoolDefSize; + if (NumBlockSizes == 0) + { + NumBlockSizes = CFE_PLATFORM_ES_POOL_MAX_BUCKETS; + } + } + + /* + * Sanity check the pool size + */ + MinimumSize = CFE_ES_GenPoolCalcMinSize(NumBlockSizes, BlockSizes, 1); + if (Size < MinimumSize) + { + CFE_ES_WriteToSysLog("CFE_ES:poolCreate Pool size(%lu) too small, need >=%lu bytes\n", (unsigned long)Size, + (unsigned long)MinimumSize); + return CFE_ES_BAD_ARGUMENT; + } + + CFE_ES_LockSharedData(__func__, __LINE__); + + /* scan for a free slot */ + PendingID = CFE_ResourceId_FindNext(CFE_ES_Global.LastMemPoolId, CFE_PLATFORM_ES_MAX_MEMORY_POOLS, + CFE_ES_CheckMemPoolSlotUsed); + PoolRecPtr = CFE_ES_LocateMemPoolRecordByID(CFE_ES_MEMHANDLE_C(PendingID)); + + if (PoolRecPtr == NULL) + { + CFE_ES_SysLogWrite_Unsync("ES Startup: No free MemPoolrary slots available\n"); + Status = CFE_ES_NO_RESOURCE_IDS_AVAILABLE; + } + else + { + /* Fully clear the entry, just in case of stale data */ + memset(PoolRecPtr, 0, sizeof(*PoolRecPtr)); + CFE_ES_MemPoolRecordSetUsed(PoolRecPtr, CFE_RESOURCEID_RESERVED); + CFE_ES_Global.LastMemPoolId = PendingID; + Status = CFE_SUCCESS; + } + + CFE_ES_UnlockSharedData(__func__, __LINE__); + + /* + * If no open resource ID was found, return now. + * + * No more inline returns after this point; execution + * must continue to the end of this function where the ID is freed + * if not fully successful. + */ + if (Status != CFE_SUCCESS) + { + return Status; + } + + Alignment = ALIGN_OF(CFE_ES_PoolAlign_t); /* memory mapped pools should be aligned */ + if (Alignment < CFE_PLATFORM_ES_MEMPOOL_ALIGN_SIZE_MIN) + { + /* + * Note about path coverage testing - depending on the + * system architecture and configuration this line may be + * unreachable. This is OK. + */ + Alignment = CFE_PLATFORM_ES_MEMPOOL_ALIGN_SIZE_MIN; + } + + /* + * Most of the work is done by the generic pool implementation. + * This subsystem works in offsets, not pointers. + */ + Status = CFE_ES_GenPoolInitialize(&PoolRecPtr->Pool, 0, Size, Alignment, NumBlockSizes, BlockSizes, + CFE_ES_MemPoolDirectRetrieve, CFE_ES_MemPoolDirectCommit); + + /* + * If successful, complete the process. + */ + if (Status == CFE_SUCCESS && UseMutex == CFE_ES_USE_MUTEX) + { + /* + ** Construct a name for the Mutex from the address + ** This is needed only because OS_MutSemCreate requires + ** a unique name for each semaphore created. + */ + snprintf(MutexName, OS_MAX_API_NAME, "Pool%08lX", CFE_ResourceId_ToInteger(PendingID)); + + /* create a mutex to protect this memory pool */ + Status = OS_MutSemCreate(&PoolRecPtr->MutexId, MutexName, 0); + if (Status != OS_SUCCESS) + { + /* log error and rewrite to CFE status code */ + CFE_ES_WriteToSysLog("CFE_ES:poolCreate OSAL error %d while creating mutex\n", (int)Status); + + Status = CFE_STATUS_EXTERNAL_RESOURCE_FAIL; + } + } + + if (Status == CFE_SUCCESS) + { + /* + * Store the base address. + * This is only relevant for memory-mapped pools which is why it is done here. + */ + PoolRecPtr->BaseAddr = (cpuaddr)MemPtr; + + /* + * Get the calling context. + * If this not a valid CFE context, then AppID will be undefined. + * We can still permit the creation of the pool but automatic cleanup + * if an exception or other event occurs will not be possible. + */ + CFE_ES_GetAppID(&PoolRecPtr->OwnerAppID); + + /* + * Store the actual/correct pool ID in the record. + */ + CFE_ES_MemPoolRecordSetUsed(PoolRecPtr, PendingID); + } + else + { + /* + * Free the entry that was reserved earlier + */ + CFE_ES_MemPoolRecordSetFree(PoolRecPtr); + PendingID = CFE_RESOURCEID_UNDEFINED; + + if (Status == CFE_ES_POOL_BOUNDS_ERROR) + { + CFE_ES_WriteToSysLog("CFE_ES:poolCreate Pool size(%lu) too small\n", (unsigned long)Size); + } + } + + /* + * Export pool ID to caller as handle + */ + *PoolID = CFE_ES_MEMHANDLE_C(PendingID); + + return (Status); +} + +int32 CFE_ES_PoolDelete(CFE_ES_MemHandle_t PoolID) +{ + CFE_ES_MemPoolRecord_t *PoolRecPtr; + osal_id_t MutexId; + int32 Status; + int32 MutDeleteStatus; + + PoolRecPtr = CFE_ES_LocateMemPoolRecordByID(PoolID); + + CFE_ES_LockSharedData(__func__, __LINE__); + + /* basic sanity check */ + if (CFE_ES_MemPoolRecordIsMatch(PoolRecPtr, PoolID)) + { + MutexId = PoolRecPtr->MutexId; /* snapshot mutex ID, will be freed later */ + CFE_ES_MemPoolRecordSetFree(PoolRecPtr); + Status = CFE_SUCCESS; + } + else + { + MutexId = OS_OBJECT_ID_UNDEFINED; + Status = CFE_ES_ERR_RESOURCEID_NOT_VALID; + } + + CFE_ES_UnlockSharedData(__func__, __LINE__); + + /* Release the mutex if it was configured. + * This is done after releasing the ES lock, to avoid + * potential conflict with holding two locks. */ + if (OS_ObjectIdDefined(MutexId)) + { + MutDeleteStatus = OS_MutSemDelete(MutexId); + if (MutDeleteStatus != OS_SUCCESS) + { + /* + * Report to syslog for informational purposes only. + * + * The MemPool entry has already been deleted, so this + * function should not return an error at this point. + */ + CFE_ES_WriteToSysLog("CFE_ES:poolDelete error %d deleting mutex\n", (int)MutDeleteStatus); + } + } + + return Status; +} + +/* +** Function: +** CFE_ES_GetPoolBuf +** +** Purpose: +** CFE_ES_GetPoolBuf allocates a block from the memory pool. +*/ +int32 CFE_ES_GetPoolBuf(CFE_ES_MemPoolBuf_t *BufPtr, CFE_ES_MemHandle_t Handle, size_t Size) +{ + int32 Status; + CFE_ES_AppId_t AppId; + CFE_ES_MemPoolRecord_t *PoolRecPtr; + size_t DataOffset; + + if (BufPtr == NULL) + { + return CFE_ES_BAD_ARGUMENT; + } + + PoolRecPtr = CFE_ES_LocateMemPoolRecordByID(Handle); + + /* basic sanity check */ + if (!CFE_ES_MemPoolRecordIsMatch(PoolRecPtr, Handle)) + { + CFE_ES_GetAppID(&AppId); + CFE_ES_WriteToSysLog("CFE_ES:getPoolBuf err:Bad handle(0x%08lX) AppId=%lu\n", CFE_RESOURCEID_TO_ULONG(Handle), + CFE_RESOURCEID_TO_ULONG(AppId)); + return (CFE_ES_ERR_RESOURCEID_NOT_VALID); + } + + /* + * Real work begins here. + * If pool is mutex-protected, take the mutex now. + */ + if (OS_ObjectIdDefined(PoolRecPtr->MutexId)) + { + OS_MutSemTake(PoolRecPtr->MutexId); + } + + /* + * Fundamental work is done as a generic routine. + * + * If successful, this gets an offset, which can then + * be translated into a pointer to return to the caller. + */ + Status = CFE_ES_GenPoolGetBlock(&PoolRecPtr->Pool, &DataOffset, Size); + + /* + * Real work ends here. + * If pool is mutex-protected, release the mutex now. + */ + if (OS_ObjectIdDefined(PoolRecPtr->MutexId)) + { + OS_MutSemGive(PoolRecPtr->MutexId); + } + + /* If not successful, return error now */ + if (Status != CFE_SUCCESS) + { + return (Status); + } + + /* Compute the actual buffer address. */ + *BufPtr = CFE_ES_MEMPOOLBUF_C(PoolRecPtr->BaseAddr + DataOffset); + + return (int32)Size; +} + +/* +** CFE_ES_GetPoolBufInfo gets the size of the specified block (if it exists). +*/ +int32 CFE_ES_GetPoolBufInfo(CFE_ES_MemHandle_t Handle, CFE_ES_MemPoolBuf_t BufPtr) +{ + int32 Status; + CFE_ES_MemPoolRecord_t *PoolRecPtr; + size_t DataOffset; + size_t DataSize; + + if (BufPtr == NULL) + { + return CFE_ES_BAD_ARGUMENT; + } + + PoolRecPtr = CFE_ES_LocateMemPoolRecordByID(Handle); + + /* basic sanity check */ + if (!CFE_ES_MemPoolRecordIsMatch(PoolRecPtr, Handle)) + { + return (CFE_ES_ERR_RESOURCEID_NOT_VALID); + } + + /* + * Real work begins here. + * If pool is mutex-protected, take the mutex now. + */ + if (OS_ObjectIdDefined(PoolRecPtr->MutexId)) + { + OS_MutSemTake(PoolRecPtr->MutexId); + } + + DataOffset = (cpuaddr)BufPtr - PoolRecPtr->BaseAddr; + + Status = CFE_ES_GenPoolGetBlockSize(&PoolRecPtr->Pool, &DataSize, DataOffset); + + /* + * Real work ends here. + * If pool is mutex-protected, release the mutex now. + */ + if (OS_ObjectIdDefined(PoolRecPtr->MutexId)) + { + OS_MutSemGive(PoolRecPtr->MutexId); + } + + if (Status == CFE_SUCCESS) + { + /* + * Historically this function returns the size of the buffer + * as an int32. This is not workable for large (64 bit) pools. + */ + Status = (int32)DataSize; + } + + return Status; +} + +/* +** CFE_ES_putPoolBuf returns a block back to the memory pool. +*/ +int32 CFE_ES_PutPoolBuf(CFE_ES_MemHandle_t Handle, CFE_ES_MemPoolBuf_t BufPtr) +{ + CFE_ES_MemPoolRecord_t *PoolRecPtr; + size_t DataSize; + size_t DataOffset; + int32 Status; + + if (BufPtr == NULL) + { + return CFE_ES_BAD_ARGUMENT; + } + + PoolRecPtr = CFE_ES_LocateMemPoolRecordByID(Handle); + + /* basic sanity check */ + if (!CFE_ES_MemPoolRecordIsMatch(PoolRecPtr, Handle)) + { + CFE_ES_WriteToSysLog("CFE_ES:putPoolBuf err:Invalid Memory Handle (0x%08lX).\n", + CFE_RESOURCEID_TO_ULONG(Handle)); + + return (CFE_ES_ERR_RESOURCEID_NOT_VALID); + } + + /* + * Real work begins here. + * If pool is mutex-protected, take the mutex now. + */ + if (OS_ObjectIdDefined(PoolRecPtr->MutexId)) + { + OS_MutSemTake(PoolRecPtr->MutexId); + } + + DataOffset = (cpuaddr)BufPtr - PoolRecPtr->BaseAddr; + + /* + * Fundamental work is done as a generic routine. + * + * If successful, this gets an offset, which can then + * be translated into a pointer to return to the caller. + */ + Status = CFE_ES_GenPoolPutBlock(&PoolRecPtr->Pool, &DataSize, DataOffset); + + /* + * Real work ends here. + * If pool is mutex-protected, release the mutex now. + */ + if (OS_ObjectIdDefined(PoolRecPtr->MutexId)) + { + OS_MutSemGive(PoolRecPtr->MutexId); + } + + /* + * If successful then modify return code to be + * the size of the original buffer that was put (backward compatible) + * + * Otherwise if not successful, log the relevant detail + */ + if (Status == CFE_SUCCESS) + { + Status = (int32)DataSize; + } + else if (Status == CFE_ES_POOL_BLOCK_INVALID) + { + CFE_ES_WriteToSysLog("CFE_ES:putPoolBuf err:Deallocating invalid or corrupt memory block @ 0x%08lX\n", + (unsigned long)BufPtr); + } + else if (Status == CFE_ES_BUFFER_NOT_IN_POOL) + { + CFE_ES_WriteToSysLog("CFE_ES_GenPoolPutBlock err:Bad offset(%lu) outside pool boundary\n", + (unsigned long)DataOffset); + } + + return Status; +} + +/* +** Function: +** CFE_ES_GetMemPoolStats +** +** Purpose: +** +*/ +int32 CFE_ES_GetMemPoolStats(CFE_ES_MemPoolStats_t *BufPtr, CFE_ES_MemHandle_t Handle) +{ + CFE_ES_AppId_t AppId; + CFE_ES_MemPoolRecord_t *PoolRecPtr; + uint16 NumBuckets; + uint16 Idx; + + if (BufPtr == NULL) + { + return CFE_ES_BAD_ARGUMENT; + } + + PoolRecPtr = CFE_ES_LocateMemPoolRecordByID(Handle); + + /* basic sanity check */ + if (!CFE_ES_MemPoolRecordIsMatch(PoolRecPtr, Handle)) + { + CFE_ES_GetAppID(&AppId); + CFE_ES_WriteToSysLog("CFE_ES:getMemPoolStats err:Bad handle(0x%08lX) AppId=%lu\n", + CFE_RESOURCEID_TO_ULONG(Handle), CFE_RESOURCEID_TO_ULONG(AppId)); + return (CFE_ES_ERR_RESOURCEID_NOT_VALID); + } + + /* + * Real work begins here. + * If pool is mutex-protected, take the mutex now. + */ + if (OS_ObjectIdDefined(PoolRecPtr->MutexId)) + { + OS_MutSemTake(PoolRecPtr->MutexId); + } + + /* + * Obtain the free and total byte count + */ + CFE_ES_GenPoolGetUsage(&PoolRecPtr->Pool, &BufPtr->NumFreeBytes, &BufPtr->PoolSize); + + /* + * Obtain the allocation and validation error counts + */ + CFE_ES_GenPoolGetCounts(&PoolRecPtr->Pool, &NumBuckets, &BufPtr->NumBlocksRequested, &BufPtr->CheckErrCtr); + + for (Idx = 0; Idx < CFE_MISSION_ES_POOL_MAX_BUCKETS; ++Idx) + { + CFE_ES_GenPoolGetBucketUsage(&PoolRecPtr->Pool, NumBuckets, &BufPtr->BlockStats[Idx]); + + if (NumBuckets > 0) + { + --NumBuckets; + } + } + + /* + * Real work ends here. + * If pool is mutex-protected, release the mutex now. + */ + if (OS_ObjectIdDefined(PoolRecPtr->MutexId)) + { + OS_MutSemGive(PoolRecPtr->MutexId); + } + + return CFE_SUCCESS; +} + +/* +** Function: +** CFE_ES_ValidateHandle +** +** Purpose: +** Insures that the handle passed in meets all of the requirements of a valid handle. +*/ +bool CFE_ES_ValidateHandle(CFE_ES_MemHandle_t Handle) +{ + CFE_ES_MemPoolRecord_t *PoolRecPtr; + CFE_ES_MemOffset_t TotalSize; + + /* Test #1) Handle must be valid */ + PoolRecPtr = CFE_ES_LocateMemPoolRecordByID(Handle); + if (!CFE_ES_MemPoolRecordIsMatch(PoolRecPtr, Handle)) + { + return false; + } + + /* Test #2) Check critical internal fields are within reason */ + if (!CFE_ES_GenPoolValidateState(&PoolRecPtr->Pool)) + { + return false; + } + + /* Test #3) Check memory address in PSP (allows both RAM and EEPROM) */ + CFE_ES_GenPoolGetUsage(&PoolRecPtr->Pool, NULL, &TotalSize); + if (CFE_PSP_MemValidateRange(PoolRecPtr->BaseAddr, TotalSize, CFE_PSP_MEM_ANY) != CFE_PSP_SUCCESS) + { + return false; + } + + return true; +} diff --git a/modules/es/fsw/src/cfe_es_mempool.h b/modules/es/fsw/src/cfe_es_mempool.h new file mode 100644 index 000000000..5c15f4cf6 --- /dev/null +++ b/modules/es/fsw/src/cfe_es_mempool.h @@ -0,0 +1,200 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/** + * @file + * + * Contains data structure definitions used by the ES mempool implementation. + * + * The ES memory pools are now built on top of the generic memory pool implementation, + * with a layer on top to translate into memory mapped buffer addresses. + */ + +#ifndef CFE_ES_MEMPOOL_H +#define CFE_ES_MEMPOOL_H + +/* +** Include Files +*/ +#include "common_types.h" +#include "cfe_resourceid.h" +#include "cfe_es_generic_pool.h" + +typedef struct +{ + /* + * The generic pool structure + * This must be the first entry in this structure. + */ + CFE_ES_GenPoolRecord_t Pool; + + /* + * The ID of this pool record + */ + CFE_ES_MemHandle_t PoolID; + + /** + * This indicates the start/base address + * of the memory block. + */ + cpuaddr BaseAddr; + + /** + * The "owner" field stores the AppID of the creator of the pool. + * If an exception or other event occurs that causes this app to exit, + * this allows ES to also release the memory pool entry. + * + * It is still possible for pools to be created outside the context of + * an ES app, but in that case the resource cannot be cleaned up if the + * app exits unexpectedly. + */ + CFE_ES_AppId_t OwnerAppID; + + /** + * Optional Mutex for serializing get/put operations + */ + osal_id_t MutexId; +} CFE_ES_MemPoolRecord_t; + +/** + * @brief Obtain an index value correlating to an ES Memory Pool ID + * + * This calculates a zero based integer value that may be used for indexing + * into a local resource table/array. + * + * Index values are only guaranteed to be unique for resources of the same + * type. For instance, the indices corresponding to two [valid] Memory Pool + * IDs will never overlap, but the index of an Memory Pool and a library ID + * may be the same. Furthermore, indices may be reused if a resource is + * deleted and re-created. + * + * @note There is no inverse of this function - indices cannot be converted + * back to the original PoolID value. The caller should retain the original ID + * for future use. + * + * @param[in] PoolID Memory Pool ID to convert + * @param[out] Idx Buffer where the calculated index will be stored + * + * @return Execution status, see @ref CFEReturnCodes + * @retval #CFE_SUCCESS @copybrief CFE_SUCCESS + */ +int32 CFE_ES_MemPoolID_ToIndex(CFE_ES_MemHandle_t PoolID, uint32 *Idx); + +/** + * @brief Locate the Pool table entry correlating with a given Pool ID. + * + * This only returns a pointer to the table entry and does _not_ + * otherwise check/validate the entry. + * + * @param[in] PoolID the Pool ID to locate + * @return pointer to Pool Table entry for the given Pool ID + */ +CFE_ES_MemPoolRecord_t *CFE_ES_LocateMemPoolRecordByID(CFE_ES_MemHandle_t PoolID); + +/** + * @brief Check if a Memory Pool record is in use or free/empty + * + * This routine checks if the Pool table entry is in use or if it is free + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * @param[in] PoolRecPtr pointer to Pool table entry + * @returns true if the entry is in use/configured, or false if it is free/empty + */ +static inline bool CFE_ES_MemPoolRecordIsUsed(const CFE_ES_MemPoolRecord_t *PoolRecPtr) +{ + return CFE_RESOURCEID_TEST_DEFINED(PoolRecPtr->PoolID); +} + +/** + * @brief Get the ID value from a Memory Pool table entry + * + * This routine converts the table entry back to an abstract ID. + * + * @param[in] PoolRecPtr pointer to Pool table entry + * @returns PoolID of entry + */ +static inline CFE_ES_MemHandle_t CFE_ES_MemPoolRecordGetID(const CFE_ES_MemPoolRecord_t *PoolRecPtr) +{ + return (PoolRecPtr->PoolID); +} + +/** + * @brief Marks a Memory Pool table entry as used (not free) + * + * This sets the internal field(s) within this entry, and marks + * it as being associated with the given Pool ID. + * + * @param[in] PoolRecPtr pointer to Pool table entry + * @param[in] PendingId the Pool ID of this entry + */ +static inline void CFE_ES_MemPoolRecordSetUsed(CFE_ES_MemPoolRecord_t *PoolRecPtr, CFE_ResourceId_t PendingId) +{ + PoolRecPtr->PoolID = CFE_ES_MEMHANDLE_C(PendingId); +} + +/** + * @brief Set a Memory Pool record table entry free (not used) + * + * This clears the internal field(s) within this entry, and allows the + * memory to be re-used in the future. + * + * @param[in] PoolRecPtr pointer to Pool table entry + */ +static inline void CFE_ES_MemPoolRecordSetFree(CFE_ES_MemPoolRecord_t *PoolRecPtr) +{ + PoolRecPtr->PoolID = CFE_ES_MEMHANDLE_UNDEFINED; +} + +/** + * @brief Check if an Mem Pool record is a match for the given Pool ID + * + * This routine confirms that the previously-located record is valid + * and matches the expected Pool ID. + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * @param[in] PoolRecPtr pointer to Pool table entry + * @param[in] PoolID expected Pool ID + * @returns true if the entry matches the given pool ID + */ +static inline bool CFE_ES_MemPoolRecordIsMatch(const CFE_ES_MemPoolRecord_t *PoolRecPtr, CFE_ES_MemHandle_t PoolID) +{ + return (PoolRecPtr != NULL && CFE_RESOURCEID_TEST_EQUAL(PoolRecPtr->PoolID, PoolID)); +} + +/** + * @brief Check if a Pool ID table slot is used + * + * Checks if a table slot is available for a potential new ID + * This is a helper function intended to be used with + * CFE_ResourceId_FindNext() for allocating new IDs + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * @param[in] CheckId pending/candidate Pool ID to check + * @returns true if the table slot for the ID is occupied, false if available + */ +bool CFE_ES_CheckMemPoolSlotUsed(CFE_ResourceId_t CheckId); + +#endif /* CFE_ES_MEMPOOL_H */ diff --git a/modules/es/fsw/src/cfe_es_module_all.h b/modules/es/fsw/src/cfe_es_module_all.h new file mode 100644 index 000000000..116a890f8 --- /dev/null +++ b/modules/es/fsw/src/cfe_es_module_all.h @@ -0,0 +1,57 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/** + * @file + * + * Encapsulates all ES module internal header files, as well + * as the public API from all other CFE core modules, OSAL, and PSP. + * + * This simplifies the set of include files that need to be put at the + * start of every source file. + */ + +#ifndef CFE_ES_MODULE_ALL_H +#define CFE_ES_MODULE_ALL_H + +/* +** Includes +*/ +#include "cfe.h" +#include "cfe_platform_cfg.h" + +#include "cfe_msgids.h" +#include "cfe_perfids.h" + +#include "cfe_es_core_internal.h" +#include "cfe_es_apps.h" +#include "cfe_es_cds.h" +#include "cfe_es_perf.h" +#include "cfe_es_generic_pool.h" +#include "cfe_es_mempool.h" +#include "cfe_es_global.h" +#include "cfe_es_cds_mempool.h" +#include "cfe_es_events.h" +#include "cfe_es_start.h" +#include "cfe_es_task.h" +#include "cfe_es_resource.h" +#include "cfe_es_log.h" + +#endif /* CFE_ES_MODULE_ALL_H */ diff --git a/modules/es/fsw/src/cfe_es_objtab.c b/modules/es/fsw/src/cfe_es_objtab.c new file mode 100644 index 000000000..2d8782e75 --- /dev/null +++ b/modules/es/fsw/src/cfe_es_objtab.c @@ -0,0 +1,137 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/* +** File: +** cfe_es_objtab.c +** +** Purpose: +** This file contains the OS_object_table for MAP Build1. +** +** References: +** Flight Software Branch C Coding Standard Version 1.0a +** cFE Flight Software Application Developers Guide +** +** Notes: +** +*/ + +/* +** Include files +*/ +#include "cfe_es_module_all.h" + +/* Init functions from other modules are in separate headers */ +#include "cfe_evs_core_internal.h" +#include "cfe_fs_core_internal.h" +#include "cfe_sb_core_internal.h" +#include "cfe_tbl_core_internal.h" +#include "cfe_time_core_internal.h" + +/* +** +** ES_object_table +** Note: The name field in this table should be no more than OS_MAX_API_NAME -1 characters. +** +*/ +CFE_ES_ObjectTable_t CFE_ES_ObjectTable[CFE_PLATFORM_ES_OBJECT_TABLE_SIZE] = { + /* + ** Spare entries -- The spares should be distributed evenly through this table + */ + {.ObjectType = CFE_ES_NULL_ENTRY}, + {.ObjectType = CFE_ES_NULL_ENTRY}, + {.ObjectType = CFE_ES_NULL_ENTRY}, + {.ObjectType = CFE_ES_NULL_ENTRY}, + + /* + ** cFE core early initialization calls. These must be done before the tasks start + */ + {.ObjectType = CFE_ES_FUNCTION_CALL, + .ObjectName = "CFE_ES_CDSEarlyInit", + .FuncPtrUnion.FunctionPtr = CFE_ES_CDS_EarlyInit}, + {.ObjectType = CFE_ES_NULL_ENTRY}, + {.ObjectType = CFE_ES_FUNCTION_CALL, + .ObjectName = "CFE_EVS_EarlyInit", + .FuncPtrUnion.FunctionPtr = CFE_EVS_EarlyInit}, + {.ObjectType = CFE_ES_NULL_ENTRY}, + {.ObjectType = CFE_ES_FUNCTION_CALL, + .ObjectName = "CFE_SB_EarlyInit", + .FuncPtrUnion.FunctionPtr = CFE_SB_EarlyInit}, + {.ObjectType = CFE_ES_NULL_ENTRY}, + {.ObjectType = CFE_ES_FUNCTION_CALL, + .ObjectName = "CFE_TIME_EarlyInit", + .FuncPtrUnion.FunctionPtr = CFE_TIME_EarlyInit}, + {.ObjectType = CFE_ES_NULL_ENTRY}, + {.ObjectType = CFE_ES_FUNCTION_CALL, + .ObjectName = "CFE_TBL_EarlyInit", + .FuncPtrUnion.FunctionPtr = CFE_TBL_EarlyInit}, + {.ObjectType = CFE_ES_NULL_ENTRY}, + {.ObjectType = CFE_ES_FUNCTION_CALL, + .ObjectName = "CFE_FS_EarlyInit", + .FuncPtrUnion.FunctionPtr = CFE_FS_EarlyInit}, + + /* + ** Spare entries + */ + {.ObjectType = CFE_ES_NULL_ENTRY}, + {.ObjectType = CFE_ES_NULL_ENTRY}, + + /* + ** cFE core tasks + */ + {.ObjectType = CFE_ES_CORE_TASK, + .ObjectName = "CFE_EVS", + .FuncPtrUnion.MainTaskPtr = CFE_EVS_TaskMain, + .ObjectPriority = CFE_PLATFORM_EVS_START_TASK_PRIORITY, + .ObjectSize = CFE_PLATFORM_EVS_START_TASK_STACK_SIZE}, + {.ObjectType = CFE_ES_NULL_ENTRY}, + {.ObjectType = CFE_ES_CORE_TASK, + .ObjectName = "CFE_SB", + .FuncPtrUnion.MainTaskPtr = CFE_SB_TaskMain, + .ObjectPriority = CFE_PLATFORM_SB_START_TASK_PRIORITY, + .ObjectSize = CFE_PLATFORM_SB_START_TASK_STACK_SIZE}, + {.ObjectType = CFE_ES_NULL_ENTRY}, + {.ObjectType = CFE_ES_CORE_TASK, + .ObjectName = "CFE_ES", + .FuncPtrUnion.MainTaskPtr = CFE_ES_TaskMain, + .ObjectPriority = CFE_PLATFORM_ES_START_TASK_PRIORITY, + .ObjectSize = CFE_PLATFORM_ES_START_TASK_STACK_SIZE}, + {.ObjectType = CFE_ES_NULL_ENTRY}, + {.ObjectType = CFE_ES_CORE_TASK, + .ObjectName = "CFE_TIME", + .FuncPtrUnion.MainTaskPtr = CFE_TIME_TaskMain, + .ObjectPriority = CFE_PLATFORM_TIME_START_TASK_PRIORITY, + .ObjectSize = CFE_PLATFORM_TIME_START_TASK_STACK_SIZE}, + {.ObjectType = CFE_ES_NULL_ENTRY}, + {.ObjectType = CFE_ES_CORE_TASK, + .ObjectName = "CFE_TBL", + .FuncPtrUnion.MainTaskPtr = CFE_TBL_TaskMain, + .ObjectPriority = CFE_PLATFORM_TBL_START_TASK_PRIORITY, + .ObjectSize = CFE_PLATFORM_TBL_START_TASK_STACK_SIZE}, + + /* + ** Spare entries + */ + {.ObjectType = CFE_ES_NULL_ENTRY}, + {.ObjectType = CFE_ES_NULL_ENTRY}, + {.ObjectType = CFE_ES_NULL_ENTRY}, + {.ObjectType = CFE_ES_NULL_ENTRY} + +}; diff --git a/modules/es/fsw/src/cfe_es_perf.c b/modules/es/fsw/src/cfe_es_perf.c new file mode 100644 index 000000000..60a00b3aa --- /dev/null +++ b/modules/es/fsw/src/cfe_es_perf.c @@ -0,0 +1,724 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/* +** File: cfe_es_perf.c +** +** Purpose: This file contains the functions that implement the software timing +** performance markers. +** +*/ + +/* +** Include Section +*/ +#include "cfe_es_module_all.h" + +#include + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* Name: CFE_ES_SetupPerfVariables */ +/* */ +/* Purpose:This function initializes filter mask,trigger mask, data & state vals */ +/* */ +/* Assumptions and Notes: This gets called from CFE_ES_Main() at startup */ +/* This code must be called before any other task or code that would use */ +/* CFE_ES_PerfLogEntry() / CFE_ES_PerfLogExit() functions */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +void CFE_ES_SetupPerfVariables(uint32 ResetType) +{ + /* Create a constant union - + * The "Endian" field will have "0x01" on a big endian processor + * and will have value "0x00" on a little endian processor. + */ + const union + { + uint16 Word; + uint8 Endian; + } EndianCheck = {.Word = 0x0100}; + + uint32 i; + CFE_ES_PerfData_t *Perf; + + /* + ** Set the pointer to the data area + */ + Perf = &CFE_ES_Global.ResetDataPtr->Perf; + + if (ResetType == CFE_PSP_RST_TYPE_PROCESSOR) + { + /* + ** On a processor reset, just IDLE the data + ** collection so the ground can dump the data + */ + Perf->MetaData.State = CFE_ES_PERF_IDLE; + } + else + { + + Perf->MetaData.Version = 1; + Perf->MetaData.Endian = EndianCheck.Endian; + Perf->MetaData.TimerTicksPerSecond = CFE_PSP_GetTimerTicksPerSecond(); + Perf->MetaData.TimerLow32Rollover = CFE_PSP_GetTimerLow32Rollover(); + + /* set data collection state to waiting for command state */ + Perf->MetaData.State = CFE_ES_PERF_IDLE; + Perf->MetaData.Mode = CFE_ES_PERF_TRIGGER_START; + Perf->MetaData.TriggerCount = 0; + Perf->MetaData.DataStart = 0; + Perf->MetaData.DataEnd = 0; + Perf->MetaData.DataCount = 0; + Perf->MetaData.InvalidMarkerReported = false; + Perf->MetaData.FilterTriggerMaskSize = CFE_ES_PERF_32BIT_WORDS_IN_MASK; + + for (i = 0; i < CFE_ES_PERF_32BIT_WORDS_IN_MASK; i++) + { + Perf->MetaData.FilterMask[i] = CFE_PLATFORM_ES_PERF_FILTMASK_INIT; + Perf->MetaData.TriggerMask[i] = CFE_PLATFORM_ES_PERF_TRIGMASK_INIT; + } + } +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_GetPerfLogDumpRemaining() -- */ +/* Estimate the number of perf log entries left to write */ +/* This is used for telemetry/progress reporting for the perf log dump request */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +uint32 CFE_ES_GetPerfLogDumpRemaining(void) +{ + CFE_ES_PerfDumpGlobal_t *PerfDumpState = &CFE_ES_Global.BackgroundPerfDumpState; + CFE_ES_PerfDumpState_t CurrentState = PerfDumpState->CurrentState; + uint32 Result; + CFE_ES_PerfData_t * Perf; + + /* + ** Set the pointer to the data area + */ + Perf = &CFE_ES_Global.ResetDataPtr->Perf; + + /* note this reads the data "live" without exclusion and as such it + * may change even between checking the state and checking the value. + * This shouldn't be a big deal, as the result should still be meaningful + * for a progress report, and the actual 32-bit counters should be atomic */ + if (CurrentState > CFE_ES_PerfDumpState_IDLE && CurrentState < CFE_ES_PerfDumpState_WRITE_PERF_ENTRIES) + { + /* dump is requested but not yet to entry writing state, + * report the entire data count from perf log */ + Result = Perf->MetaData.DataCount; + } + else if (CurrentState == CFE_ES_PerfDumpState_WRITE_PERF_ENTRIES) + { + /* dump is in active writing state, + * report the block counter (number remaining) */ + Result = PerfDumpState->StateCounter; + } + else + { + /* no dump active or dump is complete, report 0 */ + Result = 0; + } + + return Result; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_StartPerfDataCmd() -- */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +int32 CFE_ES_StartPerfDataCmd(const CFE_ES_StartPerfDataCmd_t *data) +{ + const CFE_ES_StartPerfCmd_Payload_t *CmdPtr = &data->Payload; + CFE_ES_PerfDumpGlobal_t * PerfDumpState = &CFE_ES_Global.BackgroundPerfDumpState; + CFE_ES_PerfData_t * Perf; + + /* + ** Set the pointer to the data area + */ + Perf = &CFE_ES_Global.ResetDataPtr->Perf; + + /* Ensure there is no file write in progress before proceeding */ + if (PerfDumpState->CurrentState == CFE_ES_PerfDumpState_IDLE && + PerfDumpState->PendingState == CFE_ES_PerfDumpState_IDLE) + { + /* Make sure Trigger Mode is valid */ + /* cppcheck-suppress unsignedPositive */ + if ((CmdPtr->TriggerMode >= CFE_ES_PERF_TRIGGER_START) && (CmdPtr->TriggerMode < CFE_ES_PERF_MAX_MODES)) + { + + CFE_ES_Global.TaskData.CommandCounter++; + + /* Taking lock here as this might be changing states from one active mode to another. + * In that case, need to make sure that the log is not written to while resetting the counters. */ + OS_MutSemTake(CFE_ES_Global.PerfDataMutex); + Perf->MetaData.Mode = CmdPtr->TriggerMode; + Perf->MetaData.TriggerCount = 0; + Perf->MetaData.DataStart = 0; + Perf->MetaData.DataEnd = 0; + Perf->MetaData.DataCount = 0; + Perf->MetaData.InvalidMarkerReported = false; + Perf->MetaData.State = CFE_ES_PERF_WAITING_FOR_TRIGGER; /* this must be done last */ + OS_MutSemGive(CFE_ES_Global.PerfDataMutex); + + CFE_EVS_SendEvent(CFE_ES_PERF_STARTCMD_EID, CFE_EVS_EventType_DEBUG, + "Start collecting performance data cmd received, trigger mode = %d", + (int)CmdPtr->TriggerMode); + } + else + { + CFE_ES_Global.TaskData.CommandErrorCounter++; + CFE_EVS_SendEvent(CFE_ES_PERF_STARTCMD_TRIG_ERR_EID, CFE_EVS_EventType_ERROR, + "Cannot start collecting performance data, trigger mode (%d) out of range (%d to %d)", + (int)CmdPtr->TriggerMode, (int)CFE_ES_PERF_TRIGGER_START, (int)CFE_ES_PERF_TRIGGER_END); + } /* end if */ + } + else + { + CFE_ES_Global.TaskData.CommandErrorCounter++; + CFE_EVS_SendEvent(CFE_ES_PERF_STARTCMD_ERR_EID, CFE_EVS_EventType_ERROR, + "Cannot start collecting performance data,perf data write in progress"); + } /* end if */ + + return CFE_SUCCESS; +} /* End of CFE_ES_StartPerfDataCmd() */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_StopPerfDataCmd() -- */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +int32 CFE_ES_StopPerfDataCmd(const CFE_ES_StopPerfDataCmd_t *data) +{ + const CFE_ES_StopPerfCmd_Payload_t *CmdPtr = &data->Payload; + CFE_ES_PerfDumpGlobal_t * PerfDumpState = &CFE_ES_Global.BackgroundPerfDumpState; + CFE_ES_PerfData_t * Perf; + int32 Status; + + /* + ** Set the pointer to the data area + */ + Perf = &CFE_ES_Global.ResetDataPtr->Perf; + + /* Ensure there is no file write in progress before proceeding */ + /* note - also need to check the PendingState here, in case this command + * was sent twice in succession and the background task has not awakened yet */ + if (PerfDumpState->CurrentState == CFE_ES_PerfDumpState_IDLE && + PerfDumpState->PendingState == CFE_ES_PerfDumpState_IDLE) + { + Perf->MetaData.State = CFE_ES_PERF_IDLE; + + /* Copy out the string, using default if unspecified */ + Status = CFE_FS_ParseInputFileNameEx(PerfDumpState->DataFileName, CmdPtr->DataFileName, + sizeof(PerfDumpState->DataFileName), sizeof(CmdPtr->DataFileName), + CFE_PLATFORM_ES_DEFAULT_PERF_DUMP_FILENAME, + CFE_FS_GetDefaultMountPoint(CFE_FS_FileCategory_BINARY_DATA_DUMP), + CFE_FS_GetDefaultExtension(CFE_FS_FileCategory_BINARY_DATA_DUMP)); + + if (Status == CFE_SUCCESS) + { + PerfDumpState->PendingState = CFE_ES_PerfDumpState_INIT; + CFE_ES_BackgroundWakeup(); + + CFE_ES_Global.TaskData.CommandCounter++; + + CFE_EVS_SendEvent(CFE_ES_PERF_STOPCMD_EID, CFE_EVS_EventType_DEBUG, + "Perf Stop Cmd Rcvd, will write %d entries.%dmS dly every %d entries", + (int)Perf->MetaData.DataCount, (int)CFE_PLATFORM_ES_PERF_CHILD_MS_DELAY, + (int)CFE_PLATFORM_ES_PERF_ENTRIES_BTWN_DLYS); + } + else + { + CFE_ES_Global.TaskData.CommandErrorCounter++; + CFE_EVS_SendEvent(CFE_ES_PERF_LOG_ERR_EID, CFE_EVS_EventType_ERROR, "Error parsing filename, RC = %d", + (int)Status); + } + + } /* if data to write == 0 */ + else + { + + CFE_ES_Global.TaskData.CommandErrorCounter++; + CFE_EVS_SendEvent(CFE_ES_PERF_STOPCMD_ERR2_EID, CFE_EVS_EventType_ERROR, + "Stop performance data cmd ignored,perf data write in progress"); + } /* end if */ + + return CFE_SUCCESS; +} /* End of CFE_ES_StopPerfDataCmd() */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* Function: CFE_ES_RunPerfLogDump() */ +/* */ +/* Purpose: */ +/* Write performance data to a file */ +/* This is implemented as a state machine that is invoked in the background */ +/* Each iteration should perform a limited amount of work, which will resume */ +/* on the next iteration. State is kept in a global structure. */ +/* */ +/* Arguments: */ +/* None */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +bool CFE_ES_RunPerfLogDump(uint32 ElapsedTime, void *Arg) +{ + CFE_ES_PerfDumpGlobal_t *State = (CFE_ES_PerfDumpGlobal_t *)Arg; + int32 Status; + CFE_FS_Header_t FileHdr; + size_t BlockSize; + CFE_ES_PerfData_t * Perf; + + /* + ** Set the pointer to the data area + */ + Perf = &CFE_ES_Global.ResetDataPtr->Perf; + + /* + * each time this background job is re-entered after a time delay, + * accumulate a work credit amount based on the elapsed time. + * + * This implements work-throttling as a form of cooperative + * CPU sharing with other low priority background jobs. + */ + State->WorkCredit += (ElapsedTime * CFE_PLATFORM_ES_PERF_ENTRIES_BTWN_DLYS) / CFE_PLATFORM_ES_PERF_CHILD_MS_DELAY; + + /* + * do not allow credit to accumulate indefinitely - + * after a long idle time this would defeat the purpose. + */ + if (State->WorkCredit > CFE_PLATFORM_ES_PERF_ENTRIES_BTWN_DLYS) + { + State->WorkCredit = CFE_PLATFORM_ES_PERF_ENTRIES_BTWN_DLYS; + } + + while (State->WorkCredit > 0) + { + --State->WorkCredit; + + if (State->PendingState != State->CurrentState) + { + /* + * Handle state change/entry logic. + * Zero the block counter register (may be changed later). + */ + State->StateCounter = 0; + + switch (State->PendingState) + { + case CFE_ES_PerfDumpState_OPEN_FILE: + /* Create the file to dump to */ + Status = OS_OpenCreate(&State->FileDesc, State->DataFileName, + OS_FILE_FLAG_CREATE | OS_FILE_FLAG_TRUNCATE, OS_WRITE_ONLY); + if (Status < 0) + { + State->FileDesc = OS_OBJECT_ID_UNDEFINED; + CFE_EVS_SendEvent(CFE_ES_PERF_LOG_ERR_EID, CFE_EVS_EventType_ERROR, + "Error creating file %s, RC = %d", State->DataFileName, (int)Status); + } + State->FileSize = 0; + break; + + case CFE_ES_PerfDumpState_DELAY: + /* + * Add a state entry delay before locking the "Perf" structure to + * ensure that any foreground task that may have been writing to this + * structure has completed its access. + * + * Note that the state should already have been set to IDLE, so + * no new writes will start, this is just to yield the CPU such that + * any already-started writes may finish. + * + * This can be done by simply zeroing out the current credit, + * which will cause this loop to exit for now and resume after + * some time delay (does not really matter how much time). + */ + State->WorkCredit = 0; + break; + + case CFE_ES_PerfDumpState_LOCK_DATA: + OS_MutSemTake(CFE_ES_Global.PerfDataMutex); + break; + + case CFE_ES_PerfDumpState_WRITE_FS_HDR: + case CFE_ES_PerfDumpState_WRITE_PERF_METADATA: + State->StateCounter = 1; + break; + + case CFE_ES_PerfDumpState_WRITE_PERF_ENTRIES: + State->DataPos = Perf->MetaData.DataStart; + State->StateCounter = Perf->MetaData.DataCount; + break; + + case CFE_ES_PerfDumpState_UNLOCK_DATA: + OS_MutSemGive(CFE_ES_Global.PerfDataMutex); + break; + + case CFE_ES_PerfDumpState_CLOSE_FILE: + /* close the fd */ + if (OS_ObjectIdDefined(State->FileDesc)) + { + OS_close(State->FileDesc); + State->FileDesc = OS_OBJECT_ID_UNDEFINED; + } + break; + + default: + break; + } + + State->CurrentState = State->PendingState; + } + + if (State->CurrentState == CFE_ES_PerfDumpState_IDLE) + { + break; + } + + if (State->StateCounter == 0) + { + /* + * State is finished, do any final error checking and logging + * + * Default transition is to the next state by numeric value. + * This prevent endless looping in the same state. + * + * The switch statement can override this transition, however, + * based on any relevant error checks. + */ + State->PendingState = 1 + State->CurrentState; + if (State->PendingState >= CFE_ES_PerfDumpState_MAX) + { + State->PendingState = CFE_ES_PerfDumpState_IDLE; + } + switch (State->CurrentState) + { + case CFE_ES_PerfDumpState_OPEN_FILE: + if (!OS_ObjectIdDefined(State->FileDesc)) + { + State->PendingState = CFE_ES_PerfDumpState_IDLE; + } /* end if */ + break; + + case CFE_ES_PerfDumpState_WRITE_PERF_ENTRIES: + CFE_EVS_SendEvent(CFE_ES_PERF_DATAWRITTEN_EID, CFE_EVS_EventType_DEBUG, + "%s written:Size=%lu,EntryCount=%lu", State->DataFileName, + (unsigned long)State->FileSize, (unsigned long)Perf->MetaData.DataCount); + break; + + default: + break; + } + } + else + { + /* + * State is in progress, perform work item(s) as required + */ + Status = 0; + BlockSize = 0; + switch (State->CurrentState) + { + case CFE_ES_PerfDumpState_WRITE_FS_HDR: + /* Zero cFE header, then fill in fields */ + CFE_FS_InitHeader(&FileHdr, CFE_ES_PERF_LOG_DESC, CFE_FS_SubType_ES_PERFDATA); + /* predicted total length of final output */ + FileHdr.Length = + sizeof(CFE_ES_PerfMetaData_t) + (Perf->MetaData.DataCount * sizeof(CFE_ES_PerfDataEntry_t)); + /* write the cFE header to the file */ + Status = CFE_FS_WriteHeader(State->FileDesc, &FileHdr); + BlockSize = sizeof(CFE_FS_Header_t); + break; + + case CFE_ES_PerfDumpState_WRITE_PERF_METADATA: + /* write the performance metadata to the file */ + BlockSize = sizeof(CFE_ES_PerfMetaData_t); + Status = OS_write(State->FileDesc, &Perf->MetaData, BlockSize); + break; + + case CFE_ES_PerfDumpState_WRITE_PERF_ENTRIES: + BlockSize = sizeof(CFE_ES_PerfDataEntry_t); + Status = OS_write(State->FileDesc, &Perf->DataBuffer[State->DataPos], BlockSize); + + ++State->DataPos; + if (State->DataPos >= CFE_PLATFORM_ES_PERF_DATA_BUFFER_SIZE) + { + State->DataPos = 0; + } + break; + + default: + break; + } + + if (BlockSize != 0) + { + if (Status != BlockSize) + { + CFE_ES_FileWriteByteCntErr(State->DataFileName, BlockSize, Status); + + /* skip to cleanup */ + if (State->CurrentState < CFE_ES_PerfDumpState_CLEANUP) + { + State->PendingState = CFE_ES_PerfDumpState_CLEANUP; + } + } + else + { + State->FileSize += BlockSize; + } + } + + --State->StateCounter; + } + } + + /* + * Return "true" if activity is ongoing, or "false" if not active + */ + return (State->CurrentState != CFE_ES_PerfDumpState_IDLE); +} /* end CFE_ES_PerfLogDump */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_SetPerfFilterMaskCmd() -- */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +int32 CFE_ES_SetPerfFilterMaskCmd(const CFE_ES_SetPerfFilterMaskCmd_t *data) +{ + const CFE_ES_SetPerfFilterMaskCmd_Payload_t *cmd = &data->Payload; + CFE_ES_PerfData_t * Perf; + + /* + ** Set the pointer to the data area + */ + Perf = &CFE_ES_Global.ResetDataPtr->Perf; + + if (cmd->FilterMaskNum < CFE_ES_PERF_32BIT_WORDS_IN_MASK) + { + + Perf->MetaData.FilterMask[cmd->FilterMaskNum] = cmd->FilterMask; + + CFE_EVS_SendEvent(CFE_ES_PERF_FILTMSKCMD_EID, CFE_EVS_EventType_DEBUG, + "Set Performance Filter Mask Cmd rcvd, num %u, val 0x%08X", (unsigned int)cmd->FilterMaskNum, + (unsigned int)cmd->FilterMask); + + CFE_ES_Global.TaskData.CommandCounter++; + } + else + { + CFE_EVS_SendEvent(CFE_ES_PERF_FILTMSKERR_EID, CFE_EVS_EventType_ERROR, + "Performance Filter Mask Cmd Error,Index(%u)out of range(%u)", + (unsigned int)cmd->FilterMaskNum, (unsigned int)CFE_ES_PERF_32BIT_WORDS_IN_MASK); + + CFE_ES_Global.TaskData.CommandErrorCounter++; + } + + return CFE_SUCCESS; +} /* End of CFE_ES_SetPerfFilterMaskCmd() */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_SetPerfTriggerMaskCmd() -- */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +int32 CFE_ES_SetPerfTriggerMaskCmd(const CFE_ES_SetPerfTriggerMaskCmd_t *data) +{ + const CFE_ES_SetPerfTrigMaskCmd_Payload_t *cmd = &data->Payload; + CFE_ES_PerfData_t * Perf; + + /* + ** Set the pointer to the data area + */ + Perf = &CFE_ES_Global.ResetDataPtr->Perf; + + if (cmd->TriggerMaskNum < CFE_ES_PERF_32BIT_WORDS_IN_MASK) + { + + Perf->MetaData.TriggerMask[cmd->TriggerMaskNum] = cmd->TriggerMask; + + CFE_EVS_SendEvent(CFE_ES_PERF_TRIGMSKCMD_EID, CFE_EVS_EventType_DEBUG, + "Set Performance Trigger Mask Cmd rcvd,num %u, val 0x%08X", (unsigned int)cmd->TriggerMaskNum, + (unsigned int)cmd->TriggerMask); + + CFE_ES_Global.TaskData.CommandCounter++; + } + else + { + CFE_EVS_SendEvent(CFE_ES_PERF_TRIGMSKERR_EID, CFE_EVS_EventType_ERROR, + "Performance Trigger Mask Cmd Error,Index(%u)out of range(%u)", + (unsigned int)cmd->TriggerMaskNum, (unsigned int)CFE_ES_PERF_32BIT_WORDS_IN_MASK); + + CFE_ES_Global.TaskData.CommandErrorCounter++; + } + + return CFE_SUCCESS; +} /* End of CFE_ES_SetPerfTriggerMaskCmd() */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* Name: CFE_ES_PerfLogAdd */ +/* */ +/* Purpose: This function adds a new entry to the data buffer. */ +/* */ +/* Assumptions and Notes: */ +/* */ +/* This function implements a circular buffer using an array. */ +/* DataStart points to first stored entry */ +/* DataEnd points to next available entry */ +/* if DataStart == DataEnd then the buffer is either empty or full */ +/* depending on the value of the DataCount */ +/* */ +/* Time is stored as 2 32 bit integers, (TimerLower32, TimerUpper32): */ +/* TimerLower32 is the curent value of the hardware timer register. */ +/* TimerUpper32 is the number of times the timer has rolled over. */ +/* */ +/* Time is stored as a absolute time instead of a relative time between log */ +/* entries. This will yield better accuracy since storing relative time between */ +/* entries will accumulate (rounding/sampling) errors over time. It also is */ +/* faster since the time does not need to be calculated. */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +void CFE_ES_PerfLogAdd(uint32 Marker, uint32 EntryExit) +{ + CFE_ES_PerfDataEntry_t EntryData; + uint32 DataEnd; + CFE_ES_PerfData_t * Perf; + + /* + ** Set the pointer to the data area + */ + Perf = &CFE_ES_Global.ResetDataPtr->Perf; + + /* + * If the global state is idle, exit immediately without locking or doing anything + */ + if (Perf->MetaData.State == CFE_ES_PERF_IDLE) + { + return; + } + + /* if marker is out of range... */ + if (Marker >= CFE_MISSION_ES_PERF_MAX_IDS) + { + /* if marker has not been reported previously ... */ + if (Perf->MetaData.InvalidMarkerReported == false) + { + CFE_ES_WriteToSysLog("ES PERF:Invalid performance marker %d,max is %d\n", (unsigned int)Marker, + (CFE_MISSION_ES_PERF_MAX_IDS - 1)); + Perf->MetaData.InvalidMarkerReported = true; + } /* end if */ + + return; + } /* end if */ + + /* + * check if this ID is filtered. + * This is also done outside the lock - + * normally masks should NOT be changed while perf log is active / non-idle, + * so although this is reading a global it should be constant, and this avoids + * locking (and potential task switch) if the data is ultimately not going to + * be written to the log. + */ + if (!CFE_ES_TEST_LONG_MASK(Perf->MetaData.FilterMask, Marker)) + { + return; + } + + /* + * prepare the entry data (timestamp) before locking, + * just in case the locking operation incurs a delay + */ + EntryData.Data = (Marker | (EntryExit << CFE_MISSION_ES_PERF_EXIT_BIT)); + CFE_PSP_Get_Timebase(&EntryData.TimerUpper32, &EntryData.TimerLower32); + + /* + * Acquire the perflog mutex before writing into the shared area. + * Note this lock is held for long periods while a background dump + * is taking place, but the dump should never be active at the + * same time that a capture/record is taking place. + */ + OS_MutSemTake(CFE_ES_Global.PerfDataMutex); + + /* + * Confirm that the global is still non-idle after lock + * (state could become idle while getting lock) + */ + if (Perf->MetaData.State != CFE_ES_PERF_IDLE) + { + /* copy data to next perflog slot */ + DataEnd = Perf->MetaData.DataEnd; + Perf->DataBuffer[DataEnd] = EntryData; + + ++DataEnd; + if (DataEnd >= CFE_PLATFORM_ES_PERF_DATA_BUFFER_SIZE) + { + DataEnd = 0; + } + Perf->MetaData.DataEnd = DataEnd; + + /* we have filled up the buffer */ + if (Perf->MetaData.DataCount < CFE_PLATFORM_ES_PERF_DATA_BUFFER_SIZE) + { + Perf->MetaData.DataCount++; + } + else + { + /* after the buffer fills up start and end point to the same entry since we + are now overwriting old data */ + Perf->MetaData.DataStart = Perf->MetaData.DataEnd; + } + + /* waiting for trigger */ + if (Perf->MetaData.State == CFE_ES_PERF_WAITING_FOR_TRIGGER) + { + if (CFE_ES_TEST_LONG_MASK(Perf->MetaData.TriggerMask, Marker)) + { + Perf->MetaData.State = CFE_ES_PERF_TRIGGERED; + } + } + + /* triggered */ + if (Perf->MetaData.State == CFE_ES_PERF_TRIGGERED) + { + Perf->MetaData.TriggerCount++; + if (Perf->MetaData.Mode == CFE_ES_PERF_TRIGGER_START) + { + if (Perf->MetaData.TriggerCount >= CFE_PLATFORM_ES_PERF_DATA_BUFFER_SIZE) + { + Perf->MetaData.State = CFE_ES_PERF_IDLE; + } + } + else if (Perf->MetaData.Mode == CFE_ES_PERF_TRIGGER_CENTER) + { + if (Perf->MetaData.TriggerCount >= CFE_PLATFORM_ES_PERF_DATA_BUFFER_SIZE / 2) + { + Perf->MetaData.State = CFE_ES_PERF_IDLE; + } + } + else if (Perf->MetaData.Mode == CFE_ES_PERF_TRIGGER_END) + { + Perf->MetaData.State = CFE_ES_PERF_IDLE; + } + } + } + + OS_MutSemGive(CFE_ES_Global.PerfDataMutex); + +} /* end CFE_ES_PerfLogAdd */ diff --git a/modules/es/fsw/src/cfe_es_perf.h b/modules/es/fsw/src/cfe_es_perf.h new file mode 100644 index 000000000..fa0a43599 --- /dev/null +++ b/modules/es/fsw/src/cfe_es_perf.h @@ -0,0 +1,135 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/** + * @file + * + * Purpose: Performance Analyzer data structures + * + * Design Notes: + * + * References: + * + */ + +#ifndef CFE_ES_PERF_H +#define CFE_ES_PERF_H + +/* +** Include Files +*/ +#include "common_types.h" +#include "osconfig.h" +#include "cfe_es_api_typedefs.h" + +/* +** Defines +*/ + +enum CFE_ES_PerfState_t +{ + CFE_ES_PERF_IDLE = 0, + CFE_ES_PERF_WAITING_FOR_TRIGGER, + CFE_ES_PERF_TRIGGERED, + CFE_ES_PERF_MAX_STATES +}; + +enum CFE_ES_PerfMode_t +{ + CFE_ES_PERF_TRIGGER_START = 0, + CFE_ES_PERF_TRIGGER_CENTER, + CFE_ES_PERF_TRIGGER_END, + CFE_ES_PERF_MAX_MODES +}; + +/* + * Perflog Dump Background Job states + * + * Writing performance log data is now handled by a state machine that runs + * as a background job in Executive services. When a performance log dump is + * pending, each iteration of the state machine performs a limited amount of + * work. Each iteration resumes work where the last iteration left off. + */ +typedef enum +{ + CFE_ES_PerfDumpState_IDLE, /* Placeholder for idle, no action */ + CFE_ES_PerfDumpState_INIT, /* Placeholder for entry/init, no action */ + CFE_ES_PerfDumpState_OPEN_FILE, /* Opening of the output file */ + CFE_ES_PerfDumpState_DELAY, /* Wait-state to ensure in-progress writes are finished */ + CFE_ES_PerfDumpState_LOCK_DATA, /* Locking of the global data structure */ + CFE_ES_PerfDumpState_WRITE_FS_HDR, /* Write the CFE FS file header */ + CFE_ES_PerfDumpState_WRITE_PERF_METADATA, /* Write the Perf global metadata */ + CFE_ES_PerfDumpState_WRITE_PERF_ENTRIES, /* Write the Perf Log entries (throttled) */ + CFE_ES_PerfDumpState_CLEANUP, /* Placeholder for cleanup, no action */ + CFE_ES_PerfDumpState_UNLOCK_DATA, /* Unlocking of the global data structure */ + CFE_ES_PerfDumpState_CLOSE_FILE, /* Closing of the output file */ + CFE_ES_PerfDumpState_MAX /* Placeholder for last state, no action, always last */ +} CFE_ES_PerfDumpState_t; + +/* + * Performance log dump state structure + * + * This structure is stored in global memory and keeps the state + * of the performance log dump from one iteration to the next. + * + * When state is IDLE, the background task does nothing and does not + * access or update any other members. + * + * The first state transition (IDLE->INIT) is triggered via ES command, + * where the command processor sets the PendingState. + * + * Once state is non-IDLE, the structure becomes owned by the background + * task. It will progress through the remainder of the state machine, + * eventually arriving back at IDLE when the request is completed. + */ +typedef struct +{ + CFE_ES_PerfDumpState_t CurrentState; /* the current state of the job */ + CFE_ES_PerfDumpState_t PendingState; /* the pending/next state, if transitioning */ + + char DataFileName[OS_MAX_PATH_LEN]; /* output file name from dump command */ + osal_id_t FileDesc; /* file descriptor for writing */ + uint32 WorkCredit; /* accumulator based on the passage of time */ + uint32 StateCounter; /* number of blocks/items left in current state */ + uint32 DataPos; /* last position within the Perf Log */ + size_t FileSize; /* Total file size, for progress reporing in telemetry */ +} CFE_ES_PerfDumpGlobal_t; + +/* + * Helper function to obtain the progress/remaining items from + * the background task that is writing the performance log data + * + * This is no longer just simply reading a single value from a struct, + * as it depends on the state of the overall process. The return value + * from this should mimic the value which was historically + * returned in the ES telemetry to report progress on this task. + * + * Foreground tasks/telemetry code shouldn't directly "peek" + * into data structures which it does not own. + */ +uint32 CFE_ES_GetPerfLogDumpRemaining(void); + +/* + * Implementation of the background state machine for writing + * performance log data. + */ +bool CFE_ES_RunPerfLogDump(uint32 ElapsedTime, void *Arg); + +#endif /* CFE_ES_PERF_H */ diff --git a/modules/es/fsw/src/cfe_es_resource.c b/modules/es/fsw/src/cfe_es_resource.c new file mode 100644 index 000000000..4ea57e553 --- /dev/null +++ b/modules/es/fsw/src/cfe_es_resource.c @@ -0,0 +1,408 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/* +** File: +** cfe_es_resource.c +** +** Purpose: +** Function definitions related to CFE resource management +** +** References: +** Flight Software Branch C Coding Standard Version 1.0a +** cFE Flight Software Application Developers Guide +*/ + +/* +** Includes +*/ +#include "cfe_es_module_all.h" + +#include +#include +#include + +/*********************************************************************/ +/* + * Function: CFE_ES_TaskId_ToOSAL + * + * For complete API information, see prototype in header + */ +osal_id_t CFE_ES_TaskId_ToOSAL(CFE_ES_TaskId_t id) +{ + osal_id_t Result; + unsigned long Val; + + Val = CFE_ResourceId_ToInteger(CFE_RESOURCEID_UNWRAP(id)); + Result = OS_ObjectIdFromInteger(Val ^ CFE_RESOURCEID_MARK); + + return Result; +} + +/*********************************************************************/ +/* + * Function: CFE_TaskId_FromOSAL + * + * For complete API information, see prototype in header + */ +CFE_ES_TaskId_t CFE_ES_TaskId_FromOSAL(osal_id_t id) +{ + CFE_ResourceId_t Result; + unsigned long Val; + + Val = OS_ObjectIdToInteger(id); + Result = CFE_ResourceId_FromInteger(Val ^ CFE_RESOURCEID_MARK); + + return CFE_ES_TASKID_C(Result); +} + +/*********************************************************************/ +/* + * CFE_ES_LocateAppRecordByName + * + * For complete API information, see prototype in header + */ +CFE_ES_AppRecord_t *CFE_ES_LocateAppRecordByName(const char *Name) +{ + CFE_ES_AppRecord_t *AppRecPtr; + uint32 Count; + + /* + ** Search the Application table for an app with a matching name. + */ + AppRecPtr = CFE_ES_Global.AppTable; + Count = CFE_PLATFORM_ES_MAX_APPLICATIONS; + while (true) + { + if (Count == 0) + { + AppRecPtr = NULL; + break; + } + if (CFE_ES_AppRecordIsUsed(AppRecPtr) && strcmp(Name, CFE_ES_AppRecordGetName(AppRecPtr)) == 0) + { + break; + } + + ++AppRecPtr; + --Count; + } + + return AppRecPtr; + +} /* End of CFE_ES_LocateAppRecordByName() */ + +/*********************************************************************/ +/* + * CFE_ES_LocateLibRecordByName + * + * For complete API information, see prototype in header + */ +CFE_ES_LibRecord_t *CFE_ES_LocateLibRecordByName(const char *Name) +{ + CFE_ES_LibRecord_t *LibRecPtr; + uint32 Count; + + /* + ** Search the Library table for a library with a matching name. + */ + LibRecPtr = CFE_ES_Global.LibTable; + Count = CFE_PLATFORM_ES_MAX_LIBRARIES; + while (true) + { + if (Count == 0) + { + LibRecPtr = NULL; + break; + } + if (CFE_ES_LibRecordIsUsed(LibRecPtr) && strcmp(Name, CFE_ES_LibRecordGetName(LibRecPtr)) == 0) + { + break; + } + + ++LibRecPtr; + --Count; + } + + return LibRecPtr; + +} /* End of CFE_ES_LocateLibRecordByName() */ + +/*********************************************************************/ +/* + * CFE_ES_LocateCounterRecordByName + * + * For complete API information, see prototype in header + */ +CFE_ES_GenCounterRecord_t *CFE_ES_LocateCounterRecordByName(const char *Name) +{ + CFE_ES_GenCounterRecord_t *CounterRecPtr; + uint32 Count; + + /* + ** Search the Counter table for a matching name. + */ + CounterRecPtr = CFE_ES_Global.CounterTable; + Count = CFE_PLATFORM_ES_MAX_GEN_COUNTERS; + while (true) + { + if (Count == 0) + { + CounterRecPtr = NULL; + break; + } + if (CFE_ES_CounterRecordIsUsed(CounterRecPtr) && strcmp(Name, CFE_ES_CounterRecordGetName(CounterRecPtr)) == 0) + { + break; + } + + ++CounterRecPtr; + --Count; + } + + return CounterRecPtr; +} + +/*********************************************************************/ +/* + * CFE_ES_LocateAppRecordByID + * + * For complete API information, see prototype in header + */ +CFE_ES_AppRecord_t *CFE_ES_LocateAppRecordByID(CFE_ES_AppId_t AppID) +{ + CFE_ES_AppRecord_t *AppRecPtr; + uint32 Idx; + + if (CFE_ES_AppID_ToIndex(AppID, &Idx) == CFE_SUCCESS) + { + AppRecPtr = &CFE_ES_Global.AppTable[Idx]; + } + else + { + AppRecPtr = NULL; + } + + return AppRecPtr; +} + +/*********************************************************************/ +/* + * CFE_ES_LocateLibRecordByID + * + * For complete API information, see prototype in header + */ +CFE_ES_LibRecord_t *CFE_ES_LocateLibRecordByID(CFE_ES_LibId_t LibID) +{ + CFE_ES_LibRecord_t *LibRecPtr; + uint32 Idx; + + if (CFE_ES_LibID_ToIndex(LibID, &Idx) == CFE_SUCCESS) + { + LibRecPtr = &CFE_ES_Global.LibTable[Idx]; + } + else + { + LibRecPtr = NULL; + } + + return LibRecPtr; +} + +/*********************************************************************/ +/* + * CFE_ES_LocateTaskRecordByID + * + * For complete API information, see prototype in header + */ +CFE_ES_TaskRecord_t *CFE_ES_LocateTaskRecordByID(CFE_ES_TaskId_t TaskID) +{ + CFE_ES_TaskRecord_t *TaskRecPtr; + uint32 Idx; + + if (CFE_ES_TaskID_ToIndex(TaskID, &Idx) == CFE_SUCCESS) + { + TaskRecPtr = &CFE_ES_Global.TaskTable[Idx]; + } + else + { + TaskRecPtr = NULL; + } + + return TaskRecPtr; +} + +/*********************************************************************/ +/* + * CFE_ES_LocateCounterRecordByID + * + * For complete API information, see prototype in header + */ +CFE_ES_GenCounterRecord_t *CFE_ES_LocateCounterRecordByID(CFE_ES_CounterId_t CounterID) +{ + CFE_ES_GenCounterRecord_t *CounterRecPtr; + uint32 Idx; + + if (CFE_ES_CounterID_ToIndex(CounterID, &Idx) == CFE_SUCCESS) + { + CounterRecPtr = &CFE_ES_Global.CounterTable[Idx]; + } + else + { + CounterRecPtr = NULL; + } + + return CounterRecPtr; +} + +/*********************************************************************/ +/* + * CFE_ES_GetTaskRecordByContext + * + * For complete API information, see prototype in header + * + * This function does additional validation on the task record + * and should only be called when global data is locked. + */ +CFE_ES_TaskRecord_t *CFE_ES_GetTaskRecordByContext(void) +{ + CFE_ES_TaskRecord_t *TaskRecPtr; + CFE_ES_TaskId_t TaskID; + + /* + ** Use the OS task ID to get the ES task record + */ + TaskID = CFE_ES_TaskId_FromOSAL(OS_TaskGetId()); + TaskRecPtr = CFE_ES_LocateTaskRecordByID(TaskID); + + /* + * Confirm that the entry is actually a match (this requires/assumes + * the global data is locked). + * + * If not a match, return NULL. + */ + if (!CFE_ES_TaskRecordIsMatch(TaskRecPtr, TaskID)) + { + TaskRecPtr = NULL; + } + + return TaskRecPtr; +} + +/*********************************************************************/ +/* + * CFE_ES_GetAppRecordByContext + * + * For complete API information, see prototype in header + */ +CFE_ES_AppRecord_t *CFE_ES_GetAppRecordByContext(void) +{ + CFE_ES_AppRecord_t * AppRecPtr; + CFE_ES_TaskRecord_t *TaskRecPtr; + + /* + ** Step 1: Get the task record + */ + TaskRecPtr = CFE_ES_GetTaskRecordByContext(); + if (TaskRecPtr != NULL) + { + /* + ** Step 2: get the Application ID for the current task + */ + AppRecPtr = CFE_ES_LocateAppRecordByID(TaskRecPtr->AppId); + + /* + * Confirm that the entry is actually a match (this requires/assumes + * the global data is locked). + * + * If not a match, return NULL. + */ + if (!CFE_ES_AppRecordIsMatch(AppRecPtr, TaskRecPtr->AppId)) + { + AppRecPtr = NULL; + } + } + else + { + AppRecPtr = NULL; + } + + return AppRecPtr; +} + +/* + * --------------------------------------------------------------------------------------- + * Function: CFE_ES_CheckCounterIdSlotUsed + * + * Purpose: Helper function, Aids in allocating a new ID by checking if + * a given ID is available. Must be called while locked. + * --------------------------------------------------------------------------------------- + */ +bool CFE_ES_CheckCounterIdSlotUsed(CFE_ResourceId_t CheckId) +{ + CFE_ES_GenCounterRecord_t *GenCounterRecPtr; + /* + * Note - The pointer here should never be NULL because the ID should always be + * within the expected range, but if it ever is NULL, this should return true + * such that the caller will _not_ attempt to use the record. + */ + GenCounterRecPtr = CFE_ES_LocateCounterRecordByID(CFE_ES_COUNTERID_C(CheckId)); + return (GenCounterRecPtr == NULL || CFE_ES_CounterRecordIsUsed(GenCounterRecPtr)); +} + +/* + *--------------------------------------------------------------------------------------- + * Function: CFE_ES_CheckAppIdSlotUsed + * + * Purpose: Helper function, Aids in allocating a new ID by checking if + * a given ID is available. Must be called while locked. + *--------------------------------------------------------------------------------------- + */ +bool CFE_ES_CheckAppIdSlotUsed(CFE_ResourceId_t CheckId) +{ + CFE_ES_AppRecord_t *AppRecPtr; + /* + * Note - The pointer here should never be NULL because the ID should always be + * within the expected range, but if it ever is NULL, this should return true + * such that the caller will _not_ attempt to use the record. + */ + AppRecPtr = CFE_ES_LocateAppRecordByID(CFE_ES_APPID_C(CheckId)); + return (AppRecPtr == NULL || CFE_ES_AppRecordIsUsed(AppRecPtr)); +} + +/* + * --------------------------------------------------------------------------------------- + * Function: CFE_ES_CheckLibIdSlotUsed + * + * Purpose: Helper function, Aids in allocating a new ID by checking if + * a given ID is available. Must be called while locked. + * --------------------------------------------------------------------------------------- + */ +bool CFE_ES_CheckLibIdSlotUsed(CFE_ResourceId_t CheckId) +{ + CFE_ES_LibRecord_t *LibRecPtr; + /* + * Note - The pointer here should never be NULL because the ID should always be + * within the expected range, but if it ever is NULL, this should return true + * such that the caller will _not_ attempt to use the record. + */ + LibRecPtr = CFE_ES_LocateLibRecordByID(CFE_ES_LIBID_C(CheckId)); + return (LibRecPtr == NULL || CFE_ES_LibRecordIsUsed(LibRecPtr)); +} diff --git a/modules/es/fsw/src/cfe_es_resource.h b/modules/es/fsw/src/cfe_es_resource.h new file mode 100644 index 000000000..6d6f387b4 --- /dev/null +++ b/modules/es/fsw/src/cfe_es_resource.h @@ -0,0 +1,552 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/** + * @file + * + * Contains basic prototypes and definitions related to CFE ES resource + * management and related resource IDs. + * + * A CFE ES Resource ID is a common way to identify CFE-managed resources such + * as apps, tasks, counters, memory pools, CDS blocks, and other entities. + */ + +#ifndef CFE_ES_RESOURCE_H +#define CFE_ES_RESOURCE_H + +/* +** Include Files +*/ +#include "cfe_resourceid.h" +#include "cfe_core_resourceid_basevalues.h" +#include "cfe_es_global.h" + +/** + * @brief Locate the app table entry correlating with a given app ID. + * + * This only returns a pointer to the table entry and does _not_ + * otherwise check/validate the entry. + * + * @param[in] AppID the app ID to locate + * @return pointer to App Table entry for the given app ID + */ +extern CFE_ES_AppRecord_t *CFE_ES_LocateAppRecordByID(CFE_ES_AppId_t AppID); + +/** + * @brief Locate the Library table entry correlating with a given Lib ID. + * + * This only returns a pointer to the table entry and does _not_ + * otherwise check/validate the entry. + * + * @param[in] LibID the Lib ID to locate + * @return pointer to Library Table entry for the given Lib ID + */ +extern CFE_ES_LibRecord_t *CFE_ES_LocateLibRecordByID(CFE_ES_LibId_t LibID); + +/** + * @brief Locate the task table entry correlating with a given task ID. + * + * This only returns a pointer to the table entry and does _not_ + * otherwise check/validate the entry. + * + * @param[in] TaskID the task ID to locate + * @return pointer to Task Table entry for the given task ID + */ +extern CFE_ES_TaskRecord_t *CFE_ES_LocateTaskRecordByID(CFE_ES_TaskId_t TaskID); + +/** + * @brief Locate the Counter table entry correlating with a given Counter ID. + * + * This only returns a pointer to the table entry and does _not_ + * otherwise check/validate the entry. + * + * @param[in] CounterID the Counter ID to locate + * @return pointer to Counter Table entry for the given Counter ID + */ +extern CFE_ES_GenCounterRecord_t *CFE_ES_LocateCounterRecordByID(CFE_ES_CounterId_t CounterID); + +/** + * @brief Check if an app record is in use or free/empty + * + * This routine checks if the App table entry is in use or if it is free + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * @param[in] AppRecPtr pointer to app table entry + * @returns true if the entry is in use/configured, or false if it is free/empty + */ +static inline bool CFE_ES_AppRecordIsUsed(const CFE_ES_AppRecord_t *AppRecPtr) +{ + return CFE_RESOURCEID_TEST_DEFINED(AppRecPtr->AppId); +} + +/** + * @brief Get the ID value from an app table entry + * + * This routine converts the table entry back to an abstract ID. + * + * @param[in] AppRecPtr pointer to app table entry + * @returns AppID of entry + */ +static inline CFE_ES_AppId_t CFE_ES_AppRecordGetID(const CFE_ES_AppRecord_t *AppRecPtr) +{ + return AppRecPtr->AppId; +} + +/** + * @brief Marks an app table entry as used (not free) + * + * This sets the internal field(s) within this entry, and marks + * it as being associated with the given app ID. + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * @param[in] AppRecPtr pointer to app table entry + * @param[in] PendingId the app ID of this entry + */ +static inline void CFE_ES_AppRecordSetUsed(CFE_ES_AppRecord_t *AppRecPtr, CFE_ResourceId_t PendingId) +{ + AppRecPtr->AppId = CFE_ES_APPID_C(PendingId); +} + +/** + * @brief Set an app record table entry free (not used) + * + * This clears the internal field(s) within this entry, and allows the + * memory to be re-used in the future. + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * @param[in] AppRecPtr pointer to app table entry + */ +static inline void CFE_ES_AppRecordSetFree(CFE_ES_AppRecord_t *AppRecPtr) +{ + AppRecPtr->AppId = CFE_ES_APPID_UNDEFINED; +} + +/** + * @brief Check if an app record is a match for the given AppID + * + * This routine confirms that the previously-located record is valid + * and matches the expected app ID. + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * @param[in] AppRecPtr pointer to app table entry + * @param[in] AppID expected app ID + * @returns true if the entry matches the given app ID + */ +static inline bool CFE_ES_AppRecordIsMatch(const CFE_ES_AppRecord_t *AppRecPtr, CFE_ES_AppId_t AppID) +{ + return (AppRecPtr != NULL && CFE_RESOURCEID_TEST_EQUAL(AppRecPtr->AppId, AppID)); +} + +/** + * @brief Obtain the name associated with the Application record + * + * Returns the name field from within the Application record + * + * @param[in] AppRecPtr pointer to App table entry + * @returns Pointer to Application name + */ +static inline const char *CFE_ES_AppRecordGetName(const CFE_ES_AppRecord_t *AppRecPtr) +{ + return AppRecPtr->AppName; +} + +/** + * @brief Check if a Library record is in use or free/empty + * + * This routine checks if the Lib table entry is in use or if it is free + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * @param[in] LibRecPtr pointer to Lib table entry + * @returns true if the entry is in use/configured, or false if it is free/empty + */ +static inline bool CFE_ES_LibRecordIsUsed(const CFE_ES_LibRecord_t *LibRecPtr) +{ + return CFE_RESOURCEID_TEST_DEFINED(LibRecPtr->LibId); +} + +/** + * @brief Get the ID value from a Library table entry + * + * This routine converts the table entry back to an abstract ID. + * + * @param[in] LibRecPtr pointer to Lib table entry + * @returns LibID of entry + */ +static inline CFE_ES_LibId_t CFE_ES_LibRecordGetID(const CFE_ES_LibRecord_t *LibRecPtr) +{ + /* + * The initial implementation does not store the ID in the entry; + * the ID is simply the zero-based index into the table. + */ + return (LibRecPtr->LibId); +} + +/** + * @brief Marks a Library table entry as used (not free) + * + * This sets the internal field(s) within this entry, and marks + * it as being associated with the given Lib ID. + * + * @param[in] LibRecPtr pointer to Lib table entry + * @param[in] PendingId the Lib ID of this entry + */ +static inline void CFE_ES_LibRecordSetUsed(CFE_ES_LibRecord_t *LibRecPtr, CFE_ResourceId_t PendingId) +{ + LibRecPtr->LibId = CFE_ES_LIBID_C(PendingId); +} + +/** + * @brief Set a Library record table entry free (not used) + * + * This clears the internal field(s) within this entry, and allows the + * memory to be re-used in the future. + * + * @param[in] LibRecPtr pointer to Lib table entry + */ +static inline void CFE_ES_LibRecordSetFree(CFE_ES_LibRecord_t *LibRecPtr) +{ + LibRecPtr->LibId = CFE_ES_LIBID_UNDEFINED; +} + +/** + * @brief Check if a Library record is a match for the given LibID + * + * This routine confirms that the previously-located record is valid + * and matches the expected Lib ID. + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * @param[in] LibRecPtr pointer to Lib table entry + * @param[in] LibID expected Lib ID + * @returns true if the entry matches the given Lib ID + */ +static inline bool CFE_ES_LibRecordIsMatch(const CFE_ES_LibRecord_t *LibRecPtr, CFE_ES_LibId_t LibID) +{ + return (LibRecPtr != NULL && CFE_RESOURCEID_TEST_EQUAL(LibRecPtr->LibId, LibID)); +} + +/** + * @brief Obtain the name associated with the Library record + * + * Returns the name field from within the Library record + * + * @param[in] LibRecPtr pointer to Lib table entry + * @returns Pointer to Library name + */ +static inline const char *CFE_ES_LibRecordGetName(const CFE_ES_LibRecord_t *LibRecPtr) +{ + return LibRecPtr->LibName; +} + +/** + * @brief Get the ID value from an Task table entry + * + * This routine converts the table entry back to an abstract ID. + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * @param[in] TaskRecPtr pointer to Task table entry + * @returns TaskID of entry + */ +static inline CFE_ES_TaskId_t CFE_ES_TaskRecordGetID(const CFE_ES_TaskRecord_t *TaskRecPtr) +{ + return (TaskRecPtr->TaskId); +} + +/** + * @brief Check if a Task record is in use or free/empty + * + * This routine checks if the Task table entry is in use or if it is free + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * @param[in] TaskRecPtr pointer to task table entry + * @returns true if the entry is in use/configured, or false if it is free/empty + */ +static inline bool CFE_ES_TaskRecordIsUsed(const CFE_ES_TaskRecord_t *TaskRecPtr) +{ + return CFE_RESOURCEID_TEST_DEFINED(TaskRecPtr->TaskId); +} + +/** + * @brief Marks an Task table entry as used (not free) + * + * This sets the internal field(s) within this entry, and marks + * it as being associated with the given Task ID. + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * @param[in] TaskRecPtr pointer to Task table entry + * @param[in] PendingId the Task ID of this entry + */ +static inline void CFE_ES_TaskRecordSetUsed(CFE_ES_TaskRecord_t *TaskRecPtr, CFE_ResourceId_t PendingId) +{ + TaskRecPtr->TaskId = CFE_ES_TASKID_C(PendingId); +} + +/** + * @brief Set a Task record table entry free + * + * This allows the table entry to be re-used by another Task. + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * @param[in] TaskRecPtr pointer to task table entry + * @returns true if the entry is in use/configured, or false if it is free/empty + */ +static inline void CFE_ES_TaskRecordSetFree(CFE_ES_TaskRecord_t *TaskRecPtr) +{ + TaskRecPtr->TaskId = CFE_ES_TASKID_UNDEFINED; +} + +/** + * @brief Check if a Task record is a match for the given TaskID + * + * This routine confirms that the previously-located record is valid + * and matches the expected Task ID. + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * @param[in] TaskRecPtr pointer to task table entry + * @param[in] TaskID The expected task ID to verify + * @returns true if the entry matches the given task ID + */ +static inline bool CFE_ES_TaskRecordIsMatch(const CFE_ES_TaskRecord_t *TaskRecPtr, CFE_ES_TaskId_t TaskID) +{ + return (TaskRecPtr != NULL && CFE_RESOURCEID_TEST_EQUAL(TaskRecPtr->TaskId, TaskID)); +} + +/** + * @brief Obtain the name associated with the Task record + * + * Returns the name field from within the Task record + * + * @param[in] TaskRecPtr pointer to Task table entry + * @returns Pointer to Task name + */ +static inline const char *CFE_ES_TaskRecordGetName(const CFE_ES_TaskRecord_t *TaskRecPtr) +{ + return TaskRecPtr->TaskName; +} + +/** + * @brief Check if an Counter record is in use or free/empty + * + * This routine checks if the Counter table entry is in use or if it is free + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * @param[in] CounterRecPtr pointer to Counter table entry + * @returns true if the entry is in use/configured, or false if it is free/empty + */ +static inline bool CFE_ES_CounterRecordIsUsed(const CFE_ES_GenCounterRecord_t *CounterRecPtr) +{ + return CFE_RESOURCEID_TEST_DEFINED(CounterRecPtr->CounterId); +} + +/** + * @brief Get the ID value from an Counter table entry + * + * This routine converts the table entry back to an abstract ID. + * + * @param[in] CounterRecPtr pointer to Counter table entry + * @returns CounterID of entry + */ +static inline CFE_ES_CounterId_t CFE_ES_CounterRecordGetID(const CFE_ES_GenCounterRecord_t *CounterRecPtr) +{ + return CounterRecPtr->CounterId; +} + +/** + * @brief Marks an Counter table entry as used (not free) + * + * This sets the internal field(s) within this entry, and marks + * it as being associated with the given Counter ID. + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * @param[in] CounterRecPtr pointer to Counter table entry + * @param[in] PendingId the Counter ID of this entry + */ +static inline void CFE_ES_CounterRecordSetUsed(CFE_ES_GenCounterRecord_t *CounterRecPtr, CFE_ResourceId_t PendingId) +{ + CounterRecPtr->CounterId = CFE_ES_COUNTERID_C(PendingId); +} + +/** + * @brief Set an Counter record table entry free (not used) + * + * This clears the internal field(s) within this entry, and allows the + * memory to be re-used in the future. + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * @param[in] CounterRecPtr pointer to Counter table entry + */ +static inline void CFE_ES_CounterRecordSetFree(CFE_ES_GenCounterRecord_t *CounterRecPtr) +{ + CounterRecPtr->CounterId = CFE_ES_COUNTERID_UNDEFINED; +} + +/** + * @brief Check if an Counter record is a match for the given CounterID + * + * This routine confirms that the previously-located record is valid + * and matches the expected Counter ID. + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * @param[in] CounterRecPtr pointer to Counter table entry + * @param[in] CounterID expected Counter ID + * @returns true if the entry matches the given Counter ID + */ +static inline bool CFE_ES_CounterRecordIsMatch(const CFE_ES_GenCounterRecord_t *CounterRecPtr, + CFE_ES_CounterId_t CounterID) +{ + return (CounterRecPtr != NULL && CFE_RESOURCEID_TEST_EQUAL(CounterRecPtr->CounterId, CounterID)); +} + +/** + * @brief Obtain the name associated with the counter record + * + * Returns the name field from within the counter record + * + * @param[in] CounterRecPtr pointer to Counter table entry + * @returns Pointer to counter name + */ +static inline const char *CFE_ES_CounterRecordGetName(const CFE_ES_GenCounterRecord_t *CounterRecPtr) +{ + return CounterRecPtr->CounterName; +} + +/** + * Locate and validate the app record for the calling context. + * + * Finds and validates the ES AppTable entry corresponding to the + * caller. This confirms that the fields within the table entry match the + * expected value(s), otherwise NULL is returned if no matching entry + * is found. + * + * The global data lock should be obtained prior to invoking this function. + */ +extern CFE_ES_AppRecord_t *CFE_ES_GetAppRecordByContext(void); + +/** + * Locate and validate the task record for the calling context. + * + * Finds and validates the ES TaskTable entry corresponding to the + * caller. This confirms that the fields within the table entry match the + * expected value(s), otherwise NULL is returned if no matching entry + * is found. + * + * The global data lock should be obtained prior to invoking this function. + */ +extern CFE_ES_TaskRecord_t *CFE_ES_GetTaskRecordByContext(void); + +/* + * OSAL <-> CFE task ID conversion + * + * CFE ES does not currently allocate its own task IDs; instead it piggybacks on top + * of the allocation that is already done by OSAL. This is partly for backward + * compatibility - historically the OSAL task IDs were used directly by CFE task APIs. + * + * This is _only_ used for tasks - for all other resource types ES should allocate + * its own identifiers independently of any other subsystem. This conversion may also + * be removed in a future version of CFE, if ES starts allocating task IDs independently + * of OSAL task IDs. + */ + +/** + * @brief Convert an ES Task ID to an OSAL task ID + * + * Task IDs created via CFE ES are also OSAL task IDs, but technically + * do refer to a different scope and therefore have a different type + * to represent them. + * + * This function facilitates converting between the types. + * + * @note With "simple" resource IDs, numeric values are the same and can be interchanged + * for backward compatibility, however they will be different when using "strict" IDs. + * New code should not assume equivalence between OSAL and ES task IDs. + * + * @sa CFE_ES_TaskId_FromOSAL + * + * @param[in] id The CFE task ID + * @returns The OSAL task ID + */ +extern osal_id_t CFE_ES_TaskId_ToOSAL(CFE_ES_TaskId_t id); + +/** + * @brief Convert an ES Task ID to an OSAL task ID + * + * Task IDs created via CFE ES are also OSAL task IDs, but technically + * do refer to a different scope and therefore have a different type + * to represent them. + * + * This function facilitates converting between the types. + * + * @note With "simple" resource IDs, numeric values are the same and can be interchanged + * for backward compatibility, however they will be different when using "strict" IDs. + * New code should not assume equivalence between OSAL and ES task IDs. + * + * @sa CFE_ES_TaskId_ToOSAL + * + * @param[in] id The OSAL task ID + * @returns The CFE task ID + */ +extern CFE_ES_TaskId_t CFE_ES_TaskId_FromOSAL(osal_id_t id); + +/* + * Internal functions to perform name based resource lookups + * + * These functions do not lock, they must only be used internally by ES when + * the lock is already held. + */ +CFE_ES_AppRecord_t * CFE_ES_LocateAppRecordByName(const char *Name); +CFE_ES_LibRecord_t * CFE_ES_LocateLibRecordByName(const char *Name); +CFE_ES_TaskRecord_t * CFE_ES_LocateTaskRecordByName(const char *Name); +CFE_ES_GenCounterRecord_t *CFE_ES_LocateCounterRecordByName(const char *Name); + +/* Availability check functions used in conjunction with CFE_ResourceId_FindNext() */ +bool CFE_ES_CheckAppIdSlotUsed(CFE_ResourceId_t CheckId); +bool CFE_ES_CheckLibIdSlotUsed(CFE_ResourceId_t CheckId); +bool CFE_ES_CheckCounterIdSlotUsed(CFE_ResourceId_t CheckId); + +#endif /* CFE_ES_RESOURCE_H */ diff --git a/modules/es/fsw/src/cfe_es_start.c b/modules/es/fsw/src/cfe_es_start.c new file mode 100644 index 000000000..0d65c3db3 --- /dev/null +++ b/modules/es/fsw/src/cfe_es_start.c @@ -0,0 +1,954 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/* +** File: +** cfe_es_start.c +** +** Purpose: +** This file contains the Main entrypoint and startup code for the cFE core. +** The entry point is called by the board support package for the OS. When the +** entry point is finished, the cFE should be fully initialized and running. +** +** References: +** Flight Software Branch C Coding Standard Version 1.0a +** cFE Flight Software Application Developers Guide +** +*/ + +/* +** Includes +*/ + +#include "cfe_es_module_all.h" + +#include +#include + +static int32 CFE_ES_MainTaskSyncDelay(uint32 AppStateId, uint32 TimeOutMilliseconds); + +/***************************************************************************/ + +/* +** Defines for this module +*/ + +/* +** Number of msecs to delay before exiting cFE. Allows LogMsg to get through +*/ +#define CFE_ES_PANIC_DELAY 500 + +/* +** Global data for the ES startup code and Runtime library +*/ +CFE_ES_Global_t CFE_ES_Global; + +/***************************************************************************/ +/* +** Code +*/ + +/* +** Name: CFE_ES_Main - See API and header file for details +*/ +void CFE_ES_Main(uint32 StartType, uint32 StartSubtype, uint32 ModeId, const char *StartFilePath) +{ + int32 ReturnCode; + + /* + * Clear the entire global data structure. + * This also takes care of setting all resource IDs on all table entries + * to be "undefined" (not in use). + */ + memset(&CFE_ES_Global, 0, sizeof(CFE_ES_Global)); + + /* + ** Indicate that the CFE is the earliest initialization state + */ + CFE_ES_Global.SystemState = CFE_ES_SystemState_EARLY_INIT; + + /* + ** Create the ES Shared Data Mutex + ** This must be done before ANY calls to CFE_ES_WriteToSysLog(), since this uses the mutex + */ + ReturnCode = OS_MutSemCreate(&(CFE_ES_Global.SharedDataMutex), "ES_DATA_MUTEX", 0); + if (ReturnCode != OS_SUCCESS) + { + /* Cannot use SysLog here, since that requires the reset area to be set up */ + OS_printf("ES Startup: Error: ES Shared Data Mutex could not be created. RC=0x%08X\n", + (unsigned int)ReturnCode); + + /* + ** Delay to allow the message to be read + */ + OS_TaskDelay(CFE_ES_PANIC_DELAY); + + /* + ** cFE Cannot continue to start up. + */ + CFE_PSP_Panic(CFE_PSP_PANIC_STARTUP_SEM); + + /* + * Normally CFE_PSP_Panic() will not return but it will under UT + */ + return; + } /* end if */ + + /* + ** Initialize the Reset variables. This call is required + ** Before most of the ES functions can be used including the + ** ES System log. + */ + CFE_ES_SetupResetVariables(StartType, StartSubtype, ModeId); + + /* + ** Initialize the Logic Perf variables + ** Because this is in the ES Reset area, it must be called after + ** CFE_ES_SetupResetVariables. + */ + CFE_ES_SetupPerfVariables(StartType); + + /* + ** Also Create the ES Performance Data Mutex + ** This is to separately protect against concurrent writes to the global performance log data + */ + ReturnCode = OS_MutSemCreate(&CFE_ES_Global.PerfDataMutex, "ES_PERF_MUTEX", 0); + if (ReturnCode != OS_SUCCESS) + { + CFE_ES_SysLogWrite_Unsync("ES Startup: Error: ES Performance Data Mutex could not be created. RC=0x%08X\n", + (unsigned int)ReturnCode); + + /* + ** Delay to allow the message to be read + */ + OS_TaskDelay(CFE_ES_PANIC_DELAY); + + /* + ** cFE Cannot continue to start up. + */ + CFE_PSP_Panic(CFE_PSP_PANIC_STARTUP_SEM); + + /* + * Normally CFE_PSP_Panic() will not return but it will under UT + */ + return; + } + + /* + ** Announce the startup + */ + CFE_ES_WriteToSysLog("ES Startup: CFE_ES_Main in EARLY_INIT state\n"); + + /* + ** Create and Mount the filesystems needed + */ + CFE_ES_InitializeFileSystems(StartType); + + /* + ** Install exception Handlers ( Placeholder ) + */ + CFE_PSP_AttachExceptions(); + + /* + ** Initialize the Last Id + */ + CFE_ES_Global.LastAppId = CFE_ResourceId_FromInteger(CFE_ES_APPID_BASE); + CFE_ES_Global.LastLibId = CFE_ResourceId_FromInteger(CFE_ES_LIBID_BASE); + CFE_ES_Global.LastCounterId = CFE_ResourceId_FromInteger(CFE_ES_COUNTID_BASE); + CFE_ES_Global.LastMemPoolId = CFE_ResourceId_FromInteger(CFE_ES_POOLID_BASE); + + /* + ** Indicate that the CFE core is now starting up / going multi-threaded + */ + CFE_ES_WriteToSysLog("ES Startup: CFE_ES_Main entering CORE_STARTUP state\n"); + CFE_ES_Global.SystemState = CFE_ES_SystemState_CORE_STARTUP; + + /* + ** Create the tasks, OS objects, and initialize hardware + */ + CFE_ES_CreateObjects(); + + /* + ** Indicate that the CFE core is ready + */ + CFE_ES_WriteToSysLog("ES Startup: CFE_ES_Main entering CORE_READY state\n"); + CFE_ES_Global.SystemState = CFE_ES_SystemState_CORE_READY; + + /* + ** Start the cFE Applications from the disk using the file + ** specified in the CFE_PLATFORM_ES_NONVOL_STARTUP_FILE or CFE_PLATFORM_ES_VOLATILE_STARTUP_FILE + ** ( defined in the cfe_platform_cfg.h file ) + */ + CFE_ES_StartApplications(StartType, StartFilePath); + + /* + * Wait for applications to be in at least "LATE_INIT" + * + * However, if not everything starts up, that is not a fatal error, we will + * continue anyway since the core apps are OK and control/telemetry should function. + * The problem app could be deleted/restarted/etc by the ground station. + */ + if (CFE_ES_MainTaskSyncDelay(CFE_ES_AppState_LATE_INIT, CFE_PLATFORM_ES_STARTUP_SCRIPT_TIMEOUT_MSEC) != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("ES Startup: Startup Sync failed - Applications may not have all initialized\n"); + } + + CFE_ES_WriteToSysLog("ES Startup: CFE_ES_Main entering APPS_INIT state\n"); + CFE_ES_Global.SystemState = CFE_ES_SystemState_APPS_INIT; + + /* + * Wait for applications to be "RUNNING" before moving to operational system state. + * + * However, if not everything starts up, that is not a fatal error, we will + * continue anyway since the core apps are OK and control/telemetry should function. + * The problem app could be deleted/restarted/etc by the ground station. + */ + if (CFE_ES_MainTaskSyncDelay(CFE_ES_AppState_RUNNING, CFE_PLATFORM_ES_STARTUP_SCRIPT_TIMEOUT_MSEC) != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("ES Startup: Startup Sync failed - Applications may not have all started\n"); + } + + /* + ** Startup is fully complete + */ + CFE_ES_WriteToSysLog("ES Startup: CFE_ES_Main entering OPERATIONAL state\n"); + CFE_ES_Global.SystemState = CFE_ES_SystemState_OPERATIONAL; +} + +/* +** Name: CFE_ES_SetupResetVariables +** +** Purpose: This function initializes the ES reset variables depending on the reset type. +** It will also initiate a power on reset when too many processor resets +** have happened. +** +** SYSLOGGING NOTE: Any logging in here must use CFE_ES_SysLogWrite_Unsync() as the necessary +** primitives are not even initialized yet. There is no chance for log contention here. +** +*/ +void CFE_ES_SetupResetVariables(uint32 StartType, uint32 StartSubtype, uint32 BootSource) +{ + + int32 status; + uint32 resetAreaSize; + cpuaddr ResetDataAddr; + + /* + ** Get the pointer to the Reset area from the BSP + */ + status = CFE_PSP_GetResetArea(&ResetDataAddr, &resetAreaSize); + + /* + ** Make sure the status is OK or size is big enough + */ + if (status != CFE_PSP_SUCCESS) + { + /* + ** Cannot use the ES System log without the Reset Area + */ + OS_printf("ES Startup: CFE_PSP_GetResetArea call Failed (0x%08x)!\n", (unsigned int)status); + + /* + ** Delay to allow the message to be read + */ + OS_TaskDelay(CFE_ES_PANIC_DELAY); + + /* + ** cFE Cannot continue to start up. + */ + CFE_PSP_Panic(CFE_PSP_PANIC_MEMORY_ALLOC); + + /* + * Normally unreachable, except in UT where + * CFE_PSP_Panic is a stub that may return + */ + return; + } + else if (resetAreaSize < sizeof(CFE_ES_ResetData_t)) + { + /* + ** Cannot use the ES system log without the Reset Area + */ + OS_printf("ES Startup: Error: ES Reset area not big enough. Needed: %d, Given: %d.\n", + (int)sizeof(CFE_ES_ResetData_t), (int)resetAreaSize); + /* + ** Delay to allow the message to be read + */ + OS_TaskDelay(CFE_ES_PANIC_DELAY); + + /* + ** cFE Cannot continue to start up. + */ + CFE_PSP_Panic(CFE_PSP_PANIC_MEMORY_ALLOC); + + /* + * Normally unreachable, except in UT where + * CFE_PSP_Panic is a stub that may return + */ + return; + } + + CFE_ES_Global.ResetDataPtr = (CFE_ES_ResetData_t *)ResetDataAddr; + + /* + ** Record the BootSource (bank) so it will be valid in the ER log entries. + */ + CFE_ES_Global.ResetDataPtr->ResetVars.BootSource = BootSource; + + /* + ** Determine how the system was started. The choices are: + ** CFE_ES_POWER_ON_RESET, or CFE_PSP_RST_TYPE_PROCESSOR + ** The subtypes include: + ** CFE_PSP_RST_SUBTYPE_POWER_CYCLE, CFE_PSP_RST_SUBTYPE_PUSH_BUTTON, CFE_PSP_RST_SUBTYPE_HW_SPECIAL_COMMAND, + ** CFE_PSP_RST_SUBTYPE_HW_WATCHDOG, CFE_PSP_RST_TYPE_COMMAND, or CFE_PSP_RST_SUBTYPE_EXCEPTION. + ** Some of these reset types are logged before the system is restarted. + ** ( CFE_PSP_RST_TYPE_COMMAND, CFE_PSP_RST_SUBTYPE_EXCEPTION ) while others occur + ** without the knowledge of the software and must be logged here. + */ + if (StartType == CFE_PSP_RST_TYPE_POWERON) + { + /* + ** Record the reset type and subtype + */ + CFE_ES_Global.ResetDataPtr->ResetVars.ResetSubtype = StartSubtype; + CFE_ES_Global.ResetDataPtr->ResetVars.ResetType = CFE_PSP_RST_TYPE_POWERON; + + /* + ** Log the power-on reset. + */ + if (StartSubtype == CFE_PSP_RST_SUBTYPE_POWER_CYCLE) + { + CFE_ES_SysLogWrite_Unsync("POWER ON RESET due to Power Cycle (Power Cycle).\n"); + CFE_ES_WriteToERLog(CFE_ES_LogEntryType_CORE, CFE_PSP_RST_TYPE_POWERON, StartSubtype, + "POWER ON RESET due to Power Cycle (Power Cycle)"); + } + else if (StartSubtype == CFE_PSP_RST_SUBTYPE_HW_SPECIAL_COMMAND) + { + CFE_ES_SysLogWrite_Unsync("POWER ON RESET due to HW Special Cmd (Hw Spec Cmd).\n"); + CFE_ES_WriteToERLog(CFE_ES_LogEntryType_CORE, CFE_PSP_RST_TYPE_POWERON, StartSubtype, + "POWER ON RESET due to HW Special Cmd (Hw Spec Cmd)"); + } + else + { + CFE_ES_SysLogWrite_Unsync("POWER ON RESET due to other cause (See Subtype).\n"); + CFE_ES_WriteToERLog(CFE_ES_LogEntryType_CORE, CFE_PSP_RST_TYPE_POWERON, StartSubtype, + "POWER ON RESET due to other cause (See Subtype)"); + } + + /* + ** Initialize all reset counters. + */ + CFE_ES_Global.ResetDataPtr->ResetVars.ProcessorResetCount = 0; + CFE_ES_Global.ResetDataPtr->ResetVars.MaxProcessorResetCount = CFE_PLATFORM_ES_MAX_PROCESSOR_RESETS; + CFE_ES_Global.DebugVars.DebugFlag = 0; + } + else if (StartType == CFE_PSP_RST_TYPE_PROCESSOR) + { + /* + ** If a Processor reset was not commanded, it must be a watchdog or other non-commanded reset + ** Log the reset before updating any reset variables. + */ + if (CFE_ES_Global.ResetDataPtr->ResetVars.ES_CausedReset != true) + { + CFE_ES_Global.ResetDataPtr->ResetVars.ResetType = CFE_PSP_RST_TYPE_PROCESSOR; + CFE_ES_Global.ResetDataPtr->ResetVars.ProcessorResetCount++; + + /* + ** When coming up from a Processor reset that was not caused by ES, check to see + ** if the maximum number has been exceeded + */ + if (CFE_ES_Global.ResetDataPtr->ResetVars.ProcessorResetCount > + CFE_ES_Global.ResetDataPtr->ResetVars.MaxProcessorResetCount) + { + if (StartSubtype == CFE_PSP_RST_SUBTYPE_HW_SPECIAL_COMMAND) + { + CFE_ES_Global.ResetDataPtr->ResetVars.ResetSubtype = CFE_PSP_RST_SUBTYPE_HW_SPECIAL_COMMAND; + CFE_ES_SysLogWrite_Unsync("POWER ON RESET due to max proc resets (HW Spec Cmd).\n"); + + /* + ** Log the reset in the ER Log. The log will be wiped out, but it's good to have + ** the entry just in case something fails. + */ + CFE_ES_WriteToERLog(CFE_ES_LogEntryType_CORE, CFE_PSP_RST_TYPE_POWERON, StartSubtype, + "POWER ON RESET due to max proc resets (HW Spec Cmd)."); + } + else + { + CFE_ES_Global.ResetDataPtr->ResetVars.ResetSubtype = CFE_PSP_RST_SUBTYPE_HW_WATCHDOG; + CFE_ES_SysLogWrite_Unsync("POWER ON RESET due to max proc resets (Watchdog).\n"); + + /* + ** Log the reset in the ER Log. The log will be wiped out, but it's good to have + ** the entry just in case something fails. + */ + CFE_ES_WriteToERLog(CFE_ES_LogEntryType_CORE, CFE_PSP_RST_TYPE_POWERON, StartSubtype, + "POWER ON RESET due to max proc resets (Watchdog)."); + } + /* + ** Call the BSP reset routine + */ + CFE_PSP_Restart(CFE_PSP_RST_TYPE_POWERON); + + /* + ** Should not return here. + */ + CFE_ES_SysLogWrite_Unsync("ES Startup: Error: CFE_PSP_Restart returned.\n"); + } + else /* Maximum processor reset not exceeded */ + { + if (StartSubtype == CFE_PSP_RST_SUBTYPE_HW_SPECIAL_COMMAND) + { + CFE_ES_Global.ResetDataPtr->ResetVars.ResetSubtype = CFE_PSP_RST_SUBTYPE_HW_SPECIAL_COMMAND; + CFE_ES_SysLogWrite_Unsync("PROCESSOR RESET due to Hardware Special Command (HW Spec Cmd).\n"); + + /* + ** Log the watchdog reset + */ + CFE_ES_WriteToERLog(CFE_ES_LogEntryType_CORE, CFE_PSP_RST_TYPE_PROCESSOR, StartSubtype, + "PROCESSOR RESET due to Hardware Special Command (Hw Spec Cmd)."); + } + else + { + CFE_ES_Global.ResetDataPtr->ResetVars.ResetSubtype = CFE_PSP_RST_SUBTYPE_HW_WATCHDOG; + CFE_ES_SysLogWrite_Unsync("PROCESSOR RESET due to Watchdog (Watchdog).\n"); + + /* + ** Log the watchdog reset + */ + CFE_ES_WriteToERLog(CFE_ES_LogEntryType_CORE, CFE_PSP_RST_TYPE_PROCESSOR, StartSubtype, + "PROCESSOR RESET due to Watchdog (Watchdog)."); + } + + } /* end if */ + } + /* + ** If a processor reset is due to a command or exception, the reset has already been logged. + ** Update the reset variables only. + ** The logic for detecting maximum resets is done on the command/exception side + ** on the "way down" when the command or exception handler is executed. + */ + else + { + CFE_ES_Global.ResetDataPtr->ResetVars.ResetType = CFE_PSP_RST_TYPE_PROCESSOR; + CFE_ES_Global.ResetDataPtr->ResetVars.ResetSubtype = StartSubtype; + } + + /* + ** Initialize processor reset counters. + */ + CFE_ES_Global.DebugVars.DebugFlag = 0; + } + + /* + ** Clear the commanded reset flag, in case a watchdog happens. + */ + CFE_ES_Global.ResetDataPtr->ResetVars.ES_CausedReset = false; +} + +/* +** Name: CFE_ES_InitializeFileSystems +** +** Purpose: This function initializes the file systems used in the cFE core. +** +*/ +void CFE_ES_InitializeFileSystems(uint32 StartType) +{ + int32 RetStatus; + cpuaddr RamDiskMemoryAddress; + uint32 RamDiskMemorySize; + int32 PercentFree; + OS_statvfs_t StatBuf; + + /* + ** Get the memory area for the RAM disk + */ + RetStatus = CFE_PSP_GetVolatileDiskMem(&(RamDiskMemoryAddress), &(RamDiskMemorySize)); + + if (RetStatus != CFE_PSP_SUCCESS) + { + CFE_ES_WriteToSysLog("ES Startup: Cannot Get Memory for Volatile Disk. EC = 0x%08X\n", (unsigned int)RetStatus); + + /* + ** Delay to allow the message to be read + */ + OS_TaskDelay(CFE_ES_PANIC_DELAY); + + /* + ** cFE Cannot continue to start up. + */ + CFE_PSP_Panic(CFE_PSP_PANIC_VOLATILE_DISK); + } + + /* + ** Next, either format, or just initialize the RAM disk depending on + ** the reset type + */ + if (StartType == CFE_PSP_RST_TYPE_POWERON) + { + RetStatus = OS_mkfs((void *)RamDiskMemoryAddress, "/ramdev0", "RAM", CFE_PLATFORM_ES_RAM_DISK_SECTOR_SIZE, + CFE_PLATFORM_ES_RAM_DISK_NUM_SECTORS); + if (RetStatus != OS_SUCCESS) + { + CFE_ES_WriteToSysLog("ES Startup: Error Creating Volatile(RAM) Volume. EC = 0x%08X\n", + (unsigned int)RetStatus); + + /* + ** Delay to allow the message to be read + */ + OS_TaskDelay(CFE_ES_PANIC_DELAY); + + /* + ** cFE Cannot continue to start up. + */ + CFE_PSP_Panic(CFE_PSP_PANIC_VOLATILE_DISK); + } + } + else + { + RetStatus = OS_initfs((void *)RamDiskMemoryAddress, "/ramdev0", "RAM", CFE_PLATFORM_ES_RAM_DISK_SECTOR_SIZE, + CFE_PLATFORM_ES_RAM_DISK_NUM_SECTORS); + if (RetStatus != OS_SUCCESS) + { + CFE_ES_WriteToSysLog("ES Startup: Error Initializing Volatile(RAM) Volume. EC = 0x%08X\n", + (unsigned int)RetStatus); + CFE_ES_WriteToSysLog("ES Startup: Formatting Volatile(RAM) Volume.\n"); + + RetStatus = OS_mkfs((void *)RamDiskMemoryAddress, "/ramdev0", "RAM", CFE_PLATFORM_ES_RAM_DISK_SECTOR_SIZE, + CFE_PLATFORM_ES_RAM_DISK_NUM_SECTORS); + if (RetStatus != OS_SUCCESS) + { + CFE_ES_WriteToSysLog("ES Startup: Error Creating Volatile(RAM) Volume. EC = 0x%08X\n", + (unsigned int)RetStatus); + + /* + ** Delay to allow the message to be read + */ + OS_TaskDelay(CFE_ES_PANIC_DELAY); + + /* + ** cFE Cannot continue to start up. + */ + CFE_PSP_Panic(CFE_PSP_PANIC_VOLATILE_DISK); + } + } + } + + /* + ** Now, mount the RAM disk + */ + RetStatus = OS_mount("/ramdev0", CFE_PLATFORM_ES_RAM_DISK_MOUNT_STRING); + if (RetStatus != OS_SUCCESS) + { + CFE_ES_WriteToSysLog("ES Startup: Error Mounting Volatile(RAM) Volume. EC = 0x%08X\n", (unsigned int)RetStatus); + /* + ** Delay to allow the message to be read + */ + OS_TaskDelay(CFE_ES_PANIC_DELAY); + + /* + ** cFE Cannot continue to start up. + */ + CFE_PSP_Panic(CFE_PSP_PANIC_VOLATILE_DISK); + } + + /* + ** During a Processor reset, if the RAM disk has less than a defined + ** amount of free space, reformat and re-mount it. + ** The parameter being checked is CFE_PLATFORM_ES_RAM_DISK_PERCENT_RESERVED + ** Note: When CFE_PLATFORM_ES_RAM_DISK_PERCENT_RESERVED is set to 0, this feature is + ** disabled. + */ + if ((StartType == CFE_PSP_RST_TYPE_PROCESSOR) && (CFE_PLATFORM_ES_RAM_DISK_PERCENT_RESERVED > 0)) + { + /* + ** See how many blocks are free in the RAM disk + */ + RetStatus = OS_FileSysStatVolume(CFE_PLATFORM_ES_RAM_DISK_MOUNT_STRING, &StatBuf); + if (RetStatus == OS_SUCCESS && StatBuf.total_blocks > 0) + { + /* + ** Determine if the disk is too full + */ + PercentFree = (StatBuf.blocks_free * 100) / StatBuf.total_blocks; + CFE_ES_WriteToSysLog("Volatile Disk has %d Percent free space.\n", (int)PercentFree); + + if (PercentFree < CFE_PLATFORM_ES_RAM_DISK_PERCENT_RESERVED) + { + CFE_ES_WriteToSysLog("ES Startup: Insufficent Free Space on Volatile Disk, Reformatting.\n"); + + /* + ** First, unmount the disk + */ + RetStatus = OS_unmount(CFE_PLATFORM_ES_RAM_DISK_MOUNT_STRING); + if (RetStatus == OS_SUCCESS) + { + + /* + ** Remove the file system from the OSAL + */ + RetStatus = OS_rmfs("/ramdev0"); + if (RetStatus == OS_SUCCESS) + { + + /* + ** Next, make a new file system on the disk + */ + RetStatus = OS_mkfs((void *)RamDiskMemoryAddress, "/ramdev0", "RAM", + CFE_PLATFORM_ES_RAM_DISK_SECTOR_SIZE, CFE_PLATFORM_ES_RAM_DISK_NUM_SECTORS); + if (RetStatus == OS_SUCCESS) + { + /* + ** Last, remount the disk + */ + RetStatus = OS_mount("/ramdev0", CFE_PLATFORM_ES_RAM_DISK_MOUNT_STRING); + if (RetStatus != OS_SUCCESS) + { + CFE_ES_WriteToSysLog( + "ES Startup: Error Re-Mounting Volatile(RAM) Volume. EC = 0x%08X\n", + (unsigned int)RetStatus); + /* + ** Delay to allow the message to be read + */ + OS_TaskDelay(CFE_ES_PANIC_DELAY); + + /* + ** cFE Cannot continue to start up. + */ + CFE_PSP_Panic(CFE_PSP_PANIC_VOLATILE_DISK); + + } /* end if mount */ + } + else + { + + CFE_ES_WriteToSysLog("ES Startup: Error Re-Formating Volatile(RAM) Volume. EC = 0x%08X\n", + (unsigned int)RetStatus); + /* + ** Delay to allow the message to be read + */ + OS_TaskDelay(CFE_ES_PANIC_DELAY); + + /* + ** cFE Cannot continue to start up. + */ + CFE_PSP_Panic(CFE_PSP_PANIC_VOLATILE_DISK); + + } /* end if mkfs */ + } + else /* could not Remove File system */ + { + + CFE_ES_WriteToSysLog("ES Startup: Error Removing Volatile(RAM) Volume. EC = 0x%08X\n", + (unsigned int)RetStatus); + /* + ** Delay to allow the message to be read + */ + OS_TaskDelay(CFE_ES_PANIC_DELAY); + + /* + ** cFE Cannot continue to start up. + */ + CFE_PSP_Panic(CFE_PSP_PANIC_VOLATILE_DISK); + + } /* end if OS_rmfs */ + } + else /* could not un-mount disk */ + { + CFE_ES_WriteToSysLog("ES Startup: Error Un-Mounting Volatile(RAM) Volume. EC = 0x%08X\n", + (unsigned int)RetStatus); + /* + ** Delay to allow the message to be read + */ + OS_TaskDelay(CFE_ES_PANIC_DELAY); + + /* + ** cFE Cannot continue to start up. + */ + CFE_PSP_Panic(CFE_PSP_PANIC_VOLATILE_DISK); + } + + } /* end if enough free space */ + } + else /* could not determine free blocks */ + { + /* Log error message -- note that BlocksFree returns the error code in this case */ + CFE_ES_WriteToSysLog("ES Startup: Error Determining Blocks Free on Volume. EC = 0x%08X\n", + (unsigned int)RetStatus); + + /* + ** Delay to allow the message to be read + */ + OS_TaskDelay(CFE_ES_PANIC_DELAY); + + /* + ** cFE Cannot continue to start up. + */ + CFE_PSP_Panic(CFE_PSP_PANIC_VOLATILE_DISK); + + } /* end if BlocksFree */ + + } /* end if processor reset */ + +} /* end function */ + +/*------------------------------------------------------------------------- +** +** Functional Prolog +** +** Name: CFE_ES_CreateObjects +** +** Purpose: This function reads the es_object_table and performs all of the +** application layer initialization. +**---------------------------------------------------------------------------- +*/ +void CFE_ES_CreateObjects(void) +{ + int32 ReturnCode; + uint16 i; + CFE_ES_AppRecord_t *AppRecPtr; + CFE_ResourceId_t PendingAppId; + + CFE_ES_WriteToSysLog("ES Startup: Starting Object Creation calls.\n"); + + for (i = 0; i < CFE_PLATFORM_ES_OBJECT_TABLE_SIZE; i++) + { + switch (CFE_ES_ObjectTable[i].ObjectType) + { + case CFE_ES_DRIVER_TASK: + case CFE_ES_CORE_TASK: + + /* + ** Allocate an ES AppTable entry + */ + CFE_ES_LockSharedData(__func__, __LINE__); + + PendingAppId = CFE_ResourceId_FindNext(CFE_ES_Global.LastAppId, CFE_PLATFORM_ES_MAX_APPLICATIONS, + CFE_ES_CheckAppIdSlotUsed); + AppRecPtr = CFE_ES_LocateAppRecordByID(CFE_ES_APPID_C(PendingAppId)); + if (AppRecPtr != NULL) + { + /* + ** Fill out the parameters in the AppStartParams sub-structure + */ + AppRecPtr->Type = CFE_ES_AppType_CORE; + + strncpy(AppRecPtr->AppName, CFE_ES_ObjectTable[i].ObjectName, sizeof(AppRecPtr->AppName) - 1); + AppRecPtr->AppName[sizeof(AppRecPtr->AppName) - 1] = '\0'; + + /* FileName and EntryPoint is not valid for core apps */ + AppRecPtr->StartParams.MainTaskInfo.StackSize = CFE_ES_ObjectTable[i].ObjectSize; + AppRecPtr->StartParams.MainTaskInfo.Priority = CFE_ES_ObjectTable[i].ObjectPriority; + AppRecPtr->StartParams.ExceptionAction = CFE_ES_ExceptionAction_PROC_RESTART; + + /* + ** Fill out the Task State info + */ + AppRecPtr->ControlReq.AppControlRequest = CFE_ES_RunStatus_APP_RUN; + AppRecPtr->ControlReq.AppTimerMsec = 0; + + CFE_ES_AppRecordSetUsed(AppRecPtr, CFE_RESOURCEID_RESERVED); + CFE_ES_Global.LastAppId = PendingAppId; + } + + CFE_ES_UnlockSharedData(__func__, __LINE__); + + /* + ** If a slot was found, create the application + */ + if (AppRecPtr != NULL) + { + /* + ** Start the core app main task + ** (core apps are already in memory - no loading needed) + */ + ReturnCode = CFE_ES_StartAppTask( + &AppRecPtr->MainTaskId, AppRecPtr->AppName, CFE_ES_ObjectTable[i].FuncPtrUnion.MainTaskPtr, + &AppRecPtr->StartParams.MainTaskInfo, CFE_ES_APPID_C(PendingAppId)); + + /* + * Finalize data in the app table entry, which must be done under lock. + * This transitions the entry from being RESERVED to the real type, + * either MAIN_TASK (success) or returning to INVALID (failure). + */ + CFE_ES_LockSharedData(__func__, __LINE__); + + if (ReturnCode == OS_SUCCESS) + { + CFE_ES_AppRecordSetUsed(AppRecPtr, PendingAppId); + + /* + ** Increment the Core App counter. + */ + CFE_ES_Global.RegisteredCoreApps++; + ReturnCode = CFE_SUCCESS; + } + else + { + /* failure mode - just clear the whole app table entry. + * This will set the AppType back to CFE_ES_ResourceType_INVALID (0), + * as well as clearing any other data that had been written */ + memset(AppRecPtr, 0, sizeof(*AppRecPtr)); + } + + CFE_ES_UnlockSharedData(__func__, __LINE__); + } + else + { + /* appSlot not found -- This should never happen!*/ + CFE_ES_WriteToSysLog("ES Startup: Error, No free application slots available for CORE App!\n"); + ReturnCode = CFE_ES_ERR_APP_CREATE; + } + + if (ReturnCode == CFE_SUCCESS) + { + /* + * CFE_ES_MainTaskSyncDelay() will delay this thread until the + * newly-started thread calls CFE_ES_WaitForSystemState() + */ + ReturnCode = + CFE_ES_MainTaskSyncDelay(CFE_ES_AppState_RUNNING, CFE_PLATFORM_CORE_MAX_STARTUP_MSEC * 1000); + } + + if (ReturnCode != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("ES Startup: OS_TaskCreate error creating core App: %s: EC = 0x%08X\n", + CFE_ES_ObjectTable[i].ObjectName, (unsigned int)ReturnCode); + + /* + ** Delay to allow the message to be read + */ + OS_TaskDelay(CFE_ES_PANIC_DELAY); + + /* + ** cFE Cannot continue to start up. + */ + CFE_PSP_Panic(CFE_PSP_PANIC_CORE_APP); + } + break; + + case CFE_ES_FUNCTION_CALL: /*----------------------------------------------------------*/ + + if (CFE_ES_ObjectTable[i].FuncPtrUnion.FunctionPtr != NULL) + { + CFE_ES_WriteToSysLog("ES Startup: Calling %s\n", CFE_ES_ObjectTable[i].ObjectName); + /* + ** Call the function + */ + ReturnCode = (*CFE_ES_ObjectTable[i].FuncPtrUnion.FunctionPtr)(); + if (ReturnCode != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("ES Startup: Error returned when calling function: %s: EC = 0x%08X\n", + CFE_ES_ObjectTable[i].ObjectName, (unsigned int)ReturnCode); + + /* + ** Delay to allow the message to be read + */ + OS_TaskDelay(CFE_ES_PANIC_DELAY); + + /* + ** cFE Cannot continue to start up. + */ + CFE_PSP_Panic(CFE_PSP_PANIC_CORE_APP); + + } /* end if */ + } + else + { + CFE_ES_WriteToSysLog("ES Startup: bad function pointer ( table entry = %d).\n", i); + } + break; + + case CFE_ES_NULL_ENTRY: /*-------------------------------------------------------*/ + break; + default: + break; + } /* end switch */ + + } /* end for */ + + CFE_ES_WriteToSysLog("ES Startup: Finished ES CreateObject table entries.\n"); +} + +/* +** Function: CFE_ES_MainTaskSyncDelay +** +** Purpose: Waits for all of the applications that CFE has started thus far to +** reach the indicated state, by polling the app counters in a delay loop. +** +*/ +int32 CFE_ES_MainTaskSyncDelay(uint32 AppStateId, uint32 TimeOutMilliseconds) +{ + int32 Status; + uint32 i; + uint32 WaitTime; + uint32 WaitRemaining; + uint32 AppNotReadyCounter; + CFE_ES_AppRecord_t *AppRecPtr; + + Status = CFE_ES_OPERATION_TIMED_OUT; + WaitRemaining = TimeOutMilliseconds; + while (true) + { + AppNotReadyCounter = 0; + + /* + * Count the number of apps that are NOT in (at least) in the state requested + */ + CFE_ES_LockSharedData(__func__, __LINE__); + AppRecPtr = CFE_ES_Global.AppTable; + for (i = 0; i < CFE_PLATFORM_ES_MAX_APPLICATIONS; i++) + { + if (CFE_ES_AppRecordIsUsed(AppRecPtr) && (AppRecPtr->AppState < AppStateId)) + { + ++AppNotReadyCounter; + } + ++AppRecPtr; + } + CFE_ES_UnlockSharedData(__func__, __LINE__); + + if (AppNotReadyCounter == 0) + { + /* Condition Met */ + Status = CFE_SUCCESS; + break; + } + + /* + * Must delay and check again + */ + if (WaitRemaining > CFE_PLATFORM_ES_STARTUP_SYNC_POLL_MSEC) + { + WaitTime = CFE_PLATFORM_ES_STARTUP_SYNC_POLL_MSEC; + } + else if (WaitRemaining > 0) + { + WaitTime = WaitRemaining; + } + else + { + break; + } + + OS_TaskDelay(WaitTime); + WaitRemaining -= WaitTime; + } + + return Status; +} diff --git a/modules/es/fsw/src/cfe_es_start.h b/modules/es/fsw/src/cfe_es_start.h new file mode 100644 index 000000000..e9408d370 --- /dev/null +++ b/modules/es/fsw/src/cfe_es_start.h @@ -0,0 +1,94 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/** + * @file + * + * Purpose: + * cFE core startup module defines, data types and prototypes. + * + * References: + * Flight Software Branch C Coding Standard Version 1.0a + * cFE Flight Software Application Developers Guide + * + * Notes: + * + */ + +#ifndef CFE_ES_START_H +#define CFE_ES_START_H + +/* +** Include Files +*/ +#include "cfe_es_api_typedefs.h" + +/* +** Macro Definitions +*/ + +/* +** values of object_type in OS object table +*/ +#define CFE_ES_NULL_ENTRY 0x00 +#define CFE_ES_CORE_TASK 0x01 +#define CFE_ES_DRIVER_TASK 0x02 +#define CFE_ES_BIN_SEM 0x03 +#define CFE_ES_FUNCTION_CALL 0x04 +#define CFE_ES_MUTEX_SEM 0x05 + +/* +** Type Definitions +*/ + +typedef int32 (*CFE_ES_EarlyInitFuncPtr_t)(void); /**< \brief Req'd prototype of Early Init Functions */ + +typedef union +{ + CFE_ES_EarlyInitFuncPtr_t FunctionPtr; + CFE_ES_TaskEntryFuncPtr_t MainTaskPtr; + void * VoidPtr; +} CFE_ES_FuncPtrUnion_t; + +typedef struct +{ + uint32 ObjectType; /* The type of object being created */ + char ObjectName[OS_MAX_API_NAME]; /* task or OS object name */ + CFE_ES_FuncPtrUnion_t FuncPtrUnion; /* task or function reference */ + uint32 ObjectPriority; /* object priority */ + uint32 ObjectSize; /* size used for stack, queue size, etc. */ + uint32 ObjectFlags; /* extra flags to pass */ + +} CFE_ES_ObjectTable_t; + +/* +** Exported data +*/ +extern CFE_ES_ObjectTable_t CFE_ES_ObjectTable[CFE_PLATFORM_ES_OBJECT_TABLE_SIZE]; /* es object table */ + +/* +** Function prototypes +*/ +extern void CFE_ES_CreateObjects(void); +extern void CFE_ES_SetupResetVariables(uint32 StartType, uint32 StartSubtype, uint32 BootSource); +extern void CFE_ES_InitializeFileSystems(uint32 StartType); +extern void CFE_ES_SetupPerfVariables(uint32 ResetType); + +#endif /* CFE_ES_START_H */ diff --git a/modules/es/fsw/src/cfe_es_syslog.c b/modules/es/fsw/src/cfe_es_syslog.c new file mode 100644 index 000000000..7f0376872 --- /dev/null +++ b/modules/es/fsw/src/cfe_es_syslog.c @@ -0,0 +1,556 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/* +** File: +** cfe_es_syslog.c +** +** Purpose: +** This file implements the cFE Executive Services System Log functions. +** +** References: +** Flight Software Branch C Coding Standard Version 1.0a +** cFE Flight Software Application Developers Guide +** +** Notes: +** +** Some functions have EXTERNAL SYNC REQUIREMENTS +** +** SysLog functions marked with "Unsync" in their name are designated +** as functions which are _not_ safe to be called concurrently by multiple +** threads, and also do _not_ implement any locking or protection. These +** functions expect the caller to perform all thread synchronization before +** calling it. +** +** The synchronization requirement is across all functions; i.e. it is not safe +** to call B_Unsync() while A_Unsync() is executing or vice-versa. The external +** lock must wait until A_Unsync() finishes before calling B_Unsync(). +** +** The expectation is that the required level of synchronization can be achieved +** using the existing ES shared data lock. However, if it becomes necessary, this +** could be replaced with a finer grained syslog-specific lock. +*/ + +/* +** Required header files. +*/ +#include "cfe_es_module_all.h" + +#include +#include +#include +#include + +/******************************************************************* + * + * Non-synchronized helper functions + * + * An external mutex be held while calling any function marked "Unsync" + * These helper functions are local to the ES subsystem and must _NOT_ + * be exposed to the public API. + * + * For external access, a public wrapper API must first acquire the + * necessary mutex before calling any function marked as "Unsync" + * + *******************************************************************/ + +/* + * ----------------------------------------------------------------- + * CFE_ES_SysLogClear -- + * Clear system log & index + * ----------------------------------------------------------------- + */ +void CFE_ES_SysLogClear_Unsync(void) +{ + /* + * Note - no need to actually memset the SystemLog buffer - + * by simply zeroing out the indices will cover it. + */ + + CFE_ES_Global.ResetDataPtr->SystemLogWriteIdx = 0; + CFE_ES_Global.ResetDataPtr->SystemLogEndIdx = 0; + CFE_ES_Global.ResetDataPtr->SystemLogEntryNum = 0; + +} /* End of CFE_ES_SysLogClear_Unsync() */ + +/* + * ----------------------------------------------------------------- + * CFE_ES_SysLogReadStart_Unsync -- + * Locate start (oldest message) of syslog for reading + * ----------------------------------------------------------------- + */ +void CFE_ES_SysLogReadStart_Unsync(CFE_ES_SysLogReadBuffer_t *Buffer) +{ + size_t ReadIdx; + size_t EndIdx; + size_t TotalSize; + + ReadIdx = CFE_ES_Global.ResetDataPtr->SystemLogWriteIdx; + EndIdx = CFE_ES_Global.ResetDataPtr->SystemLogEndIdx; + TotalSize = EndIdx; + + /* + * Ensure that we start reading at the start of a message + * Likely pointing to an old fragment right now -- find the end of it + */ + while (TotalSize > 0 && ReadIdx < EndIdx) + { + ++ReadIdx; + --TotalSize; + if (CFE_ES_Global.ResetDataPtr->SystemLog[ReadIdx - 1] == '\n') + { + break; + } + } + + Buffer->SizeLeft = TotalSize; + Buffer->LastOffset = ReadIdx; + Buffer->EndIdx = EndIdx; + Buffer->BlockSize = 0; +} /* End of CFE_ES_SysLogReadStart_Unsync() */ + +/* + * ----------------------------------------------------------------- + * CFE_ES_SysLogAppend_Unsync() -- + * Append a preformatted string to the syslog + * ----------------------------------------------------------------- + */ +int32 CFE_ES_SysLogAppend_Unsync(const char *LogString) +{ + int32 ReturnCode; + size_t MessageLen; + size_t WriteIdx; + size_t EndIdx; + + /* + * Sanity check - Make sure the message length is actually reasonable + * Do not allow any single message to consume more than half of the total log + * (even this may be overly generous) + */ + MessageLen = strlen(LogString); + if (MessageLen > (CFE_PLATFORM_ES_SYSTEM_LOG_SIZE / 2)) + { + MessageLen = CFE_PLATFORM_ES_SYSTEM_LOG_SIZE / 2; + ReturnCode = CFE_ES_ERR_SYS_LOG_TRUNCATED; + } + else + { + ReturnCode = CFE_SUCCESS; + } + + /* + * Final sanity check -- do not bother logging empty messages + */ + if (MessageLen == 0) + { + return ReturnCode; + } + + /* + * Real work begins -- + * Take a local snapshot of the head & tail index values + * + * WriteIdx -> indicates 1 byte past the end of the newest message + * (this is the place where new messages will be added) + * + * EndIdx -> indicates the entire size of the buffer + * + * Keeping them in local stack variables allows more efficient modification, + * since CFE_ES_Global.ResetDataPtr may point directly into a slower NVRAM space. + */ + WriteIdx = CFE_ES_Global.ResetDataPtr->SystemLogWriteIdx; + EndIdx = CFE_ES_Global.ResetDataPtr->SystemLogEndIdx; + + /* + * Check if the log message plus will fit between + * the HeadIdx and the end of the buffer. + * + * If so, then the process can proceed as normal. + * + * If not, then the action depends on the setting of "SystemLogMode" which will be + * to either discard (default) or overwrite + */ + if ((WriteIdx + MessageLen) > CFE_PLATFORM_ES_SYSTEM_LOG_SIZE) + { + if (CFE_ES_Global.ResetDataPtr->SystemLogMode == CFE_ES_LogMode_OVERWRITE) + { + /* In "overwrite" mode, start back at the beginning of the buffer */ + EndIdx = WriteIdx; + WriteIdx = 0; + } + else if (WriteIdx < (CFE_PLATFORM_ES_SYSTEM_LOG_SIZE - CFE_TIME_PRINTED_STRING_SIZE)) + { + /* In "discard" mode, save as much as possible and discard the remainder of the message + * However this should only be done if there is enough room for at least a full timestamp, + * otherwise the fragment will not be useful at all. */ + MessageLen = CFE_PLATFORM_ES_SYSTEM_LOG_SIZE - WriteIdx; + ReturnCode = CFE_ES_ERR_SYS_LOG_TRUNCATED; + } + else + { + /* entire message must be discarded */ + MessageLen = 0; + } + } + + if (MessageLen == 0) + { + ReturnCode = CFE_ES_ERR_SYS_LOG_FULL; + } + else + { + /* + * Copy the message in, EXCEPT for the last char which is probably a newline + */ + memcpy(&CFE_ES_Global.ResetDataPtr->SystemLog[WriteIdx], LogString, MessageLen - 1); + WriteIdx += MessageLen; + + /* + * Ensure the that last-written character is a newline. + * This would have been enforced already except in cases where + * the message got truncated. + */ + CFE_ES_Global.ResetDataPtr->SystemLog[WriteIdx - 1] = '\n'; + + /* + * Keep track of the buffer endpoint for future reference + */ + if (WriteIdx > EndIdx) + { + EndIdx = WriteIdx; + } + + /* + * Export updated index values to the reset area for next time. + */ + CFE_ES_Global.ResetDataPtr->SystemLogWriteIdx = WriteIdx; + CFE_ES_Global.ResetDataPtr->SystemLogEndIdx = EndIdx; + ++CFE_ES_Global.ResetDataPtr->SystemLogEntryNum; + } + + return (ReturnCode); +} /* End of CFE_ES_SysLogAppend_Unsync() */ + +/* + * ----------------------------------------------------------------- + * CFE_ES_SysLogWrite_Unsync() -- + * Identical to the public CFE_ES_WriteToSysLog() function, except + * that it operates in an unsynchronized manner. It can be used in + * cases where the appropriate lock is already held for other reasons + * ----------------------------------------------------------------- + */ +int32 CFE_ES_SysLogWrite_Unsync(const char *SpecStringPtr, ...) +{ + char TmpString[CFE_ES_MAX_SYSLOG_MSG_SIZE]; + va_list ArgPtr; + + va_start(ArgPtr, SpecStringPtr); + CFE_ES_SysLog_vsnprintf(TmpString, sizeof(TmpString), SpecStringPtr, ArgPtr); + va_end(ArgPtr); + + /* Output the entry to the console */ + OS_printf("%s", TmpString); + + /* + * Append to the syslog buffer + */ + return CFE_ES_SysLogAppend_Unsync(TmpString); +} /* End of CFE_ES_SysLogWrite_Unsync() */ + +/******************************************************************* + * + * Additional helper functions + * + * These functions either perform all necessary synchronization internally, + * or they have no specific synchronization requirements + * + *******************************************************************/ + +/* + * ----------------------------------------------------------------- + * CFE_ES_SysLogReadData -- + * Copy data out of the syslog buffer into a local buffer + * ----------------------------------------------------------------- + */ +void CFE_ES_SysLogReadData(CFE_ES_SysLogReadBuffer_t *Buffer) +{ + size_t BlockSize; + + Buffer->BlockSize = 0; + while (Buffer->SizeLeft > 0 && Buffer->BlockSize < sizeof(Buffer->Data)) + { + /* + * The next block to copy will be the SMALLEST of: + * - total remaining (un-copied) size of the syslog data + * - space available in the output buffer + * - space between the current read offset and the end of the log buffer (wrap point) + */ + BlockSize = sizeof(Buffer->Data) - Buffer->BlockSize; + if (Buffer->LastOffset >= Buffer->EndIdx) + { + Buffer->LastOffset = 0; + } + if ((Buffer->LastOffset + BlockSize) > Buffer->EndIdx) + { + BlockSize = Buffer->EndIdx - Buffer->LastOffset; + } + if (BlockSize > Buffer->SizeLeft) + { + BlockSize = Buffer->SizeLeft; + } + + if (BlockSize == 0) + { + /* should be impossible for this to happen, + * just in case, do not spin endlessly */ + break; + } + + memcpy(&Buffer->Data[Buffer->BlockSize], &CFE_ES_Global.ResetDataPtr->SystemLog[Buffer->LastOffset], BlockSize); + + Buffer->BlockSize += BlockSize; + Buffer->LastOffset += BlockSize; + Buffer->SizeLeft -= BlockSize; + } +} /* End of CFE_ES_SysLogReadData() */ + +/* + * ----------------------------------------------------------------- + * CFE_ES_SysLogOverwrite() -- + * Sets the SysLog Write mode (discard or overwrite) + * ----------------------------------------------------------------- + */ +int32 CFE_ES_SysLogSetMode(CFE_ES_LogMode_Enum_t Mode) +{ + int32 Status; + + if ((Mode == CFE_ES_LogMode_OVERWRITE) || (Mode == CFE_ES_LogMode_DISCARD)) + { + CFE_ES_Global.ResetDataPtr->SystemLogMode = Mode; + Status = CFE_SUCCESS; + } + else + { + Status = CFE_ES_BAD_ARGUMENT; + } + + return Status; +} /* End of CFE_ES_SysLogSetMode() */ + +/* + * ----------------------------------------------------------------- + * CFE_ES_SysLog_vsnprintf() -- + * Obtain a correctly formatted, time stamped message for output to the syslog, + * with arguments similar to the "vsnprintf()" C library API call + * ----------------------------------------------------------------- + */ +void CFE_ES_SysLog_vsnprintf(char *Buffer, size_t BufferSize, const char *SpecStringPtr, va_list ArgPtr) +{ + size_t StringLen; + size_t MaxLen; + int PrintLen; + + /* + * write the current time into the TmpString buffer + * + * Note that CFE_TIME_Print() is expected to produce a string of exactly + * CFE_TIME_PRINTED_STRING_SIZE in length. + */ + StringLen = 0; + if (BufferSize > (CFE_TIME_PRINTED_STRING_SIZE + 2)) + { + /* + * The "useful" buffer size is two less than the supplied buffer - + * due to the addition of a newline and a null char to terminate the string + */ + MaxLen = BufferSize - 2; + + CFE_TIME_Print(Buffer, CFE_TIME_GetTime()); + + /* using strlen() anyway in case the specific format of CFE_TIME_Print() changes someday */ + StringLen = strlen(Buffer); + if (StringLen < MaxLen) + { + /* overwrite null with a space to separate the timestamp from the content */ + Buffer[StringLen] = ' '; + ++StringLen; + + /* note that vsnprintf() may return a size larger than the buffer, if it truncates. */ + PrintLen = vsnprintf(&Buffer[StringLen], BufferSize - StringLen, SpecStringPtr, ArgPtr); + if (PrintLen > 0) + { + StringLen += PrintLen; + } + } + + if (StringLen > MaxLen) + { + /* the message got truncated */ + StringLen = MaxLen; + } + + /* + * Finalize the output string. + * + * To be consistent when writing to the console, it is important that + * every printed string end in a newline - particularly if the console is buffered. + * + * The caller may or may not have included a newline in the original format + * string. Most callers do, but some do not. + * + * Strip off all trailing whitespace, and add back a single newline + */ + while (StringLen > 0 && isspace((unsigned char)Buffer[StringLen - 1])) + { + --StringLen; + } + Buffer[StringLen] = '\n'; + ++StringLen; + } + + if (BufferSize > 0) + { + /* always output a null terminated string */ + Buffer[StringLen] = 0; + } +} /* End of CFE_ES_SysLog_vsnprintf() */ + +/* + * ----------------------------------------------------------------- + * CFE_ES_SysLog_snprintf() -- + * Obtain a correctly formatted, time stamped message for output to the syslog, + * with arguments similar to the "snprintf()" C library API call + * ----------------------------------------------------------------- + */ +void CFE_ES_SysLog_snprintf(char *Buffer, size_t BufferSize, const char *SpecStringPtr, ...) +{ + va_list ArgPtr; + + va_start(ArgPtr, SpecStringPtr); + CFE_ES_SysLog_vsnprintf(Buffer, BufferSize, SpecStringPtr, ArgPtr); + va_end(ArgPtr); +} /* End of CFE_ES_SysLog_snprintf() */ + +/* + * ----------------------------------------------------------------- + * CFE_ES_SysLogDump() -- + * Writes the contents of the syslog buffer to disk file + * ----------------------------------------------------------------- + */ +int32 CFE_ES_SysLogDump(const char *Filename) +{ + osal_id_t fd; + int32 Status; + size_t WritePos; + size_t TotalSize; + size_t LastReqSize; + union + { + CFE_ES_SysLogReadBuffer_t LogData; + CFE_FS_Header_t FileHdr; + } Buffer; + + Status = OS_OpenCreate(&fd, Filename, OS_FILE_FLAG_CREATE | OS_FILE_FLAG_TRUNCATE, OS_WRITE_ONLY); + if (Status < 0) + { + CFE_EVS_SendEvent(CFE_ES_SYSLOG2_ERR_EID, CFE_EVS_EventType_ERROR, "Error creating file %s, RC = 0x%08X", + Filename, (unsigned int)Status); + return CFE_ES_FILE_IO_ERR; + } /* end if */ + + CFE_FS_InitHeader(&Buffer.FileHdr, CFE_ES_SYS_LOG_DESC, CFE_FS_SubType_ES_SYSLOG); + + TotalSize = 0; + LastReqSize = sizeof(CFE_FS_Header_t); + Status = CFE_FS_WriteHeader(fd, &Buffer.FileHdr); + if (Status >= 0) + { + TotalSize += Status; + + /* + * Get a snapshot of the buffer pointers and read the first block of + * data while locked - ensuring that nothing additional can be written + * into the syslog buffer while getting the first block of log data. + */ + CFE_ES_LockSharedData(__func__, __LINE__); + CFE_ES_SysLogReadStart_Unsync(&Buffer.LogData); + CFE_ES_SysLogReadData(&Buffer.LogData); + CFE_ES_UnlockSharedData(__func__, __LINE__); + + while (Buffer.LogData.BlockSize > 0) + { + WritePos = 0; + while (WritePos < Buffer.LogData.BlockSize) + { + LastReqSize = Buffer.LogData.BlockSize - WritePos; + Status = OS_write(fd, &Buffer.LogData.Data[WritePos], LastReqSize); + if (Status <= 0) + { + break; + } /* end if */ + + WritePos += Status; + TotalSize += Status; + } + + if (Status <= 0) + { + break; + } + + /* + * _NOT_ taking the lock for subsequent reads -- + * + * All syslog index values use the local snapshots that were taken earlier. + * (The shared memory index values are not referenced on subsequent reads) + * + * If a new syslog message _does_ get written while this is in progress, it + * should be writing to a different part of the syslog buffer anyway, and + * probably will not overwrite the data about to be read here. + * + * There is still a possibility of a "flood" of syslogs coming in which would + * potentially overwrite unread data and cause message loss/corruption. However + * taking a lock here will not alleviate that situation - this means that the + * buffer simply isn't big enough. + */ + CFE_ES_SysLogReadData(&Buffer.LogData); + } + } + + OS_close(fd); + + if (Status <= 0) + { + CFE_ES_FileWriteByteCntErr(Filename, LastReqSize, Status); + Status = CFE_ES_FILE_IO_ERR; + } + else + { + CFE_EVS_SendEvent(CFE_ES_SYSLOG2_EID, CFE_EVS_EventType_DEBUG, "%s written:Size=%lu,Entries=%u", Filename, + (unsigned long)TotalSize, + (unsigned int)CFE_ES_Global.TaskData.HkPacket.Payload.SysLogEntries); + Status = CFE_SUCCESS; + } + + return Status; + +} /* End of CFE_ES_SysLogDump() */ + +/* end of file */ diff --git a/modules/es/fsw/src/cfe_es_task.c b/modules/es/fsw/src/cfe_es_task.c new file mode 100644 index 000000000..5c2f28301 --- /dev/null +++ b/modules/es/fsw/src/cfe_es_task.c @@ -0,0 +1,2077 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/* +** File: cfe_es_task.c +** +** Purpose: +** cFE Executive Services (ES) task +** +** References: +** Flight Software Branch C Coding Standard Version 1.0a +** cFE Flight Software Application Developers Guide +** +** Notes: +** +*/ + +/* +** Includes +*/ +#include "cfe_es_module_all.h" + +#include "cfe_version.h" +#include "target_config.h" + +#include + +/* +** Defines +*/ +#define CFE_ES_PERF_TRIGGERMASK_INT_SIZE \ + (sizeof(CFE_ES_Global.ResetDataPtr->Perf.MetaData.TriggerMask) / sizeof(uint32)) +#define CFE_ES_PERF_TRIGGERMASK_EXT_SIZE \ + (sizeof(CFE_ES_Global.TaskData.HkPacket.Payload.PerfTriggerMask) / sizeof(uint32)) +#define CFE_ES_PERF_FILTERMASK_INT_SIZE (sizeof(CFE_ES_Global.ResetDataPtr->Perf.MetaData.FilterMask) / sizeof(uint32)) +#define CFE_ES_PERF_FILTERMASK_EXT_SIZE \ + (sizeof(CFE_ES_Global.TaskData.HkPacket.Payload.PerfFilterMask) / sizeof(uint32)) + +/* +** This define should be put in the OS API headers -- Right now it matches what the OS API uses +*/ +#define OS_MAX_PRIORITY 255 + +/* +** Executive Services (ES) task global data. +*/ +CFE_ES_TaskData_t CFE_ES_TaskData; + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_TaskMain() -- Task entry point and main process loop */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +void CFE_ES_TaskMain(void) +{ + int32 Status; + uint32 AppRunStatus = CFE_ES_RunStatus_APP_RUN; + CFE_SB_Buffer_t *SBBufPtr; + + /* + ** Performance Time Stamp Entry + */ + CFE_ES_PerfLogEntry(CFE_MISSION_ES_MAIN_PERF_ID); + + /* + ** Perform task specific initialization. + */ + Status = CFE_ES_TaskInit(); + if (Status != CFE_SUCCESS) + { + /* + ** Create a syslog entry + */ + CFE_ES_WriteToSysLog("ES:Application Init Failed,RC=0x%08X\n", (unsigned int)Status); + + /* + ** Allow Core App to Exit + */ + AppRunStatus = CFE_ES_RunStatus_CORE_APP_INIT_ERROR; + + } /* end if */ + + /* + * Wait for other apps to start. + * It is important that the core apps are present before this starts receiving + * messages from the command pipe, as some of those handlers might depend on + * the other core apps. + */ + CFE_ES_WaitForSystemState(CFE_ES_SystemState_CORE_READY, CFE_PLATFORM_CORE_MAX_STARTUP_MSEC); + + /* + ** Main process loop + */ + while (AppRunStatus == CFE_ES_RunStatus_APP_RUN) + { + /* + ** Increment the main task execution counter + ** This is normally done in the CFE_ES_RunLoop call, but + ** currently CFE Child tasks and the cFE core tasks do not + ** use the RunLoop call. + */ + CFE_ES_IncrementTaskCounter(); + + /* + ** Performance Time Stamp Exit + */ + CFE_ES_PerfLogExit(CFE_MISSION_ES_MAIN_PERF_ID); + + /* + ** Wait for the next Software Bus message. + */ + Status = CFE_SB_ReceiveBuffer(&SBBufPtr, CFE_ES_Global.TaskData.CmdPipe, CFE_SB_PEND_FOREVER); + + /* + ** Performance Time Stamp Entry + */ + CFE_ES_PerfLogEntry(CFE_MISSION_ES_MAIN_PERF_ID); + + if (Status == CFE_SUCCESS) + { + /* + ** Process message. + */ + CFE_ES_TaskPipe(SBBufPtr); + + /* + * Wake up the background task, which includes the + * scanning of the ES app table for entries that may need cleanup + */ + CFE_ES_BackgroundWakeup(); + } + else + { + /* + ** SB Error: Write a SysLog Message + */ + CFE_ES_WriteToSysLog("ES:Error reading cmd pipe,RC=0x%08X\n", (unsigned int)Status); + + /* + ** Allow Core App to Exit + */ + AppRunStatus = CFE_ES_RunStatus_CORE_APP_RUNTIME_ERROR; + + } /* end if */ + + } /* end while */ + + /* + ** Performance Time Stamp Exit + */ + CFE_ES_PerfLogExit(CFE_MISSION_ES_MAIN_PERF_ID); + + /* + ** Exit the application, CFE_ES_ExitApp will not return. + */ + CFE_ES_ExitApp(AppRunStatus); + +} /* End of CFE_ES_TaskMain() */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_FindConfigKeyValue() -- Find value for given config key */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +const char *CFE_ES_FindConfigKeyValue(const CFE_ConfigKeyValue_t *ConfigList, const char *KeyName) +{ + const char *ValuePtr; + + ValuePtr = NULL; + if (KeyName != NULL && ConfigList != NULL) + { + while (ConfigList->Key != NULL) + { + if (strcmp(KeyName, ConfigList->Key) == 0) + { + ValuePtr = ConfigList->Value; + break; + } + + ++ConfigList; + } + } + + return ValuePtr; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_GenerateSingleVersionEvent() -- Send CFE_ES_VERSION_INF_EID */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int32 CFE_ES_GenerateSingleVersionEvent(const char *ModuleType, const char *ModuleName) +{ + int32 Status; + const char *VersionString; + + /* The mission version which should appear in the version list under the mission name */ + VersionString = CFE_ES_FindConfigKeyValue(GLOBAL_CONFIGDATA.ModuleVersionList, ModuleName); + + /* If NULL that means the source code was either uncontrolled or there was no way to determine its version */ + if (VersionString == NULL) + { + VersionString = "[unknown]"; + } + + /* + * Advertise the mission version information + */ + Status = CFE_EVS_SendEvent(CFE_ES_VERSION_INF_EID, CFE_EVS_EventType_INFORMATION, "Version Info: %s %s, version %s", + ModuleType, ModuleName, VersionString); + + return Status; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_GenerateVersionEvents() -- Send CFE_ES_VERSION_INF_EID's */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +void CFE_ES_GenerateVersionEvents(void) +{ + int32 Status; + CFE_ConfigName_t * ModuleNamePtr; + CFE_StaticModuleLoadEntry_t *StaticModulePtr; + + /* + * Advertise the mission version information + */ + Status = CFE_ES_GenerateSingleVersionEvent("Mission", GLOBAL_CONFIGDATA.MissionName); + if (Status != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("ES:Error sending mission version event:RC=0x%08X\n", (unsigned int)Status); + } + + /* + * Also Advertise the version information for all statically-linked core modules. + * Send a separate CFE_ES_VERSION_INF_EID for every component. + */ + ModuleNamePtr = GLOBAL_CONFIGDATA.CoreModuleList; + if (ModuleNamePtr != NULL) + { + while (Status == CFE_SUCCESS && ModuleNamePtr->Name != NULL) + { + Status = CFE_ES_GenerateSingleVersionEvent("Core Module", ModuleNamePtr->Name); + if (Status != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("ES:Error sending core module version event:RC=0x%08X\n", (unsigned int)Status); + } + ++ModuleNamePtr; + } + } + + /* + * Advertise PSP module versions + */ + StaticModulePtr = GLOBAL_CONFIGDATA.PspModuleList; + if (StaticModulePtr != NULL) + { + while (Status == CFE_SUCCESS && StaticModulePtr->Name != NULL) + { + Status = CFE_ES_GenerateSingleVersionEvent("PSP Module", StaticModulePtr->Name); + if (Status != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("ES:Error sending PSP module version event:RC=0x%08X\n", (unsigned int)Status); + } + ++StaticModulePtr; + } + } +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_GenerateBuildInfoEvents() -- Send CFE_ES_BUILD_INF_EID */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +void CFE_ES_GenerateBuildInfoEvents(void) +{ + int32 Status; + const char *BuildDate; + const char *BuildUser; + const char *BuildHost; + + BuildDate = CFE_ES_FindConfigKeyValue(GLOBAL_CONFIGDATA.BuildEnvironment, "BUILDDATE"); + BuildUser = CFE_ES_FindConfigKeyValue(GLOBAL_CONFIGDATA.BuildEnvironment, "BUILDUSER"); + BuildHost = CFE_ES_FindConfigKeyValue(GLOBAL_CONFIGDATA.BuildEnvironment, "BUILDHOST"); + + /* Ensure all strings are set to something non-NULL */ + if (BuildDate == NULL) + { + BuildDate = "[unknown]"; + } + + if (BuildUser == NULL) + { + BuildUser = "[unknown]"; + } + + if (BuildHost == NULL) + { + BuildHost = "[unknown]"; + } + + Status = CFE_EVS_SendEvent(CFE_ES_BUILD_INF_EID, CFE_EVS_EventType_INFORMATION, "Build %s by %s@%s, config %s", + BuildDate, BuildUser, BuildHost, GLOBAL_CONFIGDATA.Config); + if (Status != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("ES:Error sending build info event:RC=0x%08X\n", (unsigned int)Status); + } +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_TaskInit() -- ES task initialization */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int32 CFE_ES_TaskInit(void) +{ + int32 Status; + uint32 SizeofCfeSegment; + cpuaddr CfeSegmentAddr; + uint8 VersionNumber[4]; + + /* + ** Initialize task command execution counters + */ + CFE_ES_Global.TaskData.CommandCounter = 0; + CFE_ES_Global.TaskData.CommandErrorCounter = 0; + + /* + ** Initialize systemlog to default Power On or Processor Reset mode + */ + if (CFE_ES_GetResetType(NULL) == CFE_PSP_RST_TYPE_POWERON) + { + CFE_ES_Global.ResetDataPtr->SystemLogMode = CFE_PLATFORM_ES_DEFAULT_POR_SYSLOG_MODE; + } + else + { + CFE_ES_Global.ResetDataPtr->SystemLogMode = CFE_PLATFORM_ES_DEFAULT_PR_SYSLOG_MODE; + } + + /* + ** Register event filter table. + */ + Status = CFE_EVS_Register(NULL, 0, CFE_EVS_EventFilter_BINARY); + if (Status != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("ES:Call to CFE_EVS_Register Failed, RC = 0x%08X\n", (unsigned int)Status); + return (Status); + } + + /* + ** Initialize housekeeping packet (clear user data area) + */ + CFE_MSG_Init(&CFE_ES_Global.TaskData.HkPacket.TlmHeader.Msg, CFE_SB_ValueToMsgId(CFE_ES_HK_TLM_MID), + sizeof(CFE_ES_Global.TaskData.HkPacket)); + + /* + ** Initialize single application telemetry packet + */ + CFE_MSG_Init(&CFE_ES_Global.TaskData.OneAppPacket.TlmHeader.Msg, CFE_SB_ValueToMsgId(CFE_ES_APP_TLM_MID), + sizeof(CFE_ES_Global.TaskData.OneAppPacket)); + + /* + ** Initialize memory pool statistics telemetry packet + */ + CFE_MSG_Init(&CFE_ES_Global.TaskData.MemStatsPacket.TlmHeader.Msg, CFE_SB_ValueToMsgId(CFE_ES_MEMSTATS_TLM_MID), + sizeof(CFE_ES_Global.TaskData.MemStatsPacket)); + + /* + ** Create Software Bus message pipe + */ + Status = CFE_SB_CreatePipe(&CFE_ES_Global.TaskData.CmdPipe, CFE_ES_PIPE_DEPTH, CFE_ES_PIPE_NAME); + if (Status != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("ES:Cannot Create SB Pipe, RC = 0x%08X\n", (unsigned int)Status); + return (Status); + } + + /* + ** Subscribe to Housekeeping request commands + */ + Status = CFE_SB_Subscribe(CFE_SB_ValueToMsgId(CFE_ES_SEND_HK_MID), CFE_ES_Global.TaskData.CmdPipe); + if (Status != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("ES:Cannot Subscribe to HK packet, RC = 0x%08X\n", (unsigned int)Status); + return (Status); + } + + /* + ** Subscribe to ES task ground command packets + */ + Status = CFE_SB_Subscribe(CFE_SB_ValueToMsgId(CFE_ES_CMD_MID), CFE_ES_Global.TaskData.CmdPipe); + if (Status != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("ES:Cannot Subscribe to ES ground commands, RC = 0x%08X\n", (unsigned int)Status); + return (Status); + } + + /* + ** Compute the CRC for the cfe core code segment and place + ** in ES Housekeeping pkt. + */ + Status = CFE_PSP_GetCFETextSegmentInfo(&CfeSegmentAddr, &SizeofCfeSegment); + + if (Status == CFE_PSP_SUCCESS) + { + CFE_ES_Global.TaskData.HkPacket.Payload.CFECoreChecksum = + CFE_ES_CalculateCRC((void *)(CfeSegmentAddr), SizeofCfeSegment, 0, CFE_MISSION_ES_DEFAULT_CRC); + } + else + { + CFE_ES_Global.TaskData.HkPacket.Payload.CFECoreChecksum = 0xFFFF; + } + + /* + ** Initialize the version numbers in the ES Housekeeping pkt + */ + CFE_ES_Global.TaskData.HkPacket.Payload.CFEMajorVersion = CFE_MAJOR_VERSION; + CFE_ES_Global.TaskData.HkPacket.Payload.CFEMinorVersion = CFE_MINOR_VERSION; + CFE_ES_Global.TaskData.HkPacket.Payload.CFERevision = CFE_REVISION; + CFE_ES_Global.TaskData.HkPacket.Payload.CFEMissionRevision = CFE_MISSION_REV; + + OS_GetVersionNumber(VersionNumber); + CFE_ES_Global.TaskData.HkPacket.Payload.OSALMajorVersion = VersionNumber[0]; + CFE_ES_Global.TaskData.HkPacket.Payload.OSALMinorVersion = VersionNumber[1]; + CFE_ES_Global.TaskData.HkPacket.Payload.OSALRevision = VersionNumber[2]; + CFE_ES_Global.TaskData.HkPacket.Payload.OSALMissionRevision = VersionNumber[3]; + + CFE_PSP_GetVersionNumber(VersionNumber); + CFE_ES_Global.TaskData.HkPacket.Payload.PSPMajorVersion = VersionNumber[0]; + CFE_ES_Global.TaskData.HkPacket.Payload.PSPMinorVersion = VersionNumber[1]; + CFE_ES_Global.TaskData.HkPacket.Payload.PSPRevision = VersionNumber[2]; + CFE_ES_Global.TaskData.HkPacket.Payload.PSPMissionRevision = VersionNumber[3]; + + /* + ** Task startup event message. + */ + Status = CFE_EVS_SendEvent(CFE_ES_INIT_INF_EID, CFE_EVS_EventType_INFORMATION, "cFE ES Initialized"); + if (Status != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("ES:Error sending init event:RC=0x%08X\n", (unsigned int)Status); + return (Status); + } + + Status = + CFE_EVS_SendEvent(CFE_ES_INITSTATS_INF_EID, CFE_EVS_EventType_INFORMATION, + "cFS Versions: cfe %s, osal %s, psp %s. cFE chksm %d", CFE_SRC_VERSION, OS_GetVersionString(), + CFE_PSP_GetVersionString(), (int)CFE_ES_Global.TaskData.HkPacket.Payload.CFECoreChecksum); + + if (Status != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("ES:Error sending init stats event:RC=0x%08X\n", (unsigned int)Status); + return (Status); + } + + /* + * Generate all module version and build info events. + */ + CFE_ES_GenerateVersionEvents(); + CFE_ES_GenerateBuildInfoEvents(); + + /* + * Initialize the "background task" which is a low priority child task + * devoted to maintence duties that do not need to execute on a + * strict/precise schedule. + */ + Status = CFE_ES_BackgroundInit(); + if (Status != CFE_SUCCESS) + { + CFE_ES_WriteToSysLog("ES:Error initializing background task:RC=0x%08X\n", (unsigned int)Status); + return (Status); + } + + return (CFE_SUCCESS); + +} /* End of CFE_ES_TaskInit() */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_TaskPipe() -- Process command pipe message */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +void CFE_ES_TaskPipe(CFE_SB_Buffer_t *SBBufPtr) +{ + CFE_SB_MsgId_t MessageID = CFE_SB_INVALID_MSG_ID; + CFE_MSG_FcnCode_t CommandCode = 0; + + CFE_MSG_GetMsgId(&SBBufPtr->Msg, &MessageID); + switch (CFE_SB_MsgIdToValue(MessageID)) + { + /* + ** Housekeeping telemetry request + */ + case CFE_ES_SEND_HK_MID: + CFE_ES_HousekeepingCmd((CFE_MSG_CommandHeader_t *)SBBufPtr); + break; + + /* + ** ES task ground commands + */ + case CFE_ES_CMD_MID: + + CFE_MSG_GetFcnCode(&SBBufPtr->Msg, &CommandCode); + switch (CommandCode) + { + case CFE_ES_NOOP_CC: + if (CFE_ES_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_ES_NoopCmd_t))) + { + CFE_ES_NoopCmd((CFE_ES_NoopCmd_t *)SBBufPtr); + } + break; + + case CFE_ES_RESET_COUNTERS_CC: + if (CFE_ES_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_ES_ResetCountersCmd_t))) + { + CFE_ES_ResetCountersCmd((CFE_ES_ResetCountersCmd_t *)SBBufPtr); + } + break; + + case CFE_ES_RESTART_CC: + if (CFE_ES_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_ES_RestartCmd_t))) + { + CFE_ES_RestartCmd((CFE_ES_RestartCmd_t *)SBBufPtr); + } + break; + + case CFE_ES_START_APP_CC: + if (CFE_ES_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_ES_StartAppCmd_t))) + { + CFE_ES_StartAppCmd((CFE_ES_StartAppCmd_t *)SBBufPtr); + } + break; + + case CFE_ES_STOP_APP_CC: + if (CFE_ES_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_ES_StopAppCmd_t))) + { + CFE_ES_StopAppCmd((CFE_ES_StopAppCmd_t *)SBBufPtr); + } + break; + + case CFE_ES_RESTART_APP_CC: + if (CFE_ES_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_ES_RestartAppCmd_t))) + { + CFE_ES_RestartAppCmd((CFE_ES_RestartAppCmd_t *)SBBufPtr); + } + break; + + case CFE_ES_RELOAD_APP_CC: + if (CFE_ES_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_ES_ReloadAppCmd_t))) + { + CFE_ES_ReloadAppCmd((CFE_ES_ReloadAppCmd_t *)SBBufPtr); + } + break; + + case CFE_ES_QUERY_ONE_CC: + if (CFE_ES_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_ES_QueryOneCmd_t))) + { + CFE_ES_QueryOneCmd((CFE_ES_QueryOneCmd_t *)SBBufPtr); + } + break; + + case CFE_ES_QUERY_ALL_CC: + if (CFE_ES_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_ES_QueryAllCmd_t))) + { + CFE_ES_QueryAllCmd((CFE_ES_QueryAllCmd_t *)SBBufPtr); + } + break; + + case CFE_ES_QUERY_ALL_TASKS_CC: + if (CFE_ES_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_ES_QueryAllTasksCmd_t))) + { + CFE_ES_QueryAllTasksCmd((CFE_ES_QueryAllTasksCmd_t *)SBBufPtr); + } + break; + + case CFE_ES_CLEAR_SYSLOG_CC: + if (CFE_ES_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_ES_ClearSysLogCmd_t))) + { + CFE_ES_ClearSysLogCmd((CFE_ES_ClearSysLogCmd_t *)SBBufPtr); + } + break; + + case CFE_ES_WRITE_SYSLOG_CC: + if (CFE_ES_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_ES_WriteSysLogCmd_t))) + { + CFE_ES_WriteSysLogCmd((CFE_ES_WriteSysLogCmd_t *)SBBufPtr); + } + break; + + case CFE_ES_OVER_WRITE_SYSLOG_CC: + if (CFE_ES_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_ES_OverWriteSysLogCmd_t))) + { + CFE_ES_OverWriteSysLogCmd((CFE_ES_OverWriteSysLogCmd_t *)SBBufPtr); + } + break; + + case CFE_ES_CLEAR_ER_LOG_CC: + if (CFE_ES_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_ES_ClearERLogCmd_t))) + { + CFE_ES_ClearERLogCmd((CFE_ES_ClearERLogCmd_t *)SBBufPtr); + } + break; + + case CFE_ES_WRITE_ER_LOG_CC: + if (CFE_ES_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_ES_WriteERLogCmd_t))) + { + CFE_ES_WriteERLogCmd((CFE_ES_WriteERLogCmd_t *)SBBufPtr); + } + break; + + case CFE_ES_START_PERF_DATA_CC: + if (CFE_ES_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_ES_StartPerfDataCmd_t))) + { + CFE_ES_StartPerfDataCmd((CFE_ES_StartPerfDataCmd_t *)SBBufPtr); + } + break; + + case CFE_ES_STOP_PERF_DATA_CC: + if (CFE_ES_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_ES_StopPerfDataCmd_t))) + { + CFE_ES_StopPerfDataCmd((CFE_ES_StopPerfDataCmd_t *)SBBufPtr); + } + break; + + case CFE_ES_SET_PERF_FILTER_MASK_CC: + if (CFE_ES_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_ES_SetPerfFilterMaskCmd_t))) + { + CFE_ES_SetPerfFilterMaskCmd((CFE_ES_SetPerfFilterMaskCmd_t *)SBBufPtr); + } + break; + + case CFE_ES_SET_PERF_TRIGGER_MASK_CC: + if (CFE_ES_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_ES_SetPerfTriggerMaskCmd_t))) + { + CFE_ES_SetPerfTriggerMaskCmd((CFE_ES_SetPerfTriggerMaskCmd_t *)SBBufPtr); + } + break; + + case CFE_ES_RESET_PR_COUNT_CC: + if (CFE_ES_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_ES_ResetPRCountCmd_t))) + { + CFE_ES_ResetPRCountCmd((CFE_ES_ResetPRCountCmd_t *)SBBufPtr); + } + break; + + case CFE_ES_SET_MAX_PR_COUNT_CC: + if (CFE_ES_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_ES_SetMaxPRCountCmd_t))) + { + CFE_ES_SetMaxPRCountCmd((CFE_ES_SetMaxPRCountCmd_t *)SBBufPtr); + } + break; + + case CFE_ES_DELETE_CDS_CC: + if (CFE_ES_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_ES_DeleteCDSCmd_t))) + { + CFE_ES_DeleteCDSCmd((CFE_ES_DeleteCDSCmd_t *)SBBufPtr); + } + break; + + case CFE_ES_SEND_MEM_POOL_STATS_CC: + if (CFE_ES_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_ES_SendMemPoolStatsCmd_t))) + { + CFE_ES_SendMemPoolStatsCmd((CFE_ES_SendMemPoolStatsCmd_t *)SBBufPtr); + } + break; + + case CFE_ES_DUMP_CDS_REGISTRY_CC: + if (CFE_ES_VerifyCmdLength(&SBBufPtr->Msg, sizeof(CFE_ES_DumpCDSRegistryCmd_t))) + { + CFE_ES_DumpCDSRegistryCmd((CFE_ES_DumpCDSRegistryCmd_t *)SBBufPtr); + } + break; + + default: + CFE_EVS_SendEvent(CFE_ES_CC1_ERR_EID, CFE_EVS_EventType_ERROR, + "Invalid ground command code: ID = 0x%X, CC = %d", + (unsigned int)CFE_SB_MsgIdToValue(MessageID), (int)CommandCode); + CFE_ES_Global.TaskData.CommandErrorCounter++; + break; + } + break; + + default: + + CFE_EVS_SendEvent(CFE_ES_MID_ERR_EID, CFE_EVS_EventType_ERROR, "Invalid command pipe message ID: 0x%X", + (unsigned int)CFE_SB_MsgIdToValue(MessageID)); + CFE_ES_Global.TaskData.CommandErrorCounter++; + break; + } + +} /* End of CFE_ES_TaskPipe() */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_HousekeepingCmd() -- On-board command (HK request) */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int32 CFE_ES_HousekeepingCmd(const CFE_MSG_CommandHeader_t *data) +{ + OS_heap_prop_t HeapProp; + int32 stat; + uint32 PerfIdx; + + /* + ** Get command execution counters, system log entry count & bytes used. + */ + CFE_ES_Global.TaskData.HkPacket.Payload.CommandCounter = CFE_ES_Global.TaskData.CommandCounter; + CFE_ES_Global.TaskData.HkPacket.Payload.CommandErrorCounter = CFE_ES_Global.TaskData.CommandErrorCounter; + + CFE_ES_Global.TaskData.HkPacket.Payload.SysLogBytesUsed = + CFE_ES_MEMOFFSET_C(CFE_ES_Global.ResetDataPtr->SystemLogEndIdx); + CFE_ES_Global.TaskData.HkPacket.Payload.SysLogSize = CFE_ES_MEMOFFSET_C(CFE_PLATFORM_ES_SYSTEM_LOG_SIZE); + CFE_ES_Global.TaskData.HkPacket.Payload.SysLogEntries = CFE_ES_Global.ResetDataPtr->SystemLogEntryNum; + CFE_ES_Global.TaskData.HkPacket.Payload.SysLogMode = CFE_ES_Global.ResetDataPtr->SystemLogMode; + + CFE_ES_Global.TaskData.HkPacket.Payload.ERLogIndex = CFE_ES_Global.ResetDataPtr->ERLogIndex; + CFE_ES_Global.TaskData.HkPacket.Payload.ERLogEntries = CFE_ES_Global.ResetDataPtr->ERLogEntries; + + CFE_ES_Global.TaskData.HkPacket.Payload.RegisteredCoreApps = CFE_ES_Global.RegisteredCoreApps; + CFE_ES_Global.TaskData.HkPacket.Payload.RegisteredExternalApps = CFE_ES_Global.RegisteredExternalApps; + CFE_ES_Global.TaskData.HkPacket.Payload.RegisteredTasks = CFE_ES_Global.RegisteredTasks; + CFE_ES_Global.TaskData.HkPacket.Payload.RegisteredLibs = CFE_ES_Global.RegisteredLibs; + + CFE_ES_Global.TaskData.HkPacket.Payload.ResetType = CFE_ES_Global.ResetDataPtr->ResetVars.ResetType; + CFE_ES_Global.TaskData.HkPacket.Payload.ResetSubtype = CFE_ES_Global.ResetDataPtr->ResetVars.ResetSubtype; + CFE_ES_Global.TaskData.HkPacket.Payload.ProcessorResets = CFE_ES_Global.ResetDataPtr->ResetVars.ProcessorResetCount; + CFE_ES_Global.TaskData.HkPacket.Payload.MaxProcessorResets = + CFE_ES_Global.ResetDataPtr->ResetVars.MaxProcessorResetCount; + CFE_ES_Global.TaskData.HkPacket.Payload.BootSource = CFE_ES_Global.ResetDataPtr->ResetVars.BootSource; + + CFE_ES_Global.TaskData.HkPacket.Payload.PerfState = CFE_ES_Global.ResetDataPtr->Perf.MetaData.State; + CFE_ES_Global.TaskData.HkPacket.Payload.PerfMode = CFE_ES_Global.ResetDataPtr->Perf.MetaData.Mode; + CFE_ES_Global.TaskData.HkPacket.Payload.PerfTriggerCount = CFE_ES_Global.ResetDataPtr->Perf.MetaData.TriggerCount; + CFE_ES_Global.TaskData.HkPacket.Payload.PerfDataStart = CFE_ES_Global.ResetDataPtr->Perf.MetaData.DataStart; + CFE_ES_Global.TaskData.HkPacket.Payload.PerfDataEnd = CFE_ES_Global.ResetDataPtr->Perf.MetaData.DataEnd; + CFE_ES_Global.TaskData.HkPacket.Payload.PerfDataCount = CFE_ES_Global.ResetDataPtr->Perf.MetaData.DataCount; + CFE_ES_Global.TaskData.HkPacket.Payload.PerfDataToWrite = CFE_ES_GetPerfLogDumpRemaining(); + + /* + * Fill out the perf trigger/filter mask objects + * The entire array in the HK payload object (external size) must be filled, + * to avoid sending garbage data. + * + * If it is larger than what the platform supports (internal size), it will + * be padded with 0's + * + * If it is smaller than what the platform supports, then truncate. + */ + for (PerfIdx = 0; PerfIdx < CFE_ES_PERF_TRIGGERMASK_EXT_SIZE; ++PerfIdx) + { + if (PerfIdx < CFE_ES_PERF_TRIGGERMASK_INT_SIZE) + { + CFE_ES_Global.TaskData.HkPacket.Payload.PerfTriggerMask[PerfIdx] = + CFE_ES_Global.ResetDataPtr->Perf.MetaData.TriggerMask[PerfIdx]; + } + else + { + CFE_ES_Global.TaskData.HkPacket.Payload.PerfTriggerMask[PerfIdx] = 0; + } + } + + for (PerfIdx = 0; PerfIdx < CFE_ES_PERF_FILTERMASK_EXT_SIZE; ++PerfIdx) + { + if (PerfIdx < CFE_ES_PERF_FILTERMASK_INT_SIZE) + { + CFE_ES_Global.TaskData.HkPacket.Payload.PerfFilterMask[PerfIdx] = + CFE_ES_Global.ResetDataPtr->Perf.MetaData.FilterMask[PerfIdx]; + } + else + { + CFE_ES_Global.TaskData.HkPacket.Payload.PerfFilterMask[PerfIdx] = 0; + } + } + + stat = OS_HeapGetInfo(&HeapProp); + + /* + * If retrieving info from OSAL was not successful, + * zero out the property struct, so all sizes will + * in turn be reported in telemetry as 0. + */ + if (stat != OS_SUCCESS) + { + memset(&HeapProp, 0, sizeof(HeapProp)); + } + + CFE_ES_Global.TaskData.HkPacket.Payload.HeapBytesFree = CFE_ES_MEMOFFSET_C(HeapProp.free_bytes); + CFE_ES_Global.TaskData.HkPacket.Payload.HeapBlocksFree = CFE_ES_MEMOFFSET_C(HeapProp.free_blocks); + CFE_ES_Global.TaskData.HkPacket.Payload.HeapMaxBlockSize = CFE_ES_MEMOFFSET_C(HeapProp.largest_free_block); + + /* + ** Send housekeeping telemetry packet. + */ + CFE_SB_TimeStampMsg(&CFE_ES_Global.TaskData.HkPacket.TlmHeader.Msg); + CFE_SB_TransmitMsg(&CFE_ES_Global.TaskData.HkPacket.TlmHeader.Msg, true); + + /* + ** This command does not affect the command execution counter. + */ + + return CFE_SUCCESS; +} /* End of CFE_ES_HousekeepingCmd() */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_NoopCmd() -- ES task ground command (NO-OP) */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int32 CFE_ES_NoopCmd(const CFE_ES_NoopCmd_t *Cmd) +{ + /* + ** Advertise the build and version information with the no-op command + ** For unit testing purposes, it helps to put this first - the UT + ** is checking for the last event sent to be NOOP_INF_EID. + */ + CFE_ES_GenerateBuildInfoEvents(); + + /* + ** This command will always succeed. + */ + CFE_ES_Global.TaskData.CommandCounter++; + + CFE_EVS_SendEvent(CFE_ES_NOOP_INF_EID, CFE_EVS_EventType_INFORMATION, + "No-op command:\n cFS Versions: cfe %s, osal %s, psp %s", CFE_SRC_VERSION, OS_GetVersionString(), + CFE_PSP_GetVersionString()); + + return CFE_SUCCESS; +} /* End of CFE_ES_NoopCmd() */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_ResetCountersCmd() -- ES task ground command (reset counters) */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int32 CFE_ES_ResetCountersCmd(const CFE_ES_ResetCountersCmd_t *data) +{ + CFE_ES_Global.TaskData.CommandCounter = 0; + CFE_ES_Global.TaskData.CommandErrorCounter = 0; + + /* + ** This command will always succeed. + */ + CFE_EVS_SendEvent(CFE_ES_RESET_INF_EID, CFE_EVS_EventType_INFORMATION, "Reset Counters command"); + + return CFE_SUCCESS; +} /* End of CFE_ES_ResetCountersCmd() */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_RestartCmd() -- Restart cFE (may reset processor) */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int32 CFE_ES_RestartCmd(const CFE_ES_RestartCmd_t *data) +{ + const CFE_ES_RestartCmd_Payload_t *cmd = &data->Payload; + + if ((cmd->RestartType != CFE_PSP_RST_TYPE_PROCESSOR) && (cmd->RestartType != CFE_PSP_RST_TYPE_POWERON)) + { + CFE_ES_Global.TaskData.CommandErrorCounter++; + CFE_EVS_SendEvent(CFE_ES_BOOT_ERR_EID, CFE_EVS_EventType_ERROR, "Invalid cFE restart type: %d", + (int)cmd->RestartType); + } + else + { + /* + ** This function will not return. + */ + CFE_ES_ResetCFE(cmd->RestartType); + } + + return CFE_SUCCESS; +} /* End of CFE_ES_RestartCmd() */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_StartAppCmd() -- Load (and start) single application */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int32 CFE_ES_StartAppCmd(const CFE_ES_StartAppCmd_t *data) +{ + const CFE_ES_StartAppCmd_Payload_t *cmd = &data->Payload; + CFE_ES_AppId_t AppID; + int32 Result; + int32 AppEntryLen; + int32 AppNameLen; + char LocalAppName[OS_MAX_API_NAME]; + CFE_ES_AppStartParams_t StartParams; + + /* Create local copies of all input strings and ensure null termination */ + Result = CFE_FS_ParseInputFileNameEx(StartParams.BasicInfo.FileName, cmd->AppFileName, + sizeof(StartParams.BasicInfo.FileName), sizeof(cmd->AppFileName), NULL, + CFE_FS_GetDefaultMountPoint(CFE_FS_FileCategory_DYNAMIC_MODULE), + CFE_FS_GetDefaultExtension(CFE_FS_FileCategory_DYNAMIC_MODULE)); + + AppEntryLen = CFE_SB_MessageStringGet(StartParams.BasicInfo.InitSymbolName, cmd->AppEntryPoint, NULL, + sizeof(StartParams.BasicInfo.InitSymbolName), sizeof(cmd->AppEntryPoint)); + + AppNameLen = + CFE_SB_MessageStringGet(LocalAppName, cmd->Application, NULL, sizeof(LocalAppName), sizeof(cmd->Application)); + + /* + ** Verify command parameters + */ + if (Result != CFE_SUCCESS) + { + CFE_ES_Global.TaskData.CommandErrorCounter++; + CFE_EVS_SendEvent(CFE_ES_START_INVALID_FILENAME_ERR_EID, CFE_EVS_EventType_ERROR, + "CFE_ES_StartAppCmd: invalid filename, status=%lx", (unsigned long)Result); + } + else if (AppEntryLen <= 0) + { + CFE_ES_Global.TaskData.CommandErrorCounter++; + CFE_EVS_SendEvent(CFE_ES_START_INVALID_ENTRY_POINT_ERR_EID, CFE_EVS_EventType_ERROR, + "CFE_ES_StartAppCmd: App Entry Point is empty."); + } + else if (AppNameLen <= 0) + { + CFE_ES_Global.TaskData.CommandErrorCounter++; + CFE_EVS_SendEvent(CFE_ES_START_NULL_APP_NAME_ERR_EID, CFE_EVS_EventType_ERROR, + "CFE_ES_StartAppCmd: App Name is empty."); + } + else if (cmd->Priority > OS_MAX_PRIORITY) + { + CFE_ES_Global.TaskData.CommandErrorCounter++; + CFE_EVS_SendEvent(CFE_ES_START_PRIORITY_ERR_EID, CFE_EVS_EventType_ERROR, + "CFE_ES_StartAppCmd: Priority is too large: %d.", (int)cmd->Priority); + } + else if ((cmd->ExceptionAction != CFE_ES_ExceptionAction_RESTART_APP) && + (cmd->ExceptionAction != CFE_ES_ExceptionAction_PROC_RESTART)) + { + CFE_ES_Global.TaskData.CommandErrorCounter++; + CFE_EVS_SendEvent(CFE_ES_START_EXC_ACTION_ERR_EID, CFE_EVS_EventType_ERROR, + "CFE_ES_StartAppCmd: Invalid Exception Action: %d.", (int)cmd->ExceptionAction); + } + else + { + /* If stack size was provided, use it, otherwise use default. */ + if (cmd->StackSize == 0) + { + StartParams.MainTaskInfo.StackSize = CFE_PLATFORM_ES_DEFAULT_STACK_SIZE; + } + else + { + StartParams.MainTaskInfo.StackSize = cmd->StackSize; + } + + StartParams.MainTaskInfo.Priority = cmd->Priority; + StartParams.ExceptionAction = cmd->ExceptionAction; + + /* + ** Invoke application loader/startup function. + */ + Result = CFE_ES_AppCreate(&AppID, LocalAppName, &StartParams); + + /* + ** Send appropriate event message + */ + if (Result == CFE_SUCCESS) + { + CFE_ES_Global.TaskData.CommandCounter++; + CFE_EVS_SendEvent(CFE_ES_START_INF_EID, CFE_EVS_EventType_INFORMATION, "Started %s from %s, AppID = %lu", + LocalAppName, StartParams.BasicInfo.FileName, CFE_RESOURCEID_TO_ULONG(AppID)); + } + else + { + CFE_ES_Global.TaskData.CommandErrorCounter++; + CFE_EVS_SendEvent(CFE_ES_START_ERR_EID, CFE_EVS_EventType_ERROR, "Failed to start %s from %s, RC = 0x%08X", + LocalAppName, StartParams.BasicInfo.FileName, (unsigned int)Result); + } + + } /* End if -- command parameter validation */ + + return CFE_SUCCESS; +} /* End of CFE_ES_StartAppCmd() */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_StopAppCmd() -- Stop single application */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int32 CFE_ES_StopAppCmd(const CFE_ES_StopAppCmd_t *data) +{ + const CFE_ES_AppNameCmd_Payload_t *cmd = &data->Payload; + char LocalApp[OS_MAX_API_NAME]; + CFE_ES_AppId_t AppID; + int32 Result; + + CFE_SB_MessageStringGet(LocalApp, (char *)cmd->Application, NULL, sizeof(LocalApp), sizeof(cmd->Application)); + + Result = CFE_ES_GetAppIDByName(&AppID, LocalApp); + + if (Result == CFE_SUCCESS) + { + /* + ** Delete the App + */ + Result = CFE_ES_DeleteApp(AppID); + + /* + ** Send appropriate event message. + */ + if (Result == CFE_SUCCESS) + { + CFE_ES_Global.TaskData.CommandCounter++; + CFE_EVS_SendEvent(CFE_ES_STOP_DBG_EID, CFE_EVS_EventType_DEBUG, "Stop Application %s Initiated.", LocalApp); + } + else + { + CFE_ES_Global.TaskData.CommandErrorCounter++; + CFE_EVS_SendEvent(CFE_ES_STOP_ERR1_EID, CFE_EVS_EventType_ERROR, "Stop Application %s Failed, RC = 0x%08X", + LocalApp, (unsigned int)Result); + } + } + else + { + CFE_ES_Global.TaskData.CommandErrorCounter++; + CFE_EVS_SendEvent(CFE_ES_STOP_ERR2_EID, CFE_EVS_EventType_ERROR, + "Stop Application %s, GetAppIDByName failed. RC = 0x%08X.", LocalApp, (unsigned int)Result); + } + + return CFE_SUCCESS; +} /* End of CFE_ES_StopAppCmd() */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_RestartAppCmd() -- Restart a single application */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int32 CFE_ES_RestartAppCmd(const CFE_ES_RestartAppCmd_t *data) +{ + const CFE_ES_AppNameCmd_Payload_t *cmd = &data->Payload; + char LocalApp[OS_MAX_API_NAME]; + CFE_ES_AppId_t AppID; + int32 Result; + + CFE_SB_MessageStringGet(LocalApp, (char *)cmd->Application, NULL, sizeof(LocalApp), sizeof(cmd->Application)); + + Result = CFE_ES_GetAppIDByName(&AppID, LocalApp); + + if (Result == CFE_SUCCESS) + { + Result = CFE_ES_RestartApp(AppID); + + /* + ** Send appropriate event message. + */ + if (Result == CFE_SUCCESS) + { + CFE_ES_Global.TaskData.CommandCounter++; + CFE_EVS_SendEvent(CFE_ES_RESTART_APP_DBG_EID, CFE_EVS_EventType_DEBUG, "Restart Application %s Initiated.", + LocalApp); + } + else + { + CFE_ES_Global.TaskData.CommandErrorCounter++; + CFE_EVS_SendEvent(CFE_ES_RESTART_APP_ERR1_EID, CFE_EVS_EventType_ERROR, + "Restart Application %s Failed, RC = 0x%08X", LocalApp, (unsigned int)Result); + } + } + else + { + CFE_ES_Global.TaskData.CommandErrorCounter++; + CFE_EVS_SendEvent(CFE_ES_RESTART_APP_ERR2_EID, CFE_EVS_EventType_ERROR, + "Restart Application %s, GetAppIDByName failed. RC = 0x%08X.", LocalApp, + (unsigned int)Result); + } + + return CFE_SUCCESS; +} /* End of CFE_ES_ResetAppCmd() */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_ReloadAppCmd() -- Reload a single application */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int32 CFE_ES_ReloadAppCmd(const CFE_ES_ReloadAppCmd_t *data) +{ + const CFE_ES_AppReloadCmd_Payload_t *cmd = &data->Payload; + char LocalApp[OS_MAX_API_NAME]; + char LocalFileName[OS_MAX_PATH_LEN]; + CFE_ES_AppId_t AppID; + int32 Result; + + CFE_SB_MessageStringGet(LocalApp, (char *)cmd->Application, NULL, sizeof(LocalApp), sizeof(cmd->Application)); + + Result = CFE_ES_GetAppIDByName(&AppID, LocalApp); + + if (Result == CFE_SUCCESS) + { + /* Read input string as a file name for dynamic module */ + Result = CFE_FS_ParseInputFileNameEx(LocalFileName, cmd->AppFileName, sizeof(LocalFileName), + sizeof(cmd->AppFileName), NULL, + CFE_FS_GetDefaultMountPoint(CFE_FS_FileCategory_DYNAMIC_MODULE), + CFE_FS_GetDefaultExtension(CFE_FS_FileCategory_DYNAMIC_MODULE)); + + if (Result == CFE_SUCCESS) + { + Result = CFE_ES_ReloadApp(AppID, LocalFileName); + } + + /* + ** Send appropriate event message. + */ + if (Result == CFE_SUCCESS) + { + CFE_ES_Global.TaskData.CommandCounter++; + CFE_EVS_SendEvent(CFE_ES_RELOAD_APP_DBG_EID, CFE_EVS_EventType_DEBUG, "Reload Application %s Initiated.", + LocalApp); + } + else + { + CFE_ES_Global.TaskData.CommandErrorCounter++; + CFE_EVS_SendEvent(CFE_ES_RELOAD_APP_ERR1_EID, CFE_EVS_EventType_ERROR, + "Reload Application %s Failed, RC = 0x%08X", LocalApp, (unsigned int)Result); + } + } + else + { + CFE_ES_Global.TaskData.CommandErrorCounter++; + CFE_EVS_SendEvent(CFE_ES_RELOAD_APP_ERR2_EID, CFE_EVS_EventType_ERROR, + "Reload Application %s, GetAppIDByName failed. RC = 0x%08X.", LocalApp, (unsigned int)Result); + } + + return CFE_SUCCESS; +} /* End of CFE_ES_ReloadAppCmd() */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_QueryOneCmd() -- Request tlm packet with single app data */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int32 CFE_ES_QueryOneCmd(const CFE_ES_QueryOneCmd_t *data) +{ + const CFE_ES_AppNameCmd_Payload_t *cmd = &data->Payload; + char LocalApp[OS_MAX_API_NAME]; + union + { + CFE_ES_AppId_t AppId; + CFE_ES_LibId_t LibId; + CFE_ResourceId_t ResourceID; + } IdBuf; + int32 Result; + + CFE_SB_MessageStringGet(LocalApp, (char *)cmd->Application, NULL, sizeof(LocalApp), sizeof(cmd->Application)); + + Result = CFE_ES_GetAppIDByName(&IdBuf.AppId, LocalApp); + if (Result == CFE_ES_ERR_NAME_NOT_FOUND) + { + /* Also check for a matching library name */ + Result = CFE_ES_GetLibIDByName(&IdBuf.LibId, LocalApp); + } + + if (Result == CFE_SUCCESS) + { + Result = CFE_ES_GetModuleInfo(&(CFE_ES_Global.TaskData.OneAppPacket.Payload.AppInfo), IdBuf.ResourceID); + } + + /* + ** Send appropriate event message... + */ + if (Result == CFE_SUCCESS) + { + /* + ** Send application status telemetry packet. + */ + CFE_SB_TimeStampMsg(&CFE_ES_Global.TaskData.OneAppPacket.TlmHeader.Msg); + Result = CFE_SB_TransmitMsg(&CFE_ES_Global.TaskData.OneAppPacket.TlmHeader.Msg, true); + if (Result == CFE_SUCCESS) + { + CFE_ES_Global.TaskData.CommandCounter++; + CFE_EVS_SendEvent(CFE_ES_ONE_APP_EID, CFE_EVS_EventType_DEBUG, "Sent %s application data", LocalApp); + } + else + { + CFE_ES_Global.TaskData.CommandErrorCounter++; + CFE_EVS_SendEvent(CFE_ES_ONE_ERR_EID, CFE_EVS_EventType_ERROR, + "Failed to send %s application data, RC = 0x%08X", LocalApp, (unsigned int)Result); + } + } + else + { + CFE_ES_Global.TaskData.CommandErrorCounter++; + CFE_EVS_SendEvent(CFE_ES_ONE_APPID_ERR_EID, CFE_EVS_EventType_ERROR, + "Failed to send %s application data: GetAppIDByName Failed, RC = 0x%08X", LocalApp, + (unsigned int)Result); + } + + return CFE_SUCCESS; +} /* End of CFE_ES_QueryOneCmd() */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_QueryAllCmd() -- Write all app data to file */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int32 CFE_ES_QueryAllCmd(const CFE_ES_QueryAllCmd_t *data) +{ + CFE_FS_Header_t FileHeader; + osal_id_t FileDescriptor = OS_OBJECT_ID_UNDEFINED; + uint32 i; + uint32 EntryCount = 0; + uint32 FileSize = 0; + int32 Result; + CFE_ES_AppInfo_t AppInfo; + const CFE_ES_FileNameCmd_Payload_t *CmdPtr = &data->Payload; + char QueryAllFilename[OS_MAX_PATH_LEN]; + CFE_ResourceId_t ResourceList[CFE_ES_QUERY_ALL_MAX_ENTRIES]; + uint32 NumResources; + CFE_ES_AppRecord_t * AppRecPtr; + CFE_ES_LibRecord_t * LibRecPtr; + + /* + * Collect list of active resource IDs. + * + * This should be done while locked, but the actual writing + * of the AppInfo data should be done while NOT locked. + */ + CFE_ES_LockSharedData(__func__, __LINE__); + NumResources = 0; + AppRecPtr = CFE_ES_Global.AppTable; + for (i = 0; i < CFE_PLATFORM_ES_MAX_APPLICATIONS && NumResources < CFE_ES_QUERY_ALL_MAX_ENTRIES; ++i) + { + if (CFE_ES_AppRecordIsUsed(AppRecPtr)) + { + ResourceList[NumResources] = CFE_RESOURCEID_UNWRAP(CFE_ES_AppRecordGetID(AppRecPtr)); + ++NumResources; + } + ++AppRecPtr; + } + LibRecPtr = CFE_ES_Global.LibTable; + for (i = 0; i < CFE_PLATFORM_ES_MAX_LIBRARIES && NumResources < CFE_ES_QUERY_ALL_MAX_ENTRIES; ++i) + { + if (CFE_ES_LibRecordIsUsed(LibRecPtr)) + { + ResourceList[NumResources] = CFE_RESOURCEID_UNWRAP(CFE_ES_LibRecordGetID(LibRecPtr)); + ++NumResources; + } + ++LibRecPtr; + } + CFE_ES_UnlockSharedData(__func__, __LINE__); + + /* Copy the commanded filename, using default if unspecified */ + Result = CFE_FS_ParseInputFileNameEx(QueryAllFilename, CmdPtr->FileName, sizeof(QueryAllFilename), + sizeof(CmdPtr->FileName), CFE_PLATFORM_ES_DEFAULT_APP_LOG_FILE, + CFE_FS_GetDefaultMountPoint(CFE_FS_FileCategory_BINARY_DATA_DUMP), + CFE_FS_GetDefaultExtension(CFE_FS_FileCategory_BINARY_DATA_DUMP)); + + if (Result == CFE_SUCCESS) + { + /* + ** Check to see if the file already exists + */ + Result = OS_OpenCreate(&FileDescriptor, QueryAllFilename, OS_FILE_FLAG_NONE, OS_READ_ONLY); + if (Result >= 0) + { + OS_close(FileDescriptor); + OS_remove(QueryAllFilename); + } + + /* + ** Create ES task log data file + */ + Result = OS_OpenCreate(&FileDescriptor, QueryAllFilename, OS_FILE_FLAG_CREATE | OS_FILE_FLAG_TRUNCATE, + OS_WRITE_ONLY); + } + + if (Result >= 0) + { + /* + ** Initialize cFE file header + */ + CFE_FS_InitHeader(&FileHeader, CFE_ES_APP_LOG_DESC, CFE_FS_SubType_ES_QUERYALL); + + /* + ** Output the Standard cFE File Header to the App File + */ + Result = CFE_FS_WriteHeader(FileDescriptor, &FileHeader); + + if (Result != sizeof(CFE_FS_Header_t)) + { + OS_close(FileDescriptor); + CFE_ES_Global.TaskData.CommandErrorCounter++; + CFE_EVS_SendEvent(CFE_ES_WRHDR_ERR_EID, CFE_EVS_EventType_ERROR, + "Failed to write App Info file, WriteHdr RC = 0x%08X, exp %d", (unsigned int)Result, + (int)sizeof(CFE_FS_Header_t)); + /* + * returning "success" here as there is no other recourse; + * the full extent of the error recovery has been done + */ + return CFE_SUCCESS; + } /* end if */ + + /* + ** Maintain statistics of amount of data written to file + */ + FileSize += Result; + + /* + ** Loop through the ES AppTable for main applications + */ + for (i = 0; i < NumResources; ++i) + { + /* + ** Populate the AppInfo entry + */ + Result = CFE_ES_GetModuleInfo(&AppInfo, ResourceList[i]); + if (Result == CFE_SUCCESS) + { + /* + ** Write the local entry to file + */ + Result = OS_write(FileDescriptor, &AppInfo, sizeof(CFE_ES_AppInfo_t)); + if (Result != sizeof(CFE_ES_AppInfo_t)) + { + OS_close(FileDescriptor); + CFE_ES_Global.TaskData.CommandErrorCounter++; + CFE_EVS_SendEvent(CFE_ES_TASKWR_ERR_EID, CFE_EVS_EventType_ERROR, + "Failed to write App Info file, Task write RC = 0x%08X, exp %d", + (unsigned int)Result, (int)sizeof(CFE_ES_AppInfo_t)); + /* + * returning "success" here as there is no other recourse; + * the full extent of the error recovery has been done + */ + return CFE_SUCCESS; + } /* end if */ + + FileSize += Result; + EntryCount++; + } + + } /* end for */ + + OS_close(FileDescriptor); + CFE_ES_Global.TaskData.CommandCounter++; + CFE_EVS_SendEvent(CFE_ES_ALL_APPS_EID, CFE_EVS_EventType_DEBUG, + "App Info file written to %s, Entries=%d, FileSize=%d", QueryAllFilename, (int)EntryCount, + (int)FileSize); + } + else + { + CFE_ES_Global.TaskData.CommandErrorCounter++; + CFE_EVS_SendEvent(CFE_ES_OSCREATE_ERR_EID, CFE_EVS_EventType_ERROR, + "Failed to write App Info file, OS_OpenCreate RC = 0x%08X", (unsigned int)Result); + } + + return CFE_SUCCESS; +} /* End of CFE_ES_QueryAllCmd() */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_QueryAllTasksCmd() -- Write all Task Data to a file */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int32 CFE_ES_QueryAllTasksCmd(const CFE_ES_QueryAllTasksCmd_t *data) +{ + CFE_FS_Header_t FileHeader; + osal_id_t FileDescriptor = OS_OBJECT_ID_UNDEFINED; + uint32 i; + uint32 EntryCount = 0; + uint32 FileSize = 0; + int32 Result; + CFE_ES_TaskInfo_t TaskInfo; + const CFE_ES_FileNameCmd_Payload_t *CmdPtr = &data->Payload; + char QueryAllFilename[OS_MAX_PATH_LEN]; + CFE_ES_TaskId_t TaskList[OS_MAX_TASKS]; + uint32 NumTasks; + CFE_ES_TaskRecord_t * TaskRecPtr; + + /* + * Collect list of active task IDs. + * + * This should be done while locked, but the actual writing + * of the AppInfo data should be done while NOT locked. + */ + CFE_ES_LockSharedData(__func__, __LINE__); + NumTasks = 0; + TaskRecPtr = CFE_ES_Global.TaskTable; + for (i = 0; i < OS_MAX_TASKS; ++i) + { + if (CFE_ES_TaskRecordIsUsed(TaskRecPtr)) + { + TaskList[NumTasks] = CFE_ES_TaskRecordGetID(TaskRecPtr); + ++NumTasks; + } + ++TaskRecPtr; + } + CFE_ES_UnlockSharedData(__func__, __LINE__); + + /* + ** Copy the commanded filename into local buffer to ensure size limitation and to allow for modification + */ + Result = CFE_FS_ParseInputFileNameEx(QueryAllFilename, CmdPtr->FileName, sizeof(QueryAllFilename), + sizeof(CmdPtr->FileName), CFE_PLATFORM_ES_DEFAULT_TASK_LOG_FILE, + CFE_FS_GetDefaultMountPoint(CFE_FS_FileCategory_BINARY_DATA_DUMP), + CFE_FS_GetDefaultExtension(CFE_FS_FileCategory_BINARY_DATA_DUMP)); + + if (Result == CFE_SUCCESS) + { + /* + ** Check to see if the file already exists + */ + Result = OS_OpenCreate(&FileDescriptor, QueryAllFilename, OS_FILE_FLAG_NONE, OS_READ_ONLY); + if (Result >= 0) + { + OS_close(FileDescriptor); + OS_remove(QueryAllFilename); + } + + /* + ** Create ES task log data file + */ + Result = OS_OpenCreate(&FileDescriptor, QueryAllFilename, OS_FILE_FLAG_CREATE | OS_FILE_FLAG_TRUNCATE, + OS_WRITE_ONLY); + } + + if (Result >= 0) + { + /* + ** Initialize cFE file header + */ + CFE_FS_InitHeader(&FileHeader, CFE_ES_TASK_LOG_DESC, CFE_FS_SubType_ES_QUERYALLTASKS); + + /* + ** Output the Standard cFE File Header to the App File + */ + Result = CFE_FS_WriteHeader(FileDescriptor, &FileHeader); + + if (Result != sizeof(CFE_FS_Header_t)) + { + OS_close(FileDescriptor); + CFE_ES_Global.TaskData.CommandErrorCounter++; + CFE_EVS_SendEvent(CFE_ES_TASKINFO_WRHDR_ERR_EID, CFE_EVS_EventType_ERROR, + "Failed to write Task Info file, WriteHdr RC = 0x%08X, exp %d", (unsigned int)Result, + (int)sizeof(CFE_FS_Header_t)); + /* + * returning "success" here as there is no other recourse; + * the full extent of the error recovery has been done + */ + return CFE_SUCCESS; + } /* end if */ + + /* + ** Maintain statistics of amount of data written to file + */ + FileSize += Result; + + /* + ** Loop through the ES AppTable for main applications + */ + for (i = 0; i < NumTasks; ++i) + { + /* + ** Populate the AppInfo entry + */ + Result = CFE_ES_GetTaskInfo(&TaskInfo, TaskList[i]); + if (Result == CFE_SUCCESS) + { + /* + ** Write the local entry to file + */ + Result = OS_write(FileDescriptor, &TaskInfo, sizeof(CFE_ES_TaskInfo_t)); + if (Result != sizeof(CFE_ES_TaskInfo_t)) + { + OS_close(FileDescriptor); + CFE_ES_Global.TaskData.CommandErrorCounter++; + CFE_EVS_SendEvent(CFE_ES_TASKINFO_WR_ERR_EID, CFE_EVS_EventType_ERROR, + "Failed to write Task Info file, Task write RC = 0x%08X, exp %d", + (unsigned int)Result, (int)sizeof(CFE_ES_TaskInfo_t)); + /* + * returning "success" here as there is no other recourse; + * the full extent of the error recovery has been done + */ + return CFE_SUCCESS; + } /* end if */ + + FileSize += Result; + EntryCount++; + } + + } /* end for */ + + OS_close(FileDescriptor); + CFE_ES_Global.TaskData.CommandCounter++; + CFE_EVS_SendEvent(CFE_ES_TASKINFO_EID, CFE_EVS_EventType_DEBUG, + "Task Info file written to %s, Entries=%d, FileSize=%d", QueryAllFilename, (int)EntryCount, + (int)FileSize); + } + else + { + CFE_ES_Global.TaskData.CommandErrorCounter++; + CFE_EVS_SendEvent(CFE_ES_TASKINFO_OSCREATE_ERR_EID, CFE_EVS_EventType_ERROR, + "Failed to write Task Info file, OS_OpenCreate RC = 0x%08X", (unsigned int)Result); + } + + return CFE_SUCCESS; +} /* End of CFE_ES_QueryAllTasksCmd() */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_ClearSysLogCmd() -- Clear executive services system log */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int32 CFE_ES_ClearSysLogCmd(const CFE_ES_ClearSysLogCmd_t *data) +{ + /* + ** Clear syslog index and memory area + */ + + CFE_ES_LockSharedData(__func__, __LINE__); + CFE_ES_SysLogClear_Unsync(); + CFE_ES_UnlockSharedData(__func__, __LINE__); + + /* + ** This command will always succeed... + */ + CFE_ES_Global.TaskData.CommandCounter++; + CFE_EVS_SendEvent(CFE_ES_SYSLOG1_INF_EID, CFE_EVS_EventType_INFORMATION, "Cleared Executive Services log data"); + + return CFE_SUCCESS; +} /* End of CFE_ES_ClearSysLogCmd() */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_OverWriteSysLogCmd() -- set syslog mode */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int32 CFE_ES_OverWriteSysLogCmd(const CFE_ES_OverWriteSysLogCmd_t *data) +{ + int32 Status; + const CFE_ES_OverWriteSysLogCmd_Payload_t *CmdPtr = &data->Payload; + + Status = CFE_ES_SysLogSetMode(CmdPtr->Mode); + + if (Status != CFE_SUCCESS) + { + CFE_EVS_SendEvent(CFE_ES_ERR_SYSLOGMODE_EID, CFE_EVS_EventType_ERROR, + "Set OverWriteSysLog Command: Invalid Mode setting = %d", (int)CmdPtr->Mode); + + CFE_ES_Global.TaskData.CommandErrorCounter++; + } + else + { + CFE_EVS_SendEvent(CFE_ES_SYSLOGMODE_EID, CFE_EVS_EventType_DEBUG, + "Set OverWriteSysLog Command Received with Mode setting = %d", (int)CmdPtr->Mode); + + CFE_ES_Global.TaskData.CommandCounter++; + } + + return CFE_SUCCESS; +} /* End CFE_ES_OverWriteSysLogCmd() */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_WriteSysLogCmd() -- Process Cmd to write ES System Log to file */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int32 CFE_ES_WriteSysLogCmd(const CFE_ES_WriteSysLogCmd_t *data) +{ + const CFE_ES_FileNameCmd_Payload_t *CmdPtr = &data->Payload; + int32 Stat; + char LogFilename[OS_MAX_PATH_LEN]; + + /* + ** Copy the filename into local buffer with default name/path/extension if not specified + ** + ** Note even though this fundamentally contains strings, it is written as a binary file with an FS header, + ** not as normal text file, so still using the BINARY DATA DUMP category for its default extension. + */ + Stat = CFE_FS_ParseInputFileNameEx(LogFilename, CmdPtr->FileName, sizeof(LogFilename), sizeof(CmdPtr->FileName), + CFE_PLATFORM_ES_DEFAULT_SYSLOG_FILE, + CFE_FS_GetDefaultMountPoint(CFE_FS_FileCategory_BINARY_DATA_DUMP), + CFE_FS_GetDefaultExtension(CFE_FS_FileCategory_BINARY_DATA_DUMP)); + + if (Stat != CFE_SUCCESS) + { + CFE_EVS_SendEvent(CFE_ES_SYSLOG2_ERR_EID, CFE_EVS_EventType_ERROR, "Error parsing file name RC = 0x%08X", + (unsigned int)Stat); + } + else + { + Stat = CFE_ES_SysLogDump(LogFilename); + } + + if (Stat == CFE_SUCCESS) + { + CFE_ES_Global.TaskData.CommandCounter++; + } + else + { + CFE_ES_Global.TaskData.CommandErrorCounter++; + } /* end if */ + + return CFE_SUCCESS; +} /* end CFE_ES_WriteSysLogCmd */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_ClearERLogCmd() -- Clear The exception and reset log. */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int32 CFE_ES_ClearERLogCmd(const CFE_ES_ClearERLogCmd_t *data) +{ + /* + ** Clear ER log data buffer + */ + + memset(CFE_ES_Global.ResetDataPtr->ERLog, 0, sizeof(CFE_ES_Global.ResetDataPtr->ERLog)); + + /* + ** Reset ER log buffer index + */ + + CFE_ES_Global.ResetDataPtr->ERLogIndex = 0; + + /* + ** Set Number of Entries in ER log buffer back to zero + */ + CFE_ES_Global.ResetDataPtr->ERLogEntries = 0; + + /* + ** This command will always succeed + */ + CFE_ES_Global.TaskData.CommandCounter++; + CFE_EVS_SendEvent(CFE_ES_ERLOG1_INF_EID, CFE_EVS_EventType_INFORMATION, "Cleared ES Exception and Reset Log data"); + + return CFE_SUCCESS; +} /* End of CFE_ES_ClearERLogCmd() */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_WriteERLogCmd() -- Process Cmd to write exception & reset*/ +/* log to a file. */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int32 CFE_ES_WriteERLogCmd(const CFE_ES_WriteERLogCmd_t *data) +{ + const CFE_ES_FileNameCmd_Payload_t *CmdPtr = &data->Payload; + CFE_ES_BackgroundLogDumpGlobal_t * StatePtr; + int32 Status; + + StatePtr = &CFE_ES_Global.BackgroundERLogDumpState; + + /* check if pending before overwriting fields in the structure */ + if (CFE_FS_BackgroundFileDumpIsPending(&StatePtr->FileWrite)) + { + Status = CFE_STATUS_REQUEST_ALREADY_PENDING; + } + else + { + /* Reset the entire state object (just for good measure, ensure no stale data) */ + memset(StatePtr, 0, sizeof(*StatePtr)); + + /* + * Fill out the remainder of meta data. + * This data is currently the same for every request + */ + StatePtr->FileWrite.FileSubType = CFE_FS_SubType_ES_ERLOG; + snprintf(StatePtr->FileWrite.Description, sizeof(StatePtr->FileWrite.Description), CFE_ES_ER_LOG_DESC); + + StatePtr->FileWrite.GetData = CFE_ES_BackgroundERLogFileDataGetter; + StatePtr->FileWrite.OnEvent = CFE_ES_BackgroundERLogFileEventHandler; + + /* + ** Copy the filename into local buffer with default name/path/extension if not specified + */ + Status = CFE_FS_ParseInputFileNameEx(StatePtr->FileWrite.FileName, CmdPtr->FileName, + sizeof(StatePtr->FileWrite.FileName), sizeof(CmdPtr->FileName), + CFE_PLATFORM_ES_DEFAULT_ER_LOG_FILE, + CFE_FS_GetDefaultMountPoint(CFE_FS_FileCategory_BINARY_DATA_DUMP), + CFE_FS_GetDefaultExtension(CFE_FS_FileCategory_BINARY_DATA_DUMP)); + + if (Status == CFE_SUCCESS) + { + Status = CFE_FS_BackgroundFileDumpRequest(&StatePtr->FileWrite); + } + } + + if (Status != CFE_SUCCESS) + { + if (Status == CFE_STATUS_REQUEST_ALREADY_PENDING) + { + /* Specific event if already pending */ + CFE_EVS_SendEvent(CFE_ES_ERLOG_PENDING_ERR_EID, CFE_EVS_EventType_ERROR, + "Error log write already in progress"); + } + else + { + /* Some other validation issue e.g. bad file name */ + CFE_EVS_SendEvent(CFE_ES_ERLOG2_ERR_EID, CFE_EVS_EventType_ERROR, "Error creating file, RC = %d", + (int)Status); + } + + /* background dump did not start, consider this an error */ + CFE_ES_Global.TaskData.CommandErrorCounter++; + } + else + { + CFE_ES_Global.TaskData.CommandCounter++; + } + + return CFE_SUCCESS; +} /* end CFE_ES_WriteERLogCmd */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_VerifyCmdLength() -- Verify command packet length */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +bool CFE_ES_VerifyCmdLength(CFE_MSG_Message_t *MsgPtr, size_t ExpectedLength) +{ + bool result = true; + CFE_MSG_Size_t ActualLength = 0; + CFE_MSG_FcnCode_t FcnCode = 0; + CFE_SB_MsgId_t MsgId = CFE_SB_INVALID_MSG_ID; + + CFE_MSG_GetSize(MsgPtr, &ActualLength); + + /* + ** Verify the command packet length + */ + if (ExpectedLength != ActualLength) + { + CFE_MSG_GetMsgId(MsgPtr, &MsgId); + CFE_MSG_GetFcnCode(MsgPtr, &FcnCode); + + CFE_EVS_SendEvent(CFE_ES_LEN_ERR_EID, CFE_EVS_EventType_ERROR, + "Invalid msg length: ID = 0x%X, CC = %u, Len = %u, Expected = %u", + (unsigned int)CFE_SB_MsgIdToValue(MsgId), (unsigned int)FcnCode, (unsigned int)ActualLength, + (unsigned int)ExpectedLength); + result = false; + CFE_ES_Global.TaskData.CommandErrorCounter++; + } + + return (result); + +} /* End of CFE_ES_VerifyCmdLength() */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_ResetPRCountCmd() -- ES task ground command */ +/* (Processor Reset Count) */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int32 CFE_ES_ResetPRCountCmd(const CFE_ES_ResetPRCountCmd_t *data) +{ + /* + ** Reset the processor reset count + */ + CFE_ES_Global.ResetDataPtr->ResetVars.ProcessorResetCount = 0; + + /* + ** This command will always succeed. + */ + CFE_EVS_SendEvent(CFE_ES_RESET_PR_COUNT_EID, CFE_EVS_EventType_INFORMATION, "Set Processor Reset Count to Zero"); + + CFE_ES_Global.TaskData.CommandCounter++; + + return CFE_SUCCESS; +} /* End of CFE_ES_ResetPRCountCmd() */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_SetMaxPRCountCmd() -- Set Maximum Processor reset count */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int32 CFE_ES_SetMaxPRCountCmd(const CFE_ES_SetMaxPRCountCmd_t *data) +{ + const CFE_ES_SetMaxPRCountCmd_Payload_t *cmd = &data->Payload; + + /* + ** Set the MAX Processor reset count + */ + CFE_ES_Global.ResetDataPtr->ResetVars.MaxProcessorResetCount = cmd->MaxPRCount; + + /* + ** This command will always succeed. + */ + CFE_EVS_SendEvent(CFE_ES_SET_MAX_PR_COUNT_EID, CFE_EVS_EventType_INFORMATION, + "Maximum Processor Reset Count set to: %d", (int)cmd->MaxPRCount); + + CFE_ES_Global.TaskData.CommandCounter++; + + return CFE_SUCCESS; +} /* End of CFE_ES_RestartCmd() */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_DeleteCDSCmd() -- Delete Specified Critical Data Store */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int32 CFE_ES_DeleteCDSCmd(const CFE_ES_DeleteCDSCmd_t *data) +{ + int32 Status; + const CFE_ES_DeleteCDSCmd_Payload_t *cmd = &data->Payload; + char LocalCdsName[CFE_MISSION_ES_CDS_MAX_FULL_NAME_LEN]; + + CFE_SB_MessageStringGet(LocalCdsName, (char *)cmd->CdsName, NULL, sizeof(LocalCdsName), sizeof(cmd->CdsName)); + + Status = CFE_ES_DeleteCDS(LocalCdsName, false); + + if (Status == CFE_ES_CDS_WRONG_TYPE_ERR) + { + CFE_EVS_SendEvent(CFE_ES_CDS_DELETE_TBL_ERR_EID, CFE_EVS_EventType_ERROR, + "CDS '%s' is a Critical Table CDS. Must be deleted via TBL Command", LocalCdsName); + + CFE_ES_Global.TaskData.CommandErrorCounter++; + } + else if (Status == CFE_ES_CDS_OWNER_ACTIVE_ERR) + { + CFE_EVS_SendEvent(CFE_ES_CDS_OWNER_ACTIVE_EID, CFE_EVS_EventType_ERROR, + "CDS '%s' not deleted because owning app is active", LocalCdsName); + + CFE_ES_Global.TaskData.CommandErrorCounter++; + } + else if (Status == CFE_ES_ERR_NAME_NOT_FOUND) + { + CFE_EVS_SendEvent(CFE_ES_CDS_NAME_ERR_EID, CFE_EVS_EventType_ERROR, "Unable to locate '%s' in CDS Registry", + LocalCdsName); + + CFE_ES_Global.TaskData.CommandErrorCounter++; + } + else if (Status != CFE_SUCCESS) + { + CFE_EVS_SendEvent(CFE_ES_CDS_DELETE_ERR_EID, CFE_EVS_EventType_ERROR, + "Error while deleting '%s' from CDS, See SysLog.(Err=0x%08X)", LocalCdsName, + (unsigned int)Status); + + CFE_ES_Global.TaskData.CommandErrorCounter++; + } + else + { + CFE_EVS_SendEvent(CFE_ES_CDS_DELETED_INFO_EID, CFE_EVS_EventType_INFORMATION, + "Successfully removed '%s' from CDS", LocalCdsName); + + CFE_ES_Global.TaskData.CommandCounter++; + } + + return CFE_SUCCESS; +} /* End of CFE_ES_DeleteCDSCmd() */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_SendMemPoolStatsCmd() -- Telemeter Memory Pool Statistics */ +/* */ +/* Note: The "Application" parameter of the */ +/* CFE_ES_TlmPoolStats_t structure is not used. */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int32 CFE_ES_SendMemPoolStatsCmd(const CFE_ES_SendMemPoolStatsCmd_t *data) +{ + const CFE_ES_SendMemPoolStatsCmd_Payload_t *Cmd; + CFE_ES_MemHandle_t MemHandle; + bool ValidHandle; + + Cmd = &data->Payload; + + /* Verify the handle to make sure it is legit */ + MemHandle = Cmd->PoolHandle; + ValidHandle = CFE_ES_ValidateHandle(MemHandle); + + if (ValidHandle) + { + /* Extract the memory statistics from the memory pool */ + CFE_ES_GetMemPoolStats(&CFE_ES_Global.TaskData.MemStatsPacket.Payload.PoolStats, MemHandle); + + /* Echo the specified pool handle in the telemetry packet */ + CFE_ES_Global.TaskData.MemStatsPacket.Payload.PoolHandle = MemHandle; + + /* + ** Send memory statistics telemetry packet. + */ + CFE_SB_TimeStampMsg(&CFE_ES_Global.TaskData.MemStatsPacket.TlmHeader.Msg); + CFE_SB_TransmitMsg(&CFE_ES_Global.TaskData.MemStatsPacket.TlmHeader.Msg, true); + + CFE_ES_Global.TaskData.CommandCounter++; + CFE_EVS_SendEvent(CFE_ES_TLM_POOL_STATS_INFO_EID, CFE_EVS_EventType_DEBUG, + "Successfully telemetered memory pool stats for 0x%08lX", + CFE_RESOURCEID_TO_ULONG(Cmd->PoolHandle)); + } + else + { + CFE_ES_Global.TaskData.CommandErrorCounter++; + CFE_EVS_SendEvent(CFE_ES_INVALID_POOL_HANDLE_ERR_EID, CFE_EVS_EventType_ERROR, + "Cannot telemeter memory pool stats. Illegal Handle (0x%08lX)", + CFE_RESOURCEID_TO_ULONG(Cmd->PoolHandle)); + } + + return CFE_SUCCESS; +} /* End of CFE_ES_SendMemPoolStatsCmd() */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_DumpCDSRegistryCmd() -- Dump CDS Registry to a file */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int32 CFE_ES_DumpCDSRegistryCmd(const CFE_ES_DumpCDSRegistryCmd_t *data) +{ + CFE_FS_Header_t StdFileHeader; + osal_id_t FileDescriptor = OS_OBJECT_ID_UNDEFINED; + int32 Status; + int16 RegIndex = 0; + const CFE_ES_DumpCDSRegistryCmd_Payload_t *CmdPtr = &data->Payload; + char DumpFilename[OS_MAX_PATH_LEN]; + CFE_ES_CDS_RegRec_t * RegRecPtr; + CFE_ES_CDSRegDumpRec_t DumpRecord; + int32 FileSize = 0; + int32 NumEntries = 0; + + /* + ** Copy the filename into local buffer with default name/path/extension if not specified + */ + Status = CFE_FS_ParseInputFileNameEx(DumpFilename, CmdPtr->DumpFilename, sizeof(DumpFilename), + sizeof(CmdPtr->DumpFilename), CFE_PLATFORM_ES_DEFAULT_CDS_REG_DUMP_FILE, + CFE_FS_GetDefaultMountPoint(CFE_FS_FileCategory_BINARY_DATA_DUMP), + CFE_FS_GetDefaultExtension(CFE_FS_FileCategory_BINARY_DATA_DUMP)); + + if (Status != OS_SUCCESS) + { + CFE_EVS_SendEvent(CFE_ES_CREATING_CDS_DUMP_ERR_EID, CFE_EVS_EventType_ERROR, + "Error parsing CDS dump filename, Status=0x%08X", (unsigned int)Status); + } + else + { + /* Create a new dump file, overwriting anything that may have existed previously */ + Status = + OS_OpenCreate(&FileDescriptor, DumpFilename, OS_FILE_FLAG_CREATE | OS_FILE_FLAG_TRUNCATE, OS_WRITE_ONLY); + + if (Status != OS_SUCCESS) + { + CFE_EVS_SendEvent(CFE_ES_CREATING_CDS_DUMP_ERR_EID, CFE_EVS_EventType_ERROR, + "Error creating CDS dump file '%s', Status=0x%08X", DumpFilename, (unsigned int)Status); + } + } + + if (Status == OS_SUCCESS) + { + /* Initialize the standard cFE File Header for the Dump File */ + CFE_FS_InitHeader(&StdFileHeader, "CDS_Registry", CFE_FS_SubType_ES_CDS_REG); + + /* Output the Standard cFE File Header to the Dump File */ + Status = CFE_FS_WriteHeader(FileDescriptor, &StdFileHeader); + + /* Maintain statistics of amount of data written to file */ + FileSize += Status; + + if (Status == sizeof(CFE_FS_Header_t)) + { + Status = sizeof(CFE_ES_CDSRegDumpRec_t); + RegRecPtr = CFE_ES_Global.CDSVars.Registry; + while ((RegIndex < CFE_PLATFORM_ES_CDS_MAX_NUM_ENTRIES) && (Status == sizeof(CFE_ES_CDSRegDumpRec_t))) + { + /* Check to see if the Registry entry is empty */ + if (CFE_ES_CDSBlockRecordIsUsed(RegRecPtr)) + { + /* Fill CDS Registry Dump Record with relevant information */ + memset(&DumpRecord, 0, sizeof(DumpRecord)); + DumpRecord.Size = CFE_ES_MEMOFFSET_C(CFE_ES_CDSBlockRecordGetUserSize(RegRecPtr)); + DumpRecord.Handle = CFE_ES_CDSBlockRecordGetID(RegRecPtr); + DumpRecord.Table = RegRecPtr->Table; + strncpy(DumpRecord.Name, RegRecPtr->Name, sizeof(DumpRecord.Name) - 1); + + /* Output Registry Dump Record to Registry Dump File */ + Status = OS_write(FileDescriptor, &DumpRecord, sizeof(CFE_ES_CDSRegDumpRec_t)); + + FileSize += Status; + NumEntries++; + } + + /* Look at the next entry in the Registry */ + ++RegIndex; + ++RegRecPtr; + } + + if (Status == sizeof(CFE_ES_CDSRegDumpRec_t)) + { + CFE_EVS_SendEvent(CFE_ES_CDS_REG_DUMP_INF_EID, CFE_EVS_EventType_DEBUG, + "Successfully dumped CDS Registry to '%s':Size=%d,Entries=%d", DumpFilename, + (int)FileSize, (int)NumEntries); + + /* Increment Successful Command Counter */ + CFE_ES_Global.TaskData.CommandCounter++; + } + else + { + CFE_EVS_SendEvent(CFE_ES_CDS_DUMP_ERR_EID, CFE_EVS_EventType_ERROR, + "Error writing CDS Registry to '%s', Status=0x%08X", DumpFilename, + (unsigned int)Status); + + /* Increment Command Error Counter */ + CFE_ES_Global.TaskData.CommandErrorCounter++; + } + } + else + { + CFE_EVS_SendEvent(CFE_ES_WRITE_CFE_HDR_ERR_EID, CFE_EVS_EventType_ERROR, + "Error writing cFE File Header to '%s', Status=0x%08X", DumpFilename, + (unsigned int)Status); + + /* Increment Command Error Counter */ + CFE_ES_Global.TaskData.CommandErrorCounter++; + } + + /* We are done outputting data to the dump file. Close it. */ + OS_close(FileDescriptor); + } + else + { + /* Increment Command Error Counter */ + CFE_ES_Global.TaskData.CommandErrorCounter++; + } + + return CFE_SUCCESS; +} /* End of CFE_ES_DumpCDSRegistryCmd() */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* */ +/* CFE_ES_FileWriteByteCntErr() -- Send event to inform ground that*/ +/* a byte count discrepancy has been*/ +/* detected during the file write */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +void CFE_ES_FileWriteByteCntErr(const char *Filename, size_t Requested, int32 Status) +{ + + CFE_EVS_SendEvent(CFE_ES_FILEWRITE_ERR_EID, CFE_EVS_EventType_ERROR, + "File write,byte cnt err,file %s,request=%u,status=0x%08x", Filename, (unsigned int)Requested, + (unsigned int)Status); + +} /* End of CFE_ES_FileWriteByteCntErr() */ + +/************************/ +/* End of File Comment */ +/************************/ diff --git a/modules/es/fsw/src/cfe_es_task.h b/modules/es/fsw/src/cfe_es_task.h new file mode 100644 index 000000000..eaf8d7fbf --- /dev/null +++ b/modules/es/fsw/src/cfe_es_task.h @@ -0,0 +1,129 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/** + * @file + * + * Purpose: + * cFE Executive Services (ES) task header file + * + * References: + * Flight Software Branch C Coding Standard Version 1.0a + * cFE Flight Software Application Developers Guide + * + * Notes: + * + */ + +#ifndef CFE_ES_TASK_H +#define CFE_ES_TASK_H + +/* +** Includes +*/ +#include "cfe_es_msg.h" + +#include "cfe_es_api_typedefs.h" +#include "cfe_fs_api_typedefs.h" +#include "cfe_sb_api_typedefs.h" +#include "cfe_es_erlog_typedef.h" + +/*************************************************************************/ + +#define CFE_ES_PIPE_NAME "ES_CMD_PIPE" +#define CFE_ES_PIPE_DEPTH 12 +#define CFE_ES_LIMIT_HK 2 +#define CFE_ES_LIMIT_CMD 4 + +/* +** ES File descriptions +*/ +#define CFE_ES_SYS_LOG_DESC "ES system log data file" +#define CFE_ES_TASK_LOG_DESC "ES Task Info file" +#define CFE_ES_APP_LOG_DESC "ES Application Info file" +#define CFE_ES_ER_LOG_DESC "ES ERlog data file" +#define CFE_ES_PERF_LOG_DESC "ES Performance data file" + +/* + * Limit for the total number of entries that may be + * produced by a "query all" type command. + */ +#define CFE_ES_QUERY_ALL_MAX_ENTRIES (CFE_PLATFORM_ES_MAX_APPLICATIONS + CFE_PLATFORM_ES_MAX_LIBRARIES) + +/*************************************************************************/ +/* +** Type definitions +*/ + +/*************************************************************************/ + +/* +** ES Task function prototypes +*/ +void CFE_ES_TaskMain(void); +int32 CFE_ES_TaskInit(void); +void CFE_ES_TaskPipe(CFE_SB_Buffer_t *SBBufPtr); + +/* + * Functions related to the ES background helper task for low-priority tasks + */ +int32 CFE_ES_BackgroundInit(void); +void CFE_ES_BackgroundTask(void); +void CFE_ES_BackgroundCleanup(void); + +/* +** ES Task message dispatch functions +*/ +int32 CFE_ES_HousekeepingCmd(const CFE_MSG_CommandHeader_t *data); +int32 CFE_ES_NoopCmd(const CFE_ES_NoopCmd_t *Cmd); +int32 CFE_ES_ResetCountersCmd(const CFE_ES_ResetCountersCmd_t *data); +int32 CFE_ES_RestartCmd(const CFE_ES_RestartCmd_t *data); +int32 CFE_ES_StartAppCmd(const CFE_ES_StartAppCmd_t *data); +int32 CFE_ES_StopAppCmd(const CFE_ES_StopAppCmd_t *data); +int32 CFE_ES_RestartAppCmd(const CFE_ES_RestartAppCmd_t *data); +int32 CFE_ES_ReloadAppCmd(const CFE_ES_ReloadAppCmd_t *data); +int32 CFE_ES_QueryOneCmd(const CFE_ES_QueryOneCmd_t *data); +int32 CFE_ES_QueryAllCmd(const CFE_ES_QueryAllCmd_t *data); +int32 CFE_ES_QueryAllTasksCmd(const CFE_ES_QueryAllTasksCmd_t *data); +int32 CFE_ES_ClearSysLogCmd(const CFE_ES_ClearSysLogCmd_t *data); +int32 CFE_ES_OverWriteSysLogCmd(const CFE_ES_OverWriteSysLogCmd_t *data); +int32 CFE_ES_WriteSysLogCmd(const CFE_ES_WriteSysLogCmd_t *data); +int32 CFE_ES_ClearERLogCmd(const CFE_ES_ClearERLogCmd_t *data); +int32 CFE_ES_WriteERLogCmd(const CFE_ES_WriteERLogCmd_t *data); +int32 CFE_ES_ResetPRCountCmd(const CFE_ES_ResetPRCountCmd_t *data); +int32 CFE_ES_SetMaxPRCountCmd(const CFE_ES_SetMaxPRCountCmd_t *data); +int32 CFE_ES_DeleteCDSCmd(const CFE_ES_DeleteCDSCmd_t *data); +int32 CFE_ES_StartPerfDataCmd(const CFE_ES_StartPerfDataCmd_t *data); +int32 CFE_ES_StopPerfDataCmd(const CFE_ES_StopPerfDataCmd_t *data); +int32 CFE_ES_SetPerfFilterMaskCmd(const CFE_ES_SetPerfFilterMaskCmd_t *data); +int32 CFE_ES_SetPerfTriggerMaskCmd(const CFE_ES_SetPerfTriggerMaskCmd_t *data); +int32 CFE_ES_SendMemPoolStatsCmd(const CFE_ES_SendMemPoolStatsCmd_t *data); +int32 CFE_ES_DumpCDSRegistryCmd(const CFE_ES_DumpCDSRegistryCmd_t *data); + +/* +** Message Handler Helper Functions +*/ +bool CFE_ES_ValidateHandle(CFE_ES_MemHandle_t Handle); +bool CFE_ES_VerifyCmdLength(CFE_MSG_Message_t *MsgPtr, size_t ExpectedLength); +void CFE_ES_FileWriteByteCntErr(const char *Filename, size_t Requested, int32 Status); + +/*************************************************************************/ + +#endif /* CFE_ES_TASK_H */ diff --git a/modules/es/fsw/src/cfe_es_verify.h b/modules/es/fsw/src/cfe_es_verify.h new file mode 100644 index 000000000..751eb5a0d --- /dev/null +++ b/modules/es/fsw/src/cfe_es_verify.h @@ -0,0 +1,337 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** 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. +*/ + +/** + * @file + * + * Purpose: + * This header file performs compile time checking for ES configuration + * parameters. + * + * References: + * Flight Software Branch C Coding Standard Version 1.0a + * cFE Flight Software Application Developers Guide + * + * Notes: + * The upper limits are somewhat arbitrary right now. + * + */ + +#ifndef CFE_ES_VERIFY_H +#define CFE_ES_VERIFY_H + +#include + +#if CFE_PLATFORM_ES_MAX_APPLICATIONS < 6 +#error CFE_PLATFORM_ES_MAX_APPLICATIONS cannot be less than 6! +#endif + +#if CFE_PLATFORM_ES_MAX_LIBRARIES < 1 +#error CFE_PLATFORM_ES_MAX_LIBRARIES cannot be less than 1! +#endif + +#if CFE_PLATFORM_ES_ER_LOG_ENTRIES < 1 +#error CFE_PLATFORM_ES_ER_LOG_ENTRIES cannot be less than 10! +#endif + +#if CFE_PLATFORM_ES_SYSTEM_LOG_SIZE < 512 +#error CFE_PLATFORM_ES_SYSTEM_LOG_SIZE cannot be less than 512 Bytes! +#endif + +#if CFE_PLATFORM_ES_DEFAULT_STACK_SIZE < 2048 +#error CFE_PLATFORM_ES_DEFAULT_STACK_SIZE cannot be less than 2048 Bytes! +#endif + +/* +** Number of entries in the ES Object table ( The table that controls core cFE startup ) +*/ +#if CFE_PLATFORM_ES_OBJECT_TABLE_SIZE < 15 +#error CFE_PLATFORM_ES_OBJECT_TABLE_SIZE cannot be less than 15! +#endif + +/* +** ES Application Control Scan Rate. +*/ +#if CFE_PLATFORM_ES_APP_SCAN_RATE < 100 +#error CFE_PLATFORM_ES_APP_SCAN_RATE cannot be less than 100 milliseconds! +#elif CFE_PLATFORM_ES_APP_SCAN_RATE > 20000 +#error CFE_PLATFORM_ES_APP_SCAN_RATE cannot be greater than 20 seconds! +#endif + +/* +** ES Application Kill Timeout +*/ +#if CFE_PLATFORM_ES_APP_KILL_TIMEOUT < 1 +#error CFE_PLATFORM_ES_APP_KILL_TIMEOUT cannot be less than 1! +#elif CFE_PLATFORM_ES_APP_KILL_TIMEOUT > 100 +#error CFE_PLATFORM_ES_APP_KILL_TIMEOUT cannot be greater than 100! +#endif + +/* +** ES / cFE RAM disk parameters +*/ +#if CFE_PLATFORM_ES_RAM_DISK_SECTOR_SIZE < 128 +#error CFE_PLATFORM_ES_RAM_DISK_SECTOR_SIZE cannot be less than 128! +#endif + +#if CFE_PLATFORM_ES_RAM_DISK_NUM_SECTORS < 128 +#error CFE_PLATFORM_ES_RAM_DISK_NUM_SECTORS cannot be less than 128! +#endif + +#if CFE_PLATFORM_ES_RAM_DISK_PERCENT_RESERVED < 0 +#error CFE_PLATFORM_ES_RAM_DISK_PERCENT_RESERVED cannot be less than 0! +#elif CFE_PLATFORM_ES_RAM_DISK_PERCENT_RESERVED > 75 +#error CFE_PLATFORM_ES_RAM_DISK_PERCENT_RESERVED cannot be greater than 75! +#endif + +/* +** Critical data store size +*/ +#if CFE_PLATFORM_ES_CDS_SIZE < (8 * 1024) +#error CFE_PLATFORM_ES_CDS_SIZE cannot be less than 8Kbytes! +#elif CFE_PLATFORM_ES_CDS_SIZE > UINT32_MAX +#error CFE_PLATFORM_ES_CDS_SIZE cannot be greater than UINT32_MAX (4 Gigabytes)! +#endif + +/* +** User Reserved Memory Size. +*/ +#if CFE_PLATFORM_ES_USER_RESERVED_SIZE < (1 * 1024) +#error CFE_PLATFORM_ES_USER_RESERVED_SIZE cannot be less than 1Kbytes! +#elif CFE_PLATFORM_ES_USER_RESERVED_SIZE > UINT32_MAX +#error CFE_PLATFORM_ES_USER_RESERVED_SIZE cannot be greater than UINT32_MAX (4 Gigabytes)! +#endif + +/* +** SysLog mode +*/ +#if CFE_PLATFORM_ES_DEFAULT_SYSLOG_MODE < 0 +#error CFE_PLATFORM_ES_DEFAULT_SYSLOG_MODE cannot be less than 0! +#elif CFE_PLATFORM_ES_DEFAULT_SYSLOG_MODE > 1 +#error CFE_PLATFORM_ES_DEFAULT_SYSLOG_MODE cannot be greater than 1! +#endif + +/* +** Maximum number of performance IDs +*/ +#if CFE_MISSION_ES_PERF_MAX_IDS < 32 +#error CFE_MISSION_ES_PERF_MAX_IDS cannot be less than 32! +#endif + +/* +** Performance data buffer size +*/ +#if CFE_PLATFORM_ES_PERF_DATA_BUFFER_SIZE < 1025 +#error CFE_PLATFORM_ES_PERF_DATA_BUFFER_SIZE cannot be less than 1025 entries! +#endif + +/* +** Maximum number of Registered CDS blocks +*/ +#if CFE_PLATFORM_ES_CDS_MAX_NUM_ENTRIES < 8 +#error CFE_PLATFORM_ES_CDS_MAX_NUM_ENTRIES cannot be less than 8! +#endif + +/* +** Maximum number of processor resets before a power-on +*/ +#if CFE_PLATFORM_ES_MAX_PROCESSOR_RESETS < 0 +#error CFE_PLATFORM_ES_MAX_PROCESSOR_RESETS cannot be less than 0! +#endif + +/* +** Alignment of ES memory pool +*/ +#if CFE_PLATFORM_ES_MEMPOOL_ALIGN_SIZE_MIN <= 0 +#error CFE_PLATFORM_ES_MEMPOOL_ALIGN_SIZE_MIN cannot be 0! +#elif (CFE_PLATFORM_ES_MEMPOOL_ALIGN_SIZE_MIN & (CFE_PLATFORM_ES_MEMPOOL_ALIGN_SIZE_MIN - 1)) != 0 +#error CFE_PLATFORM_ES_MEMPOOL_ALIGN_SIZE_MIN must be a power of 2! +#endif + +/* +** Intermediate ES Memory Pool Block Sizes +*/ +#if CFE_PLATFORM_ES_MAX_BLOCK_SIZE < CFE_MISSION_SB_MAX_SB_MSG_SIZE +#error CFE_PLATFORM_ES_MAX_BLOCK_SIZE must be larger than CFE_MISSION_SB_MAX_SB_MSG_SIZE! +#endif + +#if CFE_PLATFORM_ES_MAX_BLOCK_SIZE < CFE_PLATFORM_TBL_MAX_SNGL_TABLE_SIZE +#error CFE_PLATFORM_ES_MAX_BLOCK_SIZE must be larger than CFE_PLATFORM_TBL_MAX_SNGL_TABLE_SIZE! +#endif + +#if CFE_PLATFORM_ES_MAX_BLOCK_SIZE < CFE_PLATFORM_TBL_MAX_DBL_TABLE_SIZE +#error CFE_PLATFORM_ES_MAX_BLOCK_SIZE must be larger than CFE_PLATFORM_TBL_MAX_DBL_TABLE_SIZE! +#endif + +#if CFE_PLATFORM_ES_MEM_BLOCK_SIZE_01 > CFE_PLATFORM_ES_MEM_BLOCK_SIZE_02 +#error CFE_PLATFORM_ES_MEM_BLOCK_SIZE_01 must be less than CFE_PLATFORM_ES_MEM_BLOCK_SIZE_02 +#endif + +#if CFE_PLATFORM_ES_MEM_BLOCK_SIZE_02 > CFE_PLATFORM_ES_MEM_BLOCK_SIZE_03 +#error CFE_PLATFORM_ES_MEM_BLOCK_SIZE_02 must be less than CFE_PLATFORM_ES_MEM_BLOCK_SIZE_03 +#endif + +#if CFE_PLATFORM_ES_MEM_BLOCK_SIZE_03 > CFE_PLATFORM_ES_MEM_BLOCK_SIZE_04 +#error CFE_PLATFORM_ES_MEM_BLOCK_SIZE_03 must be less than CFE_PLATFORM_ES_MEM_BLOCK_SIZE_04 +#endif + +#if CFE_PLATFORM_ES_MEM_BLOCK_SIZE_04 > CFE_PLATFORM_ES_MEM_BLOCK_SIZE_05 +#error CFE_PLATFORM_ES_MEM_BLOCK_SIZE_04 must be less than CFE_PLATFORM_ES_MEM_BLOCK_SIZE_05 +#endif + +#if CFE_PLATFORM_ES_MEM_BLOCK_SIZE_05 > CFE_PLATFORM_ES_MEM_BLOCK_SIZE_06 +#error CFE_PLATFORM_ES_MEM_BLOCK_SIZE_05 must be less than CFE_PLATFORM_ES_MEM_BLOCK_SIZE_06 +#endif + +#if CFE_PLATFORM_ES_MEM_BLOCK_SIZE_06 > CFE_PLATFORM_ES_MEM_BLOCK_SIZE_07 +#error CFE_PLATFORM_ES_MEM_BLOCK_SIZE_06 must be less than CFE_PLATFORM_ES_MEM_BLOCK_SIZE_07 +#endif + +#if CFE_PLATFORM_ES_MEM_BLOCK_SIZE_07 > CFE_PLATFORM_ES_MEM_BLOCK_SIZE_08 +#error CFE_PLATFORM_ES_MEM_BLOCK_SIZE_07 must be less than CFE_PLATFORM_ES_MEM_BLOCK_SIZE_08 +#endif + +#if CFE_PLATFORM_ES_MEM_BLOCK_SIZE_08 > CFE_PLATFORM_ES_MEM_BLOCK_SIZE_09 +#error CFE_PLATFORM_ES_MEM_BLOCK_SIZE_08 must be less than CFE_PLATFORM_ES_MEM_BLOCK_SIZE_09 +#endif + +#if CFE_PLATFORM_ES_MEM_BLOCK_SIZE_09 > CFE_PLATFORM_ES_MEM_BLOCK_SIZE_10 +#error CFE_PLATFORM_ES_MEM_BLOCK_SIZE_09 must be less than CFE_PLATFORM_ES_MEM_BLOCK_SIZE_10 +#endif + +#if CFE_PLATFORM_ES_MEM_BLOCK_SIZE_10 > CFE_PLATFORM_ES_MEM_BLOCK_SIZE_11 +#error CFE_PLATFORM_ES_MEM_BLOCK_SIZE_10 must be less than CFE_PLATFORM_ES_MEM_BLOCK_SIZE_11 +#endif + +#if CFE_PLATFORM_ES_MEM_BLOCK_SIZE_11 > CFE_PLATFORM_ES_MEM_BLOCK_SIZE_12 +#error CFE_PLATFORM_ES_MEM_BLOCK_SIZE_11 must be less than CFE_PLATFORM_ES_MEM_BLOCK_SIZE_12 +#endif + +#if CFE_PLATFORM_ES_MEM_BLOCK_SIZE_12 > CFE_PLATFORM_ES_MEM_BLOCK_SIZE_13 +#error CFE_PLATFORM_ES_MEM_BLOCK_SIZE_12 must be less than CFE_PLATFORM_ES_MEM_BLOCK_SIZE_13 +#endif + +#if CFE_PLATFORM_ES_MEM_BLOCK_SIZE_13 > CFE_PLATFORM_ES_MEM_BLOCK_SIZE_14 +#error CFE_PLATFORM_ES_MEM_BLOCK_SIZE_13 must be less than CFE_PLATFORM_ES_MEM_BLOCK_SIZE_14 +#endif + +#if CFE_PLATFORM_ES_MEM_BLOCK_SIZE_14 > CFE_PLATFORM_ES_MEM_BLOCK_SIZE_15 +#error CFE_PLATFORM_ES_MEM_BLOCK_SIZE_14 must be less than CFE_PLATFORM_ES_MEM_BLOCK_SIZE_15 +#endif + +#if CFE_PLATFORM_ES_MEM_BLOCK_SIZE_15 > CFE_PLATFORM_ES_MEM_BLOCK_SIZE_16 +#error CFE_PLATFORM_ES_MEM_BLOCK_SIZE_15 must be less than CFE_PLATFORM_ES_MEM_BLOCK_SIZE_16 +#endif + +#if CFE_PLATFORM_ES_MEM_BLOCK_SIZE_16 > CFE_PLATFORM_ES_MAX_BLOCK_SIZE +#error CFE_PLATFORM_ES_MEM_BLOCK_SIZE_16 must be less than CFE_PLATFORM_ES_MAX_BLOCK_SIZE +#endif + +/* +** Intermediate ES Critical Data Store Memory Pool Block Sizes +*/ +#if CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_01 > CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_02 +#error CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_01 must be less than CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_02 +#endif + +#if CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_02 > CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_03 +#error CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_02 must be less than CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_03 +#endif + +#if CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_03 > CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_04 +#error CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_03 must be less than CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_04 +#endif + +#if CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_04 > CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_05 +#error CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_04 must be less than CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_05 +#endif + +#if CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_05 > CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_06 +#error CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_05 must be less than CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_06 +#endif + +#if CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_06 > CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_07 +#error CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_06 must be less than CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_07 +#endif + +#if CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_07 > CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_08 +#error CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_07 must be less than CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_08 +#endif + +#if CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_08 > CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_09 +#error CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_08 must be less than CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_09 +#endif + +#if CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_09 > CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_10 +#error CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_09 must be less than CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_10 +#endif + +#if CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_10 > CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_11 +#error CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_10 must be less than CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_11 +#endif + +#if CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_11 > CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_12 +#error CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_11 must be less than CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_12 +#endif + +#if CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_12 > CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_13 +#error CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_12 must be less than CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_13 +#endif + +#if CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_13 > CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_14 +#error CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_13 must be less than CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_14 +#endif + +#if CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_14 > CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_15 +#error CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_14 must be less than CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_15 +#endif + +#if CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_15 > CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_16 +#error CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_15 must be less than CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_16 +#endif + +#if CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_16 > CFE_PLATFORM_ES_CDS_MAX_BLOCK_SIZE +#error CFE_PLATFORM_ES_CDS_MEM_BLOCK_SIZE_16 must be less than CFE_PLATFORM_ES_CDS_MAX_BLOCK_SIZE +#endif + +/* +** Validate task stack size... +*/ +#if CFE_PLATFORM_ES_START_TASK_STACK_SIZE < 2048 +#error CFE_PLATFORM_ES_START_TASK_STACK_SIZE must be greater than or equal to 2048 +#endif + +#if ((CFE_MISSION_MAX_API_LEN % 4) != 0) +#error CFE_MISSION_MAX_API_LEN must be a multiple of 4 +#endif +#if ((CFE_MISSION_MAX_PATH_LEN % 4) != 0) +#error CFE_MISSION_MAX_PATH_LEN must be a multiple of 4 +#endif +#if ((CFE_MISSION_MAX_FILE_LEN % 4) != 0) +#error CFE_MISSION_MAX_FILE_LEN must be a multiple of 4 +#endif +#if ((CFE_MISSION_ES_CDS_MAX_NAME_LENGTH % 4) != 0) +#error CFE_MISSION_ES_CDS_MAX_NAME_LENGTH must be a multiple of 4 +#endif +#if ((CFE_MISSION_ES_CDS_MAX_FULL_NAME_LEN % 4) != 0) +#error CFE_MISSION_ES_CDS_MAX_FULL_NAME_LEN must be a multiple of 4 +#endif + +#endif /* CFE_ES_VERIFY_H */