TF-RMM Release v0.1.0

This is the first external release of TF-RMM and provides a reference
implementation of Realm Management Monitor (RMM) as specified by the
RMM Beta0 specification[1].

The `docs/readme.rst` has more details about the project and
`docs/getting_started/getting-started.rst` has details on how to get
started with TF-RMM.

[1] https://developer.arm.com/documentation/den0137/1-0bet0/?lang=en

Signed-off-by: Soby Mathew <soby.mathew@arm.com>
Change-Id: I205ef14c015e4a37ae9ae1a64e4cd22eb8da746e
diff --git a/.checkpatch.conf b/.checkpatch.conf
new file mode 100644
index 0000000..216996c
--- /dev/null
+++ b/.checkpatch.conf
@@ -0,0 +1,81 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+#
+# Configure how the Linux checkpatch script should be invoked in the context of
+# the RMM source tree.
+#
+
+# This is not Linux so don't expect a Linux tree!
+--no-tree
+
+# The Linux kernel expects the SPDX license tag in the first line of each file.
+# We don't follow this yet in the RMM.
+# TODO: Temporarily ignore SPDX license tag
+--ignore SPDX_LICENSE_TAG
+
+# This clarifes the lines indications in the report.
+#
+# E.g.:
+# Without this option, we have the following output:
+#      #333: FILE: drivers/pl011/src/pl011.c:42:
+# So we have 2 lines indications (333 and 42), which is confusing.
+# We only care about the position in the source file.
+#
+# With this option, it becomes:
+#      drivers/pl011/src/pl011.c:42:
+--showfile
+
+# Don't show some messages like the list of ignored types or the suggestion to
+# use "--fix" or report changes to the maintainers.
+--quiet
+
+#
+# Ignore the following message types, as they don't necessarily make sense in
+# the context of the RMM.
+#
+
+# COMPLEX_MACRO generates false positives.
+--ignore COMPLEX_MACRO
+
+# Commit messages might contain a Gerrit Change-Id.
+--ignore GERRIT_CHANGE_ID
+
+# Do not check the format of commit messages, as Gerrit's merge commits do not
+# preserve it.
+--ignore GIT_COMMIT_ID
+
+# FILE_PATH_CHANGES reports this kind of message:
+# "added, moved or deleted file(s), does MAINTAINERS need updating?"
+# We do not use this MAINTAINERS file process in RMM.
+--ignore FILE_PATH_CHANGES
+
+# NEW_TYPEDEFS reports this kind of messages:
+# "do not add new typedefs"
+# We allow adding new typedefs in RMM.
+--ignore NEW_TYPEDEFS
+
+# Avoid "Does not appear to be a unified-diff format patch" message
+--ignore NOT_UNIFIED_DIFF
+
+# PREFER_KERNEL_TYPES reports this kind of messages (when using --strict):
+# "Prefer kernel type 'u32' over 'uint32_t'"
+--ignore PREFER_KERNEL_TYPES
+
+# COMPARISON_TO_NULL reports this kind of messages (when using --strict):
+# Comparison to NULL could be written ""
+--ignore COMPARISON_TO_NULL
+
+# UNNECESSARY_PARENTHESES reports this kind of messages (when using --strict):
+# Unnecessary parentheses around ""
+--ignore UNNECESSARY_PARENTHESES
+
+# RMM build uses __DATE__ and __TIME__ macros to print build timestamp
+# Ignore ERROR like:
+# "Use of the '__DATE__' macro makes the build non-deterministic"
+--ignore DATE_TIME
+
+# Do not allow C99 // comments
+--ignore C99_COMMENT_TOLERANCE
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..abbfa14
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,12 @@
+build
+*.cscope
+cscope.*
+.tags
+tags
+*.dump
+*-info
+*-list
+out
+*~
+*.patch
+*.swp
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..b994ee1
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,10 @@
+[submodule "mbedtls"]
+	path = ext/mbedtls
+	url = https://github.com/ARMmbed/mbedtls.git
+	ignore = dirty
+[submodule "ext/qcbor"]
+	path = ext/qcbor
+	url = https://github.com/laurencelundblade/QCBOR.git
+[submodule "ext/t_cose"]
+	path = ext/t_cose
+	url = https://github.com/matetothpal/t_cose.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..8bf52df
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,198 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+cmake_minimum_required(VERSION 3.15.0)
+
+# allow target_link_libraries() to be used with targets in other directories
+cmake_policy(SET CMP0079 NEW)
+
+#
+# Add our module search paths so we can `include()` our CMake modules.
+#
+
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/Modules")
+
+#
+# Include any dependencies.
+#
+
+include(ArmConfigOption)
+include(ArmConfigOptionOverride)
+
+#
+# Run preliminary setup scripts.
+#
+set(RMM_CONFIG_FILE "${CMAKE_SOURCE_DIR}/configs/${RMM_CONFIG}.cmake")
+if(NOT EXISTS ${RMM_CONFIG_FILE})
+    message(FATAL_ERROR "Please provide config ${RMM_CONFIG_FILE}")
+endif()
+
+include("${RMM_CONFIG_FILE}")
+
+#
+# Set the target build Architecture before we proceed further.
+# Default is aarch64.
+#
+arm_config_option(
+        NAME RMM_ARCH
+        HELP "Target Architecture for RMM build."
+        STRINGS "aarch64" "fake_host")
+
+include("cmake/Toolchains.cmake")
+include("cmake/BuildType.cmake")
+
+#
+# Initialize the project. Note that this is where the toolchain file is loaded,
+# and also where the project directory and version variables are set up.
+#
+
+project(RMM VERSION 0.1.0 LANGUAGES ASM C)
+
+#
+# Set global flags.
+#
+
+set(CMAKE_C_STANDARD 11)
+set(CMAKE_C_STANDARD_REQUIRED TRUE)
+set(CMAKE_C_EXTENSIONS TRUE)
+
+if(RMM_STATIC_ANALYSIS_CPPCHECK)
+    set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+endif()
+#
+# Include the platform makefile
+#
+include("cmake/Platforms.cmake")
+
+#
+# Include the common configuration options
+#
+include("cmake/CommonConfigs.cmake")
+
+#
+# Load in our C standard library and link it to any targets created after this
+# point. This will automatically transition these targets away from the standard
+# library provided by the toolchain, and towards our libc.
+#
+
+add_subdirectory("lib/libc")
+
+link_libraries(rmm-lib-libc)
+
+#
+# Build the MbedTLS we package in the source tree, and import the targets from
+# it so that we can model the library dependency properly.
+#
+
+include("cmake/BuildMbedTLS.cmake")
+
+set(MbedTLS_ROOT "${MbedTLS_INSTALL_DIR}")
+find_package(MbedTLS COMPONENTS Crypto REQUIRED)
+
+target_link_libraries(MbedTLS
+    INTERFACE rmm-lib-libc)
+
+#
+# Build and link QCBOR
+#
+include("cmake/BuildQCBOR.cmake")
+
+#
+# Recurse into the various component subdirectories
+#
+add_subdirectory("lib")
+add_subdirectory("runtime")
+
+if(RMM_DOCS)
+    add_subdirectory("docs")
+endif()
+
+#
+# Create the flat binary using whatever tool comes with the toolchain.
+#
+
+if(CMAKE_OBJCOPY)
+    add_custom_command(
+        COMMAND "${CMAKE_OBJCOPY}" -O binary "$<TARGET_FILE:rmm-runtime>" rmm.img
+        OUTPUT rmm.img
+        DEPENDS rmm-runtime)
+endif()
+
+#
+# Create the dump file using whatever tool comes with the toolchain.
+#
+
+if(CMAKE_OBJDUMP)
+    add_custom_command(
+        COMMAND "${CMAKE_OBJDUMP}" -dx "$<TARGET_FILE:rmm-runtime>" > rmm.dump
+        OUTPUT rmm.dump
+        DEPENDS rmm-runtime)
+endif()
+
+#
+# Copy 'rmm-runtime' executable to 'build\rmm.elf'.
+#
+
+add_custom_command(
+    COMMAND "${CMAKE_COMMAND}" -E copy_if_different "$<TARGET_FILE:rmm-runtime>" rmm.elf
+    OUTPUT rmm.elf
+    DEPENDS rmm-runtime)
+
+#
+# Copy 'rmm-runtime.map' to 'build\rmm.map'
+#
+
+add_custom_command(
+    COMMAND "${CMAKE_COMMAND}" -E copy_if_different "$<TARGET_FILE:rmm-runtime>.map" rmm.map
+    OUTPUT rmm.map
+    DEPENDS rmm-runtime)
+
+add_custom_target(rmm ALL DEPENDS rmm.img rmm.dump rmm.elf rmm.map)
+
+#
+# Set up additional tooling.
+#
+
+add_subdirectory("tools")
+
+#
+# Rules for checkpatch
+#
+
+add_custom_target(checkcodebase
+  WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
+  COMMAND ${CMAKE_COMMAND} -DCHECKCODEBASE_RUN=1 -P ${CMAKE_SOURCE_DIR}/tools/checkpatch/CheckPatch.cmake
+  )
+
+add_custom_target(checkpatch
+  WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
+  COMMAND ${CMAKE_COMMAND} -DCHECKPATCH_RUN=1 -P ${CMAKE_SOURCE_DIR}/tools/checkpatch/CheckPatch.cmake
+  )
+
+#
+# Rules for checking license and copyright headers
+#
+add_custom_target(checkspdx-codebase
+  WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
+  COMMAND ${CMAKE_COMMAND} -DCHECKSPDX_CODEBASE=1 -P ${CMAKE_SOURCE_DIR}/tools/checkspdx/CheckSPDX.cmake
+  )
+
+add_custom_target(checkspdx-patch
+  WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
+  COMMAND ${CMAKE_COMMAND} -DCHECKSPDX_PATCH=1 -P ${CMAKE_SOURCE_DIR}/tools/checkspdx/CheckSPDX.cmake
+  )
+
+#
+# Rules for checking header files include order
+#
+add_custom_target(checkincludes-codebase
+  WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
+  COMMAND ${CMAKE_COMMAND} -DCHECKINCLUDES_CODEBASE=1 -P ${CMAKE_SOURCE_DIR}/tools/checkincludes/CheckIncludes.cmake
+  )
+
+add_custom_target(checkincludes-patch
+  WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
+  COMMAND ${CMAKE_COMMAND} -DCHECKINCLUDES_PATCH=1 -P ${CMAKE_SOURCE_DIR}/tools/checkincludes/CheckIncludes.cmake
+  )
diff --git a/DCO b/DCO
new file mode 100644
index 0000000..8201f99
--- /dev/null
+++ b/DCO
@@ -0,0 +1,37 @@
+Developer Certificate of Origin
+Version 1.1
+
+Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
+1 Letterman Drive
+Suite D4700
+San Francisco, CA, 94129
+
+Everyone is permitted to copy and distribute verbatim copies of this
+license document, but changing it is not allowed.
+
+
+Developer's Certificate of Origin 1.1
+
+By making a contribution to this project, I certify that:
+
+(a) The contribution was created in whole or in part by me and I
+    have the right to submit it under the open source license
+    indicated in the file; or
+
+(b) The contribution is based upon previous work that, to the best
+    of my knowledge, is covered under an appropriate open source
+    license and I have the right under that license to submit that
+    work with modifications, whether created in whole or in part
+    by me, under the same open source license (unless I am
+    permitted to submit under a different license), as indicated
+    in the file; or
+
+(c) The contribution was provided directly to me by some other
+    person who certified (a), (b) or (c) and I have not modified
+    it.
+
+(d) I understand and agree that this project and the contribution
+    are public and that a record of the contribution (including all
+    personal information I submit with it, including my sign-off) is
+    maintained indefinitely and may be redistributed consistent with
+    this project or the open source license(s) involved.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..bb45797
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright TF-RMM Contributors
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/cmake/BuildMbedTLS.cmake b/cmake/BuildMbedTLS.cmake
new file mode 100644
index 0000000..8da145c
--- /dev/null
+++ b/cmake/BuildMbedTLS.cmake
@@ -0,0 +1,210 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+find_package(Python3 COMPONENTS Interpreter REQUIRED)
+
+#
+# Set up any necessary Mbed TLS configuration options. In the case of an
+# inability to build the project with the current set of tools, we allow the
+# user to specify an alternative toolchain and generator.
+#
+
+arm_config_option(
+    NAME MbedTLS_TOOLCHAIN_FILE
+    HELP "MbedTLS toolchain file."
+    DEFAULT "${CMAKE_TOOLCHAIN_FILE}"
+    TYPE FILEPATH
+    ADVANCED)
+
+arm_config_option(
+    NAME MbedTLS_GENERATOR
+    HELP "MbedTLS CMake generator."
+    DEFAULT "${CMAKE_GENERATOR}"
+    TYPE STRING
+    ADVANCED)
+
+arm_config_option(
+    NAME MbedTLS_GENERATOR_PLATFORM
+    HELP "MbedTLS CMake generator platform."
+    DEFAULT "${CMAKE_GENERATOR_PLATFORM}"
+    TYPE STRING
+    ADVANCED)
+
+arm_config_option(
+    NAME MbedTLS_GENERATOR_TOOLSET
+    HELP "MbedTLS CMake generator toolset."
+    DEFAULT "${CMAKE_GENERATOR_TOOLSET}"
+    TYPE STRING
+    ADVANCED)
+
+# Set up MbedTLS build type. Default is "Release".
+arm_config_option(
+    NAME MbedTLS_BUILD_TYPE
+    HELP "MbedTLS build type."
+    STRINGS "Release" "Debug")
+
+message(STATUS "MbedTLS_BUILD_TYPE set to ${MbedTLS_BUILD_TYPE}")
+
+#
+# Set up MbedTLS using our own libc. This is done by getting certain properties
+# from our libc target and applying them to MbedTLS - not ideal, but MbedTLS
+# doesn't provide an interface library for us to use.
+#
+
+get_target_property(system_includes rmm-lib-libc
+    INTERFACE_SYSTEM_INCLUDE_DIRECTORIES)
+get_target_property(includes rmm-lib-libc
+    INTERFACE_INCLUDE_DIRECTORIES)
+get_target_property(definitions rmm-lib-libc
+    INTERFACE_COMPILE_DEFINITIONS)
+get_target_property(options rmm-lib-libc
+    INTERFACE_COMPILE_OPTIONS)
+
+#
+# System include directories appear in both the `SYSTEM_INCLUDE_DIRECTORIES` and
+# `INCLUDE_DIRECTORIES` properties, so filter them out of the latter.
+#
+
+list(REMOVE_ITEM includes ${system_includes})
+
+#
+# Target properties that are not set return `<PROPERTY>-NOTFOUND`. Clear any
+# variables where this occurred.
+#
+
+foreach(set IN ITEMS system_includes includes definitions options)
+    if(NOT ${set})
+        set(${set})
+    endif()
+endforeach()
+
+#
+# Add MbedTLS config header file
+#
+
+list(APPEND includes "${RMM_SOURCE_DIR}/configs/mbedtls")
+list(APPEND definitions "MBEDTLS_CONFIG_FILE=\"<mbedtls_config.h>\"")
+
+#
+# Create compiler flags from the libc properties we retrieved.
+#
+
+list(TRANSFORM system_includes PREPEND "-isystem ")
+list(TRANSFORM includes PREPEND "-I ")
+list(TRANSFORM definitions PREPEND "-D ")
+
+foreach(set IN ITEMS system_includes includes definitions options)
+    string(REPLACE ";" " " ${set} "${${set}}")
+endforeach()
+
+string(PREPEND MbedTLS_C_FLAGS "${definitions} ")
+string(PREPEND MbedTLS_C_FLAGS "${system_includes} ")
+string(PREPEND MbedTLS_C_FLAGS "${includes} ")
+string(PREPEND MbedTLS_C_FLAGS "${options} ")
+
+#
+# Unmark some of these configuration options as advanced, in case building fails
+# in the next step and we want to re-expose them to the user.
+#
+
+mark_as_advanced(CLEAR MbedTLS_TOOLCHAIN_FILE)
+mark_as_advanced(CLEAR MbedTLS_GENERATOR)
+mark_as_advanced(CLEAR MbedTLS_GENERATOR_PLATFORM)
+mark_as_advanced(CLEAR MbedTLS_GENERATOR_TOOLSET)
+mark_as_advanced(CLEAR MbedTLS_C_FLAGS)
+
+#
+# Set up the source, binary and install directories.
+#
+
+set(MbedTLS_SOURCE_DIR "${RMM_SOURCE_DIR}/ext/mbedtls")
+set(MbedTLS_INSTALL_DIR "${RMM_BINARY_DIR}/ext/mbedtls")
+set(MbedTLS_BINARY_DIR "${MbedTLS_INSTALL_DIR}${CMAKE_FILES_DIRECTORY}")
+
+#
+# Detect whether we need to reconfigure the CMake cache from the very start,
+# which we need to do if the user has modified certain one-shot options.
+#
+
+if(MbedTLS_TOOLCHAIN_FILE_CHANGED OR
+   MbedTLS_BUILD_TYPE_CHANGED OR
+   MbedTLS_GENERATOR_CHANGED OR
+   MbedTLS_GENERATOR_PLATFORM_CHANGED OR
+   MbedTLS_GENERATOR_TOOLSET_CHANGED)
+    file(REMOVE "${MbedTLS_BINARY_DIR}/CMakeCache.txt")
+endif()
+
+#
+# Configure, build and install the CMake project.
+#
+
+unset(mbedtls_configure)
+
+list(APPEND mbedtls_configure -G "${MbedTLS_GENERATOR}")
+list(APPEND mbedtls_configure -S "${MbedTLS_SOURCE_DIR}")
+list(APPEND mbedtls_configure -B "${MbedTLS_BINARY_DIR}")
+
+if(MbedTLS_GENERATOR_PLATFORM)
+    list(APPEND mbedtls_configure -A "${MbedTLS_GENERATOR_PLATFORM}")
+endif()
+
+if(MbedTLS_GENERATOR_TOOLSET)
+    list(APPEND mbedtls_configure -T "${MbedTLS_GENERATOR_TOOLSET}")
+endif()
+
+list(APPEND mbedtls_configure -D "CMAKE_INSTALL_PREFIX=${MbedTLS_INSTALL_DIR}")
+list(APPEND mbedtls_configure -D "CMAKE_TOOLCHAIN_FILE=${MbedTLS_TOOLCHAIN_FILE}")
+list(APPEND mbedtls_configure -D "ENABLE_PROGRAMS=NO")
+list(APPEND mbedtls_configure -D "ENABLE_TESTING=NO")
+list(APPEND mbedtls_configure -D "CMAKE_C_COMPILER_WORKS=1")
+if(CMAKE_VERBOSE_MAKEFILE)
+    list(APPEND mbedtls_configure -D "CMAKE_VERBOSE_MAKEFILE=1")
+endif()
+
+#
+# Mbed TLS's build system ignores and overwrites the flags we specify in our
+# toolchain files. Un-overwrite them, because they're there for a good reason.
+#
+
+string(TOUPPER ${MbedTLS_BUILD_TYPE} build_type)
+
+if(RMM_FPU_USE_AT_REL2)
+    # Enable using floating point registers for mbed TLS
+    string(REPLACE "-mgeneral-regs-only" "" FP_CMAKE_C_FLAGS ${CMAKE_C_FLAGS})
+    # Enable using crypto and sha instructions
+    string(REGEX REPLACE "(march=[^\\ ]*)" "\\1+sha3+crypto" FP_CMAKE_C_FLAGS ${FP_CMAKE_C_FLAGS})
+    # Enable using SHA256 and SHA512 instructions in MbedTLS
+    string(APPEND FP_CMAKE_C_FLAGS
+            " -DMBEDTLS_SHA256_USE_A64_CRYPTO_ONLY=1 "
+            " -DMBEDTLS_SHA512_USE_A64_CRYPTO_ONLY=1 ")
+else()
+    set(FP_CMAKE_C_FLAGS ${CMAKE_C_FLAGS})
+endif()
+
+list(APPEND mbedtls_configure
+    -D "CMAKE_C_FLAGS=${FP_CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${build_type}} ${MbedTLS_C_FLAGS}")
+
+execute_process(
+    COMMAND "${CMAKE_COMMAND}"
+        ${mbedtls_configure})
+
+execute_process(
+    COMMAND "${CMAKE_COMMAND}"
+        --build "${MbedTLS_BINARY_DIR}"
+        --config "${MbedTLS_BUILD_TYPE}")
+
+execute_process(
+    COMMAND "${CMAKE_COMMAND}"
+        --install "${MbedTLS_BINARY_DIR}"
+        --config "${MbedTLS_BUILD_TYPE}")
+
+#
+# Mark some of the configuration options as advanced if building succeeded.
+#
+
+mark_as_advanced(FORCE MbedTLS_TOOLCHAIN_FILE)
+mark_as_advanced(FORCE MbedTLS_GENERATOR)
+mark_as_advanced(FORCE MbedTLS_GENERATOR_PLATFORM)
+mark_as_advanced(FORCE MbedTLS_GENERATOR_TOOLSET)
diff --git a/cmake/BuildQCBOR.cmake b/cmake/BuildQCBOR.cmake
new file mode 100644
index 0000000..f66683c
--- /dev/null
+++ b/cmake/BuildQCBOR.cmake
@@ -0,0 +1,82 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+add_subdirectory("ext/qcbor")
+
+#
+# Set up qcbor using our own libc. This is done by getting certain properties
+# from our libc target and applying them to qcbor - not ideal, but qcbor
+# doesn't provide an interface library for us to use.
+#
+get_target_property(system_includes rmm-lib-libc
+    INTERFACE_SYSTEM_INCLUDE_DIRECTORIES)
+get_target_property(includes rmm-lib-libc
+    INTERFACE_INCLUDE_DIRECTORIES)
+get_target_property(definitions rmm-lib-libc
+    INTERFACE_COMPILE_DEFINITIONS)
+get_target_property(options rmm-lib-libc
+    INTERFACE_COMPILE_OPTIONS)
+
+#
+# System include directories appear in both the `SYSTEM_INCLUDE_DIRECTORIES` and
+# `INCLUDE_DIRECTORIES` properties, so filter them out of the latter.
+#
+list(REMOVE_ITEM includes ${system_includes})
+
+#
+# Target properties that are not set return `<PROPERTY>-NOTFOUND`. Clear any
+# variables where this occurred.
+#
+foreach(set IN ITEMS system_includes includes definitions options)
+    if(NOT ${set})
+        set(${set})
+    endif()
+endforeach()
+
+# QCBOR custom definitions
+list(APPEND definitions "QCBOR_DISABLE_FLOAT_HW_USE")
+list(APPEND definitions "USEFULBUF_DISABLE_ALL_FLOAT")
+
+#
+# Create compiler flags from the libc properties we retrieved.
+#
+list(TRANSFORM definitions PREPEND " -D")
+
+foreach(set IN ITEMS options definitions)
+    string(REPLACE ";" " " ${set} "${${set}}")
+endforeach()
+
+string(PREPEND qcbor_C_FLAGS "${definitions} ")
+# Add the relevant build flags. TODO: Currently CBOR is only passed Release build flag
+string(PREPEND qcbor_C_FLAGS "${options} ${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_RELEASE} ")
+string(PREPEND qcbor_C_FLAGS "-Wno-maybe-uninitialized ")
+
+string(REPLACE " " ";" qcbor_C_FLAGS ${qcbor_C_FLAGS})
+
+#
+# qcbor's build system ignores and overwrites the flags we specify in our
+# toolchain files. Un-overwrite them, because they're there for a good reason.
+#
+target_include_directories(qcbor
+    PUBLIC "${RMM_SOURCE_DIR}/ext/qcbor/inc"
+)
+
+target_include_directories(qcbor
+    PRIVATE
+        ${includes}
+        ${system_includes}
+)
+
+target_compile_options(qcbor
+    PRIVATE
+        ${qcbor_C_FLAGS}
+)
+
+target_link_libraries(qcbor
+	PRIVATE
+	rmm-lib-libc
+)
+
+link_libraries(qcbor)
diff --git a/cmake/BuildType.cmake b/cmake/BuildType.cmake
new file mode 100644
index 0000000..59d1d8d
--- /dev/null
+++ b/cmake/BuildType.cmake
@@ -0,0 +1,35 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+#
+# Set up the configuration types for both single and multi-configuration
+# generators.
+#
+
+set(config_types "Debug" "Release")
+set(default_config "Release")
+
+get_property(RMM_MULTI_CONFIG GLOBAL PROPERTY "GENERATOR_IS_MULTI_CONFIG")
+
+if(RMM_MULTI_CONFIG)
+    arm_config_option(
+        NAME CMAKE_CONFIGURATION_TYPES
+        HELP "Multi-generator configuration types."
+        DEFAULT "${config_types}"
+        TYPE INTERNAL)
+
+    arm_config_option(
+        NAME CMAKE_DEFAULT_BUILD_TYPE
+        HELP "Default multi-generator configuration type."
+        DEFAULT "${default_config}"
+        TYPE INTERNAL)
+else()
+    arm_config_option(
+        NAME CMAKE_BUILD_TYPE
+        HELP "Build type."
+        STRINGS ${config_types}
+        DEFAULT ${default_config}
+        FORCE NOT CMAKE_BUILD_TYPE)
+endif()
diff --git a/cmake/CommonConfigs.cmake b/cmake/CommonConfigs.cmake
new file mode 100644
index 0000000..96dc650
--- /dev/null
+++ b/cmake/CommonConfigs.cmake
@@ -0,0 +1,93 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+#
+# Common config options
+#
+arm_config_option(
+    NAME MAX_CPUS
+    HELP "Maximum number of CPUs supported by RMM"
+    TYPE STRING
+    DEFAULT 16)
+
+#
+# The RMM is mapped with 4K pages, and all RMM APIs use the same granularity.
+#
+arm_config_option(
+    NAME GRANULE_SIZE
+    HELP "Granule Size used by RMM"
+    TYPE STRING
+    DEFAULT 4096)
+
+arm_config_option(
+    NAME RMM_DOCS
+    HELP "RMM Documentation build"
+    TYPE BOOL
+    DEFAULT OFF)
+
+# TODO: Move to lib/arch once MbedTLS compilation is moved to build phase.
+arm_config_option(
+    NAME RMM_FPU_USE_AT_REL2
+    HELP "Enable Advanced SIMD support in RMM"
+    TYPE BOOL
+    DEFAULT ON
+    DEPENDS (RMM_ARCH STREQUAL aarch64)
+    ELSE OFF)
+
+#
+# Introduce a pseudo-library purely for applying flags to RMM's libraries.
+# This is applied to any targets created after this point.
+#
+
+add_library(rmm-common INTERFACE)
+
+target_compile_definitions(rmm-common
+    INTERFACE "$<$<CONFIG:Debug>:DEBUG>")
+
+if(MAX_CPUS EQUAL 0x0)
+    message(FATAL_ERROR "MAX_CPUS is not initialized")
+endif()
+
+target_compile_definitions(rmm-common
+    INTERFACE "MAX_CPUS=U(${MAX_CPUS})")
+
+if(NOT(GRANULE_SIZE EQUAL 4096))
+    message(FATAL_ERROR "GRANULE_SIZE is not initialized correctly")
+endif()
+
+target_compile_definitions(rmm-common
+    INTERFACE "GRANULE_SIZE=U(${GRANULE_SIZE})")
+
+if(RMM_FPU_USE_AT_REL2)
+    target_compile_definitions(rmm-common
+        INTERFACE "RMM_FPU_USE_AT_REL2=1")
+endif()
+
+#
+# Project name and version
+#
+target_compile_definitions(rmm-common
+    INTERFACE "NAME=\"${PROJECT_NAME}\"")
+
+target_compile_definitions(rmm-common
+    INTERFACE "VERSION=\"${PROJECT_VERSION}\"")
+
+#
+# Get git commit information
+#
+find_package(Git)
+if(GIT_FOUND)
+  execute_process(
+    COMMAND ${GIT_EXECUTABLE} describe --always --dirty --tags
+    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+    OUTPUT_VARIABLE COMMIT_INFO
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+  )
+endif()
+
+target_compile_definitions(rmm-common
+    INTERFACE "COMMIT_INFO=\"${COMMIT_INFO}\"")
+
+link_libraries(rmm-common)
diff --git a/cmake/Modules/ArmConfigOption.cmake b/cmake/Modules/ArmConfigOption.cmake
new file mode 100644
index 0000000..07b80c7
--- /dev/null
+++ b/cmake/Modules/ArmConfigOption.cmake
@@ -0,0 +1,449 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+#[=======================================================================[.rst:
+ArmConfigOption
+---------------
+
+.. default-domain:: cmake
+
+.. command:: arm_config_option
+
+Create a configuration option with more flexibility than offered by
+:module:`cmake_dependent_option() <module:CMakeDependentOption>`.
+
+.. code:: cmake
+
+    arm_config_option(NAME <name> HELP <help> [TYPE <type>] [DEFAULT <default>]
+                      [[STRINGS <strings>...] [FREEFORM]] [ADVANCED]
+                      [[DEPENDS <depends>] [ELSE <else>]] [FORCE <force>])
+
+This helper function is intended to simplify some of the complex mechanics
+involved in creating a robust, scalable configuration system for medium to
+large projects. It incorporates basic dependency resolution, overridable default
+values, and stronger typing in order to provide a smoother experience for both
+build system developers and users.
+
+Basics
+^^^^^^
+
+Every configuration option has one of the following types:
+
+- ``BOOL`` for booleans (shows as a toggle option)
+- ``STRING`` for strings (shows as a text box)
+- ``PATH`` for directory paths (shows as a directory chooser)
+- ``FILEPATH`` for file paths (shows as a file chooser)
+
+These are the types supported by the :prop_cache:`TYPE <prop_cache:TYPE>` cache
+entry property. It's important to choose the right type for the option in order
+to provide a consistent user experience.
+
+By default, any configuration option that does not specify a type (by providing
+a value to the ``TYPE`` argument) is ``BOOL``, unless the ``STRINGS`` argument
+has been provided. For example:
+
+.. code:: cmake
+
+    arm_config_option(NAME XYZ ... TYPE BOOL) # BOOL
+    arm_config_option(NAME XYZ ... TYPE STRING) # STRING
+    arm_config_option(NAME XYZ ... TYPE PATH) # PATH
+    arm_config_option(NAME XYZ ... TYPE FILEPATH) # FILEPATH
+
+    arm_config_option(NAME XYZ ...) # BOOL
+    arm_config_option(NAME XYZ ... STRINGS ...) # STRING
+
+Likewise, every configuration option has a (default) default value, dependent on
+its type:
+
+.. code:: cmake
+
+    arm_config_option(NAME XYZ ... TYPE BOOL) # FALSE
+    arm_config_option(NAME XYZ ... TYPE STRING) # ""
+    arm_config_option(NAME XYZ ... TYPE PATH) # ""
+    arm_config_option(NAME XYZ ... TYPE FILEPATH) # ""
+
+    arm_config_option(NAME XYZ ...) # FALSE
+    arm_config_option(NAME XYZ ... STRINGS X Y Z) # "X"
+
+Note that the default value of configuration options with a ``STRINGS`` list
+will use the first element of the list as the default.
+
+The default value can be overridden by providing a value to the ``DEFAULT``
+argument:
+
+.. code:: cmake
+
+    arm_config_option(NAME XYZ ... TYPE BOOL DEFAULT TRUE) # TRUE
+    arm_config_option(NAME XYZ ... TYPE STRING DEFAULT "x") # "x"
+    arm_config_option(NAME XYZ ... TYPE PATH DEFAULT "./x") # "./x"
+    arm_config_option(NAME XYZ ... TYPE FILEPATH DEFAULT "./x.txt") # "./x.txt"
+
+For options with a ``STRINGS`` list, the value provided to the ``DEFAULT``
+argument must exist within the list unless the ``FREEFORM`` argument has been
+provided. Freeform string list options permit values outside of the list:
+
+.. code:: cmake
+
+    arm_config_option(NAME XYZ ... STRINGS X Y Z) # "X"
+    arm_config_option(NAME XYZ ... STRINGS X Y Z DEFAULT Z) # "Z"
+    arm_config_option(NAME XYZ ... STRINGS X Y Z DEFAULT A FREEFORM) # "A"
+    arm_config_option(NAME XYZ ... STRINGS X Y Z DEFAULT A) # ERROR
+
+Configuration options can be marked as "advanced" by using the ``ADVANCED``
+flag. In CMake's user interfaces, this hides the configuration option behind the
+"advanced" toggle:
+
+    arm_config_option(NAME XYZ ...) # Always visible
+    arm_config_option(NAME XYZ ... ADVANCED) # Visible only when requested
+
+Some basic usage examples follow:
+
+.. code:: cmake
+
+    arm_config_option(
+        NAME MYPROJECT_ENABLE_FOO
+        HELP "Enable the foo feature.")
+
+    arm_config_option(
+        NAME MYPROJECT_ENABLE_BAR
+        HELP "Enable the bar feature."
+        ADVANCED)
+
+    arm_config_option(
+        NAME MYPROJECT_BAZ_NAME
+        HELP "Name of the baz."
+        TYPE STRING
+        DEFAULT "Baz")
+
+    arm_config_option(
+        NAME MYPROJECT_BAZ_TYPE
+        HELP "Type of the baz."
+        STRINGS "Surly" "Bewildered" "Aloof")
+
+    if(MYPROJECT_ENABLE_FOO)
+        message(STATUS "The foo feature is enabled!")
+    endif()
+
+    if(MYPROJECT_ENABLE_BAR)
+        message(STATUS "The bar feature is enabled!")
+    endif()
+
+    message(STATUS "The name of the baz is: ${MYPROJECT_BAZ_NAME}!")
+    message(STATUS "The type of the baz is: ${MYPROJECT_BAZ_TYPE}!")
+
+Dependencies
+^^^^^^^^^^^^
+
+Dependencies between options can be modelled using the ``DEPENDS`` argument.
+This argument takes an expression in :ref:`Condition Syntax`, which determines
+whether the option will be shown.
+
+For example, if you have a feature flag ``foo``, and you have a feature flag
+``bar`` that only makes sense if ``foo`` is enabled, you might use:
+
+.. code:: cmake
+
+    arm_config_option(
+        NAME MYPROJECT_ENABLE_FOO
+        HELP "Enable the foo feature.")
+
+    arm_config_option(
+        NAME MYPROJECT_ENABLE_BAR
+        HELP "Enable the bar feature."
+        DEPENDS MYPROJECT_ENABLE_FOO)
+
+Configuration options whose dependencies have not been met are hidden from the
+GUI (that is, the cache variable is given the ``INTERNAL`` type), and the
+default value is restored.
+
+If you need a value *other* than the default to be set if the dependency is not
+met, then use the ``ELSE`` argument:
+
+.. code:: cmake
+
+    arm_config_option(
+        NAME STACK_SIZE
+        HELP "Stack size (in bytes)."
+        TYPE STRING
+        DEFAULT 512)
+
+    arm_config_option(
+        NAME HEAP_SIZE
+        HELP "Heap size (in bytes)."
+        DEFAULT 65536)
+
+    arm_config_option(
+      NAME STACKHEAP_SIZE
+      HELP "Stackheap size."
+      DEFAULT 65536
+      DEPENDS ((STACK_SIZE EQUAL 0) AND (HEAP_SIZE EQUAL 0))
+      ELSE 0)
+
+In some cases you may need to forcibly overwrite the value of a configuration
+option under certain conditions. You can do this using the ``FORCE`` argument
+which, like ``DEPENDS``, accepts :ref:`Condition Syntax`. This is typically only
+useful for augmenting existing cache variables.
+
+In the following example, ``FORCE`` is used to forcibly override the default
+value of :variable:`CMAKE_BUILD_TYPE <variable:CMAKE_BUILD_TYPE>` (``""``) with
+a new default defined by the build system configuration:
+
+.. code:: cmake
+
+    arm_config_option(
+        NAME CMAKE_BUILD_TYPE
+        HELP "Build type."
+        STRINGS "Debug" "RelWithDebInfo" "MinSizeRel" "Release"
+        DEFAULT "MinSizeRel"
+        FORCE NOT CMAKE_BUILD_TYPE)
+
+Detecting Changes
+^^^^^^^^^^^^^^^^^
+
+In some cases it's useful to know whether a configuration option has been
+modified. Any configuration option created with this function has an associated
+``${NAME}_CHANGED`` cache variable, which can be used to detect whether the
+value of ``${NAME}`` has changed between the last configuration run and the
+current one.
+
+For example:
+
+.. code:: cmake
+
+    arm_config_option(
+        NAME ENABLE_FEATURE
+        HELP "Enable the feature.")
+
+    if(ENABLE_FEATURE_CHANGED)
+        message(STATUS "The feature's been toggled!")
+    endif()
+
+Fine-Grained Control
+^^^^^^^^^^^^^^^^^^^^
+
+Additional facilities for fine-grained control over defaults and forced values
+are provided by the :command:`arm_config_option_override`.
+#]=======================================================================]
+
+include_guard()
+
+function(arm_config_option)
+    set(_options "FREEFORM;ADVANCED")
+    set(_single_args "NAME;HELP;TYPE")
+    set(_multi_args "DEFAULT;STRINGS;DEPENDS;ELSE;FORCE")
+
+    cmake_parse_arguments(
+        arg "${_options}" "${_single_args}" "${_multi_args}" ${ARGN})
+
+    if("DEFAULT" IN_LIST arg_KEYWORDS_MISSING_VALUES)
+        set(arg_DEFAULT "")
+    endif()
+
+    #
+    # Attempt to derive the type from the other arguments given. Passing STRINGS
+    # implies a type of STRING, otherwise the type is BOOL.
+    #
+
+    if(NOT DEFINED arg_TYPE)
+        if(DEFINED arg_STRINGS)
+            set(arg_TYPE "STRING")
+        else()
+            set(arg_TYPE "BOOL")
+        endif()
+    endif()
+
+    #
+    # Identify a reasonable default if one has not been provided. For BOOL this
+    # is FALSE. If STRINGS has been provided then we take the first entry in the
+    # list. For any other type we use an empty string.
+    #
+
+    if(NOT DEFINED arg_DEFAULT)
+        if(arg_TYPE MATCHES "BOOL")
+            set(arg_DEFAULT "FALSE")
+        elseif(DEFINED arg_STRINGS)
+            list(GET arg_STRINGS 0 arg_DEFAULT)
+        else()
+            set(arg_DEFAULT "")
+        endif()
+    endif()
+
+    #
+    # If no dependency condition is provided, it is implicitly TRUE.
+    #
+
+    if(NOT DEFINED arg_DEPENDS)
+        set(arg_DEPENDS "TRUE")
+    endif()
+
+    if(${arg_DEPENDS})
+        #
+        # If an internal cache variable exists by this name but the dependency
+        # condition holds, it's because it previously didn't. We need to
+        # forcibly update the variable to make it visible again.
+        #
+
+        if(DEFINED "${arg_NAME}")
+            get_property(type CACHE "${arg_NAME}" PROPERTY TYPE)
+
+            if(type STREQUAL "INTERNAL")
+                set(arg_FORCE TRUE)
+            endif()
+        endif()
+
+        #
+        # If a force variable exists, take on its value and hide the cache
+        # variable. Otherwise, if a default variable exists, just take on its
+        # value.
+        #
+
+        if(DEFINED "${arg_NAME}_FORCE")
+            set(arg_TYPE "INTERNAL")
+            set(arg_DEFAULT "${${arg_NAME}_FORCE}")
+        elseif(DEFINED "${arg_NAME}_INIT")
+            set(arg_DEFAULT "${${arg_NAME}_INIT}")
+        endif()
+    else()
+        #
+        # If the dependency condition doesn't hold, hide the cache variable from
+        # the user.
+        #
+
+        set(arg_TYPE "INTERNAL")
+
+        #
+        # If an else value has been given, now is the time to adopt it.
+        #
+
+        if(DEFINED arg_ELSE)
+            set(arg_DEFAULT "${arg_ELSE}")
+        endif()
+    endif()
+
+    #
+    # Try to detect whether the user has overridden an already
+    # forcibly-overriden variable. We throw an error in this situation to avoid
+    # a split-brain configuration, where the variable expands to two values
+    # depending on which side of this function call you are on.
+    #
+    # This usually happens if the user has defined the value on the command
+    # line, as these options are replaced every time reconfiguration
+    # happens.
+    #
+
+    if((DEFINED "${arg_NAME}") AND
+        (DEFINED "${arg_NAME}_FORCE") AND
+        (NOT "${${arg_NAME}_FORCE}" STREQUAL "${${arg_NAME}}"))
+        set(value "${${arg_NAME}}")
+        unset("${arg_NAME}" CACHE)
+
+        if(${arg_DEPENDS})
+            message(FATAL_ERROR
+                "Overridden configuration option detected!\n"
+
+                "The configuration option `${arg_NAME}` cannot be given "
+                "the value `${value}` because it has been forcibly set to "
+                "`${arg_DEFAULT}`.")
+        else()
+            string(REPLACE ";" " " dependency "${arg_DEPENDS}")
+
+            message(FATAL_ERROR
+                "Impossible configuration detected!\n"
+
+                "The configuration option `${arg_NAME}` cannot be given "
+                "the value `${value}` because it has been forcibly set to "
+                "`${arg_DEFAULT}` due to an unmet dependency:\n"
+
+                "${dependency}")
+        endif()
+    endif()
+
+    #
+    # The official documentation says that `INTERNAL` implies `FORCE`, but this
+    # does not seem to be the case in some situations, so let's be safe.
+    #
+
+    if(arg_TYPE STREQUAL "INTERNAL")
+        set(arg_FORCE TRUE)
+    endif()
+
+    #
+    # If we're being asked to forcibly update the cache variable, append FORCE
+    # to the set() call.
+    #
+
+    if((DEFINED arg_FORCE) AND (${arg_FORCE}))
+        set(force "FORCE")
+    else()
+        unset(force)
+
+        #
+        # Clear the forced-value variable so that we don't accidentally flag
+        # this
+        #
+
+        unset("${arg_NAME}_FORCE" CACHE)
+    endif()
+
+    #
+    # Update the change-tracking variable.
+    #
+
+    set(old "${${arg_NAME}_NEW}")
+    set(new "${${arg_NAME}}")
+
+    if(NOT old STREQUAL new)
+        set(changed TRUE)
+    else()
+        set(changed FALSE)
+    endif()
+
+    set("${arg_NAME}_OLD" "${old}"
+        CACHE INTERNAL "Previous value of ${arg_NAME}." FORCE)
+
+    set("${arg_NAME}_NEW" "${new}"
+        CACHE INTERNAL "Latest value of ${arg_NAME}." FORCE)
+
+    set("${arg_NAME}_CHANGED" ${changed}
+        CACHE INTERNAL "Has ${arg_NAME} just changed?" FORCE)
+
+    #
+    # Create the cache variable.
+    #
+
+    set("${arg_NAME}" "${arg_DEFAULT}"
+        CACHE "${arg_TYPE}" "${arg_HELP}" ${force})
+
+    if(arg_ADVANCED)
+        mark_as_advanced("${arg_NAME}")
+    endif()
+
+    #
+    # If we've been given a list of valid values, update the STRINGS property of
+    # the cache variable with that list.
+    #
+
+    if(DEFINED arg_STRINGS)
+        set_property(CACHE "${arg_NAME}" PROPERTY STRINGS ${arg_STRINGS})
+
+        #
+        # If we haven't been asked to offer a freeform text box, let the user
+        # know if they've provided something out of bounds.
+        #
+
+        if((NOT arg_FREEFORM) AND (NOT "${${arg_NAME}}" IN_LIST arg_STRINGS))
+            set(strings "")
+
+            foreach(string IN LISTS arg_STRINGS)
+                string(APPEND strings "\"${string}\" ")
+            endforeach()
+
+            message(FATAL_ERROR
+                "Invalid value for `${arg_NAME}`!\n"
+
+                "This configuration supports the following values: ${strings}")
+        endif()
+    endif()
+endfunction()
diff --git a/cmake/Modules/ArmConfigOptionOverride.cmake b/cmake/Modules/ArmConfigOptionOverride.cmake
new file mode 100644
index 0000000..8e5b270
--- /dev/null
+++ b/cmake/Modules/ArmConfigOptionOverride.cmake
@@ -0,0 +1,94 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+#[=======================================================================[.rst:
+ArmConfigOptionOverride
+-----------------------
+
+.. default-domain:: cmake
+
+.. command:: arm_config_option_override
+
+Override the default or final value of a configuration option defined by
+:command:`arm_config_option`.
+
+.. note::
+
+    Configuration options can only be overridden if their dependencies are met.
+    This ensures the configuration space is always in a valid state.
+
+Override Default Value
+^^^^^^^^^^^^^^^^^^^^^^
+
+.. code:: cmake
+
+    arm_config_option_override(NAME <name> DEFAULT <default>)
+
+Overrides the default value of the configuration option ``<name>`` with the
+value ``<default>``.
+
+For example:
+
+.. code:: cmake
+
+    arm_config_option_override(
+        NAME MYPROJECT_USE_FOO
+        DEFAULT TRUE)
+
+    arm_config_option(
+        NAME MYPROJECT_USE_FOO
+        HELP "Use foo.")
+
+In this situation, the configuration option ``USE_FOO`` is created with a
+default value of ``FALSE``, but will use the overridden default value of
+``TRUE``. This is most often useful in larger projects where certain default
+values make more sense under certain conditions.
+
+Forcibly Override Value
+=======================
+
+.. code:: cmake
+
+    arm_config_option_override(NAME <name> FORCE <force>)
+
+Forcibly overrides the value of the configuration option ``<name>`` with
+``<force>``.
+
+For example:
+
+.. code:: cmake
+
+    arm_config_option_override(
+        NAME MYPROJECT_USE_FOO
+        FORCE TRUE)
+
+    arm_config_option(
+        NAME MYPROJECT_USE_FOO
+        HELP "Use foo.")
+
+In this situation, ``USE_FOO`` will be forcibly set to ``TRUE``, and it will be
+hidden from the GUI. Users may also no longer configure this value themselves.
+Attempting to change the value of the configuration option will cause a
+configuration failure, and the previous value will be restored.
+#]=======================================================================]
+
+include_guard()
+
+function(arm_config_option_override)
+    set(_options "")
+    set(_single_args "NAME;DEFAULT;FORCE")
+    set(_multi_args "")
+
+    cmake_parse_arguments(arg "${_options}" "${_single_args}" "${_multi_args}"
+                          ${ARGN})
+
+    if(DEFINED arg_FORCE)
+        set("${arg_NAME}_FORCE" "${arg_FORCE}" CACHE INTERNAL
+            "Forced value for `${arg_NAME}`." FORCE)
+    elseif(DEFINED arg_DEFAULT)
+        set("${arg_NAME}_INIT" "${arg_DEFAULT}" CACHE INTERNAL
+            "Default value for `${arg_NAME}`." FORCE)
+    endif()
+endfunction()
diff --git a/cmake/Modules/ArmPreprocessSource.cmake b/cmake/Modules/ArmPreprocessSource.cmake
new file mode 100644
index 0000000..a3f511b
--- /dev/null
+++ b/cmake/Modules/ArmPreprocessSource.cmake
@@ -0,0 +1,168 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+#[=======================================================================[.rst:
+ArmPreprocessSource
+-------------------
+
+.. default-domain:: cmake
+
+.. command:: arm_preprocess_source
+
+Preprocess a file with the C preprocessor.
+
+.. code:: cmake
+
+    arm_preprocess_source(<target> <source>)
+
+Creates a target ``<target>`` which preprocesses an input file ``<source>``. The
+target created by this macro can then be used as a dependency for a higher-level
+target. The output file can be retrieved from the :prop_tgt:`LOCATION_<CONFIG>
+<prop_tgt:LOCATION_<CONFIG>>` target property.
+
+The following target properties are passed to the preprocessor:
+
+- :prop_tgt:`COMPILE_OPTIONS <prop_tgt:COMPILE_OPTIONS>`
+- :prop_tgt:`COMPILE_DEFINITIONS <prop_tgt:COMPILE_DEFINITIONS>`
+- :prop_tgt:`INCLUDE_DIRECTORIES <prop_tgt:INCLUDE_DIRECTORIES>`
+
+.. note::
+
+    The created target automatically inherits :variable:`CMAKE_C_FLAGS
+    <variable:CMAKE_<LANG>_FLAGS>` and :variable:`CMAKE_C_FLAGS_<CONFIG>
+    <variable:CMAKE_<LANG>_FLAGS_<CONFIG>>`.
+
+For example, if you wish to preprocess a file while providing the preprocessor
+definition ``-DFOO=BAR``, you would use:
+
+.. code:: cmake
+
+    arm_preprocess_source(foo "bar.ld.S")
+
+    set_target_properties(foo PROPERTIES
+        COMPILE_DEFINITIONS "FOO=BAR")
+
+    get_target_property(location foo LOCATION_${CMAKE_BUILD_TYPE})
+
+    message(STATUS "My preprocessed file is here: ${location}")
+
+For processing linker scripts specifically, see the
+:command:`arm_target_linker_script` command instead.
+#]=======================================================================]
+
+include_guard()
+
+macro(arm_preprocess_source target source)
+    #
+    # We start by trying to get the source file relative to the current source
+    # directory, which means that we can mirror where it goes in the binary
+    # directory. This just replicates what CMake does with source files it's
+    # aware of.
+    #
+
+    get_filename_component(preprocessed_source "${source}.i" ABSOLUTE)
+    file(RELATIVE_PATH preprocessed_source "${CMAKE_CURRENT_SOURCE_DIR}"
+        "${preprocessed_source}")
+
+    #
+    # If we're using a multi-config generator, we need to place the output file
+    # into the correct configuration directory.
+    #
+
+    get_property(multi_config GLOBAL
+        PROPERTY "GENERATOR_IS_MULTI_CONFIG")
+
+    if(multi_config)
+        string(PREPEND preprocessed_source "$<CONFIG>/")
+    endif()
+
+    #
+    # Make the source path absolute so that we don't need to care which
+    # working directory the preprocessor uses.
+    #
+
+    string(PREPEND preprocessed_source "${CMAKE_CURRENT_BINARY_DIR}/")
+
+    #
+    # Create a single target for all configurations. It's differentiated based
+    # on the generator expression in the dependency.
+    #
+
+    add_custom_target(${target}
+        DEPENDS "${preprocessed_source}")
+
+    #
+    # Now that we've got that out of the way, we need to generate the
+    # preprocessing command for each of the enabled configurations. Multi-config
+    # generators will use `CMAKE_CONFIGURATION_TYPES`, whereas single-config
+    # generators will use `CMAKE_BUILD_TYPE`. Only one is ever non-empty.
+    #
+
+    foreach(config IN LISTS CMAKE_CONFIGURATION_TYPES CMAKE_BUILD_TYPE)
+        #
+        # CMake provides the `CMAKE_C_CREATE_PREPROCESSED_SOURCE` variable,
+        # which describes the command line required to preprocess a C source
+        # file. This variable is in a format similar to this:
+        #
+        # <CMAKE_C_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -E <SOURCE> >
+        # <PREPROCESSED_SOURCE>
+        #
+        # We do some processing on this variable to convert these
+        # bracket-surrounded names to variables we set. For example, `<DEFINES>`
+        # is replaced with `${DEFINES}`. We then need to do some string
+        # replacement magic to expand that string out to the value of the actual
+        # variable.
+        #
+        # The values for some of these, namely include directories, definitions
+        # and other compiler options, come from properties set on the target by
+        # the caller. These are typically taken from the target that this
+        # preprocessed source file belongs to.
+        #
+
+        set(command ${CMAKE_C_CREATE_PREPROCESSED_SOURCE})
+        string(REPLACE " " ";" command ${command})
+
+        get_filename_component(SOURCE "${source}" ABSOLUTE)
+        string(REPLACE "$<CONFIG>" "${config}" PREPROCESSED_SOURCE
+            "${preprocessed_source}")
+
+        separate_arguments(FLAGS UNIX_COMMAND
+            "${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${config}} -P -x c")
+
+        if(CMAKE_C_COMPILER_TARGET)
+            if(CMAKE_C_COMPILER_ID MATCHES "Clang")
+                list(APPEND FLAGS "-target" "${CMAKE_C_COMPILER_TARGET}")
+            endif()
+        endif()
+
+        unset(DEFINES)
+        unset(INCLUDES)
+
+        list(APPEND FLAGS "$<TARGET_PROPERTY:${target},COMPILE_OPTIONS>")
+        list(APPEND DEFINES "$<TARGET_PROPERTY:${target},COMPILE_DEFINITIONS>")
+        list(APPEND INCLUDES "$<TARGET_PROPERTY:${target},INCLUDE_DIRECTORIES>")
+
+        set(DEFINES "$<$<BOOL:${DEFINES}>:-D$<JOIN:${DEFINES},$<SEMICOLON>-D>>")
+        set(INCLUDES "$<$<BOOL:${INCLUDES}>:-I$<JOIN:${INCLUDES},$<SEMICOLON>-I>>")
+
+        string(REGEX REPLACE "<([[A-Z_]+)>" "\${\\1}" command "${command}")
+        string(REGEX MATCH "\\\${[^}]*}" match "${command}")
+
+        while(match)
+            string(REGEX REPLACE "\\\${(.*)}" "\\1" variable "${match}")
+            string(REPLACE "\${${variable}}" "${${variable}}" command "${command}")
+            string(REGEX MATCH "\\\${[^}]*}" match "${command}")
+        endwhile()
+
+        add_custom_command(
+            OUTPUT "${PREPROCESSED_SOURCE}"
+            MAIN_DEPENDENCY ${source}
+            COMMAND "${command}"
+            VERBATIM COMMAND_EXPAND_LISTS)
+
+        set_target_properties(${target} PROPERTIES
+            LOCATION_${config} "${PREPROCESSED_SOURCE}")
+    endforeach()
+endmacro()
diff --git a/cmake/Modules/ArmTargetLinkerScript.cmake b/cmake/Modules/ArmTargetLinkerScript.cmake
new file mode 100644
index 0000000..087fe8e
--- /dev/null
+++ b/cmake/Modules/ArmTargetLinkerScript.cmake
@@ -0,0 +1,89 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+#[=======================================================================[.rst:
+ArmTargetLinkerScript
+---------------------
+
+.. default-domain:: cmake
+
+.. command:: arm_target_linker_script
+
+Set the linker script for a target.
+
+.. code:: cmake
+
+    arm_target_linker_script(<target> <script>)
+
+Sets the linker script of the target ``<target>`` to the script ``<script>``,
+which is first preprocessed with the C preprocessor.
+
+Properties for the linker script target may be set on `<target>-lds`.
+
+Example usage:
+
+.. code:: cmake
+
+    add_executable(my-executable "main.c")
+
+    arm_target_linker_script(my-executable "linker.lds")
+
+    set_target_properties(my-executable-lds
+        PROPERTIES COMPILE_DEFINITIONS "__LINKER__")
+
+.. note::
+
+    When preprocessing, the linker script given to this macro automatically
+    inherits :variable:`CMAKE_C_FLAGS <variable:CMAKE_<LANG>_FLAGS>` and
+    :variable:`CMAKE_C_FLAGS_<CONFIG> <variable:CMAKE_<LANG>_FLAGS_<CONFIG>>`.
+
+    It also inherits the following properties from the target ``<target>``:
+
+    - :prop_tgt:`COMPILE_OPTIONS <prop_tgt:COMPILE_OPTIONS>`
+    - :prop_tgt:`COMPILE_DEFINITIONS <prop_tgt:COMPILE_DEFINITIONS>`
+    - :prop_tgt:`INCLUDE_DIRECTORIES <prop_tgt:INCLUDE_DIRECTORIES>`
+#]=======================================================================]
+
+include_guard()
+
+include(ArmPreprocessSource)
+
+macro(arm_target_linker_script target script)
+    set(subtarget "${target}-lds")
+
+    #
+    # Preprocess the linker script before doing anything else.
+    #
+
+    arm_preprocess_source(${subtarget} "${script}")
+
+    set_target_properties(${subtarget}
+        PROPERTIES
+            COMPILE_OPTIONS
+                "$<TARGET_PROPERTY:${target},COMPILE_OPTIONS>"
+            COMPILE_DEFINITIONS
+                "$<TARGET_PROPERTY:${target},COMPILE_DEFINITIONS>"
+            INCLUDE_DIRECTORIES
+                "$<TARGET_PROPERTY:${target},INCLUDE_DIRECTORIES>")
+
+    #
+    # Add the linker script to the dependencies of the target.
+    #
+
+    add_dependencies(${target} ${subtarget})
+
+    set(location "$<TARGET_PROPERTY:${subtarget},LOCATION_$<CONFIG>>")
+
+    set_target_properties(${target}
+        PROPERTIES INTERFACE_LINK_DEPENDS "${location}")
+
+    if(CMAKE_C_COMPILER_ID STREQUAL "ARMClang")
+        target_link_options(${target}
+            PUBLIC "LINKER:--scatter" "LINKER:${location}")
+    else()
+        target_link_options(${target}
+            PUBLIC "LINKER:-T" "LINKER:${location}")
+    endif()
+endmacro()
diff --git a/cmake/Modules/FindMbedTLS.cmake b/cmake/Modules/FindMbedTLS.cmake
new file mode 100644
index 0000000..89fb237
--- /dev/null
+++ b/cmake/Modules/FindMbedTLS.cmake
@@ -0,0 +1,60 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+#[=======================================================================[.rst:
+FindMbedTLS
+===========
+
+TODO: documentation.
+#]=======================================================================]
+
+include(FindPackageHandleStandardArgs)
+
+find_path(MbedTLS_INCLUDE_DIR
+    NAMES "mbedtls/build_info.h")
+
+if(MbedTLS_INCLUDE_DIR)
+    mark_as_advanced(MbedTLS_INCLUDE_DIR)
+
+    set(MbedTLS_FOUND TRUE)
+endif()
+
+find_library(MbedTLS_Crypto_LIBRARY "mbedcrypto" PATHS "library" "lib")
+find_library(MbedTLS_TLS_LIBRARY "mbedtls" PATHS "library" "lib")
+find_library(MbedTLS_X509_LIBRARY "mbedx509" PATHS "library" "lib")
+
+foreach(component IN ITEMS Crypto TLS X509)
+    if(MbedTLS_${component}_LIBRARY)
+        mark_as_advanced(MbedTLS_${component}_LIBRARY)
+
+        set(MbedTLS_${component}_FOUND TRUE)
+    endif()
+endforeach()
+
+find_package_handle_standard_args(MbedTLS HANDLE_COMPONENTS
+    REQUIRED_VARS MbedTLS_FOUND MbedTLS_INCLUDE_DIR)
+
+if(MbedTLS_FOUND)
+    add_library(MbedTLS INTERFACE)
+
+    target_include_directories(MbedTLS
+        INTERFACE "${MbedTLS_INCLUDE_DIR}"
+                  "${RMM_SOURCE_DIR}/configs/mbedtls")
+
+    target_compile_definitions(MbedTLS
+        INTERFACE "MBEDTLS_CONFIG_FILE=<mbedtls_config.h>")
+
+    foreach(component IN ITEMS Crypto TLS X509)
+        if(MbedTLS_${component}_LIBRARY)
+            add_library(MbedTLS::${component} UNKNOWN IMPORTED)
+
+            set_target_properties(MbedTLS::${component}
+                PROPERTIES IMPORTED_LOCATION "${MbedTLS_${component}_LIBRARY}")
+
+            target_link_libraries(MbedTLS::${component}
+                INTERFACE MbedTLS)
+        endif()
+    endforeach()
+endif()
diff --git a/cmake/Modules/FindPlantUML.cmake b/cmake/Modules/FindPlantUML.cmake
new file mode 100644
index 0000000..331c221c76
--- /dev/null
+++ b/cmake/Modules/FindPlantUML.cmake
@@ -0,0 +1,60 @@
+#-------------------------------------------------------------------------------
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#-------------------------------------------------------------------------------
+
+#FindPlantuml
+#-----------
+#PlantUML is a diagram generation tool. It can generate various UML and non-UML
+#diagrams. See: http://plantuml.com/
+#
+#This module checks PlantUML availability and checks if the Java runtime is
+#available.
+#For Windows PlantUML is distributed as a jar archive and thus there is no
+#standard install location where it could be searched for.
+#Most Linux distributions come with a proper PlantUML package which installs
+#a shell script to easy starting PlantUML, but the location of the .jar file
+#is hidden.
+#Thus there is no standard location to search for the .jar file and this module
+#depends on user input.
+#
+#This module has the following parameters:
+#   PLANTUML_JAR_PATH   = variable specifying where the PlantUML java archive
+#                         (plantuml.jar) can be found. If it is not defined,
+#                         the environment variable with the same name is used.
+#                         If both is missing, that is an error.
+#
+#This module defines the following variables:
+#   PLANTUML_VERSION        = The version reported by "plantuml.jar -version"
+#   PLANTUML_FOUND          = Was the .jar file found and sucesfuly executed.
+#
+
+#include(Common/Utils)
+
+find_package(Java 1.8 COMPONENTS Runtime)
+if(Java_Runtime_FOUND)
+	#Check if the jar file is at the user defined location.
+	#Prefer the cmake variable to the environment setting.
+	if (NOT DEFINED PLANTUML_JAR_PATH)
+		if (DEFINED ENV{PLANTUML_JAR_PATH})
+			set(PLANTUML_JAR_PATH "$ENV{PLANTUML_JAR_PATH}" CACHE STRING "PLANTUML location." )
+		endif()
+	endif()
+
+	if (NOT DEFINED PLANTUML_JAR_PATH)
+		message(STATUS "PLANTUML_JAR_PATH variable is missing, PlantUML jar location is unknown.")
+	else()
+		#Get plantuml version
+		execute_process(COMMAND "${Java_JAVA_EXECUTABLE}" "-jar" "${PLANTUML_JAR_PATH}" "-version" OUTPUT_VARIABLE _PLANTUML_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
+		#Parse plantuml output
+		if(_PLANTUML_VERSION)
+			if(_PLANTUML_VERSION MATCHES ".*PlantUML version ([0-9.]+).*")
+				string(REGEX REPLACE ".*PlantUML version ([0-9.]+).*" "\\1" PLANTUML_VERSION "${_PLANTUML_VERSION}")
+			endif()
+		endif()
+	endif()
+endif()
+
+#Set "standard" find module return values
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(PlantUML REQUIRED_VARS PLANTUML_JAR_PATH PLANTUML_VERSION VERSION_VAR PLANTUML_VERSION)
diff --git a/cmake/Modules/FindPythonModules.cmake b/cmake/Modules/FindPythonModules.cmake
new file mode 100644
index 0000000..625bfec
--- /dev/null
+++ b/cmake/Modules/FindPythonModules.cmake
@@ -0,0 +1,61 @@
+#-------------------------------------------------------------------------------
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#-------------------------------------------------------------------------------
+
+#FindPythonModules
+#-----------
+#This module checks availability of Python modules.
+#
+#This module has the following parameters:
+#   PYTHON_EXECUTABLE - Location of python interpreter.
+#   COMPONENTS        - List of python modules to look for.
+#
+#This module defines the following variables:
+#   PY_XXX       - Cached string variable with the location of the module.
+#   PY_XXX_FOUND - Set if the module is available.
+#
+#   Where XXX is the upper case name of the module.
+#
+#Examples
+#   To look for m2r and report error if not found
+#       find_module(PythonModules COMPONENTS m2r)
+#       if (PY_M2R_FOUND)
+#           do something
+#       endif()
+#
+#   To look for m2r and do not report error if not found
+#       find_module(PythonModules OPTIONAL_COMPONENTS m2r)
+#       if (PY_M2R_FOUND)
+#           do something
+#       endif()
+
+if(NOT DEFINED PYTHON_EXECUTABLE)
+	message(FATAL_ERROR "FindPythonModules: mandatory parameter PYTHON_EXECUTABLE is missing.")
+endif()
+
+foreach(_mod ${PythonModules_FIND_COMPONENTS})
+	string(TOUPPER ${_mod} _mod_upper)
+	string(REPLACE "-" "_" _modname "${_mod}")
+	if (NOT PY_${_mod_upper})
+		#Execute python and try to include the module.
+		execute_process(
+			COMMAND ${PYTHON_EXECUTABLE} -c "import ${_modname}; print(${_modname}.__file__);"
+			RESULT_VARIABLE ${_mod}_status
+			OUTPUT_VARIABLE ${_mod}_path
+			ERROR_QUIET
+			OUTPUT_STRIP_TRAILING_WHITESPACE)
+		#If suceeded
+		if(NOT ${_mod}_status)
+			#Avoid trouble with directory separator on windows.
+			set("PY_${_mod_upper}" "${${_mod}_path}" CACHE STRING
+				"Location of Python module ${_mod}")
+		endif()
+	endif()
+	#Set "standard" find module return values
+	include(FindPackageHandleStandardArgs)
+	find_package_handle_standard_args(PY_${_mod_upper}
+		REQUIRED_VARS PY_${_mod_upper}
+		NAME_MISMATCHED
+		FAIL_MESSAGE "Can not find Python module ${_mod}")
+endforeach()
diff --git a/cmake/Modules/FindSphinx.cmake b/cmake/Modules/FindSphinx.cmake
new file mode 100644
index 0000000..7acab08
--- /dev/null
+++ b/cmake/Modules/FindSphinx.cmake
@@ -0,0 +1,90 @@
+#-------------------------------------------------------------------------------
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#-------------------------------------------------------------------------------
+
+#FindSphinx
+#-----------
+#Sphinx is a document generation tool written in Python.
+#See http://www.sphinx-doc.org/en/master/
+#
+#This module checks availability of the Sphinx document generator
+#(sphinx-build) and it's dependences (Python).
+#Sphinx is distributed as pip package or on Linux as a distribution specific
+#package (i.e. python-sphinx for Ubuntu). Independent of the distribution
+#method this module expects sphix-build to be either available on the PATH,
+#or to be located in a host OS specific standard location.
+#
+#This modules has the following parameters:
+#   SPHINX_PATH   = variable specifying where sphinx-build can be found.
+#                         If it is not defined the environment variable with
+#                         the same name is used. If that is also undefined,
+#                         then OS specific standard locations will be
+#                         searched.
+#
+# This modules defines the following variables:
+#   SPHINX_VERSION   = The version reported by "sphinx-build --version"
+#   SPHINX_FOUND     = True is sphinx-build was found and executed fine
+#
+
+Include(CMakeParseArguments)
+
+#Sphinx needs Python.
+find_package(PythonInterp 3)
+if (NOT PYTHONINTERP_FOUND)
+	message(STATUS "Can not find Python3.x interpreter. Pyhton3 must be installed and available on the PATH.")
+	message(STATUS "Sphinx documentation targets will not be created.")
+	return()
+endif()
+
+if (NOT DEFINED SPHINX_PATH)
+	if (DEFINED $ENV{SPHINX_PATH})
+	set(SPHINX_PATH $ENV{SPHINX_PATH})
+	endif()
+endif()
+
+
+if (DEFINED SPHINX_PATH)
+	#Find the Sphinx executable. Search only at SPHINX_PATH.
+	find_program(SPHINX_EXECUTABLE
+		NAMES sphinx-build
+		DOC "Sphinx Documentation Builder (sphinx-doc.org)"
+		PATH ${SPHINX_PATH}
+		NO_DEFAULT_PATH
+		NO_CMAKE_ENVIRONMENT_PATH
+		NO_CMAKE_PATH
+		NO_SYSTEM_ENVIRONMENT_PATH
+		NO_CMAKE_SYSTEM_PATH
+		NO_CMAKE_FIND_ROOT_PATH
+	)
+	if (SPHINX_EXECUTABLE-NOTFOUND)
+		message(STATUS "Failed to find sphinx-build at ${SPHINX_PATH}.")
+		message(STATUS "Sphinx documentation targets will not be created.")
+		return()
+	endif()
+else()
+	#Find the Sphinx executable. Search OS specific default locations.
+	find_program(SPHINX_EXECUTABLE
+	  NAMES sphinx-build
+	  DOC "Sphinx Documentation Builder (sphinx-doc.org)"
+	)
+
+	if (SPHINX_EXECUTABLE-NOTFOUND)
+		message(STATUS "Failed to find sphinx-build at OS specific default locations.")
+		message(STATUS "Sphinx documentation targets will not be created.")
+		return()
+	endif()
+endif()
+
+#Get Sphinx version
+execute_process(COMMAND "${SPHINX_EXECUTABLE}" "--version" OUTPUT_VARIABLE _SPHINX_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
+#Parse output
+if(_SPHINX_VERSION)
+	if(_SPHINX_VERSION MATCHES ".*sphinx-build[^0-9.]*([0-9.]+).*")
+		string(REGEX REPLACE ".*sphinx-build ([0-9.]+).*" "\\1" SPHINX_VERSION "${_SPHINX_VERSION}")
+	endif()
+endif()
+
+#Set "standard" find module return values
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(Sphinx REQUIRED_VARS SPHINX_EXECUTABLE SPHINX_VERSION VERSION_VAR SPHINX_VERSION)
diff --git a/cmake/Platforms.cmake b/cmake/Platforms.cmake
new file mode 100644
index 0000000..2a1a553
--- /dev/null
+++ b/cmake/Platforms.cmake
@@ -0,0 +1,28 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+#
+# Load in the platform based on its source directory.
+#
+
+arm_config_option(
+    NAME RMM_PLATFORM
+    HELP "Platform to build."
+    TYPE PATH
+    DEFAULT "" # Force a configuration failure if unset
+    DEPENDS NOT RMM_PLATFORM
+    ELSE "${RMM_PLATFORM}") # Maintain the value when it's forcibly hidden
+
+set(RMM_PLATFORM_SOURCE_DIR "${CMAKE_SOURCE_DIR}/plat/${RMM_PLATFORM}")
+
+if(NOT EXISTS ${RMM_PLATFORM_SOURCE_DIR})
+    message(FATAL_ERROR "Cannot find ${RMM_PLATFORM_SOURCE_DIR}. Invalid platform specified")
+endif()
+
+#
+# Load in the platform-specific list file.
+#
+
+add_subdirectory("${RMM_PLATFORM_SOURCE_DIR}")
diff --git a/cmake/Toolchains.cmake b/cmake/Toolchains.cmake
new file mode 100644
index 0000000..0b8cea6
--- /dev/null
+++ b/cmake/Toolchains.cmake
@@ -0,0 +1,35 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+#
+# Set up the toolchain logic. This is only necessary if a toolchain file hasn't
+# been provided. Otherwise, we force this option to an empty string.
+#
+
+if(DEFINED CACHE{CMAKE_TOOLCHAIN_FILE} AND NOT DEFINED RMM_TOOLCHAIN)
+    message(WARNING
+        "The RMM project does not support `CMAKE_TOOLCHAIN_FILE` directly. "
+        "Please use `RMM_TOOLCHAIN` to configure your desired toolchain.")
+
+    unset(CMAKE_TOOLCHAIN_FILE CACHE)
+endif()
+
+file(GLOB toolchains
+    RELATIVE "${CMAKE_SOURCE_DIR}/toolchains/${RMM_ARCH}"
+        "${CMAKE_SOURCE_DIR}/toolchains/${RMM_ARCH}/*.cmake")
+string(REPLACE ".cmake" "" toolchains "${toolchains}")
+
+arm_config_option(
+    NAME RMM_TOOLCHAIN
+    HELP "Toolchain name."
+    STRINGS ${toolchains}
+    DEFAULT ""
+    DEPENDS (NOT RMM_TOOLCHAIN IN_LIST toolchains)
+    ELSE "${RMM_TOOLCHAIN}")
+
+if(NOT EXISTS CMAKE_TOOLCHAIN_FILE)
+    set(CMAKE_TOOLCHAIN_FILE
+        "${CMAKE_SOURCE_DIR}/toolchains/${RMM_ARCH}/${RMM_TOOLCHAIN}.cmake")
+endif()
diff --git a/configs/fvp_defcfg.cmake b/configs/fvp_defcfg.cmake
new file mode 100644
index 0000000..4b230f7
--- /dev/null
+++ b/configs/fvp_defcfg.cmake
@@ -0,0 +1,46 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+#
+# Set the RMM_PLATFORM variable to Cmake cache.
+#
+set(RMM_PLATFORM "fvp" CACHE STRING "platform")
+arm_config_option_override(NAME RMM_TOOLCHAIN DEFAULT "gnu")
+
+
+#
+# Width of the virtual address space for the system.
+#
+arm_config_option_override(NAME VIRT_ADDR_SPACE_WIDTH DEFAULT 38)
+
+#
+# Set RMM_MAX_SIZE for this platform (16 MB)
+#
+arm_config_option_override(NAME RMM_MAX_SIZE DEFAULT 0x01000000)
+
+#
+# UART Base address. This must be dynamically discovered in future.
+# Use UART3 on the FVP for RMM.
+#
+arm_config_option_override(NAME RMM_UART_ADDR DEFAULT 0x1c0c0000)
+
+#
+# Maximum number of translation tables allocated by the runtime context
+# for the translation library.
+#
+arm_config_option_override(NAME PLAT_CMN_CTX_MAX_XLAT_TABLES DEFAULT 5)
+
+#
+# Disable FPU/SIMD usage in RMM. Enabling this option turns on
+# DMBEDTLS_SHAXXX_USE_A64_CRYPTO_ONLY in Mbed TLS. To run RMM that was compiled
+# this way requires Crypto.so plugin to be present for the FVP. This plugin is
+# delivered separate to the FVP, and might not be present in all environments.
+#
+arm_config_option_override(NAME RMM_FPU_USE_AT_REL2 DEFAULT OFF)
+
+#
+# Maximum number of granules supported, enough to cover 2GB of DDR0.
+#
+arm_config_option_override(NAME RMM_MAX_GRANULES DEFAULT 0x80000)
diff --git a/configs/host_defcfg.cmake b/configs/host_defcfg.cmake
new file mode 100644
index 0000000..7d89079
--- /dev/null
+++ b/configs/host_defcfg.cmake
@@ -0,0 +1,33 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+#
+# Set the RMM_PLATFORM variable to Cmake cache.
+#
+set(RMM_PLATFORM "host" CACHE STRING "platform")
+
+arm_config_option_override(NAME RMM_ARCH DEFAULT "fake_host")
+arm_config_option_override(NAME RMM_TOOLCHAIN DEFAULT "gnu")
+
+#
+# Width of the virtual address space for the system.
+#
+arm_config_option_override(NAME VIRT_ADDR_SPACE_WIDTH DEFAULT 38)
+
+#
+# Set RMM_MAX_SIZE for this platform.
+#
+arm_config_option_override(NAME RMM_MAX_SIZE DEFAULT 0x01000000)
+
+#
+# Maximum number of translation tables allocated by the runtime context
+# for the translation library.
+#
+arm_config_option_override(NAME PLAT_CMN_CTX_MAX_XLAT_TABLES DEFAULT 6)
+
+#
+# Maximum number of granules supported, enough to cover 2GB of DDR0.
+#
+arm_config_option_override(NAME RMM_MAX_GRANULES DEFAULT 0x80000)
diff --git a/configs/mbedtls/mbedtls_config.h b/configs/mbedtls/mbedtls_config.h
new file mode 100644
index 0000000..94ad950
--- /dev/null
+++ b/configs/mbedtls/mbedtls_config.h
@@ -0,0 +1,72 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+#ifndef MBEDTLS_CONFIG_H
+#define MBEDTLS_CONFIG_H
+
+/* This file is compatible with release 3.1.0 */
+#define MBEDTLS_CONFIG_VERSION         0x03010000
+
+/*
+ * Configuration file to build mbed TLS with the required features for
+ * RMM
+ */
+#define MBEDTLS_PLATFORM_MEMORY
+#define MBEDTLS_PLATFORM_FREE_MACRO buffer_alloc_free
+#define MBEDTLS_PLATFORM_CALLOC_MACRO buffer_alloc_calloc
+
+#define MBEDTLS_PLATFORM_NO_STD_FUNCTIONS
+
+#define MBEDTLS_ECP_C
+#define MBEDTLS_ECP_DP_SECP384R1_ENABLED
+#define MBEDTLS_ECP_RESTARTABLE
+#define MBEDTLS_ECDH_LEGACY_CONTEXT
+#define MBEDTLS_ECDSA_C
+#define MBEDTLS_ECDSA_DETERMINISTIC
+#define MBEDTLS_ECP_WINDOW_SIZE		(2U)	/* Valid range = [2,7] */
+
+#define MBEDTLS_ENTROPY_C
+#define MBEDTLS_NO_PLATFORM_ENTROPY
+#define MBEDTLS_ENTROPY_HARDWARE_ALT
+
+#define MBEDTLS_ASN1_PARSE_C
+#define MBEDTLS_ASN1_WRITE_C
+
+#define MBEDTLS_PLATFORM_SNPRINTF_MACRO snprintf
+
+#define MBEDTLS_BASE64_C
+#define MBEDTLS_BIGNUM_C
+
+#define MBEDTLS_ERROR_C
+
+#define MBEDTLS_HKDF_C
+#define MBEDTLS_HMAC_DRBG_C
+
+#define MBEDTLS_MD_C
+
+#define MBEDTLS_PLATFORM_C
+
+#define MBEDTLS_SHA256_C
+#define MBEDTLS_SHA224_C
+#define MBEDTLS_SHA384_C
+#define MBEDTLS_SHA512_C
+
+#define MBEDTLS_VERSION_C
+
+/*
+ * Prevent the use of 128-bit division which
+ * creates dependency on external libraries.
+ */
+#define MBEDTLS_NO_UDBL_DIVISION
+
+/* This is needed for size_t used below */
+#include <stddef.h>
+
+/*
+ * Declare memory allocation primitives to be used by MbedTLS
+ */
+void *buffer_alloc_calloc(size_t n, size_t size);
+void buffer_alloc_free(void *ptr);
+
+#endif /* MBEDTLS_CONFIG_H */
diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt
new file mode 100644
index 0000000..9f43b03
--- /dev/null
+++ b/docs/CMakeLists.txt
@@ -0,0 +1,34 @@
+#-------------------------------------------------------------------------------
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+#
+#-------------------------------------------------------------------------------
+
+add_custom_target(docs)
+
+find_package(Python3)
+find_package(Sphinx)
+find_package(PythonModules COMPONENTS sphinx-rtd-theme sphinxcontrib.plantuml)
+find_package(PlantUML)
+
+set(SPHINXCFG_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/user_guide)
+
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/conf.py.in ${CMAKE_CURRENT_BINARY_DIR}/conf.py @ONLY)
+
+if (SPHINX_FOUND AND PLANTUML_FOUND AND PY_SPHINX-RTD-THEME_FOUND AND PY_SPHINXCONTRIB.PLANTUML)
+
+    file(GLOB_RECURSE SPHINXCFG_DOC_FILES ${CMAKE_CURRENT_SOURCE_DIR} *.rst)
+
+    add_custom_command(OUTPUT "${SPHINXCFG_OUTPUT_PATH}/html/index.html"
+        OUTPUT "${SPHINXCFG_OUTPUT_PATH}/html/"
+        COMMAND "${SPHINX_EXECUTABLE}" -W -b html "${CMAKE_CURRENT_SOURCE_DIR}" "${SPHINXCFG_OUTPUT_PATH}/html" -c ${CMAKE_CURRENT_BINARY_DIR}
+        WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
+        DEPENDS ${SPHINXCFG_DOC_FILES}
+    )
+    add_custom_target(rmm_docs_userguide_html
+        DEPENDS "${SPHINXCFG_OUTPUT_PATH}/html/index.html"
+        DEPENDS "${SPHINXCFG_OUTPUT_PATH}/html/"
+    )
+    add_dependencies(docs rmm_docs_userguide_html)
+endif()
diff --git a/docs/_static/css/rmm_custom.css b/docs/_static/css/rmm_custom.css
new file mode 100644
index 0000000..efe1470
--- /dev/null
+++ b/docs/_static/css/rmm_custom.css
@@ -0,0 +1,92 @@
+/*-----------------------------------------------------------------------------
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#----------------------------------------------------------------------------*/
+
+/* Expand width to fill screen */
+.wy-nav-content {
+   max-width: none;
+}
+
+/* Flexbox Tile Grid Settings  */
+.grid {
+    list-style-type: none !important;
+    display: -webkit-box;
+    display: -ms-flexbox;
+    display: flex;
+    -ms-flex-wrap: wrap;
+        flex-wrap: wrap;
+    -webkit-box-pack: center;
+        -ms-flex-pack: center;
+            justify-content: center;
+    margin: 1rem auto;
+    max-width: calc((250px + 2rem) * 4);
+}
+
+.grid-item {
+    list-style-type: none !important;
+    -webkit-box-flex: 0;
+        -ms-flex: 0 0 auto;
+            flex: 0 0 auto;
+    width: 220px;
+    text-align: center;
+    margin: 1rem;
+}
+
+.grid-item a {
+    display: block;
+    width: 220px;
+    height: 220px;
+    padding: 22px;
+    display: -webkit-box;
+    display: -ms-flexbox;
+    display: flex;
+    -webkit-box-orient: vertical;
+    -webkit-box-direction: normal;
+        -ms-flex-direction: column;
+            flex-direction: column;
+    -webkit-box-pack: center;
+        -ms-flex-pack: center;
+            justify-content: center;
+    -webkit-box-align: center;
+        -ms-flex-align: center;
+            align-items: center;
+    border: 1px solid #c6cbce;
+    background-color: #2980B9;
+    color: white;
+}
+
+.grid-item h2 {
+    font-size: 1.1rem;
+}
+
+.grid-item img {
+    margin-bottom: 1.1rem;
+    max-width: 75%;
+}
+
+.grid-item a:hover {
+    background-color: #32cd32;
+    color: white;
+}
+
+
+.grid-item p {
+    margin-top: 0.5rem;
+    color: #333e48;
+}
+
+.grid-icon {
+   line-height: 1.8;
+   font-size: 6rem;
+   color: #343131;
+}
+
+/* override table width restrictions */
+.wy-table-responsive table td, .wy-table-responsive table th {
+    white-space: nowrap; text-align:justify;
+    table-layout: fixed;
+}
+.wy-table-responsive table th {
+    background-color: #f0f0f0;
+}
diff --git a/docs/_static/images/TrustedFirmware-Logo_standard-white.png b/docs/_static/images/TrustedFirmware-Logo_standard-white.png
new file mode 100644
index 0000000..e7bff71
--- /dev/null
+++ b/docs/_static/images/TrustedFirmware-Logo_standard-white.png
Binary files differ
diff --git a/docs/_static/images/rmm-documentation.png b/docs/_static/images/rmm-documentation.png
new file mode 100644
index 0000000..925c52e
--- /dev/null
+++ b/docs/_static/images/rmm-documentation.png
Binary files differ
diff --git a/docs/_static/images/rmm-introduction.png b/docs/_static/images/rmm-introduction.png
new file mode 100644
index 0000000..73998eb
--- /dev/null
+++ b/docs/_static/images/rmm-introduction.png
Binary files differ
diff --git a/docs/about/change-log.rst b/docs/about/change-log.rst
new file mode 100644
index 0000000..afef9da
--- /dev/null
+++ b/docs/about/change-log.rst
@@ -0,0 +1,49 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+.. SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+
+############################
+Change-log and Release notes
+############################
+
+******
+v0.1.0
+******
+
+-  First TF-RMM source release aligned to `RMM Beta0 specification`_.
+   The specified interfaces : Realm Management Interface (RMI) and
+   Realm Service Interface (RSI) are implemented which can attest
+   and run Realm VMs as described by the `Arm CCA`_ Architecture.
+
+=================
+Upcoming features
+=================
+
+-  Support SVE, Self-Hosted Debug and PMU in Realms
+-  Support LPA2 for Stage 2 Realm translation tables.
+-  Threat model covering RMM data flows.
+-  Enable Bounded Model Checker (CBMC) for source analysis.
+-  Unit test framework based on :ref:`RMM Fake host architecture`.
+
+============================
+Known issues and limitations
+============================
+
+The following is a list of issues which are expected to be fixed in the future
+releases of TF-RMM :
+
+-  The size of ``RsiHostCall`` structure is 256 bytes in the implementation
+   and aligns to `RMM Beta1 specification`_ rather than the 4 KB size
+   specified in `RMM Beta0 specification`_.
+
+-  The RSI_IPA_STATE_GET command returns error ``RSI_ERROR_INPUT`` for a
+   `destroyed` IPA instead of emulating data abort to Host.
+
+-  The `RMM Beta0 specification`_ does not require to have a CBOR bytestream
+   wrapper around the cca-platform-token and cca-realm-delegated-token, but
+   the RMM implementation does so.
+
+---------------------------
+
+.. _RMM Beta0 specification: https://developer.arm.com/documentation/den0137/1-0bet0/?lang=en
+.. _RMM Beta1 specification: https://developer.arm.com/documentation/den0137/1-0bet1/?lang=en
+.. _Arm CCA: https://www.arm.com/architecture/security-features/arm-confidential-compute-architecture
diff --git a/docs/about/dco.rst b/docs/about/dco.rst
new file mode 100644
index 0000000..c3eba58
--- /dev/null
+++ b/docs/about/dco.rst
@@ -0,0 +1,7 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+.. SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+
+Developer Certificate of Origin
+###############################
+
+.. include:: /../DCO
diff --git a/docs/about/diagrams/cca_software_arch.png b/docs/about/diagrams/cca_software_arch.png
new file mode 100644
index 0000000..bff8fa2
--- /dev/null
+++ b/docs/about/diagrams/cca_software_arch.png
Binary files differ
diff --git a/docs/about/index.rst b/docs/about/index.rst
new file mode 100644
index 0000000..5894483
--- /dev/null
+++ b/docs/about/index.rst
@@ -0,0 +1,16 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+.. SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+
+About
+=====
+
+.. toctree::
+   :maxdepth: 1
+   :caption: Contents
+   :numbered:
+
+   /readme
+   maintainers
+   change-log
+   dco
+   license
diff --git a/docs/about/license.rst b/docs/about/license.rst
new file mode 100644
index 0000000..3c367fe
--- /dev/null
+++ b/docs/about/license.rst
@@ -0,0 +1,8 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+.. SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+
+
+License
+#######
+
+.. include:: /../LICENSE
diff --git a/docs/about/maintainers.rst b/docs/about/maintainers.rst
new file mode 100644
index 0000000..f35f457
--- /dev/null
+++ b/docs/about/maintainers.rst
@@ -0,0 +1,37 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+.. SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+
+Project Maintenance
+===================
+
+Realm Management Monitor (RMM) is an open governance community project. All
+contributions are ultimately merged by the maintainers listed below. Technical
+ownership of most parts of the codebase falls on the code owners listed
+below. An acknowledgement from these code owners is required before the
+maintainers merge a contribution.
+
+More details may be found in the `Project Maintenance Process`_ document.
+
+.. |M| replace:: **Mail**
+.. |G| replace:: **GitHub ID**
+.. |F| replace:: **Files**
+
+.. _maintainers:
+
+Maintainers
+-----------
+:|M|: Alexei Fedorov <Alexei.Fedorov@arm.com>
+:|G|: `AlexeiFedorov`_
+:|M|: Dan Handley <dan.handley@arm.com>
+:|G|: `danh-arm`_
+:|M|: Soby Mathew <soby.mathew@arm.com>
+:|G|: `soby-mathew`_
+:|M|: Javier Almansa Sobrino <javier.almansasobrino@arm.com>
+:|G|: `javier-almansasobrino`_
+
+.. _AlexeiFedorov: https://github.com/AlexeiFedorov
+.. _danh-arm: https://github.com/danh-arm
+.. _soby-mathew: https://github.com/soby-mathew
+.. _javier-almansasobrino: https://github.com/javieralso-arm/
+
+.. _Project Maintenance Process: https://developer.trustedfirmware.org/w/collaboration/project-maintenance-process/
diff --git a/docs/conf.py.in b/docs/conf.py.in
new file mode 100644
index 0000000..58e3426
--- /dev/null
+++ b/docs/conf.py.in
@@ -0,0 +1,101 @@
+# -*- coding: utf-8 -*-
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+# Configuration file for the Sphinx documentation builder.
+#
+# See the options documentation at http://www.sphinx-doc.org/en/master/config
+
+import os
+
+RMM_ROOT_DIR                = "@CMAKE_SOURCE_DIR@"
+PLANTUML_JAR_PATH           = "@PLANTUML_JAR_PATH@"
+Java_JAVA_EXECUTABLE        = "@Java_JAVA_EXECUTABLE@"
+SPHINXCFG_RMM_VERSION       = "v@CMAKE_PROJECT_VERSION@"
+
+# -- Project information -----------------------------------------------------
+
+project = 'Realm Management Monitor'
+copyright = 'TF-RMM Contributors'
+author = 'TF-RMM Contributors'
+title = 'User Guide'
+version = SPHINXCFG_RMM_VERSION
+
+# -- General configuration ---------------------------------------------------
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = ['sphinx.ext.autosectionlabel', 'sphinxcontrib.plantuml']
+
+#Location of PlantUML
+plantuml = Java_JAVA_EXECUTABLE + " -jar " + PLANTUML_JAR_PATH
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix(es) of source filenames.
+source_suffix = ['.rst']
+
+# The master toctree document.
+master_doc = 'index'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path .
+exclude_patterns = []
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# Load the contents of the global substitutions file into the 'rst_prolog'
+# variable. This ensures that the substitutions are all inserted into each page.
+with open(RMM_ROOT_DIR + '/docs/global_substitutions.txt', 'r') as subs:
+  rst_prolog = subs.read()
+
+# Minimum version of sphinx required
+needs_sphinx = '2.4'
+
+# -- Options for HTML output -------------------------------------------------
+
+# Don't show the "Built with Sphinx" footer
+html_show_sphinx = False
+
+# Show copyright info in the footer
+html_show_copyright = True
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+html_theme = "sphinx_rtd_theme"
+
+# The logo to display in the sidebar
+html_logo = RMM_ROOT_DIR + '/docs/_static/images/TrustedFirmware-Logo_standard-white.png'
+
+# Options for the "sphinx-rtd-theme" theme
+html_theme_options = {
+    'collapse_navigation': False, # Can expand and collapse sidebar entries
+    'prev_next_buttons_location': 'both', # Top and bottom of the page
+    'style_external_links': True # Display an icon next to external links
+}
+
+# Path to _static directory
+html_static_path = [RMM_ROOT_DIR + '/docs/_static']
+
+# Path to css file relative to html_static_path
+html_css_files = [RMM_ROOT_DIR + '/docs/_static/css/rmm_custom.css',]
+
+# -- Options for autosectionlabel --------------------------------------------
+
+# Only generate automatic section labels for document titles
+autosectionlabel_maxdepth = 1
+
+# -- Options for plantuml ----------------------------------------------------
+
+plantuml_output_format = 'svg_img'
diff --git a/docs/design/cold-and-warm-boot-design.rst b/docs/design/cold-and-warm-boot-design.rst
new file mode 100644
index 0000000..8729eef
--- /dev/null
+++ b/docs/design/cold-and-warm-boot-design.rst
@@ -0,0 +1,74 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+.. SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+
+#############################
+RMM Cold and Warm boot design
+#############################
+
+This section covers the boot design of RMM. The below
+diagram gives an overview of the boot flow.
+
+|Boot Design|
+
+Both warm and cold boot enters RMM at the same entry point
+``rmm_entry()``. This scheme simplifies the `boot contract between
+RMM and EL3 <rmm-el3-comms>`_. The boot args as specified by boot
+contract are stashed to high registers.
+
+The boot is divided into several phases as described below:
+
+1. **Sysreg and C runtime initialization phase.**
+
+   The essential system registers are initialized. ``SCTLR_EL2.I``
+   is set to 1 which means instruction accesses to Normal memory are
+   Outer Shareable, Inner Write-Through cacheable, Outer Write-Through
+   cacheable. ``SCTLR_EL2.C`` is also set 1 and data accesses default
+   to Device-nGnRnE. The cpu-id, received as part of boot args, is programmed
+   to ``tpidr_el2`` and this can be retrieved using the helper function
+   ``my_cpuid()``. The per-CPU stack is also initialized using the cpu-id
+   received and this completes the C runtime initialization for warm boot.
+
+   Only the primary CPU enters RMM during cold boot and a global
+   variable is used to keep track whether it is cold or warm boot. If
+   cold boot, the Global Descriptor Table (GDT) and Relocations are fixed
+   up so that RMM can run as position independent executable (PIE). The BSS
+   is zero initialized which completes the C runtime initialization
+   for cold boot.
+
+2. **Platform initialization phase**
+
+   The boot args are restored to their original registers and plat_setup()
+   and plat_warmboot_setup() are invoked for cold and warm boot respectively.
+   During cold boot, the platform is expected to consume the boot manifest
+   which is part of the `boot contract <emm-el3-comms>`_. The platform
+   initializes any platform specific peripherals and also intializes and
+   configures the translation table contexts for Stage 1.
+
+3. **MMU enable phase**
+
+   The EL2&0 translation regime is enabled after suitable TLB and cache
+   invalidations.
+
+4. **RMM Main phase**
+
+   Any cold boot or warm initialization of RMM components is done in this
+   phase. This phase also involves invoking suitable EL3 services, like
+   acquiring platform attestation token for Realm attestation.
+
+After all the phases have completed successfully, RMM issues
+``RMM_BOOT_COMPLETE`` SMC. The next entry into RMM from EL3 would be for
+handling RMI calls and hence the next intruction following the SMC call
+branches to the main SMC handler routine.
+
+
+###################################
+RMM-EL3 communication specification
+###################################
+
+The communication interface between RMM and EL3 is specified in
+`RMM-EL3 communication interface <rmm-el3-comms>`_ specification in
+TF-A repository.
+
+.. |Boot Design| image:: ./diagrams/boot_design.drawio.png
+.. _`rmm-el3-comms`: https://trustedfirmware-a.readthedocs.io/en/latest/components/rmm-el3-comms-spec.html
+
diff --git a/docs/design/diagrams/boot_design.drawio.png b/docs/design/diagrams/boot_design.drawio.png
new file mode 100644
index 0000000..fff32be
--- /dev/null
+++ b/docs/design/diagrams/boot_design.drawio.png
Binary files differ
diff --git a/docs/design/diagrams/fake_host_arch.drawio.png b/docs/design/diagrams/fake_host_arch.drawio.png
new file mode 100644
index 0000000..0b359a3
--- /dev/null
+++ b/docs/design/diagrams/fake_host_arch.drawio.png
Binary files differ
diff --git a/docs/design/diagrams/root_component_dependency.drawio.png b/docs/design/diagrams/root_component_dependency.drawio.png
new file mode 100644
index 0000000..85b5990
--- /dev/null
+++ b/docs/design/diagrams/root_component_dependency.drawio.png
Binary files differ
diff --git a/docs/design/fake-host-architecture.rst b/docs/design/fake-host-architecture.rst
new file mode 100644
index 0000000..fccba97
--- /dev/null
+++ b/docs/design/fake-host-architecture.rst
@@ -0,0 +1,108 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+.. SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+
+##########################
+RMM Fake host architecture
+##########################
+
+RMM supports building and running the program natively as a regular user-space
+application on the host machine. It achieves this by emulating the ``aarch64``
+specific parts of the program on the host machine by suitable hooks in the
+program. The implementation of the hooks can differ based on the target
+employment of running the program in this mode. Some of the foreseen
+employment scenarios of this architecture includes:
+
+1. Facilitate development of architecture independent parts of
+   RMM on the host machine.
+2. Enable unit testing of components within RMM with the benefit of
+   not having to mock all the dependencies of the component.
+3. Leverage host development environment and tools for various
+   purposes like debugging, measure code coverage, fuzz testing,
+   stress testing, runtime analysis of program etc.
+4. Enable RMM compliance testing and verification of state machine
+   and locking rules on the host machine.
+5. Profile RMM on the host machine and generate useful insights
+   for possible optimizations.
+
+We expect the fake host architecture to be developed over time in future to
+cover some of the employment scenarios described above. The current code
+may not reflect the full scope of this architecture as discussed in this
+document.
+
+The fake host architecture has some limitations:
+
+1. The architecture is not intended to support multi-thread execution.
+   The intrisics to support critical section and atomics are emulated
+   as NOP.
+2. Cannot execute AArch64 assembly code on the host due to obvious
+   reasons.
+3. Cannot emulate AArch64 exceptions during RMM execution although
+   some limited form of handling exceptions occurring in Realms can
+   probably be emulated.
+4. The program links against the native compiler libraries which enables
+   use of development and debug features available on the host machine.
+   This means the libc implementation in RMM cannot be verified using
+   this architecture.
+
+The fake host architecture config is selected by setting the config
+``RMM_ARCH=fake_host`` and the platform has to be set to a variant
+of `host` when building RMM. The different variants of the `host`
+platform allow to build RMM for each of the target employment
+scenarios as listed above.
+
+*****************************
+Fake host architecture design
+*****************************
+
+|Fake Host Architecture Diagram|
+
+
+The above figure shows the fake host architecture design.
+The architecture independent parts of RMM are linked against
+suitable host emulation blocks to enable the program to run
+on the host platform.
+
+The EL3 (monitor) emulation layer emulates the entry and exception
+from EL3 into Realm-EL2. This includes entry and exit from RMM
+as part of RMI handling, entry into RMM as part of warm/cold boot,
+and EL3 service invocations by RMM using SMC calls. Similarly the
+Realm entry/exit emulation block allows emulation of running
+a Realm. It would also allow to emulate exit from Realm due to
+synchronous or asynchronous exceptions like SMC calls, IRQs, etc.
+
+The hardware emulation block allows to emulate sysreg accesses,
+granule memory delegation and NS memory accesses needed for RMM. Since
+RMM is running as a user space application, it does not have the ability
+to map granule memory to a Virtual Address space. This capability is
+needed for the ``slot buffer`` component in RMM. Hence there is
+also need to emulate VA mapping for this case.
+
+The AArch64 intrinsics emulation block allows emulation of exclusives,
+assembly instructions for various architecture extensions, barriers and
+atomics, cache and TLB operations although most of them are defined
+as NOP at the moment.
+
+Within the RMM source tree, all files within the ``fake_host``
+folder of each component implement the necessary emulation on host.
+Depending on the target employment for the fake host
+architecture, it is necessary to adapt the behaviour of
+the emulation layer. This is facilitated by the APIs defined
+in ``host_harness.h`` header. The implementation of the API
+is done by the ``host`` platform and each variant of the ``host``
+can have a different implementation of the API suiting its
+target employment. The API also facilitates test and verification
+of the emulated property as needed by the employment.
+
+
+******************************************************************
+Fake host architecture employment scenarios implemented or ongoing
+******************************************************************
+
+This section describes the currently implemented scenarios utilizing
+the fake host architecture.
+
+1. Unit testing framework in RMM which allows testing public API of
+   components and generation of code coverage data.
+
+.. |Fake Host Architecture Diagram| image:: ./diagrams/fake_host_arch.drawio.png
+
diff --git a/docs/design/file-org-and-config.rst b/docs/design/file-org-and-config.rst
new file mode 100644
index 0000000..dfe0fea
--- /dev/null
+++ b/docs/design/file-org-and-config.rst
@@ -0,0 +1,157 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+.. SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+
+.. Custom color scheme for this document
+.. raw:: html
+
+    <style> .comp {color:DarkMagenta} </style>
+
+.. role:: comp
+
+#####################################
+RMM Folder and Component organization
+#####################################
+
+**********************************
+Root Level Folders and Components
+**********************************
+
+The root level folder structure of the RMM project is as given below.
+
+::
+
+    ┌── cmake
+    ├── configs
+    ├── docs
+    ├── drivers
+    ├── ext
+    ├── lib
+    ├── plat
+    ├── runtime
+    ├── toolchains
+    └── tools
+
+The RMM functionality is implemented by files in :comp:`lib`, :comp:`ext`,
+:comp:`drivers`, :comp:`plat` and :comp:`runtime`. Each of these folders
+corresponds to a :comp:`component` in the project. Every component has a
+defined role in implementing the RMM functionality and can in-turn be
+composed of sub-components of the same role. The components have
+their own CMakelists.txt file and a defined public API which is
+exported via the public interface of the component to its dependent
+users. The :comp:`runtime` component is an exception as it does not
+have a public API.
+
+The dependency relationship between the top level components is shown
+below :
+
+|Dependency Diagram|
+
+Each component and its role is described below :
+
+* **lib**  : This component is a library of re-usable and architectural
+  code which needs to be used by other components.
+  The :comp:`lib` component is composed of several sub-components
+  and every sub-component has a public API which is exported via its
+  public interface. The functionality implemented by the sub-component
+  is not platform specific although there could be specific static
+  configuration or platform specific data provided via defined public
+  interface. All of the sub-components in :comp:`lib` are combined into
+  a single archive file which is then included in the build.
+
+  The :comp:`lib` component depends on :comp:`ext` and :comp:`plat`
+  components. All other components in the project depend on :comp:`lib`.
+
+* **ext** : This component is meant for external source dependencies of
+  the project. The sub folders are external open source projects configured
+  as git submodules. The :comp:`ext` component is only allowed to depend on
+  libc implementation in :comp:`lib` component.
+
+* **plat** :  This component implements the platform abstraction layer or
+  platform layer for short. The platform layer has the following
+  responsibilities:
+
+  #. Implement the  platform porting API as defined in platform_api.h.
+  #. Do any necessary platform specific initialization in the platform layer.
+  #. Initialize :comp:`lib` sub-components with platform specific data.
+  #. Include any platform specific drivers from the :comp:`drivers` folder
+     and initialize them as necessary.
+
+  Every platform or a family of related platforms is expected to have a
+  folder in :comp:`plat` and only one such folder corresponding to the
+  platform will be included in the build. The :comp:`plat` component depends
+  on :comp:`lib` and any platform specific drivers in :comp:`drivers`.
+
+* **drivers** : The platform specific drivers are implemented in this
+  component. Only the `plat` component is allowed to access these drivers
+  via its public interface.
+
+* **runtime** : This component implements generic RMM functionality which
+  does not need to be shared across different components. The :comp:`runtime`
+  component does not have a public interface and is not a dependency for any
+  other component. The :comp:`runtime` is compiled into the binary
+  ``rmm.img`` after linking with other components in the build.
+
+
+**********************************
+Component File and Cmake Structure
+**********************************
+
+The below figure shows the folder organization of a typical
+component (or sub-component)
+
+::
+
+    component x
+    ├── include
+    |   └── public.h
+    ├── src
+    |   ├── private_a.h
+    |   └── src_a.c
+    ├── tests
+    |   └── test.cpp
+    └── CMakeLists.txt
+
+The ``include`` folder contains the headers exposing the public API of the
+component. The ``src`` contains the private headers and implementation
+of the intended functionality. The ``tests`` contains the tests for the
+component and the ``CMakeLists.txt`` defines the build and
+inheritance rules.
+
+A typical component ``CMakeLists.txt`` has the following structure :
+
+.. code-block:: CMake
+
+    add_library(comp-x)
+
+    # Define any static config option for this component.
+    arm_config_option()
+
+    # Pass the config option to the source files as a compile
+    # option.
+    target_compile_definitions()
+
+    # Specify any private dependencies of the component. These are not
+    # inherited by child dependencies.
+    target_link_libraries(comp-x
+        PRIVATE xxx)
+
+    # Specify any private dependencies of the component. These are
+    # inherited by child dependencies and are usually included in
+    # public API header of the component.
+    target_link_libraries(comp-x
+        PUBLIC yyy)
+
+    # Export public API via public interface of this component
+    target_include_directories(comp-x
+        PUBLIC "include")
+
+    # Specify any private headers to be included for compilation
+    # of this component.
+    target_include_directories(comp-x
+        PRIVATE "src")
+
+    # Specify source files for component
+    target_sources(comp-x
+        PRIVATE xxx)
+
+.. |Dependency Diagram| image:: ./diagrams/root_component_dependency.drawio.png
diff --git a/docs/design/index.rst b/docs/design/index.rst
new file mode 100644
index 0000000..264c841
--- /dev/null
+++ b/docs/design/index.rst
@@ -0,0 +1,15 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+.. SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+
+Design
+======
+.. toctree::
+    :maxdepth: 1
+    :caption: Contents
+    :numbered:
+
+    locking
+    file-org-and-config
+    fake-host-architecture
+    cold-and-warm-boot-design
+
diff --git a/docs/design/locking.rst b/docs/design/locking.rst
new file mode 100644
index 0000000..3ce4c5d
--- /dev/null
+++ b/docs/design/locking.rst
@@ -0,0 +1,594 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+.. SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+
+.. _locking_rmm:
+
+RMM Locking Guidelines
+=========================
+
+This document outlines the locking requirements, discusses the implementation
+and provides guidelines for a deadlock free |RMM| implementation. Further, the
+document hitherto is based upon |RMM| Alpha-05 specification and is expected to
+change as the implementation proceeds.
+
+.. _locking_intro:
+
+Introduction
+-------------
+In order to meet the requirement for the |RMM| to be small, simple to reason
+about, and to co-exist with contemporary hypervisors which are already
+designed to manage system memory, the |RMM| does not include a memory allocator.
+It instead relies on an untrusted caller providing granules of memory used
+to hold both meta data to manage realms as well as code and data for realms.
+
+To maintain confidentiality and integrity of these granules, the |RMM|
+implements memory access controls by maintaining awareness of the state of each
+granule (aka Granule State, ref :ref:`locking_impl`) and enforcing rules on how
+memory granules can transition from one state to another and how a granule can
+be used depending on its state. For example, all granules that can be accessed
+by software outside the |PAR| of a realm are in a specific state, and a granule
+that holds meta data for a realm is in another specific state that prevents it
+from being used as data in a realm and accidentally corrupted by a realm, which
+could lead to internal failure in the |RMM|.
+
+Due to this complex nature of the operations supported by the |RMM|, for example
+when managing page tables for realms, the |RMM| must be able to hold locks on
+multiple objects at the same time. It is a well known fact that holding multiple
+locks at the same time can easily lead to deadlocking the system, as for example
+illustrated by the dining philosophers problem [EWD310]_. In traditional
+operating systems software such issues are avoided by defining a partial order
+on all system objects and always acquiring a lower-ordered object before a
+higher-ordered object. This solution was shown to be correct by Dijkstra
+[EWD625]_. Solutions are typically obtained by assigning an arbitrary order
+based upon certain attributes of the objects, for example by using the memory
+address of the object.
+
+Unfortunately, software such as the |RMM| cannot use these methods directly
+because the |RMM| receives an opaque pointer from the untrusted caller and it
+cannot know before locking the object if it is indeed of the expected state.
+Furthermore, MMU page tables are hierarchical data structures and operations on
+the page tables typically must be able to locate a leaf node in the hierarchy
+based on single value (a virtual address) and therefore must walk the page
+tables in their hierarchical order. This implies an order of objects in the same
+Granule State which is not known by a process executing in the |RMM| before
+holding at least one lock on object in the page table hierarchy. An obvious
+solution to these problems would be to use a single global lock for the |RMM|,
+but that would serialize all operations across all shared data structures in the
+system and severely impact performance.
+
+
+.. _locking_reqs:
+
+Requirements
+-------------
+
+To address the synchronization needs of the |RMM| described above, we must
+employ locking and lock-free mechanisms which satisfies a number of properties.
+These are discussed below:
+
+Critical Section
+*****************
+
+A critical section can be defined as a section of code within a process that
+requires access to shared resources and that must not be executed while
+another process is in a corresponding section of code [WS2001]_.
+
+Further, access to shared resources without appropriate synchronization can lead
+to **race conditions**, which can be defined as a situation in which multiple
+threads or processes read and write a shared item and the final result depends
+on the relative timing of their execution [WS2001]_.
+
+In terms of |RMM|, an access to a shared resource can be considered as a list
+of operations/instructions in program order that either reads from or writes to
+a shared memory location (e.g. the granule data structure or the memory granule
+described by the granule data structure, ref :ref:`locking_impl`). It is also
+understood that this list of operations does not execute indefinitely, but
+eventually terminates.
+
+We can now define our desired properties as follows:
+
+Mutual Exclusion
+*****************
+
+Mutual exclusion can be defined as the requirement that when one process is in a
+critical section that accesses shared resources, no other process may be in a
+critical section that accesses any of those shared resources [WS2001]_.
+
+The following example illustrates how an implementation might enforce mutual
+exclusion of critical sections using a lock on a valid granule data structure
+`struct granule *a`:
+
+.. code-block:: C
+
+	struct granule *a;
+	bool r;
+
+	r = try_lock(a);
+	if (!r) {
+		return -ERROR;
+	}
+	critical_section(a);
+	unlock(a);
+	other_work();
+
+We note that a process might fail to perform the `lock` operation on object `a`
+and return an error or successfully acquire the lock, execute the
+`critical_section()`, `unlock()` and then continue to make forward progress to
+`other_work()` function.
+
+Deadlock Avoidance
+*******************
+
+A deadlock can be defined as a situation in which two or more processes are
+unable to proceed because each is waiting for one of the others to do something
+[WS2001]_.
+
+In other words, one or more processes are trying to enter their critical
+sections but none of them make forward progress.
+
+We can then define the deadlock avoidance property as the inverse scenario:
+
+When one or more processes are trying to enter their critical sections, at least
+one of them makes forward progress.
+
+A deadlock is a fatal event if it occurs in supervisory software such as the
+|RMM|. This must be avoided as it can render the system vulnerable to exploits
+and/or unresponsive which may lead to data loss, interrupted service and
+eventually economic loss.
+
+Starvation Avoidance
+*********************
+
+Starvation can be defined as a situation in which a runnable process is
+overlooked  indefinitely by the scheduler; although it is able to proceed, it is
+never chosen [WS2001]_.
+
+Then starvation avoidance can be defined as, all processes that are trying to
+enter their critical sections eventually make forward progress.
+
+Starvation must be avoided, because if one or more processes do not make forward
+progress, the PE on which the process runs will not perform useful work and
+will be lost to the user, resulting in similar issues like a deadlocked system.
+
+Nested Critical Sections
+*************************
+
+A critical section for an object may be nested within the critical section for
+another object for the same process.  In other words, a process may enter more
+than one critical section at the same time.
+
+For example, if the |RMM| needs to copy data from one granule to another
+granule, and must be sure that both granules can only be modified by the process
+itself, it may be implemented in the following way:
+
+.. code-block:: C
+
+	struct granule *a;
+	struct granule *b;
+	bool r;
+
+	r = try_lock(a);
+	if (!r) {
+		return -ERROR;
+	}
+
+	/* critical section for granule a -- ENTER */
+
+	r = try_lock(b);
+	if (r) {
+		/* critical section for granule b -- ENTER */
+		b->foo = a->foo;
+		/* critical section for granule b -- EXIT */
+		unlock(b);
+	}
+
+	/* critical section for granule a -- EXIT */
+	unlock(a);
+
+.. _locking_impl:
+
+Implementation
+---------------
+
+The |RMM| maintains granule states by defining a data structure for each
+memory granule in the system. Conceptually, the data structure contains the
+following fields:
+
+* Granule State
+* Lock
+* Reference Count
+
+The Lock field provides mutual exclusion of processes executing in their
+critical sections which may access the shared granule data structure and the
+shared meta data which may be stored in the memory granule which is in one of
+the |RD|, |REC|, and Table states. Both the data structure describing
+the memory granule and the contents of the memory granule itself can be accessed
+by multiple PEs concurrently and we therefore require some concurrency protocol
+to avoid corruption of shared data structures. An alternative to using a lock
+providing mutual exclusion would be to design all operations that access shared
+data structures as lock-free algorithms, but due to the complexity of the data
+structures and the operation of the |RMM| we consider this too difficult to
+accomplish in practice.
+
+The Reference Count field is used to keep track of references between granules.
+For example, an |RD| describes a realm, and a |REC| describes an execution
+context within that realm, and therefore an |RD| must always exist when a |REC|
+exists. To prevent the |RMM| from destroying an |RD| while a |REC| still exists,
+the |RMM| holds a reference count on the |RD| for each |REC| associated with the
+same realm, and only when the all the RECs in a realm have been destroyed and
+the reference count on an |RD| drops to zero, can the |RD| be destroyed and the
+granule be repurposed for other use.
+
+Based on the above, we now describe the Granule State field and the current
+locking/refcount implementation:
+
+* **UnDelegated:** These are granules for which |RMM| does not prevent the |PAS|
+  of the granule from being changed by another agent to any value.
+  In this state, the granule content access is not protected by granule::lock,
+  as it is always subject to reads and writes from Non-Realm worlds.
+
+* **Delegated:** These are granules with memory only accessible by the |RMM|.
+  The granule content is protected by granule::lock. No reference counts are
+  held on this granule state.
+
+* **Realm Descriptor (RD):** These are granules containing meta data describing
+  a realm, and only accessible by the |RMM|. Granule content access is protected
+  by granule::lock. A reference count is also held on this granule for each
+  associated |REC| granule.
+
+* **Realm Execution Context (REC):** These are granules containing meta data
+  describing a virtual PE running in a realm, and are only accessible by the
+  |RMM|. The execution content access is not protected by granule::lock, because
+  we cannot enter a realm while holding the lock. Further, the following rules
+  apply with respect to the granule's reference counts:
+
+	- A reference count is held on this granule when a |REC| is running.
+
+	- As |REC| cannot be run on two PEs at the same time, the maximum value
+	  of the reference count is one.
+
+	- When the |REC| is entered, the reference count is incremented
+	  (set to 1) atomically while granule::lock is held.
+
+	- When the |REC| exits, the reference counter is released (set to 0)
+	  atomically with store-release semantics without granule::lock being
+	  held.
+
+	- The |RMM| can access the granule's content on the entry and exit path
+	  from the |REC| while the reference is held.
+
+* **Translation Table:** These are granules containing meta data describing
+  virtual to physical address translation for the realm, accessible by the |RMM|
+  and the hardware Memory Management Unit (MMU). Granule content access is
+  protected by granule::lock, but hardware translation table walks may read the
+  RTT at any point in time. Multiple granules in this state can only be locked
+  at the same time if they are part of the same tree, and only in topological
+  order from root to leaf. The topological order of concatenated root level RTTs
+  is from the lowest address to the highest address. The complete internal
+  locking order for RTT granules is: RD -> [RTT] -> ... -> RTT. A reference
+  count is held on this granule for each entry in the RTT that refers to a
+  granule:
+
+	- Table s2tte.
+
+	- Valid s2tte.
+
+	- Valid_NS s2tte.
+
+	- Assigned s2tte.
+
+* **Data:** These are granules containing realm data, accessible by the |RMM|
+  and by the realm to which it belongs. Granule content access is not protected
+  by granule::lock, as it is always subject to reads and writes from within a
+  realm. A granule in this state is always referenced from exactly one entry in
+  an RTT granule which must be locked before locking this granule. Only a single
+  DATA granule can be locked at a time on a given PE. The complete internal
+  locking order for DATA granules is: RD -> RTT -> RTT -> ... -> DATA.
+  No reference counts are held on this granule type.
+
+
+Locking
+********
+
+The |RMM| uses spinlocks along with the object state for locking implementation.
+The lock provides similar exclusive acquire semantics known from trivial
+spinlock implementations, however also allows verification of whether the locked
+object is of an expected state.
+
+The data structure for the spinlock can be described in C as follows:
+
+.. code-block:: C
+
+	typedef struct {
+		unsigned int val;
+	} spinlock_t;
+
+This data structure can be embedded in any object that requires synchronization
+of access, such as the `struct granule` described above.
+
+The following operations are defined on spinlocks:
+
+.. code-block:: C
+	:caption: **Typical spinlock operations**
+
+	/*
+	 * Locks a spinlock with acquire memory ordering semantics or goes into
+	 * a tight loop (spins) and repeatedly checks the lock variable
+	 * atomically until it becomes available.
+	 */
+	void spinlock_acquire(spinlock_t *l);
+
+	/*
+	 * Unlocks a spinlock with release memory ordering semantics. Must only
+	 * be called if the calling PE already holds the lock.
+	 */
+	void spinlock_release(spinlock_t *l);
+
+
+The above functions should not be directly used for locking/unlocking granules,
+instead the following should be used:
+
+.. code-block:: C
+	:caption: **Granule locking operations**
+
+	/*
+	 * Acquires a lock (or spins until the lock is available), then checks
+	 * if the granule is in the `expected_state`. If the `expected_state`
+	 * is matched, then returns `true`. Otherwise, releases the lock and
+	 * returns `false`.
+	 */
+	bool granule_lock_on_state_match(struct granule *g,
+					 enum granule_state expected_state);
+
+	/*
+	 * Used when we're certain of the state of an object (e.g. because we
+	 * hold a reference to it) or when locking objects whose reference is
+	 * obtained from another object, after that objects is locked.
+	 */
+	void granule_lock(struct granule *g,
+			  enum granule_state expected_state);
+
+	/*
+	 * Obtains a pointer to a locked granule at `addr` if `addr` is a valid
+	 * granule physical address and the state of the granule at `addr` is
+	 * `expected_state`.
+	 */
+	struct granule *find_lock_granule(unsigned long addr,
+					  enum granule_state expected_state);
+
+	/* Find two granules and lock them in order of their address. */
+	return_code_t find_lock_two_granules(unsigned long addr1,
+					     enum granule_state expected_state1,
+					     struct granule **g1,
+					     unsigned long addr2,
+					     enum granule_state expected_state2,
+					     struct granule **g2);
+
+	/*
+	 * Obtain a pointer to a locked granule at `addr` which is unused
+	 * (refcount = 0), if `addr` is a valid granule physical address and the
+	 * state of the granule at `addr` is `expected_state`.
+	 */
+	struct granule *find_lock_unused_granule(unsigned long addr,
+						 enum granule_state
+						 expected_state);
+
+.. code-block:: C
+	:caption: **Granule unlocking operations**
+
+	/*
+	 * Release a spinlock held on a granule. Must only be called if the
+	 * calling PE already holds the lock.
+	 */
+	void granule_unlock(struct granule *g);
+
+	/*
+	 * Sets the state and releases a spinlock held on a granule. Must only
+	 * be called if the calling PE already holds the lock.
+	 */
+	void granule_unlock_transition(struct granule *g,
+				       enum granule_state new_state);
+
+
+Reference Counting
+*******************
+
+The reference count is implemented using the **refcount** variable within the
+granule structure to keep track of the references in between granules. For
+example, the refcount is used to prevent changes to the attributes of a parent
+granule which is referenced by child granules, ie. a parent with refcount not
+equal to zero.
+
+Race conditions on the refcount variable are avoided by either locking the
+granule before accessing the variable or by lock-free mechanisms such as
+Single-Copy Atomic operations along with ARM weakly ordered
+ACQUIRE/RELEASE/RELAXED memory semantics to synchronize shared resources.
+
+The following operations are defined on refcount:
+
+.. code-block:: C
+	:caption: **Read a refcount value**
+
+	/*
+	 * Single-copy atomic read of refcount variable with RELAXED memory
+	 * ordering semantics. Use this function if lock-free access to the
+	 * refcount is required with relaxed memory ordering constraints applied
+	 * at that point.
+	 */
+	unsigned long granule_refcount_read_relaxed(struct granule *g);
+
+	/*
+	 * Single-copy atomic read of refcount variable with ACQUIRE memory
+	 * ordering semantics. Use this function if lock-free access to the
+	 * refcount is required with acquire memory ordering constraints applied
+	 * at that point.
+	 */
+	unsigned long granule_refcount_read_acquire(struct granule *g);
+
+.. code-block:: C
+	:caption: **Increment a refcount value**
+
+	/*
+	 * Increments the granule refcount. Must be called with the granule
+	 * lock held.
+	 */
+	void __granule_get(struct granule *g);
+
+	/*
+	 * Increments the granule refcount by `val`. Must be called with the
+	 * granule lock held.
+	 */
+	void __granule_refcount_inc(struct granule *g, unsigned long val);
+
+	/* Atomically increments the reference counter of the granule.*/
+	void atomic_granule_get(struct granule *g);
+
+
+.. code-block:: C
+	:caption: **Decrement a refcount value**
+
+	/*
+	 * Decrements the granule refcount. Must be called with the granule
+	 * lock held.
+	 */
+	void __granule_put(struct granule *g);
+
+	/*
+	 * Decrements the granule refcount by `val`. Asserts if refcount can
+	 * become negative. Must be called with the granule lock held.
+	 */
+	void __granule_refcount_dec(struct granule *g, unsigned long val);
+
+	/* Atomically decrements the reference counter of the granule. */
+	void atomic_granule_put(struct granule *g);
+
+	/*
+	 * Atomically decrements the reference counter of the granule. Stores to
+	 * memory with RELEASE semantics.
+	 */
+	void atomic_granule_put_release(struct granule *g);
+
+.. code-block:: C
+	:caption: **Directly access refcount value**
+
+	/*
+	 * Directly reads/writes the refcount variable. Must be called with the
+	 * granule lock held.
+	 */
+	granule->refcount;
+
+.. _locking_guidelines:
+
+Guidelines
+-----------
+
+In order to meet the :ref:`locking_reqs` discussed above, this section
+stipulates some locking and lock-free algorithm implementation guidelines for
+developers.
+
+Mutual Exclusion
+*****************
+
+The spinlock, acquire/release and atomic operations provide trivial mutual
+exclusion implementations for |RMM|. However, the following general guidelines
+should be taken into consideration:
+
+	- Appropriate deadlock avoidance techniques should be incorporated when
+	  using multiple locks.
+
+	- Lock-free access to shared resources should be atomic.
+
+	- Memory ordering constraints should be used prudently to avoid
+	  performance degradation. For e.g. on an unlocked granule (e.g. REC),
+	  prior to the refcount update, if there are associated memory
+	  operations, then the update should be done with release semantics.
+	  However, if there are no associated memory accesses to the granule
+	  prior to the refcount update then release semantics will not be
+	  required.
+
+
+Deadlock Avoidance
+******************
+
+Deadlock avoidance is provided by defining a partial order on all objects in the
+system where the locking operation will eventually fail if the caller tries to
+acquire a lock of a different state object than expected. This means that no
+two processes will be expected to acquire locks in a different order than the
+defined partial order, and we can rely on the same reasoning for deadlock
+avoidance as shown by Dijkstra [EWD625]_.
+
+To establish this partial order, the objects referenced by |RMM| can be
+classified into two categories:
+
+#. **External**: A granule state belongs to the `external` class iff _any_
+   parameter in _any_ RMI command is an address of a granule which is expected
+   to be in that state. The following granule states are `external`:
+
+	- GRANULE_STATE_NS
+	- GRANULE_STATE_DELEGATED
+	- GRANULE_STATE_RD
+	- GRANULE_STATE_REC
+
+#. **Internal**: A granule state belongs to the `internal` class iff it is not
+   an `external`. These are objects which are referenced from another
+   object after that object is locked. Each `internal` object should be
+   referenced from exactly one place. The following granule states are
+   `internal`:
+
+	- GRANULE_STATE_RTT
+	- GRANULE_STATE_DATA
+
+We now state the locking guidelines for |RMM| as:
+
+#. Granules expected to be in an `external` state must be locked before locking
+   any granules in an `internal` state.
+
+#. Granules expected to be in an `external` state must be locked in order of
+   their physical address, starting with the lowest address.
+
+#. Once a granule expected to be in an `external` state has been locked, its
+   state must be checked against the expected state. If these do not match, the
+   granule must be unlocked and no further granules may be locked within the
+   currently-executing RMM command.
+
+#. Granules in an `internal` state must be locked in order of state:
+
+	- `RTT`
+	- `DATA`
+
+#. Granules in the same `internal` state must be locked in the
+   :ref:`locking_impl` defined order for that specific state.
+
+#. A granule's state can be changed iff the granule is locked and the reference
+   count is zero.
+
+Starvation Avoidance
+********************
+
+Currently, the lock-free implementation for RMI.REC.Enter provides Starvation
+Avoidance in |RMM|. However, for the locking implementation, Starvation
+Avoidance is yet to be accomplished. This can be added by a ticket or MCS style
+locking implementation [MCS]_.
+
+Nested Critical Sections
+************************
+
+Spinlocks provide support for nested critical sections. Processes can acquire
+multiple spinlocks at the same time, as long as the locking order is not
+violated.
+
+References
+----------
+
+.. [EWD310] Dijkstra, E.W. Hierarchical ordering of sequential processes.
+	EWD 310.
+
+.. [EWD625] Dijkstra, E.W. Two starvation free solutions to a general exclusion
+	problem. EWD 625.
+
+.. [MCS] Mellor-Crummey, John M. and Scott, Michael L. Algorithms for scalable
+	synchronization on shared-memory multiprocessors. ACM TOCS, Volume 9,
+	Issue 1, Feb. 1991.
+
+.. [WS2001] Stallings, W. (2001). Operating systems: Internals and design
+	principles. Upper Saddle River, N.J: Prentice Hall.
diff --git a/docs/getting_started/build-options.rst b/docs/getting_started/build-options.rst
new file mode 100644
index 0000000..ce58ea9
--- /dev/null
+++ b/docs/getting_started/build-options.rst
@@ -0,0 +1,223 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+.. SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+
+
+.. _build_options_examples:
+
+#####################
+RMM Build Examples
+#####################
+
+The |RMM| supports a wide range of build configuration options. Some of these options
+are more regularly exercised by developers, while others are for **advanced** and
+**experimental** usage only.
+
+|RMM| can be built using either GNU(GCC) or :ref:`LLVM(Clang)<llvm_build>`
+toolchain. See :ref:`this section<getting_started_toolchain>` for toolchain
+setup and the supported versions.
+
+The build is performed in 2 stages:
+
+**Configure Stage:** In this stage, a default config file can be specified which configures
+a sane config for the chosen platform. If this default config needs to be modified, it is
+recommended to first perform a default config and then modify using the cmake ncurses as
+shown in :ref:`CMake UI Example<build_config_example>`.
+
+**Build Stage:** In this stage, the source build is performed by specifying the `--build` option.
+See any of the commands below for an example.
+
+.. note::
+
+    It is recommended to clean build if any of the build options are changed from previous build.
+
+Below are some of the typical build and configuration examples frequently used in |RMM| development
+for the FVP Platform. Detailed configuration options are described :ref:`here<build_options_table>`.
+
+RMM also supports a ``fake_host`` build which can be used to build RMM for test
+and code analysis on the host machine. See
+:ref:`this section here<fake_host_build>` for more details.
+
+1. Perform an initial default build with minimum configuration options:
+
+Build using gnu toolchain
+
+.. code-block:: bash
+
+    cmake -DRMM_CONFIG=fvp_defcfg -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR}
+    cmake --build ${RMM_BUILD_DIR}
+
+Build using LLVM toolchain
+
+.. code-block:: bash
+
+    cmake -DRMM_CONFIG=fvp_defcfg -DRMM_TOOLCHAIN=llvm -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR}
+    cmake --build ${RMM_BUILD_DIR}
+
+.. _build_config_example:
+
+2. Perform an initial default config, then modify using ccmake ncurses UI:
+
+.. code-block:: bash
+
+    cmake -DRMM_CONFIG=fvp_defcfg -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR}
+    ccmake -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR}
+    cmake --build ${RMM_BUILD_DIR}
+
+3. Perform a debug build and specify a log level:
+
+.. code-block:: bash
+
+    cmake -DRMM_CONFIG=fvp_defcfg -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR} -DCMAKE_BUILD_TYPE=Debug -DLOG_LEVEL=50
+    cmake --build ${RMM_BUILD_DIR}
+
+4. Perform a documentation build:
+
+.. code-block:: bash
+
+    export PLANTUML_JAR_PATH=<install path>/bin/plantuml/plantuml.jar
+    cmake -DRMM_CONFIG=fvp_defcfg -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR} -DRMM_DOCS=ON
+    cmake --build ${RMM_BUILD_DIR} -- docs
+
+5. Perform a clean verbose build:
+
+.. code-block:: bash
+
+    cmake -DRMM_CONFIG=fvp_defcfg -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR}
+    cmake --build ${RMM_BUILD_DIR} --clean-first --verbose
+
+6. Perform a build with Ninja Genenerator:
+
+.. code-block:: bash
+
+    cmake -DRMM_CONFIG=fvp_defcfg -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -G "Ninja" -DLOG_LEVEL=50
+    cmake --build ${RMM_BUILD_DIR}
+
+7. Perform a build with Ninja Multi Config Genenerator:
+
+.. code-block:: bash
+
+    cmake -DRMM_CONFIG=fvp_defcfg -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR} -G "Ninja Multi-Config" -DLOG_LEVEL=50
+    cmake --build ${RMM_BUILD_DIR} --config ${BUILD_TYPE}
+
+8. Perform a Cppcheck static analysis:
+
+.. code-block:: bash
+
+    cmake -DRMM_CONFIG=fvp_defcfg -DRMM_STATIC_ANALYSIS_CPPCHECK=ON -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR}
+    cmake --build ${RMM_BUILD_DIR} -- cppcheck
+    cat ${BUILD_DIR}/tools/cppcheck/cppcheck.xml
+
+9. Perform a Cppcheck static analysis with CERT_C/MISRA/THREAD SAFETY (example with MISRA):
+
+.. code-block:: bash
+
+    cmake -DRMM_CONFIG=fvp_defcfg -DRMM_STATIC_ANALYSIS_CPPCHECK=ON -DRMM_STATIC_ANALYSIS_CPPCHECK_CHECKER_MISRA=ON -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR}
+    cmake --build ${RMM_BUILD_DIR} -- cppcheck
+    cat ${BUILD_DIR}/tools/cppcheck/cppcheck.xml
+
+10. Perform a checkpatch analysis:
+
+Run checkpatch on commits in the current branch against BASE_COMMIT (default origin/master):
+
+.. code-block:: bash
+
+    cmake -DRMM_CONFIG=fvp_defcfg -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR}
+    cmake --build ${RMM_BUILD_DIR} -- checkpatch
+
+Run checkpatch on entire codebase:
+
+.. code-block:: bash
+
+    cmake -DRMM_CONFIG=fvp_defcfg -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR}
+    cmake --build ${RMM_BUILD_DIR} -- checkcodebase
+
+11. Perform a checkspdx analysis:
+
+Run checkspdx on commits in the current branch against BASE_COMMIT (default origin/master):
+
+.. code-block:: bash
+
+    cmake -DRMM_CONFIG=fvp_defcfg -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR}
+    cmake --build ${RMM_BUILD_DIR} -- checkspdx-patch
+
+Run checkspdx on entire codebase:
+
+.. code-block:: bash
+
+    cmake -DRMM_CONFIG=fvp_defcfg -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR}
+    cmake --build ${RMM_BUILD_DIR} -- checkspdx-codebase
+
+13. Check header file include order:
+
+Run checkincludes-patch on commits in the current branch against BASE_COMMIT (default origin/master):
+
+.. code-block:: bash
+
+    cmake -DRMM_CONFIG=fvp_defcfg -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR}
+    cmake --build ${RMM_BUILD_DIR} -- checkincludes-patch
+
+Run checkincludes on entire codebase:
+
+.. code-block:: bash
+
+    cmake -DRMM_CONFIG=fvp_defcfg -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR}
+    cmake --build ${RMM_BUILD_DIR} -- checkincludes-codebase
+
+.. _build_options_table:
+
+###################
+RMM Build Options
+###################
+
+The |RMM| build system supports the following CMake build options.
+
+.. csv-table:: RMM CMake Options Table
+   :header: "Option", "Valid values", "Default", "Description"
+
+   RMM_CONFIG			,			,			,"Platform build configuration, eg: fvp_defcfg for the FVP"
+   RMM_ARCH			,aarch64 | fake_host	,aarch64		,"Target Architecture for RMM build"
+   RMM_MAX_SIZE			,			,0x0			,"Maximum size for RMM image"
+   MAX_CPUS			,			,16			,"Maximum number of CPUs supported by RMM"
+   GRANULE_SIZE			,			,4096			,"Granule Size used by RMM"
+   RMM_DOCS			,ON | OFF		,OFF			,"RMM Documentation build"
+   CMAKE_BUILD_TYPE		,Debug | Release	,Release		,"CMake Build type"
+   CMAKE_CONFIGURATION_TYPES	,Debug & Release	,Debug & Release	,"Multi-generator configuration types"
+   CMAKE_DEFAULT_BUILD_TYPE	,Debug | Release	,Release		,"Default multi-generator configuration type"
+   MbedTLS_BUILD_TYPE		,Debug | Release	,Release		,"MbedTLS build type"
+   RMM_PLATFORM			,fvp | host		,			,"Platform to build"
+   RMM_TOOLCHAIN		,gnu | llvm		,			,"Toolchain name"
+   LOG_LEVEL			,			,40			,"Log level to apply for RMM (0 - 50)"
+   RMM_STATIC_ANALYSIS		,			,			,"Enable static analysis checkers"
+   RMM_STATIC_ANALYSIS_CPPCHECK				,ON | OFF	,ON	,"Enable Cppcheck static analysis"
+   RMM_STATIC_ANALYSIS_CPPCHECK_CHECKER_CERT_C		,ON | OFF	,ON	,"Enable Cppcheck's SEI CERT C checker"
+   RMM_STATIC_ANALYSIS_CPPCHECK_CHECKER_MISRA		,ON | OFF	,ON	,"Enable Cppcheck's MISRA C:2012 checker"
+   RMM_STATIC_ANALYSIS_CPPCHECK_CHECKER_THREAD_SAFETY	,ON | OFF	,ON	,"Enable Cppcheck's thread safety checker"
+   RMM_UART_ADDR		,			,0x0			,"Base addr of UART to be used for RMM logs"
+   PLAT_CMN_CTX_MAX_XLAT_TABLES ,			,0			,"Maximum number of translation tables used by the runtime context"
+   PLAT_CMN_MAX_MMAP_REGIONS    ,                       ,5                      ,"Maximum number of mmap regions to be allocated for the platform"
+   RMM_NUM_PAGES_PER_STACK	,			,3			,"Number of pages to use per CPU stack"
+   MBEDTLS_ECP_MAX_OPS		,248 -			,1000			,"Number of max operations per ECC signing iteration"
+   RMM_FPU_USE_AT_REL2		,ON | OFF		,OFF(fake_host) ON(aarch64),"Enable FPU/SIMD usage in RMM."
+   RMM_MAX_GRANULES		,			,0			,"Maximum number of memory granules available to the system"
+
+
+
+.. _llvm_build:
+
+################
+RMM LLVM Build
+################
+
+RMM can be built using LLVM Toolchain (Clang). To build using LLVM
+toolchain, set RMM_TOOLCHAIN=llvm during configuration stage.
+
+.. _fake_host_build:
+
+#####################
+RMM Fake Host Build
+#####################
+
+RMM also provides a ``fake_host`` target architecture which allows the code to
+be built natively on the host using the host toolchain. To build for
+``fake_host`` architecture, set RMM_CONFIG=host_defcfg during the
+configuration stage.
diff --git a/docs/getting_started/getting-started.rst b/docs/getting_started/getting-started.rst
new file mode 100644
index 0000000..827c66f
--- /dev/null
+++ b/docs/getting_started/getting-started.rst
@@ -0,0 +1,256 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+.. SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+
+#############
+Prerequisite
+#############
+
+This document describes the software requirements for building |RMM| for AArch64 target platforms.
+
+It may possible to build |RMM| with combinations of software packages that are different from
+those listed below, however only the software described in this document can be officially supported.
+
+###########
+Build Host
+###########
+
+The |RMM| officially supports a limited set of build environments and setups.
+In this context, official support means that the environments listed below
+are actively used by team members and active developers, hence users should
+be able to recreate the same configurations by following the instructions
+described below. In case of problems, the |RMM| team provides support only
+for these environments, but building in other environments can still be
+possible.
+
+A relatively recent Linux distribution is recommended for building RMM. We
+have performed tests using Ubuntu 18.04 LTS (64-bit) but other distributions
+should also work fine as a base, provided that the necessary tools and
+libraries can be installed.
+
+##########################
+Tool & Dependency overview
+##########################
+
+The following tools are required to obtain and build |RMM|:
+
+.. csv-table:: Tool dependencies
+   :header: "Name", "Version", "Component"
+
+   "C compiler", see :ref:`getting_started_toolchain` ,"Firmware"
+   "CMake", ">=3.15.0", "Firmware, Documentation"
+   "GNU Make", ">4.0", "Firmware, Documentation"
+   "Python",3.x,"Firmware, Documentation"
+   "Perl",>=5.26,"Firmware, Documentation"
+   "ninja-build",,"Firmware (using Ninja Generator)"
+   "Sphinx",">=2.4,<3.0.0","Documentation"
+   "sphinxcontrib-plantuml",,"Documentation"
+   "sphinx-rtd-theme",,"Documentation"
+   "Git",, "Firmware, Documentation"
+   "Graphviz dot",">v2.38.0","Documentation"
+   "docutils",">v2.38.0","Documentation"
+   "JRE",">=10.0","Documentation"
+   "plantuml",,"Documentation"
+
+.. _getting_started_toolchain:
+
+###############
+Setup Toolchain
+###############
+
+To compile |RMM| code for an AArch64 target, at least one of the
+supported AArch64 toolchains have to be available in the
+build environment.
+
+Currently, the following compilers are supported:
+
+- GCC (aarch64-none-elf-) >= 10.2-2020.11 (from the `Arm Developer website`_)
+- Clang+LLVM >= 14.0.0 (from the `LLVM Releases website`_)
+
+The respective compiler binary must be found in the shell's search path.
+Be sure to add the bin/ directory if you have downloaded a binary version.
+The toolchain to use can be set using ``RMM_TOOLCHAIN`` parameter and can
+be set to either `llvm` or `gnu`. The default toolchain is `gnu`.
+
+For non-native AArch64 target build, the ``CROSS_COMPILE`` environment
+variable must contain the right target triplet corresponding to the AArch64
+GCC compiler. Below is an example when RMM is to be built for AArch64 target
+on a non-native host machine and using GCC as the toolchain.
+
+    .. code-block:: bash
+
+      export CROSS_COMPILE=aarch64-none-elf-
+      export PATH=<path-to-aarch64-gcc>/bin:$PATH
+
+Please note that AArch64 GCC must be included in the shell's search path
+even when using Clang as the compiler as LLVM does not include some C
+standard headers like `stdlib.h` and needs to be picked up from the
+`include` folder of the AArch64 GCC. Below is an example when RMM is
+to be built for AArch64 target on a non-native host machine and using
+LLVM as the toolchain.
+
+    .. code-block:: bash
+
+      export CROSS_COMPILE=aarch64-none-elf-
+      export PATH=<path-to-aarch64-gcc>/bin:<path-to-clang+llvm>/bin:$PATH
+
+The ``CROSS_COMPILE`` variable is ignored for ``fake_host`` build and
+the native host toolchain is used for the build.
+
+#######################################
+Package Installation (Ubuntu-18.04 x64)
+#######################################
+
+If you are using the recommended Ubuntu distribution then we can install the
+required packages with the following commands:
+
+1. Install dependencies:
+
+.. code:: shell
+
+    sudo apt-get install -y git build-essential python3 python3-pip make ninja-build default-jre
+    sudo snap install cmake
+    wget https://github.com/plantuml/plantuml/releases/download/v1.2022.7/plantuml-1.2022.7.jar -P <plantuml install path>
+
+.. note::
+
+    platUML and JRE are only required for documentation build.
+
+2. Verify cmake version:
+
+.. code-block:: bash
+
+    cmake --version
+
+.. note::
+
+    Please download cmake 3.19 or later version from https://cmake.org/download/.
+
+3. Add CMake and platUML path into environment:
+
+.. code-block:: bash
+
+    export PATH=<CMake path>/bin:$PATH
+    export PLANTUML_JAR_PATH=<plantuml install path>/plantuml.jar
+
+###########################
+Install python dependencies
+###########################
+
+.. note::
+
+    The installation of Python dependencies is an optional step. This is required only
+    if building documentation.
+
+RMM's ``tools/requirements.txt`` file declares additional Python dependencies.
+Install them with ``pip3``:
+
+.. code-block:: bash
+
+    pip3 install --upgrade pip
+    cd <rmm source folder>
+    pip3 install -r tools/requirements.txt
+
+.. _getting_started_get_source:
+
+#########################
+Getting the RMM Source
+#########################
+
+Source code for |RMM| is maintained in a Git repository hosted on TrustedFirmware.org.
+To clone this repository from the server, run the following in your shell:
+
+.. code-block:: bash
+
+    git clone --recursive https://git.trustedfirmware.org/TF-RMM/tf-rmm.git
+
+Additional steps for Contributors
+*********************************
+
+If you are planning on contributing back to RMM, your commits need to
+include a ``Change-Id`` footer as explained in :ref:`mandated-trailers`.
+This footer is generated by a Git hook that needs to be installed
+inside your cloned RMM source folder.
+
+The `TF-RMM Gerrit page`_ under trustedfirmware.org contains a
+*Clone with commit-msg hook* subsection under its **Download** header where
+you can copy the command to clone the repo with the required git hooks.
+
+If needed, you can also manually install the hooks separately on an existing
+repo:
+
+.. code:: shell
+
+    curl -Lo $(git rev-parse --git-dir)/hooks/commit-msg https://review.trustedfirmware.org/tools/hooks/commit-msg
+    chmod +x $(git rev-parse --git-dir)/hooks/commit-msg
+
+You can read more about Git hooks in the *githooks* page of the `Git hooks
+documentation`_.
+
+#################################
+Install Cppcheck and dependencies
+#################################
+
+.. note::
+
+    The installation of Cppcheck is an optional step. This is required only
+    if using the Cppcheck static analysis.
+
+Follow the public documentation to install Cppcheck either from the official
+website https://cppcheck.sourceforge.io/#download or from the official github
+https://github.com/danmar/cppcheck/
+
+If you own a valid copy of a MISRA rules file:
+
+.. code-block:: bash
+
+    sudo mkdir /usr/local/share/Cppcheck/misra
+    sudo cp -a <path to the misra rules file>/<file name> /usr/local/share/Cppcheck/misra/misra.rules
+
+###########################
+Performing an Initial Build
+###########################
+
+The |RMM| sources can be compiled using multiple CMake options.
+
+For detailed instructions on build configurations and examples
+see :ref:`build_options_examples`.
+
+A typical build command for the FVP platform using GCC toolchain
+is shown below:
+
+.. code-block:: bash
+
+    cmake -DRMM_CONFIG=fvp_defcfg -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR}
+    cmake --build ${RMM_BUILD_DIR}
+
+###############
+Running the RMM
+###############
+
+The |RMM| is part of the CCA software stack and relies on EL3 Firmware to load
+the binary at boot time appropriately. It needs both EL3 Firmware and
+Non-Secure Host to be present at runtime for its functionality. The EL3
+Firmware must comply to `RMM-EL3 Communication Specification`_ and is
+typically the `TF-A`_. The Non-Secure Host can be an RME aware hypervisor
+or an appropriate Test utility running in Non-Secure world which can interact
+with |RMM| via Realm Management Interface (RMI).
+
+The `TF-A`_ project includes build and run instructions for an RME enabled
+system on the FVP platform as part of `TF-A RME documentation`_.
+The ``rmm.img`` binary is provided to the TF-A bootloader to be packaged
+in FIP using ``RMM`` build option in `TF-A`_.
+
+If |RMM| is built for the `fake_host` architecture
+(see :ref:`RMM Fake Host Build`), then the generated `rmm.elf` binary can
+run natively on the Host machine. It does this by emulating parts of the system
+as described in :ref:`RMM Fake host architecture` design.
+
+-----
+
+.. _Arm Developer website: https://developer.arm.com/open-source/gnu-toolchain/gnu-a/downloads
+.. _LLVM Releases website: https://releases.llvm.org/
+.. _RMM-EL3 Communication Specification: https://trustedfirmware-a.readthedocs.io/en/latest/components/rmm-el3-comms-spec.html
+.. _TF-A: https://www.trustedfirmware.org/projects/tf-a/
+.. _TF-A RME documentation: https://trustedfirmware-a.readthedocs.io/en/latest/components/realm-management-extension.html
+.. _TF-RMM Gerrit page: https://review.trustedfirmware.org/admin/repos/TF-RMM/tf-rmm
+.. _Git hooks documentation:  https://git-scm.com/docs/githooks
diff --git a/docs/getting_started/index.rst b/docs/getting_started/index.rst
new file mode 100644
index 0000000..b9d1cb3
--- /dev/null
+++ b/docs/getting_started/index.rst
@@ -0,0 +1,12 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+.. SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+
+Getting Started Guides
+======================
+.. toctree::
+    :maxdepth: 1
+    :caption: Contents
+    :numbered:
+
+    getting-started
+    build-options
diff --git a/docs/global_substitutions.txt b/docs/global_substitutions.txt
new file mode 100644
index 0000000..aeac5a3
--- /dev/null
+++ b/docs/global_substitutions.txt
@@ -0,0 +1,7 @@
+.. |TF-A| replace:: :term:`TF-A`
+.. |RMM| replace:: :term:`RMM`
+.. |AArch64| replace:: :term:`AArch64`
+.. |REC| replace:: :term:`REC`
+.. |RD| replace:: :term:`RD`
+.. |PAR| replace:: :term:`PAR`
+.. |PAS| replace:: :term:`PAS`
diff --git a/docs/glossary.rst b/docs/glossary.rst
new file mode 100644
index 0000000..fd36399
--- /dev/null
+++ b/docs/glossary.rst
@@ -0,0 +1,36 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+.. SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+
+Glossary
+========
+
+This glossary provides definitions for terms and abbreviations used in the RMM
+documentation.
+
+You can find additional definitions in the `Arm Glossary`_.
+
+.. glossary::
+   :sorted:
+
+   AArch64
+      64-bit execution state of the ARMv8 ISA
+
+   TF-A
+      Trusted Firmware-A
+
+   RMM
+      Realm Management Monitor
+
+   REC
+      Realm Execution Context
+
+   RD
+      Realm Descriptor
+
+   PAR
+      Protected Address Range
+
+   PAS
+      Physical Address Space
+
+.. _`Arm Glossary`: https://developer.arm.com/support/arm-glossary
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 0000000..ec6bb6e
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,49 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+.. SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+
+Realm Management Monitor Documentation
+=======================================
+
+.. raw:: html
+
+    <ul class="grid">
+        <li class="grid-item">
+            <a href="readme.html">
+                <img alt="" src="_static/images/rmm-introduction.png"/>
+                <h2>Introduction</h2>
+            </a>
+            <p>Introduction to the Realm Management Monitor Project.</p>
+        </li>
+        <li class="grid-item">
+            <a href="getting_started/index.html">
+               <img alt="" src="_static/images/rmm-documentation.png"/>
+               <h2>Getting Started</h2>
+            </a>
+            <p>Guide to set up development environment and evaluate RMM.</p>
+        </li>
+         <li class="grid-item">
+            <a href="process/index.html">
+               <img alt="" src="_static/images/rmm-documentation.png"/>
+               <h2>Process</h2>
+            </a>
+            <p>Process followed in the project and contribution guidelines.</p>
+        </li>
+        <li class="grid-item">
+            <a href="design/index.html">
+               <img alt="" src="_static/images/rmm-documentation.png"/>
+               <h2>Design</h2>
+            </a>
+            <p>Design documentation for RMM.</p>
+        </li>
+    </ul>
+
+.. toctree::
+   :maxdepth: 1
+   :hidden:
+
+   Home<self>
+   about/index
+   getting_started/index
+   process/index
+   design/index
+   glossary
diff --git a/docs/process/coding-standard.rst b/docs/process/coding-standard.rst
new file mode 100644
index 0000000..92c0816
--- /dev/null
+++ b/docs/process/coding-standard.rst
@@ -0,0 +1,645 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+.. SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+
+Coding Standard
+===============
+
+This document describes the coding rules to follow to contribute to the project.
+
+General
+-------
+
+The following coding standard is derived from `MISRA C:2012 Guidelines`_,
+`TF-A coding style`_ and `Linux kernel coding style`_ coding standards.
+
+File Encoding
+-------------
+
+The source code must use the **UTF-8** character encoding. Comments and
+documentation may use non-ASCII characters when required (e.g. Greek letters
+used for units) but code itself is still limited to ASCII characters.
+
+Language
+--------
+
+The primary language for comments and naming must be International English. In
+cases where there is a conflict between the American English and British English
+spellings of a word, the American English spelling is used.
+
+Exceptions are made when referring directly to something that does not use
+international style, such as the name of a company. In these cases the existing
+name should be used as-is.
+
+C Language Standard
+-------------------
+
+The C language mode used for |RMM| is *GNU11*. This is the "GNU dialect of ISO
+C11", which implies the *ISO C11* standard with GNU extensions.
+
+Both GCC and Clang compilers have support for *GNU11* mode, though
+Clang does lack support for a small number of GNU extensions. These
+missing extensions are rarely used, however, and should not pose a problem.
+
+Length
+------
+
+- Each file, function and scopes should have a logical uniting theme.
+
+  No length limit is set for a file.
+
+- A function should be 24 lines maximum.
+
+  This will not be enforced, any function being longer should trigger a
+  discussion during the review process.
+
+- A line must be <= 80 characters, except for string literals as it would make
+  any search for it more difficult.
+
+- A variable should not be longer than 31 characters.
+
+  Although the `C11 specification`_ specifies that the number of signitificant
+  characters in an identifier is implementation defined it sets the translation
+  limit to the 31 initial characters.
+
++--------------+-----------------------------------+
+|   TYPE       |             LIMIT                 |
++==============+===================================+
+|   function   |     24 lines (not enforced)       |
++--------------+-----------------------------------+
+|     line     |          80 characters            |
++--------------+-----------------------------------+
+|  identifier  |          31 characters            |
++--------------+-----------------------------------+
+
+
+Headers/Footers
+---------------
+
+- Include guards:
+
+.. code:: c
+
+   #ifndef FILE_NAME_H
+   #define FILE_NAME_H
+
+   <header content>
+
+   #endif /* FILE_NAME_H */
+
+- Include statement variant is <>:
+
+.. code:: c
+
+   #include <file.h>
+
+
+- Include files should be alphabetically ordered:
+
+.. code:: c
+
+   #include <axxxx.h>
+   #include <bxxxx.h>
+   [...]
+   #include <zxxxx.h>
+
+- If possible, use forward declaration of struct types in public headers.
+  This will reduce interdependence of header file inclusion.
+
+.. code:: c
+
+   #include <axxxx.h>
+   #include <bxxxx.h>
+   [...]
+   /* forward declaration */
+   struct x;
+   void foo(struct *x);
+
+
+Naming conventions
+------------------
+
+- Case:
+  Functions and variables must be in Snake Case
+
+.. code:: c
+
+  unsigned int my_snake_case_variable = 0U;
+
+  void my_snake_case_function(void)
+  {
+	  [...]
+  }
+
+
+- Local variables should be declared at the top of the closest opening scope
+  and should be short.
+
+  We won't enforce a length, and defining short is difficult, this motto
+  (from Linux) catches the spirit
+
+    +---------------------------------------------------------------------------+
+    | LOCAL variable names should be short, and to the point.                   |
+    |                                                                           |
+    | If you have some random integer loop counter,                             |
+    | it should probably be called i.                                           |
+    |                                                                           |
+    | Calling it loop_counter is non-productive,                                |
+    | if there is no chance of it being mis-understood.                         |
+    |                                                                           |
+    | Similarly, tmp can be just about any type of variable that is             |
+    | used to hold a temporary value.                                           |
+    |                                                                           |
+    | If you are afraid to mix up your local variable names,                    |
+    | you have another problem.                                                 |
+    +---------------------------------------------------------------------------+
+
+.. code:: c
+
+  int foo(const int a)
+  {
+	  int c; /* needed in the function */
+	  c = a; /* MISRA-C rules recommend to not modify arguments variables */
+
+	  if (c == 42) {
+	          int b; /* needed only in this "if" statment */
+
+		  b = bar(); /* bar will return an int */
+		  if (b != -1) {
+		          c += b;
+		  }
+	  }
+	  return c;
+  }
+
+- Use an appropraite prefix for public API of a component. For example,
+  if the component name is `bar`, then the init API of the component
+  should be called `bar_init()`.
+
+Indentation
+-----------
+
+Use **tabs** for indentation. The use of spaces for indentation is forbidden
+except in the case where a term is being indented to a boundary that cannot be
+achieved using tabs alone.
+
+Tab spacing should be set to **8 characters**.
+
+Trailing whitespaces or tabulations are not allowed and must be trimmed.
+
+Spacing
+-------
+
+Single spacing should be used around most operators, including:
+
+- Arithmetic operators (``+``, ``-``, ``/``, ``*``, ``%``)
+- Assignment operators (``=``, ``+=``, etc)
+- Boolean operators (``&&``, ``||``)
+- Comparison operators (``<``, ``>``, ``==``, etc)
+- Shift operators (``>>``, ``<<``)
+- Logical operators (``&``, ``|``, etc)
+- Flow control (``if``, ``else``, ``switch``, ``while``, ``return``, etc)
+
+No spacing should be used around the following operators
+
+- Cast (``()``)
+- Indirection (``*``)
+
+Braces
+------
+
+- Use K&R style for statements.
+
+- Function opening braces are on a new line.
+
+- Use braces even for singled line.
+
+
+.. code:: c
+
+  void function(void)
+  {
+	  /* if statement */
+	  if (my_test) {
+		  do_this();
+		  do_that();
+	  }
+
+	  /* if/else statement */
+	  if (my_Test) {
+		  do_this();
+		  do_that();
+	  } else {
+		  do_other_this();
+	  }
+  }
+
+Commenting
+----------
+
+Double-slash style of comments (//) is not allowed, below are examples of
+correct commenting.
+
+.. code:: c
+
+  /*
+   * This example illustrates the first allowed style for multi-line comments.
+   *
+   * Blank lines within multi-lines are allowed when they add clarity or when
+   * they separate multiple contexts.
+   */
+
+.. code:: c
+
+  /**************************************************************************
+   * This is the second allowed style for multi-line comments.
+   *
+   * In this style, the first and last lines use asterisks that run the full
+   * width of the comment at its widest point.
+   *
+   * This style can be used for additional emphasis.
+   *************************************************************************/
+
+.. code:: c
+
+  /* Single line comments can use this format */
+
+.. code:: c
+
+  /***************************************************************************
+   * This alternative single-line comment style can also be used for emphasis.
+   **************************************************************************/
+
+
+Error return values and Exception handling
+------------------------------------------
+
+- Function return type must be explicitly defined.
+
+- Unless specifed otherwise by an official specification, return values must be
+  used to return success or failure (Standard Posix error codes).
+
+  Return an integer if the function is an action or imperative command
+      Failure: -Exxx (STD posix error codes, unless specified otherwise)
+
+      Success: 0
+
+  Return a boolean if the function is as predicate
+      Failure: false
+
+      Success: true
+
+- If a function returns error information, then that error information shall
+  be tested.
+
+  Exceptions are allowed for STDLIB functions (memcpy/printf/...) in which case
+  it must be void casted.
+
+.. code:: c
+
+  #define MY_TRANSFORMED_ERROR  (-1)
+
+  void my_print_function(struct my_struct in_mystruct)
+  {
+	  long long transformed_a = my_transform_a(in_mystruct.a);
+
+	  if (transform_a != MY_TRANSFORMED_ERROR) {
+		  (void)printf("STRUCT\n\tfield(a): %ll\n", transformed_a);
+	  } else {
+		  (void)printf("STRUCT\n\tERROR %ll\n", transformed_a);
+	  }
+  }
+
+
+Use of asserts and panic
+------------------------
+
+Assertions, as a general rule, are only used to catch errors during
+development cycles and are removed from production binaries. They are
+useful to document pre-conditions for a function or impossible conditions
+in code. They are not substitutes for proper error checking and any
+expression used to test an assertion must not have a side-effect.
+
+For example,
+
+.. code:: c
+
+  assert(--i == 0);
+
+should not be used in code.
+
+Assertions can be used to validate input arguments to an API as long as
+the caller and callee are within the same trust boundary.
+
+``panic()`` is used in places wherein it is not possible to continue the
+execution of program sensibly. It should be used sparingly within code
+and, if possible, instead of panic(), components should return error
+back to the caller and the caller can decide on the appropriate action.
+This is particularly useful to build resilence to the program wherein
+non-functional part of the program can be disabled and, if possible,
+other functional aspects of the program can be kept running.
+
+Using COMPILER_ASSERT to check for compile time data errors
+-----------------------------------------------------------
+
+Where possible, use the ``COMPILER_ASSERT`` macro to check the validity of
+data known at compile time instead of checking validity at runtime, to avoid
+unnecessary runtime code.
+
+For example, this can be used to check that the assembler's and compiler's views
+of the size of an array is the same.
+
+.. code:: c
+
+  #include <utils_def.h>
+
+  define MY_STRUCT_SIZE 8 /* Used by assembler source files */
+
+  struct my_struct {
+      uint32_t arg1;
+      uint32_t arg2;
+  };
+
+  COMPILER_ASSERT(MY_STRUCT_SIZE == sizeof(struct my_struct));
+
+
+If ``MY_STRUCT_SIZE`` in the above example were wrong then the compiler would
+emit an error like this:
+
+::
+
+  my_struct.h:10:1: note: in expansion of macro 'COMPILER_ASSERT'
+   10 | COMPILER_ASSERT(MY_STRUCT_SIZE == sizeof(struct my_struct));
+      | ^~~~~~~~~~~~~~~
+
+Data types, structures and typedefs
+-----------------------------------
+
+- Data Types:
+
+The |RMM| codebase should be kept as portable as possible for 64-bits platforms.
+To help with this, the following data type usage guidelines should be followed:
+
+- Where possible, use the built-in *C* data types for variable storage (for
+  example, ``char``, ``int``, ``long long``, etc) instead of the standard *C11*
+  types. Most code is typically only concerned with the minimum size of the
+  data stored, which the built-in *C* types guarantee.
+
+- Avoid using the exact-size standard *C11* types in general (for example,
+  ``uint16_t``, ``uint32_t``, ``uint64_t``, etc) since they can prevent the
+  compiler from making optimizations. There are legitimate uses for them,
+  for example to represent data of a known structure. When using them in a
+  structure definition, consider how padding in the structure will work across
+  architectures.
+
+- Use ``int`` as the default integer type - it's likely to be the fastest on all
+  systems. Also this can be assumed to be 32-bit as a consequence of the
+  `Procedure Call Standard for the Arm 64-bit Architecture`_ .
+
+- Avoid use of ``short`` as this may end up being slower than ``int`` in some
+  systems. If a variable must be exactly 16-bit, use ``int16_t`` or
+  ``uint16_t``.
+
+- ``long`` are defined as LP64 (64-bit), this is guaranteed to be 64-bit.
+
+- Use ``char`` for storing text. Use ``uint8_t`` for storing other 8-bit data.
+
+- Use ``unsigned`` for integers that can never be negative (counts,
+  indices, sizes, etc). |RMM| intends to comply with MISRA "essential type"
+  coding rules (10.X), where signed and unsigned types are considered different
+  essential types. Choosing the correct type will aid this. MISRA static
+  analysers will pick up any implicit signed/unsigned conversions that may lead
+  to unexpected behaviour.
+
+- For pointer types:
+
+  - If an argument in a function declaration is pointing to a known type then
+    simply use a pointer to that type (for example: ``struct my_struct *``).
+
+  - If a variable (including an argument in a function declaration) is pointing
+    to a general, memory-mapped address, an array of pointers or another
+    structure that is likely to require pointer arithmetic then use
+    ``uintptr_t``. This will reduce the amount of casting required in the code.
+    Avoid using ``unsigned long`` or ``unsigned long long`` for this purpose; it
+    may work but is less portable.
+
+  - For other pointer arguments in a function declaration, use ``void *``. This
+    includes pointers to types that are abstracted away from the known API and
+    pointers to arbitrary data. This allows the calling function to pass a
+    pointer argument to the function without any explicit casting (the cast to
+    ``void *`` is implicit). The function implementation can then do the
+    appropriate casting to a specific type.
+
+  - Avoid pointer arithmetic generally (as this violates MISRA C 2012 rule
+    18.4) and especially on void pointers (as this is only supported via
+    language extensions and is considered non-standard). In |RMM|, setting the
+    ``W`` build flag to ``W=3`` enables the *-Wpointer-arith* compiler flag and
+    this will emit warnings where pointer arithmetic is used.
+
+  - Use ``ptrdiff_t`` to compare the difference between 2 pointers.
+
+- Use ``size_t`` when storing the ``sizeof()`` something.
+
+- Use ``ssize_t`` when returning the ``sizeof()`` something from a function that
+  can also return an error code; the signed type allows for a negative return
+  code in case of error. This practice should be used sparingly.
+
+- Use ``u_register_t`` when it's important to store the contents of a register
+  in its native size (64-bit in |AArch64|). This is not a
+  standard *C11* type but is widely available in libc implementations.
+  Where possible, cast the variable to a more appropriate type before
+  interpreting the data. For example, the following structure uses this type to
+  minimize the storage required for the set of registers:
+
+.. code:: c
+
+    typedef struct aapcs64_params {
+            u_register_t arg0;
+            u_register_t arg1;
+            u_register_t arg2;
+            u_register_t arg3;
+            u_register_t arg4;
+            u_register_t arg5;
+            u_register_t arg6;
+            u_register_t arg7;
+    } aapcs64_params_t;
+
+If some code wants to operate on ``arg0`` and knows that it represents a 32-bit
+unsigned integer on all systems, cast it to ``unsigned int``.
+
+These guidelines should be updated if additional types are needed.
+
+- Typedefs:
+
+Typedef should be avoided and used only to create opaque types.
+An opaque data type is one whose concrete data structure is not publicly
+defined. Opaque data types can be used on handles to resources that the caller
+is not expected to address directly.
+
+.. code:: c
+
+	  /* File main.c */
+	  #include <my_lib.h>
+
+	  int main(void)
+	  {
+		context_t	*context;
+		int		res;
+
+		context = my_lib_init();
+
+		res = my_lib_compute(context, "2x2");
+		if (res == -EMYLIB_ERROR) {
+			return -1
+		}
+
+		return res;
+	  }
+
+.. code:: c
+
+	  /* File my_lib.h */
+	  #ifndef MY_LIB_H
+	  #define MY_LIB_H
+
+	  typedef struct my_lib_context {
+	    [...] /* whatever internal private variables you need in my_lib */
+	  } context_t;
+
+	  #endif /* MY_LIB_H */
+
+Macros and Enums
+----------------
+
+- Favor functions over macros.
+
+- Preprocessor macros and enums values are written in all uppercase text.
+
+- A numerical value shall be typed.
+
+.. code:: c
+
+	/* Common C usage​ */
+	#define MY_MACRO 4UL
+
+	/* If used in C and ASM (included from a .S file)​ */
+	#define MY_MACRO UL(4)
+
+- Expressions resulting from the expansion of macro parameters must be enclosed
+  in parentheses.
+
+- A macro parameter immediately following a # operator mustn't be immediately
+  followed by a ## operator.
+
+.. code:: c
+
+   #define SINGLE_HASH_OP(x)		(#x)		/* allowed */
+   #define SINGLE_DOUBLE_HASH_OP(x, y)	(x ## y)	/* allowed */
+   #define MIXED_HASH_OP(x, y)		(#x ## y)	/* not allowed */
+
+- Avoid defining macros that affect the control flow (i.e. avoid using
+  return/goto in a macro).
+
+- Macro with multiple statements can be enclosed in a do-while block or in a
+  expression statement.
+
+.. code:: c
+
+	  int foo(char **b);
+
+	  #define M1(a, b)			\
+		  do {				\
+			if ((a) == 5) {		\
+				foo((b));	\
+			}			\
+		  } while (false)
+
+	  #define M2(a, b)		\
+		  ({			\
+		  if ((a) == 5) {	\
+			  foo((b));	\
+		  }			\
+		  })
+
+	  int foo(char **b)
+	  {
+		  return 42;
+	  }
+
+	  int main(int ac, char **av)
+	  {
+		  if (ac == 1) {
+			  M1(ac, av);
+		  } else if (ac == 2) {
+			  M2(ac, av);
+		  } else {
+			  return -1;
+		  }
+
+		  return ac;
+	  }
+
+Switch statements
+-----------------
+
+- Return in a *case* are allowed.
+
+- Fallthrough are allowed as long as they are commented.
+
+- Do not rely on type promotion between the switch type and the case type.
+
+Inline assembly
+---------------
+
+- Favor C language over assembly language.
+
+- Document all usage of assembly.
+
+- Do not mix C and ASM in the same file.
+
+Libc functions that are banned or to be used with caution
+---------------------------------------------------------
+
+Below is a list of functions that present security risks.
+
++------------------------+--------------------------------------+
+|    libc function       | Comments                             |
++========================+======================================+
+| ``strcpy, wcscpy``,    | use strlcpy instead                  |
+| ``strncpy``            |                                      |
++------------------------+--------------------------------------+
+| ``strcat, wcscat``,    | use strlcat instead                  |
+| ``strncat``            |                                      |
++------------------------+--------------------------------------+
+| ``sprintf, vsprintf``  | use snprintf, vsnprintf              |
+|                        | instead                              |
++------------------------+--------------------------------------+
+| ``snprintf``           | if used, ensure result fits in buffer|
+|                        | i.e : snprintf(buf,size...) < size   |
++------------------------+--------------------------------------+
+| ``vsnprintf``          | if used, inspect va_list match types |
+|                        | specified in format string           |
++------------------------+--------------------------------------+
+| ``strtok, strtok_r``,  | Should not be used                   |
+| ``strsep``             |                                      |
++------------------------+--------------------------------------+
+| ``ato*``               | Should not be used                   |
++------------------------+--------------------------------------+
+| ``*toa``               | Should not be used                   |
++------------------------+--------------------------------------+
+
+The use of above functions are discouraged and will only be allowed
+in justified cases after a discussion has been held either on the mailing
+list or during patch review and it is agreed that no alternative to their
+use is available. The code containing the banned APIs must properly justify
+their usage in the comments.
+
+The above restriction does not apply to Third Party IP code inside the ``ext/``
+directory.
+
+-----------
+
+.. _`Procedure Call Standard for the Arm 64-bit Architecture`: https://developer.arm.com/docs/ihi0055/latest/
+.. _`Linux kernel coding style`: https://www.kernel.org/doc/html/latest/process/coding-style.html
+.. _`MISRA C:2012 Guidelines`: https://www.misra.org.uk/Activities/MISRAC/tabid/160/Default.aspx
+.. _`TF-A coding style`: https://trustedfirmware-a.readthedocs.io/en/latest/process/coding-style.html
+.. _`C11 specification`: https://en.wikipedia.org/wiki/C11_(C_standard_revision)
diff --git a/docs/process/commit-style.rst b/docs/process/commit-style.rst
new file mode 100644
index 0000000..5af5fab
--- /dev/null
+++ b/docs/process/commit-style.rst
@@ -0,0 +1,107 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+.. SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+
+Commit Style
+============
+
+When writing commit messages, please think carefully about the purpose and scope
+of the change you are making: describe briefly what the change does, and
+describe in detail why it does it. This helps to ensure that changes to the
+code-base are transparent and approachable to reviewers, and it allows us to
+keep a more accurate changelog. You may use Markdown in commit messages.
+
+A good commit message provides all the background information needed for
+reviewers to understand the intent and rationale of the patch. This information
+is also useful for future reference. For example:
+
+- What does the patch do?
+- What motivated it?
+- What impact does it have?
+- How was it tested?
+- Have alternatives been considered? Why did you choose this approach over
+  another one?
+- If it fixes an `issue`_, include a reference.
+
+    - Github prescribes a format for issue fixes that can be used within the
+      commit message:
+
+      .. code::
+
+          Fixes TF-RMM/tf-rmm#<issue-number>
+
+Commit messages are expected to be of the following form, based on conventional
+commits:
+
+.. code::
+
+    <type>[optional scope]: <description>
+
+    [optional body]
+
+    [optional trailer(s)]
+
+The following `types` are permissible :
+
++--------------+---------------------------------------------------------------+
+| Type         | Description                                                   |
++==============+===============================================================+
+| ``feat``     | A new feature                                                 |
++--------------+---------------------------------------------------------------+
+| ``fix``      | A bug fix                                                     |
++--------------+---------------------------------------------------------------+
+| ``build``    | Changes that affect the build system or external dependencies |
++--------------+---------------------------------------------------------------+
+| ``docs``     | Documentation-only changes                                    |
++--------------+---------------------------------------------------------------+
+| ``perf``     | A code change that improves performance                       |
++--------------+---------------------------------------------------------------+
+| ``refactor`` | A code change that neither fixes a bug nor adds a feature     |
++--------------+---------------------------------------------------------------+
+| ``revert``   | Changes that revert a previous change                         |
++--------------+---------------------------------------------------------------+
+| ``style``    | Changes that do not affect the meaning of the code            |
+|              | (white-space, formatting, missing semi-colons, etc.)          |
++--------------+---------------------------------------------------------------+
+| ``test``     | Adding missing tests or correcting existing tests             |
++--------------+---------------------------------------------------------------+
+| ``chore``    | Any other change                                              |
++--------------+---------------------------------------------------------------+
+
+The permissible `scopes` are more flexible, and we recommend that they match
+the directory where the patch applies (or where the main subject of the
+patch is, in case of changes accross several directories).
+
+The following example commit message demonstrates the use of the
+``refactor`` type and the ``lib/arch`` scope:
+
+.. code::
+
+    refactor(lib/arch): ...
+
+    This change introduces ....
+
+    Change-Id: ...
+    Signed-off-by: ...
+
+.. _mandated-trailers:
+
+Mandated Trailers
+-----------------
+
+Commits are expected to be signed off with the ``Signed-off-by:`` trailer using
+your real name and email address. You can do this automatically by committing
+with Git's ``-s`` flag.
+
+There may be multiple ``Signed-off-by:`` lines depending on the history of the
+patch. See :ref:`copyright-license-guidance` for guidance on this.
+
+Ensure that each commit also has a unique ``Change-Id:`` line. If you have
+cloned the repository using the "`Clone with commit-msg hook`" clone method,
+then this should be done automatically for you.
+
+More details may be found in the `Gerrit Change-Ids documentation`_.
+
+--------------
+
+.. _Gerrit Change-Ids documentation: https://review.trustedfirmware.org/Documentation/user-changeid.html
+.. _issue: https://github.com/TF-RMM/tf-rmm/issues
diff --git a/docs/process/contributing.rst b/docs/process/contributing.rst
new file mode 100644
index 0000000..febcbd3
--- /dev/null
+++ b/docs/process/contributing.rst
@@ -0,0 +1,160 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+.. SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+
+*******************
+Contributor's Guide
+*******************
+
+Getting Started
+===============
+
+-  Make sure you have a Github account and you are logged on
+   `review.trustedfirmware.org`_.
+
+-  Clone `RMM`_ on your own machine as described in
+   :ref:`getting_started_get_source`.
+
+-  If you plan to contribute a major piece of work, it is usually a good idea to
+   start a discussion around it on the mailing list. This gives everyone
+   visibility of what is coming up, you might learn that somebody else is
+   already working on something similar or the community might be able to
+   provide some early input to help shaping the design of the feature.
+
+-  If you intend to include Third Party IP in your contribution, please mention
+   it explicitly in the email thread and ensure that the changes that include
+   Third Party IP are made in a separate patch (or patch series).
+
+-  Create a local topic branch based on the `RMM`_ ``main`` branch.
+
+Making Changes
+==============
+
+-  See the `License and Copyright for Contributions`_ section for guidance
+   on license and copyright.
+
+-  Ensure commits adhere to the project's :ref:`Commit Style`.
+
+-  Make commits of logical units. See these general `Git guidelines`_ for
+   contributing to a project.
+
+-  Keep the commits on topic. If you need to fix another bug or make another
+   enhancement, please address it on a separate topic branch.
+
+-  Split the patch into manageable units. Small patches are usually easier to
+   review so this will speed up the review process.
+
+-  Avoid long commit series. If you do have a long series, consider whether
+   some commits should be squashed together or addressed in a separate topic.
+
+-  Follow the :ref:`Coding Standard`.
+
+   - Use the static checks as shown in :ref:`build_options_examples` to perform
+     checks like checkpatch, checkspdx, header files include order etc.
+
+-  Where appropriate, please update the documentation.
+
+   -  Consider whether the :ref:`Design` document or other in-source
+      documentation needs updating.
+
+-  Ensure that each patch in the patch series compiles in all supported
+   configurations. For generic changes, such as on the libraries, The
+   :ref:`RMM Fake host architecture` should be able to, at least,
+   build. Patches which do not compile will not be merged.
+
+-  Please test your changes and add suitable tests in the available test
+   frameworks for any new functionality.
+
+-  Ensure that all CI automated tests pass. Failures should be fixed. They
+   might block a patch, depending on how critical they are.
+
+Submitting Changes
+==================
+
+-  Submit your changes for review at https://review.trustedfirmware.org
+   targeting the ``integration`` branch. Create a topic that describes
+   the target of your changes to help group related patches together.
+
+   .. code::
+
+       git push origin HEAD:refs/for/integration [-o topic=<your_topic>]
+
+   Refer to the `Gerrit Uploading Changes documentation`_ for more details.
+
+-  Add reviewers for your patch:
+
+   -  At least one maintainer. See the list of :ref:`maintainers`.
+
+   -  Alternatively, you might send an email to the `TF-RMM mailing list`_
+      to broadcast your review request to the community.
+
+-  The changes will then undergo further review by the designated people. Any
+   review comments will be made directly on your patch. This may require you to
+   do some rework. For controversial changes, the discussion might be moved to
+   the `TF-RMM mailing list`_ to involve more of the community.
+
+-  The patch submission rules are the following. For a patch to be approved
+   and merged in the tree, it must get a ``Code-Review+2``.
+
+   In addition to that, the patch must also get a ``Verified+1``. This is
+   usually set by the Continuous Integration (CI) bot when all automated tests
+   passed on the patch. Sometimes, some of these automated tests may fail for
+   reasons unrelated to the patch. In this case, the maintainers might
+   (after analysis of the failures) override the CI bot score to certify that
+   the patch has been correctly tested.
+
+   In the event where the CI system lacks proper tests for a patch, the patch
+   author or a reviewer might agree to perform additional manual tests
+   in their review and the reviewer incorporates the review of the additional
+   testing in the ``Code-Review+1`` to attest that the patch works as expected.
+
+-  When the changes are accepted, the :ref:`maintainers` will integrate them.
+
+   -  Typically, the :ref:`maintainers` will merge the changes into the
+      ``integration`` branch.
+
+   -  If the changes are not based on a sufficiently-recent commit, or if they
+      cannot be automatically rebased, then the :ref:`maintainers` may rebase it
+      on the ``integration`` branch or ask you to do so.
+
+   -  After final integration testing, the changes will make their way into the
+      ``main`` branch. If a problem is found during integration, the
+      :ref:`maintainers` will request your help to solve the issue. They may
+      revert your patches and ask you to resubmit a reworked version of them or
+      they may ask you to provide a fix-up patch.
+
+.. _copyright-license-guidance:
+
+License and Copyright for Contributions
+=======================================
+
+All new files should include the BSD-3-Clause SPDX license identifier
+where possible. When contributing code to us, the committer and all authors
+are required to make the submission under the terms of the
+:ref:`Developer Certificate of Origin`, confirming that the code submitted can
+(legally) become part of the project, and be subject to the same BSD-3-Clause
+license. This is done by including the standard Git ``Signed-off-by:``
+line in every commit message. If more than one person contributed to the
+commit, they should also add their own ``Signed-off-by:`` line.
+
+Files that entirely consist of contributions to this project should
+have a copyright notice and BSD-3-Clause SPDX license identifier of
+the form :
+
+.. code::
+
+   SPDX-License-Identifier: BSD-3-Clause
+   SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+
+Patches that contain changes to imported Third Party IP files should retain
+their original copyright and license notices. If changes are made to the
+imported files, then add an additional ``SPDX-FileCopyrightText`` tag line
+as shown above.
+
+--------------
+
+.. _review.trustedfirmware.org: https://review.trustedfirmware.org
+.. _RMM: https://git.trustedfirmware.org/TF-RMM/tf-rmm.git
+.. _Git guidelines: http://git-scm.com/book/ch5-2.html
+.. _Gerrit Uploading Changes documentation: https://review.trustedfirmware.org/Documentation/user-upload.html
+.. _TF-A Tests: https://trustedfirmware-a-tests.readthedocs.io
+.. _TF-RMM mailing list: https://lists.trustedfirmware.org/mailman3/lists/tf-rmm.lists.trustedfirmware.org/
diff --git a/docs/process/index.rst b/docs/process/index.rst
new file mode 100644
index 0000000..836afd2
--- /dev/null
+++ b/docs/process/index.rst
@@ -0,0 +1,14 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+.. SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+
+Process
+=======
+.. toctree::
+    :maxdepth: 1
+    :caption: Contents
+    :numbered:
+
+    coding-standard
+    security
+    commit-style
+    contributing
diff --git a/docs/process/security.rst b/docs/process/security.rst
new file mode 100644
index 0000000..4eab42b
--- /dev/null
+++ b/docs/process/security.rst
@@ -0,0 +1,13 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+.. SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+
+Security Handling
+=================
+
+Currently the RMM implementation conforms to the RMM Beta0 Specification
+which means it is not yet ready to be productised.
+
+The generic security incident process can be found at
+`TrustedFirmware.org security incident process`_.
+
+.. _TrustedFirmware.org security incident process: https://developer.trustedfirmware.org/w/collaboration/security_center/
diff --git a/docs/readme.rst b/docs/readme.rst
new file mode 100644
index 0000000..8af1396
--- /dev/null
+++ b/docs/readme.rst
@@ -0,0 +1,117 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+.. SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+
+######
+TF-RMM
+######
+
+TF-RMM (or simply RMM) is the Trusted Firmware Implementation of the `Realm
+Management Monitor (RMM) Specification`_. The RMM
+is a software component that runs at Realm EL2 and forms part of a system
+which implements the Arm Confidential Compute Architecture (Arm CCA).
+`Arm CCA`_ is an architecture which provides Protected Execution Environments
+called Realms.
+
+Prior to Arm CCA, virtual machines have to trust hypervisors that manage them
+and a resource that is managed by the hypervisor is also accessible by it.
+Exploits against the hypervisors can leak confidential data held in the virtual
+machines.  `Arm CCA`_ introduces a new confidential compute environment called
+a `Realm`. Any code or data belonging to a `Realm`, whether in memory or in
+registers, cannot be accessed or modified by the hypervisor. This means that
+the Realm owner does not need to trust the hypervisor that manages the
+resources used by the Realm.
+
+The Realm VM is initiated and controlled by the Normal world Hypervisor.
+To allow the isolated execution of the Realm VM, a new component called the
+Realm Management Monitor (RMM) is introduced, executing at R_EL2. The
+hypervisor interacts with the RMM via Realm Management Interface (RMI) to
+manage the Realm VM. Policy decisions, such as which Realm to run or what
+memory to be delegated to the Realm are made by the hypervisor and communicated
+via the RMI. The RMM also provides services to the Realm via the Realm Service
+Interface (RSI). These services include cryptographic services and
+attestation. The Realm initial state can be measured and an attestation
+report, which also includes platform attestation, can be requested via RSI.
+The RSI is also the channel for memory management requests from the
+Realm VM to the RMM.
+
+The following diagram shows the complete Arm CCA software stack running a
+confidential Realm VM :
+
+|Realm VM|
+
+Figure 1. Realm VM execution
+
+The TF-RMM interacts with the Root EL3 Firmware via the
+`RMM-EL3 Communication Interface`_ and this is implemented by the reference
+EL3 Firmware implementation `TF-A`_.
+
+More details about the RMM and how it fits in the Software Stack can be
+found in `Arm CCA Software Stack Guide`_.
+
+The :ref:`Change-log and Release notes` has the details of features implemented
+by this version of TF-RMM and lists any known issues.
+
+*******
+License
+*******
+
+Unless specifically indicated otherwise in a file, TF-RMM files are provided
+under the :ref:`BSD-3-Clause License <License>`. For contributions, please
+see :ref:`License and Copyright for Contributions <copyright-license-guidance>`.
+
+Third Party Projects
+====================
+
+The TF-RMM project requires to be linked with certain other 3rd party projects
+and they are to be cloned from their repositories into ``ext`` folder before
+building. The projects are `MbedTLS`_, `t_cose`_, and `QCBOR`_.
+
+The project also contains files which are imported from other projects
+into the source tree and may have a different license. Such files with
+different licenses are listed in the table below. This table is used by the
+``checkspdx`` tool in the project to verify license headers.
+
+.. list-table:: **List of files with different license**
+
+      * - File
+	- License
+      * - lib/libc/src/printf.c
+	- MIT
+      * - lib/libc/include/stdio.h
+	- MIT
+      * - lib/libc/src/strlcpy.c
+	- ISC
+      * - lib/libc/src/strnlen.c
+	- BSD-2-Clause
+      * - lib/allocator/src/memory_alloc.c
+	- Apache-2.0
+
+
+************
+Contributing
+************
+
+We gratefully accept bug reports and contributions from the community.
+Please see the :ref:`Contributor's Guide` for details on how to do this.
+
+********************
+Feedback and support
+********************
+
+Feedback is requested via email to:
+`tf-rmm@lists.trustedfirmware.org <tf-rmm@lists.trustedfirmware.org>`__.
+
+To report a bug, please file an `issue on Github`_
+
+-----------------
+
+.. |Realm VM| image:: ./about/diagrams/cca_software_arch.png
+.. _Realm Management Monitor (RMM) Specification: https://developer.arm.com/documentation/den0137/1-0bet0/?lang=en
+.. _Arm CCA: https://www.arm.com/architecture/security-features/arm-confidential-compute-architecture
+.. _Arm CCA Software Stack Guide: https://developer.arm.com/documentation/den0127/0100/Overview
+.. _TF-A: https://www.trustedfirmware.org/projects/tf-a/
+.. _RMM-EL3 Communication Interface: https://trustedfirmware-a.readthedocs.io/en/latest/components/rmm-el3-comms-spec.html
+.. _issue on Github: https://github.com/TF-RMM/tf-rmm/issues
+.. _MbedTLS: https://github.com/ARMmbed/mbedtls.git
+.. _t_cose: https://github.com/laurencelundblade/t_cose
+.. _QCBOR: https://github.com/laurencelundblade/QCBOR.git
diff --git a/drivers/pl011/CMakeLists.txt b/drivers/pl011/CMakeLists.txt
new file mode 100644
index 0000000..8120c09
--- /dev/null
+++ b/drivers/pl011/CMakeLists.txt
@@ -0,0 +1,28 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+add_library(rmm-driver-pl011)
+
+target_link_libraries(rmm-driver-pl011
+    PRIVATE rmm-lib)
+
+arm_config_option(
+    NAME RMM_UART_ADDR
+    HELP "Physical UART address"
+    TYPE STRING
+    DEFAULT 0x0)
+
+if(RMM_UART_ADDR EQUAL 0x0)
+    message(FATAL_ERROR "RMM_UART_ADDR is not initialized")
+endif()
+
+target_compile_definitions(rmm-driver-pl011
+    PUBLIC "RMM_UART_ADDR=ULL(${RMM_UART_ADDR})")
+
+target_include_directories(rmm-driver-pl011
+    PUBLIC "include")
+
+target_sources(rmm-driver-pl011
+    PRIVATE "src/pl011.c")
diff --git a/drivers/pl011/include/pl011.h b/drivers/pl011/include/pl011.h
new file mode 100644
index 0000000..6c84a79
--- /dev/null
+++ b/drivers/pl011/include/pl011.h
@@ -0,0 +1,57 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef PL011_H
+#define PL011_H
+
+#include <stdint.h>
+
+/* PL011 Registers */
+#define UARTDR                    0x000
+#define UARTECR                   0x004
+#define UARTFR                    0x018
+
+/* PL011 registers (out of the SBSA specification) */
+#define UARTIBRD                  0x024
+#define UARTFBRD                  0x028
+#define UARTLCR_H                 0x02C
+#define UARTCR                    0x030
+
+/* Flag reg bits */
+#define PL011_UARTFR_TXFF         (1U << 5)	/* Transmit FIFO full */
+
+/* Control reg bits */
+#define PL011_UARTCR_RXE          (1U << 9)	/* Receive enable */
+#define PL011_UARTCR_TXE          (1U << 8)	/* Transmit enable */
+#define PL011_UARTCR_UARTEN       (1U << 0)	/* UART Enable */
+
+/* FIFO Enabled / No Parity / 8 Data bit / One Stop Bit */
+#define PL011_LINE_CONTROL  (PL011_UARTLCR_H_FEN | PL011_UARTLCR_H_WLEN_8)
+
+/* Line Control Register Bits */
+#define PL011_UARTLCR_H_WLEN_8    (3U << 5)
+#define PL011_UARTLCR_H_FEN       (1U << 4)	/* FIFOs Enable */
+
+/*
+ * Function that initiates UART for console output
+ * Arguments:
+ *   baseaddr - UART base address
+ *   clock    - UART input clock which sets master trasmit/receive rate
+ *   baud     - UART Baudrate
+ * Returns:
+ *   0 on success or -1 when invalid baseaddr/clock/baud is used
+ */
+int uart_init(uintptr_t baseaddr, unsigned int clock, unsigned int baud);
+
+/*
+ * Function that outputs a character to console
+ * Arguments:
+ *   ch       - Character that must be sent to console output
+ * Returns:
+ *   void
+ */
+void uart_putc(char ch);
+
+#endif /* PL011_H */
diff --git a/drivers/pl011/src/pl011.c b/drivers/pl011/src/pl011.c
new file mode 100644
index 0000000..7384545
--- /dev/null
+++ b/drivers/pl011/src/pl011.c
@@ -0,0 +1,77 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <mmio.h>
+#include <pl011.h>
+#include <utils_def.h>
+
+static inline void uart_wait(void)
+{
+	/* Wait until there is room in the Tx FIFO */
+	while ((read32((void *)((RMM_UART_ADDR) + UARTFR))
+				& PL011_UARTFR_TXFF) != 0U) {
+		/* Do nothing */
+	}
+}
+
+/* Function that initializes UART for console output */
+int uart_init(uintptr_t base_addr,
+		unsigned int uart_clk,
+		unsigned int baud_rate)
+{
+	unsigned int div;
+
+	/* Check Base address, baud rate and UART clock for sanity */
+
+	if (base_addr == 0UL) {
+		return -1;
+	}
+
+	if (uart_clk == 0U) {
+		return -1;
+	}
+
+	if (baud_rate == 0U) {
+		return -1;
+	}
+
+	/* Disable UART before programming */
+	write32(0U, (void *)((RMM_UART_ADDR) + UARTCR));
+
+	/* Program the baudrate */
+	div = (uart_clk * 4)/baud_rate;
+
+	/* IBRD = Divisor >> 6 */
+	write32(div >> 6, (void *)((RMM_UART_ADDR) + UARTIBRD));
+
+	/* FBRD = Divisor & 0x3F */
+	write32(div & 0x3f, (void *)((RMM_UART_ADDR) + UARTFBRD));
+
+	/* Enable FIFO and set word length, parity and number of stop bits */
+	write32(PL011_LINE_CONTROL, (void *)((RMM_UART_ADDR) + UARTLCR_H));
+
+	/* Clear any pending errors */
+	write32(0U, (void *)((RMM_UART_ADDR) + UARTECR));
+
+	/* Enable Tx, Rx, and UART overall */
+	write32(PL011_UARTCR_RXE | PL011_UARTCR_TXE | PL011_UARTCR_UARTEN,
+		(void *)((RMM_UART_ADDR) + UARTCR));
+	return 0;
+}
+
+void uart_putc(char ch)
+{
+	uart_wait();
+	write8(ch, (void *)((RMM_UART_ADDR) + UARTDR));
+}
+
+/* Serial output - called from printf */
+void _putchar(char ch)
+{
+	if (ch == '\n') {
+		uart_putc('\r');
+	}
+	uart_putc(ch);
+}
diff --git a/ext/mbedtls b/ext/mbedtls
new file mode 160000
index 0000000..869298b
--- /dev/null
+++ b/ext/mbedtls
@@ -0,0 +1 @@
+Subproject commit 869298bffeea13b205343361b7a7daf2b210e33d
diff --git a/ext/qcbor b/ext/qcbor
new file mode 160000
index 0000000..7132236
--- /dev/null
+++ b/ext/qcbor
@@ -0,0 +1 @@
+Subproject commit 7132236977a2f8f54a5f80f389ad5e45de73a49e
diff --git a/ext/t_cose b/ext/t_cose
new file mode 160000
index 0000000..cecf62b
--- /dev/null
+++ b/ext/t_cose
@@ -0,0 +1 @@
+Subproject commit cecf62bf4275eae732fadb067dc9106c7815ced4
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
new file mode 100644
index 0000000..91e9807
--- /dev/null
+++ b/lib/CMakeLists.txt
@@ -0,0 +1,37 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+add_library(rmm-lib INTERFACE)
+
+target_link_libraries(rmm-lib
+    INTERFACE rmm-lib-allocator
+              rmm-lib-arch
+              rmm-lib-asc
+              rmm-lib-attestation
+              rmm-lib-common
+              rmm-lib-debug
+              rmm-lib-gic
+              rmm-lib-measurement
+              rmm-lib-realm
+              rmm-lib-rmm_el3_ifc
+              rmm-lib-smc
+              t_cose
+              rmm-lib-timers
+              rmm-lib-xlat)
+
+add_subdirectory("allocator")
+add_subdirectory("arch")
+add_subdirectory("attestation")
+add_subdirectory("asc")
+add_subdirectory("common")
+add_subdirectory("debug")
+add_subdirectory("gic")
+add_subdirectory("measurement")
+add_subdirectory("realm")
+add_subdirectory("smc")
+add_subdirectory("rmm_el3_ifc")
+add_subdirectory("t_cose")
+add_subdirectory("timers")
+add_subdirectory("xlat")
diff --git a/lib/allocator/CMakeLists.txt b/lib/allocator/CMakeLists.txt
new file mode 100644
index 0000000..25e531e
--- /dev/null
+++ b/lib/allocator/CMakeLists.txt
@@ -0,0 +1,22 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+add_library(rmm-lib-allocator)
+
+target_link_libraries(rmm-lib-allocator
+  PRIVATE
+    rmm-lib-arch
+    rmm-lib-common
+    rmm-lib-debug
+    rmm-lib-libc
+    rmm-lib-rmm_el3_ifc
+    MbedTLS::Crypto
+    t_cose)
+
+target_include_directories(rmm-lib-allocator
+    PUBLIC "include")
+
+target_sources(rmm-lib-allocator
+    PRIVATE "src/memory_alloc.c")
diff --git a/lib/allocator/include/memory_alloc.h b/lib/allocator/include/memory_alloc.h
new file mode 100644
index 0000000..5346395
--- /dev/null
+++ b/lib/allocator/include/memory_alloc.h
@@ -0,0 +1,61 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef MEMORY_ALLOC_H
+#define MEMORY_ALLOC_H
+
+#include <stddef.h>
+
+struct _memory_header;
+typedef struct memory_header_s memory_header_t;
+
+/*
+ * Number of pages per REC to be allocated. MbedTLS needs 8K of heap
+ * for attestation usecases.
+ */
+#define REC_HEAP_PAGES		2
+
+struct buffer_alloc_ctx {
+	unsigned char		*buf;
+	size_t			len;
+	memory_header_t		*first;
+	memory_header_t		*first_free;
+	int			verify;
+};
+
+struct memory_header_s {
+	size_t			magic1;
+	size_t			size;
+	size_t			alloc;
+	memory_header_t		*prev;
+	memory_header_t		*next;
+	memory_header_t		*prev_free;
+	memory_header_t		*next_free;
+	size_t			magic2;
+};
+
+
+/*
+ * Function to assign a heap context to the current CPU for
+ * use by the MbedCrypto. In case the heap needs to be isolated
+ * to the CPU executing a realm , this function must to be
+ * called before entering a Realm. This will ensure that any
+ * crypto operations triggered via RSI will used the assigned
+ * heap.
+ * Arguments:
+ *  ctx - Pointer to a valid context for the memory allocator.
+ * Returns:
+ *  0 on success or a POSIX error code or error.
+ */
+int buffer_alloc_ctx_assign(struct buffer_alloc_ctx *ctx);
+
+/*
+ * Function to unasign a heap context from the current CPU.
+ * In case the heap was isolated to the CPU executing a realm,
+ * this function must to be called after exiting a Realm.
+ */
+void buffer_alloc_ctx_unassign(void);
+
+#endif /* MEMORY_ALLOC_H */
diff --git a/lib/allocator/src/memory_alloc.c b/lib/allocator/src/memory_alloc.c
new file mode 100644
index 0000000..8c536e8
--- /dev/null
+++ b/lib/allocator/src/memory_alloc.c
@@ -0,0 +1,455 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ * SPDX-FileCopyrightText: Copyright The Mbed TLS Contributors
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <cpuid.h>
+#include <debug.h>
+#include <errno.h>
+#include <mbedtls/memory_buffer_alloc.h>
+#include <mbedtls/platform.h>
+#include <memory_alloc.h>
+#include <sizes.h>
+#include <string.h>
+
+#define MAGIC1		UL(0xFF00AA55)
+#define MAGIC2		UL(0xEE119966)
+#define MAX_BT		20
+
+#if defined(MBEDTLS_MEMORY_DEBUG)
+#error MBEDTLS_MEMORY_DEBUG is not supported by this allocator.
+#endif
+
+#if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C)
+/*
+ * If MBEDTLS_MEMORY_BUFFER_ALLOC_C is defined then the allocator from mbedTLS
+ * is going to be used, which is not desired.
+ */
+#error MBEDTLS_MEMORY_BUFFER_ALLOC_C is defined
+#endif /* MBEDTLS_MEMORY_BUFFER_ALLOC_C */
+
+#if defined(MBEDTLS_MEMORY_BACKTRACE)
+#error MBEDTLS_MEMORY_BACKTRACE is not supported by this allocator.
+#endif /* MBEDTLS_MEMORY_BACKTRACE */
+
+#if defined(MBEDTLS_THREADING_C)
+/*
+ * This allocator doesn't support multithreading. On the other hand it
+ * handles multiple heaps
+ */
+#error MBEDTLS_THREADING_C is not supported by this allocator.
+#endif /* MBEDTLS_THREADING_C */
+
+#if defined(MBEDTLS_SELF_TEST)
+#error MBEDTLS_SELF_TEST is not supported by this allocator.
+#endif /* MBEDTLS_SELF_TEST */
+
+/* Array of heaps per CPU */
+static struct buffer_alloc_ctx *ctx_per_cpu[MAX_CPUS];
+
+static inline struct buffer_alloc_ctx *get_heap_ctx(void)
+{
+	struct buffer_alloc_ctx *ctx;
+	unsigned int cpu_id = my_cpuid();
+
+	assert(cpu_id < MAX_CPUS);
+
+	ctx = ctx_per_cpu[cpu_id];
+	/* Programming error if heap is not assigned */
+	if (ctx == NULL) {
+		ERROR(" No heap assigned to this CPU %u\n", cpu_id);
+		panic();
+	}
+
+	return ctx;
+}
+
+static int verify_header(struct memory_header_s *hdr)
+{
+	if (hdr->magic1 != MAGIC1) {
+		return 1;
+	}
+
+	if (hdr->magic2 != MAGIC2) {
+		return 1;
+	}
+
+	if (hdr->alloc > 1UL) {
+		return 1;
+	}
+
+	if (hdr->prev != NULL && hdr->prev == hdr->next) {
+		return 1;
+	}
+
+	if (hdr->prev_free != NULL && hdr->prev_free == hdr->next_free)	{
+		return 1;
+	}
+
+	return 0;
+}
+
+static int verify_chain(struct buffer_alloc_ctx *heap)
+{
+	struct memory_header_s *prv = heap->first;
+	struct memory_header_s *cur;
+
+	if (prv == NULL || verify_header(prv) != 0) {
+		return 1;
+	}
+
+	if (heap->first->prev != NULL) {
+		return 1;
+	}
+
+	cur = heap->first->next;
+
+	while (cur != NULL) {
+		if (verify_header(cur) != 0) {
+			return 1;
+		}
+
+		if (cur->prev != prv) {
+			return 1;
+		}
+
+		prv = cur;
+		cur = cur->next;
+	}
+
+	return 0;
+}
+
+static void *buffer_alloc_calloc_with_heap(struct buffer_alloc_ctx *heap,
+					   size_t n,
+					   size_t size)
+{
+	struct memory_header_s *new;
+	struct memory_header_s *cur = heap->first_free;
+	unsigned char *p;
+	void *ret;
+	size_t original_len, len;
+
+	if (heap->buf == NULL || heap->first == NULL) {
+		return NULL;
+	}
+
+	original_len = len = n * size;
+
+	if (n == 0UL || size == 0UL || len / n != size) {
+		return NULL;
+	} else if (len > (size_t)-MBEDTLS_MEMORY_ALIGN_MULTIPLE) {
+		return NULL;
+	}
+
+	if ((len % MBEDTLS_MEMORY_ALIGN_MULTIPLE) != 0) {
+		len -= len % MBEDTLS_MEMORY_ALIGN_MULTIPLE;
+		len += MBEDTLS_MEMORY_ALIGN_MULTIPLE;
+	}
+
+	/* Find block that fits */
+	while (cur != NULL) {
+		if (cur->size >= len) {
+			break;
+		}
+		cur = cur->next_free;
+	}
+
+	if (cur == NULL) {
+		return NULL;
+	}
+
+	if (cur->alloc != 0UL) {
+		assert(false);
+	}
+
+	/* Found location, split block if > memory_header + 4 room left */
+	if ((cur->size - len) <
+	    (sizeof(struct memory_header_s) + MBEDTLS_MEMORY_ALIGN_MULTIPLE)) {
+		cur->alloc = 1UL;
+
+		/* Remove from free_list */
+		if (cur->prev_free != NULL) {
+			cur->prev_free->next_free = cur->next_free;
+		} else {
+			heap->first_free = cur->next_free;
+		}
+
+		if (cur->next_free != NULL) {
+			cur->next_free->prev_free = cur->prev_free;
+		}
+
+		cur->prev_free = NULL;
+		cur->next_free = NULL;
+
+		if (heap->verify & MBEDTLS_MEMORY_VERIFY_ALLOC) {
+			assert(verify_chain(heap) == 0);
+		}
+
+		ret = (unsigned char *) cur + sizeof(struct memory_header_s);
+		memset(ret, 0, original_len);
+
+		return ret;
+	}
+
+	p = ((unsigned char *) cur) + sizeof(struct memory_header_s) + len;
+	new = (struct memory_header_s *) p;
+
+	new->size = cur->size - len - sizeof(struct memory_header_s);
+	new->alloc = 0;
+	new->prev = cur;
+	new->next = cur->next;
+	new->magic1 = MAGIC1;
+	new->magic2 = MAGIC2;
+
+	if (new->next != NULL) {
+		new->next->prev = new;
+	}
+
+	/* Replace cur with new in free_list */
+	new->prev_free = cur->prev_free;
+	new->next_free = cur->next_free;
+	if (new->prev_free != NULL) {
+		new->prev_free->next_free = new;
+	} else {
+		heap->first_free = new;
+	}
+
+	if (new->next_free != NULL) {
+		new->next_free->prev_free = new;
+	}
+
+	cur->alloc = 1;
+	cur->size = len;
+	cur->next = new;
+	cur->prev_free = NULL;
+	cur->next_free = NULL;
+
+	if ((heap->verify & MBEDTLS_MEMORY_VERIFY_ALLOC) != 0) {
+		assert(verify_chain(heap) == 0);
+	}
+
+	ret = (unsigned char *) cur + sizeof(struct memory_header_s);
+	memset(ret, 0, original_len);
+
+	return ret;
+}
+
+void *buffer_alloc_calloc(size_t n, size_t size)
+{
+	struct buffer_alloc_ctx *heap = get_heap_ctx();
+
+	assert(heap);
+	return buffer_alloc_calloc_with_heap(heap, n, size);
+}
+
+static void buffer_alloc_free_with_heap(struct buffer_alloc_ctx *heap,
+					void *ptr)
+{
+	struct memory_header_s *hdr;
+	struct memory_header_s *old = NULL;
+	unsigned char *p = (unsigned char *) ptr;
+
+	if (ptr == NULL || heap->buf == NULL || heap->first == NULL) {
+		return;
+	}
+
+	if (p < heap->buf || p >= heap->buf + heap->len) {
+		assert(0);
+	}
+
+	p -= sizeof(struct memory_header_s);
+	hdr = (struct memory_header_s *) p;
+
+	assert(verify_header(hdr) == 0);
+
+	if (hdr->alloc != 1) {
+		assert(0);
+	}
+
+	hdr->alloc = 0;
+
+	/* Regroup with block before */
+	if (hdr->prev != NULL && hdr->prev->alloc == 0UL) {
+		hdr->prev->size += sizeof(struct memory_header_s) + hdr->size;
+		hdr->prev->next = hdr->next;
+		old = hdr;
+		hdr = hdr->prev;
+
+		if (hdr->next != NULL) {
+			hdr->next->prev = hdr;
+		}
+
+		memset(old, 0, sizeof(struct memory_header_s));
+	}
+
+	/* Regroup with block after */
+	if (hdr->next != NULL && hdr->next->alloc == 0UL) {
+		hdr->size += sizeof(struct memory_header_s) + hdr->next->size;
+		old = hdr->next;
+		hdr->next = hdr->next->next;
+
+		if (hdr->prev_free != NULL || hdr->next_free != NULL) {
+			if (hdr->prev_free != NULL) {
+				hdr->prev_free->next_free = hdr->next_free;
+			} else {
+				heap->first_free = hdr->next_free;
+			}
+			if (hdr->next_free != NULL) {
+				hdr->next_free->prev_free = hdr->prev_free;
+			}
+		}
+
+		hdr->prev_free = old->prev_free;
+		hdr->next_free = old->next_free;
+
+		if (hdr->prev_free != NULL) {
+			hdr->prev_free->next_free = hdr;
+		} else {
+			heap->first_free = hdr;
+		}
+
+		if (hdr->next_free != NULL) {
+			hdr->next_free->prev_free = hdr;
+		}
+
+		if (hdr->next != NULL) {
+			hdr->next->prev = hdr;
+		}
+
+		memset(old, 0, sizeof(struct memory_header_s));
+	}
+
+	/*
+	 * Prepend to free_list if we have not merged
+	 * (Does not have to stay in same order as prev / next list)
+	 */
+	if (old == NULL) {
+		hdr->next_free = heap->first_free;
+		if (heap->first_free != NULL) {
+			heap->first_free->prev_free = hdr;
+		}
+		heap->first_free = hdr;
+	}
+
+	if (heap->verify & MBEDTLS_MEMORY_VERIFY_FREE) {
+		assert(verify_chain(heap));
+	}
+}
+
+void buffer_alloc_free(void *ptr)
+{
+	struct buffer_alloc_ctx *heap = get_heap_ctx();
+
+	assert(heap);
+	buffer_alloc_free_with_heap(heap, ptr);
+}
+
+int buffer_alloc_ctx_assign(struct buffer_alloc_ctx *ctx)
+{
+	unsigned int cpuid = my_cpuid();
+
+	assert(cpuid < MAX_CPUS);
+
+	if (ctx == NULL) {
+		return -EINVAL;
+	}
+
+	if (ctx_per_cpu[cpuid] != NULL) {
+		/* multiple assign */
+		return -EINVAL;
+	}
+
+	ctx_per_cpu[cpuid] = ctx;
+
+	return 0;
+}
+
+void buffer_alloc_ctx_unassign(void)
+{
+	unsigned int cpuid = my_cpuid();
+
+	assert(cpuid < MAX_CPUS);
+
+	/* multiple unassign */
+	assert(ctx_per_cpu[cpuid] != NULL);
+
+	ctx_per_cpu[cpuid] = NULL;
+}
+
+void mbedtls_memory_buffer_set_verify(int verify)
+{
+	struct buffer_alloc_ctx *heap = get_heap_ctx();
+
+	/* this seems to be dead code */
+	assert(false);
+
+	assert(heap);
+	heap->verify = verify;
+}
+
+int mbedtls_memory_buffer_alloc_verify(void)
+{
+	struct buffer_alloc_ctx *heap = get_heap_ctx();
+
+	assert(heap);
+	return verify_chain(heap);
+}
+
+void mbedtls_memory_buffer_alloc_init(unsigned char *buf, size_t len)
+{
+	/* The heap structure is obtained from the REC
+	 * while the buffer is passed in the init function.
+	 * This way the interface can remain the same.
+	 */
+	struct buffer_alloc_ctx *heap = get_heap_ctx();
+
+	assert(heap);
+
+	memset(heap, 0, sizeof(struct buffer_alloc_ctx));
+
+	if (len < sizeof(struct memory_header_s) +
+	    MBEDTLS_MEMORY_ALIGN_MULTIPLE) {
+		return;
+	} else if ((size_t)buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE)	{
+		/* Adjust len first since buf is used in the computation */
+		len -= MBEDTLS_MEMORY_ALIGN_MULTIPLE
+			- ((size_t)buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE);
+		buf += MBEDTLS_MEMORY_ALIGN_MULTIPLE
+			- ((size_t)buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE);
+	}
+
+	memset(buf, 0, len);
+
+	heap->buf = buf;
+	heap->len = len;
+
+	heap->first = (struct memory_header_s *)buf;
+	heap->first->size = len - sizeof(struct memory_header_s);
+	heap->first->magic1 = MAGIC1;
+	heap->first->magic2 = MAGIC2;
+	heap->first_free = heap->first;
+}
+
+void mbedtls_memory_buffer_alloc_free(void)
+{
+	struct buffer_alloc_ctx *heap = get_heap_ctx();
+
+	assert(heap);
+	memset(heap, 0, sizeof(struct buffer_alloc_ctx));
+}
diff --git a/lib/arch/CMakeLists.txt b/lib/arch/CMakeLists.txt
new file mode 100644
index 0000000..bb0367e
--- /dev/null
+++ b/lib/arch/CMakeLists.txt
@@ -0,0 +1,27 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+add_library(rmm-lib-arch)
+
+target_link_libraries(rmm-lib-arch
+    PRIVATE rmm-lib-common)
+
+target_include_directories(rmm-lib-arch
+    PUBLIC "include"
+	   "include/${RMM_ARCH}")
+
+target_sources(rmm-lib-arch
+        PRIVATE "src/arch_features.c"
+                "src/fpu_helpers.c")
+
+if(NOT RMM_ARCH STREQUAL fake_host)
+    target_sources(rmm-lib-arch
+        PRIVATE "src/aarch64/cache_helpers.S"
+                "src/aarch64/fpu_helpers.S")
+else()
+    target_sources(rmm-lib-arch
+        PRIVATE "src/fake_host/cache_wrappers.c"
+                "src/fake_host/fpu_helpers_host.c")
+endif()
diff --git a/lib/arch/include/aarch64/asm_macros.S b/lib/arch/include/aarch64/asm_macros.S
new file mode 100644
index 0000000..887ab68
--- /dev/null
+++ b/lib/arch/include/aarch64/asm_macros.S
@@ -0,0 +1,166 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+#ifndef ASM_MACROS_S
+#define ASM_MACROS_S
+
+#define TLB_INVALIDATE(_type) \
+	tlbi	_type
+
+#define ENTRY(x) .global x; x
+#define ENDPROC(x)
+
+	/*
+	 * This macro is used to create a function label and place the
+	 * code into a separate text section based on the function name
+	 * to enable elimination of unused code during linking. It also adds
+	 * basic debug information to enable call stack printing most of the
+	 * time. The optional _align parameter can be used to force a
+	 * non-standard alignment (indicated in powers of 2). The default is
+	 * _align=2 because aarch64 instructions must be word aligned.
+	 * Do *not* try to use a raw .align directive. Since func
+	 * switches to a new section, this would not have the desired effect.
+	 */
+	.macro func _name, _align=2
+	/*
+	 * Add Call Frame Information entry in the .debug_frame section for
+	 * debugger consumption. This enables callstack printing in debuggers.
+	 * This does not use any space in the final loaded binary, only in the
+	 * ELF file.
+	 * Note that a function manipulating the CFA pointer location (i.e. the
+	 * x29 frame pointer on AArch64) should declare it using the
+	 * appropriate .cfi* directives, or be prepared to have a degraded
+	 * debugging experience.
+	 */
+	.cfi_sections .debug_frame
+	.section .text.asm.\_name, "ax"
+	.type \_name, %function
+	/*
+	 * .cfi_startproc and .cfi_endproc are needed to output entries in
+	 * .debug_frame
+	 */
+	.cfi_startproc
+	.align \_align
+	\_name:
+	.endm
+
+	/*
+	 * This macro is used to mark the end of a function.
+	 */
+	.macro endfunc _name
+		.cfi_endproc
+		.size \_name, . - \_name
+	.endm
+
+
+	.macro	dcache_line_size  reg, tmp
+	mrs	\tmp, ctr_el0
+	ubfx	\tmp, \tmp, #16, #4
+	mov	\reg, #4
+	lsl	\reg, \reg, \tmp
+	.endm
+
+	/*
+	 * Declare the exception vector table, enforcing it is aligned on a
+	 * 2KB boundary, as required by the ARMv8 architecture.
+	 * Use zero bytes as the fill value to be stored in the padding bytes
+	 * so that it inserts illegal AArch64 instructions. This increases
+	 * security, robustness and potentially facilitates debugging.
+	 */
+	.macro vector_base  label, section_name=.vectors
+	.section \section_name, "ax"
+	.align 11, 0
+	\label:
+	.endm
+
+	/*
+	 * Create an entry in the exception vector table, enforcing it is
+	 * aligned on a 128-byte boundary, as required by the ARMv8 architecture.
+	 * Use zero bytes as the fill value to be stored in the padding bytes
+	 * so that it inserts illegal AArch64 instructions. This increases
+	 * security, robustness and potentially facilitates debugging.
+	 */
+	.macro vector_entry  label, section_name=.vectors
+	.cfi_sections .debug_frame
+	.section \section_name, "ax"
+	.align 7, 0
+	.type \label, %function
+	.cfi_startproc
+	\label:
+	.endm
+
+	/*
+	 * Add the bytes until fill the full exception vector, whose size is always
+	 * 32 instructions. If there are more than 32 instructions in the
+	 * exception vector then an error is emitted.
+	 */
+	.macro end_vector_entry label
+	.cfi_endproc
+	.fill	\label + (32 * 4) - .
+	.endm
+
+	/*
+	 * Helper macro to generate the best mov/movk combinations according
+	 * the value to be moved. The 16 bits from '_shift' are tested and
+	 * if not zero, they are moved into '_reg' without affecting
+	 * other bits.
+	 */
+	.macro _mov_imm16 _reg, _val, _shift
+		.if (\_val >> \_shift) & 0xffff
+			.if (\_val & (1 << \_shift - 1))
+				movk	\_reg, (\_val >> \_shift) & 0xffff, LSL \_shift
+			.else
+				mov	\_reg, \_val & (0xffff << \_shift)
+			.endif
+		.endif
+	.endm
+
+	/*
+	 * Helper macro to load arbitrary values into 32 or 64-bit registers
+	 * which generates the best mov/movk combinations. Many base addresses
+	 * are 64KB aligned the macro will eliminate updating bits 15:0 in
+	 * that case
+	 */
+	.macro mov_imm _reg, _val
+		.if (\_val) == 0
+			mov	\_reg, #0
+		.else
+			_mov_imm16	\_reg, (\_val), 0
+			_mov_imm16	\_reg, (\_val), 16
+			_mov_imm16	\_reg, (\_val), 32
+			_mov_imm16	\_reg, (\_val), 48
+		.endif
+	.endm
+
+	/*
+	 * Assembler panic. At the moment there is no support for crash
+	 * reporting in assembler without having a stack available, so for
+	 * the time being just enter into a busy loop and stay there.
+	 */
+	.macro asm_panic
+		b	.
+	.endm
+
+	/*
+	 * Assembler macro to enable asm_assert. Use this macro wherever
+	 * assert is required in assembly. Please note that the macro makes
+	 * use of label '300' to provide the logic and the caller
+	 * should make sure that this label is not used to branch prior
+	 * to calling this macro.
+	 */
+	.macro ASM_ASSERT _cc
+		.ifndef .L_assert_filename
+			.pushsection .rodata.str1.1, "aS"
+			.L_assert_filename:
+				.string	__FILE__
+			.popsection
+		.endif
+		b.\_cc	300f
+		adr	x0, .L_assert_filename
+		mov	x1, __LINE__
+		asm_panic
+	300:
+	.endm
+
+#endif /* ASM_MACROS_S */
diff --git a/lib/arch/include/aarch64/atomics.h b/lib/arch/include/aarch64/atomics.h
new file mode 100644
index 0000000..171692d
--- /dev/null
+++ b/lib/arch/include/aarch64/atomics.h
@@ -0,0 +1,112 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef ATOMICS_H
+#define ATOMICS_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+/*
+ * Atomically adds @val to the 64-bit value stored at memory location @loc.
+ */
+static inline void atomic_add_64(uint64_t *loc, long val)
+{
+	asm volatile(
+	"	stadd %[val], %[loc]\n"
+	: [loc] "+Q" (*loc)
+	: [val] "r" (val)
+	: "memory");
+}
+
+/*
+ * Atomically adds @val to the 64-bit value stored at memory location @loc.
+ * Stores to memory with release semantics.
+ * Returns the old value.
+ */
+static inline unsigned long atomic_load_add_release_64(uint64_t *loc, long val)
+{
+	unsigned long old_val;
+
+	asm volatile(
+	"	ldaddl %[val], %[old_val], %[loc]\n"
+	: [loc] "+Q" (*loc),
+	  [old_val] "=r" (old_val)
+	: [val] "r" (val)
+	: "memory");
+
+	return old_val;
+}
+
+/*
+ * Atomically set bit @bit in value pointed to by @loc with release semantics.
+ */
+static inline void atomic_bit_set_release_64(uint64_t *loc, int bit)
+{
+	uint64_t mask = (1UL << bit);
+
+	asm volatile(
+	"	stsetl %[mask], %[loc]\n"
+	: [loc] "+Q" (*loc)
+	: [mask] "r" (mask)
+	: "memory"
+	);
+}
+
+/*
+ * Atomically clear bit @bit in value pointed to by @loc with release semantics.
+ */
+static inline void atomic_bit_clear_release_64(uint64_t *loc, int bit)
+{
+	uint64_t mask = (1UL << bit);
+
+	asm volatile(
+	"	stclrl %[mask], %[loc]\n"
+	: [loc] "+Q" (*loc)
+	: [mask] "r" (mask)
+	: "memory"
+	);
+}
+
+/*
+ * Test bit @bit in value pointed to by @loc with acquire semantics.
+ */
+static inline bool atomic_test_bit_acquire_64(uint64_t *loc, int bit)
+{
+	uint64_t val;
+	uint64_t mask = (1UL << bit);
+
+	asm volatile(
+	"	ldar %[val], %[loc]\n"
+	: [val] "=r" (val)
+	: [loc] "Q" (*loc)
+	: "memory"
+	);
+
+	return ((val & mask) != 0UL);
+}
+
+/*
+ * Atomically set bit @bit in value pointed to by @loc
+ * with acquire and release semantics.
+ * Return True if the previous state of @bit was 1, False otherwise.
+ */
+static inline bool atomic_bit_set_acquire_release_64(uint64_t *loc, int bit)
+{
+	uint64_t val;
+	uint64_t mask = (1UL << bit);
+
+	asm volatile(
+	"	ldsetal %[mask], %[val], %[loc]\n"
+	: [loc] "+Q" (*loc),
+	  [val] "=r" (val)
+	: [mask] "r" (mask)
+	: "memory"
+	);
+
+	return ((val & mask) != 0UL);
+}
+
+#endif /* ATOMICS_H */
diff --git a/lib/arch/include/aarch64/cpuid.h b/lib/arch/include/aarch64/cpuid.h
new file mode 100644
index 0000000..1176a93
--- /dev/null
+++ b/lib/arch/include/aarch64/cpuid.h
@@ -0,0 +1,16 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef CPUID_H
+#define CPUID_H
+
+#include <arch_helpers.h>
+
+static inline unsigned int my_cpuid(void)
+{
+	return (unsigned int)read_tpidr_el2();
+}
+
+#endif /* CPUID_H */
diff --git a/lib/arch/include/aarch64/entropy.h b/lib/arch/include/aarch64/entropy.h
new file mode 100644
index 0000000..c45c159
--- /dev/null
+++ b/lib/arch/include/aarch64/entropy.h
@@ -0,0 +1,34 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef ENTROPY_H
+#define ENTROPY_H
+
+#include <arch.h>
+#include <utils_def.h>
+
+/*
+ * Write 8 bytes of random data in random. Returns true on success, false on
+ * failure.
+ */
+static inline bool arch_collect_entropy(uint64_t *random)
+{
+	unsigned long rc;
+	uint64_t val;
+
+	asm volatile(
+	"	mrs  %[val], " __XSTRING(RNDR) "\n"
+	"	str  %[val], %[random_ptr]\n"
+	"	cset %[rc], ne\n" /* RNDR sets NZCV to 0b0100 on failure */
+	: [random_ptr] "=m" (*random),
+	  [rc] "=r" (rc),
+	  [val] "=r" (val)
+	:
+	: "cc"
+	);
+	return (rc == 1);
+}
+
+#endif /* ENTROPY_H */
diff --git a/lib/arch/include/aarch64/instr_helpers.h b/lib/arch/include/aarch64/instr_helpers.h
new file mode 100644
index 0000000..ebedb22
--- /dev/null
+++ b/lib/arch/include/aarch64/instr_helpers.h
@@ -0,0 +1,71 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef INSTR_HELPERS_H
+#define INSTR_HELPERS_H
+
+#include <types.h>
+
+/**********************************************************************
+ * Macros which create inline functions to read or write CPU system
+ * registers
+ *********************************************************************/
+
+#define DEFINE_SYSREG_READ_FUNC_(_name, _reg_name)		\
+static inline u_register_t read_ ## _name(void)			\
+{								\
+	u_register_t v;						\
+	__asm__ volatile ("mrs %0, " #_reg_name : "=r" (v));	\
+	return v;						\
+}
+
+#define DEFINE_SYSREG_WRITE_FUNC_(_name, _reg_name)		\
+static inline void write_ ## _name(u_register_t v)		\
+{								\
+	(void)v; /* To avoid MISRA-C:2012-2.7 warnings */ \
+	__asm__ volatile ("msr " #_reg_name ", %0" : : "r" (v));\
+}
+
+#define SYSREG_WRITE_CONST(reg_name, v)				\
+	__asm__ volatile ("msr " #reg_name ", %0" : : "i" (v))
+
+/**********************************************************************
+ * Macros to create inline functions for system instructions
+ *********************************************************************/
+
+/* Define function for simple system instruction */
+#define DEFINE_SYSOP_FUNC(_op)				\
+static inline void (_op)(void)				\
+{							\
+	__asm__ (#_op);					\
+}
+
+/* Define function for system instruction with register parameter */
+#define DEFINE_SYSOP_PARAM_FUNC(_op)			\
+static inline void (_op)(uint64_t v)			\
+{							\
+	(void)v; /* To avoid MISRA-C:2012-2.7 warnings */ \
+	 __asm__ (#_op "  %0" : : "r" (v));		\
+}
+
+/* Define function for system instruction with type specifier */
+#define DEFINE_SYSOP_TYPE_FUNC(_op, _type)		\
+static inline void (_op ## _type)(void)			\
+{							\
+	__asm__ (#_op " " #_type : : : "memory");			\
+}
+
+/* Define function for system instruction with register parameter */
+#define DEFINE_SYSOP_TYPE_PARAM_FUNC(_op, _type)	\
+static inline void (_op ## _type)(uint64_t v)		\
+{							\
+	(void)v; /* To avoid MISRA-C:2012-2.7 warnings */ \
+	 __asm__ (#_op " " #_type ", %0" : : "r" (v));	\
+}
+
+#define dsb(scope) asm volatile("dsb " #scope : : : "memory")
+#define dmb(scope) asm volatile("dmb " #scope : : : "memory")
+
+#endif /* INSTR_HELPERS_H */
diff --git a/lib/arch/include/aarch64/memory.h b/lib/arch/include/aarch64/memory.h
new file mode 100644
index 0000000..34fe271
--- /dev/null
+++ b/lib/arch/include/aarch64/memory.h
@@ -0,0 +1,74 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef MEMORY_H
+#define MEMORY_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+/* Single-Copy Atomic 64-bit write */
+static inline void __sca_write64(uint64_t *ptr, uint64_t val)
+{
+	/* To avoid misra-c2012-2.7 warnings */
+	(void)ptr;
+	(void)val;
+
+	asm volatile(
+	"	str %[val], %[ptr]\n"
+	: [ptr] "=m" (*ptr)
+	: [val] "r" (val)
+	);
+}
+#define SCA_WRITE64(_p, _v) __sca_write64((void *)(_p), ((uint64_t)(_v)))
+
+/* Single-Copy Atomic 64-bit write with RELEASE memory ordering semantics*/
+static inline void __sca_write64_release(uint64_t *ptr, uint64_t val)
+{
+	asm volatile(
+	"	stlr %[val], %[ptr]\n"
+	: [ptr] "=Q" (*ptr)
+	: [val] "r" (val)
+	);
+}
+#define SCA_WRITE64_RELEASE(_p, _v) __sca_write64_release((void *)(_p), ((uint64_t)(_v)))
+
+/* Single-Copy Atomic 64-bit read */
+static inline uint64_t __sca_read64(uint64_t *ptr)
+{
+	uint64_t val;
+
+	/* To avoid misra-c2012-2.7 warnings */
+	(void)ptr;
+
+	asm volatile(
+	"	ldr	%[val], %[ptr]\n"
+	: [val] "=r" (val)
+	: [ptr] "m" (*ptr)
+	);
+
+	return val;
+}
+#define SCA_READ64(_p) ((typeof(*(_p)))__sca_read64((void *)(_p)))
+
+/* Single-Copy Atomic 64-bit read with ACQUIRE memory ordering semantics */
+static inline uint64_t __sca_read64_acquire(uint64_t *ptr)
+{
+	uint64_t val;
+
+	/* To avoid misra-c2012-2.7 warnings */
+	(void)ptr;
+
+	asm volatile(
+	"	ldar	%[val], %[ptr]\n"
+	: [val] "=r" (val)
+	: [ptr] "Q" (*ptr)
+	);
+
+	return val;
+}
+#define SCA_READ64_ACQUIRE(_p) ((typeof(*(_p)))__sca_read64_acquire((void *)(_p)))
+
+#endif /* MEMORY_H */
diff --git a/lib/arch/include/aarch64/mmio.h b/lib/arch/include/aarch64/mmio.h
new file mode 100644
index 0000000..ba8c82c3
--- /dev/null
+++ b/lib/arch/include/aarch64/mmio.h
@@ -0,0 +1,80 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef MMIO_H
+#define MMIO_H
+
+#include <arch_helpers.h>
+#include <stdint.h>
+
+static inline uint8_t read8(volatile void *addr)
+{
+	uint8_t val;
+
+	dsb(ld);
+	asm volatile("ldrb %w0, [%1]" : "=r" (val) : "r" (addr));
+	dsb(ld);
+	return val;
+}
+
+static inline void write8(uint8_t val, volatile void *addr)
+{
+	dsb(st);
+	asm volatile("strb %w0, [%1]" :  : "r" (val), "r" (addr));
+	dsb(st);
+}
+
+static inline uint16_t read16(volatile void *addr)
+{
+	uint16_t val;
+
+	dsb(ld);
+	asm volatile("ldrh %w0, [%1]" : "=r" (val) : "r" (addr));
+	dsb(ld);
+	return val;
+}
+
+static inline void write16(uint16_t val, volatile void *addr)
+{
+	dsb(st);
+	asm volatile("strh %w0, [%1]" :  : "r" (val), "r" (addr));
+	dsb(st);
+}
+
+static inline uint32_t read32(volatile void *addr)
+{
+	uint32_t val;
+
+	dsb(ld);
+	asm volatile("ldr %w0, [%1]" : "=r" (val) : "r" (addr));
+	dsb(ld);
+	return val;
+}
+
+static inline void write32(uint32_t val, volatile void *addr)
+{
+	dsb(st);
+	asm volatile("str %w0, [%1]" :  : "r" (val), "r" (addr));
+	dsb(st);
+}
+
+static inline uint64_t read64(volatile void *addr)
+{
+	uint64_t val;
+
+	dsb(ld);
+	asm volatile("ldr %0, [%1]" : "=r" (val) : "r" (addr));
+	dsb(ld);
+	return val;
+}
+
+static inline void write64(uint64_t val, volatile void *addr)
+{
+	dsb(st);
+	asm volatile("str %0, [%1]" :  : "r" (val), "r" (addr));
+	dsb(st);
+}
+
+#endif /* MMIO_H */
diff --git a/lib/arch/include/aarch64/spinlock.h b/lib/arch/include/aarch64/spinlock.h
new file mode 100644
index 0000000..37cab6e
--- /dev/null
+++ b/lib/arch/include/aarch64/spinlock.h
@@ -0,0 +1,47 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef SPINLOCK_H
+#define SPINLOCK_H
+
+/*
+ * A trivial spinlock implementation, per ARM DDI 0487D.a, section K11.3.4.
+ */
+
+typedef struct {
+	unsigned int val;
+} spinlock_t;
+
+static inline void spinlock_acquire(spinlock_t *l)
+{
+	unsigned int tmp;
+
+	asm volatile(
+	"	sevl\n"
+	"	prfm	pstl1keep, %[lock]\n"
+	"1:\n"
+	"	wfe\n"
+	"	ldaxr	%w[tmp], %[lock]\n"
+	"	cbnz	%w[tmp], 1b\n"
+	"	stxr	%w[tmp], %w[one], %[lock]\n"
+	"	cbnz	%w[tmp], 1b\n"
+	: [lock] "+Q" (l->val),
+	  [tmp] "=&r" (tmp)
+	: [one] "r" (1)
+	: "memory"
+	);
+}
+
+static inline void spinlock_release(spinlock_t *l)
+{
+	asm volatile(
+	"	stlr	wzr, %[lock]\n"
+	: [lock] "+Q" (l->val)
+	:
+	: "memory"
+	);
+}
+
+#endif /* SPINLOCK_H */
diff --git a/lib/arch/include/arch.h b/lib/arch/include/arch.h
new file mode 100644
index 0000000..a67baa4
--- /dev/null
+++ b/lib/arch/include/arch.h
@@ -0,0 +1,939 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef ARCH_H
+#define ARCH_H
+
+#include <utils_def.h>
+
+/* Cache line size */
+#define CACHE_WRITEBACK_GRANULE	UL(64)
+
+/* Timer interrupt IDs defined by the Server Base System Architecture */
+#define EL1_VIRT_TIMER_PPI	UL(27)
+#define EL1_PHYS_TIMER_PPI	UL(30)
+
+/* Counter-timer Physical Offset register */
+#define CNTPOFF_EL2		S3_4_C14_C0_6
+
+/* MPAM0 Register */
+#define MPAM0_EL1		S3_0_C10_C5_1
+
+/* Interrupt Controller registers */
+#define ICC_HPPIR1_EL1		S3_0_C12_C12_2
+#define ICC_SRE_EL2		S3_4_C12_C9_5
+
+/* Interrupt Controller Control Register */
+#define	ICC_CTLR_EL1		S3_0_C12_C12_4
+
+#define ICC_CTLR_EL1_EXT_RANGE_SHIFT	UL(19)
+#define ICC_CTLR_EL1_EXT_RANGE_BIT	INPLACE(ICC_CTLR_EL1_EXT_RANGE, UL(1))
+
+/* Virtual GIC registers */
+#define ICH_AP0R0_EL2		S3_4_C12_C8_0
+#define ICH_AP0R1_EL2		S3_4_C12_C8_1
+#define ICH_AP0R2_EL2		S3_4_C12_C8_2
+#define ICH_AP0R3_EL2		S3_4_C12_C8_3
+#define ICH_AP1R0_EL2		S3_4_C12_C9_0
+#define ICH_AP1R1_EL2		S3_4_C12_C9_1
+#define ICH_AP1R2_EL2		S3_4_C12_C9_2
+#define ICH_AP1R3_EL2		S3_4_C12_C9_3
+
+#define ICH_LR0_EL2		S3_4_C12_C12_0
+#define ICH_LR1_EL2		S3_4_C12_C12_1
+#define ICH_LR2_EL2		S3_4_C12_C12_2
+#define ICH_LR3_EL2		S3_4_C12_C12_3
+#define ICH_LR4_EL2		S3_4_C12_C12_4
+#define ICH_LR5_EL2		S3_4_C12_C12_5
+#define ICH_LR6_EL2		S3_4_C12_C12_6
+#define ICH_LR7_EL2		S3_4_C12_C12_7
+#define ICH_LR8_EL2		S3_4_C12_C13_0
+#define ICH_LR9_EL2		S3_4_C12_C13_1
+#define ICH_LR10_EL2		S3_4_C12_C13_2
+#define ICH_LR11_EL2		S3_4_C12_C13_3
+#define ICH_LR12_EL2		S3_4_C12_C13_4
+#define ICH_LR13_EL2		S3_4_C12_C13_5
+#define ICH_LR14_EL2		S3_4_C12_C13_6
+#define ICH_LR15_EL2		S3_4_C12_C13_7
+
+#define ICH_HCR_EL2		S3_4_C12_C11_0
+#define ICH_VTR_EL2		S3_4_C12_C11_1
+#define ICH_MISR_EL2		S3_4_C12_C11_2
+#define ICH_VMCR_EL2		S3_4_C12_C11_7
+
+/* RNDR definition */
+#define RNDR			S3_3_C2_C4_0
+
+/* CLIDR definitions */
+#define LOC_SHIFT		U(24)
+#define CTYPE_SHIFT(n)		U(3 * ((n) - 1))
+#define CLIDR_FIELD_WIDTH	U(3)
+
+/* CSSELR definitions */
+#define LEVEL_SHIFT		U(1)
+
+/* Data cache set/way op type defines */
+#define DCISW			U(0x0)
+#define DCCISW			U(0x1)
+#define DCCSW			U(0x2)
+
+#define TCR_EL2_T0SZ_SHIFT	UL(0)
+#define TCR_EL2_T0SZ_WIDTH	UL(6)
+#define TCR_EL2_T0SZ_MASK	MASK(TCR_EL2_T0SZ)
+
+#define TCR_EL2_T1SZ_SHIFT	UL(16)
+#define TCR_EL2_T1SZ_WIDTH	UL(6)
+#define TCR_EL2_T1SZ_MASK	MASK(TCR_EL2_T0SZ)
+
+#define TCR_EL2_EPD0_SHIFT	UL(7)
+#define TCR_EL2_EPD0_WIDTH	UL(1)
+#define TCR_EL2_EPD0_BIT	INPLACE(TCR_EL2_EPD0, UL(1))
+
+#define TCR_EL2_IRGN0_SHIFT	UL(8)
+#define TCR_EL2_IRGN0_WIDTH	UL(2)
+#define TCR_EL2_IRGN0_WBWA	INPLACE(TCR_EL2_IRGN0, UL(1))
+
+#define TCR_EL2_ORGN0_SHIFT	UL(10)
+#define TCR_EL2_ORGN0_WIDTH	UL(2)
+#define TCR_EL2_ORGN0_WBWA	INPLACE(TCR_EL2_ORGN0, UL(1))
+
+#define TCR_EL2_IRGN1_SHIFT	UL(24)
+#define TCR_EL2_IRGN1_WIDTH	UL(2)
+#define TCR_EL2_IRGN1_WBWA	INPLACE(TCR_EL2_IRGN1, UL(1))
+
+#define TCR_EL2_ORGN1_SHIFT	UL(26)
+#define TCR_EL2_ORGN1_WIDTH	UL(2)
+#define TCR_EL2_ORGN1_WBWA	INPLACE(TCR_EL2_ORGN1, UL(1))
+
+#define TCR_EL2_SH0_SHIFT	UL(12)
+#define TCR_EL2_SH0_WIDTH	UL(2)
+#define TCR_EL2_SH0_IS		INPLACE(TCR_EL2_SH0, UL(3))
+
+#define TCR_EL2_SH1_SHIFT	UL(28)
+#define TCR_EL2_SH1_WIDTH	UL(2)
+#define TCR_EL2_SH1_IS		INPLACE(TCR_EL2_SH1, UL(3))
+
+#define TCR_EL2_TG0_SHIFT	UL(14)
+#define TCR_EL2_TG0_WIDTH	UL(2)
+#define TCR_EL2_TG0_4K		INPLACE(TCR_EL2_TG0, UL(0))
+
+#define TCR_EL2_TG1_SHIFT	UL(30)
+#define TCR_EL2_TG1_WIDTH	UL(2)
+#define TCR_EL2_TG1_4K		INPLACE(TCR_EL2_TG1, UL(0))
+
+#define TCR_EL2_IPS_SHIFT	UL(32)
+#define TCR_EL2_IPS_WIDTH	UL(3)
+#define TCR_PS_BITS_4GB		INPLACE(TCR_EL2_IPS, UL(0))
+#define TCR_PS_BITS_64GB	INPLACE(TCR_EL2_IPS, UL(1))
+#define TCR_PS_BITS_1TB		INPLACE(TCR_EL2_IPS, UL(2))
+#define TCR_PS_BITS_4TB		INPLACE(TCR_EL2_IPS, UL(3))
+#define TCR_PS_BITS_16TB	INPLACE(TCR_EL2_IPS, UL(4))
+#define TCR_PS_BITS_256TB	INPLACE(TCR_EL2_IPS, UL(5))
+
+#define ADDR_MASK_48_TO_63	ULL(0xFFFF000000000000)
+#define ADDR_MASK_44_TO_47	ULL(0x0000F00000000000)
+#define ADDR_MASK_42_TO_43	ULL(0x00000C0000000000)
+#define ADDR_MASK_40_TO_41	ULL(0x0000030000000000)
+#define ADDR_MASK_36_TO_39	ULL(0x000000F000000000)
+#define ADDR_MASK_32_TO_35	ULL(0x0000000F00000000)
+
+#define TCR_EL2_AS		(UL(1) << 36)
+#define TCR_EL2_HPD0		(UL(1) << 41)
+#define TCR_EL2_HPD1		(UL(1) << 42)
+#define TCR_EL2_E0PD1		(UL(1) << 56)	/* TODO: ARMv8.5-E0PD, otherwise RES0 */
+
+#define TCR_TxSZ_MIN		UL(16)
+#define TCR_TxSZ_MAX		UL(39)
+#define TCR_TxSZ_MAX_TTST	UL(48)
+
+/* HCR definitions */
+#define HCR_FWB		(UL(1) << 46)
+#define HCR_TEA		(UL(1) << 37)
+#define HCR_API		(UL(1) << 41)
+#define HCR_APK		(UL(1) << 40)
+#define HCR_TERR	(UL(1) << 36)
+#define HCR_TLOR	(UL(1) << 35)
+#define HCR_E2H		(UL(1) << 34)
+#define HCR_RW		(UL(1) << 31)
+#define HCR_TGE		(UL(1) << 27)
+#define HCR_TSW		(UL(1) << 22)
+#define HCR_TACR	(UL(1) << 21)
+#define HCR_TIDCP	(UL(1) << 20)
+#define HCR_TSC		(UL(1) << 19)
+#define HCR_TID3	(UL(1) << 18)
+#define HCR_TWE		(UL(1) << 14)
+#define HCR_TWI		(UL(1) << 13)
+#define HCR_VSE		(UL(1) << 8)
+
+#define HCR_BSU_SHIFT	10
+#define HCR_BSU_WIDTH	2
+#define HCR_BSU_IS	INPLACE(HCR_BSU, 1) /* Barriers are promoted to IS */
+
+#define HCR_FB		(UL(1) << 9)
+#define HCR_VI		(UL(1) << 7)
+#define HCR_AMO		(UL(1) << 5)
+#define HCR_IMO		(UL(1) << 4)
+#define HCR_FMO		(UL(1) << 3)
+#define HCR_PTW		(UL(1) << 2)
+#define HCR_SWIO	(UL(1) << 1)
+#define HCR_VM		(UL(1) << 0)
+
+/* TODO verify that all the traps are enabled */
+#define HCR_FLAGS (HCR_FWB | HCR_E2H | HCR_RW | HCR_TSC | HCR_AMO | \
+	HCR_BSU_IS | HCR_IMO | HCR_FMO | HCR_PTW | HCR_SWIO | HCR_VM | \
+	HCR_TID3 | HCR_TEA)
+
+#define HCR_EL2_INIT		(HCR_TGE | HCR_E2H | HCR_TEA)
+
+#define MAIR_ELx_ATTR0_SHIFT	0
+#define MAIR_ELx_ATTR0_WIDTH	8
+#define MAIR_ELx_ATTR0_MASK	MASK(MAIR_ELx_ATTR0)
+
+/*******************************************************************************
+ * Definitions of MAIR encodings for device and normal memory
+ ******************************************************************************/
+/*
+ * MAIR encodings for device memory attributes.
+ */
+#define MAIR_DEV_NGNRNE		UL(0x0) /* Device nGnRnE */
+#define MAIR_DEV_NGNRNE_IDX	0x1
+
+#define MAIR_DEV_NGNRE		UL(0x4)
+
+#define MAIR_NIOWBNTRW		0xff
+#define MAIR_NIOWBNTRW_IDX	0x0
+
+/*
+ * MAIR encodings for normal memory attributes.
+ *
+ * Cache Policy
+ *  WT:	 Write Through
+ *  WB:	 Write Back
+ *  NC:	 Non-Cacheable
+ *
+ * Transient Hint
+ *  NTR: Non-Transient
+ *  TR:	 Transient
+ *
+ * Allocation Policy
+ *  RA:	 Read Allocate
+ *  WA:	 Write Allocate
+ *  RWA: Read and Write Allocate
+ *  NA:	 No Allocation
+ */
+#define MAIR_NORM_WT_TR_WA	UL(0x1)
+#define MAIR_NORM_WT_TR_RA	UL(0x2)
+#define MAIR_NORM_WT_TR_RWA	UL(0x3)
+#define MAIR_NORM_NC		UL(0x4)
+#define MAIR_NORM_WB_TR_WA	UL(0x5)
+#define MAIR_NORM_WB_TR_RA	UL(0x6)
+#define MAIR_NORM_WB_TR_RWA	UL(0x7)
+#define MAIR_NORM_WT_NTR_NA	UL(0x8)
+#define MAIR_NORM_WT_NTR_WA	UL(0x9)
+#define MAIR_NORM_WT_NTR_RA	UL(0xa)
+#define MAIR_NORM_WT_NTR_RWA	UL(0xb)
+#define MAIR_NORM_WB_NTR_NA	UL(0xc)
+#define MAIR_NORM_WB_NTR_WA	UL(0xd)
+#define MAIR_NORM_WB_NTR_RA	UL(0xe)
+#define MAIR_NORM_WB_NTR_RWA	UL(0xf)
+
+#define MAIR_NORM_OUTER_SHIFT	U(4)
+
+#define MAKE_MAIR_NORMAL_MEMORY(inner, outer)	\
+		((inner) | ((outer) << MAIR_NORM_OUTER_SHIFT))
+
+#define MAKE_MAIR_NORMAL_MEMORY_IO(_mair) \
+		MAKE_MAIR_NORMAL_MEMORY(_mair, _mair)
+
+/*
+ * TTBR Definitions
+ */
+#define TTBR_CNP_BIT		UL(0x1)
+
+#define TTBRx_EL2_CnP_SHIFT	0
+#define TTBRx_EL2_CnP_WIDTH	1
+
+#define TTBRx_EL2_BADDR_SHIFT	1
+#define TTBRx_EL2_BADDR_WIDTH	47
+
+#define TTBRx_EL2_ASID_SHIFT	48
+#define TTBRx_EL2_ASID_WIDTH	16
+
+/*
+ * VTTBR Definitions
+ */
+#define VTTBR_EL2_VMID_SHIFT	48
+#define VTTBR_EL2_VMID_WIDTH	16
+
+/*
+ * ESR Definitions
+ */
+#define ESR_EL2_EC_SHIFT	26
+#define ESR_EL2_EC_WIDTH	6
+#define ESR_EL2_EC_MASK		MASK(ESR_EL2_EC)
+
+#define ESR_EL2_IL_SHIFT	25
+#define ESR_EL2_IL_WIDTH	1
+#define ESR_EL2_IL_MASK		MASK(ESR_EL2_IL)
+
+#define ESR_EL2_ISS_SHIFT	0
+#define ESR_EL2_ISS_WIDTH	25
+#define ESR_EL2_ISS_MASK	MASK(ESR_EL2_ISS)
+
+#define ESR_EL2_EC_UNKNOWN	INPLACE(ESR_EL2_EC, 0)
+#define ESR_EL2_EC_WFX		INPLACE(ESR_EL2_EC, 1)
+#define ESR_EL2_EC_FPU		INPLACE(ESR_EL2_EC, 7)
+#define ESR_EL2_EC_SVC		INPLACE(ESR_EL2_EC, 21)
+#define ESR_EL2_EC_HVC		INPLACE(ESR_EL2_EC, 22)
+#define ESR_EL2_EC_SMC		INPLACE(ESR_EL2_EC, 23)
+#define ESR_EL2_EC_SYSREG	INPLACE(ESR_EL2_EC, 24)
+#define ESR_EL2_EC_SVE		INPLACE(ESR_EL2_EC, 25)
+#define ESR_EL2_EC_INST_ABORT		INPLACE(ESR_EL2_EC, 32)
+#define ESR_EL2_EC_INST_ABORT_SEL	INPLACE(ESR_EL2_EC, 33)
+#define ESR_EL2_EC_DATA_ABORT		INPLACE(ESR_EL2_EC, 36)
+#define ESR_EL2_EC_DATA_ABORT_SEL	INPLACE(ESR_EL2_EC, 37)
+#define ESR_EL2_EC_SERROR		INPLACE(ESR_EL2_EC, 47)
+
+/* Data/Instruction Abort ESR fields */
+#define ESR_EL2_ABORT_ISV_BIT		(1UL << 24)
+
+#define ESR_EL2_ABORT_SAS_SHIFT		22
+#define ESR_EL2_ABORT_SAS_WIDTH		2
+#define ESR_EL2_ABORT_SAS_MASK		MASK(ESR_EL2_ABORT_SAS)
+
+#define ESR_EL2_ABORT_SAS_BYTE_VAL	0
+#define ESR_EL2_ABORT_SAS_HWORD_VAL	1
+#define ESR_EL2_ABORT_SAS_WORD_VAL	2
+#define ESR_EL2_ABORT_SAS_DWORD_VAL	3
+
+#define ESR_EL2_ABORT_SSE_BIT		(1UL << 21)
+
+#define ESR_EL2_ABORT_SRT_SHIFT		16
+#define ESR_EL2_ABORT_SRT_WIDTH		5
+#define ESR_EL2_ABORT_SRT_MASK		MASK(ESR_EL2_ABORT_SRT)
+
+#define ESR_EL2_ABORT_SET_SHIFT		11
+#define ESR_EL2_ABORT_SET_WIDTH		2
+#define ESR_EL2_ABORT_SET_MASK		MASK(ESR_EL2_ABORT_SET)
+#define ESR_EL2_ABORT_SET_UER		INPLACE(ESR_EL2_ABORT_SET, 0)
+#define ESR_EL2_ABORT_SET_UC		INPLACE(ESR_EL2_ABORT_SET, 2)
+#define ESR_EL2_ABORT_SET_UEO		INPLACE(ESR_EL2_ABORT_SET, 3)
+
+#define ESR_EL2_ABORT_SF_BIT		(1UL << 15)
+#define ESR_EL2_ABORT_FNV_BIT		(1UL << 10)
+#define ESR_EL2_ABORT_EA_BIT		(1UL << 9)
+#define ESR_EL2_ABORT_S1PTW_BIT		(1UL << 7)
+#define ESR_EL2_ABORT_WNR_BIT		(1UL << 6)
+#define ESR_EL2_ABORT_FSC_SHIFT		0
+#define ESR_EL2_ABORT_FSC_WIDTH		6
+#define ESR_EL2_ABORT_FSC_MASK		MASK(ESR_EL2_ABORT_FSC)
+
+#define ESR_EL2_ABORT_FSC_TRANSLATION_FAULT	0x04
+#define ESR_EL2_ABORT_FSC_PERMISSION_FAULT	0x0c
+#define ESR_EL2_ABORT_FSC_TRANSLATION_FAULT_L0	0x04
+#define ESR_EL2_ABORT_FSC_SEA			0x10
+#define ESR_EL2_ABORT_FSC_SEA_TTW_START		0x13
+#define ESR_EL2_ABORT_FSC_SEA_TTW_END		0x17
+#define ESR_EL2_ABORT_FSC_GPF			0x28
+#define ESR_EL2_ABORT_FSC_LEVEL_SHIFT		0
+#define ESR_EL2_ABORT_FSC_LEVEL_WIDTH		2
+#define ESR_EL2_ABORT_FSC_LEVEL_MASK		MASK(ESR_EL2_ABORT_FSC_LEVEL)
+
+/* The ESR fields that are reported to the host on Instr./Data Synchronous Abort */
+#define ESR_NONEMULATED_ABORT_MASK     ( \
+		ESR_EL2_EC_MASK        | \
+		ESR_EL2_ABORT_SET_MASK | \
+		ESR_EL2_ABORT_FNV_BIT  | \
+		ESR_EL2_ABORT_EA_BIT   | \
+		ESR_EL2_ABORT_FSC_MASK)
+
+#define ESR_EMULATED_ABORT_MASK            ( \
+		ESR_NONEMULATED_ABORT_MASK | \
+		ESR_EL2_ABORT_ISV_BIT      | \
+		ESR_EL2_ABORT_SAS_MASK     | \
+		ESR_EL2_ABORT_SF_BIT       | \
+		ESR_EL2_ABORT_WNR_BIT)
+
+#define ESR_EL2_SERROR_DFSC_SHIFT	0
+#define ESR_EL2_SERROR_DFSC_WIDTH	6
+#define ESR_EL2_SERROR_DFSC_MASK	MASK(ESR_EL2_SERROR_DFSC)
+#define ESR_EL2_SERROR_DFSC_UNCAT	INPLACE(ESR_EL2_SERROR_DFSC, 0)
+#define ESR_EL2_SERROR_DFSC_ASYNC	INPLACE(ESR_EL2_SERROR_DFSC, 1)
+
+#define ESR_EL2_SERROR_EA_BIT		(1UL << 9)
+
+#define ESR_EL2_SERROR_AET_SHIFT	10
+#define ESR_EL2_SERROR_AET_WIDTH	3
+#define ESR_EL2_SERROR_AET_MASK		MASK(ESR_EL2_SERROR_AET)
+#define ESR_EL2_SERROR_AET_UC		INPLACE(ESR_EL2_SERROR_AET, 0)
+#define ESR_EL2_SERROR_AET_UEU		INPLACE(ESR_EL2_SERROR_AET, 1)
+#define ESR_EL2_SERROR_AET_UEO		INPLACE(ESR_EL2_SERROR_AET, 2)
+#define ESR_EL2_SERROR_AET_UER		INPLACE(ESR_EL2_SERROR_AET, 3)
+#define ESR_EL2_SERROR_AET_CE		INPLACE(ESR_EL2_SERROR_AET, 6)
+
+#define ESR_EL2_SERROR_IESB_BIT		(1UL << 13)
+#define ESR_EL2_SERROR_IDS_BIT		(1UL << 24)
+
+/* The ESR fields that are reported to the host on SError */
+#define ESR_SERROR_MASK			( \
+		ESR_EL2_SERROR_IDS_BIT  | \
+		ESR_EL2_SERROR_AET_MASK | \
+		ESR_EL2_SERROR_EA_BIT   | \
+		ESR_EL2_SERROR_DFSC_MASK)
+
+#define ESR_EL2_SYSREG_TRAP_OP0_SHIFT	20
+#define ESR_EL2_SYSREG_TRAP_OP0_WIDTH	2
+#define ESR_EL2_SYSREG_TRAP_OP0_MASK	MASK(ESR_EL2_SYSREG_TRAP_OP0)
+
+#define ESR_EL2_SYSREG_TRAP_OP2_SHIFT	17
+#define ESR_EL2_SYSREG_TRAP_OP2_WIDTH	3
+#define ESR_EL2_SYSREG_TRAP_OP2_MASK	MASK(ESR_EL2_SYSREG_TRAP_OP2)
+
+#define ESR_EL2_SYSREG_TRAP_OP1_SHIFT	14
+#define ESR_EL2_SYSREG_TRAP_OP1_WIDTH	3
+#define ESR_EL2_SYSREG_TRAP_OP1_MASK	MASK(ESR_EL2_SYSREG_TRAP_OP1)
+
+#define ESR_EL2_SYSREG_TRAP_CRN_SHIFT	10
+#define ESR_EL2_SYSREG_TRAP_CRN_WIDTH	4
+#define ESR_EL2_SYSREG_TRAP_CRN_MASK	MASK(ESR_EL2_SYSREG_TRAP_CRN)
+
+#define ESR_EL2_SYSREG_TRAP_RT_SHIFT	5
+#define ESR_EL2_SYSREG_TRAP_RT_WIDTH	5
+#define ESR_EL2_SYSREG_TRAP_RT_MASK	MASK(ESR_EL2_SYSREG_TRAP_RT)
+
+#define ESR_EL2_SYSREG_TRAP_CRM_SHIFT	1
+#define ESR_EL2_SYSREG_TRAP_CRM_WIDTH	4
+#define ESR_EL2_SYSREG_TRAP_CRM_MASK	MASK(ESR_EL2_SYSREG_TRAP_CRM)
+
+/* WFx ESR fields */
+#define ESR_EL2_WFx_TI_BIT		(1UL << 0)
+
+/* xVC ESR fields */
+#define ESR_EL2_xVC_IMM_SHIFT		0
+#define ESR_EL2_xVC_IMM_WIDTH		16
+#define ESR_EL2_xVC_IMM_MASK		MASK(ESR_EL2_xVC_IMM)
+
+/* ID_AA64PFR0_EL1 definitions */
+#define ID_AA64PFR0_EL1_SVE_SHIFT	UL(32)
+#define ID_AA64PFR0_EL1_SVE_WIDTH	UL(4)
+#define ID_AA64PFR0_EL1_SVE_MASK	UL(0xf)
+
+#define ID_AA64PFR0_EL1_AMU_SHIFT	UL(44)
+#define ID_AA64PFR0_EL1_AMU_WIDTH	4
+
+/* ID_AA64MMFR0_EL1 definitions */
+#define ID_AA64MMFR0_EL1_PARANGE_SHIFT	U(0)
+#define ID_AA64MMFR0_EL1_PARANGE_MASK	ULL(0xf)
+
+#define PARANGE_0000_WIDTH	U(32)
+#define PARANGE_0001_WIDTH	U(36)
+#define PARANGE_0010_WIDTH	U(40)
+#define PARANGE_0011_WIDTH	U(42)
+#define PARANGE_0100_WIDTH	U(44)
+#define PARANGE_0101_WIDTH	U(48)
+#define PARANGE_0110_WIDTH	U(52)
+
+#define ID_AA64MMFR0_EL1_ECV_SHIFT		U(60)
+#define ID_AA64MMFR0_EL1_ECV_MASK		ULL(0xf)
+#define ID_AA64MMFR0_EL1_ECV_NOT_SUPPORTED	ULL(0x0)
+#define ID_AA64MMFR0_EL1_ECV_SUPPORTED		ULL(0x1)
+#define ID_AA64MMFR0_EL1_ECV_SELF_SYNCH	ULL(0x2)
+
+#define ID_AA64MMFR0_EL1_FGT_SHIFT		U(56)
+#define ID_AA64MMFR0_EL1_FGT_MASK		ULL(0xf)
+#define ID_AA64MMFR0_EL1_FGT_SUPPORTED		ULL(0x1)
+#define ID_AA64MMFR0_EL1_FGT_NOT_SUPPORTED	ULL(0x0)
+
+#define ID_AA64MMFR0_EL1_TGRAN4_2_SHIFT		U(40)
+#define ID_AA64MMFR0_EL1_TGRAN4_2_MASK		ULL(0xf)
+#define ID_AA64MMFR0_EL1_TGRAN4_2_TGRAN4	ULL(0x0)
+#define ID_AA64MMFR0_EL1_TGRAN4_2_NOT_SUPPORTED	ULL(0x1)
+#define ID_AA64MMFR0_EL1_TGRAN4_2_SUPPORTED	ULL(0x2)
+#define ID_AA64MMFR0_EL1_TGRAN4_2_LPA2		ULL(0x3)
+
+#define ID_AA64MMFR0_EL1_TGRAN16_2_SHIFT		U(32)
+#define ID_AA64MMFR0_EL1_TGRAN16_2_MASK			ULL(0xf)
+#define ID_AA64MMFR0_EL1_TGRAN16_2_TGRAN16		ULL(0x0)
+#define ID_AA64MMFR0_EL1_TGRAN16_2_NOT_SUPPORTED	ULL(0x1)
+#define ID_AA64MMFR0_EL1_TGRAN16_2_SUPPORTED		ULL(0x2)
+#define ID_AA64MMFR0_EL1_TGRAN16_2_LPA2			ULL(0x3)
+
+#define ID_AA64MMFR0_EL1_TGRAN4_SHIFT		U(28)
+#define ID_AA64MMFR0_EL1_TGRAN4_MASK		ULL(0xf)
+#define ID_AA64MMFR0_EL1_TGRAN4_SUPPORTED	ULL(0x0)
+#define ID_AA64MMFR0_EL1_TGRAN4_LPA2		ULL(0x1)
+#define ID_AA64MMFR0_EL1_TGRAN4_NOT_SUPPORTED	ULL(0xf)
+
+#define ID_AA64MMFR0_EL1_TGRAN64_SHIFT		UL(24)
+#define ID_AA64MMFR0_EL1_TGRAN64_MASK		UL(0xf)
+#define ID_AA64MMFR0_EL1_TGRAN64_SUPPORTED	UL(0x0)
+#define ID_AA64MMFR0_EL1_TGRAN64_NOT_SUPPORTED	UL(0xf)
+
+#define ID_AA64MMFR0_EL1_TGRAN16_SHIFT		UL(20)
+#define ID_AA64MMFR0_EL1_TGRAN16_MASK		UL(0xf)
+#define ID_AA64MMFR0_EL1_TGRAN16_NOT_SUPPORTED	UL(0x0)
+#define ID_AA64MMFR0_EL1_TGRAN16_SUPPORTED	UL(0x1)
+#define ID_AA64MMFR0_EL1_TGRAN16_LPA2		UL(0x2)
+
+/* RNDR definitions */
+#define ID_AA64ISAR0_RNDR_SHIFT			UL(60)
+#define ID_AA64ISAR0_RNDR_MASK			UL(0xF)
+
+/* ID_AA64MMFR1_EL1 definitions */
+#define ID_AA64MMFR1_EL1_VMIDBits_SHIFT		UL(4)
+#define ID_AA64MMFR1_EL1_VMIDBits_MASK		UL(0xf)
+#define ID_AA64MMFR1_EL1_VMIDBits_8		UL(0)
+#define ID_AA64MMFR1_EL1_VMIDBits_16		UL(2)
+
+/* HPFAR_EL2 definitions */
+#define HPFAR_EL2_FIPA_SHIFT		4
+#define HPFAR_EL2_FIPA_WIDTH		40
+#define HPFAR_EL2_FIPA_MASK		MASK(HPFAR_EL2_FIPA)
+#define HPFAR_EL2_FIPA_OFFSET		8
+
+/* SPSR definitions */
+#define SPSR_EL2_MODE_SHIFT		0
+#define SPSR_EL2_MODE_WIDTH		4
+#define SPSR_EL2_MODE_EL0t		INPLACE(SPSR_EL2_MODE, 0)
+
+#define SPSR_EL2_MODE_SHIFT		0
+#define SPSR_EL2_MODE_WIDTH		4
+#define SPSR_EL2_MODE_EL1h		INPLACE(SPSR_EL2_MODE, 5)
+#define SPSR_EL2_MODE_EL1t		INPLACE(SPSR_EL2_MODE, 4)
+
+/* FIXME: DAIF definitions are redundant here. Might need unification. */
+#define SPSR_EL2_nRW_SHIFT		4
+#define SPSR_EL2_nRW_WIDTH		1
+#define SPSR_EL2_nRW_AARCH64		INPLACE(SPSR_EL2_nRW, 0)
+#define SPSR_EL2_nRW_AARCH32		INPLACE(SPSR_EL2_nRW, 1)
+
+#define SPSR_EL2_DAIF_SHIFT		U(6)
+#define SPSR_EL2_DAIF_MASK		U(0xf)
+
+#define SPSR_EL2_AIF_SHIFT		U(6)
+#define SPSR_EL2_AIF_MASK		U(0x7)
+
+#define SPSR_EL2_F_SHIFT		6
+#define SPSR_EL2_F_WIDTH		1
+#define SPSR_EL2_F_BIT			INPLACE(SPSR_EL2_F, 1)
+#define DAIF_FIQ_BIT			(U(1) << 0)
+
+#define SPSR_EL2_I_SHIFT		7
+#define SPSR_EL2_I_WIDTH		1
+#define SPSR_EL2_I_BIT			INPLACE(SPSR_EL2_I, 1)
+#define DAIF_IRQ_BIT			(U(1) << 1)
+
+#define SPSR_EL2_A_SHIFT		8
+#define SPSR_EL2_A_WIDTH		1
+#define SPSR_EL2_A_BIT			INPLACE(SPSR_EL2_A, 1)
+#define DAIF_ABT_BIT			(U(1) << 2)
+
+#define SPSR_EL2_D_SHIFT		9
+#define SPSR_EL2_D_WIDTH		1
+#define SPSR_EL2_D_BIT			INPLACE(SPSR_EL2_D, 1)
+#define DAIF_DBG_BIT			(U(1) << 3)
+
+#define SPSR_EL2_SSBS_SHIFT		12
+#define SPSR_EL2_SSBS_WIDTH		1
+#define SPSR_EL2_SSBS_BIT		INPLACE(SPSR_EL2_SSBS, 1)
+
+#define SPSR_EL2_IL_SHIFT		20
+#define SPSR_EL2_IL_WIDTH		1
+#define SPSR_EL2_IL_BIT			INPLACE(SPSR_EL2_IL, 1)
+
+#define SPSR_EL2_SS_SHIFT		21
+#define SPSR_EL2_SS_WIDTH		1
+#define SPSR_EL2_SS_BIT			INPLACE(SPSR_EL2_SS, 1)
+
+#define SPSR_EL2_PAN_SHIFT		22
+#define SPSR_EL2_PAN_WIDTH		1
+#define SPSR_EL2_PAN_BIT		INPLACE(SPSR_EL2_PAN, 1)
+
+#define SPSR_EL2_UAO_SHIFT		23
+#define SPSR_EL2_UAO_WIDTH		1
+#define SPSR_EL2_UAO_BIT		INPLACE(SPSR_EL2_UAO, 1)
+
+#define SPSR_EL2_V_SHIFT		28
+#define SPSR_EL2_V_WIDTH		1
+#define SPSR_EL2_V_BIT			INPLACE(SPSR_EL2_V, 1)
+
+#define SPSR_EL2_C_SHIFT		29
+#define SPSR_EL2_C_WIDTH		1
+#define SPSR_EL2_C_BIT			INPLACE(SPSR_EL2_C, 1)
+
+#define SPSR_EL2_Z_SHIFT		30
+#define SPSR_EL2_Z_WIDTH		1
+#define SPSR_EL2_Z_BIT			INPLACE(SPSR_EL2_Z, 1)
+
+#define SPSR_EL2_N_SHIFT		31
+#define SPSR_EL2_N_WIDTH		1
+#define SPSR_EL2_N_BIT			INPLACE(SPSR_EL2_N, 1)
+
+/* VTCR definitions */
+#define VTCR_T0SZ_SHIFT		0
+#define VTCR_T0SZ_WIDTH		6
+
+#define VTCR_SL0_SHIFT		6
+#define VTCR_SL0_WIDTH		2
+
+#define VTCR_SL0_4K_L2		INPLACE(VTCR_SL0, 0)
+#define VTCR_SL0_4K_L1		INPLACE(VTCR_SL0, 1)
+#define VTCR_SL0_4K_L0		INPLACE(VTCR_SL0, 2)
+#define VTCR_SL0_4K_L3		INPLACE(VTCR_SL0, 3)
+
+#define VTCR_IRGN0_SHIFT	8
+#define VTCR_IRGN0_WIDTH	2
+#define VTCR_IRGN0_WBRAWA	INPLACE(VTCR_IRGN0, 1)
+
+#define VTCR_ORGN0_SHIFT	10
+#define VTCR_ORGN0_WIDTH	2
+#define VTCR_ORGN0_WBRAWA	INPLACE(VTCR_ORGN0, 1)
+
+#define VTCR_SH0_SHIFT		12
+#define VTCR_SH0_WIDTH		2
+#define VTCR_SH0_IS		INPLACE(VTCR_SH0, 3)
+
+#define VTCR_TG0_SHIFT		14
+#define VTCR_TG0_WIDTH		2
+#define VTCR_TG0_4K		INPLACE(VTCR_TG0, 0)
+
+#define VTCR_PS_SHIFT		16
+#define VTCR_PS_WIDTH		3
+#define VTCR_PS_40		INPLACE(VTCR_PS, 2)
+
+#define VTCR_VS			(UL(1) << 19)
+#define VTCR_NSA		(UL(1) << 30)
+#define VTCR_RES1		(UL(1) << 31)
+
+#define VTCR_FLAGS ( \
+	VTCR_IRGN0_WBRAWA | /* PTW inner cache attr. is WB RAWA*/ \
+	VTCR_ORGN0_WBRAWA | /* PTW outer cache attr. is WB RAWA*/ \
+	VTCR_SH0_IS       | /* PTW shareability attr. is Outer Sharable*/\
+	VTCR_TG0_4K       | /* 4K granule size in non-secure PT*/ \
+	VTCR_PS_40        | /* size(PA) = 40 */   \
+	/* VS = 0              size(VMID) = 8 */ \
+	/* NSW = 0             non-secure s2 is made of secure pages*/ \
+	VTCR_NSA           | /* non-secure IPA maps to non-secure PA */ \
+	VTCR_RES1 \
+	)
+
+
+/* SCTLR definitions */
+#define SCTLR_EL1_EE		(UL(1) << 25)
+#define SCTLR_EL1_SPAN		(UL(1) << 23)
+#define SCTLR_EL1_EIS		(UL(1) << 22)
+#define SCTLR_EL1_nTWE		(UL(1) << 18)
+#define SCTLR_EL1_nTWI		(UL(1) << 16)
+#define SCTLR_EL1_EOS		(UL(1) << 11)
+#define SCTLR_EL1_nAA		(UL(1) << 6)
+#define SCTLR_EL1_CP15BEN	(UL(1) << 5)
+#define SCTLR_EL1_SA0		(UL(1) << 4)
+#define SCTLR_EL1_SA		(UL(1) << 3)
+
+#define SCTLR_EL1_FLAGS (SCTLR_EL1_SPAN | SCTLR_EL1_EIS | SCTLR_EL1_nTWE | \
+	SCTLR_EL1_nTWI | SCTLR_EL1_EOS | SCTLR_EL1_nAA | SCTLR_EL1_CP15BEN | \
+	SCTLR_EL1_SA0 | SCTLR_EL1_SA)
+
+/* PMCR_EL0 Definitions */
+#define PMCR_EL0_LC_SHIFT		6
+#define PMCR_EL0_LC_WIDTH		1
+#define PMCR_EL0_LC_BIT			INPLACE(PMCR_EL0_LC, 1)
+
+#define PMCR_EL0_RES1			PMCR_EL0_LC_BIT
+
+
+/* MDSCR_EL1 Definitions */
+#define MDSCR_EL1_TDCC_SHIFT		12
+#define MDSCR_EL1_TDCC_WIDTH		1
+#define MDSCR_EL1_TDCC_BIT		INPLACE(MDSCR_EL1_TDCC, 1)
+
+/* SCTLR register definitions */
+#define SCTLR_EL2_RES1		((UL(1) << 22) /* TODO: ARMv8.5-CSEH, otherwise RES1 */ | \
+				 (1U << 11) /* TODO: ARMv8.5-CSEH, otherwise RES1 */)
+
+#define SCTLR_EL2_M		(UL(1) << 0)
+#define SCTLR_EL2_C		(UL(1) << 2)
+#define SCTLR_EL2_SA		(UL(1) << 3)
+#define SCTLR_EL2_SA0		(UL(1) << 4)
+#define SCTLR_EL2_SED		(UL(1) << 8)
+/* TODO: ARMv8.5-CSEH, otherwise RES1 */
+/* #define SCTLR_EL2_EOS	(UL(1) << 11) */
+#define SCTLR_EL2_I		(UL(1) << 12)
+#define SCTLR_EL2_DZE		(UL(1) << 14)
+#define SCTLR_EL2_UCT		(UL(1) << 15)
+#define SCTLR_EL2_NTWI		(UL(1) << 16)
+#define SCTLR_EL2_NTWE		(UL(1) << 18)
+#define SCTLR_EL2_WXN		(UL(1) << 19)
+#define SCTLR_EL2_TSCXT		(UL(1) << 20)
+/* TODO: ARMv8.5-CSEH, otherwise RES1 */
+/* #define SCTLR_EL2_EIS	(UL(1) << 22) */
+#define SCTLR_EL2_SPAN		(UL(1) << 23)
+#define SCTLR_EL2_UCI		(UL(1) << 26)
+#define SCTLR_EL2_NTLSMD	(UL(1) << 28)
+#define SCTLR_EL2_LSMAOE	(UL(1) << 29)
+/* HCR_EL2.E2H == 0b1 and HCR_EL2.TGE == 0b1 */
+#define SECURE_SCTLR_EL2_RES1	((UL(1) << 22) /* TODO: ARMv8.5-CSEH, otherwise RES1 */ | \
+				 (UL(1) << 11) /* TODO: ARMv8.5-CSEH, otherwise RES1 */)
+
+#define SCTLR_EL2_INIT		(/* SCTLR_EL2_M = 0 (MMU disabled) */  \
+				/* SCTLR_EL2_A = 0 (No alignment checks) */  \
+				 SCTLR_EL2_C   /* Data accesses are cacheable
+						* as per translation tables */ | \
+				 SCTLR_EL2_SA  /* SP aligned at EL2 */ | \
+				 SCTLR_EL2_SA0  /* SP Alignment check enable for EL0 */ \
+				/* SCTLR_EL2_CP15BEN = 0 (EL0 using AArch32:
+				 * EL0 execution of the CP15DMB, CP15DSB, and
+				 * CP15ISB instructions is UNDEFINED. */ \
+				/* SCTLR_EL2_NAA = 0 (unaligned MA fault at EL2 and EL0) */ \
+				/* SCTLR_EL2_ITD = 0 (A32 Only) */ | \
+				 SCTLR_EL2_SED /* A32 Only, RES1 for non-A32 systems */ \
+				/* SCTLR_EL2_EOS TODO: ARMv8.5-CSEH, otherwise RES1 */ | \
+				 SCTLR_EL2_I	 /* I$ is ON for EL2 and EL0 */ | \
+				 SCTLR_EL2_DZE   /* Do not trap DC ZVA */ | \
+				 SCTLR_EL2_UCT   /* Allow EL0 access to CTR_EL0 */ | \
+				 SCTLR_EL2_NTWI  /* Don't trap WFI from EL0 to EL2 */ | \
+				 SCTLR_EL2_NTWE  /* Don't trap WFE from EL0 to EL2 */ | \
+				 SCTLR_EL2_WXN   /* W implies XN */ | \
+				 SCTLR_EL2_TSCXT /* Trap EL0 accesss to SCXTNUM_EL0 */ \
+				/* SCTLR_EL2_EIS EL2 exception is context
+				 * synchronizing
+				 * TODO: ARMv8.5-CSEH, otherwise RES1 */ \
+				 /* SCTLR_EL2_SPAN = 0 (Set PSTATE.PAN = 1 on
+				 * exceptions to EL2)) */ | \
+				 SCTLR_EL2_UCI /* Allow cache maintenance
+						* instructions at EL0 */ | \
+				 SCTLR_EL2_NTLSMD /* A32/T32 only */ | \
+				 SCTLR_EL2_LSMAOE /* A32/T32 only */ | \
+				 SECURE_SCTLR_EL2_RES1)
+
+#define SCTLR_EL2_RUNTIME	(SCTLR_EL2_INIT| \
+				 SCTLR_EL2_M   /* MMU enabled */)
+
+/* CPTR_EL2 definitions */
+#define CPTR_EL2_RES1			((UL(1) << 13) | (UL(1) << 12) | (UL(1) << 9) | 0xff)
+#define CPTR_EL2_FPEN			(UL(1) << 20)
+#define CPTR_EL2_TTA			(UL(1) << 28)
+#define CPTR_EL2_TAM			(UL(1) << 30)
+#define CPTR_EL2_FPEN_SHIFT		20
+#define CPTR_EL2_FPEN_MASK		0x3
+#define	CPTR_EL2_FPEN_TRAP_ALL_00	0x0
+#define	CPTR_EL2_FPEN_TRAP_TGE_01	0x1
+#define CPTR_EL2_FPEN_TRAP_ALL_10	0x2
+#define	CPTR_EL2_FPEN_NO_TRAP_11	0x3
+#define CPTR_EL2_ZEN_SHIFT		UL(16)
+#define CPTR_EL2_ZEN_MASK		UL(0x3)
+#define CPTR_EL2_ZEN_TRAP_ALL_00	UL(0x0)
+#define CPTR_EL2_ZEN_NO_TRAP_11		UL(0x3)
+				/* Trap all FPU/SVE accesses */
+#define CPTR_EL2_INIT		((CPTR_EL2_ZEN_TRAP_ALL_00 << \
+				  CPTR_EL2_ZEN_SHIFT) | \
+				 (CPTR_EL2_FPEN_TRAP_ALL_00 << CPTR_EL2_FPEN_SHIFT) | \
+				 CPTR_EL2_TTA  /* trap trace access */ | \
+				 CPTR_EL2_TAM  /* trap AMU access */ | \
+				 CPTR_EL2_RES1)
+
+/* MDCR_EL2 definitions */
+#define MDCR_EL2_HLP		(U(1) << 26)
+#define MDCR_EL2_HCCD		(U(1) << 23)
+#define MDCR_EL2_TTRF		(U(1) << 19)
+#define MDCR_EL2_HPMD		(U(1) << 17)
+#define MDCR_EL2_TPMS		(U(1) << 14)
+#define MDCR_EL2_E2PB(x)	((x) << 12)
+#define MDCR_EL2_E2PB_EL1	U(0x3)
+#define MDCR_EL2_TDRA_BIT	(U(1) << 11)
+#define MDCR_EL2_TDOSA_BIT	(U(1) << 10)
+#define MDCR_EL2_TDA_BIT	(U(1) << 9)
+#define MDCR_EL2_TDE_BIT	(U(1) << 8)
+#define MDCR_EL2_HPME_BIT	(U(1) << 7)
+#define MDCR_EL2_TPM_BIT	(U(1) << 6)
+#define MDCR_EL2_TPMCR_BIT	(U(1) << 5)
+#define MDCR_EL2_INIT		(MDCR_EL2_TPMCR_BIT \
+				| MDCR_EL2_TPM_BIT \
+				| MDCR_EL2_TDA_BIT)
+
+/* MPIDR definitions */
+#define MPIDR_EL1_AFF_MASK	0xFF
+#define MPIDR_EL1_AFF0_SHIFT	0
+#define MPIDR_EL1_AFF1_SHIFT	8
+#define MPIDR_EL1_AFF2_SHIFT	16
+#define MPIDR_EL1_AFF3_SHIFT	32
+#define MPIDR_EL1_MT_MASK	(UL(1) << 24)
+#define MPIDR_EL1_AFFINITY_BITS	8
+
+#define MPIDR_EL1_AFF0		INPLACE(MPIDR_EL1_AFF0, MPIDR_EL1_AFF_MASK)
+#define MPIDR_EL1_AFF1		INPLACE(MPIDR_EL1_AFF1, MPIDR_EL1_AFF_MASK)
+#define MPIDR_EL1_AFF2		INPLACE(MPIDR_EL1_AFF2, MPIDR_EL1_AFF_MASK)
+#define MPIDR_EL1_AFF3		INPLACE(MPIDR_EL1_AFF3, MPIDR_EL1_AFF_MASK)
+
+/*
+ * RmiRecMpidr type definitions.
+ *
+ * 'MPIDR_EL2_AFF<n>_VAL_SHIFT' constants specify the right shift
+ * for affinity field <n> that gives the field's actual value.
+ *
+ * Aff0[3:0] - Affinity level 0
+ * For compatibility with GICv3 only Aff0[3:0] field is used,
+ * and Aff0[7:4] of a REC MPIDR value is RES0.
+ */
+#define MPIDR_EL2_AFF0_SHIFT			0
+#define MPIDR_EL2_AFF0_WIDTH			4
+#define MPIDR_EL2_AFF0_VAL_SHIFT		0
+
+/* Aff1[15:8] - Affinity level 1 */
+#define MPIDR_EL2_AFF1_SHIFT			8
+#define MPIDR_EL2_AFF1_WIDTH			8
+#define MPIDR_EL2_AFF1_VAL_SHIFT		4
+
+/* Aff2[23:16] - Affinity level 2 */
+#define MPIDR_EL2_AFF2_SHIFT			16
+#define MPIDR_EL2_AFF2_WIDTH			8
+#define MPIDR_EL2_AFF2_VAL_SHIFT		4
+
+/* Aff3[39:32] - Affinity level 3 */
+#define MPIDR_EL2_AFF3_SHIFT			32
+#define MPIDR_EL2_AFF3_WIDTH			8
+#define MPIDR_EL2_AFF3_VAL_SHIFT		12
+
+/*
+ * Extract the value of Aff<n> register field shifted right
+ * so it can be evaluated directly.
+ */
+#define	MPIDR_EL2_AFF(n, reg)	\
+	(((reg) & MASK(MPIDR_EL2_AFF##n)) >> MPIDR_EL2_AFF##n##_VAL_SHIFT)
+
+/* VMPIDR_EL2 bit [31] = RES1 */
+#define VMPIDR_EL2_RES1				(UL(1) << 31)
+
+/* ICC_SRE_EL2 defintions */
+#define ICC_SRE_EL2_ENABLE	(UL(1) << 3)	/* Enable lower EL access to ICC_SRE_EL1 */
+#define ICC_SRE_EL2_DIB		(UL(1) << 2)	/* Disable IRQ bypass   */
+#define ICC_SRE_EL2_DFB		(UL(1) << 1)	/* Disable FIQ bypass   */
+#define ICC_SRE_EL2_SRE		(UL(1) << 0)	/* Enable sysreg access */
+
+#define	ICC_SRE_EL2_INIT	(ICC_SRE_EL2_ENABLE | ICC_SRE_EL2_DIB | \
+				 ICC_SRE_EL2_DFB | ICC_SRE_EL2_SRE)
+
+/* MPAM definitions */
+#define MPAM2_EL2_INIT		0x0
+#define MPAMHCR_EL2_INIT	0x0
+
+#define PMSCR_EL2_INIT		0x0
+
+#define SYSREG_ESR(op0, op1, crn, crm, op2) \
+		(((op0) << ESR_EL2_SYSREG_TRAP_OP0_SHIFT) | \
+		 ((op1) << ESR_EL2_SYSREG_TRAP_OP1_SHIFT) | \
+		 ((crn) << ESR_EL2_SYSREG_TRAP_CRN_SHIFT) | \
+		 ((crm) << ESR_EL2_SYSREG_TRAP_CRM_SHIFT) | \
+		 ((op2) << ESR_EL2_SYSREG_TRAP_OP2_SHIFT))
+
+#define ESR_EL2_SYSREG_MASK		SYSREG_ESR(3, 7, 15, 15, 7)
+
+#define ESR_EL2_SYSREG_ID_MASK		SYSREG_ESR(3, 7, 15, 0, 0)
+#define ESR_EL2_SYSREG_ID		SYSREG_ESR(3, 0, 0, 0, 0)
+
+#define ESR_EL2_SYSREG_ID_AA64PFR0_EL1	SYSREG_ESR(3, 0, 0, 4, 0)
+#define ESR_EL2_SYSREG_ID_AA64PFR1_EL1	SYSREG_ESR(3, 0, 0, 4, 1)
+#define ESR_EL2_SYSREG_ID_AA64ZFR0_EL1	SYSREG_ESR(3, 0, 0, 4, 4)
+
+#define ESR_EL2_SYSREG_ID_AA64DFR0_EL1	SYSREG_ESR(3, 0, 0, 5, 0)
+#define ESR_EL2_SYSREG_ID_AA64DFR1_EL1	SYSREG_ESR(3, 0, 0, 5, 1)
+
+#define ESR_EL2_SYSREG_ID_AA64AFR0_EL1	SYSREG_ESR(3, 0, 0, 5, 4)
+#define ESR_EL2_SYSREG_ID_AA64AFR1_EL1	SYSREG_ESR(3, 0, 0, 5, 5)
+
+#define ESR_EL2_SYSREG_ID_AA64ISAR0_EL1	SYSREG_ESR(3, 0, 0, 6, 0)
+#define ESR_EL2_SYSREG_ID_AA64ISAR1_EL1	SYSREG_ESR(3, 0, 0, 6, 1)
+
+#define ESR_EL2_SYSREG_ID_AA64MMFR0_EL1	SYSREG_ESR(3, 0, 0, 7, 0)
+#define ESR_EL2_SYSREG_ID_AA64MMFR1_EL1	SYSREG_ESR(3, 0, 0, 7, 1)
+#define ESR_EL2_SYSREG_ID_AA64MMFR2_EL1	SYSREG_ESR(3, 0, 0, 7, 2)
+
+#define ESR_EL2_SYSREG_ID_AA64ISAR1_GPI_SHIFT	28
+#define ESR_EL2_SYSREG_ID_AA64ISAR1_GPA_SHIFT	24
+#define ESR_EL2_SYSREG_ID_AA64ISAR1_API_SHIFT	8
+#define ESR_EL2_SYSREG_ID_AA64ISAR1_APA_SHIFT	4
+
+#define ESR_EL2_SYSREG_TIMERS_MASK		SYSREG_ESR(3, 3, 15, 12, 0)
+#define ESR_EL2_SYSREG_TIMERS			SYSREG_ESR(3, 3, 14, 0, 0)
+
+#define ESR_EL2_SYSREG_TIMER_CNTP_TVAL_EL0	SYSREG_ESR(3, 3, 14, 2, 0)
+#define ESR_EL2_SYSREG_TIMER_CNTP_CTL_EL0	SYSREG_ESR(3, 3, 14, 2, 1)
+#define ESR_EL2_SYSREG_TIMER_CNTP_CVAL_EL0	SYSREG_ESR(3, 3, 14, 2, 2)
+#define ESR_EL2_SYSREG_TIMER_CNTV_TVAL_EL0	SYSREG_ESR(3, 3, 14, 3, 0)
+#define ESR_EL2_SYSREG_TIMER_CNTV_CTL_EL0	SYSREG_ESR(3, 3, 14, 3, 1)
+#define ESR_EL2_SYSREG_TIMER_CNTV_CVAL_EL0	SYSREG_ESR(3, 3, 14, 3, 2)
+
+#define ESR_EL2_SYSREG_ICC_PMR_EL1		SYSREG_ESR(3, 0, 4, 6, 0)
+
+/*
+ * GIC system registers encoding mask for registers from
+ * ICC_IAR0_EL1(3, 0, 12, 8, 0) to ICC_IGRPEN1_EL1(3, 0, 12, 12, 7).
+ */
+#define ESR_EL2_SYSREG_ICC_EL1_MASK		SYSREG_ESR(3, 3, 15, 8, 0)
+#define ESR_EL2_SYSREG_ICC_EL1			SYSREG_ESR(3, 0, 12, 8, 0)
+
+#define ESR_EL2_SYSREG_ICC_DIR			SYSREG_ESR(3, 0, 12, 11, 1)
+#define ESR_EL2_SYSREG_ICC_SGI1R_EL1		SYSREG_ESR(3, 0, 12, 11, 5)
+#define ESR_EL2_SYSREG_ICC_SGI0R_EL1		SYSREG_ESR(3, 0, 12, 11, 7)
+
+#define ESR_EL2_SYSREG_DIRECTION	(UL(1) << 0)
+#define ESR_EL2_SYSREG_IS_WRITE(esr)	(!((esr) & ESR_EL2_SYSREG_DIRECTION))
+
+#define ESR_IL(esr)	((esr) & ESR_EL2_IL_MASK)
+#define ESR_ISS(esr)	((esr) & ESR_EL2_ISS_MASK)
+
+#define ESR_EL2_SYSREG_ISS_RT(esr) \
+	((ESR_ISS(esr) & ESR_EL2_SYSREG_TRAP_RT_MASK) >> ESR_EL2_SYSREG_TRAP_RT_SHIFT)
+
+#define ICC_HPPIR1_EL1_INTID_SHIFT	0
+#define ICC_HPPIR1_EL1_INTID_WIDTH	24
+#define ICC_HPPIR1_EL1_INTID_MASK	MASK(ICC_HPPIR1_EL1_INTID)
+
+#define CNTHCTL_EL2_EL0PCTEN	(UL(1) << UL(0))
+#define CNTHCTL_EL2_EL0VCTEN	(UL(1) << UL(1))
+#define CNTHCTL_EL2_EL1PCTEN	(UL(1) << 10)
+#define CNTHCTL_EL2_EL1PTEN	(UL(1) << 11)
+#define CNTHCTL_EL2_EL1TVT	(UL(1) << 13)
+#define CNTHCTL_EL2_EL1TVCT	(UL(1) << 14)
+#define CNTHCTL_EL2_CNTVMASK	(UL(1) << 18)
+#define CNTHCTL_EL2_CNTPMASK	(UL(1) << 19)
+
+#define CNTHCTL_EL2_INIT	(CNTHCTL_EL2_EL0VCTEN | CNTHCTL_EL2_EL0PCTEN)
+
+#define CNTHCTL_EL2_NO_TRAPS	(CNTHCTL_EL2_EL1PCTEN | \
+				 CNTHCTL_EL2_EL1PTEN)
+
+#define CNTx_CTL_ENABLE		(UL(1) << 0)
+#define CNTx_CTL_IMASK		(UL(1) << 1)
+#define CNTx_CTL_ISTATUS	(UL(1) << 2)
+
+/*******************************************************************************
+ * Definitions of register offsets, fields and macros for CPU system
+ * instructions.
+ ******************************************************************************/
+
+#define TLBI_ADDR_SHIFT		U(12)
+#define TLBI_ADDR_MASK		U(0x0FFFFFFFFFFF)
+#define TLBI_ADDR(x)		(((x) >> TLBI_ADDR_SHIFT) & TLBI_ADDR_MASK)
+
+/* ID_AA64MMFR2_EL1 definitions */
+#define ID_AA64MMFR2_EL1_ST_SHIFT	U(28)
+#define ID_AA64MMFR2_EL1_ST_MASK	ULL(0xf)
+
+#define ID_AA64MMFR2_EL1_CNP_SHIFT	U(0)
+#define ID_AA64MMFR2_EL1_CNP_MASK	ULL(0xf)
+
+/* Custom defined values to indicate the vector offset to exception handlers */
+#define ARM_EXCEPTION_SYNC_LEL		0
+#define ARM_EXCEPTION_IRQ_LEL		1
+#define ARM_EXCEPTION_FIQ_LEL		2
+#define ARM_EXCEPTION_SERROR_LEL	3
+
+#define VBAR_CEL_SP_EL0_OFFSET	0x0
+#define VBAR_CEL_SP_ELx_OFFSET	0x200
+#define VBAR_LEL_AA64_OFFSET	0x400
+#define VBAR_LEL_AA32_OFFSET	0x600
+
+#endif /* ARCH_H */
diff --git a/lib/arch/include/arch_features.h b/lib/arch/include/arch_features.h
new file mode 100644
index 0000000..96aa2e0
--- /dev/null
+++ b/lib/arch/include/arch_features.h
@@ -0,0 +1,69 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef ARCH_FEATURES_H
+#define ARCH_FEATURES_H
+
+#include <arch_helpers.h>
+#include <stdbool.h>
+
+static inline bool is_armv8_4_ttst_present(void)
+{
+	return ((read_id_aa64mmfr2_el1() >> ID_AA64MMFR2_EL1_ST_SHIFT) &
+		ID_AA64MMFR2_EL1_ST_MASK) == 1U;
+}
+
+/*
+ * Check if SVE is enabled
+ * ID_AA64PFR0_EL1.SVE, bits [35:32]:
+ * 0b0000 SVE architectural state and programmers' model are not implemented.
+ * 0b0001 SVE architectural state and programmers' model are implemented.
+ */
+static inline bool is_feat_sve_present(void)
+{
+	return ((read_id_aa64pfr0_el1() >> ID_AA64PFR0_EL1_SVE_SHIFT) &
+		ID_AA64PFR0_EL1_SVE_MASK) != 0UL;
+}
+
+/*
+ * Check if RNDR is available
+ */
+static inline bool is_feat_rng_present(void)
+{
+	return ((read_ID_AA64ISAR0_EL1() >> ID_AA64ISAR0_RNDR_SHIFT) &
+		ID_AA64ISAR0_RNDR_MASK) != 0UL;
+}
+
+/*
+ * Check if FEAT_VMID16 is implemented
+ * ID_AA64MMFR1_EL1.VMIDBits, bits [7:4]:
+ * 0b0000 8 bits.
+ * 0b0010 16 bits.
+ * All other values are reserved.
+ */
+static inline bool is_feat_vmid16_present(void)
+{
+	return (((read_id_aa64mmfr1_el1() >> ID_AA64MMFR1_EL1_VMIDBits_SHIFT) &
+		ID_AA64MMFR1_EL1_VMIDBits_MASK) ==
+		ID_AA64MMFR1_EL1_VMIDBits_16);
+}
+
+/*
+ * Check if FEAT_LPA2 is implemented.
+ * 4KB granule  at stage 2 supports 52-bit input and output addresses:
+ * ID_AA64MMFR0_EL1.TGran4_2 bits [43:40]: 0b0011
+ */
+static inline bool is_feat_lpa2_4k_present(void)
+{
+	u_register_t aa64mmfr0 = read_id_aa64mmfr0_el1();
+
+	return ((((aa64mmfr0 >> ID_AA64MMFR0_EL1_TGRAN4_2_SHIFT) &
+		ID_AA64MMFR0_EL1_TGRAN4_2_MASK) ==
+		ID_AA64MMFR0_EL1_TGRAN4_2_LPA2));
+}
+
+unsigned int arch_feat_get_pa_width(void);
+
+#endif /* ARCH_FEATURES_H */
diff --git a/lib/arch/include/arch_helpers.h b/lib/arch/include/arch_helpers.h
new file mode 100644
index 0000000..34c95b3
--- /dev/null
+++ b/lib/arch/include/arch_helpers.h
@@ -0,0 +1,385 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+/* TODO: This file will need clean up */
+
+#ifndef ARCH_HELPERS_H
+#define ARCH_HELPERS_H
+
+#include <arch.h>
+#include <instr_helpers.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+/* Define read function for system register */
+#define DEFINE_SYSREG_READ_FUNC(_name)			\
+	DEFINE_SYSREG_READ_FUNC_(_name, _name)
+
+/* Define read & write function for system register */
+#define DEFINE_SYSREG_RW_FUNCS(_name)			\
+	DEFINE_SYSREG_READ_FUNC_(_name, _name)		\
+	DEFINE_SYSREG_WRITE_FUNC_(_name, _name)
+
+/* Define read & write function for renamed system register */
+#define DEFINE_RENAME_SYSREG_RW_FUNCS(_name, _reg_name)	\
+	DEFINE_SYSREG_READ_FUNC_(_name, _reg_name)	\
+	DEFINE_SYSREG_WRITE_FUNC_(_name, _reg_name)
+
+/* Define read function for renamed system register */
+#define DEFINE_RENAME_SYSREG_READ_FUNC(_name, _reg_name)	\
+	DEFINE_SYSREG_READ_FUNC_(_name, _reg_name)
+
+/* Define write function for renamed system register */
+#define DEFINE_RENAME_SYSREG_WRITE_FUNC(_name, _reg_name)	\
+	DEFINE_SYSREG_WRITE_FUNC_(_name, _reg_name)
+
+/*******************************************************************************
+ * TLB maintenance accessor prototypes
+ ******************************************************************************/
+DEFINE_SYSOP_TYPE_FUNC(tlbi, alle1)
+DEFINE_SYSOP_TYPE_FUNC(tlbi, alle1is)
+DEFINE_SYSOP_TYPE_FUNC(tlbi, alle2)
+DEFINE_SYSOP_TYPE_FUNC(tlbi, alle2is)
+DEFINE_SYSOP_TYPE_FUNC(tlbi, vmalle1)
+DEFINE_SYSOP_TYPE_FUNC(tlbi, vmalle1is)
+DEFINE_SYSOP_TYPE_FUNC(tlbi, vmalls12e1)
+
+DEFINE_SYSOP_TYPE_PARAM_FUNC(tlbi, vaae1is)
+DEFINE_SYSOP_TYPE_PARAM_FUNC(tlbi, vaale1is)
+DEFINE_SYSOP_TYPE_PARAM_FUNC(tlbi, vae2is)
+DEFINE_SYSOP_TYPE_PARAM_FUNC(tlbi, vale2is)
+DEFINE_SYSOP_TYPE_PARAM_FUNC(tlbi, ipas2e1is)
+
+/*******************************************************************************
+ * Cache maintenance accessor prototypes
+ ******************************************************************************/
+DEFINE_SYSOP_TYPE_PARAM_FUNC(dc, isw)
+DEFINE_SYSOP_TYPE_PARAM_FUNC(dc, cisw)
+DEFINE_SYSOP_TYPE_PARAM_FUNC(dc, csw)
+DEFINE_SYSOP_TYPE_PARAM_FUNC(dc, cvac)
+DEFINE_SYSOP_TYPE_PARAM_FUNC(dc, ivac)
+DEFINE_SYSOP_TYPE_PARAM_FUNC(dc, civac)
+DEFINE_SYSOP_TYPE_PARAM_FUNC(dc, cvau)
+DEFINE_SYSOP_TYPE_PARAM_FUNC(dc, zva)
+
+/*******************************************************************************
+ * Address translation accessor prototypes
+ ******************************************************************************/
+DEFINE_SYSOP_TYPE_PARAM_FUNC(at, s12e1r)
+DEFINE_SYSOP_TYPE_PARAM_FUNC(at, s12e1w)
+DEFINE_SYSOP_TYPE_PARAM_FUNC(at, s12e0r)
+DEFINE_SYSOP_TYPE_PARAM_FUNC(at, s12e0w)
+DEFINE_SYSOP_TYPE_PARAM_FUNC(at, s1e1r)
+DEFINE_SYSOP_TYPE_PARAM_FUNC(at, s1e2r)
+
+/*******************************************************************************
+ * Strip Pointer Authentication Code
+ ******************************************************************************/
+DEFINE_SYSOP_PARAM_FUNC(xpaci)
+
+/*******************************************************************************
+ * Cache management
+ ******************************************************************************/
+void flush_dcache_range(uintptr_t addr, size_t size);
+void clean_dcache_range(uintptr_t addr, size_t size);
+void inv_dcache_range(uintptr_t addr, size_t size);
+
+#define is_dcache_enabled() ((read_sctlr_el2() & SCTLR_EL2_C) != 0U)
+
+/*******************************************************************************
+ * MMU management
+ ******************************************************************************/
+#define is_mmu_enabled() ((read_sctlr_el2() & SCTLR_EL2_M) != 0U)
+
+/*******************************************************************************
+ * FPU management
+ ******************************************************************************/
+#define is_fpen_enabled() (((read_cptr_el2() >> CPTR_EL2_FPEN_SHIFT) & \
+		CPTR_EL2_FPEN_MASK) == CPTR_EL2_FPEN_NO_TRAP_11)
+
+/*******************************************************************************
+ * Misc. accessor prototypes
+ ******************************************************************************/
+
+#define write_daifclr(val) SYSREG_WRITE_CONST(daifclr, val)
+#define write_daifset(val) SYSREG_WRITE_CONST(daifset, val)
+
+DEFINE_SYSOP_FUNC(wfi)
+DEFINE_SYSOP_FUNC(wfe)
+DEFINE_SYSOP_FUNC(sev)
+DEFINE_SYSOP_FUNC(isb)
+
+static inline void enable_irq(void)
+{
+	/*
+	 * The compiler memory barrier will prevent the compiler from
+	 * scheduling non-volatile memory access after the write to the
+	 * register.
+	 *
+	 * This could happen if some initialization code issues non-volatile
+	 * accesses to an area used by an interrupt handler, in the assumption
+	 * that it is safe as the interrupts are disabled at the time it does
+	 * that (according to program order). However, non-volatile accesses
+	 * are not necessarily in program order relatively with volatile inline
+	 * assembly statements (and volatile accesses).
+	 */
+	COMPILER_BARRIER();
+	write_daifclr(DAIF_IRQ_BIT);
+	isb();
+}
+
+static inline void enable_fiq(void)
+{
+	COMPILER_BARRIER();
+	write_daifclr(DAIF_FIQ_BIT);
+	isb();
+}
+
+static inline void enable_serror(void)
+{
+	COMPILER_BARRIER();
+	write_daifclr(DAIF_ABT_BIT);
+	isb();
+}
+
+static inline void enable_debug_exceptions(void)
+{
+	COMPILER_BARRIER();
+	write_daifclr(DAIF_DBG_BIT);
+	isb();
+}
+
+static inline void disable_irq(void)
+{
+	COMPILER_BARRIER();
+	write_daifset(DAIF_IRQ_BIT);
+	isb();
+}
+
+static inline void disable_fiq(void)
+{
+	COMPILER_BARRIER();
+	write_daifset(DAIF_FIQ_BIT);
+	isb();
+}
+
+static inline void disable_serror(void)
+{
+	COMPILER_BARRIER();
+	write_daifset(DAIF_ABT_BIT);
+	isb();
+}
+
+static inline void disable_debug_exceptions(void)
+{
+	COMPILER_BARRIER();
+	write_daifset(DAIF_DBG_BIT);
+	isb();
+}
+
+/*******************************************************************************
+ * System register accessor prototypes
+ ******************************************************************************/
+DEFINE_SYSREG_RW_FUNCS(sp_el0)
+DEFINE_SYSREG_RW_FUNCS(sp_el1)
+DEFINE_SYSREG_RW_FUNCS(elr_el12)
+DEFINE_SYSREG_RW_FUNCS(spsr_el12)
+DEFINE_SYSREG_RW_FUNCS(pmuserenr_el0)
+DEFINE_SYSREG_RW_FUNCS(tpidrro_el0)
+DEFINE_SYSREG_RW_FUNCS(tpidr_el0)
+DEFINE_SYSREG_RW_FUNCS(tpidr_el2)
+DEFINE_SYSREG_RW_FUNCS(csselr_el1)
+DEFINE_SYSREG_RW_FUNCS(sctlr_el12)
+DEFINE_SYSREG_RW_FUNCS(cpacr_el12)
+DEFINE_SYSREG_RW_FUNCS(zcr_el1)
+DEFINE_SYSREG_RW_FUNCS(ttbr0_el12)
+DEFINE_SYSREG_RW_FUNCS(ttbr1_el12)
+DEFINE_SYSREG_RW_FUNCS(tcr_el12)
+DEFINE_SYSREG_RW_FUNCS(esr_el12)
+DEFINE_SYSREG_RW_FUNCS(afsr0_el12)
+DEFINE_SYSREG_RW_FUNCS(afsr1_el12)
+DEFINE_SYSREG_RW_FUNCS(far_el12)
+DEFINE_SYSREG_RW_FUNCS(mair_el12)
+DEFINE_SYSREG_RW_FUNCS(vbar_el12)
+DEFINE_SYSREG_RW_FUNCS(contextidr_el12)
+DEFINE_SYSREG_RW_FUNCS(tpidr_el1)
+DEFINE_SYSREG_RW_FUNCS(amair_el12)
+DEFINE_SYSREG_RW_FUNCS(cntkctl_el12)
+DEFINE_SYSREG_RW_FUNCS(mdscr_el1)
+DEFINE_SYSREG_RW_FUNCS(mdccint_el1)
+DEFINE_SYSREG_RW_FUNCS(disr_el1)
+DEFINE_SYSREG_RW_FUNCS(cnrv_ctl_el02)
+DEFINE_SYSREG_RW_FUNCS(vtcr_el2)
+DEFINE_SYSREG_RW_FUNCS(vsesr_el2)
+DEFINE_SYSREG_RW_FUNCS(par_el1)
+DEFINE_SYSREG_READ_FUNC(id_pfr1_el1)
+DEFINE_RENAME_SYSREG_READ_FUNC(ID_AA64PFR0_EL1, id_aa64pfr0_el1)
+DEFINE_RENAME_SYSREG_READ_FUNC(ID_AA64PFR1_EL1, id_aa64pfr1_el1)
+DEFINE_RENAME_SYSREG_READ_FUNC(ID_AA64DFR0_EL1, id_aa64dfr0_el1)
+DEFINE_RENAME_SYSREG_READ_FUNC(ID_AA64DFR1_EL1, id_aa64dfr1_el1)
+DEFINE_RENAME_SYSREG_READ_FUNC(ID_AA64AFR0_EL1, id_aa64afr0_el1)
+DEFINE_RENAME_SYSREG_READ_FUNC(ID_AA64AFR1_EL1, id_aa64afr1_el1)
+DEFINE_RENAME_SYSREG_READ_FUNC(ID_AA64ISAR0_EL1, id_aa64isar0_el1)
+DEFINE_RENAME_SYSREG_READ_FUNC(ID_AA64ISAR1_EL1, id_aa64isar1_el1)
+DEFINE_RENAME_SYSREG_READ_FUNC(ID_AA64MMFR0_EL1, id_aa64mmfr0_el1)
+DEFINE_RENAME_SYSREG_READ_FUNC(ID_AA64MMFR1_EL1, id_aa64mmfr1_el1)
+DEFINE_RENAME_SYSREG_READ_FUNC(ID_AA64MMFR2_EL1, id_aa64mmfr1_el1)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_hppir1_el1, ICC_HPPIR1_EL1)
+DEFINE_RENAME_SYSREG_RW_FUNCS(mpam0_el1, MPAM0_EL1)
+DEFINE_SYSREG_READ_FUNC(id_aa64pfr0_el1)
+DEFINE_SYSREG_READ_FUNC(id_afr0_el1)
+DEFINE_SYSREG_READ_FUNC(CurrentEl)
+DEFINE_SYSREG_READ_FUNC(ctr_el0)
+DEFINE_SYSREG_RW_FUNCS(daif)
+DEFINE_SYSREG_RW_FUNCS(spsr_el1)
+DEFINE_SYSREG_RW_FUNCS(spsr_el2)
+DEFINE_SYSREG_RW_FUNCS(elr_el1)
+DEFINE_SYSREG_RW_FUNCS(elr_el2)
+
+DEFINE_SYSREG_READ_FUNC(midr_el1)
+DEFINE_SYSREG_READ_FUNC(mpidr_el1)
+DEFINE_SYSREG_READ_FUNC(id_aa64mmfr0_el1)
+DEFINE_SYSREG_READ_FUNC(id_aa64mmfr1_el1)
+
+DEFINE_SYSREG_RW_FUNCS(hcr_el2)
+
+DEFINE_SYSREG_RW_FUNCS(vbar_el1)
+DEFINE_SYSREG_RW_FUNCS(vbar_el2)
+
+DEFINE_SYSREG_RW_FUNCS(sctlr_el1)
+DEFINE_SYSREG_RW_FUNCS(sctlr_el2)
+
+DEFINE_SYSREG_RW_FUNCS(actlr_el1)
+DEFINE_SYSREG_RW_FUNCS(actlr_el2)
+
+DEFINE_SYSREG_RW_FUNCS(esr_el1)
+DEFINE_SYSREG_RW_FUNCS(esr_el2)
+
+DEFINE_SYSREG_RW_FUNCS(afsr0_el1)
+DEFINE_SYSREG_RW_FUNCS(afsr0_el2)
+
+DEFINE_SYSREG_RW_FUNCS(afsr1_el1)
+DEFINE_SYSREG_RW_FUNCS(afsr1_el2)
+
+DEFINE_SYSREG_RW_FUNCS(far_el1)
+DEFINE_SYSREG_RW_FUNCS(far_el2)
+DEFINE_SYSREG_RW_FUNCS(hpfar_el2)
+
+DEFINE_SYSREG_RW_FUNCS(mair_el1)
+DEFINE_SYSREG_RW_FUNCS(mair_el2)
+
+DEFINE_SYSREG_RW_FUNCS(amair_el1)
+DEFINE_SYSREG_RW_FUNCS(amair_el2)
+
+DEFINE_SYSREG_READ_FUNC(rvbar_el1)
+DEFINE_SYSREG_READ_FUNC(rvbar_el2)
+
+DEFINE_SYSREG_RW_FUNCS(rmr_el1)
+DEFINE_SYSREG_RW_FUNCS(rmr_el2)
+
+DEFINE_SYSREG_RW_FUNCS(tcr_el1)
+DEFINE_SYSREG_RW_FUNCS(tcr_el2)
+
+DEFINE_SYSREG_RW_FUNCS(ttbr0_el1)
+DEFINE_SYSREG_RW_FUNCS(ttbr0_el2)
+DEFINE_SYSREG_RW_FUNCS(ttbr1_el2)
+
+DEFINE_SYSREG_RW_FUNCS(ttbr1_el1)
+
+DEFINE_SYSREG_RW_FUNCS(vttbr_el2)
+
+DEFINE_SYSREG_RW_FUNCS(cptr_el2)
+
+DEFINE_SYSREG_RW_FUNCS(cpacr_el1)
+
+DEFINE_SYSREG_RW_FUNCS(vpidr_el2)
+DEFINE_SYSREG_RW_FUNCS(vmpidr_el2)
+
+DEFINE_SYSREG_READ_FUNC(isr_el1)
+
+DEFINE_SYSREG_RW_FUNCS(mdcr_el2)
+DEFINE_SYSREG_RW_FUNCS(hstr_el2)
+DEFINE_SYSREG_RW_FUNCS(pmcr_el0)
+DEFINE_SYSREG_RW_FUNCS(mpam2_el2)
+DEFINE_SYSREG_RW_FUNCS(mpamhcr_el2)
+DEFINE_SYSREG_RW_FUNCS(pmscr_el2)
+
+/*******************************************************************************
+ * Timer register accessor prototypes
+ ******************************************************************************/
+DEFINE_SYSREG_RW_FUNCS(cntfrq_el0)
+DEFINE_SYSREG_RW_FUNCS(cnthp_ctl_el2)
+DEFINE_SYSREG_RW_FUNCS(cnthp_tval_el2)
+DEFINE_SYSREG_RW_FUNCS(cnthp_cval_el2)
+DEFINE_SYSREG_RW_FUNCS(cntps_ctl_el1)
+DEFINE_SYSREG_RW_FUNCS(cntps_tval_el1)
+DEFINE_SYSREG_RW_FUNCS(cntps_cval_el1)
+DEFINE_SYSREG_RW_FUNCS(cntp_ctl_el0)
+DEFINE_SYSREG_RW_FUNCS(cntp_tval_el0)
+DEFINE_SYSREG_RW_FUNCS(cntp_cval_el0)
+DEFINE_SYSREG_READ_FUNC(cntpct_el0)
+DEFINE_SYSREG_RW_FUNCS(cnthctl_el2)
+DEFINE_SYSREG_RW_FUNCS(cntp_ctl_el02)
+DEFINE_SYSREG_RW_FUNCS(cntp_cval_el02)
+DEFINE_SYSREG_RW_FUNCS(cntv_ctl_el02)
+DEFINE_SYSREG_RW_FUNCS(cntv_cval_el02)
+DEFINE_SYSREG_RW_FUNCS(cntvoff_el2)
+DEFINE_RENAME_SYSREG_RW_FUNCS(cntpoff_el2, CNTPOFF_EL2)
+
+/*******************************************************************************
+ * Interrupt Controller register accessor prototypes
+ ******************************************************************************/
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_sre_el2, ICC_SRE_EL2)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_ctrl_el1, ICC_CTLR_EL1)
+DEFINE_RENAME_SYSREG_READ_FUNC(icc_hppir1_el2, ICC_HPPIR1_EL1)
+
+/*******************************************************************************
+ * Virtual GIC register accessor prototypes
+ ******************************************************************************/
+DEFINE_RENAME_SYSREG_RW_FUNCS(ich_ap0r0_el2, ICH_AP0R0_EL2)
+DEFINE_RENAME_SYSREG_RW_FUNCS(ich_ap0r1_el2, ICH_AP0R1_EL2)
+DEFINE_RENAME_SYSREG_RW_FUNCS(ich_ap0r2_el2, ICH_AP0R2_EL2)
+DEFINE_RENAME_SYSREG_RW_FUNCS(ich_ap0r3_el2, ICH_AP0R3_EL2)
+DEFINE_RENAME_SYSREG_RW_FUNCS(ich_ap1r0_el2, ICH_AP1R0_EL2)
+DEFINE_RENAME_SYSREG_RW_FUNCS(ich_ap1r1_el2, ICH_AP1R1_EL2)
+DEFINE_RENAME_SYSREG_RW_FUNCS(ich_ap1r2_el2, ICH_AP1R2_EL2)
+DEFINE_RENAME_SYSREG_RW_FUNCS(ich_ap1r3_el2, ICH_AP1R3_EL2)
+
+DEFINE_RENAME_SYSREG_RW_FUNCS(ich_lr0_el2, ICH_LR0_EL2)
+DEFINE_RENAME_SYSREG_RW_FUNCS(ich_lr1_el2, ICH_LR1_EL2)
+DEFINE_RENAME_SYSREG_RW_FUNCS(ich_lr2_el2, ICH_LR2_EL2)
+DEFINE_RENAME_SYSREG_RW_FUNCS(ich_lr3_el2, ICH_LR3_EL2)
+DEFINE_RENAME_SYSREG_RW_FUNCS(ich_lr4_el2, ICH_LR4_EL2)
+DEFINE_RENAME_SYSREG_RW_FUNCS(ich_lr5_el2, ICH_LR5_EL2)
+DEFINE_RENAME_SYSREG_RW_FUNCS(ich_lr6_el2, ICH_LR6_EL2)
+DEFINE_RENAME_SYSREG_RW_FUNCS(ich_lr7_el2, ICH_LR7_EL2)
+DEFINE_RENAME_SYSREG_RW_FUNCS(ich_lr8_el2, ICH_LR8_EL2)
+DEFINE_RENAME_SYSREG_RW_FUNCS(ich_lr9_el2, ICH_LR9_EL2)
+DEFINE_RENAME_SYSREG_RW_FUNCS(ich_lr10_el2, ICH_LR10_EL2)
+DEFINE_RENAME_SYSREG_RW_FUNCS(ich_lr11_el2, ICH_LR11_EL2)
+DEFINE_RENAME_SYSREG_RW_FUNCS(ich_lr12_el2, ICH_LR12_EL2)
+DEFINE_RENAME_SYSREG_RW_FUNCS(ich_lr13_el2, ICH_LR13_EL2)
+DEFINE_RENAME_SYSREG_RW_FUNCS(ich_lr14_el2, ICH_LR14_EL2)
+DEFINE_RENAME_SYSREG_RW_FUNCS(ich_lr15_el2, ICH_LR15_EL2)
+
+DEFINE_RENAME_SYSREG_RW_FUNCS(ich_hcr_el2, ICH_HCR_EL2)
+DEFINE_RENAME_SYSREG_RW_FUNCS(ich_vmcr_el2, ICH_VMCR_EL2)
+DEFINE_RENAME_SYSREG_READ_FUNC(ich_vtr_el2, ICH_VTR_EL2)
+DEFINE_RENAME_SYSREG_READ_FUNC(ich_misr_el2, ICH_MISR_EL2)
+
+/* Armv8.2 Registers */
+DEFINE_RENAME_SYSREG_READ_FUNC(id_aa64mmfr2_el1, ID_AA64MMFR2_EL1)
+
+/* Armv8.3 Pointer Authentication Registers */
+DEFINE_RENAME_SYSREG_RW_FUNCS(apiakeyhi_el1, APIAKeyHi_EL1)
+DEFINE_RENAME_SYSREG_RW_FUNCS(apiakeylo_el1, APIAKeyLo_EL1)
+
+/* Armv8.5 MTE Registers */
+DEFINE_RENAME_SYSREG_RW_FUNCS(tfsre0_el1, TFSRE0_EL1)
+DEFINE_RENAME_SYSREG_RW_FUNCS(tfsr_el1, TFSR_EL1)
+DEFINE_RENAME_SYSREG_RW_FUNCS(rgsr_el1, RGSR_EL1)
+DEFINE_RENAME_SYSREG_RW_FUNCS(gcr_el1, GCR_EL1)
+
+#endif /* ARCH_HELPERS_H */
diff --git a/lib/arch/include/esr.h b/lib/arch/include/esr.h
new file mode 100644
index 0000000..a88b71c
--- /dev/null
+++ b/lib/arch/include/esr.h
@@ -0,0 +1,78 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef ESR_H
+#define ESR_H
+
+#include <arch.h>
+#include <assert.h>
+#include <stdbool.h>
+#include <utils_def.h>
+
+static inline unsigned int esr_srt(unsigned long esr)
+{
+	return EXTRACT(ESR_EL2_ABORT_SRT, esr);
+}
+
+static inline bool esr_is_write(unsigned long esr)
+{
+	return ((esr & ESR_EL2_ABORT_WNR_BIT) != 0UL);
+}
+
+static inline bool esr_sign_extend(unsigned long esr)
+{
+	return ((esr & ESR_EL2_ABORT_SSE_BIT) != 0UL);
+}
+
+static inline bool esr_sixty_four(unsigned long esr)
+{
+	return ((esr & ESR_EL2_ABORT_SF_BIT) != 0UL);
+}
+
+static inline unsigned int esr_sas(unsigned long esr)
+{
+	return EXTRACT(ESR_EL2_ABORT_SAS, esr);
+}
+
+static inline unsigned int access_len(unsigned long esr)
+{
+	switch (esr_sas(esr)) {
+	case ESR_EL2_ABORT_SAS_BYTE_VAL:
+		return 1U;
+	case ESR_EL2_ABORT_SAS_HWORD_VAL:
+		return 2U;
+	case ESR_EL2_ABORT_SAS_WORD_VAL:
+		return 4U;
+	default:
+		assert(esr_sas(esr) == ESR_EL2_ABORT_SAS_DWORD_VAL);
+		return 8U;
+	}
+}
+
+static inline unsigned long access_mask(unsigned long esr)
+{
+	switch (esr_sas(esr)) {
+	case ESR_EL2_ABORT_SAS_BYTE_VAL:
+		return 0xffUL;
+	case ESR_EL2_ABORT_SAS_HWORD_VAL:
+		return 0xffffUL;
+	case ESR_EL2_ABORT_SAS_WORD_VAL:
+		return 0xffffffffUL;
+	default:
+		assert(esr_sas(esr) == ESR_EL2_ABORT_SAS_DWORD_VAL);
+		return ~(0UL);
+	}
+}
+
+/*
+ * For a trapped msr/mrs sysreg access, get the transfer register in the
+ * ESR_EL2.
+ */
+static inline unsigned int esr_sysreg_rt(unsigned long esr)
+{
+	return EXTRACT(ESR_EL2_SYSREG_TRAP_RT, esr);
+}
+
+#endif /* ESR_H */
diff --git a/lib/arch/include/fake_host/atomics.h b/lib/arch/include/fake_host/atomics.h
new file mode 100644
index 0000000..bda6784
--- /dev/null
+++ b/lib/arch/include/fake_host/atomics.h
@@ -0,0 +1,78 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef ATOMICS_H
+#define ATOMICS_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+/*
+ * Atomically adds @val to the 64-bit value stored at memory location @loc.
+ */
+static inline void atomic_add_64(uint64_t *loc, long val)
+{
+	*loc = *loc + val;
+}
+
+/*
+ * Atomically adds @val to the 64-bit value stored at memory location @loc.
+ * Stores to memory with release semantics.
+ * Returns the old value.
+ */
+static inline unsigned long atomic_load_add_release_64(uint64_t *loc, long val)
+{
+	unsigned long old_val = *loc;
+
+	*loc = *loc + val;
+	return old_val;
+}
+
+/*
+ * Atomically set bit @bit in value pointed to by @val with release semantics.
+ */
+static inline void atomic_bit_set_release_64(uint64_t *loc, int bit)
+{
+	uint64_t mask = (1UL << bit);
+
+	*loc = *loc | mask;
+}
+
+/*
+ * Atomically clear bit @bit in value pointed to by @loc with release semantics.
+ */
+static inline void atomic_bit_clear_release_64(uint64_t *loc, int bit)
+{
+	uint64_t mask = ~((uint64_t)(1UL << bit));
+
+	*loc = *loc & mask;
+}
+
+/*
+ * Test bit @bit in value pointed to by @loc with acquire semantics.
+ */
+static inline bool atomic_test_bit_acquire_64(uint64_t *loc, int bit)
+{
+	uint64_t val = *loc;
+	uint64_t mask = (1UL << bit);
+
+	return ((val & mask) != 0UL);
+}
+
+/*
+ * Atomically set bit @bit in value pointed to by @val
+ * with acquire and release semantics.
+ * Return True if the previous state of @bit was 1, False otherwise.
+ */
+static inline bool atomic_bit_set_acquire_release_64(uint64_t *loc, int bit)
+{
+	uint64_t mask = (1UL << bit);
+	unsigned long old_val = *loc & mask;
+
+	*loc |= mask;
+	return (old_val != 0UL);
+}
+
+#endif /* ATOMICS_H */
diff --git a/lib/arch/include/fake_host/cpuid.h b/lib/arch/include/fake_host/cpuid.h
new file mode 100644
index 0000000..c4c6a6e
--- /dev/null
+++ b/lib/arch/include/fake_host/cpuid.h
@@ -0,0 +1,14 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef CPUID_H
+#define CPUID_H
+
+static inline unsigned int my_cpuid(void)
+{
+	return 0UL;
+}
+
+#endif /* CPUID_H */
diff --git a/lib/arch/include/fake_host/entropy.h b/lib/arch/include/fake_host/entropy.h
new file mode 100644
index 0000000..ff70dcf
--- /dev/null
+++ b/lib/arch/include/fake_host/entropy.h
@@ -0,0 +1,20 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef ENTROPY_H
+#define ENTROPY_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+static inline bool arch_collect_entropy(uint64_t *random)
+{
+	static uint64_t val;
+
+	*random = val++;
+	return true;
+}
+
+#endif /* ENTROPY_H */
diff --git a/lib/arch/include/fake_host/instr_helpers.h b/lib/arch/include/fake_host/instr_helpers.h
new file mode 100644
index 0000000..5113cc6
--- /dev/null
+++ b/lib/arch/include/fake_host/instr_helpers.h
@@ -0,0 +1,65 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef INSTR_HELPERS_H
+#define INSTR_HELPERS_H
+
+#include <host_harness.h>
+#include <stddef.h>
+
+/**********************************************************************
+ * Macros which create inline functions to read or write CPU system
+ * registers
+ *********************************************************************/
+#define DEFINE_SYSREG_READ_FUNC_(_name, _reg_name)		\
+static inline u_register_t read_ ## _name(void)			\
+{								\
+	return host_read_sysreg(#_name);					\
+}
+
+#define DEFINE_SYSREG_WRITE_FUNC_(_name, _reg_name)		\
+static inline void write_ ## _name(u_register_t v)		\
+{								\
+	host_write_sysreg(#_name, v);				\
+}
+
+#define SYSREG_WRITE_CONST(reg_name, v)				\
+		host_write_sysreg(#reg_name, v)
+
+/**********************************************************************
+ * Macros to create inline functions for system instructions
+ *********************************************************************/
+
+/* Define function for simple system instruction */
+#define DEFINE_SYSOP_FUNC(_op)				\
+static inline void (_op)(void)				\
+{							\
+	(void)_op;					\
+}
+
+/* Define function for system instruction with register parameter */
+#define DEFINE_SYSOP_PARAM_FUNC(_op)			\
+static inline void (_op)(uint64_t v)			\
+{							\
+	(void)v; /* To avoid MISRA-C:2012-2.7 warnings */ \
+}
+
+/* Define function for system instruction with type specifier */
+#define DEFINE_SYSOP_TYPE_FUNC(_op, _type)		\
+static inline void (_op ## _type)(void)			\
+{							\
+}
+
+/* Define function for system instruction with register parameter */
+#define DEFINE_SYSOP_TYPE_PARAM_FUNC(_op, _type)	\
+static inline void (_op ## _type)(uint64_t v)		\
+{							\
+	(void)v; /* To avoid MISRA-C:2012-2.7 warnings */ \
+}
+
+#define dsb(scope)
+#define dmb(scope)
+
+#endif /* INSTR_HELPERS_H */
diff --git a/lib/arch/include/fake_host/memory.h b/lib/arch/include/fake_host/memory.h
new file mode 100644
index 0000000..3f10f44
--- /dev/null
+++ b/lib/arch/include/fake_host/memory.h
@@ -0,0 +1,40 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef MEMORY_H
+#define MEMORY_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+/* Single-Copy Atomic 64-bit write */
+static inline void __sca_write64(uint64_t *ptr, uint64_t val)
+{
+	*ptr = val;
+}
+#define SCA_WRITE64(_p, _v) __sca_write64((void *)(_p), ((uint64_t)(_v)))
+
+/* Single-Copy Atomic 64-bit write with RELEASE memory ordering semantics*/
+static inline void __sca_write64_release(uint64_t *ptr, uint64_t val)
+{
+	*ptr = val;
+}
+#define SCA_WRITE64_RELEASE(_p, _v) __sca_write64_release((void *)(_p), ((uint64_t)(_v)))
+
+/* Single-Copy Atomic 64-bit read */
+static inline uint64_t __sca_read64(uint64_t *ptr)
+{
+	return *ptr;
+}
+#define SCA_READ64(_p) ((typeof(*(_p)))__sca_read64((void *)(_p)))
+
+/* Single-Copy Atomic 64-bit read with ACQUIRE memory ordering semantics */
+static inline uint64_t __sca_read64_acquire(uint64_t *ptr)
+{
+	return *ptr;
+}
+#define SCA_READ64_ACQUIRE(_p) ((typeof(*(_p)))__sca_read64_acquire((void *)(_p)))
+
+#endif /* MEMORY_H */
diff --git a/lib/arch/include/fake_host/mmio.h b/lib/arch/include/fake_host/mmio.h
new file mode 100644
index 0000000..293f803
--- /dev/null
+++ b/lib/arch/include/fake_host/mmio.h
@@ -0,0 +1,51 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef MMIO_H
+#define MMIO_H
+
+#include <stdint.h>
+
+static inline uint8_t read8(volatile void *addr)
+{
+	return *(uint8_t *)addr;
+}
+
+static inline void write8(uint8_t val, volatile void *addr)
+{
+	*(uint8_t *)addr = val;
+}
+
+static inline uint16_t read16(volatile void *addr)
+{
+	return *(uint16_t *)addr;
+}
+
+static inline void write16(uint16_t val, volatile void *addr)
+{
+	*(uint16_t *)addr = val;
+}
+
+static inline uint32_t read32(volatile void *addr)
+{
+	return *(uint32_t *)addr;
+}
+
+static inline void write32(uint32_t val, volatile void *addr)
+{
+	*(uint32_t *)addr = val;
+}
+
+static inline uint64_t read64(volatile void *addr)
+{
+	return *(uint64_t *)addr;
+}
+
+static inline void write64(uint64_t val, volatile void *addr)
+{
+	*(uint64_t *)addr = val;
+}
+
+#endif /* MMIO_H */
diff --git a/lib/arch/include/fake_host/spinlock.h b/lib/arch/include/fake_host/spinlock.h
new file mode 100644
index 0000000..7aa4b29
--- /dev/null
+++ b/lib/arch/include/fake_host/spinlock.h
@@ -0,0 +1,25 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef SPINLOCK_H
+#define SPINLOCK_H
+
+#include <host_harness.h>
+
+typedef struct spinlock_s {
+	unsigned int val;
+} spinlock_t;
+
+static inline void spinlock_acquire(spinlock_t *l)
+{
+	host_spinlock_acquire(l);
+}
+
+static inline void spinlock_release(spinlock_t *l)
+{
+	host_spinlock_release(l);
+}
+
+#endif /* SPINLOCK_H */
diff --git a/lib/arch/include/fpu_helpers.h b/lib/arch/include/fpu_helpers.h
new file mode 100644
index 0000000..2581a3b
--- /dev/null
+++ b/lib/arch/include/fpu_helpers.h
@@ -0,0 +1,93 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef FPU_HELPERS_H
+#define FPU_HELPERS_H
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <cpuid.h>
+#include <stdbool.h>
+
+/* The FPU and SIMD register bank is 32 quadword (128 bits) Q registers. */
+#define FPU_Q_SIZE		16U
+#define FPU_Q_COUNT		32U
+
+/* These defines are needed by assembly code to access the context. */
+#define FPU_CTX_OFFSET_Q	0U
+#define FPU_CTX_OFFSET_FPSR	512U
+#define FPU_CTX_OFFSET_FPCR	520U
+
+#ifdef RMM_FPU_USE_AT_REL2
+#define FPU_ALLOW(expression) \
+	do { \
+		assert(fpu_is_my_state_saved(my_cpuid())); \
+		write_cptr_el2( \
+			(read_cptr_el2() & \
+			(~(CPTR_EL2_FPEN_MASK << CPTR_EL2_FPEN_SHIFT))) | \
+			(CPTR_EL2_FPEN_NO_TRAP_11 << CPTR_EL2_FPEN_SHIFT)); \
+		isb(); \
+		expression; \
+		write_cptr_el2( \
+			(read_cptr_el2() & \
+			(~(CPTR_EL2_FPEN_MASK << CPTR_EL2_FPEN_SHIFT))) | \
+			(CPTR_EL2_FPEN_TRAP_ALL_00 << CPTR_EL2_FPEN_SHIFT)); \
+		isb(); \
+	} while (0)
+
+#define IS_FPU_ALLOWED() \
+	(fpu_is_my_state_saved(my_cpuid()) && is_fpen_enabled())
+
+#else /* RMM_FPU_USE_AT_REL2 */
+#define FPU_ALLOW(expression) \
+	do { \
+		expression; \
+	} while (0)
+
+#define IS_FPU_ALLOWED() (true)
+
+#endif /* RMM_FPU_USE_AT_REL2 */
+
+struct fpu_state {
+	unsigned __int128 q[FPU_Q_COUNT];
+	unsigned long fpsr;
+	unsigned long fpcr;
+};
+
+/* Since we use these offsets in assembly code make sure they are correct. */
+COMPILER_ASSERT(__builtin_offsetof(struct fpu_state, q) ==
+	FPU_CTX_OFFSET_Q);
+COMPILER_ASSERT(__builtin_offsetof(struct fpu_state, fpsr) ==
+	FPU_CTX_OFFSET_FPSR);
+COMPILER_ASSERT(__builtin_offsetof(struct fpu_state, fpcr) ==
+	FPU_CTX_OFFSET_FPCR);
+
+/*
+ * Save/restore FPU context to/from the `fpu_state` passed as parameter. The FPU
+ * instruction trap needs to be disabled before calling these functions.
+ * Can be used for context switching.
+ */
+void fpu_save_state(struct fpu_state *fpu);
+void fpu_restore_state(struct fpu_state *fpu);
+
+/*
+ * Save/restore FPU state to/from a per-cpu buffer allocated within the
+ * library. The FPU instruction trap is disabled by this function during the
+ * access to the FPU registers.
+ * These functions are expected to be called before FPU is used by RMM to save
+ * the incoming FPU context.
+ */
+void fpu_save_my_state(void);
+void fpu_restore_my_state(void);
+
+/*
+ * Return true iff an fpu state is saved in the per-cpu buffer in this library.
+ *
+ * After calling 'fpu_save_my_state' this function returns true. After calling
+ * 'fpu_restore_my_state' this function returns false.
+ */
+bool fpu_is_my_state_saved(unsigned int cpu_id);
+
+#endif /* FPU_HELPERS_H */
diff --git a/lib/arch/src/aarch64/cache_helpers.S b/lib/arch/src/aarch64/cache_helpers.S
new file mode 100644
index 0000000..195cf61
--- /dev/null
+++ b/lib/arch/src/aarch64/cache_helpers.S
@@ -0,0 +1,57 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <arch.h>
+#include <asm_macros.S>
+
+	.globl	flush_dcache_range
+	.globl	clean_dcache_range
+	.globl	inv_dcache_range
+
+/*
+ * This macro can be used for implementing various data cache operations `op`
+ */
+.macro do_dcache_maintenance_by_mva op
+	/* Exit early if size is zero */
+	cbz	x1, exit_loop_\op
+	dcache_line_size x2, x3
+	add	x1, x0, x1
+	sub	x3, x2, #1
+	bic	x0, x0, x3
+loop_\op:
+	dc	\op, x0
+	add	x0, x0, x2
+	cmp	x0, x1
+	b.lo	loop_\op
+	dsb	sy
+exit_loop_\op:
+	ret
+.endm
+	/* ------------------------------------------
+	 * Clean+Invalidate from base address till
+	 * size. 'x0' = addr, 'x1' = size
+	 * ------------------------------------------
+	 */
+func flush_dcache_range
+	do_dcache_maintenance_by_mva civac
+endfunc flush_dcache_range
+
+	/* ------------------------------------------
+	 * Clean from base address till size.
+	 * 'x0' = addr, 'x1' = size
+	 * ------------------------------------------
+	 */
+func clean_dcache_range
+	do_dcache_maintenance_by_mva cvac
+endfunc clean_dcache_range
+
+	/* ------------------------------------------
+	 * Invalidate from base address till
+	 * size. 'x0' = addr, 'x1' = size
+	 * ------------------------------------------
+	 */
+func inv_dcache_range
+	do_dcache_maintenance_by_mva ivac
+endfunc inv_dcache_range
diff --git a/lib/arch/src/aarch64/fpu_helpers.S b/lib/arch/src/aarch64/fpu_helpers.S
new file mode 100644
index 0000000..6b7a1ef
--- /dev/null
+++ b/lib/arch/src/aarch64/fpu_helpers.S
@@ -0,0 +1,83 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <asm_macros.S>
+
+.globl fpu_save_state
+.globl fpu_restore_state
+
+/*
+ * void fpu_save_state(struct fpu_state *fpu);
+ *
+ * This function saves the FPU state in systems supporting FPU but NOT SVE.
+ * Systems with SVE support only use the SVE save/restore since these
+ * registers are mapped into SVE registers.
+ *
+ * Since we make assumptions about register fields in C structs here, there are
+ * compiler asserts in rec.h to help guarantee that these assumptions are true.
+ */
+func fpu_save_state
+	/* Save the SIMD/FPU register bank to memory. */
+	stp	q0, q1, [x0], #32
+	stp	q2, q3, [x0], #32
+	stp	q4, q5, [x0], #32
+	stp	q6, q7, [x0], #32
+	stp	q8, q9, [x0], #32
+	stp	q10, q11, [x0], #32
+	stp	q12, q13, [x0], #32
+	stp	q14, q15, [x0], #32
+	stp	q16, q17, [x0], #32
+	stp	q18, q19, [x0], #32
+	stp	q20, q21, [x0], #32
+	stp	q22, q23, [x0], #32
+	stp	q24, q25, [x0], #32
+	stp	q26, q27, [x0], #32
+	stp	q28, q29, [x0], #32
+	stp	q30, q31, [x0], #32
+
+	/* Save the FPSR/FPCR */
+	mrs	x1, fpsr
+	mrs	x2, fpcr
+	stp	x1, x2, [x0]
+
+	ret
+endfunc fpu_save_state
+
+/*
+ * void fpu_restore_state(struct fpu_state *fpu);
+ *
+ * Function to restore a saved FPU register state on systems supporting FPU/SIMD
+ * but NOT SVE.  Systems with SVE support enabled only use the SVE save/restore
+ * since these registers are mapped into SVE registers.
+ *
+ * Since we make assumptions about register fields in C structs here, there are
+ * compiler asserts in rec.h to help guarantee that these assumptions are true.
+ */
+func fpu_restore_state
+	/* Load the SIMD/FPU register bank from memory. */
+	ldp	q0, q1, [x0], #32
+	ldp	q2, q3, [x0], #32
+	ldp	q4, q5, [x0], #32
+	ldp	q6, q7, [x0], #32
+	ldp	q8, q9, [x0], #32
+	ldp	q10, q11, [x0], #32
+	ldp	q12, q13, [x0], #32
+	ldp	q14, q15, [x0], #32
+	ldp	q16, q17, [x0], #32
+	ldp	q18, q19, [x0], #32
+	ldp	q20, q21, [x0], #32
+	ldp	q22, q23, [x0], #32
+	ldp	q24, q25, [x0], #32
+	ldp	q26, q27, [x0], #32
+	ldp	q28, q29, [x0], #32
+	ldp	q30, q31, [x0], #32
+
+	/* Load the FPSR/FPCR */
+	ldp	x1, x2, [x0]
+	msr	fpsr, x1
+	msr	fpcr, x2
+
+	ret
+endfunc fpu_restore_state
diff --git a/lib/arch/src/arch_features.c b/lib/arch/src/arch_features.c
new file mode 100644
index 0000000..67d70b6
--- /dev/null
+++ b/lib/arch/src/arch_features.c
@@ -0,0 +1,38 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <arch.h>
+#include <arch_helpers.h>
+#include <assert.h>
+#include <stdint.h>
+#include <utils_def.h>
+
+/*
+ * Return the PA width supported by the current system.
+ */
+unsigned int arch_feat_get_pa_width(void)
+{
+	/*
+	 * Physical Address ranges supported in the AArch64 Memory Model.
+	 * Value 0b110 is supported in ARMv8.2 onwards but not used in RMM.
+	 */
+	static const unsigned int pa_range_bits_arr[] = {
+		PARANGE_0000_WIDTH, PARANGE_0001_WIDTH, PARANGE_0010_WIDTH,
+		PARANGE_0011_WIDTH, PARANGE_0100_WIDTH, PARANGE_0101_WIDTH,
+		/*
+		 * FEAT_LPA/LPA2 is not supported yet in RMM,
+		 * so max PA width is 48.
+		 */
+		PARANGE_0101_WIDTH
+	};
+
+	register_t pa_range = (read_id_aa64mmfr0_el1() >>
+			       ID_AA64MMFR0_EL1_PARANGE_SHIFT) &
+			       ID_AA64MMFR0_EL1_PARANGE_MASK;
+
+	assert(pa_range < ARRAY_SIZE(pa_range_bits_arr));
+
+	return pa_range_bits_arr[pa_range];
+}
diff --git a/lib/arch/src/fake_host/cache_wrappers.c b/lib/arch/src/fake_host/cache_wrappers.c
new file mode 100644
index 0000000..6de91fa
--- /dev/null
+++ b/lib/arch/src/fake_host/cache_wrappers.c
@@ -0,0 +1,25 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <arch_helpers.h>
+
+/*******************************************************************************
+ * Cache management
+ ******************************************************************************/
+void flush_dcache_range(uintptr_t addr, size_t size)
+{
+	(void)addr;
+	(void)size;
+}
+void clean_dcache_range(uintptr_t addr, size_t size)
+{
+	(void)addr;
+	(void)size;
+}
+void inv_dcache_range(uintptr_t addr, size_t size)
+{
+	(void)addr;
+	(void)size;
+}
diff --git a/lib/arch/src/fake_host/fpu_helpers_host.c b/lib/arch/src/fake_host/fpu_helpers_host.c
new file mode 100644
index 0000000..4922251
--- /dev/null
+++ b/lib/arch/src/fake_host/fpu_helpers_host.c
@@ -0,0 +1,9 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <fpu_helpers.h>
+
+void fpu_save_state(struct fpu_state *fpu) {}
+void fpu_restore_state(struct fpu_state *fpu) {}
diff --git a/lib/arch/src/fpu_helpers.c b/lib/arch/src/fpu_helpers.c
new file mode 100644
index 0000000..70f79d9
--- /dev/null
+++ b/lib/arch/src/fpu_helpers.c
@@ -0,0 +1,51 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <cpuid.h>
+#include <fpu_helpers.h>
+#include <stdbool.h>
+
+struct rmm_fpu_state {
+	struct fpu_state state;
+	bool saved;
+};
+
+struct rmm_fpu_state rmm_fpu_state[MAX_CPUS];
+
+#ifdef RMM_FPU_USE_AT_REL2
+void fpu_save_my_state(void)
+{
+	struct rmm_fpu_state *rmm_state;
+	unsigned int cpu_id = my_cpuid();
+
+	rmm_state = rmm_fpu_state + cpu_id;
+	assert(!rmm_state->saved);
+	rmm_state->saved = true;
+	FPU_ALLOW(fpu_save_state(&(rmm_state->state)));
+}
+
+void fpu_restore_my_state(void)
+{
+	struct rmm_fpu_state *rmm_state;
+	unsigned int cpu_id = my_cpuid();
+
+	rmm_state = rmm_fpu_state + cpu_id;
+	assert(rmm_state->saved);
+	FPU_ALLOW(fpu_restore_state(&(rmm_state->state)));
+	rmm_state->saved = false;
+}
+
+bool fpu_is_my_state_saved(unsigned int cpu_id)
+{
+	assert(cpu_id < MAX_CPUS);
+	return rmm_fpu_state[cpu_id].saved;
+}
+
+#else /* RMM_FPU_USE_AT_REL2 */
+void fpu_save_my_state(void) {}
+
+void fpu_restore_my_state(void) {}
+
+#endif /* RMM_FPU_USE_AT_REL2 */
diff --git a/lib/asc/CMakeLists.txt b/lib/asc/CMakeLists.txt
new file mode 100644
index 0000000..ca50f83
--- /dev/null
+++ b/lib/asc/CMakeLists.txt
@@ -0,0 +1,16 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+add_library(rmm-lib-asc)
+
+target_link_libraries(rmm-lib-asc
+    PRIVATE rmm-lib-common
+            rmm-lib-smc)
+
+target_include_directories(rmm-lib-asc
+    PUBLIC "include")
+
+target_sources(rmm-lib-asc
+    PRIVATE "src/asc_drv.c")
diff --git a/lib/asc/include/asc.h b/lib/asc/include/asc.h
new file mode 100644
index 0000000..5ac790b
--- /dev/null
+++ b/lib/asc/include/asc.h
@@ -0,0 +1,11 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+#ifndef ASC_H
+#define ASC_H
+
+void asc_mark_secure(unsigned long addr);
+void asc_mark_nonsecure(unsigned long addr);
+
+#endif /* ASC_H */
diff --git a/lib/asc/src/asc_drv.c b/lib/asc/src/asc_drv.c
new file mode 100644
index 0000000..32b10ea
--- /dev/null
+++ b/lib/asc/src/asc_drv.c
@@ -0,0 +1,24 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <assert.h>
+#include <smc.h>
+
+void asc_mark_secure(unsigned long addr)
+{
+	__unused int ret;
+
+	ret = monitor_call(SMC_ASC_MARK_SECURE, addr, 0, 0, 0, 0, 0);
+	assert(ret == 0);
+}
+
+void asc_mark_nonsecure(unsigned long addr)
+{
+	__unused int ret;
+
+	ret = monitor_call(SMC_ASC_MARK_NONSECURE, addr, 0, 0, 0, 0, 0);
+	assert(ret == 0);
+}
+
diff --git a/lib/attestation/CMakeLists.txt b/lib/attestation/CMakeLists.txt
new file mode 100644
index 0000000..32ea79e
--- /dev/null
+++ b/lib/attestation/CMakeLists.txt
@@ -0,0 +1,50 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+add_library(rmm-lib-attestation)
+
+arm_config_option(
+    NAME ECP_MAX_OPS
+    HELP "Set the number of max operations per ECC signing iteration (min: 248)"
+    TYPE STRING
+    DEFAULT 1000
+    ADVANCED)
+
+if(ECP_MAX_OPS LESS 248)
+    message(FATAL_ERROR "ECP_MAX_OPS is less than the lowest effective
+                         value for curve and MBEDTLS_ECP_WINDOW_SIZE")
+endif()
+
+target_compile_definitions(rmm-lib-attestation
+    PRIVATE "ECP_MAX_OPS=${ECP_MAX_OPS}U")
+
+target_link_libraries(rmm-lib-attestation
+  PRIVATE
+    rmm-lib-arch
+    rmm-lib-debug
+    rmm-lib-common
+    rmm-lib-libc
+    rmm-lib-rmm_el3_ifc
+    rmm-lib-smc
+    qcbor
+)
+
+target_link_libraries(rmm-lib-attestation
+  PUBLIC
+    rmm-lib-allocator
+    rmm-lib-measurement
+    MbedTLS::Crypto
+    t_cose)
+
+target_include_directories(rmm-lib-attestation
+    PUBLIC "include"
+    PRIVATE "src")
+
+target_sources(rmm-lib-attestation
+    PRIVATE
+        "src/attestation_key.c"
+        "src/attestation_rnd.c"
+        "src/attestation_token.c"
+        "src/attestation_utils.c")
diff --git a/lib/attestation/include/attestation.h b/lib/attestation/include/attestation.h
new file mode 100644
index 0000000..a843688
--- /dev/null
+++ b/lib/attestation/include/attestation.h
@@ -0,0 +1,81 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef ATTESTATION_H
+#define ATTESTATION_H
+
+#include <t_cose/q_useful_buf.h>
+
+struct buffer_alloc_ctx;
+
+/*
+ * Performs any early initialization needed for the crypto library.
+ */
+int attestation_init(void);
+
+/*
+ * Return the platform token that was previously retrieved from the monitor.
+ *
+ * Arguments:
+ * buf - pointer to a q_useful_buf structure where the reference to the
+ *	 platform token will be returned.
+ *
+ * Returns 0 on success, and a negative error code otherwise.
+ */
+int attest_get_platform_token(struct q_useful_buf_c **buf);
+
+/*
+ * Initialize the heap buffer to be used with the given buffer_alloc_ctx.
+ * This is done when a REC is created.
+ *
+ * As a pre-requisite, ensure that a buffer_alloc_ctx has been assigned to this
+ * PE prior to calling this function.
+ *
+ * Arguments:
+ * buf - pointer to start of heap
+ * buf_size -  size of the heap
+ *
+ * Returns 0 on success, negative error code on error.
+ */
+int attestation_heap_ctx_init(unsigned char *buf, size_t buf_size);
+
+/*
+ * Assign a given buf_alloc_ctx to this CPU. This needs to be called
+ * prior to entering a Realm to allow it invoking RMM crypto operations.
+ *
+ * Arguments:
+ * ctx - pointer to buffer_alloc_ctx
+ *
+ * Returns 0 on success, negative error code on error.
+ */
+int attestation_heap_ctx_assign_pe(struct buffer_alloc_ctx *ctx);
+
+
+/*
+ * Unassign a given buf_alloc_ctx from CPU. This needs to be called
+ * after exiting the realm.
+ *
+ * Arguments:
+ * ctx - pointer to buffer_alloc_ctx
+ *
+ * Returns 0 on success, negative error code on error.
+ */
+int attestation_heap_ctx_unassign_pe(struct buffer_alloc_ctx *ctx);
+
+/*
+ * Reinit the heap on this CPU used for attestation operations.
+ *
+ * Arguments:
+ * buf		- Buffer to use as heap.
+ * buf_size	- Size of the buffer to use as heap.
+ *
+ * Returns 0 on success, negative error code otherwise.
+ *
+ * Note: This function assumes that a the allocator has a
+ * buffer_alloc_ctx assigned to it.
+ */
+int attestation_heap_reinit_pe(unsigned char *buf, size_t buf_size);
+
+#endif /* ATTESTATION_H */
diff --git a/lib/attestation/include/attestation_token.h b/lib/attestation/include/attestation_token.h
new file mode 100644
index 0000000..dc283c9
--- /dev/null
+++ b/lib/attestation/include/attestation_token.h
@@ -0,0 +1,144 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright Laurence Lundblade.
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+/*
+ * This file is derived from:
+ *    trusted-firmware-m/secure_fw/partitions/initial_attestation/attest_token.h
+ */
+
+#ifndef ATTESTATION_TOKEN_H
+#define ATTESTATION_TOKEN_H
+
+#include <measurement.h>
+#include <qcbor/qcbor.h>
+#include <t_cose/q_useful_buf.h>
+#include <t_cose/t_cose_sign1_sign.h>
+
+#define ATTEST_TOKEN_BUFFER_SIZE		GRANULE_SIZE
+
+enum attest_token_err_t {
+	/* Success */
+	ATTEST_TOKEN_ERR_SUCCESS = 0,
+	/* The buffer passed in to receive the output is too small. */
+	ATTEST_TOKEN_ERR_TOO_SMALL,
+	/*
+	 * Something went wrong formatting the CBOR, most likely the
+	 * payload has maps or arrays that are not closed.
+	 */
+	ATTEST_TOKEN_ERR_CBOR_FORMATTING,
+	/* Signing key is not found or of wrong type. */
+	ATTEST_TOKEN_ERR_SIGNING_KEY,
+	ATTEST_TOKEN_ERR_COSE_ERROR,
+	/* Signing is in progress, function should be called with the same
+	 * parameters again.
+	 */
+	ATTEST_TOKEN_ERR_COSE_SIGN_IN_PROGRESS
+};
+
+/* The state of the realm token generation */
+enum attest_token_gen_state_t {
+	ATTEST_SIGN_NOT_STARTED,
+	ATTEST_SIGN_IN_PROGRESS,
+	ATTEST_SIGN_TOKEN_WRITE_IN_PROGRESS,
+};
+
+/*
+ * The context for creating an attestation token. The caller of
+ * attest_token_encode must create one of these and pass it to the functions
+ * here. It is small enough that it can go on the stack. It is most of
+ * the memory needed to create a token except the output buffer and
+ * any memory requirements for the cryptographic operations.
+ *
+ * The structure is opaque for the caller.
+ *
+ * This is roughly 148 + 8 + 32 = 188 bytes
+ */
+
+struct attest_token_encode_ctx {
+	/* Private data structure */
+	QCBOREncodeContext                   cbor_enc_ctx;
+	uint32_t                             opt_flags;
+	int32_t                              key_select;
+	struct t_cose_sign1_sign_ctx         signer_ctx;
+	struct t_cose_sign1_sign_restart_ctx signer_restart_ctx;
+	struct t_cose_crypto_backend_ctx     crypto_ctx;
+};
+
+#define ATTEST_CHALLENGE_SIZE			(64)
+
+/*
+ * The context for signing an attestation token. Each REC contains one context
+ * that is passed to the attestation library during attestation token creation
+ * to keep track of the signing state.
+ */
+struct token_sign_ctx {
+	/*
+	 * 'state' is used to implement a state machine
+	 * to track the current state of signing.
+	 */
+	enum attest_token_gen_state_t state;
+	struct attest_token_encode_ctx ctx;
+	/* Data saved in the first iteration */
+	unsigned long token_ipa;
+	unsigned char challenge[ATTEST_CHALLENGE_SIZE];
+};
+
+/*
+ * Sign the realm token and complete the CBOR encoding.
+ * This function returns ATTEST_TOKEN_ERR_COSE_SIGN_IN_PROGRESS
+ * if signing is not complete and this function needs to be
+ * invoked again. ATTEST_TOKEN_ERR_SUCCESS is returned if
+ * signing is complete and `completed_token` is valid.
+ * Else returns one of the attest_token_err_t errors on
+ * any other error.
+ *
+ * me					Token Creation Context.
+ * completed_token		Pointer and length to completed token.
+ *
+ * This completes the token after the payload has been added. When
+ * this is called the signing algorithm is run and the final
+ * formatting of the token is completed.
+ */
+enum attest_token_err_t
+attest_realm_token_sign(struct attest_token_encode_ctx *me,
+			struct q_useful_buf_c *completed_token);
+
+/*
+ * Combine realm token and platform token to top-level cca token
+ *
+ * attest_token_buf  Pointer and length to the buffer where the token will be
+ *                   written.
+ * realm_token       Pointer and length to the realm token.
+ *
+ * Return 0 in case of error, the length of the cca token otherwise.
+ */
+size_t attest_cca_token_create(struct q_useful_buf         *attest_token_buf,
+			       const struct q_useful_buf_c *realm_token);
+
+/*
+ * Assemble the Realm token in the buffer provided in realm_token_buf,
+ * except the signature.
+ *
+ * Arguments:
+ * Algorithm		- Algorithm used during measurement.
+ * Measurement		- Array of buffers containing all the measurements.
+ * num_measurements	- Number of measurements to add to the token.
+ * rpv                  - Realm Personalization value
+ * ctx			- Token sign context, used for signing.
+ * realm_token_buf	- Buffer where to assemble the attestation token.
+ *
+ * Returns ATTEST_TOKEN_ERR_SUCCESS (0) on success or a negative error code
+ * otherwise.
+ */
+int attest_realm_token_create(enum hash_algo algorithm,
+			     unsigned char measurements[][MAX_MEASUREMENT_SIZE],
+			     unsigned int num_measurements,
+			     struct q_useful_buf_c *rpv,
+			     struct token_sign_ctx *ctx,
+			     struct q_useful_buf *realm_token_buf);
+
+
+#endif /* ATTESTATION_TOKEN_H */
diff --git a/lib/attestation/src/attestation_defs_priv.h b/lib/attestation/src/attestation_defs_priv.h
new file mode 100644
index 0000000..0295bd9
--- /dev/null
+++ b/lib/attestation/src/attestation_defs_priv.h
@@ -0,0 +1,26 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+/*
+ * TODO: Include a reference the RMM spec once the attestation
+ * token spec is included on the former.
+ */
+
+#ifndef ATTESTATION_DEFS_PRIV_H
+#define ATTESTATION_DEFS_PRIV_H
+
+#define CCA_REALM_CHALLENGE			(10)
+#define CCA_REALM_PERSONALIZATION_VALUE		(44235)
+#define CCA_REALM_HASH_ALGM_ID			(44236)
+#define CCA_REALM_PUB_KEY			(44237)
+#define CCA_REALM_INITIAL_MEASUREMENT		(44238)
+#define CCA_REALM_EXTENSIBLE_MEASUREMENTS	(44239)
+#define CCA_REALM_PUB_KEY_HASH_ALGO_ID		(44240)
+
+#define TAG_CCA_TOKEN				(399)
+#define CCA_PLAT_TOKEN				(44234)
+#define CCA_REALM_DELEGATED_TOKEN		(44241)
+
+#endif /* ATTESTATION_DEFS_PRIV_H */
diff --git a/lib/attestation/src/attestation_key.c b/lib/attestation/src/attestation_key.c
new file mode 100644
index 0000000..0ad9dd5
--- /dev/null
+++ b/lib/attestation/src/attestation_key.c
@@ -0,0 +1,278 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <assert.h>
+#include <attestation.h>
+#include <attestation_priv.h>
+#include <debug.h>
+#include <errno.h>
+#include <fpu_helpers.h>
+#include <mbedtls/sha256.h>
+#include <measurement.h>
+#include <psa/crypto.h>
+#include <rmm_el3_ifc.h>
+#include <sizes.h>
+
+#define ECC_P384_PUBLIC_KEY_SIZE	(97U)
+#define SHA256_DIGEST_SIZE		(32U)
+
+/*
+ * The size of X and Y coordinate in 2 parameter style EC public key. Format is
+ * as defined in [COSE (RFC 8152)] (https://tools.ietf.org/html/rfc8152) and
+ * [SEC 1: Elliptic Curve Cryptography](http://www.secg.org/sec1-v2.pdf).
+ *
+ * This size is well-known and documented in public standards.
+ */
+#define ECC_P384_COORD_SIZE		(48U) /* 384 bits -> 48 bytes */
+#define BIT_SIZE_OF_P384		(384U)
+
+/* ECC Curve type define for querying attestation key from monitor */
+#define ATTEST_KEY_CURVE_ECC_SECP384R1	0
+
+/*
+ * The platform token which will be needed during attestation.
+ */
+static unsigned char rmm_platform_token_buf[SZ_4K];
+static struct q_useful_buf rmm_platform_token;
+
+/*
+ * The public key is kept loaded as it is both not required to be secret (and
+ * hence can be kept in attestation memory) and immutable.
+ */
+static uint8_t realm_attest_public_key[ECC_P384_PUBLIC_KEY_SIZE];
+static size_t realm_attest_public_key_len;
+
+/*
+ * The hash of the realm attestation public key is included in the Platform
+ * attestation token as the challenge claim.
+ */
+static uint8_t realm_attest_public_key_hash[SHA256_DIGEST_SIZE];
+static size_t realm_attest_public_key_hash_len;
+
+/*
+ * The keypair for the sign operation
+ */
+static mbedtls_ecp_keypair realm_attest_keypair = {0};
+
+/* Specify the hash algorithm to use for computing the hash of the
+ * realm public key.
+ */
+static enum hash_algo public_key_hash_algo_id = HASH_ALGO_SHA256;
+
+/*
+ * TODO: review panic usage and try to gracefully exit on error. Also
+ * improve documentation of usage of MbedTLS APIs
+ */
+int attest_init_realm_attestation_key(void)
+{
+	int ret;
+	struct q_useful_buf realm_attest_private_key;
+	uintptr_t buf;
+	size_t attest_key_size = 0UL;
+
+	struct attest_rng_context rng_ctx;
+
+	assert(IS_FPU_ALLOWED());
+
+	attest_get_cpu_rng_context(&rng_ctx);
+
+	/*
+	 * The realm attestation key is requested from the root world in the
+	 * boot phase only once. Then the same key is used in the entire power
+	 * cycle to sign the realm attestation tokens.
+	 */
+	if (realm_attest_keypair.MBEDTLS_PRIVATE(d).MBEDTLS_PRIVATE(p) != NULL) {
+		ERROR("Realm attestation key already loaded.\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Get the realm attestation key. The key is retrieved in raw format.
+	 */
+	buf = rmm_el3_ifc_get_shared_buf_locked();
+
+	if (rmm_el3_ifc_get_realm_attest_key(buf,
+				rmm_el3_ifc_get_shared_buf_size(),
+				&attest_key_size,
+				ATTEST_KEY_CURVE_ECC_SECP384R1) != 0) {
+		rmm_el3_ifc_release_shared_buf();
+		return -EINVAL;
+	}
+
+	realm_attest_private_key.len = attest_key_size;
+	realm_attest_private_key.ptr = (void *)buf;
+
+	/*
+	 * Setup ECC key.
+	 * The memory for the keypair is allocated from MbedTLS Heap.
+	 */
+	mbedtls_ecp_keypair_init(&realm_attest_keypair);
+	ret = mbedtls_ecp_group_load(&realm_attest_keypair.MBEDTLS_PRIVATE(grp),
+				     MBEDTLS_ECP_DP_SECP384R1);
+	if (ret != 0) {
+		ERROR("mbedtls_ecp_group_load has failed\n");
+		rmm_el3_ifc_release_shared_buf();
+		return -EINVAL;
+	}
+
+	ret = mbedtls_mpi_read_binary(&realm_attest_keypair.MBEDTLS_PRIVATE(d),
+				      realm_attest_private_key.ptr,
+				      realm_attest_private_key.len);
+	if (ret != 0) {
+		ERROR("mbedtls_mpi_read_binary has failed\n");
+		rmm_el3_ifc_release_shared_buf();
+		return -EINVAL;
+	}
+
+	ret = mbedtls_ecp_check_privkey(&realm_attest_keypair.MBEDTLS_PRIVATE(grp),
+					&realm_attest_keypair.MBEDTLS_PRIVATE(d));
+	if (ret != 0) {
+		ERROR("mbedtls_ecp_check_privkey has failed: %d\n", ret);
+		rmm_el3_ifc_release_shared_buf();
+		return -EINVAL;
+	}
+
+	ret = mbedtls_ecp_mul(&realm_attest_keypair.MBEDTLS_PRIVATE(grp),
+			      &realm_attest_keypair.MBEDTLS_PRIVATE(Q),
+			      &realm_attest_keypair.MBEDTLS_PRIVATE(d),
+			      &realm_attest_keypair.MBEDTLS_PRIVATE(grp).G,
+			      rng_ctx.f_rng,
+			      rng_ctx.p_rng);
+	if (ret != 0) {
+		ERROR("mbedtls_ecp_mul priv has failed: %d\n", ret);
+		rmm_el3_ifc_release_shared_buf();
+		return -EINVAL;
+	}
+
+	ret = mbedtls_ecp_point_write_binary(&realm_attest_keypair.MBEDTLS_PRIVATE(grp),
+					     &realm_attest_keypair.MBEDTLS_PRIVATE(Q),
+					     MBEDTLS_ECP_PF_UNCOMPRESSED,
+					     &realm_attest_public_key_len,
+					     realm_attest_public_key,
+					     sizeof(realm_attest_public_key));
+	if (ret != 0) {
+		ERROR("mbedtls_ecp_point_write_binary pub has failed\n");
+		rmm_el3_ifc_release_shared_buf();
+		return -EINVAL;
+	}
+
+	/* Compute the hash of the realm attestation public key */
+	ret = mbedtls_sha256(realm_attest_public_key,
+			     realm_attest_public_key_len,
+			     realm_attest_public_key_hash,
+			     false);
+	if (ret != 0) {
+		ERROR("mbedtls_sha256 has failed\n");
+		rmm_el3_ifc_release_shared_buf();
+		return -EINVAL;
+	}
+
+	realm_attest_public_key_hash_len = sizeof(realm_attest_public_key_hash);
+
+	/* Clear the private key from the buffer */
+	(void)memset(realm_attest_private_key.ptr, 0,
+			realm_attest_private_key.len);
+
+	rmm_el3_ifc_release_shared_buf();
+
+	return 0;
+}
+
+int attest_get_realm_signing_key(const void **keypair)
+{
+	if (realm_attest_keypair.MBEDTLS_PRIVATE(d).MBEDTLS_PRIVATE(p) == NULL) {
+		ERROR("Realm attestation key not initialized\n");
+		return -EINVAL;
+	}
+
+	*keypair = &realm_attest_keypair;
+	return 0;
+}
+
+int attest_get_realm_public_key_hash(struct q_useful_buf_c *public_key_hash)
+{
+	if (realm_attest_keypair.MBEDTLS_PRIVATE(d).MBEDTLS_PRIVATE(p) == NULL) {
+		ERROR("Realm attestation key not initialized\n");
+		return -EINVAL;
+	}
+
+	public_key_hash->ptr = realm_attest_public_key_hash;
+	public_key_hash->len = realm_attest_public_key_hash_len;
+	return 0;
+}
+
+int attest_get_realm_public_key(struct q_useful_buf_c *public_key)
+{
+	if (realm_attest_keypair.MBEDTLS_PRIVATE(d).MBEDTLS_PRIVATE(p) == NULL) {
+		ERROR("Realm attestation key not initialized\n");
+		return -EINVAL;
+	}
+
+	public_key->ptr = realm_attest_public_key;
+	public_key->len = realm_attest_public_key_len;
+	return 0;
+}
+
+int attest_setup_platform_token(void)
+{
+	int ret;
+	uintptr_t shared_buf;
+	size_t platform_token_len = 0;
+	struct q_useful_buf_c rmm_pub_key_hash;
+
+	/*
+	 * Copy the RAK public hash value to the token buffer. This is
+	 * used as the challenge input for the token generation
+	 * thus creating a binding between the two.
+	 */
+	ret = attest_get_realm_public_key_hash(&rmm_pub_key_hash);
+	if (ret != 0) {
+		ERROR("Realm attestation key not initialized\n");
+		return ret;
+	}
+
+	shared_buf = rmm_el3_ifc_get_shared_buf_locked();
+
+	(void)memcpy((void *)shared_buf, rmm_pub_key_hash.ptr,
+					 rmm_pub_key_hash.len);
+
+	ret = rmm_el3_ifc_get_platform_token(shared_buf,
+					     rmm_el3_ifc_get_shared_buf_size(),
+					     &platform_token_len,
+					     SHA256_DIGEST_SIZE);
+
+	if (ret != 0) {
+		rmm_el3_ifc_release_shared_buf();
+		return -EINVAL;
+	}
+
+	(void)memcpy(rmm_platform_token_buf,
+		     (void *)shared_buf,
+		     platform_token_len);
+
+	rmm_el3_ifc_release_shared_buf();
+
+	rmm_platform_token.ptr = rmm_platform_token_buf;
+	rmm_platform_token.len = platform_token_len;
+
+	return 0;
+}
+
+int attest_get_platform_token(struct q_useful_buf_c **buf)
+{
+	assert(buf != NULL);
+
+	if (rmm_platform_token.ptr == NULL) {
+		return -EINVAL;
+	}
+
+	*buf = (struct q_useful_buf_c *)&rmm_platform_token;
+	return 0;
+}
+
+enum hash_algo attest_get_realm_public_key_hash_algo_id(void)
+{
+	return public_key_hash_algo_id;
+}
diff --git a/lib/attestation/src/attestation_priv.h b/lib/attestation/src/attestation_priv.h
new file mode 100644
index 0000000..852381b
--- /dev/null
+++ b/lib/attestation/src/attestation_priv.h
@@ -0,0 +1,99 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef ATTESTATION_PRIV_H
+#define ATTESTATION_PRIV_H
+
+/*
+ * A structure holding the context for generating a pseudo-random number derived
+ * from a real random seed.
+ */
+struct attest_rng_context {
+	int (*f_rng)(void *p_rng, unsigned char *output, size_t out_len);
+	void *p_rng;
+};
+
+/*
+ * Copy the rng_ctx for the current CPU to rng_ctx
+ *
+ * Arguments:
+ * rng_ctx - Pointer to the target context structure
+ */
+void attest_get_cpu_rng_context(struct attest_rng_context *rng_ctx);
+
+/*
+ * Get a pointer to the keypair for signing realm attestation token.
+ *
+ * Arguments:
+ * keypair - Pointer to the keypair for signing token.
+
+ * Returns 0 on success, negative error code on error.
+ */
+int attest_get_realm_signing_key(const void **keypair);
+
+/*
+ * Query the attestation private key from monitor and generate the public
+ * key by using MbedCryto lib. The key is cached internally for future
+ * use. The function returns early if the key has been initialized.
+ *
+ * FPU context must be saved and FPU access should be enabled by caller.
+ *
+ * Returns 0 on success, negative error code on error.
+ */
+int attest_init_realm_attestation_key(void);
+
+/*
+ * Get the hash of the realm attestation public key. The public key hash is the
+ * challenge value in the platform attestation token.
+ *
+ * Arguments:
+ * public_key_hash - Get the buffer address and size which holds
+ *                   the hash of the realm attestation public key.
+ *
+ * Returns 0 on success, negative error code on error.
+ *
+ */
+int attest_get_realm_public_key_hash(struct q_useful_buf_c *public_key_hash);
+
+/*
+ * Get the realm attestation public key hash. The public key hash is the
+ * challenge value in the platform attestation token.
+ *
+ * Arguments:
+ * public_key - Get the buffer address and size which holds the realm
+ *              attestation public key.
+ *
+ * Returns 0 on success, negative error code on error.
+ */
+int attest_get_realm_public_key(struct q_useful_buf_c *public_key);
+
+/*
+ * Get the platform token from monitor. This function needs to be called
+ * after the Realm attestation key has been initialized.
+ *
+ * Returns 0 on success, negative error code on error.
+ */
+int attest_setup_platform_token(void);
+
+/*
+ * Get the hash algorithm to use for computing the hash of the realm public key.
+ */
+enum hash_algo attest_get_realm_public_key_hash_algo_id(void);
+
+/*
+ * Initialise PRNGs for all the CPUs
+ *
+ * FPU context must be saved and FPU access should be enabled by caller.
+ *
+ * Returns 0 on success, negative error code otherwise.
+ *
+ * This function creates a separate PRNG object for all the CPUs. The PRNGs are
+ * used by Mbed TLS when it needs random data. The PRNGs are seeded with values
+ * generated by a temporary PRNG, which is in turn is seeded with a real random
+ * value.
+ */
+int attest_rnd_prng_init(void);
+
+#endif /* ATTESTATION_PRIV_H */
diff --git a/lib/attestation/src/attestation_rnd.c b/lib/attestation/src/attestation_rnd.c
new file mode 100644
index 0000000..bd2a9a5
--- /dev/null
+++ b/lib/attestation/src/attestation_rnd.c
@@ -0,0 +1,153 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <arch_features.h>
+#include <assert.h>
+#include <attestation.h>
+#include <attestation_priv.h>
+#include <cpuid.h>
+#include <entropy.h>
+#include <errno.h>
+#include <fpu_helpers.h>
+#include <mbedtls/entropy.h>
+#include <mbedtls/hmac_drbg.h>
+#include <platform_api.h>
+#include <stdbool.h>
+#include <utils_def.h>
+
+/*
+ * Allocate a PRNG object per PE in order to avoid the necessity of locking if
+ * concurrent attestation token requests are executed.
+ */
+static mbedtls_hmac_drbg_context cpu_drbg_ctx[MAX_CPUS];
+static bool prng_init_done;
+
+static int get_random_seed(unsigned char *output, size_t len)
+{
+	bool rc;
+	uint64_t *random_output;
+	uint64_t *random_end;
+
+	assert(!prng_init_done);
+
+	/* Enforce `len` is a multiple of 8 and `output` is 8-byte aligned. */
+	assert((len & 0x7UL) == 0UL && ((uintptr_t)output & 0x7UL) == 0UL);
+
+	random_output = (uint64_t *)output;
+	random_end = (uint64_t *)(output + len);
+
+	for (; random_output < random_end; ++random_output) {
+		rc = arch_collect_entropy(random_output);
+		if (!rc) {
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+/*
+ * This function is used by Mbed TLS as a source of entropy. This means it is
+ * called during RMM operation, to add entropy to the signing process.
+ * See declaration in mbedtls/entropy_poll.h.
+ * For details see `MBEDTLS_ENTROPY_HARDWARE_ALT` in mbedtls/config.h
+ */
+int mbedtls_hardware_poll(void  *data, unsigned char *output,
+			  size_t len,  size_t        *olen)
+{
+	int ret;
+	unsigned int cpu_id = my_cpuid();
+	void *rng_ctx;
+
+	assert(prng_init_done);
+
+	(void)data;
+
+	/* Not in RMM init, PRNGs are already initialized, use them. */
+	rng_ctx = &cpu_drbg_ctx[cpu_id];
+	ret = mbedtls_hmac_drbg_random(rng_ctx, output, len);
+	if (ret != 0) {
+		return ret;
+	}
+	*olen = len;
+
+	return 0;
+}
+
+void attest_get_cpu_rng_context(struct attest_rng_context *rng_ctx)
+{
+	unsigned int cpu_id = my_cpuid();
+
+	assert(prng_init_done);
+
+	rng_ctx->f_rng = mbedtls_hmac_drbg_random;
+	rng_ctx->p_rng = &cpu_drbg_ctx[cpu_id];
+}
+
+int attest_rnd_prng_init(void)
+{
+	const mbedtls_md_info_t *md_info;
+	mbedtls_hmac_drbg_context drbg_ctx;
+	uint8_t seed[128] __aligned(8) ; /* mbedtls_hardware_poll request this size */
+	unsigned int i;
+	int rc;
+	int retval = 0;
+
+	assert(!prng_init_done);
+	assert(IS_FPU_ALLOWED());
+
+	if (!is_feat_rng_present()) {
+		return -EINVAL;
+	}
+
+	/*
+	 * Setup a temporary PRNG which seeded by real TRNG and use this
+	 * instance to set up the per CPU PRNG objects. The temporary PRNG
+	 * relies on the RNDR instruction to get its seed. RNDR instruction has
+	 * an implementation defined TRNG backend. The timing of the TRNG could
+	 * be nondeterministic therefore access to it is kept on the minimum.
+	 */
+	rc = get_random_seed(seed, sizeof(seed));
+	if (rc != 0) {
+		return -EINVAL;
+	}
+
+	md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
+	mbedtls_hmac_drbg_init(&drbg_ctx);
+	rc = mbedtls_hmac_drbg_seed_buf(&drbg_ctx,
+				    md_info,
+				    seed, sizeof(seed));
+	if (rc != 0) {
+		retval = -EINVAL;
+		goto free_temp_prng;
+	}
+
+	/*
+	 * Set up the per CPU PRNG objects which going to be used during
+	 * Elliptic Curve signing to blind the private key.
+	 */
+	for (i = 0U; i < MAX_CPUS; ++i) {
+		rc = mbedtls_hmac_drbg_random(&drbg_ctx, seed, sizeof(seed));
+		if (rc != 0) {
+			retval = -EINVAL;
+			goto free_temp_prng;
+		}
+
+		mbedtls_hmac_drbg_init(&cpu_drbg_ctx[i]);
+		rc = mbedtls_hmac_drbg_seed_buf(&cpu_drbg_ctx[i], md_info,
+						seed, sizeof(seed));
+		if (rc != 0) {
+			retval = -EINVAL;
+			goto free_temp_prng;
+		}
+	}
+
+	prng_init_done = true;
+
+free_temp_prng:
+	/* Free the memory allocated by the temporary PRNG */
+	mbedtls_hmac_drbg_free(&drbg_ctx);
+
+	return retval;
+}
diff --git a/lib/attestation/src/attestation_token.c b/lib/attestation/src/attestation_token.c
new file mode 100644
index 0000000..ba6ad56
--- /dev/null
+++ b/lib/attestation/src/attestation_token.c
@@ -0,0 +1,329 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright Laurence Lundblade.
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+/*
+ * This file is derived from:
+ *    trusted-firmware-m/secure_fw/partitions/initial_attestation/attest_token_encode.c
+ */
+
+#include <assert.h>
+#include <attestation.h>
+#include <attestation_defs_priv.h>
+#include <attestation_priv.h>
+#include <attestation_token.h>
+#include <debug.h>
+#include <fpu_helpers.h>
+#include <measurement.h>
+#include <qcbor/qcbor.h>
+#include <t_cose/q_useful_buf.h>
+#include <t_cose/t_cose_common.h>
+#include <t_cose/t_cose_sign1_sign.h>
+#include <utils_def.h>
+
+/*
+ * According to IANA hash algorithm registry:
+ *   - https://www.iana.org/assignments/hash-function-text-names/hash-function-text-names.xml
+ */
+static void attest_get_hash_algo_text(enum hash_algo  algo_id,
+				      struct q_useful_buf_c *algo_text)
+{
+	const char *sha256 = "sha-256";
+	const char *sha512 = "sha-512";
+
+	switch (algo_id) {
+	case HASH_ALGO_SHA256:
+		*algo_text = UsefulBuf_FromSZ(sha256);
+		break;
+	case HASH_ALGO_SHA512:
+		*algo_text = UsefulBuf_FromSZ(sha512);
+		break;
+	default:
+		assert(false);
+	}
+}
+
+/*
+ * Outline of token creation. Much of this occurs inside
+ * t_cose_sign1_encode_parameters() and t_cose_sign1_encode_signature().
+ *
+ * - Create encoder context
+ * - Open the CBOR array that hold the COSE_Sign1
+ * - Write COSE Headers
+ *   - Protected Header
+ *      - Algorithm ID
+ *   - Unprotected Headers
+ *     - Key ID
+ * - Open payload bstr
+ *   - Write payload data, maybe lots of it
+ *   - Get bstr that is the encoded payload
+ * - Compute signature
+ *   - Create a separate encoder context for Sig_structure
+ *     - Encode CBOR context identifier
+ *     - Encode protected headers
+ *     - Encode two empty bstr
+ *     - Add one more empty bstr that is a "fake payload"
+ *     - Close off Sig_structure
+ *   - Hash all but "fake payload" of Sig_structure
+ *   - Get payload bstr ptr and length
+ *   - Continue hash of the real encoded payload
+ *   - Run ECDSA
+ * - Write signature into the CBOR output
+ * - Close CBOR array holding the COSE_Sign1
+ */
+static enum attest_token_err_t
+attest_token_encode_start(struct attest_token_encode_ctx *me,
+			  uint32_t opt_flags,
+			  int32_t key_select,
+			  int32_t cose_alg_id,
+			  const struct q_useful_buf *out_buf)
+{
+	enum t_cose_err_t cose_res;
+	uint32_t t_cose_options = 0;
+	struct t_cose_key attest_key;
+	const void *signing_key;
+	struct q_useful_buf_c attest_key_id = NULL_Q_USEFUL_BUF_C;
+
+	assert(me != NULL);
+	assert(out_buf != NULL);
+
+	/* Remember some of the configuration values */
+	me->opt_flags  = opt_flags;
+	me->key_select = key_select;
+
+	t_cose_sign1_sign_init(&(me->signer_ctx),
+			       t_cose_options,
+			       cose_alg_id);
+
+	cose_res = t_cose_sign1_set_restart_context(&(me->signer_ctx),
+						    &(me->signer_restart_ctx));
+	if (cose_res != T_COSE_SUCCESS) {
+		return ATTEST_TOKEN_ERR_COSE_ERROR;
+	}
+
+	t_cose_sign1_set_crypto_context(&(me->signer_ctx),
+					&(me->crypto_ctx));
+
+
+	/*
+	 * Get the reference to `mbedtls_ecp_keypair` and set it to t_cose.
+	 */
+	attest_get_realm_signing_key(&signing_key);
+	attest_key.crypto_lib = T_COSE_CRYPTO_LIB_MBEDTLS;
+	attest_key.k.key_ptr = (void *)signing_key;
+	t_cose_sign1_set_signing_key(&(me->signer_ctx),
+				     attest_key,
+				     attest_key_id);
+
+	/* Spin up the CBOR encoder */
+	QCBOREncode_Init(&(me->cbor_enc_ctx), *out_buf);
+
+	/*
+	 * This will cause the cose headers to be encoded and written into
+	 * out_buf using me->cbor_enc_ctx
+	 */
+	cose_res = t_cose_sign1_encode_parameters(&(me->signer_ctx),
+						  &(me->cbor_enc_ctx));
+
+	if (cose_res != T_COSE_SUCCESS) {
+		return ATTEST_TOKEN_ERR_COSE_ERROR;
+	}
+
+	QCBOREncode_OpenMap(&(me->cbor_enc_ctx));
+	return ATTEST_TOKEN_ERR_SUCCESS;
+}
+
+enum attest_token_err_t
+attest_realm_token_sign(struct attest_token_encode_ctx *me,
+			struct q_useful_buf_c *completed_token)
+{
+	/* The completed and signed encoded cose_sign1 */
+	struct q_useful_buf_c completed_token_ub;
+	enum attest_token_err_t attest_res = ATTEST_TOKEN_ERR_SUCCESS;
+	QCBORError qcbor_res;
+	enum t_cose_err_t cose_res;
+
+	assert(me != NULL);
+	assert(completed_token != NULL);
+
+	/* Finish up the COSE_Sign1. This is where the signing happens */
+	FPU_ALLOW(
+		cose_res = t_cose_sign1_encode_signature(&(me->signer_ctx),
+							 &(me->cbor_enc_ctx)));
+
+	if (cose_res == T_COSE_ERR_SIG_IN_PROGRESS) {
+		/* Token signing has not yet finished */
+		return ATTEST_TOKEN_ERR_COSE_SIGN_IN_PROGRESS;
+	}
+
+	if (cose_res != T_COSE_SUCCESS) {
+		/* Main errors are invoking the hash or signature */
+		return ATTEST_TOKEN_ERR_COSE_ERROR;
+	}
+
+	/*
+	 * Finally close off the CBOR formatting and get the pointer and length
+	 * of the resulting COSE_Sign1
+	 */
+	qcbor_res = QCBOREncode_Finish(&(me->cbor_enc_ctx),
+				       &completed_token_ub);
+
+	switch (qcbor_res) {
+	case QCBOR_ERR_BUFFER_TOO_SMALL:
+		attest_res = ATTEST_TOKEN_ERR_TOO_SMALL;
+		break;
+	case QCBOR_SUCCESS:
+		*completed_token = completed_token_ub;
+		break;
+	default:
+		/* likely from array not closed, too many closes ... */
+		attest_res = ATTEST_TOKEN_ERR_CBOR_FORMATTING;
+	}
+
+	return attest_res;
+}
+
+size_t attest_cca_token_create(struct q_useful_buf         *attest_token_buf,
+			       const struct q_useful_buf_c *realm_token)
+{
+	struct q_useful_buf_c   completed_token;
+	QCBOREncodeContext      cbor_enc_ctx;
+	QCBORError              qcbor_res;
+	struct q_useful_buf_c  *rmm_platform_token;
+
+	__unused int            ret;
+
+	/* Get the platform token */
+	ret = attest_get_platform_token(&rmm_platform_token);
+	assert(ret == 0);
+
+	QCBOREncode_Init(&cbor_enc_ctx, *attest_token_buf);
+
+	QCBOREncode_AddTag(&cbor_enc_ctx, TAG_CCA_TOKEN);
+
+	QCBOREncode_OpenMap(&cbor_enc_ctx);
+
+	QCBOREncode_AddBytesToMapN(&cbor_enc_ctx,
+				   CCA_PLAT_TOKEN,
+				   *rmm_platform_token);
+
+	QCBOREncode_AddBytesToMapN(&cbor_enc_ctx,
+				   CCA_REALM_DELEGATED_TOKEN,
+				   *realm_token);
+	QCBOREncode_CloseMap(&cbor_enc_ctx);
+
+	qcbor_res = QCBOREncode_Finish(&cbor_enc_ctx, &completed_token);
+
+	if (qcbor_res == QCBOR_ERR_BUFFER_TOO_SMALL) {
+		ERROR("CCA output token buffer too small\n");
+		return 0;
+	} else if (qcbor_res != QCBOR_SUCCESS) {
+		/* likely from array not closed, too many closes, ... */
+		assert(false);
+	} else {
+		return completed_token.len;
+	}
+	return 0;
+}
+
+/*
+ * Assemble the Realm Attestation Token in the buffer provided in
+ * realm_token_buf, except the signature.
+ *
+ * As per section A7.2.3.1 of RMM specfication, Realm Attestation token is
+ * composed of:
+ *	- Realm Challenge
+ *	- Realm Personalization Value
+ *	- Realm Hash Algorithm Id
+ *	- Realm Public Key
+ *	- Realm Public Key Hash Algorithm Id
+ *	- Realm Initial Measurement
+ *	- Realm Extensible Measurements
+ */
+int attest_realm_token_create(enum hash_algo algorithm,
+			     unsigned char measurements[][MAX_MEASUREMENT_SIZE],
+			     unsigned int num_measurements,
+			     struct q_useful_buf_c *rpv,
+			     struct token_sign_ctx *ctx,
+			     struct q_useful_buf *realm_token_buf)
+{
+	struct q_useful_buf_c buf;
+	size_t measurement_size;
+	enum attest_token_err_t token_ret;
+	int ret;
+
+	/* Can only be called in the init state */
+	assert(ctx->state == ATTEST_SIGN_NOT_STARTED);
+
+	assert(num_measurements == MEASUREMENT_SLOT_NR);
+
+	/*
+	 * Get started creating the token. This sets up the CBOR and COSE
+	 * contexts which causes the COSE headers to be constructed.
+	 */
+	token_ret = attest_token_encode_start(&(ctx->ctx),
+					      0,     /* option_flags */
+					      0,     /* key_select */
+					      T_COSE_ALGORITHM_ES384,
+					      realm_token_buf);
+	if (token_ret != ATTEST_TOKEN_ERR_SUCCESS) {
+		return token_ret;
+	}
+
+	/* Add challenge value, which is the only input from the caller. */
+	buf.ptr = ctx->challenge;
+	buf.len = ATTEST_CHALLENGE_SIZE;
+	QCBOREncode_AddBytesToMapN(&(ctx->ctx.cbor_enc_ctx),
+				   CCA_REALM_CHALLENGE,
+				   buf);
+
+	QCBOREncode_AddBytesToMapN(&(ctx->ctx.cbor_enc_ctx),
+				   CCA_REALM_PERSONALIZATION_VALUE,
+				   *rpv);
+
+	ret = attest_get_realm_public_key(&buf);
+	if (ret != 0) {
+		return ret;
+	}
+
+	QCBOREncode_AddBytesToMapN(&(ctx->ctx.cbor_enc_ctx),
+				   CCA_REALM_PUB_KEY,
+				   buf);
+
+	attest_get_hash_algo_text(algorithm, &buf);
+	QCBOREncode_AddTextToMapN(&(ctx->ctx.cbor_enc_ctx),
+				  CCA_REALM_HASH_ALGM_ID,
+				  buf);
+
+	attest_get_hash_algo_text(attest_get_realm_public_key_hash_algo_id(),
+				  &buf);
+	QCBOREncode_AddTextToMapN(&(ctx->ctx.cbor_enc_ctx),
+				  CCA_REALM_PUB_KEY_HASH_ALGO_ID,
+				  buf);
+
+	measurement_size = measurement_get_size(algorithm);
+	assert(measurement_size <= MAX_MEASUREMENT_SIZE);
+
+	/* RIM: 0, REM: 1..4 */
+	buf.ptr = &measurements[RIM_MEASUREMENT_SLOT];
+	buf.len = measurement_size;
+	QCBOREncode_AddBytesToMapN(&(ctx->ctx.cbor_enc_ctx),
+				   CCA_REALM_INITIAL_MEASUREMENT,
+				   buf);
+
+	QCBOREncode_OpenArrayInMapN(&(ctx->ctx.cbor_enc_ctx),
+				    CCA_REALM_EXTENSIBLE_MEASUREMENTS);
+
+	for (unsigned int i = 1U; i < num_measurements; ++i) {
+		buf.ptr = &measurements[i];
+		buf.len = measurement_size;
+		QCBOREncode_AddBytes(&(ctx->ctx.cbor_enc_ctx), buf);
+	}
+
+	QCBOREncode_CloseArray(&(ctx->ctx.cbor_enc_ctx));
+	QCBOREncode_CloseMap(&(ctx->ctx.cbor_enc_ctx));
+
+	return ATTEST_TOKEN_ERR_SUCCESS;
+}
diff --git a/lib/attestation/src/attestation_utils.c b/lib/attestation/src/attestation_utils.c
new file mode 100644
index 0000000..31a5189
--- /dev/null
+++ b/lib/attestation/src/attestation_utils.c
@@ -0,0 +1,142 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <assert.h>
+#include <attestation.h>
+#include <attestation_priv.h>
+#include <debug.h>
+#include <errno.h>
+#include <fpu_helpers.h>
+#include <mbedtls/ecp.h>
+#include <mbedtls/memory_buffer_alloc.h>
+#include <memory_alloc.h>
+#include <sizes.h>
+
+/*
+ * Memory buffer for the allocator during key initialization.
+ *
+ * Used to compute the public key and setup a PRNG object per CPU. PRNGs are
+ * needed for key blinding during EC signing.
+ *
+ * Memory requirements:
+ * +------------------------+-------+ ------------------------+
+ * |                        |  MAX  |  Persisting allocation  |
+ * +------------------------+-------+-------------------------+
+ * | Public key computation |  2.4K |         0.4K            |
+ * +------------------------+-------+-------------------------+
+ * |      PRNG setup        |  6.1K |          3.7K           |
+ * +------------------------+-------+-------------------------+
+ *
+ * Measured with:
+ *	src/lib/memory_buffer_alloc.c: mbedtls_memory_buffer_alloc_status()
+ */
+#define INIT_HEAP_PAGES		3
+
+static unsigned char mem_buf[INIT_HEAP_PAGES * SZ_4K]
+					__aligned(sizeof(unsigned long));
+
+static bool attest_initialized;
+
+struct buffer_alloc_ctx init_ctx;
+
+int attestation_init(void)
+{
+	int ret;
+
+	/*
+	 * Associate the allocated heap for mbedtls with the current CPU.
+	 */
+	buffer_alloc_ctx_assign(&init_ctx);
+
+	fpu_save_my_state();
+
+	FPU_ALLOW(mbedtls_memory_buffer_alloc_init(mem_buf, sizeof(mem_buf)));
+
+	/*
+	 * Set the number of max operations per ECC signing iteration
+	 * Check for effective minimum values for
+	 *  - ext/mbedtls/include/mbedtls/ecp.h:493
+	 *
+	 * This adjusts the length of a single signing loop.
+	 */
+	FPU_ALLOW(mbedtls_ecp_set_max_ops(ECP_MAX_OPS));
+
+	FPU_ALLOW(ret = attest_rnd_prng_init());
+	if (ret != 0) {
+		return ret;
+	}
+
+	/* Retrieve the platform key from root world */
+	FPU_ALLOW(ret = attest_init_realm_attestation_key());
+	if (ret != 0) {
+		return ret;
+	}
+
+	fpu_restore_my_state();
+
+	/* Retrieve the platform token from root world */
+	ret = attest_setup_platform_token();
+	if (ret != 0) {
+		return ret;
+	}
+
+	buffer_alloc_ctx_unassign();
+
+	attest_initialized = true;
+
+	return 0;
+}
+
+int attestation_heap_ctx_init(unsigned char *buf, size_t buf_size)
+{
+	assert(buf != NULL);
+
+	if (attest_initialized == false) {
+		return -EINVAL;
+	}
+
+	/* Initialise the mbedTLS heap */
+	fpu_save_my_state();
+	FPU_ALLOW(mbedtls_memory_buffer_alloc_init(buf, buf_size));
+	fpu_restore_my_state();
+
+	return 0;
+}
+
+int attestation_heap_ctx_assign_pe(struct buffer_alloc_ctx *ctx)
+{
+	assert(ctx != NULL);
+
+	if (attest_initialized == false) {
+		return -EINVAL;
+	}
+
+	/*
+	 * Associate the buffer_alloc_ctx to this CPU
+	 */
+	buffer_alloc_ctx_assign(ctx);
+	return 0;
+}
+
+int attestation_heap_ctx_unassign_pe(struct buffer_alloc_ctx *ctx)
+{
+	assert(ctx != NULL);
+
+	if (attest_initialized == false) {
+		return -EINVAL;
+	}
+
+	buffer_alloc_ctx_unassign();
+	return 0;
+}
+
+inline int attestation_heap_reinit_pe(unsigned char *buf, size_t buf_size)
+{
+	fpu_save_my_state();
+	FPU_ALLOW(mbedtls_memory_buffer_alloc_init(buf, buf_size));
+	fpu_restore_my_state();
+
+	return 0;
+}
diff --git a/lib/common/CMakeLists.txt b/lib/common/CMakeLists.txt
new file mode 100644
index 0000000..d4a980b
--- /dev/null
+++ b/lib/common/CMakeLists.txt
@@ -0,0 +1,10 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+add_library(rmm-lib-common INTERFACE)
+
+target_include_directories(rmm-lib-common
+    INTERFACE "include"
+    INTERFACE "include/${RMM_ARCH}")
diff --git a/lib/common/include/aarch64/import_sym.h b/lib/common/include/aarch64/import_sym.h
new file mode 100644
index 0000000..49b84a2
--- /dev/null
+++ b/lib/common/include/aarch64/import_sym.h
@@ -0,0 +1,17 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef IMPORT_SYM_H
+#define IMPORT_SYM_H
+
+/*
+ * Import an assembly or linker symbol as a C expression with the specified
+ * type
+ */
+#define IMPORT_SYM(type, sym, name) \
+	extern unsigned long sym;\
+	static const __attribute__((unused)) type name = (type) (void *)(&sym)
+
+#endif /* IMPORT_SYM_H */
diff --git a/lib/common/include/bitmap.h b/lib/common/include/bitmap.h
new file mode 100644
index 0000000..0b93e25
--- /dev/null
+++ b/lib/common/include/bitmap.h
@@ -0,0 +1,33 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef BITMAP_H
+#define BITMAP_H
+
+#include <sizes.h>
+
+/*
+ * Returns the index of the first bit set in @bitmap from @start inclusive.
+ * The index is zero-based from LSB (0) to MSB (63).
+ * When no such bits are set, returns BITS_PER_UL (64).
+ */
+static inline unsigned long bitmap_find_next_set_bit(unsigned long bitmap,
+						     unsigned long start)
+{
+	if (start < BITS_PER_UL) {
+		bitmap &= ~0UL << start;
+		if (bitmap != 0UL) {
+			return (unsigned long)(__builtin_ffsl(bitmap) - 1);
+		}
+	}
+	return BITS_PER_UL;
+}
+
+#define bitmap_for_each_set_bit(_bit, _bitmap, _max)		\
+	for ((_bit) = find_next_set_bit((_bitmap), 0);		\
+	     (_bit) < (_max);					\
+	     (_bit) = find_next_set_bit((_bitmap), (_bit) + 1))
+
+#endif /* BITMAP_H */
diff --git a/lib/common/include/fake_host/host_harness.h b/lib/common/include/fake_host/host_harness.h
new file mode 100644
index 0000000..f926491
--- /dev/null
+++ b/lib/common/include/fake_host/host_harness.h
@@ -0,0 +1,94 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+#ifndef HOST_HARNESS_H
+#define HOST_HARNESS_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <types.h>
+
+/* Forward declaration of buffer_slot */
+enum buffer_slot;
+
+/* Fake host wrapper to read and write sysregs */
+u_register_t host_read_sysreg(char *reg_name);
+void host_write_sysreg(char *reg_name, u_register_t v);
+
+struct spinlock_s;
+/* Fake host harness to lock and release spin lock */
+void host_spinlock_acquire(struct spinlock_s *l);
+void host_spinlock_release(struct spinlock_s *l);
+
+/*
+ * Fake host Wrapper to copy data from NS into Realm memory. The function
+ * returns true if the copy succeeds. If the access to the NS memory generates
+ * a fault, false is returned to the caller. In case of failure
+ * (when false is returned), partial data may have been written to the
+ * destination buffer.
+ *
+ * Args:
+ *    dest - The address of buffer in Realm memory to write into.
+ *    ns_src - The address of buffer in NS memory to read from.
+ *    size - The number of bytes to copy.
+ *    All arguments must be aligned to 8 bytes.
+ */
+
+bool host_memcpy_ns_read(void *dest, const void *ns_src, unsigned long size);
+
+/*
+ * Fake host wrapper to copy data from Realm into NS memory.The function
+ * returns true if the copy succeeds. If the access to the NS memory generates
+ * a fault, false is returned to the caller. In case of failure (when false is
+ * returned), partial data may have been written to the destination buffer.
+ *
+ * Args:
+ *    ns_dest - The address of buffer in NS memory to write into.
+ *    src - The address of buffer in Realm memory to read from.
+ *    size - The number of bytes to copy.
+ *    All arguments must be aligned to 8 bytes.
+ */
+bool host_memcpy_ns_write(void *ns_dest, const void *src, unsigned long size);
+
+/*
+ * Fake host wrapper to run a realm.
+ * Args:
+ *     regs - pointer to GP regs to be restored/save when entering/exiting
+ *            Realm
+ * Return: Realm exception syndrome return.
+ */
+int host_run_realm(unsigned long *regs);
+
+/*
+ * Fake Host wrapper for monitor_call.
+ */
+unsigned long host_monitor_call(unsigned long id, unsigned long arg0,
+		unsigned long arg1, unsigned long arg2, unsigned long arg3,
+		unsigned long arg4, unsigned long arg5);
+
+struct smc_result;
+/*
+ * Fake Host wrapper for monitor_call_with_res.
+ */
+void host_monitor_call_with_res(unsigned long id, unsigned long arg0,
+		unsigned long arg1, unsigned long arg2, unsigned long arg3,
+		unsigned long arg4, unsigned long arg5,
+		struct smc_result *res);
+
+/*
+ * Fake host wrapper to map a given PA.
+ *
+ * It returns the VA to which the buffer is mapped.
+ */
+void *host_buffer_arch_map(enum buffer_slot slot,
+		      unsigned long addr, bool ns);
+
+/*
+ * Fake host wrapper to unmap a buffer slot correspondig to the VA passed
+ * in `buf`.
+ */
+void host_buffer_arch_unmap(void *buf);
+
+#endif /* HOST_HARNESS_H */
diff --git a/lib/common/include/fake_host/import_sym.h b/lib/common/include/fake_host/import_sym.h
new file mode 100644
index 0000000..6ff5a2d
--- /dev/null
+++ b/lib/common/include/fake_host/import_sym.h
@@ -0,0 +1,27 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef IMPORT_SYM_H
+#define IMPORT_SYM_H
+
+/*
+ * Define dummy symbols for fake_host build.
+ * These must be defined as constant qualifiers for IMPORT_SYM to work.
+ */
+#define rmm_text_start	0x10000000UL
+#define rmm_text_end	0x20000000UL
+#define rmm_ro_start	0x20000000UL
+#define rmm_ro_end	0x40000000UL
+#define rmm_rw_start	0x40000000UL
+#define rmm_rw_end	0x50000000UL
+
+/*
+ * Emulates the import of an assembly or linker symbol as a C expression
+ * with the specified type.
+ */
+#define IMPORT_SYM(type, sym, name) \
+	static const __attribute__((unused)) type name = (type)(sym)
+
+#endif /* IMPORT_SYM_H */
diff --git a/lib/common/include/platform_api.h b/lib/common/include/platform_api.h
new file mode 100644
index 0000000..6b26ac1
--- /dev/null
+++ b/lib/common/include/platform_api.h
@@ -0,0 +1,29 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+#ifndef PLATFORM_API_H
+#define PLATFORM_API_H
+
+#include <stdint.h>
+
+void plat_warmboot_setup(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3);
+void plat_setup(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3);
+
+/*
+ * Takes an aligned granule address, validates it and if valid returns the
+ * index in the struct granules array or UINT64_MAX in case of an error.
+ *
+ * This function also validates that the granule address is a valid
+ * page address.
+ */
+unsigned long plat_granule_addr_to_idx(unsigned long addr);
+
+/*
+ * Takes an index in the struct granules array and returns the aligned granule
+ * address. The index must be within the number of granules expected by the
+ * platform.
+ */
+unsigned long plat_granule_idx_to_addr(unsigned long idx);
+
+#endif /* PLATFORM_API_H */
diff --git a/lib/common/include/sizes.h b/lib/common/include/sizes.h
new file mode 100644
index 0000000..f1b4f36
--- /dev/null
+++ b/lib/common/include/sizes.h
@@ -0,0 +1,26 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef SIZES_H
+#define SIZES_H
+
+#include <utils_def.h>
+
+#define SZ_1K	(UL(1) << 10)
+#define SZ_1M	(UL(1) << 20)
+#define SZ_1G	(UL(1) << 30)
+
+#define SZ_4K	(UL(4)  * SZ_1K)
+#define SZ_16K	(UL(16) * SZ_1K)
+#define SZ_64K	(UL(64) * SZ_1K)
+
+#define SZ_2G	(UL(2)  * SZ_1G)
+#define SZ_2M	(UL(2)  * SZ_1M)
+
+#ifndef __ASSEMBLER__
+#define BITS_PER_UL	(8 * sizeof(unsigned long))
+#endif
+
+#endif /* SIZES_H */
diff --git a/lib/common/include/types.h b/lib/common/include/types.h
new file mode 100644
index 0000000..0a0e6c7
--- /dev/null
+++ b/lib/common/include/types.h
@@ -0,0 +1,14 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef TYPES_H
+#define TYPES_H
+
+#include <stdint.h>
+
+typedef uint64_t u_register_t;
+typedef int64_t register_t;
+
+#endif /* TYPES_H */
diff --git a/lib/common/include/utils_def.h b/lib/common/include/utils_def.h
new file mode 100644
index 0000000..2dc310a
--- /dev/null
+++ b/lib/common/include/utils_def.h
@@ -0,0 +1,226 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ * SPDX-FileCopyrightText: Copyright NVIDIA Corporation.
+ */
+
+#ifndef UTILS_DEF_H
+#define UTILS_DEF_H
+
+#if !(defined(__ASSEMBLER__) || defined(__LINKER__))
+#include <stdint.h>
+#endif
+
+/*
+ * For those constants to be shared between C and other sources, apply a 'U',
+ * 'UL', 'ULL', 'L' or 'LL' suffix to the argument only in C, to avoid
+ * undefined or unintended behaviour.
+ *
+ * The GNU assembler and linker do not support these suffixes (it causes the
+ * build process to fail) therefore the suffix is omitted when used in linker
+ * scripts and assembler files.
+ */
+#if defined(__ASSEMBLER__) || defined(__LINKER__)
+# define   U(_x)	(_x)
+# define  UL(_x)	(_x)
+# define ULL(_x)	(_x)
+# define   L(_x)	(_x)
+# define  LL(_x)	(_x)
+#else
+# define  U_(_x)	(_x##U)
+# define   U(_x)	U_(_x)
+# define  UL(_x)	(_x##UL)
+# define ULL(_x)	(_x##ULL)
+# define   L(_x)	(_x##L)
+# define  LL(_x)	(_x##LL)
+#endif /* __ASSEMBLER__ */
+
+/* Short forms for commonly used attributes */
+#define __dead2		__attribute__((__noreturn__))
+#define __deprecated	__attribute__((__deprecated__))
+#define __packed	__attribute__((__packed__))
+#define __used		__attribute__((__used__))
+#define __unused	__attribute__((__unused__))
+#define __aligned(x)	__attribute__((__aligned__(x)))
+#define __section(x)	__attribute__((__section__(x)))
+
+#define __printflike(fmtarg, firstvararg) \
+		__attribute__((__format__ (__printf__, fmtarg, firstvararg)))
+
+/*
+ * The round_up() macro rounds up a value to the given boundary in a
+ * type-agnostic yet type-safe manner. The boundary must be a power of two.
+ * In other words, it computes the smallest multiple of boundary which is
+ * greater than or equal to value.
+ *
+ * round_down() is similar but rounds the value down instead.
+ */
+#define round_boundary(value, boundary)		\
+	((__typeof__(value))((boundary) - 1))
+
+#define round_up(value, boundary)		\
+	((((value) - 1) | round_boundary(value, boundary)) + 1)
+
+#define round_down(value, boundary)		\
+	((value) & ~round_boundary(value, boundary))
+
+/* Compute the number of elements in the given array */
+#define ARRAY_SIZE(a)	\
+	(sizeof(a) / sizeof((a)[0]))
+
+#define ARRAY_LEN(_a)	\
+	((sizeof(_a) / sizeof(_a[0])) + CHECK_TYPE_IS_ARRAY(_a))
+
+/*
+ * Macro checks types of array and variable/value to write
+ * and reports compilation error if they mismatch.
+ */
+#define	CHECK_ARRAY_TYPE(_a, _v)	\
+	_Static_assert(__builtin_types_compatible_p(typeof(_a[0]), typeof(_v)), \
+	"array type mismatch")
+
+/*
+ * Array read/write macros with boundary and types checks
+ * _a: name of array
+ * _i: index
+ * _v: variable/value to write
+ */
+#define	ARRAY_READ(_a, _i, _v)		\
+({					\
+	CHECK_ARRAY_TYPE(_a, _v);	\
+	if (_i >= ARRAY_SIZE(_a)) {	\
+		panic();		\
+	}				\
+	_v = _a[_i];			\
+})
+
+#define	ARRAY_WRITE(_a, _i, _v)		\
+({					\
+	CHECK_ARRAY_TYPE(_a, _v);	\
+	if (_i >= ARRAY_SIZE(_a)) {	\
+		panic();		\
+	}				\
+	_a[_i] = _v;			\
+})
+
+#define COMPILER_ASSERT(_condition) extern char compiler_assert[(_condition) ? 1 : -1]
+
+/*
+ * If _expr is false, this will result in a compile time error as it tries to
+ * define a bitfield of size -1 in that case.  Otherwise, it will define a
+ * bitfield of size 0, which is valid, and not create a compiler warning.
+ *
+ * The return value is only relevant when the compilation succeeds, and by
+ * subtracting the size of the same struct, this should always return 0 as a
+ * value and can be included in other expressions.
+ */
+#define COMPILER_ASSERT_ZERO(_expr) (sizeof(struct { char: (-!(_expr)); }) \
+				- sizeof(struct { char: 0; }))
+
+#define CHECK_TYPE_IS_ARRAY(_v) \
+	COMPILER_ASSERT_ZERO(!__builtin_types_compatible_p(typeof(_v), typeof(&(_v[0]))))
+
+#define IS_POWER_OF_TWO(x)			\
+	((((x) + UL(0)) & ((x) - UL(1))) == UL(0))
+
+#define COMPILER_BARRIER() __asm__ volatile ("" ::: "memory")
+
+#define ALIGNED(_size, _alignment) (((unsigned long)(_size) % (_alignment)) == UL(0))
+
+#define GRANULE_ALIGNED(_addr) ALIGNED((void *)(_addr), GRANULE_SIZE)
+#define GRANULE_SHIFT	(UL(12))
+#define GRANULE_MASK	(~0xfffUL)
+
+#define HAS_MPAM 0
+
+#if HAS_MPAM
+#define MPAM(_x...) _x
+#else
+#define MPAM(_x...)
+#endif
+
+#define HAS_SPE 0
+
+#if HAS_SPE
+#define SPE(_x...) _x
+#else
+#define SPE(_x...)
+#endif
+
+#if !(defined(__ASSEMBLER__) || defined(__LINKER__))
+
+/*
+ * System register field definitions.
+ *
+ * For any register field we define:
+ * - <register>_<field>_SHIFT
+ *   The bit offset of the LSB of the field.
+ * - <register>_<field>_WIDTH
+ *   The width of the field in bits.
+ *
+ * For single bit fields, we define:
+ * - <register>_<field>_BIT
+ *   The in-place value of the field with the bit set.
+ *
+ * For multi-bit fields, we define:
+ * - <register>_<field>_<enum>
+ *   The in-place value of the field set to the value corresponding to the
+ *   enumeration name.
+ *
+ * For any register field, we define:
+ * - INPLACE(<register>_<field>, val)
+ *   The in-place value of the field set to val, handling any necessary type
+ *   promotion to avoid truncation of val.
+ * - MASK(<register>_<field)
+ *   An in-place bitmask covering all bits of the field.
+ * - EXTRACT(<register_field> <register_value>)
+ *   A macro to extract the value of a register field shifted down so the
+ *   value can be evaluated directly.
+ * - EXTRACT_BIT(<register_field> <register_value>)
+ *   A macro to extract the value of a register bit shifted down so the
+ *   value can be evaluated directly.
+ */
+#define INPLACE(regfield, val) \
+	(((val) + UL(0)) << (regfield##_SHIFT))
+
+#define MASK(regfield) \
+	((~0UL >> (64UL - (regfield##_WIDTH))) << (regfield##_SHIFT))
+
+#define EXTRACT(regfield, reg) \
+	(((reg) & MASK(regfield)) >> (regfield##_SHIFT))
+
+#define EXTRACT_BIT(regfield, reg) \
+	(((reg) >> (regfield##_SHIFT)) & UL(1))
+
+/*
+ * Generates an unsigned long long (64-bit) value where the bits @_msb
+ * through @_lsb (inclusive) are set to one and all other bits are zero.  The
+ * parameters can hold values from 0 through 63 and if _msb == _lsb a single bit
+ * is set at that location.
+ */
+#define BIT_MASK_ULL(_msb, _lsb) \
+	((~ULL(0) >> (63UL - (_msb))) & (~ULL(0) << (_lsb)))
+
+/*
+ * Stringify the result of expansion of a macro argument
+ */
+#ifndef __XSTRING
+#define __STRING(x)	#x
+#define __XSTRING(x)	__STRING(x)
+#endif
+
+/*
+ * Defines member of structure and reserves space
+ * for the next member with specified offset.
+ */
+#define SET_MEMBER(member, start, end)	\
+	union {				\
+		member;			\
+		unsigned char reserved##end[end - start]; \
+	}
+
+#define	FALLTHROUGH	__attribute__((fallthrough))
+
+#endif /* !(defined(__ASSEMBLER__) || defined(__LINKER__)) */
+
+#endif /* UTILS_DEF_H */
diff --git a/lib/debug/CMakeLists.txt b/lib/debug/CMakeLists.txt
new file mode 100644
index 0000000..0b0ed4c
--- /dev/null
+++ b/lib/debug/CMakeLists.txt
@@ -0,0 +1,18 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+add_library(rmm-lib-debug INTERFACE)
+
+arm_config_option(
+    NAME LOG_LEVEL
+    HELP "Log level to apply for RMM (0 - 50)"
+    TYPE STRING
+    DEFAULT 40)
+
+target_compile_definitions(rmm-lib-debug
+    INTERFACE "LOG_LEVEL=${LOG_LEVEL}")
+
+target_include_directories(rmm-lib-debug
+    INTERFACE "include")
diff --git a/lib/debug/include/debug.h b/lib/debug/include/debug.h
new file mode 100644
index 0000000..64e87d3
--- /dev/null
+++ b/lib/debug/include/debug.h
@@ -0,0 +1,100 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef DEBUG_H
+#define DEBUG_H
+
+#ifndef __ASSEMBLER__
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#endif
+#include <utils_def.h>
+
+/*
+ * The log output macros print output to the console. These macros produce
+ * compiled log output only if the LOG_LEVEL defined in the makefile (or the
+ * make command line) is greater or equal than the level required for that
+ * type of log output.
+ *
+ * The format expected is the same as for printf(). For example:
+ * INFO("Info %s.\n", "message")    -> INFO:    Info message.
+ * WARN("Warning %s.\n", "message") -> WARNING: Warning message.
+ */
+
+#define LOG_LEVEL_NONE			U(0)
+#define LOG_LEVEL_ERROR			U(10)
+#define LOG_LEVEL_NOTICE		U(20)
+#define LOG_LEVEL_WARNING		U(30)
+#define LOG_LEVEL_INFO			U(40)
+#define LOG_LEVEL_VERBOSE		U(50)
+
+#ifndef LOG_LEVEL
+#define LOG_LEVEL	LOG_LEVEL_VERBOSE
+#endif
+
+#ifndef __ASSEMBLER__
+/*
+ * If the log output is too low then this macro is used in place of rmm_log()
+ * below. The intent is to get the compiler to evaluate the function call for
+ * type checking and format specifier correctness but let it optimize it out.
+ */
+#define no_rmm_log(fmt, ...)				\
+	do {						\
+		if (false) {				\
+			rmm_log(fmt, ##__VA_ARGS__);	\
+		}					\
+	} while (false)
+
+#if LOG_LEVEL >= LOG_LEVEL_ERROR
+# define ERROR(...)	rmm_log(__VA_ARGS__)
+#else
+# define ERROR(...)	no_rmm_log(__VA_ARGS__)
+#endif
+
+#if LOG_LEVEL >= LOG_LEVEL_NOTICE
+# define NOTICE(...)	rmm_log(__VA_ARGS__)
+#else
+# define NOTICE(...)	no_rmm_log(__VA_ARGS__)
+#endif
+
+#if LOG_LEVEL >= LOG_LEVEL_WARNING
+# define WARN(...)	rmm_log(__VA_ARGS__)
+#else
+# define WARN(...)	no_rmm_log(__VA_ARGS__)
+#endif
+
+#if LOG_LEVEL >= LOG_LEVEL_INFO
+# define INFO(...)	rmm_log(__VA_ARGS__)
+#else
+# define INFO(...)	no_rmm_log(__VA_ARGS__)
+#endif
+
+#if LOG_LEVEL >= LOG_LEVEL_VERBOSE
+# define VERBOSE(...)	rmm_log(__VA_ARGS__)
+#else
+# define VERBOSE(...)	no_rmm_log(__VA_ARGS__)
+#endif
+
+/*
+ * FIXME: Fully implement panic() handlers once it is decided how to panic.
+ */
+
+#define panic()				\
+	do {				\
+	} while (true)
+
+__attribute__((__format__(__printf__, 1, 2)))
+static inline void rmm_log(const char *fmt, ...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+	(void)vprintf(fmt, args);
+	va_end(args);
+}
+
+#endif /* __ASSEMBLER__ */
+#endif /* DEBUG_H */
diff --git a/lib/gic/CMakeLists.txt b/lib/gic/CMakeLists.txt
new file mode 100644
index 0000000..af0f3fa
--- /dev/null
+++ b/lib/gic/CMakeLists.txt
@@ -0,0 +1,18 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+add_library(rmm-lib-gic)
+
+target_link_libraries(rmm-lib-gic
+    PRIVATE rmm-lib-arch
+            rmm-lib-common
+            rmm-lib-debug
+            rmm-lib-realm)
+
+target_include_directories(rmm-lib-gic
+    PUBLIC "include")
+
+target_sources(rmm-lib-gic
+    PRIVATE "src/gic.c")
diff --git a/lib/gic/include/gic.h b/lib/gic/include/gic.h
new file mode 100644
index 0000000..d515edb
--- /dev/null
+++ b/lib/gic/include/gic.h
@@ -0,0 +1,244 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef GIC_H
+#define GIC_H
+
+#include <stdbool.h>
+#include <utils_def.h>
+
+/* The number of implemented List registers, minus one */
+#define ICH_VTR_EL2_LIST_REGS_SHIFT	UL(0)
+#define ICH_VTR_EL2_LIST_REGS_WIDTH	UL(5)
+#define ICH_VTR_EL2_LIST_REGS_MASK	MASK(ICH_VTR_EL2_LIST_REGS)
+
+/* The number of virtual interrupt identifier bits supported */
+#define ICH_VTR_EL2_ID_BITS_SHIFT	UL(23)
+#define ICH_VTR_EL2_ID_BITS_WIDTH	UL(3)
+#define ICH_VTR_EL2_ID_BITS_MASK	MASK(ICH_VTR_EL2_ID_BITS)
+
+/* The number of virtual preemption bits implemented, minus one */
+#define ICH_VTR_EL2_PRE_BITS_SHIFT	UL(26)
+#define ICH_VTR_EL2_PRE_BITS_WIDTH	UL(3)
+#define ICH_VTR_EL2_PRE_BITS_MASK	MASK(ICH_VTR_EL2_PRE_BITS)
+
+/* The number of virtual priority bits implemented, minus one */
+#define ICH_VTR_EL2_PRI_BITS_SHIFT	UL(29)
+#define ICH_VTR_EL2_PRI_BITS_WIDTH	UL(3)
+#define ICH_VTR_EL2_PRI_BITS_MASK	MASK(ICH_VTR_EL2_PRI_BITS)
+
+/* Global enable bit for the virtual CPU interface */
+#define ICH_HCR_EL2_EN_SHIFT		UL(0)
+#define ICH_HCR_EL2_EN_BIT		INPLACE(ICH_HCR_EL2_EN, UL(1))
+
+/* Underflow Interrupt Enable */
+#define ICH_HCR_EL2_UIE_SHIFT		UL(1)
+#define ICH_HCR_EL2_UIE_BIT		INPLACE(ICH_HCR_EL2_UIE, UL(1))
+
+/* List Register Entry Not Present Interrupt Enable */
+#define ICH_HCR_EL2_LRENPIE_SHIFT	UL(2)
+#define ICH_HCR_EL2_LRENPIE_BIT		INPLACE(ICH_HCR_EL2_LRENPIE, UL(1))
+
+/* No Pending Interrupt Enable */
+#define ICH_HCR_EL2_NPIE_SHIFT		UL(3)
+#define ICH_HCR_EL2_NPIE_BIT		INPLACE(ICH_HCR_EL2_NPIE, UL(1))
+
+/* VM Group 0 Enabled Interrupt Enable */
+#define ICH_HCR_EL2_VGRP0EIE_SHIFT	UL(4)
+#define ICH_HCR_EL2_VGRP0EIE_BIT	INPLACE(ICH_HCR_EL2_VGRP0EIE, UL(1))
+
+/* VM Group 0 Disabled Interrupt Enable */
+#define ICH_HCR_EL2_VGRP0DIE_SHIFT	UL(5)
+#define ICH_HCR_EL2_VGRP0DIE_BIT	INPLACE(ICH_HCR_EL2_VGRP0DIE, UL(1))
+
+/* VM Group 1 Enabled Interrupt Enable */
+#define ICH_HCR_EL2_VGRP1EIE_SHIFT	UL(6)
+#define ICH_HCR_EL2_VGRP1EIE_BIT	INPLACE(ICH_HCR_EL2_VGRP1EIE, UL(1))
+
+/* VM Group 1 Disabled Interrupt Enable */
+#define ICH_HCR_EL2_VGRP1DIE_SHIFT	UL(7)
+#define ICH_HCR_EL2_VGRP1DIE_BIT	INPLACE(ICH_HCR_EL2_VGRP1DIE, UL(1))
+
+/*
+ * When FEAT_GICv4p1 is implemented:
+ * Controls whether deactivation of virtual SGIs
+ * can increment ICH_HCR_EL2.EOIcount
+ */
+#define ICH_HCR_EL2_VSGIEEOICOUNT_SHIFT	UL(8)
+#define ICH_HCR_EL2_VSGIEEOICOUNT_BIT	INPLACE(ICH_HCR_EL2_VSGIEEOICOUNT, UL(1))
+
+/*
+ * Trap all EL1 accesses to System registers
+ * that are common to Group 0 and Group 1 to EL2
+ */
+#define ICH_HCR_EL2_TC_SHIFT		UL(10)
+#define ICH_HCR_EL2_TC_BIT		INPLACE(ICH_HCR_EL2_TC, UL(1))
+
+/*
+ * Trap all EL1 accesses to ICC_* and ICV_* System registers
+ * for Group 0 interrupts to EL2
+ */
+#define ICH_HCR_EL2_TALL0_SHIFT		UL(11)
+#define ICH_HCR_EL2_TALL0_BIT		INPLACE(ICH_HCR_EL2_TALL0, UL(1))
+
+/*
+ * Trap all EL1 accesses to ICC_* and ICV_* System registers
+ * for Group 1 interrupts to EL2
+ */
+#define ICH_HCR_EL2_TALL1_SHIFT		UL(12)
+#define ICH_HCR_EL2_TALL1_BIT		INPLACE(ICH_HCR_EL2_TALL1, UL(1))
+
+/* Trap all locally generated SEIs */
+#define ICH_HCR_EL2_TSEI_SHIFT		UL(13)
+#define ICH_HCR_EL2_TSEI_BIT		INPLACE(ICH_HCR_EL2_TSEI, UL(1))
+
+/*
+ * When FEAT_GICv3_TDIR is implemented:
+ * Trap EL1 writes to ICC_DIR_EL1 and ICV_DIR_EL1
+ */
+#define ICH_HCR_EL2_TDIR_SHIFT		UL(14)
+#define ICH_HCR_EL2_TDIR_BIT		INPLACE(ICH_HCR_EL2_TDIR, UL(1))
+
+/*
+ * When ICH_VTR_EL2.DVIM == 1:
+ * Directly-injected Virtual Interrupt Mask
+ */
+#define ICH_HCR_EL2_DVIM_SHIFT		UL(15)
+#define ICH_HCR_EL2_DVIM_BIT		INPLACE(ICH_HCR_EL2_DVIM, UL(1))
+
+#define ICH_HCR_EL2_EOI_COUNT_SHIFT	UL(27)
+#define ICH_HCR_EL2_EOI_COUNT_WIDTH	UL(5)
+#define ICH_HCR_EL2_EOI_COUNT_MASK	MASK(ICH_HCR_EL2_EOI_COUNT)
+
+/* Virtual INTID of the interrupt */
+#define ICH_LR_VINTID_SHIFT		UL(0)
+#define ICH_LR_VINTID_WIDTH		UL(32)
+#define ICH_LR_VINTID_MASK		MASK(ICH_LR_VINTID)
+
+/*
+ * Physical INTID, for hardware interrupts
+ * When ICH_LR<n>_EL2.HW is 0 (there is no corresponding physical interrupt),
+ * this field has the following meaning:
+ *	Bits[44:42] : RES0.
+ *	Bit[41] : EOI. If this bit is 1, then when the interrupt identified by
+ *		vINTID is deactivated, a maintenance interrupt is asserted.
+ *	Bits[40:32] : RES0
+ */
+#define ICH_LR_PINTID_SHIFT		UL(32)
+#define ICH_LR_PINTID_WIDTH		UL(13)
+#define ICH_LR_PINTID_MASK		MASK(ICH_LR_PINTID)
+
+#define ICH_LR_EOI_SHIFT		UL(41)
+#define ICH_LR_EOI_BIT			INPLACE(ICH_LR_EOI, UL(1))
+
+/* The priority of this interrupt */
+#define ICH_LR_PRIORITY_SHIFT		UL(48)
+#define ICH_LR_PRIORITY_WIDTH		UL(8)
+#define ICH_LR_PRIORITY_MASK		MASK(ICH_LR_PRIORITY)
+
+/* The group for this virtual interrupt */
+#define ICH_LR_GROUP_SHIFT		UL(60)
+#define ICH_LR_GROUP_BIT		INPLACE(ICH_LR_GROUP, UL(1))
+
+/*
+ * Indicates whether this virtual interrupt
+ * maps directly to a hardware interrupt
+ */
+#define ICH_LR_HW_SHIFT			UL(61)
+#define ICH_LR_HW_BIT			INPLACE(ICH_LR_HW, UL(1))
+
+/*
+ * The state of the interrupt:
+ * 0b00	Invalid (Inactive)
+ * 0b01	Pending
+ * 0b10	Active
+ * 0b11	Pending and active
+ */
+#define ICH_LR_STATE_SHIFT		UL(62)
+#define ICH_LR_STATE_WIDTH		UL(2)
+#define ICH_LR_STATE_MASK		MASK(ICH_LR_STATE)
+
+#define ICH_LR_STATE_INVALID		INPLACE(ICH_LR_STATE, UL(0))
+#define ICH_LR_STATE_PENDING		INPLACE(ICH_LR_STATE, UL(1))
+#define ICH_LR_STATE_ACTIVE		INPLACE(ICH_LR_STATE, UL(2))
+#define ICH_LR_STATE_PENDING_ACTIVE	INPLACE(ICH_LR_STATE, UL(3))
+
+/*
+ * A `_ns` mask defines bits that can be set/cleared by the NS hypervisor.
+ */
+
+#define ICH_HCR_EL2_NS_MASK			  \
+		(ICH_HCR_EL2_UIE_BIT		| \
+		 ICH_HCR_EL2_LRENPIE_BIT	| \
+		 ICH_HCR_EL2_NPIE_BIT		| \
+		 ICH_HCR_EL2_VGRP0EIE_BIT	| \
+		 ICH_HCR_EL2_VGRP0DIE_BIT	| \
+		 ICH_HCR_EL2_VGRP1EIE_BIT	| \
+		 ICH_HCR_EL2_VGRP1DIE_BIT	| \
+		 ICH_HCR_EL2_TDIR_BIT)
+/*
+ * Maximum number of Interrupt Controller
+ * Hyp Active Priorities Group 0/1 Registers [0..3]
+ */
+#define ICH_MAX_APRS		4
+
+/* Maximum number of Interrupt Controller List Registers */
+#define ICH_MAX_LRS		16
+
+/*******************************************************************************
+ * GICv3 and 3.1 definitions
+ ******************************************************************************/
+#define MIN_SGI_ID		U(0)
+#define MIN_SEC_SGI_ID		U(8)
+
+/* PPIs INTIDs 16-31 */
+#define MIN_PPI_ID		U(16)
+#define MAX_PPI_ID		U(31)
+
+/* SPIs INTIDs 32-1019 */
+#define MIN_SPI_ID		U(32)
+#define MAX_SPI_ID		U(1019)
+
+/* GICv3.1 extended PPIs INTIDs 1056-1119 */
+#define MIN_EPPI_ID		U(1056)
+#define MAX_EPPI_ID		U(1119)
+
+/* GICv3.1 extended SPIs INTIDs 4096 - 5119 */
+#define MIN_ESPI_ID		U(4096)
+#define MAX_ESPI_ID		U(5119)
+
+/* Constant to categorize LPI interrupt */
+#define MIN_LPI_ID		U(8192)
+
+struct gic_cpu_state {
+	/* Interrupt Controller Hyp Active Priorities Group 0 Registers */
+	unsigned long ich_ap0r_el2[ICH_MAX_APRS];
+	/* Interrupt Controller Hyp Active Priorities Group 1 Registers */
+	unsigned long ich_ap1r_el2[ICH_MAX_APRS];
+	/* GICv3 Virtual Machine Control Register */
+	unsigned long ich_vmcr_el2;		/* RecRun out */
+	/* Interrupt Controller Hyp Control Register */
+	unsigned long ich_hcr_el2;		/* RecRun in/out */
+
+	/* GICv3 List Registers */
+	unsigned long ich_lr_el2[ICH_MAX_LRS];	/* RecRun in/out */
+	/* GICv3 Maintenance Interrupt State Register */
+	unsigned long ich_misr_el2;		/* RecRun out */
+};
+
+struct rmi_rec_entry;
+struct rmi_rec_exit;
+
+void gic_get_virt_features(void);
+void gic_cpu_state_init(struct gic_cpu_state *gicstate);
+void gic_copy_state_from_ns(struct gic_cpu_state *gicstate,
+			    struct rmi_rec_entry *rec_entry);
+void gic_copy_state_to_ns(struct gic_cpu_state *gicstate,
+			  struct rmi_rec_exit *rec_exit);
+bool gic_validate_state(struct gic_cpu_state *gicstate);
+void gic_restore_state(struct gic_cpu_state *gicstate);
+void gic_save_state(struct gic_cpu_state *gicstate);
+
+#endif /* GIC_H */
diff --git a/lib/gic/src/gic.c b/lib/gic/src/gic.c
new file mode 100644
index 0000000..6215bea
--- /dev/null
+++ b/lib/gic/src/gic.c
@@ -0,0 +1,317 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <debug.h>
+#include <gic.h>
+#include <smc-rmi.h>
+#include <stdbool.h>
+#include <string.h>
+
+/* The macros below fall through to case (n - 1) */
+#define READ_ICH_LR_EL2(n)	{				\
+	case n:							\
+	gicstate->ich_lr_el2[n] = read_ich_lr##n##_el2();	\
+	}
+
+#define WRITE_ICH_LR_EL2(n)	{			\
+	case n:						\
+	write_ich_lr##n##_el2(gicstate->ich_lr_el2[n]);	\
+	}
+
+#define READ_ICH_APR_EL2(n)	{				\
+	case n:							\
+	gicstate->ich_ap0r_el2[n] = read_ich_ap0r##n##_el2();	\
+	gicstate->ich_ap1r_el2[n] = read_ich_ap1r##n##_el2();	\
+	}
+
+#define WRITE_ICH_APR_EL2(n)	{				\
+	case n:							\
+	write_ich_ap0r##n##_el2(gicstate->ich_ap0r_el2[n]);	\
+	write_ich_ap1r##n##_el2(gicstate->ich_ap1r_el2[n]);	\
+	}
+
+/* GIC virtualization features */
+struct gic_virt_feature_s {
+
+	/* Number of implemented List registers, minus 1 */
+	unsigned int nr_lrs;
+
+	/*
+	 * Number of Interrupt Controller Hyp Active
+	 * Priorities Group 0/1 Registers [0..3]
+	 */
+	unsigned int nr_aprs;
+
+	/* RES0 bits in the Priority field in the LRs */
+	unsigned long pri_res0_mask;
+
+	/* Max virtual interrupt identifier */
+	unsigned long max_vintid;
+
+	/* Support for extended INTID */
+	bool ext_range;
+};
+
+static struct gic_virt_feature_s gic_virt_feature;
+
+/*
+ * Read supported GIC virtualization features
+ * and set configuration variables.
+ */
+void gic_get_virt_features(void)
+{
+	/* Interrupt Controller VGIC Type Register */
+	unsigned long vtr = read_ich_vtr_el2();
+
+	unsigned long nr_pre_bits;
+	unsigned long nr_pri_bits;
+
+	/* Number of implemented List registers, minus 1 */
+	gic_virt_feature.nr_lrs = EXTRACT(ICH_VTR_EL2_LIST_REGS, vtr);
+	assert(gic_virt_feature.nr_lrs < ICH_MAX_LRS);
+
+	/* Number of virtual preemption bits implemented */
+	nr_pre_bits = EXTRACT(ICH_VTR_EL2_PRE_BITS, vtr) + 1U;
+
+	/*
+	 * Implementation must implement at least 32 levels
+	 * of virtual priority (5 priority bits)
+	 */
+	assert(nr_pre_bits >= 5UL);
+
+	/*
+	 * Number of Interrupt Controller Hyp Active Priorities
+	 * Group 0/1 Registers [0..3], minus 1
+	 */
+	gic_virt_feature.nr_aprs = (1UL << (nr_pre_bits - 5UL)) - 1UL;
+
+	/*
+	 * Get max virtual interrupt identifier
+	 * Number of virtual interrupt identifier bits supported:
+	 * 0b000 : 16 bits
+	 * 0b001 : 24 bits
+	 */
+	gic_virt_feature.max_vintid =
+				(EXTRACT(ICH_VTR_EL2_ID_BITS, vtr) == 0UL) ?
+				((1UL << 16U) - 1UL) : ((1UL << 24U) - 1UL);
+
+	/* Number of virtual priority bits implemented */
+	nr_pri_bits = EXTRACT(ICH_VTR_EL2_PRI_BITS, vtr) + 1UL;
+
+	/* RES0 bits in the Priority field in the LRs */
+	gic_virt_feature.pri_res0_mask =
+			(1UL << (ICH_LR_PRIORITY_WIDTH - nr_pri_bits)) - 1UL;
+
+	/* Support for extended INTID */
+	gic_virt_feature.ext_range = (read_icc_ctrl_el1() &
+					ICC_CTLR_EL1_EXT_RANGE_BIT) != 0UL;
+	VERBOSE("GIC with%s ExtRange:\n",
+		gic_virt_feature.ext_range ? "" : "out");
+	VERBOSE(" nr_lrs=%u nr_aprs=%u max_vintid=%lu\n",
+		gic_virt_feature.nr_lrs, gic_virt_feature.nr_aprs,
+		gic_virt_feature.max_vintid);
+	VERBOSE(" nr_pri_bits=%lu pri_res0_mask=0x%lx\n",
+		nr_pri_bits, gic_virt_feature.pri_res0_mask);
+}
+
+void gic_cpu_state_init(struct gic_cpu_state *gicstate)
+{
+	(void)memset(gicstate, 0, sizeof(*gicstate));
+	gicstate->ich_hcr_el2 =
+		ICH_HCR_EL2_EN_BIT |	/* Enable virtual CPU interface */
+		ICH_HCR_EL2_VSGIEEOICOUNT_BIT | /* Virtual SGIs not supported */
+		ICH_HCR_EL2_DVIM_BIT;	/* Direct-injection not supported */
+}
+
+void gic_copy_state_from_ns(struct gic_cpu_state *gicstate,
+			    struct rmi_rec_entry *rec_entry)
+{
+	unsigned int i;
+
+	/* Copy List Registers */
+	for (i = 0U; i <= gic_virt_feature.nr_lrs; i++) {
+		gicstate->ich_lr_el2[i] = rec_entry->gicv3_lrs[i];
+	}
+
+	/* Get bits from NS hypervisor */
+	gicstate->ich_hcr_el2 &= ~ICH_HCR_EL2_NS_MASK;
+	gicstate->ich_hcr_el2 |= rec_entry->gicv3_hcr & ICH_HCR_EL2_NS_MASK;
+}
+
+void gic_copy_state_to_ns(struct gic_cpu_state *gicstate,
+			  struct rmi_rec_exit *rec_exit)
+{
+	unsigned int i;
+
+	/* Copy List Registers */
+	for (i = 0U; i <= gic_virt_feature.nr_lrs; i++) {
+		rec_exit->gicv3_lrs[i] = gicstate->ich_lr_el2[i];
+	}
+
+	rec_exit->gicv3_misr = gicstate->ich_misr_el2;
+	rec_exit->gicv3_vmcr = gicstate->ich_vmcr_el2;
+	rec_exit->gicv3_hcr = gicstate->ich_hcr_el2 &
+		(ICH_HCR_EL2_EOI_COUNT_MASK | ICH_HCR_EL2_NS_MASK);
+}
+
+static bool is_valid_vintid(unsigned long intid)
+{
+	/* Check for INTID [0..1019] and [8192..] */
+	if (((intid) <= MAX_SPI_ID) ||
+	   (((intid) >= MIN_LPI_ID) && ((intid) <= gic_virt_feature.max_vintid))) {
+		return true;
+	}
+
+	/*
+	 * If extended INTID range sopported, check for
+	 * Extended PPI [1056..1119] and Extended SPI [4096..5119]
+	 */
+	return (gic_virt_feature.ext_range ?
+		((((intid) >= MIN_EPPI_ID) && ((intid) <= MAX_EPPI_ID)) ||
+		 (((intid) >= MIN_ESPI_ID) && ((intid) <= MAX_ESPI_ID))) :
+		false);
+}
+
+bool gic_validate_state(struct gic_cpu_state *gicstate)
+{
+	unsigned int i, j;
+
+	for (i = 0U; i <= gic_virt_feature.nr_lrs; i++) {
+		unsigned long lr = gicstate->ich_lr_el2[i];
+		unsigned long intid = EXTRACT(ICH_LR_VINTID, lr);
+
+		if ((lr & ICH_LR_STATE_MASK) == ICH_LR_STATE_INVALID) {
+			continue;
+		}
+
+		/* The RMM Specification imposes the constraint that HW == '0' */
+		if ((EXTRACT_BIT(ICH_LR_HW, lr) != 0UL) ||
+		    /* Check RES0 bits in the Priority field */
+		   ((EXTRACT(ICH_LR_PRIORITY, lr) &
+			gic_virt_feature.pri_res0_mask) != 0UL) ||
+		    /* Only the EOI bit in the pINTID is allowed to be set */
+		   ((lr & ICH_LR_PINTID_MASK & ~ICH_LR_EOI_BIT) != 0UL) ||
+		    /* Check if vINTID is in the valid range */
+		   !is_valid_vintid(intid)) {
+			return false;
+		}
+
+		/*
+		 * Behavior is UNPREDICTABLE if two or more List Registers
+		 * specify the same vINTID.
+		 */
+		for (j = i + 1U; j <= gic_virt_feature.nr_lrs; j++) {
+			unsigned long _lr = gicstate->ich_lr_el2[j];
+			unsigned long _intid = EXTRACT(ICH_LR_VINTID, _lr);
+
+			if ((_lr & ICH_LR_STATE_MASK) == ICH_LR_STATE_INVALID) {
+				continue;
+			}
+
+			if (intid == _intid) {
+				return false;
+			}
+		}
+	}
+
+	return true;
+}
+
+/* Save ICH_LR<n>_EL2 registers [n...0] */
+static void read_lrs(struct gic_cpu_state *gicstate)
+{
+	switch (gic_virt_feature.nr_lrs) {
+	READ_ICH_LR_EL2(15);
+	READ_ICH_LR_EL2(14);
+	READ_ICH_LR_EL2(13);
+	READ_ICH_LR_EL2(12);
+	READ_ICH_LR_EL2(11);
+	READ_ICH_LR_EL2(10);
+	READ_ICH_LR_EL2(9);
+	READ_ICH_LR_EL2(8);
+	READ_ICH_LR_EL2(7);
+	READ_ICH_LR_EL2(6);
+	READ_ICH_LR_EL2(5);
+	READ_ICH_LR_EL2(4);
+	READ_ICH_LR_EL2(3);
+	READ_ICH_LR_EL2(2);
+	READ_ICH_LR_EL2(1);
+	default:
+	READ_ICH_LR_EL2(0);
+	}
+}
+
+/* Restore ICH_LR<n>_EL2 registers [n...0] */
+static void write_lrs(struct gic_cpu_state *gicstate)
+{
+	switch (gic_virt_feature.nr_lrs) {
+	WRITE_ICH_LR_EL2(15);
+	WRITE_ICH_LR_EL2(14);
+	WRITE_ICH_LR_EL2(13);
+	WRITE_ICH_LR_EL2(12);
+	WRITE_ICH_LR_EL2(11);
+	WRITE_ICH_LR_EL2(10);
+	WRITE_ICH_LR_EL2(9);
+	WRITE_ICH_LR_EL2(8);
+	WRITE_ICH_LR_EL2(7);
+	WRITE_ICH_LR_EL2(6);
+	WRITE_ICH_LR_EL2(5);
+	WRITE_ICH_LR_EL2(4);
+	WRITE_ICH_LR_EL2(3);
+	WRITE_ICH_LR_EL2(2);
+	WRITE_ICH_LR_EL2(1);
+	default:
+	WRITE_ICH_LR_EL2(0);
+	}
+}
+
+/* Save ICH_AP0R<n>_EL2 and ICH_AP1R<n>_EL2 registers [n...0] */
+static void read_aprs(struct gic_cpu_state *gicstate)
+{
+	switch (gic_virt_feature.nr_aprs) {
+	READ_ICH_APR_EL2(3);
+	READ_ICH_APR_EL2(2);
+	READ_ICH_APR_EL2(1);
+	default:
+	READ_ICH_APR_EL2(0);
+	}
+}
+
+/* Restore ICH_AP0R<n>_EL2 and ICH_AP1R<n>_EL2 registers [n...0] */
+static void write_aprs(struct gic_cpu_state *gicstate)
+{
+	switch (gic_virt_feature.nr_aprs) {
+	WRITE_ICH_APR_EL2(3);
+	WRITE_ICH_APR_EL2(2);
+	WRITE_ICH_APR_EL2(1);
+	default:
+	WRITE_ICH_APR_EL2(0);
+	}
+}
+
+void gic_restore_state(struct gic_cpu_state *gicstate)
+{
+	write_aprs(gicstate);
+	write_lrs(gicstate);
+
+	write_ich_vmcr_el2(gicstate->ich_vmcr_el2);
+	write_ich_hcr_el2(gicstate->ich_hcr_el2);
+}
+
+void gic_save_state(struct gic_cpu_state *gicstate)
+{
+	read_aprs(gicstate);
+	read_lrs(gicstate);
+
+	/* Save the status, including MISR */
+	gicstate->ich_vmcr_el2 = read_ich_vmcr_el2();
+	gicstate->ich_hcr_el2 = read_ich_hcr_el2();
+	gicstate->ich_misr_el2 = read_ich_misr_el2();
+
+	/* On REC exit, set ICH_HCR_EL2.En == '0' */
+	write_ich_hcr_el2(gicstate->ich_hcr_el2 & ~ICH_HCR_EL2_EN_BIT);
+}
diff --git a/lib/libc/CMakeLists.txt b/lib/libc/CMakeLists.txt
new file mode 100644
index 0000000..1631332
--- /dev/null
+++ b/lib/libc/CMakeLists.txt
@@ -0,0 +1,38 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+if(NOT RMM_ARCH STREQUAL fake_host)
+    add_library(rmm-lib-libc)
+
+    target_link_libraries(rmm-lib-libc
+    PUBLIC rmm-lib-arch
+           rmm-lib-common
+           rmm-lib-debug)
+
+    target_include_directories(rmm-lib-libc SYSTEM
+        PUBLIC "include")
+
+    target_sources(rmm-lib-libc
+        PRIVATE "src/abort.c"
+            "src/assert.c"
+            "src/memcmp.c"
+            "src/memcpy.c"
+            "src/memmove.c"
+            "src/printf.c"
+            "src/strlen.c"
+            "src/strcmp.c"
+            "src/strlcpy.c"
+            "src/strncmp.c"
+            "src/strnlen.c")
+
+    target_sources(rmm-lib-libc
+        PRIVATE
+           "src/aarch64/memset.S")
+
+target_compile_definitions(rmm-lib-libc
+    PRIVATE "PRINTF_DISABLE_SUPPORT_FLOAT")
+else()
+   add_library(rmm-lib-libc INTERFACE)
+endif()
diff --git a/lib/libc/include/stdio.h b/lib/libc/include/stdio.h
new file mode 100644
index 0000000..e7f2355
--- /dev/null
+++ b/lib/libc/include/stdio.h
@@ -0,0 +1,117 @@
+/*
+ * SPDX-License-Identifier: MIT
+ * SPDX-FileCopyrightText: Copyright Marco Paland (info@paland.com), PALANDesign Hannover, Germany
+ * SPDX-FileCopyrightText: Copyright TF-RMM 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
+ * n 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.
+ *
+ * \brief Tiny printf, sprintf and snprintf implementation, optimized for
+ * speed on
+ *        embedded systems with a very limited resources.
+ *        Use this instead of bloated standard/newlib printf.
+ *        These routines are thread safe and reentrant.
+ */
+
+#ifndef STDIO_H
+#define STDIO_H
+
+#include <stdarg.h>
+#include <stddef.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * Output a character to a custom device like UART, used by the printf() function
+ * This function is declared here only. You have to write your custom implementation somewhere
+ * \param character: Character to output
+ */
+void _putchar(char character);
+
+
+/**
+ * Tiny printf implementation
+ * You have to implement _putchar if you use printf()
+ * To avoid conflicts with the regular printf() API it is overridden by macro defines
+ * and internal underscore-appended functions like printf_() are used
+ * \param format A string that specifies the format of the output
+ * \return The number of characters that are written into the array, not counting the terminating null character
+ */
+#define printf printf_
+int printf_(const char *format, ...);
+
+
+/**
+ * Tiny sprintf implementation
+ * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD!
+ * \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output!
+ * \param format A string that specifies the format of the output
+ * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character
+ */
+#define sprintf sprintf_
+int sprintf_(char *buffer, const char *format, ...);
+
+
+/**
+ * Tiny snprintf/vsnprintf implementation
+ * \param buffer A pointer to the buffer where to store the formatted string
+ * \param count The maximum number of characters to store in the buffer, including a terminating null character
+ * \param format A string that specifies the format of the output
+ * \param va A value identifying a variable arguments list
+ * \return The number of characters that COULD have been written into the buffer, not counting the terminating
+ *         null character. A value equal or larger than count indicates truncation. Only when the returned value
+ *         is non-negative and less than count, the string has been completely written.
+ */
+#define snprintf  snprintf_
+#define vsnprintf vsnprintf_
+int  snprintf_(char *buffer, size_t count, const char *format, ...);
+int vsnprintf_(char *buffer, size_t count, const char *format, va_list va);
+
+
+/**
+ * Tiny vprintf implementation
+ * \param format A string that specifies the format of the output
+ * \param va A value identifying a variable arguments list
+ * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character
+ */
+#define vprintf vprintf_
+int vprintf_(const char *format, va_list va);
+
+
+/**
+ * printf with output function
+ * You may use this as dynamic alternative to printf() with its fixed _putchar() output
+ * \param out An output function which takes one character and an argument pointer
+ * \param arg An argument pointer for user data passed to output function
+ * \param format A string that specifies the format of the output
+ * \return The number of characters that are sent to the output function, not counting the terminating null character
+ */
+int fctprintf(void (*out)(char character, void *arg), void *arg,
+	      const char *format, ...);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif  /* STDIO_H */
diff --git a/lib/libc/include/string.h b/lib/libc/include/string.h
new file mode 100644
index 0000000..8772708
--- /dev/null
+++ b/lib/libc/include/string.h
@@ -0,0 +1,22 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef STRING_H
+#define STRING_H
+
+#include <stddef.h>
+
+int memcmp(const void *s1, const void *s2, size_t n);
+void *memcpy(void *dest, const void *src, size_t n);
+void *memmove(void *dest, const void *src, size_t n);
+void *memset(void *s, int c, size_t n);
+
+int strcmp(const char *s1, const char *s2);
+size_t strlen(const char *s);
+size_t strlcpy(char *dst, const char *src, size_t dsize);
+int strncmp(const char *s1, const char *s2, size_t n);
+size_t strnlen(const char *s, size_t maxlen);
+
+#endif
diff --git a/lib/libc/src/aarch64/memset.S b/lib/libc/src/aarch64/memset.S
new file mode 100644
index 0000000..32c04e0
--- /dev/null
+++ b/lib/libc/src/aarch64/memset.S
@@ -0,0 +1,63 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <asm_macros.S>
+
+	.global	memset
+
+/* -----------------------------------------------------------------------
+ * void *memset(void *dst, int val, size_t count)
+ *
+ * Copy the value of 'val' (converted to an unsigned char) into
+ * each of the first 'count' characters of the object pointed to by 'dst'.
+ *
+ * Returns the value of 'dst'.
+ * -----------------------------------------------------------------------
+ */
+func memset
+	cbz	x2, exit		/* exit if 'count' = 0 */
+	mov	x3, x0			/* keep x0 */
+	tst	x0, #7
+	b.eq	aligned			/* 8-bytes aligned */
+
+	/* Unaligned 'dst' */
+unaligned:
+	strb	w1, [x3], #1
+	subs	x2, x2, #1
+	b.eq	exit			/* exit if 0 */
+	tst	x3, #7
+	b.ne	unaligned		/* continue while unaligned */
+
+	/* 8-bytes aligned */
+aligned:cbz	x1, x1_zero
+	bfi	w1, w1, #8, #8		/* propagate 'val' */
+	bfi	w1, w1, #16, #16
+	bfi	x1, x1, #32, #32
+
+x1_zero:ands	x4, x2, #~0x3f
+	b.eq	less_64
+
+write_64:
+	.rept	4
+	stp	x1, x1, [x3], #16	/* write 64 bytes in a loop */
+	.endr
+	subs	x4, x4, #64
+	b.ne	write_64
+less_64:tbz	w2, #5, less_32		/* < 32 bytes */
+	stp	x1, x1, [x3], #16	/* write 32 bytes */
+	stp	x1, x1, [x3], #16
+less_32:tbz	w2, #4, less_16		/* < 16 bytes */
+	stp	x1, x1, [x3], #16	/* write 16 bytes */
+less_16:tbz	w2, #3, less_8		/* < 8 bytes */
+	str	x1, [x3], #8		/* write 8 bytes */
+less_8:	tbz	w2, #2, less_4		/* < 4 bytes */
+	str	w1, [x3], #4		/* write 4 bytes */
+less_4:	tbz	w2, #1, less_2		/* < 2 bytes */
+	strh	w1, [x3], #2		/* write 2 bytes */
+less_2:	tbz	w2, #0, exit
+	strb	w1, [x3]		/* write 1 byte */
+exit:	ret
+
+endfunc	memset
diff --git a/lib/libc/src/abort.c b/lib/libc/src/abort.c
new file mode 100644
index 0000000..ad4dd4e
--- /dev/null
+++ b/lib/libc/src/abort.c
@@ -0,0 +1,12 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <debug.h>
+
+void abort(void)
+{
+	ERROR("ABORT\n");
+	panic();
+}
diff --git a/lib/libc/src/assert.c b/lib/libc/src/assert.c
new file mode 100644
index 0000000..563da4f
--- /dev/null
+++ b/lib/libc/src/assert.c
@@ -0,0 +1,26 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <debug.h>
+
+void __assert(const char *file, int line, const char *expression)
+{
+	ERROR("Assertion \"%s\" failed %s:%d\n", expression, file, line);
+	while (true) {
+		wfe();
+	}
+}
+
+void __assert_func(const char *file, int line, const char *func, const char *expression)
+{
+	ERROR("Assertion \"%s\" failed %s:%d, %s\n", expression, file, line, func);
+	while (true) {
+		wfe();
+	}
+}
+
+
diff --git a/lib/libc/src/memcmp.c b/lib/libc/src/memcmp.c
new file mode 100644
index 0000000..9ef89e6
--- /dev/null
+++ b/lib/libc/src/memcmp.c
@@ -0,0 +1,25 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <stddef.h>
+#include <string.h>
+
+int memcmp(const void *s1, const void *s2, size_t len)
+{
+	const unsigned char *s = s1;
+	const unsigned char *d = s2;
+	unsigned char sc;
+	unsigned char dc;
+
+	while (len--) {
+		sc = *s++;
+		dc = *d++;
+		if (sc - dc) {
+			return (sc - dc);
+		}
+	}
+
+	return 0;
+}
diff --git a/lib/libc/src/memcpy.c b/lib/libc/src/memcpy.c
new file mode 100644
index 0000000..47fee23
--- /dev/null
+++ b/lib/libc/src/memcpy.c
@@ -0,0 +1,19 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <stddef.h>
+#include <string.h>
+
+void *memcpy(void *dst, const void *src, size_t len)
+{
+	const char *s = src;
+	char *d = dst;
+
+	while (len--) {
+		*d++ = *s++;
+	}
+
+	return dst;
+}
diff --git a/lib/libc/src/memmove.c b/lib/libc/src/memmove.c
new file mode 100644
index 0000000..b8acb65
--- /dev/null
+++ b/lib/libc/src/memmove.c
@@ -0,0 +1,34 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ * SPDX-FileCopyrightText: Copyright Jon Medhurst <tixy@linaro.org>
+ */
+
+#include <stddef.h>
+#include <string.h>
+
+void *memmove(void *dst, const void *src, size_t len)
+{
+	/*
+	 * The following test makes use of unsigned arithmetic overflow to
+	 * more efficiently test the condition !(src <= dst && dst < str+len).
+	 * It also avoids the situation where the more explicit test would give
+	 * incorrect results were the calculation str+len to overflow (though
+	 * that issue is probably moot as such usage is probably undefined
+	 * behaviour and a bug anyway.
+	 */
+	if ((size_t)dst - (size_t)src >= len) {
+		/* destination not in source data, so can safely use memcpy */
+		return memcpy(dst, src, len);
+	}
+
+	/* copy backwards... */
+	const char *end = dst;
+	const char *s = (const char *)src + len;
+	char *d = (char *)dst + len;
+
+	while (d != end) {
+		*--d = *--s;
+	}
+	return dst;
+}
diff --git a/lib/libc/src/printf.c b/lib/libc/src/printf.c
new file mode 100644
index 0000000..d700426
--- /dev/null
+++ b/lib/libc/src/printf.c
@@ -0,0 +1,1043 @@
+/*
+ * SPDX-License-Identifier: MIT
+ * SPDX-FileCopyrightText: Copyright Marco Paland (info@paland.com), PALANDesign Hannover, Germany
+ * SPDX-FileCopyrightText: Copyright TF-RMM 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.
+ *
+ * \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for
+ * speed on
+ *   embedded systems with a very limited resources. These routines are thread
+ *   safe and reentrant!
+ *   Use this instead of the bloated standard/newlib printf cause these use
+ *   malloc for printf (and may not be thread safe).
+ */
+
+/*
+ * fine this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the
+ * printf_config.h header file
+ * default: undefined
+ */
+/*
+ * support for the floating point type (%f)
+ * default: activated
+ */
+#ifndef PRINTF_DISABLE_SUPPORT_FLOAT
+#define PRINTF_SUPPORT_FLOAT
+#endif
+
+/* import float.h for DBL_MAX */
+#if defined(PRINTF_SUPPORT_FLOAT)
+#include <float.h>
+#endif
+
+#ifdef PRINTF_INCLUDE_CONFIG_H
+#include "printf_config.h"
+#endif
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+
+/*
+ * 'ntoa' conversion buffer size, this must be big enough to hold one converted
+ * numeric number including padded zeros (dynamically created on stack)
+ * default: 32 byte
+ */
+#ifndef PRINTF_NTOA_BUFFER_SIZE
+#define PRINTF_NTOA_BUFFER_SIZE    32U
+#endif
+
+/*
+ *'ftoa' conversion buffer size, this must be big enough to hold one converted
+ * float number including padded zeros (dynamically created on stack)
+ * default: 32 byte
+ */
+#ifndef PRINTF_FTOA_BUFFER_SIZE
+#define PRINTF_FTOA_BUFFER_SIZE    32U
+#endif
+
+/*
+ * support for exponential floating point notation (%e/%g)
+ * default: activated
+ */
+#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL
+#define PRINTF_SUPPORT_EXPONENTIAL
+#endif
+
+/*
+ * define the default floating point precision
+ * default: 6 digits
+ */
+#ifndef PRINTF_DEFAULT_FLOAT_PRECISION
+#define PRINTF_DEFAULT_FLOAT_PRECISION  6U
+#endif
+
+/*
+ * define the largest float suitable to print with %f
+ * default: 1e9
+ */
+#ifndef PRINTF_MAX_FLOAT
+#define PRINTF_MAX_FLOAT  1e9
+#endif
+
+/*
+ * support for the long long types (%llu or %p)
+ * default: activated
+ */
+#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG
+#define PRINTF_SUPPORT_LONG_LONG
+#endif
+
+/*
+ * support for the ptrdiff_t type (%t)
+ * ptrdiff_t is normally defined in <stddef.h> as long or long long type
+ * default: activated
+ */
+#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T
+#define PRINTF_SUPPORT_PTRDIFF_T
+#endif
+
+/******************************************************************************/
+
+/* internal flag definitions */
+#define FLAGS_ZEROPAD   (1U <<  0U)
+#define FLAGS_LEFT      (1U <<  1U)
+#define FLAGS_PLUS      (1U <<  2U)
+#define FLAGS_SPACE     (1U <<  3U)
+#define FLAGS_HASH      (1U <<  4U)
+#define FLAGS_UPPERCASE (1U <<  5U)
+#define FLAGS_CHAR      (1U <<  6U)
+#define FLAGS_SHORT     (1U <<  7U)
+#define FLAGS_LONG      (1U <<  8U)
+#define FLAGS_LONG_LONG (1U <<  9U)
+#define FLAGS_PRECISION (1U << 10U)
+#define FLAGS_ADAPT_EXP (1U << 11U)
+
+/* output function type */
+typedef void (*out_fct_type)(char character, void *buffer, size_t idx,
+			     size_t maxlen);
+
+
+/* wrapper (used as buffer) for output function type */
+typedef struct {
+	void (*fct)(char character, void *arg);
+	void *arg;
+} out_fct_wrap_type;
+
+
+/* internal buffer output */
+static inline void _out_buffer(char character, void *buffer, size_t idx,
+			       size_t maxlen)
+{
+	if (idx < maxlen) {
+		((char *)buffer)[idx] = character;
+	}
+}
+
+
+/* internal null output */
+static inline void _out_null(char character, void *buffer, size_t idx,
+			     size_t maxlen)
+{
+	(void)character; (void)buffer; (void)idx; (void)maxlen;
+}
+
+
+/* internal _putchar wrapper */
+static inline void _out_char(char character, void *buffer, size_t idx,
+			     size_t maxlen)
+{
+	(void)buffer; (void)idx; (void)maxlen;
+	if (character) {
+		_putchar(character);
+	}
+}
+
+
+/* internal output function wrapper */
+static inline void _out_fct(char character, void *buffer, size_t idx,
+			    size_t maxlen)
+{
+	(void)idx; (void)maxlen;
+	if (character) {
+		/* buffer is the output fct pointer */
+		((out_fct_wrap_type *)buffer)->fct(character,
+						  ((out_fct_wrap_type *)buffer)->arg);
+	}
+}
+
+
+/*
+ * internal secure strlen
+ * \return The length of the string (excluding the terminating 0) limited by
+ * 'maxsize'
+ */
+static inline unsigned int _strnlen_s(const char *str, size_t maxsize)
+{
+	const char *s;
+
+	for (s = str; *s && maxsize--; ++s) {
+		;
+	}
+
+	return (unsigned int)(s - str);
+}
+
+/*
+ * internal test if char is a digit (0-9)
+ * \return true if char is a digit
+ */
+static inline bool _is_digit(char ch)
+{
+	return (ch >= '0') && (ch <= '9');
+}
+
+
+/* internal ASCII string to unsigned int conversion */
+static unsigned int _atoi(const char **str)
+{
+	unsigned int i = 0U;
+
+	while (_is_digit(**str)) {
+		i = i * 10U + (unsigned int)(*((*str)++) - '0');
+	}
+	return i;
+}
+
+
+/* output the specified string in reverse, taking care of any zero-padding */
+static size_t _out_rev(out_fct_type out, char *buffer, size_t idx, size_t maxlen, const char *buf, size_t len, unsigned int width, unsigned int flags)
+{
+	const size_t start_idx = idx;
+
+	/* pad spaces up to given width */
+	if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
+		for (size_t i = len; i < width; i++) {
+			out(' ', buffer, idx++, maxlen);
+		}
+	}
+
+	/* reverse string */
+	while (len) {
+		out(buf[--len], buffer, idx++, maxlen);
+	}
+
+	/* append pad spaces up to given width */
+	if (flags & FLAGS_LEFT) {
+		while (idx - start_idx < width) {
+			out(' ', buffer, idx++, maxlen);
+		}
+	}
+
+	return idx;
+}
+
+
+/* internal itoa format */
+static size_t _ntoa_format(out_fct_type out, char *buffer, size_t idx, size_t maxlen, char *buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags)
+{
+	/* pad leading zeros */
+	if (!(flags & FLAGS_LEFT)) {
+		if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
+			width--;
+		}
+		while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
+			buf[len++] = '0';
+		}
+		while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
+			buf[len++] = '0';
+		}
+	}
+
+	/* handle hash */
+	if (flags & FLAGS_HASH) {
+		if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) {
+			len--;
+			if (len && (base == 16U)) {
+				len--;
+			}
+		}
+		if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
+			buf[len++] = 'x';
+		} else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
+			buf[len++] = 'X';
+		} else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
+			buf[len++] = 'b';
+		}
+		if (len < PRINTF_NTOA_BUFFER_SIZE) {
+			buf[len++] = '0';
+		}
+	}
+
+	if (len < PRINTF_NTOA_BUFFER_SIZE) {
+		if (negative) {
+			buf[len++] = '-';
+		} else if (flags & FLAGS_PLUS) {
+			/* ignore the space if the '+' exists */
+			buf[len++] = '+';
+		} else if (flags & FLAGS_SPACE) {
+			buf[len++] = ' ';
+		}
+	}
+
+	return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
+}
+
+
+/* internal itoa for 'long' type */
+static size_t _ntoa_long(out_fct_type out, char *buffer, size_t idx,
+			 size_t maxlen, unsigned long value, bool negative,
+			 unsigned long base, unsigned int prec,
+			 unsigned int width, unsigned int flags)
+{
+	char buf[PRINTF_NTOA_BUFFER_SIZE];
+	size_t len = 0U;
+
+	/* no hash for 0 values */
+	if (!value) {
+		flags &= ~FLAGS_HASH;
+	}
+
+	/* write if precision != 0 and value is != 0 */
+	if (!(flags & FLAGS_PRECISION) || value) {
+		do {
+			const char digit = (char)(value % base);
+
+			buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
+			value /= base;
+		} while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
+	}
+
+	return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative,
+			    (unsigned int)base, prec, width, flags);
+}
+
+
+/* internal itoa for 'long long' type */
+#if defined(PRINTF_SUPPORT_LONG_LONG)
+static size_t _ntoa_long_long(out_fct_type out, char *buffer, size_t idx,
+			      size_t maxlen, unsigned long long value,
+			      bool negative, unsigned long long base,
+			      unsigned int prec, unsigned int width,
+			      unsigned int flags)
+{
+	char buf[PRINTF_NTOA_BUFFER_SIZE];
+	size_t len = 0U;
+
+	/* no hash for 0 values */
+	if (!value) {
+		flags &= ~FLAGS_HASH;
+	}
+
+	/* write if precision != 0 and value is != 0 */
+	if (!(flags & FLAGS_PRECISION) || value) {
+		do {
+			const char digit = (char)(value % base);
+
+			buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
+			value /= base;
+		} while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
+	}
+
+	return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative,
+			    (unsigned int)base, prec, width, flags);
+}
+#endif  /* PRINTF_SUPPORT_LONG_LONG */
+
+
+#if defined(PRINTF_SUPPORT_FLOAT)
+
+#if defined(PRINTF_SUPPORT_EXPONENTIAL)
+/*
+ * forward declaration so that _ftoa can switch to exp notation for
+ * values > PRINTF_MAX_FLOAT
+ */
+static size_t _etoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen,
+		    double value, unsigned int prec, unsigned int width,
+		    unsigned int flags);
+#endif
+
+
+/* internal ftoa for fixed decimal floating point */
+static size_t _ftoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen,
+		    double value, unsigned int prec, unsigned int width,
+		    unsigned int flags)
+{
+	char buf[PRINTF_FTOA_BUFFER_SIZE];
+	size_t len  = 0U;
+	double diff = 0.0;
+
+	/* powers of 10 */
+	static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000,
+		1000000, 10000000, 100000000, 1000000000 };
+
+	/* test for special values */
+	if (value != value) {
+		return _out_rev(out, buffer, idx, maxlen, "nan", 3, width,
+				flags);
+	}
+
+	if (value < -DBL_MAX) {
+		return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width,
+				flags);
+	}
+
+	if (value > DBL_MAX) {
+		return _out_rev(out, buffer, idx, maxlen,
+				(flags & FLAGS_PLUS) ? "fni+" : "fni",
+				(flags & FLAGS_PLUS) ? 4U : 3U, width, flags);
+	}
+
+	/*
+	 * test for very large values
+	 * standard printf behavior is to print EVERY whole number digit --
+	 * which could be 100s of characters overflowing your buffers == bad
+	 */
+	if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) {
+#if defined(PRINTF_SUPPORT_EXPONENTIAL)
+		return _etoa(out, buffer, idx, maxlen, value, prec, width,
+			     flags);
+#else
+		return 0U;
+#endif
+	}
+
+	/* test for negative */
+	bool negative = false;
+
+	if (value < 0) {
+		negative = true;
+		value = 0 - value;
+	}
+
+	/* set default precision, if not set explicitly */
+	if (!(flags & FLAGS_PRECISION)) {
+		prec = PRINTF_DEFAULT_FLOAT_PRECISION;
+	}
+	/*
+	 * limit precision to 9, cause a prec >= 10 can lead to overflow
+	 * errors
+	 */
+	while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) {
+		buf[len++] = '0';
+		prec--;
+	}
+
+	int whole = (int)value;
+	double tmp = (value - whole) * pow10[prec];
+	unsigned long frac = (unsigned long)tmp;
+
+	diff = tmp - frac;
+
+	if (diff > 0.5) {
+		++frac;
+		/* handle rollover, e.g. case 0.99 with prec 1 is 1.0 */
+		if (frac >= pow10[prec]) {
+			frac = 0;
+			++whole;
+		}
+	} else if (diff < 0.5) {
+		/* skip */
+	} else if ((frac == 0U) || (frac & 1U)) {
+		/* if halfway, round up if odd OR if last digit is 0 */
+		++frac;
+	}
+
+	if (prec == 0U) {
+		diff = value - (double)whole;
+		if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) {
+			/*
+			 * exactly 0.5 and ODD, then round up
+			 * 1.5 -> 2, but 2.5 -> 2
+			 */
+			++whole;
+		}
+	} else {
+		unsigned int count = prec;
+
+		/* now do fractional part, as an unsigned number */
+		while (len < PRINTF_FTOA_BUFFER_SIZE) {
+			--count;
+			buf[len++] = (char)(48U + (frac % 10U));
+			frac /= 10U;
+			if (!frac) {
+				break;
+			}
+		}
+		/* add extra 0s */
+		while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) {
+			buf[len++] = '0';
+		}
+		if (len < PRINTF_FTOA_BUFFER_SIZE) {
+			/* add decimal */
+			buf[len++] = '.';
+		}
+	}
+
+	/* do whole part, number is reversed */
+	while (len < PRINTF_FTOA_BUFFER_SIZE) {
+		buf[len++] = (char)(48 + (whole % 10));
+		whole /= 10;
+		if (!whole) {
+			break;
+		}
+	}
+
+	/* pad leading zeros */
+	if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) {
+		if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
+			width--;
+		}
+		while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) {
+			buf[len++] = '0';
+		}
+	}
+
+	if (len < PRINTF_FTOA_BUFFER_SIZE) {
+		if (negative) {
+			buf[len++] = '-';
+		} else if (flags & FLAGS_PLUS) {
+			/* ignore the space if the '+' exists */
+			buf[len++] = '+';
+		} else if (flags & FLAGS_SPACE) {
+			buf[len++] = ' ';
+		}
+	}
+
+	return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
+}
+
+
+#if defined(PRINTF_SUPPORT_EXPONENTIAL)
+/*
+ * internal ftoa variant for exponential floating-point type, contributed by
+ * Martijn Jasperse <m.jasperse@gmail.com>
+ */
+static size_t _etoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen,
+		    double value, unsigned int prec, unsigned int width,
+		    unsigned int flags)
+{
+	/* check for NaN and special values */
+	if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) {
+		return _ftoa(out, buffer, idx, maxlen, value, prec, width,
+			     flags);
+	}
+
+	/* determine the sign */
+	const bool negative = value < 0;
+
+	if (negative) {
+		value = -value;
+	}
+
+	/* default precision */
+	if (!(flags & FLAGS_PRECISION)) {
+		prec = PRINTF_DEFAULT_FLOAT_PRECISION;
+	}
+
+	/*
+	 * determine the decimal exponent
+	 * based on the algorithm by David Gay
+	 * (https://www.ampl.com/netlib/fp/dtoa.c)
+	 */
+	union {
+		uint64_t U;
+		double   F;
+	} conv;
+
+	conv.F = value;
+	/* effectively log2 */
+	int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023;
+
+	/* drop the exponent so conv.F is now in [1,2) */
+	conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U);
+
+	/*
+	 * now approximate log10 from the log2 integer part and an expansion
+	 * of ln around 1.5
+	 */
+	int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168);
+	/*
+	 * now we want to compute 10^expval but we want to be sure it won't
+	 * overflow
+	 */
+	exp2 = (int)(expval * 3.321928094887362 + 0.5);
+	const double z  = expval * 2.302585092994046 - exp2 * 0.6931471805599453;
+	const double z2 = z * z;
+
+	conv.U = (uint64_t)(exp2 + 1023) << 52U;
+	/*
+	 * compute exp(z) using continued fractions, see
+	 * https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
+	 */
+	conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));
+	/* correct for rounding errors */
+	if (value < conv.F) {
+		expval--;
+		conv.F /= 10;
+	}
+
+	/*
+	 * the exponent format is "%+03d" and largest value is "307", so set
+	 * aside 4-5 characters
+	 */
+	unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U;
+
+	/*
+	 * in "%g" mode, "prec" is the number of *significant figures* not
+	 * decimals
+	 */
+	if (flags & FLAGS_ADAPT_EXP) {
+		/* do we want to fall-back to "%f" mode? */
+		if ((value >= 1e-4) && (value < 1e6)) {
+			if ((int)prec > expval) {
+				prec = (unsigned int)((int)prec - expval - 1);
+			} else {
+				prec = 0;
+			}
+			/* make sure _ftoa respects precision */
+			flags |= FLAGS_PRECISION;
+			/* no characters in exponent */
+			minwidth = 0U;
+			expval   = 0;
+		} else {
+			/* we use one sigfig for the whole part */
+			if ((prec > 0) && (flags & FLAGS_PRECISION)) {
+				--prec;
+			}
+		}
+	}
+
+	/* will everything fit? */
+	unsigned int fwidth = width;
+
+	if (width > minwidth) {
+		/*
+		 * we didn't fall-back so subtract the characters required for
+		 * the exponent
+		 */
+		fwidth -= minwidth;
+	} else {
+		/* not enough characters, so go back to default sizing */
+		fwidth = 0U;
+	}
+	if ((flags & FLAGS_LEFT) && minwidth) {
+		/* if we're padding on the right, DON'T pad the floating part */
+		fwidth = 0U;
+	}
+
+	/* rescale the float value */
+	if (expval) {
+		value /= conv.F;
+	}
+
+	/* output the floating part */
+	const size_t start_idx = idx;
+
+	idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP);
+
+	/* output the exponent part */
+	if (minwidth) {
+		/* output the exponential symbol */
+		out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++,
+		    maxlen);
+		/* output the exponent value */
+		idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth-1, FLAGS_ZEROPAD | FLAGS_PLUS);
+		/* might need to right-pad spaces */
+		if (flags & FLAGS_LEFT) {
+			while (idx - start_idx < width) {
+				out(' ', buffer, idx++, maxlen);
+			}
+		}
+	}
+	return idx;
+}
+#endif  /* PRINTF_SUPPORT_EXPONENTIAL */
+#endif  /* PRINTF_SUPPORT_FLOAT */
+
+
+/* internal vsnprintf */
+static int _vsnprintf(out_fct_type out, char *buffer, const size_t maxlen,
+		      const char *format, va_list va)
+{
+	unsigned int flags, width, precision, n;
+	size_t idx = 0U;
+
+	if (!buffer) {
+		/* use null output function */
+		out = _out_null;
+	}
+
+	while (*format) {
+		/* format specifier?  %[flags][width][.precision][length] */
+		if (*format != '%') {
+			/* no */
+			out(*format, buffer, idx++, maxlen);
+			format++;
+			continue;
+		} else {
+			/* yes, evaluate it */
+			format++;
+		}
+
+		/* evaluate flags */
+		flags = 0U;
+		do {
+			switch (*format) {
+			case '0':
+				flags |= FLAGS_ZEROPAD; format++; n = 1U;
+				break;
+			case '-':
+				flags |= FLAGS_LEFT;    format++; n = 1U;
+				break;
+			case '+':
+				flags |= FLAGS_PLUS;    format++; n = 1U;
+				break;
+			case ' ':
+				flags |= FLAGS_SPACE;   format++; n = 1U;
+				break;
+			case '#':
+				flags |= FLAGS_HASH;    format++; n = 1U;
+				break;
+			default:
+				n = 0U;
+				break;
+			}
+		} while (n);
+
+		/* evaluate width field */
+		width = 0U;
+		if (_is_digit(*format)) {
+			width = _atoi(&format);
+		} else if (*format == '*') {
+			const int w = va_arg(va, int);
+
+			if (w < 0) {
+				flags |= FLAGS_LEFT; /* reverse padding */
+				width = (unsigned int)-w;
+			} else {
+				width = (unsigned int)w;
+			}
+			format++;
+		}
+
+		/* evaluate precision field */
+		precision = 0U;
+		if (*format == '.') {
+			flags |= FLAGS_PRECISION;
+			format++;
+			if (_is_digit(*format)) {
+				precision = _atoi(&format);
+			} else if (*format == '*') {
+				const int prec = (int)va_arg(va, int);
+
+				precision = prec > 0 ? (unsigned int)prec : 0U;
+				format++;
+			}
+		}
+
+		/* evaluate length field */
+		switch (*format) {
+		case 'l':
+			flags |= FLAGS_LONG;
+			format++;
+			if (*format == 'l') {
+				flags |= FLAGS_LONG_LONG;
+				format++;
+			}
+			break;
+		case 'h':
+			flags |= FLAGS_SHORT;
+			format++;
+			if (*format == 'h') {
+				flags |= FLAGS_CHAR;
+				format++;
+			}
+			break;
+#if defined(PRINTF_SUPPORT_PTRDIFF_T)
+		case 't':
+			flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
+			format++;
+			break;
+#endif
+		case 'j':
+			flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
+			format++;
+			break;
+		case 'z':
+			flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
+			format++;
+			break;
+		default:
+			break;
+		}
+
+		/* evaluate specifier */
+		switch (*format) {
+		case 'd':
+		case 'i':
+		case 'u':
+		case 'x':
+		case 'X':
+		case 'o':
+		case 'b': {
+			/* set the base */
+			unsigned int base;
+
+			if (*format == 'x' || *format == 'X') {
+				base = 16U;
+			} else if (*format == 'o') {
+				base =  8U;
+			} else if (*format == 'b') {
+				base =  2U;
+			} else {
+				base = 10U;
+				/* no hash for dec format */
+				flags &= ~FLAGS_HASH;
+			}
+			/* uppercase */
+			if (*format == 'X') {
+				flags |= FLAGS_UPPERCASE;
+			}
+
+			/* no plus or space flag for u, x, X, o, b */
+			if ((*format != 'i') && (*format != 'd')) {
+				flags &= ~(FLAGS_PLUS | FLAGS_SPACE);
+			}
+
+			/* ignore '0' flag when precision is given */
+			if (flags & FLAGS_PRECISION) {
+				flags &= ~FLAGS_ZEROPAD;
+			}
+
+			/* convert the integer */
+			if ((*format == 'i') || (*format == 'd')) {
+				/* signed */
+				if (flags & FLAGS_LONG_LONG) {
+#if defined(PRINTF_SUPPORT_LONG_LONG)
+					const long long value = va_arg(va, long long);
+
+					idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
+#endif
+				} else if (flags & FLAGS_LONG) {
+					const long value = va_arg(va, long);
+
+					idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
+				} else {
+					const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int);
+
+					idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
+				}
+			} else {
+				/* unsigned */
+				if (flags & FLAGS_LONG_LONG) {
+#if defined(PRINTF_SUPPORT_LONG_LONG)
+					idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags);
+#endif
+				} else if (flags & FLAGS_LONG) {
+					idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags);
+				} else {
+					const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int);
+
+					idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags);
+				}
+			}
+			format++;
+			break;
+		}
+#if defined(PRINTF_SUPPORT_FLOAT)
+		case 'f':
+		case 'F':
+			if (*format == 'F') {
+				flags |= FLAGS_UPPERCASE;
+			}
+
+			idx = _ftoa(out, buffer, idx, maxlen,
+				    va_arg(va, double), precision, width, flags);
+			format++;
+			break;
+#if defined(PRINTF_SUPPORT_EXPONENTIAL)
+		case 'e':
+		case 'E':
+		case 'g':
+		case 'G':
+			if ((*format == 'g') || (*format == 'G')) {
+				flags |= FLAGS_ADAPT_EXP;
+			}
+
+			if ((*format == 'E') || (*format == 'G')) {
+				flags |= FLAGS_UPPERCASE;
+			}
+
+			idx = _etoa(out, buffer, idx, maxlen,
+				    va_arg(va, double), precision, width, flags);
+			format++;
+			break;
+#endif  /* PRINTF_SUPPORT_EXPONENTIAL */
+#endif  /* PRINTF_SUPPORT_FLOAT */
+		case 'c': {
+			unsigned int l = 1U;
+			/* pre padding */
+			if (!(flags & FLAGS_LEFT)) {
+				while (l++ < width) {
+					out(' ', buffer, idx++, maxlen);
+				}
+			}
+			/* char output */
+			out((char)va_arg(va, int), buffer, idx++, maxlen);
+			/* post padding */
+			if (flags & FLAGS_LEFT) {
+				while (l++ < width) {
+					out(' ', buffer, idx++, maxlen);
+				}
+			}
+			format++;
+			break;
+		}
+
+		case 's': {
+			const char *p = va_arg(va, char*);
+			unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1);
+			/* pre padding */
+			if (flags & FLAGS_PRECISION) {
+				l = (l < precision ? l : precision);
+			}
+			if (!(flags & FLAGS_LEFT)) {
+				while (l++ < width) {
+					out(' ', buffer, idx++, maxlen);
+				}
+			}
+			/* string output */
+			while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) {
+				out(*(p++), buffer, idx++, maxlen);
+			}
+			/* post padding */
+			if (flags & FLAGS_LEFT) {
+				while (l++ < width) {
+					out(' ', buffer, idx++, maxlen);
+				}
+			}
+			format++;
+			break;
+		}
+
+		case 'p': {
+			width = sizeof(void *) * 2U;
+			flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE;
+#if defined(PRINTF_SUPPORT_LONG_LONG)
+			const bool is_ll = sizeof(uintptr_t) == sizeof(long long);
+
+			if (is_ll) {
+				idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void *), false, 16U, precision, width, flags);
+			} else {
+#endif
+				idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void *)), false, 16U, precision, width, flags);
+#if defined(PRINTF_SUPPORT_LONG_LONG)
+			}
+#endif
+			format++;
+			break;
+		}
+
+		case '%':
+			out('%', buffer, idx++, maxlen);
+			format++;
+			break;
+
+		default:
+			out(*format, buffer, idx++, maxlen);
+			format++;
+			break;
+		}
+	}
+
+	/* termination */
+	out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
+
+	/* return written chars without terminating \0 */
+	return (int)idx;
+}
+
+
+/*****************************************************************************/
+
+int printf_(const char *format, ...)
+{
+	va_list va;
+
+	va_start(va, format);
+	char buffer[1];
+	const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va);
+
+	va_end(va);
+	return ret;
+}
+
+
+int sprintf_(char *buffer, const char *format, ...)
+{
+	va_list va;
+
+	va_start(va, format);
+	const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va);
+
+	va_end(va);
+	return ret;
+}
+
+
+int snprintf_(char *buffer, size_t count, const char *format, ...)
+{
+	va_list va;
+
+	va_start(va, format);
+	const int ret = _vsnprintf(_out_buffer, buffer, count, format, va);
+
+	va_end(va);
+	return ret;
+}
+
+
+int vprintf_(const char *format, va_list va)
+{
+	char buffer[1];
+
+	return _vsnprintf(_out_char, buffer, (size_t)-1, format, va);
+}
+
+
+int vsnprintf_(char *buffer, size_t count, const char *format, va_list va)
+{
+	return _vsnprintf(_out_buffer, buffer, count, format, va);
+}
+
+
+int fctprintf(void (*out)(char character, void *arg), void *arg,
+	      const char *format, ...)
+{
+	va_list va;
+
+	va_start(va, format);
+	const out_fct_wrap_type out_fct_wrap = { out, arg };
+	const int ret = _vsnprintf(_out_fct, (char *)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va);
+
+	va_end(va);
+	return ret;
+}
diff --git a/lib/libc/src/strcmp.c b/lib/libc/src/strcmp.c
new file mode 100644
index 0000000..3719125
--- /dev/null
+++ b/lib/libc/src/strcmp.c
@@ -0,0 +1,48 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright The Regents of the University of California.
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <string.h>
+
+/*
+ * Compare strings.
+ */
+int strcmp(const char *s1, const char *s2)
+{
+	while (*s1 == *s2++) {
+		if (*s1++ == '\0') {
+			return 0;
+		}
+	}
+
+	return (*(const unsigned char *)s1 - *(const unsigned char *)(s2 - 1));
+}
diff --git a/lib/libc/src/strlcpy.c b/lib/libc/src/strlcpy.c
new file mode 100644
index 0000000..dfc5965
--- /dev/null
+++ b/lib/libc/src/strlcpy.c
@@ -0,0 +1,55 @@
+/*
+ * SPDX-License-Identifier: ISC
+ * SPDX-FileCopyrightText: Copyright Todd C. Miller <Todd.Miller@courtesan.com>
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * OpenBSD: strlcpy.c,v 1.12 2015/01/15 03:54:12 millert Exp $
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+/*
+ * Copy string src to buffer dst of size dsize.  At most dsize-1
+ * chars will be copied.  Always NUL terminates (unless dsize == 0).
+ * Returns strlen(src); if retval >= dsize, truncation occurred.
+ */
+size_t strlcpy(char *dst, const char *src, size_t dsize)
+{
+	const char *osrc = src;
+	size_t nleft = dsize;
+
+	/* Copy as many bytes as will fit. */
+	if (nleft != 0UL) {
+		while (--nleft != 0UL) {
+			*dst++ = *src++;
+			if (*(dst - 1) == '\0') {
+				break;
+			}
+		}
+	}
+
+	/* Not enough room in dst, add NUL and traverse rest of src. */
+	if (nleft == 0UL) {
+		if (dsize != 0UL) {
+			*dst = '\0'; /* NUL-terminate dst */
+		}
+		while (*src++) {
+			;
+		}
+	}
+
+	return (src - osrc - 1); /* count does not include NUL */
+}
diff --git a/lib/libc/src/strlen.c b/lib/libc/src/strlen.c
new file mode 100644
index 0000000..1d6f7bd
--- /dev/null
+++ b/lib/libc/src/strlen.c
@@ -0,0 +1,17 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <string.h>
+
+size_t strlen(const char *s)
+{
+	const char *cursor = s;
+
+	while (*cursor) {
+		cursor++;
+	}
+
+	return cursor - s;
+}
diff --git a/lib/libc/src/strncmp.c b/lib/libc/src/strncmp.c
new file mode 100644
index 0000000..f5a3723
--- /dev/null
+++ b/lib/libc/src/strncmp.c
@@ -0,0 +1,50 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright The Regents of the University of California.
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <string.h>
+
+int strncmp(const char *s1, const char *s2, size_t n)
+{
+	if (n == 0UL) {
+		return 0;
+	}
+
+	do {
+		if (*s1 != *s2++) {
+			return (*(const unsigned char *)s1 -
+				*(const unsigned char *)(s2 - 1));
+		}
+		if (*s1++ == '\0') {
+			break;
+		}
+	} while (--n != 0UL);
+
+	return 0;
+}
diff --git a/lib/libc/src/strnlen.c b/lib/libc/src/strnlen.c
new file mode 100644
index 0000000..e1a975b
--- /dev/null
+++ b/lib/libc/src/strnlen.c
@@ -0,0 +1,40 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ * SPDX-FileCopyrightText: Copyright David Schultz <das@FreeBSD.org
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <string.h>
+
+size_t strnlen(const char *s, size_t maxlen)
+{
+	size_t len;
+
+	for (len = 0UL; len < maxlen; len++, s++) {
+		if (!*s) {
+			break;
+		}
+	}
+	return len;
+}
diff --git a/lib/measurement/CMakeLists.txt b/lib/measurement/CMakeLists.txt
new file mode 100644
index 0000000..ed42000
--- /dev/null
+++ b/lib/measurement/CMakeLists.txt
@@ -0,0 +1,18 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+add_library(rmm-lib-measurement)
+
+target_link_libraries(rmm-lib-measurement
+    PUBLIC MbedTLS::Crypto
+    PRIVATE rmm-lib-arch
+            rmm-lib-common
+            rmm-lib-debug)
+
+target_include_directories(rmm-lib-measurement
+    PUBLIC "include")
+
+target_sources(rmm-lib-measurement
+    PRIVATE "src/measurement.c")
diff --git a/lib/measurement/include/measurement.h b/lib/measurement/include/measurement.h
new file mode 100644
index 0000000..dd54d92
--- /dev/null
+++ b/lib/measurement/include/measurement.h
@@ -0,0 +1,166 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef MEASUREMENT_H
+#define MEASUREMENT_H
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <utils_def.h>
+
+/* Supported algorithms */
+enum hash_algo {
+	HASH_ALGO_SHA256 = 0,
+	HASH_ALGO_SHA512 = 1,
+};
+
+/*
+ * RmiDataMeasureContent enumeration representing
+ * whether to measure DATA Granule contents.
+ */
+enum rmi_data_measure_content {
+	/* Do not measure DATA Granule contents */
+	RMI_NO_MEASURE_CONTENT = 0U,
+
+	/* Measure DATA Granule contents */
+	RMI_MEASURE_CONTENT = 1U
+};
+
+/*
+ * Types of measurement headers as specified in RMM Spec. section C1.1.2
+ */
+#define MEASUREMENT_REALM_HEADER	(1U)
+#define MEASUREMENT_DATA_HEADER		(2U)
+#define MEASUREMENT_REC_HEADER		(3U)
+
+/* Measurement slot reserved for RIM */
+#define RIM_MEASUREMENT_SLOT		(0U)
+
+/* Maximum number of measurements */
+#define MEASUREMENT_SLOT_NR		(5U)
+
+/* Size in bytes of the SHA256 measurement */
+#define SHA256_SIZE			(32U)
+
+/* Size in bytes of the SHA512 measurement */
+#define SHA512_SIZE			(64U)
+
+#define MEASURE_DESC_TYPE_DATA		0x0
+#define MEASURE_DESC_TYPE_REC		0x1
+#define MEASURE_DESC_TYPE_RIPAS		0x2
+
+/*
+ * Size in bytes of the largest measurement type that can be supported.
+ * This macro needs to be updated accordingly if new algorithms are supported.
+ */
+#define MAX_MEASUREMENT_SIZE		SHA512_SIZE
+
+/* RmmMeasurementDescriptorData type as per RMM spec */
+struct measurement_desc_data {
+	/* Measurement descriptor type, value 0x0 */
+	SET_MEMBER(unsigned char desc_type, 0x0, 0x8);
+	/* Length of this data structure in bytes */
+	SET_MEMBER(unsigned long len, 0x8, 0x10);
+	/* Current RIM value */
+	SET_MEMBER(unsigned char rim[MAX_MEASUREMENT_SIZE], 0x10, 0x50);
+	/* IPA at which the DATA Granule is mapped in the Realm */
+	SET_MEMBER(unsigned long ipa, 0x50, 0x58);
+	/* Flags provided by Host */
+	SET_MEMBER(unsigned long flags, 0x58, 0x60);
+	/*
+	 * Hash of contents of DATA Granule, or zero if flags indicate DATA
+	 * Granule contents are unmeasured
+	 */
+	SET_MEMBER(unsigned char content[MAX_MEASUREMENT_SIZE], 0x60, 0x100);
+};
+COMPILER_ASSERT(sizeof(struct measurement_desc_data) == 0x100);
+
+COMPILER_ASSERT(offsetof(struct measurement_desc_data, desc_type) == 0x0);
+COMPILER_ASSERT(offsetof(struct measurement_desc_data, len) == 0x8);
+COMPILER_ASSERT(offsetof(struct measurement_desc_data, rim) == 0x10);
+COMPILER_ASSERT(offsetof(struct measurement_desc_data, ipa) == 0x50);
+COMPILER_ASSERT(offsetof(struct measurement_desc_data, flags) == 0x58);
+COMPILER_ASSERT(offsetof(struct measurement_desc_data, content) == 0x60);
+
+/* RmmMeasurementDescriptorRec type as per RMM spec */
+struct measurement_desc_rec {
+	/* Measurement descriptor type, value 0x1 */
+	SET_MEMBER(unsigned char desc_type, 0x0, 0x8);
+	/* Length of this data structure in bytes */
+	SET_MEMBER(unsigned long len, 0x8, 0x10);
+	/* Current RIM value */
+	SET_MEMBER(unsigned char rim[MAX_MEASUREMENT_SIZE], 0x10, 0x50);
+	/* Hash of 4KB page which contains REC parameters data structure */
+	SET_MEMBER(unsigned char content[MAX_MEASUREMENT_SIZE], 0x50, 0x100);
+};
+COMPILER_ASSERT(sizeof(struct measurement_desc_rec) == 0x100);
+
+COMPILER_ASSERT(offsetof(struct measurement_desc_rec, desc_type) ==  0x0);
+COMPILER_ASSERT(offsetof(struct measurement_desc_rec, len) ==  0x8);
+COMPILER_ASSERT(offsetof(struct measurement_desc_rec, rim) ==  0x10);
+COMPILER_ASSERT(offsetof(struct measurement_desc_rec, content) ==  0x50);
+
+/* RmmMeasurementDescriptorRipas type as per RMM spec */
+struct measurement_desc_ripas {
+	/* Measurement descriptor type, value 0x2 */
+	SET_MEMBER(unsigned char desc_type, 0x0, 0x8);
+	/* Length of this data structure in bytes */
+	SET_MEMBER(unsigned long len, 0x8, 0x10);
+	/* Current RIM value */
+	SET_MEMBER(unsigned char rim[MAX_MEASUREMENT_SIZE], 0x10, 0x50);
+	/* IPA at which the RIPAS change occurred */
+	SET_MEMBER(unsigned long ipa, 0x50, 0x58);
+	/* RTT level at which the RIPAS change occurred */
+	SET_MEMBER(unsigned char level, 0x58, 0x100);
+};
+COMPILER_ASSERT(sizeof(struct measurement_desc_ripas) == 0x100);
+
+COMPILER_ASSERT(offsetof(struct measurement_desc_ripas, desc_type) == 0x0);
+COMPILER_ASSERT(offsetof(struct measurement_desc_ripas, len) == 0x8);
+COMPILER_ASSERT(offsetof(struct measurement_desc_ripas, rim) == 0x10);
+COMPILER_ASSERT(offsetof(struct measurement_desc_ripas, ipa) == 0x50);
+COMPILER_ASSERT(offsetof(struct measurement_desc_ripas, level) == 0x58);
+
+/*
+ * Calculate the hash of data with algorithm hash_algo to the buffer `out`.
+ */
+void measurement_hash_compute(enum hash_algo hash_algo,
+			      void *data,
+			      size_t size, unsigned char *out);
+
+/* Extend a measurement with algorithm hash_algo. */
+void measurement_extend(enum hash_algo hash_algo,
+			void *current_measurement,
+			void *extend_measurement,
+			size_t extend_measurement_size,
+			unsigned char *out);
+
+/*
+ * Return the hash size in bytes for the selected measurement algorithm.
+ *
+ * Arguments:
+ *	- algorithm:	Algorithm to check.
+ */
+static inline size_t measurement_get_size(
+					const enum hash_algo algorithm)
+{
+	size_t ret = 0;
+
+	switch (algorithm) {
+	case HASH_ALGO_SHA256:
+		ret = (size_t)SHA256_SIZE;
+		break;
+	case HASH_ALGO_SHA512:
+		ret = (size_t)SHA512_SIZE;
+		break;
+	default:
+		assert(false);
+	}
+
+	return ret;
+}
+
+#endif /* MEASUREMENT_H */
diff --git a/lib/measurement/src/measurement.c b/lib/measurement/src/measurement.c
new file mode 100644
index 0000000..7975926
--- /dev/null
+++ b/lib/measurement/src/measurement.c
@@ -0,0 +1,189 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <assert.h>
+#include <debug.h>
+#include <fpu_helpers.h>
+#include <mbedtls/sha256.h>
+#include <mbedtls/sha512.h>
+#include <measurement.h>
+#include <stdbool.h>
+
+#if LOG_LEVEL >= LOG_LEVEL_VERBOSE
+static void measurement_print(unsigned char *measurement,
+			      const enum hash_algo algorithm)
+{
+	unsigned int size = 0U;
+	assert(measurement != NULL);
+
+	VERBOSE("Measurement ");
+
+	switch (algorithm) {
+	case HASH_ALGO_SHA256:
+		VERBOSE("(SHA256): 0x");
+		size = SHA256_SIZE;
+		break;
+	case HASH_ALGO_SHA512:
+		VERBOSE("(SHA512): 0x");
+		size = SHA512_SIZE;
+		break;
+	default:
+		/* Prevent static check and MISRA warnings */
+		assert(false);
+	}
+
+	for (unsigned int i = 0U; i < size; ++i) {
+		VERBOSE("%02x", *(measurement+i));
+	}
+	VERBOSE("\n");
+}
+#endif /* LOG_LEVEL */
+
+static void do_hash(enum hash_algo hash_algo,
+		    void *data,
+		    size_t size,
+		    unsigned char *out)
+{
+	__unused int ret;
+
+	assert(size <= GRANULE_SIZE);
+	assert((data != NULL) && (out != NULL));
+
+	fpu_save_my_state();
+
+	if (hash_algo == HASH_ALGO_SHA256) {
+		/* 0 to indicate SHA256 not SHA224 */
+		FPU_ALLOW(ret = mbedtls_sha256(data, size, out, 0));
+
+		assert(ret == 0);
+	} else if (hash_algo == HASH_ALGO_SHA512) {
+		/* 0 to indicate SHA512 not SHA384 */
+		FPU_ALLOW(ret = mbedtls_sha512(data, size, out, 0));
+
+		assert(ret == 0);
+	} else {
+		assert(false);
+	}
+
+	fpu_restore_my_state();
+
+#if LOG_LEVEL >= LOG_LEVEL_VERBOSE
+	measurement_print(out, hash_algo);
+#endif
+}
+
+void measurement_hash_compute(enum hash_algo hash_algo,
+			      void *data,
+			      size_t size,
+			      unsigned char *out)
+{
+	do_hash(hash_algo, data, size, out);
+}
+
+static void measurement_extend_sha256(void *current_measurement,
+				      size_t current_measurement_size,
+				      void *extend_measurement,
+				      size_t extend_measurement_size,
+				      unsigned char *out)
+{
+	mbedtls_sha256_context sha256_ctx;
+
+	__unused int ret = 0;
+
+	mbedtls_sha256_init(&sha256_ctx);
+	/* 0 to indicate SHA256 not SHA224 */
+	ret = mbedtls_sha256_starts(&sha256_ctx, 0);
+	assert(ret == 0);
+
+	/* Update the measurement */
+	ret = mbedtls_sha256_update(
+		&sha256_ctx,
+		(unsigned char *)current_measurement,
+		current_measurement_size);
+	assert(ret == 0);
+
+	ret = mbedtls_sha256_update(&sha256_ctx,
+					(unsigned char *)extend_measurement,
+					extend_measurement_size);
+	assert(ret == 0);
+
+	ret = mbedtls_sha256_finish(&sha256_ctx, out);
+	assert(ret == 0);
+}
+
+static void measurement_extend_sha512(void *current_measurement,
+				      size_t current_measurement_size,
+				      void *extend_measurement,
+				      size_t extend_measurement_size,
+				      unsigned char *out)
+{
+	mbedtls_sha512_context sha512_ctx;
+	__unused int ret = 0;
+
+	mbedtls_sha512_init(&sha512_ctx);
+	/* 0 to indicate SHA256 not SHA384 */
+	ret = mbedtls_sha512_starts(&sha512_ctx, 0);
+	assert(ret == 0);
+
+	/* Update the measurement */
+	ret = mbedtls_sha512_update(
+		&sha512_ctx,
+		(unsigned char *)current_measurement,
+		current_measurement_size);
+	assert(ret == 0);
+
+	ret = mbedtls_sha512_update(
+		&sha512_ctx,
+		(unsigned char *)extend_measurement_size,
+		extend_measurement_size);
+	assert(ret == 0);
+
+	ret = mbedtls_sha512_finish(&sha512_ctx, out);
+	assert(ret == 0);
+}
+
+void measurement_extend(enum hash_algo hash_algo,
+			void *current_measurement,
+			void *extend_measurement,
+			size_t extend_measurement_size,
+			unsigned char *out)
+{
+	size_t current_measurement_size = measurement_get_size(hash_algo);
+
+	/* We limit the maximum size of the payload to be of GRANULE_SIZE */
+	assert(current_measurement != NULL);
+	assert(extend_measurement_size <= GRANULE_SIZE);
+	assert(extend_measurement != NULL);
+	assert(out != NULL);
+
+	fpu_save_my_state();
+
+	switch (hash_algo) {
+	case HASH_ALGO_SHA256:
+		FPU_ALLOW(
+			measurement_extend_sha256(current_measurement,
+					  current_measurement_size,
+					  extend_measurement,
+					  extend_measurement_size,
+					  out));
+		break;
+	case HASH_ALGO_SHA512:
+		FPU_ALLOW(
+			measurement_extend_sha512(current_measurement,
+					  current_measurement_size,
+					  extend_measurement,
+					  extend_measurement_size,
+					  out));
+		break;
+	default:
+		assert(false);
+	}
+
+	fpu_restore_my_state();
+
+#if LOG_LEVEL >= LOG_LEVEL_VERBOSE
+	measurement_print(out, hash_algo);
+#endif
+}
diff --git a/lib/realm/CMakeLists.txt b/lib/realm/CMakeLists.txt
new file mode 100644
index 0000000..a4b161f
--- /dev/null
+++ b/lib/realm/CMakeLists.txt
@@ -0,0 +1,66 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+add_library(rmm-lib-realm)
+
+arm_config_option(
+    NAME VIRT_ADDR_SPACE_WIDTH
+    HELP "Size in bits of the virtual address space."
+    DEFAULT 0x0
+    TYPE STRING
+    ADVANCED)
+
+#
+# RMM_MAX_GRANULES. Maximum number of granules supported.
+#
+arm_config_option(
+    NAME RMM_MAX_GRANULES
+    HELP "Maximum number of granules supported"
+    DEFAULT 0x0
+    TYPE STRING)
+
+if(VIRT_ADDR_SPACE_WIDTH EQUAL 0x0)
+    message(FATAL_ERROR "VIRT_ADDR_SPACE_WIDTH is not initialized")
+endif()
+
+target_compile_definitions(rmm-lib-realm
+    PUBLIC "VIRT_ADDR_SPACE_SIZE=(1ULL << ULL(${VIRT_ADDR_SPACE_WIDTH}))")
+
+if (RMM_MAX_GRANULES EQUAL 0x0)
+    message (FATAL_ERROR "RMM_MAX_GRANULES not configured")
+endif()
+
+# Export RMM_MAX_GRANULES for use in `plat` component.
+target_compile_definitions(rmm-lib-realm
+    PUBLIC "RMM_MAX_GRANULES=U(${RMM_MAX_GRANULES})")
+
+target_link_libraries(rmm-lib-realm
+    PRIVATE rmm-lib-arch
+            rmm-lib-common
+            rmm-lib-debug
+            rmm-lib-gic
+            rmm-lib-xlat
+            rmm-lib-allocator
+            rmm-lib-attestation
+            rmm-platform)
+
+target_link_libraries(rmm-lib-realm
+    PUBLIC rmm-lib-measurement
+           rmm-lib-smc)
+
+target_include_directories(rmm-lib-realm
+    PUBLIC "include"
+    PRIVATE "src/include/${RMM_ARCH}")
+
+target_sources(rmm-lib-realm
+    PRIVATE "src/buffer.c"
+            "src/granule.c"
+            "src/s2tt.c"
+            "src/sve.c")
+
+if(NOT RMM_ARCH STREQUAL fake_host)
+    target_sources(rmm-lib-realm
+        PRIVATE "src/aarch64/sve_helpers.S")
+endif()
diff --git a/lib/realm/include/buffer.h b/lib/realm/include/buffer.h
new file mode 100644
index 0000000..804179f
--- /dev/null
+++ b/lib/realm/include/buffer.h
@@ -0,0 +1,91 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef BUFFER_H
+#define BUFFER_H
+
+#include <assert.h>
+#include <smc-rmi.h>
+#include <stdbool.h>
+#include <utils_def.h>
+
+enum buffer_slot {
+	/*
+	 * NS.
+	 */
+	SLOT_NS,
+
+	/*
+	 * RMM-private.
+	 */
+	SLOT_DELEGATED,
+	SLOT_RD,
+	SLOT_REC,
+	SLOT_REC2,		/* Some commands access two REC granules at a time*/
+	SLOT_REC_TARGET,	/* Target REC for interrupts */
+	SLOT_REC_AUX0,		/* Reserve slots for max rec auxiliary granules
+				 * so that all of them can be mapped at once.
+				 * If the max aux granules is 0, no slots will
+				 * be reserved.
+				 */
+	SLOT_RTT = SLOT_REC_AUX0 + MAX_REC_AUX_GRANULES,
+	SLOT_RTT2,		/* Some commands access two RTT granules at a time*/
+	SLOT_RSI_CALL,
+	NR_CPU_SLOTS
+};
+
+struct granule;
+
+void assert_cpu_slots_empty(void);
+void *granule_map(struct granule *g, enum buffer_slot slot);
+void buffer_unmap(void *buf);
+
+bool ns_buffer_read(enum buffer_slot slot,
+		    struct granule *granule,
+		    unsigned int offset,
+		    unsigned int size,
+		    void *dest);
+bool ns_buffer_write(enum buffer_slot slot,
+		     struct granule *granule,
+		     unsigned int offset,
+		     unsigned int size,
+		     void *src);
+
+/*
+ * Initializes and enables the VMSA for the slot buffer mechanism.
+ *
+ * Create an empty translation context for the current CPU.
+ * If the context already exists (e.g. current CPU was previously
+ * turned on and therefore the context is already in memory),
+ * nothing happens.
+ */
+void slot_buf_setup_xlat(void);
+
+/*
+ * Finishes initializing the slot buffer mechanism.
+ * This function should be called after the MMU is enabled.
+ */
+void slot_buf_init(void);
+
+/******************************************************************************
+ * Internal APIs not meant to be invoked by generic RMM code.
+ * These are exposed to facilitate testing.
+ *****************************************************************************/
+
+/*
+ * Maps a given PA into the specified slot. This API verifies that the slot
+ * matches the 'ns' argument.
+ *
+ * On success, it returns the VA of the slot where the PA has been mapped to.
+ * Otherwise, it will return NULL.
+ */
+void *buffer_map_internal(enum buffer_slot slot, unsigned long addr, bool ns);
+
+/*
+ * Unmaps the slot buffer corresponding to the VA passed via `buf` argument.
+ */
+void buffer_unmap_internal(void *buf);
+
+#endif /* BUFFER_H */
diff --git a/lib/realm/include/granule.h b/lib/realm/include/granule.h
new file mode 100644
index 0000000..34595c5
--- /dev/null
+++ b/lib/realm/include/granule.h
@@ -0,0 +1,239 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef GRANULE_H
+#define GRANULE_H
+
+#include <assert.h>
+#include <atomics.h>
+#include <buffer.h>
+#include <granule_types.h>
+#include <memory.h>
+#include <spinlock.h>
+#include <status.h>
+
+static inline unsigned long granule_refcount_read_relaxed(struct granule *g)
+{
+	return __sca_read64(&g->refcount);
+}
+
+static inline unsigned long granule_refcount_read_acquire(struct granule *g)
+{
+	return __sca_read64_acquire(&g->refcount);
+}
+
+/*
+ * Sanity-check unlocked granule invariants.
+ *
+ * These invariants must hold for any granule which is unlocked.
+ *
+ * These invariants may not hold transiently while a granule is locked (e.g.
+ * when transitioning to/from delegated state).
+ *
+ * Note: this function is purely for debug/documentation purposes, and is not
+ * intended as a mechanism to ensure correctness.
+ */
+static inline void __granule_assert_unlocked_invariants(struct granule *g,
+							enum granule_state state)
+{
+	switch (state) {
+	case GRANULE_STATE_NS:
+		assert(granule_refcount_read_relaxed(g) == 0UL);
+		break;
+	case GRANULE_STATE_DELEGATED:
+		assert(g->refcount == 0UL);
+		break;
+	case GRANULE_STATE_RD:
+		/*
+		 * refcount is used to check if RD and associated granules can
+		 * be freed because they're no longer referenced by any other
+		 * object. Can be any non-negative number.
+		 */
+		break;
+	case GRANULE_STATE_REC:
+		assert(granule_refcount_read_relaxed(g) <= 1UL);
+		break;
+	case GRANULE_STATE_DATA:
+		assert(g->refcount == 0UL);
+		break;
+	case GRANULE_STATE_RTT:
+		assert(g->refcount >= 0UL);
+		break;
+	case GRANULE_STATE_REC_AUX:
+		assert(g->refcount == 0UL);
+		break;
+	default:
+		/* Unknown granule type */
+		assert(false);
+	}
+}
+
+/* Must be called with g->lock held */
+static inline enum granule_state granule_get_state(struct granule *g)
+{
+	return g->state;
+}
+
+/* Must be called with g->lock held */
+static inline void granule_set_state(struct granule *g,
+				     enum granule_state state)
+{
+	g->state = state;
+}
+
+/*
+ * Acquire the spinlock and then check expected state
+ * Fails if unexpected locking sequence detected.
+ * Also asserts if invariant conditions are met.
+ */
+static inline bool granule_lock_on_state_match(struct granule *g,
+				    enum granule_state expected_state)
+{
+	spinlock_acquire(&g->lock);
+
+	if (granule_get_state(g) != expected_state) {
+		spinlock_release(&g->lock);
+		return false;
+	}
+
+	__granule_assert_unlocked_invariants(g, expected_state);
+	return true;
+}
+
+/*
+ * Used when we're certain of the type of an object (e.g. because we hold a
+ * reference to it). In these cases we should never fail to acquire the lock.
+ */
+static inline void granule_lock(struct granule *g,
+				enum granule_state expected_state)
+{
+	__unused bool locked = granule_lock_on_state_match(g, expected_state);
+
+	assert(locked);
+}
+
+static inline void granule_unlock(struct granule *g)
+{
+	__granule_assert_unlocked_invariants(g, granule_get_state(g));
+	spinlock_release(&g->lock);
+}
+
+/* Transtion state to @new_state and unlock the granule */
+static inline void granule_unlock_transition(struct granule *g,
+					     enum granule_state new_state)
+{
+	granule_set_state(g, new_state);
+	granule_unlock(g);
+}
+
+unsigned long granule_addr(struct granule *g);
+struct granule *addr_to_granule(unsigned long addr);
+struct granule *find_granule(unsigned long addr);
+struct granule *find_lock_granule(unsigned long addr,
+				  enum granule_state expected_state);
+
+bool find_lock_two_granules(unsigned long addr1,
+			    enum granule_state expected_state1,
+			    struct granule **g1,
+			    unsigned long addr2,
+			    enum granule_state expected_state2,
+			    struct granule **g2);
+
+void granule_memzero(struct granule *g, enum buffer_slot slot);
+
+void granule_memzero_mapped(void *buf);
+
+/* Must be called with g->lock held */
+static inline void __granule_get(struct granule *g)
+{
+	g->refcount++;
+}
+
+/* Must be called with g->lock held */
+static inline void __granule_put(struct granule *g)
+{
+	assert(g->refcount > 0UL);
+	g->refcount--;
+}
+
+/* Must be called with g->lock held */
+static inline void __granule_refcount_inc(struct granule *g, unsigned long val)
+{
+	g->refcount += val;
+}
+
+/* Must be called with g->lock held */
+static inline void __granule_refcount_dec(struct granule *g, unsigned long val)
+{
+	assert(g->refcount >= val);
+	g->refcount -= val;
+}
+
+/*
+ * Atomically increments the reference counter of the granule.
+ */
+static inline void atomic_granule_get(struct granule *g)
+{
+	atomic_add_64(&g->refcount, 1UL);
+}
+
+/*
+ * Atomically decrements the reference counter of the granule.
+ */
+static inline void atomic_granule_put(struct granule *g)
+{
+	atomic_add_64(&g->refcount, -1L);
+}
+
+/*
+ * Atomically decrements the reference counter of the granule.
+ * Stores to memory with release semantics.
+ */
+static inline void atomic_granule_put_release(struct granule *g)
+{
+	unsigned long old_refcount __unused;
+
+	old_refcount = atomic_load_add_release_64(&g->refcount, -1L);
+	assert(old_refcount > 0UL);
+}
+
+/*
+ * Obtain a pointer to a locked unused granule at @addr if @addr is a valid
+ * granule physical address, the state of the granule at @addr is
+ * @expected_state, and the granule at @addr is unused.
+ *
+ * Returns:
+ *     struct granule if @addr is a valid granule physical address.
+ *     RMI_ERROR_INPUT if @addr is not aligned to the size of a granule.
+ *     RMI_ERROR_INPUT if @addr is out of range.
+ *     RMI_ERROR_INPUT if the state of the granule at @addr is not
+ *     @expected_state.
+ *     RMI_ERROR_IN_USE if the granule at @addr has a non-zero
+ *     reference count.
+ */
+static inline
+struct granule *find_lock_unused_granule(unsigned long addr,
+					 enum granule_state expected_state)
+{
+	struct granule *g;
+
+	g = find_lock_granule(addr, expected_state);
+	if (g == NULL) {
+		return status_ptr(RMI_ERROR_INPUT);
+	}
+
+	/*
+	 * Granules can have lock-free access (e.g. REC), thus using acquire
+	 * semantics to avoid race conditions.
+	 */
+	if (granule_refcount_read_acquire(g)) {
+		granule_unlock(g);
+		return status_ptr(RMI_ERROR_IN_USE);
+	}
+
+	return g;
+}
+
+#endif /* GRANULE_H */
diff --git a/lib/realm/include/granule_types.h b/lib/realm/include/granule_types.h
new file mode 100644
index 0000000..be69ccf
--- /dev/null
+++ b/lib/realm/include/granule_types.h
@@ -0,0 +1,216 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef GRANULE_TYPES_H
+#define GRANULE_TYPES_H
+
+#include <spinlock.h>
+
+/*
+ * Locking Order
+ * -------------
+ * Granules must be locked in a specific order to prevent deadlocks.
+ *
+ * We define two classes of granule states: `external` and `internal`.
+ *
+ * A granule state belongs to the `external` class iff _any_ parameter to _any_
+ * RMI command is an address of a granule which is expected to be in that state
+ * i.e the lock is only acquired if the granule state of the address in RMI
+ * command matches to that of the expected state.
+ *
+ * The following granule states are `external`:
+ *
+ * - GRANULE_STATE_NS
+ * - GRANULE_STATE_DELEGATED
+ * - GRANULE_STATE_RD
+ * - GRANULE_STATE_REC
+ *
+ * Otherwise a granule state is considered `internal`.
+ *
+ * The following granule states are `internal`:
+ *
+ * - GRANULE_STATE_RTT
+ * - GRANULE_STATE_DATA
+ * - GRANULE_STATE_REC_AUX
+ *
+ * The following locking rules must be followed in all cases:
+ *
+ * 1. Granules expected to be in an `external` state must be locked before
+ *    locking any granules in an `internal` state.
+ *
+ * 2. Granules expected to be in an `external` state must be locked in order
+ *    of their physical address, starting with the lowest address.
+ *
+ * 3. Once a granule expected to be in an `external` state has been locked, its
+ *    state must be checked against the expected state. If these do not match,
+ *    the granule must be unlocked and no further granules may be locked.
+ *
+ * 4. Granules in an `internal` state must be locked in order of state:
+ *    1. `RTT`
+ *    2. `DATA`
+ *    3. `REC_AUX`
+ *
+ * 5. Granules in the same `internal` state must be locked in the order defined
+ *    below for that specific state.
+ *
+ * A granule's state can be changed iff the granule is locked.
+ *
+ * Invariants
+ * ----------
+ * GRANULE_STATE_DELEGATED is special, in that it is the gateway between the
+ * non-secure and realm world.  We maintain the property that any unlocked
+ * granule with state == GRANULE_STATE_DELEGATED contains only zeroes; while
+ * locked these may contain non-zero values.
+ */
+
+enum granule_state {
+	/*
+	 * Non-Secure granule (external)
+	 *
+	 * Granule content is not protected by granule::lock, as it is always
+	 * subject to reads and writes from the NS world.
+	 */
+	GRANULE_STATE_NS,
+	/*
+	 * TODO: remove the next line when spec aligment is done
+	 * currently this has been renamed in alp03 and is needed for CBMC testbench
+	 */
+	GRANULE_STATE_UNDELEGATED = GRANULE_STATE_NS,
+	/*
+	 * Delegated Granule (external)
+	 *
+	 * Granule content is protected by granule::lock.
+	 *
+	 * No references are held on this granule type.
+	 */
+	GRANULE_STATE_DELEGATED,
+	/*
+	 * Realm Descriptor Granule (external)
+	 *
+	 * Granule content is protected by granule::lock.
+	 *
+	 * A reference is held on this granule:
+	 * - For each associated REC granule.
+	 *
+	 * The RD may only be destroyed when the following objects
+	 * have a reference count of zero:
+	 * - The root-level RTT
+	 */
+	GRANULE_STATE_RD,
+	/*
+	 * Realm Execution Context Granule (external)
+	 *
+	 * Granule content (see struct rec) comprises execution
+	 * context state and cached realm information copied from the RD.
+	 *
+	 * Execution context is not protected by granule::lock, because we can't
+	 * enter a Realm while holding the lock.
+	 *
+	 * The following rules with respect to the granule's reference apply:
+	 * - A reference is held on this granule when a REC is running.
+	 * - As REC cannot be run on two PEs at the same time, the maximum
+	 *   value of the reference count is one.
+	 * - When the REC in entered, the reference count is incremented
+	 *   (set to 1) atomically while granule::lock is held.
+	 * - When the REC exits, the reference counter is released (set to 0)
+	 *   atomically with store-release semantics without granule::lock being
+	 *   held.
+	 * - The RMM can access the granule's content on the entry and exit path
+	 *   from the REC while the reference is held.
+	 */
+	GRANULE_STATE_REC,
+	/*
+	 * Realm Execution Context auxiliary granule (internal)
+	 *
+	 * Granule auxiliary content is used to store any state that cannot
+	 * fit in the main REC page. This is typically used for context
+	 * save/restore of PE features like SVE, SME, etc.
+	 *
+	 * Granule content is not protected by granule::lock nor the reference
+	 * count. The RMM can access the content of the auxiliary granules
+	 * only while holding a lock or reference to the parent REC granule.
+	 *
+	 * The granule::lock is held during a state change to
+	 * GRANULE_STATE_REC_AUX and from GRANULE_STATE_REC_AUX.
+	 *
+	 * The complete internal locking order when changing REC_AUX
+	 * granule's state is:
+	 *
+	 * REC -> REC_AUX[0] -> REC_AUX[1] -> ... -> REC_AUX[n-1]
+	 */
+	GRANULE_STATE_REC_AUX,
+
+	/*
+	 * Data Granule (internal)
+	 *
+	 * Granule content is not protected by granule::lock, as it is always
+	 * subject to reads and writes from within a Realm.
+	 *
+	 * A granule in this state is always referenced from exactly one entry
+	 * in an RTT granule which must be locked before locking this granule.
+	 * Only a single DATA granule can be locked at a time.
+	 * The complete internal locking order for DATA granules is:
+	 *
+	 * RD -> RTT -> RTT -> ... -> DATA
+	 *
+	 * No references are held on this granule type.
+	 */
+	GRANULE_STATE_DATA,
+	/*
+	 * RTT Granule (internal)
+	 *
+	 * Granule content is protected by granule::lock.
+	 *
+	 * Granule content is protected by granule::lock, but hardware
+	 * translation table walks may read the RTT at any point in time.
+	 * TODO: do we wish/need to use hardware access flag management?
+	 *
+	 * Multiple granules in this state can only be locked at the same time
+	 * if they are part of the same tree, and only in topological order
+	 * from root to leaf. The topological order of concatenated root level
+	 * RTTs is from lowest address to highest address.
+	 *
+	 * The complete internal locking order for RTT granules is:
+	 *
+	 * RD -> [RTT] -> ... -> RTT
+	 *
+	 * A reference is held on this granule for each entry in the RTT that
+	 * refers to a granule:
+	 *   - Table s2tte.
+	 *   - Valid s2tte.
+	 *   - Valid_NS s2tte.
+	 *   - Assigned s2tte.
+	 */
+	GRANULE_STATE_RTT
+};
+
+struct granule {
+	/*
+	 * @lock protects the struct granule itself. Take this lock whenever
+	 * inspecting or modifying any other fields in this struct.
+	 */
+	spinlock_t lock;
+
+	/*
+	 * @state is the state of the granule.
+	 */
+	enum granule_state state;
+
+	/*
+	 * @refcount counts RMM and realm references to this granule with the
+	 * following rules:
+	 *  - The @state of the granule cannot be modified when @refcount
+	 *    is non-zero.
+	 *  - When a granule is mapped into the RMM, either the granule lock
+	 *    must be held or a reference must be held.
+	 *  - The content of the granule itself can be modified when
+	 *    @refcount is non-zero without holding @lock.  However, specific
+	 *    types of granules may impose further restrictions on concurrent
+	 *    access.
+	 */
+	unsigned long refcount;
+};
+
+#endif /* GRANULE_TYPES_H */
diff --git a/lib/realm/include/realm.h b/lib/realm/include/realm.h
new file mode 100644
index 0000000..e34fa10
--- /dev/null
+++ b/lib/realm/include/realm.h
@@ -0,0 +1,246 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef REALM_H
+#define REALM_H
+
+#include <assert.h>
+#include <measurement.h>
+#include <memory.h>
+#include <rec.h>
+#include <table.h>
+
+#define REALM_STATE_NEW		0
+#define REALM_STATE_ACTIVE	1
+#define REALM_STATE_SYSTEM_OFF	2
+
+/*
+ * Stage 2 configuration of the Realm
+ */
+struct realm_s2_context {
+	/* Number of IPA bits */
+	unsigned int ipa_bits;
+
+	/* Starting level of the stage 2 translation */
+	int s2_starting_level;
+
+	/* Number of concatenated starting level rtts */
+	unsigned int num_root_rtts;
+
+	/* First level RTT, pointed to by Realm TTBR */
+	struct granule *g_rtt;
+
+	/* Virtual Machine Identifier */
+	unsigned int vmid;
+
+	/*
+	 * TODO: we will need other translation regime state, e.g. TCR, MAIR(?).
+	 */
+};
+
+/* struct rd is protected by the rd granule lock */
+struct rd {
+	/*
+	 * 'state' & 'rec_count' are only accessed through dedicated
+	 * primitives where the following rules apply:
+	 *
+	 * (1) To write the value, the RMI handler must hold the rd granule
+	 *     lock and use a single copy atomic store with release semantics.
+	 *
+	 * (2) To read the value, the RMI handler must either:
+	 *     - Hold the rd granule lock and use a 64-bit single copy
+	 *       atomic load, or
+	 *     - Hold the rd reference count and use a 64-bit single copy
+	 *       atomic load with acquire semantics.
+	 *
+	 * Other members of the structure are accessed with rd granule lock held.
+	 */
+	/* 64-bit variable accessed with READ64/WRITE64/ACQUIRE semantic */
+	unsigned long state;
+
+	/* Reference count */
+	unsigned long rec_count;
+
+	/* Stage 2 configuration of the Realm */
+	struct realm_s2_context s2_ctx;
+
+	/* Number of auxiliary REC granules for the Realm */
+	unsigned int num_rec_aux;
+
+	/* Algorithm to use for measurements */
+	enum hash_algo algorithm;
+
+	/* Realm measurement */
+	unsigned char measurement[MEASUREMENT_SLOT_NR][MAX_MEASUREMENT_SIZE];
+
+	/* Realm Personalization Value */
+	unsigned char rpv[RPV_SIZE];
+};
+COMPILER_ASSERT(sizeof(struct rd) <= GRANULE_SIZE);
+
+/*
+ * Sets the rd's state while holding the rd granule lock.
+ */
+static inline void set_rd_state(struct rd *rd, unsigned long state)
+{
+	SCA_WRITE64_RELEASE(&rd->state, state);
+}
+
+/*
+ * Gets the rd's state while holding the rd granule lock.
+ */
+static inline unsigned long get_rd_state_locked(struct rd *rd)
+{
+	return SCA_READ64(&rd->state);
+}
+
+/*
+ * Gets the rd's state while holding the rd's reference count, without
+ * holding the rd granule lock.
+ */
+static inline unsigned long get_rd_state_unlocked(struct rd *rd)
+{
+	return SCA_READ64_ACQUIRE(&rd->state);
+}
+
+/*
+ * Sets the rd's rec_count while holding the rd granule lock.
+ */
+static inline void set_rd_rec_count(struct rd *rd, unsigned long val)
+{
+	SCA_WRITE64_RELEASE(&rd->rec_count, val);
+}
+
+/*
+ * Gets the rd's rec_count while holding the rd granule lock.
+ */
+static inline unsigned long get_rd_rec_count_locked(struct rd *rd)
+{
+	return SCA_READ64(&rd->rec_count);
+}
+
+/*
+ * Gets the rd's rec_count while holding the rd's reference count, without
+ * holding the rd granule lock.
+ */
+static inline unsigned long get_rd_rec_count_unlocked(struct rd *rd)
+{
+	return SCA_READ64_ACQUIRE(&rd->rec_count);
+}
+
+static inline unsigned long realm_ipa_bits(struct rd *rd)
+{
+	return rd->s2_ctx.ipa_bits;
+}
+
+/*
+ * Gets the rd's IPA size.
+ */
+static inline unsigned long realm_ipa_size(struct rd *rd)
+{
+	return (1UL << realm_ipa_bits(rd));
+}
+
+static inline unsigned long realm_par_size(struct rd *rd)
+{
+	return (realm_ipa_size(rd) / 2U);
+}
+
+static inline int realm_rtt_starting_level(struct rd *rd)
+{
+	return rd->s2_ctx.s2_starting_level;
+}
+
+/*
+ * Checks that 'address' is within container's parameters.
+ *
+ * 'container_base' is the start address of the container.
+ * 'container_end' is the first address after the container.
+ * The container must not overflow.
+ */
+static inline bool addr_is_contained(unsigned long container_base,
+				     unsigned long container_end,
+				     unsigned long address)
+{
+	assert(container_base <= (container_end - 1));
+	return address >= container_base && address <= (container_end - 1);
+}
+
+/*
+ * Checks that region is within container's parameters.
+ *
+ * 'container_base' is the start address of the container.
+ * 'container_end' is the first address after the container.
+ * The container must not overflow.
+ * 'region_base' is the start address of the region.
+ * 'region_end' is the first address after the region.
+ * The region must not overflow.
+ */
+static inline bool region_is_contained(unsigned long container_base,
+				       unsigned long container_end,
+				       unsigned long region_base,
+				       unsigned long region_end)
+{
+	assert(region_base <= (region_end - 1UL));
+	return addr_is_contained(container_base, container_end, region_base) &&
+	       addr_is_contained(container_base, container_end, region_end - 1UL);
+}
+
+static inline unsigned long rec_ipa_size(struct rec *rec)
+{
+	return (1UL << rec->realm_info.ipa_bits);
+}
+
+static inline unsigned long rec_par_size(struct rec *rec)
+{
+	return (rec_ipa_size(rec) / 2U);
+}
+
+static inline bool addr_in_rec_par(struct rec *rec, unsigned long addr)
+{
+	return (addr < rec_par_size(rec));
+}
+
+static inline bool region_in_rec_par(struct rec *rec,
+				     unsigned long base, unsigned long end)
+{
+	return region_is_contained(0UL, rec_par_size(rec), base, end);
+}
+
+static inline bool addr_in_par(struct rd *rd, unsigned long addr)
+{
+	return (addr < realm_par_size(rd));
+}
+
+enum s2_walk_status {
+	/* Successful translation */
+	WALK_SUCCESS,
+	/* Parameter 'ipa' is unaligned or is not Protected IPA */
+	WALK_INVALID_PARAMS,
+	/* Mapping is not in the page table */
+	WALK_FAIL
+};
+
+struct s2_walk_result {
+	unsigned long pa;
+	unsigned long rtt_level;
+	enum ripas ripas;
+	bool destroyed;
+	struct granule *llt;
+};
+
+static inline bool s2_walk_result_match_ripas(struct s2_walk_result *res,
+					      enum ripas ripas)
+{
+	return (!res->destroyed && (res->ripas == ripas));
+}
+
+enum s2_walk_status realm_ipa_to_pa(struct rd *rd,
+				    unsigned long ipa,
+				    struct s2_walk_result *res);
+
+void realm_ipa_get_ripas(struct rec *rec, unsigned long ipa,
+			 enum ripas *ripas_ptr, bool *s2tte_destroyed);
+#endif /* REALM_H */
diff --git a/lib/realm/include/rec.h b/lib/realm/include/rec.h
new file mode 100644
index 0000000..457226d
--- /dev/null
+++ b/lib/realm/include/rec.h
@@ -0,0 +1,243 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef REC_H
+#define REC_H
+
+#ifndef __ASSEMBLER__
+
+#include <arch.h>
+#include <attestation_token.h>
+#include <fpu_helpers.h>
+#include <gic.h>
+#include <memory_alloc.h>
+#include <ripas.h>
+#include <sizes.h>
+#include <smc-rmi.h>
+#include <utils_def.h>
+
+struct granule;
+
+/*
+ * System registers whose contents are specific to a REC.
+ */
+struct sysreg_state {
+	unsigned long sp_el0;
+	unsigned long sp_el1;
+	unsigned long elr_el1;
+	unsigned long spsr_el1;
+	unsigned long pmcr_el0;
+	unsigned long pmuserenr_el0;
+	unsigned long tpidrro_el0;
+	unsigned long tpidr_el0;
+	unsigned long csselr_el1;
+	unsigned long sctlr_el1;
+	unsigned long actlr_el1;
+	unsigned long cpacr_el1;
+	unsigned long zcr_el1;
+	unsigned long ttbr0_el1;
+	unsigned long ttbr1_el1;
+	unsigned long tcr_el1;
+	unsigned long esr_el1;
+	unsigned long afsr0_el1;
+	unsigned long afsr1_el1;
+	unsigned long far_el1;
+	unsigned long mair_el1;
+	unsigned long vbar_el1;
+	unsigned long contextidr_el1;
+	unsigned long tpidr_el1;
+	unsigned long amair_el1;
+	unsigned long cntkctl_el1;
+	unsigned long par_el1;
+	unsigned long mdscr_el1;
+	unsigned long mdccint_el1;
+	unsigned long disr_el1;
+	unsigned long mpam0_el1;
+
+	/* Timer Registers */
+	unsigned long cnthctl_el2;
+	unsigned long cntvoff_el2;
+	unsigned long cntpoff_el2;
+	unsigned long cntp_ctl_el0;
+	unsigned long cntp_cval_el0;
+	unsigned long cntv_ctl_el0;
+	unsigned long cntv_cval_el0;
+
+	/* GIC Registers */
+	struct gic_cpu_state gicstate;
+
+	/* TODO MPAM */
+	/* TODO Performance Monitor Registers */
+	/* TODO Pointer Authentication Registers */
+
+	unsigned long vmpidr_el2;	/* restored only */
+	unsigned long hcr_el2;		/* restored only */
+};
+
+/*
+ * System registers whose contents are
+ * common across all RECs in a Realm.
+ */
+struct common_sysreg_state {
+	unsigned long vttbr_el2;
+	unsigned long vtcr_el2;
+	unsigned long hcr_el2;
+};
+
+/*
+ * This structure is aligned on cache line size to avoid cache line trashing
+ * when allocated as an array for N CPUs.
+ */
+struct ns_state {
+	struct sysreg_state sysregs;
+	unsigned long sp_el0;
+	unsigned long icc_sre_el2;
+	struct fpu_state *fpu; /* FPU/SVE saved lazily. */
+	struct sve_state *sve;
+} __attribute__((aligned(CACHE_WRITEBACK_GRANULE)));
+
+/*
+ * This structure contains pointers to data that is allocated
+ * in auxilary granules.
+ */
+struct rec_aux_data {
+	uint8_t *attest_heap_buf; /* Pointer to the heap buffer of this REC. */
+};
+
+/* This structure is used for storing FPU/SIMD context for realm. */
+struct rec_fpu_context {
+	struct fpu_state fpu;
+	bool used;
+};
+
+struct rec {
+	struct granule *g_rec; /* the granule in which this rec lives */
+	unsigned long rec_idx; /* Which rec is this */
+	bool runnable;
+
+	unsigned long regs[31];
+	unsigned long pc;
+	unsigned long pstate;
+
+	struct sysreg_state sysregs;
+	struct common_sysreg_state common_sysregs;
+
+	struct {
+		unsigned long start;
+		unsigned long end;
+		unsigned long addr;
+		enum ripas ripas;
+	} set_ripas;
+
+	/*
+	 * Common values across all RECs in a Realm.
+	 */
+	struct {
+		unsigned long ipa_bits;
+		int s2_starting_level;
+		struct granule *g_rtt;
+		struct granule *g_rd;
+	} realm_info;
+
+	struct {
+		/*
+		 * The contents of the *_EL2 system registers at the last time
+		 * the REC exited to the host due to a synchronous exception.
+		 * These are the unsanitized register values which may differ
+		 * from the value returned to the host in rec_exit structure.
+		 */
+		unsigned long esr;
+		unsigned long hpfar;
+		unsigned long far;
+	} last_run_info;
+
+	/* Structure for storing FPU/SIMD context for realm. */
+	struct rec_fpu_context fpu_ctx;
+
+	/* Pointer to per-cpu non-secure state */
+	struct ns_state *ns;
+
+	struct {
+		/*
+		 * Set to 'true' when there is a pending PSCI
+		 * command that must be resolved by the host.
+		 * The command is encoded in rec->regs[0].
+		 *
+		 * A REC with pending PSCI is not schedulable.
+		 */
+		bool pending;
+	} psci_info;
+
+	/* Number of auxiliary granules */
+	unsigned int num_rec_aux;
+
+	/* Addresses of auxiliary granules */
+	struct granule *g_aux[MAX_REC_AUX_GRANULES];
+	struct rec_aux_data aux_data;
+
+	unsigned char rmm_realm_token_buf[SZ_1K];
+	struct q_useful_buf_c rmm_realm_token;
+
+	struct token_sign_ctx token_sign_ctx;
+
+	/* Buffer allocation info used for heap init and management */
+	struct {
+		struct buffer_alloc_ctx ctx;
+		bool ctx_initialised;
+	} alloc_info;
+
+	struct {
+		unsigned long vsesr_el2;
+		bool inject;
+	} serror_info;
+
+	/* True if host call is pending */
+	bool host_call;
+};
+COMPILER_ASSERT(sizeof(struct rec) <= GRANULE_SIZE);
+
+/*
+ * Check that mpidr has a valid value with all fields except
+ * Aff3[39:32]:Aff2[23:16]:Aff1[15:8]:Aff0[3:0] set to 0.
+ */
+static inline bool mpidr_is_valid(unsigned long mpidr)
+{
+	return (mpidr & ~(MASK(MPIDR_EL2_AFF0) |
+			  MASK(MPIDR_EL2_AFF1) |
+			  MASK(MPIDR_EL2_AFF2) |
+			  MASK(MPIDR_EL2_AFF3))) == 0ULL;
+}
+
+/*
+ * Calculate REC index from mpidr value.
+ * index = Aff3[39:32]:Aff2[23:16]:Aff1[15:8]:Aff0[3:0]
+ */
+static inline unsigned long mpidr_to_rec_idx(unsigned long mpidr)
+{
+	return (MPIDR_EL2_AFF(0, mpidr) +
+		MPIDR_EL2_AFF(1, mpidr) +
+		MPIDR_EL2_AFF(2, mpidr) +
+		MPIDR_EL2_AFF(3, mpidr));
+}
+
+void rec_run_loop(struct rec *rec, struct rmi_rec_exit *rec_exit);
+
+unsigned long smc_rec_create(unsigned long rec_addr,
+			     unsigned long rd_addr,
+			     unsigned long rec_params_addr);
+
+unsigned long smc_rec_destroy(unsigned long rec_addr);
+
+unsigned long smc_rec_enter(unsigned long rec_addr,
+			    unsigned long rec_run_addr);
+
+void inject_serror(struct rec *rec, unsigned long vsesr);
+
+void emulate_stage2_data_abort(struct rec *rec, struct rmi_rec_exit *exit,
+			       unsigned long rtt_level);
+
+#endif /* __ASSEMBLER__ */
+
+#endif /* REC_H */
diff --git a/lib/realm/include/ripas.h b/lib/realm/include/ripas.h
new file mode 100644
index 0000000..5aca101
--- /dev/null
+++ b/lib/realm/include/ripas.h
@@ -0,0 +1,23 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef RIPAS_H
+#define RIPAS_H
+
+/* The RmmRipas enumeration representing realm IPA state */
+#define	EMPTY	0U	/* Unused IPA location */
+#define	RAM	1U	/* Private code or data owned by the Realm */
+
+/*
+ * The RmiRipas enumeration representing realm IPA state.
+ *
+ * Map RmmRipas to RmiRipas to simplify code/decode operations.
+ */
+enum ripas {
+	RMI_EMPTY = EMPTY,
+	RMI_RAM = RAM
+};
+
+#endif /* RIPAS_H */
diff --git a/lib/realm/include/smc-rmi.h b/lib/realm/include/smc-rmi.h
new file mode 100644
index 0000000..0535436
--- /dev/null
+++ b/lib/realm/include/smc-rmi.h
@@ -0,0 +1,423 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef SMC_RMI_H
+#define SMC_RMI_H
+
+#include <measurement.h>
+#include <smc.h>
+
+/*
+ * This file describes the Realm Management Interface (RMI) Application Binary
+ * Interface (ABI) for SMC calls made from Non-secure state to the RMM and
+ * serviced by the RMM.
+ *
+ * See doc/rmm_interface.md for more details.
+ */
+
+/*
+ * The major version number of the RMI implementation.  Increase this whenever
+ * the binary format or semantics of the SMC calls change.
+ */
+#define RMI_ABI_VERSION_MAJOR		(56U)
+
+/*
+ * The minor version number of the RMI implementation.  Increase this when
+ * a bug is fixed, or a feature is added without breaking binary compatibility.
+ */
+#define RMI_ABI_VERSION_MINOR		(0U)
+
+#define RMI_ABI_VERSION			((RMI_ABI_VERSION_MAJOR << 16U) | \
+					RMI_ABI_VERSION_MINOR)
+
+#define RMI_ABI_VERSION_GET_MAJOR(_version) ((_version) >> 16U)
+#define RMI_ABI_VERSION_GET_MINOR(_version) ((_version) & 0xFFFFU)
+
+#define SMC64_RMI_FID(_offset)		SMC64_STD_FID(RMI, _offset)
+
+#define IS_SMC64_RMI_FID(_fid)		IS_SMC64_STD_FAST_IN_RANGE(RMI, _fid)
+
+/*
+ * The number of GPRs (starting from X0) that are
+ * configured by the host when a REC is created.
+ */
+#define REC_CREATE_NR_GPRS		(8U)
+
+#define REC_PARAMS_FLAG_RUNNABLE	(1UL << 0U)
+
+/*
+ * The number of GPRs (starting from X0) per voluntary exit context.
+ * Per SMCCC.
+ */
+#define REC_EXIT_NR_GPRS		(31U)
+
+/* RmiHashAlgorithm type */
+#define RMI_HASH_ALGO_SHA256	HASH_ALGO_SHA256
+#define RMI_HASH_ALGO_SHA512	HASH_ALGO_SHA512
+
+/* Maximum number of Interrupt Controller List Registers */
+#define REC_GIC_NUM_LRS			(16U)
+
+/* Maximum number of auxiliary granules required for a REC */
+#define MAX_REC_AUX_GRANULES		(16U)
+
+#define REC_ENTRY_FLAG_EMUL_MMIO	(1UL << 0U)
+#define REC_ENTRY_FLAG_INJECT_SEA	(1UL << 1U)
+
+/* Flags to specify if WFI/WFE should be trapped to host */
+#define REC_ENTRY_FLAG_TRAP_WFI		(1UL << 2U)
+#define REC_ENTRY_FLAG_TRAP_WFE		(1UL << 3U)
+
+/*
+ * RmiRecExitReason represents the reason for a REC exit.
+ * This is returned to NS hosts via RMI_REC_ENTER::run_ptr.
+ */
+#define RMI_EXIT_SYNC			(0U)
+#define RMI_EXIT_IRQ			(1U)
+#define RMI_EXIT_FIQ			(2U)
+#define RMI_EXIT_PSCI			(3U)
+#define RMI_EXIT_RIPAS_CHANGE		(4U)
+#define RMI_EXIT_HOST_CALL		(5U)
+#define RMI_EXIT_SERROR			(6U)
+
+/* RmiRttEntryState represents the state of an RTTE */
+#define RMI_RTT_STATE_UNASSIGNED	(0U)
+#define RMI_RTT_STATE_DESTROYED		(1U)
+#define RMI_RTT_STATE_ASSIGNED		(2U)
+#define RMI_RTT_STATE_TABLE		(3U)
+#define RMI_RTT_STATE_VALID_NS		(4U)
+
+/* no parameters */
+#define SMC_RMM_VERSION				SMC64_RMI_FID(U(0x0))
+
+/*
+ * arg0 == target granule address
+ */
+#define SMC_RMM_GRANULE_DELEGATE		SMC64_RMI_FID(U(0x1))
+
+/*
+ * arg0 == target granule address
+ */
+#define SMC_RMM_GRANULE_UNDELEGATE		SMC64_RMI_FID(U(0x2))
+
+/* RmiDataMeasureContent type */
+#define RMI_NO_MEASURE_CONTENT 0
+#define RMI_MEASURE_CONTENT  1
+
+/*
+ * arg0 == data address
+ * arg1 == RD address
+ * arg2 == map address
+ * arg3 == SRC address
+ * arg4 == flags
+ */
+#define SMC_RMM_DATA_CREATE			SMC64_RMI_FID(U(0x3))
+
+/*
+ * arg0 == data address
+ * arg1 == RD address
+ * arg2 == map address
+ */
+#define SMC_RMM_DATA_CREATE_UNKNOWN		SMC64_RMI_FID(U(0x4))
+
+/*
+ * arg0 == RD address
+ * arg1 == map address
+ */
+#define SMC_RMM_DATA_DESTROY			SMC64_RMI_FID(U(0x5))
+
+/*
+ * arg0 == RD address
+ */
+#define SMC_RMM_REALM_ACTIVATE			SMC64_RMI_FID(U(0x7))
+
+/*
+ * arg0 == RD address
+ * arg1 == struct rmi_realm_params addr
+ */
+#define SMC_RMM_REALM_CREATE			SMC64_RMI_FID(U(0x8))
+
+/*
+ * arg0 == RD address
+ */
+#define SMC_RMM_REALM_DESTROY			SMC64_RMI_FID(U(0x9))
+
+/*
+ * arg0 == REC address
+ * arg1 == RD address
+ * arg2 == struct rmm_rec address
+ */
+#define SMC_RMM_REC_CREATE			SMC64_RMI_FID(U(0xA))
+
+/*
+ * arg0 == REC address
+ */
+#define SMC_RMM_REC_DESTROY			SMC64_RMI_FID(U(0xB))
+
+/*
+ * arg0 == rec address
+ * arg1 == rec_run address
+ */
+#define SMC_RMM_REC_ENTER			SMC64_RMI_FID(U(0xC))
+
+/*
+ * arg0 == RTT address
+ * arg1 == RD address
+ * arg2 == map address
+ * arg3 == level
+ */
+#define SMC_RMM_RTT_CREATE			SMC64_RMI_FID(U(0xD))
+
+/*
+ * arg0 == RTT address
+ * arg1 == RD address
+ * arg2 == map address
+ * arg3 == level
+ */
+#define SMC_RMM_RTT_DESTROY			SMC64_RMI_FID(U(0xE))
+
+/*
+ * arg0 == RD address
+ * arg1 == map address
+ * arg2 == level
+ * arg3 == s2tte
+ */
+#define SMC_RMM_RTT_MAP_UNPROTECTED		SMC64_RMI_FID(U(0xF))
+
+/*
+ * arg0 == RD address
+ * arg1 == map address
+ * arg2 == level
+ * ret1 == level
+ * ret2 == s2tte type
+ * ret3 == s2tte
+ * ret4 == ripas
+ */
+#define SMC_RMM_RTT_READ_ENTRY			SMC64_RMI_FID(U(0x11))
+
+/*
+ * arg0 == RD address
+ * arg1 == map address
+ * arg2 == level
+ */
+#define SMC_RMM_RTT_UNMAP_UNPROTECTED		SMC64_RMI_FID(U(0x12))
+
+/*
+ * arg0 == calling rec address
+ * arg1 == target rec address
+ */
+#define SMC_RMM_PSCI_COMPLETE			SMC64_RMI_FID(U(0x14))
+
+/*
+ * arg0 == Feature register index
+ */
+#define SMC_RMM_FEATURES			SMC64_RMI_FID(U(0x15))
+
+/*
+ * arg0 == RTT address
+ * arg1 == RD address
+ * arg2 == map address
+ * arg3 == level
+ */
+#define SMC_RMM_RTT_FOLD			SMC64_RMI_FID(U(0x16))
+
+/*
+ * arg0 == RD address
+ */
+#define SMC_RMM_REC_AUX_COUNT			SMC64_RMI_FID(U(0x17))
+
+/*
+ * arg1 == RD address
+ * arg2 == map address
+ * arg3 == level
+ */
+#define SMC_RMM_RTT_INIT_RIPAS			SMC64_RMI_FID(U(0x18))
+
+/*
+ * arg0 == RD address
+ * arg1 == REC address
+ * arg2 == map address
+ * arg3 == level
+ * arg4 == ripas
+ */
+#define SMC_RMM_RTT_SET_RIPAS			SMC64_RMI_FID(U(0x19))
+
+/* Size of Realm Personalization Value */
+#define RPV_SIZE		64
+
+/*
+ * The Realm attribute parameters are shared by the Host via
+ * RMI_REALM_CREATE::params_ptr. The values can be observed or modified
+ * either by the Host or by the Realm.
+ */
+struct rmi_realm_params {
+	/* Realm feature register 0 */
+	SET_MEMBER(unsigned long features_0, 0, 0x100);		/* Offset 0 */
+	/* Measurement algorithm */
+	SET_MEMBER(unsigned char hash_algo, 0x100, 0x400);	/* 0x100 */
+	/* Realm Personalization Value */
+	SET_MEMBER(unsigned char rpv[RPV_SIZE], 0x400, 0x800);	/* 0x400 */
+	SET_MEMBER(struct {
+			/* Virtual Machine Identifier */
+			unsigned short vmid;			/* 0x800 */
+			/* Realm Translation Table base */
+			unsigned long rtt_base;			/* 0x808 */
+			/* RTT starting level */
+			long rtt_level_start;			/* 0x810 */
+			/* Number of starting level RTTs */
+			unsigned int rtt_num_start;		/* 0x818 */
+		   }, 0x800, 0x1000);
+};
+
+COMPILER_ASSERT(sizeof(struct rmi_realm_params) == 0x1000);
+
+COMPILER_ASSERT(offsetof(struct rmi_realm_params, features_0) == 0);
+COMPILER_ASSERT(offsetof(struct rmi_realm_params, hash_algo) == 0x100);
+COMPILER_ASSERT(offsetof(struct rmi_realm_params, rpv) == 0x400);
+COMPILER_ASSERT(offsetof(struct rmi_realm_params, vmid) == 0x800);
+COMPILER_ASSERT(offsetof(struct rmi_realm_params, rtt_base) == 0x808);
+COMPILER_ASSERT(offsetof(struct rmi_realm_params, rtt_level_start) == 0x810);
+COMPILER_ASSERT(offsetof(struct rmi_realm_params, rtt_num_start) == 0x818);
+
+/*
+ * The REC attribute parameters are shared by the Host via
+ * MI_REC_CREATE::params_ptr. The values can be observed or modified
+ * either by the Host or by the Realm which owns the REC.
+ */
+struct rmi_rec_params {
+	/* Flags */
+	SET_MEMBER(unsigned long flags, 0, 0x100);	/* Offset 0 */
+	/* MPIDR of the REC */
+	SET_MEMBER(unsigned long mpidr, 0x100, 0x200);	/* 0x100 */
+	/* Program counter */
+	SET_MEMBER(unsigned long pc, 0x200, 0x300);	/* 0x200 */
+	/* General-purpose registers */
+	SET_MEMBER(unsigned long gprs[REC_CREATE_NR_GPRS], 0x300, 0x800); /* 0x300 */
+	SET_MEMBER(struct {
+			/* Number of auxiliary Granules */
+			unsigned long num_aux;			/* 0x800 */
+			/* Addresses of auxiliary Granules */
+			unsigned long aux[MAX_REC_AUX_GRANULES];/* 0x808 */
+		   }, 0x800, 0x1000);
+};
+
+COMPILER_ASSERT(sizeof(struct rmi_rec_params) == 0x1000);
+
+COMPILER_ASSERT(offsetof(struct rmi_rec_params, flags) == 0);
+COMPILER_ASSERT(offsetof(struct rmi_rec_params, mpidr) == 0x100);
+COMPILER_ASSERT(offsetof(struct rmi_rec_params, pc) == 0x200);
+COMPILER_ASSERT(offsetof(struct rmi_rec_params, gprs) == 0x300);
+COMPILER_ASSERT(offsetof(struct rmi_rec_params, num_aux) == 0x800);
+COMPILER_ASSERT(offsetof(struct rmi_rec_params, aux) == 0x808);
+
+/*
+ * Structure contains data passed from the Host to the RMM on REC entry
+ */
+struct rmi_rec_entry {
+	/* Flags */
+	SET_MEMBER(unsigned long flags, 0, 0x200);	/* Offset 0 */
+	/* General-purpose registers */
+	SET_MEMBER(unsigned long gprs[REC_EXIT_NR_GPRS], 0x200, 0x300); /* 0x200 */
+	SET_MEMBER(struct {
+			/* GICv3 Hypervisor Control Register */
+			unsigned long gicv3_hcr;			/* 0x300 */
+			/* GICv3 List Registers */
+			unsigned long gicv3_lrs[REC_GIC_NUM_LRS];	/* 0x308 */
+		   }, 0x300, 0x800);
+};
+
+COMPILER_ASSERT(sizeof(struct rmi_rec_entry) == 0x800);
+
+COMPILER_ASSERT(offsetof(struct rmi_rec_entry, flags) == 0);
+COMPILER_ASSERT(offsetof(struct rmi_rec_entry, gprs) == 0x200);
+COMPILER_ASSERT(offsetof(struct rmi_rec_entry, gicv3_hcr) == 0x300);
+COMPILER_ASSERT(offsetof(struct rmi_rec_entry, gicv3_lrs) == 0x308);
+
+/*
+ * Structure contains data passed from the RMM to the Host on REC exit
+ */
+struct rmi_rec_exit {
+	/* Exit reason */
+	SET_MEMBER(unsigned long exit_reason, 0, 0x100);/* Offset 0 */
+	SET_MEMBER(struct {
+			/* Exception Syndrome Register */
+			unsigned long esr;		/* 0x100 */
+			/* Fault Address Register */
+			unsigned long far;		/* 0x108 */
+			/* Hypervisor IPA Fault Address register */
+			unsigned long hpfar;		/* 0x110 */
+		   }, 0x100, 0x200);
+	/* General-purpose registers */
+	SET_MEMBER(unsigned long gprs[REC_EXIT_NR_GPRS], 0x200, 0x300); /* 0x200 */
+	SET_MEMBER(struct {
+			/* GICv3 Hypervisor Control Register */
+			unsigned long gicv3_hcr;	/* 0x300 */
+			/* GICv3 List Registers */
+			unsigned long gicv3_lrs[REC_GIC_NUM_LRS]; /* 0x308 */
+			/* GICv3 Maintenance Interrupt State Register */
+			unsigned long gicv3_misr;	/* 0x388 */
+			/* GICv3 Virtual Machine Control Register */
+			unsigned long gicv3_vmcr;	/* 0x390 */
+		   }, 0x300, 0x400);
+	SET_MEMBER(struct {
+			/* Counter-timer Physical Timer Control Register */
+			unsigned long cntp_ctl;		/* 0x400 */
+			/* Counter-timer Physical Timer CompareValue Register */
+			unsigned long cntp_cval;	/* 0x408 */
+			/* Counter-timer Virtual Timer Control Register */
+			unsigned long cntv_ctl;		/* 0x410 */
+			/* Counter-timer Virtual Timer CompareValue Register */
+			unsigned long cntv_cval;	/* 0x418 */
+		   }, 0x400, 0x500);
+	SET_MEMBER(struct {
+			/* Base address of pending RIPAS change */
+			unsigned long ripas_base;	/* 0x500 */
+			/* Size of pending RIPAS change */
+			unsigned long ripas_size;	/* 0x508 */
+			/* RIPAS value of pending RIPAS change */
+			unsigned char ripas_value;	/* 0x510 */
+		   }, 0x500, 0x600);
+	/* Host call immediate value */
+	SET_MEMBER(unsigned int imm, 0x600, 0x800);	/* 0x600 */
+};
+
+COMPILER_ASSERT(sizeof(struct rmi_rec_exit) == 0x800);
+
+COMPILER_ASSERT(offsetof(struct rmi_rec_exit, exit_reason) == 0);
+COMPILER_ASSERT(offsetof(struct rmi_rec_exit, esr) == 0x100);
+COMPILER_ASSERT(offsetof(struct rmi_rec_exit, far) == 0x108);
+COMPILER_ASSERT(offsetof(struct rmi_rec_exit, hpfar) == 0x110);
+COMPILER_ASSERT(offsetof(struct rmi_rec_exit, gprs) == 0x200);
+COMPILER_ASSERT(offsetof(struct rmi_rec_exit, gicv3_hcr) == 0x300);
+COMPILER_ASSERT(offsetof(struct rmi_rec_exit, gicv3_lrs) == 0x308);
+COMPILER_ASSERT(offsetof(struct rmi_rec_exit, gicv3_misr) == 0x388);
+COMPILER_ASSERT(offsetof(struct rmi_rec_exit, gicv3_vmcr) == 0x390);
+COMPILER_ASSERT(offsetof(struct rmi_rec_exit, cntp_ctl) == 0x400);
+COMPILER_ASSERT(offsetof(struct rmi_rec_exit, cntp_cval) == 0x408);
+COMPILER_ASSERT(offsetof(struct rmi_rec_exit, cntv_ctl) == 0x410);
+COMPILER_ASSERT(offsetof(struct rmi_rec_exit, cntv_cval) == 0x418);
+COMPILER_ASSERT(offsetof(struct rmi_rec_exit, ripas_base) == 0x500);
+COMPILER_ASSERT(offsetof(struct rmi_rec_exit, ripas_size) == 0x508);
+COMPILER_ASSERT(offsetof(struct rmi_rec_exit, ripas_value) == 0x510);
+COMPILER_ASSERT(offsetof(struct rmi_rec_exit, imm) == 0x600);
+
+/*
+ * Structure contains shared information between RMM and Host
+ * during REC entry and REC exit.
+ */
+struct rmi_rec_run {
+	/* Entry information */
+	SET_MEMBER(struct rmi_rec_entry entry, 0, 0x800);	/* Offset 0 */
+	/* Exit information */
+	SET_MEMBER(struct rmi_rec_exit exit, 0x800, 0x1000);	/* 0x800 */
+};
+
+COMPILER_ASSERT(sizeof(struct rmi_rec_run) <= GRANULE_SIZE);
+
+COMPILER_ASSERT(offsetof(struct rmi_rec_run, entry) == 0);
+COMPILER_ASSERT(offsetof(struct rmi_rec_run, exit) == 0x800);
+
+#endif /* SMC_RMI_H */
diff --git a/lib/realm/include/smc-rsi.h b/lib/realm/include/smc-rsi.h
new file mode 100644
index 0000000..e269f07
--- /dev/null
+++ b/lib/realm/include/smc-rsi.h
@@ -0,0 +1,177 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef SMC_RSI_H
+#define SMC_RSI_H
+
+#include <smc.h>
+#include <stddef.h>
+#include <utils_def.h>
+
+/*
+ * This file describes the Realm Services Interface (RSI) Application Binary
+ * Interface (ABI) for SMC calls made from within the Realm to the RMM and
+ * serviced by the RMM.
+ *
+ * See doc/rmm_interface.md for more details.
+ */
+
+/*
+ * The major version number of the RSI implementation.  Increase this whenever
+ * the binary format or semantics of the SMC calls change.
+ */
+#define RSI_ABI_VERSION_MAJOR		12U
+
+/*
+ * The minor version number of the RSI implementation.  Increase this when
+ * a bug is fixed, or a feature is added without breaking binary compatibility.
+ */
+#define RSI_ABI_VERSION_MINOR		0
+
+#define RSI_ABI_VERSION			((RSI_ABI_VERSION_MAJOR << 16U) | \
+					 RSI_ABI_VERSION_MINOR)
+
+#define RSI_ABI_VERSION_GET_MAJOR(_version) ((_version) >> 16U)
+#define RSI_ABI_VERSION_GET_MINOR(_version) ((_version) & 0xFFFFU)
+
+#define IS_SMC64_RSI_FID(_fid)		IS_SMC64_STD_FAST_IN_RANGE(RSI, _fid)
+
+#define SMC64_RSI_FID(_offset)		SMC64_STD_FID(RSI, _offset)
+
+#define SMC_RSI_ABI_VERSION		SMC64_RSI_FID(U(0x0))
+
+/* RSI Status code enumeration as per Section D4.3.6 of the RMM Spec */
+typedef enum {
+	/* Command completed successfully */
+	RSI_SUCCESS = 0U,
+
+	/*
+	 * The value of a command input value
+	 * caused the command to fail
+	 */
+	RSI_ERROR_INPUT	= 1U,
+
+	/*
+	 * The state of the current Realm or current REC
+	 * does not match the state expected by the command
+	 */
+	RSI_ERROR_STATE	= 2U,
+
+	/* The operation requested by the command is not complete */
+	RSI_INCOMPLETE = 3U,
+
+	RSI_ERROR_COUNT
+} rsi_status_t;
+
+/*
+ * Returns a measurement.
+ * arg1: Measurement index (0..4), measurement (RIM or REM) to read
+ * ret0: Status / error
+ * ret1: Measurement value, bytes:  0 -  7
+ * ret2: Measurement value, bytes:  7 - 15
+ * ret3: Measurement value, bytes: 16 - 23
+ * ret4: Measurement value, bytes: 24 - 31
+ * ret5: Measurement value, bytes: 32 - 39
+ * ret6: Measurement value, bytes: 40 - 47
+ * ret7: Measurement value, bytes: 48 - 55
+ * ret8: Measurement value, bytes: 56 - 63
+ */
+#define SMC_RSI_MEASUREMENT_READ	SMC64_RSI_FID(U(0x2))
+
+/*
+ * Extends a REM.
+ * arg0:  Measurement index (1..4), measurement (REM) to extend
+ * arg1:  Measurement size in bytes
+ * arg3:  Challenge value, bytes:  0 -  7
+ * arg4:  Challenge value, bytes:  7 - 15
+ * arg5:  Challenge value, bytes: 16 - 23
+ * arg6:  Challenge value, bytes: 24 - 31
+ * arg7:  Challenge value, bytes: 32 - 39
+ * arg8:  Challenge value, bytes: 40 - 47
+ * arg9:  Challenge value, bytes: 48 - 55
+ * arg10: Challenge value, bytes: 56 - 63
+ * ret0:  Status / error
+ */
+#define SMC_RSI_MEASUREMENT_EXTEND	SMC64_RSI_FID(U(0x3))
+
+/*
+ * Initialize the operation to retrieve an attestation token.
+ * arg1: The IPA of token buffer
+ * arg2: Challenge value, bytes:  0 -  7
+ * arg3: Challenge value, bytes:  7 - 15
+ * arg4: Challenge value, bytes: 16 - 23
+ * arg5: Challenge value, bytes: 24 - 31
+ * arg6: Challenge value, bytes: 32 - 39
+ * arg7: Challenge value, bytes: 40 - 47
+ * arg8: Challenge value, bytes: 48 - 55
+ * arg9: Challenge value, bytes: 56 - 63
+ * ret0: Status / error
+ * ret1: Size of completed token in bytes
+ */
+#define SMC_RSI_ATTEST_TOKEN_INIT	SMC64_RSI_FID(U(0x4))
+
+/*
+ * Continue the operation to retrieve an attestation token.
+ * arg1: The IPA of token buffer
+ * ret0: Status / error
+ * ret1: Size of completed token in bytes
+ */
+#define SMC_RSI_ATTEST_TOKEN_CONTINUE	SMC64_RSI_FID(U(0x5))
+
+struct rsi_realm_config {
+	/* IPA width in bits */
+	SET_MEMBER(unsigned long ipa_width, 0, 0x1000);	/* Offset 0 */
+};
+
+COMPILER_ASSERT(sizeof(struct rsi_realm_config) == 0x1000);
+
+COMPILER_ASSERT(offsetof(struct rsi_realm_config, ipa_width) == 0);
+
+/*
+ * arg0 == struct rsi_realm_config address
+ */
+#define SMC_RSI_REALM_CONFIG		SMC64_RSI_FID(U(0x6))
+
+/*
+ * arg0 == IPA address of target region
+ * arg1 == Size of target region in bytes
+ * arg2 == RIPAS value
+ * ret0 == Status / error
+ * ret1 == Top of modified IPA range
+ */
+#define SMC_RSI_IPA_STATE_SET		SMC64_RSI_FID(U(0x7))
+
+/*
+ * arg0 == IPA
+ * ret0 == Status / error
+ * ret1 == RIPAS value
+ */
+#define SMC_RSI_IPA_STATE_GET		SMC64_RSI_FID(U(0x8))
+
+#define RSI_HOST_CALL_NR_GPRS		7U
+
+struct rsi_host_call {
+	SET_MEMBER(struct {
+		/* Immediate value */
+		unsigned int imm;		/* Offset 0 */
+		/* Registers */
+		unsigned long gprs[RSI_HOST_CALL_NR_GPRS];
+		}, 0, 0x100);
+};
+
+COMPILER_ASSERT(sizeof(struct rsi_host_call) == 0x100);
+
+COMPILER_ASSERT(offsetof(struct rsi_host_call, imm) == 0);
+COMPILER_ASSERT(offsetof(struct rsi_host_call, gprs[0]) == 8);
+COMPILER_ASSERT(offsetof(struct rsi_host_call,
+			 gprs[RSI_HOST_CALL_NR_GPRS - 1]) ==
+			 8 * RSI_HOST_CALL_NR_GPRS);
+
+/*
+ * arg0 == struct rsi_host_call addr
+ */
+#define SMC_RSI_HOST_CALL		SMC64_RSI_FID(U(0x9))
+
+#endif /* SMC_RSI_H */
diff --git a/lib/realm/include/status.h b/lib/realm/include/status.h
new file mode 100644
index 0000000..51b48f1
--- /dev/null
+++ b/lib/realm/include/status.h
@@ -0,0 +1,141 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef STATUS_H
+#define STATUS_H
+
+#include <stdbool.h>
+
+/*
+ * Status codes which can be returned from RMM commands.
+ *
+ * For each code, the meaning of return_code_t::index is stated.
+ */
+typedef enum {
+	/*
+	 * Command completed successfully.
+	 *
+	 * index is zero.
+	 */
+	RMI_SUCCESS = 0,
+
+	/*
+	 * The value of a command input value caused the command to fail.
+	 *
+	 * index is zero.
+	 */
+	RMI_ERROR_INPUT = 1,
+
+	/*
+	 * An attribute of a Realm does not match the expected value.
+	 *
+	 * index varies between usages.
+	 */
+	RMI_ERROR_REALM = 2,
+
+	/*
+	 * An attribute of a REC does not match the expected value.
+	 *
+	 * index is zero.
+	 */
+	RMI_ERROR_REC = 3,
+
+	/*
+	 * An RTT walk terminated before reaching the target RTT level,
+	 * or reached an RTTE with an unexpected value.
+	 *
+	 * index: RTT level at which the walk terminated
+	 */
+	RMI_ERROR_RTT = 4,
+
+	/*
+	 * An operation cannot be completed because a resource is in use.
+	 *
+	 * index is zero.
+	 */
+	RMI_ERROR_IN_USE = 5,
+
+	RMI_ERROR_COUNT
+} status_t;
+
+/*
+ * Logical representation of return code returned by RMM commands.
+ * Each failure mode of a given command should return a unique return code, so
+ * that the caller can use it to unambiguously identify the failure mode.  To
+ * avoid having a very large list of enumerated values, the return code is
+ * composed of a status which identifies the category of the error (for example,
+ * an address was misaligned), and an index which disambiguates between multiple
+ * similar failure modes (for example, a command may take multiple addresses as
+ * its input; the index identifies _which_ of them was misaligned.)
+ */
+typedef struct {
+	status_t status;
+	unsigned int index;
+} return_code_t;
+
+/*
+ * Convenience function for creating a return_code_t.
+ */
+static inline return_code_t make_return_code(unsigned int status,
+					     unsigned int index)
+{
+	return (return_code_t) {status, index};
+}
+
+/*
+ * Pack a return_code_t into a binary format, suitable for storing in a
+ * register before exit from the RMM.
+ */
+static inline unsigned long pack_struct_return_code(return_code_t return_code)
+{
+	return (unsigned long)(return_code.status | (return_code.index << 8));
+}
+
+/*
+ * Pack a return code into a binary format, suitable for storing in a register
+ * on exit from the RMM.
+ */
+static inline unsigned long pack_return_code(unsigned int status, unsigned int index)
+{
+	/* The width of @status and @index is 8 bits */
+	assert((status <= 0xffU) && (index <= 0xffU));
+	return pack_struct_return_code(make_return_code(status, index));
+}
+
+/*
+ * Unpacks a return code.
+ */
+static inline return_code_t unpack_return_code(unsigned long error_code)
+{
+	return make_return_code(error_code & 0xffU, error_code >> 8);
+}
+
+#define MAX_ERR 4095
+
+/*
+ * Cast a status value to a pointer.
+ */
+static inline void *status_ptr(status_t status)
+{
+	return (void *)(-1 * (unsigned long)status);
+}
+
+/*
+ * Check whether a pointer value represents an error.
+ */
+static inline bool ptr_is_err(const void *ptr)
+{
+	return (unsigned long)ptr >= (unsigned long)(-MAX_ERR);
+}
+
+/*
+ * Cast a pointer to a status value.
+ */
+static inline status_t ptr_status(const void *ptr)
+{
+	return (status_t)(-1 * (unsigned long)ptr);
+}
+
+#endif /* STATUS_H */
diff --git a/lib/realm/include/sve.h b/lib/realm/include/sve.h
new file mode 100644
index 0000000..1804d03
--- /dev/null
+++ b/lib/realm/include/sve.h
@@ -0,0 +1,54 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef SVE_H
+#define SVE_H
+
+#include <arch.h>
+#include <utils_def.h>
+
+/*
+ * SVE vector length in bytes and derived values
+ */
+#define SVE_VLA_ZCR_LEN_BITS	UL(4)
+#define SVE_VLA_LEN_MAX		(UL(1) << SVE_VLA_ZCR_LEN_BITS)
+#define SVE_VLA_ZCR_LEN_MAX	(SVE_VLA_LEN_MAX - UL(1))
+#define SVE_VLA_VL_MIN		UL(16)
+#define SVE_VLA_VL_MAX		(SVE_VLA_VL_MIN * SVE_VLA_LEN_MAX)
+#define SVE_VLA_Z_REGS_MAX	UL(32)
+#define SVE_VLA_P_REGS_MAX	UL(16)
+#define SVE_VLA_FFR_REGS_MAX	UL(1)
+#define SVE_VLA_Z_LEN_MAX	(SVE_VLA_VL_MAX * SVE_VLA_Z_REGS_MAX)
+#define SVE_VLA_P_LEN_MAX	((SVE_VLA_VL_MAX * SVE_VLA_P_REGS_MAX) >> UL(3))
+#define SVE_VLA_FFR_LEN_MAX	((SVE_VLA_VL_MAX * SVE_VLA_FFR_REGS_MAX) >> \
+				 UL(3))
+#define SVE_ZCR_FP_REGS_NUM	UL(4)
+
+#ifndef __ASSEMBLER__
+
+/*
+ * SVE context structure. Align on cache writeback granule to minimise cache line
+ * thashing when allocated as an array for use by each CPU.
+ */
+struct sve_state {
+	unsigned long zcr_fpu[SVE_ZCR_FP_REGS_NUM];
+	unsigned char z[SVE_VLA_Z_LEN_MAX];
+	unsigned char p_ffr[SVE_VLA_P_LEN_MAX + SVE_VLA_FFR_LEN_MAX];
+} __attribute__((aligned(CACHE_WRITEBACK_GRANULE)));
+
+void save_sve_zcr_fpu_state(unsigned long *data);
+void save_sve_z_state(unsigned char *data);
+void save_sve_p_ffr_state(unsigned char *data);
+
+void restore_sve_zcr_fpu_state(unsigned long *data);
+void restore_sve_z_state(unsigned char *data);
+void restore_sve_p_ffr_state(unsigned char *data);
+
+void save_sve_state(struct sve_state *sve);
+void restore_sve_state(struct sve_state *sve);
+
+#endif /* __ASSEMBLER__ */
+
+#endif /* __SVE_H_ */
diff --git a/lib/realm/include/table.h b/lib/realm/include/table.h
new file mode 100644
index 0000000..450da86
--- /dev/null
+++ b/lib/realm/include/table.h
@@ -0,0 +1,120 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef TABLE_H
+#define TABLE_H
+
+#include <arch_features.h>
+#include <memory.h>
+
+#define MIN_IPA_BITS		32
+#define MAX_IPA_BITS		48
+#define MAX_IPA_SIZE		(1UL << MAX_IPA_BITS)
+
+#define MIN_STARTING_LEVEL	0
+#define RTT_PAGE_LEVEL		3
+#define RTT_MIN_BLOCK_LEVEL	2
+
+/* TODO: Fix this when introducing LPA2 support */
+COMPILER_ASSERT(MIN_STARTING_LEVEL >= 0);
+
+/* TODO: Allow the NS caller to select the stage 2 starting level */
+#define RTT_STARTING_LEVEL	0
+
+/*
+ * S2TTE_STRIDE: The number of bits resolved in a single level of translation
+ * walk (except for the starting level which may resolve more or fewer bits).
+ */
+#define S2TTE_STRIDE		(GRANULE_SHIFT - 3)
+#define S2TTES_PER_S2TT		(1 << S2TTE_STRIDE)
+
+struct rd;
+enum ripas;
+
+unsigned long s2tte_create_ripas(enum ripas ripas);
+unsigned long s2tte_create_unassigned(enum ripas ripas);
+unsigned long s2tte_create_destroyed(void);
+unsigned long s2tte_create_assigned_empty(unsigned long pa, long level);
+unsigned long s2tte_create_valid(unsigned long pa, long level);
+unsigned long s2tte_create_invalid_ns(void);
+unsigned long s2tte_create_valid_ns(unsigned long s2tte, long level);
+unsigned long s2tte_create_table(unsigned long pa, long level);
+
+bool host_ns_s2tte_is_valid(unsigned long s2tte, long level);
+unsigned long host_ns_s2tte(unsigned long s2tte, long level);
+
+bool s2tte_is_unassigned(unsigned long s2tte);
+bool s2tte_is_destroyed(unsigned long s2tte);
+bool s2tte_is_assigned(unsigned long s2tte, long level);
+bool s2tte_is_valid(unsigned long s2tte, long level);
+bool s2tte_is_valid_ns(unsigned long s2tte, long level);
+bool s2tte_is_table(unsigned long s2tte, long level);
+
+enum ripas s2tte_get_ripas(unsigned long s2tte);
+
+void s2tt_init_unassigned(unsigned long *s2tt, enum ripas ripas);
+void s2tt_init_destroyed(unsigned long *s2tt);
+void s2tt_init_assigned_empty(unsigned long *s2tt, unsigned long pa, long level);
+void s2tt_init_valid(unsigned long *s2tt, unsigned long pa, long level);
+void s2tt_init_valid_ns(unsigned long *s2tt, unsigned long pa, long level);
+
+unsigned long s2tte_pa(unsigned long s2tte, long level);
+unsigned long s2tte_pa_table(unsigned long s2tte, long level);
+bool addr_is_level_aligned(unsigned long addr, long level);
+bool addr_block_intersects_par(struct rd *rd, unsigned long addr, long level);
+unsigned long s2tte_map_size(int level);
+
+struct realm_s2_context;
+void invalidate_page(const struct realm_s2_context *ctx, unsigned long addr);
+void invalidate_block(const struct realm_s2_context *ctx, unsigned long addr);
+void invalidate_pages_in_block(const struct realm_s2_context *ctx, unsigned long addr);
+
+bool table_is_unassigned_block(unsigned long *table, enum ripas *ripas);
+bool table_is_destroyed_block(unsigned long *table);
+
+bool table_maps_assigned_block(unsigned long *table, long level);
+bool table_maps_valid_block(unsigned long *table, long level);
+bool table_maps_valid_ns_block(unsigned long *table, long level);
+
+struct rtt_walk {
+	struct granule *g_llt;
+	unsigned long index;
+	long last_level;
+};
+
+void rtt_walk_lock_unlock(struct granule *g_root,
+			  int start_level,
+			  unsigned long ipa_bits,
+			  unsigned long map_addr,
+			  long level,
+			  struct rtt_walk *wi);
+
+/*
+ * The MMU is a separate observer, and requires that translation table updates
+ * are made with single-copy-atomic stores, necessitating inline assembly. For
+ * consistency we use accessors for both reads and writes of translation table
+ * entries.
+ */
+static inline void __tte_write(uint64_t *ttep, uint64_t tte)
+{
+	SCA_WRITE64(ttep, tte);
+	dsb(ish);
+}
+#define s1tte_write(s1ttep, s1tte)	__tte_write(s1ttep, s1tte)
+#define s2tte_write(s2ttep, s2tte)	__tte_write(s2ttep, s2tte)
+
+static inline uint64_t __tte_read(uint64_t *ttep)
+{
+	return SCA_READ64(ttep);
+}
+#define s1tte_read(s1ttep)	__tte_read(s1ttep)
+#define s2tte_read(s2ttep)	__tte_read(s2ttep)
+
+static inline unsigned int max_ipa_size(void)
+{
+	return arch_feat_get_pa_width();
+}
+
+#endif /* TABLE_H */
diff --git a/lib/realm/include/vmid.h b/lib/realm/include/vmid.h
new file mode 100644
index 0000000..33fa777
--- /dev/null
+++ b/lib/realm/include/vmid.h
@@ -0,0 +1,14 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef VMID_H
+#define VMID_H
+
+#include <stdbool.h>
+
+bool vmid_reserve(unsigned int vmid);
+void vmid_free(unsigned int vmid);
+
+#endif /* VMID_H */
diff --git a/lib/realm/src/aarch64/sve_helpers.S b/lib/realm/src/aarch64/sve_helpers.S
new file mode 100644
index 0000000..f7fc7b1
--- /dev/null
+++ b/lib/realm/src/aarch64/sve_helpers.S
@@ -0,0 +1,235 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+/*
+ * void save_sve_zcr_fpu_state(unsigned long *data)
+ */
+
+#include <asm_macros.S>
+#include <sve.h>
+
+.globl save_sve_zcr_fpu_state
+.globl save_sve_z_state
+.globl save_sve_p_ffr_state
+.globl restore_sve_zcr_fpu_state
+.globl restore_sve_z_state
+.globl restore_sve_p_ffr_state
+
+func save_sve_zcr_fpu_state
+.arch_extension sve
+	/* Save the FPSR/FPCR */
+	mrs	x1, fpsr
+	mrs	x2, fpcr
+	stp	x1, x2, [x0], #16
+
+	/* Save the ZCR_EL1/EL2 */
+	mrs	x1, zcr_el1
+	mrs	x2, zcr_el2
+	stp	x1, x2, [x0]
+
+	ret
+.arch_extension nosve
+endfunc save_sve_zcr_fpu_state
+
+/*
+ * void save_sve_z_state(unsigned char *data)
+ */
+func save_sve_z_state
+.arch_extension sve
+	/* maximise VL */
+	mrs	x1, zcr_el2
+	orr	x2, x1, #SVE_VLA_ZCR_LEN_MAX
+	msr	zcr_el2, x2
+
+	/* Save the z register bank to memory. */
+	str 	z0, [x0, #0, MUL VL]
+	str	z1, [x0, #1, MUL VL]
+	str	z2, [x0, #2, MUL VL]
+	str	z3, [x0, #3, MUL VL]
+	str	z4, [x0, #4, MUL VL]
+	str	z5, [x0, #5, MUL VL]
+	str	z6, [x0, #6, MUL VL]
+	str	z7, [x0, #7, MUL VL]
+	str	z8, [x0, #8, MUL VL]
+	str	z9, [x0, #9, MUL VL]
+	str	z10, [x0, #10, MUL VL]
+	str	z11, [x0, #11, MUL VL]
+	str	z12, [x0, #12, MUL VL]
+	str	z13, [x0, #13, MUL VL]
+	str	z14, [x0, #14, MUL VL]
+	str	z15, [x0, #15, MUL VL]
+	str	z16, [x0, #16, MUL VL]
+	str	z17, [x0, #17, MUL VL]
+	str	z18, [x0, #18, MUL VL]
+	str	z19, [x0, #19, MUL VL]
+	str	z20, [x0, #20, MUL VL]
+	str	z21, [x0, #21, MUL VL]
+	str	z22, [x0, #22, MUL VL]
+	str	z23, [x0, #23, MUL VL]
+	str	z24, [x0, #24, MUL VL]
+	str	z25, [x0, #25, MUL VL]
+	str	z26, [x0, #26, MUL VL]
+	str	z27, [x0, #27, MUL VL]
+	str	z28, [x0, #28, MUL VL]
+	str	z29, [x0, #29, MUL VL]
+	str	z30, [x0, #30, MUL VL]
+	str	z31, [x0, #31, MUL VL]
+
+	/* restore back zcr */
+	msr	zcr_el2, x1
+
+	ret
+.arch_extension nosve
+endfunc save_sve_z_state
+
+/*
+ * void save_sve_p_ffr_state(unsigned char *data)
+ */
+func save_sve_p_ffr_state
+.arch_extension sve
+	/* maximise VL */
+	mrs	x1, zcr_el2
+	orr	x2, x1, #SVE_VLA_ZCR_LEN_MAX
+	msr	zcr_el2, x2
+
+	/* Save the P register bank to memory. */
+	str 	p0, [x0, #0, MUL VL]
+	str 	p1, [x0, #1, MUL VL]
+	str 	p2, [x0, #2, MUL VL]
+	str 	p3, [x0, #3, MUL VL]
+	str 	p4, [x0, #4, MUL VL]
+	str 	p5, [x0, #5, MUL VL]
+	str 	p6, [x0, #6, MUL VL]
+	str 	p7, [x0, #7, MUL VL]
+	str 	p8, [x0, #8, MUL VL]
+	str 	p9, [x0, #9, MUL VL]
+	str 	p10, [x0, #10, MUL VL]
+	str 	p11, [x0, #11, MUL VL]
+	str 	p12, [x0, #12, MUL VL]
+	str 	p13, [x0, #13, MUL VL]
+	str 	p14, [x0, #14, MUL VL]
+	str 	p15, [x0, #15, MUL VL]
+
+	/* Save the ffr register bank to memory. */
+	rdffr	p0.B
+	str 	p0, [x0, #16, MUL VL]
+
+	/* restore back zcr */
+	msr	zcr_el2, x1
+
+	ret
+.arch_extension nosve
+endfunc save_sve_p_ffr_state
+
+/*
+ * void restore_sve_zcr_fpu_state(unsigned long *data)
+ */
+func restore_sve_zcr_fpu_state
+.arch_extension sve
+	/* Load the FPSR/FPCR */
+	ldp	x1, x2, [x0], #16
+	msr	fpsr, x1
+	msr	fpcr, x2
+
+	/* Load the ZCR_EL1/EL2 */
+	ldp	x1, x2, [x0]
+	msr	zcr_el1, x1
+	msr	zcr_el2, x2
+
+	ret
+.arch_extension nosve
+endfunc restore_sve_zcr_fpu_state
+
+/*
+ * void restore_sve_z_state(unsigned char *data)
+ */
+func restore_sve_z_state
+.arch_extension sve
+	/* maximise VL */
+	mrs	x1, zcr_el2
+	orr	x2, x1, #SVE_VLA_ZCR_LEN_MAX
+	msr	zcr_el2, x2
+
+	/* Load the z register bank from memory. */
+	ldr 	z0, [x0, #0, MUL VL]
+	ldr	z1, [x0, #1, MUL VL]
+	ldr	z2, [x0, #2, MUL VL]
+	ldr	z3, [x0, #3, MUL VL]
+	ldr	z4, [x0, #4, MUL VL]
+	ldr	z5, [x0, #5, MUL VL]
+	ldr	z6, [x0, #6, MUL VL]
+	ldr	z7, [x0, #7, MUL VL]
+	ldr	z8, [x0, #8, MUL VL]
+	ldr	z9, [x0, #9, MUL VL]
+	ldr	z10, [x0, #10, MUL VL]
+	ldr	z11, [x0, #11, MUL VL]
+	ldr	z12, [x0, #12, MUL VL]
+	ldr	z13, [x0, #13, MUL VL]
+	ldr	z14, [x0, #14, MUL VL]
+	ldr	z15, [x0, #15, MUL VL]
+	ldr	z16, [x0, #16, MUL VL]
+	ldr	z17, [x0, #17, MUL VL]
+	ldr	z18, [x0, #18, MUL VL]
+	ldr	z19, [x0, #19, MUL VL]
+	ldr	z20, [x0, #20, MUL VL]
+	ldr	z21, [x0, #21, MUL VL]
+	ldr	z22, [x0, #22, MUL VL]
+	ldr	z23, [x0, #23, MUL VL]
+	ldr	z24, [x0, #24, MUL VL]
+	ldr	z25, [x0, #25, MUL VL]
+	ldr	z26, [x0, #26, MUL VL]
+	ldr	z27, [x0, #27, MUL VL]
+	ldr	z28, [x0, #28, MUL VL]
+	ldr	z29, [x0, #29, MUL VL]
+	ldr	z30, [x0, #30, MUL VL]
+	ldr	z31, [x0, #31, MUL VL]
+
+	/* restore back zcr */
+	msr	zcr_el2, x1
+
+	ret
+.arch_extension nosve
+endfunc restore_sve_z_state
+
+/*
+ * void restore_sve_p_ffr_state(unsigned char *data)
+ */
+func restore_sve_p_ffr_state
+.arch_extension sve
+	/* maximise VL */
+	mrs	x1, zcr_el2
+	orr	x2, x1, #SVE_VLA_ZCR_LEN_MAX
+	msr	zcr_el2, x2
+
+	/* Load the P register bank from memory. */
+	ldr 	p1, [x0, #1, MUL VL]
+	ldr 	p2, [x0, #2, MUL VL]
+	ldr 	p3, [x0, #3, MUL VL]
+	ldr 	p4, [x0, #4, MUL VL]
+	ldr 	p5, [x0, #5, MUL VL]
+	ldr 	p6, [x0, #6, MUL VL]
+	ldr 	p7, [x0, #7, MUL VL]
+	ldr 	p8, [x0, #8, MUL VL]
+	ldr 	p9, [x0, #9, MUL VL]
+	ldr 	p10, [x0, #10, MUL VL]
+	ldr 	p11, [x0, #11, MUL VL]
+	ldr 	p12, [x0, #12, MUL VL]
+	ldr 	p13, [x0, #13, MUL VL]
+	ldr 	p14, [x0, #14, MUL VL]
+	ldr 	p15, [x0, #15, MUL VL]
+
+	/* Load the ffr register bank from memory. */
+	ldr 	p0, [x0, #16, MUL VL]
+	wrffr	p0.B
+
+	/* restore P0 */
+	ldr 	p0, [x0]
+
+	/* restore back zcr */
+	msr	zcr_el2, x1
+
+	ret
+.arch_extension nosve
+endfunc restore_sve_p_ffr_state
diff --git a/lib/realm/src/buffer.c b/lib/realm/src/buffer.c
new file mode 100644
index 0000000..44823aa
--- /dev/null
+++ b/lib/realm/src/buffer.c
@@ -0,0 +1,390 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <arch.h>
+#include <arch_helpers.h>
+#include <assert.h>
+#include <attestation_token.h>
+#include <buffer.h>
+#include <cpuid.h>
+#include <debug.h>
+#include <errno.h>
+#include <gic.h>
+#include <granule.h>
+#include <memory_alloc.h>
+#include <sizes.h>
+#include <slot_buf_arch.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <table.h>
+#include <xlat_contexts.h>
+#include <xlat_tables.h>
+
+/*
+ * The VA space size for the high region, which maps the slot buffers,
+ * needs to be a power of two, so round NR_CPU_SLOTS up to the closest
+ * power of two.
+ */
+#define ROUNDED_NR_CPU_SLOTS (1ULL << (64ULL - \
+				       __builtin_clzll((NR_CPU_SLOTS) - 1)))
+
+#define RMM_SLOT_BUF_VA_SIZE	((ROUNDED_NR_CPU_SLOTS) * (GRANULE_SIZE))
+
+#define SLOT_VIRT		((ULL(0xffffffffffffffff) - \
+				 RMM_SLOT_BUF_VA_SIZE + ULL(1)))
+
+/*
+ * All the slot buffers for a given CPU must be mapped by a single translation
+ * table, which means the max VA size should be <= 4KB * 512
+ */
+COMPILER_ASSERT((RMM_SLOT_BUF_VA_SIZE) <= (GRANULE_SIZE * XLAT_TABLE_ENTRIES));
+
+/*
+ * For all translation stages if FEAT_TTST is implemented, while
+ * the PE is executing in AArch64 state and is using 4KB
+ * translation granules, the min address space size is 64KB
+ */
+COMPILER_ASSERT((RMM_SLOT_BUF_VA_SIZE) >= (1 << 16U));
+
+#define RMM_SLOT_BUF_MMAP	MAP_REGION_TRANSIENT(			\
+					SLOT_VIRT,			\
+					RMM_SLOT_BUF_VA_SIZE,		\
+					PAGE_SIZE)
+
+#define SLOT_BUF_MMAP_REGIONS		UL(1)
+
+/*
+ * Attributes for a buffer slot page descriptor.
+ * Note that the AF bit on the descriptor is handled by the translation
+ * library (it assumes that access faults are not handled) so it does not
+ * need to be specified here.
+ */
+#define SLOT_DESC_ATTR \
+	(MT_RW_DATA | MT_SHAREABILITY_ISH | MT_NG)
+
+/*
+ * The base tables for all the contexts are manually allocated as a continous
+ * block of memory.
+ */
+static uint64_t transient_base_table[XLAT_TABLE_ENTRIES * MAX_CPUS]
+				    __aligned(BASE_XLAT_TABLES_ALIGNMENT)
+				    __section("slot_buffer_xlat_tbls");
+
+/* Allocate per-cpu xlat_ctx_tbls */
+static struct xlat_ctx_tbls slot_buf_tbls[MAX_CPUS];
+
+/*
+ * Allocate mmap regions and define common xlat_ctx_cfg shared will
+ * all slot_buf_xlat_ctx
+ */
+XLAT_REGISTER_VA_SPACE(slot_buf, VA_HIGH_REGION,
+		       SLOT_BUF_MMAP_REGIONS,
+		       RMM_SLOT_BUF_VA_SIZE);
+
+/* context definition */
+static struct xlat_ctx slot_buf_xlat_ctx[MAX_CPUS];
+
+/*
+ * Allocate a cache to store the last level table entry where the slot buffers
+ * are mapped to avoid needing to perform a table walk every time a buffer
+ * slot operation is needed.
+ */
+static struct xlat_table_entry te_cache[MAX_CPUS];
+
+static uintptr_t slot_to_va(enum buffer_slot slot)
+{
+	assert(slot < NR_CPU_SLOTS);
+
+	return (uintptr_t)(SLOT_VIRT + (GRANULE_SIZE * slot));
+}
+
+static inline struct xlat_ctx *get_slot_buf_xlat_ctx(void)
+{
+	return &slot_buf_xlat_ctx[my_cpuid()];
+}
+
+static inline struct xlat_table_entry *get_cache_entry(void)
+{
+	return &te_cache[my_cpuid()];
+}
+
+__unused static uint64_t slot_to_descriptor(enum buffer_slot slot)
+{
+	uint64_t *entry = xlat_get_pte_from_table(get_cache_entry(),
+						  slot_to_va(slot));
+
+	return xlat_read_descriptor(entry);
+}
+
+/*
+ * Setup xlat table for slot buffer mechanism for each PE.
+ * Must be called for every PE in the system
+ */
+void slot_buf_setup_xlat(void)
+{
+	unsigned int cpuid = my_cpuid();
+	int ret = xlat_ctx_create_dynamic(get_slot_buf_xlat_ctx(),
+					  &slot_buf_xlat_ctx_cfg,
+					  &slot_buf_tbls[cpuid],
+					  &transient_base_table[
+						XLAT_TABLE_ENTRIES * cpuid],
+					  GET_NUM_BASE_LEVEL_ENTRIES(
+							RMM_SLOT_BUF_VA_SIZE),
+					  NULL,
+					  0U);
+
+	if (ret == -EINVAL) {
+		/*
+		 * If the context was already created, carry on with the
+		 * initialization. If it cannot be created, panic.
+		 */
+		ERROR("%s (%u): Failed to create the empty context for the slot buffers\n",
+					__func__, __LINE__);
+		panic();
+	}
+
+	if (xlat_ctx_cfg_initialized(get_slot_buf_xlat_ctx()) == false) {
+		/* Add necessary mmap regions during cold boot */
+		struct xlat_mmap_region slot_buf_regions[] = {
+			RMM_SLOT_BUF_MMAP,
+			{0}
+		};
+
+		if (xlat_mmap_add_ctx(get_slot_buf_xlat_ctx(),
+				      slot_buf_regions, true) != 0) {
+			ERROR("%s (%u): Failed to map slot buffer memory on high region\n",
+				__func__, __LINE__);
+			panic();
+		}
+
+	}
+
+	if (xlat_ctx_tbls_initialized(get_slot_buf_xlat_ctx()) == false) {
+		/*
+		 * Initialize the translation tables for the current context.
+		 * This is done on the first boot of each CPU.
+		 */
+		int err;
+
+		err = xlat_init_tables_ctx(get_slot_buf_xlat_ctx());
+		if (err != 0) {
+			ERROR("%s (%u): xlat initialization failed with code %i\n",
+			__func__, __LINE__, err);
+			panic();
+		}
+	}
+
+	/*
+	 * Confugure MMU registers. This function assumes that all the
+	 * contexts of a particular VA region (HIGH or LOW VA) use the same
+	 * limits for VA and PA spaces.
+	 */
+	if (xlat_arch_setup_mmu_cfg(get_slot_buf_xlat_ctx())) {
+		ERROR("%s (%u): MMU registers failed to initialize\n",
+					__func__, __LINE__);
+		panic();
+	}
+}
+
+/*
+ * Finishes initializing the slot buffer mechanism.
+ * This function must be called after the MMU is enabled.
+ */
+void slot_buf_init(void)
+{
+	if (is_mmu_enabled() == false) {
+		ERROR("%s: MMU must be enabled\n", __func__);
+		panic();
+	}
+
+	/*
+	 * Initialize (if not done yet) the internal cache with the last level
+	 * translation table that holds the MMU descriptors for the slot
+	 * buffers, so we can access them faster when we need to map/unmap.
+	 */
+	if ((get_cache_entry())->table == NULL) {
+		if (xlat_get_table_from_va(get_cache_entry(),
+					   get_slot_buf_xlat_ctx(),
+					   slot_to_va(SLOT_NS)) != 0) {
+			ERROR("%s (%u): Failed to initialize table entry cache for CPU %u\n",
+					__func__, __LINE__, my_cpuid());
+			panic();
+
+		}
+	}
+}
+
+/*
+ * Buffer slots are intended to be transient, and should not be live at
+ * entry/exit of the RMM.
+ */
+void assert_cpu_slots_empty(void)
+{
+	unsigned int i;
+
+	for (i = 0; i < NR_CPU_SLOTS; i++) {
+		assert(slot_to_descriptor(i) == INVALID_DESC);
+	}
+}
+
+static inline bool is_ns_slot(enum buffer_slot slot)
+{
+	return slot == SLOT_NS;
+}
+
+static inline bool is_realm_slot(enum buffer_slot slot)
+{
+	return (slot != SLOT_NS) && (slot < NR_CPU_SLOTS);
+}
+
+static void *ns_granule_map(enum buffer_slot slot, struct granule *granule)
+{
+	unsigned long addr = granule_addr(granule);
+
+	assert(is_ns_slot(slot));
+	return buffer_arch_map(slot, addr, true);
+}
+
+static void ns_buffer_unmap(enum buffer_slot slot)
+{
+	assert(is_ns_slot(slot));
+
+	buffer_arch_unmap((void *)slot_to_va(slot));
+}
+
+/*
+ * Maps a granule @g into the provided @slot, returning
+ * the virtual address.
+ *
+ * The caller must either hold @g::lock or hold a reference.
+ */
+void *granule_map(struct granule *g, enum buffer_slot slot)
+{
+	unsigned long addr = granule_addr(g);
+
+	assert(is_realm_slot(slot));
+
+	return buffer_arch_map(slot, addr, false);
+}
+
+void buffer_unmap(void *buf)
+{
+	buffer_arch_unmap(buf);
+}
+
+bool memcpy_ns_read(void *dest, const void *ns_src, unsigned long size);
+bool memcpy_ns_write(void *ns_dest, const void *src, unsigned long size);
+
+/*
+ * Map a Non secure granule @g into the slot @slot and read data from
+ * this granule to @dest. Unmap the granule once the read is done.
+ *
+ * It returns 'true' on success or `false` if not all data are copied.
+ * Only the least significant bits of @offset are considered, which allows the
+ * full PA of a non-granule aligned buffer to be used for the @offset parameter.
+ */
+bool ns_buffer_read(enum buffer_slot slot,
+		    struct granule *ns_gr,
+		    unsigned int offset,
+		    unsigned int size,
+		    void *dest)
+{
+	uintptr_t src;
+	bool retval;
+
+	assert(is_ns_slot(slot));
+	assert(ns_gr != NULL);
+
+	/*
+	 * To simplify the trapping mechanism around NS access,
+	 * memcpy_ns_read uses a single 8-byte LDR instruction and
+	 * all parameters must be aligned accordingly.
+	 */
+	assert(ALIGNED(size, 8));
+	assert(ALIGNED(offset, 8));
+	assert(ALIGNED(dest, 8));
+
+	offset &= ~GRANULE_MASK;
+	assert(offset + size <= GRANULE_SIZE);
+
+	src = (uintptr_t)ns_granule_map(slot, ns_gr) + offset;
+	retval = memcpy_ns_read(dest, (void *)src, size);
+	ns_buffer_unmap(slot);
+
+	return retval;
+}
+
+/*
+ * Map a Non secure granule @g into the slot @slot and write data from
+ * this granule to @dest. Unmap the granule once the write is done.
+ *
+ * It returns 'true' on success or `false` if not all data are copied.
+ * Only the least significant bits of @offset are considered, which allows the
+ * full PA of a non-granule aligned buffer to be used for the @offset parameter.
+ */
+bool ns_buffer_write(enum buffer_slot slot,
+		     struct granule *ns_gr,
+		     unsigned int offset,
+		     unsigned int size,
+		     void *src)
+{
+	uintptr_t dest;
+	bool retval;
+
+	assert(is_ns_slot(slot));
+	assert(ns_gr != NULL);
+
+	/*
+	 * To simplify the trapping mechanism around NS access,
+	 * memcpy_ns_write uses a single 8-byte STR instruction and
+	 * all parameters must be aligned accordingly.
+	 */
+	assert(ALIGNED(size, 8));
+	assert(ALIGNED(offset, 8));
+	assert(ALIGNED(src, 8));
+
+	offset &= ~GRANULE_MASK;
+	assert(offset + size <= GRANULE_SIZE);
+
+	dest = (uintptr_t)ns_granule_map(slot, ns_gr) + offset;
+	retval = memcpy_ns_write((void *)dest, src, size);
+	ns_buffer_unmap(slot);
+
+	return retval;
+}
+
+/******************************************************************************
+ * Internal helpers
+ ******************************************************************************/
+
+void *buffer_map_internal(enum buffer_slot slot, unsigned long addr, bool ns)
+{
+	uint64_t attr = SLOT_DESC_ATTR;
+	uintptr_t va = slot_to_va(slot);
+	struct xlat_table_entry *entry = get_cache_entry();
+
+	assert(GRANULE_ALIGNED(addr));
+
+	attr |= (ns == true ? MT_NS : MT_REALM);
+
+	if (xlat_map_memory_page_with_attrs(entry, va,
+					    (uintptr_t)addr, attr) != 0) {
+		/* Error mapping the buffer */
+		return NULL;
+	}
+
+	return (void *)va;
+}
+
+void buffer_unmap_internal(void *buf)
+{
+	/*
+	 * Prevent the compiler from moving prior loads/stores to buf after the
+	 * update to the translation table. Otherwise, those could fault.
+	 */
+	COMPILER_BARRIER();
+
+	xlat_unmap_memory_page(get_cache_entry(), (uintptr_t)buf);
+}
diff --git a/lib/realm/src/granule.c b/lib/realm/src/granule.c
new file mode 100644
index 0000000..3b5ef48
--- /dev/null
+++ b/lib/realm/src/granule.c
@@ -0,0 +1,260 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <assert.h>
+#include <buffer.h>
+#include <debug.h>
+#include <granule.h>
+#include <mmio.h>
+#include <platform_api.h>
+#include <smc.h>
+#include <status.h>
+#include <stddef.h>
+#include <string.h>
+#include <utils_def.h>
+
+static struct granule granules[RMM_MAX_GRANULES];
+
+/*
+ * Takes a valid pointer to a struct granule, and returns the granule physical
+ * address.
+ *
+ * This is purely a lookup, and provides no guarantees about the attributes of
+ * the granule (i.e. whether it is locked, its state or its reference count).
+ */
+unsigned long granule_addr(struct granule *g)
+{
+	unsigned long idx;
+
+	assert(g != NULL);
+
+	idx = g - &granules[0];
+
+	return plat_granule_idx_to_addr(idx);
+}
+
+/*
+ * Takes a granule index, and returns a pointer to the struct granule.
+ *
+ * This is purely a lookup, and provides no guarantees about the attributes of
+ * the granule (i.e. whether it is locked, its state or its reference count).
+ */
+static struct granule *granule_from_idx(unsigned long idx)
+{
+	assert(idx < RMM_MAX_GRANULES);
+	return &granules[idx];
+}
+
+/*
+ * Takes an aligned granule address, and returns a pointer to the corresponding
+ * struct granule.
+ *
+ * This is purely a lookup, and provides no guarantees about the attributes of
+ * the granule (i.e. whether it is locked, its state or its reference count).
+ */
+struct granule *addr_to_granule(unsigned long addr)
+{
+	unsigned long idx;
+
+	assert(GRANULE_ALIGNED(addr));
+
+	idx = plat_granule_addr_to_idx(addr);
+	return granule_from_idx(idx);
+}
+
+/*
+ * Verifies whether @addr is a valid granule physical address, and returns a
+ * pointer to the corresponding struct granule.
+ *
+ * This is purely a lookup, and provides no guarantees w.r.t the state of the
+ * granule (e.g. locking).
+ *
+ * Returns:
+ *     Pointer to the struct granule if @addr is a valid granule physical
+ *     address.
+ *     NULL if any of:
+ *     - @addr is not aligned to the size of a granule.
+ *     - @addr is out of range.
+ */
+struct granule *find_granule(unsigned long addr)
+{
+	unsigned long idx;
+
+	if (!GRANULE_ALIGNED(addr)) {
+		return NULL;
+	}
+
+	idx = plat_granule_addr_to_idx(addr);
+
+	if (idx >= RMM_MAX_GRANULES) {
+		return NULL;
+	}
+
+	return granule_from_idx(idx);
+}
+
+/*
+ * Obtain a pointer to a locked granule at @addr if @addr is a valid granule
+ * physical address and the state of the granule at @addr is @expected_state.
+ *
+ * Returns:
+ *	A valid granule pointer if @addr is a valid granule physical address.
+ *	NULL if any of:
+ *	- @addr is not aligned to the size of a granule.
+ *	- @addr is out of range.
+ *	- if the state of the granule at @addr is not
+ *	@expected_state.
+ */
+struct granule *find_lock_granule(unsigned long addr,
+				  enum granule_state expected_state)
+{
+	struct granule *g;
+
+	g = find_granule(addr);
+	if (g == NULL) {
+		return NULL;
+	}
+
+	if (!granule_lock_on_state_match(g, expected_state)) {
+		return NULL;
+	}
+
+	return g;
+}
+
+struct granule_set {
+	unsigned int idx;
+	unsigned long addr;
+	enum granule_state state;
+	struct granule *g;
+	struct granule **g_ret;
+};
+
+/*
+ * Sort a set of granules by their address.
+ */
+static void sort_granules(struct granule_set *granules,
+			unsigned long n)
+{
+	unsigned long i;
+
+	for (i = 1UL; i < n; i++) {
+		struct granule_set temp = granules[i];
+		unsigned long j = i;
+
+		while ((j > 0UL) && (granules[j - 1].addr > temp.addr)) {
+			granules[j] = granules[j - 1];
+			j--;
+		}
+		if (i != j) {
+			granules[j] = temp;
+		}
+	}
+}
+
+/*
+ * Find a set of granules and lock them in order of their address.
+ *
+ * @granules: Pointer to array of @n items.  Each item must be pre-populated
+ *		with ->addr set to the granule's address, and ->state set to
+ *		the expected state of the granule, and ->g_ret pointing to
+ *		a valid 'struct granule *'.
+ *		This function sorts the supplied array in place.
+ * @n: Number of struct granule_set in array pointed to by @granules
+ *
+ * Returns:
+ *     True if all granules in @granules were successfully locked.
+ *
+ *     False if any two entries in @granules have the same ->addr, or
+ *     if, for any entry in @granules, any of the following is true:
+ *       - entry->addr is not aligned to the size of a granule
+ *       - entry->addr is out of range
+ *       - the state of the granule at entry->addr is not entry->state
+ *
+ * Locking only succeeds if the granules are in their expected states as per the
+ * locking rules in granule_types.h.
+ *
+ * If the function succeeds, for all items in @granules, ->g points to a locked
+ * granule in ->state and *->g_ret is set to the pointer value.
+ *
+ * If the function fails, no lock is held and no *->g_ret pointers are
+ * modified.
+ */
+static bool find_lock_granules(struct granule_set *granules,
+				unsigned long n)
+{
+	long i;
+
+	for (i = 0L; i < n; i++) {
+		granules[i].idx = i;
+	}
+
+	sort_granules(granules, n);
+
+	for (i = 0L; i < n; i++) {
+		/* Check for duplicates */
+		if ((i > 0L) && (granules[i].addr == granules[i - 1].addr)) {
+			goto out_err;
+		}
+
+		granules[i].g = find_lock_granule(granules[i].addr,
+						granules[i].state);
+		if (granules[i].g == NULL) {
+			goto out_err;
+		}
+	}
+
+	for (i = 0L; i < n; i++) {
+		*granules[i].g_ret = granules[i].g;
+	}
+
+	return true;
+
+out_err:
+	for (i = i - 1; i >= 0L; i--) {
+		granule_unlock(granules[i].g);
+	}
+
+	return false;
+}
+
+/*
+ * Find two granules and lock them in order of their address.
+ *
+ * See find_lock_granules().
+ */
+bool find_lock_two_granules(
+			unsigned long addr1,
+			enum granule_state expected_state1,
+			struct granule **g1,
+			unsigned long addr2,
+			enum granule_state expected_state2,
+			struct granule **g2)
+{
+	struct granule_set granules[] = {
+		{0U, addr1, expected_state1, NULL, g1},
+		{1U, addr2, expected_state2, NULL, g2}
+	};
+
+	assert((g1 != NULL) && (g2 != NULL));
+
+	return find_lock_granules(granules, ARRAY_SIZE(granules));
+}
+
+void granule_memzero(struct granule *g, enum buffer_slot slot)
+{
+	unsigned long *buf;
+
+	assert(g != NULL);
+
+	buf = granule_map(g, slot);
+	(void)memset(buf, 0, GRANULE_SIZE);
+	buffer_unmap(buf);
+}
+
+void granule_memzero_mapped(void *buf)
+{
+	(void)memset(buf, 0, GRANULE_SIZE);
+}
diff --git a/lib/realm/src/include/aarch64/slot_buf_arch.h b/lib/realm/src/include/aarch64/slot_buf_arch.h
new file mode 100644
index 0000000..15bc95b
--- /dev/null
+++ b/lib/realm/src/include/aarch64/slot_buf_arch.h
@@ -0,0 +1,12 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef SLOT_BUF_ARCH_H
+#define SLOT_BUF_ARCH_H
+
+#define buffer_arch_map			buffer_map_internal
+#define buffer_arch_unmap		buffer_unmap_internal
+
+#endif /* SLOT_BUF_ARCH_H */
diff --git a/lib/realm/src/include/fake_host/slot_buf_arch.h b/lib/realm/src/include/fake_host/slot_buf_arch.h
new file mode 100644
index 0000000..d71a2d8
--- /dev/null
+++ b/lib/realm/src/include/fake_host/slot_buf_arch.h
@@ -0,0 +1,22 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef SLOT_BUF_ARCH_H
+#define SLOT_BUF_ARCH_H
+
+#include <host_harness.h>
+
+static void *buffer_arch_map(enum buffer_slot slot,
+			      unsigned long addr, bool ns)
+{
+	return host_buffer_arch_map(slot, addr, ns);
+}
+
+static void buffer_arch_unmap(void *buf)
+{
+	return host_buffer_arch_unmap(buf);
+}
+
+#endif /* SLOT_BUF_ARCH_H */
diff --git a/lib/realm/src/s2tt.c b/lib/realm/src/s2tt.c
new file mode 100644
index 0000000..7b6f197
--- /dev/null
+++ b/lib/realm/src/s2tt.c
@@ -0,0 +1,886 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <arch_helpers.h>
+#include <attestation_token.h>
+#include <bitmap.h>
+#include <buffer.h>
+#include <gic.h>
+#include <granule.h>
+#include <memory_alloc.h>
+#include <realm.h>
+#include <ripas.h>
+#include <smc.h>
+#include <status.h>
+#include <stddef.h>
+#include <string.h>
+#include <table.h>
+
+/*
+ * For prototyping we assume 4K pages
+ */
+#define BLOCK_L2_SIZE		(GRANULE_SIZE * S2TTES_PER_S2TT)
+
+/*
+ * The maximum number of bits supported by the RMM for a stage 2 translation
+ * output address (including stage 2 table entries).
+ */
+#define S2TTE_OA_BITS			48
+
+#define DESC_TYPE_MASK			0x3UL
+#define S2TTE_L012_TABLE		0x3UL
+#define S2TTE_L012_BLOCK		0x1UL
+#define S2TTE_L3_PAGE			0x3UL
+#define S2TTE_Lx_INVALID		0x0UL
+
+/*
+ * The following constants for the mapping attributes (S2_TTE_MEMATTR_*)
+ * assume that HCR_EL2.FWB is set.
+ */
+#define S2TTE_MEMATTR_SHIFT		2
+#define S2TTE_MEMATTR_MASK		(0x7UL << S2TTE_MEMATTR_SHIFT)
+#define S2TTE_MEMATTR_FWB_NORMAL_WB	((1UL << 4) | (2UL << 2))
+#define S2TTE_MEMATTR_FWB_RESERVED	((1UL << 4) | (0UL << 2))
+
+#define S2TTE_AP_SHIFT			6
+#define S2TTE_AP_MASK			(3UL << S2TTE_AP_SHIFT)
+#define S2TTE_AP_RW			(3UL << S2TTE_AP_SHIFT)
+
+#define S2TTE_SH_SHIFT			8
+#define S2TTE_SH_MASK			(3UL << S2TTE_SH_SHIFT)
+#define S2TTE_SH_NS			(0UL << S2TTE_SH_SHIFT)
+#define S2TTE_SH_RESERVED		(1UL << S2TTE_SH_SHIFT)
+#define S2TTE_SH_OS			(2UL << S2TTE_SH_SHIFT)
+#define S2TTE_SH_IS			(3UL << S2TTE_SH_SHIFT)	/* Inner Shareable */
+
+/*
+ * We set HCR_EL2.FWB So we set bit[4] to 1 and bits[3:2] to 2 and force
+ * everyting to be Normal Write-Back
+ */
+#define S2TTE_MEMATTR_FWB_NORMAL_WB	((1UL << 4) | (2UL << 2))
+#define S2TTE_AF			(1UL << 10)
+#define S2TTE_XN			(2UL << 53)
+#define S2TTE_NS			(1UL << 55)
+
+#define S2TTE_ATTRS	(S2TTE_MEMATTR_FWB_NORMAL_WB | S2TTE_AP_RW | \
+			S2TTE_SH_IS | S2TTE_AF)
+
+#define S2TTE_TABLE	S2TTE_L012_TABLE
+#define S2TTE_BLOCK	(S2TTE_ATTRS | S2TTE_L012_BLOCK)
+#define S2TTE_PAGE	(S2TTE_ATTRS | S2TTE_L3_PAGE)
+#define S2TTE_BLOCK_NS	(S2TTE_NS | S2TTE_XN | S2TTE_AF | S2TTE_L012_BLOCK)
+#define S2TTE_PAGE_NS	(S2TTE_NS | S2TTE_XN | S2TTE_AF | S2TTE_L3_PAGE)
+#define S2TTE_INVALID	0
+
+/*
+ * The type of an S2TTE is one of the following:
+ *
+ * - Invalid
+ * - Valid page
+ * - Valid block
+ * - Table
+ *
+ * Within an invalid S2TTE for a Protected IPA, architecturally RES0 bits are
+ * used to encode the HIPAS and RIPAS.
+ *
+ * A valid S2TTE for a Protected IPA implies HIPAS=ASSIGNED and RIPAS=RAM.
+ *
+ * An invalid S2TTE for an Unprotected IPA implies HIPAS=INVALID_NS.
+ * A valid S2TTE for an Unprotected IPA implies HIPAS=VALID_NS.
+ *
+ * The following table defines the mapping from a (HIPAS, RIPAS) tuple to the
+ * value of the S2TTE.
+ *
+ * ------------------------------------------------------------------------------
+ * IPA		HIPAS		RIPAS		S2TTE value
+ * ==============================================================================
+ * Protected	UNASSIGNED	EMPTY		(S2TTE_INVALID_HIPAS_UNASSIGNED	|
+ *						 S2TTE_INVALID_RIPAS_EMPTY)
+ * Protected	UNASSIGNED	RAM		(S2TTE_INVALID_HIPAS_UNASSIGNED	|
+ *						 S2TTE_INVALID_RIPAS_RAM)
+ * Protected	ASSIGNED	EMPTY		(S2TTE_INVALID_HIPAS_ASSIGNED	|
+ *						 S2TTE_INVALID_RIPAS_EMPTY)
+ * Protected	ASSIGNED	RAM		Valid page / block with NS=0
+ * Protected	DESTROYED	*		S2TTE_INVALID_DESTROYED
+ * Unprotected	INVALID_NS	N/A		S2TTE_INVALID_UNPROTECTED
+ * Unprotected	VALID_NS	N/A		Valid page / block with NS=1
+ * ------------------------------------------------------------------------------
+ */
+
+#define S2TTE_INVALID_HIPAS_SHIFT	2
+#define S2TTE_INVALID_HIPAS_WIDTH	4
+#define S2TTE_INVALID_HIPAS_MASK	MASK(S2TTE_INVALID_HIPAS)
+
+#define S2TTE_INVALID_HIPAS_UNASSIGNED	(INPLACE(S2TTE_INVALID_HIPAS, 0))
+#define S2TTE_INVALID_HIPAS_ASSIGNED	(INPLACE(S2TTE_INVALID_HIPAS, 1))
+#define S2TTE_INVALID_HIPAS_DESTROYED	(INPLACE(S2TTE_INVALID_HIPAS, 2))
+
+#define S2TTE_INVALID_RIPAS_SHIFT	6
+#define S2TTE_INVALID_RIPAS_WIDTH	1
+#define S2TTE_INVALID_RIPAS_MASK	MASK(S2TTE_INVALID_RIPAS)
+
+#define S2TTE_INVALID_RIPAS_EMPTY	(INPLACE(S2TTE_INVALID_RIPAS, 0))
+#define S2TTE_INVALID_RIPAS_RAM		(INPLACE(S2TTE_INVALID_RIPAS, 1))
+
+#define S2TTE_INVALID_DESTROYED		S2TTE_INVALID_HIPAS_DESTROYED
+#define S2TTE_INVALID_UNPROTECTED	0x0UL
+
+#define NR_RTT_LEVELS	4
+
+/*
+ * Invalidates S2 TLB entries from [ipa, ipa + size] region tagged with `vmid`.
+ */
+static void stage2_tlbi_ipa(const struct realm_s2_context *s2_ctx,
+			    unsigned long ipa,
+			    unsigned long size)
+{
+	/*
+	 * Notes:
+	 *
+	 * - This follows the description provided in the Arm ARM on
+	 *   "Invalidation of TLB entries from stage 2 translations".
+	 *
+	 * - @TODO: Provide additional information to this primitive so that
+	 *   we can utilize:
+	 *   - The TTL level hint, see FEAT_TTL,
+	 *   - Final level lookup only invalidation,
+	 *   - Address range invalidation.
+	 */
+
+	/*
+	 * Save the current content of vttb_el2.
+	 */
+	unsigned long old_vttbr_el2 = read_vttbr_el2();
+
+	/*
+	 * Make 'vmid' the `current vmid`. Note that the tlbi instructions
+	 * bellow target the TLB entries that match the `current vmid`.
+	 */
+	write_vttbr_el2(INPLACE(VTTBR_EL2_VMID, s2_ctx->vmid));
+	isb();
+
+	/*
+	 * Invalidate entries in S2 TLB caches that
+	 * match both `ipa` & the `current vmid`.
+	 */
+	while (size != 0UL) {
+		tlbiipas2e1is(ipa >> 12);
+		size -= GRANULE_SIZE;
+		ipa += GRANULE_SIZE;
+	}
+	dsb(ish);
+
+	/*
+	 * The architecture does not require TLB invalidation by IPA to affect
+	 * combined Stage-1 + Stage-2 TLBs. Therefore we must invalidate all of
+	 * Stage-1 (tagged with the `current vmid`) after invalidating Stage-2.
+	 */
+	tlbivmalle1is();
+	dsb(ish);
+	isb();
+
+	/*
+	 * Restore the old content of vttb_el2.
+	 */
+	write_vttbr_el2(old_vttbr_el2);
+	isb();
+}
+
+/*
+ * Invalidate S2 TLB entries with "addr" IPA.
+ * Call this function after:
+ * 1.  A L3 page desc has been removed.
+ */
+void invalidate_page(const struct realm_s2_context *s2_ctx, unsigned long addr)
+{
+	stage2_tlbi_ipa(s2_ctx, addr, GRANULE_SIZE);
+}
+
+/*
+ * Invalidate S2 TLB entries with "addr" IPA.
+ * Call this function after:
+ * 1.  A L2 block desc has been removed, or
+ * 2a. A L2 table desc has been removed, where
+ * 2b. All S2TTEs in L3 table that the L2 table desc was pointed to were invalid.
+ */
+void invalidate_block(const struct realm_s2_context *s2_ctx, unsigned long addr)
+{
+	stage2_tlbi_ipa(s2_ctx, addr, GRANULE_SIZE);
+}
+
+/*
+ * Invalidate S2 TLB entries with "addr" IPA.
+ * Call this function after:
+ * 1a. A L2 table desc has been removed, where
+ * 1b. Some S2TTEs in the table that the L2 table desc was pointed to were valid.
+ */
+void invalidate_pages_in_block(const struct realm_s2_context *s2_ctx, unsigned long addr)
+{
+	stage2_tlbi_ipa(s2_ctx, addr, BLOCK_L2_SIZE);
+}
+
+/*
+ * Return the index of the entry describing @addr in the translation table at
+ * level @level.  This only works for non-concatenated page tables, so should
+ * not be called to get the index for the starting level.
+ *
+ * See the library pseudocode
+ * aarch64/translation/vmsa_addrcalc/AArch64.TTEntryAddress on which this is
+ * modeled.
+ */
+static unsigned long s2_addr_to_idx(unsigned long addr, long level)
+{
+	int levels = RTT_PAGE_LEVEL - level;
+	int lsb = levels * S2TTE_STRIDE + GRANULE_SHIFT;
+
+	addr >>= lsb;
+	addr &= (1UL << S2TTE_STRIDE) - 1;
+	return addr;
+}
+
+/*
+ * Return the index of the entry describing @addr in the translation table
+ * starting level.  This may return an index >= S2TTES_PER_S2TT when the
+ * combination of @start_level and @ipa_bits implies concatenated
+ * stage 2 tables.
+ *
+ * See the library pseudocode
+ * aarch64/translation/vmsa_addrcalc/AArch64.S2SLTTEntryAddress on which
+ * this is modeled.
+ */
+static unsigned long s2_sl_addr_to_idx(unsigned long addr, int start_level,
+				       unsigned long ipa_bits)
+{
+	int levels = RTT_PAGE_LEVEL - start_level;
+	int lsb = levels * S2TTE_STRIDE + GRANULE_SHIFT;
+
+	addr &= (1UL << ipa_bits) - 1UL;
+	addr >>= lsb;
+	return addr;
+}
+
+static unsigned long addr_level_mask(unsigned long addr, long level)
+{
+	int levels = RTT_PAGE_LEVEL - level;
+	unsigned int lsb = levels * S2TTE_STRIDE + GRANULE_SHIFT;
+	unsigned int msb = S2TTE_OA_BITS - 1;
+
+	return addr & BIT_MASK_ULL(msb, lsb);
+}
+
+static inline unsigned long table_entry_to_phys(unsigned long entry)
+{
+	return addr_level_mask(entry, RTT_PAGE_LEVEL);
+}
+
+static inline bool entry_is_table(unsigned long entry)
+{
+	return (entry & DESC_TYPE_MASK) == S2TTE_L012_TABLE;
+}
+
+static unsigned long __table_get_entry(struct granule *g_tbl,
+				       unsigned long idx)
+{
+	unsigned long *table, entry;
+
+	table = granule_map(g_tbl, SLOT_RTT);
+	entry = s2tte_read(&table[idx]);
+	buffer_unmap(table);
+
+	return entry;
+}
+
+static struct granule *__find_next_level_idx(struct granule *g_tbl,
+					     unsigned long idx)
+{
+	const unsigned long entry = __table_get_entry(g_tbl, idx);
+
+	if (!entry_is_table(entry)) {
+		return NULL;
+	}
+
+	return addr_to_granule(table_entry_to_phys(entry));
+}
+
+static struct granule *__find_lock_next_level(struct granule *g_tbl,
+					      unsigned long map_addr,
+					      long level)
+{
+	const unsigned long idx = s2_addr_to_idx(map_addr, level);
+	struct granule *g = __find_next_level_idx(g_tbl, idx);
+
+	if (g != NULL) {
+		granule_lock(g, GRANULE_STATE_RTT);
+	}
+
+	return g;
+}
+
+/*
+ * Walk an RTT until level @level using @map_addr.
+ * @g_root is the root (level 0) table and must be locked before the call.
+ * @start_level is the initial lookup level used for the stage 2 translation
+ * tables which may depend on the configuration of the realm, factoring in the
+ * IPA size of the realm and the desired starting level (within the limits
+ * defined by the Armv8 VMSA including options for stage 2 table concatenation).
+ * The function uses hand-over-hand locking to avoid race conditions and allow
+ * concurrent access to RTT tree which is not part of the current walk; when a
+ * next level table is reached it is locked before releasing previously locked
+ * table.
+ * The walk stops when either:
+ * - The entry found is a leaf entry (not an RTT Table entry), or
+ * - Level @level is reached.
+ *
+ * On return:
+ * - rtt_walk::last_level is the last level that has been reached by the walk.
+ * - rtt_walk.g_llt points to the TABLE granule at level @rtt_walk::level.
+ *   The granule is locked.
+ * - rtt_walk::index is the entry index at rtt_walk.g_llt for @map_addr.
+ */
+void rtt_walk_lock_unlock(struct granule *g_root,
+			  int start_level,
+			  unsigned long ipa_bits,
+			  unsigned long map_addr,
+			  long level,
+			  struct rtt_walk *wi)
+{
+	struct granule *g_tbls[NR_RTT_LEVELS] = { NULL };
+	unsigned long sl_idx;
+	int i, last_level;
+
+	assert(start_level >= MIN_STARTING_LEVEL);
+	assert(level >= start_level);
+	assert(map_addr < (1UL << ipa_bits));
+	assert(wi != NULL);
+
+	/* Handle concatenated starting level (SL) tables */
+	sl_idx = s2_sl_addr_to_idx(map_addr, start_level, ipa_bits);
+	if (sl_idx >= S2TTES_PER_S2TT) {
+		unsigned int tt_num = (sl_idx >> S2TTE_STRIDE);
+		struct granule *g_concat_root = g_root + tt_num;
+
+		granule_lock(g_concat_root, GRANULE_STATE_RTT);
+		granule_unlock(g_root);
+		g_root = g_concat_root;
+	}
+
+	g_tbls[start_level] = g_root;
+	for (i = start_level; i < level; i++) {
+		/*
+		 * Lock next RTT level. Correct locking order is guaranteed
+		 * because reference is obtained from a locked granule
+		 * (previous level). Also, hand-over-hand locking/unlocking is
+		 * used to avoid race conditions.
+		 */
+		g_tbls[i + 1] = __find_lock_next_level(g_tbls[i], map_addr, i);
+		if (g_tbls[i + 1] == NULL) {
+			last_level = i;
+			goto out;
+		}
+		granule_unlock(g_tbls[i]);
+	}
+
+	last_level = level;
+out:
+	wi->last_level = last_level;
+	wi->g_llt = g_tbls[last_level];
+	wi->index = s2_addr_to_idx(map_addr, last_level);
+}
+
+/*
+ * Creates a value which can be OR'd with an s2tte to set RIPAS=@ripas.
+ */
+unsigned long s2tte_create_ripas(enum ripas ripas)
+{
+	if (ripas == RMI_EMPTY) {
+		return S2TTE_INVALID_RIPAS_EMPTY;
+	}
+	return S2TTE_INVALID_RIPAS_RAM;
+}
+
+/*
+ * Creates an invalid s2tte with HIPAS=UNASSIGNED and RIPAS=@ripas.
+ */
+unsigned long s2tte_create_unassigned(enum ripas ripas)
+{
+	return S2TTE_INVALID_HIPAS_UNASSIGNED | s2tte_create_ripas(ripas);
+}
+
+/*
+ * Creates an invalid s2tte with HIPAS=DESTROYED.
+ */
+unsigned long s2tte_create_destroyed(void)
+{
+	return S2TTE_INVALID_DESTROYED;
+}
+
+/*
+ * Creates an invalid s2tte with output address @pa, HIPAS=ASSIGNED and
+ * RIPAS=EMPTY, at level @level.
+ */
+unsigned long s2tte_create_assigned_empty(unsigned long pa, long level)
+{
+	assert(level >= RTT_MIN_BLOCK_LEVEL);
+	assert(addr_is_level_aligned(pa, level));
+	return (pa | S2TTE_INVALID_HIPAS_ASSIGNED | S2TTE_INVALID_RIPAS_EMPTY);
+}
+
+/*
+ * Creates a page or block s2tte for a Protected IPA, with output address @pa.
+ */
+unsigned long s2tte_create_valid(unsigned long pa, long level)
+{
+	assert(level >= RTT_MIN_BLOCK_LEVEL);
+	assert(addr_is_level_aligned(pa, level));
+	if (level == RTT_PAGE_LEVEL) {
+		return (pa | S2TTE_PAGE);
+	}
+	return (pa | S2TTE_BLOCK);
+}
+
+/*
+ * Creates an invalid s2tte with HIPAS=INVALID_NS.
+ */
+unsigned long s2tte_create_invalid_ns(void)
+{
+	return S2TTE_INVALID_UNPROTECTED;
+}
+
+/*
+ * Creates a page or block s2tte for an Unprotected IPA at level @level.
+ *
+ * The following S2 TTE fields are provided through @s2tte argument:
+ * - The physical address
+ * - MemAttr
+ * - S2AP
+ * - Shareability
+ */
+unsigned long s2tte_create_valid_ns(unsigned long s2tte, long level)
+{
+	assert(level >= RTT_MIN_BLOCK_LEVEL);
+	if (level == RTT_PAGE_LEVEL) {
+		return (s2tte | S2TTE_PAGE_NS);
+	}
+	return (s2tte | S2TTE_BLOCK_NS);
+}
+
+/*
+ * Validate the portion of NS S2TTE that is provided by the host.
+ */
+bool host_ns_s2tte_is_valid(unsigned long s2tte, long level)
+{
+	unsigned long mask = addr_level_mask(~0UL, level) |
+			     S2TTE_MEMATTR_MASK |
+			     S2TTE_AP_MASK |
+			     S2TTE_SH_MASK;
+
+	/*
+	 * Test that all fields that are not controlled by the host are zero
+	 * and that the output address is correctly aligned. Note that
+	 * the host is permitted to map any physical address outside PAR.
+	 */
+	if ((s2tte & ~mask) != 0UL) {
+		return false;
+	}
+
+	/*
+	 * Only one value masked by S2TTE_MEMATTR_MASK is invalid/reserved.
+	 */
+	if ((s2tte & S2TTE_MEMATTR_MASK) == S2TTE_MEMATTR_FWB_RESERVED) {
+		return false;
+	}
+
+	/*
+	 * Only one value masked by S2TTE_SH_MASK is invalid/reserved.
+	 */
+	if ((s2tte & S2TTE_SH_MASK) == S2TTE_SH_RESERVED) {
+		return false;
+	}
+
+	/*
+	 * Note that all the values that are masked by S2TTE_AP_MASK are valid.
+	 */
+	return true;
+}
+
+/*
+ * Returns the portion of NS S2TTE that is set by the host.
+ */
+unsigned long host_ns_s2tte(unsigned long s2tte, long level)
+{
+	unsigned long mask = addr_level_mask(~0UL, level) |
+			     S2TTE_MEMATTR_MASK |
+			     S2TTE_AP_MASK |
+			     S2TTE_SH_MASK;
+	return (s2tte & mask);
+}
+
+/*
+ * Creates a table s2tte at level @level with output address @pa.
+ */
+unsigned long s2tte_create_table(unsigned long pa, long level)
+{
+	assert(level < RTT_PAGE_LEVEL);
+	assert(GRANULE_ALIGNED(pa));
+
+	return (pa | S2TTE_TABLE);
+}
+
+/*
+ * Returns true if @s2tte has HIPAS=@hipas.
+ */
+static bool s2tte_has_hipas(unsigned long s2tte, unsigned long hipas)
+{
+	unsigned long desc_type = s2tte & DESC_TYPE_MASK;
+	unsigned long invalid_desc_hipas = s2tte & S2TTE_INVALID_HIPAS_MASK;
+
+	if ((desc_type != S2TTE_Lx_INVALID) || (invalid_desc_hipas != hipas)) {
+		return false;
+	}
+	return true;
+}
+
+/*
+ * Returns true if @s2tte has HIPAS=UNASSIGNED or HIPAS=INVALID_NS.
+ */
+bool s2tte_is_unassigned(unsigned long s2tte)
+{
+	return s2tte_has_hipas(s2tte, S2TTE_INVALID_HIPAS_UNASSIGNED);
+}
+
+/*
+ * Returns true if @s2tte has HIPAS=DESTROYED.
+ */
+bool s2tte_is_destroyed(unsigned long s2tte)
+{
+	return s2tte_has_hipas(s2tte, S2TTE_INVALID_HIPAS_DESTROYED);
+}
+
+/*
+ * Returns true if @s2tte has HIPAS=ASSIGNED.
+ */
+bool s2tte_is_assigned(unsigned long s2tte, long level)
+{
+	(void)level;
+
+	return s2tte_has_hipas(s2tte, S2TTE_INVALID_HIPAS_ASSIGNED);
+}
+
+static bool s2tte_check(unsigned long s2tte, long level, unsigned long ns)
+{
+	unsigned long desc_type;
+
+	if ((s2tte & S2TTE_NS) != ns) {
+		return false;
+	}
+
+	desc_type = s2tte & DESC_TYPE_MASK;
+
+	/* Only pages at L3 and valid blocks at L2 allowed */
+	if (((level == RTT_PAGE_LEVEL) && (desc_type == S2TTE_L3_PAGE)) ||
+	    ((level == RTT_MIN_BLOCK_LEVEL) && (desc_type == S2TTE_BLOCK))) {
+		return true;
+	}
+
+	return false;
+}
+
+/*
+ * Returns true if @s2tte is a page or block s2tte, and NS=0.
+ */
+bool s2tte_is_valid(unsigned long s2tte, long level)
+{
+	return s2tte_check(s2tte, level, 0UL);
+}
+
+/*
+ * Returns true if @s2tte is a page or block s2tte, and NS=1.
+ */
+bool s2tte_is_valid_ns(unsigned long s2tte, long level)
+{
+	return s2tte_check(s2tte, level, S2TTE_NS);
+}
+
+/*
+ * Returns true if @s2tte is a table at level @level.
+ */
+bool s2tte_is_table(unsigned long s2tte, long level)
+{
+	unsigned long desc_type = s2tte & DESC_TYPE_MASK;
+
+	if ((level < RTT_PAGE_LEVEL) && (desc_type == S2TTE_TABLE)) {
+		return true;
+	}
+
+	return false;
+}
+
+/*
+ * Returns RIPAS of @s2tte.
+ *
+ * Caller should ensure that HIPAS=UNASSIGNED or HIPAS=ASSIGNED.
+ * The s2tte must be not valid/invalid descriptor.
+ */
+enum ripas s2tte_get_ripas(unsigned long s2tte)
+{
+	unsigned long desc_ripas = s2tte & S2TTE_INVALID_RIPAS_MASK;
+
+	/*
+	 * If valid s2tte descriptor is passed, then ensure S2AP[0]
+	 * bit is 1 (S2AP is set to RW for lower EL), which corresponds
+	 * to RIPAS_RAM (bit[6]) on a valid descriptor.
+	 */
+	if (((s2tte & DESC_TYPE_MASK) != S2TTE_Lx_INVALID) &&
+	     (desc_ripas != S2TTE_INVALID_RIPAS_RAM)) {
+		assert(false);
+	}
+
+	if (desc_ripas == S2TTE_INVALID_RIPAS_EMPTY) {
+		return RMI_EMPTY;
+	}
+
+	return RMI_RAM;
+}
+
+/*
+ * Populates @s2tt with s2ttes which have HIPAS=UNASSIGNED and RIPAS=@ripas.
+ *
+ * The granule is populated before it is made a table,
+ * hence, don't use s2tte_write for access.
+ */
+void s2tt_init_unassigned(unsigned long *s2tt, enum ripas ripas)
+{
+	for (unsigned int i = 0U; i < S2TTES_PER_S2TT; i++) {
+		s2tt[i] = s2tte_create_unassigned(ripas);
+	}
+
+	dsb(ish);
+}
+
+/*
+ * Populates @s2tt with s2ttes which have HIPAS=DESTROYED.
+ *
+ * The granule is populated before it is made a table,
+ * hence, don't use s2tte_write for access.
+ */
+void s2tt_init_destroyed(unsigned long *s2tt)
+{
+	for (unsigned int i = 0U; i < S2TTES_PER_S2TT; i++) {
+		s2tt[i] = s2tte_create_destroyed();
+	}
+
+	dsb(ish);
+}
+
+unsigned long s2tte_map_size(int level)
+{
+	int levels, lsb;
+
+	assert(level <= RTT_PAGE_LEVEL);
+
+	levels = RTT_PAGE_LEVEL - level;
+	lsb = levels * S2TTE_STRIDE + GRANULE_SHIFT;
+	return 1UL << lsb;
+}
+
+/*
+ * Populates @s2tt with HIPAS=ASSIGNED, RIPAS=EMPTY s2ttes that refer to a
+ * contiguous memory block starting at @pa, and mapped at level @level.
+ *
+ * The granule is populated before it is made a table,
+ * hence, don't use s2tte_write for access.
+ */
+void s2tt_init_assigned_empty(unsigned long *s2tt, unsigned long pa, long level)
+{
+	const unsigned long map_size = s2tte_map_size(level);
+	unsigned int i;
+
+	for (i = 0U; i < S2TTES_PER_S2TT; i++) {
+		s2tt[i] = s2tte_create_assigned_empty(pa, level);
+		pa += map_size;
+	}
+	dsb(ish);
+}
+
+/*
+ * Populates @s2tt with HIPAS=VALID, RIPAS=@ripas s2ttes that refer to a
+ * contiguous memory block starting at @pa, and mapped at level @level.
+ *
+ * The granule is populated before it is made a table,
+ * hence, don't use s2tte_write for access.
+ */
+void s2tt_init_valid(unsigned long *s2tt, unsigned long pa, long level)
+{
+	const unsigned long map_size = s2tte_map_size(level);
+	unsigned int i;
+
+	for (i = 0U; i < S2TTES_PER_S2TT; i++) {
+		s2tt[i] = s2tte_create_valid(pa, level);
+		pa += map_size;
+	}
+	dsb(ish);
+}
+
+/*
+ * Populates @s2tt with HIPAS=VALID_NS, RIPAS=@ripas s2ttes that refer to a
+ * contiguous memory block starting at @pa, and mapped at level @level.
+ *
+ * The granule is populated before it is made a table,
+ * hence, don't use s2tte_write for access.
+ */
+void s2tt_init_valid_ns(unsigned long *s2tt, unsigned long pa, long level)
+{
+	const unsigned long map_size = s2tte_map_size(level);
+	unsigned int i;
+
+	for (i = 0U; i < S2TTES_PER_S2TT; i++) {
+		s2tt[i] = s2tte_create_valid_ns(pa, level);
+		pa += map_size;
+	}
+	dsb(ish);
+}
+
+/* Returns physical address of a page entry or block */
+unsigned long s2tte_pa(unsigned long s2tte, long level)
+{
+	if (s2tte_is_unassigned(s2tte) || s2tte_is_destroyed(s2tte) ||
+	    s2tte_is_table(s2tte, level)) {
+		assert(false);
+	}
+	return addr_level_mask(s2tte, level);
+}
+
+/* Returns physical address of a table entry */
+unsigned long s2tte_pa_table(unsigned long s2tte, long level)
+{
+	assert(s2tte_is_table(s2tte, level));
+	return addr_level_mask(s2tte, RTT_PAGE_LEVEL);
+}
+
+bool addr_is_level_aligned(unsigned long addr, long level)
+{
+	return (addr == addr_level_mask(addr, level));
+}
+
+typedef bool (*s2tte_type_checker)(unsigned long s2tte);
+
+static bool __table_is_uniform_block(unsigned long *table,
+			      s2tte_type_checker s2tte_is_x,
+			      enum ripas *ripas_ptr)
+{
+	unsigned long s2tte = s2tte_read(&table[0]);
+	enum ripas ripas;
+	unsigned int i;
+
+	if (!s2tte_is_x(s2tte)) {
+		return false;
+	}
+
+	if (ripas_ptr != NULL) {
+		ripas = s2tte_get_ripas(s2tte);
+	}
+
+	for (i = 1U; i < S2TTES_PER_S2TT; i++) {
+		s2tte = s2tte_read(&table[i]);
+
+		if (!s2tte_is_x(s2tte)) {
+			return false;
+		}
+
+		if ((ripas_ptr != NULL) &&
+		    (s2tte_get_ripas(s2tte) != ripas)) {
+			return false;
+		}
+	}
+
+	if (ripas_ptr != NULL) {
+		*ripas_ptr = ripas;
+	}
+
+	return true;
+}
+
+/*
+ * Returns true if all s2ttes in @table have HIPAS=UNASSIGNED and
+ * have the same RIPAS.
+ *
+ * If return value is true, the RIPAS value is returned in @ripas.
+ */
+bool table_is_unassigned_block(unsigned long *table, enum ripas *ripas)
+{
+	return __table_is_uniform_block(table, s2tte_is_unassigned, ripas);
+}
+
+/*
+ * Returns true if all s2ttes in @table have HIPAS=DESTROYED.
+ */
+bool table_is_destroyed_block(unsigned long *table)
+{
+	return __table_is_uniform_block(table, s2tte_is_destroyed, NULL);
+}
+
+typedef bool (*s2tte_type_level_checker)(unsigned long s2tte, long level);
+
+static bool __table_maps_block(unsigned long *table,
+			       long level,
+			       s2tte_type_level_checker s2tte_is_x)
+{
+	unsigned long base_pa;
+	unsigned long map_size = s2tte_map_size(level);
+	unsigned long s2tte = s2tte_read(&table[0]);
+	unsigned int i;
+
+	if (!s2tte_is_x(s2tte, level)) {
+		return false;
+	}
+
+	base_pa = s2tte_pa(s2tte, level);
+	if (!addr_is_level_aligned(base_pa, level - 1L)) {
+		return false;
+	}
+
+	for (i = 1U; i < S2TTES_PER_S2TT; i++) {
+		unsigned long expected_pa = base_pa + (i * map_size);
+
+		s2tte = s2tte_read(&table[i]);
+
+		if (!s2tte_is_x(s2tte, level)) {
+			return false;
+		}
+
+		if (s2tte_pa(s2tte, level) != expected_pa) {
+			return false;
+		}
+	}
+
+	return true;
+}
+
+/*
+ * Returns true if all s2ttes in @table have HIPAS=ASSIGNED
+ * and refer to a contiguous block of granules aligned to @level - 1.
+ */
+bool table_maps_assigned_block(unsigned long *table, long level)
+{
+	return __table_maps_block(table, level, s2tte_is_assigned);
+}
+
+/*
+ * Returns true if all s2ttes in @table have HIPAS=VALID and
+ * refer to a contiguous block of granules aligned to @level - 1.
+ */
+bool table_maps_valid_block(unsigned long *table, long level)
+{
+	return __table_maps_block(table, level, s2tte_is_valid);
+}
+
+/*
+ * Returns true if all s2ttes in @table have HIPAS=VALID_NS and
+ * refer to a contiguous block of granules aligned to @level - 1.
+ */
+bool table_maps_valid_ns_block(unsigned long *table, long level)
+{
+	return __table_maps_block(table, level, s2tte_is_valid_ns);
+}
diff --git a/lib/realm/src/sve.c b/lib/realm/src/sve.c
new file mode 100644
index 0000000..e3b3348
--- /dev/null
+++ b/lib/realm/src/sve.c
@@ -0,0 +1,36 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <sve.h>
+
+/*
+ * Save the SVE context to memory.
+ * The function saves the maximum implemented
+ * SVE context size.
+ */
+void save_sve_state(struct sve_state *sve)
+{
+	assert(sve != NULL);
+
+	save_sve_zcr_fpu_state(sve->zcr_fpu);
+	save_sve_z_state(sve->z);
+	save_sve_p_ffr_state(sve->p_ffr);
+}
+
+/*
+ * Restore the SVE context from memory.
+ * The function restores the maximum implemented
+ * SVE context size.
+ */
+void restore_sve_state(struct sve_state *sve)
+{
+	assert(sve != NULL);
+
+	restore_sve_z_state(sve->z);
+	restore_sve_p_ffr_state(sve->p_ffr);
+	restore_sve_zcr_fpu_state(sve->zcr_fpu);
+}
diff --git a/lib/rmm_el3_ifc/CMakeLists.txt b/lib/rmm_el3_ifc/CMakeLists.txt
new file mode 100644
index 0000000..0c41c57
--- /dev/null
+++ b/lib/rmm_el3_ifc/CMakeLists.txt
@@ -0,0 +1,28 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+add_library(rmm-lib-rmm_el3_ifc)
+
+target_include_directories(rmm-lib-rmm_el3_ifc
+    PUBLIC "include"
+    PRIVATE "src")
+
+target_link_libraries(rmm-lib-rmm_el3_ifc
+    PRIVATE
+        rmm-lib-arch
+        rmm-lib-common
+        rmm-lib-debug
+        rmm-lib-xlat
+        rmm-lib-smc)
+
+target_sources(rmm-lib-rmm_el3_ifc
+    PRIVATE "src/rmm_el3_ifc.c"
+            "src/rmm_el3_ifc_manifest.c"
+            "src/rmm_el3_runtime.c")
+
+if (NOT RMM_ARCH STREQUAL fake_host)
+    target_sources(rmm-lib-rmm_el3_ifc
+        PRIVATE "src/${RMM_ARCH}/rmm_el3_ifc_helpers.S")
+endif()
diff --git a/lib/rmm_el3_ifc/include/rmm_el3_ifc.h b/lib/rmm_el3_ifc/include/rmm_el3_ifc.h
new file mode 100644
index 0000000..4ec8ff5
--- /dev/null
+++ b/lib/rmm_el3_ifc/include/rmm_el3_ifc.h
@@ -0,0 +1,221 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef RMM_EL3_IFC_H
+#define RMM_EL3_IFC_H
+
+#ifndef __ASSEMBLER__
+
+#include <arch_helpers.h>
+#include <sizes.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <utils_def.h>
+
+/****************************************************************************
+ * Boot interface related functions
+ ***************************************************************************/
+
+/*
+ * Accessors to the parameters obtained through the boot interface arguments.
+ */
+unsigned int rmm_el3_ifc_get_version(void);
+uintptr_t rmm_el3_ifc_get_shared_buf_pa(void);
+
+static inline size_t rmm_el3_ifc_get_shared_buf_size(void)
+{
+	return SZ_4K;
+}
+
+/*
+ * Validate the boot arguments and Initialize the rmm_el3_ifc library.
+ * This function must be called only once during cold boot.
+ *
+ * This function must be called prior to enable the MMU and data cache for
+ * RMM execution.
+ *
+ * Args:
+ *	- x0 - x3: Arguments passed through registers x0 to x3.
+ *	- shared_buf_va: Virtual address where the RMM-EL3 shared
+ *	  will be mapped by the platform.
+ *
+ * Return:
+ *	- 0 on success or a negative error code otherwise.
+ */
+int rmm_el3_ifc_init(unsigned long x0, unsigned long x1, unsigned long x2,
+		     unsigned long x3, uintptr_t shared_buf_va);
+
+/*
+ * This function performs an early validation of the CPU Id received
+ * during warm boot and stores it into tpidr_el2.
+ *
+ * If the validation fails it will call into EL3 and will not return
+ * to the caller.
+ *
+ * Args:
+ *	- x0: CPU Id received from EL3.
+ * Return:
+ *	- Validated CPU Id or will not return on an error.
+ */
+unsigned int rmm_el3_ifc_validate_cpuid(unsigned long x0);
+
+/*
+ * Return a pointer to the RMM <-> EL3 shared pointer and lock it to prevent
+ * concurrent access.
+ *
+ * Return:	Exclusive pointer to the RMM <-> EL3 shared area.
+ */
+uintptr_t rmm_el3_ifc_get_shared_buf_locked(void);
+
+/*
+ * Release the RMM <-> EL3 buffer.
+ */
+void rmm_el3_ifc_release_shared_buf(void);
+
+/*****************************************************************************
+ * Boot Manifest functions and structures.
+ ****************************************************************************/
+
+/* Boot manifest core structure as per v0.1 */
+struct rmm_core_manifest {
+	uint32_t version;	/* Manifest version */
+	uintptr_t plat_data;	/* Manifest platform data */
+};
+
+COMPILER_ASSERT(offsetof(struct rmm_core_manifest, version) == 0);
+COMPILER_ASSERT(offsetof(struct rmm_core_manifest, plat_data) == 8);
+
+/*
+ * Accessors to the Boot Manifest data.
+ */
+unsigned int rmm_el3_ifc_get_manifest_version(void);
+
+/*
+ * Return a pointer to the platform manifest data if setup by EL3 Firmware.
+ *
+ * This function must be called only after the core manifest has
+ * been processed (See rmm_el3_ifc_process_boot_manifest()). Also, since
+ * the shared buffer can be reclaimed for communication during rmm_main(), we
+ * restrict this call to be allowed before the MMU is enabled by the platform.
+ */
+uintptr_t rmm_el3_ifc_get_plat_manifest_pa(void);
+
+/****************************************************************************
+ * RMM-EL3 Runtime APIs
+ ***************************************************************************/
+
+/*
+ * Get the realm attestation key to sign the realm attestation token. It is
+ * expected that only the private key is retrieved in raw format.
+ *
+ * Args:
+ *	- buf:		Pointer to the buffer used to get the attestation key
+ *			from EL3. This must belong to the RMM-EL3 shared memory
+ *			and must be locked before use.
+ *	- buflen	Maximum size for the Realm Attestation Key.
+ *	- len:		Pointer to a size_t variable to store the size of the
+ *			received realm attestation key.
+ *	- crv:		ECC Crve type for querying attestation key from monitor.
+ *
+ * Return:
+ *	- 0 On success or a negative error code otherwise.
+ */
+int rmm_el3_ifc_get_realm_attest_key(uintptr_t buf, size_t buflen,
+				     size_t *len, unsigned int crv);
+
+/*
+ * Get the platform token from the EL3 firmware and pass the public hash
+ * value to it.
+ * The caller of this API should have filled the public key hash at `buf`
+ * and the length of the key hash must be stored in hash_size.
+ *
+ * Args:
+ *	- buf:		Pointer to the buffer used to get the platform token
+ *			from EL3. This must belong to the RMM-EL3 shared memory
+ *			and must be locked before use.
+ *	- buflen	Maximum size for the Platform Token.
+ *	- len:		Pointer where the size of the retrieved platform token
+ *			will be stored.
+ *	- hash_size:	Size of the SHA digest used for the token generation.
+ *
+ * Return:
+ *	- 0 On success or a negative error code otherwise.
+ */
+int rmm_el3_ifc_get_platform_token(uintptr_t buf, size_t buflen,
+				   size_t *len, size_t hash_size);
+
+#endif /* __ASSEMBLER__ */
+
+/*************************************
+ * SMC codes for the EL3-RMM interface
+ *************************************/
+
+#define SMC_RMM_GET_REALM_ATTEST_KEY	SMC64_STD_FID(RMM_EL3, U(2))
+#define SMC_RMM_GET_PLAT_TOKEN	SMC64_STD_FID(RMM_EL3, U(3))
+
+					/* 0x1CF */
+#define SMC_RMM_BOOT_COMPLETE		SMC64_STD_FID(RMM_EL3, U(0x1F))
+
+/* SMC_RMM_BOOT_COMPLETE return codes */
+#define E_RMM_BOOT_SUCCESS				(0)
+#define E_RMM_BOOT_UNKNOWN_ERROR			(-1)
+#define E_RMM_BOOT_VERSION_MISMATCH			(-2)
+#define E_RMM_BOOT_CPUS_OUT_OF_RANGE			(-3)
+#define E_RMM_BOOT_CPU_ID_OUT_OF_RANGE			(-4)
+#define E_RMM_BOOT_INVALID_SHARED_POINTER		(-5)
+#define E_RMM_BOOT_MANIFEST_VERSION_NOT_SUPPORTED	(-6)
+#define E_RMM_BOOT_MANIFEST_DATA_ERROR			(-7)
+
+/************************
+ * Version related macros
+ ************************/
+
+/*
+ * Boot Interface version encoding:
+ *	- Bit[31] RES0
+ *	- Bits [30:16] Major version
+ *	- Bits [15:0] Minor version
+ */
+#define RMM_EL3_IFC_GET_VERS_MAJOR(_version)				\
+				(((_version) >> 16) & 0x7FFF)
+#define RMM_EL3_IFC_GET_VERS_MINOR(_version)				\
+				((_version) & 0xFFFF)
+#define RMM_EL3_IFC_SUPPORTED_VERSION (					\
+		(((RMM_EL3_IFC_VERS_MAJOR) & 0x7FFF) << 16) |		\
+		((RMM_EL3_IFC_VERS_MINOR) & 0xFFFF)			\
+	)
+
+/*
+ * The Major version value for the Boot Interface supported by this
+ * implementation of RMM.
+ */
+#define RMM_EL3_IFC_VERS_MAJOR		(U(0))
+
+/*
+ * The Minor version value for the Boot interface supported by this
+ * implementation of RMM.
+ */
+#define RMM_EL3_IFC_VERS_MINOR		(U(1))
+
+/*
+ * The Major version value for the Boot Manifest supported by this
+ * implementation of RMM.
+ */
+#define RMM_EL3_MANIFEST_VERS_MAJOR	(U(0))
+
+/*
+ * The Minor version value for the Boot Manifest supported by this
+ * implementation of RMM.
+ */
+#define RMM_EL3_MANIFEST_VERS_MINOR	(U(1))
+
+#define RMM_EL3_MANIFEST_GET_VERS_MAJOR					\
+				RMM_EL3_IFC_GET_VERS_MAJOR
+#define RMM_EL3_MANIFEST_GET_VERS_MINOR					\
+				RMM_EL3_IFC_GET_VERS_MINOR
+#define RMM_EL3_MANIFEST_VERSION					\
+				RMM_EL3_IFC_SUPPORTED_VERSION
+
+#endif /* RMM_EL3_IFC_H */
diff --git a/lib/rmm_el3_ifc/src/aarch64/rmm_el3_ifc_helpers.S b/lib/rmm_el3_ifc/src/aarch64/rmm_el3_ifc_helpers.S
new file mode 100644
index 0000000..d3fdb1c
--- /dev/null
+++ b/lib/rmm_el3_ifc/src/aarch64/rmm_el3_ifc_helpers.S
@@ -0,0 +1,42 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <asm_macros.S>
+#include <rmm_el3_ifc.h>
+#include <smc.h>
+
+	.globl rmm_el3_ifc_validate_cpuid
+
+/*
+ * This function performs an early validation of the CPU Id received
+ * during warm boot and stores it into tpidr_el2.
+ *
+ * If the validation fails it will call into EL3 and will not return
+ * to the caller.
+ *
+ * It returns the CPU Id.
+ *
+ * Clobber list: x0, x1
+ */
+func rmm_el3_ifc_validate_cpuid
+	/*
+	 * Check that the current CPU Id does not exceed the maximum allowed.
+	 */
+	mov_imm	x1, MAX_CPUS
+	cmp	x0, x1
+	b.hs	1f
+	/* Setup this CPU Id */
+	msr	tpidr_el2, x0
+	ret
+
+1:
+	/* CPU Id out of range */
+	mov_imm	x0, SMC_RMM_BOOT_COMPLETE
+	mov_imm	x1, E_RMM_BOOT_CPU_ID_OUT_OF_RANGE
+	smc	#0
+	/* EL3 Firmware should never return here, so panic if it does */
+	asm_panic
+
+endfunc rmm_el3_ifc_validate_cpuid
diff --git a/lib/rmm_el3_ifc/src/rmm_el3_ifc.c b/lib/rmm_el3_ifc/src/rmm_el3_ifc.c
new file mode 100644
index 0000000..ab57aec
--- /dev/null
+++ b/lib/rmm_el3_ifc/src/rmm_el3_ifc.c
@@ -0,0 +1,129 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <debug.h>
+#include <rmm_el3_ifc.h>
+#include <rmm_el3_ifc_priv.h>
+#include <smc.h>
+#include <stdint.h>
+#include <xlat_defs.h>
+
+/* Boot Interface arguments */
+static uintptr_t rmm_shared_buffer_start_pa;
+static unsigned long rmm_el3_ifc_abi_version;
+
+/* Platform paramters */
+uintptr_t rmm_shared_buffer_start_va;
+
+/* Internal status */
+static bool initialized;
+
+/*
+ * Abort the boot process and return to EL3 FW reporting
+ * the ec error code.
+ */
+__dead2 static void report_fail_to_el3(uint64_t ec)
+{
+	(void)monitor_call(SMC_RMM_BOOT_COMPLETE,
+			   ec, 0UL, 0UL, 0UL, 0UL, 0UL);
+	/* EL3 should never return back here */
+	panic();
+}
+
+/*
+ * Validate the boot arguments and Initialize the rmm_el3_ifc library.
+ * This function must be called only once during cold boot.
+ *
+ * This function must be called prior to enable the MMU and data cache.
+ */
+int rmm_el3_ifc_init(unsigned long x0, unsigned long x1, unsigned long x2,
+		     unsigned long x3, uintptr_t shared_buf_va)
+{
+	assert(is_mmu_enabled() == false);
+	assert(initialized == false);
+	assert((shared_buf_va & PAGE_SIZE_MASK) == 0UL);
+	assert(shared_buf_va != 0UL);
+
+	/*
+	 * Validate that the version number is correct. Only the Major is
+	 * considered for this check.
+	 *
+	 * Version Minor will be used to implement compatibility where
+	 * feasible.
+	 *
+	 * x1: RMM-EL3 Interface version.
+	 */
+	if (RMM_EL3_IFC_GET_VERS_MAJOR(x1) != RMM_EL3_IFC_VERS_MAJOR) {
+		report_fail_to_el3(E_RMM_BOOT_VERSION_MISMATCH);
+	}
+
+	/*
+	 * Validate the number of CPUs received from EL3.
+	 *
+	 * x2: Number of CPUs in the system as reported by EL3.
+	 */
+	if (x2 > MAX_CPUS) {
+		report_fail_to_el3(E_RMM_BOOT_CPUS_OUT_OF_RANGE);
+	}
+
+	/*
+	 * Validate that the CPU Id is in the range of the maximum
+	 * number of CPUs.
+	 *
+	 * x0: CPU Id.
+	 * x2: Number of CPUs in the system as reported by EL3.
+	 */
+	if (x0 >= x2) {
+		report_fail_to_el3(E_RMM_BOOT_CPU_ID_OUT_OF_RANGE);
+	}
+
+	/*
+	 * Validate that the shared buffer pointer is not NULL.
+	 *
+	 * x3: Pointer to the start of the EL3-RMM shared buffer.
+	 */
+	if ((x3 == 0UL) || ((x3 & PAGE_SIZE_MASK) != 0U)) {
+		report_fail_to_el3(E_RMM_BOOT_INVALID_SHARED_POINTER);
+	}
+
+	rmm_el3_ifc_abi_version = x1;
+	rmm_shared_buffer_start_pa = (uintptr_t)x3;
+	rmm_shared_buffer_start_va = shared_buf_va;
+
+	initialized = true;
+
+	flush_dcache_range((uintptr_t)(void *)&rmm_shared_buffer_start_pa,
+			 sizeof(rmm_shared_buffer_start_pa));
+	flush_dcache_range((uintptr_t)(void *)&rmm_el3_ifc_abi_version,
+			 sizeof(rmm_el3_ifc_abi_version));
+	flush_dcache_range((uintptr_t)(void *)&rmm_shared_buffer_start_va,
+			 sizeof(rmm_shared_buffer_start_va));
+	flush_dcache_range((uintptr_t)(void *)&initialized, sizeof(bool));
+
+	/* Process the Boot Manifest */
+	rmm_el3_ifc_process_boot_manifest();
+
+	return 0;
+}
+
+/*
+ * Get a pointer to the PA of the start of the RMM<->EL3 shared area.
+ */
+uintptr_t rmm_el3_ifc_get_shared_buf_pa(void)
+{
+	assert((initialized == true));
+
+	return rmm_shared_buffer_start_pa;
+}
+
+/* Get the raw value of the boot interface version */
+unsigned int rmm_el3_ifc_get_version(void)
+{
+	assert(initialized == true);
+
+	return rmm_el3_ifc_abi_version;
+}
diff --git a/lib/rmm_el3_ifc/src/rmm_el3_ifc_manifest.c b/lib/rmm_el3_ifc/src/rmm_el3_ifc_manifest.c
new file mode 100644
index 0000000..00a405c
--- /dev/null
+++ b/lib/rmm_el3_ifc/src/rmm_el3_ifc_manifest.c
@@ -0,0 +1,73 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <debug.h>
+#include <rmm_el3_ifc.h>
+#include <smc.h>
+#include <stdint.h>
+#include <string.h>
+#include <utils_def.h>
+
+/*
+ * Local copy of the core boot manifest to be used during runtime.
+ */
+static struct rmm_core_manifest local_core_manifest;
+
+/*
+ * Manifest status
+ */
+static bool manifest_processed;
+
+void rmm_el3_ifc_process_boot_manifest(void)
+{
+	assert(is_mmu_enabled() == false);
+	assert(manifest_processed == false);
+
+	/*
+	 * The boot manifest is expected to be on the shared area.
+	 * Make a local copy of it.
+	 */
+	(void)memcpy(&local_core_manifest,
+		     (void *)rmm_el3_ifc_get_shared_buf_pa(),
+		     sizeof(struct rmm_core_manifest));
+
+	flush_dcache_range((uintptr_t)(void *)&local_core_manifest,
+			 sizeof(local_core_manifest));
+
+	/*
+	 * Validate the Boot Manifest Version.
+	 * Only the version major is taken into account on the verification.
+	 */
+	if ((RMM_EL3_MANIFEST_GET_VERS_MAJOR(local_core_manifest.version)) >
+					RMM_EL3_MANIFEST_VERS_MAJOR) {
+		(void)monitor_call(SMC_RMM_BOOT_COMPLETE,
+				   E_RMM_BOOT_MANIFEST_VERSION_NOT_SUPPORTED,
+				   0UL, 0UL, 0UL, 0UL, 0UL);
+		/* EL3 should never return back here */
+		panic();
+	}
+
+	manifest_processed = true;
+	flush_dcache_range((uintptr_t)(void *)&manifest_processed,
+			 sizeof(bool));
+}
+
+/* Return the raw value of the received boot manifest */
+unsigned int rmm_el3_ifc_get_manifest_version(void)
+{
+	assert(manifest_processed == true);
+
+	return local_core_manifest.version;
+}
+
+/* Return a pointer to the platform manifest */
+uintptr_t rmm_el3_ifc_get_plat_manifest_pa(void)
+{
+	assert((manifest_processed == true) && (is_mmu_enabled() == false));
+
+	return local_core_manifest.plat_data;
+}
diff --git a/lib/rmm_el3_ifc/src/rmm_el3_ifc_priv.h b/lib/rmm_el3_ifc/src/rmm_el3_ifc_priv.h
new file mode 100644
index 0000000..0782383
--- /dev/null
+++ b/lib/rmm_el3_ifc/src/rmm_el3_ifc_priv.h
@@ -0,0 +1,25 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef RMM_EL3_IFC_PRIV_H
+#define RMM_EL3_IFC_PRIV_H
+
+/*
+ * Function to process the boot manifest.
+ *
+ * Args:	None.
+ * Return:	- This function does not return any value, but it will never
+ *		  exit if there is an error.
+ *
+ * NOTE:	This function must be called with the MMU disabled.
+ * NOTE2:	At return, the plat_data field of the manifest local copy
+ *		will be pointing to the platform manifest in the shared area
+ *		(if a platform manifest was loaded by EL3). Platform code is
+ *		responsible for processing the platform manifest and keeping a
+ *		local copy of it if needed at runtime.
+ */
+void rmm_el3_ifc_process_boot_manifest(void);
+
+#endif /* RMM_EL3_IFC_PRIV_H */
diff --git a/lib/rmm_el3_ifc/src/rmm_el3_runtime.c b/lib/rmm_el3_ifc/src/rmm_el3_runtime.c
new file mode 100644
index 0000000..f037d68
--- /dev/null
+++ b/lib/rmm_el3_ifc/src/rmm_el3_runtime.c
@@ -0,0 +1,107 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <assert.h>
+#include <debug.h>
+#include <rmm_el3_ifc.h>
+#include <sizes.h>
+#include <smc.h>
+#include <spinlock.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <xlat_defs.h>
+
+/* Platform parameter */
+extern uintptr_t rmm_shared_buffer_start_va;
+
+/* Spinlock used to protect the EL3<->RMM shared area */
+static spinlock_t shared_area_lock = {0U};
+
+/*
+ * Get and lock a pointer to the start of the RMM<->EL3 shared buffer.
+ */
+uintptr_t rmm_el3_ifc_get_shared_buf_locked(void)
+{
+	spinlock_acquire(&shared_area_lock);
+
+	return rmm_shared_buffer_start_va;
+}
+
+/*
+ * Release the RMM <-> EL3 buffer.
+ */
+void rmm_el3_ifc_release_shared_buf(void)
+{
+	spinlock_release(&shared_area_lock);
+}
+
+/*
+ * Get the realm attestation key to sign the realm attestation token. It is
+ * expected that only the private key is retrieved in raw format.
+ */
+int rmm_el3_ifc_get_realm_attest_key(uintptr_t buf, size_t buflen,
+				     size_t *len, unsigned int crv)
+{
+	struct smc_result smc_res;
+	unsigned long buffer_pa;
+	unsigned long offset =
+		(unsigned long)(buf - rmm_shared_buffer_start_va);
+
+	assert((offset + buflen) <= rmm_el3_ifc_get_shared_buf_size());
+	assert((buf & ~PAGE_SIZE_MASK) == rmm_shared_buffer_start_va);
+
+	buffer_pa = (unsigned long)rmm_el3_ifc_get_shared_buf_pa() + offset;
+
+	monitor_call_with_res(SMC_RMM_GET_REALM_ATTEST_KEY,
+			      buffer_pa,
+			      buflen,
+			      crv, 0UL, 0UL, 0UL, &smc_res);
+
+	if (smc_res.x[0] != 0UL) {
+		ERROR("Failed to get realm attestation key x0 = 0x%lx\n",
+				smc_res.x[0]);
+	}
+
+	*len = smc_res.x[1];
+
+	return smc_res.x[0];
+}
+
+/*
+ * Get the platform token from the EL3 firmware.
+ * The caller must have already populated the public hash in `buf` which is an
+ * input for platform token computation.
+ */
+int rmm_el3_ifc_get_platform_token(uintptr_t buf, size_t buflen,
+				   size_t *len, size_t hash_size)
+{
+	struct smc_result smc_res;
+	unsigned long buffer_pa;
+	unsigned long offset =
+		(unsigned long)(buf - rmm_shared_buffer_start_va);
+
+	assert((offset + buflen) <= rmm_el3_ifc_get_shared_buf_size());
+	assert((buf & ~PAGE_SIZE_MASK) == rmm_shared_buffer_start_va);
+
+	buffer_pa = (unsigned long)rmm_el3_ifc_get_shared_buf_pa() + offset;
+	/* Get the available space on the buffer after the offset */
+
+	monitor_call_with_res(SMC_RMM_GET_PLAT_TOKEN,
+			      buffer_pa,
+			      buflen,
+			      hash_size,
+			      0UL, 0UL, 0UL, &smc_res);
+
+	if (smc_res.x[0] != 0UL) {
+		ERROR("Failed to get platform token x0 = 0x%lx\n",
+				smc_res.x[0]);
+		return smc_res.x[0];
+	}
+
+	*len = smc_res.x[1];
+
+	return smc_res.x[0];
+}
diff --git a/lib/smc/CMakeLists.txt b/lib/smc/CMakeLists.txt
new file mode 100644
index 0000000..4e733c1
--- /dev/null
+++ b/lib/smc/CMakeLists.txt
@@ -0,0 +1,21 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+add_library(rmm-lib-smc)
+
+target_link_libraries(rmm-lib-smc
+    PRIVATE rmm-lib-arch
+            rmm-lib-common)
+
+target_include_directories(rmm-lib-smc
+    PUBLIC "include")
+
+if(NOT RMM_ARCH STREQUAL fake_host)
+    target_sources(rmm-lib-smc
+        PRIVATE "src/aarch64/smc.S")
+else()
+    target_sources(rmm-lib-smc
+        PRIVATE "src/fake_host/smc_wrapper.c")
+endif()
diff --git a/lib/smc/include/smc.h b/lib/smc/include/smc.h
new file mode 100644
index 0000000..5ec2ac4
--- /dev/null
+++ b/lib/smc/include/smc.h
@@ -0,0 +1,195 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef SMC_H
+#define SMC_H
+
+#include <utils_def.h>
+
+/* FID: Type - Fast Call */
+#define SMC_TYPE_SHIFT		U(31)
+#define SMC_TYPE_MASK		U(1)
+#define SMC_TYPE_FAST		U(1)
+
+/* FID: Calling convention - SMC32/SMC64 */
+#define SMC_CC_SHIFT		U(30)
+#define SMC_CC_MASK		U(1)
+#define SMC_CC_SMC32		U(0)
+#define SMC_CC_SMC64		U(1)
+
+/* FID: Owning entity number - Standard Secure Service Calls */
+#define SMC_OEN_SHIFT		U(24)
+#define SMC_OEN_MASK		U(0x3F)
+#define SMC_OEN_STD		U(0x4)
+#define SMC_OEN_ARCH		U(0x0)
+
+/* FID: Must be zero (MBZ) */
+#define SMC_MBZ_SHIFT		U(16)
+#define SMC_MBZ_MASK		U(0xFF)
+#define SMC_MBZ_ZERO		U(0x0)
+
+/* FID: Function number */
+#define SMC_FNUM_SHIFT		U(0)
+#define SMC_FNUM_MASK		U(0xFFFF)
+
+#define SMC_FIELD_VAL(_field, _val)					    \
+	(((_val) & SMC_##_field##_MASK) << SMC_##_field##_SHIFT)
+
+#define SMC_SET_FIELD(_init_val, _field, _val)                              \
+	(((_init_val) & ~SMC_FIELD_VAL(_field, SMC_##_field##_MASK)) |	    \
+	 SMC_FIELD_VAL(_field, _val))
+
+#define SMC_GET_FIELD(_fid, _field)					    \
+	(((_fid) >> SMC_##_field##_SHIFT) & SMC_##_field##_MASK)
+
+/* Arm Architecture Call range function IDs */
+				/* 0x80000000 */
+#define SMC_ARCH_CALL_BASE	(SMC_SET_FIELD(U(0), TYPE, SMC_TYPE_FAST) | \
+				SMC_SET_FIELD(U(0), OEN, SMC_OEN_ARCH))
+
+				/* 0x8000FFFF */
+#define SMC_ARCH_CALL_LIMIT	(SMC_SET_FIELD(SMC_ARCH_CALL_BASE, FNUM,    \
+					       U(0xFFFF)))
+
+/*
+ * We allocate all RMM calls as function IDs within the Standard Secure
+ * Service Call range category defined in the SMCCC.
+ */
+				/* 0x84000000 */
+#define SMC_STD_CALL_BASE	(SMC_SET_FIELD(U(0), TYPE, SMC_TYPE_FAST) | \
+				SMC_SET_FIELD(U(0), OEN, SMC_OEN_STD))
+
+				/* 0x840001CF */
+#define SMC_STD_CALL_LIMIT	(SMC_SET_FIELD(SMC_STD_CALL_BASE, FNUM,     \
+					       U(0x1CF)))
+
+/* STD calls FNUM Min/Max ranges */
+#define SMC32_PSCI_FNUM_MIN	(U(0x0))
+#define SMC32_PSCI_FNUM_MAX	(U(0x14))
+
+#define SMC64_PSCI_FNUM_MIN	(U(0x0))
+#define SMC64_PSCI_FNUM_MAX	(U(0x14))
+
+#define SMC64_RMI_FNUM_MIN	(U(0x150))
+#define SMC64_RMI_FNUM_MAX	(U(0x169))
+
+#define SMC64_RSI_FNUM_MIN	(U(0x190))
+#define SMC64_RSI_FNUM_MAX	(U(0x1AF))
+
+#define SMC64_RMM_EL3_FNUM_MIN	(U(0x1B0))
+#define SMC64_RMM_EL3_FNUM_MAX	(U(0x1CF))
+
+/* Utility macros for FID range values */
+#define SMC32_ARCH_FID(_offset)						   \
+	(SMC_SET_FIELD(SMC_ARCH_CALL_BASE, CC, SMC_CC_SMC32)		|  \
+	 SMC_SET_FIELD(SMC_ARCH_CALL_BASE, FNUM, (_offset)))
+
+#define SMC32_STD_FID(_range, _offset)					   \
+	(SMC_SET_FIELD(SMC_STD_CALL_BASE, CC, SMC_CC_SMC32)		|  \
+	 SMC_SET_FIELD(SMC_STD_CALL_BASE, FNUM,				   \
+	 (SMC32_##_range##_FNUM_MIN + (_offset))))
+
+#define SMC64_STD_FID(_range, _offset)					   \
+	(SMC_SET_FIELD(SMC_STD_CALL_BASE, CC, SMC_CC_SMC64)		|  \
+	 SMC_SET_FIELD(SMC_STD_CALL_BASE, FNUM,				   \
+	 (SMC64_##_range##_FNUM_MIN + (_offset))))
+
+#define IS_SMC64_FID_IN_RANGE(_range, _fid)				   \
+	((SMC_GET_FIELD(_fid, FNUM)	>= SMC64_##_range##_FNUM_MIN)	&& \
+	 (SMC_GET_FIELD(_fid, FNUM)	<= SMC64_##_range##_FNUM_MAX))
+
+#define IS_SMC32_FID_IN_RANGE(_range, _fid)				   \
+	((SMC_GET_FIELD(_fid, FNUM)	>= SMC32_##_range##_FNUM_MIN)	&& \
+	 (SMC_GET_FIELD(_fid, FNUM)	<= SMC32_##_range##_FNUM_MAX))
+
+#define IS_SMC64_FID_STD_FAST(_fid)					   \
+	(((_fid) & ~SMC_FIELD_VAL(FNUM, SMC_FNUM_MASK)) ==		   \
+	 ((SMC_FIELD_VAL(CC, SMC_CC_SMC64)				|  \
+	   SMC_FIELD_VAL(TYPE, SMC_TYPE_FAST)				|  \
+	   SMC_FIELD_VAL(OEN, SMC_OEN_STD))))
+
+#define IS_SMC32_FID_STD_FAST(_fid)					   \
+	(((_fid) & ~SMC_FIELD_VAL(FNUM, SMC_FNUM_MASK)) ==		   \
+	 ((SMC_FIELD_VAL(CC, SMC_CC_SMC32)				|  \
+	   SMC_FIELD_VAL(TYPE, SMC_TYPE_FAST)				|  \
+	   SMC_FIELD_VAL(OEN, SMC_OEN_STD))))
+
+#define IS_SMC64_STD_FAST_IN_RANGE(_range, _fid)			   \
+	(IS_SMC64_FID_STD_FAST(_fid) && IS_SMC64_FID_IN_RANGE(_range, _fid))
+
+#define IS_SMC32_STD_FAST_IN_RANGE(_range, _fid)			   \
+	(IS_SMC32_FID_STD_FAST(_fid) && IS_SMC32_FID_IN_RANGE(_range, _fid))
+
+#define SMC64_NUM_FIDS_IN_RANGE(_range)					   \
+	(SMC64_##_range##_FNUM_MAX - SMC64_##_range##_FNUM_MIN + 1)
+
+/* Gets the offset in a range. Inputs must be pre-verified */
+#define SMC64_FID_OFFSET_FROM_RANGE_MIN(_range, _fid)			   \
+	(SMC_GET_FIELD(_fid, FNUM) - SMC64_##_range##_FNUM_MIN)
+
+/* Implementation defined FID values */
+					/* 0x18F */
+#define SMC_RMM_REQ_COMPLETE		SMC64_STD_FID(RMI, U(0x3F))
+
+					/* 0x1B0 - 0x1B3 */
+#define SMC_ASC_MARK_SECURE		SMC64_STD_FID(RMM_EL3, U(0))
+#define SMC_ASC_MARK_NONSECURE		SMC64_STD_FID(RMM_EL3, U(1))
+
+/* ARM ARCH call FIDs */
+#define SMCCC_VERSION			SMC32_ARCH_FID(U(0))
+#define SMCCC_ARCH_FEATURES		SMC32_ARCH_FID(U(1))
+#define SMCCC_ARCH_SOC_ID		SMC32_ARCH_FID(U(2))
+#define SMCCC_ARCH_WORKAROUND_2		SMC32_ARCH_FID(U(0x7FFF))
+#define SMCCC_ARCH_WORKAROUND_1		SMC32_ARCH_FID(U(0x8000))
+
+/* Implemented version of the SMC Calling Convention */
+#define SMCCC_VERSION_MAJOR	U(1)
+#define SMCCC_VERSION_MINOR	U(2)
+
+/*
+ * SMCCC version encoding:
+ *  Bit[31] must be zero
+ *  Bits [30:16] Major version
+ *  Bits [15:0] Minor version
+ */
+#define SMCCC_VERSION_NUMBER						  \
+	((SMCCC_VERSION_MAJOR << U(16)) | SMCCC_VERSION_MINOR)
+
+/* SMCCC return codes */
+#define SMC_SUCCESS		0
+#define SMC_NOT_SUPPORTED	(-1)
+#define SMC_NOT_REQUIRED	(-2)
+#define SMC_INVALID_PARAMETER	(-3)
+
+#define SMC_UNKNOWN		(-1)
+
+#ifndef __ASSEMBLER__
+unsigned long monitor_call(unsigned long id,
+			unsigned long arg0,
+			unsigned long arg1,
+			unsigned long arg2,
+			unsigned long arg3,
+			unsigned long arg4,
+			unsigned long arg5);
+
+/* Result registers X0-X4 */
+#define SMC_RESULT_REGS		5U
+
+struct smc_result {
+	unsigned long x[SMC_RESULT_REGS];
+};
+
+void monitor_call_with_res(unsigned long id,
+			   unsigned long arg0,
+			   unsigned long arg1,
+			   unsigned long arg2,
+			   unsigned long arg3,
+			   unsigned long arg4,
+			   unsigned long arg5,
+			   struct smc_result *res);
+
+#endif /* __ASSEMBLER__ */
+
+#endif /* SMC_H */
diff --git a/lib/smc/src/aarch64/smc.S b/lib/smc/src/aarch64/smc.S
new file mode 100644
index 0000000..f46838c
--- /dev/null
+++ b/lib/smc/src/aarch64/smc.S
@@ -0,0 +1,43 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <asm_macros.S>
+
+.globl monitor_call
+.globl monitor_call_with_res
+
+func monitor_call
+	/* As this is a function call, the arguments must already be in
+	 * place in accordance to SMCCC.
+	 */
+	smc #0
+	ret
+endfunc monitor_call
+
+/*
+ * Issue an SMC call to EL3 monitor with the ability to return more than 1
+ * result register.
+ * The arguments to this function are :
+ *    x0 - x6 - SMC call arguments
+ *    x7 - Reference to smc_result structure allocated by caller
+ * Return :
+ *    x0 - x3 Return values from SMC
+ * The return args are also copied to smc_result data structure.
+ */
+func monitor_call_with_res
+	/*
+	 * Push the value of x7 to the stack, as the SMC might change the
+	 * content. (push two registers to maintain 16 bit aligned stack)
+	 */
+	stp x7, x8, [sp, #-16]!
+	/* Call SMC */
+	smc #0
+	/* Pop the saved values from stack */
+	ldp x7, x8, [sp], #16
+	/* Fill the smc_result structure */
+	stp x0, x1, [x7, #0]
+	stp x2, x3, [x7, #16]
+	ret
+endfunc monitor_call_with_res
diff --git a/lib/smc/src/fake_host/smc_wrapper.c b/lib/smc/src/fake_host/smc_wrapper.c
new file mode 100644
index 0000000..d2ade4b
--- /dev/null
+++ b/lib/smc/src/fake_host/smc_wrapper.c
@@ -0,0 +1,31 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <host_harness.h>
+#include <smc.h>
+
+unsigned long monitor_call(unsigned long id,
+			unsigned long arg0,
+			unsigned long arg1,
+			unsigned long arg2,
+			unsigned long arg3,
+			unsigned long arg4,
+			unsigned long arg5)
+{
+	return host_monitor_call(id, arg0, arg1, arg2, arg3, arg4, arg5);
+}
+
+void monitor_call_with_res(unsigned long id,
+			   unsigned long arg0,
+			   unsigned long arg1,
+			   unsigned long arg2,
+			   unsigned long arg3,
+			   unsigned long arg4,
+			   unsigned long arg5,
+			   struct smc_result *res)
+{
+	host_monitor_call_with_res(id, arg0, arg1, arg2,
+				   arg3, arg4, arg5, res);
+}
diff --git a/lib/t_cose/CMakeLists.txt b/lib/t_cose/CMakeLists.txt
new file mode 100644
index 0000000..332be72
--- /dev/null
+++ b/lib/t_cose/CMakeLists.txt
@@ -0,0 +1,41 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+add_library(t_cose)
+
+set(T_COSE_SOURCE_DIR "${RMM_SOURCE_DIR}/ext/t_cose")
+
+target_compile_definitions(t_cose
+     PUBLIC
+         "T_COSE_USE_MBEDTLS_CRYPTO"
+)
+
+target_link_libraries(t_cose
+    PRIVATE
+        rmm-lib-common
+        rmm-lib-libc
+)
+
+target_link_libraries(t_cose
+    PUBLIC
+       MbedTLS::Crypto
+       qcbor
+)
+
+target_include_directories(t_cose
+    PUBLIC
+        "${T_COSE_SOURCE_DIR}/inc"
+    PRIVATE
+        "${T_COSE_SOURCE_DIR}/src"
+)
+
+target_sources(t_cose
+    PRIVATE
+        "${T_COSE_SOURCE_DIR}/src/t_cose_parameters.c"
+        "${T_COSE_SOURCE_DIR}/src/t_cose_sign1_sign.c"
+        "${T_COSE_SOURCE_DIR}/src/t_cose_sign1_verify.c"
+        "${T_COSE_SOURCE_DIR}/src/t_cose_util.c"
+        "${T_COSE_SOURCE_DIR}/crypto_adapters/t_cose_mbedtls_crypto.c"
+)
diff --git a/lib/timers/CMakeLists.txt b/lib/timers/CMakeLists.txt
new file mode 100644
index 0000000..f905025
--- /dev/null
+++ b/lib/timers/CMakeLists.txt
@@ -0,0 +1,23 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+add_library(rmm-lib-timers)
+
+target_link_libraries(rmm-lib-timers
+    PRIVATE rmm-lib-arch
+            rmm-lib-attestation
+            rmm-lib-common
+            rmm-lib-gic
+            rmm-lib-measurement
+            rmm-lib-realm
+            rmm-lib-smc
+            rmm-lib-debug
+            rmm-lib-xlat)
+
+target_include_directories(rmm-lib-timers
+    PUBLIC "include")
+
+target_sources(rmm-lib-timers
+    PRIVATE "src/timers.c")
diff --git a/lib/timers/include/timers.h b/lib/timers/include/timers.h
new file mode 100644
index 0000000..940118b
--- /dev/null
+++ b/lib/timers/include/timers.h
@@ -0,0 +1,15 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef TIMERS_H
+#define TIMERS_H
+
+struct rec;
+struct rmi_rec_exit;
+
+bool check_pending_timers(struct rec *rec);
+void report_timer_state_to_ns(struct rmi_rec_exit *rec_exit);
+
+#endif /* TIMERS_H */
diff --git a/lib/timers/src/timers.c b/lib/timers/src/timers.c
new file mode 100644
index 0000000..f16a6ad
--- /dev/null
+++ b/lib/timers/src/timers.c
@@ -0,0 +1,95 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <arch_helpers.h>
+#include <attestation_token.h>
+#include <gic.h>
+#include <memory_alloc.h>
+#include <rec.h>
+#include <smc-rmi.h>
+
+/*
+ * Check that timer output is asserted:
+ * Timer enabled: CNTx_CTL_ENABLE = 1
+ * Timer condition is met: CNTx_CTL_ISTATUS = 1
+ * Timer interrupt is not masked: CNTx_CTL_IMASK = 0
+ */
+#define	TIMER_ASSERTED(reg)						\
+	(((reg) &							\
+	(CNTx_CTL_ENABLE | CNTx_CTL_ISTATUS | CNTx_CTL_IMASK)) ==	\
+	(CNTx_CTL_ENABLE | CNTx_CTL_ISTATUS))
+
+/*
+ * Check the pending state of the timers.
+ *
+ * When a timer output is asserted, its interrupt signal should be masked at
+ * EL2 when running the Realm to prevent the physical interrupt from
+ * continuously exiting the Realm.
+ *
+ * When a timer output is not asserted, the interrupt signal should be
+ * unmasked such that if the timer output becomes asserted again, an exit from
+ * the Realm happens due to a physical IRQ and we can inject a virtual
+ * interrupt again.
+ */
+bool check_pending_timers(struct rec *rec)
+{
+	unsigned long cntv_ctl = read_cntv_ctl_el02();
+	unsigned long cntp_ctl = read_cntp_ctl_el02();
+	unsigned long cnthctl_old = rec->sysregs.cnthctl_el2;
+
+	if (TIMER_ASSERTED(cntv_ctl)) {
+		rec->sysregs.cnthctl_el2 |= CNTHCTL_EL2_CNTVMASK;
+	} else {
+		rec->sysregs.cnthctl_el2 &= ~CNTHCTL_EL2_CNTVMASK;
+	}
+
+	if (TIMER_ASSERTED(cntp_ctl)) {
+		rec->sysregs.cnthctl_el2 |= CNTHCTL_EL2_CNTPMASK;
+	} else {
+		rec->sysregs.cnthctl_el2 &= ~CNTHCTL_EL2_CNTPMASK;
+	}
+
+	if (cnthctl_old != rec->sysregs.cnthctl_el2) {
+		write_cnthctl_el2(rec->sysregs.cnthctl_el2);
+		isb();
+	}
+
+	/*
+	 * We don't want to run the Realm just to immediately exit due a
+	 * physical interrupt casused by one of the timer interrupts not having
+	 * been retired from the CPU interface yet. Check that the interrupts
+	 * are retired before entering the Realm.
+	 */
+	while (true) {
+		unsigned long hppir = read_icc_hppir1_el1();
+		unsigned int intid = EXTRACT(ICC_HPPIR1_EL1_INTID, hppir);
+
+		if (!((((rec->sysregs.cnthctl_el2 & CNTHCTL_EL2_CNTVMASK) != 0UL) &&
+			(intid == EL1_VIRT_TIMER_PPI)) ||
+		      (((rec->sysregs.cnthctl_el2 & CNTHCTL_EL2_CNTPMASK) != 0UL) &&
+			(intid == EL1_PHYS_TIMER_PPI)))) {
+			break;
+		}
+	}
+
+	/*
+	 * Check if the timers changed their output status based on
+	 * the previously saved timer state at the last Realm exit.
+	 */
+	return (TIMER_ASSERTED(cntv_ctl) !=
+		TIMER_ASSERTED(rec->sysregs.cntv_ctl_el0)) ||
+		(TIMER_ASSERTED(cntp_ctl) !=
+		 TIMER_ASSERTED(rec->sysregs.cntp_ctl_el0));
+}
+
+void report_timer_state_to_ns(struct rmi_rec_exit *rec_exit)
+{
+	/* Expose Realm EL1 timer state */
+	rec_exit->cntv_ctl = read_cntv_ctl_el02();
+	rec_exit->cntv_cval = read_cntv_cval_el02() - read_cntvoff_el2();
+
+	rec_exit->cntp_ctl = read_cntp_ctl_el02();
+	rec_exit->cntp_cval = read_cntp_cval_el02() - read_cntpoff_el2();
+}
diff --git a/lib/xlat/CMakeLists.txt b/lib/xlat/CMakeLists.txt
new file mode 100644
index 0000000..86fbce0
--- /dev/null
+++ b/lib/xlat/CMakeLists.txt
@@ -0,0 +1,43 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+add_library(rmm-lib-xlat)
+
+target_link_libraries(rmm-lib-xlat
+    PRIVATE rmm-lib-common
+            rmm-lib-libc
+            rmm-lib-arch
+            rmm-lib-debug)
+
+target_include_directories(rmm-lib-xlat
+    PUBLIC "include")
+
+target_include_directories(rmm-lib-xlat
+    PRIVATE "src")
+
+target_sources(rmm-lib-xlat
+    PRIVATE "src/xlat_tables_core.c"
+            "src/xlat_tables_utils.c"
+	    "src/xlat_tables_arch.c")
+
+if(NOT RMM_ARCH STREQUAL fake_host)
+    target_sources(rmm-lib-xlat
+        PRIVATE "src/aarch64/enable_mmu.S")
+endif()
+
+arm_config_option(
+    NAME XLAT_GRANULARITY_SIZE_SHIFT
+    HELP "Size in bits of the translation granularity"
+    TYPE STRING
+    STRINGS "12" "14" "16"
+    DEFAULT "12"
+    ADVANCED)
+
+if(NOT(XLAT_GRANULARITY_SIZE_SHIFT EQUAL "12"))
+    message(FATAL_ERROR "Only 4K granularity is supported at the moment")
+endif()
+
+target_compile_definitions(rmm-lib-xlat
+    PUBLIC "XLAT_GRANULARITY_SIZE_SHIFT=UL(${XLAT_GRANULARITY_SIZE_SHIFT})")
diff --git a/lib/xlat/include/xlat_contexts.h b/lib/xlat/include/xlat_contexts.h
new file mode 100644
index 0000000..f0f6c09
--- /dev/null
+++ b/lib/xlat/include/xlat_contexts.h
@@ -0,0 +1,259 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ * SPDX-FileCopyrightText: Copyright Arm Limited and Contributors.
+ */
+
+/* This file is derived from xlat_table_v2 library in TF-A project */
+
+#ifndef XLAT_CONTEXTS_H
+#define XLAT_CONTEXTS_H
+
+#ifndef __ASSEMBLER__
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <utils_def.h>
+#include <xlat_defs.h>
+
+/* Forward declaration */
+struct xlat_mmap_region;
+
+/* Enumerator to identify the right address space within a context */
+typedef enum xlat_addr_region_id {
+	VA_LOW_REGION = 0,
+	VA_HIGH_REGION,
+	VA_REGIONS
+} xlat_addr_region_id_t;
+
+/*
+ * Structure to hold all the xlat tables and related information within a
+ * context. This allows to reuse the same xlat_ctx_cfg part of the context
+ * on several PEs that share the same memory map region whilst keeping
+ * private tables for each PE.
+ */
+struct xlat_ctx_tbls {
+	/*
+	 * Array of finer-grain translation tables.
+	 * For example, if the initial lookup level is 1 then this array would
+	 * contain both level-2 and level-3 entries.
+	 */
+	uint64_t (*tables)[XLAT_TABLE_ENTRIES];
+	unsigned int tables_num;
+	unsigned int next_table;
+
+	/*
+	 * Base translation table.
+	 * It has the same number of entries as the ones used for other levels
+	 * although it is possible that not all the entries are used.
+	 *
+	 * If, as an example, the translation tables for the current context
+	 * start at L1, then the *tables field will contain the L2 and L3
+	 * tables.
+	 */
+	uint64_t *base_table;
+	unsigned int max_base_table_entries;
+
+	/* Set to true when the translation tables are initialized. */
+	bool initialized;
+};
+
+/* Struct that holds the context configuration */
+struct xlat_ctx_cfg {
+	/*
+	 * Maximum size allowed for the VA space handled by the context.
+	 */
+	uintptr_t max_va_size;
+
+	/*
+	 * Array of all memory regions stored in order of ascending base_va.
+	 * The list is terminated by the first entry with
+	 * size == 0 or when all entries are used (as specified by mmap_num).
+	 */
+	struct xlat_mmap_region *mmap;
+	unsigned int mmap_num;
+
+	/*
+	 * Base address for the virtual space on this context.
+	 */
+	uintptr_t base_va;
+
+	/*
+	 * Max Physical and Virtual addresses currently in use by the
+	 * current context. These will get updated as we map memory
+	 * regions but it will never go beyond the maximum physical address
+	 * or max_va_size respectively.
+	 *
+	 * max_mapped_pa is an absolute Physical Address.
+	 */
+	uintptr_t max_mapped_pa;
+	uintptr_t max_mapped_va_offset;
+
+	/* Level of the base translation table. */
+	unsigned int base_level;
+
+	/*
+	 * Virtual address region handled by this context.
+	 */
+	xlat_addr_region_id_t region;
+
+	bool initialized;
+};
+
+/*
+ * Struct that holds the context itself, composed of
+ * a pointer to the context config and a pointer to the
+ * translation tables associated to it.
+ */
+struct xlat_ctx {
+	struct xlat_ctx_cfg *cfg;
+	struct xlat_ctx_tbls *tbls;
+};
+
+/*
+ * The translation tables are aligned to their size. For 4KB graularity, this
+ * is aligned to 4KB as well.
+ */
+#define XLAT_TABLES_ALIGNMENT		XLAT_TABLE_SIZE
+
+/*
+ * Align the base tables to page boundary. This migh generate larger tables
+ * than needed, but it simplifies the code, which is a good trade-off
+ * since we have enough memory.
+ */
+#define BASE_XLAT_TABLES_ALIGNMENT	XLAT_TABLE_SIZE
+
+/*
+ * Compute the number of entries required at the initial lookup level to
+ * address the whole virtual address space.
+ */
+#define GET_NUM_BASE_LEVEL_ENTRIES(addr_space_size)			\
+	((addr_space_size) >>						\
+		XLAT_ADDR_SHIFT(GET_XLAT_TABLE_LEVEL_BASE(addr_space_size)))
+
+/*
+ * Macro to check if the xlat_ctx_cfg part of a context is valid.
+ */
+#define XLAT_TABLES_CTX_CFG_VALID(_ctx)	((_ctx)->cfg != NULL)
+
+/*
+ * Macro to check if the xlat_ctx_tbls part of a context is valid.
+ */
+#define XLAT_TABLES_CTX_TBL_VALID(_ctx)		((_ctx)->tbls != NULL)
+
+/*
+ * Macro to allocate translation tables to be used within a context.
+ */
+#define XLAT_CREATE_TABLES(_tblset_name,				\
+			   _xlat_tables_count,				\
+			   _virt_addr_space_size,			\
+			   _tables_section)				\
+									\
+	static uint64_t _tblset_name##_base_xlat_table			\
+		[(XLAT_TABLE_ENTRIES)]					\
+		__aligned((BASE_XLAT_TABLES_ALIGNMENT))			\
+		__section((_tables_section));				\
+									\
+	static uint64_t _tblset_name##_xlat_tables[(_xlat_tables_count)]\
+		[(XLAT_TABLE_ENTRIES)]					\
+		__aligned((XLAT_TABLES_ALIGNMENT))			\
+		__section((_tables_section));				\
+									\
+	static struct xlat_ctx_tbls _tblset_name##_tbls = {		\
+		.tables = _tblset_name##_xlat_tables,			\
+		.tables_num = (_xlat_tables_count),			\
+		.next_table = 0,					\
+		.base_table = _tblset_name##_base_xlat_table,		\
+		.max_base_table_entries =				\
+			GET_NUM_BASE_LEVEL_ENTRIES(_virt_addr_space_size),\
+		.initialized = false					\
+	}								\
+
+/*
+ * Macro used to define the xlat_ctx_cfg and xlat_mmap_region array
+ * associated with a context.
+ */
+#define XLAT_REGISTER_VA_SPACE(_ctx_name, _region, _mmap_count,		\
+			_virt_addr_space_size)				\
+	COMPILER_ASSERT(((_region) < VA_REGIONS));			\
+	COMPILER_ASSERT(((unsigned long)(_virt_addr_space_size)		\
+					% GRANULE_SIZE) == UL(0));	\
+	COMPILER_ASSERT((unsigned long)(_virt_addr_space_size)		\
+					<= (MAX_VIRT_ADDR_SPACE_SIZE));	\
+									\
+	static struct xlat_mmap_region _ctx_name##_mmap[(_mmap_count)];	\
+									\
+	static struct xlat_ctx_cfg _ctx_name##_xlat_ctx_cfg = {		\
+		.max_va_size = (_virt_addr_space_size),			\
+		.base_va = 0ULL,					\
+		.mmap = _ctx_name##_mmap,				\
+		.mmap_num = (_mmap_count),				\
+		.max_mapped_va_offset = 0ULL,				\
+		.max_mapped_pa = 0ULL,					\
+		.base_level =						\
+			(GET_XLAT_TABLE_LEVEL_BASE((_virt_addr_space_size))),\
+		.region = (_region),					\
+		.initialized = false					\
+	}
+
+/*
+ * Macro to generate a context and associate the translation table set passed
+ * to it by ref.
+ */
+#define XLAT_REGISTER_CONTEXT_FULL_SPEC(_ctx_name, _region, _mmap_count,\
+			_virt_addr_space_size,				\
+			_tables_set)					\
+	XLAT_REGISTER_VA_SPACE(_ctx_name, (_region),			\
+			       (_mmap_count), (_virt_addr_space_size));	\
+									\
+	static struct xlat_ctx _ctx_name##_xlat_ctx = {			\
+		.cfg = &(_ctx_name##_xlat_ctx_cfg),			\
+		.tbls = (_tables_set)					\
+	}
+
+/*
+ * Statically allocate a translation context and associated translation
+ * tables. Also initialize them.
+ *
+ * _ctx_name:
+ *   Prefix for the translation context variable.
+ *   E.g. If _ctx_name is 'foo', the variable will be called 'foo_xlat_ctx'.
+ *   Useful to distinguish multiple contexts from one another.
+ *
+ * _region:
+ *   Region mapped by this context (high or low address region).
+ *   See @xlat_ctx_region_id_t for more info.
+ *
+ * _mmap_count:
+ *   Number ofstruct xlat_mmap_region to allocate.
+ *   Would be defined during the context creation.
+ *
+ * _xlat_tables_count:
+ *   Number of non-base tables to allocate at level others than the
+ *   initial lookup.
+ *
+ * _virt_addr_space_size:
+ *   Size (in bytes) of the virtual address space that can be accessed by this
+ *   context.
+ *
+ * _section_name:
+ *   Specify the name of the section where the translation tables have to be
+ *   placed by the linker.
+ */
+#define XLAT_REGISTER_CONTEXT(_ctx_name, _region, _mmap_count,		\
+			      _xlat_tables_count,			\
+			      _virt_addr_space_size,			\
+			      _section_name)				\
+		XLAT_CREATE_TABLES(_ctx_name, (_xlat_tables_count),	\
+				   (_virt_addr_space_size),		\
+				   (_section_name));			\
+									\
+		XLAT_REGISTER_CONTEXT_FULL_SPEC(_ctx_name, (_region),	\
+						(_mmap_count),		\
+						(_virt_addr_space_size),\
+						&(_ctx_name##_tbls))
+
+#endif /*__ASSEMBLER__*/
+
+#endif /* XLAT_CONTEXTS_H */
diff --git a/lib/xlat/include/xlat_defs.h b/lib/xlat/include/xlat_defs.h
new file mode 100644
index 0000000..ca41825
--- /dev/null
+++ b/lib/xlat/include/xlat_defs.h
@@ -0,0 +1,141 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ * SPDX-FileCopyrightText: Copyright Arm Limited and Contributors.
+ */
+
+/* This file is derived from xlat_table_v2 library in TF-A project */
+
+#ifndef XLAT_DEFS_H
+#define XLAT_DEFS_H
+
+#include <arch.h>
+#include <utils_def.h>
+
+#define PAGE_SIZE_4KB		(1UL << 12)
+#define PAGE_SIZE_16KB		(1UL << 14)
+#define PAGE_SIZE_64KB		(1UL << 16)
+
+/*
+ * The ARMv8-A architecture allows translation granule sizes of 4KB, 16KB or 64KB.
+ *
+ * Only 4K granularities are allowed on this library.
+ */
+#define PAGE_SIZE		(UL(1) << XLAT_GRANULARITY_SIZE_SHIFT)
+#define PAGE_SIZE_MASK		(PAGE_SIZE - UL(1))
+#define IS_PAGE_ALIGNED(addr)	(((addr) & PAGE_SIZE_MASK) == U(0))
+
+#define XLAT_ENTRY_SIZE_SHIFT	UL(3)	/* Each MMU table entry is 8 bytes */
+#define XLAT_ENTRY_SIZE		(UL(1) << XLAT_ENTRY_SIZE_SHIFT)
+
+/* Size of one complete table */
+#define XLAT_TABLE_SIZE_SHIFT	XLAT_GRANULARITY_SIZE_SHIFT
+#define XLAT_TABLE_SIZE		(UL(1) << XLAT_TABLE_SIZE_SHIFT)
+
+#define XLAT_TABLE_LEVEL_MAX	UL(3)
+
+/* Values for number of entries in each MMU translation table */
+#define XLAT_TABLE_ENTRIES_SHIFT (XLAT_TABLE_SIZE_SHIFT - XLAT_ENTRY_SIZE_SHIFT)
+#define XLAT_TABLE_ENTRIES	(UL(1) << XLAT_TABLE_ENTRIES_SHIFT)
+#define XLAT_TABLE_ENTRIES_MASK	(XLAT_TABLE_ENTRIES - UL(1))
+
+/* Values to convert a memory address to an index into a translation table */
+#define L3_XLAT_ADDRESS_SHIFT	XLAT_GRANULARITY_SIZE_SHIFT
+#define L2_XLAT_ADDRESS_SHIFT	(L3_XLAT_ADDRESS_SHIFT + XLAT_TABLE_ENTRIES_SHIFT)
+#define L1_XLAT_ADDRESS_SHIFT	(L2_XLAT_ADDRESS_SHIFT + XLAT_TABLE_ENTRIES_SHIFT)
+#define L0_XLAT_ADDRESS_SHIFT	(L1_XLAT_ADDRESS_SHIFT + XLAT_TABLE_ENTRIES_SHIFT)
+#define XLAT_ADDR_SHIFT(level)	(XLAT_GRANULARITY_SIZE_SHIFT + \
+		  ((XLAT_TABLE_LEVEL_MAX - (level)) * XLAT_TABLE_ENTRIES_SHIFT))
+
+#define XLAT_BLOCK_SIZE(level)	(UL(1) << XLAT_ADDR_SHIFT(level))
+/* Mask to get the bits used to index inside a block of a certain level */
+#define XLAT_BLOCK_MASK(level)	(XLAT_BLOCK_SIZE(level) - UL(1))
+/* Mask to get the address bits common to a block of a certain table level*/
+#define XLAT_ADDR_MASK(level)	(~XLAT_BLOCK_MASK(level))
+/*
+ * Extract from the given virtual address the index into the given lookup level.
+ * This macro assumes the system is using the 4KB translation granule.
+ */
+#define XLAT_TABLE_IDX(virtual_addr, level)	\
+	(((virtual_addr) >> XLAT_ADDR_SHIFT(level)) & ULL(0x1FF))
+
+/* Mask to get the PA given a L3 descriptor entry (4KB granularity) */
+#define XLAT_TTE_L3_PA_MASK	ULL(0x0000FFFFFFFFF000)
+
+/*
+ * In AArch64 state, the MMU may support 4KB, 16KB and 64KB page
+ * granularity. For 4KB granularity, a level 0 table descriptor doesn't support
+ * block translation. For 16KB, the same thing happens to levels 0 and 1. For
+ * 64KB, same for level 1. See section D4.3.1 of the ARMv8-A Architecture
+ * Reference Manual (DDI 0487A.k) for more information.
+ *
+ * The define below specifies the first table level that allows block
+ * descriptors.
+ */
+#if PAGE_SIZE == PAGE_SIZE_4KB
+# define MIN_LVL_BLOCK_DESC	U(1)
+#elif (PAGE_SIZE == PAGE_SIZE_16KB) || (PAGE_SIZE == PAGE_SIZE_64KB)
+# define MIN_LVL_BLOCK_DESC	U(2)
+#endif
+
+#define XLAT_TABLE_LEVEL_MIN	U(0)
+
+/* Mask used to know if an address belongs to a high va region. */
+#define HIGH_REGION_MASK	(ULL(0xFFF) << 52)
+
+/*
+ * Define the architectural limits of the virtual address space in AArch64
+ * state.
+ *
+ * TCR.TxSZ is calculated as 64 minus the width of said address space.
+ * The value of TCR.TxSZ must be in the range 16 to 39 [1] or 48 [2],
+ * depending on Small Translation Table Support which means that
+ * the virtual address space width must be in the range 48 to 25 or 16 bits.
+ *
+ * [1] See the ARMv8-A Architecture Reference Manual (DDI 0487A.j) for more
+ * information:
+ * Page 1730: 'Input address size', 'For all translation stages'.
+ * [2] See section 12.2.55 in the ARMv8-A Architecture Reference Manual
+ * (DDI 0487D.a)
+ */
+/* Maximum value of TCR_ELx.T(0,1)SZ is 39 */
+#define MIN_VIRT_ADDR_SPACE_SIZE	(UL(1) << (UL(64) - TCR_TxSZ_MAX))
+
+/* Maximum value of TCR_ELx.T(0,1)SZ is 48 */
+#define MIN_VIRT_ADDR_SPACE_SIZE_TTST	\
+				(UL(1) << (UL(64) - TCR_TxSZ_MAX_TTST))
+#define MAX_VIRT_ADDR_SPACE_SIZE	(UL(1) << (UL(64) - TCR_TxSZ_MIN))
+
+/*
+ * Here we calculate the initial lookup level from the value of the given
+ * virtual address space size. For a 4 KB page size,
+ * - level 0 supports virtual address spaces of widths 48 to 40 bits;
+ * - level 1 from 39 to 31;
+ * - level 2 from 30 to 22.
+ * - level 3 from 21 to 16.
+ *
+ * Small Translation Table (Armv8.4-TTST) support allows the starting level
+ * of the translation table from 3 for 4KB granularity. See section 12.2.55 in
+ * the ARMv8-A Architecture Reference Manual (DDI 0487D.a). In Armv8.3 and below
+ * wider or narrower address spaces are not supported. As a result, level 3
+ * cannot be used as initial lookup level with 4 KB granularity. See section
+ * D4.2.5 in the ARMv8-A Architecture Reference Manual (DDI 0487A.j) for more
+ * information.
+ *
+ * For example, for a 35-bit address space (i.e. virt_addr_space_size ==
+ * 1 << 35), TCR.TxSZ will be programmed to (64 - 35) = 29. According to Table
+ * D4-11 in the ARM ARM, the initial lookup level for an address space like that
+ * is 1.
+ *
+ * Note that this macro assumes that the given virtual address space size is
+ * valid.
+ */
+#define GET_XLAT_TABLE_LEVEL_BASE(_virt_addr_space_sz)			\
+	(((_virt_addr_space_sz) > (ULL(1) << L0_XLAT_ADDRESS_SHIFT))	\
+	? 0U								\
+	: (((_virt_addr_space_sz) > (ULL(1) << L1_XLAT_ADDRESS_SHIFT))	\
+	? 1U								\
+	: (((_virt_addr_space_sz) > (ULL(1) << L2_XLAT_ADDRESS_SHIFT))	\
+	? 2U : 3U)))
+
+#endif /* XLAT_DEFS_H */
diff --git a/lib/xlat/include/xlat_tables.h b/lib/xlat/include/xlat_tables.h
new file mode 100644
index 0000000..cff7d72
--- /dev/null
+++ b/lib/xlat/include/xlat_tables.h
@@ -0,0 +1,324 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ * SPDX-FileCopyrightText: Copyright Arm Limited and Contributors.
+ */
+
+/* This file is derived from xlat_table_v2 library in TF-A project */
+
+#ifndef XLAT_TABLES_H
+#define XLAT_TABLES_H
+
+#ifndef __ASSEMBLER__
+
+#include <memory.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#endif
+
+#include <xlat_contexts.h>
+#include <xlat_defs.h>
+
+#ifndef __ASSEMBLER__
+
+/*
+ * Default granularity size for a struct xlat_mmap_region.
+ * Useful when no specific granularity is required.
+ *
+ * By default, choose the biggest possible block size allowed by the
+ * architectural state and granule size in order to minimize the number of page
+ * tables required for the mapping.
+ */
+#define REGION_DEFAULT_GRANULARITY	XLAT_BLOCK_SIZE(MIN_LVL_BLOCK_DESC)
+
+/*
+ * Helper macro to define a struct xlat_mmap_region. This macro allows to
+ * specify all the fields of the structure but its parameter list is not
+ * guaranteed to remain stable as we add members to struct xlat_mmap_region.
+ */
+#define MAP_REGION_FULL_SPEC(_pa, _va, _sz, _attr, _gr)		\
+	{							\
+		.base_pa = (_pa),				\
+		.base_va = (_va),				\
+		.size = (_sz),					\
+		.attr = (_attr),				\
+		.granularity = (_gr),				\
+	}
+
+/* Helper macro to define anstruct xlat_mmap_region. */
+#define MAP_REGION(_pa, _va, _sz, _attr)	\
+	MAP_REGION_FULL_SPEC(_pa, _va, _sz, _attr, REGION_DEFAULT_GRANULARITY)
+
+/* Helper macro to define anstruct xlat_mmap_region with an identity mapping. */
+#define MAP_REGION_FLAT(_adr, _sz, _attr)			\
+	MAP_REGION(_adr, _adr, _sz, _attr)
+
+/*
+ * Helper macro to define an mmap_region_t to map with the desired granularity
+ * of translation tables but with invalid page descriptors.
+ *
+ * The granularity value passed to this macro must be a valid block or page
+ * size. When using a 4KB translation granule, this might be 4KB, 2MB or 1GB.
+ * Passing REGION_DEFAULT_GRANULARITY is also allowed and means that the library
+ * is free to choose the granularity for this region.
+ *
+ * This macro can be used to define transient regions where memory used to
+ * reserve VA can be assigned to a PA dynamically. These VA will fault if it
+ * is accessed before a valid PA is assigned to it.
+ */
+
+#define MAP_REGION_TRANSIENT(_va, _sz, _gr)			\
+	MAP_REGION_FULL_SPEC(ULL(0), _va, _sz, MT_TRANSIENT, _gr)
+
+/* Definition of an invalid descriptor */
+#define INVALID_DESC		UL(0x0)
+
+/*
+ * Shifts and masks to access fields of an mmap attribute
+ */
+#define MT_TYPE_SHIFT		UL(0)
+#define MT_TYPE_WIDTH		UL(4)
+#define MT_TYPE_MASK		MASK(MT_TYPE)
+#define MT_TYPE(_attr)		((_attr) & MT_TYPE_MASK)
+/* Access permissions (RO/RW) */
+#define MT_PERM_SHIFT		(MT_TYPE_SHIFT + MT_TYPE_WIDTH)
+/* Access permissions for instruction execution (EXECUTE/EXECUTE_NEVER) */
+#define MT_EXECUTE_FLAG_SHIFT	(MT_PERM_SHIFT + 1UL)
+
+/* Contiguos descriptor flag */
+#define MT_CONT_SHIFT		(MT_EXECUTE_FLAG_SHIFT + 1UL)
+
+/* NG Flag */
+#define MT_NG_SHIFT		(MT_CONT_SHIFT + 1UL)
+
+/* Shareability attribute for the memory region */
+#define MT_SHAREABILITY_SHIFT	(MT_NG_SHIFT + 1UL)
+#define MT_SHAREABILITY_WIDTH	UL(2)
+#define MT_SHAREABILITY_MASK	MASK(MT_SHAREABILITY)
+#define MT_SHAREABILITY(_attr)	((_attr) & MT_SHAREABILITY_MASK)
+
+/* Physical address space (REALM/NS, as ROOT/SECURE do not apply to R-EL2) */
+#define MT_PAS_SHIFT		(MT_SHAREABILITY_SHIFT + MT_SHAREABILITY_WIDTH)
+#define MT_PAS_WIDTH		UL(1)
+#define MT_PAS_MASK		MASK(MT_PAS)
+#define MT_PAS(_attr)		((_attr) & MT_PAS_MASK)
+
+/* All other bits are reserved */
+
+/*
+ * Memory mapping attributes
+ */
+
+/*
+ * Memory types supported.
+ * These are organised so that, going down the list, the memory types are
+ * getting weaker; conversely going up the list the memory types are getting
+ * stronger.
+ */
+#define MT_DEVICE		UL(0)
+#define MT_NON_CACHEABLE	UL(1)
+#define MT_MEMORY		UL(2)
+#define MT_TRANSIENT		UL(3)
+/* Values up to 7 are reserved to add new memory types in the future */
+
+#define MT_RO			INPLACE(MT_PERM, 0UL)
+#define MT_RW			INPLACE(MT_PERM, 1UL)
+
+#define MT_REALM		INPLACE(MT_PAS, 0UL)
+#define MT_NS			INPLACE(MT_PAS, 1UL)
+
+/*
+ * Access permissions for instruction execution are only relevant for normal
+ * read-only memory, i.e. MT_MEMORY | MT_RO. They are ignored (and potentially
+ * overridden) otherwise:
+ *  - Device memory is always marked as execute-never.
+ *  - Read-write normal memory is always marked as execute-never.
+ */
+#define MT_EXECUTE		INPLACE(MT_EXECUTE_FLAG, 0UL)
+#define MT_EXECUTE_NEVER	INPLACE(MT_EXECUTE_FLAG, 1UL)
+
+/*
+ * Shareability defines the visibility of any cache changes to
+ * all masters belonging to a shareable domain.
+ *
+ * MT_SHAREABILITY_ISH: For inner shareable domain
+ * MT_SHAREABILITY_OSH: For outer shareable domain
+ * MT_SHAREABILITY_NSH: For non shareable domain
+ */
+#define MT_SHAREABILITY_ISH	INPLACE(MT_SHAREABILITY, 1UL)
+#define MT_SHAREABILITY_OSH	INPLACE(MT_SHAREABILITY, 2UL)
+#define MT_SHAREABILITY_NSH	INPLACE(MT_SHAREABILITY, 3UL)
+
+#define MT_CONT			INPLACE(MT_CONT, 1UL)
+#define MT_NG			INPLACE(MT_NG, 1UL)
+
+/* Compound attributes for most common usages */
+#define MT_CODE			(MT_MEMORY | MT_SHAREABILITY_ISH \
+				 | MT_RO | MT_EXECUTE)
+#define MT_RO_DATA		(MT_MEMORY | MT_SHAREABILITY_ISH \
+				 | MT_RO | MT_EXECUTE_NEVER)
+#define MT_RW_DATA		(MT_MEMORY | MT_SHAREABILITY_ISH \
+				 | MT_RW | MT_EXECUTE_NEVER)
+
+/*
+ * Structure for specifying a single region of memory.
+ */
+struct xlat_mmap_region {
+	uintptr_t	base_pa;	/* Base PA for the current region. */
+	uintptr_t	base_va;	/* Base VA for the current region. */
+	size_t		size;		/* Size of the current region. */
+	uint64_t	attr;		/* Attrs for the current region. */
+	size_t		granularity;    /* Region granularity. */
+};
+
+/*
+ * Structure containing a table entry and its related information.
+ */
+struct xlat_table_entry {
+	uint64_t *table;	/* Pointer to the translation table. */
+	uintptr_t base_va;	/* Context base VA for the current entry. */
+	unsigned int level;	/* Table level of the current entry. */
+	unsigned int entries;   /* Number of entries used by this table. */
+};
+
+/******************************************************************************
+ * Generic translation table APIs.
+ *****************************************************************************/
+
+static inline void xlat_write_descriptor(uint64_t *entry, uint64_t desc)
+{
+	SCA_WRITE64(entry, desc);
+}
+
+static inline uint64_t xlat_read_descriptor(uint64_t *entry)
+{
+	return SCA_READ64(entry);
+}
+
+/*
+ * Initialize translation tables (and mark xlat_ctx_cfg as initialized if
+ * not already initialized) associated to the current context.
+ *
+ * The struct xlat_ctx_cfg of the context might be shared with other
+ * contexts that might have already initialized it. This is expected and
+ * should not cause any problem.
+ *
+ * This function assumes that the xlat_ctx_cfg field of the context has been
+ * properly configured by previous calls to xlat_mmap_add_region_ctx().
+ *
+ * This function returns 0 on success or an error code otherwise.
+ */
+int xlat_init_tables_ctx(struct xlat_ctx *ctx);
+
+/*
+ * Add a memory region with defined base PA and base VA. This function can only
+ * be used before marking the xlat_ctx_cfg for the current xlat_ctx as
+ * initialized.
+ *
+ * The region cannot be removed once added.
+ *
+ * This function returns 0 on success or an error code otherwise.
+ */
+int xlat_mmap_add_region_ctx(struct xlat_ctx *ctx,
+			     struct xlat_mmap_region *mm);
+
+/*
+ * Add an array of memory regions with defined base PA and base VA.
+ * This function needs to be called before initialiting the xlat_ctx_cfg.
+ * Setting the `last` argument to true will initialise the xlat_ctx_cfg.
+ *
+ * The regions cannot be removed once added.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+int xlat_mmap_add_ctx(struct xlat_ctx *ctx,
+		      struct xlat_mmap_region *mm,
+		      bool last);
+
+/*
+ * Return a table entry structure given a context and a VA.
+ * The return structure is populated on the retval field.
+ *
+ * This function returns 0 on success or a negative error code otherwise.
+ */
+int xlat_get_table_from_va(struct xlat_table_entry * const retval,
+			   const struct xlat_ctx * const ctx,
+			   const uintptr_t va);
+
+/*
+ * Function to unmap a physical memory page from the descriptor entry and
+ * VA given.
+ * This function implements the "Break" part of the Break-Before-Make semantics
+ * mandated by the Armv8.x architecture in order to update the page descriptors.
+ *
+ * This function returns 0 on success or a negative error code otherwise.
+ */
+int xlat_unmap_memory_page(struct xlat_table_entry * const table,
+			   const uintptr_t va);
+
+/*
+ * Function to map a physical memory page from the descriptor table entry
+ * and VA given. This function implements the "Make" part of the
+ * Break-Before-Make semantics mandated by the armv8.x architecture in order
+ * to update the page descriptors.
+ *
+ * This function returns 0 on success or a negative error code otherwise.
+ */
+int xlat_map_memory_page_with_attrs(const struct xlat_table_entry * const table,
+				    const uintptr_t va,
+				    const uintptr_t pa,
+				    const uint64_t attrs);
+
+/*
+ * This function finds the descriptor entry on a table given the corresponding
+ * table entry structure and the VA for that descriptor.
+ *
+ */
+uint64_t *xlat_get_pte_from_table(const struct xlat_table_entry * const table,
+				    const uintptr_t va);
+
+/*
+ * Set up the MMU configuration registers for the specified platform parameters.
+ *
+ * This function must be called for each context as it configures the
+ * appropriate TTBRx register depending on it.
+ *
+ * This function also assumes that the contexts for high and low VA halfs share
+ * the same virtual address space as well as the same physical address space,
+ * so it is safe to call it for each context initialization.
+ *
+ * Returns 0 on success or a negative error code otherwise.
+ */
+int xlat_arch_setup_mmu_cfg(struct xlat_ctx * const ctx);
+
+/* MMU control */
+void xlat_enable_mmu_el2(void);
+
+/*
+ * Returns true if the xlat_ctx_cfg field in the xlat_ctx is initialized.
+ */
+bool xlat_ctx_cfg_initialized(const struct xlat_ctx * const ctx);
+
+/*
+ * Returns true if the translation tables on the current context are already
+ * initialized or false otherwise.
+ */
+bool xlat_ctx_tbls_initialized(const struct xlat_ctx * const ctx);
+
+/*
+ * Initialize a context dynamically at runtime using the given xlat_ctx_cfg
+ * and xlat_ctx_tbls structures.
+ *
+ * Return 0 if success or a Posix erro code otherwise.
+ */
+int xlat_ctx_create_dynamic(struct xlat_ctx *ctx,
+			    struct xlat_ctx_cfg *cfg,
+			    struct xlat_ctx_tbls *tbls,
+			    void *base_tables,
+			    unsigned int base_level_entries,
+			    void *tables_ptr,
+			    unsigned int ntables);
+
+#endif /*__ASSEMBLER__*/
+#endif /* XLAT_TABLES_H */
diff --git a/lib/xlat/src/aarch64/enable_mmu.S b/lib/xlat/src/aarch64/enable_mmu.S
new file mode 100644
index 0000000..5acf68d
--- /dev/null
+++ b/lib/xlat/src/aarch64/enable_mmu.S
@@ -0,0 +1,41 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ * SPDX-FileCopyrightText: Copyright Arm Limited and Contributors.
+ */
+
+/* This file is derived from xlat_table_v2 library in TF-A project */
+
+#include <asm_macros.S>
+#include <xlat_tables.h>
+
+	.global	xlat_enable_mmu_el2
+
+	func xlat_enable_mmu_el2
+		/* Invalidate all TLB entries */
+		TLB_INVALIDATE(alle2)
+
+		/* Set HCR_EL2.E2H to 1 to enable Realm EL2&0 Regime. */
+		mrs	x5, hcr_el2
+		mov_imm x4, HCR_RW
+		orr	x5, x5, x4
+		msr	hcr_el2, x5
+
+		/*
+		 * Ensure all translation table writes have drained into memory, the TLB
+		 * invalidation is complete, and translation register writes are
+		 * committed before enabling the MMU
+		 */
+		dsb	ish
+		isb
+
+		/* Set and clear required fields of SCTLR */
+		mrs	x4, sctlr_el2
+		mov_imm	x5, SCTLR_EL2_WXN | SCTLR_EL2_M
+		orr	x4, x4, x5
+		msr	sctlr_el2, x4
+
+		isb
+
+		ret
+	endfunc xlat_enable_mmu_el2
diff --git a/lib/xlat/src/xlat_defs_private.h b/lib/xlat/src/xlat_defs_private.h
new file mode 100644
index 0000000..7e5a7cf
--- /dev/null
+++ b/lib/xlat/src/xlat_defs_private.h
@@ -0,0 +1,119 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ * SPDX-FileCopyrightText: Copyright Arm Limited and Contributors.
+ */
+
+/* This file is derived from xlat_table_v2 library in TF-A project */
+
+#ifndef XLAT_DEFS_PRIVATE_H
+#define XLAT_DEFS_PRIVATE_H
+
+#include <arch.h>
+#include <utils_def.h>
+#include <xlat_defs.h>
+
+/* Miscellaneous MMU related constants */
+#define NUM_2MB_IN_GB		(UL(1) << 9)
+#define NUM_4K_IN_2MB		(UL(1) << 9)
+#define NUM_GB_IN_4GB		(UL(1) << 2)
+
+#define TWO_MB_SHIFT		UL(21)
+#define ONE_GB_SHIFT		UL(30)
+#define FOUR_KB_SHIFT		UL(12)
+
+#define ONE_GB_INDEX(x)		((x) >> ONE_GB_SHIFT)
+#define TWO_MB_INDEX(x)		((x) >> TWO_MB_SHIFT)
+#define FOUR_KB_INDEX(x)	((x) >> FOUR_KB_SHIFT)
+
+/*
+ * A block descriptor points to a region of memory bigger than the granule size
+ * (e.g. a 2MB region when the granule size is 4KB).
+ */
+#define BLOCK_DESC		UL(0x1) /* Table levels 0-2 */
+/* A table descriptor points to the next level of translation table. */
+#define TABLE_DESC		UL(0x3) /* Table levels 0-2 */
+/*
+ * A page descriptor points to a page, i.e. a memory region whose size is the
+ * translation granule size (e.g. 4KB).
+ */
+#define PAGE_DESC		UL(0x3) /* Table level 3 */
+
+#define DESC_MASK		UL(0x3)
+
+#define FIRST_LEVEL_DESC_N	ONE_GB_SHIFT
+#define SECOND_LEVEL_DESC_N	TWO_MB_SHIFT
+#define THIRD_LEVEL_DESC_N	FOUR_KB_SHIFT
+
+#define XN			(ULL(1) << 2)
+#define UXN			(ULL(1) << 2)
+#define PXN			(ULL(1) << 1)
+#define CONT_HINT		(ULL(1) << 0)
+#define UPPER_ATTRS(x)		(((x) & ULL(0x7)) << 52)
+
+#define NON_GLOBAL		(UL(1) << 9)
+#define ACCESS_FLAG		(UL(1) << 8)
+#define NSH			(UL(0x0) << 6)
+#define OSH			(UL(0x2) << 6)
+#define ISH			(UL(0x3) << 6)
+
+/* Guarded Page bit */
+#define GP			(ULL(1) << 50)
+
+#define TABLE_ADDR_MASK		XLAT_TTE_L3_PA_MASK
+
+#define AP2_SHIFT			UL(0x7)
+#define AP2_RO				ULL(0x1)
+#define AP2_RW				ULL(0x0)
+
+#define AP1_SHIFT			UL(0x6)
+
+/*
+ * The following definitions must all be passed to the LOWER_ATTRS() macro to
+ * get the right bitmask.
+ */
+#define AP_RO				(AP2_RO << UL(5))
+#define AP_RW				(AP2_RW << UL(5))
+#define AP_ACCESS_UNPRIVILEGED		(AP1_ACCESS_UNPRIVILEGED    << UL(4))
+#define AP_NO_ACCESS_UNPRIVILEGED	(AP1_NO_ACCESS_UNPRIVILEGED << UL(4))
+#define NS				(U(0x1) << UL(3))   /* Bit[5] absolutely */
+#define ATTR_NON_CACHEABLE_INDEX	UL(0x2)
+#define ATTR_DEVICE_INDEX		UL(0x1)
+#define ATTR_IWBWA_OWBWA_NTR_INDEX	UL(0x0)
+#define NG_HINT				(ULL(1) << 9)
+#define LOWER_ATTRS(x)			(((x) & UL(0xfff)) << UL(2))
+
+/* Normal Memory, Outer Write-Through non-transient, Inner Non-cacheable */
+#define ATTR_NON_CACHEABLE		MAKE_MAIR_NORMAL_MEMORY(MAIR_NORM_NC, MAIR_NORM_NC)
+/* Device-nGnRE */
+#define ATTR_DEVICE			MAIR_DEV_NGNRE
+/* Normal Memory, Outer Write-Back non-transient, Inner Write-Back non-transient */
+#define ATTR_IWBWA_OWBWA_NTR		MAKE_MAIR_NORMAL_MEMORY(MAIR_NORM_WB_NTR_RWA, MAIR_NORM_WB_NTR_RWA)
+#define MAIR_ATTR_SET(attr, index)	((attr) << (index << UL(3)))
+#define ATTR_INDEX_MASK			U(0x3)
+#define ATTR_INDEX_GET(attr)		(((attr) >> UL(2)) & ATTR_INDEX_MASK)
+
+/*
+ * Shift values for the attributes fields in a block or page descriptor.
+ * See section D4.3.3 in the ARMv8-A ARM (issue B.a).
+ */
+
+/* Memory attributes index field, AttrIndx[2:0]. */
+#define ATTR_INDEX_SHIFT		2
+/* Non-secure bit, NS. */
+#define NS_SHIFT			5
+/* Shareability field, SH[1:0] */
+#define SHAREABILITY_SHIFT		8
+/* The Access Flag, AF. */
+#define ACCESS_FLAG_SHIFT		10
+/* The not global bit, nG. */
+#define NOT_GLOBAL_SHIFT		11
+/* Non-secure extension bit. Only valid in the EL3 translation regime */
+/* Contiguous hint bit. */
+#define CONT_HINT_SHIFT			52
+/* Execute-never bits, XN. */
+#define PXN_SHIFT			53
+#define XN_SHIFT			54
+#define UXN_SHIFT			XN_SHIFT
+
+#endif /* XLAT_DEFS_PRIVATE_H */
diff --git a/lib/xlat/src/xlat_tables_arch.c b/lib/xlat/src/xlat_tables_arch.c
new file mode 100644
index 0000000..6966814
--- /dev/null
+++ b/lib/xlat/src/xlat_tables_arch.c
@@ -0,0 +1,233 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ * SPDX-FileCopyrightText: Copyright Arm Limited and Contributors.
+ */
+
+/* This file is derived from xlat_table_v2 library in TF-A project */
+
+#include <arch_features.h>
+#include <debug.h>
+#include <errno.h>
+#include <xlat_contexts.h>
+#include <xlat_defs_private.h>
+#include <xlat_tables.h>
+#include <xlat_tables_private.h>
+
+/*
+ * Encode a Physical Address Space size for its use in TCR_ELx.
+ */
+static unsigned long long tcr_physical_addr_size_bits(uintptr_t max_addr)
+{
+	if ((max_addr & ADDR_MASK_48_TO_63) != 0U) {
+		/* Physical address can't exceed 48 bits */
+		panic();
+	}
+
+	/* 48 bits address */
+	if ((max_addr & ADDR_MASK_44_TO_47) != 0U) {
+		return TCR_PS_BITS_256TB;
+	}
+
+	/* 44 bits address */
+	if ((max_addr & ADDR_MASK_42_TO_43) != 0U) {
+		return TCR_PS_BITS_16TB;
+	}
+
+	/* 42 bits address */
+	if ((max_addr & ADDR_MASK_40_TO_41) != 0U) {
+		return TCR_PS_BITS_4TB;
+	}
+
+	/* 40 bits address */
+	if ((max_addr & ADDR_MASK_36_TO_39) != 0U) {
+		return TCR_PS_BITS_1TB;
+	}
+
+	/* 36 bits address */
+	if ((max_addr & ADDR_MASK_32_TO_35) != 0U) {
+		return TCR_PS_BITS_64GB;
+	}
+
+	return TCR_PS_BITS_4GB;
+}
+
+/*
+ * Configure MMU registers. This function assumes that all the contexts use the
+ * same limits for VA and PA spaces.
+ */
+int xlat_arch_setup_mmu_cfg(struct xlat_ctx * const ctx)
+{
+	uint64_t mair;
+	uint64_t tcr;
+	uint64_t ttbrx;
+	uintptr_t va_space_size;
+	struct xlat_ctx_cfg *ctx_cfg;
+	struct xlat_ctx_tbls *ctx_tbls;
+	unsigned int txsz;
+	unsigned int t0sz;
+	unsigned int t1sz;
+
+	if (ctx == NULL) {
+		return -EINVAL;
+	}
+
+	ctx_cfg = ctx->cfg;
+	ctx_tbls = ctx->tbls;
+
+	if (ctx_cfg == NULL || ctx_tbls == NULL) {
+		return -EINVAL;
+	}
+
+	if (xlat_ctx_cfg_initialized(ctx) == false) {
+		return -EINVAL;
+	}
+
+	/* MMU cannot be enabled at this point */
+	if (is_mmu_enabled() == true) {
+		return -EPERM;
+	}
+
+	/* Set attributes in the right indices of the MAIR. */
+	mair = MAIR_ATTR_SET(ATTR_DEVICE, ATTR_DEVICE_INDEX);
+	mair |= MAIR_ATTR_SET(ATTR_IWBWA_OWBWA_NTR, ATTR_IWBWA_OWBWA_NTR_INDEX);
+	mair |= MAIR_ATTR_SET(ATTR_NON_CACHEABLE, ATTR_NON_CACHEABLE_INDEX);
+
+	va_space_size = (uintptr_t)ctx_cfg->max_va_size;
+
+	/*
+	 * __builtin_ctzll(0) is undefined but here we are guaranteed that
+	 * va_space_size is in the range [1,UINTPTR_MAX].
+	 */
+	txsz = 64 - __builtin_ctzll(va_space_size);
+
+	/*
+	 * Read TCR_EL2 in order to extract t0sz and t1sz. So we can update the right
+	 * field depending on which context we are configuring and leave the other one
+	 * untouched.
+	 * It will not be a problem if TCR_EL2 was previoulsy configured, as the new
+	 * value of it will be the same with the only difference of the txsz field we
+	 * want to update.
+	 */
+	tcr = read_tcr_el2();
+	if (ctx_cfg->region == VA_LOW_REGION) {
+		t0sz = txsz;
+		t1sz = (tcr >> TCR_EL2_T1SZ_SHIFT) & TCR_EL2_T1SZ_MASK;
+	} else {
+		t0sz = (tcr >> TCR_EL2_T0SZ_SHIFT) & TCR_EL2_T0SZ_MASK;
+		t1sz = txsz;
+	}
+
+	/* Recompute the value for TCR_EL2 */
+	tcr = (uint64_t)t0sz << TCR_EL2_T0SZ_SHIFT;
+	tcr |= (uint64_t)t1sz << TCR_EL2_T1SZ_SHIFT;
+
+	/*
+	 * Set the cacheability and shareability attributes for memory
+	 * associated with translation table walks.
+	 */
+	/* Inner & outer WBWA & shareable for both halfs. */
+	tcr |= TCR_EL2_IRGN0_WBWA | TCR_EL2_ORGN0_WBWA | TCR_EL2_SH0_IS;
+	tcr |= TCR_EL2_IRGN1_WBWA | TCR_EL2_ORGN1_WBWA | TCR_EL2_SH1_IS;
+
+	/*
+	 * ASID and hierarchical permissions.
+	 */
+	tcr |= TCR_EL2_AS | TCR_EL2_HPD0 | TCR_EL2_HPD1;
+
+	/*
+	 * Granule size. Only 4K supported on both halfs.
+	 */
+	tcr |= TCR_EL2_TG0_4K | TCR_EL2_TG1_4K;
+
+	/*
+	 * Set physical address size to the limit supported by the PE.
+	 */
+	tcr |= tcr_physical_addr_size_bits(xlat_arch_get_max_supported_pa());
+
+	write_mair_el2(mair);
+	write_tcr_el2(tcr);
+
+	/*
+	 * Set TTBR bits as well and enable CnP bit so as to share page
+	 * tables with all PEs.
+	 */
+	ttbrx = (uint64_t)(void *)ctx_tbls->base_table;
+
+	/*
+	 * The VA region is not common for the HIGH region as it is used
+	 * by slot buffer.
+	 */
+	if (ctx_cfg->region == VA_HIGH_REGION) {
+		ttbrx &= ~TTBR_CNP_BIT;
+	} else {
+		ttbrx |= TTBR_CNP_BIT;
+	}
+
+	if (ctx_cfg->region == VA_LOW_REGION) {
+		write_ttbr0_el2(ttbrx);
+	} else {
+		write_ttbr1_el2(ttbrx);
+	}
+
+	return 0;
+}
+
+uintptr_t xlat_arch_get_max_supported_pa(void)
+{
+	return (1UL << arch_feat_get_pa_width()) - 1UL;
+}
+
+void xlat_arch_tlbi_va(uintptr_t va)
+{
+	/*
+	 * Ensure the translation table write has drained into memory before
+	 * invalidating the TLB entry.
+	 */
+	dsb(ishst);
+
+	tlbivae2is(TLBI_ADDR(va));
+}
+
+void xlat_arch_tlbi_va_sync(void)
+{
+	/*
+	 * A TLB maintenance instruction can complete at any time after
+	 * it is issued, but is only guaranteed to be complete after the
+	 * execution of DSB by the PE that executed the TLB maintenance
+	 * instruction. After the TLB invalidate instruction is
+	 * complete, no new memory accesses using the invalidated TLB
+	 * entries will be observed by any observer of the system
+	 * domain. See section D4.8.2 of the ARMv8 (issue k), paragraph
+	 * "Ordering and completion of TLB maintenance instructions".
+	 */
+	dsb(ish);
+
+	/*
+	 * The effects of a completed TLB maintenance instruction are
+	 * only guaranteed to be visible on the PE that executed the
+	 * instruction after the execution of an ISB instruction by the
+	 * PE that executed the TLB maintenance instruction.
+	 */
+	isb();
+}
+
+/*
+ * Determine the physical address space encoded in the 'attr' parameter.
+ */
+uint64_t xlat_arch_get_pas(uint64_t attr)
+{
+	uint64_t pas = MT_PAS(attr);
+
+	switch (pas) {
+	case MT_REALM:
+		return 0U;
+	case MT_NS:
+		return LOWER_ATTRS(NS);
+	default:
+		panic();
+	}
+
+	/* Avoid -Werror=return-type. Should never reach here. */
+	return 0U;
+}
diff --git a/lib/xlat/src/xlat_tables_core.c b/lib/xlat/src/xlat_tables_core.c
new file mode 100644
index 0000000..1d8559d
--- /dev/null
+++ b/lib/xlat/src/xlat_tables_core.c
@@ -0,0 +1,949 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ * SPDX-FileCopyrightText: Copyright Arm Limited and Contributors.
+ */
+
+/* This file is derived from xlat_table_v2 library in TF-A project */
+
+#include <arch_features.h>
+#include <arch_helpers.h>
+#include <debug.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <utils_def.h>
+#include <xlat_contexts.h>
+#include "xlat_defs_private.h"
+#include <xlat_tables.h>
+#include "xlat_tables_private.h"
+
+/*
+ * Enumeration of actions that can be made when mapping table entries depending
+ * on the previous value in that entry and information about the region being
+ * mapped.
+ */
+typedef enum {
+
+	/* Do nothing */
+	ACTION_NONE,
+
+	/* Write a block (or page, if in level 3) entry. */
+	ACTION_WRITE_BLOCK_ENTRY,
+
+	/*
+	 * Create a new table and write a table entry pointing to it. Recurse
+	 * into it for further processing.
+	 */
+	ACTION_CREATE_NEW_TABLE,
+
+	/*
+	 * There is a table descriptor in this entry, read it and recurse into
+	 * that table for further processing.
+	 */
+	ACTION_RECURSE_INTO_TABLE,
+
+} action_t;
+
+/* Returns a pointer to the first empty translation table. */
+static inline uint64_t *xlat_table_get_empty(struct xlat_ctx *ctx)
+{
+	assert(ctx->tbls->next_table < ctx->tbls->tables_num);
+	return ctx->tbls->tables[ctx->tbls->next_table++];
+}
+
+/*
+ * Function that returns the index of the first table affected by the VA on
+ * the specified mmap region.
+ */
+static uintptr_t xlat_tables_find_start_va(struct xlat_mmap_region *mm,
+					   const uintptr_t table_base_va,
+					   const unsigned int level)
+{
+	uintptr_t table_idx_va;
+
+	if (mm->base_va > table_base_va) {
+		/* Find the first index of the table affected by the region. */
+		table_idx_va = mm->base_va & ~XLAT_BLOCK_MASK(level);
+	} else {
+		/* Start from the beginning of the table. */
+		table_idx_va = table_base_va;
+	}
+
+	return table_idx_va;
+}
+
+/*
+ * Function that returns table index for the given VA and level arguments.
+ */
+static inline unsigned int  xlat_tables_va_to_index(const uintptr_t table_base_va,
+						    const uintptr_t va,
+						    const unsigned int level)
+{
+	return (unsigned int)((va - table_base_va) >> XLAT_ADDR_SHIFT(level));
+}
+
+/*
+ * From the given arguments, it decides which action to take when mapping the
+ * specified region.
+ */
+static action_t xlat_tables_map_region_action(const struct xlat_mmap_region *mm,
+			unsigned int desc_type, uintptr_t dest_pa,
+			uintptr_t table_entry_base_va, unsigned int level)
+{
+	uintptr_t mm_end_va = mm->base_va + mm->size - 1UL;
+	uintptr_t table_entry_end_va =
+			table_entry_base_va + XLAT_BLOCK_SIZE(level) - 1UL;
+
+	/*
+	 * The descriptor types allowed depend on the current table level.
+	 */
+
+	if ((mm->base_va <= table_entry_base_va) &&
+	    (mm_end_va >= table_entry_end_va)) {
+
+		/*
+		 * Table entry is covered by region
+		 * --------------------------------
+		 *
+		 * This means that this table entry can describe the whole
+		 * translation with this granularity in principle.
+		 */
+
+		if (level == 3U) {
+			/*
+			 * Last level, only page descriptors are allowed.
+			 */
+			if (desc_type == PAGE_DESC) {
+				/*
+				 * There's another region mapped here, don't
+				 * overwrite.
+				 */
+				return ACTION_NONE;
+			} else {
+				if (desc_type != INVALID_DESC) {
+					ERROR("%s (%u): Expected invalid descriptor\n",
+						__func__, __LINE__);
+					panic();
+				}
+				return ACTION_WRITE_BLOCK_ENTRY;
+			}
+
+		} else {
+
+			/*
+			 * Other levels. Table descriptors are allowed. Block
+			 * descriptors too, but they have some limitations.
+			 */
+
+			if (desc_type == TABLE_DESC) {
+				/* There's already a table, recurse into it. */
+				return ACTION_RECURSE_INTO_TABLE;
+
+			} else if (desc_type == INVALID_DESC) {
+				/*
+				 * There's nothing mapped here, create a new
+				 * entry.
+				 *
+				 * Check if the destination granularity allows
+				 * us to use a block descriptor or we need a
+				 * finer table for it.
+				 *
+				 * Also, check if the current level allows block
+				 * descriptors. If not, create a table instead.
+				 */
+				if (((dest_pa & XLAT_BLOCK_MASK(level)) != 0U)
+				    || (level < MIN_LVL_BLOCK_DESC) ||
+				    (mm->granularity < XLAT_BLOCK_SIZE(level))) {
+					return ACTION_CREATE_NEW_TABLE;
+				} else {
+					return ACTION_WRITE_BLOCK_ENTRY;
+				}
+
+			} else {
+				/*
+				 * There's another region mapped here, don't
+				 * overwrite.
+				 */
+				if (desc_type != BLOCK_DESC) {
+					ERROR("%s (%u): Excpected block descriptor\n",
+						__func__, __LINE__);
+					panic();
+				}
+
+				return ACTION_NONE;
+			}
+		}
+
+	} else if ((mm->base_va <= table_entry_end_va) ||
+		   (mm_end_va >= table_entry_base_va)) {
+
+		/*
+		 * Region partially covers table entry
+		 * -----------------------------------
+		 *
+		 * This means that this table entry can't describe the whole
+		 * translation, a finer table is needed.
+
+		 * There cannot be partial block overlaps in level 3. If that
+		 * happens, some of the preliminary checks when adding the
+		 * mmap region failed to detect that PA and VA must at least be
+		 * aligned to PAGE_SIZE.
+		 */
+		if (level >= 3U) {
+			ERROR("%s (%u): Expected table level below 3\n",
+				__func__, __LINE__);
+			panic();
+		}
+
+		if (desc_type == INVALID_DESC) {
+			/*
+			 * The block is not fully covered by the region. Create
+			 * a new table, recurse into it and try to map the
+			 * region with finer granularity.
+			 */
+			return ACTION_CREATE_NEW_TABLE;
+
+		} else {
+			if (desc_type != TABLE_DESC) {
+				ERROR("%s (%u): Expected table descriptor\n",
+					__func__, __LINE__);
+				panic();
+			}
+			/*
+			 * The block is not fully covered by the region, but
+			 * there is already a table here. Recurse into it and
+			 * try to map with finer granularity.
+			 *
+			 * PAGE_DESC for level 3 has the same value as
+			 * TABLE_DESC, but this code can't run on a level 3
+			 * table because there can't be overlaps in level 3.
+			 */
+			return ACTION_RECURSE_INTO_TABLE;
+		}
+	} else {
+
+		/*
+		 * This table entry is outside of the region specified in the
+		 * arguments, don't write anything to it.
+		 */
+		return ACTION_NONE;
+	}
+}
+
+/*
+ * Recursive function that writes to the translation tables and maps the
+ * specified region. On success, it returns the VA of the last byte that was
+ * successfully mapped. On error, it returns the VA of the next entry that
+ * should have been mapped.
+ *
+ * NOTE: This function violates misra-c2012-17.2 due to recursivity.
+ */
+static uintptr_t xlat_tables_map_region(struct xlat_ctx *ctx,
+					struct xlat_mmap_region *mm,
+					uintptr_t table_base_va,
+					uintptr_t *const table_base,
+					unsigned int table_entries,
+					unsigned int level)
+{
+	uintptr_t table_idx_va;
+	unsigned int table_idx;
+	uintptr_t mm_end_va;
+	struct xlat_ctx_cfg *ctx_cfg;
+
+	assert(mm != NULL);
+	assert(ctx != NULL);
+	ctx_cfg = ctx->cfg;
+
+	assert(ctx_cfg != NULL);
+	assert((level >= ctx_cfg->base_level) &&
+					(level <= XLAT_TABLE_LEVEL_MAX));
+
+	mm_end_va = mm->base_va + mm->size - 1U;
+
+	if ((level < ctx_cfg->base_level) || (level > XLAT_TABLE_LEVEL_MAX)) {
+		ERROR("%s (%u): Level out of boundaries (%u)\n",
+			__func__, __LINE__, level);
+		panic();
+	}
+
+	table_idx_va = xlat_tables_find_start_va(mm, table_base_va, level);
+	table_idx = xlat_tables_va_to_index(table_base_va, table_idx_va, level);
+
+	while (table_idx < table_entries) {
+		uintptr_t table_idx_pa;
+		uint64_t *subtable;
+		uint64_t desc;
+
+		desc = table_base[table_idx];
+
+		table_idx_pa = mm->base_pa + table_idx_va - mm->base_va;
+
+		action_t action = xlat_tables_map_region_action(mm,
+			(uint32_t)(desc & DESC_MASK), table_idx_pa,
+			table_idx_va, level);
+
+		if (action == ACTION_WRITE_BLOCK_ENTRY) {
+
+			table_base[table_idx] =
+				xlat_desc(mm->attr, table_idx_pa, level);
+
+		} else if (action == ACTION_CREATE_NEW_TABLE) {
+			uintptr_t end_va;
+
+			subtable = xlat_table_get_empty(ctx);
+			if (subtable == NULL) {
+				/* Not enough free tables to map this region */
+				ERROR("%s (%u): Not enough free tables to map region\n",
+					__func__, __LINE__);
+				panic();
+			}
+
+			/* Point to new subtable from this one. */
+			table_base[table_idx] =
+				TABLE_DESC | (uintptr_t)(void *)subtable;
+
+			/* Recurse to write into subtable */
+			/* FIXME: This violates misra-c2012-17.2 */
+			end_va = xlat_tables_map_region(ctx, mm, table_idx_va,
+					       subtable, XLAT_TABLE_ENTRIES,
+					       level + 1U);
+			if (end_va !=
+				(table_idx_va + XLAT_BLOCK_SIZE(level) - 1UL)) {
+				return end_va;
+			}
+
+		} else if (action == ACTION_RECURSE_INTO_TABLE) {
+			uintptr_t end_va;
+
+			subtable = (uint64_t *)(void *)(desc & TABLE_ADDR_MASK);
+			/* Recurse to write into subtable */
+			/* FIXME: This violates misra-c2012-17.2 */
+			end_va = xlat_tables_map_region(ctx, mm, table_idx_va,
+					       subtable, XLAT_TABLE_ENTRIES,
+					       level + 1U);
+			if (end_va !=
+				(table_idx_va + XLAT_BLOCK_SIZE(level) - 1UL)) {
+				return end_va;
+			}
+
+		} else {
+			if (action != ACTION_NONE) {
+				ERROR("%s (%u): Unexpected action: %u\n",
+					__func__, __LINE__, action);
+				panic();
+			}
+		}
+
+		table_idx++;
+		table_idx_va += XLAT_BLOCK_SIZE(level);
+
+		/* If reached the end of the region, exit */
+		if (mm_end_va <= table_idx_va) {
+			break;
+		}
+	}
+
+	return table_idx_va - 1U;
+}
+
+/*
+ * Function that verifies that a region can be mapped.
+ * Returns:
+ *        0: Success, the mapping is allowed.
+ *   EINVAL: Invalid values were used as arguments.
+ *   ERANGE: The memory limits were surpassed.
+ *   ENOMEM: There is not enough memory in the mmap array.
+ *    EPERM: Region overlaps another one in an invalid way.
+ * EALREADY: The context configuration is already marked as initialized.
+ */
+static int mmap_add_region_check(const struct xlat_ctx *ctx,
+				 const struct xlat_mmap_region *mm)
+{
+	uintptr_t base_pa = mm->base_pa;
+	uintptr_t base_va = mm->base_va;
+	size_t size = mm->size;
+	size_t granularity = mm->granularity;
+	uintptr_t end_pa = base_pa + size - 1UL;
+	uintptr_t end_va = base_va + size - 1UL;
+	unsigned int index;
+	struct xlat_ctx_cfg *ctx_cfg = ctx->cfg;
+
+	if (!IS_PAGE_ALIGNED(base_pa) || !IS_PAGE_ALIGNED(base_va) ||
+			!IS_PAGE_ALIGNED(size)) {
+		return -EFAULT;
+	}
+
+	if ((granularity != XLAT_BLOCK_SIZE(1U)) &&
+	    (granularity != XLAT_BLOCK_SIZE(2U)) &&
+	    (granularity != XLAT_BLOCK_SIZE(3U))) {
+		return -EINVAL;
+	}
+
+	/* Check for overflows */
+	if ((base_pa > end_pa) || (base_va > end_va)) {
+		return -ERANGE;
+	}
+
+	/*
+	 * end_va is calculated as an offset with regards to the base address
+	 * for the current context, so compare it against max_va_size to ensure
+	 * we are within the allowed range.
+	 */
+	if (end_va > ctx_cfg->max_va_size) {
+		return -ERANGE;
+	}
+
+	if (end_pa > xlat_arch_get_max_supported_pa()) {
+		return -ERANGE;
+	}
+
+	/* Check that there is space in the ctx->mmap array */
+	if (ctx_cfg->mmap[ctx_cfg->mmap_num - 1U].size != 0UL) {
+		return -ENOMEM;
+	}
+
+	/* Check for PAs and VAs overlaps with all other regions in this context */
+	index = 0U;
+	while ((index < ctx_cfg->mmap_num) &&
+	       (ctx_cfg->mmap[index].size != 0UL)) {
+		uintptr_t mm_cursor_end_va = ctx_cfg->mmap[index].base_va +
+					     ctx_cfg->mmap[index].size - 1UL;
+
+		unsigned long long mm_cursor_end_pa =
+				ctx_cfg->mmap[index].base_pa
+				+ ctx_cfg->mmap[index].size - 1UL;
+
+		bool separated_pa = (end_pa < ctx_cfg->mmap[index].base_pa) ||
+						(base_pa > mm_cursor_end_pa);
+		bool separated_va = (end_va < ctx_cfg->mmap[index].base_va) ||
+						(base_va > mm_cursor_end_va);
+
+		if (!separated_va || !separated_pa) {
+			return -EPERM;
+		}
+		++index;
+	}
+
+	return 0;
+}
+
+/*
+ * Returns a block/page table descriptor for the given level and attributes.
+ */
+uint64_t xlat_desc(uint64_t attr, uintptr_t addr_pa, unsigned int level)
+{
+	uint64_t desc;
+	uint32_t mem_type;
+	uint32_t shareability_type;
+
+	if ((MT_TYPE(attr) == MT_TRANSIENT)) {
+		/* Transient entry requested. */
+		desc = 0ULL;
+		return desc;
+	}
+
+	/* Make sure that the granularity is fine enough to map this address. */
+	if ((addr_pa & XLAT_BLOCK_MASK(level)) != 0U) {
+		ERROR("%s (%u): 0x%lx has incorrect granularity\n",
+			__func__, __LINE__, addr_pa);
+	}
+
+	desc = addr_pa;
+	/*
+	 * There are different translation table descriptors for level 3 and the
+	 * rest.
+	 */
+	desc |= (level == XLAT_TABLE_LEVEL_MAX) ? PAGE_DESC : BLOCK_DESC;
+	/*
+	 * Always set the access flag, as this library assumes access flag
+	 * faults aren't managed.
+	 */
+	desc |= LOWER_ATTRS(ACCESS_FLAG);
+
+	/* Determine the physical address space this region belongs to. */
+	desc |= xlat_arch_get_pas(attr);
+
+	/*
+	 * Deduce other fields of the descriptor based on the MT_RW memory
+	 * region attributes.
+	 */
+	desc |= ((attr & MT_RW) != 0UL) ? LOWER_ATTRS(AP_RW) : LOWER_ATTRS(AP_RO);
+
+	if ((attr & MT_CONT) != 0UL) {
+		desc |= XLAT_GET_CONT_HINT();
+	}
+
+	if ((attr & MT_NG) != 0UL) {
+		desc |= XLAT_GET_NG_HINT();
+	}
+
+	/*
+	 * Mark this area as non-executable for unpriviledged exception levels.
+	 */
+	desc |= XLAT_GET_UXN_DESC();
+
+	/*
+	 * Deduce shareability domain and executability of the memory region
+	 * from the memory type of the attributes (MT_TYPE).
+	 *
+	 * Data accesses to device memory and non-cacheable normal memory are
+	 * coherent for all observers in the system, and correspondingly are
+	 * always treated as being Outer Shareable. Therefore, for these 2 types
+	 * of memory, it is not strictly needed to set the shareability field
+	 * in the translation tables.
+	 */
+	mem_type = MT_TYPE(attr);
+	if (mem_type == MT_DEVICE) {
+		desc |= LOWER_ATTRS(ATTR_DEVICE_INDEX | OSH);
+		/*
+		 * Always map device memory as execute-never.
+		 * This is to avoid the possibility of a speculative instruction
+		 * fetch, which could be an issue if this memory region
+		 * corresponds to a read-sensitive peripheral.
+		 */
+		desc |= XLAT_GET_PXN_DESC();
+
+	} else { /* Normal memory */
+		/*
+		 * Always map read-write normal memory as execute-never.
+		 * This library assumes that it is used by software that does
+		 * not self-modify its code, therefore R/W memory is reserved
+		 * for data storage, which must not be executable.
+		 *
+		 * Note that setting the XN bit here is for consistency only.
+		 * The function that enables the MMU sets the SCTLR_ELx.WXN bit,
+		 * which makes any writable memory region to be treated as
+		 * execute-never, regardless of the value of the XN bit in the
+		 * translation table.
+		 *
+		 * For read-only memory, rely on the MT_EXECUTE/MT_EXECUTE_NEVER
+		 * attribute to figure out the value of the PXN bit.  The actual
+		 * XN bit(s) to set in the descriptor depends on the context's
+		 * translation regime and the policy applied in
+		 * XLAT_GET_PXN_DESC().
+		 */
+		if (((attr & MT_RW) != 0UL) || ((attr & MT_EXECUTE_NEVER) != 0UL)) {
+			desc |= XLAT_GET_PXN_DESC();
+		}
+
+		shareability_type = MT_SHAREABILITY(attr);
+		if (mem_type == MT_MEMORY) {
+			desc |= LOWER_ATTRS(ATTR_IWBWA_OWBWA_NTR_INDEX);
+			if (shareability_type == MT_SHAREABILITY_NSH) {
+				desc |= LOWER_ATTRS(NSH);
+			} else if (shareability_type == MT_SHAREABILITY_OSH) {
+				desc |= LOWER_ATTRS(OSH);
+			} else {
+				desc |= LOWER_ATTRS(ISH);
+			}
+
+			/* Check if Branch Target Identification is enabled */
+			/* TODO: This is needed if BTI is enabled. Double check this code. */
+			/* Set GP bit for block and page code entries
+			 * if BTI mechanism is implemented.
+			 */
+		} else {
+			if (mem_type != MT_NON_CACHEABLE) {
+				/* Only non cacheable memory at this point */
+				panic();
+			}
+			desc |= LOWER_ATTRS(ATTR_NON_CACHEABLE_INDEX | OSH);
+		}
+	}
+
+	return desc;
+}
+
+/*****************************************************************************
+ * Public part of the core translation library.
+ ****************************************************************************/
+
+/*
+ * Add a memory region with defined base PA and base VA. This function can only
+ * be used before marking the xlat_ctx_cfg for the current xlat_ctx as
+ * initialized.
+ *
+ * The region cannot be removed once added.
+ *
+ * This function returns 0 on success or an error code otherwise.
+ */
+int xlat_mmap_add_region_ctx(struct xlat_ctx *ctx,
+			     struct xlat_mmap_region *mm)
+{
+	unsigned int mm_last_idx = 0U;
+	unsigned int mm_cursor_idx = 0U;
+	uintptr_t end_pa;
+	uintptr_t end_va;
+	struct xlat_ctx_cfg *ctx_cfg;
+	struct xlat_ctx_tbls *ctx_tbls;
+	int ret;
+
+	if (ctx == NULL) {
+		return -EINVAL;
+	}
+
+	ctx_cfg = ctx->cfg;
+	ctx_tbls = ctx->tbls;
+
+	if (ctx_cfg == NULL || ctx_tbls == NULL) {
+		return -EINVAL;
+	}
+
+	if (mm == NULL) {
+		return -EINVAL;
+	}
+
+	/* The context data cannot be initialized */
+	if (xlat_ctx_cfg_initialized(ctx) == true) {
+		return -EINVAL;
+	}
+
+	/* Memory regions must be added before initializing the xlat tables. */
+	assert(ctx_tbls->initialized == false);
+
+	/* Ignore empty regions */
+	if (mm->size == 0UL) {
+		return 0;
+	}
+
+	if (ctx_cfg->region == VA_LOW_REGION) {
+		/*
+		 * Initialize the base_va for the current context if not
+		 * initialized yet.
+		 *
+		 * For the low region, the architecture mandates that
+		 * base_va has to be 0.
+		 *
+		 * Overwriting this field should not be a problem as its value
+		 * is expected to be always the same.
+		 */
+		ctx_cfg->base_va = 0ULL;
+
+		if ((mm->base_va & HIGH_REGION_MASK) ||
+		     ((mm->base_va + mm->size) & HIGH_REGION_MASK)) {
+			ERROR("%s (%u): Base VA and address space do not match: ",
+							__func__, __LINE__);
+			ERROR("Base va = 0x%lx, Address space = Low region\n",
+				mm->base_va);
+			return -EINVAL;
+		}
+	} else {
+		/*
+		 * Initialize the base_va for the current context if not
+		 * initialized yet.
+		 *
+		 * For the high region, the architecture mandates that
+		 * base_va has to be 0xFFFF-FFFF-FFFF-FFFF minus the VA space
+		 * size plus one.
+		 *
+		 * Overwriting this field should not be a problem as its value
+		 * is expected to be always the same.
+		 */
+		ctx_cfg->base_va = (ULONG_MAX - ctx_cfg->max_va_size + 1ULL);
+
+		if (mm->base_va < ctx_cfg->base_va) {
+			ERROR("%s (%u): Base VA is not aligned with the high region start: ",
+							__func__, __LINE__);
+			ERROR("Base VA = 0x%lx, high region start VA = 0x%lx\n",
+				mm->base_va, ctx_cfg->base_va);
+			return -EINVAL;
+		}
+
+		/*
+		 * If this context is handling the high half region of the VA,
+		 * adjust the start address of this area by substracting the
+		 * start address of the region as the table entries are
+		 * relative to the latter. Once ttbr1_el2 is configured, the
+		 * MMU will translate the addresses properly.
+		 */
+		mm->base_va -= ctx_cfg->base_va;
+	}
+
+	end_pa = mm->base_pa + mm->size - 1UL;
+	end_va = mm->base_va + mm->size - 1UL;
+
+	ret = mmap_add_region_check(ctx, mm);
+	if (ret != 0) {
+		ERROR("%s (%u): mmap_add_region_check() failed. error %d\n",
+					__func__, __LINE__, ret);
+		return ret;
+	}
+
+	/*
+	 * Find correct place in mmap to insert new region.
+	 * Overlapping is not allowed.
+	 */
+	while (((ctx_cfg->mmap[mm_cursor_idx].base_va) < mm->base_va)
+	       && (ctx_cfg->mmap[mm_cursor_idx].size != 0UL)
+	       && (mm_cursor_idx < ctx_cfg->mmap_num)) {
+		++mm_cursor_idx;
+	}
+
+	/*
+	 * Find the last entry marker in the mmap
+	 */
+	while ((mm_last_idx < ctx_cfg->mmap_num) &&
+	       (ctx_cfg->mmap[mm_last_idx].size != 0UL)) {
+		++mm_last_idx;
+	}
+
+	/*
+	 * Check if we have enough space in the memory mapping table.
+	 * This shouldn't happen as we have checked in mmap_add_region_check
+	 * that there is free space.
+	 */
+	assert(ctx_cfg->mmap[mm_last_idx].size == 0UL);
+
+	/*
+	 * Make room for new region by moving other regions up by one place.
+	 */
+	(void)memmove((void *)(&ctx_cfg->mmap[mm_cursor_idx + 1U]),
+		      (void *)(&ctx_cfg->mmap[mm_cursor_idx]),
+		      sizeof(struct xlat_mmap_region) *
+						(mm_last_idx - mm_cursor_idx));
+
+	/* Store the memory mapping information into the context. */
+	(void)memcpy((void *)(&ctx_cfg->mmap[mm_cursor_idx]), (void *)mm,
+						sizeof(struct xlat_mmap_region));
+
+	if (end_pa > ctx_cfg->max_mapped_pa) {
+		ctx_cfg->max_mapped_pa = end_pa;
+	}
+
+	if (end_va > ctx_cfg->max_mapped_va_offset) {
+		ctx_cfg->max_mapped_va_offset = end_va;
+	}
+
+	return 0;
+}
+
+/*
+ * Add an array of memory regions with defined base PA and base VA.
+ * This function needs to be called before initialiting the xlat_ctx_cfg.
+ * Setting the `last` argument to true will initialise the xlat_ctx_cfg.
+ *
+ * The regions cannot be removed once added.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+int xlat_mmap_add_ctx(struct xlat_ctx *ctx,
+		      struct xlat_mmap_region *mm,
+		      bool last)
+{
+	if ((ctx == NULL) || (mm == NULL)) {
+		return -EINVAL;
+	}
+
+	struct xlat_mmap_region *mm_cursor = mm;
+
+	while (mm_cursor->size != 0UL) {
+		int retval;
+
+		retval = xlat_mmap_add_region_ctx(ctx, mm_cursor);
+		if (retval != 0) {
+			/*
+			 * In case of error, stop an return.
+			 * Note, the context might be in an invalid
+			 * state and it will need to be restarted.
+			 */
+			return retval;
+		}
+		mm_cursor++;
+	}
+
+	if (last) {
+		/*
+		 * Mark the configuration part of the context as initialized.
+		 * From this point on, no more memory mapping areas can be
+		 * added to this context (or any other sharing the same
+		 * configuration).
+		 */
+		ctx->cfg->initialized = true;
+		flush_dcache_range((uintptr_t)(void *)ctx->cfg,
+				   sizeof(struct xlat_ctx_cfg));
+
+	}
+
+#if LOG_LEVEL >= LOG_LEVEL_VERBOSE
+	VERBOSE("Runtime mapings");
+	if (ctx->cfg->region == VA_LOW_REGION) {
+		VERBOSE("(Low Region):\n");
+	} else {
+		VERBOSE("(High Region):\n");
+	}
+
+	for (unsigned int i = 0U; i < ctx->cfg->mmap_num; i++) {
+		VERBOSE("\tRegion: 0x%lx - 0x%lx has attributes 0x%lx\n",
+			ctx->cfg->mmap[i].base_va,
+			ctx->cfg->mmap[i].base_va + ctx->cfg->mmap[i].size - 1U,
+			ctx->cfg->mmap[i].attr);
+	}
+#endif /* LOG_LEVEL_VERBOSE */
+
+	return 0;
+}
+
+/*
+ * Initialize translation tables (and mark xlat_ctx_cfg as initialized if
+ * not already initialized) associated to the current context.
+ *
+ * The struct xlat_ctx_cfg of the context might be shared with other
+ * contexts that might have already initialized it. This is expected and
+ * should not cause any problem.
+ *
+ * This function assumes that the xlat_ctx_cfg field of the context has been
+ * properly configured by previous calls to xlat_mmap_add_region_ctx().
+ *
+ * This function returns 0 on success or an error code otherwise.
+ */
+int xlat_init_tables_ctx(struct xlat_ctx *ctx)
+{
+	struct xlat_ctx_cfg *ctx_cfg;
+	struct xlat_ctx_tbls *ctx_tbls;
+	unsigned int index;
+
+	if (ctx == NULL) {
+		return -EINVAL;
+	}
+
+	ctx_cfg = ctx->cfg;
+	ctx_tbls = ctx->tbls;
+
+	if (ctx_cfg == NULL || ctx_tbls == NULL) {
+		return -EINVAL;
+	}
+
+	if (xlat_ctx_tbls_initialized(ctx)) {
+		VERBOSE("%s (%u): Translation tables already initialized\n",
+					__func__, __LINE__);
+		return -EALREADY;
+	}
+
+	if (!xlat_ctx_cfg_initialized(ctx)) {
+		VERBOSE("%s (%u): Translation context configuration not initialized\n",
+					__func__, __LINE__);
+		return -EINVAL;
+	}
+
+	if (is_mmu_enabled() == true) {
+		ERROR("%s (%u): MMU is already enabled\n", __func__, __LINE__);
+		return -EINVAL;
+	}
+
+	xlat_mmap_print(ctx);
+
+	/*
+	 * All tables must be zeroed/initialized before mapping any region
+	 * as they are allocated outside the .bss area.
+	 */
+	for (unsigned int i = 0U; i < XLAT_TABLE_ENTRIES; i++) {
+		ctx_tbls->base_table[i] = INVALID_DESC;
+	}
+
+	for (unsigned int j = 0; j < ctx_tbls->tables_num; j++) {
+		for (unsigned int i = 0U; i < XLAT_TABLE_ENTRIES; i++) {
+			ctx_tbls->tables[j][i] = INVALID_DESC;
+		}
+	}
+
+	index = 0U;
+	while ((index < ctx_cfg->mmap_num) &&
+	       (ctx_cfg->mmap[index].size != 0UL)) {
+		uintptr_t end_va = xlat_tables_map_region(ctx,
+						&ctx_cfg->mmap[index],
+						0U,
+						ctx_tbls->base_table,
+						ctx_tbls->max_base_table_entries,
+						ctx_cfg->base_level);
+		if (end_va != (ctx_cfg->mmap[index].base_va +
+					ctx_cfg->mmap[index].size - 1UL)) {
+			ERROR("%s (%u): Not enough memory to map region: "
+			      " VA:0x%lx  PA:0x%lx  size:0x%zx  attr:0x%lx\n",
+			      __func__, __LINE__, ctx_cfg->mmap[index].base_va,
+						  ctx_cfg->mmap[index].base_pa,
+						  ctx_cfg->mmap[index].size,
+						  ctx_cfg->mmap[index].attr);
+			return -ENOMEM;
+		}
+
+		++index;
+	}
+
+	/* Flush the cache as a good measure */
+	flush_dcache_range((uintptr_t)(void *)ctx_tbls->base_table,
+			 sizeof(uint64_t) * XLAT_TABLE_ENTRIES);
+	flush_dcache_range((uintptr_t)(void *)ctx_tbls->tables,
+			 sizeof(uint64_t) * (unsigned long)ctx_tbls->tables_num
+						* XLAT_TABLE_ENTRIES);
+
+	ctx_tbls->initialized = true;
+
+	flush_dcache_range((uintptr_t)(void *)ctx_tbls,
+			   sizeof(struct xlat_ctx_tbls));
+	flush_dcache_range((uintptr_t)(void *)ctx, sizeof(struct xlat_ctx));
+
+	xlat_tables_print(ctx);
+
+	return 0;
+}
+
+/*
+ * Initialize a context dynamically at runtime using the given xlat_ctx_cfg
+ * and xlat_ctx_tbls structures.
+ *
+ * Return 0 if success or a Posix erro code otherwise.
+ */
+int xlat_ctx_create_dynamic(struct xlat_ctx *ctx,
+			    struct xlat_ctx_cfg *cfg,
+			    struct xlat_ctx_tbls *tbls,
+			    void *base_tables,
+			    unsigned int base_level_entries,
+			    void *tables_ptr,
+			    unsigned int ntables)
+{
+	if (ctx == NULL) {
+		return -EINVAL;
+	}
+
+	if (XLAT_TABLES_CTX_CFG_VALID(ctx) &&
+	    XLAT_TABLES_CTX_TBL_VALID(ctx)) {
+		return -EALREADY;
+	}
+
+	/* Add the configuration to the context */
+	XLAT_SETUP_CTX_CFG(ctx, cfg);
+
+	/* Initialize the tables structure */
+	XLAT_INIT_CTX_TBLS(tbls, tables_ptr, ntables,
+			   base_tables, base_level_entries);
+
+	/* Add the tables to the context */
+	XLAT_SETUP_CTX_TBLS(ctx, tbls);
+
+	return 0;
+}
+
+/*
+ * Returns true if the context is already initialized and false otherwise.
+ * This function only takes into account whether xlat_ctx_cfg is initialized.
+ */
+bool xlat_ctx_cfg_initialized(const struct xlat_ctx * const ctx)
+{
+	assert(ctx != NULL);
+	assert(ctx->cfg != NULL);
+	return ctx->cfg->initialized;
+}
+
+/*
+ * Returns true if the translation tables on the current context are already
+ * initialized or false otherwise.
+ */
+bool xlat_ctx_tbls_initialized(const struct xlat_ctx * const ctx)
+{
+	assert(ctx != NULL);
+	assert(ctx->tbls != NULL);
+	return ctx->tbls->initialized;
+}
diff --git a/lib/xlat/src/xlat_tables_private.h b/lib/xlat/src/xlat_tables_private.h
new file mode 100644
index 0000000..79cf8a8
--- /dev/null
+++ b/lib/xlat/src/xlat_tables_private.h
@@ -0,0 +1,128 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ * SPDX-FileCopyrightText: Copyright Arm Limited and Contributors.
+ */
+
+/* This file is derived from xlat_table_v2 library in TF-A project */
+
+#ifndef XLAT_TABLES_PRIVATE_H
+#define XLAT_TABLES_PRIVATE_H
+
+#include <stdbool.h>
+#include <xlat_contexts.h>
+
+/* Determine the physical address space encoded in the 'attr' parameter. */
+uint64_t xlat_arch_get_pas(uint64_t attr);
+
+/*
+ * Invalidate all TLB entries that match the given virtual address. This
+ * operation applies to all PEs in the same Inner Shareable domain as the PE
+ * that executes this function. This functions must be called for every
+ * translation table entry that is modified. It only affects the EL2
+ * Translate regime
+ */
+void xlat_arch_tlbi_va(uintptr_t va);
+
+/*
+ * This function has to be called at the end of any code that uses the function
+ * xlat_arch_tlbi_va().
+ */
+void xlat_arch_tlbi_va_sync(void);
+
+/* Print VA, PA, size and attributes of all regions in the mmap array. */
+void xlat_mmap_print(const struct xlat_ctx *ctx);
+
+/*
+ * Print the current state of the translation tables by reading them from
+ * memory.
+ */
+void xlat_tables_print(struct xlat_ctx *ctx);
+
+/*
+ * Returns a block/page table descriptor for the given level and attributes.
+ */
+uintptr_t xlat_desc(uint64_t attr, uintptr_t addr_pa, unsigned int level);
+
+/*
+ * Return the maximum physical address supported by the hardware.
+ */
+uintptr_t xlat_arch_get_max_supported_pa(void);
+
+/*
+ * Return the unpriviledged execute-never mask that will prevent instruction
+ * fetch by EL0 at the EL2&0 translation regime.
+ */
+#define XLAT_GET_UXN_DESC() (UPPER_ATTRS(UXN))
+
+/*
+ * Return the priviledged execute-never mask that will prevent instruction
+ * fetch by EL2 at the EL2&0 translation regime.
+ */
+#define XLAT_GET_PXN_DESC() (UPPER_ATTRS(PXN))
+
+/*
+ * Return the contiguous mask for a page or block descriptor
+ */
+#define XLAT_GET_CONT_HINT() (UPPER_ATTRS(CONT_HINT))
+
+/*
+ * Return the NG flag for a page or block descriptor
+ */
+#define XLAT_GET_NG_HINT() (LOWER_ATTRS(NG_HINT))
+
+/*
+ * Set up the xlat_ctx_cfg field of a given context.
+ * This macro doesn't check parameters.
+ *
+ * _ctx:
+ *   Pointer to xlat_ctx.
+ *
+ * _cfg:
+ *   reference to xlat_ctx_cfg that needs to be set to _ctx.
+ */
+#define XLAT_SETUP_CTX_CFG(_ctx, _cfg)		(_ctx)->cfg = (_cfg)
+
+/*
+ * Set up the xlat_ctx_tbls field of a given context.
+ * This macro doesn't check parameters.
+ *
+ * _ctx:
+ *   Context where to add the configuration strucutre.
+ *
+ * _tlbs:
+ *   Reference to the xlat_ctx_tlbs structure where to add to the context.
+ */
+#define XLAT_SETUP_CTX_TBLS(_ctx, _tbls)	(_ctx)->tbls = (_tbls)
+
+/*
+ * Initialize an existing xlat_ctx_tbls structure with the given parameters
+ * This macro doesn't check parameters.
+ *
+ * _tlbs:
+ *   Pointer to xlat_ctx.
+ *
+ * _tables:
+ *   pointer to non-base xlat_ctx_tbls.
+ *
+ * _tnum:
+ *   Maximum number of intermediate tables that can fit in the _tables area.
+ *
+ * _btables:
+ *   pointer to base xlat_ctx_tbls.
+ *
+ * _bt_entries:
+ *   Maximum number of entries available on the base table.
+ */
+#define XLAT_INIT_CTX_TBLS(_tbls, _tables, _tnum,			\
+			   _btables, _bt_entries)			\
+	{								\
+		(_tbls)->tables = (_tables);				\
+		(_tbls)->tables_num = (_tnum);				\
+		(_tbls)->base_table = (_btables);			\
+		(_tbls)->max_base_table_entries = (_bt_entries);	\
+		(_tbls)->next_table = 0U;				\
+		(_tbls)->initialized = false;				\
+	}
+
+#endif /* XLAT_TABLES_PRIVATE_H */
diff --git a/lib/xlat/src/xlat_tables_utils.c b/lib/xlat/src/xlat_tables_utils.c
new file mode 100644
index 0000000..93cda40
--- /dev/null
+++ b/lib/xlat/src/xlat_tables_utils.c
@@ -0,0 +1,517 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ * SPDX-FileCopyrightText: Copyright Arm Limited and Contributors.
+ */
+
+/* This file is derived from xlat_table_v2 library in TF-A project */
+
+#include <arch_helpers.h>
+#include <debug.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <utils_def.h>
+#include <xlat_contexts.h>
+#include "xlat_defs_private.h"
+#include <xlat_tables.h>
+#include "xlat_tables_private.h"
+
+#if LOG_LEVEL < LOG_LEVEL_VERBOSE
+
+void xlat_mmap_print(const struct xlat_ctx *ctx)
+{
+	(void)ctx;
+
+	/* Empty */
+}
+
+void xlat_tables_print(struct xlat_ctx *ctx)
+{
+	(void)ctx;
+
+	/* Empty */
+}
+
+#else /* if LOG_LEVEL >= LOG_LEVEL_VERBOSE */
+
+void xlat_mmap_print(const struct xlat_ctx *ctx)
+{
+	VERBOSE("mmap:\n");
+
+	for (unsigned int i = 0U; i < ctx->cfg->mmap_num; i++) {
+		uintptr_t base_va;
+
+		base_va = ((ctx->cfg->region == VA_LOW_REGION) ?
+				ctx->cfg->mmap[i].base_va :
+				(ctx->cfg->mmap[i].base_va
+					+ ctx->cfg->base_va));
+		if (MT_TYPE(ctx->cfg->mmap[i].attr) != MT_TRANSIENT) {
+			VERBOSE(" VA:0x%lx  PA:0x%lx  size:0x%zx  attr:0x%lx  granularity:0x%zx\n",
+			       base_va, ctx->cfg->mmap[i].base_pa,
+			       ctx->cfg->mmap[i].size, ctx->cfg->mmap[i].attr,
+			       ctx->cfg->mmap[i].granularity);
+		} else {
+			VERBOSE(" VA:0x%lx  PA: TRANSIENT  size:0x%zx  granularity:0x%zx\n",
+			       base_va, ctx->cfg->mmap[i].size,
+			       ctx->cfg->mmap[i].granularity);
+		}
+	};
+	VERBOSE("\n");
+}
+
+/* Print the attributes of the specified block descriptor. */
+static void xlat_desc_print(uint64_t desc)
+{
+	uint64_t mem_type_index = ATTR_INDEX_GET(desc);
+
+	if (mem_type_index == ATTR_IWBWA_OWBWA_NTR_INDEX) {
+		VERBOSE("MEM");
+	} else if (mem_type_index == ATTR_NON_CACHEABLE_INDEX) {
+		VERBOSE("NC");
+	} else {
+		if (mem_type_index != ATTR_DEVICE_INDEX) {
+			/* Unsupported memory type */
+			panic();
+		}
+		VERBOSE("DEV");
+	}
+
+	VERBOSE(((desc & LOWER_ATTRS(AP_RO)) != 0ULL) ? "-RO" : "-RW");
+	VERBOSE(((desc & UPPER_ATTRS(PXN)) != 0ULL) ? "-PXN" : "-PEXEC");
+	VERBOSE(((desc & UPPER_ATTRS(XN)) != 0ULL) ? "-XN" : "-EXEC");
+
+	if ((desc & LOWER_ATTRS(NS)) == 0ULL) {
+		VERBOSE("-RL");
+	} else {
+		VERBOSE("-N");
+	}
+
+	/* Check Guarded Page bit */
+	if ((desc & GP) != 0ULL) {
+		VERBOSE("-GP");
+	}
+}
+
+static const char * const level_spacers[] = {
+	"[LV0] ",
+	"  [LV1] ",
+	"    [LV2] ",
+	"      [LV3] "
+};
+
+static const char *invalid_descriptors_ommited =
+		"%s(%d invalid descriptors omitted)\n";
+
+/*
+ * Recursive function that reads the translation tables passed as an argument
+ * and prints their status.
+ */
+static void xlat_tables_print_internal(struct xlat_ctx *ctx, uintptr_t table_base_va,
+		const uint64_t *table_base, unsigned int table_entries,
+		unsigned int level)
+{
+	uint64_t *addr_inner;
+	unsigned int invalid_row_count;
+	unsigned int table_idx = 0U;
+	size_t level_size;
+	uintptr_t table_idx_va;
+
+	if (level > XLAT_TABLE_LEVEL_MAX) {
+		/* Level out of bounds */
+		panic();
+	}
+
+	assert((ctx != NULL) &&
+	       (ctx->cfg != NULL) &&
+	       (ctx->tbls != NULL));
+
+	level_size = XLAT_BLOCK_SIZE(level);
+	table_idx_va = (ctx->cfg->region == (VA_LOW_REGION) ?
+			(table_base_va) :
+			(table_base_va + ctx->cfg->base_va));
+
+	/*
+	 * Keep track of how many invalid descriptors are counted in a row.
+	 * Whenever multiple invalid descriptors are found, only the first one
+	 * is printed, and a line is added to inform about how many descriptors
+	 * have been omitted.
+	 */
+	invalid_row_count = 0U;
+
+	while (table_idx < table_entries) {
+		uint64_t desc;
+
+		desc = table_base[table_idx];
+
+		if ((desc & DESC_MASK) == INVALID_DESC) {
+
+			if (invalid_row_count == 0U) {
+				VERBOSE("%sVA:0x%lx size:0x%zx\n",
+					level_spacers[level],
+					table_idx_va, level_size);
+			}
+			invalid_row_count++;
+
+		} else {
+
+			if (invalid_row_count > 1U) {
+				VERBOSE(invalid_descriptors_ommited,
+				       level_spacers[level],
+				       invalid_row_count - 1U);
+			}
+			invalid_row_count = 0U;
+
+			/*
+			 * Check if this is a table or a block. Tables are only
+			 * allowed in levels other than 3, but DESC_PAGE has the
+			 * same value as DESC_TABLE, so we need to check.
+			 */
+			if (((desc & DESC_MASK) == TABLE_DESC) &&
+					(level < XLAT_TABLE_LEVEL_MAX)) {
+				/*
+				 * Do not print any PA for a table descriptor,
+				 * as it doesn't directly map physical memory
+				 * but instead points to the next translation
+				 * table in the translation table walk.
+				 */
+				VERBOSE("%sVA:0x%lx size:0x%zx\n",
+				       level_spacers[level],
+				       table_idx_va, level_size);
+
+				addr_inner = (uint64_t *)(void *)(desc & TABLE_ADDR_MASK);
+
+				/* FIXME: Recursion. */
+				xlat_tables_print_internal(ctx, table_idx_va,
+					addr_inner, XLAT_TABLE_ENTRIES,
+					level + 1U);
+			} else {
+				VERBOSE("%sVA:0x%lx PA:0x%lx size:0x%zx ",
+				       level_spacers[level], table_idx_va,
+				       (uint64_t)(desc & TABLE_ADDR_MASK),
+				       level_size);
+				xlat_desc_print(desc);
+				VERBOSE("\n");
+			}
+		}
+
+		table_idx++;
+		table_idx_va += level_size;
+	}
+
+	if (invalid_row_count > 1U) {
+		VERBOSE(invalid_descriptors_ommited,
+			level_spacers[level], invalid_row_count - 1U);
+	}
+}
+
+void xlat_tables_print(struct xlat_ctx *ctx)
+{
+	unsigned int used_page_tables;
+	struct xlat_ctx_cfg *ctx_cfg = ctx->cfg;
+
+	assert(ctx_cfg != NULL);
+
+	uintptr_t max_mapped_va_offset = (ctx_cfg->region == (VA_LOW_REGION) ?
+			(ctx_cfg->max_mapped_va_offset) :
+			(ctx_cfg->max_mapped_va_offset + ctx_cfg->base_va));
+	uintptr_t max_allowed_va = (ctx_cfg->region == (VA_LOW_REGION) ?
+			(ctx_cfg->max_va_size) :
+			(ctx_cfg->max_va_size + ctx_cfg->base_va));
+
+	VERBOSE("Translation tables state:\n");
+	VERBOSE("  Max allowed PA:  0x%lx\n", xlat_arch_get_max_supported_pa());
+	VERBOSE("  Max allowed VA:  0x%lx\n", max_allowed_va);
+	VERBOSE("  Max mapped PA:   0x%lx", ctx_cfg->max_mapped_pa);
+	for (unsigned int i = 0U; i < ctx_cfg->mmap_num; i++) {
+		if (ctx_cfg->mmap[i].attr == MT_TRANSIENT) {
+			/*
+			 * If there is a transient region on this context, we
+			 * do not know what will be the highest PA, so print a
+			 * note on the log.
+			 */
+			VERBOSE(" - Estimated (transient region)");
+			break;
+		}
+	}
+	VERBOSE("\n");
+	VERBOSE("  Max mapped VA:   0x%lx\n", max_mapped_va_offset);
+
+	VERBOSE("  Initial lookup level: %u\n", ctx_cfg->base_level);
+	VERBOSE("  Entries @initial lookup level: %u\n",
+					ctx->tbls->max_base_table_entries);
+
+	used_page_tables = ctx->tbls->next_table;
+	VERBOSE("  Used %d tables out of %d (spare: %d)\n",
+				used_page_tables, ctx->tbls->tables_num,
+				ctx->tbls->tables_num - used_page_tables);
+
+	xlat_tables_print_internal(ctx, 0U, ctx->tbls->base_table,
+				   ctx->tbls->max_base_table_entries,
+				   ctx_cfg->base_level);
+}
+
+#endif /* LOG_LEVEL >= LOG_LEVEL_VERBOSE */
+
+/*
+ * Do a translation table walk to find the last level table that maps
+ * virtual_addr.
+ *
+ * On success, return the address of the last level table within the
+ * translation table. Its lookup level is stored in '*out_level'.
+ * On error, return NULL.
+ */
+static uint64_t *find_xlat_last_table(uintptr_t virtual_addr,
+				      const struct xlat_ctx * const ctx,
+				      unsigned int * const out_level)
+{
+	unsigned int start_level;
+	uint64_t *ret_table;
+	unsigned int entries;
+	struct xlat_ctx_tbls *ctx_tbls;
+	struct xlat_ctx_cfg *ctx_cfg;
+
+
+	assert(ctx != NULL);
+	assert(ctx->cfg != NULL);
+	assert(ctx->tbls != NULL);
+	assert(out_level != NULL);
+
+	ctx_tbls = ctx->tbls;
+	ctx_cfg = ctx->cfg;
+	start_level = ctx_cfg->base_level;
+	ret_table = ctx_tbls->base_table;
+	entries = ctx_tbls->max_base_table_entries;
+
+	for (unsigned int level = start_level;
+	     level <= XLAT_TABLE_LEVEL_MAX;
+	     level++) {
+		unsigned int idx;
+		uint64_t desc;
+		uint64_t desc_type;
+
+		idx = XLAT_TABLE_IDX(virtual_addr, level);
+		if (idx >= entries) {
+			WARN("Missing xlat table entry at address 0x%lx\n",
+			      virtual_addr);
+			return NULL;
+		}
+
+		desc = ret_table[idx];
+		desc_type = desc & DESC_MASK;
+
+		if (desc_type != TABLE_DESC) {
+			if (((desc_type == BLOCK_DESC) ||
+			    (((desc_type == PAGE_DESC) || (desc_type == INVALID_DESC))
+			      && (level == XLAT_TABLE_LEVEL_MAX)))) {
+				*out_level = level;
+				return ret_table;
+			}
+			return NULL;
+		}
+
+		ret_table = (uint64_t *)(void *)(desc & TABLE_ADDR_MASK);
+		entries = XLAT_TABLE_ENTRIES;
+	}
+
+	/*
+	 * This shouldn't be reached, the translation table walk should end at
+	 * most at level XLAT_TABLE_LEVEL_MAX and return from inside the loop
+	 * but we need this to avoid MISRA problems.
+	 */
+	return NULL;
+}
+
+/*****************************************************************************
+ * Public part of the utility library for translation tables.
+ ****************************************************************************/
+
+/*
+ * Function to unmap a physical memory page from the descriptor entry and
+ * VA given.
+ * This function implements the "Break" part of the Break-Before-Make semantics
+ * needed by the Armv8.x architecture in order to update the page descriptors.
+ *
+ * This function returns 0 on success or an error code otherwise.
+ *
+ * For simplicity, this function will not take into consideration holes on the
+ * table pointed by entry, as long as va belongs to the VA space owned by the
+ * context.
+ */
+int xlat_unmap_memory_page(struct xlat_table_entry * const table,
+			   const uintptr_t va)
+{
+	uint64_t *entry;
+
+	assert(table != NULL);
+
+	entry = xlat_get_pte_from_table(table, va);
+
+	if (entry == NULL) {
+		return -EFAULT;
+	}
+
+	/*
+	 * No need to perform any checks on this page descriptor as it is going
+	 * to be made invalid anyway.
+	 */
+	xlat_write_descriptor(entry, INVALID_DESC);
+
+	/* Invalidate any cached copy of this mapping in the TLBs. */
+	xlat_arch_tlbi_va(va);
+
+	/* Ensure completion of the invalidation. */
+	xlat_arch_tlbi_va_sync();
+
+	return 0;
+}
+
+/*
+ * Function to map a physical memory page from the descriptor table entry
+ * and VA given. This function implements the "Make" part of the
+ * Break-Before-Make semantics needed by the armv8.x architecture in order
+ * to update the page descriptors.
+ *
+ * This function eturns 0 on success or an error code otherwise.
+ *
+ * For simplicity, this function will not take into consideration holes on the
+ * table pointed by entry, as long as va belongs to the VA space owned by the
+ * context.
+ */
+int xlat_map_memory_page_with_attrs(const struct xlat_table_entry * const table,
+				    const uintptr_t va,
+				    const uintptr_t pa,
+				    const uint64_t attrs)
+{
+	uint64_t desc;
+	uint64_t *desc_ptr;
+
+	assert(table != NULL);
+
+	desc_ptr = xlat_get_pte_from_table(table, va);
+
+	if (desc_ptr == NULL) {
+		return -EFAULT;
+	}
+
+	/* This function must only be called on invalid descriptors */
+	if (xlat_read_descriptor(desc_ptr) != INVALID_DESC) {
+		return -EFAULT;
+	}
+
+	/* Check that pa is within boundaries */
+	if (pa > xlat_arch_get_max_supported_pa()) {
+		return -EFAULT;
+	}
+
+	/* Generate the new descriptor */
+	desc = xlat_desc(attrs, pa, table->level);
+
+	xlat_write_descriptor(desc_ptr, desc);
+
+	/* Ensure the translation table write has drained into memory */
+	dsb(ishst);
+	isb();
+
+	return 0;
+}
+
+/*
+ * Return a table entry structure given a context and a VA.
+ * The return structure is populated on the retval field.
+ *
+ * This function returns 0 on success or a Linux error code otherwise.
+ */
+int xlat_get_table_from_va(struct xlat_table_entry * const retval,
+			   const struct xlat_ctx * const ctx,
+			   const uintptr_t va)
+{
+	uintptr_t page_va;
+	uint64_t *table;
+	unsigned int level;
+	struct xlat_ctx_cfg *ctx_cfg;
+
+	assert((ctx != NULL) &&
+	       (ctx->cfg != NULL) &&
+	       (ctx->tbls != NULL) &&
+	       (retval != NULL) &&
+	       (ctx->tbls->initialized == true));
+
+	ctx_cfg = ctx->cfg;
+
+	/* Check if the VA is within the mapped range */
+	if (((va > (ctx_cfg->max_mapped_va_offset + ctx_cfg->base_va))
+		|| (va < ctx_cfg->base_va))) {
+		return -EFAULT;
+	}
+
+	/*
+	 * From the translation tables point of view, the VA is actually an
+	 * offset with regards to the base address of the VA space, so before
+	 * using a VA, we need to extract the base VA from it.
+	 */
+	page_va = va - ctx_cfg->base_va;
+	page_va &= ~PAGE_SIZE_MASK; /* Page address of the VA address passed. */
+
+	table = find_xlat_last_table(page_va, ctx, &level);
+
+	if (table == NULL) {
+		WARN("Address 0x%lx is not mapped.\n", va);
+		return -EFAULT;
+	}
+
+	/* Maximum number of entries used by this table. */
+	if (level == ctx_cfg->base_level) {
+		retval->entries = ctx->tbls->max_base_table_entries;
+	} else {
+		retval->entries = XLAT_TABLE_ENTRIES;
+	}
+
+	retval->table = table;
+	retval->level = level;
+	retval->base_va = ctx_cfg->base_va;
+
+	return 0;
+}
+
+/*
+ * This function finds the descriptor entry on a table given the corresponding
+ * table entry structure and the VA for that descriptor.
+ *
+ * If va is not mapped by the table pointed by entry, it returns NULL.
+ *
+ * For simplicity and as long as va belongs to the VA space owned by the
+ * translation context, this function will not take into consideration holes
+ * on the table pointed by entry either because the address is not mapped by
+ * the caller or left as INVALID_DESC for future dynamic mapping.
+ */
+uint64_t *xlat_get_pte_from_table(const struct xlat_table_entry * const entry,
+				    const uintptr_t va)
+{
+	unsigned int index;
+	uint64_t *table;
+	uintptr_t va_offset;
+
+	assert(entry != NULL);
+
+	if (va < entry->base_va) {
+		return NULL;
+	}
+
+	/*
+	 * From the translation tables point of view, the VA is actually an
+	 * offset with regards to the base address of the VA space, so before
+	 * using a VA, we need to extract the base VA from it.
+	 */
+
+	va_offset = va - entry->base_va;
+	table = entry->table;
+	index = XLAT_TABLE_IDX(va_offset, entry->level);
+
+	if (index >= entry->entries) {
+		return NULL;
+	}
+
+	return &table[index];
+}
diff --git a/plat/common/CMakeLists.txt b/plat/common/CMakeLists.txt
new file mode 100644
index 0000000..d59b130
--- /dev/null
+++ b/plat/common/CMakeLists.txt
@@ -0,0 +1,45 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+#
+# This cmake file is meant to be included from plat CMakelists.txt
+#
+add_library(rmm-plat-common)
+
+target_link_libraries(rmm-plat-common
+    PRIVATE rmm-lib)
+
+#
+# PLAT_CMN_CTX_MAX_XLAT_TABLES is allowed to be 0, and in case when there are
+# not enough tables the xlat tables creation will fail.
+#
+arm_config_option(
+    NAME PLAT_CMN_CTX_MAX_XLAT_TABLES
+    HELP "Maximum number of translation tables to be allocated for the static xlat tables"
+    DEFAULT 0x0
+    TYPE STRING)
+
+#
+# PLAT_CMN_MAX_MMAP_REGIONS is set a default value and in case when there are
+# not enough mmap regions allocated, adding regions to the xlat tables will
+# fail.
+#
+arm_config_option(
+    NAME PLAT_CMN_MAX_MMAP_REGIONS
+    HELP "Maximum number of static regions to be mapped in xlat tables"
+    DEFAULT 0x5
+    TYPE STRING)
+
+target_compile_definitions(rmm-plat-common
+    PUBLIC "PLAT_CMN_CTX_MAX_XLAT_TABLES=U(${PLAT_CMN_CTX_MAX_XLAT_TABLES})")
+
+target_compile_definitions(rmm-plat-common
+    PUBLIC "PLAT_CMN_MAX_MMAP_REGIONS=U(${PLAT_CMN_MAX_MMAP_REGIONS})")
+
+target_include_directories(rmm-plat-common
+    PUBLIC "include")
+
+target_sources(rmm-plat-common
+    PRIVATE "src/plat_common_init.c")
diff --git a/plat/common/include/plat_common.h b/plat/common/include/plat_common.h
new file mode 100644
index 0000000..c458248
--- /dev/null
+++ b/plat/common/include/plat_common.h
@@ -0,0 +1,17 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef PLAT_COMMON_H
+#define PLAT_COMMON_H
+
+/* Forward declaration */
+struct xlat_mmap_region;
+
+int plat_cmn_setup(unsigned long x0, unsigned long x1,
+		   unsigned long x2, unsigned long x3,
+		   struct xlat_mmap_region *plat_regions);
+int plat_cmn_warmboot_setup(void);
+
+#endif /* PLAT_COMMON_H */
diff --git a/plat/common/src/plat_common_init.c b/plat/common/src/plat_common_init.c
new file mode 100644
index 0000000..d9c7ff8
--- /dev/null
+++ b/plat/common/src/plat_common_init.c
@@ -0,0 +1,160 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <buffer.h>
+#include <cpuid.h>
+#include <debug.h>
+#include <gic.h>
+#include <import_sym.h>
+#include <rmm_el3_ifc.h>
+#include <sizes.h>
+#include <stdint.h>
+#include <xlat_contexts.h>
+#include <xlat_tables.h>
+
+IMPORT_SYM(uintptr_t, rmm_text_start,		RMM_CODE_START);
+IMPORT_SYM(uintptr_t, rmm_text_end,		RMM_CODE_END);
+IMPORT_SYM(uintptr_t, rmm_ro_start,		RMM_RO_START);
+IMPORT_SYM(uintptr_t, rmm_ro_end,		RMM_RO_END);
+IMPORT_SYM(uintptr_t, rmm_rw_start,		RMM_RW_START);
+IMPORT_SYM(uintptr_t, rmm_rw_end,		RMM_RW_END);
+
+/*
+ * Leave an invalid page between the end of RMM memory and the beginning
+ * of the shared buffer VA. This will help to detect any memory access
+ * underflow by RMM.
+ */
+#define RMM_SHARED_BUFFER_START	(RMM_RW_END + SZ_4K)
+/*
+ * Memory map REGIONS used for the RMM runtime (static mappings)
+ */
+#define RMM_CODE_SIZE		(RMM_CODE_END - RMM_CODE_START)
+#define RMM_RO_SIZE		(RMM_RO_END - RMM_RO_START)
+#define RMM_RW_SIZE		(RMM_RW_END - RMM_RW_START)
+
+#define RMM_CODE		MAP_REGION_FLAT(			\
+					RMM_CODE_START,			\
+					RMM_CODE_SIZE,			\
+					MT_CODE | MT_REALM)
+
+#define RMM_RO			MAP_REGION_FLAT(			\
+					RMM_RO_START,			\
+					RMM_RO_SIZE,			\
+					MT_RO_DATA | MT_REALM)
+
+#define RMM_RW			MAP_REGION_FLAT(			\
+					RMM_RW_START,			\
+					RMM_RW_SIZE,			\
+					MT_RW_DATA | MT_REALM)
+
+/*
+ * Some of the fields for the RMM_SHARED region will be populated
+ * at runtime.
+ */
+#define RMM_SHARED		MAP_REGION(				\
+					0U,				\
+					RMM_SHARED_BUFFER_START,	\
+					0U,				\
+					MT_RW_DATA | MT_REALM)
+
+
+XLAT_REGISTER_CONTEXT(runtime, VA_LOW_REGION, PLAT_CMN_MAX_MMAP_REGIONS,
+		      PLAT_CMN_CTX_MAX_XLAT_TABLES,
+		      VIRT_ADDR_SPACE_SIZE,
+		      "xlat_static_tables");
+
+/*
+ * Platform common cold boot setup for RMM.
+ *
+ * This function should only be invoked once during cold boot
+ * and is expected to setup architecture and platform components
+ * common for all PEs executing RMM.
+ * The xlat tables and GIC driver are initialized by this function.
+ */
+int plat_cmn_setup(unsigned long x0, unsigned long x1,
+		   unsigned long x2, unsigned long x3,
+		   struct xlat_mmap_region *plat_regions)
+{
+	int ret;
+
+	/* Initialize the RMM <-> EL3 interface */
+	ret = rmm_el3_ifc_init(x0, x1, x2, x3, RMM_SHARED_BUFFER_START);
+	if (ret != 0) {
+		ERROR("%s (%u): Failed to initialized RMM EL3 Interface\n",
+		      __func__, __LINE__);
+		return ret;
+	}
+
+	/*
+	 * xlat library might modify the memory mappings
+	 * to optimize it, so don't make this constant.
+	 */
+	struct xlat_mmap_region runtime_regions[] = {
+		RMM_CODE,
+		RMM_RO,
+		RMM_RW,
+		RMM_SHARED,
+		{0}
+	};
+
+	assert(plat_regions != NULL);
+
+	ret = xlat_mmap_add_ctx(&runtime_xlat_ctx, plat_regions, false);
+	if (ret != 0) {
+		ERROR("%s (%u): Failed to add platform regions to xlat mapping\n",
+			__func__, __LINE__);
+		return ret;
+	}
+
+	/* Setup the parameters of the shared area */
+	runtime_regions[3].base_pa = rmm_el3_ifc_get_shared_buf_pa();
+	runtime_regions[3].size = rmm_el3_ifc_get_shared_buf_size();
+
+	ret = xlat_mmap_add_ctx(&runtime_xlat_ctx, runtime_regions, true);
+	if (ret != 0) {
+		ERROR("%s (%u): Failed to add RMM common regions to xlat mapping\n",
+					__func__, __LINE__);
+		return ret;
+	}
+
+	ret = xlat_init_tables_ctx(&runtime_xlat_ctx);
+	if (ret != 0) {
+		ERROR("%s (%u): xlat initialization failed\n", __func__, __LINE__);
+		return ret;
+	}
+
+	/* Read supported GIC virtualization features and init GIC variables */
+	gic_get_virt_features();
+
+	return 0;
+}
+
+/*
+ * Local PE common platform setup for RMM.
+ *
+ * This function will only be invoked during
+ * warm boot and is expected to setup architecture and platform
+ * components local to a PE executing RMM.
+ */
+int plat_cmn_warmboot_setup(void)
+{
+	int ret;
+
+	/* Setup the MMU cfg for the low region (runtime context). */
+	ret = xlat_arch_setup_mmu_cfg(&runtime_xlat_ctx);
+	if (ret != 0) {
+		ERROR("%s (%u): Failed to setup xlat tables for CPU[%u]\n",
+					__func__, __LINE__, my_cpuid());
+		return ret;
+	}
+
+	/* Setup the MMU cfg for the slot buffer context (high region) */
+	slot_buf_setup_xlat();
+
+	VERBOSE("xlat tables configured for CPU[%u]\n", my_cpuid());
+	return 0;
+}
diff --git a/plat/fvp/CMakeLists.txt b/plat/fvp/CMakeLists.txt
new file mode 100644
index 0000000..19d3618
--- /dev/null
+++ b/plat/fvp/CMakeLists.txt
@@ -0,0 +1,23 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+add_library(rmm-fvp)
+
+add_subdirectory("${RMM_SOURCE_DIR}/drivers/pl011" ${RMM_BINARY_DIR}/drivers/pl011)
+add_subdirectory("${RMM_SOURCE_DIR}/plat/common" ${RMM_BINARY_DIR}/plat/common)
+
+target_link_libraries(rmm-fvp
+    PRIVATE rmm-driver-pl011
+            rmm-lib
+            rmm-plat-common)
+
+target_sources(rmm-fvp
+    PRIVATE "src/fvp_setup.c"
+            "src/fvp_granule.c")
+
+target_include_directories(rmm-fvp
+    PRIVATE "src/include")
+
+add_library(rmm-platform ALIAS rmm-fvp)
diff --git a/plat/fvp/src/fvp_granule.c b/plat/fvp/src/fvp_granule.c
new file mode 100644
index 0000000..7d52012
--- /dev/null
+++ b/plat/fvp/src/fvp_granule.c
@@ -0,0 +1,27 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <assert.h>
+#include <fvp_private.h>
+#include <utils_def.h>
+
+COMPILER_ASSERT(RMM_MAX_GRANULES >= FVP_NR_GRANULES);
+
+unsigned long plat_granule_addr_to_idx(unsigned long addr)
+{
+	if (!(GRANULE_ALIGNED(addr) &&
+				(addr < (FVP_DRAM0_BASE + FVP_DRAM0_SIZE))) &&
+				(addr >= FVP_DRAM0_BASE)) {
+		return UINT64_MAX;
+	}
+
+	return (addr - FVP_DRAM0_BASE) / GRANULE_SIZE;
+}
+
+unsigned long plat_granule_idx_to_addr(unsigned long idx)
+{
+	assert(idx < FVP_NR_GRANULES);
+	return FVP_DRAM0_BASE + (idx * GRANULE_SIZE);
+}
diff --git a/plat/fvp/src/fvp_setup.c b/plat/fvp/src/fvp_setup.c
new file mode 100644
index 0000000..8267fea
--- /dev/null
+++ b/plat/fvp/src/fvp_setup.c
@@ -0,0 +1,67 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+#include <debug.h>
+#include <fvp_private.h>
+#include <pl011.h>
+#include <plat_common.h>
+#include <sizes.h>
+#include <xlat_tables.h>
+
+#define FVP_RMM_UART		MAP_REGION_FLAT(			\
+					RMM_UART_ADDR,			\
+					SZ_4K,				\
+					MT_DEVICE | MT_RW | MT_REALM)
+
+/* TBD Initialize UART for early log */
+struct xlat_mmap_region plat_regions[] = {
+	FVP_RMM_UART,
+	{0}
+};
+
+/*
+ * Local platform setup for RMM.
+ *
+ * This function will only be invoked during
+ * warm boot and is expected to setup architecture and platform
+ * components local to a PE executing RMM.
+ */
+void plat_warmboot_setup(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3)
+{
+	/* Avoid MISRA C:2012-2.7 warnings */
+	(void)x0;
+	(void)x1;
+	(void)x2;
+	(void)x3;
+
+	if (plat_cmn_warmboot_setup() != 0) {
+		panic();
+	}
+}
+
+/*
+ * Global platform setup for RMM.
+ *
+ * This function will only be invoked once during cold boot
+ * and is expected to setup architecture and platform components
+ * common for all PEs executing RMM. The translation tables should
+ * be initialized by this function.
+ */
+void plat_setup(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3)
+{
+	/* Avoid MISRA C:2012-2.7 warnings */
+	(void)x0;
+	(void)x1;
+	(void)x2;
+	(void)x3;
+
+	uart_init(RMM_UART_ADDR, FVP_UART_CLK_IN_HZ, FVP_UART_BAUDRATE);
+
+	/* Initialize xlat table */
+	if (plat_cmn_setup(x0, x1, x2, x3, plat_regions) != 0) {
+		panic();
+	}
+
+	plat_warmboot_setup(x0, x1, x2, x3);
+}
diff --git a/plat/fvp/src/include/fvp_private.h b/plat/fvp/src/include/fvp_private.h
new file mode 100644
index 0000000..fa03c61
--- /dev/null
+++ b/plat/fvp/src/include/fvp_private.h
@@ -0,0 +1,25 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef FVP_PRIVATE_H
+#define FVP_PRIVATE_H
+
+/* Default number of CPUs per cluster */
+#define	FVP_MAX_CPUS_PER_CLUSTER	4
+
+/* Default number of threads per CPU */
+#define	FVP_MAX_PE_PER_CPU		1
+
+#define FVP_UART_BAUDRATE		115200
+#define FVP_UART_CLK_IN_HZ		14745600
+
+/* Base address and size for the DRAM0 (allocating 2GB of space) */
+#define FVP_DRAM0_BASE			UL(0x80000000)
+#define FVP_DRAM0_SIZE			UL(0x80000000)
+
+/* Total number of granules on the current platform */
+#define FVP_NR_GRANULES			(FVP_DRAM0_SIZE/GRANULE_SIZE)
+
+#endif /* FVP_PRIVATE_H */
diff --git a/plat/host/CMakeLists.txt b/plat/host/CMakeLists.txt
new file mode 100644
index 0000000..62fe84b
--- /dev/null
+++ b/plat/host/CMakeLists.txt
@@ -0,0 +1,8 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+add_subdirectory("../common" ${RMM_BINARY_DIR}/plat/common)
+add_subdirectory("common")
+add_subdirectory("host_build")
diff --git a/plat/host/common/CMakeLists.txt b/plat/host/common/CMakeLists.txt
new file mode 100644
index 0000000..5bbd41a
--- /dev/null
+++ b/plat/host/common/CMakeLists.txt
@@ -0,0 +1,18 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+add_library(rmm-host-common)
+
+target_link_libraries(rmm-host-common
+    PRIVATE  rmm-plat-common
+             rmm-lib)
+
+target_sources(rmm-host-common
+    PRIVATE "src/host_harness_cmn.c"
+            "src/host_platform_api_cmn.c"
+            "src/host_utils.c")
+
+target_include_directories(rmm-host-common
+    PUBLIC "include")
diff --git a/plat/host/common/include/host_defs.h b/plat/host/common/include/host_defs.h
new file mode 100644
index 0000000..b7d6340
--- /dev/null
+++ b/plat/host/common/include/host_defs.h
@@ -0,0 +1,17 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef HOST_DEFS_H
+#define HOST_DEFS_H
+
+#include <utils_def.h>
+
+/* Allocate 1GB of space to be used as physical granules */
+#define HOST_MEM_SIZE			UL(0x40000000)
+
+/* Total number of granules on the current platform */
+#define HOST_NR_GRANULES		(HOST_MEM_SIZE/GRANULE_SIZE)
+
+#endif /* HOST_DEFS_H */
diff --git a/plat/host/common/include/host_utils.h b/plat/host/common/include/host_utils.h
new file mode 100644
index 0000000..77a3b5e
--- /dev/null
+++ b/plat/host/common/include/host_utils.h
@@ -0,0 +1,108 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef HOST_UTILS_H
+#define HOST_UTILS_H
+
+#include <types.h>
+
+/***********************************************************************
+ * Utility functions to be used across different host platform variants.
+ **********************************************************************/
+
+/* Maximum number of sysregs for which we can install callbacks */
+#define SYSREG_MAX_CBS		(10U)
+
+/* Maximum size allowed for a sysreg name */
+#define MAX_SYSREG_NAME_LEN	(25U)
+
+/*
+ * Callback prototype invoked when a sysreg is read.
+ *
+ * Arguments:
+ *	reg - Pointer to the emulated register
+ *
+ * Returns:
+ *	Value read from the emulated sysreg
+ */
+typedef u_register_t (*rd_cb_t)(u_register_t *reg);
+
+/*
+ * Callback prototype invoked when a sysreg is written.
+ *
+ * Arguments:
+ *	val - Value to be written to the sysreg
+ *	reg - Pointer to the emulated sysreg
+ *
+ * Returns:
+ *	Void
+ */
+typedef void (*wr_cb_t)(u_register_t val, u_register_t *reg);
+
+/*
+ * Structure to hold the callback pointers and value of the emulated sysreg.
+ */
+struct sysreg_cb {
+	char sysreg[MAX_SYSREG_NAME_LEN + 1U];
+	rd_cb_t rd_cb;
+	wr_cb_t wr_cb;
+	u_register_t value;
+};
+
+/*
+ * Return the callbacks for a given sysreg or NULL
+ * if no callbacks are found.
+ */
+struct sysreg_cb *host_util_get_sysreg_cb(char *name);
+
+/*
+ * Setup callbacks for sysreg read and write operations.
+ *
+ * This API allows to setup callbacks for each sysreg to be called upon
+ * read or write operations. This allows to control what to return on
+ * a read or how to process a write.
+ *
+ * Argsuments:
+ *	name - String containing the name of the sysreg. The name of
+ *	       the sysreg cannot exceed MAX_SYSREG_NAME_LEN (excluding
+ *	       the terminating null character) or it will be truncated.
+ *	rd_cb - Callback to be invoked on a read operation.
+ *	wr_cb - Callback to be invoked on a write operation.
+ *	init - Value used as reset value for the sysreg.
+ *
+ * Returns:
+ *	0 on success or a negative error code otherwise.
+ */
+int host_util_set_sysreg_cb(char *name, rd_cb_t rd_cb, wr_cb_t wr_cb,
+			    u_register_t init);
+
+/*
+ * Setup generic callbacks for sysreg read and write operations.
+ *
+ * This API allows to setup generic callbacks for each sysreg to be called upon
+ * read or write operations.
+ *
+ * Arguments:
+ *	name - String containing the name of the sysreg. The name of
+ *	       the sysreg cannot exceed MAX_SYSREG_NAME_LEN (excluding
+ *	       the terminating null character) or it will be truncated.
+ *	init - Value used as reset value for the sysreg.
+ *
+ * Returns:
+ *	0 on success or a negative error code otherwise.
+ */
+int host_util_set_default_sysreg_cb(char *name, u_register_t init);
+
+/*
+ * Clear the list of sysreg callbacks.
+ */
+void host_util_reset_all_sysreg_cb(void);
+
+/*
+ * Return the configured address for the granule base.
+ */
+unsigned long host_util_get_granule_base(void);
+
+#endif /* HOST_UTILS_H */
diff --git a/plat/host/common/src/host_harness_cmn.c b/plat/host/common/src/host_harness_cmn.c
new file mode 100644
index 0000000..0a4cf05
--- /dev/null
+++ b/plat/host/common/src/host_harness_cmn.c
@@ -0,0 +1,110 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <arch.h>
+#include <host_utils.h>
+#include <spinlock.h>
+#include <string.h>
+
+bool host_memcpy_ns_read(void *dest, const void *ns_src, unsigned long size)
+{
+	(void)memcpy(dest, ns_src, size);
+	return true;
+}
+
+bool host_memcpy_ns_write(void *ns_dest, const void *src, unsigned long size)
+{
+	(void)memcpy(ns_dest, src, size);
+	return true;
+}
+
+unsigned long host_monitor_call(unsigned long id,
+			unsigned long arg0,
+			unsigned long arg1,
+			unsigned long arg2,
+			unsigned long arg3,
+			unsigned long arg4,
+			unsigned long arg5)
+{
+	/* Avoid MISRA C:2102-2.7 warnings */
+	(void)id;
+	(void)arg0;
+	(void)arg1;
+	(void)arg2;
+	(void)arg3;
+	(void)arg4;
+	(void)arg5;
+	return 0UL;
+}
+
+void host_monitor_call_with_res(unsigned long id,
+			unsigned long arg0,
+			unsigned long arg1,
+			unsigned long arg2,
+			unsigned long arg3,
+			unsigned long arg4,
+			unsigned long arg5,
+			struct smc_result *res)
+{
+	/* Avoid MISRA C:2102-2.7 warnings */
+	(void)id;
+	(void)arg0;
+	(void)arg1;
+	(void)arg2;
+	(void)arg3;
+	(void)arg4;
+	(void)arg5;
+	(void)res;
+}
+
+int host_run_realm(unsigned long *regs)
+{
+	/* Return an arbitrary exception */
+	return ARM_EXCEPTION_SYNC_LEL;
+}
+
+void host_spinlock_acquire(spinlock_t *l)
+{
+	l->val = 1;
+}
+
+void host_spinlock_release(spinlock_t *l)
+{
+	l->val = 0;
+}
+
+u_register_t host_read_sysreg(char *reg_name)
+{
+	struct sysreg_cb *callbacks = host_util_get_sysreg_cb(reg_name);
+
+	/*
+	 * Return 0UL as default value for registers which do not have
+	 * a read callback installed.
+	 */
+	if (callbacks == NULL) {
+		return 0UL;
+	}
+
+	if (callbacks->rd_cb == NULL) {
+		return 0UL;
+	}
+
+	return callbacks->rd_cb(&callbacks->value);
+}
+
+void host_write_sysreg(char *reg_name, u_register_t v)
+{
+	struct sysreg_cb *callbacks = host_util_get_sysreg_cb(reg_name);
+
+	/*
+	 * Ignore the write if the register does not have a write
+	 * callback installed.
+	 */
+	if (callbacks != NULL) {
+		if (callbacks->wr_cb != NULL) {
+			callbacks->wr_cb(v, &callbacks->value);
+		}
+	}
+}
diff --git a/plat/host/common/src/host_platform_api_cmn.c b/plat/host/common/src/host_platform_api_cmn.c
new file mode 100644
index 0000000..a5cf7e0
--- /dev/null
+++ b/plat/host/common/src/host_platform_api_cmn.c
@@ -0,0 +1,75 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <debug.h>
+#include <host_defs.h>
+#include <host_utils.h>
+#include <plat_common.h>
+#include <stdint.h>
+#include <xlat_tables.h>
+
+COMPILER_ASSERT(RMM_MAX_GRANULES >= HOST_NR_GRANULES);
+
+/* No regions to add for host */
+struct xlat_mmap_region plat_regions[] = {
+	{0}
+};
+
+/*
+ * Local platform setup for RMM.
+ *
+ * This function will only be invoked during
+ * warm boot and is expected to setup architecture and platform
+ * components local to a PE executing RMM.
+ */
+void plat_warmboot_setup(uint64_t x0, uint64_t x1,
+			 uint64_t x2, uint64_t x3)
+{
+	/* Avoid MISRA C:2102-2.7 warnings */
+	(void)x0;
+	(void)x1;
+	(void)x2;
+	(void)x3;
+
+	if (plat_cmn_warmboot_setup() != 0) {
+		panic();
+	}
+}
+
+/*
+ * Global platform setup for RMM.
+ *
+ * This function will only be invoked once during cold boot
+ * and is expected to setup architecture and platform components
+ * common for all PEs executing RMM. The translation tables should
+ * be initialized by this function.
+ */
+void plat_setup(uint64_t x0, uint64_t x1,
+		uint64_t x2, uint64_t x3)
+{
+	/* Initialize xlat table */
+	if (plat_cmn_setup(x0, x1, x2, x3, plat_regions) != 0) {
+		panic();
+	}
+
+	plat_warmboot_setup(x0, x1, x2, x3);
+}
+
+unsigned long plat_granule_addr_to_idx(unsigned long addr)
+{
+	if (!(GRANULE_ALIGNED(addr) &&
+		(addr < (host_util_get_granule_base() + HOST_MEM_SIZE))) &&
+		(addr >= host_util_get_granule_base())) {
+		return UINT64_MAX;
+	}
+
+	return (addr - host_util_get_granule_base()) / GRANULE_SIZE;
+}
+
+unsigned long plat_granule_idx_to_addr(unsigned long idx)
+{
+	assert(idx < HOST_NR_GRANULES);
+	return host_util_get_granule_base() + (idx * GRANULE_SIZE);
+}
diff --git a/plat/host/common/src/host_utils.c b/plat/host/common/src/host_utils.c
new file mode 100644
index 0000000..f1f9103
--- /dev/null
+++ b/plat/host/common/src/host_utils.c
@@ -0,0 +1,94 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <debug.h>
+#include <errno.h>
+#include <host_defs.h>
+#include <host_utils.h>
+#include <plat_common.h>
+#include <string.h>
+#include <xlat_tables.h>
+
+static struct sysreg_cb callbacks[SYSREG_MAX_CBS];
+static unsigned int installed_cb_idx;
+
+/*
+ * Allocate memory to emulate physical memory to initialize the
+ * granule library.
+ */
+static unsigned char granules_buffer[HOST_MEM_SIZE] __aligned(GRANULE_SIZE);
+
+/*
+ * Generic callback to access a sysreg for reading.
+ */
+static u_register_t sysreg_rd_cb(u_register_t *reg)
+{
+	return *reg;
+}
+
+/*
+ * Generic callback to access a sysreg for writing.
+ */
+static void sysreg_wr_cb(u_register_t val, u_register_t *reg)
+{
+	*reg = val;
+}
+
+struct sysreg_cb *host_util_get_sysreg_cb(char *name)
+{
+	for (unsigned int i = 0U; i < SYSREG_MAX_CBS; i++) {
+		if (strncmp(name, &callbacks[i].sysreg[0],
+			    MAX_SYSREG_NAME_LEN) == 0) {
+			return &callbacks[i];
+		}
+	}
+
+	return (struct sysreg_cb *)NULL;
+}
+
+int host_util_set_sysreg_cb(char *name, rd_cb_t rd_cb, wr_cb_t wr_cb,
+			    u_register_t init)
+{
+	if (installed_cb_idx < SYSREG_MAX_CBS) {
+		callbacks[installed_cb_idx].rd_cb = rd_cb;
+		callbacks[installed_cb_idx].wr_cb = wr_cb;
+		callbacks[installed_cb_idx].value = init;
+
+		(void)strncpy(&(callbacks[installed_cb_idx].sysreg[0]),
+			      &name[0], MAX_SYSREG_NAME_LEN);
+
+		/*
+		 * Add a string termination character in case the
+		 * name were truncated.
+		 */
+		callbacks[installed_cb_idx].sysreg[MAX_SYSREG_NAME_LEN] = '\0';
+
+		++installed_cb_idx;
+
+		return 0;
+	}
+
+	return -ENOMEM;
+}
+
+void host_util_reset_all_sysreg_cb(void)
+{
+
+	(void)memset((void *)callbacks, 0,
+		     sizeof(struct sysreg_cb) * SYSREG_MAX_CBS);
+
+	installed_cb_idx = 0U;
+}
+
+int host_util_set_default_sysreg_cb(char *name, u_register_t init)
+{
+	return host_util_set_sysreg_cb(name, &sysreg_rd_cb,
+				     &sysreg_wr_cb, init);
+}
+
+unsigned long host_util_get_granule_base(void)
+{
+	return (unsigned long)granules_buffer;
+}
diff --git a/plat/host/host_build/CMakeLists.txt b/plat/host/host_build/CMakeLists.txt
new file mode 100644
index 0000000..7611598
--- /dev/null
+++ b/plat/host/host_build/CMakeLists.txt
@@ -0,0 +1,16 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+add_library(rmm-plat-host_build)
+
+target_link_libraries(rmm-plat-host_build
+    PRIVATE rmm-lib
+            rmm-host-common)
+
+target_sources(rmm-plat-host_build
+    PRIVATE "src/host_setup.c"
+            "src/host_harness.c")
+
+add_library(rmm-platform ALIAS rmm-plat-host_build)
diff --git a/plat/host/host_build/src/host_harness.c b/plat/host/host_build/src/host_harness.c
new file mode 100644
index 0000000..85394ff
--- /dev/null
+++ b/plat/host/host_build/src/host_harness.c
@@ -0,0 +1,21 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <buffer.h>
+#include <host_harness.h>
+
+void *host_buffer_arch_map(enum buffer_slot slot,
+			    unsigned long addr, bool ns)
+{
+	(void)slot;
+	(void)ns;
+
+	return (void *)addr;
+}
+
+void host_buffer_arch_unmap(void *buf)
+{
+	(void)buf;
+}
diff --git a/plat/host/host_build/src/host_setup.c b/plat/host/host_build/src/host_setup.c
new file mode 100644
index 0000000..ead28aa
--- /dev/null
+++ b/plat/host/host_build/src/host_setup.c
@@ -0,0 +1,92 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <arch.h>
+#include <debug.h>
+#include <gic.h>
+#include <host_utils.h>
+#include <platform_api.h>
+#include <rmm_el3_ifc.h>
+#include <stdint.h>
+#include <xlat_tables.h>
+
+#define RMM_EL3_IFC_ABI_VERSION		(RMM_EL3_IFC_SUPPORTED_VERSION)
+#define RMM_EL3_MAX_CPUS		(1U)
+
+/*
+ * Define and set the Boot Interface arguments.
+ */
+static unsigned char el3_rmm_shared_buffer[PAGE_SIZE] __aligned(PAGE_SIZE);
+
+/*
+ * Create a basic boot manifest.
+ */
+static struct rmm_core_manifest *boot_manifest =
+			(struct rmm_core_manifest *)el3_rmm_shared_buffer;
+
+/*
+ * Performs some initialization needed before RMM can be ran, such as
+ * setting up callbacks for sysreg access.
+ */
+static void setup_sysreg_and_boot_manifest(void)
+{
+	/*
+	 * Initialize ID_AA64MMFR0_EL1 with a physical address
+	 * range of 48 bits (PARange bits set to 0b0101)
+	 */
+	(void)host_util_set_default_sysreg_cb("id_aa64mmfr0_el1",
+				INPLACE(ID_AA64MMFR0_EL1_PARANGE, 5UL));
+
+	/*
+	 * Initialize ICH_VTR_EL2 with 6 preemption bits.
+	 * (PREbits is equal number of preemption bits minus one)
+	 */
+	(void)host_util_set_default_sysreg_cb("ich_vtr_el2",
+				INPLACE(ICH_VTR_EL2_PRE_BITS, 5UL));
+
+	/* SCTLR_EL2 is reset to zero */
+	(void)host_util_set_default_sysreg_cb("sctlr_el2", 0UL);
+
+	/* Initialize the boot manifest */
+	boot_manifest->version = RMM_EL3_IFC_SUPPORTED_VERSION;
+	boot_manifest->plat_data = (uintptr_t)NULL;
+}
+
+/*
+ * Function to emulate the MMU enablement for the fake_host architecture.
+ */
+static void enable_fake_host_mmu(void)
+{
+	write_sctlr_el2(SCTLR_EL2_WXN | SCTLR_EL2_M);
+}
+
+void rmm_main(void);
+
+int main(int argc, char *argv[])
+{
+	(void)argc;
+	(void)argv;
+
+	setup_sysreg_and_boot_manifest();
+
+	VERBOSE("RMM: Beginning of Fake Host execution\n");
+
+	plat_setup(0UL,
+		   RMM_EL3_IFC_ABI_VERSION,
+		   RMM_EL3_MAX_CPUS,
+		   (uintptr_t)&el3_rmm_shared_buffer);
+
+	/*
+	 * Enable the MMU. This is needed as some initialization code
+	 * called by rmm_main() asserts that the mmu is enabled.
+	 */
+	enable_fake_host_mmu();
+
+	rmm_main();
+
+	VERBOSE("RMM: Fake Host execution completed\n");
+
+	return 0;
+}
diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt
new file mode 100644
index 0000000..4c7fbfa
--- /dev/null
+++ b/runtime/CMakeLists.txt
@@ -0,0 +1,108 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+include(ArmTargetLinkerScript)
+
+add_executable(rmm-runtime)
+
+target_link_options(rmm-runtime
+    PRIVATE "-Wl,-Map=$<TARGET_FILE:rmm-runtime>.map")
+
+arm_config_option(
+    NAME RSI_LOG_LEVEL
+    HELP "Log level to apply for RSI calls (0 - 50)"
+    TYPE STRING
+    DEFAULT 40)
+
+target_compile_definitions(rmm-runtime
+    PRIVATE "RSI_LOG_LEVEL=${RSI_LOG_LEVEL}")
+
+arm_config_option(
+    NAME RMM_NUM_PAGES_PER_STACK
+    HELP "Number of pages to use per CPU stack"
+    TYPE STRING
+    DEFAULT 3
+    ADVANCED)
+
+target_compile_definitions(rmm-runtime
+    PRIVATE "RMM_NUM_PAGES_PER_STACK=${RMM_NUM_PAGES_PER_STACK}")
+
+target_link_libraries(rmm-runtime
+    PRIVATE rmm-lib
+            rmm-platform)
+
+target_include_directories(rmm-runtime
+    PRIVATE "include")
+
+if(NOT RMM_ARCH STREQUAL fake_host)
+    target_sources(rmm-runtime
+        PRIVATE "core/aarch64/entry.S"
+                "core/aarch64/head.S"
+                "core/aarch64/helpers.S"
+                "core/aarch64/ns_access.S"
+                "core/aarch64/run-asm.S"
+                "core/aarch64/vectors.S")
+else()
+    target_sources(rmm-runtime
+        PRIVATE "core/fake_host/runtime_core_stub.c")
+endif()
+
+target_sources(rmm-runtime
+    PRIVATE "core/exit.c"
+            "core/handler.c"
+            "core/init.c"
+            "core/inject_exp.c"
+            "core/run.c"
+            "core/sysregs.c"
+            "core/vmid.c")
+
+target_sources(rmm-runtime
+    PRIVATE "rmi/feature.c"
+            "rmi/granule.c"
+            "rmi/realm.c"
+            "rmi/rec.c"
+            "rmi/rtt.c"
+            "rmi/run.c"
+            "rmi/system.c")
+
+target_sources(rmm-runtime
+    PRIVATE "rsi/config.c"
+            "rsi/host_call.c"
+            "rsi/logger.c"
+            "rsi/memory.c"
+            "rsi/psci.c"
+            "rsi/realm_ipa_helper.c"
+            "rsi/realm_attest.c"
+            "rsi/system.c")
+
+arm_config_option(
+    NAME RMM_MAX_SIZE
+    HELP "Maximum size for RMM image"
+    TYPE STRING
+    DEFAULT 0x0
+    ADVANCED)
+
+if(RMM_MAX_SIZE EQUAL 0x0)
+    message(FATAL_ERROR "RMM_MAX_SIZE is not initialized")
+endif()
+
+if(NOT RMM_ARCH STREQUAL fake_host)
+    arm_target_linker_script(rmm-runtime "linker.lds")
+
+    set_target_properties(rmm-runtime-lds
+        PROPERTIES COMPILE_DEFINITIONS "__LINKER__")
+
+    set_property(TARGET rmm-runtime-lds APPEND
+        PROPERTY COMPILE_DEFINITIONS "GRANULE_SIZE=UL(${GRANULE_SIZE})")
+
+    set_property(TARGET rmm-runtime-lds APPEND
+        PROPERTY COMPILE_DEFINITIONS "MAX_CPUS=UL(${MAX_CPUS})")
+
+    set_property(TARGET rmm-runtime-lds APPEND
+        PROPERTY COMPILE_DEFINITIONS "RMM_MAX_SIZE=UL(${RMM_MAX_SIZE})")
+
+    set_property(TARGET rmm-runtime-lds APPEND
+        PROPERTY COMPILE_DEFINITIONS "RMM_NUM_PAGES_PER_STACK=UL(${RMM_NUM_PAGES_PER_STACK})")
+endif()
diff --git a/runtime/core/aarch64/entry.S b/runtime/core/aarch64/entry.S
new file mode 100644
index 0000000..5b557d6
--- /dev/null
+++ b/runtime/core/aarch64/entry.S
@@ -0,0 +1,44 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <asm_macros.S>
+#include <smc.h>
+
+.globl rmm_handler
+
+func rmm_handler
+	/*
+	 * Save Link Register and X4, as per SMCCC v1.2 its value
+	 * must be preserved unless it contains result, as specified
+	 * in the function definition.
+	 */
+	stp	x4, lr, [sp, #-16]!
+
+	/*
+	 * Zero the space for X0-X3 in the smc_result structure
+	 * and pass its address as the last argument.
+	 */
+	stp	xzr, xzr, [sp, #-16]!
+	stp	xzr, xzr, [sp, #-16]!
+	mov	x7, sp
+
+	bl	handle_ns_smc
+
+	/*
+	 * Copy command output values back to caller. Since this is
+	 * done through SMC, X0 is used as the FID, and X1-X5 contain
+	 * the values of X0-X4 copied from the smc_result structure.
+	 */
+	ldr	x0, =SMC_RMM_REQ_COMPLETE
+	ldp	x1, x2, [sp], #16
+	ldp	x3, x4, [sp], #16
+	ldp	x5, lr, [sp], #16
+
+	smc	#0
+
+	/* Continue the rmm handling loop */
+	b	rmm_handler
+endfunc rmm_handler
diff --git a/runtime/core/aarch64/head.S b/runtime/core/aarch64/head.S
new file mode 100644
index 0000000..16d3dac
--- /dev/null
+++ b/runtime/core/aarch64/head.S
@@ -0,0 +1,175 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <arch.h>
+#include <asm_macros.S>
+#include <rmm_el3_ifc.h>
+#include <sizes.h>
+#include <smc.h>
+#include <xlat_tables.h>
+
+#define RMM_STACK_SIZE		(SZ_4K * RMM_NUM_PAGES_PER_STACK)
+
+.globl rmm_entry
+
+/*
+ * Initialize essential R-EL2 sysregs and C runtime environment
+ */
+.macro rmm_el2_init_env _vector, _is_cold_boot_flag, _warm_boot
+
+	/*
+	 * Stash arguments from previous boot stage
+	 */
+	mov	x20, x0
+	mov	x21, x1
+	mov	x22, x2
+	mov	x23, x3
+
+	mov_imm	x1, SCTLR_EL2_INIT
+	msr	sctlr_el2, x1
+
+	mov_imm	x2, HCR_EL2_INIT
+	msr	hcr_el2, x2
+
+	mov_imm	x3, CPTR_EL2_INIT
+	msr	cptr_el2, x3
+
+	mov_imm	x4, ICC_SRE_EL2_INIT
+	msr	ICC_SRE_EL2, x4
+
+	isb
+
+	ldr	x1, \_is_cold_boot_flag
+	cbz	x1, 1f
+
+	/*
+	 * As PIE is enabled, fixup the Global Descriptor Table only
+	 * once during cold boot. This is needed before accessing any
+	 * symbol addresses.
+	 */
+	bl	fixup_gdt_reloc
+
+	/* Cold and warm boot need to go through this path */
+1:
+	/* Early validate and init CPU Id */
+	mov	x0, x20
+	bl	rmm_el3_ifc_validate_cpuid
+
+	/* Setup stack on this CPU. X0 already contains the CPU Id */
+	bl	rmm_get_my_stack
+	mov	sp, x0
+
+	/*
+	 * Setup exception vectors
+	 */
+	adrp	x3, \_vector
+	add	x3, x3, :lo12:\_vector
+	msr	vbar_el2, x3
+	isb
+
+	/*
+	 * Find out whether this is a cold or warm boot
+	 */
+	ldr	x1, \_is_cold_boot_flag
+	cbnz	x1, 2f
+
+	/*
+	 * Restore arguments in preparation for the warm boot path
+	 */
+	mov	x0, x20
+	mov	x1, x21
+	mov	x2, x22
+	mov	x3, x23
+	b	\_warm_boot
+
+2:
+	/*
+	 * Update cold boot flag to indicate cold boot is done
+	 */
+	adr	x2, \_is_cold_boot_flag
+	str	xzr, [x2]
+
+	/*
+	 * Initialize BSS section
+	 */
+	adrp	x0, bss_start
+	add	x0, x0, :lo12:bss_start
+	adrp	x1, bss_end
+	add	x1, x1, :lo12:bss_end
+	sub	x2, x1, x0
+	mov	x1, xzr
+	bl	memset
+
+	/*
+	 * Restore args received from previous BL image
+	 */
+	mov	x0, x20
+	mov	x1, x21
+	mov	x2, x22
+	mov	x3, x23
+.endm
+
+/*
+ * This is the main entry for both Primary and secondary PEs.
+ */
+func rmm_entry
+
+	rmm_el2_init_env el2_vectors, cold_boot_flag, skip_to_warmboot
+
+	/*
+	 * Initialize platform specific peripherals like UART and
+	 * xlat tables.
+	 */
+	bl	plat_setup
+	bl	xlat_enable_mmu_el2
+
+	bl	rmm_main
+	b	smc_ret
+
+skip_to_warmboot:
+	/*
+	 * Carry on with the rest of the RMM warmboot path
+	 */
+	bl	plat_warmboot_setup
+	bl	xlat_enable_mmu_el2
+
+	bl	rmm_warmboot_main
+smc_ret:
+	mov_imm	x0, SMC_RMM_BOOT_COMPLETE
+	mov_imm	x1, E_RMM_BOOT_SUCCESS
+	smc	#0
+
+	/* Jump to the SMC handler post-init */
+	b	rmm_handler
+
+	/*
+	 * Flag to mark if it is a cold boot.
+	 * 1: cold boot, 0: warmboot.
+	 */
+.align 3
+cold_boot_flag:
+	.dword		1
+endfunc rmm_entry
+
+/*
+ * Return the stack for a given PE index in x0
+ * stack-start				     stack_end
+ *       o--sz---o....o--sz---o--sz---o--sz---o
+ *       ^\_____/^....^\_____/^\_____/^\_____/^
+ * id = (MAX_CPU-1)      2       1       0
+ * Arg : x0 - CPU position
+ * sz: RMM_STACK_SIZE bytes.
+ */
+func rmm_get_my_stack
+#ifndef NDEBUG
+	cmp	x0, #MAX_CPUS
+	ASM_ASSERT lo
+#endif
+	adrp	x1, stack_end
+	add	x1, x1, :lo12:stack_end
+	mov	x2, #(RMM_STACK_SIZE)	/* stack size per CPU */
+	umsubl	x0, w0, w2, x1
+	ret
+endfunc rmm_get_my_stack
diff --git a/runtime/core/aarch64/helpers.S b/runtime/core/aarch64/helpers.S
new file mode 100644
index 0000000..55bfc97
--- /dev/null
+++ b/runtime/core/aarch64/helpers.S
@@ -0,0 +1,127 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <arch.h>
+#include <asm_macros.S>
+#include <xlat_defs.h>
+
+	.globl	fixup_gdt_reloc
+
+/* ---------------------------------------------------------------------------
+ * Helper to fixup Global Descriptor table (GDT) and dynamic relocations
+ * (.rela.dyn) at runtime.
+ *
+ * This function is meant to be used when the firmware is compiled with -fpie
+ * and linked with -pie options. We rely on the linker script exporting
+ * appropriate markers for start and end of the section. For Global Offset
+ * Table (GOT), we expect 'rmm_got_start' and 'rmm_got_end' symbols to be
+ * defined. Similarly for *.rela.dyn, we expect rmm_rela_start and rmm_rela_end
+ * to be defined. We also expect `rmm_base` and `rmm_end` symbols to be
+ * defined by the linker script and are 4KB aligned. The RMM should be
+ * statically linked to start at 0x0.
+ *
+ * Clobber list: x0 to x7.
+ * ---------------------------------------------------------------------------
+ */
+
+/* Relocation codes */
+#define	R_AARCH64_NONE		0
+#define	R_AARCH64_RELATIVE	1027
+
+func fixup_gdt_reloc
+	/* Lower Limit for fixup */
+	mov	x0, xzr
+	/* rmm_base and rmm_end are 4KB aligned hence adrp is enough */
+	adrp	x2, rmm_base
+	adrp	x1, rmm_end
+	/* Upper Limit for fixup (rmm_end - rmm_base) */
+	sub	x1, x1, x2
+
+	/*
+	 * Since RMM will be compiled to start at 0x0, the current
+         * PC relative `rmm_base` loaded in x2 will be the Diff(S)
+	 * to be applied to the fixups.
+	 */
+	cbz	x2, 4f	/* Diff(S) = 0. No relocation needed */
+
+	adrp	x6, rmm_got_start
+	add	x6, x6, :lo12:rmm_got_start
+	adrp	x7, rmm_got_end
+	add	x7, x7, :lo12:rmm_got_end
+
+	/*
+	 * GOT is an array of 64_bit addresses which must be fixed up as
+	 * new_addr = old_addr + Diff(S).
+	 * The new_addr is the address currently the binary is executing from
+	 * and old_addr is the address at compile time.
+	 */
+1:	ldr	x3, [x6]
+	/* Skip adding offset if address is < lower limit */
+	cmp	x3, x0
+	b.lo	2f
+
+	/* Skip adding offset if address is > upper limit */
+	cmp	x3, x1
+	b.hi	2f
+	add	x3, x3, x2
+	str	x3, [x6]
+
+2:	add	x6, x6, #8
+	cmp	x6, x7
+	b.lo	1b
+
+	/* Starting dynamic relocations */
+3:	adrp	x6, rmm_rela_start
+	add	x6, x6, :lo12:rmm_rela_start
+	adrp	x7, rmm_rela_end
+	add	x7, x7, :lo12:rmm_rela_end
+
+	/*
+	 * According to ELF-64 specification, the RELA data structure is as
+	 * follows:
+	 *	typedef struct {
+	 *		Elf64_Addr r_offset;
+	 *		Elf64_Xword r_info;
+	 *		Elf64_Sxword r_addend;
+	 *	} Elf64_Rela;
+	 *
+	 * r_offset is address of reference
+	 * r_info is symbol index and type of relocation (in this case
+	 * code 1027 which corresponds to R_AARCH64_RELATIVE).
+	 * r_addend is constant part of expression.
+	 *
+	 * Size of Elf64_Rela structure is 24 bytes.
+	 */
+
+1:	ldr	x3, [x6, #8]	/* r_info */
+	/* Skip R_AARCH64_NONE entry with code 0 */
+	cbz	x3, 2f
+
+#ifndef NDEBUG
+	/* Assert that the relocation type is R_AARCH64_RELATIVE */
+	cmp	x3, #R_AARCH64_RELATIVE
+	ASM_ASSERT eq
+#endif
+	ldr	x4, [x6, #16]	/* r_addend */
+
+	/* Skip adding offset if r_addend is < lower limit */
+	cmp	x4, x0
+	b.lo	2f
+
+	/* Skip adding offset if r_addend entry is > upper limit */
+	cmp	x4, x1
+	b.hi	2f
+
+	ldr	x3, [x6]	/* r_offset */
+	add	x4, x4, x2	/* Diff(S) + r_addend */
+	str	x4, [x3, x2]
+
+2:	add	x6, x6, #24
+	cmp	x6, x7
+	b.lo	1b
+
+4:
+	ret
+endfunc fixup_gdt_reloc
diff --git a/runtime/core/aarch64/ns_access.S b/runtime/core/aarch64/ns_access.S
new file mode 100644
index 0000000..b39d2ec
--- /dev/null
+++ b/runtime/core/aarch64/ns_access.S
@@ -0,0 +1,79 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <asm_macros.S>
+
+.section ".text"
+
+/*
+ * The following addresses are registered with the exception handler:
+ */
+.global ns_read
+.global ns_write
+
+.global memcpy_ns_read
+.global memcpy_ns_write
+.global ns_access_ret_0
+
+/*
+ * Copy data from NS into Realm memory.
+ * The function returns 1 if the copy succeeds.
+ * If the access to the NS memory generates a GPF, the exception handler
+ * returns to ns_access_ret_0 and 0 is returned to the caller.
+ * In case of failure (when 0 is returned), partial data may have been
+ * written to the destination buffer
+ *
+ * x0 - The address of buffer in Realm memory to write into
+ * x1 - The address of buffer in NS memory to read from.
+ * x2 - The number of bytes to read in bytes.
+ * All arguments must be aligned to 8 bytes.
+ */
+func memcpy_ns_read
+	cbz	x2, 2f
+	mov	x3, #0
+1:
+ns_read:
+	ldr	x4, [x1], #8
+	str	x4, [x0], #8
+	add	x3, x3, #8
+	cmp	x3, x2
+	bne	1b
+2:
+	mov	x0, #1
+	ret
+endfunc memcpy_ns_read
+
+/*
+ * Copy data from Realm into NS memory.
+ * The function returns 1 if the copy succeeds.
+ * If the access to the NS memory generates a GPF, the exception handler
+ * returns to ns_access_ret_0 and 0 is returned to the caller.
+ * In case of failure (when 0 is returned), partial data may have been
+ * written to the destination buffer
+ *
+ * x0 - The address of buffer in NS memory to write into
+ * x1 - The address of buffer in Realm memory to read from.
+ * x2 - The number of bytes to write.
+ * All arguments must be aligned to 8 bytes.
+ */
+func memcpy_ns_write
+	cbz	x2, 2f
+	mov	x3, #0
+1:
+	ldr	x4, [x1], #8
+ns_write:
+	str	x4, [x0], #8
+	add	x3, x3, #8
+	cmp	x3, x2
+	bne	1b
+2:
+	mov	x0, #1
+	ret
+endfunc memcpy_ns_write
+
+func ns_access_ret_0
+	mov	x0, #0
+	ret
+endfunc ns_access_ret_0
diff --git a/runtime/core/aarch64/run-asm.S b/runtime/core/aarch64/run-asm.S
new file mode 100644
index 0000000..7024b0e
--- /dev/null
+++ b/runtime/core/aarch64/run-asm.S
@@ -0,0 +1,103 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <asm_macros.S>
+#include <rec.h>
+#include <sve.h>
+
+.globl run_realm
+.globl realm_exit
+
+/*
+ * int run_realm(unsigned long *regs);
+ *
+ * Per the AAPCS we must preserve x19-x29, along with the SP. We may freely
+ * corrupt x0-18 and the flags, but need the LR to return to our caller.
+ */
+func run_realm
+	/* Push RMM registers to the stack */
+	sub	sp, sp, #(16 * 6)
+	stp	x19, x20, [sp, #(16 * 0)]
+	stp	x21, x22, [sp, #(16 * 1)]
+	stp	x23, x24, [sp, #(16 * 2)]
+	stp	x25, x26, [sp, #(16 * 3)]
+	stp	x27, x28, [sp, #(16 * 4)]
+	stp	x29, x30, [sp, #(16 * 5)]
+
+	/* Push rec pointer to the stack for realm_exit */
+	stp	x0, xzr, [sp, #-16]!
+
+	/* load realm GPRs (offsetof(rec, rec->regs[0]) == 0) */
+	ldp	x2,  x3,  [x0, #(16 * 1)]
+	ldp	x4,  x5,  [x0, #(16 * 2)]
+	ldp	x6,  x7,  [x0, #(16 * 3)]
+	ldp	x8,  x9,  [x0, #(16 * 4)]
+	ldp	x10, x11, [x0, #(16 * 5)]
+	ldp	x12, x13, [x0, #(16 * 6)]
+	ldp	x14, x15, [x0, #(16 * 7)]
+	ldp	x16, x17, [x0, #(16 * 8)]
+	ldp	x18, x19, [x0, #(16 * 9)]
+	ldp	x20, x21, [x0, #(16 * 10)]
+	ldp	x22, x23, [x0, #(16 * 11)]
+	ldp	x24, x25, [x0, #(16 * 12)]
+	ldp	x26, x27, [x0, #(16 * 13)]
+	ldp	x28, x29, [x0, #(16 * 14)]
+	ldr	x30,      [x0, #(16 * 15)]
+	ldp	x0,  x1,  [x0, #(16 * 0)]
+
+	eret
+	sb
+endfunc run_realm
+
+func realm_exit
+	/*
+	 * We come here with realm's x0 and x1 on the stack and exit_reason in
+	 * x0. See el2_vectors in runtime/core/aarch64/vectors.S.
+	 *
+	 * First, restore realm_gprs ptr to x1
+	 */
+
+	/* Recover the rec pointer */
+	ldr	x1, [sp, #16]
+
+	/* Store realm GPRs (offsetof(rec, rec->regs[0]) == 0) */
+	stp	x2,  x3,  [x1, #(16 * 1)]
+	stp	x4,  x5,  [x1, #(16 * 2)]
+	stp	x6,  x7,  [x1, #(16 * 3)]
+	stp	x8,  x9,  [x1, #(16 * 4)]
+	stp	x10, x11, [x1, #(16 * 5)]
+	stp	x12, x13, [x1, #(16 * 6)]
+	stp	x14, x15, [x1, #(16 * 7)]
+	stp	x16, x17, [x1, #(16 * 8)]
+	stp	x18, x19, [x1, #(16 * 9)]
+	stp	x20, x21, [x1, #(16 * 10)]
+	stp	x22, x23, [x1, #(16 * 11)]
+	stp	x24, x25, [x1, #(16 * 12)]
+	stp	x26, x27, [x1, #(16 * 13)]
+	stp	x28, x29, [x1, #(16 * 14)]
+	str	x30,      [x1, #(16 * 15)]
+
+	/* x0 and x1 as stored by el2_vectors */
+	ldp	x2, x3,	  [sp]
+	stp	x2, x3,   [x1, #(16 * 0)]
+
+	/* Move sp to the realm regs */
+	add	sp, sp, #32
+
+	/*
+	 * Restore the RMM registers from the stack
+	 * including the return address to return to
+	 * after calling run_realm().
+	 */
+	ldp	x19, x20, [sp, #(16 * 0)]
+	ldp	x21, x22, [sp, #(16 * 1)]
+	ldp	x23, x24, [sp, #(16 * 2)]
+	ldp	x25, x26, [sp, #(16 * 3)]
+	ldp	x27, x28, [sp, #(16 * 4)]
+	ldp	x29, x30, [sp, #(16 * 5)]
+	add	sp, sp, #(16 * 6)
+
+	ret
+endfunc realm_exit
diff --git a/runtime/core/aarch64/vectors.S b/runtime/core/aarch64/vectors.S
new file mode 100644
index 0000000..e5cbaf0
--- /dev/null
+++ b/runtime/core/aarch64/vectors.S
@@ -0,0 +1,107 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <arch.h>
+#include <asm_macros.S>
+
+	.section ".text"
+
+	.macro ventry_unused error_message
+	.balign	0x80
+	wfe
+	b	.-4
+	.endm
+
+	.macro ventry label
+		.balign	0x80
+		b	\label
+	.endm
+
+	// VBAR_EL3[10:0] are hardwired to 0, align vector address accordingly
+	.balign 0x800
+
+ENTRY(el2_vectors):
+	ventry_unused	exc_sync_sp0
+	ventry_unused	exc_irq_sp0
+	ventry_unused	exc_fiq_sp0
+	ventry_unused	exc_serror_sp0
+
+	ventry		el2_sync_cel
+	ventry_unused	exc_irq_spx
+	ventry_unused	exc_fiq_spx
+	ventry_unused	exc_serror_spx
+
+	ventry		el2_sync_lel
+	ventry		el2_irq_lel
+	ventry		el2_fiq_lel
+	ventry		el2_serror_lel
+
+	ventry_unused	exc_sync_lel_32
+	ventry_unused	exc_irq_lel_32
+	ventry_unused	exc_fiq_lel_32
+	ventry_unused	exc_serror_lel_32
+ENDPROC(el2_vectors)
+
+el2_sync_lel:
+	stp	x0, x1, [sp, #-16]!
+	mov	x0, #ARM_EXCEPTION_SYNC_LEL
+	b	realm_exit
+ENDPROC(el2_sync_lel)
+
+el2_irq_lel:
+	stp	x0, x1, [sp, #-16]!
+	mov	x0, #ARM_EXCEPTION_IRQ_LEL
+	b	realm_exit
+ENDPROC(el2_sync_lel)
+
+el2_fiq_lel:
+	stp	x0, x1, [sp, #-16]!
+	mov	x0, #ARM_EXCEPTION_FIQ_LEL
+	b	realm_exit
+ENDPROC(el2_sync_lel)
+
+el2_serror_lel:
+	stp	x0, x1, [sp, #-16]!
+	mov	x0, #ARM_EXCEPTION_SERROR_LEL
+	b	realm_exit
+ENDPROC(el2_serror_lel)
+
+el2_sync_cel:
+	stp	x0, x1, [sp, #-16]!
+	stp	x2, x3, [sp, #-16]!
+	stp	x4, x5, [sp, #-16]!
+	stp	x6, x7, [sp, #-16]!
+	stp	x8, x9, [sp, #-16]!
+	stp	x10, x11, [sp, #-16]!
+	stp	x12, x13, [sp, #-16]!
+	stp	x14, x15, [sp, #-16]!
+	stp	x16, x17, [sp, #-16]!
+	stp	x18, xzr, [sp, #-16]!
+	stp	x29, lr, [sp, #-16]!
+
+	bl	handle_rmm_trap
+
+	/*
+	 * If it doesn't panic the RMM, handle_rmm_trap
+	 * returns the new value of PC in x0.
+	 */
+	msr	elr_el2, x0
+
+	ldp	x29, lr, [sp], #16
+	ldp	x18, xzr, [sp], #16
+	ldp	x16, x17, [sp], #16
+	ldp	x14, x15, [sp], #16
+	ldp	x12, x13, [sp], #16
+	ldp	x10, x11, [sp], #16
+	ldp	x8, x9, [sp], #16
+	ldp	x6, x7, [sp], #16
+	ldp	x4, x5, [sp], #16
+	ldp	x2, x3, [sp], #16
+	ldp	x0, x1, [sp], #16
+
+	eret
+	sb
+
+ENDPROC(el2_sync_cel)
diff --git a/runtime/core/exit.c b/runtime/core/exit.c
new file mode 100644
index 0000000..b16fc5a
--- /dev/null
+++ b/runtime/core/exit.c
@@ -0,0 +1,759 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <arch.h>
+#include <arch_helpers.h>
+#include <attestation_token.h>
+#include <buffer.h>
+#include <esr.h>
+#include <exit.h>
+#include <fpu_helpers.h>
+#include <gic.h>
+#include <granule.h>
+#include <inject_exp.h>
+#include <memory_alloc.h>
+#include <psci.h>
+#include <realm.h>
+#include <realm_attest.h>
+#include <rec.h>
+#include <rsi-config.h>
+#include <rsi-handler.h>
+#include <rsi-host-call.h>
+#include <rsi-logger.h>
+#include <rsi-memory.h>
+#include <rsi-walk.h>
+#include <smc-rmi.h>
+#include <smc-rsi.h>
+#include <status.h>
+#include <sve.h>
+#include <sysreg_traps.h>
+#include <table.h>
+
+void save_fpu_state(struct fpu_state *fpu);
+void restore_fpu_state(struct fpu_state *fpu);
+
+static void system_abort(void)
+{
+	/*
+	 * TODO: report the abort to the EL3.
+	 * We need to establish the exact EL3 API first.
+	 */
+	assert(false);
+}
+
+static bool fixup_aarch32_data_abort(struct rec *rec, unsigned long *esr)
+{
+	unsigned long spsr = read_spsr_el2();
+
+	if ((spsr & SPSR_EL2_nRW_AARCH32) != 0UL) {
+		/*
+		 * mmio emulation of AArch32 reads/writes is not supported.
+		 */
+		*esr &= ~ESR_EL2_ABORT_ISV_BIT;
+		return true;
+	}
+	return false;
+}
+
+static unsigned long get_dabt_write_value(struct rec *rec, unsigned long esr)
+{
+	unsigned int rt = esr_srt(esr);
+
+	/* Handle xzr */
+	if (rt == 31U) {
+		return 0UL;
+	}
+	return rec->regs[rt] & access_mask(esr);
+}
+
+/*
+ * Returns 'true' if access from @rec to @addr is within the Protected IPA space.
+ */
+static bool access_in_rec_par(struct rec *rec, unsigned long addr)
+{
+	/*
+	 * It is OK to check only the base address of the access because:
+	 * - The Protected IPA space starts at address zero.
+	 * - The IPA width is below 64 bits, therefore the access cannot
+	 *   wrap around.
+	 */
+	return addr_in_rec_par(rec, addr);
+}
+
+/*
+ * Returns 'true' if the @ipa is in PAR and its RIPAS is 'empty'.
+ *
+ * @ipa must be aligned to the granule size.
+ */
+static bool ipa_is_empty(unsigned long ipa, struct rec *rec)
+{
+	unsigned long s2tte, *ll_table;
+	struct rtt_walk wi;
+	enum ripas ripas;
+	bool ret;
+
+	assert(GRANULE_ALIGNED(ipa));
+
+	if (!addr_in_rec_par(rec, ipa)) {
+		return false;
+	}
+	granule_lock(rec->realm_info.g_rtt, GRANULE_STATE_RTT);
+
+	rtt_walk_lock_unlock(rec->realm_info.g_rtt,
+			     rec->realm_info.s2_starting_level,
+			     rec->realm_info.ipa_bits,
+			     ipa, RTT_PAGE_LEVEL, &wi);
+
+	ll_table = granule_map(wi.g_llt, SLOT_RTT);
+	s2tte = s2tte_read(&ll_table[wi.index]);
+
+	if (s2tte_is_destroyed(s2tte)) {
+		ret = false;
+		goto out_unmap_ll_table;
+	}
+	ripas = s2tte_get_ripas(s2tte);
+	ret = (ripas == RMI_EMPTY);
+
+out_unmap_ll_table:
+	buffer_unmap(ll_table);
+	granule_unlock(wi.g_llt);
+	return ret;
+}
+
+static bool fsc_is_external_abort(unsigned long fsc)
+{
+	if (fsc == ESR_EL2_ABORT_FSC_SEA) {
+		return true;
+	}
+
+	if ((fsc >= ESR_EL2_ABORT_FSC_SEA_TTW_START) &&
+	    (fsc <= ESR_EL2_ABORT_FSC_SEA_TTW_END)) {
+		return true;
+	}
+
+	return false;
+}
+
+/*
+ * Handles Data/Instruction Aborts at a lower EL with External Abort fault
+ * status code (D/IFSC).
+ * Returns 'true' if the exception is the external abort and the `rec_exit`
+ * structure is populated, 'false' otherwise.
+ */
+static bool handle_sync_external_abort(struct rec *rec,
+				       struct rmi_rec_exit *rec_exit,
+				       unsigned long esr)
+{
+	unsigned long fsc = esr & ESR_EL2_ABORT_FSC_MASK;
+	unsigned long set = esr & ESR_EL2_ABORT_SET_MASK;
+
+	if (!fsc_is_external_abort(fsc)) {
+		return false;
+	}
+
+	switch (set) {
+	case ESR_EL2_ABORT_SET_UER:
+		/*
+		 * The recoverable SEA.
+		 * Inject the sync. abort into the Realm.
+		 * Report the exception to the host.
+		 */
+		inject_sync_idabort(ESR_EL2_ABORT_FSC_SEA);
+		/*
+		 * Fall through.
+		 */
+	case ESR_EL2_ABORT_SET_UEO:
+		/*
+		 * The restartable SEA.
+		 * Report the exception to the host.
+		 * The REC restarts the same instruction.
+		 */
+		rec_exit->esr = esr & ESR_NONEMULATED_ABORT_MASK;
+
+		/*
+		 * The value of the HPFAR_EL2 is not provided to the host as
+		 * it is undefined for external aborts.
+		 *
+		 * We also don't provide the content of FAR_EL2 because it
+		 * has no practical value to the host without the HPFAR_EL2.
+		 */
+		break;
+	case ESR_EL2_ABORT_SET_UC:
+		/*
+		 * The uncontainable SEA.
+		 * Fatal to the system.
+		 */
+		system_abort();
+		break;
+	default:
+		assert(false);
+	}
+
+	return true;
+}
+
+void emulate_stage2_data_abort(struct rec *rec,
+			       struct rmi_rec_exit *rec_exit,
+			       unsigned long rtt_level)
+{
+	unsigned long fipa = rec->regs[1];
+
+	assert(rtt_level <= RTT_PAGE_LEVEL);
+
+	/*
+	 * Setup Exception Syndrom Register to emulate a real data abort
+	 * and return to NS host to handle it.
+	 */
+	rec_exit->esr = (ESR_EL2_EC_DATA_ABORT |
+			(ESR_EL2_ABORT_FSC_TRANSLATION_FAULT_L0 + rtt_level));
+	rec_exit->far = 0UL;
+	rec_exit->hpfar = fipa >> HPFAR_EL2_FIPA_OFFSET;
+	rec_exit->exit_reason = RMI_EXIT_SYNC;
+}
+
+/*
+ * Returns 'true' if the abort is handled and the RMM should return to the Realm,
+ * and returns 'false' if the exception should be reported to the HS host.
+ */
+static bool handle_data_abort(struct rec *rec, struct rmi_rec_exit *rec_exit,
+			      unsigned long esr)
+{
+	unsigned long far = 0UL;
+	unsigned long hpfar = read_hpfar_el2();
+	unsigned long fipa = (hpfar & HPFAR_EL2_FIPA_MASK) << HPFAR_EL2_FIPA_OFFSET;
+	unsigned long write_val = 0UL;
+
+	if (handle_sync_external_abort(rec, rec_exit, esr)) {
+		/*
+		 * All external aborts are immediately reported to the host.
+		 */
+		return false;
+	}
+
+	/*
+	 * The memory access that crosses a page boundary may cause two aborts
+	 * with `hpfar_el2` values referring to two consecutive pages.
+	 *
+	 * Insert the SEA and return to the Realm if the granule's RIPAS is EMPTY.
+	 */
+	if (ipa_is_empty(fipa, rec)) {
+		inject_sync_idabort(ESR_EL2_ABORT_FSC_SEA);
+		return true;
+	}
+
+	if (fixup_aarch32_data_abort(rec, &esr) ||
+	    access_in_rec_par(rec, fipa)) {
+		esr &= ESR_NONEMULATED_ABORT_MASK;
+		goto end;
+	}
+
+	if (esr_is_write(esr)) {
+		write_val = get_dabt_write_value(rec, esr);
+	}
+
+	far = read_far_el2() & ~GRANULE_MASK;
+	esr &= ESR_EMULATED_ABORT_MASK;
+
+end:
+	rec_exit->esr = esr;
+	rec_exit->far = far;
+	rec_exit->hpfar = hpfar;
+	rec_exit->gprs[0] = write_val;
+
+	return false;
+}
+
+/*
+ * Returns 'true' if the abort is handled and the RMM should return to the Realm,
+ * and returns 'false' if the exception should be reported to the NS host.
+ */
+static bool handle_instruction_abort(struct rec *rec, struct rmi_rec_exit *rec_exit,
+				     unsigned long esr)
+{
+	unsigned long fsc = esr & ESR_EL2_ABORT_FSC_MASK;
+	unsigned long fsc_type = fsc & ~ESR_EL2_ABORT_FSC_LEVEL_MASK;
+	unsigned long hpfar = read_hpfar_el2();
+	unsigned long fipa = (hpfar & HPFAR_EL2_FIPA_MASK) << HPFAR_EL2_FIPA_OFFSET;
+
+	if (handle_sync_external_abort(rec, rec_exit, esr)) {
+		/*
+		 * All external aborts are immediately reported to the host.
+		 */
+		return false;
+	}
+
+	/*
+	 * Insert the SEA and return to the Realm if:
+	 * - The instruction abort is at an Unprotected IPA, or
+	 * - The granule's RIPAS is EMPTY
+	 */
+	if (!access_in_rec_par(rec, fipa) || ipa_is_empty(fipa, rec)) {
+		inject_sync_idabort(ESR_EL2_ABORT_FSC_SEA);
+		return true;
+	}
+
+	if (fsc_type != ESR_EL2_ABORT_FSC_TRANSLATION_FAULT) {
+		unsigned long far = read_far_el2();
+
+		/*
+		 * TODO: Should this ever happen, or is it an indication of an
+		 * internal consistency failure in the RMM which should lead
+		 * to a panic instead?
+		 */
+
+		ERROR("Unhandled instruction abort:\n");
+		ERROR("    FSC: %12s0x%02lx\n", " ", fsc);
+		ERROR("    FAR: %16lx\n", far);
+		ERROR("  HPFAR: %16lx\n", hpfar);
+		return false;
+	}
+
+	rec_exit->hpfar = hpfar;
+	rec_exit->esr = esr & ESR_NONEMULATED_ABORT_MASK;
+
+	return false;
+}
+
+/*
+ * Return 'false' if no IRQ is pending,
+ * return 'true' if there is an IRQ pending, and need to return to host.
+ */
+static bool check_pending_irq(void)
+{
+	unsigned long pending_irq;
+
+	pending_irq = read_isr_el1();
+
+	return (pending_irq != 0UL);
+}
+
+static void advance_pc(void)
+{
+	unsigned long pc = read_elr_el2();
+
+	write_elr_el2(pc + 4UL);
+}
+
+static void return_result_to_realm(struct rec *rec, struct smc_result result)
+{
+	rec->regs[0] = result.x[0];
+	rec->regs[1] = result.x[1];
+	rec->regs[2] = result.x[2];
+	rec->regs[3] = result.x[3];
+}
+
+/*
+ * Return 'true' if execution should continue in the REC, otherwise return
+ * 'false' to go back to the NS caller of REC.Enter.
+ */
+static bool handle_realm_rsi(struct rec *rec, struct rmi_rec_exit *rec_exit)
+{
+	bool ret_to_rec = true;	/* Return to Realm */
+	unsigned int function_id = rec->regs[0];
+
+	RSI_LOG_SET(rec->regs[1], rec->regs[2],
+		    rec->regs[3], rec->regs[4], rec->regs[5]);
+
+	if (!IS_SMC32_PSCI_FID(function_id) && !IS_SMC64_PSCI_FID(function_id)
+	    && !IS_SMC64_RSI_FID(function_id)) {
+
+		ERROR("Invalid RSI function_id = %x\n", function_id);
+		rec->regs[0] = SMC_UNKNOWN;
+		return true;
+	}
+
+	switch (function_id) {
+	case SMCCC_VERSION:
+		rec->regs[0] = SMCCC_VERSION_NUMBER;
+		break;
+	case SMC_RSI_ABI_VERSION:
+		rec->regs[0] = system_rsi_abi_version();
+		break;
+	case SMC32_PSCI_FID_MIN ... SMC32_PSCI_FID_MAX:
+	case SMC64_PSCI_FID_MIN ... SMC64_PSCI_FID_MAX: {
+		struct psci_result res;
+
+		res = psci_rsi(rec,
+			       function_id,
+			       rec->regs[1],
+			       rec->regs[2],
+			       rec->regs[3]);
+
+		if (!rec->psci_info.pending) {
+			rec->regs[0] = res.smc_res.x[0];
+			rec->regs[1] = res.smc_res.x[1];
+			rec->regs[2] = res.smc_res.x[2];
+			rec->regs[3] = res.smc_res.x[3];
+		}
+
+		if (res.hvc_forward.forward_psci_call) {
+			unsigned int i;
+
+			rec_exit->exit_reason = RMI_EXIT_PSCI;
+			rec_exit->gprs[0] = function_id;
+			rec_exit->gprs[1] = res.hvc_forward.x1;
+			rec_exit->gprs[2] = res.hvc_forward.x2;
+			rec_exit->gprs[3] = res.hvc_forward.x3;
+
+			for (i = 4U; i < REC_EXIT_NR_GPRS; i++) {
+				rec_exit->gprs[i] = 0UL;
+			}
+
+			advance_pc();
+			ret_to_rec = false;
+		}
+		break;
+	}
+	case SMC_RSI_ATTEST_TOKEN_INIT:
+		rec->regs[0] = handle_rsi_attest_token_init(rec);
+		break;
+	case SMC_RSI_ATTEST_TOKEN_CONTINUE: {
+		struct attest_result res;
+		attest_realm_token_sign_continue_start();
+		while (true) {
+			/*
+			 * Possible outcomes:
+			 *     if res.incomplete is true
+			 *         if IRQ pending
+			 *             check for pending IRQ and return to host
+			 *         else try a new iteration
+			 *     else
+			 *         if RTT table walk has failed,
+			 *             emulate data abort back to host
+			 *         otherwise
+			 *             return to realm because the token
+			 *             creation is complete or input parameter
+			 *             validation failed.
+			 */
+			handle_rsi_attest_token_continue(rec, &res);
+
+			if (res.incomplete) {
+				if (check_pending_irq()) {
+					rec_exit->exit_reason = RMI_EXIT_IRQ;
+					/* Return to NS host to handle IRQ. */
+					ret_to_rec = false;
+					break;
+				}
+			} else {
+				if (res.walk_result.abort) {
+					emulate_stage2_data_abort(
+						rec, rec_exit,
+						res.walk_result.rtt_level);
+					ret_to_rec = false; /* Exit to Host */
+					break;
+				}
+
+				/* Return to Realm */
+				return_result_to_realm(rec, res.smc_res);
+				break;
+			}
+		}
+		attest_realm_token_sign_continue_finish();
+		break;
+	}
+	case SMC_RSI_MEASUREMENT_READ:
+		rec->regs[0] = handle_rsi_read_measurement(rec);
+		break;
+	case SMC_RSI_MEASUREMENT_EXTEND:
+		rec->regs[0] = handle_rsi_extend_measurement(rec);
+		break;
+	case SMC_RSI_REALM_CONFIG: {
+		struct rsi_config_result res;
+
+		res = handle_rsi_realm_config(rec);
+		if (res.walk_result.abort) {
+			emulate_stage2_data_abort(rec, rec_exit,
+						  res.walk_result.rtt_level);
+			ret_to_rec = false; /* Exit to Host */
+		} else {
+			/* Return to Realm */
+			return_result_to_realm(rec, res.smc_res);
+		}
+		break;
+	}
+	case SMC_RSI_IPA_STATE_SET:
+		if (handle_rsi_ipa_state_set(rec, rec_exit)) {
+			rec->regs[0] = RSI_ERROR_INPUT;
+		} else {
+			advance_pc();
+			ret_to_rec = false; /* Return to Host */
+		}
+		break;
+	case SMC_RSI_IPA_STATE_GET: {
+		enum ripas ripas;
+
+		rec->regs[0] = handle_rsi_ipa_state_get(rec, rec->regs[1],
+							&ripas);
+		if (rec->regs[0] == RSI_SUCCESS) {
+			rec->regs[1] = ripas;
+		}
+		break;
+	}
+	case SMC_RSI_HOST_CALL: {
+		struct rsi_host_call_result res;
+
+		res = handle_rsi_host_call(rec, rec_exit);
+
+		if (res.walk_result.abort) {
+			emulate_stage2_data_abort(rec, rec_exit,
+						  res.walk_result.rtt_level);
+		} else {
+			rec->regs[0] = res.smc_result;
+
+			/*
+			 * Return to Realm in case of error,
+			 * parent function calls advance_pc()
+			 */
+			if (rec->regs[0] == RSI_SUCCESS) {
+				advance_pc();
+
+				/* Exit to Host */
+				rec->host_call = true;
+				rec_exit->exit_reason = RMI_EXIT_HOST_CALL;
+				ret_to_rec = false;
+			}
+		}
+		break;
+	}
+
+	default:
+		rec->regs[0] = SMC_UNKNOWN;
+		break;
+	}
+
+	/* Log RSI call */
+	RSI_LOG_EXIT(function_id, rec->regs[0], ret_to_rec);
+	return ret_to_rec;
+}
+
+/*
+ * Return 'true' if the RMM handled the exception,
+ * 'false' to return to the Non-secure host.
+ */
+static bool handle_exception_sync(struct rec *rec, struct rmi_rec_exit *rec_exit)
+{
+	const unsigned long esr = read_esr_el2();
+
+	switch (esr & ESR_EL2_EC_MASK) {
+	case ESR_EL2_EC_WFX:
+		rec_exit->esr = esr & (ESR_EL2_EC_MASK | ESR_EL2_WFx_TI_BIT);
+		advance_pc();
+		return false;
+	case ESR_EL2_EC_HVC:
+		realm_inject_undef_abort();
+		return true;
+	case ESR_EL2_EC_SMC:
+		if (!handle_realm_rsi(rec, rec_exit)) {
+			return false;
+		}
+		/*
+		 * Advance PC.
+		 * HCR_EL2.TSC traps execution of the SMC instruction.
+		 * It is not a routing control for the SMC exception.
+		 * Trap exceptions and SMC exceptions have different
+		 * preferred return addresses.
+		 */
+		advance_pc();
+		return true;
+	case ESR_EL2_EC_SYSREG: {
+		bool ret = handle_sysreg_access_trap(rec, rec_exit, esr);
+
+		advance_pc();
+		return ret;
+	}
+	case ESR_EL2_EC_INST_ABORT:
+		return handle_instruction_abort(rec, rec_exit, esr);
+	case ESR_EL2_EC_DATA_ABORT:
+		return handle_data_abort(rec, rec_exit, esr);
+	case ESR_EL2_EC_FPU: {
+		unsigned long cptr;
+
+		/*
+		 * Realm has requested FPU/SIMD access, so save NS state and
+		 * load realm state.  Start by disabling traps so we can save
+		 * the NS state and load the realm state.
+		 */
+		cptr = read_cptr_el2();
+		cptr &= ~(CPTR_EL2_FPEN_MASK << CPTR_EL2_FPEN_SHIFT);
+		cptr |= (CPTR_EL2_FPEN_NO_TRAP_11 << CPTR_EL2_FPEN_SHIFT);
+		cptr &= ~(CPTR_EL2_ZEN_MASK << CPTR_EL2_ZEN_SHIFT);
+		cptr |= (CPTR_EL2_ZEN_NO_TRAP_11 << CPTR_EL2_ZEN_SHIFT);
+		write_cptr_el2(cptr);
+
+		/*
+		 * Save NS state, restore realm state, and set flag indicating
+		 * realm has used FPU so we know to save and restore NS state at
+		 * realm exit.
+		 */
+		if (rec->ns->sve != NULL) {
+			save_sve_state(rec->ns->sve);
+		} else {
+			assert(rec->ns->fpu != NULL);
+			fpu_save_state(rec->ns->fpu);
+		}
+		fpu_restore_state(&rec->fpu_ctx.fpu);
+		rec->fpu_ctx.used = true;
+
+		/*
+		 * Disable SVE for now, until per rec save/restore is
+		 * implemented
+		 */
+		cptr = read_cptr_el2();
+		cptr &= ~(CPTR_EL2_ZEN_MASK << CPTR_EL2_ZEN_SHIFT);
+		cptr |= (CPTR_EL2_ZEN_TRAP_ALL_00 << CPTR_EL2_ZEN_SHIFT);
+		write_cptr_el2(cptr);
+
+		/*
+		 * Return 'true' indicating that this exception
+		 * has been handled and execution can continue.
+		 */
+		return true;
+	}
+	default:
+		/*
+		 * TODO: Check if there are other exit reasons we could
+		 * encounter here and handle them appropriately
+		 */
+		break;
+	}
+
+	VERBOSE("Unhandled sync exit ESR: %08lx (EC: %lx ISS: %lx)\n",
+		esr,
+		(esr & ESR_EL2_EC_MASK) >> ESR_EL2_EC_SHIFT,
+		(esr & ESR_EL2_ISS_MASK) >> ESR_EL2_ISS_SHIFT);
+
+	/*
+	 * Zero values in esr, far & hpfar of 'rec_exit' structure
+	 * will be returned to the NS host.
+	 * The only information that may leak is when there was
+	 * some unhandled/unknown reason for the exception.
+	 */
+	return false;
+}
+
+/*
+ * Return 'true' if the RMM handled the exception, 'false' to return to the
+ * Non-secure host.
+ */
+static bool handle_exception_serror_lel(struct rec *rec, struct rmi_rec_exit *rec_exit)
+{
+	const unsigned long esr = read_esr_el2();
+
+	if (esr & ESR_EL2_SERROR_IDS_BIT) {
+		/*
+		 * Implementation defined content of the esr.
+		 */
+		system_abort();
+	}
+
+	if ((esr & ESR_EL2_SERROR_DFSC_MASK) != ESR_EL2_SERROR_DFSC_ASYNC) {
+		/*
+		 * Either Uncategorized or Reserved fault status code.
+		 */
+		system_abort();
+	}
+
+	switch (esr & ESR_EL2_SERROR_AET_MASK) {
+	case ESR_EL2_SERROR_AET_UEU:	/* Unrecoverable RAS Error */
+	case ESR_EL2_SERROR_AET_UER:	/* Recoverable RAS Error */
+		/*
+		 * The abort is fatal to the current S/W. Inject the SError into
+		 * the Realm so it can e.g. shut down gracefully or localize the
+		 * problem at the specific EL0 application.
+		 *
+		 * Note: Consider shutting down the Realm here to avoid
+		 * the host's attack on unstable Realms.
+		 */
+		inject_serror(rec, esr);
+		/*
+		 * Fall through.
+		 */
+	case ESR_EL2_SERROR_AET_CE:	/* Corrected RAS Error */
+	case ESR_EL2_SERROR_AET_UEO:	/* Restartable RAS Error */
+		/*
+		 * Report the exception to the host.
+		 */
+		rec_exit->esr = esr & ESR_SERROR_MASK;
+		break;
+	case ESR_EL2_SERROR_AET_UC:	/* Uncontainable RAS Error */
+		system_abort();
+		break;
+	default:
+		/*
+		 * Unrecognized Asynchronous Error Type
+		 */
+		assert(false);
+	}
+
+	return false;
+}
+
+static bool handle_exception_irq_lel(struct rec *rec, struct rmi_rec_exit *rec_exit)
+{
+	(void)rec;
+
+	rec_exit->exit_reason = RMI_EXIT_IRQ;
+
+	/*
+	 * With GIC all virtual interrupt programming
+	 * must go via the NS hypervisor.
+	 */
+	return false;
+}
+
+/* Returns 'true' when returning to Realm (S) and false when to NS */
+bool handle_realm_exit(struct rec *rec, struct rmi_rec_exit *rec_exit, int exception)
+{
+	switch (exception) {
+	case ARM_EXCEPTION_SYNC_LEL: {
+		bool ret;
+
+		/*
+		 * TODO: Sanitize ESR to ensure it doesn't leak sensitive
+		 * information.
+		 */
+		rec_exit->exit_reason = RMI_EXIT_SYNC;
+		ret = handle_exception_sync(rec, rec_exit);
+		if (!ret) {
+			rec->last_run_info.esr = read_esr_el2();
+			rec->last_run_info.far = read_far_el2();
+			rec->last_run_info.hpfar = read_hpfar_el2();
+		}
+		return ret;
+
+		/*
+		 * TODO: Much more detailed handling of exit reasons.
+		 */
+	}
+	case ARM_EXCEPTION_IRQ_LEL:
+		return handle_exception_irq_lel(rec, rec_exit);
+	case ARM_EXCEPTION_FIQ_LEL:
+		rec_exit->exit_reason = RMI_EXIT_FIQ;
+		break;
+	case ARM_EXCEPTION_SERROR_LEL: {
+		const unsigned long esr = read_esr_el2();
+		bool ret;
+
+		/*
+		 * TODO: Sanitize ESR to ensure it doesn't leak sensitive
+		 * information.
+		 */
+		rec_exit->exit_reason = RMI_EXIT_SERROR;
+		ret = handle_exception_serror_lel(rec, rec_exit);
+		if (!ret) {
+			rec->last_run_info.esr = esr;
+			rec->last_run_info.far = read_far_el2();
+			rec->last_run_info.hpfar = read_hpfar_el2();
+		}
+		return ret;
+	}
+	default:
+		INFO("Unrecognized exit reason: %d\n", exception);
+		break;
+	};
+
+	return false;
+}
diff --git a/runtime/core/fake_host/runtime_core_stub.c b/runtime/core/fake_host/runtime_core_stub.c
new file mode 100644
index 0000000..e71ca2e
--- /dev/null
+++ b/runtime/core/fake_host/runtime_core_stub.c
@@ -0,0 +1,23 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <host_harness.h>
+#include <run.h>
+
+bool memcpy_ns_read(void *dest, const void *ns_src, unsigned long size)
+{
+	return host_memcpy_ns_read(dest, ns_src, size);
+}
+
+
+bool memcpy_ns_write(void *ns_dest, const void *src, unsigned long size)
+{
+	return host_memcpy_ns_write(ns_dest, src, size);
+}
+
+int run_realm(unsigned long *regs)
+{
+	return host_run_realm(regs);
+}
diff --git a/runtime/core/handler.c b/runtime/core/handler.c
new file mode 100644
index 0000000..b7f3a55
--- /dev/null
+++ b/runtime/core/handler.c
@@ -0,0 +1,388 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <arch.h>
+#include <arch_helpers.h>
+#include <assert.h>
+#include <buffer.h>
+#include <debug.h>
+#include <sizes.h>
+#include <smc-handler.h>
+#include <smc-rmi.h>
+#include <smc.h>
+#include <status.h>
+#include <utils_def.h>
+
+#define STATUS_HANDLER(_id)[_id] = #_id
+
+const char *status_handler[] = {
+	STATUS_HANDLER(RMI_SUCCESS),
+	STATUS_HANDLER(RMI_ERROR_INPUT),
+	STATUS_HANDLER(RMI_ERROR_REALM),
+	STATUS_HANDLER(RMI_ERROR_REC),
+	STATUS_HANDLER(RMI_ERROR_RTT),
+	STATUS_HANDLER(RMI_ERROR_IN_USE)
+};
+COMPILER_ASSERT(ARRAY_LEN(status_handler) == RMI_ERROR_COUNT);
+
+/*
+ * At this level (in handle_ns_smc) we distinguish the RMI calls only on:
+ * - The number of input arguments [0..4], and whether
+ * - The function returns up to three output values in addition
+ *   to the return status code.
+ * Hence, the naming syntax is:
+ * - `*_[0..4]` when no output values are returned, and
+ * - `*_[0..4]_o` when the function returns some output values.
+ */
+
+typedef unsigned long (*handler_0)(void);
+typedef unsigned long (*handler_1)(unsigned long arg0);
+typedef unsigned long (*handler_2)(unsigned long arg0, unsigned long arg1);
+typedef unsigned long (*handler_3)(unsigned long arg0, unsigned long arg1,
+				   unsigned long arg2);
+typedef unsigned long (*handler_4)(unsigned long arg0, unsigned long arg1,
+				   unsigned long arg2, unsigned long arg3);
+typedef unsigned long (*handler_5)(unsigned long arg0, unsigned long arg1,
+				   unsigned long arg2, unsigned long arg3,
+				   unsigned long arg4);
+typedef void (*handler_1_o)(unsigned long arg0, struct smc_result *ret);
+typedef void (*handler_3_o)(unsigned long arg0, unsigned long arg1,
+			    unsigned long arg2, struct smc_result *ret);
+
+enum rmi_type {
+	rmi_type_0,
+	rmi_type_1,
+	rmi_type_2,
+	rmi_type_3,
+	rmi_type_4,
+	rmi_type_5,
+	rmi_type_1_o,
+	rmi_type_3_o
+};
+
+struct smc_handler {
+	const char	*fn_name;
+	enum rmi_type	type;
+	union {
+		handler_0	f0;
+		handler_1	f1;
+		handler_2	f2;
+		handler_3	f3;
+		handler_4	f4;
+		handler_5	f5;
+		handler_1_o	f1_o;
+		handler_3_o	f3_o;
+		void		*fn_dummy;
+	};
+	bool		log_exec;	/* print handler execution */
+	bool		log_error;	/* print in case of error status */
+	unsigned int	out_values;	/* number of output values */
+};
+
+/*
+ * Get handler ID from FID
+ * Precondition: FID is an RMI call
+ */
+#define SMC_RMI_HANDLER_ID(_fid) SMC64_FID_OFFSET_FROM_RANGE_MIN(RMI, _fid)
+
+#define HANDLER_0(_id, _fn, _exec, _error)[SMC_RMI_HANDLER_ID(_id)] = { \
+	.fn_name = #_id, \
+	.type = rmi_type_0, .f0 = _fn, .log_exec = _exec, .log_error = _error,	   \
+	.out_values = 0U }
+#define HANDLER_1(_id, _fn, _exec, _error)[SMC_RMI_HANDLER_ID(_id)] = { \
+	.fn_name = #_id, \
+	.type = rmi_type_1, .f1 = _fn, .log_exec = _exec, .log_error = _error,	   \
+	.out_values = 0U }
+#define HANDLER_2(_id, _fn, _exec, _error)[SMC_RMI_HANDLER_ID(_id)] = { \
+	.fn_name = #_id, \
+	.type = rmi_type_2, .f2 = _fn, .log_exec = _exec, .log_error = _error,     \
+	.out_values = 0U }
+#define HANDLER_3(_id, _fn, _exec, _error)[SMC_RMI_HANDLER_ID(_id)] = { \
+	.fn_name = #_id, \
+	.type = rmi_type_3, .f3 = _fn, .log_exec = _exec, .log_error = _error,	   \
+	.out_values = 0U }
+#define HANDLER_4(_id, _fn, _exec, _error)[SMC_RMI_HANDLER_ID(_id)] = { \
+	.fn_name = #_id, \
+	.type = rmi_type_4, .f4 = _fn, .log_exec = _exec, .log_error = _error,	   \
+	.out_values = 0U }
+#define HANDLER_5(_id, _fn, _exec, _error)[SMC_RMI_HANDLER_ID(_id)] = { \
+	.fn_name = #_id, \
+	.type = rmi_type_5, .f5 = _fn, .log_exec = _exec, .log_error = _error,	   \
+	.out_values = 0U }
+#define HANDLER_1_O(_id, _fn, _exec, _error, _values)[SMC_RMI_HANDLER_ID(_id)] = { \
+	.fn_name = #_id, \
+	.type = rmi_type_1_o, .f1_o = _fn, .log_exec = _exec, .log_error = _error, \
+	.out_values = _values }
+#define HANDLER_3_O(_id, _fn, _exec, _error, _values)[SMC_RMI_HANDLER_ID(_id)] = { \
+	.fn_name = #_id, \
+	.type = rmi_type_3_o, .f3_o = _fn, .log_exec = _exec, .log_error = _error, \
+	.out_values = _values }
+
+/*
+ * The 3rd value enables the execution log.
+ * The 4th value enables the error log.
+ */
+static const struct smc_handler smc_handlers[] = {
+	HANDLER_0(SMC_RMM_VERSION,		 smc_version,			true,  true),
+	HANDLER_1_O(SMC_RMM_FEATURES,		 smc_read_feature_register,	true,  true, 1U),
+	HANDLER_1(SMC_RMM_GRANULE_DELEGATE,	 smc_granule_delegate,		false, true),
+	HANDLER_1(SMC_RMM_GRANULE_UNDELEGATE,	 smc_granule_undelegate,	false, true),
+	HANDLER_2(SMC_RMM_REALM_CREATE,		 smc_realm_create,		true,  true),
+	HANDLER_1(SMC_RMM_REALM_DESTROY,	 smc_realm_destroy,		true,  true),
+	HANDLER_1(SMC_RMM_REALM_ACTIVATE,	 smc_realm_activate,		true,  true),
+	HANDLER_3(SMC_RMM_REC_CREATE,		 smc_rec_create,		true,  true),
+	HANDLER_1(SMC_RMM_REC_DESTROY,		 smc_rec_destroy,		true,  true),
+	HANDLER_2(SMC_RMM_REC_ENTER,		 smc_rec_enter,			false, true),
+	HANDLER_5(SMC_RMM_DATA_CREATE,		 smc_data_create,		false, false),
+	HANDLER_3(SMC_RMM_DATA_CREATE_UNKNOWN,	 smc_data_create_unknown,	false, false),
+	HANDLER_2(SMC_RMM_DATA_DESTROY,		 smc_data_destroy,		false, true),
+	HANDLER_4(SMC_RMM_RTT_CREATE,		 smc_rtt_create,		false, true),
+	HANDLER_4(SMC_RMM_RTT_DESTROY,		 smc_rtt_destroy,		false, true),
+	HANDLER_4(SMC_RMM_RTT_FOLD,		 smc_rtt_fold,			false, true),
+	HANDLER_4(SMC_RMM_RTT_MAP_UNPROTECTED,	 smc_rtt_map_unprotected,	false, false),
+	HANDLER_3(SMC_RMM_RTT_UNMAP_UNPROTECTED, smc_rtt_unmap_unprotected,	false, false),
+	HANDLER_3_O(SMC_RMM_RTT_READ_ENTRY,	 smc_rtt_read_entry,		false, true, 4U),
+	HANDLER_2(SMC_RMM_PSCI_COMPLETE,	 smc_psci_complete,		true,  true),
+	HANDLER_1_O(SMC_RMM_REC_AUX_COUNT,	 smc_rec_aux_count,		true,  true, 1U),
+	HANDLER_3(SMC_RMM_RTT_INIT_RIPAS,	 smc_rtt_init_ripas,		false, true),
+	HANDLER_5(SMC_RMM_RTT_SET_RIPAS,	 smc_rtt_set_ripas,		false, true)
+};
+
+COMPILER_ASSERT(ARRAY_LEN(smc_handlers) == SMC64_NUM_FIDS_IN_RANGE(RMI));
+
+static bool rmi_call_log_enabled = true;
+
+static void rmi_log_on_exit(unsigned long handler_id,
+			    unsigned long arg0,
+			    unsigned long arg1,
+			    unsigned long arg2,
+			    unsigned long arg3,
+			    unsigned long arg4,
+			    struct smc_result *ret)
+{
+	const struct smc_handler *handler = &smc_handlers[handler_id];
+	unsigned long function_id = SMC64_RMI_FID(handler_id);
+	unsigned int i;
+	return_code_t rc;
+
+	if (!handler->log_exec && !handler->log_error) {
+		return;
+	}
+
+	if (function_id == SMC_RMM_VERSION) {
+		/*
+		 * RMM_VERSION is special because it returns the
+		 * version number, not the error code.
+		 */
+		INFO("%-29s %8lx %8lx %8lx %8lx %8lx > %lx\n",
+		     handler->fn_name, arg0, arg1, arg2, arg3, arg4,
+		     ret->x[0]);
+		return;
+	}
+
+	rc = unpack_return_code(ret->x[0]);
+
+	if ((handler->log_exec) ||
+	    (handler->log_error && (rc.status != RMI_SUCCESS))) {
+		INFO("%-29s %8lx %8lx %8lx %8lx %8lx > ",
+			handler->fn_name, arg0, arg1, arg2, arg3, arg4);
+		if (rc.status >= RMI_ERROR_COUNT) {
+			INFO("%lx", ret->x[0]);
+		} else {
+			INFO("%s", status_handler[rc.status]);
+		}
+
+		/* Check for index */
+		if (((function_id == SMC_RMM_REC_ENTER) &&
+		     (rc.status == RMI_ERROR_REALM)) ||
+		     (rc.status == RMI_ERROR_RTT)) {
+			INFO(" %x", rc.index);
+		}
+
+		/* Print output values */
+		for (i = 1U; i <= handler->out_values; i++) {
+			INFO(" %8lx", ret->x[i]);
+		}
+
+		INFO("\n");
+	}
+}
+
+void handle_ns_smc(unsigned long function_id,
+		   unsigned long arg0,
+		   unsigned long arg1,
+		   unsigned long arg2,
+		   unsigned long arg3,
+		   unsigned long arg4,
+		   unsigned long arg5,
+		   struct smc_result *ret)
+{
+	unsigned long handler_id;
+	const struct smc_handler *handler = NULL;
+
+	if (IS_SMC64_RMI_FID(function_id)) {
+		handler_id = SMC_RMI_HANDLER_ID(function_id);
+		if (handler_id < ARRAY_LEN(smc_handlers)) {
+			handler = &smc_handlers[handler_id];
+		}
+	}
+
+	/*
+	 * Check if handler exists and 'fn_dummy' is not NULL
+	 * for not implemented 'function_id' calls in SMC RMI range.
+	 */
+	if ((handler == NULL) || (handler->fn_dummy == NULL)) {
+		VERBOSE("[%s] unknown function_id: %lx\n",
+			__func__, function_id);
+		ret->x[0] = SMC_UNKNOWN;
+		return;
+	}
+
+	assert_cpu_slots_empty();
+
+	switch (handler->type) {
+	case rmi_type_0:
+		ret->x[0] = handler->f0();
+		break;
+	case rmi_type_1:
+		ret->x[0] = handler->f1(arg0);
+		break;
+	case rmi_type_2:
+		ret->x[0] = handler->f2(arg0, arg1);
+		break;
+	case rmi_type_3:
+		ret->x[0] = handler->f3(arg0, arg1, arg2);
+		break;
+	case rmi_type_4:
+		ret->x[0] = handler->f4(arg0, arg1, arg2, arg3);
+		break;
+	case rmi_type_5:
+		ret->x[0] = handler->f5(arg0, arg1, arg2, arg3, arg4);
+		break;
+	case rmi_type_1_o:
+		handler->f1_o(arg0, ret);
+		break;
+	case rmi_type_3_o:
+		handler->f3_o(arg0, arg1, arg2, ret);
+		break;
+	default:
+		assert(false);
+	}
+
+	if (rmi_call_log_enabled) {
+		rmi_log_on_exit(handler_id, arg0, arg1, arg2, arg3, arg4, ret);
+	}
+
+	assert_cpu_slots_empty();
+}
+
+static void report_unexpected(void)
+{
+	unsigned long spsr = read_spsr_el2();
+	unsigned long esr = read_esr_el2();
+	unsigned long elr = read_elr_el2();
+	unsigned long far = read_far_el2();
+
+	INFO("----\n");
+	INFO("Unexpected exception:\n");
+	INFO("SPSR_EL2: 0x%016lx\n", spsr);
+	INFO("ESR_EL2:  0x%016lx\n", esr);
+	INFO("ELR_EL2:  0x%016lx\n", elr);
+	INFO("FAR_EL2:  0x%016lx\n", far);
+	INFO("----\n");
+
+}
+
+unsigned long handle_realm_trap(unsigned long *regs)
+{
+	report_unexpected();
+
+	while (1) {
+		wfe();
+	}
+}
+
+/*
+ * Identifies an abort that the RMM may recover from.
+ */
+struct rmm_trap_element {
+	/*
+	 * The PC at the time of abort.
+	 */
+	unsigned long aborted_pc;
+	/*
+	 * New value of the PC.
+	 */
+	unsigned long new_pc;
+};
+
+#define RMM_TRAP_HANDLER(_aborted_pc, _new_pc) \
+	{ .aborted_pc = (unsigned long)(&_aborted_pc), \
+	  .new_pc = (unsigned long)(&_new_pc) }
+
+/*
+ * The registered locations of load/store instructions that access NS memory.
+ */
+extern void *ns_read;
+extern void *ns_write;
+
+/*
+ * The new value of the PC when the GPF occurs on a registered location.
+ */
+extern void *ns_access_ret_0;
+
+struct rmm_trap_element rmm_trap_list[] = {
+	RMM_TRAP_HANDLER(ns_read, ns_access_ret_0),
+	RMM_TRAP_HANDLER(ns_write, ns_access_ret_0),
+};
+#define RMM_TRAP_LIST_SIZE (sizeof(rmm_trap_list)/sizeof(struct rmm_trap_element))
+
+static void fatal_abort(void)
+{
+	report_unexpected();
+
+	while (1) {
+		wfe();
+	}
+}
+
+static bool is_el2_data_abort_gpf(unsigned long esr)
+{
+	if (((esr & ESR_EL2_EC_MASK) == ESR_EL2_EC_DATA_ABORT_SEL) &&
+	    ((esr & ESR_EL2_ABORT_FSC_MASK) == ESR_EL2_ABORT_FSC_GPF))
+		return true;
+	return false;
+}
+
+/*
+ * Handles the RMM's aborts.
+ * It compares the PC at the time of the abort with the registered addresses.
+ * If it finds a match, it returns the new value of the PC that the RMM should
+ * continue from. Other register values are preserved.
+ * If no match is found, it aborts the RMM.
+ */
+unsigned long handle_rmm_trap(void)
+{
+	int i;
+
+	unsigned long esr = read_esr_el2();
+	unsigned long elr = read_elr_el2();
+
+	/*
+	 * Only the GPF data aborts are recoverable.
+	 */
+	if (!is_el2_data_abort_gpf(esr)) {
+		fatal_abort();
+	}
+
+	for (i = 0; i < RMM_TRAP_LIST_SIZE; i++) {
+		if (rmm_trap_list[i].aborted_pc == elr) {
+			return rmm_trap_list[i].new_pc;
+		}
+	}
+
+	fatal_abort();
+	return 0;
+}
diff --git a/runtime/core/init.c b/runtime/core/init.c
new file mode 100644
index 0000000..ad86fbc
--- /dev/null
+++ b/runtime/core/init.c
@@ -0,0 +1,88 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <arch_helpers.h>
+#include <attestation.h>
+#include <buffer.h>
+#include <debug.h>
+#include <rmm_el3_ifc.h>
+#include <smc-rmi.h>
+#include <smc-rsi.h>
+
+#ifdef NDEBUG
+#define RMM_BUILD_TYPE	"release"
+#else
+#define RMM_BUILD_TYPE	"debug"
+#endif
+
+#define VER_STRING(toolchain, major, minor, patch) \
+		toolchain __STRING(major) "." \
+		__STRING(minor) "." __STRING(patch)
+
+static void rmm_arch_init(void)
+{
+	MPAM(write_mpam2_el2(MPAM2_EL2_INIT));
+	MPAM(write_mpamhcr_el2(MPAMHCR_EL2_INIT));
+	SPE(write_pmscr_el2(PMSCR_EL2_INIT));
+
+	write_cnthctl_el2(CNTHCTL_EL2_INIT);
+	write_mdcr_el2(MDCR_EL2_INIT);
+}
+
+void rmm_warmboot_main(void)
+{
+	/*
+	 * Do the rest of RMM architecture init
+	 */
+	rmm_arch_init();
+
+	/*
+	 * Finish initializing the slot buffer mechanism
+	 */
+	slot_buf_init();
+}
+
+void rmm_main(void)
+{
+	unsigned int rmm_el3_ifc_version = rmm_el3_ifc_get_version();
+	unsigned int manifest_version = rmm_el3_ifc_get_manifest_version();
+
+	/*
+	 * Report project name, version, build type and
+	 * commit information if it is present
+	 */
+	NOTICE("Booting %s v.%s(%s) %s Built with %s\n",
+		NAME, VERSION, RMM_BUILD_TYPE, COMMIT_INFO,
+#ifdef __clang__
+	VER_STRING("Clang ", __clang_major__, __clang_minor__,
+		__clang_patchlevel__)
+#else
+	VER_STRING("GCC ", __GNUC__, __GNUC_MINOR__,
+		__GNUC_PATCHLEVEL__)
+#endif
+		);
+
+	/* Report Boot Interface version */
+	NOTICE("RMM-EL3 Interface v.%u.%u\n",
+		RMM_EL3_IFC_GET_VERS_MAJOR(rmm_el3_ifc_version),
+		RMM_EL3_IFC_GET_VERS_MINOR(rmm_el3_ifc_version));
+
+	/* Report Boot Manifest version */
+	NOTICE("Boot Manifest Interface v.%u.%u\n",
+		RMM_EL3_MANIFEST_GET_VERS_MAJOR(manifest_version),
+		RMM_EL3_MANIFEST_GET_VERS_MINOR(manifest_version));
+
+	/* Report RMI/RSI ABI versions and build timestamp */
+	NOTICE("RMI/RSI ABI v.%u.%u/%u.%u built: %s %s\n",
+		RMI_ABI_VERSION_MAJOR, RMI_ABI_VERSION_MINOR,
+		RSI_ABI_VERSION_MAJOR, RSI_ABI_VERSION_MINOR,
+		__DATE__, __TIME__);
+
+	rmm_warmboot_main();
+
+	if (attestation_init() != 0) {
+		WARN("Attestation init failed.\n");
+	}
+}
diff --git a/runtime/core/inject_exp.c b/runtime/core/inject_exp.c
new file mode 100644
index 0000000..cc818f8
--- /dev/null
+++ b/runtime/core/inject_exp.c
@@ -0,0 +1,169 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <arch.h>
+#include <arch_helpers.h>
+#include <assert.h>
+#include <inject_exp.h>
+#include <rec.h>
+
+/*
+ * Calculate the address of the vector entry when an exception is inserted
+ * into the Realm.
+ *
+ * @vbar The base address of the vector table in the Realm.
+ * @spsr The Saved Program Status Register at EL2.
+ */
+static unsigned long calc_vector_entry(unsigned long vbar, unsigned long spsr)
+{
+	unsigned long offset;
+
+	if ((spsr & MASK(SPSR_EL2_MODE)) == SPSR_EL2_MODE_EL1h) {
+		offset = VBAR_CEL_SP_ELx_OFFSET;
+	} else if ((spsr & MASK(SPSR_EL2_MODE)) == SPSR_EL2_MODE_EL1t) {
+		offset = VBAR_CEL_SP_EL0_OFFSET;
+	} else if ((spsr & MASK(SPSR_EL2_MODE)) == SPSR_EL2_MODE_EL0t) {
+		if ((spsr & MASK(SPSR_EL2_nRW)) == SPSR_EL2_nRW_AARCH64) {
+			offset = VBAR_LEL_AA64_OFFSET;
+		} else {
+			offset = VBAR_LEL_AA32_OFFSET;
+		}
+	} else {
+		assert(false);
+		offset = 0UL;
+	}
+
+	return vbar + offset;
+}
+
+/*
+ * Calculate the value of the pstate when an exception
+ * is inserted into the Realm.
+ */
+static unsigned long calc_pstate(void)
+{
+	/*
+	 * The pstate is EL1, AArch64, SPSel = SP_ELx and:
+	 * DAIF = '1111b'
+	 * NZCV = '0000b'
+	 * TODO: setup TCO, DIT, UAO, PAN, SSBS, BTYPE
+	 */
+	unsigned long pstate = SPSR_EL2_MODE_EL1h |
+			       SPSR_EL2_nRW_AARCH64 |
+			       SPSR_EL2_F_BIT |
+			       SPSR_EL2_I_BIT |
+			       SPSR_EL2_A_BIT |
+			       SPSR_EL2_D_BIT;
+	return pstate;
+}
+
+/*
+ * Calculate the content of the Realm's esr_el1 register when
+ * the Synchronous Instruction or Data Abort is injected into
+ * the Realm (EL1).
+ *
+ * The value is constructed from the @esr_el2 & @spsr_el2 that
+ * are captured when the exception from the Realm was taken to EL2.
+ *
+ * The fault status code (ESR_EL1.I/DFSC) is set to @fsc
+ */
+static unsigned long calc_esr_idabort(unsigned long esr_el2,
+				      unsigned long spsr_el2,
+				      unsigned long fsc)
+{
+	/*
+	 * Copy esr_el2 into esr_el1 apart from the following fields:
+	 * - The exception class (EC). Its value depends on whether the
+	 *   exception to EL2 was from either EL1 or EL0.
+	 * - I/DFSC. It will be set to @fsc.
+	 * - FnV. It will set to zero.
+	 * - S1PTW. It will be set to zero.
+	 */
+	unsigned long esr_el1 = esr_el2 & ~(ESR_EL2_EC_MASK  |
+					    ESR_EL2_ABORT_FSC_MASK |
+					    ESR_EL2_ABORT_FNV_BIT |
+					    ESR_EL2_ABORT_S1PTW_BIT);
+
+	unsigned long ec = esr_el2 & ESR_EL2_EC_MASK;
+
+	assert((ec == ESR_EL2_EC_INST_ABORT) || (ec == ESR_EL2_EC_DATA_ABORT));
+	if ((spsr_el2 & MASK(SPSR_EL2_MODE)) != SPSR_EL2_MODE_EL0t) {
+		ec += 1UL << ESR_EL2_EC_SHIFT;
+	}
+	esr_el1 |= ec;
+
+	/*
+	 * Set the I/DFSC.
+	 */
+	assert((fsc & ~ESR_EL2_ABORT_FSC_MASK) == 0UL);
+	esr_el1 |= fsc;
+
+	/*
+	 * Set the EA.
+	 */
+	esr_el1 |= ESR_EL2_ABORT_EA_BIT;
+
+	return esr_el1;
+}
+
+/*
+ * Inject the Synchronous Instruction or Data Abort into the current REC.
+ * The I/DFSC field in the ESR_EL1 is set to @fsc
+ */
+void inject_sync_idabort(unsigned long fsc)
+{
+	unsigned long esr_el2 = read_esr_el2();
+	unsigned long far_el2 = read_far_el2();
+	unsigned long elr_el2 = read_elr_el2();
+	unsigned long spsr_el2 = read_spsr_el2();
+	unsigned long vbar_el2 = read_vbar_el12();
+
+	unsigned long esr_el1 = calc_esr_idabort(esr_el2, spsr_el2, fsc);
+	unsigned long pc = calc_vector_entry(vbar_el2, spsr_el2);
+	unsigned long pstate = calc_pstate();
+
+	write_far_el12(far_el2);
+	write_elr_el12(elr_el2);
+	write_spsr_el12(spsr_el2);
+	write_esr_el12(esr_el1);
+	write_elr_el2(pc);
+	write_spsr_el2(pstate);
+}
+
+/*
+ * Inject the Synchronous Instruction or Data Abort into @rec.
+ * The I/DFSC field in the ESR_EL1 is set to @fsc
+ */
+void inject_sync_idabort_rec(struct rec *rec, unsigned long fsc)
+{
+	rec->sysregs.far_el1 = rec->last_run_info.far;
+	rec->sysregs.elr_el1 = rec->pc;
+	rec->sysregs.spsr_el1 = rec->pstate;
+	rec->sysregs.esr_el1 = calc_esr_idabort(rec->last_run_info.esr,
+						rec->pstate, fsc);
+	rec->pc = calc_vector_entry(rec->sysregs.vbar_el1, rec->pstate);
+	rec->pstate = calc_pstate();
+}
+
+/*
+ * Inject the Undefined Synchronous Exception into the current REC.
+ */
+void realm_inject_undef_abort(void)
+{
+	unsigned long esr = ESR_EL2_IL_MASK | ESR_EL2_EC_UNKNOWN;
+	unsigned long elr = read_elr_el2();
+	unsigned long spsr = read_spsr_el2();
+	unsigned long vbar = read_vbar_el12();
+
+	unsigned long pc = calc_vector_entry(vbar, spsr);
+	unsigned long pstate = calc_pstate();
+
+	write_elr_el12(elr);
+	write_spsr_el12(spsr);
+	write_esr_el12(esr);
+
+	write_elr_el2(pc);
+	write_spsr_el2(pstate);
+}
diff --git a/runtime/core/run.c b/runtime/core/run.c
new file mode 100644
index 0000000..9127072
--- /dev/null
+++ b/runtime/core/run.c
@@ -0,0 +1,357 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <arch.h>
+#include <arch_features.h>
+#include <attestation.h>
+#include <buffer.h>
+#include <cpuid.h>
+#include <exit.h>
+#include <fpu_helpers.h>
+#include <rec.h>
+#include <run.h>
+#include <smc-rmi.h>
+#include <sve.h>
+#include <timers.h>
+
+static struct ns_state g_ns_data[MAX_CPUS];
+static uint8_t g_sve_data[MAX_CPUS][sizeof(struct sve_state)]
+		__attribute__((aligned(sizeof(__uint128_t))));
+
+/*
+ * Initialize the aux data and any buffer pointers to the aux granule memory for
+ * use by REC when it is entered.
+ */
+static void init_aux_data(struct rec_aux_data *aux_data,
+			  void *rec_aux,
+			  unsigned int num_rec_aux)
+{
+	aux_data->attest_heap_buf = (uint8_t *)rec_aux;
+
+	/* Ensure we have enough aux granules for use by REC */
+	assert(num_rec_aux >= REC_HEAP_PAGES);
+}
+
+/*
+ * The parent REC granules lock is expected to be acquired
+ * before functions map_rec_aux() and unmap_rec_aux() are called.
+ */
+static void *map_rec_aux(struct granule *rec_aux_pages[], unsigned long num_aux)
+{
+	void *rec_aux = NULL;
+
+	for (unsigned long i = 0UL; i < num_aux; i++) {
+		void *aux = granule_map(rec_aux_pages[i], SLOT_REC_AUX0 + i);
+
+		if (i == 0UL) {
+			rec_aux = aux;
+		}
+	}
+	return rec_aux;
+}
+
+static void unmap_rec_aux(void *rec_aux, unsigned long num_aux)
+{
+	unsigned char *rec_aux_vaddr = (unsigned char *)rec_aux;
+
+	for (unsigned long i = 0UL; i < num_aux; i++) {
+		buffer_unmap(rec_aux_vaddr + i * GRANULE_SIZE);
+	}
+}
+
+static void save_sysreg_state(struct sysreg_state *sysregs)
+{
+	sysregs->sp_el0 = read_sp_el0();
+	sysregs->sp_el1 = read_sp_el1();
+	sysregs->elr_el1 = read_elr_el12();
+	sysregs->spsr_el1 = read_spsr_el12();
+	sysregs->pmcr_el0 = read_pmcr_el0();
+	sysregs->pmuserenr_el0 = read_pmuserenr_el0();
+	sysregs->tpidrro_el0 = read_tpidrro_el0();
+	sysregs->tpidr_el0 = read_tpidr_el0();
+	sysregs->csselr_el1 = read_csselr_el1();
+	sysregs->sctlr_el1 = read_sctlr_el12();
+	sysregs->actlr_el1 = read_actlr_el1();
+	sysregs->cpacr_el1 = read_cpacr_el12();
+	sysregs->ttbr0_el1 = read_ttbr0_el12();
+	sysregs->ttbr1_el1 = read_ttbr1_el12();
+	sysregs->tcr_el1 = read_tcr_el12();
+	sysregs->esr_el1 = read_esr_el12();
+	sysregs->afsr0_el1 = read_afsr0_el12();
+	sysregs->afsr1_el1 = read_afsr1_el12();
+	sysregs->far_el1 = read_far_el12();
+	sysregs->mair_el1 = read_mair_el12();
+	sysregs->vbar_el1 = read_vbar_el12();
+
+	sysregs->contextidr_el1 = read_contextidr_el12();
+	sysregs->tpidr_el1 = read_tpidr_el1();
+	sysregs->amair_el1 = read_amair_el12();
+	sysregs->cntkctl_el1 = read_cntkctl_el12();
+	sysregs->par_el1 = read_par_el1();
+	sysregs->mdscr_el1 = read_mdscr_el1();
+	sysregs->mdccint_el1 = read_mdccint_el1();
+	sysregs->disr_el1 = read_disr_el1();
+	MPAM(sysregs->mpam0_el1 = read_mpam0_el1();)
+
+	/* Timer registers */
+	sysregs->cntpoff_el2 = read_cntpoff_el2();
+	sysregs->cntvoff_el2 = read_cntvoff_el2();
+	sysregs->cntp_ctl_el0 = read_cntp_ctl_el02();
+	sysregs->cntp_cval_el0 = read_cntp_cval_el02();
+	sysregs->cntv_ctl_el0 = read_cntv_ctl_el02();
+	sysregs->cntv_cval_el0 = read_cntv_cval_el02();
+}
+
+static void save_realm_state(struct rec *rec)
+{
+	save_sysreg_state(&rec->sysregs);
+
+	rec->pc = read_elr_el2();
+	rec->pstate = read_spsr_el2();
+
+	gic_save_state(&rec->sysregs.gicstate);
+}
+
+static void restore_sysreg_state(struct sysreg_state *sysregs)
+{
+	write_sp_el0(sysregs->sp_el0);
+	write_sp_el1(sysregs->sp_el1);
+	write_elr_el12(sysregs->elr_el1);
+	write_spsr_el12(sysregs->spsr_el1);
+	write_pmcr_el0(sysregs->pmcr_el0);
+	write_pmuserenr_el0(sysregs->pmuserenr_el0);
+	write_tpidrro_el0(sysregs->tpidrro_el0);
+	write_tpidr_el0(sysregs->tpidr_el0);
+	write_csselr_el1(sysregs->csselr_el1);
+	write_sctlr_el12(sysregs->sctlr_el1);
+	write_actlr_el1(sysregs->actlr_el1);
+	write_cpacr_el12(sysregs->cpacr_el1);
+	write_ttbr0_el12(sysregs->ttbr0_el1);
+	write_ttbr1_el12(sysregs->ttbr1_el1);
+	write_tcr_el12(sysregs->tcr_el1);
+	write_esr_el12(sysregs->esr_el1);
+	write_afsr0_el12(sysregs->afsr0_el1);
+	write_afsr1_el12(sysregs->afsr1_el1);
+	write_far_el12(sysregs->far_el1);
+	write_mair_el12(sysregs->mair_el1);
+	write_vbar_el12(sysregs->vbar_el1);
+
+	write_contextidr_el12(sysregs->contextidr_el1);
+	write_tpidr_el1(sysregs->tpidr_el1);
+	write_amair_el12(sysregs->amair_el1);
+	write_cntkctl_el12(sysregs->cntkctl_el1);
+	write_par_el1(sysregs->par_el1);
+	write_mdscr_el1(sysregs->mdscr_el1);
+	write_mdccint_el1(sysregs->mdccint_el1);
+	write_disr_el1(sysregs->disr_el1);
+	MPAM(write_mpam0_el1(sysregs->mpam0_el1);)
+	write_vmpidr_el2(sysregs->vmpidr_el2);
+
+	/* Timer registers */
+	write_cntpoff_el2(sysregs->cntpoff_el2);
+	write_cntvoff_el2(sysregs->cntvoff_el2);
+
+	/*
+	 * Restore CNTx_CVAL registers before CNTx_CTL to avoid
+	 * raising the interrupt signal briefly before lowering
+	 * it again due to some expired CVAL left in the timer
+	 * register.
+	 */
+	write_cntp_cval_el02(sysregs->cntp_cval_el0);
+	write_cntp_ctl_el02(sysregs->cntp_ctl_el0);
+	write_cntv_cval_el02(sysregs->cntv_cval_el0);
+	write_cntv_ctl_el02(sysregs->cntv_ctl_el0);
+}
+
+static void restore_realm_state(struct rec *rec)
+{
+	/*
+	 * Restore this early to give time to the timer mask to propagate to
+	 * the GIC.  Issue an ISB to ensure the register write is actually
+	 * performed before doing the remaining work.
+	 */
+	write_cnthctl_el2(rec->sysregs.cnthctl_el2);
+	isb();
+
+	restore_sysreg_state(&rec->sysregs);
+	write_elr_el2(rec->pc);
+	write_spsr_el2(rec->pstate);
+	write_hcr_el2(rec->sysregs.hcr_el2);
+
+	gic_restore_state(&rec->sysregs.gicstate);
+}
+
+static void configure_realm_stage2(struct rec *rec)
+{
+	write_vtcr_el2(rec->common_sysregs.vtcr_el2);
+	write_vttbr_el2(rec->common_sysregs.vttbr_el2);
+}
+
+static void save_ns_state(struct ns_state *ns_state)
+{
+	save_sysreg_state(&ns_state->sysregs);
+
+	/*
+	 * CNTHCTL_EL2 is saved/restored separately from the main system
+	 * registers, because the Realm configuration is written on every
+	 * entry to the Realm, see `check_pending_timers`.
+	 */
+	ns_state->sysregs.cnthctl_el2 = read_cnthctl_el2();
+
+	ns_state->icc_sre_el2 = read_icc_sre_el2();
+}
+
+static void restore_ns_state(struct ns_state *ns_state)
+{
+	restore_sysreg_state(&ns_state->sysregs);
+
+	/*
+	 * CNTHCTL_EL2 is saved/restored separately from the main system
+	 * registers, because the Realm configuration is written on every
+	 * entry to the Realm, see `check_pending_timers`.
+	 */
+	write_cnthctl_el2(ns_state->sysregs.cnthctl_el2);
+
+	write_icc_sre_el2(ns_state->icc_sre_el2);
+}
+
+static void activate_events(struct rec *rec)
+{
+	/*
+	 * The only event that may be activated at the Realm is the SError.
+	 */
+	if (rec->serror_info.inject) {
+		write_vsesr_el2(rec->serror_info.vsesr_el2);
+		write_hcr_el2(rec->sysregs.hcr_el2 | HCR_VSE);
+		rec->serror_info.inject = false;
+	}
+}
+
+void inject_serror(struct rec *rec, unsigned long vsesr)
+{
+	rec->serror_info.vsesr_el2 = vsesr;
+	rec->serror_info.inject = true;
+}
+
+void rec_run_loop(struct rec *rec, struct rmi_rec_exit *rec_exit)
+{
+	struct ns_state *ns_state;
+	int realm_exception_code;
+	void *rec_aux;
+	unsigned int cpuid = my_cpuid();
+
+	assert(rec->ns == NULL);
+
+	assert(cpuid < MAX_CPUS);
+	ns_state = &g_ns_data[cpuid];
+
+	/* ensure SVE/FPU context is cleared */
+	assert(ns_state->sve == NULL);
+	assert(ns_state->fpu == NULL);
+
+	/* Map auxiliary granules */
+	rec_aux = map_rec_aux(rec->g_aux, rec->num_rec_aux);
+
+	init_aux_data(&(rec->aux_data), rec_aux, rec->num_rec_aux);
+
+	/*
+	 * The attset heap on the REC aux pages is mapped now. It is time to
+	 * associate it with the current CPU.
+	 * This heap will be used for attestation RSI calls when the
+	 * REC is running.
+	 */
+	attestation_heap_ctx_assign_pe(&rec->alloc_info.ctx);
+
+	/*
+	 * Initialise the heap for attestation if necessary.
+	 */
+	if (!rec->alloc_info.ctx_initialised) {
+		(void)attestation_heap_ctx_init(rec->aux_data.attest_heap_buf,
+						REC_HEAP_PAGES * SZ_4K);
+		rec->alloc_info.ctx_initialised = true;
+	}
+
+	if (is_feat_sve_present()) {
+		ns_state->sve = (struct sve_state *)&g_sve_data[cpuid];
+	} else {
+		ns_state->fpu = (struct fpu_state *)&g_sve_data[cpuid];
+	}
+
+	save_ns_state(ns_state);
+	restore_realm_state(rec);
+
+	/* Prepare for lazy save/restore of FPU/SIMD registers. */
+	rec->ns = ns_state;
+	assert(rec->fpu_ctx.used == false);
+
+	configure_realm_stage2(rec);
+
+	do {
+		/*
+		 * We must check the status of the arch timers in every
+		 * iteration of the loop to ensure we update the timer
+		 * mask on each entry to the realm and that we report any
+		 * change in output level to the NS caller.
+		 */
+		if (check_pending_timers(rec)) {
+			rec_exit->exit_reason = RMI_EXIT_IRQ;
+			break;
+		}
+
+		activate_events(rec);
+		realm_exception_code = run_realm(&rec->regs[0]);
+	} while (handle_realm_exit(rec, rec_exit, realm_exception_code));
+
+	/*
+	 * Check if FPU/SIMD was used, and if it was, save the realm state,
+	 * restore the NS state, and reenable traps in CPTR_EL2.
+	 */
+	if (rec->fpu_ctx.used) {
+		unsigned long cptr;
+
+		cptr = read_cptr_el2();
+		cptr &= ~(CPTR_EL2_ZEN_MASK << CPTR_EL2_ZEN_SHIFT);
+		cptr |= (CPTR_EL2_ZEN_NO_TRAP_11 << CPTR_EL2_ZEN_SHIFT);
+		write_cptr_el2(cptr);
+
+		fpu_save_state(&rec->fpu_ctx.fpu);
+		if (ns_state->sve != NULL) {
+			restore_sve_state(ns_state->sve);
+		} else {
+			assert(ns_state->fpu != NULL);
+			fpu_restore_state(ns_state->fpu);
+		}
+
+		cptr = read_cptr_el2();
+		cptr &= ~(CPTR_EL2_FPEN_MASK << CPTR_EL2_FPEN_SHIFT);
+		cptr |= (CPTR_EL2_FPEN_TRAP_ALL_00 << CPTR_EL2_FPEN_SHIFT);
+		cptr &= ~(CPTR_EL2_ZEN_MASK << CPTR_EL2_ZEN_SHIFT);
+		cptr |= (CPTR_EL2_ZEN_TRAP_ALL_00 << CPTR_EL2_ZEN_SHIFT);
+		write_cptr_el2(cptr);
+		rec->fpu_ctx.used = false;
+	}
+
+	/*
+	 * Clear FPU/SVE context while exiting
+	 */
+	ns_state->sve = NULL;
+	ns_state->fpu = NULL;
+
+	/*
+	 * Clear NS pointer since that struct is local to this function.
+	 */
+	rec->ns = NULL;
+
+	report_timer_state_to_ns(rec_exit);
+
+	save_realm_state(rec);
+	restore_ns_state(ns_state);
+
+	/* Undo the heap association */
+	attestation_heap_ctx_unassign_pe(&rec->alloc_info.ctx);
+	/* Unmap auxiliary granules */
+	unmap_rec_aux(rec_aux, rec->num_rec_aux);
+}
diff --git a/runtime/core/sysregs.c b/runtime/core/sysregs.c
new file mode 100644
index 0000000..55fac96
--- /dev/null
+++ b/runtime/core/sysregs.c
@@ -0,0 +1,222 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <arch.h>
+#include <arch_helpers.h>
+#include <debug.h>
+#include <esr.h>
+#include <memory_alloc.h>
+#include <rec.h>
+#include <smc-rmi.h>
+
+#define SYSREG_READ_CASE(reg) \
+	case ESR_EL2_SYSREG_##reg: return read_##reg()
+
+static unsigned long read_idreg(unsigned int idreg)
+{
+	switch (idreg) {
+	SYSREG_READ_CASE(ID_AA64PFR0_EL1);
+	SYSREG_READ_CASE(ID_AA64PFR1_EL1);
+	/*
+	 * TODO: not supported without SVE:
+	 * SYSREG_READ_CASE(ID_AA64ZFR0_EL1);
+	 */
+	SYSREG_READ_CASE(ID_AA64DFR0_EL1);
+	SYSREG_READ_CASE(ID_AA64DFR1_EL1);
+	SYSREG_READ_CASE(ID_AA64AFR0_EL1);
+	SYSREG_READ_CASE(ID_AA64AFR1_EL1);
+	SYSREG_READ_CASE(ID_AA64ISAR0_EL1);
+	SYSREG_READ_CASE(ID_AA64ISAR1_EL1);
+	SYSREG_READ_CASE(ID_AA64MMFR0_EL1);
+	SYSREG_READ_CASE(ID_AA64MMFR1_EL1);
+	SYSREG_READ_CASE(ID_AA64MMFR2_EL1);
+
+	default:
+		/* All other encodings are in the RES0 space */
+		return 0UL;
+	}
+}
+
+/*
+ * Handle ID_AA64XXX<n>_EL1 instructions
+ */
+static bool handle_id_sysreg_trap(struct rec *rec,
+				  struct rmi_rec_exit *rec_exit,
+				  unsigned long esr)
+{
+	unsigned int rt;
+	unsigned long idreg, mask;
+
+	/*
+	 * We only set HCR_EL2.TID3 to trap ID registers at the moment and
+	 * that only traps reads of registers. Seeing a write here indicates a
+	 * consistency problem with the RMM and we should panic immediately.
+	 */
+	assert(!ESR_EL2_SYSREG_IS_WRITE(esr));
+
+	/*
+	 * Read Rt value from the issued instruction,
+	 * the general-purpose register used for the transfer.
+	 */
+	rt = ESR_EL2_SYSREG_ISS_RT(esr);
+
+	/* Handle writes to XZR register */
+	if (rt == 31U) {
+		return true;
+	}
+
+	idreg = esr & ESR_EL2_SYSREG_MASK;
+
+	if (idreg == ESR_EL2_SYSREG_ID_AA64ISAR1_EL1) {
+		/* Clear Address and Generic Authentication bits */
+		mask = (0xfUL << ESR_EL2_SYSREG_ID_AA64ISAR1_APA_SHIFT) |
+		       (0xfUL << ESR_EL2_SYSREG_ID_AA64ISAR1_API_SHIFT) |
+		       (0xfUL << ESR_EL2_SYSREG_ID_AA64ISAR1_GPA_SHIFT) |
+		       (0xfUL << ESR_EL2_SYSREG_ID_AA64ISAR1_GPI_SHIFT);
+	/*
+	 * Workaround for TF-A trapping AMU registers access
+	 * to EL3 in Realm state
+	 */
+	} else if (idreg == ESR_EL2_SYSREG_ID_AA64PFR0_EL1) {
+		/* Clear support for Activity Monitors Extension */
+		mask = MASK(ID_AA64PFR0_EL1_AMU);
+
+		/*
+		 * Clear support for SVE. This is a temporary fix until RMM
+		 * completely supports SVE.
+		 */
+		mask |= MASK(ID_AA64PFR0_EL1_SVE);
+	} else {
+		mask = 0UL;
+	}
+
+	ARRAY_WRITE(rec->regs, rt, read_idreg(idreg) & ~mask);
+
+	return true;
+}
+
+static bool handle_icc_el1_sysreg_trap(struct rec *rec,
+				       struct rmi_rec_exit *rec_exit,
+				       unsigned long esr)
+{
+	__unused unsigned long sysreg = esr & ESR_EL2_SYSREG_MASK;
+
+	/*
+	 * We should only have configured ICH_HCR_EL2 to trap on DIR and we
+	 * always trap on the SGIRs following the architecture, so make sure
+	 * we're not accidentally trapping on some other register here.
+	 */
+	assert((sysreg == ESR_EL2_SYSREG_ICC_DIR) ||
+	       (sysreg == ESR_EL2_SYSREG_ICC_SGI1R_EL1) ||
+	       (sysreg == ESR_EL2_SYSREG_ICC_SGI0R_EL1));
+
+	/*
+	 * The registers above should only trap to EL2 for writes, read
+	 * instructions are not defined and should cause an Undefined exception
+	 * at EL1.
+	 */
+	assert(ESR_EL2_SYSREG_IS_WRITE(esr));
+
+	rec_exit->exit_reason = RMI_EXIT_SYNC;
+	rec_exit->esr = esr;
+	return false;
+}
+
+typedef bool (*sysreg_handler_fn)(struct rec *rec, struct rmi_rec_exit *rec_exit,
+				  unsigned long esr);
+
+struct sysreg_handler {
+	unsigned long esr_mask;
+	unsigned long esr_value;
+	sysreg_handler_fn fn;
+};
+
+#define SYSREG_HANDLER(_mask, _value, _handler_fn) \
+	{ .esr_mask = (_mask), .esr_value = (_value), .fn = _handler_fn }
+
+static const struct sysreg_handler sysreg_handlers[] = {
+	SYSREG_HANDLER(ESR_EL2_SYSREG_ID_MASK, ESR_EL2_SYSREG_ID, handle_id_sysreg_trap),
+	SYSREG_HANDLER(ESR_EL2_SYSREG_ICC_EL1_MASK, ESR_EL2_SYSREG_ICC_EL1, handle_icc_el1_sysreg_trap),
+	SYSREG_HANDLER(ESR_EL2_SYSREG_MASK, ESR_EL2_SYSREG_ICC_PMR_EL1, handle_icc_el1_sysreg_trap)
+};
+
+static unsigned long get_sysreg_write_value(struct rec *rec, unsigned long esr)
+{
+	unsigned int rt = esr_sysreg_rt(esr);
+	unsigned long val;
+
+	/* Handle reads from XZR register */
+	if (rt == 31U) {
+		return 0UL;
+	}
+
+	ARRAY_READ(rec->regs, rt, val);
+	return val;
+}
+
+static void emulate_sysreg_access_ns(struct rec *rec, struct rmi_rec_exit *rec_exit,
+				     unsigned long esr)
+{
+	if (ESR_EL2_SYSREG_IS_WRITE(esr)) {
+		rec_exit->gprs[0] = get_sysreg_write_value(rec, esr);
+	}
+}
+
+/*
+ * Handle trapped MSR, MRS or System instruction execution
+ * in AArch64 state
+ */
+bool handle_sysreg_access_trap(struct rec *rec, struct rmi_rec_exit *rec_exit,
+			       unsigned long esr)
+{
+	/*
+	 * Read Rt value from the issued instruction,
+	 * the general-purpose register used for the transfer.
+	 */
+	unsigned int rt = ESR_EL2_SYSREG_ISS_RT(esr);
+	unsigned int i;
+	unsigned int __unused op0, op1, crn, crm, op2;
+	unsigned long __unused sysreg;
+
+	/* Check for 32-bit instruction trapped */
+	assert(ESR_IL(esr) != 0UL);
+
+	for (i = 0U; i < ARRAY_LEN(sysreg_handlers); i++) {
+		const struct sysreg_handler *handler = &sysreg_handlers[i];
+		bool handled;
+
+		if ((esr & handler->esr_mask) == handler->esr_value) {
+			handled = handler->fn(rec, rec_exit, esr);
+			if (!handled) {
+				emulate_sysreg_access_ns(rec, rec_exit, esr);
+			}
+			return handled;
+		}
+	}
+
+	/*
+	 * For now, treat all unhandled accesses as RAZ/WI.
+	 * Handle writes to XZR register.
+	 */
+	if (!ESR_EL2_SYSREG_IS_WRITE(esr) && (rt != 31U)) {
+		ARRAY_WRITE(rec->regs, rt, 0UL);
+	}
+
+	sysreg = esr & ESR_EL2_SYSREG_MASK;
+
+	/* Extract sytem register encoding */
+	op0 = EXTRACT(ESR_EL2_SYSREG_TRAP_OP0, sysreg);
+	op1 = EXTRACT(ESR_EL2_SYSREG_TRAP_OP1, sysreg);
+	crn = EXTRACT(ESR_EL2_SYSREG_TRAP_CRN, sysreg);
+	crm = EXTRACT(ESR_EL2_SYSREG_TRAP_CRM, sysreg);
+	op2 = EXTRACT(ESR_EL2_SYSREG_TRAP_OP2, sysreg);
+
+	INFO("Unhandled %s S%u_%u_C%u_C%u_%u\n",
+		ESR_EL2_SYSREG_IS_WRITE(esr) ? "write" : "read",
+		op0, op1, crn, crm, op2);
+
+	return true;
+}
diff --git a/runtime/core/vmid.c b/runtime/core/vmid.c
new file mode 100644
index 0000000..64c30b3
--- /dev/null
+++ b/runtime/core/vmid.c
@@ -0,0 +1,65 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <arch_features.h>
+#include <assert.h>
+#include <atomics.h>
+#include <sizes.h>
+#include <spinlock.h>
+#include <vmid.h>
+
+#define VMID8_COUNT		(1U << 8)
+#define VMID16_COUNT		(1U << 16)
+#define MAX_VMID_COUNT		VMID16_COUNT
+#define VMID_ARRAY_LONG_SIZE	(MAX_VMID_COUNT / BITS_PER_UL)
+
+/*
+ * The bitmap for the reserved/used VMID values.
+ */
+static unsigned long vmids[VMID_ARRAY_LONG_SIZE];
+
+/*
+ * Marks the VMID value to be in use. It returns:
+ * - True, on success
+ * - False, if the vmid is out of range,
+ *   or if it was already reserved (in use).
+ */
+bool vmid_reserve(unsigned int vmid)
+{
+	unsigned int offset;
+	unsigned int vmid_count;
+
+	/* Number of supported VMID values */
+	vmid_count = is_feat_vmid16_present() ?	VMID16_COUNT : VMID8_COUNT;
+	/*
+	 * The input from NS as part of RMI_REALM_CREATE is 'short int' type,
+	 * so this check will not fail on systems with FEAT_VMID16 implemented.
+	 */
+	if (vmid >= vmid_count) {
+		return false;
+	}
+
+	offset = vmid / BITS_PER_UL;
+
+	return !atomic_bit_set_acquire_release_64(&vmids[offset], vmid);
+}
+
+/*
+ * Marks the VMID value to be not in use.
+ */
+void vmid_free(unsigned int vmid)
+{
+	unsigned int offset;
+	unsigned int __unused vmid_count;
+
+	/* Number of supported VMID values */
+	vmid_count = is_feat_vmid16_present() ? VMID16_COUNT : VMID8_COUNT;
+
+	/* Check the number of supported VMID values */
+	assert(vmid < vmid_count);
+	offset = vmid / BITS_PER_UL;
+
+	atomic_bit_clear_release_64(&vmids[offset], vmid);
+}
diff --git a/runtime/include/exit.h b/runtime/include/exit.h
new file mode 100644
index 0000000..10fa258
--- /dev/null
+++ b/runtime/include/exit.h
@@ -0,0 +1,16 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef EXIT_H
+#define EXIT_H
+
+#include <stdbool.h>
+
+struct rec;
+struct rmi_rec_exit;
+
+bool handle_realm_exit(struct rec *rec, struct rmi_rec_exit *rec_exit, int exception);
+
+#endif /* EXIT_H */
diff --git a/runtime/include/feature.h b/runtime/include/feature.h
new file mode 100644
index 0000000..b390813
--- /dev/null
+++ b/runtime/include/feature.h
@@ -0,0 +1,32 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef FEATURE_H
+#define FEATURE_H
+
+#include <arch.h>
+
+#define	RMM_FEATURE_MIN_IPA_SIZE		PARANGE_0000_WIDTH
+
+#define RMM_FEATURE_REGISTER_0_INDEX		UL(0)
+
+#define RMM_FEATURE_REGISTER_0_S2SZ_SHIFT	UL(0)
+#define RMM_FEATURE_REGISTER_0_S2SZ_WIDTH	UL(8)
+
+#define RMM_FEATURE_REGISTER_0_LPA2_SHIFT	UL(8)
+#define RMM_FEATURE_REGISTER_0_LPA2_WIDTH	UL(1)
+
+#define	RMI_NO_LPA2				UL(0)
+#define	RMI_LPA2				UL(1)
+
+#define RMM_FEATURE_REGISTER_0_HASH_SHA_256_SHIFT	UL(28)
+#define RMM_FEATURE_REGISTER_0_HASH_SHA_256_WIDTH	UL(1)
+
+#define RMM_FEATURE_REGISTER_0_HASH_SHA_512_SHIFT	UL(29)
+#define RMM_FEATURE_REGISTER_0_HASH_SHA_512_WIDTH	UL(1)
+
+bool validate_feature_register(unsigned long index, unsigned long value);
+
+#endif /* FEATURE_H */
diff --git a/runtime/include/inject_exp.h b/runtime/include/inject_exp.h
new file mode 100644
index 0000000..c899576
--- /dev/null
+++ b/runtime/include/inject_exp.h
@@ -0,0 +1,15 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef INJECT_EXP_H
+#define INJECT_EXP_H
+
+struct rec;
+
+void inject_sync_idabort(unsigned long fsc);
+void inject_sync_idabort_rec(struct rec *rec, unsigned long fsc);
+void realm_inject_undef_abort(void);
+
+#endif /* INJECT_EXP_H */
diff --git a/runtime/include/psci.h b/runtime/include/psci.h
new file mode 100644
index 0000000..e11b5e8
--- /dev/null
+++ b/runtime/include/psci.h
@@ -0,0 +1,119 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef PSCI_H
+#define PSCI_H
+
+#include <smc.h>
+#include <status.h>
+#include <stdbool.h>
+
+#define SMC32_PSCI_FID(_offset)		SMC32_STD_FID(PSCI, _offset)
+#define SMC64_PSCI_FID(_offset)		SMC64_STD_FID(PSCI, _offset)
+
+#define IS_SMC32_PSCI_FID(_fid)		IS_SMC32_STD_FAST_IN_RANGE(PSCI, _fid)
+#define IS_SMC64_PSCI_FID(_fid)		IS_SMC64_STD_FAST_IN_RANGE(PSCI, _fid)
+
+#define SMC32_PSCI_FID_MIN		SMC32_PSCI_FID(SMC32_PSCI_FNUM_MIN)
+#define SMC32_PSCI_FID_MAX		SMC32_PSCI_FID(SMC32_PSCI_FNUM_MAX)
+
+#define SMC64_PSCI_FID_MIN		SMC64_PSCI_FID(SMC64_PSCI_FNUM_MIN)
+#define SMC64_PSCI_FID_MAX		SMC64_PSCI_FID(SMC64_PSCI_FNUM_MAX)
+
+#define SMC32_PSCI_VERSION		SMC32_PSCI_FID(0x0U)
+
+#define SMC32_PSCI_CPU_SUSPEND		SMC32_PSCI_FID(0x1U)
+#define SMC64_PSCI_CPU_SUSPEND		SMC64_PSCI_FID(0x1U)
+
+#define SMC32_PSCI_CPU_OFF		SMC32_PSCI_FID(0x2U)
+
+#define SMC32_PSCI_CPU_ON		SMC32_PSCI_FID(0x3U)
+#define SMC64_PSCI_CPU_ON		SMC64_PSCI_FID(0x3U)
+
+#define SMC32_PSCI_AFFINITY_INFO	SMC32_PSCI_FID(0x4U)
+#define SMC64_PSCI_AFFINITY_INFO	SMC64_PSCI_FID(0x4U)
+
+#define SMC32_PSCI_MIGRATE		SMC32_PSCI_FID(0x5U)
+#define SMC64_PSCI_MIGRATE		SMC64_PSCI_FID(0x5U)
+
+#define SMC32_PSCI_MIGRATE_INFO_TYPE	SMC32_PSCI_FID(0x6U)
+
+#define SMC32_PSCI_MIGRATE_INFO_UP_CPU	SMC32_PSCI_FID(0x7U)
+#define SMC64_PSCI_MIGRATE_INFO_UP_CPU	SMC64_PSCI_FID(0x7U)
+
+#define SMC32_PSCI_SYSTEM_OFF		SMC32_PSCI_FID(0x8U)
+
+#define SMC32_PSCI_SYSTEM_RESET		SMC32_PSCI_FID(0x9U)
+
+#define SMC32_PSCI_FEATURES		SMC32_PSCI_FID(0xAU)
+
+#define SMC32_PSCI_CPU_FREEZE		SMC32_PSCI_FID(0xBU)
+
+#define SMC32_PSCI_CPU_DEFAULT_SUSPEND	SMC32_PSCI_FID(0xCU)
+#define SMC64_PSCI_CPU_DEFAULT_SUSPEND	SMC64_PSCI_FID(0xCU)
+
+#define SMC32_PSCI_NODE_HW_STATE	SMC32_PSCI_FID(0xDU)
+#define SMC64_PSCI_NODE_HW_STATE	SMC64_PSCI_FID(0xDU)
+
+#define SMC32_PSCI_SYSTEM_SUSPEND	SMC32_PSCI_FID(0xEU)
+#define SMC64_PSCI_SYSTEM_SUSPEND	SMC64_PSCI_FID(0xEU)
+
+#define SMC32_PSCI_SET_SUSPEND_MODE	SMC32_PSCI_FID(0xFU)
+
+#define SMC32_PSCI_STAT_RESIDENCY	SMC32_PSCI_FID(0x10U)
+#define SMC64_PSCI_STAT_RESIDENCY	SMC64_PSCI_FID(0x10U)
+
+#define SMC32_PSCI_STAT_COUNT		SMC32_PSCI_FID(0x11U)
+#define SMC64_PSCI_STAT_COUNT		SMC64_PSCI_FID(0x11U)
+
+#define SMC32_PSCI_SYSTEM_RESET2	SMC32_PSCI_FID(0x12U)
+#define SMC64_PSCI_SYSTEM_RESET2	SMC64_PSCI_FID(0x12U)
+
+#define SMC32_PSCI_MEM_PROTECT		SMC32_PSCI_FID(0x13U)
+
+#define SMC32_PSCI_MEM_PROTECT_CHECK_RANGE	SMC32_PSCI_FID(0x14U)
+#define SMC64_PSCI_MEM_PROTECT_CHECK_RANGE	SMC64_PSCI_FID(0x14U)
+
+#define PSCI_RETURN_SUCCESS		UL(0)
+#define PSCI_RETURN_NOT_SUPPORTED	UL(-1)
+#define PSCI_RETURN_INVALID_PARAMS	UL(-2)
+#define PSCI_RETURN_DENIED		UL(-3)
+#define PSCI_RETURN_ALREADY_ON		UL(-4)
+#define PSCI_RETURN_ON_PENDING		UL(-5)
+#define PSCI_RETURN_INTERNAL_FAILURE	UL(-6)
+#define PSCI_RETURN_NOT_PRESENT		UL(-7)
+#define PSCI_RETURN_DISABLED		UL(-8)
+#define PSCI_RETURN_INVALID_ADDRESS	UL(-9)
+
+#define PSCI_AFFINITY_INFO_ON		UL(0)
+#define PSCI_AFFINITY_INFO_OFF		UL(1)
+#define PSCI_AFFINITY_INFO_ON_PENDING	UL(2)
+
+#define PSCI_NODE_HW_ON			UL(0)
+#define PSCI_NODE_HW_OFF		UL(1)
+#define PSCI_NODE_HW_STANDBY		UL(2)
+
+struct rec;
+
+struct psci_result {
+	struct {
+		bool forward_psci_call;
+		unsigned long x1;
+		unsigned long x2;
+		unsigned long x3;
+	} hvc_forward;
+	struct smc_result smc_res;
+};
+
+struct psci_result psci_rsi(struct rec *rec,
+			    unsigned int function_id,
+			    unsigned long arg0,
+			    unsigned long arg1,
+			    unsigned long arg2);
+
+unsigned long psci_complete_request(struct rec *calling_rec,
+				    struct rec *target_rec);
+
+#endif /* PSCI_H */
diff --git a/runtime/include/realm_attest.h b/runtime/include/realm_attest.h
new file mode 100644
index 0000000..6e2d1b1
--- /dev/null
+++ b/runtime/include/realm_attest.h
@@ -0,0 +1,42 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef REALM_ATTEST_H
+#define REALM_ATTEST_H
+
+#include <rsi-walk.h>
+#include <stdbool.h>
+
+struct rec;
+
+struct attest_result {
+	/*
+	 * If true, RMM should repeat the operation.
+	 *
+	 * If false, contents of @access are valid.
+	 */
+	bool incomplete;
+
+	/*
+	 * Result of RTT walk performed by RSI command.
+	 */
+	struct rsi_walk_result walk_result;
+
+	/*
+	 * If @incomplete is false and @walk_result.abort is false,
+	 * @smc_result contains GPR values to be returned to the Realm.
+	 */
+	struct smc_result smc_res;
+};
+
+unsigned long handle_rsi_read_measurement(struct rec *rec);
+unsigned long handle_rsi_extend_measurement(struct rec *rec);
+unsigned long handle_rsi_attest_token_init(struct rec *rec);
+void attest_realm_token_sign_continue_start(void);
+void handle_rsi_attest_token_continue(struct rec *rec,
+				      struct attest_result *res);
+void attest_realm_token_sign_continue_finish(void);
+
+#endif /* REALM_ATTEST_H */
diff --git a/runtime/include/rsi-config.h b/runtime/include/rsi-config.h
new file mode 100644
index 0000000..aaa3fe5
--- /dev/null
+++ b/runtime/include/rsi-config.h
@@ -0,0 +1,29 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef RSI_CONFIG_H
+#define RSI_CONFIG_H
+
+#include <rsi-walk.h>
+#include <smc.h>
+
+struct rec;
+
+struct rsi_config_result {
+	/*
+	 * Result of RTT walk performed by RSI command.
+	 */
+	struct rsi_walk_result walk_result;
+
+	/*
+	 * If @walk_result.abort is false, @smc_res contains GPR values to be
+	 * returned to the Realm.
+	 */
+	struct smc_result smc_res;
+};
+
+struct rsi_config_result handle_rsi_realm_config(struct rec *rec);
+
+#endif /* RSI_CONFIG_H */
diff --git a/runtime/include/rsi-handler.h b/runtime/include/rsi-handler.h
new file mode 100644
index 0000000..0151229
--- /dev/null
+++ b/runtime/include/rsi-handler.h
@@ -0,0 +1,11 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef RSI_HANDLER_H
+#define RSI_HANDLER_H
+
+unsigned long system_rsi_abi_version(void);
+
+#endif /* RSI_HANDLER_H */
diff --git a/runtime/include/rsi-host-call.h b/runtime/include/rsi-host-call.h
new file mode 100644
index 0000000..aa58d47
--- /dev/null
+++ b/runtime/include/rsi-host-call.h
@@ -0,0 +1,34 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef RSI_HOST_CALL_H
+#define RSI_HOST_CALL_H
+
+#include <rsi-walk.h>
+#include <smc-rmi.h>
+
+struct rmi_rec_entry;
+struct rmi_rec_exit;
+
+struct rsi_host_call_result {
+	/*
+	 * Result of RTT walk performed by RSI command.
+	 */
+	struct rsi_walk_result walk_result;
+
+	/*
+	 * If @walk_result.abort is false,
+	 * @smc_result contains X0 value to be returned to the Realm.
+	 */
+	unsigned long smc_result;
+};
+
+struct rsi_host_call_result handle_rsi_host_call(struct rec *rec,
+						 struct rmi_rec_exit *rec_exit);
+
+struct rsi_walk_result complete_rsi_host_call(struct rec *rec,
+					      struct rmi_rec_entry *rec_entry);
+
+#endif /* RSI_HOST_CALL_H */
diff --git a/runtime/include/rsi-logger.h b/runtime/include/rsi-logger.h
new file mode 100644
index 0000000..86131d6
--- /dev/null
+++ b/runtime/include/rsi-logger.h
@@ -0,0 +1,41 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef RSI_LOGGER_H
+#define RSI_LOGGER_H
+
+#include <debug.h>
+
+/*
+ * RSI_LOG_LEVEL debug level is set to one of:
+ * LOG_LEVEL_NONE    = 0
+ * LOG_LEVEL_ERROR   = 10
+ * LOG_LEVEL_NOTICE  = 20
+ * LOG_LEVEL_WARNING = 30
+ * LOG_LEVEL_INFO    = 40
+ * LOG_LEVEL_VERBOSE = 50
+ */
+#if (RSI_LOG_LEVEL >= LOG_LEVEL_ERROR) && (RSI_LOG_LEVEL <= LOG_LEVEL)
+
+void rsi_log_on_exit(unsigned int function_id, unsigned long args[5],
+		     unsigned long res, bool exit_to_rec);
+
+/* Store SMC RSI parameters */
+# define RSI_LOG_SET(x0, x1, x2, x3, x4)	\
+	unsigned long rsi_log_args[5] = {x0, x1, x2, x3, x4}
+
+/*
+ * Macro prints RSI call function name, parameters
+ * and result when returning back to REC
+ */
+# define RSI_LOG_EXIT(id, res, ret)	\
+	rsi_log_on_exit(id, rsi_log_args, res, ret)
+
+#else
+# define RSI_LOG_SET(x0, x1, x2, x3, x4)
+# define RSI_LOG_EXIT(id, res, ret)
+
+#endif /* (>= LOG_LEVEL_ERROR) && (<= LOG_LEVEL) */
+#endif /* RSI_LOGGER_H */
diff --git a/runtime/include/rsi-memory.h b/runtime/include/rsi-memory.h
new file mode 100644
index 0000000..e74e15e
--- /dev/null
+++ b/runtime/include/rsi-memory.h
@@ -0,0 +1,19 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef	RSI_MEMORY_H
+#define	RSI_MEMORY_H
+
+#include <smc-rsi.h>
+
+struct rec;
+struct rmi_rec_exit;
+
+bool handle_rsi_ipa_state_set(struct rec *rec, struct rmi_rec_exit *rec_exit);
+
+rsi_status_t handle_rsi_ipa_state_get(struct rec *rec, unsigned long ipa,
+				      enum ripas *ripas);
+
+#endif /* RSI_MEMORY_H */
diff --git a/runtime/include/rsi-walk.h b/runtime/include/rsi-walk.h
new file mode 100644
index 0000000..01ff6c1
--- /dev/null
+++ b/runtime/include/rsi-walk.h
@@ -0,0 +1,20 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef RSI_WALK_H
+#define RSI_WALK_H
+
+struct rsi_walk_result {
+	/*
+	 * If true, RTT walk failed due to missing PTE at level @rtt_level.
+	 *
+	 * If false, @smc_result contains GPR values to be returned to the
+	 * Realm.
+	 */
+	bool abort;
+	unsigned long rtt_level;
+};
+
+#endif /* RSI_WALK_H */
diff --git a/runtime/include/run.h b/runtime/include/run.h
new file mode 100644
index 0000000..3c3d8ae
--- /dev/null
+++ b/runtime/include/run.h
@@ -0,0 +1,17 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef RUN_H
+#define RUN_H
+
+/*
+ * Function to enter Realm with `regs` pointing to GP Regs to be
+ * restored/saved when entering/exiting the Realm. This function
+ * returns with the Realm exception code which is populated by
+ * Realm_exit() on aarch64.
+ */
+int run_realm(unsigned long *regs);
+
+#endif /* RUN_H */
diff --git a/runtime/include/smc-handler.h b/runtime/include/smc-handler.h
new file mode 100644
index 0000000..e9e49fc
--- /dev/null
+++ b/runtime/include/smc-handler.h
@@ -0,0 +1,95 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef SMC_HANDLER_H
+#define SMC_HANDLER_H
+
+#include <smc.h>
+
+unsigned long smc_version(void);
+
+void smc_read_feature_register(unsigned long index,
+				struct smc_result *ret_struct);
+
+unsigned long smc_data_create(unsigned long data_addr,
+			      unsigned long rd_addr,
+			      unsigned long map_addr,
+			      unsigned long src_addr,
+			      unsigned long flags);
+
+unsigned long smc_data_create_unknown(unsigned long data_addr,
+				      unsigned long rd_addr,
+				      unsigned long map_addr);
+
+unsigned long smc_data_destroy(unsigned long rd_addr,
+			       unsigned long map_addr);
+
+unsigned long smc_granule_delegate(unsigned long addr);
+
+unsigned long smc_granule_undelegate(unsigned long addr);
+
+unsigned long smc_realm_activate(unsigned long rd_addr);
+
+unsigned long smc_realm_create(unsigned long rd_addr,
+			     unsigned long realm_params_addr);
+
+unsigned long smc_realm_destroy(unsigned long rd_addr);
+
+unsigned long smc_rec_create(unsigned long rec_addr,
+			     unsigned long rd_addr,
+			     unsigned long rec_params_addr);
+
+unsigned long smc_rec_destroy(unsigned long rec_addr);
+
+unsigned long smc_rec_enter(unsigned long rec_addr,
+			    unsigned long rec_run_addr);
+
+void smc_rec_aux_count(unsigned long rd_addr,
+			struct smc_result *ret_struct);
+
+unsigned long smc_rtt_create(unsigned long rtt_addr,
+			     unsigned long rd_addr,
+			     unsigned long map_addr,
+			     unsigned long ulevel);
+
+unsigned long smc_rtt_destroy(unsigned long rtt_addr,
+			      unsigned long rd_addr,
+			      unsigned long map_addr,
+			      unsigned long ulevel);
+
+unsigned long smc_rtt_fold(unsigned long rtt_addr,
+			   unsigned long rd_addr,
+			   unsigned long map_addr,
+			   unsigned long ulevel);
+
+unsigned long smc_rtt_map_unprotected(unsigned long rd_addr,
+				      unsigned long map_addr,
+				      unsigned long ulevel,
+				      unsigned long s2tte);
+
+unsigned long smc_rtt_unmap_unprotected(unsigned long rd_addr,
+					unsigned long map_addr,
+					unsigned long ulevel);
+
+void smc_rtt_read_entry(unsigned long rd_addr,
+			unsigned long map_addr,
+			unsigned long ulevel,
+			struct smc_result *ret_struct);
+
+unsigned long smc_psci_complete(unsigned long calling_rec_addr,
+				unsigned long target_rec_addr);
+
+unsigned long smc_rtt_init_ripas(unsigned long rd_addr,
+				 unsigned long map_addr,
+				 unsigned long ulevel);
+
+unsigned long smc_rtt_set_ripas(unsigned long rd_addr,
+				unsigned long rec_addr,
+				unsigned long map_addr,
+				unsigned long ulevel,
+				unsigned long uripas);
+
+
+#endif /* SMC_HANDLER_H */
diff --git a/runtime/include/sysreg_traps.h b/runtime/include/sysreg_traps.h
new file mode 100644
index 0000000..2fc0a84
--- /dev/null
+++ b/runtime/include/sysreg_traps.h
@@ -0,0 +1,15 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef SYSREGS_H
+#define SYSREGS_H
+
+struct rec;
+struct rmi_rec_exit;
+
+bool handle_sysreg_access_trap(struct rec *rec, struct rmi_rec_exit *rec_exit,
+			       unsigned long esr);
+
+#endif /* SYSREGS_H */
diff --git a/runtime/linker.lds b/runtime/linker.lds
new file mode 100644
index 0000000..0928fae
--- /dev/null
+++ b/runtime/linker.lds
@@ -0,0 +1,106 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <sizes.h>
+
+ENTRY(rmm_entry)
+
+MEMORY {
+	RAM (rwx): ORIGIN = 0x0, LENGTH = RMM_MAX_SIZE
+}
+
+SECTIONS
+{
+	rmm_base = .;
+
+	.text . : {
+		rmm_text_start = .;
+		*head.S.obj(.text*)
+		. = ALIGN(8);
+		*(.text*)
+		. = ALIGN(GRANULE_SIZE);
+	} >RAM
+
+	rmm_text_end = .;
+
+	ASSERT(rmm_text_end == ALIGN(GRANULE_SIZE), "rmm_text_end is not page aligned")
+
+	.rodata ALIGN(GRANULE_SIZE) : {
+		rmm_ro_start = .;
+		*(.rodata*)
+		. = ALIGN(8);
+		rmm_got_start = .;
+		*(.got)
+		rmm_got_end = .;
+	} >RAM
+
+	/*
+	 * The xlat_static_table section is for full, aligned page tables.
+	 * The static tables must not change once the MMU is enabled, so
+	 * allocate them on the RO area to keep them protected from writing.
+	 *
+	 * The memory will be cleared by the xlat library during start up.
+	 */
+	xlat_table ALIGN(GRANULE_SIZE) : {
+		*(xlat_static_tables)
+	} >RAM
+
+	rmm_ro_end = .;
+
+	ASSERT(rmm_ro_end == ALIGN(GRANULE_SIZE), "rmm_ro_end is not page aligned")
+
+	/* Align rw data to the next 2MB block */
+	.data ALIGN(SZ_2M) : {
+		rmm_rw_start = .;
+		*(.data*)
+	} >RAM
+
+	/*
+	 * .rela.dyn needs to come after .data for the read-elf utility to
+	 * parse this section correctly.
+	 */
+	.rela.dyn ALIGN(8) : {
+		rmm_rela_start = .;
+		*(.rela*)
+		rmm_rela_end = .;
+	} >RAM
+
+	.percpu ALIGN(GRANULE_SIZE) (NOLOAD) : {
+		stack_start = .;
+		. = . + (RMM_NUM_PAGES_PER_STACK * GRANULE_SIZE * MAX_CPUS);
+		stack_end = .;
+	} >RAM
+
+	.bss ALIGN(16) (NOLOAD) : {
+		bss_start = .;
+		*(.bss*)
+		bss_end = .;
+	} >RAM
+
+	/*
+	 * The slot_buffer_xlat_tbl section is for full, aligned page tables.
+	 * The dynamic tables are used for transient memory areas that can
+	 * change at any time, so the tables must have RW access.
+	 *
+	 * The tables will be erased by the xlat library during start up.
+	 */
+	slot_buffer_xlat_tbl ALIGN(GRANULE_SIZE) (NOLOAD) : {
+		*(slot_buffer_xlat_tbls)
+	} >RAM
+
+	rmm_rw_end = .;
+	rmm_end = rmm_rw_end;
+
+	ASSERT(rmm_rw_end == ALIGN(GRANULE_SIZE), "rmm_rw_end is not page aligned")
+
+	/DISCARD/ : { *(.dynstr*) }
+	/DISCARD/ : { *(.dynsym*) }
+	/DISCARD/ : { *(.dynamic*) }
+	/DISCARD/ : { *(.hash*) }
+	/DISCARD/ : { *(.plt*) }
+	/DISCARD/ : { *(.interp*) }
+	/DISCARD/ : { *(.gnu*) }
+	/DISCARD/ : { *(.note*) }
+}
diff --git a/runtime/rmi/feature.c b/runtime/rmi/feature.c
new file mode 100644
index 0000000..fa52ba8
--- /dev/null
+++ b/runtime/rmi/feature.c
@@ -0,0 +1,73 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <arch_features.h>
+#include <assert.h>
+#include <feature.h>
+#include <smc-handler.h>
+#include <smc-rmi.h>
+#include <status.h>
+
+static unsigned long get_feature_register_0(void)
+{
+	/* Set S2SZ field */
+	unsigned long s2sz = arch_feat_get_pa_width();
+	unsigned long feat_reg0 = INPLACE(RMM_FEATURE_REGISTER_0_S2SZ, s2sz);
+
+	/* Set LPA2 field */
+	if (is_feat_lpa2_4k_present()) {
+		feat_reg0 |= INPLACE(RMM_FEATURE_REGISTER_0_LPA2, RMI_LPA2);
+	}
+
+	/* Set support for SHA256 and SHA512 hash algorithms */
+	feat_reg0 |= INPLACE(RMM_FEATURE_REGISTER_0_HASH_SHA_256, 1);
+	feat_reg0 |= INPLACE(RMM_FEATURE_REGISTER_0_HASH_SHA_512, 1);
+
+	return feat_reg0;
+}
+
+void smc_read_feature_register(unsigned long index,
+				struct smc_result *ret_struct)
+{
+	switch (index) {
+	case RMM_FEATURE_REGISTER_0_INDEX:
+		ret_struct->x[0] = RMI_SUCCESS;
+		ret_struct->x[1] = get_feature_register_0();
+		break;
+	default:
+		ret_struct->x[0] = RMI_ERROR_INPUT;
+	}
+}
+
+static bool validate_feature_register_0(unsigned long value)
+{
+	unsigned long feat_reg0 = get_feature_register_0();
+	unsigned long s2sz = EXTRACT(RMM_FEATURE_REGISTER_0_S2SZ, value);
+
+	/* Validate S2SZ field */
+	if ((s2sz < RMM_FEATURE_MIN_IPA_SIZE) ||
+	    (s2sz > EXTRACT(RMM_FEATURE_REGISTER_0_S2SZ, feat_reg0))) {
+		return false;
+	}
+
+	/* Validate LPA2 flag */
+	if ((EXTRACT(RMM_FEATURE_REGISTER_0_LPA2, value) == RMI_LPA2) &&
+	    !is_feat_lpa2_4k_present()) {
+		return false;
+	}
+
+	return true;
+}
+
+bool validate_feature_register(unsigned long index, unsigned long value)
+{
+	switch (index) {
+	case RMM_FEATURE_REGISTER_0_INDEX:
+		return validate_feature_register_0(value);
+	default:
+		assert(false);
+		return false;
+	}
+}
diff --git a/runtime/rmi/granule.c b/runtime/rmi/granule.c
new file mode 100644
index 0000000..991905c
--- /dev/null
+++ b/runtime/rmi/granule.c
@@ -0,0 +1,43 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <asc.h>
+#include <granule.h>
+#include <smc-handler.h>
+#include <smc-rmi.h>
+#include <smc.h>
+
+unsigned long smc_granule_delegate(unsigned long addr)
+{
+	struct granule *g;
+
+	g = find_lock_granule(addr, GRANULE_STATE_NS);
+	if (g == NULL) {
+		return RMI_ERROR_INPUT;
+	}
+
+	granule_set_state(g, GRANULE_STATE_DELEGATED);
+	asc_mark_secure(addr);
+	granule_memzero(g, SLOT_DELEGATED);
+
+	granule_unlock(g);
+	return RMI_SUCCESS;
+}
+
+unsigned long smc_granule_undelegate(unsigned long addr)
+{
+	struct granule *g;
+
+	g = find_lock_granule(addr, GRANULE_STATE_DELEGATED);
+	if (g == NULL) {
+		return RMI_ERROR_INPUT;
+	}
+
+	asc_mark_nonsecure(addr);
+	granule_set_state(g, GRANULE_STATE_NS);
+
+	granule_unlock(g);
+	return RMI_SUCCESS;
+}
diff --git a/runtime/rmi/realm.c b/runtime/rmi/realm.c
new file mode 100644
index 0000000..b348e90
--- /dev/null
+++ b/runtime/rmi/realm.c
@@ -0,0 +1,392 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <assert.h>
+#include <buffer.h>
+#include <feature.h>
+#include <granule.h>
+#include <measurement.h>
+#include <realm.h>
+#include <smc-handler.h>
+#include <smc-rmi.h>
+#include <smc.h>
+#include <stddef.h>
+#include <string.h>
+#include <table.h>
+#include <vmid.h>
+
+unsigned long smc_realm_activate(unsigned long rd_addr)
+{
+	struct rd *rd;
+	struct granule *g_rd;
+	unsigned long ret;
+
+	g_rd = find_lock_granule(rd_addr, GRANULE_STATE_RD);
+	if (g_rd == NULL) {
+		return RMI_ERROR_INPUT;
+	}
+
+	rd = granule_map(g_rd, SLOT_RD);
+	if (get_rd_state_locked(rd) == REALM_STATE_NEW) {
+		set_rd_state(rd, REALM_STATE_ACTIVE);
+		ret = RMI_SUCCESS;
+	} else {
+		ret = RMI_ERROR_REALM;
+	}
+	buffer_unmap(rd);
+
+	granule_unlock(g_rd);
+
+	return ret;
+}
+
+static bool get_realm_params(struct rmi_realm_params *realm_params,
+				unsigned long realm_params_addr)
+{
+	bool ns_access_ok;
+	struct granule *g_realm_params;
+
+	g_realm_params = find_granule(realm_params_addr);
+	if ((g_realm_params == NULL) || (g_realm_params->state != GRANULE_STATE_NS)) {
+		return false;
+	}
+
+	ns_access_ok = ns_buffer_read(SLOT_NS, g_realm_params, 0U,
+				      sizeof(*realm_params), realm_params);
+
+	return ns_access_ok;
+}
+
+/*
+ * See the library pseudocode
+ * aarch64/translation/vmsa_faults/AArch64.S2InconsistentSL on which this is
+ * modeled.
+ */
+static bool s2_inconsistent_sl(unsigned int ipa_bits, int sl)
+{
+	int levels = RTT_PAGE_LEVEL - sl;
+	unsigned int sl_min_ipa_bits, sl_max_ipa_bits;
+
+	/*
+	 * The maximum number of concatenated tables is 16,
+	 * hence we are adding 4 to the 'sl_max_ipa_bits'.
+	 */
+	sl_min_ipa_bits = levels * S2TTE_STRIDE + GRANULE_SHIFT + 1U;
+	sl_max_ipa_bits = sl_min_ipa_bits + (S2TTE_STRIDE - 1U) + 4U;
+
+	return ((ipa_bits < sl_min_ipa_bits) || (ipa_bits > sl_max_ipa_bits));
+}
+
+static bool validate_ipa_bits_and_sl(unsigned int ipa_bits, long sl)
+{
+	if ((ipa_bits < MIN_IPA_BITS) || (ipa_bits > MAX_IPA_BITS)) {
+		return false;
+	}
+
+	if ((sl < MIN_STARTING_LEVEL) || (sl > RTT_PAGE_LEVEL)) {
+		return false;
+	}
+
+	/*
+	 * We assume ARMv8.4-TTST is supported with RME so the only SL
+	 * configuration we need to check with 4K granules is SL == 0 following
+	 * the library pseudocode aarch64/translation/vmsa_faults/AArch64.S2InvalidSL.
+	 *
+	 * Note that this only checks invalid SL values against the properties
+	 * of the hardware platform, other misconfigurations between IPA size
+	 * and SL is checked in s2_inconsistent_sl.
+	 */
+	if ((sl == 0L) && (max_ipa_size() < 44U)) {
+		return false;
+	}
+
+	return !s2_inconsistent_sl(ipa_bits, sl);
+}
+
+static unsigned int requested_ipa_bits(struct rmi_realm_params *p)
+{
+	return EXTRACT(RMM_FEATURE_REGISTER_0_S2SZ, p->features_0);
+}
+
+static unsigned int s2_num_root_rtts(unsigned int ipa_bits, int sl)
+{
+	unsigned int levels = (unsigned int)(RTT_PAGE_LEVEL - sl);
+	unsigned int sl_ipa_bits;
+
+	/* First calculate how many bits can be resolved without concatenation */
+	sl_ipa_bits = levels * S2TTE_STRIDE /* Bits resolved by table walk without SL */
+		      + GRANULE_SHIFT	    /* Bits directly mapped to OA */
+		      + S2TTE_STRIDE;	    /* Bits resolved by single SL */
+
+	if (sl_ipa_bits >= ipa_bits) {
+		return 1U;
+	}
+
+	return (1U << (ipa_bits - sl_ipa_bits));
+}
+
+static bool validate_realm_params(struct rmi_realm_params *p)
+{
+	if (!validate_feature_register(RMM_FEATURE_REGISTER_0_INDEX,
+					p->features_0)) {
+		return false;
+	}
+
+	if (!validate_ipa_bits_and_sl(requested_ipa_bits(p),
+					p->rtt_level_start)) {
+		return false;
+	}
+
+	if (s2_num_root_rtts(requested_ipa_bits(p),
+				p->rtt_level_start) != p->rtt_num_start) {
+		return false;
+	}
+
+	/*
+	 * TODO: Check the VMSA configuration which is either static for the
+	 * RMM or per realm with the supplied parameters and store the
+	 * configuration on the RD, and it can potentially be copied into RECs
+	 * later.
+	 */
+
+	switch (p->hash_algo) {
+	case RMI_HASH_ALGO_SHA256:
+	case RMI_HASH_ALGO_SHA512:
+		break;
+	default:
+		return false;
+	}
+
+	/* Check VMID collision and reserve it atomically if available */
+	return vmid_reserve((unsigned int)p->vmid);
+}
+
+/*
+ * Update the realm measurement with the realm parameters.
+ */
+static void realm_params_measure(struct rd *rd,
+				 struct rmi_realm_params *realm_params)
+{
+	/* By specification realm_params is 4KB */
+	unsigned char buffer[SZ_4K] = {0};
+	struct rmi_realm_params *realm_params_measured =
+		(struct rmi_realm_params *)&buffer[0];
+
+	realm_params_measured->hash_algo = realm_params->hash_algo;
+	/* TODO: Add later */
+	/* realm_params_measured->features_0 = realm_params->features_0; */
+
+	/* Measure relevant realm params this will be the init value of RIM */
+	measurement_hash_compute(rd->algorithm,
+			       buffer,
+			       sizeof(buffer),
+			       rd->measurement[RIM_MEASUREMENT_SLOT]);
+}
+
+static void free_sl_rtts(struct granule *g_rtt, unsigned int num_rtts)
+{
+	unsigned int i;
+
+	for (i = 0U; i < num_rtts; i++) {
+		struct granule *g = g_rtt + i;
+
+		granule_lock(g, GRANULE_STATE_RTT);
+		granule_memzero(g, SLOT_RTT);
+		granule_unlock_transition(g, GRANULE_STATE_DELEGATED);
+	}
+}
+
+static bool find_lock_rd_granules(unsigned long rd_addr,
+				  struct granule **p_g_rd,
+				  unsigned long rtt_base_addr,
+				  unsigned int num_rtts,
+				  struct granule **p_g_rtt_base)
+{
+	struct granule *g_rd = NULL, *g_rtt_base = NULL;
+	int i = 0;
+
+	if (rd_addr < rtt_base_addr) {
+		g_rd = find_lock_granule(rd_addr, GRANULE_STATE_DELEGATED);
+		if (g_rd == NULL) {
+			goto out_err;
+		}
+	}
+
+	for (; i < num_rtts; i++) {
+		unsigned long rtt_addr = rtt_base_addr + i * GRANULE_SIZE;
+		struct granule *g_rtt;
+
+		g_rtt = find_lock_granule(rtt_addr, GRANULE_STATE_DELEGATED);
+		if (g_rtt == NULL) {
+			goto out_err;
+		}
+
+		if (i == 0) {
+			g_rtt_base = g_rtt;
+		}
+	}
+
+	if (g_rd == NULL) {
+		g_rd = find_lock_granule(rd_addr, GRANULE_STATE_DELEGATED);
+		if (g_rd == NULL) {
+			goto out_err;
+		}
+	}
+
+	*p_g_rd = g_rd;
+	*p_g_rtt_base = g_rtt_base;
+
+	return true;
+
+out_err:
+	for (i = i - 1; i >= 0; i--) {
+		granule_unlock(g_rtt_base + i);
+	}
+
+	if (g_rd != NULL) {
+		granule_unlock(g_rd);
+	}
+
+	return false;
+}
+
+unsigned long smc_realm_create(unsigned long rd_addr,
+			       unsigned long realm_params_addr)
+{
+	struct granule *g_rd, *g_rtt_base;
+	struct rd *rd;
+	struct rmi_realm_params p;
+	unsigned int i;
+
+	if (!get_realm_params(&p, realm_params_addr)) {
+		return RMI_ERROR_INPUT;
+	}
+
+	if (!validate_realm_params(&p)) {
+		return RMI_ERROR_INPUT;
+	}
+
+	/*
+	 * At this point VMID is reserved for the Realm
+	 *
+	 * Check for aliasing between rd_addr and
+	 * starting level RTT address(es)
+	 */
+	if (addr_is_contained(p.rtt_base,
+			      p.rtt_base + p.rtt_num_start * GRANULE_SIZE,
+			      rd_addr)) {
+
+		/* Free reserved VMID before returning */
+		vmid_free((unsigned int)p.vmid);
+		return RMI_ERROR_INPUT;
+	}
+
+	if (!find_lock_rd_granules(rd_addr, &g_rd, p.rtt_base,
+				  p.rtt_num_start, &g_rtt_base)) {
+		/* Free reserved VMID */
+		vmid_free((unsigned int)p.vmid);
+		return RMI_ERROR_INPUT;
+	}
+
+	rd = granule_map(g_rd, SLOT_RD);
+	set_rd_state(rd, REALM_STATE_NEW);
+	set_rd_rec_count(rd, 0UL);
+	rd->s2_ctx.g_rtt = find_granule(p.rtt_base);
+	rd->s2_ctx.ipa_bits = requested_ipa_bits(&p);
+	rd->s2_ctx.s2_starting_level = p.rtt_level_start;
+	rd->s2_ctx.num_root_rtts = p.rtt_num_start;
+	memcpy(&rd->rpv[0], &p.rpv[0], RPV_SIZE);
+
+	rd->s2_ctx.vmid = (unsigned int)p.vmid;
+
+	rd->num_rec_aux = MAX_REC_AUX_GRANULES;
+
+	(void)memcpy(&rd->rpv[0], &p.rpv[0], RPV_SIZE);
+
+	rd->algorithm = p.hash_algo;
+
+	switch (p.hash_algo) {
+	case RMI_HASH_ALGO_SHA256:
+		rd->algorithm = HASH_ALGO_SHA256;
+		break;
+	case RMI_HASH_ALGO_SHA512:
+		rd->algorithm = HASH_ALGO_SHA512;
+		break;
+	}
+	realm_params_measure(rd, &p);
+
+	buffer_unmap(rd);
+
+	granule_unlock_transition(g_rd, GRANULE_STATE_RD);
+
+	for (i = 0U; i < p.rtt_num_start; i++) {
+		granule_unlock_transition(g_rtt_base + i, GRANULE_STATE_RTT);
+	}
+
+	return RMI_SUCCESS;
+}
+
+static unsigned long total_root_rtt_refcount(struct granule *g_rtt,
+					     unsigned int num_rtts)
+{
+	unsigned long refcount = 0UL;
+	unsigned int i;
+
+	for (i = 0U; i < num_rtts; i++) {
+		struct granule *g = g_rtt + i;
+
+	       /*
+		* Lock starting from the RTT root.
+		* Enforcing locking order RD->RTT is enough to ensure
+		* deadlock free locking guarentee.
+		*/
+		granule_lock(g, GRANULE_STATE_RTT);
+		refcount += g->refcount;
+		granule_unlock(g);
+	}
+
+	return refcount;
+}
+
+unsigned long smc_realm_destroy(unsigned long rd_addr)
+{
+	struct granule *g_rd;
+	struct granule *g_rtt;
+	struct rd *rd;
+	unsigned int num_rtts;
+
+	/* RD should not be destroyed if refcount != 0. */
+	g_rd = find_lock_unused_granule(rd_addr, GRANULE_STATE_RD);
+	if (ptr_is_err(g_rd)) {
+		return (unsigned long)ptr_status(g_rd);
+	}
+
+	rd = granule_map(g_rd, SLOT_RD);
+	g_rtt = rd->s2_ctx.g_rtt;
+	num_rtts = rd->s2_ctx.num_root_rtts;
+
+	/*
+	 * All the mappings in the Realm have been removed and the TLB caches
+	 * are invalidated. Therefore, there are no TLB entries tagged with
+	 * this Realm's VMID (in this security state).
+	 * Just release the VMID value so it can be used in another Realm.
+	 */
+	vmid_free(rd->s2_ctx.vmid);
+	buffer_unmap(rd);
+
+	/* Check if granules are unused */
+	if (total_root_rtt_refcount(g_rtt, num_rtts) != 0UL) {
+		granule_unlock(g_rd);
+		return RMI_ERROR_IN_USE;
+	}
+
+	free_sl_rtts(g_rtt, num_rtts);
+
+	/* This implictly destroys the measurement */
+	granule_memzero(g_rd, SLOT_RD);
+	granule_unlock_transition(g_rd, GRANULE_STATE_DELEGATED);
+
+	return RMI_SUCCESS;
+}
diff --git a/runtime/rmi/rec.c b/runtime/rmi/rec.c
new file mode 100644
index 0000000..5f82ad1
--- /dev/null
+++ b/runtime/rmi/rec.c
@@ -0,0 +1,416 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <arch.h>
+#include <arch_features.h>
+#include <attestation.h>
+#include <buffer.h>
+#include <cpuid.h>
+#include <gic.h>
+#include <granule.h>
+#include <mbedtls/memory_buffer_alloc.h>
+#include <measurement.h>
+#include <memory_alloc.h>
+#include <psci.h>
+#include <realm.h>
+#include <rec.h>
+#include <smc-handler.h>
+#include <smc-rmi.h>
+#include <smc.h>
+#include <spinlock.h>
+#include <stddef.h>
+#include <string.h>
+
+/*
+ * Allocate a dummy rec_params for copying relevant parameters for measurement
+ */
+static struct rmi_rec_params rec_params_per_cpu[MAX_CPUS];
+
+static void rec_params_measure(struct rd *rd, struct rmi_rec_params *rec_params)
+{
+	struct measurement_desc_rec measure_desc = {0};
+	struct rmi_rec_params *rec_params_measured =
+		&(rec_params_per_cpu[my_cpuid()]);
+
+	memset(rec_params_measured, 0, sizeof(*rec_params_measured));
+
+	/* Copy the relevant parts of the rmi_rec_params structure to be
+	 * measured
+	 */
+	rec_params_measured->pc = rec_params->pc;
+	rec_params_measured->flags = rec_params->flags;
+	memcpy(rec_params_measured->gprs,
+	       rec_params->gprs,
+	       sizeof(rec_params->gprs));
+
+	/* Initialize the measurement descriptior structure */
+	measure_desc.desc_type = MEASURE_DESC_TYPE_REC;
+	measure_desc.len = sizeof(struct measurement_desc_rec);
+	memcpy(measure_desc.rim,
+	       &rd->measurement[RIM_MEASUREMENT_SLOT],
+	       measurement_get_size(rd->algorithm));
+
+	/*
+	 * Hashing the REC params structure and store the result in the
+	 * measurement descriptor structure.
+	 */
+	measurement_hash_compute(rd->algorithm,
+				rec_params_measured,
+				sizeof(*rec_params_measured),
+				measure_desc.content);
+
+	/*
+	 * Hashing the measurement descriptor structure; the result is the
+	 * updated RIM.
+	 */
+	measurement_hash_compute(rd->algorithm,
+			       &measure_desc,
+			       sizeof(measure_desc),
+			       rd->measurement[RIM_MEASUREMENT_SLOT]);
+}
+
+static void init_rec_sysregs(struct rec *rec, unsigned long mpidr)
+{
+	/* Set non-zero values only */
+	rec->sysregs.pmcr_el0 = PMCR_EL0_RES1;
+	rec->sysregs.sctlr_el1 = SCTLR_EL1_FLAGS;
+	rec->sysregs.mdscr_el1 = MDSCR_EL1_TDCC_BIT;
+	rec->sysregs.vmpidr_el2 = mpidr | VMPIDR_EL2_RES1;
+
+	rec->sysregs.cnthctl_el2 = CNTHCTL_EL2_NO_TRAPS;
+}
+
+/*
+ * Starting level of the stage 2 translation
+ * lookup to VTCR_EL2.SL0[7:6].
+ */
+static const unsigned long sl0_val[] = {
+	VTCR_SL0_4K_L0,
+	VTCR_SL0_4K_L1,
+	VTCR_SL0_4K_L2,
+	VTCR_SL0_4K_L3
+};
+
+static unsigned long realm_vtcr(struct rd *rd)
+{
+	unsigned long t0sz, sl0;
+	unsigned long vtcr = is_feat_vmid16_present() ?
+				(VTCR_FLAGS | VTCR_VS) : VTCR_FLAGS;
+	int s2_starting_level = realm_rtt_starting_level(rd);
+
+	/* TODO: Support LPA2 with -1 */
+	assert((s2_starting_level >= 0) && (s2_starting_level <= 3));
+	sl0 = sl0_val[s2_starting_level];
+
+	t0sz = 64UL - realm_ipa_bits(rd);
+	t0sz &= MASK(VTCR_T0SZ);
+
+	vtcr |= t0sz;
+	vtcr |= sl0;
+
+	return vtcr;
+}
+
+static void init_common_sysregs(struct rec *rec, struct rd *rd)
+{
+	/* Set non-zero values only */
+	rec->common_sysregs.hcr_el2 = HCR_FLAGS;
+	rec->common_sysregs.vtcr_el2 =  realm_vtcr(rd);
+	rec->common_sysregs.vttbr_el2 = granule_addr(rd->s2_ctx.g_rtt);
+	rec->common_sysregs.vttbr_el2 &= MASK(TTBRx_EL2_BADDR);
+	rec->common_sysregs.vttbr_el2 |= INPLACE(VTTBR_EL2_VMID, rd->s2_ctx.vmid);
+}
+
+static void init_rec_regs(struct rec *rec,
+			  struct rmi_rec_params *rec_params,
+			  struct rd *rd)
+{
+	unsigned int i;
+
+	/*
+	 * We only need to set non-zero values here because we're intializing
+	 * data structures in the rec granule which was just converted from
+	 * the DELEGATED state to REC state, and we can rely on the RMM
+	 * invariant that DELEGATED granules are always zero-filled.
+	 */
+
+	for (i = 0U; i < REC_CREATE_NR_GPRS; i++) {
+		rec->regs[i] = rec_params->gprs[i];
+	}
+
+	rec->pc = rec_params->pc;
+	rec->pstate = SPSR_EL2_MODE_EL1h |
+		      SPSR_EL2_nRW_AARCH64 |
+		      SPSR_EL2_F_BIT |
+		      SPSR_EL2_I_BIT |
+		      SPSR_EL2_A_BIT |
+		      SPSR_EL2_D_BIT;
+
+	init_rec_sysregs(rec, rec_params->mpidr);
+	init_common_sysregs(rec, rd);
+}
+
+/*
+ * This function will only be invoked when the REC create fails
+ * or when REC is being destroyed. Hence the REC will not be in
+ * use when this function is called and therefore no lock is
+ * acquired before its invocation.
+ */
+static void free_rec_aux_granules(struct granule *rec_aux[],
+				  unsigned int cnt, bool scrub)
+{
+	for (unsigned int i = 0U; i < cnt; i++) {
+		struct granule *g_rec_aux = rec_aux[i];
+
+		granule_lock(g_rec_aux, GRANULE_STATE_REC_AUX);
+		if (scrub) {
+			granule_memzero(g_rec_aux, SLOT_REC_AUX0 + i);
+		}
+		granule_unlock_transition(g_rec_aux, GRANULE_STATE_DELEGATED);
+	}
+}
+
+unsigned long smc_rec_create(unsigned long rec_addr,
+			     unsigned long rd_addr,
+			     unsigned long rec_params_addr)
+{
+	struct granule *g_rd;
+	struct granule *g_rec;
+	struct granule *rec_aux_granules[MAX_REC_AUX_GRANULES];
+	struct granule *g_rec_params;
+	struct rec *rec;
+	struct rd *rd;
+	struct rmi_rec_params rec_params;
+	unsigned long rec_idx;
+	enum granule_state new_rec_state = GRANULE_STATE_DELEGATED;
+	unsigned long ret;
+	bool ns_access_ok;
+	unsigned int num_rec_aux;
+
+	g_rec_params = find_granule(rec_params_addr);
+	if ((g_rec_params == NULL) || (g_rec_params->state != GRANULE_STATE_NS)) {
+		return RMI_ERROR_INPUT;
+	}
+
+	ns_access_ok = ns_buffer_read(SLOT_NS, g_rec_params, 0U,
+				      sizeof(rec_params), &rec_params);
+
+	if (!ns_access_ok) {
+		return RMI_ERROR_INPUT;
+	}
+
+	num_rec_aux = (unsigned int)rec_params.num_aux;
+	if (num_rec_aux > MAX_REC_AUX_GRANULES) {
+		return RMI_ERROR_INPUT;
+	}
+
+	/* Loop through rec_aux_granules and transit them */
+	for (unsigned int i = 0U; i < num_rec_aux; i++) {
+		struct granule *g_rec_aux = find_lock_granule(
+						rec_params.aux[i],
+						GRANULE_STATE_DELEGATED);
+		if (g_rec_aux == NULL) {
+			free_rec_aux_granules(rec_aux_granules, i, false);
+			return RMI_ERROR_INPUT;
+		}
+		granule_unlock_transition(g_rec_aux, GRANULE_STATE_REC_AUX);
+		rec_aux_granules[i] = g_rec_aux;
+	}
+
+	if (!find_lock_two_granules(rec_addr,
+				GRANULE_STATE_DELEGATED,
+				&g_rec,
+				rd_addr,
+				GRANULE_STATE_RD,
+				&g_rd)) {
+		ret = RMI_ERROR_INPUT;
+		goto out_free_aux;
+	}
+
+	rec = granule_map(g_rec, SLOT_REC);
+	rd = granule_map(g_rd, SLOT_RD);
+
+	if (get_rd_state_locked(rd) != REALM_STATE_NEW) {
+		ret = RMI_ERROR_REALM;
+		goto out_unmap;
+	}
+
+	rec_idx = get_rd_rec_count_locked(rd);
+	if (!mpidr_is_valid(rec_params.mpidr) ||
+	   (rec_idx != mpidr_to_rec_idx(rec_params.mpidr))) {
+		ret = RMI_ERROR_INPUT;
+		goto out_unmap;
+	}
+
+	/* Verify the auxiliary granule count with rd lock held */
+	if (num_rec_aux != rd->num_rec_aux) {
+		ret = RMI_ERROR_INPUT;
+		goto out_unmap;
+	}
+
+	rec->g_rec = g_rec;
+	rec->rec_idx = rec_idx;
+
+	init_rec_regs(rec, &rec_params, rd);
+	gic_cpu_state_init(&rec->sysregs.gicstate);
+
+	/* Copy addresses of auxiliary granules */
+	(void)memcpy(rec->g_aux, rec_aux_granules,
+			num_rec_aux * sizeof(rec->g_aux[0]));
+
+	rec->num_rec_aux = num_rec_aux;
+
+	rec->realm_info.ipa_bits = realm_ipa_bits(rd);
+	rec->realm_info.s2_starting_level = realm_rtt_starting_level(rd);
+	rec->realm_info.g_rtt = rd->s2_ctx.g_rtt;
+	rec->realm_info.g_rd = g_rd;
+
+	rec_params_measure(rd, &rec_params);
+
+	/*
+	 * RD has a lock-free access from RMI_REC_DESTROY, hence increment
+	 * refcount atomically. Also, since the granule is only used for
+	 * refcount update, only an atomic operation will suffice and
+	 * release/acquire semantics are not required.
+	 */
+	atomic_granule_get(g_rd);
+	new_rec_state = GRANULE_STATE_REC;
+	rec->runnable = rec_params.flags & REC_PARAMS_FLAG_RUNNABLE;
+
+	rec->alloc_info.ctx_initialised = false;
+	/* Initialize attestation state */
+	rec->token_sign_ctx.state = ATTEST_SIGN_NOT_STARTED;
+
+	set_rd_rec_count(rd, rec_idx + 1U);
+
+	ret = RMI_SUCCESS;
+
+out_unmap:
+	buffer_unmap(rd);
+	buffer_unmap(rec);
+
+	granule_unlock(g_rd);
+	granule_unlock_transition(g_rec, new_rec_state);
+
+out_free_aux:
+	if (ret != RMI_SUCCESS) {
+		free_rec_aux_granules(rec_aux_granules, num_rec_aux, false);
+	}
+	return ret;
+}
+
+unsigned long smc_rec_destroy(unsigned long rec_addr)
+{
+	struct granule *g_rec;
+	struct granule *g_rd;
+	struct rec *rec;
+
+	/* REC should not be destroyed if refcount != 0 */
+	g_rec = find_lock_unused_granule(rec_addr, GRANULE_STATE_REC);
+	if (ptr_is_err(g_rec)) {
+		return (unsigned long)ptr_status(g_rec);
+	}
+
+	rec = granule_map(g_rec, SLOT_REC);
+
+	g_rd = rec->realm_info.g_rd;
+
+	/* Free and scrub the auxiliary granules */
+	free_rec_aux_granules(rec->g_aux, rec->num_rec_aux, true);
+
+	granule_memzero_mapped(rec);
+	buffer_unmap(rec);
+
+	granule_unlock_transition(g_rec, GRANULE_STATE_DELEGATED);
+
+	/*
+	 * Decrement refcount. The refcount should be balanced before
+	 * RMI_REC_DESTROY returns, and until this occurs a transient
+	 * over-estimate of the refcount (in-between the unlock and decreasing
+	 * the refcount) is legitimate. Also, since the granule is only used for
+	 * refcount update, only an atomic operation will suffice and
+	 * release/acquire semantics are not required.
+	 */
+	atomic_granule_put(g_rd);
+
+	return RMI_SUCCESS;
+}
+
+void smc_rec_aux_count(unsigned long rd_addr, struct smc_result *ret_struct)
+{
+	unsigned int num_rec_aux;
+	struct granule *g_rd;
+	struct rd *rd;
+
+	g_rd = find_lock_granule(rd_addr, GRANULE_STATE_RD);
+	if (g_rd == NULL) {
+		ret_struct->x[0] = RMI_ERROR_INPUT;
+		return;
+	}
+
+	rd = granule_map(g_rd, SLOT_RD);
+	num_rec_aux = rd->num_rec_aux;
+	buffer_unmap(rd);
+	granule_unlock(g_rd);
+
+	ret_struct->x[0] = RMI_SUCCESS;
+	ret_struct->x[1] = (unsigned long)num_rec_aux;
+}
+
+unsigned long smc_psci_complete(unsigned long calling_rec_addr,
+				unsigned long target_rec_addr)
+{
+	struct granule *g_calling_rec, *g_target_rec;
+	struct rec  *calling_rec, *target_rec;
+	unsigned long ret;
+
+	assert(calling_rec_addr != 0UL);
+	assert(target_rec_addr != 0UL);
+
+	if (!GRANULE_ALIGNED(calling_rec_addr)) {
+		return RMI_ERROR_INPUT;
+	}
+
+	if (!GRANULE_ALIGNED(target_rec_addr)) {
+		return RMI_ERROR_INPUT;
+	}
+
+	if (!find_lock_two_granules(calling_rec_addr,
+					GRANULE_STATE_REC,
+					&g_calling_rec,
+					target_rec_addr,
+					GRANULE_STATE_REC,
+					&g_target_rec)) {
+		return RMI_ERROR_INPUT;
+	}
+
+	/*
+	 * The access to a REC from RMI_REC_ENTER is only protected by the
+	 * reference counter. Here, we may access the volatile (non constant)
+	 * members of REC structure (such as rec->running) only if the counter
+	 * is zero.
+	 */
+	if (granule_refcount_read_acquire(g_calling_rec) != 0UL) {
+		/*
+		 * The `calling` REC is running on another PE and therefore it
+		 * may not have a pending PSCI request.
+		 */
+		ret = RMI_ERROR_INPUT;
+		goto out_unlock;
+	}
+
+	calling_rec = granule_map(g_calling_rec, SLOT_REC);
+	target_rec = granule_map(g_target_rec, SLOT_REC2);
+
+	ret = psci_complete_request(calling_rec, target_rec);
+
+	buffer_unmap(target_rec);
+	buffer_unmap(calling_rec);
+out_unlock:
+	granule_unlock(g_calling_rec);
+	granule_unlock(g_target_rec);
+
+	return ret;
+}
diff --git a/runtime/rmi/rtt.c b/runtime/rmi/rtt.c
new file mode 100644
index 0000000..e474e5d
--- /dev/null
+++ b/runtime/rmi/rtt.c
@@ -0,0 +1,1299 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <buffer.h>
+#include <granule.h>
+#include <measurement.h>
+#include <realm.h>
+#include <ripas.h>
+#include <smc-handler.h>
+#include <smc-rmi.h>
+#include <smc.h>
+#include <stddef.h>
+#include <string.h>
+#include <table.h>
+
+/*
+ * Validate the map_addr value passed to RMI_RTT_* and RMI_DATA_* commands.
+ */
+static bool validate_map_addr(unsigned long map_addr,
+			      unsigned long level,
+			      struct rd *rd)
+{
+
+	if (map_addr >= realm_ipa_size(rd)) {
+		return false;
+	}
+	if (!addr_is_level_aligned(map_addr, level)) {
+		return false;
+	}
+	return true;
+}
+
+/*
+ * Structure commands can operate on all RTTs except for the root RTT so
+ * the minimal valid level is the stage 2 starting level + 1.
+ */
+static bool validate_rtt_structure_cmds(unsigned long map_addr,
+					long level,
+					struct rd *rd)
+{
+	int min_level = realm_rtt_starting_level(rd) + 1;
+
+	if ((level < min_level) || (level > RTT_PAGE_LEVEL)) {
+		return false;
+	}
+	return validate_map_addr(map_addr, level, rd);
+}
+
+/*
+ * Map/Unmap commands can operate up to a level 2 block entry so min_level is
+ * the smallest block size.
+ */
+static bool validate_rtt_map_cmds(unsigned long map_addr,
+				  long level,
+				  struct rd *rd)
+{
+	if ((level < RTT_MIN_BLOCK_LEVEL) || (level > RTT_PAGE_LEVEL)) {
+		return false;
+	}
+	return validate_map_addr(map_addr, level, rd);
+}
+
+/*
+ * Entry commands can operate on any entry so the minimal valid level is the
+ * stage 2 starting level.
+ */
+static bool validate_rtt_entry_cmds(unsigned long map_addr,
+				    long level,
+				    struct rd *rd)
+{
+	if ((level < realm_rtt_starting_level(rd)) ||
+	    (level > RTT_PAGE_LEVEL)) {
+		return false;
+	}
+	return validate_map_addr(map_addr, level, rd);
+}
+
+unsigned long smc_rtt_create(unsigned long rtt_addr,
+			     unsigned long rd_addr,
+			     unsigned long map_addr,
+			     unsigned long ulevel)
+{
+	struct granule *g_rd;
+	struct granule *g_tbl;
+	struct rd *rd;
+	struct granule *g_table_root;
+	struct rtt_walk wi;
+	unsigned long *s2tt, *parent_s2tt, parent_s2tte;
+	long level = (long)ulevel;
+	unsigned long ipa_bits;
+	unsigned long ret;
+	struct realm_s2_context s2_ctx;
+	int sl;
+
+	if (!find_lock_two_granules(rtt_addr,
+				    GRANULE_STATE_DELEGATED,
+				    &g_tbl,
+				    rd_addr,
+				    GRANULE_STATE_RD,
+				    &g_rd)) {
+		return RMI_ERROR_INPUT;
+	}
+
+	rd = granule_map(g_rd, SLOT_RD);
+
+	if (!validate_rtt_structure_cmds(map_addr, level, rd)) {
+		buffer_unmap(rd);
+		granule_unlock(g_rd);
+		granule_unlock(g_tbl);
+		return RMI_ERROR_INPUT;
+	}
+
+	g_table_root = rd->s2_ctx.g_rtt;
+	sl = realm_rtt_starting_level(rd);
+	ipa_bits = realm_ipa_bits(rd);
+	s2_ctx = rd->s2_ctx;
+	buffer_unmap(rd);
+
+	/*
+	 * Lock the RTT root. Enforcing locking order RD->RTT is enough to
+	 * ensure deadlock free locking guarentee.
+	 */
+	granule_lock(g_table_root, GRANULE_STATE_RTT);
+
+	/* Unlock RD after locking RTT Root */
+	granule_unlock(g_rd);
+
+	rtt_walk_lock_unlock(g_table_root, sl, ipa_bits,
+				map_addr, level - 1L, &wi);
+	if (wi.last_level != level - 1L) {
+		ret = pack_return_code(RMI_ERROR_RTT, wi.last_level);
+		goto out_unlock_llt;
+	}
+
+	parent_s2tt = granule_map(wi.g_llt, SLOT_RTT);
+	parent_s2tte = s2tte_read(&parent_s2tt[wi.index]);
+	s2tt = granule_map(g_tbl, SLOT_DELEGATED);
+
+	if (s2tte_is_unassigned(parent_s2tte)) {
+		/*
+		 * Note that if map_addr is an Unprotected IPA, the RIPAS field
+		 * is guaranteed to be zero, in both parent and child s2ttes.
+		 */
+		enum ripas ripas = s2tte_get_ripas(parent_s2tte);
+
+		s2tt_init_unassigned(s2tt, ripas);
+
+		/*
+		 * Increase the refcount of the parent, the granule was
+		 * locked while table walking and hand-over-hand locking.
+		 * Atomicity and acquire/release semantics not required because
+		 * the table is accessed always locked.
+		 */
+		__granule_get(wi.g_llt);
+
+	} else if (s2tte_is_destroyed(parent_s2tte)) {
+		s2tt_init_destroyed(s2tt);
+		__granule_get(wi.g_llt);
+
+	} else if (s2tte_is_assigned(parent_s2tte, level - 1L)) {
+		unsigned long block_pa;
+
+		/*
+		 * We should observe parent assigned s2tte only when
+		 * we create tables above this level.
+		 */
+		assert(level > RTT_MIN_BLOCK_LEVEL);
+
+		block_pa = s2tte_pa(parent_s2tte, level - 1L);
+
+		s2tt_init_assigned_empty(s2tt, block_pa, level);
+
+		/*
+		 * Increase the refcount to mark the granule as in-use. refcount
+		 * is incremented by S2TTES_PER_S2TT (ref RTT unfolding).
+		 */
+		__granule_refcount_inc(g_tbl, S2TTES_PER_S2TT);
+
+	} else if (s2tte_is_valid(parent_s2tte, level - 1L)) {
+		unsigned long block_pa;
+
+		/*
+		 * We should observe parent valid s2tte only when
+		 * we create tables above this level.
+		 */
+		assert(level > RTT_MIN_BLOCK_LEVEL);
+
+		/*
+		 * Break before make. This may cause spurious S2 aborts.
+		 */
+		s2tte_write(&parent_s2tt[wi.index], 0UL);
+		invalidate_block(&s2_ctx, map_addr);
+
+		block_pa = s2tte_pa(parent_s2tte, level - 1L);
+
+		s2tt_init_valid(s2tt, block_pa, level);
+
+		/*
+		 * Increase the refcount to mark the granule as in-use. refcount
+		 * is incremented by S2TTES_PER_S2TT (ref RTT unfolding).
+		 */
+		__granule_refcount_inc(g_tbl, S2TTES_PER_S2TT);
+
+	} else if (s2tte_is_valid_ns(parent_s2tte, level - 1L)) {
+		unsigned long block_pa;
+
+		/*
+		 * We should observe parent valid_ns s2tte only when
+		 * we create tables above this level.
+		 */
+		assert(level > RTT_MIN_BLOCK_LEVEL);
+
+		/*
+		 * Break before make. This may cause spurious S2 aborts.
+		 */
+		s2tte_write(&parent_s2tt[wi.index], 0UL);
+		invalidate_block(&s2_ctx, map_addr);
+
+		block_pa = s2tte_pa(parent_s2tte, level - 1L);
+
+		s2tt_init_valid_ns(s2tt, block_pa, level);
+
+		/*
+		 * Increase the refcount to mark the granule as in-use. refcount
+		 * is incremented by S2TTES_PER_S2TT (ref RTT unfolding).
+		 */
+		__granule_refcount_inc(g_tbl, S2TTES_PER_S2TT);
+
+	} else if (s2tte_is_table(parent_s2tte, level - 1L)) {
+		ret = pack_return_code(RMI_ERROR_RTT,
+					(unsigned int)(level - 1L));
+		goto out_unmap_table;
+
+	} else {
+		assert(false);
+	}
+
+	ret = RMI_SUCCESS;
+
+	granule_set_state(g_tbl, GRANULE_STATE_RTT);
+
+	parent_s2tte = s2tte_create_table(rtt_addr, level - 1L);
+	s2tte_write(&parent_s2tt[wi.index], parent_s2tte);
+
+out_unmap_table:
+	buffer_unmap(s2tt);
+	buffer_unmap(parent_s2tt);
+out_unlock_llt:
+	granule_unlock(wi.g_llt);
+	granule_unlock(g_tbl);
+	return ret;
+}
+
+unsigned long smc_rtt_fold(unsigned long rtt_addr,
+			   unsigned long rd_addr,
+			   unsigned long map_addr,
+			   unsigned long ulevel)
+{
+	struct granule *g_rd;
+	struct granule *g_tbl;
+	struct rd *rd;
+	struct granule *g_table_root;
+	struct rtt_walk wi;
+	unsigned long *table, *parent_s2tt, parent_s2tte;
+	long level = (long)ulevel;
+	unsigned long ipa_bits;
+	unsigned long ret;
+	struct realm_s2_context s2_ctx;
+	int sl;
+	enum ripas ripas;
+
+	g_rd = find_lock_granule(rd_addr, GRANULE_STATE_RD);
+	if (g_rd == NULL) {
+		return RMI_ERROR_INPUT;
+	}
+
+	rd = granule_map(g_rd, SLOT_RD);
+
+	if (!validate_rtt_structure_cmds(map_addr, level, rd)) {
+		buffer_unmap(rd);
+		granule_unlock(g_rd);
+		return RMI_ERROR_INPUT;
+	}
+
+	g_table_root = rd->s2_ctx.g_rtt;
+	sl = realm_rtt_starting_level(rd);
+	ipa_bits = realm_ipa_bits(rd);
+	s2_ctx = rd->s2_ctx;
+	buffer_unmap(rd);
+	granule_lock(g_table_root, GRANULE_STATE_RTT);
+	granule_unlock(g_rd);
+
+	rtt_walk_lock_unlock(g_table_root, sl, ipa_bits,
+				map_addr, level - 1L, &wi);
+	if (wi.last_level != level - 1UL) {
+		ret = pack_return_code(RMI_ERROR_RTT, wi.last_level);
+		goto out_unlock_parent_table;
+	}
+
+	parent_s2tt = granule_map(wi.g_llt, SLOT_RTT);
+	parent_s2tte = s2tte_read(&parent_s2tt[wi.index]);
+	if (!s2tte_is_table(parent_s2tte, level - 1L)) {
+		ret = pack_return_code(RMI_ERROR_RTT,
+					(unsigned int)(level - 1L));
+		goto out_unmap_parent_table;
+	}
+
+	/*
+	 * Check that the 'rtt_addr' RTT is used at (map_addr, level).
+	 * Note that this also verifies that the rtt_addr is properly aligned.
+	 */
+	if (rtt_addr != s2tte_pa_table(parent_s2tte, level - 1L)) {
+		ret = pack_return_code(RMI_ERROR_RTT,
+					(unsigned int)(level - 1L));
+		goto out_unmap_parent_table;
+	}
+
+	g_tbl = find_lock_granule(rtt_addr, GRANULE_STATE_RTT);
+
+	/*
+	 * A table descriptor S2TTE always points to a TABLE granule.
+	 */
+	assert(g_tbl);
+
+	table = granule_map(g_tbl, SLOT_RTT2);
+
+	/*
+	 * The command can succeed only if all 512 S2TTEs are of the same type.
+	 * We first check the table's ref. counter to speed up the case when
+	 * the host makes a guess whether a memory region can be folded.
+	 */
+	if (g_tbl->refcount == 0UL) {
+		if (table_is_destroyed_block(table)) {
+			parent_s2tte = s2tte_create_destroyed();
+			__granule_put(wi.g_llt);
+
+		} else if (table_is_unassigned_block(table, &ripas)) {
+			/*
+			 * Note that if map_addr is an Unprotected IPA, the
+			 * RIPAS field is guaranteed to be zero, in both parent
+			 * and child s2ttes.
+			 */
+			parent_s2tte = s2tte_create_unassigned(ripas);
+			__granule_put(wi.g_llt);
+		} else {
+			/*
+			 * The table holds a mixture of destroyed and
+			 * unassigned entries.
+			 */
+			ret = RMI_ERROR_IN_USE;
+			goto out_unmap_table;
+		}
+
+	} else if (g_tbl->refcount == S2TTES_PER_S2TT) {
+
+		unsigned long s2tte, block_pa;
+
+		/* The RMM specification does not allow creating block
+		 * entries less than RTT_MIN_BLOCK_LEVEL even though
+		 * permitted by the Arm Architecture.
+		 * Hence ensure that the table being folded is at a level
+		 * higher than the RTT_MIN_BLOCK_LEVEL.
+		 *
+		 * A fully populated table cannot be destroyed if that
+		 * would create a block mapping below RTT_MIN_BLOCK_LEVEL.
+		 */
+		if (level <= RTT_MIN_BLOCK_LEVEL) {
+			ret = RMI_ERROR_IN_USE;
+			goto out_unmap_table;
+		}
+
+		s2tte = s2tte_read(&table[0]);
+		block_pa = s2tte_pa(s2tte, level - 1L);
+
+		/*
+		 * The table must also refer to a contiguous block through
+		 * the same type of s2tte, either Assigned, Valid  or Valid_NS.
+		 */
+		if (table_maps_assigned_block(table, level)) {
+			parent_s2tte = s2tte_create_assigned_empty(block_pa, level - 1L);
+		} else if (table_maps_valid_block(table, level)) {
+			parent_s2tte = s2tte_create_valid(block_pa, level - 1L);
+		} else if (table_maps_valid_ns_block(table, level)) {
+			parent_s2tte = s2tte_create_valid_ns(block_pa, level - 1L);
+		/* This 'else' case should not happen */
+		} else {
+			assert(false);
+		}
+
+		__granule_refcount_dec(g_tbl, S2TTES_PER_S2TT);
+	} else {
+		/*
+		 * The table holds a mixture of different types of s2ttes.
+		 */
+		ret = RMI_ERROR_IN_USE;
+		goto out_unmap_table;
+	}
+
+	ret = RMI_SUCCESS;
+
+	/*
+	 * Break before make.
+	 */
+	s2tte_write(&parent_s2tt[wi.index], 0UL);
+
+	if (s2tte_is_valid(parent_s2tte, level - 1L) ||
+	    s2tte_is_valid_ns(parent_s2tte, level - 1L)) {
+		invalidate_pages_in_block(&s2_ctx, map_addr);
+	} else {
+		invalidate_block(&s2_ctx, map_addr);
+	}
+
+	s2tte_write(&parent_s2tt[wi.index], parent_s2tte);
+
+	granule_memzero_mapped(table);
+	granule_set_state(g_tbl, GRANULE_STATE_DELEGATED);
+
+out_unmap_table:
+	buffer_unmap(table);
+	granule_unlock(g_tbl);
+out_unmap_parent_table:
+	buffer_unmap(parent_s2tt);
+out_unlock_parent_table:
+	granule_unlock(wi.g_llt);
+	return ret;
+}
+
+unsigned long smc_rtt_destroy(unsigned long rtt_addr,
+			      unsigned long rd_addr,
+			      unsigned long map_addr,
+			      unsigned long ulevel)
+{
+	struct granule *g_rd;
+	struct granule *g_tbl;
+	struct rd *rd;
+	struct granule *g_table_root;
+	struct rtt_walk wi;
+	unsigned long *table, *parent_s2tt, parent_s2tte;
+	long level = (long)ulevel;
+	unsigned long ipa_bits;
+	unsigned long ret;
+	struct realm_s2_context s2_ctx;
+	int sl;
+	bool in_par;
+
+	g_rd = find_lock_granule(rd_addr, GRANULE_STATE_RD);
+	if (g_rd == NULL) {
+		return RMI_ERROR_INPUT;
+	}
+
+	rd = granule_map(g_rd, SLOT_RD);
+
+	if (!validate_rtt_structure_cmds(map_addr, level, rd)) {
+		buffer_unmap(rd);
+		granule_unlock(g_rd);
+		return RMI_ERROR_INPUT;
+	}
+
+	g_table_root = rd->s2_ctx.g_rtt;
+	sl = realm_rtt_starting_level(rd);
+	ipa_bits = realm_ipa_bits(rd);
+	s2_ctx = rd->s2_ctx;
+	in_par = addr_in_par(rd, map_addr);
+	buffer_unmap(rd);
+	granule_lock(g_table_root, GRANULE_STATE_RTT);
+	granule_unlock(g_rd);
+
+	rtt_walk_lock_unlock(g_table_root, sl, ipa_bits,
+				map_addr, level - 1L, &wi);
+	if (wi.last_level != level - 1UL) {
+		ret = pack_return_code(RMI_ERROR_RTT, wi.last_level);
+		goto out_unlock_parent_table;
+	}
+
+	parent_s2tt = granule_map(wi.g_llt, SLOT_RTT);
+	parent_s2tte = s2tte_read(&parent_s2tt[wi.index]);
+	if (!s2tte_is_table(parent_s2tte, level - 1L)) {
+		ret = pack_return_code(RMI_ERROR_RTT,
+					(unsigned int)(level - 1L));
+		goto out_unmap_parent_table;
+	}
+
+	/*
+	 * Check that the 'rtt_addr' RTT is used at (map_addr, level).
+	 * Note that this also verifies that the rtt_addr is properly aligned.
+	 */
+	if (rtt_addr != s2tte_pa_table(parent_s2tte, level - 1L)) {
+		ret = RMI_ERROR_INPUT;
+		goto out_unmap_parent_table;
+	}
+
+	/*
+	 * Lock the RTT granule. The 'rtt_addr' is verified, thus can be treated
+	 * as an internal granule.
+	 */
+	g_tbl = find_lock_granule(rtt_addr, GRANULE_STATE_RTT);
+
+	/*
+	 * A table descriptor S2TTE always points to a TABLE granule.
+	 */
+	assert(g_tbl != NULL);
+
+	/*
+	 * Read the refcount value. RTT granule is always accessed locked, thus
+	 * the refcount can be accessed without atomic operations.
+	 */
+	if (g_tbl->refcount != 0UL) {
+		ret = RMI_ERROR_IN_USE;
+		goto out_unlock_table;
+	}
+
+	ret = RMI_SUCCESS;
+
+	table = granule_map(g_tbl, SLOT_RTT2);
+
+	if (in_par) {
+		parent_s2tte = s2tte_create_destroyed();
+	} else {
+		parent_s2tte = s2tte_create_invalid_ns();
+	}
+
+	__granule_put(wi.g_llt);
+
+	/*
+	 * Break before make. Note that this may cause spurious S2 aborts.
+	 */
+	s2tte_write(&parent_s2tt[wi.index], 0UL);
+	invalidate_block(&s2_ctx, map_addr);
+	s2tte_write(&parent_s2tt[wi.index], parent_s2tte);
+
+	granule_memzero_mapped(table);
+	granule_set_state(g_tbl, GRANULE_STATE_DELEGATED);
+
+	buffer_unmap(table);
+out_unlock_table:
+	granule_unlock(g_tbl);
+out_unmap_parent_table:
+	buffer_unmap(parent_s2tt);
+out_unlock_parent_table:
+	granule_unlock(wi.g_llt);
+	return ret;
+}
+
+enum map_unmap_ns_op {
+	MAP_NS,
+	UNMAP_NS
+};
+
+/*
+ * We don't hold a reference on the NS granule when it is
+ * mapped into a realm. Instead we rely on the guarantees
+ * provided by the architecture to ensure that a NS access
+ * to a protected granule is prohibited even within the realm.
+ */
+static unsigned long map_unmap_ns(unsigned long rd_addr,
+				  unsigned long map_addr,
+				  long level,
+				  unsigned long host_s2tte,
+				  enum map_unmap_ns_op op)
+{
+	struct granule *g_rd;
+	struct rd *rd;
+	struct granule *g_table_root;
+	unsigned long *s2tt, s2tte;
+	struct rtt_walk wi;
+	unsigned long ipa_bits;
+	unsigned long ret;
+	struct realm_s2_context s2_ctx;
+	int sl;
+
+	g_rd = find_lock_granule(rd_addr, GRANULE_STATE_RD);
+	if (g_rd == NULL) {
+		return RMI_ERROR_INPUT;
+	}
+
+	rd = granule_map(g_rd, SLOT_RD);
+
+	if (!validate_rtt_map_cmds(map_addr, level, rd)) {
+		buffer_unmap(rd);
+		granule_unlock(g_rd);
+		return RMI_ERROR_INPUT;
+	}
+
+	g_table_root = rd->s2_ctx.g_rtt;
+	sl = realm_rtt_starting_level(rd);
+	ipa_bits = realm_ipa_bits(rd);
+
+	/*
+	 * We don't have to check PAR boundaries for unmap_ns
+	 * operation because we already test that the s2tte is Valid_NS
+	 * and only outside-PAR IPAs can be translated by such s2tte.
+	 *
+	 * For "map_ns", however, the s2tte is verified to be Unassigned
+	 * but both inside & outside PAR IPAs can be translated by such s2ttes.
+	 */
+	if ((op == MAP_NS) && addr_in_par(rd, map_addr)) {
+		buffer_unmap(rd);
+		granule_unlock(g_rd);
+		return RMI_ERROR_INPUT;
+	}
+
+	s2_ctx = rd->s2_ctx;
+	buffer_unmap(rd);
+
+	granule_lock(g_table_root, GRANULE_STATE_RTT);
+	granule_unlock(g_rd);
+
+	rtt_walk_lock_unlock(g_table_root, sl, ipa_bits,
+				map_addr, level, &wi);
+	if (wi.last_level != level) {
+		ret = pack_return_code(RMI_ERROR_RTT, wi.last_level);
+		goto out_unlock_llt;
+	}
+
+	s2tt = granule_map(wi.g_llt, SLOT_RTT);
+	s2tte = s2tte_read(&s2tt[wi.index]);
+
+	if (op == MAP_NS) {
+		if (!s2tte_is_unassigned(s2tte)) {
+			ret = pack_return_code(RMI_ERROR_RTT,
+						(unsigned int)level);
+			goto out_unmap_table;
+		}
+
+		s2tte = s2tte_create_valid_ns(host_s2tte, level);
+		s2tte_write(&s2tt[wi.index], s2tte);
+		__granule_get(wi.g_llt);
+
+	} else if (op == UNMAP_NS) {
+		/*
+		 * The following check also verifies that map_addr is outside
+		 * PAR, as valid_NS s2tte may only cover outside PAR IPA range.
+		 */
+		if (!s2tte_is_valid_ns(s2tte, level)) {
+			ret = pack_return_code(RMI_ERROR_RTT,
+						(unsigned int)level);
+			goto out_unmap_table;
+		}
+
+		s2tte = s2tte_create_invalid_ns();
+		s2tte_write(&s2tt[wi.index], s2tte);
+		__granule_put(wi.g_llt);
+		if (level == RTT_PAGE_LEVEL) {
+			invalidate_page(&s2_ctx, map_addr);
+		} else {
+			invalidate_block(&s2_ctx, map_addr);
+		}
+	}
+
+	ret = RMI_SUCCESS;
+
+out_unmap_table:
+	buffer_unmap(s2tt);
+out_unlock_llt:
+	granule_unlock(wi.g_llt);
+	return ret;
+}
+
+unsigned long smc_rtt_map_unprotected(unsigned long rd_addr,
+				      unsigned long map_addr,
+				      unsigned long ulevel,
+				      unsigned long s2tte)
+{
+	long level = (long)ulevel;
+
+	if (!host_ns_s2tte_is_valid(s2tte, level)) {
+		return RMI_ERROR_INPUT;
+	}
+
+	return map_unmap_ns(rd_addr, map_addr, level, s2tte, MAP_NS);
+}
+
+unsigned long smc_rtt_unmap_unprotected(unsigned long rd_addr,
+					unsigned long map_addr,
+					unsigned long ulevel)
+{
+	return map_unmap_ns(rd_addr, map_addr, (long)ulevel, 0UL, UNMAP_NS);
+}
+
+void smc_rtt_read_entry(unsigned long rd_addr,
+			unsigned long map_addr,
+			unsigned long ulevel,
+			struct smc_result *ret)
+{
+	struct granule *g_rd, *g_rtt_root;
+	struct rd *rd;
+	struct rtt_walk wi;
+	unsigned long *s2tt, s2tte;
+	unsigned long ipa_bits;
+	long level = (long)ulevel;
+	int sl;
+
+	g_rd = find_lock_granule(rd_addr, GRANULE_STATE_RD);
+	if (g_rd == NULL) {
+		ret->x[0] = RMI_ERROR_INPUT;
+		return;
+	}
+
+	rd = granule_map(g_rd, SLOT_RD);
+
+	if (!validate_rtt_entry_cmds(map_addr, level, rd)) {
+		buffer_unmap(rd);
+		granule_unlock(g_rd);
+		ret->x[0] = RMI_ERROR_INPUT;
+		return;
+	}
+
+	g_rtt_root = rd->s2_ctx.g_rtt;
+	sl = realm_rtt_starting_level(rd);
+	ipa_bits = realm_ipa_bits(rd);
+	buffer_unmap(rd);
+
+	granule_lock(g_rtt_root, GRANULE_STATE_RTT);
+	granule_unlock(g_rd);
+
+	rtt_walk_lock_unlock(g_rtt_root, sl, ipa_bits,
+				map_addr, level, &wi);
+	s2tt = granule_map(wi.g_llt, SLOT_RTT);
+	s2tte = s2tte_read(&s2tt[wi.index]);
+	ret->x[1] =  wi.last_level;
+	ret->x[3] = 0UL;
+	ret->x[4] = 0UL;
+
+	if (s2tte_is_unassigned(s2tte)) {
+		enum ripas ripas = s2tte_get_ripas(s2tte);
+
+		ret->x[2] = RMI_RTT_STATE_UNASSIGNED;
+		ret->x[4] = (unsigned long)ripas;
+	} else if (s2tte_is_destroyed(s2tte)) {
+		ret->x[2] = RMI_RTT_STATE_DESTROYED;
+	} else if (s2tte_is_assigned(s2tte, wi.last_level)) {
+		ret->x[2] = RMI_RTT_STATE_ASSIGNED;
+		ret->x[3] = s2tte_pa(s2tte, wi.last_level);
+		ret->x[4] = RMI_EMPTY;
+	} else if (s2tte_is_valid(s2tte, wi.last_level)) {
+		ret->x[2] = RMI_RTT_STATE_ASSIGNED;
+		ret->x[3] = s2tte_pa(s2tte, wi.last_level);
+		ret->x[4] = RMI_RAM;
+	} else if (s2tte_is_valid_ns(s2tte, wi.last_level)) {
+		ret->x[2] = RMI_RTT_STATE_VALID_NS;
+		ret->x[3] = host_ns_s2tte(s2tte, wi.last_level);
+	} else if (s2tte_is_table(s2tte, wi.last_level)) {
+		ret->x[2] = RMI_RTT_STATE_TABLE;
+		ret->x[3] = s2tte_pa_table(s2tte, wi.last_level);
+	} else {
+		assert(false);
+	}
+
+	buffer_unmap(s2tt);
+	granule_unlock(wi.g_llt);
+
+	ret->x[0] = RMI_SUCCESS;
+}
+
+static void data_granule_measure(struct rd *rd, void *data,
+				 unsigned long ipa,
+				 unsigned long flags)
+{
+	struct measurement_desc_data measure_desc = {0};
+
+	/* Initialize the measurement descriptior structure */
+	measure_desc.desc_type = MEASURE_DESC_TYPE_DATA;
+	measure_desc.len = sizeof(struct measurement_desc_data);
+	measure_desc.ipa = ipa;
+	measure_desc.flags = flags;
+	memcpy(measure_desc.rim,
+	       &rd->measurement[RIM_MEASUREMENT_SLOT],
+	       measurement_get_size(rd->algorithm));
+
+	if (flags == RMI_MEASURE_CONTENT) {
+		/*
+		 * Hashing the data granules and store the result in the
+		 * measurement descriptor structure.
+		 */
+		measurement_hash_compute(rd->algorithm,
+					data,
+					GRANULE_SIZE,
+					measure_desc.content);
+	}
+
+	/*
+	 * Hashing the measurement descriptor structure; the result is the
+	 * updated RIM.
+	 */
+	measurement_hash_compute(rd->algorithm,
+			       &measure_desc,
+			       sizeof(measure_desc),
+			       rd->measurement[RIM_MEASUREMENT_SLOT]);
+}
+
+static unsigned long validate_data_create_unknown(unsigned long map_addr,
+						  struct rd *rd)
+{
+	if (!addr_in_par(rd, map_addr)) {
+		return RMI_ERROR_INPUT;
+	}
+
+	if (!validate_map_addr(map_addr, RTT_PAGE_LEVEL, rd)) {
+		return RMI_ERROR_INPUT;
+	}
+
+	return RMI_SUCCESS;
+}
+
+static unsigned long validate_data_create(unsigned long map_addr,
+					  struct rd *rd)
+{
+	if (get_rd_state_locked(rd) != REALM_STATE_NEW) {
+		return RMI_ERROR_REALM;
+	}
+
+	return validate_data_create_unknown(map_addr, rd);
+}
+
+/*
+ * Implements both Data.Create and Data.CreateUnknown
+ *
+ * if @g_src == NULL, this implemented Data.CreateUnknown
+ * and otherwise this implemented Data.Create.
+ */
+static unsigned long data_create(unsigned long data_addr,
+				 unsigned long rd_addr,
+				 unsigned long map_addr,
+				 struct granule *g_src,
+				 unsigned long flags)
+{
+	struct granule *g_data;
+	struct granule *g_rd;
+	struct granule *g_table_root;
+	struct rd *rd;
+	struct rtt_walk wi;
+	unsigned long s2tte, *s2tt;
+	enum ripas ripas;
+	enum granule_state new_data_state = GRANULE_STATE_DELEGATED;
+	unsigned long ipa_bits;
+	unsigned long ret;
+	int __unused meas_ret;
+	int sl;
+
+	if (!find_lock_two_granules(data_addr,
+				    GRANULE_STATE_DELEGATED,
+				    &g_data,
+				    rd_addr,
+				    GRANULE_STATE_RD,
+				    &g_rd)) {
+		return RMI_ERROR_INPUT;
+	}
+
+	rd = granule_map(g_rd, SLOT_RD);
+
+	ret = (g_src != NULL) ?
+		validate_data_create(map_addr, rd) :
+		validate_data_create_unknown(map_addr, rd);
+
+	if (ret != RMI_SUCCESS) {
+		goto out_unmap_rd;
+	}
+
+	g_table_root = rd->s2_ctx.g_rtt;
+	sl = realm_rtt_starting_level(rd);
+	ipa_bits = realm_ipa_bits(rd);
+	granule_lock(g_table_root, GRANULE_STATE_RTT);
+	rtt_walk_lock_unlock(g_table_root, sl, ipa_bits,
+			     map_addr, RTT_PAGE_LEVEL, &wi);
+	if (wi.last_level != RTT_PAGE_LEVEL) {
+		ret = pack_return_code(RMI_ERROR_RTT, wi.last_level);
+		goto out_unlock_ll_table;
+	}
+
+	s2tt = granule_map(wi.g_llt, SLOT_RTT);
+	s2tte = s2tte_read(&s2tt[wi.index]);
+	if (!s2tte_is_unassigned(s2tte)) {
+		ret = pack_return_code(RMI_ERROR_RTT, RTT_PAGE_LEVEL);
+		goto out_unmap_ll_table;
+	}
+
+	ripas = s2tte_get_ripas(s2tte);
+
+	if (g_src != NULL) {
+		bool ns_access_ok;
+		void *data = granule_map(g_data, SLOT_DELEGATED);
+
+		ns_access_ok = ns_buffer_read(SLOT_NS, g_src, 0U,
+					      GRANULE_SIZE, data);
+
+		if (!ns_access_ok) {
+			/*
+			 * Some data may be copied before the failure. Zero
+			 * g_data granule as it will remain in delegated state.
+			 */
+			(void)memset(data, 0, GRANULE_SIZE);
+			buffer_unmap(data);
+			ret = RMI_ERROR_INPUT;
+			goto out_unmap_ll_table;
+		}
+
+
+		data_granule_measure(rd, data, map_addr, flags);
+
+		buffer_unmap(data);
+	}
+
+	new_data_state = GRANULE_STATE_DATA;
+
+	s2tte = (ripas == RMI_EMPTY) ?
+		s2tte_create_assigned_empty(data_addr, RTT_PAGE_LEVEL) :
+		s2tte_create_valid(data_addr, RTT_PAGE_LEVEL);
+
+	s2tte_write(&s2tt[wi.index], s2tte);
+	__granule_get(wi.g_llt);
+
+	ret = RMI_SUCCESS;
+
+out_unmap_ll_table:
+	buffer_unmap(s2tt);
+out_unlock_ll_table:
+	granule_unlock(wi.g_llt);
+out_unmap_rd:
+	buffer_unmap(rd);
+	granule_unlock(g_rd);
+	granule_unlock_transition(g_data, new_data_state);
+	return ret;
+}
+
+unsigned long smc_data_create(unsigned long data_addr,
+			      unsigned long rd_addr,
+			      unsigned long map_addr,
+			      unsigned long src_addr,
+			      unsigned long flags)
+{
+	struct granule *g_src;
+	unsigned long ret;
+
+	if (flags != RMI_NO_MEASURE_CONTENT && flags != RMI_MEASURE_CONTENT) {
+		return RMI_ERROR_INPUT;
+	}
+
+	g_src = find_granule(src_addr);
+	if ((g_src == NULL) || (g_src->state != GRANULE_STATE_NS)) {
+		return RMI_ERROR_INPUT;
+	}
+
+	ret = data_create(data_addr, rd_addr, map_addr, g_src, flags);
+
+	return ret;
+}
+
+unsigned long smc_data_create_unknown(unsigned long data_addr,
+				      unsigned long rd_addr,
+				      unsigned long map_addr)
+{
+	return data_create(data_addr, rd_addr, map_addr, NULL, 0);
+}
+
+unsigned long smc_data_destroy(unsigned long rd_addr,
+			       unsigned long map_addr)
+{
+	struct granule *g_data;
+	struct granule *g_rd;
+	struct granule *g_table_root;
+	struct rtt_walk wi;
+	unsigned long data_addr, s2tte, *s2tt;
+	struct rd *rd;
+	unsigned long ipa_bits;
+	unsigned long ret;
+	struct realm_s2_context s2_ctx;
+	bool valid;
+	int sl;
+
+	g_rd = find_lock_granule(rd_addr, GRANULE_STATE_RD);
+	if (g_rd == NULL) {
+		return RMI_ERROR_INPUT;
+	}
+
+	rd = granule_map(g_rd, SLOT_RD);
+
+	if (!validate_map_addr(map_addr, RTT_PAGE_LEVEL, rd)) {
+		buffer_unmap(rd);
+		granule_unlock(g_rd);
+		return RMI_ERROR_INPUT;
+	}
+
+	g_table_root = rd->s2_ctx.g_rtt;
+	sl = realm_rtt_starting_level(rd);
+	ipa_bits = realm_ipa_bits(rd);
+	s2_ctx = rd->s2_ctx;
+	buffer_unmap(rd);
+
+	granule_lock(g_table_root, GRANULE_STATE_RTT);
+	granule_unlock(g_rd);
+
+	rtt_walk_lock_unlock(g_table_root, sl, ipa_bits,
+				map_addr, RTT_PAGE_LEVEL, &wi);
+	if (wi.last_level != RTT_PAGE_LEVEL) {
+		ret = pack_return_code(RMI_ERROR_RTT, wi.last_level);
+		goto out_unlock_ll_table;
+	}
+
+	s2tt = granule_map(wi.g_llt, SLOT_RTT);
+	s2tte = s2tte_read(&s2tt[wi.index]);
+
+	valid = s2tte_is_valid(s2tte, RTT_PAGE_LEVEL);
+
+	/*
+	 * Check if either HIPAS=ASSIGNED or map_addr is a
+	 * valid Protected IPA.
+	 */
+	if (!valid && !s2tte_is_assigned(s2tte, RTT_PAGE_LEVEL)) {
+		ret = pack_return_code(RMI_ERROR_RTT, RTT_PAGE_LEVEL);
+		goto out_unmap_ll_table;
+	}
+
+	data_addr = s2tte_pa(s2tte, RTT_PAGE_LEVEL);
+
+	/*
+	 * We have already established either HIPAS=ASSIGNED or a valid mapping.
+	 * If valid, transition HIPAS to DESTROYED and if HIPAS=ASSIGNED,
+	 * transition to UNASSIGNED.
+	 */
+	s2tte = valid ? s2tte_create_destroyed() :
+			s2tte_create_unassigned(RMI_EMPTY);
+
+	s2tte_write(&s2tt[wi.index], s2tte);
+
+	if (valid) {
+		invalidate_page(&s2_ctx, map_addr);
+	}
+
+	__granule_put(wi.g_llt);
+
+	/*
+	 * Lock the data granule and check expected state. Correct locking order
+	 * is guaranteed because granule address is obtained from a locked
+	 * granule by table walk. This lock needs to be acquired before a state
+	 * transition to or from GRANULE_STATE_DATA for granule address can happen.
+	 */
+	g_data = find_lock_granule(data_addr, GRANULE_STATE_DATA);
+	assert(g_data);
+	granule_memzero(g_data, SLOT_DELEGATED);
+	granule_unlock_transition(g_data, GRANULE_STATE_DELEGATED);
+
+	ret = RMI_SUCCESS;
+
+out_unmap_ll_table:
+	buffer_unmap(s2tt);
+out_unlock_ll_table:
+	granule_unlock(wi.g_llt);
+
+	return ret;
+}
+
+static bool update_ripas(unsigned long *s2tte, unsigned long level,
+			 enum ripas ripas)
+{
+	if (s2tte_is_table(*s2tte, level)) {
+		return false;
+	}
+
+	if (s2tte_is_valid(*s2tte, level)) {
+		if (ripas == RMI_EMPTY) {
+			unsigned long pa = s2tte_pa(*s2tte, level);
+			*s2tte = s2tte_create_assigned_empty(pa, level);
+		}
+		return true;
+	}
+
+	if (s2tte_is_unassigned(*s2tte) || s2tte_is_assigned(*s2tte, level)) {
+		*s2tte |= s2tte_create_ripas(ripas);
+		return true;
+	}
+
+	return false;
+}
+
+static void ripas_granule_measure(struct rd *rd,
+				  unsigned long ipa,
+				  unsigned long level)
+{
+	struct measurement_desc_ripas measure_desc = {0};
+
+	/* Initialize the measurement descriptior structure */
+	measure_desc.desc_type = MEASURE_DESC_TYPE_RIPAS;
+	measure_desc.len = sizeof(struct measurement_desc_ripas);
+	measure_desc.ipa = ipa;
+	measure_desc.level = level;
+	memcpy(measure_desc.rim,
+	       &rd->measurement[RIM_MEASUREMENT_SLOT],
+	       measurement_get_size(rd->algorithm));
+
+	/*
+	 * Hashing the measurement descriptor structure; the result is the
+	 * updated RIM.
+	 */
+	measurement_hash_compute(rd->algorithm,
+				 &measure_desc,
+				 sizeof(measure_desc),
+				 rd->measurement[RIM_MEASUREMENT_SLOT]);
+}
+
+unsigned long smc_rtt_init_ripas(unsigned long rd_addr,
+				 unsigned long map_addr,
+				 unsigned long ulevel)
+{
+	struct granule *g_rd, *g_rtt_root;
+	struct rd *rd;
+	unsigned long ipa_bits;
+	struct rtt_walk wi;
+	unsigned long s2tte, *s2tt;
+	unsigned long ret;
+	long level = (long)ulevel;
+	int sl;
+
+	g_rd = find_lock_granule(rd_addr, GRANULE_STATE_RD);
+	if (g_rd == NULL) {
+		return RMI_ERROR_INPUT;
+	}
+
+	rd = granule_map(g_rd, SLOT_RD);
+
+	if (get_rd_state_locked(rd) != REALM_STATE_NEW) {
+		buffer_unmap(rd);
+		granule_unlock(g_rd);
+		return RMI_ERROR_REALM;
+	}
+
+	if (!validate_rtt_entry_cmds(map_addr, level, rd)) {
+		buffer_unmap(rd);
+		granule_unlock(g_rd);
+		return RMI_ERROR_INPUT;
+	}
+
+	if (!addr_in_par(rd, map_addr)) {
+		buffer_unmap(rd);
+		granule_unlock(g_rd);
+		return RMI_ERROR_INPUT;
+	}
+
+	g_rtt_root = rd->s2_ctx.g_rtt;
+	sl = realm_rtt_starting_level(rd);
+	ipa_bits = realm_ipa_bits(rd);
+
+	granule_lock(g_rtt_root, GRANULE_STATE_RTT);
+	granule_unlock(g_rd);
+
+	rtt_walk_lock_unlock(g_rtt_root, sl, ipa_bits,
+				map_addr, level, &wi);
+	if (wi.last_level != level) {
+		ret = pack_return_code(RMI_ERROR_RTT, wi.last_level);
+		goto out_unlock_llt;
+	}
+
+	s2tt = granule_map(wi.g_llt, SLOT_RTT);
+	s2tte = s2tte_read(&s2tt[wi.index]);
+
+	/* Allowed only for HIPAS=UNASSIGNED */
+	if (s2tte_is_table(s2tte, level) || !s2tte_is_unassigned(s2tte)) {
+		ret = pack_return_code(RMI_ERROR_RTT, (unsigned int)level);
+		goto out_unmap_llt;
+	}
+
+	s2tte |= s2tte_create_ripas(RMI_RAM);
+
+	s2tte_write(&s2tt[wi.index], s2tte);
+
+	ripas_granule_measure(rd, map_addr, level);
+
+	ret = RMI_SUCCESS;
+
+out_unmap_llt:
+	buffer_unmap(s2tt);
+out_unlock_llt:
+	buffer_unmap(rd);
+	granule_unlock(wi.g_llt);
+	return ret;
+}
+
+unsigned long smc_rtt_set_ripas(unsigned long rd_addr,
+				unsigned long rec_addr,
+				unsigned long map_addr,
+				unsigned long ulevel,
+				unsigned long uripas)
+{
+	struct granule *g_rd, *g_rec, *g_rtt_root;
+	struct rec *rec;
+	struct rd *rd;
+	unsigned long map_size, ipa_bits;
+	struct rtt_walk wi;
+	unsigned long s2tte, *s2tt;
+	struct realm_s2_context s2_ctx;
+	long level = (long)ulevel;
+	enum ripas ripas = (enum ripas)uripas;
+	unsigned long ret;
+	bool valid;
+	int sl;
+
+	if (ripas > RMI_RAM) {
+		return RMI_ERROR_INPUT;
+	}
+
+	if (!find_lock_two_granules(rd_addr,
+				   GRANULE_STATE_RD,
+				   &g_rd,
+				   rec_addr,
+				   GRANULE_STATE_REC,
+				   &g_rec)) {
+		return RMI_ERROR_INPUT;
+	}
+
+	if (granule_refcount_read_acquire(g_rec) != 0UL) {
+		ret = RMI_ERROR_IN_USE;
+		goto out_unlock_rec_rd;
+	}
+
+	rec = granule_map(g_rec, SLOT_REC);
+
+	if (g_rd != rec->realm_info.g_rd) {
+		ret = RMI_ERROR_REC;
+		goto out_unmap_rec;
+	}
+
+	if (ripas != rec->set_ripas.ripas) {
+		ret = RMI_ERROR_INPUT;
+		goto out_unmap_rec;
+	}
+
+	if (map_addr != rec->set_ripas.addr) {
+		/* Target region is not next chunk of requested region */
+		ret = RMI_ERROR_INPUT;
+		goto out_unmap_rec;
+	}
+
+	rd = granule_map(g_rd, SLOT_RD);
+
+	if (!validate_rtt_entry_cmds(map_addr, level, rd)) {
+		ret = RMI_ERROR_INPUT;
+		goto out_unmap_rd;
+	}
+
+	map_size = s2tte_map_size(level);
+	if (map_addr + map_size > rec->set_ripas.end) {
+		/* Target region extends beyond end of requested region */
+		ret = RMI_ERROR_INPUT;
+		goto out_unmap_rd;
+	}
+
+	g_rtt_root = rd->s2_ctx.g_rtt;
+	sl = realm_rtt_starting_level(rd);
+	ipa_bits = realm_ipa_bits(rd);
+	s2_ctx = rd->s2_ctx;
+
+	granule_lock(g_rtt_root, GRANULE_STATE_RTT);
+
+	rtt_walk_lock_unlock(g_rtt_root, sl, ipa_bits,
+				map_addr, level, &wi);
+	if (wi.last_level != level) {
+		ret = pack_return_code(RMI_ERROR_RTT, wi.last_level);
+		goto out_unlock_llt;
+	}
+
+	s2tt = granule_map(wi.g_llt, SLOT_RTT);
+	s2tte = s2tte_read(&s2tt[wi.index]);
+
+	valid = s2tte_is_valid(s2tte, level);
+
+	if (!update_ripas(&s2tte, level, ripas)) {
+		ret = pack_return_code(RMI_ERROR_RTT, (unsigned int)level);
+		goto out_unmap_llt;
+	}
+
+	s2tte_write(&s2tt[wi.index], s2tte);
+
+	if (valid && (ripas == RMI_EMPTY)) {
+		if (level == RTT_PAGE_LEVEL) {
+			invalidate_page(&s2_ctx, map_addr);
+		} else {
+			invalidate_block(&s2_ctx, map_addr);
+		}
+	}
+
+	rec->set_ripas.addr += map_size;
+
+	ret = RMI_SUCCESS;
+
+out_unmap_llt:
+	buffer_unmap(s2tt);
+out_unlock_llt:
+	granule_unlock(wi.g_llt);
+out_unmap_rd:
+	buffer_unmap(rd);
+out_unmap_rec:
+	buffer_unmap(rec);
+out_unlock_rec_rd:
+	granule_unlock(g_rec);
+	granule_unlock(g_rd);
+	return ret;
+}
diff --git a/runtime/rmi/run.c b/runtime/rmi/run.c
new file mode 100644
index 0000000..27ec48e
--- /dev/null
+++ b/runtime/rmi/run.c
@@ -0,0 +1,304 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <arch.h>
+#include <debug.h>
+#include <esr.h>
+#include <gic.h>
+#include <granule.h>
+#include <inject_exp.h>
+#include <memory_alloc.h>
+#include <psci.h>
+#include <realm.h>
+#include <rec.h>
+#include <rsi-host-call.h>
+#include <smc-handler.h>
+#include <smc-rmi.h>
+#include <smc-rsi.h>
+#include <smc.h>
+#include <timers.h>
+
+static void reset_last_run_info(struct rec *rec)
+{
+	rec->last_run_info.esr = 0UL;
+}
+
+static bool complete_mmio_emulation(struct rec *rec, struct rmi_rec_entry *rec_entry)
+{
+	unsigned long esr = rec->last_run_info.esr;
+	unsigned int rt = esr_srt(esr);
+
+	if ((rec_entry->flags & REC_ENTRY_FLAG_EMUL_MMIO) == 0UL) {
+		return true;
+	}
+
+	if (((esr & ESR_EL2_EC_MASK) != ESR_EL2_EC_DATA_ABORT) ||
+	    !(esr & ESR_EL2_ABORT_ISV_BIT)) {
+		/*
+		 * MMIO emulation is requested but the REC did not exit with
+		 * an emulatable exit.
+		 */
+		return false;
+	}
+
+	/*
+	 * Emulate mmio read (unless the load is to xzr)
+	 */
+	if (!esr_is_write(esr) && (rt != 31U)) {
+		unsigned long val;
+
+		val = rec_entry->gprs[0] & access_mask(esr);
+
+		if (esr_sign_extend(esr)) {
+			unsigned int bit_count = access_len(esr) * 8U;
+			unsigned long mask = 1UL << (bit_count - 1U);
+
+			val = (val ^ mask) - mask;
+			if (!esr_sixty_four(esr)) {
+				val &= (1UL << 32U) - 1UL;
+			}
+		}
+
+		rec->regs[rt] = val;
+	}
+
+	rec->pc = rec->pc + 4UL;
+	return true;
+}
+
+static void complete_set_ripas(struct rec *rec)
+{
+	if (rec->set_ripas.start != rec->set_ripas.end) {
+		/* Pending request from Realm */
+		rec->regs[0] = RSI_SUCCESS;
+		rec->regs[1] = rec->set_ripas.addr;
+
+		rec->set_ripas.start = 0UL;
+		rec->set_ripas.end = 0UL;
+	}
+}
+
+static bool complete_sea_insertion(struct rec *rec, struct rmi_rec_entry *rec_entry)
+{
+	unsigned long esr = rec->last_run_info.esr;
+	unsigned long fipa;
+	unsigned long hpfar = rec->last_run_info.hpfar;
+
+	if ((rec_entry->flags & REC_ENTRY_FLAG_INJECT_SEA) == 0UL) {
+		return true;
+	}
+
+	if ((esr & ESR_EL2_EC_MASK) != ESR_EL2_EC_DATA_ABORT) {
+		return false;
+	}
+
+	fipa = (hpfar & HPFAR_EL2_FIPA_MASK) << HPFAR_EL2_FIPA_OFFSET;
+	if (addr_in_rec_par(rec, fipa)) {
+		return false;
+	}
+
+	inject_sync_idabort_rec(rec, ESR_EL2_ABORT_FSC_SEA);
+	return true;
+}
+
+
+static void complete_sysreg_emulation(struct rec *rec, struct rmi_rec_entry *rec_entry)
+{
+	unsigned long esr = rec->last_run_info.esr;
+	unsigned int rt = esr_sysreg_rt(esr);
+
+	if ((esr & ESR_EL2_EC_MASK) != ESR_EL2_EC_SYSREG) {
+		return;
+	}
+
+	if (ESR_EL2_SYSREG_IS_WRITE(esr)) {
+		return;
+	}
+
+	/* Handle xzr */
+	if (rt != 31U) {
+		rec->regs[rt] = rec_entry->gprs[0];
+	}
+}
+
+static void complete_hvc_exit(struct rec *rec, struct rmi_rec_entry *rec_entry)
+{
+	unsigned long esr = rec->last_run_info.esr;
+	unsigned int i;
+
+	if ((esr & ESR_EL2_EC_MASK) != ESR_EL2_EC_HVC) {
+		return;
+	}
+
+	for (i = 0U; i < REC_EXIT_NR_GPRS; i++) {
+		rec->regs[i] = rec_entry->gprs[i];
+	}
+}
+
+static bool complete_host_call(struct rec *rec, struct rmi_rec_run *rec_run)
+{
+	struct rsi_walk_result walk_result;
+
+	if (!rec->host_call) {
+		return true;
+	}
+
+	walk_result = complete_rsi_host_call(rec, &rec_run->entry);
+
+	if (walk_result.abort) {
+		emulate_stage2_data_abort(rec, &rec_run->exit, walk_result.rtt_level);
+		return false;
+	}
+
+	rec->host_call = false;
+	return true;
+}
+
+unsigned long smc_rec_enter(unsigned long rec_addr,
+			    unsigned long rec_run_addr)
+{
+	struct granule *g_rec;
+	struct granule *g_run;
+	struct rec *rec;
+	struct rd *rd;
+	struct rmi_rec_run rec_run;
+	unsigned long realm_state, ret;
+	bool success;
+
+	/*
+	 * The content of `rec_run.exit` shall be returned to the host.
+	 * Zero the structure to avoid the leakage of
+	 * the content of the RMM's stack.
+	 */
+	(void)memset(&rec_run.exit, 0, sizeof(struct rmi_rec_exit));
+
+	g_run = find_granule(rec_run_addr);
+	if ((g_run == NULL) || (g_run->state != GRANULE_STATE_NS)) {
+		return RMI_ERROR_INPUT;
+	}
+
+	/* For a REC to be runnable, it should be unused (refcount = 0) */
+	g_rec = find_lock_unused_granule(rec_addr, GRANULE_STATE_REC);
+	if (ptr_is_err(g_rec)) {
+		return (unsigned long)ptr_status(g_rec);
+	}
+
+	/*
+	 * Increment refcount. REC can have lock-free access, thus atomic access
+	 * is required. Also, since the granule is only used for refcount
+	 * update, only an atomic operation will suffice and release/acquire
+	 * semantics are not required.
+	 */
+	atomic_granule_get(g_rec);
+
+	/* Unlock the granule before switching to realm world. */
+	granule_unlock(g_rec);
+
+	success = ns_buffer_read(SLOT_NS, g_run, 0U,
+				 sizeof(struct rmi_rec_entry), &rec_run.entry);
+
+	if (!success) {
+		/*
+		 * Decrement refcount. Lock-free access to REC, thus atomic and
+		 * release semantics is required.
+		 */
+		atomic_granule_put_release(g_rec);
+		return RMI_ERROR_INPUT;
+	}
+
+	rec = granule_map(g_rec, SLOT_REC);
+
+	rd = granule_map(rec->realm_info.g_rd, SLOT_RD);
+	realm_state = get_rd_state_unlocked(rd);
+	buffer_unmap(rd);
+
+	switch (realm_state) {
+	case REALM_STATE_NEW:
+		ret = pack_return_code(RMI_ERROR_REALM, 0U);
+		goto out_unmap_buffers;
+		break;
+	case REALM_STATE_ACTIVE:
+		break;
+	case REALM_STATE_SYSTEM_OFF:
+		ret = pack_return_code(RMI_ERROR_REALM, 1U);
+		goto out_unmap_buffers;
+		break;
+	default:
+		assert(false);
+		break;
+	}
+
+	if (!rec->runnable) {
+		ret = RMI_ERROR_REC;
+		goto out_unmap_buffers;
+	}
+
+	/* REC with pending PSCI command is not schedulable */
+	if (rec->psci_info.pending) {
+		ret = RMI_ERROR_REC;
+		goto out_unmap_buffers;
+	}
+
+	/*
+	 * Check GIC state after checking other conditions but before doing
+	 * anything which may have side effects.
+	 */
+	gic_copy_state_from_ns(&rec->sysregs.gicstate, &rec_run.entry);
+	if (!gic_validate_state(&rec->sysregs.gicstate)) {
+		ret = RMI_ERROR_REC;
+		goto out_unmap_buffers;
+	}
+
+	if (!complete_mmio_emulation(rec, &rec_run.entry)) {
+		ret = RMI_ERROR_REC;
+		goto out_unmap_buffers;
+	}
+
+	if (!complete_sea_insertion(rec, &rec_run.entry)) {
+		ret = RMI_ERROR_REC;
+		goto out_unmap_buffers;
+	}
+
+	complete_set_ripas(rec);
+	complete_sysreg_emulation(rec, &rec_run.entry);
+	complete_hvc_exit(rec, &rec_run.entry);
+
+	if (!complete_host_call(rec, &rec_run)) {
+		ret = RMI_SUCCESS;
+		goto out_unmap_buffers;
+	}
+
+	reset_last_run_info(rec);
+
+	rec->sysregs.hcr_el2 = rec->common_sysregs.hcr_el2;
+	if ((rec_run.entry.flags & REC_ENTRY_FLAG_TRAP_WFI) != 0UL) {
+		rec->sysregs.hcr_el2 |= HCR_TWI;
+	}
+	if ((rec_run.entry.flags & REC_ENTRY_FLAG_TRAP_WFE) != 0UL) {
+		rec->sysregs.hcr_el2 |= HCR_TWE;
+	}
+
+	ret = RMI_SUCCESS;
+
+	rec_run_loop(rec, &rec_run.exit);
+	/* Undo the heap association */
+
+	gic_copy_state_to_ns(&rec->sysregs.gicstate, &rec_run.exit);
+
+out_unmap_buffers:
+	buffer_unmap(rec);
+
+	if (ret == RMI_SUCCESS) {
+		if (!ns_buffer_write(SLOT_NS, g_run,
+				     offsetof(struct rmi_rec_run, exit),
+				     sizeof(struct rmi_rec_exit), &rec_run.exit)) {
+			ret = RMI_ERROR_INPUT;
+		}
+	}
+
+	atomic_granule_put_release(g_rec);
+
+	return ret;
+}
diff --git a/runtime/rmi/system.c b/runtime/rmi/system.c
new file mode 100644
index 0000000..24e53ef
--- /dev/null
+++ b/runtime/rmi/system.c
@@ -0,0 +1,16 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+#include <assert.h>
+#include <debug.h>
+#include <smc-handler.h>
+#include <smc-rmi.h>
+
+COMPILER_ASSERT(RMI_ABI_VERSION_MAJOR <= 0x7FFF);
+COMPILER_ASSERT(RMI_ABI_VERSION_MINOR <= 0xFFFF);
+
+unsigned long smc_version(void)
+{
+	return RMI_ABI_VERSION;
+}
diff --git a/runtime/rsi/config.c b/runtime/rsi/config.c
new file mode 100644
index 0000000..38a6446
--- /dev/null
+++ b/runtime/rsi/config.c
@@ -0,0 +1,67 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <granule.h>
+#include <realm.h>
+#include <rsi-config.h>
+#include <rsi-walk.h>
+#include <smc-rsi.h>
+
+struct rsi_config_result  handle_rsi_realm_config(struct rec *rec)
+{
+	struct rsi_config_result res = { 0 };
+	unsigned long ipa = rec->regs[1];
+	struct rd *rd;
+	enum s2_walk_status walk_status;
+	struct s2_walk_result walk_res;
+	struct granule *gr;
+	struct rsi_realm_config *config;
+
+	if (!GRANULE_ALIGNED(ipa) || !addr_in_rec_par(rec, ipa)) {
+		res.smc_res.x[0] = RSI_ERROR_INPUT;
+		return res;
+	}
+
+	rd = granule_map(rec->realm_info.g_rd, SLOT_RD);
+
+	walk_status = realm_ipa_to_pa(rd, ipa, &walk_res);
+
+	if (walk_status == WALK_FAIL) {
+		if (s2_walk_result_match_ripas(&walk_res, RMI_EMPTY)) {
+			res.smc_res.x[0] = RSI_ERROR_INPUT;
+		} else {
+			/* Exit to Host */
+			res.walk_result.abort = true;
+			res.walk_result.rtt_level = walk_res.rtt_level;
+		}
+		goto out_unmap_rd;
+	}
+
+	if (walk_status == WALK_INVALID_PARAMS) {
+		/* Return error to Realm */
+		res.smc_res.x[0] = RSI_ERROR_INPUT;
+		goto out_unmap_rd;
+	}
+
+	/* Map Realm data granule to RMM address space */
+	gr = find_granule(walk_res.pa);
+	config = (struct rsi_realm_config *)granule_map(gr, SLOT_RSI_CALL);
+
+	/* Populate config structure */
+	config->ipa_width = rec->realm_info.ipa_bits;
+
+	/* Unmap Realm data granule */
+	buffer_unmap(config);
+
+	/* Unlock last level RTT */
+	granule_unlock(walk_res.llt);
+
+	/* Write output values */
+	res.smc_res.x[0] = RSI_SUCCESS;
+
+out_unmap_rd:
+	buffer_unmap(rd);
+	return res;
+}
diff --git a/runtime/rsi/host_call.c b/runtime/rsi/host_call.c
new file mode 100644
index 0000000..3f70146
--- /dev/null
+++ b/runtime/rsi/host_call.c
@@ -0,0 +1,144 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <buffer.h>
+#include <granule.h>
+#include <realm.h>
+#include <rsi-host-call.h>
+#include <smc-rsi.h>
+#include <status.h>
+#include <string.h>
+
+/*
+ * If the RIPAS of the target IPA is empty then return value is RSI_ERROR_INPUT.
+ *
+ * If the RTT walk fails then:
+ *   - @rsi_walk_result.abort is true and @rsi_walk_result.rtt_level is the
+ *     last level reached by the walk.
+ *   - Return value is RSI_SUCCESS.
+ *
+ * If the RTT walk succeeds then:
+ *   - If @exit is not NULL and @entry is NULL, then copy host call arguments
+ *     from host call data structure (in Realm memory) to @exit.
+ *   - If @exit is NULL and @entry is not NULL, then copy host call results to
+ *     host call data structure (in Realm memory).
+ *   - Return value is RSI_SUCCESS.
+ */
+static unsigned int do_host_call(struct rec *rec,
+				 struct rmi_rec_exit *rec_exit,
+				 struct rmi_rec_entry *rec_entry,
+				 struct rsi_walk_result *rsi_walk_result)
+{
+	enum s2_walk_status walk_status;
+	struct s2_walk_result walk_result;
+	unsigned long ipa = rec->regs[1];
+	unsigned long page_ipa;
+	struct rd *rd;
+	struct granule *gr;
+	unsigned char *data;
+	struct rsi_host_call *host_call;
+	unsigned int i;
+	unsigned int ret = RSI_SUCCESS;
+
+	assert(addr_in_rec_par(rec, ipa));
+	assert(((unsigned long)rec_entry | (unsigned long)rec_exit) != 0UL);
+
+	rd = granule_map(rec->realm_info.g_rd, SLOT_RD);
+
+	page_ipa = ipa & GRANULE_MASK;
+	walk_status = realm_ipa_to_pa(rd, page_ipa, &walk_result);
+
+	switch (walk_status) {
+	case WALK_SUCCESS:
+		break;
+	case WALK_FAIL:
+		if (s2_walk_result_match_ripas(&walk_result, RMI_EMPTY)) {
+			ret = RSI_ERROR_INPUT;
+		} else {
+			rsi_walk_result->abort = true;
+			rsi_walk_result->rtt_level = walk_result.rtt_level;
+		}
+		goto out;
+	case WALK_INVALID_PARAMS:
+		assert(false);
+		break;
+	}
+
+	/* Map Realm data granule to RMM address space */
+	gr = find_granule(walk_result.pa);
+	data = (unsigned char *)granule_map(gr, SLOT_RSI_CALL);
+	host_call = (struct rsi_host_call *)(data + (ipa - page_ipa));
+
+	if (rec_exit != NULL) {
+		/* Copy host call arguments to REC exit data structure */
+		rec_exit->imm = host_call->imm;
+		for (i = 0U; i < RSI_HOST_CALL_NR_GPRS; i++) {
+			rec_exit->gprs[i] = host_call->gprs[i];
+		}
+	}
+
+	if (rec_entry != NULL) {
+		/* Copy host call results to host call data structure */
+		for (i = 0U; i < RSI_HOST_CALL_NR_GPRS; i++) {
+			host_call->gprs[i] = rec_entry->gprs[i];
+		}
+	}
+
+	/* Unmap Realm data granule */
+	buffer_unmap(data);
+
+	/* Unlock last level RTT */
+	granule_unlock(walk_result.llt);
+
+out:
+	buffer_unmap(rd);
+	return ret;
+}
+
+struct rsi_host_call_result handle_rsi_host_call(struct rec *rec,
+						 struct rmi_rec_exit *rec_exit)
+{
+	struct rsi_host_call_result res = { { false, 0UL } };
+	unsigned long ipa = rec->regs[1];
+
+	if (!ALIGNED(ipa, sizeof(struct rsi_host_call))) {
+		res.smc_result = RSI_ERROR_INPUT;
+		return res;
+	}
+
+	if ((ipa / GRANULE_SIZE) !=
+		((ipa + sizeof(struct rsi_host_call) - 1UL) / GRANULE_SIZE)) {
+		res.smc_result = RSI_ERROR_INPUT;
+		return res;
+	}
+
+	if (!addr_in_rec_par(rec, ipa)) {
+		res.smc_result = RSI_ERROR_INPUT;
+		return res;
+	}
+
+	res.smc_result = do_host_call(rec, rec_exit, NULL, &res.walk_result);
+
+	return res;
+}
+
+struct rsi_walk_result complete_rsi_host_call(struct rec *rec,
+					      struct rmi_rec_entry *rec_entry)
+{
+	struct rsi_walk_result res = { false, 0UL };
+
+	/*
+	 * Do the necessary to walk the S2 RTTs and copy args from NS Host
+	 * to the host call data structure. But it is possible for the
+	 * RIPAS of the IPA to be EMPTY and hence this call can return
+	 * RSI_ERROR_INPUT. In this case, we return RSI_SUCCESS to Realm
+	 * and Realm may take an abort on accessing the IPA (depending on
+	 * the RIPAS of IPA at that time). This is a situation which can be
+	 * controlled from Realm and Realm should avoid this.
+	 */
+	(void)do_host_call(rec, NULL, rec_entry, &res);
+
+	return res;
+}
diff --git a/runtime/rsi/logger.c b/runtime/rsi/logger.c
new file mode 100644
index 0000000..03b41b5
--- /dev/null
+++ b/runtime/rsi/logger.c
@@ -0,0 +1,134 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <assert.h>
+#include <debug.h>
+#include <psci.h>
+#include <rsi-logger.h>
+#include <smc-rsi.h>
+#include <utils_def.h>
+
+/* RMI handler uses 29 chars for function name */
+#define	MAX_NAME_LEN	29U
+
+/* 5 64-bit parameters separated by space + 1 trailing space */
+#define PARAMS_STR_LEN	(5U * sizeof("0123456789ABCDEF") + 1U)
+
+#define	MAX_STATUS_LEN	sizeof("{RSI_ERROR_INPUT}")
+
+#define	BUFFER_SIZE	(MAX_NAME_LEN + PARAMS_STR_LEN +	\
+			sizeof("> ") - 1U +		\
+			MAX_STATUS_LEN)
+
+#define RSI_FUNCTION(id) \
+	[SMC_RSI_##id - SMC_RSI_ABI_VERSION] = #id
+
+static const char *rsi_logger[] = {
+	RSI_FUNCTION(ABI_VERSION),		/* 0xC4000190 */
+	RSI_FUNCTION(MEASUREMENT_READ),		/* 0xC4000192 */
+	RSI_FUNCTION(MEASUREMENT_EXTEND),	/* 0xC4000193 */
+	RSI_FUNCTION(ATTEST_TOKEN_INIT),	/* 0xC4000194 */
+	RSI_FUNCTION(ATTEST_TOKEN_CONTINUE),	/* 0xC4000195 */
+	RSI_FUNCTION(REALM_CONFIG),		/* 0xC4000196 */
+	RSI_FUNCTION(IPA_STATE_SET),		/* 0xC4000197 */
+	RSI_FUNCTION(IPA_STATE_GET),		/* 0xC4000198 */
+	RSI_FUNCTION(HOST_CALL)			/* 0xC4000199 */
+};
+
+#define RSI_STATUS_HANDLER(id)[id] = #id
+
+const char *rsi_status_handler[] = {
+	RSI_STATUS_HANDLER(RSI_SUCCESS),
+	RSI_STATUS_HANDLER(RSI_ERROR_INPUT),
+	RSI_STATUS_HANDLER(RSI_ERROR_STATE),
+	RSI_STATUS_HANDLER(RSI_INCOMPLETE)
+};
+
+COMPILER_ASSERT(ARRAY_LEN(rsi_status_handler) == RSI_ERROR_COUNT);
+
+static int print_entry(unsigned int id, unsigned long args[5],
+		       char *buf, size_t len)
+{
+	char name[sizeof("SMC_RSI_ATTEST_TOKEN_CONTINUE")];
+	int cnt __unused;
+
+	switch (id) {
+	case SMC_RSI_ABI_VERSION ... SMC_RSI_HOST_CALL:
+
+		if (rsi_logger[id - SMC_RSI_ABI_VERSION] != NULL) {
+			cnt = snprintf(name, sizeof(name), "%s%s", "SMC_RSI_",
+			       rsi_logger[id - SMC_RSI_ABI_VERSION]);
+		} else {
+			/* Handle gaps in RSI commands numbering */
+			cnt = snprintf(name, sizeof(name), "%s%08x", "SMC_RSI_", id);
+		}
+
+		break;
+
+	/* SMC32 PSCI calls */
+	case SMC32_PSCI_FID_MIN ... SMC32_PSCI_FID_MAX:
+		FALLTHROUGH;
+	case SMC64_PSCI_FID_MIN ... SMC64_PSCI_FID_MAX:
+		cnt = snprintf(name, sizeof(name), "%s%08x", "PSCI_", id);
+		break;
+
+	/* Other SMC calls */
+	default:
+		cnt = snprintf(name, sizeof(name), "%s%08x", "SMC_", id);
+		break;
+	}
+
+	assert((cnt > 0) && (cnt < sizeof(name)));
+
+	return snprintf(buf, len, "%-29s %8lx %8lx %8lx %8lx %8lx ",
+			name, args[0], args[1], args[2], args[3], args[4]);
+}
+
+static int print_status(char *buf, size_t len, unsigned long res)
+{
+	return_code_t rc = unpack_return_code(res);
+
+	if ((unsigned long)rc.status >= RSI_ERROR_COUNT) {
+		return snprintf(buf, len, "> %lx", res);
+	}
+
+	return snprintf(buf, len, "> %s",
+			rsi_status_handler[rc.status]);
+}
+
+static int print_code(char *buf, size_t len, unsigned long res)
+{
+	return snprintf(buf, len, "> %lx", res);
+}
+
+void rsi_log_on_exit(unsigned int function_id, unsigned long args[5],
+		     unsigned long res, bool exit_to_rec)
+{
+	char buffer[BUFFER_SIZE];
+	char *buf_ptr = buffer;
+	size_t buf_len = sizeof(buffer);
+	int cnt = print_entry(function_id, args, buf_ptr, buf_len);
+
+	assert((cnt > 0) && (cnt < buf_len));
+
+	buf_ptr += cnt;
+	buf_len -= cnt;
+
+	/* Print result when execution continues in REC */
+	if (exit_to_rec) {
+		if ((function_id >= SMC_RSI_MEASUREMENT_READ) &&
+		    (function_id <= SMC_RSI_HOST_CALL)) {
+			/* Print status */
+			cnt = print_status(buf_ptr, buf_len, res);
+		} else {
+			/* Print result code */
+			cnt = print_code(buf_ptr, buf_len, res);
+		}
+
+		assert((cnt > 0) && (cnt < buf_len));
+	}
+
+	rmm_log("%s\n", buffer);
+}
diff --git a/runtime/rsi/memory.c b/runtime/rsi/memory.c
new file mode 100644
index 0000000..c59272b
--- /dev/null
+++ b/runtime/rsi/memory.c
@@ -0,0 +1,74 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <realm.h>
+#include <ripas.h>
+#include <rsi-memory.h>
+#include <smc-rsi.h>
+#include <status.h>
+
+bool handle_rsi_ipa_state_set(struct rec *rec, struct rmi_rec_exit *rec_exit)
+{
+	unsigned long start = rec->regs[1];
+	unsigned long size = rec->regs[2];
+	unsigned long end = start + size;
+	enum ripas ripas = (enum ripas)rec->regs[3];
+
+	if (ripas > RMI_RAM) {
+		return true;
+	}
+
+	if (!GRANULE_ALIGNED(start)) {
+		return true;
+	}
+
+	if (!GRANULE_ALIGNED(size)) {
+		return true;
+	}
+
+	if (end <= start) {
+		/* Size is zero, or range overflows */
+		return true;
+	}
+
+	if (!region_in_rec_par(rec, start, end)) {
+		return true;
+	}
+
+	rec->set_ripas.start = start;
+	rec->set_ripas.end = end;
+	rec->set_ripas.addr = start;
+	rec->set_ripas.ripas = ripas;
+
+	rec_exit->exit_reason = RMI_EXIT_RIPAS_CHANGE;
+	rec_exit->ripas_base = start;
+	rec_exit->ripas_size = size;
+	rec_exit->ripas_value = (unsigned int)ripas;
+
+	return false;
+}
+
+rsi_status_t handle_rsi_ipa_state_get(struct rec *rec, unsigned long ipa,
+				      enum ripas *ripas_ptr)
+{
+	bool s2tte_destroyed;
+
+	if (!GRANULE_ALIGNED(ipa)) {
+		return RSI_ERROR_INPUT;
+	}
+
+	if (!addr_in_rec_par(rec, ipa)) {
+		return RSI_ERROR_INPUT;
+	}
+
+	realm_ipa_get_ripas(rec, ipa, ripas_ptr, &s2tte_destroyed);
+	if (s2tte_destroyed == true) {
+		/* TODO: handle destroyed state appropriately */
+		return RSI_ERROR_INPUT;
+	}
+
+	return RSI_SUCCESS;
+}
diff --git a/runtime/rsi/psci.c b/runtime/rsi/psci.c
new file mode 100644
index 0000000..737fdad
--- /dev/null
+++ b/runtime/rsi/psci.c
@@ -0,0 +1,369 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <granule.h>
+#include <psci.h>
+#include <realm.h>
+#include <rec.h>
+#include <smc-rmi.h>
+#include <smc.h>
+#include <stdint.h>
+
+static struct psci_result psci_version(struct rec *rec)
+{
+	struct psci_result result = { 0 };
+	unsigned int version_1_1 = (1U << 16) | 1U;
+
+	result.smc_res.x[0] = (unsigned long)version_1_1;
+	return result;
+}
+
+static struct psci_result psci_cpu_suspend(struct rec *rec,
+					  unsigned long entry_point_address,
+					  unsigned long context_id)
+{
+	struct psci_result result = { 0 };
+
+	/*
+	 * We treat all target power states as suspend requests, so all we
+	 * need to do is inform that NS hypervisor and we can ignore all the
+	 * parameters.
+	 */
+	result.hvc_forward.forward_psci_call = true;
+
+	result.smc_res.x[0] = PSCI_RETURN_SUCCESS;
+	return result;
+}
+
+static struct psci_result psci_cpu_off(struct rec *rec)
+{
+	struct psci_result result = { 0 };
+
+	result.hvc_forward.forward_psci_call = true;
+
+	/*
+	 * It should be fine to set this flag without holding a lock on the
+	 * REC or without explicit memory barriers or ordering semantics
+	 * operations, because we already ensure that a REC can only be in an
+	 * executing state once at any given time, and we're in this execution
+	 * context already, and we will be holding a reference count on the
+	 * REC at this point, which will be dropped and re-evaluated with
+	 * proper barriers before any CPU can evaluate the runnable field
+	 * after this change.
+	 */
+	rec->runnable = false;
+
+	result.smc_res.x[0] = PSCI_RETURN_SUCCESS;
+	return result;
+}
+
+static void psci_reset_rec(struct rec *rec, unsigned long caller_sctlr_el1)
+{
+	/* Set execution level to EL1 (AArch64) and mask exceptions */
+	rec->pstate = SPSR_EL2_MODE_EL1h |
+		      SPSR_EL2_nRW_AARCH64 |
+		      SPSR_EL2_F_BIT |
+		      SPSR_EL2_I_BIT |
+		      SPSR_EL2_A_BIT |
+		      SPSR_EL2_D_BIT;
+
+	/* Disable stage 1 MMU and caches */
+	rec->sysregs.sctlr_el1 = SCTLR_EL1_FLAGS;
+
+	/* Set the endianness of the target to that of the caller */
+	rec->sysregs.sctlr_el1 |= caller_sctlr_el1 & SCTLR_EL1_EE;
+}
+
+static unsigned long rd_map_read_rec_count(struct granule *g_rd)
+{
+	unsigned long rec_count;
+	struct rd *rd = granule_map(g_rd, SLOT_RD);
+
+	rec_count = get_rd_rec_count_unlocked(rd);
+	buffer_unmap(rd);
+	return rec_count;
+}
+
+static struct psci_result psci_cpu_on(struct rec *rec,
+				      unsigned long target_cpu,
+				      unsigned long entry_point_address,
+				      unsigned long context_id)
+{
+	struct psci_result result = { 0 };
+	unsigned long target_rec_idx;
+
+	/* Check that entry_point_address is a Protected Realm Address */
+	if (!addr_in_rec_par(rec, entry_point_address)) {
+		result.smc_res.x[0] = PSCI_RETURN_INVALID_ADDRESS;
+		return result;
+	}
+
+	/* Get REC index from MPIDR */
+	target_rec_idx = mpidr_to_rec_idx(target_cpu);
+
+	/*
+	 * Check that the target_cpu is a valid value.
+	 * Note that the RMM enforces that the REC are created with
+	 * consecutively increasing indexes starting from zero.
+	 */
+	if (target_rec_idx >= rd_map_read_rec_count(rec->realm_info.g_rd)) {
+		result.smc_res.x[0] = PSCI_RETURN_INVALID_PARAMS;
+		return result;
+	}
+
+	/* Check if we're trying to turn ourselves on */
+	if (target_rec_idx == rec->rec_idx) {
+		result.smc_res.x[0] = PSCI_RETURN_ALREADY_ON;
+		return result;
+	}
+
+	rec->psci_info.pending = true;
+
+	result.hvc_forward.forward_psci_call = true;
+	result.hvc_forward.x1 = target_cpu;
+	return result;
+}
+
+static struct psci_result psci_affinity_info(struct rec *rec,
+					     unsigned long target_affinity,
+					     unsigned long lowest_affinity_level)
+{
+	struct psci_result result = { 0 };
+	unsigned long target_rec_idx;
+
+	if (lowest_affinity_level != 0UL) {
+		result.smc_res.x[0] = PSCI_RETURN_INVALID_PARAMS;
+		return result;
+	}
+
+	/* Get REC index from MPIDR */
+	target_rec_idx = mpidr_to_rec_idx(target_affinity);
+
+	/*
+	 * Check that the target_affinity is a valid value.
+	 * Note that the RMM enforces that the REC are created with
+	 * consecutively increasing indexes starting from zero.
+	 */
+	if (target_rec_idx >= rd_map_read_rec_count(rec->realm_info.g_rd)) {
+		result.smc_res.x[0] = PSCI_RETURN_INVALID_PARAMS;
+		return result;
+	}
+
+	/* Check if the vCPU targets itself */
+	if (target_rec_idx == rec->rec_idx) {
+		result.smc_res.x[0] = PSCI_AFFINITY_INFO_ON;
+		return result;
+	}
+
+	rec->psci_info.pending = true;
+
+	result.hvc_forward.forward_psci_call = true;
+	result.hvc_forward.x1 = target_affinity;
+	return result;
+}
+
+/*
+ * Turning a system off or requesting a reboot of a realm is enforced by the
+ * RMM by preventing execution of a REC after the function has run.  Reboot
+ * functionality must be provided by the host hypervisor by creating a new
+ * Realm with associated attestation, measurement etc.
+ */
+static void system_off_reboot(struct rec *rec)
+{
+	struct rd *rd;
+	struct granule *g_rd = rec->realm_info.g_rd;
+
+	/*
+	 * The RECs (and, consequently, the PSCI calls) run without any
+	 * RMM lock held. Therefore, we cannot cause a deadlock when we acquire
+	 * the rd lock here before we set the Realm's new state.
+	 */
+	granule_lock(g_rd, GRANULE_STATE_RD);
+	rd = granule_map(rec->realm_info.g_rd, SLOT_RD);
+
+	set_rd_state(rd, REALM_STATE_SYSTEM_OFF);
+
+	buffer_unmap(rd);
+	granule_unlock(g_rd);
+
+	/* TODO: Invalidate all stage 2 entris to ensure REC exits */
+}
+
+static struct psci_result psci_system_off(struct rec *rec)
+{
+	struct psci_result result = { 0 };
+
+	system_off_reboot(rec);
+
+	result.hvc_forward.forward_psci_call = true;
+	return result;
+}
+
+static struct psci_result psci_system_reset(struct rec *rec)
+{
+	struct psci_result result = { 0 };
+
+	system_off_reboot(rec);
+
+	result.hvc_forward.forward_psci_call = true;
+	return result;
+}
+
+static struct psci_result psci_features(struct rec *rec,
+				       unsigned int psci_func_id)
+{
+	struct psci_result result = { 0 };
+	unsigned long ret;
+
+	switch (psci_func_id) {
+	case SMC32_PSCI_CPU_SUSPEND:
+	case SMC64_PSCI_CPU_SUSPEND:
+	case SMC32_PSCI_CPU_OFF:
+	case SMC32_PSCI_CPU_ON:
+	case SMC64_PSCI_CPU_ON:
+	case SMC32_PSCI_AFFINITY_INFO:
+	case SMC64_PSCI_AFFINITY_INFO:
+	case SMC32_PSCI_SYSTEM_OFF:
+	case SMC32_PSCI_SYSTEM_RESET:
+	case SMC32_PSCI_FEATURES:
+	case SMCCC_VERSION:
+		ret = 0UL;
+		break;
+	default:
+		ret = PSCI_RETURN_NOT_SUPPORTED;
+	}
+
+	result.smc_res.x[0] = ret;
+	return result;
+}
+
+struct psci_result psci_rsi(struct rec *rec,
+			    unsigned int function_id,
+			    unsigned long arg0,
+			    unsigned long arg1,
+			    unsigned long arg2)
+{
+	struct psci_result result;
+
+	switch (function_id) {
+	case SMC32_PSCI_VERSION:
+		result = psci_version(rec);
+		break;
+	case SMC32_PSCI_CPU_SUSPEND:
+	case SMC64_PSCI_CPU_SUSPEND:
+		result = psci_cpu_suspend(rec, arg0, arg1);
+		break;
+	case SMC32_PSCI_CPU_OFF:
+		result = psci_cpu_off(rec);
+		break;
+	case SMC32_PSCI_CPU_ON:
+		arg0 = (unsigned int)arg0;
+		arg1 = (unsigned int)arg1;
+		arg2 = (unsigned int)arg2;
+		/* Fall through */
+	case SMC64_PSCI_CPU_ON:
+		result = psci_cpu_on(rec, arg0, arg1, arg2);
+		break;
+	case SMC32_PSCI_AFFINITY_INFO:
+		arg0 = (unsigned int)arg0;
+		arg1 = (unsigned int)arg1;
+		FALLTHROUGH;
+	case SMC64_PSCI_AFFINITY_INFO:
+		result = psci_affinity_info(rec, arg0, arg1);
+		break;
+	case SMC32_PSCI_SYSTEM_OFF:
+		result = psci_system_off(rec);
+		break;
+	case SMC32_PSCI_SYSTEM_RESET:
+		result = psci_system_reset(rec);
+		break;
+	case SMC32_PSCI_FEATURES:
+		result = psci_features(rec, arg0);
+		break;
+	default:
+		result.smc_res.x[0] = PSCI_RETURN_NOT_SUPPORTED;
+		result.hvc_forward.forward_psci_call = false;
+		break;
+	}
+
+	return result;
+}
+
+/*
+ * In the following two functions, it is only safe to access the runnable field
+ * on the target_rec once the target_rec is no longer running on another PE and
+ * all writes performed by the other PE as part of smc_rec_enter is also
+ * guaranteed to be observed here, which we know when we read a zero refcount
+ * on the target rec using acquire semantics paired with the release semantics
+ * on the reference count in smc_rec_enter. If we observe a non-zero refcount
+ * it simply means that the target_rec is running and we can return the
+ * corresponding value.
+ */
+static unsigned long complete_psci_cpu_on(struct rec *target_rec,
+					  unsigned long entry_point_address,
+					  unsigned long caller_sctlr_el1)
+{
+	if ((granule_refcount_read_acquire(target_rec->g_rec) != 0UL) ||
+		target_rec->runnable) {
+		return PSCI_RETURN_ALREADY_ON;
+	}
+
+	psci_reset_rec(target_rec, caller_sctlr_el1);
+	target_rec->pc = entry_point_address;
+	target_rec->runnable = true;
+	return PSCI_RETURN_SUCCESS;
+}
+
+static unsigned long complete_psci_affinity_info(struct rec *target_rec)
+{
+	if ((granule_refcount_read_acquire(target_rec->g_rec) != 0UL) ||
+		target_rec->runnable) {
+		return PSCI_AFFINITY_INFO_ON;
+	}
+
+	return PSCI_AFFINITY_INFO_OFF;
+}
+
+unsigned long psci_complete_request(struct rec *calling_rec,
+				    struct rec *target_rec)
+{
+	unsigned long ret = PSCI_RETURN_NOT_SUPPORTED;
+	unsigned long mpidr = calling_rec->regs[1];
+
+	if (!calling_rec->psci_info.pending) {
+		return RMI_ERROR_INPUT;
+	}
+
+	if (calling_rec->realm_info.g_rd != target_rec->realm_info.g_rd) {
+		return RMI_ERROR_INPUT;
+	}
+
+	if (mpidr_to_rec_idx(mpidr) != target_rec->rec_idx) {
+		return RMI_ERROR_INPUT;
+	}
+
+	switch (calling_rec->regs[0]) {
+	case SMC32_PSCI_CPU_ON:
+	case SMC64_PSCI_CPU_ON:
+		ret = complete_psci_cpu_on(target_rec,
+					   calling_rec->regs[2],
+					   calling_rec->sysregs.sctlr_el1);
+		break;
+	case SMC32_PSCI_AFFINITY_INFO:
+	case SMC64_PSCI_AFFINITY_INFO:
+		ret = complete_psci_affinity_info(target_rec);
+		break;
+	default:
+		assert(false);
+	}
+
+	calling_rec->regs[0] = ret;
+	calling_rec->regs[1] = 0;
+	calling_rec->regs[2] = 0;
+	calling_rec->regs[3] = 0;
+	calling_rec->psci_info.pending = false;
+
+	return RMI_SUCCESS;
+}
diff --git a/runtime/rsi/realm_attest.c b/runtime/rsi/realm_attest.c
new file mode 100644
index 0000000..a55f32d
--- /dev/null
+++ b/runtime/rsi/realm_attest.c
@@ -0,0 +1,379 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <attestation.h>
+#include <attestation_token.h>
+#include <debug.h>
+#include <granule.h>
+#include <measurement.h>
+#include <realm.h>
+#include <realm_attest.h>
+#include <smc-rsi.h>
+#include <smc.h>
+#include <string.h>
+#include <utils_def.h>
+
+#define MAX_EXTENDED_SIZE		(64U)
+
+/*
+ * Return the Realm Personalization Value.
+ *
+ * Arguments:
+ * rd    - The Realm descriptor.
+ * claim - The structure to return the Realm Personalization Value claim
+ */
+static void get_rpv(struct rd *rd, struct q_useful_buf_c *claim)
+{
+	claim->ptr = (uint8_t *)&(rd->rpv[0]);
+	claim->len = RPV_SIZE;
+}
+
+/*
+ * Save the input parameters in the context for later iterations to check for
+ * consistency.
+ */
+static void save_input_parameters(struct rec *rec)
+{
+	rec->token_sign_ctx.token_ipa = rec->regs[1];
+	(void)memcpy(rec->token_sign_ctx.challenge, &rec->regs[2],
+		     ATTEST_CHALLENGE_SIZE);
+}
+
+/*
+ * Verify that in all the iterations the input parameters are the same
+ * as in the initial call.
+ */
+static bool verify_input_parameters_consistency(struct rec *rec)
+{
+	return rec->token_sign_ctx.token_ipa == rec->regs[1];
+}
+
+/*
+ * Function to continue with the sign operation.
+ * It returns void as the result will be updated in the
+ * struct attest_result passed as argument.
+ */
+static void attest_token_continue_sign_state(struct rec *rec,
+					     struct attest_result *res)
+{
+	/*
+	 * Sign and finish creating the token.
+	 */
+	enum attest_token_err_t ret =
+		attest_realm_token_sign(&(rec->token_sign_ctx.ctx),
+					&(rec->rmm_realm_token));
+
+	if ((ret == ATTEST_TOKEN_ERR_COSE_SIGN_IN_PROGRESS) ||
+		(ret == ATTEST_TOKEN_ERR_SUCCESS)) {
+		/*
+		 * Return to RSI handler function after each iteration
+		 * to check is there anything else to do (pending IRQ)
+		 * or next signing iteration can be executed.
+		 */
+		res->incomplete = true;
+		res->smc_res.x[0] = RSI_INCOMPLETE;
+
+		/* If this was the last signing cycle */
+		if (ret == ATTEST_TOKEN_ERR_SUCCESS) {
+			rec->token_sign_ctx.state =
+				ATTEST_SIGN_TOKEN_WRITE_IN_PROGRESS;
+		}
+	} else {
+		/* Accessible only in case of failure during token signing */
+		ERROR("FATAL_ERROR: Realm token creation failed\n");
+		panic();
+	}
+}
+
+/*
+ * Function to continue with the token write operation.
+ * It returns void as the result will be updated in the
+ * struct attest_result passed as argument.
+ */
+static void attest_token_continue_write_state(struct rec *rec,
+					      struct attest_result *res)
+{
+	struct rd *rd = NULL;
+	struct granule *gr;
+	uint8_t *realm_att_token;
+	unsigned long realm_att_token_ipa = rec->regs[1];
+	enum s2_walk_status walk_status;
+	struct s2_walk_result walk_res = { 0UL };
+	struct q_useful_buf     attest_token_buf;
+	size_t    attest_token_len;
+
+	/*
+	 * The refcount on rd and rec will protect from any changes
+	 * while REC is running.
+	 */
+	rd = granule_map(rec->realm_info.g_rd, SLOT_RD);
+
+	/*
+	 * Translate realm granule IPA to PA. If returns with
+	 * WALK_SUCCESS then the last level page table (llt),
+	 * which holds the realm_att_token_buf mapping, is locked.
+	 */
+	walk_status = realm_ipa_to_pa(rd, realm_att_token_ipa, &walk_res);
+	buffer_unmap(rd);
+
+	/* Walk parameter validity was checked by RSI_ATTESTATION_TOKEN_INIT */
+	assert(walk_status != WALK_INVALID_PARAMS);
+
+	if (walk_status == WALK_FAIL) {
+		if (s2_walk_result_match_ripas(&walk_res, RMI_EMPTY)) {
+			res->smc_res.x[0] = RSI_ERROR_INPUT;
+		} else {
+			/*
+			 * Translation failed, IPA is not mapped. Return to NS host to
+			 * fix the issue.
+			 */
+			res->walk_result.abort = true;
+			res->walk_result.rtt_level = walk_res.rtt_level;
+			res->smc_res.x[0] = RSI_INCOMPLETE;
+		}
+		return;
+	}
+
+	/* Map realm data granule to RMM address space */
+	gr = find_granule(walk_res.pa);
+	realm_att_token = granule_map(gr, SLOT_RSI_CALL);
+
+	attest_token_buf.ptr = realm_att_token;
+	attest_token_buf.len = ATTEST_TOKEN_BUFFER_SIZE;
+
+	attest_token_len = attest_cca_token_create(&attest_token_buf,
+						   &rec->rmm_realm_token);
+
+	/* Unmap realm granule */
+	buffer_unmap(realm_att_token);
+
+	/* Unlock last level page table (walk_res.g_llt) */
+	granule_unlock(walk_res.llt);
+
+	/* Write output parameters */
+	if (attest_token_len == 0) {
+		res->smc_res.x[0] = RSI_ERROR_INPUT;
+	} else {
+		res->smc_res.x[0] = RSI_SUCCESS;
+		res->smc_res.x[1] = attest_token_len;
+	}
+
+	/* The signing has either succeeded or failed. Reset the state. */
+	rec->token_sign_ctx.state = ATTEST_SIGN_NOT_STARTED;
+}
+
+unsigned long handle_rsi_attest_token_init(struct rec *rec)
+{
+	struct rd *rd = NULL;
+	unsigned long ret;
+	unsigned long realm_buf_ipa = rec->regs[1];
+	struct q_useful_buf rmm_realm_token_buf = {
+		rec->rmm_realm_token_buf, sizeof(rec->rmm_realm_token_buf)};
+	struct q_useful_buf_c rpv;
+	int att_ret;
+
+	assert(rec != NULL);
+
+	/*
+	 * Calling RSI_ATTESTATION_TOKEN_INIT any time aborts any ongoing
+	 * operation.
+	 * TODO: This can be moved to attestation lib
+	 */
+	if (rec->token_sign_ctx.state != ATTEST_SIGN_NOT_STARTED) {
+		int restart;
+
+		rec->token_sign_ctx.state = ATTEST_SIGN_NOT_STARTED;
+		restart = attestation_heap_reinit_pe(rec->aux_data.attest_heap_buf,
+						      REC_HEAP_PAGES * SZ_4K);
+		if (restart != 0) {
+			/* There is no provision for this failure so panic */
+			panic();
+		}
+	}
+
+	if (!GRANULE_ALIGNED(realm_buf_ipa)) {
+		return RSI_ERROR_INPUT;
+	}
+
+	/*
+	 * rd lock is acquired so that measurement cannot be updated
+	 * simultaneously by another rec
+	 */
+	granule_lock(rec->realm_info.g_rd, GRANULE_STATE_RD);
+	rd = granule_map(rec->realm_info.g_rd, SLOT_RD);
+	if (!addr_in_par(rd, realm_buf_ipa)) {
+		ret = RSI_ERROR_INPUT;
+		goto out_unmap_rd;
+	}
+
+	/*
+	 * Save the input parameters in the context for later iterations
+	 * to check.
+	 */
+	save_input_parameters(rec);
+
+	get_rpv(rd, &rpv);
+	att_ret = attest_realm_token_create(rd->algorithm, rd->measurement,
+					    MEASUREMENT_SLOT_NR,
+					    &rpv,
+					    &rec->token_sign_ctx,
+					    &rmm_realm_token_buf);
+	if (att_ret != 0) {
+		ERROR("FATAL_ERROR: Realm token creation failed,\n");
+		panic();
+	}
+
+	rec->token_sign_ctx.state = ATTEST_SIGN_IN_PROGRESS;
+	ret = RSI_SUCCESS;
+
+out_unmap_rd:
+	buffer_unmap(rd);
+	granule_unlock(rec->realm_info.g_rd);
+	return ret;
+}
+
+void attest_realm_token_sign_continue_start(void)
+{
+	fpu_save_my_state();
+}
+
+void attest_realm_token_sign_continue_finish(void)
+{
+	fpu_restore_my_state();
+}
+
+void handle_rsi_attest_token_continue(struct rec *rec,
+				      struct attest_result *res)
+{
+	assert(rec != NULL);
+	assert(res != NULL);
+
+	/* Initialize attest_result */
+	res->incomplete = false;
+	res->walk_result.abort = false;
+
+	if (!verify_input_parameters_consistency(rec)) {
+		res->smc_res.x[0] = RSI_ERROR_INPUT;
+		return;
+	}
+
+	switch (rec->token_sign_ctx.state) {
+	case ATTEST_SIGN_NOT_STARTED:
+		/*
+		 * Before this call the initial attestation token call
+		 * (SMC_RSI_ATTEST_TOKEN_INIT) must have been executed
+		 * successfully.
+		 */
+		res->smc_res.x[0] = RSI_ERROR_STATE;
+		break;
+	case ATTEST_SIGN_IN_PROGRESS:
+		attest_token_continue_sign_state(rec, res);
+		break;
+	case ATTEST_SIGN_TOKEN_WRITE_IN_PROGRESS:
+		attest_token_continue_write_state(rec, res);
+		break;
+	default:
+		/* Any other state is considered an error. */
+		assert(false);
+	}
+}
+
+unsigned long handle_rsi_extend_measurement(struct rec *rec)
+{
+	struct granule *g_rd;
+	struct rd *rd;
+	unsigned long index;
+	unsigned long rd_addr;
+	size_t size;
+	unsigned long ret;
+	void *extend_measurement;
+	unsigned char *current_measurement;
+	int __unused meas_ret;
+
+	/*
+	 * rd lock is acquired so that measurement cannot be updated
+	 * simultaneously by another rec
+	 */
+	rd_addr = granule_addr(rec->realm_info.g_rd);
+	g_rd = find_lock_granule(rd_addr, GRANULE_STATE_RD);
+
+	assert(g_rd != NULL);
+
+	rd = granule_map(rec->realm_info.g_rd, SLOT_RD);
+
+	/*
+	 * X1:     index
+	 * X2:     size
+	 * X3-X10: measurement value
+	 */
+	index = rec->regs[1];
+
+	if ((index == RIM_MEASUREMENT_SLOT) ||
+	    (index >= MEASUREMENT_SLOT_NR)) {
+		ret = RSI_ERROR_INPUT;
+		goto out_unmap_rd;
+	}
+
+	size  = rec->regs[2];
+
+	if (size > MAX_EXTENDED_SIZE) {
+		ret = RSI_ERROR_INPUT;
+		goto out_unmap_rd;
+	}
+
+	extend_measurement = &rec->regs[3];
+	current_measurement = rd->measurement[index];
+
+	measurement_extend(rd->algorithm,
+			   current_measurement,
+			   extend_measurement,
+			   size,
+			   current_measurement);
+
+	ret = RSI_SUCCESS;
+
+out_unmap_rd:
+	buffer_unmap(rd);
+	granule_unlock(g_rd);
+	return ret;
+}
+
+unsigned long handle_rsi_read_measurement(struct rec *rec)
+{
+	struct rd *rd;
+	unsigned long idx;
+	size_t measurement_size;
+
+	assert(rec != NULL);
+
+	/* X1: Index */
+	idx = rec->regs[1];
+
+	if (idx >= MEASUREMENT_SLOT_NR) {
+		return RSI_ERROR_INPUT;
+	}
+
+	/*
+	 * rd lock is acquired so that measurement cannot be updated
+	 * simultaneously by another rec
+	 */
+	granule_lock(rec->realm_info.g_rd, GRANULE_STATE_RD);
+	rd = granule_map(rec->realm_info.g_rd, SLOT_RD);
+
+	measurement_size = measurement_get_size(rd->algorithm);
+
+	(void)memcpy(&rec->regs[1], rd->measurement[idx], measurement_size);
+
+	/* Zero-initialize the unused area */
+	if (measurement_size < MAX_MEASUREMENT_SIZE) {
+		(void)memset((char *)(&rec->regs[1]) + measurement_size,
+			     0, MAX_MEASUREMENT_SIZE - measurement_size);
+	}
+
+	buffer_unmap(rd);
+	granule_unlock(rec->realm_info.g_rd);
+
+	return RSI_SUCCESS;
+}
diff --git a/runtime/rsi/realm_ipa_helper.c b/runtime/rsi/realm_ipa_helper.c
new file mode 100644
index 0000000..d6e38e9
--- /dev/null
+++ b/runtime/rsi/realm_ipa_helper.c
@@ -0,0 +1,137 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <buffer.h>
+#include <granule.h>
+#include <realm.h>
+
+/**
+ * Translate a realm granule IPA to PA.
+ *
+ * Parameters:
+ * [in]   rd		    Pointer to realm descriptor granule.
+ * [in]   ipa		    The intermediate physical address of the realm granule.
+ * [in]   s2_walk	    Address of s2_walk_result structure to return:
+ * [out]  s2_walk.pa	    The physical address of the realm granule.
+ * [out]  s2_walk.rtt_level The last level reached by the table walk.
+ * [out]  s2_walk.ripas	    RIPAS of s2tte.
+ * [out]  s2_walk.destroyed 'true', if s2tte has HIPAS=DESTROYED.
+ * [out]  s2_walk.llt	    Pointer to the last level page table which contains
+ *			    the mapping of the granule. If function returns with
+ *			    WALK_SUCCESS then 'llt' must be unlocked by the caller.
+ *			    Lock avoids to destoy the realm granule while RMM
+ *			    accessing to it.
+ * Returns:
+ * WALK_SUCCESS		Translation succeeded.
+ * WALK_INVALID_PARAMS	Parameter 'ipa' is unaligned or is not a Protected IPA.
+ * WALK_FAIL		Mapping is not in the page table. NS Host needs to fix.
+ */
+enum s2_walk_status realm_ipa_to_pa(struct rd *rd,
+				    unsigned long ipa,
+				    struct s2_walk_result *s2_walk)
+{
+	struct granule *g_table_root;
+	struct rtt_walk wi;
+	unsigned long s2tte, *ll_table, offset;
+	enum s2_walk_status walk_status;
+
+	if (!GRANULE_ALIGNED(ipa) || !addr_in_par(rd, ipa)) {
+		return WALK_INVALID_PARAMS;
+	}
+
+	/*
+	 * SW table walk to find corresponding PA. It handles cases when buffer
+	 * is mapped on page level or on block level.
+	 *
+	 * Todo:
+	 * - Page mapping is assumed.
+	 */
+	g_table_root = rd->s2_ctx.g_rtt;
+	granule_lock(g_table_root, GRANULE_STATE_RTT);
+	rtt_walk_lock_unlock(g_table_root,
+			     realm_rtt_starting_level(rd),
+			     realm_ipa_bits(rd),
+			     ipa,
+			     RTT_PAGE_LEVEL,
+			     &wi);
+
+	ll_table = granule_map(wi.g_llt, SLOT_RTT);
+
+	/* Must be unlocked by caller */
+	s2_walk->llt = wi.g_llt;
+	s2tte = s2tte_read(&ll_table[wi.index]);
+
+	if (!s2tte_is_valid(s2tte, wi.last_level)) {
+		/*
+		 * This 'tte' is still not been made valid by the Host.
+		 * Depending on the RIPAS value, the caller needs to
+		 * emulate a Data Abort back to the Host or return error
+		 * back to Realm.
+		 */
+		s2_walk->rtt_level = wi.last_level;
+		if (s2tte_is_destroyed(s2tte)) {
+			s2_walk->destroyed = true;
+		} else {
+			s2_walk->ripas = s2tte_get_ripas(s2tte);
+		}
+		granule_unlock(wi.g_llt);
+		walk_status = WALK_FAIL;
+		goto out_unmap_table;
+	}
+
+	s2_walk->pa = s2tte_pa(s2tte, wi.last_level);
+	offset = ipa & (s2tte_map_size(wi.last_level) - 1UL);
+	s2_walk->pa += offset;
+	s2_walk->ripas = RMI_RAM;
+
+	walk_status = WALK_SUCCESS;
+
+out_unmap_table:
+	buffer_unmap(ll_table);
+	return walk_status;
+}
+
+/*
+ * Get RIPAS of IPA
+ *
+ * Parameters:
+ *	[in]  @rec:		Pointer to the rec
+ *	[in]  @ipa:		IPA for which RIPAS is queried.
+ *	[out] @ripas_ptr:	RIPAS value returned for the IPA
+ *	[out] @s2tte_destroyed: Set to true when s2tte has HIPAS=DESTROYED
+ * Returns:
+ *	None
+ */
+void realm_ipa_get_ripas(struct rec *rec, unsigned long ipa,
+			 enum ripas *ripas_ptr, bool *s2tte_destroyed)
+{
+	unsigned long s2tte, *ll_table;
+	struct rtt_walk wi;
+
+	assert(ripas_ptr != NULL);
+	assert(s2tte_destroyed != NULL);
+	assert(GRANULE_ALIGNED(ipa));
+	assert(addr_in_rec_par(rec, ipa));
+
+	granule_lock(rec->realm_info.g_rtt, GRANULE_STATE_RTT);
+
+	rtt_walk_lock_unlock(rec->realm_info.g_rtt,
+			     rec->realm_info.s2_starting_level,
+			     rec->realm_info.ipa_bits,
+			     ipa, RTT_PAGE_LEVEL, &wi);
+
+	ll_table = granule_map(wi.g_llt, SLOT_RTT);
+	s2tte = s2tte_read(&ll_table[wi.index]);
+
+	if (s2tte_is_destroyed(s2tte)) {
+		*s2tte_destroyed = true;
+	} else {
+		*s2tte_destroyed = false;
+		*ripas_ptr = s2tte_get_ripas(s2tte);
+	}
+
+	buffer_unmap(ll_table);
+	granule_unlock(wi.g_llt);
+}
diff --git a/runtime/rsi/system.c b/runtime/rsi/system.c
new file mode 100644
index 0000000..e13d54b
--- /dev/null
+++ b/runtime/rsi/system.c
@@ -0,0 +1,14 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+#include <assert.h>
+#include <smc-rsi.h>
+
+COMPILER_ASSERT(RSI_ABI_VERSION_MAJOR <= 0x7FFF);
+COMPILER_ASSERT(RSI_ABI_VERSION_MINOR <= 0xFFFF);
+
+unsigned long system_rsi_abi_version(void)
+{
+	return RSI_ABI_VERSION;
+}
diff --git a/toolchains/aarch64/common_aarch64.cmake b/toolchains/aarch64/common_aarch64.cmake
new file mode 100644
index 0000000..bafa481
--- /dev/null
+++ b/toolchains/aarch64/common_aarch64.cmake
@@ -0,0 +1,20 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+include_guard()
+
+include(${CMAKE_CURRENT_LIST_DIR}/../common.cmake)
+
+foreach(language IN ITEMS ASM C)
+	string(APPEND CMAKE_${language}_FLAGS_INIT "-ffreestanding ")
+	string(APPEND CMAKE_${language}_FLAGS_INIT "-march=armv8.5-a ")
+	string(APPEND CMAKE_${language}_FLAGS_INIT "-mgeneral-regs-only ")
+	string(APPEND CMAKE_${language}_FLAGS_INIT "-mstrict-align ")
+	string(APPEND CMAKE_${language}_FLAGS_INIT "-fpie ")
+	string(APPEND CMAKE_${language}_FLAGS_INIT "-gdwarf-4 ")
+endforeach()
+
+string(APPEND CMAKE_EXE_LINKER_FLAGS_INIT "-nostdlib ")
+string(APPEND CMAKE_EXE_LINKER_FLAGS_INIT "-Wl,-pie ")
diff --git a/toolchains/aarch64/gnu.cmake b/toolchains/aarch64/gnu.cmake
new file mode 100644
index 0000000..802a92a
--- /dev/null
+++ b/toolchains/aarch64/gnu.cmake
@@ -0,0 +1,18 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM 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/aarch64/llvm.cmake b/toolchains/aarch64/llvm.cmake
new file mode 100644
index 0000000..c63319e
--- /dev/null
+++ b/toolchains/aarch64/llvm.cmake
@@ -0,0 +1,63 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+include_guard()
+
+include(${CMAKE_CURRENT_LIST_DIR}/common_aarch64.cmake)
+
+set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
+
+find_program(CMAKE_C_COMPILER
+    NAMES "clang"
+    DOC "Path to clang."
+    REQUIRED)
+
+set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
+
+find_program(CMAKE_OBJCOPY
+    NAMES "llvm-objcopy"
+    DOC "Path to llvm-objcopy."
+    REQUIRED)
+
+find_program(CMAKE_OBJDUMP
+    NAMES "llvm-objdump"
+    DOC "Path to llvm-objcopy."
+    REQUIRED)
+
+find_program(CMAKE_AR
+    NAMES "llvm-ar"
+    DOC "Path to llvm-ar."
+    REQUIRED)
+
+find_program(CMAKE_RANLIB
+    NAMES "llvm-ranlib"
+    DOC "Path to llvm-ranlib."
+    REQUIRED)
+
+# Find the path to AArch64 gcc
+find_program(A64_GCC
+    NAMES "$ENV{CROSS_COMPILE}gcc"
+    DOC "Path to aarch64 gcc"
+    REQUIRED)
+
+# Get the AArch64 GCC triplet
+execute_process(COMMAND ${A64_GCC} -dumpmachine
+    OUTPUT_VARIABLE A64-GCC-TRIPLET
+    OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+# Construct the path to `include` folder of AArch64 GCC toolchain
+get_filename_component(A64_GCC_DIR ${A64_GCC} DIRECTORY)
+set(A64_GCC_INC_DIR "${A64_GCC_DIR}/../${A64-GCC-TRIPLET}/include")
+message(STATUS "Using ${A64_GCC_INC_DIR} for std include headers")
+
+foreach(language IN ITEMS ASM C)
+    set(CMAKE_${language}_COMPILER_TARGET "${A64-GCC-TRIPLET}")
+    string(APPEND CMAKE_${language}_STANDARD_INCLUDE_DIRECTORIES "${A64_GCC_INC_DIR}")
+    string(APPEND CMAKE_${language}_FLAGS_INIT "-Wno-unknown-warning-option ")
+    string(APPEND CMAKE_${language}_FLAGS_INIT "-Wno-unused-function ")
+endforeach()
+
+# Use lld as default linker
+string(APPEND CMAKE_EXE_LINKER_FLAGS_INIT "-fuse-ld=lld ")
diff --git a/toolchains/common.cmake b/toolchains/common.cmake
new file mode 100644
index 0000000..4d710fb
--- /dev/null
+++ b/toolchains/common.cmake
@@ -0,0 +1,24 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+include_guard()
+
+set(CMAKE_SYSTEM_NAME "Generic")
+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)
+
+foreach(language IN ITEMS ASM C)
+    string(APPEND CMAKE_${language}_FLAGS_INIT "-fno-common ")
+    string(APPEND CMAKE_${language}_FLAGS_INIT "-fomit-frame-pointer ")
+    string(APPEND CMAKE_${language}_FLAGS_INIT "-ffunction-sections ")
+    string(APPEND CMAKE_${language}_FLAGS_INIT "-fdata-sections ")
+    string(APPEND CMAKE_${language}_FLAGS_INIT "-Wall -Werror ")
+    string(APPEND CMAKE_${language}_FLAGS_DEBUG_INIT "-Og ")
+    string(APPEND CMAKE_${language}_FLAGS_RELEASE_INIT "-g ")
+endforeach()
+
+string(APPEND CMAKE_EXE_LINKER_FLAGS_INIT "-Wl,--gc-sections ")
diff --git a/toolchains/fake_host/gnu.cmake b/toolchains/fake_host/gnu.cmake
new file mode 100644
index 0000000..ca2f48f
--- /dev/null
+++ b/toolchains/fake_host/gnu.cmake
@@ -0,0 +1,17 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+include_guard()
+
+include(${CMAKE_CURRENT_LIST_DIR}/../common.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/fake_host/llvm.cmake b/toolchains/fake_host/llvm.cmake
new file mode 100644
index 0000000..cb0f207
--- /dev/null
+++ b/toolchains/fake_host/llvm.cmake
@@ -0,0 +1,23 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+include_guard()
+
+include(${CMAKE_CURRENT_LIST_DIR}/../common.cmake)
+
+find_program(CMAKE_C_COMPILER
+    NAMES "clang"
+    DOC "Path to clang."
+    REQUIRED)
+
+set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
+
+foreach(language IN ITEMS ASM C)
+    string(APPEND CMAKE_${language}_FLAGS_INIT "-Wno-unknown-warning-option ")
+    string(APPEND CMAKE_${language}_FLAGS_INIT "-Wno-unused-function ")
+endforeach()
+
+string(APPEND CMAKE_EXE_LINKER_FLAGS_INIT "-Wl,--build-id=none ")
+string(APPEND CMAKE_EXE_LINKER_FLAGS_INIT "-fuse-ld=lld ")
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
new file mode 100644
index 0000000..8021dbb
--- /dev/null
+++ b/tools/CMakeLists.txt
@@ -0,0 +1,10 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+arm_config_option(
+    NAME RMM_STATIC_ANALYSIS
+    HELP "Enable static analysis checkers.")
+
+add_subdirectory("cppcheck")
diff --git a/tools/checkincludes/CheckIncludes.cmake b/tools/checkincludes/CheckIncludes.cmake
new file mode 100644
index 0000000..786df4e
--- /dev/null
+++ b/tools/checkincludes/CheckIncludes.cmake
@@ -0,0 +1,153 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+#
+# This script is called from main CMakeLists.txt to determine if header files
+# included are in proper order
+#
+find_package(Git REQUIRED)
+find_package(Python3 REQUIRED)
+find_program(CHECKINCLUDES_EXECUTABLE "checkincludes.py"
+  PATHS ${CMAKE_SOURCE_DIR}
+  PATH_SUFFIXES tools/checkincludes
+  DOC "Path to checkincludes.py"
+  )
+
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/tools/common")
+include(GitUtils)
+
+# List of directories and files to exclude from checking for target
+list(APPEND glob_excludes "^.git")
+list(APPEND glob_excludes "^out")
+list(APPEND glob_excludes "^build")
+list(APPEND glob_excludes "^ext")
+list(APPEND glob_excludes "^tools")
+
+# checkincludes_get_stats: Parse and returns number of errors and warnings
+function(checkincludes_get_stats stats_arg errors_ret)
+  string(FIND "${stats_arg}" "total:" idx REVERSE)
+  if(NOT ${idx} EQUAL -1)
+    string(LENGTH "${stats_arg}" len)
+    string(SUBSTRING "${stats_arg}" ${idx} ${len} last_line)
+
+    string(REPLACE " " ";" last_line_list ${last_line})
+    list(GET last_line_list 1 errors)
+  else()
+    set(errors 1)
+  endif()
+
+  set(${errors_ret} ${errors} PARENT_SCOPE)
+endfunction()
+
+#
+# print_stats_and_exit: Print summary of all errors and warnings.
+# If there are errors call message(FATAL_ERROR)
+#
+function(print_stats_and_exit check_type total_errors)
+  message(STATUS "${check_type}: total errors: ${total_errors}")
+
+  if(${total_errors} GREATER 0)
+    message(FATAL_ERROR "${check_type}: FAILED")
+  endif()
+
+  message(STATUS "${check_type}: PASSED")
+endfunction()
+
+# filter all files except *.c/*.h/*.S files
+function(filter_source_files all_files source_files_ret)
+  foreach(exclude IN LISTS glob_excludes)
+    list(FILTER all_files EXCLUDE REGEX "${exclude}")
+  endforeach()
+
+  foreach(source_file ${all_files})
+    if(NOT source_file MATCHES ".c$" AND
+        NOT source_file MATCHES ".S$" AND
+        NOT source_file MATCHES ".h$")
+      list(REMOVE_ITEM all_files ${source_file})
+    endif()
+  endforeach()
+
+  set(${source_files_ret} ${all_files} PARENT_SCOPE)
+endfunction()
+
+# Run checkincludes.py on the list of files.
+function(run_checkincludes source_files errors_ret)
+  set(errors 0)
+
+  string(REPLACE ";" " " source_files "${source_files}")
+  separate_arguments(source_files NATIVE_COMMAND "${source_files}")
+
+  execute_process(
+    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+    COMMAND ${CHECKINCLUDES_EXECUTABLE} ${source_files}
+    OUTPUT_VARIABLE checkincludes_output
+    RESULT_VARIABLE checkincludes_rc
+    ECHO_OUTPUT_VARIABLE
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+    )
+
+  # checkincludes failed for this file. Collect no.of errors
+  if(${checkincludes_rc} GREATER 0)
+    checkincludes_get_stats("${checkincludes_output}" errors)
+  endif()
+
+  set(${errors_ret} ${errors} PARENT_SCOPE)
+endfunction()
+
+#
+# Run checkincludes on entire codebase. This verifies all files in this
+# repository in "GLOB_INCLUDES".
+#
+# Exits with FATAL_ERROR upon errors. Warnings are ignored (temporary)
+#
+if(CHECKINCLUDES_CODEBASE)
+  set(source_files "")
+
+  if (GIT_FOUND AND IS_DIRECTORY .git)
+    Git_Get_All_Files(all_files)
+  else()
+    file(GLOB_RECURSE all_files RELATIVE ${CMAKE_SOURCE_DIR} "*")
+  endif()
+
+  filter_source_files("${all_files}" source_files)
+
+  if(NOT source_files)
+    message(STATUS "checkincludes-codebase: No files to check")
+    return()
+  endif()
+
+  run_checkincludes("${source_files}" total_errors)
+  print_stats_and_exit("checkincludes-codebase" ${total_errors})
+endif()
+
+#
+# Check header files include order on pending commits.
+#
+# Exits with FATAL_ERROR upon errors.
+#
+if(CHECKINCLUDES_PATCH)
+  if(GIT_NOT_FOUND OR NOT IS_DIRECTORY .git)
+    message(FATAL_ERROR "Required dependencies Git not found")
+  endif()
+
+  # Get list of commits to check
+  Git_Get_Pending_Commits(pending_commits)
+
+  # Iterate throuth list of commit ids
+  set(total_errors 0)
+  foreach(commit IN LISTS pending_commits)
+    message(STATUS "Checking commit: ${commit}")
+
+    Git_Get_Files_In_Commit("${commit}" files_in_commit)
+
+    set(source_files "")
+    filter_source_files("${files_in_commit}" source_files)
+
+    run_checkincludes("${source_files}" errors)
+    MATH(EXPR total_errors "${total_errors}+${errors}")
+  endforeach()
+
+  print_stats_and_exit("checkincludes-patch" ${total_errors})
+endif()
diff --git a/tools/checkincludes/checkincludes.py b/tools/checkincludes/checkincludes.py
new file mode 100755
index 0000000..a18605a
--- /dev/null
+++ b/tools/checkincludes/checkincludes.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python3
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+# SPDX-FileCopyrightText: Copyright Arm Limited and Contributors.
+#
+
+from argparse import ArgumentParser
+import codecs
+import os
+import re
+import sys
+import logging
+from os import access, R_OK
+from os.path import isfile
+
+INCLUDE_RE = re.compile(r"^\s*#\s*include\s\s*(?P<path>[\"<].+[\">])")
+
+# exit program with rc
+def print_error_and_exit(total_errors):
+    if total_errors:
+        print("total: " + str(total_errors) + " errors")
+        sys.exit(1)
+    else:
+        sys.exit(0)
+
+def include_paths(lines):
+    """List all include paths in a file. Ignore starting `+` in diff mode."""
+    pattern = INCLUDE_RE
+    matches = (pattern.match(line) for line in lines)
+    return [m.group("path") for m in matches if m]
+
+# check if 'file' is a regular file and it is readable
+def file_readable(file):
+    if not isfile(file):
+        print(file + ": WARNING: File not found")
+        return 0
+
+    if not access(file, R_OK):
+        print(file + ": WARNING: File not readable")
+        return 0
+
+    return 1
+
+def file_include_list(path):
+    """Return a list of all include paths in a file or None on failure."""
+    try:
+        with codecs.open(path, encoding="utf-8") as f:
+            return include_paths(f)
+    except Exception:
+        logging.exception(path + ": ERROR while parsing.")
+        return ([])
+
+def check_includes(file):
+    """Checks whether the order of includes in the file specified in the path
+    is correct or not."""
+    print("Checking file: " + file)
+    if not file_readable(file):
+        return 0
+
+    inc_list = file_include_list(file)
+
+    # If there are less than 2 includes there's no need to check.
+    if len(inc_list) < 2:
+        return 0
+
+    # remove leading and trailing <, >
+    inc_list = [x[1:-1] for x in inc_list]
+
+    if sorted(inc_list) != inc_list:
+        print(file + ": ERROR: includes not in order. Include order should be " +
+              ', '.join(sorted(inc_list)))
+        return 1
+    else:
+        return 0
+
+if __name__ == "__main__":
+    ap = ArgumentParser(description='Check #include orders')
+    ap.add_argument('files', nargs='*', help='Check files.')
+    args = ap.parse_args()
+
+    total_errors = 0
+    for file in args.files:
+        total_errors += check_includes(file)
+
+    print_error_and_exit(total_errors)
diff --git a/tools/checkpatch/CheckPatch.cmake b/tools/checkpatch/CheckPatch.cmake
new file mode 100644
index 0000000..51830cb
--- /dev/null
+++ b/tools/checkpatch/CheckPatch.cmake
@@ -0,0 +1,162 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+#
+# This script is called from main CMakeLists.txt to determine if code complies
+# to coding standards as mentioned in docs/getting_started/coding-standard.rst
+#
+# Runs checkpatch.pl on entire codebase if variable CHECKCODEBASE_RUN is defined.
+#
+# Runs checkpatch.pl on new commits if variable CHECKCODEBASE_RUN is defined.
+#
+find_package(Git REQUIRED)
+find_package(Perl REQUIRED)
+find_program(CHECKPATCH_EXECUTABLE "checkpatch.pl"
+  PATHS ${CMAKE_SOURCE_DIR}
+  PATH_SUFFIXES tools/checkpatch
+  DOC "Path to checkpatch.pl"
+  )
+
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/tools/common")
+include(GitUtils)
+
+#
+# List of directories and files to exclude from checking for target checkcodebase
+#
+list(APPEND glob_excludes "^.git")
+list(APPEND glob_excludes "^out")
+list(APPEND glob_excludes "^build")
+list(APPEND glob_excludes "^ext")
+list(APPEND glob_excludes "^tools")
+list(APPEND glob_excludes ".patch$")
+list(APPEND glob_excludes ".md$")
+list(APPEND glob_excludes "~$")
+list(APPEND glob_excludes ".swp$")
+list(APPEND glob_excludes "^cscope.")
+
+set(total_errors "0")
+set(total_warnings "0")
+
+# Check if pre-reqs met
+if(PERL_NOT_FOUND OR NOT EXISTS ${CHECKPATCH_EXECUTABLE})
+  message(FATAL_ERROR "required dependencies not found")
+endif()
+
+#
+# checkpatch_get_stats: Parse and returns number of errors and warnings
+#
+function(checkpatch_get_stats stats_arg errors_ret warnings_ret)
+  string(FIND "${stats_arg}" "total:" idx REVERSE)
+  string(LENGTH "${stats_arg}" len)
+  string(SUBSTRING "${stats_arg}" ${idx} ${len} last_line)
+
+  string(REPLACE " " ";" last_line_list ${last_line})
+  list(GET last_line_list 1 errors)
+  list(GET last_line_list 3 warnings)
+
+  set(${errors_ret} ${errors} PARENT_SCOPE)
+  set(${warnings_ret} ${warnings} PARENT_SCOPE)
+endfunction()
+
+#
+# print_stats_and_exit: Print summary of all errors and warnings.
+# If there are errors call message(FATAL_ERROR)
+#
+function(print_stats_and_exit check_type total_errors total_warnings)
+  message(STATUS "${check_type}: total errors: ${total_errors} "
+    "warnings: ${total_warnings}")
+
+  if(${total_errors} GREATER 0)
+    message(FATAL_ERROR "${check_type}: FAILED")
+  endif()
+
+  message(STATUS "${check_type}: PASSED")
+endfunction()
+
+#
+# Run checkpatch on entire codebase. This verifies all files in this repository
+# except the files listed in "glob_excludes".
+#
+# Exits with FATAL_ERROR upon errors. Warnings are ignored (temporary)
+#
+if(CHECKCODEBASE_RUN)
+  set(source_files "")
+
+  if (GIT_FOUND AND IS_DIRECTORY .git)
+    Git_Get_All_Files(source_files)
+  else()
+    file(GLOB_RECURSE source_files RELATIVE ${CMAKE_SOURCE_DIR} "*")
+  endif()
+
+  # Filter out 'glob_excludes'
+  foreach(exclude IN LISTS glob_excludes)
+    list(FILTER source_files EXCLUDE REGEX "${exclude}")
+  endforeach()
+
+  if(NOT source_files)
+    message(STATUS "checkcodebase: No files to check")
+    return()
+  endif()
+
+  foreach(source_file ${source_files})
+    execute_process(
+      COMMAND ${CMAKE_COMMAND} -E echo "Checking file ${source_file}"
+      )
+
+    execute_process(
+      WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+      COMMAND ${CHECKPATCH_EXECUTABLE} -f ${source_file}
+      OUTPUT_VARIABLE checkpatch_output
+      RESULT_VARIABLE checkpatch_rc
+      ECHO_OUTPUT_VARIABLE
+      OUTPUT_STRIP_TRAILING_WHITESPACE
+      )
+
+    # checkpatch.pl failed for this file. Collect no.of errors and warnings
+    if(${checkpatch_rc})
+      checkpatch_get_stats("${checkpatch_output}" errors warnings)
+      MATH(EXPR total_errors "${total_errors}+${errors}")
+      MATH(EXPR total_warnings "${total_warnings}+${warnings}")
+    endif()
+  endforeach()
+
+  print_stats_and_exit("checkcodebase" ${total_errors}, ${total_warnings})
+endif()
+
+#
+# Run checkpatch on pending commits.
+#
+# Exits with FATAL_ERROR upon errors.
+#
+if(CHECKPATCH_RUN)
+  if(GIT_NOT_FOUND OR NOT IS_DIRECTORY .git)
+    message(FATAL_ERROR "Required dependencies Git not found")
+  endif()
+
+  # Get list of commits to check
+  Git_Get_Pending_Commits(pending_commits)
+
+  foreach(commit IN LISTS pending_commits)
+    message(STATUS "Checking commit: ${commit}")
+
+    execute_process(
+      WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+      COMMAND ${GIT_EXECUTABLE} diff --format=email "${commit}~..${commit}"
+      COMMAND ${CHECKPATCH_EXECUTABLE} -
+      OUTPUT_VARIABLE checkpatch_output
+      RESULT_VARIABLE checkpatch_rc
+      ECHO_OUTPUT_VARIABLE
+      )
+
+    # checkpatch.pl failed for this commit. Collect no.of errors and warnings
+    if(${checkpatch_rc})
+      checkpatch_get_stats("${checkpatch_output}" errors warnings)
+      MATH(EXPR total_errors "${total_errors}+${errors}")
+      MATH(EXPR total_warnings "${total_warnings}+${warnings}")
+    endif()
+  endforeach()
+
+  print_stats_and_exit("checkpatch" ${total_errors}, ${total_warnings})
+endif()
diff --git a/tools/checkpatch/checkpatch.pl b/tools/checkpatch/checkpatch.pl
new file mode 100755
index 0000000..5fb8377
--- /dev/null
+++ b/tools/checkpatch/checkpatch.pl
@@ -0,0 +1,7558 @@
+#!/usr/bin/env perl
+# SPDX-License-Identifier: GPL-2.0
+#
+# (c) 2001, Dave Jones. (the file handling bit)
+# (c) 2005, Joel Schopp <jschopp@austin.ibm.com> (the ugly bit)
+# (c) 2007,2008, Andy Whitcroft <apw@uk.ibm.com> (new conditions, test suite)
+# (c) 2008-2010 Andy Whitcroft <apw@canonical.com>
+# (c) 2010-2018 Joe Perches <joe@perches.com>
+#
+# This file is taken from Linux kernel 5.15.0-rc2 at SHA:
+# 4ce9f970457899defdf68e26e0502c7245002eb3
+#
+use strict;
+use warnings;
+use POSIX;
+use File::Basename;
+use Cwd 'abs_path';
+use Term::ANSIColor qw(:constants);
+use Encode qw(decode encode);
+
+my $P = $0;
+my $D = dirname(abs_path($P));
+
+my $V = '0.32';
+
+use Getopt::Long qw(:config no_auto_abbrev);
+
+my $quiet = 0;
+my $verbose = 0;
+my %verbose_messages = ();
+my %verbose_emitted = ();
+my $tree = 1;
+my $chk_signoff = 1;
+my $chk_patch = 1;
+my $tst_only;
+my $emacs = 0;
+my $terse = 0;
+my $showfile = 0;
+my $file = 0;
+my $git = 0;
+my %git_commits = ();
+my $check = 0;
+my $check_orig = 0;
+my $summary = 1;
+my $mailback = 0;
+my $summary_file = 0;
+my $show_types = 0;
+my $list_types = 0;
+my $fix = 0;
+my $fix_inplace = 0;
+my $root;
+my $gitroot = $ENV{'GIT_DIR'};
+$gitroot = ".git" if !defined($gitroot);
+my %debug;
+my %camelcase = ();
+my %use_type = ();
+my @use = ();
+my %ignore_type = ();
+my @ignore = ();
+my $help = 0;
+my $configuration_file = ".checkpatch.conf";
+my $max_line_length = 100;
+my $ignore_perl_version = 0;
+my $minimum_perl_version = 5.10.0;
+my $min_conf_desc_length = 4;
+my $spelling_file = "$D/spelling.txt";
+my $codespell = 0;
+my $codespellfile = "/usr/share/codespell/dictionary.txt";
+my $conststructsfile = "$D/const_structs.checkpatch";
+my $docsfile = "$D/../Documentation/dev-tools/checkpatch.rst";
+my $typedefsfile;
+my $color = "auto";
+my $allow_c99_comments = 1; # Can be overridden by --ignore C99_COMMENT_TOLERANCE
+# git output parsing needs US English output, so first set backtick child process LANGUAGE
+my $git_command ='export LANGUAGE=en_US.UTF-8; git';
+my $tabsize = 8;
+my ${CONFIG_} = "CONFIG_";
+
+sub help {
+	my ($exitcode) = @_;
+
+	print << "EOM";
+Usage: $P [OPTION]... [FILE]...
+Version: $V
+
+Options:
+  -q, --quiet                quiet
+  -v, --verbose              verbose mode
+  --no-tree                  run without a kernel tree
+  --no-signoff               do not check for 'Signed-off-by' line
+  --patch                    treat FILE as patchfile (default)
+  --emacs                    emacs compile window format
+  --terse                    one line per report
+  --showfile                 emit diffed file position, not input file position
+  -g, --git                  treat FILE as a single commit or git revision range
+                             single git commit with:
+                               <rev>
+                               <rev>^
+                               <rev>~n
+                             multiple git commits with:
+                               <rev1>..<rev2>
+                               <rev1>...<rev2>
+                               <rev>-<count>
+                             git merges are ignored
+  -f, --file                 treat FILE as regular source file
+  --subjective, --strict     enable more subjective tests
+  --list-types               list the possible message types
+  --types TYPE(,TYPE2...)    show only these comma separated message types
+  --ignore TYPE(,TYPE2...)   ignore various comma separated message types
+  --show-types               show the specific message type in the output
+  --max-line-length=n        set the maximum line length, (default $max_line_length)
+                             if exceeded, warn on patches
+                             requires --strict for use with --file
+  --min-conf-desc-length=n   set the min description length, if shorter, warn
+  --tab-size=n               set the number of spaces for tab (default $tabsize)
+  --root=PATH                PATH to the kernel tree root
+  --no-summary               suppress the per-file summary
+  --mailback                 only produce a report in case of warnings/errors
+  --summary-file             include the filename in summary
+  --debug KEY=[0|1]          turn on/off debugging of KEY, where KEY is one of
+                             'values', 'possible', 'type', and 'attr' (default
+                             is all off)
+  --test-only=WORD           report only warnings/errors containing WORD
+                             literally
+  --fix                      EXPERIMENTAL - may create horrible results
+                             If correctable single-line errors exist, create
+                             "<inputfile>.EXPERIMENTAL-checkpatch-fixes"
+                             with potential errors corrected to the preferred
+                             checkpatch style
+  --fix-inplace              EXPERIMENTAL - may create horrible results
+                             Is the same as --fix, but overwrites the input
+                             file.  It's your fault if there's no backup or git
+  --ignore-perl-version      override checking of perl version.  expect
+                             runtime errors.
+  --codespell                Use the codespell dictionary for spelling/typos
+                             (default:/usr/share/codespell/dictionary.txt)
+  --codespellfile            Use this codespell dictionary
+  --typedefsfile             Read additional types from this file
+  --color[=WHEN]             Use colors 'always', 'never', or only when output
+                             is a terminal ('auto'). Default is 'auto'.
+  --kconfig-prefix=WORD      use WORD as a prefix for Kconfig symbols (default
+                             ${CONFIG_})
+  -h, --help, --version      display this help and exit
+
+When FILE is - read standard input.
+EOM
+
+	exit($exitcode);
+}
+
+sub uniq {
+	my %seen;
+	return grep { !$seen{$_}++ } @_;
+}
+
+sub list_types {
+	my ($exitcode) = @_;
+
+	my $count = 0;
+
+	local $/ = undef;
+
+	open(my $script, '<', abs_path($P)) or
+	    die "$P: Can't read '$P' $!\n";
+
+	my $text = <$script>;
+	close($script);
+
+	my %types = ();
+	# Also catch when type or level is passed through a variable
+	while ($text =~ /(?:(\bCHK|\bWARN|\bERROR|&\{\$msg_level})\s*\(|\$msg_type\s*=)\s*"([^"]+)"/g) {
+		if (defined($1)) {
+			if (exists($types{$2})) {
+				$types{$2} .= ",$1" if ($types{$2} ne $1);
+			} else {
+				$types{$2} = $1;
+			}
+		} else {
+			$types{$2} = "UNDETERMINED";
+		}
+	}
+
+	print("#\tMessage type\n\n");
+	if ($color) {
+		print(" ( Color coding: ");
+		print(RED . "ERROR" . RESET);
+		print(" | ");
+		print(YELLOW . "WARNING" . RESET);
+		print(" | ");
+		print(GREEN . "CHECK" . RESET);
+		print(" | ");
+		print("Multiple levels / Undetermined");
+		print(" )\n\n");
+	}
+
+	foreach my $type (sort keys %types) {
+		my $orig_type = $type;
+		if ($color) {
+			my $level = $types{$type};
+			if ($level eq "ERROR") {
+				$type = RED . $type . RESET;
+			} elsif ($level eq "WARN") {
+				$type = YELLOW . $type . RESET;
+			} elsif ($level eq "CHK") {
+				$type = GREEN . $type . RESET;
+			}
+		}
+		print(++$count . "\t" . $type . "\n");
+		if ($verbose && exists($verbose_messages{$orig_type})) {
+			my $message = $verbose_messages{$orig_type};
+			$message =~ s/\n/\n\t/g;
+			print("\t" . $message . "\n\n");
+		}
+	}
+
+	exit($exitcode);
+}
+
+my $conf = which_conf($configuration_file);
+if (-f $conf) {
+	my @conf_args;
+	open(my $conffile, '<', "$conf")
+	    or warn "$P: Can't find a readable $configuration_file file $!\n";
+
+	while (<$conffile>) {
+		my $line = $_;
+
+		$line =~ s/\s*\n?$//g;
+		$line =~ s/^\s*//g;
+		$line =~ s/\s+/ /g;
+
+		next if ($line =~ m/^\s*#/);
+		next if ($line =~ m/^\s*$/);
+
+		my @words = split(" ", $line);
+		foreach my $word (@words) {
+			last if ($word =~ m/^#/);
+			push (@conf_args, $word);
+		}
+	}
+	close($conffile);
+	unshift(@ARGV, @conf_args) if @conf_args;
+}
+
+sub load_docs {
+	open(my $docs, '<', "$docsfile")
+	    or warn "$P: Can't read the documentation file $docsfile $!\n";
+
+	my $type = '';
+	my $desc = '';
+	my $in_desc = 0;
+
+	while (<$docs>) {
+		chomp;
+		my $line = $_;
+		$line =~ s/\s+$//;
+
+		if ($line =~ /^\s*\*\*(.+)\*\*$/) {
+			if ($desc ne '') {
+				$verbose_messages{$type} = trim($desc);
+			}
+			$type = $1;
+			$desc = '';
+			$in_desc = 1;
+		} elsif ($in_desc) {
+			if ($line =~ /^(?:\s{4,}|$)/) {
+				$line =~ s/^\s{4}//;
+				$desc .= $line;
+				$desc .= "\n";
+			} else {
+				$verbose_messages{$type} = trim($desc);
+				$type = '';
+				$desc = '';
+				$in_desc = 0;
+			}
+		}
+	}
+
+	if ($desc ne '') {
+		$verbose_messages{$type} = trim($desc);
+	}
+	close($docs);
+}
+
+# Perl's Getopt::Long allows options to take optional arguments after a space.
+# Prevent --color by itself from consuming other arguments
+foreach (@ARGV) {
+	if ($_ eq "--color" || $_ eq "-color") {
+		$_ = "--color=$color";
+	}
+}
+
+GetOptions(
+	'q|quiet+'	=> \$quiet,
+	'v|verbose!'	=> \$verbose,
+	'tree!'		=> \$tree,
+	'signoff!'	=> \$chk_signoff,
+	'patch!'	=> \$chk_patch,
+	'emacs!'	=> \$emacs,
+	'terse!'	=> \$terse,
+	'showfile!'	=> \$showfile,
+	'f|file!'	=> \$file,
+	'g|git!'	=> \$git,
+	'subjective!'	=> \$check,
+	'strict!'	=> \$check,
+	'ignore=s'	=> \@ignore,
+	'types=s'	=> \@use,
+	'show-types!'	=> \$show_types,
+	'list-types!'	=> \$list_types,
+	'max-line-length=i' => \$max_line_length,
+	'min-conf-desc-length=i' => \$min_conf_desc_length,
+	'tab-size=i'	=> \$tabsize,
+	'root=s'	=> \$root,
+	'summary!'	=> \$summary,
+	'mailback!'	=> \$mailback,
+	'summary-file!'	=> \$summary_file,
+	'fix!'		=> \$fix,
+	'fix-inplace!'	=> \$fix_inplace,
+	'ignore-perl-version!' => \$ignore_perl_version,
+	'debug=s'	=> \%debug,
+	'test-only=s'	=> \$tst_only,
+	'codespell!'	=> \$codespell,
+	'codespellfile=s'	=> \$codespellfile,
+	'typedefsfile=s'	=> \$typedefsfile,
+	'color=s'	=> \$color,
+	'no-color'	=> \$color,	#keep old behaviors of -nocolor
+	'nocolor'	=> \$color,	#keep old behaviors of -nocolor
+	'kconfig-prefix=s'	=> \${CONFIG_},
+	'h|help'	=> \$help,
+	'version'	=> \$help
+) or help(1);
+
+help(0) if ($help);
+
+die "$P: --git cannot be used with --file or --fix\n" if ($git && ($file || $fix));
+die "$P: --verbose cannot be used with --terse\n" if ($verbose && $terse);
+
+if ($color =~ /^[01]$/) {
+	$color = !$color;
+} elsif ($color =~ /^always$/i) {
+	$color = 1;
+} elsif ($color =~ /^never$/i) {
+	$color = 0;
+} elsif ($color =~ /^auto$/i) {
+	$color = (-t STDOUT);
+} else {
+	die "$P: Invalid color mode: $color\n";
+}
+
+load_docs() if ($verbose);
+list_types(0) if ($list_types);
+
+$fix = 1 if ($fix_inplace);
+$check_orig = $check;
+
+my $exit = 0;
+
+my $perl_version_ok = 1;
+if ($^V && $^V lt $minimum_perl_version) {
+	$perl_version_ok = 0;
+	printf "$P: requires at least perl version %vd\n", $minimum_perl_version;
+	exit(1) if (!$ignore_perl_version);
+}
+
+#if no filenames are given, push '-' to read patch from stdin
+if ($#ARGV < 0) {
+	push(@ARGV, '-');
+}
+
+# skip TAB size 1 to avoid additional checks on $tabsize - 1
+die "$P: Invalid TAB size: $tabsize\n" if ($tabsize < 2);
+
+sub hash_save_array_words {
+	my ($hashRef, $arrayRef) = @_;
+
+	my @array = split(/,/, join(',', @$arrayRef));
+	foreach my $word (@array) {
+		$word =~ s/\s*\n?$//g;
+		$word =~ s/^\s*//g;
+		$word =~ s/\s+/ /g;
+		$word =~ tr/[a-z]/[A-Z]/;
+
+		next if ($word =~ m/^\s*#/);
+		next if ($word =~ m/^\s*$/);
+
+		$hashRef->{$word}++;
+	}
+}
+
+sub hash_show_words {
+	my ($hashRef, $prefix) = @_;
+
+	if (keys %$hashRef) {
+		print "\nNOTE: $prefix message types:";
+		foreach my $word (sort keys %$hashRef) {
+			print " $word";
+		}
+		print "\n";
+	}
+}
+
+hash_save_array_words(\%ignore_type, \@ignore);
+hash_save_array_words(\%use_type, \@use);
+
+my $dbg_values = 0;
+my $dbg_possible = 0;
+my $dbg_type = 0;
+my $dbg_attr = 0;
+for my $key (keys %debug) {
+	## no critic
+	eval "\${dbg_$key} = '$debug{$key}';";
+	die "$@" if ($@);
+}
+
+my $rpt_cleaners = 0;
+
+if ($terse) {
+	$emacs = 1;
+	$quiet++;
+}
+
+if ($tree) {
+	if (defined $root) {
+		if (!top_of_kernel_tree($root)) {
+			die "$P: $root: --root does not point at a valid tree\n";
+		}
+	} else {
+		if (top_of_kernel_tree('.')) {
+			$root = '.';
+		} elsif ($0 =~ m@(.*)/scripts/[^/]*$@ &&
+						top_of_kernel_tree($1)) {
+			$root = $1;
+		}
+	}
+
+	if (!defined $root) {
+		print "Must be run from the top-level dir. of a kernel tree\n";
+		exit(2);
+	}
+}
+
+my $emitted_corrupt = 0;
+
+our $Ident	= qr{
+			[A-Za-z_][A-Za-z\d_]*
+			(?:\s*\#\#\s*[A-Za-z_][A-Za-z\d_]*)*
+		}x;
+our $Storage	= qr{extern|static|asmlinkage};
+our $Sparse	= qr{
+			__user|
+			__kernel|
+			__force|
+			__iomem|
+			__must_check|
+			__kprobes|
+			__ref|
+			__refconst|
+			__refdata|
+			__rcu|
+			__private
+		}x;
+our $InitAttributePrefix = qr{__(?:mem|cpu|dev|net_|)};
+our $InitAttributeData = qr{$InitAttributePrefix(?:initdata\b)};
+our $InitAttributeConst = qr{$InitAttributePrefix(?:initconst\b)};
+our $InitAttributeInit = qr{$InitAttributePrefix(?:init\b)};
+our $InitAttribute = qr{$InitAttributeData|$InitAttributeConst|$InitAttributeInit};
+
+# Notes to $Attribute:
+# We need \b after 'init' otherwise 'initconst' will cause a false positive in a check
+our $Attribute	= qr{
+			const|
+			volatile|
+			__percpu|
+			__nocast|
+			__safe|
+			__bitwise|
+			__packed__|
+			__packed2__|
+			__naked|
+			__maybe_unused|
+			__always_unused|
+			__noreturn|
+			__used|
+			__cold|
+			__pure|
+			__noclone|
+			__deprecated|
+			__read_mostly|
+			__ro_after_init|
+			__kprobes|
+			$InitAttribute|
+			____cacheline_aligned|
+			____cacheline_aligned_in_smp|
+			____cacheline_internodealigned_in_smp|
+			__weak
+		  }x;
+our $Modifier;
+our $Inline	= qr{inline|__always_inline|noinline|__inline|__inline__};
+our $Member	= qr{->$Ident|\.$Ident|\[[^]]*\]};
+our $Lval	= qr{$Ident(?:$Member)*};
+
+our $Int_type	= qr{(?i)llu|ull|ll|lu|ul|l|u};
+our $Binary	= qr{(?i)0b[01]+$Int_type?};
+our $Hex	= qr{(?i)0x[0-9a-f]+$Int_type?};
+our $Int	= qr{[0-9]+$Int_type?};
+our $Octal	= qr{0[0-7]+$Int_type?};
+our $String	= qr{(?:\b[Lu])?"[X\t]*"};
+our $Float_hex	= qr{(?i)0x[0-9a-f]+p-?[0-9]+[fl]?};
+our $Float_dec	= qr{(?i)(?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:e-?[0-9]+)?[fl]?};
+our $Float_int	= qr{(?i)[0-9]+e-?[0-9]+[fl]?};
+our $Float	= qr{$Float_hex|$Float_dec|$Float_int};
+our $Constant	= qr{$Float|$Binary|$Octal|$Hex|$Int};
+our $Assignment	= qr{\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=};
+our $Compare    = qr{<=|>=|==|!=|<|(?<!-)>};
+our $Arithmetic = qr{\+|-|\*|\/|%};
+our $Operators	= qr{
+			<=|>=|==|!=|
+			=>|->|<<|>>|<|>|!|~|
+			&&|\|\||,|\^|\+\+|--|&|\||$Arithmetic
+		  }x;
+
+our $c90_Keywords = qr{do|for|while|if|else|return|goto|continue|switch|default|case|break}x;
+
+our $BasicType;
+our $NonptrType;
+our $NonptrTypeMisordered;
+our $NonptrTypeWithAttr;
+our $Type;
+our $TypeMisordered;
+our $Declare;
+our $DeclareMisordered;
+
+our $NON_ASCII_UTF8	= qr{
+	[\xC2-\xDF][\x80-\xBF]               # non-overlong 2-byte
+	|  \xE0[\xA0-\xBF][\x80-\xBF]        # excluding overlongs
+	| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}  # straight 3-byte
+	|  \xED[\x80-\x9F][\x80-\xBF]        # excluding surrogates
+	|  \xF0[\x90-\xBF][\x80-\xBF]{2}     # planes 1-3
+	| [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
+	|  \xF4[\x80-\x8F][\x80-\xBF]{2}     # plane 16
+}x;
+
+our $UTF8	= qr{
+	[\x09\x0A\x0D\x20-\x7E]              # ASCII
+	| $NON_ASCII_UTF8
+}x;
+
+our $typeC99Typedefs = qr{(?:__)?(?:[us]_?)?int_?(?:8|16|32|64)_t};
+our $typeOtherOSTypedefs = qr{(?x:
+	u_(?:char|short|int|long) |          # bsd
+	u(?:nchar|short|int|long)            # sysv
+)};
+our $typeKernelTypedefs = qr{(?x:
+	(?:__)?(?:u|s|be|le)(?:8|16|32|64)|
+	atomic_t
+)};
+our $typeTypedefs = qr{(?x:
+	$typeC99Typedefs\b|
+	$typeOtherOSTypedefs\b|
+	$typeKernelTypedefs\b
+)};
+
+our $zero_initializer = qr{(?:(?:0[xX])?0+$Int_type?|NULL|false)\b};
+
+our $logFunctions = qr{(?x:
+	printk(?:_ratelimited|_once|_deferred_once|_deferred|)|
+	(?:[a-z0-9]+_){1,2}(?:printk|emerg|alert|crit|err|warning|warn|notice|info|debug|dbg|vdbg|devel|cont|WARN)(?:_ratelimited|_once|)|
+	TP_printk|
+	WARN(?:_RATELIMIT|_ONCE|)|
+	panic|
+	MODULE_[A-Z_]+|
+	seq_vprintf|seq_printf|seq_puts
+)};
+
+our $allocFunctions = qr{(?x:
+	(?:(?:devm_)?
+		(?:kv|k|v)[czm]alloc(?:_array)?(?:_node)? |
+		kstrdup(?:_const)? |
+		kmemdup(?:_nul)?) |
+	(?:\w+)?alloc_skb(?:_ip_align)? |
+				# dev_alloc_skb/netdev_alloc_skb, et al
+	dma_alloc_coherent
+)};
+
+our $signature_tags = qr{(?xi:
+	Signed-off-by:|
+	Co-developed-by:|
+	Acked-by:|
+	Tested-by:|
+	Reviewed-by:|
+	Reported-by:|
+	Suggested-by:|
+	To:|
+	Cc:
+)};
+
+our $tracing_logging_tags = qr{(?xi:
+	[=-]*> |
+	<[=-]* |
+	\[ |
+	\] |
+	start |
+	called |
+	entered |
+	entry |
+	enter |
+	in |
+	inside |
+	here |
+	begin |
+	exit |
+	end |
+	done |
+	leave |
+	completed |
+	out |
+	return |
+	[\.\!:\s]*
+)};
+
+sub edit_distance_min {
+	my (@arr) = @_;
+	my $len = scalar @arr;
+	if ((scalar @arr) < 1) {
+		# if underflow, return
+		return;
+	}
+	my $min = $arr[0];
+	for my $i (0 .. ($len-1)) {
+		if ($arr[$i] < $min) {
+			$min = $arr[$i];
+		}
+	}
+	return $min;
+}
+
+sub get_edit_distance {
+	my ($str1, $str2) = @_;
+	$str1 = lc($str1);
+	$str2 = lc($str2);
+	$str1 =~ s/-//g;
+	$str2 =~ s/-//g;
+	my $len1 = length($str1);
+	my $len2 = length($str2);
+	# two dimensional array storing minimum edit distance
+	my @distance;
+	for my $i (0 .. $len1) {
+		for my $j (0 .. $len2) {
+			if ($i == 0) {
+				$distance[$i][$j] = $j;
+			} elsif ($j == 0) {
+				$distance[$i][$j] = $i;
+			} elsif (substr($str1, $i-1, 1) eq substr($str2, $j-1, 1)) {
+				$distance[$i][$j] = $distance[$i - 1][$j - 1];
+			} else {
+				my $dist1 = $distance[$i][$j - 1]; #insert distance
+				my $dist2 = $distance[$i - 1][$j]; # remove
+				my $dist3 = $distance[$i - 1][$j - 1]; #replace
+				$distance[$i][$j] = 1 + edit_distance_min($dist1, $dist2, $dist3);
+			}
+		}
+	}
+	return $distance[$len1][$len2];
+}
+
+sub find_standard_signature {
+	my ($sign_off) = @_;
+	my @standard_signature_tags = (
+		'Signed-off-by:', 'Co-developed-by:', 'Acked-by:', 'Tested-by:',
+		'Reviewed-by:', 'Reported-by:', 'Suggested-by:'
+	);
+	foreach my $signature (@standard_signature_tags) {
+		return $signature if (get_edit_distance($sign_off, $signature) <= 2);
+	}
+
+	return "";
+}
+
+our @typeListMisordered = (
+	qr{char\s+(?:un)?signed},
+	qr{int\s+(?:(?:un)?signed\s+)?short\s},
+	qr{int\s+short(?:\s+(?:un)?signed)},
+	qr{short\s+int(?:\s+(?:un)?signed)},
+	qr{(?:un)?signed\s+int\s+short},
+	qr{short\s+(?:un)?signed},
+	qr{long\s+int\s+(?:un)?signed},
+	qr{int\s+long\s+(?:un)?signed},
+	qr{long\s+(?:un)?signed\s+int},
+	qr{int\s+(?:un)?signed\s+long},
+	qr{int\s+(?:un)?signed},
+	qr{int\s+long\s+long\s+(?:un)?signed},
+	qr{long\s+long\s+int\s+(?:un)?signed},
+	qr{long\s+long\s+(?:un)?signed\s+int},
+	qr{long\s+long\s+(?:un)?signed},
+	qr{long\s+(?:un)?signed},
+);
+
+our @typeList = (
+	qr{void},
+	qr{(?:(?:un)?signed\s+)?char},
+	qr{(?:(?:un)?signed\s+)?short\s+int},
+	qr{(?:(?:un)?signed\s+)?short},
+	qr{(?:(?:un)?signed\s+)?int},
+	qr{(?:(?:un)?signed\s+)?long\s+int},
+	qr{(?:(?:un)?signed\s+)?long\s+long\s+int},
+	qr{(?:(?:un)?signed\s+)?long\s+long},
+	qr{(?:(?:un)?signed\s+)?long},
+	qr{(?:un)?signed},
+	qr{float},
+	qr{double},
+	qr{bool},
+	qr{struct\s+$Ident},
+	qr{union\s+$Ident},
+	qr{enum\s+$Ident},
+	qr{${Ident}_t},
+	qr{${Ident}_handler},
+	qr{${Ident}_handler_fn},
+	@typeListMisordered,
+);
+
+our $C90_int_types = qr{(?x:
+	long\s+long\s+int\s+(?:un)?signed|
+	long\s+long\s+(?:un)?signed\s+int|
+	long\s+long\s+(?:un)?signed|
+	(?:(?:un)?signed\s+)?long\s+long\s+int|
+	(?:(?:un)?signed\s+)?long\s+long|
+	int\s+long\s+long\s+(?:un)?signed|
+	int\s+(?:(?:un)?signed\s+)?long\s+long|
+
+	long\s+int\s+(?:un)?signed|
+	long\s+(?:un)?signed\s+int|
+	long\s+(?:un)?signed|
+	(?:(?:un)?signed\s+)?long\s+int|
+	(?:(?:un)?signed\s+)?long|
+	int\s+long\s+(?:un)?signed|
+	int\s+(?:(?:un)?signed\s+)?long|
+
+	int\s+(?:un)?signed|
+	(?:(?:un)?signed\s+)?int
+)};
+
+our @typeListFile = ();
+our @typeListWithAttr = (
+	@typeList,
+	qr{struct\s+$InitAttribute\s+$Ident},
+	qr{union\s+$InitAttribute\s+$Ident},
+);
+
+our @modifierList = (
+	qr{fastcall},
+);
+our @modifierListFile = ();
+
+our @mode_permission_funcs = (
+	["module_param", 3],
+	["module_param_(?:array|named|string)", 4],
+	["module_param_array_named", 5],
+	["debugfs_create_(?:file|u8|u16|u32|u64|x8|x16|x32|x64|size_t|atomic_t|bool|blob|regset32|u32_array)", 2],
+	["proc_create(?:_data|)", 2],
+	["(?:CLASS|DEVICE|SENSOR|SENSOR_DEVICE|IIO_DEVICE)_ATTR", 2],
+	["IIO_DEV_ATTR_[A-Z_]+", 1],
+	["SENSOR_(?:DEVICE_|)ATTR_2", 2],
+	["SENSOR_TEMPLATE(?:_2|)", 3],
+	["__ATTR", 2],
+);
+
+my $word_pattern = '\b[A-Z]?[a-z]{2,}\b';
+
+#Create a search pattern for all these functions to speed up a loop below
+our $mode_perms_search = "";
+foreach my $entry (@mode_permission_funcs) {
+	$mode_perms_search .= '|' if ($mode_perms_search ne "");
+	$mode_perms_search .= $entry->[0];
+}
+$mode_perms_search = "(?:${mode_perms_search})";
+
+our %deprecated_apis = (
+	"synchronize_rcu_bh"			=> "synchronize_rcu",
+	"synchronize_rcu_bh_expedited"		=> "synchronize_rcu_expedited",
+	"call_rcu_bh"				=> "call_rcu",
+	"rcu_barrier_bh"			=> "rcu_barrier",
+	"synchronize_sched"			=> "synchronize_rcu",
+	"synchronize_sched_expedited"		=> "synchronize_rcu_expedited",
+	"call_rcu_sched"			=> "call_rcu",
+	"rcu_barrier_sched"			=> "rcu_barrier",
+	"get_state_synchronize_sched"		=> "get_state_synchronize_rcu",
+	"cond_synchronize_sched"		=> "cond_synchronize_rcu",
+);
+
+#Create a search pattern for all these strings to speed up a loop below
+our $deprecated_apis_search = "";
+foreach my $entry (keys %deprecated_apis) {
+	$deprecated_apis_search .= '|' if ($deprecated_apis_search ne "");
+	$deprecated_apis_search .= $entry;
+}
+$deprecated_apis_search = "(?:${deprecated_apis_search})";
+
+our $mode_perms_world_writable = qr{
+	S_IWUGO		|
+	S_IWOTH		|
+	S_IRWXUGO	|
+	S_IALLUGO	|
+	0[0-7][0-7][2367]
+}x;
+
+our %mode_permission_string_types = (
+	"S_IRWXU" => 0700,
+	"S_IRUSR" => 0400,
+	"S_IWUSR" => 0200,
+	"S_IXUSR" => 0100,
+	"S_IRWXG" => 0070,
+	"S_IRGRP" => 0040,
+	"S_IWGRP" => 0020,
+	"S_IXGRP" => 0010,
+	"S_IRWXO" => 0007,
+	"S_IROTH" => 0004,
+	"S_IWOTH" => 0002,
+	"S_IXOTH" => 0001,
+	"S_IRWXUGO" => 0777,
+	"S_IRUGO" => 0444,
+	"S_IWUGO" => 0222,
+	"S_IXUGO" => 0111,
+);
+
+#Create a search pattern for all these strings to speed up a loop below
+our $mode_perms_string_search = "";
+foreach my $entry (keys %mode_permission_string_types) {
+	$mode_perms_string_search .= '|' if ($mode_perms_string_search ne "");
+	$mode_perms_string_search .= $entry;
+}
+our $single_mode_perms_string_search = "(?:${mode_perms_string_search})";
+our $multi_mode_perms_string_search = qr{
+	${single_mode_perms_string_search}
+	(?:\s*\|\s*${single_mode_perms_string_search})*
+}x;
+
+sub perms_to_octal {
+	my ($string) = @_;
+
+	return trim($string) if ($string =~ /^\s*0[0-7]{3,3}\s*$/);
+
+	my $val = "";
+	my $oval = "";
+	my $to = 0;
+	my $curpos = 0;
+	my $lastpos = 0;
+	while ($string =~ /\b(($single_mode_perms_string_search)\b(?:\s*\|\s*)?\s*)/g) {
+		$curpos = pos($string);
+		my $match = $2;
+		my $omatch = $1;
+		last if ($lastpos > 0 && ($curpos - length($omatch) != $lastpos));
+		$lastpos = $curpos;
+		$to |= $mode_permission_string_types{$match};
+		$val .= '\s*\|\s*' if ($val ne "");
+		$val .= $match;
+		$oval .= $omatch;
+	}
+	$oval =~ s/^\s*\|\s*//;
+	$oval =~ s/\s*\|\s*$//;
+	return sprintf("%04o", $to);
+}
+
+our $allowed_asm_includes = qr{(?x:
+	irq|
+	memory|
+	time|
+	reboot
+)};
+# memory.h: ARM has a custom one
+
+# Load common spelling mistakes and build regular expression list.
+my $misspellings;
+my %spelling_fix;
+
+if (open(my $spelling, '<', $spelling_file)) {
+	while (<$spelling>) {
+		my $line = $_;
+
+		$line =~ s/\s*\n?$//g;
+		$line =~ s/^\s*//g;
+
+		next if ($line =~ m/^\s*#/);
+		next if ($line =~ m/^\s*$/);
+
+		my ($suspect, $fix) = split(/\|\|/, $line);
+
+		$spelling_fix{$suspect} = $fix;
+	}
+	close($spelling);
+} else {
+	warn "No typos will be found - file '$spelling_file': $!\n";
+}
+
+if ($codespell) {
+	if (open(my $spelling, '<', $codespellfile)) {
+		while (<$spelling>) {
+			my $line = $_;
+
+			$line =~ s/\s*\n?$//g;
+			$line =~ s/^\s*//g;
+
+			next if ($line =~ m/^\s*#/);
+			next if ($line =~ m/^\s*$/);
+			next if ($line =~ m/, disabled/i);
+
+			$line =~ s/,.*$//;
+
+			my ($suspect, $fix) = split(/->/, $line);
+
+			$spelling_fix{$suspect} = $fix;
+		}
+		close($spelling);
+	} else {
+		warn "No codespell typos will be found - file '$codespellfile': $!\n";
+	}
+}
+
+$misspellings = join("|", sort keys %spelling_fix) if keys %spelling_fix;
+
+sub read_words {
+	my ($wordsRef, $file) = @_;
+
+	if (open(my $words, '<', $file)) {
+		while (<$words>) {
+			my $line = $_;
+
+			$line =~ s/\s*\n?$//g;
+			$line =~ s/^\s*//g;
+
+			next if ($line =~ m/^\s*#/);
+			next if ($line =~ m/^\s*$/);
+			if ($line =~ /\s/) {
+				print("$file: '$line' invalid - ignored\n");
+				next;
+			}
+
+			$$wordsRef .= '|' if (defined $$wordsRef);
+			$$wordsRef .= $line;
+		}
+		close($file);
+		return 1;
+	}
+
+	return 0;
+}
+
+my $const_structs;
+if (show_type("CONST_STRUCT")) {
+	read_words(\$const_structs, $conststructsfile)
+	    or warn "No structs that should be const will be found - file '$conststructsfile': $!\n";
+}
+
+if (defined($typedefsfile)) {
+	my $typeOtherTypedefs;
+	read_words(\$typeOtherTypedefs, $typedefsfile)
+	    or warn "No additional types will be considered - file '$typedefsfile': $!\n";
+	$typeTypedefs .= '|' . $typeOtherTypedefs if (defined $typeOtherTypedefs);
+}
+
+sub build_types {
+	my $mods = "(?x:  \n" . join("|\n  ", (@modifierList, @modifierListFile)) . "\n)";
+	my $all = "(?x:  \n" . join("|\n  ", (@typeList, @typeListFile)) . "\n)";
+	my $Misordered = "(?x:  \n" . join("|\n  ", @typeListMisordered) . "\n)";
+	my $allWithAttr = "(?x:  \n" . join("|\n  ", @typeListWithAttr) . "\n)";
+	$Modifier	= qr{(?:$Attribute|$Sparse|$mods)};
+	$BasicType	= qr{
+				(?:$typeTypedefs\b)|
+				(?:${all}\b)
+		}x;
+	$NonptrType	= qr{
+			(?:$Modifier\s+|const\s+)*
+			(?:
+				(?:typeof|__typeof__)\s*\([^\)]*\)|
+				(?:$typeTypedefs\b)|
+				(?:${all}\b)
+			)
+			(?:\s+$Modifier|\s+const)*
+		  }x;
+	$NonptrTypeMisordered	= qr{
+			(?:$Modifier\s+|const\s+)*
+			(?:
+				(?:${Misordered}\b)
+			)
+			(?:\s+$Modifier|\s+const)*
+		  }x;
+	$NonptrTypeWithAttr	= qr{
+			(?:$Modifier\s+|const\s+)*
+			(?:
+				(?:typeof|__typeof__)\s*\([^\)]*\)|
+				(?:$typeTypedefs\b)|
+				(?:${allWithAttr}\b)
+			)
+			(?:\s+$Modifier|\s+const)*
+		  }x;
+	$Type	= qr{
+			$NonptrType
+			(?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+){0,4}
+			(?:\s+$Inline|\s+$Modifier)*
+		  }x;
+	$TypeMisordered	= qr{
+			$NonptrTypeMisordered
+			(?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+){0,4}
+			(?:\s+$Inline|\s+$Modifier)*
+		  }x;
+	$Declare	= qr{(?:$Storage\s+(?:$Inline\s+)?)?$Type};
+	$DeclareMisordered	= qr{(?:$Storage\s+(?:$Inline\s+)?)?$TypeMisordered};
+}
+build_types();
+
+our $Typecast	= qr{\s*(\(\s*$NonptrType\s*\)){0,1}\s*};
+
+# Using $balanced_parens, $LvalOrFunc, or $FuncArg
+# requires at least perl version v5.10.0
+# Any use must be runtime checked with $^V
+
+our $balanced_parens = qr/(\((?:[^\(\)]++|(?-1))*\))/;
+our $LvalOrFunc	= qr{((?:[\&\*]\s*)?$Lval)\s*($balanced_parens{0,1})\s*};
+our $FuncArg = qr{$Typecast{0,1}($LvalOrFunc|$Constant|$String)};
+
+our $declaration_macros = qr{(?x:
+	(?:$Storage\s+)?(?:[A-Z_][A-Z0-9]*_){0,2}(?:DEFINE|DECLARE)(?:_[A-Z0-9]+){1,6}\s*\(|
+	(?:$Storage\s+)?[HLP]?LIST_HEAD\s*\(|
+	(?:SKCIPHER_REQUEST|SHASH_DESC|AHASH_REQUEST)_ON_STACK\s*\(
+)};
+
+our %allow_repeated_words = (
+	add => '',
+	added => '',
+	bad => '',
+	be => '',
+);
+
+sub deparenthesize {
+	my ($string) = @_;
+	return "" if (!defined($string));
+
+	while ($string =~ /^\s*\(.*\)\s*$/) {
+		$string =~ s@^\s*\(\s*@@;
+		$string =~ s@\s*\)\s*$@@;
+	}
+
+	$string =~ s@\s+@ @g;
+
+	return $string;
+}
+
+sub seed_camelcase_file {
+	my ($file) = @_;
+
+	return if (!(-f $file));
+
+	local $/;
+
+	open(my $include_file, '<', "$file")
+	    or warn "$P: Can't read '$file' $!\n";
+	my $text = <$include_file>;
+	close($include_file);
+
+	my @lines = split('\n', $text);
+
+	foreach my $line (@lines) {
+		next if ($line !~ /(?:[A-Z][a-z]|[a-z][A-Z])/);
+		if ($line =~ /^[ \t]*(?:#[ \t]*define|typedef\s+$Type)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)/) {
+			$camelcase{$1} = 1;
+		} elsif ($line =~ /^\s*$Declare\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[\(\[,;]/) {
+			$camelcase{$1} = 1;
+		} elsif ($line =~ /^\s*(?:union|struct|enum)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[;\{]/) {
+			$camelcase{$1} = 1;
+		}
+	}
+}
+
+our %maintained_status = ();
+
+sub is_maintained_obsolete {
+	my ($filename) = @_;
+
+	return 0 if (!$tree || !(-e "$root/scripts/get_maintainer.pl"));
+
+	if (!exists($maintained_status{$filename})) {
+		$maintained_status{$filename} = `perl $root/scripts/get_maintainer.pl --status --nom --nol --nogit --nogit-fallback -f $filename 2>&1`;
+	}
+
+	return $maintained_status{$filename} =~ /obsolete/i;
+}
+
+sub is_SPDX_License_valid {
+	my ($license) = @_;
+
+	return 1 if (!$tree || which("python3") eq "" || !(-x "$root/scripts/spdxcheck.py") || !(-e "$gitroot"));
+
+	my $root_path = abs_path($root);
+	my $status = `cd "$root_path"; echo "$license" | scripts/spdxcheck.py -`;
+	return 0 if ($status ne "");
+	return 1;
+}
+
+my $camelcase_seeded = 0;
+sub seed_camelcase_includes {
+	return if ($camelcase_seeded);
+
+	my $files;
+	my $camelcase_cache = "";
+	my @include_files = ();
+
+	$camelcase_seeded = 1;
+
+	if (-e "$gitroot") {
+		my $git_last_include_commit = `${git_command} log --no-merges --pretty=format:"%h%n" -1 -- include`;
+		chomp $git_last_include_commit;
+		$camelcase_cache = ".checkpatch-camelcase.git.$git_last_include_commit";
+	} else {
+		my $last_mod_date = 0;
+		$files = `find $root/include -name "*.h"`;
+		@include_files = split('\n', $files);
+		foreach my $file (@include_files) {
+			my $date = POSIX::strftime("%Y%m%d%H%M",
+						   localtime((stat $file)[9]));
+			$last_mod_date = $date if ($last_mod_date < $date);
+		}
+		$camelcase_cache = ".checkpatch-camelcase.date.$last_mod_date";
+	}
+
+	if ($camelcase_cache ne "" && -f $camelcase_cache) {
+		open(my $camelcase_file, '<', "$camelcase_cache")
+		    or warn "$P: Can't read '$camelcase_cache' $!\n";
+		while (<$camelcase_file>) {
+			chomp;
+			$camelcase{$_} = 1;
+		}
+		close($camelcase_file);
+
+		return;
+	}
+
+	if (-e "$gitroot") {
+		$files = `${git_command} ls-files "include/*.h"`;
+		@include_files = split('\n', $files);
+	}
+
+	foreach my $file (@include_files) {
+		seed_camelcase_file($file);
+	}
+
+	if ($camelcase_cache ne "") {
+		unlink glob ".checkpatch-camelcase.*";
+		open(my $camelcase_file, '>', "$camelcase_cache")
+		    or warn "$P: Can't write '$camelcase_cache' $!\n";
+		foreach (sort { lc($a) cmp lc($b) } keys(%camelcase)) {
+			print $camelcase_file ("$_\n");
+		}
+		close($camelcase_file);
+	}
+}
+
+sub git_is_single_file {
+	my ($filename) = @_;
+
+	return 0 if ((which("git") eq "") || !(-e "$gitroot"));
+
+	my $output = `${git_command} ls-files -- $filename 2>/dev/null`;
+	my $count = $output =~ tr/\n//;
+	return $count eq 1 && $output =~ m{^${filename}$};
+}
+
+sub git_commit_info {
+	my ($commit, $id, $desc) = @_;
+
+	return ($id, $desc) if ((which("git") eq "") || !(-e "$gitroot"));
+
+	my $output = `${git_command} log --no-color --format='%H %s' -1 $commit 2>&1`;
+	$output =~ s/^\s*//gm;
+	my @lines = split("\n", $output);
+
+	return ($id, $desc) if ($#lines < 0);
+
+	if ($lines[0] =~ /^error: short SHA1 $commit is ambiguous/) {
+# Maybe one day convert this block of bash into something that returns
+# all matching commit ids, but it's very slow...
+#
+#		echo "checking commits $1..."
+#		git rev-list --remotes | grep -i "^$1" |
+#		while read line ; do
+#		    git log --format='%H %s' -1 $line |
+#		    echo "commit $(cut -c 1-12,41-)"
+#		done
+	} elsif ($lines[0] =~ /^fatal: ambiguous argument '$commit': unknown revision or path not in the working tree\./ ||
+		 $lines[0] =~ /^fatal: bad object $commit/) {
+		$id = undef;
+	} else {
+		$id = substr($lines[0], 0, 12);
+		$desc = substr($lines[0], 41);
+	}
+
+	return ($id, $desc);
+}
+
+$chk_signoff = 0 if ($file);
+
+my @rawlines = ();
+my @lines = ();
+my @fixed = ();
+my @fixed_inserted = ();
+my @fixed_deleted = ();
+my $fixlinenr = -1;
+
+# If input is git commits, extract all commits from the commit expressions.
+# For example, HEAD-3 means we need check 'HEAD, HEAD~1, HEAD~2'.
+die "$P: No git repository found\n" if ($git && !-e "$gitroot");
+
+if ($git) {
+	my @commits = ();
+	foreach my $commit_expr (@ARGV) {
+		my $git_range;
+		if ($commit_expr =~ m/^(.*)-(\d+)$/) {
+			$git_range = "-$2 $1";
+		} elsif ($commit_expr =~ m/\.\./) {
+			$git_range = "$commit_expr";
+		} else {
+			$git_range = "-1 $commit_expr";
+		}
+		my $lines = `${git_command} log --no-color --no-merges --pretty=format:'%H %s' $git_range`;
+		foreach my $line (split(/\n/, $lines)) {
+			$line =~ /^([0-9a-fA-F]{40,40}) (.*)$/;
+			next if (!defined($1) || !defined($2));
+			my $sha1 = $1;
+			my $subject = $2;
+			unshift(@commits, $sha1);
+			$git_commits{$sha1} = $subject;
+		}
+	}
+	die "$P: no git commits after extraction!\n" if (@commits == 0);
+	@ARGV = @commits;
+}
+
+my $vname;
+$allow_c99_comments = !defined $ignore_type{"C99_COMMENT_TOLERANCE"};
+for my $filename (@ARGV) {
+	my $FILE;
+	my $is_git_file = git_is_single_file($filename);
+	my $oldfile = $file;
+	$file = 1 if ($is_git_file);
+	if ($git) {
+		open($FILE, '-|', "git format-patch -M --stdout -1 $filename") ||
+			die "$P: $filename: git format-patch failed - $!\n";
+	} elsif ($file) {
+		open($FILE, '-|', "diff -u /dev/null $filename") ||
+			die "$P: $filename: diff failed - $!\n";
+	} elsif ($filename eq '-') {
+		open($FILE, '<&STDIN');
+	} else {
+		open($FILE, '<', "$filename") ||
+			die "$P: $filename: open failed - $!\n";
+	}
+	if ($filename eq '-') {
+		$vname = 'Your patch';
+	} elsif ($git) {
+		$vname = "Commit " . substr($filename, 0, 12) . ' ("' . $git_commits{$filename} . '")';
+	} else {
+		$vname = $filename;
+	}
+	while (<$FILE>) {
+		chomp;
+		push(@rawlines, $_);
+		$vname = qq("$1") if ($filename eq '-' && $_ =~ m/^Subject:\s+(.+)/i);
+	}
+	close($FILE);
+
+	if ($#ARGV > 0 && $quiet == 0) {
+		print '-' x length($vname) . "\n";
+		print "$vname\n";
+		print '-' x length($vname) . "\n";
+	}
+
+	if (!process($filename)) {
+		$exit = 1;
+	}
+	@rawlines = ();
+	@lines = ();
+	@fixed = ();
+	@fixed_inserted = ();
+	@fixed_deleted = ();
+	$fixlinenr = -1;
+	@modifierListFile = ();
+	@typeListFile = ();
+	build_types();
+	$file = $oldfile if ($is_git_file);
+}
+
+if (!$quiet) {
+	hash_show_words(\%use_type, "Used");
+	hash_show_words(\%ignore_type, "Ignored");
+
+	if (!$perl_version_ok) {
+		print << "EOM"
+
+NOTE: perl $^V is not modern enough to detect all possible issues.
+      An upgrade to at least perl $minimum_perl_version is suggested.
+EOM
+	}
+	if ($exit) {
+		print << "EOM"
+
+NOTE: If any of the errors are false positives, please report
+      them to the maintainer, see CHECKPATCH in MAINTAINERS.
+EOM
+	}
+}
+
+exit($exit);
+
+sub top_of_kernel_tree {
+	my ($root) = @_;
+
+	my @tree_check = (
+		"COPYING", "CREDITS", "Kbuild", "MAINTAINERS", "Makefile",
+		"README", "Documentation", "arch", "include", "drivers",
+		"fs", "init", "ipc", "kernel", "lib", "scripts",
+	);
+
+	foreach my $check (@tree_check) {
+		if (! -e $root . '/' . $check) {
+			return 0;
+		}
+	}
+	return 1;
+}
+
+sub parse_email {
+	my ($formatted_email) = @_;
+
+	my $name = "";
+	my $quoted = "";
+	my $name_comment = "";
+	my $address = "";
+	my $comment = "";
+
+	if ($formatted_email =~ /^(.*)<(\S+\@\S+)>(.*)$/) {
+		$name = $1;
+		$address = $2;
+		$comment = $3 if defined $3;
+	} elsif ($formatted_email =~ /^\s*<(\S+\@\S+)>(.*)$/) {
+		$address = $1;
+		$comment = $2 if defined $2;
+	} elsif ($formatted_email =~ /(\S+\@\S+)(.*)$/) {
+		$address = $1;
+		$comment = $2 if defined $2;
+		$formatted_email =~ s/\Q$address\E.*$//;
+		$name = $formatted_email;
+		$name = trim($name);
+		$name =~ s/^\"|\"$//g;
+		# If there's a name left after stripping spaces and
+		# leading quotes, and the address doesn't have both
+		# leading and trailing angle brackets, the address
+		# is invalid. ie:
+		#   "joe smith joe@smith.com" bad
+		#   "joe smith <joe@smith.com" bad
+		if ($name ne "" && $address !~ /^<[^>]+>$/) {
+			$name = "";
+			$address = "";
+			$comment = "";
+		}
+	}
+
+	# Extract comments from names excluding quoted parts
+	# "John D. (Doe)" - Do not extract
+	if ($name =~ s/\"(.+)\"//) {
+		$quoted = $1;
+	}
+	while ($name =~ s/\s*($balanced_parens)\s*/ /) {
+		$name_comment .= trim($1);
+	}
+	$name =~ s/^[ \"]+|[ \"]+$//g;
+	$name = trim("$quoted $name");
+
+	$address = trim($address);
+	$address =~ s/^\<|\>$//g;
+	$comment = trim($comment);
+
+	if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
+		$name =~ s/(?<!\\)"/\\"/g; ##escape quotes
+		$name = "\"$name\"";
+	}
+
+	return ($name, $name_comment, $address, $comment);
+}
+
+sub format_email {
+	my ($name, $name_comment, $address, $comment) = @_;
+
+	my $formatted_email;
+
+	$name =~ s/^[ \"]+|[ \"]+$//g;
+	$address = trim($address);
+	$address =~ s/(?:\.|\,|\")+$//; ##trailing commas, dots or quotes
+
+	if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
+		$name =~ s/(?<!\\)"/\\"/g; ##escape quotes
+		$name = "\"$name\"";
+	}
+
+	$name_comment = trim($name_comment);
+	$name_comment = " $name_comment" if ($name_comment ne "");
+	$comment = trim($comment);
+	$comment = " $comment" if ($comment ne "");
+
+	if ("$name" eq "") {
+		$formatted_email = "$address";
+	} else {
+		$formatted_email = "$name$name_comment <$address>";
+	}
+	$formatted_email .= "$comment";
+	return $formatted_email;
+}
+
+sub reformat_email {
+	my ($email) = @_;
+
+	my ($email_name, $name_comment, $email_address, $comment) = parse_email($email);
+	return format_email($email_name, $name_comment, $email_address, $comment);
+}
+
+sub same_email_addresses {
+	my ($email1, $email2) = @_;
+
+	my ($email1_name, $name1_comment, $email1_address, $comment1) = parse_email($email1);
+	my ($email2_name, $name2_comment, $email2_address, $comment2) = parse_email($email2);
+
+	return $email1_name eq $email2_name &&
+	       $email1_address eq $email2_address &&
+	       $name1_comment eq $name2_comment &&
+	       $comment1 eq $comment2;
+}
+
+sub which {
+	my ($bin) = @_;
+
+	foreach my $path (split(/:/, $ENV{PATH})) {
+		if (-e "$path/$bin") {
+			return "$path/$bin";
+		}
+	}
+
+	return "";
+}
+
+sub which_conf {
+	my ($conf) = @_;
+
+	foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) {
+		if (-e "$path/$conf") {
+			return "$path/$conf";
+		}
+	}
+
+	return "";
+}
+
+sub expand_tabs {
+	my ($str) = @_;
+
+	my $res = '';
+	my $n = 0;
+	for my $c (split(//, $str)) {
+		if ($c eq "\t") {
+			$res .= ' ';
+			$n++;
+			for (; ($n % $tabsize) != 0; $n++) {
+				$res .= ' ';
+			}
+			next;
+		}
+		$res .= $c;
+		$n++;
+	}
+
+	return $res;
+}
+sub copy_spacing {
+	(my $res = shift) =~ tr/\t/ /c;
+	return $res;
+}
+
+sub line_stats {
+	my ($line) = @_;
+
+	# Drop the diff line leader and expand tabs
+	$line =~ s/^.//;
+	$line = expand_tabs($line);
+
+	# Pick the indent from the front of the line.
+	my ($white) = ($line =~ /^(\s*)/);
+
+	return (length($line), length($white));
+}
+
+my $sanitise_quote = '';
+
+sub sanitise_line_reset {
+	my ($in_comment) = @_;
+
+	if ($in_comment) {
+		$sanitise_quote = '*/';
+	} else {
+		$sanitise_quote = '';
+	}
+}
+sub sanitise_line {
+	my ($line) = @_;
+
+	my $res = '';
+	my $l = '';
+
+	my $qlen = 0;
+	my $off = 0;
+	my $c;
+
+	# Always copy over the diff marker.
+	$res = substr($line, 0, 1);
+
+	for ($off = 1; $off < length($line); $off++) {
+		$c = substr($line, $off, 1);
+
+		# Comments we are whacking completely including the begin
+		# and end, all to $;.
+		if ($sanitise_quote eq '' && substr($line, $off, 2) eq '/*') {
+			$sanitise_quote = '*/';
+
+			substr($res, $off, 2, "$;$;");
+			$off++;
+			next;
+		}
+		if ($sanitise_quote eq '*/' && substr($line, $off, 2) eq '*/') {
+			$sanitise_quote = '';
+			substr($res, $off, 2, "$;$;");
+			$off++;
+			next;
+		}
+		if ($sanitise_quote eq '' && substr($line, $off, 2) eq '//') {
+			$sanitise_quote = '//';
+
+			substr($res, $off, 2, $sanitise_quote);
+			$off++;
+			next;
+		}
+
+		# A \ in a string means ignore the next character.
+		if (($sanitise_quote eq "'" || $sanitise_quote eq '"') &&
+		    $c eq "\\") {
+			substr($res, $off, 2, 'XX');
+			$off++;
+			next;
+		}
+		# Regular quotes.
+		if ($c eq "'" || $c eq '"') {
+			if ($sanitise_quote eq '') {
+				$sanitise_quote = $c;
+
+				substr($res, $off, 1, $c);
+				next;
+			} elsif ($sanitise_quote eq $c) {
+				$sanitise_quote = '';
+			}
+		}
+
+		#print "c<$c> SQ<$sanitise_quote>\n";
+		if ($off != 0 && $sanitise_quote eq '*/' && $c ne "\t") {
+			substr($res, $off, 1, $;);
+		} elsif ($off != 0 && $sanitise_quote eq '//' && $c ne "\t") {
+			substr($res, $off, 1, $;);
+		} elsif ($off != 0 && $sanitise_quote && $c ne "\t") {
+			substr($res, $off, 1, 'X');
+		} else {
+			substr($res, $off, 1, $c);
+		}
+	}
+
+	if ($sanitise_quote eq '//') {
+		$sanitise_quote = '';
+	}
+
+	# The pathname on a #include may be surrounded by '<' and '>'.
+	if ($res =~ /^.\s*\#\s*include\s+\<(.*)\>/) {
+		my $clean = 'X' x length($1);
+		$res =~ s@\<.*\>@<$clean>@;
+
+	# The whole of a #error is a string.
+	} elsif ($res =~ /^.\s*\#\s*(?:error|warning)\s+(.*)\b/) {
+		my $clean = 'X' x length($1);
+		$res =~ s@(\#\s*(?:error|warning)\s+).*@$1$clean@;
+	}
+
+	if ($allow_c99_comments && $res =~ m@(//.*$)@) {
+		my $match = $1;
+		$res =~ s/\Q$match\E/"$;" x length($match)/e;
+	}
+
+	return $res;
+}
+
+sub get_quoted_string {
+	my ($line, $rawline) = @_;
+
+	return "" if (!defined($line) || !defined($rawline));
+	return "" if ($line !~ m/($String)/g);
+	return substr($rawline, $-[0], $+[0] - $-[0]);
+}
+
+sub ctx_statement_block {
+	my ($linenr, $remain, $off) = @_;
+	my $line = $linenr - 1;
+	my $blk = '';
+	my $soff = $off;
+	my $coff = $off - 1;
+	my $coff_set = 0;
+
+	my $loff = 0;
+
+	my $type = '';
+	my $level = 0;
+	my @stack = ();
+	my $p;
+	my $c;
+	my $len = 0;
+
+	my $remainder;
+	while (1) {
+		@stack = (['', 0]) if ($#stack == -1);
+
+		#warn "CSB: blk<$blk> remain<$remain>\n";
+		# If we are about to drop off the end, pull in more
+		# context.
+		if ($off >= $len) {
+			for (; $remain > 0; $line++) {
+				last if (!defined $lines[$line]);
+				next if ($lines[$line] =~ /^-/);
+				$remain--;
+				$loff = $len;
+				$blk .= $lines[$line] . "\n";
+				$len = length($blk);
+				$line++;
+				last;
+			}
+			# Bail if there is no further context.
+			#warn "CSB: blk<$blk> off<$off> len<$len>\n";
+			if ($off >= $len) {
+				last;
+			}
+			if ($level == 0 && substr($blk, $off) =~ /^.\s*#\s*define/) {
+				$level++;
+				$type = '#';
+			}
+		}
+		$p = $c;
+		$c = substr($blk, $off, 1);
+		$remainder = substr($blk, $off);
+
+		#warn "CSB: c<$c> type<$type> level<$level> remainder<$remainder> coff_set<$coff_set>\n";
+
+		# Handle nested #if/#else.
+		if ($remainder =~ /^#\s*(?:ifndef|ifdef|if)\s/) {
+			push(@stack, [ $type, $level ]);
+		} elsif ($remainder =~ /^#\s*(?:else|elif)\b/) {
+			($type, $level) = @{$stack[$#stack - 1]};
+		} elsif ($remainder =~ /^#\s*endif\b/) {
+			($type, $level) = @{pop(@stack)};
+		}
+
+		# Statement ends at the ';' or a close '}' at the
+		# outermost level.
+		if ($level == 0 && $c eq ';') {
+			last;
+		}
+
+		# An else is really a conditional as long as its not else if
+		if ($level == 0 && $coff_set == 0 &&
+				(!defined($p) || $p =~ /(?:\s|\}|\+)/) &&
+				$remainder =~ /^(else)(?:\s|{)/ &&
+				$remainder !~ /^else\s+if\b/) {
+			$coff = $off + length($1) - 1;
+			$coff_set = 1;
+			#warn "CSB: mark coff<$coff> soff<$soff> 1<$1>\n";
+			#warn "[" . substr($blk, $soff, $coff - $soff + 1) . "]\n";
+		}
+
+		if (($type eq '' || $type eq '(') && $c eq '(') {
+			$level++;
+			$type = '(';
+		}
+		if ($type eq '(' && $c eq ')') {
+			$level--;
+			$type = ($level != 0)? '(' : '';
+
+			if ($level == 0 && $coff < $soff) {
+				$coff = $off;
+				$coff_set = 1;
+				#warn "CSB: mark coff<$coff>\n";
+			}
+		}
+		if (($type eq '' || $type eq '{') && $c eq '{') {
+			$level++;
+			$type = '{';
+		}
+		if ($type eq '{' && $c eq '}') {
+			$level--;
+			$type = ($level != 0)? '{' : '';
+
+			if ($level == 0) {
+				if (substr($blk, $off + 1, 1) eq ';') {
+					$off++;
+				}
+				last;
+			}
+		}
+		# Preprocessor commands end at the newline unless escaped.
+		if ($type eq '#' && $c eq "\n" && $p ne "\\") {
+			$level--;
+			$type = '';
+			$off++;
+			last;
+		}
+		$off++;
+	}
+	# We are truly at the end, so shuffle to the next line.
+	if ($off == $len) {
+		$loff = $len + 1;
+		$line++;
+		$remain--;
+	}
+
+	my $statement = substr($blk, $soff, $off - $soff + 1);
+	my $condition = substr($blk, $soff, $coff - $soff + 1);
+
+	#warn "STATEMENT<$statement>\n";
+	#warn "CONDITION<$condition>\n";
+
+	#print "coff<$coff> soff<$off> loff<$loff>\n";
+
+	return ($statement, $condition,
+			$line, $remain + 1, $off - $loff + 1, $level);
+}
+
+sub statement_lines {
+	my ($stmt) = @_;
+
+	# Strip the diff line prefixes and rip blank lines at start and end.
+	$stmt =~ s/(^|\n)./$1/g;
+	$stmt =~ s/^\s*//;
+	$stmt =~ s/\s*$//;
+
+	my @stmt_lines = ($stmt =~ /\n/g);
+
+	return $#stmt_lines + 2;
+}
+
+sub statement_rawlines {
+	my ($stmt) = @_;
+
+	my @stmt_lines = ($stmt =~ /\n/g);
+
+	return $#stmt_lines + 2;
+}
+
+sub statement_block_size {
+	my ($stmt) = @_;
+
+	$stmt =~ s/(^|\n)./$1/g;
+	$stmt =~ s/^\s*{//;
+	$stmt =~ s/}\s*$//;
+	$stmt =~ s/^\s*//;
+	$stmt =~ s/\s*$//;
+
+	my @stmt_lines = ($stmt =~ /\n/g);
+	my @stmt_statements = ($stmt =~ /;/g);
+
+	my $stmt_lines = $#stmt_lines + 2;
+	my $stmt_statements = $#stmt_statements + 1;
+
+	if ($stmt_lines > $stmt_statements) {
+		return $stmt_lines;
+	} else {
+		return $stmt_statements;
+	}
+}
+
+sub ctx_statement_full {
+	my ($linenr, $remain, $off) = @_;
+	my ($statement, $condition, $level);
+
+	my (@chunks);
+
+	# Grab the first conditional/block pair.
+	($statement, $condition, $linenr, $remain, $off, $level) =
+				ctx_statement_block($linenr, $remain, $off);
+	#print "F: c<$condition> s<$statement> remain<$remain>\n";
+	push(@chunks, [ $condition, $statement ]);
+	if (!($remain > 0 && $condition =~ /^\s*(?:\n[+-])?\s*(?:if|else|do)\b/s)) {
+		return ($level, $linenr, @chunks);
+	}
+
+	# Pull in the following conditional/block pairs and see if they
+	# could continue the statement.
+	for (;;) {
+		($statement, $condition, $linenr, $remain, $off, $level) =
+				ctx_statement_block($linenr, $remain, $off);
+		#print "C: c<$condition> s<$statement> remain<$remain>\n";
+		last if (!($remain > 0 && $condition =~ /^(?:\s*\n[+-])*\s*(?:else|do)\b/s));
+		#print "C: push\n";
+		push(@chunks, [ $condition, $statement ]);
+	}
+
+	return ($level, $linenr, @chunks);
+}
+
+sub ctx_block_get {
+	my ($linenr, $remain, $outer, $open, $close, $off) = @_;
+	my $line;
+	my $start = $linenr - 1;
+	my $blk = '';
+	my @o;
+	my @c;
+	my @res = ();
+
+	my $level = 0;
+	my @stack = ($level);
+	for ($line = $start; $remain > 0; $line++) {
+		next if ($rawlines[$line] =~ /^-/);
+		$remain--;
+
+		$blk .= $rawlines[$line];
+
+		# Handle nested #if/#else.
+		if ($lines[$line] =~ /^.\s*#\s*(?:ifndef|ifdef|if)\s/) {
+			push(@stack, $level);
+		} elsif ($lines[$line] =~ /^.\s*#\s*(?:else|elif)\b/) {
+			$level = $stack[$#stack - 1];
+		} elsif ($lines[$line] =~ /^.\s*#\s*endif\b/) {
+			$level = pop(@stack);
+		}
+
+		foreach my $c (split(//, $lines[$line])) {
+			##print "C<$c>L<$level><$open$close>O<$off>\n";
+			if ($off > 0) {
+				$off--;
+				next;
+			}
+
+			if ($c eq $close && $level > 0) {
+				$level--;
+				last if ($level == 0);
+			} elsif ($c eq $open) {
+				$level++;
+			}
+		}
+
+		if (!$outer || $level <= 1) {
+			push(@res, $rawlines[$line]);
+		}
+
+		last if ($level == 0);
+	}
+
+	return ($level, @res);
+}
+sub ctx_block_outer {
+	my ($linenr, $remain) = @_;
+
+	my ($level, @r) = ctx_block_get($linenr, $remain, 1, '{', '}', 0);
+	return @r;
+}
+sub ctx_block {
+	my ($linenr, $remain) = @_;
+
+	my ($level, @r) = ctx_block_get($linenr, $remain, 0, '{', '}', 0);
+	return @r;
+}
+sub ctx_statement {
+	my ($linenr, $remain, $off) = @_;
+
+	my ($level, @r) = ctx_block_get($linenr, $remain, 0, '(', ')', $off);
+	return @r;
+}
+sub ctx_block_level {
+	my ($linenr, $remain) = @_;
+
+	return ctx_block_get($linenr, $remain, 0, '{', '}', 0);
+}
+sub ctx_statement_level {
+	my ($linenr, $remain, $off) = @_;
+
+	return ctx_block_get($linenr, $remain, 0, '(', ')', $off);
+}
+
+sub ctx_locate_comment {
+	my ($first_line, $end_line) = @_;
+
+	# If c99 comment on the current line, or the line before or after
+	my ($current_comment) = ($rawlines[$end_line - 1] =~ m@^\+.*(//.*$)@);
+	return $current_comment if (defined $current_comment);
+	($current_comment) = ($rawlines[$end_line - 2] =~ m@^[\+ ].*(//.*$)@);
+	return $current_comment if (defined $current_comment);
+	($current_comment) = ($rawlines[$end_line] =~ m@^[\+ ].*(//.*$)@);
+	return $current_comment if (defined $current_comment);
+
+	# Catch a comment on the end of the line itself.
+	($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*(?:\\\s*)?$@);
+	return $current_comment if (defined $current_comment);
+
+	# Look through the context and try and figure out if there is a
+	# comment.
+	my $in_comment = 0;
+	$current_comment = '';
+	for (my $linenr = $first_line; $linenr < $end_line; $linenr++) {
+		my $line = $rawlines[$linenr - 1];
+		#warn "           $line\n";
+		if ($linenr == $first_line and $line =~ m@^.\s*\*@) {
+			$in_comment = 1;
+		}
+		if ($line =~ m@/\*@) {
+			$in_comment = 1;
+		}
+		if (!$in_comment && $current_comment ne '') {
+			$current_comment = '';
+		}
+		$current_comment .= $line . "\n" if ($in_comment);
+		if ($line =~ m@\*/@) {
+			$in_comment = 0;
+		}
+	}
+
+	chomp($current_comment);
+	return($current_comment);
+}
+sub ctx_has_comment {
+	my ($first_line, $end_line) = @_;
+	my $cmt = ctx_locate_comment($first_line, $end_line);
+
+	##print "LINE: $rawlines[$end_line - 1 ]\n";
+	##print "CMMT: $cmt\n";
+
+	return ($cmt ne '');
+}
+
+sub raw_line {
+	my ($linenr, $cnt) = @_;
+
+	my $offset = $linenr - 1;
+	$cnt++;
+
+	my $line;
+	while ($cnt) {
+		$line = $rawlines[$offset++];
+		next if (defined($line) && $line =~ /^-/);
+		$cnt--;
+	}
+
+	return $line;
+}
+
+sub get_stat_real {
+	my ($linenr, $lc) = @_;
+
+	my $stat_real = raw_line($linenr, 0);
+	for (my $count = $linenr + 1; $count <= $lc; $count++) {
+		$stat_real = $stat_real . "\n" . raw_line($count, 0);
+	}
+
+	return $stat_real;
+}
+
+sub get_stat_here {
+	my ($linenr, $cnt, $here) = @_;
+
+	my $herectx = $here . "\n";
+	for (my $n = 0; $n < $cnt; $n++) {
+		$herectx .= raw_line($linenr, $n) . "\n";
+	}
+
+	return $herectx;
+}
+
+sub cat_vet {
+	my ($vet) = @_;
+	my ($res, $coded);
+
+	$res = '';
+	while ($vet =~ /([^[:cntrl:]]*)([[:cntrl:]]|$)/g) {
+		$res .= $1;
+		if ($2 ne '') {
+			$coded = sprintf("^%c", unpack('C', $2) + 64);
+			$res .= $coded;
+		}
+	}
+	$res =~ s/$/\$/;
+
+	return $res;
+}
+
+my $av_preprocessor = 0;
+my $av_pending;
+my @av_paren_type;
+my $av_pend_colon;
+
+sub annotate_reset {
+	$av_preprocessor = 0;
+	$av_pending = '_';
+	@av_paren_type = ('E');
+	$av_pend_colon = 'O';
+}
+
+sub annotate_values {
+	my ($stream, $type) = @_;
+
+	my $res;
+	my $var = '_' x length($stream);
+	my $cur = $stream;
+
+	print "$stream\n" if ($dbg_values > 1);
+
+	while (length($cur)) {
+		@av_paren_type = ('E') if ($#av_paren_type < 0);
+		print " <" . join('', @av_paren_type) .
+				"> <$type> <$av_pending>" if ($dbg_values > 1);
+		if ($cur =~ /^(\s+)/o) {
+			print "WS($1)\n" if ($dbg_values > 1);
+			if ($1 =~ /\n/ && $av_preprocessor) {
+				$type = pop(@av_paren_type);
+				$av_preprocessor = 0;
+			}
+
+		} elsif ($cur =~ /^(\(\s*$Type\s*)\)/ && $av_pending eq '_') {
+			print "CAST($1)\n" if ($dbg_values > 1);
+			push(@av_paren_type, $type);
+			$type = 'c';
+
+		} elsif ($cur =~ /^($Type)\s*(?:$Ident|,|\)|\(|\s*$)/) {
+			print "DECLARE($1)\n" if ($dbg_values > 1);
+			$type = 'T';
+
+		} elsif ($cur =~ /^($Modifier)\s*/) {
+			print "MODIFIER($1)\n" if ($dbg_values > 1);
+			$type = 'T';
+
+		} elsif ($cur =~ /^(\#\s*define\s*$Ident)(\(?)/o) {
+			print "DEFINE($1,$2)\n" if ($dbg_values > 1);
+			$av_preprocessor = 1;
+			push(@av_paren_type, $type);
+			if ($2 ne '') {
+				$av_pending = 'N';
+			}
+			$type = 'E';
+
+		} elsif ($cur =~ /^(\#\s*(?:undef\s*$Ident|include\b))/o) {
+			print "UNDEF($1)\n" if ($dbg_values > 1);
+			$av_preprocessor = 1;
+			push(@av_paren_type, $type);
+
+		} elsif ($cur =~ /^(\#\s*(?:ifdef|ifndef|if))/o) {
+			print "PRE_START($1)\n" if ($dbg_values > 1);
+			$av_preprocessor = 1;
+
+			push(@av_paren_type, $type);
+			push(@av_paren_type, $type);
+			$type = 'E';
+
+		} elsif ($cur =~ /^(\#\s*(?:else|elif))/o) {
+			print "PRE_RESTART($1)\n" if ($dbg_values > 1);
+			$av_preprocessor = 1;
+
+			push(@av_paren_type, $av_paren_type[$#av_paren_type]);
+
+			$type = 'E';
+
+		} elsif ($cur =~ /^(\#\s*(?:endif))/o) {
+			print "PRE_END($1)\n" if ($dbg_values > 1);
+
+			$av_preprocessor = 1;
+
+			# Assume all arms of the conditional end as this
+			# one does, and continue as if the #endif was not here.
+			pop(@av_paren_type);
+			push(@av_paren_type, $type);
+			$type = 'E';
+
+		} elsif ($cur =~ /^(\\\n)/o) {
+			print "PRECONT($1)\n" if ($dbg_values > 1);
+
+		} elsif ($cur =~ /^(__attribute__)\s*\(?/o) {
+			print "ATTR($1)\n" if ($dbg_values > 1);
+			$av_pending = $type;
+			$type = 'N';
+
+		} elsif ($cur =~ /^(sizeof)\s*(\()?/o) {
+			print "SIZEOF($1)\n" if ($dbg_values > 1);
+			if (defined $2) {
+				$av_pending = 'V';
+			}
+			$type = 'N';
+
+		} elsif ($cur =~ /^(if|while|for)\b/o) {
+			print "COND($1)\n" if ($dbg_values > 1);
+			$av_pending = 'E';
+			$type = 'N';
+
+		} elsif ($cur =~/^(case)/o) {
+			print "CASE($1)\n" if ($dbg_values > 1);
+			$av_pend_colon = 'C';
+			$type = 'N';
+
+		} elsif ($cur =~/^(return|else|goto|typeof|__typeof__)\b/o) {
+			print "KEYWORD($1)\n" if ($dbg_values > 1);
+			$type = 'N';
+
+		} elsif ($cur =~ /^(\()/o) {
+			print "PAREN('$1')\n" if ($dbg_values > 1);
+			push(@av_paren_type, $av_pending);
+			$av_pending = '_';
+			$type = 'N';
+
+		} elsif ($cur =~ /^(\))/o) {
+			my $new_type = pop(@av_paren_type);
+			if ($new_type ne '_') {
+				$type = $new_type;
+				print "PAREN('$1') -> $type\n"
+							if ($dbg_values > 1);
+			} else {
+				print "PAREN('$1')\n" if ($dbg_values > 1);
+			}
+
+		} elsif ($cur =~ /^($Ident)\s*\(/o) {
+			print "FUNC($1)\n" if ($dbg_values > 1);
+			$type = 'V';
+			$av_pending = 'V';
+
+		} elsif ($cur =~ /^($Ident\s*):(?:\s*\d+\s*(,|=|;))?/) {
+			if (defined $2 && $type eq 'C' || $type eq 'T') {
+				$av_pend_colon = 'B';
+			} elsif ($type eq 'E') {
+				$av_pend_colon = 'L';
+			}
+			print "IDENT_COLON($1,$type>$av_pend_colon)\n" if ($dbg_values > 1);
+			$type = 'V';
+
+		} elsif ($cur =~ /^($Ident|$Constant)/o) {
+			print "IDENT($1)\n" if ($dbg_values > 1);
+			$type = 'V';
+
+		} elsif ($cur =~ /^($Assignment)/o) {
+			print "ASSIGN($1)\n" if ($dbg_values > 1);
+			$type = 'N';
+
+		} elsif ($cur =~/^(;|{|})/) {
+			print "END($1)\n" if ($dbg_values > 1);
+			$type = 'E';
+			$av_pend_colon = 'O';
+
+		} elsif ($cur =~/^(,)/) {
+			print "COMMA($1)\n" if ($dbg_values > 1);
+			$type = 'C';
+
+		} elsif ($cur =~ /^(\?)/o) {
+			print "QUESTION($1)\n" if ($dbg_values > 1);
+			$type = 'N';
+
+		} elsif ($cur =~ /^(:)/o) {
+			print "COLON($1,$av_pend_colon)\n" if ($dbg_values > 1);
+
+			substr($var, length($res), 1, $av_pend_colon);
+			if ($av_pend_colon eq 'C' || $av_pend_colon eq 'L') {
+				$type = 'E';
+			} else {
+				$type = 'N';
+			}
+			$av_pend_colon = 'O';
+
+		} elsif ($cur =~ /^(\[)/o) {
+			print "CLOSE($1)\n" if ($dbg_values > 1);
+			$type = 'N';
+
+		} elsif ($cur =~ /^(-(?![->])|\+(?!\+)|\*|\&\&|\&)/o) {
+			my $variant;
+
+			print "OPV($1)\n" if ($dbg_values > 1);
+			if ($type eq 'V') {
+				$variant = 'B';
+			} else {
+				$variant = 'U';
+			}
+
+			substr($var, length($res), 1, $variant);
+			$type = 'N';
+
+		} elsif ($cur =~ /^($Operators)/o) {
+			print "OP($1)\n" if ($dbg_values > 1);
+			if ($1 ne '++' && $1 ne '--') {
+				$type = 'N';
+			}
+
+		} elsif ($cur =~ /(^.)/o) {
+			print "C($1)\n" if ($dbg_values > 1);
+		}
+		if (defined $1) {
+			$cur = substr($cur, length($1));
+			$res .= $type x length($1);
+		}
+	}
+
+	return ($res, $var);
+}
+
+sub possible {
+	my ($possible, $line) = @_;
+	my $notPermitted = qr{(?:
+		^(?:
+			$Modifier|
+			$Storage|
+			$Type|
+			DEFINE_\S+
+		)$|
+		^(?:
+			goto|
+			return|
+			case|
+			else|
+			asm|__asm__|
+			do|
+			\#|
+			\#\#|
+		)(?:\s|$)|
+		^(?:typedef|struct|enum)\b
+	    )}x;
+	warn "CHECK<$possible> ($line)\n" if ($dbg_possible > 2);
+	if ($possible !~ $notPermitted) {
+		# Check for modifiers.
+		$possible =~ s/\s*$Storage\s*//g;
+		$possible =~ s/\s*$Sparse\s*//g;
+		if ($possible =~ /^\s*$/) {
+
+		} elsif ($possible =~ /\s/) {
+			$possible =~ s/\s*$Type\s*//g;
+			for my $modifier (split(' ', $possible)) {
+				if ($modifier !~ $notPermitted) {
+					warn "MODIFIER: $modifier ($possible) ($line)\n" if ($dbg_possible);
+					push(@modifierListFile, $modifier);
+				}
+			}
+
+		} else {
+			warn "POSSIBLE: $possible ($line)\n" if ($dbg_possible);
+			push(@typeListFile, $possible);
+		}
+		build_types();
+	} else {
+		warn "NOTPOSS: $possible ($line)\n" if ($dbg_possible > 1);
+	}
+}
+
+my $prefix = '';
+
+sub show_type {
+	my ($type) = @_;
+
+	$type =~ tr/[a-z]/[A-Z]/;
+
+	return defined $use_type{$type} if (scalar keys %use_type > 0);
+
+	return !defined $ignore_type{$type};
+}
+
+sub report {
+	my ($level, $type, $msg) = @_;
+
+	if (!show_type($type) ||
+	    (defined $tst_only && $msg !~ /\Q$tst_only\E/)) {
+		return 0;
+	}
+	my $output = '';
+	if ($color) {
+		if ($level eq 'ERROR') {
+			$output .= RED;
+		} elsif ($level eq 'WARNING') {
+			$output .= YELLOW;
+		} else {
+			$output .= GREEN;
+		}
+	}
+	$output .= $prefix . $level . ':';
+	if ($show_types) {
+		$output .= BLUE if ($color);
+		$output .= "$type:";
+	}
+	$output .= RESET if ($color);
+	$output .= ' ' . $msg . "\n";
+
+	if ($showfile) {
+		my @lines = split("\n", $output, -1);
+		splice(@lines, 1, 1);
+		$output = join("\n", @lines);
+	}
+
+	if ($terse) {
+		$output = (split('\n', $output))[0] . "\n";
+	}
+
+	if ($verbose && exists($verbose_messages{$type}) &&
+	    !exists($verbose_emitted{$type})) {
+		$output .= $verbose_messages{$type} . "\n\n";
+		$verbose_emitted{$type} = 1;
+	}
+
+	push(our @report, $output);
+
+	return 1;
+}
+
+sub report_dump {
+	our @report;
+}
+
+sub fixup_current_range {
+	my ($lineRef, $offset, $length) = @_;
+
+	if ($$lineRef =~ /^\@\@ -\d+,\d+ \+(\d+),(\d+) \@\@/) {
+		my $o = $1;
+		my $l = $2;
+		my $no = $o + $offset;
+		my $nl = $l + $length;
+		$$lineRef =~ s/\+$o,$l \@\@/\+$no,$nl \@\@/;
+	}
+}
+
+sub fix_inserted_deleted_lines {
+	my ($linesRef, $insertedRef, $deletedRef) = @_;
+
+	my $range_last_linenr = 0;
+	my $delta_offset = 0;
+
+	my $old_linenr = 0;
+	my $new_linenr = 0;
+
+	my $next_insert = 0;
+	my $next_delete = 0;
+
+	my @lines = ();
+
+	my $inserted = @{$insertedRef}[$next_insert++];
+	my $deleted = @{$deletedRef}[$next_delete++];
+
+	foreach my $old_line (@{$linesRef}) {
+		my $save_line = 1;
+		my $line = $old_line;	#don't modify the array
+		if ($line =~ /^(?:\+\+\+|\-\-\-)\s+\S+/) {	#new filename
+			$delta_offset = 0;
+		} elsif ($line =~ /^\@\@ -\d+,\d+ \+\d+,\d+ \@\@/) {	#new hunk
+			$range_last_linenr = $new_linenr;
+			fixup_current_range(\$line, $delta_offset, 0);
+		}
+
+		while (defined($deleted) && ${$deleted}{'LINENR'} == $old_linenr) {
+			$deleted = @{$deletedRef}[$next_delete++];
+			$save_line = 0;
+			fixup_current_range(\$lines[$range_last_linenr], $delta_offset--, -1);
+		}
+
+		while (defined($inserted) && ${$inserted}{'LINENR'} == $old_linenr) {
+			push(@lines, ${$inserted}{'LINE'});
+			$inserted = @{$insertedRef}[$next_insert++];
+			$new_linenr++;
+			fixup_current_range(\$lines[$range_last_linenr], $delta_offset++, 1);
+		}
+
+		if ($save_line) {
+			push(@lines, $line);
+			$new_linenr++;
+		}
+
+		$old_linenr++;
+	}
+
+	return @lines;
+}
+
+sub fix_insert_line {
+	my ($linenr, $line) = @_;
+
+	my $inserted = {
+		LINENR => $linenr,
+		LINE => $line,
+	};
+	push(@fixed_inserted, $inserted);
+}
+
+sub fix_delete_line {
+	my ($linenr, $line) = @_;
+
+	my $deleted = {
+		LINENR => $linenr,
+		LINE => $line,
+	};
+
+	push(@fixed_deleted, $deleted);
+}
+
+sub ERROR {
+	my ($type, $msg) = @_;
+
+	if (report("ERROR", $type, $msg)) {
+		our $clean = 0;
+		our $cnt_error++;
+		return 1;
+	}
+	return 0;
+}
+sub WARN {
+	my ($type, $msg) = @_;
+
+	if (report("WARNING", $type, $msg)) {
+		our $clean = 0;
+		our $cnt_warn++;
+		return 1;
+	}
+	return 0;
+}
+sub CHK {
+	my ($type, $msg) = @_;
+
+	if ($check && report("CHECK", $type, $msg)) {
+		our $clean = 0;
+		our $cnt_chk++;
+		return 1;
+	}
+	return 0;
+}
+
+sub check_absolute_file {
+	my ($absolute, $herecurr) = @_;
+	my $file = $absolute;
+
+	##print "absolute<$absolute>\n";
+
+	# See if any suffix of this path is a path within the tree.
+	while ($file =~ s@^[^/]*/@@) {
+		if (-f "$root/$file") {
+			##print "file<$file>\n";
+			last;
+		}
+	}
+	if (! -f _)  {
+		return 0;
+	}
+
+	# It is, so see if the prefix is acceptable.
+	my $prefix = $absolute;
+	substr($prefix, -length($file)) = '';
+
+	##print "prefix<$prefix>\n";
+	if ($prefix ne ".../") {
+		WARN("USE_RELATIVE_PATH",
+		     "use relative pathname instead of absolute in changelog text\n" . $herecurr);
+	}
+}
+
+sub trim {
+	my ($string) = @_;
+
+	$string =~ s/^\s+|\s+$//g;
+
+	return $string;
+}
+
+sub ltrim {
+	my ($string) = @_;
+
+	$string =~ s/^\s+//;
+
+	return $string;
+}
+
+sub rtrim {
+	my ($string) = @_;
+
+	$string =~ s/\s+$//;
+
+	return $string;
+}
+
+sub string_find_replace {
+	my ($string, $find, $replace) = @_;
+
+	$string =~ s/$find/$replace/g;
+
+	return $string;
+}
+
+sub tabify {
+	my ($leading) = @_;
+
+	my $source_indent = $tabsize;
+	my $max_spaces_before_tab = $source_indent - 1;
+	my $spaces_to_tab = " " x $source_indent;
+
+	#convert leading spaces to tabs
+	1 while $leading =~ s@^([\t]*)$spaces_to_tab@$1\t@g;
+	#Remove spaces before a tab
+	1 while $leading =~ s@^([\t]*)( {1,$max_spaces_before_tab})\t@$1\t@g;
+
+	return "$leading";
+}
+
+sub pos_last_openparen {
+	my ($line) = @_;
+
+	my $pos = 0;
+
+	my $opens = $line =~ tr/\(/\(/;
+	my $closes = $line =~ tr/\)/\)/;
+
+	my $last_openparen = 0;
+
+	if (($opens == 0) || ($closes >= $opens)) {
+		return -1;
+	}
+
+	my $len = length($line);
+
+	for ($pos = 0; $pos < $len; $pos++) {
+		my $string = substr($line, $pos);
+		if ($string =~ /^($FuncArg|$balanced_parens)/) {
+			$pos += length($1) - 1;
+		} elsif (substr($line, $pos, 1) eq '(') {
+			$last_openparen = $pos;
+		} elsif (index($string, '(') == -1) {
+			last;
+		}
+	}
+
+	return length(expand_tabs(substr($line, 0, $last_openparen))) + 1;
+}
+
+sub get_raw_comment {
+	my ($line, $rawline) = @_;
+	my $comment = '';
+
+	for my $i (0 .. (length($line) - 1)) {
+		if (substr($line, $i, 1) eq "$;") {
+			$comment .= substr($rawline, $i, 1);
+		}
+	}
+
+	return $comment;
+}
+
+sub exclude_global_initialisers {
+	my ($realfile) = @_;
+
+	# Do not check for BPF programs (tools/testing/selftests/bpf/progs/*.c, samples/bpf/*_kern.c, *.bpf.c).
+	return $realfile =~ m@^tools/testing/selftests/bpf/progs/.*\.c$@ ||
+		$realfile =~ m@^samples/bpf/.*_kern\.c$@ ||
+		$realfile =~ m@/bpf/.*\.bpf\.c$@;
+}
+
+sub process {
+	my $filename = shift;
+
+	my $linenr=0;
+	my $prevline="";
+	my $prevrawline="";
+	my $stashline="";
+	my $stashrawline="";
+
+	my $length;
+	my $indent;
+	my $previndent=0;
+	my $stashindent=0;
+
+	our $clean = 1;
+	my $signoff = 0;
+	my $author = '';
+	my $authorsignoff = 0;
+	my $author_sob = '';
+	my $is_patch = 0;
+	my $is_binding_patch = -1;
+	my $in_header_lines = $file ? 0 : 1;
+	my $in_commit_log = 0;		#Scanning lines before patch
+	my $has_patch_separator = 0;	#Found a --- line
+	my $has_commit_log = 0;		#Encountered lines before patch
+	my $commit_log_lines = 0;	#Number of commit log lines
+	my $commit_log_possible_stack_dump = 0;
+	my $commit_log_long_line = 0;
+	my $commit_log_has_diff = 0;
+	my $reported_maintainer_file = 0;
+	my $non_utf8_charset = 0;
+
+	my $last_git_commit_id_linenr = -1;
+
+	my $last_blank_line = 0;
+	my $last_coalesced_string_linenr = -1;
+
+	our @report = ();
+	our $cnt_lines = 0;
+	our $cnt_error = 0;
+	our $cnt_warn = 0;
+	our $cnt_chk = 0;
+
+	# Trace the real file/line as we go.
+	my $realfile = '';
+	my $realline = 0;
+	my $realcnt = 0;
+	my $here = '';
+	my $context_function;		#undef'd unless there's a known function
+	my $in_comment = 0;
+	my $comment_edge = 0;
+	my $first_line = 0;
+	my $p1_prefix = '';
+
+	my $prev_values = 'E';
+
+	# suppression flags
+	my %suppress_ifbraces;
+	my %suppress_whiletrailers;
+	my %suppress_export;
+	my $suppress_statement = 0;
+
+	my %signatures = ();
+
+	# Pre-scan the patch sanitizing the lines.
+	# Pre-scan the patch looking for any __setup documentation.
+	#
+	my @setup_docs = ();
+	my $setup_docs = 0;
+
+	my $camelcase_file_seeded = 0;
+
+	my $checklicenseline = 1;
+
+	sanitise_line_reset();
+	my $line;
+	foreach my $rawline (@rawlines) {
+		$linenr++;
+		$line = $rawline;
+
+		push(@fixed, $rawline) if ($fix);
+
+		if ($rawline=~/^\+\+\+\s+(\S+)/) {
+			$setup_docs = 0;
+			if ($1 =~ m@Documentation/admin-guide/kernel-parameters.txt$@) {
+				$setup_docs = 1;
+			}
+			#next;
+		}
+		if ($rawline =~ /^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) {
+			$realline=$1-1;
+			if (defined $2) {
+				$realcnt=$3+1;
+			} else {
+				$realcnt=1+1;
+			}
+			$in_comment = 0;
+
+			# Guestimate if this is a continuing comment.  Run
+			# the context looking for a comment "edge".  If this
+			# edge is a close comment then we must be in a comment
+			# at context start.
+			my $edge;
+			my $cnt = $realcnt;
+			for (my $ln = $linenr + 1; $cnt > 0; $ln++) {
+				next if (defined $rawlines[$ln - 1] &&
+					 $rawlines[$ln - 1] =~ /^-/);
+				$cnt--;
+				#print "RAW<$rawlines[$ln - 1]>\n";
+				last if (!defined $rawlines[$ln - 1]);
+				if ($rawlines[$ln - 1] =~ m@(/\*|\*/)@ &&
+				    $rawlines[$ln - 1] !~ m@"[^"]*(?:/\*|\*/)[^"]*"@) {
+					($edge) = $1;
+					last;
+				}
+			}
+			if (defined $edge && $edge eq '*/') {
+				$in_comment = 1;
+			}
+
+			# Guestimate if this is a continuing comment.  If this
+			# is the start of a diff block and this line starts
+			# ' *' then it is very likely a comment.
+			if (!defined $edge &&
+			    $rawlines[$linenr] =~ m@^.\s*(?:\*\*+| \*)(?:\s|$)@)
+			{
+				$in_comment = 1;
+			}
+
+			##print "COMMENT:$in_comment edge<$edge> $rawline\n";
+			sanitise_line_reset($in_comment);
+
+		} elsif ($realcnt && $rawline =~ /^(?:\+| |$)/) {
+			# Standardise the strings and chars within the input to
+			# simplify matching -- only bother with positive lines.
+			$line = sanitise_line($rawline);
+		}
+		push(@lines, $line);
+
+		if ($realcnt > 1) {
+			$realcnt-- if ($line =~ /^(?:\+| |$)/);
+		} else {
+			$realcnt = 0;
+		}
+
+		#print "==>$rawline\n";
+		#print "-->$line\n";
+
+		if ($setup_docs && $line =~ /^\+/) {
+			push(@setup_docs, $line);
+		}
+	}
+
+	$prefix = '';
+
+	$realcnt = 0;
+	$linenr = 0;
+	$fixlinenr = -1;
+	foreach my $line (@lines) {
+		$linenr++;
+		$fixlinenr++;
+		my $sline = $line;	#copy of $line
+		$sline =~ s/$;/ /g;	#with comments as spaces
+
+		my $rawline = $rawlines[$linenr - 1];
+		my $raw_comment = get_raw_comment($line, $rawline);
+
+# check if it's a mode change, rename or start of a patch
+		if (!$in_commit_log &&
+		    ($line =~ /^ mode change [0-7]+ => [0-7]+ \S+\s*$/ ||
+		    ($line =~ /^rename (?:from|to) \S+\s*$/ ||
+		     $line =~ /^diff --git a\/[\w\/\.\_\-]+ b\/\S+\s*$/))) {
+			$is_patch = 1;
+		}
+
+#extract the line range in the file after the patch is applied
+		if (!$in_commit_log &&
+		    $line =~ /^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@(.*)/) {
+			my $context = $4;
+			$is_patch = 1;
+			$first_line = $linenr + 1;
+			$realline=$1-1;
+			if (defined $2) {
+				$realcnt=$3+1;
+			} else {
+				$realcnt=1+1;
+			}
+			annotate_reset();
+			$prev_values = 'E';
+
+			%suppress_ifbraces = ();
+			%suppress_whiletrailers = ();
+			%suppress_export = ();
+			$suppress_statement = 0;
+			if ($context =~ /\b(\w+)\s*\(/) {
+				$context_function = $1;
+			} else {
+				undef $context_function;
+			}
+			next;
+
+# track the line number as we move through the hunk, note that
+# new versions of GNU diff omit the leading space on completely
+# blank context lines so we need to count that too.
+		} elsif ($line =~ /^( |\+|$)/) {
+			$realline++;
+			$realcnt-- if ($realcnt != 0);
+
+			# Measure the line length and indent.
+			($length, $indent) = line_stats($rawline);
+
+			# Track the previous line.
+			($prevline, $stashline) = ($stashline, $line);
+			($previndent, $stashindent) = ($stashindent, $indent);
+			($prevrawline, $stashrawline) = ($stashrawline, $rawline);
+
+			#warn "line<$line>\n";
+
+		} elsif ($realcnt == 1) {
+			$realcnt--;
+		}
+
+		my $hunk_line = ($realcnt != 0);
+
+		$here = "#$linenr: " if (!$file);
+		$here = "#$realline: " if ($file);
+
+		my $found_file = 0;
+		# extract the filename as it passes
+		if ($line =~ /^diff --git.*?(\S+)$/) {
+			$realfile = $1;
+			$realfile =~ s@^([^/]*)/@@ if (!$file);
+			$in_commit_log = 0;
+			$found_file = 1;
+		} elsif ($line =~ /^\+\+\+\s+(\S+)/) {
+			$realfile = $1;
+			$realfile =~ s@^([^/]*)/@@ if (!$file);
+			$in_commit_log = 0;
+
+			$p1_prefix = $1;
+			if (!$file && $tree && $p1_prefix ne '' &&
+			    -e "$root/$p1_prefix") {
+				WARN("PATCH_PREFIX",
+				     "patch prefix '$p1_prefix' exists, appears to be a -p0 patch\n");
+			}
+
+			if ($realfile =~ m@^include/asm/@) {
+				ERROR("MODIFIED_INCLUDE_ASM",
+				      "do not modify files in include/asm, change architecture specific files in include/asm-<architecture>\n" . "$here$rawline\n");
+			}
+			$found_file = 1;
+		}
+
+#make up the handle for any error we report on this line
+		if ($showfile) {
+			$prefix = "$realfile:$realline: "
+		} elsif ($emacs) {
+			if ($file) {
+				$prefix = "$filename:$realline: ";
+			} else {
+				$prefix = "$filename:$linenr: ";
+			}
+		}
+
+		if ($found_file) {
+			if (is_maintained_obsolete($realfile)) {
+				WARN("OBSOLETE",
+				     "$realfile is marked as 'obsolete' in the MAINTAINERS hierarchy.  No unnecessary modifications please.\n");
+			}
+			if ($realfile =~ m@^(?:drivers/net/|net/|drivers/staging/)@) {
+				$check = 1;
+			} else {
+				$check = $check_orig;
+			}
+			$checklicenseline = 1;
+
+			if ($realfile !~ /^MAINTAINERS/) {
+				my $last_binding_patch = $is_binding_patch;
+
+				$is_binding_patch = () = $realfile =~ m@^(?:Documentation/devicetree/|include/dt-bindings/)@;
+
+				if (($last_binding_patch != -1) &&
+				    ($last_binding_patch ^ $is_binding_patch)) {
+					WARN("DT_SPLIT_BINDING_PATCH",
+					     "DT binding docs and includes should be a separate patch. See: Documentation/devicetree/bindings/submitting-patches.rst\n");
+				}
+			}
+
+			next;
+		}
+
+		$here .= "FILE: $realfile:$realline:" if ($realcnt != 0);
+
+		my $hereline = "$here\n$rawline\n";
+		my $herecurr = "$here\n$rawline\n";
+		my $hereprev = "$here\n$prevrawline\n$rawline\n";
+
+		$cnt_lines++ if ($realcnt != 0);
+
+# Verify the existence of a commit log if appropriate
+# 2 is used because a $signature is counted in $commit_log_lines
+		if ($in_commit_log) {
+			if ($line !~ /^\s*$/) {
+				$commit_log_lines++;	#could be a $signature
+			}
+		} elsif ($has_commit_log && $commit_log_lines < 2) {
+			WARN("COMMIT_MESSAGE",
+			     "Missing commit description - Add an appropriate one\n");
+			$commit_log_lines = 2;	#warn only once
+		}
+
+# Check if the commit log has what seems like a diff which can confuse patch
+		if ($in_commit_log && !$commit_log_has_diff &&
+		    (($line =~ m@^\s+diff\b.*a/([\w/]+)@ &&
+		      $line =~ m@^\s+diff\b.*a/[\w/]+\s+b/$1\b@) ||
+		     $line =~ m@^\s*(?:\-\-\-\s+a/|\+\+\+\s+b/)@ ||
+		     $line =~ m/^\s*\@\@ \-\d+,\d+ \+\d+,\d+ \@\@/)) {
+			ERROR("DIFF_IN_COMMIT_MSG",
+			      "Avoid using diff content in the commit message - patch(1) might not work\n" . $herecurr);
+			$commit_log_has_diff = 1;
+		}
+
+# Check for incorrect file permissions
+		if ($line =~ /^new (file )?mode.*[7531]\d{0,2}$/) {
+			my $permhere = $here . "FILE: $realfile\n";
+			if ($realfile !~ m@scripts/@ &&
+			    $realfile !~ /\.(py|pl|awk|sh)$/) {
+				ERROR("EXECUTE_PERMISSIONS",
+				      "do not set execute permissions for source files\n" . $permhere);
+			}
+		}
+
+# Check the patch for a From:
+		if (decode("MIME-Header", $line) =~ /^From:\s*(.*)/) {
+			$author = $1;
+			my $curline = $linenr;
+			while(defined($rawlines[$curline]) && ($rawlines[$curline++] =~ /^[ \t]\s*(.*)/)) {
+				$author .= $1;
+			}
+			$author = encode("utf8", $author) if ($line =~ /=\?utf-8\?/i);
+			$author =~ s/"//g;
+			$author = reformat_email($author);
+		}
+
+# Check the patch for a signoff:
+		if ($line =~ /^\s*signed-off-by:\s*(.*)/i) {
+			$signoff++;
+			$in_commit_log = 0;
+			if ($author ne ''  && $authorsignoff != 1) {
+				if (same_email_addresses($1, $author)) {
+					$authorsignoff = 1;
+				} else {
+					my $ctx = $1;
+					my ($email_name, $email_comment, $email_address, $comment1) = parse_email($ctx);
+					my ($author_name, $author_comment, $author_address, $comment2) = parse_email($author);
+
+					if (lc $email_address eq lc $author_address && $email_name eq $author_name) {
+						$author_sob = $ctx;
+						$authorsignoff = 2;
+					} elsif (lc $email_address eq lc $author_address) {
+						$author_sob = $ctx;
+						$authorsignoff = 3;
+					} elsif ($email_name eq $author_name) {
+						$author_sob = $ctx;
+						$authorsignoff = 4;
+
+						my $address1 = $email_address;
+						my $address2 = $author_address;
+
+						if ($address1 =~ /(\S+)\+\S+(\@.*)/) {
+							$address1 = "$1$2";
+						}
+						if ($address2 =~ /(\S+)\+\S+(\@.*)/) {
+							$address2 = "$1$2";
+						}
+						if ($address1 eq $address2) {
+							$authorsignoff = 5;
+						}
+					}
+				}
+			}
+		}
+
+# Check for patch separator
+		if ($line =~ /^---$/) {
+			$has_patch_separator = 1;
+			$in_commit_log = 0;
+		}
+
+# Check if MAINTAINERS is being updated.  If so, there's probably no need to
+# emit the "does MAINTAINERS need updating?" message on file add/move/delete
+		if ($line =~ /^\s*MAINTAINERS\s*\|/) {
+			$reported_maintainer_file = 1;
+		}
+
+# Check signature styles
+		if (!$in_header_lines &&
+		    $line =~ /^(\s*)([a-z0-9_-]+by:|$signature_tags)(\s*)(.*)/i) {
+			my $space_before = $1;
+			my $sign_off = $2;
+			my $space_after = $3;
+			my $email = $4;
+			my $ucfirst_sign_off = ucfirst(lc($sign_off));
+
+			if ($sign_off !~ /$signature_tags/) {
+				my $suggested_signature = find_standard_signature($sign_off);
+				if ($suggested_signature eq "") {
+					WARN("BAD_SIGN_OFF",
+					     "Non-standard signature: $sign_off\n" . $herecurr);
+				} else {
+					if (WARN("BAD_SIGN_OFF",
+						 "Non-standard signature: '$sign_off' - perhaps '$suggested_signature'?\n" . $herecurr) &&
+					    $fix) {
+						$fixed[$fixlinenr] =~ s/$sign_off/$suggested_signature/;
+					}
+				}
+			}
+			if (defined $space_before && $space_before ne "") {
+				if (WARN("BAD_SIGN_OFF",
+					 "Do not use whitespace before $ucfirst_sign_off\n" . $herecurr) &&
+				    $fix) {
+					$fixed[$fixlinenr] =
+					    "$ucfirst_sign_off $email";
+				}
+			}
+			if ($sign_off =~ /-by:$/i && $sign_off ne $ucfirst_sign_off) {
+				if (WARN("BAD_SIGN_OFF",
+					 "'$ucfirst_sign_off' is the preferred signature form\n" . $herecurr) &&
+				    $fix) {
+					$fixed[$fixlinenr] =
+					    "$ucfirst_sign_off $email";
+				}
+
+			}
+			if (!defined $space_after || $space_after ne " ") {
+				if (WARN("BAD_SIGN_OFF",
+					 "Use a single space after $ucfirst_sign_off\n" . $herecurr) &&
+				    $fix) {
+					$fixed[$fixlinenr] =
+					    "$ucfirst_sign_off $email";
+				}
+			}
+
+			my ($email_name, $name_comment, $email_address, $comment) = parse_email($email);
+			my $suggested_email = format_email(($email_name, $name_comment, $email_address, $comment));
+			if ($suggested_email eq "") {
+				ERROR("BAD_SIGN_OFF",
+				      "Unrecognized email address: '$email'\n" . $herecurr);
+			} else {
+				my $dequoted = $suggested_email;
+				$dequoted =~ s/^"//;
+				$dequoted =~ s/" </ </;
+				# Don't force email to have quotes
+				# Allow just an angle bracketed address
+				if (!same_email_addresses($email, $suggested_email)) {
+					if (WARN("BAD_SIGN_OFF",
+						 "email address '$email' might be better as '$suggested_email'\n" . $herecurr) &&
+					    $fix) {
+						$fixed[$fixlinenr] =~ s/\Q$email\E/$suggested_email/;
+					}
+				}
+
+				# Address part shouldn't have comments
+				my $stripped_address = $email_address;
+				$stripped_address =~ s/\([^\(\)]*\)//g;
+				if ($email_address ne $stripped_address) {
+					if (WARN("BAD_SIGN_OFF",
+						 "address part of email should not have comments: '$email_address'\n" . $herecurr) &&
+					    $fix) {
+						$fixed[$fixlinenr] =~ s/\Q$email_address\E/$stripped_address/;
+					}
+				}
+
+				# Only one name comment should be allowed
+				my $comment_count = () = $name_comment =~ /\([^\)]+\)/g;
+				if ($comment_count > 1) {
+					WARN("BAD_SIGN_OFF",
+					     "Use a single name comment in email: '$email'\n" . $herecurr);
+				}
+
+
+				# stable@vger.kernel.org or stable@kernel.org shouldn't
+				# have an email name. In addition comments should strictly
+				# begin with a #
+				if ($email =~ /^.*stable\@(?:vger\.)?kernel\.org/i) {
+					if (($comment ne "" && $comment !~ /^#.+/) ||
+					    ($email_name ne "")) {
+						my $cur_name = $email_name;
+						my $new_comment = $comment;
+						$cur_name =~ s/[a-zA-Z\s\-\"]+//g;
+
+						# Remove brackets enclosing comment text
+						# and # from start of comments to get comment text
+						$new_comment =~ s/^\((.*)\)$/$1/;
+						$new_comment =~ s/^\[(.*)\]$/$1/;
+						$new_comment =~ s/^[\s\#]+|\s+$//g;
+
+						$new_comment = trim("$new_comment $cur_name") if ($cur_name ne $new_comment);
+						$new_comment = " # $new_comment" if ($new_comment ne "");
+						my $new_email = "$email_address$new_comment";
+
+						if (WARN("BAD_STABLE_ADDRESS_STYLE",
+							 "Invalid email format for stable: '$email', prefer '$new_email'\n" . $herecurr) &&
+						    $fix) {
+							$fixed[$fixlinenr] =~ s/\Q$email\E/$new_email/;
+						}
+					}
+				} elsif ($comment ne "" && $comment !~ /^(?:#.+|\(.+\))$/) {
+					my $new_comment = $comment;
+
+					# Extract comment text from within brackets or
+					# c89 style /*...*/ comments
+					$new_comment =~ s/^\[(.*)\]$/$1/;
+					$new_comment =~ s/^\/\*(.*)\*\/$/$1/;
+
+					$new_comment = trim($new_comment);
+					$new_comment =~ s/^[^\w]$//; # Single lettered comment with non word character is usually a typo
+					$new_comment = "($new_comment)" if ($new_comment ne "");
+					my $new_email = format_email($email_name, $name_comment, $email_address, $new_comment);
+
+					if (WARN("BAD_SIGN_OFF",
+						 "Unexpected content after email: '$email', should be: '$new_email'\n" . $herecurr) &&
+					    $fix) {
+						$fixed[$fixlinenr] =~ s/\Q$email\E/$new_email/;
+					}
+				}
+			}
+
+# Check for duplicate signatures
+			my $sig_nospace = $line;
+			$sig_nospace =~ s/\s//g;
+			$sig_nospace = lc($sig_nospace);
+			if (defined $signatures{$sig_nospace}) {
+				WARN("BAD_SIGN_OFF",
+				     "Duplicate signature\n" . $herecurr);
+			} else {
+				$signatures{$sig_nospace} = 1;
+			}
+
+# Check Co-developed-by: immediately followed by Signed-off-by: with same name and email
+			if ($sign_off =~ /^co-developed-by:$/i) {
+				if ($email eq $author) {
+					WARN("BAD_SIGN_OFF",
+					      "Co-developed-by: should not be used to attribute nominal patch author '$author'\n" . "$here\n" . $rawline);
+				}
+				if (!defined $lines[$linenr]) {
+					WARN("BAD_SIGN_OFF",
+					     "Co-developed-by: must be immediately followed by Signed-off-by:\n" . "$here\n" . $rawline);
+				} elsif ($rawlines[$linenr] !~ /^\s*signed-off-by:\s*(.*)/i) {
+					WARN("BAD_SIGN_OFF",
+					     "Co-developed-by: must be immediately followed by Signed-off-by:\n" . "$here\n" . $rawline . "\n" .$rawlines[$linenr]);
+				} elsif ($1 ne $email) {
+					WARN("BAD_SIGN_OFF",
+					     "Co-developed-by and Signed-off-by: name/email do not match \n" . "$here\n" . $rawline . "\n" .$rawlines[$linenr]);
+				}
+			}
+		}
+
+# Check email subject for common tools that don't need to be mentioned
+		if ($in_header_lines &&
+		    $line =~ /^Subject:.*\b(?:checkpatch|sparse|smatch)\b[^:]/i) {
+			WARN("EMAIL_SUBJECT",
+			     "A patch subject line should describe the change not the tool that found it\n" . $herecurr);
+		}
+
+# Check for Gerrit Change-Ids not in any patch context
+		if ($realfile eq '' && !$has_patch_separator && $line =~ /^\s*change-id:/i) {
+			if (ERROR("GERRIT_CHANGE_ID",
+			          "Remove Gerrit Change-Id's before submitting upstream\n" . $herecurr) &&
+			    $fix) {
+				fix_delete_line($fixlinenr, $rawline);
+			}
+		}
+
+# Check if the commit log is in a possible stack dump
+		if ($in_commit_log && !$commit_log_possible_stack_dump &&
+		    ($line =~ /^\s*(?:WARNING:|BUG:)/ ||
+		     $line =~ /^\s*\[\s*\d+\.\d{6,6}\s*\]/ ||
+					# timestamp
+		     $line =~ /^\s*\[\<[0-9a-fA-F]{8,}\>\]/) ||
+		     $line =~ /^(?:\s+\w+:\s+[0-9a-fA-F]+){3,3}/ ||
+		     $line =~ /^\s*\#\d+\s*\[[0-9a-fA-F]+\]\s*\w+ at [0-9a-fA-F]+/) {
+					# stack dump address styles
+			$commit_log_possible_stack_dump = 1;
+		}
+
+# Check for line lengths > 75 in commit log, warn once
+		if ($in_commit_log && !$commit_log_long_line &&
+		    length($line) > 75 &&
+		    !($line =~ /^\s*[a-zA-Z0-9_\/\.]+\s+\|\s+\d+/ ||
+					# file delta changes
+		      $line =~ /^\s*(?:[\w\.\-]+\/)++[\w\.\-]+:/ ||
+					# filename then :
+		      $line =~ /^\s*(?:Fixes:|Link:|$signature_tags)/i ||
+					# A Fixes: or Link: line or signature tag line
+		      $commit_log_possible_stack_dump)) {
+			WARN("COMMIT_LOG_LONG_LINE",
+			     "Possible unwrapped commit description (prefer a maximum 75 chars per line)\n" . $herecurr);
+			$commit_log_long_line = 1;
+		}
+
+# Reset possible stack dump if a blank line is found
+		if ($in_commit_log && $commit_log_possible_stack_dump &&
+		    $line =~ /^\s*$/) {
+			$commit_log_possible_stack_dump = 0;
+		}
+
+# Check for lines starting with a #
+		if ($in_commit_log && $line =~ /^#/) {
+			if (WARN("COMMIT_COMMENT_SYMBOL",
+				 "Commit log lines starting with '#' are dropped by git as comments\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/^/ /;
+			}
+		}
+
+# Check for git id commit length and improperly formed commit descriptions
+# A correctly formed commit description is:
+#    commit <SHA-1 hash length 12+ chars> ("Complete commit subject")
+# with the commit subject '("' prefix and '")' suffix
+# This is a fairly compilicated block as it tests for what appears to be
+# bare SHA-1 hash with  minimum length of 5.  It also avoids several types of
+# possible SHA-1 matches.
+# A commit match can span multiple lines so this block attempts to find a
+# complete typical commit on a maximum of 3 lines
+		if ($perl_version_ok &&
+		    $in_commit_log && !$commit_log_possible_stack_dump &&
+		    $line !~ /^\s*(?:Link|Patchwork|http|https|BugLink|base-commit):/i &&
+		    $line !~ /^This reverts commit [0-9a-f]{7,40}/ &&
+		    (($line =~ /\bcommit\s+[0-9a-f]{5,}\b/i ||
+		      ($line =~ /\bcommit\s*$/i && defined($rawlines[$linenr]) && $rawlines[$linenr] =~ /^\s*[0-9a-f]{5,}\b/i)) ||
+		     ($line =~ /(?:\s|^)[0-9a-f]{12,40}(?:[\s"'\(\[]|$)/i &&
+		      $line !~ /[\<\[][0-9a-f]{12,40}[\>\]]/i &&
+		      $line !~ /\bfixes:\s*[0-9a-f]{12,40}/i))) {
+			my $init_char = "c";
+			my $orig_commit = "";
+			my $short = 1;
+			my $long = 0;
+			my $case = 1;
+			my $space = 1;
+			my $id = '0123456789ab';
+			my $orig_desc = "commit description";
+			my $description = "";
+			my $herectx = $herecurr;
+			my $has_parens = 0;
+			my $has_quotes = 0;
+
+			my $input = $line;
+			if ($line =~ /(?:\bcommit\s+[0-9a-f]{5,}|\bcommit\s*$)/i) {
+				for (my $n = 0; $n < 2; $n++) {
+					if ($input =~ /\bcommit\s+[0-9a-f]{5,}\s*($balanced_parens)/i) {
+						$orig_desc = $1;
+						$has_parens = 1;
+						# Always strip leading/trailing parens then double quotes if existing
+						$orig_desc = substr($orig_desc, 1, -1);
+						if ($orig_desc =~ /^".*"$/) {
+							$orig_desc = substr($orig_desc, 1, -1);
+							$has_quotes = 1;
+						}
+						last;
+					}
+					last if ($#lines < $linenr + $n);
+					$input .= " " . trim($rawlines[$linenr + $n]);
+					$herectx .= "$rawlines[$linenr + $n]\n";
+				}
+				$herectx = $herecurr if (!$has_parens);
+			}
+
+			if ($input =~ /\b(c)ommit\s+([0-9a-f]{5,})\b/i) {
+				$init_char = $1;
+				$orig_commit = lc($2);
+				$short = 0 if ($input =~ /\bcommit\s+[0-9a-f]{12,40}/i);
+				$long = 1 if ($input =~ /\bcommit\s+[0-9a-f]{41,}/i);
+				$space = 0 if ($input =~ /\bcommit [0-9a-f]/i);
+				$case = 0 if ($input =~ /\b[Cc]ommit\s+[0-9a-f]{5,40}[^A-F]/);
+			} elsif ($input =~ /\b([0-9a-f]{12,40})\b/i) {
+				$orig_commit = lc($1);
+			}
+
+			($id, $description) = git_commit_info($orig_commit,
+							      $id, $orig_desc);
+
+			if (defined($id) &&
+			    ($short || $long || $space || $case || ($orig_desc ne $description) || !$has_quotes) &&
+			    $last_git_commit_id_linenr != $linenr - 1) {
+				ERROR("GIT_COMMIT_ID",
+				      "Please use git commit description style 'commit <12+ chars of sha1> (\"<title line>\")' - ie: '${init_char}ommit $id (\"$description\")'\n" . $herectx);
+			}
+			#don't report the next line if this line ends in commit and the sha1 hash is the next line
+			$last_git_commit_id_linenr = $linenr if ($line =~ /\bcommit\s*$/i);
+		}
+
+# Check for added, moved or deleted files
+		if (!$reported_maintainer_file && !$in_commit_log &&
+		    ($line =~ /^(?:new|deleted) file mode\s*\d+\s*$/ ||
+		     $line =~ /^rename (?:from|to) [\w\/\.\-]+\s*$/ ||
+		     ($line =~ /\{\s*([\w\/\.\-]*)\s*\=\>\s*([\w\/\.\-]*)\s*\}/ &&
+		      (defined($1) || defined($2))))) {
+			$is_patch = 1;
+			$reported_maintainer_file = 1;
+			WARN("FILE_PATH_CHANGES",
+			     "added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr);
+		}
+
+# Check for adding new DT bindings not in schema format
+		if (!$in_commit_log &&
+		    ($line =~ /^new file mode\s*\d+\s*$/) &&
+		    ($realfile =~ m@^Documentation/devicetree/bindings/.*\.txt$@)) {
+			WARN("DT_SCHEMA_BINDING_PATCH",
+			     "DT bindings should be in DT schema format. See: Documentation/devicetree/bindings/writing-schema.rst\n");
+		}
+
+# Check for wrappage within a valid hunk of the file
+		if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) {
+			ERROR("CORRUPTED_PATCH",
+			      "patch seems to be corrupt (line wrapped?)\n" .
+				$herecurr) if (!$emitted_corrupt++);
+		}
+
+# UTF-8 regex found at http://www.w3.org/International/questions/qa-forms-utf-8.en.php
+		if (($realfile =~ /^$/ || $line =~ /^\+/) &&
+		    $rawline !~ m/^$UTF8*$/) {
+			my ($utf8_prefix) = ($rawline =~ /^($UTF8*)/);
+
+			my $blank = copy_spacing($rawline);
+			my $ptr = substr($blank, 0, length($utf8_prefix)) . "^";
+			my $hereptr = "$hereline$ptr\n";
+
+			CHK("INVALID_UTF8",
+			    "Invalid UTF-8, patch and commit message should be encoded in UTF-8\n" . $hereptr);
+		}
+
+# Check if it's the start of a commit log
+# (not a header line and we haven't seen the patch filename)
+		if ($in_header_lines && $realfile =~ /^$/ &&
+		    !($rawline =~ /^\s+(?:\S|$)/ ||
+		      $rawline =~ /^(?:commit\b|from\b|[\w-]+:)/i)) {
+			$in_header_lines = 0;
+			$in_commit_log = 1;
+			$has_commit_log = 1;
+		}
+
+# Check if there is UTF-8 in a commit log when a mail header has explicitly
+# declined it, i.e defined some charset where it is missing.
+		if ($in_header_lines &&
+		    $rawline =~ /^Content-Type:.+charset="(.+)".*$/ &&
+		    $1 !~ /utf-8/i) {
+			$non_utf8_charset = 1;
+		}
+
+		if ($in_commit_log && $non_utf8_charset && $realfile =~ /^$/ &&
+		    $rawline =~ /$NON_ASCII_UTF8/) {
+			WARN("UTF8_BEFORE_PATCH",
+			    "8-bit UTF-8 used in possible commit log\n" . $herecurr);
+		}
+
+# Check for absolute kernel paths in commit message
+		if ($tree && $in_commit_log) {
+			while ($line =~ m{(?:^|\s)(/\S*)}g) {
+				my $file = $1;
+
+				if ($file =~ m{^(.*?)(?::\d+)+:?$} &&
+				    check_absolute_file($1, $herecurr)) {
+					#
+				} else {
+					check_absolute_file($file, $herecurr);
+				}
+			}
+		}
+
+# Check for various typo / spelling mistakes
+		if (defined($misspellings) &&
+		    ($in_commit_log || $line =~ /^(?:\+|Subject:)/i)) {
+			while ($rawline =~ /(?:^|[^\w\-'`])($misspellings)(?:[^\w\-'`]|$)/gi) {
+				my $typo = $1;
+				my $blank = copy_spacing($rawline);
+				my $ptr = substr($blank, 0, $-[1]) . "^" x length($typo);
+				my $hereptr = "$hereline$ptr\n";
+				my $typo_fix = $spelling_fix{lc($typo)};
+				$typo_fix = ucfirst($typo_fix) if ($typo =~ /^[A-Z]/);
+				$typo_fix = uc($typo_fix) if ($typo =~ /^[A-Z]+$/);
+				my $msg_level = \&WARN;
+				$msg_level = \&CHK if ($file);
+				if (&{$msg_level}("TYPO_SPELLING",
+						  "'$typo' may be misspelled - perhaps '$typo_fix'?\n" . $hereptr) &&
+				    $fix) {
+					$fixed[$fixlinenr] =~ s/(^|[^A-Za-z@])($typo)($|[^A-Za-z@])/$1$typo_fix$3/;
+				}
+			}
+		}
+
+# check for invalid commit id
+		if ($in_commit_log && $line =~ /(^fixes:|\bcommit)\s+([0-9a-f]{6,40})\b/i) {
+			my $id;
+			my $description;
+			($id, $description) = git_commit_info($2, undef, undef);
+			if (!defined($id)) {
+				WARN("UNKNOWN_COMMIT_ID",
+				     "Unknown commit id '$2', maybe rebased or not pulled?\n" . $herecurr);
+			}
+		}
+
+# check for repeated words separated by a single space
+# avoid false positive from list command eg, '-rw-r--r-- 1 root root'
+		if (($rawline =~ /^\+/ || $in_commit_log) &&
+		    $rawline !~ /[bcCdDlMnpPs\?-][rwxsStT-]{9}/) {
+			pos($rawline) = 1 if (!$in_commit_log);
+			while ($rawline =~ /\b($word_pattern) (?=($word_pattern))/g) {
+
+				my $first = $1;
+				my $second = $2;
+				my $start_pos = $-[1];
+				my $end_pos = $+[2];
+				if ($first =~ /(?:struct|union|enum)/) {
+					pos($rawline) += length($first) + length($second) + 1;
+					next;
+				}
+
+				next if (lc($first) ne lc($second));
+				next if ($first eq 'long');
+
+				# check for character before and after the word matches
+				my $start_char = '';
+				my $end_char = '';
+				$start_char = substr($rawline, $start_pos - 1, 1) if ($start_pos > ($in_commit_log ? 0 : 1));
+				$end_char = substr($rawline, $end_pos, 1) if ($end_pos < length($rawline));
+
+				next if ($start_char =~ /^\S$/);
+				next if (index(" \t.,;?!", $end_char) == -1);
+
+				# avoid repeating hex occurrences like 'ff ff fe 09 ...'
+				if ($first =~ /\b[0-9a-f]{2,}\b/i) {
+					next if (!exists($allow_repeated_words{lc($first)}));
+				}
+
+				if (WARN("REPEATED_WORD",
+					 "Possible repeated word: '$first'\n" . $herecurr) &&
+				    $fix) {
+					$fixed[$fixlinenr] =~ s/\b$first $second\b/$first/;
+				}
+			}
+
+			# if it's a repeated word on consecutive lines in a comment block
+			if ($prevline =~ /$;+\s*$/ &&
+			    $prevrawline =~ /($word_pattern)\s*$/) {
+				my $last_word = $1;
+				if ($rawline =~ /^\+\s*\*\s*$last_word /) {
+					if (WARN("REPEATED_WORD",
+						 "Possible repeated word: '$last_word'\n" . $hereprev) &&
+					    $fix) {
+						$fixed[$fixlinenr] =~ s/(\+\s*\*\s*)$last_word /$1/;
+					}
+				}
+			}
+		}
+
+# ignore non-hunk lines and lines being removed
+		next if (!$hunk_line || $line =~ /^-/);
+
+#trailing whitespace
+		if ($line =~ /^\+.*\015/) {
+			my $herevet = "$here\n" . cat_vet($rawline) . "\n";
+			if (ERROR("DOS_LINE_ENDINGS",
+				  "DOS line endings\n" . $herevet) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/[\s\015]+$//;
+			}
+		} elsif ($rawline =~ /^\+.*\S\s+$/ || $rawline =~ /^\+\s+$/) {
+			my $herevet = "$here\n" . cat_vet($rawline) . "\n";
+			if (ERROR("TRAILING_WHITESPACE",
+				  "trailing whitespace\n" . $herevet) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\s+$//;
+			}
+
+			$rpt_cleaners = 1;
+		}
+
+# Check for FSF mailing addresses.
+		if ($rawline =~ /\bwrite to the Free/i ||
+		    $rawline =~ /\b675\s+Mass\s+Ave/i ||
+		    $rawline =~ /\b59\s+Temple\s+Pl/i ||
+		    $rawline =~ /\b51\s+Franklin\s+St/i) {
+			my $herevet = "$here\n" . cat_vet($rawline) . "\n";
+			my $msg_level = \&ERROR;
+			$msg_level = \&CHK if ($file);
+			&{$msg_level}("FSF_MAILING_ADDRESS",
+				      "Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. Linux already includes a copy of the GPL.\n" . $herevet)
+		}
+
+# check for Kconfig help text having a real description
+# Only applies when adding the entry originally, after that we do not have
+# sufficient context to determine whether it is indeed long enough.
+		if ($realfile =~ /Kconfig/ &&
+		    # 'choice' is usually the last thing on the line (though
+		    # Kconfig supports named choices), so use a word boundary
+		    # (\b) rather than a whitespace character (\s)
+		    $line =~ /^\+\s*(?:config|menuconfig|choice)\b/) {
+			my $length = 0;
+			my $cnt = $realcnt;
+			my $ln = $linenr + 1;
+			my $f;
+			my $is_start = 0;
+			my $is_end = 0;
+			for (; $cnt > 0 && defined $lines[$ln - 1]; $ln++) {
+				$f = $lines[$ln - 1];
+				$cnt-- if ($lines[$ln - 1] !~ /^-/);
+				$is_end = $lines[$ln - 1] =~ /^\+/;
+
+				next if ($f =~ /^-/);
+				last if (!$file && $f =~ /^\@\@/);
+
+				if ($lines[$ln - 1] =~ /^\+\s*(?:bool|tristate|prompt)\s*["']/) {
+					$is_start = 1;
+				} elsif ($lines[$ln - 1] =~ /^\+\s*(?:---)?help(?:---)?$/) {
+					$length = -1;
+				}
+
+				$f =~ s/^.//;
+				$f =~ s/#.*//;
+				$f =~ s/^\s+//;
+				next if ($f =~ /^$/);
+
+				# This only checks context lines in the patch
+				# and so hopefully shouldn't trigger false
+				# positives, even though some of these are
+				# common words in help texts
+				if ($f =~ /^\s*(?:config|menuconfig|choice|endchoice|
+						  if|endif|menu|endmenu|source)\b/x) {
+					$is_end = 1;
+					last;
+				}
+				$length++;
+			}
+			if ($is_start && $is_end && $length < $min_conf_desc_length) {
+				WARN("CONFIG_DESCRIPTION",
+				     "please write a paragraph that describes the config symbol fully\n" . $herecurr);
+			}
+			#print "is_start<$is_start> is_end<$is_end> length<$length>\n";
+		}
+
+# check MAINTAINERS entries
+		if ($realfile =~ /^MAINTAINERS$/) {
+# check MAINTAINERS entries for the right form
+			if ($rawline =~ /^\+[A-Z]:/ &&
+			    $rawline !~ /^\+[A-Z]:\t\S/) {
+				if (WARN("MAINTAINERS_STYLE",
+					 "MAINTAINERS entries use one tab after TYPE:\n" . $herecurr) &&
+				    $fix) {
+					$fixed[$fixlinenr] =~ s/^(\+[A-Z]):\s*/$1:\t/;
+				}
+			}
+# check MAINTAINERS entries for the right ordering too
+			my $preferred_order = 'MRLSWQBCPTFXNK';
+			if ($rawline =~ /^\+[A-Z]:/ &&
+			    $prevrawline =~ /^[\+ ][A-Z]:/) {
+				$rawline =~ /^\+([A-Z]):\s*(.*)/;
+				my $cur = $1;
+				my $curval = $2;
+				$prevrawline =~ /^[\+ ]([A-Z]):\s*(.*)/;
+				my $prev = $1;
+				my $prevval = $2;
+				my $curindex = index($preferred_order, $cur);
+				my $previndex = index($preferred_order, $prev);
+				if ($curindex < 0) {
+					WARN("MAINTAINERS_STYLE",
+					     "Unknown MAINTAINERS entry type: '$cur'\n" . $herecurr);
+				} else {
+					if ($previndex >= 0 && $curindex < $previndex) {
+						WARN("MAINTAINERS_STYLE",
+						     "Misordered MAINTAINERS entry - list '$cur:' before '$prev:'\n" . $hereprev);
+					} elsif ((($prev eq 'F' && $cur eq 'F') ||
+						  ($prev eq 'X' && $cur eq 'X')) &&
+						 ($prevval cmp $curval) > 0) {
+						WARN("MAINTAINERS_STYLE",
+						     "Misordered MAINTAINERS entry - list file patterns in alphabetic order\n" . $hereprev);
+					}
+				}
+			}
+		}
+
+		if (($realfile =~ /Makefile.*/ || $realfile =~ /Kbuild.*/) &&
+		    ($line =~ /\+(EXTRA_[A-Z]+FLAGS).*/)) {
+			my $flag = $1;
+			my $replacement = {
+				'EXTRA_AFLAGS' =>   'asflags-y',
+				'EXTRA_CFLAGS' =>   'ccflags-y',
+				'EXTRA_CPPFLAGS' => 'cppflags-y',
+				'EXTRA_LDFLAGS' =>  'ldflags-y',
+			};
+
+			WARN("DEPRECATED_VARIABLE",
+			     "Use of $flag is deprecated, please use \`$replacement->{$flag} instead.\n" . $herecurr) if ($replacement->{$flag});
+		}
+
+# check for DT compatible documentation
+		if (defined $root &&
+			(($realfile =~ /\.dtsi?$/ && $line =~ /^\+\s*compatible\s*=\s*\"/) ||
+			 ($realfile =~ /\.[ch]$/ && $line =~ /^\+.*\.compatible\s*=\s*\"/))) {
+
+			my @compats = $rawline =~ /\"([a-zA-Z0-9\-\,\.\+_]+)\"/g;
+
+			my $dt_path = $root . "/Documentation/devicetree/bindings/";
+			my $vp_file = $dt_path . "vendor-prefixes.yaml";
+
+			foreach my $compat (@compats) {
+				my $compat2 = $compat;
+				$compat2 =~ s/\,[a-zA-Z0-9]*\-/\,<\.\*>\-/;
+				my $compat3 = $compat;
+				$compat3 =~ s/\,([a-z]*)[0-9]*\-/\,$1<\.\*>\-/;
+				`grep -Erq "$compat|$compat2|$compat3" $dt_path`;
+				if ( $? >> 8 ) {
+					WARN("UNDOCUMENTED_DT_STRING",
+					     "DT compatible string \"$compat\" appears un-documented -- check $dt_path\n" . $herecurr);
+				}
+
+				next if $compat !~ /^([a-zA-Z0-9\-]+)\,/;
+				my $vendor = $1;
+				`grep -Eq "\\"\\^\Q$vendor\E,\\.\\*\\":" $vp_file`;
+				if ( $? >> 8 ) {
+					WARN("UNDOCUMENTED_DT_STRING",
+					     "DT compatible string vendor \"$vendor\" appears un-documented -- check $vp_file\n" . $herecurr);
+				}
+			}
+		}
+
+# check for using SPDX license tag at beginning of files
+		if ($realline == $checklicenseline) {
+			if ($rawline =~ /^[ \+]\s*\#\!\s*\//) {
+				$checklicenseline = 2;
+			} elsif ($rawline =~ /^\+/) {
+				my $comment = "";
+				if ($realfile =~ /\.(h|s|S)$/) {
+					$comment = '/*';
+				} elsif ($realfile =~ /\.(c|dts|dtsi)$/) {
+					$comment = '//';
+				} elsif (($checklicenseline == 2) || $realfile =~ /\.(sh|pl|py|awk|tc|yaml)$/) {
+					$comment = '#';
+				} elsif ($realfile =~ /\.rst$/) {
+					$comment = '..';
+				}
+
+# check SPDX comment style for .[chsS] files
+				if ($realfile =~ /\.[chsS]$/ &&
+				    $rawline =~ /SPDX-License-Identifier:/ &&
+				    $rawline !~ m@^\+\s*\Q$comment\E\s*@) {
+					WARN("SPDX_LICENSE_TAG",
+					     "Improper SPDX comment style for '$realfile', please use '$comment' instead\n" . $herecurr);
+				}
+
+				if ($comment !~ /^$/ &&
+				    $rawline !~ m@^\+\Q$comment\E SPDX-License-Identifier: @) {
+					WARN("SPDX_LICENSE_TAG",
+					     "Missing or malformed SPDX-License-Identifier tag in line $checklicenseline\n" . $herecurr);
+				} elsif ($rawline =~ /(SPDX-License-Identifier: .*)/) {
+					my $spdx_license = $1;
+					if (!is_SPDX_License_valid($spdx_license)) {
+						WARN("SPDX_LICENSE_TAG",
+						     "'$spdx_license' is not supported in LICENSES/...\n" . $herecurr);
+					}
+					if ($realfile =~ m@^Documentation/devicetree/bindings/@ &&
+					    not $spdx_license =~ /GPL-2\.0.*BSD-2-Clause/) {
+						my $msg_level = \&WARN;
+						$msg_level = \&CHK if ($file);
+						if (&{$msg_level}("SPDX_LICENSE_TAG",
+
+								  "DT binding documents should be licensed (GPL-2.0-only OR BSD-2-Clause)\n" . $herecurr) &&
+						    $fix) {
+							$fixed[$fixlinenr] =~ s/SPDX-License-Identifier: .*/SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)/;
+						}
+					}
+				}
+			}
+		}
+
+# check for embedded filenames
+		if ($rawline =~ /^\+.*\Q$realfile\E/) {
+			WARN("EMBEDDED_FILENAME",
+			     "It's generally not useful to have the filename in the file\n" . $herecurr);
+		}
+
+# check we are in a valid source file if not then ignore this hunk
+		next if ($realfile !~ /\.(h|c|s|S|sh|dtsi|dts)$/);
+
+# check for using SPDX-License-Identifier on the wrong line number
+		if ($realline != $checklicenseline &&
+		    $rawline =~ /\bSPDX-License-Identifier:/ &&
+		    substr($line, @-, @+ - @-) eq "$;" x (@+ - @-)) {
+			WARN("SPDX_LICENSE_TAG",
+			     "Misplaced SPDX-License-Identifier tag - use line $checklicenseline instead\n" . $herecurr);
+		}
+
+# line length limit (with some exclusions)
+#
+# There are a few types of lines that may extend beyond $max_line_length:
+#	logging functions like pr_info that end in a string
+#	lines with a single string
+#	#defines that are a single string
+#	lines with an RFC3986 like URL
+#
+# There are 3 different line length message types:
+# LONG_LINE_COMMENT	a comment starts before but extends beyond $max_line_length
+# LONG_LINE_STRING	a string starts before but extends beyond $max_line_length
+# LONG_LINE		all other lines longer than $max_line_length
+#
+# if LONG_LINE is ignored, the other 2 types are also ignored
+#
+
+		if ($line =~ /^\+/ && $length > $max_line_length) {
+			my $msg_type = "LONG_LINE";
+
+			# Check the allowed long line types first
+
+			# logging functions that end in a string that starts
+			# before $max_line_length
+			if ($line =~ /^\+\s*$logFunctions\s*\(\s*(?:(?:KERN_\S+\s*|[^"]*))?($String\s*(?:|,|\)\s*;)\s*)$/ &&
+			    length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) {
+				$msg_type = "";
+
+			# lines with only strings (w/ possible termination)
+			# #defines with only strings
+			} elsif ($line =~ /^\+\s*$String\s*(?:\s*|,|\)\s*;)\s*$/ ||
+				 $line =~ /^\+\s*#\s*define\s+\w+\s+$String$/) {
+				$msg_type = "";
+
+			# More special cases
+			} elsif ($line =~ /^\+.*\bEFI_GUID\s*\(/ ||
+				 $line =~ /^\+\s*(?:\w+)?\s*DEFINE_PER_CPU/) {
+				$msg_type = "";
+
+			# URL ($rawline is used in case the URL is in a comment)
+			} elsif ($rawline =~ /^\+.*\b[a-z][\w\.\+\-]*:\/\/\S+/i) {
+				$msg_type = "";
+
+			# Otherwise set the alternate message types
+
+			# a comment starts before $max_line_length
+			} elsif ($line =~ /($;[\s$;]*)$/ &&
+				 length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) {
+				$msg_type = "LONG_LINE_COMMENT"
+
+			# a quoted string starts before $max_line_length
+			} elsif ($sline =~ /\s*($String(?:\s*(?:\\|,\s*|\)\s*;\s*))?)$/ &&
+				 length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) {
+				$msg_type = "LONG_LINE_STRING"
+			}
+
+			if ($msg_type ne "" &&
+			    (show_type("LONG_LINE") || show_type($msg_type))) {
+				my $msg_level = \&WARN;
+				$msg_level = \&CHK if ($file);
+				&{$msg_level}($msg_type,
+					      "line length of $length exceeds $max_line_length columns\n" . $herecurr);
+			}
+		}
+
+# check for adding lines without a newline.
+		if ($line =~ /^\+/ && defined $lines[$linenr] && $lines[$linenr] =~ /^\\ No newline at end of file/) {
+			if (WARN("MISSING_EOF_NEWLINE",
+			         "adding a line without newline at end of file\n" . $herecurr) &&
+			    $fix) {
+				fix_delete_line($fixlinenr+1, "No newline at end of file");
+			}
+		}
+
+# check for .L prefix local symbols in .S files
+		if ($realfile =~ /\.S$/ &&
+		    $line =~ /^\+\s*(?:[A-Z]+_)?SYM_[A-Z]+_(?:START|END)(?:_[A-Z_]+)?\s*\(\s*\.L/) {
+			WARN("AVOID_L_PREFIX",
+			     "Avoid using '.L' prefixed local symbol names for denoting a range of code via 'SYM_*_START/END' annotations; see Documentation/asm-annotations.rst\n" . $herecurr);
+		}
+
+# check we are in a valid source file C or perl if not then ignore this hunk
+		next if ($realfile !~ /\.(h|c|pl|dtsi|dts)$/);
+
+# at the beginning of a line any tabs must come first and anything
+# more than $tabsize must use tabs.
+		if ($rawline =~ /^\+\s* \t\s*\S/ ||
+		    $rawline =~ /^\+\s*        \s*/) {
+			my $herevet = "$here\n" . cat_vet($rawline) . "\n";
+			$rpt_cleaners = 1;
+			if (ERROR("CODE_INDENT",
+				  "code indent should use tabs where possible\n" . $herevet) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/^\+([ \t]+)/"\+" . tabify($1)/e;
+			}
+		}
+
+# check for space before tabs.
+		if ($rawline =~ /^\+/ && $rawline =~ / \t/) {
+			my $herevet = "$here\n" . cat_vet($rawline) . "\n";
+			if (WARN("SPACE_BEFORE_TAB",
+				"please, no space before tabs\n" . $herevet) &&
+			    $fix) {
+				while ($fixed[$fixlinenr] =~
+					   s/(^\+.*) {$tabsize,$tabsize}\t/$1\t\t/) {}
+				while ($fixed[$fixlinenr] =~
+					   s/(^\+.*) +\t/$1\t/) {}
+			}
+		}
+
+# check for assignments on the start of a line
+		if ($sline =~ /^\+\s+($Assignment)[^=]/) {
+			my $operator = $1;
+			if (CHK("ASSIGNMENT_CONTINUATIONS",
+				"Assignment operator '$1' should be on the previous line\n" . $hereprev) &&
+			    $fix && $prevrawline =~ /^\+/) {
+				# add assignment operator to the previous line, remove from current line
+				$fixed[$fixlinenr - 1] .= " $operator";
+				$fixed[$fixlinenr] =~ s/\Q$operator\E\s*//;
+			}
+		}
+
+# check for && or || at the start of a line
+		if ($rawline =~ /^\+\s*(&&|\|\|)/) {
+			my $operator = $1;
+			if (CHK("LOGICAL_CONTINUATIONS",
+				"Logical continuations should be on the previous line\n" . $hereprev) &&
+			    $fix && $prevrawline =~ /^\+/) {
+				# insert logical operator at last non-comment, non-whitepsace char on previous line
+				$prevline =~ /[\s$;]*$/;
+				my $line_end = substr($prevrawline, $-[0]);
+				$fixed[$fixlinenr - 1] =~ s/\Q$line_end\E$/ $operator$line_end/;
+				$fixed[$fixlinenr] =~ s/\Q$operator\E\s*//;
+			}
+		}
+
+# check indentation starts on a tab stop
+		if ($perl_version_ok &&
+		    $sline =~ /^\+\t+( +)(?:$c90_Keywords\b|\{\s*$|\}\s*(?:else\b|while\b|\s*$)|$Declare\s*$Ident\s*[;=])/) {
+			my $indent = length($1);
+			if ($indent % $tabsize) {
+				if (WARN("TABSTOP",
+					 "Statements should start on a tabstop\n" . $herecurr) &&
+				    $fix) {
+					$fixed[$fixlinenr] =~ s@(^\+\t+) +@$1 . "\t" x ($indent/$tabsize)@e;
+				}
+			}
+		}
+
+# check multi-line statement indentation matches previous line
+		if ($perl_version_ok &&
+		    $prevline =~ /^\+([ \t]*)((?:$c90_Keywords(?:\s+if)\s*)|(?:$Declare\s*)?(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*|(?:\*\s*)*$Lval\s*=\s*$Ident\s*)\(.*(\&\&|\|\||,)\s*$/) {
+			$prevline =~ /^\+(\t*)(.*)$/;
+			my $oldindent = $1;
+			my $rest = $2;
+
+			my $pos = pos_last_openparen($rest);
+			if ($pos >= 0) {
+				$line =~ /^(\+| )([ \t]*)/;
+				my $newindent = $2;
+
+				my $goodtabindent = $oldindent .
+					"\t" x ($pos / $tabsize) .
+					" "  x ($pos % $tabsize);
+				my $goodspaceindent = $oldindent . " "  x $pos;
+
+				if ($newindent ne $goodtabindent &&
+				    $newindent ne $goodspaceindent) {
+
+					if (CHK("PARENTHESIS_ALIGNMENT",
+						"Alignment should match open parenthesis\n" . $hereprev) &&
+					    $fix && $line =~ /^\+/) {
+						$fixed[$fixlinenr] =~
+						    s/^\+[ \t]*/\+$goodtabindent/;
+					}
+				}
+			}
+		}
+
+# check for space after cast like "(int) foo" or "(struct foo) bar"
+# avoid checking a few false positives:
+#   "sizeof(<type>)" or "__alignof__(<type>)"
+#   function pointer declarations like "(*foo)(int) = bar;"
+#   structure definitions like "(struct foo) { 0 };"
+#   multiline macros that define functions
+#   known attributes or the __attribute__ keyword
+		if ($line =~ /^\+(.*)\(\s*$Type\s*\)([ \t]++)((?![={]|\\$|$Attribute|__attribute__))/ &&
+		    (!defined($1) || $1 !~ /\b(?:sizeof|__alignof__)\s*$/)) {
+			if (CHK("SPACING",
+				"No space is necessary after a cast\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~
+				    s/(\(\s*$Type\s*\))[ \t]+/$1/;
+			}
+		}
+
+# Block comment styles
+# Networking with an initial /*
+		if ($realfile =~ m@^(drivers/net/|net/)@ &&
+		    $prevrawline =~ /^\+[ \t]*\/\*[ \t]*$/ &&
+		    $rawline =~ /^\+[ \t]*\*/ &&
+		    $realline > 3) { # Do not warn about the initial copyright comment block after SPDX-License-Identifier
+			WARN("NETWORKING_BLOCK_COMMENT_STYLE",
+			     "networking block comments don't use an empty /* line, use /* Comment...\n" . $hereprev);
+		}
+
+# Block comments use * on subsequent lines
+		if ($prevline =~ /$;[ \t]*$/ &&			#ends in comment
+		    $prevrawline =~ /^\+.*?\/\*/ &&		#starting /*
+		    $prevrawline !~ /\*\/[ \t]*$/ &&		#no trailing */
+		    $rawline =~ /^\+/ &&			#line is new
+		    $rawline !~ /^\+[ \t]*\*/) {		#no leading *
+			WARN("BLOCK_COMMENT_STYLE",
+			     "Block comments use * on subsequent lines\n" . $hereprev);
+		}
+
+# Block comments use */ on trailing lines
+		if ($rawline !~ m@^\+[ \t]*\*/[ \t]*$@ &&	#trailing */
+		    $rawline !~ m@^\+.*/\*.*\*/[ \t]*$@ &&	#inline /*...*/
+		    $rawline !~ m@^\+.*\*{2,}/[ \t]*$@ &&	#trailing **/
+		    $rawline =~ m@^\+[ \t]*.+\*\/[ \t]*$@) {	#non blank */
+			WARN("BLOCK_COMMENT_STYLE",
+			     "Block comments use a trailing */ on a separate line\n" . $herecurr);
+		}
+
+# Block comment * alignment
+		if ($prevline =~ /$;[ \t]*$/ &&			#ends in comment
+		    $line =~ /^\+[ \t]*$;/ &&			#leading comment
+		    $rawline =~ /^\+[ \t]*\*/ &&		#leading *
+		    (($prevrawline =~ /^\+.*?\/\*/ &&		#leading /*
+		      $prevrawline !~ /\*\/[ \t]*$/) ||		#no trailing */
+		     $prevrawline =~ /^\+[ \t]*\*/)) {		#leading *
+			my $oldindent;
+			$prevrawline =~ m@^\+([ \t]*/?)\*@;
+			if (defined($1)) {
+				$oldindent = expand_tabs($1);
+			} else {
+				$prevrawline =~ m@^\+(.*/?)\*@;
+				$oldindent = expand_tabs($1);
+			}
+			$rawline =~ m@^\+([ \t]*)\*@;
+			my $newindent = $1;
+			$newindent = expand_tabs($newindent);
+			if (length($oldindent) ne length($newindent)) {
+				WARN("BLOCK_COMMENT_STYLE",
+				     "Block comments should align the * on each line\n" . $hereprev);
+			}
+		}
+
+# check for missing blank lines after struct/union declarations
+# with exceptions for various attributes and macros
+		if ($prevline =~ /^[\+ ]};?\s*$/ &&
+		    $line =~ /^\+/ &&
+		    !($line =~ /^\+\s*$/ ||
+		      $line =~ /^\+\s*EXPORT_SYMBOL/ ||
+		      $line =~ /^\+\s*MODULE_/i ||
+		      $line =~ /^\+\s*\#\s*(?:end|elif|else)/ ||
+		      $line =~ /^\+[a-z_]*init/ ||
+		      $line =~ /^\+\s*(?:static\s+)?[A-Z_]*ATTR/ ||
+		      $line =~ /^\+\s*DECLARE/ ||
+		      $line =~ /^\+\s*builtin_[\w_]*driver/ ||
+		      $line =~ /^\+\s*__setup/)) {
+			if (CHK("LINE_SPACING",
+				"Please use a blank line after function/struct/union/enum declarations\n" . $hereprev) &&
+			    $fix) {
+				fix_insert_line($fixlinenr, "\+");
+			}
+		}
+
+# check for multiple consecutive blank lines
+		if ($prevline =~ /^[\+ ]\s*$/ &&
+		    $line =~ /^\+\s*$/ &&
+		    $last_blank_line != ($linenr - 1)) {
+			if (CHK("LINE_SPACING",
+				"Please don't use multiple blank lines\n" . $hereprev) &&
+			    $fix) {
+				fix_delete_line($fixlinenr, $rawline);
+			}
+
+			$last_blank_line = $linenr;
+		}
+
+# check for missing blank lines after declarations
+# (declarations must have the same indentation and not be at the start of line)
+		if (($prevline =~ /\+(\s+)\S/) && $sline =~ /^\+$1\S/) {
+			# use temporaries
+			my $sl = $sline;
+			my $pl = $prevline;
+			# remove $Attribute/$Sparse uses to simplify comparisons
+			$sl =~ s/\b(?:$Attribute|$Sparse)\b//g;
+			$pl =~ s/\b(?:$Attribute|$Sparse)\b//g;
+			if (($pl =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ ||
+			# function pointer declarations
+			     $pl =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ ||
+			# foo bar; where foo is some local typedef or #define
+			     $pl =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ ||
+			# known declaration macros
+			     $pl =~ /^\+\s+$declaration_macros/) &&
+			# for "else if" which can look like "$Ident $Ident"
+			    !($pl =~ /^\+\s+$c90_Keywords\b/ ||
+			# other possible extensions of declaration lines
+			      $pl =~ /(?:$Compare|$Assignment|$Operators)\s*$/ ||
+			# not starting a section or a macro "\" extended line
+			      $pl =~ /(?:\{\s*|\\)$/) &&
+			# looks like a declaration
+			    !($sl =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ ||
+			# function pointer declarations
+			      $sl =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ ||
+			# foo bar; where foo is some local typedef or #define
+			      $sl =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ ||
+			# known declaration macros
+			      $sl =~ /^\+\s+$declaration_macros/ ||
+			# start of struct or union or enum
+			      $sl =~ /^\+\s+(?:static\s+)?(?:const\s+)?(?:union|struct|enum|typedef)\b/ ||
+			# start or end of block or continuation of declaration
+			      $sl =~ /^\+\s+(?:$|[\{\}\.\#\"\?\:\(\[])/ ||
+			# bitfield continuation
+			      $sl =~ /^\+\s+$Ident\s*:\s*\d+\s*[,;]/ ||
+			# other possible extensions of declaration lines
+			      $sl =~ /^\+\s+\(?\s*(?:$Compare|$Assignment|$Operators)/)) {
+				if (WARN("LINE_SPACING",
+					 "Missing a blank line after declarations\n" . $hereprev) &&
+				    $fix) {
+					fix_insert_line($fixlinenr, "\+");
+				}
+			}
+		}
+
+# check for spaces at the beginning of a line.
+# Exceptions:
+#  1) within comments
+#  2) indented preprocessor commands
+#  3) hanging labels
+		if ($rawline =~ /^\+ / && $line !~ /^\+ *(?:$;|#|$Ident:)/)  {
+			my $herevet = "$here\n" . cat_vet($rawline) . "\n";
+			if (WARN("LEADING_SPACE",
+				 "please, no spaces at the start of a line\n" . $herevet) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/^\+([ \t]+)/"\+" . tabify($1)/e;
+			}
+		}
+
+# check we are in a valid C source file if not then ignore this hunk
+		next if ($realfile !~ /\.(h|c)$/);
+
+# check for unusual line ending [ or (
+		if ($line =~ /^\+.*([\[\(])\s*$/) {
+			CHK("OPEN_ENDED_LINE",
+			    "Lines should not end with a '$1'\n" . $herecurr);
+		}
+
+# check if this appears to be the start function declaration, save the name
+		if ($sline =~ /^\+\{\s*$/ &&
+		    $prevline =~ /^\+(?:(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*)?($Ident)\(/) {
+			$context_function = $1;
+		}
+
+# check if this appears to be the end of function declaration
+		if ($sline =~ /^\+\}\s*$/) {
+			undef $context_function;
+		}
+
+# check indentation of any line with a bare else
+# (but not if it is a multiple line "if (foo) return bar; else return baz;")
+# if the previous line is a break or return and is indented 1 tab more...
+		if ($sline =~ /^\+([\t]+)(?:}[ \t]*)?else(?:[ \t]*{)?\s*$/) {
+			my $tabs = length($1) + 1;
+			if ($prevline =~ /^\+\t{$tabs,$tabs}break\b/ ||
+			    ($prevline =~ /^\+\t{$tabs,$tabs}return\b/ &&
+			     defined $lines[$linenr] &&
+			     $lines[$linenr] !~ /^[ \+]\t{$tabs,$tabs}return/)) {
+				WARN("UNNECESSARY_ELSE",
+				     "else is not generally useful after a break or return\n" . $hereprev);
+			}
+		}
+
+# check indentation of a line with a break;
+# if the previous line is a goto, return or break
+# and is indented the same # of tabs
+		if ($sline =~ /^\+([\t]+)break\s*;\s*$/) {
+			my $tabs = $1;
+			if ($prevline =~ /^\+$tabs(goto|return|break)\b/) {
+				if (WARN("UNNECESSARY_BREAK",
+					 "break is not useful after a $1\n" . $hereprev) &&
+				    $fix) {
+					fix_delete_line($fixlinenr, $rawline);
+				}
+			}
+		}
+
+# check for RCS/CVS revision markers
+		if ($rawline =~ /^\+.*\$(Revision|Log|Id)(?:\$|)/) {
+			WARN("CVS_KEYWORD",
+			     "CVS style keyword markers, these will _not_ be updated\n". $herecurr);
+		}
+
+# check for old HOTPLUG __dev<foo> section markings
+		if ($line =~ /\b(__dev(init|exit)(data|const|))\b/) {
+			WARN("HOTPLUG_SECTION",
+			     "Using $1 is unnecessary\n" . $herecurr);
+		}
+
+# Check for potential 'bare' types
+		my ($stat, $cond, $line_nr_next, $remain_next, $off_next,
+		    $realline_next);
+#print "LINE<$line>\n";
+		if ($linenr > $suppress_statement &&
+		    $realcnt && $sline =~ /.\s*\S/) {
+			($stat, $cond, $line_nr_next, $remain_next, $off_next) =
+				ctx_statement_block($linenr, $realcnt, 0);
+			$stat =~ s/\n./\n /g;
+			$cond =~ s/\n./\n /g;
+
+#print "linenr<$linenr> <$stat>\n";
+			# If this statement has no statement boundaries within
+			# it there is no point in retrying a statement scan
+			# until we hit end of it.
+			my $frag = $stat; $frag =~ s/;+\s*$//;
+			if ($frag !~ /(?:{|;)/) {
+#print "skip<$line_nr_next>\n";
+				$suppress_statement = $line_nr_next;
+			}
+
+			# Find the real next line.
+			$realline_next = $line_nr_next;
+			if (defined $realline_next &&
+			    (!defined $lines[$realline_next - 1] ||
+			     substr($lines[$realline_next - 1], $off_next) =~ /^\s*$/)) {
+				$realline_next++;
+			}
+
+			my $s = $stat;
+			$s =~ s/{.*$//s;
+
+			# Ignore goto labels.
+			if ($s =~ /$Ident:\*$/s) {
+
+			# Ignore functions being called
+			} elsif ($s =~ /^.\s*$Ident\s*\(/s) {
+
+			} elsif ($s =~ /^.\s*else\b/s) {
+
+			# declarations always start with types
+			} elsif ($prev_values eq 'E' && $s =~ /^.\s*(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?((?:\s*$Ident)+?)\b(?:\s+$Sparse)?\s*\**\s*(?:$Ident|\(\*[^\)]*\))(?:\s*$Modifier)?\s*(?:;|=|,|\()/s) {
+				my $type = $1;
+				$type =~ s/\s+/ /g;
+				possible($type, "A:" . $s);
+
+			# definitions in global scope can only start with types
+			} elsif ($s =~ /^.(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?($Ident)\b\s*(?!:)/s) {
+				possible($1, "B:" . $s);
+			}
+
+			# any (foo ... *) is a pointer cast, and foo is a type
+			while ($s =~ /\(($Ident)(?:\s+$Sparse)*[\s\*]+\s*\)/sg) {
+				possible($1, "C:" . $s);
+			}
+
+			# Check for any sort of function declaration.
+			# int foo(something bar, other baz);
+			# void (*store_gdt)(x86_descr_ptr *);
+			if ($prev_values eq 'E' && $s =~ /^(.(?:typedef\s*)?(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*(?:\b$Ident|\(\*\s*$Ident\))\s*)\(/s) {
+				my ($name_len) = length($1);
+
+				my $ctx = $s;
+				substr($ctx, 0, $name_len + 1, '');
+				$ctx =~ s/\)[^\)]*$//;
+
+				for my $arg (split(/\s*,\s*/, $ctx)) {
+					if ($arg =~ /^(?:const\s+)?($Ident)(?:\s+$Sparse)*\s*\**\s*(:?\b$Ident)?$/s || $arg =~ /^($Ident)$/s) {
+
+						possible($1, "D:" . $s);
+					}
+				}
+			}
+
+		}
+
+#
+# Checks which may be anchored in the context.
+#
+
+# Check for switch () and associated case and default
+# statements should be at the same indent.
+		if ($line=~/\bswitch\s*\(.*\)/) {
+			my $err = '';
+			my $sep = '';
+			my @ctx = ctx_block_outer($linenr, $realcnt);
+			shift(@ctx);
+			for my $ctx (@ctx) {
+				my ($clen, $cindent) = line_stats($ctx);
+				if ($ctx =~ /^\+\s*(case\s+|default:)/ &&
+							$indent != $cindent) {
+					$err .= "$sep$ctx\n";
+					$sep = '';
+				} else {
+					$sep = "[...]\n";
+				}
+			}
+			if ($err ne '') {
+				ERROR("SWITCH_CASE_INDENT_LEVEL",
+				      "switch and case should be at the same indent\n$hereline$err");
+			}
+		}
+
+# if/while/etc brace do not go on next line, unless defining a do while loop,
+# or if that brace on the next line is for something else
+		if ($line =~ /(.*)\b((?:if|while|for|switch|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|do\b|else\b)/ && $line !~ /^.\s*\#/) {
+			my $pre_ctx = "$1$2";
+
+			my ($level, @ctx) = ctx_statement_level($linenr, $realcnt, 0);
+
+			if ($line =~ /^\+\t{6,}/) {
+				WARN("DEEP_INDENTATION",
+				     "Too many leading tabs - consider code refactoring\n" . $herecurr);
+			}
+
+			my $ctx_cnt = $realcnt - $#ctx - 1;
+			my $ctx = join("\n", @ctx);
+
+			my $ctx_ln = $linenr;
+			my $ctx_skip = $realcnt;
+
+			while ($ctx_skip > $ctx_cnt || ($ctx_skip == $ctx_cnt &&
+					defined $lines[$ctx_ln - 1] &&
+					$lines[$ctx_ln - 1] =~ /^-/)) {
+				##print "SKIP<$ctx_skip> CNT<$ctx_cnt>\n";
+				$ctx_skip-- if (!defined $lines[$ctx_ln - 1] || $lines[$ctx_ln - 1] !~ /^-/);
+				$ctx_ln++;
+			}
+
+			#print "realcnt<$realcnt> ctx_cnt<$ctx_cnt>\n";
+			#print "pre<$pre_ctx>\nline<$line>\nctx<$ctx>\nnext<$lines[$ctx_ln - 1]>\n";
+
+			if ($ctx !~ /{\s*/ && defined($lines[$ctx_ln - 1]) && $lines[$ctx_ln - 1] =~ /^\+\s*{/) {
+				ERROR("OPEN_BRACE",
+				      "that open brace { should be on the previous line\n" .
+					"$here\n$ctx\n$rawlines[$ctx_ln - 1]\n");
+			}
+			if ($level == 0 && $pre_ctx !~ /}\s*while\s*\($/ &&
+			    $ctx =~ /\)\s*\;\s*$/ &&
+			    defined $lines[$ctx_ln - 1])
+			{
+				my ($nlength, $nindent) = line_stats($lines[$ctx_ln - 1]);
+				if ($nindent > $indent) {
+					WARN("TRAILING_SEMICOLON",
+					     "trailing semicolon indicates no statements, indent implies otherwise\n" .
+						"$here\n$ctx\n$rawlines[$ctx_ln - 1]\n");
+				}
+			}
+		}
+
+# Check relative indent for conditionals and blocks.
+		if ($line =~ /\b(?:(?:if|while|for|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|(?:do|else)\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) {
+			($stat, $cond, $line_nr_next, $remain_next, $off_next) =
+				ctx_statement_block($linenr, $realcnt, 0)
+					if (!defined $stat);
+			my ($s, $c) = ($stat, $cond);
+
+			substr($s, 0, length($c), '');
+
+			# remove inline comments
+			$s =~ s/$;/ /g;
+			$c =~ s/$;/ /g;
+
+			# Find out how long the conditional actually is.
+			my @newlines = ($c =~ /\n/gs);
+			my $cond_lines = 1 + $#newlines;
+
+			# Make sure we remove the line prefixes as we have
+			# none on the first line, and are going to readd them
+			# where necessary.
+			$s =~ s/\n./\n/gs;
+			while ($s =~ /\n\s+\\\n/) {
+				$cond_lines += $s =~ s/\n\s+\\\n/\n/g;
+			}
+
+			# We want to check the first line inside the block
+			# starting at the end of the conditional, so remove:
+			#  1) any blank line termination
+			#  2) any opening brace { on end of the line
+			#  3) any do (...) {
+			my $continuation = 0;
+			my $check = 0;
+			$s =~ s/^.*\bdo\b//;
+			$s =~ s/^\s*{//;
+			if ($s =~ s/^\s*\\//) {
+				$continuation = 1;
+			}
+			if ($s =~ s/^\s*?\n//) {
+				$check = 1;
+				$cond_lines++;
+			}
+
+			# Also ignore a loop construct at the end of a
+			# preprocessor statement.
+			if (($prevline =~ /^.\s*#\s*define\s/ ||
+			    $prevline =~ /\\\s*$/) && $continuation == 0) {
+				$check = 0;
+			}
+
+			my $cond_ptr = -1;
+			$continuation = 0;
+			while ($cond_ptr != $cond_lines) {
+				$cond_ptr = $cond_lines;
+
+				# If we see an #else/#elif then the code
+				# is not linear.
+				if ($s =~ /^\s*\#\s*(?:else|elif)/) {
+					$check = 0;
+				}
+
+				# Ignore:
+				#  1) blank lines, they should be at 0,
+				#  2) preprocessor lines, and
+				#  3) labels.
+				if ($continuation ||
+				    $s =~ /^\s*?\n/ ||
+				    $s =~ /^\s*#\s*?/ ||
+				    $s =~ /^\s*$Ident\s*:/) {
+					$continuation = ($s =~ /^.*?\\\n/) ? 1 : 0;
+					if ($s =~ s/^.*?\n//) {
+						$cond_lines++;
+					}
+				}
+			}
+
+			my (undef, $sindent) = line_stats("+" . $s);
+			my $stat_real = raw_line($linenr, $cond_lines);
+
+			# Check if either of these lines are modified, else
+			# this is not this patch's fault.
+			if (!defined($stat_real) ||
+			    $stat !~ /^\+/ && $stat_real !~ /^\+/) {
+				$check = 0;
+			}
+			if (defined($stat_real) && $cond_lines > 1) {
+				$stat_real = "[...]\n$stat_real";
+			}
+
+			#print "line<$line> prevline<$prevline> indent<$indent> sindent<$sindent> check<$check> continuation<$continuation> s<$s> cond_lines<$cond_lines> stat_real<$stat_real> stat<$stat>\n";
+
+			if ($check && $s ne '' &&
+			    (($sindent % $tabsize) != 0 ||
+			     ($sindent < $indent) ||
+			     ($sindent == $indent &&
+			      ($s !~ /^\s*(?:\}|\{|else\b)/)) ||
+			     ($sindent > $indent + $tabsize))) {
+				WARN("SUSPECT_CODE_INDENT",
+				     "suspect code indent for conditional statements ($indent, $sindent)\n" . $herecurr . "$stat_real\n");
+			}
+		}
+
+		# Track the 'values' across context and added lines.
+		my $opline = $line; $opline =~ s/^./ /;
+		my ($curr_values, $curr_vars) =
+				annotate_values($opline . "\n", $prev_values);
+		$curr_values = $prev_values . $curr_values;
+		if ($dbg_values) {
+			my $outline = $opline; $outline =~ s/\t/ /g;
+			print "$linenr > .$outline\n";
+			print "$linenr > $curr_values\n";
+			print "$linenr >  $curr_vars\n";
+		}
+		$prev_values = substr($curr_values, -1);
+
+#ignore lines not being added
+		next if ($line =~ /^[^\+]/);
+
+# check for self assignments used to avoid compiler warnings
+# e.g.:	int foo = foo, *bar = NULL;
+#	struct foo bar = *(&(bar));
+		if ($line =~ /^\+\s*(?:$Declare)?([A-Za-z_][A-Za-z\d_]*)\s*=/) {
+			my $var = $1;
+			if ($line =~ /^\+\s*(?:$Declare)?$var\s*=\s*(?:$var|\*\s*\(?\s*&\s*\(?\s*$var\s*\)?\s*\)?)\s*[;,]/) {
+				WARN("SELF_ASSIGNMENT",
+				     "Do not use self-assignments to avoid compiler warnings\n" . $herecurr);
+			}
+		}
+
+# check for dereferences that span multiple lines
+		if ($prevline =~ /^\+.*$Lval\s*(?:\.|->)\s*$/ &&
+		    $line =~ /^\+\s*(?!\#\s*(?!define\s+|if))\s*$Lval/) {
+			$prevline =~ /($Lval\s*(?:\.|->))\s*$/;
+			my $ref = $1;
+			$line =~ /^.\s*($Lval)/;
+			$ref .= $1;
+			$ref =~ s/\s//g;
+			WARN("MULTILINE_DEREFERENCE",
+			     "Avoid multiple line dereference - prefer '$ref'\n" . $hereprev);
+		}
+
+# check for declarations of signed or unsigned without int
+		while ($line =~ m{\b($Declare)\s*(?!char\b|short\b|int\b|long\b)\s*($Ident)?\s*[=,;\[\)\(]}g) {
+			my $type = $1;
+			my $var = $2;
+			$var = "" if (!defined $var);
+			if ($type =~ /^(?:(?:$Storage|$Inline|$Attribute)\s+)*((?:un)?signed)((?:\s*\*)*)\s*$/) {
+				my $sign = $1;
+				my $pointer = $2;
+
+				$pointer = "" if (!defined $pointer);
+
+				if (WARN("UNSPECIFIED_INT",
+					 "Prefer '" . trim($sign) . " int" . rtrim($pointer) . "' to bare use of '$sign" . rtrim($pointer) . "'\n" . $herecurr) &&
+				    $fix) {
+					my $decl = trim($sign) . " int ";
+					my $comp_pointer = $pointer;
+					$comp_pointer =~ s/\s//g;
+					$decl .= $comp_pointer;
+					$decl = rtrim($decl) if ($var eq "");
+					$fixed[$fixlinenr] =~ s@\b$sign\s*\Q$pointer\E\s*$var\b@$decl$var@;
+				}
+			}
+		}
+
+# TEST: allow direct testing of the type matcher.
+		if ($dbg_type) {
+			if ($line =~ /^.\s*$Declare\s*$/) {
+				ERROR("TEST_TYPE",
+				      "TEST: is type\n" . $herecurr);
+			} elsif ($dbg_type > 1 && $line =~ /^.+($Declare)/) {
+				ERROR("TEST_NOT_TYPE",
+				      "TEST: is not type ($1 is)\n". $herecurr);
+			}
+			next;
+		}
+# TEST: allow direct testing of the attribute matcher.
+		if ($dbg_attr) {
+			if ($line =~ /^.\s*$Modifier\s*$/) {
+				ERROR("TEST_ATTR",
+				      "TEST: is attr\n" . $herecurr);
+			} elsif ($dbg_attr > 1 && $line =~ /^.+($Modifier)/) {
+				ERROR("TEST_NOT_ATTR",
+				      "TEST: is not attr ($1 is)\n". $herecurr);
+			}
+			next;
+		}
+
+# check for initialisation to aggregates open brace on the next line
+		if ($line =~ /^.\s*{/ &&
+		    $prevline =~ /(?:^|[^=])=\s*$/) {
+			if (ERROR("OPEN_BRACE",
+				  "that open brace { should be on the previous line\n" . $hereprev) &&
+			    $fix && $prevline =~ /^\+/ && $line =~ /^\+/) {
+				fix_delete_line($fixlinenr - 1, $prevrawline);
+				fix_delete_line($fixlinenr, $rawline);
+				my $fixedline = $prevrawline;
+				$fixedline =~ s/\s*=\s*$/ = {/;
+				fix_insert_line($fixlinenr, $fixedline);
+				$fixedline = $line;
+				$fixedline =~ s/^(.\s*)\{\s*/$1/;
+				fix_insert_line($fixlinenr, $fixedline);
+			}
+		}
+
+#
+# Checks which are anchored on the added line.
+#
+
+# check for malformed paths in #include statements (uses RAW line)
+		if ($rawline =~ m{^.\s*\#\s*include\s+[<"](.*)[">]}) {
+			my $path = $1;
+			if ($path =~ m{//}) {
+				ERROR("MALFORMED_INCLUDE",
+				      "malformed #include filename\n" . $herecurr);
+			}
+			if ($path =~ "^uapi/" && $realfile =~ m@\binclude/uapi/@) {
+				ERROR("UAPI_INCLUDE",
+				      "No #include in ...include/uapi/... should use a uapi/ path prefix\n" . $herecurr);
+			}
+		}
+
+# no C99 // comments
+		if ($line =~ m{//}) {
+			if (ERROR("C99_COMMENTS",
+				  "do not use C99 // comments\n" . $herecurr) &&
+			    $fix) {
+				my $line = $fixed[$fixlinenr];
+				if ($line =~ /\/\/(.*)$/) {
+					my $comment = trim($1);
+					$fixed[$fixlinenr] =~ s@\/\/(.*)$@/\* $comment \*/@;
+				}
+			}
+		}
+		# Remove C99 comments.
+		$line =~ s@//.*@@;
+		$opline =~ s@//.*@@;
+
+# EXPORT_SYMBOL should immediately follow the thing it is exporting, consider
+# the whole statement.
+#print "APW <$lines[$realline_next - 1]>\n";
+		if (defined $realline_next &&
+		    exists $lines[$realline_next - 1] &&
+		    !defined $suppress_export{$realline_next} &&
+		    ($lines[$realline_next - 1] =~ /EXPORT_SYMBOL.*\((.*)\)/)) {
+			# Handle definitions which produce identifiers with
+			# a prefix:
+			#   XXX(foo);
+			#   EXPORT_SYMBOL(something_foo);
+			my $name = $1;
+			if ($stat =~ /^(?:.\s*}\s*\n)?.([A-Z_]+)\s*\(\s*($Ident)/ &&
+			    $name =~ /^${Ident}_$2/) {
+#print "FOO C name<$name>\n";
+				$suppress_export{$realline_next} = 1;
+
+			} elsif ($stat !~ /(?:
+				\n.}\s*$|
+				^.DEFINE_$Ident\(\Q$name\E\)|
+				^.DECLARE_$Ident\(\Q$name\E\)|
+				^.LIST_HEAD\(\Q$name\E\)|
+				^.(?:$Storage\s+)?$Type\s*\(\s*\*\s*\Q$name\E\s*\)\s*\(|
+				\b\Q$name\E(?:\s+$Attribute)*\s*(?:;|=|\[|\()
+			    )/x) {
+#print "FOO A<$lines[$realline_next - 1]> stat<$stat> name<$name>\n";
+				$suppress_export{$realline_next} = 2;
+			} else {
+				$suppress_export{$realline_next} = 1;
+			}
+		}
+		if (!defined $suppress_export{$linenr} &&
+		    $prevline =~ /^.\s*$/ &&
+		    ($line =~ /EXPORT_SYMBOL.*\((.*)\)/)) {
+#print "FOO B <$lines[$linenr - 1]>\n";
+			$suppress_export{$linenr} = 2;
+		}
+		if (defined $suppress_export{$linenr} &&
+		    $suppress_export{$linenr} == 2) {
+			WARN("EXPORT_SYMBOL",
+			     "EXPORT_SYMBOL(foo); should immediately follow its function/variable\n" . $herecurr);
+		}
+
+# check for global initialisers.
+		if ($line =~ /^\+$Type\s*$Ident(?:\s+$Modifier)*\s*=\s*($zero_initializer)\s*;/ &&
+		    !exclude_global_initialisers($realfile)) {
+			if (ERROR("GLOBAL_INITIALISERS",
+				  "do not initialise globals to $1\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/(^.$Type\s*$Ident(?:\s+$Modifier)*)\s*=\s*$zero_initializer\s*;/$1;/;
+			}
+		}
+# check for static initialisers.
+		if ($line =~ /^\+.*\bstatic\s.*=\s*($zero_initializer)\s*;/) {
+			if (ERROR("INITIALISED_STATIC",
+				  "do not initialise statics to $1\n" .
+				      $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/(\bstatic\s.*?)\s*=\s*$zero_initializer\s*;/$1;/;
+			}
+		}
+
+# check for misordered declarations of char/short/int/long with signed/unsigned
+		while ($sline =~ m{(\b$TypeMisordered\b)}g) {
+			my $tmp = trim($1);
+			WARN("MISORDERED_TYPE",
+			     "type '$tmp' should be specified in [[un]signed] [short|int|long|long long] order\n" . $herecurr);
+		}
+
+# check for unnecessary <signed> int declarations of short/long/long long
+		while ($sline =~ m{\b($TypeMisordered(\s*\*)*|$C90_int_types)\b}g) {
+			my $type = trim($1);
+			next if ($type !~ /\bint\b/);
+			next if ($type !~ /\b(?:short|long\s+long|long)\b/);
+			my $new_type = $type;
+			$new_type =~ s/\b\s*int\s*\b/ /;
+			$new_type =~ s/\b\s*(?:un)?signed\b\s*/ /;
+			$new_type =~ s/^const\s+//;
+			$new_type = "unsigned $new_type" if ($type =~ /\bunsigned\b/);
+			$new_type = "const $new_type" if ($type =~ /^const\b/);
+			$new_type =~ s/\s+/ /g;
+			$new_type = trim($new_type);
+			if (WARN("UNNECESSARY_INT",
+				 "Prefer '$new_type' over '$type' as the int is unnecessary\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\b\Q$type\E\b/$new_type/;
+			}
+		}
+
+# check for static const char * arrays.
+		if ($line =~ /\bstatic\s+const\s+char\s*\*\s*(\w+)\s*\[\s*\]\s*=\s*/) {
+			WARN("STATIC_CONST_CHAR_ARRAY",
+			     "static const char * array should probably be static const char * const\n" .
+				$herecurr);
+		}
+
+# check for initialized const char arrays that should be static const
+		if ($line =~ /^\+\s*const\s+(char|unsigned\s+char|_*u8|(?:[us]_)?int8_t)\s+\w+\s*\[\s*(?:\w+\s*)?\]\s*=\s*"/) {
+			if (WARN("STATIC_CONST_CHAR_ARRAY",
+				 "const array should probably be static const\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/(^.\s*)const\b/${1}static const/;
+			}
+		}
+
+# check for static char foo[] = "bar" declarations.
+		if ($line =~ /\bstatic\s+char\s+(\w+)\s*\[\s*\]\s*=\s*"/) {
+			WARN("STATIC_CONST_CHAR_ARRAY",
+			     "static char array declaration should probably be static const char\n" .
+				$herecurr);
+		}
+
+# check for const <foo> const where <foo> is not a pointer or array type
+		if ($sline =~ /\bconst\s+($BasicType)\s+const\b/) {
+			my $found = $1;
+			if ($sline =~ /\bconst\s+\Q$found\E\s+const\b\s*\*/) {
+				WARN("CONST_CONST",
+				     "'const $found const *' should probably be 'const $found * const'\n" . $herecurr);
+			} elsif ($sline !~ /\bconst\s+\Q$found\E\s+const\s+\w+\s*\[/) {
+				WARN("CONST_CONST",
+				     "'const $found const' should probably be 'const $found'\n" . $herecurr);
+			}
+		}
+
+# check for const static or static <non ptr type> const declarations
+# prefer 'static const <foo>' over 'const static <foo>' and 'static <foo> const'
+		if ($sline =~ /^\+\s*const\s+static\s+($Type)\b/ ||
+		    $sline =~ /^\+\s*static\s+($BasicType)\s+const\b/) {
+			if (WARN("STATIC_CONST",
+				 "Move const after static - use 'static const $1'\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\bconst\s+static\b/static const/;
+				$fixed[$fixlinenr] =~ s/\bstatic\s+($BasicType)\s+const\b/static const $1/;
+			}
+		}
+
+# check for non-global char *foo[] = {"bar", ...} declarations.
+		if ($line =~ /^.\s+(?:static\s+|const\s+)?char\s+\*\s*\w+\s*\[\s*\]\s*=\s*\{/) {
+			WARN("STATIC_CONST_CHAR_ARRAY",
+			     "char * array declaration might be better as static const\n" .
+				$herecurr);
+		}
+
+# check for sizeof(foo)/sizeof(foo[0]) that could be ARRAY_SIZE(foo)
+		if ($line =~ m@\bsizeof\s*\(\s*($Lval)\s*\)@) {
+			my $array = $1;
+			if ($line =~ m@\b(sizeof\s*\(\s*\Q$array\E\s*\)\s*/\s*sizeof\s*\(\s*\Q$array\E\s*\[\s*0\s*\]\s*\))@) {
+				my $array_div = $1;
+				if (WARN("ARRAY_SIZE",
+					 "Prefer ARRAY_SIZE($array)\n" . $herecurr) &&
+				    $fix) {
+					$fixed[$fixlinenr] =~ s/\Q$array_div\E/ARRAY_SIZE($array)/;
+				}
+			}
+		}
+
+# check for function declarations without arguments like "int foo()"
+		if ($line =~ /(\b$Type\s*$Ident)\s*\(\s*\)/) {
+			if (ERROR("FUNCTION_WITHOUT_ARGS",
+				  "Bad function definition - $1() should probably be $1(void)\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/(\b($Type)\s+($Ident))\s*\(\s*\)/$2 $3(void)/;
+			}
+		}
+
+# check for new typedefs, only function parameters and sparse annotations
+# make sense.
+		if ($line =~ /\btypedef\s/ &&
+		    $line !~ /\btypedef\s+$Type\s*\(\s*\*?$Ident\s*\)\s*\(/ &&
+		    $line !~ /\btypedef\s+$Type\s+$Ident\s*\(/ &&
+		    $line !~ /\b$typeTypedefs\b/ &&
+		    $line !~ /\b__bitwise\b/) {
+			WARN("NEW_TYPEDEFS",
+			     "do not add new typedefs\n" . $herecurr);
+		}
+
+# * goes on variable not on type
+		# (char*[ const])
+		while ($line =~ m{(\($NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)\))}g) {
+			#print "AA<$1>\n";
+			my ($ident, $from, $to) = ($1, $2, $2);
+
+			# Should start with a space.
+			$to =~ s/^(\S)/ $1/;
+			# Should not end with a space.
+			$to =~ s/\s+$//;
+			# '*'s should not have spaces between.
+			while ($to =~ s/\*\s+\*/\*\*/) {
+			}
+
+##			print "1: from<$from> to<$to> ident<$ident>\n";
+			if ($from ne $to) {
+				if (ERROR("POINTER_LOCATION",
+					  "\"(foo$from)\" should be \"(foo$to)\"\n" .  $herecurr) &&
+				    $fix) {
+					my $sub_from = $ident;
+					my $sub_to = $ident;
+					$sub_to =~ s/\Q$from\E/$to/;
+					$fixed[$fixlinenr] =~
+					    s@\Q$sub_from\E@$sub_to@;
+				}
+			}
+		}
+		while ($line =~ m{(\b$NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)($Ident))}g) {
+			#print "BB<$1>\n";
+			my ($match, $from, $to, $ident) = ($1, $2, $2, $3);
+
+			# Should start with a space.
+			$to =~ s/^(\S)/ $1/;
+			# Should not end with a space.
+			$to =~ s/\s+$//;
+			# '*'s should not have spaces between.
+			while ($to =~ s/\*\s+\*/\*\*/) {
+			}
+			# Modifiers should have spaces.
+			$to =~ s/(\b$Modifier$)/$1 /;
+
+##			print "2: from<$from> to<$to> ident<$ident>\n";
+			if ($from ne $to && $ident !~ /^$Modifier$/) {
+				if (ERROR("POINTER_LOCATION",
+					  "\"foo${from}bar\" should be \"foo${to}bar\"\n" .  $herecurr) &&
+				    $fix) {
+
+					my $sub_from = $match;
+					my $sub_to = $match;
+					$sub_to =~ s/\Q$from\E/$to/;
+					$fixed[$fixlinenr] =~
+					    s@\Q$sub_from\E@$sub_to@;
+				}
+			}
+		}
+
+# avoid BUG() or BUG_ON()
+		if ($line =~ /\b(?:BUG|BUG_ON)\b/) {
+			my $msg_level = \&WARN;
+			$msg_level = \&CHK if ($file);
+			&{$msg_level}("AVOID_BUG",
+				      "Avoid crashing the kernel - try using WARN_ON & recovery code rather than BUG() or BUG_ON()\n" . $herecurr);
+		}
+
+# avoid LINUX_VERSION_CODE
+		if ($line =~ /\bLINUX_VERSION_CODE\b/) {
+			WARN("LINUX_VERSION_CODE",
+			     "LINUX_VERSION_CODE should be avoided, code should be for the version to which it is merged\n" . $herecurr);
+		}
+
+# check for uses of printk_ratelimit
+		if ($line =~ /\bprintk_ratelimit\s*\(/) {
+			WARN("PRINTK_RATELIMITED",
+			     "Prefer printk_ratelimited or pr_<level>_ratelimited to printk_ratelimit\n" . $herecurr);
+		}
+
+# printk should use KERN_* levels
+		if ($line =~ /\bprintk\s*\(\s*(?!KERN_[A-Z]+\b)/) {
+			WARN("PRINTK_WITHOUT_KERN_LEVEL",
+			     "printk() should include KERN_<LEVEL> facility level\n" . $herecurr);
+		}
+
+# prefer variants of (subsystem|netdev|dev|pr)_<level> to printk(KERN_<LEVEL>
+		if ($line =~ /\b(printk(_once|_ratelimited)?)\s*\(\s*KERN_([A-Z]+)/) {
+			my $printk = $1;
+			my $modifier = $2;
+			my $orig = $3;
+			$modifier = "" if (!defined($modifier));
+			my $level = lc($orig);
+			$level = "warn" if ($level eq "warning");
+			my $level2 = $level;
+			$level2 = "dbg" if ($level eq "debug");
+			$level .= $modifier;
+			$level2 .= $modifier;
+			WARN("PREFER_PR_LEVEL",
+			     "Prefer [subsystem eg: netdev]_$level2([subsystem]dev, ... then dev_$level2(dev, ... then pr_$level(...  to $printk(KERN_$orig ...\n" . $herecurr);
+		}
+
+# prefer dev_<level> to dev_printk(KERN_<LEVEL>
+		if ($line =~ /\bdev_printk\s*\(\s*KERN_([A-Z]+)/) {
+			my $orig = $1;
+			my $level = lc($orig);
+			$level = "warn" if ($level eq "warning");
+			$level = "dbg" if ($level eq "debug");
+			WARN("PREFER_DEV_LEVEL",
+			     "Prefer dev_$level(... to dev_printk(KERN_$orig, ...\n" . $herecurr);
+		}
+
+# trace_printk should not be used in production code.
+		if ($line =~ /\b(trace_printk|trace_puts|ftrace_vprintk)\s*\(/) {
+			WARN("TRACE_PRINTK",
+			     "Do not use $1() in production code (this can be ignored if built only with a debug config option)\n" . $herecurr);
+		}
+
+# ENOSYS means "bad syscall nr" and nothing else.  This will have a small
+# number of false positives, but assembly files are not checked, so at
+# least the arch entry code will not trigger this warning.
+		if ($line =~ /\bENOSYS\b/) {
+			WARN("ENOSYS",
+			     "ENOSYS means 'invalid syscall nr' and nothing else\n" . $herecurr);
+		}
+
+# ENOTSUPP is not a standard error code and should be avoided in new patches.
+# Folks usually mean EOPNOTSUPP (also called ENOTSUP), when they type ENOTSUPP.
+# Similarly to ENOSYS warning a small number of false positives is expected.
+		if (!$file && $line =~ /\bENOTSUPP\b/) {
+			if (WARN("ENOTSUPP",
+				 "ENOTSUPP is not a SUSV4 error code, prefer EOPNOTSUPP\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\bENOTSUPP\b/EOPNOTSUPP/;
+			}
+		}
+
+# function brace can't be on same line, except for #defines of do while,
+# or if closed on same line
+		if ($perl_version_ok &&
+		    $sline =~ /$Type\s*$Ident\s*$balanced_parens\s*\{/ &&
+		    $sline !~ /\#\s*define\b.*do\s*\{/ &&
+		    $sline !~ /}/) {
+			if (ERROR("OPEN_BRACE",
+				  "open brace '{' following function definitions go on the next line\n" . $herecurr) &&
+			    $fix) {
+				fix_delete_line($fixlinenr, $rawline);
+				my $fixed_line = $rawline;
+				$fixed_line =~ /(^..*$Type\s*$Ident\(.*\)\s*)\{(.*)$/;
+				my $line1 = $1;
+				my $line2 = $2;
+				fix_insert_line($fixlinenr, ltrim($line1));
+				fix_insert_line($fixlinenr, "\+{");
+				if ($line2 !~ /^\s*$/) {
+					fix_insert_line($fixlinenr, "\+\t" . trim($line2));
+				}
+			}
+		}
+
+# open braces for enum, union and struct go on the same line.
+		if ($line =~ /^.\s*{/ &&
+		    $prevline =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?\s*$/) {
+			if (ERROR("OPEN_BRACE",
+				  "open brace '{' following $1 go on the same line\n" . $hereprev) &&
+			    $fix && $prevline =~ /^\+/ && $line =~ /^\+/) {
+				fix_delete_line($fixlinenr - 1, $prevrawline);
+				fix_delete_line($fixlinenr, $rawline);
+				my $fixedline = rtrim($prevrawline) . " {";
+				fix_insert_line($fixlinenr, $fixedline);
+				$fixedline = $rawline;
+				$fixedline =~ s/^(.\s*)\{\s*/$1\t/;
+				if ($fixedline !~ /^\+\s*$/) {
+					fix_insert_line($fixlinenr, $fixedline);
+				}
+			}
+		}
+
+# missing space after union, struct or enum definition
+		if ($line =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident){1,2}[=\{]/) {
+			if (WARN("SPACING",
+				 "missing space after $1 definition\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~
+				    s/^(.\s*(?:typedef\s+)?(?:enum|union|struct)(?:\s+$Ident){1,2})([=\{])/$1 $2/;
+			}
+		}
+
+# Function pointer declarations
+# check spacing between type, funcptr, and args
+# canonical declaration is "type (*funcptr)(args...)"
+		if ($line =~ /^.\s*($Declare)\((\s*)\*(\s*)($Ident)(\s*)\)(\s*)\(/) {
+			my $declare = $1;
+			my $pre_pointer_space = $2;
+			my $post_pointer_space = $3;
+			my $funcname = $4;
+			my $post_funcname_space = $5;
+			my $pre_args_space = $6;
+
+# the $Declare variable will capture all spaces after the type
+# so check it for a missing trailing missing space but pointer return types
+# don't need a space so don't warn for those.
+			my $post_declare_space = "";
+			if ($declare =~ /(\s+)$/) {
+				$post_declare_space = $1;
+				$declare = rtrim($declare);
+			}
+			if ($declare !~ /\*$/ && $post_declare_space =~ /^$/) {
+				WARN("SPACING",
+				     "missing space after return type\n" . $herecurr);
+				$post_declare_space = " ";
+			}
+
+# unnecessary space "type  (*funcptr)(args...)"
+# This test is not currently implemented because these declarations are
+# equivalent to
+#	int  foo(int bar, ...)
+# and this is form shouldn't/doesn't generate a checkpatch warning.
+#
+#			elsif ($declare =~ /\s{2,}$/) {
+#				WARN("SPACING",
+#				     "Multiple spaces after return type\n" . $herecurr);
+#			}
+
+# unnecessary space "type ( *funcptr)(args...)"
+			if (defined $pre_pointer_space &&
+			    $pre_pointer_space =~ /^\s/) {
+				WARN("SPACING",
+				     "Unnecessary space after function pointer open parenthesis\n" . $herecurr);
+			}
+
+# unnecessary space "type (* funcptr)(args...)"
+			if (defined $post_pointer_space &&
+			    $post_pointer_space =~ /^\s/) {
+				WARN("SPACING",
+				     "Unnecessary space before function pointer name\n" . $herecurr);
+			}
+
+# unnecessary space "type (*funcptr )(args...)"
+			if (defined $post_funcname_space &&
+			    $post_funcname_space =~ /^\s/) {
+				WARN("SPACING",
+				     "Unnecessary space after function pointer name\n" . $herecurr);
+			}
+
+# unnecessary space "type (*funcptr) (args...)"
+			if (defined $pre_args_space &&
+			    $pre_args_space =~ /^\s/) {
+				WARN("SPACING",
+				     "Unnecessary space before function pointer arguments\n" . $herecurr);
+			}
+
+			if (show_type("SPACING") && $fix) {
+				$fixed[$fixlinenr] =~
+				    s/^(.\s*)$Declare\s*\(\s*\*\s*$Ident\s*\)\s*\(/$1 . $declare . $post_declare_space . '(*' . $funcname . ')('/ex;
+			}
+		}
+
+# check for spacing round square brackets; allowed:
+#  1. with a type on the left -- int [] a;
+#  2. at the beginning of a line for slice initialisers -- [0...10] = 5,
+#  3. inside a curly brace -- = { [0...10] = 5 }
+		while ($line =~ /(.*?\s)\[/g) {
+			my ($where, $prefix) = ($-[1], $1);
+			if ($prefix !~ /$Type\s+$/ &&
+			    ($where != 0 || $prefix !~ /^.\s+$/) &&
+			    $prefix !~ /[{,:]\s+$/) {
+				if (ERROR("BRACKET_SPACE",
+					  "space prohibited before open square bracket '['\n" . $herecurr) &&
+				    $fix) {
+				    $fixed[$fixlinenr] =~
+					s/^(\+.*?)\s+\[/$1\[/;
+				}
+			}
+		}
+
+# check for spaces between functions and their parentheses.
+		while ($line =~ /($Ident)\s+\(/g) {
+			my $name = $1;
+			my $ctx_before = substr($line, 0, $-[1]);
+			my $ctx = "$ctx_before$name";
+
+			# Ignore those directives where spaces _are_ permitted.
+			if ($name =~ /^(?:
+				if|for|while|switch|return|case|
+				volatile|__volatile__|
+				__attribute__|format|__extension__|
+				asm|__asm__)$/x)
+			{
+			# cpp #define statements have non-optional spaces, ie
+			# if there is a space between the name and the open
+			# parenthesis it is simply not a parameter group.
+			} elsif ($ctx_before =~ /^.\s*\#\s*define\s*$/) {
+
+			# cpp #elif statement condition may start with a (
+			} elsif ($ctx =~ /^.\s*\#\s*elif\s*$/) {
+
+			# If this whole things ends with a type its most
+			# likely a typedef for a function.
+			} elsif ($ctx =~ /$Type$/) {
+
+			} else {
+				if (WARN("SPACING",
+					 "space prohibited between function name and open parenthesis '('\n" . $herecurr) &&
+					     $fix) {
+					$fixed[$fixlinenr] =~
+					    s/\b$name\s+\(/$name\(/;
+				}
+			}
+		}
+
+# Check operator spacing.
+		if (!($line=~/\#\s*include/)) {
+			my $fixed_line = "";
+			my $line_fixed = 0;
+
+			my $ops = qr{
+				<<=|>>=|<=|>=|==|!=|
+				\+=|-=|\*=|\/=|%=|\^=|\|=|&=|
+				=>|->|<<|>>|<|>|=|!|~|
+				&&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|%|
+				\?:|\?|:
+			}x;
+			my @elements = split(/($ops|;)/, $opline);
+
+##			print("element count: <" . $#elements . ">\n");
+##			foreach my $el (@elements) {
+##				print("el: <$el>\n");
+##			}
+
+			my @fix_elements = ();
+			my $off = 0;
+
+			foreach my $el (@elements) {
+				push(@fix_elements, substr($rawline, $off, length($el)));
+				$off += length($el);
+			}
+
+			$off = 0;
+
+			my $blank = copy_spacing($opline);
+			my $last_after = -1;
+
+			for (my $n = 0; $n < $#elements; $n += 2) {
+
+				my $good = $fix_elements[$n] . $fix_elements[$n + 1];
+
+##				print("n: <$n> good: <$good>\n");
+
+				$off += length($elements[$n]);
+
+				# Pick up the preceding and succeeding characters.
+				my $ca = substr($opline, 0, $off);
+				my $cc = '';
+				if (length($opline) >= ($off + length($elements[$n + 1]))) {
+					$cc = substr($opline, $off + length($elements[$n + 1]));
+				}
+				my $cb = "$ca$;$cc";
+
+				my $a = '';
+				$a = 'V' if ($elements[$n] ne '');
+				$a = 'W' if ($elements[$n] =~ /\s$/);
+				$a = 'C' if ($elements[$n] =~ /$;$/);
+				$a = 'B' if ($elements[$n] =~ /(\[|\()$/);
+				$a = 'O' if ($elements[$n] eq '');
+				$a = 'E' if ($ca =~ /^\s*$/);
+
+				my $op = $elements[$n + 1];
+
+				my $c = '';
+				if (defined $elements[$n + 2]) {
+					$c = 'V' if ($elements[$n + 2] ne '');
+					$c = 'W' if ($elements[$n + 2] =~ /^\s/);
+					$c = 'C' if ($elements[$n + 2] =~ /^$;/);
+					$c = 'B' if ($elements[$n + 2] =~ /^(\)|\]|;)/);
+					$c = 'O' if ($elements[$n + 2] eq '');
+					$c = 'E' if ($elements[$n + 2] =~ /^\s*\\$/);
+				} else {
+					$c = 'E';
+				}
+
+				my $ctx = "${a}x${c}";
+
+				my $at = "(ctx:$ctx)";
+
+				my $ptr = substr($blank, 0, $off) . "^";
+				my $hereptr = "$hereline$ptr\n";
+
+				# Pull out the value of this operator.
+				my $op_type = substr($curr_values, $off + 1, 1);
+
+				# Get the full operator variant.
+				my $opv = $op . substr($curr_vars, $off, 1);
+
+				# Ignore operators passed as parameters.
+				if ($op_type ne 'V' &&
+				    $ca =~ /\s$/ && $cc =~ /^\s*[,\)]/) {
+
+#				# Ignore comments
+#				} elsif ($op =~ /^$;+$/) {
+
+				# ; should have either the end of line or a space or \ after it
+				} elsif ($op eq ';') {
+					if ($ctx !~ /.x[WEBC]/ &&
+					    $cc !~ /^\\/ && $cc !~ /^;/) {
+						if (ERROR("SPACING",
+							  "space required after that '$op' $at\n" . $hereptr)) {
+							$good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " ";
+							$line_fixed = 1;
+						}
+					}
+
+				# // is a comment
+				} elsif ($op eq '//') {
+
+				#   :   when part of a bitfield
+				} elsif ($opv eq ':B') {
+					# skip the bitfield test for now
+
+				# No spaces for:
+				#   ->
+				} elsif ($op eq '->') {
+					if ($ctx =~ /Wx.|.xW/) {
+						if (ERROR("SPACING",
+							  "spaces prohibited around that '$op' $at\n" . $hereptr)) {
+							$good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]);
+							if (defined $fix_elements[$n + 2]) {
+								$fix_elements[$n + 2] =~ s/^\s+//;
+							}
+							$line_fixed = 1;
+						}
+					}
+
+				# , must not have a space before and must have a space on the right.
+				} elsif ($op eq ',') {
+					my $rtrim_before = 0;
+					my $space_after = 0;
+					if ($ctx =~ /Wx./) {
+						if (ERROR("SPACING",
+							  "space prohibited before that '$op' $at\n" . $hereptr)) {
+							$line_fixed = 1;
+							$rtrim_before = 1;
+						}
+					}
+					if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/) {
+						if (ERROR("SPACING",
+							  "space required after that '$op' $at\n" . $hereptr)) {
+							$line_fixed = 1;
+							$last_after = $n;
+							$space_after = 1;
+						}
+					}
+					if ($rtrim_before || $space_after) {
+						if ($rtrim_before) {
+							$good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]);
+						} else {
+							$good = $fix_elements[$n] . trim($fix_elements[$n + 1]);
+						}
+						if ($space_after) {
+							$good .= " ";
+						}
+					}
+
+				# '*' as part of a type definition -- reported already.
+				} elsif ($opv eq '*_') {
+					#warn "'*' is part of type\n";
+
+				# unary operators should have a space before and
+				# none after.  May be left adjacent to another
+				# unary operator, or a cast
+				} elsif ($op eq '!' || $op eq '~' ||
+					 $opv eq '*U' || $opv eq '-U' ||
+					 $opv eq '&U' || $opv eq '&&U') {
+					if ($ctx !~ /[WEBC]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) {
+						if (ERROR("SPACING",
+							  "space required before that '$op' $at\n" . $hereptr)) {
+							if ($n != $last_after + 2) {
+								$good = $fix_elements[$n] . " " . ltrim($fix_elements[$n + 1]);
+								$line_fixed = 1;
+							}
+						}
+					}
+					if ($op eq '*' && $cc =~/\s*$Modifier\b/) {
+						# A unary '*' may be const
+
+					} elsif ($ctx =~ /.xW/) {
+						if (ERROR("SPACING",
+							  "space prohibited after that '$op' $at\n" . $hereptr)) {
+							$good = $fix_elements[$n] . rtrim($fix_elements[$n + 1]);
+							if (defined $fix_elements[$n + 2]) {
+								$fix_elements[$n + 2] =~ s/^\s+//;
+							}
+							$line_fixed = 1;
+						}
+					}
+
+				# unary ++ and unary -- are allowed no space on one side.
+				} elsif ($op eq '++' or $op eq '--') {
+					if ($ctx !~ /[WEOBC]x[^W]/ && $ctx !~ /[^W]x[WOBEC]/) {
+						if (ERROR("SPACING",
+							  "space required one side of that '$op' $at\n" . $hereptr)) {
+							$good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " ";
+							$line_fixed = 1;
+						}
+					}
+					if ($ctx =~ /Wx[BE]/ ||
+					    ($ctx =~ /Wx./ && $cc =~ /^;/)) {
+						if (ERROR("SPACING",
+							  "space prohibited before that '$op' $at\n" . $hereptr)) {
+							$good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]);
+							$line_fixed = 1;
+						}
+					}
+					if ($ctx =~ /ExW/) {
+						if (ERROR("SPACING",
+							  "space prohibited after that '$op' $at\n" . $hereptr)) {
+							$good = $fix_elements[$n] . trim($fix_elements[$n + 1]);
+							if (defined $fix_elements[$n + 2]) {
+								$fix_elements[$n + 2] =~ s/^\s+//;
+							}
+							$line_fixed = 1;
+						}
+					}
+
+				# << and >> may either have or not have spaces both sides
+				} elsif ($op eq '<<' or $op eq '>>' or
+					 $op eq '&' or $op eq '^' or $op eq '|' or
+					 $op eq '+' or $op eq '-' or
+					 $op eq '*' or $op eq '/' or
+					 $op eq '%')
+				{
+					if ($check) {
+						if (defined $fix_elements[$n + 2] && $ctx !~ /[EW]x[EW]/) {
+							if (CHK("SPACING",
+								"spaces preferred around that '$op' $at\n" . $hereptr)) {
+								$good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " ";
+								$fix_elements[$n + 2] =~ s/^\s+//;
+								$line_fixed = 1;
+							}
+						} elsif (!defined $fix_elements[$n + 2] && $ctx !~ /Wx[OE]/) {
+							if (CHK("SPACING",
+								"space preferred before that '$op' $at\n" . $hereptr)) {
+								$good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]);
+								$line_fixed = 1;
+							}
+						}
+					} elsif ($ctx =~ /Wx[^WCE]|[^WCE]xW/) {
+						if (ERROR("SPACING",
+							  "need consistent spacing around '$op' $at\n" . $hereptr)) {
+							$good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " ";
+							if (defined $fix_elements[$n + 2]) {
+								$fix_elements[$n + 2] =~ s/^\s+//;
+							}
+							$line_fixed = 1;
+						}
+					}
+
+				# A colon needs no spaces before when it is
+				# terminating a case value or a label.
+				} elsif ($opv eq ':C' || $opv eq ':L') {
+					if ($ctx =~ /Wx./ and $realfile !~ m@.*\.lds\.h$@) {
+						if (ERROR("SPACING",
+							  "space prohibited before that '$op' $at\n" . $hereptr)) {
+							$good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]);
+							$line_fixed = 1;
+						}
+					}
+
+				# All the others need spaces both sides.
+				} elsif ($ctx !~ /[EWC]x[CWE]/) {
+					my $ok = 0;
+
+					# Ignore email addresses <foo@bar>
+					if (($op eq '<' &&
+					     $cc =~ /^\S+\@\S+>/) ||
+					    ($op eq '>' &&
+					     $ca =~ /<\S+\@\S+$/))
+					{
+						$ok = 1;
+					}
+
+					# for asm volatile statements
+					# ignore a colon with another
+					# colon immediately before or after
+					if (($op eq ':') &&
+					    ($ca =~ /:$/ || $cc =~ /^:/)) {
+						$ok = 1;
+					}
+
+					# messages are ERROR, but ?: are CHK
+					if ($ok == 0) {
+						my $msg_level = \&ERROR;
+						$msg_level = \&CHK if (($op eq '?:' || $op eq '?' || $op eq ':') && $ctx =~ /VxV/);
+
+						if (&{$msg_level}("SPACING",
+								  "spaces required around that '$op' $at\n" . $hereptr)) {
+							$good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " ";
+							if (defined $fix_elements[$n + 2]) {
+								$fix_elements[$n + 2] =~ s/^\s+//;
+							}
+							$line_fixed = 1;
+						}
+					}
+				}
+				$off += length($elements[$n + 1]);
+
+##				print("n: <$n> GOOD: <$good>\n");
+
+				$fixed_line = $fixed_line . $good;
+			}
+
+			if (($#elements % 2) == 0) {
+				$fixed_line = $fixed_line . $fix_elements[$#elements];
+			}
+
+			if ($fix && $line_fixed && $fixed_line ne $fixed[$fixlinenr]) {
+				$fixed[$fixlinenr] = $fixed_line;
+			}
+
+
+		}
+
+# check for whitespace before a non-naked semicolon
+		if ($line =~ /^\+.*\S\s+;\s*$/) {
+			if (WARN("SPACING",
+				 "space prohibited before semicolon\n" . $herecurr) &&
+			    $fix) {
+				1 while $fixed[$fixlinenr] =~
+				    s/^(\+.*\S)\s+;/$1;/;
+			}
+		}
+
+# check for multiple assignments
+		if ($line =~ /^.\s*$Lval\s*=\s*$Lval\s*=(?!=)/) {
+			CHK("MULTIPLE_ASSIGNMENTS",
+			    "multiple assignments should be avoided\n" . $herecurr);
+		}
+
+## # check for multiple declarations, allowing for a function declaration
+## # continuation.
+## 		if ($line =~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Ident.*/ &&
+## 		    $line !~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Type\s*$Ident.*/) {
+##
+## 			# Remove any bracketed sections to ensure we do not
+## 			# falsely report the parameters of functions.
+## 			my $ln = $line;
+## 			while ($ln =~ s/\([^\(\)]*\)//g) {
+## 			}
+## 			if ($ln =~ /,/) {
+## 				WARN("MULTIPLE_DECLARATION",
+##				     "declaring multiple variables together should be avoided\n" . $herecurr);
+## 			}
+## 		}
+
+#need space before brace following if, while, etc
+		if (($line =~ /\(.*\)\{/ && $line !~ /\($Type\)\{/) ||
+		    $line =~ /\b(?:else|do)\{/) {
+			if (ERROR("SPACING",
+				  "space required before the open brace '{'\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/^(\+.*(?:do|else|\)))\{/$1 {/;
+			}
+		}
+
+## # check for blank lines before declarations
+##		if ($line =~ /^.\t+$Type\s+$Ident(?:\s*=.*)?;/ &&
+##		    $prevrawline =~ /^.\s*$/) {
+##			WARN("SPACING",
+##			     "No blank lines before declarations\n" . $hereprev);
+##		}
+##
+
+# closing brace should have a space following it when it has anything
+# on the line
+		if ($line =~ /}(?!(?:,|;|\)|\}))\S/) {
+			if (ERROR("SPACING",
+				  "space required after that close brace '}'\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~
+				    s/}((?!(?:,|;|\)))\S)/} $1/;
+			}
+		}
+
+# check spacing on square brackets
+		if ($line =~ /\[\s/ && $line !~ /\[\s*$/) {
+			if (ERROR("SPACING",
+				  "space prohibited after that open square bracket '['\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~
+				    s/\[\s+/\[/;
+			}
+		}
+		if ($line =~ /\s\]/) {
+			if (ERROR("SPACING",
+				  "space prohibited before that close square bracket ']'\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~
+				    s/\s+\]/\]/;
+			}
+		}
+
+# check spacing on parentheses
+		if ($line =~ /\(\s/ && $line !~ /\(\s*(?:\\)?$/ &&
+		    $line !~ /for\s*\(\s+;/) {
+			if (ERROR("SPACING",
+				  "space prohibited after that open parenthesis '('\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~
+				    s/\(\s+/\(/;
+			}
+		}
+		if ($line =~ /(\s+)\)/ && $line !~ /^.\s*\)/ &&
+		    $line !~ /for\s*\(.*;\s+\)/ &&
+		    $line !~ /:\s+\)/) {
+			if (ERROR("SPACING",
+				  "space prohibited before that close parenthesis ')'\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~
+				    s/\s+\)/\)/;
+			}
+		}
+
+# check unnecessary parentheses around addressof/dereference single $Lvals
+# ie: &(foo->bar) should be &foo->bar and *(foo->bar) should be *foo->bar
+
+		while ($line =~ /(?:[^&]&\s*|\*)\(\s*($Ident\s*(?:$Member\s*)+)\s*\)/g) {
+			my $var = $1;
+			if (CHK("UNNECESSARY_PARENTHESES",
+				"Unnecessary parentheses around $var\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\(\s*\Q$var\E\s*\)/$var/;
+			}
+		}
+
+# check for unnecessary parentheses around function pointer uses
+# ie: (foo->bar)(); should be foo->bar();
+# but not "if (foo->bar) (" to avoid some false positives
+		if ($line =~ /(\bif\s*|)(\(\s*$Ident\s*(?:$Member\s*)+\))[ \t]*\(/ && $1 !~ /^if/) {
+			my $var = $2;
+			if (CHK("UNNECESSARY_PARENTHESES",
+				"Unnecessary parentheses around function pointer $var\n" . $herecurr) &&
+			    $fix) {
+				my $var2 = deparenthesize($var);
+				$var2 =~ s/\s//g;
+				$fixed[$fixlinenr] =~ s/\Q$var\E/$var2/;
+			}
+		}
+
+# check for unnecessary parentheses around comparisons in if uses
+# when !drivers/staging or command-line uses --strict
+		if (($realfile !~ m@^(?:drivers/staging/)@ || $check_orig) &&
+		    $perl_version_ok && defined($stat) &&
+		    $stat =~ /(^.\s*if\s*($balanced_parens))/) {
+			my $if_stat = $1;
+			my $test = substr($2, 1, -1);
+			my $herectx;
+			while ($test =~ /(?:^|[^\w\&\!\~])+\s*\(\s*([\&\!\~]?\s*$Lval\s*(?:$Compare\s*$FuncArg)?)\s*\)/g) {
+				my $match = $1;
+				# avoid parentheses around potential macro args
+				next if ($match =~ /^\s*\w+\s*$/);
+				if (!defined($herectx)) {
+					$herectx = $here . "\n";
+					my $cnt = statement_rawlines($if_stat);
+					for (my $n = 0; $n < $cnt; $n++) {
+						my $rl = raw_line($linenr, $n);
+						$herectx .=  $rl . "\n";
+						last if $rl =~ /^[ \+].*\{/;
+					}
+				}
+				CHK("UNNECESSARY_PARENTHESES",
+				    "Unnecessary parentheses around '$match'\n" . $herectx);
+			}
+		}
+
+# check that goto labels aren't indented (allow a single space indentation)
+# and ignore bitfield definitions like foo:1
+# Strictly, labels can have whitespace after the identifier and before the :
+# but this is not allowed here as many ?: uses would appear to be labels
+		if ($sline =~ /^.\s+[A-Za-z_][A-Za-z\d_]*:(?!\s*\d+)/ &&
+		    $sline !~ /^. [A-Za-z\d_][A-Za-z\d_]*:/ &&
+		    $sline !~ /^.\s+default:/) {
+			if (WARN("INDENTED_LABEL",
+				 "labels should not be indented\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~
+				    s/^(.)\s+/$1/;
+			}
+		}
+
+# check if a statement with a comma should be two statements like:
+#	foo = bar(),	/* comma should be semicolon */
+#	bar = baz();
+		if (defined($stat) &&
+		    $stat =~ /^\+\s*(?:$Lval\s*$Assignment\s*)?$FuncArg\s*,\s*(?:$Lval\s*$Assignment\s*)?$FuncArg\s*;\s*$/) {
+			my $cnt = statement_rawlines($stat);
+			my $herectx = get_stat_here($linenr, $cnt, $here);
+			WARN("SUSPECT_COMMA_SEMICOLON",
+			     "Possible comma where semicolon could be used\n" . $herectx);
+		}
+
+# return is not a function
+		if (defined($stat) && $stat =~ /^.\s*return(\s*)\(/s) {
+			my $spacing = $1;
+			if ($perl_version_ok &&
+			    $stat =~ /^.\s*return\s*($balanced_parens)\s*;\s*$/) {
+				my $value = $1;
+				$value = deparenthesize($value);
+				if ($value =~ m/^\s*$FuncArg\s*(?:\?|$)/) {
+					ERROR("RETURN_PARENTHESES",
+					      "return is not a function, parentheses are not required\n" . $herecurr);
+				}
+			} elsif ($spacing !~ /\s+/) {
+				ERROR("SPACING",
+				      "space required before the open parenthesis '('\n" . $herecurr);
+			}
+		}
+
+# unnecessary return in a void function
+# at end-of-function, with the previous line a single leading tab, then return;
+# and the line before that not a goto label target like "out:"
+		if ($sline =~ /^[ \+]}\s*$/ &&
+		    $prevline =~ /^\+\treturn\s*;\s*$/ &&
+		    $linenr >= 3 &&
+		    $lines[$linenr - 3] =~ /^[ +]/ &&
+		    $lines[$linenr - 3] !~ /^[ +]\s*$Ident\s*:/) {
+			WARN("RETURN_VOID",
+			     "void function return statements are not generally useful\n" . $hereprev);
+		}
+
+# if statements using unnecessary parentheses - ie: if ((foo == bar))
+		if ($perl_version_ok &&
+		    $line =~ /\bif\s*((?:\(\s*){2,})/) {
+			my $openparens = $1;
+			my $count = $openparens =~ tr@\(@\(@;
+			my $msg = "";
+			if ($line =~ /\bif\s*(?:\(\s*){$count,$count}$LvalOrFunc\s*($Compare)\s*$LvalOrFunc(?:\s*\)){$count,$count}/) {
+				my $comp = $4;	#Not $1 because of $LvalOrFunc
+				$msg = " - maybe == should be = ?" if ($comp eq "==");
+				WARN("UNNECESSARY_PARENTHESES",
+				     "Unnecessary parentheses$msg\n" . $herecurr);
+			}
+		}
+
+# comparisons with a constant or upper case identifier on the left
+#	avoid cases like "foo + BAR < baz"
+#	only fix matches surrounded by parentheses to avoid incorrect
+#	conversions like "FOO < baz() + 5" being "misfixed" to "baz() > FOO + 5"
+		if ($perl_version_ok &&
+		    $line =~ /^\+(.*)\b($Constant|[A-Z_][A-Z0-9_]*)\s*($Compare)\s*($LvalOrFunc)/) {
+			my $lead = $1;
+			my $const = $2;
+			my $comp = $3;
+			my $to = $4;
+			my $newcomp = $comp;
+			if ($lead !~ /(?:$Operators|\.)\s*$/ &&
+			    $to !~ /^(?:Constant|[A-Z_][A-Z0-9_]*)$/ &&
+			    WARN("CONSTANT_COMPARISON",
+				 "Comparisons should place the constant on the right side of the test\n" . $herecurr) &&
+			    $fix) {
+				if ($comp eq "<") {
+					$newcomp = ">";
+				} elsif ($comp eq "<=") {
+					$newcomp = ">=";
+				} elsif ($comp eq ">") {
+					$newcomp = "<";
+				} elsif ($comp eq ">=") {
+					$newcomp = "<=";
+				}
+				$fixed[$fixlinenr] =~ s/\(\s*\Q$const\E\s*$Compare\s*\Q$to\E\s*\)/($to $newcomp $const)/;
+			}
+		}
+
+# Return of what appears to be an errno should normally be negative
+		if ($sline =~ /\breturn(?:\s*\(+\s*|\s+)(E[A-Z]+)(?:\s*\)+\s*|\s*)[;:,]/) {
+			my $name = $1;
+			if ($name ne 'EOF' && $name ne 'ERROR' && $name !~ /^EPOLL/) {
+				WARN("USE_NEGATIVE_ERRNO",
+				     "return of an errno should typically be negative (ie: return -$1)\n" . $herecurr);
+			}
+		}
+
+# Need a space before open parenthesis after if, while etc
+		if ($line =~ /\b(if|while|for|switch)\(/) {
+			if (ERROR("SPACING",
+				  "space required before the open parenthesis '('\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~
+				    s/\b(if|while|for|switch)\(/$1 \(/;
+			}
+		}
+
+# Check for illegal assignment in if conditional -- and check for trailing
+# statements after the conditional.
+		if ($line =~ /do\s*(?!{)/) {
+			($stat, $cond, $line_nr_next, $remain_next, $off_next) =
+				ctx_statement_block($linenr, $realcnt, 0)
+					if (!defined $stat);
+			my ($stat_next) = ctx_statement_block($line_nr_next,
+						$remain_next, $off_next);
+			$stat_next =~ s/\n./\n /g;
+			##print "stat<$stat> stat_next<$stat_next>\n";
+
+			if ($stat_next =~ /^\s*while\b/) {
+				# If the statement carries leading newlines,
+				# then count those as offsets.
+				my ($whitespace) =
+					($stat_next =~ /^((?:\s*\n[+-])*\s*)/s);
+				my $offset =
+					statement_rawlines($whitespace) - 1;
+
+				$suppress_whiletrailers{$line_nr_next +
+								$offset} = 1;
+			}
+		}
+		if (!defined $suppress_whiletrailers{$linenr} &&
+		    defined($stat) && defined($cond) &&
+		    $line =~ /\b(?:if|while|for)\s*\(/ && $line !~ /^.\s*#/) {
+			my ($s, $c) = ($stat, $cond);
+
+			if ($c =~ /\bif\s*\(.*[^<>!=]=[^=].*/s) {
+				if (ERROR("ASSIGN_IN_IF",
+					  "do not use assignment in if condition\n" . $herecurr) &&
+				    $fix && $perl_version_ok) {
+					if ($rawline =~ /^\+(\s+)if\s*\(\s*(\!)?\s*\(\s*(($Lval)\s*=\s*$LvalOrFunc)\s*\)\s*(?:($Compare)\s*($FuncArg))?\s*\)\s*(\{)?\s*$/) {
+						my $space = $1;
+						my $not = $2;
+						my $statement = $3;
+						my $assigned = $4;
+						my $test = $8;
+						my $against = $9;
+						my $brace = $15;
+						fix_delete_line($fixlinenr, $rawline);
+						fix_insert_line($fixlinenr, "$space$statement;");
+						my $newline = "${space}if (";
+						$newline .= '!' if defined($not);
+						$newline .= '(' if (defined $not && defined($test) && defined($against));
+						$newline .= "$assigned";
+						$newline .= " $test $against" if (defined($test) && defined($against));
+						$newline .= ')' if (defined $not && defined($test) && defined($against));
+						$newline .= ')';
+						$newline .= " {" if (defined($brace));
+						fix_insert_line($fixlinenr + 1, $newline);
+					}
+				}
+			}
+
+			# Find out what is on the end of the line after the
+			# conditional.
+			substr($s, 0, length($c), '');
+			$s =~ s/\n.*//g;
+			$s =~ s/$;//g;	# Remove any comments
+			if (length($c) && $s !~ /^\s*{?\s*\\*\s*$/ &&
+			    $c !~ /}\s*while\s*/)
+			{
+				# Find out how long the conditional actually is.
+				my @newlines = ($c =~ /\n/gs);
+				my $cond_lines = 1 + $#newlines;
+				my $stat_real = '';
+
+				$stat_real = raw_line($linenr, $cond_lines)
+							. "\n" if ($cond_lines);
+				if (defined($stat_real) && $cond_lines > 1) {
+					$stat_real = "[...]\n$stat_real";
+				}
+
+				ERROR("TRAILING_STATEMENTS",
+				      "trailing statements should be on next line\n" . $herecurr . $stat_real);
+			}
+		}
+
+# Check for bitwise tests written as boolean
+		if ($line =~ /
+			(?:
+				(?:\[|\(|\&\&|\|\|)
+				\s*0[xX][0-9]+\s*
+				(?:\&\&|\|\|)
+			|
+				(?:\&\&|\|\|)
+				\s*0[xX][0-9]+\s*
+				(?:\&\&|\|\||\)|\])
+			)/x)
+		{
+			WARN("HEXADECIMAL_BOOLEAN_TEST",
+			     "boolean test with hexadecimal, perhaps just 1 \& or \|?\n" . $herecurr);
+		}
+
+# if and else should not have general statements after it
+		if ($line =~ /^.\s*(?:}\s*)?else\b(.*)/) {
+			my $s = $1;
+			$s =~ s/$;//g;	# Remove any comments
+			if ($s !~ /^\s*(?:\sif|(?:{|)\s*\\?\s*$)/) {
+				ERROR("TRAILING_STATEMENTS",
+				      "trailing statements should be on next line\n" . $herecurr);
+			}
+		}
+# if should not continue a brace
+		if ($line =~ /}\s*if\b/) {
+			ERROR("TRAILING_STATEMENTS",
+			      "trailing statements should be on next line (or did you mean 'else if'?)\n" .
+				$herecurr);
+		}
+# case and default should not have general statements after them
+		if ($line =~ /^.\s*(?:case\s*.*|default\s*):/g &&
+		    $line !~ /\G(?:
+			(?:\s*$;*)(?:\s*{)?(?:\s*$;*)(?:\s*\\)?\s*$|
+			\s*return\s+
+		    )/xg)
+		{
+			ERROR("TRAILING_STATEMENTS",
+			      "trailing statements should be on next line\n" . $herecurr);
+		}
+
+		# Check for }<nl>else {, these must be at the same
+		# indent level to be relevant to each other.
+		if ($prevline=~/}\s*$/ and $line=~/^.\s*else\s*/ &&
+		    $previndent == $indent) {
+			if (ERROR("ELSE_AFTER_BRACE",
+				  "else should follow close brace '}'\n" . $hereprev) &&
+			    $fix && $prevline =~ /^\+/ && $line =~ /^\+/) {
+				fix_delete_line($fixlinenr - 1, $prevrawline);
+				fix_delete_line($fixlinenr, $rawline);
+				my $fixedline = $prevrawline;
+				$fixedline =~ s/}\s*$//;
+				if ($fixedline !~ /^\+\s*$/) {
+					fix_insert_line($fixlinenr, $fixedline);
+				}
+				$fixedline = $rawline;
+				$fixedline =~ s/^(.\s*)else/$1} else/;
+				fix_insert_line($fixlinenr, $fixedline);
+			}
+		}
+
+		if ($prevline=~/}\s*$/ and $line=~/^.\s*while\s*/ &&
+		    $previndent == $indent) {
+			my ($s, $c) = ctx_statement_block($linenr, $realcnt, 0);
+
+			# Find out what is on the end of the line after the
+			# conditional.
+			substr($s, 0, length($c), '');
+			$s =~ s/\n.*//g;
+
+			if ($s =~ /^\s*;/) {
+				if (ERROR("WHILE_AFTER_BRACE",
+					  "while should follow close brace '}'\n" . $hereprev) &&
+				    $fix && $prevline =~ /^\+/ && $line =~ /^\+/) {
+					fix_delete_line($fixlinenr - 1, $prevrawline);
+					fix_delete_line($fixlinenr, $rawline);
+					my $fixedline = $prevrawline;
+					my $trailing = $rawline;
+					$trailing =~ s/^\+//;
+					$trailing = trim($trailing);
+					$fixedline =~ s/}\s*$/} $trailing/;
+					fix_insert_line($fixlinenr, $fixedline);
+				}
+			}
+		}
+
+#Specific variable tests
+		while ($line =~ m{($Constant|$Lval)}g) {
+			my $var = $1;
+
+#CamelCase
+			if ($var !~ /^$Constant$/ &&
+			    $var =~ /[A-Z][a-z]|[a-z][A-Z]/ &&
+#Ignore some autogenerated defines and enum values
+			    $var !~ /^(?:[A-Z]+_){1,5}[A-Z]{1,3}[a-z]/ &&
+#Ignore Page<foo> variants
+			    $var !~ /^(?:Clear|Set|TestClear|TestSet|)Page[A-Z]/ &&
+#Ignore SI style variants like nS, mV and dB
+#(ie: max_uV, regulator_min_uA_show, RANGE_mA_VALUE)
+			    $var !~ /^(?:[a-z0-9_]*|[A-Z0-9_]*)?_?[a-z][A-Z](?:_[a-z0-9_]+|_[A-Z0-9_]+)?$/ &&
+#Ignore some three character SI units explicitly, like MiB and KHz
+			    $var !~ /^(?:[a-z_]*?)_?(?:[KMGT]iB|[KMGT]?Hz)(?:_[a-z_]+)?$/) {
+				while ($var =~ m{($Ident)}g) {
+					my $word = $1;
+					next if ($word !~ /[A-Z][a-z]|[a-z][A-Z]/);
+					if ($check) {
+						seed_camelcase_includes();
+						if (!$file && !$camelcase_file_seeded) {
+							seed_camelcase_file($realfile);
+							$camelcase_file_seeded = 1;
+						}
+					}
+					if (!defined $camelcase{$word}) {
+						$camelcase{$word} = 1;
+						CHK("CAMELCASE",
+						    "Avoid CamelCase: <$word>\n" . $herecurr);
+					}
+				}
+			}
+		}
+
+#no spaces allowed after \ in define
+		if ($line =~ /\#\s*define.*\\\s+$/) {
+			if (WARN("WHITESPACE_AFTER_LINE_CONTINUATION",
+				 "Whitespace after \\ makes next lines useless\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\s+$//;
+			}
+		}
+
+# warn if <asm/foo.h> is #included and <linux/foo.h> is available and includes
+# itself <asm/foo.h> (uses RAW line)
+		if ($tree && $rawline =~ m{^.\s*\#\s*include\s*\<asm\/(.*)\.h\>}) {
+			my $file = "$1.h";
+			my $checkfile = "include/linux/$file";
+			if (-f "$root/$checkfile" &&
+			    $realfile ne $checkfile &&
+			    $1 !~ /$allowed_asm_includes/)
+			{
+				my $asminclude = `grep -Ec "#include\\s+<asm/$file>" $root/$checkfile`;
+				if ($asminclude > 0) {
+					if ($realfile =~ m{^arch/}) {
+						CHK("ARCH_INCLUDE_LINUX",
+						    "Consider using #include <linux/$file> instead of <asm/$file>\n" . $herecurr);
+					} else {
+						WARN("INCLUDE_LINUX",
+						     "Use #include <linux/$file> instead of <asm/$file>\n" . $herecurr);
+					}
+				}
+			}
+		}
+
+# multi-statement macros should be enclosed in a do while loop, grab the
+# first statement and ensure its the whole macro if its not enclosed
+# in a known good container
+		if ($realfile !~ m@/vmlinux.lds.h$@ &&
+		    $line =~ /^.\s*\#\s*define\s*$Ident(\()?/) {
+			my $ln = $linenr;
+			my $cnt = $realcnt;
+			my ($off, $dstat, $dcond, $rest);
+			my $ctx = '';
+			my $has_flow_statement = 0;
+			my $has_arg_concat = 0;
+			($dstat, $dcond, $ln, $cnt, $off) =
+				ctx_statement_block($linenr, $realcnt, 0);
+			$ctx = $dstat;
+			#print "dstat<$dstat> dcond<$dcond> cnt<$cnt> off<$off>\n";
+			#print "LINE<$lines[$ln-1]> len<" . length($lines[$ln-1]) . "\n";
+
+			$has_flow_statement = 1 if ($ctx =~ /\b(goto|return)\b/);
+			$has_arg_concat = 1 if ($ctx =~ /\#\#/ && $ctx !~ /\#\#\s*(?:__VA_ARGS__|args)\b/);
+
+			$dstat =~ s/^.\s*\#\s*define\s+$Ident(\([^\)]*\))?\s*//;
+			my $define_args = $1;
+			my $define_stmt = $dstat;
+			my @def_args = ();
+
+			if (defined $define_args && $define_args ne "") {
+				$define_args = substr($define_args, 1, length($define_args) - 2);
+				$define_args =~ s/\s*//g;
+				$define_args =~ s/\\\+?//g;
+				@def_args = split(",", $define_args);
+			}
+
+			$dstat =~ s/$;//g;
+			$dstat =~ s/\\\n.//g;
+			$dstat =~ s/^\s*//s;
+			$dstat =~ s/\s*$//s;
+
+			# Flatten any parentheses and braces
+			while ($dstat =~ s/\([^\(\)]*\)/1u/ ||
+			       $dstat =~ s/\{[^\{\}]*\}/1u/ ||
+			       $dstat =~ s/.\[[^\[\]]*\]/1u/)
+			{
+			}
+
+			# Flatten any obvious string concatenation.
+			while ($dstat =~ s/($String)\s*$Ident/$1/ ||
+			       $dstat =~ s/$Ident\s*($String)/$1/)
+			{
+			}
+
+			# Make asm volatile uses seem like a generic function
+			$dstat =~ s/\b_*asm_*\s+_*volatile_*\b/asm_volatile/g;
+
+			my $exceptions = qr{
+				$Declare|
+				module_param_named|
+				MODULE_PARM_DESC|
+				DECLARE_PER_CPU|
+				DEFINE_PER_CPU|
+				__typeof__\(|
+				union|
+				struct|
+				\.$Ident\s*=\s*|
+				^\"|\"$|
+				^\[
+			}x;
+			#print "REST<$rest> dstat<$dstat> ctx<$ctx>\n";
+
+			$ctx =~ s/\n*$//;
+			my $stmt_cnt = statement_rawlines($ctx);
+			my $herectx = get_stat_here($linenr, $stmt_cnt, $here);
+
+			if ($dstat ne '' &&
+			    $dstat !~ /^(?:$Ident|-?$Constant),$/ &&			# 10, // foo(),
+			    $dstat !~ /^(?:$Ident|-?$Constant);$/ &&			# foo();
+			    $dstat !~ /^[!~-]?(?:$Lval|$Constant)$/ &&		# 10 // foo() // !foo // ~foo // -foo // foo->bar // foo.bar->baz
+			    $dstat !~ /^'X'$/ && $dstat !~ /^'XX'$/ &&			# character constants
+			    $dstat !~ /$exceptions/ &&
+			    $dstat !~ /^\.$Ident\s*=/ &&				# .foo =
+			    $dstat !~ /^(?:\#\s*$Ident|\#\s*$Constant)\s*$/ &&		# stringification #foo
+			    $dstat !~ /^do\s*$Constant\s*while\s*$Constant;?$/ &&	# do {...} while (...); // do {...} while (...)
+			    $dstat !~ /^while\s*$Constant\s*$Constant\s*$/ &&		# while (...) {...}
+			    $dstat !~ /^for\s*$Constant$/ &&				# for (...)
+			    $dstat !~ /^for\s*$Constant\s+(?:$Ident|-?$Constant)$/ &&	# for (...) bar()
+			    $dstat !~ /^do\s*{/ &&					# do {...
+			    $dstat !~ /^\(\{/ &&						# ({...
+			    $ctx !~ /^.\s*#\s*define\s+TRACE_(?:SYSTEM|INCLUDE_FILE|INCLUDE_PATH)\b/)
+			{
+				if ($dstat =~ /^\s*if\b/) {
+					ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE",
+					      "Macros starting with if should be enclosed by a do - while loop to avoid possible if/else logic defects\n" . "$herectx");
+				} elsif ($dstat =~ /;/) {
+					ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE",
+					      "Macros with multiple statements should be enclosed in a do - while loop\n" . "$herectx");
+				} else {
+					ERROR("COMPLEX_MACRO",
+					      "Macros with complex values should be enclosed in parentheses\n" . "$herectx");
+				}
+
+			}
+
+			# Make $define_stmt single line, comment-free, etc
+			my @stmt_array = split('\n', $define_stmt);
+			my $first = 1;
+			$define_stmt = "";
+			foreach my $l (@stmt_array) {
+				$l =~ s/\\$//;
+				if ($first) {
+					$define_stmt = $l;
+					$first = 0;
+				} elsif ($l =~ /^[\+ ]/) {
+					$define_stmt .= substr($l, 1);
+				}
+			}
+			$define_stmt =~ s/$;//g;
+			$define_stmt =~ s/\s+/ /g;
+			$define_stmt = trim($define_stmt);
+
+# check if any macro arguments are reused (ignore '...' and 'type')
+			foreach my $arg (@def_args) {
+			        next if ($arg =~ /\.\.\./);
+			        next if ($arg =~ /^type$/i);
+				my $tmp_stmt = $define_stmt;
+				$tmp_stmt =~ s/\b(__must_be_array|offsetof|sizeof|sizeof_field|__stringify|typeof|__typeof__|__builtin\w+|typecheck\s*\(\s*$Type\s*,|\#+)\s*\(*\s*$arg\s*\)*\b//g;
+				$tmp_stmt =~ s/\#+\s*$arg\b//g;
+				$tmp_stmt =~ s/\b$arg\s*\#\#//g;
+				my $use_cnt = () = $tmp_stmt =~ /\b$arg\b/g;
+				if ($use_cnt > 1) {
+					CHK("MACRO_ARG_REUSE",
+					    "Macro argument reuse '$arg' - possible side-effects?\n" . "$herectx");
+				    }
+# check if any macro arguments may have other precedence issues
+				if ($tmp_stmt =~ m/($Operators)?\s*\b$arg\b\s*($Operators)?/m &&
+				    ((defined($1) && $1 ne ',') ||
+				     (defined($2) && $2 ne ','))) {
+					CHK("MACRO_ARG_PRECEDENCE",
+					    "Macro argument '$arg' may be better as '($arg)' to avoid precedence issues\n" . "$herectx");
+				}
+			}
+
+# check for macros with flow control, but without ## concatenation
+# ## concatenation is commonly a macro that defines a function so ignore those
+			if ($has_flow_statement && !$has_arg_concat) {
+				my $cnt = statement_rawlines($ctx);
+				my $herectx = get_stat_here($linenr, $cnt, $here);
+
+				WARN("MACRO_WITH_FLOW_CONTROL",
+				     "Macros with flow control statements should be avoided\n" . "$herectx");
+			}
+
+# check for line continuations outside of #defines, preprocessor #, and asm
+
+		} else {
+			if ($prevline !~ /^..*\\$/ &&
+			    $line !~ /^\+\s*\#.*\\$/ &&		# preprocessor
+			    $line !~ /^\+.*\b(__asm__|asm)\b.*\\$/ &&	# asm
+			    $line =~ /^\+.*\\$/) {
+				WARN("LINE_CONTINUATIONS",
+				     "Avoid unnecessary line continuations\n" . $herecurr);
+			}
+		}
+
+# do {} while (0) macro tests:
+# single-statement macros do not need to be enclosed in do while (0) loop,
+# macro should not end with a semicolon
+		if ($perl_version_ok &&
+		    $realfile !~ m@/vmlinux.lds.h$@ &&
+		    $line =~ /^.\s*\#\s*define\s+$Ident(\()?/) {
+			my $ln = $linenr;
+			my $cnt = $realcnt;
+			my ($off, $dstat, $dcond, $rest);
+			my $ctx = '';
+			($dstat, $dcond, $ln, $cnt, $off) =
+				ctx_statement_block($linenr, $realcnt, 0);
+			$ctx = $dstat;
+
+			$dstat =~ s/\\\n.//g;
+			$dstat =~ s/$;/ /g;
+
+			if ($dstat =~ /^\+\s*#\s*define\s+$Ident\s*${balanced_parens}\s*do\s*{(.*)\s*}\s*while\s*\(\s*0\s*\)\s*([;\s]*)\s*$/) {
+				my $stmts = $2;
+				my $semis = $3;
+
+				$ctx =~ s/\n*$//;
+				my $cnt = statement_rawlines($ctx);
+				my $herectx = get_stat_here($linenr, $cnt, $here);
+
+				if (($stmts =~ tr/;/;/) == 1 &&
+				    $stmts !~ /^\s*(if|while|for|switch)\b/) {
+					WARN("SINGLE_STATEMENT_DO_WHILE_MACRO",
+					     "Single statement macros should not use a do {} while (0) loop\n" . "$herectx");
+				}
+				if (defined $semis && $semis ne "") {
+					WARN("DO_WHILE_MACRO_WITH_TRAILING_SEMICOLON",
+					     "do {} while (0) macros should not be semicolon terminated\n" . "$herectx");
+				}
+			} elsif ($dstat =~ /^\+\s*#\s*define\s+$Ident.*;\s*$/) {
+				$ctx =~ s/\n*$//;
+				my $cnt = statement_rawlines($ctx);
+				my $herectx = get_stat_here($linenr, $cnt, $here);
+
+				WARN("TRAILING_SEMICOLON",
+				     "macros should not use a trailing semicolon\n" . "$herectx");
+			}
+		}
+
+# check for redundant bracing round if etc
+#
+# RMM: The changes to braces {} rules are taken from QEMU project based on
+# below commits:
+# b646968336 checkpatch: adjust to QEMUisms
+# 2b9aef6fcd checkpatch: add check for `while` and `for`
+#
+		if ($line =~ /(^.*)\b(?:if|while|for)\b/ &&
+			$line !~ /\#\s*if/) {
+			my $allowed = 0;
+
+			# Check the pre-context.
+			if ($line =~ /(\}.*?)$/) {
+				my $pre = $1;
+
+				if ($line !~ /else/) {
+					$allowed = 1;
+				}
+			}
+			my ($level, $endln, @chunks) =
+				ctx_statement_full($linenr, $realcnt, 1);
+			#print "chunks<$#chunks> linenr<$linenr> endln<$endln> level<$level>\n";
+			#print "APW: <<$chunks[1][0]>><<$chunks[1][1]>>\n";
+			if ($#chunks >= 0 && $level == 0) {
+				my $seen = 0;
+				my $herectx = $here . "\n";
+				my $ln = $linenr - 1;
+				for my $chunk (@chunks) {
+					my ($cond, $block) = @{$chunk};
+
+					# If the condition carries leading newlines, then count those as offsets.
+					my ($whitespace) = ($cond =~ /^((?:\s*\n[+-])*\s*)/s);
+					my $offset = statement_rawlines($whitespace) - 1;
+
+					#print "COND<$cond> whitespace<$whitespace> offset<$offset>\n";
+
+					# We have looked at and allowed this specific line.
+					$suppress_ifbraces{$ln + $offset} = 1;
+
+					$herectx .= "$rawlines[$ln + $offset]\n[...]\n";
+					$ln += statement_rawlines($block) - 1;
+
+					substr($block, 0, length($cond), '');
+
+					$seen++ if ($block =~ /^\s*{/);
+
+					#print "cond<$cond> block<$block> allowed<$allowed[$allow]>\n";
+					if (statement_lines($cond) > 1) {
+						#print "APW: ALLOWED: cond<$cond>\n";
+						$allowed = 1;
+					}
+					if ($block =~/\b(?:if|for|while)\b/) {
+						#print "APW: ALLOWED: block<$block>\n";
+						$allowed = 1;
+					}
+					if (statement_block_size($block) > 1) {
+						#print "APW: ALLOWED: lines block<$block>\n";
+						$allowed = 1;
+					}
+				}
+				if ($seen != ($#chunks + 1) && !$allowed) {
+					ERROR("BRACES", "braces {} are necessary for all arms of this statement\n" . $herectx);
+				}
+			}
+		}
+#
+# RMM: The changes to braces {} rules are taken from QEMU project based on
+# below commits:
+# 789f88d0b2 checkpatch: Fix bracing false positives on #else
+# d0510af26d checkpatch: Fix bracing false positives on #if
+# 01c4330b58 checkpatch: fix braces {} handling
+#
+		if (!defined $suppress_ifbraces{$linenr - 1} &&
+					$line =~ /\b(if|while|for|else)\b/ &&
+					$line !~ /\#\s*if/ &&
+					$line !~ /\#\s*else/) {
+			my $allowed = 0;
+
+			# Check the pre-context.
+			if (substr($line, 0, $-[0]) =~ /(\}\s*)$/) {
+                            my $pre = $1;
+
+                            if ($line !~ /else/) {
+                                #print "APW: ALLOWED: pre<$pre> line<$line>\n"
+                                $allowed = 1;
+                            }
+                        }
+
+			my ($level, $endln, @chunks) =
+				ctx_statement_full($linenr, $realcnt, $-[0]);
+
+			# Check the condition.
+			my ($cond, $block) = @{$chunks[0]};
+			#print "CHECKING<$linenr> cond<$cond> block<$block>\n";
+			if (defined $cond) {
+				substr($block, 0, length($cond), '');
+			}
+			if (statement_lines($cond) > 1) {
+				#print "APW: ALLOWED: cond<$cond>\n";
+				$allowed = 1;
+			}
+			if ($block =~/\b(?:if|for|while)\b/) {
+				#print "APW: ALLOWED: block<$block>\n";
+				$allowed = 1;
+			}
+			if (statement_block_size($block) > 1) {
+				#print "APW: ALLOWED: lines block<$block>\n";
+				$allowed = 1;
+			}
+			# Check the post-context.
+			if (defined $chunks[1]) {
+				my ($cond, $block) = @{$chunks[1]};
+				if (defined $cond) {
+					substr($block, 0, length($cond), '');
+				}
+				if ($block =~ /^\s*\{/) {
+					#print "APW: ALLOWED: chunk-1 block<$block>\n";
+					$allowed = 1;
+				}
+			}
+			if ($level == 0 && $block !~ /^\s*\{/ && !$allowed) {
+				my $herectx = $here . "\n";;
+				my $cnt = statement_rawlines($block);
+
+				for (my $n = 0; $n < $cnt; $n++) {
+					$herectx .= raw_line($linenr, $n) . "\n";;
+				}
+
+				ERROR("BRACES", "braces {} are necessary even for single statement blocks\n" . $herectx);
+			}
+		}
+
+# check for single line unbalanced braces
+		if ($sline =~ /^.\s*\}\s*else\s*$/ ||
+		    $sline =~ /^.\s*else\s*\{\s*$/) {
+			CHK("BRACES", "Unbalanced braces around else statement\n" . $herecurr);
+		}
+
+# check for unnecessary blank lines around braces
+		if (($line =~ /^.\s*}\s*$/ && $prevrawline =~ /^.\s*$/)) {
+			if (CHK("BRACES",
+				"Blank lines aren't necessary before a close brace '}'\n" . $hereprev) &&
+			    $fix && $prevrawline =~ /^\+/) {
+				fix_delete_line($fixlinenr - 1, $prevrawline);
+			}
+		}
+		if (($rawline =~ /^.\s*$/ && $prevline =~ /^..*{\s*$/)) {
+			if (CHK("BRACES",
+				"Blank lines aren't necessary after an open brace '{'\n" . $hereprev) &&
+			    $fix) {
+				fix_delete_line($fixlinenr, $rawline);
+			}
+		}
+
+# no volatiles please
+		my $asm_volatile = qr{\b(__asm__|asm)\s+(__volatile__|volatile)\b};
+		if ($line =~ /\bvolatile\b/ && $line !~ /$asm_volatile/) {
+			WARN("VOLATILE",
+			     "Use of volatile is usually wrong: see Documentation/process/volatile-considered-harmful.rst\n" . $herecurr);
+		}
+
+# Check for user-visible strings broken across lines, which breaks the ability
+# to grep for the string.  Make exceptions when the previous string ends in a
+# newline (multiple lines in one string constant) or '\t', '\r', ';', or '{'
+# (common in inline assembly) or is a octal \123 or hexadecimal \xaf value
+		if ($line =~ /^\+\s*$String/ &&
+		    $prevline =~ /"\s*$/ &&
+		    $prevrawline !~ /(?:\\(?:[ntr]|[0-7]{1,3}|x[0-9a-fA-F]{1,2})|;\s*|\{\s*)"\s*$/) {
+			if (WARN("SPLIT_STRING",
+				 "quoted string split across lines\n" . $hereprev) &&
+				     $fix &&
+				     $prevrawline =~ /^\+.*"\s*$/ &&
+				     $last_coalesced_string_linenr != $linenr - 1) {
+				my $extracted_string = get_quoted_string($line, $rawline);
+				my $comma_close = "";
+				if ($rawline =~ /\Q$extracted_string\E(\s*\)\s*;\s*$|\s*,\s*)/) {
+					$comma_close = $1;
+				}
+
+				fix_delete_line($fixlinenr - 1, $prevrawline);
+				fix_delete_line($fixlinenr, $rawline);
+				my $fixedline = $prevrawline;
+				$fixedline =~ s/"\s*$//;
+				$fixedline .= substr($extracted_string, 1) . trim($comma_close);
+				fix_insert_line($fixlinenr - 1, $fixedline);
+				$fixedline = $rawline;
+				$fixedline =~ s/\Q$extracted_string\E\Q$comma_close\E//;
+				if ($fixedline !~ /\+\s*$/) {
+					fix_insert_line($fixlinenr, $fixedline);
+				}
+				$last_coalesced_string_linenr = $linenr;
+			}
+		}
+
+# check for missing a space in a string concatenation
+		if ($prevrawline =~ /[^\\]\w"$/ && $rawline =~ /^\+[\t ]+"\w/) {
+			WARN('MISSING_SPACE',
+			     "break quoted strings at a space character\n" . $hereprev);
+		}
+
+# check for an embedded function name in a string when the function is known
+# This does not work very well for -f --file checking as it depends on patch
+# context providing the function name or a single line form for in-file
+# function declarations
+		if ($line =~ /^\+.*$String/ &&
+		    defined($context_function) &&
+		    get_quoted_string($line, $rawline) =~ /\b$context_function\b/ &&
+		    length(get_quoted_string($line, $rawline)) != (length($context_function) + 2)) {
+			WARN("EMBEDDED_FUNCTION_NAME",
+			     "Prefer using '\"%s...\", __func__' to using '$context_function', this function's name, in a string\n" . $herecurr);
+		}
+
+# check for unnecessary function tracing like uses
+# This does not use $logFunctions because there are many instances like
+# 'dprintk(FOO, "%s()\n", __func__);' which do not match $logFunctions
+		if ($rawline =~ /^\+.*\([^"]*"$tracing_logging_tags{0,3}%s(?:\s*\(\s*\)\s*)?$tracing_logging_tags{0,3}(?:\\n)?"\s*,\s*__func__\s*\)\s*;/) {
+			if (WARN("TRACING_LOGGING",
+				 "Unnecessary ftrace-like logging - prefer using ftrace\n" . $herecurr) &&
+			    $fix) {
+                                fix_delete_line($fixlinenr, $rawline);
+			}
+		}
+
+# check for spaces before a quoted newline
+		if ($rawline =~ /^.*\".*\s\\n/) {
+			if (WARN("QUOTED_WHITESPACE_BEFORE_NEWLINE",
+				 "unnecessary whitespace before a quoted newline\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/^(\+.*\".*)\s+\\n/$1\\n/;
+			}
+
+		}
+
+# concatenated string without spaces between elements
+		if ($line =~ /$String[A-Z_]/ ||
+		    ($line =~ /([A-Za-z0-9_]+)$String/ && $1 !~ /^[Lu]$/)) {
+			if (CHK("CONCATENATED_STRING",
+				"Concatenated strings should use spaces between elements\n" . $herecurr) &&
+			    $fix) {
+				while ($line =~ /($String)/g) {
+					my $extracted_string = substr($rawline, $-[0], $+[0] - $-[0]);
+					$fixed[$fixlinenr] =~ s/\Q$extracted_string\E([A-Za-z0-9_])/$extracted_string $1/;
+					$fixed[$fixlinenr] =~ s/([A-Za-z0-9_])\Q$extracted_string\E/$1 $extracted_string/;
+				}
+			}
+		}
+
+# uncoalesced string fragments
+		if ($line =~ /$String\s*[Lu]?"/) {
+			if (WARN("STRING_FRAGMENTS",
+				 "Consecutive strings are generally better as a single string\n" . $herecurr) &&
+			    $fix) {
+				while ($line =~ /($String)(?=\s*")/g) {
+					my $extracted_string = substr($rawline, $-[0], $+[0] - $-[0]);
+					$fixed[$fixlinenr] =~ s/\Q$extracted_string\E\s*"/substr($extracted_string, 0, -1)/e;
+				}
+			}
+		}
+
+# check for non-standard and hex prefixed decimal printf formats
+		my $show_L = 1;	#don't show the same defect twice
+		my $show_Z = 1;
+		while ($line =~ /(?:^|")([X\t]*)(?:"|$)/g) {
+			my $string = substr($rawline, $-[1], $+[1] - $-[1]);
+			$string =~ s/%%/__/g;
+			# check for %L
+			if ($show_L && $string =~ /%[\*\d\.\$]*L([diouxX])/) {
+				WARN("PRINTF_L",
+				     "\%L$1 is non-standard C, use %ll$1\n" . $herecurr);
+				$show_L = 0;
+			}
+			# check for %Z
+			if ($show_Z && $string =~ /%[\*\d\.\$]*Z([diouxX])/) {
+				WARN("PRINTF_Z",
+				     "%Z$1 is non-standard C, use %z$1\n" . $herecurr);
+				$show_Z = 0;
+			}
+			# check for 0x<decimal>
+			if ($string =~ /0x%[\*\d\.\$\Llzth]*[diou]/) {
+				ERROR("PRINTF_0XDECIMAL",
+				      "Prefixing 0x with decimal output is defective\n" . $herecurr);
+			}
+		}
+
+# check for line continuations in quoted strings with odd counts of "
+		if ($rawline =~ /\\$/ && $sline =~ tr/"/"/ % 2) {
+			WARN("LINE_CONTINUATIONS",
+			     "Avoid line continuations in quoted strings\n" . $herecurr);
+		}
+
+# warn about #if 0
+		if ($line =~ /^.\s*\#\s*if\s+0\b/) {
+			WARN("IF_0",
+			     "Consider removing the code enclosed by this #if 0 and its #endif\n" . $herecurr);
+		}
+
+# warn about #if 1
+		if ($line =~ /^.\s*\#\s*if\s+1\b/) {
+			WARN("IF_1",
+			     "Consider removing the #if 1 and its #endif\n" . $herecurr);
+		}
+
+# check for needless "if (<foo>) fn(<foo>)" uses
+		if ($prevline =~ /\bif\s*\(\s*($Lval)\s*\)/) {
+			my $tested = quotemeta($1);
+			my $expr = '\s*\(\s*' . $tested . '\s*\)\s*;';
+			if ($line =~ /\b(kfree|usb_free_urb|debugfs_remove(?:_recursive)?|(?:kmem_cache|mempool|dma_pool)_destroy)$expr/) {
+				my $func = $1;
+				if (WARN('NEEDLESS_IF',
+					 "$func(NULL) is safe and this check is probably not required\n" . $hereprev) &&
+				    $fix) {
+					my $do_fix = 1;
+					my $leading_tabs = "";
+					my $new_leading_tabs = "";
+					if ($lines[$linenr - 2] =~ /^\+(\t*)if\s*\(\s*$tested\s*\)\s*$/) {
+						$leading_tabs = $1;
+					} else {
+						$do_fix = 0;
+					}
+					if ($lines[$linenr - 1] =~ /^\+(\t+)$func\s*\(\s*$tested\s*\)\s*;\s*$/) {
+						$new_leading_tabs = $1;
+						if (length($leading_tabs) + 1 ne length($new_leading_tabs)) {
+							$do_fix = 0;
+						}
+					} else {
+						$do_fix = 0;
+					}
+					if ($do_fix) {
+						fix_delete_line($fixlinenr - 1, $prevrawline);
+						$fixed[$fixlinenr] =~ s/^\+$new_leading_tabs/\+$leading_tabs/;
+					}
+				}
+			}
+		}
+
+# check for unnecessary "Out of Memory" messages
+		if ($line =~ /^\+.*\b$logFunctions\s*\(/ &&
+		    $prevline =~ /^[ \+]\s*if\s*\(\s*(\!\s*|NULL\s*==\s*)?($Lval)(\s*==\s*NULL\s*)?\s*\)/ &&
+		    (defined $1 || defined $3) &&
+		    $linenr > 3) {
+			my $testval = $2;
+			my $testline = $lines[$linenr - 3];
+
+			my ($s, $c) = ctx_statement_block($linenr - 3, $realcnt, 0);
+#			print("line: <$line>\nprevline: <$prevline>\ns: <$s>\nc: <$c>\n\n\n");
+
+			if ($s =~ /(?:^|\n)[ \+]\s*(?:$Type\s*)?\Q$testval\E\s*=\s*(?:\([^\)]*\)\s*)?\s*$allocFunctions\s*\(/ &&
+			    $s !~ /\b__GFP_NOWARN\b/ ) {
+				WARN("OOM_MESSAGE",
+				     "Possible unnecessary 'out of memory' message\n" . $hereprev);
+			}
+		}
+
+# check for logging functions with KERN_<LEVEL>
+		if ($line !~ /printk(?:_ratelimited|_once)?\s*\(/ &&
+		    $line =~ /\b$logFunctions\s*\(.*\b(KERN_[A-Z]+)\b/) {
+			my $level = $1;
+			if (WARN("UNNECESSARY_KERN_LEVEL",
+				 "Possible unnecessary $level\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\s*$level\s*//;
+			}
+		}
+
+# check for logging continuations
+		if ($line =~ /\bprintk\s*\(\s*KERN_CONT\b|\bpr_cont\s*\(/) {
+			WARN("LOGGING_CONTINUATION",
+			     "Avoid logging continuation uses where feasible\n" . $herecurr);
+		}
+
+# check for unnecessary use of %h[xudi] and %hh[xudi] in logging functions
+		if (defined $stat &&
+		    $line =~ /\b$logFunctions\s*\(/ &&
+		    index($stat, '"') >= 0) {
+			my $lc = $stat =~ tr@\n@@;
+			$lc = $lc + $linenr;
+			my $stat_real = get_stat_real($linenr, $lc);
+			pos($stat_real) = index($stat_real, '"');
+			while ($stat_real =~ /[^\"%]*(%[\#\d\.\*\-]*(h+)[idux])/g) {
+				my $pspec = $1;
+				my $h = $2;
+				my $lineoff = substr($stat_real, 0, $-[1]) =~ tr@\n@@;
+				if (WARN("UNNECESSARY_MODIFIER",
+					 "Integer promotion: Using '$h' in '$pspec' is unnecessary\n" . "$here\n$stat_real\n") &&
+				    $fix && $fixed[$fixlinenr + $lineoff] =~ /^\+/) {
+					my $nspec = $pspec;
+					$nspec =~ s/h//g;
+					$fixed[$fixlinenr + $lineoff] =~ s/\Q$pspec\E/$nspec/;
+				}
+			}
+		}
+
+# check for mask then right shift without a parentheses
+		if ($perl_version_ok &&
+		    $line =~ /$LvalOrFunc\s*\&\s*($LvalOrFunc)\s*>>/ &&
+		    $4 !~ /^\&/) { # $LvalOrFunc may be &foo, ignore if so
+			WARN("MASK_THEN_SHIFT",
+			     "Possible precedence defect with mask then right shift - may need parentheses\n" . $herecurr);
+		}
+
+# check for pointer comparisons to NULL
+		if ($perl_version_ok) {
+			while ($line =~ /\b$LvalOrFunc\s*(==|\!=)\s*NULL\b/g) {
+				my $val = $1;
+				my $equal = "!";
+				$equal = "" if ($4 eq "!=");
+				if (CHK("COMPARISON_TO_NULL",
+					"Comparison to NULL could be written \"${equal}${val}\"\n" . $herecurr) &&
+					    $fix) {
+					$fixed[$fixlinenr] =~ s/\b\Q$val\E\s*(?:==|\!=)\s*NULL\b/$equal$val/;
+				}
+			}
+		}
+
+# check for bad placement of section $InitAttribute (e.g.: __initdata)
+		if ($line =~ /(\b$InitAttribute\b)/) {
+			my $attr = $1;
+			if ($line =~ /^\+\s*static\s+(?:const\s+)?(?:$attr\s+)?($NonptrTypeWithAttr)\s+(?:$attr\s+)?($Ident(?:\[[^]]*\])?)\s*[=;]/) {
+				my $ptr = $1;
+				my $var = $2;
+				if ((($ptr =~ /\b(union|struct)\s+$attr\b/ &&
+				      ERROR("MISPLACED_INIT",
+					    "$attr should be placed after $var\n" . $herecurr)) ||
+				     ($ptr !~ /\b(union|struct)\s+$attr\b/ &&
+				      WARN("MISPLACED_INIT",
+					   "$attr should be placed after $var\n" . $herecurr))) &&
+				    $fix) {
+					$fixed[$fixlinenr] =~ s/(\bstatic\s+(?:const\s+)?)(?:$attr\s+)?($NonptrTypeWithAttr)\s+(?:$attr\s+)?($Ident(?:\[[^]]*\])?)\s*([=;])\s*/"$1" . trim(string_find_replace($2, "\\s*$attr\\s*", " ")) . " " . trim(string_find_replace($3, "\\s*$attr\\s*", "")) . " $attr" . ("$4" eq ";" ? ";" : " = ")/e;
+				}
+			}
+		}
+
+# check for $InitAttributeData (ie: __initdata) with const
+		if ($line =~ /\bconst\b/ && $line =~ /($InitAttributeData)/) {
+			my $attr = $1;
+			$attr =~ /($InitAttributePrefix)(.*)/;
+			my $attr_prefix = $1;
+			my $attr_type = $2;
+			if (ERROR("INIT_ATTRIBUTE",
+				  "Use of const init definition must use ${attr_prefix}initconst\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~
+				    s/$InitAttributeData/${attr_prefix}initconst/;
+			}
+		}
+
+# check for $InitAttributeConst (ie: __initconst) without const
+		if ($line !~ /\bconst\b/ && $line =~ /($InitAttributeConst)/) {
+			my $attr = $1;
+			if (ERROR("INIT_ATTRIBUTE",
+				  "Use of $attr requires a separate use of const\n" . $herecurr) &&
+			    $fix) {
+				my $lead = $fixed[$fixlinenr] =~
+				    /(^\+\s*(?:static\s+))/;
+				$lead = rtrim($1);
+				$lead = "$lead " if ($lead !~ /^\+$/);
+				$lead = "${lead}const ";
+				$fixed[$fixlinenr] =~ s/(^\+\s*(?:static\s+))/$lead/;
+			}
+		}
+
+# check for __read_mostly with const non-pointer (should just be const)
+		if ($line =~ /\b__read_mostly\b/ &&
+		    $line =~ /($Type)\s*$Ident/ && $1 !~ /\*\s*$/ && $1 =~ /\bconst\b/) {
+			if (ERROR("CONST_READ_MOSTLY",
+				  "Invalid use of __read_mostly with const type\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\s+__read_mostly\b//;
+			}
+		}
+
+# don't use __constant_<foo> functions outside of include/uapi/
+		if ($realfile !~ m@^include/uapi/@ &&
+		    $line =~ /(__constant_(?:htons|ntohs|[bl]e(?:16|32|64)_to_cpu|cpu_to_[bl]e(?:16|32|64)))\s*\(/) {
+			my $constant_func = $1;
+			my $func = $constant_func;
+			$func =~ s/^__constant_//;
+			if (WARN("CONSTANT_CONVERSION",
+				 "$constant_func should be $func\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\b$constant_func\b/$func/g;
+			}
+		}
+
+# prefer usleep_range over udelay
+		if ($line =~ /\budelay\s*\(\s*(\d+)\s*\)/) {
+			my $delay = $1;
+			# ignore udelay's < 10, however
+			if (! ($delay < 10) ) {
+				CHK("USLEEP_RANGE",
+				    "usleep_range is preferred over udelay; see Documentation/timers/timers-howto.rst\n" . $herecurr);
+			}
+			if ($delay > 2000) {
+				WARN("LONG_UDELAY",
+				     "long udelay - prefer mdelay; see arch/arm/include/asm/delay.h\n" . $herecurr);
+			}
+		}
+
+# warn about unexpectedly long msleep's
+		if ($line =~ /\bmsleep\s*\((\d+)\);/) {
+			if ($1 < 20) {
+				WARN("MSLEEP",
+				     "msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.rst\n" . $herecurr);
+			}
+		}
+
+# check for comparisons of jiffies
+		if ($line =~ /\bjiffies\s*$Compare|$Compare\s*jiffies\b/) {
+			WARN("JIFFIES_COMPARISON",
+			     "Comparing jiffies is almost always wrong; prefer time_after, time_before and friends\n" . $herecurr);
+		}
+
+# check for comparisons of get_jiffies_64()
+		if ($line =~ /\bget_jiffies_64\s*\(\s*\)\s*$Compare|$Compare\s*get_jiffies_64\s*\(\s*\)/) {
+			WARN("JIFFIES_COMPARISON",
+			     "Comparing get_jiffies_64() is almost always wrong; prefer time_after64, time_before64 and friends\n" . $herecurr);
+		}
+
+# warn about #ifdefs in C files
+#		if ($line =~ /^.\s*\#\s*if(|n)def/ && ($realfile =~ /\.c$/)) {
+#			print "#ifdef in C files should be avoided\n";
+#			print "$herecurr";
+#			$clean = 0;
+#		}
+
+# warn about spacing in #ifdefs
+		if ($line =~ /^.\s*\#\s*(ifdef|ifndef|elif)\s\s+/) {
+			if (ERROR("SPACING",
+				  "exactly one space required after that #$1\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~
+				    s/^(.\s*\#\s*(ifdef|ifndef|elif))\s{2,}/$1 /;
+			}
+
+		}
+
+# check for spinlock_t definitions without a comment.
+		if ($line =~ /^.\s*(struct\s+mutex|spinlock_t)\s+\S+;/ ||
+		    $line =~ /^.\s*(DEFINE_MUTEX)\s*\(/) {
+			my $which = $1;
+			if (!ctx_has_comment($first_line, $linenr)) {
+				CHK("UNCOMMENTED_DEFINITION",
+				    "$1 definition without comment\n" . $herecurr);
+			}
+		}
+# check for memory barriers without a comment.
+
+		my $barriers = qr{
+			mb|
+			rmb|
+			wmb
+		}x;
+		my $barrier_stems = qr{
+			mb__before_atomic|
+			mb__after_atomic|
+			store_release|
+			load_acquire|
+			store_mb|
+			(?:$barriers)
+		}x;
+		my $all_barriers = qr{
+			(?:$barriers)|
+			smp_(?:$barrier_stems)|
+			virt_(?:$barrier_stems)
+		}x;
+
+		if ($line =~ /\b(?:$all_barriers)\s*\(/) {
+			if (!ctx_has_comment($first_line, $linenr)) {
+				WARN("MEMORY_BARRIER",
+				     "memory barrier without comment\n" . $herecurr);
+			}
+		}
+
+		my $underscore_smp_barriers = qr{__smp_(?:$barrier_stems)}x;
+
+		if ($realfile !~ m@^include/asm-generic/@ &&
+		    $realfile !~ m@/barrier\.h$@ &&
+		    $line =~ m/\b(?:$underscore_smp_barriers)\s*\(/ &&
+		    $line !~ m/^.\s*\#\s*define\s+(?:$underscore_smp_barriers)\s*\(/) {
+			WARN("MEMORY_BARRIER",
+			     "__smp memory barriers shouldn't be used outside barrier.h and asm-generic\n" . $herecurr);
+		}
+
+# check for waitqueue_active without a comment.
+		if ($line =~ /\bwaitqueue_active\s*\(/) {
+			if (!ctx_has_comment($first_line, $linenr)) {
+				WARN("WAITQUEUE_ACTIVE",
+				     "waitqueue_active without comment\n" . $herecurr);
+			}
+		}
+
+# check for data_race without a comment.
+		if ($line =~ /\bdata_race\s*\(/) {
+			if (!ctx_has_comment($first_line, $linenr)) {
+				WARN("DATA_RACE",
+				     "data_race without comment\n" . $herecurr);
+			}
+		}
+
+# check of hardware specific defines
+		if ($line =~ m@^.\s*\#\s*if.*\b(__i386__|__powerpc64__|__sun__|__s390x__)\b@ && $realfile !~ m@include/asm-@) {
+			CHK("ARCH_DEFINES",
+			    "architecture specific defines should be avoided\n" .  $herecurr);
+		}
+
+# check that the storage class is not after a type
+		if ($line =~ /\b($Type)\s+($Storage)\b/) {
+			WARN("STORAGE_CLASS",
+			     "storage class '$2' should be located before type '$1'\n" . $herecurr);
+		}
+# Check that the storage class is at the beginning of a declaration
+		if ($line =~ /\b$Storage\b/ &&
+		    $line !~ /^.\s*$Storage/ &&
+		    $line =~ /^.\s*(.+?)\$Storage\s/ &&
+		    $1 !~ /[\,\)]\s*$/) {
+			WARN("STORAGE_CLASS",
+			     "storage class should be at the beginning of the declaration\n" . $herecurr);
+		}
+
+# check the location of the inline attribute, that it is between
+# storage class and type.
+		if ($line =~ /\b$Type\s+$Inline\b/ ||
+		    $line =~ /\b$Inline\s+$Storage\b/) {
+			ERROR("INLINE_LOCATION",
+			      "inline keyword should sit between storage class and type\n" . $herecurr);
+		}
+
+# Check for __inline__ and __inline, prefer inline
+		if ($realfile !~ m@\binclude/uapi/@ &&
+		    $line =~ /\b(__inline__|__inline)\b/) {
+			if (WARN("INLINE",
+				 "plain inline is preferred over $1\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\b(__inline__|__inline)\b/inline/;
+
+			}
+		}
+
+# Check for compiler attributes
+		if ($realfile !~ m@\binclude/uapi/@ &&
+		    $rawline =~ /\b__attribute__\s*\(\s*($balanced_parens)\s*\)/) {
+			my $attr = $1;
+			$attr =~ s/\s*\(\s*(.*)\)\s*/$1/;
+
+			my %attr_list = (
+				"alias"				=> "__alias",
+				"aligned"			=> "__aligned",
+				"always_inline"			=> "__always_inline",
+				"assume_aligned"		=> "__assume_aligned",
+				"cold"				=> "__cold",
+				"const"				=> "__attribute_const__",
+				"copy"				=> "__copy",
+				"designated_init"		=> "__designated_init",
+				"externally_visible"		=> "__visible",
+				"format"			=> "printf|scanf",
+				"gnu_inline"			=> "__gnu_inline",
+				"malloc"			=> "__malloc",
+				"mode"				=> "__mode",
+				"no_caller_saved_registers"	=> "__no_caller_saved_registers",
+				"noclone"			=> "__noclone",
+				"noinline"			=> "noinline",
+				"nonstring"			=> "__nonstring",
+				"noreturn"			=> "__noreturn",
+				"packed"			=> "__packed",
+				"pure"				=> "__pure",
+				"section"			=> "__section",
+				"used"				=> "__used",
+				"weak"				=> "__weak"
+			);
+
+			while ($attr =~ /\s*(\w+)\s*(${balanced_parens})?/g) {
+				my $orig_attr = $1;
+				my $params = '';
+				$params = $2 if defined($2);
+				my $curr_attr = $orig_attr;
+				$curr_attr =~ s/^[\s_]+|[\s_]+$//g;
+				if (exists($attr_list{$curr_attr})) {
+					my $new = $attr_list{$curr_attr};
+					if ($curr_attr eq "format" && $params) {
+						$params =~ /^\s*\(\s*(\w+)\s*,\s*(.*)/;
+						$new = "__$1\($2";
+					} else {
+						$new = "$new$params";
+					}
+					if (WARN("PREFER_DEFINED_ATTRIBUTE_MACRO",
+						 "Prefer $new over __attribute__(($orig_attr$params))\n" . $herecurr) &&
+					    $fix) {
+						my $remove = "\Q$orig_attr\E" . '\s*' . "\Q$params\E" . '(?:\s*,\s*)?';
+						$fixed[$fixlinenr] =~ s/$remove//;
+						$fixed[$fixlinenr] =~ s/\b__attribute__/$new __attribute__/;
+						$fixed[$fixlinenr] =~ s/\}\Q$new\E/} $new/;
+						$fixed[$fixlinenr] =~ s/ __attribute__\s*\(\s*\(\s*\)\s*\)//;
+					}
+				}
+			}
+
+			# Check for __attribute__ unused, prefer __always_unused or __maybe_unused
+			if ($attr =~ /^_*unused/) {
+				WARN("PREFER_DEFINED_ATTRIBUTE_MACRO",
+				     "__always_unused or __maybe_unused is preferred over __attribute__((__unused__))\n" . $herecurr);
+			}
+		}
+
+# Check for __attribute__ weak, or __weak declarations (may have link issues)
+		if ($perl_version_ok &&
+		    $line =~ /(?:$Declare|$DeclareMisordered)\s*$Ident\s*$balanced_parens\s*(?:$Attribute)?\s*;/ &&
+		    ($line =~ /\b__attribute__\s*\(\s*\(.*\bweak\b/ ||
+		     $line =~ /\b__weak\b/)) {
+			ERROR("WEAK_DECLARATION",
+			      "Using weak declarations can have unintended link defects\n" . $herecurr);
+		}
+
+# check for c99 types like uint8_t used outside of uapi/ and tools/
+		if ($realfile !~ m@\binclude/uapi/@ &&
+		    $realfile !~ m@\btools/@ &&
+		    $line =~ /\b($Declare)\s*$Ident\s*[=;,\[]/) {
+			my $type = $1;
+			if ($type =~ /\b($typeC99Typedefs)\b/) {
+				$type = $1;
+				my $kernel_type = 'u';
+				$kernel_type = 's' if ($type =~ /^_*[si]/);
+				$type =~ /(\d+)/;
+				$kernel_type .= $1;
+				if (CHK("PREFER_KERNEL_TYPES",
+					"Prefer kernel type '$kernel_type' over '$type'\n" . $herecurr) &&
+				    $fix) {
+					$fixed[$fixlinenr] =~ s/\b$type\b/$kernel_type/;
+				}
+			}
+		}
+
+# check for cast of C90 native int or longer types constants
+		if ($line =~ /(\(\s*$C90_int_types\s*\)\s*)($Constant)\b/) {
+			my $cast = $1;
+			my $const = $2;
+			my $suffix = "";
+			my $newconst = $const;
+			$newconst =~ s/${Int_type}$//;
+			$suffix .= 'U' if ($cast =~ /\bunsigned\b/);
+			if ($cast =~ /\blong\s+long\b/) {
+			    $suffix .= 'LL';
+			} elsif ($cast =~ /\blong\b/) {
+			    $suffix .= 'L';
+			}
+			if (WARN("TYPECAST_INT_CONSTANT",
+				 "Unnecessary typecast of c90 int constant - '$cast$const' could be '$const$suffix'\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\Q$cast\E$const\b/$newconst$suffix/;
+			}
+		}
+
+# check for sizeof(&)
+		if ($line =~ /\bsizeof\s*\(\s*\&/) {
+			WARN("SIZEOF_ADDRESS",
+			     "sizeof(& should be avoided\n" . $herecurr);
+		}
+
+# check for sizeof without parenthesis
+		if ($line =~ /\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/) {
+			if (WARN("SIZEOF_PARENTHESIS",
+				 "sizeof $1 should be sizeof($1)\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/"sizeof(" . trim($1) . ")"/ex;
+			}
+		}
+
+# check for struct spinlock declarations
+		if ($line =~ /^.\s*\bstruct\s+spinlock\s+\w+\s*;/) {
+			WARN("USE_SPINLOCK_T",
+			     "struct spinlock should be spinlock_t\n" . $herecurr);
+		}
+
+# check for seq_printf uses that could be seq_puts
+		if ($sline =~ /\bseq_printf\s*\(.*"\s*\)\s*;\s*$/) {
+			my $fmt = get_quoted_string($line, $rawline);
+			$fmt =~ s/%%//g;
+			if ($fmt !~ /%/) {
+				if (WARN("PREFER_SEQ_PUTS",
+					 "Prefer seq_puts to seq_printf\n" . $herecurr) &&
+				    $fix) {
+					$fixed[$fixlinenr] =~ s/\bseq_printf\b/seq_puts/;
+				}
+			}
+		}
+
+# check for vsprintf extension %p<foo> misuses
+		if ($perl_version_ok &&
+		    defined $stat &&
+		    $stat =~ /^\+(?![^\{]*\{\s*).*\b(\w+)\s*\(.*$String\s*,/s &&
+		    $1 !~ /^_*volatile_*$/) {
+			my $stat_real;
+
+			my $lc = $stat =~ tr@\n@@;
+			$lc = $lc + $linenr;
+		        for (my $count = $linenr; $count <= $lc; $count++) {
+				my $specifier;
+				my $extension;
+				my $qualifier;
+				my $bad_specifier = "";
+				my $fmt = get_quoted_string($lines[$count - 1], raw_line($count, 0));
+				$fmt =~ s/%%//g;
+
+				while ($fmt =~ /(\%[\*\d\.]*p(\w)(\w*))/g) {
+					$specifier = $1;
+					$extension = $2;
+					$qualifier = $3;
+					if ($extension !~ /[4SsBKRraEehMmIiUDdgVCbGNOxtf]/ ||
+					    ($extension eq "f" &&
+					     defined $qualifier && $qualifier !~ /^w/) ||
+					    ($extension eq "4" &&
+					     defined $qualifier && $qualifier !~ /^cc/)) {
+						$bad_specifier = $specifier;
+						last;
+					}
+					if ($extension eq "x" && !defined($stat_real)) {
+						if (!defined($stat_real)) {
+							$stat_real = get_stat_real($linenr, $lc);
+						}
+						WARN("VSPRINTF_SPECIFIER_PX",
+						     "Using vsprintf specifier '\%px' potentially exposes the kernel memory layout, if you don't really need the address please consider using '\%p'.\n" . "$here\n$stat_real\n");
+					}
+				}
+				if ($bad_specifier ne "") {
+					my $stat_real = get_stat_real($linenr, $lc);
+					my $ext_type = "Invalid";
+					my $use = "";
+					if ($bad_specifier =~ /p[Ff]/) {
+						$use = " - use %pS instead";
+						$use =~ s/pS/ps/ if ($bad_specifier =~ /pf/);
+					}
+
+					WARN("VSPRINTF_POINTER_EXTENSION",
+					     "$ext_type vsprintf pointer extension '$bad_specifier'$use\n" . "$here\n$stat_real\n");
+				}
+			}
+		}
+
+# Check for misused memsets
+		if ($perl_version_ok &&
+		    defined $stat &&
+		    $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*$FuncArg\s*\)/) {
+
+			my $ms_addr = $2;
+			my $ms_val = $7;
+			my $ms_size = $12;
+
+			if ($ms_size =~ /^(0x|)0$/i) {
+				ERROR("MEMSET",
+				      "memset to 0's uses 0 as the 2nd argument, not the 3rd\n" . "$here\n$stat\n");
+			} elsif ($ms_size =~ /^(0x|)1$/i) {
+				WARN("MEMSET",
+				     "single byte memset is suspicious. Swapped 2nd/3rd argument?\n" . "$here\n$stat\n");
+			}
+		}
+
+# Check for memcpy(foo, bar, ETH_ALEN) that could be ether_addr_copy(foo, bar)
+#		if ($perl_version_ok &&
+#		    defined $stat &&
+#		    $stat =~ /^\+(?:.*?)\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) {
+#			if (WARN("PREFER_ETHER_ADDR_COPY",
+#				 "Prefer ether_addr_copy() over memcpy() if the Ethernet addresses are __aligned(2)\n" . "$here\n$stat\n") &&
+#			    $fix) {
+#				$fixed[$fixlinenr] =~ s/\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/ether_addr_copy($2, $7)/;
+#			}
+#		}
+
+# Check for memcmp(foo, bar, ETH_ALEN) that could be ether_addr_equal*(foo, bar)
+#		if ($perl_version_ok &&
+#		    defined $stat &&
+#		    $stat =~ /^\+(?:.*?)\bmemcmp\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) {
+#			WARN("PREFER_ETHER_ADDR_EQUAL",
+#			     "Prefer ether_addr_equal() or ether_addr_equal_unaligned() over memcmp()\n" . "$here\n$stat\n")
+#		}
+
+# check for memset(foo, 0x0, ETH_ALEN) that could be eth_zero_addr
+# check for memset(foo, 0xFF, ETH_ALEN) that could be eth_broadcast_addr
+#		if ($perl_version_ok &&
+#		    defined $stat &&
+#		    $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) {
+#
+#			my $ms_val = $7;
+#
+#			if ($ms_val =~ /^(?:0x|)0+$/i) {
+#				if (WARN("PREFER_ETH_ZERO_ADDR",
+#					 "Prefer eth_zero_addr over memset()\n" . "$here\n$stat\n") &&
+#				    $fix) {
+#					$fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_zero_addr($2)/;
+#				}
+#			} elsif ($ms_val =~ /^(?:0xff|255)$/i) {
+#				if (WARN("PREFER_ETH_BROADCAST_ADDR",
+#					 "Prefer eth_broadcast_addr() over memset()\n" . "$here\n$stat\n") &&
+#				    $fix) {
+#					$fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_broadcast_addr($2)/;
+#				}
+#			}
+#		}
+
+# strlcpy uses that should likely be strscpy
+		if ($line =~ /\bstrlcpy\s*\(/) {
+			WARN("STRLCPY",
+			     "Prefer strscpy over strlcpy - see: https://lore.kernel.org/r/CAHk-=wgfRnXz0W3D37d01q3JFkr_i_uTL=V6A6G1oUZcprmknw\@mail.gmail.com/\n" . $herecurr);
+		}
+
+# typecasts on min/max could be min_t/max_t
+		if ($perl_version_ok &&
+		    defined $stat &&
+		    $stat =~ /^\+(?:.*?)\b(min|max)\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\)/) {
+			if (defined $2 || defined $7) {
+				my $call = $1;
+				my $cast1 = deparenthesize($2);
+				my $arg1 = $3;
+				my $cast2 = deparenthesize($7);
+				my $arg2 = $8;
+				my $cast;
+
+				if ($cast1 ne "" && $cast2 ne "" && $cast1 ne $cast2) {
+					$cast = "$cast1 or $cast2";
+				} elsif ($cast1 ne "") {
+					$cast = $cast1;
+				} else {
+					$cast = $cast2;
+				}
+				WARN("MINMAX",
+				     "$call() should probably be ${call}_t($cast, $arg1, $arg2)\n" . "$here\n$stat\n");
+			}
+		}
+
+# check usleep_range arguments
+		if ($perl_version_ok &&
+		    defined $stat &&
+		    $stat =~ /^\+(?:.*?)\busleep_range\s*\(\s*($FuncArg)\s*,\s*($FuncArg)\s*\)/) {
+			my $min = $1;
+			my $max = $7;
+			if ($min eq $max) {
+				WARN("USLEEP_RANGE",
+				     "usleep_range should not use min == max args; see Documentation/timers/timers-howto.rst\n" . "$here\n$stat\n");
+			} elsif ($min =~ /^\d+$/ && $max =~ /^\d+$/ &&
+				 $min > $max) {
+				WARN("USLEEP_RANGE",
+				     "usleep_range args reversed, use min then max; see Documentation/timers/timers-howto.rst\n" . "$here\n$stat\n");
+			}
+		}
+
+# check for naked sscanf
+		if ($perl_version_ok &&
+		    defined $stat &&
+		    $line =~ /\bsscanf\b/ &&
+		    ($stat !~ /$Ident\s*=\s*sscanf\s*$balanced_parens/ &&
+		     $stat !~ /\bsscanf\s*$balanced_parens\s*(?:$Compare)/ &&
+		     $stat !~ /(?:$Compare)\s*\bsscanf\s*$balanced_parens/)) {
+			my $lc = $stat =~ tr@\n@@;
+			$lc = $lc + $linenr;
+			my $stat_real = get_stat_real($linenr, $lc);
+			WARN("NAKED_SSCANF",
+			     "unchecked sscanf return value\n" . "$here\n$stat_real\n");
+		}
+
+# check for simple sscanf that should be kstrto<foo>
+		if ($perl_version_ok &&
+		    defined $stat &&
+		    $line =~ /\bsscanf\b/) {
+			my $lc = $stat =~ tr@\n@@;
+			$lc = $lc + $linenr;
+			my $stat_real = get_stat_real($linenr, $lc);
+			if ($stat_real =~ /\bsscanf\b\s*\(\s*$FuncArg\s*,\s*("[^"]+")/) {
+				my $format = $6;
+				my $count = $format =~ tr@%@%@;
+				if ($count == 1 &&
+				    $format =~ /^"\%(?i:ll[udxi]|[udxi]ll|ll|[hl]h?[udxi]|[udxi][hl]h?|[hl]h?|[udxi])"$/) {
+					WARN("SSCANF_TO_KSTRTO",
+					     "Prefer kstrto<type> to single variable sscanf\n" . "$here\n$stat_real\n");
+				}
+			}
+		}
+
+# check for new externs in .h files.
+		if ($realfile =~ /\.h$/ &&
+		    $line =~ /^\+\s*(extern\s+)$Type\s*$Ident\s*\(/s) {
+			if (CHK("AVOID_EXTERNS",
+				"extern prototypes should be avoided in .h files\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/(.*)\bextern\b\s*(.*)/$1$2/;
+			}
+		}
+
+# check for new externs in .c files.
+		if ($realfile =~ /\.c$/ && defined $stat &&
+		    $stat =~ /^.\s*(?:extern\s+)?$Type\s+($Ident)(\s*)\(/s)
+		{
+			my $function_name = $1;
+			my $paren_space = $2;
+
+			my $s = $stat;
+			if (defined $cond) {
+				substr($s, 0, length($cond), '');
+			}
+			if ($s =~ /^\s*;/)
+			{
+				WARN("AVOID_EXTERNS",
+				     "externs should be avoided in .c files\n" .  $herecurr);
+			}
+
+			if ($paren_space =~ /\n/) {
+				WARN("FUNCTION_ARGUMENTS",
+				     "arguments for function declarations should follow identifier\n" . $herecurr);
+			}
+
+		} elsif ($realfile =~ /\.c$/ && defined $stat &&
+		    $stat =~ /^.\s*extern\s+/)
+		{
+			WARN("AVOID_EXTERNS",
+			     "externs should be avoided in .c files\n" .  $herecurr);
+		}
+
+# check for function declarations that have arguments without identifier names
+		if (defined $stat &&
+		    $stat =~ /^.\s*(?:extern\s+)?$Type\s*(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*\(\s*([^{]+)\s*\)\s*;/s &&
+		    $1 ne "void") {
+			my $args = trim($1);
+			while ($args =~ m/\s*($Type\s*(?:$Ident|\(\s*\*\s*$Ident?\s*\)\s*$balanced_parens)?)/g) {
+				my $arg = trim($1);
+				if ($arg =~ /^$Type$/ && $arg !~ /enum\s+$Ident$/) {
+					WARN("FUNCTION_ARGUMENTS",
+					     "function definition argument '$arg' should also have an identifier name\n" . $herecurr);
+				}
+			}
+		}
+
+# check for function definitions
+		if ($perl_version_ok &&
+		    defined $stat &&
+		    $stat =~ /^.\s*(?:$Storage\s+)?$Type\s*($Ident)\s*$balanced_parens\s*{/s) {
+			$context_function = $1;
+
+# check for multiline function definition with misplaced open brace
+			my $ok = 0;
+			my $cnt = statement_rawlines($stat);
+			my $herectx = $here . "\n";
+			for (my $n = 0; $n < $cnt; $n++) {
+				my $rl = raw_line($linenr, $n);
+				$herectx .=  $rl . "\n";
+				$ok = 1 if ($rl =~ /^[ \+]\{/);
+				$ok = 1 if ($rl =~ /\{/ && $n == 0);
+				last if $rl =~ /^[ \+].*\{/;
+			}
+			if (!$ok) {
+				ERROR("OPEN_BRACE",
+				      "open brace '{' following function definitions go on the next line\n" . $herectx);
+			}
+		}
+
+# checks for new __setup's
+		if ($rawline =~ /\b__setup\("([^"]*)"/) {
+			my $name = $1;
+
+			if (!grep(/$name/, @setup_docs)) {
+				CHK("UNDOCUMENTED_SETUP",
+				    "__setup appears un-documented -- check Documentation/admin-guide/kernel-parameters.txt\n" . $herecurr);
+			}
+		}
+
+# check for pointless casting of alloc functions
+		if ($line =~ /\*\s*\)\s*$allocFunctions\b/) {
+			WARN("UNNECESSARY_CASTS",
+			     "unnecessary cast may hide bugs, see http://c-faq.com/malloc/mallocnocast.html\n" . $herecurr);
+		}
+
+# alloc style
+# p = alloc(sizeof(struct foo), ...) should be p = alloc(sizeof(*p), ...)
+		if ($perl_version_ok &&
+		    $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*((?:kv|k|v)[mz]alloc(?:_node)?)\s*\(\s*(sizeof\s*\(\s*struct\s+$Lval\s*\))/) {
+			CHK("ALLOC_SIZEOF_STRUCT",
+			    "Prefer $3(sizeof(*$1)...) over $3($4...)\n" . $herecurr);
+		}
+
+# check for k[mz]alloc with multiplies that could be kmalloc_array/kcalloc
+		if ($perl_version_ok &&
+		    defined $stat &&
+		    $stat =~ /^\+\s*($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)\s*,/) {
+			my $oldfunc = $3;
+			my $a1 = $4;
+			my $a2 = $10;
+			my $newfunc = "kmalloc_array";
+			$newfunc = "kcalloc" if ($oldfunc eq "kzalloc");
+			my $r1 = $a1;
+			my $r2 = $a2;
+			if ($a1 =~ /^sizeof\s*\S/) {
+				$r1 = $a2;
+				$r2 = $a1;
+			}
+			if ($r1 !~ /^sizeof\b/ && $r2 =~ /^sizeof\s*\S/ &&
+			    !($r1 =~ /^$Constant$/ || $r1 =~ /^[A-Z_][A-Z0-9_]*$/)) {
+				my $cnt = statement_rawlines($stat);
+				my $herectx = get_stat_here($linenr, $cnt, $here);
+
+				if (WARN("ALLOC_WITH_MULTIPLY",
+					 "Prefer $newfunc over $oldfunc with multiply\n" . $herectx) &&
+				    $cnt == 1 &&
+				    $fix) {
+					$fixed[$fixlinenr] =~ s/\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)/$1 . ' = ' . "$newfunc(" . trim($r1) . ', ' . trim($r2)/e;
+				}
+			}
+		}
+
+# check for krealloc arg reuse
+		if ($perl_version_ok &&
+		    $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*krealloc\s*\(\s*($Lval)\s*,/ &&
+		    $1 eq $3) {
+			WARN("KREALLOC_ARG_REUSE",
+			     "Reusing the krealloc arg is almost always a bug\n" . $herecurr);
+		}
+
+# check for alloc argument mismatch
+		if ($line =~ /\b((?:devm_)?(?:kcalloc|kmalloc_array))\s*\(\s*sizeof\b/) {
+			WARN("ALLOC_ARRAY_ARGS",
+			     "$1 uses number as first arg, sizeof is generally wrong\n" . $herecurr);
+		}
+
+# check for multiple semicolons
+		if ($line =~ /;\s*;\s*$/) {
+			if (WARN("ONE_SEMICOLON",
+				 "Statements terminations use 1 semicolon\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/(\s*;\s*){2,}$/;/g;
+			}
+		}
+
+# check for #defines like: 1 << <digit> that could be BIT(digit), it is not exported to uapi
+		if ($realfile !~ m@^include/uapi/@ &&
+		    $line =~ /#\s*define\s+\w+\s+\(?\s*1\s*([ulUL]*)\s*\<\<\s*(?:\d+|$Ident)\s*\)?/) {
+			my $ull = "";
+			$ull = "_ULL" if (defined($1) && $1 =~ /ll/i);
+			if (CHK("BIT_MACRO",
+				"Prefer using the BIT$ull macro\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\(?\s*1\s*[ulUL]*\s*<<\s*(\d+|$Ident)\s*\)?/BIT${ull}($1)/;
+			}
+		}
+
+# check for IS_ENABLED() without CONFIG_<FOO> ($rawline for comments too)
+		if ($rawline =~ /\bIS_ENABLED\s*\(\s*(\w+)\s*\)/ && $1 !~ /^${CONFIG_}/) {
+			WARN("IS_ENABLED_CONFIG",
+			     "IS_ENABLED($1) is normally used as IS_ENABLED(${CONFIG_}$1)\n" . $herecurr);
+		}
+
+# check for #if defined CONFIG_<FOO> || defined CONFIG_<FOO>_MODULE
+		if ($line =~ /^\+\s*#\s*if\s+defined(?:\s*\(?\s*|\s+)(${CONFIG_}[A-Z_]+)\s*\)?\s*\|\|\s*defined(?:\s*\(?\s*|\s+)\1_MODULE\s*\)?\s*$/) {
+			my $config = $1;
+			if (WARN("PREFER_IS_ENABLED",
+				 "Prefer IS_ENABLED(<FOO>) to ${CONFIG_}<FOO> || ${CONFIG_}<FOO>_MODULE\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] = "\+#if IS_ENABLED($config)";
+			}
+		}
+
+# check for /* fallthrough */ like comment, prefer fallthrough;
+		my @fallthroughs = (
+			'fallthrough',
+			'@fallthrough@',
+			'lint -fallthrough[ \t]*',
+			'intentional(?:ly)?[ \t]*fall(?:(?:s | |-)[Tt]|t)hr(?:ough|u|ew)',
+			'(?:else,?\s*)?FALL(?:S | |-)?THR(?:OUGH|U|EW)[ \t.!]*(?:-[^\n\r]*)?',
+			'Fall(?:(?:s | |-)[Tt]|t)hr(?:ough|u|ew)[ \t.!]*(?:-[^\n\r]*)?',
+			'fall(?:s | |-)?thr(?:ough|u|ew)[ \t.!]*(?:-[^\n\r]*)?',
+		    );
+		if ($raw_comment ne '') {
+			foreach my $ft (@fallthroughs) {
+				if ($raw_comment =~ /$ft/) {
+					my $msg_level = \&WARN;
+					$msg_level = \&CHK if ($file);
+					&{$msg_level}("PREFER_FALLTHROUGH",
+						      "Prefer 'fallthrough;' over fallthrough comment\n" . $herecurr);
+					last;
+				}
+			}
+		}
+
+# check for switch/default statements without a break;
+		if ($perl_version_ok &&
+		    defined $stat &&
+		    $stat =~ /^\+[$;\s]*(?:case[$;\s]+\w+[$;\s]*:[$;\s]*|)*[$;\s]*\bdefault[$;\s]*:[$;\s]*;/g) {
+			my $cnt = statement_rawlines($stat);
+			my $herectx = get_stat_here($linenr, $cnt, $here);
+
+			WARN("DEFAULT_NO_BREAK",
+			     "switch default: should use break\n" . $herectx);
+		}
+
+# check for gcc specific __FUNCTION__
+		if ($line =~ /\b__FUNCTION__\b/) {
+			if (WARN("USE_FUNC",
+				 "__func__ should be used instead of gcc specific __FUNCTION__\n"  . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\b__FUNCTION__\b/__func__/g;
+			}
+		}
+
+# check for uses of __DATE__, __TIME__, __TIMESTAMP__
+		while ($line =~ /\b(__(?:DATE|TIME|TIMESTAMP)__)\b/g) {
+			ERROR("DATE_TIME",
+			      "Use of the '$1' macro makes the build non-deterministic\n" . $herecurr);
+		}
+
+# check for use of yield()
+		if ($line =~ /\byield\s*\(\s*\)/) {
+			WARN("YIELD",
+			     "Using yield() is generally wrong. See yield() kernel-doc (sched/core.c)\n"  . $herecurr);
+		}
+
+# check for comparisons against true and false
+		if ($line =~ /\+\s*(.*?)\b(true|false|$Lval)\s*(==|\!=)\s*(true|false|$Lval)\b(.*)$/i) {
+			my $lead = $1;
+			my $arg = $2;
+			my $test = $3;
+			my $otype = $4;
+			my $trail = $5;
+			my $op = "!";
+
+			($arg, $otype) = ($otype, $arg) if ($arg =~ /^(?:true|false)$/i);
+
+			my $type = lc($otype);
+			if ($type =~ /^(?:true|false)$/) {
+				if (("$test" eq "==" && "$type" eq "true") ||
+				    ("$test" eq "!=" && "$type" eq "false")) {
+					$op = "";
+				}
+
+				CHK("BOOL_COMPARISON",
+				    "Using comparison to $otype is error prone\n" . $herecurr);
+
+## maybe suggesting a correct construct would better
+##				    "Using comparison to $otype is error prone.  Perhaps use '${lead}${op}${arg}${trail}'\n" . $herecurr);
+
+			}
+		}
+
+# check for semaphores initialized locked
+		if ($line =~ /^.\s*sema_init.+,\W?0\W?\)/) {
+			WARN("CONSIDER_COMPLETION",
+			     "consider using a completion\n" . $herecurr);
+		}
+
+# recommend kstrto* over simple_strto* and strict_strto*
+		if ($line =~ /\b((simple|strict)_(strto(l|ll|ul|ull)))\s*\(/) {
+			WARN("CONSIDER_KSTRTO",
+			     "$1 is obsolete, use k$3 instead\n" . $herecurr);
+		}
+
+# check for __initcall(), use device_initcall() explicitly or more appropriate function please
+		if ($line =~ /^.\s*__initcall\s*\(/) {
+			WARN("USE_DEVICE_INITCALL",
+			     "please use device_initcall() or more appropriate function instead of __initcall() (see include/linux/init.h)\n" . $herecurr);
+		}
+
+# check for spin_is_locked(), suggest lockdep instead
+		if ($line =~ /\bspin_is_locked\(/) {
+			WARN("USE_LOCKDEP",
+			     "Where possible, use lockdep_assert_held instead of assertions based on spin_is_locked\n" . $herecurr);
+		}
+
+# check for deprecated apis
+		if ($line =~ /\b($deprecated_apis_search)\b\s*\(/) {
+			my $deprecated_api = $1;
+			my $new_api = $deprecated_apis{$deprecated_api};
+			WARN("DEPRECATED_API",
+			     "Deprecated use of '$deprecated_api', prefer '$new_api' instead\n" . $herecurr);
+		}
+
+# check for various structs that are normally const (ops, kgdb, device_tree)
+# and avoid what seem like struct definitions 'struct foo {'
+		if (defined($const_structs) &&
+		    $line !~ /\bconst\b/ &&
+		    $line =~ /\bstruct\s+($const_structs)\b(?!\s*\{)/) {
+			WARN("CONST_STRUCT",
+			     "struct $1 should normally be const\n" . $herecurr);
+		}
+
+# use of NR_CPUS is usually wrong
+# ignore definitions of NR_CPUS and usage to define arrays as likely right
+# ignore designated initializers using NR_CPUS
+		if ($line =~ /\bNR_CPUS\b/ &&
+		    $line !~ /^.\s*\s*#\s*if\b.*\bNR_CPUS\b/ &&
+		    $line !~ /^.\s*\s*#\s*define\b.*\bNR_CPUS\b/ &&
+		    $line !~ /^.\s*$Declare\s.*\[[^\]]*NR_CPUS[^\]]*\]/ &&
+		    $line !~ /\[[^\]]*\.\.\.[^\]]*NR_CPUS[^\]]*\]/ &&
+		    $line !~ /\[[^\]]*NR_CPUS[^\]]*\.\.\.[^\]]*\]/ &&
+		    $line !~ /^.\s*\.\w+\s*=\s*.*\bNR_CPUS\b/)
+		{
+			WARN("NR_CPUS",
+			     "usage of NR_CPUS is often wrong - consider using cpu_possible(), num_possible_cpus(), for_each_possible_cpu(), etc\n" . $herecurr);
+		}
+
+# Use of __ARCH_HAS_<FOO> or ARCH_HAVE_<BAR> is wrong.
+		if ($line =~ /\+\s*#\s*define\s+((?:__)?ARCH_(?:HAS|HAVE)\w*)\b/) {
+			ERROR("DEFINE_ARCH_HAS",
+			      "#define of '$1' is wrong - use Kconfig variables or standard guards instead\n" . $herecurr);
+		}
+
+# likely/unlikely comparisons similar to "(likely(foo) > 0)"
+		if ($perl_version_ok &&
+		    $line =~ /\b((?:un)?likely)\s*\(\s*$FuncArg\s*\)\s*$Compare/) {
+			WARN("LIKELY_MISUSE",
+			     "Using $1 should generally have parentheses around the comparison\n" . $herecurr);
+		}
+
+# return sysfs_emit(foo, fmt, ...) fmt without newline
+		if ($line =~ /\breturn\s+sysfs_emit\s*\(\s*$FuncArg\s*,\s*($String)/ &&
+		    substr($rawline, $-[6], $+[6] - $-[6]) !~ /\\n"$/) {
+			my $offset = $+[6] - 1;
+			if (WARN("SYSFS_EMIT",
+				 "return sysfs_emit(...) formats should include a terminating newline\n" . $herecurr) &&
+			    $fix) {
+				substr($fixed[$fixlinenr], $offset, 0) = '\\n';
+			}
+		}
+
+# nested likely/unlikely calls
+		if ($line =~ /\b(?:(?:un)?likely)\s*\(\s*!?\s*(IS_ERR(?:_OR_NULL|_VALUE)?|WARN)/) {
+			WARN("LIKELY_MISUSE",
+			     "nested (un)?likely() calls, $1 already uses unlikely() internally\n" . $herecurr);
+		}
+
+# whine mightly about in_atomic
+		if ($line =~ /\bin_atomic\s*\(/) {
+			if ($realfile =~ m@^drivers/@) {
+				ERROR("IN_ATOMIC",
+				      "do not use in_atomic in drivers\n" . $herecurr);
+			} elsif ($realfile !~ m@^kernel/@) {
+				WARN("IN_ATOMIC",
+				     "use of in_atomic() is incorrect outside core kernel code\n" . $herecurr);
+			}
+		}
+
+# check for lockdep_set_novalidate_class
+		if ($line =~ /^.\s*lockdep_set_novalidate_class\s*\(/ ||
+		    $line =~ /__lockdep_no_validate__\s*\)/ ) {
+			if ($realfile !~ m@^kernel/lockdep@ &&
+			    $realfile !~ m@^include/linux/lockdep@ &&
+			    $realfile !~ m@^drivers/base/core@) {
+				ERROR("LOCKDEP",
+				      "lockdep_no_validate class is reserved for device->mutex.\n" . $herecurr);
+			}
+		}
+
+		if ($line =~ /debugfs_create_\w+.*\b$mode_perms_world_writable\b/ ||
+		    $line =~ /DEVICE_ATTR.*\b$mode_perms_world_writable\b/) {
+			WARN("EXPORTED_WORLD_WRITABLE",
+			     "Exporting world writable files is usually an error. Consider more restrictive permissions.\n" . $herecurr);
+		}
+
+# check for DEVICE_ATTR uses that could be DEVICE_ATTR_<FOO>
+# and whether or not function naming is typical and if
+# DEVICE_ATTR permissions uses are unusual too
+		if ($perl_version_ok &&
+		    defined $stat &&
+		    $stat =~ /\bDEVICE_ATTR\s*\(\s*(\w+)\s*,\s*\(?\s*(\s*(?:${multi_mode_perms_string_search}|0[0-7]{3,3})\s*)\s*\)?\s*,\s*(\w+)\s*,\s*(\w+)\s*\)/) {
+			my $var = $1;
+			my $perms = $2;
+			my $show = $3;
+			my $store = $4;
+			my $octal_perms = perms_to_octal($perms);
+			if ($show =~ /^${var}_show$/ &&
+			    $store =~ /^${var}_store$/ &&
+			    $octal_perms eq "0644") {
+				if (WARN("DEVICE_ATTR_RW",
+					 "Use DEVICE_ATTR_RW\n" . $herecurr) &&
+				    $fix) {
+					$fixed[$fixlinenr] =~ s/\bDEVICE_ATTR\s*\(\s*$var\s*,\s*\Q$perms\E\s*,\s*$show\s*,\s*$store\s*\)/DEVICE_ATTR_RW(${var})/;
+				}
+			} elsif ($show =~ /^${var}_show$/ &&
+				 $store =~ /^NULL$/ &&
+				 $octal_perms eq "0444") {
+				if (WARN("DEVICE_ATTR_RO",
+					 "Use DEVICE_ATTR_RO\n" . $herecurr) &&
+				    $fix) {
+					$fixed[$fixlinenr] =~ s/\bDEVICE_ATTR\s*\(\s*$var\s*,\s*\Q$perms\E\s*,\s*$show\s*,\s*NULL\s*\)/DEVICE_ATTR_RO(${var})/;
+				}
+			} elsif ($show =~ /^NULL$/ &&
+				 $store =~ /^${var}_store$/ &&
+				 $octal_perms eq "0200") {
+				if (WARN("DEVICE_ATTR_WO",
+					 "Use DEVICE_ATTR_WO\n" . $herecurr) &&
+				    $fix) {
+					$fixed[$fixlinenr] =~ s/\bDEVICE_ATTR\s*\(\s*$var\s*,\s*\Q$perms\E\s*,\s*NULL\s*,\s*$store\s*\)/DEVICE_ATTR_WO(${var})/;
+				}
+			} elsif ($octal_perms eq "0644" ||
+				 $octal_perms eq "0444" ||
+				 $octal_perms eq "0200") {
+				my $newshow = "$show";
+				$newshow = "${var}_show" if ($show ne "NULL" && $show ne "${var}_show");
+				my $newstore = $store;
+				$newstore = "${var}_store" if ($store ne "NULL" && $store ne "${var}_store");
+				my $rename = "";
+				if ($show ne $newshow) {
+					$rename .= " '$show' to '$newshow'";
+				}
+				if ($store ne $newstore) {
+					$rename .= " '$store' to '$newstore'";
+				}
+				WARN("DEVICE_ATTR_FUNCTIONS",
+				     "Consider renaming function(s)$rename\n" . $herecurr);
+			} else {
+				WARN("DEVICE_ATTR_PERMS",
+				     "DEVICE_ATTR unusual permissions '$perms' used\n" . $herecurr);
+			}
+		}
+
+# Mode permission misuses where it seems decimal should be octal
+# This uses a shortcut match to avoid unnecessary uses of a slow foreach loop
+# o Ignore module_param*(...) uses with a decimal 0 permission as that has a
+#   specific definition of not visible in sysfs.
+# o Ignore proc_create*(...) uses with a decimal 0 permission as that means
+#   use the default permissions
+		if ($perl_version_ok &&
+		    defined $stat &&
+		    $line =~ /$mode_perms_search/) {
+			foreach my $entry (@mode_permission_funcs) {
+				my $func = $entry->[0];
+				my $arg_pos = $entry->[1];
+
+				my $lc = $stat =~ tr@\n@@;
+				$lc = $lc + $linenr;
+				my $stat_real = get_stat_real($linenr, $lc);
+
+				my $skip_args = "";
+				if ($arg_pos > 1) {
+					$arg_pos--;
+					$skip_args = "(?:\\s*$FuncArg\\s*,\\s*){$arg_pos,$arg_pos}";
+				}
+				my $test = "\\b$func\\s*\\(${skip_args}($FuncArg(?:\\|\\s*$FuncArg)*)\\s*[,\\)]";
+				if ($stat =~ /$test/) {
+					my $val = $1;
+					$val = $6 if ($skip_args ne "");
+					if (!($func =~ /^(?:module_param|proc_create)/ && $val eq "0") &&
+					    (($val =~ /^$Int$/ && $val !~ /^$Octal$/) ||
+					     ($val =~ /^$Octal$/ && length($val) ne 4))) {
+						ERROR("NON_OCTAL_PERMISSIONS",
+						      "Use 4 digit octal (0777) not decimal permissions\n" . "$here\n" . $stat_real);
+					}
+					if ($val =~ /^$Octal$/ && (oct($val) & 02)) {
+						ERROR("EXPORTED_WORLD_WRITABLE",
+						      "Exporting writable files is usually an error. Consider more restrictive permissions.\n" . "$here\n" . $stat_real);
+					}
+				}
+			}
+		}
+
+# check for uses of S_<PERMS> that could be octal for readability
+		while ($line =~ m{\b($multi_mode_perms_string_search)\b}g) {
+			my $oval = $1;
+			my $octal = perms_to_octal($oval);
+			if (WARN("SYMBOLIC_PERMS",
+				 "Symbolic permissions '$oval' are not preferred. Consider using octal permissions '$octal'.\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\Q$oval\E/$octal/;
+			}
+		}
+
+# validate content of MODULE_LICENSE against list from include/linux/module.h
+		if ($line =~ /\bMODULE_LICENSE\s*\(\s*($String)\s*\)/) {
+			my $extracted_string = get_quoted_string($line, $rawline);
+			my $valid_licenses = qr{
+						GPL|
+						GPL\ v2|
+						GPL\ and\ additional\ rights|
+						Dual\ BSD/GPL|
+						Dual\ MIT/GPL|
+						Dual\ MPL/GPL|
+						Proprietary
+					}x;
+			if ($extracted_string !~ /^"(?:$valid_licenses)"$/x) {
+				WARN("MODULE_LICENSE",
+				     "unknown module license " . $extracted_string . "\n" . $herecurr);
+			}
+		}
+
+# check for sysctl duplicate constants
+		if ($line =~ /\.extra[12]\s*=\s*&(zero|one|int_max)\b/) {
+			WARN("DUPLICATED_SYSCTL_CONST",
+				"duplicated sysctl range checking value '$1', consider using the shared one in include/linux/sysctl.h\n" . $herecurr);
+		}
+	}
+
+	# If we have no input at all, then there is nothing to report on
+	# so just keep quiet.
+	if ($#rawlines == -1) {
+		exit(0);
+	}
+
+	# In mailback mode only produce a report in the negative, for
+	# things that appear to be patches.
+	if ($mailback && ($clean == 1 || !$is_patch)) {
+		exit(0);
+	}
+
+	# This is not a patch, and we are in 'no-patch' mode so
+	# just keep quiet.
+	if (!$chk_patch && !$is_patch) {
+		exit(0);
+	}
+
+	if (!$is_patch && $filename !~ /cover-letter\.patch$/) {
+		ERROR("NOT_UNIFIED_DIFF",
+		      "Does not appear to be a unified-diff format patch\n");
+	}
+	if ($is_patch && $has_commit_log && $chk_signoff) {
+		if ($signoff == 0) {
+			ERROR("MISSING_SIGN_OFF",
+			      "Missing Signed-off-by: line(s)\n");
+		} elsif ($authorsignoff != 1) {
+			# authorsignoff values:
+			# 0 -> missing sign off
+			# 1 -> sign off identical
+			# 2 -> names and addresses match, comments mismatch
+			# 3 -> addresses match, names different
+			# 4 -> names match, addresses different
+			# 5 -> names match, addresses excluding subaddress details (refer RFC 5233) match
+
+			my $sob_msg = "'From: $author' != 'Signed-off-by: $author_sob'";
+
+			if ($authorsignoff == 0) {
+				ERROR("NO_AUTHOR_SIGN_OFF",
+				      "Missing Signed-off-by: line by nominal patch author '$author'\n");
+			} elsif ($authorsignoff == 2) {
+				CHK("FROM_SIGN_OFF_MISMATCH",
+				    "From:/Signed-off-by: email comments mismatch: $sob_msg\n");
+			} elsif ($authorsignoff == 3) {
+				WARN("FROM_SIGN_OFF_MISMATCH",
+				     "From:/Signed-off-by: email name mismatch: $sob_msg\n");
+			} elsif ($authorsignoff == 4) {
+				WARN("FROM_SIGN_OFF_MISMATCH",
+				     "From:/Signed-off-by: email address mismatch: $sob_msg\n");
+			} elsif ($authorsignoff == 5) {
+				WARN("FROM_SIGN_OFF_MISMATCH",
+				     "From:/Signed-off-by: email subaddress mismatch: $sob_msg\n");
+			}
+		}
+	}
+
+	print report_dump();
+	if ($summary && !($clean == 1 && $quiet == 1)) {
+		print "$filename " if ($summary_file);
+		print "total: $cnt_error errors, $cnt_warn warnings, " .
+			(($check)? "$cnt_chk checks, " : "") .
+			"$cnt_lines lines checked\n";
+	}
+
+	if ($quiet == 0) {
+		# If there were any defects found and not already fixing them
+		if (!$clean and !$fix) {
+			print << "EOM"
+
+NOTE: For some of the reported defects, checkpatch may be able to
+      mechanically convert to the typical style using --fix or --fix-inplace.
+EOM
+		}
+		# If there were whitespace errors which cleanpatch can fix
+		# then suggest that.
+		if ($rpt_cleaners) {
+			$rpt_cleaners = 0;
+			print << "EOM"
+
+NOTE: Whitespace errors detected.
+      You may wish to use scripts/cleanpatch or scripts/cleanfile
+EOM
+		}
+	}
+
+	if ($clean == 0 && $fix &&
+	    ("@rawlines" ne "@fixed" ||
+	     $#fixed_inserted >= 0 || $#fixed_deleted >= 0)) {
+		my $newfile = $filename;
+		$newfile .= ".EXPERIMENTAL-checkpatch-fixes" if (!$fix_inplace);
+		my $linecount = 0;
+		my $f;
+
+		@fixed = fix_inserted_deleted_lines(\@fixed, \@fixed_inserted, \@fixed_deleted);
+
+		open($f, '>', $newfile)
+		    or die "$P: Can't open $newfile for write\n";
+		foreach my $fixed_line (@fixed) {
+			$linecount++;
+			if ($file) {
+				if ($linecount > 3) {
+					$fixed_line =~ s/^\+//;
+					print $f $fixed_line . "\n";
+				}
+			} else {
+				print $f $fixed_line . "\n";
+			}
+		}
+		close($f);
+
+		if (!$quiet) {
+			print << "EOM";
+
+Wrote EXPERIMENTAL --fix correction(s) to '$newfile'
+
+Do _NOT_ trust the results written to this file.
+Do _NOT_ submit these changes without inspecting them for correctness.
+
+This EXPERIMENTAL file is simply a convenience to help rewrite patches.
+No warranties, expressed or implied...
+EOM
+		}
+	}
+
+	if ($quiet == 0) {
+		print "\n";
+		if ($clean == 1) {
+			print "$vname has no obvious style problems and is ready for submission.\n";
+		} else {
+			print "$vname has style problems, please review.\n";
+		}
+	}
+	return $clean;
+}
diff --git a/tools/checkpatch/const_structs.checkpatch b/tools/checkpatch/const_structs.checkpatch
new file mode 100644
index 0000000..421376d
--- /dev/null
+++ b/tools/checkpatch/const_structs.checkpatch
@@ -0,0 +1 @@
+dummy
diff --git a/tools/checkpatch/spelling.txt b/tools/checkpatch/spelling.txt
new file mode 100644
index 0000000..4a08d91
--- /dev/null
+++ b/tools/checkpatch/spelling.txt
@@ -0,0 +1,1612 @@
+# Originally from Debian's Lintian tool. Various false positives have been
+# removed, and various additions have been made as they've been discovered
+# in the kernel source.
+#
+# License: GPLv2
+#
+# The format of each line is:
+# mistake||correction
+#
+# This file is taken from Linux kernel 5.15.0-rc2 at SHA:
+# c1c9142004e7e21d6d3d2cd6a339845771ce6a27
+#
+abandonning||abandoning
+abigious||ambiguous
+abitrary||arbitrary
+abitrate||arbitrate
+abnornally||abnormally
+abnrormal||abnormal
+abord||abort
+aboslute||absolute
+abov||above
+abreviated||abbreviated
+absense||absence
+absolut||absolute
+absoulte||absolute
+acccess||access
+acceess||access
+accelaration||acceleration
+acceleratoin||acceleration
+accelleration||acceleration
+accesing||accessing
+accesnt||accent
+accessable||accessible
+accesss||access
+accidentaly||accidentally
+accidentually||accidentally
+acclerated||accelerated
+accoding||according
+accomodate||accommodate
+accomodates||accommodates
+accordign||according
+accoring||according
+accout||account
+accquire||acquire
+accquired||acquired
+accross||across
+accumalate||accumulate
+accumalator||accumulator
+acessable||accessible
+acess||access
+acessing||accessing
+achitecture||architecture
+acient||ancient
+acitions||actions
+acitve||active
+acknowldegement||acknowledgment
+acknowledgement||acknowledgment
+ackowledge||acknowledge
+ackowledged||acknowledged
+acording||according
+activete||activate
+actived||activated
+actualy||actually
+acumulating||accumulating
+acumulative||accumulative
+acumulator||accumulator
+acutally||actually
+adapater||adapter
+addional||additional
+additionaly||additionally
+additonal||additional
+addres||address
+adddress||address
+addreses||addresses
+addresss||address
+addrress||address
+aditional||additional
+aditionally||additionally
+aditionaly||additionally
+adminstrative||administrative
+adress||address
+adresses||addresses
+adrresses||addresses
+advertisment||advertisement
+adviced||advised
+afecting||affecting
+againt||against
+agaist||against
+aggreataon||aggregation
+aggreation||aggregation
+ajust||adjust
+albumns||albums
+alegorical||allegorical
+algined||aligned
+algorith||algorithm
+algorithmical||algorithmically
+algoritm||algorithm
+algoritms||algorithms
+algorithmn||algorithm
+algorrithm||algorithm
+algorritm||algorithm
+aligment||alignment
+alignement||alignment
+allign||align
+alligned||aligned
+alllocate||allocate
+alloated||allocated
+allocatote||allocate
+allocatrd||allocated
+allocte||allocate
+allocted||allocated
+allpication||application
+alocate||allocate
+alogirhtms||algorithms
+alogrithm||algorithm
+alot||a lot
+alow||allow
+alows||allows
+alreay||already
+alredy||already
+altough||although
+alue||value
+ambigious||ambiguous
+ambigous||ambiguous
+amoung||among
+amout||amount
+amplifer||amplifier
+amplifyer||amplifier
+an union||a union
+an user||a user
+an userspace||a userspace
+an one||a one
+analysator||analyzer
+ang||and
+anniversery||anniversary
+annoucement||announcement
+anomolies||anomalies
+anomoly||anomaly
+anway||anyway
+aplication||application
+appearence||appearance
+applicaion||application
+appliction||application
+applictions||applications
+applys||applies
+appplications||applications
+appropiate||appropriate
+appropriatly||appropriately
+approriate||appropriate
+approriately||appropriately
+apropriate||appropriate
+aquainted||acquainted
+aquired||acquired
+aquisition||acquisition
+arbitary||arbitrary
+architechture||architecture
+arguement||argument
+arguements||arguments
+arithmatic||arithmetic
+aritmetic||arithmetic
+arne't||aren't
+arraival||arrival
+artifical||artificial
+artillary||artillery
+asign||assign
+asser||assert
+assertation||assertion
+assertting||asserting
+assgined||assigned
+assiged||assigned
+assigment||assignment
+assigments||assignments
+assistent||assistant
+assocaited||associated
+assocating||associating
+assocation||association
+associcated||associated
+assotiated||associated
+asssert||assert
+assum||assume
+assumtpion||assumption
+asuming||assuming
+asycronous||asynchronous
+asynchnous||asynchronous
+asynchromous||asynchronous
+asymetric||asymmetric
+asymmeric||asymmetric
+atleast||at least
+atomatically||automatically
+atomicly||atomically
+atempt||attempt
+atrributes||attributes
+attachement||attachment
+attatch||attach
+attched||attached
+attemp||attempt
+attemps||attempts
+attemping||attempting
+attepmpt||attempt
+attnetion||attention
+attruibutes||attributes
+authentification||authentication
+authenicated||authenticated
+automaticaly||automatically
+automaticly||automatically
+automatize||automate
+automatized||automated
+automatizes||automates
+autonymous||autonomous
+auxillary||auxiliary
+auxilliary||auxiliary
+avaiable||available
+avaible||available
+availabe||available
+availabled||available
+availablity||availability
+availaible||available
+availale||available
+availavility||availability
+availble||available
+availiable||available
+availible||available
+avalable||available
+avaliable||available
+aysnc||async
+backgroud||background
+backword||backward
+backwords||backwards
+bahavior||behavior
+bakup||backup
+baloon||balloon
+baloons||balloons
+bandwith||bandwidth
+banlance||balance
+batery||battery
+beacuse||because
+becasue||because
+becomming||becoming
+becuase||because
+beeing||being
+befor||before
+begining||beginning
+beter||better
+betweeen||between
+bianries||binaries
+bitmast||bitmask
+boardcast||broadcast
+borad||board
+boundry||boundary
+brievely||briefly
+brigde||bridge
+broadcase||broadcast
+broadcat||broadcast
+bufer||buffer
+bufufer||buffer
+cacluated||calculated
+caculate||calculate
+caculation||calculation
+cadidate||candidate
+cahces||caches
+calender||calendar
+calescing||coalescing
+calle||called
+callibration||calibration
+callled||called
+callser||caller
+calucate||calculate
+calulate||calculate
+cancelation||cancellation
+cancle||cancel
+canot||cannot
+capabilites||capabilities
+capabilties||capabilities
+capabilty||capability
+capabitilies||capabilities
+capablity||capability
+capatibilities||capabilities
+capapbilities||capabilities
+caputure||capture
+carefuly||carefully
+cariage||carriage
+catagory||category
+cehck||check
+challange||challenge
+challanges||challenges
+chache||cache
+chanell||channel
+changable||changeable
+chanined||chained
+channle||channel
+channnel||channel
+charachter||character
+charachters||characters
+charactor||character
+charater||character
+charaters||characters
+charcter||character
+chcek||check
+chck||check
+checksumed||checksummed
+checksuming||checksumming
+childern||children
+childs||children
+chiled||child
+chked||checked
+chnage||change
+chnages||changes
+chnnel||channel
+choosen||chosen
+chouse||chose
+circumvernt||circumvent
+claread||cleared
+clared||cleared
+closeing||closing
+clustred||clustered
+cnfiguration||configuration
+coexistance||coexistence
+colescing||coalescing
+collapsable||collapsible
+colorfull||colorful
+comand||command
+comit||commit
+commerical||commercial
+comming||coming
+comminucation||communication
+commited||committed
+commiting||committing
+committ||commit
+commnunication||communication
+commoditiy||commodity
+comsume||consume
+comsumer||consumer
+comsuming||consuming
+compability||compatibility
+compaibility||compatibility
+comparsion||comparison
+compatability||compatibility
+compatable||compatible
+compatibililty||compatibility
+compatibiliy||compatibility
+compatibilty||compatibility
+compatiblity||compatibility
+competion||completion
+compilant||compliant
+compleatly||completely
+completition||completion
+completly||completely
+complient||compliant
+componnents||components
+compoment||component
+comppatible||compatible
+compres||compress
+compresion||compression
+comression||compression
+comunicate||communicate
+comunication||communication
+conbination||combination
+conditionaly||conditionally
+conditon||condition
+condtion||condition
+conected||connected
+conector||connector
+configration||configuration
+configred||configured
+configuartion||configuration
+configuation||configuration
+configued||configured
+configuratoin||configuration
+configuraton||configuration
+configuretion||configuration
+configutation||configuration
+conider||consider
+conjuction||conjunction
+connecetd||connected
+connectinos||connections
+connetor||connector
+connnection||connection
+connnections||connections
+consistancy||consistency
+consistant||consistent
+containes||contains
+containts||contains
+contaisn||contains
+contant||contact
+contence||contents
+contiguos||contiguous
+continious||continuous
+continous||continuous
+continously||continuously
+continueing||continuing
+contraints||constraints
+contruct||construct
+contol||control
+contoller||controller
+controled||controlled
+controler||controller
+controll||control
+contruction||construction
+contry||country
+conuntry||country
+convertion||conversion
+convertor||converter
+convienient||convenient
+convinient||convenient
+corected||corrected
+correponding||corresponding
+correponds||corresponds
+correspoding||corresponding
+cotrol||control
+cound||could
+couter||counter
+coutner||counter
+cryptocraphic||cryptographic
+cunter||counter
+curently||currently
+cylic||cyclic
+dafault||default
+deactive||deactivate
+deafult||default
+deamon||daemon
+debouce||debounce
+decendant||descendant
+decendants||descendants
+decompres||decompress
+decsribed||described
+decription||description
+dectected||detected
+defailt||default
+deferal||deferral
+deffered||deferred
+defferred||deferred
+definate||definite
+definately||definitely
+definiation||definition
+defintion||definition
+defintions||definitions
+defualt||default
+defult||default
+deintializing||deinitializing
+deintialize||deinitialize
+deintialized||deinitialized
+deivce||device
+delared||declared
+delare||declare
+delares||declares
+delaring||declaring
+delemiter||delimiter
+delievered||delivered
+demodualtor||demodulator
+demension||dimension
+dependancies||dependencies
+dependancy||dependency
+dependant||dependent
+dependend||dependent
+depreacted||deprecated
+depreacte||deprecate
+desactivate||deactivate
+desciptor||descriptor
+desciptors||descriptors
+descripto||descriptor
+descripton||description
+descrition||description
+descritptor||descriptor
+desctiptor||descriptor
+desriptor||descriptor
+desriptors||descriptors
+desination||destination
+destionation||destination
+destoried||destroyed
+destory||destroy
+destoryed||destroyed
+destorys||destroys
+destroied||destroyed
+detabase||database
+deteced||detected
+detectt||detect
+develope||develop
+developement||development
+developped||developed
+developpement||development
+developper||developer
+developpment||development
+deveolpment||development
+devided||divided
+deviece||device
+devision||division
+diable||disable
+diabled||disabled
+dicline||decline
+dictionnary||dictionary
+didnt||didn't
+diferent||different
+differrence||difference
+diffrent||different
+differenciate||differentiate
+diffrentiate||differentiate
+difinition||definition
+digial||digital
+dimention||dimension
+dimesions||dimensions
+diconnected||disconnected
+disabed||disabled
+disble||disable
+disgest||digest
+disired||desired
+dispalying||displaying
+diplay||display
+directon||direction
+direcly||directly
+direectly||directly
+diregard||disregard
+disassocation||disassociation
+disapear||disappear
+disapeared||disappeared
+disappared||disappeared
+disbale||disable
+disbaled||disabled
+disble||disable
+disbled||disabled
+disconnet||disconnect
+discontinous||discontinuous
+disharge||discharge
+disnabled||disabled
+dispertion||dispersion
+dissapears||disappears
+dissconect||disconnect
+distiction||distinction
+divisable||divisible
+divsiors||divisors
+docuentation||documentation
+documantation||documentation
+documentaion||documentation
+documment||document
+doesnt||doesn't
+donwload||download
+donwloading||downloading
+dorp||drop
+dosen||doesn
+downlad||download
+downlads||downloads
+droped||dropped
+droput||dropout
+druing||during
+dyanmic||dynamic
+dynmaic||dynamic
+eanable||enable
+eanble||enable
+easilly||easily
+ecspecially||especially
+edditable||editable
+editting||editing
+efective||effective
+effectivness||effectiveness
+efficently||efficiently
+ehther||ether
+eigth||eight
+elementry||elementary
+eletronic||electronic
+embeded||embedded
+enabledi||enabled
+enbale||enable
+enble||enable
+enchanced||enhanced
+encorporating||incorporating
+encrupted||encrypted
+encrypiton||encryption
+encryptio||encryption
+endianess||endianness
+enpoint||endpoint
+enhaced||enhanced
+enlightnment||enlightenment
+enqueing||enqueuing
+entires||entries
+entites||entities
+entrys||entries
+enocded||encoded
+enought||enough
+enterily||entirely
+enviroiment||environment
+enviroment||environment
+environement||environment
+environent||environment
+eqivalent||equivalent
+equiped||equipped
+equivelant||equivalent
+equivilant||equivalent
+eror||error
+errorr||error
+errror||error
+estbalishment||establishment
+etsablishment||establishment
+etsbalishment||establishment
+evalute||evaluate
+evalutes||evaluates
+evalution||evaluation
+excecutable||executable
+exceded||exceeded
+exceds||exceeds
+exceeed||exceed
+excellant||excellent
+execeeded||exceeded
+execeeds||exceeds
+exeed||exceed
+exeeds||exceeds
+exeuction||execution
+existance||existence
+existant||existent
+exixt||exist
+exlcude||exclude
+exlcusive||exclusive
+exmaple||example
+expecially||especially
+experies||expires
+explicite||explicit
+explicitely||explicitly
+explict||explicit
+explictely||explicitly
+explictly||explicitly
+expresion||expression
+exprimental||experimental
+extened||extended
+exteneded||extended
+extensability||extensibility
+extention||extension
+extenstion||extension
+extracter||extractor
+faied||failed
+faield||failed
+faild||failed
+failded||failed
+failer||failure
+faill||fail
+failied||failed
+faillure||failure
+failue||failure
+failuer||failure
+failng||failing
+faireness||fairness
+falied||failed
+faliure||failure
+fallbck||fallback
+familar||familiar
+fatser||faster
+feauture||feature
+feautures||features
+fetaure||feature
+fetaures||features
+fileystem||filesystem
+fimrware||firmware
+fimware||firmware
+firmare||firmware
+firmaware||firmware
+firware||firmware
+firwmare||firmware
+finanize||finalize
+findn||find
+finilizes||finalizes
+finsih||finish
+flusing||flushing
+folloing||following
+followign||following
+followings||following
+follwing||following
+fonud||found
+forseeable||foreseeable
+forse||force
+fortan||fortran
+forwardig||forwarding
+frambuffer||framebuffer
+framming||framing
+framwork||framework
+frequence||frequency
+frequncy||frequency
+frequancy||frequency
+frome||from
+fucntion||function
+fuction||function
+fuctions||functions
+fullill||fulfill
+funcation||function
+funcion||function
+functionallity||functionality
+functionaly||functionally
+functionnality||functionality
+functonality||functionality
+funtion||function
+funtions||functions
+furthur||further
+futhermore||furthermore
+futrue||future
+gatable||gateable
+gateing||gating
+gauage||gauge
+gaurenteed||guaranteed
+generiously||generously
+genereate||generate
+genereted||generated
+genric||generic
+globel||global
+grabing||grabbing
+grahical||graphical
+grahpical||graphical
+granularty||granularity
+grapic||graphic
+grranted||granted
+guage||gauge
+guarenteed||guaranteed
+guarentee||guarantee
+halfs||halves
+hander||handler
+handfull||handful
+hanlde||handle
+hanled||handled
+happend||happened
+hardare||hardware
+harware||hardware
+havind||having
+heirarchically||hierarchically
+heirarchy||hierarchy
+helpfull||helpful
+hearbeat||heartbeat
+heterogenous||heterogeneous
+hexdecimal||hexadecimal
+hybernate||hibernate
+hierachy||hierarchy
+hierarchie||hierarchy
+homogenous||homogeneous
+howver||however
+hsould||should
+hypervior||hypervisor
+hypter||hyper
+identidier||identifier
+iligal||illegal
+illigal||illegal
+illgal||illegal
+iomaped||iomapped
+imblance||imbalance
+immeadiately||immediately
+immedaite||immediate
+immedate||immediate
+immediatelly||immediately
+immediatly||immediately
+immidiate||immediate
+immutible||immutable
+impelentation||implementation
+impementated||implemented
+implemantation||implementation
+implemenation||implementation
+implementaiton||implementation
+implementated||implemented
+implemention||implementation
+implementd||implemented
+implemetation||implementation
+implemntation||implementation
+implentation||implementation
+implmentation||implementation
+implmenting||implementing
+incative||inactive
+incomming||incoming
+incompatabilities||incompatibilities
+incompatable||incompatible
+incompatble||incompatible
+inconsistant||inconsistent
+increas||increase
+incremeted||incremented
+incrment||increment
+incuding||including
+inculde||include
+indendation||indentation
+indended||intended
+independant||independent
+independantly||independently
+independed||independent
+indiate||indicate
+indicat||indicate
+inexpect||inexpected
+inferface||interface
+infinit||infinite
+infomation||information
+informatiom||information
+informations||information
+informtion||information
+infromation||information
+ingore||ignore
+inital||initial
+initalized||initialized
+initalised||initialized
+initalise||initialize
+initalize||initialize
+initation||initiation
+initators||initiators
+initialiazation||initialization
+initializationg||initialization
+initializiation||initialization
+initialze||initialize
+initialzed||initialized
+initialzing||initializing
+initilization||initialization
+initilize||initialize
+initliaze||initialize
+initilized||initialized
+inofficial||unofficial
+inrerface||interface
+insititute||institute
+instace||instance
+instal||install
+instanciate||instantiate
+instanciated||instantiated
+instuments||instruments
+insufficent||insufficient
+inteface||interface
+integreated||integrated
+integrety||integrity
+integrey||integrity
+intendet||intended
+intented||intended
+interanl||internal
+interchangable||interchangeable
+interferring||interfering
+interger||integer
+intergrated||integrated
+intermittant||intermittent
+internel||internal
+interoprability||interoperability
+interuupt||interrupt
+interupt||interrupt
+interupts||interrupts
+interrface||interface
+interrrupt||interrupt
+interrup||interrupt
+interrups||interrupts
+interruptted||interrupted
+interupted||interrupted
+intiailized||initialized
+intial||initial
+intialisation||initialisation
+intialised||initialised
+intialise||initialise
+intialization||initialization
+intialized||initialized
+intialize||initialize
+intregral||integral
+intrerrupt||interrupt
+intrrupt||interrupt
+intterrupt||interrupt
+intuative||intuitive
+inavlid||invalid
+invaid||invalid
+invaild||invalid
+invailid||invalid
+invald||invalid
+invalde||invalid
+invalide||invalid
+invalidiate||invalidate
+invalud||invalid
+invididual||individual
+invokation||invocation
+invokations||invocations
+ireelevant||irrelevant
+irrelevent||irrelevant
+isnt||isn't
+isssue||issue
+issus||issues
+iteraions||iterations
+iternations||iterations
+itertation||iteration
+itslef||itself
+jave||java
+jeffies||jiffies
+jumpimng||jumping
+juse||just
+jus||just
+kown||known
+langage||language
+langauage||language
+langauge||language
+langugage||language
+lauch||launch
+layed||laid
+legnth||length
+leightweight||lightweight
+lengh||length
+lenght||length
+lenth||length
+lesstiff||lesstif
+libaries||libraries
+libary||library
+librairies||libraries
+libraris||libraries
+licenceing||licencing
+limted||limited
+logaritmic||logarithmic
+loggging||logging
+loggin||login
+logile||logfile
+loobpack||loopback
+loosing||losing
+losted||lost
+maangement||management
+machinary||machinery
+maibox||mailbox
+maintainance||maintenance
+maintainence||maintenance
+maintan||maintain
+makeing||making
+mailformed||malformed
+malplaced||misplaced
+malplace||misplace
+managable||manageable
+managament||management
+managment||management
+mangement||management
+manger||manager
+manoeuvering||maneuvering
+manufaucturing||manufacturing
+mappping||mapping
+maping||mapping
+matchs||matches
+mathimatical||mathematical
+mathimatic||mathematic
+mathimatics||mathematics
+maximium||maximum
+maxium||maximum
+mechamism||mechanism
+meetign||meeting
+memeory||memory
+memmber||member
+memoery||memory
+memroy||memory
+ment||meant
+mergable||mergeable
+mesage||message
+messags||messages
+messgaes||messages
+messsage||message
+messsages||messages
+metdata||metadata
+micropone||microphone
+microprocesspr||microprocessor
+migrateable||migratable
+milliseonds||milliseconds
+minium||minimum
+minimam||minimum
+miniumum||minimum
+minumum||minimum
+misalinged||misaligned
+miscelleneous||miscellaneous
+misformed||malformed
+mispelled||misspelled
+mispelt||misspelt
+mising||missing
+mismactch||mismatch
+missign||missing
+missmanaged||mismanaged
+missmatch||mismatch
+misssing||missing
+miximum||maximum
+mmnemonic||mnemonic
+mnay||many
+modfiy||modify
+modifer||modifier
+modulues||modules
+momery||memory
+memomry||memory
+monitring||monitoring
+monochorome||monochrome
+monochromo||monochrome
+monocrome||monochrome
+mopdule||module
+mroe||more
+multipler||multiplier
+mulitplied||multiplied
+multidimensionnal||multidimensional
+multipe||multiple
+multple||multiple
+mumber||number
+muticast||multicast
+mutilcast||multicast
+mutiple||multiple
+mutli||multi
+nams||names
+navagating||navigating
+nead||need
+neccecary||necessary
+neccesary||necessary
+neccessary||necessary
+necesary||necessary
+neded||needed
+negaive||negative
+negoitation||negotiation
+negotation||negotiation
+nerver||never
+nescessary||necessary
+nessessary||necessary
+noticable||noticeable
+notication||notification
+notications||notifications
+notifcations||notifications
+notifed||notified
+notity||notify
+nubmer||number
+numebr||number
+numner||number
+obtaion||obtain
+obusing||abusing
+occassionally||occasionally
+occationally||occasionally
+occurance||occurrence
+occurances||occurrences
+occurd||occurred
+occured||occurred
+occurence||occurrence
+occure||occurred
+occuring||occurring
+offser||offset
+offet||offset
+offlaod||offload
+offloded||offloaded
+offseting||offsetting
+omited||omitted
+omiting||omitting
+omitt||omit
+ommiting||omitting
+ommitted||omitted
+onself||oneself
+ony||only
+openning||opening
+operatione||operation
+opertaions||operations
+opportunies||opportunities
+optionnal||optional
+optmizations||optimizations
+orientatied||orientated
+orientied||oriented
+orignal||original
+originial||original
+otherise||otherwise
+ouput||output
+oustanding||outstanding
+overaall||overall
+overhread||overhead
+overlaping||overlapping
+overflw||overflow
+overlfow||overflow
+overide||override
+overrided||overridden
+overriden||overridden
+overrrun||overrun
+overun||overrun
+overwritting||overwriting
+overwriten||overwritten
+pacakge||package
+pachage||package
+packacge||package
+packege||package
+packge||package
+packtes||packets
+pakage||package
+paket||packet
+pallette||palette
+paln||plan
+paramameters||parameters
+paramaters||parameters
+paramater||parameter
+parametes||parameters
+parametised||parametrised
+paramter||parameter
+paramters||parameters
+parmaters||parameters
+particuarly||particularly
+particularily||particularly
+partion||partition
+partions||partitions
+partiton||partition
+pased||passed
+passin||passing
+pathes||paths
+pattrns||patterns
+pecularities||peculiarities
+peformance||performance
+peforming||performing
+peice||piece
+pendantic||pedantic
+peprocessor||preprocessor
+perfomance||performance
+perfoming||performing
+perfomring||performing
+periperal||peripheral
+peripherial||peripheral
+permissons||permissions
+peroid||period
+persistance||persistence
+persistant||persistent
+phoneticly||phonetically
+plalform||platform
+platfoem||platform
+platfrom||platform
+plattform||platform
+pleaes||please
+ploting||plotting
+plugable||pluggable
+poinnter||pointer
+pointeur||pointer
+poiter||pointer
+posible||possible
+positon||position
+possibilites||possibilities
+potocol||protocol
+powerfull||powerful
+pramater||parameter
+preamle||preamble
+preample||preamble
+preapre||prepare
+preceeded||preceded
+preceeding||preceding
+preceed||precede
+precendence||precedence
+precission||precision
+preemptable||preemptible
+prefered||preferred
+prefferably||preferably
+prefitler||prefilter
+preform||perform
+premption||preemption
+prepaired||prepared
+prepate||prepare
+preperation||preparation
+preprare||prepare
+pressre||pressure
+presuambly||presumably
+previosuly||previously
+primative||primitive
+princliple||principle
+priorty||priority
+privilaged||privileged
+privilage||privilege
+priviledge||privilege
+priviledges||privileges
+privleges||privileges
+probaly||probably
+procceed||proceed
+proccesors||processors
+procesed||processed
+proces||process
+procesing||processing
+processessing||processing
+processess||processes
+processpr||processor
+processsed||processed
+processsing||processing
+procteted||protected
+prodecure||procedure
+progamming||programming
+progams||programs
+progess||progress
+programable||programmable
+programers||programmers
+programm||program
+programms||programs
+progresss||progress
+prohibitted||prohibited
+prohibitting||prohibiting
+promiscous||promiscuous
+promps||prompts
+pronnounced||pronounced
+prononciation||pronunciation
+pronouce||pronounce
+pronunce||pronounce
+propery||property
+propigate||propagate
+propigation||propagation
+propogation||propagation
+propogate||propagate
+prosess||process
+protable||portable
+protcol||protocol
+protecion||protection
+protedcted||protected
+protocoll||protocol
+promixity||proximity
+psudo||pseudo
+psuedo||pseudo
+psychadelic||psychedelic
+purgable||purgeable
+pwoer||power
+queing||queuing
+quering||querying
+queus||queues
+randomally||randomly
+raoming||roaming
+reasearcher||researcher
+reasearchers||researchers
+reasearch||research
+receieve||receive
+recepient||recipient
+recevied||received
+receving||receiving
+recievd||received
+recieved||received
+recieve||receive
+reciever||receiver
+recieves||receives
+recieving||receiving
+recogniced||recognised
+recognizeable||recognizable
+recommanded||recommended
+recyle||recycle
+redircet||redirect
+redirectrion||redirection
+redundacy||redundancy
+reename||rename
+refcounf||refcount
+refence||reference
+refered||referred
+referenace||reference
+refering||referring
+refernces||references
+refernnce||reference
+refrence||reference
+registed||registered
+registerd||registered
+registeration||registration
+registeresd||registered
+registerred||registered
+registes||registers
+registraration||registration
+regsiter||register
+regster||register
+regualar||regular
+reguator||regulator
+regulamentations||regulations
+reigstration||registration
+releated||related
+relevent||relevant
+reloade||reload
+remoote||remote
+remore||remote
+removeable||removable
+repectively||respectively
+replacable||replaceable
+replacments||replacements
+replys||replies
+reponse||response
+representaion||representation
+reqeust||request
+reqister||register
+requed||requeued
+requestied||requested
+requiere||require
+requirment||requirement
+requred||required
+requried||required
+requst||request
+requsted||requested
+reregisteration||reregistration
+reseting||resetting
+reseved||reserved
+reseverd||reserved
+resizeable||resizable
+resouce||resource
+resouces||resources
+resoures||resources
+responce||response
+resrouce||resource
+ressizes||resizes
+ressource||resource
+ressources||resources
+restesting||retesting
+resumbmitting||resubmitting
+retransmited||retransmitted
+retreived||retrieved
+retreive||retrieve
+retreiving||retrieving
+retrive||retrieve
+retrived||retrieved
+retrun||return
+retun||return
+retuned||returned
+reudce||reduce
+reuest||request
+reuqest||request
+reutnred||returned
+revsion||revision
+rmeoved||removed
+rmeove||remove
+rmeoves||removes
+rountine||routine
+routins||routines
+rquest||request
+runing||running
+runned||ran
+runnning||running
+runtine||runtime
+sacrifying||sacrificing
+safly||safely
+safty||safety
+savable||saveable
+scaleing||scaling
+scaned||scanned
+scaning||scanning
+scarch||search
+schdule||schedule
+seach||search
+searchs||searches
+secion||section
+secquence||sequence
+secund||second
+segement||segment
+seleted||selected
+semaphone||semaphore
+senario||scenario
+senarios||scenarios
+sentivite||sensitive
+separatly||separately
+sepcify||specify
+seperated||separated
+seperately||separately
+seperate||separate
+seperatly||separately
+seperator||separator
+sepperate||separate
+seqeunce||sequence
+seqeuncer||sequencer
+seqeuencer||sequencer
+sequece||sequence
+sequemce||sequence
+sequencial||sequential
+serivce||service
+serveral||several
+servive||service
+setts||sets
+settting||setting
+shapshot||snapshot
+shotdown||shutdown
+shoud||should
+shouldnt||shouldn't
+shoule||should
+shrinked||shrunk
+siginificantly||significantly
+signabl||signal
+significanly||significantly
+similary||similarly
+similiar||similar
+simlar||similar
+simliar||similar
+simpified||simplified
+singaled||signaled
+singal||signal
+singed||signed
+sleeped||slept
+sliped||slipped
+softwade||software
+softwares||software
+soley||solely
+souce||source
+speach||speech
+specfic||specific
+specfield||specified
+speciefied||specified
+specifc||specific
+specifed||specified
+specificatin||specification
+specificaton||specification
+specificed||specified
+specifing||specifying
+specifiy||specify
+specifiying||specifying
+speficied||specified
+speicify||specify
+speling||spelling
+spinlcok||spinlock
+spinock||spinlock
+splitted||split
+spreaded||spread
+spurrious||spurious
+sructure||structure
+stablilization||stabilization
+staically||statically
+staion||station
+standardss||standards
+standartization||standardization
+standart||standard
+standy||standby
+stardard||standard
+staticly||statically
+statuss||status
+stoped||stopped
+stoping||stopping
+stoppped||stopped
+straming||streaming
+struc||struct
+structres||structures
+stuct||struct
+strucuture||structure
+stucture||structure
+sturcture||structure
+subdirectoires||subdirectories
+suble||subtle
+substract||subtract
+submited||submitted
+submition||submission
+succeded||succeeded
+suceed||succeed
+succesfully||successfully
+succesful||successful
+successed||succeeded
+successfull||successful
+successfuly||successfully
+sucessfully||successfully
+sucessful||successful
+sucess||success
+superflous||superfluous
+superseeded||superseded
+suplied||supplied
+suported||supported
+suport||support
+supportet||supported
+suppored||supported
+supportin||supporting
+suppoted||supported
+suppported||supported
+suppport||support
+supprot||support
+supress||suppress
+surpressed||suppressed
+surpresses||suppresses
+susbsystem||subsystem
+suspeneded||suspended
+suspsend||suspend
+suspicously||suspiciously
+swaping||swapping
+switchs||switches
+swith||switch
+swithable||switchable
+swithc||switch
+swithced||switched
+swithcing||switching
+swithed||switched
+swithing||switching
+swtich||switch
+syfs||sysfs
+symetric||symmetric
+synax||syntax
+synchonized||synchronized
+synchronuously||synchronously
+syncronize||synchronize
+syncronized||synchronized
+syncronizing||synchronizing
+syncronus||synchronous
+syste||system
+sytem||system
+sythesis||synthesis
+taht||that
+tansmit||transmit
+targetted||targeted
+targetting||targeting
+taskelt||tasklet
+teh||the
+temorary||temporary
+temproarily||temporarily
+temperture||temperature
+thead||thread
+therfore||therefore
+thier||their
+threds||threads
+threee||three
+threshhold||threshold
+thresold||threshold
+throught||through
+trackling||tracking
+troughput||throughput
+trys||tries
+thses||these
+tiggers||triggers
+tiggered||triggered
+tipically||typically
+timeing||timing
+timout||timeout
+tmis||this
+toogle||toggle
+torerable||tolerable
+traget||target
+traking||tracking
+tramsmitted||transmitted
+tramsmit||transmit
+tranasction||transaction
+tranceiver||transceiver
+tranfer||transfer
+tranmission||transmission
+transcevier||transceiver
+transciever||transceiver
+transferd||transferred
+transfered||transferred
+transfering||transferring
+transision||transition
+transmittd||transmitted
+transormed||transformed
+trasfer||transfer
+trasmission||transmission
+treshold||threshold
+triggerd||triggered
+trigerred||triggered
+trigerring||triggering
+trun||turn
+tunning||tuning
+ture||true
+tyep||type
+udpate||update
+uesd||used
+uknown||unknown
+usccess||success
+uncommited||uncommitted
+uncompatible||incompatible
+unconditionaly||unconditionally
+undeflow||underflow
+underun||underrun
+unecessary||unnecessary
+unexecpted||unexpected
+unexepected||unexpected
+unexpcted||unexpected
+unexpectd||unexpected
+unexpeted||unexpected
+unexpexted||unexpected
+unfortunatelly||unfortunately
+unifiy||unify
+uniterrupted||uninterrupted
+unintialized||uninitialized
+unitialized||uninitialized
+unkmown||unknown
+unknonw||unknown
+unknouwn||unknown
+unknow||unknown
+unkown||unknown
+unamed||unnamed
+uneeded||unneeded
+unneded||unneeded
+unneccecary||unnecessary
+unneccesary||unnecessary
+unneccessary||unnecessary
+unnecesary||unnecessary
+unneedingly||unnecessarily
+unnsupported||unsupported
+unmached||unmatched
+unprecise||imprecise
+unregester||unregister
+unresgister||unregister
+unrgesiter||unregister
+unsinged||unsigned
+unstabel||unstable
+unsolicitied||unsolicited
+unsuccessfull||unsuccessful
+unsuported||unsupported
+untill||until
+ununsed||unused
+unuseful||useless
+unvalid||invalid
+upate||update
+upsupported||unsupported
+usefule||useful
+usefull||useful
+usege||usage
+usera||users
+usualy||usually
+usupported||unsupported
+utilites||utilities
+utillities||utilities
+utilties||utilities
+utiltity||utility
+utitity||utility
+utitlty||utility
+vaid||valid
+vaild||valid
+valide||valid
+variantions||variations
+varible||variable
+varient||variant
+vaule||value
+verbse||verbose
+veify||verify
+veriosn||version
+verisons||versions
+verison||version
+verson||version
+vicefersa||vice-versa
+virtal||virtual
+virtaul||virtual
+virtiual||virtual
+visiters||visitors
+vitual||virtual
+vunerable||vulnerable
+wakeus||wakeups
+wathdog||watchdog
+wating||waiting
+wiat||wait
+wether||whether
+whataver||whatever
+whcih||which
+whenver||whenever
+wheter||whether
+whe||when
+wierd||weird
+wiil||will
+wirte||write
+withing||within
+wnat||want
+wont||won't
+workarould||workaround
+writeing||writing
+writting||writing
+wtih||with
+zombe||zombie
+zomebie||zombie
diff --git a/tools/checkspdx/CheckSPDX.cmake b/tools/checkspdx/CheckSPDX.cmake
new file mode 100644
index 0000000..7a4acff
--- /dev/null
+++ b/tools/checkspdx/CheckSPDX.cmake
@@ -0,0 +1,149 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+#
+# This script is called from main CMakeLists.txt to determine if SPDX headers
+# are in proper format
+#
+find_package(Git REQUIRED)
+find_package(Python3 REQUIRED)
+find_program(CHECKSPDX_EXECUTABLE "checkspdx.py"
+  PATHS ${CMAKE_SOURCE_DIR}
+  PATH_SUFFIXES tools/checkspdx
+  DOC "Path to checkspdx.py"
+  )
+
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/tools/common")
+include(GitUtils)
+
+# List of directories and files to exclude from checking for target
+list(APPEND glob_excludes "^.git")
+list(APPEND glob_excludes "^out")
+list(APPEND glob_excludes "^build")
+list(APPEND glob_excludes "^ext")
+list(APPEND glob_excludes "^tools")
+list(APPEND glob_excludes ".patch$")
+list(APPEND glob_excludes ".md$")
+list(APPEND glob_excludes "~$")
+list(APPEND glob_excludes ".swp$")
+list(APPEND glob_excludes "^cscope.")
+list(APPEND glob_excludes ".png$")
+list(APPEND glob_excludes "LICENSE")
+list(APPEND glob_excludes "DCO")
+list(APPEND glob_excludes "docs/global_substitutions.txt")
+
+# checkspdx_get_stats: Parse and returns number of errors and warnings
+function(checkspdx_get_stats stats_arg errors_ret)
+  string(FIND "${stats_arg}" "total:" idx REVERSE)
+  if(NOT ${idx} EQUAL -1)
+    string(LENGTH "${stats_arg}" len)
+    string(SUBSTRING "${stats_arg}" ${idx} ${len} last_line)
+
+    string(REPLACE " " ";" last_line_list ${last_line})
+    list(GET last_line_list 1 errors)
+  else()
+    set(errors 1)
+  endif()
+
+  set(${errors_ret} ${errors} PARENT_SCOPE)
+endfunction()
+
+#
+# print_stats_and_exit: Print summary of all errors and warnings.
+# If there are errors call message(FATAL_ERROR)
+#
+function(print_stats_and_exit check_type total_errors)
+  message(STATUS "${check_type}: total errors: ${total_errors}")
+
+  if(${total_errors} GREATER 0)
+    message(FATAL_ERROR "${check_type}: FAILED")
+  endif()
+
+  message(STATUS "${check_type}: PASSED")
+endfunction()
+
+# Run checkspdx.py on the list of files.
+function(run_checkspdx source_files errors_ret)
+  set(errors 0)
+
+  string(REPLACE ";" " " source_files "${source_files}")
+  separate_arguments(source_files NATIVE_COMMAND "${source_files}")
+
+  execute_process(
+    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+    COMMAND ${CHECKSPDX_EXECUTABLE} -r docs/readme.rst ${source_files}
+    OUTPUT_VARIABLE checkspdx_output
+    RESULT_VARIABLE checkspdx_rc
+    ECHO_OUTPUT_VARIABLE
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+    )
+
+  # checkspdx failed for this file. Collect no.of errors
+  if(${checkspdx_rc} GREATER 0)
+    checkspdx_get_stats("${checkspdx_output}" errors)
+  endif()
+
+  set(${errors_ret} ${errors} PARENT_SCOPE)
+endfunction()
+
+#
+# Run checkspdx on entire codebase. This verifies all files in this repository
+# except the files listed in "glob_excludes".
+#
+# Exits with FATAL_ERROR upon errors. Warnings are ignored (temporary)
+#
+if(CHECKSPDX_CODEBASE)
+  set(source_files "")
+
+  if (GIT_FOUND AND IS_DIRECTORY .git)
+    Git_Get_All_Files(source_files)
+  else()
+    file(GLOB_RECURSE source_files RELATIVE ${CMAKE_SOURCE_DIR} "*")
+  endif()
+
+  # Filter out 'glob_excludes'
+  foreach(exclude IN LISTS glob_excludes)
+    list(FILTER source_files EXCLUDE REGEX "${exclude}")
+  endforeach()
+
+  if(NOT source_files)
+    message(STATUS "check-spdx-codebase: No files to check")
+    return()
+  endif()
+
+  run_checkspdx("${source_files}" total_errors)
+  print_stats_and_exit("checkspdx-codebase" ${total_errors})
+endif()
+
+#
+# Check SPDX complaiance on pending commits.
+#
+# Exits with FATAL_ERROR upon errors.
+#
+if(CHECKSPDX_PATCH)
+  if(GIT_NOT_FOUND OR NOT IS_DIRECTORY .git)
+    message(FATAL_ERROR "Required dependencies Git not found")
+  endif()
+
+  # Get list of commits to check
+  Git_Get_Pending_Commits(pending_commits)
+
+  # Iterate throuth list of commit ids
+  set(total_errors 0)
+  foreach(commit IN LISTS pending_commits)
+    message(STATUS "Checking commit: ${commit}")
+
+    Git_Get_Files_In_Commit("${commit}" source_files)
+
+    foreach(exclude IN LISTS glob_excludes)
+      list(FILTER source_files EXCLUDE REGEX "${exclude}")
+    endforeach()
+
+    run_checkspdx("${source_files}" errors)
+    MATH(EXPR total_errors "${total_errors}+${errors}")
+  endforeach()
+
+  print_stats_and_exit("checkspdx-patch" ${total_errors})
+endif()
diff --git a/tools/checkspdx/checkspdx.py b/tools/checkspdx/checkspdx.py
new file mode 100755
index 0000000..4c98675
--- /dev/null
+++ b/tools/checkspdx/checkspdx.py
@@ -0,0 +1,221 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+from argparse import ArgumentParser
+import locale
+import traceback
+import sys
+import re
+import os
+from os import access, R_OK
+from os.path import isfile
+
+SPDX_LICENSE_TAG = 'SPDX-License-Identifier:'
+SPDX_COPYRIGHT_TAG = 'SPDX-FileCopyrightText:'
+SPDX_DEFAULT_LICENSE = 'BSD-3-Clause'
+OTHER_PROJECTS_FILES = []
+PREFERRED_SPDX_LICENSE_LINE_NUMBER = 2
+RST_PREFERRED_SPDX_LICENSE_LINE_NUMBER = 1
+
+COPYRIGHT_KEYWORD = 'Copyright'
+LICENSE_TAG_PATTERN = r'^(#|\*|//|..|/\*| \*)[ \t*#/]* ' + SPDX_LICENSE_TAG + r'[\w\W]*'
+COPYRIGHT_TAG_PATTERN=r'^(#|\*|//|..|/\*| \*)[ \t*#/]* ' + SPDX_COPYRIGHT_TAG + r'[\w\W]*'
+
+THIRD_PARTY_FILE_TABLE = '.. list-table:: \*\*List of files with different license\*\*'
+RST_TABLE_COL1_PATTERN = r'^( |\t)*\* - [\w\W]*'
+RST_TABLE_COL2_PATTERN = r'^( |\t)*- [\w\W]*'
+
+# check if 'file' is a regular file and it is readable
+def file_readable(file):
+    if not isfile(file):
+        print(file + ": WARNING: File not found")
+        return 0
+
+    if not access(file, R_OK):
+        print(file + ": WARNING: File not readable")
+        return 0
+
+    return 1
+
+# exit program with rc
+def print_error_and_exit(total_errors):
+    if total_errors:
+        print("total: " + str(total_errors) + " errors")
+        sys.exit(1)
+    else:
+        sys.exit(0)
+
+# Get other project file and its license name
+def get_other_projects_files(rst_file):
+    if not file_readable(rst_file):
+        return ""
+
+    empty_line = 0
+    col_num = 1
+    add_row = 0
+
+    with open(rst_file, encoding='utf-8') as fh:
+        for line in fh:
+            if re.search(r'^' + THIRD_PARTY_FILE_TABLE + r'$', line):
+                # Parse the rst table
+                for line in fh:
+                    line = line.rstrip()
+                    # second empty line denotes end of table
+                    if empty_line > 1:
+                        break
+
+                    # ignore first empty line
+                    if not line:
+                        empty_line += 1
+                        continue
+
+                    if col_num == 1 and re.search(RST_TABLE_COL1_PATTERN, line):
+                        col1 = line.split('-',1)[1].strip()
+                        col_num = 2
+                    elif col_num == 2 and re.search(RST_TABLE_COL2_PATTERN,
+                                                    line):
+                        col2 = line.split('-',1)[1].strip()
+                        col_num = 1
+                        add_row = 1
+                    else:
+                        print(rst_file + ": WARNING: Invalid list-table " +
+                              "format in line \"" + line + "\"")
+                        break
+
+                    if add_row:
+                        OTHER_PROJECTS_FILES.append(col1 + "%" + col2)
+                        add_row = 0
+
+                # after parsing the table break
+                break
+
+    return
+
+def license_in_other_project(file, license):
+    search_str = file + "%" + license
+    if search_str in OTHER_PROJECTS_FILES:
+        return 1
+    else:
+        return 0
+
+# Check "SPDX-License-Identifier" tag is at required line number
+# Check if "SPDX-License-Identifier" has a valid license
+def verify_spdx_license_tag(file, line, line_number):
+    errors = 0
+
+    if re.search(r'\.rst$', file):
+        preferred_line_no = RST_PREFERRED_SPDX_LICENSE_LINE_NUMBER
+    else:
+        preferred_line_no = PREFERRED_SPDX_LICENSE_LINE_NUMBER
+
+    if line_number != preferred_line_no:
+        print(file + ": ERROR: \"" + SPDX_LICENSE_TAG + "\" is at line:" +
+              str(line_number) + " preferred line number is " +
+              str(preferred_line_no))
+        errors += 1
+
+    license_string = line.split(SPDX_LICENSE_TAG)[1].strip()
+    if license_string:
+        license = license_string.split()[0]
+        if (license != SPDX_DEFAULT_LICENSE and
+            not license_in_other_project(file, license)):
+            print(file + ": ERROR: Invalid license \"" + license +
+                  "\" at line: " + str(line_number))
+            errors += 1
+    else:
+        print(file + ": ERROR: License name not found at line: " +
+              str(line_number))
+        errors += 1
+
+    return errors
+
+# Check if "SPDX-FileCopyrightText:" starts with COPYRIGHT_KEYWORD
+def verify_spdx_copyright_tag(file, line, line_number):
+    errors = 0
+
+    cpr_string = line.split(SPDX_COPYRIGHT_TAG)[1].strip()
+    if not cpr_string or COPYRIGHT_KEYWORD != cpr_string.split()[0]:
+        print(file + ": ERROR: Copyright text doesn't starts with \"" +
+              COPYRIGHT_KEYWORD + " \" keyword at line: " + str(line_number))
+        errors += 1
+
+    return errors
+
+#
+# Check for tags: "SPDX-License-Identifier", "SPDX-FileCopyrightText"
+#
+# Check if "SPDX-FileCopyrightText" is present.
+# This tag must appear be after "SPDX-License-Identifier" tag
+#
+def verify_spdx_headers(file):
+    print("Checking file: " + file)
+    if not file_readable(file):
+        return 0
+
+    lic_tag_found = 0
+    cpr_tag_found = 0
+    errors = 0
+
+    # read first 25 lines
+    with open(file, encoding='utf-8') as fh:
+        for l in range(1, 25):
+            line = fh.readline()
+            if not line: # EOF
+                break
+            line = line.rstrip()
+            if not line: # empty line
+                continue
+
+            if re.search(LICENSE_TAG_PATTERN, line):
+                if lic_tag_found >= 1:
+                    print(file + ": ERROR: Duplicate \"" + SPDX_LICENSE_TAG +
+                          "\" tag at line: " + str(l))
+                    errors += 1
+                else:
+                    errors += verify_spdx_license_tag(file, line, l)
+                lic_tag_found += 1
+                continue
+
+            if re.search(COPYRIGHT_TAG_PATTERN, line):
+                if not lic_tag_found:
+                    print(file + ": ERROR: \"" + SPDX_COPYRIGHT_TAG +
+                          "\" at line: " + str(l) + " must come after \""
+                          + SPDX_LICENSE_TAG + "\"")
+                    errors += 1
+                errors += verify_spdx_copyright_tag(file, line, l)
+                cpr_tag_found += 1
+                continue
+
+    if not lic_tag_found:
+        print(file + ": ERROR: Missing \"" + SPDX_LICENSE_TAG + "\" tag")
+        errors += 1
+
+    if not cpr_tag_found:
+        print(file + ": ERROR: Missing \"" + SPDX_COPYRIGHT_TAG + "\" tag")
+        errors += 1
+
+    if errors:
+        print(file + ": " + str(errors) + " errors found")
+
+    return errors
+
+# main
+if __name__ == '__main__':
+    ap = ArgumentParser(description='Check SPDX headers')
+    ap.add_argument('files', nargs='*', help='Check files.')
+    ap.add_argument('-r', '--readme-rst', type=str,
+                    help='path to readme.rst file', required=True)
+    args = ap.parse_args()
+
+    total_errors = 0
+    readme_file = args.readme_rst
+
+    # Parse readme file and get the list of files that have non-default license
+    get_other_projects_files(readme_file)
+
+    for file in args.files:
+        total_errors += verify_spdx_headers(file)
+
+    print_error_and_exit(total_errors)
diff --git a/tools/common/GitUtils.cmake b/tools/common/GitUtils.cmake
new file mode 100644
index 0000000..22b7fca
--- /dev/null
+++ b/tools/common/GitUtils.cmake
@@ -0,0 +1,125 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+#
+# Returns:
+# @FileList_Out:        All files in the Git repo in list format. Empty list
+#                       on error
+#
+function(Git_Get_All_Files FileList_Out)
+  if (GIT_NOT_FOUND OR NOT IS_DIRECTORY .git)
+    set(${FileList_Out} "" PARENT_SCOPE)
+    return()
+  endif()
+
+  execute_process(
+    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+    COMMAND ${GIT_EXECUTABLE} ls-files
+    OUTPUT_VARIABLE git_ls_files
+    RESULT_VARIABLE git_rc
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+    )
+
+  # convert string to list
+  if(NOT "${git_ls_files}" STREQUAL "")
+    string(REPLACE "\n" ";" all_files ${git_ls_files})
+  else()
+    set(all_files "")
+  endif()
+
+  set(${FileList_Out} ${all_files} PARENT_SCOPE)
+endfunction()
+
+#
+# Returns:
+# @CommitIdList_Out:    All commit ids in current branch between HEAD and
+#                       upstream tracking branch in List format. Empty list
+#                       on error
+#
+function(Git_Get_Pending_Commits CommitIdList_Out)
+  if (GIT_NOT_FOUND OR NOT IS_DIRECTORY .git)
+    set(${CommitIdList_Out} "" PARENT_SCOPE)
+    return()
+  endif()
+
+  # Get the upstream branch the current (local) branch is tracking
+  execute_process(
+    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+    COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref --symbolic-full-name @{u}
+    OUTPUT_VARIABLE git_upstream_branch
+    RESULT_VARIABLE git_rc
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+    )
+
+  if ("${git_upstream_branch}" STREQUAL "")
+    message(STATUS "Warning: Upstream branch not set. Trying \"origin/main\"")
+    set(git_upstream_branch "origin/main")
+  endif()
+
+  # Get the merge base
+  execute_process(
+    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+    COMMAND ${GIT_EXECUTABLE} merge-base HEAD ${git_upstream_branch}
+    OUTPUT_VARIABLE git_merge_base
+    RESULT_VARIABLE git_rc
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+    )
+
+  if("${git_merge_base}" STREQUAL "")
+    set(${CommitIdList_Out} "" PARENT_SCOPE)
+    return()
+  endif()
+
+  # Get list of commits between $merge_base and HEAD
+  execute_process(
+    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+    COMMAND ${GIT_EXECUTABLE} rev-list --no-merges "${git_merge_base}..HEAD"
+    OUTPUT_VARIABLE git_rev_output
+    RESULT_VARIABLE git_rc
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+    )
+
+  # convert to list
+  if(NOT "${git_rev_output}" STREQUAL "")
+    string(REPLACE "\n" ";" git_rev_list ${git_rev_output})
+  else()
+    set(git_rev_list "")
+  endif()
+
+  set(${CommitIdList_Out} ${git_rev_list} PARENT_SCOPE)
+endfunction()
+
+#
+# Args In:
+# @CommitId_In:         Commit's SHA
+#
+# Returns:
+# @FileList_Out:        Files Added or Modified or Deleted by the @CommitId_In
+#                       in list format. Empty list on error
+#
+function(Git_Get_Files_In_Commit CommitId_In FileList_Out)
+  if (GIT_NOT_FOUND OR NOT IS_DIRECTORY .git OR "${CommitId_In}" STREQUAL "")
+    set(${FileList_Out} "" PARENT_SCOPE)
+    return()
+  endif()
+
+  execute_process(
+    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+    # Get list of files that are Added or Renamed or Modified by this commit
+    COMMAND ${GIT_EXECUTABLE} show --diff-filter=ARM --pretty=format: --name-only ${CommitId_In}
+    OUTPUT_VARIABLE git_files
+    RESULT_VARIABLE git_rc
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+    )
+
+  # convert string to list
+  if(NOT "${git_files}" STREQUAL "")
+    string(REPLACE "\n" ";" source_files ${git_files})
+  else()
+    set(source_files "")
+  endif()
+
+  set(${FileList_Out} ${source_files} PARENT_SCOPE)
+endfunction()
diff --git a/tools/cppcheck/CMakeLists.txt b/tools/cppcheck/CMakeLists.txt
new file mode 100644
index 0000000..dd79962
--- /dev/null
+++ b/tools/cppcheck/CMakeLists.txt
@@ -0,0 +1,158 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+if(RMM_STATIC_ANALYSIS)
+    find_program(RMM_STATIC_ANALYSIS_CPPCHECK_PATH "cppcheck"
+        DOC "Path to Cppcheck.")
+endif()
+
+if(EXISTS "${RMM_STATIC_ANALYSIS}")
+    mark_as_advanced(FORCE RMM_STATIC_ANALYSIS_CPPCHECK_PATH)
+else()
+    mark_as_advanced(CLEAR RMM_STATIC_ANALYSIS_CPPCHECK_PATH)
+endif()
+
+arm_config_option(
+    NAME RMM_STATIC_ANALYSIS_CPPCHECK
+    HELP "Enable Cppcheck static analysis."
+    DEFAULT TRUE
+    DEPENDS (RMM_STATIC_ANALYSIS) AND
+            (EXISTS "${RMM_STATIC_ANALYSIS_CPPCHECK_PATH}")
+    ELSE FALSE)
+
+arm_config_option(
+    NAME RMM_STATIC_ANALYSIS_CPPCHECK_FLAGS
+    HELP "Cppcheck command line options."
+    TYPE STRING
+    DEFAULT ""
+    DEPENDS RMM_STATIC_ANALYSIS_CPPCHECK
+    ADVANCED)
+
+arm_config_option(
+    NAME RMM_STATIC_ANALYSIS_CPPCHECK_CHECKER_CERT_C
+    HELP "Enable Cppcheck's SEI CERT C checker."
+    DEFAULT TRUE
+    DEPENDS RMM_STATIC_ANALYSIS_CPPCHECK
+    ELSE FALSE)
+
+arm_config_option(
+    NAME RMM_STATIC_ANALYSIS_CPPCHECK_CHECKER_MISRA
+    HELP "Enable Cppcheck's MISRA C:2012 checker."
+    DEFAULT TRUE
+    DEPENDS RMM_STATIC_ANALYSIS_CPPCHECK
+    ELSE FALSE)
+
+arm_config_option(
+    NAME RMM_STATIC_ANALYSIS_CPPCHECK_CHECKER_THREAD_SAFETY
+    HELP "Enable Cppcheck's thread safety checker."
+    DEFAULT TRUE
+    DEPENDS RMM_STATIC_ANALYSIS_CPPCHECK
+    ELSE FALSE)
+
+if(RMM_STATIC_ANALYSIS_CPPCHECK)
+    #
+    # Set up checkers.
+    #
+
+    set(cppcheck-flags)
+
+    list(APPEND cppcheck-flags "--enable=all")
+    list(APPEND cppcheck-flags "--xml")
+    list(APPEND cppcheck-flags "--xml-version=2")
+    list(APPEND cppcheck-flags "--output-file=${CMAKE_CURRENT_BINARY_DIR}/cppcheck.xml")
+
+    if(RMM_STATIC_ANALYSIS_CPPCHECK_CHECKER_CERT_C)
+        list(APPEND cppcheck-flags "--addon=cert")
+    endif()
+
+    if(RMM_STATIC_ANALYSIS_CPPCHECK_CHECKER_MISRA)
+	list(APPEND cppcheck-flags "--addon=${CMAKE_CURRENT_SOURCE_DIR}/misra.json")
+    endif()
+
+    if(RMM_STATIC_ANALYSIS_CPPCHECK_CHECKER_THREAD_SAFETY)
+        list(APPEND cppcheck-flags "--addon=threadsafety")
+    endif()
+
+    #
+    # Pass CHAR_BIT to Mbed TLS to supress error:
+    # "mbed TLS requires a platform with 8-bit chars"
+    #
+
+    list(APPEND cppcheck-flags "-DCHAR_BIT=8")
+
+    #
+    # Suppress files or directories we don't want to receive warnings about. If
+    # you want to suppress specific files without using an inline suppression,
+    # do it in `suppressions.txt`.
+    #
+
+    list(APPEND cppcheck-flags
+        "--inline-suppr" # Allow inline suppressions
+        "--suppressions-list=${CMAKE_CURRENT_SOURCE_DIR}/suppressions.txt")
+
+    #
+    # Determine implicit C compiler definitions by pulling them from the
+    # compiler and dumping them into a header file. This is for those situations
+    # where we're relying on compiler implementation details, but Cppcheck
+    # doesn't expose them.
+    #
+
+    file(TOUCH "${CMAKE_CURRENT_BINARY_DIR}/null.txt")
+
+    separate_arguments(cflags NATIVE_COMMAND "${CMAKE_C_FLAGS}")
+
+    execute_process(
+        COMMAND ${CMAKE_C_COMPILER} ${cflags} -dM -E -
+        INPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/null.txt"
+        OUTPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/implicit-defines.h")
+
+    list(APPEND cppcheck-flags
+        "--include=${CMAKE_CURRENT_BINARY_DIR}/implicit-defines.h"
+        "--suppress=*:${CMAKE_CURRENT_BINARY_DIR}/implicit-defines.h")
+
+    #
+    # Traditionally we would let Cppcheck use its own standard library headers,
+    # but it appears to be lacking some critical symbols like `CHAR_BIT` from
+    # `<limits.h>`. Luckily, CMake makes this relatively easy for us to do. We
+    # don't analyze these headers, we just make them available for inclusion.
+    #
+
+    foreach(include IN LISTS CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES)
+        list(APPEND cppcheck-flags "-I${include}")
+        list(APPEND cppcheck-flags "--suppress=*:${include}/*")
+    endforeach()
+
+    #
+    # Configure the platform file. This is done based on the current compiler,
+    # if possible, so that we can communicate certain implementation details to
+    # Cppcheck and avoid false positives.
+    #
+
+    set(platform-xml
+        "${CMAKE_CURRENT_SOURCE_DIR}/compilers/${CMAKE_C_COMPILER_ID}.xml")
+
+    if(EXISTS "${platform-xml}")
+        list(APPEND cppcheck-flags "--platform=${platform-xml}")
+    else()
+        message(WARNING
+            "No Cppcheck platform file is available for this compiler. Static "
+            "analysis results may be inaccurate.")
+    endif()
+
+    separate_arguments(cppcheck-flags-user
+        NATIVE_COMMAND "${RMM_STATIC_ANALYSIS_CPPCHECK_FLAGS}")
+
+
+    set(COMPILE_COMMANDS_FILE "${CMAKE_BINARY_DIR}/compile_commands.json")
+
+    add_custom_target(cppcheck
+      COMMAND ${RMM_STATIC_ANALYSIS_CPPCHECK_PATH}
+              --project=${COMPILE_COMMANDS_FILE} ${cppcheck-flags} ||
+              (test ! -e ${COMPILE_COMMANDS_FILE} &&
+              echo 'please generate with -DRMM_STATIC_ANALYSIS_CPPCHECK=ON')
+    )
+else()
+    unset(CMAKE_C_CPPCHECK CACHE)
+endif()
diff --git a/tools/cppcheck/compilers/GNU.xml b/tools/cppcheck/compilers/GNU.xml
new file mode 100644
index 0000000..9515eb1
--- /dev/null
+++ b/tools/cppcheck/compilers/GNU.xml
@@ -0,0 +1,17 @@
+<?xml version="1"?>
+<platform>
+    <char_bit>8</char_bit>
+    <default-sign>signed</default-sign>
+    <sizeof>
+        <short>2</short>
+        <int>4</int>
+        <long>8</long>
+        <long-long>8</long-long>
+        <float>4</float>
+        <double>8</double>
+        <long-double>16</long-double>
+        <pointer>8</pointer>
+        <size_t>8</size_t>
+        <wchar_t>4</wchar_t>
+    </sizeof>
+</platform>
diff --git a/tools/cppcheck/misra.json b/tools/cppcheck/misra.json
new file mode 100644
index 0000000..2932aa2
--- /dev/null
+++ b/tools/cppcheck/misra.json
@@ -0,0 +1,7 @@
+{
+"script": "misra.py",
+    "args": [
+	"--suppress-rules=1.2,2.3,2.4,2.5,5.7,8.7,8.9,8.13,9.1,11.5,15.1,15.5,16.1,16.3,21.6",
+	"--rule-texts=/usr/local/share/Cppcheck/misra/misra.rules"
+    ]
+}
diff --git a/tools/cppcheck/suppressions.txt b/tools/cppcheck/suppressions.txt
new file mode 100644
index 0000000..549a0eb
--- /dev/null
+++ b/tools/cppcheck/suppressions.txt
@@ -0,0 +1,18 @@
+// Cppcheck includes its own system headers so we do not ask it to search for
+// our own (freestanding headers are provided by the compiler, and anything else
+// is provided by the `rmm-lib-libc` target).
+missingIncludeSystem
+
+// Cppcheck reports a lot of false positives for unused functions, as it cannot
+// analyze the SMC call boundary. Short of manually suppressing half of the
+// code-base, there's not much we can do about this.
+unusedFunction
+
+// Because files are analyzed individually, Cppcheck sometimes complains that
+// the `missingIncludeSystem` suppression was useless. It's right, but it's
+// also not particularly helpful.
+unmatchedSuppression
+
+// We don't want to receive warnings about code we don't control, which includes
+// any external libraries we vendor.
+*:ext/*
diff --git a/tools/requirements.txt b/tools/requirements.txt
new file mode 100644
index 0000000..4449570
--- /dev/null
+++ b/tools/requirements.txt
@@ -0,0 +1,6 @@
+graphviz
+Sphinx>=2.4,<3.0.0
+sphinx-rtd-theme
+sphinxcontrib-plantuml
+docutils==0.16
+jinja2<3.1