[PATCH 1/3] arm64: Add late jump to kernel board hook

Add empty weak assembler function armv8_switch_to_el2_prep() which is jumped to just before U-Boot determines which EL it is running in and decides which path to take to boot the Linux kernel.
This weak function is meant to be used by architecture specific code to implement jump to a firmware blob, which then returns right past this weak function and continues execution of U-Boot code which then boots the Linux kernel. One example of such use case is when U-Boot jump tp TFA BL31, which switches from EL3 to EL2 and then returns to U-Boot code newly running in EL2 and starts the Linux kernel.
The weak function is called with caches already disabled and DM shut down. Any preparatory work or even loading of more data must be done in board_prep_linux(), this hook is meant only for the final jump to the firmware and return to U-Boot before booting Linux.
Signed-off-by: Marek Vasut marek.vasut+renesas@mailbox.org --- Cc: Andre Przywara andre.przywara@arm.com Cc: Caleb Connolly caleb.connolly@linaro.org Cc: Igor Opaniuk igor.opaniuk@gmail.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Julien Masson jmasson@baylibre.com Cc: Mattijs Korpershoek mkorpershoek@baylibre.com Cc: Maxim Moskalets maximmosk4@gmail.com Cc: Michael Walle mwalle@kernel.org Cc: Nobuhiro Iwamatsu iwamatsu@nigauri.org Cc: Patrick Rudolph patrick.rudolph@9elements.com Cc: Paul Barker paul.barker.ct@bp.renesas.com Cc: Paul-Erwan Rio paulerwan.rio@gmail.com Cc: Peter Hoyes Peter.Hoyes@arm.com Cc: Raymond Mao raymond.mao@linaro.org Cc: Sam Protsenko semen.protsenko@linaro.org Cc: Simon Glass sjg@chromium.org Cc: Sughosh Ganu sughosh.ganu@linaro.org Cc: Tom Rini trini@konsulko.com Cc: u-boot@lists.denx.de --- arch/arm/cpu/armv8/transition.S | 8 ++++++++ arch/arm/include/asm/system.h | 16 ++++++++++++++++ 2 files changed, 24 insertions(+)
diff --git a/arch/arm/cpu/armv8/transition.S b/arch/arm/cpu/armv8/transition.S index 9dbdff3a4fc..85f13ccd0d2 100644 --- a/arch/arm/cpu/armv8/transition.S +++ b/arch/arm/cpu/armv8/transition.S @@ -9,8 +9,16 @@ #include <linux/linkage.h> #include <asm/macro.h>
+.pushsection .text.armv8_switch_to_el2_prep, "ax" +WEAK(armv8_switch_to_el2_prep) + ret +ENDPROC(armv8_switch_to_el2_prep) +.popsection + .pushsection .text.armv8_switch_to_el2, "ax" ENTRY(armv8_switch_to_el2) + bl armv8_switch_to_el2_prep + nop switch_el x6, 1f, 0f, 0f 0: cmp x5, #ES_TO_AARCH64 diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h index dbf9ab43e28..091082281c7 100644 --- a/arch/arm/include/asm/system.h +++ b/arch/arm/include/asm/system.h @@ -238,6 +238,22 @@ int __asm_flush_l3_dcache(void); int __asm_invalidate_l3_icache(void); void __asm_switch_ttbr(u64 new_ttbr);
+/* + * armv8_switch_to_el2_prep() - prepare for switch from EL3 to EL2 for ARMv8 + * + * @args: For loading 64-bit OS, fdt address. + * For loading 32-bit OS, zero. + * @mach_nr: For loading 64-bit OS, zero. + * For loading 32-bit OS, machine nr + * @fdt_addr: For loading 64-bit OS, zero. + * For loading 32-bit OS, fdt address. + * @arg4: Input argument. + * @entry_point: kernel entry point + * @es_flag: execution state flag, ES_TO_AARCH64 or ES_TO_AARCH32 + */ +void armv8_switch_to_el2_prep(u64 args, u64 mach_nr, u64 fdt_addr, + u64 arg4, u64 entry_point, u64 es_flag); + /* * armv8_switch_to_el2() - switch from EL3 to EL2 for ARMv8 *

Add support for starting TFA from U-Boot running in EL3 as part of fitImage boot, so the user can start U-Boot in the highest privilege level on the platform, bundle TFA, Linux, DT into a single fitImage and boot such a bundle as a whole.
There are two main benefits of this approach. First is the ability to run U-Boot in EL3, where it has unrestricted access to the entire system and can act as a useful debug tool, as it was always intended to be used. Second is the ability to easily and safely update of any component in the fitImage, be it TFA, Linux or DT.
The boot process is similar to regular Linux with DT fitImage boot process, except the TFA has to be bundled into the fitImage. For the bundling instructions, see below. The TFA is started as a 'loadables' with custom U_BOOT_FIT_LOADABLE_HANDLER and armv8_switch_to_el2_prep() handling implemented in board code, and performing the handoff and boot in case the TFA was loaded.
The loadables handler is optional and meant to set up any sort of handoff structures used by the TFA BL31 or perform any other setup that is needed by the blob. The custom armv8_switch_to_el2_prep() has to implement the jump to TFA BL31 with return to U-Boot just before booting the Linux kernel.
Example fitImage image and configuration section:
/dts-v1/;
/ { description = "Linux kernel with FDT blob and TFA BL31";
images { kernel-1 { ... }; fdt-1 { ... }; atf-1 { /* This is the TFA BL31 image */ description = "TFA BL31"; data = /incbin/("../build/plat/release/bl31.bin"); type = "tfa-bl31"; arch = "arm64"; os = "arm-trusted-firmware"; compression = "none"; load = <0x46400000>; entry = <0x46400000>; }; };
configurations { default = "conf-1"; conf-1 { description = "Boot Linux"; kernel = "kernel-1"; fdt = "fdt-1"; loadables = "atf-1"; /* This is the TFA BL31 loadable */ }; }; };
Signed-off-by: Marek Vasut marek.vasut+renesas@mailbox.org --- Cc: Andre Przywara andre.przywara@arm.com Cc: Caleb Connolly caleb.connolly@linaro.org Cc: Igor Opaniuk igor.opaniuk@gmail.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Julien Masson jmasson@baylibre.com Cc: Mattijs Korpershoek mkorpershoek@baylibre.com Cc: Maxim Moskalets maximmosk4@gmail.com Cc: Michael Walle mwalle@kernel.org Cc: Nobuhiro Iwamatsu iwamatsu@nigauri.org Cc: Patrick Rudolph patrick.rudolph@9elements.com Cc: Paul Barker paul.barker.ct@bp.renesas.com Cc: Paul-Erwan Rio paulerwan.rio@gmail.com Cc: Peter Hoyes Peter.Hoyes@arm.com Cc: Raymond Mao raymond.mao@linaro.org Cc: Sam Protsenko semen.protsenko@linaro.org Cc: Simon Glass sjg@chromium.org Cc: Sughosh Ganu sughosh.ganu@linaro.org Cc: Tom Rini trini@konsulko.com Cc: u-boot@lists.denx.de --- boot/image-fit.c | 1 + boot/image.c | 1 + include/image.h | 1 + 3 files changed, 3 insertions(+)
diff --git a/boot/image-fit.c b/boot/image-fit.c index db7fb61bca9..3b6d7231aff 100644 --- a/boot/image-fit.c +++ b/boot/image-fit.c @@ -2167,6 +2167,7 @@ int fit_image_load(struct bootm_headers *images, ulong addr, type_ok = fit_image_check_type(fit, noffset, image_type) || fit_image_check_type(fit, noffset, IH_TYPE_FIRMWARE) || fit_image_check_type(fit, noffset, IH_TYPE_TEE) || + fit_image_check_type(fit, noffset, IH_TYPE_TFA_BL31) || (image_type == IH_TYPE_KERNEL && fit_image_check_type(fit, noffset, IH_TYPE_KERNEL_NOLOAD));
diff --git a/boot/image.c b/boot/image.c index abac254e026..139c5bd035a 100644 --- a/boot/image.c +++ b/boot/image.c @@ -183,6 +183,7 @@ static const table_entry_t uimage_type[] = { { IH_TYPE_FDT_LEGACY, "fdt_legacy", "legacy Image with Flat Device Tree ", }, { IH_TYPE_RENESAS_SPKG, "spkgimage", "Renesas SPKG Image" }, { IH_TYPE_STARFIVE_SPL, "sfspl", "StarFive SPL Image" }, + { IH_TYPE_TFA_BL31, "tfa-bl31", "TFA BL31 Image", }, { -1, "", "", }, };
diff --git a/include/image.h b/include/image.h index 0a61dfd556c..3adb7219809 100644 --- a/include/image.h +++ b/include/image.h @@ -232,6 +232,7 @@ enum image_type_t { IH_TYPE_FDT_LEGACY, /* Binary Flat Device Tree Blob in a Legacy Image */ IH_TYPE_RENESAS_SPKG, /* Renesas SPKG image */ IH_TYPE_STARFIVE_SPL, /* StarFive SPL image */ + IH_TYPE_TFA_BL31, /* TFA BL31 image */
IH_TYPE_COUNT, /* Number of image types */ };

Hi Marex,
Thanks for the patch
-----Original Message----- From: U-Boot u-boot-bounces@lists.denx.de On Behalf Of Marek Vasut Sent: 12 January 2025 22:37 Subject: [PATCH 2/3] image: Add support for starting TFA BL31 as fitImage loadables
Add support for starting TFA from U-Boot running in EL3 as part of fitImage boot, so the user can start U-Boot in the highest privilege level on the platform, bundle TFA, Linux, DT into a single fitImage and boot such a bundle as a whole.
There are two main benefits of this approach. First is the ability to run U-Boot in EL3, where it has unrestricted access to the entire system and can act as a useful debug tool, as it was always intended to be used. Second is the ability to easily and safely update of any component in the fitImage, be it TFA, Linux or DT.
In this case, who will do switching from secure world to normal world, u-boot? Do you have any link to the u-boot patch from secure to normal world switching with kernel entry point passed to BL31?
Otherwise, kernel, also will be executing in secure world mode.
Cheers, Biju

On 1/13/25 1:15 PM, Biju Das wrote:
Hi Marex,
Thanks for the patch
-----Original Message----- From: U-Boot u-boot-bounces@lists.denx.de On Behalf Of Marek Vasut Sent: 12 January 2025 22:37 Subject: [PATCH 2/3] image: Add support for starting TFA BL31 as fitImage loadables
Add support for starting TFA from U-Boot running in EL3 as part of fitImage boot, so the user can start U-Boot in the highest privilege level on the platform, bundle TFA, Linux, DT into a single fitImage and boot such a bundle as a whole.
There are two main benefits of this approach. First is the ability to run U-Boot in EL3, where it has unrestricted access to the entire system and can act as a useful debug tool, as it was always intended to be used. Second is the ability to easily and safely update of any component in the fitImage, be it TFA, Linux or DT.
In this case, who will do switching from secure world to normal world, u-boot? Do you have any link to the u-boot patch from secure to normal world switching with kernel entry point passed to BL31?
Otherwise, kernel, also will be executing in secure world mode.
See 3/3 , U-Boot runs in EL3 , before starting kernel it jumps into TFA BL31 , TFA BL31 does its setup and switches from EL3 to EL2 , TFA BL31 returns to U-Boot armv8_switch_to_el2 which checks if this code is running in EL2 (it is) and does the last few steps before starting kernel, and then finally jumps to the kernel .

Hi Marex,
-----Original Message----- From: Marek Vasut marek.vasut@mailbox.org Sent: 13 January 2025 12:40 Subject: Re: [PATCH 2/3] image: Add support for starting TFA BL31 as fitImage loadables
On 1/13/25 1:15 PM, Biju Das wrote:
Hi Marex,
Thanks for the patch
-----Original Message----- From: U-Boot u-boot-bounces@lists.denx.de On Behalf Of Marek Vasut Sent: 12 January 2025 22:37 Subject: [PATCH 2/3] image: Add support for starting TFA BL31 as fitImage loadables
Add support for starting TFA from U-Boot running in EL3 as part of fitImage boot, so the user can start U-Boot in the highest privilege level on the platform, bundle TFA, Linux, DT into a single fitImage and boot such a bundle as a
whole.
There are two main benefits of this approach. First is the ability to run U-Boot in EL3, where it has unrestricted access to the entire system and can act as a useful debug tool, as it was always intendedSecure) to be used. Second is the ability to easily and safely update of any component in the fitImage, be
it TFA, Linux or DT.
In this case, who will do switching from secure world to normal world, u-boot? Do you have any link to the u-boot patch from secure to normal world switching with kernel entry point passed to BL31?
Otherwise, kernel, also will be executing in secure world mode.
See 3/3 , U-Boot runs in EL3 , before starting kernel it jumps into TFA BL31 , TFA BL31 does its setup and switches from EL3 to EL2 , TFA BL31 returns to U-Boot armv8_switch_to_el2 which checks if this code is running in EL2 (it is) and does the last few steps before starting kernel, and then finally jumps to the kernel .
Thanks for the explanation. I missed the details in 3/3.
BL2(Secure)->U-boot(Secure)->BL31(Secure)-->U-boot(Normal)->Linux Kernel(Normal)
Cheers, Biju

