编辑
2025-04-03
记录知识
0

还是基于rtems_task_wake_after的测试例子,如果我们设置tick不为0,那么线程将进行休眠等待。当等待事件超过,会主动触发thread unlock,此时会调用调度器的_Scheduler_Unblock函数。本文延续之前的测试例子,演示一下rtems的恢复阻塞调度函数

一、测试程序

还是在rtems_task_wake_after中,我们原子的设置了thread的flag,然后设置了ticks的timeout 如下

_Thread_Set_state( executing, STATES_WAITING_FOR_TIME ); _Thread_Wait_flags_set( executing, THREAD_WAIT_STATE_BLOCKED ); _Thread_Add_timeout_ticks( executing, cpu_self, ticks );

二、代码解析

对于设置timeout,我们可以看到函数回调_Thread_Timeout,如下

static inline void _Thread_Add_timeout_ticks( Thread_Control *the_thread, Per_CPU_Control *cpu, Watchdog_Interval ticks ) { ISR_lock_Context lock_context; _ISR_lock_ISR_disable_and_acquire( &the_thread->Timer.Lock, &lock_context ); the_thread->Timer.header = &cpu->Watchdog.Header[ PER_CPU_WATCHDOG_TICKS ]; the_thread->Timer.Watchdog.routine = _Thread_Timeout; _Watchdog_Per_CPU_insert_ticks( &the_thread->Timer.Watchdog, cpu, ticks ); _ISR_lock_Release_and_ISR_enable( &the_thread->Timer.Lock, &lock_context ); }

_Thread_Timeout函数如下

void _Thread_Timeout( Watchdog_Control *the_watchdog ) { Thread_Control *the_thread; the_thread = RTEMS_CONTAINER_OF( the_watchdog, Thread_Control, Timer.Watchdog ); _Thread_Continue( the_thread, STATUS_TIMEOUT ); }

当超时的时候,就会进入关键函数_Thread_Continue,如下

