Skip to content

Commit

Permalink
Support ARM GICv3 mode
Browse files Browse the repository at this point in the history
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 assigned 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.

Signed-off-by: David Wang <david.wang@arm.com>
  • Loading branch information
David Wang committed Apr 10, 2017
1 parent 25096c4 commit 801167d
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 10 deletions.
5 changes: 5 additions & 0 deletions core/arch/arm/include/kernel/thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
8 changes: 8 additions & 0 deletions core/arch/arm/kernel/thread_a32.S
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
8 changes: 8 additions & 0 deletions core/arch/arm/kernel/thread_a64.S
Original file line number Diff line number Diff line change
Expand Up @@ -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
49 changes: 44 additions & 5 deletions core/drivers/gic.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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
Expand All @@ -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;

Expand Down Expand Up @@ -189,16 +209,23 @@ 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 */
write32(GICC_CTLR_ENABLEGRP0 | GICC_CTLR_ENABLEGRP1 | GICC_CTLR_FIQEN,
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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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++) {
Expand Down
72 changes: 71 additions & 1 deletion core/include/drivers/gic.h
Original file line number Diff line number Diff line change
@@ -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.
*
Expand Down Expand Up @@ -56,4 +56,74 @@ 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*/
10 changes: 6 additions & 4 deletions documentation/interrupt_handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down

0 comments on commit 801167d

Please sign in to comment.