aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortushar khandelwal <tushar.khandelwal@arm.com>2019-05-16 17:55:32 +0100
committerTushar Khandelwal <tushar.khandelwal@arm.com>2019-09-04 11:37:10 +0100
commit23f01c5de10e9c6a51361fbc6949f6383e434900 (patch)
treefc22dcbd7611fa0d777ad5147749472bc1668e33
parente1f79b99491dac3452b842f09d97c808da1b5ff2 (diff)
add base firmware framework
this commit adds support for architecture, nvic, framework and build tools Change-Id: I0b0621dfafece9b2f08913311bcd8a3c456b6ff0 Signed-off-by: Tushar Khandelwal <tushar.khandelwal@arm.com>
-rw-r--r--.gitignore1
-rw-r--r--.gitmodules3
-rw-r--r--Makefile195
-rw-r--r--arch/src/Makefile40
-rw-r--r--arch/src/arm_main.c38
-rw-r--r--arch/src/arm_mm.c57
-rw-r--r--arch/src/armv6-m/arm_nvic.c322
-rw-r--r--arch/src/armv6-m/armv6-crt0.S56
-rw-r--r--arch/src/armv6-m/exception.S42
-rw-r--r--arch/src/armv6-m/ld.S191
-rw-r--r--arch/src/armv6-m/reset-v6.S20
-rw-r--r--arch/src/armv7-m/arm_nvic.c352
-rw-r--r--arch/src/armv7-m/armv7-crt0.S53
-rw-r--r--arch/src/armv7-m/exception.S41
-rw-r--r--arch/src/armv7-m/ld.S191
-rw-r--r--arch/src/armv7-m/reset-v7.S39
-rw-r--r--arch/src/section.h77
m---------cmsis0
-rw-r--r--framework/include/fwk_arch.h251
-rw-r--r--framework/include/fwk_assert.h80
-rw-r--r--framework/include/fwk_banner.h19
-rw-r--r--framework/include/fwk_dlist.h119
-rw-r--r--framework/include/fwk_element.h58
-rw-r--r--framework/include/fwk_errno.h88
-rw-r--r--framework/include/fwk_event.h104
-rw-r--r--framework/include/fwk_host.h33
-rw-r--r--framework/include/fwk_id.h544
-rw-r--r--framework/include/fwk_interrupt.h197
-rw-r--r--framework/include/fwk_list.h211
-rw-r--r--framework/include/fwk_macros.h200
-rw-r--r--framework/include/fwk_math.h71
-rw-r--r--framework/include/fwk_mm.h93
-rw-r--r--framework/include/fwk_module.h459
-rw-r--r--framework/include/fwk_multi_thread.h95
-rw-r--r--framework/include/fwk_notification.h80
-rw-r--r--framework/include/fwk_slist.h145
-rw-r--r--framework/include/fwk_thread.h98
-rw-r--r--framework/include/internal/fwk_id.h130
-rw-r--r--framework/include/internal/fwk_module.h132
-rw-r--r--framework/include/internal/fwk_multi_thread.h123
-rw-r--r--framework/include/internal/fwk_notification.h46
-rw-r--r--framework/include/internal/fwk_single_thread.h45
-rw-r--r--framework/include/internal/fwk_thread.h63
-rw-r--r--framework/src/Makefile29
-rw-r--r--framework/src/fwk_arch.c90
-rw-r--r--framework/src/fwk_assert.c64
-rw-r--r--framework/src/fwk_dlist.c135
-rw-r--r--framework/src/fwk_id.c154
-rw-r--r--framework/src/fwk_interrupt.c182
-rw-r--r--framework/src/fwk_mm.c123
-rw-r--r--framework/src/fwk_module.c670
-rw-r--r--framework/src/fwk_multi_thread.c952
-rw-r--r--framework/src/fwk_notification.c326
-rw-r--r--framework/src/fwk_slist.c160
-rw-r--r--framework/src/fwk_thread.c240
-rw-r--r--readme.md49
-rwxr-xr-xtools/build_string.py28
-rw-r--r--tools/build_system/cpu.mk34
-rw-r--r--tools/build_system/defs.mk84
-rw-r--r--tools/build_system/doc.md203
-rw-r--r--tools/build_system/firmware.mk241
-rw-r--r--tools/build_system/lib.mk58
-rw-r--r--tools/build_system/rules.mk211
-rwxr-xr-xtools/check_spacing.py236
-rwxr-xr-xtools/check_style.py171
-rwxr-xr-xtools/check_tabs.py139
-rwxr-xr-xtools/gen_module_code.py124
67 files changed, 9905 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..8373b3a
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "cmsis"]
+ path = cmsis
+ url = https://github.com/ARM-software/CMSIS_5
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..5e18916
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,195 @@
+#
+# Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+#
+# Version
+#
+export VERSION_MAJOR := 0
+export VERSION_MINOR := 1
+export VERSION_PATCH := 0
+
+#
+# Directories
+#
+export TOP_DIR := $(shell pwd)
+export ARCH_DIR := $(TOP_DIR)/arch
+export DOC_DIR := $(TOP_DIR)/doc
+export FWK_DIR := $(TOP_DIR)/framework
+export TOOLS_DIR := $(TOP_DIR)/tools
+export BS_DIR := $(TOOLS_DIR)/build_system
+export PRODUCTS_DIR := $(TOP_DIR)/product
+export MODULES_DIR := $(TOP_DIR)/module
+export OS_DIR := $(TOP_DIR)/cmsis/CMSIS/RTOS2
+
+BUILD_STRING := $(shell $(TOOLS_DIR)/build_string.py 2>/dev/null)
+export VERSION_STRING = v$(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH)
+export VERSION_DESCRIBE_STRING = $(VERSION_STRING)_$(BUILD_STRING)
+
+#
+# Tools
+#
+export RM := rm -rf
+export MD := mkdir -p
+export CP := cp -r
+export DOC := doxygen
+
+#
+# Default options
+#
+DEFAULT_MODE := release
+DEFAULT_BUILD_PATH := $(TOP_DIR)/build
+DEFAULT_VERBOSE := n
+
+#
+# Top-level configuration
+#
+.DEFAULT_GOAL = all
+# MAKECMDGOALS isn't set if there's no explicit goal in the
+# command line, so set the default.
+MAKECMDGOALS ?= $(.DEFAULT_GOAL)
+
+# Mode
+export MODE ?= $(DEFAULT_MODE)
+ifneq ($(filter-out debug release, $(MODE)),)
+ $(error "Invalid MODE parameter. Aborting...")
+endif
+
+# Build directory
+BUILD_PATH ?= $(DEFAULT_BUILD_PATH)
+ifeq ($(BUILD_PATH),)
+ $(error "Invalid BUILD_PATH parameter. Aborting...")
+endif
+export BUILD_DIR = $(abspath $(BUILD_PATH))
+export BUILD_DOC_DIR = $(BUILD_DIR)/doc
+
+# Verbose mode: y/n
+V ?= $(DEFAULT_VERBOSE)
+export V
+ifeq ($(V),n)
+ MAKEFLAGS += --silent
+ MAKEFLAGS += --no-print-directory
+endif
+
+#
+# Products
+#
+PRODUCTS := $(shell ls $(PRODUCTS_DIR) 2>/dev/null)
+
+PRODUCT_INDEPENDENT_GOALS := clean doc help test lib-% module-%
+
+ifneq ($(filter-out $(PRODUCT_INDEPENDENT_GOALS), $(MAKECMDGOALS)),)
+ ifeq ($(PRODUCT),)
+ $(error "You must define PRODUCT. Aborting...")
+ endif
+
+ export PRODUCT_DIR := $(PRODUCTS_DIR)/$(PRODUCT)
+
+ ifeq ($(wildcard $(PRODUCT_DIR)/product.mk),)
+ $(error "Missing product.mk in $(PRODUCT_DIR)")
+ endif
+
+ include $(PRODUCT_DIR)/product.mk
+
+ ifeq ($(BS_FIRMWARE_LIST),)
+ $(error "You must define BS_FIRMWARE_LIST in product.mk. Aborting...")
+ endif
+
+ FIRMWARE_TARGETS := $(addprefix firmware-, $(BS_FIRMWARE_LIST))
+
+define msg_start
+================================================================
+Arm SE Software build System
+Platform : $(BS_PRODUCT_NAME)
+Mode : $(MODE)
+Firmware(s) : $(BS_FIRMWARE_LIST)
+================================================================
+endef
+
+ $(info $(msg_start))
+endif
+
+#
+# Rules
+#
+
+.PHONY: all
+all: $(FIRMWARE_TARGETS)
+
+module-%: $(MODULES_DIR)/%
+ $(MAKE) -C $(MODULES_DIR)/$*/src
+
+firmware-%: $(PRODUCT_DIR)/%
+ $(MAKE) -f $(PRODUCT_DIR)/$*/firmware.mk FIRMWARE=$*
+
+lib-%: $(TOP_DIR)/%
+ $(MAKE) -C $*/src
+
+.PHONY: test
+test:
+ $(MAKE) -C $(FWK_DIR)/test all
+
+.PHONY: doc
+doc:
+ $(MAKE) -C $(DOC_DIR) doc
+
+.PHONY: clean
+clean:
+ $(RM) $(BUILD_DIR)
+
+.PHONY: help
+help:
+ @echo "Arm SE Software build system"
+ @echo ""
+ @echo "Usage: make [TARGET] [PRODUCT=<name>] [OPTIONS]"
+ @echo ""
+ @echo "--------------------------------------------------------------------"
+ @echo "| Available Targets |"
+ @echo "--------------------------------------------------------------------"
+ @echo " all Build all firmware defined by PRODUCT=<product>"
+ @echo " clean Remove all built products"
+ @echo " firmware-<name> Build a specific firmware from PRODUCT=<product>"
+ @echo " help Show this documentation"
+ @echo " lib-<name> Build a specific project library"
+ @echo " test Build and run the framework test cases"
+ @echo ""
+ @echo "--------------------------------------------------------------------"
+ @echo "| Product Selection |"
+ @echo "--------------------------------------------------------------------"
+ @echo " PRODUCT"
+ @echo " Available products: $(PRODUCTS)"
+ @echo " Select the target product to build. This is a required"
+ @echo " parameter."
+ @echo ""
+ @echo "--------------------------------------------------------------------"
+ @echo "| Options |"
+ @echo "--------------------------------------------------------------------"
+ @echo " BUILD_PATH"
+ @echo " Value: <Full, user provided path>"
+ @echo " Default: '$(DEFAULT_BUILD_PATH)'"
+ @echo " Set the base directory used during the build process."
+ @echo ""
+ @echo " CROSS_COMPILE"
+ @echo " Value: <User provided prefix>"
+ @echo " Default: None."
+ @echo " Provide a prefix for the GCC cross compiler binaries. This"
+ @echo " may be used to avoid conflicts with existing toolchains on"
+ @echo " the system path."
+ @echo ""
+ @echo " MODE"
+ @echo " Value: <debug | release>"
+ @echo " Default: '$(DEFAULT_MODE)'"
+ @echo " Choose between release and debug mode."
+ @echo ""
+ @echo " O"
+ @echo " Value: <Compiler-specific optimization level>"
+ @echo " Default: <Compiler and MODE specific>"
+ @echo " Set the desired level of optimization the compiler will use."
+ @echo ""
+ @echo " V"
+ @echo " Value: <y|n>"
+ @echo " Default: $(DEFAULT_VERBOSE)"
+ @echo " Enable or disable verbose mode for the build system."
+ @echo ""
diff --git a/arch/src/Makefile b/arch/src/Makefile
new file mode 100644
index 0000000..d9a9205
--- /dev/null
+++ b/arch/src/Makefile
@@ -0,0 +1,40 @@
+#
+# Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+include $(BS_DIR)/cpu.mk
+
+BS_LIB_NAME = arch
+
+BS_LIB_SOURCES_armv6-m += armv6-m/exception.S
+BS_LIB_SOURCES_armv6-m += armv6-m/reset-v6.S
+BS_LIB_SOURCES_armv6-m += armv6-m/armv6-crt0.S
+BS_LIB_SOURCES_armv6-m += armv6-m/arm_nvic.c
+BS_LIB_SOURCES_armv6-m += arm_main.c
+BS_LIB_SOURCES_armv6-m += arm_mm.c
+
+BS_LIB_INCLUDES_armv6-m += $(TOP_DIR)/cmsis/CMSIS/Core/Include/
+
+BS_LIB_SOURCES_armv7-m += armv7-m/exception.S
+BS_LIB_SOURCES_armv7-m += armv7-m/reset-v7.S
+BS_LIB_SOURCES_armv7-m += armv7-m/armv7-crt0.S
+BS_LIB_SOURCES_armv7-m += armv7-m/arm_nvic.c
+BS_LIB_SOURCES_armv7-m += arm_main.c
+BS_LIB_SOURCES_armv7-m += arm_mm.c
+
+BS_LIB_INCLUDES_armv7-m += $(TOP_DIR)/cmsis/CMSIS/Core/Include/
+
+BS_LIB_INCLUDES += $(ARCH_DIR)/include
+BS_LIB_INCLUDES += $(FWK_DIR)/include
+
+#
+# Select arch-specific sources and includes
+#
+BS_LIB_SOURCES += $(BS_LIB_SOURCES_$(BS_ARCH_CPU))
+BS_LIB_SOURCES += $(BS_LIB_SOURCES_$(BS_ARCH_ARCH))
+BS_LIB_INCLUDES += $(BS_LIB_INCLUDES_$(BS_ARCH_CPU))
+BS_LIB_INCLUDES += $(BS_LIB_INCLUDES_$(BS_ARCH_ARCH))
+
+include $(BS_DIR)/lib.mk
diff --git a/arch/src/arm_main.c b/arch/src/arm_main.c
new file mode 100644
index 0000000..eb116a7
--- /dev/null
+++ b/arch/src/arm_main.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stdbool.h>
+#include <stdnoreturn.h>
+#include <fwk_arch.h>
+#include <fwk_errno.h>
+#include <cmsis_compiler.h>
+
+extern int arm_nvic_init(struct fwk_arch_interrupt_driver **driver);
+extern int arm_mm_init(struct fwk_arch_mm_data *data);
+
+/*
+ * Error handler for failures that occur during early initialization.
+ */
+static noreturn void arm_panic(void)
+{
+ while (true)
+ __WFI();
+}
+
+static struct fwk_arch_init_driver arch_init_driver = {
+ .mm = arm_mm_init,
+ .interrupt = arm_nvic_init,
+};
+
+void arm_main(void)
+{
+ int status;
+
+ status = fwk_arch_init(&arch_init_driver);
+ if (status != FWK_SUCCESS)
+ arm_panic();
+}
diff --git a/arch/src/arm_mm.c b/arch/src/arm_mm.c
new file mode 100644
index 0000000..a356150
--- /dev/null
+++ b/arch/src/arm_mm.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Memory initialization.
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <fwk_arch.h>
+#include <fwk_errno.h>
+#include <fwk_macros.h>
+#include <section.h>
+
+int arm_mm_init(struct fwk_arch_mm_data *data)
+{
+ const unsigned int ALIGNMENT = sizeof(uint64_t);
+
+ uintptr_t start;
+ uintptr_t end;
+
+ if (data == NULL)
+ return FWK_E_PARAM;
+
+ if (ARCH_SECTION_HEAP_SIZE == 0)
+ return FWK_E_NOMEM;
+
+ /* Check if the main stack section overlaps the .bss section */
+ if (ARCH_SECTION_STACK_START <= ARCH_SECTION_BSS_END) {
+ assert(false);
+ return FWK_E_PANIC;
+ }
+
+ /* Ensure the start address did not overflow following 64-bit alignment */
+ start = FWK_ALIGN_NEXT(ARCH_SECTION_HEAP_START, ALIGNMENT);
+ if (start < ARCH_SECTION_HEAP_START)
+ return FWK_E_NOMEM;
+
+ /* Ensure the end address did not underflow following 64-bit alignment */
+ end = FWK_ALIGN_PREVIOUS(ARCH_SECTION_HEAP_END, ALIGNMENT);
+ if (end > ARCH_SECTION_HEAP_END)
+ return FWK_E_NOMEM;
+
+ /*
+ * Ensure there is space left after performing 64-bit alignment on the start
+ * and end addresses.
+ */
+ if (end <= start)
+ return FWK_E_NOMEM;
+
+ data->start = start;
+ data->size = end - start;
+
+ return FWK_SUCCESS;
+}
diff --git a/arch/src/armv6-m/arm_nvic.c b/arch/src/armv6-m/arm_nvic.c
new file mode 100644
index 0000000..6c2eb33
--- /dev/null
+++ b/arch/src/armv6-m/arm_nvic.c
@@ -0,0 +1,322 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Interrupt management.
+ */
+
+#include <limits.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdnoreturn.h>
+#include <fwk_arch.h>
+#include <fwk_errno.h>
+#include <fwk_interrupt.h>
+#include <fwk_macros.h>
+#include <fwk_mm.h>
+#include <cmsis_compiler.h>
+
+#ifdef BUILD_HAS_MULTITHREADING
+ #include <rtx_os.h>
+#endif
+
+extern noreturn void exception_invalid(void);
+
+struct nvic;
+
+#define SCB_VTOR ((FWK_RW uint32_t *)(0xE000ED08UL))
+#define SCS_NVIC ((struct nvic *)(0xE000E100UL))
+
+enum exception_num {
+ EXCEPTION_NUM_INVALID = 0U,
+ EXCEPTION_NUM_RESET = 1U,
+ EXCEPTION_NUM_NMI = 2U,
+ EXCEPTION_NUM_HARDFAULT = 3U,
+ EXCEPTION_NUM_SVCALL = 11U,
+ EXCEPTION_NUM_PENDSV = 14U,
+ EXCEPTION_NUM_SYSTICK = 15U,
+ EXCEPTION_NUM_COUNT,
+};
+
+struct nvic {
+ FWK_RW uint32_t ISER; /* Interrupt Set Enabled Register */
+ FWK_RW uint32_t ICER; /* Interrupt Clear Enabled Register */
+ FWK_RW uint32_t ISPR; /* Interrupt Set Pending Register */
+ FWK_RW uint32_t ICPR; /* Interrupt Clear Pending Register */
+ FWK_W uint32_t IPR; /* Interrupt Priority Register */
+};
+
+static uint32_t isr_count;
+static uint32_t irq_count;
+
+/*
+ * For interrupts with parameters, their entry in the vector table points to a
+ * global handler that calls a registered function in the callback table with a
+ * corresponding parameter. Entries in the vector table for interrupts without
+ * parameters point directly to the handler functions.
+ */
+struct callback {
+ void (*func)(uintptr_t param);
+ uintptr_t param;
+};
+
+static void (**vector)(void);
+static struct callback *callback;
+
+static void irq_global(void)
+{
+ struct callback *entry = &callback[__get_IPSR()];
+ entry->func(entry->param);
+}
+
+static int global_enable(void)
+{
+ __enable_irq();
+
+ return FWK_SUCCESS;
+}
+
+static int global_disable(void)
+{
+ __disable_irq();
+
+ return FWK_SUCCESS;
+}
+
+static int is_enabled(unsigned int interrupt, bool *enabled)
+{
+ if (interrupt >= irq_count)
+ return FWK_E_PARAM;
+
+ *enabled = SCS_NVIC->ISER & (UINT32_C(1) << (interrupt & 0x1F));
+
+ return FWK_SUCCESS;
+}
+
+static int enable(unsigned int interrupt)
+{
+ if (interrupt >= irq_count)
+ return FWK_E_PARAM;
+
+ SCS_NVIC->ISER = UINT32_C(1) << (interrupt & 0x1F);
+
+ return FWK_SUCCESS;
+}
+
+static int disable(unsigned int interrupt)
+{
+ if (interrupt >= irq_count)
+ return FWK_E_PARAM;
+
+ SCS_NVIC->ICER = UINT32_C(1) << (interrupt & 0x1F);
+
+ return FWK_SUCCESS;
+}
+
+static int is_pending(unsigned int interrupt, bool *pending)
+{
+ if (interrupt >= irq_count)
+ return FWK_E_PARAM;
+
+ *pending = SCS_NVIC->ISPR & (UINT32_C(1) << (interrupt & 0x1F));
+
+ return FWK_SUCCESS;
+}
+
+static int set_pending(unsigned int interrupt)
+{
+ if (interrupt >= irq_count)
+ return FWK_E_PARAM;
+
+
+
+ return FWK_SUCCESS;
+}
+
+static int clear_pending(unsigned int interrupt)
+{
+ if (interrupt >= irq_count)
+ return FWK_E_PARAM;
+
+ SCS_NVIC->ICPR = UINT32_C(1) << (interrupt & 0x1F);
+
+ return FWK_SUCCESS;
+}
+
+static int set_isr_irq(unsigned int interrupt, void (*isr)(void))
+{
+ if (interrupt >= irq_count)
+ return FWK_E_PARAM;
+
+ vector[EXCEPTION_NUM_COUNT + interrupt] = isr;
+
+ return FWK_SUCCESS;
+}
+
+static int set_isr_irq_param(unsigned int interrupt,
+ void (*isr)(uintptr_t param),
+ uintptr_t parameter)
+{
+ struct callback *entry;
+ if (interrupt >= irq_count)
+ return FWK_E_PARAM;
+
+ vector[EXCEPTION_NUM_COUNT + interrupt] = irq_global;
+
+ entry = &callback[EXCEPTION_NUM_COUNT + interrupt];
+ entry->func = isr;
+ entry->param = parameter;
+
+ return FWK_SUCCESS;
+}
+
+static int set_isr_nmi(void (*isr)(void))
+{
+ vector[EXCEPTION_NUM_NMI] = isr;
+
+ return FWK_SUCCESS;
+}
+
+static int set_isr_nmi_param(void (*isr)(uintptr_t param), uintptr_t parameter)
+{
+ struct callback *entry;
+
+ vector[EXCEPTION_NUM_NMI] = irq_global;
+
+ entry = &callback[EXCEPTION_NUM_NMI];
+ entry->func = isr;
+ entry->param = parameter;
+
+ return FWK_SUCCESS;
+}
+
+static int set_isr_fault(void (*isr)(void))
+{
+ vector[EXCEPTION_NUM_HARDFAULT] = isr;
+
+ return FWK_SUCCESS;
+}
+
+static int get_current(unsigned int *interrupt)
+{
+ *interrupt = __get_IPSR();
+
+ /* Not an interrupt */
+ if (*interrupt == 0)
+ return FWK_E_STATE;
+
+ if (*interrupt == EXCEPTION_NUM_NMI)
+ *interrupt = FWK_INTERRUPT_NMI;
+ else if ((*interrupt) < EXCEPTION_NUM_COUNT)
+ *interrupt = FWK_INTERRUPT_EXCEPTION;
+ else
+ *interrupt -= EXCEPTION_NUM_COUNT;
+
+ return FWK_SUCCESS;
+}
+
+static const struct fwk_arch_interrupt_driver arm_nvic_driver = {
+ .global_enable = global_enable,
+ .global_disable = global_disable,
+ .is_enabled = is_enabled,
+ .enable = enable,
+ .disable = disable,
+ .is_pending = is_pending,
+ .set_pending = set_pending,
+ .clear_pending = clear_pending,
+ .set_isr_irq = set_isr_irq,
+ .set_isr_irq_param = set_isr_irq_param,
+ .set_isr_nmi = set_isr_nmi,
+ .set_isr_nmi_param = set_isr_nmi_param,
+ .set_isr_fault = set_isr_fault,
+ .get_current = get_current,
+};
+
+static void irq_invalid(void)
+{
+ static unsigned int spurious = 0;
+
+ spurious++;
+
+ disable(__get_IPSR());
+}
+
+int arm_nvic_init(const struct fwk_arch_interrupt_driver **driver)
+{
+ uint32_t align_entries;
+ uint32_t align_word;
+ unsigned int i;
+
+ if (driver == NULL)
+ return FWK_E_PARAM;
+
+ /* Find the number of interrupt lines implemented in hardware */
+ irq_count = 32;
+ isr_count = irq_count + EXCEPTION_NUM_COUNT;
+
+ /*
+ * Allocate and initialize a table for the callback functions and their
+ * corresponding parameters.
+ */
+ callback = fwk_mm_calloc(isr_count, sizeof(callback[0]));
+ if (callback == NULL)
+ return FWK_E_NOMEM;
+ /*
+ * The base address for the vector table must align on the number of
+ * entries in the table, corresponding to a word boundary rounded up to the
+ * next power of two.
+ *
+ * For example, for a vector table with 48 entries, the base address must be
+ * on a 64-word boundary.
+ */
+
+ /* Calculate the next power of two */
+ align_entries = UINT32_C(1) << (32 - __CLZ(isr_count - 1));
+
+ /* Calculate alignment on a word boundary */
+ align_word = align_entries * sizeof(vector[0]);
+
+ vector = fwk_mm_alloc_aligned(isr_count, sizeof(vector[0]), align_word);
+
+ if (vector == NULL)
+ return FWK_E_NOMEM;
+
+ /*
+ * Initialize all exception entries to point to the exception_invalid()
+ * handler.
+ *
+ * Note: Initialization starts from entry 1 since entry 0 is not an
+ * exception pointer but the default stack pointer.
+ */
+ for (i = 1; i < EXCEPTION_NUM_COUNT; i++)
+ vector[i] = exception_invalid;
+
+ /* Initialize IRQs */
+ for (i = 0; i < irq_count; i++) {
+ /* Initialize all IRQ entries to point to the irq_invalid() handler */
+ vector[EXCEPTION_NUM_COUNT + i] = irq_invalid;
+
+ /* Ensure IRQs are disabled during boot sequence */
+ disable(i);
+ clear_pending(i);
+ }
+
+#ifdef BUILD_HAS_MULTITHREADING
+ /* Set exception entries that are implemented and handled by RTX */
+ vector[EXCEPTION_NUM_SVCALL] = SVC_Handler;
+ vector[EXCEPTION_NUM_PENDSV] = PendSV_Handler;
+ vector[EXCEPTION_NUM_SYSTICK] = SysTick_Handler;
+#endif
+
+ __DMB();
+
+ /* Switch to the new vector table */
+ *SCB_VTOR = (uint32_t)vector;
+ __DMB();
+
+ *driver = &arm_nvic_driver;
+
+ return FWK_SUCCESS;
+}
diff --git a/arch/src/armv6-m/armv6-crt0.S b/arch/src/armv6-m/armv6-crt0.S
new file mode 100644
index 0000000..26632ad
--- /dev/null
+++ b/arch/src/armv6-m/armv6-crt0.S
@@ -0,0 +1,56 @@
+ /*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * startup file.
+ */
+
+ .syntax unified
+
+ .text
+
+ .global start
+ .thumb_func
+ .func start
+start:
+
+/* Load .data, which is 4-byte aligned */
+ ldr r1, =__DATA_SIZE__
+ cmp r1, #0
+ beq 6f
+ ldr r4, =4
+ ldr r0, =__DATA_LMA_START__
+ ldr r2, =__DATA_START__
+ cmp r0, r2
+ beq 6f
+5:
+ ldr r3, [r0]
+ add r0, r0, r4
+ str r3, [r2]
+ add r2, r2, r4
+ subs r1, r4
+ bne 5b
+6:
+ /* Clear .bss, which is also 4-byte aligned */
+ ldr r1, =__BSS_SIZE__
+ cmp r1, #0
+ beq 8f
+ ldr r0, =__BSS_START__
+ ldr r2, =0
+7:
+ str r2, [r0]
+ add r0, r4
+ subs r1, r4
+ bne 7b
+
+8:
+ dsb
+ bl arm_main
+
+9:
+ wfi
+ b 9b
+ .pool
+ .endfunc
diff --git a/arch/src/armv6-m/exception.S b/arch/src/armv6-m/exception.S
new file mode 100644
index 0000000..9cdd907
--- /dev/null
+++ b/arch/src/armv6-m/exception.S
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * exceptions.
+ */
+
+
+ .syntax unified
+
+ .section .exception
+
+ .global exceptions
+exceptions:
+ .word __STACK_END__
+ .word exception_reset /* Reset */
+ .word exception_invalid /* NMI */
+ .word exception_invalid /* HardFault */
+ .word exception_invalid /* MemManage */
+ .word exception_invalid /* Bus Fault */
+ .word exception_invalid /* UsageFault */
+ .word exception_invalid /* Reserved */
+ .word exception_invalid /* Reserved */
+ .word exception_invalid /* Reserved */
+ .word exception_invalid /* Reserved */
+ .word exception_invalid /* SVCall */
+ .word exception_invalid /* DebugMonitor */
+ .word exception_invalid /* Reserved */
+ .word exception_invalid /* PendSV */
+ .word exception_invalid /* SysTick */
+
+ .section .text
+
+ .global exception_invalid
+ .thumb_func
+ .func exception_invalid
+exception_invalid:
+ wfi
+ b exception_invalid
+ .endfunc
diff --git a/arch/src/armv6-m/ld.S b/arch/src/armv6-m/ld.S
new file mode 100644
index 0000000..98c03e7
--- /dev/null
+++ b/arch/src/armv6-m/ld.S
@@ -0,0 +1,191 @@
+ /*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * scatter file.
+ */
+
+ /*
+ * There are three supported memory layouts for the ARMv6-M architecture:
+ *
+ * Layout 1 - Single region:
+ * - All sections are placed in one contiguous region.
+ * - This layout uses only the mem0 memory region.
+ * - The memory is considered RXW by the linker, but the sections can be
+ * configured later on with different attributes using the MPU.
+ * - The main stack is placed at the end of mem0.
+ * - This layout is mainly used by second-stage firmware that is loaded directly
+ * into a single RAM.
+ *
+ * Layout 2 - Dual region with relocation:
+ * - One region is used for .text and .data (storage).
+ * - A second region is used for the remaining sections.
+ * - This layout uses memory regions mem0 and mem1 as the first and second
+ * regions, respectively.
+ * - The main stack is placed at the end of mem1.
+ * - This layout is mainly used by ROM firmware which uses part of the RAM for
+ * the data sections.
+ *
+ * Layout 3 - Dual region without relocation
+ * - One region is used only for the .text section.
+ * - A second region is used for all data sections.
+ * - This layout uses memory regions mem0 and mem1 as the first and second
+ * regions, respectively.
+ * - The main stack is placed at the end of mem1.
+ * - The main difference from layout 2 is that there is no relocation of the
+ * .data section.
+ * - This layout is mainly used by second-stage firmware loaded directly into
+ * two RAM regions. One of the RAM regions is attached to the instruction bus,
+ * which improves the performance as data and instruction accesses are
+ * independent.
+ *
+ */
+
+#define FWK_MEM_MODE_INVALID 0
+#define FWK_MEM_MODE_SINGLE_REGION 1
+#define FWK_MEM_MODE_DUAL_REGION_RELOCATION 2
+#define FWK_MEM_MODE_DUAL_REGION_NO_RELOCATION 3
+
+#include <fmw_memory.ld.S>
+
+#define STACK_ALIGNMENT 8
+
+/*
+ * Input validation
+ */
+
+#ifndef FIRMWARE_MEM_MODE
+ #error "FIRMWARE_MEM_MODE has not been configured"
+#endif
+
+#ifndef FIRMWARE_STACK_SIZE
+ #error "FIRMWARE_STACK_SIZE has not been configured"
+#endif
+
+#ifndef FIRMWARE_MEM0_BASE
+ #error "FIRMWARE_MEM0_BASE has not been configured"
+#endif
+
+#ifndef FIRMWARE_MEM0_SIZE
+ #error "FIRMWARE_MEM0_SIZE has not been configured"
+#endif
+
+#if ((FIRMWARE_MEM_MODE != FWK_MEM_MODE_SINGLE_REGION) && \
+ (FIRMWARE_MEM_MODE != FWK_MEM_MODE_DUAL_REGION_RELOCATION) && \
+ (FIRMWARE_MEM_MODE != FWK_MEM_MODE_DUAL_REGION_NO_RELOCATION))
+ #error "FIRMWARE_MEM_MODE has been configured improperly"
+#endif
+
+#if FIRMWARE_MEM_MODE != FWK_MEM_MODE_SINGLE_REGION
+ #ifndef FIRMWARE_MEM1_BASE
+ #error "FIRMWARE_MEM1_BASE has not been configured"
+ #endif
+
+ #ifndef FIRMWARE_MEM1_SIZE
+ #error "FIRMWARE_MEM1_SIZE has not been configured"
+ #endif
+#endif
+
+/*
+ * Calculate stack region in the data memory.
+ */
+
+#if FIRMWARE_MEM_MODE == FWK_MEM_MODE_SINGLE_REGION
+ ASSERT(FIRMWARE_STACK_SIZE < FIRMWARE_MEM0_SIZE,
+ "FIRMWARE_STACK_SIZE does not fit in MEM0")
+ #define UNALIGNED_STACK_BASE \
+ (FIRMWARE_MEM0_BASE + FIRMWARE_MEM0_SIZE - FIRMWARE_STACK_SIZE)
+#else
+ ASSERT(FIRMWARE_STACK_SIZE < FIRMWARE_MEM1_SIZE,
+ "FIRMWARE_STACK_SIZE does not fit in MEM1")
+ #define UNALIGNED_STACK_BASE \
+ (FIRMWARE_MEM1_BASE + FIRMWARE_MEM1_SIZE - FIRMWARE_STACK_SIZE)
+#endif
+
+#define STACK_BASE \
+ ( \
+ ((UNALIGNED_STACK_BASE + STACK_ALIGNMENT - 1) / STACK_ALIGNMENT) \
+ * STACK_ALIGNMENT \
+ )
+
+#define STACK_SIZE \
+ (( \
+ ((STACK_BASE + FIRMWARE_STACK_SIZE) / STACK_ALIGNMENT) \
+ * STACK_ALIGNMENT \
+ ) - STACK_BASE)
+
+ASSERT(STACK_SIZE > 0, "FIRMWARE_STACK_SIZE is too small")
+
+ENTRY(exception_reset)
+
+MEMORY {
+#if FIRMWARE_MEM_MODE == FWK_MEM_MODE_SINGLE_REGION
+ /* Only one memory region with read, execute and write attributes */
+ mem0 (rxw): ORIGIN = FIRMWARE_MEM0_BASE, LENGTH = FIRMWARE_MEM0_SIZE - \
+ FIRMWARE_STACK_SIZE
+#else
+ mem0 (rx): ORIGIN = FIRMWARE_MEM0_BASE, LENGTH = FIRMWARE_MEM0_SIZE
+ mem1 (rxw): ORIGIN = FIRMWARE_MEM1_BASE, LENGTH = FIRMWARE_MEM1_SIZE - \
+ FIRMWARE_STACK_SIZE
+#endif
+
+ stack (rw): ORIGIN = STACK_BASE, LENGTH = STACK_SIZE
+}
+
+SECTIONS {
+ .text : {
+ KEEP(*(.exception))
+ *(.text*)
+ *(.rodata*)
+ } > mem0
+
+ .data : {
+ . = ALIGN(4);
+ *(.data*)
+ . = ALIGN(4);
+#if FIRMWARE_MEM_MODE == FWK_MEM_MODE_SINGLE_REGION
+ } > mem0 /* .data follows .text in mem0 */
+#elif FIRMWARE_MEM_MODE == FWK_MEM_MODE_DUAL_REGION_NO_RELOCATION
+ } > mem1 /* .data is the first section in mem1 */
+#elif FIRMWARE_MEM_MODE == FWK_MEM_MODE_DUAL_REGION_RELOCATION
+ } > mem1 AT>mem0 /* Run-time image is at mem1, but loaded from mem0 */
+#else
+ ASSERT(0, "Unrecognized FIRMWARE_MEM_MODE")
+#endif
+
+ .bss : {
+ . = ALIGN(4);
+ *(.bss*)
+ . = ALIGN(4);
+#if FIRMWARE_MEM_MODE == FWK_MEM_MODE_SINGLE_REGION
+ } > mem0 /* Run-time image is at mem1, but loaded from mem0 */
+#else
+ } > mem1 /* .bss follows .data in mem1 */
+#endif
+
+ .stack : {
+ . = . + STACK_SIZE;
+ } > stack
+
+ __TEXT_START__ = LOADADDR(.text);
+ __TEXT_SIZE__ = SIZEOF(.text);
+ __TEXT_END__ = __TEXT_START__ + __TEXT_SIZE__;
+
+ __STACK_START__ = LOADADDR(.stack);
+ __STACK_SIZE__ = SIZEOF(.stack);
+ __STACK_END__ = __STACK_START__ + __STACK_SIZE__;
+
+ __DATA_LMA_START__ = LOADADDR(.data);
+ __DATA_START__ = ADDR(.data);
+ __DATA_SIZE__ = SIZEOF(.data);
+
+ __BSS_START__ = ADDR(.bss);
+ __BSS_SIZE__ = SIZEOF(.bss);
+ __BSS_END__ = __BSS_START__ + __BSS_SIZE__;
+
+ __HEAP_START__ = __BSS_START__ + __BSS_SIZE__;
+ __HEAP_END__ = __STACK_START__;
+ __HEAP_SIZE__ = __HEAP_END__ - __HEAP_START__;
+}
diff --git a/arch/src/armv6-m/reset-v6.S b/arch/src/armv6-m/reset-v6.S
new file mode 100644
index 0000000..4425081
--- /dev/null
+++ b/arch/src/armv6-m/reset-v6.S
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * reset vector.
+ */
+
+ .syntax unified
+
+ .text
+
+ .global exception_reset
+ .thumb_func
+ .func exception_reset
+exception_reset:
+ bl start
+ .pool
+ .endfunc
diff --git a/arch/src/armv7-m/arm_nvic.c b/arch/src/armv7-m/arm_nvic.c
new file mode 100644
index 0000000..c113ba4
--- /dev/null
+++ b/arch/src/armv7-m/arm_nvic.c
@@ -0,0 +1,352 @@
+ /*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Interrupt management.
+ */
+
+#include <limits.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdnoreturn.h>
+#include <fwk_arch.h>
+#include <fwk_errno.h>
+#include <fwk_interrupt.h>
+#include <fwk_macros.h>
+#include <fwk_mm.h>
+#include <cmsis_compiler.h>
+
+#ifdef BUILD_HAS_MULTITHREADING
+ #include <rtx_os.h>
+#endif
+
+extern noreturn void exception_invalid(void);
+
+struct nvic;
+
+#define SCB_SHCSR ((FWK_RW uint32_t *)(0xE000ED24UL))
+#define SCB_VTOR ((FWK_RW uint32_t *)(0xE000ED08UL))
+#define SCS_ICTR ((FWK_R uint32_t *)(0xE000E004UL))
+#define SCS_STIR ((FWK_W uint32_t *)(0xE000EF00UL))
+#define SCS_NVIC ((struct nvic *)(0xE000E100UL))
+
+#define SCB_SHCSR_MEMFAULTENA_MASK (1 << 16)
+#define SCB_SHCSR_BUSFAULTENA_MASK (1 << 17)
+#define SCB_SHCSR_USGFAULTENA_MASK (1 << 18)
+
+enum exception_num {
+ EXCEPTION_NUM_INVALID = 0U,
+ EXCEPTION_NUM_RESET = 1U,
+ EXCEPTION_NUM_NMI = 2U,
+ EXCEPTION_NUM_HARDFAULT = 3U,
+ EXCEPTION_NUM_MEMMANAGE = 4U,
+ EXCEPTION_NUM_BUSFAULT = 5U,
+ EXCEPTION_NUM_USAGEFAULT = 6U,
+ EXCEPTION_NUM_SVCALL = 11U,
+ EXCEPTION_NUM_DEBUGMONITOR = 12U,
+ EXCEPTION_NUM_PENDSV = 14U,
+ EXCEPTION_NUM_SYSTICK = 15U,
+ EXCEPTION_NUM_COUNT,
+};
+
+struct nvic {
+ FWK_RW uint32_t ISER[16]; /* Interrupt Set Enabled Register */
+ uint32_t RESERVED0[16];
+ FWK_RW uint32_t ICER[16]; /* Interrupt Clear Enabled Register */
+ uint32_t RESERVED1[16];
+ FWK_RW uint32_t ISPR[16]; /* Interrupt Set Pending Register */
+ uint32_t RESERVED2[16];
+ FWK_RW uint32_t ICPR[16]; /* Interrupt Clear Pending Register */
+ uint32_t RESERVED3[16];
+ FWK_R uint32_t IABR[16]; /* Interrupt Active Bit Register */
+ uint32_t RESERVED4[48];
+ FWK_W uint32_t IPR[16]; /* Interrupt Priority Register */
+ uint32_t RESERVED5[48];
+};
+
+static uint32_t isr_count;
+static uint32_t irq_count;
+
+/*
+ * For interrupts with parameters, their entry in the vector table points to a
+ * global handler that calls a registered function in the callback table with a
+ * corresponding parameter. Entries in the vector table for interrupts without
+ * parameters point directly to the handler functions.
+ */
+struct callback {
+ void (*func)(uintptr_t param);
+ uintptr_t param;
+};
+
+static void (**vector)(void);
+static struct callback *callback;
+
+static void irq_global(void)
+{
+ struct callback *entry = &callback[__get_IPSR()];
+ entry->func(entry->param);
+}
+
+static int global_enable(void)
+{
+ __enable_irq();
+
+ return FWK_SUCCESS;
+}
+
+static int global_disable(void)
+{
+ __disable_irq();
+
+ return FWK_SUCCESS;
+}
+
+static int is_enabled(unsigned int interrupt, bool *enabled)
+{
+ if (interrupt >= irq_count)
+ return FWK_E_PARAM;
+
+ *enabled = SCS_NVIC->ISER[interrupt / 32] &
+ (UINT32_C(1) << (interrupt & 0x1F));
+
+ return FWK_SUCCESS;
+}
+
+static int enable(unsigned int interrupt)
+{
+ if (interrupt >= irq_count)
+ return FWK_E_PARAM;
+
+ SCS_NVIC->ISER[interrupt / 32] = UINT32_C(1) << (interrupt & 0x1F);
+
+ return FWK_SUCCESS;
+}
+
+static int disable(unsigned int interrupt)
+{
+ if (interrupt >= irq_count)
+ return FWK_E_PARAM;
+
+ SCS_NVIC->ICER[interrupt / 32] = UINT32_C(1) << (interrupt & 0x1F);
+
+ return FWK_SUCCESS;
+}
+
+static int is_pending(unsigned int interrupt, bool *pending)
+{
+ if (interrupt >= irq_count)
+ return FWK_E_PARAM;
+
+ *pending = SCS_NVIC->ISPR[interrupt / 32] &
+ (UINT32_C(1) << (interrupt & 0x1F));
+
+ return FWK_SUCCESS;
+}
+
+static int set_pending(unsigned int interrupt)
+{
+ if (interrupt >= irq_count)
+ return FWK_E_PARAM;
+
+ *SCS_STIR = interrupt;
+
+ return FWK_SUCCESS;
+}
+
+static int clear_pending(unsigned int interrupt)
+{
+ if (interrupt >= irq_count)
+ return FWK_E_PARAM;
+
+ SCS_NVIC->ICPR[interrupt / 32] = UINT32_C(1) << (interrupt & 0x1F);
+
+ return FWK_SUCCESS;
+}
+
+static int set_isr_irq(unsigned int interrupt, void (*isr)(void))
+{
+ if (interrupt >= irq_count)
+ return FWK_E_PARAM;
+
+ vector[EXCEPTION_NUM_COUNT + interrupt] = isr;
+
+ return FWK_SUCCESS;
+}
+
+static int set_isr_irq_param(unsigned int interrupt,
+ void (*isr)(uintptr_t param),
+ uintptr_t parameter)
+{
+ struct callback *entry;
+ if (interrupt >= irq_count)
+ return FWK_E_PARAM;
+
+ vector[EXCEPTION_NUM_COUNT + interrupt] = irq_global;
+
+ entry = &callback[EXCEPTION_NUM_COUNT + interrupt];
+ entry->func = isr;
+ entry->param = parameter;
+
+ return FWK_SUCCESS;
+}
+
+static int set_isr_nmi(void (*isr)(void))
+{
+ vector[EXCEPTION_NUM_NMI] = isr;
+
+ return FWK_SUCCESS;
+}
+
+static int set_isr_nmi_param(void (*isr)(uintptr_t param), uintptr_t parameter)
+{
+ struct callback *entry;
+
+ vector[EXCEPTION_NUM_NMI] = irq_global;
+
+ entry = &callback[EXCEPTION_NUM_NMI];
+ entry->func = isr;
+ entry->param = parameter;
+
+ return FWK_SUCCESS;
+}
+
+static int set_isr_fault(void (*isr)(void))
+{
+ vector[EXCEPTION_NUM_HARDFAULT] = isr;
+ vector[EXCEPTION_NUM_MEMMANAGE] = isr;
+ vector[EXCEPTION_NUM_BUSFAULT] = isr;
+ vector[EXCEPTION_NUM_USAGEFAULT] = isr;
+
+ return FWK_SUCCESS;
+}
+
+static int get_current(unsigned int *interrupt)
+{
+ *interrupt = __get_IPSR();
+
+ /* Not an interrupt */
+ if (*interrupt == 0)
+ return FWK_E_STATE;
+
+ if (*interrupt == EXCEPTION_NUM_NMI)
+ *interrupt = FWK_INTERRUPT_NMI;
+ else if ((*interrupt) < EXCEPTION_NUM_COUNT)
+ *interrupt = FWK_INTERRUPT_EXCEPTION;
+ else
+ *interrupt -= EXCEPTION_NUM_COUNT;
+
+ return FWK_SUCCESS;
+}
+
+static const struct fwk_arch_interrupt_driver arm_nvic_driver = {
+ .global_enable = global_enable,
+ .global_disable = global_disable,
+ .is_enabled = is_enabled,
+ .enable = enable,
+ .disable = disable,
+ .is_pending = is_pending,
+ .set_pending = set_pending,
+ .clear_pending = clear_pending,
+ .set_isr_irq = set_isr_irq,
+ .set_isr_irq_param = set_isr_irq_param,
+ .set_isr_nmi = set_isr_nmi,
+ .set_isr_nmi_param = set_isr_nmi_param,
+ .set_isr_fault = set_isr_fault,
+ .get_current = get_current,
+};
+
+static void irq_invalid(void)
+{
+ static unsigned int spurious = 0;
+
+ spurious++;
+
+ disable(__get_IPSR());
+}
+
+int arm_nvic_init(const struct fwk_arch_interrupt_driver **driver)
+{
+ uint32_t ictr_intlinesnum;
+ uint32_t align_entries;
+ uint32_t align_word;
+ unsigned int i;
+
+ if (driver == NULL)
+ return FWK_E_PARAM;
+
+ /* Find the number of interrupt lines implemented in hardware */
+ ictr_intlinesnum = *SCS_ICTR & 0x0000000F;
+ irq_count = (ictr_intlinesnum + 1) * 32;
+ isr_count = irq_count + EXCEPTION_NUM_COUNT;
+
+ /*
+ * Allocate and initialize a table for the callback functions and their
+ * corresponding parameters.
+ */
+ callback = fwk_mm_calloc(isr_count, sizeof(callback[0]));
+ if (callback == NULL)
+ return FWK_E_NOMEM;
+ /*
+ * The base address for the vector table must align on the number of
+ * entries in the table, corresponding to a word boundary rounded up to the
+ * next power of two.
+ *
+ * For example, for a vector table with 48 entries, the base address must be
+ * on a 64-word boundary.
+ */
+
+ /* Calculate the next power of two */
+ align_entries = UINT32_C(1) << (32 - __CLZ(isr_count - 1));
+
+ /* Calculate alignment on a word boundary */
+ align_word = align_entries * sizeof(vector[0]);
+
+ vector = fwk_mm_alloc_aligned(isr_count, sizeof(vector[0]), align_word);
+
+ if (vector == NULL)
+ return FWK_E_NOMEM;
+
+ /*
+ * Initialize all exception entries to point to the exception_invalid()
+ * handler.
+ *
+ * Note: Initialization starts from entry 1 since entry 0 is not an
+ * exception pointer but the default stack pointer.
+ */
+ for (i = 1; i < EXCEPTION_NUM_COUNT; i++)
+ vector[i] = exception_invalid;
+
+ /* Initialize IRQs */
+ for (i = 0; i < irq_count; i++) {
+ /* Initialize all IRQ entries to point to the irq_invalid() handler */
+ vector[EXCEPTION_NUM_COUNT + i] = irq_invalid;
+
+ /* Ensure IRQs are disabled during boot sequence */
+ disable(i);
+ clear_pending(i);
+ }
+
+#ifdef BUILD_HAS_MULTITHREADING
+ /* Set exception entries that are implemented and handled by RTX */
+ vector[EXCEPTION_NUM_SVCALL] = SVC_Handler;
+ vector[EXCEPTION_NUM_PENDSV] = PendSV_Handler;
+ vector[EXCEPTION_NUM_SYSTICK] = SysTick_Handler;
+#endif
+
+ __DMB();
+
+ /* Switch to the new vector table */
+ *SCB_VTOR = (uint32_t)vector;
+ __DMB();
+
+ /* Enable the Usage, Bus and Memory faults which are disabled by default */
+ *SCB_SHCSR |= SCB_SHCSR_MEMFAULTENA_MASK |
+ SCB_SHCSR_BUSFAULTENA_MASK |
+ SCB_SHCSR_USGFAULTENA_MASK;
+
+ *driver = &arm_nvic_driver;
+
+ return FWK_SUCCESS;
+}
diff --git a/arch/src/armv7-m/armv7-crt0.S b/arch/src/armv7-m/armv7-crt0.S
new file mode 100644
index 0000000..f01ef1a
--- /dev/null
+++ b/arch/src/armv7-m/armv7-crt0.S
@@ -0,0 +1,53 @@
+ /*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * startup file.
+ */
+
+
+ .syntax unified
+
+ .text
+
+ .global start
+ .thumb_func
+ .func start
+start:
+ /* Load .data, which is 4-byte aligned */
+ ldr r1, =__DATA_SIZE__
+ cmp r1, #0
+ beq 6f
+ ldr r0, =__DATA_LMA_START__
+ ldr r2, =__DATA_START__
+ cmp r0, r2
+ beq 6f
+5:
+ ldr r3, [r0], #4
+ str r3, [r2], #4
+ subs r1, #4
+ bne 5b
+
+6:
+ /* Clear .bss, which is also 4-byte aligned */
+ ldr r1, =__BSS_SIZE__
+ cmp r1, #0
+ beq 8f
+ ldr r0, =__BSS_START__
+ mov r2, #0
+7:
+ str r2, [r0], #4
+ subs r1, #4
+ bne 7b
+
+8:
+ dsb
+ bl arm_main
+
+9:
+ wfi
+ b 9b
+ .pool
+ .endfunc
diff --git a/arch/src/armv7-m/exception.S b/arch/src/armv7-m/exception.S
new file mode 100644
index 0000000..1f943f2
--- /dev/null
+++ b/arch/src/armv7-m/exception.S
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * exceptions.
+ */
+
+ .syntax unified
+
+ .section .exception
+
+ .global exceptions
+exceptions:
+ .word __STACK_END__
+ .word exception_reset /* Reset */
+ .word exception_invalid /* NMI */
+ .word exception_invalid /* HardFault */
+ .word exception_invalid /* MemManage */
+ .word exception_invalid /* Bus Fault */
+ .word exception_invalid /* UsageFault */
+ .word exception_invalid /* Reserved */
+ .word exception_invalid /* Reserved */
+ .word exception_invalid /* Reserved */
+ .word exception_invalid /* Reserved */
+ .word exception_invalid /* SVCall */
+ .word exception_invalid /* DebugMonitor */
+ .word exception_invalid /* Reserved */
+ .word exception_invalid /* PendSV */
+ .word exception_invalid /* SysTick */
+
+ .section .text
+
+ .global exception_invalid
+ .thumb_func
+ .func exception_invalid
+exception_invalid:
+ wfi
+ b exception_invalid
+ .endfunc
diff --git a/arch/src/armv7-m/ld.S b/arch/src/armv7-m/ld.S
new file mode 100644
index 0000000..068fcca
--- /dev/null
+++ b/arch/src/armv7-m/ld.S
@@ -0,0 +1,191 @@
+ /*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * scatter file.
+ */
+
+/*
+ * There are three supported memory layouts for the ARMv7-M architecture:
+ *
+ * Layout 1 - Single region:
+ * - All sections are placed in one contiguous region.
+ * - This layout uses only the mem0 memory region.
+ * - The memory is considered RXW by the linker, but the sections can be
+ * configured later on with different attributes using the MPU.
+ * - The main stack is placed at the end of mem0.
+ * - This layout is mainly used by second-stage firmware that is loaded directly
+ * into a single RAM.
+ *
+ * Layout 2 - Dual region with relocation:
+ * - One region is used for .text and .data (storage).
+ * - A second region is used for the remaining sections.
+ * - This layout uses memory regions mem0 and mem1 as the first and second
+ * regions, respectively.
+ * - The main stack is placed at the end of mem1.
+ * - This layout is mainly used by ROM firmware which uses part of the RAM for
+ * the data sections.
+ *
+ * Layout 3 - Dual region without relocation
+ * - One region is used only for the .text section.
+ * - A second region is used for all data sections.
+ * - This layout uses memory regions mem0 and mem1 as the first and second
+ * regions, respectively.
+ * - The main stack is placed at the end of mem1.
+ * - The main difference from layout 2 is that there is no relocation of the
+ * .data section.
+ * - This layout is mainly used by second-stage firmware loaded directly into
+ * two RAM regions. One of the RAM regions is attached to the instruction bus,
+ * which improves the performance as data and instruction accesses are
+ * independent.
+ *
+ */
+
+#define FWK_MEM_MODE_INVALID 0
+#define FWK_MEM_MODE_SINGLE_REGION 1
+#define FWK_MEM_MODE_DUAL_REGION_RELOCATION 2
+#define FWK_MEM_MODE_DUAL_REGION_NO_RELOCATION 3
+
+#include <fmw_memory.ld.S>
+
+#define STACK_ALIGNMENT 8
+
+/*
+ * Input validation
+ */
+
+#ifndef FIRMWARE_MEM_MODE
+ #error "FIRMWARE_MEM_MODE has not been configured"
+#endif
+
+#ifndef FIRMWARE_STACK_SIZE
+ #error "FIRMWARE_STACK_SIZE has not been configured"
+#endif
+
+#ifndef FIRMWARE_MEM0_BASE
+ #error "FIRMWARE_MEM0_BASE has not been configured"
+#endif
+
+#ifndef FIRMWARE_MEM0_SIZE
+ #error "FIRMWARE_MEM0_SIZE has not been configured"
+#endif
+
+#if ((FIRMWARE_MEM_MODE != FWK_MEM_MODE_SINGLE_REGION) && \
+ (FIRMWARE_MEM_MODE != FWK_MEM_MODE_DUAL_REGION_RELOCATION) && \
+ (FIRMWARE_MEM_MODE != FWK_MEM_MODE_DUAL_REGION_NO_RELOCATION))
+ #error "FIRMWARE_MEM_MODE has been configured improperly"
+#endif
+
+#if FIRMWARE_MEM_MODE != FWK_MEM_MODE_SINGLE_REGION
+ #ifndef FIRMWARE_MEM1_BASE
+ #error "FIRMWARE_MEM1_BASE has not been configured"
+ #endif
+
+ #ifndef FIRMWARE_MEM1_SIZE
+ #error "FIRMWARE_MEM1_SIZE has not been configured"
+ #endif
+#endif
+
+/*
+ * Calculate stack region in the data memory.
+ */
+
+#if FIRMWARE_MEM_MODE == FWK_MEM_MODE_SINGLE_REGION
+ ASSERT(FIRMWARE_STACK_SIZE < FIRMWARE_MEM0_SIZE,
+ "FIRMWARE_STACK_SIZE does not fit in MEM0")
+ #define UNALIGNED_STACK_BASE \
+ (FIRMWARE_MEM0_BASE + FIRMWARE_MEM0_SIZE - FIRMWARE_STACK_SIZE)
+#else
+ ASSERT(FIRMWARE_STACK_SIZE < FIRMWARE_MEM1_SIZE,
+ "FIRMWARE_STACK_SIZE does not fit in MEM1")
+ #define UNALIGNED_STACK_BASE \
+ (FIRMWARE_MEM1_BASE + FIRMWARE_MEM1_SIZE - FIRMWARE_STACK_SIZE)
+#endif
+
+#define STACK_BASE \
+ ( \
+ ((UNALIGNED_STACK_BASE + STACK_ALIGNMENT - 1) / STACK_ALIGNMENT) \
+ * STACK_ALIGNMENT \
+ )
+
+#define STACK_SIZE \
+ (( \
+ ((STACK_BASE + FIRMWARE_STACK_SIZE) / STACK_ALIGNMENT) \
+ * STACK_ALIGNMENT \
+ ) - STACK_BASE)
+
+ASSERT(STACK_SIZE > 0, "FIRMWARE_STACK_SIZE is too small")
+
+ENTRY(exception_reset)
+
+MEMORY {
+#if FIRMWARE_MEM_MODE == FWK_MEM_MODE_SINGLE_REGION
+ /* Only one memory region with read, execute and write attributes */
+ mem0 (rxw): ORIGIN = FIRMWARE_MEM0_BASE, LENGTH = FIRMWARE_MEM0_SIZE - \
+ FIRMWARE_STACK_SIZE
+#else
+ mem0 (rx): ORIGIN = FIRMWARE_MEM0_BASE, LENGTH = FIRMWARE_MEM0_SIZE
+ mem1 (rxw): ORIGIN = FIRMWARE_MEM1_BASE, LENGTH = FIRMWARE_MEM1_SIZE - \
+ FIRMWARE_STACK_SIZE
+#endif
+
+ stack (rw): ORIGIN = STACK_BASE, LENGTH = STACK_SIZE
+}
+
+SECTIONS {
+ .text : {
+ KEEP(*(.exception))
+ *(.text*)
+ *(.rodata*)
+ } > mem0
+
+ .data : {
+ . = ALIGN(4);
+ *(.data*)
+ . = ALIGN(4);
+#if FIRMWARE_MEM_MODE == FWK_MEM_MODE_SINGLE_REGION
+ } > mem0 /* .data follows .text in mem0 */
+#elif FIRMWARE_MEM_MODE == FWK_MEM_MODE_DUAL_REGION_NO_RELOCATION
+ } > mem1 /* .data is the first section in mem1 */
+#elif FIRMWARE_MEM_MODE == FWK_MEM_MODE_DUAL_REGION_RELOCATION
+ } > mem1 AT>mem0 /* Run-time image is at mem1, but loaded from mem0 */
+#else
+ ASSERT(0, "Unrecognized FIRMWARE_MEM_MODE")
+#endif
+
+ .bss : {
+ . = ALIGN(4);
+ *(.bss*)
+ . = ALIGN(4);
+#if FIRMWARE_MEM_MODE == FWK_MEM_MODE_SINGLE_REGION
+ } > mem0 /* Run-time image is at mem1, but loaded from mem0 */
+#else
+ } > mem1 /* .bss follows .data in mem1 */
+#endif
+
+ .stack : {
+ . = . + STACK_SIZE;
+ } > stack
+
+ __TEXT_START__ = LOADADDR(.text);
+ __TEXT_SIZE__ = SIZEOF(.text);
+ __TEXT_END__ = __TEXT_START__ + __TEXT_SIZE__;
+
+ __STACK_START__ = LOADADDR(.stack);
+ __STACK_SIZE__ = SIZEOF(.stack);
+ __STACK_END__ = __STACK_START__ + __STACK_SIZE__;
+
+ __DATA_LMA_START__ = LOADADDR(.data);
+ __DATA_START__ = ADDR(.data);
+ __DATA_SIZE__ = SIZEOF(.data);
+
+ __BSS_START__ = ADDR(.bss);
+ __BSS_SIZE__ = SIZEOF(.bss);
+ __BSS_END__ = __BSS_START__ + __BSS_SIZE__;
+
+ __HEAP_START__ = __BSS_START__ + __BSS_SIZE__;
+ __HEAP_END__ = __STACK_START__;
+ __HEAP_SIZE__ = __HEAP_END__ - __HEAP_START__;
+}
diff --git a/arch/src/armv7-m/reset-v7.S b/arch/src/armv7-m/reset-v7.S
new file mode 100644
index 0000000..4ba1898
--- /dev/null
+++ b/arch/src/armv7-m/reset-v7.S
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * reset vector.
+ */
+
+ .syntax unified
+
+ .text
+
+ .global exception_reset
+ .thumb_func
+ .func exception_reset
+exception_reset:
+ /*
+ * Set up the Configuration Control Register (CCR) in the System Control
+ * Block (1) by setting the following flag bits:
+ *
+ * UNALIGN_TRP [3]: Enable trapping on unaligned word or halfword accesses.
+ * DIV_0_TRP [4]: Enable trapping on division by zero.
+ * STKALIGN [9]: Enable automatic DWORD stack-alignment on exception
+ * entry (2).
+ *
+ * All other bits are left in their default state.
+ *
+ * (1) ARM® v7-M Architecture Reference Manual, section B3.2.8.
+ * (2) ARM® v7-M Architecture Reference Manual, section B1.5.7.
+ */
+ ldr r0, =0xE000ED14 /* Load CCR address (architecture-defined) */
+ ldr r1, [r0] /* Load existing CCR value */
+ orr r1, #0x218 /* Set flag bits */
+ str r1, [r0] /* Store modified value back to the CCR */
+
+ bl start
+ .pool
+ .endfunc
diff --git a/arch/src/section.h b/arch/src/section.h
new file mode 100644
index 0000000..96447dd
--- /dev/null
+++ b/arch/src/section.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Section symbols.
+ */
+
+#ifndef ARCH_SECTION_H
+#define ARCH_SECTION_H
+
+#include <stddef.h>
+
+extern unsigned int __TEXT_START__;
+extern unsigned int __TEXT_SIZE__;
+extern unsigned int __TEXT_END__;
+
+extern unsigned int __DATA_START__;
+extern unsigned int __DATA_SIZE__;
+extern unsigned int __DATA_END__;
+
+extern unsigned int __BSS_START__;
+extern unsigned int __BSS_SIZE__;
+extern unsigned int __BSS_END__;
+
+extern unsigned int __STACK_START__;
+extern unsigned int __STACK_SIZE__;
+extern unsigned int __STACK_END__;
+
+/* Beginning of the .text section */
+#define ARCH_SECTION_TEXT_START ((uintptr_t)(&__TEXT_START__))
+
+/* End of the .text section */
+#define ARCH_SECTION_TEXT_END ((uintptr_t)(&__TEXT_END__))
+
+/* Size of the .text section */
+#define ARCH_SECTION_TEXT_SIZE ((size_t)(&__TEXT_SIZE__))
+
+/* Start of the .data section */
+#define ARCH_SECTION_DATA_START ((uintptr_t)(&__DATA_START__))
+
+/* End of the .data section */
+#define ARCH_SECTION_DATA_END ((uintptr_t)(&__DATA_END__))
+
+/* Size of the .data section */
+#define ARCH_SECTION_DATA_SIZE ((size_t)(&__DATA_SIZE__))
+
+/* Start of the .bss section */
+#define ARCH_SECTION_BSS_START ((uintptr_t)(&__BSS_START__))
+
+/* End of the .bss section */
+#define ARCH_SECTION_BSS_END ((uintptr_t)(&__BSS_END__))
+
+/* Size of the .bss section */
+#define ARCH_SECTION_BSS_SIZE ((size_t)(&__BSS_SIZE__))
+
+/* Start of the .heap section */
+#define ARCH_SECTION_HEAP_START ARCH_SECTION_BSS_END
+
+/* End of the .heap section */
+#define ARCH_SECTION_HEAP_END ARCH_SECTION_STACK_START
+
+/* Size of the .heap section */
+#define ARCH_SECTION_HEAP_SIZE ((size_t)(ARCH_SECTION_HEAP_END - \
+ ARCH_SECTION_HEAP_START))
+
+/* Start of the .stack section */
+#define ARCH_SECTION_STACK_START ((uintptr_t)(&__STACK_START__))
+
+/* End of the .stack section */
+#define ARCH_SECTION_STACK_END ((uintptr_t)(&__STACK_END__))
+
+/* Size of the .stack section */
+#define ARCH_SECTION_STACK_SIZE ((size_t)(&__STACK_SIZE__))
+
+#endif /* ARCH_SECTION_H */
diff --git a/cmsis b/cmsis
new file mode 160000
+Subproject 80cc44bba16cb4c8f495b7aa9709d41ac50e952
diff --git a/framework/include/fwk_arch.h b/framework/include/fwk_arch.h
new file mode 100644
index 0000000..071f4bb
--- /dev/null
+++ b/framework/include/fwk_arch.h
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Framework API for the architecture layer.
+ */
+
+#ifndef FWK_ARCH_H
+#define FWK_ARCH_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/*!
+ * \addtogroup GroupLibFramework Framework
+ * @{
+ */
+
+/*!
+ * \defgroup GroupArch Architecture Interface
+ * @{
+ */
+
+/*!
+ * \brief Interrupt driver interface.
+ *
+ * \details The interrupt driver interface allows the framework to provide
+ * architecture-specific handling of the system interrupts.
+ */
+struct fwk_arch_interrupt_driver {
+ /*!
+ * \brief Enable interrupts.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ */
+ int (*global_enable)(void);
+
+ /*!
+ * \brief Disable interrupts.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ */
+ int (*global_disable)(void);
+
+ /*!
+ * \brief Test whether an interrupt is enabled.
+ *
+ * \param interrupt Interrupt number.
+ * \param [out] enabled \c true if the interrupt is enabled, else \c false.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ */
+ int (*is_enabled)(unsigned int interrupt, bool *enabled);
+
+ /*!
+ * \brief Enable an interrupt.
+ *
+ * \param interrupt Interrupt number.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ */
+ int (*enable)(unsigned int interrupt);
+
+ /*!
+ * \brief Disable an interrupt.
+ *
+ * \param interrupt Interrupt number.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ */
+ int (*disable)(unsigned int interrupt);
+
+ /*!
+ * \brief Check if an interrupt is pending.
+ *
+ * \param interrupt Interrupt number.
+ * \param [out] pending \c true if the interrupt is pending, else \c false.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ */
+ int (*is_pending)(unsigned int interrupt, bool *pending);
+
+ /*!
+ * \brief Set an interrupt as pending.
+ *
+ * \param interrupt Interrupt number.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ */
+ int (*set_pending)(unsigned int interrupt);
+
+ /*!
+ * \brief Clear a pending interrupt.
+ *
+ * \param interrupt Interrupt number.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ */
+ int (*clear_pending)(unsigned int interrupt);
+
+ /*!
+ * \brief Set an IRQ interrupt service routine.
+ *
+ * \param interrupt Interrupt number.
+ * \param isr Pointer to the interrupt service routine function.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ */
+ int (*set_isr_irq)(unsigned int interrupt, void (*isr)(void));
+
+ /*!
+ * \brief Set an IRQ interrupt service routine that should receive a
+ * parameter.
+ *
+ * \param interrupt Interrupt number.
+ * \param isr Pointer to the interrupt service routine function.
+ * \param parameter Parameter that should be passed to the isr function.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ */
+ int (*set_isr_irq_param)(unsigned int interrupt,
+ void (*isr)(uintptr_t param),
+ uintptr_t parameter);
+
+ /*!
+ * \brief Set the interrupt service routine for the non-maskable interrupt
+ * (NMI).
+ *
+ * \param isr Pointer to the NMI interrupt service routine function.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ */
+ int (*set_isr_nmi)(void (*isr)(void));
+
+ /*!
+ * \brief Set the interrupt service routine for the non-maskable interrupt
+ * (NMI) that should receive a parameter.
+ *
+ * \param isr Pointer to the NMI interrupt service routine function.
+ * \param parameter Parameter that should be passed to the isr function.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ */
+ int (*set_isr_nmi_param)(void (*isr)(uintptr_t param), uintptr_t parameter);
+
+ /*!
+ * \brief Set the fault interrupt service routine.
+ *
+ * \param isr Pointer to the fault interrupt service routine function.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ */
+ int (*set_isr_fault)(void (*isr)(void));
+
+ /*!
+ * \brief Get the interrupt number for the current interrupt service routine
+ * being processed.
+ *
+ * \param [out] interrupt Interrupt number.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ * \retval FWK_E_STATE An interrupt is not currently being serviced.
+ */
+ int (*get_current)(unsigned int *interrupt);
+};
+
+/*!
+ * \brief Memory management data descriptor.
+ */
+struct fwk_arch_mm_data {
+ /*! Base address of the memory area used for dynamic memory allocation */
+ uintptr_t start;
+
+ /*! Size of the memory area used for dynamic memory allocation */
+ size_t size;
+};
+
+/*!
+ * \brief Initialization driver interface.
+ *
+ * \details The initialization driver interface allows the framework to request
+ * low-level (architecture-specific) initialization.
+ */
+struct fwk_arch_init_driver {
+ /*!
+ * \brief Memory management initialization.
+ *
+ * \details This handler is used by the framework to request memory used
+ * for dynamic allocation.
+ *
+ * \param data Pointer to the memory management descriptor the handler must
+ * fill in.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ * \retval FWK_E_NOMEM Insufficient memory available.
+ * \retval FWK_E_PANIC Unrecoverable initialization error.
+ */
+ int (*mm)(struct fwk_arch_mm_data *data);
+
+ /*!
+ * \brief Interrupt driver initialization.
+ *
+ * \details This handler is used by the framework library to request the
+ * interrupt driver.
+ *
+ * \param [out] driver Pointer to an interrupt driver.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_PARAM The parameter received by the handler is invalid.
+ * \retval FWK_E_PANIC Unrecoverable initialization error.
+ */
+ int (*interrupt)(struct fwk_arch_interrupt_driver **driver);
+};
+
+/*!
+ * \brief Initialize the framework library.
+ *
+ * \param driver Pointer to an initialization driver used to perform the
+ * initialization.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ * \retval FWK_E_PANIC Unrecoverable initialization error.
+ */
+int fwk_arch_init(const struct fwk_arch_init_driver *driver);
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* FWK_ARCH_H */
diff --git a/framework/include/fwk_assert.h b/framework/include/fwk_assert.h
new file mode 100644
index 0000000..d1040bf
--- /dev/null
+++ b/framework/include/fwk_assert.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef FWK_ASSERT_H
+#define FWK_ASSERT_H
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdnoreturn.h>
+
+/*!
+ * \addtogroup GroupLibFramework
+ * \defgroup GroupAssert Assertion Helpers
+ * \{
+ */
+
+/*!
+ * \brief Force a target-dependent, unrecoverable trap.
+ *
+ * \note If the framework is being built in debug mode then this function will
+ * loop indefinitely in order to facilitate the connection of a debugger
+ * instead of trapping.
+ */
+noreturn void fwk_trap(void);
+
+/*!
+ * \brief Mark a code path as unreachable.
+ *
+ * \details This function will trap in debug builds, but in release builds
+ * this code path will be marked as unreachable to the optimizer.
+ */
+noreturn void fwk_unreachable(void);
+
+/*!
+ * \brief Expect the success of a condition.
+ *
+ * \details This function will trap in debug builds if \p condition evaluates to
+ * \c false, but will otherwise do nothing.
+ *
+ * Example usage:
+ *
+ * \code{.c}
+ * int n = 5;
+ *
+ * if (!fwk_expect(n == 42))
+ * return FWK_E_STATE;
+ * \endcode
+ *
+ * In this example, \ref fwk_expect() is used to test the value of \c n. In
+ * debug builds, the condition will not hold and the program will trap. In
+ * release builds, the branch will be taken so the error can be properly
+ * handled.
+ *
+ * \param condition Condition to test.
+ *
+ * \retval true The expectation held.
+ * \retval false The expectation did not hold.
+ */
+bool fwk_expect(bool condition);
+
+/*!
+ * \brief Assert an invariant.
+ *
+ * \details This function will trap in debug builds if \p condition evaluates to
+ * \c false. Otherwise, it will be asserted to the optimizer that
+ * \p condition will always evaluates to \c true.
+ *
+ * \param condition Condition to test.
+ */
+void fwk_assert(bool condition);
+
+/*!
+ * \}
+ */
+
+#endif /* FWK_ASSERT_H */
diff --git a/framework/include/fwk_banner.h b/framework/include/fwk_banner.h
new file mode 100644
index 0000000..e8d0b7b
--- /dev/null
+++ b/framework/include/fwk_banner.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef FWK_BANNER_H
+
+#define FWK_BANNER_H
+
+#define FWK_BANNER_ES " ___ ___\r\n" \
+ "| / __|\r\n"\
+ "|=== \\___\r\n"\
+ "|___ |___/\r\n"\
+ "External System Cortex-M3 Processor\n\r"\
+ "Running RTX RTOS\n\r"
+
+#endif /* FWK_BANNER_H */
diff --git a/framework/include/fwk_dlist.h b/framework/include/fwk_dlist.h
new file mode 100644
index 0000000..cdb55be
--- /dev/null
+++ b/framework/include/fwk_dlist.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Intrusive circular doubly-linked list.
+ */
+
+#ifndef FWK_DLIST_H
+#define FWK_DLIST_H
+
+#include <fwk_slist.h>
+
+/*!
+ * \addtogroup GroupLibFramework Framework
+ * @{
+ */
+
+/*!
+ * \addtogroup GroupLinkedList Linked Lists
+ * @{
+ */
+
+/*!
+ * \brief Doubly-linked list.
+ *
+ * \internal
+ * \note This structure can be safely used in the place of \ref fwk_slist,
+ * \ref fwk_slist_node, or \ref fwk_dlist_node.
+ */
+struct fwk_dlist {
+ /*! Pointer to the list head */
+ struct fwk_dlist_node *head;
+
+ /*! Pointer to the list tail */
+ struct fwk_dlist_node *tail;
+};
+
+/*!
+ * \brief Doubly-linked list node.
+ *
+ * \internal
+ * \note This structure can be safely used in the place of \ref fwk_slist_node.
+ */
+struct fwk_dlist_node {
+ /*! Pointer to the next node in the list */
+ struct fwk_dlist_node *next;
+
+ /*! Pointer to the previous node in the list */
+ struct fwk_dlist_node *prev;
+};
+
+/*!
+ * @cond
+ */
+
+/*
+ * Add a new node to the head of a doubly-linked list.
+ *
+ * For internal use only.
+ * See fwk_list_push_head(list, new) for the public interface.
+ */
+void __fwk_dlist_push_head(
+ struct fwk_dlist *list,
+ struct fwk_dlist_node *new);
+
+/*
+ * Add a new node to the end of a doubly-linked list.
+ *
+ * For internal use only.
+ * See fwk_list_push_tail(list, new) for the public interface.
+ */
+void __fwk_dlist_push_tail(
+ struct fwk_dlist *list,
+ struct fwk_dlist_node *new);
+
+/*
+ * Remove and return the head node from a doubly-linked list.
+ *
+ * For internal use only.
+ * See fwk_list_pop_head(list) for the public interface.
+ */
+struct fwk_dlist_node *__fwk_dlist_pop_head(struct fwk_dlist *list);
+
+/*
+ * Remove a node from a doubly-linked list.
+ *
+ * For internal use only.
+ * See fwk_list_remove(list, node) for the public interface.
+ */
+void __fwk_dlist_remove(
+ struct fwk_dlist *list,
+ struct fwk_dlist_node *node);
+
+/*
+ * Insert a node into a doubly-linked list.
+ *
+ * For internal use only.
+ * See fwk_list_insert(list, new, node) for the public interface.
+ */
+void __fwk_dlist_insert(
+ struct fwk_dlist *list,
+ struct fwk_dlist_node *restrict new,
+ struct fwk_dlist_node *restrict node);
+
+/*!
+ * @endcond
+ */
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* FWK_DLIST_H */
diff --git a/framework/include/fwk_element.h b/framework/include/fwk_element.h
new file mode 100644
index 0000000..f5374ca
--- /dev/null
+++ b/framework/include/fwk_element.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Element definitions.
+ */
+
+#ifndef FWK_ELEMENT_H
+#define FWK_ELEMENT_H
+
+#include <stddef.h>
+
+/*!
+ * \addtogroup GroupLibFramework Framework
+ * @{
+ */
+
+/*!
+ * \defgroup GroupElement Elements
+ * @{
+ */
+
+/*!
+ * \brief Element descriptor.
+ */
+struct fwk_element {
+ /*! Element name */
+ const char *name;
+
+ /*! Number of sub-elements */
+ size_t sub_element_count;
+
+ /*!
+ * \brief Pointer to element-specific configuration data
+ *
+ * \details Because each element is expected to have some associated
+ * configuration data this pointer must be non-NULL for the framework
+ * to consider the element as valid.
+ *
+ * A fake, non-NULL pointer should be provided in the case where no
+ * element-specific configuration data is available. In this case the
+ * module code must not make any attempt to dereference or store the
+ * pointer during element initialization.
+ */
+ const void *data;
+};
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* FWK_ELEMENT_H */
diff --git a/framework/include/fwk_errno.h b/framework/include/fwk_errno.h
new file mode 100644
index 0000000..05b61a7
--- /dev/null
+++ b/framework/include/fwk_errno.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Standard return codes.
+ */
+
+#ifndef FWK_ERRNO_H
+#define FWK_ERRNO_H
+
+/*!
+ * \addtogroup GroupLibFramework Framework
+ * @{
+ */
+
+/*!
+ * \defgroup GroupErrno Return Codes
+ * @{
+ */
+
+/*! Success */
+#define FWK_SUCCESS 0
+
+/*! Invalid parameter(s) */
+#define FWK_E_PARAM -1
+
+/*! Invalid alignment */
+#define FWK_E_ALIGN -2
+
+/*! Invalid size */
+#define FWK_E_SIZE -3
+
+/*! Invalid handler or callback */
+#define FWK_E_HANDLER -4
+
+/*! Invalid access or permission denied */
+#define FWK_E_ACCESS -5
+
+/*! Value out of range */
+#define FWK_E_RANGE -6
+
+/*! Operation timed out */
+#define FWK_E_TIMEOUT -7
+
+/*! Memory allocation failed */
+#define FWK_E_NOMEM -8
+
+/*! Invalid power state */
+#define FWK_E_PWRSTATE -9
+
+/*! Not supported or disabled */
+#define FWK_E_SUPPORT -10
+
+/*! Device error */
+#define FWK_E_DEVICE -11
+
+/*! Handler or resource busy */
+#define FWK_E_BUSY -12
+
+/*! OS error response */
+#define FWK_E_OS -13
+
+/*! Unexpected or invalid data */
+#define FWK_E_DATA -14
+
+/*! Invalid state for the device or component */
+#define FWK_E_STATE -15
+
+/*! Accessing an uninitialized resource */
+#define FWK_E_INIT -16
+
+/*! Configuration overwritten */
+#define FWK_E_OVERWRITTEN -17
+
+/*! Unrecoverable error */
+#define FWK_E_PANIC -18
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* FWK_ERRNO_H */
diff --git a/framework/include/fwk_event.h b/framework/include/fwk_event.h
new file mode 100644
index 0000000..cebf203
--- /dev/null
+++ b/framework/include/fwk_event.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Events.
+ */
+
+#ifndef FWK_EVENT_H
+#define FWK_EVENT_H
+
+#include <stdalign.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <fwk_id.h>
+#include <fwk_list.h>
+
+/*!
+ * \addtogroup GroupLibFramework Framework
+ * @{
+ */
+
+/*!
+ * \defgroup GroupEvent Events
+ * @{
+ */
+
+/*!
+ * \brief Number of bytes available within each event definition for
+ * event-specific parameters.
+ */
+#define FWK_EVENT_PARAMETERS_SIZE 16
+
+/*!
+ * \brief Event.
+ *
+ * \details Events are used to facilitate event-based inter-process
+ * communication between modules. Each event represents an asynchronous
+ * message to one module from another, to which the target module can
+ * respond if necessary.
+ */
+struct fwk_event {
+ /*!
+ * \internal
+ * \brief Linked list node.
+ */
+ struct fwk_slist_node slist_node;
+
+ /*! Identifier of the event source */
+ fwk_id_t source_id;
+
+ /*! Identifier of the event target */
+ fwk_id_t target_id;
+
+ /*!
+ * Unique event number used to identify an event. The cookie is
+ * automatically set by the framework.
+ */
+ uint32_t cookie;
+
+ /*! Flag indicating whether the event is a response to another event */
+ bool is_response;
+
+ /*! Flag indicating whether the event source expects a response */
+ bool response_requested;
+
+ /*! Flag indicating whether the event is a notification */
+ bool is_notification;
+
+ /*!
+ * \brief Flag indicating whether the event is a delayed response
+ */
+ bool is_delayed_response;
+
+ /*!
+ * \internal
+ * \brief Flag indicating whether the event is a response event that a
+ * thread is waiting for to resume execution.
+ */
+ bool is_thread_wakeup_event;
+
+ /*!
+ * \brief Event identifier.
+ *
+ * \details Each module or element may define its own set of events. The
+ * event identifier must therefore be interpreted in the context of its
+ * target.
+ */
+ fwk_id_t id;
+
+ /*! Table of event parameters */
+ alignas(uintmax_t) uint8_t params[FWK_EVENT_PARAMETERS_SIZE];
+};
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* FWK_EVENT_H */
diff --git a/framework/include/fwk_host.h b/framework/include/fwk_host.h
new file mode 100644
index 0000000..2943a83
--- /dev/null
+++ b/framework/include/fwk_host.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+
+#ifndef FWK_HOST_H
+#define FWK_HOST_H
+
+#ifdef BUILD_HOST
+#include <stdio.h>
+
+/*!
+ * \brief Print a message using the host's standard output.
+ *
+ * \param fmt Const char pointer to the message format string.
+ * \param ... Additional arguments for the % specifiers within the message.
+ *
+ * \return On success, the number of characters written.
+ * \return On failure, a negative number containing the error code as per the
+ * printf() specification.
+ */
+#define FWK_HOST_PRINT printf
+
+#else
+#define FWK_HOST_PRINT(...) \
+ do { \
+ } while (0)
+#endif
+
+#endif /* FWK_HOST_H */
diff --git a/framework/include/fwk_id.h b/framework/include/fwk_id.h
new file mode 100644
index 0000000..a70c012
--- /dev/null
+++ b/framework/include/fwk_id.h
@@ -0,0 +1,544 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Identifiers.
+ */
+
+#ifndef FWK_ID_H
+#define FWK_ID_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <internal/fwk_id.h>
+#include <fwk_errno.h>
+
+/*!
+ * \addtogroup GroupLibFramework Framework
+ * @{
+ */
+
+/*!
+ * \defgroup GroupId Identifiers
+ * @{
+ */
+
+/*!
+ * \brief Identifier type.
+ */
+enum fwk_id_type {
+ /*! None */
+ FWK_ID_TYPE_NONE = __FWK_ID_TYPE_NONE,
+ /*! Module */
+ FWK_ID_TYPE_MODULE = __FWK_ID_TYPE_MODULE,
+ /*! Element */
+ FWK_ID_TYPE_ELEMENT = __FWK_ID_TYPE_ELEMENT,
+ /*! Sub-element */
+ FWK_ID_TYPE_SUB_ELEMENT = __FWK_ID_TYPE_SUB_ELEMENT,
+ /*! API */
+ FWK_ID_TYPE_API = __FWK_ID_TYPE_API,
+ /*! Event */
+ FWK_ID_TYPE_EVENT = __FWK_ID_TYPE_EVENT,
+ /*! Notification */
+ FWK_ID_TYPE_NOTIFICATION = __FWK_ID_TYPE_NOTIFICATION,
+};
+
+/*!
+ * \brief Build a 'none' identifier.
+ *
+ * \note This macro expands to a designated initializer, and can be used to
+ * initialize a \ref fwk_id_t.
+ *
+ * \details Build an id that explicitly refers to nothing.
+ *
+ * Example usage:
+ * \code{.c}
+ * static const fwk_id_t unknown = FWK_ID_NONE_INIT;
+ * \endcode
+ *
+ * \return invalid identifier.
+ */
+#define FWK_ID_NONE_INIT \
+ { \
+ .common = { \
+ .type = __FWK_ID_TYPE_NONE, \
+ }, \
+ }
+
+/*!
+ * \brief Build a 'none' identifier.
+ *
+ * \note This macro expands to a compound literal, and can be used as an lvalue
+ * expression returning type \ref fwk_id_t.
+ *
+ * \details Build an id that explicitly refers to nothing.
+ *
+ * Example usage:
+ * \code{.c}
+ * fwk_id_t get_id(void)
+ * {
+ * if (...)
+ * return FWK_ID_NONE;
+ * }
+ * \endcode
+ *
+ * \return invalid identifier.
+ */
+#define FWK_ID_NONE ((fwk_id_t) FWK_ID_NONE_INIT)
+
+/*!
+ * \brief Build a module identifier from a module index.
+ *
+ * \note This macro expands to a designated initializer, and can be used to
+ * initialize a \ref fwk_id_t.
+ *
+ * \details Example usage:
+ * \code{.c}
+ * static const fwk_id_t my_module = FWK_ID_MODULE_INIT(42);
+ * \endcode
+ *
+ * \param MODULE_IDX Module index.
+ *
+ * \return Module identifier.
+ */
+#define FWK_ID_MODULE_INIT(MODULE_IDX) \
+ { \
+ .common = { \
+ .type = __FWK_ID_TYPE_MODULE, \
+ .module_idx = MODULE_IDX, \
+ }, \
+ }
+
+/*!
+ * \brief Build a module identifier from a module index.
+ *
+ * \note This macro expands to a compound literal, and can be used as an lvalue
+ * expression returning type \ref fwk_id_t.
+ *
+ * \details Example usage:
+ * \code{.c}
+ * fwk_id_t get_module_42(void)
+ * {
+ * return FWK_ID_MODULE(42);
+ * }
+ * \endcode
+ *
+ * \param MODULE_IDX Module index.
+ *
+ * \return Module identifier.
+ */
+#define FWK_ID_MODULE(MODULE_IDX) ((fwk_id_t) FWK_ID_MODULE_INIT(MODULE_IDX))
+
+/*!
+ * \brief Build an element identifier from a module index and an element index.
+ *
+ * \note This macro expands to a designated initializer, and can be used to
+ * initialize a \ref fwk_id_t.
+ *
+ * \details Example usage:
+ * \code{.c}
+ * static const fwk_id_t my_element = FWK_ID_ELEMENT_INIT(42, 58);
+ * \endcode
+ *
+ * \param MODULE_IDX Module index.
+ * \param ELEMENT_IDX Element index.
+ *
+ * \return Element identifier.
+ */
+#define FWK_ID_ELEMENT_INIT(MODULE_IDX, ELEMENT_IDX) \
+ { \
+ .element = { \
+ .type = __FWK_ID_TYPE_ELEMENT, \
+ .module_idx = MODULE_IDX, \
+ .element_idx = ELEMENT_IDX, \
+ }, \
+ }
+
+/*!
+ * \brief Build an element identifier from a module index and an element index.
+ *
+ * \note This macro expands to a compound literal, and can be used as an lvalue
+ * expression returning type \ref fwk_id_t.
+ *
+ * \details Example usage:
+ * \code{.c}
+ * fwk_id_t get_element_42_58(void)
+ * {
+ * return FWK_ID_ELEMENT(42, 58);
+ * }
+ * \endcode
+ *
+ * \param MODULE_IDX Module index.
+ * \param ELEMENT_IDX Element index.
+ *
+ * \return Element identifier.
+ */
+#define FWK_ID_ELEMENT(MODULE_IDX, ELEMENT_IDX) \
+ ((fwk_id_t) FWK_ID_ELEMENT_INIT(MODULE_IDX, ELEMENT_IDX))
+
+/*!
+ * \brief Build a sub-element identifier from a module index, an element index
+ * and a sub-element index.
+ *
+ * \note This macro expands to a designated initializer, and can be used to
+ * initialize a \ref fwk_id_t.
+ *
+ * \details Example usage:
+ * \code{.c}
+ * static const fwk_id_t my_sub_element =
+ FWK_ID_SUB_ELEMENT_INIT(42, 58, 1);
+ * \endcode
+ *
+ * \param MODULE_IDX Module index.
+ * \param ELEMENT_IDX Element index.
+ * \param SUB_ELEMENT_IDX Sub-element index.
+ *
+ * \return Sub-element identifier.
+ */
+#define FWK_ID_SUB_ELEMENT_INIT(MODULE_IDX, ELEMENT_IDX, SUB_ELEMENT_IDX) \
+ { \
+ .sub_element = { \
+ .type = __FWK_ID_TYPE_SUB_ELEMENT, \
+ .module_idx = MODULE_IDX, \
+ .element_idx = ELEMENT_IDX, \
+ .sub_element_idx = SUB_ELEMENT_IDX, \
+ }, \
+ }
+
+/*!
+ * \brief Build an sub-element identifier from a module index, an element index
+ * and a sub-element index.
+ *
+ * \note This macro expands to a compound literal, and can be used as an lvalue
+ * expression returning type \ref fwk_id_t.
+ *
+ * \details Example usage:
+ * \code{.c}
+ * fwk_id_t get_sub_element_42_58_1(void)
+ * {
+ * return FWK_ID_SUB_ELEMENT(42, 58, 1);
+ * }
+ * \endcode
+ *
+ * \param MODULE_IDX Module index.
+ * \param ELEMENT_IDX Element index.
+ * \param SUB_ELEMENT_IDX Sub-element index.
+ *
+ * \return Sub-element identifier.
+ */
+#define FWK_ID_SUB_ELEMENT(MODULE_IDX, ELEMENT_IDX, SUB_ELEMENT_IDX) \
+ ((fwk_id_t) FWK_ID_SUB_ELEMENT_INIT(MODULE_IDX, ELEMENT_IDX, \
+ SUB_ELEMENT_IDX))
+
+/*!
+ * \brief Build an API identifier from a module index and an API index.
+ *
+ * \note This macro expands to a designated initializer, and can be used to
+ * initialize a \ref fwk_id_t.
+ *
+ * \details Example usage:
+ * \code{.c}
+ * static const fwk_id_t my_api = FWK_ID_API_INIT(42, 14);
+ * \endcode
+ *
+ * \param MODULE_IDX Module index.
+ * \param API_IDX API index.
+ *
+ * \return API identifier.
+ */
+#define FWK_ID_API_INIT(MODULE_IDX, API_IDX) \
+ { \
+ .api = { \
+ .type = __FWK_ID_TYPE_API, \
+ .module_idx = MODULE_IDX, \
+ .api_idx = API_IDX, \
+ }, \
+ }
+
+/*!
+ * \brief Build an API identifier from a module index and an API index.
+ *
+ * \note This macro expands to a compound literal, and can be used as an lvalue
+ * expression returning type \ref fwk_id_t.
+ *
+ * \details Example usage:
+ * \code{.c}
+ * fwk_id_t get_api_42_14(void)
+ * {
+ * return FWK_ID_API(42, 14);
+ * }
+ * \endcode
+ *
+ * \param MODULE_IDX Module index.
+ * \param API_IDX API index.
+ *
+ * \return API identifier.
+ */
+#define FWK_ID_API(MODULE_IDX, API_IDX) \
+ ((fwk_id_t) FWK_ID_API_INIT(MODULE_IDX, API_IDX))
+
+/*!
+ * \brief Build an event identifier from a module index and an event index.
+ *
+ * \note This macro expands to a designated initializer, and can be used to
+ * initialize a \ref fwk_id_t.
+ *
+ * \details Example usage:
+ * \code{.c}
+ * static const fwk_id_t my_event = FWK_ID_EVENT_INIT(42, 56);
+ * \endcode
+ *
+ * \param MODULE_IDX Module index.
+ * \param EVENT_IDX Event index.
+ *
+ * \return Event identifier.
+ */
+#define FWK_ID_EVENT_INIT(MODULE_IDX, EVENT_IDX) \
+ { \
+ .event = { \
+ .type = __FWK_ID_TYPE_EVENT, \
+ .module_idx = MODULE_IDX, \
+ .event_idx = EVENT_IDX, \
+ }, \
+ }
+
+/*!
+ * \brief Build an event identifier from a module index and an event index.
+ *
+ * \note This macro expands to a compound literal, and can be used as an lvalue
+ * expression returning type \ref fwk_id_t.
+ *
+ * \details Example usage:
+ * \code{.c}
+ * fwk_id_t get_event_42_56(void)
+ * {
+ * return FWK_ID_EVENT(42, 56);
+ * }
+ * \endcode
+ *
+ * \param MODULE_IDX Module index.
+ * \param EVENT_IDX Event index.
+ *
+ * \return Event identifier.
+ */
+#define FWK_ID_EVENT(MODULE_IDX, EVENT_IDX) \
+ ((fwk_id_t) FWK_ID_EVENT_INIT(MODULE_IDX, EVENT_IDX))
+
+/*!
+ * \brief Build a notification identifier from a module index and a
+ * notification index.
+ *
+ * \note This macro expands to a designated initializer, and can be used to
+ * initialize a \ref fwk_id_t.
+ *
+ * \details Example usage:
+ * \code{.c}
+ * static const fwk_id_t my_notification =
+ FWK_ID_NOTIFICATION_INIT(42, 56);
+ * \endcode
+ *
+ * \param MODULE_IDX Module index.
+ * \param NOTIFICATION_IDX Notification index.
+ *
+ * \return Notification identifier.
+ */
+#define FWK_ID_NOTIFICATION_INIT(MODULE_IDX, NOTIFICATION_IDX) \
+ { \
+ .notification = { \
+ .type = __FWK_ID_TYPE_NOTIFICATION, \
+ .module_idx = MODULE_IDX, \
+ .notification_idx = NOTIFICATION_IDX, \
+ }, \
+ }
+
+/*!
+ * \brief Build an notification identifier from a module index and a
+ * notification index.
+ *
+ * \note This macro expands to a compound literal, and can be used as an lvalue
+ * expression returning type \ref fwk_id_t.
+ *
+ * \details Example usage:
+ * \code{.c}
+ * fwk_id_t get_notification_42_56(void)
+ * {
+ * return FWK_ID_NOTIFICATION(42, 56);
+ * }
+ * \endcode
+ *
+ * \param MODULE_IDX Module index.
+ * \param NOTIFICATION_IDX Notification index.
+ *
+ * \return Notification identifier.
+ */
+#define FWK_ID_NOTIFICATION(MODULE_IDX, NOTIFICATION_IDX) \
+ ((fwk_id_t) FWK_ID_NOTIFICATION_INIT(MODULE_IDX, NOTIFICATION_IDX))
+
+/*!
+ * \brief Build a string representation of an identifier.
+ *
+ * \details Strings returned by this macro are in the format <tt>[MMM]</tt> for
+ * module identifiers and <tt>[MMM:EEEE]</tt> for element identifiers,
+ * where \c M refers to the module index and \c E refers to the element
+ * index.
+ *
+ * \param ID Module or element identifier.
+ *
+ * \return String representation of the identifier.
+ *
+ * \hideinitializer
+ */
+#define FWK_ID_STR(ID) (__fwk_id_str(ID).str)
+
+/*!
+ * \brief Generic identifier.
+ */
+typedef union __fwk_id fwk_id_t;
+
+/*!
+ * \brief Check if the identifier is of a certain identifier type.
+ *
+ * \param id Identifier.
+ * \param type Identifier type.
+ *
+ * \retval true The identifier is of the type specified.
+ * \retval false The identifier is not of the type specified.
+ */
+bool fwk_id_is_type(fwk_id_t id, enum fwk_id_type type);
+
+/*!
+ * \brief Retrieve the type of an identifier.
+ *
+ * \param id Identifier.
+ *
+ * \return Identifier type.
+ */
+enum fwk_id_type fwk_id_get_type(fwk_id_t id);
+
+/*!
+ * \brief Check if two identifiers refer to the same entity.
+ *
+ * \param left First identifier.
+ * \param right Second identifier.
+ *
+ * \retval true The identifiers refer to the same entity.
+ * \retval false The identifiers do not refer to the same entity.
+ */
+bool fwk_id_is_equal(fwk_id_t left, fwk_id_t right);
+
+/*!
+ * \brief Retrieve the identifier of the module that owns a given identifier.
+ *
+ * \note If the given identifier already refers to a module, the returned
+ * identifier refers to that same module.
+ *
+ * \param id Identifier.
+ *
+ * \return Identifier of the owning module.
+ */
+fwk_id_t fwk_id_build_module_id(fwk_id_t id);
+
+/*!
+ * \brief Retrieve the identifier of an element for a given identifier and
+ * element index.
+ *
+ * \details Usually the given identifier will be a module identifier and the
+ * function will be used to build an element identifier for an element with
+ * the given index that is owned by that module.
+ *
+ * \note If the identifier provided refers to an element, sub-element, API,
+ * event, or notification instead of a module then the parent module of
+ * the identifier is used when building the returned element identifier.
+ *
+ * \param id Identifier.
+ * \param element_idx Element index.
+ *
+ * \return Element identifier associated with the element index for the module.
+ */
+fwk_id_t fwk_id_build_element_id(fwk_id_t id, unsigned int element_idx);
+
+/*!
+ * \brief Retrieve the identifier of an API for a given identifier and
+ * API index.
+ *
+ * \details Usually the given identifier will be a module identifier and the
+ * function will be used to build an API identifier for an API with the
+ * given index that is owned by that module.
+ *
+ * \note If the identifier provided refers to an element, sub-element, API,
+ * event, or notification instead of a module then the parent module of
+ * the identifier is used when building the returned API identifier.
+ *
+ * \param id Identifier.
+ * \param api_idx API index.
+ *
+ * \return API identifier associated with the API index for the module.
+ */
+fwk_id_t fwk_id_build_api_id(fwk_id_t id, unsigned int api_idx);
+
+/*!
+ * \brief Retrieve the module index of an identifier.
+ *
+ * \param id Identifier.
+ *
+ * \return Module index.
+ */
+unsigned int fwk_id_get_module_idx(fwk_id_t id);
+
+/*!
+ * \brief Retrieve the index of an element from its identifier or the identifier
+ * of one of its sub-element.
+ *
+ * \param element_id Element or sub-element identifier.
+ *
+ * \return Element index.
+ */
+unsigned int fwk_id_get_element_idx(fwk_id_t element_id);
+
+/*!
+ * \brief Retrieve the index of a sub-element from its identifier.
+ *
+ * \param sub_element_id Sub-element identifier.
+ *
+ * \return Sub-element index.
+ */
+unsigned int fwk_id_get_sub_element_idx(fwk_id_t sub_element_id);
+
+/*!
+ * \brief Retrieve the index of an API from its identifier.
+ *
+ * \param api_id API identifier.
+ *
+ * \return API index.
+ */
+unsigned int fwk_id_get_api_idx(fwk_id_t api_id);
+
+/*!
+ * \brief Retrieve the index of an event from its identifier.
+ *
+ * \param event_id Event identifier.
+ *
+ * \return Event index.
+ */
+unsigned int fwk_id_get_event_idx(fwk_id_t event_id);
+
+/*!
+ * \brief Retrieve the index of a notification from its identifier.
+ *
+ * \param notification_id Notification identifier.
+ *
+ * \return Notification index.
+ */
+unsigned int fwk_id_get_notification_idx(fwk_id_t notification_id);
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* FWK_ID_H */
diff --git a/framework/include/fwk_interrupt.h b/framework/include/fwk_interrupt.h
new file mode 100644
index 0000000..bb87470
--- /dev/null
+++ b/framework/include/fwk_interrupt.h
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Interrupt management.
+ */
+
+#ifndef FWK_INTERRUPT_H
+#define FWK_INTERRUPT_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <limits.h>
+
+/*!
+ * \addtogroup GroupLibFramework Framework
+ * @{
+ */
+
+/*!
+ * \defgroup GroupInterrupt Interrupt Management
+ * @{
+ */
+
+/*!
+ * \brief Non-maskable interrupt identifier.
+ *
+ * \note This identifier can only be used in specific functions of this API.
+ * Please refer to the individual function documentation for more details.
+ */
+#define FWK_INTERRUPT_NMI UINT_MAX
+
+/*!
+ * \brief Non-existing interrupt identifier.
+ *
+ * \details Non-existing interrupt identifier to be used by configuration data
+ * when an interrupt identifier is expected but the system does not support
+ * it.
+ *
+ * \note This identifier cannot be used with the Interrupt Management component
+ * API. Its intention is to provide a standard way to describe an
+ * unconnected (or invalid) IRQ to the drivers.
+ */
+#define FWK_INTERRUPT_NONE (UINT_MAX - 1)
+
+/*!
+ * \brief Exception identifier.
+ *
+ * \details Exception interrupt identifier.
+ *
+ * \note This identifier can only be used in specific functions of this API.
+ * Please refer to the individual function documentation for more details.
+ */
+#define FWK_INTERRUPT_EXCEPTION (UINT_MAX - 2)
+
+/*!
+ * \brief Enable interrupts.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_INIT The component has not been initialized.
+ */
+int fwk_interrupt_global_enable(void);
+
+/*!
+ * \brief Disable interrupts.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_INIT The component has not been initialized.
+ */
+int fwk_interrupt_global_disable(void);
+
+/*!
+ * \brief Test whether an interrupt is enabled.
+ *
+ * \param interrupt Interrupt number.
+ * \param [out] enabled \c true if the interrupt is enabled, else \c false.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ * \retval FWK_E_INIT The component has not been initialized.
+ */
+int fwk_interrupt_is_enabled(unsigned int interrupt, bool *enabled);
+
+/*!
+ * \brief Enable interrupt.
+ *
+ * \param interrupt Interrupt number.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ * \retval FWK_E_INIT The component has not been initialized.
+ */
+int fwk_interrupt_enable(unsigned int interrupt);
+
+/*!
+ * \brief Disable interrupt.
+ *
+ * \param interrupt Interrupt number.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ * \retval FWK_E_INIT The component has not been initialized.
+ */
+int fwk_interrupt_disable(unsigned int interrupt);
+
+/*!
+ * \brief Check if an interrupt is pending.
+ *
+ * \param interrupt Interrupt number.
+ * \param [out] pending \c true if the interrupt is pending, else \c false.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ * \retval FWK_E_INIT The component has not been initialized.
+ */
+int fwk_interrupt_is_pending(unsigned int interrupt, bool *pending);
+
+/*!
+ * \brief Change interrupt status to pending.
+ *
+ * \param interrupt Interrupt number.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ * \retval FWK_E_INIT The component has not been initialized.
+ */
+int fwk_interrupt_set_pending(unsigned int interrupt);
+
+/*!
+ * \brief Clear the interrupt pending status.
+ *
+ * \param interrupt Interrupt number.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ * \retval FWK_E_INIT The component has not been initialized.
+ */
+int fwk_interrupt_clear_pending(unsigned int interrupt);
+
+/*!
+ * \brief Assign an interrupt service routine to an interrupt.
+ *
+ * \param interrupt Interrupt number. This function accepts FWK_INTERRUPT_NMI as
+ * an interrupt number.
+ * \param isr Pointer to the interrupt service routine function.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ * \retval FWK_E_INIT The component has not been initialized.
+ */
+int fwk_interrupt_set_isr(unsigned int interrupt, void (*isr)(void));
+
+/*!
+ * \brief Assign an interrupt service routine that receives a parameter to an
+ * interrupt.
+ *
+ * \param interrupt Interrupt number. This function accepts FWK_INTERRUPT_NMI as
+ * an interrupt number.
+ * \param isr Pointer to the interrupt service routine function.
+ * \param param Parameter that should be passed to the isr when it is called.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ * \retval FWK_E_INIT The component has not been initialized.
+ */
+int fwk_interrupt_set_isr_param(unsigned int interrupt,
+ void (*isr)(uintptr_t param),
+ uintptr_t param);
+
+/*!
+ * \brief Get the interrupt number for the interrupt service routine being
+ * processed.
+ *
+ * \param [out] interrupt Interrupt number. This function may return
+ * also:
+ * * FWK_INTERRUPT_NMI - When the current interrupt is an NMI.
+ * * FWK_INTERRUPT_EXCEPTION - When the current interrupt is an
+ * exception.
+ *
+ * \retval FWK_SUCCESS Operation succeeded.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ * \retval FWK_E_STATE An interrupt is not currently being serviced.
+ * \retval FWK_E_INIT The component has not been initialized.
+ */
+int fwk_interrupt_get_current(unsigned int *interrupt);
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* FWK_INTERRUPT_H */
diff --git a/framework/include/fwk_list.h b/framework/include/fwk_list.h
new file mode 100644
index 0000000..41d7a62
--- /dev/null
+++ b/framework/include/fwk_list.h
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Intrusive circular singly and doubly-linked lists.
+ */
+
+#ifndef FWK_LIST_H
+#define FWK_LIST_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <fwk_dlist.h>
+#include <fwk_slist.h>
+
+/*!
+ * \addtogroup GroupLibFramework Framework
+ * @{
+ */
+
+/*!
+ * \addtogroup GroupLinkedList Linked Lists
+ * @{
+ */
+
+/*!
+ * \brief Get the container in which a linked list node is stored.
+ *
+ * \param node Pointer to the linked list node to get the container of.
+ * \param type Type of the container.
+ * \param member Name of the linked list node member.
+ *
+ * \return Pointer to the container.
+ */
+#define FWK_LIST_GET(node, type, member) \
+ ((type *)(((uintptr_t)node) - offsetof(type, member)))
+
+/*!
+ * \brief Initialize a linked list.
+ *
+ * \param list Pointer to the list to initialize. Must not be \c NULL.
+ *
+ * \return None.
+ */
+#define fwk_list_init(list) \
+ _Generic((list), \
+ struct fwk_slist * : __fwk_slist_init, \
+ struct fwk_dlist * : __fwk_slist_init \
+ )((struct fwk_slist *)list)
+
+/*!
+ * \brief Retrieve the node at the head of a linked list.
+ *
+ * \param list Pointer to the list to retrieve the head of. Must not be \c NULL.
+ *
+ * \retval NULL The list is empty.
+ * \return Pointer to the node at the head of the linked list.
+ */
+#define fwk_list_head(list) \
+ ((void *)_Generic((list), \
+ const struct fwk_slist * : __fwk_slist_head, \
+ const struct fwk_dlist * : __fwk_slist_head, \
+ struct fwk_slist * : __fwk_slist_head, \
+ struct fwk_dlist * : __fwk_slist_head \
+ )((const struct fwk_slist *)list))
+
+/*!
+ * \brief Test whether a linked list is empty or not.
+ *
+ * \param list Pointer to the list to test. Must not be \c NULL.
+ *
+ * \retval true The linked list is empty.
+ * \retval false The linked list is not empty.
+ */
+#define fwk_list_is_empty(list) \
+ _Generic((list), \
+ const struct fwk_slist * : __fwk_slist_is_empty, \
+ const struct fwk_dlist * : __fwk_slist_is_empty, \
+ struct fwk_slist * : __fwk_slist_is_empty, \
+ struct fwk_dlist * : __fwk_slist_is_empty \
+ )((const struct fwk_slist *)list)
+
+/*!
+ * \brief Add a new node to the head of a linked list.
+ *
+ * \param list Pointer to the list to add to. Must not be \c NULL.
+ * \param new Pointer to the node to add. Must not be \c NULL. In debug mode,
+ * the node links must be \c NULL as they are checked to ensure the node is
+ * not already in use.
+ *
+ * \return None.
+ */
+#define fwk_list_push_head(list, new) \
+ _Generic((list), \
+ struct fwk_slist * : __fwk_slist_push_head, \
+ struct fwk_dlist * : __fwk_dlist_push_head \
+ )(list, new)
+
+/*!
+ * \brief Add a new node to the end of a linked list.
+ *
+ * \param list Pointer to the list to add to. Must not be \c NULL.
+ * \param new Pointer to the node to add. Must not be \c NULL. In debug mode,
+ * the node links must be \c NULL as they are checked to ensure the node is
+ * not already in use.
+ *
+ * \return None.
+ */
+#define fwk_list_push_tail(list, new) \
+ _Generic((list), \
+ struct fwk_slist * : __fwk_slist_push_tail, \
+ struct fwk_dlist * : __fwk_dlist_push_tail \
+ )(list, new)
+
+/*!
+ * \brief Remove and return the head node from a linked list.
+ *
+ * \param list Pointer to the list to remove from. Must not be \c NULL.
+ *
+ * \retval NULL The list was empty.
+ * \return The linked list node that was removed. In debug mode, the node links
+ * are set to \c NULL to ensure the node no longer references the list it
+ * has been removed from.
+ */
+#define fwk_list_pop_head(list) \
+ _Generic((list), \
+ struct fwk_slist * : __fwk_slist_pop_head, \
+ struct fwk_dlist * : __fwk_dlist_pop_head \
+ )(list)
+
+/*!
+ * \brief Get the next node of a linked list.
+ *
+ * \param list Pointer to the list to get the node from. Must not be \c NULL.
+ * \param node Pointer to the node to get the next one from. Must not be \c
+ * NULL.
+ *
+ * \retval NULL 'node' was the last node of the list.
+ * \return The pointer to the next node.
+ */
+#define fwk_list_next(list, node) \
+ ((void *) _Generic((list), \
+ const struct fwk_slist * : __fwk_slist_next, \
+ const struct fwk_dlist * : __fwk_slist_next, \
+ struct fwk_slist * : __fwk_slist_next, \
+ struct fwk_dlist * : __fwk_slist_next \
+ )((const struct fwk_slist *)list, (const struct fwk_slist_node *)node))
+
+/*!
+ * \brief Remove a node from a linked list.
+ *
+ * \details In debug mode, the node being removed has its links set to \c NULL
+ * to ensure the node no longer references the list it has been removed from.
+ *
+ * \param list Pointer to the list to remove the node from. Must not be \c NULL.
+ * \param node Pointer to the node to remove. Must not be \c NULL. The node must
+ * be in the list.
+ *
+ * \return None.
+ */
+#define fwk_list_remove(list, node) \
+ _Generic((list), \
+ struct fwk_slist * : __fwk_slist_remove, \
+ struct fwk_dlist * : __fwk_dlist_remove \
+ )(list, node)
+
+/*!
+ * \brief Insert a node into a linked list.
+ *
+ * \param list Pointer to the list. Must not be \c NULL.
+ * \param new Pointer to the node to insert. Must not be \c NULL. In debug mode,
+ * the node links must be \c NULL as they are checked to ensure the node is
+ * not already in use.
+ * \param node Pointer to the node that \p new will be inserted before. If this
+ * is \c NULL then the new node will be inserted at the tail of the list.
+ *
+ * \return None.
+ */
+#define fwk_list_insert(list, new, node) \
+ _Generic((list), \
+ struct fwk_dlist * : __fwk_dlist_insert \
+ )(list, new, node)
+
+/*!
+ * \brief Check if a node is in a list.
+ *
+ * \param list Pointer to the list. Must not be \c NULL.
+ * \param node Pointer to the node. Must not be \c NULL.
+ *
+ * \retval true \p node is in \p list.
+ * \retval false \p node is not in \p list.
+ */
+#define fwk_list_contains(list, node) \
+ _Generic((list), \
+ const struct fwk_slist * : __fwk_slist_contains, \
+ const struct fwk_dlist * : __fwk_slist_contains, \
+ struct fwk_slist * : __fwk_slist_contains, \
+ struct fwk_dlist * : __fwk_slist_contains \
+ )((const struct fwk_slist *)list, (const struct fwk_slist_node *)node)
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* FWK_LIST_H */
diff --git a/framework/include/fwk_macros.h b/framework/include/fwk_macros.h
new file mode 100644
index 0000000..313ed4e
--- /dev/null
+++ b/framework/include/fwk_macros.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Generic, library-wide macros.
+ */
+
+#ifndef FWK_MACROS_H
+#define FWK_MACROS_H
+
+/*!
+ * \addtogroup GroupLibFramework Framework
+ * @{
+ */
+
+/*!
+ * \defgroup GroupMacros Macros
+ * @{
+ */
+
+/*!
+ * \brief Get the number of elements in an array.
+ *
+ * \param A Pointer to the array.
+ *
+ * \return The number of elements in the array.
+ */
+#define FWK_ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0]))
+
+/*!
+ * \brief Aligns a value to the next multiple.
+ *
+ * \param VALUE Value to be aligned.
+ * \param INTERVAL Multiple to align to.
+ *
+ * \return The nearest multiple which is greater than or equal to VALUE.
+ */
+#define FWK_ALIGN_NEXT(VALUE, INTERVAL) ((\
+ ((VALUE) + (INTERVAL) - 1) / (INTERVAL)) * (INTERVAL))
+
+/*!
+ * \brief Aligns a value to the previous multiple.
+ *
+ * \param VALUE Value to be aligned.
+ * \param INTERVAL Multiple to align to.
+ *
+ * \return The nearest multiple which is smaller than or equal to VALUE.
+ */
+#define FWK_ALIGN_PREVIOUS(VALUE, INTERVAL) ( \
+ ((VALUE) / (INTERVAL)) * (INTERVAL))
+
+/*!
+ * \brief Hertz unit.
+ */
+#define FWK_HZ (1UL)
+
+/*!
+ * \brief Kilohertz unit.
+ */
+#define FWK_KHZ (1000UL)
+
+/*!
+ * \brief Megahertz unit.
+ */
+#define FWK_MHZ (1000UL * 1000UL)
+
+/*!
+ * \brief Gigahertz unit.
+ */
+#define FWK_GHZ (1000UL * 1000UL * 1000UL)
+
+/*!
+ * \brief Kibibyte (2¹⁰) unit.
+ *
+ * \details Kibibyte (2¹⁰) unit which contrasts with the SI unit KB (10³)
+ */
+#define FWK_KIB (1024UL)
+
+/*!
+ * \brief Mebibyte (2²⁰) unit.
+ *
+ * \details Mebibyte (2²⁰) unit which contrasts with the SI unit MB (10⁶)
+ */
+#define FWK_MIB (1024UL * 1024UL)
+
+/*!
+ * \brief Gibibyte (2³⁰) unit.
+ *
+ * \details Gibibyte (2³⁰) unit which contrasts with the SI unit GB (10⁹)
+ */
+#define FWK_GIB (1024UL * 1024UL * 1024UL)
+
+/*!
+ * \brief Tebibyte (2⁴⁰) unit.
+ *
+ * \details Tebibyte (2⁴⁰) unit which contrasts with the SI unit TB (10¹²)
+ */
+#define FWK_TIB (1024ULL * 1024ULL * 1024ULL * 1024ULL)
+
+/** @cond */
+#define __FWK_STRINGIFY(X) #X
+/** @endcond */
+
+/*!
+ * \brief Stringify the value of a macro.
+ *
+ * \param X The macro to be stringified.
+ *
+ * \return The stringified value.
+ */
+#define FWK_STRINGIFY(X) __FWK_STRINGIFY(X)
+
+/*!
+ * \brief Read-only register.
+ *
+ * \details This qualifier can be used to describe a memory mapped read-only
+ * register.
+ */
+#define FWK_R const volatile
+
+/*!
+ * \brief Write-only register.
+ *
+ * \details This qualifier can be used to describe a memory mapped write-only
+ * register.
+ */
+#define FWK_W volatile
+
+/*!
+ * \brief Read/write register.
+ *
+ * \details This qualifier can be used to describe a memory mapped read/write
+ * register.
+ */
+#define FWK_RW volatile
+
+/*!
+ * \brief Get the smaller of two values.
+ *
+ * \param a The first value to compare.
+ *
+ * \param b The second value to compare.
+ *
+ * \note The __auto_type extension is used to normalize the types and to protect
+ * against double evaluation.
+ *
+ * \return The smallest value from a and b. If both are equal, b is returned.
+ */
+#define FWK_MIN(a, b) \
+ __extension__({ \
+ __auto_type _a = (a); \
+ __auto_type _b = (b); \
+ _a < _b ? _a : _b; \
+ })
+
+/*!
+ * \brief Get the larger of two values.
+ *
+ * \param a The first value to compare.
+ *
+ * \param b The second value to compare.
+ *
+ * \note The __auto_type extension is used to normalize the types and to protect
+ * against double evaluation.
+ *
+ * \return The largest value from a and b. If both are equal, b is returned.
+ */
+#define FWK_MAX(a, b) \
+ __extension__({ \
+ __auto_type _a = (a); \
+ __auto_type _b = (b); \
+ _a > _b ? _a : _b; \
+ })
+
+/*!
+ * \brief Firmware version in UINT32_T format
+ *
+ * \details The macro encodes a 'major, minor and patch' based version data
+ * into a 32 bits value using the following schema:
+ * [31:24] Major field
+ * [23:16] Minor field
+ * [15:0] Patch field
+ *
+ * \return None.
+ */
+#define FWK_BUILD_VERSION (((BUILD_VERSION_MAJOR & 0xff) << 24) | \
+ ((BUILD_VERSION_MINOR & 0xff) << 16) | \
+ (BUILD_VERSION_PATCH & 0xffff))
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* FWK_MACROS_H */
diff --git a/framework/include/fwk_math.h b/framework/include/fwk_math.h
new file mode 100644
index 0000000..64905eb
--- /dev/null
+++ b/framework/include/fwk_math.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+
+#ifndef FWK_MATH_H
+#define FWK_MATH_H
+
+#include <limits.h>
+#include <stdint.h>
+
+/*!
+ * \ingroup GroupLibFramework
+ * \defgroup GroupMath Math
+ *
+ * \details Math helper functions to implement several common operations in an
+ * efficient way.
+ *
+ * @{
+ */
+
+/*!
+ * \brief Raise two to an exponent.
+ *
+ * \param exp The exponent to raise to.
+ *
+ * \return Two raised to \p exp.
+ *
+ * \note The type of the value returned by this macro is that of the \p exp
+ * parameter.
+ */
+#define fwk_math_pow2(exp) (((__typeof__(exp))1) << (exp))
+
+/*!
+ * \brief Count the leading zeros of an unsigned integer.
+ *
+ * \param num Operation input. The result is undefined if \p num is \c 0.
+ *
+ * \return Number of leading zeroes in the \p num parameter.
+ *
+ * \note The type of the value returned by this macro is that of the \p num
+ * parameter.
+ */
+#define fwk_math_clz(num) _Generic((num), \
+ unsigned int: __builtin_clz, \
+ unsigned long: __builtin_clzl, \
+ unsigned long long: __builtin_clzll)(num)
+
+/*!
+ * \brief Calculate the binary logarithm (log2) of an integer value.
+ *
+ * \param num Operation input. The result is undefined if \p num is \c 0.
+ *
+ * \return The binary logarithm of \p num.
+ *
+ * \note The type of the value returned by this macro is that of the \p num
+ * parameter.
+ * \warning If \p num is not a power of two, the result of this call is rounded
+ * down to the next whole number.
+ */
+#define fwk_math_log2(num) \
+ ((sizeof(num) * CHAR_BIT) - fwk_math_clz(num) - 1)
+
+/*!
+ * @}
+ */
+
+#endif /* FWK_MATH_H */
diff --git a/framework/include/fwk_mm.h b/framework/include/fwk_mm.h
new file mode 100644
index 0000000..46fe42c
--- /dev/null
+++ b/framework/include/fwk_mm.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Memory management.
+ */
+
+#ifndef FWK_MM_H
+#define FWK_MM_H
+
+#include <stdalign.h>
+#include <stddef.h>
+
+/*!
+ * \addtogroup GroupLibFramework Framework
+ * @{
+ */
+
+/*!
+ * \defgroup GroupMM Memory Management
+ * @{
+ */
+
+/*!
+ * \brief Default allocation alignment based on the alignment of uintmax_t
+ * on the platform the software is built for.
+ */
+#define FWK_MM_DEFAULT_ALIGNMENT (alignof(uintmax_t))
+
+/*!
+ * \brief Allocate a block of memory.
+ *
+ * \note Memory allocated by this function will be aligned by default on the
+ * value defined by \ref FWK_MM_DEFAULT_ALIGNMENT.
+ *
+ * \param num Number of items.
+ * \param size Item size in bytes.
+ *
+ * \retval NULL Allocation failed.
+ * \return Pointer to a newly-allocated block of memory.
+ */
+void *fwk_mm_alloc(size_t num, size_t size);
+
+/*!
+ * \brief Allocate a block of memory with specified alignment.
+ *
+ * \param num Number of items.
+ * \param size Item size in bytes.
+ * \param alignment Memory alignment in bytes. Must be a power of two.
+ *
+ * \retval NULL Allocation failed.
+ * \return Pointer to a newly-allocated block of memory.
+ */
+void *fwk_mm_alloc_aligned(size_t num, size_t size, unsigned int alignment);
+
+/*!
+ * \brief Allocate a block of memory and initialize all its bits to zero.
+ *
+ * \note Memory allocated by this function will be aligned by default on the
+ * value defined by \ref FWK_MM_DEFAULT_ALIGNMENT.
+ *
+ * \param num Number of items.
+ * \param size Item size in bytes.
+ *
+ * \retval NULL Allocation failed.
+ * \return Pointer to a newly-allocated block of memory.
+ */
+void *fwk_mm_calloc(size_t num, size_t size);
+
+/*!
+ * \brief Allocate a block of memory with specified alignment and initialize
+ * all its bits to zero.
+ *
+ * \param num Number of items.
+ * \param size Item size in bytes.
+ * \param alignment Memory alignment in bytes. Must be a power of two.
+ *
+ * \retval NULL Allocation failed.
+ * \return Pointer to a newly-allocated block of memory.
+ */
+void *fwk_mm_calloc_aligned(size_t num, size_t size, unsigned int alignment);
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* FWK_MM_H */
diff --git a/framework/include/fwk_module.h b/framework/include/fwk_module.h
new file mode 100644
index 0000000..b981aa2
--- /dev/null
+++ b/framework/include/fwk_module.h
@@ -0,0 +1,459 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Module definitions.
+ */
+
+#ifndef FWK_MODULE_H
+#define FWK_MODULE_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <fwk_element.h>
+#include <fwk_event.h>
+#include <fwk_id.h>
+
+/*!
+ * \addtogroup GroupLibFramework Framework
+ * @{
+ */
+
+/*!
+ * \defgroup GroupModule Modules
+ * @{
+ */
+
+/*!
+ * \brief Module types.
+ */
+enum fwk_module_type {
+ /*! Hardware Abstraction Layer */
+ FWK_MODULE_TYPE_HAL,
+ /*! Device driver */
+ FWK_MODULE_TYPE_DRIVER,
+ /*! Protocol */
+ FWK_MODULE_TYPE_PROTOCOL,
+ /*! Service provider */
+ FWK_MODULE_TYPE_SERVICE,
+ /*! The number of defined module types */
+ FWK_MODULE_TYPE_COUNT
+};
+
+/*!
+ * \brief Module or element state flags.
+ */
+enum fwk_module_state {
+ /*! The module or element has not yet been initialized */
+ FWK_MODULE_STATE_UNINITIALIZED = 0,
+
+ /*! The module or element has been initialized successfully */
+ FWK_MODULE_STATE_INITIALIZED,
+
+ /*! The module or element has bound successfully */
+ FWK_MODULE_STATE_BOUND,
+
+ /*! The module or element has started successfully */
+ FWK_MODULE_STATE_STARTED,
+
+ /*! The module or element is suspended */
+ FWK_MODULE_STATE_SUSPENDED,
+
+ /*! The number of defined module or element states */
+ FWK_MODULE_STATE_COUNT
+};
+
+/*!
+ * \brief Module descriptor.
+ */
+struct fwk_module {
+ /*! Module name */
+ const char *name;
+
+ /*! Module type */
+ enum fwk_module_type type;
+
+ /*! Number of APIs defined by the module */
+ unsigned int api_count;
+
+ /*! Number of events defined by the module */
+ unsigned int event_count;
+
+ #ifdef BUILD_HAS_NOTIFICATION
+ /*! Number of notifications defined by the module */
+ unsigned int notification_count;
+ #endif
+
+ /*!
+ * \brief Pointer to the module initialization function.
+ *
+ * \details This function is invoked during the initialization stage, which
+ * is the first pre-runtime stage. It is called before any module
+ * element initialization is performed. This function must not make any
+ * assumptions about the initialization state of other modules or
+ * their elements.
+ *
+ * \note This function is \b mandatory and must be implemented by all
+ * modules.
+ *
+ * \param module_id Identifier of the module being initialized.
+ * \param element_count Number of module elements.
+ * \param data Module-specific configuration data.
+ *
+ * \retval FWK_SUCCESS The module was initialized successfully.
+ * \retval FWK_E_NOMEM A memory allocation failed.
+ * \return One of the other module-defined error codes.
+ */
+ int (*init)(fwk_id_t module_id, unsigned int element_count,
+ const void *data);
+
+ /*!
+ * \brief Pointer to the module element initialization function.
+ *
+ * \details This function is invoked once for each module element during the
+ * initialization stage. Module element initialization occurs after the
+ * call to the module initialization function and before the call to
+ * the module post-initialization function.
+ *
+ * Elements are initialized in the order they are declared in the
+ * module configuration data. The initialization function must not
+ * make any assumptions about the initialization state of other modules
+ * and their elements.
+ *
+ * \note This function is \b mandatory for modules with elements.
+ *
+ * \param element_id Identifier of the module element being initialized.
+ * \param sub_element_count Number of sub-elements.
+ * \param data Element-specific configuration data.
+ *
+ * \retval FWK_SUCCESS The element was initialized successfully.
+ * \retval FWK_E_NOMEM A memory allocation failed.
+ * \return One of the other module-defined error codes.
+ */
+ int (*element_init)(fwk_id_t element_id, unsigned int sub_element_count,
+ const void *data);
+
+ /*!
+ * \brief Pointer to the module post-initialization function.
+ *
+ * \details This function is invoked to finalize the module initialization
+ * once the module and its elements have been successfully initialized.
+ *
+ * The framework does not mandate a particular purpose for this
+ * function. It may be used to deal with dependencies between elements,
+ * for example.
+ *
+ * \note This function is \b optional.
+ *
+ * \param module_id Identifier of the module element.
+ *
+ * \retval FWK_SUCCESS The module post-initialization was successful.
+ * \retval FWK_E_NOMEM A memory allocation failed.
+ * \return One of the other module-defined error codes.
+ */
+ int (*post_init)(fwk_id_t module_id);
+
+ /*!
+ * \brief Pointer to the bind function.
+ *
+ * \details This function provides a place for the module and its elements
+ * to bind to other modules and/or elements. Bindings expose APIs
+ * to other modules and elements, and form the core building block of
+ * cross-module interaction.
+ *
+ * This function is called by the framework during the bind stage of
+ * the pre-runtime phase. It is called once over the module and then
+ * over all its elements in the initial binding, and a second time to
+ * allow entities with more complex binding strategies to finalize
+ * their bindings.
+ *
+ * The first round of calls may be used by a module or element to
+ * discover the entities it needs to bind to, and the second round to
+ * bind to them.
+ *
+ * \note This function is \b optional.
+ *
+ * \param id Identifier of the module or element to bind.
+ * \param round Current call round, \c 0 for the first round \c 1 for
+ * the second round.
+ *
+ * \retval FWK_SUCCESS The binding was successful.
+ * \retval FWK_E_ACCESS At least one binding request was rejected.
+ * \retval FWK_E_NOMEM A memory allocation failed.
+ * \return One of the other module-defined error codes.
+ */
+ int (*bind)(fwk_id_t id, unsigned int round);
+
+ /*!
+ * \brief Pointer to the start function.
+ *
+ * \details This function is called by the framework for the module and then
+ * for all of its elements during the start stage. Elements are
+ * started in the order they are declared in the module configuration
+ * data.
+ *
+ * The framework does not mandate a particular purpose for this
+ * function. It may be used to perform any final processing of the
+ * module and its elements before entering the runtime phase. A
+ * possible example of this would be a driver module that must enable
+ * an interrupt after it has completed initialization.
+ *
+ * \note This function is \b optional.
+ *
+ * \param id Identifier of the module or element to start.
+ *
+ * \retval FWK_SUCCESS The module or element was successfully started.
+ * \return One of the other module-defined error codes.
+ */
+ int (*start)(fwk_id_t id);
+
+ /*!
+ * \brief Pointer to the bind request processing function.
+ *
+ * \details This function is called by the framework when it receives a
+ * request from another entity to bind to the module, or an element of
+ * the module. It can be called only during the initialization phase
+ * once 'target_id' has been initialized and during the binding phase.
+ *
+ * It allows access control, for instance, to deny certain entities
+ * access. Furthermore, in the case where the module includes different
+ * implementations of an API, it allows the module to select the
+ * implementation to provide based on the requesting entity and/or
+ * target entity.
+ *
+ * \note This function is \b optional.
+ *
+ * \param source_id Identifier of the module or element making the
+ * bind request.
+ * \param target_id Identifier of the module or element to bind to.
+ * \param api_id Identifier of the API to return.
+ * \param [out] api Pointer to the API implementation to be used by the
+ * requester.
+ *
+ * \retval FWK_SUCCESS The binding request was accepted by the module or
+ * element.
+ * \retval FWK_E_ACCESS The binding request was rejected by the module or
+ * element.
+ * \return One of the other module-defined error codes.
+ */
+ int (*process_bind_request)(fwk_id_t source_id, fwk_id_t target_id,
+ fwk_id_t api_id, const void **api);
+
+ /*!
+ * \brief Process an event.
+ *
+ * \details This function is called by the framework for events targeting
+ * the module or one of its elements.
+ *
+ * \note This function is \b optional. If a response event is expected and
+ * the \ref fwk_event.is_delayed_response flag is not set by the
+ * processing function then the response event is immediately and
+ * automatically sent to the event's source by the framework. If
+ * however the \ref fwk_event.is_delayed_response flag is set by the
+ * processing function then the framework does not send the response
+ * event and it is the responsability of the event's target to send it
+ * at some point.
+ *
+ * \param event Pointer to the event to be processed.
+ * \param [out] resp_event The response event to the provided event if
+ * any.
+ *
+ * \retval FWK_SUCCESS The event was processed successfully.
+ * \return One of the other module-defined error codes.
+ */
+ int (*process_event)(const struct fwk_event *event,
+ struct fwk_event *resp_event);
+
+ /*!
+ * \brief Process a notification.
+ *
+ * \details This function is called by the framework when a notification is
+ * received by a module, element, or sub-element.
+ *
+ * \note This function is \b optional.
+ *
+ * \param event Pointer to the notification event to be processed.
+ * \param [out] resp_event The response event to the provided event, if
+ * any.
+ *
+ * \retval FWK_SUCCESS The notification was processed successfully.
+ * \return One of the other module-defined error codes.
+ */
+ int (*process_notification)(const struct fwk_event *event,
+ struct fwk_event *resp_event);
+};
+
+/*!
+ * \brief Module configuration.
+ */
+struct fwk_module_config {
+ /*!
+ * \brief Pointer to the function to get the table of element descriptions.
+ *
+ * \param module_id Identifier of the module.
+ *
+ * \details The table of module element descriptions ends with an invalid
+ * element description where the pointer to the element name is
+ * equal to NULL.
+ *
+ * \warning The framework does not copy the element description data and
+ * keep a pointer to the ones returned by this function. Pointers
+ * returned by this function must thus points to data with static
+ * storage or data stored in memory allocated from the memory
+ * management component.
+ */
+ const struct fwk_element *(*get_element_table)(fwk_id_t module_id);
+
+ /*! Pointer to the module-specific configuration data */
+ const void *data;
+};
+
+/*!
+ * \brief Check if an identifier refers to a valid module.
+ *
+ * \param id Identifier to be checked.
+ *
+ * \retval true The identifier refers to a valid module.
+ * \retval false The identifier does not refer to a valid module.
+ */
+bool fwk_module_is_valid_module_id(fwk_id_t id);
+
+/*!
+ * \brief Check if an identifier refers to a valid element.
+ *
+ * \param id Identifier to be checked.
+ *
+ * \retval true The identifier refers to a valid element.
+ * \retval false The identifier does not refer to a valid element.
+ */
+bool fwk_module_is_valid_element_id(fwk_id_t id);
+
+/*!
+ * \brief Check if an identifier refers to a valid sub-element.
+ *
+ * \param id Identifier to be checked.
+ *
+ * \retval true The identifier refers to a valid sub-element.
+ * \retval false The identifier does not refer to a valid sub-element.
+ */
+bool fwk_module_is_valid_sub_element_id(fwk_id_t id);
+
+/*!
+ * \brief Check if an identifier refers to a valid module, element or
+ * sub-element.
+ *
+ * \param id Identifier to be checked.
+ *
+ * \retval true The identifier refers to a valid module, element or
+ * sub-element.
+ * \retval false The identifier does not refer to a valid module, element or
+ * sub-element.
+ */
+bool fwk_module_is_valid_entity_id(fwk_id_t id);
+
+/*!
+ * \brief Check if an identifier refers to a valid API.
+ *
+ * \param id Identifier to be checked.
+ *
+ * \retval true The identifier refers to a valid API.
+ * \retval false The identifier does not refer to a valid API.
+ */
+bool fwk_module_is_valid_api_id(fwk_id_t id);
+
+/*!
+ * \brief Check if an identifier refers to a valid event.
+ *
+ * \param id Identifier to be checked.
+ *
+ * \retval true The identifier refers to a valid event.
+ * \retval false The identifier does not refer to a valid event.
+ */
+bool fwk_module_is_valid_event_id(fwk_id_t id);
+
+/*!
+ * \brief Check if an identifier refers to a valid notification.
+ *
+ * \param id Identifier to be checked.
+ *
+ * \retval true The identifier refers to a valid notification.
+ * \retval false The identifier does not refer to a valid notification.
+ */
+bool fwk_module_is_valid_notification_id(fwk_id_t id);
+
+/*!
+ * \brief Get the number of elements within a module.
+ *
+ * \param module_id Identifier of the module.
+ *
+ * \retval FWK_E_PARAM The identifier of the module is invalid.
+ * \return Number of module elements.
+ */
+int fwk_module_get_element_count(fwk_id_t module_id);
+
+/*!
+ * \brief Get the name of a module or element.
+ *
+ * \param id Identifier of the module or element.
+ *
+ * \return The pointer to the module or element name, NULL if the identifier is
+ * not valid.
+ */
+const char *fwk_module_get_name(fwk_id_t id);
+
+/*!
+ * \brief Get the configuration data of a module or element.
+ *
+ * \param id Identifier of the module or element.
+ *
+ * \return The pointer to the module/element-specific configuration data, NULL
+ * if the identifier is not valid.
+ */
+const void *fwk_module_get_data(fwk_id_t id);
+
+/*!
+ * \brief Check whether a module or element is in a state where it can accept
+ * calls to one of its APIs.
+ *
+ * \param id Identifier of a module or element.
+ *
+ * \retval FWK_SUCCESS The module or element can service API calls.
+ * \retval FWK_E_PARAM The identifier is invalid.
+ * \retval FWK_E_STATE The module or the element is not initialized or
+ * suspended.
+ */
+int fwk_module_check_call(fwk_id_t id);
+
+/*!
+ * \brief Bind to an API of a module or an element.
+ *
+ * \details The framework will accept the bind request in one of the two
+ * following cases:
+ * 1) The execution is at the pre-runtime initialization stage and the
+ * entity 'target_id' has already been initialized.
+ * 2) The execution is at the pre-runtime binding stage.
+ *
+ * \param target_id Identifier of the module or element to bind to.
+ * \param api_id Identifier of the API to return an implementation of.
+ * \param api [out] Pointer to storage for the pointer to the API.
+ *
+ * \retval FWK_SUCCESS The API was returned.
+ * \retval FWK_E_PARAM A least one of the identifiers is invalid.
+ * \retval FWK_E_STATE Call outside of the pre-runtime bind stage.
+ * \retval FWK_E_ACCESS The access to the API was refused.
+ * \retval FWK_E_HANDLER The returned API pointer is invalid (NULL).
+ */
+int fwk_module_bind(fwk_id_t target_id, fwk_id_t api_id, const void *api);
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* FWK_MODULE_H */
diff --git a/framework/include/fwk_multi_thread.h b/framework/include/fwk_multi_thread.h
new file mode 100644
index 0000000..7190fc1
--- /dev/null
+++ b/framework/include/fwk_multi_thread.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Thread Management.
+ */
+
+
+#ifndef FWK_MULTI_THREAD_H
+#define FWK_MULTI_THREAD_H
+
+#include <fwk_event.h>
+#include <fwk_id.h>
+#include <fwk_thread.h>
+
+/*!
+ * \addtogroup GroupLibFramework Framework
+ * @{
+ */
+
+/*!
+ * \defgroup GroupThread Threading
+ *
+ * @{
+ */
+
+/*!
+ * \brief Create a thread for a module or element
+ *
+ * \details Create a thread for a module or element as well as its event queue.
+ * Modules and elements bound to the module or element can then place
+ * events in its queue using the 'fwk_thread_put_event()' function from
+ * the framework API.
+ *
+ * Events will be processed by the created thread when the framework calls
+ * the module or element's 'process_event()' function. Note that the thread
+ * will process only a single event from the queue each time the
+ * 'process_event()' function is called.
+ *
+ * \param id Identifier of the module or element to create the thread for.
+ *
+ * \retval FWK_SUCCESS The module/element thread was created.
+ * \retval FWK_E_PARAM The identifier is not a valid module or element
+ * identifier.
+ * \retval FWK_E_INIT The thread framework component is not initialized.
+ * \retval FWK_E_STATE The execution has already started, or it is not possible
+ * to create a thread anymore, or a thread has already been created for the
+ * given module or element.
+ * \retval FWK_E_NOMEM A memory allocation failed.
+ * \retval FWK_E_OS Operating system error.
+ */
+int fwk_thread_create(fwk_id_t id);
+
+/*!
+ * \brief Put an event in a module or element thread queue and wait for it to
+ * be processed.
+ *
+ * \details This framework API function can only be called from a module or
+ * element's thread. The calling thread is suspended until the event
+ * has been completely processed. As a consequence, this function cannot
+ * be called during the pre-runtime phases.
+ *
+ * The identifier of the event's source is filled in by the framework
+ * with the identifier of the entity calling the function. Thus there is
+ * no need for the caller to fill this event's field in.
+ *
+ * The event identifier and target identifier are validated and must
+ * belong to the same module.
+ *
+ * \param event Event to put into the queue for processing. Must not be \c NULL.
+ * \param resp_event Pointer to storage for the response event. Must not be \c
+ * NULL.
+ *
+ * \retval FWK_SUCCESS The event was successfully processed.
+ * \retval FWK_E_STATE The execution is not started.
+ * \retval FWK_E_PARAM One or more of the parameters were invalid.
+ * \retval FWK_E_PARAM One or more fields in the \p event parameter were
+ * invalid.
+ * \retval FWK_E_ACCESS The API is called from an ISR, called from the common
+ * thread, or the event targets the calling thread.
+ */
+int fwk_thread_put_event_and_wait(struct fwk_event *event,
+ struct fwk_event *resp_event);
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* FWK_MULTI_THREAD_H */
diff --git a/framework/include/fwk_notification.h b/framework/include/fwk_notification.h
new file mode 100644
index 0000000..06d42ab
--- /dev/null
+++ b/framework/include/fwk_notification.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Notification definitions.
+ */
+
+#ifndef FWK_NOTIFICATION_H
+#define FWK_NOTIFICATION_H
+
+#include <fwk_event.h>
+#include <fwk_id.h>
+
+/*!
+ * \ingroup GroupLibFramework
+ * \defgroup GroupNotification Notifications
+ * @{
+ */
+
+/*!
+ * \brief Subscribe to a notification.
+ *
+ * \param notification_id Identifier of the notification.
+ * \param source_id Notification source identifier.
+ * \param target_id Notification target identifier.
+ *
+ * \retval FWK_SUCCESS The subscription was successful.
+ * \retval FWK_E_INIT The notification component has not been initialized.
+ * \retval FWK_E_HANDLER The function was called from an interrupt handler.
+ * \retval FWK_E_PARAM One or more identifiers were invalid.
+ * \retval FWK_E_STATE The entity \p target_id has already subscribed to the
+ * notification \p notification_id from the entity \p source_id.
+ * \retval FWK_E_NOMEM The maximum number of subscriptions has been reached.
+ */
+int fwk_notification_subscribe(fwk_id_t notification_id, fwk_id_t source_id,
+ fwk_id_t target_id);
+
+/*!
+ * \brief Unsubscribe from a notification.
+ *
+ * \param notification_id Identifier of the notification.
+ * \param source_id Notification source identifier.
+ * \param target_id Notification target identifier.
+ *
+ * \retval FWK_SUCCESS The subscription was successfully cancelled.
+ * \retval FWK_E_INIT The notification component has not been initialized.
+ * \retval FWK_E_HANDLER The function was called from an interrupt handler.
+ * \retval FWK_E_PARAM One or more identifiers were invalid.
+ * \retval FWK_E_STATE The entity \p target_id has not subscribed to the
+ * notification \p notification_id from the entity \p source_id.
+ */
+int fwk_notification_unsubscribe(fwk_id_t notification_id, fwk_id_t source_id,
+ fwk_id_t target_id);
+
+/*!
+ * \brief Send a notification to all entities that are subscribed to it.
+ *
+ * \note During the runtime phase, if called from a thread, the
+ * identifier of the event source is populated automatically with the
+ * identifier of the calling entity.
+ *
+ * \param notification_event Pointer to the notification event. Must not be
+ * \c NULL.
+ * \param [out] count Number of notification events that were sent. Must not be
+ * \c NULL.
+ *
+ * \retval FWK_SUCCESS All subscribers were notified successfully.
+ * \retval FWK_E_INIT The notification component has not been initialized.
+ * \retval FWK_E_PARAM One of more parameters were invalid.
+ */
+int fwk_notification_notify(struct fwk_event *notification_event,
+ unsigned int *count);
+
+/*!
+ * @}
+ */
+
+#endif /* FWK_NOTIFICATION_H */
diff --git a/framework/include/fwk_slist.h b/framework/include/fwk_slist.h
new file mode 100644
index 0000000..a055348
--- /dev/null
+++ b/framework/include/fwk_slist.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Intrusive circular singly-linked list.
+ */
+
+#ifndef FWK_SLIST_H
+#define FWK_SLIST_H
+
+#include <stdbool.h>
+
+/*!
+ * \addtogroup GroupLibFramework Framework
+ * @{
+ */
+
+/*!
+ * \addtogroup GroupLinkedList Linked Lists
+ * @{
+ */
+
+/*!
+ * \brief Singly-linked list.
+ *
+ * \internal
+ * \note This structure can be safely used in the place of \ref fwk_slist_node.
+ */
+struct fwk_slist {
+ /*! Pointer to the list head */
+ struct fwk_slist_node *head;
+
+ /*! Pointer to the list tail */
+ struct fwk_slist_node *tail;
+};
+
+/*!
+ * \brief Singly-linked list node.
+ */
+struct fwk_slist_node {
+ /*! Pointer to the next node in the list */
+ struct fwk_slist_node *next;
+};
+
+/*!
+ * @cond
+ */
+
+/*
+ * Initialize a singly-linked list.
+ *
+ * For internal use only.
+ * See fwk_list_init(list, new) for the public interface.
+ */
+void __fwk_slist_init(struct fwk_slist *list);
+
+/*
+ * Retrieve the node at the head of a singly-linked list.
+ *
+ * For internal use only.
+ * See fwk_list_head(list) for the public interface.
+ */
+struct fwk_slist_node *__fwk_slist_head(const struct fwk_slist *list);
+
+/*
+ * Test whether a singly-linked list is empty or not.
+ *
+ * For internal use only.
+ * See fwk_list_is_empty(list) for the public interface.
+ */
+bool __fwk_slist_is_empty(const struct fwk_slist *list);
+
+/*
+ * Add a new node to the head of a singly-linked list.
+ *
+ * For internal use only.
+ * See fwk_list_push_head(list, new) for the public interface.
+ */
+void __fwk_slist_push_head(
+ struct fwk_slist *list,
+ struct fwk_slist_node *new);
+
+/*
+ * Add a new node to the end of a singly-linked list.
+ *
+ * For internal use only.
+ * See fwk_list_push_tail(list, new) for the public interface.
+ */
+void __fwk_slist_push_tail(
+ struct fwk_slist *list,
+ struct fwk_slist_node *new);
+
+/*
+ * Remove and return the head node from a singly-linked list.
+ *
+ * For internal use only.
+ * See fwk_list_pop_head(list) for the public interface.
+ */
+struct fwk_slist_node *__fwk_slist_pop_head(struct fwk_slist *list);
+
+/*
+ * Get the next node from a singly-linked list.
+ *
+ * For internal use only.
+ * See fwk_list_next(list, node) for the public interface.
+ */
+struct fwk_slist_node *__fwk_slist_next(
+ const struct fwk_slist *list,
+ const struct fwk_slist_node *node);
+
+/*
+ * Remove a node from a singly-linked list.
+ *
+ * For internal use only.
+ * See fwk_list_remove(list, node) for the public interface.
+ */
+void __fwk_slist_remove(
+ struct fwk_slist *list,
+ struct fwk_slist_node *node);
+
+/*
+ * Test if a node is in a singly-linked list.
+ *
+ * For internal use only.
+ * See fwk_list_contains(list, node) for the public interface.
+ */
+bool __fwk_slist_contains(
+ const struct fwk_slist *list,
+ const struct fwk_slist_node *node);
+
+/*!
+ * @endcond
+ */
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* FWK_SLIST_H */
diff --git a/framework/include/fwk_thread.h b/framework/include/fwk_thread.h
new file mode 100644
index 0000000..dd0251f
--- /dev/null
+++ b/framework/include/fwk_thread.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Thread definitions.
+ */
+
+#ifndef FWK_THREAD_H
+#define FWK_THREAD_H
+
+#include <fwk_event.h>
+
+/*!
+ * \addtogroup GroupLibFramework Framework
+ * @{
+ */
+
+/*!
+ * \defgroup GroupThread Threading
+ *
+ * @{
+ */
+
+/*!
+ * \brief Put an event in one of the event queues.
+ *
+ * \details The framework copies the event description into its internal data
+ * and so does not keep track of the pointer passed as a parameter.
+ *
+ * If the function is called from a thread the event is put into the
+ * targeted thread queue. Furthermore, in the runtime phase, the source
+ * identifier of the event is populated with the identifier of the caller,
+ * and it is therefore unnecessary for the caller to do so manually. Note
+ * that this does not occur in the pre-runtime phase.
+ *
+ * If the function is called from an ISR, the event is put in the ISR
+ * event queue.
+ *
+ * In both cases, the event identifier and target identifier are checked
+ * to be valid and to refer to the same module.
+ *
+ * In the case of a delayed response event, the event's 'is_response' flag
+ * must be set.
+ *
+ * If multi-threading is enabled, and \p event is a delayed response, the
+ * \ref fwk_event.cookie field of \p event must match that of the event
+ * it is responding to.
+ *
+ * \param event Pointer to the event to queue. Must not be \c NULL.
+ *
+ * \retval FWK_SUCCESS The event was queued.
+ * \retval FWK_E_INIT The thread framework component is not initialized.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ * \retval FWK_E_PARAM One or more fields in the \p event parameter were
+ * invalid.
+ * \retval FWK_E_OS Operating system error.
+ */
+int fwk_thread_put_event(struct fwk_event *event);
+
+/*!
+ * \brief Get a copy of a delayed response event.
+ *
+ * \details When a module or element delays a response as part of the processing
+ * of an event requiring a response, the framework automatically stores the
+ * delayed response as part of the module or element data internal to the
+ * framework. This function allows to get a copy of such delayed response
+ * event. The event is not removed from the module or element internal
+ * data.
+ *
+ * \param id Identifier of the module or element that delayed the response.
+ * \param cookie Cookie of the event which the response has been delayed
+ * for. The cookie identifies the response event among the response events
+ * the entity \p id may have delayed.
+ * \param event Pointer to storage for the copy of the response event.
+ *
+ * \retval FWK_SUCCESS The event copy was returned.
+ * \retval FWK_E_INIT The thread framework component is not initialized.
+ * \retval FWK_E_ACCESS The API is called from an ISR.
+ * \retval FWK_E_PARAM The identifier \p id is not a module or element
+ * identifier.
+ * \retval FWK_E_PARAM The cookie parameter does not match one of the cookies
+ * of the response events delayed by the module or element \p id.
+ * \retval FWK_E_PARAM The pointer \p event is equal to NULL.
+ */
+int fwk_thread_get_delayed_response(fwk_id_t id, uint32_t cookie,
+ struct fwk_event *event);
+
+/*!
+ * @}
+ */
+
+/*!
+ * @}
+ */
+
+#endif /* FWK_THREAD_H */
diff --git a/framework/include/internal/fwk_id.h b/framework/include/internal/fwk_id.h
new file mode 100644
index 0000000..08da426
--- /dev/null
+++ b/framework/include/internal/fwk_id.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+
+#ifndef FWK_INTERNAL_ID_H
+#define FWK_INTERNAL_ID_H
+
+/* Identifier type */
+enum __fwk_id_type {
+ /*
+ * Invalid variant.
+ *
+ * This type is used to catch some classes of missing-initialization errors,
+ * and should not be used to initialize new identifiers.
+ */
+ __FWK_ID_TYPE_INVALID,
+
+ /*
+ * None variant.
+ *
+ * This type is used when an id needs to be explicitly initialized to not
+ * refer to any entity.
+ */
+ __FWK_ID_TYPE_NONE,
+
+ /* Module */
+ __FWK_ID_TYPE_MODULE,
+
+ /* Element */
+ __FWK_ID_TYPE_ELEMENT,
+
+ /* Sub-element */
+ __FWK_ID_TYPE_SUB_ELEMENT,
+
+ /* API */
+ __FWK_ID_TYPE_API,
+
+ /* Event */
+ __FWK_ID_TYPE_EVENT,
+
+ /* Notification */
+ __FWK_ID_TYPE_NOTIFICATION,
+
+ /* Number of defined types */
+ __FWK_ID_TYPE_COUNT,
+};
+
+/*
+ * Generic identifier.
+ *
+ * This type should be treated as though it is a variant, but where the type
+ * switches on all the fields _after_ module_idx. The `type` and `module_idx`
+ * fields use the same mask in both variants, but prefer to access them through
+ * the \c common field in agnostic code.
+ *
+ * This identifier fits within the `uint32_t` type, and so should generally be
+ * passed by value.
+ */
+union __fwk_id {
+ uint32_t value; /* Integer value */
+
+ struct {
+ uint32_t type : 4; /* Identifier type */
+ uint32_t module_idx : 8; /* Module index */
+ uint32_t reserved : 20; /* Reserved */
+ } common; /* Common fields */
+
+ struct {
+ uint32_t type : 4; /* Identifier type */
+ uint32_t module_idx : 8; /* Module index */
+ uint32_t element_idx : 12; /* Element index */
+ uint32_t reserved : 8; /* Reserved */
+ } element; /* Element variant */
+
+ struct {
+ uint32_t type : 4; /* Identifier type */
+ uint32_t module_idx : 8; /* Module index */
+ uint32_t element_idx : 12; /* Element index */
+ uint32_t sub_element_idx : 8; /* Sub-element index */
+ } sub_element; /* Sub-element variant */
+
+ struct {
+ uint32_t type : 4; /* Identifier type */
+ uint32_t module_idx : 8; /* Module index */
+ uint32_t api_idx : 4; /* API index */
+ uint32_t reserved : 16; /* Reserved */
+ } api; /* API variant */
+
+ struct {
+ uint32_t type : 4; /* Identifier type */
+ uint32_t module_idx : 8; /* Module index */
+ uint32_t event_idx : 6; /* Event index */
+ uint32_t reserved : 14; /* Reserved */
+ } event; /* Event variant */
+
+ struct {
+ uint32_t type : 4; /* Identifier type */
+ uint32_t module_idx : 8; /* Module index */
+ uint32_t notification_idx : 6; /* Notification index */
+ uint32_t reserved : 14; /* Reserved */
+ } notification; /* Notification variant */
+};
+
+/*
+ * Print format helper structure.
+ *
+ * This structure is necessary to prevent the string buffer from decaying to
+ * a pointer when returned from __fwk_id_str(). This ensures the string
+ * buffer is kept in scope for the entire expression, as opposed to going out of
+ * scope once __fwk_id_str() returns.
+ */
+struct __fwk_id_fmt {
+ char str[20]; /* Identifier string representation */
+};
+
+/*
+ * Build the string representation of an identifier.
+ *
+ * \param id Identifier.
+ *
+ * \return Buffer structure containing the string representation of the
+ * identifier.
+ */
+struct __fwk_id_fmt __fwk_id_str(union __fwk_id id);
+
+#endif /* FWK_INTERNAL_ID_H */
diff --git a/framework/include/internal/fwk_module.h b/framework/include/internal/fwk_module.h
new file mode 100644
index 0000000..6b8bc4f
--- /dev/null
+++ b/framework/include/internal/fwk_module.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Framework private module definitions.
+ */
+
+#ifndef FWK_INTERNAL_MODULE_H
+#define FWK_INTERNAL_MODULE_H
+
+#include <stddef.h>
+#include <fwk_module.h>
+#include <internal/fwk_notification.h>
+
+/*
+ * Module context.
+ */
+struct fwk_module_ctx {
+ /* Module identifier */
+ fwk_id_t id;
+
+ /* Module state */
+ enum fwk_module_state state;
+
+ /* Module description */
+ const struct fwk_module *desc;
+
+ /* Module configuration */
+ const struct fwk_module_config *config;
+
+ /* Number of elements */
+ size_t element_count;
+
+ /* Table of element contexts */
+ struct fwk_element_ctx *element_ctx_table;
+
+ /* Module thread context */
+ struct __fwk_thread_ctx *thread_ctx;
+
+ #ifdef BUILD_HAS_NOTIFICATION
+ /*
+ * Table of notification subscription lists. One list per type of
+ * notification defined by the module.
+ */
+ struct fwk_dlist *subscription_dlist_table;
+ #endif
+
+ /* List of delayed response events */
+ struct fwk_slist delayed_response_list;
+};
+
+/*
+ * Element context.
+ */
+struct fwk_element_ctx {
+ /* Element state */
+ enum fwk_module_state state;
+
+ /* Element description */
+ const struct fwk_element *desc;
+
+ /* Number of sub-elements */
+ size_t sub_element_count;
+
+ /* Element thread context */
+ struct __fwk_thread_ctx *thread_ctx;
+
+ #ifdef BUILD_HAS_NOTIFICATION
+ /*
+ * Table of notification subscription lists. One list per type of
+ * notification defined by the element's module.
+ */
+ struct fwk_dlist *subscription_dlist_table;
+ #endif
+
+ /* List of delayed response events */
+ struct fwk_slist delayed_response_list;
+};
+
+/*
+ * \brief Initialize the module framework component.
+ *
+ * \retval FWK_SUCCESS The module framework component was initialized.
+ * \retval FWK_E_INIT The module framework component was already initialized.
+ * \return One of the other framework error codes depending on the
+ * irrecoverable error that occurred.
+ */
+int __fwk_module_init(void);
+
+/*
+ * \brief Get a pointer to the context of a module or element.
+ *
+ * \param id Module or element identifier.
+ * If the identifier provided does not refer to a valid module or element,
+ * the behaviour of this function is undefined.
+ *
+ * \return Pointer to the module context.
+ */
+struct fwk_module_ctx *__fwk_module_get_ctx(fwk_id_t id);
+
+/*
+ * \brief Get the state of a module or element.
+ *
+ * \param id Module, element or sub-element identifier.
+ * \param state [out] State of the module or element.
+ *
+ * \retval FWK_SUCCESS The state was returned.
+ * \retval FWK_E_PARAM One or more parameters were invalid.
+ */
+int __fwk_module_get_state(fwk_id_t id, enum fwk_module_state *state);
+
+/*
+ * \brief Get a pointer to the framework context of an element.
+ *
+ * \param element_id Element identifier.
+ * If the identifier provided does not refer to a valid element, the
+ * behaviour of this function is undefined.
+ *
+ * \return Pointer to the element context.
+ */
+struct fwk_element_ctx *__fwk_module_get_element_ctx(fwk_id_t element_id);
+
+/*
+ * \brief Reset the module framework component.
+ *
+ * \note Only for testing.
+ */
+void __fwk_module_reset(void);
+
+#endif /* FWK_INTERNAL_MODULE_H */
diff --git a/framework/include/internal/fwk_multi_thread.h b/framework/include/internal/fwk_multi_thread.h
new file mode 100644
index 0000000..9b60a56
--- /dev/null
+++ b/framework/include/internal/fwk_multi_thread.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef FWK_INTERNAL_MULTI_THREAD_H
+#define FWK_INTERNAL_MULTI_THREAD_H
+
+#include <stdbool.h>
+#include <fwk_event.h>
+#include <fwk_id.h>
+#include <fwk_list.h>
+#include <fwk_multi_thread.h>
+#include <internal/fwk_thread.h>
+#include <cmsis_os2.h>
+
+/*
+ * Module/element thread context.
+ */
+struct __fwk_thread_ctx {
+ /*
+ * Link for the queue of ready threads, threads having at least one event
+ * in their event queue and ready to process the first one in their queue.
+ * Those threads are not waiting for the completion of the processing of
+ * an event by another thread.
+ */
+ struct fwk_slist_node slist_node;
+
+ /* Identifier of the module or element that created the thread */
+ fwk_id_t id;
+
+ /*
+ * Identifier given by the underlying OS to the thread created by the
+ * module or element.
+ */
+ osThreadId_t os_thread_id;
+
+ /* Thread queue of events */
+ struct fwk_slist event_queue;
+
+ /*
+ * Flag indicating if the thread is waiting for the completion of the
+ * processing of an event by another thread (true) or not (false). A thread
+ * enters this waiting state when calling the fwk_thread_put_and_wait()
+ * framework API requesting another thread to process an event. A thread
+ * leaves this waiting state when the processing of the aforementioned
+ * event is completed. The execution of the thread is then resumed
+ * immediately: no other event processing occurs between the end of the
+ * event processing and the waiting thread execution being resumed.
+ */
+ bool waiting_event_processing_completion;
+
+ /*
+ * Pointer to storage for the response event when waiting for the
+ * completion of the processing of an event by another thread. This field
+ * has a valid value if and only if the flag
+ * 'waiting_event_processing_completion' is equal to 'true'. The storage
+ * is allocated by the module or element having the identifier 'id'.
+ * The pointer to the storage is saved in the present context to be
+ * passed in to the 'process_event' framework API of the module/element
+ * processing the event which an event response is expected from.
+ */
+ struct fwk_event *response_event;
+};
+
+/*
+ * Multi-thread component context. Exposed for testing only.
+ */
+struct __fwk_multi_thread_ctx {
+ /* Initialization completed flag */
+ bool initialized;
+
+ /* Execution running flag */
+ bool running;
+
+ /* Waiting for an ISR event flag */
+ bool waiting_for_isr_event;
+
+ /* Common default thread context */
+ struct __fwk_thread_ctx common_thread_ctx;
+
+ /* Context of the thread being currently executed */
+ struct __fwk_thread_ctx *current_thread_ctx;
+
+ /* Event being currently processed */
+ struct fwk_event *current_event;
+
+ /*
+ * Counter used to generate event cookies.
+ */
+ uint32_t event_cookie_counter;
+
+ /*
+ * Queue of free events, event structures free to be filled in and linked
+ * to the queue of ISR events or to one of the thread event queues.
+ */
+ struct fwk_slist event_free_queue;
+
+ /*
+ * Queue of events generated by ISRs and not dispatched yet to the
+ * threads.
+ */
+ struct fwk_slist event_isr_queue;
+
+ /*
+ * Queue of the threads with pending events in their queue and not waiting
+ * for the completion of the processing of an event following the call to
+ * the fwk_thread_put_event_and_wait() interface function. Those threads are
+ * ready to execute as soon as the CPU becomes available for them.
+ */
+ struct fwk_slist thread_ready_queue;
+};
+
+/*
+ * \brief Get multi thread component context.
+ *
+ * \note Only for testing.
+ */
+struct __fwk_multi_thread_ctx *__fwk_multi_thread_get_ctx(void);
+
+#endif /* FWK_INTERNAL_MULTI_THREAD_H */
diff --git a/framework/include/internal/fwk_notification.h b/framework/include/internal/fwk_notification.h
new file mode 100644
index 0000000..a0d9c34
--- /dev/null
+++ b/framework/include/internal/fwk_notification.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef FWK_INTERNAL_NOTIFICATION_H
+#define FWK_INTERNAL_NOTIFICATION_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <fwk_list.h>
+#include <fwk_notification.h>
+
+struct __fwk_notification_subscription {
+ struct fwk_dlist_node dlist_node;
+
+ /* Identifier of the notification source entity. */
+ fwk_id_t source_id;
+
+ /* Identifier of the notification target entity. */
+ fwk_id_t target_id;
+};
+
+/*
+ * \brief Initialize the notification framework component.
+ *
+ * \param notification_count The maximum number of notification subscriptions at
+ * any time.
+ *
+ * \retval FWK_SUCCESS The notification framework component was initialized.
+ * \retval FWK_E_PARAM The maximum number of notification subscriptions is equal
+ * to zero.
+ * \retval FWK_E_NOMEM Insufficient memory available to allocate the
+ * notification subscription.
+ */
+int __fwk_notification_init(size_t notification_count);
+
+/*
+ * \brief Reset the notification framework component.
+ *
+ * \note Only for testing.
+ */
+void __fwk_notification_reset(void);
+
+#endif /* FWK_INTERNAL_NOTIFICATION_H */
diff --git a/framework/include/internal/fwk_single_thread.h b/framework/include/internal/fwk_single_thread.h
new file mode 100644
index 0000000..0bc3be0
--- /dev/null
+++ b/framework/include/internal/fwk_single_thread.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+#ifndef FWK_INTERNAL_SINGLE_THREAD_H
+#define FWK_INTERNAL_SINGLE_THREAD_H
+
+#include <stdbool.h>
+#include <fwk_event.h>
+#include <fwk_list.h>
+
+/*
+ * Thread component context. Exposed for testing purposes only.
+ */
+
+struct __fwk_thread_ctx {
+ /* Thread framework component initialization completed flag */
+ bool initialized;
+
+ /*
+ * Queue of event structures that are free to be filled in and linked
+ * to the event queue or the ISR event queue.
+ */
+ struct fwk_slist free_event_queue;
+
+ /* Queue of events, generated by ISRs, that are awaiting processing */
+ struct fwk_slist isr_event_queue;
+
+ /* Queue of events that are awaiting processing */
+ struct fwk_slist event_queue;
+
+ /* The event currently being processed */
+ struct fwk_event *current_event;
+};
+
+/*
+ * \brief Get thread component context.
+ *
+ * \note Only for testing.
+ */
+struct __fwk_thread_ctx *__fwk_thread_get_ctx(void);
+
+#endif /* FWK_INTERNAL_SINGLE_THREAD_H */
diff --git a/framework/include/internal/fwk_thread.h b/framework/include/internal/fwk_thread.h
new file mode 100644
index 0000000..e33b9bb
--- /dev/null
+++ b/framework/include/internal/fwk_thread.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef FWK_INTERNAL_THREAD_H
+#define FWK_INTERNAL_THREAD_H
+
+#include <stddef.h>
+#include <stdnoreturn.h>
+#include <fwk_event.h>
+#include <fwk_thread.h>
+
+/*
+ * \brief Initialize the thread framework component.
+ *
+ * \param event_count The maximum number of events in all queues at all time.
+ *
+ * \retval FWK_SUCCESS The thread framework component was initialized.
+ * \retval FWK_E_NOMEM Insufficient memory available for event queues.
+ */
+int __fwk_thread_init(size_t event_count);
+
+/*
+ * \brief Begin waiting for and processing events raised by modules and
+ * interrupt handlers.
+ *
+ * \return The function does not return.
+ */
+noreturn void __fwk_thread_run(void);
+
+/*
+ * \brief Get the event being currently processed.
+ *
+ * \return The event being currently processed, or \c NULL if event processing
+ * has not yet begun.
+ */
+const struct fwk_event *__fwk_thread_get_current_event(void);
+
+/*
+ * \brief Put a notification event in one of the event queues.
+ *
+ * \details The thread component copies the notification event description into
+ * its internal data and so does not keep track of the pointer passed as a
+ * parameter.
+ *
+ * If the function is called from a thread, the event is put into the
+ * targeted thread queue.
+ *
+ * If the function is called from an ISR, the validity of the event source
+ * identifier is checked and the event is put in the ISR event queue.
+ *
+ * \param event Pointer to the notification event to queue.
+ *
+ * \retval FWK_SUCCESS The event was queued.
+ * \retval FWK_E_PARAM The source identifier is not valid.
+ * \retval FWK_E_NOMEM No memory space to copy the event data.
+ */
+int __fwk_thread_put_notification(struct fwk_event *event);
+
+#endif /* FWK_INTERNAL_THREAD_H */
diff --git a/framework/src/Makefile b/framework/src/Makefile
new file mode 100644
index 0000000..a7b3644
--- /dev/null
+++ b/framework/src/Makefile
@@ -0,0 +1,29 @@
+#
+# Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+BS_LIB_NAME := framework
+
+BS_LIB_SOURCES += fwk_arch.c
+BS_LIB_SOURCES += fwk_assert.c
+BS_LIB_SOURCES += fwk_dlist.c
+BS_LIB_SOURCES += fwk_id.c
+BS_LIB_SOURCES += fwk_interrupt.c
+BS_LIB_SOURCES += fwk_mm.c
+BS_LIB_SOURCES += fwk_module.c
+BS_LIB_SOURCES += fwk_slist.c
+ifeq ($(BUILD_HAS_MULTITHREADING),yes)
+ BS_LIB_SOURCES += fwk_multi_thread.c
+else
+ BS_LIB_SOURCES += fwk_thread.c
+endif
+ifeq ($(BUILD_HAS_NOTIFICATION),yes)
+ BS_LIB_SOURCES += fwk_notification.c
+endif
+
+BS_LIB_INCLUDES += $(ARCH_DIR)/include
+BS_LIB_INCLUDES += $(FWK_DIR)/include
+
+include $(BS_DIR)/lib.mk
diff --git a/framework/src/fwk_arch.c b/framework/src/fwk_arch.c
new file mode 100644
index 0000000..3360a16
--- /dev/null
+++ b/framework/src/fwk_arch.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Framework API for the architecture layer.
+ */
+
+#include <fwk_arch.h>
+#include <fwk_errno.h>
+#include <fwk_host.h>
+#include <fwk_mm.h>
+#include <internal/fwk_module.h>
+
+extern int fwk_mm_init(uintptr_t start, size_t size);
+extern int fwk_interrupt_init(const struct fwk_arch_interrupt_driver *driver);
+
+static int mm_init(int (*mm_init_handler)(struct fwk_arch_mm_data *data))
+{
+ int status;
+ struct fwk_arch_mm_data data;
+
+ /*
+ * Retrieve a description of the memory area used for dynamic memory
+ * allocation from the architecture layer.
+ */
+ status = mm_init_handler(&data);
+ if (status != FWK_SUCCESS)
+ return FWK_E_PANIC;
+
+ /* Initialize the memory management component */
+ status = fwk_mm_init(data.start, data.size);
+ if (status != FWK_SUCCESS)
+ return FWK_E_PANIC;
+
+ return FWK_SUCCESS;
+}
+
+static int interrupt_init(
+ int (*interrupt_init_handler)(struct fwk_arch_interrupt_driver **driver))
+{
+ int status;
+ struct fwk_arch_interrupt_driver *driver;
+
+ /*
+ * Retrieve a pointer to the interrupt management driver from the
+ * architecture layer.
+ */
+ status = interrupt_init_handler(&driver);
+ if (status != FWK_SUCCESS)
+ return FWK_E_PANIC;
+
+ /* Initialize the interrupt management component */
+ status = fwk_interrupt_init(driver);
+ if (status != FWK_SUCCESS)
+ return FWK_E_PANIC;
+
+ return FWK_SUCCESS;
+}
+
+int fwk_arch_init(const struct fwk_arch_init_driver *driver)
+{
+ int status;
+
+ FWK_HOST_PRINT("[Framework] Initializing\n");
+
+ if (driver == NULL)
+ return FWK_E_PARAM;
+
+ if (driver->mm == NULL)
+ return FWK_E_PARAM;
+
+ /* Initialize memory management */
+ status = mm_init(driver->mm);
+ if (status != FWK_SUCCESS)
+ return FWK_E_PANIC;
+
+ /* Initialize interrupt management */
+ status = interrupt_init(driver->interrupt);
+ if (status != FWK_SUCCESS)
+ return FWK_E_PANIC;
+
+ /* Initialize modules */
+ status = __fwk_module_init();
+ if (status != FWK_SUCCESS)
+ return FWK_E_PANIC;
+
+ return FWK_SUCCESS;
+}
diff --git a/framework/src/fwk_assert.c b/framework/src/fwk_assert.c
new file mode 100644
index 0000000..31d7e5e
--- /dev/null
+++ b/framework/src/fwk_assert.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stdlib.h>
+#include <fwk_assert.h>
+#include <fwk_interrupt.h>
+
+void fwk_trap(void)
+{
+ #ifdef BUILD_MODE_DEBUG
+ /*
+ * Disable interrupts to ensure the program state cannot be disturbed by
+ * interrupt handlers.
+ */
+ fwk_interrupt_global_disable();
+
+ while (true)
+ continue;
+ #else
+ __builtin_trap();
+ #endif
+}
+
+void fwk_unreachable(void)
+{
+ #ifdef BUILD_MODE_DEBUG
+ fwk_trap();
+ #else
+ /*
+ * Let the optimizer know that anything after this point is unreachable.
+ */
+ __builtin_unreachable();
+ #endif
+}
+
+bool fwk_expect(bool condition)
+{
+ /* Failed expectations are potentially recoverable */
+ #if defined(BUILD_MODE_DEBUG) && !defined(BUILD_TESTS)
+ if (!condition)
+ fwk_trap();
+ #endif
+
+ return condition;
+}
+
+void fwk_assert(bool condition)
+{
+ /* Failed invariants are unrecoverable */
+ if (!condition)
+ fwk_unreachable();
+}
+
+#ifdef __NEWLIB__
+void __assert_func(const char *file, int line,
+ const char *function, const char *assertion)
+{
+ fwk_trap();
+}
+#endif
diff --git a/framework/src/fwk_dlist.c b/framework/src/fwk_dlist.c
new file mode 100644
index 0000000..142448d
--- /dev/null
+++ b/framework/src/fwk_dlist.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Intrusive circular doubly-linked list.
+ */
+
+#include <stddef.h>
+#include <fwk_assert.h>
+#include <fwk_list.h>
+
+void __fwk_dlist_push_head(
+ struct fwk_dlist *list,
+ struct fwk_dlist_node *new)
+{
+ assert(list != NULL);
+ assert(new != NULL);
+ fwk_expect(new->prev == NULL);
+
+ new->prev = (struct fwk_dlist_node *)list;
+ list->head->prev = new;
+
+ __fwk_slist_push_head(
+ (struct fwk_slist *)list,
+ (struct fwk_slist_node *)new);
+}
+
+void __fwk_dlist_push_tail(
+ struct fwk_dlist *list,
+ struct fwk_dlist_node *new)
+{
+ assert(list != NULL);
+ assert(new != NULL);
+ fwk_expect(new->prev == NULL);
+
+ new->prev = list->tail;
+
+ __fwk_slist_push_tail(
+ (struct fwk_slist *)list,
+ (struct fwk_slist_node *)new);
+}
+
+struct fwk_dlist_node *__fwk_dlist_pop_head(struct fwk_dlist *list)
+{
+ struct fwk_dlist_node *popped;
+
+ assert(list != NULL);
+
+ popped = (struct fwk_dlist_node *)__fwk_slist_pop_head(
+ (struct fwk_slist *)list);
+
+ list->head->prev = (struct fwk_dlist_node *)list;
+
+#ifdef BUILD_MODE_DEBUG
+ if (popped != NULL)
+ popped->prev = NULL;
+#endif
+
+ return popped;
+}
+
+void __fwk_dlist_remove(
+ struct fwk_dlist *list,
+ struct fwk_dlist_node *node)
+{
+ assert(list != NULL);
+ assert(node != NULL);
+ assert(node != (struct fwk_dlist_node *)list);
+ assert(node->prev != NULL);
+ assert(node->next != NULL);
+
+ assert(__fwk_slist_contains(
+ (struct fwk_slist *)list,
+ (struct fwk_slist_node *)node));
+
+ node->prev->next = node->next;
+ node->next->prev = node->prev;
+
+#ifdef BUILD_MODE_DEBUG
+ node->prev = NULL;
+ node->next = NULL;
+#endif
+}
+
+void __fwk_dlist_insert(
+ struct fwk_dlist *list,
+ struct fwk_dlist_node *restrict new,
+ struct fwk_dlist_node *restrict node)
+{
+ assert(list != NULL);
+ assert(new != NULL);
+ assert(new != node);
+ fwk_expect(new->next == NULL);
+ fwk_expect(new->prev == NULL);
+
+ if (node == NULL) {
+ __fwk_dlist_push_tail(list, new);
+
+ return;
+ }
+
+ assert(node->prev != NULL);
+ assert(node->next != NULL);
+
+ assert(__fwk_slist_contains(
+ (struct fwk_slist *)list,
+ (struct fwk_slist_node *)node));
+
+ node->prev->next = new;
+ new->prev = node->prev;
+ new->next = node;
+ node->prev = new;
+}
+
+static_assert(offsetof(struct fwk_dlist, head) ==
+ offsetof(struct fwk_slist, head),
+ "fwk_dlist::head not aligned with fwk_slist::head");
+
+static_assert(offsetof(struct fwk_dlist, tail) ==
+ offsetof(struct fwk_slist, tail),
+ "fwk_dlist::tail not aligned with fwk_slist::tail");
+
+static_assert(offsetof(struct fwk_dlist, head) ==
+ offsetof(struct fwk_slist_node, next),
+ "fwk_dlist::head not aligned with fwk_slist_node::next");
+
+static_assert(offsetof(struct fwk_dlist, head) ==
+ offsetof(struct fwk_dlist_node, next),
+ "fwk_dlist::head not aligned with fwk_dlist_node::next");
+
+static_assert(offsetof(struct fwk_dlist, tail) ==
+ offsetof(struct fwk_dlist_node, prev),
+ "fwk_dlist::tail not aligned with fwk_dlist_node::prev");
diff --git a/framework/src/fwk_id.c b/framework/src/fwk_id.c
new file mode 100644
index 0000000..bd536a0
--- /dev/null
+++ b/framework/src/fwk_id.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Identifiers.
+ */
+
+#include <stdio.h>
+#include <fwk_assert.h>
+#include <fwk_id.h>
+
+struct __fwk_id_fmt __fwk_id_str(fwk_id_t id)
+{
+ struct __fwk_id_fmt fmt;
+
+ assert(id.common.type != __FWK_ID_TYPE_INVALID);
+ assert(id.common.type < __FWK_ID_TYPE_COUNT);
+
+ switch (id.common.type) {
+ case FWK_ID_TYPE_MODULE:
+ snprintf(fmt.str, sizeof(fmt.str), "[MOD %u]", id.common.module_idx);
+ break;
+
+ case FWK_ID_TYPE_ELEMENT:
+ snprintf(fmt.str, sizeof(fmt.str), "[ELM %u:%u]", id.element.module_idx,
+ id.element.element_idx);
+ break;
+
+ case FWK_ID_TYPE_SUB_ELEMENT:
+ snprintf(fmt.str, sizeof(fmt.str), "[SELM %u:%u:%u]",
+ id.sub_element.module_idx, id.sub_element.element_idx,
+ id.sub_element.sub_element_idx);
+ break;
+
+ case FWK_ID_TYPE_API:
+ snprintf(fmt.str, sizeof(fmt.str), "[API %u:%u]", id.api.module_idx,
+ id.api.api_idx);
+ break;
+
+ case FWK_ID_TYPE_EVENT:
+ snprintf(fmt.str, sizeof(fmt.str), "[EVT %u:%u]", id.event.module_idx,
+ id.event.event_idx);
+ break;
+
+ case FWK_ID_TYPE_NOTIFICATION:
+ snprintf(fmt.str, sizeof(fmt.str), "[NOT %u:%u]",
+ id.notification.module_idx, id.notification.notification_idx);
+ break;
+
+ default:
+ snprintf(fmt.str, sizeof(fmt.str), "<invalid>");
+
+ break;
+ }
+
+ return fmt;
+}
+
+bool fwk_id_is_type(fwk_id_t id, enum fwk_id_type type)
+{
+ assert(id.common.type != __FWK_ID_TYPE_INVALID);
+ assert(id.common.type < __FWK_ID_TYPE_COUNT);
+
+ return id.common.type == type;
+}
+
+enum fwk_id_type fwk_id_get_type(fwk_id_t id)
+{
+ assert(id.common.type != __FWK_ID_TYPE_INVALID);
+ assert(id.common.type < __FWK_ID_TYPE_COUNT);
+
+ return id.common.type;
+}
+
+bool fwk_id_is_equal(fwk_id_t left, fwk_id_t right)
+{
+ assert(left.common.type != __FWK_ID_TYPE_INVALID);
+ assert(left.common.type < __FWK_ID_TYPE_COUNT);
+
+ return left.value == right.value;
+}
+
+fwk_id_t fwk_id_build_module_id(fwk_id_t id)
+{
+ assert(id.common.type != __FWK_ID_TYPE_INVALID);
+ assert(id.common.type < __FWK_ID_TYPE_COUNT);
+
+ return FWK_ID_MODULE(id.common.module_idx);
+}
+
+fwk_id_t fwk_id_build_element_id(fwk_id_t id, unsigned int element_idx)
+{
+ assert(id.common.type != __FWK_ID_TYPE_INVALID);
+ assert(id.common.type < __FWK_ID_TYPE_COUNT);
+
+ return FWK_ID_ELEMENT(id.common.module_idx, element_idx);
+}
+
+fwk_id_t fwk_id_build_api_id(fwk_id_t id, unsigned int api_idx)
+{
+ assert(id.common.type != __FWK_ID_TYPE_INVALID);
+ assert(id.common.type < __FWK_ID_TYPE_COUNT);
+
+ return FWK_ID_API(id.common.module_idx, api_idx);
+}
+
+unsigned int fwk_id_get_module_idx(fwk_id_t id)
+{
+ assert(id.common.type != __FWK_ID_TYPE_INVALID);
+ assert(id.common.type < __FWK_ID_TYPE_COUNT);
+
+ return id.common.module_idx;
+}
+
+unsigned int fwk_id_get_element_idx(fwk_id_t element_id)
+{
+ assert((element_id.common.type == __FWK_ID_TYPE_ELEMENT) ||
+ (element_id.common.type == __FWK_ID_TYPE_SUB_ELEMENT));
+
+ return element_id.element.element_idx;
+}
+
+unsigned int fwk_id_get_sub_element_idx(fwk_id_t sub_element_id)
+{
+ assert(sub_element_id.common.type == __FWK_ID_TYPE_SUB_ELEMENT);
+
+ return sub_element_id.sub_element.sub_element_idx;
+}
+
+unsigned int fwk_id_get_api_idx(fwk_id_t api_id)
+{
+ assert(api_id.common.type == __FWK_ID_TYPE_API);
+
+ return api_id.api.api_idx;
+}
+
+unsigned int fwk_id_get_event_idx(fwk_id_t event_id)
+{
+ assert(event_id.common.type == __FWK_ID_TYPE_EVENT);
+
+ return event_id.event.event_idx;
+}
+
+unsigned int fwk_id_get_notification_idx(fwk_id_t notification_id)
+{
+ assert(notification_id.common.type == __FWK_ID_TYPE_NOTIFICATION);
+
+ return notification_id.notification.notification_idx;
+}
+
+static_assert(sizeof(fwk_id_t) == sizeof(uint32_t),
+ "fwk_id_t has invalid size");
diff --git a/framework/src/fwk_interrupt.c b/framework/src/fwk_interrupt.c
new file mode 100644
index 0000000..3bd475e
--- /dev/null
+++ b/framework/src/fwk_interrupt.c
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Interrupt management.
+ */
+
+#include <limits.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <fwk_arch.h>
+#include <fwk_errno.h>
+#include <fwk_interrupt.h>
+#include <fwk_mm.h>
+
+static bool initialized;
+static const struct fwk_arch_interrupt_driver *driver;
+
+int fwk_interrupt_init(const struct fwk_arch_interrupt_driver *_driver)
+{
+ /* Validate driver by checking that all function pointers are non-null */
+ if (_driver == NULL)
+ return FWK_E_PARAM;
+ if (_driver->global_enable == NULL)
+ return FWK_E_PARAM;
+ if (_driver->global_disable == NULL)
+ return FWK_E_PARAM;
+ if (_driver->is_enabled == NULL)
+ return FWK_E_PARAM;
+ if (_driver->enable == NULL)
+ return FWK_E_PARAM;
+ if (_driver->disable == NULL)
+ return FWK_E_PARAM;
+ if (_driver->is_pending == NULL)
+ return FWK_E_PARAM;
+ if (_driver->set_pending == NULL)
+ return FWK_E_PARAM;
+ if (_driver->clear_pending == NULL)
+ return FWK_E_PARAM;
+ if (_driver->set_isr_irq == NULL)
+ return FWK_E_PARAM;
+ if (_driver->set_isr_irq_param == NULL)
+ return FWK_E_PARAM;
+ if (_driver->set_isr_nmi == NULL)
+ return FWK_E_PARAM;
+ if (_driver->set_isr_nmi_param == NULL)
+ return FWK_E_PARAM;
+ if (_driver->set_isr_fault == NULL)
+ return FWK_E_PARAM;
+ if (_driver->get_current == NULL)
+ return FWK_E_PARAM;
+
+ driver = _driver;
+ initialized = true;
+
+ return FWK_SUCCESS;
+}
+
+int fwk_interrupt_global_enable(void)
+{
+ if (!initialized)
+ return FWK_E_INIT;
+
+ return driver->global_enable();
+}
+
+int fwk_interrupt_global_disable(void)
+{
+ if (!initialized)
+ return FWK_E_INIT;
+
+ return driver->global_disable();
+}
+
+int fwk_interrupt_is_enabled(unsigned int interrupt, bool *enabled)
+{
+ if (!initialized)
+ return FWK_E_INIT;
+
+ if (enabled == NULL)
+ return FWK_E_PARAM;
+
+ return driver->is_enabled(interrupt, enabled);
+}
+
+int fwk_interrupt_enable(unsigned int interrupt)
+{
+ if (!initialized)
+ return FWK_E_INIT;
+
+ return driver->enable(interrupt);
+}
+
+int fwk_interrupt_disable(unsigned int interrupt)
+{
+ if (!initialized)
+ return FWK_E_INIT;
+
+ return driver->disable(interrupt);
+}
+
+int fwk_interrupt_is_pending(unsigned int interrupt, bool *pending)
+{
+ if (!initialized)
+ return FWK_E_INIT;
+
+ if (pending == NULL)
+ return FWK_E_PARAM;
+
+ return driver->is_pending(interrupt, pending);
+}
+
+int fwk_interrupt_set_pending(unsigned int interrupt)
+{
+ if (!initialized)
+ return FWK_E_INIT;
+
+ return driver->set_pending(interrupt);
+}
+
+int fwk_interrupt_clear_pending(unsigned int interrupt)
+{
+ if (!initialized)
+ return FWK_E_INIT;
+
+ return driver->clear_pending(interrupt);
+}
+
+int fwk_interrupt_set_isr(unsigned int interrupt, void (*isr)(void))
+{
+ if (!initialized)
+ return FWK_E_INIT;
+
+ if (isr == NULL)
+ return FWK_E_PARAM;
+
+ if (interrupt == FWK_INTERRUPT_NMI)
+ return driver->set_isr_nmi(isr);
+ else
+ return driver->set_isr_irq(interrupt, isr);
+}
+
+int fwk_interrupt_set_isr_param(unsigned int interrupt,
+ void (*isr)(uintptr_t param),
+ uintptr_t param)
+{
+ if (!initialized)
+ return FWK_E_INIT;
+
+ if (isr == NULL)
+ return FWK_E_PARAM;
+
+ if (interrupt == FWK_INTERRUPT_NMI)
+ return driver->set_isr_nmi_param(isr, param);
+ else
+ return driver->set_isr_irq_param(interrupt, isr, param);
+}
+
+int fwk_interrupt_get_current(unsigned int *interrupt)
+{
+ if (!initialized)
+ return FWK_E_INIT;
+
+ if (interrupt == NULL)
+ return FWK_E_PARAM;
+
+ return driver->get_current(interrupt);
+}
+
+/* This function is only for internal use by the framework */
+int fwk_interrupt_set_isr_fault(void (*isr)(void))
+{
+ if (!initialized)
+ return FWK_E_INIT;
+
+ if (isr == NULL)
+ return FWK_E_PARAM;
+
+ return driver->set_isr_fault(isr);
+}
diff --git a/framework/src/fwk_mm.c b/framework/src/fwk_mm.c
new file mode 100644
index 0000000..f61032b
--- /dev/null
+++ b/framework/src/fwk_mm.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Memory management.
+ */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fwk_errno.h>
+#include <fwk_macros.h>
+#include <fwk_mm.h>
+
+static bool initialized;
+static bool mm_locked;
+static uintptr_t heap_free;
+static uintptr_t heap_end;
+
+/*
+ * Initialize the memory management component.
+ *
+ * This function is not exposed by the memory management component but is
+ * used by the framework during its initialization routine.
+ *
+ * \retval FWK_SUCCESS Initialization was successful.
+ * \retval FWK_E_STATE The component has already been initialized.
+ * \retval FWK_E_RANGE There is a problem with the memory layout provided.
+ */
+int fwk_mm_init(uintptr_t start, size_t size)
+{
+ if (initialized)
+ return FWK_E_INIT;
+
+ if ((start == 0) || (size == 0))
+ return FWK_E_RANGE;
+
+ heap_free = start;
+ heap_end = start + size;
+
+ initialized = true;
+
+ return FWK_SUCCESS;
+}
+
+void fwk_mm_lock(void)
+{
+ mm_locked = true;
+}
+
+void *fwk_mm_alloc(size_t num, size_t size)
+{
+ return fwk_mm_alloc_aligned(num, size, FWK_MM_DEFAULT_ALIGNMENT);
+}
+
+void *fwk_mm_alloc_aligned(size_t num, size_t size, unsigned int alignment)
+{
+ uintptr_t start;
+ size_t total_size;
+ bool overflow;
+
+ if (mm_locked || !num || !size || !alignment || !initialized)
+ return NULL;
+
+ /* Ensure 'alignment' is a power of two */
+ if (alignment & (alignment - 1))
+ return NULL;
+
+ overflow = __builtin_mul_overflow(num, size, &total_size);
+
+ /* Ensure the computation of 'total_size' has not overflowed */
+ if (overflow)
+ return NULL;
+
+ start = FWK_ALIGN_NEXT(heap_free, alignment);
+
+ /* Ensure there is no overflow during the alignment */
+ if (start < heap_free)
+ return NULL;
+
+ /* Ensure 'total_size' fits in the remaining heap area */
+ if (total_size > (heap_end - start))
+ return NULL;
+
+ heap_free = start + total_size;
+
+ return (void *)start;
+}
+
+void *fwk_mm_calloc(size_t num, size_t size)
+{
+ return fwk_mm_calloc_aligned(num, size, FWK_MM_DEFAULT_ALIGNMENT);
+}
+
+void *fwk_mm_calloc_aligned(size_t num, size_t size, unsigned int alignment)
+{
+ void *start;
+
+ start = fwk_mm_alloc_aligned(num, size, alignment);
+ if (start != NULL)
+ memset(start, 0, num * size);
+
+ return start;
+}
+
+#ifdef __NEWLIB__
+void *_sbrk(intptr_t increment)
+{
+ if (increment == 0) {
+ return (void *)heap_end;
+ } else {
+ errno = ENOMEM;
+
+ return (void *)-1;
+ }
+}
+#endif
diff --git a/framework/src/fwk_module.c b/framework/src/fwk_module.c
new file mode 100644
index 0000000..85a1cc4
--- /dev/null
+++ b/framework/src/fwk_module.c
@@ -0,0 +1,670 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Module facilities.
+ */
+
+#include <string.h>
+#include <fwk_errno.h>
+#include <fwk_host.h>
+#include <fwk_mm.h>
+#include <internal/fwk_module.h>
+#include <internal/fwk_thread.h>
+#ifdef BUILD_HAS_NOTIFICATION
+#include <internal/fwk_notification.h>
+#endif
+
+#define EVENT_COUNT 64
+#define NOTIFICATION_COUNT 64
+#define BIND_ROUND_MAX 1
+
+/* Pre-runtime phase stages */
+enum module_stage {
+ MODULE_STAGE_INITIALIZE,
+ MODULE_STAGE_BIND,
+ MODULE_STAGE_START
+};
+
+struct context {
+ /* Flag indicating whether all modules have been initialized */
+ bool initialized;
+
+ /* Number of modules */
+ unsigned int module_count;
+
+ /* Table of module contexts */
+ struct fwk_module_ctx *module_ctx_table;
+
+ /* Pre-runtime phase stage */
+ enum module_stage stage;
+
+ /*
+ * Identifier of module or element currently binding to other modules or
+ * elements as part as of the binding stage.
+ */
+ fwk_id_t bind_id;
+};
+
+extern const struct fwk_module *module_table[];
+extern const struct fwk_module_config *module_config_table[];
+
+static struct context ctx;
+
+#ifdef BUILD_HOST
+static const char err_msg_line[] = "[MOD] Error %d in %s @%d\n";
+static const char err_msg_func[] = "[MOD] Error %d in %s\n";
+#endif
+
+/*
+ * Static functions
+ */
+
+#ifdef BUILD_HAS_NOTIFICATION
+static int init_notification_dlist_table(size_t count,
+ struct fwk_dlist **notification_dlist_table)
+{
+ struct fwk_dlist *dlist_table;
+ unsigned int dlist_idx;
+
+ dlist_table = fwk_mm_calloc(count, sizeof(struct fwk_dlist));
+ if (dlist_table == NULL) {
+ FWK_HOST_PRINT(err_msg_line, FWK_E_NOMEM, __func__, __LINE__);
+ return FWK_E_NOMEM;
+ }
+ *notification_dlist_table = dlist_table;
+
+ for (dlist_idx = 0; dlist_idx < count; dlist_idx++)
+ fwk_list_init(&dlist_table[dlist_idx]);
+
+ return FWK_SUCCESS;
+}
+#endif
+
+static int init_elements(struct fwk_module_ctx *module_ctx,
+ const struct fwk_element *element_table)
+{
+ int status;
+ const struct fwk_module *module;
+ unsigned int element_idx;
+ fwk_id_t element_id;
+ struct fwk_element_ctx *element_ctx;
+ const struct fwk_element *element;
+
+ module = module_ctx->desc;
+ if (module->element_init == NULL)
+ return FWK_E_PARAM;
+
+ module_ctx->element_ctx_table =
+ fwk_mm_calloc(module_ctx->element_count,
+ sizeof(struct fwk_element_ctx));
+ if (module_ctx->element_ctx_table == NULL)
+ return FWK_E_NOMEM;
+
+ for (element_idx = 0; element_idx < module_ctx->element_count;
+ element_idx++) {
+
+ element_ctx = &module_ctx->element_ctx_table[element_idx];
+ element = &element_table[element_idx];
+ element_id = fwk_id_build_element_id(module_ctx->id, element_idx);
+ ctx.bind_id = element_id;
+
+ /* Each element must have a valid pointer to specific data */
+ if (element->data == NULL) {
+ FWK_HOST_PRINT(err_msg_line, FWK_E_DATA, __func__, __LINE__);
+ return FWK_E_DATA;
+ }
+
+ element_ctx->desc = element;
+ element_ctx->sub_element_count = element->sub_element_count;
+ fwk_list_init(&element_ctx->delayed_response_list);
+
+ #ifdef BUILD_HAS_NOTIFICATION
+ if (module->notification_count) {
+ status = init_notification_dlist_table(module->notification_count,
+ &element_ctx->subscription_dlist_table);
+ if (status != FWK_SUCCESS)
+ return status;
+ }
+ #endif
+
+ status = module->element_init(
+ element_id, element->sub_element_count, element->data);
+ if (status != FWK_SUCCESS) {
+ FWK_HOST_PRINT(err_msg_func, status, __func__);
+ return status;
+ }
+
+ element_ctx->state = FWK_MODULE_STATE_INITIALIZED;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int init_module(struct fwk_module_ctx *module_ctx,
+ const struct fwk_module *module,
+ const struct fwk_module_config *module_config)
+{
+ int status;
+ const struct fwk_element *element_table = NULL;
+ unsigned int count;
+
+ if ((module->name == NULL) ||
+ (module->type >= FWK_MODULE_TYPE_COUNT) ||
+ (module->init == NULL) ||
+ (module_config == NULL) ||
+ ((module->api_count > 0) && (module->process_bind_request == NULL)))
+ return FWK_E_PARAM;
+
+ module_ctx->desc = module;
+ module_ctx->config = module_config;
+ fwk_list_init(&module_ctx->delayed_response_list);
+ ctx.bind_id = module_ctx->id;
+
+ #ifdef BUILD_HAS_NOTIFICATION
+ if (module->notification_count) {
+ status = init_notification_dlist_table(module->notification_count,
+ &module_ctx->subscription_dlist_table);
+ if (status != FWK_SUCCESS)
+ return status;
+ }
+ #endif
+
+ if (module_config->get_element_table != NULL) {
+ element_table = module_config->get_element_table(module_ctx->id);
+ if (element_table == NULL)
+ return FWK_E_PARAM;
+
+ for (count = 0; element_table[count].name != NULL; count++)
+ continue;
+
+ module_ctx->element_count = count;
+ }
+
+ status = module->init(module_ctx->id, module_ctx->element_count,
+ module_config->data);
+ if (status != FWK_SUCCESS) {
+ FWK_HOST_PRINT(err_msg_line, status, __func__, __LINE__);
+ return status;
+ }
+
+ if (module_ctx->element_count > 0) {
+ status = init_elements(module_ctx, element_table);
+ if (status != FWK_SUCCESS) {
+ FWK_HOST_PRINT(err_msg_line, status, __func__, __LINE__);
+ return status;
+ }
+ }
+
+ if (module->post_init != NULL) {
+ status = module->post_init(module_ctx->id);
+ if (status != FWK_SUCCESS) {
+ FWK_HOST_PRINT(err_msg_line, status, __func__, __LINE__);
+ return status;
+ }
+ }
+
+ module_ctx->state = FWK_MODULE_STATE_INITIALIZED;
+
+ return FWK_SUCCESS;
+}
+
+static int init_modules(void)
+{
+ int status;
+ unsigned int module_idx;
+ struct fwk_module_ctx *module_ctx;
+
+ while (module_table[ctx.module_count] != NULL)
+ ctx.module_count++;
+
+ ctx.module_ctx_table = fwk_mm_calloc(ctx.module_count,
+ sizeof(struct fwk_module_ctx));
+ if (ctx.module_ctx_table == NULL) {
+ FWK_HOST_PRINT(err_msg_line, FWK_E_NOMEM, __func__, __LINE__);
+ return FWK_E_NOMEM;
+ }
+
+ for (module_idx = 0; module_idx < ctx.module_count; module_idx++) {
+ module_ctx = &ctx.module_ctx_table[module_idx];
+ module_ctx->id = FWK_ID_MODULE(module_idx);
+ status = init_module(module_ctx, module_table[module_idx],
+ module_config_table[module_idx]);
+ if (status != FWK_SUCCESS) {
+ FWK_HOST_PRINT(err_msg_line, status, __func__, __LINE__);
+ return status;
+ }
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int bind_elements(struct fwk_module_ctx *module_ctx,
+ unsigned int round)
+{
+ int status;
+ const struct fwk_module *module;
+ unsigned int element_idx;
+
+ module = module_ctx->desc;
+
+ for (element_idx = 0; element_idx < module_ctx->element_count;
+ element_idx++) {
+
+ ctx.bind_id = fwk_id_build_element_id(module_ctx->id, element_idx);
+ status = module->bind(ctx.bind_id, round);
+ if (status != FWK_SUCCESS) {
+ FWK_HOST_PRINT(err_msg_func, status, __func__);
+ return status;
+ }
+
+ if (round == BIND_ROUND_MAX) {
+ module_ctx->element_ctx_table[element_idx].state =
+ FWK_MODULE_STATE_BOUND;
+ }
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int bind_module(struct fwk_module_ctx *module_ctx,
+ unsigned int round)
+{
+ int status;
+ const struct fwk_module *module;
+
+ module = module_ctx->desc;
+ if (module->bind == NULL) {
+ module_ctx->state = FWK_MODULE_STATE_BOUND;
+ return FWK_SUCCESS;
+ }
+
+ ctx.bind_id = module_ctx->id;
+ status = module->bind(module_ctx->id, round);
+ if (status != FWK_SUCCESS) {
+ FWK_HOST_PRINT(err_msg_func, status, __func__);
+ return status;
+ }
+
+ if (round == BIND_ROUND_MAX)
+ module_ctx->state = FWK_MODULE_STATE_BOUND;
+
+ return bind_elements(module_ctx, round);
+}
+
+static int bind_modules(unsigned int round)
+{
+ int status;
+ unsigned int module_idx;
+ struct fwk_module_ctx *module_ctx;
+
+ for (module_idx = 0; module_idx < ctx.module_count; module_idx++) {
+ module_ctx = &ctx.module_ctx_table[module_idx];
+ status = bind_module(module_ctx, round);
+ if (status != FWK_SUCCESS)
+ return status;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int start_elements(struct fwk_module_ctx *module_ctx)
+{
+ int status;
+ const struct fwk_module *module;
+ unsigned int element_idx;
+
+ module = module_ctx->desc;
+ for (element_idx = 0; element_idx < module_ctx->element_count;
+ element_idx++) {
+
+ if (module->start != NULL) {
+ status = module->start(
+ fwk_id_build_element_id(module_ctx->id, element_idx));
+ if (status != FWK_SUCCESS) {
+ FWK_HOST_PRINT(err_msg_func, status, __func__);
+ return status;
+ }
+ }
+
+ module_ctx->element_ctx_table[element_idx].state =
+ FWK_MODULE_STATE_STARTED;
+ }
+
+ return FWK_SUCCESS;
+}
+
+static int start_module(struct fwk_module_ctx *module_ctx)
+{
+ int status;
+ const struct fwk_module *module;
+
+ module = module_ctx->desc;
+
+ if (module->start != NULL) {
+ status = module->start(module_ctx->id);
+ if (status != FWK_SUCCESS) {
+ FWK_HOST_PRINT(err_msg_func, status, __func__);
+ return status;
+ }
+ }
+
+ module_ctx->state = FWK_MODULE_STATE_STARTED;
+
+ return start_elements(module_ctx);
+}
+
+static int start_modules(void)
+{
+ int status;
+ unsigned int module_idx;
+ struct fwk_module_ctx *module_ctx;
+
+ for (module_idx = 0; module_idx < ctx.module_count; module_idx++) {
+ module_ctx = &ctx.module_ctx_table[module_idx];
+ status = start_module(module_ctx);
+ if (status != FWK_SUCCESS)
+ return status;
+ }
+
+ return FWK_SUCCESS;
+}
+
+/*
+ * Private interface functions
+ */
+
+int __fwk_module_init(void)
+{
+ int status;
+ unsigned int bind_round;
+
+ if (ctx.initialized) {
+ FWK_HOST_PRINT(err_msg_func, FWK_E_STATE, __func__);
+ return FWK_E_STATE;
+ }
+
+ status = __fwk_thread_init(EVENT_COUNT);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx.stage = MODULE_STAGE_INITIALIZE;
+ status = init_modules();
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx.stage = MODULE_STAGE_BIND;
+ for (bind_round = 0; bind_round <= BIND_ROUND_MAX; bind_round++) {
+ status = bind_modules(bind_round);
+ if (status != FWK_SUCCESS)
+ return status;
+ }
+
+ #ifdef BUILD_HAS_NOTIFICATION
+ status = __fwk_notification_init(NOTIFICATION_COUNT);
+ if (status != FWK_SUCCESS)
+ return status;
+ #endif
+
+ ctx.stage = MODULE_STAGE_START;
+ status = start_modules();
+ if (status != FWK_SUCCESS)
+ return status;
+
+ ctx.initialized = true;
+
+ __fwk_thread_run();
+
+ return FWK_SUCCESS;
+}
+
+struct fwk_module_ctx *__fwk_module_get_ctx(fwk_id_t id)
+{
+ return &ctx.module_ctx_table[fwk_id_get_module_idx(id)];
+}
+
+struct fwk_element_ctx *__fwk_module_get_element_ctx(fwk_id_t element_id)
+{
+ struct fwk_module_ctx *module_ctx = __fwk_module_get_ctx(element_id);
+
+ return &module_ctx->element_ctx_table[element_id.element.element_idx];
+}
+
+int __fwk_module_get_state(fwk_id_t id, enum fwk_module_state *state)
+{
+ if (state == NULL)
+ return FWK_E_PARAM;
+
+ if (fwk_module_is_valid_element_id(id) ||
+ fwk_module_is_valid_sub_element_id(id))
+ *state = __fwk_module_get_element_ctx(id)->state;
+ else {
+ if (fwk_module_is_valid_module_id(id))
+ *state = __fwk_module_get_ctx(id)->state;
+ else
+ return FWK_E_PARAM;
+ }
+
+ return FWK_SUCCESS;
+}
+
+void __fwk_module_reset(void)
+{
+ ctx = (struct context){ 0 };
+}
+
+/*
+ * Public interface functions
+ */
+
+bool fwk_module_is_valid_module_id(fwk_id_t id)
+{
+ if (!fwk_id_is_type(id, FWK_ID_TYPE_MODULE))
+ return false;
+
+ if (fwk_id_get_module_idx(id) >= ctx.module_count)
+ return false;
+
+ return true;
+}
+
+bool fwk_module_is_valid_element_id(fwk_id_t id)
+{
+ unsigned int module_idx;
+
+ if (!fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT))
+ return false;
+
+ module_idx = fwk_id_get_module_idx(id);
+ if (module_idx >= ctx.module_count)
+ return false;
+
+ return (fwk_id_get_element_idx(id) <
+ ctx.module_ctx_table[module_idx].element_count);
+}
+
+bool fwk_module_is_valid_sub_element_id(fwk_id_t id)
+{
+ unsigned int module_idx;
+ struct fwk_module_ctx *module_ctx;
+ unsigned int element_idx;
+
+ if (!fwk_id_is_type(id, FWK_ID_TYPE_SUB_ELEMENT))
+ return false;
+
+ module_idx = fwk_id_get_module_idx(id);
+ if (module_idx >= ctx.module_count)
+ return false;
+ module_ctx = &ctx.module_ctx_table[module_idx];
+
+ element_idx = fwk_id_get_element_idx(id);
+ if (element_idx >= module_ctx->element_count)
+ return false;
+
+ return (fwk_id_get_sub_element_idx(id) <
+ module_ctx->element_ctx_table[element_idx].sub_element_count);
+}
+
+bool fwk_module_is_valid_entity_id(fwk_id_t id)
+{
+ switch (fwk_id_get_type(id)) {
+ case FWK_ID_TYPE_MODULE:
+ return fwk_module_is_valid_module_id(id);
+
+ case FWK_ID_TYPE_ELEMENT:
+ return fwk_module_is_valid_element_id(id);
+
+ case FWK_ID_TYPE_SUB_ELEMENT:
+ return fwk_module_is_valid_sub_element_id(id);
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+bool fwk_module_is_valid_api_id(fwk_id_t id)
+{
+ unsigned int module_idx;
+
+ if (!fwk_id_is_type(id, FWK_ID_TYPE_API))
+ return false;
+
+ module_idx = fwk_id_get_module_idx(id);
+ if (module_idx >= ctx.module_count)
+ return false;
+
+ return (fwk_id_get_api_idx(id) <
+ ctx.module_ctx_table[module_idx].desc->api_count);
+}
+
+bool fwk_module_is_valid_event_id(fwk_id_t id)
+{
+ unsigned int module_idx;
+
+ if (!fwk_id_is_type(id, FWK_ID_TYPE_EVENT))
+ return false;
+
+ module_idx = fwk_id_get_module_idx(id);
+ if (module_idx >= ctx.module_count)
+ return false;
+
+ return (fwk_id_get_event_idx(id) <
+ ctx.module_ctx_table[module_idx].desc->event_count);
+}
+
+bool fwk_module_is_valid_notification_id(fwk_id_t id)
+{
+ #ifdef BUILD_HAS_NOTIFICATION
+ unsigned int module_idx;
+
+ if (!fwk_id_is_type(id, FWK_ID_TYPE_NOTIFICATION))
+ return false;
+
+ module_idx = fwk_id_get_module_idx(id);
+ if (module_idx >= ctx.module_count)
+ return false;
+
+ return (fwk_id_get_notification_idx(id) <
+ ctx.module_ctx_table[module_idx].desc->notification_count);
+ #else
+ return false;
+ #endif
+}
+
+int fwk_module_get_element_count(fwk_id_t id)
+{
+ if (fwk_module_is_valid_module_id(id))
+ return __fwk_module_get_ctx(id)->element_count;
+ else
+ return FWK_E_PARAM;
+}
+
+const char *fwk_module_get_name(fwk_id_t id)
+{
+ if (fwk_module_is_valid_element_id(id))
+ return __fwk_module_get_element_ctx(id)->desc->name;
+ else if (fwk_module_is_valid_module_id(id))
+ return __fwk_module_get_ctx(id)->desc->name;
+
+ return NULL;
+}
+
+const void *fwk_module_get_data(fwk_id_t id)
+{
+ if (fwk_module_is_valid_element_id(id))
+ return __fwk_module_get_element_ctx(id)->desc->data;
+ else if (fwk_module_is_valid_module_id(id))
+ return __fwk_module_get_ctx(id)->config->data;
+
+ return NULL;
+}
+
+int fwk_module_check_call(fwk_id_t id)
+{
+ int status;
+ enum fwk_module_state state;
+
+ status = __fwk_module_get_state(id, &state);
+ if (status != FWK_SUCCESS)
+ goto error;
+
+ if ((state == FWK_MODULE_STATE_UNINITIALIZED) ||
+ (state == FWK_MODULE_STATE_SUSPENDED)) {
+ status = FWK_E_STATE;
+ goto error;
+ }
+
+ return FWK_SUCCESS;
+
+error:
+ FWK_HOST_PRINT(err_msg_func, status, __func__);
+ return status;
+}
+
+int fwk_module_bind(fwk_id_t target_id, fwk_id_t api_id, const void *api)
+{
+ int status = FWK_E_PARAM;
+ struct fwk_module_ctx *module_ctx;
+
+ if (!fwk_module_is_valid_entity_id(target_id))
+ goto error;
+
+ if (!fwk_module_is_valid_api_id(api_id))
+ goto error;
+
+ if (fwk_id_get_module_idx(target_id) !=
+ fwk_id_get_module_idx(api_id))
+ goto error;
+
+ module_ctx = __fwk_module_get_ctx(target_id);
+
+ if (((ctx.stage != MODULE_STAGE_INITIALIZE) ||
+ (module_ctx->state != FWK_MODULE_STATE_INITIALIZED)) &&
+ (ctx.stage != MODULE_STAGE_BIND) && (ctx.stage != MODULE_STAGE_START)) {
+
+ status = FWK_E_STATE;
+ goto error;
+ }
+
+ status = module_ctx->desc->process_bind_request(ctx.bind_id, target_id,
+ api_id, (const void **)api);
+ if (status != FWK_SUCCESS) {
+ FWK_HOST_PRINT(err_msg_line, status, __func__, __LINE__);
+ return status;
+ }
+
+ if (*(void **)api == NULL) {
+ status = FWK_E_HANDLER;
+ goto error;
+ }
+
+ return FWK_SUCCESS;
+
+error:
+ FWK_HOST_PRINT(err_msg_func, status, __func__);
+ return status;
+}
diff --git a/framework/src/fwk_multi_thread.c b/framework/src/fwk_multi_thread.c
new file mode 100644
index 0000000..af4757c
--- /dev/null
+++ b/framework/src/fwk_multi_thread.c
@@ -0,0 +1,952 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <fwk_assert.h>
+#include <fwk_errno.h>
+#include <fwk_host.h>
+#include <fwk_interrupt.h>
+#include <fwk_element.h>
+#include <fwk_mm.h>
+#include <internal/fwk_module.h>
+#include <internal/fwk_notification.h>
+#include <internal/fwk_multi_thread.h>
+#include <rtx_os.h>
+
+#define SIGNAL_ISR_EVENT 0x01
+#define SIGNAL_EVENT_TO_PROCESS 0x02
+#define SIGNAL_EVENT_PROCESSED 0x04
+#define SIGNAL_NO_READY_THREAD 0x08
+
+static struct __fwk_multi_thread_ctx ctx;
+#ifdef BUILD_HOST
+static const char err_msg_line[] = "[THR] Error %d @%d\n";
+static const char err_msg_func[] = "[THR] Error %d in %s\n";
+#endif
+
+/*
+ * Static functions
+ */
+
+/*
+ * Initialize the attributes of thread.
+ *
+ * \param attr Pointer to thread's attributes.
+ *
+ * \retval FWK_SUCCESS The initialization succeeded.
+ * \retval FWK_E_NOMEM A memory allocation failed.
+ */
+static int init_thread_attr(osThreadAttr_t *attr)
+{
+ attr->name = "";
+ attr->attr_bits = osThreadDetached;
+ attr->cb_size = osRtxThreadCbSize;
+ attr->cb_mem = fwk_mm_calloc(1, attr->cb_size);
+ if (attr->cb_mem == NULL)
+ return FWK_E_NOMEM;
+
+ attr->stack_size = 256 * 4;
+ attr->stack_mem = fwk_mm_calloc(1, attr->stack_size);
+ if (attr->stack_mem == NULL)
+ return FWK_E_NOMEM;
+
+ attr->priority = osPriorityNormal;
+
+ return FWK_SUCCESS;
+}
+
+/*
+ * Put back an event into the queue of free events.
+ *
+ * \param event Pointer to the event.
+ *
+ * \pre \p event must not be NULL
+ */
+static void free_event(struct fwk_event *event)
+{
+ fwk_interrupt_global_disable();
+ fwk_list_push_tail(&ctx.event_free_queue, &event->slist_node);
+ fwk_interrupt_global_enable();
+}
+
+/*
+ * Duplicate an event.
+ *
+ * \param event Pointer to the event to duplicate.
+ *
+ * \pre \p event must not be NULL
+ *
+ * \return The pointer to the duplicated event, NULL if the allocation to
+ * duplicate the event failed.
+ */
+static struct fwk_event *duplicate_event(struct fwk_event *event)
+{
+ struct fwk_event *allocated_event;
+
+ assert(event != NULL);
+
+ fwk_interrupt_global_disable();
+ allocated_event = FWK_LIST_GET(fwk_list_pop_head(&ctx.event_free_queue),
+ struct fwk_event, slist_node);
+ fwk_interrupt_global_enable();
+
+ if (allocated_event != NULL) {
+ *allocated_event = *event;
+ return allocated_event;
+ }
+
+ assert(false);
+ FWK_HOST_PRINT(err_msg_func, FWK_E_NOMEM, __func__);
+ return NULL;
+}
+
+/*
+ * Get the thread context of a given module or element.
+ *
+ * \param id Module or element identifier.
+ *
+ * \return Thread context, NULL if the identifier is not valid.
+ */
+static struct __fwk_thread_ctx *thread_get_ctx(fwk_id_t id)
+{
+ struct fwk_module_ctx *module_ctx;
+ struct fwk_element_ctx *element_ctx;
+
+ if (fwk_module_is_valid_element_id(id)) {
+ element_ctx = __fwk_module_get_element_ctx(id);
+ if (element_ctx->thread_ctx != NULL)
+ return element_ctx->thread_ctx;
+ else
+ id = FWK_ID_MODULE(id.element.module_idx);
+ }
+
+ if (fwk_module_is_valid_module_id(id)) {
+ module_ctx = __fwk_module_get_ctx(id);
+ return (module_ctx->thread_ctx != NULL) ?
+ module_ctx->thread_ctx : &ctx.common_thread_ctx;
+ }
+
+ return NULL;
+}
+
+/*
+ * Get the list of delayed responses for a given module or element.
+ *
+ * \note The function assumes the validity of all its input parameters.
+ *
+ * \param id Identifier of the module or element.
+ *
+ * \return A pointer to the list of subscriptions.
+ */
+static struct fwk_slist *get_delayed_response_list(fwk_id_t id)
+{
+ if (fwk_id_is_type(id, FWK_ID_TYPE_MODULE))
+ return &__fwk_module_get_ctx(id)->delayed_response_list;
+
+ return &__fwk_module_get_element_ctx(id)->delayed_response_list;
+}
+
+/*
+ * Search delayed response.
+ *
+ * \note The function assumes the validity of all its input parameters.
+ *
+ * \param id Identifier of the module or element that delayed the response.
+ * \param cookie Cookie of the event which the response has been delayed
+ * for. This cookie identifies the response among the several responses
+ * that the entity 'id' may have delayed.
+ *
+ * \return A pointer to the delayed response event, NULL if not found.
+ */
+static struct fwk_event *search_delayed_response(fwk_id_t id, uint32_t cookie)
+{
+ struct fwk_slist *delayed_response_list;
+ struct fwk_slist_node *delayed_response_node;
+ struct fwk_event *delayed_response;
+
+ delayed_response_list = get_delayed_response_list(id);
+ delayed_response_node = fwk_list_head(delayed_response_list);
+
+ while (delayed_response_node != NULL) {
+ delayed_response = FWK_LIST_GET(delayed_response_node,
+ struct fwk_event, slist_node);
+ if (delayed_response->cookie == cookie)
+ return delayed_response;
+
+ delayed_response_node = fwk_list_next(delayed_response_list,
+ delayed_response_node);
+ }
+
+ return NULL;
+}
+
+/*
+ * Put an event in the ISR event queue.
+ *
+ * This function is a sub-routine of the fwk_thread_put_event() interface
+ * function.
+ *
+ * \param event Pointer to the ISR event to queue.
+ *
+ * \retval FWK_SUCCESS The event was put successfully.
+ * \retval FWK_E_NOMEM The free event queue is empty.
+ */
+static int put_isr_event(struct fwk_event *event)
+{
+ struct fwk_event *allocated_event;
+ uint32_t flags;
+
+ allocated_event = duplicate_event(event);
+ if (allocated_event == NULL)
+ return FWK_E_NOMEM;
+
+ FWK_HOST_PRINT("[THR] Add ISR event (%s,%s,%s)\n",
+ FWK_ID_STR(event->source_id),
+ FWK_ID_STR(event->target_id), FWK_ID_STR(event->id));
+
+ /*
+ * Assumption: there are no interrupt priorities, at least among interrupts
+ * leading to the call of fwk_thread_put_event(). As a consequence the
+ * access to the ISR queue of events is not protected here against
+ * concurrent accesses, as this function should only be executed from within
+ * an interrupt context.
+ */
+ fwk_list_push_tail(&ctx.event_isr_queue, &allocated_event->slist_node);
+ if (ctx.waiting_for_isr_event) {
+ flags = osThreadFlagsSet(ctx.common_thread_ctx.os_thread_id,
+ SIGNAL_ISR_EVENT);
+ if ((int32_t)flags < 0) {
+ FWK_HOST_PRINT(err_msg_func, FWK_E_OS, __func__);
+ return FWK_E_OS;
+ }
+ ctx.waiting_for_isr_event = false;
+ }
+
+ return FWK_SUCCESS;
+}
+
+/*
+ * Check if an event is the wake-up event waited for by a thread.
+ *
+ * \param thread_ctx Pointer to the context of the waiting thread.
+ * \param event Pointer to the event to be checked.
+ *
+ * \pre \p thread_ctx must not be NULL
+ * \pre \p event must not be NULL
+ *
+ * \retval True The event is the wake-up event waited for by the thread.
+ * \retval False The event is not the wake-up event waited for by the thread.
+ */
+static bool is_thread_wakeup_event(struct __fwk_thread_ctx *thread_ctx,
+ struct fwk_event *event)
+{
+ if (!thread_ctx->waiting_event_processing_completion)
+ return false;
+
+ if (!event->is_response)
+ return false;
+
+ return thread_ctx->response_event->cookie == event->cookie;
+}
+
+/*
+ * Put an event in a thread queue.
+ *
+ * This function is a sub-routine of the fwk_thread_put_event() and
+ * fwk_thread_put_event_and_wait() interface functions. If the thread queue was
+ * empty, the thread is added at the end of the list of threads having at least
+ * one event pending in its event queue.
+ *
+ * \param thread_ctx Pointer to the context of the thread target of the event.
+ * \param event Pointer to the event to queue.
+ *
+ * \retval FWK_SUCCESS The event was put successfully.
+ * \retval FWK_E_PARAM The event source is not valid.
+ */
+static int put_event(struct __fwk_thread_ctx *target_thread_ctx,
+ struct fwk_event *event)
+{
+ int status = FWK_E_PARAM;
+ struct fwk_event *allocated_event;
+ bool is_empty;
+
+ FWK_HOST_PRINT("[THR] Add event to thread queue (%s,%s,%s)\n",
+ FWK_ID_STR(event->source_id), FWK_ID_STR(event->target_id),
+ FWK_ID_STR(event->id));
+
+ event->is_thread_wakeup_event = is_thread_wakeup_event(
+ target_thread_ctx, event);
+
+ if (event->is_delayed_response) {
+ allocated_event = search_delayed_response(event->source_id,
+ event->cookie);
+ if (allocated_event == NULL)
+ goto error;
+
+ fwk_list_remove(get_delayed_response_list(event->source_id),
+ &allocated_event->slist_node);
+
+ memcpy(allocated_event->params, event->params,
+ sizeof(allocated_event->params));
+ allocated_event->is_thread_wakeup_event = event->is_thread_wakeup_event;
+ } else {
+ allocated_event = duplicate_event(event);
+ if (allocated_event == NULL)
+ return FWK_E_NOMEM;
+ }
+
+ allocated_event->cookie = event->cookie = ctx.event_cookie_counter++;
+
+ if (allocated_event->is_thread_wakeup_event) {
+ fwk_list_push_head(&target_thread_ctx->event_queue,
+ &allocated_event->slist_node);
+ fwk_list_push_head(&ctx.thread_ready_queue,
+ &target_thread_ctx->slist_node);
+ } else {
+ is_empty = fwk_list_is_empty(&target_thread_ctx->event_queue);
+ fwk_list_push_tail(&target_thread_ctx->event_queue,
+ &allocated_event->slist_node);
+
+ if (is_empty &&
+ (target_thread_ctx != ctx.current_thread_ctx) &&
+ (!(target_thread_ctx->waiting_event_processing_completion)))
+ fwk_list_push_tail(&ctx.thread_ready_queue,
+ &target_thread_ctx->slist_node);
+ }
+
+ return FWK_SUCCESS;
+
+error:
+ FWK_HOST_PRINT(err_msg_func, status, __func__);
+ return status;
+}
+
+/*
+ * Process event requiring a response
+ *
+ * This function is a sub-routine of process_next_thread_event().
+ *
+ * \param event Pointer to the current event.
+ */
+static void process_event_requiring_response(struct fwk_event *event)
+{
+ int status;
+ const struct fwk_module *module;
+ struct __fwk_thread_ctx *source_thread_ctx;
+ struct fwk_event resp_event, *allocated_event;
+ int (*process_event)(const struct fwk_event *event,
+ struct fwk_event *resp_event);
+
+ module = __fwk_module_get_ctx(event->target_id)->desc;
+ source_thread_ctx = thread_get_ctx(event->source_id);
+
+ process_event = event->is_notification ?
+ module->process_notification : module->process_event;
+
+ resp_event = *event;
+ resp_event.source_id = event->target_id;
+ resp_event.target_id = event->source_id;
+ resp_event.is_delayed_response = false;
+
+ status = process_event(event, &resp_event);
+ if (status != FWK_SUCCESS)
+ FWK_HOST_PRINT(err_msg_line, status, __LINE__);
+
+ resp_event.is_response = true;
+ resp_event.response_requested = false;
+ if (!resp_event.is_delayed_response)
+ put_event(source_thread_ctx, &resp_event);
+ else {
+ allocated_event = duplicate_event(&resp_event);
+ if (allocated_event != NULL) {
+ fwk_list_push_tail(get_delayed_response_list(resp_event.source_id),
+ &allocated_event->slist_node);
+ }
+ }
+}
+
+/*
+ * Process the next event of a given thread.
+ *
+ * This function is a sub-routine of thread_function().
+ *
+ * \param thread_ctx Pointer to the context of the thread, the next event of
+ * which has to be processed.
+ */
+static void process_next_thread_event(struct __fwk_thread_ctx *thread_ctx)
+{
+ int status;
+ struct fwk_event *event, async_resp_event;
+ const struct fwk_module *module;
+
+ /*
+ * Extract the event from the thread event queue and update the pointer to
+ * the currently active thread context and to the currently processed
+ * event.
+ */
+ ctx.current_thread_ctx = thread_ctx;
+ ctx.current_event = event =
+ FWK_LIST_GET(fwk_list_pop_head(&thread_ctx->event_queue),
+ struct fwk_event, slist_node);
+ assert(event != NULL);
+
+ FWK_HOST_PRINT("[THR] Process thread event (%s,%s,%s)\n",
+ FWK_ID_STR(event->source_id),
+ FWK_ID_STR(event->target_id), FWK_ID_STR(event->id));
+
+ if (event->response_requested)
+ process_event_requiring_response(event);
+ else {
+ module = __fwk_module_get_ctx(event->target_id)->desc;
+ if (event->is_notification)
+ status = module->process_notification(event, &async_resp_event);
+ else
+ status = module->process_event(event, &async_resp_event);
+ if (status != FWK_SUCCESS)
+ FWK_HOST_PRINT(err_msg_line, status, __LINE__);
+ }
+
+ /* No event currently processed, no thread currently active. */
+ ctx.current_event = NULL;
+ ctx.current_thread_ctx = NULL;
+
+ free_event(event);
+
+ if (!fwk_list_is_empty(&thread_ctx->event_queue))
+ fwk_list_push_tail(&ctx.thread_ready_queue, &thread_ctx->slist_node);
+}
+
+/*
+ * Launch the processing of the next event.
+ *
+ * \param current_thread_ctx Pointer to the context of the thread currently
+ * executed.
+ *
+ * \retval NULL There is no event to process for the time being.
+ * \return Pointer to the context of the thread the next event will be
+ * processed within. If the thread is different from the one currently
+ * executing the function has raised the SIGNAL_EVENT_TO_PROCESS to the
+ * next thread for it to start executing when the current thread will go
+ * waiting.
+ */
+static struct __fwk_thread_ctx *launch_next_event_processing(
+ struct __fwk_thread_ctx *current_thread_ctx)
+{
+ struct __fwk_thread_ctx *next_thread_ctx;
+ uint32_t flags;
+ struct fwk_event *event;
+
+ while (!fwk_list_is_empty(&ctx.thread_ready_queue)) {
+ next_thread_ctx = FWK_LIST_GET(
+ fwk_list_pop_head(&ctx.thread_ready_queue),
+ struct __fwk_thread_ctx, slist_node);
+
+ if (next_thread_ctx == current_thread_ctx)
+ return current_thread_ctx;
+
+ event = FWK_LIST_GET(fwk_list_head(&next_thread_ctx->event_queue),
+ struct fwk_event, slist_node);
+
+ if (event->is_thread_wakeup_event) {
+ *next_thread_ctx->response_event = *event;
+ flags = osThreadFlagsSet(next_thread_ctx->os_thread_id,
+ SIGNAL_EVENT_PROCESSED);
+ if ((int32_t)flags >= 0) {
+ event = FWK_LIST_GET(fwk_list_pop_head(
+ &next_thread_ctx->event_queue), struct fwk_event,
+ slist_node);
+ free_event(event);
+ return next_thread_ctx;
+ }
+ } else {
+ flags = osThreadFlagsSet(next_thread_ctx->os_thread_id,
+ SIGNAL_EVENT_TO_PROCESS);
+ if ((int32_t)flags >= 0)
+ return next_thread_ctx;
+ }
+
+ FWK_HOST_PRINT(err_msg_line, FWK_E_OS, __LINE__);
+ event = FWK_LIST_GET(fwk_list_pop_head(&next_thread_ctx->event_queue),
+ struct fwk_event, slist_node);
+ free_event(event);
+
+ if (!fwk_list_is_empty(&next_thread_ctx->event_queue)) {
+ fwk_list_push_tail(&ctx.thread_ready_queue,
+ &next_thread_ctx->slist_node);
+ }
+ }
+
+ return NULL;
+}
+
+static void thread_function(struct __fwk_thread_ctx *thread_ctx,
+ struct __fwk_thread_ctx *next_thread_ctx)
+{
+ uint32_t flags;
+ uint32_t signals;
+
+ signals = (thread_ctx == &ctx.common_thread_ctx) ?
+ (SIGNAL_EVENT_TO_PROCESS | SIGNAL_NO_READY_THREAD) :
+ SIGNAL_EVENT_TO_PROCESS;
+
+ for (;;) {
+ /*
+ * If the thread which the next event has to be processed within is the
+ * present thread, proceed straight to the processing of the next event.
+ * Otherwise, wait for a signal:
+ * 1) just the event to process signal for specific threads
+ * 2) event to process and no ready threads for the common thread
+ */
+ if (thread_ctx != next_thread_ctx) {
+ flags = osThreadFlagsWait(signals, osFlagsWaitAny, osWaitForever);
+ /* If there is more than one flag raised or not an expected one */
+ if ((flags & (flags - 1)) || (!(flags & signals))) {
+ FWK_HOST_PRINT(err_msg_line, FWK_E_OS, __LINE__);
+ continue;
+ }
+ if (flags & SIGNAL_NO_READY_THREAD)
+ return;
+ }
+
+ /* Process the first event of the thread event queue */
+ next_thread_ctx = NULL;
+ process_next_thread_event(thread_ctx);
+ next_thread_ctx = launch_next_event_processing(thread_ctx);
+
+ /*
+ * There is an event to process now. The thread it has to be processed
+ * within is the thread of context 'next_thread_ctx'. If it has to be
+ * processed by the current thread (next_thread_ctx == thread_ctx), loop
+ * to process it. If the event has to be processed by another thread
+ * (next_thread_ctx != thread_ctx), the present thread has successfully
+ * signalled it to the other thread, loop to wait for a wake-up signal.
+ */
+ if (next_thread_ctx != NULL)
+ continue;
+
+ /*
+ * No event to process for the time being. If the current thread is the
+ * common one just return to the __fwk_thread_run() function to get the
+ * next ISR event. Otherwise, signal the common thread with a
+ * SIGNAL_NO_READY_THREAD signal that it has to get the next ISR event.
+ */
+ if (thread_ctx == &ctx.common_thread_ctx)
+ return;
+ flags = osThreadFlagsSet(
+ ctx.common_thread_ctx.os_thread_id, SIGNAL_NO_READY_THREAD);
+ if ((int32_t)flags < 0)
+ FWK_HOST_PRINT(err_msg_line, FWK_E_OS, __LINE__);
+ }
+}
+
+static void specific_thread_function(void *arg)
+{
+ assert(arg != NULL);
+
+ thread_function((struct __fwk_thread_ctx *)arg, NULL);
+}
+
+static void get_next_isr_event(void)
+{
+ uint32_t flags;
+ struct fwk_event *isr_event;
+ struct __fwk_thread_ctx *target_thread_ctx;
+
+ for (;;) {
+ fwk_interrupt_global_disable();
+ if (fwk_list_is_empty(&ctx.event_isr_queue)) {
+ /* Wait for an ISR event. */
+ ctx.waiting_for_isr_event = true;
+ fwk_interrupt_global_enable();
+ flags = osThreadFlagsWait(
+ SIGNAL_ISR_EVENT, osFlagsWaitAll, osWaitForever);
+ if (flags != SIGNAL_ISR_EVENT)
+ FWK_HOST_PRINT(err_msg_line, FWK_E_OS, __LINE__);
+ continue;
+ }
+
+ isr_event = FWK_LIST_GET(fwk_list_pop_head(&ctx.event_isr_queue),
+ struct fwk_event, slist_node);
+ fwk_interrupt_global_enable();
+
+ assert(isr_event != NULL);
+ FWK_HOST_PRINT("[THR] Get ISR event (%s,%s,%s)\n",
+ FWK_ID_STR(isr_event->source_id),
+ FWK_ID_STR(isr_event->target_id),
+ FWK_ID_STR(isr_event->id));
+
+ target_thread_ctx = thread_get_ctx(isr_event->target_id);
+
+ isr_event->is_thread_wakeup_event = is_thread_wakeup_event(
+ target_thread_ctx, isr_event);
+ isr_event->cookie = ctx.event_cookie_counter++;
+
+ if (isr_event->is_thread_wakeup_event) {
+ fwk_list_push_head(&target_thread_ctx->event_queue,
+ &isr_event->slist_node);
+ } else {
+ fwk_list_push_tail(&target_thread_ctx->event_queue,
+ &isr_event->slist_node);
+ }
+
+ if (!(target_thread_ctx->waiting_event_processing_completion) ||
+ isr_event->is_thread_wakeup_event) {
+ fwk_list_push_head(&ctx.thread_ready_queue,
+ &target_thread_ctx->slist_node);
+ break;
+ }
+ }
+}
+
+static void common_thread_function(void *arg)
+{
+ struct __fwk_thread_ctx *next_thread_ctx;
+
+ ctx.running = true;
+
+ for (;;) {
+ next_thread_ctx = launch_next_event_processing(&ctx.common_thread_ctx);
+
+ if (next_thread_ctx != NULL)
+ thread_function(&ctx.common_thread_ctx, next_thread_ctx);
+
+ get_next_isr_event();
+ }
+}
+
+/*
+ * Private interface functions
+ */
+
+int __fwk_thread_init(size_t event_count)
+{
+ int status;
+ struct fwk_event *event_table, *event_table_end, *event;
+ osThreadAttr_t thread_attr;
+
+ fwk_interrupt_global_enable();
+ status = osKernelInitialize();
+ if (status != osOK) {
+ status = FWK_E_OS;
+ goto error;
+ }
+
+ event_table = fwk_mm_calloc(event_count, sizeof(struct fwk_event));
+ if (event_table == NULL) {
+ status = FWK_E_NOMEM;
+ goto error;
+ }
+
+ /* All the event structures are free to be used. */
+ fwk_list_init(&ctx.event_free_queue);
+ fwk_list_init(&(ctx.thread_ready_queue));
+ fwk_list_init(&(ctx.event_isr_queue));
+ fwk_list_init(&(ctx.common_thread_ctx.event_queue));
+ for (event = event_table, event_table_end = event_table + event_count;
+ event < event_table_end; event++)
+ fwk_list_push_tail(&ctx.event_free_queue,
+ &event->slist_node);
+
+ status = init_thread_attr(&thread_attr);
+ if (status != FWK_SUCCESS)
+ goto error;
+
+ ctx.common_thread_ctx.os_thread_id = osThreadNew(common_thread_function,
+ &ctx.common_thread_ctx, &thread_attr);
+ if (ctx.common_thread_ctx.os_thread_id == NULL) {
+ status = FWK_E_OS;
+ goto error;
+ }
+
+ ctx.initialized = true;
+
+ return FWK_SUCCESS;
+
+error:
+ FWK_HOST_PRINT(err_msg_func, status, __func__);
+ return status;
+}
+
+struct __fwk_multi_thread_ctx *__fwk_multi_thread_get_ctx(void)
+{
+ return &ctx;
+}
+
+noreturn void __fwk_thread_run(void)
+{
+ osKernelStart();
+
+ while (true)
+ continue;
+}
+
+const struct fwk_event *__fwk_thread_get_current_event(void)
+{
+ return ctx.current_event;
+}
+
+#ifdef BUILD_HAS_NOTIFICATION
+int __fwk_thread_put_notification(struct fwk_event *event)
+{
+ unsigned int interrupt;
+
+ event->is_response = false;
+ event->is_notification = true;
+ event->is_delayed_response = false;
+
+ /* Call from a thread */
+ if (fwk_interrupt_get_current(&interrupt) != FWK_SUCCESS)
+ return put_event(thread_get_ctx(event->target_id), event);
+
+ /* Call from an ISR */
+ if (!fwk_module_is_valid_entity_id(event->source_id)) {
+ FWK_HOST_PRINT(err_msg_func, FWK_E_PARAM, __func__);
+ return FWK_E_PARAM;
+ }
+
+ return put_isr_event(event);
+}
+#endif
+
+/*
+ * Public interface functions
+ */
+int fwk_thread_create(fwk_id_t id)
+{
+ int status;
+ struct __fwk_thread_ctx **p_thread_ctx, *thread_ctx;
+ osThreadAttr_t thread_attr;
+
+ if (!ctx.initialized) {
+ status = FWK_E_INIT;
+ goto error;
+ }
+
+ if (fwk_module_is_valid_element_id(id))
+ p_thread_ctx = &__fwk_module_get_element_ctx(id)->thread_ctx;
+ else if (fwk_module_is_valid_module_id(id))
+ p_thread_ctx = &__fwk_module_get_ctx(id)->thread_ctx;
+ else {
+ status = FWK_E_PARAM;
+ goto error;
+ }
+
+ status = init_thread_attr(&thread_attr);
+ if (status != FWK_SUCCESS)
+ goto error;
+
+ if ((ctx.running) || (*p_thread_ctx != NULL)) {
+ status = FWK_E_STATE;
+ goto error;
+ }
+
+ thread_ctx = fwk_mm_calloc(1, sizeof(struct __fwk_thread_ctx));
+ if (thread_ctx == NULL) {
+ status = FWK_E_NOMEM;
+ goto error;
+ }
+
+ fwk_list_init(&thread_ctx->event_queue);
+ thread_ctx->id = id;
+ thread_ctx->os_thread_id = osThreadNew(specific_thread_function, thread_ctx,
+ &thread_attr);
+ if (thread_ctx->os_thread_id == NULL) {
+ status = FWK_E_OS;
+ goto error;
+ }
+
+ *p_thread_ctx = thread_ctx;
+
+ return FWK_SUCCESS;
+
+error:
+ FWK_HOST_PRINT(err_msg_func, status, __func__);
+ return status;
+}
+
+int fwk_thread_put_event(struct fwk_event *event)
+{
+ int status = FWK_E_PARAM;
+ struct __fwk_thread_ctx *thread_ctx;
+ unsigned int interrupt;
+
+ if (!ctx.initialized) {
+ status = FWK_E_INIT;
+ goto error;
+ }
+
+ if (event == NULL)
+ goto error;
+
+ thread_ctx = thread_get_ctx(event->target_id);
+ if (thread_ctx == NULL)
+ goto error;
+
+ if ((fwk_interrupt_get_current(&interrupt) != FWK_SUCCESS) &&
+ (ctx.current_event != NULL))
+ event->source_id = ctx.current_event->target_id;
+ else {
+ if (!fwk_module_is_valid_entity_id(event->source_id))
+ goto error;
+ }
+
+ if (event->is_notification) {
+ if (!fwk_module_is_valid_notification_id(event->id))
+ goto error;
+ if ((!event->is_response) || (event->response_requested))
+ goto error;
+ if (fwk_id_get_module_idx(event->target_id) !=
+ fwk_id_get_module_idx(event->id))
+ goto error;
+ } else {
+ if (!fwk_module_is_valid_event_id(event->id))
+ goto error;
+ if (event->is_response) {
+ if (fwk_id_get_module_idx(event->source_id) !=
+ fwk_id_get_module_idx(event->id))
+ goto error;
+ if (event->response_requested)
+ goto error;
+ } else {
+ if (fwk_id_get_module_idx(event->target_id) !=
+ fwk_id_get_module_idx(event->id))
+ goto error;
+ }
+ }
+
+ /* Call from a thread */
+ if (fwk_interrupt_get_current(&interrupt) != FWK_SUCCESS) {
+ event->is_delayed_response = event->is_response;
+ return put_event(thread_ctx, event);
+ }
+
+ /* Call from an ISR */
+ return put_isr_event(event);
+
+error:
+ FWK_HOST_PRINT(err_msg_func, status, __func__);
+ return status;
+}
+
+int fwk_thread_put_event_and_wait(struct fwk_event *event,
+ struct fwk_event *resp_event)
+{
+ int status = FWK_E_PARAM;
+ struct __fwk_thread_ctx *target_thread_ctx;
+ unsigned int interrupt;
+ struct __fwk_thread_ctx *calling_thread_ctx;
+ struct fwk_event *processed_event;
+ uint32_t flags;
+
+ if (!ctx.running) {
+ status = FWK_E_STATE;
+ goto error;
+ }
+
+ if ((event == NULL) || (resp_event == NULL))
+ goto error;
+
+ target_thread_ctx = thread_get_ctx(event->target_id);
+ if (target_thread_ctx == NULL)
+ goto error;
+
+ if (!fwk_module_is_valid_event_id(event->id))
+ goto error;
+
+ if ((fwk_interrupt_get_current(&interrupt) == FWK_SUCCESS) ||
+ (ctx.current_thread_ctx == &ctx.common_thread_ctx) ||
+ (target_thread_ctx == ctx.current_thread_ctx)) {
+ status = FWK_E_ACCESS;
+ goto error;
+ }
+
+ if (ctx.current_event != NULL)
+ event->source_id = ctx.current_event->target_id;
+ else if (!fwk_module_is_valid_entity_id(event->source_id))
+ goto error;
+
+ event->is_response = false;
+ event->is_delayed_response = false;
+ event->response_requested = true;
+ event->is_notification = false;
+ status = put_event(target_thread_ctx, event);
+ if (status != FWK_SUCCESS)
+ return status;
+
+ resp_event->cookie = event->cookie;
+ ctx.current_thread_ctx->response_event = resp_event;
+ ctx.current_thread_ctx->waiting_event_processing_completion = true;
+
+ /* Save the current thread and the current event */
+ calling_thread_ctx = ctx.current_thread_ctx;
+ processed_event = ctx.current_event;
+
+ /*
+ * Launch the processing of the next event if possible.
+ */
+ launch_next_event_processing(ctx.current_thread_ctx);
+
+ /* Wait the completion of 'processed_event' */
+ flags = osThreadFlagsWait(SIGNAL_EVENT_PROCESSED,
+ osFlagsWaitAll, osWaitForever);
+
+ calling_thread_ctx->response_event = NULL;
+ calling_thread_ctx->waiting_event_processing_completion = false;
+
+ if (flags != SIGNAL_EVENT_PROCESSED) {
+ status = FWK_E_OS;
+ goto error;
+ }
+
+ /* Restore the context of the current thread and the current event */
+ ctx.current_thread_ctx = calling_thread_ctx;
+ ctx.current_event = processed_event;
+
+ return FWK_SUCCESS;
+
+error:
+ FWK_HOST_PRINT(err_msg_func, status, __func__);
+ return status;
+}
+
+int fwk_thread_get_delayed_response(fwk_id_t id, uint32_t cookie,
+ struct fwk_event *event)
+{
+ int status = FWK_E_PARAM;
+ struct fwk_event *delayed_response;
+ unsigned int interrupt;
+
+ if (!ctx.initialized) {
+ status = FWK_E_INIT;
+ goto error;
+ }
+
+ if (fwk_interrupt_get_current(&interrupt) == FWK_SUCCESS) {
+ status = FWK_E_ACCESS;
+ goto error;
+ }
+
+ if (!fwk_module_is_valid_entity_id(id))
+ goto error;
+
+ if (event == NULL)
+ goto error;
+
+ delayed_response = search_delayed_response(id, cookie);
+ if (delayed_response == NULL)
+ goto error;
+
+ *event = *delayed_response;
+
+ return FWK_SUCCESS;
+
+error:
+ FWK_HOST_PRINT(err_msg_func, status, __func__);
+ return status;
+}
diff --git a/framework/src/fwk_notification.c b/framework/src/fwk_notification.c
new file mode 100644
index 0000000..779d3e6
--- /dev/null
+++ b/framework/src/fwk_notification.c
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Notification facilities.
+ */
+
+#include <fwk_assert.h>
+#include <fwk_host.h>
+#include <fwk_interrupt.h>
+#include <fwk_list.h>
+#include <fwk_mm.h>
+#include <internal/fwk_module.h>
+#include <internal/fwk_notification.h>
+#include <internal/fwk_thread.h>
+
+struct notification_ctx {
+ /*
+ * Flag indicating whether the notification framework component is
+ * initialized.
+ */
+ bool initialized;
+
+ /*
+ * Queue of notification subscription structures that are free.
+ */
+ struct fwk_dlist free_subscription_dlist;
+};
+
+static struct notification_ctx ctx;
+
+#ifdef BUILD_HOST
+static const char err_msg_func[] = "[NOT] Error %d in %s\n";
+#endif
+
+/*
+ * Static functions
+ */
+
+/*
+ * Get list of subscriptions for a given notification emitted by a given source
+ *
+ * \note The function assumes the validity of all its input parameters.
+ *
+ * \param notification_id Identifier of the notification.
+ * \param source_id Identifier of the emitter of the notification.
+ *
+ * \return A pointer to the doubly-linked list of subscriptions.
+ */
+static struct fwk_dlist *get_subscription_dlist(
+ fwk_id_t notification_id, fwk_id_t source_id)
+{
+ struct fwk_dlist *subscription_dlist_table;
+
+ if (fwk_id_is_type(source_id, FWK_ID_TYPE_MODULE)) {
+ subscription_dlist_table =
+ __fwk_module_get_ctx(source_id)->subscription_dlist_table;
+ } else {
+ subscription_dlist_table =
+ __fwk_module_get_element_ctx(source_id)->subscription_dlist_table;
+ }
+
+ return &subscription_dlist_table[
+ fwk_id_get_notification_idx(notification_id)];
+}
+
+/*
+ * Search for a subscription with a given source and target identifier in a list
+ * of subscriptions.
+ *
+ * \note The function assumes the validity of all its input parameters.
+ *
+ * \param subscription_dlist Pointer to the doubly-linked list of subscriptions
+ * to search.
+ * \param source_id Identifier of the emitter of the notification.
+ * \param target_id Identifier of the target of the notification.
+ *
+ * \return A pointer to the subscription that has been found if any, NULL
+ * otherwise.
+ */
+static struct __fwk_notification_subscription *search_subscription(
+ struct fwk_dlist *subscription_dlist,
+ fwk_id_t source_id, fwk_id_t target_id)
+{
+ struct fwk_dlist_node *node;
+ struct __fwk_notification_subscription *subscription;
+
+ for (node = fwk_list_head(subscription_dlist); node != NULL;
+ node = fwk_list_next(subscription_dlist, node)) {
+ subscription = FWK_LIST_GET(node,
+ struct __fwk_notification_subscription, dlist_node);
+
+ if (fwk_id_is_equal(subscription->source_id, source_id) &&
+ fwk_id_is_equal(subscription->target_id, target_id))
+ return subscription;
+ }
+
+ return NULL;
+}
+
+/*
+ * Send all the notifications associated with a notification event.
+ *
+ * \note The function assumes the validity of all its input parameters. The
+ * function is a sub-routine of 'fwk_notification_notify'.
+ *
+ * \param notification_event Pointer to the notification event.
+ * \param count Pointer to storage for the number of notifications being sent.
+ */
+static void send_notifications(struct fwk_event *notification_event,
+ unsigned int *count)
+{
+ int status;
+ struct fwk_dlist *subscription_dlist;
+ struct fwk_dlist_node *node;
+ struct __fwk_notification_subscription *subscription;
+
+ subscription_dlist = get_subscription_dlist(notification_event->id,
+ notification_event->source_id);
+ notification_event->is_response = false;
+ notification_event->is_notification = true;
+
+ for (node = fwk_list_head(subscription_dlist); node != NULL;
+ node = fwk_list_next(subscription_dlist, node)) {
+ subscription = FWK_LIST_GET(node,
+ struct __fwk_notification_subscription, dlist_node);
+
+ if (!fwk_id_is_equal(subscription->source_id,
+ notification_event->source_id))
+ continue;
+
+ notification_event->target_id = subscription->target_id;
+
+ status = __fwk_thread_put_notification(notification_event);
+ if (status == FWK_SUCCESS)
+ (*count)++;
+ }
+}
+
+/*
+ * Private interface functions
+ */
+
+int __fwk_notification_init(size_t notification_count)
+{
+ struct __fwk_notification_subscription *subscription_table,
+ *subscription_table_upper_limit, *subscription;
+
+ subscription_table = fwk_mm_calloc(
+ notification_count, sizeof(struct __fwk_notification_subscription));
+ if (subscription_table == NULL) {
+ FWK_HOST_PRINT(err_msg_func, FWK_E_NOMEM, __func__);
+ return FWK_E_NOMEM;
+ }
+
+ /* All the subscription structures are free to be used. */
+ fwk_list_init(&ctx.free_subscription_dlist);
+ for (subscription = subscription_table,
+ subscription_table_upper_limit = subscription + notification_count;
+ subscription < subscription_table_upper_limit;
+ subscription++)
+ fwk_list_push_tail(&ctx.free_subscription_dlist,
+ &subscription->dlist_node);
+
+ ctx.initialized = true;
+
+ return FWK_SUCCESS;
+}
+
+void __fwk_notification_reset(void)
+{
+ ctx = (struct notification_ctx){ 0 };
+}
+
+/*
+ * Public interface functions
+ */
+
+int fwk_notification_subscribe(fwk_id_t notification_id, fwk_id_t source_id,
+ fwk_id_t target_id)
+{
+ int status;
+ unsigned int interrupt;
+ struct fwk_dlist *subscription_dlist;
+ struct __fwk_notification_subscription *subscription;
+
+ if (!ctx.initialized) {
+ status = FWK_E_INIT;
+ goto error;
+ }
+
+ if (fwk_interrupt_get_current(&interrupt) == FWK_SUCCESS) {
+ status = FWK_E_HANDLER;
+ goto error;
+ }
+
+ if (!fwk_module_is_valid_notification_id(notification_id) ||
+ !fwk_module_is_valid_entity_id(source_id) ||
+ !fwk_module_is_valid_entity_id(target_id) ||
+ !fwk_id_is_equal(fwk_id_build_module_id(notification_id),
+ fwk_id_build_module_id(source_id))) {
+ status = FWK_E_PARAM;
+ goto error;
+ }
+
+ subscription_dlist = get_subscription_dlist(notification_id, source_id);
+ if (search_subscription(subscription_dlist, source_id, target_id) != NULL) {
+ status = FWK_E_STATE;
+ goto error;
+ }
+
+ subscription = FWK_LIST_GET(
+ fwk_list_pop_head(&ctx.free_subscription_dlist),
+ struct __fwk_notification_subscription, dlist_node);
+
+ if (subscription == NULL) {
+ status = FWK_E_NOMEM;
+ assert(false);
+ goto error;
+ }
+
+ subscription->source_id = source_id;
+ subscription->target_id = target_id;
+
+ fwk_interrupt_global_disable();
+ fwk_list_push_tail(subscription_dlist, &subscription->dlist_node);
+ fwk_interrupt_global_enable();
+
+ return FWK_SUCCESS;
+
+error:
+ FWK_HOST_PRINT(err_msg_func, status, __func__);
+ return status;
+}
+
+int fwk_notification_unsubscribe(fwk_id_t notification_id, fwk_id_t source_id,
+ fwk_id_t target_id)
+{
+ int status;
+ unsigned int interrupt;
+ struct fwk_dlist *subscription_dlist;
+ struct __fwk_notification_subscription *subscription;
+
+ if (!ctx.initialized) {
+ status = FWK_E_INIT;
+ goto error;
+ }
+
+ if (fwk_interrupt_get_current(&interrupt) == FWK_SUCCESS) {
+ status = FWK_E_HANDLER;
+ goto error;
+ }
+
+ if (!(fwk_module_is_valid_notification_id(notification_id)) ||
+ !(fwk_module_is_valid_entity_id(source_id)) ||
+ !(fwk_module_is_valid_entity_id(target_id)) ||
+ !fwk_id_is_equal(fwk_id_build_module_id(notification_id),
+ fwk_id_build_module_id(source_id))) {
+ status = FWK_E_PARAM;
+ goto error;
+ }
+
+ subscription_dlist = get_subscription_dlist(notification_id, source_id);
+ subscription = search_subscription(subscription_dlist,
+ source_id, target_id);
+ if (subscription == NULL) {
+ status = FWK_E_STATE;
+ goto error;
+ }
+
+ fwk_interrupt_global_disable();
+ fwk_list_remove(subscription_dlist, &subscription->dlist_node);
+ fwk_interrupt_global_enable();
+ fwk_list_push_tail(&ctx.free_subscription_dlist, &subscription->dlist_node);
+
+ return FWK_SUCCESS;
+
+error:
+ FWK_HOST_PRINT(err_msg_func, status, __func__);
+ return status;
+}
+
+int fwk_notification_notify(struct fwk_event *notification_event,
+ unsigned int *count)
+{
+ int status;
+ unsigned int interrupt;
+ const struct fwk_event *current_event;
+
+ if (!ctx.initialized) {
+ status = FWK_E_INIT;
+ goto error;
+ }
+
+ if ((notification_event == NULL) || (count == NULL))
+ return FWK_E_PARAM;
+
+ if (fwk_interrupt_get_current(&interrupt) == FWK_SUCCESS) {
+ if (!fwk_module_is_valid_entity_id(notification_event->source_id)) {
+ status = FWK_E_PARAM;
+ goto error;
+ }
+ } else {
+ current_event = __fwk_thread_get_current_event();
+ if (current_event != NULL)
+ notification_event->source_id = current_event->target_id;
+ }
+
+ if (!fwk_module_is_valid_notification_id(notification_event->id) ||
+ (fwk_id_get_module_idx(notification_event->id) !=
+ fwk_id_get_module_idx(notification_event->source_id))) {
+ status = FWK_E_PARAM;
+ goto error;
+ }
+
+ *count = 0;
+ send_notifications(notification_event, count);
+
+ return FWK_SUCCESS;
+
+error:
+ FWK_HOST_PRINT(err_msg_func, status, __func__);
+ return status;
+}
diff --git a/framework/src/fwk_slist.c b/framework/src/fwk_slist.c
new file mode 100644
index 0000000..650b29f
--- /dev/null
+++ b/framework/src/fwk_slist.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Intrusive circular singly-linked list.
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <fwk_assert.h>
+#include <fwk_list.h>
+
+void __fwk_slist_init(struct fwk_slist *list)
+{
+ assert(list != NULL);
+
+ list->head = (struct fwk_slist_node *)list;
+ list->tail = (struct fwk_slist_node *)list;
+}
+
+struct fwk_slist_node *__fwk_slist_head(const struct fwk_slist *list)
+{
+ assert(list != NULL);
+
+ if (fwk_list_is_empty(list))
+ return NULL;
+
+ return list->head;
+}
+
+bool __fwk_slist_is_empty(const struct fwk_slist *list)
+{
+ bool is_empty;
+
+ assert(list != NULL);
+
+ is_empty = list->head == (struct fwk_slist_node *)list;
+
+ if (is_empty)
+ assert(list->tail == list->head);
+
+ return is_empty;
+}
+
+void __fwk_slist_push_head(
+ struct fwk_slist *list,
+ struct fwk_slist_node *new)
+{
+ assert(list != NULL);
+ assert(new != NULL);
+ fwk_expect(new->next == NULL);
+
+ new->next = list->head;
+
+ list->head = new;
+ if (list->tail == (struct fwk_slist_node *)list)
+ list->tail = new;
+}
+
+void __fwk_slist_push_tail(
+ struct fwk_slist *list,
+ struct fwk_slist_node *new)
+{
+ assert(list != NULL);
+ assert(new != NULL);
+ fwk_expect(new->next == NULL);
+
+ new->next = (struct fwk_slist_node *)list;
+
+ list->tail->next = new;
+ list->tail = new;
+}
+
+struct fwk_slist_node *__fwk_slist_pop_head(struct fwk_slist *list)
+{
+ struct fwk_slist_node *popped;
+
+ assert(list != NULL);
+
+ if (fwk_list_is_empty(list))
+ return NULL;
+
+ popped = list->head;
+ if (popped->next == (struct fwk_slist_node *)list)
+ list->tail = (struct fwk_slist_node *)list;
+
+ list->head = popped->next;
+
+#ifdef BUILD_MODE_DEBUG
+ popped->next = NULL;
+#endif
+
+ return popped;
+}
+
+struct fwk_slist_node *__fwk_slist_next(
+ const struct fwk_slist *list,
+ const struct fwk_slist_node *node)
+{
+ assert(list != NULL);
+ assert(node != NULL);
+
+ assert(__fwk_slist_contains(list, node));
+
+ return (node->next == (struct fwk_slist_node *)list) ? NULL : node->next;
+}
+
+void __fwk_slist_remove(
+ struct fwk_slist *list,
+ struct fwk_slist_node *node)
+{
+ assert(list != NULL);
+ assert(node != NULL);
+ assert(node->next != NULL);
+
+ struct fwk_slist_node *node_iter = (struct fwk_slist_node *)list;
+
+ while (node_iter->next != (struct fwk_slist_node *)list) {
+ if (node_iter->next == node) {
+ node_iter->next = node->next;
+ if (node->next == (struct fwk_slist_node *)list)
+ list->tail = (struct fwk_slist_node *)node_iter;
+ #ifdef BUILD_MODE_DEBUG
+ node->next = NULL;
+ #endif
+ return;
+ }
+ node_iter = node_iter->next;
+ }
+
+ assert(false);
+}
+
+bool __fwk_slist_contains(
+ const struct fwk_slist *list,
+ const struct fwk_slist_node *node)
+{
+ const struct fwk_slist_node *node_iter;
+
+ assert(list != NULL);
+ assert(node != NULL);
+ assert(node->next != NULL);
+
+ node_iter = (struct fwk_slist_node *)list;
+
+ while (node_iter->next != (struct fwk_slist_node *)list) {
+ if (node_iter->next == node)
+ return true;
+
+ node_iter = node_iter->next;
+ }
+
+ return false;
+}
+
+static_assert(offsetof(struct fwk_slist, head) ==
+ offsetof(struct fwk_slist_node, next),
+ "fwk_slist::head not aligned with fwk_slist_node::next");
diff --git a/framework/src/fwk_thread.c b/framework/src/fwk_thread.c
new file mode 100644
index 0000000..c3d1219
--- /dev/null
+++ b/framework/src/fwk_thread.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Description:
+ * Single-thread facilities.
+ */
+
+#include <stdbool.h>
+#include <string.h>
+#include <fwk_assert.h>
+#include <fwk_element.h>
+#include <fwk_errno.h>
+#include <fwk_host.h>
+#include <fwk_id.h>
+#include <fwk_interrupt.h>
+#include <fwk_mm.h>
+#include <internal/fwk_module.h>
+#include <internal/fwk_notification.h>
+#include <internal/fwk_single_thread.h>
+#include <internal/fwk_thread.h>
+
+static struct __fwk_thread_ctx ctx;
+
+#ifdef BUILD_HOST
+static const char err_msg_line[] = "[THR] Error %d in %s @%d\n";
+static const char err_msg_func[] = "[THR] Error %d in %s\n";
+#endif
+
+/*
+ * Static functions
+ */
+
+static int put_event(struct fwk_event *event)
+{
+ struct fwk_event *free_event;
+ unsigned int interrupt;
+
+ free_event = FWK_LIST_GET(fwk_list_pop_head(&ctx.free_event_queue),
+ struct fwk_event, slist_node);
+
+ if (free_event == NULL) {
+ FWK_HOST_PRINT(err_msg_func, FWK_E_NOMEM, __func__);
+ assert(false);
+ return FWK_E_NOMEM;
+ }
+
+ *free_event = *event;
+
+ if (fwk_interrupt_get_current(&interrupt) != FWK_SUCCESS)
+ fwk_list_push_tail(&ctx.event_queue, &free_event->slist_node);
+ else
+ fwk_list_push_tail(&ctx.isr_event_queue, &free_event->slist_node);
+
+ return FWK_SUCCESS;
+}
+
+static void process_next_event(void)
+{
+ int status;
+ struct fwk_event *event, async_response_event = {0};
+ const struct fwk_module *module;
+ int (*process_event)(const struct fwk_event *event,
+ struct fwk_event *resp_event);
+
+
+ ctx.current_event = event = FWK_LIST_GET(
+ fwk_list_pop_head(&ctx.event_queue), struct fwk_event, slist_node);
+
+ FWK_HOST_PRINT("[THR] Get event (%s,%s,%s)\n",
+ FWK_ID_STR(event->source_id), FWK_ID_STR(event->target_id),
+ FWK_ID_STR(event->id));
+
+ module = __fwk_module_get_ctx(event->target_id)->desc;
+ process_event = event->is_notification ? module->process_notification :
+ module->process_event;
+
+ if (event->response_requested) {
+ async_response_event.source_id = event->target_id;
+ async_response_event.target_id = event->source_id;
+ async_response_event.id = event->id;
+ memcpy(&async_response_event.params, &event->params,
+ sizeof(async_response_event.params));
+
+ status = process_event(event, &async_response_event);
+ if (status != FWK_SUCCESS)
+ FWK_HOST_PRINT(err_msg_line, status, __func__, __LINE__);
+
+ async_response_event.is_response = true;
+ async_response_event.response_requested = false;
+ async_response_event.is_notification = event->is_notification;
+ if (!async_response_event.is_delayed_response)
+ put_event(&async_response_event);
+ } else {
+ status = process_event(event, &async_response_event);
+ if (status != FWK_SUCCESS)
+ FWK_HOST_PRINT(err_msg_line, status, __func__, __LINE__);
+ }
+
+ ctx.current_event = NULL;
+
+ fwk_interrupt_global_disable();
+ fwk_list_push_tail(&ctx.free_event_queue, &event->slist_node);
+ fwk_interrupt_global_enable();
+
+ return;
+}
+
+static void process_isr(void)
+{
+ struct fwk_event *isr_event;
+
+ fwk_interrupt_global_disable();
+ isr_event = FWK_LIST_GET(fwk_list_pop_head(&ctx.isr_event_queue),
+ struct fwk_event, slist_node);
+ fwk_interrupt_global_enable();
+
+ FWK_HOST_PRINT("[THR] Get ISR event (%s,%s,%s)\n",
+ FWK_ID_STR(isr_event->source_id),
+ FWK_ID_STR(isr_event->target_id),
+ FWK_ID_STR(isr_event->id));
+
+ fwk_list_push_tail(&ctx.event_queue, &isr_event->slist_node);
+}
+
+/*
+ * Private interface functions
+ */
+
+int __fwk_thread_init(size_t event_count)
+{
+ int status;
+ struct fwk_event *event_table, *event;
+
+ event_table = fwk_mm_calloc(event_count, sizeof(struct fwk_event));
+ if (event_table == NULL) {
+ status = FWK_E_NOMEM;
+ goto error;
+ }
+
+ /* All the event structures are free to be used. */
+ fwk_list_init(&ctx.free_event_queue);
+ fwk_list_init(&ctx.event_queue);
+ fwk_list_init(&ctx.isr_event_queue);
+
+ for (event = event_table;
+ event < (event_table + event_count);
+ event++)
+ fwk_list_push_tail(&ctx.free_event_queue, &event->slist_node);
+
+ ctx.initialized = true;
+
+ return FWK_SUCCESS;
+
+error:
+ FWK_HOST_PRINT(err_msg_func, status, __func__);
+ return status;
+}
+
+noreturn void __fwk_thread_run(void)
+{
+ for (;;) {
+ while (!fwk_list_is_empty(&ctx.event_queue))
+ process_next_event();
+
+ while (fwk_list_is_empty(&ctx.isr_event_queue))
+ continue;
+
+ process_isr();
+ }
+}
+
+struct __fwk_thread_ctx *__fwk_thread_get_ctx(void)
+{
+ return &ctx;
+}
+
+const struct fwk_event *__fwk_thread_get_current_event(void)
+{
+ return ctx.current_event;
+}
+
+#ifdef BUILD_HAS_NOTIFICATION
+int __fwk_thread_put_notification(struct fwk_event *event)
+{
+ event->is_response = false;
+ event->is_notification = true;
+
+ return put_event(event);
+}
+#endif
+
+/*
+ * Public interface functions
+ */
+
+int fwk_thread_put_event(struct fwk_event *event)
+{
+ int status = FWK_E_PARAM;
+ unsigned int interrupt;
+
+ if (!ctx.initialized) {
+ status = FWK_E_INIT;
+ goto error;
+ }
+
+ if (event == NULL)
+ goto error;
+
+ if ((fwk_interrupt_get_current(&interrupt) != FWK_SUCCESS) &&
+ (ctx.current_event != NULL))
+ event->source_id = ctx.current_event->target_id;
+ else if (!fwk_module_is_valid_entity_id(event->source_id))
+ goto error;
+
+ if (!fwk_module_is_valid_entity_id(event->target_id) ||
+ !fwk_module_is_valid_event_id(event->id))
+ goto error;
+
+ if (event->is_response) {
+ if (fwk_id_get_module_idx(event->source_id) !=
+ fwk_id_get_module_idx(event->id))
+ goto error;
+ if (event->response_requested)
+ goto error;
+ } else {
+ if (fwk_id_get_module_idx(event->target_id) !=
+ fwk_id_get_module_idx(event->id))
+ goto error;
+ if (event->is_notification)
+ goto error;
+ }
+
+ return put_event(event);
+
+error:
+ FWK_HOST_PRINT(err_msg_func, status, __func__);
+ return status;
+}
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..204ab85
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,49 @@
+Readme
+======
+
+Prerequisites (Tools)
+---------------------
+
+To properly build External system firmware for a target product, the following tools are
+required:
+
+- Git
+- [GNU Arm Embedded Toolchain ("6-2017-q2-update" or later)](https://developer.arm.com/open-source/gnu-toolchain/gnu-rm)
+- GNU Make
+- Python 3 (3.5.0 or later)
+
+Prerequisites (CMSIS Libraries)
+-------------------------------
+
+External system firmware requires the use of components from the Cortex Microcontroller
+System Interface Standard (CMSIS) Software Pack, specifically the CMSIS Core
+and CMSIS Real-Time Operating System (RTOS) components. The CMSIS Software pack
+is included as a Git submodule.
+
+To retrieve the CMSIS Software pack, just initialize and update the submodule
+of the repository.
+
+ $> git submodule update --init
+
+The required CMSIS components are now present and the correct version has been
+selected.
+
+Building Products
+-----------------
+To build a product the basic command format for invoking 'make' is:
+
+ $> make [TARGET] <PRODUCT=<name>> [OPTIONS]
+
+It is not necessary to provide a target since the default target for the product
+will build all the firmware contained within the product.
+
+The 'help' target provides further information on the arguments that can be
+given:
+
+ $> make help
+
+For more guidance on the build system, refer to the full set of documentation
+that includes the 'Build System' chapter.
+
+
+
diff --git a/tools/build_string.py b/tools/build_string.py
new file mode 100755
index 0000000..e3e9567
--- /dev/null
+++ b/tools/build_string.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python3
+#
+#
+# Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+
+import subprocess
+from datetime import datetime
+
+
+def main():
+ # Check if git is available and that the source is within a git repository
+ try:
+ cmd = 'git describe --tags --dirty --always'
+ output = subprocess.check_output(cmd.split())
+ git_string = output.decode('utf-8').strip()
+ except BaseException:
+ git_string = 'Unknown'
+
+ print("{}-{}".format(datetime.now().strftime('%Y-%m-%d_%H-%M-%S'),
+ git_string))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/build_system/cpu.mk b/tools/build_system/cpu.mk
new file mode 100644
index 0000000..3a61b6c
--- /dev/null
+++ b/tools/build_system/cpu.mk
@@ -0,0 +1,34 @@
+#
+# Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+include $(BS_DIR)/defs.mk
+
+BS_ARCH_CPU := $(BS_FIRMWARE_CPU)
+
+# Supported ARMv6-M CPUs
+ARMV6M_CPU := cortex-m0plus
+
+# Supported ARMv7-M CPUs
+ARMV7M_CPUS := cortex-m3 cortex-m7
+
+ifneq ($(findstring $(BS_FIRMWARE_CPU),$(ARMV6M_CPU)),)
+ BS_ARCH_ARCH := armv6-m
+ BS_ARCH_ISA := thumb
+ $(call add_once,LDFLAGS_GCC,--specs=nano.specs)
+
+
+else ifneq ($(findstring $(BS_FIRMWARE_CPU),$(ARMV7M_CPUS)),)
+ BS_ARCH_ARCH := armv7-m
+ BS_ARCH_ISA := thumb
+ $(call add_once,LDFLAGS_GCC,--specs=nano.specs)
+
+else ifeq ($(BS_FIRMWARE_CPU),host)
+ BS_ARCH_ARCH := host
+
+else
+ $(erro "$(BS_FIRMWARE_CPU) is not a supported CPU. Aborting...")
+endif
+
diff --git a/tools/build_system/defs.mk b/tools/build_system/defs.mk
new file mode 100644
index 0000000..ca34962
--- /dev/null
+++ b/tools/build_system/defs.mk
@@ -0,0 +1,84 @@
+#
+# Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+# Escape comma character
+comma = ,
+
+#
+# Suffix used for directories containing resources built with multithreading
+# support enabled
+MULTHREADING_SUFFIX := _mt
+
+#
+# Suffix used for directories containing resources built with notification
+# support enabled
+NOTIFICATION_SUFFIX := _nt
+
+#
+# SE Software Version
+#
+$(call add_once,DEFINES,BUILD_VERSION_MAJOR=$(VERSION_MAJOR))
+$(call add_once,DEFINES,BUILD_VERSION_MINOR=$(VERSION_MINOR))
+$(call add_once,DEFINES,BUILD_VERSION_PATCH=$(VERSION_PATCH))
+$(call add_once,DEFINES,BUILD_VERSION_STRING=\"$(VERSION_STRING)\")
+$(call add_once,DEFINES, \
+ BUILD_VERSION_DESCRIBE_STRING=\"$(VERSION_DESCRIBE_STRING)\")
+
+#
+# Returns a path relative to the top directory
+#
+# Param $1 Target path
+# Return String containing the relative path
+define relative-path
+ $(subst $(TOP_DIR)/,,$1)
+endef
+
+#
+# Shows an action that is being performed on a path
+#
+# Param $1 Action string
+# Return None.
+define show-action
+ @echo [$1] $(call relative-path, $2)
+endef
+
+#
+# Returns the path to the library binary
+#
+# Param $1 Library name
+# Return String containing the library path
+define lib_path
+ $(BUILD_FIRMWARE_DIR)/$1/$(MODE)/lib/lib.a
+endef
+
+#
+# Adds a value to a list variable only once.
+#
+# Param $1 Variable name to receive the value
+# Param $2 Value to be added to the list. If the value is already in the list
+# then no action is performed.
+# Return None.
+define add_once
+ $(eval $(call _add_once,$1,$2))
+endef
+
+define _add_once
+ ifeq ($$(findstring $2,$($1)),)
+ $1 += $2
+ endif
+endef
+
+#
+# Converts input string to uppercase
+#
+# Param $1 String
+# Return Input string in uppercase
+define to_upper
+$(shell echo $(1) | tr '[:lower:]' '[:upper:]')
+endef
+
+.SECONDEXPANSION:
+targetdir := $$(dir $$@)
diff --git a/tools/build_system/doc.md b/tools/build_system/doc.md
new file mode 100644
index 0000000..4e45cb2
--- /dev/null
+++ b/tools/build_system/doc.md
@@ -0,0 +1,203 @@
+Build System
+============
+
+Overview
+========
+
+The External System Software build system is composed of a top-level Makefile and a
+collection of .mk files used to build and describe the building blocks and tools
+that create the final firmware binaries.
+
+This documentation covers the use of the build system when creating products,
+firmware, and modules.
+
+For details on how to build an existing product, please refer to the
+documentation using the build system's "help" parameter:
+
+ $> make help
+
+Product and firmware hierarchy
+==============================
+
+A product is a collection of firmware. All products are located under the
+__product__ directory and must adhere to the following hierarchy:
+
+ <root>
+ └─ product
+    └── <product>
+    ├── product.mk
+ │ ├── include
+ │ │ └── <product level header files...>
+ │ └── src
+ │ └── <product level source files...>
+    ├── <firmware>
+    │   ├── firmware.mk
+    │   └── <firmware level files...>
+    └── <firmware>
+       ├── firmware.mk
+       └── <firmware level files...>
+
+__Note:__ The names of the \<product\> and \<firmware\> directories must not
+contain spaces.
+
+The product.mk and firmware.mk files are described in the following sections.
+
+The product.mk file
+-------------------
+
+The product.mk file describes a product to the build system.
+
+The following parameters are mandatory:
+
+* __BS_PRODUCT_NAME__ - Human-friendly name for the product. The content of this
+ variable is exposed to the compilation units.
+* __BS_PRODUCT_FIRMWARE_LIST__ - List of firmware directories under the current
+ product.
+
+The firmware.mk file
+--------------------
+
+The firmware.mk file describes a firmware to the build system.
+
+The following parameters are mandatory:
+* __BS_FIRMWARE_CPU__ - CPU architecture.
+* __BS_FIRMWARE_HAS_MULTITHREADING__ <yes|no> - Multithreading support. When set
+ to yes, firmware will be built with multithreading support.
+* __BS_FIRMWARE_HAS_NOTIFICATION__ <yes|no> - Notification support. When set
+ to yes, firmware will be built with notification support.
+* __BS_FIRMWARE_MODULES__ - The list of modules to be included and built into
+ the firmware and any APIs to be omitted from each module.
+* __BS_FIRMWARE_MODULE_HEADERS_ONLY__ - The list of modules to have their header
+ files included into the firmware's build allowing other modules to use their
+ definitions. __Note__: These modules are not built into the firmware, only
+ their header files are made available.
+* __BS_FIRMWARE_SOURCES__ - The list of source files to be built as part of the
+ firmware. The source files (.S and .c) can be either at product or firmware
+ level.
+
+The format of the __BS_FIRMWARE_MODULES__ parameter can be seen in the following
+example:
+\code
+BS_FIRMWARE_MODULES := module_a \
+ module_b \
+ module_c,foo,bar \
+\endcode
+
+In this example, modules A, B, and C will all be built into the
+firmware. Module C will have its 'foo' and 'bar' APIs removed.
+
+If a module should remove an API, it will be compiled with a \#define in the
+following format:
+\code BUILD_DISABLE_API_<api_name_upper> \endcode
+
+For example, module C (only) will have the following definitions when it is
+compiled for this firmware:
+\code
+BUILD_DISABLE_API_FOO
+BUILD_DISABLE_API_BAR
+\endcode
+
+
+Module
+======
+
+Modules are the building blocks of a firmware product and are built as
+libraries. Modules can be implemented under the modules/ directory at the root
+of the project, or they can be product-specific and implemented under the
+product/\<product\>/modules directory. In either case, modules have the
+following directory structure:
+
+ <module root>
+ └── <module>
+ ├── include
+ │ └── <header files...>
+ ├── src
+ │ ├── Makefile
+ │ └── <source files...>
+ └── doc
+ └── <documentation files...>
+
+__Note:__ The name of the \<module\> directory must not contain spaces.
+
+The name of the \<module\> directory is used in __BS_FIRMWARE_MODULES__ by the
+firmware (see firmware.mk, above).
+
+The __doc__ directory is optional and may contain markdown (.md) based
+documentation.
+
+ The following parameters are required in each module's Makefile:
+* __BS_LIB_NAME__ - Human-friendly name for the module.
+* __BS_LIB_SOURCES__ - List of source files to be built as part of the module.
+
+Module Code Generation
+======================
+
+When a firmware is built there are two prerequisite files that will be generated
+by the build system, specifically by the __gen_module_code.py__ script:
+* fwk_module_idx.h: Contains an enumeration of the indices of the modules that
+ make up the firmware. The ordering of the module indices in the enumeration
+ within fwk_module_idx.h is guaranteed to follow the order of the module
+ names in the BS_FIRMWARE_MODULES list within the firmware's firmware.mk
+ file. This same ordering is used by the framework at runtime when performing
+ operations that involve iterating over all the modules that are present in
+ the firmware, such as the init_modules() function in fwk_module.c.
+* fwk_module_list.c: Contains a table of pointers to module descriptors, one
+ for each module that is being built as part of the firmware. This file and
+ its contents are used internally by the framework and should not normally
+ be used by other units such as modules.
+
+Multithreading Support {#section_multithreading}
+======================
+
+When building a firmware and its dependencies, the
+BS_FIRMWARE_HAS_MULTITHREADING parameter controls whether multithreading support
+is enabled or not.
+
+When multithreading support is enabled, the following applies:
+
+* The BUILD_HAS_MULTITHREADING definition is defined for the units being built.
+* Multithreading specific APIs are made available to the modules via the
+ framework components (see \ref GroupLibFramework).
+* The header files from the underlying software layer responsible for providing
+ the multithreading capabilities (e.g. embedded OS) are added to the include
+ path for the units being built.
+* The underlying software responsible for providing the multithreading support
+ is included in the firmware during the linking phase.
+
+Notification Support {#section_notification}
+====================
+
+When building a firmware and its dependencies, the
+BS_FIRMWARE_HAS_NOTIFICATION parameter controls whether notification support
+is enabled or not.
+
+When notification support is enabled, the following applies:
+
+* The BUILD_HAS_NOTIFICATION definition is defined for the units being built.
+* Notification specific APIs are made available to the modules via the
+ framework components (see \ref GroupLibFramework).
+
+Definitions
+===========
+
+The build system sets the following definitions during the compilation of C
+and assembly units:
+
+* __BUILD_HOST__ - Set when the CPU target is "host".
+* __BUILD_HAS_MULTITHREADING__ - Set when the build has multithreading support.
+* __BUILD_HAS_NOTIFICATION__ - Set when the build has notification support.
+* __BUILD_STRING__ - A string containing build information (date, time and git
+ commit). The string is assembled using the tool build_string.py.
+* __BUILD_TESTS__ - Set when building the framework unit tests.
+* __BUILD_MODE_DEBUG__ - Set when building in debug mode.
+* __NDEBUG__ - Set when building in release mode. This definition is used by the
+ standard C library to disable the assert() support.
+* __BUILD_VERSION_MAJOR__ - Major version number.
+* __BUILD_VERSION_MINOR__ - Minor version number.
+* __BUILD_VERSION_PATCH__ - Patch version number.
+* __BUILD_VERSION_STRING__ - String version using the format
+ "v<major>.<minor>.<patch>". Example: "v2.3.1".
+* __BUILD_VERSION_DESCRIBE_STRING__ - String containing version, date and git
+ commit description. If the source code is not under a git repository, the
+ string __unknown__ will be used instead.
+* __BUILD_HAS_MOD_<MODULE NAME>__ - Set for each module being part of the build.
diff --git a/tools/build_system/firmware.mk b/tools/build_system/firmware.mk
new file mode 100644
index 0000000..91c7103
--- /dev/null
+++ b/tools/build_system/firmware.mk
@@ -0,0 +1,241 @@
+#
+# Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+include $(BS_DIR)/defs.mk
+
+$(info == Building firmware $(FIRMWARE))
+
+#
+# Validate that all mandatory definitions are defined
+#
+ifeq ($(BS_FIRMWARE_CPU),)
+ $(error "You must define BS_FIRMWARE_CPU. Aborting...")
+endif
+
+ifeq ($(BS_FIRMWARE_HAS_MULTITHREADING),)
+ $(error "You must define BS_FIRMWARE_HAS_MULTITHREADING. \
+ Valid options are: 'yes' and 'no'. \
+ Aborting...")
+endif
+
+ifeq ($(BS_FIRMWARE_HAS_NOTIFICATION),)
+ $(error "You must define BS_FIRMWARE_HAS_NOTIFICATION. \
+ Valid options are: 'yes' and 'no'. \
+ Aborting...")
+endif
+
+ifneq ($(filter-out yes no,$(BS_FIRMWARE_HAS_MULTITHREADING)),)
+ $(error "Invalid parameter for BS_FIRMWARE_HAS_MULTITHREADING. \
+ Valid options are: 'yes' and 'no'. \
+ Aborting...")
+endif
+
+ifneq ($(filter-out yes no,$(BS_FIRMWARE_HAS_NOTIFICATION)),)
+ $(error "Invalid parameter for BS_FIRMWARE_HAS_NOTIFICATION. \
+ Valid options are: 'yes' and 'no'. \
+ Aborting...")
+endif
+
+export BS_FIRMWARE_CPU
+export BS_FIRMWARE_HAS_MULTITHREADING
+export BS_FIRMWARE_HAS_NOTIFICATION
+
+#
+# Generate FIRMWARE_MODULES_LIST and list of excluded APIs for each module in
+# the following format:
+#
+# <MODULE_NAME>_DISABLED_APIS = <API_1> <API_2> ...
+#
+# NOTE: <MODULE_NAME> and <API_n> are all in uppercase
+#
+SPLIT_INPUT = $(subst $(comma), ,$(1))
+GET_MODULE_NAME = $(word 1,$(SPLIT_INPUT))
+
+define process_module_entry
+ export $(call to_upper,$(GET_MODULE_NAME))_DISABLED_APIS = \
+ $(call to_upper,$(filter-out $(GET_MODULE_NAME),$(SPLIT_INPUT)))
+
+ FIRMWARE_MODULES_LIST += $(GET_MODULE_NAME)
+ DEFINES += BUILD_HAS_MOD_$(call to_upper,$(GET_MODULE_NAME))=1
+endef
+
+$(foreach entry, \
+ $(BS_FIRMWARE_MODULES), \
+ $(eval $(call process_module_entry, $(entry))))
+
+include $(BS_DIR)/cpu.mk
+
+BUILD_PRODUCT_DIR := $(BUILD_DIR)/product/$(PRODUCT)
+BUILD_FIRMWARE_DIR := $(BUILD_PRODUCT_DIR)/$(FIRMWARE)
+BIN_DIR := $(BUILD_FIRMWARE_DIR)/$(MODE)/bin
+OBJ_DIR := $(BUILD_FIRMWARE_DIR)/$(MODE)/obj
+PRODUCT_MODULES_DIR := $(PRODUCT_DIR)/module
+FIRMWARE_DIR := $(PRODUCT_DIR)/$(FIRMWARE)
+
+TARGET := $(BIN_DIR)/firmware
+TARGET_BIN := $(TARGET).bin
+TARGET_ELF := $(TARGET).elf
+
+vpath %.c $(FIRMWARE_DIR)
+vpath %.S $(FIRMWARE_DIR)
+vpath %.c $(PRODUCT_DIR)/src
+vpath %.S $(PRODUCT_DIR)/src
+
+goal: $(TARGET_BIN)
+
+ifneq ($(BS_ARCH_CPU),host)
+ SCATTER_SRC = $(ARCH_DIR)/src/$(BS_ARCH_ARCH)/ld.S
+ SCATTER_PP = $(OBJ_DIR)/ld_preproc.s
+endif
+
+#
+# Sources
+#
+SOURCES = $(BS_FIRMWARE_SOURCES)
+
+#
+# Modules
+#
+ALL_STANDARD_MODULES := $(shell ls $(MODULES_DIR) 2>/dev/null)
+ALL_PRODUCT_MODULES := $(shell ls $(PRODUCT_MODULES_DIR) 2>/dev/null)
+
+# Check for conflicts between module names
+CONFLICTING_MODULES := $(filter $(ALL_PRODUCT_MODULES), $(ALL_STANDARD_MODULES))
+ifneq ($(CONFLICTING_MODULES),)
+ $(error "The following module(s) in '$(PRODUCT)' conflict with modules of \
+ the same name in $(MODULES_DIR): $(CONFLICTING_MODULES). \
+ Aborting...")
+endif
+
+# Check for missing or invalid modules
+MISSING_MODULES := $(filter-out $(ALL_STANDARD_MODULES) $(ALL_PRODUCT_MODULES),\
+ $(FIRMWARE_MODULES_LIST) $(BS_FIRMWARE_MODULE_HEADERS_ONLY))
+ifneq ($(MISSING_MODULES),)
+ $(error "Missing or invalid module(s): $(MISSING_MODULES). Aborting...")
+endif
+
+# Modules selected to be built into the firmware
+BUILD_STANDARD_MODULES := $(filter $(ALL_STANDARD_MODULES), \
+ $(FIRMWARE_MODULES_LIST))
+BUILD_PRODUCT_MODULES := $(filter $(ALL_PRODUCT_MODULES), \
+ $(FIRMWARE_MODULES_LIST))
+
+# Module selected to have their headers made available for inclusion by other
+# modules and their configuration files. These modules are not built into the
+# firmware.
+HEADER_STANDARD_MODULES := $(filter $(BS_FIRMWARE_MODULE_HEADERS_ONLY), \
+ $(ALL_STANDARD_MODULES))
+HEADER_PRODUCT_MODULES := $(filter $(BS_FIRMWARE_MODULE_HEADERS_ONLY), \
+ $(ALL_PRODUCT_MODULES))
+
+ifeq ($(BS_FIRMWARE_HAS_MULTITHREADING),yes)
+ BUILD_SUFFIX := $(MULTHREADING_SUFFIX)
+ BUILD_HAS_MULTITHREADING := yes
+
+ ifeq ($(BS_ARCH_ARCH),armv7-m)
+ LIBS_y += $(OS_DIR)/RTX/Library/GCC/libRTX_CM3.a
+ else ifeq ($(BS_ARCH_ARCH),armv6-m)
+ LIBS_y += $(OS_DIR)/RTX/Library/GCC/libRTX_CM0.a
+ endif
+
+ INCLUDES += $(OS_DIR)/RTX/Source
+ INCLUDES += $(OS_DIR)/RTX/Include
+ INCLUDES += $(OS_DIR)/../Core/Include
+else
+ BUILD_HAS_MULTITHREADING := no
+endif
+export BUILD_HAS_MULTITHREADING
+
+ifeq ($(BS_FIRMWARE_HAS_NOTIFICATION),yes)
+ BUILD_SUFFIX := $(BUILD_SUFFIX)$(NOTIFICATION_SUFFIX)
+ BUILD_HAS_NOTIFICATION := yes
+else
+ BUILD_HAS_NOTIFICATION := no
+endif
+export BUILD_HAS_NOTIFICATION
+
+ifeq ($(BS_FIRMWARE_HAS_OPENAMP),yes)
+ BUILD_HAS_OPENAMP := yes
+else
+ BUILD_HAS_OPENAMP := no
+endif
+export BUILD_HAS_OPENAMP
+
+# Add directories to the list of targets to build
+LIB_TARGETS_y += $(patsubst %,$(MODULES_DIR)/%/src, \
+ $(BUILD_STANDARD_MODULES))
+LIB_TARGETS_y += $(patsubst %,$(PRODUCT_MODULES_DIR)/%/src, \
+ $(BUILD_PRODUCT_MODULES))
+
+# Add lib path to the list of modules to link
+MODULE_LIBS_y += $(patsubst %, \
+ $(BUILD_FIRMWARE_DIR)/module/%$(BUILD_SUFFIX)/$(MODE)/lib/lib.a, \
+ $(BUILD_STANDARD_MODULES) $(BUILD_PRODUCT_MODULES))
+
+# Create a list of include directories from the selected modules
+MODULE_INCLUDES += $(patsubst %,$(MODULES_DIR)/%/include, \
+ $(BUILD_STANDARD_MODULES))
+MODULE_INCLUDES += $(patsubst %,$(PRODUCT_MODULES_DIR)/%/include, \
+ $(BUILD_PRODUCT_MODULES))
+MODULE_INCLUDES += $(patsubst %,$(MODULES_DIR)/%/include, \
+ $(HEADER_STANDARD_MODULES))
+MODULE_INCLUDES += $(patsubst %,$(PRODUCT_MODULES_DIR)/%/include, \
+ $(HEADER_PRODUCT_MODULES))
+
+# Default product include directories
+PRODUCT_INCLUDES += $(PRODUCT_DIR)
+PRODUCT_INCLUDES += $(PRODUCT_DIR)/include
+
+# Add the firmware directory to the main INCLUDES list
+INCLUDES += $(FIRMWARE_DIR)
+
+# Add module and product includes to the main INCLUDES list
+export INCLUDES += $(MODULE_INCLUDES) $(PRODUCT_INCLUDES)
+
+#
+# Standard libraries
+#
+LIB_TARGETS_y += $(ARCH_DIR)/src
+LIB_TARGETS_y += $(FWK_DIR)/src
+
+LIBS_y += $(call lib_path,arch$(BUILD_SUFFIX))
+LIBS_y += $(call lib_path,framework$(BUILD_SUFFIX))
+
+SOURCES += $(BUILD_FIRMWARE_DIR)/fwk_module_list.c
+$(BUILD_FIRMWARE_DIR)/fwk_module_list.c: gen_module
+EXTRA_DEP := gen_module
+export EXTRA_DEP
+export BUILD_FIRMWARE_DIR
+export FIRMWARE_MODULES_LIST
+export DEFINES
+
+include $(BS_DIR)/rules.mk
+
+LDFLAGS_GCC += -Wl,-Map=$(TARGET).map
+
+ifneq ($(BS_ARCH_CPU),host)
+ LDFLAGS_GCC += -Wl,-n
+endif
+
+$(TARGET_ELF): $(LIB_TARGETS_y) $(SCATTER_PP) $(OBJECTS) | $(targetdir)
+ $(call show-action,LD,$@)
+ $(LD) $(LDFLAGS) \
+ -Wl,--start-group \
+ $(BUILTIN_LIBS) \
+ $(MODULE_LIBS_y) \
+ $(LIBS_y) \
+ $(OBJECTS) \
+ -Wl,--end-group \
+ -o $@
+ $(SIZE) $@
+
+$(SCATTER_PP): $(SCATTER_SRC) | $(targetdir)
+ $(call show-action,GEN,$@)
+ $(CC) $(CFLAGS) -E -P -C $< -o $@
+
+$(TARGET_BIN): $(TARGET_ELF)
+ $(call show-action,BIN,$@)
+ $(OBJCOPY) -O binary $< $@
diff --git a/tools/build_system/lib.mk b/tools/build_system/lib.mk
new file mode 100644
index 0000000..29fee25
--- /dev/null
+++ b/tools/build_system/lib.mk
@@ -0,0 +1,58 @@
+#
+# Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+include $(BS_DIR)/defs.mk
+
+$(info == Building $(BS_LIB_NAME) for $(BS_FIRMWARE_CPU))
+
+SOURCES := $(BS_LIB_SOURCES)
+INCLUDES += $(BS_LIB_INCLUDES)
+DEFINES += $(BS_LIB_DEFINES)
+
+ifeq ($(BUILD_FIRMWARE_DIR),)
+ # We are not building underneath a particular firmware, so we use the target
+ # CPU to identify where to build this library
+ BUILD_FIRMWARE_DIR := $(BUILD_DIR)/$(BS_FIRMWARE_CPU)
+endif
+
+# Discard the product-specific path part of product-specific modules
+LIB_BASE := $(subst product/$(PRODUCT)/,,$(shell pwd))
+
+# Extract the part of the path that will be used as the build subdirectory
+LIB_BASE := $(patsubst $(TOP_DIR)/%/src,%,$(LIB_BASE))
+
+#
+# Generate the name of the list that holds all of the disabled APIS for this
+# library
+#
+DISABLED_APIS_LIST_NAME = \
+ $(subst MODULE/,,$(call to_upper,$(LIB_BASE)))_DISABLED_APIS
+
+# Define BUILD_DISABLE_API_<API> in this library for every disabled API
+$(foreach api, \
+ $($(DISABLED_APIS_LIST_NAME)), \
+ $(eval DEFINES += BUILD_DISABLE_API_$(api)))
+
+
+ifeq ($(BS_FIRMWARE_HAS_MULTITHREADING),yes)
+ LIB_BASE := $(LIB_BASE)$(MULTHREADING_SUFFIX)
+endif
+
+ifeq ($(BS_FIRMWARE_HAS_NOTIFICATION),yes)
+ LIB_BASE := $(LIB_BASE)$(NOTIFICATION_SUFFIX)
+endif
+
+LIB_DIR = $(BUILD_FIRMWARE_DIR)/$(LIB_BASE)/$(MODE)/lib
+OBJ_DIR = $(BUILD_FIRMWARE_DIR)/$(LIB_BASE)/$(MODE)/obj
+
+LIB = $(LIB_DIR)/lib.a
+
+goal: $(LIB)
+
+INCLUDES += $(shell pwd)
+INCLUDES += $(TOP_DIR)/$(LIB_BASE)/include
+
+include $(BS_DIR)/rules.mk
diff --git a/tools/build_system/rules.mk b/tools/build_system/rules.mk
new file mode 100644
index 0000000..40de893
--- /dev/null
+++ b/tools/build_system/rules.mk
@@ -0,0 +1,211 @@
+#
+# Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+include $(BS_DIR)/defs.mk
+include $(BS_DIR)/cpu.mk
+
+ifeq ($(BUILD_HAS_MULTITHREADING),yes)
+ # Add the OS directory to the main INCLUDES list
+ INCLUDES += $(OS_DIR)/Include
+ DEFINES += BUILD_HAS_MULTITHREADING
+endif
+
+ifeq ($(BUILD_HAS_NOTIFICATION),yes)
+ DEFINES += BUILD_HAS_NOTIFICATION
+endif
+
+#
+# GCC-specific flags
+#
+export CC := $(CROSS_COMPILE)gcc
+export AS := $(CROSS_COMPILE)gcc
+export LD := $(CROSS_COMPILE)gcc
+export AR := $(CROSS_COMPILE)gcc-ar
+export OBJCOPY := $(CROSS_COMPILE)objcopy
+export SIZE := $(CROSS_COMPILE)size
+
+#
+# GCC-specific optimization levels for debug and release modes
+#
+DEFAULT_OPT_GCC_DEBUG := g
+DEFAULT_OPT_GCC_RELEASE := 3
+
+#
+# Compiler options used when building for the host
+#
+ifeq ($(BS_ARCH_ARCH),host)
+ CFLAGS_GCC += -mtune=native
+ ASFLAGS_GCC += -mtune=native
+ LDFLAGS_GCC += -mtune=native
+
+ ifeq ($(BUILD_HAS_MULTITHREADING),yes)
+ LDFLAGS_GCC += -pthread
+ endif
+
+ DEFINES += BUILD_HOST
+
+#
+# Compiler options used when cross compiling
+#
+else
+ LDFLAGS_GCC += -Wl,--script=$(SCATTER_PP)
+
+ CFLAGS_GCC += -mcpu=$(BS_ARCH_CPU)
+ ASFLAGS_GCC += -mcpu=$(BS_ARCH_CPU)
+ LDFLAGS_GCC += -mcpu=$(BS_ARCH_CPU)
+
+ # Optional ISA ("sub-arch") parameter
+ ifneq ($(BS_ARCH_ISA),)
+ CFLAGS_GCC += -m$(BS_ARCH_ISA)
+ ASFLAGS_GCC += -m$(BS_ARCH_ISA)
+ LDFLAGS_GCC += -m$(BS_ARCH_ISA)
+ endif
+endif
+
+#
+# Warning flags
+#
+
+CFLAGS_GCC += -Werror
+CFLAGS_GCC += -Wall
+CFLAGS_GCC += -Wextra
+CFLAGS_GCC += -Werror=implicit-function-declaration
+
+CFLAGS_GCC += -Wno-unused-parameter
+
+# GCC is not currently consistent in how it applies this warning, but this flag
+# should be removed should we move to a version that can build the firmware
+# without it.
+CFLAGS_GCC += -Wno-missing-field-initializers
+
+CFLAGS_GCC += -g
+CFLAGS_GCC += -std=c11
+
+CFLAGS_GCC += -fno-exceptions
+CFLAGS_GCC += -ffunction-sections
+CFLAGS_GCC += -fdata-sections
+
+DEP_CFLAGS_GCC = -MD -MP
+
+ASFLAGS_GCC += -x
+ASFLAGS_GCC += assembler-with-cpp
+ASFLAGS_GCC += -g
+
+ARFLAGS_GCC = -rc
+
+LDFLAGS_GCC += -Wl,--cref
+LDFLAGS_GCC += -Wl,--gc-sections
+
+# Force an undefined reference to the exceptions table so that it is included
+# even if no code refers to it.
+LDFLAGS_GCC += -Wl,--undefined=exceptions
+
+BUILTIN_LIBS_GCC := -lc -lgcc
+
+ifeq ($(MODE),release)
+ O ?= $(DEFAULT_OPT_GCC_RELEASE)
+
+ # Disable assertions in release mode
+ DEFINES += NDEBUG
+
+else
+ O ?= $(DEFAULT_OPT_GCC_DEBUG)
+
+ DEFINES += BUILD_MODE_DEBUG
+endif
+
+# Secure development flags
+CFLAGS_GCC += -grecord-gcc-switches
+CFLAGS_GCC += -pipe
+LDFLAGS_GCC += -Wl,-z,defs
+LDFLAGS_GCC += -Wl,-z,now
+LDFLAGS_GCC += -Wl,-z,relro
+
+#
+# Always include the framework library
+#
+INCLUDES += $(FWK_DIR)/include
+
+#
+# Toolchain-independent flags
+#
+CFLAGS += -O$(O)
+CFLAGS += $(addprefix -I,$(INCLUDES)) $(addprefix -D,$(DEFINES))
+ASFLAGS += $(addprefix -I,$(INCLUDES)) $(addprefix -D,$(DEFINES))
+
+#
+# Assign toolchain-specific flags
+#
+CFLAGS += $(CFLAGS_GCC)
+ASFLAGS += $(ASFLAGS_GCC)
+ARFLAGS = $(ARFLAGS_GCC)
+LDFLAGS += $(LDFLAGS_GCC)
+DEP_CFLAGS = $(DEP_CFLAGS_GCC)
+BUILTIN_LIBS = $(BUILTIN_LIBS_GCC)
+
+#
+# Variables for targets
+#
+OBJECTS := $(addprefix $(OBJ_DIR)/, \
+ $(patsubst %.S,%.o, \
+ $(patsubst %.s,%.o, \
+ $(patsubst %.c,%.o, \
+ $(SOURCES)))))
+
+GENERATED_DIRS := $(sort $(BUILD_DIR) \
+ $(BUILD_DOC_DIR) \
+ $(BUILD_FIRMWARE_DIR) \
+ $(dir $(LIB) \
+ $(OBJECTS) \
+ $(SCATTER_PP) \
+ $(SCATTER_SRC) \
+ $(TARGET_BIN)))
+
+#
+# Module code generation
+#
+.PHONY: gen_module
+gen_module: $(TOOLS_DIR)/gen_module_code.py $(BUILD_FIRMWARE_DIR)
+ $(TOOLS_DIR)/gen_module_code.py --path $(BUILD_FIRMWARE_DIR) $(FIRMWARE_MODULES_LIST)
+
+# Include BUILD_FIRMWARE_DIR in the compilation
+export INCLUDES += $(BUILD_FIRMWARE_DIR)
+
+#
+# Targets
+#
+
+.PHONY: $(LIB_TARGETS_y)
+$(LIB_TARGETS_y):
+ $(MAKE) -C $@
+
+$(LIB): $(OBJECTS) | $(targetdir)
+ $(call show-action,AR,$@)
+ $(AR) $(ARFLAGS) $@ $(OBJECTS)
+
+$(OBJ_DIR)/%.o: %.c $(EXTRA_DEP) | $(targetdir)
+ $(call show-action,CC,$<)
+ $(CC) -c $(CFLAGS) $(DEP_CFLAGS) $< -o $@
+
+$(OBJ_DIR)/%.o: %.s | $(targetdir)
+ $(call show-action,AS,$<)
+ $(AS) -c $(ASFLAGS) $(DEP_CFLAGS) $< -o $@
+
+$(OBJ_DIR)/%.o: %.S | $(targetdir)
+ $(call show-action,AS,$<)
+ $(AS) -c $(CFLAGS) $(DEP_CFLAGS) $< -o $@
+
+$(GENERATED_DIRS):
+ $(call show-action,MD,$@)
+ $(MD) $@
+
+.PHONY: doc
+doc: | $(BUILD_DOC_DIR)
+ $(call show-action,DOC,)
+ $(DOC) $(DOC_DIR)/config.dxy
+
+# Include dependency files
+-include $(OBJECTS:%.o=%.d)
diff --git a/tools/check_spacing.py b/tools/check_spacing.py
new file mode 100755
index 0000000..22a87e6
--- /dev/null
+++ b/tools/check_spacing.py
@@ -0,0 +1,236 @@
+#!/usr/bin/env python3
+#
+#
+# Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+"""
+ Check for trailing spaces and non-UNIX line endings in the source code.
+"""
+import argparse
+import os
+import re
+import shutil
+import subprocess
+import sys
+import tempfile
+import fnmatch
+
+#
+# Directories to exclude
+#
+EXCLUDE_DIRECTORIES = [
+ '.git',
+ 'build',
+ 'tools',
+ 'cmsis',
+]
+
+#
+# Exclude patterns (applied to files only)
+#
+EXCLUDE = [
+ '*.html',
+ '*.xml',
+ '*.css',
+ '*.gif',
+ '*.dat',
+ '*.pyc',
+ '*.jar',
+ '*.md',
+ '*.swp',
+]
+
+KEYWORDS = [
+ 'for',
+ 'if',
+ 'switch',
+ 'while',
+]
+
+#
+# File types for which spaces after keywords will be corrected
+#
+FILE_TYPES = [
+ '*.c',
+ '*.h',
+]
+
+
+def is_valid_type(filename):
+ for file_type in FILE_TYPES:
+ if fnmatch.fnmatch(filename, file_type):
+ return True
+ return False
+
+
+def main(argv=[], prog_name=''):
+ parser = argparse.ArgumentParser(prog=prog_name)
+ parser.add_argument('-t', '--trim',
+ help='Remove trailing spaces.',
+ action='store_true',
+ default=False)
+ parser.add_argument('-c', '--correct',
+ help='Correct spaces after keywords.',
+ action='store_true',
+ default=False)
+ args = parser.parse_args(argv)
+
+ print('Checking for incorrect spacing in the code...')
+ if args.trim:
+ print("Trim mode is enabled.")
+ if args.correct:
+ print("Correct mode is enabled.")
+
+ regex_patterns = dict.fromkeys(KEYWORDS, 0)
+
+ trailing_spaces_count = 0
+ trailing_lines_count = 0
+ correct_spaces_count = 0
+ modified_files = 0
+ non_unix_eol_files = 0
+
+ cwd = os.getcwd()
+ print("Executing from {}".format(cwd))
+
+ for i, directory in enumerate(EXCLUDE_DIRECTORIES):
+ EXCLUDE_DIRECTORIES[i] = os.path.abspath(directory)
+ print("\tAdding to the exclude list: {}"
+ .format(EXCLUDE_DIRECTORIES[i]))
+
+ for root, dirs, files in os.walk(cwd, topdown=True):
+ #
+ # Exclude directories based on the EXCLUDE_DIRECTORIES pattern list
+ #
+ dirs[:] = [d for d in dirs
+ if os.path.join(root, d) not in EXCLUDE_DIRECTORIES]
+
+ #
+ # Exclude files based on the EXCLUDE pattern list
+ #
+ matches = list()
+ for filename in files:
+ for file_pattern in EXCLUDE:
+ if fnmatch.fnmatch(filename, file_pattern):
+ matches.append(filename)
+ break
+ for match in matches:
+ files.remove(match)
+
+ for keyword in KEYWORDS:
+ regex_patterns[keyword] = re.compile(
+ '(.*\\W)(%s)(\\s*)(\\(.*)' % keyword)
+ #
+ # Check files
+ #
+ for filename in files:
+ path = os.path.join(root, filename)
+ content = ''
+ trailing_spaces = 0
+ trailing_lines = 0
+ incorrect_spaces = 0
+
+ with open(path, encoding="utf-8") as file:
+ for line, string in enumerate(file):
+ # Note that all newlines are converted to '\n',
+ # so the following will work regardless of
+ # what the underlying file format is using to
+ # represent a line break.
+ if string.endswith(' \n'):
+ print('{}:{} has trailing space'.format(line, path))
+ trailing_spaces += 1
+ if args.trim:
+ string = string.rstrip()+'\n'
+ if not is_valid_type(os.path.basename(path)):
+ content += string
+ continue
+ for keyword in KEYWORDS:
+ key_index = string.find(keyword)
+ if key_index == -1:
+ continue
+ m = regex_patterns[keyword].search(string)
+ if m and m.group(3) != ' ':
+ incorrect_spaces += 1
+ print('Abnormal spacing. "{}", {}:{} --> {}'
+ .format(keyword, path, line,
+ string.rstrip()))
+ if args.correct:
+ string = m.group(1) + m.group(2) + ' ' + \
+ m.group(4) + '\n'
+ content += string
+
+ if content.endswith('\n\n'):
+ print('Blank line at the end of file --> {}'.format(path))
+ c_len = len(content)
+ if args.trim:
+ content = content.rstrip()+'\n'
+ trailing_lines += c_len - len(content.rstrip() + '\n')
+ #
+ # If file.newlines has been set it is either a string with
+ # the determined line ending or a tuple with all the line
+ # endings we have encountered
+ #
+ if file.newlines:
+ if isinstance(file.newlines, tuple):
+ print('{} has mixed line endings'.format(path))
+ non_unix_eol_files += 1
+ elif file.newlines != '\n':
+ print('{} has non-UNIX line endings'.format(path))
+ non_unix_eol_files += 1
+
+ #
+ # Trim and/or correct file, depending on the provided arguments
+ #
+ write_file = False
+ if args.trim and (trailing_spaces or trailing_lines) != 0:
+ print("Trimming {}...".format(path))
+ write_file = True
+ if args.correct and incorrect_spaces != 0:
+ print("Correcting {}...".format(path))
+ write_file = True
+ if write_file:
+ modified_files += 1
+ with open(path, 'w') as file:
+ file.write(content)
+
+ trailing_spaces_count += trailing_spaces
+ trailing_lines_count += trailing_lines
+ correct_spaces_count += incorrect_spaces
+
+ if trailing_spaces_count == 0:
+ print("No trailing spaces found")
+ else:
+ print('{} trailing spaces found.'.format(trailing_spaces_count))
+
+ if trailing_lines_count == 0:
+ print("No trailing lines found")
+ else:
+ print('{} trailing lines found.'.format(trailing_lines_count))
+
+ if correct_spaces_count == 0:
+ print("No abnormal spaces found")
+ else:
+ print('Abnormal spaces found on {} lines.'
+ .format(correct_spaces_count))
+
+ if (args.trim or args.correct) and modified_files:
+ print("{} files modified".format(modified_files))
+
+ if non_unix_eol_files == 0:
+ print("No files with non-UNIX or mixed line endings found")
+ else:
+ print("{} files have non-UNIX or mixed line endings"
+ .format(non_unix_eol_files))
+
+ if (trailing_spaces_count or
+ trailing_lines_count or
+ correct_spaces_count or
+ non_unix_eol_files):
+ return 1
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:], sys.argv[0]))
diff --git a/tools/check_style.py b/tools/check_style.py
new file mode 100755
index 0000000..8f9f45c
--- /dev/null
+++ b/tools/check_style.py
@@ -0,0 +1,171 @@
+#!/usr/bin/env python3
+#
+#
+# Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+"""
+Check whether the files adhere to the prescribed coding style. Validation
+is performed by checkpatch.pl which is found on the environment path or
+via user supplied path.
+"""
+
+import argparse
+import os
+import fnmatch
+import sys
+import subprocess
+
+#
+# Checkpatch.pl location (assume it is available through the environment path
+# by default)
+#
+script_path = 'checkpatch.pl'
+
+#
+# Directories to scan. Only used when --input-mode is set to "project".
+#
+DIRECTORIES = [
+ 'arch',
+ 'framework',
+ 'module',
+ 'product',
+ 'tools',
+]
+
+#
+# Supported file types. Only used when --input-mode is set to "project".
+#
+FILE_TYPES = [
+ '*.c',
+ '*.h',
+]
+
+#
+# Default ignored types. These are rules within checkpatch that conflict with
+# the SE Software coding style and so they should never be enabled.
+#
+IGNORED_TYPES = [
+ 'LEADING_SPACE', # Incompatible with spaces for indentation
+ 'CODE_INDENT', # Incompatible with spaces for indentation
+ 'SUSPECT_CODE_INDENT', # Incompatible with spaces for indentation
+ 'POINTER_LOCATION', # Doesn't agree with our function declaration style
+ 'BLOCK_COMMENT_STYLE', # Doesn't tolerate asterisks on each block line
+ 'AVOID_EXTERNS', # We use the extern keyword
+ 'NEW_TYPEDEFS', # We add new typedefs
+ 'VOLATILE', # We use volatile
+ 'MACRO_WITH_FLOW_CONTROL', # Some 'capture' macros use do/while loops
+ 'LINE_SPACING', # We don't require a blank line after declarations
+ 'SPLIT_STRING', # We allow strings to be split across lines
+ 'FILE_PATH_CHANGES', # Specific to the kernel development process
+]
+
+error_count = 0
+
+
+def is_valid_file_type(filename):
+ return any([fnmatch.fnmatch(filename, t) for t in FILE_TYPES])
+
+
+def check_file(checkpatch_params, filename):
+ global error_count
+
+ cmd = '{} {}'.format(checkpatch_params, filename)
+
+ try:
+ subprocess.check_call(cmd, shell=True, stdin=0)
+ except subprocess.CalledProcessError:
+ error_count += 1
+
+
+def main(argv=[], prog_name=''):
+ global script_path
+ print('Arm SE Software Checkpatch Wrapper')
+ parser = argparse.ArgumentParser(prog=prog_name)
+
+ input_mode_list = ['stdin', 'project']
+
+ # Optional parameters
+ parser.add_argument('-s', '--spacing', action='store_true',
+ help='Check for correct use of spaces',
+ required=False)
+
+ parser.add_argument('-l', '--line-length', action='store_true',
+ dest='length',
+ help='Check for lines longer than 80 characters',
+ required=False)
+
+ parser.add_argument('-i', '--initializers', action='store_true',
+ help='Check for redundant variable initialization',
+ required=False)
+
+ parser.add_argument('-m', '--input-mode', choices=input_mode_list,
+ help='Input mode for the content to be checked. '
+ 'Default: %(default)s',
+ required=False, default=input_mode_list[0])
+
+ parser.add_argument('-p', '--path', action='store', dest='path',
+ help='Path to checkpatch.pl file. If not specified, '
+ 'the script will be found on the environment '
+ 'path.',
+ required=False)
+
+ args = parser.parse_args(argv)
+
+ # Override path to checkpatch.pl if necessary
+ if args.path:
+ script_path = args.path
+
+ # Print the path to checkpatch.pl as confirmation
+ print('checkpatch.pl path:', script_path, '\n')
+
+ # Enable optional tests
+ if not args.spacing:
+ IGNORED_TYPES.extend(['SPACING', 'MISSING_SPACE', 'BRACKET_SPACE'])
+
+ if not args.length:
+ IGNORED_TYPES.extend(['LONG_LINE', 'LONG_LINE_COMMENT',
+ 'LONG_LINE_STRING'])
+ if not args.initializers:
+ IGNORED_TYPES.extend(['GLOBAL_INITIALISERS', 'INITIALISED_STATIC'])
+
+ ignore_list = '--ignore ' + (','.join(map(str, IGNORED_TYPES)))
+
+ checkpatch_params = '{} --show-types --no-tree --no-summary {}'.format(
+ script_path,
+ ignore_list,
+ )
+
+ if args.input_mode == 'project':
+ print("Checking the coding style of the whole project...")
+ checkpatch_params += ' --terse --file'
+ for directory in DIRECTORIES:
+ for root, dirs, files in os.walk(directory):
+ for file in files:
+ filename = os.path.join(root, file)
+ if is_valid_file_type(file):
+ check_file(checkpatch_params, filename)
+ if error_count > 0:
+ print('{} files contained coding style errors.'.
+ format(error_count))
+
+ elif args.input_mode == 'stdin':
+ print("Checking content via standard input...")
+ check_file(checkpatch_params, '-')
+
+ else:
+ print('FAILED: Invalid input mode')
+ return 1
+
+ if error_count > 0:
+ print('FAILED: One or more files contained coding style errors.')
+ return 1
+
+ print('PASSED: No files contained coding style errors.')
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:], sys.argv[0]))
diff --git a/tools/check_tabs.py b/tools/check_tabs.py
new file mode 100755
index 0000000..cc1bb2d
--- /dev/null
+++ b/tools/check_tabs.py
@@ -0,0 +1,139 @@
+#!/usr/bin/env python3
+#
+#
+# Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+"""
+ Check for tabs in the source code.
+"""
+import argparse
+import os
+import re
+import shutil
+import subprocess
+import sys
+import tempfile
+import fnmatch
+
+#
+# Directories to exclude
+#
+EXCLUDE_DIRECTORIES = [
+ '.git',
+ 'build',
+ 'cmsis',
+]
+
+#
+# Exclude patterns (applied to files only)
+#
+EXCLUDE = [
+ 'Makefile',
+ '*.mk',
+ '*.html',
+ '*.xml',
+ '*.css',
+ '*.gif',
+ '*.dat',
+ '*.swp',
+ '*.pyc',
+ '.gitmodules',
+ '*.svg',
+]
+
+
+def convert(path):
+ print("\tConverting all tabs in %s into spaces..." % path)
+ try:
+ file, temp_file = tempfile.mkstemp(prefix='tabs_to_spaces_')
+ print("Using %s" % temp_file)
+ subprocess.check_call('expand -t4 %s > %s' % (path, temp_file),
+ shell=True)
+ shutil.copyfile(temp_file, path)
+ except Exception as e:
+ print("Error: Failed to convert file %s with %s" % (path, e))
+ sys.exit(1)
+ finally:
+ if os.path.exists(temp_file):
+ os.remove(temp_file)
+
+
+def main(argv=[], prog_name=''):
+ parser = argparse.ArgumentParser(prog=prog_name)
+ parser.add_argument('-c', '--convert',
+ help='Convert tabs to 4 spaces.',
+ action='store_true',
+ default=False)
+ args = parser.parse_args(argv)
+
+ print('Checking the presence of tabs in the code...')
+ if args.convert:
+ print("Conversion mode is enabled.")
+
+ tabs_found_count = 0
+
+ # Get the files ignored by Git
+ # (This is better than 'git check-ignore' because it includes the files
+ # excluded by .git/info/exclude)
+ git_clean_output = subprocess.check_output("git clean -ndX".split())
+ git_clean_output = git_clean_output.decode()
+ git_ignores = [l.split()[-1] for l in git_clean_output.splitlines()]
+
+ cwd = os.getcwd()
+ print("Executing from %s" % cwd)
+
+ for i, directory in enumerate(EXCLUDE_DIRECTORIES):
+ EXCLUDE_DIRECTORIES[i] = os.path.abspath(directory)
+ print("\tAdding to the exclude list: %s" % EXCLUDE_DIRECTORIES[i])
+
+ for root, dirs, files in os.walk(cwd, topdown=True):
+ #
+ # Exclude directories based on the EXCLUDE_DIRECTORIES pattern list
+ #
+ dirs[:] = [d for d in dirs
+ if os.path.join(root, d) not in EXCLUDE_DIRECTORIES]
+
+ #
+ # Exclude files based on the EXCLUDE pattern list and the files
+ # Git ignores.
+ #
+ matches = list()
+
+ files = [f for f in files
+ if os.path.join(root, f) not in git_ignores]
+
+ for filename in files:
+ for file_pattern in (EXCLUDE + git_ignores):
+ if fnmatch.fnmatch(filename, file_pattern):
+ matches.append(filename)
+ break
+ for match in matches:
+ files.remove(match)
+
+ #
+ # Check files
+ #
+ for filename in files:
+ path = os.path.join(root, filename)
+ print("processing %s" % filename)
+ with open(path, encoding="utf-8") as file:
+ for line, string in enumerate(file):
+ if '\t' in string:
+ print('%d:%s has tab' % (line, path))
+ tabs_found_count += 1
+ if args.convert:
+ convert(path)
+ break
+
+ if tabs_found_count == 0:
+ print("No tabs found")
+ return 0
+ else:
+ print('%d tab(s) found.' % tabs_found_count)
+ return 1
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:], sys.argv[0]))
diff --git a/tools/gen_module_code.py b/tools/gen_module_code.py
new file mode 100755
index 0000000..6bf50e0
--- /dev/null
+++ b/tools/gen_module_code.py
@@ -0,0 +1,124 @@
+#!/usr/bin/env python3
+#
+#
+# Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+# Description:
+# This tool takes a list of module names and generates two files:
+# * fwk_modules_id.h: Contains an enumeration giving the modules' indices.
+# * fwk_modules_list.c: Contains a table of pointers to a module descriptor.
+#
+# Note: The files are updated only if their contents will differ, relative to
+# the last time the tool was run.
+#
+
+import argparse
+import os
+import sys
+
+DEFAULT_PATH = 'build/'
+
+FILENAME_H = "fwk_module_idx.h"
+TEMPLATE_H = "/* This file was auto generated using {} */\n" \
+ "#ifndef FWK_MODULE_IDX_H\n" \
+ "#define FWK_MODULE_IDX_H\n" \
+ "\n" \
+ "#include <fwk_id.h>\n" \
+ "\n" \
+ "enum fwk_module_idx {{\n" \
+ "{}" \
+ "}};\n" \
+ "\n" \
+ "{}" \
+ "\n" \
+ "#endif /* FWK_MODULE_IDX_H */\n"
+
+FILENAME_C = "fwk_module_list.c"
+TEMPLATE_C = "/* This file was auto generated using {} */\n" \
+ "#include <stddef.h>\n" \
+ "#include <fwk_module.h>\n" \
+ "\n" \
+ "{}" \
+ "\n" \
+ "const struct fwk_module *module_table[] = {{\n" \
+ "{}" \
+ " NULL\n" \
+ "}};\n" \
+ "\n" \
+ "const struct fwk_module_config *module_config_table[] = {{\n" \
+ "{}" \
+ " NULL\n" \
+ "}};\n"
+
+
+def generate_file(path, filename, content):
+ full_filename = os.path.join(path, filename)
+ with open(full_filename, 'a+') as f:
+ f.seek(0)
+ if f.read() != content:
+ print("[GEN] {}...".format(full_filename))
+ f.seek(0)
+ f.truncate()
+ f.write(content)
+
+
+def generate_header(path, modules):
+ enum = ""
+ const = ""
+ for module in modules:
+ enum += " FWK_MODULE_IDX_{},\n".format(module.upper())
+ const += "static const fwk_id_t fwk_module_id_{} = " \
+ "FWK_ID_MODULE_INIT(FWK_MODULE_IDX_{});\n".format(module,
+ module
+ .upper())
+
+ content = TEMPLATE_H.format(sys.argv[0], enum, const)
+ generate_file(path, FILENAME_H, content)
+
+
+def generate_c(path, modules):
+ module_entry = ""
+ config_entry = ""
+ extern_entry = ""
+ for module in modules:
+ extern_entry += "extern const struct fwk_module module_{};\n"\
+ .format(module.lower())
+ extern_entry += "extern const struct fwk_module_config config_{};\n"\
+ .format(module.lower())
+ module_entry += " &module_{},\n".format(module.lower())
+ config_entry += " &config_{},\n".format(module.lower())
+
+ content = TEMPLATE_C.format(sys.argv[0], extern_entry, module_entry,
+ config_entry)
+ generate_file(path, FILENAME_C, content)
+
+
+def main():
+ parser = argparse.ArgumentParser(description="Generates a header file and \
+ source file enumerating the modules that are included in a firmware.")
+
+ parser.add_argument('modules',
+ metavar='module',
+ type=str,
+ nargs='*',
+ help='A list of module names that are included in a \
+ firmware.')
+
+ parser.add_argument('-p', '--path',
+ help='Path to the location where generated files are \
+ written. If the files exist then they will be \
+ overwritten.',
+ default=DEFAULT_PATH)
+
+ args = parser.parse_args()
+
+ modules = args.modules
+
+ generate_header(args.path, modules)
+ generate_c(args.path, modules)
+
+
+if __name__ == "__main__":
+ main()