Blog
Blog copied to clipboard
aarch64体系结构与编程7--ARM64的中断处理
ARM64异常处理之中断处理
- ARM核心两个和中断相关的 管脚:nIRQ 和 nFIQ
- 每个CPU核心有一对这样的 中断相关的管脚
- PSTATE状态中有两个比特位和中断相关
- I:用来屏蔽IRQ中断
- F:用来屏蔽FIQ中断
ARM64中的GIC控制器
- ARM提供了标准的GIC控制器,例如树莓派4b上支持GIC-400
- 树莓派3b上支持 传统的中断方式(legacy interrupt)
中断处理过程
树莓派4b上的中断控制器
- 树莓派4b上提供两种中断控制器
- GIC-400 (默认)
- 传统的中断控制器(legacy interrupt controller)
- 树莓派4b支持多种中断源
- ARM Core N:ARM Core本身的中断源,例如Core里面的generic timer
- ARMC:可以被VPU和CPU访问的中断源,例如mailbox等
- ARM_LOCAL:只能被CPU访问的中断源,例如本地timer等
这里的IRQ routing可以是legacy,也可以是gic.
下图是legace的方式routing
legacy routing处理流程
中断状态寄存器 路由过程:
- 先读SOURCEn中断状态寄存器
- SOURCEn寄存器 bit 8 是否置位,若置位,读取PENDING2寄存器
- 若PENDING2的bit24置位,则读PENDING0寄存器
- 若PENDING2的bit25置位,则读PENDING1寄存器
举例:以ARM Core的generic timer为例
Cortex-A72支持4个ARM Core的generic timer
- CNT_PS_IRQ:Secure EL1 Physical Timer Event interrupt
- CNT_PNS_IRQ:Nonsecure EL 1 Physical Timer Event interrupt
- CNT_HP_IRQ: Hypervisor Physical Timer Event interrupt, EL2
- CNT_V_IRQ:Virtual Timer Event interrupt EL3
ARM_LOCAL寄存器中有:
- 4个IRQ_SOURCE寄存器,每个CPU一个,表示中断源的状态
- 4个FIQ_SOURCE寄存器,每个CPU一个
- 4个TIMER_CNTRL寄存器,每个CPU一个,用来enable对应的中断源
以 Nonsecure EL 1 Physical Timer为例,相关初始化的寄存器是在ARM Core中,见armv8.6手册。
CNTP_CTL_EL0, Counter-timer Physical Timer Control register. (第D13.8.16章)
CNTP_TVAL_EL0, Counter-timer Physical Timer TimerValue register(第D13.8.18章)
Timer支持两种触发方式:见第D11.2.4章
- 64位的 comparevalue的方式
- timervalue的方式,就是初始化一个值,当递减到0的时候,timer中断就触发
EL1的 Nonsecure generic timer的中断处理流程
- 初始化timer,设置cntp_ctl_el0寄存器的enable域为1
- 给timer的TimeValue一个初值,设置cntp_tval_el0寄存器
- 打开树莓派中断控制器中和timer相关的中断,设置TIMER_CNTRL0寄存器中的CNT_PNS_IRQ为1.
- 打开PSTATE寄存器中的IRQ中断总开关
- Timer中断发生
- 跳转到el1_irq汇编函数
- 保存中断上下文(使用kernel_entry宏)
- 跳转到中断处理函数
- 读取ARM_LOCAL中中断状态寄存器IRQ_SOURCE0
- 判断是否CNT_PNS_IRQ中断发生,如果是,重新设置TimeValue
- 返回到el1_irq汇编函数
- 恢复中断上下文
- 返回中断现场
### 中断现场(中断上下文) 中断发生瞬间,CPU的状态,包括:
- PSTATE寄存器
- PC值
- SP值
- X0~x30寄存器
使用一个栈框数据结构来描述需要保存的中断现场(struct pt_regs)
保存中断现场
中断发生时,中断上下文保存到当前进程的内核栈里,使用栈框保存
恢复中断现场
中断返回时,从内核栈恢复中断现场
GIC中断控制器介绍
早期的中断控制器
传统的中断控制器,例如树莓派4b上的legacy interrupt controller
- 中断enable寄存器
- 中断disable寄存器
- 中断状态寄存器
传统的使用简单状态寄存器的方式来管理中断,变得越来越难管理
- 中断源变得原来越多
- 不同类型的中断,比如多核间的中断,中断优先级,软件定义的中断等
GIC中断控制器发展历史
在Cortex-A时期就开发了GIC中断控制器 GICV1: ✓ 支持8核 ✓ 支持多大1020个中断源 ✓ 8bit优先级 ✓ 支持软件触发中断 ✓ TrustZone支持 IP: GIC-390 应用场景: Cortex-A9 MPCore
GICV2: ✓ 虚拟化支持 ✓ 支持secure software IP: GIC-400 应用场景: Cortex-A7 MPCore 树莓派4b
GICV3: ✓ 支持CPU核心数量大于8 ✓ 支持基于消息的中断 ✓ 支持更多的中断ID IP: GIC-500 GIC-600
GIC支持的中断类型
- SGI:软件产生的中断(Software Generated Interrupt),软中断即软件产生的中断,用于给其他CPU核心发送中断信号
- PPI:私有外设中断(Private Peripheral Interrupt),私有的外设中断,该中断是某个指定的CPU独有的
- SPI:共享外设中断(Shared Peripheral Interrupt),共享的外设中断,所有CPU都可以访问这个中断
- LPI,本地特殊外设中断(Locality-specific Peripheral Interrupt),GICv3新增的中断类型。基于消息传递的中断类型
GICV2中断控制器中断号分配情况
中断类型 | 中断号范围 |
---|---|
软件触发中断(SGI) | 0~15 |
私有外设中断(PPI) | 16~31 |
共享外设中断(SPI) | 32~1019 |
注意:中断号1020~1023是保留的
中断状态
- 不活跃状态(inactive):中断处于无效状态
- 等待状态(pending):中断处于有效状态,但是等待CPU响应该中断
- 活跃状态(active):CPU已经响应该中断
- 活跃并等待状态(active and pending):CPU正在响应该中断,但是该中断源又发送中断过来
GICV2中断控制器
The Distributor registers (GICD_)包含了中断设置和配置 The CPU Interface registers (GICC_)包含CPU相关的特殊设置
中断路由
GICD_ITARGETSRn 寄存器用来配置Distributor可以把中断路由到哪个CPU上。
- 8bit来表示一个中断源,每个bit代表能路由的CPU
- 某个bit设置了,说明该中断源可以路由到这个CPU上
- 前32个中断源的路由配置是 硬件设置好的,RO
- 第33~1019号中断,可以由软件来配置其路由, RW
GIC-400中断信号时序
- T1 The Distributor detects the assertion of Group 0 interrupt M.
- T2 The Distributor sets interrupt M to pending.
- T17 The CPU interface asserts nFIQCPU[n].(The assertion of nFIQCPU[n] occurs some CLK cycles after interrupt M becomes pending. In Figure B-1, the latency at the physical interface is tph = 15 clock cycles.)
- T42 The Distributor detects the assertion of a higher priority Group 0 interrupt, N.
- T43 The Distributor replaces interrupt M with interrupt N as the highest priority pending interrupt and sets N to pending.
- T58 tph clock cycles after interrupt N became pending, the CPU interface asserts
nFIQCPU[n]. The state of nFIQCPU[n] is unchanged because nFIQCPU[n]
was asserted at T17.
The CPU interface updates the InterruptID field in the GICC_IAR, to contain the ID value for interrupt N. - T61 The processor reads the GICC_IAR, acknowledging the highest priority pending interrupt, N. The Distributor sets interrupt N to active and pending
- T61-T131 The processor services interrupt N.
- T64 3 clock cycles after interrupt N has been acknowledged, the CPU interface deasserts nFIQCPU[n]
- T126 The peripheral deasserts interrupt N.
- T128 The pending state is removed from N.
- T131 The processor writes to the End of Interrupt Register, GICC_EOIR, with the ID of interrupt N and the Distributor deactivates interrupt N.
- T146 tph clock cycles after GICC_EOIR was written to for N, the Distributor forwards the new highest priority pending interrupt, M, to the CPU interface, which asserts nFIQCPU[n].
- T211 The processor reads the GICC_IAR, acknowledging the highest priority pending interrupt, M, and the Distributor sets interrupt M to active and pending.
- T214 3 clock cycles after interrupt M has been acknowledged, the CPU interface deasserts nFIQCPU[n].
GICv2寄存器
GICv2寄存器分成两组:
- D:表示Distributor的寄存器
- C:表示CPU interface的寄存器
有些寄存器是按照 中断号 来描述的,比如使用某几个比特位来描述一个 中断号 的相关属性
同一个寄存器可以n个。
例如GICD_ISENABLERn寄存器,它是用来 使能某个中断号的。“n”表示它有n个这样的寄存器
Table 4-1 Distributor register map,可以看到这个寄存器从0x100 到 0x17c,这些都是这个寄存器
上面寄存器里的每个比特位表示一个中断号的enable
寄存器n的计算
树莓派4b上的GIC-400中断号分配情况
访问GIC-400寄存器
树莓派4b上的GIC-400的基地址
GIC-400上distributor和CPU interface的offset
GIC-400初始化流程
1.设置distributor和CPU interface寄存器组的基地址
2.读取GICD_TYPER寄存器,计算当前GIC最大支持多少个中断源。
3.初始化distributor
a. Disable distributor
b. 设置SPI中断的路由
c. 设置SPI中断的触发类型,例如level触发
d. Disactive和disable所有的中断源
e. Enable distributor
4.初始化CPU interface
a. 设置GIC_CPU_PRIMASK,设置中断优先级mask level
b. Enable CPU interface
注册中断
- 初始化外设
- 查找该外设的中断在GIC-400的中断号,例如PNS timer的中断号为30
- 设置GIC_DIST_ENABLE_SET寄存器来 enable这个中断号
- 打开设备相关的中断,例如树莓派上的generic timer,需要打开ARM_LOCAL寄存器组中的TIMER_CNTRL0寄存器中相关的enable位
- 打开CPU的PSTATE中I位(PSTATE.I)
中断响应
- 中断发生
- 异常向量表
- 跳转到GIC中断函数里,gic_handle_irq()
- 读取GICC_IAR寄存器,获取中断号
- 根据中断号来进行相应中断处理,例如读取的中断号为30,说明的是PNS的generic timer,然后跳转到generic timer的处理函数里