Merge changes from topic "el0-app-support" into integration

* changes:
  feat(lib): implement app support for host build
  feat(app/common): initialise console for apps
  feat(app/common/el0_app): add app helper library
  feat(app/common): add app service framework
  feat(app): add framework to run an el0 app
  feat(lib/xlat): get ASID from TTBR1_EL2
  feat(lib/slot_buf): Add new slots
  feat(app): add code to parse app bin headers
  build(app): add python scripts for El0 apps
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0d94f41..c231396 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -107,6 +107,7 @@
 # Recurse into the various component subdirectories
 #
 add_subdirectory("lib")
+add_subdirectory("app")
 add_subdirectory("runtime")
 
 if(RMM_DOCS)
@@ -114,25 +115,33 @@
 endif()
 
 #
-# Copy 'rmm-runtime' executable to 'build/$<CONFIG>/rmm.elf'.
+# Copy 'rmm-runtime' executable to 'build/$<CONFIG>/rmm_core.elf'.
 #
 
 set(ARTEFACT_DIR "${CMAKE_BINARY_DIR}/$<CONFIG>")
 add_custom_command(
     COMMAND ${CMAKE_COMMAND} -E make_directory "${ARTEFACT_DIR}"
-    COMMAND "${CMAKE_COMMAND}" -E copy_if_different "$<TARGET_FILE:rmm-runtime>" "${ARTEFACT_DIR}/rmm.elf"
-    OUTPUT rmm.elf
+    COMMAND "${CMAKE_COMMAND}" -E copy_if_different "$<TARGET_FILE:rmm-runtime>" "${ARTEFACT_DIR}/rmm_core.elf"
+    OUTPUT rmm_core.elf
     DEPENDS rmm-runtime)
 
 #
+# Create rmm.elf as a copy of rmm_core.elf to keep CI working
+#
+add_custom_command(
+    COMMAND "${CMAKE_COMMAND}" -E copy ${ARTEFACT_DIR}/rmm_core.elf ${ARTEFACT_DIR}/rmm.elf
+    OUTPUT rmm.elf
+    DEPENDS rmm_core.elf)
+
+#
 # Create the flat binary using whatever tool comes with the toolchain.
 #
 
 if(CMAKE_OBJCOPY)
     add_custom_command(
-        COMMAND "${CMAKE_OBJCOPY}" -O binary "${ARTEFACT_DIR}/rmm.elf" "${ARTEFACT_DIR}/rmm.img"
-        OUTPUT rmm.img
-        DEPENDS rmm.elf)
+        COMMAND "${CMAKE_OBJCOPY}" -O binary "${ARTEFACT_DIR}/rmm_core.elf" "${ARTEFACT_DIR}/rmm_core.img"
+        OUTPUT rmm_core.img
+        DEPENDS rmm_core.elf)
 endif()
 
 #
@@ -141,21 +150,36 @@
 
 if(CMAKE_OBJDUMP)
     add_custom_command(
-        COMMAND "${CMAKE_OBJDUMP}" -dxS "${ARTEFACT_DIR}/rmm.elf" > "${ARTEFACT_DIR}/rmm.dump"
-        OUTPUT rmm.dump
-        DEPENDS rmm.elf)
+        COMMAND "${CMAKE_OBJDUMP}" -dxS "${ARTEFACT_DIR}/rmm_core.elf" > "${ARTEFACT_DIR}/rmm_core.dump"
+        OUTPUT rmm_core.dump
+        DEPENDS rmm_core.elf)
 endif()
 
 #
-# Copy 'rmm-runtime.map' to 'build/$<CONFIG>/rmm.map'
+# Copy 'rmm-runtime.map' to 'build/$<CONFIG>/rmm_core.map'
 #
 
 add_custom_command(
-    COMMAND "${CMAKE_COMMAND}" -E copy_if_different "$<TARGET_FILE:rmm-runtime>.map" "${ARTEFACT_DIR}/rmm.map"
-    OUTPUT rmm.map
+    COMMAND "${CMAKE_COMMAND}" -E copy_if_different "$<TARGET_FILE:rmm-runtime>.map" "${ARTEFACT_DIR}/rmm_core.map"
+    OUTPUT rmm_core.map
     DEPENDS rmm-runtime)
 
-add_custom_target(rmm ALL DEPENDS rmm.img rmm.dump rmm.elf rmm.map)
+if(NOT RMM_ARCH STREQUAL fake_host)
+    set(RMM_IMG "rmm.img")
+    find_program(BUNDLE_APP_RMM "bundle_app_rmm.py"
+        PATHS ${CMAKE_SOURCE_DIR}
+        PATH_SUFFIXES app
+        DOC "bundle_app_rmm.py")
+
+    set(BUNDLE_COMMAND_OUTPUT "${ARTEFACT_DIR}/bundle_app_out.txt")
+    add_custom_command(
+        # TODO: Call ${BUNDLE_APP_RMM} instead of copy if an app is added.
+        COMMAND "${CMAKE_COMMAND}" -E copy ${ARTEFACT_DIR}/rmm_core.img ${ARTEFACT_DIR}/${RMM_IMG} 2> ${BUNDLE_COMMAND_OUTPUT}
+        OUTPUT ${RMM_IMG}
+        DEPENDS rmm_core.img)
+endif()
+
+add_custom_target(rmm ALL DEPENDS rmm_core.img rmm_core.dump rmm_core.elf rmm.elf rmm_core.map ${RMM_IMG})
 
 #
 # Set up additional tooling.
diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt
new file mode 100644
index 0000000..287878a
--- /dev/null
+++ b/app/CMakeLists.txt
@@ -0,0 +1,19 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+# List of libraries that are included in the app stub library
+set(RMM_EL2_STUB_LIBRARIES "")
+
+add_subdirectory("common")
+
+# List of apps to be packaged to final binary
+# The contents of the list is populated by the CMakeLists in the subdirectories
+set(EL0_APP_BIN_LIST "")
+
+set(EL0_APP_BIN_LIST ${EL0_APP_BIN_LIST} PARENT_SCOPE)
+
+add_library(rmm-el2-stub INTERFACE)
+target_link_libraries(rmm-el2-stub
+    INTERFACE ${RMM_EL2_STUB_LIBRARIES})
diff --git a/app/bundle_app_rmm.py b/app/bundle_app_rmm.py
new file mode 100755
index 0000000..eb1deac
--- /dev/null
+++ b/app/bundle_app_rmm.py
@@ -0,0 +1,150 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+"""
+Script creating a bundle from app binaries and RMM binary
+
+The script prepends the app binaries to the RMM binary, and generates a branch
+instruction at the beginning of the binary file. This way the RMM code can start
+running when the execution reaches the beginning of the bundled binary.
+
+A bundled RMM binary has the following structure:
+```
+    +------------------------------+                          -+
+    |     BL rmm_bin_offset        | Generated by this script  |
+    |                              |                           |
+    | Rest of the header unchanged |                           |
+    |..............................|                           |
+    |                              |                           + app_bin_file_1
+    |      App binary content      |                           |
+    |                              |                           |
+    +------------------------------+                          -+
+    |     Unchanged bin header     |                           |
+    |..............................|                           |
+    |                              |                           + app_bin_file_2
+    |      App binary content      |                           |
+    |                              |                           |
+    +------------------------------+                          -+
+    |                              |                           |
+                  ...                                         ...
+    |                              |                           |
+    +------------------------------+                          -+
+    |     Unchanged bin header     |                           |
+    |..............................|                           |
+    |                              |                           + app_bin_file_n
+    |      App binary content      |                           |
+    |                              |                           |
+    +------------------------------+                          -+
+    |                              |                           |
+    |      RMM binary content      |                           +rmm_bin
+    |                              |                           |
+    +------------------------------+                          -+
+```
+"""
+
+from argparse import ArgumentParser
+import logging
+import struct
+
+logger = None
+
+
+def initial_branch_instruction(offset):
+    """Generate the initial branch instruction to jump to RMM text"""
+    assert offset > 0
+    assert offset % 4 == 0
+    imm = offset // 4
+    assert imm < (1 << 26)  # imm can be at most 25 bits
+    template = 0x94000000
+    # Use struct to make sure that the result is a 4 byte integer in
+    # little-endian byte order
+    return struct.pack("<I", template | imm)
+
+
+def main():
+    """Main function of the script"""
+
+    parser = ArgumentParser(
+        description="Create a bundle from the app and RMM binaries."
+    )
+    parser.add_argument(
+        "app_bin_files",
+        metavar="APP_BIN_FILE",
+        type=str,
+        nargs="+",
+        help="input application data file(s) for bin generation",
+    )
+    parser.add_argument(
+        "--out-bin",
+        metavar="FILE",
+        type=str,
+        required=True,
+        help="the output bin file generated by gen_app_bin.py",
+    )
+    parser.add_argument(
+        "--rmm-bin",
+        metavar="FILE",
+        type=str,
+        required=True,
+        help="the RMM bin input file for bin generation",
+    )
+    parser.add_argument(
+        "--log-file-name",
+        metavar="FILE",
+        type=str,
+        required=False,
+        default="",
+        help="write logs to 'FILE' as well",
+    )
+
+    args = parser.parse_args()
+
+    global logger
+    logger = logging.getLogger()
+    logger.setLevel(logging.DEBUG)
+    fmt = logging.Formatter("%(levelname)s: %(message)s")
+    hdl = logging.StreamHandler()
+    hdl.setFormatter(fmt)
+    logger.addHandler(hdl)
+
+    if args.log_file_name:
+        hdl = logging.FileHandler(args.log_file_name, mode="w")
+        hdl.setFormatter(fmt)
+        logger.addHandler(hdl)
+
+    apps_size = 0
+    app_bin_contents = []
+
+    # Collect the contents of the app bin files and concatenate them in a list.
+    for app_bin_file_name in args.app_bin_files:
+        with open(app_bin_file_name, "rb") as app_bin_file:
+            app_bin_content = app_bin_file.read()
+            apps_size += len(app_bin_content)
+            app_bin_contents.append(app_bin_content)
+
+    # Create the bundled bin file
+    with open(args.out_bin, "wb") as out_file:
+        # Write the starting branch instruction
+        out_file.write(initial_branch_instruction(apps_size))
+        # for the first entry, the Initial branch instruction is added in place
+        # the first 4 bytes of the padding in the app header.
+        start_offset = 4
+        for app_bin_content in app_bin_contents:
+            out_file.write(app_bin_content[start_offset:])
+            # For the rest of the files, write the full header
+            start_offset = 0
+
+        # Add the RMM bin file to the bundle
+        with open(args.rmm_bin, "rb") as rmm_bin_file:
+            out_file.write(rmm_bin_file.read())
+
+    logger.info(
+        f"{args.out_bin} was successfully created. Added {len(args.app_bin_files)} app(s)."
+    )
+    logger.info(f"The offset of the RMM bin is {apps_size} (0x{apps_size:x}) bytes")
+
+
+if __name__ == "__main__":
+    main()
diff --git a/app/common/CMakeLists.txt b/app/common/CMakeLists.txt
new file mode 100644
index 0000000..acc4d86
--- /dev/null
+++ b/app/common/CMakeLists.txt
@@ -0,0 +1,10 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+add_subdirectory("el0_app")
+add_subdirectory("framework")
+add_subdirectory("rmm_svc")
+
+set(RMM_EL2_STUB_LIBRARIES ${RMM_EL2_STUB_LIBRARIES} PARENT_SCOPE)
diff --git a/app/common/el0_app/CMakeLists.txt b/app/common/el0_app/CMakeLists.txt
new file mode 100644
index 0000000..59f90d8
--- /dev/null
+++ b/app/common/el0_app/CMakeLists.txt
@@ -0,0 +1,37 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+add_library(rmm-app-common-el0_app)
+
+target_link_libraries(rmm-app-common-el0_app
+  PRIVATE
+  rmm-lib-console
+  rmm-lib-common
+  rmm-lib-debug
+)
+
+target_include_directories(rmm-app-common-el0_app
+  PUBLIC "include"
+         "include/${RMM_ARCH}"
+  PRIVATE "../../common/include"
+          "../../common/include/${RMM_ARCH}")
+
+target_sources(rmm-app-common-el0_app
+  PRIVATE
+    "src/el0_app_helpers.c"
+    "src/${RMM_ARCH}/el0_app_arch.c")
+
+target_compile_definitions(rmm-app-common-el0_app
+    PUBLIC
+        "ATTEST_EL3_TOKEN_SIGN=$<IF:$<BOOL:${ATTEST_EL3_TOKEN_SIGN}>,1,0>"
+)
+
+if (RMM_ARCH STREQUAL fake_host)
+    target_link_libraries(rmm-app-common-el0_app
+        PRIVATE pthread)
+else()
+    target_sources(rmm-app-common-el0_app
+        PRIVATE "src/${RMM_ARCH}/el0_app_helpers.S")
+endif()
diff --git a/app/common/el0_app/include/aarch64/el0_app_helpers_arch.h b/app/common/el0_app/include/aarch64/el0_app_helpers_arch.h
new file mode 100644
index 0000000..c7406c2
--- /dev/null
+++ b/app/common/el0_app/include/aarch64/el0_app_helpers_arch.h
@@ -0,0 +1,27 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef EL0_APP_HELPERS_ARCH_H
+#define EL0_APP_HELPERS_ARCH_H
+
+#include <app_common.h>
+#include <arch_helpers.h>
+
+__unused static void *get_heap_start(void)
+{
+	uint64_t heap_properties = read_tpidrro_el0();
+
+	return  (void *)(heap_properties & HEAP_VA_MASK);
+}
+
+__unused static size_t get_heap_size(void)
+{
+	uint64_t heap_properties = read_tpidrro_el0();
+
+	return  (heap_properties & HEAP_PAGE_COUNT_MASK) * GRANULE_SIZE;
+}
+
+#endif /* EL0_APP_HELPERS_ARCH_H */
+
diff --git a/app/common/el0_app/include/el0_app_helpers.h b/app/common/el0_app/include/el0_app_helpers.h
new file mode 100644
index 0000000..6488b28
--- /dev/null
+++ b/app/common/el0_app/include/el0_app_helpers.h
@@ -0,0 +1,29 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef EL0_APP_HELPERS_H
+#define EL0_APP_HELPERS_H
+
+#include <el0_app_helpers_arch.h>
+#include <stddef.h>
+
+void *get_shared_mem_start(void);
+size_t get_shared_mem_size(void);
+
+unsigned long el0_app_entry_func(
+	unsigned long func_id,
+	unsigned long arg_0,
+	unsigned long arg_1,
+	unsigned long arg_2,
+	unsigned long arg_3);
+
+unsigned long el0_app_service_call(unsigned long service_index,
+				   unsigned long arg0,
+				   unsigned long arg1,
+				   unsigned long arg2,
+				   unsigned long arg3);
+
+#endif /* EL0_APP_HELPERS_H */
+
diff --git a/app/common/el0_app/include/fake_host/el0_app_helpers_arch.h b/app/common/el0_app/include/fake_host/el0_app_helpers_arch.h
new file mode 100644
index 0000000..789c476
--- /dev/null
+++ b/app/common/el0_app/include/fake_host/el0_app_helpers_arch.h
@@ -0,0 +1,15 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef EL0_APP_HELPERS_ARCH_H
+#define EL0_APP_HELPERS_ARCH_H
+
+#include <stddef.h>
+
+void *get_heap_start(void);
+size_t get_heap_size(void);
+
+#endif /* EL0_APP_HELPERS_ARCH_H */
+
diff --git a/app/common/el0_app/linker.lds b/app/common/el0_app/linker.lds
new file mode 100644
index 0000000..32a1b61
--- /dev/null
+++ b/app/common/el0_app/linker.lds
@@ -0,0 +1,74 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <sizes.h>
+#include <utils_def.h>
+#include <xlat_high_va.h>
+
+#if (GRANULE_SIZE == 0)
+#error "Required config options not set for the linker script."
+#endif
+
+ENTRY(el0_app_entry)
+
+#define APP_VA_START	(UL(0xffffffffffffffff) - XLAT_HIGH_VA_SIZE + 1)
+
+#define EL0_APP_TEXT_START	(APP_VA_START + (2*4096)) /* leave some va space for the app_reg_ctx area */
+
+MEMORY {
+	/* The length value is specified as XLAT_HIGH_VA_SIZE -1 as it seems
+	 * that the llvm linker cannot handle if the last byte is the region is
+	 * UINT64_MAX.
+	 */
+	RAM (rwx): ORIGIN = APP_VA_START, LENGTH = XLAT_HIGH_VA_SIZE - 1
+}
+
+SECTIONS
+{
+	.text EL0_APP_TEXT_START : {
+		*app_helpers.S.obj(.text*)
+		*(.text*)
+		. = ALIGN(GRANULE_SIZE);
+	} >RAM
+
+	.rodata ALIGN(GRANULE_SIZE) : {
+		*(.rodata*)
+		*(.got*)
+		. = ALIGN(GRANULE_SIZE);
+	} >RAM
+
+	.data ALIGN(GRANULE_SIZE) : {
+		*(.data*)
+		. = ALIGN(GRANULE_SIZE);
+	} >RAM
+
+	.bss ALIGN(GRANULE_SIZE) (NOLOAD) : {
+		*(.bss*)
+		. = ALIGN(GRANULE_SIZE);
+	} >RAM
+
+	.shared ALIGN(GRANULE_SIZE) (NOLOAD) : {
+		el0_app_shared_start = .;
+		. = . + (GRANULE_SIZE);
+		el0_app_shared_end = .;
+	} >RAM
+
+	/*
+	 * Add this section (although not used) to make sure that it is not
+	 * generated by the linker in between other sections used by the app
+	 */
+	.rela.dyn ALIGN(8) : {
+		*(.rela*)
+	} >RAM
+
+	/DISCARD/ : { *(.dynstr*) }
+	/DISCARD/ : { *(.dynsym*) }
+	/DISCARD/ : { *(.dynamic*) }
+	/DISCARD/ : { *(.gnu*) }
+	/DISCARD/ : { *(.hash*) }
+	/DISCARD/ : { *(.interp*) }
+	/DISCARD/ : { *(.note*) }
+	/DISCARD/ : { *(.plt*) }
+}
diff --git a/app/common/el0_app/src/aarch64/el0_app_arch.c b/app/common/el0_app/src/aarch64/el0_app_arch.c
new file mode 100644
index 0000000..859e635
--- /dev/null
+++ b/app/common/el0_app/src/aarch64/el0_app_arch.c
@@ -0,0 +1,21 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <el0_app_helpers.h>
+#include <import_sym.h>
+#include <stdint.h>
+
+IMPORT_SYM(uintptr_t, el0_app_shared_start, EL0_APP_SHARED_START);
+IMPORT_SYM(uintptr_t, el0_app_shared_end, EL0_APP_SHARED_END);
+
+void *get_shared_mem_start(void)
+{
+	return (void *)EL0_APP_SHARED_START;
+}
+
+size_t get_shared_mem_size(void)
+{
+	return EL0_APP_SHARED_END - EL0_APP_SHARED_START;
+}
diff --git a/app/common/el0_app/src/aarch64/el0_app_helpers.S b/app/common/el0_app/src/aarch64/el0_app_helpers.S
new file mode 100644
index 0000000..0ee6c45
--- /dev/null
+++ b/app/common/el0_app/src/aarch64/el0_app_helpers.S
@@ -0,0 +1,42 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <app_common.h>
+#include <asm_macros.S>
+
+.globl el0_app_entry
+.globl el0_app_service_call
+
+/*
+ * This function must be the first function in the file!
+ * This function is the entry point, linked to be the first in the .text
+ * section.
+ */
+func el0_app_entry
+	mov	x20, x0
+	mov	x21, x1
+	mov	x22, x2
+	mov	x23, x3
+	mov	x24, x4
+	mov	x25, lr
+	bl init_console
+	mov	x0, x20
+	mov	x1, x21
+	mov	x2, x22
+	mov	x3, x23
+	mov	x4, x24
+	mov	lr, x25
+app_loop:
+	bl el0_app_entry_func
+	svc APP_EXIT_CALL
+	b app_loop
+endfunc el0_app_entry
+
+func el0_app_service_call
+	svc APP_SERVICE_CALL
+	ret
+endfunc el0_app_service_call
+
+
diff --git a/app/common/el0_app/src/el0_app_helpers.c b/app/common/el0_app/src/el0_app_helpers.c
new file mode 100644
index 0000000..0c25ea6
--- /dev/null
+++ b/app/common/el0_app/src/el0_app_helpers.c
@@ -0,0 +1,56 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <app_common.h>
+#include <console.h>
+#include <debug.h>
+#include <el0_app_helpers.h>
+#include <errno.h>
+#include <limits.h>
+
+int app_console_putc(int character, const struct console *console);
+static void app_console_flush(const struct console *console);
+void init_console(void);
+void mbedtls_exit_panic(unsigned int reason);
+
+static struct console app_console = {
+	.flush = app_console_flush,
+	.putc = app_console_putc
+};
+
+int app_console_putc(int character, const struct console *console)
+{
+	signed char *shared_buf = (signed char *)get_shared_mem_start();
+	unsigned long ret;
+
+	(void) console;
+	shared_buf[0] = (signed char)character;
+	ret = el0_app_service_call(APP_SERVICE_PRINT, 1, 0, 0, 0);
+
+	if (ret > (unsigned long)INT_MAX) {
+		return -1;
+	} else {
+		return (int)ret;
+	}
+
+}
+
+static void app_console_flush(const struct console *console)
+{
+	(void)console;
+	/* Do nothing */
+}
+
+void init_console(void)
+{
+	(void)console_register(&app_console);
+}
+
+/* Used by Mbed TLS buffer alloc */
+void mbedtls_exit_panic(unsigned int reason)
+{
+	(void) reason;
+	panic();
+}
diff --git a/app/common/el0_app/src/fake_host/el0_app_arch.c b/app/common/el0_app/src/fake_host/el0_app_arch.c
new file mode 100644
index 0000000..0b93583
--- /dev/null
+++ b/app/common/el0_app/src/fake_host/el0_app_arch.c
@@ -0,0 +1,291 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <app_common.h>
+#include <app_common_arch.h>
+#include <assert.h>
+#include <debug.h>
+#include <el0_app_helpers.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <utils_def.h>
+
+extern size_t app_heap_page_count;
+
+static uint8_t shared_buffer[GRANULE_SIZE];
+static struct app_instance_data_list_t *instance_list;
+
+struct app_instance_data_t {
+	int read_from_main_fd;
+	int write_to_main_fd;
+	void *heap_start;
+	size_t heap_size;
+};
+
+struct app_instance_data_list_t {
+	struct app_instance_data_t data;
+	pthread_t thread_id;
+	int read_from_instance_fd;
+	int write_to_instance_fd;
+	struct app_instance_data_list_t *next;
+};
+
+void *get_shared_mem_start(void)
+{
+	return shared_buffer;
+}
+
+size_t get_shared_mem_size(void)
+{
+	return GRANULE_SIZE;
+}
+
+static struct app_instance_data_list_t *get_instance_list_item(pthread_t thread_id)
+{
+	struct app_instance_data_list_t *curr = instance_list;
+
+	while (curr != NULL) {
+		if (curr->thread_id == thread_id) {
+			return curr;
+		}
+		curr = curr->next;
+	}
+	return NULL;
+}
+
+static struct app_instance_data_t *get_instance_data(pthread_t thread_id)
+{
+	struct app_instance_data_list_t *curr =
+		get_instance_list_item(thread_id);
+	if (curr != NULL) {
+		return &curr->data;
+	}
+	return NULL;
+}
+
+void *get_heap_start(void)
+{
+	pthread_t thread_id = pthread_self();
+	struct app_instance_data_t *app_data = get_instance_data(thread_id);
+
+	return app_data->heap_start;
+}
+
+static void insert_instance_data(struct app_instance_data_list_t *inst_data)
+{
+	struct app_instance_data_list_t **tail = &instance_list;
+
+	while (*tail != NULL) {
+		tail = &((*tail)->next);
+	}
+	*tail = inst_data;
+}
+
+/* Call a service from the app
+ *
+ * Write the service call ID and parameters to the RMM, and read back the
+ * results.
+ */
+unsigned long el0_app_service_call(unsigned long service_index,
+				   unsigned long arg0,
+				   unsigned long arg1,
+				   unsigned long arg2,
+				   unsigned long arg3)
+{
+
+	pthread_t thread_id = pthread_self();
+	struct app_instance_data_t *app_data = get_instance_data(thread_id);
+
+	unsigned long reason = APP_SERVICE_CALL;
+
+	unsigned long bytes_to_forward =
+		6 * sizeof(unsigned long) +
+		sizeof(shared_buffer);
+
+	WRITE_OR_EXIT(app_data->write_to_main_fd, &bytes_to_forward, sizeof(bytes_to_forward));
+	WRITE_OR_EXIT(app_data->write_to_main_fd, &reason, sizeof(reason));
+	WRITE_OR_EXIT(app_data->write_to_main_fd, &service_index, sizeof(service_index));
+	WRITE_OR_EXIT(app_data->write_to_main_fd, &arg0, sizeof(arg0));
+	WRITE_OR_EXIT(app_data->write_to_main_fd, &arg1, sizeof(arg1));
+	WRITE_OR_EXIT(app_data->write_to_main_fd, &arg2, sizeof(arg2));
+	WRITE_OR_EXIT(app_data->write_to_main_fd, &arg3, sizeof(arg3));
+	WRITE_OR_EXIT(app_data->write_to_main_fd, shared_buffer, sizeof(shared_buffer));
+
+	unsigned long retval;
+
+	READ_OR_EXIT(app_data->read_from_main_fd, &retval, sizeof(retval));
+	READ_OR_EXIT(app_data->read_from_main_fd, shared_buffer, sizeof(shared_buffer));
+	return retval;
+}
+
+void *app_instance_main(void *args)
+{
+	struct app_instance_data_t *app_data =
+		(struct app_instance_data_t *)args;
+
+	unsigned long arg0, arg1, arg2, arg3;
+
+
+	while (true) {
+		unsigned long app_func_id;
+
+		READ_OR_EXIT(app_data->read_from_main_fd, &app_func_id, sizeof(app_func_id));
+		READ_OR_EXIT(app_data->read_from_main_fd, &arg0, sizeof(arg0));
+		READ_OR_EXIT(app_data->read_from_main_fd, &arg1, sizeof(arg1));
+		READ_OR_EXIT(app_data->read_from_main_fd, &arg2, sizeof(arg2));
+		READ_OR_EXIT(app_data->read_from_main_fd, &arg3, sizeof(arg3));
+		READ_OR_EXIT(app_data->read_from_main_fd, shared_buffer, sizeof(shared_buffer));
+
+		unsigned long retval = el0_app_entry_func(app_func_id, arg0, arg1, arg2, arg3);
+		unsigned long reason = APP_EXIT_CALL;
+
+		unsigned long bytes_to_forward =
+			2 * sizeof(unsigned long) +
+			sizeof(shared_buffer);
+
+		WRITE_OR_EXIT(app_data->write_to_main_fd, &bytes_to_forward,
+			sizeof(bytes_to_forward));
+		WRITE_OR_EXIT(app_data->write_to_main_fd, &reason, sizeof(reason));
+		WRITE_OR_EXIT(app_data->write_to_main_fd, &retval, sizeof(retval));
+		WRITE_OR_EXIT(app_data->write_to_main_fd, shared_buffer, sizeof(shared_buffer));
+	}
+	return NULL;
+}
+
+static pthread_t create_app_instance(void)
+{
+	/* TODO: The app instances are leaked, because there is no way currently
+	 * to get notification of the main process about an app being destroyed.
+	 */
+	struct app_instance_data_list_t *instance_list_new;
+	int ret;
+
+	instance_list_new = (struct app_instance_data_list_t *)
+		malloc(sizeof(struct app_instance_data_list_t));
+	if (instance_list_new == NULL) {
+		exit(1);
+	}
+
+	int fds_main_to_instance[2];
+	int fds_instance_to_main[2];
+
+	if (pipe(fds_main_to_instance) == -1) {
+		exit(1);
+	}
+	if (pipe(fds_instance_to_main) == -1) {
+		exit(1);
+	}
+
+	instance_list_new->read_from_instance_fd = fds_instance_to_main[0];
+	instance_list_new->write_to_instance_fd = fds_main_to_instance[1];
+	instance_list_new->data.read_from_main_fd = fds_main_to_instance[0];
+	instance_list_new->data.write_to_main_fd = fds_instance_to_main[1];
+
+	instance_list_new->data.heap_size = app_heap_page_count * GRANULE_SIZE;
+	instance_list_new->data.heap_start = malloc(instance_list_new->data.heap_size);
+	if (instance_list_new->data.heap_start == NULL) {
+		exit(1);
+	}
+
+	insert_instance_data(instance_list_new);
+
+	ret = pthread_create(&(instance_list_new->thread_id),
+			     NULL,
+			     app_instance_main,
+			     &(instance_list_new->data));
+	if (ret != 0) {
+		exit(1);
+	}
+	return instance_list_new->thread_id;
+}
+
+void call_app_instance(int process_read_fd, int process_write_fd, pthread_t thread_id)
+{
+
+	struct app_instance_data_list_t *instance_data = get_instance_list_item(thread_id);
+
+	unsigned long num_bytes_to_forward;
+	unsigned long bytes_forwarded = 0;
+	char copy_buffer[6*1024];
+
+	/* Send the call details */
+	READ_OR_EXIT(process_read_fd, &num_bytes_to_forward, sizeof(num_bytes_to_forward));
+	while (bytes_forwarded < num_bytes_to_forward) {
+		size_t to_forward = min(num_bytes_to_forward, sizeof(copy_buffer));
+
+		READ_OR_EXIT(process_read_fd, copy_buffer, to_forward);
+		WRITE_OR_EXIT(instance_data->write_to_instance_fd, copy_buffer, to_forward);
+		bytes_forwarded += to_forward;
+	}
+
+	/* return the response */
+	bytes_forwarded = 0;
+	READ_OR_EXIT(instance_data->read_from_instance_fd, &num_bytes_to_forward,
+		sizeof(num_bytes_to_forward));
+	while (bytes_forwarded < num_bytes_to_forward) {
+		size_t to_forward = min(num_bytes_to_forward, sizeof(copy_buffer));
+
+		READ_OR_EXIT(instance_data->read_from_instance_fd, copy_buffer, to_forward);
+		WRITE_OR_EXIT(process_write_fd, copy_buffer, to_forward);
+		bytes_forwarded += to_forward;
+	}
+}
+
+int main(int argc, char **argv)
+{
+	char *end;
+	int process_read_fd;
+	int process_write_fd;
+
+	if (argc != 3) {
+		ERROR("argc is %d instead of 3\n", argc);
+		return 1;
+	}
+
+	process_read_fd = (int)strtol(argv[1], &end, 0);
+	if (end != argv[1] + strlen(argv[1])) {
+		ERROR("App: Invalid read fd '%s'.\n", argv[1]);
+		return 1;
+	}
+
+	process_write_fd = (int)strtol(argv[2], &end, 0);
+
+	if (end != argv[2] + strlen(argv[2])) {
+		ERROR("App: Invalid write fd '%s'.\n", argv[2]);
+		return 1;
+	}
+
+	while (true) {
+		unsigned long command;
+
+		/* read the command */
+		READ_OR_EXIT_NOERROR(process_read_fd, &command, sizeof(command));
+
+		switch (command) {
+		case CREATE_NEW_APP_INSTANCE:
+		{
+			pthread_t thread_id = create_app_instance();
+
+			WRITE_OR_EXIT(process_write_fd, &thread_id, sizeof(thread_id));
+			break;
+		}
+		case CALL_APP_INSTANCE:
+		{
+			pthread_t thread_id;
+
+			READ_OR_EXIT(process_read_fd, &thread_id, sizeof(thread_id));
+			call_app_instance(process_read_fd, process_write_fd, thread_id);
+			break;
+		}
+		default:
+			ERROR("APP - Invalid command %lu (%lx)\n", command, command);
+			exit(1);
+		}
+	}
+	return 0;
+}
diff --git a/app/common/framework/CMakeLists.txt b/app/common/framework/CMakeLists.txt
new file mode 100644
index 0000000..85ab60f
--- /dev/null
+++ b/app/common/framework/CMakeLists.txt
@@ -0,0 +1,47 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+add_library(rmm-app-cmn-fw)
+
+target_link_libraries(rmm-app-cmn-fw
+    PRIVATE
+        rmm-lib-arch
+        rmm-lib-debug
+        rmm-lib-console
+        rmm-lib-slot_buf
+        rmm-el2-stub
+        rmm-lib-xlat)
+
+target_include_directories(rmm-app-cmn-fw
+    PRIVATE "src"
+    PUBLIC "include"
+           "include/${RMM_ARCH}"
+           "../include/${RMM_ARCH}"
+           "../../common/include")
+
+if((HOST_VARIANT STREQUAL "host_cbmc") OR (HOST_VARIANT STREQUAL "host_test"))
+    target_sources(rmm-app-cmn-fw
+        PRIVATE "src/no_app_support/app.c"
+                "src/no_app_support/app_header.c"
+        )
+else()
+    target_sources(rmm-app-cmn-fw
+        PRIVATE "src/${RMM_ARCH}/app.c"
+                "src/${RMM_ARCH}/app_header.c"
+        )
+endif()
+
+target_compile_definitions(rmm-app-cmn-fw
+    PUBLIC
+        "ATTEST_EL3_TOKEN_SIGN=$<IF:$<BOOL:${ATTEST_EL3_TOKEN_SIGN}>,1,0>"
+)
+
+if (NOT RMM_ARCH STREQUAL fake_host)
+    target_sources(rmm-app-cmn-fw
+        PRIVATE "src/${RMM_ARCH}/app-asm.S")
+endif()
+
+list(APPEND RMM_EL2_STUB_LIBRARIES "rmm-app-cmn-fw")
+set(RMM_EL2_STUB_LIBRARIES ${RMM_EL2_STUB_LIBRARIES} PARENT_SCOPE)
diff --git a/app/common/framework/include/aarch64/app_fw_structures.h b/app/common/framework/include/aarch64/app_fw_structures.h
new file mode 100644
index 0000000..737dcf0
--- /dev/null
+++ b/app/common/framework/include/aarch64/app_fw_structures.h
@@ -0,0 +1,90 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef APP_FW_STRUCTURES_H
+#define APP_FW_STRUCTURES_H
+
+#include <utils_def.h>
+#include <xlat_high_va.h>
+
+#define APP_SAVED_GEN_REG_COUNT		U(31)
+
+#define APP_XLAT_TABLE_COUNT		U(1)
+
+#define APP_TTBR1_EL2_OFFSET		UL(0)
+#define RMM_TTBR1_EL2_OFFSET		UL(8)
+#define RMM_VBAR_EL2_OFFSET		UL(16)
+#define RMM_TPIDR_EL0_OFFSET		UL(24)
+#define RMM_TPIDRRO_EL0_OFFSET		UL(32)
+#define PC_OFFSET			UL(40)
+#define PSTATE_OFFSET			UL(48)
+#define APP_SAVED_REGS_OFFSET		UL(56)
+#define SP_EL0_OFFSET			UL(304)
+
+#ifndef __ASSEMBLER__
+
+/* This structure must always be aligned to page boundary as it is mapped into
+ * the app VA space.
+ */
+struct app_reg_ctx {
+	/*
+	 * Members are defined with macros to make sure that the member offsets
+	 * that are used by the assembly code are aligned with the structure
+	 * definition.
+	 */
+	SET_MEMBER(uint64_t app_ttbr1_el2, APP_TTBR1_EL2_OFFSET, RMM_TTBR1_EL2_OFFSET);
+	SET_MEMBER(uint64_t rmm_ttbr1_el2, RMM_TTBR1_EL2_OFFSET, RMM_VBAR_EL2_OFFSET);
+	SET_MEMBER(uint64_t rmm_vbar_el2, RMM_VBAR_EL2_OFFSET, RMM_TPIDR_EL0_OFFSET);
+	SET_MEMBER(uint64_t rmm_tpidr_el0, RMM_TPIDR_EL0_OFFSET, RMM_TPIDRRO_EL0_OFFSET);
+	SET_MEMBER(uint64_t rmm_tpidrro_el0, RMM_TPIDRRO_EL0_OFFSET, PC_OFFSET);
+	SET_MEMBER(uint64_t pc, PC_OFFSET, PSTATE_OFFSET);
+	SET_MEMBER(uint64_t pstate, PSTATE_OFFSET, APP_SAVED_REGS_OFFSET);
+	SET_MEMBER(uint64_t app_regs[APP_SAVED_GEN_REG_COUNT],
+		APP_SAVED_REGS_OFFSET, SP_EL0_OFFSET);
+	SET_MEMBER(uint64_t sp_el0, SP_EL0_OFFSET, GRANULE_SIZE);
+} __aligned(GRANULE_SIZE);
+COMPILER_ASSERT(sizeof(struct app_reg_ctx) == GRANULE_SIZE);
+COMPILER_ASSERT(SIZEOF_MEMBER(struct app_reg_ctx, app_regs) ==
+			(sizeof(unsigned long) * APP_SAVED_GEN_REG_COUNT));
+/*
+ * NS_TPIDR[RO]_EL0_OFFSET registers are saved/restored with a single stp/ldr
+ * instruction, so they must be close.
+ */
+COMPILER_ASSERT((RMM_TPIDR_EL0_OFFSET + sizeof(unsigned long)) == RMM_TPIDRRO_EL0_OFFSET);
+/*
+ * The code entering and exiting from EL0 app assumes that sp_el0 is
+ * right after the `regs` array.
+ */
+COMPILER_ASSERT(U(offsetof(struct app_reg_ctx, sp_el0)) ==
+	(U(offsetof(struct app_reg_ctx, app_regs)) +
+		(APP_SAVED_GEN_REG_COUNT * sizeof(unsigned long))));
+/* Make sure that the last member in the list is at the desired offset */
+COMPILER_ASSERT(U(offsetof(struct app_reg_ctx, sp_el0)) == SP_EL0_OFFSET);
+
+struct app_data_cfg {
+	/* Structures for setting up and storing app translation related data */
+	struct xlat_ctx_cfg app_va_xlat_ctx_cfg;
+	struct xlat_ctx app_va_xlat_ctx;
+	struct xlat_ctx_tbls app_va_tbls;
+	struct xlat_mmu_cfg mmu_config;
+	struct xlat_llt_info cached_app_llt_info;
+	uintptr_t app_reg_ctx_pa;
+
+	uintptr_t shared_page_pa;
+	uintptr_t el0_shared_page_va;
+	void *el2_shared_page; /* Is NULL while the shared page is not mapped */
+	uintptr_t heap_va;
+	uintptr_t heap_size;
+	uintptr_t stack_buf_start_va;
+
+	/* App entry point VA */
+	uintptr_t entry_point;
+
+	bool app_entered;
+};
+COMPILER_ASSERT((XLAT_TABLE_ENTRIES * APP_XLAT_TABLE_COUNT) <= GRANULE_SIZE);
+
+#endif /* __ASSEMBLER__ */
+#endif /* APP_FW_STRUCTURES_H */
diff --git a/app/common/framework/include/aarch64/app_header_structures.h b/app/common/framework/include/aarch64/app_header_structures.h
new file mode 100644
index 0000000..341143e
--- /dev/null
+++ b/app/common/framework/include/aarch64/app_header_structures.h
@@ -0,0 +1,66 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef APP_HEADER_STRUCTURES_H
+#define APP_HEADER_STRUCTURES_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <utils_def.h>
+
+#define APP_NAME_BUF_SIZE	32U
+#define APP_HEADER_SIZE		GRANULE_SIZE
+
+struct app_header {
+	/* When the final rmm binary is constructed, an initial bl instruction
+	 * is inserted at the beginning of the img file that branches to the
+	 * offset of the function rmm_entry.
+	 * The first 4 bytes of the padding field is modified to hold the bl
+	 * instruction. This technique allows image packing logic to avoid
+	 * adding an extra 4K alignment to the overall image and keep the
+	 * alignments intact.
+	 * Section offsets in this structures are calculated from the byte after
+	 * the page containing the header.
+	 * This structure needs to match the header format in app/gen_app_bin.py
+	 * file.
+	 */
+	uint64_t padding;
+
+	uint64_t app_header_magic;
+	uint32_t app_header_version;
+	const char app_name[APP_NAME_BUF_SIZE]; /* Null terminated string */
+	uint32_t app_id;
+	uint64_t app_len; /* including header */
+
+	uintptr_t section_text_offset;
+	uintptr_t section_text_va;
+	size_t section_text_size;
+
+	uintptr_t section_rodata_offset;
+	uintptr_t section_rodata_va;
+	size_t section_rodata_size;
+
+	uintptr_t section_data_offset;
+	uintptr_t section_data_va;
+	size_t section_data_size;
+
+	/* Following are not allocated in the bin */
+	uintptr_t section_bss_va;
+	size_t section_bss_size;
+
+	uintptr_t section_shared_va;
+
+	size_t stack_page_count;
+
+	size_t heap_page_count;
+
+	/* Reserve a few dwords for later extending the header */
+	uint64_t reserved[10];
+
+	uint64_t app_header_magic2;
+};
+COMPILER_ASSERT(sizeof(struct app_header) <= APP_HEADER_SIZE);
+
+#endif /* APP_HEADER_STRUCTURES_H */
diff --git a/app/common/framework/include/app.h b/app/common/framework/include/app.h
new file mode 100644
index 0000000..ea2c281
--- /dev/null
+++ b/app/common/framework/include/app.h
@@ -0,0 +1,92 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef APP_H
+#define APP_H
+
+#include <app_fw_structures.h>
+
+#define APP_VA_START	(UL(0xffffffffffffffff) - XLAT_HIGH_VA_SIZE + 1U)
+
+#ifndef __ASSEMBLER__
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#define GRANULE_COUNT(size)	(round_up(size, GRANULE_SIZE) / GRANULE_SIZE)
+
+/*
+ * Function to set up the App framework. Called once, during cold boot.
+ */
+void app_framework_setup(void);
+
+/*
+ * Function to calculate the number of per instance granules that are used by
+ * this app.
+ *
+ * Arguments:
+ *	- app_id: The ID of the application.
+ *
+ * Return:
+ *	- The number of granules required by the app.
+ */
+size_t app_get_required_granule_count(unsigned long app_id);
+
+/*
+ * Initialise the config data for an App instance.
+ *
+ * Arguments:
+ *	- app_data: Pointer to the config to be initialised
+ *	- app_id: The id of the app to be initialised
+ *	- granule_pas: An array of Granule PAS that can be used for per instance
+ *        data in the app
+ *	- granule_count: The number of elements in the granule_pas array.
+ *
+ * Return:
+ *	- 0 on success or a negative POSIX error otherwise.
+ */
+int app_init_data(struct app_data_cfg *app_data,
+		      unsigned long app_id,
+		      uintptr_t granule_pas[],
+		      size_t granule_count);
+
+/*
+ * Resume the app instance execution specified by the app data config. This
+ * function loads the app register context, runs the app, and returns to the
+ * caller if the app finished running by calling an SVC or caused an exception.
+ *
+ * Arguments:
+ *	- app_data: An initialised app config (selects the app instance to run)
+ *	- app_func_id: The id of the function in the app to be run
+ *	- arg0 - arg3: Arguments to the app function
+ *
+ * Return:
+ *	- App specific return value
+ */
+unsigned long app_run(struct app_data_cfg *app_data,
+			  unsigned long app_func_id,
+			  unsigned long arg0,
+			  unsigned long arg1,
+			  unsigned long arg2,
+			  unsigned long arg3);
+
+/*
+ * Map the app shared page in the EL2 VA space
+ *
+ * Arguments:
+ *	- app_data: An initialised app config
+ */
+void app_map_shared_page(struct app_data_cfg *app_data);
+
+/*
+ * Unmap the app shared page from the EL2 VA space
+ *
+ * Arguments:
+ *	- app_data: An initialised app config
+ */
+void app_unmap_shared_page(struct app_data_cfg *app_data);
+
+#endif /* __ASSEMBLER__ */
+#endif /* APP_H */
diff --git a/app/common/framework/include/app_header.h b/app/common/framework/include/app_header.h
new file mode 100644
index 0000000..5dd5117
--- /dev/null
+++ b/app/common/framework/include/app_header.h
@@ -0,0 +1,48 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef APP_HEADER_H
+#define APP_HEADER_H
+
+#ifndef __ASSEMBLER__
+#include <app_header_structures.h>
+#include <stddef.h>
+#include <stdint.h>
+#endif
+
+#define APP_COUNT		0U
+
+#ifndef __ASSEMBLER__
+
+/*
+ * Return the RMM image start address.
+ *
+ * Return:
+ *	- The physical address of the start of the RMM image.
+ */
+uint64_t app_get_rmm_start(void);
+
+/*
+ * Return a pointer to the app_header structure at an index.
+ *
+ * Arguments:
+ *	- app_idx: The index of the app
+ *	- app_header: Out parameter, pointer to the app header of the `app_idx`.
+ *
+ * Return:
+ *	- 0 on success or a negative POSIX error otherwise.
+ */
+int app_get_header_ptr_at_index(unsigned long app_index, struct app_header **app_header);
+
+/*
+ * Initialise the internal app header structures.
+ *
+ * This function must be called once during cold boot to initialise the internal
+ * app header structures.
+ */
+void app_info_setup(void);
+#endif
+
+#endif /* APP_HEADER_H */
diff --git a/app/common/framework/include/fake_host/app_fw_structures.h b/app/common/framework/include/fake_host/app_fw_structures.h
new file mode 100644
index 0000000..6d5082e
--- /dev/null
+++ b/app/common/framework/include/fake_host/app_fw_structures.h
@@ -0,0 +1,32 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef APP_FW_STRUCTURES_H
+#define APP_FW_STRUCTURES_H
+
+#include <debug.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <utils_def.h>
+
+struct app_process_data {
+	unsigned long app_id;
+	int fd_rmm_to_app_process;
+	int fd_app_process_to_rmm;
+	pid_t pid;
+};
+
+struct app_data_cfg {
+	unsigned long app_id;
+	void *el2_shared_page;
+	/* This thread ID is valid in the corresponding app process, not in the
+	 * main RMM process!
+	 */
+	pthread_t thread_id;
+};
+
+#endif /* APP_FW_STRUCTURES_H */
diff --git a/app/common/framework/include/fake_host/app_header_structures.h b/app/common/framework/include/fake_host/app_header_structures.h
new file mode 100644
index 0000000..84adb64
--- /dev/null
+++ b/app/common/framework/include/fake_host/app_header_structures.h
@@ -0,0 +1,14 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef APP_HEADER_STRUCTURES_H
+#define APP_HEADER_STRUCTURES_H
+
+struct app_header {
+	unsigned long app_id;
+	char *app_elf_name;
+};
+
+#endif /* APP_HEADER_STRUCTURES_H */
diff --git a/app/common/framework/src/aarch64/app-asm.S b/app/common/framework/src/aarch64/app-asm.S
new file mode 100644
index 0000000..e5ace59
--- /dev/null
+++ b/app/common/framework/src/aarch64/app-asm.S
@@ -0,0 +1,215 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <app.h>
+#include <arch.h>
+#include <asm_macros.S>
+#include <xlat_high_va.h>
+
+.globl run_app
+.globl app_exit
+
+	.section ".text"
+
+/* TODO: It should be possible to encode the lower EL state in tpidr_el2 and the
+ * vectors could check this to differentiate between running VHE EL0 mode or
+ * Realms. This could make it possible to unify the two vector tables.
+ */
+ENTRY(app_vectors):
+	ventry_unused	exc_sync_sp0
+	ventry_unused	exc_irq_sp0
+	ventry_unused	exc_fiq_sp0
+	ventry_unused	exc_serror_sp0
+
+	ventry_unused	exc_sync_spx
+	ventry_unused	exc_irq_spx
+	ventry_unused	exc_fiq_spx
+	ventry_unused	exc_serror_spx
+
+	ventry		el2_sync_lel
+	ventry_unused	el2_irq_lel
+	ventry_unused	el2_fiq_lel
+	ventry_unused	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(app_vectors)
+
+el2_sync_lel:
+	msr	tpidr_el0, x0
+	mov	x0, #ARM_EXCEPTION_SYNC_LEL
+	b	back_from_el0
+ENDPROC(el2_sync_lel)
+
+/*
+ * int run_app(struct app_reg_ctx* app_reg_ctx, uint64_t heap_properties);
+ *
+ * Per the AAPCS the function must preserve x19-x29, along with the SP. The
+ * function may freely corrupt x0-18 and the flags, but needs the LR to return
+ * to its caller.
+ */
+func run_app
+	/* 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)]
+
+	/* save TPIDR* registers */
+	mrs	x19, tpidr_el0
+	mrs	x20, tpidrro_el0
+	stp	x19, x20, [x0, #RMM_TPIDR_EL0_OFFSET]
+
+	/* Set heap properties in TPIDRRO_EL0 for the app to read */
+	msr	tpidrro_el0, x1
+
+	/* save RMM TTBR */
+	mrs	x1, ttbr1_el2
+	str	x1, [x0, #RMM_TTBR1_EL2_OFFSET]
+
+	/* Save original vbar_el2 */
+	mrs	x1, vbar_el2
+	str	x1, [x0, #RMM_VBAR_EL2_OFFSET]
+
+	/* Update TTBR register:
+	 * Point ttbr1_el2 to the app's translation table
+	 */
+	ldr	x1, [x0, #APP_TTBR1_EL2_OFFSET]
+	msr	ttbr1_el2, x1
+
+	/* Invalidate TLB for the App ASID. The ASID is extracted from the TTBR
+	 * value. The ASID is encoded in the bits [63:48] in both the TTBR value
+	 * and in the TLBI operand, so here only bits [47:0] needs to be
+	 * cleared.
+	 */
+	/* TODO: Further optimise TLB management.
+	 * - Since Text, RO, RW and BSS are shared for all the instances of the
+	 *   App, here we could do a TLBI ASID only for the instance specific
+	 *   memory like stack, heap and shared memory.
+	 * - There is a further optimization for App instances specific to CPU.
+	 *   By using special ASID for CPU app instances (for example MSB set),
+	 *   TLB invalidation for those can be omitted.
+	 */
+	and	x1, x1, #(~((1 << 48) - 1))
+	tlbi	aside1, x1
+	dsb	nsh
+	isb
+
+	/* starting from this point the app mapping is active, so to access
+	 * app_reg_ctx, it must be done via the VA in the app mapping.
+	 * So update x0.
+	 */
+	mov_imm	x0, APP_VA_START
+
+	/* Disable Low VA range for unprivileged code */
+	mrs	x1, tcr_el2
+	orr	x1, x1, #TCR_EL2_E0PD0
+	msr	tcr_el2, x1
+
+	/* update the vector table base address to the app_return vector */
+	adrp	x1, app_vectors
+	add	x1, x1, :lo12:app_vectors
+	msr	vbar_el2, x1
+	isb
+
+	/* load app GPRs */
+	ldp	x2,  x3,  [x0, #(APP_SAVED_REGS_OFFSET + 16 * 1)]
+	ldp	x4,  x5,  [x0, #(APP_SAVED_REGS_OFFSET + 16 * 2)]
+	ldp	x6,  x7,  [x0, #(APP_SAVED_REGS_OFFSET + 16 * 3)]
+	ldp	x8,  x9,  [x0, #(APP_SAVED_REGS_OFFSET + 16 * 4)]
+	ldp	x10, x11, [x0, #(APP_SAVED_REGS_OFFSET + 16 * 5)]
+	ldp	x12, x13, [x0, #(APP_SAVED_REGS_OFFSET + 16 * 6)]
+	ldp	x14, x15, [x0, #(APP_SAVED_REGS_OFFSET + 16 * 7)]
+	ldp	x16, x17, [x0, #(APP_SAVED_REGS_OFFSET + 16 * 8)]
+	ldp	x18, x19, [x0, #(APP_SAVED_REGS_OFFSET + 16 * 9)]
+	ldp	x20, x21, [x0, #(APP_SAVED_REGS_OFFSET + 16 * 10)]
+	ldp	x22, x23, [x0, #(APP_SAVED_REGS_OFFSET + 16 * 11)]
+	ldp	x24, x25, [x0, #(APP_SAVED_REGS_OFFSET + 16 * 12)]
+	ldp	x26, x27, [x0, #(APP_SAVED_REGS_OFFSET + 16 * 13)]
+	ldp	x28, x29, [x0, #(APP_SAVED_REGS_OFFSET + 16 * 14)]
+	ldr	x30,      [x0, #(APP_SAVED_REGS_OFFSET + 16 * 15)]
+	ldr	x1,       [x0, #(APP_SAVED_REGS_OFFSET + 16 * 15 + 8)]
+	msr	sp_el0, x1
+	ldp	x0,  x1,  [x0, #(APP_SAVED_REGS_OFFSET + 16 * 0)]
+
+	eret
+	sb
+
+endfunc run_app
+
+func back_from_el0
+
+	/* Load the address of the app_reg_ctx to x1 */
+	msr	tpidrro_el0, x1
+	mov_imm	x1, APP_VA_START
+
+	/* Store app GPRs */
+	stp	x2,  x3,  [x1, #(APP_SAVED_REGS_OFFSET + 16 * 1)]
+	stp	x4,  x5,  [x1, #(APP_SAVED_REGS_OFFSET + 16 * 2)]
+	stp	x6,  x7,  [x1, #(APP_SAVED_REGS_OFFSET + 16 * 3)]
+	stp	x8,  x9,  [x1, #(APP_SAVED_REGS_OFFSET + 16 * 4)]
+	stp	x10, x11, [x1, #(APP_SAVED_REGS_OFFSET + 16 * 5)]
+	stp	x12, x13, [x1, #(APP_SAVED_REGS_OFFSET + 16 * 6)]
+	stp	x14, x15, [x1, #(APP_SAVED_REGS_OFFSET + 16 * 7)]
+	stp	x16, x17, [x1, #(APP_SAVED_REGS_OFFSET + 16 * 8)]
+	stp	x18, x19, [x1, #(APP_SAVED_REGS_OFFSET + 16 * 9)]
+	stp	x20, x21, [x1, #(APP_SAVED_REGS_OFFSET + 16 * 10)]
+	stp	x22, x23, [x1, #(APP_SAVED_REGS_OFFSET + 16 * 11)]
+	stp	x24, x25, [x1, #(APP_SAVED_REGS_OFFSET + 16 * 12)]
+	stp	x26, x27, [x1, #(APP_SAVED_REGS_OFFSET + 16 * 13)]
+	stp	x28, x29, [x1, #(APP_SAVED_REGS_OFFSET + 16 * 14)]
+	str	x30,      [x1, #(APP_SAVED_REGS_OFFSET + 16 * 15)]
+	mrs	x2, sp_el0
+	str	x2,       [x1, #(APP_SAVED_REGS_OFFSET + 16 * 15 + 8)]
+
+	/* Load the exception handler stack bottom to SP_EL0 */
+	mov_imm	x2, RMM_CPU_EH_STACK_END_VA
+	msr	sp_el0, x2
+
+	/* x0 and x1 as stored by el2_vectors and this function */
+	mrs	x2, tpidr_el0
+	mrs	x3, tpidrro_el0
+	stp	x2, x3,   [x1, #(APP_SAVED_REGS_OFFSET + 16 * 0)]
+
+	/* restore TPIDR* registers */
+	ldp	x19, x20, [x1, #RMM_TPIDR_EL0_OFFSET]
+	msr	tpidr_el0, x19
+	msr	tpidrro_el0, x20
+
+	/* restore original vbar_el2 */
+	ldr	x2, [x1, #RMM_VBAR_EL2_OFFSET]
+	msr	vbar_el2, x2
+	isb
+
+	/* restore TTBR register */
+	ldr	x2, [x1, #RMM_TTBR1_EL2_OFFSET]
+	msr	ttbr1_el2, x2
+
+	mrs	x2, tcr_el2
+	and	x2, x2, #(~TCR_EL2_E0PD1) /* TODO: restore original value instead!!! */
+	msr	tcr_el2, x2
+	isb
+
+	/*
+	 * Restore the RMM registers from the stack
+	 * including the return address to return to
+	 * after calling run_app().
+	 */
+	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 back_from_el0
diff --git a/app/common/framework/src/aarch64/app.c b/app/common/framework/src/aarch64/app.c
new file mode 100644
index 0000000..8d4f945
--- /dev/null
+++ b/app/common/framework/src/aarch64/app.c
@@ -0,0 +1,670 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <app.h>
+#include <app_common.h>
+#include <app_header.h>
+#include <app_header_private.h>
+#include <app_services.h>
+#include <arch.h>
+#include <arch_helpers.h>
+#include <assert.h>
+#include <buffer.h>
+#include <debug.h>
+#include <errno.h>
+#include <granule.h>
+#include <import_sym.h>
+#include <utils_def.h>
+#include <xlat_contexts.h>
+#include <xlat_tables.h>
+
+#define GRANULE_PA_IDX_APP_REG_CTX	0U
+#define GRANULE_PA_IDX_APP_PAGE_TABLE	1U
+#define GRANULE_PA_IDX_COUNT		2U
+
+#define RMM_APP_APP_REG_CTX_MMAP_IDX	0U
+#define RMM_APP_TEXT_MMAP_IDX		1U
+#define RMM_APP_RODATA_MMAP_IDX		2U
+#define RMM_APP_DATA_MMAP_IDX		3U
+#define RMM_APP_BSS_MMAP_IDX		4U
+#define RMM_APP_SHARED_IDX		5U
+#define RMM_APP_HEAP_IDX		6U
+#define RMM_APP_STACK_IDX		7U
+#define RMM_APP_MMAP_REGION_COUNT	8U
+
+#define ASID_SIZE_NO_FEAT_ASID16	8U
+
+struct app_id_data {
+	struct xlat_ctx_cfg app_va_xlat_ctx_cfg_base;
+	struct xlat_mmap_region mm_regions_array[RMM_APP_MMAP_REGION_COUNT];
+	uintptr_t el0_shared_page_va;
+	uintptr_t heap_va;
+	uintptr_t stack_buf_start_va;
+};
+
+static struct app_id_data app_id_data_array[APP_COUNT];
+
+struct app_bss_memory_t {
+	uintptr_t pa;
+	size_t size;
+};
+static struct app_bss_memory_t app_bss_memory_array[APP_COUNT];
+
+/* This function is implemented in assembly */
+/* TODO: get this declarations properly from a header */
+int run_app(struct app_reg_ctx *app_reg_ctx, uint64_t heap_properties);
+
+IMPORT_SYM(uintptr_t, rmm_rw_start, RMM_RW_RANGE_START);
+IMPORT_SYM(uintptr_t, rmm_rw_end, RMM_RW_RANGE_END);
+
+static bool in_rmm_rw_range(uintptr_t address)
+{
+	return (address >= RMM_RW_RANGE_START) && (address < RMM_RW_RANGE_END);
+}
+
+static void *map_page_to_slot(uintptr_t pa, enum buffer_slot slot)
+{
+	/* See whether the pa is in the rmm RW area */
+	if (in_rmm_rw_range(pa)) {
+		return (void *)pa;
+	}
+	/* Assume delegated granule */
+	struct granule *app_data_granule = find_lock_granule(pa, GRANULE_STATE_REC_AUX);
+
+	if (app_data_granule == NULL) {
+		ERROR("ERROR %s:%d\n", __func__, __LINE__);
+		return NULL;
+	}
+	return buffer_granule_map(app_data_granule, slot);
+}
+
+static void *slot_map_app_pagetable(uintptr_t pa)
+{
+	return map_page_to_slot(pa, SLOT_APP_PAGE_TABLE);
+}
+
+static void *slot_map_page_to_init(uintptr_t pa)
+{
+	return map_page_to_slot(pa, SLOT_APP_INIT);
+}
+
+static void *slot_map_app_reg_ctx_page(uintptr_t pa)
+{
+	return map_page_to_slot(pa, SLOT_APP_INIT);
+}
+
+static void unmap_page(uintptr_t pa, void *va)
+{
+	struct granule *g;
+
+	if (in_rmm_rw_range(pa)) {
+		return;
+	}
+	buffer_unmap(va);
+	g = find_granule(pa);
+	granule_unlock(g);
+}
+
+static int init_app_translation(size_t app_id,
+				struct app_data_cfg *app_data,
+				uintptr_t page_table_pa,
+				void *page_table)
+{
+	int ret;
+	size_t app_index;
+
+	if (!GRANULE_ALIGNED(page_table_pa)) {
+		return -EINVAL;
+	}
+
+	ret = app_get_index(app_id, &app_index);
+	if (ret != 0) {
+		return ret;
+	}
+
+	/* To prevent array subscript <unknown> is outside array bounds warning */
+	/* cppcheck-suppress unsignedPositive
+	 * As app_index is unsigned, app_index >= APP_COUNT is always true if
+	 * APP_COUNT is zero.
+	 */
+	/* coverity[no_effect:SUPPRESS] */
+	/* coverity[misra_c_2012_rule_14_3_violation:SUPPRESS] */
+	if (app_index >= APP_COUNT) {
+		return -EINVAL;
+	}
+
+	/* Copy the prepared base config into the app instance's own config */
+	/* coverity[deadcode:SUPPRESS] */
+	/* coverity[misra_c_2012_rule_14_3_violation:SUPPRESS] */
+	app_data->app_va_xlat_ctx_cfg = app_id_data_array[app_index].app_va_xlat_ctx_cfg_base;
+	app_data->el0_shared_page_va = app_id_data_array[app_index].el0_shared_page_va;
+	app_data->heap_va = app_id_data_array[app_index].heap_va;
+	app_data->stack_buf_start_va = app_id_data_array[app_index].stack_buf_start_va;
+
+	/*
+	 * Initialize the translation tables for the APP.
+	 */
+	ret = xlat_ctx_init(&app_data->app_va_xlat_ctx,
+				&app_data->app_va_xlat_ctx_cfg,
+				&app_data->app_va_tbls,
+				page_table,
+				APP_XLAT_TABLE_COUNT);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = xlat_arch_setup_mmu_cfg(&app_data->app_va_xlat_ctx, &app_data->mmu_config);
+	if (ret != 0) {
+		return ret;
+	}
+
+	/* TODO: The xlat library writes the VA of the base table to te TTBR.
+	 * For delegated granules this VA doesn't match with the PA, so need
+	 * to update the TTBR here.
+	 * One change to do in future could be to change
+	 * xlat_arch_setup_mmu_cfg() to take max_va, region and base table
+	 * address as input and give the mmu_config as output.
+	 */
+	if (!in_rmm_rw_range((uintptr_t)page_table)) {
+		unsigned long ttbrx = app_data->mmu_config.ttbrx;
+
+		assert((page_table_pa & MASK(TTBRx_EL2_BADDR)) == page_table_pa);
+		/* clear the bits for TTBRx_EL2_BADDR */
+		ttbrx &= ~MASK(TTBRx_EL2_BADDR);
+		/* write the new address */
+		ttbrx |= page_table_pa;
+		app_data->mmu_config.ttbrx = ttbrx;
+	}
+
+	/* Make sure that the app ID fits in the ASID field */
+	assert(app_id < (1LU << ASID_SIZE_NO_FEAT_ASID16));
+	/* ASID 0 is reserved to EL2 RMM */
+	assert(app_id != 0U);
+	/* Set the ASID field in the TTBR to the app ID */
+	app_data->mmu_config.ttbrx |= INPLACE(TTBRx_EL2_ASID, app_id);
+
+	/*
+	 * TODO: This limits the max APP VA size. (i.e. a single 3rd level table
+	 * is used). This is 2MB of address space. Provide a more general
+	 * solution (updating the cache when mapping the pages and llt changes,
+	 * etc.)
+	 */
+	return xlat_get_llt_from_va(&app_data->cached_app_llt_info,
+					&app_data->app_va_xlat_ctx,
+					APP_VA_START);
+}
+
+/* Map a page in the transient region in the APP VA space */
+static int app_xlat_map(struct app_data_cfg *app_data,
+			  uintptr_t va,
+			  uintptr_t pa,
+			  uint64_t attr)
+{
+	struct xlat_llt_info *entry = &app_data->cached_app_llt_info;
+
+	assert(GRANULE_ALIGNED(pa));
+	/* TODO: Some xlat_... functions assume they are modifying the
+	 * in-context xlat tables (and hence does all dsb, isb) , but these are
+	 * not required when modifying an out of context xlat table.
+	 */
+	return xlat_map_memory_page_with_attrs(entry, va, pa, attr);
+}
+
+static int allocate_bss(size_t app_id, size_t bss_size, uintptr_t *pa)
+{
+	/* TODO: For each application RMM should allocate the required
+	 * amount of zero initialised memory (from EL3). As currently this
+	 * allocation mechanism is not available, as a temporary workaround the
+	 * BSS memory for an app is allocated in the app's rmm_stub library.
+	 */
+	size_t app_index;
+	int ret;
+	struct app_header *app_header;
+
+	(void)bss_size;
+
+	ret = app_get_index(app_id, &app_index);
+	if (ret != 0) {
+		return ret;
+	}
+	ret = app_get_header_ptr_at_index(app_index, &app_header);
+	assert(ret == 0);
+	assert(app_bss_memory_array[app_index].size == bss_size);
+	*pa = app_bss_memory_array[app_index].pa;
+	return 0;
+}
+
+size_t app_get_required_granule_count(unsigned long app_id)
+{
+	struct app_header *app_header;
+	int ret;
+
+	ret = app_get_header_ptr(app_id, &app_header);
+	if (ret != 0) {
+		return 0UL;
+	}
+
+	return app_get_required_granule_count_from_header(app_header);
+}
+
+static uintptr_t section_start_pa(uintptr_t app_header, size_t section_offset)
+{
+	return app_header +
+	       APP_HEADER_SIZE +    /* Skip the padded app header */
+	       section_offset;
+}
+
+void app_map_shared_page(struct app_data_cfg *app_data)
+{
+	assert(app_data->el2_shared_page == NULL);
+	app_data->el2_shared_page = map_page_to_slot(app_data->shared_page_pa, SLOT_APP_SHARED);
+}
+
+void app_unmap_shared_page(struct app_data_cfg *app_data)
+{
+	assert(app_data->el2_shared_page != NULL);
+	unmap_page(app_data->shared_page_pa, app_data->el2_shared_page);
+	app_data->el2_shared_page = NULL;
+}
+
+static int app_rw_page_xlat_map(struct app_data_cfg *app_data,
+	      uintptr_t va,
+	      size_t section_size,
+	      const char *section_name,
+	      size_t *next_granule_idx,
+	      uintptr_t granule_pas[],
+	      size_t granule_count)
+{
+	size_t section_bytes_mapped;
+
+	for (section_bytes_mapped = 0;
+	     section_bytes_mapped < section_size;
+	     section_bytes_mapped += GRANULE_SIZE) {
+		int ret;
+
+		if (*next_granule_idx >= granule_count) {
+			return -EINVAL;
+		}
+
+		INFO("    mapping %s page: 0x%lx -> 0x%lx\n",
+			section_name, granule_pas[*next_granule_idx], va);
+		ret = app_xlat_map(
+			app_data,
+			va,
+			granule_pas[*next_granule_idx],
+			(MT_RW_DATA | MT_REALM | MT_AP_UNPRIV | MT_NG));
+		if (ret != 0) {
+			return ret;
+		}
+		*next_granule_idx += 1UL;
+		va += GRANULE_SIZE;
+	}
+	return 0;
+
+}
+
+static int app_shared_xlat_map(struct app_data_cfg *app_data,
+	       uintptr_t va,
+	       size_t *next_granule_idx,
+	       uintptr_t granule_pas[],
+	       size_t granule_count)
+{
+
+	size_t shared_page_idx = *next_granule_idx;
+	int ret;
+
+	ret = app_rw_page_xlat_map(app_data, va, GRANULE_SIZE, ".shared",
+	      next_granule_idx, granule_pas, granule_count);
+	if (ret != 0) {
+		return ret;
+	}
+	app_data->shared_page_pa = granule_pas[shared_page_idx];
+	return ret;
+}
+
+static int app_stack_xlat_map(struct app_data_cfg *app_data,
+	      uintptr_t va,
+	      size_t stack_size,
+	      size_t *next_granule_idx,
+	      uintptr_t granule_pas[],
+	      size_t granule_count)
+{
+	return app_rw_page_xlat_map(app_data, va, stack_size, ".stack",
+	      next_granule_idx, granule_pas, granule_count);
+}
+
+static int app_heap_xlat_map(struct app_data_cfg *app_data,
+	      uintptr_t va,
+	      size_t heap_size,
+	      size_t *next_granule_idx,
+	      uintptr_t granule_pas[],
+	      size_t granule_count)
+{
+	return app_rw_page_xlat_map(app_data, va, heap_size, ".heap",
+	      next_granule_idx, granule_pas, granule_count);
+}
+
+int app_init_data(struct app_data_cfg *app_data,
+		      unsigned long app_id,
+		      uintptr_t granule_pas[],
+		      size_t granule_count)
+{
+	struct app_header *app_header = NULL;
+	int ret = 0;
+	/* idx 0 and 1 is used for app_reg_ctx and for page table */;
+	size_t next_granule_idx = GRANULE_PA_IDX_COUNT;
+	uintptr_t stack_top;
+
+	INFO("Initialising app %lu\n", app_id);
+
+	if (app_data == NULL) {
+		ERROR("%s (%u): app data is NULL\n", __func__, __LINE__);
+		return -EINVAL;
+	}
+
+	if (app_get_header_ptr(app_id, &app_header) < 0) {
+		ERROR("%s (%u): failed to get header ptr for app_id %lu:\n",
+			__func__, __LINE__, app_id);
+		return -EINVAL;
+	}
+
+	if (granule_count < app_get_required_granule_count(app_id)) {
+		ERROR("%s (%u): Not enough RW pages: %lu instead of %lu\n",
+			__func__, __LINE__, granule_count, app_get_required_granule_count(app_id));
+		return -ENOMEM;
+	}
+
+	/* Initialise the app_data structure */
+	(void)memset(app_data, 0, sizeof(app_data[0]));
+
+	size_t stack_size = app_header->stack_page_count * GRANULE_SIZE;
+	size_t heap_size = app_header->heap_page_count * GRANULE_SIZE;
+
+	INFO("    stack_size = %lu\n", stack_size);
+	INFO("    heap_size = %lu\n", heap_size);
+
+	void *page_table = slot_map_app_pagetable(granule_pas[GRANULE_PA_IDX_APP_PAGE_TABLE]);
+
+	ret = init_app_translation(
+		app_id, app_data, granule_pas[GRANULE_PA_IDX_APP_PAGE_TABLE], page_table);
+	if (ret != 0) {
+		goto unmap_page_table;
+	}
+
+	/* Map the app_reg_ctx page to the dedicated transient region */
+	ret = app_xlat_map(app_data,
+			  APP_VA_START,
+			  granule_pas[GRANULE_PA_IDX_APP_REG_CTX],
+			  XLAT_NG_DATA_ATTR);
+	if (ret != 0) {
+		goto unmap_page_table;
+	}
+
+	ret = app_shared_xlat_map(app_data, app_data->el0_shared_page_va,
+		&next_granule_idx, granule_pas, granule_count);
+	if (ret != 0) {
+		goto unmap_page_table;
+	}
+	ret = app_stack_xlat_map(app_data, app_data->stack_buf_start_va, stack_size,
+		&next_granule_idx, granule_pas, granule_count);
+	if (ret != 0) {
+		goto unmap_page_table;
+	}
+	stack_top = app_data->stack_buf_start_va + stack_size;
+
+	app_data->heap_size = heap_size;
+	ret = app_heap_xlat_map(app_data, app_data->heap_va, app_data->heap_size,
+		&next_granule_idx, granule_pas, granule_count);
+	if (ret != 0) {
+		goto unmap_page_table;
+	}
+
+	/* Set up register initial values for entering the app */
+	app_data->entry_point = app_header->section_text_va;
+
+	app_data->app_reg_ctx_pa = granule_pas[GRANULE_PA_IDX_APP_REG_CTX];
+	struct app_reg_ctx *app_reg_ctx =
+		(struct app_reg_ctx *)slot_map_page_to_init(app_data->app_reg_ctx_pa);
+
+	if (app_reg_ctx == NULL) {
+		ERROR("%s (%u): Failed to map app_reg_ctx page\n", __func__, __LINE__);
+		goto unmap_page_table;
+	}
+	app_reg_ctx->app_ttbr1_el2 = app_data->mmu_config.ttbrx;
+	app_reg_ctx->sp_el0 = stack_top;
+	app_reg_ctx->pstate = SPSR_EL2_MODE_EL0t |
+				       SPSR_EL2_nRW_AARCH64 |
+				       SPSR_EL2_F_BIT |
+				       SPSR_EL2_I_BIT |
+				       SPSR_EL2_A_BIT |
+				       SPSR_EL2_D_BIT;
+	app_reg_ctx->pc = app_data->entry_point;
+	unmap_page(app_data->app_reg_ctx_pa, app_reg_ctx);
+unmap_page_table:
+	unmap_page(granule_pas[GRANULE_PA_IDX_APP_PAGE_TABLE], page_table);
+	return ret;
+}
+
+/* TODO:
+ * Collect the bss memory addresses allocated by the app rmm stub.
+ * Remove this once RMM memory allocation is sorted out.
+ */
+static void collect_app_bss(void)
+{
+}
+
+void app_framework_setup(void)
+{
+	size_t app_index;
+	struct app_header *app_header;
+	struct app_id_data *app_id_data;
+
+	/* coverity[misra_c_2012_rule_2_2_violation:SUPPRESS] */
+	collect_app_bss();
+
+	/* cppcheck-suppress unsignedLessThanZero
+	 * As app_index is unsigned, app_index < APP_COUNT cannot be true when
+	 * APP_COUNT is 0.
+	 */
+	/* coverity[no_effect:SUPPRESS] */
+	/* coverity[misra_c_2012_rule_14_3_violation:SUPPRESS] */
+	for (app_index = 0; app_index < APP_COUNT; ++app_index) {
+		/* coverity[deadcode:SUPPRESS] */
+		/* coverity[misra_c_2012_rule_14_3_violation:SUPPRESS] */
+		int ret;
+		uintptr_t bss_pa;
+
+		ret = app_get_header_ptr_at_index(app_index, &app_header);
+		assert(ret == 0);
+		app_id_data = &app_id_data_array[app_index];
+
+		struct xlat_mmap_region region_app_reg_ctx = MAP_REGION_TRANSIENT(
+					APP_VA_START,
+					GRANULE_SIZE,
+					PAGE_SIZE);
+		app_id_data->mm_regions_array[RMM_APP_APP_REG_CTX_MMAP_IDX] = region_app_reg_ctx;
+
+		struct xlat_mmap_region region_text = {
+			section_start_pa((uintptr_t)app_header, app_header->section_text_offset),
+			app_header->section_text_va,
+			app_header->section_text_size,
+			MT_CODE | MT_REALM | MT_EXEC_UNPRIV | MT_NG,
+			PAGE_SIZE
+		};
+		app_id_data->mm_regions_array[RMM_APP_TEXT_MMAP_IDX] = region_text;
+
+		struct xlat_mmap_region region_rodata = {
+			section_start_pa((uintptr_t)app_header, app_header->section_rodata_offset),
+			app_header->section_rodata_va,
+			app_header->section_rodata_size,
+			MT_RO_DATA | MT_REALM | MT_AP_UNPRIV | MT_NG,
+			PAGE_SIZE
+		};
+		app_id_data->mm_regions_array[RMM_APP_RODATA_MMAP_IDX] = region_rodata;
+
+		struct xlat_mmap_region region_data = {
+			section_start_pa((uintptr_t)app_header, app_header->section_data_offset),
+			app_header->section_data_va,
+			app_header->section_data_size,
+			(MT_RW_DATA | MT_REALM | MT_AP_UNPRIV | MT_NG),
+			PAGE_SIZE
+		};
+		app_id_data->mm_regions_array[RMM_APP_DATA_MMAP_IDX] = region_data;
+
+		ret = allocate_bss(app_header->app_id, app_header->section_bss_size, &bss_pa);
+		if (ret != 0) {
+			panic();
+		}
+		struct xlat_mmap_region region_bss = {
+			bss_pa,
+			app_header->section_bss_va,
+			app_header->section_bss_size,
+			(MT_RW_DATA | MT_REALM | MT_AP_UNPRIV | MT_NG),
+			PAGE_SIZE
+		};
+		app_id_data->mm_regions_array[RMM_APP_BSS_MMAP_IDX] = region_bss;
+
+		/* Pages for sections below are allocated per instantiation of
+		 * the app.
+		 */
+		struct xlat_mmap_region region_shared = MAP_REGION_TRANSIENT(
+			app_header->section_shared_va,
+			GRANULE_SIZE,
+			PAGE_SIZE);
+		app_id_data->mm_regions_array[RMM_APP_SHARED_IDX] = region_shared;
+		app_id_data->el0_shared_page_va = region_shared.base_va;
+
+		struct xlat_mmap_region region_heap = MAP_REGION_TRANSIENT(
+			/* Additional granule offset to base_va for heap underflow protection */
+			region_shared.base_va + region_shared.size + GRANULE_SIZE,
+			app_header->heap_page_count * GRANULE_SIZE,
+			PAGE_SIZE);
+		app_id_data->mm_regions_array[RMM_APP_HEAP_IDX] = region_heap;
+		app_id_data->heap_va = region_heap.base_va;
+
+		struct xlat_mmap_region region_stack = MAP_REGION_TRANSIENT(
+			/* Additional granule offset to base_va for stack overflow protection */
+			region_heap.base_va + region_heap.size + GRANULE_SIZE,
+			app_header->stack_page_count * GRANULE_SIZE,
+			PAGE_SIZE);
+		app_id_data->mm_regions_array[RMM_APP_STACK_IDX] = region_stack;
+		app_id_data->stack_buf_start_va = region_stack.base_va;
+
+		/* We are using here the same VA size that is configured for the high va
+		 * range, so that we can skip setting up other registers than ttbrx_el2
+		 * for mmu setup.
+		 */
+		ret = xlat_ctx_cfg_init(&app_id_data->app_va_xlat_ctx_cfg_base, VA_HIGH_REGION,
+					app_id_data->mm_regions_array,
+					RMM_APP_MMAP_REGION_COUNT,
+					XLAT_HIGH_VA_SIZE);
+		if (ret != 0) {
+			panic();
+		}
+	}
+}
+
+static uint64_t encode_heap_data(unsigned long heap_va, size_t heap_size)
+{
+	size_t heap_page_count = heap_size / GRANULE_SIZE;
+
+	assert((heap_va & HEAP_VA_MASK) == heap_va);
+	assert((heap_page_count & HEAP_PAGE_COUNT_MASK) == heap_page_count);
+	return heap_va | heap_page_count;
+}
+
+unsigned long app_run(struct app_data_cfg *app_data,
+			  unsigned long app_func_id,
+			  unsigned long arg0,
+			  unsigned long arg1,
+			  unsigned long arg2,
+			  unsigned long arg3)
+{
+	unsigned long retval;
+	unsigned long old_hcr_el2 = read_hcr_el2();
+	unsigned long old_elr_el2 = read_elr_el2();
+	unsigned long old_spsr_el2 = read_spsr_el2();
+
+	write_hcr_el2(HCR_EL2_INIT);
+
+	struct app_reg_ctx *app_reg_ctx =
+		(struct app_reg_ctx *)
+		slot_map_app_reg_ctx_page(app_data->app_reg_ctx_pa);
+
+	assert(app_reg_ctx != NULL);
+
+	write_elr_el2(app_reg_ctx->pc);
+	write_spsr_el2(app_reg_ctx->pstate);
+
+	assert(!app_data->app_entered);
+	app_data->app_entered = true;
+
+	app_reg_ctx->app_regs[0] = app_func_id;
+	app_reg_ctx->app_regs[1] = arg0;
+	app_reg_ctx->app_regs[2] = arg1;
+	app_reg_ctx->app_regs[3] = arg2;
+	app_reg_ctx->app_regs[4] = arg3;
+
+	while (true) {
+		int app_exception_code;
+		unsigned long esr;
+
+		app_exception_code = run_app(app_reg_ctx,
+			encode_heap_data(app_data->heap_va, app_data->heap_size));
+
+		app_reg_ctx->pc = read_elr_el2();
+		app_reg_ctx->pstate = read_spsr_el2();
+
+		esr = read_esr_el2();
+
+		if ((app_exception_code == ARM_EXCEPTION_SYNC_LEL) &&
+		    ((esr & MASK(ESR_EL2_EC)) == ESR_EL2_EC_SVC)) {
+			/* EL0 app called SVC as expected
+			 * In case of SVC, the Low 16 bits contain the imm16
+			 * value of the SVC instruction executed by the app.
+			 */
+			/* TODO: in future an app could be pre-empted by
+			 * interrupt or there could be other valid exceptions.
+			 */
+			uint16_t imm16 = (uint16_t)EXTRACT(ESR_EL2_ISS, esr);
+
+			if (imm16 == APP_EXIT_CALL) {
+				break;
+			}
+			if (imm16 == APP_SERVICE_CALL) {
+				app_reg_ctx->app_regs[0] =
+					call_app_service(app_reg_ctx->app_regs[0],
+							 app_data,
+							 app_reg_ctx->app_regs[1],
+							 app_reg_ctx->app_regs[2],
+							 app_reg_ctx->app_regs[3],
+							 app_reg_ctx->app_regs[4]);
+				continue;
+			}
+		}
+
+		unsigned long elr_el2 = read_elr_el2();
+
+		ERROR("Failed to return properly from the EL0 app\n");
+		ERROR("    ELR_EL2 = 0x%lx\n", elr_el2);
+
+		assert(false);
+	}
+
+	/* Return the value in X0 as EL0 app return value */
+	retval = app_reg_ctx->app_regs[0];
+
+	unmap_page(app_data->app_reg_ctx_pa, app_reg_ctx);
+
+	assert(app_data->app_entered);
+	app_data->app_entered = false;
+
+	write_hcr_el2(old_hcr_el2);
+	write_elr_el2(old_elr_el2);
+	write_spsr_el2(old_spsr_el2);
+	isb();
+
+	return retval;
+}
diff --git a/app/common/framework/src/aarch64/app_header.c b/app/common/framework/src/aarch64/app_header.c
new file mode 100644
index 0000000..e171d42
--- /dev/null
+++ b/app/common/framework/src/aarch64/app_header.c
@@ -0,0 +1,236 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <app.h>
+#include <app_header.h>
+#include <app_header_private.h>
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <stddef.h>
+#include <utils_def.h>
+
+static struct app_header *app_header_ptrs[APP_COUNT];
+static uint64_t rmm_start_address;
+static uint64_t rmm_core_start_address;
+
+/* The function is called from assembly */
+void app_save_rmm_entry_info(uint64_t rmm_start, uint64_t rmm_core_start);
+
+void app_save_rmm_entry_info(uint64_t rmm_start, uint64_t rmm_core_start)
+{
+	if ((rmm_start == 0U) || (rmm_core_start < rmm_start)) {
+		panic();
+	}
+	rmm_start_address = rmm_start;
+	rmm_core_start_address = rmm_core_start;
+}
+
+uint64_t app_get_rmm_start(void)
+{
+	return rmm_start_address;
+}
+
+int app_get_index(unsigned long app_id, size_t *app_index)
+{
+	size_t i;
+
+	assert(app_index != NULL);
+	/* cppcheck-suppress unsignedLessThanZero
+	 * As i is unsigned, i < APP_COUNT cannot be true when APP_COUNT is 0.
+	 */
+	/* coverity[no_effect:SUPPRESS] */
+	/* coverity[misra_c_2012_rule_14_3_violation:SUPPRESS] */
+	for (i = 0; i < APP_COUNT; ++i) {
+		/* coverity[deadcode:SUPPRESS] */
+		/* coverity[misra_c_2012_rule_14_3_violation:SUPPRESS] */
+		if (app_header_ptrs[i]->app_id == app_id) {
+			*app_index = i;
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+int app_get_header_ptr(unsigned long app_id, struct app_header **app_header)
+{
+	size_t app_index;
+
+	assert(app_header != NULL);
+	if (app_get_index(app_id, &app_index) < 0) {
+		return -EINVAL;
+	}
+
+	*app_header = app_header_ptrs[app_index];
+
+	return 0;
+}
+
+int app_get_header_ptr_at_index(unsigned long app_index, struct app_header **app_header)
+{
+	/* cppcheck-suppress unsignedPositive
+	 * As app_index is unsigned, app_index >= APP_COUNT is always true if
+	 * APP_COUNT is zero.
+	 */
+	/* coverity[no_effect:SUPPRESS] */
+	/* coverity[misra_c_2012_rule_14_3_violation:SUPPRESS] */
+	if (app_index >= APP_COUNT) {
+		return -EINVAL;
+	}
+	/* coverity[deadcode:SUPPRESS] */
+	/* coverity[misra_c_2012_rule_14_3_violation:SUPPRESS] */
+	*app_header = app_header_ptrs[app_index];
+	return 0;
+}
+
+size_t app_get_required_granule_count_from_header(struct app_header *app_header)
+{
+	assert(app_header != NULL);
+
+	return app_header->stack_page_count +
+	       app_header->heap_page_count +
+	       1U + /* Shared page */
+	       1U + /* App register context */
+	       1U;  /* Page Table (assuming single page) */
+}
+
+static void dump_header(struct app_header *app_header)
+{
+
+	INFO("    app_header_magic: #1=%016lx, #2=%016lx\n",
+		app_header->app_header_magic, app_header->app_header_magic2);
+	if ((app_header->app_name[APP_NAME_BUF_SIZE - 1U] == '\0')) {
+		INFO("    app_name               =   %s\n", app_header->app_name);
+	} else {
+		INFO("    app_name INVALID\n");
+	}
+	INFO("    app_header_version     =   0x%16x\n",
+		app_header->app_header_version);
+	INFO("    app_id                 =     %16d\n", app_header->app_id);
+	INFO("    app_len                =   0x%16lx\n", app_header->app_len);
+	INFO("    required granule_count =     %16lu\n\n",
+		app_get_required_granule_count_from_header(app_header));
+
+	INFO("    section  |   offset |               va |     size\n");
+	INFO("    ---------|----------|------------------|---------\n");
+	INFO("    text     | %8lx | %16lx | %8lx\n",
+		app_header->section_text_offset,
+		app_header->section_text_va,
+		app_header->section_text_size);
+	INFO("    rodata   | %8lx | %16lx | %8lx\n",
+		app_header->section_rodata_offset,
+		app_header->section_rodata_va,
+		app_header->section_rodata_size);
+	INFO("    data     | %8lx | %16lx | %8lx\n",
+		app_header->section_data_offset,
+		app_header->section_data_va,
+		app_header->section_data_size);
+	INFO("    bss      |      N/A | %16lx | %8lx\n",
+		app_header->section_bss_va,
+		app_header->section_bss_size);
+	INFO("    shared   |      N/A | %16lx | %8lx\n",
+		app_header->section_shared_va,
+		GRANULE_SIZE);
+	INFO("    stack    |      N/A |              N/A | %8lx\n",
+		app_header->stack_page_count * GRANULE_SIZE);
+	INFO("    heap     |      N/A |              N/A | %8lx\n\n",
+		app_header->heap_page_count * GRANULE_SIZE);
+}
+
+static int sanity_check_header(struct app_header *app_header)
+{
+	if (
+		/* Version */
+		(app_header->app_header_version !=
+			(((uint32_t)HEADER_VERSION_MAJOR << 16U) | HEADER_VERSION_MINOR)) ||
+		/* App name is NULL terminated */
+		(app_header->app_name[APP_NAME_BUF_SIZE - 1U] != '\0') ||
+		/* Alignments */
+		(!GRANULE_ALIGNED(app_header->app_len)) ||
+		(!GRANULE_ALIGNED(app_header->section_text_offset)) ||
+		(!GRANULE_ALIGNED(app_header->section_text_va)) ||
+		(!GRANULE_ALIGNED(app_header->section_text_size)) ||
+		(!GRANULE_ALIGNED(app_header->section_rodata_offset)) ||
+		(!GRANULE_ALIGNED(app_header->section_rodata_va)) ||
+		(!GRANULE_ALIGNED(app_header->section_rodata_size)) ||
+		(!GRANULE_ALIGNED(app_header->section_data_offset)) ||
+		(!GRANULE_ALIGNED(app_header->section_data_va)) ||
+		(!GRANULE_ALIGNED(app_header->section_data_size)) ||
+		(!GRANULE_ALIGNED(app_header->section_bss_va)) ||
+		(!GRANULE_ALIGNED(app_header->section_bss_size)) ||
+		(!GRANULE_ALIGNED(app_header->section_shared_va)) ||
+		/* Magic */
+		(app_header->app_header_magic != APP_HEADER_MAGIC) ||
+		(app_header->app_header_magic2 != APP_HEADER_MAGIC) ||
+		/* Section overlap / order */
+		((app_header->section_text_offset + app_header->section_text_size) !=
+			app_header->section_rodata_offset) ||
+		((app_header->section_rodata_offset + app_header->section_rodata_size) !=
+			app_header->section_data_offset) ||
+		/* App va space is expected to be contiguous */
+		((app_header->section_text_va + app_header->section_text_size) !=
+			app_header->section_rodata_va) ||
+		((app_header->section_rodata_va + app_header->section_rodata_size) !=
+			app_header->section_data_va) ||
+		((app_header->section_data_va + app_header->section_data_size) !=
+			app_header->section_bss_va) ||
+		((app_header->section_bss_va + app_header->section_bss_size) !=
+			app_header->section_shared_va) ||
+		/* app bin is long enough */
+		((app_header->section_data_offset + app_header->section_data_size +
+			APP_HEADER_SIZE) != app_header->app_len)
+	) {
+		return 1;
+	}
+	return 0;
+}
+
+void app_info_setup(void)
+{
+	unsigned long i;
+	struct app_header *app_header = (struct app_header *)rmm_start_address;
+
+	INFO("Loading apps. RMM Core start address: 0x%lx\n", rmm_core_start_address);
+
+	/* cppcheck-suppress unsignedLessThanZero
+	 * As i is unsigned, i < APP_COUNT cannot be true when APP_COUNT is 0.
+	 */
+	/* coverity[no_effect:SUPPRESS] */
+	/* coverity[misra_c_2012_rule_14_3_violation:SUPPRESS] */
+	for (i = 0; i < APP_COUNT; ++i) {
+		/* coverity[deadcode:SUPPRESS] */
+		/* coverity[misra_c_2012_rule_14_3_violation:SUPPRESS] */
+		if ((uintptr_t)app_header >= (uintptr_t)rmm_core_start_address) {
+			ERROR("App header overlaps with RMM binary\n");
+			panic();
+		}
+
+		INFO("App header @%lu (at 0x%lx):\n", i, (uintptr_t)app_header);
+		dump_header(app_header);
+
+		if (sanity_check_header(app_header) != 0) {
+			ERROR("App header sanity check failed\n");
+			panic();
+		}
+
+		app_header_ptrs[i] = app_header;
+
+		/* Check overflow */
+		if ((UINT64_MAX - app_header->app_len) < (uintptr_t)app_header) {
+			panic();
+		}
+		app_header = (struct app_header *)&(((char *)app_header)[app_header->app_len]);
+	}
+
+	if ((uintptr_t)app_header != (uintptr_t)rmm_core_start_address) {
+		/* There are extra bytes after the last header before the
+		 * rmm_entry function. Maybe there were more apps provided to
+		 * the bundle app than APP_COUNT?
+		 */
+		ERROR("Unexpected bytes between last app and RMM Core start\n");
+		panic();
+	}
+}
diff --git a/app/common/framework/src/app_header_private.h b/app/common/framework/src/app_header_private.h
new file mode 100644
index 0000000..de51c06
--- /dev/null
+++ b/app/common/framework/src/app_header_private.h
@@ -0,0 +1,66 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef APP_HEADER_PRIVATE_H
+#define APP_HEADER_PRIVATE_H
+
+#ifndef __ASSEMBLER__
+#include <app_header_structures.h>
+#include <stddef.h>
+#include <stdint.h>
+#endif
+
+#define APP_HEADER_MAGIC	0x000E10ABB4EAD000UL  /* El0 APP HEAD */
+
+#define HEADER_VERSION_MAJOR	0U
+#define HEADER_VERSION_MINOR	1U
+#define HEADER_VERSION		((HEADER_VERSION_MAJOR << 16) | HEADER_VERSION_MINOR)
+
+#ifndef __ASSEMBLER__
+
+/*
+ * Get the index of the app from the app_id.
+ *
+ * Apps are indexed starting from 0, in the same order they appear in the RMM
+ * image.
+ *
+ * Arguments:
+ *	- app_id: The id of the app.
+ *	- app_idx: Out parameter, the index of the app with `app_id`.
+ *
+ * Return:
+ *	- 0 on success or a negative POSIX error otherwise.
+ */
+int app_get_index(unsigned long app_id, size_t *app_index);
+
+/*
+ * Return a pointer to the app_header structure of an app of an app_id.
+ *
+ * Arguments:
+ *	- app_id: The id of the app.
+ *	- app_header: Out parameter, pointer to the app header of the `app_id`.
+ *
+ * Return:
+ *	- 0 on success or a negative POSIX error otherwise.
+ */
+int app_get_header_ptr(unsigned long app_id, struct app_header **app_header);
+
+/*
+ * Get the number of instance specific pages that are necessary for the app.
+ *
+ * This can be used to get the number of instance specific granules necessary
+ * to instantiate an application that is identified by the app_header.
+ *
+ * Arguments:
+ *	- app_header: pointer to the app header.
+ *
+ * Return:
+ *	- The number of granules necessary.
+ */
+size_t app_get_required_granule_count_from_header(struct app_header *app_header);
+
+#endif
+
+#endif /* APP_HEADER_PRIVATE_H */
diff --git a/app/common/framework/src/fake_host/app.c b/app/common/framework/src/fake_host/app.c
new file mode 100644
index 0000000..b1a4aab
--- /dev/null
+++ b/app/common/framework/src/fake_host/app.c
@@ -0,0 +1,278 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <app.h>
+#include <app_common.h>
+#include <app_common_arch.h>
+#include <app_header.h>
+#include <app_header_private.h>
+#include <app_services.h>
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <unistd.h>
+
+/*
+ * TODO: The host application uses a single shared page that is the same for all
+ *       the application instances. The best location for this buffer would be
+ *       in struct app_data_cfg, but it doesn't fit there as a size limit on
+ *       the `struct rec` size which contains attestation helper app's data.
+ *       Remove this TODO once the aarch64 implementation uses a single shared
+ *       page as well.
+ */
+static char shared_page[GRANULE_SIZE];
+
+/* In case APP_COUNT is 0 or 1 then set a meaningful array size to prevent array
+ * subscript <unknown> is outside array bounds warning
+ */
+/* NOLINTNEXTLINE(misc-redundant-expression) */
+static struct app_process_data app_process_datas[(APP_COUNT < 2U)?(2U):(APP_COUNT)];
+static size_t initialised_app_process_data_count;
+
+void int_to_str(int value, char *buf, size_t buf_size)
+{
+	char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
+	char reverse_buf[sizeof(int) * 3 + 2];
+	char *c = reverse_buf;
+	size_t buf_idx = 0;
+
+	if (buf_size < sizeof(reverse_buf)) {
+		if (buf_size > 0U) {
+			buf[0] = '\0';
+		}
+		return;
+	}
+
+	if (value < 0) {
+		buf[buf_idx++] = '-';
+		value = -value;
+	}
+	do {
+		*c = digits[value%10];
+		value = value / 10;
+		++c;
+	} while (value != 0);
+	do {
+		--c;
+		buf[buf_idx] = *c;
+		++buf_idx;
+	} while (c != reverse_buf);
+	buf[buf_idx] = '\0';
+}
+
+static void start_app_process(unsigned long app_id, int read_fd, int write_fd)
+{
+	int ret;
+	struct app_header *app_header;
+
+	ret = app_get_header_ptr(app_id, &app_header);
+	assert(ret == 0);
+	char s_read_fd[sizeof(int) * 3 + 2];
+	char s_write_fd[sizeof(int) * 3 + 2];
+
+	int_to_str(read_fd, s_read_fd, sizeof(s_read_fd));
+	int_to_str(write_fd, s_write_fd, sizeof(s_write_fd));
+	char *args[] = {app_header->app_elf_name, s_read_fd, s_write_fd, NULL};
+
+	ret = execv(app_header->app_elf_name, args);
+	if (ret == -1) {
+		perror("Failed to create child process");
+		exit(1);
+	}
+}
+
+static struct app_process_data *create_app_process(unsigned long app_id)
+{
+	/* To prevent array subscript <unknown> is outside array bounds warning */
+	/* cppcheck-suppress unsignedPositive
+	 * As initialised_app_process_data_count is unsigned,
+	 * initialised_app_process_data_count >= APP_COUNT is always true if
+	 * APP_COUNT is zero.
+	 */
+	if ((initialised_app_process_data_count == 0UL) &&
+	    (initialised_app_process_data_count >= APP_COUNT)) {
+		return NULL;
+	}
+
+	struct app_process_data *ret = &app_process_datas[initialised_app_process_data_count];
+
+	int fds_rmm_to_app_process[2];
+	int fds_app_process_to_rmm[2];
+
+	if (pipe(fds_rmm_to_app_process) == -1) {
+		return NULL;
+	}
+	if (pipe(fds_app_process_to_rmm) == -1) {
+		return NULL;
+	}
+
+	ret->pid = fork();
+
+	if (ret->pid == 0) {
+		/* We are in the child process, close the unnecessary fds and
+		 * execv into the app.
+		 */
+		close(fds_rmm_to_app_process[1]);
+		close(fds_app_process_to_rmm[0]);
+		start_app_process(app_id, fds_rmm_to_app_process[0], fds_app_process_to_rmm[1]);
+		/* The function above should never return */
+		assert(false);
+	}
+
+	close(fds_rmm_to_app_process[0]);
+	close(fds_app_process_to_rmm[1]);
+	ret->fd_rmm_to_app_process = fds_rmm_to_app_process[1];
+	ret->fd_app_process_to_rmm = fds_app_process_to_rmm[0];
+	ret->app_id = app_id;
+
+	++initialised_app_process_data_count;
+	return ret;
+}
+
+static struct app_process_data *get_app_process_data(unsigned long app_id)
+{
+	size_t i;
+
+	/* To prevent array subscript <unknown> is outside array bounds warning */
+	/* cppcheck-suppress unsignedPositive
+	 * As initialised_app_process_data_count is unsigned,
+	 * initialised_app_process_data_count >= APP_COUNT is always true if
+	 * APP_COUNT is zero.
+	 */
+	if ((initialised_app_process_data_count == 0UL) &&
+	    (initialised_app_process_data_count >= APP_COUNT)) {
+		return NULL;
+	}
+
+	for (i = 0; i < initialised_app_process_data_count; ++i) {
+		if (app_process_datas[i].app_id == app_id) {
+			return &app_process_datas[i];
+		}
+	}
+
+	return NULL;
+}
+
+void app_framework_setup(void)
+{
+}
+
+int app_init_data(struct app_data_cfg *app_data,
+		      unsigned long app_id,
+		      uintptr_t granule_pas[],
+		      size_t granule_count)
+{
+	struct app_process_data *app_process_data;
+	unsigned long command;
+
+	(void)granule_pas;
+	(void)granule_count;
+
+	app_process_data = get_app_process_data(app_id);
+	if (app_process_data == NULL) {
+		app_process_data = create_app_process(app_id);
+		if (app_process_data == NULL) {
+			return -EINVAL;
+		}
+	}
+
+	/* Create the thread for this app instance */
+	command = CREATE_NEW_APP_INSTANCE;
+
+	WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process, &command, sizeof(command));
+	READ_OR_EXIT(app_process_data->fd_app_process_to_rmm, &app_data->thread_id,
+		sizeof(app_data->thread_id));
+	app_data->el2_shared_page = NULL;
+	app_data->app_id = app_id;
+	return 0;
+}
+
+void app_map_shared_page(struct app_data_cfg *app_data)
+{
+	assert(app_data->el2_shared_page == NULL);
+	app_data->el2_shared_page = shared_page;
+}
+
+void app_unmap_shared_page(struct app_data_cfg *app_data)
+{
+	assert(app_data->el2_shared_page != NULL);
+	app_data->el2_shared_page = NULL;
+}
+
+unsigned long app_run(struct app_data_cfg *app_data,
+		      unsigned long app_func_id,
+		      unsigned long arg0,
+		      unsigned long arg1,
+		      unsigned long arg2,
+		      unsigned long arg3)
+{
+	unsigned long retval;
+	struct app_process_data *app_process_data;
+
+	app_process_data = get_app_process_data(app_data->app_id);
+	if (app_process_data == NULL) {
+		exit(1);
+	}
+
+	const unsigned long command = CALL_APP_INSTANCE;
+
+	WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process, &command, sizeof(command));
+	WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process, &app_data->thread_id,
+		sizeof(app_data->thread_id));
+
+	unsigned long bytes_to_forward =
+		5 * sizeof(unsigned long) +
+		sizeof(shared_page);
+
+	WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process, &bytes_to_forward,
+		sizeof(bytes_to_forward));
+	WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process, &app_func_id, sizeof(app_func_id));
+	WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process, &arg0, sizeof(arg0));
+	WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process, &arg1, sizeof(arg1));
+	WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process, &arg2, sizeof(arg2));
+	WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process, &arg3, sizeof(arg3));
+	WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process, shared_page, GRANULE_SIZE);
+
+	unsigned long reason;
+
+	while (true) {
+		READ_OR_EXIT(app_process_data->fd_app_process_to_rmm, &reason, sizeof(reason));
+		if (reason == APP_EXIT_CALL) {
+			break;
+		}
+
+		/* assume service call */
+		unsigned long service_index;
+
+		READ_OR_EXIT(app_process_data->fd_app_process_to_rmm, &service_index,
+			sizeof(service_index));
+		READ_OR_EXIT(app_process_data->fd_app_process_to_rmm, &arg0, sizeof(arg0));
+		READ_OR_EXIT(app_process_data->fd_app_process_to_rmm, &arg1, sizeof(arg1));
+		READ_OR_EXIT(app_process_data->fd_app_process_to_rmm, &arg2, sizeof(arg2));
+		READ_OR_EXIT(app_process_data->fd_app_process_to_rmm, &arg3, sizeof(arg3));
+		READ_OR_EXIT(app_process_data->fd_app_process_to_rmm, shared_page, GRANULE_SIZE);
+
+		retval = call_app_service(service_index, app_data, arg0, arg1, arg2, arg3);
+
+		bytes_to_forward =
+			sizeof(unsigned long) +
+			sizeof(shared_page);
+
+		WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process, &command, sizeof(command));
+		WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process, &app_data->thread_id,
+			sizeof(app_data->thread_id));
+		WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process, &bytes_to_forward,
+			sizeof(bytes_to_forward));
+
+		WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process, &retval, sizeof(retval));
+		WRITE_OR_EXIT(app_process_data->fd_rmm_to_app_process, shared_page, GRANULE_SIZE);
+	}
+
+	READ_OR_EXIT(app_process_data->fd_app_process_to_rmm, &retval, sizeof(retval));
+	READ_OR_EXIT(app_process_data->fd_app_process_to_rmm, shared_page, GRANULE_SIZE);
+
+	return retval;
+}
diff --git a/app/common/framework/src/fake_host/app_header.c b/app/common/framework/src/fake_host/app_header.c
new file mode 100644
index 0000000..2b71fa4
--- /dev/null
+++ b/app/common/framework/src/fake_host/app_header.c
@@ -0,0 +1,66 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <app.h>
+#include <app_header.h>
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <stddef.h>
+
+static struct app_header app_headers[APP_COUNT];
+
+void app_info_setup(void)
+{
+}
+
+uint64_t app_get_rmm_start(void)
+{
+	return 0UL;
+}
+
+int app_get_index(unsigned long app_id, size_t *app_index)
+{
+	size_t i;
+
+	for (i = 0; i < APP_COUNT; ++i) {
+		if (app_headers[i].app_id == app_id) {
+			*app_index = i;
+			return 0;
+		}
+	}
+	ERROR("Failed to get index of app id %lu\n", app_id);
+	return -EINVAL;
+}
+
+int app_get_header_ptr_at_index(unsigned long app_index, struct app_header **app_header)
+{
+	if (app_index >= APP_COUNT) {
+		return -EINVAL;
+	}
+	*app_header = &app_headers[app_index];
+	return 0;
+}
+
+int app_get_header_ptr(unsigned long app_id, struct app_header **app_header)
+{
+
+	size_t app_index = 0;
+
+	if (app_get_index(app_id, &app_index) != 0) {
+		return -EINVAL;
+	}
+
+	*app_header = &app_headers[app_index];
+
+	return 0;
+}
+
+size_t app_get_required_granule_count_from_header(struct app_header *app_header)
+{
+	(void)app_header;
+	return 0;
+}
diff --git a/app/common/framework/src/no_app_support/app.c b/app/common/framework/src/no_app_support/app.c
new file mode 100644
index 0000000..9af052e
--- /dev/null
+++ b/app/common/framework/src/no_app_support/app.c
@@ -0,0 +1,55 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <app.h>
+#include <app_header.h>
+#include <app_services.h>
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <unistd.h>
+
+void app_framework_setup(void)
+{
+}
+
+int app_init_data(struct app_data_cfg *app_data,
+		      unsigned long app_id,
+		      uintptr_t granule_pas[],
+		      size_t granule_count)
+{
+	(void)app_data;
+	(void)app_id;
+	(void)granule_pas;
+	(void)granule_count;
+	return 0;
+}
+
+unsigned long app_run(struct app_data_cfg *app_data,
+		      unsigned long app_func_id,
+		      unsigned long arg0,
+		      unsigned long arg1,
+		      unsigned long arg2,
+		      unsigned long arg3)
+{
+	(void)app_data;
+	(void)app_func_id;
+	(void)arg0;
+	(void)arg1;
+	(void)arg2;
+	(void)arg3;
+	return 0;
+}
+
+void app_map_shared_page(struct app_data_cfg *app_data)
+{
+	(void)app_data;
+}
+
+void app_unmap_shared_page(struct app_data_cfg *app_data)
+{
+	(void)app_data;
+}
diff --git a/app/common/framework/src/no_app_support/app_header.c b/app/common/framework/src/no_app_support/app_header.c
new file mode 100644
index 0000000..75f77f9
--- /dev/null
+++ b/app/common/framework/src/no_app_support/app_header.c
@@ -0,0 +1,48 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <app.h>
+#include <app_header.h>
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <stddef.h>
+
+void app_info_setup(void)
+{
+}
+
+uint64_t app_get_rmm_start(void)
+{
+	return 0UL;
+}
+
+int app_get_index(unsigned long app_id, size_t *app_index)
+{
+	(void)app_id;
+	(void)app_index;
+	return -EINVAL;
+}
+
+int app_get_header_ptr_at_index(unsigned long app_index, struct app_header **app_header)
+{
+	(void)app_index;
+	(void)app_header;
+	return -EINVAL;
+}
+
+int app_get_header_ptr(unsigned long app_id, struct app_header **app_header)
+{
+	(void)app_id;
+	(void)app_header;
+	return -EINVAL;
+}
+
+size_t app_get_required_granule_count_from_header(struct app_header *app_header)
+{
+	(void)app_header;
+	return 0;
+}
diff --git a/app/common/include/app_common.h b/app/common/include/app_common.h
new file mode 100644
index 0000000..b087c2d
--- /dev/null
+++ b/app/common/include/app_common.h
@@ -0,0 +1,28 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef APP_COMMON_H
+#define APP_COMMON_H
+
+#ifndef __ASSEMBLER__
+#include <stddef.h>
+#include <stdint.h>
+#endif /* __ASSEMBLER__ */
+#include <utils_def.h>
+
+/* The heap properties are encoded in a single unsigned long.
+ * It is assumed that the heap_va is granule aligned, so at
+ * least the lower 12 bits are always zero.
+ */
+#define HEAP_PAGE_COUNT_MASK ((1UL << 12U) - 1UL)
+#define HEAP_VA_MASK (~HEAP_PAGE_COUNT_MASK)
+
+#define APP_EXIT_CALL		UL(23)
+#define APP_SERVICE_CALL	UL(47)
+
+#define APP_SERVICE_COUNT			16U
+#define APP_SERVICE_PRINT			2U
+
+#endif /* APP_COMMON_H */
diff --git a/app/common/include/fake_host/app_common_arch.h b/app/common/include/fake_host/app_common_arch.h
new file mode 100644
index 0000000..78f5c73
--- /dev/null
+++ b/app/common/include/fake_host/app_common_arch.h
@@ -0,0 +1,40 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef APP_COMMON_ARCH_H
+#define APP_COMMON_ARCH_H
+
+#define CREATE_NEW_APP_INSTANCE		13
+#define CALL_APP_INSTANCE		21
+
+#define OP_OR_EXIT(op, fd, buf, count, error_on_eof)                                               \
+	do {                                                                                       \
+		size_t count_processed = 0;                                                        \
+		size_t op_or_exit_ret;                                                             \
+		while (1) {                                                                        \
+			op_or_exit_ret = op(fd, &((char *)buf)[count_processed],                   \
+					    count - count_processed);                              \
+			if (op_or_exit_ret > 0) {                                                  \
+				count_processed += op_or_exit_ret;                                 \
+				if (count_processed == count) {                                    \
+					break;                                                     \
+				}                                                                  \
+			} else {                                                                   \
+				if (error_on_eof) {                                                \
+					ERROR("ERROR: Failed to " #op " %lu bytes at %s:%d\n"      \
+						, count, __func__, __LINE__);                      \
+					exit(1);                                                   \
+				} else {                                                           \
+					exit(0);                                                   \
+				}                                                                  \
+			}                                                                          \
+		}                                                                                  \
+	} while (0)
+
+#define READ_OR_EXIT_NOERROR(fd, buf, count) OP_OR_EXIT(read, fd, buf, count, false)
+#define READ_OR_EXIT(fd, buf, count) OP_OR_EXIT(read, fd, buf, count, true)
+#define WRITE_OR_EXIT(fd, buf, count) OP_OR_EXIT(write, fd, buf, count, true)
+
+#endif /* APP_COMMON_ARCH_H */
diff --git a/app/common/rmm_svc/CMakeLists.txt b/app/common/rmm_svc/CMakeLists.txt
new file mode 100644
index 0000000..5576e77
--- /dev/null
+++ b/app/common/rmm_svc/CMakeLists.txt
@@ -0,0 +1,27 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+add_library(rmm-app-cmn-svc)
+
+target_link_libraries(rmm-app-cmn-svc
+    PRIVATE
+        rmm-lib-arch
+        rmm-lib-console
+        rmm-lib-debug
+        rmm-lib-rmm_el3_ifc
+        rmm-lib-slot_buf
+        rmm-lib-xlat
+        rmm-el2-stub)
+
+target_include_directories(rmm-app-cmn-svc
+    PUBLIC "include"
+    PUBLIC "../../common/include")
+
+target_sources(rmm-app-cmn-svc
+    PRIVATE "src/app_services.c"
+    )
+
+list(APPEND RMM_EL2_STUB_LIBRARIES "rmm-app-cmn-svc")
+set(RMM_EL2_STUB_LIBRARIES ${RMM_EL2_STUB_LIBRARIES} PARENT_SCOPE)
diff --git a/app/common/rmm_svc/include/app_services.h b/app/common/rmm_svc/include/app_services.h
new file mode 100644
index 0000000..c154a96
--- /dev/null
+++ b/app/common/rmm_svc/include/app_services.h
@@ -0,0 +1,19 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <app_fw_structures.h>
+
+#ifndef APP_SERVICES_H
+#define APP_SERVICES_H
+
+/* Services management */
+uint64_t call_app_service(unsigned long service_id,
+			  struct app_data_cfg *app_data,
+			  unsigned long arg0,
+			  unsigned long arg1,
+			  unsigned long arg2,
+			  unsigned long arg3);
+
+#endif /* APP_SERVICES_H */
diff --git a/app/common/rmm_svc/src/app_services.c b/app/common/rmm_svc/src/app_services.c
new file mode 100644
index 0000000..17c4f4b
--- /dev/null
+++ b/app/common/rmm_svc/src/app_services.c
@@ -0,0 +1,72 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <app.h>
+#include <app_common.h>
+#include <app_services.h>
+#include <assert.h>
+#include <console.h>
+
+typedef uint64_t (*app_service_func)(struct app_data_cfg *app_data,
+			  unsigned long arg0,
+			  unsigned long arg1,
+			  unsigned long arg2,
+			  unsigned long arg3);
+
+static uint64_t app_service_print(struct app_data_cfg *app_data,
+			  unsigned long arg0,
+			  unsigned long arg1,
+			  unsigned long arg2,
+			  unsigned long arg3)
+{
+	size_t len = arg0;
+	size_t i;
+	size_t offset = 0;
+	char print_buf[4];
+
+	(void)arg1;
+	(void)arg2;
+	(void)arg3;
+
+	while (len > 0U) {
+		char *shared_page;
+		size_t to_print = len;
+
+		if (to_print > sizeof(print_buf)) {
+			to_print = sizeof(print_buf);
+		}
+		shared_page = app_data->el2_shared_page;
+		assert(shared_page != NULL);
+		(void)memcpy(print_buf, &shared_page[offset], to_print);
+		for (i = 0; i < to_print; ++i) {
+			(void)console_putc((int)print_buf[i]);
+		}
+		offset += to_print;
+		len -= to_print;
+	}
+	return 0;
+}
+
+static app_service_func service_functions[APP_SERVICE_COUNT] = {
+	[APP_SERVICE_PRINT] = app_service_print};
+
+uint64_t call_app_service(unsigned long service_id,
+			  struct app_data_cfg *app_data,
+			  unsigned long arg0,
+			  unsigned long arg1,
+			  unsigned long arg2,
+			  unsigned long arg3)
+{
+	(void)arg0;
+	(void)arg1;
+	(void)arg2;
+	(void)arg3;
+
+	assert(service_id < APP_SERVICE_COUNT);
+	assert(service_functions[service_id] != NULL);
+
+	return service_functions[service_id](app_data, arg0, arg1, arg2, arg3);
+}
+
diff --git a/app/gen_app_bin.py b/app/gen_app_bin.py
new file mode 100755
index 0000000..7c1ac2a
--- /dev/null
+++ b/app/gen_app_bin.py
@@ -0,0 +1,360 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+"""
+Script for creating a bin file from the compiled app's elf file.
+
+The name of the elf sections that are expected and added in the output binary
+are hardcoded in the script. The resulting binary consists of a header which is
+page in size and content of selected sections which are appended after the
+header.
+```
++----------------------+
+|                      |
+|       Header         |
+|                      |
++----------------------+
+|                      |
+| Content of section 1 |
+|                      |
++----------------------+
+|                      |
+           ...
+|                      |
++----------------------+
+|                      |
+|Content of section n  |
+|                      |
++----------------------+
+```
+"""
+
+from argparse import ArgumentParser
+from collections import namedtuple
+import logging
+import struct
+
+
+from elftools.elf.elffile import ELFFile
+
+INDENTATION = "\t"
+APP_HEADER_MAGIC = 0x000E10ABB4EAD000  # El0 APP HEAD
+HEADER_VERSION_MAJOR = 0
+HEADER_VERSION_MINOR = 1
+# TODO: get page size from command line
+PAGE_SIZE = 4096
+APP_HEADER_RESERVED_BYTES = 10 * 8
+APP_NAME_BUF_SIZE = 32
+
+HEADER_VERSION = (HEADER_VERSION_MAJOR << 16) | HEADER_VERSION_MINOR
+
+# The header format needs to match the header in
+# "app/common/framework/include/fake_host/app_header_structures.h" file as defined in
+# 'struct el0_app_header'
+BIN_HEADER_FORMAT = "".join(
+    [
+        "<",  # little endian
+        "Q",  # uint64_t padding;
+        "Q",  # uint64_t app_header_magic;
+        "L",  # uint32_t app_header_version;
+        f"{APP_NAME_BUF_SIZE}s" # const char app_name[APP_NAME_BUF_SIZE];
+        "L",  # uint32_t app_id;
+        "Q",  # uint64_t app_len; /* including header */
+        "Q",  # uintptr_t section_text_offset;
+        "Q",  # uintptr_t section_text_va;
+        "Q",  # size_t section_text_size;
+        "Q",  # uintptr_t section_rodata_offset;
+        "Q",  # uintptr_t section_rodata_va;
+        "Q",  # size_t section_rodata_size;
+        "Q",  # uintptr_t section_data_offset;
+        "Q",  # uintptr_t section_data_va;
+        "Q",  # size_t section_data_size;
+        "Q",  # uintptr_t section_bss_va ;
+        "Q",  # size_t section_bss_size;
+        "Q",  # section_shared_va;
+        "Q",  # size_t stack_page_count;
+        "Q",  # size_t heap_page_count;
+        f"{APP_HEADER_RESERVED_BYTES}s"  # reserved
+        "Q",  # uint64_t app_header_magic2;
+    ]
+)
+
+SectionParams = namedtuple("SectionParams", ["name", "emit"])
+SectionData = namedtuple("SectionData", ["params", "vma", "size", "data"])
+
+
+def get_section(sections, idx, name):
+    """Returns the section in the sections list at the given index.
+
+    The function makes sure that the section at the specified index has the
+    specified name.
+    """
+    if sections[idx].params.name != name:
+        logging.error(
+            f"At idx {idx} section name '{sections[idx].params.name}' doesn't match '{name}'"
+        )
+        assert False
+    return sections[idx]
+
+def get_app_name_bytes(app_name):
+    if len(app_name) >= (APP_NAME_BUF_SIZE):
+        app_name = app_name[:APP_NAME_BUF_SIZE -1]
+    b_appname = app_name.encode()
+    assert(len(b_appname) < APP_NAME_BUF_SIZE)
+    return bytes().join([b_appname, bytes([0] * (APP_NAME_BUF_SIZE - len(b_appname)))])
+
+def emit_bin_file_header(out_bin_file, app_name, app_id, app_len, stack_page_count, heap_page_count, sections):
+    """Emit the bin file header that will be parsed by the RMM code"""
+    text_section = get_section(sections, 0, ".text")
+    rodata_section = get_section(sections, 1, ".rodata")
+    data_section = get_section(sections, 2, ".data")
+    bss_section = get_section(sections, 3, ".bss")
+    shared_section = get_section(sections, 4, ".shared")
+    text_offset = 0
+    rodata_offset = text_offset + text_section.size
+    data_offset = rodata_offset + rodata_section.size
+    header = struct.pack(
+        BIN_HEADER_FORMAT,
+        0,
+        APP_HEADER_MAGIC,
+        HEADER_VERSION,
+        get_app_name_bytes(app_name),
+        app_id,
+        app_len,
+        0,  # text
+        text_section.vma,
+        text_section.size,
+        rodata_offset,  # rodata
+        rodata_section.vma,
+        rodata_section.size,
+        data_offset,  # data
+        data_section.vma,
+        data_section.size,
+        bss_section.vma,  # bss
+        bss_section.size,
+        shared_section.vma,  # shared
+        stack_page_count,  # stack
+        heap_page_count,  # heap
+        bytes(APP_HEADER_RESERVED_BYTES),
+        APP_HEADER_MAGIC,
+    )
+    logging.info(f"Emitting binary header, {len(header)} bytes.")
+    logging.info(
+        f"    app_header_magic: #1={APP_HEADER_MAGIC:016x}, #2={APP_HEADER_MAGIC:016x}"
+    )
+    logging.info(f"    app_name               =   {app_name:16}")
+    logging.info(f"    app_header_version     =   0x{HEADER_VERSION:16x}")
+    logging.info(f"    app_id                 =     {app_id:16}")
+    logging.info(f"    app_len                =   0x{app_len:16x}")
+
+    logging.info("    section  |   offset |               va |     size")
+    logging.info("    ---------|----------|------------------|---------")
+    logging.info(
+        f"    text     | {0:8x} | {text_section.vma:16x} | {text_section.size:8x}"
+    )
+    logging.info(
+        f"    rodata   | {rodata_offset:8x} | {rodata_section.vma:16x} | {rodata_section.size:8x}"
+    )
+    logging.info(
+        f"    data     | {data_offset:8x} | {data_section.vma:16x} | {data_section.size:8x}"
+    )
+    logging.info(
+        f"    bss      |      N/A | {bss_section.vma:16x} | {bss_section.size:8x}"
+    )
+    logging.info(f"    shared   |      N/A | {shared_section.vma:16x} | {PAGE_SIZE:8x}")
+    logging.info(
+        f"    stack    |      N/A |              N/A | {stack_page_count*PAGE_SIZE:8x}"
+    )
+    logging.info(
+        f"    heap     |      N/A |              N/A | {heap_page_count*PAGE_SIZE:8x}"
+    )
+
+    out_bin_file.write(header)
+
+    # emit padding to keep the app binary page aligned
+    assert len(header) < PAGE_SIZE
+    out_bin_file.write(bytearray([0] * (PAGE_SIZE - len(header))))
+
+    return PAGE_SIZE
+
+
+def emit_section_data(out_bin_file, sections):
+    """Emitting content of a section
+
+    Return the number of bytes emitted for this section
+    """
+    bytes_emitted = 0
+    for section in sections:
+        if section.data is not None and section.params.emit:
+            logging.info(
+                f"Emitting section '{section.params.name}', {len(section.data)} bytes."
+            )
+            out_bin_file.write(section.data)
+            bytes_emitted += len(section.data)
+    return bytes_emitted
+
+
+def calc_sections_size(sections):
+    """Calculates the length of the sections part of the bin"""
+    length = PAGE_SIZE  # accounting for header
+    for section in sections:
+        if section.data is not None and section.params.emit:
+            length += len(section.data)
+    if length % PAGE_SIZE != 0:
+        length += PAGE_SIZE - (length % PAGE_SIZE)
+    return length
+
+
+def emit_bin_file(out_bin_file_name, app_name, app_id, stack_page_count, heap_page_count, sections):
+    """Write the bin file"""
+    bytes_emitted = 0
+    with open(out_bin_file_name, "wb") as out_bin_file:
+        # Calculate the length of the bin file payload to be written in to the header
+        app_len = calc_sections_size(sections)
+        # Write the bin header
+        bytes_emitted += emit_bin_file_header(
+            out_bin_file, app_name, app_id, app_len, stack_page_count, heap_page_count, sections
+        )
+        # Write the sections that needs to be emitted
+        bytes_emitted += emit_section_data(out_bin_file, sections)
+
+        # Add padding so that the bin file is aligned to page boundary
+        padding_length = PAGE_SIZE - (((bytes_emitted + PAGE_SIZE - 1) % PAGE_SIZE) + 1)
+        assert (bytes_emitted + padding_length) % PAGE_SIZE == 0
+        assert padding_length < PAGE_SIZE
+        if padding_length:
+            out_bin_file.write(bytearray([0] * padding_length))
+            bytes_emitted += padding_length
+        assert bytes_emitted == app_len
+
+
+def get_sections_data(elffile, sections_params):
+    elf_section_idx = 0
+    section_idx = 0
+    sections_found = []
+
+    expected_section_names = [section.name for section in sections_params]
+
+    # Assume that the interesting sections are in the expected order
+    while elf_section_idx < elffile.num_sections() and section_idx < len(sections_params):
+        elf_section = elffile.get_section(elf_section_idx)
+
+        if elf_section.name not in expected_section_names[section_idx:]:
+            logging.info(
+                f"Skipping section {elf_section.name}, ({elf_section.data_size} bytes)"
+            )
+            elf_section_idx += 1
+            if elf_section_idx == elffile.num_sections():
+                break
+            continue
+
+        section_params = sections_params[section_idx]
+
+        if elf_section.name != section_params.name:
+            logging.info(
+                f"Section {expected_section_names[section_idx]} not found in the elf file"
+            )
+            section_idx += 1
+            continue
+
+        assert elf_section.name == section_params.name
+
+        logging.info(f"Found section {elf_section.name} size={elf_section.data_size}")
+
+        section_data = SectionData(
+            params=section_params,
+            vma=elf_section.header["sh_addr"],
+            size=elf_section.data_size,
+            data=elf_section.data(),
+        )
+        sections_found.append(section_data)
+        assert section_data.size == len(section_data.data)
+
+        elf_section_idx += 1
+        section_idx += 1
+
+    while elf_section_idx < elffile.num_sections():
+        elf_section = elffile.get_section(elf_section_idx)
+        logging.info(
+            f"Skipping section {elf_section.name}, ({elf_section.data_size} bytes)"
+        )
+        elf_section_idx += 1
+
+    while section_idx < len(sections_params):
+        section = sections_params[section_idx]
+        logging.info(f"Section {section.name} not found in the elf file")
+        section_idx += 1
+
+    return sections_found
+
+
+def parse_elf_file(elf_file_name):
+    """parse the elf file
+
+    returns the relevant sections' data found in the file
+    """
+    with open(elf_file_name, "rb") as in_file:
+        sections = [
+            SectionParams(".text", emit=True),
+            SectionParams(".rodata", emit=True),
+            SectionParams(".data", emit=True),
+            SectionParams(".bss", emit=False),
+            SectionParams(".shared", emit=False),
+        ]
+
+        elffile = ELFFile(in_file)
+
+        logging.info(f"{elf_file_name} has {elffile.num_sections()} sections.")
+
+        # Assume that the interesting sections are in the expected order
+        return get_sections_data(elffile, sections)
+
+
+def main():
+    """Main function of the script"""
+    logging.basicConfig(format="%(levelname)s: %(message)s", level=logging.DEBUG)
+
+    parser = ArgumentParser(description="Generate app data files for packaging.")
+    parser.add_argument(
+        "--elf-file", type=str, required=True, help="elf file to generate raw data for"
+    )
+    parser.add_argument(
+        "--app-id",
+        type=lambda v: int(v, 0),
+        required=True,
+        help="The ID of the application used in the RMM code",
+    )
+    parser.add_argument(
+        "--app-name",
+        required=True,
+        help="The name of the app",
+    )
+    parser.add_argument(
+        "--stack-page-count",
+        type=int,
+        required=True,
+        help="The stack size required by the application",
+    )
+    parser.add_argument(
+        "--heap-page-count",
+        type=int,
+        required=True,
+        help="The heap size required by the application (0 is valid)",
+    )
+    parser.add_argument(
+        "--out-bin",
+        type=str,
+        required=True,
+        help="application data for the bin generation",
+    )
+    args = parser.parse_args()
+
+    logging.info(f"Processing {args.elf_file}, app_name='{args.app_name}', app_id={args.app_id:x}")
+    sections = parse_elf_file(args.elf_file)
+    emit_bin_file(args.out_bin, args.app_name, args.app_id, args.stack_page_count, args.heap_page_count, sections)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/cmake/Modules/UnitTestFramework.cmake b/cmake/Modules/UnitTestFramework.cmake
index 48d3cf8..a6457f7 100644
--- a/cmake/Modules/UnitTestFramework.cmake
+++ b/cmake/Modules/UnitTestFramework.cmake
@@ -68,7 +68,7 @@
     add_custom_target(run-unittests
         WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
         COMMAND ctest "${CMAKE_CTEST_ARGUMENTS}" -C "$<CONFIG>"
-        DEPENDS rmm.elf rmm.map
+        DEPENDS rmm_core.elf rmm_core.map
     )
 endif()
 
@@ -115,7 +115,7 @@
             # Run all tests at once
             add_test(NAME "${arg_NAME}"
                     WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
-                    COMMAND ${CMAKE_BINARY_DIR}/$<CONFIG>/rmm.elf
+                    COMMAND ${CMAKE_BINARY_DIR}/$<CONFIG>/rmm_core.elf
                             -g${arg_NAME}
                             -r${arg_ITERATIONS})
         else()
@@ -124,7 +124,7 @@
             foreach(TEST IN LISTS arg_RUN_ISOLATED_TESTS)
                 add_test(NAME "${arg_NAME}::${TEST}"
                          WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
-                         COMMAND ${CMAKE_BINARY_DIR}/$<CONFIG>/rmm.elf
+                         COMMAND ${CMAKE_BINARY_DIR}/$<CONFIG>/rmm_core.elf
                                  -sg${arg_NAME}
                                  -sn${TEST})
             endforeach()
diff --git a/docs/getting_started/build-options.rst b/docs/getting_started/build-options.rst
index a3a17b7..e82c232 100644
--- a/docs/getting_started/build-options.rst
+++ b/docs/getting_started/build-options.rst
@@ -204,7 +204,7 @@
 
     cmake -DRMM_CONFIG=host_defcfg -DHOST_VARIANT=host_test -DCMAKE_BUILD_TYPE=Debug -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR}
     cmake --build ${RMM_BUILD_DIR} -- build -j
-    ${RMM_BUILD_DIR}/Debug/rmm.elf -gxlat -v -r${NUMBER_OF_TEST_ITERATIONS}
+    ${RMM_BUILD_DIR}/Debug/rmm_core.elf -gxlat -v -r${NUMBER_OF_TEST_ITERATIONS}
 
 16. Generate Coverage Report.
 
@@ -225,7 +225,7 @@
 
     cmake -DRMM_CONFIG=host_defcfg -DHOST_VARIANT=host_test -DRMM_COVERAGE=ON -DCMAKE_BUILD_TYPE=Debug -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR}
     cmake --build ${RMM_BUILD_DIR} -- build -j
-    ${RMM_BUILD_DIR}/Debug/rmm.elf -gxlat
+    ${RMM_BUILD_DIR}/Debug/rmm_core.elf -gxlat
     cmake --build ${RMM_BUILD_DIR} -- run-coverage
 
 Run coverage analysis on the `host_build` variant of host platform:
@@ -233,7 +233,7 @@
 .. code-block:: bash
 
     cmake -DRMM_CONFIG=host_defcfg -DHOST_VARIANT=host_build -DRMM_COVERAGE=ON -DCMAKE_BUILD_TYPE=Debug -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR}
-    ${RMM_BUILD_DIR}/Debug/rmm.elf
+    ${RMM_BUILD_DIR}/Debug/rmm_core.elf
     cmake --build ${RMM_BUILD_DIR} -- run-coverage
 
 The above commands will automatically generate the HTML coverage report in folder
diff --git a/docs/getting_started/getting-started.rst b/docs/getting_started/getting-started.rst
index 040d9ce..0a49039 100644
--- a/docs/getting_started/getting-started.rst
+++ b/docs/getting_started/getting-started.rst
@@ -56,6 +56,7 @@
    "gcovr",">=v4.2","Tools(Coverage analysis)"
    "CBMC",">=5.84.0","Tools(CBMC analysis)"
    "Cppcheck",">=2.14.0","Tools(Cppcheck)"
+   "pyelftools","==0.31","Firmware (EL0 app)"
 
 .. _getting_started_toolchain:
 
@@ -136,10 +137,23 @@
 Install python dependencies
 ###########################
 
+RMM's ``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 requirements.txt
+
+#############################################
+Install python dependencies for Documentation
+#############################################
+
 .. note::
 
-    The installation of Python dependencies is an optional step. This is required only
-    if building documentation.
+    The installation of Python dependencies for documentation is an optional
+    step. This is required only if building documentation.
 
 RMM's ``docs/requirements.txt`` file declares additional Python dependencies.
 Install them with ``pip3``:
@@ -301,7 +315,7 @@
 |RMM|.
 
 If |RMM| is built for the `fake_host` architecture
-(see :ref:`RMM Fake Host Build`), then the generated `rmm.elf` binary can
+(see :ref:`RMM Fake Host Build`), then the generated `rmm_core.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.
 
diff --git a/lib/arch/include/aarch64/asm_macros.S b/lib/arch/include/aarch64/asm_macros.S
index 787a78a..f92ac99 100644
--- a/lib/arch/include/aarch64/asm_macros.S
+++ b/lib/arch/include/aarch64/asm_macros.S
@@ -164,4 +164,18 @@
 	300:
 	.endm
 
+	.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
+
 #endif /* ASM_MACROS_S */
diff --git a/lib/arch/include/arch.h b/lib/arch/include/arch.h
index d80027f..425f15f 100644
--- a/lib/arch/include/arch.h
+++ b/lib/arch/include/arch.h
@@ -136,10 +136,12 @@
 #define TCR_EL2_DS_WIDTH	UL(1)
 #define TCR_EL2_DS_LPA2_EN	INPLACE(TCR_EL2_DS, UL(1))
 
+#define TCR_EL2_A1		(UL(1) << 22)
 #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_EL2_E0PD0		(UL(1) << 55)
+#define TCR_EL2_E0PD1		(UL(1) << 56)
 
 #define TCR_TxSZ_MIN		UL(16)
 #define TCR_TxSZ_MIN_LPA2	UL(12)
diff --git a/lib/common/include/utils_def.h b/lib/common/include/utils_def.h
index bda9dc4..94bb86d 100644
--- a/lib/common/include/utils_def.h
+++ b/lib/common/include/utils_def.h
@@ -63,6 +63,8 @@
 #define round_down(value, boundary)		\
 	((value) & ~round_boundary(value, boundary))
 
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+
 /* Size of a 'm_' member of 's_' structure */
 /* cppcheck-suppress [misra-c2012-20.7] */
 #define SIZE_OF(s_, m_)		(sizeof(((struct s_ *)NULL)->m_))
@@ -244,6 +246,9 @@
 #define IF_CBMC(x)
 #endif /* CBMC */
 
+/* cppcheck-suppress misra-c2012-20.7 */
+#define SIZEOF_MEMBER(struct_type, member) (sizeof(((struct_type *)0)->member))
+
 #endif /* !(defined(__ASSEMBLER__) || defined(__LINKER__)) */
 
 #endif /* UTILS_DEF_H */
diff --git a/lib/rmm_el3_ifc/src/fake_host/cbmc_rmm_el3_ifc.c b/lib/rmm_el3_ifc/src/fake_host/cbmc_rmm_el3_ifc.c
index cbdbdcf..844cba0 100644
--- a/lib/rmm_el3_ifc/src/fake_host/cbmc_rmm_el3_ifc.c
+++ b/lib/rmm_el3_ifc/src/fake_host/cbmc_rmm_el3_ifc.c
@@ -16,4 +16,31 @@
 	return 0;
 }
 
+uintptr_t rmm_el3_ifc_get_shared_buf_locked(void)
+{
+	ASSERT(false, "rmm_el3_ifc_get_shared_buf_locked");
+	return 0;
+}
+
+void rmm_el3_ifc_release_shared_buf(void)
+{
+	ASSERT(false, "rmm_el3_ifc_release_shared_buf");
+}
+
+int rmm_el3_ifc_get_realm_attest_key(uintptr_t buf, size_t buflen,
+				     size_t *len, unsigned int crv)
+{
+	ASSERT(false, "rmm_el3_ifc_get_realm_attest_key");
+	return 0;
+}
+
+int rmm_el3_ifc_get_platform_token(uintptr_t buf, size_t buflen,
+				   size_t hash_size,
+				   size_t *token_hunk_len,
+				   size_t *remaining_len)
+{
+	ASSERT(false, "rmm_el3_ifc_get_platform_token");
+	return 0;
+}
+
 #endif /* CBMC */
diff --git a/lib/slot_buf/include/buffer.h b/lib/slot_buf/include/buffer.h
index be02ddc..c3bc70a 100644
--- a/lib/slot_buf/include/buffer.h
+++ b/lib/slot_buf/include/buffer.h
@@ -39,7 +39,14 @@
 	SLOT_RSI_CALL = U(SLOT_VDEV_AUX0) + VDEV_PARAM_AUX_GRANULES_MAX,
 	SLOT_EL3_TOKEN_SIGN_REC,	/* Slot for target REC during EL3 sign flow */
 	SLOT_EL3_TOKEN_SIGN_AUX0,	/* Slots for AUX granules on target REC for EL3 sign flow */
-	NR_CPU_SLOTS = U(SLOT_EL3_TOKEN_SIGN_AUX0) + MAX_REC_AUX_GRANULES
+	/* TODO: The number of slots for app framework can be optimised as in
+	 * theory it should be enough to have a single page being mapped, this
+	 * is just for convenience.
+	 */
+	SLOT_APP_INIT = U(SLOT_EL3_TOKEN_SIGN_AUX0) + MAX_REC_AUX_GRANULES,
+	SLOT_APP_PAGE_TABLE,
+	SLOT_APP_SHARED,
+	NR_CPU_SLOTS
 };
 
 bool check_cpu_slots_empty(void);
diff --git a/lib/xlat/include/xlat_high_va.h b/lib/xlat/include/xlat_high_va.h
index 78b3a2a..5ca0128 100644
--- a/lib/xlat/include/xlat_high_va.h
+++ b/lib/xlat/include/xlat_high_va.h
@@ -10,7 +10,7 @@
 #include <xlat_tables.h>
 
 /* The per CPU High VA space is used for Slot buffer and per-CPU stack mapping */
-#define XLAT_HIGH_VA_SLOT_NUM	(U(108))
+#define XLAT_HIGH_VA_SLOT_NUM	(U(111))
 
 /* Calculate the slot buffer's virtual address */
 #define SLOT_VIRT		(((UL(0xFFFFFFFFFFFFFFFF)) - \
diff --git a/lib/xlat/src/xlat_tables_arch.c b/lib/xlat/src/xlat_tables_arch.c
index a8e9348..73b8137 100644
--- a/lib/xlat/src/xlat_tables_arch.c
+++ b/lib/xlat/src/xlat_tables_arch.c
@@ -166,6 +166,9 @@
 	 */
 	tcr |= TCR_EL2_AS | TCR_EL2_HPD0 | TCR_EL2_HPD1;
 
+	/* The current ASID is defined by TTBR1_EL2 */
+	tcr |= TCR_EL2_A1;
+
 	/*
 	 * Granule size. Only 4K supported on both halfs.
 	 */
diff --git a/lib/xlat/tests/xlat_tests_base_g2.cpp b/lib/xlat/tests/xlat_tests_base_g2.cpp
index 0a89ea3..34ade3a 100644
--- a/lib/xlat/tests/xlat_tests_base_g2.cpp
+++ b/lib/xlat/tests/xlat_tests_base_g2.cpp
@@ -2118,6 +2118,9 @@
 	/* Hierarchical permissions */
 	exp_tcr |= TCR_EL2_AS | TCR_EL2_HPD0 | TCR_EL2_HPD1;
 
+	/* The current ASID is defined by TTBR1_EL2 */
+	exp_tcr |= TCR_EL2_A1;
+
 	/*
 	 * Xlat library configures TCR_EL2.IPS to the max
 	 * supported by the PE.
diff --git a/plat/common/CMakeLists.txt b/plat/common/CMakeLists.txt
index 31ec794..bc4f93f 100644
--- a/plat/common/CMakeLists.txt
+++ b/plat/common/CMakeLists.txt
@@ -9,7 +9,8 @@
 add_library(rmm-plat-common)
 
 target_link_libraries(rmm-plat-common
-    PRIVATE rmm-lib)
+    PRIVATE rmm-lib
+            rmm-el2-stub)
 
 #
 # PLAT_CMN_CTX_MAX_XLAT_TABLES is allowed to be 0, and in case when there are
diff --git a/plat/common/src/plat_common_init.c b/plat/common/src/plat_common_init.c
index 7ad9986..719da85 100644
--- a/plat/common/src/plat_common_init.c
+++ b/plat/common/src/plat_common_init.c
@@ -5,6 +5,7 @@
 
 #ifndef CBMC
 
+#include <app_header.h>
 #include <arch_helpers.h>
 #include <assert.h>
 #include <buffer.h>
@@ -37,6 +38,19 @@
 #define RMM_RO_SIZE		(RMM_RO_END - RMM_RO_START)
 #define RMM_RW_SIZE		(RMM_RW_END - RMM_RW_START)
 
+/* Map the application binary data as RO. This is necessary so that the RMM can
+ * simply access the app header structures. Execution is not enabled as RMM is
+ * never intended to run app code in EL2. Write is not enabled as data pages
+ * might only be written from EL0, and for that purpose a separate mapping is
+ * created.
+ */
+#if APP_COUNT != 0
+#define RMM_APP			MAP_REGION_FLAT(			\
+					0,				\
+					0,				\
+					(MT_RO_DATA | MT_REALM))
+#endif
+
 #define RMM_CODE		MAP_REGION_FLAT(			\
 					RMM_CODE_START,			\
 					RMM_CODE_SIZE,			\
@@ -75,8 +89,17 @@
 					0,				\
 					(MT_DEVICE | MT_RW | MT_REALM))
 
+
+#if APP_COUNT == 0
 /* Number of common memory mapping regions */
 #define COMMON_REGIONS		(4U)
+#define REGION_RMM_SHARED_IDX	3
+#else
+/* Number of common memory mapping regions */
+#define COMMON_REGIONS		(5U)
+#define REGION_RMM_APP_IDX	0
+#define REGION_RMM_SHARED_IDX	4
+#endif
 
 /* Total number of memory mapping regions */
 #define TOTAL_MMAP_REGIONS	(COMMON_REGIONS + PLAT_CMN_EXTRA_MMAP_REGIONS)
@@ -118,9 +141,15 @@
 {
 	int ret;
 	unsigned int plat_offset, cmn_offset;
+#if APP_COUNT != 0
+	uint64_t rmm_img_start = app_get_rmm_start();
+#endif
 
 	/* Common regions sorted by ascending VA */
 	struct xlat_mmap_region regions[COMMON_REGIONS] = {
+#if APP_COUNT != 0
+		RMM_APP,
+#endif
 		RMM_CODE,
 		RMM_RO,
 		RMM_RW,
@@ -135,9 +164,16 @@
 		return -EINVAL;
 	}
 
+#if APP_COUNT != 0
+	/* setup the parameters for the application binary data */
+	regions[REGION_RMM_APP_IDX].base_pa = rmm_img_start;
+	regions[REGION_RMM_APP_IDX].base_va = rmm_img_start;
+	regions[REGION_RMM_APP_IDX].size = RMM_CODE_START - rmm_img_start;
+#endif
+
 	/* Setup the parameters of the shared area */
-	regions[3].base_pa = get_shared_buf_pa();
-	regions[3].size = rmm_el3_ifc_get_shared_buf_size();
+	regions[REGION_RMM_SHARED_IDX].base_pa = get_shared_buf_pa();
+	regions[REGION_RMM_SHARED_IDX].size = rmm_el3_ifc_get_shared_buf_size();
 
 	plat_offset = COMMON_REGIONS;
 	cmn_offset = 0U;
diff --git a/plat/host/common/CMakeLists.txt b/plat/host/common/CMakeLists.txt
index 94f3bf0..242475e 100644
--- a/plat/host/common/CMakeLists.txt
+++ b/plat/host/common/CMakeLists.txt
@@ -14,6 +14,9 @@
     PRIVATE  rmm-lib
              rmm-plat-common)
 
+target_link_libraries(rmm-host-common
+    PRIVATE  rmm-mbedtls) # Mbed TLS is added for host emulating signing in EL3
+
 target_sources(rmm-host-common
     PRIVATE "src/host_console.c"
             "src/host_harness_cmn.c"
diff --git a/plat/host/host_build/CMakeLists.txt b/plat/host/host_build/CMakeLists.txt
index 7611598..d30a65b 100644
--- a/plat/host/host_build/CMakeLists.txt
+++ b/plat/host/host_build/CMakeLists.txt
@@ -7,7 +7,8 @@
 
 target_link_libraries(rmm-plat-host_build
     PRIVATE rmm-lib
-            rmm-host-common)
+            rmm-host-common
+            rmm-el2-stub)
 
 target_sources(rmm-plat-host_build
     PRIVATE "src/host_setup.c"
diff --git a/plat/host/host_build/src/host_setup.c b/plat/host/host_build/src/host_setup.c
index f95f657..6c1e646 100644
--- a/plat/host/host_build/src/host_setup.c
+++ b/plat/host/host_build/src/host_setup.c
@@ -3,6 +3,7 @@
  * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
  */
 
+#include <app_header.h>
 #include <arch_helpers.h>
 #include <assert.h>
 #include <debug.h>
@@ -296,10 +297,79 @@
 
 void rmm_main(void);
 
+void print_help(char *app_name)
+{
+	rmm_log("Run RMM on the host\n\n");
+	rmm_log("Usage: %s [-h|--help] [app_id app_elf [...]]\n\n", app_name);
+
+	rmm_log("Arguments:\n");
+	rmm_log("  -h, --help      print this message and exit.\n");
+	rmm_log("  app_id          Integer value of app id of the app.\n");
+	rmm_log("  app_elf         path to the app's elf file.\n");
+}
+
+void process_command_line_arguments(int argc, char *argv[])
+{
+	int ret __unused;
+	int i;
+	unsigned int curr_app_index = 0;
+	char *end;
+	struct app_header *app_header;
+
+	for (i = 1; i < argc; ++i) {
+		if (strcmp(argv[i], "--help") == 0 ||
+		   strcmp(argv[i], "-h") == 0) {
+			print_help(argv[0]);
+			exit(0);
+		}
+		if (curr_app_index >= APP_COUNT) {
+			ERROR("Too many apps (more than %d).\n", APP_COUNT);
+			exit(1);
+		}
+		if (i+1 >= argc) {
+			ERROR("Invalid number of arguments.\n");
+			exit(1);
+		}
+
+		ret = app_get_header_ptr_at_index(curr_app_index, &app_header);
+		assert(ret == 0);
+		app_header->app_id = strtol(argv[i], &end, 0);
+		if (end != argv[i] + strlen(argv[i])) {
+			ERROR("Invalid app id '%s'.\n", argv[i]);
+			exit(1);
+		}
+		i += 1;
+		app_header->app_elf_name = argv[i];
+		NOTICE("Registering app at idx %u: id=%lu, filename='%s'\n",
+			curr_app_index, app_header->app_id, app_header->app_elf_name);
+
+		/* test opening the image file */
+		FILE *f = fopen(app_header->app_elf_name, "rb");
+
+		if (f == NULL) {
+			ERROR("Failed to open file '%s'\n", app_header->app_elf_name);
+			exit(1);
+		}
+		size_t bytes_read = fread(&ret, 1U, 1U, f);
+
+		if (bytes_read != 1) {
+			ERROR("Failed to read from file '%s'\n", app_header->app_elf_name);
+			exit(1);
+		}
+		ret = fclose(f);
+		if (ret != 0) {
+			ERROR("Failed to close file during read test'%s'\n",
+				app_header->app_elf_name);
+			exit(1);
+		}
+		++curr_app_index;
+	}
+}
+
 int main(int argc, char *argv[])
 {
-	(void)argc;
-	(void)argv;
+
+	process_command_line_arguments(argc, argv);
 
 	VERBOSE("RMM: Beginning of Fake Host execution\n");
 
diff --git a/plat/host/host_cbmc/include/tb_rec.h b/plat/host/host_cbmc/include/tb_rec.h
index 1916780..a88a755 100644
--- a/plat/host/host_cbmc/include/tb_rec.h
+++ b/plat/host/host_cbmc/include/tb_rec.h
@@ -18,7 +18,6 @@
  * There is a mismatch in the type of `struct rec` against spec.
  */
 struct rmm_rec {
-	enum attest_token_gen_state_t attest_state;
 	struct granule *aux[MAX_REC_AUX_GRANULES];
 	struct Rec_Runnable flags;
 	uint64_t gprs[RPV_SIZE];
diff --git a/plat/host/host_cbmc/src/tb_rec.c b/plat/host/host_cbmc/src/tb_rec.c
index d5c1424..2a3d7d3 100644
--- a/plat/host/host_cbmc/src/tb_rec.c
+++ b/plat/host/host_cbmc/src/tb_rec.c
@@ -82,9 +82,6 @@
 
 bool AuxStateEqual(struct granule **aux, uint64_t num_aux, unsigned char state)
 {
-	struct granule *g_rec_aux = aux[0];
-	unsigned char aux_state = granule_unlocked_state(g_rec_aux);
-
 	__CPROVER_assert(num_aux >= 0 && num_aux <= MAX_REC_AUX_GRANULES,
 		"internal: _AuxStateEqual range check.");
 	for (int i = 0; i < num_aux && i < MAX_REC_AUX_GRANULES; ++i) {
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..aa6a238
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,6 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+pyelftools>=0.31
\ No newline at end of file
diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt
index 6766bbc..c3f36de 100644
--- a/runtime/CMakeLists.txt
+++ b/runtime/CMakeLists.txt
@@ -21,7 +21,8 @@
 
 target_link_libraries(rmm-runtime
     PRIVATE rmm-lib
-            rmm-platform)
+            rmm-platform
+            rmm-el2-stub)
 
 target_include_directories(rmm-runtime
     PRIVATE "include")
diff --git a/runtime/core/aarch64/head.S b/runtime/core/aarch64/head.S
index f89232f..44fe582 100644
--- a/runtime/core/aarch64/head.S
+++ b/runtime/core/aarch64/head.S
@@ -3,6 +3,7 @@
  * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
  */
 
+#include <app_header.h>
 #include <arch.h>
 #include <asm_macros.S>
 #include <rmm_el3_ifc.h>
@@ -21,6 +22,7 @@
 	mov	x21, x1
 	mov	x22, x2
 	mov	x23, x3
+	mov	x24, lr
 
 	/* Disable DIT */
 	msr	DIT, xzr
@@ -124,6 +126,20 @@
 	bl	memset
 
 	/*
+	 * Save RMM image addresses.
+	 */
+	adrp	x1, rmm_entry /* rmm_entry is page alined */
+#ifndef APP_COUNT
+#error APP_COUNT is not defined
+#endif
+#if APP_COUNT == 0
+	mov	x0, x1
+#else
+	sub	x0, x24, #4
+#endif
+	bl 	app_save_rmm_entry_info
+
+	/*
 	 * Restore args received from previous BL image
 	 */
 	mov	x0, x20
diff --git a/runtime/core/aarch64/vectors.S b/runtime/core/aarch64/vectors.S
index 0ce8438..959538b 100644
--- a/runtime/core/aarch64/vectors.S
+++ b/runtime/core/aarch64/vectors.S
@@ -8,20 +8,6 @@
 
 	.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
diff --git a/runtime/core/init.c b/runtime/core/init.c
index a89f2ed..a1217a3 100644
--- a/runtime/core/init.c
+++ b/runtime/core/init.c
@@ -3,6 +3,8 @@
  * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
  */
 
+#include <app.h>
+#include <app_header.h>
 #include <arch_features.h>
 #include <attestation.h>
 #include <buffer.h>
@@ -107,6 +109,9 @@
 	       RSI_ABI_VERSION_GET_MAJOR(rsi_revision),
 	       RSI_ABI_VERSION_GET_MINOR(rsi_revision));
 
+	app_info_setup();
+	app_framework_setup();
+
 	rmm_warmboot_main();
 
 	simd_init();
diff --git a/toolchains/common.cmake b/toolchains/common.cmake
index 1bd2b91..c3eb449 100644
--- a/toolchains/common.cmake
+++ b/toolchains/common.cmake
@@ -17,6 +17,7 @@
     string(APPEND CMAKE_${language}_FLAGS_INIT "-fno-delete-null-pointer-checks ")
     string(APPEND CMAKE_${language}_FLAGS_INIT "-Wall -Werror -Wstrict-overflow ")
     string(APPEND CMAKE_${language}_FLAGS_INIT "-Wextra -Wno-implicit-fallthrough ")
+    string(APPEND CMAKE_${language}_FLAGS_INIT "-Wno-type-limits ")
     string(APPEND CMAKE_${language}_FLAGS_INIT "-gdwarf-4 ")
     string(APPEND CMAKE_${language}_FLAGS_INIT "-D_FORTIFY_SOURCE=2 ")
     string(APPEND CMAKE_${language}_FLAGS_DEBUG_INIT "-Og -Wnull-dereference ")