diff options
author | tushar khandelwal <tushar.khandelwal@arm.com> | 2019-05-16 17:55:32 +0100 |
---|---|---|
committer | Tushar Khandelwal <tushar.khandelwal@arm.com> | 2019-09-04 11:37:10 +0100 |
commit | 23f01c5de10e9c6a51361fbc6949f6383e434900 (patch) | |
tree | fc22dcbd7611fa0d777ad5147749472bc1668e33 | |
parent | e1f79b99491dac3452b842f09d97c808da1b5ff2 (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>
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() |