/* SPDX-License-Identifier: BSD-2-Clause */ /** * @file * * @ingroup ScoreValFatal */ /* * Copyright (C) 2021, 2024 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * This file is part of the RTEMS quality process and was automatically * generated. If you find something that needs to be fixed or * worded better please post a report or patch to an RTEMS mailing list * or raise a bug report: * * https://www.rtems.org/bugs.html * * For information on updating and regenerating please refer to the How-To * section in the Software Requirements Engineering chapter of the * RTEMS Software Engineering manual. The manual is provided as a part of * a release. For development sources please refer to the online * documentation at: * * https://docs.rtems.org */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include "tx-support.h" #include /** * @defgroup ScoreValFatal spec:/score/val/fatal * * @ingroup TestsuitesValidationNoClock0 * @ingroup TestsuitesValidationOneCpu0 * * @brief Tests some fatal errors. * * This test case performs the following actions: * * - Construct a task with a task body which returns. Check that the right * fatal error occurred. * * - Construct a task which performs a direct thread dispatch with maskable * interrupts disabled. Where robust thread dispatching is required, check * that the right fatal error occurred, otherwise check that no fatal error * occurred. * * - Construct a task which performs an on demand thread dispatch with maskable * interrupts disabled. Where robust thread dispatching is required, check * that the right fatal error occurred, otherwise check that no fatal error * occurred. * * - Construct a task which performs a direct thread dispatch with a thread * dispatch level not equal to one. Check that the right fatal error * occurred. * * - Create a mutex and construct a task which produces a deadlock which * involves the allocator mutex. * * - Check that rtems_fatal() terminates the system. Since SetFatalHandler() * requires an initial extension this validates CONFIGURE_INITIAL_EXTENSIONS. * * @{ */ /** * @brief Test context for spec:/score/val/fatal test case. */ typedef struct { /** * @brief This member is a fatal extension invocation counter. */ Atomic_Uint counter; /** * @brief This member contains the last fatal source. */ rtems_fatal_source source; /** * @brief This member contains the last fatal code. */ rtems_fatal_code code; } ScoreValFatal_Context; static ScoreValFatal_Context ScoreValFatal_Instance; typedef ScoreValFatal_Context Context; static unsigned int GetFatalCounter( const Context *ctx ) { return _Atomic_Load_uint( &ctx->counter, ATOMIC_ORDER_RELAXED ); } static unsigned int ResetFatalInfo( Context *ctx ) { ctx->source = RTEMS_FATAL_SOURCE_APPLICATION; ctx->code = INTERNAL_ERROR_NO_MPCI; return GetFatalCounter( ctx ); } static void Fatal( rtems_fatal_source source, rtems_fatal_code code, Context *ctx ) { ctx->source = source; ctx->code = code; _Atomic_Fetch_add_uint( &ctx->counter, 1, ATOMIC_ORDER_RELAXED ); } static void FatalTaskExit( rtems_fatal_source source, rtems_fatal_code code, void *arg ) { Fatal( source, code, arg ); rtems_task_exit(); } static void ExitTask( rtems_task_argument arg ) { (void) arg; } static void FatalBadThreadDispatchEnvironment( rtems_fatal_source source, rtems_fatal_code code, void *arg ) { Fatal( source, code, arg ); _ISR_Set_level( 0 ); _Thread_Dispatch_unnest( _Per_CPU_Get() ); rtems_task_exit(); } static void ISRDisabledDirectThreadDispatchTask( rtems_task_argument arg ) { rtems_interrupt_level level; (void) arg; rtems_interrupt_local_disable( level ); (void) level; rtems_task_exit(); } static void ISRDisabledOnDemandThreadDispatchTask( rtems_task_argument arg ) { rtems_interrupt_level level; (void) arg; rtems_interrupt_local_disable( level ); (void) level; SetSelfPriority( PRIO_VERY_HIGH ); } static void FatalBadThreadDispatchDisableLevel( rtems_fatal_source source, rtems_fatal_code code, void *arg ) { Per_CPU_Control *cpu_self; Fatal( source, code, arg ); cpu_self = _Per_CPU_Get(); _Thread_Dispatch_unnest( cpu_self ); _Thread_Dispatch_direct_no_return( cpu_self ); } static void BadLevelThreadDispatchTask( rtems_task_argument arg ) { (void) arg; _Thread_Dispatch_disable(); rtems_task_exit(); } static jmp_buf before_fatal; static rtems_id deadlock_mutex; static bool ThreadCreateDeadlock( rtems_tcb *executing, rtems_tcb *created ) { (void) executing; (void) created; ObtainMutex( deadlock_mutex ); ReleaseMutex( deadlock_mutex ); return true; } static void FatalJumpBack( rtems_fatal_source source, rtems_fatal_code code, void *arg ) { SetFatalHandler( NULL, NULL ); Fatal( source, code, arg ); longjmp( before_fatal, 1 ); } static void ThreadQueueDeadlockTask( rtems_task_argument arg ) { rtems_id id; (void) arg; id = CreateTask( "DORM", PRIO_NORMAL ); DeleteTask( id ); rtems_task_exit(); } static T_fixture ScoreValFatal_Fixture = { .setup = NULL, .stop = NULL, .teardown = NULL, .scope = NULL, .initial_context = &ScoreValFatal_Instance }; /** * @brief Construct a task with a task body which returns. Check that the * right fatal error occurred. */ static void ScoreValFatal_Action_0( ScoreValFatal_Context *ctx ) { rtems_id id; unsigned int counter; SetFatalHandler( FatalTaskExit, ctx ); SetSelfPriority( PRIO_NORMAL ); counter = ResetFatalInfo( ctx ); id = CreateTask( "EXIT", PRIO_HIGH ); StartTask( id, ExitTask, NULL ); T_eq_uint( GetFatalCounter( ctx ), counter + 1 ); T_eq_int( ctx->source, INTERNAL_ERROR_CORE ); T_eq_ulong( ctx->code, INTERNAL_ERROR_THREAD_EXITTED ); RestoreRunnerPriority(); SetFatalHandler( NULL, NULL ); } /** * @brief Construct a task which performs a direct thread dispatch with * maskable interrupts disabled. Where robust thread dispatching is * required, check that the right fatal error occurred, otherwise check that * no fatal error occurred. */ static void ScoreValFatal_Action_1( ScoreValFatal_Context *ctx ) { rtems_id id; unsigned int counter; SetFatalHandler( FatalBadThreadDispatchEnvironment, ctx ); SetSelfPriority( PRIO_NORMAL ); counter = ResetFatalInfo( ctx ); id = CreateTask( "BENV", PRIO_HIGH ); StartTask( id, ISRDisabledDirectThreadDispatchTask, NULL ); #if CPU_ENABLE_ROBUST_THREAD_DISPATCH == FALSE if ( rtems_configuration_get_maximum_processors() > 1 ) { #endif T_eq_uint( GetFatalCounter( ctx ), counter + 1 ); T_eq_int( ctx->source, INTERNAL_ERROR_CORE ); T_eq_ulong( ctx->code, INTERNAL_ERROR_BAD_THREAD_DISPATCH_ENVIRONMENT ); #if CPU_ENABLE_ROBUST_THREAD_DISPATCH == FALSE } else { T_eq_uint( GetFatalCounter( ctx ), counter ); } #endif RestoreRunnerPriority(); SetFatalHandler( NULL, NULL ); } /** * @brief Construct a task which performs an on demand thread dispatch with * maskable interrupts disabled. Where robust thread dispatching is * required, check that the right fatal error occurred, otherwise check that * no fatal error occurred. */ static void ScoreValFatal_Action_2( ScoreValFatal_Context *ctx ) { rtems_id id; unsigned int counter; SetFatalHandler( FatalBadThreadDispatchEnvironment, ctx ); SetSelfPriority( PRIO_NORMAL ); counter = ResetFatalInfo( ctx ); id = CreateTask( "BENV", PRIO_HIGH ); StartTask( id, ISRDisabledOnDemandThreadDispatchTask, NULL ); #if CPU_ENABLE_ROBUST_THREAD_DISPATCH == FALSE if ( rtems_configuration_get_maximum_processors() > 1 ) { #endif T_eq_uint( GetFatalCounter( ctx ), counter + 1 ); T_eq_int( ctx->source, INTERNAL_ERROR_CORE ); T_eq_ulong( ctx->code, INTERNAL_ERROR_BAD_THREAD_DISPATCH_ENVIRONMENT ); #if CPU_ENABLE_ROBUST_THREAD_DISPATCH == FALSE } else { T_eq_uint( GetFatalCounter( ctx ), counter ); } #endif RestoreRunnerPriority(); SetFatalHandler( NULL, NULL ); } /** * @brief Construct a task which performs a direct thread dispatch with a * thread dispatch level not equal to one. Check that the right fatal error * occurred. */ static void ScoreValFatal_Action_3( ScoreValFatal_Context *ctx ) { rtems_id id; unsigned int counter; SetFatalHandler( FatalBadThreadDispatchDisableLevel, ctx ); SetSelfPriority( PRIO_NORMAL ); counter = ResetFatalInfo( ctx ); id = CreateTask( "BLVL", PRIO_HIGH ); StartTask( id, BadLevelThreadDispatchTask, NULL ); T_eq_uint( GetFatalCounter( ctx ), counter + 1 ); T_eq_int( ctx->source, INTERNAL_ERROR_CORE ); T_eq_ulong( ctx->code, INTERNAL_ERROR_BAD_THREAD_DISPATCH_DISABLE_LEVEL ); RestoreRunnerPriority(); SetFatalHandler( NULL, NULL ); } /** * @brief Create a mutex and construct a task which produces a deadlock which * involves the allocator mutex. */ static void ScoreValFatal_Action_4( ScoreValFatal_Context *ctx ) { rtems_extensions_table extensions; rtems_status_code sc; rtems_id extension_id; rtems_id task_id; unsigned int counter; memset( &extensions, 0, sizeof( extensions ) ); extensions.thread_create = ThreadCreateDeadlock; sc = rtems_extension_create( rtems_build_name( 'T', 'E', 'X', 'T' ), &extensions, &extension_id ); T_rsc_success( sc ); deadlock_mutex = CreateMutex(); SetFatalHandler( FatalJumpBack, ctx ); SetSelfPriority( PRIO_NORMAL ); counter = ResetFatalInfo( ctx ); ObtainMutex( deadlock_mutex ); task_id = CreateTask( "WORK", PRIO_HIGH ); StartTask( task_id, ThreadQueueDeadlockTask, NULL ); if ( setjmp( before_fatal ) == 0 ) { (void) CreateTask( "DLCK", PRIO_NORMAL ); } ReleaseMutex( deadlock_mutex ); T_eq_uint( GetFatalCounter( ctx ), counter + 1 ); T_eq_int( ctx->source, INTERNAL_ERROR_CORE ); T_eq_ulong( ctx->code, INTERNAL_ERROR_THREAD_QUEUE_DEADLOCK ); RestoreRunnerPriority(); sc = rtems_extension_delete( extension_id ); T_rsc_success( sc ); DeleteMutex( deadlock_mutex ); } /** * @brief Check that rtems_fatal() terminates the system. Since * SetFatalHandler() requires an initial extension this validates * CONFIGURE_INITIAL_EXTENSIONS. */ static void ScoreValFatal_Action_5( ScoreValFatal_Context *ctx ) { unsigned int counter; SetFatalHandler( FatalJumpBack, ctx ); counter = ResetFatalInfo( ctx ); if ( setjmp( before_fatal ) == 0 ) { rtems_fatal( 123, 4567890 ); } T_eq_uint( GetFatalCounter( ctx ), counter + 1 ); T_eq_int( ctx->source, 123 ); T_eq_ulong( ctx->code, 4567890 ); } /** * @fn void T_case_body_ScoreValFatal( void ) */ T_TEST_CASE_FIXTURE( ScoreValFatal, &ScoreValFatal_Fixture ) { ScoreValFatal_Context *ctx; ctx = T_fixture_context(); ScoreValFatal_Action_0( ctx ); ScoreValFatal_Action_1( ctx ); ScoreValFatal_Action_2( ctx ); ScoreValFatal_Action_3( ctx ); ScoreValFatal_Action_4( ctx ); ScoreValFatal_Action_5( ctx ); } /** @} */