Falcon Mode documentation only covers SPL entering the kernel itself, which
is not what happens on ARM64 SoCs needing ARM Trusted Firmware resident to
provide firmware services: SPL loads a FIT holding BL31 as its firmware
image and the kernel as a loadable, and BL31 enters the kernel as BL33.

Describe that flow, what the kernel image may be given that neither
booti_setup() nor bootz_setup() runs, how it differs from the classic flow,
and how to configure and build it on Rockchip.

Signed-off-by: Alexey Charkov <[email protected]>
---
 doc/board/rockchip/rockchip.rst |   9 +++
 doc/develop/falcon.rst          | 131 +++++++++++++++++++++++++++++++++++++++-
 2 files changed, 138 insertions(+), 2 deletions(-)

diff --git a/doc/board/rockchip/rockchip.rst b/doc/board/rockchip/rockchip.rst
index de2df3046e28..42c47c51036f 100644
--- a/doc/board/rockchip/rockchip.rst
+++ b/doc/board/rockchip/rockchip.rst
@@ -325,6 +325,15 @@ To build rk3588 boards:
         make evb-rk3588_defconfig
         make CROSS_COMPILE=aarch64-linux-gnu-
 
+Falcon mode
+"""""""""""
+
+ARM64 Rockchip SoCs can also boot Linux straight from SPL through TF-A,
+without U-Boot proper running at all. Enabling CONFIG_ROCKCHIP_FALCON_IMAGE
+makes binman build a ``u-boot-rockchip-falcon.itb`` holding TF-A, the kernel
+and an optional initramfs, which are passed in with LINUX_KERNEL and
+LINUX_INITRD. See :doc:`../../develop/falcon` for the details.
+
 Flashing
 --------
 
diff --git a/doc/develop/falcon.rst b/doc/develop/falcon.rst
index 3fc23f65c458..f29aaee06a10 100644
--- a/doc/develop/falcon.rst
+++ b/doc/develop/falcon.rst
@@ -95,11 +95,138 @@ Function that a board must implement
 ------------------------------------
 
 void spl_board_prepare_for_linux(void)
-    optional, called from SPL before starting the kernel
+    optional, called from SPL before starting the kernel. Not called when the
+    kernel is entered through TF-A, see `Falcon Mode with TF-A`_; use
+    spl_board_prepare_for_boot() instead, which is called on every path.
 
 spl_start_uboot()
     required, returns "0" if SPL should start the kernel, "1" if U-Boot
-    must be started.
+    must be started. It may be called from more than one place in a single
+    SPL run, so prefer spl_falcon_boot() when querying the result - that
+    caches the answer, which matters for implementations that are not
+    idempotent, such as those sampling a button.
+
+Falcon Mode with TF-A
+---------------------
+
+On ARM64 SoCs which need ARM Trusted Firmware resident to provide firmware
+services to the OS, the kernel cannot usefully be entered directly from SPL.
+Instead SPL loads a FIT holding BL31 as its ``firmware`` image and the kernel
+as a ``loadable``, hands control to BL31, and BL31 enters the kernel as BL33.
+
+SPL picks BL33 in spl_invoke_atf(), which looks for an image with
+``os = "linux"`` in the ``/fit-images`` node it appended to the device tree
+and falls back to ``os = "u-boot"``. The kernel is entered at EL2 with the
+device tree address in x0, as the arm64 boot protocol requires.
+
+Nothing generates a FIT holding both an OS and a U-Boot payload, so the two
+are separate images, and it is the loader which decides where to read from.
+In Falcon mode it tries the OS image first and falls back to the U-Boot one
+if that cannot be loaded, unless secure Falcon mode is in effect, in which
+case there is no fallback. For UFS the two are addressed by
+CONFIG_SPL_UFS_RAW_OS_DEVNUM and CONFIG_SPL_UFS_RAW_OS_SECTOR against
+CONFIG_SPL_UFS_RAW_U_BOOT_DEVNUM and CONFIG_SPL_UFS_RAW_U_BOOT_SECTOR, so
+they can live on different logical units, at different offsets, or both.
+spl_invoke_atf() then hands over to whichever of the two actually loaded,
+which is what makes that fallback work.
+
+Pointing both at one location is also possible, and is what the defaults do:
+only one region then has to be reserved, at the cost of having nothing left
+to fall back to.
+
+What the kernel image may be
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The payload is an ordinary FIT image node with ``type = "kernel"`` and
+``os = "linux"``. SPL copies it to its ``load`` address, and BL31 enters it
+at ``entry``, or at ``load`` if no entry point is given.
+
+Neither booti_setup() nor bootz_setup() runs in this flow - SPL does not
+enter the kernel itself, it only places it in memory for BL31 - so there is
+no inspection of the arm64 Image header and no relocation to the text offset
+the kernel asks for. The addresses in the FIT have to be directly usable,
+which for an arm64 ``Image`` means 2 MiB aligned, and clear of the device
+tree, of the initramfs and of every region BL31 and OP-TEE are linked to run
+from - each of those is split into several regions when it is supplied as an
+ELF, rather than occupying one contiguous range.
+
+The image is loaded verbatim unless CONFIG_SPL_GZIP or CONFIG_SPL_LZMA is
+enabled, in which case the FIT ``compression`` property may be set to
+``gzip`` or ``lzma`` and SPL decompresses while loading. Two limits apply
+then. The uncompressed image has to fit in CONFIG_SYS_BOOTM_LEN. And the
+compressed one is first staged at CONFIG_SYS_LOAD_ADDR, so that address has
+to clear both the region being decompressed to and, when the FIT is itself
+already in memory, the FIT: booting from RAM reads the image out of DRAM at
+CONFIG_SPL_LOAD_FIT_ADDRESS, and a staging buffer landing inside it makes
+the copy overwrite its own source, which shows up as::
+
+    ## Checking hash(es) for Image kernel ... sha256 error!
+
+The device trees in the FIT
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Each device tree in the FIT can be pre-patched at build time with a
+``/chosen`` node holding the kernel command line and the initramfs location,
+so that nothing has to fix them up before boot. See the ``fit,bootargs`` and
+``fit,initrd`` properties of binman's :ref:`etype_fit`.
+
+Differences from the classic flow
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* The device tree comes from the same FIT as the kernel, so no separate
+  'args' file is involved and CONFIG_SPL_OS_BOOT_ARGS is not needed.
+
+* CONFIG_SPL_BOOTI provides an entry path which is unused here.
+
+* Because U-Boot proper is never loaded in this flow, SPL panics rather than
+  jumping to CONFIG_TEXT_BASE if neither a kernel nor a U-Boot image can be
+  resolved from the FIT.
+
+Neither of the first two defaults to y when CONFIG_SPL_ATF is enabled, so
+nothing has to be turned off by hand.
+
+Example: Rockchip RK3576
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Set the following in the board defconfig::
+
+        CONFIG_SPL_OS_BOOT=y
+        CONFIG_ROCKCHIP_FALCON_IMAGE=y
+
+On a board whose SPL can load from SPI flash, CONFIG_SYS_SPI_KERNEL_OFFS
+becomes visible and has no default, so give it a value suiting the flash
+layout even if the kernel is loaded from elsewhere. The load addresses of
+the kernel, device tree and initramfs default to offsets from the start of
+DRAM which clear BL31 and OP-TEE, and can be adjusted with
+CONFIG_ROCKCHIP_FALCON_KERNEL_LOAD and friends.
+
+Enabling CONFIG_SPL_LZMA or CONFIG_SPL_GZIP additionally compresses the
+kernel in the image, trading some decompression time for a smaller one - for
+a 30 MiB arm64 Image, roughly 35% of the original with LZMA and 39% with
+gzip. Note that this compresses U-Boot itself as well, since both use the
+same setting.
+
+The kernel and, optionally, an initramfs are passed in at build time::
+
+        $ make BL31=.../bl31.elf \
+               ROCKCHIP_TPL=.../rk3576_ddr_lp4_2112MHz_lp5_2736MHz_v1.13.bin \
+               LINUX_KERNEL=.../Image \
+               LINUX_INITRD=.../ramdisk.cpio.zst
+
+This produces ``u-boot-rockchip-falcon.itb``, to be written at
+CONFIG_SPL_UFS_RAW_OS_SECTOR (or the equivalent for the boot medium in use).
+Its default shares the region with U-Boot, which suits Falcon-only boot;
+point it elsewhere to keep a U-Boot image to fall back to.
+
+With CONFIG_ROCKCHIP_MASKROM_IMAGE enabled, an equivalent maskrom payload
+``u-boot-rockchip-usb472-falcon.bin`` is built as well, which can be loaded
+straight into RAM::
+
+        rockusb download-sram u-boot-rockchip-usb471.bin
+        rockusb download-ddr u-boot-rockchip-usb472-falcon.bin
+
+Rockchip attempts Falcon Mode boot whenever it is enabled. Boards which need
+a runtime choice override board_spl_start_uboot().
 
 Environment variables
 ---------------------

-- 
2.54.0

Reply via email to