From 35e8c7661ae12ac95bbf394b2b3d19d2c7d03369 Mon Sep 17 00:00:00 2001 From: Sandrine Bailleux Date: Tue, 10 Nov 2015 10:01:19 +0000 Subject: [PATCH 1/8] Introduce SPIN_ON_BL1_EXIT build flag This patch introduces a new build flag, SPIN_ON_BL1_EXIT, which puts an infinite loop in BL1. It is intended to help debugging the post-BL2 phase of the Trusted Firmware by stopping execution in BL1 just before handing over to BL31. At this point, the developer may take control of the target using a debugger. This feature is disabled by default and can be enabled by rebuilding BL1 with SPIN_ON_BL1_EXIT=1. User Guide updated accordingly. Change-Id: I6b6779d5949c9e5571dd371255520ef1ac39685c --- Makefile | 5 +++++ bl1/aarch64/bl1_exceptions.S | 6 ++++++ bl1/bl1_main.c | 8 ++++++++ docs/user-guide.md | 25 +++++++++++++++++++++++++ 4 files changed, 44 insertions(+) diff --git a/Makefile b/Makefile index 7a4be44af..c3252c23b 100644 --- a/Makefile +++ b/Makefile @@ -89,6 +89,9 @@ TRUSTED_BOARD_BOOT := 0 PROGRAMMABLE_RESET_ADDRESS := 0 # Build flag to treat usage of deprecated platform and framework APIs as error. ERROR_DEPRECATED := 0 +# Flag to introduce an infinite loop in BL1 just before it exits into the next +# image. This is meant to help debugging the post-BL2 phase. +SPIN_ON_BL1_EXIT := 0 ################################################################################ @@ -348,6 +351,7 @@ $(eval $(call assert_boolean,PROGRAMMABLE_RESET_ADDRESS)) $(eval $(call assert_boolean,PSCI_EXTENDED_STATE_ID)) $(eval $(call assert_boolean,ERROR_DEPRECATED)) $(eval $(call assert_boolean,ENABLE_PLAT_COMPAT)) +$(eval $(call assert_boolean,SPIN_ON_BL1_EXIT)) ################################################################################ @@ -370,6 +374,7 @@ $(eval $(call add_define,PROGRAMMABLE_RESET_ADDRESS)) $(eval $(call add_define,PSCI_EXTENDED_STATE_ID)) $(eval $(call add_define,ERROR_DEPRECATED)) $(eval $(call add_define,ENABLE_PLAT_COMPAT)) +$(eval $(call add_define,SPIN_ON_BL1_EXIT)) ################################################################################ diff --git a/bl1/aarch64/bl1_exceptions.S b/bl1/aarch64/bl1_exceptions.S index 0bd048571..ee66ef00c 100644 --- a/bl1/aarch64/bl1_exceptions.S +++ b/bl1/aarch64/bl1_exceptions.S @@ -207,6 +207,12 @@ func smc_handler64 bl disable_mmu_icache_el3 tlbi alle3 +#if SPIN_ON_BL1_EXIT + bl print_debug_loop_message +debug_loop: + b debug_loop +#endif + bl bl1_plat_prepare_exit ldp x6, x7, [x20, #(ENTRY_POINT_INFO_ARGS_OFFSET + 0x30)] diff --git a/bl1/bl1_main.c b/bl1/bl1_main.c index f62f31d5a..73f023aba 100644 --- a/bl1/bl1_main.c +++ b/bl1/bl1_main.c @@ -208,3 +208,11 @@ void bl1_print_bl31_ep_info(const entry_point_info_t *bl31_ep_info) NOTICE("BL1: Booting BL3-1\n"); print_entry_point_info(bl31_ep_info); } + +#if SPIN_ON_BL1_EXIT +void print_debug_loop_message(void) +{ + NOTICE("BL1: Debug loop, spinning forever\n"); + NOTICE("BL1: Please connect the debugger to continue\n"); +} +#endif diff --git a/docs/user-guide.md b/docs/user-guide.md index a7a0c11f3..3db7a8609 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -372,6 +372,12 @@ performed. Firmware as error. It can take the value 1 (flag the use of deprecated APIs as error) or 0. The default is 0. +* `SPIN_ON_BL1_EXIT`: This option introduces an infinite loop in BL1. It can + take either 0 (no loop) or 1 (add a loop). 0 is the default. This loop stops + execution in BL1 just before handing over to BL31. At this point, all + firmware images have been loaded in memory and the MMU as well as the caches + are turned off. Refer to the "Debugging options" section for more details. + #### ARM development platform specific build options * `ARM_TSP_RAM_LOCATION`: location of the TSP binary. Options: @@ -500,6 +506,25 @@ Extra debug options can be passed to the build system by setting `CFLAGS`: BL33=/ \ make PLAT= DEBUG=1 V=1 all fip +It is also possible to introduce an infinite loop to help in debugging the +post-BL2 phase of the Trusted Firmware. This can be done by rebuilding BL1 with +the `SPIN_ON_BL1_EXIT=1` build flag. Refer to the "Summary of build options" +section. In this case, the developer may take control of the target using a +debugger when indicated by the console output. When using DS-5, the following +commands can be used: + + # Stop target execution + interrupt + + # + # Prepare your debugging environment, e.g. set breakpoints + # + + # Jump over the debug loop + set var $AARCH64::$Core::$PC = $AARCH64::$Core::$PC + 4 + + # Resume execution + continue ### Building the Test Secure Payload From 862b5dc2d1f45b0195678dcb6556b0721444505f Mon Sep 17 00:00:00 2001 From: Sandrine Bailleux Date: Tue, 10 Nov 2015 15:01:57 +0000 Subject: [PATCH 2/8] Pass the entry point info to bl1_plat_prepare_exit() This patch modifies the prototype of the bl1_plat_prepare_exit() platform API to pass the address of the entry point info structure received from BL2. The structure contains information that can be useful, depending on the kind of clean up or bookkeeping operations to perform. The weak implementation of this function ignores this argument to preserve platform backwards compatibility. NOTE: THIS PATCH MAY BREAK PLATFORM PORTS THAT ARE RELYING ON THE FORMER PROTOTYPE OF THE BL1_PLAT_PREPARE_EXIT() API. Change-Id: I3fc18f637de06c85719c4ee84c85d6a4572a0fdb --- bl1/aarch64/bl1_exceptions.S | 1 + docs/porting-guide.md | 9 +++++---- plat/common/aarch64/platform_helpers.S | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/bl1/aarch64/bl1_exceptions.S b/bl1/aarch64/bl1_exceptions.S index ee66ef00c..5415d3955 100644 --- a/bl1/aarch64/bl1_exceptions.S +++ b/bl1/aarch64/bl1_exceptions.S @@ -213,6 +213,7 @@ debug_loop: b debug_loop #endif + mov x0, x20 bl bl1_plat_prepare_exit ldp x6, x7, [x20, #(ENTRY_POINT_INFO_ARGS_OFFSET + 0x30)] diff --git a/docs/porting-guide.md b/docs/porting-guide.md index 6e71025cc..2c828c271 100644 --- a/docs/porting-guide.md +++ b/docs/porting-guide.md @@ -810,13 +810,14 @@ represents the entry point system state for BL2. ### Function : bl1_plat_prepare_exit() [optional] - Argument : void + Argument : entry_point_info_t * Return : void -This function is called prior to exiting BL1 in response to the `RUN_IMAGE_SMC` +This function is called prior to exiting BL1 in response to the `RUN_IMAGE` SMC request raised by BL2. It should be used to perform platform specific clean up -or bookkeeping operations before transferring control to the next image. This -function runs with MMU disabled. +or bookkeeping operations before transferring control to the next image. It +receives the address of the `entry_point_info_t` structure passed from BL2. +This function runs with MMU disabled. 3.2 Boot Loader Stage 2 (BL2) diff --git a/plat/common/aarch64/platform_helpers.S b/plat/common/aarch64/platform_helpers.S index 56b88bc0e..29f01ce9e 100644 --- a/plat/common/aarch64/platform_helpers.S +++ b/plat/common/aarch64/platform_helpers.S @@ -115,7 +115,7 @@ func plat_disable_acp endfunc plat_disable_acp /* ----------------------------------------------------- - * void bl1_plat_prepare_exit(void); + * void bl1_plat_prepare_exit(entry_point_info_t *ep_info); * Called before exiting BL1. Default: do nothing * ----------------------------------------------------- */ From 4c117f6c49330682cabdbf6d082e7c02dd32e84b Mon Sep 17 00:00:00 2001 From: Sandrine Bailleux Date: Thu, 26 Nov 2015 16:31:34 +0000 Subject: [PATCH 3/8] CSS: Enable booting of EL3 payloads This patch adds support for booting EL3 payloads on CSS platforms, for example Juno. In this scenario, the Trusted Firmware follows its normal boot flow up to the point where it would normally pass control to the BL31 image. At this point, it jumps to the EL3 payload entry point address instead. Before handing over to the EL3 payload, the data SCP writes for AP at the beginning of the Trusted SRAM is restored, i.e. we zero the first 128 bytes and restore the SCP Boot configuration. The latter is saved before transferring the BL30 image to SCP and is restored just after the transfer (in BL2). The goal is to make it appear that the EL3 payload is the first piece of software to run on the target. The BL31 entrypoint info structure is updated to make the primary CPU jump to the EL3 payload instead of the BL31 image. The mailbox is populated with the EL3 payload entrypoint address, which releases the secondary CPUs out of their holding pen (if the SCP has powered them on). The arm_program_trusted_mailbox() function has been exported for this purpose. The TZC-400 configuration in BL2 is simplified: it grants secure access only to the whole DRAM. Other security initialization is unchanged. This alternative boot flow is disabled by default. A new build option EL3_PAYLOAD_BASE has been introduced to enable it and provide the EL3 payload's entry point address. The build system has been modified such that BL31 and BL33 are not compiled and/or not put in the FIP in this case, as those images are not used in this boot flow. Change-Id: Id2e26fa57988bbc32323a0effd022ab42f5b5077 --- Makefile | 17 +++++++++++++ bl2/bl2_main.c | 19 ++++++++++++++ docs/porting-guide.md | 9 ++++--- include/plat/arm/common/plat_arm.h | 1 + plat/arm/common/arm_bl1_setup.c | 14 +++++++++++ plat/arm/common/arm_common.mk | 5 ++++ plat/arm/common/arm_pm.c | 2 +- plat/arm/common/arm_security.c | 14 +++++++++-- plat/arm/css/common/css_bl2_setup.c | 39 +++++++++++++++++++++++++++++ 9 files changed, 114 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index c3252c23b..80e05fa63 100644 --- a/Makefile +++ b/Makefile @@ -235,6 +235,10 @@ INCLUDE_TBBR_MK := 1 ################################################################################ ifneq (${SPD},none) +ifdef EL3_PAYLOAD_BASE + $(warning "SPD and EL3_PAYLOAD_BASE are incompatible build options.") + $(warning "The SPD and its BL32 companion will be present but ignored.") +endif # We expect to locate an spd.mk under the specified SPD directory SPD_MAKE := $(shell m="services/spd/${SPD}/${SPD}.mk"; [ -f "$$m" ] && echo "$$m") @@ -300,7 +304,12 @@ endif # supplied for the FIP and Certificate generation tools. This flag can be # overridden by the platform. ifdef BL2_SOURCES +ifndef EL3_PAYLOAD_BASE NEED_BL33 ?= yes +else +# The BL33 image is not needed when booting an EL3 payload. +NEED_BL33 := no +endif endif # Process TBB related flags @@ -375,6 +384,10 @@ $(eval $(call add_define,PSCI_EXTENDED_STATE_ID)) $(eval $(call add_define,ERROR_DEPRECATED)) $(eval $(call add_define,ENABLE_PLAT_COMPAT)) $(eval $(call add_define,SPIN_ON_BL1_EXIT)) +# Define the EL3_PAYLOAD_BASE flag only if it is provided. +ifdef EL3_PAYLOAD_BASE +$(eval $(call add_define,EL3_PAYLOAD_BASE)) +endif ################################################################################ @@ -392,9 +405,13 @@ include bl2/bl2.mk endif ifdef BL31_SOURCES +# When booting an EL3 payload, there is no need to compile the BL31 image nor +# put it in the FIP. +ifndef EL3_PAYLOAD_BASE NEED_BL31 := yes include bl31/bl31.mk endif +endif ################################################################################ diff --git a/bl2/bl2_main.c b/bl2/bl2_main.c index f8a2372fe..a3f016f9c 100644 --- a/bl2/bl2_main.c +++ b/bl2/bl2_main.c @@ -83,6 +83,7 @@ static int load_bl30(void) return e; } +#ifndef EL3_PAYLOAD_BASE /******************************************************************************* * Load the BL3-1 image. * The bl2_to_bl31_params and bl31_ep_info params will be updated with the @@ -190,6 +191,7 @@ static int load_bl33(bl31_params_t *bl2_to_bl31_params) return e; } +#endif /* EL3_PAYLOAD_BASE */ /******************************************************************************* * The only thing to do in BL2 is to load further images and pass control to @@ -232,6 +234,22 @@ void bl2_main(void) bl2_to_bl31_params = bl2_plat_get_bl31_params(); bl31_ep_info = bl2_plat_get_bl31_ep_info(); +#ifdef EL3_PAYLOAD_BASE + /* + * In the case of an EL3 payload, we don't need to load any further + * images. Just update the BL31 entrypoint info structure to make BL1 + * jump to the EL3 payload. + * The pointer to the memory the platform has set aside to pass + * information to BL3-1 in the normal boot flow is reused here, even + * though only a fraction of the information contained in the + * bl31_params_t structure makes sense in the context of EL3 payloads. + * This will be refined in the future. + */ + VERBOSE("BL2: Populating the entrypoint info for the EL3 payload\n"); + bl31_ep_info->pc = EL3_PAYLOAD_BASE; + bl31_ep_info->args.arg0 = (unsigned long) bl2_to_bl31_params; + bl2_plat_set_bl31_ep_info(NULL, bl31_ep_info); +#else e = load_bl31(bl2_to_bl31_params, bl31_ep_info); if (e) { ERROR("Failed to load BL3-1 (%i)\n", e); @@ -253,6 +271,7 @@ void bl2_main(void) ERROR("Failed to load BL3-3 (%i)\n", e); plat_error_handler(e); } +#endif /* EL3_PAYLOAD_BASE */ /* Flush the params to be passed to memory */ bl2_plat_flush_bl31_params(); diff --git a/docs/porting-guide.md b/docs/porting-guide.md index 2c828c271..5e823e815 100644 --- a/docs/porting-guide.md +++ b/docs/porting-guide.md @@ -1001,10 +1001,13 @@ structure in BL2 memory. Argument : image_info *, entry_point_info * Return : void -This function is called after loading BL3-1 image and it can be used to -overwrite the entry point set by loader and also set the security state -and SPSR which represents the entry point system state for BL3-1. +In the normal boot flow, this function is called after loading BL3-1 image and +it can be used to overwrite the entry point set by loader and also set the +security state and SPSR which represents the entry point system state for BL3-1. +When booting an EL3 payload instead, this function is called after populating +its entry point address and can be used for the same purpose for the payload +image. It receives a null pointer as its first argument in this case. ### Function : bl2_plat_set_bl32_ep_info() [mandatory] diff --git a/include/plat/arm/common/plat_arm.h b/include/plat/arm/common/plat_arm.h index 044e18ef7..aadf58d83 100644 --- a/include/plat/arm/common/plat_arm.h +++ b/include/plat/arm/common/plat_arm.h @@ -149,6 +149,7 @@ int arm_validate_power_state(unsigned int power_state, psci_power_state_t *req_state); int arm_validate_ns_entrypoint(uintptr_t entrypoint); void arm_system_pwr_domain_resume(void); +void arm_program_trusted_mailbox(uintptr_t address); /* Topology utility function */ int arm_check_mpidr(u_register_t mpidr); diff --git a/plat/arm/common/arm_bl1_setup.c b/plat/arm/common/arm_bl1_setup.c index ddf383fe2..887223cf4 100644 --- a/plat/arm/common/arm_bl1_setup.c +++ b/plat/arm/common/arm_bl1_setup.c @@ -145,6 +145,20 @@ void bl1_platform_setup(void) arm_bl1_platform_setup(); } +void bl1_plat_prepare_exit(entry_point_info_t *ep_info) +{ +#ifdef EL3_PAYLOAD_BASE + /* + * Program the EL3 payload's entry point address into the CPUs mailbox + * in order to release secondary CPUs from their holding pen and make + * them jump there. + */ + arm_program_trusted_mailbox(ep_info->pc); + dsbsy(); + sev(); +#endif +} + /******************************************************************************* * Before calling this function BL2 is loaded in memory and its entrypoint * is set by load_image. This is a placeholder for the platform to change diff --git a/plat/arm/common/arm_common.mk b/plat/arm/common/arm_common.mk index eb5ae1116..7b2352759 100644 --- a/plat/arm/common/arm_common.mk +++ b/plat/arm/common/arm_common.mk @@ -81,6 +81,11 @@ BL1_SOURCES += drivers/arm/cci/cci.c \ plat/arm/common/arm_bl1_setup.c \ plat/arm/common/arm_io_storage.c \ plat/common/aarch64/platform_up_stack.S +ifdef EL3_PAYLOAD_BASE +# Need the arm_program_trusted_mailbox() function to release secondary CPUs from +# their holding pen +BL1_SOURCES += plat/arm/common/arm_pm.c +endif BL2_SOURCES += drivers/arm/tzc400/tzc400.c \ drivers/io/io_fip.c \ diff --git a/plat/arm/common/arm_pm.c b/plat/arm/common/arm_pm.c index d13d2683c..cae65970f 100644 --- a/plat/arm/common/arm_pm.c +++ b/plat/arm/common/arm_pm.c @@ -175,7 +175,7 @@ void arm_system_pwr_domain_resume(void) * from reset. This function assumes that the Trusted mail box base is within * the ARM_SHARED_RAM region ******************************************************************************/ -static void arm_program_trusted_mailbox(uintptr_t address) +void arm_program_trusted_mailbox(uintptr_t address) { uintptr_t *mailbox = (void *) PLAT_ARM_TRUSTED_MAILBOX_BASE; diff --git a/plat/arm/common/arm_security.c b/plat/arm/common/arm_security.c index 990d8d4a6..8b46aaedd 100644 --- a/plat/arm/common/arm_security.c +++ b/plat/arm/common/arm_security.c @@ -40,8 +40,13 @@ /******************************************************************************* * Initialize the TrustZone Controller for ARM standard platforms. - * Configure Region 0 with no access, Region 1 with secure access only, and - * the remaining DRAM regions access from the given Non-Secure masters. + * Configure: + * - Region 0 with no access; + * - Region 1 with secure access only; + * - the remaining DRAM regions access from the given Non-Secure masters. + * + * When booting an EL3 payload, this is simplified: we configure region 0 with + * secure access only and do not enable any other region. ******************************************************************************/ void arm_tzc_setup(void) { @@ -52,6 +57,7 @@ void arm_tzc_setup(void) /* Disable filters. */ tzc_disable_filters(); +#ifndef EL3_PAYLOAD_BASE /* Region 0 set to no access by default */ tzc_configure_region0(TZC_REGION_S_NONE, 0); @@ -73,6 +79,10 @@ void arm_tzc_setup(void) ARM_DRAM2_BASE, ARM_DRAM2_END, TZC_REGION_S_NONE, PLAT_ARM_TZC_NS_DEV_ACCESS); +#else + /* Allow secure access only to DRAM for EL3 payloads. */ + tzc_configure_region0(TZC_REGION_S_RDWR, 0); +#endif /* EL3_PAYLOAD_BASE */ /* * Raise an exception if a NS device tries to access secure memory diff --git a/plat/arm/css/common/css_bl2_setup.c b/plat/arm/css/common/css_bl2_setup.c index 2e423d962..6054f7a56 100644 --- a/plat/arm/css/common/css_bl2_setup.c +++ b/plat/arm/css/common/css_bl2_setup.c @@ -29,7 +29,11 @@ */ #include +#include #include +#include +#include +#include #include "css_scp_bootloader.h" /* Weak definition may be overridden in specific CSS based platform */ @@ -55,3 +59,38 @@ int bl2_plat_handle_bl30(image_info_t *bl30_image_info) return ret; } + +#ifdef EL3_PAYLOAD_BASE +/* + * We need to override some of the platform functions when booting an EL3 + * payload. + */ + +static unsigned int scp_boot_config; + +void bl2_early_platform_setup(meminfo_t *mem_layout) +{ + arm_bl2_early_platform_setup(mem_layout); + + /* Save SCP Boot config before it gets overwritten by BL30 loading */ + scp_boot_config = mmio_read_32(SCP_BOOT_CFG_ADDR); + VERBOSE("BL2: Saved SCP Boot config = 0x%x\n", scp_boot_config); +} + +void bl2_platform_setup(void) +{ + arm_bl2_platform_setup(); + + /* + * Before releasing the AP cores out of reset, the SCP writes some data + * at the beginning of the Trusted SRAM. It is is overwritten before + * reaching this function. We need to restore this data, as if the + * target had just come out of reset. This implies: + * - zeroing the first 128 bytes of Trusted SRAM; + * - restoring the SCP boot configuration. + */ + VERBOSE("BL2: Restoring SCP reset data in Trusted SRAM\n"); + memset((void *) ARM_TRUSTED_SRAM_BASE, 0, 128); + mmio_write_32(SCP_BOOT_CFG_ADDR, scp_boot_config); +} +#endif /* EL3_PAYLOAD_BASE */ From 2bc420676c70b864ef82cde517401696abaafad4 Mon Sep 17 00:00:00 2001 From: Sandrine Bailleux Date: Wed, 29 Apr 2015 16:28:52 +0100 Subject: [PATCH 4/8] CSS: Put secondary CPUs in a pen when booting an EL3 payload By default, only the primary CPU is powered on by SCP on CSS platforms. Secondary CPUs are then powered on later using PSCI calls. However, it is possible to power on more than one CPU at boot time using platform specific settings. In this case, several CPUs will enter the Trusted Firmware and execute the cold boot path code. This is currently not supported and secondary CPUs will panic. This patch preserves this behaviour in the normal boot flow. However, when booting an EL3 payload, secondary CPUs are now held in a pen until their mailbox is populated, at which point they jump to this address. Note that, since all CPUs share the same mailbox, they will all be released from their holding pen at the same time and the EL3 payload is responsible to arbitrate execution between CPUs if required. Change-Id: I83737e0c9f15ca5e73afbed2e9c761bc580735b9 --- plat/arm/css/common/aarch64/css_helpers.S | 33 +++++++++++++++++------ 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/plat/arm/css/common/aarch64/css_helpers.S b/plat/arm/css/common/aarch64/css_helpers.S index d1702708f..27476186d 100644 --- a/plat/arm/css/common/aarch64/css_helpers.S +++ b/plat/arm/css/common/aarch64/css_helpers.S @@ -37,19 +37,36 @@ .globl css_calc_core_pos_swap_cluster .weak plat_is_my_cpu_primary - /* ----------------------------------------------------- - * void plat_secondary_cold_boot_setup (void); + /* --------------------------------------------------------------------- + * void plat_secondary_cold_boot_setup(void); * - * This function performs any platform specific actions - * needed for a secondary cpu after a cold reset e.g - * mark the cpu's presence, mechanism to place it in a - * holding pen etc. - * ----------------------------------------------------- + * In the normal boot flow, cold-booting secondary CPUs is not yet + * implemented and they panic. + * + * When booting an EL3 payload, secondary CPUs are placed in a holding + * pen, waiting for their mailbox to be populated. Note that all CPUs + * share the same mailbox ; therefore, populating it will release all + * CPUs from their holding pen. If finer-grained control is needed then + * this should be handled in the code that secondary CPUs jump to. + * --------------------------------------------------------------------- */ func plat_secondary_cold_boot_setup - /* todo: Implement secondary CPU cold boot setup on CSS platforms */ +#ifndef EL3_PAYLOAD_BASE + /* TODO: Implement secondary CPU cold boot setup on CSS platforms */ cb_panic: b cb_panic +#else + mov_imm x0, PLAT_ARM_TRUSTED_MAILBOX_BASE + + /* Wait until the mailbox gets populated */ +poll_mailbox: + ldr x1, [x0] + cbz x1, 1f + br x1 +1: + wfe + b poll_mailbox +#endif /* EL3_PAYLOAD_BASE */ endfunc plat_secondary_cold_boot_setup /* --------------------------------------------------------------------- From cdf1408856d980c0f6dd40b33138a9d6c46598c0 Mon Sep 17 00:00:00 2001 From: Sandrine Bailleux Date: Fri, 2 Oct 2015 14:35:25 +0100 Subject: [PATCH 5/8] FVP: Do not power off secondary CPUs when booting an EL3 payload Normally, in the FVP port, secondary CPUs are immediately powered down if they are powered on at reset. However, when booting an EL3 payload, we need to keep them powered on as the requirement is for all CPUs to enter the EL3 payload image. This patch puts them in a holding pen instead of powering them off. Change-Id: I6526a88b907a0ddb820bead72f1d350a99b1692c --- docs/porting-guide.md | 8 +++++--- plat/arm/board/fvp/aarch64/fvp_helpers.S | 13 +++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/docs/porting-guide.md b/docs/porting-guide.md index 5e823e815..82bed6b04 100644 --- a/docs/porting-guide.md +++ b/docs/porting-guide.md @@ -468,9 +468,11 @@ for placing the executing secondary CPU in a platform-specific state until the primary CPU performs the necessary actions to bring it out of that state and allow entry into the OS. This function must not return. -In the ARM FVP port, each secondary CPU powers itself off. The primary CPU is -responsible for powering up the secondary CPU when normal world software -requires them. +In the ARM FVP port, when using the normal boot flow, each secondary CPU powers +itself off. The primary CPU is responsible for powering up the secondary CPUs +when normal world software requires them. When booting an EL3 payload instead, +they stay powered on and are put in a holding pen until their mailbox gets +populated. This function fulfills requirement 2 above. diff --git a/plat/arm/board/fvp/aarch64/fvp_helpers.S b/plat/arm/board/fvp/aarch64/fvp_helpers.S index 865c615a3..2c24e61f9 100644 --- a/plat/arm/board/fvp/aarch64/fvp_helpers.S +++ b/plat/arm/board/fvp/aarch64/fvp_helpers.S @@ -60,6 +60,7 @@ * ----------------------------------------------------- */ func plat_secondary_cold_boot_setup +#ifndef EL3_PAYLOAD_BASE /* --------------------------------------------- * Power down this cpu. * TODO: Do we need to worry about powering the @@ -93,6 +94,18 @@ func plat_secondary_cold_boot_setup wfi cb_panic: b cb_panic +#else + mov_imm x0, PLAT_ARM_TRUSTED_MAILBOX_BASE + + /* Wait until the entrypoint gets populated */ +poll_mailbox: + ldr x1, [x0] + cbz x1, 1f + br x1 +1: + wfe + b poll_mailbox +#endif /* EL3_PAYLOAD_BASE */ endfunc plat_secondary_cold_boot_setup /* --------------------------------------------------------------------- From aba2f1e023014c58cf72a1378df0a6a17d2871dc Mon Sep 17 00:00:00 2001 From: Sandrine Bailleux Date: Thu, 1 Oct 2015 10:25:15 +0100 Subject: [PATCH 6/8] Document the EL3 payload support - Document the new build option EL3_PAYLOAD_BASE - Document the EL3 payload boot flow - Document the FVP model parameters to boot an EL3 payload Change-Id: Ie6535914a9a68626e4401659bee4fcfd53d4bd37 --- docs/user-guide.md | 92 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 85 insertions(+), 7 deletions(-) diff --git a/docs/user-guide.md b/docs/user-guide.md index 3db7a8609..53745f0eb 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -9,9 +9,10 @@ Contents : 4. [Getting the Trusted Firmware source code](#4--getting-the-trusted-firmware-source-code) 5. [Building the Trusted Firmware](#5--building-the-trusted-firmware) 6. [Building the rest of the software stack](#6--building-the-rest-of-the-software-stack) -7. [Preparing the images to run on FVP](#7--preparing-the-images-to-run-on-fvp) -8. [Running the software on FVP](#8--running-the-software-on-fvp) -9. [Running the software on Juno](#9--running-the-software-on-juno) +7. [EL3 payloads alternative boot flow](#7--el3-payloads-alternative-boot-flow) +8. [Preparing the images to run on FVP](#8--preparing-the-images-to-run-on-fvp) +9. [Running the software on FVP](#9--running-the-software-on-fvp) +10. [Running the software on Juno](#10--running-the-software-on-juno) 1. Introduction @@ -378,6 +379,11 @@ performed. firmware images have been loaded in memory and the MMU as well as the caches are turned off. Refer to the "Debugging options" section for more details. +* `EL3_PAYLOAD_BASE`: This option enables booting an EL3 payload instead of + the normal boot flow. It must specify the entry point address of the EL3 + payload. Please refer to the "Booting an EL3 payload" section for more + details. + #### ARM development platform specific build options * `ARM_TSP_RAM_LOCATION`: location of the TSP binary. Options: @@ -724,9 +730,49 @@ above. The EDK2 binary for use with the ARM Trusted Firmware can be found here: instructions in the "Building the Trusted Firmware" section. -7. Preparing the images to run on FVP +7. EL3 payloads alternative boot flow +-------------------------------------- + +On a pre-production system, the ability to execute arbitrary, bare-metal code at +the highest exception level is required. It allows full, direct access to the +hardware, for example to run silicon soak tests. + +Although it is possible to implement some baremetal secure firmware from +scratch, this is a complex task on some platforms, depending on the level of +configuration required to put the system in the expected state. + +Rather than booting a baremetal application, a possible compromise is to boot +`EL3 payloads` through the Trusted Firmware instead. This is implemented as an +alternative boot flow, where a modified BL2 boots an EL3 payload, instead of +loading the other BL images and passing control to BL31. It reduces the +complexity of developing EL3 baremetal code by: + +* putting the system into a known architectural state; +* taking care of platform secure world initialization; +* loading the BL30 image if required by the platform. + +When booting an EL3 payload on ARM standard platforms, the configuration of the +TrustZone controller is simplified such that only region 0 is enabled and is +configured to permit secure access only. This gives full access to the whole +DRAM to the EL3 payload. + +The system is left in the same state as when entering BL31 in the default boot +flow. In particular: + +* Running in EL3; +* Current state is AArch64; +* Little-endian data access; +* All exceptions disabled; +* MMU disabled; +* Caches disabled. + + +8. Preparing the images to run on FVP -------------------------------------- +Note: This section can be ignored when booting an EL3 payload, as no Flattened +Device Tree or kernel image is needed in this case. + ### Obtaining the Flattened Device Trees Depending on the FVP configuration and Linux configuration used, different @@ -774,7 +820,7 @@ Copy the kernel image file `linux/arch/arm64/boot/Image` to the directory from which the FVP is launched. Alternatively a symbolic link may be used. -8. Running the software on FVP +9. Running the software on FVP ------------------------------- This version of the ARM Trusted Firmware has been tested on the following ARM @@ -1073,9 +1119,41 @@ The `bp.variant` parameter corresponds to the build variant field of the `SYS_ID` register. Setting this to `0x0` allows the ARM Trusted Firmware to detect the legacy VE memory map while configuring the GIC. +### Booting an EL3 payload on FVP -9. Running the software on Juno --------------------------------- +Booting an EL3 payload on FVP requires a couple of changes to the way the +model is normally invoked. + +First of all, the EL3 payload image is not part of the FIP and is not loaded by +the Trusted Firmware. Therefore, it must be loaded in memory some other way. +There are 2 ways of doing that: + +1. It can be loaded over JTAG at the appropriate time. The infinite loop + introduced in BL1 when compiling the Trusted Firmware with + `SPIN_ON_BL1_EXIT=1` stops execution at the right moment for a debugger to + take control of the target and load the payload. + +2. It can be pre-loaded in the FVP memory using the following model parameter: + + --data=""@ + + The base address provided to the FVP must match the `EL3_PAYLOAD_BASE` + address used when building the Trusted Firmware. + +Secondly, the EL3 payloads boot flow requires the CPUs mailbox to be cleared +at reset for the secondary CPUs holding pen to work properly. Unfortunately, +its reset value is undefined on FVP. One way to clear it is to create an +8-byte file containing all zero bytes and pre-load it into the FVP memory at the +mailbox address (i.e. `0x04000000`) using the same `--data` FVP parameter as +described above. + +The following command creates such a file called `mailbox.dat`: + + dd if=/dev/zero of=mailbox.dat bs=1 count=8 + + +10. Running the software on Juno +--------------------------------- This version of the ARM Trusted Firmware has been tested on Juno r0 and Juno r1. From a9bec67dfda087ac739a29cbc4eb4ccb38da3e45 Mon Sep 17 00:00:00 2001 From: Sandrine Bailleux Date: Fri, 30 Oct 2015 15:05:17 +0000 Subject: [PATCH 7/8] Introduce COLD_BOOT_SINGLE_CPU build option This patch introduces a new build option named COLD_BOOT_SINGLE_CPU, which allows platforms that only release a single CPU out of reset to slightly optimise their cold boot code, both in terms of code size and performance. COLD_BOOT_SINGLE_CPU defaults to 0, which assumes that the platform may release several CPUs out of reset. In this case, the cold reset code needs to coordinate all CPUs via the usual primary/secondary CPU distinction. If a platform guarantees that only a single CPU will ever be released out of reset, there is no need to arbitrate execution ; the notion of primary and secondary CPUs itself no longer exists. Such platforms may set COLD_BOOT_SINGLE_CPU to 1 in order to compile out the primary/secondary CPU identification in the cold reset code. All ARM standard platforms can release several CPUs out of reset so they use COLD_BOOT_SINGLE_CPU=0. However, on CSS platforms like Juno, bringing up more than one CPU at reset should only be attempted when booting an EL3 payload, as it is not fully supported in the normal boot flow. For platforms using COLD_BOOT_SINGLE_CPU=1, the following 2 platform APIs become optional: - plat_secondary_cold_boot_setup(); - plat_is_my_cpu_primary(). The Porting Guide has been updated to reflect that. User Guide updated as well. Change-Id: Ic5b474e61b7aec1377d1e0b6925d17dfc376c46b --- Makefile | 5 +++++ bl1/aarch64/bl1_entrypoint.S | 2 +- bl31/aarch64/bl31_entrypoint.S | 2 +- docs/porting-guide.md | 13 +++++++++++-- docs/user-guide.md | 9 +++++++++ 5 files changed, 27 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 80e05fa63..0c3303f86 100644 --- a/Makefile +++ b/Makefile @@ -89,6 +89,9 @@ TRUSTED_BOARD_BOOT := 0 PROGRAMMABLE_RESET_ADDRESS := 0 # Build flag to treat usage of deprecated platform and framework APIs as error. ERROR_DEPRECATED := 0 +# By default, consider that the platform may release several CPUs out of reset. +# The platform Makefile is free to override this value. +COLD_BOOT_SINGLE_CPU := 0 # Flag to introduce an infinite loop in BL1 just before it exits into the next # image. This is meant to help debugging the post-BL2 phase. SPIN_ON_BL1_EXIT := 0 @@ -357,6 +360,7 @@ $(eval $(call assert_boolean,CREATE_KEYS)) $(eval $(call assert_boolean,SAVE_KEYS)) $(eval $(call assert_boolean,TRUSTED_BOARD_BOOT)) $(eval $(call assert_boolean,PROGRAMMABLE_RESET_ADDRESS)) +$(eval $(call assert_boolean,COLD_BOOT_SINGLE_CPU)) $(eval $(call assert_boolean,PSCI_EXTENDED_STATE_ID)) $(eval $(call assert_boolean,ERROR_DEPRECATED)) $(eval $(call assert_boolean,ENABLE_PLAT_COMPAT)) @@ -380,6 +384,7 @@ $(eval $(call add_define,LOG_LEVEL)) $(eval $(call add_define,USE_COHERENT_MEM)) $(eval $(call add_define,TRUSTED_BOARD_BOOT)) $(eval $(call add_define,PROGRAMMABLE_RESET_ADDRESS)) +$(eval $(call add_define,COLD_BOOT_SINGLE_CPU)) $(eval $(call add_define,PSCI_EXTENDED_STATE_ID)) $(eval $(call add_define,ERROR_DEPRECATED)) $(eval $(call add_define,ENABLE_PLAT_COMPAT)) diff --git a/bl1/aarch64/bl1_entrypoint.S b/bl1/aarch64/bl1_entrypoint.S index 4fc52918a..83594f218 100644 --- a/bl1/aarch64/bl1_entrypoint.S +++ b/bl1/aarch64/bl1_entrypoint.S @@ -51,7 +51,7 @@ func bl1_entrypoint el3_entrypoint_common \ _set_endian=1 \ _warm_boot_mailbox=!PROGRAMMABLE_RESET_ADDRESS \ - _secondary_cold_boot=1 \ + _secondary_cold_boot=!COLD_BOOT_SINGLE_CPU \ _init_memory=1 \ _init_c_runtime=1 \ _exception_vectors=bl1_exceptions diff --git a/bl31/aarch64/bl31_entrypoint.S b/bl31/aarch64/bl31_entrypoint.S index 636b1d287..45aa85dc4 100644 --- a/bl31/aarch64/bl31_entrypoint.S +++ b/bl31/aarch64/bl31_entrypoint.S @@ -85,7 +85,7 @@ func bl31_entrypoint el3_entrypoint_common \ _set_endian=1 \ _warm_boot_mailbox=!PROGRAMMABLE_RESET_ADDRESS \ - _secondary_cold_boot=1 \ + _secondary_cold_boot=!COLD_BOOT_SINGLE_CPU \ _init_memory=1 \ _init_c_runtime=1 \ _exception_vectors=runtime_exceptions diff --git a/docs/porting-guide.md b/docs/porting-guide.md index 82bed6b04..6c6122adf 100644 --- a/docs/porting-guide.md +++ b/docs/porting-guide.md @@ -459,7 +459,7 @@ type of reset nor to query the warm reset entrypoint. Therefore, implementing this function is not required on such platforms. -### Function : plat_secondary_cold_boot_setup() [mandatory] +### Function : plat_secondary_cold_boot_setup() [mandatory when COLD_BOOT_SINGLE_CPU == 0] Argument : void @@ -476,8 +476,12 @@ populated. This function fulfills requirement 2 above. +Note that for platforms that can't release secondary CPUs out of reset, only the +primary CPU will execute the cold boot code. Therefore, implementing this +function is not required on such platforms. -### Function : plat_is_my_cpu_primary() [mandatory] + +### Function : plat_is_my_cpu_primary() [mandatory when COLD_BOOT_SINGLE_CPU == 0] Argument : void Return : unsigned int @@ -487,6 +491,11 @@ secondary CPU. A return value of zero indicates that the CPU is not the primary CPU, while a non-zero return value indicates that the CPU is the primary CPU. +Note that for platforms that can't release secondary CPUs out of reset, only the +primary CPU will execute the cold boot code. Therefore, there is no need to +distinguish between primary and secondary CPUs and implementing this function is +not required. + ### Function : platform_mem_init() [mandatory] diff --git a/docs/user-guide.md b/docs/user-guide.md index 53745f0eb..f7e4f4c79 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -359,6 +359,15 @@ performed. implication for `plat_get_my_entrypoint()` platform porting interface. (see the [Porting Guide] for details) +* `COLD_BOOT_SINGLE_CPU`: This option indicates whether the platform may + release several CPUs out of reset. It can take either 0 (several CPUs may be + brought up) or 1 (only one CPU will ever be brought up during cold reset). + Default is 0. If the platform always brings up a single CPU, there is no + need to distinguish between primary and secondary CPUs and the boot path can + be optimised. The `plat_is_my_cpu_primary()` and + `plat_secondary_cold_boot_setup()` platform porting interfaces do not need + to be implemented in this case. + * `PSCI_EXTENDED_STATE_ID`: As per PSCI1.0 Specification, there are 2 formats possible for the PSCI power-state parameter viz original and extended State-ID formats. This flag if set to 1, configures the generic PSCI layer From dc2d4038b9c95b8509eb7df82d02911d2843528f Mon Sep 17 00:00:00 2001 From: Sandrine Bailleux Date: Wed, 11 Nov 2015 11:39:32 +0000 Subject: [PATCH 8/8] User Guide: Remove reference to porting guide The implications of the 'PROGRAMMABLE_RESET_ADDRESS' build option on the platform porting layer are simple enough to be described in the User Guide directly. This patch removes the reference to the Porting Guide. Change-Id: I7f753b18abd20effc4fd30836609e1fd51d9221d --- docs/user-guide.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/user-guide.md b/docs/user-guide.md index f7e4f4c79..25f884efb 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -355,9 +355,9 @@ performed. either 0 (fixed) or 1 (programmable). Default is 0. If the platform has a programmable reset address, it is expected that a CPU will start executing code directly at the right address, both on a cold and warm reset. In this - case, there is no need to identify the entrypoint on boot and this has - implication for `plat_get_my_entrypoint()` platform porting interface. - (see the [Porting Guide] for details) + case, there is no need to identify the entrypoint on boot and the boot path + can be optimised. The `plat_get_my_entrypoint()` platform porting interface + does not need to be implemented in this case. * `COLD_BOOT_SINGLE_CPU`: This option indicates whether the platform may release several CPUs out of reset. It can take either 0 (several CPUs may be @@ -1255,6 +1255,5 @@ _Copyright (c) 2013-2015, ARM Limited and Contributors. All rights reserved._ [Juno Software Guide]: http://community.arm.com/docs/DOC-8396 [DS-5]: http://www.arm.com/products/tools/software-tools/ds-5/index.php [mbedTLS Repository]: https://github.com/ARMmbed/mbedtls.git -[Porting Guide]: ./porting-guide.md [PSCI]: http://infocenter.arm.com/help/topic/com.arm.doc.den0022c/DEN0022C_Power_State_Coordination_Interface.pdf "Power State Coordination Interface PDD (ARM DEN 0022C)" [Trusted Board Boot]: trusted-board-boot.md