arm64 FIQ

arm64架构FIQ中断

Posted by icecube on January 13, 2024

FIQ简介

FIQ中断的设计旨在提供比普通中断更高的优先级。
在AArch64架构中,中断级别是由中断控制器来管理的。
FIQ通常具有较高的中断级别,以确保它们在系统中的其他中断之前被快速处理。

较高的优先级意味着可以打断普通中断,在系统发生异常发生时深入到异常现场。
系统陷入内核态运行时如果出现关中断死循环的场景,或者关中断后处理时间过长,都可以利用FIQ中断捕捉问题上下文。

当发生FIQ中断时,处理器会跳转到预先注册好的fiq handler,
在这个handler里把异常上下文(寄存器以及栈等信息),输出到串口或者保存到指定位置便于后续分析定位问题。

FIQ的触发通常由中断控制器生成,这个控制器一般是SOC中的一部分。
中断控制器负责检测中断请求并向处理器报告中断的类型和优先级。
arm 架构通用中断控制器是gicv2或者gicv3。

CPU内部定时器

通用定时器为Arm核心提供了一个标准化的定时器框架。
通用定时器包括系统计数器和一组每核心per-cpu的定时器。

每个核心都有一组定时器。这些定时器是比较器,与系统计数器提供的广播系统计数进行比较。
软件可以配置定时器在将来的特定点生成中断或事件。
EL1 Physical timer和EL1 virtual timer 这两个per-cpu定时器是必须实现的。

定时器生成的中断以电平触发方式进行。这意味着一旦达到定时器触发条件,定时器将继续发出中断,直到发生以下情况之一:
• IMASK设置为1,屏蔽中断。
• ENABLE清零为0,禁用定时器。
• 写入TVAL或CVAL,以便不再满足触发条件。
在为定时器编写中断处理程序时,重要的是在在GIC中停用中断之前,软件清除中断。否则,GIC将再次发出相同的中断信号。

Register                    Purpose

<timer>_CTL_EL<x>           Control register
<timer>_CVAL_EL<x>          Comparator value
<timer>_TVAL_EL<x>          Timer value
Timer                               Register EL<x> prefix     SBSA recommended INTID  

EL1 physical timer                  CNTP EL0                    30
EL1 virtual timer                   CNTV EL0                    27
Non-secure EL2 physical timer       CNTHP EL2                   26
Non-secure EL2 virtual timer        CNTHV EL2                   28
EL3 physical timer                  CNTPS EL1                   29
Secure EL2 physical timer           CNTHPS EL2                  20
Secure EL2 virtual timer            CNTHVS EL2                  19

配置 FIQ

参考官网介绍
https://developer.arm.com/documentation/ihi0048/b/Interrupt-Handling-and-Prioritization/Example-GIC-usage-models/Using-IRQs-and-FIQs-to-provide-Non-secure-and-Secure-interrupts

Figure 3.9 shows a system that implements the GIC Security Extensions,
connected to a processor that implements the ARM processor Security Extensions. This implementation:
• uses Group 0 interrupts as Secure interrupts, signaled as FIQs
• uses Group 1 interrupts as Non-secure interrupts, signaled as IRQs.
This means that, on the processor, FIQ interrupts are never routed to Non-secure software, and IRQ interrupts are never routed to Secure software.

需要SoC厂商提供ATF固件把PPI 27 配置到group 0,PPI 27 才可以作为FIQ中断上报给CPU

FIQ功能实现

嵌入式设备不需要虚拟化功能,因此可以利用 per-cpu vritual timer 实现FIQ功能。

  1. ATF固件把GIC配置成Using IRQs and FIQs to provide Non-secure and Secure interrupts,SCR_EL3.FIQ = 1
    把per-cpu virtual timer(PPI 27)中断配置到group0作为FIQ上报
  2. 系统初始化启动阶段调用smc注册FIQ回调函数 fiq handler(在fiq handler中记录CPU异常现场)
  3. armv8利用per-cpu virtual timer 监控本核,即可以在进程切换的位置刷新timer(等同于喂狗),
  4. 一旦内核态出现关中断死循环异常,也就不会再刷新timer(不再喂狗),
    定时器超时就会在EL1 产生FIQ,先路由至EL3的ATF简单处理FIQ中断,然后回到EL1 执行内核注册的回调函数fiq handler
    这就实现了类似于x86架构的nmi中断。

刷新定时器喂狗

void touch_virtimer_watchdog(void)
{
    unsigned long long ctlel0;
    unsigned long timeout = 30;

    // set timeout
    write_sysreg(timeout * arch_timer_get_cntfrq(), cntv_tval_el0);

    // enable virt timer
    ctlel0 = read_sysreg(cntv_ctl_el0);
    ctlel0 |= ARCH_TIMER_CTRL_ENABLE;
    ctlel0 &= ~ARCH_TIMER_CTRL_ENABLE;
    write_sysreg(ctlel0, cntv_ctl_el0);  
}

基于arch_timer利用hrtimer硬中断中喂狗,每个核绑定一个hrtimer

static DEFINE_PER_CPU(struct hrtimer, fiq_hrtimers);

/* timeout callback */
static enum hrtimer_restart fiq_hrtimer_callback(struct hrtimer *timer)
{
       touch_virtimer_watchdog();
       hrtimer_forward(timer, ktime_get(), ktime_set(5, 0));  // 5s timeout
       return HRTIMER_RESTART;
}

/* restart the timer */
static void fiq_hrtimer_touchdog(void *info)
{
       struct hrtimer *hrtimer = this_cpu_ptr(&fiq_hrtimers);
       hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED_HARD);
       hrtimer->function = fiq_hrtimer_callback;

       hrtimer_start(hrtimer, ktime_set(0, 1000), HRTIMER_MODE_REL_PINNED_HARD);
}

/* register the per-cpu hrtimer */
void smp_cpus_done(void)
{
    ...
    on_each_cpu(fiq_hrtimer_touchdog, NULL, 1);
    ...
}


SoC外部定时器

外部定时器的编程接口与内部定时器相同,但是这些定时器是通过内存映射寄存器访问的。
这些寄存器的位置具体实现取决于SoC,会在SoC手册中说明。

外部内存映射定时器的中断通常作为共享外设中断(SPIs)由GIC报给cpu。

  1. SoC上有external memory-mapped timers组件
  2. boot阶段配置SoC external memory-mapped timer 48 49 号中断为FIQ类型,每个核分配一个timer,0核48号中断,1核49号中断
  3. 要访问SoC external memory-mapped timer硬件,在系统启动初始化时用memory mapped IO的方式建立好映射关系
    初始化timer寄存器,使能timer,设定超时阀值,并且负责喂狗
  4. FIQ经由arm64架构异常向量表,实现FIQ异常向量处理函数,记录被FIQ中断的异常现场信息

修改dts文件,关闭定时器功能,当做watchdog,初始化的时候写入狗叫阀值30s

 &timer0 {
-	status = "okay";
+	status = "disabled";      // 把定时器功能关闭,当做watchdog,初始化的时候写入狗叫阀值30s
 };

参考

https://tc.gts3.org/cs3210/2020/spring/r/aarch64-generic-timer.pdf
https://developer.arm.com/documentation/ddi0406/c/Appendices/System-level-implementation-of-the-Generic-Timer/About-the-memory-mapped-view-of-the-counter-and-timer
https://developer.arm.com/documentation/ihi0048/b/Interrupt-Handling-and-Prioritization/Example-GIC-usage-models/Supporting-IRQs-and-FIQs-when-not-using-the-processor-Security-Extensions