diff --git a/bl31/bl31.mk b/bl31/bl31.mk index c0dc2fd..54e8cc4 100644 --- a/bl31/bl31.mk +++ b/bl31/bl31.mk @@ -31,6 +31,7 @@ BL31_SOURCES += bl31/bl31_main.c \ bl31/context_mgmt.c \ bl31/runtime_svc.c \ + bl31/interrupt_mgmt.c \ bl31/aarch64/bl31_arch_setup.c \ bl31/aarch64/bl31_entrypoint.S \ bl31/aarch64/context.S \ diff --git a/bl31/context_mgmt.c b/bl31/context_mgmt.c index bdb8513..78bfa89 100644 --- a/bl31/context_mgmt.c +++ b/bl31/context_mgmt.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -160,6 +161,11 @@ ctx = cm_get_context(read_mpidr(), security_state); assert(ctx); + /* Program the interrupt routing model for this security state */ + scr &= ~SCR_FIQ_BIT; + scr &= ~SCR_IRQ_BIT; + scr |= get_scr_el3_from_routing_model(security_state); + /* Populate EL3 state so that we've the right context before doing ERET */ state = get_el3state_ctx(ctx); write_ctx_reg(state, CTX_SPSR_EL3, spsr); diff --git a/bl31/interrupt_mgmt.c b/bl31/interrupt_mgmt.c new file mode 100644 index 0000000..2b0c797 --- /dev/null +++ b/bl31/interrupt_mgmt.c @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2014, ARM Limited and Contributors. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +/******************************************************************************* + * Local structure and corresponding array to keep track of the state of the + * registered interrupt handlers for each interrupt type. + * The field descriptions are: + * + * 'flags' : Bit[0], Routing model for this interrupt type when execution is + * not in EL3 in the secure state. '1' implies that this + * interrupt will be routed to EL3. '0' implies that this + * interrupt will be routed to the current exception level. + * + * Bit[1], Routing model for this interrupt type when execution is + * not in EL3 in the non-secure state. '1' implies that this + * interrupt will be routed to EL3. '0' implies that this + * interrupt will be routed to the current exception level. + * + * All other bits are reserved and SBZ. + * + * 'scr_el3[2]' : Mapping of the routing model in the 'flags' field to the + * value of the SCR_EL3.IRQ or FIQ bit for each security state. + * There are two instances of this field corresponding to the + * two security states. + ******************************************************************************/ +typedef struct intr_type_desc { + interrupt_type_handler_t handler; + uint32_t flags; + uint32_t scr_el3[2]; +} intr_type_desc_t; + +static intr_type_desc_t intr_type_descs[MAX_INTR_TYPES]; + +/******************************************************************************* + * This function validates the interrupt type. EL3 interrupts are currently not + * supported. + ******************************************************************************/ +static int32_t validate_interrupt_type(uint32_t type) +{ + if (type == INTR_TYPE_EL3) + return -ENOTSUP; + + if (type != INTR_TYPE_S_EL1 && type != INTR_TYPE_NS) + return -EINVAL; + + return 0; +} + +/******************************************************************************* +* This function validates the routing model for this type of interrupt + ******************************************************************************/ +static int32_t validate_routing_model(uint32_t type, uint32_t flags) +{ + flags >>= INTR_RM_FLAGS_SHIFT; + flags &= INTR_RM_FLAGS_MASK; + + if (type == INTR_TYPE_S_EL1) + return validate_sel1_interrupt_rm(flags); + + if (type == INTR_TYPE_NS) + return validate_ns_interrupt_rm(flags); + + return -EINVAL; +} + +/******************************************************************************* + * This function returns the cached copy of the SCR_EL3 which contains the + * routing model (expressed through the IRQ and FIQ bits) for a security state + * which was stored through a call to 'set_routing_model()' earlier. + ******************************************************************************/ +uint32_t get_scr_el3_from_routing_model(uint32_t security_state) +{ + uint32_t scr_el3; + + assert(security_state <= NON_SECURE); + scr_el3 = intr_type_descs[INTR_TYPE_NS].scr_el3[security_state]; + scr_el3 |= intr_type_descs[INTR_TYPE_S_EL1].scr_el3[security_state]; + scr_el3 |= intr_type_descs[INTR_TYPE_EL3].scr_el3[security_state]; + return scr_el3; +} + +/******************************************************************************* + * This function uses the 'interrupt_type_flags' parameter to obtain the value + * of the trap bit (IRQ/FIQ) in the SCR_EL3 for a security state for this + * interrupt type. It uses it to update the SCR_EL3 in the cpu context and the + * 'intr_type_desc' for that security state. + ******************************************************************************/ +static void set_scr_el3_from_rm(uint32_t type, + uint32_t interrupt_type_flags, + uint32_t security_state) +{ + uint32_t flag, bit_pos; + + flag = get_interrupt_rm_flag(interrupt_type_flags, security_state); + bit_pos = plat_interrupt_type_to_line(type, security_state); + intr_type_descs[type].scr_el3[security_state] = flag << bit_pos; + cm_write_scr_el3_bit(security_state, bit_pos, flag); +} + +/******************************************************************************* + * This function validates the routing model specified in the 'flags' and + * updates internal data structures to reflect the new routing model. It also + * updates the copy of SCR_EL3 for each security state with the new routing + * model in the 'cpu_context' structure for this cpu. + ******************************************************************************/ +int32_t set_routing_model(uint32_t type, uint32_t flags) +{ + int32_t rc; + + rc = validate_interrupt_type(type); + if (rc) + return rc; + + rc = validate_routing_model(type, flags); + if (rc) + return rc; + + /* Update the routing model in internal data structures */ + intr_type_descs[type].flags = flags; + set_scr_el3_from_rm(type, flags, SECURE); + set_scr_el3_from_rm(type, flags, NON_SECURE); + + return 0; +} + +/******************************************************************************* + * This function registers a handler for the 'type' of interrupt specified. It + * also validates the routing model specified in the 'flags' for this type of + * interrupt. + ******************************************************************************/ +int32_t register_interrupt_type_handler(uint32_t type, + interrupt_type_handler_t handler, + uint32_t flags) +{ + int32_t rc; + + /* Validate the 'handler' parameter */ + if (!handler) + return -EINVAL; + + /* Validate the 'flags' parameter */ + if (flags & INTR_TYPE_FLAGS_MASK) + return -EINVAL; + + /* Check if a handler has already been registered */ + if (intr_type_descs[type].handler) + return -EALREADY; + + rc = set_routing_model(type, flags); + if (rc) + return rc; + + /* Save the handler */ + intr_type_descs[type].handler = handler; + + return 0; +} + +/******************************************************************************* + * This function is called when an interrupt is generated and returns the + * handler for the interrupt type (if registered). It returns NULL if the + * interrupt type is not supported or its handler has not been registered. + ******************************************************************************/ +interrupt_type_handler_t get_interrupt_type_handler(uint32_t type) +{ + if (validate_interrupt_type(type)) + return NULL; + + return intr_type_descs[type].handler; +} + diff --git a/docs/user-guide.md b/docs/user-guide.md index e7f0df5..201db38 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -157,6 +157,10 @@ * `V`: Verbose build. If assigned anything other than 0, the build commands are printed. Default is 0 +* `FVP_GIC_ARCH`: Choice of ARM GIC architecture version used by the FVP port + for implementing the platform GIC API. This API is used by the interrupt + management framework. Default is 2 i.e. version 2.0 + ### Creating a Firmware Image Package FIPs are automatically created as part of the build instructions described in diff --git a/drivers/arm/gic/gic_v2.c b/drivers/arm/gic/gic_v2.c index 00464cb..27a39b9 100644 --- a/drivers/arm/gic/gic_v2.c +++ b/drivers/arm/gic/gic_v2.c @@ -28,8 +28,10 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include #include #include +#include #include /******************************************************************************* @@ -290,3 +292,27 @@ (1 << iface) << (byte_off << 3)); } +/******************************************************************************* + * This function allows the interrupt management framework to determine (through + * the platform) which interrupt line (IRQ/FIQ) to use for an interrupt type to + * route it to EL3. The interrupt line is represented as the bit position of the + * IRQ or FIQ bit in the SCR_EL3. + ******************************************************************************/ +uint32_t gicv2_interrupt_type_to_line(uint32_t cpuif_base, uint32_t type) +{ + uint32_t gicc_ctlr; + + /* Non-secure interrupts are signalled on the IRQ line always */ + if (type == INTR_TYPE_NS) + return __builtin_ctz(SCR_IRQ_BIT); + + /* + * Secure interrupts are signalled using the IRQ line if the FIQ_EN + * bit is not set else they are signalled using the FIQ line. + */ + gicc_ctlr = gicc_read_ctlr(cpuif_base); + if (gicc_ctlr & FIQ_EN) + return __builtin_ctz(SCR_FIQ_BIT); + else + return __builtin_ctz(SCR_IRQ_BIT); +} diff --git a/include/bl31/interrupt_mgmt.h b/include/bl31/interrupt_mgmt.h new file mode 100644 index 0000000..0b24f39 --- /dev/null +++ b/include/bl31/interrupt_mgmt.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2014, ARM Limited and Contributors. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __INTERRUPT_MGMT_H__ +#define __INTERRUPT_MGMT_H__ + +#include + +/******************************************************************************* + * Constants for the types of interrupts recognised by the IM framework + ******************************************************************************/ +#define INTR_TYPE_S_EL1 0 +#define INTR_TYPE_EL3 1 +#define INTR_TYPE_NS 2 +#define MAX_INTR_TYPES 3 +#define INTR_TYPE_INVAL MAX_INTR_TYPES +/* + * Constant passed to the interrupt handler in the 'id' field when the + * framework does not read the gic registers to determine the interrupt id. + */ +#define INTR_ID_UNAVAILABLE 0xFFFFFFFF + + +/******************************************************************************* + * Mask for _both_ the routing model bits in the 'flags' parameter and + * constants to define the valid routing models for each supported interrupt + * type + ******************************************************************************/ +#define INTR_RM_FLAGS_SHIFT 0x0 +#define INTR_RM_FLAGS_MASK 0x3 +/* Routed to EL3 from NS. Taken to S-EL1 from Secure */ +#define INTR_SEL1_VALID_RM0 0x2 +/* Routed to EL3 from NS and Secure */ +#define INTR_SEL1_VALID_RM1 0x3 +/* Routed to EL1/EL2 from NS and to S-EL1 from Secure */ +#define INTR_NS_VALID_RM0 0x0 +/* Routed to EL1/EL2 from NS and to EL3 from Secure */ +#define INTR_NS_VALID_RM1 0x1 + + +/******************************************************************************* + * Constants for the _individual_ routing model bits in the 'flags' field for + * each interrupt type and mask to validate the 'flags' parameter while + * registering an interrupt handler + ******************************************************************************/ +#define INTR_TYPE_FLAGS_MASK 0xFFFFFFFC + +#define INTR_RM_FROM_SEC_SHIFT SECURE /* BIT[0] */ +#define INTR_RM_FROM_NS_SHIFT NON_SECURE /* BIT[1] */ +#define INTR_RM_FROM_FLAG_MASK 1 +#define get_interrupt_rm_flag(flag, ss) (((flag >> INTR_RM_FLAGS_SHIFT) >> ss) \ + & INTR_RM_FROM_FLAG_MASK) +#define set_interrupt_rm_flag(flag, ss) (flag |= 1 << ss) +#define clr_interrupt_rm_flag(flag, ss) (flag &= ~(1 << ss)) + + +/******************************************************************************* + * Macros to validate the routing model bits in the 'flags' for a type + * of interrupt. If the model does not match one of the valid masks + * -EINVAL is returned. + ******************************************************************************/ +#define validate_sel1_interrupt_rm(x) (x == INTR_SEL1_VALID_RM0 ? 0 : \ + (x == INTR_SEL1_VALID_RM1 ? 0 :\ + -EINVAL)) + +#define validate_ns_interrupt_rm(x) (x == INTR_NS_VALID_RM0 ? 0 : \ + (x == INTR_NS_VALID_RM1 ? 0 :\ + -EINVAL)) + +/******************************************************************************* + * Macros to set the 'flags' parameter passed to an interrupt type handler. Only + * the flag to indicate the security state when the exception was generated is + * supported. + ******************************************************************************/ +#define INTR_SRC_SS_FLAG_SHIFT 0 /* BIT[0] */ +#define INTR_SRC_SS_FLAG_MASK 1 +#define set_interrupt_src_ss(flag, val) (flag |= val << INTR_SRC_SS_FLAG_SHIFT) +#define clr_interrupt_src_ss(flag) (flag &= ~(1 << INTR_SRC_SS_FLAG_SHIFT)) +#define get_interrupt_src_ss(flag) ((flag >> INTR_SRC_SS_FLAG_SHIFT) & \ + INTR_SRC_SS_FLAG_MASK) + +#ifndef __ASSEMBLY__ + +/* Prototype for defining a handler for an interrupt type */ +typedef uint64_t (*interrupt_type_handler_t)(uint32_t id, + uint32_t flags, + void *handle, + void *cookie); + +/******************************************************************************* + * Function & variable prototypes + ******************************************************************************/ +extern uint32_t get_scr_el3_from_routing_model(uint32_t security_state); +extern int32_t set_routing_model(uint32_t type, uint32_t flags); +extern int32_t register_interrupt_type_handler(uint32_t type, + interrupt_type_handler_t handler, + uint32_t flags); +extern interrupt_type_handler_t get_interrupt_type_handler(uint32_t interrupt_type); + +#endif /*__ASSEMBLY__*/ +#endif /* __INTERRUPT_MGMT_H__ */ diff --git a/include/drivers/arm/gic_v2.h b/include/drivers/arm/gic_v2.h index ccf3d32..91c3f11 100644 --- a/include/drivers/arm/gic_v2.h +++ b/include/drivers/arm/gic_v2.h @@ -298,6 +298,12 @@ mmio_write_32(base + GICC_DIR, val); } +/******************************************************************************* + * Prototype of function to map an interrupt type to the interrupt line used to + * signal it. + ******************************************************************************/ +uint32_t gicv2_interrupt_type_to_line(uint32_t cpuif_base, uint32_t type); + #endif /*__ASSEMBLY__*/ #endif /* __GIC_V2_H__ */ diff --git a/plat/fvp/plat_gic.c b/plat/fvp/plat_gic.c index db3c9cf..dd409f5 100644 --- a/plat/fvp/plat_gic.c +++ b/plat/fvp/plat_gic.c @@ -29,9 +29,12 @@ */ #include +#include +#include #include #include #include +#include #include #include @@ -284,3 +287,38 @@ gic_cpuif_setup(gicc_base); gic_distif_setup(gicd_base); } + +/******************************************************************************* + * An ARM processor signals interrupt exceptions through the IRQ and FIQ pins. + * The interrupt controller knows which pin/line it uses to signal a type of + * interrupt. The platform knows which interrupt controller type is being used + * in a particular security state e.g. with an ARM GIC, normal world could use + * the GICv2 features while the secure world could use GICv3 features and vice + * versa. + * This function is exported by the platform to let the interrupt management + * framework determine for a type of interrupt and security state, which line + * should be used in the SCR_EL3 to control its routing to EL3. The interrupt + * line is represented as the bit position of the IRQ or FIQ bit in the SCR_EL3. + ******************************************************************************/ +uint32_t plat_interrupt_type_to_line(uint32_t type, uint32_t security_state) +{ + uint32_t gicc_base = platform_get_cfgvar(CONFIG_GICC_ADDR); + + assert(type == INTR_TYPE_S_EL1 || + type == INTR_TYPE_EL3 || + type == INTR_TYPE_NS); + + assert(security_state == NON_SECURE || security_state == SECURE); + + /* + * We ignore the security state parameter under the assumption that + * both normal and secure worlds are using ARM GICv2. This parameter + * will be used when the secure world starts using GICv3. + */ +#if FVP_GIC_ARCH == 2 + return gicv2_interrupt_type_to_line(gicc_base, type); +#else +#error "Invalid GIC architecture version specified for FVP port" +#endif +} + diff --git a/plat/fvp/platform.h b/plat/fvp/platform.h index 40f780e..e1580de 100644 --- a/plat/fvp/platform.h +++ b/plat/fvp/platform.h @@ -397,6 +397,8 @@ extern void gic_cpuif_setup(unsigned int); extern void gic_pcpu_distif_setup(unsigned int); extern void gic_setup(void); +extern uint32_t plat_interrupt_type_to_line(uint32_t type, + uint32_t security_state); /* Declarations for fvp_topology.c */ extern int plat_setup_topology(void); diff --git a/plat/fvp/platform.mk b/plat/fvp/platform.mk index 511a25c..a8054bf 100644 --- a/plat/fvp/platform.mk +++ b/plat/fvp/platform.mk @@ -67,3 +67,9 @@ plat/fvp/aarch64/plat_helpers.S \ plat/fvp/aarch64/plat_common.c \ plat/fvp/drivers/pwrc/fvp_pwrc.c + +# Flag used by the FVP port to determine the version of ARM GIC architecture +# to use for interrupt management in EL3. +FVP_GIC_ARCH := 2 +$(eval $(call add_define,FVP_GIC_ARCH)) +