ChinOS
关于GIC
GIC是一个针对Cortex-A和Cortex-R设计的中断控制器,SOC厂商根据选择的CORE来选择不同的GIC版本进行搭配
GICv3
GICv3的文档请参考ARM文档中心Documentation – Arm Developer
Arm Generic Interrupt Controller v3 and v4 Overview
GICv3架构
GICv3由以下模块组成
- Distributor interface
- SPI中断的管理,将中断发送给redistributor
- Redistributor interface
- PPI,SGI,LPI中断的管理,将中断发送给cpu interface
- CPU interface
- 传输中断给core
- ITS
- 用来解析LPI中断
其中,cpu interface是实现在core内部的,distributor,redistributor,ITS是实现在gic内部的
中断分组
不同异常等级下的分组:
- GROUP0下的所有中断都被识别为FIQ中断;
- 非安全世界下的EL0/1/2下,GROUP1下的所有中断被识别为IRQ中断,这也是我们操作系统最常用的中断分组,一般操作系统和hypervisor都处于非安全世界(除了TrustOS外)
中断类型
Shared Peripheral Interrupt (SPI)
共享外设中断,也就是通常用的外设中断,可以多个CPU或者说CORE处理,不限定特定的CPU
Private Peripheral Interrupt (PPI)
私有外设中断,这是每个核心私有的中断。PPI会送达到指定的CPU上,应用场景有CPU本地时钟
Software Generated Interrupt (SGI)
软中断,软件可以通过写GICD_SGIR寄存器来触发一个中断事件,一般用于核间通信
Locality-specific Peripheral Interrupt (LPI)
消息中断,不通过专用中断线向GIC发送消息,通过写GIC寄存器发送中断
不同中断类型的中断号划分
中断处理流程
通过distributor的中断流程
- 外设发起中断,发送给distributor
- distributor将该中断,分发给合适的re-distributor
- re-distributor将中断信息,发送给cpu interface
- cpu interface产生合适的中断异常给处理器
- 处理器接收该异常,并且处理该中断
不通过distributor,比如LPI中断
- 外设发起中断,发送给ITS
- ITS分析中断,决定将来发送的re-distributor
- ITS将中断发送给合适的re-distributor
GICv3初始化
GIC的初始化实际上就是其主要模块(distributor/redistributor/cpu_interface)的初始化
Distributor初始化
Distributor主要初始化和配置SPI中断:
static void distributor_init(void)
{
u32 i;
// read gic version
u32 pidr2 = getreg32(GICD_PIDR2);
u32 gic_version = bitfield_get(pidr2, 4, 4);
assert((gic_version == ARCH_REV_GICV3) || (gic_version == ARCH_REV_GICV4));
// read gic max interrupts
u32 typer = getreg32(GICD_TYPER);
gic_max_int = (bitfield_get(typer, 0, 5) + 1) * 32;
kprintf("gic_version = %d, gic_max_int = %d, typer = %p\n", gic_version, gic_max_int, typer);
// disable distributor
putreg32(0, GICD_CTLR);
gicd_wait_rwp();
// assume level-trigger
for (i = 32; i < gic_max_int; i += 16) {
putreg32(0, GICD_ICFGR(i / 16));
}
// default priority for global interrupts
u32 priority = (GIC_PRI_IRQ << 24 | GIC_PRI_IRQ << 16 | GIC_PRI_IRQ << 8 |
GIC_PRI_IRQ);
for (i = 32; i < gic_max_int; i += 4) {
putreg32(priority, GICD_IPRIORITYR(i / 4));
}
// distributor config: mask and clear all spis, set group 1.
for (i = 32; i < gic_max_int; i += 32) {
putreg32(~0, GICD_ICENABLER(i / 32));
putreg32(~0, GICD_ICPENDR(i / 32));
putreg32(~0, GICD_IGROUPR(i / 32));
putreg32(0, GICD_IGRPMODR(i / 32));
}
gicd_wait_rwp();
// enable distributor with ARE, group 1 enable
putreg32(CTLR_ENABLE_G0 | CTLR_ENABLE_G1NS | CTLR_ARE_S, GICD_CTLR);
gicd_wait_rwp();
// route all global IRQs to this CPU
u64 affinity = mpidr_to_gic_affinity();
kprintf("affinity = %p\n", affinity);
for (i = 32; i < gic_max_int; i++) {
putreg64(GICD_IROUTER(i), affinity);
}
gicd_wait_rwp();
isb();
dsb();
}
Redistributor初始化
Redistributor主要初始化和配置PPI/SGI/LPI中断
static void redistributor_init(void)
{
u32 i;
u32 cpu = 0;
// mark PE online
u32 waker = getreg32(GICR_WAKER(cpu));
kprintf("waker = %p\n", waker);
waker &= ~GICR_WAKER_ProcessorSleep;
kprintf("waker = %p\n", waker);
putreg32(waker, GICR_WAKER(cpu));
while (getreg32(GICR_WAKER(cpu)) & GICR_WAKER_ChildrenAsleep) {
kprintf("GICv3: GICR_WAKER returned non-zero %x\n", waker);
halt();
}
/* deactivate SGIs/PPIs */
putreg32(~0, GICR_ICACTIVER0(cpu));
gicr_wait_rwp();
/* set ICFGR0 for SGIs as edge-triggered */
u32 icfgr0 = getreg32(GICR_ICFGR0(cpu));
kprintf("icfgr0 = %p\n", icfgr0);
putreg32(0xaaaaaaaa, GICR_ICFGR0(cpu));
gicr_wait_rwp();
/* set ICFGR1 for PPIs as level-triggered */
u32 icfgr1 = getreg32(GICR_ICFGR1(cpu));
kprintf("icfgr1 = %p\n", icfgr1);
putreg32(0, GICR_ICFGR1(cpu));
gicr_wait_rwp();
/* set priority on PPI and SGI interrupts */
u32 priority = (GIC_PRI_IRQ << 24 | GIC_PRI_IRQ << 16 | GIC_PRI_IRQ << 8 |
GIC_PRI_IRQ);
for (i = 0; i < 32; i += 4) {
putreg32(priority, GICR_IPRIORITYR0(cpu) + i);
}
gicr_wait_rwp();
// redistributer config: configure sgi/ppi as non-secure group 1.
putreg32(~0, GICR_IGROUPR0(cpu));
gicr_wait_rwp();
// redistributer config: clear and mask sgi/ppi.
putreg32(~0, GICR_ICENABLER0(cpu));
putreg32(~0, GICR_ICPENDR0(cpu));
gicr_wait_rwp();
// TODO lpi init
}
cpu_interface初始化
static void cpu_interface_init(void)
{
u32 sre;
// enable system register access
MRS(ICC_SRE_EL1, sre);
kprintf("sre = %p\n", sre);
sre |= GICC_SRE_EL1_SRE;
kprintf("sre = %p\n", sre);
MSR(ICC_SRE_EL1, sre);
// no priority grouping: ICC_BPR1_EL1
MSR(ICC_BPR1_EL1, 0);
// set priority mask register: ICC_PMR_EL
MSR(ICC_PMR_EL1, DEFAULT_PMR_VALUE);
// EOI drops priority and deactivates the interrupt: ICC_CTLR_EL1
u32 icc_ctlr;
MRS(ICC_CTLR_EL1, icc_ctlr);
kprintf("icc_ctlr = %p\n", icc_ctlr);
icc_ctlr &= ~GICC_CTLR_EL1_EOImode_drop;
kprintf("icc_ctlr = %p\n", icc_ctlr);
MSR(ICC_CTLR_EL1, icc_ctlr);
// Enable Group1 interrupts: ICC_IGRPEN1_EL1
MSR(ICC_IGRPEN1_EL1, 1);
// Sync at once at the end of cpu interface configuration
isb();
dsb();
}
中断向量表初始化
不同异常等级下所对应的中断向量表VBAR_ELx,决定了中断产生后跳转到哪个地址去执行
static void relocate_vector(void)
{
unsigned long current_el;
unsigned long vbase = (unsigned long)vector;
MRS("CurrentEL", current_el);
current_el = bitfield_get(current_el, 2, 2);
switch(current_el)
{
case 0:
break;
case 1:
MSR("VBAR_EL1", vbase);
MRS("VBAR_EL1", vbase);
kprintf("VBAR_EL1 = %p\n", vbase);
break;
case 2:
MSR("VBAR_EL2", vbase);
MRS("VBAR_EL2", vbase);
kprintf("VBAR_EL2 = %p\n", vbase);
break;
case 3:
MSR("VBAR_EL3", vbase);
MRS("VBAR_EL3", vbase);
kprintf("VBAR_EL3 = %p\n", vbase);
break;
default:
break;
}
}
vector符号定义在vector.S中,注意一个异常向量表占用0x800空间,需要2^11=2048字节对齐;
一个异常向量表里有16个表项,每个表项占用0x80空间,需要2^7=128字节对齐
.global vector
.macro ventry label
.align 7
b \label
.endm
.section ".vector", "ax"
.align 11
vector:
ventry invalid_vector_entry // Synchronous EL1t/EL2t
ventry invalid_vector_entry // IRQ EL1t/EL2t
ventry invalid_vector_entry // FIQ EL1t/EL2t
ventry invalid_vector_entry // SError EL1t/EL2t
ventry cur_el_sync // Current EL Synchronous (EL1/2)
ventry cur_el_irq // IRQ
ventry invalid_vector_entry // FIQ
ventry cur_el_serr // SError
ventry lower_el_sync // Synchronous 64-bit EL0/EL1
ventry lower_el_irq // IRQ 64-bit EL0/EL1
ventry invalid_vector_entry // FIQ 64-bit EL0/EL1
ventry lower_el_serr // SError 64-bit EL0/EL1
ventry invalid_vector_entry // Synchronous 32-bit EL0/EL1
ventry invalid_vector_entry // IRQ 32-bit EL0/EL1
ventry invalid_vector_entry // FIQ 32-bit EL0/EL1
ventry invalid_vector_entry // SError 32-bit EL0/EL1
invalid_vector_entry:
b .
cur_el_sync:
b .
cur_el_irq:
bl decode_irq
eret
b .
cur_el_serr:
b .
lower_el_sync:
b .
lower_el_irq:
b .
lower_el_serr:
b .
默认情况下,不同异常等级使用各自异常等级下的VBAR_ELx,所以表1和表4一般不使用,表2和表3使用的最多,表2代表在当前异常等级产生的中断,表3代表在比当前异常等级低产生中断。对于我们操作系统来说,操作系统工作在EL1,那么表2代表在内核态(EL1)下产生的中断,表1代表在用户态(EL0)产生的中断
使能VBAR_ELx
SPSel为1代表使能
u64 spsel;
MRS("SPSel", spsel);
kprintf("SPSel = %p\n", spsel);