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)&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 (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 ")