vector_base bl1_vector_table
b bl1_entrypoint
b report_exception /* Undef */
b bl1_aarch32_smc_handler /* SMC call */
b report_exception /* Prefetch abort */
b report_exception /* Data abort */
b report_exception /* Reserved */ b report_exception /* IRQ */ b report_exception /* FIQ */
func bl1_aarch32_smc_handler /* ------------------------------------------------ * SMC in BL1 is handled assuming that the MMU is * turned off by BL2. * ------------------------------------------------ */
/* ----------------------------------------------
* Only RUN_IMAGE SMC is supported.
* ----------------------------------------------
*/<span>
mov r8, #BL1_SMC_RUN_IMAGE--------------------------<span style="color: rgba(255, 0, 0, 1)">仅支持BL1_SMC_RUN_IMAGE SMC调用;其他调用触发report_exception。</span>
cmp r8, r0
blne report_exception
/* ------------------------------------------------
* Make sure only Secure world reaches here.
* ------------------------------------------------
*/<span>
ldcopr r8, SCR
tst r8, #SCR_NS_BIT---------------------------------<span style="color: rgba(255, 0, 0, 1)">如果处于非安全状态,则触发report_exception。</span>
blne report_exception
/* ---------------------------------------------------------------------
* Pass control to next secure image.
* Here it expects r1 to contain the address of a entry_point_info_t
* structure describing the BL entrypoint.
* ---------------------------------------------------------------------
*/<span>
mov r8, r1------------------------------------------第一个参数r0是功能id,即BL1_SMC_RUN_IMAGE_SMC。第二个参数是entry_point_info_t变量。
mov r0, r1
bl bl1_print_next_bl_ep_info
#if SPIN_ON_BL1_EXIT bl print_debug_loop_message debug_loop: b debug_loop #endif
mov r0, r8
bl bl1_plat_prepare_exit
stcopr r0, TLBIALL
dsb sy
isb
/*
* Extract PC and SPSR based on struct `entry_point_info_t`
* and load it in LR and SPSR registers respectively.
*/<span>
ldr lr, [r8, #ENTRY_POINT_INFO_PC_OFFSET]
ldr r1, [r8, #(ENTRY_POINT_INFO_PC_OFFSET + 4<span>)]
msr spsr, r1
add r8, r8, #ENTRY_POINT_INFO_ARGS_OFFSET
ldm r8, {r0, r1, r2, r3}----------------------------执行跳转到BL31。
eret
func bl1_entrypoint
/* ---------------------------------------------------------------------
* If the reset address is programmable then bl1_entrypoint() is
* executed only on the cold boot path. Therefore, we can skip the warm
* boot mailbox mechanism.
* ---------------------------------------------------------------------
*/
el3_entrypoint_common \
_set_endian=1 \----------------------是否设定大小端。
_warm_boot_mailbox=!PROGRAMMABLE_RESET_ADDRESS \-----是否检查当前属于冷启动还是热启动。
_secondary_cold_boot=!COLD_BOOT_SINGLE_CPU \---------确定当前CPU是主CPU还是从CPU。
_init_memory=1 \---------------------是否初始化memory。 _init_c_runtime=1 \----------------------是否初始化C语言执行环境。 _exception_vectors=bl1_exceptions-----------------------异常向量表。
/* ---------------------------------------------
* Architectural init. can be generic e.g.
* enabling stack alignment and platform spec-
* ific e.g. MMU & page table setup as per the
* platform memory map. Perform the latter here
* and the former in bl1_main.
* ---------------------------------------------
*/<span>
bl bl1_early_platform_setup-------------------初始化memory、page table,所需外围设备初始化等。
bl bl1_plat_arch_setup
/* --------------------------------------------------
* Initialize platform and jump to our c-entry point
* for this type of reset.
* --------------------------------------------------
*/<span>
bl <a href="#bl1_main" rel="noopener">bl1_main</a>--------------------------------------------进行必要初始化,加载BL2镜像然后为退出EL3进入S.EL1做好准备。<br> /* --------------------------------------------------
* Do the transition to next boot image.
* --------------------------------------------------
*/<span>
b <a href="#el3_exit" rel="noopener">el3_exit</a>
.if<span> \_set_endian-------------------------------------------------------在进行内存读写之前,设置好系统的大小端。
/* -------------------------------------------------------------
* Set the CPU endianness before doing anything that might
* involve memory reads or writes.
* -------------------------------------------------------------
*/<span>
mrs x0, sctlr_el3
bic x0, x0, #SCTLR_EE_BIT
msr sctlr_el3, x0
isb
.endif /* _set_endian */<span>
.if<span> \_warm_boot_mailbox------------------------------------------------根据当前平台的entrypoint判断是冷启动还是热启动。
/* -------------------------------------------------------------
* This code will be executed for both warm and cold resets.
* Now is the time to distinguish between the two.
* Query the platform entrypoint address and if it is not zero
* then it means it is a warm boot so jump to this address.
* -------------------------------------------------------------
*/<span>
bl plat_get_my_entrypoint
cbz x0, do_cold_boot--------------------------------------------如果为0说明是冷启动,执行do_cold_boot();非0则跳转到entrypoint执行。
br x0
do_cold_boot:
.endif /* _warm_boot_mailbox */
/* ---------------------------------------------------------------------
* It is a cold boot.
* Perform any processor specific actions upon reset e.g. cache, TLB
* invalidations etc.
* ---------------------------------------------------------------------
*/<span>
bl reset_handler---------------------------------------------------执行reset_handler()。
el3_arch_init_common \_exception_vectors------------------------------初始化异常向量。
.if<span> \_secondary_cold_boot---------------------------------------------判断当前CPU是主CPU还是从CPU。
/* -------------------------------------------------------------
* Check if this is a primary or secondary CPU cold boot.
* The primary CPU will set up the platform while the
* secondaries are placed in a platform-specific state until the
* primary CPU performs the necessary actions to bring them out
* of that state and allows entry into the OS.
* -------------------------------------------------------------
*/<span>
bl plat_is_my_cpu_primary
cbnz w0, do_primary_cold_boot
/* This is a cold boot on a secondary CPU */<span>
bl plat_secondary_cold_boot_setup
/* plat_secondary_cold_boot_setup() is not supposed to return */<span>
bl el3_panic
do_primary_cold_boot:
.endif /* _secondary_cold_boot */
/* ---------------------------------------------------------------------
* Initialize memory now. Secondary CPU initialization won't get to this
* point.
* ---------------------------------------------------------------------
*/<span>
.if<span> \_init_memory----------------------------------------------------初始化内存。
bl platform_mem_init
.endif /* _init_memory */
/* ---------------------------------------------------------------------
* Init C runtime environment:
* - Zero-initialise the NOBITS sections. There are 2 of them:
* - the .bss section;
* - the coherent memory section (if any).
* - Relocate the data section from ROM to RAM, if required.
* ---------------------------------------------------------------------
*/<span>
.if<span> \_init_c_runtime-------------------------------------------------初始化C执行环境。
#if IMAGE_BL31 /* ------------------------------------------------------------- * Invalidate the RW memory used by the BL31 image. This * includes the data and NOBITS sections. This is done to * safeguard against possible corruption of this memory by * dirty cache lines in a system cache as a result of use by * an earlier boot loader stage. * ------------------------------------------------------------- / adr x0, RW_START adr x1, RW_END sub x1, x1, x0 bl inv_dcache_range #endif / IMAGE_BL31 */
/* ---------------------------------------------------------------------
* Use SP_EL0 for the C runtime stack.
* ---------------------------------------------------------------------
*/<span>
msr spsel, #0
/* ---------------------------------------------------------------------
* Allocate a stack whose memory will be marked as Normal-IS-WBWA when
* the MMU is enabled. There is no risk of reading stale stack memory
* after enabling the MMU as only the primary CPU is running at the
* moment.
* ---------------------------------------------------------------------
*/<span>
bl plat_set_my_stack
.endm</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
/*******************************************************************************
* Function to perform late architectural and platform specific initialization.
* It also queries the platform to load and run next BL image. Only called
* by the primary cpu after a cold boot.
******************************************************************************/
void bl1_main(void)
{
unsigned int image_id;
/* Perform platform setup in BL1. */<span>
bl1_platform_setup();
/* Get the image id of next image to load and run. */<span>
image_id =<span> bl1_plat_get_next_image_id();----------------------<span style="color: rgba(255, 0, 0, 1)">获取下一级启动镜像的ID。</span>
/*
* We currently interpret any image id other than
* BL2_IMAGE_ID as the start of firmware update.
*/
if (image_id ==<span> BL2_IMAGE_ID)
<a href="#bl1_load_bl2" rel="noopener">bl1_load_bl2</a>();------------------------------------------<span style="color: rgba(255, 0, 0, 1)">将BL2镜像加载到TSRAM中。</span>
else<span>
NOTICE("BL1-FWU: *******FWU Process Started*******\n"<span>);
<a href="#bl1_prepare_next_image" rel="noopener">bl1_prepare_next_image</a>(image_id);----------------------------<span style="color: rgba(255, 0, 0, 1)">获取image_id对应镜像描述信息,并为进入下一级镜像执行准备好上下文。</span>
if<span> (err) {
ERROR("Failed to load BL2 firmware.\n"<span>);
plat_error_handler(err);
}
/*
* Create a new layout of memory for BL2 as seen by BL1 i.e.
* tell it the amount of total and free memory available.
* This layout is created at the first free address visible
* to BL2. BL2 will read the memory layout before using its
* memory for other purposes.
*/
This function prepares the context for Secure/Normal world images.
Normal world images are transitioned to EL2(if supported) else EL1. ******************************************************************************/ void bl1_prepare_next_image(unsigned int image_id) { unsigned int security_state; image_desc_t *image_desc; entry_point_info_t *next_bl_ep;
#if CTX_INCLUDE_AARCH32_REGS /* * Ensure that the build flag to save AArch32 system registers in CPU * context is not set for AArch64-only platforms. */ if (((read_id_aa64pfr0_el1() >> ID_AA64PFR0_EL1_SHIFT) & ID_AA64PFR0_ELX_MASK) == 0x1) { ERROR("EL1 supports AArch64-only. Please set build flag " “CTX_INCLUDE_AARCH32_REGS = 0”); panic(); } #endif
/* Get the image descriptor. */<span>
image_desc =<span> bl1_plat_get_image_desc(image_id);---------------<span style="color: rgba(255, 0, 0, 1)">获取镜像描述信息,包括入口地址、名字等等。</span>
assert(image_desc);
/* Get the entry point info. */<span>
next_bl_ep = &image_desc-><span>ep_info;
/* Get the image security state. */<span>
security_state = GET_SECURITY_STATE(next_bl_ep-><span>h.attr);------<span style="color: rgba(255, 0, 0, 1)">镜像是属于安全还是非安全镜像。</span>
/* Setup the Secure/Non-Secure context if not done already. */
if (!<span>cm_get_context(security_state))
cm_set_context(&<span>bl1_cpu_context[security_state], security_state);
/* Prepare the SPSR for the next BL image. */
if (security_state ==<span> SECURE) {-------------------------------<span style="color: rgba(255, 0, 0, 1)">设置镜像运行的SPSR数据。</span>
next_bl_ep->spsr =<span> SPSR_64(MODE_EL1, MODE_SP_ELX,
DISABLE_ALL_EXCEPTIONS);
} else<span> {
/* Use EL2 if supported else use EL1. */
if (read_id_aa64pfr0_el1() &<span>
(ID_AA64PFR0_ELX_MASK <<<span> ID_AA64PFR0_EL2_SHIFT)) {
next_bl_ep->spsr =<span> SPSR_64(MODE_EL2, MODE_SP_ELX,
DISABLE_ALL_EXCEPTIONS);
} else<span> {
next_bl_ep->spsr =<span> SPSR_64(MODE_EL1, MODE_SP_ELX,
DISABLE_ALL_EXCEPTIONS);
}
}
/* Allow platform to make change */<span>
bl1_plat_set_ep_info(image_id, next_bl_ep);
/* Prepare the context for the next BL image. */<span>
cm_init_my_context(next_bl_ep);
cm_prepare_el3_exit(security_state);--------------------------<span style="color: rgba(255, 0, 0, 1)">为运行下一个镜像,EL3做好准备。</span>
/* Indicate that image is in execution state. */<span>
image_desc->state =<span> IMAGE_STATE_EXECUTED;
print_entry_point_info(next_bl_ep);---------------------------<span style="color: rgba(255, 0, 0, 1)">打印下一级进行相关信息。下面即将el3_exit,退出EL3进入新的进项运行。</span>
func bl2_entrypoint
/*---------------------------------------------
* Save from x1 the extents of the tzram
* available to BL2 for future use.
* x0 is not currently used.
* ---------------------------------------------
*/
mov x20, x1
/* ---------------------------------------------
* Set the exception vector to something sane.
* ---------------------------------------------
*/<span>
adr x0, early_exceptions
msr vbar_el1, x0-------------------------------------<span style="color: rgba(255, 0, 0, 1)">设定异常向量。</span>
isb
/* ---------------------------------------------
* Enable the SError interrupt now that the
* exception vectors have been setup.
* ---------------------------------------------
*/<span>
msr daifclr, #DAIF_ABT_BIT
/* ---------------------------------------------
* Enable the instruction cache, stack pointer
* and data access alignment checks
* ---------------------------------------------
*/<span>
mov x1, #(SCTLR_I_BIT | SCTLR_A_BIT |<span> SCTLR_SA_BIT)
mrs x0, sctlr_el1
orr x0, x0, x1
msr sctlr_el1, x0----------------------------------<span style="color: rgba(255, 0, 0, 1)">配置cache、内存对齐等属性。</span>
isb
/* ---------------------------------------------
* Invalidate the RW memory used by the BL2
* image. This includes the data and NOBITS
* sections. This is done to safeguard against
* possible corruption of this memory by dirty
* cache lines in a system cache as a result of
* use by an earlier boot loader stage.
* ---------------------------------------------
*/<span>
adr x0, __RW_START__
adr x1, __RW_END__
sub x1, x1, x0
bl inv_dcache_range-------------------------------<span style="color: rgba(255, 0, 0, 1)">将BL2的__RW_START__和__RW_END__之间的内存刷回DDR中。</span>
/* ---------------------------------------------
* Zero out NOBITS sections. There are 2 of them:
* - the .bss section;
* - the coherent memory section.
* ---------------------------------------------
*/<span>
ldr x0, =<span>__BSS_START__
ldr x1, =<span>__BSS_SIZE__
bl zeromem16--------------------------------------<span style="color: rgba(255, 0, 0, 1)">初始化__BSS_START__开始,大小为__BSS_SIZE__内存为0。</span>
/* --------------------------------------------
* Allocate a stack whose memory will be marked
* as Normal-IS-WBWA when the MMU is enabled.
* There is no risk of reading stale stack
* memory after enabling the MMU as only the
* primary cpu is running at the moment.
* --------------------------------------------
*/<span>
bl plat_set_my_stack
/* ---------------------------------------------
* Perform early platform setup & platform
* specific early arch. setup e.g. mmu setup
* ---------------------------------------------
*/<span>
mov x0, x20
bl bl2_early_platform_setup
bl bl2_plat_arch_setup
/* ---------------------------------------------
* Jump to main function.
* ---------------------------------------------
*/<span>
bl bl2_main------------------------------------<span style="color: rgba(255, 0, 0, 1)">跳转到BL2主函数执行,该函数加载BL3x镜像,并通过SMC调用BL1指定SMC函数将CPU执行权交给BL31。</span>
/* ---------------------------------------------
* Should never reach this point.
* ---------------------------------------------
*/<span>
no_ret plat_panic_handler
/*******************************************************************************
* The only thing to do in BL2 is to load further images and pass control to
* next BL. The memory occupied by BL2 will be reclaimed by BL3x stages. BL2
* runs entirely in S-EL1.
******************************************************************************/
void bl2_main(void)
{
entry_point_info_t *next_bl_ep_info;
#ifdef AARCH32 /* * For AArch32 state BL1 and BL2 share the MMU setup. * Given that BL2 does not map BL1 regions, MMU needs * to be disabled in order to go back to BL1. / disable_mmu_icache_secure(); #endif / AARCH32 */
/*
* Run next BL image via an SMC to BL1. Information on how to pass
* control to the BL32 (if present) and BL33 software images will
* be passed to next BL image as an argument.
*/<span>
smc(BL1_SMC_RUN_IMAGE, (unsigned long)next_bl_ep_info, 0, 0, 0, 0, 0, 0<span>);----------<span style="color: rgba(255, 0, 0, 1)">发起SMC异常启动BL31镜像,交给BL1 <a href="#bl1_aarch32_smc_handler" rel="noopener"><span style="color: rgba(255, 0, 0, 1)">bl1_aarch32_smc_handler</span></a>处理。</span>
This function loads SCP_BL2/BL3x images and returns the ep_info for
the next executable image. ******************************************************************************/ entry_point_info_t *bl2_load_images(void) { bl_params_t *bl2_to_next_bl_params; bl_load_info_t *bl2_load_info; const bl_load_info_node_t *bl2_node_info; int plat_setup_done = 0; int err;
/*
Get information about the images to load. */ bl2_load_info = plat_get_bl_image_load_info();-----------------获取待加载镜像BL3x或SCP_EL2信息,然后遍历处理。 assert(bl2_load_info); assert(bl2_load_info->head); assert(bl2_load_info->h.type == PARAM_BL_LOAD_INFO); assert(bl2_load_info->h.version >= VERSION_2); bl2_node_info = bl2_load_info->head;---------------------------bl2_node_info指向镜像第一个对象。
while (bl2_node_info) {----------------------------------------循环遍历bl2_mem_params_descs成员并加载处理。 /* * Perform platform setup before loading the image, * if indicated in the image attributes AND if NOT * already done before. */ if (bl2_node_info->image_info->h.attr & IMAGE_ATTRIB_PLAT_SETUP) {----确定加载前是否需要进行特定平台初始化。 if (plat_setup_done) { WARN(“BL2: Platform setup already done!!\n”); } else { INFO(“BL2: Doing platform setup\n”); bl2_platform_setup(); plat_setup_done = 1; } }
if (!(bl2_node_info->image_info->h.attr &<span> IMAGE_ATTRIB_SKIP_LOADING)) {----<span style="color: rgba(255, 0, 0, 1)">确定是否需要跳过加载到RAM步骤;如否,则进行验证并加载。</span>
INFO("BL2: Loading image id %d\n", bl2_node_info-><span>image_id);
err = load_auth_image(bl2_node_info-><span>image_id,
bl2_node_info-><span>image_info);----------------------------------------<span style="color: rgba(255, 0, 0, 1)">将镜像加载到RAM,然后进行验证。</span>
if<span> (err) {
ERROR("BL2: Failed to load image (%i)\n"<span>, err);
plat_error_handler(err);
}
} else<span> {
INFO("BL2: Skip loading image id %d\n", bl2_node_info-><span>image_id);
}
/* Allow platform to handle image information. */<span>
err = bl2_plat_handle_post_image_load(bl2_node_info-><span>image_id);-------------<span style="color: rgba(255, 0, 0, 1)">修改特定镜像的加载信息。</span>
if<span> (err) {
ERROR("BL2: Failure in post image load handling (%i)\n"<span>, err);
plat_error_handler(err);
}
/* Go to next image */<span>
bl2_node_info = bl2_node_info-><span>next_load_info;------------------------------<span style="color: rgba(255, 0, 0, 1)">循环加载下一个镜像。</span>
}
/*
Get information to pass to the next image. */ bl2_to_next_bl_params = plat_get_next_bl_params();------------------------------获取下一个执行镜像入口信息,属性为EXECUTABLE和EP_FIRST_EXE,也即BL31。 assert(bl2_to_next_bl_params); assert(bl2_to_next_bl_params->head); assert(bl2_to_next_bl_params->h.type == PARAM_BL_PARAMS); assert(bl2_to_next_bl_params->h.version >= VERSION_2);
/* Flush the parameters to be passed to next image */ plat_flush_next_bl_params();----------------------------------------------------将bl_mem_params_desc_ptr数据刷到DDR中,后面即将通过SMC跳转到BL1启动BL31,保持数据一致性。
func bl31_entrypoint
#if !RESET_TO_BL31
/* ---------------------------------------------------------------
* Preceding bootloader has populated x0 with a pointer to a
* 'bl31_params' structure & x1 with a pointer to platform
* specific structure
* ---------------------------------------------------------------
*/
#if HISILICON
/* hisilicon use args from uboot,
* load_fip.c has set value in address 8 and 16
*/
mov x0, 8
mov x1, 16
ldr x20, [x0]
ldr x21, [x1]
#else
mov x20, x0
mov x21, x1
#endif
/* ---------------------------------------------------------------------
* For !RESET_TO_BL31 systems, only the primary CPU ever reaches
* bl31_entrypoint() during the cold boot flow, so the cold/warm boot
* and primary/secondary CPU logic should not be executed in this case.
*
* Also, assume that the previous bootloader has already set up the CPU
* endianness and has initialised the memory.
* ---------------------------------------------------------------------
*/
el3_entrypoint_common \
_set_endian=0 \
_warm_boot_mailbox=0 \
_secondary_cold_boot=0 \
_init_memory=0 \
_init_c_runtime=1 \
_exception_vectors=runtime_exceptions----------------------------runtime_exceptions是ATF的异常向量表。
#else / --------------------------------------------------------------------- * For RESET_TO_BL31 systems which have a programmable reset address, * bl31_entrypoint() is executed only on the cold boot path so we can * skip the warm boot mailbox mechanism. * --------------------------------------------------------------------- / el3_entrypoint_common _set_endian=1 _warm_boot_mailbox=!PROGRAMMABLE_RESET_ADDRESS _secondary_cold_boot=!COLD_BOOT_SINGLE_CPU _init_memory=1 _init_c_runtime=1 _exception_vectors=runtime_exceptions
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> ---------------------------------------------------------------------
* For RESET_TO_BL31 systems, BL31 is the first bootloader to run so
* there's no argument to relay from a previous bootloader. Zero the
* arguments passed to the platform layer to reflect that.
* ---------------------------------------------------------------------
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
mov x0, </span><span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">
mov x1, </span><span style="color: rgba(128, 0, 128, 1)">0</span>
/ ------------------------------------------------------------- * Clean the .data & .bss sections to main memory. This ensures * that any global data which was initialised by the primary CPU * is visible to secondary CPUs before they enable their data * caches and participate in coherency. * ------------------------------------------------------------- / adr x0, DATA_START adr x1, DATA_END sub x1, x1, x0 bl clean_dcache_range
/* -----------------------------------------------------
* This routine assumes that the SP_EL3 is pointing to
* a valid context structure from where the gp regs and
* other special registers can be retrieved.
* -----------------------------------------------------
*/
func el3_exit
/* -----------------------------------------------------
* Save the current SP_EL0 i.e. the EL3 runtime stack
* which will be used for handling the next SMC. Then
* switch to SP_EL3
* -----------------------------------------------------
*/
mov x17, sp
msr spsel, #1
str x17, [sp, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP]
/*******************************************************************************
* BL31 is responsible for setting up the runtime services for the primary cpu
* before passing control to the bootloader or an Operating System. This
* function calls runtime_svc_init() which initializes all registered runtime
* services. The run time services would setup enough context for the core to
* swtich to the next exception level. When this function returns, the core will
* switch to the programmed exception level via. an ERET.
******************************************************************************/
void bl31_main(void)
{
NOTICE("BL31: %s\n", version_string);
NOTICE("BL31: %s\n", build_message);
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> Perform platform setup in BL31 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
bl31_platform_setup();---------------------------------------------------<span style="color: rgba(255, 0, 0, 1)">初始化GIC、Timer、Power等工作。
</span></span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> Initialise helper libraries </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
bl31_lib_init();---------------------------------------------------------<span style="color: rgba(255, 0, 0, 1)">初始化BL31中相关全局变量。
</span></span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> Initialize the runtime services e.g. psci. </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
INFO(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">BL31: Initializing runtime services\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
<a href="#runtime_svc_init" rel="noopener">runtime_svc_init</a>();------------------------------------------------------<span style="color: rgba(255, 0, 0, 1)">初始化EL3 ATF中注册的服务,编译时放在rt_svc_decs特定段中。
</span></span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
* All the cold boot actions on the primary cpu are done. We now need to
* decide which is the next image (BL32 or BL33) and how to execute it.
* If the SPD runtime service is present, it would want to pass control
* to BL32 first in S-EL1. In that case, SPD would have registered a
* function to intialize bl32 where it takes responsibility of entering
* S-EL1 and returning control back to bl31_main. Once this is done we
* can prepare entry into BL33 as normal.
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
* If SPD had registerd an init hook, invoke it.
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (bl32_init) {
INFO(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">BL31: Initializing BL32\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
(</span>*<span style="color: rgba(0, 0, 0, 1)">bl32_init)();------------------------------------------------------<span style="color: rgba(255, 0, 0, 1)">如果注册了TOS支持,调用对应的init函数初始化TOS。对于optee就是<a href="#opteed_init" rel="noopener">opteed_init</a>()。</span>
}
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
* We are ready to enter the next EL. Prepare entry into the image
* corresponding to the desired security state after the next ERET.
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
bl31_prepare_next_image_entry();-----------------------------------------<span style="color: rgba(255, 0, 0, 1)">准备跳转到BL33,
</span></span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
* Perform any platform specific runtime setup prior to cold boot exit
* from BL31
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
bl31_plat_runtime_setup();-----------------------------------------------<span style="color: rgba(255, 0, 0, 1)">BL31退出前准备工作。</span>
int psci_cpu_suspend(unsigned int power_state,
uintptr_t entrypoint,
u_register_t context_id)
{
int rc;
unsigned int target_pwrlvl, is_power_down_state;
entry_point_info_t ep;
psci_power_state_t state_info = { {PSCI_LOCAL_STATE_RUN} };
plat_local_state_t cpu_pd_state;
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> Validate the power_state parameter </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
rc </span>= psci_validate_power_state(power_state, &<span style="color: rgba(0, 0, 0, 1)">state_info);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (rc !=<span style="color: rgba(0, 0, 0, 1)"> PSCI_E_SUCCESS) {
assert(rc </span>==<span style="color: rgba(0, 0, 0, 1)"> PSCI_E_INVALID_PARAMS);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> rc;
}
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
* Get the value of the state type bit from the power state parameter.
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
is_power_down_state </span>=<span style="color: rgba(0, 0, 0, 1)"> psci_get_pstate_type(power_state);
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> Sanity check the requested suspend levels </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
assert(psci_validate_suspend_req(</span>&<span style="color: rgba(0, 0, 0, 1)">state_info, is_power_down_state)
</span>==<span style="color: rgba(0, 0, 0, 1)"> PSCI_E_SUCCESS);
target_pwrlvl </span>= psci_find_target_suspend_lvl(&<span style="color: rgba(0, 0, 0, 1)">state_info);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (target_pwrlvl ==<span style="color: rgba(0, 0, 0, 1)"> PSCI_INVALID_PWR_LVL) {
ERROR(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Invalid target power level for suspend operation\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
panic();
}
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> Fast path for CPU standby.</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (is_cpu_standby_req(is_power_down_state, target_pwrlvl)) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (!psci_plat_pm_ops-><span style="color: rgba(0, 0, 0, 1)">cpu_standby)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> PSCI_E_INVALID_PARAMS;
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
* Set the state of the CPU power domain to the platform
* specific retention state and enter the standby state.
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
cpu_pd_state </span>=<span style="color: rgba(0, 0, 0, 1)"> state_info.pwr_domain_state[PSCI_CPU_PWR_LVL];
psci_set_cpu_local_state(cpu_pd_state);
#if ENABLE_PSCI_STAT / * Capture time-stamp before CPU standby * No cache maintenance is needed as caches * are ON through out the CPU standby operation. / PMF_CAPTURE_TIMESTAMP(psci_svc, PSCI_STAT_ID_ENTER_LOW_PWR, PMF_NO_CACHE_MAINT); #endif
<span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> PSCI_E_SUCCESS;
}
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
* If a power down state has been requested, we need to verify entry
* point and program entry information.
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (is_power_down_state) {
rc </span>= psci_validate_entry_point(&<span style="color: rgba(0, 0, 0, 1)">ep, entrypoint, context_id);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (rc !=<span style="color: rgba(0, 0, 0, 1)"> PSCI_E_SUCCESS)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> rc;
}
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
* Do what is needed to enter the power down state. Upon success,
* enter the final wfi which will power down this CPU. This function
* might return if the power down was abandoned for any reason, e.g.
* arrival of an interrupt
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
psci_cpu_suspend_start(</span>&<span style="color: rgba(0, 0, 0, 1)">ep,
target_pwrlvl,
</span>&<span style="color: rgba(0, 0, 0, 1)">state_info,
is_power_down_state);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> PSCI_E_SUCCESS;
}
5.3 PSCI_CPU_OFF
int psci_cpu_off(void)
{
int rc;
unsigned int target_pwrlvl = PLAT_MAX_PWR_LVL;
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
* Do what is needed to power off this CPU and possible higher power
* levels if it able to do so. Upon success, enter the final wfi
* which will power down this CPU.
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
rc </span>=<span style="color: rgba(0, 0, 0, 1)"> psci_do_cpu_off(target_pwrlvl);
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
* The only error cpu_off can return is E_DENIED. So check if that's
* indeed the case.
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
assert(rc </span>==<span style="color: rgba(0, 0, 0, 1)"> PSCI_E_DENIED);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> rc;
}
5.4 PSCI_CPU_ON
/*******************************************************************************
* PSCI frontend api for servicing SMCs. Described in the PSCI spec.
******************************************************************************/
int psci_cpu_on(u_register_t target_cpu,
uintptr_t entrypoint,
u_register_t context_id)
{ int rc; entry_point_info_t ep;
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> Determine if the cpu exists of not </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
rc </span>=<span style="color: rgba(0, 0, 0, 1)"> psci_validate_mpidr(target_cpu);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (rc !=<span style="color: rgba(0, 0, 0, 1)"> PSCI_E_SUCCESS)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> PSCI_E_INVALID_PARAMS;
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> Validate the entry point and get the entry_point_info </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
rc </span>= psci_validate_entry_point(&<span style="color: rgba(0, 0, 0, 1)">ep, entrypoint, context_id);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (rc !=<span style="color: rgba(0, 0, 0, 1)"> PSCI_E_SUCCESS)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> rc;
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
* To turn this cpu on, specify which power
* levels need to be turned on
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> psci_cpu_on_start(target_cpu, &<span style="color: rgba(0, 0, 0, 1)">ep);
}
5.5 PSCI_AFFINITY_INFO
int psci_affinity_info(u_register_t target_affinity,
unsigned int lowest_affinity_level)
{
unsigned int target_idx;
This function invokes the migrate info hook in the spd_pm_ops. It performs
the necessary return value validation. If the Secure Payload is UP and
migrate capable, it returns the mpidr of the CPU on which the Secure payload
is resident through the mpidr parameter. Else the value of the parameter on
return is undefined. ******************************************************************************/ int psci_spd_migrate_info(u_register_t *mpidr) { int rc;
if (!psci_spd_pm || !psci_spd_pm->svc_migrate_info) return PSCI_E_NOT_SUPPORTED;
/*******************************************************************************
* OPTEE Dispatcher setup. The OPTEED finds out the OPTEE entrypoint and type
* (aarch32/aarch64) if not already known and initialises the context for entry
* into OPTEE for its initialization.
******************************************************************************/
int32_t opteed_setup(void)
{
entry_point_info_t *optee_ep_info;
uint32_t linear_id;
/*******************************************************************************
* This function passes control to the OPTEE image (BL32) for the first time
* on the primary cpu after a cold boot. It assumes that a valid secure
* context has already been created by opteed_setup() which can be directly
* used. It also assumes that a valid non-secure context has been
* initialised by PSCI so it does not need to save and restore any
* non-secure state. This function performs a synchronous entry into
* OPTEE. OPTEE passes control back to this routine through a SMC.
******************************************************************************/
static int32_t opteed_init(void)
{
uint32_t linear_id = plat_my_core_pos();
optee_context_t *optee_ctx = &opteed_sp_context[linear_id];
entry_point_info_t *optee_entry_point;
uint64_t rc;
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
* Get information about the OPTEE (BL32) image. Its
* absence is a critical failure.
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
optee_entry_point </span>=<span style="color: rgba(0, 0, 0, 1)"> bl31_plat_get_next_image_ep_info(SECURE);-----------------------------<span style="color: rgba(255, 0, 0, 1)">获取optee os镜像信息。</span>
assert(optee_entry_point);
cm_init_my_context(optee_entry_point);----------------------------------------------------<span style="color: rgba(255, 0, 0, 1)">设置当前CPU进入安全状态的上下文。
</span></span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
* Arrange for an entry into OPTEE. It will be returned via
* OPTEE_ENTRY_DONE case
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
rc </span>=<span style="color: rgba(0, 0, 0, 1)"><a href="#opteed_synchronous_sp_entry" rel="noopener"> opteed_synchronous_sp_entry</a>(optee_ctx);----------------------------------------------<span style="color: rgba(255, 0, 0, 1)">启动optee os,并等待OPTEE_ENTRY_DONE返回结果。</span>
assert(rc </span>!= <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> rc;
Applies the S-EL1 system register context from optee_ctx->cpu_ctx.
Saves the current C runtime state (callee saved registers) on the stack
frame and saves a reference to this state.
Calls el3_exit() so that the EL3 system and general purpose registers
from the optee_ctx->cpu_ctx are used to enter the OPTEE image. *****************************************************************************/ uint64_t opteed_synchronous_sp_entry(optee_context_t optee_ctx) { uint64_t rc;
case TEESMC_OPTEED_RETURN_ON_DONE:-----------------------------------------表示optee由cpu_on导致的启动完成,0标识成功,其他失败。 case TEESMC_OPTEED_RETURN_RESUME_DONE:-------------------------------------表示optee从cpu_suspend导致的休眠中唤醒完成;0表示成功,其他失败。 case TEESMC_OPTEED_RETURN_OFF_DONE:----------------------------------------下面分表表示optee对cpu_off/cpu_suspend/system_off/system_reset的响应结果;0表示成功,其他表示失败。其中system_off和system_reset无返回参数。 case TEESMC_OPTEED_RETURN_SUSPEND_DONE: case TEESMC_OPTEED_RETURN_SYSTEM_OFF_DONE: case TEESMC_OPTEED_RETURN_SYSTEM_RESET_DONE: opteed_synchronous_sp_exit(optee_ctx, x1);
case TEESMC_OPTEED_RETURN_CALL_DONE:---------------------------------------optee处理完smc之后,需要返回普通世界,x1-x4返回参数。 assert(handle == cm_get_context(SECURE)); cm_el1_sysregs_context_save(SECURE);