Hi Marek, Biju,
On 1/13/25 2:31 PM, Biju Das wrote:
Hi Marex,
-----Original Message----- From: Marek Vasut marek.vasut@mailbox.org Sent: 13 January 2025 12:40 Subject: Re: [PATCH 2/3] image: Add support for starting TFA BL31 as fitImage loadables
On 1/13/25 1:15 PM, Biju Das wrote:
Hi Marex,
Thanks for the patch
-----Original Message----- From: U-Boot u-boot-bounces@lists.denx.de On Behalf Of Marek Vasut Sent: 12 January 2025 22:37 Subject: [PATCH 2/3] image: Add support for starting TFA BL31 as fitImage loadables
Add support for starting TFA from U-Boot running in EL3 as part of fitImage boot, so the user can start U-Boot in the highest privilege level on the platform, bundle TFA, Linux, DT into a single fitImage and boot such a bundle as a
whole.
There are two main benefits of this approach. First is the ability to run U-Boot in EL3, where it has unrestricted access to the entire system and can act as a useful debug tool, as it was always intendedSecure) to be used. Second is the ability to easily and safely update of any component in the fitImage, be
it TFA, Linux or DT.
In this case, who will do switching from secure world to normal world, u-boot? Do you have any link to the u-boot patch from secure to normal world switching with kernel entry point passed to BL31?
Otherwise, kernel, also will be executing in secure world mode.
See 3/3 , U-Boot runs in EL3 , before starting kernel it jumps into TFA BL31 , TFA BL31 does its setup and switches from EL3 to EL2 , TFA BL31 returns to U-Boot armv8_switch_to_el2 which checks if this code is running in EL2 (it is) and does the last few steps before starting kernel, and then finally jumps to the kernel .
Thanks for the explanation. I missed the details in 3/3.
BL2(Secure)->U-boot(Secure)->BL31(Secure)-->U-boot(Normal)->Linux Kernel(Normal)
If the fit image is missing the TF-A loadable property for example (and maybe in other corner case scenario?) the kernel will start in EL3 though and fail very early since it needs TF-A BL31 for a few things. However, part of the kernel code will actually run in EL3 and that could be enough to mess things up with a carefully crafted kernel image.
Is this actually possible or am I making stuff up? If it is possible, isn't that an issue?
Currently, this is already possible and has happened a few times already at my company with people forgetting to bundle TF-A in U-Boot and then the kernel would boot but stops early (no console output even IIRC). But the bootloader is not THAT often updated in the field compared to kernel (especially if coming from distros) which would now allow to store the TF-A, so this opens an even bigger can of worms? In a way, it allows to update TF-A likely more securely (which is VERY welcome on Rockchip for example, where we usually start new SoC bringups with a blob from Rockchip and then have upstream TF-A catch up a few years later. Having BL31 part of U-Boot is a huge pain because it is difficult for some devices to do safe updates of U-Boot) but may introduce some bigger security issues?
Finally, another question, what happens if we have a U-Boot with bundled TF-A BL31 AND it tries to boot a fitimage with a TF-A BL31 loadable? Should we only allow to load tf-a BL31 if running from EL3 (meaning if in EL2, TF-A is expected to be already running)?
Cheers, Quentin

