Initial commit
Change-Id: I26f6fdfd4962e2c724bf6b68893156f11d37d4b1
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3570d14
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,96 @@
+# Created by https://www.toptal.com/developers/gitignore/api/c++,cmake,c
+# Edit at https://www.toptal.com/developers/gitignore?templates=c++,cmake,c
+
+### C ###
+# Prerequisites
+*.d
+build/
+unity
+
+# Object files
+*.o
+*.ko
+*.obj
+*.elf
+
+# Linker output
+*.ilk
+*.map
+*.exp
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
+# Debug files
+*.dSYM/
+*.su
+*.idb
+*.pdb
+
+# Kernel Module Compile Results
+*.mod*
+*.cmd
+.tmp_versions/
+modules.order
+Module.symvers
+Mkfile.old
+dkms.conf
+
+### C++ ###
+# Prerequisites
+
+# Compiled Object files
+*.slo
+
+# Precompiled Headers
+
+# Compiled Dynamic libraries
+
+# Fortran module files
+*.mod
+*.smod
+
+# Compiled Static libraries
+*.lai
+
+# Executables
+
+### CMake ###
+CMakeLists.txt.user
+CMakeCache.txt
+CMakeFiles
+CMakeScripts
+Testing
+Makefile
+cmake_install.cmake
+install_manifest.txt
+compile_commands.json
+CTestTestfile.cmake
+_deps
+
+### CMake Patch ###
+# External projects
+*-prefix/
+
+# End of https://www.toptal.com/developers/gitignore/api/c++,cmake,c
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..71b5b35
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,46 @@
+#
+# Copyright The Transfer List Library Contributors
+#
+# SPDX-License-Identifier: MIT OR GPL-2.0-or-later
+#
+
+cmake_minimum_required(VERSION 3.15)
+
+project(libtl VERSION 0.9 LANGUAGES C CXX ASM)
+
+#
+# Set global flags.
+#
+set(CMAKE_C_STANDARD 11)
+set(CMAKE_C_STANDARD_REQUIRED TRUE)
+set(CMAKE_C_EXTENSIONS TRUE)
+
+add_library(cxx_compiler_flags INTERFACE)
+target_compile_features(cxx_compiler_flags INTERFACE cxx_std_11)
+
+SET(TARGET_GROUP release CACHE STRING "Specify the Build Target [\"release\" by default]")
+
+add_library(tl STATIC "${CMAKE_CURRENT_SOURCE_DIR}/src/transfer_list.c")
+target_include_directories(tl PUBLIC include)
+target_link_libraries(tl PUBLIC cxx_compiler_flags)
+
+if(TARGET_GROUP STREQUAL test)
+ include(CTest)
+
+ # Check if local unity/ directory exists
+ if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/unity/CMakeLists.txt")
+ message(STATUS "Using local Unity framework.")
+ add_subdirectory(unity)
+ else()
+ message(STATUS "Fetching Unity framework using FetchContent...")
+ include(FetchContent)
+ FetchContent_Declare(
+ unity
+ GIT_REPOSITORY https://github.com/ThrowTheSwitch/Unity.git
+ GIT_TAG master
+ )
+ FetchContent_MakeAvailable(unity)
+ endif()
+
+ add_subdirectory(test)
+endif()
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..be9c85d
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,25 @@
+Contributing
+============
+We gratefully accept bug reports and contributions from the community. All contributions are
+reviewed by the project team / community, and may need some modifications to be accepted.
+
+Continuous Integration Tests
+----------------------------
+Once a PR has been made, the Continuous Integration (CI) tests are triggered and run. You should
+follow the result of the CI tests, and fix failures.
+
+License and Copyright
+---------------------
+Unless specifically indicated otherwise in a file, Transfer List Library files are provided under a
+**dual-license**:[MIT](LICENSE-MIT.md) **OR** [GPL-2.0-or-later](LICENSE-GPL-2.0-or-later.md)
+license. See the [LICENSE](LICENSE) file for the full text of these licenses.
+
+Contributors must accept that their contributions are made under both the MIT AND GPL-2.0-or-later
+licenses.
+
+All new files should include the standard SPDX license identifier where possible, i.e.
+"SPDX-License-Identifier: MIT OR GPL-2.0-or-later".
+
+The copyright on contributions is retained by the original authors of the code. Where possible for
+new files, this should be noted in a comment at the top of the file in the form:
+"Copyright The Transfer List Library Contributors".
diff --git a/LICENSE-GPL-2.0-or-later.md b/LICENSE-GPL-2.0-or-later.md
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/LICENSE-GPL-2.0-or-later.md
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/LICENSE-MIT.md b/LICENSE-MIT.md
new file mode 100644
index 0000000..baa0431
--- /dev/null
+++ b/LICENSE-MIT.md
@@ -0,0 +1,19 @@
+Copyright The Transfer List Library Contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..63dadcc
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,28 @@
+# Transfer List Library Licensing
+
+The Transfer List Library files are provided under a **dual-license**: [MIT](LICENSE-MIT.md)
+**OR** [GPL-2.0-or-later](LICENSE-GPL-2.0-or-later.md).
+
+This means that users may **choose** which of these licenses they take the code under.
+
+The full text of each of these licenses is provided in:
+- **[LICENSE-MIT.md](LICENSE-MIT.md)**
+- **[LICENSE-GPL-2.0-or-later.md](LICENSE-GPL-2.0-or-later.md)**
+
+## SPDX Identifiers
+
+Individual files contain the following tag instead of the full license text:
+
+```plaintext
+SPDX-License-Identifier: MIT OR GPL-2.0-or-later
+```
+
+## Other Projects
+
+This project includes and depends on additional third-party software components:
+
+### Unity Test Framework
+
+- **Purpose**: Used for unit testing.
+- **Repository**: [https://github.com/ThrowTheSwitch/Unity](https://github.com/ThrowTheSwitch/Unity)
+- **License**: MIT License
diff --git a/Makefile.tll b/Makefile.tll
new file mode 100644
index 0000000..4bd295b
--- /dev/null
+++ b/Makefile.tll
@@ -0,0 +1,22 @@
+#
+# Copyright The Transfer List Library Contributors.
+#
+# SPDX-License-Identifier: MIT OR GPL-2.0-or-later
+#
+
+#
+# This is not a complete Makefile of itself. Instead, it is designed to
+# be easily embeddable into other systems of Makefiles.
+#
+
+LIBTL_soname = libtl.$(SHAREDLIB_EXT).1
+LIBTL_INCLUDES = debug.h transfer_list.h utils_def_exp.h utils_def.h
+LIBTL_VERSION = version.lds
+LIBTL_SRCS = src/transfer_list.c
+LIBTL_OBJS = $(TLL_SRCS:src/%.c=%.o)
+LIBTL_LIB = libtll-$(DTC_VERSION).$(SHAREDLIB_EXT)
+
+libtl_clean:
+ @$(VECHO) CLEAN "(libfdt)"
+ rm -f $(STD_CLEANFILES:%=$(LIBTL_dir)/%)
+ rm -f $(LIBTL_dir)/$(TLL_soname)
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..117dbd8
--- /dev/null
+++ b/README.md
@@ -0,0 +1,55 @@
+# Transfer List Library (LibTL)
+
+The Transfer List Library (LibTL) implements the [Firmware
+Handoff](https://github.com/FirmwareHandoff/firmware_handoff) specification,
+providing a streamlined interface for managing transfer lists. LibTL offers a
+user-friendly interface for:
+
+- Creating transfer lists
+- Reading and extracting data from transfer lists
+- Manipulating and updating transfer lists
+
+The library supports building with host tools such as Clang and GCC, and cross
+compilation with the Aarch64 GNU compiler.
+
+## Building with CMake
+
+To configure the project, use the following command. This will default to using
+GCC as the toolchain and create a build directory named `build/`:
+
+```sh
+cmake -B build
+```
+
+To build the project, use:
+
+```sh
+cmake --build build
+```
+
+This will output libtl.a in the build directory.
+
+For cross-compilation or selecting a different compiler, specify the target
+toolchain file using the `CMAKE_TOOLCHAIN_FILE` option and the target with
+`CROSS_COMPILE`:
+
+```sh
+export CROSS_COMPILE=aarch64-none-elf-
+cmake -B build -DCMAKE_TOOLCHAIN_FILE=toolchains/host/gnu.cmake
+```
+
+## Testing with CTest
+
+Tests for LibTL are provided in the folder `test`, to configure the project for
+test builds, run:
+
+```sh
+cmake -B build -DTARGET_GROUP=test
+cmake --build build
+```
+
+Then, to run the tests with `ctest`, use:
+
+```sh
+ctest --test-dir build/
+```
diff --git a/include/math_utils.h b/include/math_utils.h
new file mode 100644
index 0000000..6966287
--- /dev/null
+++ b/include/math_utils.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright The Transfer List Library Contributors
+ *
+ * SPDX-License-Identifier: MIT OR GPL-2.0-or-later
+ */
+
+#ifndef MATH_UTILS_H
+#define MATH_UTILS_H
+
+#include <stdint.h>
+
+/**
+ * @brief Rounds up a given value to the nearest multiple of a boundary.
+ *
+ * @param value The value to round up.
+ * @param boundary The alignment boundary (must be a power of two).
+ * @return The smallest multiple of `boundary` that is greater than or equal to `value`.
+ */
+static inline uintptr_t align_up(uintptr_t value, uintptr_t boundary)
+{
+ return (value + boundary - 1) & ~(boundary - 1);
+}
+
+/**
+ * @brief Checks whether `value` is aligned to the specified `boundary`.
+ *
+ * @param value The value to check.
+ * @param boundary The alignment boundary (should be a power of two for efficiency).
+ * @return `1` if `value` is aligned, `0` otherwise.
+ */
+static inline int is_aligned(uintptr_t value, uintptr_t boundary) {
+ return (value % boundary) == 0;
+}
+
+/**
+ * @brief Safely adds `a` and `b`, detecting overflow.
+ *
+ * @param a First operand.
+ * @param b Second operand.
+ * @param res Pointer to store the result if no overflow occurs.
+ * @return `1` if overflow occurs, `0` otherwise.
+ */
+#define add_overflow(a, b, res) __builtin_add_overflow((a), (b), (res))
+
+/**
+ * @brief Rounds up `v` to the nearest multiple of `size`, detecting overflow.
+ *
+ * @param v The value to round up.
+ * @param size The alignment boundary (must be a power of two).
+ * @param res Pointer to store the rounded-up result.
+ * @return `1` if an overflow occurs, `0` otherwise.
+ */
+#define round_up_overflow(v, size, res) \
+ (__extension__({ \
+ typeof(res) __res = res; \
+ typeof(*(__res)) __roundup_tmp = 0; \
+ typeof(v) __roundup_mask = (typeof(v))(size) - 1; \
+ \
+ add_overflow((v), __roundup_mask, &__roundup_tmp) ? \
+ 1 : \
+ (void)(*(__res) = __roundup_tmp & ~__roundup_mask), \
+ 0; \
+ }))
+
+/**
+ * @brief Adds `a` and `b`, then rounds up the result to the nearest multiple
+ * of `size`, detecting overflow.
+ *
+ * @param a First operand for addition.
+ * @param b Second operand for addition.
+ * @param size The alignment boundary (must be positive).
+ * @param res Pointer to store the final rounded result.
+ * @return `1` if an overflow occurs during addition or rounding, `0` otherwise.
+ */
+#define add_with_round_up_overflow(a, b, size, res) \
+ (__extension__({ \
+ typeof(a) __a = (a); \
+ typeof(__a) __add_res = 0; \
+ \
+ add_overflow((__a), (b), &__add_res) ? 1 : \
+ round_up_overflow(__add_res, (size), (res)) ? 1 : \
+ 0; \
+ }))
+
+#endif /* MATH_UTILS_H */
diff --git a/include/transfer_list.h b/include/transfer_list.h
new file mode 100644
index 0000000..7ae57bc
--- /dev/null
+++ b/include/transfer_list.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright The Transfer List Library Contributors
+ *
+ * SPDX-License-Identifier: MIT OR GPL-2.0-or-later
+ */
+
+#ifndef TRANSFER_LIST_H
+#define TRANSFER_LIST_H
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#define TRANSFER_LIST_SIGNATURE 0x4a0fb10b
+#define TRANSFER_LIST_VERSION 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 3U
+
+/* Alignment required by TE header start address, in bytes */
+#define TRANSFER_LIST_GRANULE 8U
+
+/*
+ * 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 32UL
+#define REGISTER_CONVENTION_VERSION_SHIFT_32 24UL
+#define REGISTER_CONVENTION_VERSION_MASK 0xffUL
+#define REGISTER_CONVENTION_VERSION 1UL
+
+#define TRANSFER_LIST_HANDOFF_X1_VALUE(__version) \
+ ((TRANSFER_LIST_SIGNATURE & \
+ ((1UL << REGISTER_CONVENTION_VERSION_SHIFT_64) - 1)) | \
+ (((__version)®ISTER_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)®ISTER_CONVENTION_VERSION_MASK) \
+ << REGISTER_CONVENTION_VERSION_SHIFT_32))
+
+#ifndef __ASSEMBLER__
+
+#define TL_FLAGS_HAS_CHECKSUM (1U << 0U)
+
+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_OPTEE_PAGABLE_PART = 0x100,
+ TL_TAG_DT_SPMC_MANIFEST = 0x101,
+ TL_TAG_EXEC_EP_INFO64 = 0x102,
+ TL_TAG_TB_FW_CONFIG = 0x103,
+ TL_TAG_SRAM_LAYOUT64 = 0x104,
+ TL_TAG_MBEDTLS_HEAP_INFO = 0x105,
+};
+
+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)];
+ */
+};
+
+static_assert(sizeof(struct transfer_list_entry) == 0x8U,
+ "assert_transfer_list_entry_size");
+
+void transfer_list_dump(struct transfer_list_header *tl);
+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/src/transfer_list.c b/src/transfer_list.c
new file mode 100644
index 0000000..20903eb
--- /dev/null
+++ b/src/transfer_list.c
@@ -0,0 +1,486 @@
+/*
+ * Copyright The Transfer List Library Contributors
+ *
+ * SPDX-License-Identifier: MIT OR GPL-2.0-or-later
+ */
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <math_utils.h>
+#include <transfer_list.h>
+
+void transfer_list_dump(struct transfer_list_header *tl)
+{
+ struct transfer_list_entry *te = NULL;
+ int i = 0;
+
+ if (!tl) {
+ return;
+ }
+ printf("Dump transfer list:\n");
+ printf("signature 0x%x\n", tl->signature);
+ printf("checksum 0x%x\n", tl->checksum);
+ printf("version 0x%x\n", tl->version);
+ printf("hdr_size 0x%x\n", tl->hdr_size);
+ printf("alignment 0x%x\n", tl->alignment);
+ printf("size 0x%x\n", tl->size);
+ printf("max_size 0x%x\n", tl->max_size);
+ printf("flags 0x%x\n", tl->flags);
+ while (true) {
+ te = transfer_list_next(tl, te);
+ if (!te) {
+ break;
+ }
+ printf("Entry %d:\n", i++);
+ printf("tag_id 0x%x\n", te->tag_id);
+ printf("hdr_size 0x%x\n", te->hdr_size);
+ printf("data_size 0x%x\n", te->data_size);
+ printf("data_addr 0x%lx\n",
+ (unsigned long)transfer_list_entry_data(te));
+ }
+}
+
+/*******************************************************************************
+ * 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) {
+ printf("Bad transfer list signature %#" PRIx32 "\n",
+ tl->signature);
+ return TL_OPS_NON;
+ }
+
+ if (!tl->max_size) {
+ printf("Bad transfer list max size %#" PRIx32 "\n",
+ tl->max_size);
+ return TL_OPS_NON;
+ }
+
+ if (tl->size > tl->max_size) {
+ printf("Bad transfer list size %#" PRIx32 "\n", tl->size);
+ return TL_OPS_NON;
+ }
+
+ if (tl->hdr_size != sizeof(struct transfer_list_header)) {
+ printf("Bad transfer list header size %#" PRIx32 "\n",
+ tl->hdr_size);
+ return TL_OPS_NON;
+ }
+
+ if (!transfer_list_verify_checksum(tl)) {
+ printf("Bad transfer list checksum %#" PRIx32 "\n",
+ tl->checksum);
+ return TL_OPS_NON;
+ }
+
+ if (tl->version == 0) {
+ printf("Transfer list version is invalid\n");
+ return TL_OPS_NON;
+ } else if (tl->version == TRANSFER_LIST_VERSION) {
+ printf("Transfer list version is valid for all operations\n");
+ return TL_OPS_ALL;
+ } else if (tl->version > TRANSFER_LIST_VERSION) {
+ printf("Transfer list version is valid for read-only\n");
+ return TL_OPS_RO;
+ }
+
+ printf("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;
+ uintptr_t te_end;
+ size_t sz = 0;
+
+ if (!tl || (tag_id & (1 << 24))) {
+ return NULL;
+ }
+
+ /*
+ * skip the step 1 (optional step)
+ * new TE will be added into the tail
+ */
+ tl_ev = (uintptr_t)tl + tl->size;
+ te = (struct transfer_list_entry *)align_up(tl_ev,
+ TRANSFER_LIST_GRANULE);
+
+ te_end = align_up((uintptr_t)te + sizeof(*te) + data_size,
+ TRANSFER_LIST_GRANULE);
+
+ if (te_end > (uintptr_t)tl + tl->max_size) {
+ return NULL;
+ }
+
+ te->tag_id = tag_id;
+ te->hdr_size = sizeof(*te);
+ te->data_size = data_size;
+ tl->size += te_end - 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 = align_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;
+}
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
new file mode 100644
index 0000000..fb07b9c
--- /dev/null
+++ b/test/CMakeLists.txt
@@ -0,0 +1,18 @@
+#
+# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#
+
+file(GLOB TEST_SOURCES "*.c")
+
+foreach(src IN ITEMS ${TEST_SOURCES})
+ get_filename_component(suite_name ${src} NAME_WE)
+ add_executable(${suite_name} ${src})
+
+ target_link_libraries(${suite_name} unity tl)
+ add_test(${suite_name} ${suite_name})
+endforeach()
+
+enable_testing()
diff --git a/test/entry_manipulation.c b/test/entry_manipulation.c
new file mode 100644
index 0000000..72088bb
--- /dev/null
+++ b/test/entry_manipulation.c
@@ -0,0 +1,129 @@
+/*
+ * Copyright The Transfer List Library Contributors
+ *
+ * SPDX-License-Identifier: MIT OR GPL-2.0-or-later
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "test.h"
+#include "transfer_list.h"
+#include "unity.h"
+
+void *buffer = NULL;
+
+uint8_t byte_sum(const char *ptr, size_t len)
+{
+ uint8_t sum;
+
+ for (size_t i = 0; i < len; i++) {
+ sum += ptr[i];
+ }
+
+ return sum;
+}
+
+void test_add()
+{
+ struct transfer_list_header *tl;
+ struct transfer_list_entry *te;
+
+ TEST_ASSERT(tl = transfer_list_init((void *)buffer, TL_SIZE));
+
+ TEST_ASSERT(te = transfer_list_add(tl, test_tag, sizeof(test_data),
+ &test_data));
+ TEST_ASSERT_EQUAL(0, byte_sum((char *)tl, tl->max_size));
+ TEST_ASSERT(*(int *)transfer_list_entry_data(te) == test_data);
+
+ /* Try to add a TE larger greater than allocated TL space */
+ TEST_ASSERT_NULL(te = transfer_list_add(tl, 2, TL_SIZE, &test_data));
+ TEST_ASSERT_EQUAL(0, byte_sum((char *)tl, tl->max_size));
+ TEST_ASSERT_NULL(transfer_list_find(tl, 0x2));
+
+ unsigned int tags[4] = { TAG_GENERIC_START, TAG_GENERIC_END,
+ TAG_NON_STANDARD_START, TAG_NON_STANDARD_END };
+
+ for (size_t i = 0; i < 4; i++) {
+ TEST_ASSERT(te = transfer_list_add(tl, tags[i],
+ sizeof(test_data),
+ &test_data));
+ TEST_ASSERT_EQUAL(0, byte_sum((char *)tl, tl->max_size));
+ TEST_ASSERT(te = transfer_list_find(tl, tags[i]));
+ TEST_ASSERT(*(int *)transfer_list_entry_data(te) == test_data);
+ }
+
+ transfer_list_dump(tl);
+ /* Add some out of bound tags. */
+ TEST_ASSERT_NULL(
+ transfer_list_add(tl, 1 << 24, sizeof(test_data), &test_data));
+
+ TEST_ASSERT_NULL(
+ transfer_list_add(tl, -1, sizeof(test_data), &test_data));
+}
+
+void test_add_with_align()
+{
+ struct transfer_list_header *tl =
+ transfer_list_init(buffer, TL_MAX_SIZE);
+ struct transfer_list_entry *te;
+
+ unsigned int test_id = 1;
+ const unsigned int entry_size = 0xff;
+ int *data;
+
+ TEST_ASSERT(tl->size == tl->hdr_size);
+
+ /*
+ * When a new TE with a larger alignment requirement than already exists
+ * appears, the TE should be added and TL alignement updated.
+ */
+ for (char align = 0; align < (1 << 4); align++, test_id++) {
+ TEST_ASSERT(
+ te = transfer_list_add_with_align(
+ tl, test_id, entry_size, &test_data, align));
+ TEST_ASSERT(tl->alignment >= align);
+ TEST_ASSERT(te = transfer_list_find(tl, test_id));
+ TEST_ASSERT(data = transfer_list_entry_data(te));
+ TEST_ASSERT_FALSE((uintptr_t)data % (1 << align));
+ TEST_ASSERT_EQUAL(*(int *)data, test_data);
+ }
+}
+
+void test_rem()
+{
+ struct transfer_list_header *tl = transfer_list_init(buffer, TL_SIZE);
+ struct transfer_list_entry *te;
+
+ TEST_ASSERT_EQUAL(tl->size, tl->hdr_size);
+
+ /* Add a test TE, check the TL has been updated with its contents. */
+ TEST_ASSERT(
+ transfer_list_add(tl, test_tag, tl->max_size / 8, &test_data));
+ TEST_ASSERT(te = transfer_list_find(tl, test_tag));
+ TEST_ASSERT(byte_sum((void *)tl, tl->size) == 0);
+
+ /* Remove the TE and make sure it isn't present in the TL. */
+ TEST_ASSERT_TRUE(transfer_list_rem(tl, te));
+ TEST_ASSERT(byte_sum((void *)tl, tl->size) == 0);
+ TEST_ASSERT_NULL(transfer_list_find(tl, test_tag));
+}
+
+void setUp(void)
+{
+ buffer = malloc(TL_MAX_SIZE);
+}
+
+void tearDown(void)
+{
+ free(buffer);
+ buffer = NULL;
+}
+
+int main(void)
+{
+ UNITY_BEGIN();
+ RUN_TEST(test_add);
+ RUN_TEST(test_add_with_align);
+ return UNITY_END();
+}
diff --git a/test/test.h b/test/test.h
new file mode 100644
index 0000000..8ae4d3f
--- /dev/null
+++ b/test/test.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright The Transfer List Library Contributors
+ *
+ * SPDX-License-Identifier: MIT OR GPL-2.0-or-later
+ */
+
+#if !defined(TEST_H)
+#define TEST_H
+
+#define TL_MAX_SIZE 0x10000
+#define TL_SIZE 0x1000
+
+enum {
+ TAG_GENERIC_START = 0,
+ TAG_GENERIC_END = 0x7fffff,
+ TAG_NON_STANDARD_START = 0xfff000,
+ TAG_NON_STANDARD_END = 0xffffff,
+ TAG_OUT_OF_BOUND = (1 << 24),
+};
+
+const unsigned int test_tag = 1;
+int test_data = 0xdeadbeef;
+
+#endif /* TEST_H */
diff --git a/test/transfer_list_setup.c b/test/transfer_list_setup.c
new file mode 100644
index 0000000..d2c6aec
--- /dev/null
+++ b/test/transfer_list_setup.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright The Transfer List Library Contributors
+ *
+ * SPDX-License-Identifier: MIT OR GPL-2.0-or-later
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "test.h"
+#include "transfer_list.h"
+#include "unity.h"
+
+void *buffer = NULL;
+
+void test_init()
+{
+ struct transfer_list_header *tl = buffer;
+
+ /* Attempt to init TL with 0 size */
+ TEST_ASSERT_NULL(transfer_list_init(tl, 0));
+ TEST_ASSERT(transfer_list_check_header(tl) == TL_OPS_NON);
+
+ /* Init TL with an invalid memory address */
+ TEST_ASSERT_NULL(transfer_list_init((void *)1, TL_SIZE));
+ TEST_ASSERT(transfer_list_check_header(tl) == TL_OPS_NON);
+
+ /* Valid memory address and large enough TL */
+ TEST_ASSERT(transfer_list_init(tl, TL_SIZE));
+ TEST_ASSERT(transfer_list_check_header(tl) == TL_OPS_ALL);
+ TEST_ASSERT_NULL(transfer_list_next(tl, NULL));
+}
+
+void test_init_alignment()
+{
+ struct transfer_list_header *tl = buffer;
+ memset(tl, 0, TL_SIZE);
+
+ TEST_ASSERT_NULL(transfer_list_init((void *)tl + 0xff, TL_SIZE));
+ TEST_ASSERT(transfer_list_check_header(tl) == TL_OPS_NON);
+}
+
+void test_relocate()
+{
+ struct transfer_list_header *tl, *new_tl;
+ struct transfer_list_entry *te;
+
+ void *new_buf = malloc(TL_SIZE * 2);
+ unsigned int test_tag = 0x1;
+ int test_payload = 0xdeadbeef;
+
+ tl = buffer;
+ memset(tl, 0, TL_SIZE);
+
+ TEST_ASSERT(transfer_list_check_header(tl) == TL_OPS_NON);
+
+ tl = transfer_list_init(tl, TL_SIZE / 2);
+
+ // Add TE's until we run out of space
+ while ((te = transfer_list_add(tl, test_tag, tl->max_size / 8,
+ &test_payload))) {
+ TEST_ASSERT(te = transfer_list_find(tl, test_tag++));
+ TEST_ASSERT(*(int *)transfer_list_entry_data(te) ==
+ test_payload++);
+ }
+
+ // Relocate the TL and make sure all the information we put in is still there.
+ TEST_ASSERT(
+ new_tl = transfer_list_relocate(tl, new_buf, tl->max_size * 2));
+ TEST_ASSERT(transfer_list_check_header(new_tl) == TL_OPS_ALL);
+
+ unsigned int i = test_tag;
+ while ((te = transfer_list_find(tl, --i))) {
+ int *data = transfer_list_entry_data(te);
+ TEST_ASSERT_NOT_NULL(data);
+ TEST_ASSERT(*data == --test_payload);
+ }
+
+ // Add the last TE we failed to add into the relocated TL.
+ TEST_ASSERT(te = transfer_list_add(new_tl, test_tag, TL_SIZE / 8,
+ &test_payload));
+ TEST_ASSERT(*(int *)transfer_list_find(new_tl, test_tag) =
+ ++test_payload);
+}
+
+void test_bad_reloc()
+{
+ struct transfer_list_header *tl = transfer_list_init(buffer, TL_SIZE);
+ void *new_buf = malloc(TL_SIZE);
+
+ /* Relocate to invalid memory address. */
+ TEST_ASSERT_NULL(transfer_list_relocate(tl, 0, tl->max_size));
+
+ /*
+ * Try relocate to area with insufficent memory, check at the end that we
+ * still have a valid TL in the original location.
+ */
+ TEST_ASSERT_NULL(transfer_list_relocate(tl, new_buf, 0));
+ TEST_ASSERT(transfer_list_check_header(tl) == TL_OPS_ALL);
+}
+
+void setUp(void)
+{
+ buffer = malloc(TL_MAX_SIZE);
+}
+
+void tearDown(void)
+{
+ free(buffer);
+ buffer = NULL;
+}
+
+int main(void)
+{
+ UNITY_BEGIN();
+ RUN_TEST(test_bad_reloc);
+ RUN_TEST(test_init_alignment);
+ RUN_TEST(test_init);
+ RUN_TEST(test_relocate);
+ return UNITY_END();
+}
diff --git a/toolchains/aarch64/common_aarch64.cmake b/toolchains/aarch64/common_aarch64.cmake
new file mode 100644
index 0000000..99975d0
--- /dev/null
+++ b/toolchains/aarch64/common_aarch64.cmake
@@ -0,0 +1,42 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright LibTL Contributors.
+#
+
+include_guard()
+include(CheckCCompilerFlag)
+
+include(${CMAKE_CURRENT_LIST_DIR}/../common.cmake)
+
+set(CMAKE_SYSTEM_NAME "Generic")
+set(CMAKE_SYSTEM_PROCESSOR aarch64)
+
+add_compile_options(
+ -ffreestanding
+ -mbranch-protection=standard
+ -mgeneral-regs-only
+ -mstrict-align
+ -fpie)
+
+add_link_options(
+ "$<$<CONFIG:Debug>:-fno-omit-frame-pointer>"
+ "$<$<CONFIG:Relase>:-fomit-frame-pointer>")
+
+add_link_options(
+ -nostdlib
+ -Wl,-pie)
+
+# Detect applicable "march=" option supported by compiler
+function(detect_and_set_march)
+ set (march_list 9.2 9.1 9 8.8 8.7 8.6 8.5)
+
+ foreach(v ${march_list})
+ string(REPLACE "." "_" n ${v})
+ check_c_compiler_flag("-march=armv${v}-a" COMPILER_SUPPORTS_${n})
+ if(COMPILER_SUPPORTS_${n})
+ add_compile_options("-march=armv${v}-a")
+ return()
+ endif()
+ endforeach()
+ message(FATAL_ERROR "Suitable -march not detected. Please upgrade aarch64 compiler." )
+endfunction()
diff --git a/toolchains/aarch64/gnu.cmake b/toolchains/aarch64/gnu.cmake
new file mode 100644
index 0000000..a6e18f0
--- /dev/null
+++ b/toolchains/aarch64/gnu.cmake
@@ -0,0 +1,18 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright LibTL Contributors.
+#
+
+include_guard()
+
+include(${CMAKE_CURRENT_LIST_DIR}/common_aarch64.cmake)
+
+find_program(CMAKE_ASM_COMPILER
+ NAMES "$ENV{CROSS_COMPILE}gcc"
+ DOC "Path to an aarch64 compiler."
+ REQUIRED)
+
+find_program(CMAKE_C_COMPILER
+ NAMES "$ENV{CROSS_COMPILE}gcc"
+ DOC "Path to an aarch64 compiler."
+ REQUIRED)
diff --git a/toolchains/common.cmake b/toolchains/common.cmake
new file mode 100644
index 0000000..552a8c8
--- /dev/null
+++ b/toolchains/common.cmake
@@ -0,0 +1,27 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright LibTL Contributors.
+#
+
+include_guard()
+
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
+
+add_compile_options(
+ "-fno-common"
+ "-ffunction-sections"
+ "-fdata-sections"
+ "-Wall"
+ "-Werror"
+ "-gdwarf-4"
+ "$<$<CONFIG:Debug>:-Og>"
+ "$<$<CONFIG:Release>:-g>"
+ )
+
+add_link_options(
+ "-Wl,--gc-sections"
+ "-g"
+ )
diff --git a/toolchains/host/common_host.cmake b/toolchains/host/common_host.cmake
new file mode 100644
index 0000000..9e661d7
--- /dev/null
+++ b/toolchains/host/common_host.cmake
@@ -0,0 +1,16 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright LibTL Contributors.
+#
+
+include_guard()
+
+include(${CMAKE_CURRENT_LIST_DIR}/../common.cmake)
+
+foreach(language IN ITEMS ASM C CXX)
+ string(APPEND CMAKE_${language}_FLAGS_INIT "-fno-omit-frame-pointer -pg ")
+endforeach()
+
+# 'march=" option is not applicable for fake_host
+function(detect_and_set_march)
+endfunction()
diff --git a/toolchains/host/gnu.cmake b/toolchains/host/gnu.cmake
new file mode 100644
index 0000000..f470d1b
--- /dev/null
+++ b/toolchains/host/gnu.cmake
@@ -0,0 +1,17 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright LibTL Contributors.
+#
+
+include_guard()
+
+include(${CMAKE_CURRENT_LIST_DIR}/common_host.cmake)
+
+find_program(CMAKE_C_COMPILER
+ NAMES "gcc"
+ DOC "Path to gcc."
+ REQUIRED)
+
+set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
+
+string(APPEND CMAKE_EXE_LINKER_FLAGS_INIT "-Wl,--build-id=none ")
diff --git a/toolchains/host/llvm.cmake b/toolchains/host/llvm.cmake
new file mode 100644
index 0000000..d9da42a
--- /dev/null
+++ b/toolchains/host/llvm.cmake
@@ -0,0 +1,29 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright LibTL Contributors.
+#
+
+include_guard()
+
+include(${CMAKE_CURRENT_LIST_DIR}/common_host.cmake)
+
+find_program(CMAKE_C_COMPILER
+ NAMES "clang"
+ DOC "Path to clang."
+ REQUIRED)
+
+find_program(CMAKE_CXX_COMPILER
+ NAMES "clang++"
+ DOC "Path to clang++."
+ REQUIRED)
+
+set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
+
+foreach(language IN ITEMS ASM C CXX)
+ string(APPEND CMAKE_${language}_FLAGS_INIT "-Wno-unknown-warning-option ")
+ string(APPEND CMAKE_${language}_FLAGS_INIT "-Wno-unused-function ")
+ string(APPEND CMAKE_${language}_FLAGS_INIT "-fPIC ")
+endforeach()
+
+string(APPEND CMAKE_EXE_LINKER_FLAGS_INIT "-Wl,--build-id=none ")
+string(APPEND CMAKE_EXE_LINKER_FLAGS_INIT "-fuse-ld=lld ")