From 0eb8774bd83003ed197d31138003760636757dd8 Mon Sep 17 00:00:00 2001 From: David Wang Date: Wed, 5 Apr 2017 18:30:07 +0800 Subject: [PATCH] Support ARM GICv3 mode In ARM GICv3 mode, the interrupts are used as below for optee_os. * FIQ - Foreign interrupts not handled by optee_os. This includes the non-secure interrupts that should be handled by the REE and the secure interrupts assiged to the monitor (aarch32 Monitor mode or aarch64 EL3). * IRQ - Native interrupts for optee_os. And optee_os should use the system register interface to access the GICC registers in GICv3 mode. A new build flag `CFG_ARM_GICV3=y` should be set to support GICv3 mode. Change-Id: Ic3311c5e1d906399b7b1fb7936ce65d6abb7ff0a Signed-off-by: David Wang --- core/arch/arm/include/kernel/thread.h | 5 ++ core/arch/arm/kernel/thread_a32.S | 8 ++++ core/arch/arm/kernel/thread_a64.S | 8 ++++ core/drivers/gic.c | 49 +++++++++++++++++-- core/include/drivers/gic.h | 68 ++++++++++++++++++++++++++- documentation/interrupt_handling.md | 10 ++-- 6 files changed, 138 insertions(+), 10 deletions(-) diff --git a/core/arch/arm/include/kernel/thread.h b/core/arch/arm/include/kernel/thread.h index 831b5d6876b..5e340d3d652 100644 --- a/core/arch/arm/include/kernel/thread.h +++ b/core/arch/arm/include/kernel/thread.h @@ -306,8 +306,13 @@ void thread_restore_foreign_intr(void); * thread_*_exceptions() functions below. * These definitions are compatible with both ARM32 and ARM64. */ +#if defined(CFG_ARM_GICV3) +#define THREAD_EXCP_FOREIGN_INTR (ARM32_CPSR_F >> ARM32_CPSR_F_SHIFT) +#define THREAD_EXCP_NATIVE_INTR (ARM32_CPSR_I >> ARM32_CPSR_F_SHIFT) +#else #define THREAD_EXCP_FOREIGN_INTR (ARM32_CPSR_I >> ARM32_CPSR_F_SHIFT) #define THREAD_EXCP_NATIVE_INTR (ARM32_CPSR_F >> ARM32_CPSR_F_SHIFT) +#endif #define THREAD_EXCP_ALL (THREAD_EXCP_FOREIGN_INTR \ | THREAD_EXCP_NATIVE_INTR \ | (ARM32_CPSR_A >> ARM32_CPSR_F_SHIFT)) diff --git a/core/arch/arm/kernel/thread_a32.S b/core/arch/arm/kernel/thread_a32.S index 1d2b0e70992..d13b9bbec7b 100644 --- a/core/arch/arm/kernel/thread_a32.S +++ b/core/arch/arm/kernel/thread_a32.S @@ -507,14 +507,22 @@ END_FUNC thread_rpc LOCAL_FUNC thread_fiq_handler , : UNWIND( .fnstart) UNWIND( .cantunwind) +#if defined(CFG_ARM_GICV3) + foreign_intr_handler fiq +#else native_intr_handler fiq +#endif UNWIND( .fnend) END_FUNC thread_fiq_handler LOCAL_FUNC thread_irq_handler , : UNWIND( .fnstart) UNWIND( .cantunwind) +#if defined(CFG_ARM_GICV3) + native_intr_handler irq +#else foreign_intr_handler irq +#endif UNWIND( .fnend) END_FUNC thread_irq_handler diff --git a/core/arch/arm/kernel/thread_a64.S b/core/arch/arm/kernel/thread_a64.S index b77ef7c1881..bd27e4e5b0d 100644 --- a/core/arch/arm/kernel/thread_a64.S +++ b/core/arch/arm/kernel/thread_a64.S @@ -826,9 +826,17 @@ END_FUNC el0_sync_abort .endm LOCAL_FUNC elx_irq , : +#if defined(CFG_ARM_GICV3) + native_intr_handler irq +#else foreign_intr_handler irq +#endif END_FUNC elx_irq LOCAL_FUNC elx_fiq , : +#if defined(CFG_ARM_GICV3) + foreign_intr_handler fiq +#else native_intr_handler fiq +#endif END_FUNC elx_fiq diff --git a/core/drivers/gic.c b/core/drivers/gic.c index 93979a0a033..2b6d4a10667 100644 --- a/core/drivers/gic.c +++ b/core/drivers/gic.c @@ -105,7 +105,7 @@ static const struct itr_ops gic_ops = { .set_affinity = gic_op_set_affinity, }; -static size_t probe_max_it(vaddr_t gicc_base, vaddr_t gicd_base) +static size_t probe_max_it(vaddr_t gicc_base __maybe_unused, vaddr_t gicd_base) { int i; uint32_t old_ctlr; @@ -116,8 +116,13 @@ static size_t probe_max_it(vaddr_t gicc_base, vaddr_t gicd_base) /* * Probe which interrupt number is the largest. */ +#if defined(CFG_ARM_GICV3) + old_ctlr = read_icc_ctlr_el1(); + write_icc_ctlr_el1(0); +#else old_ctlr = read32(gicc_base + GICC_CTLR); write32(0, gicc_base + GICC_CTLR); +#endif for (i = max_regs; i >= 0; i--) { uint32_t old_reg; uint32_t reg; @@ -135,13 +140,21 @@ static size_t probe_max_it(vaddr_t gicc_base, vaddr_t gicd_base) } } out: +#if defined(CFG_ARM_GICV3) + write_icc_ctlr_el1(old_ctlr); +#else write32(old_ctlr, gicc_base + GICC_CTLR); +#endif return ret; } void gic_cpu_init(struct gic_data *gd) { +#if defined(CFG_ARM_GICV3) + assert(gd->gicd_base); +#else assert(gd->gicd_base && gd->gicc_base); +#endif /* per-CPU interrupts config: * ID0-ID7(SGI) for Non-secure interrupts @@ -153,14 +166,21 @@ void gic_cpu_init(struct gic_data *gd) /* Set the priority mask to permit Non-secure interrupts, and to * allow the Non-secure world to adjust the priority mask itself */ +#if defined(CFG_ARM_GICV3) + write_icc_pmr_el1(0x80); + write_icc_ctlr_el1(GICC_CTLR_ENABLEGRP0 | GICC_CTLR_ENABLEGRP1 + | GICC_CTLR_FIQEN); +#else write32(0x80, gd->gicc_base + GICC_PMR); /* Enable GIC */ write32(GICC_CTLR_ENABLEGRP0 | GICC_CTLR_ENABLEGRP1 | GICC_CTLR_FIQEN, gd->gicc_base + GICC_CTLR); +#endif } -void gic_init(struct gic_data *gd, vaddr_t gicc_base, vaddr_t gicd_base) +void gic_init(struct gic_data *gd, vaddr_t gicc_base __maybe_unused, + vaddr_t gicd_base) { size_t n; @@ -189,6 +209,12 @@ void gic_init(struct gic_data *gd, vaddr_t gicc_base, vaddr_t gicd_base) /* Set the priority mask to permit Non-secure interrupts, and to * allow the Non-secure world to adjust the priority mask itself */ +#if defined(CFG_ARM_GICV3) + write_icc_pmr_el1(0x80); + write_icc_ctlr_el1(GICC_CTLR_ENABLEGRP0 | GICC_CTLR_ENABLEGRP1 + | GICC_CTLR_FIQEN); + write_icc_ctlr_el1(GICD_CTLR_ENABLEGRP0 | GICD_CTLR_ENABLEGRP1); +#else write32(0x80, gd->gicc_base + GICC_PMR); /* Enable GIC */ @@ -196,9 +222,10 @@ void gic_init(struct gic_data *gd, vaddr_t gicc_base, vaddr_t gicd_base) gd->gicc_base + GICC_CTLR); write32(GICD_CTLR_ENABLEGRP0 | GICD_CTLR_ENABLEGRP1, gd->gicd_base + GICD_CTLR); +#endif } -void gic_init_base_addr(struct gic_data *gd, vaddr_t gicc_base, +void gic_init_base_addr(struct gic_data *gd, vaddr_t gicc_base __maybe_unused, vaddr_t gicd_base) { gd->gicc_base = gicc_base; @@ -320,14 +347,22 @@ static void gic_it_raise_sgi(struct gic_data *gd, size_t it, write32(mask, gd->gicd_base + GICD_SGIR); } -static uint32_t gic_read_iar(struct gic_data *gd) +static uint32_t gic_read_iar(struct gic_data *gd __maybe_unused) { +#if defined(CFG_ARM_GICV3) + return read_icc_iar0_el1(); +#else return read32(gd->gicc_base + GICC_IAR); +#endif } -static void gic_write_eoir(struct gic_data *gd, uint32_t eoir) +static void gic_write_eoir(struct gic_data *gd __maybe_unused, uint32_t eoir) { +#if defined(CFG_ARM_GICV3) + write_icc_eoir0_el1(eoir); +#else write32(eoir, gd->gicc_base + GICC_EOIR); +#endif } static bool gic_it_is_enabled(struct gic_data *gd, size_t it) @@ -361,7 +396,11 @@ void gic_dump_state(struct gic_data *gd) { int i; +#if defined(CFG_ARM_GICV3) + DMSG("GICC_CTLR: 0x%x", read_icc_ctlr_el1()); +#else DMSG("GICC_CTLR: 0x%x", read32(gd->gicc_base + GICC_CTLR)); +#endif DMSG("GICD_CTLR: 0x%x", read32(gd->gicd_base + GICD_CTLR)); for (i = 0; i < (int)gd->max_it; i++) { diff --git a/core/include/drivers/gic.h b/core/include/drivers/gic.h index 2032e06c318..d26410c30b7 100644 --- a/core/include/drivers/gic.h +++ b/core/include/drivers/gic.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Linaro Limited + * Copyright (c) 2016-2017, Linaro Limited * Copyright (c) 2014, STMicroelectronics International N.V. * All rights reserved. * @@ -56,4 +56,70 @@ void gic_cpu_init(struct gic_data *gd); void gic_it_handle(struct gic_data *gd); void gic_dump_state(struct gic_data *gd); + + +#if defined(CFG_ARM_GICV3) +#if defined(ARM64) +static inline uint32_t read_icc_ctlr_el1(void) +{ + uint32_t v; + __asm__ volatile ("mrs %0, S3_0_C12_C12_4" : "=r" (v)); + return v; +} + +static inline void write_icc_ctlr_el1(uint32_t v) +{ + __asm__ volatile ("msr S3_0_C12_C12_4, %0" : : "r" (v)); +} + +static inline void write_icc_pmr_el1(uint32_t v) +{ + __asm__ volatile ("msr S3_0_C4_C6_0, %0" : : "r" (v)); +} + +static inline uint32_t read_icc_iar0_el1(void) +{ + uint32_t v; + __asm__ volatile ("mrs %0, S3_0_c12_c8_0" : "=r" (v)); + return v; +} + +static inline void write_icc_eoir0_el1(uint32_t v) +{ + __asm__ volatile ("msr S3_0_c12_c8_1, %0" : : "r" (v)); +} +#elif defined(ARM32) +static inline uint32_t read_icc_ctlr_el1(void) +{ + uint32_t v; + __asm__ volatile ("mrc p15,0,%0,c12,c12,4" : "=r" (v)); + return v; +} + +static inline void write_icc_ctlr_el1(uint32_t v) +{ + __asm__ volatile ("mcr p15,0,%0,c12,c12,4" : : "r" (v)); +} + +static inline void write_icc_pmr_el1(uint32_t v) +{ + __asm__ volatile ("mcr p15,0,%0,c4,c6,0" : : "r" (v)); +} + +static inline uint32_t read_icc_iar0_el1(void) +{ + uint32_t v; + __asm__ volatile ("mrc p15,0,%0,c12,c8,0" : "=r" (v)); + return v; +} + +static inline void write_icc_eoir0_el1(uint32_t v) +{ + __asm__ volatile ("mcr p15,0,%0,c12,c8,1" : : "r" (v)); +} +#else +#error Neither ARM32 nor ARM64 is defined +#endif +#endif /* CFG_ARM_GICV3 */ + #endif /*__DRIVERS_GIC_H*/ diff --git a/documentation/interrupt_handling.md b/documentation/interrupt_handling.md index 806335798e2..95aef8e1e93 100644 --- a/documentation/interrupt_handling.md +++ b/documentation/interrupt_handling.md @@ -28,10 +28,6 @@ Two types of interrupt are defined in optee_os: For ARM GICv2 mode, native interrupt is sent as FIQ and foreign interrupt is sent as IRQ. -For ARM GICv3 mode, foreign interrupt is sent as FIQ which could be handled -by either secure world (EL3 in AArch64) or normal world. This mode is not -supported yet. - Since IRQs are received using the state vector the actual vector used depends on the current state of the CPU. If the NS (non-secure) bit in SCR (Secure Control Register) is set then either HVBAR or VBAR (non-secure) is @@ -41,6 +37,12 @@ receive IRQ that are supposed to be handled by normal world. When secure world receives an IRQ it has to be forwarded to normal world for processing. +For ARM GICv3 mode, foreign interrupt is sent as FIQ which could be handled +by either secure world (aarch32 Monitor mode or aarch64 EL3) or normal world. +ARM GICv3 mode can be enabled by setting `CFG_ARM_GICV3=y`. + +The following descriptions take ARM GICv2 mode as the example. + # The monitor The monitor manages all entry and exit of secure world. To enter secure world from normal world the monitor saves the state of normal world