On 1/15/25 12:51 PM, Quentin Schulz wrote:
Hi Marek, Biju,
Hi,
[...]
See 3/3 , U-Boot runs in EL3 , before starting kernel it jumps into TFA BL31 , TFA BL31 does its setup and switches from EL3 to EL2 , TFA BL31 returns to U-Boot armv8_switch_to_el2 which checks if this code is running in EL2 (it is) and does the last few steps before starting kernel, and then finally jumps to the kernel .
Thanks for the explanation. I missed the details in 3/3.
BL2(Secure)->U-boot(Secure)->BL31(Secure)-->U-boot(Normal)->Linux Kernel(Normal)
If the fit image is missing the TF-A loadable property for example (and maybe in other corner case scenario?) the kernel will start in EL3 though and fail very early since it needs TF-A BL31 for a few things.
Not quite, even if the TFA loadable is not present, the kernel will still be started through armv8_switch_to_el2() , which will switch the core into EL2 and then start the kernel. The kernel will however likely fail to boot because it will attempt to access PSCI via SMC and there will be nothing there, so it will fail at that point.
There will be a follow up patch for R-Car Gen4, which will extend and enable the current minimal R-Car Gen4 PSCI support in U-Boot, so even if you do not bundle TFA into the fitImage, the kernel won't fail to boot entirely.
With the current R-Car Gen4 PSCI implementation in U-Boot, the kernel can boot and run on a single CPU core only, because the CPU core start and stop callbacks are not implemented and not advertised to Linux yet.
Any other SoC that would like to move TFA start after U-Boot start would also likely have to implement at least minimal PSCI implementation to provide a safety net for users who do not bundle the TFA BL31 PSCI provider blob into the fitImage for some reason. The current R-Car Gen4 PSCI implementation is generic enough, that it can act as a template for such other future SoC specific PSCI implementations.
However, part of the kernel code will actually run in EL3 and that could be enough to mess things up with a carefully crafted kernel image.
Is this actually possible or am I making stuff up? If it is possible, isn't that an issue?
Currently, this is already possible and has happened a few times already at my company with people forgetting to bundle TF-A in U-Boot and then the kernel would boot but stops early (no console output even IIRC). But the bootloader is not THAT often updated in the field compared to kernel (especially if coming from distros) which would now allow to store the TF-A, so this opens an even bigger can of worms?
See above, I think that answers this question.
In a way, it allows to update TF-A likely more securely
That is very much the point of this, yes.
Also, safely update as much as possible in lockstep, for example in case someone has the idea to extend the TFA PSCI implementation with custom SMC call handler(s) and make vendor Linux kernel fork depend on those custom SMC calls for something (*). If the next kernel fork depends on a different set of custom SMC calls (because stable ABI in such cases may not be a guarantee) then updating only the kernel may lead to unbootable system. Worse, updating the bootloader and kernel may lead to kernel boot failure, AND if (usually seldom updated) recovery system kernel did not get updated in lockstep with bootloader, recovery system which depends on the old SMC ABI would also fail to boot, leaving the system unrecoverable.
If both kernel and the custom TFA implementation are updated and booted as part of the same fitImage file, the ABI problem goes away.
Also, it allows for easy experimentation with the TFA, you can keep swapping blobs in and out as needed to test them out.
(*) This is not the case with Renesas to my knowledge , whew !
(which is VERY welcome on Rockchip for example, where we usually start new SoC bringups with a blob from Rockchip and then have upstream TF-A catch up a few years later. Having BL31 part of U-Boot is a huge pain because it is difficult for some devices to do safe updates of U-Boot) but may introduce some bigger security issues?
U-Boot will run in EL3 instead of EL2 in this case, which gives it complete unrestricted access to the entire system. It can act as a debug tool, the way it was designed to work since the very beginning.
Do you have anything specific in mind here ?
Finally, another question, what happens if we have a U-Boot with bundled TF-A BL31 AND it tries to boot a fitimage with a TF-A BL31 loadable?
It very much depends on the SoC specific TFA BL31 start handler implementation (see patch 3/3). It is possible to add check in there to test whether the system is already running in EL2 for example, and if anything responds to PSCI version SMC call, and if so, skip starting the TFA again.
I don't think TFA BL31 can be started in EL2 in any case.
Should we only allow to load tf-a BL31 if running from EL3 (meaning if in EL2, TF-A is expected to be already running)?
That's for the SoC specific handler to decide, see above.

