From b726c1690736110be349fd84f87a70d5272ab805 Mon Sep 17 00:00:00 2001 From: Antonio Nino Diaz Date: Fri, 11 May 2018 11:15:10 +0100 Subject: [PATCH 1/2] plat/arm: Introduce ARM_LINUX_KERNEL_AS_BL33 build option Normally, BL33 needs to contain a boot loader like U-Boot or UEFI that eventually gives control to the OS. However, in some cases, this boot sequence may be too slow. For example, when doing tests in a cycle-accurate emulator, the user may only be interested in the interaction between the Trusted Firmware and the OS, not in the boot process itself. The new option ARM_LINUX_KERNEL_AS_BL33 allows BL33 to contain the Linux kernel image by changing the value of registers x0-x3 to the values expected by the kernel. This option requires the device tree blob (DTB) to be present in memory. Its address must be specified in the newly introduced ARM_PRELOADED_DTB_BASE build option. For now, it only supports AArch64 kernels. This option is only available when RESET_TO_BL31=1. For this reason the BL33 binary must be preloaded in memory and PRELOADED_BL33_BASE must be used. For example, if the kernel is loaded at 0x80080000 and the DTB is loaded at address 0x82000000, the firmware could be built like this: CROSS_COMPILE=aarch64-linux-gnu- \ make PLAT=fvp DEBUG=1 \ RESET_TO_BL31=1 \ ARM_LINUX_KERNEL_AS_BL33=1 \ PRELOADED_BL33_BASE=0x80080000 \ ARM_PRELOADED_DTB_BASE=0x82000000 \ all fip Change-Id: If9dc847c65ae2d0c27b51f0fd44fc06b28497db9 Signed-off-by: Antonio Nino Diaz --- docs/user-guide.rst | 11 ++++++++++- plat/arm/common/arm_bl31_setup.c | 17 +++++++++++++++-- plat/arm/common/arm_common.mk | 21 +++++++++++++++++++++ 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/docs/user-guide.rst b/docs/user-guide.rst index aed54a6315..4a8c3a1567 100644 --- a/docs/user-guide.rst +++ b/docs/user-guide.rst @@ -590,7 +590,7 @@ Common build options firmware images have been loaded in memory, and the MMU and caches are turned off. Refer to the "Debugging options" section for more details. -- ``SP_MIN_WITH_SECURE_FIQ``: Boolean flag to indicate the SP_MIN handles +- ``SP_MIN_WITH_SECURE_FIQ``: Boolean flag to indicate the SP_MIN handles secure interrupts (caught through the FIQ line). Platforms can enable this directive if they need to handle such interruption. When enabled, the FIQ are handled in monitor mode and non secure world is not allowed @@ -678,6 +678,15 @@ Arm development platform specific build options Trusted Watchdog may be disabled at build time for testing or development purposes. +- ``ARM_LINUX_KERNEL_AS_BL33``: The Linux kernel expects registers x0-x3 to + have specific values at boot. This boolean option allows the Trusted Firmware + to have a Linux kernel image as BL33 by preparing the registers to these + values before jumping to BL33. This option defaults to 0 (disabled). For now, + it only supports AArch64 kernels. ``RESET_TO_BL31`` must be 1 when using it. + If this option is set to 1, ``ARM_PRELOADED_DTB_BASE`` must be set to the + location of a device tree blob (DTB) already loaded in memory. The Linux + Image address must be specified using the ``PRELOADED_BL33_BASE`` option. + - ``ARM_RECOM_STATE_ID_ENC``: The PSCI1.0 specification recommends an encoding for the construction of composite state-ID in the power-state parameter. The existing PSCI clients currently do not support this encoding of diff --git a/plat/arm/common/arm_bl31_setup.c b/plat/arm/common/arm_bl31_setup.c index 3c70f9de85..ae0bf0488a 100644 --- a/plat/arm/common/arm_bl31_setup.c +++ b/plat/arm/common/arm_bl31_setup.c @@ -80,7 +80,7 @@ void arm_bl31_early_platform_setup(bl31_params_t *from_bl2, uintptr_t soc_fw_con assert(from_bl2 == NULL); assert(plat_params_from_bl2 == NULL); -#ifdef BL32_BASE +# ifdef BL32_BASE /* Populate entry point information for BL32 */ SET_PARAM_HEAD(&bl32_image_ep_info, PARAM_EP, @@ -89,7 +89,7 @@ void arm_bl31_early_platform_setup(bl31_params_t *from_bl2, uintptr_t soc_fw_con SET_SECURITY_STATE(bl32_image_ep_info.h.attr, SECURE); bl32_image_ep_info.pc = BL32_BASE; bl32_image_ep_info.spsr = arm_get_spsr_for_bl32_entry(); -#endif /* BL32_BASE */ +# endif /* BL32_BASE */ /* Populate entry point information for BL33 */ SET_PARAM_HEAD(&bl33_image_ep_info, @@ -105,6 +105,19 @@ void arm_bl31_early_platform_setup(bl31_params_t *from_bl2, uintptr_t soc_fw_con bl33_image_ep_info.spsr = arm_get_spsr_for_bl33_entry(); SET_SECURITY_STATE(bl33_image_ep_info.h.attr, NON_SECURE); +# if ARM_LINUX_KERNEL_AS_BL33 + /* + * According to the file ``Documentation/arm64/booting.txt`` of the + * Linux kernel tree, Linux expects the physical address of the device + * tree blob (DTB) in x0, while x1-x3 are reserved for future use and + * must be 0. + */ + bl33_image_ep_info.args.arg0 = (u_register_t)ARM_PRELOADED_DTB_BASE; + bl33_image_ep_info.args.arg1 = 0U; + bl33_image_ep_info.args.arg2 = 0U; + bl33_image_ep_info.args.arg3 = 0U; +# endif + #else /* RESET_TO_BL31 */ /* diff --git a/plat/arm/common/arm_common.mk b/plat/arm/common/arm_common.mk index 015e454a12..055819bf99 100644 --- a/plat/arm/common/arm_common.mk +++ b/plat/arm/common/arm_common.mk @@ -80,6 +80,27 @@ ARM_XLAT_TABLES_LIB_V1 := 0 $(eval $(call assert_boolean,ARM_XLAT_TABLES_LIB_V1)) $(eval $(call add_define,ARM_XLAT_TABLES_LIB_V1)) +# Don't have the Linux kernel as a BL33 image by default +ARM_LINUX_KERNEL_AS_BL33 := 0 +$(eval $(call assert_boolean,ARM_LINUX_KERNEL_AS_BL33)) +$(eval $(call add_define,ARM_LINUX_KERNEL_AS_BL33)) + +ifeq (${ARM_LINUX_KERNEL_AS_BL33},1) + ifneq (${ARCH},aarch64) + $(error "ARM_LINUX_KERNEL_AS_BL33 is only available in AArch64.") + endif + ifneq (${RESET_TO_BL31},1) + $(error "ARM_LINUX_KERNEL_AS_BL33 is only available if RESET_TO_BL31=1.") + endif + ifndef PRELOADED_BL33_BASE + $(error "PRELOADED_BL33_BASE must be set if ARM_LINUX_KERNEL_AS_BL33 is used.") + endif + ifndef ARM_PRELOADED_DTB_BASE + $(error "ARM_PRELOADED_DTB_BASE must be set if ARM_LINUX_KERNEL_AS_BL33 is used.") + endif + $(eval $(call add_define,ARM_PRELOADED_DTB_BASE)) +endif + # Use an implementation of SHA-256 with a smaller memory footprint but reduced # speed. $(eval $(call add_define,MBEDTLS_SHA256_SMALLER)) From 32412a8a6b0da74d13d086c7f48afeaff7e885c5 Mon Sep 17 00:00:00 2001 From: Antonio Nino Diaz Date: Mon, 14 May 2018 09:12:34 +0100 Subject: [PATCH 2/2] Replace bootwrapped kernel instructions from User Guide The instructions to boot the bootwrapped kernel were outdated. Also, the bootwrapped kernel boot flow isn't really useful. It was meant to be a replacement for the Trusted Firmware-A, not to be used as the next step during boot. The instructions have been removed in favour of the new build option ARM_LINUX_KERNEL_AS_BL33. This new system directly boots the Linux kernel from BL31, and requires RESET_TO_BL31 to be 1. Also, the kernel has to be preloaded in memory, so PRELOADED_BL33_BASE has to be set to its address. This way, the runtime services of the Trusted Firmware-A are available for the kernel in the least possible amount of time. This new system requires the DTB to be patched so that the kernel knows where the ramdisk is. A short script to add this information to the DTB has been added to the User Guide. The information related to it can be found in the following file in the Linux kernel tree: ``Documentation/devicetree/bindings/chosen.txt`` Change-Id: Ide135580959e09f6aa8e4425f37ea55d97439178 Signed-off-by: Antonio Nino Diaz --- docs/user-guide.rst | 101 +++++++++++++++++++++++++++++++++----------- 1 file changed, 76 insertions(+), 25 deletions(-) diff --git a/docs/user-guide.rst b/docs/user-guide.rst index 4a8c3a1567..280831448c 100644 --- a/docs/user-guide.rst +++ b/docs/user-guide.rst @@ -1484,41 +1484,92 @@ without a BL33 and prepare to jump to a BL33 image loaded at address make PRELOADED_BL33_BASE=0x80000000 PLAT=fvp all fip -Boot of a preloaded bootwrapped kernel image on Base FVP -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Boot of a preloaded kernel image on Base FVP +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The following example uses the AArch64 boot wrapper. This simplifies normal -world booting while also making use of TF-A features. It can be obtained from -its repository with: +The following example uses a simplified boot flow by directly jumping from the +TF-A to the Linux kernel, which will use a ramdisk as filesystem. This can be +useful if both the kernel and the device tree blob (DTB) are already present in +memory (like in FVP). + +For example, if the kernel is loaded at ``0x80080000`` and the DTB is loaded at +address ``0x82000000``, the firmware can be built like this: :: - git clone git://git.kernel.org/pub/scm/linux/kernel/git/mark/boot-wrapper-aarch64.git + CROSS_COMPILE=aarch64-linux-gnu- \ + make PLAT=fvp DEBUG=1 \ + RESET_TO_BL31=1 \ + ARM_LINUX_KERNEL_AS_BL33=1 \ + PRELOADED_BL33_BASE=0x80080000 \ + ARM_PRELOADED_DTB_BASE=0x82000000 \ + all fip -After compiling it, an ELF file is generated. It can be loaded with the -following command: +Now, it is needed to modify the DTB so that the kernel knows the address of the +ramdisk. The following script generates a patched DTB from the provided one, +assuming that the ramdisk is loaded at address ``0x84000000``. Note that this +script assumes that the user is using a ramdisk image prepared for U-Boot, like +the ones provided by Linaro. If using a ramdisk without this header,the ``0x40`` +offset in ``INITRD_START`` has to be removed. + +.. code:: bash + + #!/bin/bash + + # Path to the input DTB + KERNEL_DTB=/ + # Path to the output DTB + PATCHED_KERNEL_DTB=/ + # Base address of the ramdisk + INITRD_BASE=0x84000000 + # Path to the ramdisk + INITRD=/ + + # Skip uboot header (64 bytes) + INITRD_START=$(printf "0x%x" $((${INITRD_BASE} + 0x40)) ) + INITRD_SIZE=$(stat -Lc %s ${INITRD}) + INITRD_END=$(printf "0x%x" $((${INITRD_BASE} + ${INITRD_SIZE})) ) + + CHOSEN_NODE=$(echo \ + "/ { \ + chosen { \ + linux,initrd-start = <${INITRD_START}>; \ + linux,initrd-end = <${INITRD_END}>; \ + }; \ + };") + + echo $(dtc -O dts -I dtb ${KERNEL_DTB}) ${CHOSEN_NODE} | \ + dtc -O dtb -o ${PATCHED_KERNEL_DTB} - + +And the FVP binary can be run with the following command: :: - /FVP_Base_AEMv8A-AEMv8A \ - -C bp.secureflashloader.fname=bl1.bin \ - -C bp.flashloader0.fname=fip.bin \ - -a cluster0.cpu0= \ - --start cluster0.cpu0=0x0 + /FVP_Base_AEMv8A-AEMv8A \ + -C pctl.startup=0.0.0.0 \ + -C bp.secure_memory=1 \ + -C cluster0.NUM_CORES=4 \ + -C cluster1.NUM_CORES=4 \ + -C cache_state_modelled=1 \ + -C cluster0.cpu0.RVBAR=0x04020000 \ + -C cluster0.cpu1.RVBAR=0x04020000 \ + -C cluster0.cpu2.RVBAR=0x04020000 \ + -C cluster0.cpu3.RVBAR=0x04020000 \ + -C cluster1.cpu0.RVBAR=0x04020000 \ + -C cluster1.cpu1.RVBAR=0x04020000 \ + -C cluster1.cpu2.RVBAR=0x04020000 \ + -C cluster1.cpu3.RVBAR=0x04020000 \ + --data cluster0.cpu0="/bl31.bin"@0x04020000 \ + --data cluster0.cpu0="/"@0x82000000 \ + --data cluster0.cpu0="/"@0x80080000 \ + --data cluster0.cpu0="/"@0x84000000 -The ``-a cluster0.cpu0=`` option loads the ELF file. It -also sets the PC register to the ELF entry point address, which is not the -desired behaviour, so the ``--start cluster0.cpu0=0x0`` option forces the PC back -to 0x0 (the BL1 entry point address) on CPU #0. The ``PRELOADED_BL33_BASE`` define -used when compiling the FIP must match the ELF entry point. +Boot of a preloaded kernel image on Juno +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Boot of a preloaded bootwrapped kernel image on Juno -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The procedure to obtain and compile the boot wrapper is very similar to the case -of the FVP. The execution must be stopped at the end of bl2\_main(), and the -loading method explained above in the EL3 payload boot flow section may be used -to load the ELF file over JTAG on Juno. +The Trusted Firmware must be compiled in a similar way as for FVP explained +above. The process to load binaries to memory is the one explained in +`Booting an EL3 payload on Juno`_. Running the software on FVP ---------------------------