rtems系统默认的初始化的section名字不是init,而是rtemsroset,在开启过程中,通过遍历rtemsroset下的函数指针,调用此函数指针从而对系统进行初始化动作,这里根据代码解析rtems的初始化流程
这个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
这里我们通过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宏来设置,这里解析这个宏定义,如下
#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
这里看到
这里假设第一个初始化函数_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_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的初始化时序
在进行boot_card初始化之前,这里提前做了bsp的hook,本文分析bsp_start_hook_1的代码
这里将向量表写到异常向量表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对齐。
这里获取当前的处理器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
首先需要留意的是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
这里调用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到物理地址的查询步骤,可以看我的其他文章,这里不赘述
这里主要关注如下寄存器
TTBR0_EL1 SCTLR_EL1
对于TTBR0_EL1,这里将ttb写入ttbr0_el1
对于SCTLR_EL1。做了如下位与
sctlr |= AARCH64_SCTLR_EL1_I | AARCH64_SCTLR_EL1_C | AARCH64_SCTLR_EL1_M;
这里直接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流程介绍完毕
根据之前的了解,我们知道了rtems启动需要经过多个步骤,现在我们根据代码分析,记录一下初始化流程
总流程在函数rtems_initialize_executive中,其实现如下:
void rtems_initialize_executive(void) { const rtems_sysinit_item *item; /* Invoke the registered system initialization handlers */ RTEMS_LINKER_SET_FOREACH( _Sysinit, item ) { ( *item->handler )(); } _Syst_Malloc_Initializeem_state_Set( SYSTEM_STATE_UP ); _SMP_Request_start_multitasking(); _Thread_Start_multitasking(); /******************************************************************* ******************************************************************* ******************************************************************* ****** APPLICATION RUNS HERE ****** ****** THE FUNCTION NEVER RETURNS ****** ******************************************************************* ******************************************************************* *******************************************************************/ }
故分析可知,其主要调用如下:
_Workspace_Handler_initialization _Malloc_Initialize bsp_start zynq_uart_kernel_init _User_extensions_Handler_initialization rtems_initialize_data_structures _Scheduler_Ensure_exactly_one_processor _RTEMS_tasks_Manager_initialization _Thread_Create_idle bsp_r1_heap_extend rtems_libio_init rtems_filesystem_initialize _Console_simple_Initialize _RTEMS_tasks_Initialize_user_task rtems_libio_post_driver _SMP_Request_start_multitasking _Thread_Start_multitasking
初始化堆工作区
_Workspace_Handler_initialization _Workspace_Initialize_for_one_area _Heap_Initialize _Heap_Get_first_and_last_block
初始化malloc
_Malloc_Initialize _Malloc_Initialize_for_one_area _Heap_Initialize _Heap_Get_first_and_last_block
bsp主要初始化中断向量表和增加一致性cache区域,并初始化ecc
bsp_start bsp_interrupt_initialize bsp_interrupt_facility_initialize AArch64_set_exception_handler AArch64_get_vector_base_address VBAR_EL1 *vector_address = handler; /* Execution template: Save volatile regs on interrupt stack Execute irq handler Restore volatile regs from interrupt stack Return to embedded exception vector code */ _AArch64_Exception_interrupt_nest AArch64_Interrupt_Handler bsp_interrupt_dispatch bsp_interrupt_handler_dispatch_unchecked bsp_interrupt_dispatch_entries ( *entry->handler )( entry->arg ); /* Execution template: Save volatile registers on thread stack(some x, all q, ELR, etc.) Switch to interrupt stack Execute interrupt handler Switch to thread stack Call thread dispatch Restore volatile registers from thread stack Return to embedded exception vector code */ _AArch64_Exception_interrupt_no_nest AArch64_Interrupt_Handler _AArch64_Exception_thread_dispatch rtems_cache_coherent_add_area add_area _Heap_Initialize _Heap_Get_first_and_last_block zynqmp_ecc_init
串口初始化,这里后面的字符输出通过zynq_uart_kernel_output_char回调到每个字符的输出
zynq_uart_kernel_init zynq_uart_initialize zynq_uart_kernel_output_char
用户扩展的初始化handler,因为默认没有,故不分析
_User_extensions_Handler_initialization _Chain_Append_unprotected
这里完成cpu,thread,isr,scheduler,smp的初始化
rtems_initialize_data_structures _CPU_Initialize _Thread_Dispatch_initialization _ISR_Handler_initialization _Thread_Handler_initialization _Scheduler_Handler_initialization _Scheduler_priority_Initialize _Scheduler_priority_Ready_queue_initialize _SMP_Handler_initialize _CPU_SMP_Finalize_initialization rtems_interrupt_entry_install # IPI 为bsp_inter_processor_interrupt bsp_interrupt_entry_install bsp_interrupt_entry_install_first
这里只是断言了cpu为1个
_RTEMS_tasks_Manager_initialization _Thread_Initialize_information _User_extensions_Add_API_set _User_extensions_Add_set
创建idle线程
_Thread_Create_idle _Thread_Create_idle_for_CPU _Thread_Initialize _Thread_Try_initialize _User_extensions_Thread_create # 创建线程 cpu->executing = idle; _System_state_Set( SYSTEM_STATE_BEFORE_MULTITASKING );
扩展的heap
libio初始化
imfs的初始化,设置root
rtems_filesystem_initialize mount register_root_file_system mkdir mknod rtems_filesystem_mknod IMFS_mknod
添加/dev/console
_Console_simple_Initialize IMFS_add_node
设置Init函数
_RTEMS_tasks_Initialize_user_task # _RTEMS_tasks_User_task_table/Init rtems_task_create _RTEMS_tasks_Create _RTEMS_tasks_Allocate _Thread_Initialize
rtems_libio_post_driver open # /dev/console
等待状态PER_CPU_STATE_READY_TO_START_MULTITASKING
SYSTEM_STATE_UP _SMP_Request_start_multitasking _SMP_Wait_for_ready_to_start_multitasking _SMP_Try_to_process_message _SMP_Process_message SMP_MESSAGE_SHUTDOWN/SMP_MESSAGE_PERFORM_JOBS/SMP_MESSAGE_FORCE_PROCESSING
启动线程任务
_Thread_Start_multitasking _CPU_Start_multitasking _AArch64_Start_multitasking .L_check_is_executing .L_restore ldp fp, lr, [x1, #0x50] # _CPU_Context_switch_no_return _Thread_Handler _Thread_Entry_adaptor_numeric Init
至此,我们完成了rtems的全部的启动流程,可以看到,最后_Thread_Start_multitasking
会主动回调在rtems_task_start传入的adaptor函数指针,而这个指针指向Init函数。操作系统启动完成。
有些寄存器不太清晰,这里通过aarch64的芯片体系架构规范查询后留作记录,方便记忆
这里bit[2:3]判断当前处理elX。
系统控制寄存器,包括内存相关的控制
这里需要注意I,C,M位分别是
用于保持内核级的线程结构指针
内存屏障指令
获取cpu亲和性寄存器
这里bit[7:0]是PE下的亲和性寄存器值
此寄存器可以查询设置的物理地址范围,寄存器描述如下:
我们留意parange,如下解释
TCR寄存器是Translation Control Register,这里用来控制虚拟地址到物理地址转换的一些特性,本文介绍其中一些bit的含义
其寄存器描述如下:
这里TCR_EL1用作EL1下的虚拟地址到物理地址转换信息,能够设置虚拟地址大小,页表格式,缓存策略,访问权限等。下面详细介绍一些位
T0SZ用作设置ttbr0的size,顾名思义,具体解释如下
配置inner内存属性为 如果是1,则是write-back,read-allocate,write-allocate cacheable 。
orgn0 是配置outer 如果是1 则内存属性为 write-back,read-allocate,write-allocate cacheable 。
配置cache的模式,如果是11则为inner shareable
配置虚拟地址页面大小,如果是00,则是4k
配置物理地址大小,如果是0x101则 48bit 256TB
配置tlb miss后,是否遍历ttbrx,还是触发translation fault