void _Thread_Continue( Thread_Control *the_thread, Status_Control status ) { Thread_queue_Context queue_context; Thread_Wait_flags wait_flags; bool unblock; _Thread_queue_Context_initialize( &queue_context ); _Thread_queue_Context_clear_priority_updates( &queue_context ); _Thread_Wait_acquire( the_thread, &queue_context ); wait_flags = _Thread_Wait_flags_get( the_thread ); if ( wait_flags != THREAD_WAIT_STATE_READY ) { Thread_Wait_flags wait_class; bool success; _Thread_Wait_cancel( the_thread, &queue_context ); the_thread->Wait.return_code = status; wait_class = wait_flags & THREAD_WAIT_CLASS_MASK; success = _Thread_Wait_flags_try_change_release( the_thread, wait_class | THREAD_WAIT_STATE_INTEND_TO_BLOCK, THREAD_WAIT_STATE_READY ); if ( success ) { unblock = false; } else { _Assert( _Thread_Wait_flags_get( the_thread ) == ( wait_class | THREAD_WAIT_STATE_BLOCKED ) ); _Thread_Wait_flags_set( the_thread, THREAD_WAIT_STATE_READY ); unblock = true; } } else { unblock = false; } _Thread_Wait_release( the_thread, &queue_context ); _Thread_Priority_update( &queue_context ); if ( unblock ) { _Thread_Wait_tranquilize( the_thread ); _Thread_Unblock( the_thread ); #if defined(RTEMS_MULTIPROCESSING) if ( !_Objects_Is_local_id( the_thread->Object.id ) ) { _Thread_MP_Free_proxy( the_thread ); } #endif } }

这里我们清楚的看到wait_flags != THREAD_WAIT_STATE_READY 如果线程不是就绪态,则通过等待success = _Thread_Wait_flags_try_change_release,如果原子操作成功,则设置线程为就绪态,_Thread_Wait_flags_set( the_thread, THREAD_WAIT_STATE_READY );。

在设置线程的就绪态之后,会将unblock标准设置为true。unblock = true; 这样,最后根据此标志位调用_Thread_Unblock( the_thread );

_Thread_Unblock的调用流程是:_Thread_Unblock--->_Thread_Clear_state--->_Thread_Clear_state_locked--->_Scheduler_Unblock

_Scheduler_Unblock的作用就是将本任务加入就绪队列。

三、测试结果

对于thread设置的timeout,其堆栈如下

#0 _Thread_Timeout (the_watchdog=0x105be0 <_RTEMS_tasks_Objects+2024>) at ../../../cpukit/score/src/threadtimeout.c:110 #1 0x0000000000022234 in _Watchdog_Do_tickle (header=header@entry=0x1023a8 <_Per_CPU_Information+808>, first=0x105be0 <_RTEMS_tasks_Objects+2024>, now=102, lock=lock@entry=0x102398 <_Per_CPU_Information+792>, lock_context=lock_context@entry=0x105178 <_ISR_Stack_area_begin+8056>) at ../../../cpukit/score/src/watchdogtick.c:66 #2 0x00000000000222f4 in _Watchdog_Tick (cpu=0x102080 <_Per_CPU_Information>) at ../../../cpukit/score/src/watchdogtick.c:105 #3 0x0000000000026c3c in rtems_timecounter_tick () at ../../../cpukit/include/rtems/timecounter.h:101 #4 Clock_driver_timecounter_tick (arg=<optimized out>) at ../../../bsps/aarch64/include/../../shared/dev/clock/clockimpl.h:124 #5 Clock_isr (arg=<optimized out>) at ../../../bsps/aarch64/include/../../shared/dev/clock/clockimpl.h:237 #6 0x0000000000026d9c in bsp_interrupt_dispatch_entries (entry=0x1026e8 <arm_gt_interrupt_entry>) at ../../../bsps/include/bsp/irq-generic.h:571 #7 bsp_interrupt_handler_dispatch_unchecked (vector=30) at ../../../bsps/include/bsp/irq-generic.h:627 #8 bsp_interrupt_dispatch () at ../../../bsps/shared/dev/irq/arm-gicv2.c:98 #9 0x0000000000029410 in .AArch64_Interrupt_Handler () at ../../../cpukit/score/cpu/aarch64/aarch64-exception-interrupt.S:87

当_Thread_Timeout 之后,其调用堆栈如下

#0 _Scheduler_priority_Unblock (scheduler=0x2d248 <_Scheduler_Table>, the_thread=0x1059e0 <_RTEMS_tasks_Objects+1512>, node=0x105de0 <_RTEMS_tasks_Objects+2536>) at ../../../cpukit/include/rtems/score/schedulerimpl.h:108 #1 0x0000000000025008 in _Scheduler_Unblock (the_thread=0x1059e0 <_RTEMS_tasks_Objects+1512>) at ../../../cpukit/include/rtems/score/schedulerimpl.h:344 #2 _Thread_Clear_state_locked (the_thread=the_thread@entry=0x1059e0 <_RTEMS_tasks_Objects+1512>, state=state@entry=805396479) at ../../../cpukit/score/src/threadclearstate.c:65 #3 0x0000000000025070 in _Thread_Clear_state (the_thread=the_thread@entry=0x1059e0 <_RTEMS_tasks_Objects+1512>, state=805396479) at ../../../cpukit/score/src/threadclearstate.c:81 #4 0x0000000000021b18 in _Thread_Unblock (the_thread=0x1059e0 <_RTEMS_tasks_Objects+1512>) at ../../../cpukit/include/rtems/score/threadimpl.h:1098 #5 _Thread_Continue (the_thread=0x1059e0 <_RTEMS_tasks_Objects+1512>, status=STATUS_TIMEOUT) at ../../../cpukit/score/src/threadtimeout.c:91

四、结果

至此,我们通过测试rtems_task_wake_after函数,将其设置了1s的timeout,可以验证调度器的 block和unblock。unblock会在超时函数之后,清楚block标志位后,直接调用_Scheduler_Unblock函数

编辑
2025-04-03
记录知识
0

rtems提供了更新任务优先级的函数,最常用的场景是主动调用rtems_task_set_priority来调整任务的优先级,当前其他相关线程的操作都可以操作优先级,本文仅以最简单和通用的方式解释更新任务优先级

一、函数实现

rtems_task_set_priority的函数实现如下:

rtems_status_code rtems_task_set_priority( rtems_id id, rtems_task_priority new_priority, rtems_task_priority *old_priority_p )_RTEMS_tasks_Set_priority { Thread_Control *the_thread; Thread_queue_Context queue_context; const Scheduler_Control *scheduler; Priority_Control old_priority; rtems_status_code status; if ( old_priority_p == NULL ) { return RTEMS_INVALID_ADDRESS; } _Thread_queue_Context_initialize( &queue_context ); _Thread_queue_Context_clear_priority_updates( &queue_context ); the_thread = _Thread_Get( id, &queue_context.Lock_context.Lock_context ); if ( the_thread == NULL ) { #if defined(RTEMS_MULTIPROCESSING) return _RTEMS_tasks_MP_Set_priority( id, new_priority, old_priority_p ); #else return RTEMS_INVALID_ID; #endif } _Thread_Wait_acquire_critical( the_thread, &queue_context ); scheduler = _Thread_Scheduler_get_home( the_thread ); old_priority = _Thread_Get_priority( the_thread ); if ( new_priority != RTEMS_CURRENT_PRIORITY ) { status = _RTEMS_tasks_Set_priority( the_thread, scheduler, new_priority, &queue_context ); } else { _Thread_Wait_release( the_thread, &queue_context ); status = RTEMS_SUCCESSFUL; } *old_priority_p = _RTEMS_Priority_From_core( scheduler, old_priority ); return status; }

对于此函数,其作用主要是获取线程后加锁然后更新优先级,最后释放锁后返回旧的优先级, 所以我们关心主要函数_RTEMS_tasks_Set_priority。其调用路径如下

_RTEMS_tasks_Set_priority--->_Thread_Priority_update--->_Scheduler_Update_priority

二、测试程序

测试调整优先级的方法是在task创建之后的任意时间,根据taskid来调整优先级,故部分代码如下

status = rtems_task_start( Task_id[ 1 ], Task_1, 0 ); directive_failed( status, "rtems_task_start of TA1" ); rtems_task_priority previous_priority; status = rtems_task_set_priority( Task_id[ 1 ], 253, &previous_priority ); printf("Kylin: set priority 254 ret=%d prev=%d \n", status, previous_priority ); status = rtems_task_start( Task_id[ 2 ], Task_2, 0 ); directive_failed( status, "rtems_task_start of TA2" ); status = rtems_task_set_priority( Task_id[ 2 ], 254, &previous_priority ); printf("Kylin: set priority 255 ret=%d prev=%d \n", status, previous_priority );

我们可以调整task1和task2的优先级,这样task1和task2的启动顺序会出现不一样。日志如下

task1 优于 task2

Kylin: set priority 254 ret=0 prev=4 Kylin: set priority 255 ret=0 prev=4 TA1 - rtems_signal_send - RTEMS_SIGNAL_16 to self TA1 - rtems_signal_send - RTEMS_SIGNAL_0 to self TA2 - rtems_signal_send - RTEMS_SIGNAL_17 to self TA2 - rtems_signal_send - RTEMS_SIGNAL_18 and RTEMS_SIGNAL_19 to self

task2优于task1

Kylin: set priority 254 ret=0 prev=4 Kylin: set priority 255 ret=0 prev=4 TA2 - rtems_signal_send - RTEMS_SIGNAL_17 to self TA2 - rtems_signal_send - RTEMS_SIGNAL_18 and RTEMS_SIGNAL_19 to self TA1 - rtems_signal_send - RTEMS_SIGNAL_16 to self TA1 - rtems_signal_send - RTEMS_SIGNAL_0 to self

三、总结

本文简单的演示了调整任务优先级,由于目前未了解不同调度器的实现,故此实验比较简单

编辑
2025-04-03
记录知识
0

在rtems中,创建一个任务的时候会绑定一个调度器节点,_Scheduler_Node_initialize函数就是初始化调度器节点。本文基于rtems的任务创建函数rtems_task_create来介绍其初始化调度器节点的过程

一、代码流程

根据示例,代码可以调用rtems_task_create来创建一个task,如

status = rtems_task_create( Task_name[ 1 ], 4, RTEMS_MINIMUM_STACK_SIZE * 2, RTEMS_DEFAULT_MODES, RTEMS_DEFAULT_ATTRIBUTES, &Task_id[ 1 ] );

本文关注其初始化调度器节点,所以其调用流程如下

rtems_task_create--->_RTEMS_tasks_Create--->_Thread_Initialize--->_Thread_Try_initialize--->_Thread_Initialize_scheduler_and_wait_nodes--->_Scheduler_Node_initialize

二、关键代码

对于每个task的初始化过程中,需要初始化调度器节点,这里核心代码如下

do { Priority_Control priority; if ( scheduler == config->scheduler ) { priority = config->priority; home_scheduler_node = scheduler_node; } else { /* * Use the idle thread priority for the non-home scheduler instances by * default. */ priority = _Scheduler_Map_priority( scheduler, scheduler->maximum_priority ); } _Scheduler_Node_initialize( scheduler, scheduler_node, the_thread, priority ); /* * Since the size of a scheduler node depends on the application * configuration, the _Scheduler_Node_size constant is used to get the next * scheduler node. Using sizeof( Scheduler_Node ) would be wrong. */ scheduler_node = (Scheduler_Node *) ( (uintptr_t) scheduler_node + _Scheduler_Node_size ); ++scheduler; ++scheduler_index; } while ( scheduler_index < _Scheduler_Count ); /* * The thread is initialized to use exactly one scheduler node which is * provided by its home scheduler. */ _Assert( home_scheduler_node != NULL ); _Chain_Initialize_one( &the_thread->Scheduler.Wait_nodes, &home_scheduler_node->Thread.Wait_node ); _Chain_Initialize_one( &the_thread->Scheduler.Scheduler_nodes, &home_scheduler_node->Thread.Scheduler_node.Chain );
  • 如果当前调度器是主调度器,则设置优先级,并将节点设置为主调度器节点
  • 如果不是主调度器,则设置为maximum_priority优先级
  • 根据设置的优先级,初始化调度器节点
  • 定位下一个调度器节点,将调度器指针自加,如果有多个调度器,则循环初始化

三、总结

根据上面的代码演示,我们知道在每个task创建的时候,都会初始化调度器节点。由于此功能没有实际演示的效果,故无需编写测试代码演示

编辑
2025-04-03
记录知识
0

rtems系统默认的初始化的section名字不是init,而是rtemsroset,在开启过程中,通过遍历rtemsroset下的函数指针,调用此函数指针从而对系统进行初始化动作,这里根据代码解析rtems的初始化流程

一、rtemsroset

这个section是rtems的初始化section,不同于常规的elf的.init,我们可以通过如下获得section信息

# rtems-exeinfo -S build/aarch64/zynqmp_qemu/testsuites/samples/hello.exe RTEMS Executable Info 6.ca7bcc490ee8-modified rtems-exeinfo -S build/aarch64/zynqmp_qemu/testsuites/samples/hello.exe exe: build/aarch64/zynqmp_qemu/testsuites/samples/hello.exe Compilation: Producers: 2 | GNU AS 2.43: 13 objects | GNU C17 13.3.0 20240521 (RTEMS 6, RSB b1aec32059aa0e86385ff75ec01daf93713fa382-modified, Newlib 1b3dcfd): 282 objects Common flags: 7 | -ftls-model=local-exec -mno-outline-atomics -mcpu=cortex-a53 -mfix-cortex-a53-835769 -mfix-cortex-a53-843419 -mlittle-endian -mabi=lp64 Sections: 38 -------------- addr: 0x00000000 0x00000000 size: 0 align: 0 relocs: 0 .bss WA------------ addr: 0x00100780 0x00103618 size: 11928 align: 64 relocs: 0 .comment ---MS--------- addr: 0x00000000 0x0000006c size: 108 align: 1 relocs: 0 .data WA------------ addr: 0x00100000 0x00100768 size: 1896 align: 16 relocs: 0 .debug_abbrev -------------- addr: 0x00000000 0x000290ae size: 168110 align: 1 relocs: 0 .debug_aranges -------------- addr: 0x00000000 0x00004e00 size: 19968 align: 16 relocs: 0 .debug_frame -------------- addr: 0x00000000 0x00007e10 size: 32272 align: 8 relocs: 0 .debug_gdb_scripts ---MS--------- addr: 0x00000000 0x00000087 size: 135 align: 1 relocs: 0 .debug_info -------------- addr: 0x00000000 0x0014e550 size: 1369424 align: 1 relocs: 0 .debug_line -------------- addr: 0x00000000 0x00039263 size: 234083 align: 1 relocs: 0 .debug_line_str ---MS--------- addr: 0x00000000 0x00004a60 size: 19040 align: 1 relocs: 0 .debug_loclists -------------- addr: 0x00000000 0x0004b84b size: 309323 align: 1 relocs: 0 .debug_rnglists -------------- addr: 0x00000000 0x0000aa8b size: 43659 align: 1 relocs: 0 .debug_str ---MS--------- addr: 0x00000000 0x00012110 size: 74000 align: 1 relocs: 0 .eh_frame -A------------ addr: 0x00032a28 0x00032ae0 size: 184 align: 8 relocs: 0 .fini -AE----------- addr: 0x00030064 0x00030098 size: 52 align: 4 relocs: 0 .fini_array WA------------ addr: 0x00032b00 0x00032b08 size: 8 align: 8 relocs: 0 .got WA------------ addr: 0x00032b08 0x00032b68 size: 96 align: 8 relocs: 0 .init -AE----------- addr: 0x00030030 0x00030064 size: 52 align: 4 relocs: 0 .init_array WA------------ addr: 0x00032af8 0x00032b00 size: 8 align: 8 relocs: 0 .nocachenoload WA------------ addr: 0x3fec0000 0x3ffc0000 size: 1048576 align: 1 relocs: 0 .noinit WA------------ addr: 0x00105640 0x00106360 size: 3360 align: 8 relocs: 0 .robarrier W------------- addr: 0x00030098 0x00030098 size: 0 align: 1 relocs: 0 .rodata -A------------ addr: 0x000300a0 0x00032a21 size: 10625 align: 16 relocs: 0 .rtemsroset -A------------ addr: 0x00032b68 0x00032c18 size: 176 align: 8 relocs: 0 .rtemsstack WA------------ addr: 0x00103640 0x00105640 size: 8192 align: 64 relocs: 0 .rwbarrier WA------------ addr: 0x00032c18 0x00100000 size: 840680 align: 1 relocs: 0 .shstrtab -------------- addr: 0x00000000 0x00000178 size: 376 align: 1 relocs: 0 .stack W------------- addr: 0x3fec0000 0x3fec0000 size: 0 align: 1 relocs: 0 .start -AE----------- addr: 0x00018000 0x00018868 size: 2152 align: 16 relocs: 0 .strtab -------------- addr: 0x00000000 0x00005a5f size: 23135 align: 1 relocs: 0 .symtab -------------- addr: 0x00000000 0x0000b6a0 size: 46752 align: 8 relocs: 0 .tbss WA-------T---- addr: 0x00032af8 0x00032b28 size: 48 align: 8 relocs: 0 .tdata WA-------T---- addr: 0x00032ae0 0x00032af8 size: 24 align: 8 relocs: 0 .text -AE----------- addr: 0x00019000 0x00030030 size: 94256 align: 2048 relocs: 0 .vector W------------- addr: 0x00100000 0x00100000 size: 0 align: 1 relocs: 0 .work WA------------ addr: 0x00106360 0x3fec0000 size: 1071357088 align: 1 relocs: 0 .xbarrier W------------- addr: 0x00018868 0x00018868 size: 0 align: 1 relocs: 0

上面我们看到了.rtemsroset。我们接下来解析如下:

# rtems-exeinfo -I build/aarch64/zynqmp_qemu/testsuites/samples/hello.exe RTEMS Executable Info 6.ca7bcc490ee8-modified rtems-exeinfo -I build/aarch64/zynqmp_qemu/testsuites/samples/hello.exe exe: build/aarch64/zynqmp_qemu/testsuites/samples/hello.exe Compilation: Producers: 2 | GNU AS 2.43: 13 objects | GNU C17 13.3.0 20240521 (RTEMS 6, RSB b1aec32059aa0e86385ff75ec01daf93713fa382-modified, Newlib 1b3dcfd): 282 objects Common flags: 7 | -ftls-model=local-exec -mno-outline-atomics -mcpu=cortex-a53 -mfix-cortex-a53-835769 -mfix-cortex-a53-843419 -mlittle-endian -mabi=lp64 Init sections: 2 .init 0xa9bf7bfd no symbol (maybe static to a module) 0xa9bf73fb no symbol (maybe static to a module) 0xa9bf6bf9 no symbol (maybe static to a module) 0xa9bf63f7 no symbol (maybe static to a module) 0xa9bf5bf5 no symbol (maybe static to a module) 0xa9bf53f3 no symbol (maybe static to a module) .rtemsroset 0x00025910 _Workspace_Handler_initialization 0x0001f900 _Malloc_Initialize 0x00025a60 bsp_start 0x00025670 zynq_uart_kernel_init 0x0001e440 _User_extensions_Handler_initialization 0x000206c0 rtems_initialize_data_structures 0x0001ed30 _Scheduler_Ensure_exactly_one_processor 0x000240e0 _RTEMS_tasks_Manager_initialization 0x00021a30 _Thread_Create_idle 0x00024e10 bsp_r1_heap_extend 0x0001f6f0 rtems_libio_init

可以发现.rtemsroset有多个函数指针如下

0x00025910 _Workspace_Handler_initialization 0x0001f900 _Malloc_Initialize 0x00025a60 bsp_start 0x00025670 zynq_uart_kernel_init 0x0001e440 _User_extensions_Handler_initialization 0x000206c0 rtems_initialize_data_structures 0x0001ed30 _Scheduler_Ensure_exactly_one_processor 0x000240e0 _RTEMS_tasks_Manager_initialization 0x00021a30 _Thread_Create_idle 0x00024e10 bsp_r1_heap_extend 0x0001f6f0 rtems_libio_init

1.1 完整解析

这里我们通过rtems-exeinfo来读取rtemsroset的section的信息,但是这个工具读取初始化流程的函数并不完整,完整的初始化流程如下。

0x00025910 _Workspace_Handler_initialization 0x0001f900 _Malloc_Initialize 0x00025a60 bsp_start 0x00025670 zynq_uart_kernel_init 0x0001e440 _User_extensions_Handler_initialization 0x000206c0 rtems_initialize_data_structures 0x0001ed30 _Scheduler_Ensure_exactly_one_processor 0x000240e0 _RTEMS_tasks_Manager_initialization 0x00021a30 _Thread_Create_idle 0x00024e10 bsp_r1_heap_extend 0x0001f6f0 rtems_libio_init 0x00019150 rtems_filesystem_initialize 0x00019200 _Console_simple_Initialize 0x0001c290 _RTEMS_tasks_Initialize_user_task 0x00019790 rtems_libio_post_driver

值得注意的是,rtems-exeinfo程序解析的不对,少了如下

0x00019150 rtems_filesystem_initialize 0x00019200 _Console_simple_Initialize 0x0001c290 _RTEMS_tasks_Initialize_user_task 0x00019790 rtems_libio_post_driver

二、RTEMS_SYSINIT_ITEM

我们搜索代码,可以知道这些函数指针通过RTEMS_SYSINIT_ITEM宏来设置,这里解析这个宏定义,如下

#define RTEMS_SYSINIT_ITEM( handler, module, order ) \ _RTEMS_SYSINIT_ITEM( handler, module, order )

上面只是封装,所以我们查看_RTEMS_SYSINIT_ITEM

#define _RTEMS_SYSINIT_ITEM( handler, module, order ) \ _RTEMS_SYSINIT_INDEX_ITEM( handler, 0x##module##order )

这里组合了module, order作为地址,我们继续看_RTEMS_SYSINIT_INDEX_ITEM

#define _RTEMS_SYSINIT_INDEX_ITEM( handler, index ) \ enum { _Sysinit_##handler = index }; \ RTEMS_LINKER_ROSET_ITEM_ORDERED( \ _Sysinit, \ rtems_sysinit_item, \ handler, \ index \ ) = { handler }

这里定义了一个enum值,并且设置了RTEMS_LINKER_ROSET_ITEM_ORDERED,我们查看其定义

#define RTEMS_LINKER_ROSET_ITEM_ORDERED( set, type, item, order ) \ RTEMS_LINKER_SET_ALIGN( type ) type const _Linker_set_##set##_##item \ RTEMS_SECTION( ".rtemsroset." #set ".content.0." RTEMS_XSTRING( order ) ) \ RTEMS_USED

这里看到

  • set 作为变量名字组合标签
  • type 作为变量类型
  • item 将函数名字组合变量名
  • order 作为__section__的的地址

这里假设第一个初始化函数_Thread_Create_idle ,其定义如下:

RTEMS_SYSINIT_ITEM( _Thread_Create_idle, RTEMS_SYSINIT_IDLE_THREADS, RTEMS_SYSINIT_ORDER_MIDDLE );

其解析后如下:

enum { _Sysinit__Thread_Create_idle = 0x001d0080 }; __attribute__(( __aligned__( _Alignof( rtems_sysinit_item ) ) )) rtems_sysinit_item const _Linker_set__Sysinit__Thread_Create_idle __attribute__(( __section__( ".rtemsroset." "_Sysinit" ".content.0." "0x001d0080" ) )) __attribute__(( __used__ )) = { _Thread_Create_idle }

三、RTEMS_LINKER_SET_FOREACH

我们在rtems_initialize_executive中可以看到调用如下

/* Invoke the registered system initialization handlers */ RTEMS_LINKER_SET_FOREACH( _Sysinit, item ) { ( *item->handler )(); }

此时查看RTEMS_LINKER_SET_FOREACH定义

#define RTEMS_LINKER_SET_FOREACH( set, item ) \ for ( \ item = (void *) _Linker_set_Obfuscate( RTEMS_LINKER_SET_BEGIN( set ) ) ; \ item != RTEMS_LINKER_SET_END( set ) ; \ ++item \ )

然后查看两个宏定义

#define RTEMS_LINKER_SET_BEGIN( set ) \ _Linker_set_##set##_begin #define RTEMS_LINKER_SET_END( set ) \ _Linker_set_##set##_end

确定内联函数_Linker_set_Obfuscate

static inline uintptr_t _Linker_set_Obfuscate( const void *ptr ) { uintptr_t addr; addr = (uintptr_t) ptr; RTEMS_OBFUSCATE_VARIABLE( addr ); return addr; }

至此,我们可以知道,通过遍历_Linker_set__Sysinit_begin到_Linker_set__Sysinit_end获取到了每个item,然后之间调用handler函数指针,即如下

typedef void ( *rtems_sysinit_handler )( void ); typedef struct { rtems_sysinit_handler handler; } rtems_sysinit_item;

这里可以看到如下

(gdb) p _Linker_set__Sysinit_begin $49 = 0x32b68 <_Linker_set__Sysinit__Workspace_Handler_initialization> (gdb) p _Linker_set__Sysinit_end $50 = 0x32be0 <_Copyright_Notice>

四、总结

至此,我们清楚的知道了rtems的初始化时序

编辑
2025-04-03
记录知识
0

在进行boot_card初始化之前,这里提前做了bsp的hook,本文分析bsp_start_hook_1的代码

一、AArch64_start_set_vector_base

这里将向量表写到异常向量表VBAR_EL1中,变量是bsp_start_vector_table_begin,如下定义

bsp_start_vector_table_begin: .balign 0x800 Vector_table_el3: /* * The exception handler for synchronous exceptions from the current EL * using SP0. */ curr_el_sp0_sync: sub sp, sp, #AARCH64_EXCEPTION_FRAME_SIZE /* reserve space for CEF */ str lr, [sp, #AARCH64_EXCEPTION_FRAME_REGISTER_LR_OFFSET] /* shove lr into CEF */ bl .push_exception_context_start /* bl to CEF store routine */ /* Save original sp in x0 for .push_exception_context_finish */ add x0, sp, #AARCH64_EXCEPTION_FRAME_SIZE /* save original sp */ /* Push the remainder of the context */ bl .push_exception_context_finish /* get jump target and branch/link */ bl curr_el_sp0_sync_get_pc /* Get current execution address */ curr_el_sp0_sync_get_pc: /* The current PC is now in LR */ mov x0, #0x7f /* Mask to use in BIC, lower 7 bits */ bic x0, lr, x0 /* Mask LR to base of current vector */ ldr x1, [x0, #0x78] /* Load target from last word in vector */ and lr, lr, #0x780 /* Mask off bits for vector number */ lsr lr, lr, #7 /* Shift the vector bits down */ /* Store the vector */ str lr, [sp, #AARCH64_EXCEPTION_FRAME_REGISTER_VECTOR_OFFSET] mov x0, sp blr x1 b twiddle nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop /* Takes up the space of 2 instructions */ #ifdef AARCH64_MULTILIB_ARCH_V8_ILP32 .word _AArch64_Exception_default .word 0x0 #else .dword _AArch64_Exception_default #endif .balign 0x80 /* The exception handler for IRQ exceptions from the current EL using SP0. */ curr_el_sp0_irq: stp x0, lr, [sp, #-0x10]! /* Push x0,lr on to the stack */ bl curr_el_sp0_irq_get_pc /* Get current execution address */ curr_el_sp0_irq_get_pc: /* The current PC is now in LR */ JUMP_HANDLER JUMP_TARGET_SP0 .balign 0x80 /* The exception handler for FIQ exceptions from the current EL using SP0. */ curr_el_sp0_fiq: stp x0, lr, [sp, #-0x10]! /* Push x0,lr on to the stack */ bl curr_el_sp0_fiq_get_pc /* Get current execution address */ curr_el_sp0_fiq_get_pc: /* The current PC is now in LR */ JUMP_HANDLER JUMP_TARGET_SP0 .balign 0x80 /* * The exception handler for system error exceptions from the current EL using * SP0. */ curr_el_sp0_serror: stp x0, lr, [sp, #-0x10]! /* Push x0,lr on to the stack */ bl curr_el_sp0_serror_get_pc /* Get current execution address */ curr_el_sp0_serror_get_pc: /* The current PC is now in LR */ JUMP_HANDLER JUMP_TARGET_SP0 .balign 0x80 /* * The exception handler for synchronous exceptions from the current EL using * the current SP. */ curr_el_spx_sync: msr spsel, #0 /* switch to exception stack */ sub sp, sp, #AARCH64_EXCEPTION_FRAME_SIZE /* reserve space for CEF */ str lr, [sp, #AARCH64_EXCEPTION_FRAME_REGISTER_LR_OFFSET] /* shove lr into CEF */ bl .push_exception_context_start /* bl to CEF store routine */ /* Save original sp in x0 for .push_exception_context_finish */ msr spsel, #1 mov x0, sp msr spsel, #0 /* Push the remainder of the context */ bl .push_exception_context_finish /* get jump target and branch/link */ bl curr_el_spx_sync_get_pc /* Get current execution address */ curr_el_spx_sync_get_pc: /* The current PC is now in LR */ mov x0, #0x7f /* Mask to use in BIC, lower 7 bits */ bic x0, lr, x0 /* Mask LR to base of current vector */ ldr x1, [x0, #0x78] /* Load target from last word in vector */ and lr, lr, #0x780 /* Mask off bits for vector number */ lsr lr, lr, #7 /* Shift the vector bits down */ /* Store the vector */ str lr, [sp, #AARCH64_EXCEPTION_FRAME_REGISTER_VECTOR_OFFSET] mov x0, sp blr x1 b twiddle nop nop nop nop nop nop nop nop nop nop nop nop /* Takes up the space of 2 instructions */ #ifdef AARCH64_MULTILIB_ARCH_V8_ILP32 .word _AArch64_Exception_default .word 0x0 #else .dword _AArch64_Exception_default #endif .balign 0x80 /* * The exception handler for IRQ exceptions from the current EL using the * current SP. */ curr_el_spx_irq: stp x0, lr, [sp, #-0x10]! /* Push x0,lr on to the stack */ bl curr_el_spx_irq_get_pc /* Get current execution address */ curr_el_spx_irq_get_pc: /* The current PC is now in LR */ JUMP_HANDLER JUMP_TARGET_SPx .balign 0x80 /* * The exception handler for FIQ exceptions from the current EL using the * current SP. */ curr_el_spx_fiq: stp x0, lr, [sp, #-0x10]! /* Push x0,lr on to the stack */ bl curr_el_spx_fiq_get_pc /* Get current execution address */ curr_el_spx_fiq_get_pc: /* The current PC is now in LR */ JUMP_HANDLER JUMP_TARGET_SPx .balign 0x80 /* * The exception handler for system error exceptions from the current EL using * the current SP. */ curr_el_spx_serror: stp x0, lr, [sp, #-0x10]! /* Push x0,lr on to the stack */ bl curr_el_spx_serror_get_pc /* Get current execution address */ curr_el_spx_serror_get_pc: /* The current PC is now in LR */ JUMP_HANDLER JUMP_TARGET_SPx .balign 0x80 /* * The exception handler for synchronous exceptions from a lower EL (AArch64). */ lower_el_aarch64_sync: stp x0, lr, [sp, #-0x10]! /* Push x0,lr on to the stack */ bl lower_el_aarch64_sync_get_pc /* Get current execution address */ lower_el_aarch64_sync_get_pc: /* The current PC is now in LR */ JUMP_HANDLER JUMP_TARGET_SPx .balign 0x80 /* The exception handler for IRQ exceptions from a lower EL (AArch64). */ lower_el_aarch64_irq: stp x0, lr, [sp, #-0x10]! /* Push x0,lr on to the stack */ bl lower_el_aarch64_irq_get_pc /* Get current execution address */ lower_el_aarch64_irq_get_pc: /* The current PC is now in LR */ JUMP_HANDLER JUMP_TARGET_SPx .balign 0x80 /* The exception handler for FIQ exceptions from a lower EL (AArch64). */ lower_el_aarch64_fiq: stp x0, lr, [sp, #-0x10]! /* Push x0,lr on to the stack */ bl lower_el_aarch64_fiq_get_pc /* Get current execution address */ lower_el_aarch64_fiq_get_pc: /* The current PC is now in LR */ JUMP_HANDLER JUMP_TARGET_SPx .balign 0x80 /* * The exception handler for system error exceptions from a lower EL(AArch64). */ lower_el_aarch64_serror: /* Push x0,lr on to the stack */ stp x0, lr, [sp, #-0x10]! /* Get current execution address */ bl lower_el_aarch64_serror_get_pc lower_el_aarch64_serror_get_pc: /* The current PC is now in LR */ JUMP_HANDLER JUMP_TARGET_SPx .balign 0x80 /* * The exception handler for the synchronous exception from a lower EL(AArch32). */ lower_el_aarch32_sync: stp x0, lr, [sp, #-0x10]! /* Push x0,lr on to the stack */ bl lower_el_aarch32_sync_get_pc /* Get current execution address */ lower_el_aarch32_sync_get_pc: /* The current PC is now in LR */ JUMP_HANDLER JUMP_TARGET_SPx .balign 0x80 /* The exception handler for the IRQ exception from a lower EL (AArch32). */ lower_el_aarch32_irq: stp x0, lr, [sp, #-0x10]! /* Push x0,lr on to the stack */ bl lower_el_aarch32_irq_get_pc /* Get current execution address */ lower_el_aarch32_irq_get_pc: /* The current PC is now in LR */ JUMP_HANDLER JUMP_TARGET_SPx .balign 0x80 /* The exception handler for the FIQ exception from a lower EL (AArch32). */ lower_el_aarch32_fiq: stp x0, lr, [sp, #-0x10]! /* Push x0,lr on to the stack */ bl lower_el_aarch32_fiq_get_pc /* Get current execution address */ lower_el_aarch32_fiq_get_pc: /* The current PC is now in LR */ JUMP_HANDLER JUMP_TARGET_SPx .balign 0x80 /* * The exception handler for the system error exception from a lower EL * (AArch32). */ lower_el_aarch32_serror: /* Push x0,lr on to the stack */ stp x0, lr, [sp, #-0x10]! /* Get current execution address */ bl lower_el_aarch32_serror_get_pc lower_el_aarch32_serror_get_pc : /* The current PC is now in LR */ JUMP_HANDLER JUMP_TARGET_SPx bsp_start_vector_table_end:

可以看到,这里面包含了预设的一系列的异常向量表。每条向量表之间按照0x80对齐。

二、_SMP_Get_current_processor

这里获取当前的处理器index,如下

uint32_t _CPU_SMP_Get_current_processor( void ) { return _Per_CPU_Get_index( _CPU_Get_current_per_CPU_control() ); }

这里对于_CPU_Get_current_per_CPU_control,如下实现

static inline struct Per_CPU_Control *_AARCH64_Get_current_per_CPU_control( void ) { struct Per_CPU_Control *cpu_self; uint64_t value; __asm__ volatile ( "mrs %0, TPIDR_EL1" : "=&r" ( value ) : : "memory" ); /* Use EL1 Thread ID Register (TPIDR_EL1) */ cpu_self = (struct Per_CPU_Control *)(uintptr_t)value; return cpu_self; }

这里获取了TPIDR_EL1寄存器的值

而对于_Per_CPU_Get_index,实现如下:

static inline uint32_t _Per_CPU_Get_index( const Per_CPU_Control *cpu ) { #if defined(RTEMS_SMP) const Per_CPU_Control_envelope *per_cpu_envelope = ( const Per_CPU_Control_envelope * ) cpu; return ( uint32_t ) ( per_cpu_envelope - &_Per_CPU_Information[ 0 ] ); #else (void) cpu; return 0; #endif }

这里拿cpu_self 减去 &_Per_CPU_Information[ 0 ],这里取了_Per_CPU_Information[ 0 ] 的地址,也就是_Per_CPU_Information的地址, 我们看如下代码

ldr x1, =_Per_CPU_Information add x1, x1, x0, lsl #PER_CPU_CONTROL_SIZE_LOG2 # 10 msr TPIDR_EL1, x1

这里x0从mpidr_el1获得

mpidr_el1在Aarch64寄存器介绍提过,0xff获得cpu的亲和性,代码如下

FUNCTION_ENTRY(_AArch64_Get_current_processor_for_system_start) /* Return the affinity level 0 reported by the MPIDR_EL1 */ mrs x0, mpidr_el1 and x0, x0, #0xff ret FUNCTION_END(_AArch64_Get_current_processor_for_system_start)

这里x0是0,对于

add x1, x1, x0, lsl #10

可以知道 x1 = x1 + x0 << 10。这里x1还是x1, 也就是_Per_CPU_Information的地址。

所以_CPU_SMP_Get_current_processor 返回了0

三、zynqmp_setup_mmu_and_cache

首先需要留意的是aarch64_mmu_setup,代码如下:

BSP_START_TEXT_SECTION static inline void aarch64_mmu_setup( void ) { /* Set TCR */ /* 256TB/48 bits mappable (64-0x10) */ _AArch64_Write_tcr_el1( AARCH64_TCR_EL1_T0SZ( 0x10 ) | AARCH64_TCR_EL1_IRGN0( 0x1 ) | AARCH64_TCR_EL1_ORGN0( 0x1 ) | AARCH64_TCR_EL1_SH0( 0x3 ) | AARCH64_TCR_EL1_TG0( 0x0 ) | AARCH64_TCR_EL1_IPS( 0x5ULL ) | AARCH64_TCR_EL1_EPD1 ); /* Set MAIR */ _AArch64_Write_mair_el1( AARCH64_MAIR_EL1_ATTR0( 0x0 ) | AARCH64_MAIR_EL1_ATTR1( 0x4 ) | AARCH64_MAIR_EL1_ATTR2( 0x44 ) | AARCH64_MAIR_EL1_ATTR3( 0xFF ) ); }

tcr可以查看Aarch64的TCR寄存器介绍解释

mair_el1内存属性寄存器,可以查看:Aarch64的MAIR寄存器介绍

我们可以知道mair_el1的值是0xffffffffff440400。可以计算如下:

AARCH64_MAIR_EL1_ATTR0( 0x0 ) : Device-nGnRnE AARCH64_MAIR_EL1_ATTR1( 0x4 ) : Device-nGnRE AARCH64_MAIR_EL1_ATTR2( 0x44 ) : Normal-Inner+Outer Non-cacheable AARCH64_MAIR_EL1_ATTR3( 0xFF ) : Normal-Inner+Outer Write-Back Non-transient Inner+Outer Read-Allocate, Inner+Outer Write-Allocate.

对于aarch64_mmu_setup_translation_table函数,实现如下:

BSP_START_TEXT_SECTION void aarch64_mmu_setup_translation_table( aarch64_mmu_control *control, const aarch64_mmu_config_entry *config_table, size_t config_count ) { size_t i; aarch64_mmu_page_table_set_blocks( control->ttb, (uintptr_t) NULL, MMU_MAX_SUBTABLE_PAGE_BITS, 0 ); /* Configure entries required for each memory section */ for ( i = 0; i < config_count; ++i ) { rtems_status_code sc; sc = aarch64_mmu_set_translation_table_entries( control, &config_table[i] ); if ( sc != RTEMS_SUCCESSFUL ) { bsp_fatal( AARCH64_FATAL_MMU_CANNOT_MAP_BLOCK ); } } }

此函数设置mmu的一级页表,这里本地变量解析如下:

page_table: ttb base: 0 bit_offset: 39 page_flag: 0 default_attr: 0 MMU_BITS_PER_LEVEL: 每级页表占9bit

对于for ( uint64_t i = 0; i < ( 1 << MMU_BITS_PER_LEVEL ); i++ ) 而言

这里 i 是 0-511 (bits=9)

对于page_table[i] = base | ( i << bits_offset );

base | i < 39 将512个页表index设置到bit39-48上

page_table[i] |= default_attr | page_flag; 为一级页表设置默认属性和flag

ID_AA64MMFR0_EL1寄存器查询设置的物理地址范围

我们留意parange

对于代码,我们可以知道id_reg是0x0010。则max_mappable是 1 << 40 也就是 0x10000000000

3.2 aarch64_mmu_map_block

这里调用aarch64_mmu_map_block,我们查看如下:

return aarch64_mmu_map_block( control, control->ttb, 0x0, begin, size, -1, config->flags );

参数解析如下:

control: aarch64_mmu_instance ttb: bsp_translation_table_base root_address: 0 addr: aarch64_mmu_config_table→begin size: aarch64_mmu_config_table size level: -1 flag: aarch64_mmu_config_table->flag

值得注意的是,此函数会递归调用(aarch64_mmu_map_block)进行页表映射,到pte后为实际物理地址, 如下:

page_table[index] = addr | flags | page_flag;

这里关于ttbrx到物理地址的查询步骤,可以看我的其他文章,这里不赘述

3.3 aarch64_mmu_enable

这里主要关注如下寄存器

TTBR0_EL1 SCTLR_EL1

对于TTBR0_EL1,这里将ttb写入ttbr0_el1

对于SCTLR_EL1。做了如下位与

sctlr |= AARCH64_SCTLR_EL1_I | AARCH64_SCTLR_EL1_C | AARCH64_SCTLR_EL1_M;

四、bsp_start_clear_bss

这里直接memset将bss段清空,如下

BSP_START_TEXT_SECTION static inline void bsp_start_clear_bss(void) { memset(bsp_section_bss_begin, 0, (size_t) bsp_section_bss_size); }

五、总结

至此,关于进入bootcard前的bsp_start_hook_1流程介绍完毕