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