Merge "fix(xilinx): add API to get powerdown request status" into integration
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..858e0c1
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,4 @@
+[submodule "libtl"]
+ path = contrib/libtl
+ url = https://review.trustedfirmware.org/shared/transfer-list-library
+ shallow = true
diff --git a/Makefile b/Makefile
index cbf0a8a..88c7b69 100644
--- a/Makefile
+++ b/Makefile
@@ -1114,6 +1114,10 @@
$(info DICE_PROTECTION_ENVIRONMENT is an experimental feature)
endif
+ifeq (${LFA_SUPPORT},1)
+ $(warning LFA_SUPPORT is an experimental feature)
+endif #(LFA_SUPPORT)
+
################################################################################
# Process platform overrideable behaviour
################################################################################
@@ -1315,6 +1319,7 @@
EARLY_CONSOLE \
PRESERVE_DSU_PMU_REGS \
HOB_LIST \
+ LFA_SUPPORT \
)))
# Numeric_Flags
@@ -1545,6 +1550,7 @@
EARLY_CONSOLE \
PRESERVE_DSU_PMU_REGS \
HOB_LIST \
+ LFA_SUPPORT \
)))
ifeq (${PLATFORM_REPORT_CTX_MEM_USE}, 1)
@@ -1832,7 +1838,7 @@
memmap: all
$(if $(host-poetry),$(q)poetry -q install --no-root)
- $(q)$(if $(host-poetry),poetry run )memory -sr ${BUILD_PLAT}
+ $(q)$(if $(host-poetry),poetry run )memory symbols --root ${BUILD_PLAT}
tl: ${BUILD_PLAT}/tl.bin
${BUILD_PLAT}/tl.bin: ${HW_CONFIG}
diff --git a/bl31/bl31.mk b/bl31/bl31.mk
index e390915..d267b11 100644
--- a/bl31/bl31.mk
+++ b/bl31/bl31.mk
@@ -164,6 +164,10 @@
${RMMD_SOURCES}
endif
+ifeq (${USE_DSU_DRIVER},1)
+BL31_SOURCES += drivers/arm/dsu/dsu.c
+endif
+
ifeq ($(FEATURE_DETECTION),1)
BL31_SOURCES += common/feat_detect.c
endif
@@ -177,6 +181,11 @@
${MBEDTLS_SOURCES}
endif
+ifeq (${LFA_SUPPORT},1)
+include services/std_svc/lfa/lfa.mk
+BL31_SOURCES += ${LFA_SOURCES}
+endif
+
ifeq ($(CROS_WIDEVINE_SMC),1)
BL31_SOURCES += services/oem/chromeos/widevine_smc_handlers.c
endif
@@ -200,11 +209,13 @@
CRASH_REPORTING \
EL3_EXCEPTION_HANDLING \
SDEI_SUPPORT \
+ USE_DSU_DRIVER \
)))
$(eval $(call add_defines,\
$(sort \
- CRASH_REPORTING \
- EL3_EXCEPTION_HANDLING \
- SDEI_SUPPORT \
+ CRASH_REPORTING \
+ EL3_EXCEPTION_HANDLING \
+ SDEI_SUPPORT \
+ USE_DSU_DRIVER \
)))
diff --git a/bl31/bl31_main.c b/bl31/bl31_main.c
index a9f89fc..ba26366 100644
--- a/bl31/bl31_main.c
+++ b/bl31/bl31_main.c
@@ -17,6 +17,7 @@
#include <common/debug.h>
#include <common/feat_detect.h>
#include <common/runtime_svc.h>
+#include <drivers/arm/dsu.h>
#include <drivers/arm/gic.h>
#include <drivers/console.h>
#include <lib/bootmarker_capture.h>
@@ -56,7 +57,7 @@
* Variable to indicate whether next image to execute after BL31 is BL33
* (non-secure & default) or BL32 (secure).
******************************************************************************/
-static uint32_t next_image_type = NON_SECURE;
+static uint32_t next_image_type = (uint32_t)NON_SECURE;
#ifdef SUPPORT_UNKNOWN_MPID
/*
@@ -146,6 +147,10 @@
/* Perform platform setup in BL31 */
bl31_platform_setup();
+#if USE_DSU_DRIVER
+ dsu_driver_init(&plat_dsu_data);
+#endif
+
#if USE_GIC_DRIVER
/*
* Initialize the GIC driver as well as per-cpu and global interfaces.
diff --git a/changelog.yaml b/changelog.yaml
index f2f821e..e0a4f42 100644
--- a/changelog.yaml
+++ b/changelog.yaml
@@ -775,6 +775,9 @@
- title: ChromeOS
scope: cros
+ - title: Live Firmware Activation
+ scope: lfa
+
- title: Secure Payload Dispatcher
scope: spd
@@ -840,7 +843,7 @@
- title: ROMlib
scope: romlib
- - title: GPT
+ - title: Granule Protection Tables
scope: gpt
deprecated:
diff --git a/common/tf_log.c b/common/tf_log.c
index bef1739..f678975 100644
--- a/common/tf_log.c
+++ b/common/tf_log.c
@@ -12,7 +12,7 @@
#include <plat/common/platform.h>
/* Set the default maximum log level to the `LOG_LEVEL` build flag */
-static unsigned int max_log_level = LOG_LEVEL;
+static uint32_t max_log_level = LOG_LEVEL;
/*
* The common log function which is invoked by TF-A code.
@@ -23,12 +23,12 @@
*/
void tf_log(const char *fmt, ...)
{
- unsigned int log_level;
+ uint32_t log_level;
va_list args;
const char *prefix_str;
/* We expect the LOG_MARKER_* macro as the first character */
- log_level = fmt[0];
+ log_level = (uint32_t)fmt[0];
/* Verify that log_level is one of LOG_MARKER_* macro defined in debug.h */
assert((log_level > 0U) && (log_level <= LOG_LEVEL_VERBOSE));
@@ -40,7 +40,7 @@
prefix_str = plat_log_get_prefix(log_level);
while (*prefix_str != '\0') {
- (void)putchar(*prefix_str);
+ (void)putchar((int)*prefix_str);
prefix_str++;
}
@@ -51,7 +51,7 @@
void tf_log_newline(const char log_fmt[2])
{
- unsigned int log_level = log_fmt[0];
+ uint32_t log_level = (uint32_t)log_fmt[0];
/* Verify that log_level is one of LOG_MARKER_* macro defined in debug.h */
assert((log_level > 0U) && (log_level <= LOG_LEVEL_VERBOSE));
@@ -69,12 +69,12 @@
* maximum log level is determined by `LOG_LEVEL` build flag at compile time
* and this helper can set a lower (or equal) log level than the one at compile.
*/
-void tf_log_set_max_level(unsigned int log_level)
+void tf_log_set_max_level(uint32_t log_level)
{
assert(log_level <= LOG_LEVEL_VERBOSE);
assert((log_level % 10U) == 0U);
/* Cap log_level to the compile time maximum. */
- if (log_level <= (unsigned int)LOG_LEVEL)
+ if (log_level <= (uint32_t)LOG_LEVEL)
max_log_level = log_level;
}
diff --git a/contrib/libtl b/contrib/libtl
new file mode 160000
index 0000000..67d85f1
--- /dev/null
+++ b/contrib/libtl
@@ -0,0 +1 @@
+Subproject commit 67d85f181b726d2823eea43e8bb4ffb97559d348
diff --git a/docs/about/maintainers.rst b/docs/about/maintainers.rst
index 10b5c16..569f932 100644
--- a/docs/about/maintainers.rst
+++ b/docs/about/maintainers.rst
@@ -507,6 +507,14 @@
:|F|: include/services/arm_arch_svc.h
:|F|: include/services/std_svc.h
+Live Firmware Activation Service
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+:|M|: Manish Badarkhe <manish.badarkhe@arm.com>
+:|G|: `ManishVB-Arm`_
+:|F|: services/std_svc/lfa
+:|F|: include/plat/common/plat_lfa.h
+:|F|: include/services/lfa_svc.h
+
Platform Ports
~~~~~~~~~~~~~~
diff --git a/docs/design/firmware-design.rst b/docs/design/firmware-design.rst
index 1306ecb..669bd96 100644
--- a/docs/design/firmware-design.rst
+++ b/docs/design/firmware-design.rst
@@ -1056,6 +1056,18 @@
integrating PSCI library with AArch32 EL3 Runtime Software can be found
at :ref:`PSCI Library Integration guide for Armv8-A AArch32 systems`.
+DSU driver
+----------
+
+Platforms that include a DSU (DynamIQ Shared Unit) can define
+the ``USE_DSU_DRIVER`` build flag to enable the DSU driver.
+This driver is responsible for configuring DSU-related powerdown
+and power feature settings using ``dsu_driver_init()`` and for
+preserving the context of DSU PMU system registers.
+
+To support the DSU driver, platforms must define the ``plat_dsu_data``
+structure.
+
.. _firmware_design_sel1_spd:
Secure-EL1 Payloads and Dispatchers
diff --git a/docs/design/interrupt-framework-design.rst b/docs/design/interrupt-framework-design.rst
index dfb2eac..515cf5e 100644
--- a/docs/design/interrupt-framework-design.rst
+++ b/docs/design/interrupt-framework-design.rst
@@ -649,7 +649,7 @@
.. code:: c
- uint32_t plat_ic_get_interrupt_type(void);
+ uint32_t plat_ic_get_pending_interrupt_type(void);
It should return either ``INTR_TYPE_S_EL1`` or ``INTR_TYPE_NS``.
diff --git a/docs/getting_started/build-options.rst b/docs/getting_started/build-options.rst
index e80f3d1..f99840b 100644
--- a/docs/getting_started/build-options.rst
+++ b/docs/getting_started/build-options.rst
@@ -1119,6 +1119,11 @@
(Coherent memory region is included) or 0 (Coherent memory region is
excluded). Default is 1.
+- ``USE_DSU_DRIVER``: This flag enables DSU (DynamIQ Shared Unit) driver.
+ The DSU driver allows save/restore of DSU PMU registers through
+ ``PRESERVE_DSU_PMU_REGS`` build option and allows platforms to
+ configure powerdown and power settings of DSU.
+
- ``ARM_IO_IN_DTB``: This flag determines whether to use IO based on the
firmware configuration framework. This will move the io_policies into a
configuration device tree, instead of static structure in the code base.
@@ -1497,6 +1502,9 @@
per the `PSA Crypto API specification`_. This feature is only supported if
using MbedTLS 3.x version. It is disabled (``0``) by default.
+- ``LFA_SUPPORT``: Boolean flag to enable support for Live Firmware
+ activation as per the specification. This option defaults to 0.
+
- ``TRANSFER_LIST``: Setting this to ``1`` enables support for Firmware
Handoff using Transfer List defined in `Firmware Handoff specification`_.
This defaults to ``0``. Current implementation follows the Firmware Handoff
diff --git a/docs/getting_started/prerequisites.rst b/docs/getting_started/prerequisites.rst
index da7a2c3..66c278e 100644
--- a/docs/getting_started/prerequisites.rst
+++ b/docs/getting_started/prerequisites.rst
@@ -182,6 +182,44 @@
You can read more about Git hooks in the *githooks* page of the Git
documentation, available `here <https://git-scm.com/docs/githooks>`_.
+.. _git_submodules:
+
+Cloning Additional Git Submodules
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Some dependencies in TF-A, such as Transfer List Library ``libtl``, are managed
+using Git submodules. Submodules allow external repositories to be included
+within the main project while maintaining their own commit history.
+
+Initial Clone with Submodules
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If you're cloning the repository for the first time, run the following commands
+to initialize and fetch all submodules:
+
+.. code-block:: bash
+
+ git clone --recurse-submodules "https://git.trustedfirmware.org/TF-A/trusted-firmware-a"
+
+This ensures all submodules (including ``libtl``) are correctly checked out.
+
+Updating Submodules
+^^^^^^^^^^^^^^^^^^^
+
+If the project updates the reference to a submodule (e.g., points to a new
+commit of ``libtl``), you can update your local copy by running:
+
+.. code-block:: bash
+
+ git pull
+ git submodule update --init --recursive
+
+To fetch the latest commits from all submodules, you can use:
+
+.. code-block:: bash
+
+ git submodule update --remote
+
--------------
*Copyright (c) 2021-2025, Arm Limited. All rights reserved.*
diff --git a/docs/license.rst b/docs/license.rst
index e35b9bb..05458b9 100644
--- a/docs/license.rst
+++ b/docs/license.rst
@@ -119,6 +119,15 @@
- ``include/lib/hob/mmram.h``
- ``include/lib/hob/mpinfo.h``
+- Some source files originating from the `mbed OS`_ project.
+ These files are licensed under the Apache License, Version 2.0, which is a
+ permissive license compatible with BSD-3-Clause. Any contributions to this
+ code must also be made under the terms of `Apache License 2.0`_.
+ These files are:
+
+ - ``tools/memory/memory/mapsummary.py``
+ - ``tools/memory/memory/mapsummary_flamegraph.hmtl``
+
.. _FreeBSD: http://www.freebsd.org
.. _Linux MIT license: https://raw.githubusercontent.com/torvalds/linux/master/LICENSES/preferred/MIT
.. _SCC: http://www.simple-cc.org/
@@ -126,3 +135,4 @@
.. _Apache License 2.0: https://www.apache.org/licenses/LICENSE-2.0.txt
.. _pydevicetree: https://pypi.org/project/pydevicetree/
.. _edk2: https://github.com/tianocore/edk2
+.. _mbed OS: https://github.com/ARMmbed/mbed-os/
diff --git a/docs/porting-guide.rst b/docs/porting-guide.rst
index 7e40e47..4157659 100644
--- a/docs/porting-guide.rst
+++ b/docs/porting-guide.rst
@@ -3928,6 +3928,58 @@
Enabling the MEASURED_BOOT flag adds extra platform requirements. Please refer
to :ref:`Measured Boot Design` for more details.
+Live Firmware Activation Interface
+----------------------------------
+
+Function : plat_lfa_get_components()
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ Argument : plat_lfa_component_info_t **
+ Return : int
+
+This platform API provides the list of LFA components available for activation.
+It populates a pointer to an array of ``plat_lfa_component_info_t`` structures,
+which contain information about each component (like UUID, ID, etc.). It returns
+0 on success, or a standard error code on failure.
+
+Function : is_plat_lfa_activation_pending()
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ Argument : uint32_t
+ Return : bool
+
+This platform API checks if the specified LFA component, identified
+by its ``lfa_component_id``, is available for activation. It returns
+true if available, otherwise false.
+
+Function : plat_lfa_cancel()
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ Argument : uint32_t
+ Return : int
+
+This platform API allows the platform to cancel an ongoing update or activation
+process for the specified ``lfa_component_id``. It returns 0 on success or
+a standard error code on failure.
+
+Function : plat_lfa_load_auth_image()
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ Argument : uint32_t
+ Return : int
+
+The platform uses this API to load, authenticate and measure the component
+specified by ``lfa_component_id``. It should return 0 on success or appropriate
+error codes for load/authentication failures.
+
--------------
*Copyright (c) 2013-2025, Arm Limited and Contributors. All rights reserved.*
diff --git a/docs/tools/memory-layout-tool.rst b/docs/tools/memory-layout-tool.rst
index d9c358d..0a447c5 100644
--- a/docs/tools/memory-layout-tool.rst
+++ b/docs/tools/memory-layout-tool.rst
@@ -41,7 +41,7 @@
.. code:: shell
- $ poetry run memory -s
+ $ poetry run memory symbols
build-path: build/fvp/release
Virtual Address Map:
+------------__BL1_RAM_END__------------+---------------------------------------+
@@ -116,14 +116,14 @@
Memory Footprint
~~~~~~~~~~~~~~~~
-The tool enables users to view static memory consumption. When the options
-``-f``, or ``--footprint`` are provided, the script analyses the ELF binaries in
-the build path to generate a table (per memory type), showing memory allocation
-and usage. This is the default output generated by the tool.
+The tool enables users to view static memory consumption. When the ``footprint``
+command is provided, the script analyses the ELF binaries in the build path to
+generate a table (per memory type), showing memory allocation and usage. This is
+the default output generated by the tool.
.. code:: shell
- $ poetry run memory -f
+ $ poetry run memory footprint
build-path: build/fvp/release
+----------------------------------------------------------------------------+
| Memory Usage (bytes) [RAM] |
@@ -150,13 +150,13 @@
Memory Tree
~~~~~~~~~~~
-A hierarchical view of the memory layout can be produced by passing the option
-``-t`` or ``--tree`` to the tool. This gives the start, end, and size of each
-module, their ELF segments as well as sections.
+A hierarchical view of the memory layout can be produced by passing the ``tree``
+command to the tool. This gives the start, end, and size of each module, their
+ELF segments as well as sections.
.. code:: shell
- $ poetry run memory -t
+ $ poetry run memory tree
build-path: build/fvp/release
name start end size
bl1 0 400c000 400c000
@@ -209,7 +209,7 @@
.. code::
- $ poetry run memory -t --depth 2
+ $ poetry run memory tree --depth 2
build-path: build/fvp/release
name start end size
bl1 0 400c000 400c000
@@ -229,6 +229,169 @@
├── 00 4003000 4010000 d000
└── 01 4010000 4021000 11000
+Memory Summary
+~~~~~~~~~~~~~~
+
+The tool provides a by-translation-unit summary of the sizes (``text``, ``bss``,
+``data``) contributed by each translation unit or group of translation units.
+For example, to print a table of an FVP build, with a path depth of 3:
+
+.. code::
+
+ $ poetry run memory summary build/fvp/debug/bl1/bl1.map -d 3
+ | Module | .text | .data | .bss |
+ |----------------------------------------|---------------|-------------|---------------|
+ | [fill] | 3204(+3204) | 0(+0) | 97(+97) |
+ | bl1/aem_generic.o | 0(+0) | 0(+0) | 0(+0) |
+ | bl1/arm_bl1_fwu.o | 224(+224) | 80(+80) | 0(+0) |
+ | bl1/arm_bl1_setup.o | 608(+608) | 0(+0) | 17(+17) |
+ | bl1/arm_common.o | 116(+116) | 0(+0) | 0(+0) |
+ | bl1/arm_console.o | 116(+116) | 0(+0) | 40(+40) |
+ | bl1/arm_dev_rotpk.o | 0(+0) | 0(+0) | 0(+0) |
+ | bl1/arm_dyn_cfg.o | 276(+276) | 0(+0) | 7184(+7184) |
+ | bl1/arm_dyn_cfg_helpers.o | 364(+364) | 0(+0) | 0(+0) |
+ | bl1/arm_err.o | 12(+12) | 0(+0) | 0(+0) |
+ | bl1/arm_fconf_io.o | 0(+0) | 952(+952) | 0(+0) |
+ | bl1/arm_helpers.o | 44(+44) | 0(+0) | 0(+0) |
+ | bl1/arm_io_storage.o | 480(+480) | 0(+0) | 32(+32) |
+ | bl1/auth_mod.o | 1288(+1288) | 0(+0) | 0(+0) |
+ | bl1/backtrace.o | 444(+444) | 0(+0) | 0(+0) |
+ | bl1/bl1_arch_setup.o | 16(+16) | 0(+0) | 0(+0) |
+ | bl1/bl1_context_mgmt.o | 340(+340) | 0(+0) | 1392(+1392) |
+ | bl1/bl1_entrypoint.o | 236(+236) | 0(+0) | 0(+0) |
+ | bl1/bl1_exceptions.o | 2240(+2240) | 0(+0) | 0(+0) |
+ | bl1/bl1_fwu.o | 2188(+2188) | 44(+44) | 0(+0) |
+ | bl1/bl1_main.o | 620(+620) | 0(+0) | 0(+0) |
+ | bl1/bl_common.o | 772(+772) | 0(+0) | 4(+4) |
+ | bl1/board_arm_helpers.o | 44(+44) | 0(+0) | 0(+0) |
+ | bl1/board_arm_trusted_boot.o | 44(+44) | 16(+16) | 0(+0) |
+ | bl1/cache_helpers.o | 112(+112) | 0(+0) | 0(+0) |
+ | bl1/cci.o | 408(+408) | 0(+0) | 24(+24) |
+ | bl1/context.o | 348(+348) | 0(+0) | 0(+0) |
+ | bl1/context_mgmt.o | 1692(+1692) | 0(+0) | 48(+48) |
+ | bl1/cortex_a35.o | 96(+96) | 0(+0) | 0(+0) |
+ | bl1/cortex_a53.o | 248(+248) | 0(+0) | 0(+0) |
+ | bl1/cortex_a57.o | 384(+384) | 0(+0) | 0(+0) |
+ | bl1/cortex_a72.o | 356(+356) | 0(+0) | 0(+0) |
+ | bl1/cortex_a73.o | 304(+304) | 0(+0) | 0(+0) |
+ | bl1/cpu_helpers.o | 200(+200) | 0(+0) | 0(+0) |
+ | bl1/crypto_mod.o | 380(+380) | 0(+0) | 0(+0) |
+ | bl1/debug.o | 224(+224) | 0(+0) | 0(+0) |
+ | bl1/delay_timer.o | 64(+64) | 0(+0) | 8(+8) |
+ | bl1/enable_mmu.o | 112(+112) | 0(+0) | 0(+0) |
+ | bl1/errata_report.o | 564(+564) | 0(+0) | 0(+0) |
+ | bl1/fconf.o | 148(+148) | 0(+0) | 0(+0) |
+ | bl1/fconf_dyn_cfg_getter.o | 656(+656) | 32(+32) | 144(+144) |
+ | bl1/fconf_tbbr_getter.o | 332(+332) | 0(+0) | 24(+24) |
+ | bl1/fdt_wrappers.o | 452(+452) | 0(+0) | 0(+0) |
+ | bl1/fvp_bl1_setup.o | 168(+168) | 0(+0) | 0(+0) |
+ | bl1/fvp_common.o | 512(+512) | 0(+0) | 8(+8) |
+ | bl1/fvp_cpu_pwr.o | 136(+136) | 0(+0) | 0(+0) |
+ | bl1/fvp_err.o | 44(+44) | 0(+0) | 0(+0) |
+ | bl1/fvp_helpers.o | 148(+148) | 0(+0) | 0(+0) |
+ | bl1/fvp_io_storage.o | 228(+228) | 0(+0) | 16(+16) |
+ | bl1/fvp_trusted_boot.o | 292(+292) | 0(+0) | 0(+0) |
+ | bl1/generic_delay_timer.o | 136(+136) | 0(+0) | 16(+16) |
+ | bl1/img_parser_mod.o | 588(+588) | 0(+0) | 20(+20) |
+ | bl1/io_fip.o | 1332(+1332) | 0(+0) | 100(+100) |
+ | bl1/io_memmap.o | 736(+736) | 16(+16) | 32(+32) |
+ | bl1/io_semihosting.o | 648(+648) | 16(+16) | 0(+0) |
+ | bl1/io_storage.o | 1268(+1268) | 0(+0) | 104(+104) |
+ | bl1/mbedtls_common.o | 208(+208) | 0(+0) | 4(+4) |
+ | bl1/mbedtls_crypto.o | 636(+636) | 0(+0) | 0(+0) |
+ | bl1/mbedtls_x509_parser.o | 1588(+1588) | 0(+0) | 120(+120) |
+ | bl1/misc_helpers.o | 392(+392) | 0(+0) | 0(+0) |
+ | bl1/multi_console.o | 528(+528) | 1(+1) | 8(+8) |
+ | bl1/pl011_console.o | 308(+308) | 0(+0) | 0(+0) |
+ | bl1/plat_bl1_common.o | 208(+208) | 0(+0) | 0(+0) |
+ | bl1/plat_bl_common.o | 40(+40) | 0(+0) | 0(+0) |
+ | bl1/plat_common.o | 48(+48) | 0(+0) | 8(+8) |
+ | bl1/plat_log_common.o | 48(+48) | 0(+0) | 0(+0) |
+ | bl1/plat_tbbr.o | 128(+128) | 0(+0) | 0(+0) |
+ | bl1/platform_helpers.o | 12(+12) | 0(+0) | 0(+0) |
+ | bl1/platform_up_stack.o | 16(+16) | 0(+0) | 0(+0) |
+ | bl1/semihosting.o | 352(+352) | 0(+0) | 0(+0) |
+ | bl1/semihosting_call.o | 8(+8) | 0(+0) | 0(+0) |
+ | bl1/smmu_v3.o | 296(+296) | 0(+0) | 0(+0) |
+ | bl1/sp805.o | 64(+64) | 0(+0) | 0(+0) |
+ | bl1/tbbr_cot_bl1.o | 0(+0) | 48(+48) | 156(+156) |
+ | bl1/tbbr_cot_common.o | 0(+0) | 144(+144) | 306(+306) |
+ | bl1/tbbr_img_desc.o | 0(+0) | 768(+768) | 0(+0) |
+ | bl1/tf_log.o | 200(+200) | 4(+4) | 0(+0) |
+ | bl1/xlat_tables_arch.o | 736(+736) | 0(+0) | 0(+0) |
+ | bl1/xlat_tables_context.o | 192(+192) | 96(+96) | 1296(+1296) |
+ | bl1/xlat_tables_core.o | 2112(+2112) | 0(+0) | 0(+0) |
+ | bl1/xlat_tables_utils.o | 8(+8) | 0(+0) | 0(+0) |
+ | lib/libc.a/assert.o | 48(+48) | 0(+0) | 0(+0) |
+ | lib/libc.a/exit.o | 64(+64) | 0(+0) | 8(+8) |
+ | lib/libc.a/memchr.o | 44(+44) | 0(+0) | 0(+0) |
+ | lib/libc.a/memcmp.o | 52(+52) | 0(+0) | 0(+0) |
+ | lib/libc.a/memcpy.o | 32(+32) | 0(+0) | 0(+0) |
+ | lib/libc.a/memmove.o | 52(+52) | 0(+0) | 0(+0) |
+ | lib/libc.a/memset.o | 140(+140) | 0(+0) | 0(+0) |
+ | lib/libc.a/printf.o | 1532(+1532) | 0(+0) | 0(+0) |
+ | lib/libc.a/snprintf.o | 1748(+1748) | 0(+0) | 0(+0) |
+ | lib/libc.a/strcmp.o | 44(+44) | 0(+0) | 0(+0) |
+ | lib/libc.a/strlen.o | 28(+28) | 0(+0) | 0(+0) |
+ | lib/libfdt.a/fdt.o | 1460(+1460) | 0(+0) | 0(+0) |
+ | lib/libfdt.a/fdt_ro.o | 1392(+1392) | 0(+0) | 0(+0) |
+ | lib/libfdt.a/fdt_wip.o | 244(+244) | 0(+0) | 0(+0) |
+ | lib/libmbedtls.a/asn1parse.o | 956(+956) | 0(+0) | 0(+0) |
+ | lib/libmbedtls.a/bignum.o | 6796(+6796) | 0(+0) | 0(+0) |
+ | lib/libmbedtls.a/bignum_core.o | 3252(+3252) | 0(+0) | 0(+0) |
+ | lib/libmbedtls.a/constant_time.o | 280(+280) | 0(+0) | 8(+8) |
+ | lib/libmbedtls.a/md.o | 504(+504) | 0(+0) | 0(+0) |
+ | lib/libmbedtls.a/memory_buffer_alloc.o | 1264(+1264) | 0(+0) | 40(+40) |
+ | lib/libmbedtls.a/oid.o | 752(+752) | 0(+0) | 0(+0) |
+ | lib/libmbedtls.a/pk.o | 872(+872) | 0(+0) | 0(+0) |
+ | lib/libmbedtls.a/pk_wrap.o | 848(+848) | 0(+0) | 0(+0) |
+ | lib/libmbedtls.a/pkparse.o | 516(+516) | 0(+0) | 0(+0) |
+ | lib/libmbedtls.a/platform.o | 92(+92) | 24(+24) | 0(+0) |
+ | lib/libmbedtls.a/platform_util.o | 96(+96) | 0(+0) | 0(+0) |
+ | lib/libmbedtls.a/rsa.o | 6588(+6588) | 0(+0) | 0(+0) |
+ | lib/libmbedtls.a/rsa_alt_helpers.o | 2340(+2340) | 0(+0) | 0(+0) |
+ | lib/libmbedtls.a/sha256.o | 1448(+1448) | 0(+0) | 0(+0) |
+ | lib/libmbedtls.a/x509.o | 1028(+1028) | 0(+0) | 0(+0) |
+ | Subtotals | 69632(+69632) | 2241(+2241) | 11264(+11264) |
+ Total Static RAM memory (data + bss): 13505(+13505) bytes
+ Total Flash memory (text + data): 71873(+71873) bytes
+
+A delta between two images can be generated by passing the ``--old`` option with
+a path to the previous map file.
+
+For example:
+
+.. code::
+
+ $ poetry run memory summary ../maps/fvp-tbb-mbedtls/bl1.map --old ../maps/fvp-tbb-mbedtls/bl1.map.old -d 1
+ | Module | .text | .data | .bss |
+ |-----------|---------------|----------|--------------|
+ | [fill] | 780(-2424) | 0(+0) | 321(+224) |
+ | bl1 | 32024(+108) | 2217(+0) | 11111(+0) |
+ | lib | 45020(+10508) | 24(+0) | 1880(+1824) |
+ | Subtotals | 77824(+8192) | 2241(+0) | 13312(+2048) |
+ Total Static RAM memory (data + bss): 15553(+2048) bytes
+ Total Flash memory (text + data): 80065(+8192) bytes
+
+Note that since the old map file includes the required suffix, specifying the
+``--old`` argument is optional here.
+
+Under some circumstances, some executables are padded to meet certain
+alignments, such as a 4KB page boundary, and excluding that padding can provide
+more helpful diffs. Taking the last example, and adding the ``-e`` argument
+yields such a summary:
+
+.. code::
+
+ $ poetry run memory summary ../maps/fvp-tbb-mbedtls/bl1.map --old ../maps/fvp-tbb-mbedtls/bl1.map.old -d 1 -e
+ | Module | .text | .data | .bss |
+ |-----------|---------------|----------|--------------|
+ | bl1 | 32024(+108) | 2217(+0) | 11111(+0) |
+ | lib | 45020(+10508) | 24(+0) | 1880(+1824) |
+ | Subtotals | 77044(+10616) | 2241(+0) | 12991(+1824) |
+ Total Static RAM memory (data + bss): 15232(+1824) bytes
+ Total Flash memory (text + data): 79285(+10616) bytes
+
--------------
*Copyright (c) 2023-2025, Arm Limited. All rights reserved.*
diff --git a/drivers/arm/css/dsu/dsu.c b/drivers/arm/dsu/dsu.c
similarity index 83%
rename from drivers/arm/css/dsu/dsu.c
rename to drivers/arm/dsu/dsu.c
index f0e8df1..dea89c5 100644
--- a/drivers/arm/css/dsu/dsu.c
+++ b/drivers/arm/dsu/dsu.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, Arm Limited. All rights reserved.
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -10,7 +10,9 @@
#include <arch_helpers.h>
#include <common/bl_common.h>
#include <common/debug.h>
-#include <drivers/arm/css/dsu.h>
+#include <drivers/arm/dsu.h>
+#include <dsu_def.h>
+#include <lib/utils_def.h>
#include <plat/arm/common/plat_arm.h>
#include <plat/common/platform.h>
@@ -133,3 +135,30 @@
restore_dsu_pmu_state(&cluster_pmu_context[cluster_pos]);
}
+void dsu_driver_init(const dsu_driver_data_t *plat_driver_data)
+{
+ uint64_t actlr_el3 = read_actlr_el3();
+ uint64_t pwrctlr = read_clusterpwrctlr_el1();
+ uint64_t pwrdn = read_clusterpwrdn_el1();
+
+ /* enable access to power control registers. */
+ actlr_el3 |= ACTLR_EL3_PWREN_BIT;
+ write_actlr_el3(actlr_el3);
+
+ UPDATE_REG_FIELD(CLUSTERPWRCTLR_FUNCRET, pwrctlr,
+ plat_driver_data->clusterpwrctlr_funcret);
+
+ UPDATE_REG_FIELD(CLUSTERPWRCTLR_CACHEPWR, pwrctlr,
+ plat_driver_data->clusterpwrctlr_cachepwr);
+
+ write_clusterpwrctlr_el1(pwrctlr);
+
+ UPDATE_REG_FIELD(CLUSTERPWRDN_PWRDN, pwrdn,
+ plat_driver_data->clusterpwrdwn_pwrdn);
+
+ UPDATE_REG_FIELD(CLUSTERPWRDN_MEMRET, pwrdn,
+ plat_driver_data->clusterpwrdwn_memret);
+
+ write_clusterpwrdn_el1(pwrdn);
+}
+
diff --git a/drivers/arm/gic/v2/gicdv2_helpers.c b/drivers/arm/gic/v2/gicdv2_helpers.c
index 2f3f7f8..464bb34 100644
--- a/drivers/arm/gic/v2/gicdv2_helpers.c
+++ b/drivers/arm/gic/v2/gicdv2_helpers.c
@@ -320,7 +320,7 @@
void gicd_set_ipriorityr(uintptr_t base, unsigned int id, unsigned int pri)
{
- uint8_t val = pri & GIC_PRI_MASK;
+ uint8_t val = (uint8_t)(pri & GIC_PRI_MASK);
mmio_write_8(base + GICD_IPRIORITYR + id, val);
}
diff --git a/drivers/imx/usdhc/imx_usdhc.c b/drivers/imx/usdhc/imx_usdhc.c
index f6a27dc..30caeba 100644
--- a/drivers/imx/usdhc/imx_usdhc.c
+++ b/drivers/imx/usdhc/imx_usdhc.c
@@ -227,6 +227,8 @@
*xfertype |= XFERTYPE_CICEN;
*xfertype |= XFERTYPE_CCCEN;
break;
+ case MMC_RESPONSE_NONE:
+ break;
default:
ERROR("Invalid CMD response: %u\n", cmd->resp_type);
return -EINVAL;
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index b51e744..93a958c 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -437,7 +437,7 @@
int ret;
/* CMD0: reset to IDLE */
- ret = mmc_send_cmd(MMC_CMD(0), 0, 0, NULL);
+ ret = mmc_send_cmd(MMC_CMD(0), 0, MMC_RESPONSE_NONE, NULL);
if (ret != 0) {
return ret;
}
diff --git a/drivers/nxp/crypto/caam/src/hw_key_blob.c b/drivers/nxp/crypto/caam/src/hw_key_blob.c
index 6bcb6ba..0ac750d 100644
--- a/drivers/nxp/crypto/caam/src/hw_key_blob.c
+++ b/drivers/nxp/crypto/caam/src/hw_key_blob.c
@@ -43,6 +43,10 @@
struct job_descriptor *jobdesc = &desc;
uint32_t in_sz = 16U;
+ if (size <= 0 || size > 16) {
+ ERROR("Error: Requested invalid length of HUK.\n");
+ return -1;
+ }
/* Output blob will have 32 bytes key blob in beginning and
* 16 byte HMAC identifier at end of data blob
*/
diff --git a/drivers/nxp/crypto/caam/src/rng.c b/drivers/nxp/crypto/caam/src/rng.c
index 58430db..0331040 100644
--- a/drivers/nxp/crypto/caam/src/rng.c
+++ b/drivers/nxp/crypto/caam/src/rng.c
@@ -41,7 +41,7 @@
if (rdsta & RNG_STATE0_HANDLE_INSTANTIATED) {
*state_handle = 0;
ret_code = 1;
- } else if (rdsta & RNG_STATE0_HANDLE_INSTANTIATED) {
+ } else if (rdsta & RNG_STATE1_HANDLE_INSTANTIATED) {
*state_handle = 1;
ret_code = 1;
}
diff --git a/fdts/fvp-base-psci-common.dtsi b/fdts/fvp-base-psci-common.dtsi
index bdb0229..2a128d8 100644
--- a/fdts/fvp-base-psci-common.dtsi
+++ b/fdts/fvp-base-psci-common.dtsi
@@ -29,10 +29,7 @@
chosen {
stdout-path = "serial0:115200n8";
-/* SPM_MM doesn't like this */
-#if SPM_MM == 0
bootargs = "console=ttyAMA0 earlycon=pl011,0x1c090000 root=/dev/vda ip=on";
-#endif
};
aliases {
diff --git a/include/arch/aarch64/arch.h b/include/arch/aarch64/arch.h
index b607945..5506cb1 100644
--- a/include/arch/aarch64/arch.h
+++ b/include/arch/aarch64/arch.h
@@ -1576,7 +1576,7 @@
/*******************************************************************************
* Definitions for DynamicIQ Shared Unit registers
******************************************************************************/
-#define CLUSTERPWRDN_EL1 S3_0_c15_c3_6
+#define CLUSTERPWRDN_EL1 S3_0_C15_C3_6
/*******************************************************************************
* FEAT_FPMR - Floating point Mode Register
diff --git a/include/arch/aarch64/el3_common_macros.S b/include/arch/aarch64/el3_common_macros.S
index fce0f2c..ee5d8d9 100644
--- a/include/arch/aarch64/el3_common_macros.S
+++ b/include/arch/aarch64/el3_common_macros.S
@@ -65,7 +65,11 @@
* ---------------------------------------------------------------------
*/
bl plat_my_core_pos
- bl _cpu_data_by_index
+ /* index into the cpu_data */
+ mov_imm x1, CPU_DATA_SIZE
+ mul x0, x0, x1
+ adr_l x1, percpu_data
+ add x0, x0, x1
msr tpidr_el3, x0
#endif /* IMAGE_BL31 */
diff --git a/include/common/debug.h b/include/common/debug.h
index 0ddb400..6d7f2c6 100644
--- a/include/common/debug.h
+++ b/include/common/debug.h
@@ -32,6 +32,7 @@
#include <cdefs.h>
#include <stdarg.h>
#include <stdbool.h>
+#include <stdint.h>
#include <stdio.h>
#include <drivers/console.h>
@@ -135,7 +136,7 @@
void tf_log(const char *fmt, ...) __printflike(1, 2);
void tf_log_newline(const char log_fmt[2]);
-void tf_log_set_max_level(unsigned int log_level);
+void tf_log_set_max_level(uint32_t log_level);
#endif /* __ASSEMBLER__ */
#endif /* DEBUG_H */
diff --git a/include/drivers/arm/css/dsu.h b/include/drivers/arm/css/dsu.h
deleted file mode 100644
index 4d7822b..0000000
--- a/include/drivers/arm/css/dsu.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef DSU_H
-#define DSU_H
-
-#define PMCR_N_MAX 0x1f
-
-#define save_pmu_reg(state, reg) state->reg = read_##reg()
-
-#define restore_pmu_reg(context, reg) write_##reg(context->reg)
-
-typedef struct cluster_pmu_state{
- uint64_t clusterpmcr;
- uint64_t clusterpmcntenset;
- uint64_t clusterpmccntr;
- uint64_t clusterpmovsset;
- uint64_t clusterpmselr;
- uint64_t clusterpmsevtyper;
- uint64_t counter_val[PMCR_N_MAX];
- uint64_t counter_type[PMCR_N_MAX];
-} cluster_pmu_state_t;
-
-static inline unsigned int read_cluster_eventctr_num(void)
-{
- return ((read_clusterpmcr() >> CLUSTERPMCR_N_SHIFT) &
- CLUSTERPMCR_N_MASK);
-}
-
-
-void save_dsu_pmu_state(cluster_pmu_state_t *cluster_pmu_context);
-
-void restore_dsu_pmu_state(cluster_pmu_state_t *cluster_pmu_context);
-
-void cluster_on_dsu_pmu_context_restore(void);
-
-void cluster_off_dsu_pmu_context_save(void);
-
-#endif /* DSU_H */
diff --git a/include/drivers/arm/dsu.h b/include/drivers/arm/dsu.h
new file mode 100644
index 0000000..492babd
--- /dev/null
+++ b/include/drivers/arm/dsu.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2025, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef DSU_H
+#define DSU_H
+
+#if defined(__aarch64__)
+#include <dsu_def.h>
+
+/*
+ * Power Control Registers enable bit of Auxilary Control register.
+ * ACTLR_EL3_PWREN_BIT definition is same among cores like Cortex-X925,
+ * Cortex-X4, Cortex-A520, Cortex-A725 that are used in a cluster
+ * with DSU.
+ */
+#define ACTLR_EL3_PWREN_BIT BIT(7)
+
+#define PMCR_N_MAX 0x1f
+
+#define save_pmu_reg(state, reg) state->reg = read_##reg()
+
+#define restore_pmu_reg(context, reg) write_##reg(context->reg)
+
+typedef struct cluster_pmu_state {
+ uint64_t clusterpmcr;
+ uint64_t clusterpmcntenset;
+ uint64_t clusterpmccntr;
+ uint64_t clusterpmovsset;
+ uint64_t clusterpmselr;
+ uint64_t clusterpmsevtyper;
+ uint64_t counter_val[PMCR_N_MAX];
+ uint64_t counter_type[PMCR_N_MAX];
+} cluster_pmu_state_t;
+
+typedef struct dsu_driver_data {
+ uint8_t clusterpwrdwn_pwrdn;
+ uint8_t clusterpwrdwn_memret;
+ uint8_t clusterpwrctlr_cachepwr;
+ uint8_t clusterpwrctlr_funcret;
+} dsu_driver_data_t;
+
+extern const dsu_driver_data_t plat_dsu_data;
+
+static inline unsigned int read_cluster_eventctr_num(void)
+{
+ return ((read_clusterpmcr() >> CLUSTERPMCR_N_SHIFT) &
+ CLUSTERPMCR_N_MASK);
+}
+
+void save_dsu_pmu_state(cluster_pmu_state_t *cluster_pmu_context);
+
+void restore_dsu_pmu_state(cluster_pmu_state_t *cluster_pmu_context);
+
+void cluster_on_dsu_pmu_context_restore(void);
+
+void cluster_off_dsu_pmu_context_save(void);
+
+void dsu_driver_init(const dsu_driver_data_t *data);
+#endif
+#endif /* DSU_H */
diff --git a/include/drivers/cadence/cdns_sdmmc.h b/include/drivers/cadence/cdns_sdmmc.h
index f8d616f..895a705 100644
--- a/include/drivers/cadence/cdns_sdmmc.h
+++ b/include/drivers/cadence/cdns_sdmmc.h
@@ -342,7 +342,6 @@
/* MMC Peripheral Definition */
#define SOCFPGA_MMC_BLOCK_MASK (SOCFPGA_MMC_BLOCK_SIZE - U(1))
#define SOCFPGA_MMC_BOOT_CLK_RATE (400 * 1000)
-#define MMC_RESPONSE_NONE 0
#define SDHC_CDNS_SRS03_VALUE 0x01020013
/* Value randomly chosen for eMMC RCA, it should be > 1 */
diff --git a/include/drivers/measured_boot/event_log/event_handoff.h b/include/drivers/measured_boot/event_log/event_handoff.h
index e969d1f..f8c8716 100644
--- a/include/drivers/measured_boot/event_log/event_handoff.h
+++ b/include/drivers/measured_boot/event_log/event_handoff.h
@@ -10,7 +10,7 @@
#include <stdint.h>
-#include <lib/transfer_list.h>
+#include <transfer_list.h>
/**
* Initializes or extends the TPM event log in the transfer list.
diff --git a/include/drivers/measured_boot/event_log/event_log.h b/include/drivers/measured_boot/event_log/event_log.h
index b5adfdc..1313111 100644
--- a/include/drivers/measured_boot/event_log/event_log.h
+++ b/include/drivers/measured_boot/event_log/event_log.h
@@ -11,7 +11,9 @@
#include <stdint.h>
#include <drivers/auth/crypto_mod.h>
+#if TRANSFER_LIST
#include "event_handoff.h"
+#endif
#include "tcg.h"
/*
diff --git a/include/drivers/mmc.h b/include/drivers/mmc.h
index 454a85a..55ed35c 100644
--- a/include/drivers/mmc.h
+++ b/include/drivers/mmc.h
@@ -52,6 +52,7 @@
#define MMC_RESPONSE_R5 (MMC_RSP_48 | MMC_RSP_CRC | MMC_RSP_CMD_IDX)
#define MMC_RESPONSE_R6 (MMC_RSP_48 | MMC_RSP_CRC | MMC_RSP_CMD_IDX)
#define MMC_RESPONSE_R7 (MMC_RSP_48 | MMC_RSP_CRC | MMC_RSP_CMD_IDX)
+#define MMC_RESPONSE_NONE 0U
/* Value randomly chosen for eMMC RCA, it should be > 1 */
#define MMC_FIX_RCA 6
diff --git a/include/drivers/nxp/crypto/caam/sec_hw_specific.h b/include/drivers/nxp/crypto/caam/sec_hw_specific.h
index bc11aca..02bd4d0 100644
--- a/include/drivers/nxp/crypto/caam/sec_hw_specific.h
+++ b/include/drivers/nxp/crypto/caam/sec_hw_specific.h
@@ -123,6 +123,7 @@
/* RNG RDSTA bitmask */
#define RNG_STATE0_HANDLE_INSTANTIATED 0x00000001
+#define RNG_STATE1_HANDLE_INSTANTIATED 0x00000002
#define RTMCTL_PRGM 0x00010000 /* 1 -> program mode, 0 -> run mode */
/* use von Neumann data in both entropy shifter and statistical checker */
#define RTMCTL_SAMP_MODE_VON_NEUMANN_ES_SC 0
diff --git a/include/lib/cpus/aarch64/dsu_def.h b/include/lib/cpus/aarch64/dsu_def.h
index 3f6dbfe..089ea52 100644
--- a/include/lib/cpus/aarch64/dsu_def.h
+++ b/include/lib/cpus/aarch64/dsu_def.h
@@ -32,13 +32,27 @@
* DSU Cluster Auxiliary Control registers definitions
********************************************************************/
#define CLUSTERACTLR_EL1 S3_0_C15_C3_3
-#define CLUSTERPWRCTLR_EL1 S3_0_C15_C3_5
+
+/* CLUSTERPWRCTLR_EL1 register definitions */
+#define CLUSTERPWRCTLR_EL1 S3_0_C15_C3_5
+#define CLUSTERPWRCTLR_FUNCRET_WIDTH U(3)
+#define CLUSTERPWRCTLR_FUNCRET_SHIFT U(0)
+#define CLUSTERPWRCTLR_FUNCRET_RESET U(0)
+#define CLUSTERPWRCTLR_CACHEPWR_WIDTH U(4)
+#define CLUSTERPWRCTLR_CACHEPWR_SHIFT U(4)
+#define CLUSTERPWRCTLR_CACHEPWR_RESET U(7)
#define CLUSTERACTLR_EL1_ASSERT_CBUSY (ULL(1) << 8)
#define CLUSTERACTLR_EL1_DISABLE_CLOCK_GATING (ULL(1) << 15)
#define CLUSTERACTLR_EL1_DISABLE_SCLK_GATING (ULL(3) << 15)
#define CLUSTERACTLR_EL1_IGNORE_INTERCONNECT_CBUSY (ULL(3) << 20)
+/* CLUSTERPWRDN_EL1 register definitions */
+#define CLUSTERPWRDN_PWRDN_WIDTH U(1)
+#define CLUSTERPWRDN_PWRDN_SHIFT U(0)
+#define CLUSTERPWRDN_MEMRET_WIDTH U(1)
+#define CLUSTERPWRDN_MEMRET_SHIFT U(1)
+
/********************************************************************
* Masks applied for DSU errata workarounds
********************************************************************/
diff --git a/include/lib/el3_runtime/cpu_data.h b/include/lib/el3_runtime/cpu_data.h
index 3dc156a..20a6c39 100644
--- a/include/lib/el3_runtime/cpu_data.h
+++ b/include/lib/el3_runtime/cpu_data.h
@@ -138,7 +138,7 @@
void *cpu_context[CPU_DATA_CONTEXT_NUM];
#endif /* __aarch64__ */
entry_point_info_t *warmboot_ep_info;
- uintptr_t cpu_ops_ptr;
+ struct cpu_ops *cpu_ops_ptr;
struct psci_cpu_data psci_svc_cpu_data;
#if ENABLE_PAUTH
uint64_t apiakey[2];
@@ -196,16 +196,19 @@
assert_cpu_data_pmf_ts0_offset_mismatch);
#endif
-struct cpu_data *_cpu_data_by_index(uint32_t cpu_index);
+static inline cpu_data_t *_cpu_data_by_index(unsigned int cpu_index)
+{
+ return &percpu_data[cpu_index];
+}
#ifdef __aarch64__
/* Return the cpu_data structure for the current CPU. */
-static inline struct cpu_data *_cpu_data(void)
+static inline cpu_data_t *_cpu_data(void)
{
return (cpu_data_t *)read_tpidr_el3();
}
#else
-struct cpu_data *_cpu_data(void);
+cpu_data_t *_cpu_data(void);
#endif
/*
diff --git a/include/lib/libc/stdbool.h b/include/lib/libc/stdbool.h
index c2c9b22..30ced2a 100644
--- a/include/lib/libc/stdbool.h
+++ b/include/lib/libc/stdbool.h
@@ -9,8 +9,8 @@
#define bool _Bool
-#define true (0 < 1)
-#define false (0 > 1)
+#define true (0 == 0)
+#define false (0 == 1)
#define __bool_true_false_are_defined 1
diff --git a/include/lib/transfer_list.h b/include/lib/transfer_list.h
deleted file mode 100644
index bdc6349..0000000
--- a/include/lib/transfer_list.h
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (c) 2023-2024, Linaro Limited and Contributors. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef __TRANSFER_LIST_H
-#define __TRANSFER_LIST_H
-
-#include <stdbool.h>
-#include <stdint.h>
-
-#include <common/ep_info.h>
-#include <lib/utils_def.h>
-
-#define TRANSFER_LIST_SIGNATURE U(0x4a0fb10b)
-#define TRANSFER_LIST_VERSION U(0x0001)
-
-/*
- * Init value of maximum alignment required by any TE data in the TL
- * specified as a power of two
- */
-#define TRANSFER_LIST_INIT_MAX_ALIGN U(3)
-
-/* Alignment required by TE header start address, in bytes */
-#define TRANSFER_LIST_GRANULE U(8)
-
-/*
- * Version of the register convention used.
- * Set to 1 for both AArch64 and AArch32 according to fw handoff spec v0.9
- */
-#define REGISTER_CONVENTION_VERSION_SHIFT_64 UL(32)
-#define REGISTER_CONVENTION_VERSION_SHIFT_32 UL(24)
-#define REGISTER_CONVENTION_VERSION_MASK UL(0xff)
-#define REGISTER_CONVENTION_VERSION UL(1)
-
-#define TRANSFER_LIST_HANDOFF_X1_VALUE(__version) \
- ((TRANSFER_LIST_SIGNATURE & \
- ((1UL << REGISTER_CONVENTION_VERSION_SHIFT_64) - 1)) | \
- (((__version) & REGISTER_CONVENTION_VERSION_MASK) << \
- REGISTER_CONVENTION_VERSION_SHIFT_64))
-
-#define TRANSFER_LIST_HANDOFF_R1_VALUE(__version) \
- ((TRANSFER_LIST_SIGNATURE & \
- ((1UL << REGISTER_CONVENTION_VERSION_SHIFT_32) - 1)) | \
- (((__version) & REGISTER_CONVENTION_VERSION_MASK) << \
- REGISTER_CONVENTION_VERSION_SHIFT_32))
-
-#ifndef __ASSEMBLER__
-
-#define TL_FLAGS_HAS_CHECKSUM BIT(0)
-
-enum transfer_list_tag_id {
- TL_TAG_EMPTY = 0,
- TL_TAG_FDT = 1,
- TL_TAG_HOB_BLOCK = 2,
- TL_TAG_HOB_LIST = 3,
- TL_TAG_ACPI_TABLE_AGGREGATE = 4,
- TL_TAG_TPM_EVLOG = 5,
- TL_TAG_OPTEE_PAGABLE_PART = 0x100,
- TL_TAG_DT_SPMC_MANIFEST = 0x101,
- TL_TAG_EXEC_EP_INFO64 = 0x102,
- TL_TAG_SRAM_LAYOUT64 = 0x104,
- TL_TAG_MBEDTLS_HEAP_INFO = 0x105,
- TL_TAG_EXEC_EP_INFO32 = 0x106,
- TL_TAG_SRAM_LAYOUT32 = 0x107,
-};
-
-enum transfer_list_ops {
- TL_OPS_NON, /* invalid for any operation */
- TL_OPS_ALL, /* valid for all operations */
- TL_OPS_RO, /* valid for read only */
- TL_OPS_CUS, /* abort or switch to special code to interpret */
-};
-
-struct transfer_list_header {
- uint32_t signature;
- uint8_t checksum;
- uint8_t version;
- uint8_t hdr_size;
- uint8_t alignment; /* max alignment of TE data */
- uint32_t size; /* TL header + all TEs */
- uint32_t max_size;
- uint32_t flags;
- uint32_t reserved; /* spare bytes */
- /*
- * Commented out element used to visualize dynamic part of the
- * data structure.
- *
- * Note that struct transfer_list_entry also is dynamic in size
- * so the elements can't be indexed directly but instead must be
- * traversed in order
- *
- * struct transfer_list_entry entries[];
- */
-};
-
-struct __attribute__((packed)) transfer_list_entry {
- uint32_t tag_id : 24;
- uint8_t hdr_size;
- uint32_t data_size;
- /*
- * Commented out element used to visualize dynamic part of the
- * data structure.
- *
- * Note that padding is added at the end of @data to make to reach
- * a 8-byte boundary.
- *
- * uint8_t data[ROUNDUP(data_size, 8)];
- */
-};
-
-CASSERT(sizeof(struct transfer_list_entry) == U(0x8), assert_transfer_list_entry_size);
-
-void transfer_entry_dump(struct transfer_list_entry *te);
-void transfer_list_dump(struct transfer_list_header *tl);
-struct transfer_list_header *transfer_list_ensure(void *addr, size_t size);
-entry_point_info_t *
-transfer_list_set_handoff_args(struct transfer_list_header *tl,
- entry_point_info_t *ep_info);
-struct transfer_list_header *transfer_list_init(void *addr, size_t max_size);
-
-struct transfer_list_header *
-transfer_list_relocate(struct transfer_list_header *tl, void *addr,
- size_t max_size);
-enum transfer_list_ops
-transfer_list_check_header(const struct transfer_list_header *tl);
-
-void transfer_list_update_checksum(struct transfer_list_header *tl);
-bool transfer_list_verify_checksum(const struct transfer_list_header *tl);
-
-bool transfer_list_set_data_size(struct transfer_list_header *tl,
- struct transfer_list_entry *entry,
- uint32_t new_data_size);
-
-void *transfer_list_entry_data(struct transfer_list_entry *entry);
-bool transfer_list_rem(struct transfer_list_header *tl,
- struct transfer_list_entry *entry);
-
-struct transfer_list_entry *transfer_list_add(struct transfer_list_header *tl,
- uint32_t tag_id,
- uint32_t data_size,
- const void *data);
-
-struct transfer_list_entry *
-transfer_list_add_with_align(struct transfer_list_header *tl, uint32_t tag_id,
- uint32_t data_size, const void *data,
- uint8_t alignment);
-
-struct transfer_list_entry *
-transfer_list_next(struct transfer_list_header *tl,
- struct transfer_list_entry *last);
-
-struct transfer_list_entry *transfer_list_find(struct transfer_list_header *tl,
- uint32_t tag_id);
-
-#endif /*__ASSEMBLER__*/
-#endif /*__TRANSFER_LIST_H*/
diff --git a/include/lib/utils_def.h b/include/lib/utils_def.h
index 68e464a..7dcc5ce 100644
--- a/include/lib/utils_def.h
+++ b/include/lib/utils_def.h
@@ -86,6 +86,12 @@
#define EXTRACT(regfield, reg) \
(((reg) & MASK(regfield)) >> (regfield##_SHIFT))
+#define UPDATE_REG_FIELD(regfield, reg, val) \
+ do { \
+ (reg) &= ~(MASK(regfield)); \
+ (reg) |= ((uint64_t)(val) << (regfield##_SHIFT)); \
+ } while (0)
+
/*
* This variant of div_round_up can be used in macro definition but should not
* be used in C code as the `div` parameter is evaluated twice.
diff --git a/include/plat/arm/common/plat_arm.h b/include/plat/arm/common/plat_arm.h
index aed85f5..4a9c2d8 100644
--- a/include/plat/arm/common/plat_arm.h
+++ b/include/plat/arm/common/plat_arm.h
@@ -17,9 +17,11 @@
#include <lib/el3_runtime/cpu_data.h>
#include <lib/gpt_rme/gpt_rme.h>
#include <lib/spinlock.h>
-#include <lib/transfer_list.h>
#include <lib/utils_def.h>
#include <lib/xlat_tables/xlat_tables_compat.h>
+#if TRANSFER_LIST
+#include <transfer_list.h>
+#endif
/*******************************************************************************
* Forward declarations
@@ -290,6 +292,7 @@
void arm_bl31_plat_arch_setup(void);
/* Firmware Handoff utility functions */
+#if TRANSFER_LIST
void arm_transfer_list_dyn_cfg_init(struct transfer_list_header *secure_tl);
void arm_transfer_list_populate_ep_info(bl_mem_params_node_t *next_param_node,
struct transfer_list_header *secure_tl);
@@ -298,6 +301,7 @@
struct transfer_list_entry *
arm_transfer_list_set_heap_info(struct transfer_list_header *tl);
void arm_transfer_list_get_heap_info(void **heap_addr, size_t *heap_size);
+#endif
/* TSP utility functions */
void arm_tsp_early_platform_setup(u_register_t arg0, u_register_t arg1,
diff --git a/include/plat/common/plat_lfa.h b/include/plat/common/plat_lfa.h
new file mode 100644
index 0000000..fa7c2f9
--- /dev/null
+++ b/include/plat/common/plat_lfa.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef PLAT_LFA_H
+#define PLAT_LFA_H
+
+#include <services/lfa_component_desc.h>
+#include <tools_share/uuid.h>
+
+typedef struct plat_lfa_component_info {
+ const uint32_t lfa_component_id;
+ const uuid_t uuid;
+ struct lfa_component_ops *activator;
+ bool activation_pending;
+} plat_lfa_component_info_t;
+
+uint32_t plat_lfa_get_components(plat_lfa_component_info_t **components);
+bool is_plat_lfa_activation_pending(uint32_t lfa_component_id);
+int plat_lfa_cancel(uint32_t lfa_component_id);
+int plat_lfa_load_auth_image(uint32_t lfa_component_id);
+
+#endif /* PLAT_LFA_H */
diff --git a/include/plat/common/platform.h b/include/plat/common/platform.h
index b9985a3..8c6ee98 100644
--- a/include/plat/common/platform.h
+++ b/include/plat/common/platform.h
@@ -24,6 +24,9 @@
#if DRTM_SUPPORT
#include "plat_drtm.h"
#endif /* DRTM_SUPPORT */
+#if LFA_SUPPORT
+#include "plat_lfa.h"
+#endif /* LFA_SUPPORT */
/*******************************************************************************
* Forward declarations
diff --git a/include/services/bl31_lfa.h b/include/services/bl31_lfa.h
new file mode 100644
index 0000000..cfe436c
--- /dev/null
+++ b/include/services/bl31_lfa.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef BL31_LFA_H
+#define BL31_LFA_H
+
+#include <services/lfa_component_desc.h>
+
+struct lfa_component_ops *get_bl31_activator(void);
+
+#endif /* BL31_LFA_H */
diff --git a/include/services/lfa_component_desc.h b/include/services/lfa_component_desc.h
new file mode 100644
index 0000000..5f198bd
--- /dev/null
+++ b/include/services/lfa_component_desc.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef LFA_COMPONENT_DESC_H
+#define LFA_COMPONENT_DESC_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+typedef enum {
+ PRIME_NONE = 0,
+ PRIME_STARTED,
+ PRIME_COMPLETE,
+} lfa_prime_status_t;
+
+struct lfa_component_status {
+ uint32_t component_id;
+ lfa_prime_status_t prime_status;
+ bool cpu_rendezvous_required;
+};
+
+typedef int32_t (*component_prime_fn)(struct lfa_component_status *activation);
+typedef int32_t (*component_activate_fn)(struct lfa_component_status *activation,
+ uint64_t ep_address,
+ uint64_t context_id);
+
+struct lfa_component_ops {
+ component_prime_fn prime;
+ component_activate_fn activate;
+ bool may_reset_cpu;
+ bool cpu_rendezvous_required;
+};
+
+#endif /* LFA_COMPONENT_DESC_H */
diff --git a/include/services/lfa_holding_pen.h b/include/services/lfa_holding_pen.h
new file mode 100644
index 0000000..9420747
--- /dev/null
+++ b/include/services/lfa_holding_pen.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef LFA_HOLDING_PEN_H
+#define LFA_HOLDING_PEN_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <services/lfa_svc.h>
+
+bool lfa_holding_start(void);
+enum lfa_retc lfa_holding_wait(void);
+void lfa_holding_release(enum lfa_retc status);
+
+#endif
diff --git a/include/services/lfa_svc.h b/include/services/lfa_svc.h
new file mode 100644
index 0000000..69d549c
--- /dev/null
+++ b/include/services/lfa_svc.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef LFA_SVC_H
+#define LFA_SVC_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <lib/smccc.h>
+#include <services/lfa_component_desc.h>
+#include <tools_share/uuid.h>
+
+/*
+ * SMC function IDs for LFA Service
+ * Upper word bits set: Fast call, SMC64, Standard Secure Svc. Call (OEN = 4)
+ */
+#define LFA_FID(func_num) \
+ ((SMC_TYPE_FAST << FUNCID_TYPE_SHIFT) | \
+ (SMC_64 << FUNCID_CC_SHIFT) | \
+ (OEN_STD_START << FUNCID_OEN_SHIFT) | \
+ ((func_num) << FUNCID_NUM_SHIFT))
+
+#define LFA_VERSION LFA_FID(0x2E0)
+#define LFA_FEATURES LFA_FID(0x2E1)
+#define LFA_GET_INFO LFA_FID(0x2E2)
+#define LFA_GET_INVENTORY LFA_FID(0x2E3)
+#define LFA_PRIME LFA_FID(0x2E4)
+#define LFA_ACTIVATE LFA_FID(0x2E5)
+#define LFA_CANCEL LFA_FID(0x2E6)
+
+/* Check whether FID is in the range */
+#define is_lfa_fid(_fid) \
+ ((_fid >= LFA_VERSION) && (_fid <= LFA_CANCEL))
+
+/* LFA Service Calls version numbers */
+#define LFA_VERSION_MAJOR U(1)
+#define LFA_VERSION_MAJOR_SHIFT 16
+#define LFA_VERSION_MAJOR_MASK U(0x7FFF)
+#define LFA_VERSION_MINOR U(0)
+#define LFA_VERSION_MINOR_SHIFT 0
+#define LFA_VERSION_MINOR_MASK U(0xFFFF)
+
+#define LFA_VERSION_VAL \
+ ((((LFA_VERSION_MAJOR) & LFA_VERSION_MAJOR_MASK) << \
+ LFA_VERSION_MAJOR_SHIFT) \
+ | (((LFA_VERSION_MINOR) & LFA_VERSION_MINOR_MASK) << \
+ LFA_VERSION_MINOR_SHIFT))
+
+#define LFA_INVALID_COMPONENT U(0xFFFFFFFF)
+
+#define LFA_ACTIVATION_CAPABLE_SHIFT 0
+#define LFA_ACTIVATION_PENDING_SHIFT 1
+#define LFA_MAY_RESET_CPU_SHIFT 2
+#define LFA_CPU_RENDEZVOUS_OPTIONAL_SHIFT 3
+
+#define LFA_SKIP_CPU_RENDEZVOUS_BIT BIT(0)
+
+/* List of errors as per the specification */
+enum lfa_retc {
+ LFA_SUCCESS = 0,
+ LFA_NOT_SUPPORTED = -1,
+ LFA_BUSY = -2,
+ LFA_AUTH_ERROR = -3,
+ LFA_NO_MEMORY = -4,
+ LFA_CRITICAL_ERROR = -5,
+ LFA_DEVICE_ERROR = -6,
+ LFA_WRONG_STATE = -7,
+ LFA_INVALID_PARAMETERS = -8,
+ LFA_COMPONENT_WRONG_STATE = -9,
+ LFA_INVALID_ADDRESS = -10,
+ LFA_ACTIVATION_FAILED = -11,
+};
+
+/* Initialization routine for the LFA service */
+int lfa_setup(void);
+
+uint64_t lfa_smc_handler(uint32_t 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);
+void lfa_reset_activation(void);
+
+#endif /* LFA_SVC_H */
diff --git a/include/services/rmmd_rmm_lfa.h b/include/services/rmmd_rmm_lfa.h
new file mode 100644
index 0000000..6720cb5
--- /dev/null
+++ b/include/services/rmmd_rmm_lfa.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef RMMD_RMM_LFA_H
+#define RMMD_RMM_LFA_H
+
+#include <services/lfa_component_desc.h>
+
+struct lfa_component_ops *get_rmm_activator(void);
+
+#endif /* RMMD_RMM_LFA_H */
diff --git a/lib/el3_runtime/aarch32/cpu_data.S b/lib/el3_runtime/aarch32/cpu_data.S
deleted file mode 100644
index e59b7fd..0000000
--- a/lib/el3_runtime/aarch32/cpu_data.S
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2016, Arm Limited and Contributors. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <asm_macros.S>
-#include <lib/el3_runtime/cpu_data.h>
-
- .globl _cpu_data
- .globl _cpu_data_by_index
-
-/* -----------------------------------------------------------------
- * cpu_data_t *_cpu_data(void)
- *
- * Return the cpu_data structure for the current CPU.
- * -----------------------------------------------------------------
- */
-func _cpu_data
- /* r12 is pushed to meet the 8 byte stack alignment requirement */
- push {r12, lr}
- bl plat_my_core_pos
- pop {r12, lr}
- b _cpu_data_by_index
-endfunc _cpu_data
-
-/* -----------------------------------------------------------------
- * cpu_data_t *_cpu_data_by_index(uint32_t cpu_index)
- *
- * Return the cpu_data structure for the CPU with given linear index
- *
- * This can be called without a valid stack.
- * clobbers: r0, r1
- * -----------------------------------------------------------------
- */
-func _cpu_data_by_index
- mov_imm r1, CPU_DATA_SIZE
- mul r0, r0, r1
- ldr r1, =percpu_data
- add r0, r0, r1
- bx lr
-endfunc _cpu_data_by_index
diff --git a/lib/el3_runtime/aarch64/cpu_data.S b/lib/el3_runtime/aarch64/cpu_data.S
deleted file mode 100644
index 02d9415..0000000
--- a/lib/el3_runtime/aarch64/cpu_data.S
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (c) 2014-2025, Arm Limited and Contributors. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <asm_macros.S>
-#include <lib/el3_runtime/cpu_data.h>
-
-.globl _cpu_data_by_index
-
-/* -----------------------------------------------------------------
- * cpu_data_t *_cpu_data_by_index(uint32_t cpu_index)
- *
- * Return the cpu_data structure for the CPU with given linear index
- *
- * This can be called without a valid stack.
- * clobbers: x0, x1
- * -----------------------------------------------------------------
- */
-func _cpu_data_by_index
- mov_imm x1, CPU_DATA_SIZE
- mul x0, x0, x1
- adrp x1, percpu_data
- add x1, x1, :lo12:percpu_data
- add x0, x0, x1
- ret
-endfunc _cpu_data_by_index
diff --git a/lib/el3_runtime/cpu_data_array.c b/lib/el3_runtime/cpu_data_array.c
index 2056182..f2e97f0 100644
--- a/lib/el3_runtime/cpu_data_array.c
+++ b/lib/el3_runtime/cpu_data_array.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2016, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2014-2025, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -8,6 +8,14 @@
#include <lib/cassert.h>
#include <lib/el3_runtime/cpu_data.h>
+#include <plat/common/platform.h>
/* The per_cpu_ptr_cache_t space allocation */
cpu_data_t percpu_data[PLATFORM_CORE_COUNT];
+
+#ifndef __aarch64__
+cpu_data_t *_cpu_data(void)
+{
+ return _cpu_data_by_index(plat_my_core_pos());
+}
+#endif
diff --git a/lib/psci/psci_lib.mk b/lib/psci/psci_lib.mk
index 527ad3a..e1dbec2 100644
--- a/lib/psci/psci_lib.mk
+++ b/lib/psci/psci_lib.mk
@@ -1,11 +1,10 @@
#
-# Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved.
+# Copyright (c) 2016-2025, Arm Limited and Contributors. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
PSCI_LIB_SOURCES := lib/el3_runtime/cpu_data_array.c \
- lib/el3_runtime/${ARCH}/cpu_data.S \
lib/el3_runtime/${ARCH}/context_mgmt.c \
lib/cpus/${ARCH}/cpu_helpers.S \
lib/cpus/errata_report.c \
diff --git a/lib/psci/psci_main.c b/lib/psci/psci_main.c
index f126f49..dfec9a1 100644
--- a/lib/psci/psci_main.c
+++ b/lib/psci/psci_main.c
@@ -300,7 +300,7 @@
flush_cpu_data_by_index(target_idx,
psci_svc_cpu_data.aff_info_state);
- return psci_get_aff_info_state_by_idx(target_idx);
+ return (int)psci_get_aff_info_state_by_idx(target_idx);
}
int psci_migrate(u_register_t target_cpu)
diff --git a/lib/psci/psci_setup.c b/lib/psci/psci_setup.c
index 0863a82..44c9bdb 100644
--- a/lib/psci/psci_setup.c
+++ b/lib/psci/psci_setup.c
@@ -63,8 +63,7 @@
/* Initialize with an invalid mpidr */
psci_cpu_pd_nodes[node_idx].mpidr = PSCI_INVALID_MPIDR;
- svc_cpu_data =
- &(_cpu_data_by_index(node_idx)->psci_svc_cpu_data);
+ svc_cpu_data = &get_cpu_data_by_index(node_idx, psci_svc_cpu_data);
/* Set the Affinity Info for the cores as OFF */
svc_cpu_data->aff_info_state = AFF_STATE_OFF;
diff --git a/lib/transfer_list/transfer_list.c b/lib/transfer_list/transfer_list.c
deleted file mode 100644
index 4d4a167..0000000
--- a/lib/transfer_list/transfer_list.c
+++ /dev/null
@@ -1,546 +0,0 @@
-/*
- * Copyright (c) 2023, Linaro Limited and Contributors. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#include <arch.h>
-#include <assert.h>
-#include <inttypes.h>
-#include <string.h>
-
-#include <common/debug.h>
-#include <lib/transfer_list.h>
-#include <lib/utils_def.h>
-
-void transfer_list_dump(struct transfer_list_header *tl)
-{
- struct transfer_list_entry *te = NULL;
- int i = 0;
-
- if (!tl) {
- return;
- }
- INFO("Dump transfer list:\n");
- INFO("signature 0x%x\n", tl->signature);
- INFO("checksum 0x%x\n", tl->checksum);
- INFO("version 0x%x\n", tl->version);
- INFO("hdr_size 0x%x\n", tl->hdr_size);
- INFO("alignment 0x%x\n", tl->alignment);
- INFO("size 0x%x\n", tl->size);
- INFO("max_size 0x%x\n", tl->max_size);
- INFO("flags 0x%x\n", tl->flags);
- while (true) {
- te = transfer_list_next(tl, te);
- if (!te) {
- break;
- }
-
- INFO("Entry %d:\n", i++);
- transfer_entry_dump(te);
- }
-}
-
-void transfer_entry_dump(struct transfer_list_entry *te)
-{
- if (te) {
- INFO("tag_id 0x%x\n", te->tag_id);
- INFO("hdr_size 0x%x\n", te->hdr_size);
- INFO("data_size 0x%x\n", te->data_size);
- INFO("data_addr 0x%lx\n",
- (unsigned long)transfer_list_entry_data(te));
- }
-}
-
-/*******************************************************************************
- * Set the handoff arguments according to the transfer list payload
- * Return pointer to the entry point info if arguments are set properly
- * or NULL if not
- ******************************************************************************/
-entry_point_info_t *
-transfer_list_set_handoff_args(struct transfer_list_header *tl,
- entry_point_info_t *ep_info)
-{
- struct transfer_list_entry *te = NULL;
- void *dt = NULL;
-
- if (!ep_info || !tl || transfer_list_check_header(tl) == TL_OPS_NON) {
- return NULL;
- }
-
- te = transfer_list_find(tl, TL_TAG_FDT);
- dt = transfer_list_entry_data(te);
-
-#ifdef __aarch64__
- if (GET_RW(ep_info->spsr) == MODE_RW_64) {
- ep_info->args.arg0 = (uintptr_t)dt;
- ep_info->args.arg1 = TRANSFER_LIST_HANDOFF_X1_VALUE(REGISTER_CONVENTION_VERSION);
- ep_info->args.arg2 = 0;
- } else
-#endif
- {
- ep_info->args.arg0 = 0;
- ep_info->args.arg1 = TRANSFER_LIST_HANDOFF_R1_VALUE(REGISTER_CONVENTION_VERSION);
- ep_info->args.arg2 = (uintptr_t)dt;
- }
-
- ep_info->args.arg3 = (uintptr_t)tl;
-
- return ep_info;
-}
-
-/*******************************************************************************
- * Creating a transfer list in a reserved memory region specified
- * Compliant to 2.4.5 of Firmware handoff specification (v0.9)
- * Return pointer to the created transfer list or NULL on error
- ******************************************************************************/
-struct transfer_list_header *transfer_list_init(void *addr, size_t max_size)
-{
- struct transfer_list_header *tl = addr;
-
- if (!addr || max_size == 0) {
- return NULL;
- }
-
- if (!is_aligned((uintptr_t)addr, 1 << TRANSFER_LIST_INIT_MAX_ALIGN) ||
- !is_aligned(max_size, 1 << TRANSFER_LIST_INIT_MAX_ALIGN) ||
- max_size < sizeof(*tl)) {
- return NULL;
- }
-
- memset(tl, 0, max_size);
- tl->signature = TRANSFER_LIST_SIGNATURE;
- tl->version = TRANSFER_LIST_VERSION;
- tl->hdr_size = sizeof(*tl);
- tl->alignment = TRANSFER_LIST_INIT_MAX_ALIGN; /* initial max align */
- tl->size = sizeof(*tl); /* initial size is the size of header */
- tl->max_size = max_size;
- tl->flags = TL_FLAGS_HAS_CHECKSUM;
-
- transfer_list_update_checksum(tl);
-
- return tl;
-}
-
-/*******************************************************************************
- * Relocating a transfer list to a reserved memory region specified
- * Compliant to 2.4.6 of Firmware handoff specification (v0.9)
- * Return pointer to the relocated transfer list or NULL on error
- ******************************************************************************/
-struct transfer_list_header *
-transfer_list_relocate(struct transfer_list_header *tl, void *addr,
- size_t max_size)
-{
- uintptr_t new_addr, align_mask, align_off;
- struct transfer_list_header *new_tl;
- uint32_t new_max_size;
-
- if (!tl || !addr || max_size == 0) {
- return NULL;
- }
-
- align_mask = (1 << tl->alignment) - 1;
- align_off = (uintptr_t)tl & align_mask;
- new_addr = ((uintptr_t)addr & ~align_mask) + align_off;
-
- if (new_addr < (uintptr_t)addr) {
- new_addr += (1 << tl->alignment);
- }
-
- new_max_size = max_size - (new_addr - (uintptr_t)addr);
-
- /* the new space is not sufficient for the tl */
- if (tl->size > new_max_size) {
- return NULL;
- }
-
- new_tl = (struct transfer_list_header *)new_addr;
- memmove(new_tl, tl, tl->size);
- new_tl->max_size = new_max_size;
-
- transfer_list_update_checksum(new_tl);
-
- return new_tl;
-}
-
-/*******************************************************************************
- * Verifying the header of a transfer list
- * Compliant to 2.4.1 of Firmware handoff specification (v0.9)
- * Return transfer list operation status code
- ******************************************************************************/
-enum transfer_list_ops
-transfer_list_check_header(const struct transfer_list_header *tl)
-{
- if (!tl) {
- return TL_OPS_NON;
- }
-
- if (tl->signature != TRANSFER_LIST_SIGNATURE) {
- VERBOSE("Bad transfer list signature 0x%x\n", tl->signature);
- return TL_OPS_NON;
- }
-
- if (!tl->max_size) {
- VERBOSE("Bad transfer list max size 0x%x\n", tl->max_size);
- return TL_OPS_NON;
- }
-
- if (tl->size > tl->max_size) {
- VERBOSE("Bad transfer list size 0x%x\n", tl->size);
- return TL_OPS_NON;
- }
-
- if (tl->hdr_size != sizeof(struct transfer_list_header)) {
- VERBOSE("Bad transfer list header size 0x%x\n", tl->hdr_size);
- return TL_OPS_NON;
- }
-
- if (!transfer_list_verify_checksum(tl)) {
- VERBOSE("Bad transfer list checksum 0x%x\n", tl->checksum);
- return TL_OPS_NON;
- }
-
- if (tl->version == 0) {
- VERBOSE("Transfer list version is invalid\n");
- return TL_OPS_NON;
- } else if (tl->version == TRANSFER_LIST_VERSION) {
- INFO("Transfer list version is valid for all operations\n");
- return TL_OPS_ALL;
- } else if (tl->version > TRANSFER_LIST_VERSION) {
- INFO("Transfer list version is valid for read-only\n");
- return TL_OPS_RO;
- }
-
- INFO("Old transfer list version is detected\n");
- return TL_OPS_CUS;
-}
-
-/*******************************************************************************
- * Enumerate the next transfer entry
- * Return pointer to the next transfer entry or NULL on error
- ******************************************************************************/
-struct transfer_list_entry *transfer_list_next(struct transfer_list_header *tl,
- struct transfer_list_entry *last)
-{
- struct transfer_list_entry *te = NULL;
- uintptr_t tl_ev = 0;
- uintptr_t va = 0;
- uintptr_t ev = 0;
- size_t sz = 0;
-
- if (!tl) {
- return NULL;
- }
-
- tl_ev = (uintptr_t)tl + tl->size;
-
- if (last) {
- va = (uintptr_t)last;
- /* check if the total size overflow */
- if (add_overflow(last->hdr_size, last->data_size, &sz)) {
- return NULL;
- }
- /* roundup to the next entry */
- if (add_with_round_up_overflow(va, sz, TRANSFER_LIST_GRANULE,
- &va)) {
- return NULL;
- }
- } else {
- va = (uintptr_t)tl + tl->hdr_size;
- }
-
- te = (struct transfer_list_entry *)va;
-
- if (va + sizeof(*te) > tl_ev || te->hdr_size < sizeof(*te) ||
- add_overflow(te->hdr_size, te->data_size, &sz) ||
- add_overflow(va, sz, &ev) || ev > tl_ev) {
- return NULL;
- }
-
- return te;
-}
-
-/*******************************************************************************
- * Calculate the byte sum of a transfer list
- * Return byte sum of the transfer list
- ******************************************************************************/
-static uint8_t calc_byte_sum(const struct transfer_list_header *tl)
-{
- uint8_t *b = (uint8_t *)tl;
- uint8_t cs = 0;
- size_t n = 0;
-
- for (n = 0; n < tl->size; n++) {
- cs += b[n];
- }
-
- return cs;
-}
-
-/*******************************************************************************
- * Update the checksum of a transfer list
- * Return updated checksum of the transfer list
- ******************************************************************************/
-void transfer_list_update_checksum(struct transfer_list_header *tl)
-{
- uint8_t cs;
-
- if (!tl || !(tl->flags & TL_FLAGS_HAS_CHECKSUM)) {
- return;
- }
-
- cs = calc_byte_sum(tl);
- cs -= tl->checksum;
- cs = 256 - cs;
- tl->checksum = cs;
- assert(transfer_list_verify_checksum(tl));
-}
-
-/*******************************************************************************
- * Verify the checksum of a transfer list
- * Return true if verified or false if not
- ******************************************************************************/
-bool transfer_list_verify_checksum(const struct transfer_list_header *tl)
-{
- if (!tl) {
- return false;
- }
-
- if (!(tl->flags & TL_FLAGS_HAS_CHECKSUM)) {
- return true;
- }
-
- return !calc_byte_sum(tl);
-}
-
-/*******************************************************************************
- * Update the data size of a transfer entry
- * Return true on success or false on error
- ******************************************************************************/
-bool transfer_list_set_data_size(struct transfer_list_header *tl,
- struct transfer_list_entry *te,
- uint32_t new_data_size)
-{
- uintptr_t tl_old_ev, new_ev = 0, old_ev = 0, ru_new_ev;
- struct transfer_list_entry *dummy_te = NULL;
- size_t gap = 0;
- size_t mov_dis = 0;
- size_t sz = 0;
-
- if (!tl || !te) {
- return false;
- }
- tl_old_ev = (uintptr_t)tl + tl->size;
-
- /*
- * calculate the old and new end of TE
- * both must be roundup to align with TRANSFER_LIST_GRANULE
- */
- if (add_overflow(te->hdr_size, te->data_size, &sz) ||
- add_with_round_up_overflow((uintptr_t)te, sz, TRANSFER_LIST_GRANULE,
- &old_ev)) {
- return false;
- }
- if (add_overflow(te->hdr_size, new_data_size, &sz) ||
- add_with_round_up_overflow((uintptr_t)te, sz, TRANSFER_LIST_GRANULE,
- &new_ev)) {
- return false;
- }
-
- if (new_ev > old_ev) {
- /*
- * move distance should be roundup
- * to meet the requirement of TE data max alignment
- * ensure that the increased size doesn't exceed
- * the max size of TL
- */
- mov_dis = new_ev - old_ev;
- if (round_up_overflow(mov_dis, 1 << tl->alignment, &mov_dis) ||
- tl->size + mov_dis > tl->max_size) {
- return false;
- }
- ru_new_ev = old_ev + mov_dis;
- memmove((void *)ru_new_ev, (void *)old_ev, tl_old_ev - old_ev);
- tl->size += mov_dis;
- gap = ru_new_ev - new_ev;
- } else {
- gap = old_ev - new_ev;
- }
-
- if (gap >= sizeof(*dummy_te)) {
- /* create a dummy TE to fill up the gap */
- dummy_te = (struct transfer_list_entry *)new_ev;
- dummy_te->tag_id = TL_TAG_EMPTY;
- dummy_te->hdr_size = sizeof(*dummy_te);
- dummy_te->data_size = gap - sizeof(*dummy_te);
- }
-
- te->data_size = new_data_size;
-
- transfer_list_update_checksum(tl);
- return true;
-}
-
-/*******************************************************************************
- * Remove a specified transfer entry from a transfer list
- * Return true on success or false on error
- ******************************************************************************/
-bool transfer_list_rem(struct transfer_list_header *tl,
- struct transfer_list_entry *te)
-{
- if (!tl || !te || (uintptr_t)te > (uintptr_t)tl + tl->size) {
- return false;
- }
- te->tag_id = TL_TAG_EMPTY;
- transfer_list_update_checksum(tl);
- return true;
-}
-
-/*******************************************************************************
- * Add a new transfer entry into a transfer list
- * Compliant to 2.4.3 of Firmware handoff specification (v0.9)
- * Return pointer to the added transfer entry or NULL on error
- ******************************************************************************/
-struct transfer_list_entry *transfer_list_add(struct transfer_list_header *tl,
- uint32_t tag_id,
- uint32_t data_size,
- const void *data)
-{
- uintptr_t max_tl_ev, tl_ev, ev;
- struct transfer_list_entry *te = NULL;
- uint8_t *te_data = NULL;
- size_t sz = 0;
-
- if (!tl) {
- return NULL;
- }
-
- max_tl_ev = (uintptr_t)tl + tl->max_size;
- tl_ev = (uintptr_t)tl + tl->size;
- ev = tl_ev;
-
- /*
- * skip the step 1 (optional step)
- * new TE will be added into the tail
- */
- if (add_overflow(sizeof(*te), data_size, &sz) ||
- add_with_round_up_overflow(ev, sz, TRANSFER_LIST_GRANULE, &ev) ||
- ev > max_tl_ev) {
- return NULL;
- }
-
- te = (struct transfer_list_entry *)tl_ev;
- te->tag_id = tag_id;
- te->hdr_size = sizeof(*te);
- te->data_size = data_size;
- tl->size += ev - tl_ev;
-
- if (data) {
- /* get TE data pointer */
- te_data = transfer_list_entry_data(te);
- if (!te_data) {
- return NULL;
- }
- memmove(te_data, data, data_size);
- }
-
- transfer_list_update_checksum(tl);
-
- return te;
-}
-
-/*******************************************************************************
- * Add a new transfer entry into a transfer list with specified new data
- * alignment requirement
- * Compliant to 2.4.4 of Firmware handoff specification (v0.9)
- * Return pointer to the added transfer entry or NULL on error
- ******************************************************************************/
-struct transfer_list_entry *
-transfer_list_add_with_align(struct transfer_list_header *tl, uint32_t tag_id,
- uint32_t data_size, const void *data,
- uint8_t alignment)
-{
- struct transfer_list_entry *te = NULL;
- uintptr_t tl_ev, ev, new_tl_ev;
- size_t dummy_te_data_sz = 0;
-
- if (!tl) {
- return NULL;
- }
-
- tl_ev = (uintptr_t)tl + tl->size;
- ev = tl_ev + sizeof(struct transfer_list_entry);
-
- if (!is_aligned(ev, 1 << alignment)) {
- /*
- * TE data address is not aligned to the new alignment
- * fill the gap with an empty TE as a placeholder before
- * adding the desire TE
- */
- new_tl_ev = round_up(ev, 1 << alignment) -
- sizeof(struct transfer_list_entry);
- dummy_te_data_sz =
- new_tl_ev - tl_ev - sizeof(struct transfer_list_entry);
- if (!transfer_list_add(tl, TL_TAG_EMPTY, dummy_te_data_sz,
- NULL)) {
- return NULL;
- }
- }
-
- te = transfer_list_add(tl, tag_id, data_size, data);
-
- if (alignment > tl->alignment) {
- tl->alignment = alignment;
- transfer_list_update_checksum(tl);
- }
-
- return te;
-}
-
-/*******************************************************************************
- * Search for an existing transfer entry with the specified tag id from a
- * transfer list
- * Return pointer to the found transfer entry or NULL on error
- ******************************************************************************/
-struct transfer_list_entry *transfer_list_find(struct transfer_list_header *tl,
- uint32_t tag_id)
-{
- struct transfer_list_entry *te = NULL;
-
- do {
- te = transfer_list_next(tl, te);
- } while (te && (te->tag_id != tag_id));
-
- return te;
-}
-
-/*******************************************************************************
- * Retrieve the data pointer of a specified transfer entry
- * Return pointer to the transfer entry data or NULL on error
- ******************************************************************************/
-void *transfer_list_entry_data(struct transfer_list_entry *entry)
-{
- if (!entry) {
- return NULL;
- }
- return (uint8_t *)entry + entry->hdr_size;
-}
-
-/*******************************************************************************
- * Verifies that the transfer list has not already been initialized, then
- * initializes it at the specified memory location.
- *
- * Return pointer to the transfer list or NULL on error
- * *****************************************************************************/
-struct transfer_list_header *transfer_list_ensure(void *addr, size_t size)
-{
- struct transfer_list_header *tl = NULL;
-
- if (transfer_list_check_header(addr) == TL_OPS_ALL) {
- return (struct transfer_list_header *)addr;
- }
-
- tl = transfer_list_init((void *)addr, size);
-
- return tl;
-}
diff --git a/lib/transfer_list/transfer_list.mk b/lib/transfer_list/transfer_list.mk
index 3ec4df2..91b6b57 100644
--- a/lib/transfer_list/transfer_list.mk
+++ b/lib/transfer_list/transfer_list.mk
@@ -1,21 +1,25 @@
#
-# Copyright (c) 2023-2024, Arm Limited and Contributors. All rights reserved.
+# Copyright (c) 2023-2025, Arm Limited and Contributors. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
ifeq (${TRANSFER_LIST},1)
-ifeq (${ARCH},aarch32)
-$(eval $(call add_define,TRANSFER_LIST_AARCH32))
-endif
+# Default path if not set externally
+LIBTL_PATH ?= contrib/libtl
-TRANSFER_LIST_SOURCES += $(addprefix lib/transfer_list/, \
- transfer_list.c)
+# Common include paths (always needed)
+INCLUDES += -I$(LIBTL_PATH)/include \
+ -I$(LIBTL_PATH)/include/arm
-BL31_SOURCES += $(TRANSFER_LIST_SOURCES)
-BL2_SOURCES += $(TRANSFER_LIST_SOURCES)
-BL1_SOURCES += $(TRANSFER_LIST_SOURCES)
+LIBTL_SRC_PATH := $(LIBTL_PATH)/src
+
+LIBTL_SRCS := $(addprefix $(LIBTL_SRC_PATH)/, \
+ arm/ep_info.c \
+ generic/logging.c \
+ generic/transfer_list.c)
+
+$(eval $(call MAKE_LIB,tl))
endif # TRANSFER_LIST
-
diff --git a/make_helpers/defaults.mk b/make_helpers/defaults.mk
index 4ccca9f..b9df27e 100644
--- a/make_helpers/defaults.mk
+++ b/make_helpers/defaults.mk
@@ -435,3 +435,9 @@
# This flag is temporary and it is expected once the interface is
# finalized, this flag will be removed.
RMMD_ENABLE_IDE_KEY_PROG := 0
+
+# Live firmware activation support
+LFA_SUPPORT := 0
+
+# Enable support for arm DSU driver.
+USE_DSU_DRIVER := 0
diff --git a/plat/amd/common/include/plat_xfer_list.h b/plat/amd/common/include/plat_xfer_list.h
index 24a9c0c..1563200 100644
--- a/plat/amd/common/include/plat_xfer_list.h
+++ b/plat/amd/common/include/plat_xfer_list.h
@@ -7,7 +7,7 @@
#ifndef PLAT_XFER_LIST_H
#define PLAT_XFER_LIST_H
-#include <lib/transfer_list.h>
+#include <transfer_list.h>
int32_t transfer_list_populate_ep_info(entry_point_info_t *bl32,
entry_point_info_t *bl33);
diff --git a/plat/amd/common/plat_fdt.c b/plat/amd/common/plat_fdt.c
index e72c0dd..194d538 100644
--- a/plat/amd/common/plat_fdt.c
+++ b/plat/amd/common/plat_fdt.c
@@ -10,7 +10,9 @@
#include <platform_def.h>
#include <plat_fdt.h>
+#ifdef TRANSFER_LIST
#include <plat_xfer_list.h>
+#endif
#define FIT_CONFS_PATH "/configurations"
diff --git a/plat/amd/common/plat_xfer_list.c b/plat/amd/common/plat_xfer_list.c
index 19c882b..d8dc09d 100644
--- a/plat/amd/common/plat_xfer_list.c
+++ b/plat/amd/common/plat_xfer_list.c
@@ -4,11 +4,12 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stddef.h>
-#include <arch_helpers.h>
-#include <common/debug.h>
-#include <lib/transfer_list.h>
+
#include <platform_def.h>
+#include <arch_helpers.h>
+#include <common/debug.h>
+#include <transfer_list.h>
static struct transfer_list_header *tl_hdr;
static int32_t tl_ops_holder;
diff --git a/plat/amd/versal2/bl31_setup.c b/plat/amd/versal2/bl31_setup.c
index 94ace7d..28bcaa1 100644
--- a/plat/amd/versal2/bl31_setup.c
+++ b/plat/amd/versal2/bl31_setup.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2020, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2018-2025, Arm Limited and Contributors. All rights reserved.
* Copyright (c) 2018-2022, Xilinx, Inc. All rights reserved.
* Copyright (c) 2022-2025, Advanced Micro Devices, Inc. All rights reserved.
*
@@ -27,7 +27,9 @@
#include <plat_fdt.h>
#include <plat_private.h>
#include <plat_startup.h>
+#if TRANSFER_LIST
#include <plat_xfer_list.h>
+#endif
#include <pm_api_sys.h>
#include <pm_client.h>
diff --git a/plat/arm/board/fvp/fvp_bl2_setup.c b/plat/arm/board/fvp/fvp_bl2_setup.c
index 989f058..852a1e7 100644
--- a/plat/arm/board/fvp/fvp_bl2_setup.c
+++ b/plat/arm/board/fvp/fvp_bl2_setup.c
@@ -12,7 +12,9 @@
#include <fvp_pas_def.h>
#include <lib/fconf/fconf.h>
#include <lib/fconf/fconf_dyn_cfg_getter.h>
-#include <lib/transfer_list.h>
+#if TRANSFER_LIST
+#include <transfer_list.h>
+#endif
#include <plat/arm/common/plat_arm.h>
#include <plat/common/platform.h>
diff --git a/plat/arm/board/fvp/fvp_lfa.c b/plat/arm/board/fvp/fvp_lfa.c
new file mode 100644
index 0000000..3c5321d
--- /dev/null
+++ b/plat/arm/board/fvp/fvp_lfa.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <errno.h>
+#include <plat/common/platform.h>
+#include <services/bl31_lfa.h>
+#include <services/rmmd_rmm_lfa.h>
+#include <tools_share/firmware_image_package.h>
+
+#include <fvp_lfa_components.h>
+
+/* Keep this array consistent with enum fvp_lfa_component_id_t */
+static plat_lfa_component_info_t fvp_lfa_components[LFA_MAX_DEFINED_COMPONENTS] = {
+ [LFA_BL31_COMPONENT] = {LFA_BL31_COMPONENT, UUID_EL3_RUNTIME_FIRMWARE_BL31,
+ NULL, false},
+#if BL32_BASE
+ [LFA_BL32_COMPONENT] = {LFA_BL32_COMPONENT, UUID_SECURE_PAYLOAD_BL32,
+ NULL, false},
+#endif /* BL32_BASE */
+ [LFA_BL33_COMPONENT] = {LFA_BL33_COMPONENT, UUID_NON_TRUSTED_FIRMWARE_BL33,
+ NULL, false},
+#if ENABLE_RME
+ [LFA_RMM_COMPONENT] = {LFA_RMM_COMPONENT, UUID_REALM_MONITOR_MGMT_FIRMWARE,
+ NULL, false},
+#endif /* ENABLE_RME */
+};
+
+uint32_t plat_lfa_get_components(plat_lfa_component_info_t **components)
+{
+ if (components == NULL) {
+ return -EINVAL;
+ }
+
+ fvp_lfa_components[LFA_BL31_COMPONENT].activator = get_bl31_activator();
+#if ENABLE_RME
+ fvp_lfa_components[LFA_RMM_COMPONENT].activator = get_rmm_activator();
+#endif /* ENABLE_RME */
+
+ *components = fvp_lfa_components;
+ return LFA_MAX_DEFINED_COMPONENTS;
+}
+
+bool is_plat_lfa_activation_pending(uint32_t lfa_component_id)
+{
+#if ENABLE_RME
+ if (lfa_component_id == LFA_RMM_COMPONENT) {
+ return true;
+ }
+#endif /* ENABLE_RME */
+
+ return false;
+}
+
+int plat_lfa_cancel(uint32_t lfa_component_id)
+{
+ /* placeholder function to do cancel LFA of given component */
+ return 0;
+}
+
+int plat_lfa_load_auth_image(uint32_t img_id)
+{
+ /*
+ * In AEM FVP, we don't want to bloat the code by adding
+ * loading and authentication mechanism, so here we assumed
+ * that the components are pre-loaded and authenticated already.
+ */
+ return 0;
+}
diff --git a/plat/arm/board/fvp/include/fvp_lfa_components.h b/plat/arm/board/fvp/include/fvp_lfa_components.h
new file mode 100644
index 0000000..09dcdfd
--- /dev/null
+++ b/plat/arm/board/fvp/include/fvp_lfa_components.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef FVP_LFA_COMPONENTS_H
+#define FVP_LFA_COMPONENTS_H
+
+/*
+ * Define platform-specific numeric IDs for LFA FVP components.
+ */
+typedef enum {
+ LFA_BL31_COMPONENT = 0,
+#if BL32_BASE
+ LFA_BL32_COMPONENT,
+#endif /* BL32_BASE */
+ LFA_BL33_COMPONENT,
+#if ENABLE_RME
+ LFA_RMM_COMPONENT,
+#endif /* ENABLE_RME */
+ LFA_MAX_DEFINED_COMPONENTS
+} fvp_lfa_component_id_t;
+
+#endif /* FVP_LFA_COMPONENTS_H */
diff --git a/plat/arm/board/fvp/platform.mk b/plat/arm/board/fvp/platform.mk
index ea1b7e7..7f1dfc6 100644
--- a/plat/arm/board/fvp/platform.mk
+++ b/plat/arm/board/fvp/platform.mk
@@ -591,3 +591,7 @@
# Build macro necessary for running SPM tests on FVP platform
$(eval $(call add_define,PLAT_TEST_SPM))
+
+ifeq (${LFA_SUPPORT},1)
+BL31_SOURCES += plat/arm/board/fvp/fvp_lfa.c
+endif
diff --git a/plat/arm/board/tc/platform.mk b/plat/arm/board/tc/platform.mk
index b29f0d6..b7edf28 100644
--- a/plat/arm/board/tc/platform.mk
+++ b/plat/arm/board/tc/platform.mk
@@ -16,6 +16,7 @@
HW_ASSISTED_COHERENCY := 1
USE_COHERENT_MEM := 0
USE_GIC_DRIVER := 3
+USE_DSU_DRIVER := 1
GIC_ENABLE_V4_EXTN := 1
GICV3_SUPPORT_GIC600 := 1
override NEED_BL2U := no
@@ -162,7 +163,7 @@
${TC_BASE}/tc_topology.c \
lib/fconf/fconf.c \
lib/fconf/fconf_dyn_cfg_getter.c \
- drivers/arm/css/dsu/dsu.c \
+ drivers/arm/dsu/dsu.c \
drivers/cfi/v2m/v2m_flash.c \
lib/utils/mem_region.c \
plat/arm/common/arm_nor_psci_mem_protect.c \
diff --git a/plat/arm/board/tc/tc_bl31_setup.c b/plat/arm/board/tc/tc_bl31_setup.c
index 7f2014b..073e487 100644
--- a/plat/arm/board/tc/tc_bl31_setup.c
+++ b/plat/arm/board/tc/tc_bl31_setup.c
@@ -14,6 +14,7 @@
#include <common/debug.h>
#include <drivers/arm/css/css_mhu_doorbell.h>
#include <drivers/arm/css/scmi.h>
+#include <drivers/arm/dsu.h>
#include <drivers/arm/sbsa.h>
#include <lib/fconf/fconf.h>
#include <lib/fconf/fconf_dyn_cfg_getter.h>
@@ -74,6 +75,13 @@
#endif
};
+const dsu_driver_data_t plat_dsu_data = {
+ .clusterpwrdwn_pwrdn = false,
+ .clusterpwrdwn_memret = false,
+ .clusterpwrctlr_cachepwr = CLUSTERPWRCTLR_CACHEPWR_RESET,
+ .clusterpwrctlr_funcret = CLUSTERPWRCTLR_FUNCRET_RESET
+};
+
#if (TARGET_PLATFORM == 3) || (TARGET_PLATFORM == 4)
static void enable_ns_mcn_pmu(void)
{
diff --git a/plat/arm/common/arm_bl1_setup.c b/plat/arm/common/arm_bl1_setup.c
index 06a919c..adfc848 100644
--- a/plat/arm/common/arm_bl1_setup.c
+++ b/plat/arm/common/arm_bl1_setup.c
@@ -15,7 +15,7 @@
#include <lib/fconf/fconf.h>
#include <lib/fconf/fconf_dyn_cfg_getter.h>
#if TRANSFER_LIST
-#include <lib/transfer_list.h>
+#include <transfer_list.h>
#endif
#include <lib/utils.h>
#include <lib/xlat_tables/xlat_tables_compat.h>
diff --git a/plat/arm/common/arm_bl2_setup.c b/plat/arm/common/arm_bl2_setup.c
index 522017f..78ab862 100644
--- a/plat/arm/common/arm_bl2_setup.c
+++ b/plat/arm/common/arm_bl2_setup.c
@@ -20,7 +20,7 @@
#include <lib/fconf/fconf_dyn_cfg_getter.h>
#include <lib/gpt_rme/gpt_rme.h>
#if TRANSFER_LIST
-#include <lib/transfer_list.h>
+#include <transfer_list.h>
#endif
#ifdef SPD_opteed
#include <lib/optee_utils.h>
@@ -321,6 +321,7 @@
{
entry_point_info_t *ep __unused;
+#if TRANSFER_LIST
/*
* Information might have been added to the TL before this (i.e. event log)
* make sure the checksum is up to date.
@@ -332,4 +333,5 @@
assert(ep != NULL);
arm_transfer_list_populate_ep_info(next_param_node, secure_tl);
+#endif
}
diff --git a/plat/arm/common/arm_bl31_setup.c b/plat/arm/common/arm_bl31_setup.c
index f196269..ce6b21e 100644
--- a/plat/arm/common/arm_bl31_setup.c
+++ b/plat/arm/common/arm_bl31_setup.c
@@ -18,7 +18,7 @@
#include <lib/gpt_rme/gpt_rme.h>
#include <lib/mmio.h>
#if TRANSFER_LIST
-#include <lib/transfer_list.h>
+#include <transfer_list.h>
#endif
#include <lib/xlat_tables/xlat_tables_compat.h>
#include <plat/arm/common/plat_arm.h>
diff --git a/plat/arm/common/arm_common.mk b/plat/arm/common/arm_common.mk
index 53fe806..6c2b1ba 100644
--- a/plat/arm/common/arm_common.mk
+++ b/plat/arm/common/arm_common.mk
@@ -320,8 +320,11 @@
endif
ifeq (${TRANSFER_LIST}, 1)
- include lib/transfer_list/transfer_list.mk
- TRANSFER_LIST_SOURCES += plat/arm/common/arm_transfer_list.c
+include lib/transfer_list/transfer_list.mk
+
+BL1_SOURCES += plat/arm/common/arm_transfer_list.c
+BL2_SOURCES += plat/arm/common/arm_transfer_list.c
+BL31_SOURCES += plat/arm/common/arm_transfer_list.c
endif
ifneq ($(filter 1,${ENABLE_PMF} ${ETHOSN_NPU_DRIVER}),)
diff --git a/plat/arm/css/common/css_pm.c b/plat/arm/css/common/css_pm.c
index 18882d3..80da3d9 100644
--- a/plat/arm/css/common/css_pm.c
+++ b/plat/arm/css/common/css_pm.c
@@ -12,7 +12,7 @@
#include <bl31/interrupt_mgmt.h>
#include <common/debug.h>
#include <drivers/arm/css/css_scp.h>
-#include <drivers/arm/css/dsu.h>
+#include <drivers/arm/dsu.h>
#include <lib/cassert.h>
#include <plat/arm/common/plat_arm.h>
diff --git a/plat/common/plat_bl_common.c b/plat/common/plat_bl_common.c
index 4772bad..f10f2d7 100644
--- a/plat/common/plat_bl_common.c
+++ b/plat/common/plat_bl_common.c
@@ -9,7 +9,9 @@
#include <arch_helpers.h>
#include <common/bl_common.h>
#include <common/debug.h>
-#include <lib/transfer_list.h>
+#if TRANSFER_LIST
+#include <transfer_list.h>
+#endif
#include <lib/xlat_tables/xlat_tables_compat.h>
#include <plat/common/platform.h>
#include <services/arm_arch_svc.h>
diff --git a/plat/imx/imx8m/imx8mp/gpc.c b/plat/imx/imx8m/imx8mp/gpc.c
index a95eb36..5e2d9e4 100644
--- a/plat/imx/imx8m/imx8mp/gpc.c
+++ b/plat/imx/imx8m/imx8mp/gpc.c
@@ -268,23 +268,6 @@
/* set the PGC bit */
mmio_setbits_32(IMX_GPC_BASE + pwr_domain->pgc_offset, 0x1);
- /*
- * leave the G1, G2, H1 power domain on until VPUMIX power off,
- * otherwise system will hang due to VPUMIX ACK
- */
- if (domain_id == VPU_H1 || domain_id == VPU_G1 || domain_id == VPU_G2) {
- return;
- }
-
- if (domain_id == VPUMIX) {
- mmio_write_32(IMX_GPC_BASE + PU_PGC_DN_TRG, VPU_G1_PWR_REQ |
- VPU_G2_PWR_REQ | VPU_H1_PWR_REQ);
-
- while (mmio_read_32(IMX_GPC_BASE + PU_PGC_DN_TRG) & (VPU_G1_PWR_REQ |
- VPU_G2_PWR_REQ | VPU_H1_PWR_REQ))
- ;
- }
-
/* power down the domain */
mmio_setbits_32(IMX_GPC_BASE + PU_PGC_DN_TRG, pwr_domain->pwr_req);
diff --git a/plat/imx/imx8ulp/imx8ulp_psci.c b/plat/imx/imx8ulp/imx8ulp_psci.c
index 59af8be..e67d0b5 100644
--- a/plat/imx/imx8ulp/imx8ulp_psci.c
+++ b/plat/imx/imx8ulp/imx8ulp_psci.c
@@ -289,7 +289,9 @@
/* LDO1 should be power off in PD mode */
} else if (mode == PD_PWR_MODE) {
/* overwrite the buck3 voltage setting in active mode */
- upower_pmic_i2c_read(0x22, &volt);
+ if (upower_pmic_i2c_read(0x22, &volt) != 0) {
+ panic();
+ }
pd_pmic_reg_cfgs[3].i2c_data = volt;
memcpy(&pwr_sys_cfg->ps_apd_pmic_reg_data_cfg, &pd_pmic_reg_cfgs,
sizeof(ps_apd_pmic_reg_data_cfgs_t));
diff --git a/plat/mediatek/drivers/pmic/rules.mk b/plat/mediatek/drivers/pmic/rules.mk
index dc228ee..13ce658 100644
--- a/plat/mediatek/drivers/pmic/rules.mk
+++ b/plat/mediatek/drivers/pmic/rules.mk
@@ -9,8 +9,8 @@
MODULE := pmic
ifeq (${CONFIG_MTK_PMIC_SHUTDOWN_V2}, y)
-LOCAL_SRCS-y += ${LOCAL_DIR}/pmic_common_swap_api.c
-LOCAL_SRCS-y := ${LOCAL_DIR}/pmic_psc.c
+LOCAL_SRCS-y := ${LOCAL_DIR}/pmic_common_swap_api.c
+LOCAL_SRCS-y += ${LOCAL_DIR}/pmic_psc.c
LOCAL_SRCS-${CONFIG_MTK_PMIC_LOWPOWER} += ${LOCAL_DIR}/${MTK_SOC}/pmic_lowpower_init.c
LOCAL_SRCS-${CONFIG_MTK_PMIC_LOWPOWER} += ${LOCAL_DIR}/${MTK_SOC}/pmic_swap_api.c
LOCAL_SRCS-${CONFIG_MTK_PMIC_SHUTDOWN_CFG} += ${LOCAL_DIR}/${MTK_SOC}/pmic_shutdown_cfg.c
diff --git a/plat/nxp/common/sip_svc/sip_svc.c b/plat/nxp/common/sip_svc/sip_svc.c
index 1c8668e..4eec0ac 100644
--- a/plat/nxp/common/sip_svc/sip_svc.c
+++ b/plat/nxp/common/sip_svc/sip_svc.c
@@ -100,7 +100,7 @@
}
/* break is not required as SMC_RETx return */
case SIP_SVC_HUK:
- if (is_sec_enabled() == false) {
+ if (ns != 0 || is_sec_enabled() == false) {
NOTICE("SEC is disabled.\n");
SMC_RET1(handle, SMC_UNK);
}
diff --git a/plat/nxp/s32/s32g274ardb2/include/platform_def.h b/plat/nxp/s32/s32g274ardb2/include/platform_def.h
index cb16658..227c8e6 100644
--- a/plat/nxp/s32/s32g274ardb2/include/platform_def.h
+++ b/plat/nxp/s32/s32g274ardb2/include/platform_def.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2024 NXP
+ * Copyright 2024-2025 NXP
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -44,24 +44,34 @@
#define BL33_BASE UL(0x34500000)
#define BL33_LIMIT UL(0x345FF000)
+/* IO buffer used to copy images from storage */
+#define IO_BUFFER_BASE BL33_LIMIT
+#define IO_BUFFER_SIZE U(0x13000)
+
#define PLAT_PHY_ADDR_SPACE_SIZE (ULL(1) << 36)
/* We'll be doing a 1:1 mapping anyway */
#define PLAT_VIRT_ADDR_SPACE_SIZE (ULL(1) << 36)
-#define MAX_MMAP_REGIONS U(18)
-#define MAX_XLAT_TABLES U(32)
+#define MAX_MMAP_REGIONS U(21)
+#define MAX_XLAT_TABLES U(33)
/* Console settings */
#define UART_BASE UL(0x401C8000)
#define UART_BAUDRATE U(115200)
#define UART_CLOCK_HZ U(125000000)
+/* uSDHC */
+#define S32G_USDHC_BASE UL(0x402F0000)
+
#define S32G_FIP_BASE UL(0x34100000)
#define S32G_FIP_SIZE UL(0x100000)
#define MAX_IO_HANDLES U(2)
#define MAX_IO_DEVICES U(2)
+/* uSDHC as block device */
+#define MAX_IO_BLOCK_DEVICES U(1)
+
/* GIC settings */
#define S32G_GIC_BASE UL(0x50800000)
#define PLAT_GICD_BASE S32G_GIC_BASE
diff --git a/plat/nxp/s32/s32g274ardb2/plat_bl2_el3_setup.c b/plat/nxp/s32/s32g274ardb2/plat_bl2_el3_setup.c
index 0929f9d..810b7bb 100644
--- a/plat/nxp/s32/s32g274ardb2/plat_bl2_el3_setup.c
+++ b/plat/nxp/s32/s32g274ardb2/plat_bl2_el3_setup.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2024 NXP
+ * Copyright 2024-2025 NXP
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -8,7 +8,10 @@
#include <common/debug.h>
#include <common/desc_image_load.h>
+#include <drivers/generic_delay_timer.h>
+#include <imx_usdhc.h>
#include <lib/mmio.h>
+#include <lib/utils.h>
#include <lib/xlat_tables/xlat_tables_v2.h>
#include <plat/common/platform.h>
#include <plat_console.h>
@@ -70,6 +73,29 @@
mmio_write_32(SIUL2_PC10_LIN0_IMCR, LIN0_RX_IMCR_CFG);
}
+static void init_s32g_usdhc(void)
+{
+ static struct mmc_device_info sd_device_info = {
+ .mmc_dev_type = MMC_IS_SD_HC,
+ .ocr_voltage = OCR_3_2_3_3 | OCR_3_3_3_4,
+ };
+ imx_usdhc_params_t params;
+
+ zeromem(¶ms, sizeof(imx_usdhc_params_t));
+
+ params.reg_base = S32G_USDHC_BASE;
+ params.clk_rate = 25000000;
+ params.bus_width = MMC_BUS_WIDTH_4;
+ params.flags = MMC_FLAG_SD_CMD6;
+
+ imx_usdhc_init(¶ms, &sd_device_info);
+}
+
+static void plat_s32_mmc_setup(void)
+{
+ init_s32g_usdhc();
+}
+
void bl2_el3_early_platform_setup(u_register_t arg0, u_register_t arg1,
u_register_t arg2, u_register_t arg3)
{
@@ -103,9 +129,18 @@
panic();
}
+ generic_delay_timer_init();
+
+ /* Configure the generic timer frequency to ensure proper operation
+ * of the architectural timer in BL2.
+ */
+ write_cntfrq_el0(plat_get_syscnt_freq2());
+
linflex_config_pinctrl();
console_s32g2_register();
+ plat_s32_mmc_setup();
+
plat_s32g2_io_setup();
}
diff --git a/plat/nxp/s32/s32g274ardb2/plat_helpers.S b/plat/nxp/s32/s32g274ardb2/plat_helpers.S
index a7dda0d..924808b 100644
--- a/plat/nxp/s32/s32g274ardb2/plat_helpers.S
+++ b/plat/nxp/s32/s32g274ardb2/plat_helpers.S
@@ -1,5 +1,5 @@
/*
- * Copyright 2024 NXP
+ * Copyright 2024-2025 NXP
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -121,6 +121,9 @@
mov_imm x1, BL33_LIMIT
sub x1, x1, x0
bl zeromem
+ mov_imm x0, IO_BUFFER_BASE
+ mov_imm x1, IO_BUFFER_SIZE
+ bl zeromem
mov x30, x10
ret
endfunc platform_mem_init
diff --git a/plat/nxp/s32/s32g274ardb2/plat_io_storage.c b/plat/nxp/s32/s32g274ardb2/plat_io_storage.c
index db6bcc5..c4efe01 100644
--- a/plat/nxp/s32/s32g274ardb2/plat_io_storage.c
+++ b/plat/nxp/s32/s32g274ardb2/plat_io_storage.c
@@ -1,14 +1,19 @@
/*
- * Copyright 2024 NXP
+ * Copyright 2024-2025 NXP
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
+#include <drivers/io/io_block.h>
#include <drivers/io/io_driver.h>
#include <drivers/io/io_fip.h>
#include <drivers/io/io_memmap.h>
+#include <drivers/mmc.h>
+#include <drivers/partition/partition.h>
+#include <lib/utils.h>
+#include <lib/xlat_tables/xlat_tables_v2.h>
#include <plat/common/platform.h>
#include <tools_share/firmware_image_package.h>
@@ -20,24 +25,23 @@
int (*check)(const uintptr_t spec);
};
-static int open_memmap(const uintptr_t spec);
-static int open_fip(const uintptr_t spec);
-
static uintptr_t fip_dev_handle;
-static uintptr_t memmap_dev_handle;
+static io_block_spec_t fip_mmc_spec;
-static int open_memmap(const uintptr_t spec)
+static uintptr_t mmc_dev_handle;
+
+static int open_mmc(const uintptr_t spec)
{
- uintptr_t temp_handle = 0U;
+ uintptr_t temp_handle;
int result;
- result = io_dev_init(memmap_dev_handle, (uintptr_t)0);
+ result = io_dev_init(mmc_dev_handle, (uintptr_t)0U);
if (result != 0) {
return result;
}
- result = io_open(memmap_dev_handle, spec, &temp_handle);
+ result = io_open(mmc_dev_handle, spec, &temp_handle);
if (result == 0) {
(void)io_close(temp_handle);
}
@@ -66,16 +70,43 @@
void plat_s32g2_io_setup(void)
{
- static const io_dev_connector_t *memmap_dev_con;
+ static const io_block_dev_spec_t mmc_dev_spec = {
+ /* It's used as temp buffer in block driver. */
+ .buffer = {
+ .offset = IO_BUFFER_BASE,
+ .length = IO_BUFFER_SIZE,
+ },
+ .ops = {
+ .read = mmc_read_blocks,
+ .write = mmc_write_blocks,
+ },
+ .block_size = MMC_BLOCK_SIZE,
+ };
static const io_dev_connector_t *fip_dev_con;
+ static const io_dev_connector_t *mmc_dev_con;
+ partition_entry_t fip_part;
+ uintptr_t io_buf_base;
int result __unused;
+ size_t io_buf_size;
+ int ret;
- result = register_io_dev_memmap(&memmap_dev_con);
+ io_buf_base = mmc_dev_spec.buffer.offset;
+ io_buf_size = mmc_dev_spec.buffer.length;
+
+ ret = mmap_add_dynamic_region(io_buf_base, io_buf_base,
+ io_buf_size,
+ MT_MEMORY | MT_RW | MT_SECURE);
+ if (ret != 0) {
+ ERROR("Failed to map the IO buffer\n");
+ panic();
+ }
+
+ result = register_io_dev_block(&mmc_dev_con);
assert(result == 0);
- result = io_dev_open(memmap_dev_con, (uintptr_t)0,
- &memmap_dev_handle);
+ result = io_dev_open(mmc_dev_con, (uintptr_t)&mmc_dev_spec,
+ &mmc_dev_handle);
assert(result == 0);
result = register_io_dev_fip(&fip_dev_con);
@@ -84,14 +115,24 @@
result = io_dev_open(fip_dev_con, (uintptr_t)0,
&fip_dev_handle);
assert(result == 0);
+
+ ret = gpt_partition_init();
+ if (ret != 0) {
+ ERROR("Could not load MBR partition table\n");
+ panic();
+ }
+
+ fip_part = get_partition_entry_list()->list[FIP_PART];
+ fip_mmc_spec.offset = fip_part.start;
+ fip_mmc_spec.length = fip_part.length;
}
int plat_get_image_source(unsigned int image_id, uintptr_t *dev_handle,
uintptr_t *image_spec)
{
- static const io_block_spec_t fip_block_spec = {
- .offset = S32G_FIP_BASE,
- .length = S32G_FIP_SIZE,
+ static const io_block_spec_t mbr_spec = {
+ .offset = 0,
+ .length = PLAT_PARTITION_BLOCK_SIZE,
};
static const io_uuid_spec_t bl31_uuid_spec = {
@@ -102,11 +143,11 @@
.uuid = UUID_NON_TRUSTED_FIRMWARE_BL33,
};
- static const struct plat_io_policy policies[BL33_IMAGE_ID + 1] = {
+ static const struct plat_io_policy policies[GPT_IMAGE_ID + 1] = {
[FIP_IMAGE_ID] = {
- .dev_handle = &memmap_dev_handle,
- .image_spec = (uintptr_t)&fip_block_spec,
- .check = open_memmap,
+ .dev_handle = &mmc_dev_handle,
+ .image_spec = (uintptr_t)&fip_mmc_spec,
+ .check = open_mmc,
},
[BL31_IMAGE_ID] = {
.dev_handle = &fip_dev_handle,
@@ -118,6 +159,11 @@
.image_spec = (uintptr_t)&bl33_uuid_spec,
.check = open_fip,
},
+ [GPT_IMAGE_ID] = {
+ .dev_handle = &mmc_dev_handle,
+ .image_spec = (uintptr_t)&mbr_spec,
+ .check = open_mmc,
+ },
};
const struct plat_io_policy *policy;
int result;
diff --git a/plat/nxp/s32/s32g274ardb2/platform.mk b/plat/nxp/s32/s32g274ardb2/platform.mk
index 4ec7cd0..25e9ebd 100644
--- a/plat/nxp/s32/s32g274ardb2/platform.mk
+++ b/plat/nxp/s32/s32g274ardb2/platform.mk
@@ -1,5 +1,5 @@
#
-# Copyright 2024 NXP
+# Copyright 2024-2025 NXP
#
# SPDX-License-Identifier: BSD-3-Clause
#
@@ -20,7 +20,8 @@
S32_ERRATA_LIST += ERRATA_S32_051700
PLAT_INCLUDES = \
- -I${PLAT_S32G274ARDB2}/include
+ -I${PLAT_S32G274ARDB2}/include \
+ -Idrivers/imx/usdhc \
PROGRAMMABLE_RESET_ADDRESS := 1
@@ -41,12 +42,19 @@
PLAT_XLAT_TABLES_DYNAMIC := 1
$(eval $(call add_define,PLAT_XLAT_TABLES_DYNAMIC))
+NXP_ESDHC_LE := 1
+$(eval $(call add_define,NXP_ESDHC_LE))
+
# Selecting Drivers for SoC
$(eval $(call SET_NXP_MAKE_FLAG,CONSOLE_NEEDED,BL_COMM))
$(eval $(call SET_NXP_MAKE_FLAG,CLK_NEEDED,BL_COMM))
include ${PLAT_DRIVERS_PATH}/drivers.mk
+# Selecting the raw partition where the FIP image is stored
+FIP_PART ?= 0
+$(eval $(call add_define,FIP_PART))
+
BL_COMMON_SOURCES += \
${PLAT_S32G274ARDB2}/plat_console.c \
${PLAT_S32G274ARDB2}/plat_helpers.S \
@@ -60,11 +68,21 @@
${PLAT_S32G274ARDB2}/plat_io_storage.c \
${PLAT_S32G274ARDB2}/s32cc_ncore.c \
common/desc_image_load.c \
+ common/tf_crc32.c \
+ drivers/delay_timer/delay_timer.c \
+ drivers/delay_timer/generic_delay_timer.c \
+ drivers/imx/usdhc/imx_usdhc.c \
+ drivers/io/io_block.c \
drivers/io/io_fip.c \
drivers/io/io_memmap.c \
drivers/io/io_storage.c \
+ drivers/mmc/mmc.c \
+ drivers/partition/gpt.c \
+ drivers/partition/partition.c \
lib/cpus/aarch64/cortex_a53.S \
+BL2_CPPFLAGS += -march=armv8-a+crc
+
BL31_SOURCES += \
${GICV3_SOURCES} \
${PLAT_S32G274ARDB2}/plat_bl31_setup.c \
diff --git a/plat/nxp/s32/s32g274ardb2/s32cc_bl_common.c b/plat/nxp/s32/s32g274ardb2/s32cc_bl_common.c
index 4664438..eb903c5 100644
--- a/plat/nxp/s32/s32g274ardb2/s32cc_bl_common.c
+++ b/plat/nxp/s32/s32g274ardb2/s32cc_bl_common.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2024 NXP
+ * Copyright 2024-2025 NXP
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -7,6 +7,7 @@
#include <common/bl_common.h>
#include <lib/xlat_tables/xlat_tables_v2.h>
+#include <plat/common/platform.h>
#include <s32cc-bl-common.h>
@@ -38,3 +39,8 @@
return 0;
}
+
+unsigned int plat_get_syscnt_freq2(void)
+{
+ return COUNTER_FREQUENCY;
+}
diff --git a/plat/nxp/s32/s32g274ardb2/s32g2_soc.c b/plat/nxp/s32/s32g274ardb2/s32g2_soc.c
index 0001352..c005bad 100644
--- a/plat/nxp/s32/s32g274ardb2/s32g2_soc.c
+++ b/plat/nxp/s32/s32g274ardb2/s32g2_soc.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2024 NXP
+ * Copyright 2024-2025 NXP
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -45,8 +45,3 @@
return (int)core_id;
}
-
-unsigned int plat_get_syscnt_freq2(void)
-{
- return COUNTER_FREQUENCY;
-}
diff --git a/plat/nxp/soc-lx2160a/lx2160aqds/platform.mk b/plat/nxp/soc-lx2160a/lx2160aqds/platform.mk
index 226b22b..12fbac4 100644
--- a/plat/nxp/soc-lx2160a/lx2160aqds/platform.mk
+++ b/plat/nxp/soc-lx2160a/lx2160aqds/platform.mk
@@ -12,8 +12,8 @@
NXP_COINED_BB := no
# DDR Compilation Configs
-NUM_OF_DDRC := 1
-DDRC_NUM_DIMM := 1
+NUM_OF_DDRC := 2
+DDRC_NUM_DIMM := 2
DDRC_NUM_CS := 2
DDR_ECC_EN := yes
#enable address decoding feature
diff --git a/plat/qemu/common/qemu_bl2_setup.c b/plat/qemu/common/qemu_bl2_setup.c
index 71f9cf7..209dcd7 100644
--- a/plat/qemu/common/qemu_bl2_setup.c
+++ b/plat/qemu/common/qemu_bl2_setup.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2015-2025, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -19,7 +19,9 @@
#include <common/fdt_fixup.h>
#include <common/fdt_wrappers.h>
#include <lib/optee_utils.h>
-#include <lib/transfer_list.h>
+#if TRANSFER_LIST
+#include <transfer_list.h>
+#endif
#include <lib/utils.h>
#include <plat/common/platform.h>
@@ -50,7 +52,7 @@
/* Data structure which holds the extents of the trusted SRAM for BL2 */
static meminfo_t bl2_tzram_layout __aligned(CACHE_WRITEBACK_GRANULE);
-static struct transfer_list_header *bl2_tl;
+static struct transfer_list_header __maybe_unused *bl2_tl;
void bl2_early_platform_setup2(u_register_t arg0, u_register_t arg1,
u_register_t arg2, u_register_t arg3)
@@ -344,11 +346,11 @@
INFO("Handoff to BL32\n");
bl_mem_params->ep_info.spsr = qemu_get_spsr_for_bl32_entry();
- if (TRANSFER_LIST &&
- transfer_list_set_handoff_args(bl2_tl,
- &bl_mem_params->ep_info))
+#if TRANSFER_LIST
+ if (transfer_list_set_handoff_args(bl2_tl,
+ &bl_mem_params->ep_info))
break;
-
+#endif
INFO("Using default arguments\n");
#if defined(SPMC_OPTEE)
/*
diff --git a/plat/qemu/common/qemu_bl31_setup.c b/plat/qemu/common/qemu_bl31_setup.c
index 1c5e0ea..a350ce5 100644
--- a/plat/qemu/common/qemu_bl31_setup.c
+++ b/plat/qemu/common/qemu_bl31_setup.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2015-2025, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -9,7 +9,9 @@
#include <common/bl_common.h>
#include <drivers/arm/pl061_gpio.h>
#include <lib/gpt_rme/gpt_rme.h>
-#include <lib/transfer_list.h>
+#if TRANSFER_LIST
+#include <transfer_list.h>
+#endif
#include <plat/common/platform.h>
#if ENABLE_RME
#ifdef PLAT_qemu
@@ -79,7 +81,7 @@
#if ENABLE_RME
static entry_point_info_t rmm_image_ep_info;
#endif
-static struct transfer_list_header *bl31_tl;
+static struct transfer_list_header __maybe_unused *bl31_tl;
/*******************************************************************************
* Perform any BL3-1 early platform setup. Here is an opportunity to copy
@@ -92,8 +94,8 @@
void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1,
u_register_t arg2, u_register_t arg3)
{
- bool is64 = false;
- uint64_t hval;
+ bool __maybe_unused is64 = false;
+ uint64_t __maybe_unused hval;
/* Initialize the console to provide early debug support */
qemu_console_init();
@@ -119,11 +121,11 @@
* They are stored in Secure RAM, in BL2's address space.
*/
while (bl_params) {
-#ifdef __aarch64__
+#if defined(__aarch64__) && TRANSFER_LIST
if (bl_params->image_id == BL31_IMAGE_ID &&
GET_RW(bl_params->ep_info->spsr) == MODE_RW_64)
is64 = true;
-#endif
+#endif /* defined(__aarch64__) && TRANSFER_LIST */
if (bl_params->image_id == BL32_IMAGE_ID)
bl32_image_ep_info = *bl_params->ep_info;
@@ -145,8 +147,8 @@
panic();
#endif
- if (!TRANSFER_LIST ||
- !transfer_list_check_header((void *)arg3))
+#if TRANSFER_LIST
+ if (!transfer_list_check_header((void *)arg3))
return;
if (is64)
@@ -156,6 +158,7 @@
if (arg1 != hval)
return;
+#endif
bl31_tl = (void *)arg3; /* saved TL address from BL2 */
}
diff --git a/plat/xilinx/zynqmp/bl31_zynqmp_setup.c b/plat/xilinx/zynqmp/bl31_zynqmp_setup.c
index b29e1c6..65616e5 100644
--- a/plat/xilinx/zynqmp/bl31_zynqmp_setup.c
+++ b/plat/xilinx/zynqmp/bl31_zynqmp_setup.c
@@ -64,6 +64,26 @@
DISABLE_ALL_EXCEPTIONS);
}
+static inline uint64_t read_cntvct_el0(void)
+{
+ uint64_t val;
+
+ asm volatile("mrs %0, cntvct_el0" : "=r" (val));
+ return val;
+}
+
+static inline void reset_cntvct_el0_to_zero(void)
+{
+ asm volatile(
+ "mrs x0, cntpct_el0\n" /* Read physical counter into x0 */
+ "neg x0, x0\n" /* Negate it: x0 = -x0 */
+ "msr cntvoff_el2, x0\n" /* Write offset to virtual counter */
+ :
+ :
+ : "x0", "memory"
+ );
+}
+
/*
* Perform any BL31 specific platform actions. Here is an opportunity to copy
* parameters passed by the calling EL (S-EL1 in BL2 & EL3 in BL1) before they
@@ -93,6 +113,10 @@
/* Initialize the platform config for future decision making */
zynqmp_config_setup();
+ INFO("Counter TICK 0x%lx\n", read_cntvct_el0());
+ reset_cntvct_el0_to_zero();
+ INFO("Counter TICK after reset 0x%lx\n", read_cntvct_el0());
+
/*
* Do initial security configuration to allow DRAM/device access. On
* Base ZYNQMP only DRAM security is programmable (via TrustZone), but
diff --git a/services/arm_arch_svc/arm_arch_svc_setup.c b/services/arm_arch_svc/arm_arch_svc_setup.c
index 6051de8..329f59b 100644
--- a/services/arm_arch_svc/arm_arch_svc_setup.c
+++ b/services/arm_arch_svc/arm_arch_svc_setup.c
@@ -17,7 +17,7 @@
static int32_t smccc_version(void)
{
- return MAKE_SMCCC_VERSION(SMCCC_MAJOR_VERSION, SMCCC_MINOR_VERSION);
+ return (int32_t)MAKE_SMCCC_VERSION(SMCCC_MAJOR_VERSION, SMCCC_MINOR_VERSION);
}
static int32_t smccc_arch_features(u_register_t arg1)
@@ -294,7 +294,7 @@
arm_arch_svc,
OEN_ARM_START,
OEN_ARM_END,
- SMC_TYPE_FAST,
+ (uint8_t)SMC_TYPE_FAST,
NULL,
arm_arch_svc_smc_handler
);
diff --git a/services/spd/opteed/opteed_main.c b/services/spd/opteed/opteed_main.c
index 8910ec6..adfb298 100644
--- a/services/spd/opteed/opteed_main.c
+++ b/services/spd/opteed/opteed_main.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2013-2025, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -27,7 +27,9 @@
#include <lib/coreboot.h>
#include <lib/el3_runtime/context_mgmt.h>
#include <lib/optee_utils.h>
-#include <lib/transfer_list.h>
+#if TRANSFER_LIST
+#include <transfer_list.h>
+#endif
#include <lib/xlat_tables/xlat_tables_v2.h>
#if OPTEE_ALLOW_SMC_LOAD
#include <libfdt.h>
@@ -40,7 +42,7 @@
#include "teesmc_opteed.h"
#if OPTEE_ALLOW_SMC_LOAD
-static struct transfer_list_header *bl31_tl;
+static struct transfer_list_header __maybe_unused *bl31_tl;
#endif
/*******************************************************************************
@@ -163,9 +165,9 @@
uint64_t arg1;
uint64_t arg2;
uint64_t arg3;
- struct transfer_list_header *tl = NULL;
- struct transfer_list_entry *te = NULL;
- void *dt = NULL;
+ struct transfer_list_header __maybe_unused *tl = NULL;
+ struct transfer_list_entry __maybe_unused *te = NULL;
+ void __maybe_unused *dt = NULL;
linear_id = plat_my_core_pos();
@@ -190,8 +192,10 @@
if (!optee_ep_info->pc)
return 1;
+#if TRANSFER_LIST
tl = (void *)optee_ep_info->args.arg3;
- if (TRANSFER_LIST && transfer_list_check_header(tl)) {
+
+ if (transfer_list_check_header(tl)) {
te = transfer_list_find(tl, TL_TAG_FDT);
dt = transfer_list_entry_data(te);
@@ -199,7 +203,7 @@
if (opteed_rw == OPTEE_AARCH64) {
if (optee_ep_info->args.arg1 !=
TRANSFER_LIST_HANDOFF_X1_VALUE(
- REGISTER_CONVENTION_VERSION))
+ REGISTER_CONVENTION_VERSION))
return 1;
arg0 = (uint64_t)dt;
@@ -207,7 +211,7 @@
} else {
if (optee_ep_info->args.arg1 !=
TRANSFER_LIST_HANDOFF_R1_VALUE(
- REGISTER_CONVENTION_VERSION))
+ REGISTER_CONVENTION_VERSION))
return 1;
arg0 = 0;
@@ -216,7 +220,10 @@
arg1 = optee_ep_info->args.arg1;
arg3 = optee_ep_info->args.arg3;
- } else {
+
+ } else
+#endif /* TRANSFER_LIST */
+ {
/* Default handoff arguments */
opteed_rw = optee_ep_info->args.arg0;
arg0 = optee_ep_info->args.arg1; /* opteed_pageable_part */
@@ -225,9 +232,9 @@
arg3 = 0;
}
- opteed_init_optee_ep_state(optee_ep_info, opteed_rw, optee_ep_info->pc,
- arg0, arg1, arg2, arg3,
- &opteed_sp_context[linear_id]);
+ opteed_init_optee_ep_state(optee_ep_info, opteed_rw,
+ optee_ep_info->pc, arg0, arg1, arg2,
+ arg3, &opteed_sp_context[linear_id]);
/*
* All OPTEED initialization done. Now register our init function with
@@ -430,9 +437,9 @@
return fdt_finish(fdt_buf);
}
+#if TRANSFER_LIST
static int32_t create_smc_tl(const void *fdt, uint32_t fdt_sz)
{
-#if TRANSFER_LIST
bl31_tl = transfer_list_init((void *)(uintptr_t)FW_HANDOFF_BASE,
FW_HANDOFF_SIZE);
if (!bl31_tl) {
@@ -445,10 +452,8 @@
return -1;
}
return 0;
-#else
- return -1;
-#endif
}
+#endif
/*******************************************************************************
* This function is responsible for handling the SMC that loads the OP-TEE
@@ -546,8 +551,8 @@
dt_addr = (uint64_t)fdt_buf;
flush_dcache_range(dt_addr, OPTEED_FDT_SIZE);
- if (TRANSFER_LIST &&
- !create_smc_tl((void *)dt_addr, OPTEED_FDT_SIZE)) {
+#if TRANSFER_LIST
+ if (!create_smc_tl((void *)dt_addr, OPTEED_FDT_SIZE)) {
struct transfer_list_entry *te = NULL;
void *dt = NULL;
@@ -565,7 +570,9 @@
}
arg3 = (uint64_t)bl31_tl;
- } else {
+ } else
+#endif /* TRANSFER_LIST */
+ {
/* Default handoff arguments */
arg2 = dt_addr;
}
diff --git a/services/std_svc/lfa/bl31_lfa.c b/services/std_svc/lfa/bl31_lfa.c
new file mode 100644
index 0000000..6f66826
--- /dev/null
+++ b/services/std_svc/lfa/bl31_lfa.c
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <services/bl31_lfa.h>
+#include <services/lfa_svc.h>
+
+static int32_t lfa_bl31_prime(struct lfa_component_status *activation)
+{
+ return LFA_WRONG_STATE;
+}
+
+static int32_t lfa_bl31_activate(struct lfa_component_status *activation,
+ uint64_t ep_address,
+ uint64_t context_id)
+{
+ return LFA_WRONG_STATE;
+}
+
+static struct lfa_component_ops bl31_activator = {
+ .prime = lfa_bl31_prime,
+ .activate = lfa_bl31_activate,
+ .may_reset_cpu = false,
+ .cpu_rendezvous_required = true,
+};
+
+struct lfa_component_ops *get_bl31_activator(void)
+{
+ return &bl31_activator;
+}
diff --git a/services/std_svc/lfa/lfa.mk b/services/std_svc/lfa/lfa.mk
new file mode 100644
index 0000000..056b537
--- /dev/null
+++ b/services/std_svc/lfa/lfa.mk
@@ -0,0 +1,14 @@
+#
+# Copyright (c) 2025, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+LFA_SOURCES += $(addprefix services/std_svc/lfa/, \
+ lfa_main.c \
+ bl31_lfa.c \
+ lfa_holding_pen.c)
+
+ifeq (${ENABLE_RME}, 1)
+LFA_SOURCES += services/std_svc/rmmd/rmmd_rmm_lfa.c
+endif
diff --git a/services/std_svc/lfa/lfa_holding_pen.c b/services/std_svc/lfa/lfa_holding_pen.c
new file mode 100644
index 0000000..8ee260c
--- /dev/null
+++ b/services/std_svc/lfa/lfa_holding_pen.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <string.h>
+
+#include <common/debug.h>
+#include <lib/psci/psci_lib.h>
+#include <lib/spinlock.h>
+#include <lib/utils_def.h>
+#include <plat/common/platform.h>
+#include <services/lfa_holding_pen.h>
+
+#include <platform_def.h>
+
+static spinlock_t holding_lock;
+static spinlock_t activation_lock;
+static uint32_t activation_count;
+static enum lfa_retc activation_status;
+
+/**
+ * lfa_holding_start - Called by each active CPU to coordinate live activation.
+ *
+ * Note that only CPUs that are active at the time of activation will
+ * participate in CPU rendezvous.
+ *
+ * This function is invoked by each CPU participating in the LFA Activate
+ * process. It increments the shared activation count under `activation_lock`
+ * to track how many CPUs have entered the activation phase.
+ *
+ * The first CPU to enter acquires the `holding_lock`, which ensures
+ * serialization during the wait and activation phases. This lock is
+ * released only after the last CPU completes the activation.
+ *
+ * The function returns `true` only for the last CPU to enter, allowing it
+ * to proceed with performing the live firmware activation. All other CPUs
+ * receive `false` and will wait in `lfa_holding_wait()` until activation
+ * is complete.
+ *
+ * @return `true` for the last CPU, `false` for all others.
+ */
+bool lfa_holding_start(void)
+{
+ bool status;
+ unsigned int no_of_cpus;
+
+ spin_lock(&activation_lock);
+
+ if (activation_count == 0U) {
+ /* First CPU locks holding lock */
+ spin_lock(&holding_lock);
+ }
+
+ activation_count += 1U;
+
+ no_of_cpus = psci_num_cpus_running_on_safe(plat_my_core_pos());
+ status = (activation_count == no_of_cpus);
+ if (!status) {
+ VERBOSE("Hold, %d CPU left\n",
+ PLATFORM_CORE_COUNT - activation_count);
+ }
+
+ spin_unlock(&activation_lock);
+
+ return status;
+}
+
+/**
+ * lfa_holding_wait - CPUs wait until activation is completed by the last CPU.
+ *
+ * All CPUs are serialized using `holding_lock`, which is initially acquired
+ * by the first CPU in `lfa_holding_start()` and only released by the last
+ * CPU through `lfa_holding_release()`. This ensures that no two CPUs enter
+ * the critical section at the same time during the wait phase. Once the
+ * last CPU completes activation, each CPU decrements the activation count
+ * and returns the final activation status, which was set by the last CPU
+ * to complete the activation process.
+ *
+ * @return Activation status set by the last CPU.
+ */
+enum lfa_retc lfa_holding_wait(void)
+{
+ spin_lock(&holding_lock);
+ activation_count -= 1U;
+ spin_unlock(&holding_lock);
+ return activation_status;
+}
+
+/**
+ * lfa_holding_release - Called by the last CPU to complete activation.
+ *
+ * This function is used by the last participating CPU after it completes
+ * live firmware activation. It updates the shared activation status and
+ * resets the activation count. Finally, it releases the `holding_lock` to
+ * allow other CPUs that were waiting in `lfa_holding_wait()` to proceed.
+ *
+ * @param status Activation status to be shared with other CPUs.
+ */
+void lfa_holding_release(enum lfa_retc status)
+{
+ activation_count = 0U;
+ activation_status = status;
+ spin_unlock(&holding_lock);
+}
diff --git a/services/std_svc/lfa/lfa_main.c b/services/std_svc/lfa/lfa_main.c
new file mode 100644
index 0000000..1cf65ae
--- /dev/null
+++ b/services/std_svc/lfa/lfa_main.c
@@ -0,0 +1,337 @@
+/*
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <errno.h>
+
+#include <plat/common/platform.h>
+#include <services/bl31_lfa.h>
+#include <services/lfa_svc.h>
+#include <services/rmmd_rmm_lfa.h>
+#include <smccc_helpers.h>
+
+static uint32_t lfa_component_count;
+static plat_lfa_component_info_t *lfa_components;
+static struct lfa_component_status current_activation;
+static bool is_lfa_initialized;
+
+void lfa_reset_activation(void)
+{
+ current_activation.component_id = LFA_INVALID_COMPONENT;
+ current_activation.prime_status = PRIME_NONE;
+ current_activation.cpu_rendezvous_required = false;
+}
+
+static int convert_to_lfa_error(int ret)
+{
+ switch (ret) {
+ case 0:
+ return LFA_SUCCESS;
+ case -EAUTH:
+ return LFA_AUTH_ERROR;
+ case -ENOMEM:
+ return LFA_NO_MEMORY;
+ default:
+ return LFA_DEVICE_ERROR;
+ }
+}
+
+static bool lfa_initialize_components(void)
+{
+ lfa_component_count = plat_lfa_get_components(&lfa_components);
+
+ if (lfa_component_count == 0U || lfa_components == NULL) {
+ /* unlikely to reach here */
+ ERROR("Invalid LFA component setup: count = 0 or components are NULL");
+ return false;
+ }
+
+ return true;
+}
+
+static uint64_t get_fw_activation_flags(uint32_t fw_seq_id)
+{
+ const plat_lfa_component_info_t *comp =
+ &lfa_components[fw_seq_id];
+ uint64_t flags = 0ULL;
+
+ flags |= ((comp->activator == NULL ? 0ULL : 1ULL)
+ << LFA_ACTIVATION_CAPABLE_SHIFT);
+ flags |= (uint64_t)(comp->activation_pending)
+ << LFA_ACTIVATION_PENDING_SHIFT;
+
+ if (comp->activator != NULL) {
+ flags |= ((comp->activator->may_reset_cpu ? 1ULL : 0ULL)
+ << LFA_MAY_RESET_CPU_SHIFT);
+ flags |= ((comp->activator->cpu_rendezvous_required ? 0ULL : 1ULL)
+ << LFA_CPU_RENDEZVOUS_OPTIONAL_SHIFT);
+ }
+
+ return flags;
+}
+
+static int lfa_cancel(uint32_t component_id)
+{
+ int ret = LFA_SUCCESS;
+
+ if (lfa_component_count == 0U) {
+ return LFA_WRONG_STATE;
+ }
+
+ /* Check if component ID is in range. */
+ if ((component_id >= lfa_component_count) ||
+ (component_id != current_activation.component_id)) {
+ return LFA_INVALID_PARAMETERS;
+ }
+
+ ret = plat_lfa_cancel(component_id);
+ if (ret != LFA_SUCCESS) {
+ return LFA_BUSY;
+ }
+
+ /* TODO: add proper termination prime and activate phases */
+ lfa_reset_activation();
+
+ return ret;
+}
+
+static int lfa_activate(uint32_t component_id, uint64_t flags,
+ uint64_t ep_address, uint64_t context_id)
+{
+ int ret = LFA_ACTIVATION_FAILED;
+ struct lfa_component_ops *activator;
+
+ if ((lfa_component_count == 0U) ||
+ (!lfa_components[component_id].activation_pending) ||
+ (current_activation.prime_status != PRIME_COMPLETE)) {
+ return LFA_COMPONENT_WRONG_STATE;
+ }
+
+ /* Check if fw_seq_id is in range. */
+ if ((component_id >= lfa_component_count) ||
+ (current_activation.component_id != component_id)) {
+ return LFA_INVALID_PARAMETERS;
+ }
+
+ if (lfa_components[component_id].activator == NULL) {
+ return LFA_NOT_SUPPORTED;
+ }
+
+ activator = lfa_components[component_id].activator;
+ if (activator->activate != NULL) {
+ /*
+ * Pass skip_cpu_rendezvous (flag[0]) only if flag[0]==1
+ * & CPU_RENDEZVOUS is not required.
+ */
+ if (flags & LFA_SKIP_CPU_RENDEZVOUS_BIT) {
+ if (!activator->cpu_rendezvous_required) {
+ INFO("Skipping rendezvous requested by caller.\n");
+ current_activation.cpu_rendezvous_required = false;
+ }
+ /*
+ * Return error if caller tries to skip rendezvous when
+ * it is required.
+ */
+ else {
+ ERROR("CPU Rendezvous is required, can't skip.\n");
+ return LFA_INVALID_PARAMETERS;
+ }
+ }
+
+ ret = activator->activate(¤t_activation, ep_address,
+ context_id);
+ }
+
+ lfa_components[component_id].activation_pending = false;
+
+ return ret;
+}
+
+static int lfa_prime(uint32_t component_id, uint64_t *flags)
+{
+ int ret = LFA_SUCCESS;
+ struct lfa_component_ops *activator;
+
+ if (lfa_component_count == 0U ||
+ !lfa_components[component_id].activation_pending) {
+ return LFA_WRONG_STATE;
+ }
+
+ /* Check if fw_seq_id is in range. */
+ if (component_id >= lfa_component_count) {
+ return LFA_INVALID_PARAMETERS;
+ }
+
+ if (lfa_components[component_id].activator == NULL) {
+ return LFA_NOT_SUPPORTED;
+ }
+
+ switch (current_activation.prime_status) {
+ case PRIME_NONE:
+ current_activation.component_id = component_id;
+ current_activation.prime_status = PRIME_STARTED;
+ break;
+
+ case PRIME_STARTED:
+ if (current_activation.component_id != component_id) {
+ /* Mismatched component trying to continue PRIME - error */
+ return LFA_WRONG_STATE;
+ }
+ break;
+
+ case PRIME_COMPLETE:
+ default:
+ break;
+ }
+
+ ret = plat_lfa_load_auth_image(component_id);
+ ret = convert_to_lfa_error(ret);
+
+ activator = lfa_components[component_id].activator;
+ if (activator->prime != NULL) {
+ ret = activator->prime(¤t_activation);
+ if (ret != LFA_SUCCESS) {
+ /*
+ * TODO: it should be LFA_PRIME_FAILED but specification
+ * has not define this error yet
+ */
+ return ret;
+ }
+ }
+
+ current_activation.prime_status = PRIME_COMPLETE;
+
+ /* TODO: split this into multiple PRIME calls */
+ *flags = 0ULL;
+
+ return ret;
+}
+
+int lfa_setup(void)
+{
+ is_lfa_initialized = lfa_initialize_components();
+ if (!is_lfa_initialized) {
+ return -1;
+ }
+
+ lfa_reset_activation();
+
+ return 0;
+}
+
+uint64_t lfa_smc_handler(uint32_t 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)
+{
+ uint64_t retx1, retx2;
+ uint64_t lfa_flags;
+ uint8_t *uuid_p;
+ uint32_t fw_seq_id = (uint32_t)x1;
+ int ret;
+
+ /**
+ * TODO: Acquire serialization lock.
+ */
+
+ if (!is_lfa_initialized) {
+ return LFA_NOT_SUPPORTED;
+ }
+
+ switch (smc_fid) {
+ case LFA_VERSION:
+ SMC_RET1(handle, LFA_VERSION_VAL);
+ break;
+
+ case LFA_FEATURES:
+ SMC_RET1(handle, is_lfa_fid(x1) ? LFA_SUCCESS : LFA_NOT_SUPPORTED);
+ break;
+
+ case LFA_GET_INFO:
+ /**
+ * The current specification limits this input parameter to be zero for
+ * version 1.0 of LFA
+ */
+ if (x1 == 0ULL) {
+ SMC_RET3(handle, LFA_SUCCESS, lfa_component_count, 0);
+ } else {
+ SMC_RET1(handle, LFA_INVALID_PARAMETERS);
+ }
+ break;
+
+ case LFA_GET_INVENTORY:
+ if (lfa_component_count == 0U) {
+ SMC_RET1(handle, LFA_WRONG_STATE);
+ }
+
+ /*
+ * Check if fw_seq_id is in range. LFA_GET_INFO must be called first to scan
+ * platform firmware and create a valid number of firmware components.
+ */
+ if (fw_seq_id >= lfa_component_count) {
+ SMC_RET1(handle, LFA_INVALID_PARAMETERS);
+ }
+
+ /*
+ * grab the UUID of asked fw_seq_id and set the return UUID
+ * variables
+ */
+ uuid_p = (uint8_t *)&lfa_components[fw_seq_id].uuid;
+ memcpy(&retx1, uuid_p, sizeof(uint64_t));
+ memcpy(&retx2, uuid_p + sizeof(uint64_t), sizeof(uint64_t));
+
+ /*
+ * check the given fw_seq_id's update available
+ * and accordingly set the active_pending flag
+ */
+ lfa_components[fw_seq_id].activation_pending =
+ is_plat_lfa_activation_pending(fw_seq_id);
+
+ INFO("Component %lu %s live activation:\n", x1,
+ lfa_components[fw_seq_id].activator ? "supports" :
+ "does not support");
+
+ if (lfa_components[fw_seq_id].activator != NULL) {
+ INFO("Activation pending: %s\n",
+ lfa_components[fw_seq_id].activation_pending ? "true" : "false");
+ }
+
+ INFO("x1 = 0x%016lx, x2 = 0x%016lx\n", retx1, retx2);
+
+ SMC_RET4(handle, LFA_SUCCESS, retx1, retx2, get_fw_activation_flags(fw_seq_id));
+
+ break;
+
+ case LFA_PRIME:
+ ret = lfa_prime(x1, &lfa_flags);
+ if (ret != LFA_SUCCESS) {
+ SMC_RET1(handle, ret);
+ } else {
+ SMC_RET2(handle, ret, lfa_flags);
+ }
+ break;
+
+ case LFA_ACTIVATE:
+ ret = lfa_activate(fw_seq_id, x2, x3, x4);
+ /* TODO: implement activate again */
+ SMC_RET2(handle, ret, 0ULL);
+
+ break;
+
+ case LFA_CANCEL:
+ ret = lfa_cancel(x1);
+ SMC_RET1(handle, ret);
+ break;
+
+ default:
+ WARN("Unimplemented LFA Service Call: 0x%x\n", smc_fid);
+ SMC_RET1(handle, SMC_UNK);
+ break; /* unreachable */
+
+ }
+
+ SMC_RET1(handle, SMC_UNK);
+
+ return 0;
+}
diff --git a/services/std_svc/rmmd/rmmd_rmm_lfa.c b/services/std_svc/rmmd/rmmd_rmm_lfa.c
new file mode 100644
index 0000000..966266b
--- /dev/null
+++ b/services/std_svc/rmmd/rmmd_rmm_lfa.c
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2025, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <services/lfa_svc.h>
+#include <services/rmmd_rmm_lfa.h>
+
+static int32_t lfa_rmm_prime(struct lfa_component_status *activation)
+{
+ return LFA_WRONG_STATE;
+}
+
+static int32_t lfa_rmm_activate(struct lfa_component_status *activation,
+ uint64_t ep_address, uint64_t context_id)
+{
+ return LFA_WRONG_STATE;
+}
+
+static struct lfa_component_ops rmm_activator = {
+ .prime = lfa_rmm_prime,
+ .activate = lfa_rmm_activate,
+ .may_reset_cpu = false,
+ .cpu_rendezvous_required = true,
+};
+
+struct lfa_component_ops *get_rmm_activator(void)
+{
+ return &rmm_activator;
+}
diff --git a/services/std_svc/spm/spm_mm/spm_mm_setup.c b/services/std_svc/spm/spm_mm/spm_mm_setup.c
index 66ce84c..ebc5387 100644
--- a/services/std_svc/spm/spm_mm/spm_mm_setup.c
+++ b/services/std_svc/spm/spm_mm/spm_mm_setup.c
@@ -20,7 +20,7 @@
#include <lib/hob/mpinfo.h>
#endif
#if TRANSFER_LIST
-#include <lib/transfer_list.h>
+#include <transfer_list.h>
#endif
#include <lib/xlat_tables/xlat_tables_v2.h>
#include <platform_def.h>
diff --git a/services/std_svc/std_svc_setup.c b/services/std_svc/std_svc_setup.c
index deca1c0..11c6031 100644
--- a/services/std_svc/std_svc_setup.c
+++ b/services/std_svc/std_svc_setup.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2023, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2014-2025, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -15,6 +15,7 @@
#include <lib/runtime_instr.h>
#include <services/drtm_svc.h>
#include <services/errata_abi_svc.h>
+#include <services/lfa_svc.h>
#include <services/pci_svc.h>
#include <services/rmmd_svc.h>
#include <services/sdei.h>
@@ -86,6 +87,15 @@
}
#endif /* DRTM_SUPPORT */
+#if LFA_SUPPORT
+ /*
+ * Setup/Initialize resources useful during LFA
+ */
+ if (lfa_setup() != 0) {
+ ret = 1;
+ }
+#endif /* LFA_SUPPORT */
+
return ret;
}
@@ -217,6 +227,13 @@
}
#endif /* DRTM_SUPPORT */
+#if LFA_SUPPORT
+ if (is_lfa_fid(smc_fid)) {
+ return lfa_smc_handler(smc_fid, x1, x2, x3, x4, cookie, handle, flags);
+ }
+#endif /* LFA_SUPPORT */
+
+
switch (smc_fid) {
case ARM_STD_SVC_CALL_COUNT:
/*
diff --git a/tools/memory/poetry.lock b/tools/memory/poetry.lock
index 2747479..67641ee 100644
--- a/tools/memory/poetry.lock
+++ b/tools/memory/poetry.lock
@@ -40,6 +40,103 @@
]
[[package]]
+name = "jinja2"
+version = "3.1.6"
+description = "A very fast and expressive template engine."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"},
+ {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"},
+]
+
+[package.dependencies]
+MarkupSafe = ">=2.0"
+
+[package.extras]
+i18n = ["Babel (>=2.7)"]
+
+[[package]]
+name = "markupsafe"
+version = "2.1.5"
+description = "Safely add untrusted strings to HTML/XML markup."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"},
+ {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"},
+ {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"},
+ {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"},
+ {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"},
+ {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"},
+ {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"},
+ {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"},
+]
+
+[[package]]
+name = "nodeenv"
+version = "1.9.1"
+description = "Node.js virtual environment builder"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
+files = [
+ {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"},
+ {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"},
+]
+
+[[package]]
name = "prettytable"
version = "3.11.0"
description = "A simple Python library for easily displaying tabular data in a visually appealing ASCII table format"
@@ -68,6 +165,53 @@
]
[[package]]
+name = "pyright"
+version = "1.1.399"
+description = "Command line wrapper for pyright"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "pyright-1.1.399-py3-none-any.whl", hash = "sha256:55f9a875ddf23c9698f24208c764465ffdfd38be6265f7faf9a176e1dc549f3b"},
+ {file = "pyright-1.1.399.tar.gz", hash = "sha256:439035d707a36c3d1b443aec980bc37053fbda88158eded24b8eedcf1c7b7a1b"},
+]
+
+[package.dependencies]
+nodeenv = ">=1.6.0"
+typing-extensions = ">=4.1"
+
+[package.extras]
+all = ["nodejs-wheel-binaries", "twine (>=3.4.1)"]
+dev = ["twine (>=3.4.1)"]
+nodejs = ["nodejs-wheel-binaries"]
+
+[[package]]
+name = "ruff"
+version = "0.11.2"
+description = "An extremely fast Python linter and code formatter, written in Rust."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "ruff-0.11.2-py3-none-linux_armv6l.whl", hash = "sha256:c69e20ea49e973f3afec2c06376eb56045709f0212615c1adb0eda35e8a4e477"},
+ {file = "ruff-0.11.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:2c5424cc1c4eb1d8ecabe6d4f1b70470b4f24a0c0171356290b1953ad8f0e272"},
+ {file = "ruff-0.11.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:ecf20854cc73f42171eedb66f006a43d0a21bfb98a2523a809931cda569552d9"},
+ {file = "ruff-0.11.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c543bf65d5d27240321604cee0633a70c6c25c9a2f2492efa9f6d4b8e4199bb"},
+ {file = "ruff-0.11.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:20967168cc21195db5830b9224be0e964cc9c8ecf3b5a9e3ce19876e8d3a96e3"},
+ {file = "ruff-0.11.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:955a9ce63483999d9f0b8f0b4a3ad669e53484232853054cc8b9d51ab4c5de74"},
+ {file = "ruff-0.11.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:86b3a27c38b8fce73bcd262b0de32e9a6801b76d52cdb3ae4c914515f0cef608"},
+ {file = "ruff-0.11.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a3b66a03b248c9fcd9d64d445bafdf1589326bee6fc5c8e92d7562e58883e30f"},
+ {file = "ruff-0.11.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0397c2672db015be5aa3d4dac54c69aa012429097ff219392c018e21f5085147"},
+ {file = "ruff-0.11.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:869bcf3f9abf6457fbe39b5a37333aa4eecc52a3b99c98827ccc371a8e5b6f1b"},
+ {file = "ruff-0.11.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2a2b50ca35457ba785cd8c93ebbe529467594087b527a08d487cf0ee7b3087e9"},
+ {file = "ruff-0.11.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7c69c74bf53ddcfbc22e6eb2f31211df7f65054bfc1f72288fc71e5f82db3eab"},
+ {file = "ruff-0.11.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6e8fb75e14560f7cf53b15bbc55baf5ecbe373dd5f3aab96ff7aa7777edd7630"},
+ {file = "ruff-0.11.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:842a472d7b4d6f5924e9297aa38149e5dcb1e628773b70e6387ae2c97a63c58f"},
+ {file = "ruff-0.11.2-py3-none-win32.whl", hash = "sha256:aca01ccd0eb5eb7156b324cfaa088586f06a86d9e5314b0eb330cb48415097cc"},
+ {file = "ruff-0.11.2-py3-none-win_amd64.whl", hash = "sha256:3170150172a8f994136c0c66f494edf199a0bbea7a409f649e4bc8f4d7084080"},
+ {file = "ruff-0.11.2-py3-none-win_arm64.whl", hash = "sha256:52933095158ff328f4c77af3d74f0379e34fd52f175144cefc1b192e7ccd32b4"},
+ {file = "ruff-0.11.2.tar.gz", hash = "sha256:ec47591497d5a1050175bdf4e1a4e6272cddff7da88a2ad595e1e326041d8d94"},
+]
+
+[[package]]
name = "six"
version = "1.17.0"
description = "Python 2 and 3 compatibility utilities"
@@ -79,6 +223,17 @@
]
[[package]]
+name = "typing-extensions"
+version = "4.13.2"
+description = "Backported and Experimental Type Hints for Python 3.8+"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"},
+ {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"},
+]
+
+[[package]]
name = "wcwidth"
version = "0.2.13"
description = "Measures the displayed width of unicode strings in a terminal"
@@ -92,4 +247,4 @@
[metadata]
lock-version = "2.0"
python-versions = "^3.8.0"
-content-hash = "d7c185b3dbfc9bba145f12146e18ce501caf081d7762f138bc5a7fde99f40543"
+content-hash = "72f05cdcfe5278c3fb4408ba76cc502c83a56615681d8307bf67fe759a9da442"
diff --git a/tools/memory/pyproject.toml b/tools/memory/pyproject.toml
index c2fdfcb..70d3de7 100644
--- a/tools/memory/pyproject.toml
+++ b/tools/memory/pyproject.toml
@@ -12,10 +12,18 @@
prettytable = "^3.5.0"
pyelftools = "^0.29.0"
python = "^3.8.0"
+jinja2 = "^3.1.6"
[tool.poetry.scripts]
memory = "memory.memmap:main"
+[tool.poetry.group.dev]
+optional = true
+
+[tool.poetry.group.dev.dependencies]
+ruff = "^0.11.2"
+pyright = "^1.1.399"
+
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
diff --git a/tools/memory/src/memory/buildparser.py b/tools/memory/src/memory/buildparser.py
deleted file mode 100755
index ea417e1..0000000
--- a/tools/memory/src/memory/buildparser.py
+++ /dev/null
@@ -1,88 +0,0 @@
-#
-# Copyright (c) 2023-2025, Arm Limited. All rights reserved.
-#
-# SPDX-License-Identifier: BSD-3-Clause
-#
-
-import re
-from pathlib import Path
-
-from memory.elfparser import TfaElfParser
-from memory.mapparser import TfaMapParser
-
-
-class TfaBuildParser:
- """A class for performing analysis on the memory layout of a TF-A build."""
-
- def __init__(self, path: Path, map_backend=False):
- self._modules = dict()
- self._path = path
- self.map_backend = map_backend
- self._parse_modules()
-
- def __getitem__(self, module: str):
- """Returns an TfaElfParser instance indexed by module."""
- return self._modules[module]
-
- def _parse_modules(self):
- """Parse the build files using the selected backend."""
- backend = TfaElfParser
- files = list(self._path.glob("**/*.elf"))
- io_perms = "rb"
-
- if self.map_backend or len(files) == 0:
- backend = TfaMapParser
- files = self._path.glob("**/*.map")
- io_perms = "r"
-
- for file in files:
- module_name = file.name.split("/")[-1].split(".")[0]
- with open(file, io_perms) as f:
- self._modules[module_name] = backend(f)
-
- if not len(self._modules):
- raise FileNotFoundError(
- f"failed to find files to analyse in path {self._path}!"
- )
-
- @property
- def symbols(self) -> list:
- return [
- (*sym, k) for k, v in self._modules.items() for sym in v.symbols
- ]
-
- @staticmethod
- def filter_symbols(symbols: list, regex: str = None) -> list:
- """Returns a map of symbols to modules."""
- regex = r".*" if not regex else regex
- return sorted(
- filter(lambda s: re.match(regex, s[0]), symbols),
- key=lambda s: (-s[1], s[0]),
- reverse=True,
- )
-
- def get_mem_usage_dict(self) -> dict:
- """Returns map of memory usage per memory type for each module."""
- mem_map = {}
- for k, v in self._modules.items():
- mod_mem_map = v.get_memory_layout()
- if len(mod_mem_map):
- mem_map[k] = mod_mem_map
- return mem_map
-
- def get_mem_tree_as_dict(self) -> dict:
- """Returns _tree of modules, segments and segments and their total
- memory usage."""
- return {
- k: {
- "name": k,
- **v.get_mod_mem_usage_dict(),
- **{"children": v.get_seg_map_as_dict()},
- }
- for k, v in self._modules.items()
- }
-
- @property
- def module_names(self):
- """Returns sorted list of module names."""
- return sorted(self._modules.keys())
diff --git a/tools/memory/src/memory/elfparser.py b/tools/memory/src/memory/elfparser.py
index e6581c9..019d6da 100644
--- a/tools/memory/src/memory/elfparser.py
+++ b/tools/memory/src/memory/elfparser.py
@@ -6,9 +6,22 @@
import re
from dataclasses import asdict, dataclass
-from typing import BinaryIO
+from typing import (
+ Any,
+ BinaryIO,
+ Dict,
+ Iterable,
+ List,
+ Optional,
+ Tuple,
+ Union,
+)
from elftools.elf.elffile import ELFFile
+from elftools.elf.sections import Section, SymbolTableSection
+from elftools.elf.segments import Segment
+
+from memory.image import Image, Region
@dataclass(frozen=True)
@@ -17,10 +30,10 @@
start: int
end: int
size: int
- children: list
+ children: List["TfaMemObject"]
-class TfaElfParser:
+class TfaElfParser(Image):
"""A class representing an ELF file built for TF-A.
Provides a basic interface for reading the symbol table and other
@@ -28,53 +41,70 @@
the contents an ELF file.
"""
- def __init__(self, elf_file: BinaryIO):
- self._segments = {}
- self._memory_layout = {}
+ def __init__(self, elf_file: BinaryIO) -> None:
+ self._segments: Dict[int, TfaMemObject] = {}
+ self._memory_layout: Dict[str, Dict[str, int]] = {}
elf = ELFFile(elf_file)
- self._symbols = {
- sym.name: sym.entry["st_value"]
- for sym in elf.get_section_by_name(".symtab").iter_symbols()
+ symtab = elf.get_section_by_name(".symtab")
+ assert isinstance(symtab, SymbolTableSection)
+
+ self._symbols: Dict[str, int] = {
+ sym.name: sym.entry["st_value"] for sym in symtab.iter_symbols()
}
self.set_segment_section_map(elf.iter_segments(), elf.iter_sections())
self._memory_layout = self.get_memory_layout_from_symbols()
- self._start = elf["e_entry"]
+ self._start: int = elf["e_entry"]
+ self._size: int
+ self._free: int
self._size, self._free = self._get_mem_usage()
- self._end = self._start + self._size
+ self._end: int = self._start + self._size
+
+ self._footprint: Dict[str, Region] = {}
+
+ for mem, attrs in self._memory_layout.items():
+ self._footprint[mem] = Region(
+ attrs["start"],
+ attrs["end"],
+ attrs["length"],
+ )
@property
- def symbols(self):
- return self._symbols.items()
+ def symbols(self) -> Dict[str, int]:
+ return self._symbols
@staticmethod
- def tfa_mem_obj_factory(elf_obj, name=None, children=None, segment=False):
+ def tfa_mem_obj_factory(
+ elf_obj: Union[Segment, Section],
+ name: Optional[str] = None,
+ children: Optional[List[TfaMemObject]] = None,
+ ) -> TfaMemObject:
"""Converts a pyelfparser Segment or Section to a TfaMemObject."""
# Ensure each segment is provided a name since they aren't in the
# program header.
- assert not (
- segment and name is None
- ), "Attempting to make segment without a name"
-
- if children is None:
- children = list()
+ assert not (isinstance(elf_obj, Segment) and name is None), (
+ "Attempting to make segment without a name"
+ )
# Segment and sections header keys have different prefixes.
- vaddr = "p_vaddr" if segment else "sh_addr"
- size = "p_memsz" if segment else "sh_size"
+ vaddr = "p_vaddr" if isinstance(elf_obj, Segment) else "sh_addr"
+ size = "p_memsz" if isinstance(elf_obj, Segment) else "sh_size"
+
+ name = name if isinstance(elf_obj, Segment) else elf_obj.name
+ assert name is not None
# TODO figure out how to handle free space for sections and segments
return TfaMemObject(
- name if segment else elf_obj.name,
+ name,
elf_obj[vaddr],
elf_obj[vaddr] + elf_obj[size],
elf_obj[size],
- [] if not children else children,
+ children or [],
)
- def _get_mem_usage(self) -> (int, int):
+ def _get_mem_usage(self) -> Tuple[int, int]:
"""Get total size and free space for this component."""
size = free = 0
@@ -89,36 +119,37 @@
return size, free
- def set_segment_section_map(self, segments, sections):
+ def set_segment_section_map(
+ self,
+ segments: Iterable[Segment],
+ sections: Iterable[Section],
+ ) -> None:
"""Set segment to section mappings."""
- segments = list(
- filter(lambda seg: seg["p_type"] == "PT_LOAD", segments)
- )
+ segments = filter(lambda seg: seg["p_type"] == "PT_LOAD", segments)
+ segments_list = list(segments)
for sec in sections:
- for n, seg in enumerate(segments):
+ for n, seg in enumerate(segments_list):
if seg.section_in_segment(sec):
- if n not in self._segments.keys():
+ if n not in self._segments:
self._segments[n] = self.tfa_mem_obj_factory(
- seg, name=f"{n:#02}", segment=True
+ seg, name=f"{n:#02}"
)
- self._segments[n].children.append(
- self.tfa_mem_obj_factory(sec)
- )
+ self._segments[n].children.append(self.tfa_mem_obj_factory(sec))
- def get_memory_layout_from_symbols(self, expr=None) -> dict:
+ def get_memory_layout_from_symbols(self) -> Dict[str, Dict[str, int]]:
"""Retrieve information about the memory configuration from the symbol
table.
"""
- assert len(self._symbols), "Symbol table is empty!"
+ assert self._symbols, "Symbol table is empty!"
- expr = r".*(.?R.M)_REGION.*(START|END|LENGTH)" if not expr else expr
+ expr = r".*(.?R.M)_REGION.*(START|END|LENGTH)"
region_symbols = filter(lambda s: re.match(expr, s), self._symbols)
- memory_layout = {}
+ memory_layout: Dict[str, Dict[str, int]] = {}
for symbol in region_symbols:
- region, _, attr = tuple(symbol.lower().strip("__").split("_"))
+ region, _, attr = symbol.lower().strip("__").split("_")
if region not in memory_layout:
memory_layout[region] = {}
@@ -127,29 +158,30 @@
return memory_layout
- def get_seg_map_as_dict(self):
+ def get_seg_map_as_dict(self) -> List[Dict[str, Any]]:
"""Get a dictionary of segments and their section mappings."""
- return [asdict(v) for k, v in self._segments.items()]
+ return [asdict(segment) for segment in self._segments.values()]
- def get_memory_layout(self):
+ def get_memory_layout(self) -> Dict[str, Region]:
"""Get the total memory consumed by this module from the memory
configuration.
- {"rom": {"start": 0x0, "end": 0xFF, "length": ... }
"""
- mem_dict = {}
+ mem_dict: Dict[str, Region] = {}
for mem, attrs in self._memory_layout.items():
- limit = attrs["start"] + attrs["length"]
- mem_dict[mem] = {
- "start": attrs["start"],
- "limit": limit,
- "size": attrs["end"] - attrs["start"],
- "free": limit - attrs["end"],
- "total": attrs["length"],
- }
+ mem_dict[mem] = Region(
+ attrs["start"],
+ attrs["end"],
+ attrs["length"],
+ )
+
return mem_dict
- def get_mod_mem_usage_dict(self):
+ @property
+ def footprint(self) -> Dict[str, Region]:
+ return self._footprint
+
+ def get_mod_mem_usage_dict(self) -> Dict[str, int]:
"""Get the total memory consumed by the module, this combines the
information in the memory configuration.
"""
diff --git a/tools/memory/src/memory/image.py b/tools/memory/src/memory/image.py
new file mode 100644
index 0000000..cda1d8a
--- /dev/null
+++ b/tools/memory/src/memory/image.py
@@ -0,0 +1,77 @@
+#
+# Copyright (c) 2023-2025, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+from abc import ABC, abstractmethod
+from dataclasses import dataclass
+from typing import Dict, Optional
+
+
+@dataclass
+class Region:
+ """Represents a memory region."""
+
+ start: Optional[int] = None
+ """Memory address of the beginning of the region."""
+
+ end: Optional[int] = None
+ """Memory address of the end of the region."""
+
+ length: Optional[int] = None
+ """Current size of the region in bytes."""
+
+ @property
+ def limit(self) -> Optional[int]:
+ """Largest possible end memory address of the region."""
+
+ if self.start is None:
+ return None
+
+ if self.length is None:
+ return None
+
+ return self.start + self.length
+
+ @property
+ def size(self) -> Optional[int]:
+ """Maximum possible size of the region in bytes."""
+
+ if self.end is None:
+ return None
+
+ if self.start is None:
+ return None
+
+ return self.end - self.start
+
+ @property
+ def free(self) -> Optional[int]:
+ """Number of bytes that the region is permitted to further expand."""
+
+ if self.limit is None:
+ return None
+
+ if self.end is None:
+ return None
+
+ return self.limit - self.end
+
+
+class Image(ABC):
+ """An image under analysis."""
+
+ @property
+ @abstractmethod
+ def footprint(self) -> Dict[str, Region]:
+ """Get metrics about the memory regions that this image occupies."""
+
+ pass
+
+ @property
+ @abstractmethod
+ def symbols(self) -> Dict[str, int]:
+ """Get a dictionary of the image's symbols and their corresponding addresses."""
+
+ pass
diff --git a/tools/memory/src/memory/mapparser.py b/tools/memory/src/memory/mapparser.py
index 1c28e71..24ee264 100644
--- a/tools/memory/src/memory/mapparser.py
+++ b/tools/memory/src/memory/mapparser.py
@@ -4,11 +4,14 @@
# SPDX-License-Identifier: BSD-3-Clause
#
+from collections import defaultdict
from re import match, search
-from typing import TextIO
+from typing import Dict, TextIO
+
+from memory.image import Image, Region
-class TfaMapParser:
+class TfaMapParser(Image):
"""A class representing a map file built for TF-A.
Provides a basic interface for reading the symbol table. The constructor
@@ -16,17 +19,31 @@
are supported at this stage.
"""
- def __init__(self, map_file: TextIO):
- self._symbols = self.read_symbols(map_file)
+ def __init__(self, map_file: TextIO) -> None:
+ self._symbols: Dict[str, int] = self.read_symbols(map_file)
+ assert self._symbols, "Symbol table is empty!"
+
+ self._footprint: Dict[str, Region] = defaultdict(Region)
+
+ expr = r".*(.?R.M)_REGION.*(START|END|LENGTH)"
+ for symbol in filter(lambda s: match(expr, s), self._symbols):
+ region, _, attr = symbol.lower().strip("__").split("_")
+
+ if attr == "start":
+ self._footprint[region].start = self._symbols[symbol]
+ elif attr == "end":
+ self._footprint[region].end = self._symbols[symbol]
+ if attr == "length":
+ self._footprint[region].length = self._symbols[symbol]
@property
- def symbols(self):
- return self._symbols.items()
+ def symbols(self) -> Dict[str, int]:
+ return self._symbols
@staticmethod
- def read_symbols(file: TextIO, pattern: str = None) -> dict:
- pattern = r"\b(0x\w*)\s*(\w*)\s=" if not pattern else pattern
- symbols = {}
+ def read_symbols(file: TextIO) -> Dict[str, int]:
+ pattern = r"\b(0x\w*)\s*(\w*)\s="
+ symbols: Dict[str, int] = {}
for line in file.readlines():
match = search(pattern, line)
@@ -37,39 +54,6 @@
return symbols
- def get_memory_layout(self) -> dict:
- """Get the total memory consumed by this module from the memory
- configuration.
- {"rom": {"start": 0x0, "end": 0xFF, "length": ... }
- """
- assert len(self._symbols), "Symbol table is empty!"
- expr = r".*(.?R.M)_REGION.*(START|END|LENGTH)"
- memory_layout = {}
-
- region_symbols = filter(lambda s: match(expr, s), self._symbols)
-
- for symbol in region_symbols:
- region, _, attr = tuple(symbol.lower().strip("__").split("_"))
- if region not in memory_layout:
- memory_layout[region] = {}
-
- memory_layout[region][attr] = self._symbols[symbol]
-
- if "start" and "length" and "end" in memory_layout[region]:
- memory_layout[region]["limit"] = (
- memory_layout[region]["start"]
- + memory_layout[region]["length"]
- )
- memory_layout[region]["free"] = (
- memory_layout[region]["limit"]
- - memory_layout[region]["end"]
- )
- memory_layout[region]["total"] = memory_layout[region][
- "length"
- ]
- memory_layout[region]["size"] = (
- memory_layout[region]["end"]
- - memory_layout[region]["start"]
- )
-
- return memory_layout
+ @property
+ def footprint(self) -> Dict[str, Region]:
+ return self._footprint
diff --git a/tools/memory/src/memory/memmap.py b/tools/memory/src/memory/memmap.py
index f46db8c..e02010b 100755
--- a/tools/memory/src/memory/memmap.py
+++ b/tools/memory/src/memory/memmap.py
@@ -1,19 +1,32 @@
-#!/usr/bin/env python3
-
#
# Copyright (c) 2023-2025, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
+import re
+import shutil
+from dataclasses import dataclass
from pathlib import Path
+from typing import Any, Dict, List, Optional
import click
-from memory.buildparser import TfaBuildParser
+
+from memory.elfparser import TfaElfParser
+from memory.image import Image
+from memory.mapparser import TfaMapParser
from memory.printer import TfaPrettyPrinter
+from memory.summary import MapParser
-@click.command()
+@dataclass
+class Context:
+ build_path: Optional[Path] = None
+ printer: Optional[TfaPrettyPrinter] = None
+
+
+@click.group()
+@click.pass_obj
@click.option(
"-r",
"--root",
@@ -36,75 +49,171 @@
type=click.Choice(["debug", "release"], case_sensitive=False),
)
@click.option(
- "-f",
- "--footprint",
- is_flag=True,
- show_default=True,
- help="Generate a high level view of memory usage by memory types.",
+ "-w",
+ "--width",
+ type=int,
+ default=shutil.get_terminal_size().columns,
+ help="Column width for printing.",
)
@click.option(
- "-t",
- "--tree",
- is_flag=True,
- help="Generate a hierarchical view of the modules, segments and sections.",
-)
-@click.option(
- "--depth",
- default=3,
- help="Generate a virtual address map of important TF symbols.",
-)
-@click.option(
- "-s",
- "--symbols",
- is_flag=True,
- help="Generate a map of important TF symbols.",
-)
-@click.option("-w", "--width", type=int, envvar="COLUMNS")
-@click.option(
"-d",
is_flag=True,
default=False,
help="Display numbers in decimal base.",
)
+def cli(
+ obj: Context,
+ root: Optional[Path],
+ platform: str,
+ build_type: str,
+ width: int,
+ d: bool,
+):
+ obj.build_path = root if root is not None else Path("build", platform, build_type)
+ click.echo(f"build-path: {obj.build_path.resolve()}")
+
+ obj.printer = TfaPrettyPrinter(columns=width, as_decimal=d)
+
+
+@cli.command()
+@click.pass_obj
@click.option(
"--no-elf-images",
is_flag=True,
help="Analyse the build's map files instead of ELF images.",
)
-def main(
- root: Path,
- platform: str,
- build_type: str,
- footprint: str,
- tree: bool,
- symbols: bool,
- depth: int,
- width: int,
- d: bool,
- no_elf_images: bool,
-):
- build_path = root if root else Path("build/", platform, build_type)
- click.echo(f"build-path: {build_path.resolve()}")
+def footprint(obj: Context, no_elf_images: bool):
+ """Generate a high level view of memory usage by memory types."""
- parser = TfaBuildParser(build_path, map_backend=no_elf_images)
- printer = TfaPrettyPrinter(columns=width, as_decimal=d)
+ assert obj.build_path is not None
+ assert obj.printer is not None
- if footprint or not (tree or symbols):
- printer.print_footprint(parser.get_mem_usage_dict())
+ elf_image_paths: List[Path] = (
+ [] if no_elf_images else list(obj.build_path.glob("**/*.elf"))
+ )
- if tree:
- printer.print_mem_tree(
- parser.get_mem_tree_as_dict(), parser.module_names, depth=depth
- )
+ map_file_paths: List[Path] = (
+ [] if not no_elf_images else list(obj.build_path.glob("**/*.map"))
+ )
- if symbols:
- expr = (
- r"(.*)(TEXT|BSS|RO|RODATA|STACKS|_OPS|PMF|XLAT|GOT|FCONF|RELA"
- r"|R.M)(.*)(START|UNALIGNED|END)__$"
- )
- printer.print_symbol_table(
- parser.filter_symbols(parser.symbols, expr), parser.module_names
- )
+ images: Dict[str, Image] = dict()
+
+ for elf_image_path in elf_image_paths:
+ with open(elf_image_path, "rb") as elf_image_io:
+ images[elf_image_path.stem.upper()] = TfaElfParser(elf_image_io)
+
+ for map_file_path in map_file_paths:
+ with open(map_file_path, "r") as map_file_io:
+ images[map_file_path.stem.upper()] = TfaMapParser(map_file_io)
+
+ obj.printer.print_footprint({k: v.footprint for k, v in images.items()})
+
+
+@cli.command()
+@click.pass_obj
+@click.option(
+ "--depth",
+ default=3,
+ show_default=True,
+ help="Generate a virtual address map of important TF symbols.",
+)
+def tree(obj: Context, depth: int):
+ """Generate a hierarchical view of the modules, segments and sections."""
+
+ assert obj.build_path is not None
+ assert obj.printer is not None
+
+ paths: List[Path] = list(obj.build_path.glob("**/*.elf"))
+ images: Dict[str, TfaElfParser] = dict()
+
+ for path in paths:
+ with open(path, "rb") as io:
+ images[path.stem] = TfaElfParser(io)
+
+ mtree: Dict[str, Dict[str, Any]] = {
+ k: {
+ "name": k,
+ **v.get_mod_mem_usage_dict(),
+ **{"children": v.get_seg_map_as_dict()},
+ }
+ for k, v in images.items()
+ }
+
+ obj.printer.print_mem_tree(mtree, list(mtree.keys()), depth=depth)
+
+
+@cli.command()
+@click.pass_obj
+@click.option(
+ "--no-elf-images",
+ is_flag=True,
+ help="Analyse the build's map files instead of ELF images.",
+)
+def symbols(obj: Context, no_elf_images: bool):
+ """Generate a map of important TF symbols."""
+
+ assert obj.build_path is not None
+ assert obj.printer is not None
+
+ expr: str = (
+ r"(.*)(TEXT|BSS|RO|RODATA|STACKS|_OPS|PMF|XLAT|GOT|FCONF|RELA"
+ r"|R.M)(.*)(START|UNALIGNED|END)__$"
+ )
+
+ elf_image_paths: List[Path] = (
+ [] if no_elf_images else list(obj.build_path.glob("**/*.elf"))
+ )
+
+ map_file_paths: List[Path] = (
+ [] if not no_elf_images else list(obj.build_path.glob("**/*.map"))
+ )
+
+ images: Dict[str, Image] = dict()
+
+ for elf_image_path in elf_image_paths:
+ with open(elf_image_path, "rb") as elf_image_io:
+ images[elf_image_path.stem] = TfaElfParser(elf_image_io)
+
+ for map_file_path in map_file_paths:
+ with open(map_file_path, "r") as map_file_io:
+ images[map_file_path.stem] = TfaMapParser(map_file_io)
+
+ symbols = {k: v.symbols for k, v in images.items()}
+ symbols = {
+ image: {
+ symbol: symbol_value
+ for symbol, symbol_value in symbols.items()
+ if re.match(expr, symbol)
+ }
+ for image, symbols in symbols.items()
+ }
+
+ obj.printer.print_symbol_table(symbols, list(images.keys()))
+
+
+@cli.command()
+@click.option("-o", "--old", type=click.Path(exists=True))
+@click.option("-d", "--depth", type=int, default=2)
+@click.option("-e", "--exclude-fill")
+@click.option(
+ "-t",
+ "--type",
+ type=click.Choice(MapParser.export_formats, case_sensitive=False),
+ default="table",
+)
+@click.argument("file", type=click.Path(exists=True))
+def summary(file: Path, old: Optional[Path], depth: int, exclude_fill: bool, type: str):
+ """Summarize the sizes of translation units within the resulting binary"""
+ memap = MapParser()
+
+ if not memap.parse(file, old, exclude_fill):
+ exit(1)
+
+ memap.generate_output(type, depth)
+
+
+def main():
+ cli(obj=Context())
if __name__ == "__main__":
diff --git a/tools/memory/src/memory/printer.py b/tools/memory/src/memory/printer.py
index f797139..6debf53 100755
--- a/tools/memory/src/memory/printer.py
+++ b/tools/memory/src/memory/printer.py
@@ -4,10 +4,14 @@
# SPDX-License-Identifier: BSD-3-Clause
#
+from typing import Any, Dict, List, Optional, Tuple
+
from anytree import RenderTree
from anytree.importer import DictImporter
from prettytable import PrettyTable
+from memory.image import Region
+
class TfaPrettyPrinter:
"""A class for printing the memory layout of ELF files.
@@ -17,19 +21,29 @@
structured and consumed.
"""
- def __init__(self, columns: int = None, as_decimal: bool = False):
- self.term_size = columns if columns and columns > 120 else 120
- self._tree = None
- self._footprint = None
- self._symbol_map = None
- self.as_decimal = as_decimal
+ def __init__(self, columns: int, as_decimal: bool = False) -> None:
+ self.term_size: int = columns
+ self._tree: Optional[List[str]] = None
+ self._symbol_map: Optional[List[str]] = None
+ self.as_decimal: bool = as_decimal
- def format_args(self, *args, width=10, fmt=None):
- if not fmt and type(args[0]) is int:
+ def format_args(
+ self,
+ *args: Any,
+ width: int = 10,
+ fmt: Optional[str] = None,
+ ) -> List[str]:
+ if not fmt and isinstance(args[0], int):
fmt = f">{width}x" if not self.as_decimal else f">{width}"
- return [f"{arg:{fmt}}" if fmt else arg for arg in args]
+ return [f"{arg:{fmt}}" if fmt else str(arg) for arg in args]
- def format_row(self, leading, *args, width=10, fmt=None):
+ def format_row(
+ self,
+ leading: str,
+ *args: Any,
+ width: int = 10,
+ fmt: Optional[str] = None,
+ ) -> str:
formatted_args = self.format_args(*args, width=width, fmt=fmt)
return leading + " ".join(formatted_args)
@@ -39,9 +53,9 @@
section_name: str,
rel_pos: int,
columns: int,
- width: int = None,
+ width: int,
is_edge: bool = False,
- ):
+ ) -> str:
empty_col = "{:{}{}}"
# Some symbols are longer than the column width, truncate them until
@@ -50,28 +64,26 @@
if len_over > 0:
section_name = section_name[len_over:-len_over]
- sec_row = f"+{section_name:-^{width-1}}+"
+ sec_row = f"+{section_name:-^{width - 1}}+"
sep, fill = ("+", "-") if is_edge else ("|", "")
sec_row_l = empty_col.format(sep, fill + "<", width) * rel_pos
- sec_row_r = empty_col.format(sep, fill + ">", width) * (
- columns - rel_pos - 1
- )
+ sec_row_r = empty_col.format(sep, fill + ">", width) * (columns - rel_pos - 1)
return leading + sec_row_l + sec_row + sec_row_r
def print_footprint(
- self, app_mem_usage: dict, sort_key: str = None, fields: list = None
+ self,
+ app_mem_usage: Dict[str, Dict[str, Region]],
):
- assert len(app_mem_usage), "Empty memory layout dictionary!"
- if not fields:
- fields = ["Component", "Start", "Limit", "Size", "Free", "Total"]
+ assert app_mem_usage, "Empty memory layout dictionary!"
- sort_key = fields[0] if not sort_key else sort_key
+ fields = ["Component", "Start", "Limit", "Size", "Free", "Total"]
+ sort_key = fields[0]
# Iterate through all the memory types, create a table for each
# type, rows represent a single module.
- for mem in sorted(set(k for _, v in app_mem_usage.items() for k in v)):
+ for mem in sorted({k for v in app_mem_usage.values() for k in v}):
table = PrettyTable(
sortby=sort_key,
title=f"Memory Usage (bytes) [{mem.upper()}]",
@@ -79,13 +91,19 @@
)
for mod, vals in app_mem_usage.items():
- if mem in vals.keys():
+ if mem in vals:
val = vals[mem]
table.add_row(
[
- mod.upper(),
+ mod,
*self.format_args(
- *[val[k.lower()] for k in fields[1:]]
+ *[
+ val.start if val.start is not None else "?",
+ val.limit if val.limit is not None else "?",
+ val.size if val.size is not None else "?",
+ val.free if val.free is not None else "?",
+ val.length if val.length is not None else "?",
+ ]
),
]
)
@@ -93,31 +111,34 @@
def print_symbol_table(
self,
- symbols: list,
- modules: list,
+ symbol_table: Dict[str, Dict[str, int]],
+ modules: List[str],
start: int = 12,
- ):
- assert len(symbols), "Empty symbol list!"
+ ) -> None:
+ assert len(symbol_table), "Empty symbol list!"
modules = sorted(modules)
- col_width = int((self.term_size - start) / len(modules))
+ col_width = (self.term_size - start) // len(modules)
address_fixed_width = 11
- num_fmt = (
- f"0=#0{address_fixed_width}x" if not self.as_decimal else ">10"
- )
+ num_fmt = f"0=#0{address_fixed_width}x" if not self.as_decimal else ">10"
_symbol_map = [
- " " * start
- + "".join(self.format_args(*modules, fmt=f"^{col_width}"))
+ " " * start + "".join(self.format_args(*modules, fmt=f"^{col_width}"))
]
last_addr = None
- for i, (name, addr, mod) in enumerate(symbols):
+ symbols_list: List[Tuple[str, int, str]] = [
+ (name, addr, mod)
+ for mod, syms in symbol_table.items()
+ for name, addr in syms.items()
+ ]
+
+ symbols_list.sort(key=lambda x: (-x[1], x[0]), reverse=True)
+
+ for i, (name, addr, mod) in enumerate(symbols_list):
# Do not print out an address twice if two symbols overlap,
# for example, at the end of one region and start of another.
- leading = (
- f"{addr:{num_fmt}}" + " " if addr != last_addr else " " * start
- )
+ leading = f"{addr:{num_fmt}}" + " " if addr != last_addr else " " * start
_symbol_map.append(
self.map_elf_symbol(
@@ -125,28 +146,30 @@
name,
modules.index(mod),
len(modules),
- width=col_width,
- is_edge=(not i or i == len(symbols) - 1),
+ col_width,
+ is_edge=(i == 0 or i == len(symbols_list) - 1),
)
)
last_addr = addr
- self._symbol_map = ["Memory Layout:"]
- self._symbol_map += list(reversed(_symbol_map))
+ self._symbol_map = ["Memory Layout:"] + list(reversed(_symbol_map))
print("\n".join(self._symbol_map))
def print_mem_tree(
- self, mem_map_dict, modules, depth=1, min_pad=12, node_right_pad=12
- ):
+ self,
+ mem_map_dict: Dict[str, Any],
+ modules: List[str],
+ depth: int = 1,
+ min_pad: int = 12,
+ node_right_pad: int = 12,
+ ) -> None:
# Start column should have some padding between itself and its data
# values.
anchor = min_pad + node_right_pad * (depth - 1)
headers = ["start", "end", "size"]
- self._tree = [
- (f"{'name':<{anchor}}" + " ".join(f"{arg:>10}" for arg in headers))
- ]
+ self._tree = [f"{'name':<{anchor}}" + " ".join(f"{arg:>10}" for arg in headers)]
for mod in sorted(modules):
root = DictImporter().import_(mem_map_dict[mod])
diff --git a/tools/memory/src/memory/summary.py b/tools/memory/src/memory/summary.py
new file mode 100644
index 0000000..b116caa
--- /dev/null
+++ b/tools/memory/src/memory/summary.py
@@ -0,0 +1,557 @@
+#
+# Copyright (c) 2016-2025, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+import json
+import os
+import re
+from collections import defaultdict
+from copy import deepcopy
+from os.path import (
+ abspath,
+ basename,
+ commonprefix,
+ dirname,
+ join,
+ relpath,
+ splitext,
+)
+from pathlib import Path
+from sys import stdout
+from typing import IO, Any, Dict, List, Optional, Pattern, Tuple, Union
+
+from jinja2 import FileSystemLoader, StrictUndefined
+from jinja2.environment import Environment
+from prettytable import HEADER, PrettyTable
+
+ModuleStats = Dict[str, int]
+Modules = Dict[str, ModuleStats]
+
+SECTIONS: Tuple[str, ...] = (".text", ".data", ".bss", ".heap", ".stack")
+MISC_FLASH_SECTIONS: Tuple[str, ...] = (".interrupts", ".flash_config")
+OTHER_SECTIONS: Tuple[str, ...] = (
+ ".interrupts_ram",
+ ".init",
+ ".ARM.extab",
+ ".ARM.exidx",
+ ".ARM.attributes",
+ ".eh_frame",
+ ".init_array",
+ ".fini_array",
+ ".jcr",
+ ".stab",
+ ".stabstr",
+ ".ARM.exidx",
+ ".ARM",
+)
+ALL_SECTIONS: Tuple[str, ...] = (
+ SECTIONS + OTHER_SECTIONS + MISC_FLASH_SECTIONS + ("unknown", "OUTPUT")
+)
+
+
+class Parser:
+ """Internal interface for parsing"""
+
+ _RE_OBJECT_FILE: Pattern[str] = re.compile(r"^(.+\/.+\.o(bj)?)$")
+ _RE_LIBRARY_OBJECT: Pattern[str] = re.compile(
+ r"((^.+" + r"" + r"lib.+\.a)\((.+\.o(bj)?)\))$"
+ )
+ _RE_STD_SECTION: Pattern[str] = re.compile(r"^\s+.*0x(\w{8,16})\s+0x(\w+)\s(.+)$")
+ _RE_FILL_SECTION: Pattern[str] = re.compile(
+ r"^\s*\*fill\*\s+0x(\w{8,16})\s+0x(\w+).*$"
+ )
+ _RE_TRANS_FILE: Pattern[str] = re.compile(r"^(.+\/|.+\.ltrans.o(bj)?)$")
+ _OBJECT_EXTENSIONS: Tuple[str, ...] = (".o", ".obj")
+
+ _modules: Modules
+ _fill: bool
+
+ def __init__(self, fill: bool = True):
+ self._modules: Modules = {}
+ self._fill = fill
+
+ def module_add(self, object_name: str, size: int, section: str):
+ """Adds a module or section to the list
+
+ Positional arguments:
+ object_name - name of the entry to add
+ size - the size of the module being added
+ section - the section the module contributes to
+ """
+ if (
+ not object_name
+ or not size
+ or not section
+ or (not self._fill and object_name == "[fill]")
+ ):
+ return
+
+ if object_name in self._modules:
+ self._modules[object_name].setdefault(section, 0)
+ self._modules[object_name][section] += size
+ return
+
+ obj_split = os.sep + basename(object_name)
+ for module_path, contents in self._modules.items():
+ if module_path.endswith(obj_split) or module_path == object_name:
+ contents.setdefault(section, 0)
+ contents[section] += size
+ return
+
+ new_module: ModuleStats = defaultdict(int)
+ new_module[section] = size
+ self._modules[object_name] = new_module
+
+ def module_replace(self, old_object: str, new_object: str):
+ """Replaces an object name with a new one"""
+ if old_object in self._modules:
+ self._modules[new_object] = self._modules.pop(old_object)
+
+ def check_new_section(self, line: str) -> Optional[str]:
+ """Check whether a new section in a map file has been detected
+
+ Positional arguments:
+ line - the line to check for a new section
+
+ return value - A section name, if a new section was found, None
+ otherwise
+ """
+ line_s = line.strip()
+ for i in ALL_SECTIONS:
+ if line_s.startswith(i):
+ return i
+ if line.startswith("."):
+ return "unknown"
+ else:
+ return None
+
+ def parse_object_name(self, line: str) -> str:
+ """Parse a path to object file
+
+ Positional arguments:
+ line - the path to parse the object and module name from
+
+ return value - an object file name
+ """
+ if re.match(self._RE_TRANS_FILE, line):
+ return "[misc]"
+
+ test_re_file_name = re.match(self._RE_OBJECT_FILE, line)
+
+ if test_re_file_name:
+ object_name = test_re_file_name.group(1)
+
+ return object_name
+ else:
+ test_re_obj_name = re.match(self._RE_LIBRARY_OBJECT, line)
+
+ if test_re_obj_name:
+ return join(test_re_obj_name.group(2), test_re_obj_name.group(3))
+ else:
+ if not line.startswith("LONG") and not line.startswith("linker stubs"):
+ print("Unknown object name found in GCC map file: %s" % line)
+ return "[misc]"
+
+ def parse_section(self, line: str) -> Tuple[str, int]:
+ """Parse data from a section of gcc map file
+
+ examples:
+ 0x00004308 0x7c ./BUILD/K64F/GCC_ARM/spi_api.o
+ .text 0x00000608 0x198 ./BUILD/K64F/HAL_CM4.o
+
+ Positional arguments:
+ line - the line to parse a section from
+ """
+ is_fill = re.match(self._RE_FILL_SECTION, line)
+ if is_fill:
+ o_name: str = "[fill]"
+ o_size: int = int(is_fill.group(2), 16)
+ return o_name, o_size
+
+ is_section = re.match(self._RE_STD_SECTION, line)
+ if is_section:
+ o_size: int = int(is_section.group(2), 16)
+ if o_size:
+ o_name: str = self.parse_object_name(is_section.group(3))
+ return o_name, o_size
+
+ return "", 0
+
+ def parse_mapfile(self, file_desc: IO[str]) -> Modules:
+ """Main logic to decode gcc map files
+
+ Positional arguments:
+ file_desc - a stream object to parse as a gcc map file
+ """
+ current_section: str = "unknown"
+
+ with file_desc as infile:
+ for line in infile:
+ if line.startswith("Linker script and memory map"):
+ current_section = "unknown"
+ break
+
+ for line in infile:
+ next_section = self.check_new_section(line)
+
+ if next_section == "OUTPUT":
+ break
+ elif next_section:
+ current_section = next_section
+
+ object_name, object_size = self.parse_section(line)
+ self.module_add(object_name, object_size, current_section)
+
+ def is_obj(name: str) -> bool:
+ return not name.startswith("[") or not name.endswith("]")
+
+ common_prefix: str = dirname(
+ commonprefix([o for o in self._modules.keys() if is_obj(o)])
+ )
+ new_modules: Modules = {}
+ for name, stats in self._modules.items():
+ if is_obj(name):
+ new_modules[relpath(name, common_prefix)] = stats
+ else:
+ new_modules[name] = stats
+ return new_modules
+
+
+class MapParser(object):
+ """An object that represents parsed results, parses the memory map files,
+ and writes out different file types of memory results
+ """
+
+ print_sections: Tuple[str, ...] = (".text", ".data", ".bss")
+ delta_sections: Tuple[str, ...] = (".text-delta", ".data-delta", ".bss-delta")
+
+ # sections to print info (generic for all toolchains)
+ sections: Tuple[str, ...] = SECTIONS
+ misc_flash_sections: Tuple[str, ...] = MISC_FLASH_SECTIONS
+ other_sections: Tuple[str, ...] = OTHER_SECTIONS
+
+ modules: Modules
+ old_modules: Modules
+ short_modules: Modules
+ mem_report: List[Dict[str, Union[str, ModuleStats]]]
+ mem_summary: Dict[str, int]
+ subtotal: Dict[str, int]
+ tc_name: Optional[str]
+
+ RAM_FORMAT_STR: str = "Total Static RAM memory (data + bss): {}({:+}) bytes\n"
+ ROM_FORMAT_STR: str = "Total Flash memory (text + data): {}({:+}) bytes\n"
+
+ def __init__(self):
+ # list of all modules and their sections
+ # full list - doesn't change with depth
+ self.modules: Modules = {}
+ self.old_modules = {}
+ # short version with specific depth
+ self.short_modules: Modules = {}
+
+ # Memory report (sections + summary)
+ self.mem_report: List[Dict[str, Union[str, ModuleStats]]] = []
+
+ # Memory summary
+ self.mem_summary: Dict[str, int] = {}
+
+ # Totals of ".text", ".data" and ".bss"
+ self.subtotal: Dict[str, int] = {}
+
+ # Name of the toolchain, for better headings
+ self.tc_name = None
+
+ def reduce_depth(self, depth: Optional[int]):
+ """
+ populates the short_modules attribute with a truncated module list
+
+ (1) depth = 1:
+ main.o
+ mbed-os
+
+ (2) depth = 2:
+ main.o
+ mbed-os/test.o
+ mbed-os/drivers
+
+ """
+ if depth == 0 or depth is None:
+ self.short_modules = deepcopy(self.modules)
+ else:
+ self.short_modules = dict()
+ for module_name, v in self.modules.items():
+ split_name = module_name.split(os.sep)
+ if split_name[0] == "":
+ split_name = split_name[1:]
+ new_name = join(*split_name[:depth])
+ self.short_modules.setdefault(new_name, defaultdict(int))
+ for section_idx, value in v.items():
+ self.short_modules[new_name][section_idx] += value
+ delta_name = section_idx + "-delta"
+ self.short_modules[new_name][delta_name] += value
+
+ for module_name, v in self.old_modules.items():
+ split_name = module_name.split(os.sep)
+ if split_name[0] == "":
+ split_name = split_name[1:]
+ new_name = join(*split_name[:depth])
+ self.short_modules.setdefault(new_name, defaultdict(int))
+ for section_idx, value in v.items():
+ delta_name = section_idx + "-delta"
+ self.short_modules[new_name][delta_name] -= value
+
+ export_formats: List[str] = ["json", "html", "table"]
+
+ def generate_output(
+ self,
+ export_format: str,
+ depth: Optional[int],
+ file_output: Optional[str] = None,
+ ) -> Optional[bool]:
+ """Generates summary of memory map data
+
+ Positional arguments:
+ export_format - the format to dump
+
+ Keyword arguments:
+ file_desc - descriptor (either stdout or file)
+ depth - directory depth on report
+
+ Returns: generated string for the 'table' format, otherwise Nonef
+ """
+ if depth is None or depth > 0:
+ self.reduce_depth(depth)
+ self.compute_report()
+ try:
+ if file_output:
+ file_desc = open(file_output, "w")
+ else:
+ file_desc = stdout
+ except IOError as error:
+ print("I/O error({0}): {1}".format(error.errno, error.strerror))
+ return False
+
+ to_call = {
+ "json": self.generate_json,
+ "html": self.generate_html,
+ "table": self.generate_table,
+ }[export_format]
+ to_call(file_desc)
+
+ if file_desc is not stdout:
+ file_desc.close()
+
+ @staticmethod
+ def _move_up_tree(tree: Dict[str, Any], next_module: str) -> Dict[str, Any]:
+ tree.setdefault("children", [])
+ for child in tree["children"]:
+ if child["name"] == next_module:
+ return child
+
+ new_module = {"name": next_module, "value": 0, "delta": 0}
+ tree["children"].append(new_module)
+
+ return new_module
+
+ def generate_html(self, file_desc: IO[str]):
+ """Generate a json file from a memory map for D3
+
+ Positional arguments:
+ file_desc - the file to write out the final report to
+ """
+
+ tree_text = {"name": ".text", "value": 0, "delta": 0}
+ tree_bss = {"name": ".bss", "value": 0, "delta": 0}
+ tree_data = {"name": ".data", "value": 0, "delta": 0}
+
+ def accumulate(tree_root: Dict[str, Any], size_key: str, stats: ModuleStats):
+ parts = module_name.split(os.sep)
+
+ val = stats.get(size_key, 0)
+ tree_root["value"] += val
+ tree_root["delta"] += val
+
+ cur = tree_root
+ for part in parts:
+ cur = self._move_up_tree(cur, part)
+ cur["value"] += val
+ cur["delta"] += val
+
+ def subtract(tree_root: Dict[str, Any], size_key: str, stats: ModuleStats):
+ parts = module_name.split(os.sep)
+
+ cur = tree_root
+ cur["delta"] -= stats.get(size_key, 0)
+
+ for part in parts:
+ children = {c["name"]: c for c in cur.get("children", [])}
+ if part not in children:
+ return
+
+ cur = children[part]
+ cur["delta"] -= stats.get(size_key, 0)
+
+ for module_name, dct in self.modules.items():
+ accumulate(tree_text, ".text", dct)
+ accumulate(tree_data, ".data", dct)
+ accumulate(tree_bss, ".bss", dct)
+
+ for module_name, dct in self.old_modules.items():
+ subtract(tree_text, ".text", dct)
+ subtract(tree_data, ".data", dct)
+ subtract(tree_bss, ".bss", dct)
+
+ jinja_loader = FileSystemLoader(dirname(abspath(__file__)))
+ jinja_environment = Environment(loader=jinja_loader, undefined=StrictUndefined)
+ template = jinja_environment.get_template("templates/summary-flamegraph.html")
+
+ name, _ = splitext(basename(file_desc.name))
+
+ if name.endswith("_map"):
+ name = name[:-4]
+ if self.tc_name:
+ name = f"{name} {self.tc_name}"
+
+ file_desc.write(
+ template.render(
+ {
+ "name": name,
+ "rom": json.dumps(
+ {
+ "name": "ROM",
+ "value": tree_text["value"] + tree_data["value"],
+ "delta": tree_text["delta"] + tree_data["delta"],
+ "children": [tree_text, tree_data],
+ }
+ ),
+ "ram": json.dumps(
+ {
+ "name": "RAM",
+ "value": tree_bss["value"] + tree_data["value"],
+ "delta": tree_bss["delta"] + tree_data["delta"],
+ "children": [tree_bss, tree_data],
+ }
+ ),
+ }
+ )
+ )
+
+ def generate_json(self, file_desc: IO[str]):
+ """Generate a json file from a memory map
+
+ Positional arguments:
+ file_desc - the file to write out the final report to
+ """
+ file_desc.write(json.dumps(self.mem_report, indent=4))
+ file_desc.write("\n")
+
+ def generate_table(self, file_desc: IO[str]):
+ """Generate a table from a memory map
+
+ Returns: string of the generated table
+ """
+ # Create table
+ columns = ["Module"]
+ columns.extend(self.print_sections)
+
+ table = PrettyTable(columns, junction_char="|", hrules=HEADER)
+ table.align["Module"] = "l"
+
+ for col in self.print_sections:
+ table.align[col] = "r"
+
+ for i in sorted(self.short_modules):
+ row = [i]
+
+ for k in self.print_sections:
+ row.append(
+ "{}({:+})".format(
+ self.short_modules[i][k], self.short_modules[i][k + "-delta"]
+ )
+ )
+
+ table.add_row(row)
+
+ subtotal_row = ["Subtotals"]
+ for k in self.print_sections:
+ subtotal_row.append(
+ "{}({:+})".format(self.subtotal[k], self.subtotal[k + "-delta"])
+ )
+
+ table.add_row(subtotal_row)
+
+ output = table.get_string()
+ output += "\n"
+
+ output += self.RAM_FORMAT_STR.format(
+ self.mem_summary["static_ram"], self.mem_summary["static_ram_delta"]
+ )
+ output += self.ROM_FORMAT_STR.format(
+ self.mem_summary["total_flash"], self.mem_summary["total_flash_delta"]
+ )
+ file_desc.write(output)
+
+ def compute_report(self):
+ """Generates summary of memory usage for main areas"""
+ self.subtotal = defaultdict(int)
+
+ for mod in self.modules.values():
+ for k in self.sections:
+ self.subtotal[k] += mod[k]
+ self.subtotal[k + "-delta"] += mod[k]
+
+ for mod in self.old_modules.values():
+ for k in self.sections:
+ self.subtotal[k + "-delta"] -= mod[k]
+
+ self.mem_summary = {
+ "static_ram": self.subtotal[".data"] + self.subtotal[".bss"],
+ "static_ram_delta": self.subtotal[".data-delta"]
+ + self.subtotal[".bss-delta"],
+ "total_flash": (self.subtotal[".text"] + self.subtotal[".data"]),
+ "total_flash_delta": self.subtotal[".text-delta"]
+ + self.subtotal[".data-delta"],
+ }
+
+ self.mem_report = []
+ if self.short_modules:
+ for name, sizes in sorted(self.short_modules.items()):
+ self.mem_report.append(
+ {
+ "module": name,
+ "size": {
+ k: sizes.get(k, 0)
+ for k in (self.print_sections + self.delta_sections)
+ },
+ }
+ )
+
+ self.mem_report.append({"summary": self.mem_summary})
+
+ def parse(
+ self, mapfile: Path, oldfile: Optional[Path] = None, no_fill: bool = False
+ ) -> bool:
+ """Parse and decode map file depending on the toolchain
+
+ Positional arguments:
+ mapfile - the file name of the memory map file
+ toolchain - the toolchain used to create the file
+ """
+ try:
+ with open(mapfile, "r") as file_input:
+ self.modules = Parser(not no_fill).parse_mapfile(file_input)
+ try:
+ if oldfile is not None:
+ with open(oldfile, "r") as old_input:
+ self.old_modules = Parser(not no_fill).parse_mapfile(old_input)
+ else:
+ self.old_modules = self.modules
+ except IOError:
+ self.old_modules = {}
+ return True
+
+ except IOError as error:
+ print("I/O error({0}): {1}".format(error.errno, error.strerror))
+ return False
diff --git a/tools/memory/src/memory/templates/summary-flamegraph.html b/tools/memory/src/memory/templates/summary-flamegraph.html
new file mode 100644
index 0000000..9ec8ecb
--- /dev/null
+++ b/tools/memory/src/memory/templates/summary-flamegraph.html
@@ -0,0 +1,110 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+
+ <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
+ integrity="sha256-916EbMg70RQy9LHiGkXzG8hSg9EdNy97GazNG/aiY1w=" crossorigin="anonymous" />
+ <link rel="stylesheet" type="text/css"
+ href="https://cdn.jsdelivr.net/gh/spiermar/d3-flame-graph@1.0.4/dist/d3.flameGraph.min.css"
+ integrity="sha256-w762vSe6WGrkVZ7gEOpnn2Y+FSmAGlX77jYj7nhuCyY=" crossorigin="anonymous" />
+
+ <style>
+ /* Space out content a bit */
+ body {
+ padding-top: 20px;
+ padding-bottom: 20px;
+ }
+
+ /* Custom page header */
+ .header {
+ padding-bottom: 20px;
+ padding-right: 15px;
+ padding-left: 15px;
+ border-bottom: 1px solid #e5e5e5;
+ }
+
+ /* Make the masthead heading the same height as the navigation */
+ .header h3 {
+ margin-top: 0;
+ margin-bottom: 0;
+ line-height: 40px;
+ }
+ </style>
+
+ <title>{{name}} Memory Details</title>
+</head>
+
+<body>
+ <div class="container">
+ <div class="header clearfix">
+ <h3 class="text-muted">{{name}} Memory Details</h3>
+ </div>
+ <div id="chart-rom">
+ </div>
+ <hr />
+ <div id="chart-ram">
+ </div>
+ <hr />
+ <div id="details"></div>
+ </div>
+
+ <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"
+ integrity="sha256-r7j1FXNTvPzHR41+V71Jvej6fIq4v4Kzu5ee7J/RitM=" crossorigin="anonymous">
+ </script>
+ <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.7.1/d3-tip.min.js"
+ integrity="sha256-z0A2CQF8xxCKuOJsn4sJ5HBjxiHHRAfTX8hDF4RSN5s=" crossorigin="anonymous">
+ </script>
+ <script type="text/javascript"
+ src="https://cdn.jsdelivr.net/gh/spiermar/d3-flame-graph@1.0.4/dist/d3.flameGraph.min.js"
+ integrity="sha256-I1CkrWbmjv+GWjgbulJ4i0vbzdrDGfxqdye2qNlhG3Q=" crossorigin="anonymous">
+ </script>
+
+ <script type="text/javascript">
+ var tip = d3.tip()
+ .direction("s")
+ .offset([8, 0])
+ .attr('class', 'd3-flame-graph-tip')
+ .html(function (d) { return "module: " + d.data.name + ", bytes: " + d.data.value + ", delta: " + d.data.delta; });
+ var colorizer = function (d) {
+ if (d.data.delta > 0) {
+ ratio = (d.data.value - d.data.delta) / d.data.value;
+ green = ("0" + (Number(ratio * 0xFF | 0).toString(16))).slice(-2).toUpperCase();
+ blue = ("0" + (Number(ratio * 0xEE | 0).toString(16))).slice(-2).toUpperCase();
+ console.log(d.data.name, green, blue);
+ return "#EE" + green + blue
+ } else if (d.data.delta < 0) {
+ ratio = (d.data.value + d.data.delta) / d.data.value;
+ green = ("0" + (Number(ratio * 0xFF | 0).toString(16))).slice(-2).toUpperCase();
+ red = ("0" + (Number(ratio * 0xFF | 0).toString(16))).slice(-2).toUpperCase();
+ console.log(d.data.name, red, green);
+ return "#" + red + green + "EE";
+ } else {
+ return "#FFFFEE";
+ }
+ }
+ var flameGraph_rom = d3.flameGraph()
+ .transitionDuration(250)
+ .transitionEase(d3.easeCubic)
+ .sort(true)
+ .color(colorizer)
+ .tooltip(tip);
+ var flameGraph_ram = d3.flameGraph()
+ .transitionDuration(250)
+ .transitionEase(d3.easeCubic)
+ .sort(true)
+ .color(colorizer)
+ .tooltip(tip);
+ var rom_elem = d3.select("#chart-rom");
+ flameGraph_rom.width(rom_elem.node().getBoundingClientRect().width);
+ rom_elem.datum({{ rom }}).call(flameGraph_rom);
+ var ram_elem = d3.select("#chart-ram");
+ flameGraph_ram.width(ram_elem.node().getBoundingClientRect().width);
+ ram_elem.datum({{ ram }}).call(flameGraph_ram);
+ </script>
+</body>
+
+</html>
diff --git a/tools/sptool/sp_mk_generator.py b/tools/sptool/sp_mk_generator.py
index 9a00c74..3dd1d4e 100644
--- a/tools/sptool/sp_mk_generator.py
+++ b/tools/sptool/sp_mk_generator.py
@@ -1,5 +1,5 @@
#!/usr/bin/python3
-# Copyright (c) 2020-2024, Arm Limited. All rights reserved.
+# Copyright (c) 2020-2025, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
@@ -156,7 +156,7 @@
def get_load_address(sp_layout, sp, args :dict):
''' Helper to fetch load-address from pm file listed in sp_layout.json'''
with open(get_sp_manifest_full_path(sp_layout[sp], args), "r") as pm_f:
- load_address_lines = [l for l in pm_f if 'load-address' in l]
+ load_address_lines = [l for l in pm_f if re.search(r'load-address[^-]', l)]
if len(load_address_lines) != 1:
return None