/* SPDX-License-Identifier: BSD-2-Clause */ /** * @file * * @ingroup RTEMSTestSuitesValidation * * @brief This source file contains the implementation of support functions for * the validation test cases. */ /* * Copyright (C) 2021 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "tx-support.h" #include "ts-config.h" #include #include #include #include #include #include #include rtems_id DoCreateTask( rtems_name name, rtems_task_priority priority ) { rtems_status_code sc; rtems_id id; sc = rtems_task_create( name, priority, TEST_MINIMUM_STACK_SIZE, RTEMS_DEFAULT_MODES, RTEMS_DEFAULT_ATTRIBUTES, &id ); T_assert_rsc_success( sc ); return id; } void StartTask( rtems_id id, rtems_task_entry entry, void *arg ) { rtems_status_code sc; sc = rtems_task_start( id, entry, (rtems_task_argument) arg); T_assert_rsc_success( sc ); } void DeleteTask( rtems_id id ) { if ( id != 0 ) { rtems_status_code sc; sc = rtems_task_delete( id ); T_quiet_rsc_success( sc ); } } void SuspendTask( rtems_id id ) { rtems_status_code sc; sc = rtems_task_suspend( id ); T_quiet_rsc_success( sc ); } void SuspendSelf( void ) { SuspendTask( RTEMS_SELF ); } void ResumeTask( rtems_id id ) { rtems_status_code sc; sc = rtems_task_resume( id ); T_quiet_rsc_success( sc ); } bool IsTaskSuspended( rtems_id id ) { rtems_status_code sc; sc = rtems_task_is_suspended( id ); T_quiet_true( sc == RTEMS_SUCCESSFUL || sc == RTEMS_ALREADY_SUSPENDED ); return sc == RTEMS_ALREADY_SUSPENDED; } rtems_event_set QueryPendingEvents( void ) { rtems_status_code sc; rtems_event_set events; events = 0; sc = rtems_event_receive( RTEMS_PENDING_EVENTS, RTEMS_EVENT_ALL | RTEMS_NO_WAIT, 0, &events ); T_quiet_rsc_success( sc ); return events; } rtems_event_set PollAnyEvents( void ) { rtems_event_set events; events = 0; (void) rtems_event_receive( RTEMS_ALL_EVENTS, RTEMS_EVENT_ANY | RTEMS_NO_WAIT, 0, &events ); return events; } rtems_event_set ReceiveAnyEvents( void ) { return ReceiveAnyEventsTimed( RTEMS_NO_TIMEOUT ); } rtems_event_set ReceiveAnyEventsTimed( rtems_interval ticks ) { rtems_event_set events; events = 0; (void) rtems_event_receive( RTEMS_ALL_EVENTS, RTEMS_EVENT_ANY | RTEMS_WAIT, ticks, &events ); return events; } void ReceiveAllEvents( rtems_event_set events ) { rtems_status_code sc; rtems_event_set received; received = 0; sc = rtems_event_receive( events, RTEMS_EVENT_ALL | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &received ); T_quiet_rsc_success( sc ); T_quiet_eq_u32( received, events ); } void SendEvents( rtems_id id, rtems_event_set events ) { rtems_status_code sc; sc = rtems_event_send( id, events ); T_quiet_rsc_success( sc ); } rtems_mode GetMode( void ) { return SetMode( RTEMS_DEFAULT_MODES, RTEMS_CURRENT_MODE ); } rtems_mode SetMode( rtems_mode set, rtems_mode mask ) { rtems_status_code sc; rtems_mode previous; sc = rtems_task_mode( set, mask, &previous ); T_quiet_rsc_success( sc ); return previous; } rtems_task_priority GetPriority( rtems_id id ) { return SetPriority( id, RTEMS_CURRENT_PRIORITY ); } rtems_task_priority GetPriorityByScheduler( rtems_id task_id, rtems_id scheduler_id ) { rtems_status_code sc; rtems_task_priority priority; priority = PRIO_INVALID; sc = rtems_task_get_priority( task_id, scheduler_id, &priority ); if ( sc != RTEMS_SUCCESSFUL ) { return PRIO_INVALID; } return priority; } rtems_task_priority SetPriority( rtems_id id, rtems_task_priority priority ) { rtems_status_code sc; rtems_task_priority previous; previous = PRIO_INVALID; sc = rtems_task_set_priority( id, priority, &previous ); T_quiet_rsc_success( sc ); return previous; } rtems_task_priority GetSelfPriority( void ) { return SetPriority( RTEMS_SELF, RTEMS_CURRENT_PRIORITY ); } rtems_task_priority SetSelfPriority( rtems_task_priority priority ) { return SetPriority( RTEMS_SELF, priority ); } rtems_task_priority SetSelfPriorityNoYield( rtems_task_priority priority ) { rtems_status_code sc; rtems_id id; /* * If the priority is lowered, then this sequence ensures that we do not * carry out an implicit yield. */ sc = rtems_semaphore_create( rtems_build_name( 'T', 'E', 'M', 'P' ), 0, RTEMS_BINARY_SEMAPHORE | RTEMS_PRIORITY | RTEMS_PRIORITY_CEILING, 1, &id ); T_quiet_rsc_success( sc ); priority = SetSelfPriority( priority ); ReleaseMutex( id ); DeleteMutex( id ); return priority; } rtems_id GetScheduler( rtems_id id ) { rtems_status_code sc; rtems_id scheduler_id; scheduler_id = 0xffffffff; sc = rtems_task_get_scheduler( id, &scheduler_id ); T_quiet_rsc_success( sc ); return scheduler_id; } rtems_id GetSelfScheduler( void ) { return GetScheduler( RTEMS_SELF ); } void SetScheduler( rtems_id task_id, rtems_id scheduler_id, rtems_task_priority priority ) { rtems_status_code sc; sc = rtems_task_set_scheduler( task_id, scheduler_id, priority ); T_quiet_rsc_success( sc ); } void SetSelfScheduler( rtems_id scheduler_id, rtems_task_priority priority ) { SetScheduler( RTEMS_SELF, scheduler_id, priority ); } void GetAffinity( rtems_id id, cpu_set_t *set ) { rtems_status_code sc; CPU_ZERO( set ); sc = rtems_task_get_affinity( id, sizeof( *set ), set ); T_quiet_rsc_success( sc ); } void GetSelfAffinity( cpu_set_t *set ) { GetAffinity( RTEMS_SELF, set ); } void SetAffinity( rtems_id id, const cpu_set_t *set ) { rtems_status_code sc; sc = rtems_task_set_affinity( id, sizeof( *set ), set ); T_quiet_rsc_success( sc ); } void SetSelfAffinity( const cpu_set_t *set ) { SetAffinity( RTEMS_SELF, set ); } void SetAffinityOne( rtems_id id, uint32_t cpu_index ) { cpu_set_t set; CPU_ZERO( &set ); CPU_SET( (int) cpu_index, &set ); SetAffinity( id, &set ); } void SetSelfAffinityOne( uint32_t cpu_index ) { SetAffinityOne( RTEMS_SELF, cpu_index ); } void SetAffinityAll( rtems_id id ) { cpu_set_t set; CPU_FILL( &set ); SetAffinity( id, &set ); } void SetSelfAffinityAll( void ) { SetAffinityAll( RTEMS_SELF ); } void Yield( void ) { rtems_status_code sc; sc = rtems_task_wake_after( RTEMS_YIELD_PROCESSOR ); T_quiet_rsc_success( sc ); } void YieldTask( rtems_id id ) { Thread_Control *the_thread; ISR_lock_Context lock_context; Per_CPU_Control *cpu_self; the_thread = _Thread_Get( id, &lock_context ); if ( the_thread == NULL ) { return; } cpu_self = _Thread_Dispatch_disable_critical( &lock_context ); _ISR_lock_ISR_enable( &lock_context); _Thread_Yield( the_thread ); _Thread_Dispatch_direct( cpu_self ); } void AddProcessor( rtems_id scheduler_id, uint32_t cpu_index ) { rtems_status_code sc; sc = rtems_scheduler_add_processor( scheduler_id, cpu_index ); T_quiet_rsc_success( sc ); } void RemoveProcessor( rtems_id scheduler_id, uint32_t cpu_index ) { rtems_status_code sc; sc = rtems_scheduler_remove_processor( scheduler_id, cpu_index ); T_quiet_rsc_success( sc ); } rtems_id CreateMutex( void ) { rtems_status_code sc; rtems_id id; id = INVALID_ID; sc = rtems_semaphore_create( rtems_build_name( 'M', 'U', 'T', 'X' ), 1, RTEMS_BINARY_SEMAPHORE | RTEMS_PRIORITY | RTEMS_INHERIT_PRIORITY, 0, &id ); T_rsc_success( sc ); return id; } rtems_id CreateMutexNoProtocol( void ) { rtems_status_code sc; rtems_id id; id = INVALID_ID; sc = rtems_semaphore_create( rtems_build_name( 'M', 'U', 'T', 'X' ), 1, RTEMS_BINARY_SEMAPHORE | RTEMS_PRIORITY, 0, &id ); T_rsc_success( sc ); return id; } rtems_id CreateMutexFIFO( void ) { rtems_status_code sc; rtems_id id; id = INVALID_ID; sc = rtems_semaphore_create( rtems_build_name( 'M', 'U', 'T', 'X' ), 1, RTEMS_BINARY_SEMAPHORE | RTEMS_FIFO, 0, &id ); T_rsc_success( sc ); return id; } void DeleteMutex( rtems_id id ) { if ( id != INVALID_ID ) { rtems_status_code sc; sc = rtems_semaphore_delete( id ); T_rsc_success( sc ); } } bool IsMutexOwner( rtems_id id ) { Semaphore_Control *the_semaphore; Thread_queue_Context queue_context; the_semaphore = _Semaphore_Get( id, &queue_context ); if ( the_semaphore == NULL ) { return false; } _ISR_lock_ISR_enable( &queue_context.Lock_context.Lock_context ); return the_semaphore->Core_control.Wait_queue.Queue.owner == _Thread_Get_executing(); } void ObtainMutex( rtems_id id ) { rtems_status_code sc; sc = rtems_semaphore_obtain( id, RTEMS_WAIT, RTEMS_NO_TIMEOUT ); T_rsc_success( sc ); } void ObtainMutexTimed( rtems_id id, rtems_interval ticks ) { rtems_status_code sc; sc = rtems_semaphore_obtain( id, RTEMS_WAIT, ticks ); T_rsc_success( sc ); } void ObtainMutexDeadlock( rtems_id id ) { rtems_status_code sc; sc = rtems_semaphore_obtain( id, RTEMS_WAIT, RTEMS_NO_TIMEOUT ); T_rsc( sc, RTEMS_INCORRECT_STATE ); } void ReleaseMutex( rtems_id id ) { rtems_status_code sc; sc = rtems_semaphore_release( id ); T_rsc_success( sc ); } Thread_queue_Queue *GetMutexThreadQueue( rtems_id id ) { Semaphore_Control *the_semaphore; Thread_queue_Context queue_context; the_semaphore = _Semaphore_Get( id, &queue_context ); if ( the_semaphore == NULL ) { return NULL; } _ISR_lock_ISR_enable( &queue_context.Lock_context.Lock_context ); return &the_semaphore->Core_control.Wait_queue.Queue; } void RestoreRunnerASR( void ) { rtems_status_code sc; sc = rtems_signal_catch( NULL, RTEMS_DEFAULT_MODES ); T_quiet_rsc_success( sc ); } void RestoreRunnerMode( void ) { rtems_status_code sc; rtems_mode mode; sc = rtems_task_mode( RTEMS_DEFAULT_MODES, RTEMS_ALL_MODE_MASKS, &mode ); T_quiet_rsc_success( sc ); } void RestoreRunnerPriority( void ) { SetSelfPriority( 1 ); } void RestoreRunnerScheduler( void ) { SetSelfScheduler( SCHEDULER_A_ID, 1 ); } Thread_Control *GetThread( rtems_id id ) { Thread_Control *the_thread; ISR_lock_Context lock_context; the_thread = _Thread_Get( id, &lock_context ); if ( the_thread == NULL ) { return NULL; } _ISR_lock_ISR_enable( &lock_context); return the_thread; } Thread_Control *GetExecuting( void ) { return _Thread_Get_executing(); } void KillZombies( void ) { _RTEMS_Lock_allocator(); _Thread_Kill_zombies(); _RTEMS_Unlock_allocator(); } void WaitForExecutionStop( rtems_id task_id ) { #if defined( RTEMS_SMP ) Thread_Control *the_thread; the_thread = GetThread( task_id ); T_assert_not_null( the_thread ); while ( _Thread_Is_executing_on_a_processor( the_thread ) ) { /* Wait */ } #else (void) task_id; #endif } void WaitForIntendToBlock( rtems_id task_id ) { #if defined( RTEMS_SMP ) Thread_Control *the_thread; Thread_Wait_flags intend_to_block; the_thread = GetThread( task_id ); T_assert_not_null( the_thread ); intend_to_block = THREAD_WAIT_CLASS_OBJECT | THREAD_WAIT_STATE_INTEND_TO_BLOCK; while ( _Thread_Wait_flags_get_acquire( the_thread ) != intend_to_block ) { /* Wait */ } #else (void) task_id; #endif } void WaitForHeir( uint32_t cpu_index, rtems_id task_id ) { Per_CPU_Control *cpu; cpu = _Per_CPU_Get_by_index( cpu_index ); while ( cpu->heir->Object.id != task_id ) { RTEMS_COMPILER_MEMORY_BARRIER(); } } void WaitForNextTask( uint32_t cpu_index, rtems_id task_id ) { Per_CPU_Control *cpu; cpu = _Per_CPU_Get_by_index( cpu_index ); while ( cpu->heir->Object.id == task_id ) { RTEMS_COMPILER_MEMORY_BARRIER(); } while ( cpu->thread_dispatch_disable_level != 0 ) { RTEMS_COMPILER_MEMORY_BARRIER(); } } void GetTaskTimerInfo( rtems_id id, TaskTimerInfo *info ) { GetTaskTimerInfoByThread( GetThread( id ), info ); } void GetTaskTimerInfoByThread( struct _Thread_Control *thread, TaskTimerInfo *info ) { info->expire_ticks = 0; info->expire_timespec.tv_sec = -1; info->expire_timespec.tv_nsec = -1; if ( thread != NULL ) { ISR_lock_Context lock_context; ISR_lock_Context lock_context_2; Per_CPU_Control *cpu; _ISR_lock_ISR_disable_and_acquire( &thread->Timer.Lock, &lock_context ); info->expire_ticks = thread->Timer.Watchdog.expire; #if defined( RTEMS_SMP ) cpu = thread->Timer.Watchdog.cpu; #else cpu = _Per_CPU_Get(); #endif _Watchdog_Per_CPU_acquire_critical( cpu, &lock_context_2 ); if ( _Watchdog_Is_scheduled( &thread->Timer.Watchdog ) ) { const Watchdog_Header *hdr; hdr = thread->Timer.header; if ( hdr == &cpu->Watchdog.Header[ PER_CPU_WATCHDOG_TICKS ] ) { info->state = TASK_TIMER_TICKS; } else { _Watchdog_Ticks_to_timespec( info->expire_ticks, &info->expire_timespec ); if ( hdr == &cpu->Watchdog.Header[ PER_CPU_WATCHDOG_REALTIME ] ) { info->state = TASK_TIMER_REALTIME; } else { T_quiet_eq_ptr( hdr, &cpu->Watchdog.Header[ PER_CPU_WATCHDOG_MONOTONIC ] ); info->state = TASK_TIMER_MONOTONIC; } } } else { info->state = TASK_TIMER_INACTIVE; } _Watchdog_Per_CPU_release_critical( cpu, &lock_context_2 ); _ISR_lock_Release_and_ISR_enable( &thread->Timer.Lock, &lock_context ); } else { info->state = TASK_TIMER_INVALID; } } #if defined( RTEMS_SMP ) static void DoWatchdogTick( void *arg ) { (void) arg; _Watchdog_Tick( _Per_CPU_Get() ); } #endif void ClockTick( void ) { Per_CPU_Control *cpu_self; cpu_self = _Thread_Dispatch_disable(); #if defined( RTEMS_SMP ) DoWatchdogTick( NULL ); _SMP_Othercast_action( DoWatchdogTick, NULL ); #else _Watchdog_Tick( cpu_self ); #endif _Thread_Dispatch_enable( cpu_self ); } static void FinalWatchdogTick( Per_CPU_Control *cpu ) { ISR_lock_Context lock_context; Watchdog_Header *header; Watchdog_Control *first; _ISR_lock_ISR_disable_and_acquire( &cpu->Watchdog.Lock, &lock_context ); header = &cpu->Watchdog.Header[ PER_CPU_WATCHDOG_TICKS ]; first = _Watchdog_Header_first( header ); if ( first != NULL ) { _Watchdog_Tickle( header, first, UINT64_MAX, &cpu->Watchdog.Lock, &lock_context ); } header = &cpu->Watchdog.Header[ PER_CPU_WATCHDOG_MONOTONIC ]; first = _Watchdog_Header_first( header ); if ( first != NULL ) { _Watchdog_Tickle( header, first, UINT64_MAX, &cpu->Watchdog.Lock, &lock_context ); } header = &cpu->Watchdog.Header[ PER_CPU_WATCHDOG_REALTIME ]; first = _Watchdog_Header_first( header ); if ( first != NULL ) { _Watchdog_Tickle( header, first, UINT64_MAX, &cpu->Watchdog.Lock, &lock_context ); } _ISR_lock_Release_and_ISR_enable( &cpu->Watchdog.Lock, &lock_context ); } #if defined( RTEMS_SMP ) static void DoFinalWatchdogTick( void *arg ) { (void) arg; FinalWatchdogTick( _Per_CPU_Get() ); } #endif void FinalClockTick( void ) { Per_CPU_Control *cpu_self; cpu_self = _Thread_Dispatch_disable(); #if defined( RTEMS_SMP ) DoFinalWatchdogTick( NULL ); _SMP_Othercast_action( DoFinalWatchdogTick, NULL ); #else FinalWatchdogTick( cpu_self ); #endif _Thread_Dispatch_enable( cpu_self ); } static FatalHandler fatal_handler; static void *fatal_arg; void FatalInitialExtension( rtems_fatal_source source, bool always_set_to_false, rtems_fatal_code code ) { FatalHandler fatal; T_quiet_false( always_set_to_false ); fatal = fatal_handler; if ( fatal != NULL ) { ( *fatal )( source, code, fatal_arg ); } } void SetFatalHandler( FatalHandler fatal, void *arg ) { fatal_handler = fatal; fatal_arg = arg; } static rtems_id task_switch_id; static rtems_task_switch_extension task_switch_extension; static void TaskSwitchExtension( rtems_tcb *executing, rtems_tcb *heir ) { ( *task_switch_extension )( executing, heir ); } void SetTaskSwitchExtension( rtems_task_switch_extension task_switch ) { rtems_task_switch_extension last; rtems_status_code sc; last = task_switch_extension; if ( task_switch == NULL ) { if ( last != NULL ) { sc = rtems_extension_delete( task_switch_id ); T_quiet_rsc_success( sc ); task_switch_extension = NULL; } } else { task_switch_extension = task_switch; if ( last == NULL ) { rtems_extensions_table table = { .thread_switch = TaskSwitchExtension }; sc = rtems_extension_create( rtems_build_name( 'T', 'S', 'W', 'I' ), &table, &task_switch_id ); T_quiet_rsc_success( sc ); } } } void ClearExtensionCalls( ExtensionCalls *calls ) { memset( calls, 0, sizeof( *calls ) ); } void CopyExtensionCalls( const ExtensionCalls *from, ExtensionCalls *to ) { memcpy( to, from, sizeof( *to ) ); } #if defined(RTEMS_SMP) static volatile bool delay_thread_dispatch; static void DelayThreadDispatchHandler( void *arg ) { (void) arg; while ( delay_thread_dispatch ) { /* Wait */ } } static const Per_CPU_Job_context delay_thread_dispatch_context = { .handler = DelayThreadDispatchHandler }; static Per_CPU_Job delay_thread_dispatch_job = { .context = &delay_thread_dispatch_context }; #endif void StartDelayThreadDispatch( uint32_t cpu_index ) { #if defined(RTEMS_SMP) if ( rtems_configuration_get_maximum_processors() > cpu_index ) { delay_thread_dispatch = true; _Per_CPU_Submit_job( _Per_CPU_Get_by_index( cpu_index ), &delay_thread_dispatch_job ); } #endif } void StopDelayThreadDispatch( uint32_t cpu_index ) { #if defined(RTEMS_SMP) if ( rtems_configuration_get_maximum_processors() > cpu_index ) { Per_CPU_Control *cpu_self; cpu_self = _Thread_Dispatch_disable(); delay_thread_dispatch = false; _Per_CPU_Wait_for_job( _Per_CPU_Get_by_index( cpu_index ), &delay_thread_dispatch_job ); _Thread_Dispatch_enable( cpu_self ); } #endif } bool AreInterruptsEnabled( void ) { return _ISR_Get_level() == 0; } static bool IsWhiteSpace( char c ) { return c == ' ' || c == '\t'; } bool IsWhiteSpaceOnly( const char *s ) { char c; while ( ( c = *s ) != '\0' ) { if ( !IsWhiteSpace( c ) ) { return false; } ++s; } return true; } static const char *EatWhiteSpace( const char *s ) { char c; while ( ( c = *s ) != '\0' ) { if ( !IsWhiteSpace( c ) ) { break; } ++s; } return s; } bool IsEqualIgnoreWhiteSpace( const char *a, const char *b ) { while ( true ) { a = EatWhiteSpace( a ); b = EatWhiteSpace( b ); if ( *a != *b ) { return false; } if ( *a == '\0' ) { return true; } ++a; ++b; } return true; } #if defined(RTEMS_SMP) bool TicketLockIsAvailable( const SMP_ticket_lock_Control *lock ) { unsigned int now_serving; unsigned int next_ticket; now_serving = _Atomic_Load_uint( &lock->now_serving, ATOMIC_ORDER_RELAXED ); next_ticket = _Atomic_Load_uint( &lock->next_ticket, ATOMIC_ORDER_RELAXED ); return now_serving == next_ticket; } void TicketLockWaitForOwned( const SMP_ticket_lock_Control *lock ) { while ( TicketLockIsAvailable( lock ) ) { /* Wait */ } } void TicketLockWaitForOthers( const SMP_ticket_lock_Control *lock, unsigned int others ) { unsigned int expected; unsigned int actual; expected = _Atomic_Load_uint( &lock->now_serving, ATOMIC_ORDER_RELAXED ); expected += others + 1; do { actual = _Atomic_Load_uint( &lock->next_ticket, ATOMIC_ORDER_RELAXED ); } while ( expected != actual ); } #endif