编辑
2025-04-03
工作知识
0
请注意,本文编写于 64 天前,最后修改于 64 天前,其中某些信息可能已经过时。

目录

一、AArch64startsetvectorbase
二、SMPGetcurrentprocessor
三、zynqmpsetupmmuandcache
3.2 aarch64mmumap_block
3.3 aarch64mmuenable
四、bspstartclear_bss
五、总结

在进行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流程介绍完毕