You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2753 lines
119 KiB
2753 lines
119 KiB
Trusted Firmware-A design
|
|
=========================
|
|
|
|
|
|
.. section-numbering::
|
|
:suffix: .
|
|
|
|
.. contents::
|
|
|
|
Trusted Firmware-A (TF-A) implements a subset of the Trusted Board Boot
|
|
Requirements (TBBR) Platform Design Document (PDD) [1]_ for Arm reference
|
|
platforms. The TBB sequence starts when the platform is powered on and runs up
|
|
to the stage where it hands-off control to firmware running in the normal
|
|
world in DRAM. This is the cold boot path.
|
|
|
|
TF-A also implements the Power State Coordination Interface PDD [2]_ as a
|
|
runtime service. PSCI is the interface from normal world software to firmware
|
|
implementing power management use-cases (for example, secondary CPU boot,
|
|
hotplug and idle). Normal world software can access TF-A runtime services via
|
|
the Arm SMC (Secure Monitor Call) instruction. The SMC instruction must be
|
|
used as mandated by the SMC Calling Convention [3]_.
|
|
|
|
TF-A implements a framework for configuring and managing interrupts generated
|
|
in either security state. The details of the interrupt management framework
|
|
and its design can be found in TF-A Interrupt Management Design guide [4]_.
|
|
|
|
TF-A also implements a library for setting up and managing the translation
|
|
tables. The details of this library can be found in `Xlat_tables design`_.
|
|
|
|
TF-A can be built to support either AArch64 or AArch32 execution state.
|
|
|
|
Cold boot
|
|
---------
|
|
|
|
The cold boot path starts when the platform is physically turned on. If
|
|
``COLD_BOOT_SINGLE_CPU=0``, one of the CPUs released from reset is chosen as the
|
|
primary CPU, and the remaining CPUs are considered secondary CPUs. The primary
|
|
CPU is chosen through platform-specific means. The cold boot path is mainly
|
|
executed by the primary CPU, other than essential CPU initialization executed by
|
|
all CPUs. The secondary CPUs are kept in a safe platform-specific state until
|
|
the primary CPU has performed enough initialization to boot them.
|
|
|
|
Refer to the `Reset Design`_ for more information on the effect of the
|
|
``COLD_BOOT_SINGLE_CPU`` platform build option.
|
|
|
|
The cold boot path in this implementation of TF-A depends on the execution
|
|
state. For AArch64, it is divided into five steps (in order of execution):
|
|
|
|
- Boot Loader stage 1 (BL1) *AP Trusted ROM*
|
|
- Boot Loader stage 2 (BL2) *Trusted Boot Firmware*
|
|
- Boot Loader stage 3-1 (BL31) *EL3 Runtime Software*
|
|
- Boot Loader stage 3-2 (BL32) *Secure-EL1 Payload* (optional)
|
|
- Boot Loader stage 3-3 (BL33) *Non-trusted Firmware*
|
|
|
|
For AArch32, it is divided into four steps (in order of execution):
|
|
|
|
- Boot Loader stage 1 (BL1) *AP Trusted ROM*
|
|
- Boot Loader stage 2 (BL2) *Trusted Boot Firmware*
|
|
- Boot Loader stage 3-2 (BL32) *EL3 Runtime Software*
|
|
- Boot Loader stage 3-3 (BL33) *Non-trusted Firmware*
|
|
|
|
Arm development platforms (Fixed Virtual Platforms (FVPs) and Juno) implement a
|
|
combination of the following types of memory regions. Each bootloader stage uses
|
|
one or more of these memory regions.
|
|
|
|
- Regions accessible from both non-secure and secure states. For example,
|
|
non-trusted SRAM, ROM and DRAM.
|
|
- Regions accessible from only the secure state. For example, trusted SRAM and
|
|
ROM. The FVPs also implement the trusted DRAM which is statically
|
|
configured. Additionally, the Base FVPs and Juno development platform
|
|
configure the TrustZone Controller (TZC) to create a region in the DRAM
|
|
which is accessible only from the secure state.
|
|
|
|
The sections below provide the following details:
|
|
|
|
- dynamic configuration of Boot Loader stages
|
|
- initialization and execution of the first three stages during cold boot
|
|
- specification of the EL3 Runtime Software (BL31 for AArch64 and BL32 for
|
|
AArch32) entrypoint requirements for use by alternative Trusted Boot
|
|
Firmware in place of the provided BL1 and BL2
|
|
|
|
Dynamic Configuration during cold boot
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Each of the Boot Loader stages may be dynamically configured if required by the
|
|
platform. The Boot Loader stage may optionally specify a firmware
|
|
configuration file and/or hardware configuration file as listed below:
|
|
|
|
- HW_CONFIG - The hardware configuration file. Can be shared by all Boot Loader
|
|
stages and also by the Normal World Rich OS.
|
|
- TB_FW_CONFIG - Trusted Boot Firmware configuration file. Shared between BL1
|
|
and BL2.
|
|
- SOC_FW_CONFIG - SoC Firmware configuration file. Used by BL31.
|
|
- TOS_FW_CONFIG - Trusted OS Firmware configuration file. Used by Trusted OS
|
|
(BL32).
|
|
- NT_FW_CONFIG - Non Trusted Firmware configuration file. Used by Non-trusted
|
|
firmware (BL33).
|
|
|
|
The Arm development platforms use the Flattened Device Tree format for the
|
|
dynamic configuration files.
|
|
|
|
Each Boot Loader stage can pass up to 4 arguments via registers to the next
|
|
stage. BL2 passes the list of the next images to execute to the *EL3 Runtime
|
|
Software* (BL31 for AArch64 and BL32 for AArch32) via `arg0`. All the other
|
|
arguments are platform defined. The Arm development platforms use the following
|
|
convention:
|
|
|
|
- BL1 passes the address of a meminfo_t structure to BL2 via ``arg1``. This
|
|
structure contains the memory layout available to BL2.
|
|
- When dynamic configuration files are present, the firmware configuration for
|
|
the next Boot Loader stage is populated in the first available argument and
|
|
the generic hardware configuration is passed the next available argument.
|
|
For example,
|
|
|
|
- If TB_FW_CONFIG is loaded by BL1, then its address is passed in ``arg0``
|
|
to BL2.
|
|
- If HW_CONFIG is loaded by BL1, then its address is passed in ``arg2`` to
|
|
BL2. Note, ``arg1`` is already used for meminfo_t.
|
|
- If SOC_FW_CONFIG is loaded by BL2, then its address is passed in ``arg1``
|
|
to BL31. Note, ``arg0`` is used to pass the list of executable images.
|
|
- Similarly, if HW_CONFIG is loaded by BL1 or BL2, then its address is
|
|
passed in ``arg2`` to BL31.
|
|
- For other BL3x images, if the firmware configuration file is loaded by
|
|
BL2, then its address is passed in ``arg0`` and if HW_CONFIG is loaded
|
|
then its address is passed in ``arg1``.
|
|
|
|
BL1
|
|
~~~
|
|
|
|
This stage begins execution from the platform's reset vector at EL3. The reset
|
|
address is platform dependent but it is usually located in a Trusted ROM area.
|
|
The BL1 data section is copied to trusted SRAM at runtime.
|
|
|
|
On the Arm development platforms, BL1 code starts execution from the reset
|
|
vector defined by the constant ``BL1_RO_BASE``. The BL1 data section is copied
|
|
to the top of trusted SRAM as defined by the constant ``BL1_RW_BASE``.
|
|
|
|
The functionality implemented by this stage is as follows.
|
|
|
|
Determination of boot path
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Whenever a CPU is released from reset, BL1 needs to distinguish between a warm
|
|
boot and a cold boot. This is done using platform-specific mechanisms (see the
|
|
``plat_get_my_entrypoint()`` function in the `Porting Guide`_). In the case of a
|
|
warm boot, a CPU is expected to continue execution from a separate
|
|
entrypoint. In the case of a cold boot, the secondary CPUs are placed in a safe
|
|
platform-specific state (see the ``plat_secondary_cold_boot_setup()`` function in
|
|
the `Porting Guide`_) while the primary CPU executes the remaining cold boot path
|
|
as described in the following sections.
|
|
|
|
This step only applies when ``PROGRAMMABLE_RESET_ADDRESS=0``. Refer to the
|
|
`Reset Design`_ for more information on the effect of the
|
|
``PROGRAMMABLE_RESET_ADDRESS`` platform build option.
|
|
|
|
Architectural initialization
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
BL1 performs minimal architectural initialization as follows.
|
|
|
|
- Exception vectors
|
|
|
|
BL1 sets up simple exception vectors for both synchronous and asynchronous
|
|
exceptions. The default behavior upon receiving an exception is to populate
|
|
a status code in the general purpose register ``X0/R0`` and call the
|
|
``plat_report_exception()`` function (see the `Porting Guide`_). The status
|
|
code is one of:
|
|
|
|
For AArch64:
|
|
|
|
::
|
|
|
|
0x0 : Synchronous exception from Current EL with SP_EL0
|
|
0x1 : IRQ exception from Current EL with SP_EL0
|
|
0x2 : FIQ exception from Current EL with SP_EL0
|
|
0x3 : System Error exception from Current EL with SP_EL0
|
|
0x4 : Synchronous exception from Current EL with SP_ELx
|
|
0x5 : IRQ exception from Current EL with SP_ELx
|
|
0x6 : FIQ exception from Current EL with SP_ELx
|
|
0x7 : System Error exception from Current EL with SP_ELx
|
|
0x8 : Synchronous exception from Lower EL using aarch64
|
|
0x9 : IRQ exception from Lower EL using aarch64
|
|
0xa : FIQ exception from Lower EL using aarch64
|
|
0xb : System Error exception from Lower EL using aarch64
|
|
0xc : Synchronous exception from Lower EL using aarch32
|
|
0xd : IRQ exception from Lower EL using aarch32
|
|
0xe : FIQ exception from Lower EL using aarch32
|
|
0xf : System Error exception from Lower EL using aarch32
|
|
|
|
For AArch32:
|
|
|
|
::
|
|
|
|
0x10 : User mode
|
|
0x11 : FIQ mode
|
|
0x12 : IRQ mode
|
|
0x13 : SVC mode
|
|
0x16 : Monitor mode
|
|
0x17 : Abort mode
|
|
0x1a : Hypervisor mode
|
|
0x1b : Undefined mode
|
|
0x1f : System mode
|
|
|
|
The ``plat_report_exception()`` implementation on the Arm FVP port programs
|
|
the Versatile Express System LED register in the following format to
|
|
indicate the occurence of an unexpected exception:
|
|
|
|
::
|
|
|
|
SYS_LED[0] - Security state (Secure=0/Non-Secure=1)
|
|
SYS_LED[2:1] - Exception Level (EL3=0x3, EL2=0x2, EL1=0x1, EL0=0x0)
|
|
For AArch32 it is always 0x0
|
|
SYS_LED[7:3] - Exception Class (Sync/Async & origin). This is the value
|
|
of the status code
|
|
|
|
A write to the LED register reflects in the System LEDs (S6LED0..7) in the
|
|
CLCD window of the FVP.
|
|
|
|
BL1 does not expect to receive any exceptions other than the SMC exception.
|
|
For the latter, BL1 installs a simple stub. The stub expects to receive a
|
|
limited set of SMC types (determined by their function IDs in the general
|
|
purpose register ``X0/R0``):
|
|
|
|
- ``BL1_SMC_RUN_IMAGE``: This SMC is raised by BL2 to make BL1 pass control
|
|
to EL3 Runtime Software.
|
|
- All SMCs listed in section "BL1 SMC Interface" in the `Firmware Update`_
|
|
Design Guide are supported for AArch64 only. These SMCs are currently
|
|
not supported when BL1 is built for AArch32.
|
|
|
|
Any other SMC leads to an assertion failure.
|
|
|
|
- CPU initialization
|
|
|
|
BL1 calls the ``reset_handler()`` function which in turn calls the CPU
|
|
specific reset handler function (see the section: "CPU specific operations
|
|
framework").
|
|
|
|
- Control register setup (for AArch64)
|
|
|
|
- ``SCTLR_EL3``. Instruction cache is enabled by setting the ``SCTLR_EL3.I``
|
|
bit. Alignment and stack alignment checking is enabled by setting the
|
|
``SCTLR_EL3.A`` and ``SCTLR_EL3.SA`` bits. Exception endianness is set to
|
|
little-endian by clearing the ``SCTLR_EL3.EE`` bit.
|
|
|
|
- ``SCR_EL3``. The register width of the next lower exception level is set
|
|
to AArch64 by setting the ``SCR.RW`` bit. The ``SCR.EA`` bit is set to trap
|
|
both External Aborts and SError Interrupts in EL3. The ``SCR.SIF`` bit is
|
|
also set to disable instruction fetches from Non-secure memory when in
|
|
secure state.
|
|
|
|
- ``CPTR_EL3``. Accesses to the ``CPACR_EL1`` register from EL1 or EL2, or the
|
|
``CPTR_EL2`` register from EL2 are configured to not trap to EL3 by
|
|
clearing the ``CPTR_EL3.TCPAC`` bit. Access to the trace functionality is
|
|
configured not to trap to EL3 by clearing the ``CPTR_EL3.TTA`` bit.
|
|
Instructions that access the registers associated with Floating Point
|
|
and Advanced SIMD execution are configured to not trap to EL3 by
|
|
clearing the ``CPTR_EL3.TFP`` bit.
|
|
|
|
- ``DAIF``. The SError interrupt is enabled by clearing the SError interrupt
|
|
mask bit.
|
|
|
|
- ``MDCR_EL3``. The trap controls, ``MDCR_EL3.TDOSA``, ``MDCR_EL3.TDA`` and
|
|
``MDCR_EL3.TPM``, are set so that accesses to the registers they control
|
|
do not trap to EL3. AArch64 Secure self-hosted debug is disabled by
|
|
setting the ``MDCR_EL3.SDD`` bit. Also ``MDCR_EL3.SPD32`` is set to
|
|
disable AArch32 Secure self-hosted privileged debug from S-EL1.
|
|
|
|
- Control register setup (for AArch32)
|
|
|
|
- ``SCTLR``. Instruction cache is enabled by setting the ``SCTLR.I`` bit.
|
|
Alignment checking is enabled by setting the ``SCTLR.A`` bit.
|
|
Exception endianness is set to little-endian by clearing the
|
|
``SCTLR.EE`` bit.
|
|
|
|
- ``SCR``. The ``SCR.SIF`` bit is set to disable instruction fetches from
|
|
Non-secure memory when in secure state.
|
|
|
|
- ``CPACR``. Allow execution of Advanced SIMD instructions at PL0 and PL1,
|
|
by clearing the ``CPACR.ASEDIS`` bit. Access to the trace functionality
|
|
is configured not to trap to undefined mode by clearing the
|
|
``CPACR.TRCDIS`` bit.
|
|
|
|
- ``NSACR``. Enable non-secure access to Advanced SIMD functionality and
|
|
system register access to implemented trace registers.
|
|
|
|
- ``FPEXC``. Enable access to the Advanced SIMD and floating-point
|
|
functionality from all Exception levels.
|
|
|
|
- ``CPSR.A``. The Asynchronous data abort interrupt is enabled by clearing
|
|
the Asynchronous data abort interrupt mask bit.
|
|
|
|
- ``SDCR``. The ``SDCR.SPD`` field is set to disable AArch32 Secure
|
|
self-hosted privileged debug.
|
|
|
|
Platform initialization
|
|
^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
On Arm platforms, BL1 performs the following platform initializations:
|
|
|
|
- Enable the Trusted Watchdog.
|
|
- Initialize the console.
|
|
- Configure the Interconnect to enable hardware coherency.
|
|
- Enable the MMU and map the memory it needs to access.
|
|
- Configure any required platform storage to load the next bootloader image
|
|
(BL2).
|
|
- If the BL1 dynamic configuration file, ``TB_FW_CONFIG``, is available, then
|
|
load it to the platform defined address and make it available to BL2 via
|
|
``arg0``.
|
|
|
|
Firmware Update detection and execution
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
After performing platform setup, BL1 common code calls
|
|
``bl1_plat_get_next_image_id()`` to determine if `Firmware Update`_ is required or
|
|
to proceed with the normal boot process. If the platform code returns
|
|
``BL2_IMAGE_ID`` then the normal boot sequence is executed as described in the
|
|
next section, else BL1 assumes that `Firmware Update`_ is required and execution
|
|
passes to the first image in the `Firmware Update`_ process. In either case, BL1
|
|
retrieves a descriptor of the next image by calling ``bl1_plat_get_image_desc()``.
|
|
The image descriptor contains an ``entry_point_info_t`` structure, which BL1
|
|
uses to initialize the execution state of the next image.
|
|
|
|
BL2 image load and execution
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
In the normal boot flow, BL1 execution continues as follows:
|
|
|
|
#. BL1 prints the following string from the primary CPU to indicate successful
|
|
execution of the BL1 stage:
|
|
|
|
::
|
|
|
|
"Booting Trusted Firmware"
|
|
|
|
#. BL1 loads a BL2 raw binary image from platform storage, at a
|
|
platform-specific base address. Prior to the load, BL1 invokes
|
|
``bl1_plat_handle_pre_image_load()`` which allows the platform to update or
|
|
use the image information. If the BL2 image file is not present or if
|
|
there is not enough free trusted SRAM the following error message is
|
|
printed:
|
|
|
|
::
|
|
|
|
"Failed to load BL2 firmware."
|
|
|
|
#. BL1 invokes ``bl1_plat_handle_post_image_load()`` which again is intended
|
|
for platforms to take further action after image load. This function must
|
|
populate the necessary arguments for BL2, which may also include the memory
|
|
layout. Further description of the memory layout can be found later
|
|
in this document.
|
|
|
|
#. BL1 passes control to the BL2 image at Secure EL1 (for AArch64) or at
|
|
Secure SVC mode (for AArch32), starting from its load address.
|
|
|
|
BL2
|
|
~~~
|
|
|
|
BL1 loads and passes control to BL2 at Secure-EL1 (for AArch64) or at Secure
|
|
SVC mode (for AArch32) . BL2 is linked against and loaded at a platform-specific
|
|
base address (more information can be found later in this document).
|
|
The functionality implemented by BL2 is as follows.
|
|
|
|
Architectural initialization
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
For AArch64, BL2 performs the minimal architectural initialization required
|
|
for subsequent stages of TF-A and normal world software. EL1 and EL0 are given
|
|
access to Floating Point and Advanced SIMD registers by clearing the
|
|
``CPACR.FPEN`` bits.
|
|
|
|
For AArch32, the minimal architectural initialization required for subsequent
|
|
stages of TF-A and normal world software is taken care of in BL1 as both BL1
|
|
and BL2 execute at PL1.
|
|
|
|
Platform initialization
|
|
^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
On Arm platforms, BL2 performs the following platform initializations:
|
|
|
|
- Initialize the console.
|
|
- Configure any required platform storage to allow loading further bootloader
|
|
images.
|
|
- Enable the MMU and map the memory it needs to access.
|
|
- Perform platform security setup to allow access to controlled components.
|
|
- Reserve some memory for passing information to the next bootloader image
|
|
EL3 Runtime Software and populate it.
|
|
- Define the extents of memory available for loading each subsequent
|
|
bootloader image.
|
|
- If BL1 has passed TB_FW_CONFIG dynamic configuration file in ``arg0``,
|
|
then parse it.
|
|
|
|
Image loading in BL2
|
|
^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Image loading scheme in BL2 depends on ``LOAD_IMAGE_V2`` build option. If the
|
|
flag is disabled, the BLxx images are loaded, by calling the respective
|
|
load\_blxx() function from BL2 generic code. If the flag is enabled, the BL2
|
|
generic code loads the images based on the list of loadable images provided
|
|
by the platform. BL2 passes the list of executable images provided by the
|
|
platform to the next handover BL image. By default, this flag is disabled for
|
|
AArch64 and the AArch32 build is supported only if this flag is enabled.
|
|
|
|
The list of loadable images provided by the platform may also contain
|
|
dynamic configuration files. The files are loaded and can be parsed as
|
|
needed in the ``bl2_plat_handle_post_image_load()`` function. These
|
|
configuration files can be passed to next Boot Loader stages as arguments
|
|
by updating the corresponding entrypoint information in this function.
|
|
|
|
SCP\_BL2 (System Control Processor Firmware) image load
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Some systems have a separate System Control Processor (SCP) for power, clock,
|
|
reset and system control. BL2 loads the optional SCP\_BL2 image from platform
|
|
storage into a platform-specific region of secure memory. The subsequent
|
|
handling of SCP\_BL2 is platform specific. For example, on the Juno Arm
|
|
development platform port the image is transferred into SCP's internal memory
|
|
using the Boot Over MHU (BOM) protocol after being loaded in the trusted SRAM
|
|
memory. The SCP executes SCP\_BL2 and signals to the Application Processor (AP)
|
|
for BL2 execution to continue.
|
|
|
|
EL3 Runtime Software image load
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
BL2 loads the EL3 Runtime Software image from platform storage into a platform-
|
|
specific address in trusted SRAM. If there is not enough memory to load the
|
|
image or image is missing it leads to an assertion failure. If ``LOAD_IMAGE_V2``
|
|
is disabled and if image loads successfully, BL2 updates the amount of trusted
|
|
SRAM used and available for use by EL3 Runtime Software. This information is
|
|
populated at a platform-specific memory address.
|
|
|
|
AArch64 BL32 (Secure-EL1 Payload) image load
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
BL2 loads the optional BL32 image from platform storage into a platform-
|
|
specific region of secure memory. The image executes in the secure world. BL2
|
|
relies on BL31 to pass control to the BL32 image, if present. Hence, BL2
|
|
populates a platform-specific area of memory with the entrypoint/load-address
|
|
of the BL32 image. The value of the Saved Processor Status Register (``SPSR``)
|
|
for entry into BL32 is not determined by BL2, it is initialized by the
|
|
Secure-EL1 Payload Dispatcher (see later) within BL31, which is responsible for
|
|
managing interaction with BL32. This information is passed to BL31.
|
|
|
|
BL33 (Non-trusted Firmware) image load
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
BL2 loads the BL33 image (e.g. UEFI or other test or boot software) from
|
|
platform storage into non-secure memory as defined by the platform.
|
|
|
|
BL2 relies on EL3 Runtime Software to pass control to BL33 once secure state
|
|
initialization is complete. Hence, BL2 populates a platform-specific area of
|
|
memory with the entrypoint and Saved Program Status Register (``SPSR``) of the
|
|
normal world software image. The entrypoint is the load address of the BL33
|
|
image. The ``SPSR`` is determined as specified in Section 5.13 of the
|
|
`PSCI PDD`_. This information is passed to the EL3 Runtime Software.
|
|
|
|
AArch64 BL31 (EL3 Runtime Software) execution
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
BL2 execution continues as follows:
|
|
|
|
#. BL2 passes control back to BL1 by raising an SMC, providing BL1 with the
|
|
BL31 entrypoint. The exception is handled by the SMC exception handler
|
|
installed by BL1.
|
|
|
|
#. BL1 turns off the MMU and flushes the caches. It clears the
|
|
``SCTLR_EL3.M/I/C`` bits, flushes the data cache to the point of coherency
|
|
and invalidates the TLBs.
|
|
|
|
#. BL1 passes control to BL31 at the specified entrypoint at EL3.
|
|
|
|
Running BL2 at EL3 execution level
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Some platforms have a non-TF-A Boot ROM that expects the next boot stage
|
|
to execute at EL3. On these platforms, TF-A BL1 is a waste of memory
|
|
as its only purpose is to ensure TF-A BL2 is entered at S-EL1. To avoid
|
|
this waste, a special mode enables BL2 to execute at EL3, which allows
|
|
a non-TF-A Boot ROM to load and jump directly to BL2. This mode is selected
|
|
when the build flag BL2_AT_EL3 is enabled. The main differences in this
|
|
mode are:
|
|
|
|
#. BL2 includes the reset code and the mailbox mechanism to differentiate
|
|
cold boot and warm boot. It runs at EL3 doing the arch
|
|
initialization required for EL3.
|
|
|
|
#. BL2 does not receive the meminfo information from BL1 anymore. This
|
|
information can be passed by the Boot ROM or be internal to the
|
|
BL2 image.
|
|
|
|
#. Since BL2 executes at EL3, BL2 jumps directly to the next image,
|
|
instead of invoking the RUN_IMAGE SMC call.
|
|
|
|
|
|
We assume 3 different types of BootROM support on the platform:
|
|
|
|
#. The Boot ROM always jumps to the same address, for both cold
|
|
and warm boot. In this case, we will need to keep a resident part
|
|
of BL2 whose memory cannot be reclaimed by any other image. The
|
|
linker script defines the symbols __TEXT_RESIDENT_START__ and
|
|
__TEXT_RESIDENT_END__ that allows the platform to configure
|
|
correctly the memory map.
|
|
#. The platform has some mechanism to indicate the jump address to the
|
|
Boot ROM. Platform code can then program the jump address with
|
|
psci_warmboot_entrypoint during cold boot.
|
|
#. The platform has some mechanism to program the reset address using
|
|
the PROGRAMMABLE_RESET_ADDRESS feature. Platform code can then
|
|
program the reset address with psci_warmboot_entrypoint during
|
|
cold boot, bypassing the boot ROM for warm boot.
|
|
|
|
In the last 2 cases, no part of BL2 needs to remain resident at
|
|
runtime. In the first 2 cases, we expect the Boot ROM to be able to
|
|
differentiate between warm and cold boot, to avoid loading BL2 again
|
|
during warm boot.
|
|
|
|
This functionality can be tested with FVP loading the image directly
|
|
in memory and changing the address where the system jumps at reset.
|
|
For example:
|
|
|
|
-C cluster0.cpu0.RVBAR=0x4022000
|
|
--data cluster0.cpu0=bl2.bin@0x4022000
|
|
|
|
With this configuration, FVP is like a platform of the first case,
|
|
where the Boot ROM jumps always to the same address. For simplification,
|
|
BL32 is loaded in DRAM in this case, to avoid other images reclaiming
|
|
BL2 memory.
|
|
|
|
|
|
AArch64 BL31
|
|
~~~~~~~~~~~~
|
|
|
|
The image for this stage is loaded by BL2 and BL1 passes control to BL31 at
|
|
EL3. BL31 executes solely in trusted SRAM. BL31 is linked against and
|
|
loaded at a platform-specific base address (more information can be found later
|
|
in this document). The functionality implemented by BL31 is as follows.
|
|
|
|
Architectural initialization
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Currently, BL31 performs a similar architectural initialization to BL1 as
|
|
far as system register settings are concerned. Since BL1 code resides in ROM,
|
|
architectural initialization in BL31 allows override of any previous
|
|
initialization done by BL1.
|
|
|
|
BL31 initializes the per-CPU data framework, which provides a cache of
|
|
frequently accessed per-CPU data optimised for fast, concurrent manipulation
|
|
on different CPUs. This buffer includes pointers to per-CPU contexts, crash
|
|
buffer, CPU reset and power down operations, PSCI data, platform data and so on.
|
|
|
|
It then replaces the exception vectors populated by BL1 with its own. BL31
|
|
exception vectors implement more elaborate support for handling SMCs since this
|
|
is the only mechanism to access the runtime services implemented by BL31 (PSCI
|
|
for example). BL31 checks each SMC for validity as specified by the
|
|
`SMC calling convention PDD`_ before passing control to the required SMC
|
|
handler routine.
|
|
|
|
BL31 programs the ``CNTFRQ_EL0`` register with the clock frequency of the system
|
|
counter, which is provided by the platform.
|
|
|
|
Platform initialization
|
|
^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
BL31 performs detailed platform initialization, which enables normal world
|
|
software to function correctly.
|
|
|
|
On Arm platforms, this consists of the following:
|
|
|
|
- Initialize the console.
|
|
- Configure the Interconnect to enable hardware coherency.
|
|
- Enable the MMU and map the memory it needs to access.
|
|
- Initialize the generic interrupt controller.
|
|
- Initialize the power controller device.
|
|
- Detect the system topology.
|
|
|
|
Runtime services initialization
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
BL31 is responsible for initializing the runtime services. One of them is PSCI.
|
|
|
|
As part of the PSCI initializations, BL31 detects the system topology. It also
|
|
initializes the data structures that implement the state machine used to track
|
|
the state of power domain nodes. The state can be one of ``OFF``, ``RUN`` or
|
|
``RETENTION``. All secondary CPUs are initially in the ``OFF`` state. The cluster
|
|
that the primary CPU belongs to is ``ON``; any other cluster is ``OFF``. It also
|
|
initializes the locks that protect them. BL31 accesses the state of a CPU or
|
|
cluster immediately after reset and before the data cache is enabled in the
|
|
warm boot path. It is not currently possible to use 'exclusive' based spinlocks,
|
|
therefore BL31 uses locks based on Lamport's Bakery algorithm instead.
|
|
|
|
The runtime service framework and its initialization is described in more
|
|
detail in the "EL3 runtime services framework" section below.
|
|
|
|
Details about the status of the PSCI implementation are provided in the
|
|
"Power State Coordination Interface" section below.
|
|
|
|
AArch64 BL32 (Secure-EL1 Payload) image initialization
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
If a BL32 image is present then there must be a matching Secure-EL1 Payload
|
|
Dispatcher (SPD) service (see later for details). During initialization
|
|
that service must register a function to carry out initialization of BL32
|
|
once the runtime services are fully initialized. BL31 invokes such a
|
|
registered function to initialize BL32 before running BL33. This initialization
|
|
is not necessary for AArch32 SPs.
|
|
|
|
Details on BL32 initialization and the SPD's role are described in the
|
|
"Secure-EL1 Payloads and Dispatchers" section below.
|
|
|
|
BL33 (Non-trusted Firmware) execution
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
EL3 Runtime Software initializes the EL2 or EL1 processor context for normal-
|
|
world cold boot, ensuring that no secure state information finds its way into
|
|
the non-secure execution state. EL3 Runtime Software uses the entrypoint
|
|
information provided by BL2 to jump to the Non-trusted firmware image (BL33)
|
|
at the highest available Exception Level (EL2 if available, otherwise EL1).
|
|
|
|
Using alternative Trusted Boot Firmware in place of BL1 & BL2 (AArch64 only)
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Some platforms have existing implementations of Trusted Boot Firmware that
|
|
would like to use TF-A BL31 for the EL3 Runtime Software. To enable this
|
|
firmware architecture it is important to provide a fully documented and stable
|
|
interface between the Trusted Boot Firmware and BL31.
|
|
|
|
Future changes to the BL31 interface will be done in a backwards compatible
|
|
way, and this enables these firmware components to be independently enhanced/
|
|
updated to develop and exploit new functionality.
|
|
|
|
Required CPU state when calling ``bl31_entrypoint()`` during cold boot
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
This function must only be called by the primary CPU.
|
|
|
|
On entry to this function the calling primary CPU must be executing in AArch64
|
|
EL3, little-endian data access, and all interrupt sources masked:
|
|
|
|
::
|
|
|
|
PSTATE.EL = 3
|
|
PSTATE.RW = 1
|
|
PSTATE.DAIF = 0xf
|
|
SCTLR_EL3.EE = 0
|
|
|
|
X0 and X1 can be used to pass information from the Trusted Boot Firmware to the
|
|
platform code in BL31:
|
|
|
|
::
|
|
|
|
X0 : Reserved for common TF-A information
|
|
X1 : Platform specific information
|
|
|
|
BL31 zero-init sections (e.g. ``.bss``) should not contain valid data on entry,
|
|
these will be zero filled prior to invoking platform setup code.
|
|
|
|
Use of the X0 and X1 parameters
|
|
'''''''''''''''''''''''''''''''
|
|
|
|
The parameters are platform specific and passed from ``bl31_entrypoint()`` to
|
|
``bl31_early_platform_setup()``. The value of these parameters is never directly
|
|
used by the common BL31 code.
|
|
|
|
The convention is that ``X0`` conveys information regarding the BL31, BL32 and
|
|
BL33 images from the Trusted Boot firmware and ``X1`` can be used for other
|
|
platform specific purpose. This convention allows platforms which use TF-A's
|
|
BL1 and BL2 images to transfer additional platform specific information from
|
|
Secure Boot without conflicting with future evolution of TF-A using ``X0`` to
|
|
pass a ``bl31_params`` structure.
|
|
|
|
BL31 common and SPD initialization code depends on image and entrypoint
|
|
information about BL33 and BL32, which is provided via BL31 platform APIs.
|
|
This information is required until the start of execution of BL33. This
|
|
information can be provided in a platform defined manner, e.g. compiled into
|
|
the platform code in BL31, or provided in a platform defined memory location
|
|
by the Trusted Boot firmware, or passed from the Trusted Boot Firmware via the
|
|
Cold boot Initialization parameters. This data may need to be cleaned out of
|
|
the CPU caches if it is provided by an earlier boot stage and then accessed by
|
|
BL31 platform code before the caches are enabled.
|
|
|
|
TF-A's BL2 implementation passes a ``bl31_params`` structure in
|
|
``X0`` and the Arm development platforms interpret this in the BL31 platform
|
|
code.
|
|
|
|
MMU, Data caches & Coherency
|
|
''''''''''''''''''''''''''''
|
|
|
|
BL31 does not depend on the enabled state of the MMU, data caches or
|
|
interconnect coherency on entry to ``bl31_entrypoint()``. If these are disabled
|
|
on entry, these should be enabled during ``bl31_plat_arch_setup()``.
|
|
|
|
Data structures used in the BL31 cold boot interface
|
|
''''''''''''''''''''''''''''''''''''''''''''''''''''
|
|
|
|
These structures are designed to support compatibility and independent
|
|
evolution of the structures and the firmware images. For example, a version of
|
|
BL31 that can interpret the BL3x image information from different versions of
|
|
BL2, a platform that uses an extended entry\_point\_info structure to convey
|
|
additional register information to BL31, or a ELF image loader that can convey
|
|
more details about the firmware images.
|
|
|
|
To support these scenarios the structures are versioned and sized, which enables
|
|
BL31 to detect which information is present and respond appropriately. The
|
|
``param_header`` is defined to capture this information:
|
|
|
|
.. code:: c
|
|
|
|
typedef struct param_header {
|
|
uint8_t type; /* type of the structure */
|
|
uint8_t version; /* version of this structure */
|
|
uint16_t size; /* size of this structure in bytes */
|
|
uint32_t attr; /* attributes: unused bits SBZ */
|
|
} param_header_t;
|
|
|
|
The structures using this format are ``entry_point_info``, ``image_info`` and
|
|
``bl31_params``. The code that allocates and populates these structures must set
|
|
the header fields appropriately, and the ``SET_PARAM_HEAD()`` a macro is defined
|
|
to simplify this action.
|
|
|
|
Required CPU state for BL31 Warm boot initialization
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
When requesting a CPU power-on, or suspending a running CPU, TF-A provides
|
|
the platform power management code with a Warm boot initialization
|
|
entry-point, to be invoked by the CPU immediately after the reset handler.
|
|
On entry to the Warm boot initialization function the calling CPU must be in
|
|
AArch64 EL3, little-endian data access and all interrupt sources masked:
|
|
|
|
::
|
|
|
|
PSTATE.EL = 3
|
|
PSTATE.RW = 1
|
|
PSTATE.DAIF = 0xf
|
|
SCTLR_EL3.EE = 0
|
|
|
|
The PSCI implementation will initialize the processor state and ensure that the
|
|
platform power management code is then invoked as required to initialize all
|
|
necessary system, cluster and CPU resources.
|
|
|
|
AArch32 EL3 Runtime Software entrypoint interface
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
To enable this firmware architecture it is important to provide a fully
|
|
documented and stable interface between the Trusted Boot Firmware and the
|
|
AArch32 EL3 Runtime Software.
|
|
|
|
Future changes to the entrypoint interface will be done in a backwards
|
|
compatible way, and this enables these firmware components to be independently
|
|
enhanced/updated to develop and exploit new functionality.
|
|
|
|
Required CPU state when entering during cold boot
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
This function must only be called by the primary CPU.
|
|
|
|
On entry to this function the calling primary CPU must be executing in AArch32
|
|
EL3, little-endian data access, and all interrupt sources masked:
|
|
|
|
::
|
|
|
|
PSTATE.AIF = 0x7
|
|
SCTLR.EE = 0
|
|
|
|
R0 and R1 are used to pass information from the Trusted Boot Firmware to the
|
|
platform code in AArch32 EL3 Runtime Software:
|
|
|
|
::
|
|
|
|
R0 : Reserved for common TF-A information
|
|
R1 : Platform specific information
|
|
|
|
Use of the R0 and R1 parameters
|
|
'''''''''''''''''''''''''''''''
|
|
|
|
The parameters are platform specific and the convention is that ``R0`` conveys
|
|
information regarding the BL3x images from the Trusted Boot firmware and ``R1``
|
|
can be used for other platform specific purpose. This convention allows
|
|
platforms which use TF-A's BL1 and BL2 images to transfer additional platform
|
|
specific information from Secure Boot without conflicting with future
|
|
evolution of TF-A using ``R0`` to pass a ``bl_params`` structure.
|
|
|
|
The AArch32 EL3 Runtime Software is responsible for entry into BL33. This
|
|
information can be obtained in a platform defined manner, e.g. compiled into
|
|
the AArch32 EL3 Runtime Software, or provided in a platform defined memory
|
|
location by the Trusted Boot firmware, or passed from the Trusted Boot Firmware
|
|
via the Cold boot Initialization parameters. This data may need to be cleaned
|
|
out of the CPU caches if it is provided by an earlier boot stage and then
|
|
accessed by AArch32 EL3 Runtime Software before the caches are enabled.
|
|
|
|
When using AArch32 EL3 Runtime Software, the Arm development platforms pass a
|
|
``bl_params`` structure in ``R0`` from BL2 to be interpreted by AArch32 EL3 Runtime
|
|
Software platform code.
|
|
|
|
MMU, Data caches & Coherency
|
|
''''''''''''''''''''''''''''
|
|
|
|
AArch32 EL3 Runtime Software must not depend on the enabled state of the MMU,
|
|
data caches or interconnect coherency in its entrypoint. They must be explicitly
|
|
enabled if required.
|
|
|
|
Data structures used in cold boot interface
|
|
'''''''''''''''''''''''''''''''''''''''''''
|
|
|
|
The AArch32 EL3 Runtime Software cold boot interface uses ``bl_params`` instead
|
|
of ``bl31_params``. The ``bl_params`` structure is based on the convention
|
|
described in AArch64 BL31 cold boot interface section.
|
|
|
|
Required CPU state for warm boot initialization
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
When requesting a CPU power-on, or suspending a running CPU, AArch32 EL3
|
|
Runtime Software must ensure execution of a warm boot initialization entrypoint.
|
|
If TF-A BL1 is used and the PROGRAMMABLE\_RESET\_ADDRESS build flag is false,
|
|
then AArch32 EL3 Runtime Software must ensure that BL1 branches to the warm
|
|
boot entrypoint by arranging for the BL1 platform function,
|
|
plat\_get\_my\_entrypoint(), to return a non-zero value.
|
|
|
|
In this case, the warm boot entrypoint must be in AArch32 EL3, little-endian
|
|
data access and all interrupt sources masked:
|
|
|
|
::
|
|
|
|
PSTATE.AIF = 0x7
|
|
SCTLR.EE = 0
|
|
|
|
The warm boot entrypoint may be implemented by using TF-A
|
|
``psci_warmboot_entrypoint()`` function. In that case, the platform must fulfil
|
|
the pre-requisites mentioned in the `PSCI Library integration guide`_.
|
|
|
|
EL3 runtime services framework
|
|
------------------------------
|
|
|
|
Software executing in the non-secure state and in the secure state at exception
|
|
levels lower than EL3 will request runtime services using the Secure Monitor
|
|
Call (SMC) instruction. These requests will follow the convention described in
|
|
the SMC Calling Convention PDD (`SMCCC`_). The `SMCCC`_ assigns function
|
|
identifiers to each SMC request and describes how arguments are passed and
|
|
returned.
|
|
|
|
The EL3 runtime services framework enables the development of services by
|
|
different providers that can be easily integrated into final product firmware.
|
|
The following sections describe the framework which facilitates the
|
|
registration, initialization and use of runtime services in EL3 Runtime
|
|
Software (BL31).
|
|
|
|
The design of the runtime services depends heavily on the concepts and
|
|
definitions described in the `SMCCC`_, in particular SMC Function IDs, Owning
|
|
Entity Numbers (OEN), Fast and Yielding calls, and the SMC32 and SMC64 calling
|
|
conventions. Please refer to that document for more detailed explanation of
|
|
these terms.
|
|
|
|
The following runtime services are expected to be implemented first. They have
|
|
not all been instantiated in the current implementation.
|
|
|
|
#. Standard service calls
|
|
|
|
This service is for management of the entire system. The Power State
|
|
Coordination Interface (`PSCI`_) is the first set of standard service calls
|
|
defined by Arm (see PSCI section later).
|
|
|
|
#. Secure-EL1 Payload Dispatcher service
|
|
|
|
If a system runs a Trusted OS or other Secure-EL1 Payload (SP) then
|
|
it also requires a *Secure Monitor* at EL3 to switch the EL1 processor
|
|
context between the normal world (EL1/EL2) and trusted world (Secure-EL1).
|
|
The Secure Monitor will make these world switches in response to SMCs. The
|
|
`SMCCC`_ provides for such SMCs with the Trusted OS Call and Trusted
|
|
Application Call OEN ranges.
|
|
|
|
The interface between the EL3 Runtime Software and the Secure-EL1 Payload is
|
|
not defined by the `SMCCC`_ or any other standard. As a result, each
|
|
Secure-EL1 Payload requires a specific Secure Monitor that runs as a runtime
|
|
service - within TF-A this service is referred to as the Secure-EL1 Payload
|
|
Dispatcher (SPD).
|
|
|
|
TF-A provides a Test Secure-EL1 Payload (TSP) and its associated Dispatcher
|
|
(TSPD). Details of SPD design and TSP/TSPD operation are described in the
|
|
"Secure-EL1 Payloads and Dispatchers" section below.
|
|
|
|
#. CPU implementation service
|
|
|
|
This service will provide an interface to CPU implementation specific
|
|
services for a given platform e.g. access to processor errata workarounds.
|
|
This service is currently unimplemented.
|
|
|
|
Additional services for Arm Architecture, SiP and OEM calls can be implemented.
|
|
Each implemented service handles a range of SMC function identifiers as
|
|
described in the `SMCCC`_.
|
|
|
|
Registration
|
|
~~~~~~~~~~~~
|
|
|
|
A runtime service is registered using the ``DECLARE_RT_SVC()`` macro, specifying
|
|
the name of the service, the range of OENs covered, the type of service and
|
|
initialization and call handler functions. This macro instantiates a ``const struct rt_svc_desc`` for the service with these details (see ``runtime_svc.h``).
|
|
This structure is allocated in a special ELF section ``rt_svc_descs``, enabling
|
|
the framework to find all service descriptors included into BL31.
|
|
|
|
The specific service for a SMC Function is selected based on the OEN and call
|
|
type of the Function ID, and the framework uses that information in the service
|
|
descriptor to identify the handler for the SMC Call.
|
|
|
|
The service descriptors do not include information to identify the precise set
|
|
of SMC function identifiers supported by this service implementation, the
|
|
security state from which such calls are valid nor the capability to support
|
|
64-bit and/or 32-bit callers (using SMC32 or SMC64). Responding appropriately
|
|
to these aspects of a SMC call is the responsibility of the service
|
|
implementation, the framework is focused on integration of services from
|
|
different providers and minimizing the time taken by the framework before the
|
|
service handler is invoked.
|
|
|
|
Details of the parameters, requirements and behavior of the initialization and
|
|
call handling functions are provided in the following sections.
|
|
|
|
Initialization
|
|
~~~~~~~~~~~~~~
|
|
|
|
``runtime_svc_init()`` in ``runtime_svc.c`` initializes the runtime services
|
|
framework running on the primary CPU during cold boot as part of the BL31
|
|
initialization. This happens prior to initializing a Trusted OS and running
|
|
Normal world boot firmware that might in turn use these services.
|
|
Initialization involves validating each of the declared runtime service
|
|
descriptors, calling the service initialization function and populating the
|
|
index used for runtime lookup of the service.
|
|
|
|
The BL31 linker script collects all of the declared service descriptors into a
|
|
single array and defines symbols that allow the framework to locate and traverse
|
|
the array, and determine its size.
|
|
|
|
The framework does basic validation of each descriptor to halt firmware
|
|
initialization if service declaration errors are detected. The framework does
|
|
not check descriptors for the following error conditions, and may behave in an
|
|
unpredictable manner under such scenarios:
|
|
|
|
#. Overlapping OEN ranges
|
|
#. Multiple descriptors for the same range of OENs and ``call_type``
|
|
#. Incorrect range of owning entity numbers for a given ``call_type``
|
|
|
|
Once validated, the service ``init()`` callback is invoked. This function carries
|
|
out any essential EL3 initialization before servicing requests. The ``init()``
|
|
function is only invoked on the primary CPU during cold boot. If the service
|
|
uses per-CPU data this must either be initialized for all CPUs during this call,
|
|
or be done lazily when a CPU first issues an SMC call to that service. If
|
|
``init()`` returns anything other than ``0``, this is treated as an initialization
|
|
error and the service is ignored: this does not cause the firmware to halt.
|
|
|
|
The OEN and call type fields present in the SMC Function ID cover a total of
|
|
128 distinct services, but in practice a single descriptor can cover a range of
|
|
OENs, e.g. SMCs to call a Trusted OS function. To optimize the lookup of a
|
|
service handler, the framework uses an array of 128 indices that map every
|
|
distinct OEN/call-type combination either to one of the declared services or to
|
|
indicate the service is not handled. This ``rt_svc_descs_indices[]`` array is
|
|
populated for all of the OENs covered by a service after the service ``init()``
|
|
function has reported success. So a service that fails to initialize will never
|
|
have it's ``handle()`` function invoked.
|
|
|
|
The following figure shows how the ``rt_svc_descs_indices[]`` index maps the SMC
|
|
Function ID call type and OEN onto a specific service handler in the
|
|
``rt_svc_descs[]`` array.
|
|
|
|
|Image 1|
|
|
|
|
Handling an SMC
|
|
~~~~~~~~~~~~~~~
|
|
|
|
When the EL3 runtime services framework receives a Secure Monitor Call, the SMC
|
|
Function ID is passed in W0 from the lower exception level (as per the
|
|
`SMCCC`_). If the calling register width is AArch32, it is invalid to invoke an
|
|
SMC Function which indicates the SMC64 calling convention: such calls are
|
|
ignored and return the Unknown SMC Function Identifier result code ``0xFFFFFFFF``
|
|
in R0/X0.
|
|
|
|
Bit[31] (fast/yielding call) and bits[29:24] (owning entity number) of the SMC
|
|
Function ID are combined to index into the ``rt_svc_descs_indices[]`` array. The
|
|
resulting value might indicate a service that has no handler, in this case the
|
|
framework will also report an Unknown SMC Function ID. Otherwise, the value is
|
|
used as a further index into the ``rt_svc_descs[]`` array to locate the required
|
|
service and handler.
|
|
|
|
The service's ``handle()`` callback is provided with five of the SMC parameters
|
|
directly, the others are saved into memory for retrieval (if needed) by the
|
|
handler. The handler is also provided with an opaque ``handle`` for use with the
|
|
supporting library for parameter retrieval, setting return values and context
|
|
manipulation; and with ``flags`` indicating the security state of the caller. The
|
|
framework finally sets up the execution stack for the handler, and invokes the
|
|
services ``handle()`` function.
|
|
|
|
On return from the handler the result registers are populated in X0-X3 before
|
|
restoring the stack and CPU state and returning from the original SMC.
|
|
|
|
Power State Coordination Interface
|
|
----------------------------------
|
|
|
|
TODO: Provide design walkthrough of PSCI implementation.
|
|
|
|
The PSCI v1.1 specification categorizes APIs as optional and mandatory. All the
|
|
mandatory APIs in PSCI v1.1, PSCI v1.0 and in PSCI v0.2 draft specification
|
|
`Power State Coordination Interface PDD`_ are implemented. The table lists
|
|
the PSCI v1.1 APIs and their support in generic code.
|
|
|
|
An API implementation might have a dependency on platform code e.g. CPU\_SUSPEND
|
|
requires the platform to export a part of the implementation. Hence the level
|
|
of support of the mandatory APIs depends upon the support exported by the
|
|
platform port as well. The Juno and FVP (all variants) platforms export all the
|
|
required support.
|
|
|
|
+-----------------------------+-------------+-------------------------------+
|
|
| PSCI v1.1 API | Supported | Comments |
|
|
+=============================+=============+===============================+
|
|
| ``PSCI_VERSION`` | Yes | The version returned is 1.1 |
|
|
+-----------------------------+-------------+-------------------------------+
|
|
| ``CPU_SUSPEND`` | Yes\* | |
|
|
+-----------------------------+-------------+-------------------------------+
|
|
| ``CPU_OFF`` | Yes\* | |
|
|
+-----------------------------+-------------+-------------------------------+
|
|
| ``CPU_ON`` | Yes\* | |
|
|
+-----------------------------+-------------+-------------------------------+
|
|
| ``AFFINITY_INFO`` | Yes | |
|
|
+-----------------------------+-------------+-------------------------------+
|
|
| ``MIGRATE`` | Yes\*\* | |
|
|
+-----------------------------+-------------+-------------------------------+
|
|
| ``MIGRATE_INFO_TYPE`` | Yes\*\* | |
|
|
+-----------------------------+-------------+-------------------------------+
|
|
| ``MIGRATE_INFO_CPU`` | Yes\*\* | |
|
|
+-----------------------------+-------------+-------------------------------+
|
|
| ``SYSTEM_OFF`` | Yes\* | |
|
|
+-----------------------------+-------------+-------------------------------+
|
|
| ``SYSTEM_RESET`` | Yes\* | |
|
|
+-----------------------------+-------------+-------------------------------+
|
|
| ``PSCI_FEATURES`` | Yes | |
|
|
+-----------------------------+-------------+-------------------------------+
|
|
| ``CPU_FREEZE`` | No | |
|
|
+-----------------------------+-------------+-------------------------------+
|
|
| ``CPU_DEFAULT_SUSPEND`` | No | |
|
|
+-----------------------------+-------------+-------------------------------+
|
|
| ``NODE_HW_STATE`` | Yes\* | |
|
|
+-----------------------------+-------------+-------------------------------+
|
|
| ``SYSTEM_SUSPEND`` | Yes\* | |
|
|
+-----------------------------+-------------+-------------------------------+
|
|
| ``PSCI_SET_SUSPEND_MODE`` | No | |
|
|
+-----------------------------+-------------+-------------------------------+
|
|
| ``PSCI_STAT_RESIDENCY`` | Yes\* | |
|
|
+-----------------------------+-------------+-------------------------------+
|
|
| ``PSCI_STAT_COUNT`` | Yes\* | |
|
|
+-----------------------------+-------------+-------------------------------+
|
|
| ``SYSTEM_RESET2`` | Yes\* | |
|
|
+-----------------------------+-------------+-------------------------------+
|
|
| ``MEM_PROTECT`` | Yes\* | |
|
|
+-----------------------------+-------------+-------------------------------+
|
|
| ``MEM_PROTECT_CHECK_RANGE`` | Yes\* | |
|
|
+-----------------------------+-------------+-------------------------------+
|
|
|
|
\*Note : These PSCI APIs require platform power management hooks to be
|
|
registered with the generic PSCI code to be supported.
|
|
|
|
\*\*Note : These PSCI APIs require appropriate Secure Payload Dispatcher
|
|
hooks to be registered with the generic PSCI code to be supported.
|
|
|
|
The PSCI implementation in TF-A is a library which can be integrated with
|
|
AArch64 or AArch32 EL3 Runtime Software for Armv8-A systems. A guide to
|
|
integrating PSCI library with AArch32 EL3 Runtime Software can be found
|
|
`here`_.
|
|
|
|
Secure-EL1 Payloads and Dispatchers
|
|
-----------------------------------
|
|
|
|
On a production system that includes a Trusted OS running in Secure-EL1/EL0,
|
|
the Trusted OS is coupled with a companion runtime service in the BL31
|
|
firmware. This service is responsible for the initialisation of the Trusted
|
|
OS and all communications with it. The Trusted OS is the BL32 stage of the
|
|
boot flow in TF-A. The firmware will attempt to locate, load and execute a
|
|
BL32 image.
|
|
|
|
TF-A uses a more general term for the BL32 software that runs at Secure-EL1 -
|
|
the *Secure-EL1 Payload* - as it is not always a Trusted OS.
|
|
|
|
TF-A provides a Test Secure-EL1 Payload (TSP) and a Test Secure-EL1 Payload
|
|
Dispatcher (TSPD) service as an example of how a Trusted OS is supported on a
|
|
production system using the Runtime Services Framework. On such a system, the
|
|
Test BL32 image and service are replaced by the Trusted OS and its dispatcher
|
|
service. The TF-A build system expects that the dispatcher will define the
|
|
build flag ``NEED_BL32`` to enable it to include the BL32 in the build either
|
|
as a binary or to compile from source depending on whether the ``BL32`` build
|
|
option is specified or not.
|
|
|
|
The TSP runs in Secure-EL1. It is designed to demonstrate synchronous
|
|
communication with the normal-world software running in EL1/EL2. Communication
|
|
is initiated by the normal-world software
|
|
|
|
- either directly through a Fast SMC (as defined in the `SMCCC`_)
|
|
|
|
- or indirectly through a `PSCI`_ SMC. The `PSCI`_ implementation in turn
|
|
informs the TSPD about the requested power management operation. This allows
|
|
the TSP to prepare for or respond to the power state change
|
|
|
|
The TSPD service is responsible for.
|
|
|
|
- Initializing the TSP
|
|
|
|
- Routing requests and responses between the secure and the non-secure
|
|
states during the two types of communications just described
|
|
|
|
Initializing a BL32 Image
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The Secure-EL1 Payload Dispatcher (SPD) service is responsible for initializing
|
|
the BL32 image. It needs access to the information passed by BL2 to BL31 to do
|
|
so. This is provided by:
|
|
|
|
.. code:: c
|
|
|
|
entry_point_info_t *bl31_plat_get_next_image_ep_info(uint32_t);
|
|
|
|
which returns a reference to the ``entry_point_info`` structure corresponding to
|
|
the image which will be run in the specified security state. The SPD uses this
|
|
API to get entry point information for the SECURE image, BL32.
|
|
|
|
In the absence of a BL32 image, BL31 passes control to the normal world
|
|
bootloader image (BL33). When the BL32 image is present, it is typical
|
|
that the SPD wants control to be passed to BL32 first and then later to BL33.
|
|
|
|
To do this the SPD has to register a BL32 initialization function during
|
|
initialization of the SPD service. The BL32 initialization function has this
|
|
prototype:
|
|
|
|
.. code:: c
|
|
|
|
int32_t init(void);
|
|
|
|
and is registered using the ``bl31_register_bl32_init()`` function.
|
|
|
|
TF-A supports two approaches for the SPD to pass control to BL32 before
|
|
returning through EL3 and running the non-trusted firmware (BL33):
|
|
|
|
#. In the BL32 setup function, use ``bl31_set_next_image_type()`` to
|
|
request that the exit from ``bl31_main()`` is to the BL32 entrypoint in
|
|
Secure-EL1. BL31 will exit to BL32 using the asynchronous method by
|
|
calling ``bl31_prepare_next_image_entry()`` and ``el3_exit()``.
|
|
|
|
When the BL32 has completed initialization at Secure-EL1, it returns to
|
|
BL31 by issuing an SMC, using a Function ID allocated to the SPD. On
|
|
receipt of this SMC, the SPD service handler should switch the CPU context
|
|
from trusted to normal world and use the ``bl31_set_next_image_type()`` and
|
|
``bl31_prepare_next_image_entry()`` functions to set up the initial return to
|
|
the normal world firmware BL33. On return from the handler the framework
|
|
will exit to EL2 and run BL33.
|
|
|
|
#. The BL32 setup function registers an initialization function using
|
|
``bl31_register_bl32_init()`` which provides a SPD-defined mechanism to
|
|
invoke a 'world-switch synchronous call' to Secure-EL1 to run the BL32
|
|
entrypoint.
|
|
NOTE: The Test SPD service included with TF-A provides one implementation
|
|
of such a mechanism.
|
|
|
|
On completion BL32 returns control to BL31 via a SMC, and on receipt the
|
|
SPD service handler invokes the synchronous call return mechanism to return
|
|
to the BL32 initialization function. On return from this function,
|
|
``bl31_main()`` will set up the return to the normal world firmware BL33 and
|
|
continue the boot process in the normal world.
|
|
|
|
Crash Reporting in BL31
|
|
-----------------------
|
|
|
|
BL31 implements a scheme for reporting the processor state when an unhandled
|
|
exception is encountered. The reporting mechanism attempts to preserve all the
|
|
register contents and report it via a dedicated UART (PL011 console). BL31
|
|
reports the general purpose, EL3, Secure EL1 and some EL2 state registers.
|
|
|
|
A dedicated per-CPU crash stack is maintained by BL31 and this is retrieved via
|
|
the per-CPU pointer cache. The implementation attempts to minimise the memory
|
|
required for this feature. The file ``crash_reporting.S`` contains the
|
|
implementation for crash reporting.
|
|
|
|
The sample crash output is shown below.
|
|
|
|
::
|
|
|
|
x0 :0x000000004F00007C
|
|
x1 :0x0000000007FFFFFF
|
|
x2 :0x0000000004014D50
|
|
x3 :0x0000000000000000
|
|
x4 :0x0000000088007998
|
|
x5 :0x00000000001343AC
|
|
x6 :0x0000000000000016
|
|
x7 :0x00000000000B8A38
|
|
x8 :0x00000000001343AC
|
|
x9 :0x00000000000101A8
|
|
x10 :0x0000000000000002
|
|
x11 :0x000000000000011C
|
|
x12 :0x00000000FEFDC644
|
|
x13 :0x00000000FED93FFC
|
|
x14 :0x0000000000247950
|
|
x15 :0x00000000000007A2
|
|
x16 :0x00000000000007A4
|
|
x17 :0x0000000000247950
|
|
x18 :0x0000000000000000
|
|
x19 :0x00000000FFFFFFFF
|
|
x20 :0x0000000004014D50
|
|
x21 :0x000000000400A38C
|
|
x22 :0x0000000000247950
|
|
x23 :0x0000000000000010
|
|
x24 :0x0000000000000024
|
|
x25 :0x00000000FEFDC868
|
|
x26 :0x00000000FEFDC86A
|
|
x27 :0x00000000019EDEDC
|
|
x28 :0x000000000A7CFDAA
|
|
x29 :0x0000000004010780
|
|
x30 :0x000000000400F004
|
|
scr_el3 :0x0000000000000D3D
|
|
sctlr_el3 :0x0000000000C8181F
|
|
cptr_el3 :0x0000000000000000
|
|
tcr_el3 :0x0000000080803520
|
|
daif :0x00000000000003C0
|
|
mair_el3 :0x00000000000004FF
|
|
spsr_el3 :0x00000000800003CC
|
|
elr_el3 :0x000000000400C0CC
|
|
ttbr0_el3 :0x00000000040172A0
|
|
esr_el3 :0x0000000096000210
|
|
sp_el3 :0x0000000004014D50
|
|
far_el3 :0x000000004F00007C
|
|
spsr_el1 :0x0000000000000000
|
|
elr_el1 :0x0000000000000000
|
|
spsr_abt :0x0000000000000000
|
|
spsr_und :0x0000000000000000
|
|
spsr_irq :0x0000000000000000
|
|
spsr_fiq :0x0000000000000000
|
|
sctlr_el1 :0x0000000030C81807
|
|
actlr_el1 :0x0000000000000000
|
|
cpacr_el1 :0x0000000000300000
|
|
csselr_el1 :0x0000000000000002
|
|
sp_el1 :0x0000000004028800
|
|
esr_el1 :0x0000000000000000
|
|
ttbr0_el1 :0x000000000402C200
|
|
ttbr1_el1 :0x0000000000000000
|
|
mair_el1 :0x00000000000004FF
|
|
amair_el1 :0x0000000000000000
|
|
tcr_el1 :0x0000000000003520
|
|
tpidr_el1 :0x0000000000000000
|
|
tpidr_el0 :0x0000000000000000
|
|
tpidrro_el0 :0x0000000000000000
|
|
dacr32_el2 :0x0000000000000000
|
|
ifsr32_el2 :0x0000000000000000
|
|
par_el1 :0x0000000000000000
|
|
far_el1 :0x0000000000000000
|
|
afsr0_el1 :0x0000000000000000
|
|
afsr1_el1 :0x0000000000000000
|
|
contextidr_el1 :0x0000000000000000
|
|
vbar_el1 :0x0000000004027000
|
|
cntp_ctl_el0 :0x0000000000000000
|
|
cntp_cval_el0 :0x0000000000000000
|
|
cntv_ctl_el0 :0x0000000000000000
|
|
cntv_cval_el0 :0x0000000000000000
|
|
cntkctl_el1 :0x0000000000000000
|
|
sp_el0 :0x0000000004010780
|
|
|
|
Guidelines for Reset Handlers
|
|
-----------------------------
|
|
|
|
TF-A implements a framework that allows CPU and platform ports to perform
|
|
actions very early after a CPU is released from reset in both the cold and warm
|
|
boot paths. This is done by calling the ``reset_handler()`` function in both
|
|
the BL1 and BL31 images. It in turn calls the platform and CPU specific reset
|
|
handling functions.
|
|
|
|
Details for implementing a CPU specific reset handler can be found in
|
|
Section 8. Details for implementing a platform specific reset handler can be
|
|
found in the `Porting Guide`_ (see the ``plat_reset_handler()`` function).
|
|
|
|
When adding functionality to a reset handler, keep in mind that if a different
|
|
reset handling behavior is required between the first and the subsequent
|
|
invocations of the reset handling code, this should be detected at runtime.
|
|
In other words, the reset handler should be able to detect whether an action has
|
|
already been performed and act as appropriate. Possible courses of actions are,
|
|
e.g. skip the action the second time, or undo/redo it.
|
|
|
|
Configuring secure interrupts
|
|
-----------------------------
|
|
|
|
The GIC driver is responsible for performing initial configuration of secure
|
|
interrupts on the platform. To this end, the platform is expected to provide the
|
|
GIC driver (either GICv2 or GICv3, as selected by the platform) with the
|
|
interrupt configuration during the driver initialisation.
|
|
|
|
There are two ways to specify secure interrupt configuration:
|
|
|
|
#. Array of secure interrupt properties: In this scheme, in both GICv2 and GICv3
|
|
driver data structures, the ``interrupt_props`` member points to an array of
|
|
interrupt properties. Each element of the array specifies the interrupt
|
|
number and its configuration, viz. priority, group, configuration. Each
|
|
element of the array shall be populated by the macro ``INTR_PROP_DESC()``.
|
|
The macro takes the following arguments:
|
|
|
|
- 10-bit interrupt number,
|
|
|
|
- 8-bit interrupt priority,
|
|
|
|
- Interrupt type (one of ``INTR_TYPE_EL3``, ``INTR_TYPE_S_EL1``,
|
|
``INTR_TYPE_NS``),
|
|
|
|
- Interrupt configuration (either ``GIC_INTR_CFG_LEVEL`` or
|
|
``GIC_INTR_CFG_EDGE``).
|
|
|
|
#. Array of secure interrupts: In this scheme, the GIC driver is provided an
|
|
array of secure interrupt numbers. The GIC driver, at the time of
|
|
initialisation, iterates through the array and assigns each interrupt
|
|
the appropriate group.
|
|
|
|
- For the GICv2 driver, in ``gicv2_driver_data`` structure, the
|
|
``g0_interrupt_array`` member of the should point to the array of
|
|
interrupts to be assigned to *Group 0*, and the ``g0_interrupt_num``
|
|
member of the should be set to the number of interrupts in the array.
|
|
|
|
- For the GICv3 driver, in ``gicv3_driver_data`` structure:
|
|
|
|
- The ``g0_interrupt_array`` member of the should point to the array of
|
|
interrupts to be assigned to *Group 0*, and the ``g0_interrupt_num``
|
|
member of the should be set to the number of interrupts in the array.
|
|
|
|
- The ``g1s_interrupt_array`` member of the should point to the array of
|
|
interrupts to be assigned to *Group 1 Secure*, and the
|
|
``g1s_interrupt_num`` member of the should be set to the number of
|
|
interrupts in the array.
|
|
|
|
**Note that this scheme is deprecated.**
|
|
|
|
CPU specific operations framework
|
|
---------------------------------
|
|
|
|
Certain aspects of the Armv8-A architecture are implementation defined,
|
|
that is, certain behaviours are not architecturally defined, but must be
|
|
defined and documented by individual processor implementations. TF-A
|
|
implements a framework which categorises the common implementation defined
|
|
behaviours and allows a processor to export its implementation of that
|
|
behaviour. The categories are:
|
|
|
|
#. Processor specific reset sequence.
|
|
|
|
#. Processor specific power down sequences.
|
|
|
|
#. Processor specific register dumping as a part of crash reporting.
|
|
|
|
#. Errata status reporting.
|
|
|
|
Each of the above categories fulfils a different requirement.
|
|
|
|
#. allows any processor specific initialization before the caches and MMU
|
|
are turned on, like implementation of errata workarounds, entry into
|
|
the intra-cluster coherency domain etc.
|
|
|
|
#. allows each processor to implement the power down sequence mandated in
|
|
its Technical Reference Manual (TRM).
|
|
|
|
#. allows a processor to provide additional information to the developer
|
|
in the event of a crash, for example Cortex-A53 has registers which
|
|
can expose the data cache contents.
|
|
|
|
#. allows a processor to define a function that inspects and reports the status
|
|
of all errata workarounds on that processor.
|
|
|
|
Please note that only 2. is mandated by the TRM.
|
|
|
|
The CPU specific operations framework scales to accommodate a large number of
|
|
different CPUs during power down and reset handling. The platform can specify
|
|
any CPU optimization it wants to enable for each CPU. It can also specify
|
|
the CPU errata workarounds to be applied for each CPU type during reset
|
|
handling by defining CPU errata compile time macros. Details on these macros
|
|
can be found in the `cpu-specific-build-macros.rst`_ file.
|
|
|
|
The CPU specific operations framework depends on the ``cpu_ops`` structure which
|
|
needs to be exported for each type of CPU in the platform. It is defined in
|
|
``include/lib/cpus/aarch64/cpu_macros.S`` and has the following fields : ``midr``,
|
|
``reset_func()``, ``cpu_pwr_down_ops`` (array of power down functions) and
|
|
``cpu_reg_dump()``.
|
|
|
|
The CPU specific files in ``lib/cpus`` export a ``cpu_ops`` data structure with
|
|
suitable handlers for that CPU. For example, ``lib/cpus/aarch64/cortex_a53.S``
|
|
exports the ``cpu_ops`` for Cortex-A53 CPU. According to the platform
|
|
configuration, these CPU specific files must be included in the build by
|
|
the platform makefile. The generic CPU specific operations framework code exists
|
|
in ``lib/cpus/aarch64/cpu_helpers.S``.
|
|
|
|
CPU specific Reset Handling
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
After a reset, the state of the CPU when it calls generic reset handler is:
|
|
MMU turned off, both instruction and data caches turned off and not part
|
|
of any coherency domain.
|
|
|
|
The BL entrypoint code first invokes the ``plat_reset_handler()`` to allow
|
|
the platform to perform any system initialization required and any system
|
|
errata workarounds that needs to be applied. The ``get_cpu_ops_ptr()`` reads
|
|
the current CPU midr, finds the matching ``cpu_ops`` entry in the ``cpu_ops``
|
|
array and returns it. Note that only the part number and implementer fields
|
|
in midr are used to find the matching ``cpu_ops`` entry. The ``reset_func()`` in
|
|
the returned ``cpu_ops`` is then invoked which executes the required reset
|
|
handling for that CPU and also any errata workarounds enabled by the platform.
|
|
This function must preserve the values of general purpose registers x20 to x29.
|
|
|
|
Refer to Section "Guidelines for Reset Handlers" for general guidelines
|
|
regarding placement of code in a reset handler.
|
|
|
|
CPU specific power down sequence
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
During the BL31 initialization sequence, the pointer to the matching ``cpu_ops``
|
|
entry is stored in per-CPU data by ``init_cpu_ops()`` so that it can be quickly
|
|
retrieved during power down sequences.
|
|
|
|
Various CPU drivers register handlers to perform power down at certain power
|
|
levels for that specific CPU. The PSCI service, upon receiving a power down
|
|
request, determines the highest power level at which to execute power down
|
|
sequence for a particular CPU. It uses the ``prepare_cpu_pwr_dwn()`` function to
|
|
pick the right power down handler for the requested level. The function
|
|
retrieves ``cpu_ops`` pointer member of per-CPU data, and from that, further
|
|
retrieves ``cpu_pwr_down_ops`` array, and indexes into the required level. If the
|
|
requested power level is higher than what a CPU driver supports, the handler
|
|
registered for highest level is invoked.
|
|
|
|
At runtime the platform hooks for power down are invoked by the PSCI service to
|
|
perform platform specific operations during a power down sequence, for example
|
|
turning off CCI coherency during a cluster power down.
|
|
|
|
CPU specific register reporting during crash
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
If the crash reporting is enabled in BL31, when a crash occurs, the crash
|
|
reporting framework calls ``do_cpu_reg_dump`` which retrieves the matching
|
|
``cpu_ops`` using ``get_cpu_ops_ptr()`` function. The ``cpu_reg_dump()`` in
|
|
``cpu_ops`` is invoked, which then returns the CPU specific register values to
|
|
be reported and a pointer to the ASCII list of register names in a format
|
|
expected by the crash reporting framework.
|
|
|
|
CPU errata status reporting
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Errata workarounds for CPUs supported in TF-A are applied during both cold and
|
|
warm boots, shortly after reset. Individual Errata workarounds are enabled as
|
|
build options. Some errata workarounds have potential run-time implications;
|
|
therefore some are enabled by default, others not. Platform ports shall
|
|
override build options to enable or disable errata as appropriate. The CPU
|
|
drivers take care of applying errata workarounds that are enabled and applicable
|
|
to a given CPU. Refer to the section titled *CPU Errata Workarounds* in `CPUBM`_
|
|
for more information.
|
|
|
|
Functions in CPU drivers that apply errata workaround must follow the
|
|
conventions listed below.
|
|
|
|
The errata workaround must be authored as two separate functions:
|
|
|
|
- One that checks for errata. This function must determine whether that errata
|
|
applies to the current CPU. Typically this involves matching the current
|
|
CPUs revision and variant against a value that's known to be affected by the
|
|
errata. If the function determines that the errata applies to this CPU, it
|
|
must return ``ERRATA_APPLIES``; otherwise, it must return
|
|
``ERRATA_NOT_APPLIES``. The utility functions ``cpu_get_rev_var`` and
|
|
``cpu_rev_var_ls`` functions may come in handy for this purpose.
|
|
|
|
For an errata identified as ``E``, the check function must be named
|
|
``check_errata_E``.
|
|
|
|
This function will be invoked at different times, both from assembly and from
|
|
C run time. Therefore it must follow AAPCS, and must not use stack.
|
|
|
|
- Another one that applies the errata workaround. This function would call the
|
|
check function described above, and applies errata workaround if required.
|
|
|
|
CPU drivers that apply errata workaround can optionally implement an assembly
|
|
function that report the status of errata workarounds pertaining to that CPU.
|
|
For a driver that registers the CPU, for example, ``cpux`` via. ``declare_cpu_ops``
|
|
macro, the errata reporting function, if it exists, must be named
|
|
``cpux_errata_report``. This function will always be called with MMU enabled; it
|
|
must follow AAPCS and may use stack.
|
|
|
|
In a debug build of TF-A, on a CPU that comes out of reset, both BL1 and the
|
|
runtime firmware (BL31 in AArch64, and BL32 in AArch32) will invoke errata
|
|
status reporting function, if one exists, for that type of CPU.
|
|
|
|
To report the status of each errata workaround, the function shall use the
|
|
assembler macro ``report_errata``, passing it:
|
|
|
|
- The build option that enables the errata;
|
|
|
|
- The name of the CPU: this must be the same identifier that CPU driver
|
|
registered itself with, using ``declare_cpu_ops``;
|
|
|
|
- And the errata identifier: the identifier must match what's used in the
|
|
errata's check function described above.
|
|
|
|
The errata status reporting function will be called once per CPU type/errata
|
|
combination during the software's active life time.
|
|
|
|
It's expected that whenever an errata workaround is submitted to TF-A, the
|
|
errata reporting function is appropriately extended to report its status as
|
|
well.
|
|
|
|
Reporting the status of errata workaround is for informational purpose only; it
|
|
has no functional significance.
|
|
|
|
Memory layout of BL images
|
|
--------------------------
|
|
|
|
Each bootloader image can be divided in 2 parts:
|
|
|
|
- the static contents of the image. These are data actually stored in the
|
|
binary on the disk. In the ELF terminology, they are called ``PROGBITS``
|
|
sections;
|
|
|
|
- the run-time contents of the image. These are data that don't occupy any
|
|
space in the binary on the disk. The ELF binary just contains some
|
|
metadata indicating where these data will be stored at run-time and the
|
|
corresponding sections need to be allocated and initialized at run-time.
|
|
In the ELF terminology, they are called ``NOBITS`` sections.
|
|
|
|
All PROGBITS sections are grouped together at the beginning of the image,
|
|
followed by all NOBITS sections. This is true for all TF-A images and it is
|
|
governed by the linker scripts. This ensures that the raw binary images are
|
|
as small as possible. If a NOBITS section was inserted in between PROGBITS
|
|
sections then the resulting binary file would contain zero bytes in place of
|
|
this NOBITS section, making the image unnecessarily bigger. Smaller images
|
|
allow faster loading from the FIP to the main memory.
|
|
|
|
Linker scripts and symbols
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Each bootloader stage image layout is described by its own linker script. The
|
|
linker scripts export some symbols into the program symbol table. Their values
|
|
correspond to particular addresses. TF-A code can refer to these symbols to
|
|
figure out the image memory layout.
|
|
|
|
Linker symbols follow the following naming convention in TF-A.
|
|
|
|
- ``__<SECTION>_START__``
|
|
|
|
Start address of a given section named ``<SECTION>``.
|
|
|
|
- ``__<SECTION>_END__``
|
|
|
|
End address of a given section named ``<SECTION>``. If there is an alignment
|
|
constraint on the section's end address then ``__<SECTION>_END__`` corresponds
|
|
to the end address of the section's actual contents, rounded up to the right
|
|
boundary. Refer to the value of ``__<SECTION>_UNALIGNED_END__`` to know the
|
|
actual end address of the section's contents.
|
|
|
|
- ``__<SECTION>_UNALIGNED_END__``
|
|
|
|
End address of a given section named ``<SECTION>`` without any padding or
|
|
rounding up due to some alignment constraint.
|
|
|
|
- ``__<SECTION>_SIZE__``
|
|
|
|
Size (in bytes) of a given section named ``<SECTION>``. If there is an
|
|
alignment constraint on the section's end address then ``__<SECTION>_SIZE__``
|
|
corresponds to the size of the section's actual contents, rounded up to the
|
|
right boundary. In other words, ``__<SECTION>_SIZE__ = __<SECTION>_END__ - _<SECTION>_START__``. Refer to the value of ``__<SECTION>_UNALIGNED_SIZE__``
|
|
to know the actual size of the section's contents.
|
|
|
|
- ``__<SECTION>_UNALIGNED_SIZE__``
|
|
|
|
Size (in bytes) of a given section named ``<SECTION>`` without any padding or
|
|
rounding up due to some alignment constraint. In other words,
|
|
``__<SECTION>_UNALIGNED_SIZE__ = __<SECTION>_UNALIGNED_END__ - __<SECTION>_START__``.
|
|
|
|
Some of the linker symbols are mandatory as TF-A code relies on them to be
|
|
defined. They are listed in the following subsections. Some of them must be
|
|
provided for each bootloader stage and some are specific to a given bootloader
|
|
stage.
|
|
|
|
The linker scripts define some extra, optional symbols. They are not actually
|
|
used by any code but they help in understanding the bootloader images' memory
|
|
layout as they are easy to spot in the link map files.
|
|
|
|
Common linker symbols
|
|
^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
All BL images share the following requirements:
|
|
|
|
- The BSS section must be zero-initialised before executing any C code.
|
|
- The coherent memory section (if enabled) must be zero-initialised as well.
|
|
- The MMU setup code needs to know the extents of the coherent and read-only
|
|
memory regions to set the right memory attributes. When
|
|
``SEPARATE_CODE_AND_RODATA=1``, it needs to know more specifically how the
|
|
read-only memory region is divided between code and data.
|
|
|
|
The following linker symbols are defined for this purpose:
|
|
|
|
- ``__BSS_START__``
|
|
- ``__BSS_SIZE__``
|
|
- ``__COHERENT_RAM_START__`` Must be aligned on a page-size boundary.
|
|
- ``__COHERENT_RAM_END__`` Must be aligned on a page-size boundary.
|
|
- ``__COHERENT_RAM_UNALIGNED_SIZE__``
|
|
- ``__RO_START__``
|
|
- ``__RO_END__``
|
|
- ``__TEXT_START__``
|
|
- ``__TEXT_END__``
|
|
- ``__RODATA_START__``
|
|
- ``__RODATA_END__``
|
|
|
|
BL1's linker symbols
|
|
^^^^^^^^^^^^^^^^^^^^
|
|
|
|
BL1 being the ROM image, it has additional requirements. BL1 resides in ROM and
|
|
it is entirely executed in place but it needs some read-write memory for its
|
|
mutable data. Its ``.data`` section (i.e. its allocated read-write data) must be
|
|
relocated from ROM to RAM before executing any C code.
|
|
|
|
The following additional linker symbols are defined for BL1:
|
|
|
|
- ``__BL1_ROM_END__`` End address of BL1's ROM contents, covering its code
|
|
and ``.data`` section in ROM.
|
|
- ``__DATA_ROM_START__`` Start address of the ``.data`` section in ROM. Must be
|
|
aligned on a 16-byte boundary.
|
|
- ``__DATA_RAM_START__`` Address in RAM where the ``.data`` section should be
|
|
copied over. Must be aligned on a 16-byte boundary.
|
|
- ``__DATA_SIZE__`` Size of the ``.data`` section (in ROM or RAM).
|
|
- ``__BL1_RAM_START__`` Start address of BL1 read-write data.
|
|
- ``__BL1_RAM_END__`` End address of BL1 read-write data.
|
|
|
|
How to choose the right base addresses for each bootloader stage image
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
There is currently no support for dynamic image loading in TF-A. This means
|
|
that all bootloader images need to be linked against their ultimate runtime
|
|
locations and the base addresses of each image must be chosen carefully such
|
|
that images don't overlap each other in an undesired way. As the code grows,
|
|
the base addresses might need adjustments to cope with the new memory layout.
|
|
|
|
The memory layout is completely specific to the platform and so there is no
|
|
general recipe for choosing the right base addresses for each bootloader image.
|
|
However, there are tools to aid in understanding the memory layout. These are
|
|
the link map files: ``build/<platform>/<build-type>/bl<x>/bl<x>.map``, with ``<x>``
|
|
being the stage bootloader. They provide a detailed view of the memory usage of
|
|
each image. Among other useful information, they provide the end address of
|
|
each image.
|
|
|
|
- ``bl1.map`` link map file provides ``__BL1_RAM_END__`` address.
|
|
- ``bl2.map`` link map file provides ``__BL2_END__`` address.
|
|
- ``bl31.map`` link map file provides ``__BL31_END__`` address.
|
|
- ``bl32.map`` link map file provides ``__BL32_END__`` address.
|
|
|
|
For each bootloader image, the platform code must provide its start address
|
|
as well as a limit address that it must not overstep. The latter is used in the
|
|
linker scripts to check that the image doesn't grow past that address. If that
|
|
happens, the linker will issue a message similar to the following:
|
|
|
|
::
|
|
|
|
aarch64-none-elf-ld: BLx has exceeded its limit.
|
|
|
|
Additionally, if the platform memory layout implies some image overlaying like
|
|
on FVP, BL31 and TSP need to know the limit address that their PROGBITS
|
|
sections must not overstep. The platform code must provide those.
|
|
|
|
When LOAD\_IMAGE\_V2 is disabled, TF-A provides a mechanism to verify at boot
|
|
time that the memory to load a new image is free to prevent overwriting a
|
|
previously loaded image. For this mechanism to work, the platform must specify
|
|
the memory available in the system as regions, where each region consists of
|
|
base address, total size and the free area within it (as defined in the
|
|
``meminfo_t`` structure). TF-A retrieves these memory regions by calling the
|
|
corresponding platform API:
|
|
|
|
- ``meminfo_t *bl1_plat_sec_mem_layout(void)``
|
|
- ``meminfo_t *bl2_plat_sec_mem_layout(void)``
|
|
- ``void bl2_plat_get_scp_bl2_meminfo(meminfo_t *scp_bl2_meminfo)``
|
|
- ``void bl2_plat_get_bl32_meminfo(meminfo_t *bl32_meminfo)``
|
|
- ``void bl2_plat_get_bl33_meminfo(meminfo_t *bl33_meminfo)``
|
|
|
|
For example, in the case of BL1 loading BL2, ``bl1_plat_sec_mem_layout()`` will
|
|
return the region defined by the platform where BL1 intends to load BL2. The
|
|
``load_image()`` function will check that the memory where BL2 will be loaded is
|
|
within the specified region and marked as free.
|
|
|
|
The actual number of regions and their base addresses and sizes is platform
|
|
specific. The platform may return the same region or define a different one for
|
|
each API. However, the overlap verification mechanism applies only to a single
|
|
region. Hence, it is the platform responsibility to guarantee that different
|
|
regions do not overlap, or that if they do, the overlapping images are not
|
|
accessed at the same time. This could be used, for example, to load temporary
|
|
images (e.g. certificates) or firmware images prior to being transfered to its
|
|
corresponding processor (e.g. the SCP BL2 image).
|
|
|
|
To reduce fragmentation and simplify the tracking of free memory, all the free
|
|
memory within a region is always located in one single buffer defined by its
|
|
base address and size. TF-A implements a top/bottom load approach:
|
|
after a new image is loaded, it checks how much memory remains free above and
|
|
below the image. The smallest area is marked as unavailable, while the larger
|
|
area becomes the new free memory buffer. Platforms should take this behaviour
|
|
into account when defining the base address for each of the images. For example,
|
|
if an image is loaded near the middle of the region, small changes in image size
|
|
could cause a flip between a top load and a bottom load, which may result in an
|
|
unexpected memory layout.
|
|
|
|
The following diagram is an example of an image loaded in the bottom part of
|
|
the memory region. The region is initially free (nothing has been loaded yet):
|
|
|
|
::
|
|
|
|
Memory region
|
|
+----------+
|
|
| |
|
|
| | <<<<<<<<<<<<< Free
|
|
| |
|
|
|----------| +------------+
|
|
| image | <<<<<<<<<<<<< | image |
|
|
|----------| +------------+
|
|
| xxxxxxxx | <<<<<<<<<<<<< Marked as unavailable
|
|
+----------+
|
|
|
|
And the following diagram is an example of an image loaded in the top part:
|
|
|
|
::
|
|
|
|
Memory region
|
|
+----------+
|
|
| xxxxxxxx | <<<<<<<<<<<<< Marked as unavailable
|
|
|----------| +------------+
|
|
| image | <<<<<<<<<<<<< | image |
|
|
|----------| +------------+
|
|
| |
|
|
| | <<<<<<<<<<<<< Free
|
|
| |
|
|
+----------+
|
|
|
|
When LOAD\_IMAGE\_V2 is enabled, TF-A does not provide any mechanism to verify
|
|
at boot time that the memory to load a new image is free to prevent overwriting
|
|
a previously loaded image. The platform must specify the memory available in
|
|
the system for all the relevant BL images to be loaded.
|
|
|
|
For example, in the case of BL1 loading BL2, ``bl1_plat_sec_mem_layout()`` will
|
|
return the region defined by the platform where BL1 intends to load BL2. The
|
|
``load_image()`` function performs bounds check for the image size based on the
|
|
base and maximum image size provided by the platforms. Platforms must take
|
|
this behaviour into account when defining the base/size for each of the images.
|
|
|
|
Memory layout on Arm development platforms
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
The following list describes the memory layout on the Arm development platforms:
|
|
|
|
- A 4KB page of shared memory is used for communication between Trusted
|
|
Firmware and the platform's power controller. This is located at the base of
|
|
Trusted SRAM. The amount of Trusted SRAM available to load the bootloader
|
|
images is reduced by the size of the shared memory.
|
|
|
|
The shared memory is used to store the CPUs' entrypoint mailbox. On Juno,
|
|
this is also used for the MHU payload when passing messages to and from the
|
|
SCP.
|
|
|
|
- Another 4 KB page is reserved for passing memory layout between BL1 and BL2
|
|
and also the dynamic firmware configurations.
|
|
|
|
- On FVP, BL1 is originally sitting in the Trusted ROM at address ``0x0``. On
|
|
Juno, BL1 resides in flash memory at address ``0x0BEC0000``. BL1 read-write
|
|
data are relocated to the top of Trusted SRAM at runtime.
|
|
|
|
- BL2 is loaded below BL1 RW
|
|
|
|
- EL3 Runtime Software, BL31 for AArch64 and BL32 for AArch32 (e.g. SP\_MIN),
|
|
is loaded at the top of the Trusted SRAM, such that its NOBITS sections will
|
|
overwrite BL1 R/W data and BL2. This implies that BL1 global variables
|
|
remain valid only until execution reaches the EL3 Runtime Software entry
|
|
point during a cold boot.
|
|
|
|
- On Juno, SCP\_BL2 is loaded temporarily into the EL3 Runtime Software memory
|
|
region and transfered to the SCP before being overwritten by EL3 Runtime
|
|
Software.
|
|
|
|
- BL32 (for AArch64) can be loaded in one of the following locations:
|
|
|
|
- Trusted SRAM
|
|
- Trusted DRAM (FVP only)
|
|
- Secure region of DRAM (top 16MB of DRAM configured by the TrustZone
|
|
controller)
|
|
|
|
When BL32 (for AArch64) is loaded into Trusted SRAM, it is loaded below
|
|
BL31.
|
|
|
|
When LOAD\_IMAGE\_V2 is disabled the memory regions for the overlap detection
|
|
mechanism at boot time are defined as follows (shown per API):
|
|
|
|
- ``meminfo_t *bl1_plat_sec_mem_layout(void)``
|
|
|
|
This region corresponds to the whole Trusted SRAM except for the shared
|
|
memory at the base. This region is initially free. At boot time, BL1 will
|
|
mark the BL1(rw) section within this region as occupied. The BL1(rw) section
|
|
is placed at the top of Trusted SRAM.
|
|
|
|
- ``meminfo_t *bl2_plat_sec_mem_layout(void)``
|
|
|
|
This region corresponds to the whole Trusted SRAM as defined by
|
|
``bl1_plat_sec_mem_layout()``, but with the BL1(rw) section marked as
|
|
occupied. This memory region is used to check that BL2 and BL31 do not
|
|
overlap with each other. BL2\_BASE and BL1\_RW\_BASE are carefully chosen so
|
|
that the memory for BL31 is top loaded above BL2.
|
|
|
|
- ``void bl2_plat_get_scp_bl2_meminfo(meminfo_t *scp_bl2_meminfo)``
|
|
|
|
This region is an exact copy of the region defined by
|
|
``bl2_plat_sec_mem_layout()``. Being a disconnected copy means that all the
|
|
changes made to this region by the TF-A will not be propagated. This
|
|
approach is valid because the SCP BL2 image is loaded temporarily while it
|
|
is being transferred to the SCP, so this memory is reused afterwards.
|
|
|
|
- ``void bl2_plat_get_bl32_meminfo(meminfo_t *bl32_meminfo)``
|
|
|
|
This region depends on the location of the BL32 image. Currently, Arm
|
|
platforms support three different locations (detailed below): Trusted SRAM,
|
|
Trusted DRAM and the TZC-Secured DRAM.
|
|
|
|
- ``void bl2_plat_get_bl33_meminfo(meminfo_t *bl33_meminfo)``
|
|
|
|
This region corresponds to the Non-Secure DDR-DRAM, excluding the
|
|
TZC-Secured area.
|
|
|
|
The location of the BL32 image will result in different memory maps. This is
|
|
illustrated for both FVP and Juno in the following diagrams, using the TSP as
|
|
an example.
|
|
|
|
Note: Loading the BL32 image in TZC secured DRAM doesn't change the memory
|
|
layout of the other images in Trusted SRAM.
|
|
|
|
**FVP with TSP in Trusted SRAM with firmware configs :**
|
|
(These diagrams only cover the AArch64 case)
|
|
|
|
::
|
|
|
|
DRAM
|
|
0xffffffff +----------+
|
|
: :
|
|
|----------|
|
|
|HW_CONFIG |
|
|
0x83000000 |----------| (non-secure)
|
|
| |
|
|
0x80000000 +----------+
|
|
|
|
Trusted SRAM
|
|
0x04040000 +----------+ loaded by BL2 +----------------+
|
|
| BL1 (rw) | <<<<<<<<<<<<< | |
|
|
|----------| <<<<<<<<<<<<< | BL31 NOBITS |
|
|
| BL2 | <<<<<<<<<<<<< | |
|
|
|----------| <<<<<<<<<<<<< |----------------|
|
|
| | <<<<<<<<<<<<< | BL31 PROGBITS |
|
|
| | <<<<<<<<<<<<< |----------------|
|
|
| | <<<<<<<<<<<<< | BL32 |
|
|
0x04002000 +----------+ +----------------+
|
|
|fw_configs|
|
|
0x04001000 +----------+
|
|
| Shared |
|
|
0x04000000 +----------+
|
|
|
|
Trusted ROM
|
|
0x04000000 +----------+
|
|
| BL1 (ro) |
|
|
0x00000000 +----------+
|
|
|
|
**FVP with TSP in Trusted DRAM with firmware configs (default option):**
|
|
|
|
::
|
|
|
|
DRAM
|
|
0xffffffff +--------------+
|
|
: :
|
|
|--------------|
|
|
| HW_CONFIG |
|
|
0x83000000 |--------------| (non-secure)
|
|
| |
|
|
0x80000000 +--------------+
|
|
|
|
Trusted DRAM
|
|
0x08000000 +--------------+
|
|
| BL32 |
|
|
0x06000000 +--------------+
|
|
|
|
Trusted SRAM
|
|
0x04040000 +--------------+ loaded by BL2 +----------------+
|
|
| BL1 (rw) | <<<<<<<<<<<<< | |
|
|
|--------------| <<<<<<<<<<<<< | BL31 NOBITS |
|
|
| BL2 | <<<<<<<<<<<<< | |
|
|
|--------------| <<<<<<<<<<<<< |----------------|
|
|
| | <<<<<<<<<<<<< | BL31 PROGBITS |
|
|
| | +----------------+
|
|
+--------------+
|
|
| fw_configs |
|
|
0x04001000 +--------------+
|
|
| Shared |
|
|
0x04000000 +--------------+
|
|
|
|
Trusted ROM
|
|
0x04000000 +--------------+
|
|
| BL1 (ro) |
|
|
0x00000000 +--------------+
|
|
|
|
**FVP with TSP in TZC-Secured DRAM with firmware configs :**
|
|
|
|
::
|
|
|
|
DRAM
|
|
0xffffffff +----------+
|
|
| BL32 | (secure)
|
|
0xff000000 +----------+
|
|
| |
|
|
|----------|
|
|
|HW_CONFIG |
|
|
0x83000000 |----------| (non-secure)
|
|
| |
|
|
0x80000000 +----------+
|
|
|
|
Trusted SRAM
|
|
0x04040000 +----------+ loaded by BL2 +----------------+
|
|
| BL1 (rw) | <<<<<<<<<<<<< | |
|
|
|----------| <<<<<<<<<<<<< | BL31 NOBITS |
|
|
| BL2 | <<<<<<<<<<<<< | |
|
|
|----------| <<<<<<<<<<<<< |----------------|
|
|
| | <<<<<<<<<<<<< | BL31 PROGBITS |
|
|
| | +----------------+
|
|
0x04002000 +----------+
|
|
|fw_configs|
|
|
0x04001000 +----------+
|
|
| Shared |
|
|
0x04000000 +----------+
|
|
|
|
Trusted ROM
|
|
0x04000000 +----------+
|
|
| BL1 (ro) |
|
|
0x00000000 +----------+
|
|
|
|
**Juno with BL32 in Trusted SRAM :**
|
|
|
|
::
|
|
|
|
Flash0
|
|
0x0C000000 +----------+
|
|
: :
|
|
0x0BED0000 |----------|
|
|
| BL1 (ro) |
|
|
0x0BEC0000 |----------|
|
|
: :
|
|
0x08000000 +----------+ BL31 is loaded
|
|
after SCP_BL2 has
|
|
Trusted SRAM been sent to SCP
|
|
0x04040000 +----------+ loaded by BL2 +----------------+
|
|
| BL1 (rw) | <<<<<<<<<<<<< | |
|
|
|----------| <<<<<<<<<<<<< | BL31 NOBITS |
|
|
| BL2 | <<<<<<<<<<<<< | |
|
|
|----------| <<<<<<<<<<<<< |----------------|
|
|
| SCP_BL2 | <<<<<<<<<<<<< | BL31 PROGBITS |
|
|
|----------| <<<<<<<<<<<<< |----------------|
|
|
| | <<<<<<<<<<<<< | BL32 |
|
|
| | +----------------+
|
|
| |
|
|
0x04001000 +----------+
|
|
| MHU |
|
|
0x04000000 +----------+
|
|
|
|
**Juno with BL32 in TZC-secured DRAM :**
|
|
|
|
::
|
|
|
|
DRAM
|
|
0xFFE00000 +----------+
|
|
| BL32 | (secure)
|
|
0xFF000000 |----------|
|
|
| |
|
|
: : (non-secure)
|
|
| |
|
|
0x80000000 +----------+
|
|
|
|
Flash0
|
|
0x0C000000 +----------+
|
|
: :
|
|
0x0BED0000 |----------|
|
|
| BL1 (ro) |
|
|
0x0BEC0000 |----------|
|
|
: :
|
|
0x08000000 +----------+ BL31 is loaded
|
|
after SCP_BL2 has
|
|
Trusted SRAM been sent to SCP
|
|
0x04040000 +----------+ loaded by BL2 +----------------+
|
|
| BL1 (rw) | <<<<<<<<<<<<< | |
|
|
|----------| <<<<<<<<<<<<< | BL31 NOBITS |
|
|
| BL2 | <<<<<<<<<<<<< | |
|
|
|----------| <<<<<<<<<<<<< |----------------|
|
|
| SCP_BL2 | <<<<<<<<<<<<< | BL31 PROGBITS |
|
|
|----------| +----------------+
|
|
0x04001000 +----------+
|
|
| MHU |
|
|
0x04000000 +----------+
|
|
|
|
Firmware Image Package (FIP)
|
|
----------------------------
|
|
|
|
Using a Firmware Image Package (FIP) allows for packing bootloader images (and
|
|
potentially other payloads) into a single archive that can be loaded by TF-A
|
|
from non-volatile platform storage. A driver to load images from a FIP has
|
|
been added to the storage layer and allows a package to be read from supported
|
|
platform storage. A tool to create Firmware Image Packages is also provided
|
|
and described below.
|
|
|
|
Firmware Image Package layout
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The FIP layout consists of a table of contents (ToC) followed by payload data.
|
|
The ToC itself has a header followed by one or more table entries. The ToC is
|
|
terminated by an end marker entry, and since the size of the ToC is 0 bytes,
|
|
the offset equals the total size of the FIP file. All ToC entries describe some
|
|
payload data that has been appended to the end of the binary package. With the
|
|
information provided in the ToC entry the corresponding payload data can be
|
|
retrieved.
|
|
|
|
::
|
|
|
|
------------------
|
|
| ToC Header |
|
|
|----------------|
|
|
| ToC Entry 0 |
|
|
|----------------|
|
|
| ToC Entry 1 |
|
|
|----------------|
|
|
| ToC End Marker |
|
|
|----------------|
|
|
| |
|
|
| Data 0 |
|
|
| |
|
|
|----------------|
|
|
| |
|
|
| Data 1 |
|
|
| |
|
|
------------------
|
|
|
|
The ToC header and entry formats are described in the header file
|
|
``include/tools_share/firmware_image_package.h``. This file is used by both the
|
|
tool and TF-A.
|
|
|
|
The ToC header has the following fields:
|
|
|
|
::
|
|
|
|
`name`: The name of the ToC. This is currently used to validate the header.
|
|
`serial_number`: A non-zero number provided by the creation tool
|
|
`flags`: Flags associated with this data.
|
|
Bits 0-31: Reserved
|
|
Bits 32-47: Platform defined
|
|
Bits 48-63: Reserved
|
|
|
|
A ToC entry has the following fields:
|
|
|
|
::
|
|
|
|
`uuid`: All files are referred to by a pre-defined Universally Unique
|
|
IDentifier [UUID] . The UUIDs are defined in
|
|
`include/tools_share/firmware_image_package.h`. The platform translates
|
|
the requested image name into the corresponding UUID when accessing the
|
|
package.
|
|
`offset_address`: The offset address at which the corresponding payload data
|
|
can be found. The offset is calculated from the ToC base address.
|
|
`size`: The size of the corresponding payload data in bytes.
|
|
`flags`: Flags associated with this entry. None are yet defined.
|
|
|
|
Firmware Image Package creation tool
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The FIP creation tool can be used to pack specified images into a binary
|
|
package that can be loaded by TF-A from platform storage. The tool currently
|
|
only supports packing bootloader images. Additional image definitions can be
|
|
added to the tool as required.
|
|
|
|
The tool can be found in ``tools/fiptool``.
|
|
|
|
Loading from a Firmware Image Package (FIP)
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The Firmware Image Package (FIP) driver can load images from a binary package on
|
|
non-volatile platform storage. For the Arm development platforms, this is
|
|
currently NOR FLASH.
|
|
|
|
Bootloader images are loaded according to the platform policy as specified by
|
|
the function ``plat_get_image_source()``. For the Arm development platforms, this
|
|
means the platform will attempt to load images from a Firmware Image Package
|
|
located at the start of NOR FLASH0.
|
|
|
|
The Arm development platforms' policy is to only allow loading of a known set of
|
|
images. The platform policy can be modified to allow additional images.
|
|
|
|
Use of coherent memory in TF-A
|
|
------------------------------
|
|
|
|
There might be loss of coherency when physical memory with mismatched
|
|
shareability, cacheability and memory attributes is accessed by multiple CPUs
|
|
(refer to section B2.9 of `Arm ARM`_ for more details). This possibility occurs
|
|
in TF-A during power up/down sequences when coherency, MMU and caches are
|
|
turned on/off incrementally.
|
|
|
|
TF-A defines coherent memory as a region of memory with Device nGnRE attributes
|
|
in the translation tables. The translation granule size in TF-A is 4KB. This
|
|
is the smallest possible size of the coherent memory region.
|
|
|
|
By default, all data structures which are susceptible to accesses with
|
|
mismatched attributes from various CPUs are allocated in a coherent memory
|
|
region (refer to section 2.1 of `Porting Guide`_). The coherent memory region
|
|
accesses are Outer Shareable, non-cacheable and they can be accessed
|
|
with the Device nGnRE attributes when the MMU is turned on. Hence, at the
|
|
expense of at least an extra page of memory, TF-A is able to work around
|
|
coherency issues due to mismatched memory attributes.
|
|
|
|
The alternative to the above approach is to allocate the susceptible data
|
|
structures in Normal WriteBack WriteAllocate Inner shareable memory. This
|
|
approach requires the data structures to be designed so that it is possible to
|
|
work around the issue of mismatched memory attributes by performing software
|
|
cache maintenance on them.
|
|
|
|
Disabling the use of coherent memory in TF-A
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
It might be desirable to avoid the cost of allocating coherent memory on
|
|
platforms which are memory constrained. TF-A enables inclusion of coherent
|
|
memory in firmware images through the build flag ``USE_COHERENT_MEM``.
|
|
This flag is enabled by default. It can be disabled to choose the second
|
|
approach described above.
|
|
|
|
The below sections analyze the data structures allocated in the coherent memory
|
|
region and the changes required to allocate them in normal memory.
|
|
|
|
Coherent memory usage in PSCI implementation
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The ``psci_non_cpu_pd_nodes`` data structure stores the platform's power domain
|
|
tree information for state management of power domains. By default, this data
|
|
structure is allocated in the coherent memory region in TF-A because it can be
|
|
accessed by multple CPUs, either with caches enabled or disabled.
|
|
|
|
.. code:: c
|
|
|
|
typedef struct non_cpu_pwr_domain_node {
|
|
/*
|
|
* Index of the first CPU power domain node level 0 which has this node
|
|
* as its parent.
|
|
*/
|
|
unsigned int cpu_start_idx;
|
|
|
|
/*
|
|
* Number of CPU power domains which are siblings of the domain indexed
|
|
* by 'cpu_start_idx' i.e. all the domains in the range 'cpu_start_idx
|
|
* -> cpu_start_idx + ncpus' have this node as their parent.
|
|
*/
|
|
unsigned int ncpus;
|
|
|
|
/*
|
|
* Index of the parent power domain node.
|
|
* TODO: Figure out whether to whether using pointer is more efficient.
|
|
*/
|
|
unsigned int parent_node;
|
|
|
|
plat_local_state_t local_state;
|
|
|
|
unsigned char level;
|
|
|
|
/* For indexing the psci_lock array*/
|
|
unsigned char lock_index;
|
|
} non_cpu_pd_node_t;
|
|
|
|
In order to move this data structure to normal memory, the use of each of its
|
|
fields must be analyzed. Fields like ``cpu_start_idx``, ``ncpus``, ``parent_node``
|
|
``level`` and ``lock_index`` are only written once during cold boot. Hence removing
|
|
them from coherent memory involves only doing a clean and invalidate of the
|
|
cache lines after these fields are written.
|
|
|
|
The field ``local_state`` can be concurrently accessed by multiple CPUs in
|
|
different cache states. A Lamport's Bakery lock ``psci_locks`` is used to ensure
|
|
mutual exlusion to this field and a clean and invalidate is needed after it
|
|
is written.
|
|
|
|
Bakery lock data
|
|
~~~~~~~~~~~~~~~~
|
|
|
|
The bakery lock data structure ``bakery_lock_t`` is allocated in coherent memory
|
|
and is accessed by multiple CPUs with mismatched attributes. ``bakery_lock_t`` is
|
|
defined as follows:
|
|
|
|
.. code:: c
|
|
|
|
typedef struct bakery_lock {
|
|
/*
|
|
* The lock_data is a bit-field of 2 members:
|
|
* Bit[0] : choosing. This field is set when the CPU is
|
|
* choosing its bakery number.
|
|
* Bits[1 - 15] : number. This is the bakery number allocated.
|
|
*/
|
|
volatile uint16_t lock_data[BAKERY_LOCK_MAX_CPUS];
|
|
} bakery_lock_t;
|
|
|
|
It is a characteristic of Lamport's Bakery algorithm that the volatile per-CPU
|
|
fields can be read by all CPUs but only written to by the owning CPU.
|
|
|
|
Depending upon the data cache line size, the per-CPU fields of the
|
|
``bakery_lock_t`` structure for multiple CPUs may exist on a single cache line.
|
|
These per-CPU fields can be read and written during lock contention by multiple
|
|
CPUs with mismatched memory attributes. Since these fields are a part of the
|
|
lock implementation, they do not have access to any other locking primitive to
|
|
safeguard against the resulting coherency issues. As a result, simple software
|
|
cache maintenance is not enough to allocate them in coherent memory. Consider
|
|
the following example.
|
|
|
|
CPU0 updates its per-CPU field with data cache enabled. This write updates a
|
|
local cache line which contains a copy of the fields for other CPUs as well. Now
|
|
CPU1 updates its per-CPU field of the ``bakery_lock_t`` structure with data cache
|
|
disabled. CPU1 then issues a DCIVAC operation to invalidate any stale copies of
|
|
its field in any other cache line in the system. This operation will invalidate
|
|
the update made by CPU0 as well.
|
|
|
|
To use bakery locks when ``USE_COHERENT_MEM`` is disabled, the lock data structure
|
|
has been redesigned. The changes utilise the characteristic of Lamport's Bakery
|
|
algorithm mentioned earlier. The bakery\_lock structure only allocates the memory
|
|
for a single CPU. The macro ``DEFINE_BAKERY_LOCK`` allocates all the bakery locks
|
|
needed for a CPU into a section ``bakery_lock``. The linker allocates the memory
|
|
for other cores by using the total size allocated for the bakery\_lock section
|
|
and multiplying it with (PLATFORM\_CORE\_COUNT - 1). This enables software to
|
|
perform software cache maintenance on the lock data structure without running
|
|
into coherency issues associated with mismatched attributes.
|
|
|
|
The bakery lock data structure ``bakery_info_t`` is defined for use when
|
|
``USE_COHERENT_MEM`` is disabled as follows:
|
|
|
|
.. code:: c
|
|
|
|
typedef struct bakery_info {
|
|
/*
|
|
* The lock_data is a bit-field of 2 members:
|
|
* Bit[0] : choosing. This field is set when the CPU is
|
|
* choosing its bakery number.
|
|
* Bits[1 - 15] : number. This is the bakery number allocated.
|
|
*/
|
|
volatile uint16_t lock_data;
|
|
} bakery_info_t;
|
|
|
|
The ``bakery_info_t`` represents a single per-CPU field of one lock and
|
|
the combination of corresponding ``bakery_info_t`` structures for all CPUs in the
|
|
system represents the complete bakery lock. The view in memory for a system
|
|
with n bakery locks are:
|
|
|
|
::
|
|
|
|
bakery_lock section start
|
|
|----------------|
|
|
| `bakery_info_t`| <-- Lock_0 per-CPU field
|
|
| Lock_0 | for CPU0
|
|
|----------------|
|
|
| `bakery_info_t`| <-- Lock_1 per-CPU field
|
|
| Lock_1 | for CPU0
|
|
|----------------|
|
|
| .... |
|
|
|----------------|
|
|
| `bakery_info_t`| <-- Lock_N per-CPU field
|
|
| Lock_N | for CPU0
|
|
------------------
|
|
| XXXXX |
|
|
| Padding to |
|
|
| next Cache WB | <--- Calculate PERCPU_BAKERY_LOCK_SIZE, allocate
|
|
| Granule | continuous memory for remaining CPUs.
|
|
------------------
|
|
| `bakery_info_t`| <-- Lock_0 per-CPU field
|
|
| Lock_0 | for CPU1
|
|
|----------------|
|
|
| `bakery_info_t`| <-- Lock_1 per-CPU field
|
|
| Lock_1 | for CPU1
|
|
|----------------|
|
|
| .... |
|
|
|----------------|
|
|
| `bakery_info_t`| <-- Lock_N per-CPU field
|
|
| Lock_N | for CPU1
|
|
------------------
|
|
| XXXXX |
|
|
| Padding to |
|
|
| next Cache WB |
|
|
| Granule |
|
|
------------------
|
|
|
|
Consider a system of 2 CPUs with 'N' bakery locks as shown above. For an
|
|
operation on Lock\_N, the corresponding ``bakery_info_t`` in both CPU0 and CPU1
|
|
``bakery_lock`` section need to be fetched and appropriate cache operations need
|
|
to be performed for each access.
|
|
|
|
On Arm Platforms, bakery locks are used in psci (``psci_locks``) and power controller
|
|
driver (``arm_lock``).
|
|
|
|
Non Functional Impact of removing coherent memory
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Removal of the coherent memory region leads to the additional software overhead
|
|
of performing cache maintenance for the affected data structures. However, since
|
|
the memory where the data structures are allocated is cacheable, the overhead is
|
|
mostly mitigated by an increase in performance.
|
|
|
|
There is however a performance impact for bakery locks, due to:
|
|
|
|
- Additional cache maintenance operations, and
|
|
- Multiple cache line reads for each lock operation, since the bakery locks
|
|
for each CPU are distributed across different cache lines.
|
|
|
|
The implementation has been optimized to minimize this additional overhead.
|
|
Measurements indicate that when bakery locks are allocated in Normal memory, the
|
|
minimum latency of acquiring a lock is on an average 3-4 micro seconds whereas
|
|
in Device memory the same is 2 micro seconds. The measurements were done on the
|
|
Juno Arm development platform.
|
|
|
|
As mentioned earlier, almost a page of memory can be saved by disabling
|
|
``USE_COHERENT_MEM``. Each platform needs to consider these trade-offs to decide
|
|
whether coherent memory should be used. If a platform disables
|
|
``USE_COHERENT_MEM`` and needs to use bakery locks in the porting layer, it can
|
|
optionally define macro ``PLAT_PERCPU_BAKERY_LOCK_SIZE`` (see the
|
|
`Porting Guide`_). Refer to the reference platform code for examples.
|
|
|
|
Isolating code and read-only data on separate memory pages
|
|
----------------------------------------------------------
|
|
|
|
In the Armv8-A VMSA, translation table entries include fields that define the
|
|
properties of the target memory region, such as its access permissions. The
|
|
smallest unit of memory that can be addressed by a translation table entry is
|
|
a memory page. Therefore, if software needs to set different permissions on two
|
|
memory regions then it needs to map them using different memory pages.
|
|
|
|
The default memory layout for each BL image is as follows:
|
|
|
|
::
|
|
|
|
| ... |
|
|
+-------------------+
|
|
| Read-write data |
|
|
+-------------------+ Page boundary
|
|
| <Padding> |
|
|
+-------------------+
|
|
| Exception vectors |
|
|
+-------------------+ 2 KB boundary
|
|
| <Padding> |
|
|
+-------------------+
|
|
| Read-only data |
|
|
+-------------------+
|
|
| Code |
|
|
+-------------------+ BLx_BASE
|
|
|
|
Note: The 2KB alignment for the exception vectors is an architectural
|
|
requirement.
|
|
|
|
The read-write data start on a new memory page so that they can be mapped with
|
|
read-write permissions, whereas the code and read-only data below are configured
|
|
as read-only.
|
|
|
|
However, the read-only data are not aligned on a page boundary. They are
|
|
contiguous to the code. Therefore, the end of the code section and the beginning
|
|
of the read-only data one might share a memory page. This forces both to be
|
|
mapped with the same memory attributes. As the code needs to be executable, this
|
|
means that the read-only data stored on the same memory page as the code are
|
|
executable as well. This could potentially be exploited as part of a security
|
|
attack.
|
|
|
|
TF provides the build flag ``SEPARATE_CODE_AND_RODATA`` to isolate the code and
|
|
read-only data on separate memory pages. This in turn allows independent control
|
|
of the access permissions for the code and read-only data. In this case,
|
|
platform code gets a finer-grained view of the image layout and can
|
|
appropriately map the code region as executable and the read-only data as
|
|
execute-never.
|
|
|
|
This has an impact on memory footprint, as padding bytes need to be introduced
|
|
between the code and read-only data to ensure the segragation of the two. To
|
|
limit the memory cost, this flag also changes the memory layout such that the
|
|
code and exception vectors are now contiguous, like so:
|
|
|
|
::
|
|
|
|
| ... |
|
|
+-------------------+
|
|
| Read-write data |
|
|
+-------------------+ Page boundary
|
|
| <Padding> |
|
|
+-------------------+
|
|
| Read-only data |
|
|
+-------------------+ Page boundary
|
|
| <Padding> |
|
|
+-------------------+
|
|
| Exception vectors |
|
|
+-------------------+ 2 KB boundary
|
|
| <Padding> |
|
|
+-------------------+
|
|
| Code |
|
|
+-------------------+ BLx_BASE
|
|
|
|
With this more condensed memory layout, the separation of read-only data will
|
|
add zero or one page to the memory footprint of each BL image. Each platform
|
|
should consider the trade-off between memory footprint and security.
|
|
|
|
This build flag is disabled by default, minimising memory footprint. On Arm
|
|
platforms, it is enabled.
|
|
|
|
Publish and Subscribe Framework
|
|
-------------------------------
|
|
|
|
The Publish and Subscribe Framework allows EL3 components to define and publish
|
|
events, to which other EL3 components can subscribe.
|
|
|
|
The following macros are provided by the framework:
|
|
|
|
- ``REGISTER_PUBSUB_EVENT(event)``: Defines an event, and takes one argument,
|
|
the event name, which must be a valid C identifier. All calls to
|
|
``REGISTER_PUBSUB_EVENT`` macro must be placed in the file
|
|
``pubsub_events.h``.
|
|
|
|
- ``PUBLISH_EVENT_ARG(event, arg)``: Publishes a defined event, by iterating
|
|
subscribed handlers and calling them in turn. The handlers will be passed the
|
|
parameter ``arg``. The expected use-case is to broadcast an event.
|
|
|
|
- ``PUBLISH_EVENT(event)``: Like ``PUBLISH_EVENT_ARG``, except that the value
|
|
``NULL`` is passed to subscribed handlers.
|
|
|
|
- ``SUBSCRIBE_TO_EVENT(event, handler)``: Registers the ``handler`` to
|
|
subscribe to ``event``. The handler will be executed whenever the ``event``
|
|
is published.
|
|
|
|
- ``for_each_subscriber(event, subscriber)``: Iterates through all handlers
|
|
subscribed for ``event``. ``subscriber`` must be a local variable of type
|
|
``pubsub_cb_t *``, and will point to each subscribed handler in turn during
|
|
iteration. This macro can be used for those patterns that none of the
|
|
``PUBLISH_EVENT_*()`` macros cover.
|
|
|
|
Publishing an event that wasn't defined using ``REGISTER_PUBSUB_EVENT`` will
|
|
result in build error. Subscribing to an undefined event however won't.
|
|
|
|
Subscribed handlers must be of type ``pubsub_cb_t``, with following function
|
|
signature:
|
|
|
|
::
|
|
|
|
typedef void* (*pubsub_cb_t)(const void *arg);
|
|
|
|
There may be arbitrary number of handlers registered to the same event. The
|
|
order in which subscribed handlers are notified when that event is published is
|
|
not defined. Subscribed handlers may be executed in any order; handlers should
|
|
not assume any relative ordering amongst them.
|
|
|
|
Publishing an event on a PE will result in subscribed handlers executing on that
|
|
PE only; it won't cause handlers to execute on a different PE.
|
|
|
|
Note that publishing an event on a PE blocks until all the subscribed handlers
|
|
finish executing on the PE.
|
|
|
|
TF-A generic code publishes and subscribes to some events within. Platform
|
|
ports are discouraged from subscribing to them. These events may be withdrawn,
|
|
renamed, or have their semantics altered in the future. Platforms may however
|
|
register, publish, and subscribe to platform-specific events.
|
|
|
|
Publish and Subscribe Example
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
A publisher that wants to publish event ``foo`` would:
|
|
|
|
- Define the event ``foo`` in the ``pubsub_events.h``.
|
|
|
|
::
|
|
|
|
REGISTER_PUBSUB_EVENT(foo);
|
|
|
|
- Depending on the nature of event, use one of ``PUBLISH_EVENT_*()`` macros to
|
|
publish the event at the appropriate path and time of execution.
|
|
|
|
A subscriber that wants to subscribe to event ``foo`` published above would
|
|
implement:
|
|
|
|
::
|
|
|
|
void *foo_handler(const void *arg)
|
|
{
|
|
void *result;
|
|
|
|
/* Do handling ... */
|
|
|
|
return result;
|
|
}
|
|
|
|
SUBSCRIBE_TO_EVENT(foo, foo_handler);
|
|
|
|
Performance Measurement Framework
|
|
---------------------------------
|
|
|
|
The Performance Measurement Framework (PMF) facilitates collection of
|
|
timestamps by registered services and provides interfaces to retrieve them
|
|
from within TF-A. A platform can choose to expose appropriate SMCs to
|
|
retrieve these collected timestamps.
|
|
|
|
By default, the global physical counter is used for the timestamp
|
|
value and is read via ``CNTPCT_EL0``. The framework allows to retrieve
|
|
timestamps captured by other CPUs.
|
|
|
|
Timestamp identifier format
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
A PMF timestamp is uniquely identified across the system via the
|
|
timestamp ID or ``tid``. The ``tid`` is composed as follows:
|
|
|
|
::
|
|
|
|
Bits 0-7: The local timestamp identifier.
|
|
Bits 8-9: Reserved.
|
|
Bits 10-15: The service identifier.
|
|
Bits 16-31: Reserved.
|
|
|
|
#. The service identifier. Each PMF service is identified by a
|
|
service name and a service identifier. Both the service name and
|
|
identifier are unique within the system as a whole.
|
|
|
|
#. The local timestamp identifier. This identifier is unique within a given
|
|
service.
|
|
|
|
Registering a PMF service
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
To register a PMF service, the ``PMF_REGISTER_SERVICE()`` macro from ``pmf.h``
|
|
is used. The arguments required are the service name, the service ID,
|
|
the total number of local timestamps to be captured and a set of flags.
|
|
|
|
The ``flags`` field can be specified as a bitwise-OR of the following values:
|
|
|
|
::
|
|
|
|
PMF_STORE_ENABLE: The timestamp is stored in memory for later retrieval.
|
|
PMF_DUMP_ENABLE: The timestamp is dumped on the serial console.
|
|
|
|
The ``PMF_REGISTER_SERVICE()`` reserves memory to store captured
|
|
timestamps in a PMF specific linker section at build time.
|
|
Additionally, it defines necessary functions to capture and
|
|
retrieve a particular timestamp for the given service at runtime.
|
|
|
|
The macro ``PMF_REGISTER_SERVICE()`` only enables capturing PMF timestamps
|
|
from within TF-A. In order to retrieve timestamps from outside of TF-A, the
|
|
``PMF_REGISTER_SERVICE_SMC()`` macro must be used instead. This macro
|
|
accepts the same set of arguments as the ``PMF_REGISTER_SERVICE()``
|
|
macro but additionally supports retrieving timestamps using SMCs.
|
|
|
|
Capturing a timestamp
|
|
~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
PMF timestamps are stored in a per-service timestamp region. On a
|
|
system with multiple CPUs, each timestamp is captured and stored
|
|
in a per-CPU cache line aligned memory region.
|
|
|
|
Having registered the service, the ``PMF_CAPTURE_TIMESTAMP()`` macro can be
|
|
used to capture a timestamp at the location where it is used. The macro
|
|
takes the service name, a local timestamp identifier and a flag as arguments.
|
|
|
|
The ``flags`` field argument can be zero, or ``PMF_CACHE_MAINT`` which
|
|
instructs PMF to do cache maintenance following the capture. Cache
|
|
maintenance is required if any of the service's timestamps are captured
|
|
with data cache disabled.
|
|
|
|
To capture a timestamp in assembly code, the caller should use
|
|
``pmf_calc_timestamp_addr`` macro (defined in ``pmf_asm_macros.S``) to
|
|
calculate the address of where the timestamp would be stored. The
|
|
caller should then read ``CNTPCT_EL0`` register to obtain the timestamp
|
|
and store it at the determined address for later retrieval.
|
|
|
|
Retrieving a timestamp
|
|
~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
From within TF-A, timestamps for individual CPUs can be retrieved using either
|
|
``PMF_GET_TIMESTAMP_BY_MPIDR()`` or ``PMF_GET_TIMESTAMP_BY_INDEX()`` macros.
|
|
These macros accept the CPU's MPIDR value, or its ordinal position
|
|
respectively.
|
|
|
|
From outside TF-A, timestamps for individual CPUs can be retrieved by calling
|
|
into ``pmf_smc_handler()``.
|
|
|
|
.. code:: c
|
|
|
|
Interface : pmf_smc_handler()
|
|
Argument : unsigned int smc_fid, u_register_t x1,
|
|
u_register_t x2, u_register_t x3,
|
|
u_register_t x4, void *cookie,
|
|
void *handle, u_register_t flags
|
|
Return : uintptr_t
|
|
|
|
smc_fid: Holds the SMC identifier which is either `PMF_SMC_GET_TIMESTAMP_32`
|
|
when the caller of the SMC is running in AArch32 mode
|
|
or `PMF_SMC_GET_TIMESTAMP_64` when the caller is running in AArch64 mode.
|
|
x1: Timestamp identifier.
|
|
x2: The `mpidr` of the CPU for which the timestamp has to be retrieved.
|
|
This can be the `mpidr` of a different core to the one initiating
|
|
the SMC. In that case, service specific cache maintenance may be
|
|
required to ensure the updated copy of the timestamp is returned.
|
|
x3: A flags value that is either 0 or `PMF_CACHE_MAINT`. If
|
|
`PMF_CACHE_MAINT` is passed, then the PMF code will perform a
|
|
cache invalidate before reading the timestamp. This ensures
|
|
an updated copy is returned.
|
|
|
|
The remaining arguments, ``x4``, ``cookie``, ``handle`` and ``flags`` are unused
|
|
in this implementation.
|
|
|
|
PMF code structure
|
|
~~~~~~~~~~~~~~~~~~
|
|
|
|
#. ``pmf_main.c`` consists of core functions that implement service registration,
|
|
initialization, storing, dumping and retrieving timestamps.
|
|
|
|
#. ``pmf_smc.c`` contains the SMC handling for registered PMF services.
|
|
|
|
#. ``pmf.h`` contains the public interface to Performance Measurement Framework.
|
|
|
|
#. ``pmf_asm_macros.S`` consists of macros to facilitate capturing timestamps in
|
|
assembly code.
|
|
|
|
#. ``pmf_helpers.h`` is an internal header used by ``pmf.h``.
|
|
|
|
Armv8-A Architecture Extensions
|
|
-------------------------------
|
|
|
|
TF-A makes use of Armv8-A Architecture Extensions where applicable. This
|
|
section lists the usage of Architecture Extensions, and build flags
|
|
controlling them.
|
|
|
|
In general, and unless individually mentioned, the build options
|
|
``ARM_ARCH_MAJOR`` and ``ARM_ARCH_MINOR`` selects the Architecture Extension to
|
|
target when building TF-A. Subsequent Arm Architecture Extensions are backward
|
|
compatible with previous versions.
|
|
|
|
The build system only requires that ``ARM_ARCH_MAJOR`` and ``ARM_ARCH_MINOR`` have a
|
|
valid numeric value. These build options only control whether or not
|
|
Architecture Extension-specific code is included in the build. Otherwise, TF-A
|
|
targets the base Armv8.0-A architecture; i.e. as if ``ARM_ARCH_MAJOR`` == 8
|
|
and ``ARM_ARCH_MINOR`` == 0, which are also their respective default values.
|
|
|
|
See also the *Summary of build options* in `User Guide`_.
|
|
|
|
For details on the Architecture Extension and available features, please refer
|
|
to the respective Architecture Extension Supplement.
|
|
|
|
Armv8.1-A
|
|
~~~~~~~~~
|
|
|
|
This Architecture Extension is targeted when ``ARM_ARCH_MAJOR`` >= 8, or when
|
|
``ARM_ARCH_MAJOR`` == 8 and ``ARM_ARCH_MINOR`` >= 1.
|
|
|
|
- The Compare and Swap instruction is used to implement spinlocks. Otherwise,
|
|
the load-/store-exclusive instruction pair is used.
|
|
|
|
Armv8.2-A
|
|
~~~~~~~~~
|
|
|
|
This Architecture Extension is targeted when ``ARM_ARCH_MAJOR`` == 8 and
|
|
``ARM_ARCH_MINOR`` >= 2.
|
|
|
|
- The Common not Private (CnP) bit is enabled to indicate that multiple
|
|
Processing Elements in the same Inner Shareable domain use the same
|
|
translation table entries for a given stage of translation for a particular
|
|
translation regime.
|
|
|
|
Armv7-A
|
|
~~~~~~~
|
|
|
|
This Architecture Extension is targeted when ``ARM_ARCH_MAJOR`` == 7.
|
|
|
|
There are several Armv7-A extensions available. Obviously the TrustZone
|
|
extension is mandatory to support the TF-A bootloader and runtime services.
|
|
|
|
Platform implementing an Armv7-A system can to define from its target
|
|
Cortex-A architecture through ``ARM_CORTEX_A<X> = yes`` in their
|
|
``plaform.mk`` script. For example ``ARM_CORTEX_A15=yes`` for a
|
|
Cortex-A15 target.
|
|
|
|
Platform can also set ``ARM_WITH_NEON=yes`` to enable neon support.
|
|
Note that using neon at runtime has constraints on non secure wolrd context.
|
|
TF-A does not yet provide VFP context management.
|
|
|
|
Directive ``ARM_CORTEX_A<x>`` and ``ARM_WITH_NEON`` are used to set
|
|
the toolchain target architecture directive.
|
|
|
|
Platform may choose to not define straight the toolchain target architecture
|
|
directive by defining ``MARCH32_DIRECTIVE``.
|
|
I.e:
|
|
|
|
::
|
|
|
|
MARCH32_DIRECTIVE := -mach=armv7-a
|
|
|
|
Code Structure
|
|
--------------
|
|
|
|
TF-A code is logically divided between the three boot loader stages mentioned
|
|
in the previous sections. The code is also divided into the following
|
|
categories (present as directories in the source code):
|
|
|
|
- **Platform specific.** Choice of architecture specific code depends upon
|
|
the platform.
|
|
- **Common code.** This is platform and architecture agnostic code.
|
|
- **Library code.** This code comprises of functionality commonly used by all
|
|
other code. The PSCI implementation and other EL3 runtime frameworks reside
|
|
as Library components.
|
|
- **Stage specific.** Code specific to a boot stage.
|
|
- **Drivers.**
|
|
- **Services.** EL3 runtime services (eg: SPD). Specific SPD services
|
|
reside in the ``services/spd`` directory (e.g. ``services/spd/tspd``).
|
|
|
|
Each boot loader stage uses code from one or more of the above mentioned
|
|
categories. Based upon the above, the code layout looks like this:
|
|
|
|
::
|
|
|
|
Directory Used by BL1? Used by BL2? Used by BL31?
|
|
bl1 Yes No No
|
|
bl2 No Yes No
|
|
bl31 No No Yes
|
|
plat Yes Yes Yes
|
|
drivers Yes No Yes
|
|
common Yes Yes Yes
|
|
lib Yes Yes Yes
|
|
services No No Yes
|
|
|
|
The build system provides a non configurable build option IMAGE\_BLx for each
|
|
boot loader stage (where x = BL stage). e.g. for BL1 , IMAGE\_BL1 will be
|
|
defined by the build system. This enables TF-A to compile certain code only
|
|
for specific boot loader stages
|
|
|
|
All assembler files have the ``.S`` extension. The linker source files for each
|
|
boot stage have the extension ``.ld.S``. These are processed by GCC to create the
|
|
linker scripts which have the extension ``.ld``.
|
|
|
|
FDTs provide a description of the hardware platform and are used by the Linux
|
|
kernel at boot time. These can be found in the ``fdts`` directory.
|
|
|
|
References
|
|
----------
|
|
|
|
.. [#] Trusted Board Boot Requirements CLIENT PDD (Arm DEN0006C-1). Available
|
|
under NDA through your Arm account representative.
|
|
.. [#] `Power State Coordination Interface PDD`_
|
|
.. [#] `SMC Calling Convention PDD`_
|
|
.. [#] `TF-A Interrupt Management Design guide`_.
|
|
|
|
--------------
|
|
|
|
*Copyright (c) 2013-2018, Arm Limited and Contributors. All rights reserved.*
|
|
|
|
.. _Reset Design: ./reset-design.rst
|
|
.. _Porting Guide: ./porting-guide.rst
|
|
.. _Firmware Update: ./firmware-update.rst
|
|
.. _PSCI PDD: http://infocenter.arm.com/help/topic/com.arm.doc.den0022d/Power_State_Coordination_Interface_PDD_v1_1_DEN0022D.pdf
|
|
.. _SMC calling convention PDD: http://infocenter.arm.com/help/topic/com.arm.doc.den0028b/ARM_DEN0028B_SMC_Calling_Convention.pdf
|
|
.. _PSCI Library integration guide: ./psci-lib-integration-guide.rst
|
|
.. _SMCCC: http://infocenter.arm.com/help/topic/com.arm.doc.den0028b/ARM_DEN0028B_SMC_Calling_Convention.pdf
|
|
.. _PSCI: http://infocenter.arm.com/help/topic/com.arm.doc.den0022d/Power_State_Coordination_Interface_PDD_v1_1_DEN0022D.pdf
|
|
.. _Power State Coordination Interface PDD: http://infocenter.arm.com/help/topic/com.arm.doc.den0022d/Power_State_Coordination_Interface_PDD_v1_1_DEN0022D.pdf
|
|
.. _here: ./psci-lib-integration-guide.rst
|
|
.. _cpu-specific-build-macros.rst: ./cpu-specific-build-macros.rst
|
|
.. _CPUBM: ./cpu-specific-build-macros.rst
|
|
.. _Arm ARM: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0487a.e/index.html
|
|
.. _User Guide: ./user-guide.rst
|
|
.. _SMC Calling Convention PDD: http://infocenter.arm.com/help/topic/com.arm.doc.den0028b/ARM_DEN0028B_SMC_Calling_Convention.pdf
|
|
.. _TF-A Interrupt Management Design guide: ./interrupt-framework-design.rst
|
|
.. _Xlat_tables design: xlat-tables-lib-v2-design.rst
|
|
|
|
.. |Image 1| image:: diagrams/rt-svc-descs-layout.png?raw=true
|
|
|