On Sun, Jan 12, 2025 at 11:36:58PM +0100, Marek Vasut wrote:
Add support for starting TFA from U-Boot running in EL3 as part of fitImage boot, so the user can start U-Boot in the highest privilege level on the platform, bundle TFA, Linux, DT into a single fitImage and boot such a bundle as a whole.
There are two main benefits of this approach. First is the ability to run U-Boot in EL3, where it has unrestricted access to the entire system and can act as a useful debug tool, as it was always intended to be used. Second is the ability to easily and safely update of any component in the fitImage, be it TFA, Linux or DT.
The boot process is similar to regular Linux with DT fitImage boot process, except the TFA has to be bundled into the fitImage. For the bundling instructions, see below. The TFA is started as a 'loadables' with custom U_BOOT_FIT_LOADABLE_HANDLER and armv8_switch_to_el2_prep() handling implemented in board code, and performing the handoff and boot in case the TFA was loaded.
The loadables handler is optional and meant to set up any sort of handoff structures used by the TFA BL31 or perform any other setup that is needed by the blob. The custom armv8_switch_to_el2_prep() has to implement the jump to TFA BL31 with return to U-Boot just before booting the Linux kernel.
Example fitImage image and configuration section:
/dts-v1/;
/ { description = "Linux kernel with FDT blob and TFA BL31";
images { kernel-1 { ... }; fdt-1 { ... }; atf-1 { /* This is the TFA BL31 image */ description = "TFA BL31"; data = /incbin/("../build/plat/release/bl31.bin"); type = "tfa-bl31"; arch = "arm64"; os = "arm-trusted-firmware"; compression = "none"; load = <0x46400000>; entry = <0x46400000>; }; }; configurations { default = "conf-1"; conf-1 { description = "Boot Linux"; kernel = "kernel-1"; fdt = "fdt-1"; loadables = "atf-1"; /* This is the TFA BL31 loadable */ }; };
};
Signed-off-by: Marek Vasut marek.vasut+renesas@mailbox.org
Reviewed-by: Tom Rini trini@konsulko.com

