SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause copyrights: - Copyright (C) 2021, 2024 embedded brains GmbH & Co. KG enabled-by: true links: [] test-actions: - action-brief: | Construct a task with a task body which returns. Check that the right fatal error occurred. action-code: | 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 ); checks: [] links: - role: validation uid: ../thread/req/fatal-thread-exitted - action-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. action-code: | 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 ); checks: [] links: - role: validation uid: ../thread/req/fatal-bad-thread-dispatch-environment-ipi - role: validation uid: ../thread/req/fatal-bad-thread-dispatch-environment-robust - role: validation uid: ../thread/req/thread-dispatch-isr-disabled - action-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. action-code: | 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 ); checks: [] links: - role: validation uid: ../thread/req/fatal-bad-thread-dispatch-environment-ipi - role: validation uid: ../thread/req/fatal-bad-thread-dispatch-environment-robust - role: validation uid: ../thread/req/thread-dispatch-isr-disabled - action-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. action-code: | 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 ); checks: [] links: - role: validation uid: ../thread/req/fatal-bad-thread-dispatch-disable-level - action-brief: | Create a mutex and construct a task which produces a deadlock which involves the allocator mutex. action-code: | 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 ); checks: [] links: - role: validation uid: ../object/req/fatal-allocator-mutex-deadlock - action-brief: | Check that ${/rtems/fatal/if/fatal:/name} terminates the system. Since SetFatalHandler() requires an initial extension this validates ${/acfg/if/initial-extensions:/name}. action-code: | 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 ); checks: [] links: - role: validation uid: /rtems/fatal/req/fatal - role: validation uid: /acfg/req/initial-extensions test-brief: | Tests some fatal errors. test-context: - brief: | This member is a fatal extension invocation counter. description: null member: | Atomic_Uint counter - brief: | This member contains the last fatal source. description: null member: | rtems_fatal_source source - brief: | This member contains the last fatal code. description: null member: | rtems_fatal_code code test-context-support: null test-description: null test-header: null test-includes: - rtems.h - rtems/score/atomic.h - rtems/score/isrlevel.h - rtems/score/threaddispatch.h - setjmp.h - string.h test-local-includes: - tx-support.h test-setup: null test-stop: null test-support: | typedef ${.:/test-context-type} 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(); } test-target: testsuites/validation/tc-score-fatal.c test-teardown: null type: test-case