第二章目录:
Chapter 02.TF-A(Arm Trusted Firmware, ATF ) BL1-ROMCode(本文)
Chapter 02.TF-A(Arm Trusted Firmware, ATF ) BL1-ROMCode - Continued(1)
Chapter 02.TF-A(Arm Trusted Firmware, ATF ) BL1-ROMCode - Continued(2)
下一章:
Chapter 03.TF-A(Arm Trusted Firmware, ATF ) Chain of Trust (CoT)
export CROSS_COMPILE=
Make PLAT=fvp COLD_BOOT_SINGLE_CPU=1 SPD=tspd bl1
变量:
COLD_BOOT_SINGLE_CPU=1 复位时只release一个core,HW设计时确定的
复位入口地址:Cold/warm reset entry:bl1_entrypoint
文件:bl1_entrypoint.S
Ld文件:bl1.ld.s
在编译bl1时,会在./build/
BL1阶段从EL3上的平台复位向量开始执行。复位地址是平台确定的,通常位于 trust ROM上。 BL1 的data section在运行时复制到 trust SRAM。
在Arm开发平台上,BL1代码从BL1_RO_BASE位置开始执行。 BL1 data section 复制到trust SRAM的顶部,由BL1_RW_BASE定义。
BL阶段实现的功能如下:
当CPU从复位释放时,BL1需要区分 warm boot和cold boot。 这是使用平台特定的机制完成的(参考Porting Guide中的plat_get_my_entrypoint()函数)。 在warm boot的情况下,CPU应该从一个不同于cold boot的单独的入口继续执行。 在cold boot的情况下,从CPU处于特定于安全平台的状态(参考Porting Guide的plat_secondary_cold_boot_setup()函数),主CPU执行剩余的cold boot路径。
注意,只有在PROGRAMMABLE_RESET_ADDRESS = 0时适用。 有关PROGRAMMABLE_RESET_ADDRESS平台build选项的效果的更多信息,参考Reset Design 。
BL1执行以下的最小的架构初始化:
BL1为同步和异步异常设置简单的异常向量。 接收异常时的默认行为是填充通用寄存器X0 / R0中的状态代码并调用plat_report_exception()函数(参考Porting Guide)。 status code :
对于AArch64:
0x0 : Synchronous exception from Current EL with SP_EL0
0x1 : IRQ exception from Current EL with SP_EL0
0x2 : FIQ exception from Current EL with SP_EL0
0x3 : System Error exception from Current EL with SP_EL0
0x4 : Synchronous exception from Current EL with SP_ELx
0x5 : IRQ exception from Current EL with SP_ELx
0x6 : FIQ exception from Current EL with SP_ELx
0x7 : System Error exception from Current EL with SP_ELx
0x8 : Synchronous exception from Lower EL using aarch64
0x9 : IRQ exception from Lower EL using aarch64
0xa : FIQ exception from Lower EL using aarch64
0xb : System Error exception from Lower EL using aarch64
0xc : Synchronous exception from Lower EL using aarch32
0xd : IRQ exception from Lower EL using aarch32
0xe : FIQ exception from Lower EL using aarch32
0xf : System Error exception from Lower EL using aarch32
Arm FVP上的plat_report_exception() 按以下格式对Versatile Express系统LED寄存器进行编程,表示异常:
SYS_LED[0] - Security state (Secure=0/Non-Secure=1) SYS_LED[2:1] - Exception Level (EL3=0x3, EL2=0x2, EL1=0x1, EL0=0x0) For AArch32 it is always 0x0 SYS_LED[7:3] - Exception Class (Sync/Async & origin). This is the value of the status code |
这是fvp的调试手段。可以参数下。
对LED寄存器的写入反映在FVP的CLCD window的System LED(S6LED0..7)中。
除了SMC异常,BL1不希望收到任何异常。BL1初始化安装一个stub代码。 stub期望收到一组有限的SMC类型(由通用寄存器X0 / R0中的功能ID确定):
除了SMC异常,BL1不希望收到任何异常。BL1初始化安装一个stub代码。 stub期望收到一组有限的SMC类型(由通用寄存器X0 / R0中的功能ID确定):
2. CPU 初始化
BL1调用reset_handler()函数,该函数又调用CPU特定的复位处理函数(SoC厂商自己实现一个plat_开头的函数)
3. Control register 设置(AArch64)
文档里说的很清楚了,直接贴原文:
SCTLR_EL3. Instruction cache is enabled by setting the SCTLR_EL3.I bit. Alignment and stack alignment checking is enabled by setting the SCTLR_EL3.A and SCTLR_EL3.SA bits. Exception endianness is set to little-endian by clearing the SCTLR_EL3.EE bit.
SCR_EL3. The register width of the next lower exception level is set to AArch64 by setting the SCR.RW bit. The SCR.EA bit is set to trap both External Aborts and SError Interrupts in EL3. The SCR.SIF bit is also set to disable instruction fetches from Non-secure memory when in secure state.
CPTR_EL3. Accesses to the CPACR_EL1 register from EL1 or EL2, or the CPTR_EL2 register from EL2 are configured to not trap to EL3 by clearing the CPTR_EL3.TCPAC bit. Access to the trace functionality is configured not to trap to EL3 by clearing the CPTR_EL3.TTA bit. Instructions that access the registers associated with Floating Point and Advanced SIMD execution are configured to not trap to EL3 by clearing the CPTR_EL3.TFP bit.
DAIF. The SError interrupt is enabled by clearing the SError interrupt mask bit.
MDCR_EL3. The trap controls, MDCR_EL3.TDOSA, MDCR_EL3.TDA and MDCR_EL3.TPM, are set so that accesses to the registers they control do not trap to EL3. AArch64 Secure self-hosted debug is disabled by setting the MDCR_EL3.SDD bit. Also MDCR_EL3.SPD32 is set to disable AArch32 Secure self-hosted privileged debug from S-EL1.
对于ARM fvp,BL1 执行下列的初始化步骤:
执行平台设置后,BL1公共代码调用bl1_plat_get_next_image_id()以确定是否需要固件更新或继续正常启动过程。 如果平台代码返回BL2_IMAGE_ID,则正常引导BL2,否则BL1假定需要进行固件更新,并且执行将传递到固件更新过程中的第一个映像(固件更新镜像可能是多个)。 在任何一种情况下,BL1都是通过调用bl1_plat_get_image_desc()来获取下一个image的描述符。 image描述符包含entry_point_info_t结构体,BL1用它来初始化下一image的执行状态
在正常启动流程中,BL1继续执行:
Booting Trusted Firmware
Failed to load BL2 firmware.
BL1将控制权交给安全EL1(对于AArch64)或安全SVC模式(对于AArch32)的BL2,从BL2的加载地址开始执
先看bl1.ld.s
15行,BL_RO_BASE在arm-def.h中定义
361行,在platform.h中定义
Fvp平台上是reset entry在00000000地址
大小:64MB
注意这个USE_ROM_LIB:
在make_helpers/defaults.mk中:
具体作用,官方并没有给出详细介绍,后面通过代码分析。
同理,RAM地址(这里指的是SRAM):0403 5000~0404 0000 大小:B000,44K。
后面分析bl2时会发现,实际SRAM大小为:
(platform_def.h),但bl2会占用一部分SRAM空间。
关注bl1.ld.s的25行,默认的编译选项是SEPARATE_CODE_AND_RODATA =1的。
bl1_entrypoint.S
代码不多,直接贴出来
27行,在.include\common\aarch64\el3_common_macros.S line 174 中定义的宏,
199行,设置sctlr_el3:清sctlr_el3.EE(设置小端)sctlr_el3.WXN memory access permissions disable,SCTLR_EL3.SA (disable Stack Alignment check) SCTLR_EL3.A(disable Alignment fault checking)
默认 PROGRAMMABLE_RESET_ADDRESS = 0 ,则执行213行
plat_get_my_entrypoint在plat\arm\board\fvp\aarch64\fvp_helpers.S,124行
主要工作是区分cold reset 和warm reset。 FVP上是可以从The Power Control SYS Status Register (PSYSR) 获取CPU wake-up原因。
plat_get_my_entrypoint()
在禁用MMU和Cache的情况下调用此函数(SCTLR_EL3.M = 0且SCTLR_EL3.C = 0)。 该函数负责使用各个平台自己的方法区分当前CPU的warm boot和cold boot。 如果是热复位,则它返回在BL31初始化期间提供给plat_setup_psci_ops()的warm boot入口点。 如果是冷复位,必须返回零。
简单的说,这个函数在cold boot时返回0,warm boot, 返回malibox中包含的地址相关信息
,这个地址很有可能是电源状态转换之前设置的入口地址。各个平台需要实现:
回到el3_entrypoint_common,如果是cold boot,则执行do_cold_boot,则否branch到x0地址执行(mailbox返回的一个地址)
224行,加载exception_vector->x0->vbar_el3。exception_vector宏参数,值为bl1_exceptions
如果是cold boot,在234行跳转到reset_handler(lib/cpus/aarch64/cpu_helpers.S, 行24)
Arm提供的reset_handler是一个通用架构,各个平台需要自己实现plat_reset_handler
我们看下关于reset_handler 和plat_reset_handler(porting-guide.html#handling-reset)
复位要做的工作:
For each CPU, the reset vector code is responsible for the following tasks:
- Distinguishing between a cold boot and a warm boot.
- In the case of a cold boot and the CPU being a secondary CPU, ensuring that the CPU is placed in a platform-specific state until the primary CPU performs the necessary steps to remove it from this state.
In the case of a warm boot, ensuring that the CPU jumps to a platform- specific address in the BL31 image in the same processor mode as it was when released from reset.
reset_handler
Plat_reset_handler
看arm是如何要求的:
A platform may need to do additional initialization after reset. This function allows the platform to do the platform specific intializations. Platform specific errata workarounds could also be implemented here. The api should preserve the values of callee saved registers x19 to x29.
The default implementation doesn’t do anything. If a platform needs to override the default implementation, refer to the Firmware Design for general guidelines.
After a reset, the state of the CPU when it calls generic reset handler is: MMU turned off, both instruction and data caches turned off and not part of any coherency domain.
The BL entrypoint code first invokes the plat_reset_handler() to allow the platform to perform any system initialization required and any system errata workarounds that needs to be applied. The get_cpu_ops_ptr() reads the current CPU midr, finds the matching cpu_ops entry in the cpu_ops array and returns it. Note that only the part number and implementer fields in midr are used to find the matching cpu_ops entry. The reset_func() in the returned cpu_ops is then invoked which executes the required reset handling for that CPU and also any errata workarounds enabled by the platform. This function must preserve the values of general purpose registers x20 to x29.
Refer to Section “Guidelines for Reset Handlers” for general guidelines regarding placement of code in a reset handler.
复位后,CPU调用通用复位处理程序时的状态为:MMU关闭,指令和数据缓存都关闭,不属于任何一致性域。
BL入口代码首先调用plat_reset_handler()以允许平台执行所需的系统初始化以及需要应用的系统勘误表解决方法。 get_cpu_ops_ptr()读取当前的CPU midr,在cpu_ops数组中找到匹配的cpu_ops入口并将它返回。 请注意,只有midr中的part number and implementer 字段用于查找匹配的cpu_ops入口。 然后调用返回的cpu_ops中的reset_func(),它执行该CPU所需的复位处理以及平台启用的勘误解决方法。 此函数必须保留通用寄存器x20到x29的值。
Arm fvp平台上plat_reset_handler(plat/common/aarch64/platform_helpers.S,122行)什么也没做,其他厂商,marvel在这里使能L1/L2 ECC(MMU操作之前使能),rockchip针对A72和其他Soc修正的一些寄存器值
回到reset_handler,在看get_cpu_ops_ptr(lib/cpus/aarch64/cpu_helpers.S, 144行),前面已经说了,他的作用是获取获取一个cpu_ops, cpu_ops是一个会变定义的结构体内容是:
实现方式是根据MIDR匹配这个结构体数组,返回在cpu_ops从 __CPU_OPS_START__处的偏移量。
事实上,在编译fvp版本的bl1时,会把lib/aarch64/目录下的文件进行编译连接(可参考下platform.mk),所有的cpu_ops都放在cpu_opssection通过midr查找匹配。
reset_handler第37~43行,
获取cpu_reset_func的地址,跳转执行,使用br的意义应该是非子例程返回,即不更新x30寄存器,应该是在cpu_reset_func中自己更新或者不需要更新?
cpu_reset_func,我们使用cortex_a35进行说明,代码在lib/aarch64/cortex_a35.S中,44行
至此reset_handler 执行完成,返回调用者bl1_entrypoint->.macro.el3_entrypoint_common,执行236行:
在el3_common_macro.S,16行,主要设置sctlr_el3,
初始化SCR_EL3
初始化MDCR_EL3
使能Abort和Serror中断
初始化CPTR_EL3
bl1_entrypoint->.macro.el3_entrypoint_common,238行
由于设置了COLD_BOOT_SINGLE_CPU=1,在参数传递时
不执行这部分代码
继续,初始化memory
\plat\arm\common\aarch64\arm_helpers.S,81行
在porting guide中:
目前git上还没有厂商实现
继续bl1_entrypoint->.macro.el3_entrypoint_common,276行. 293行~299行,初始化C环境。清bss,由于默认USE_COHERENT_MEM =1,清coherent_ram段。Copy .data* 段数据到__DATA_RAM_START__ (0x04035000)
使用SP_EL0作为C runtime的SP
设置stack
plat\common\aarch64\platform_up_stack.S
40行,get_up_stack是一个宏,在include\common\aarch64\asm_macros.S中定义的
第二个platform_normal_stacks:
Declare_stack 在(include/commonaarch64/asm_macro_common.S,92行):
将栈放到.section tzfw_normal_stacks段(对应到bl1.ld.s ,122行)
栈大小0x450,位置__STACKS_END__=0x43035540,__STACKS_START__=0x40350f1,SP:SP_EL0
回到bl1_entrypoint->.macro.el3_entrypoint_common,337行
由于默认STACK_PROTECTOR_ENABLED=0,暂时不做分析。
至此,el3_entrypoint_common宏执行完毕。
继续执行bl1_entrypoint,
(To be continued )