BL1是SoC系统启动的第一个阶段,其主要目的是初始化系统环境和启动第二阶段镜像BL2。在Trust zone firmware里面被定义为Architectural initialization,platform initaliztion, firmware update detection and execution阶段,顾名思义,在这个阶段需要对整个SoC完成初始化。需要包含以下流程:
Architectural initialization
Exception vectors 初始化异常向量表
CPU initializtion CPU初始化
Control register setup for aarch64 控制寄存器配置
platform initaliztion
Enable the Trusted watchdog 使能看门狗
initialze the console 初始化控制台
configure the interconnect to enable hardware coherency 配置使能硬件一致性维护机制
enable MMU and map the memory it needs to access 使能MMU,映射内存
configure any required platform storage to load the next bootloader image(BL2) load bl2
/* ---------------------------------------------------------------------
* 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 \
_init_sctlr=1 \
_warm_boot_mailbox=!PROGRAMMABLE_RESET_ADDRESS \
_secondary_cold_boot=!COLD_BOOT_SINGLE_CPU \
_init_memory=1 \
_init_c_runtime=1 \
_exception_vectors=bl1_exceptions \
_pie_fixup_size=0
.if \_init_sctlr
/* -------------------------------------------------------------
* This is the initialisation of SCTLR and so must ensure that
* all fields are explicitly set rather than relying on hw. Some
* fields reset to an IMPLEMENTATION DEFINED value.
*
* SCTLR.TE: Set to zero so that exceptions to an Exception
* Level executing at PL1 are taken to A32 state.
*
* SCTLR.EE: Set the CPU endianness before doing anything that
* might involve memory reads or writes. Set to zero to select
* Little Endian.
*
* SCTLR.V: Set to zero to select the normal exception vectors
* with base address held in VBAR.
*
* SCTLR.DSSBS: Set to zero to disable speculation store bypass
* safe behaviour upon exception entry to EL3.
* -------------------------------------------------------------
*/
ldr r0, =(SCTLR_RESET_VAL & ~(SCTLR_TE_BIT | SCTLR_EE_BIT | \
SCTLR_V_BIT | SCTLR_DSSBS_BIT))
stcopr r0, SCTLR
isb
.endif /* _init_sctlr */
.if \_warm_boot_mailbox
/* -------------------------------------------------------------
* 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.
* -------------------------------------------------------------
*/
bl plat_get_my_entrypoint
cbz x0, do_cold_boot
br x0
do_cold_boot:
.endif /* _warm_boot_mailbox */
#if ENABLE_PIE
/*
* ------------------------------------------------------------
* If PIE is enabled fixup the Global descriptor Table only
* once during primary core cold boot path.
*
* Compile time base address, required for fixup, is calculated
* using "pie_fixup" label present within first page.
* ------------------------------------------------------------
*/
pie_fixup:
ldr x0, =pie_fixup
and x0, x0, #~(PAGE_SIZE_MASK)
mov_imm x1, \_pie_fixup_size
add x1, x1, x0
bl fixup_gdt_reloc
#endif /* ENABLE_PIE */
.endif /* _pie_fixup_size */
Function : bl1_early_platform_setup() [mandatory]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
::
Argument : void
Return : void
This function executes with the MMU and data caches disabled. It is only called
by the primary CPU.
On Arm standard platforms, this function:
- Enables a secure instance of SP805 to act as the Trusted Watchdog.
- Initializes a UART (PL011 console), which enables access to the ``printf``
family of functions in BL1.
- Enables issuing of snoop and DVM (Distributed Virtual Memory) requests to
the CCI slave interface corresponding to the cluster that includes the
primary CPU.
我们来参考一下marvell的:
/*
* BL1 specific platform actions shared between Marvell standard platforms.
*/
void marvell_bl1_early_platform_setup(void)
{
/* Initialize the console to provide early debug support */
marvell_console_boot_init();
/* Allow BL1 to see the whole Trusted RAM */
bl1_ram_layout.total_base = MARVELL_BL_RAM_BASE;
bl1_ram_layout.total_size = MARVELL_BL_RAM_SIZE;
}
This function performs any platform-specific and architectural setup that the platform requires. Platform-specific setup might include configuration of memory controllers and the interconnect.
In Arm standard platforms, this function enables the MMU.
This function helps fulfill requirement 2 above.
/*
* Perform the very early platform specific architecture setup shared between
* MARVELL standard platforms. This only does basic initialization. Later
* architectural setup (bl1_arch_setup()) does not do anything platform
* specific.
*/
void marvell_bl1_plat_arch_setup(void)
{
marvell_setup_page_tables(bl1_ram_layout.total_base,
bl1_ram_layout.total_size,
BL1_RO_BASE,
BL1_RO_LIMIT,
BL1_RO_DATA_BASE,
BL1_RO_DATA_END
#if USE_COHERENT_MEM
, BL_COHERENT_RAM_BASE,
BL_COHERENT_RAM_END
#endif
);
enable_mmu_el3(0);
}
Ensure that MMU/Caches and coherency are turned on
Perform remaining generic architectural setup from EL3
Initialize authentication module
Initialize the measured boot
Perform platform setup in BL1.
Get the image id of next image to load and run.
Teardown the measured boot driver
/*******************************************************************************
* 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;
/* Announce our arrival */
NOTICE(FIRMWARE_WELCOME_STR);
NOTICE("BL1: %s\n", version_string);
NOTICE("BL1: %s\n", build_message);
INFO("BL1: RAM %p - %p\n", (void *)BL1_RAM_BASE, (void *)BL1_RAM_LIMIT);
print_errata_status();
#if ENABLE_ASSERTIONS
u_register_t val;
/*
* Ensure that MMU/Caches and coherency are turned on
*/
#ifdef __aarch64__
val = read_sctlr_el3();
#else
val = read_sctlr();
#endif
assert((val & SCTLR_M_BIT) != 0);
assert((val & SCTLR_C_BIT) != 0);
assert((val & SCTLR_I_BIT) != 0);
/*
* Check that Cache Writeback Granule (CWG) in CTR_EL0 matches the
* provided platform value
*/
val = (read_ctr_el0() >> CTR_CWG_SHIFT) & CTR_CWG_MASK;
/*
* If CWG is zero, then no CWG information is available but we can
* at least check the platform value is less than the architectural
* maximum.
*/
if (val != 0)
assert(CACHE_WRITEBACK_GRANULE == SIZE_FROM_LOG2_WORDS(val));
else
assert(CACHE_WRITEBACK_GRANULE <= MAX_CACHE_LINE_SIZE);
#endif /* ENABLE_ASSERTIONS */
/* Perform remaining generic architectural setup from EL3 */
bl1_arch_setup();
crypto_mod_init();
/* Initialize authentication module */
auth_mod_init();
/* Initialize the measured boot */
bl1_plat_mboot_init();
/* Perform platform setup in BL1. */
bl1_platform_setup();
#if ENABLE_PAUTH
/* Store APIAKey_EL1 key */
bl1_apiakey[0] = read_apiakeylo_el1();
bl1_apiakey[1] = read_apiakeyhi_el1();
#endif /* ENABLE_PAUTH */
/* Get the image id of next image to load and run. */
image_id = bl1_plat_get_next_image_id();
/*
* We currently interpret any image id other than
* BL2_IMAGE_ID as the start of firmware update.
*/
if (image_id == BL2_IMAGE_ID)
bl1_load_bl2();
else
NOTICE("BL1-FWU: *******FWU Process Started*******\n");
/* Teardown the measured boot driver */
bl1_plat_mboot_finish();
bl1_prepare_next_image(image_id);
console_flush();
}
This function executes with the MMU and data caches enabled. It is responsible for performing any remaining platform-specific setup that can occur after the MMU and data cache have been enabled.
if support for multiple boot sources is required, it initializes the boot sequence used by plat_try_next_boot_source().
In Arm standard platforms, this function initializes the storage abstraction layer used to load the next bootloader image.
/*
* The following function checks if Firmware update is needed,
* by checking if TOC in FIP image is valid or not.
*/
unsigned int bl1_plat_get_next_image_id(void)
{
int32_t boot_mode;
unsigned int ret;
boot_mode = mmio_read_32(ONCHIPROM_PARAM_BASE);
switch (boot_mode) {
case BOOT_USB_DOWNLOAD:
case BOOT_UART_DOWNLOAD:
ret = NS_BL1U_IMAGE_ID;
break;
default:
WARN("Invalid boot mode is found:%d\n", boot_mode);
panic();
}
return ret;
}
2.3.4.2 bl2 image load
desc = bl1_plat_get_image_desc(BL2_IMAGE_ID); (1)
info = &desc->image_info;
err = bl1_plat_handle_pre_image_load(BL2_IMAGE_ID); (2)
if (err != 0) {
ERROR("Failure in pre image load handling of BL2 (%d)\n", err);
plat_error_handler(err);
}
err = load_auth_image(BL2_IMAGE_ID, info); (3)
if (err != 0) {
ERROR("Failed to load BL2 firmware.\n");
plat_error_handler(err);
}
err = bl1_plat_handle_post_image_load(BL2_IMAGE_ID); (4)
if (err != 0) {
ERROR("Failure in post image load handling of BL2 (%d)\n", err);
plat_error_handler(err);
}
/* ---------------------------------------------
* Set the exception vector to something sane.
* ---------------------------------------------
*/
adr x0, early_exceptions
msr vbar_el1, x0
isb
/* ---------------------------------------------
* Enable the SError interrupt now that the
* exception vectors have been setup.
* ---------------------------------------------
*/
msr daifclr, #DAIF_ABT_BIT
/* ---------------------------------------------
* Enable the instruction cache, stack pointer
* and data access alignment checks and disable
* speculative loads.
* ---------------------------------------------
*/
mov x1, #(SCTLR_I_BIT | SCTLR_A_BIT | SCTLR_SA_BIT)
mrs x0, sctlr_el1
orr x0, x0, x1
bic x0, x0, #SCTLR_DSSBS_BIT
msr sctlr_el1, x0
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.
* ---------------------------------------------
*/
adr x0, __RW_START__
adr x1, __RW_END__
sub x1, x1, x0
bl inv_dcache_range
这部分和bl1很像,配置如下:
使能i-cache
对齐检查
栈对齐检查
3.1.4 C env and stack
/* ---------------------------------------------
* Zero out NOBITS sections. There are 2 of them:
* - the .bss section;
* - the coherent memory section.
* ---------------------------------------------
*/
adrp x0, __BSS_START__
add x0, x0, :lo12:__BSS_START__
adrp x1, __BSS_END__
add x1, x1, :lo12:__BSS_END__
sub x1, x1, x0
bl zeromem
#if USE_COHERENT_MEM
adrp x0, __COHERENT_RAM_START__
add x0, x0, :lo12:__COHERENT_RAM_START__
adrp x1, __COHERENT_RAM_END_UNALIGNED__
add x1, x1, :lo12:__COHERENT_RAM_END_UNALIGNED__
sub x1, x1, x0
bl zeromem
#endif
/* --------------------------------------------
* 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.
* --------------------------------------------
*/
bl plat_set_my_stack
/* ---------------------------------------------
* Initialize the stack protector canary before
* any C code is called.
* ---------------------------------------------
*/
#if STACK_PROTECTOR_ENABLED
bl update_stack_protector_canary
#endif
a bl32用于加载trust os,在启动流程中不是必须的,此处暂时不讨论 b 若由bl2直接启动linux,则设置linux的启动参数。我们知道armv8架构的linux启动参数都是通过dtb传递的,因此这里将dtb地址设置为其启动参数 c 对于其它类型的bl33(如uboot),则将当前处理器的affinity信息作为其启动参数 d 设置bl33的spsr
#if !RESET_TO_BL31
/* ---------------------------------------------------------------------
* 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 initialised the
* SCTLR_EL3, including the endianness, and has initialised the memory.
* ---------------------------------------------------------------------
*/
el3_entrypoint_common \
_init_sctlr=0 \
_warm_boot_mailbox=0 \
_secondary_cold_boot=0 \
_init_memory=0 \
_init_c_runtime=1 \
_exception_vectors=runtime_exceptions \
_pie_fixup_size=BL31_LIMIT - BL31_BASE
#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 \
_init_sctlr=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 \
_pie_fixup_size=BL31_LIMIT - BL31_BASE
(2)支持从bl31开始启动的基础是armv8支持动态设置cpu的重启地址,armv8架构提供了RVBAR(reset vector base address register)寄存器用于设置reset时cpu的启动位置。该寄存器一共有三个:RVBAR_EL1、RVBAR_EL2和RVBAR_EL3,根据系统实现的最高异常等级确定使用哪一个。我们知道armv8重启总是从最高异常等级开始执行,因此我们只需要设置最高异常等级的RVBAR寄存器即可。由于bl31运行在el3下,故若我们需要支持启动从bl31开始,就可通过将地址设置到RVBAR_EL3寄存器实现。