Implement custom U_BOOT_FIT_LOADABLE_HANDLER and armv8_switch_to_el2_prep() handling in case the TFA was loaded. The loadables handler sets up custom handoff structure used by Renesas TFA fork in fixed location in DRAM and indicates the TFA has been loaded.
The custom armv8_switch_to_el2_prep() handling tests whether the TFA BL31 was previously loaded and the custom handoff structure was set up, and if so, jumps to TFA BL31 which switches from EL3 to EL2 and then returns to U-Boot just past bl in armv8_switch_to_el2() to finish starting the Linux kernel.
The jump to Linux through TFA works in such a way that the custom armv8_switch_to_el2_prep() handler configures the custom handoff structure such that the target jump address of the TFA BL31 on exit is set to the armv8_switch_to_el2() + 4, which is just past the bl, and just before the U-Boot code which implements the Linux kernel boot from either EL. The registers passed through the TFA BL31 are all the registers passed into armv8_switch_to_el2_prep() to assure maximum compatibility with all the boot modes. The armv8_switch_to_el2_prep() handler jumps to the TFA BL31, which does its setup, drops EL from EL3 to EL2 and finally jumps to the armv8_switch_to_el2() + 4 entry point, which then allows U-Boot to boot the Linux kernel the usual way.
In order to build suitable kernel fitImage, build TFA first, temporarily from downstream repository: remote: https://github.com/renesas-rcar/arm-trusted-firmware.git branch: rcar_gen4_v2.7_v4x
``` $ git clean -fqdx $ MBEDTLS_DIR=/path/to/mbedtls/ make -j$(nproc) bl31 \ PLAT=rcar_gen4 ARCH=aarch64 LSI=V4H SPD=none \ CTX_INCLUDE_AARCH32_REGS=0 MBEDTLS_COMMON_MK=1 \ PTP_NONSECURE_ACCESS=1 LOG_LEVEL=20 DEBUG=0 \ ENABLE_ASSERTIONS=0 E=0 ```
Build Linux kernel Image and device tree from current mainline Linux kernel repository, obtain 'Image' and 'r8a779g0-white-hawk.dtb' .
Bundle the files together using provided fit-image.its fitImage description: ``` $ mkimage -f fit-image.its fitImage ```
To start the kernel fiImage generated in previous step, load fitImage to DRAM and use the 'bootm' command to start it: => load 0x58000000 ... fitImage && bootm 0x58000000
Signed-off-by: Marek Vasut marek.vasut+renesas@mailbox.org --- Cc: Andre Przywara andre.przywara@arm.com Cc: Caleb Connolly caleb.connolly@linaro.org Cc: Igor Opaniuk igor.opaniuk@gmail.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Julien Masson jmasson@baylibre.com Cc: Mattijs Korpershoek mkorpershoek@baylibre.com Cc: Maxim Moskalets maximmosk4@gmail.com Cc: Michael Walle mwalle@kernel.org Cc: Nobuhiro Iwamatsu iwamatsu@nigauri.org Cc: Patrick Rudolph patrick.rudolph@9elements.com Cc: Paul Barker paul.barker.ct@bp.renesas.com Cc: Paul-Erwan Rio paulerwan.rio@gmail.com Cc: Peter Hoyes Peter.Hoyes@arm.com Cc: Raymond Mao raymond.mao@linaro.org Cc: Sam Protsenko semen.protsenko@linaro.org Cc: Simon Glass sjg@chromium.org Cc: Sughosh Ganu sughosh.ganu@linaro.org Cc: Tom Rini trini@konsulko.com Cc: u-boot@lists.denx.de --- board/renesas/common/gen4-common.c | 118 +++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+)
diff --git a/board/renesas/common/gen4-common.c b/board/renesas/common/gen4-common.c index 52a0639073b..c7f3f9a30ab 100644 --- a/board/renesas/common/gen4-common.c +++ b/board/renesas/common/gen4-common.c @@ -7,11 +7,13 @@
#include <asm/arch/renesas.h> #include <asm/arch/sys_proto.h> +#include <asm/armv8/mmu.h> #include <asm/global_data.h> #include <asm/io.h> #include <asm/mach-types.h> #include <asm/processor.h> #include <asm/system.h> +#include <image.h> #include <linux/errno.h>
#define RST_BASE 0xE6160000 /* Domain0 */ @@ -88,3 +90,119 @@ int ft_board_setup(void *blob, struct bd_info *bd) { return 0; } + +/* R-Car Gen4 TFA BL31 handoff structure and handling. */ +struct param_header { + u8 type; + u8 version; + u16 size; + u32 attr; +}; + +struct tfa_image_info { + struct param_header h; + uintptr_t image_base; + u32 image_size; + u32 image_max_size; +}; + +struct aapcs64_params { + u64 arg0; + u64 arg1; + u64 arg2; + u64 arg3; + u64 arg4; + u64 arg5; + u64 arg6; + u64 arg7; +}; + +struct entry_point_info { + struct param_header h; + uintptr_t pc; + u32 spsr; + struct aapcs64_params args; +}; + +struct bl2_to_bl31_params_mem { + struct tfa_image_info bl32_image_info; + struct tfa_image_info bl33_image_info; + struct entry_point_info bl33_ep_info; + struct entry_point_info bl32_ep_info; +}; + +/* Default jump address, return to U-Boot */ +#define BL33_BASE 0x44100000 +/* Custom parameters address passed to TFA by ICUMXA loader */ +#define PARAMS_BASE 0x46422200 + +/* Usually such a structure is produced by ICUMXA and passed in at 0x46422200 */ +static const struct bl2_to_bl31_params_mem blinfo_template = { + .bl33_ep_info.h.type = 1, /* PARAM_EP */ + .bl33_ep_info.h.version = 2, /* Version 2 */ + .bl33_ep_info.h.size = sizeof(struct entry_point_info), + .bl33_ep_info.h.attr = 0x81, /* Executable | Non-Secure */ + .bl33_ep_info.spsr = 0x2c9, /* Mode=EL2, SP=ELX, Exceptions=OFF */ + .bl33_ep_info.pc = BL33_BASE, + + .bl33_image_info.h.type = 1, /* PARAM_EP */ + .bl33_image_info.h.version = 2, /* Version 2 */ + .bl33_image_info.h.size = sizeof(struct image_info), + .bl33_image_info.h.attr = 0, + .bl33_image_info.image_base = BL33_BASE, +}; + +static bool tfa_bl31_image_loaded; +static ulong tfa_bl31_image_addr; + +static void tfa_bl31_image_process(ulong image, size_t size) +{ + /* Custom parameters address passed to TFA by ICUMXA loader */ + struct bl2_to_bl31_params_mem *blinfo = (struct bl2_to_bl31_params_mem *)PARAMS_BASE; + + /* Clear a page and copy template */ + memset((void *)PARAMS_BASE, 0, PAGE_SIZE); + memcpy(blinfo, &blinfo_template, sizeof(*blinfo)); + tfa_bl31_image_addr = image; + tfa_bl31_image_loaded = true; +} + +U_BOOT_FIT_LOADABLE_HANDLER(IH_TYPE_TFA_BL31, tfa_bl31_image_process); + +void armv8_switch_to_el2_prep(u64 args, u64 mach_nr, u64 fdt_addr, + u64 arg4, u64 entry_point, u64 es_flag) +{ + typedef void __noreturn (*image_entry_noargs_t)(void); + image_entry_noargs_t image_entry = + (image_entry_noargs_t)(void *)tfa_bl31_image_addr; + struct bl2_to_bl31_params_mem *blinfo = + (struct bl2_to_bl31_params_mem *)PARAMS_BASE; + + /* + * Destination address in arch/arm/cpu/armv8/transition.S + * right past the first bl in armv8_switch_to_el2() to let + * the rest of U-Boot pre-Linux code run. The code does run + * without stack pointer! + */ + const u64 ep = ((u64)(uintptr_t)&armv8_switch_to_el2) + 4; + + /* If TFA BL31 was not part of the fitImage, do regular boot. */ + if (!tfa_bl31_image_loaded) + return; + + /* + * Set up kernel entry point and parameters: + * x0 is FDT address, x1..x3 must be 0 + */ + blinfo->bl33_ep_info.pc = ep; + blinfo->bl33_ep_info.args.arg0 = args; + blinfo->bl33_ep_info.args.arg1 = mach_nr; + blinfo->bl33_ep_info.args.arg2 = fdt_addr; + blinfo->bl33_ep_info.args.arg3 = arg4; + blinfo->bl33_ep_info.args.arg4 = entry_point; + blinfo->bl33_ep_info.args.arg5 = es_flag; + blinfo->bl33_image_info.image_base = ep; + + /* Jump to TFA BL31 */ + image_entry(); +}

On Sun, Jan 12, 2025 at 11:36:57PM +0100, Marek Vasut wrote:
Add empty weak assembler function armv8_switch_to_el2_prep() which is jumped to just before U-Boot determines which EL it is running in and decides which path to take to boot the Linux kernel.
This weak function is meant to be used by architecture specific code to implement jump to a firmware blob, which then returns right past this weak function and continues execution of U-Boot code which then boots the Linux kernel. One example of such use case is when U-Boot jump tp TFA BL31, which switches from EL3 to EL2 and then returns to U-Boot code newly running in EL2 and starts the Linux kernel.
The weak function is called with caches already disabled and DM shut down. Any preparatory work or even loading of more data must be done in board_prep_linux(), this hook is meant only for the final jump to the firmware and return to U-Boot before booting Linux.
Signed-off-by: Marek Vasut marek.vasut+renesas@mailbox.org
Reviewed-by: Tom Rini trini@konsulko.com
participants (5)
-
Biju Das
-
Marek Vasut
-
Marek Vasut
-
Quentin Schulz
-
Tom Rini