From 8db978b5a8606a658c65b16fab7edd7a17c7c940 Mon Sep 17 00:00:00 2001 From: Jeenu Viswambharan Date: Fri, 22 Sep 2017 08:32:09 +0100 Subject: GIC: Add API to raise secure SGI API documentation updated. Change-Id: I129725059299af6cc612bafa8d74817f779d7c4f Signed-off-by: Jeenu Viswambharan --- docs/platform-interrupt-controller-API.rst | 17 +++++++++++++ drivers/arm/gic/v2/gicv2_main.c | 35 +++++++++++++++++++++++++++ drivers/arm/gic/v2/gicv2_private.h | 8 ++++++ drivers/arm/gic/v3/gicv3_main.c | 39 ++++++++++++++++++++++++++++++ include/drivers/arm/gicv2.h | 14 +++++++++++ include/drivers/arm/gicv3.h | 25 +++++++++++++++++++ include/lib/aarch32/arch.h | 1 + include/lib/aarch32/arch_helpers.h | 4 +++ include/lib/aarch64/arch.h | 1 + include/lib/aarch64/arch_helpers.h | 1 + include/plat/common/platform.h | 1 + plat/common/plat_gicv2.c | 20 +++++++++++++++ plat/common/plat_gicv3.c | 12 +++++++++ 13 files changed, 178 insertions(+) diff --git a/docs/platform-interrupt-controller-API.rst b/docs/platform-interrupt-controller-API.rst index 3d46cf34..74291bca 100644 --- a/docs/platform-interrupt-controller-API.rst +++ b/docs/platform-interrupt-controller-API.rst @@ -200,6 +200,23 @@ For GICv2: ``INTR_TYPE_S_EL1`` maps to Group 0. Otherwise, ``INTR_TYPE_EL3`` maps to Group 0 interrupt. +Function: void plat_ic_raise_el3_sgi(int sgi_num, u_register_t target); [optional] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + Argument : int + Argument : u_register_t + Return : void + +This API should raise an EL3 SGI. The first parameter, ``sgi_num``, specifies +the ID of the SGI. The second parameter, ``target``, must be the MPIDR of the +target PE. + +In case of ARM standard platforms using GIC, the implementation of the API +inserts barrier to make memory updates visible before raising SGI, then writes +to appropriate *SGI Register* in order to raise the EL3 SGI. + ---- *Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.* diff --git a/drivers/arm/gic/v2/gicv2_main.c b/drivers/arm/gic/v2/gicv2_main.c index 8048c6a7..b59e4586 100644 --- a/drivers/arm/gic/v2/gicv2_main.c +++ b/drivers/arm/gic/v2/gicv2_main.c @@ -368,3 +368,38 @@ void gicv2_set_interrupt_type(unsigned int id, unsigned int type) } spin_unlock(&gic_lock); } + +/******************************************************************************* + * This function raises the specified SGI to requested targets. + * + * The proc_num parameter must be the linear index of the target PE in the + * system. + ******************************************************************************/ +void gicv2_raise_sgi(int sgi_num, int proc_num) +{ + unsigned int sgir_val, target; + + assert(driver_data); + assert(proc_num < GICV2_MAX_TARGET_PE); + assert(driver_data->gicd_base); + + /* + * Target masks array must have been supplied, and the core position + * should be valid. + */ + assert(driver_data->target_masks); + assert(proc_num < driver_data->target_masks_num); + + /* Don't raise SGI if the mask hasn't been populated */ + target = driver_data->target_masks[proc_num]; + assert(target != 0); + + sgir_val = GICV2_SGIR_VALUE(SGIR_TGT_SPECIFIC, target, sgi_num); + + /* + * Ensure that any shared variable updates depending on out of band + * interrupt trigger are observed before raising SGI. + */ + dsbishst(); + gicd_write_sgir(driver_data->gicd_base, sgir_val); +} diff --git a/drivers/arm/gic/v2/gicv2_private.h b/drivers/arm/gic/v2/gicv2_private.h index 7703cbe5..915fc3a7 100644 --- a/drivers/arm/gic/v2/gicv2_private.h +++ b/drivers/arm/gic/v2/gicv2_private.h @@ -31,6 +31,14 @@ static inline unsigned int gicd_read_pidr2(uintptr_t base) return mmio_read_32(base + GICD_PIDR2_GICV2); } +/******************************************************************************* + * GIC Distributor interface accessors for writing entire registers + ******************************************************************************/ +static inline void gicd_write_sgir(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICD_SGIR, val); +} + /******************************************************************************* * GIC CPU interface accessors for reading entire registers ******************************************************************************/ diff --git a/drivers/arm/gic/v3/gicv3_main.c b/drivers/arm/gic/v3/gicv3_main.c index 04b47129..a5067322 100644 --- a/drivers/arm/gic/v3/gicv3_main.c +++ b/drivers/arm/gic/v3/gicv3_main.c @@ -960,3 +960,42 @@ void gicv3_set_interrupt_type(unsigned int id, unsigned int proc_num, spin_unlock(&gic_lock); } } + +/******************************************************************************* + * This function raises the specified Secure Group 0 SGI. + * + * The target parameter must be a valid MPIDR in the system. + ******************************************************************************/ +void gicv3_raise_secure_g0_sgi(int sgi_num, u_register_t target) +{ + unsigned int tgt, aff3, aff2, aff1, aff0; + uint64_t sgi_val; + + /* Verify interrupt number is in the SGI range */ + assert((sgi_num >= MIN_SGI_ID) && (sgi_num < MIN_PPI_ID)); + + /* Extract affinity fields from target */ + aff0 = MPIDR_AFFLVL0_VAL(target); + aff1 = MPIDR_AFFLVL1_VAL(target); + aff2 = MPIDR_AFFLVL2_VAL(target); + aff3 = MPIDR_AFFLVL3_VAL(target); + + /* + * Make target list from affinity 0, and ensure GICv3 SGI can target + * this PE. + */ + assert(aff0 < GICV3_MAX_SGI_TARGETS); + tgt = BIT(aff0); + + /* Raise SGI to PE specified by its affinity */ + sgi_val = GICV3_SGIR_VALUE(aff3, aff2, aff1, sgi_num, SGIR_IRM_TO_AFF, + tgt); + + /* + * Ensure that any shared variable updates depending on out of band + * interrupt trigger are observed before raising SGI. + */ + dsbishst(); + write_icc_sgi0r_el1(sgi_val); + isb(); +} diff --git a/include/drivers/arm/gicv2.h b/include/drivers/arm/gicv2.h index 229355cc..c9bcbb65 100644 --- a/include/drivers/arm/gicv2.h +++ b/include/drivers/arm/gicv2.h @@ -36,6 +36,19 @@ #define CPENDSGIR_SHIFT 2 #define SPENDSGIR_SHIFT CPENDSGIR_SHIFT +#define SGIR_TGTLSTFLT_SHIFT 24 +#define SGIR_TGTLSTFLT_MASK 0x3 +#define SGIR_TGTLST_SHIFT 16 +#define SGIR_TGTLST_MASK 0xff +#define SGIR_INTID_MASK 0xf + +#define SGIR_TGT_SPECIFIC 0 + +#define GICV2_SGIR_VALUE(tgt_lst_flt, tgt, intid) \ + ((((tgt_lst_flt) & SGIR_TGTLSTFLT_MASK) << SGIR_TGTLSTFLT_SHIFT) | \ + (((tgt) & SGIR_TGTLST_MASK) << SGIR_TGTLST_SHIFT) | \ + ((intid) & SGIR_INTID_MASK)) + /******************************************************************************* * GICv2 specific CPU interface register offsets and constants. ******************************************************************************/ @@ -157,6 +170,7 @@ void gicv2_enable_interrupt(unsigned int id); void gicv2_disable_interrupt(unsigned int id); void gicv2_set_interrupt_priority(unsigned int id, unsigned int priority); void gicv2_set_interrupt_type(unsigned int id, unsigned int type); +void gicv2_raise_sgi(int sgi_num, int proc_num); #endif /* __ASSEMBLY__ */ #endif /* __GICV2_H__ */ diff --git a/include/drivers/arm/gicv3.h b/include/drivers/arm/gicv3.h index 68430fb0..bf294f13 100644 --- a/include/drivers/arm/gicv3.h +++ b/include/drivers/arm/gicv3.h @@ -24,6 +24,9 @@ /* Constant to categorize LPI interrupt */ #define MIN_LPI_ID 8192 +/* GICv3 can only target up to 16 PEs with SGI */ +#define GICV3_MAX_SGI_TARGETS 16 + /******************************************************************************* * GICv3 specific Distributor interface register offsets and constants. ******************************************************************************/ @@ -165,6 +168,27 @@ #define IAR1_EL1_INTID_SHIFT 0 #define IAR1_EL1_INTID_MASK 0xffffff +/* ICC SGI macros */ +#define SGIR_TGT_MASK 0xffff +#define SGIR_AFF1_SHIFT 16 +#define SGIR_INTID_SHIFT 24 +#define SGIR_INTID_MASK 0xf +#define SGIR_AFF2_SHIFT 32 +#define SGIR_IRM_SHIFT 40 +#define SGIR_IRM_MASK 0x1 +#define SGIR_AFF3_SHIFT 48 +#define SGIR_AFF_MASK 0xf + +#define SGIR_IRM_TO_AFF 0 + +#define GICV3_SGIR_VALUE(aff3, aff2, aff1, intid, irm, tgt) \ + ((((uint64_t) (aff3) & SGIR_AFF_MASK) << SGIR_AFF3_SHIFT) | \ + (((uint64_t) (irm) & SGIR_IRM_MASK) << SGIR_IRM_SHIFT) | \ + (((uint64_t) (aff2) & SGIR_AFF_MASK) << SGIR_AFF2_SHIFT) | \ + (((intid) & SGIR_INTID_MASK) << SGIR_INTID_SHIFT) | \ + (((aff1) & SGIR_AFF_MASK) << SGIR_AFF1_SHIFT) | \ + ((tgt) & SGIR_TGT_MASK)) + /***************************************************************************** * GICv3 ITS registers and constants *****************************************************************************/ @@ -357,6 +381,7 @@ void gicv3_set_interrupt_priority(unsigned int id, unsigned int proc_num, unsigned int priority); void gicv3_set_interrupt_type(unsigned int id, unsigned int proc_num, unsigned int group); +void gicv3_raise_secure_g0_sgi(int sgi_num, u_register_t target); #endif /* __ASSEMBLY__ */ #endif /* __GICV3_H__ */ diff --git a/include/lib/aarch32/arch.h b/include/lib/aarch32/arch.h index 5fbb83a6..e2c5f24f 100644 --- a/include/lib/aarch32/arch.h +++ b/include/lib/aarch32/arch.h @@ -44,6 +44,7 @@ (((mpidr) >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK) #define MPIDR_AFFLVL2_VAL(mpidr) \ (((mpidr) >> MPIDR_AFF2_SHIFT) & MPIDR_AFFLVL_MASK) +#define MPIDR_AFFLVL3_VAL(mpidr) 0 /* * The MPIDR_MAX_AFFLVL count starts from 0. Take care to diff --git a/include/lib/aarch32/arch_helpers.h b/include/lib/aarch32/arch_helpers.h index 1f4f2162..c8caea28 100644 --- a/include/lib/aarch32/arch_helpers.h +++ b/include/lib/aarch32/arch_helpers.h @@ -266,6 +266,7 @@ DEFINE_COPROCR_RW_FUNCS(icc_iar0_el1, ICC_IAR0) DEFINE_COPROCR_RW_FUNCS(icc_iar1_el1, ICC_IAR1) DEFINE_COPROCR_RW_FUNCS(icc_eoir0_el1, ICC_EOIR0) DEFINE_COPROCR_RW_FUNCS(icc_eoir1_el1, ICC_EOIR1) +DEFINE_COPROCR_RW_FUNCS_64(icc_sgi0r_el1, ICC_SGI0R_EL1_64) DEFINE_COPROCR_RW_FUNCS(hdcr, HDCR) DEFINE_COPROCR_RW_FUNCS(cnthp_ctl, CNTHP_CTL) @@ -325,4 +326,7 @@ DEFINE_DCOP_PARAM_FUNC(cvac, DCCMVAC) #define read_ctr_el0() read_ctr() +#define write_icc_sgi0r_el1(_v) \ + write64_icc_sgi0r_el1(_v) + #endif /* __ARCH_HELPERS_H__ */ diff --git a/include/lib/aarch64/arch.h b/include/lib/aarch64/arch.h index 3fb6846c..bbe1e4f9 100644 --- a/include/lib/aarch64/arch.h +++ b/include/lib/aarch64/arch.h @@ -77,6 +77,7 @@ #define ICC_IAR1_EL1 S3_0_c12_c12_0 #define ICC_EOIR0_EL1 S3_0_c12_c8_1 #define ICC_EOIR1_EL1 S3_0_c12_c12_1 +#define ICC_SGI0R_EL1 S3_0_c12_c11_7 /******************************************************************************* * Generic timer memory mapped registers & offsets diff --git a/include/lib/aarch64/arch_helpers.h b/include/lib/aarch64/arch_helpers.h index dbbbaeb0..b880497c 100644 --- a/include/lib/aarch64/arch_helpers.h +++ b/include/lib/aarch64/arch_helpers.h @@ -322,6 +322,7 @@ DEFINE_RENAME_SYSREG_READ_FUNC(icc_iar0_el1, ICC_IAR0_EL1) DEFINE_RENAME_SYSREG_READ_FUNC(icc_iar1_el1, ICC_IAR1_EL1) DEFINE_RENAME_SYSREG_WRITE_FUNC(icc_eoir0_el1, ICC_EOIR0_EL1) DEFINE_RENAME_SYSREG_WRITE_FUNC(icc_eoir1_el1, ICC_EOIR1_EL1) +DEFINE_RENAME_SYSREG_WRITE_FUNC(icc_sgi0r_el1, ICC_SGI0R_EL1) #define IS_IN_EL(x) \ diff --git a/include/plat/common/platform.h b/include/plat/common/platform.h index c97b7d38..b4e33d9b 100644 --- a/include/plat/common/platform.h +++ b/include/plat/common/platform.h @@ -82,6 +82,7 @@ void plat_ic_enable_interrupt(unsigned int id); int plat_ic_has_interrupt_type(unsigned int type); void plat_ic_set_interrupt_type(unsigned int id, unsigned int type); void plat_ic_set_interrupt_priority(unsigned int id, unsigned int priority); +void plat_ic_raise_el3_sgi(int sgi_num, u_register_t target); /******************************************************************************* * Optional common functions (may be overridden) diff --git a/plat/common/plat_gicv2.c b/plat/common/plat_gicv2.c index c785d831..5df9c79b 100644 --- a/plat/common/plat_gicv2.c +++ b/plat/common/plat_gicv2.c @@ -7,6 +7,7 @@ #include #include #include +#include /* * The following platform GIC functions are weakly defined. They @@ -29,6 +30,7 @@ #pragma weak plat_ic_disable_interrupt #pragma weak plat_ic_set_interrupt_priority #pragma weak plat_ic_set_interrupt_type +#pragma weak plat_ic_raise_el3_sgi /* * This function returns the highest priority pending interrupt at @@ -220,3 +222,21 @@ void plat_ic_set_interrupt_type(unsigned int id, unsigned int type) gicv2_set_interrupt_type(id, gicv2_type); } + +void plat_ic_raise_el3_sgi(int sgi_num, u_register_t target) +{ +#if GICV2_G0_FOR_EL3 + int id; + + /* Target must be a valid MPIDR in the system */ + id = plat_core_pos_by_mpidr(target); + assert(id >= 0); + + /* Verify that this is a secure SGI */ + assert(plat_ic_get_interrupt_type(sgi_num) == INTR_TYPE_EL3); + + gicv2_raise_sgi(sgi_num, id); +#else + assert(0); +#endif +} diff --git a/plat/common/plat_gicv3.c b/plat/common/plat_gicv3.c index f4279ec4..819ca451 100644 --- a/plat/common/plat_gicv3.c +++ b/plat/common/plat_gicv3.c @@ -35,6 +35,7 @@ #pragma weak plat_ic_disable_interrupt #pragma weak plat_ic_set_interrupt_priority #pragma weak plat_ic_set_interrupt_type +#pragma weak plat_ic_raise_el3_sgi CASSERT((INTR_TYPE_S_EL1 == INTR_GROUP1S) && (INTR_TYPE_NS == INTR_GROUP1NS) && @@ -217,6 +218,17 @@ void plat_ic_set_interrupt_type(unsigned int id, unsigned int type) { gicv3_set_interrupt_type(id, plat_my_core_pos(), type); } + +void plat_ic_raise_el3_sgi(int sgi_num, u_register_t target) +{ + /* Target must be a valid MPIDR in the system */ + assert(plat_core_pos_by_mpidr(target) >= 0); + + /* Verify that this is a secure EL3 SGI */ + assert(plat_ic_get_interrupt_type(sgi_num) == INTR_TYPE_EL3); + + gicv3_raise_secure_g0_sgi(sgi_num, target); +} #endif #ifdef IMAGE_BL32 -- cgit v1.2.3