From 1272f6d58a8c4dbc7ce1c7643d0dff1de84b9a8d Mon Sep 17 00:00:00 2001 From: Joseph Hickey Date: Thu, 18 Apr 2024 10:44:09 -0400 Subject: [PATCH 1/2] Fix #2550, Use resource ID for validation index Use a resourceID value for access into the validation result structure array. This allows for consistent lookup, matching, and free/in-use determination, as well as improved resilience to race conditions and stale data. --- .../fsw/inc/cfe_core_resourceid_basevalues.h | 8 + modules/tbl/fsw/src/cfe_tbl_api.c | 138 +++--- modules/tbl/fsw/src/cfe_tbl_internal.c | 168 +++++++- modules/tbl/fsw/src/cfe_tbl_internal.h | 100 ++++- modules/tbl/fsw/src/cfe_tbl_resource.c | 67 +++ modules/tbl/fsw/src/cfe_tbl_resource.h | 268 ++++++++++++ modules/tbl/fsw/src/cfe_tbl_task.h | 49 ++- modules/tbl/fsw/src/cfe_tbl_task_cmds.c | 106 ++--- modules/tbl/fsw/src/cfe_tbl_transaction.c | 4 +- modules/tbl/fsw/src/cfe_tbl_transaction.h | 17 +- modules/tbl/ut-coverage/tbl_UT.c | 400 +++++++++++------- modules/tbl/ut-coverage/tbl_UT.h | 5 + 12 files changed, 1000 insertions(+), 330 deletions(-) diff --git a/modules/resourceid/fsw/inc/cfe_core_resourceid_basevalues.h b/modules/resourceid/fsw/inc/cfe_core_resourceid_basevalues.h index 3c851430b..443136be6 100644 --- a/modules/resourceid/fsw/inc/cfe_core_resourceid_basevalues.h +++ b/modules/resourceid/fsw/inc/cfe_core_resourceid_basevalues.h @@ -70,6 +70,10 @@ enum /* configuration registry */ CFE_RESOURCEID_CONFIGID_BASE_OFFSET = OS_OBJECT_TYPE_USER + 7, + + /* TBL managed resources */ + CFE_RESOURCEID_TBL_VALRESULTID_BASE_OFFSET = OS_OBJECT_TYPE_USER + 8, + }; /* @@ -92,6 +96,10 @@ enum /* configuration registry */ CFE_CONFIGID_BASE = CFE_RESOURCEID_MAKE_BASE(CFE_RESOURCEID_CONFIGID_BASE_OFFSET), + + /* TBL managed resources */ + CFE_TBL_VALRESULTID_BASE = CFE_RESOURCEID_MAKE_BASE(CFE_RESOURCEID_TBL_VALRESULTID_BASE_OFFSET), + }; /** @} */ diff --git a/modules/tbl/fsw/src/cfe_tbl_api.c b/modules/tbl/fsw/src/cfe_tbl_api.c index 36e08f068..e63252ef8 100644 --- a/modules/tbl/fsw/src/cfe_tbl_api.c +++ b/modules/tbl/fsw/src/cfe_tbl_api.c @@ -839,128 +839,90 @@ CFE_Status_t CFE_TBL_ReleaseAddresses(uint16 NumTables, const CFE_TBL_Handle_t T *-----------------------------------------------------------------*/ CFE_Status_t CFE_TBL_Validate(CFE_TBL_Handle_t TblHandle) { - CFE_TBL_TxnState_t Txn; - int32 Status; - CFE_ES_AppId_t ThisAppId; - CFE_TBL_RegistryRec_t *RegRecPtr; - char AppName[OS_MAX_API_NAME] = {"UNKNOWN"}; + CFE_TBL_TxnState_t Txn; + int32 Status; + CFE_TBL_RegistryRec_t * RegRecPtr; + char AppName[OS_MAX_API_NAME] = {"UNKNOWN"}; + CFE_TBL_LoadBuff_t * BuffPtr; + CFE_TBL_ValidationResult_t *ResultPtr; + const char * LogTagStr; + + ResultPtr = NULL; + BuffPtr = NULL; + LogTagStr = "(none)"; /* Verify that this application has the right to perform operation */ Status = CFE_TBL_TxnStartFromHandle(&Txn, TblHandle, CFE_TBL_TxnContext_OWNER_APP); - - ThisAppId = CFE_TBL_TxnAppId(&Txn); - if (Status == CFE_SUCCESS) { /* Get pointers to pertinent records in registry and handles */ RegRecPtr = CFE_TBL_TxnRegRec(&Txn); - CFE_TBL_TxnFinish(&Txn); - - CFE_ES_GetAppName(AppName, ThisAppId, sizeof(AppName)); - /* Identify the image to be validated, starting with the Inactive Buffer */ - if (RegRecPtr->ValidateInactiveIndex != CFE_TBL_NO_VALIDATION_PENDING) + ResultPtr = CFE_TBL_CheckValidationRequest(&RegRecPtr->ValidateInactiveId); + if (ResultPtr != NULL) + { + LogTagStr = "inactive"; + BuffPtr = CFE_TBL_GetInactiveBuffer(RegRecPtr); + } + else { - /* Identify whether the Inactive Buffer is a shared buffer or a dedicated one */ - if (RegRecPtr->DoubleBuffered) + ResultPtr = CFE_TBL_CheckValidationRequest(&RegRecPtr->ValidateActiveId); + if (ResultPtr != NULL) { - /* Call the Application's Validation function for the Inactive Buffer */ - Status = - (RegRecPtr->ValidationFuncPtr)(RegRecPtr->Buffers[(1U - RegRecPtr->ActiveBufferIndex)].BufferPtr); - - /* Allow buffer to be activated after passing validation */ - if (Status == CFE_SUCCESS) - { - RegRecPtr->Buffers[(1U - RegRecPtr->ActiveBufferIndex)].Validated = true; - } + LogTagStr = "active"; + BuffPtr = CFE_TBL_GetActiveBuffer(RegRecPtr); } - else - { - /* Call the Application's Validation function for the appropriate shared buffer */ - Status = (RegRecPtr->ValidationFuncPtr)(CFE_TBL_Global.LoadBuffs[RegRecPtr->LoadInProgress].BufferPtr); + } - /* Allow buffer to be activated after passing validation */ - if (Status == CFE_SUCCESS) - { - CFE_TBL_Global.LoadBuffs[RegRecPtr->LoadInProgress].Validated = true; - } - } + CFE_TBL_TxnFinish(&Txn); - if (Status == CFE_SUCCESS) - { - CFE_EVS_SendEventWithAppID(CFE_TBL_VALIDATION_INF_EID, CFE_EVS_EventType_INFORMATION, - CFE_TBL_Global.TableTaskAppId, "%s validation successful for Inactive '%s'", - AppName, RegRecPtr->Name); - } - else + if (ResultPtr != NULL) + { + if (BuffPtr == NULL) { - CFE_EVS_SendEventWithAppID(CFE_TBL_VALIDATION_ERR_EID, CFE_EVS_EventType_ERROR, - CFE_TBL_Global.TableTaskAppId, - "%s validation failed for Inactive '%s', Status=0x%08X", AppName, - RegRecPtr->Name, (unsigned int)Status); - - if (Status > CFE_SUCCESS) - { - CFE_ES_WriteToSysLog("%s: App(%lu) Validation func return code invalid (Stat=0x%08X) for '%s'\n", - __func__, CFE_RESOURCEID_TO_ULONG(CFE_TBL_Global.TableTaskAppId), - (unsigned int)Status, RegRecPtr->Name); - } + /* No buffer, it cannot be valid */ + ResultPtr->Result = -1; } - - /* Save the result of the Validation function for the Table Services Task */ - CFE_TBL_Global.ValidationResults[RegRecPtr->ValidateInactiveIndex].Result = Status; - - /* Once validation is complete, set flags to indicate response is ready */ - CFE_TBL_Global.ValidationResults[RegRecPtr->ValidateInactiveIndex].State = CFE_TBL_VALIDATION_PERFORMED; - RegRecPtr->ValidateInactiveIndex = CFE_TBL_NO_VALIDATION_PENDING; - - /* Since the validation was successfully performed (although maybe not a successful result) */ - /* return a success status */ - Status = CFE_SUCCESS; - } - else if (RegRecPtr->ValidateActiveIndex != CFE_TBL_NO_VALIDATION_PENDING) - { - /* Perform validation on the currently active table buffer */ - /* Identify whether the Active Buffer is a shared buffer or a dedicated one */ - if (RegRecPtr->DoubleBuffered) + else if (RegRecPtr->ValidationFuncPtr == NULL) { - /* Call the Application's Validation function for the Dedicated Active Buffer */ - Status = (RegRecPtr->ValidationFuncPtr)(RegRecPtr->Buffers[RegRecPtr->ActiveBufferIndex].BufferPtr); + /* no validation function, assume its OK */ + ResultPtr->Result = 0; } else { - /* Call the Application's Validation function for the static buffer */ - Status = (RegRecPtr->ValidationFuncPtr)(RegRecPtr->Buffers[0].BufferPtr); + /* Save the result of the Validation function for the Table Services Task */ + ResultPtr->Result = (RegRecPtr->ValidationFuncPtr)(BuffPtr->BufferPtr); } - if (Status == CFE_SUCCESS) + /* Get the app name for logging */ + CFE_ES_GetAppName(AppName, CFE_TBL_TxnAppId(&Txn), sizeof(AppName)); + + /* Allow buffer to be activated after passing validation */ + if (ResultPtr->Result == 0) { + BuffPtr->Validated = true; CFE_EVS_SendEventWithAppID(CFE_TBL_VALIDATION_INF_EID, CFE_EVS_EventType_INFORMATION, - CFE_TBL_Global.TableTaskAppId, "%s validation successful for Active '%s'", - AppName, RegRecPtr->Name); + CFE_TBL_Global.TableTaskAppId, "%s validation successful for %s '%s'", + AppName, LogTagStr, RegRecPtr->Name); } else { CFE_EVS_SendEventWithAppID(CFE_TBL_VALIDATION_ERR_EID, CFE_EVS_EventType_ERROR, CFE_TBL_Global.TableTaskAppId, - "%s validation failed for Active '%s', Status=0x%08X", AppName, - RegRecPtr->Name, (unsigned int)Status); + "%s validation failed for %s '%s', Status=0x%08X", AppName, RegRecPtr->Name, + LogTagStr, (unsigned int)Status); - if (Status > CFE_SUCCESS) + if (ResultPtr->Result > 0) { CFE_ES_WriteToSysLog("%s: App(%lu) Validation func return code invalid (Stat=0x%08X) for '%s'\n", __func__, CFE_RESOURCEID_TO_ULONG(CFE_TBL_Global.TableTaskAppId), - (unsigned int)Status, RegRecPtr->Name); + (unsigned int)ResultPtr->Result, RegRecPtr->Name); } } - /* Save the result of the Validation function for the Table Services Task */ - CFE_TBL_Global.ValidationResults[RegRecPtr->ValidateActiveIndex].Result = Status; - - /* Once validation is complete, reset the flags */ - CFE_TBL_Global.ValidationResults[RegRecPtr->ValidateActiveIndex].State = CFE_TBL_VALIDATION_PERFORMED; - RegRecPtr->ValidateActiveIndex = CFE_TBL_NO_VALIDATION_PENDING; + /* Once validation is complete, set flags to indicate response is ready */ + ResultPtr->State = CFE_TBL_VALIDATION_PERFORMED; /* Since the validation was successfully performed (although maybe not a successful result) */ /* return a success status */ @@ -973,8 +935,8 @@ CFE_Status_t CFE_TBL_Validate(CFE_TBL_Handle_t TblHandle) } else { - CFE_ES_WriteToSysLog("%s: App(%lu) does not have access to Tbl Handle=%d\n", __func__, - CFE_RESOURCEID_TO_ULONG(ThisAppId), (int)TblHandle); + CFE_ES_WriteToSysLog("%s: App(%lu) does not have access to Tbl Handle=%lu\n", __func__, + CFE_TBL_TxnAppIdAsULong(&Txn), CFE_TBL_TxnHandleAsULong(&Txn)); } return Status; diff --git a/modules/tbl/fsw/src/cfe_tbl_internal.c b/modules/tbl/fsw/src/cfe_tbl_internal.c index 4ab8ef482..76053e463 100644 --- a/modules/tbl/fsw/src/cfe_tbl_internal.c +++ b/modules/tbl/fsw/src/cfe_tbl_internal.c @@ -89,12 +89,6 @@ int32 CFE_TBL_EarlyInit(void) CFE_TBL_HandleLinkInit(&CFE_TBL_Global.Handles[i].Link); } - /* Initialize the Table Validation Results Records nonzero values */ - for (i = 0; i < CFE_PLATFORM_TBL_MAX_NUM_VALIDATIONS; i++) - { - CFE_TBL_Global.ValidationResults[i].State = CFE_TBL_VALIDATION_FREE; - } - /* Initialize the Dump-Only Table Dump Control Blocks nonzero values */ for (i = 0; i < CFE_PLATFORM_TBL_MAX_SIMULTANEOUS_LOADS; i++) { @@ -223,13 +217,13 @@ void CFE_TBL_InitRegistryRecord(CFE_TBL_RegistryRec_t *RegRecPtr) { memset(RegRecPtr, 0, sizeof(*RegRecPtr)); - RegRecPtr->OwnerAppId = CFE_TBL_NOT_OWNED; - RegRecPtr->NotificationMsgId = CFE_SB_INVALID_MSG_ID; - RegRecPtr->LoadInProgress = CFE_TBL_NO_LOAD_IN_PROGRESS; - RegRecPtr->ValidateActiveIndex = CFE_TBL_NO_VALIDATION_PENDING; - RegRecPtr->ValidateInactiveIndex = CFE_TBL_NO_VALIDATION_PENDING; - RegRecPtr->CDSHandle = CFE_ES_CDS_BAD_HANDLE; - RegRecPtr->DumpControlIndex = CFE_TBL_NO_DUMP_PENDING; + RegRecPtr->OwnerAppId = CFE_TBL_NOT_OWNED; + RegRecPtr->NotificationMsgId = CFE_SB_INVALID_MSG_ID; + RegRecPtr->LoadInProgress = CFE_TBL_NO_LOAD_IN_PROGRESS; + RegRecPtr->ValidateActiveId = CFE_TBL_NO_VALIDATION_PENDING; + RegRecPtr->ValidateInactiveId = CFE_TBL_NO_VALIDATION_PENDING; + RegRecPtr->CDSHandle = CFE_ES_CDS_BAD_HANDLE; + RegRecPtr->DumpControlIndex = CFE_TBL_NO_DUMP_PENDING; CFE_TBL_HandleLinkInit(&RegRecPtr->AccessList); } @@ -322,6 +316,115 @@ int32 CFE_TBL_UnlockRegistry(void) return Status; } +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_TBL_LoadBuff_t *CFE_TBL_GetActiveBuffer(CFE_TBL_RegistryRec_t *RegRecPtr) +{ + int32 BufferIndex; + + /* + * This should be simpler because ActiveBufferIndex always refers to + * the active buffer, and applies to both single and double buffered + * (That is, it is always 0 on a single-buffered table). + * + * However, legacy code always checked the double buffer flag before + * using ActiveBufferIndex so this will to (at least for now) + */ + if (RegRecPtr->DoubleBuffered) + { + BufferIndex = RegRecPtr->ActiveBufferIndex; + } + else + { + BufferIndex = 0; + } + + return &RegRecPtr->Buffers[BufferIndex]; +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +int32 CFE_TBL_GetNextLocalBufferId(CFE_TBL_RegistryRec_t *RegRecPtr) +{ + /* This implements a flip-flop buffer: if active is 1, return 0 and vice versa */ + return (1 - (RegRecPtr->ActiveBufferIndex & 1)); +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_TBL_LoadBuff_t *CFE_TBL_GetInactiveBuffer(CFE_TBL_RegistryRec_t *RegRecPtr) +{ + int32 BufferIndex; + CFE_TBL_LoadBuff_t *Result; + + if (RegRecPtr->DoubleBuffered) + { + /* Determine the index of the Inactive Buffer Pointer */ + BufferIndex = CFE_TBL_GetNextLocalBufferId(RegRecPtr); + + /* then return the pointer to it */ + Result = &RegRecPtr->Buffers[BufferIndex]; + } + else if (!RegRecPtr->UserDefAddr && RegRecPtr->LoadInProgress != CFE_TBL_NO_LOAD_IN_PROGRESS) + { + /* + * The only time a single buffered table has an inactive buffer is when its loading, and + * this always refers to a shared load buffer + */ + Result = &CFE_TBL_Global.LoadBuffs[RegRecPtr->LoadInProgress]; + } + else + { + /* Tables with a user-defined address never have an inactive buffer */ + Result = NULL; + } + + return Result; +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_TBL_LoadBuff_t *CFE_TBL_GetSelectedBuffer(CFE_TBL_RegistryRec_t * RegRecPtr, + CFE_TBL_BufferSelect_Enum_t BufferSelect) +{ + CFE_TBL_LoadBuff_t *Result; + + switch (BufferSelect) + { + case CFE_TBL_BufferSelect_INACTIVE: + Result = CFE_TBL_GetInactiveBuffer(RegRecPtr); + break; + case CFE_TBL_BufferSelect_ACTIVE: + Result = CFE_TBL_GetActiveBuffer(RegRecPtr); + break; + default: + CFE_EVS_SendEvent(CFE_TBL_ILLEGAL_BUFF_PARAM_ERR_EID, CFE_EVS_EventType_ERROR, + "Cmd for Table '%s' had illegal buffer parameter (0x%08X)", RegRecPtr->Name, + (unsigned int)BufferSelect); + + Result = NULL; + break; + } + + return Result; +} + /*---------------------------------------------------------------- * * Application-scope internal function @@ -1546,3 +1649,42 @@ void CFE_TBL_CountAccessDescHelper(CFE_TBL_AccessDescriptor_t *AccDescPtr, void ++(*Count); } + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_TBL_ValidationResult_t *CFE_TBL_CheckValidationRequest(CFE_TBL_ValidationResultId_t *ValIdPtr) +{ + CFE_TBL_ValidationResult_t * ResultPtr; + CFE_TBL_ValidationResultId_t ValId; + + ValId = *ValIdPtr; + + /* + * always clear the flag, regardless of "IsMatch" above. If it was not a match, + * that means the ID was stale, and it will never be a match (ie. it was aborted somehow) + * + * However, because this also acts as a flag, only write to the global if it was set to a value, + * do not unconditionally write undefined value here. + */ + if (CFE_RESOURCEID_TEST_DEFINED(ValId)) + { + *ValIdPtr = CFE_TBL_VALRESULTID_UNDEFINED; + + ResultPtr = CFE_TBL_LocateValidationResultByID(ValId); + } + else + { + ResultPtr = NULL; + } + + if (!CFE_TBL_ValidationResultIsMatch(ResultPtr, ValId)) + { + ResultPtr = NULL; + } + + return ResultPtr; +} diff --git a/modules/tbl/fsw/src/cfe_tbl_internal.h b/modules/tbl/fsw/src/cfe_tbl_internal.h index 76bc5bc3f..23c7654c0 100644 --- a/modules/tbl/fsw/src/cfe_tbl_internal.h +++ b/modules/tbl/fsw/src/cfe_tbl_internal.h @@ -57,7 +57,7 @@ * \param AccDescPtr Pointer to the current access descriptor * \param Arg Opaque argument from caller (passed through) */ -typedef void (* const CFE_TBL_AccessDescFunc_t)(CFE_TBL_AccessDescriptor_t *AccDescPtr, void *Arg); +typedef void (*const CFE_TBL_AccessDescFunc_t)(CFE_TBL_AccessDescriptor_t *AccDescPtr, void *Arg); /***************************** Function Prototypes **********************************/ @@ -647,6 +647,104 @@ void CFE_TBL_HandleListRemoveLink(CFE_TBL_RegistryRec_t *RegRecPtr, CFE_TBL_Acce */ void CFE_TBL_HandleListInsertLink(CFE_TBL_RegistryRec_t *RegRecPtr, CFE_TBL_AccessDescriptor_t *AccessDescPtr); +/*---------------------------------------------------------------------------------------*/ +/** +** \brief Gets the ID of the next buffer to use on a double-buffered table +** +** \par Description +** This returns the identifier for the local table buffer that should be +** loaded next. +** +** \par Assumptions, External Events, and Notes: +** This is not applicable to single-buffered tables. +** +** \param RegRecPtr The table registry record +** \returns Identifier of next buffer to use +*/ +int32 CFE_TBL_GetNextLocalBufferId(CFE_TBL_RegistryRec_t *RegRecPtr); + +/*---------------------------------------------------------------------------------------*/ +/** +** \brief Gets the currently-active buffer pointer for a table +** +** \par Description +** This returns a pointer to the currently active table buffer. On a single-buffered +** table, this is always the first/only buffer. This function never returns NULL, as +** all tables have at least one buffer. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param RegRecPtr The table registry record +** \returns Pointer to the active table buffer +*/ +CFE_TBL_LoadBuff_t *CFE_TBL_GetActiveBuffer(CFE_TBL_RegistryRec_t *RegRecPtr); + +/*---------------------------------------------------------------------------------------*/ +/** +** \brief Gets the inactive buffer pointer for a table +** +** \par Description +** This returns a pointer to inactive table buffer. On a double-buffered table +** this refers to whichever buffer is _not_ currently active (that is, the opposite +** buffer from what is returned by CFE_TBL_GetActiveBuffer()). +** +** On a single-buffered, if there is a load in progress that is utilizing one of the +** global/shared load buffers, then this returns a pointer to that buffer. If there +** is no load in progress, this returns NULL to indicate there is no inactive buffer. +** +** \par Assumptions, External Events, and Notes: +** This funtion may return NULL if there is no inactive buffer associated with the table +** +** \param RegRecPtr The table registry record +** \returns Pointer to the inactive table buffer +*/ +CFE_TBL_LoadBuff_t *CFE_TBL_GetInactiveBuffer(CFE_TBL_RegistryRec_t *RegRecPtr); + +/*---------------------------------------------------------------------------------------*/ +/** +** \brief Gets the buffer pointer for a table based on the selection enum +** +** \par Description +** Gets either the active buffer (see CFE_TBL_GetActiveBuffer()) or the inactive +** buffer (see CFE_TBL_GetInactiveBuffer()) based on the BufferSelect parameter. +** +** \par Assumptions, External Events, and Notes: +** This funtion may return NULL if there is no buffer associated with the table +** This will send an event if the BufferSelect parameter is not valid +** +** \param RegRecPtr The table registry record +** \param BufferSelect The buffer to obtain (active or inactive) +** \returns Pointer to the selected table buffer +*/ +CFE_TBL_LoadBuff_t *CFE_TBL_GetSelectedBuffer(CFE_TBL_RegistryRec_t * RegRecPtr, + CFE_TBL_BufferSelect_Enum_t BufferSelect); + +/*---------------------------------------------------------------------------------------*/ +/** +** \brief Checks if a validation request is pending and clears the request +** +** \par Description +** This checks the given flag (which is a request ID) to determine if a table validation +** request is pending. +** +** If no validation request is pending, this returns NULL and nothing else is done. +** +** If a validation request is pending, then this clears the request (by writing +** #CFE_TBL_VALRESULTID_UNDEFINED to the request flag) and returns a pointer to the +** corresponding Validation Result buffer that refers to that request. The request +** should be in the PENDING state. +** +** \par Assumptions, External Events, and Notes: +** This will clear the flag if there was a pending request, as it is expected that the +** caller will be performing the validation at this time. +** +** \param ValIdPtr Pointer to the table validation request flag (from the table registry entry) +** \returns Pointer to the request, if a request was pending +** \retval NULL if no request was pending +*/ +CFE_TBL_ValidationResult_t *CFE_TBL_CheckValidationRequest(CFE_TBL_ValidationResultId_t *ValIdPtr); + /* ** Globals specific to the TBL module */ diff --git a/modules/tbl/fsw/src/cfe_tbl_resource.c b/modules/tbl/fsw/src/cfe_tbl_resource.c index 9f0113f73..06e90b987 100644 --- a/modules/tbl/fsw/src/cfe_tbl_resource.c +++ b/modules/tbl/fsw/src/cfe_tbl_resource.c @@ -32,6 +32,7 @@ ** Includes */ #include "cfe_tbl_module_all.h" +#include "cfe_core_resourceid_basevalues.h" /*---------------------------------------------------------------- * @@ -51,6 +52,18 @@ CFE_Status_t CFE_TBL_Handle_ToIndex(CFE_TBL_Handle_t TblHandle, uint32 *Idx) return CFE_SUCCESS; } +/*---------------------------------------------------------------- + * + * Implemented per public API + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_Status_t CFE_TBL_ValidationResultId_ToIndex(CFE_TBL_ValidationResultId_t ValResultId, uint32 *Idx) +{ + return CFE_ResourceId_ToIndex(CFE_RESOURCEID_UNWRAP(ValResultId), CFE_TBL_VALRESULTID_BASE, + CFE_PLATFORM_TBL_MAX_NUM_VALIDATIONS, Idx); +} + /*---------------------------------------------------------------- * * Application-scope internal function @@ -138,3 +151,57 @@ CFE_TBL_Handle_t CFE_TBL_AccessDescriptorGetHandle(const CFE_TBL_AccessDescripto /* The pointer should be to an entry within the Handles array */ return (AccessDescPtr - CFE_TBL_Global.Handles); } + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_TBL_ValidationResult_t *CFE_TBL_LocateValidationResultByID(CFE_TBL_ValidationResultId_t ValResultId) +{ + CFE_TBL_ValidationResult_t *ResultPtr; + uint32 Idx; + + if (CFE_TBL_ValidationResultId_ToIndex(ValResultId, &Idx) == CFE_SUCCESS) + { + ResultPtr = &CFE_TBL_Global.ValidationResults[Idx]; + } + else + { + ResultPtr = NULL; + } + + return ResultPtr; +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +bool CFE_TBL_CheckValidationResultSlotUsed(CFE_ResourceId_t CheckId) +{ + CFE_TBL_ValidationResult_t *BuffPtr; + + /* + * 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. + */ + BuffPtr = CFE_TBL_LocateValidationResultByID(CFE_TBL_VALRESULTID_C(CheckId)); + return (BuffPtr == NULL || CFE_TBL_ValidationResultIsUsed(BuffPtr)); +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_ResourceId_t CFE_TBL_GetNextValResultBlock(void) +{ + return CFE_ResourceId_FindNext(CFE_TBL_Global.LastValidationResultId, CFE_PLATFORM_TBL_MAX_NUM_VALIDATIONS, + CFE_TBL_CheckValidationResultSlotUsed); +} diff --git a/modules/tbl/fsw/src/cfe_tbl_resource.h b/modules/tbl/fsw/src/cfe_tbl_resource.h index 2195665ec..5eba6922b 100644 --- a/modules/tbl/fsw/src/cfe_tbl_resource.h +++ b/modules/tbl/fsw/src/cfe_tbl_resource.h @@ -24,6 +24,37 @@ * * A CFE TBL Resource ID is a common way to identify CFE-managed resources such * as registry entries, buffers, state records, and other entities. + * + * ABOUT RESOURCE TABLE ACCESSORS + * ============================== + * + * These accessors facilitate consistent lookup/matching/allocation/deallocation patterns + * across all TBL resources. The following types of resources can be managed in this + * fashion: + * + * - Access Descriptors (Table Handles, external identifiers) + * - Registry Records (Table registry, internal identifiers) + * - Load Buffers (both shared and table-specific) + * - Validation Results + * - Dump State + * - CDS registries + * + * A full set of accessors contains the following basic methods: + * + * | **Method** | **Description** | + * |:------------|:------------------------------------------------------| + * | LocateByID | Returns a pointer to the entry associated with an ID | + * | ToIndex | Converts an entry ID to a 0-based array index | + * | IsUsed | Checks if a given entry is currently in use | + * | SetUsed | Sets an entry as being in use / not available | + * | SetFree | Sets an entry as being available / not in use | + * | GetId | Gets the resource ID associated with an entry pointer | + * | IsMatch | Checks if an entry pointer is a match to the given ID | + * | GetNext | Returns the next/pending ID suitable for a new record | + * + * This file should implement each method for each supported resource type that + * implements these access patterns. + * */ #ifndef CFE_TBL_RESOURCE_H @@ -36,6 +67,205 @@ #include "cfe_core_resourceid_basevalues.h" #include "cfe_tbl_task.h" +/* + * --------------------------------------------------------------------------------------- + * + * ~~~ VALIDATION RESULT TABLE ACCESSORS ~~~ + * + * These operate on CFE_TBL_ValidationResult_t* and CFE_TBL_ValidationResultId_t types + * + * --------------------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Locate the validation result table entry correlating with a given registry ID. + * + * This only returns a pointer to the table entry where the record + * should reside, but does _not_ actually check/validate the entry. + * + * If the passed-in ID parameter is not within the acceptable range of ID + * values for applications, such that it could never be valid under + * any circumstances, then NULL is returned. Otherwise, a pointer to the + * corresponding table entry is returned, indicating the location where + * that ID should reside, if it is currently in use. + * + * @note This only returns where the ID should reside, not that it actually + * resides there. If looking up an existing ID, then caller must additionally + * confirm that the returned record is a match to the expected ID before using + * or modifying the data within the returned record pointer. + * + * The CFE_TBL_ValidationResultIsMatch() function can be used to check/confirm + * if the returned table entry is a positive match for the given ID. + * + * @sa CFE_TBL_ValidationResultIsMatch() + * + * @param[in] ValResultId the registry ID to locate + * @return pointer to Validation Result Table entry for the given registry ID, or NULL if out of range + */ +CFE_TBL_ValidationResult_t *CFE_TBL_LocateValidationResultByID(CFE_TBL_ValidationResultId_t ValResultId); + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Get the array index correlating with a Validation Result ID + * + * Calculates the array position/index of the global array entry for + * the given result ID. + * + * @param[in] BlockID the ID/handle of the validation result 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 ID is outside valid range + */ +CFE_Status_t CFE_TBL_ValidationResultId_ToIndex(CFE_TBL_ValidationResultId_t ValResultId, uint32 *Idx); + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Check if a validation result table entry is in use or free/empty + * + * This routine checks if the 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. + * + * @note This internal helper function must only be used on result pointers + * that are known to refer to an actual table location (i.e. non-null). + * + * @param[in] BuffPtr pointer to validation result table entry + * @returns true if the entry is in use/configured, or false if it is free/empty + */ +static inline bool CFE_TBL_ValidationResultIsUsed(const CFE_TBL_ValidationResult_t *BuffPtr) +{ + return (CFE_RESOURCEID_TEST_DEFINED(BuffPtr->ValId)); +} + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Marks a validation result table entry as in use (not avaliable) + * + * This sets the internal field(s) within this entry, and marks + * it as being associated with the given validation result ID. + * + * @note This internal helper function must only be used on record pointers + * that are known to refer to an actual table location (i.e. non-null). + * + * @param[in] BuffPtr pointer to validation result table entry + * @param[in] PendingId the ID of this entry that will be set + */ +static inline void CFE_TBL_ValidationResultSetUsed(CFE_TBL_ValidationResult_t *BuffPtr, CFE_ResourceId_t PendingId) +{ + BuffPtr->ValId = CFE_TBL_VALRESULTID_C(PendingId); +} + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Marks a validation result table entry as available (not in use) + * + * This clears the internal field(s) within this entry, and marks + * it as not being associated with any validation result ID. + * + * @note This internal helper function must only be used on record pointers + * that are known to refer to an actual table location (i.e. non-null). + * + * @param[in] BuffPtr pointer to validation result table entry + */ +static inline void CFE_TBL_ValidationResultSetFree(CFE_TBL_ValidationResult_t *BuffPtr) +{ + BuffPtr->State = CFE_TBL_VALIDATION_FREE; /* for backward compatibility; not part of "IsUsed" check anymore */ + BuffPtr->ValId = CFE_TBL_VALRESULTID_UNDEFINED; +} + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Get the ID value from a validation result table entry + * + * This routine converts the table entry pointer to its corresponding ID. + * + * @note This internal helper function must only be used on record pointers + * that are known to refer to an actual table location (i.e. non-null). + * + * @param[in] BuffPtr pointer to table entry + * @returns ID of entry + */ +static inline CFE_TBL_ValidationResultId_t CFE_TBL_ValidationResultGetId(const CFE_TBL_ValidationResult_t *BuffPtr) +{ + return BuffPtr->ValId; +} + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Check if a validation result entry is a match for the given ID + * + * This routine confirms that the previously-located result record is valid + * and matches the expected validation result ID. + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * This function may be used in conjunction with CFE_TBL_LocateValidationResultByID() + * to confirm that the located record is a positive match to the expected ID. + * As such, the record pointer is also permitted to be NULL, to alleviate the + * need for the caller to handle this possibility explicitly. + * + * Once a record pointer has been successfully validated using this routine, + * it may be safely passed to all other internal functions. + * + * @sa CFE_TBL_LocateValidationResultByID + * + * @param[in] BuffPtr pointer to validation result table entry, or NULL + * @param[in] ValId expected validation result ID + * @returns true if the entry matches the given ID + */ +static inline bool CFE_TBL_ValidationResultIsMatch(const CFE_TBL_ValidationResult_t *BuffPtr, + CFE_TBL_ValidationResultId_t ValId) +{ + return (BuffPtr != NULL && CFE_RESOURCEID_TEST_EQUAL(BuffPtr->ValId, ValId)); +} + +/** + * @brief Determine the next ID to use for validation results + * + * Obtains an ID value that is usable for a new validation result. If no validation + * result entries are available, then UNDEFINED is returned. + * + * @returns ID to use for next result, or UNDEFINED if no slots available + */ +CFE_ResourceId_t CFE_TBL_GetNextValResultBlock(void); + +/** + * Test if a slot corresponding to a pending ID is used + * + * This is an internal helper function for CFE_ResourceId_FindNext(), and not + * typically called directly. It is prototyped here for unit testing. + * + * @returns True if used, False if available + */ +bool CFE_TBL_CheckValidationResultSlotUsed(CFE_ResourceId_t CheckId); + +/* + * --------------------------------------------------------------------------------------- + * + * ~~~ REGISTRY RECORD TABLE ACCESSORS ~~~ + * + * These operate on CFE_TBL_RegistryRec_t* and CFE_TBL_RegId_t types + * + * --------------------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Get the array index correlating with a Registry Record ID + * + * Calculates the array position/index of the global array entry for + * the given registry ID. + * + * @param[in] RegId the ID/handle of the registry record 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 ID is outside valid range + */ +CFE_Status_t CFE_TBL_RegId_ToIndex(CFE_TBL_RegId_t RegId, uint32 *Idx); + /*---------------------------------------------------------------------------------------*/ /** * @brief Locate the registry table entry correlating with a given registry ID. @@ -125,6 +355,20 @@ static inline bool CFE_TBL_RegistryRecordIsMatch(const CFE_TBL_RegistryRec_t *Re return (RegRecPtr != NULL); } +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Get the ID value from a registry record + * + * This routine converts the registry record pointer to its corresponding ID. + * + * @note This internal helper function must only be used on record pointers + * that are known to refer to an actual table location (i.e. non-null). + * + * @param[in] RegRecPtr pointer to table entry + * @returns ID of entry + */ +CFE_TBL_RegId_t CFE_TBL_RegistryRecordGetID(const CFE_TBL_RegistryRec_t *RegRecPtr); + /*---------------------------------------------------------------------------------------*/ /** * @brief Obtain the name associated with the Application record @@ -142,6 +386,30 @@ static inline const char *CFE_TBL_RegistryRecordGetName(const CFE_TBL_RegistryRe return RegRecPtr->Name; } +/* + * --------------------------------------------------------------------------------------- + * + * ~~~ ACCESS DESCRIPTOR TABLE ACCESSORS ~~~ + * + * These operate on CFE_TBL_AccessDescriptor_t* and CFE_TBL_Handle_t types + * + * --------------------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Get the array index correlating with a table handle/access ID + * + * Calculates the array position/index of the global array entry for + * the given handle. + * + * @param[in] TblHandle the ID/handle of the access descriptor 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 ID is outside valid range + */ +CFE_Status_t CFE_TBL_Handle_ToIndex(CFE_TBL_Handle_t TblHandle, uint32 *Idx); + /*---------------------------------------------------------------------------------------*/ /** * @brief Get the Handle ID from an an access descriptor pointer diff --git a/modules/tbl/fsw/src/cfe_tbl_task.h b/modules/tbl/fsw/src/cfe_tbl_task.h index d1e369b7c..5176d0f3f 100644 --- a/modules/tbl/fsw/src/cfe_tbl_task.h +++ b/modules/tbl/fsw/src/cfe_tbl_task.h @@ -66,7 +66,7 @@ ** This macro is used to indicate no Validation is Pending by assigning it to ** #CFE_TBL_RegistryRec_t::ValidateActiveIndex or #CFE_TBL_RegistryRec_t::ValidateInactiveIndex */ -#define CFE_TBL_NO_VALIDATION_PENDING (-1) +#define CFE_TBL_NO_VALIDATION_PENDING CFE_TBL_VALRESULTID_UNDEFINED /** \brief Value indicating when no Dump is Pending on a Dump-Only Table */ /** @@ -77,6 +77,16 @@ /************************ Internal Structure Definitions *****************************/ +/** + * @brief A type for Validation Result Buffer IDs + * + * This is the type that is used for any API accepting or returning a Validation Result ID + */ +typedef CFE_RESOURCEID_BASE_TYPE CFE_TBL_ValidationResultId_t; + +#define CFE_TBL_VALRESULTID_C(val) ((CFE_TBL_ValidationResultId_t)CFE_RESOURCEID_WRAP(val)) +#define CFE_TBL_VALRESULTID_UNDEFINED CFE_TBL_VALRESULTID_C(CFE_RESOURCEID_UNDEFINED) + /*******************************************************************************/ /** \brief Identifies the current state of a validation sequence. */ @@ -105,6 +115,8 @@ typedef enum */ typedef struct { + CFE_TBL_ValidationResultId_t ValId; + CFE_TBL_ValidationState_t State; /**< \brief Current state of this block of data */ int32 Result; /**< \brief Result returned by Application's Validation function */ uint32 CrcOfTable; /**< \brief Data Integrity Value computed on Table Buffer */ @@ -189,21 +201,23 @@ typedef struct CFE_TBL_CallbackFuncPtr_t ValidationFuncPtr; /**< \brief Ptr to Owner App's function that validates tbl contents */ CFE_TIME_SysTime_t TimeOfLastUpdate; /**< \brief Time when Table was last updated */ CFE_TBL_HandleLink_t AccessList; /**< \brief Linked List of associated access descriptors */ - int32 LoadInProgress; /**< \brief Flag identifies inactive buffer and whether load in progress */ - int32 ValidateActiveIndex; /**< \brief Index to Validation Request on Active Table Result data */ - int32 ValidateInactiveIndex; /**< \brief Index to Validation Request on Inactive Table Result data */ - int32 DumpControlIndex; /**< \brief Index to Dump Control Block */ - CFE_ES_CDSHandle_t CDSHandle; /**< \brief Handle to Critical Data Store for Critical Tables */ - CFE_MSG_FcnCode_t NotificationCC; /**< \brief Command Code of an associated management notification message */ - bool CriticalTable; /**< \brief Flag indicating whether table is a Critical Table */ - bool TableLoadedOnce; /**< \brief Flag indicating whether table has been loaded once or not */ - bool LoadPending; /**< \brief Flag indicating an inactive buffer is ready to be copied */ - bool DumpOnly; /**< \brief Flag indicating Table is NOT to be loaded */ - bool DoubleBuffered; /**< \brief Flag indicating Table has a dedicated inactive buffer */ - bool UserDefAddr; /**< \brief Flag indicating Table address was defined by Owner Application */ - bool NotifyByMsg; /**< \brief Flag indicating Table Services should notify owning App via message - when table requires management */ - uint8 ActiveBufferIndex; /**< \brief Index identifying which buffer is the active buffer */ + int32 LoadInProgress; /**< \brief Flag identifies inactive buffer and whether load in progress */ + CFE_TBL_ValidationResultId_t + ValidateActiveId; /**< \brief Index to Validation Request on Active Table Result data */ + CFE_TBL_ValidationResultId_t + ValidateInactiveId; /**< \brief Index to Validation Request on Inactive Table Result data */ + int32 DumpControlIndex; /**< \brief Index to Dump Control Block */ + CFE_ES_CDSHandle_t CDSHandle; /**< \brief Handle to Critical Data Store for Critical Tables */ + CFE_MSG_FcnCode_t NotificationCC; /**< \brief Command Code of an associated management notification message */ + bool CriticalTable; /**< \brief Flag indicating whether table is a Critical Table */ + bool TableLoadedOnce; /**< \brief Flag indicating whether table has been loaded once or not */ + bool LoadPending; /**< \brief Flag indicating an inactive buffer is ready to be copied */ + bool DumpOnly; /**< \brief Flag indicating Table is NOT to be loaded */ + bool DoubleBuffered; /**< \brief Flag indicating Table has a dedicated inactive buffer */ + bool UserDefAddr; /**< \brief Flag indicating Table address was defined by Owner Application */ + bool NotifyByMsg; /**< \brief Flag indicating Table Services should notify owning App via message + when table requires management */ + uint8 ActiveBufferIndex; /**< \brief Index identifying which buffer is the active buffer */ char Name[CFE_TBL_MAX_FULL_NAME_LEN]; /**< \brief Processor specific table name */ char LastFileLoaded[OS_MAX_PATH_LEN]; /**< \brief Filename of last file loaded into table */ } CFE_TBL_RegistryRec_t; @@ -349,6 +363,9 @@ typedef struct * Registry dump state info (background job) */ CFE_TBL_RegDumpStateInfo_t RegDumpState; + + CFE_ResourceId_t LastValidationResultId; + } CFE_TBL_Global_t; /*************************************************************************/ diff --git a/modules/tbl/fsw/src/cfe_tbl_task_cmds.c b/modules/tbl/fsw/src/cfe_tbl_task_cmds.c index afb4f5536..615676308 100644 --- a/modules/tbl/fsw/src/cfe_tbl_task_cmds.c +++ b/modules/tbl/fsw/src/cfe_tbl_task_cmds.c @@ -179,16 +179,21 @@ void CFE_TBL_GetHkData(void) /* Locate a completed, but unreported, validation request */ i = 0; - while ((i < CFE_PLATFORM_TBL_MAX_NUM_VALIDATIONS) && (ValPtr == NULL)) + while (true) { - if (CFE_TBL_Global.ValidationResults[i].State == CFE_TBL_VALIDATION_PERFORMED) + if (i >= CFE_PLATFORM_TBL_MAX_NUM_VALIDATIONS) { - ValPtr = &CFE_TBL_Global.ValidationResults[i]; + ValPtr = NULL; + break; } - else + + ValPtr = &CFE_TBL_Global.ValidationResults[i]; + if (CFE_TBL_ValidationResultIsUsed(ValPtr) && ValPtr->State == CFE_TBL_VALIDATION_PERFORMED) { - i++; + break; } + + ++i; } if (ValPtr != NULL) @@ -216,7 +221,8 @@ void CFE_TBL_GetHkData(void) ValPtr->CrcOfTable = 0; ValPtr->TableName[0] = '\0'; ValPtr->ActiveBuffer = false; - ValPtr->State = CFE_TBL_VALIDATION_FREE; + + CFE_TBL_ValidationResultSetFree(ValPtr); } CFE_TBL_Global.HkPacket.Payload.ValidationCounter = CFE_TBL_Global.ValidationCounter; @@ -798,82 +804,60 @@ CFE_TBL_CmdProcRet_t CFE_TBL_DumpToFile(const char *DumpFilename, const char *Ta int32 CFE_TBL_ValidateCmd(const CFE_TBL_ValidateCmd_t *data) { CFE_TBL_CmdProcRet_t ReturnCode = CFE_TBL_INC_ERR_CTR; /* Assume failure */ - int16 RegIndex; + CFE_TBL_TxnState_t Txn; + CFE_Status_t Status; const CFE_TBL_ValidateCmd_Payload_t *CmdPtr = &data->Payload; CFE_TBL_RegistryRec_t * RegRecPtr; - void * ValidationDataPtr = NULL; + CFE_TBL_LoadBuff_t * SelectedBufferPtr; char TableName[CFE_TBL_MAX_FULL_NAME_LEN]; uint32 CrcOfTable; - int32 ValIndex; + CFE_ResourceId_t PendingValId; + CFE_TBL_ValidationResult_t * ValResultPtr; + + SelectedBufferPtr = NULL; /* Make sure all strings are null terminated before attempting to process them */ CFE_SB_MessageStringGet(TableName, (char *)CmdPtr->TableName, NULL, sizeof(TableName), sizeof(CmdPtr->TableName)); /* Before doing anything, lets make sure the table that is to be dumped exists */ - RegIndex = CFE_TBL_FindTableInRegistry(TableName); - - if (RegIndex != CFE_TBL_NOT_FOUND) + Status = CFE_TBL_TxnStartFromName(&Txn, TableName, CFE_TBL_TxnContext_UNDEFINED); + if (Status == CFE_SUCCESS) { /* Obtain a pointer to registry information about specified table */ - RegRecPtr = &CFE_TBL_Global.Registry[RegIndex]; + RegRecPtr = CFE_TBL_TxnRegRec(&Txn); + CFE_TBL_TxnFinish(&Txn); /* Determine what data is to be validated */ - if (CmdPtr->ActiveTableFlag == CFE_TBL_BufferSelect_ACTIVE) - { - ValidationDataPtr = RegRecPtr->Buffers[RegRecPtr->ActiveBufferIndex].BufferPtr; - } - else if (CmdPtr->ActiveTableFlag == CFE_TBL_BufferSelect_INACTIVE) /* Validating Inactive Buffer */ + SelectedBufferPtr = CFE_TBL_GetSelectedBuffer(RegRecPtr, CmdPtr->ActiveTableFlag); + + if (SelectedBufferPtr == NULL) { - /* If this is a double buffered table, locating the inactive buffer is trivial */ - if (RegRecPtr->DoubleBuffered) - { - ValidationDataPtr = RegRecPtr->Buffers[(1U - RegRecPtr->ActiveBufferIndex)].BufferPtr; - } - else - { - /* For single buffered tables, the index to the inactive buffer is kept in 'LoadInProgress' */ - if (RegRecPtr->LoadInProgress != CFE_TBL_NO_LOAD_IN_PROGRESS) - { - ValidationDataPtr = CFE_TBL_Global.LoadBuffs[RegRecPtr->LoadInProgress].BufferPtr; - } - else - { - CFE_EVS_SendEvent(CFE_TBL_NO_INACTIVE_BUFFER_ERR_EID, CFE_EVS_EventType_ERROR, - "No Inactive Buffer for Table '%s' present", TableName); - } - } + CFE_EVS_SendEvent(CFE_TBL_NO_INACTIVE_BUFFER_ERR_EID, CFE_EVS_EventType_ERROR, + "No Buffer for Table '%s' present", TableName); } else { - CFE_EVS_SendEvent(CFE_TBL_ILLEGAL_BUFF_PARAM_ERR_EID, CFE_EVS_EventType_ERROR, - "Cmd for Table '%s' had illegal buffer parameter (0x%08X)", TableName, - (unsigned int)CmdPtr->ActiveTableFlag); - } + /* If we have located the data to be validated, then proceed with notifying the application, if */ + /* necessary, and computing the CRC value for the block of memory */ - /* If we have located the data to be validated, then proceed with notifying the application, if */ - /* necessary, and computing the CRC value for the block of memory */ - if (ValidationDataPtr != NULL) - { /* Find a free Validation Response Block */ - ValIndex = 0; - while ((ValIndex < CFE_PLATFORM_TBL_MAX_NUM_VALIDATIONS) && - (CFE_TBL_Global.ValidationResults[ValIndex].State != CFE_TBL_VALIDATION_FREE)) - { - ValIndex++; - } - - if (ValIndex < CFE_PLATFORM_TBL_MAX_NUM_VALIDATIONS) + PendingValId = CFE_TBL_GetNextValResultBlock(); + ValResultPtr = CFE_TBL_LocateValidationResultByID(CFE_TBL_VALRESULTID_C(PendingValId)); + if (ValResultPtr != NULL) { /* Allocate this Validation Response Block */ - CFE_TBL_Global.ValidationResults[ValIndex].State = CFE_TBL_VALIDATION_PENDING; - CFE_TBL_Global.ValidationResults[ValIndex].Result = 0; - memcpy(CFE_TBL_Global.ValidationResults[ValIndex].TableName, TableName, CFE_TBL_MAX_FULL_NAME_LEN); + ValResultPtr->State = CFE_TBL_VALIDATION_PENDING; + ValResultPtr->Result = 0; + memcpy(ValResultPtr->TableName, TableName, CFE_TBL_MAX_FULL_NAME_LEN); /* Compute the CRC on the specified table buffer */ - CrcOfTable = CFE_ES_CalculateCRC(ValidationDataPtr, RegRecPtr->Size, 0, CFE_MISSION_ES_DEFAULT_CRC); + CrcOfTable = + CFE_ES_CalculateCRC(SelectedBufferPtr->BufferPtr, RegRecPtr->Size, 0, CFE_MISSION_ES_DEFAULT_CRC); + + ValResultPtr->CrcOfTable = CrcOfTable; + ValResultPtr->ActiveBuffer = (CmdPtr->ActiveTableFlag != 0); - CFE_TBL_Global.ValidationResults[ValIndex].CrcOfTable = CrcOfTable; - CFE_TBL_Global.ValidationResults[ValIndex].ActiveBuffer = (CmdPtr->ActiveTableFlag != 0); + CFE_TBL_ValidationResultSetUsed(ValResultPtr, PendingValId); /* If owner has a validation function, then notify the */ /* table owner that there is data to be validated */ @@ -881,11 +865,11 @@ int32 CFE_TBL_ValidateCmd(const CFE_TBL_ValidateCmd_t *data) { if (CmdPtr->ActiveTableFlag) { - RegRecPtr->ValidateActiveIndex = ValIndex; + RegRecPtr->ValidateActiveId = CFE_TBL_ValidationResultGetId(ValResultPtr); } else { - RegRecPtr->ValidateInactiveIndex = ValIndex; + RegRecPtr->ValidateInactiveId = CFE_TBL_ValidationResultGetId(ValResultPtr); } /* If application requested notification by message, then do so */ @@ -904,7 +888,7 @@ int32 CFE_TBL_ValidateCmd(const CFE_TBL_ValidateCmd_t *data) /* If there isn't a validation function pointer, then the process is complete */ /* By setting this value, we are letting the Housekeeping process recognize it */ /* as data to be sent to the ground in telemetry. */ - CFE_TBL_Global.ValidationResults[ValIndex].State = CFE_TBL_VALIDATION_PERFORMED; + ValResultPtr->State = CFE_TBL_VALIDATION_PERFORMED; CFE_EVS_SendEvent(CFE_TBL_ASSUMED_VALID_INF_EID, CFE_EVS_EventType_INFORMATION, "Tbl Services assumes '%s' is valid. No Validation Function has been registered", diff --git a/modules/tbl/fsw/src/cfe_tbl_transaction.c b/modules/tbl/fsw/src/cfe_tbl_transaction.c index 68dcc92c5..479ac1e27 100644 --- a/modules/tbl/fsw/src/cfe_tbl_transaction.c +++ b/modules/tbl/fsw/src/cfe_tbl_transaction.c @@ -284,8 +284,8 @@ CFE_Status_t CFE_TBL_TxnGetTableStatus(CFE_TBL_TxnState_t *Txn) { Status = CFE_TBL_INFO_UPDATE_PENDING; } - else if ((RegRecPtr->ValidateActiveIndex != CFE_TBL_NO_VALIDATION_PENDING) || - (RegRecPtr->ValidateInactiveIndex != CFE_TBL_NO_VALIDATION_PENDING)) + else if (CFE_RESOURCEID_TEST_DEFINED(RegRecPtr->ValidateActiveId) || + CFE_RESOURCEID_TEST_DEFINED(RegRecPtr->ValidateInactiveId)) { Status = CFE_TBL_INFO_VALIDATION_PENDING; } diff --git a/modules/tbl/fsw/src/cfe_tbl_transaction.h b/modules/tbl/fsw/src/cfe_tbl_transaction.h index da61d1fe4..0d39bdeb5 100644 --- a/modules/tbl/fsw/src/cfe_tbl_transaction.h +++ b/modules/tbl/fsw/src/cfe_tbl_transaction.h @@ -32,7 +32,7 @@ /* * Required header files... -*/ + */ #include "cfe_es_api_typedefs.h" #include "cfe_tbl_api_typedefs.h" #include "cfe_platform_cfg.h" @@ -122,6 +122,11 @@ static inline CFE_TBL_Handle_t CFE_TBL_TxnHandle(const CFE_TBL_TxnState_t *Txn) return Txn->Handle; } +static inline unsigned long CFE_TBL_TxnHandleAsULong(const CFE_TBL_TxnState_t *Txn) +{ + return (unsigned long)CFE_TBL_TxnHandle(Txn); +} + /** * Gets the access descriptor object */ @@ -138,6 +143,11 @@ static inline CFE_TBL_RegId_t CFE_TBL_TxnRegId(const CFE_TBL_TxnState_t *Txn) return Txn->RegId; } +static inline unsigned long CFE_TBL_TxnRegIdAsULong(const CFE_TBL_TxnState_t *Txn) +{ + return (unsigned long)CFE_TBL_TxnRegId(Txn); +} + /** * Gets the registry record object */ @@ -156,6 +166,11 @@ static inline CFE_ES_AppId_t CFE_TBL_TxnAppId(const CFE_TBL_TxnState_t *Txn) return Txn->AppId; } +static inline unsigned long CFE_TBL_TxnAppIdAsULong(const CFE_TBL_TxnState_t *Txn) +{ + return CFE_RESOURCEID_TO_ULONG(CFE_TBL_TxnAppId(Txn)); +} + /***************************** Function Prototypes **********************************/ /*---------------------------------------------------------------------------------------*/ diff --git a/modules/tbl/ut-coverage/tbl_UT.c b/modules/tbl/ut-coverage/tbl_UT.c index b1358578f..3affe155d 100644 --- a/modules/tbl/ut-coverage/tbl_UT.c +++ b/modules/tbl/ut-coverage/tbl_UT.c @@ -113,6 +113,52 @@ void UT_TBL_SetupHeader(CFE_TBL_File_Hdr_t *TblFileHeader, size_t Offset, size_t } } +/* Sets up the indicated validation request/result buffer as VALIDATION_PENDING */ +void UT_TBL_SetupPendingValidation(uint32 ArrayIndex, bool UseActive, CFE_TBL_RegistryRec_t *RegRecPtr, + CFE_TBL_ValidationResult_t **ValResultOut) +{ + CFE_TBL_ValidationResult_t *ValResultPtr; + CFE_ResourceId_t PendingId; + + ValResultPtr = &CFE_TBL_Global.ValidationResults[ArrayIndex]; + PendingId = CFE_ResourceId_FromInteger(CFE_TBL_VALRESULTID_BASE + ArrayIndex); + + memset(ValResultPtr, 0, sizeof(*ValResultPtr)); + + ValResultPtr->State = CFE_TBL_VALIDATION_PENDING; + + ValResultPtr->ValId = CFE_TBL_VALRESULTID_C(PendingId); + ValResultPtr->ActiveBuffer = UseActive; + + snprintf(ValResultPtr->TableName, sizeof(ValResultPtr->TableName), "ut_cfe_tbl.UT_Table%u", + (unsigned int)ArrayIndex + 1); + + if (RegRecPtr != NULL) + { + if (UseActive) + { + RegRecPtr->ValidateActiveId = ValResultPtr->ValId; + } + else + { + RegRecPtr->ValidateInactiveId = ValResultPtr->ValId; + } + } + + if (ValResultOut != NULL) + { + *ValResultOut = ValResultPtr; + } +} + +/* Resets the indicated validation request/result buffer to the free/unused state */ +void UT_TBL_ResetValidationState(uint32 ArrayIndex) +{ + CFE_TBL_ValidationResult_t *ValResultPtr; + ValResultPtr = &CFE_TBL_Global.ValidationResults[ArrayIndex]; + memset(ValResultPtr, 0, sizeof(*ValResultPtr)); +} + /* ** Functions */ @@ -128,6 +174,14 @@ void UtTest_Setup(void) UT_ADD_TEST(Test_CFE_TBL_InitData); UT_ADD_TEST(Test_CFE_TBL_SearchCmdHndlrTbl); + /* + * Shared resource access patterns + * (do this early because many other APIs depend on these working correctly) + */ + UT_ADD_TEST(Test_CFE_TBL_ResourceID_ValidationResult); + UT_ADD_TEST(Test_CFE_TBL_ResourceID_RegistryRecord); + UT_ADD_TEST(Test_CFE_TBL_ResourceID_AccessDescriptor); + /* cfe_tbl_task_cmds.c functions */ /* This should be done first (it initializes working data structures) */ UT_ADD_TEST(Test_CFE_TBL_DeleteCDSCmd); @@ -209,11 +263,7 @@ void UT_ResetTableRegistry(void) /* Initialize the table validation results records */ for (i = 0; i < CFE_PLATFORM_TBL_MAX_NUM_VALIDATIONS; i++) { - CFE_TBL_Global.ValidationResults[i].State = CFE_TBL_VALIDATION_FREE; - CFE_TBL_Global.ValidationResults[i].CrcOfTable = 0; - CFE_TBL_Global.ValidationResults[i].Result = 0; - CFE_TBL_Global.ValidationResults[i].ActiveBuffer = false; - CFE_TBL_Global.ValidationResults[i].TableName[0] = '\0'; + UT_TBL_ResetValidationState(i); } /* Initialize the dump-only table dump control blocks */ @@ -673,7 +723,6 @@ void Test_CFE_TBL_ResetCmd(void) */ void Test_CFE_TBL_ValidateCmd(void) { - int i; uint8 Buff; uint8 * BuffPtr = &Buff; CFE_TBL_ValidateCmd_t ValidateCmd; @@ -696,18 +745,14 @@ void Test_CFE_TBL_ValidateCmd(void) ValidateCmd.Payload.ActiveTableFlag = CFE_TBL_BufferSelect_ACTIVE; CFE_TBL_Global.Registry[0].Buffers[CFE_TBL_Global.Registry[0].ActiveBufferIndex].BufferPtr = BuffPtr; - for (i = 0; i < CFE_PLATFORM_TBL_MAX_NUM_VALIDATIONS; i++) - { - CFE_TBL_Global.ValidationResults[i].State = CFE_TBL_VALIDATION_PENDING; - } - + UT_SetDeferredRetcode(UT_KEY(CFE_ResourceId_FindNext), 1, 0); UtAssert_INT32_EQ(CFE_TBL_ValidateCmd(&ValidateCmd), CFE_TBL_INC_ERR_CTR); /* Test where the active buffer has data, but there is no validation * function pointer */ UT_InitData(); - CFE_TBL_Global.ValidationResults[0].State = CFE_TBL_VALIDATION_FREE; + UT_TBL_ResetValidationState(0); CFE_TBL_Global.Registry[0].ValidationFuncPtr = NULL; UtAssert_INT32_EQ(CFE_TBL_ValidateCmd(&ValidateCmd), CFE_TBL_INC_CMD_CTR); @@ -715,7 +760,7 @@ void Test_CFE_TBL_ValidateCmd(void) * exists, and the active table flag is set */ UT_InitData(); - CFE_TBL_Global.ValidationResults[0].State = CFE_TBL_VALIDATION_FREE; + UT_TBL_ResetValidationState(0); CFE_TBL_Global.Registry[0].ValidationFuncPtr = ValFuncPtr; ValidateCmd.Payload.ActiveTableFlag = true; UtAssert_INT32_EQ(CFE_TBL_ValidateCmd(&ValidateCmd), CFE_TBL_INC_CMD_CTR); @@ -724,11 +769,11 @@ void Test_CFE_TBL_ValidateCmd(void) * validation function pointer exists */ UT_InitData(); + UT_TBL_ResetValidationState(0); ValidateCmd.Payload.ActiveTableFlag = CFE_TBL_BufferSelect_INACTIVE; CFE_TBL_Global.Registry[0].DoubleBuffered = true; CFE_TBL_Global.Registry[0].Buffers[1 - CFE_TBL_Global.Registry[0].ActiveBufferIndex].BufferPtr = BuffPtr; - CFE_TBL_Global.ValidationResults[0].State = CFE_TBL_VALIDATION_FREE; - CFE_TBL_Global.Registry[0].ValidationFuncPtr = ValFuncPtr; + CFE_TBL_Global.Registry[0].ValidationFuncPtr = ValFuncPtr; UtAssert_INT32_EQ(CFE_TBL_ValidateCmd(&ValidateCmd), CFE_TBL_INC_CMD_CTR); /* Test with the buffer inactive, the table is single-buffered with a @@ -736,10 +781,10 @@ void Test_CFE_TBL_ValidateCmd(void) * notification message should be sent */ UT_InitData(); + UT_TBL_ResetValidationState(0); CFE_TBL_Global.Registry[0].NotifyByMsg = false; CFE_TBL_Global.Registry[0].DoubleBuffered = false; CFE_TBL_Global.LoadBuffs[CFE_TBL_Global.Registry[0].LoadInProgress].BufferPtr = BuffPtr; - CFE_TBL_Global.ValidationResults[0].State = CFE_TBL_VALIDATION_FREE; CFE_TBL_Global.Registry[0].LoadInProgress = CFE_TBL_NO_LOAD_IN_PROGRESS + 1; UtAssert_INT32_EQ(CFE_TBL_ValidateCmd(&ValidateCmd), CFE_TBL_INC_CMD_CTR); @@ -749,10 +794,10 @@ void Test_CFE_TBL_ValidateCmd(void) */ UT_InitData(); UT_SetDeferredRetcode(UT_KEY(CFE_SB_TransmitMsg), 1, CFE_SB_INTERNAL_ERR); + UT_TBL_ResetValidationState(0); CFE_TBL_Global.Registry[0].NotifyByMsg = true; CFE_TBL_Global.Registry[0].DoubleBuffered = false; CFE_TBL_Global.LoadBuffs[CFE_TBL_Global.Registry[0].LoadInProgress].BufferPtr = BuffPtr; - CFE_TBL_Global.ValidationResults[0].State = CFE_TBL_VALIDATION_FREE; CFE_TBL_Global.Registry[0].LoadInProgress = CFE_TBL_NO_LOAD_IN_PROGRESS + 1; UtAssert_INT32_EQ(CFE_TBL_ValidateCmd(&ValidateCmd), CFE_TBL_INC_CMD_CTR); @@ -818,11 +863,12 @@ void Test_CFE_TBL_GetTblRegData(void) */ void Test_CFE_TBL_GetHkData(void) { - int i; - int32 NumLoadPendingIndex = CFE_PLATFORM_TBL_MAX_NUM_TABLES - 1; - int32 FreeSharedBuffIndex = CFE_PLATFORM_TBL_MAX_SIMULTANEOUS_LOADS - 1; - int32 ValTableIndex = CFE_PLATFORM_TBL_MAX_NUM_VALIDATIONS - 1; - CFE_ES_AppId_t AppID; + int i; + int32 NumLoadPendingIndex = CFE_PLATFORM_TBL_MAX_NUM_TABLES - 1; + int32 FreeSharedBuffIndex = CFE_PLATFORM_TBL_MAX_SIMULTANEOUS_LOADS - 1; + int32 ValTableIndex = CFE_PLATFORM_TBL_MAX_NUM_VALIDATIONS - 1; + CFE_ES_AppId_t AppID; + CFE_TBL_ValidationResult_t *ValResultPtr; /* Get the AppID being used for UT */ CFE_ES_GetAppID(&AppID); @@ -849,17 +895,18 @@ void Test_CFE_TBL_GetHkData(void) /* Test making a ValPtr with result = CFE_SUCCESS */ UT_InitData(); - CFE_TBL_Global.SuccessValCounter = 0; - CFE_TBL_Global.ValidationResults[ValTableIndex].State = CFE_TBL_VALIDATION_PERFORMED; - CFE_TBL_Global.ValidationResults[ValTableIndex].Result = CFE_SUCCESS; + CFE_TBL_Global.SuccessValCounter = 0; + UT_TBL_SetupPendingValidation(ValTableIndex, false, NULL, &ValResultPtr); + ValResultPtr->State = CFE_TBL_VALIDATION_PERFORMED; CFE_TBL_GetHkData(); UtAssert_UINT32_EQ(CFE_TBL_Global.SuccessValCounter, 1); /* Test making a ValPtr without result = CFE_SUCCESS */ UT_InitData(); - CFE_TBL_Global.FailedValCounter = 0; - CFE_TBL_Global.ValidationResults[ValTableIndex].State = CFE_TBL_VALIDATION_PERFORMED; - CFE_TBL_Global.ValidationResults[ValTableIndex].Result = CFE_SUCCESS - 1; + CFE_TBL_Global.FailedValCounter = 0; + UT_TBL_SetupPendingValidation(ValTableIndex, false, NULL, &ValResultPtr); + ValResultPtr->State = CFE_TBL_VALIDATION_PERFORMED; + ValResultPtr->Result = CFE_SUCCESS - 1; CFE_TBL_GetHkData(); UtAssert_UINT32_EQ(CFE_TBL_Global.FailedValCounter, 1); @@ -2469,14 +2516,19 @@ void Test_CFE_TBL_ReleaseAddresses(void) */ void Test_CFE_TBL_Validate(void) { - int16 RegIndex; - CFE_TBL_RegistryRec_t *RegRecPtr; + int16 RegIndex; + CFE_TBL_RegistryRec_t * RegRecPtr; + CFE_TBL_ValidationResult_t *ValResultPtr; + UtPrintf("Begin Test Validate"); /* Test setup */ RegIndex = CFE_TBL_FindTableInRegistry("ut_cfe_tbl.UT_Table1"); RegRecPtr = &CFE_TBL_Global.Registry[RegIndex]; + /* Refer to the test validation function */ + RegRecPtr->ValidationFuncPtr = Test_CFE_TBL_ValidationFunc; + /* Test response to attempt to validate a table that an application is * not allowed to see */ @@ -2497,39 +2549,61 @@ void Test_CFE_TBL_Validate(void) UT_InitData(); /* a. Configure table for validation */ - CFE_TBL_Global.ValidationResults[0].State = CFE_TBL_VALIDATION_PENDING; - CFE_TBL_Global.ValidationResults[0].Result = 0; - strncpy(CFE_TBL_Global.ValidationResults[0].TableName, "ut_cfe_tbl.UT_Table1", - sizeof(CFE_TBL_Global.ValidationResults[0].TableName) - 1); - CFE_TBL_Global.ValidationResults[0].TableName[sizeof(CFE_TBL_Global.ValidationResults[0].TableName) - 1] = '\0'; - CFE_TBL_Global.ValidationResults[0].CrcOfTable = 0; - CFE_TBL_Global.ValidationResults[0].ActiveBuffer = false; - RegRecPtr->ValidateInactiveIndex = 0; + UT_TBL_SetupPendingValidation(0, false, RegRecPtr, &ValResultPtr); + + RegRecPtr->LoadInProgress = 0; /* b. Perform failed validation */ UT_SetDeferredRetcode(UT_KEY(Test_CFE_TBL_ValidationFunc), 1, -1); CFE_UtAssert_SUCCESS(CFE_TBL_Validate(App1TblHandle1)); CFE_UtAssert_EVENTSENT(CFE_TBL_VALIDATION_ERR_EID); CFE_UtAssert_EVENTCOUNT(1); - UtAssert_INT32_EQ(CFE_TBL_Global.ValidationResults[0].Result, -1); + UtAssert_INT32_EQ(ValResultPtr->Result, -1); + + /* Test validation on table w/user-defined address (this is not a valid combo) */ + UT_InitData(); + + /* a. Configure table for validation and set UserDefAddr flag */ + UT_TBL_SetupPendingValidation(0, false, RegRecPtr, &ValResultPtr); + RegRecPtr->UserDefAddr = true; + + /* b. Perform validation */ + CFE_UtAssert_SUCCESS(CFE_TBL_Validate(App1TblHandle1)); + CFE_UtAssert_EVENTSENT(CFE_TBL_VALIDATION_ERR_EID); + CFE_UtAssert_EVENTCOUNT(1); + UtAssert_INT32_EQ(ValResultPtr->Result, -1); + RegRecPtr->UserDefAddr = false; + + /* Test case where validation request is stale */ + UT_InitData(); + + /* a. Configure table for validation and modify the ID so it will not match */ + UT_TBL_SetupPendingValidation(0, false, RegRecPtr, &ValResultPtr); + ValResultPtr->ValId = CFE_TBL_VALRESULTID_C(CFE_ResourceId_FromInteger(1)); + + /* b. Perform validation */ + UtAssert_INT32_EQ(CFE_TBL_Validate(App1TblHandle1), CFE_TBL_INFO_NO_VALIDATION_PENDING); + UtAssert_INT32_EQ(ValResultPtr->Result, CFE_SUCCESS); /* Test successful validation */ UT_InitData(); /* a. Configure table for validation */ - CFE_TBL_Global.ValidationResults[0].State = CFE_TBL_VALIDATION_PENDING; - CFE_TBL_Global.ValidationResults[0].Result = 0; - strncpy(CFE_TBL_Global.ValidationResults[0].TableName, "ut_cfe_tbl.UT_Table1", - sizeof(CFE_TBL_Global.ValidationResults[0].TableName) - 1); - CFE_TBL_Global.ValidationResults[0].TableName[sizeof(CFE_TBL_Global.ValidationResults[0].TableName) - 1] = '\0'; - CFE_TBL_Global.ValidationResults[0].CrcOfTable = 0; - CFE_TBL_Global.ValidationResults[0].ActiveBuffer = false; - RegRecPtr->ValidateInactiveIndex = 0; + UT_TBL_SetupPendingValidation(0, false, RegRecPtr, &ValResultPtr); - /* b. Perform failed validation */ + /* b. Perform validation */ UT_SetDeferredRetcode(UT_KEY(Test_CFE_TBL_ValidationFunc), 1, CFE_SUCCESS); CFE_UtAssert_SUCCESS(CFE_TBL_Validate(App1TblHandle1)); - UtAssert_INT32_EQ(CFE_TBL_Global.ValidationResults[0].Result, CFE_SUCCESS); + UtAssert_INT32_EQ(ValResultPtr->Result, CFE_SUCCESS); + + /* Set up a case where the entry does not have a validation function ptr */ + UT_TBL_SetupPendingValidation(0, false, RegRecPtr, &ValResultPtr); + ValResultPtr->Result = -1; + RegRecPtr->ValidationFuncPtr = NULL; + + /* b. Perform failed validation */ + CFE_UtAssert_SUCCESS(CFE_TBL_Validate(App1TblHandle1)); + UtAssert_INT32_EQ(ValResultPtr->Result, CFE_SUCCESS); } /* @@ -2545,6 +2619,7 @@ void Test_CFE_TBL_Manage(void) void * App2TblPtr; CFE_TBL_AccessDescriptor_t *AccessDescPtr; CFE_TBL_Handle_t AccessIterator; + CFE_TBL_ValidationResult_t *ValResultPtr; memset(&TestTable1, 0, sizeof(TestTable1)); @@ -2561,6 +2636,10 @@ void Test_CFE_TBL_Manage(void) /* "Load" image into inactive buffer for table */ RegIndex = CFE_TBL_FindTableInRegistry("ut_cfe_tbl.UT_Table1"); RegRecPtr = &CFE_TBL_Global.Registry[RegIndex]; + + /* Refer to the test validation function */ + RegRecPtr->ValidationFuncPtr = Test_CFE_TBL_ValidationFunc; + CFE_UtAssert_SUCCESS(CFE_TBL_GetWorkingBuffer(&WorkingBufferPtr, RegRecPtr, false)); UT_SetAppID(UT_TBL_APPID_1); UtAssert_INT32_EQ(CFE_TBL_Load(App1TblHandle1, CFE_TBL_SRC_ADDRESS, &TestTable1), CFE_TBL_ERR_LOAD_IN_PROGRESS); @@ -2573,21 +2652,14 @@ void Test_CFE_TBL_Manage(void) UT_InitData(); /* Configure table for validation */ - CFE_TBL_Global.ValidationResults[0].State = CFE_TBL_VALIDATION_PENDING; - CFE_TBL_Global.ValidationResults[0].Result = 0; - strncpy(CFE_TBL_Global.ValidationResults[0].TableName, "ut_cfe_tbl.UT_Table1", - sizeof(CFE_TBL_Global.ValidationResults[0].TableName) - 1); - CFE_TBL_Global.ValidationResults[0].TableName[sizeof(CFE_TBL_Global.ValidationResults[0].TableName) - 1] = '\0'; - CFE_TBL_Global.ValidationResults[0].CrcOfTable = 0; - CFE_TBL_Global.ValidationResults[0].ActiveBuffer = false; - RegRecPtr->ValidateInactiveIndex = 0; + UT_TBL_SetupPendingValidation(0, false, RegRecPtr, &ValResultPtr); /* Perform validation via manage call */ UT_SetDeferredRetcode(UT_KEY(Test_CFE_TBL_ValidationFunc), 1, -1); CFE_UtAssert_SUCCESS(CFE_TBL_Manage(App1TblHandle1)); CFE_UtAssert_EVENTSENT(CFE_TBL_VALIDATION_ERR_EID); CFE_UtAssert_EVENTCOUNT(1); - UtAssert_INT32_EQ(CFE_TBL_Global.ValidationResults[0].Result, -1); + UtAssert_INT32_EQ(ValResultPtr->Result, -1); /* Test response to processing an unsuccessful validation request on * inactive buffer ; validation function return code is invalid @@ -2595,21 +2667,14 @@ void Test_CFE_TBL_Manage(void) UT_InitData(); /* Configure table for validation */ - CFE_TBL_Global.ValidationResults[0].State = CFE_TBL_VALIDATION_PENDING; - CFE_TBL_Global.ValidationResults[0].Result = 0; - strncpy(CFE_TBL_Global.ValidationResults[0].TableName, "ut_cfe_tbl.UT_Table1", - sizeof(CFE_TBL_Global.ValidationResults[0].TableName) - 1); - CFE_TBL_Global.ValidationResults[0].TableName[sizeof(CFE_TBL_Global.ValidationResults[0].TableName) - 1] = '\0'; - CFE_TBL_Global.ValidationResults[0].CrcOfTable = 0; - CFE_TBL_Global.ValidationResults[0].ActiveBuffer = false; - RegRecPtr->ValidateInactiveIndex = 0; + UT_TBL_SetupPendingValidation(0, false, RegRecPtr, &ValResultPtr); /* Perform validation via manage call */ UT_SetDeferredRetcode(UT_KEY(Test_CFE_TBL_ValidationFunc), 1, 1); CFE_UtAssert_SUCCESS(CFE_TBL_Manage(App1TblHandle1)); CFE_UtAssert_EVENTSENT(CFE_TBL_VALIDATION_ERR_EID); CFE_UtAssert_EVENTCOUNT(1); - UtAssert_INT32_EQ(CFE_TBL_Global.ValidationResults[0].Result, 1); + UtAssert_INT32_EQ(ValResultPtr->Result, 1); /* Test response to processing an unsuccessful validation request; * CFE_TBL_Validate does not return CFE_SUCCESS @@ -2617,20 +2682,13 @@ void Test_CFE_TBL_Manage(void) UT_InitData(); /* Configure table for validation */ - CFE_TBL_Global.ValidationResults[0].State = CFE_TBL_VALIDATION_PENDING; - CFE_TBL_Global.ValidationResults[0].Result = 0; - strncpy(CFE_TBL_Global.ValidationResults[0].TableName, "ut_cfe_tbl.UT_Table1", - sizeof(CFE_TBL_Global.ValidationResults[0].TableName) - 1); - CFE_TBL_Global.ValidationResults[0].TableName[sizeof(CFE_TBL_Global.ValidationResults[0].TableName) - 1] = '\0'; - CFE_TBL_Global.ValidationResults[0].CrcOfTable = 0; - CFE_TBL_Global.ValidationResults[0].ActiveBuffer = false; - RegRecPtr->ValidateInactiveIndex = 0; + UT_TBL_SetupPendingValidation(0, false, RegRecPtr, &ValResultPtr); /* Perform validation via manage call */ UT_SetDeferredRetcode(UT_KEY(CFE_ES_GetAppID), 2, CFE_ES_ERR_RESOURCEID_NOT_VALID); UtAssert_INT32_EQ(CFE_TBL_Manage(App1TblHandle1), CFE_ES_ERR_RESOURCEID_NOT_VALID); CFE_UtAssert_EVENTCOUNT(0); - UtAssert_INT32_EQ(CFE_TBL_Global.ValidationResults[0].Result, 0); + UtAssert_INT32_EQ(ValResultPtr->Result, 0); /* Test response to processing a successful validation request on an * inactive buffer @@ -2638,21 +2696,15 @@ void Test_CFE_TBL_Manage(void) UT_InitData(); /* Configure table for validation */ - CFE_TBL_Global.ValidationResults[0].State = CFE_TBL_VALIDATION_PENDING; - CFE_TBL_Global.ValidationResults[0].Result = 1; - strncpy(CFE_TBL_Global.ValidationResults[0].TableName, "ut_cfe_tbl.UT_Table1", - sizeof(CFE_TBL_Global.ValidationResults[0].TableName) - 1); - CFE_TBL_Global.ValidationResults[0].TableName[sizeof(CFE_TBL_Global.ValidationResults[0].TableName) - 1] = '\0'; - CFE_TBL_Global.ValidationResults[0].CrcOfTable = 0; - CFE_TBL_Global.ValidationResults[0].ActiveBuffer = false; - RegRecPtr->ValidateInactiveIndex = 0; + UT_TBL_SetupPendingValidation(0, false, RegRecPtr, &ValResultPtr); + ValResultPtr->Result = 1; /* Perform validation via manage call */ UT_SetDeferredRetcode(UT_KEY(Test_CFE_TBL_ValidationFunc), 1, CFE_SUCCESS); CFE_UtAssert_SUCCESS(CFE_TBL_Manage(App1TblHandle1)); CFE_UtAssert_EVENTSENT(CFE_TBL_VALIDATION_INF_EID); CFE_UtAssert_EVENTCOUNT(1); - UtAssert_INT32_EQ(CFE_TBL_Global.ValidationResults[0].Result, 0); + UtAssert_INT32_EQ(ValResultPtr->Result, 0); /* Test response to processing an unsuccessful validation request on an * active buffer @@ -2660,21 +2712,14 @@ void Test_CFE_TBL_Manage(void) UT_InitData(); /* Configure table for validation */ - CFE_TBL_Global.ValidationResults[0].State = CFE_TBL_VALIDATION_PENDING; - CFE_TBL_Global.ValidationResults[0].Result = 0; - strncpy(CFE_TBL_Global.ValidationResults[0].TableName, "ut_cfe_tbl.UT_Table1", - sizeof(CFE_TBL_Global.ValidationResults[0].TableName) - 1); - CFE_TBL_Global.ValidationResults[0].TableName[sizeof(CFE_TBL_Global.ValidationResults[0].TableName) - 1] = '\0'; - CFE_TBL_Global.ValidationResults[0].CrcOfTable = 0; - CFE_TBL_Global.ValidationResults[0].ActiveBuffer = true; - RegRecPtr->ValidateActiveIndex = 0; + UT_TBL_SetupPendingValidation(0, true, RegRecPtr, &ValResultPtr); /* Perform validation via manage call */ UT_SetDeferredRetcode(UT_KEY(Test_CFE_TBL_ValidationFunc), 1, -1); CFE_UtAssert_SUCCESS(CFE_TBL_Manage(App1TblHandle1)); CFE_UtAssert_EVENTSENT(CFE_TBL_VALIDATION_ERR_EID); CFE_UtAssert_EVENTCOUNT(1); - UtAssert_INT32_EQ(CFE_TBL_Global.ValidationResults[0].Result, -1); + UtAssert_INT32_EQ(ValResultPtr->Result, -1); /* Test response to processing an unsuccessful validation request on * an active buffer @@ -2682,21 +2727,14 @@ void Test_CFE_TBL_Manage(void) UT_InitData(); /* Configure table for validation */ - CFE_TBL_Global.ValidationResults[0].State = CFE_TBL_VALIDATION_PENDING; - CFE_TBL_Global.ValidationResults[0].Result = 0; - strncpy(CFE_TBL_Global.ValidationResults[0].TableName, "ut_cfe_tbl.UT_Table1", - sizeof(CFE_TBL_Global.ValidationResults[0].TableName) - 1); - CFE_TBL_Global.ValidationResults[0].TableName[sizeof(CFE_TBL_Global.ValidationResults[0].TableName) - 1] = '\0'; - CFE_TBL_Global.ValidationResults[0].CrcOfTable = 0; - CFE_TBL_Global.ValidationResults[0].ActiveBuffer = true; - RegRecPtr->ValidateActiveIndex = 0; + UT_TBL_SetupPendingValidation(0, true, RegRecPtr, &ValResultPtr); /* Perform validation via manage call */ UT_SetDeferredRetcode(UT_KEY(Test_CFE_TBL_ValidationFunc), 1, 1); CFE_UtAssert_SUCCESS(CFE_TBL_Manage(App1TblHandle1)); CFE_UtAssert_EVENTSENT(CFE_TBL_VALIDATION_ERR_EID); CFE_UtAssert_EVENTCOUNT(1); - UtAssert_INT32_EQ(CFE_TBL_Global.ValidationResults[0].Result, 1); + UtAssert_INT32_EQ(ValResultPtr->Result, 1); /* Test response to processing a successful validation request on an * active buffer @@ -2704,21 +2742,15 @@ void Test_CFE_TBL_Manage(void) UT_InitData(); /* Configure table for validation */ - CFE_TBL_Global.ValidationResults[0].State = CFE_TBL_VALIDATION_PENDING; - CFE_TBL_Global.ValidationResults[0].Result = 1; - strncpy(CFE_TBL_Global.ValidationResults[0].TableName, "ut_cfe_tbl.UT_Table1", - sizeof(CFE_TBL_Global.ValidationResults[0].TableName) - 1); - CFE_TBL_Global.ValidationResults[0].TableName[sizeof(CFE_TBL_Global.ValidationResults[0].TableName) - 1] = '\0'; - CFE_TBL_Global.ValidationResults[0].CrcOfTable = 0; - CFE_TBL_Global.ValidationResults[0].ActiveBuffer = true; - RegRecPtr->ValidateActiveIndex = 0; + UT_TBL_SetupPendingValidation(0, true, RegRecPtr, &ValResultPtr); + ValResultPtr->Result = 1; /* Perform validation via manage call */ UT_SetDeferredRetcode(UT_KEY(Test_CFE_TBL_ValidationFunc), 1, CFE_SUCCESS); CFE_UtAssert_SUCCESS(CFE_TBL_Manage(App1TblHandle1)); CFE_UtAssert_EVENTSENT(CFE_TBL_VALIDATION_INF_EID); CFE_UtAssert_EVENTCOUNT(1); - UtAssert_INT32_EQ(CFE_TBL_Global.ValidationResults[0].Result, 0); + UtAssert_INT32_EQ(ValResultPtr->Result, 0); /* Test response to processing an update request on a locked table */ /* a. Test setup - part 1 */ @@ -2792,21 +2824,14 @@ void Test_CFE_TBL_Manage(void) CFE_UtAssert_SUCCESS(CFE_TBL_GetWorkingBuffer(&WorkingBufferPtr, RegRecPtr, false)); /* Configure table for validation */ - CFE_TBL_Global.ValidationResults[0].State = CFE_TBL_VALIDATION_PENDING; - CFE_TBL_Global.ValidationResults[0].Result = 0; - strncpy(CFE_TBL_Global.ValidationResults[0].TableName, "ut_cfe_tbl.UT_Table2", - sizeof(CFE_TBL_Global.ValidationResults[0].TableName) - 1); - CFE_TBL_Global.ValidationResults[0].TableName[sizeof(CFE_TBL_Global.ValidationResults[0].TableName) - 1] = '\0'; - CFE_TBL_Global.ValidationResults[0].CrcOfTable = 0; - CFE_TBL_Global.ValidationResults[0].ActiveBuffer = false; - RegRecPtr->ValidateInactiveIndex = 0; + UT_TBL_SetupPendingValidation(1, false, RegRecPtr, &ValResultPtr); /* Perform validation via manage call */ UT_SetDeferredRetcode(UT_KEY(Test_CFE_TBL_ValidationFunc), 1, -1); CFE_UtAssert_SUCCESS(CFE_TBL_Manage(App1TblHandle2)); CFE_UtAssert_EVENTSENT(CFE_TBL_VALIDATION_ERR_EID); CFE_UtAssert_EVENTCOUNT(1); - UtAssert_INT32_EQ(CFE_TBL_Global.ValidationResults[0].Result, -1); + UtAssert_INT32_EQ(ValResultPtr->Result, -1); /* Test successfully processing a validation request on an inactive buffer * (double buffered) @@ -2814,21 +2839,14 @@ void Test_CFE_TBL_Manage(void) UT_InitData(); /* Configure table for validation */ - CFE_TBL_Global.ValidationResults[0].State = CFE_TBL_VALIDATION_PENDING; - CFE_TBL_Global.ValidationResults[0].Result = 1; - strncpy(CFE_TBL_Global.ValidationResults[0].TableName, "ut_cfe_tbl.UT_Table2", - sizeof(CFE_TBL_Global.ValidationResults[0].TableName) - 1); - CFE_TBL_Global.ValidationResults[0].TableName[sizeof(CFE_TBL_Global.ValidationResults[0].TableName) - 1] = '\0'; - CFE_TBL_Global.ValidationResults[0].CrcOfTable = 0; - CFE_TBL_Global.ValidationResults[0].ActiveBuffer = false; - RegRecPtr->ValidateInactiveIndex = 0; + UT_TBL_SetupPendingValidation(1, false, RegRecPtr, &ValResultPtr); /* Perform validation via manage call */ UT_SetDeferredRetcode(UT_KEY(Test_CFE_TBL_ValidationFunc), 1, CFE_SUCCESS); CFE_UtAssert_SUCCESS(CFE_TBL_Manage(App1TblHandle2)); CFE_UtAssert_EVENTSENT(CFE_TBL_VALIDATION_INF_EID); CFE_UtAssert_EVENTCOUNT(1); - UtAssert_INT32_EQ(CFE_TBL_Global.ValidationResults[0].Result, 0); + UtAssert_INT32_EQ(ValResultPtr->Result, 0); /* Test processing an unsuccessful validation request on an active buffer * (double buffered) @@ -2836,21 +2854,14 @@ void Test_CFE_TBL_Manage(void) UT_InitData(); /* Configure table for validation */ - CFE_TBL_Global.ValidationResults[0].State = CFE_TBL_VALIDATION_PENDING; - CFE_TBL_Global.ValidationResults[0].Result = 0; - strncpy(CFE_TBL_Global.ValidationResults[0].TableName, "ut_cfe_tbl.UT_Table2", - sizeof(CFE_TBL_Global.ValidationResults[0].TableName) - 1); - CFE_TBL_Global.ValidationResults[0].TableName[sizeof(CFE_TBL_Global.ValidationResults[0].TableName) - 1] = '\0'; - CFE_TBL_Global.ValidationResults[0].CrcOfTable = 0; - CFE_TBL_Global.ValidationResults[0].ActiveBuffer = true; - RegRecPtr->ValidateActiveIndex = 0; + UT_TBL_SetupPendingValidation(1, true, RegRecPtr, &ValResultPtr); /* Perform validation via manage call */ UT_SetDeferredRetcode(UT_KEY(Test_CFE_TBL_ValidationFunc), 1, -1); CFE_UtAssert_SUCCESS(CFE_TBL_Manage(App1TblHandle2)); CFE_UtAssert_EVENTSENT(CFE_TBL_VALIDATION_ERR_EID); CFE_UtAssert_EVENTCOUNT(1); - UtAssert_INT32_EQ(CFE_TBL_Global.ValidationResults[0].Result, -1); + UtAssert_INT32_EQ(ValResultPtr->Result, -1); /* Test successfully processing a validation request on active buffer * (double buffered) @@ -2858,21 +2869,15 @@ void Test_CFE_TBL_Manage(void) UT_InitData(); /* Configure table for validation */ - CFE_TBL_Global.ValidationResults[0].State = CFE_TBL_VALIDATION_PENDING; - CFE_TBL_Global.ValidationResults[0].Result = 1; - strncpy(CFE_TBL_Global.ValidationResults[0].TableName, "ut_cfe_tbl.UT_Table2", - sizeof(CFE_TBL_Global.ValidationResults[0].TableName) - 1); - CFE_TBL_Global.ValidationResults[0].TableName[sizeof(CFE_TBL_Global.ValidationResults[0].TableName) - 1] = '\0'; - CFE_TBL_Global.ValidationResults[0].CrcOfTable = 0; - CFE_TBL_Global.ValidationResults[0].ActiveBuffer = true; - RegRecPtr->ValidateActiveIndex = 0; + UT_TBL_SetupPendingValidation(1, true, RegRecPtr, &ValResultPtr); + ValResultPtr->Result = 1; /* Perform validation via manage call */ UT_SetDeferredRetcode(UT_KEY(Test_CFE_TBL_ValidationFunc), 1, CFE_SUCCESS); CFE_UtAssert_SUCCESS(CFE_TBL_Manage(App1TblHandle2)); CFE_UtAssert_EVENTSENT(CFE_TBL_VALIDATION_INF_EID); CFE_UtAssert_EVENTCOUNT(1); - UtAssert_INT32_EQ(CFE_TBL_Global.ValidationResults[0].Result, 0); + UtAssert_INT32_EQ(ValResultPtr->Result, 0); /* Test successfully processing a table dump request */ UT_InitData(); @@ -3999,6 +4004,105 @@ void Test_CFE_TBL_Internal(void) CFE_TBL_ERR_INVALID_SIZE); } +/* + * Tests the resource accessors for Validation Results + */ +void Test_CFE_TBL_ResourceID_ValidationResult(void) +{ + uint32 Idx; + CFE_TBL_ValidationResultId_t InvalidResultId; + CFE_TBL_ValidationResultId_t ValidResultId; + CFE_ResourceId_t PendingId; + + UT_InitData(); + + InvalidResultId = CFE_TBL_VALRESULTID_UNDEFINED; + UT_SetDefaultReturnValue(UT_KEY(CFE_ResourceId_ToIndex), CFE_ES_ERR_RESOURCEID_NOT_VALID); + UtAssert_INT32_EQ(CFE_TBL_ValidationResultId_ToIndex(InvalidResultId, &Idx), CFE_ES_ERR_RESOURCEID_NOT_VALID); + + /* by definition, looking up the undefined value should always be NULL */ + UtAssert_NULL(CFE_TBL_LocateValidationResultByID(InvalidResultId)); + UT_ResetState(UT_KEY(CFE_ResourceId_ToIndex)); + + ValidResultId = CFE_TBL_VALRESULTID_C(CFE_ResourceId_FromInteger(CFE_TBL_VALRESULTID_BASE + 1)); + UtAssert_INT32_EQ(CFE_TBL_ValidationResultId_ToIndex(ValidResultId, &Idx), CFE_SUCCESS); + + UtAssert_VOIDCALL(PendingId = CFE_TBL_GetNextValResultBlock()); + UtAssert_BOOL_TRUE(CFE_ResourceId_IsDefined(PendingId)); + + /* The slot should be available right now */ + UtAssert_BOOL_FALSE(CFE_TBL_CheckValidationResultSlotUsed(PendingId)); + + /* Make it used and confirm it is reported as not available */ + CFE_TBL_ValidationResultSetUsed(CFE_TBL_LocateValidationResultByID(CFE_TBL_VALRESULTID_C(PendingId)), PendingId); + UtAssert_BOOL_TRUE(CFE_TBL_CheckValidationResultSlotUsed(PendingId)); + + /* Test case where no ID is available */ + UT_SetDefaultReturnValue(UT_KEY(CFE_ResourceId_FindNext), 0); + UtAssert_VOIDCALL(PendingId = CFE_TBL_GetNextValResultBlock()); + UtAssert_BOOL_FALSE(CFE_ResourceId_IsDefined(PendingId)); + + /* A nonexistent slot is always "unavailable" */ + UtAssert_BOOL_TRUE(CFE_TBL_CheckValidationResultSlotUsed(PendingId)); + UT_ResetState(UT_KEY(CFE_ResourceId_FindNext)); +} + +/* + * Tests the resource accessors for Table Registry Records + */ +void Test_CFE_TBL_ResourceID_RegistryRecord(void) +{ + uint32 Idx; + CFE_TBL_RegId_t InvalidRegId; + CFE_TBL_RegId_t ValidRegId; + CFE_TBL_RegistryRec_t *RegRecPtr; + + UT_InitData(); + + InvalidRegId = (CFE_TBL_RegId_t)(-1); + UtAssert_INT32_EQ(CFE_TBL_RegId_ToIndex(InvalidRegId, &Idx), CFE_TBL_ERR_INVALID_HANDLE); + + /* by definition, looking up the undefined value should always be NULL */ + UtAssert_NULL(CFE_TBL_LocateRegistryRecordByID(InvalidRegId)); + + InvalidRegId = (CFE_TBL_RegId_t)(CFE_PLATFORM_TBL_MAX_NUM_TABLES + 1); + UtAssert_INT32_EQ(CFE_TBL_RegId_ToIndex(InvalidRegId, &Idx), CFE_TBL_ERR_INVALID_HANDLE); + + /* Now with a valid ID */ + ValidRegId = (CFE_TBL_RegId_t)(1); + UtAssert_INT32_EQ(CFE_TBL_RegId_ToIndex(ValidRegId, &Idx), CFE_SUCCESS); + UtAssert_UINT32_EQ(Idx, 1); + UtAssert_NOT_NULL(RegRecPtr = CFE_TBL_LocateRegistryRecordByID(ValidRegId)); + + UtAssert_UINT32_EQ(CFE_TBL_RegistryRecordGetID(RegRecPtr), ValidRegId); +} + +/* + * Tests the resource accessors for Table Access Descriptors + */ +void Test_CFE_TBL_ResourceID_AccessDescriptor(void) +{ + uint32 Idx; + CFE_TBL_Handle_t InvalidHandle; + CFE_TBL_Handle_t ValidHandle; + + UT_InitData(); + + InvalidHandle = (CFE_TBL_Handle_t)(-1); + UtAssert_INT32_EQ(CFE_TBL_Handle_ToIndex(InvalidHandle, &Idx), CFE_TBL_ERR_INVALID_HANDLE); + + /* by definition, looking up the undefined value should always be NULL */ + UtAssert_NULL(CFE_TBL_LocateRegistryRecordByID(InvalidHandle)); + + InvalidHandle = (CFE_TBL_Handle_t)(CFE_PLATFORM_TBL_MAX_NUM_HANDLES + 1); + UtAssert_INT32_EQ(CFE_TBL_Handle_ToIndex(InvalidHandle, &Idx), CFE_TBL_ERR_INVALID_HANDLE); + + /* Now with a valid ID */ + ValidHandle = (CFE_TBL_Handle_t)(1); + UtAssert_INT32_EQ(CFE_TBL_RegId_ToIndex(ValidHandle, &Idx), CFE_SUCCESS); + UtAssert_UINT32_EQ(Idx, 1); +} + /* ** Test function executed when the contents of a table need to be validated */ diff --git a/modules/tbl/ut-coverage/tbl_UT.h b/modules/tbl/ut-coverage/tbl_UT.h index d4f2d80c7..95fd0d97b 100644 --- a/modules/tbl/ut-coverage/tbl_UT.h +++ b/modules/tbl/ut-coverage/tbl_UT.h @@ -680,4 +680,9 @@ void Test_CFE_TBL_Internal(void); ******************************************************************************/ int32 Test_CFE_TBL_ValidationFunc(void *TblPtr); +/* Test cases for resource ID access patterns based on shared resource types */ +void Test_CFE_TBL_ResourceID_ValidationResult(void); +void Test_CFE_TBL_ResourceID_RegistryRecord(void); +void Test_CFE_TBL_ResourceID_AccessDescriptor(void); + #endif /* TBL_UT_H */ From b9066712147e384d86b8b0aab698a23adf663e50 Mon Sep 17 00:00:00 2001 From: Joseph Hickey Date: Thu, 18 Apr 2024 10:52:32 -0400 Subject: [PATCH 2/2] Fix #2550, Use resource ID for dump control index Use a resourceID value for access into the dump control structure array. This allows for consistent lookup, matching, and free/in-use determination, as well as improved resilience to race conditions and stale data. --- .../fsw/inc/cfe_core_resourceid_basevalues.h | 2 + modules/tbl/fsw/src/cfe_tbl_api.c | 48 ++-- modules/tbl/fsw/src/cfe_tbl_internal.c | 17 +- modules/tbl/fsw/src/cfe_tbl_resource.c | 66 +++++ modules/tbl/fsw/src/cfe_tbl_resource.h | 183 +++++++++++++- modules/tbl/fsw/src/cfe_tbl_task.h | 45 ++-- modules/tbl/fsw/src/cfe_tbl_task_cmds.c | 82 +++--- modules/tbl/fsw/src/cfe_tbl_transaction.c | 2 +- modules/tbl/ut-coverage/tbl_UT.c | 235 ++++++++++++------ modules/tbl/ut-coverage/tbl_UT.h | 1 + 10 files changed, 496 insertions(+), 185 deletions(-) diff --git a/modules/resourceid/fsw/inc/cfe_core_resourceid_basevalues.h b/modules/resourceid/fsw/inc/cfe_core_resourceid_basevalues.h index 443136be6..a2011ba51 100644 --- a/modules/resourceid/fsw/inc/cfe_core_resourceid_basevalues.h +++ b/modules/resourceid/fsw/inc/cfe_core_resourceid_basevalues.h @@ -73,6 +73,7 @@ enum /* TBL managed resources */ CFE_RESOURCEID_TBL_VALRESULTID_BASE_OFFSET = OS_OBJECT_TYPE_USER + 8, + CFE_RESOURCEID_TBL_DUMPCTRLID_BASE_OFFSET = OS_OBJECT_TYPE_USER + 9, }; @@ -99,6 +100,7 @@ enum /* TBL managed resources */ CFE_TBL_VALRESULTID_BASE = CFE_RESOURCEID_MAKE_BASE(CFE_RESOURCEID_TBL_VALRESULTID_BASE_OFFSET), + CFE_TBL_DUMPCTRLID_BASE = CFE_RESOURCEID_MAKE_BASE(CFE_RESOURCEID_TBL_DUMPCTRLID_BASE_OFFSET), }; diff --git a/modules/tbl/fsw/src/cfe_tbl_api.c b/modules/tbl/fsw/src/cfe_tbl_api.c index e63252ef8..92dc8933c 100644 --- a/modules/tbl/fsw/src/cfe_tbl_api.c +++ b/modules/tbl/fsw/src/cfe_tbl_api.c @@ -1096,7 +1096,7 @@ CFE_Status_t CFE_TBL_DumpToBuffer(CFE_TBL_Handle_t TblHandle) int32 Status; CFE_TBL_RegistryRec_t *RegRecPtr = NULL; CFE_TBL_DumpControl_t *DumpCtrlPtr = NULL; - CFE_ES_AppId_t ThisAppId; + CFE_TBL_LoadBuff_t * ActiveBufPtr; Status = CFE_TBL_TxnStartFromHandle(&Txn, TblHandle, CFE_TBL_TxnContext_ACCESSOR_APP); @@ -1104,36 +1104,34 @@ CFE_Status_t CFE_TBL_DumpToBuffer(CFE_TBL_Handle_t TblHandle) { Status = CFE_TBL_TxnGetTableStatus(&Txn); - RegRecPtr = CFE_TBL_TxnRegRec(&Txn); - - CFE_TBL_TxnFinish(&Txn); - } - else - { - ThisAppId = CFE_TBL_TxnAppId(&Txn); - - CFE_ES_WriteToSysLog("%s: App(%lu) does not have access to Tbl Handle=%d\n", __func__, - CFE_RESOURCEID_TO_ULONG(ThisAppId), (int)TblHandle); - } + /* Make sure the table has been requested to be dumped */ + if (Status == CFE_TBL_INFO_DUMP_PENDING) + { + RegRecPtr = CFE_TBL_TxnRegRec(&Txn); + DumpCtrlPtr = CFE_TBL_LocateDumpCtrlByID(RegRecPtr->DumpControlId); + ActiveBufPtr = CFE_TBL_GetActiveBuffer(RegRecPtr); - /* Make sure the table has been requested to be dumped */ - if (Status == CFE_TBL_INFO_DUMP_PENDING) - { - DumpCtrlPtr = &CFE_TBL_Global.DumpControlBlocks[RegRecPtr->DumpControlIndex]; + /* Copy the contents of the active buffer to the assigned dump buffer */ + memcpy(DumpCtrlPtr->DumpBufferPtr->BufferPtr, ActiveBufPtr->BufferPtr, DumpCtrlPtr->Size); - /* Copy the contents of the active buffer to the assigned dump buffer */ - memcpy(DumpCtrlPtr->DumpBufferPtr->BufferPtr, RegRecPtr->Buffers[0].BufferPtr, DumpCtrlPtr->Size); + /* Save the current time so that the header in the dump file can have the correct time */ + DumpCtrlPtr->DumpBufferPtr->FileTime = CFE_TIME_GetTime(); - /* Save the current time so that the header in the dump file can have the correct time */ - DumpCtrlPtr->DumpBufferPtr->FileTime = CFE_TIME_GetTime(); + /* Disassociate the dump request from the table */ + RegRecPtr->DumpControlId = CFE_TBL_NO_DUMP_PENDING; - /* Disassociate the dump request from the table */ - RegRecPtr->DumpControlIndex = CFE_TBL_NO_DUMP_PENDING; + /* Notify the Table Services Application that the dump buffer is ready to be written to a file */ + DumpCtrlPtr->State = CFE_TBL_DUMP_PERFORMED; - /* Notify the Table Services Application that the dump buffer is ready to be written to a file */ - DumpCtrlPtr->State = CFE_TBL_DUMP_PERFORMED; + Status = CFE_SUCCESS; + } - Status = CFE_SUCCESS; + CFE_TBL_TxnFinish(&Txn); + } + else + { + CFE_ES_WriteToSysLog("%s: App(%lu) does not have access to Tbl Handle=%lu\n", __func__, + CFE_TBL_TxnAppIdAsULong(&Txn), CFE_TBL_TxnHandleAsULong(&Txn)); } return Status; diff --git a/modules/tbl/fsw/src/cfe_tbl_internal.c b/modules/tbl/fsw/src/cfe_tbl_internal.c index 76053e463..fe736b0df 100644 --- a/modules/tbl/fsw/src/cfe_tbl_internal.c +++ b/modules/tbl/fsw/src/cfe_tbl_internal.c @@ -92,8 +92,6 @@ int32 CFE_TBL_EarlyInit(void) /* Initialize the Dump-Only Table Dump Control Blocks nonzero values */ for (i = 0; i < CFE_PLATFORM_TBL_MAX_SIMULTANEOUS_LOADS; i++) { - CFE_TBL_Global.DumpControlBlocks[i].State = CFE_TBL_DUMP_FREE; - /* Prevent Shared Buffers from being used until successfully allocated */ CFE_TBL_Global.LoadBuffs[i].Taken = true; } @@ -223,7 +221,7 @@ void CFE_TBL_InitRegistryRecord(CFE_TBL_RegistryRec_t *RegRecPtr) RegRecPtr->ValidateActiveId = CFE_TBL_NO_VALIDATION_PENDING; RegRecPtr->ValidateInactiveId = CFE_TBL_NO_VALIDATION_PENDING; RegRecPtr->CDSHandle = CFE_ES_CDS_BAD_HANDLE; - RegRecPtr->DumpControlIndex = CFE_TBL_NO_DUMP_PENDING; + RegRecPtr->DumpControlId = CFE_TBL_NO_DUMP_PENDING; CFE_TBL_HandleLinkInit(&RegRecPtr->AccessList); } @@ -1021,19 +1019,22 @@ void CFE_TBL_ByteSwapUint32(uint32 *Uint32ToSwapPtr) *-----------------------------------------------------------------*/ int32 CFE_TBL_CleanUpApp(CFE_ES_AppId_t AppId) { - uint32 i; - CFE_TBL_TxnState_t Txn; + uint32 i; + CFE_TBL_TxnState_t Txn; + CFE_TBL_DumpControl_t *DumpCtrlPtr; /* Scan Dump Requests to determine if any of the tables that */ /* were to be dumped will be deleted */ for (i = 0; i < CFE_PLATFORM_TBL_MAX_SIMULTANEOUS_LOADS; i++) { + DumpCtrlPtr = &CFE_TBL_Global.DumpControlBlocks[i]; + /* Check to see if the table to be dumped is owned by the App to be deleted */ - if ((CFE_TBL_Global.DumpControlBlocks[i].State != CFE_TBL_DUMP_FREE) && - CFE_RESOURCEID_TEST_EQUAL(CFE_TBL_Global.DumpControlBlocks[i].RegRecPtr->OwnerAppId, AppId)) + if (CFE_TBL_DumpCtrlBlockIsUsed(DumpCtrlPtr) && + CFE_RESOURCEID_TEST_EQUAL(DumpCtrlPtr->RegRecPtr->OwnerAppId, AppId)) { /* If so, then remove the dump request */ - CFE_TBL_Global.DumpControlBlocks[i].State = CFE_TBL_DUMP_FREE; + CFE_TBL_DumpCtrlBlockSetFree(DumpCtrlPtr); } } diff --git a/modules/tbl/fsw/src/cfe_tbl_resource.c b/modules/tbl/fsw/src/cfe_tbl_resource.c index 06e90b987..23275fa8e 100644 --- a/modules/tbl/fsw/src/cfe_tbl_resource.c +++ b/modules/tbl/fsw/src/cfe_tbl_resource.c @@ -52,6 +52,18 @@ CFE_Status_t CFE_TBL_Handle_ToIndex(CFE_TBL_Handle_t TblHandle, uint32 *Idx) return CFE_SUCCESS; } +/*---------------------------------------------------------------- + * + * Implemented per public API + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_Status_t CFE_TBL_DumpCtrlId_ToIndex(CFE_TBL_DumpCtrlId_t DumpCtrlId, uint32 *Idx) +{ + return CFE_ResourceId_ToIndex(CFE_RESOURCEID_UNWRAP(DumpCtrlId), CFE_TBL_DUMPCTRLID_BASE, + CFE_PLATFORM_TBL_MAX_SIMULTANEOUS_LOADS, Idx); +} + /*---------------------------------------------------------------- * * Implemented per public API @@ -152,6 +164,60 @@ CFE_TBL_Handle_t CFE_TBL_AccessDescriptorGetHandle(const CFE_TBL_AccessDescripto return (AccessDescPtr - CFE_TBL_Global.Handles); } +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_TBL_DumpControl_t *CFE_TBL_LocateDumpCtrlByID(CFE_TBL_DumpCtrlId_t BlockId) +{ + CFE_TBL_DumpControl_t *BlockPtr; + uint32 Idx; + + if (CFE_TBL_DumpCtrlId_ToIndex(BlockId, &Idx) == CFE_SUCCESS) + { + BlockPtr = &CFE_TBL_Global.DumpControlBlocks[Idx]; + } + else + { + BlockPtr = NULL; + } + + return BlockPtr; +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +bool CFE_TBL_CheckDumpCtrlSlotUsed(CFE_ResourceId_t CheckId) +{ + CFE_TBL_DumpControl_t *BlockPtr; + + /* + * 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. + */ + BlockPtr = CFE_TBL_LocateDumpCtrlByID(CFE_TBL_DUMPCTRLID_C(CheckId)); + return (BlockPtr == NULL || CFE_TBL_DumpCtrlBlockIsUsed(BlockPtr)); +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_ResourceId_t CFE_TBL_GetNextDumpCtrlBlock(void) +{ + return CFE_ResourceId_FindNext(CFE_TBL_Global.LastDumpCtrlBlockId, CFE_PLATFORM_TBL_MAX_SIMULTANEOUS_LOADS, + CFE_TBL_CheckDumpCtrlSlotUsed); +} + /*---------------------------------------------------------------- * * Application-scope internal function diff --git a/modules/tbl/fsw/src/cfe_tbl_resource.h b/modules/tbl/fsw/src/cfe_tbl_resource.h index 5eba6922b..7217b1684 100644 --- a/modules/tbl/fsw/src/cfe_tbl_resource.h +++ b/modules/tbl/fsw/src/cfe_tbl_resource.h @@ -112,7 +112,7 @@ CFE_TBL_ValidationResult_t *CFE_TBL_LocateValidationResultByID(CFE_TBL_Validatio * Calculates the array position/index of the global array entry for * the given result ID. * - * @param[in] BlockID the ID/handle of the validation result block to retrieve + * @param[in] ValResultId the ID of the validation result entry 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 ID is outside valid range @@ -242,6 +242,187 @@ CFE_ResourceId_t CFE_TBL_GetNextValResultBlock(void); */ bool CFE_TBL_CheckValidationResultSlotUsed(CFE_ResourceId_t CheckId); +/* + * --------------------------------------------------------------------------------------- + * + * ~~~ DUMP CONTROL BLOCK ACCESSORS ~~~ + * + * These operate on CFE_TBL_DumpControl_t* and CFE_TBL_DumpCtrlId_t types + * + * --------------------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Get the array index correlating with a Dump Control Block ID + * + * Calculates the array position/index of the global array entry for + * the given block ID. + * + * @param[in] DumpCtrlId the ID/handle of the control 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 ID is outside valid range + */ +CFE_Status_t CFE_TBL_DumpCtrlId_ToIndex(CFE_TBL_DumpCtrlId_t DumpCtrlId, uint32 *Idx); + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Locate the registry table entry correlating with a given registry ID. + * + * This only returns a pointer to the table entry where the record + * should reside, but does _not_ actually check/validate the entry. + * + * If the passed-in ID parameter is not within the acceptable range of ID + * values for applications, such that it could never be valid under + * any circumstances, then NULL is returned. Otherwise, a pointer to the + * corresponding table entry is returned, indicating the location where + * that ID should reside, if it is currently in use. + * + * @note This only returns where the ID should reside, not that it actually + * resides there. If looking up an existing ID, then caller must additionally + * confirm that the returned record is a match to the expected ID before using + * or modifying the data within the returned record pointer. + * + * The CFE_TBL_DumpCtrlBlockIsMatch() function can be used to check/confirm + * if the returned table entry is a positive match for the given ID. + * + * @sa CFE_TBL_DumpCtrlBlockIsMatch() + * + * @param[in] BlockId the block ID to locate + * @return pointer to dump control block for the given ID, or NULL if out of range + */ +CFE_TBL_DumpControl_t *CFE_TBL_LocateDumpCtrlByID(CFE_TBL_DumpCtrlId_t BlockId); + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Check if a dump control block is a match for the given ID + * + * This routine confirms that the previously-located block pointer is valid + * and matches the expected block ID. + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * This function may be used in conjunction with CFE_TBL_LocateDumpCtrlByID() + * to confirm that the located record is a positive match to the expected ID. + * As such, the record pointer is also permitted to be NULL, to alleviate the + * need for the caller to handle this possibility explicitly. + * + * Once a record pointer has been successfully validated using this routine, + * it may be safely passed to all other internal functions. + * + * @sa CFE_TBL_LocateDumpCtrlByID + * + * @param[in] BlockPtr pointer to validation result table entry, or NULL + * @param[in] BlockId expected validation result ID + * @returns true if the entry matches the given ID + */ +static inline bool CFE_TBL_DumpCtrlBlockIsMatch(const CFE_TBL_DumpControl_t *BlockPtr, CFE_TBL_DumpCtrlId_t BlockId) +{ + return (BlockPtr != NULL && CFE_RESOURCEID_TEST_EQUAL(BlockPtr->BlockId, BlockId)); +} + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Check if a dump control block is in use or free/empty + * + * This routine checks if the block 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. + * + * @note This internal helper function must only be used on result pointers + * that are known to refer to an actual table location (i.e. non-null). + * + * @param[in] BlockPtr pointer to dump control block + * @returns true if the entry is in use/configured, or false if it is free/empty + */ +static inline bool CFE_TBL_DumpCtrlBlockIsUsed(const CFE_TBL_DumpControl_t *BlockPtr) +{ + return (CFE_RESOURCEID_TEST_DEFINED(BlockPtr->BlockId)); +} + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Marks a dump control block as in use (not avaliable) + * + * This sets the internal field(s) within this entry, and marks + * it as being associated with the given block ID. + * + * @note This internal helper function must only be used on record pointers + * that are known to refer to an actual table location (i.e. non-null). + * + * @param[in] BlockPtr pointer to dump control block + * @param[in] PendingId the ID of this entry that will be set + */ +static inline void CFE_TBL_DumpCtrlBlockSetUsed(CFE_TBL_DumpControl_t *BlockPtr, CFE_ResourceId_t PendingId) +{ + BlockPtr->BlockId = CFE_TBL_DUMPCTRLID_C(PendingId); +} + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Marks a dump control block as available (not in use) + * + * This clears the internal field(s) within this entry, and marks + * it as not being associated with any block ID. + * + * @note This internal helper function must only be used on record pointers + * that are known to refer to an actual table location (i.e. non-null). + * + * @param[in] BlockPtr pointer to dump control block + */ +static inline void CFE_TBL_DumpCtrlBlockSetFree(CFE_TBL_DumpControl_t *BlockPtr) +{ + BlockPtr->State = CFE_TBL_DUMP_FREE; + BlockPtr->BlockId = CFE_TBL_DUMPCTRLID_UNDEFINED; +} + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Get the ID value from a dump control block + * + * This routine converts the block pointer to its corresponding ID. + * + * @note This internal helper function must only be used on record pointers + * that are known to refer to an actual table location (i.e. non-null). + * + * @param[in] BlockPtr pointer to block + * @returns ID of entry + */ +static inline CFE_TBL_DumpCtrlId_t CFE_TBL_DumpCtrlBlockGetId(const CFE_TBL_DumpControl_t *BlockPtr) +{ + return BlockPtr->BlockId; +} + +/* + * 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. + */ + +/** + * @brief Determine the next ID to use for a dump control block + * + * Obtains an ID value that is usable for a new dump control block. If no blocks + * are available, then UNDEFINED is returned. + * + * @returns ID to use for next control block, or UNDEFINED if no slots available + */ +CFE_ResourceId_t CFE_TBL_GetNextDumpCtrlBlock(void); + +/** + * Test if a slot corresponding to a pending ID is used + * + * This is an internal helper function for CFE_ResourceId_FindNext(), and not + * typically called directly. It is prototyped here for unit testing. + * + * @returns True if used, False if available + */ +bool CFE_TBL_CheckDumpCtrlSlotUsed(CFE_ResourceId_t CheckId); + /* * --------------------------------------------------------------------------------------- * diff --git a/modules/tbl/fsw/src/cfe_tbl_task.h b/modules/tbl/fsw/src/cfe_tbl_task.h index 5176d0f3f..c510cd46b 100644 --- a/modules/tbl/fsw/src/cfe_tbl_task.h +++ b/modules/tbl/fsw/src/cfe_tbl_task.h @@ -64,16 +64,16 @@ /** \brief Value indicating when no Validation is Pending */ /** ** This macro is used to indicate no Validation is Pending by assigning it to -** #CFE_TBL_RegistryRec_t::ValidateActiveIndex or #CFE_TBL_RegistryRec_t::ValidateInactiveIndex +** #CFE_TBL_RegistryRec_t::ValidateActiveId or #CFE_TBL_RegistryRec_t::ValidateInactiveId */ #define CFE_TBL_NO_VALIDATION_PENDING CFE_TBL_VALRESULTID_UNDEFINED /** \brief Value indicating when no Dump is Pending on a Dump-Only Table */ /** ** This macro is used to indicate no Dump is Pending by assigning it to -** #CFE_TBL_RegistryRec_t::DumpControlIndex +** #CFE_TBL_RegistryRec_t::DumpControlId */ -#define CFE_TBL_NO_DUMP_PENDING (-1) +#define CFE_TBL_NO_DUMP_PENDING CFE_TBL_DUMPCTRLID_UNDEFINED /************************ Internal Structure Definitions *****************************/ @@ -87,6 +87,16 @@ typedef CFE_RESOURCEID_BASE_TYPE CFE_TBL_ValidationResultId_t; #define CFE_TBL_VALRESULTID_C(val) ((CFE_TBL_ValidationResultId_t)CFE_RESOURCEID_WRAP(val)) #define CFE_TBL_VALRESULTID_UNDEFINED CFE_TBL_VALRESULTID_C(CFE_RESOURCEID_UNDEFINED) +/** + * @brief A type for Dump Control Block IDs + * + * This is the type that is used for any API accepting or returning a dump control block + */ +typedef CFE_RESOURCEID_BASE_TYPE CFE_TBL_DumpCtrlId_t; + +#define CFE_TBL_DUMPCTRLID_C(val) ((CFE_TBL_DumpCtrlId_t)CFE_RESOURCEID_WRAP(val)) +#define CFE_TBL_DUMPCTRLID_UNDEFINED CFE_TBL_DUMPCTRLID_C(CFE_RESOURCEID_UNDEFINED) + /*******************************************************************************/ /** \brief Identifies the current state of a validation sequence. */ @@ -205,19 +215,19 @@ typedef struct CFE_TBL_ValidationResultId_t ValidateActiveId; /**< \brief Index to Validation Request on Active Table Result data */ CFE_TBL_ValidationResultId_t - ValidateInactiveId; /**< \brief Index to Validation Request on Inactive Table Result data */ - int32 DumpControlIndex; /**< \brief Index to Dump Control Block */ - CFE_ES_CDSHandle_t CDSHandle; /**< \brief Handle to Critical Data Store for Critical Tables */ - CFE_MSG_FcnCode_t NotificationCC; /**< \brief Command Code of an associated management notification message */ - bool CriticalTable; /**< \brief Flag indicating whether table is a Critical Table */ - bool TableLoadedOnce; /**< \brief Flag indicating whether table has been loaded once or not */ - bool LoadPending; /**< \brief Flag indicating an inactive buffer is ready to be copied */ - bool DumpOnly; /**< \brief Flag indicating Table is NOT to be loaded */ - bool DoubleBuffered; /**< \brief Flag indicating Table has a dedicated inactive buffer */ - bool UserDefAddr; /**< \brief Flag indicating Table address was defined by Owner Application */ - bool NotifyByMsg; /**< \brief Flag indicating Table Services should notify owning App via message - when table requires management */ - uint8 ActiveBufferIndex; /**< \brief Index identifying which buffer is the active buffer */ + ValidateInactiveId; /**< \brief Index to Validation Request on Inactive Table Result data */ + CFE_TBL_DumpCtrlId_t DumpControlId; /**< \brief Index to Dump Control Block */ + CFE_ES_CDSHandle_t CDSHandle; /**< \brief Handle to Critical Data Store for Critical Tables */ + CFE_MSG_FcnCode_t NotificationCC; /**< \brief Command Code of an associated management notification message */ + bool CriticalTable; /**< \brief Flag indicating whether table is a Critical Table */ + bool TableLoadedOnce; /**< \brief Flag indicating whether table has been loaded once or not */ + bool LoadPending; /**< \brief Flag indicating an inactive buffer is ready to be copied */ + bool DumpOnly; /**< \brief Flag indicating Table is NOT to be loaded */ + bool DoubleBuffered; /**< \brief Flag indicating Table has a dedicated inactive buffer */ + bool UserDefAddr; /**< \brief Flag indicating Table address was defined by Owner Application */ + bool NotifyByMsg; /**< \brief Flag indicating Table Services should notify owning App via message + when table requires management */ + uint8 ActiveBufferIndex; /**< \brief Index identifying which buffer is the active buffer */ char Name[CFE_TBL_MAX_FULL_NAME_LEN]; /**< \brief Processor specific table name */ char LastFileLoaded[OS_MAX_PATH_LEN]; /**< \brief Filename of last file loaded into table */ } CFE_TBL_RegistryRec_t; @@ -245,6 +255,8 @@ typedef struct */ typedef struct { + CFE_TBL_DumpCtrlId_t BlockId; + CFE_TBL_DumpState_t State; /**< \brief Current state of this block of data */ size_t Size; /**< \brief Number of bytes to be dumped */ CFE_TBL_LoadBuff_t * DumpBufferPtr; /**< \brief Address where dumped data is to be stored temporarily */ @@ -365,6 +377,7 @@ typedef struct CFE_TBL_RegDumpStateInfo_t RegDumpState; CFE_ResourceId_t LastValidationResultId; + CFE_ResourceId_t LastDumpCtrlBlockId; } CFE_TBL_Global_t; diff --git a/modules/tbl/fsw/src/cfe_tbl_task_cmds.c b/modules/tbl/fsw/src/cfe_tbl_task_cmds.c index 615676308..1033464d4 100644 --- a/modules/tbl/fsw/src/cfe_tbl_task_cmds.c +++ b/modules/tbl/fsw/src/cfe_tbl_task_cmds.c @@ -86,10 +86,11 @@ int32 CFE_TBL_SendHkCmd(const CFE_TBL_SendHkCmd_t *data) /* Check to see if there are any dump-only table dumps pending */ for (i = 0; i < CFE_PLATFORM_TBL_MAX_SIMULTANEOUS_LOADS; i++) { - if (CFE_TBL_Global.DumpControlBlocks[i].State == CFE_TBL_DUMP_PERFORMED) + DumpCtrlPtr = &CFE_TBL_Global.DumpControlBlocks[i]; + + if (CFE_TBL_DumpCtrlBlockIsUsed(DumpCtrlPtr) && DumpCtrlPtr->State == CFE_TBL_DUMP_PERFORMED) { - DumpCtrlPtr = &CFE_TBL_Global.DumpControlBlocks[i]; - Status = CFE_TBL_DumpToFile(DumpCtrlPtr->DumpBufferPtr->DataSource, DumpCtrlPtr->TableName, + Status = CFE_TBL_DumpToFile(DumpCtrlPtr->DumpBufferPtr->DataSource, DumpCtrlPtr->TableName, DumpCtrlPtr->DumpBufferPtr->BufferPtr, DumpCtrlPtr->Size); /* If dump file was successfully written, update the file header so that the timestamp */ @@ -125,7 +126,7 @@ int32 CFE_TBL_SendHkCmd(const CFE_TBL_SendHkCmd_t *data) DumpCtrlPtr->RegRecPtr->LoadInProgress = CFE_TBL_NO_LOAD_IN_PROGRESS; /* Free the Dump Control Block for later use */ - DumpCtrlPtr->State = CFE_TBL_DUMP_FREE; + CFE_TBL_DumpCtrlBlockSetFree(DumpCtrlPtr); } } @@ -536,17 +537,20 @@ int32 CFE_TBL_LoadCmd(const CFE_TBL_LoadCmd_t *data) int32 CFE_TBL_DumpCmd(const CFE_TBL_DumpCmd_t *data) { CFE_TBL_CmdProcRet_t ReturnCode = CFE_TBL_INC_ERR_CTR; /* Assume failure */ - int16 RegIndex; + CFE_TBL_TxnState_t Txn; const CFE_TBL_DumpCmd_Payload_t *CmdPtr = &data->Payload; char DumpFilename[OS_MAX_PATH_LEN]; char TableName[CFE_TBL_MAX_FULL_NAME_LEN]; CFE_TBL_RegistryRec_t * RegRecPtr; - void * DumpDataAddr = NULL; CFE_TBL_LoadBuff_t * WorkingBufferPtr; - int32 DumpIndex; - int32 Status; + CFE_TBL_LoadBuff_t * SelectedBufferPtr; + CFE_ResourceId_t PendingDumpId; + CFE_Status_t Status; CFE_TBL_DumpControl_t * DumpCtrlPtr; + SelectedBufferPtr = NULL; + WorkingBufferPtr = NULL; + /* Make sure all strings are null terminated before attempting to process them */ CFE_SB_MessageStringGet(DumpFilename, (char *)CmdPtr->DumpFilename, NULL, sizeof(DumpFilename), sizeof(CmdPtr->DumpFilename)); @@ -554,76 +558,47 @@ int32 CFE_TBL_DumpCmd(const CFE_TBL_DumpCmd_t *data) CFE_SB_MessageStringGet(TableName, (char *)CmdPtr->TableName, NULL, sizeof(TableName), sizeof(CmdPtr->TableName)); /* Before doing anything, lets make sure the table that is to be dumped exists */ - RegIndex = CFE_TBL_FindTableInRegistry(TableName); + Status = CFE_TBL_TxnStartFromName(&Txn, TableName, CFE_TBL_TxnContext_UNDEFINED); - if (RegIndex != CFE_TBL_NOT_FOUND) + if (Status == CFE_SUCCESS) { /* Obtain a pointer to registry information about specified table */ - RegRecPtr = &CFE_TBL_Global.Registry[RegIndex]; + RegRecPtr = CFE_TBL_TxnRegRec(&Txn); /* Determine what data is to be dumped */ - if (CmdPtr->ActiveTableFlag == CFE_TBL_BufferSelect_ACTIVE) - { - DumpDataAddr = RegRecPtr->Buffers[RegRecPtr->ActiveBufferIndex].BufferPtr; - } - else if (CmdPtr->ActiveTableFlag == CFE_TBL_BufferSelect_INACTIVE) /* Dumping Inactive Buffer */ - { - /* If this is a double buffered table, locating the inactive buffer is trivial */ - if (RegRecPtr->DoubleBuffered) - { - DumpDataAddr = RegRecPtr->Buffers[(1U - RegRecPtr->ActiveBufferIndex)].BufferPtr; - } - else - { - /* For single buffered tables, the index to the inactive buffer is kept in 'LoadInProgress' */ - /* Unless this is a table whose address was defined by the owning Application. */ - if ((RegRecPtr->LoadInProgress != CFE_TBL_NO_LOAD_IN_PROGRESS) && (!RegRecPtr->UserDefAddr)) - { - DumpDataAddr = CFE_TBL_Global.LoadBuffs[RegRecPtr->LoadInProgress].BufferPtr; - } - else - { - CFE_EVS_SendEvent(CFE_TBL_NO_INACTIVE_BUFFER_ERR_EID, CFE_EVS_EventType_ERROR, - "No Inactive Buffer for Table '%s' present", TableName); - } - } - } - else + SelectedBufferPtr = CFE_TBL_GetSelectedBuffer(RegRecPtr, CmdPtr->ActiveTableFlag); + + CFE_TBL_TxnFinish(&Txn); + + if (SelectedBufferPtr == NULL) { CFE_EVS_SendEvent(CFE_TBL_ILLEGAL_BUFF_PARAM_ERR_EID, CFE_EVS_EventType_ERROR, "Cmd for Table '%s' had illegal buffer parameter (0x%08X)", TableName, (unsigned int)CmdPtr->ActiveTableFlag); } - - /* If we have located the data to be dumped, then proceed with creating the file and dumping the data */ - if (DumpDataAddr != NULL) + else { + /* If we have located the data to be dumped, then proceed with creating the file and dumping the data */ /* If this is not a dump only table, then we can perform the dump immediately */ if (!RegRecPtr->DumpOnly) { - ReturnCode = CFE_TBL_DumpToFile(DumpFilename, TableName, DumpDataAddr, RegRecPtr->Size); + ReturnCode = CFE_TBL_DumpToFile(DumpFilename, TableName, SelectedBufferPtr->BufferPtr, RegRecPtr->Size); } else /* Dump Only tables need to synchronize their dumps with the owner's execution */ { /* Make sure a dump is not already in progress */ - if (RegRecPtr->DumpControlIndex == CFE_TBL_NO_DUMP_PENDING) + if (!CFE_RESOURCEID_TEST_DEFINED(RegRecPtr->DumpControlId)) { /* Find a free Dump Control Block */ - DumpIndex = 0; - while ((DumpIndex < CFE_PLATFORM_TBL_MAX_SIMULTANEOUS_LOADS) && - (CFE_TBL_Global.DumpControlBlocks[DumpIndex].State != CFE_TBL_DUMP_FREE)) - { - DumpIndex++; - } - - if (DumpIndex < CFE_PLATFORM_TBL_MAX_SIMULTANEOUS_LOADS) + PendingDumpId = CFE_TBL_GetNextDumpCtrlBlock(); + DumpCtrlPtr = CFE_TBL_LocateDumpCtrlByID(CFE_TBL_DUMPCTRLID_C(PendingDumpId)); + if (DumpCtrlPtr != NULL) { /* Allocate a shared memory buffer for storing the data to be dumped */ Status = CFE_TBL_GetWorkingBuffer(&WorkingBufferPtr, RegRecPtr, false); if (Status == CFE_SUCCESS) { - DumpCtrlPtr = &CFE_TBL_Global.DumpControlBlocks[DumpIndex]; DumpCtrlPtr->State = CFE_TBL_DUMP_PENDING; DumpCtrlPtr->RegRecPtr = RegRecPtr; @@ -634,7 +609,8 @@ int32 CFE_TBL_DumpCmd(const CFE_TBL_DumpCmd_t *data) DumpCtrlPtr->Size = RegRecPtr->Size; /* Notify the owning application that a dump is pending */ - RegRecPtr->DumpControlIndex = DumpIndex; + CFE_TBL_DumpCtrlBlockSetUsed(DumpCtrlPtr, PendingDumpId); + RegRecPtr->DumpControlId = CFE_TBL_DumpCtrlBlockGetId(DumpCtrlPtr); /* If application requested notification by message, then do so */ CFE_TBL_SendNotificationMsg(RegRecPtr); diff --git a/modules/tbl/fsw/src/cfe_tbl_transaction.c b/modules/tbl/fsw/src/cfe_tbl_transaction.c index 479ac1e27..63beb11a3 100644 --- a/modules/tbl/fsw/src/cfe_tbl_transaction.c +++ b/modules/tbl/fsw/src/cfe_tbl_transaction.c @@ -289,7 +289,7 @@ CFE_Status_t CFE_TBL_TxnGetTableStatus(CFE_TBL_TxnState_t *Txn) { Status = CFE_TBL_INFO_VALIDATION_PENDING; } - else if (RegRecPtr->DumpControlIndex != CFE_TBL_NO_DUMP_PENDING) + else if (CFE_RESOURCEID_TEST_DEFINED(RegRecPtr->DumpControlId)) { Status = CFE_TBL_INFO_DUMP_PENDING; } diff --git a/modules/tbl/ut-coverage/tbl_UT.c b/modules/tbl/ut-coverage/tbl_UT.c index 3affe155d..3d245b005 100644 --- a/modules/tbl/ut-coverage/tbl_UT.c +++ b/modules/tbl/ut-coverage/tbl_UT.c @@ -159,6 +159,46 @@ void UT_TBL_ResetValidationState(uint32 ArrayIndex) memset(ValResultPtr, 0, sizeof(*ValResultPtr)); } +void UT_TBL_SetupPendingDump(uint32 ArrayIndex, CFE_TBL_LoadBuff_t *DumpBufferPtr, CFE_TBL_RegistryRec_t *RegRecPtr, + CFE_TBL_DumpControl_t **DumpCtrlOut) +{ + CFE_TBL_DumpControl_t *DumpCtrlPtr; + CFE_ResourceId_t PendingId; + + DumpCtrlPtr = &CFE_TBL_Global.DumpControlBlocks[ArrayIndex]; + + PendingId = CFE_ResourceId_FromInteger(CFE_TBL_DUMPCTRLID_BASE + ArrayIndex); + + memset(DumpCtrlPtr, 0, sizeof(*DumpCtrlPtr)); + + DumpCtrlPtr->State = CFE_TBL_DUMP_PENDING; + DumpCtrlPtr->BlockId = CFE_TBL_DUMPCTRLID_C(PendingId); + DumpCtrlPtr->RegRecPtr = RegRecPtr; + DumpCtrlPtr->DumpBufferPtr = DumpBufferPtr; + DumpCtrlPtr->Size = 1; + + snprintf(DumpCtrlPtr->TableName, sizeof(DumpCtrlPtr->TableName), "ut_cfe_tbl.UT_Table%u", + (unsigned int)ArrayIndex + 1); + + if (RegRecPtr != NULL) + { + RegRecPtr->DumpControlId = DumpCtrlPtr->BlockId; + } + + if (DumpCtrlOut != NULL) + { + *DumpCtrlOut = DumpCtrlPtr; + } +} + +/* Resets the indicated dump control block to the free/unused state */ +void UT_TBL_ResetDumpCtrlState(uint32 ArrayIndex) +{ + CFE_TBL_DumpControl_t *DumpCtrlPtr; + DumpCtrlPtr = &CFE_TBL_Global.DumpControlBlocks[ArrayIndex]; + memset(DumpCtrlPtr, 0, sizeof(*DumpCtrlPtr)); +} + /* ** Functions */ @@ -181,6 +221,7 @@ void UtTest_Setup(void) UT_ADD_TEST(Test_CFE_TBL_ResourceID_ValidationResult); UT_ADD_TEST(Test_CFE_TBL_ResourceID_RegistryRecord); UT_ADD_TEST(Test_CFE_TBL_ResourceID_AccessDescriptor); + UT_ADD_TEST(Test_CFE_TBL_ResourceID_DumpControl); /* cfe_tbl_task_cmds.c functions */ /* This should be done first (it initializes working data structures) */ @@ -269,10 +310,7 @@ void UT_ResetTableRegistry(void) /* Initialize the dump-only table dump control blocks */ for (i = 0; i < CFE_PLATFORM_TBL_MAX_SIMULTANEOUS_LOADS; i++) { - CFE_TBL_Global.DumpControlBlocks[i].State = CFE_TBL_DUMP_FREE; - CFE_TBL_Global.DumpControlBlocks[i].DumpBufferPtr = NULL; - CFE_TBL_Global.DumpControlBlocks[i].Size = 0; - CFE_TBL_Global.DumpControlBlocks[i].TableName[0] = '\0'; + UT_TBL_ResetDumpCtrlState(i); /* Free all shared buffers */ CFE_TBL_Global.LoadBuffs[i].Taken = false; @@ -1071,7 +1109,7 @@ void Test_CFE_TBL_DumpRegCmd(void) */ void Test_CFE_TBL_DumpCmd(void) { - int i, k, u; + int i, u; uint8 Buff; uint8 * BuffPtr = &Buff; CFE_TBL_LoadBuff_t Load = {0}; @@ -1107,9 +1145,9 @@ void Test_CFE_TBL_DumpCmd(void) CFE_TBL_Global.Registry[i].DumpOnly = true; } - CFE_TBL_Global.DumpControlBlocks[2].State = CFE_TBL_DUMP_PENDING; - CFE_TBL_Global.DumpControlBlocks[3].State = CFE_TBL_DUMP_FREE; - CFE_TBL_Global.Registry[2].DumpControlIndex = CFE_TBL_NO_DUMP_PENDING; + UT_TBL_SetupPendingDump(2, NULL, NULL, NULL); + UT_TBL_ResetDumpCtrlState(3); + CFE_TBL_Global.Registry[2].DumpControlId = CFE_TBL_NO_DUMP_PENDING; CFE_TBL_Global.Registry[2].LoadInProgress = CFE_TBL_NO_LOAD_IN_PROGRESS + 1; CFE_TBL_Global.Registry[2].DoubleBuffered = false; CFE_TBL_Global.LoadBuffs[CFE_TBL_Global.Registry[2].LoadInProgress] = Load; @@ -1123,11 +1161,11 @@ void Test_CFE_TBL_DumpCmd(void) * available */ UT_InitData(); - CFE_TBL_Global.DumpControlBlocks[2].State = CFE_TBL_DUMP_FREE; - CFE_TBL_Global.Registry[2].DumpControlIndex = CFE_TBL_NO_DUMP_PENDING; - CFE_TBL_Global.Registry[2].LoadInProgress = CFE_TBL_NO_LOAD_IN_PROGRESS; - CFE_TBL_Global.Registry[2].TableLoadedOnce = true; - CFE_TBL_Global.Registry[2].DoubleBuffered = false; + UT_TBL_ResetDumpCtrlState(2); + CFE_TBL_Global.Registry[2].DumpControlId = CFE_TBL_NO_DUMP_PENDING; + CFE_TBL_Global.Registry[2].LoadInProgress = CFE_TBL_NO_LOAD_IN_PROGRESS; + CFE_TBL_Global.Registry[2].TableLoadedOnce = true; + CFE_TBL_Global.Registry[2].DoubleBuffered = false; for (u = 0; u < CFE_PLATFORM_TBL_MAX_SIMULTANEOUS_LOADS; u++) { @@ -1142,14 +1180,9 @@ void Test_CFE_TBL_DumpCmd(void) * dump only table dumps have been requested */ UT_InitData(); - CFE_TBL_Global.Registry[2].DumpControlIndex = CFE_TBL_NO_DUMP_PENDING; - - for (k = 0; k < CFE_PLATFORM_TBL_MAX_SIMULTANEOUS_LOADS; k++) - { - CFE_TBL_Global.DumpControlBlocks[k].State = CFE_TBL_DUMP_PENDING; - } - - CFE_TBL_Global.Registry[2].NotifyByMsg = true; + CFE_TBL_Global.Registry[2].DumpControlId = CFE_TBL_NO_DUMP_PENDING; + CFE_TBL_Global.Registry[2].NotifyByMsg = true; + UT_SetDeferredRetcode(UT_KEY(CFE_ResourceId_FindNext), 1, 0); UtAssert_INT32_EQ(CFE_TBL_DumpCmd(&DumpCmd), CFE_TBL_INC_ERR_CTR); /* Test with an inactive buffer, double-buffered, dump already in progress; @@ -1159,7 +1192,7 @@ void Test_CFE_TBL_DumpCmd(void) DumpCmd.Payload.ActiveTableFlag = CFE_TBL_BufferSelect_INACTIVE; CFE_TBL_Global.Registry[2].DoubleBuffered = true; CFE_TBL_Global.Registry[2].Buffers[(1 - CFE_TBL_Global.Registry[2].ActiveBufferIndex)].BufferPtr = BuffPtr; - CFE_TBL_Global.Registry[2].DumpControlIndex = CFE_TBL_NO_DUMP_PENDING + 1; + CFE_TBL_Global.Registry[2].DumpControlId = CFE_TBL_DUMPCTRLID_C(CFE_ResourceId_FromInteger(1)); UtAssert_INT32_EQ(CFE_TBL_DumpCmd(&DumpCmd), CFE_TBL_INC_ERR_CTR); /* Test with an inactive buffer, single-buffered, pointer created, is a @@ -1368,13 +1401,14 @@ void Test_CFE_TBL_LoadCmd(void) */ void Test_CFE_TBL_SendHkCmd(void) { - int i; - CFE_TBL_LoadBuff_t DumpBuff; - CFE_TBL_LoadBuff_t * DumpBuffPtr = &DumpBuff; - CFE_TBL_RegistryRec_t RegRecPtr; - uint8 Buff; - void * BuffPtr = &Buff; - int32 LoadInProg = 0; + int i; + CFE_TBL_LoadBuff_t DumpBuff; + CFE_TBL_LoadBuff_t * DumpBuffPtr = &DumpBuff; + CFE_TBL_RegistryRec_t *RegRecPtr; + uint8 Buff; + void * BuffPtr = &Buff; + int32 LoadInProg = 0; + CFE_TBL_DumpControl_t *DumpCtrlPtr; UtPrintf("Begin Test Housekeeping Command"); @@ -1382,25 +1416,21 @@ void Test_CFE_TBL_SendHkCmd(void) * to send Hk packet */ UT_InitData(); - strncpy(CFE_TBL_Global.DumpControlBlocks[0].TableName, "housekeepingtest", - sizeof(CFE_TBL_Global.DumpControlBlocks[0].TableName) - 1); - CFE_TBL_Global.DumpControlBlocks[0].TableName[sizeof(CFE_TBL_Global.DumpControlBlocks[0].TableName) - 1] = '\0'; - CFE_TBL_Global.DumpControlBlocks[0].Size = 10; - LoadInProg = CFE_TBL_NO_LOAD_IN_PROGRESS + 1; - RegRecPtr.LoadInProgress = LoadInProg; - CFE_TBL_Global.DumpControlBlocks[0].RegRecPtr = &RegRecPtr; - DumpBuffPtr->Taken = true; - DumpBuffPtr->Validated = true; - DumpBuffPtr->BufferPtr = BuffPtr; - DumpBuffPtr->FileTime = CFE_TIME_ZERO_VALUE; + RegRecPtr = &CFE_TBL_Global.Registry[0]; + UT_TBL_SetupPendingDump(0, DumpBuffPtr, RegRecPtr, &DumpCtrlPtr); + LoadInProg = CFE_TBL_NO_LOAD_IN_PROGRESS + 1; + RegRecPtr->LoadInProgress = LoadInProg; + DumpBuffPtr->Taken = true; + DumpBuffPtr->Validated = true; + DumpBuffPtr->BufferPtr = BuffPtr; + DumpBuffPtr->FileTime = CFE_TIME_ZERO_VALUE; strncpy(DumpBuffPtr->DataSource, "hkSource", sizeof(DumpBuffPtr->DataSource) - 1); DumpBuffPtr->DataSource[sizeof(DumpBuffPtr->DataSource) - 1] = '\0'; - CFE_TBL_Global.DumpControlBlocks[0].DumpBufferPtr = DumpBuffPtr; - CFE_TBL_Global.DumpControlBlocks[0].State = CFE_TBL_DUMP_PERFORMED; + DumpCtrlPtr->State = CFE_TBL_DUMP_PERFORMED; for (i = 1; i < CFE_PLATFORM_TBL_MAX_SIMULTANEOUS_LOADS; i++) { - CFE_TBL_Global.DumpControlBlocks[i].State = CFE_TBL_DUMP_PENDING; + UT_TBL_SetupPendingDump(i, NULL, NULL, NULL); } UT_SetDeferredRetcode(UT_KEY(CFE_SB_TransmitMsg), 1, CFE_SUCCESS - 1); @@ -1409,35 +1439,38 @@ void Test_CFE_TBL_SendHkCmd(void) for (i = 1; i < CFE_PLATFORM_TBL_MAX_SIMULTANEOUS_LOADS; i++) { - CFE_TBL_Global.DumpControlBlocks[i].State = CFE_TBL_DUMP_PENDING; + UT_TBL_SetupPendingDump(i, NULL, NULL, NULL); } - RegRecPtr.LoadInProgress = LoadInProg; - CFE_TBL_Global.DumpControlBlocks[0].RegRecPtr = &RegRecPtr; + RegRecPtr->LoadInProgress = LoadInProg; /* Test response to inability to open dump file */ UT_InitData(); - CFE_TBL_Global.DumpControlBlocks[0].State = CFE_TBL_DUMP_PERFORMED; - CFE_TBL_Global.HkTlmTblRegIndex = CFE_TBL_NOT_FOUND + 1; + UT_TBL_SetupPendingDump(0, DumpBuffPtr, RegRecPtr, &DumpCtrlPtr); + DumpCtrlPtr->State = CFE_TBL_DUMP_PERFORMED; + CFE_TBL_Global.HkTlmTblRegIndex = CFE_TBL_NOT_FOUND + 1; UT_SetDefaultReturnValue(UT_KEY(OS_OpenCreate), OS_ERROR); UtAssert_INT32_EQ(CFE_TBL_SendHkCmd(NULL), CFE_TBL_DONT_INC_CTR); /* Test response to an invalid table and a dump file create failure */ UT_InitData(); - CFE_TBL_Global.HkTlmTblRegIndex = CFE_TBL_NOT_FOUND; - CFE_TBL_Global.DumpControlBlocks[0].State = CFE_TBL_DUMP_PERFORMED; + UT_TBL_SetupPendingDump(0, DumpBuffPtr, RegRecPtr, &DumpCtrlPtr); + CFE_TBL_Global.HkTlmTblRegIndex = CFE_TBL_NOT_FOUND; + DumpCtrlPtr->State = CFE_TBL_DUMP_PERFORMED; UT_SetDefaultReturnValue(UT_KEY(OS_OpenCreate), OS_ERROR); UtAssert_INT32_EQ(CFE_TBL_SendHkCmd(NULL), CFE_TBL_DONT_INC_CTR); /* Test response to a file time stamp failure */ UT_InitData(); - CFE_TBL_Global.DumpControlBlocks[0].State = CFE_TBL_DUMP_PERFORMED; + UT_TBL_SetupPendingDump(0, DumpBuffPtr, RegRecPtr, &DumpCtrlPtr); + DumpCtrlPtr->State = CFE_TBL_DUMP_PERFORMED; UT_SetDeferredRetcode(UT_KEY(CFE_FS_SetTimestamp), 1, OS_SUCCESS - 1); UtAssert_INT32_EQ(CFE_TBL_SendHkCmd(NULL), CFE_TBL_DONT_INC_CTR); /* Test response to OS_OpenCreate failure */ UT_InitData(); - CFE_TBL_Global.DumpControlBlocks[0].State = CFE_TBL_DUMP_PERFORMED; + UT_TBL_SetupPendingDump(0, DumpBuffPtr, RegRecPtr, &DumpCtrlPtr); + DumpCtrlPtr->State = CFE_TBL_DUMP_PERFORMED; UT_SetDeferredRetcode(UT_KEY(OS_OpenCreate), 3, -1); UtAssert_INT32_EQ(CFE_TBL_SendHkCmd(NULL), CFE_TBL_DONT_INC_CTR); } @@ -2620,6 +2653,7 @@ void Test_CFE_TBL_Manage(void) CFE_TBL_AccessDescriptor_t *AccessDescPtr; CFE_TBL_Handle_t AccessIterator; CFE_TBL_ValidationResult_t *ValResultPtr; + CFE_TBL_DumpControl_t * DumpCtrlPtr; memset(&TestTable1, 0, sizeof(TestTable1)); @@ -2882,22 +2916,13 @@ void Test_CFE_TBL_Manage(void) /* Test successfully processing a table dump request */ UT_InitData(); CFE_UtAssert_SUCCESS(CFE_TBL_GetWorkingBuffer(&WorkingBufferPtr, RegRecPtr, false)); - CFE_TBL_Global.DumpControlBlocks[0].State = CFE_TBL_DUMP_PENDING; - CFE_TBL_Global.DumpControlBlocks[0].RegRecPtr = RegRecPtr; + UT_TBL_SetupPendingDump(0, WorkingBufferPtr, RegRecPtr, &DumpCtrlPtr); /* Save the name of the desired dump filename, table name, and size for * later */ - CFE_TBL_Global.DumpControlBlocks[0].DumpBufferPtr = WorkingBufferPtr; - strncpy(CFE_TBL_Global.DumpControlBlocks[0].DumpBufferPtr->DataSource, "MyDumpFilename", - sizeof(CFE_TBL_Global.DumpControlBlocks[0].DumpBufferPtr->DataSource) - 1); - CFE_TBL_Global.DumpControlBlocks[0] - .DumpBufferPtr->DataSource[sizeof(CFE_TBL_Global.DumpControlBlocks[0].DumpBufferPtr->DataSource) - 1] = 0; - strncpy(CFE_TBL_Global.DumpControlBlocks[0].TableName, "ut_cfe_tbl.UT_Table2", - sizeof(CFE_TBL_Global.DumpControlBlocks[0].TableName) - 1); - CFE_TBL_Global.DumpControlBlocks[0].TableName[sizeof(CFE_TBL_Global.DumpControlBlocks[0].TableName) - 1] = 0; - CFE_TBL_Global.DumpControlBlocks[0].Size = RegRecPtr->Size; - RegRecPtr->DumpControlIndex = 0; + strncpy(WorkingBufferPtr->DataSource, "MyDumpFilename", sizeof(WorkingBufferPtr->DataSource) - 1); + WorkingBufferPtr->DataSource[sizeof(WorkingBufferPtr->DataSource) - 1] = 0; CFE_UtAssert_SUCCESS(CFE_TBL_Manage(App1TblHandle2)); CFE_UtAssert_EVENTCOUNT(0); } @@ -3225,6 +3250,7 @@ void Test_CFE_TBL_Internal(void) CFE_TBL_LoadBuff_t * WorkingBufferPtr; CFE_TBL_RegistryRec_t * RegRecPtr; CFE_TBL_AccessDescriptor_t *AccessDescPtr; + CFE_TBL_DumpControl_t * DumpCtrlPtr; char FilenameLong[OS_MAX_PATH_LEN + 10]; char Filename[OS_MAX_PATH_LEN]; int32 i; @@ -3762,16 +3788,18 @@ void Test_CFE_TBL_Internal(void) UT_InitData(); UT_SetAppID(UT_TBL_APPID_1); UT_SetDefaultReturnValue(UT_KEY(CFE_ES_PutPoolBuf), -1); - AccessDescPtr = &CFE_TBL_Global.Handles[App1TblHandle1]; - RegRecPtr = &CFE_TBL_Global.Registry[AccessDescPtr->RegIndex]; - CFE_TBL_Global.DumpControlBlocks[3].State = CFE_TBL_DUMP_PENDING; - CFE_TBL_Global.DumpControlBlocks[3].RegRecPtr = RegRecPtr; - RegRecPtr->LoadInProgress = 1; - CFE_TBL_Global.LoadBuffs[1].Taken = true; + AccessDescPtr = &CFE_TBL_Global.Handles[App1TblHandle1]; + RegRecPtr = &CFE_TBL_Global.Registry[AccessDescPtr->RegIndex]; + WorkingBufferPtr = &CFE_TBL_Global.LoadBuffs[1]; + + UT_TBL_SetupPendingDump(3, WorkingBufferPtr, RegRecPtr, &DumpCtrlPtr); + + RegRecPtr->LoadInProgress = 1; + WorkingBufferPtr->Taken = true; CFE_UtAssert_SUCCESS(CFE_TBL_CleanUpApp(UT_TBL_APPID_1)); - UtAssert_INT32_EQ(CFE_TBL_Global.DumpControlBlocks[3].State, CFE_TBL_DUMP_FREE); + UtAssert_INT32_EQ(DumpCtrlPtr->State, CFE_TBL_DUMP_FREE); CFE_UtAssert_RESOURCEID_EQ(RegRecPtr->OwnerAppId, CFE_TBL_NOT_OWNED); - UtAssert_BOOL_FALSE(CFE_TBL_Global.LoadBuffs[RegRecPtr->LoadInProgress].Taken); + UtAssert_BOOL_FALSE(WorkingBufferPtr->Taken); UtAssert_INT32_EQ(RegRecPtr->LoadInProgress, CFE_TBL_NO_LOAD_IN_PROGRESS); /* Test response to an attempt to use an invalid table handle */ @@ -3900,16 +3928,18 @@ void Test_CFE_TBL_Internal(void) UT_InitData(); UT_SetAppID(UT_TBL_APPID_1); UT_SetDefaultReturnValue(UT_KEY(CFE_ES_PutPoolBuf), -1); - CFE_TBL_Global.Handles[0].AppId = UT_TBL_APPID_1; - CFE_TBL_Global.Handles[0].UsedFlag = true; - CFE_TBL_Global.Handles[0].RegIndex = 0; - CFE_TBL_Global.Registry[0].OwnerAppId = UT_TBL_APPID_2; - CFE_TBL_Global.DumpControlBlocks[3].State = CFE_TBL_DUMP_PENDING; - CFE_TBL_Global.DumpControlBlocks[3].RegRecPtr = &CFE_TBL_Global.Registry[0]; - CFE_TBL_Global.Handles[1].AppId = UT_TBL_APPID_1; - CFE_TBL_Global.Handles[1].UsedFlag = false; + RegRecPtr = &CFE_TBL_Global.Registry[0]; + UT_TBL_SetupPendingDump(3, NULL, RegRecPtr, &DumpCtrlPtr); + + CFE_TBL_Global.Handles[0].AppId = UT_TBL_APPID_1; + CFE_TBL_Global.Handles[0].UsedFlag = true; + CFE_TBL_Global.Handles[0].RegIndex = 0; + RegRecPtr->OwnerAppId = UT_TBL_APPID_2; + CFE_TBL_Global.Handles[1].AppId = UT_TBL_APPID_1; + CFE_TBL_Global.Handles[1].UsedFlag = false; + CFE_UtAssert_SUCCESS(CFE_TBL_CleanUpApp(UT_TBL_APPID_1)); - UtAssert_INT32_EQ(CFE_TBL_Global.DumpControlBlocks[3].State, CFE_TBL_DUMP_PENDING); + UtAssert_INT32_EQ(DumpCtrlPtr->State, CFE_TBL_DUMP_PENDING); CFE_UtAssert_RESOURCEID_EQ(RegRecPtr->OwnerAppId, UT_TBL_APPID_2); #if (CFE_PLATFORM_TBL_VALID_SCID_COUNT > 0) @@ -4103,6 +4133,49 @@ void Test_CFE_TBL_ResourceID_AccessDescriptor(void) UtAssert_UINT32_EQ(Idx, 1); } +/* + * Tests the resource accessors for Dump Control Blocks + */ +void Test_CFE_TBL_ResourceID_DumpControl(void) +{ + uint32 Idx; + CFE_TBL_DumpCtrlId_t InvalidBlockId; + CFE_TBL_DumpCtrlId_t ValidBlockId; + CFE_ResourceId_t PendingId; + + UT_InitData(); + + InvalidBlockId = CFE_TBL_DUMPCTRLID_UNDEFINED; + UT_SetDefaultReturnValue(UT_KEY(CFE_ResourceId_ToIndex), CFE_ES_ERR_RESOURCEID_NOT_VALID); + UtAssert_INT32_EQ(CFE_TBL_DumpCtrlId_ToIndex(InvalidBlockId, &Idx), CFE_ES_ERR_RESOURCEID_NOT_VALID); + + /* by definition, looking up the undefined value should always be NULL */ + UtAssert_NULL(CFE_TBL_LocateDumpCtrlByID(InvalidBlockId)); + UT_ResetState(UT_KEY(CFE_ResourceId_ToIndex)); + + ValidBlockId = CFE_TBL_DUMPCTRLID_C(CFE_ResourceId_FromInteger(CFE_TBL_DUMPCTRLID_BASE + 1)); + UtAssert_INT32_EQ(CFE_TBL_DumpCtrlId_ToIndex(ValidBlockId, &Idx), CFE_SUCCESS); + + UtAssert_VOIDCALL(PendingId = CFE_TBL_GetNextDumpCtrlBlock()); + UtAssert_BOOL_TRUE(CFE_ResourceId_IsDefined(PendingId)); + + /* The slot should be available right now */ + UtAssert_BOOL_FALSE(CFE_TBL_CheckDumpCtrlSlotUsed(PendingId)); + + /* Make it used and confirm it is reported as not available */ + CFE_TBL_DumpCtrlBlockSetUsed(CFE_TBL_LocateDumpCtrlByID(CFE_TBL_DUMPCTRLID_C(PendingId)), PendingId); + UtAssert_BOOL_TRUE(CFE_TBL_CheckDumpCtrlSlotUsed(PendingId)); + + /* Test case where no ID is available */ + UT_SetDefaultReturnValue(UT_KEY(CFE_ResourceId_FindNext), 0); + UtAssert_VOIDCALL(PendingId = CFE_TBL_GetNextDumpCtrlBlock()); + UtAssert_BOOL_FALSE(CFE_ResourceId_IsDefined(PendingId)); + + /* A nonexistent slot is always "unavailable" */ + UtAssert_BOOL_TRUE(CFE_TBL_CheckDumpCtrlSlotUsed(PendingId)); + UT_ResetState(UT_KEY(CFE_ResourceId_FindNext)); +} + /* ** Test function executed when the contents of a table need to be validated */ diff --git a/modules/tbl/ut-coverage/tbl_UT.h b/modules/tbl/ut-coverage/tbl_UT.h index 95fd0d97b..b4b5fd8db 100644 --- a/modules/tbl/ut-coverage/tbl_UT.h +++ b/modules/tbl/ut-coverage/tbl_UT.h @@ -684,5 +684,6 @@ int32 Test_CFE_TBL_ValidationFunc(void *TblPtr); void Test_CFE_TBL_ResourceID_ValidationResult(void); void Test_CFE_TBL_ResourceID_RegistryRecord(void); void Test_CFE_TBL_ResourceID_AccessDescriptor(void); +void Test_CFE_TBL_ResourceID_DumpControl(void); #endif /* TBL_UT_H */