FIQ/IRQ 转换原理详解【原创】
FIQ/IRQ 转换原理详解
🔍 FIQ/IRQ 转换的原理
1. 传统 ARM 架构的中断机制
传统 ARM 处理器(如 ARM926EJ-S):
- 有两个专用的中断信号:
nIRQ
和nFIQ
nIRQ
: 普通中断请求(Normal Interrupt Request)nFIQ
: 快速中断请求(Fast Interrupt Request)- 这两个信号是硬件直接路由的
2. GIC 中断控制器的架构
GIC(Generic Interrupt Controller)引入了更复杂的机制:
传统方式: 外设 → nIRQ/nFIQ → CPU
GIC方式: 外设 → GIC Distributor → GIC CPU Interface → IRQ/FIQ → CPU
3. GIC 的中断分组机制
关键概念 - Interrupt Groups:
- Group 0: 安全中断(Secure Interrupts)
- Group 1: 非安全中断(Non-secure Interrupts)
路由规则(GICv2):
// GICv2 中断路由规则
Group 0 中断 → 发送 FIQ 信号到 CPU
Group 1 中断 → 发送 IRQ 信号到 CPU
这个路由是通过 GICD_IGROUP
寄存器控制的:
// IGROUP 寄存器中每个位控制一个中断的分组
bit = 0 → Group 0 (FIQ)
bit = 1 → Group 1 (IRQ)
⚙️ 硬件支持要求
1. 必需的硬件支持
处理器要求:
- ARM Cortex-A 系列处理器
- 支持 TrustZone 安全扩展
- 支持 FIQ 模式
GIC 版本要求:
- GICv2: 支持 Group 0/1 → FIQ/IRQ 路由
- GICv3: 改变了路由规则(更复杂)
- GICv4/v5: 进一步优化
2. 关键寄存器支持
/* GIC Distributor 寄存器 */
#define GIC_DIST_IGROUP 0x080 // 中断分组寄存器
#define GIC_DIST_CTRL 0x000 // 分发器控制寄存器
/* GIC CPU Interface 寄存器 */
#define GIC_CPU_CTRL 0x00 // CPU 接口控制寄存器
#define GIC_CPU_CTRL_FIQEn (1<<3) // FIQ 使能位
🚫 为什么有人说 GIC-400 不支持 IRQ→FIQ?
1. 误解的来源
主要原因:
- 安全模式限制: Group 0/1 的设置需要在 Secure 模式下进行
- Linux 的限制: Linux 通常运行在 Non-secure 模式,无法修改 IGROUP 寄存器
- 默认配置: 大多数系统默认将所有中断配置为 Group 1 (IRQ)
2. 实际的技术限制
在 Non-secure 模式下:
// Linux 运行在 Non-secure 模式,无法访问:
// - GICD_IGROUP 寄存器(只能在 Secure 模式访问)
// - Group 0 中断的配置
// - 安全相关的 GIC 寄存器
证据来自内核社区:
"Group is configured by software using the GICD_IGROUPR
registers...
The register is also Secure access only...
Which means something on the receiving CPU must write that registers to mark the SGI you're sending as being G1, and the code doing the writes will need to be running in Secure state."
3. 常见代码问题
错误的实现方式:
/* 这段代码在 Linux(Non-secure 模式)下不会工作! */
void __iomem *igroup_reg = gic_dist_base + GIC_DIST_IGROUP + reg_offset;
val = readl_relaxed(igroup_reg); // GICD_IGROUP 寄存器
val &= ~mask; // 试图设置为 Group 0 (FIQ)
writel_relaxed(val, igroup_reg); // 写入会被忽略或导致异常
问题:
- Linux 运行在 EL1(Non-secure)
- GICD_IGROUP 寄存器只能在 EL3(Secure)模式下访问
- 写入操作会被硬件忽略或引发异常
✅ 正确的解决方案
1. 在 Secure 模式下配置
需要在 Secure World(如 ARM Trusted Firmware)中配置:
// 在 ATF 或 Secure Monitor 中
void setup_fiq_routing(unsigned int irq) {
void __iomem *gicd_base = get_gicd_base();
u32 reg_offset = (irq / 32) * 4;
u32 bit_offset = irq % 32;
u32 mask = 1U << bit_offset;
u32 val;
// 只有在 Secure 模式下才能访问 IGROUP
val = readl(gicd_base + GICD_IGROUP + reg_offset);
val &= ~mask; // 设置为 Group 0 (FIQ)
writel(val, gicd_base + GICD_IGROUP + reg_offset);
// 同时需要配置 CPU Interface
val = readl(gicd_base + GICC_CTLR);
val |= GICC_CTLR_ENABLE_GRP0; // 启用 Group 0
writel(val, gicd_base + GICC_CTLR);
}
2. 使用 SMC 调用
从 Linux 调用 Secure World 服务:
// Linux 中的代码
#include <linux/arm-smccc.h>
#define SMC_SETUP_FIQ 0x82000001 // 自定义 SMC 功能号
int imx6ul_set_irq_to_fiq(unsigned int irq, bool enable) {
struct arm_smccc_res res;
// 调用 Secure Monitor Call
arm_smccc_smc(SMC_SETUP_FIQ, irq, enable, 0, 0, 0, 0, 0, &res);
if (res.a0 != 0) {
pr_err("Failed to setup FIQ: %ld\n", res.a0);
return -EINVAL;
}
pr_info("IRQ %u %s FIQ via SMC\n", irq, enable ? "converted to" : "restored from");
return 0;
}
3. ATF 中的 SMC 处理
在 ARM Trusted Firmware 中实现:
// ATF bl31/services/std_svc/fiq_smc.c
#include <lib/smccc.h>
#define SMC_SETUP_FIQ 0x82000001
static uintptr_t fiq_smc_handler(uint32_t smc_fid,
u_register_t x1,
u_register_t x2,
u_register_t x3,
u_register_t x4,
void *cookie,
void *handle,
u_register_t flags)
{
uint32_t irq = (uint32_t)x1;
uint32_t enable = (uint32_t)x2;
uintptr_t gicd_base = get_gicd_base();
switch (smc_fid) {
case SMC_SETUP_FIQ:
if (irq >= 1020) {
SMC_RET1(handle, -1); // Invalid IRQ
}
// 配置 IGROUP 寄存器
uint32_t reg_offset = (irq / 32) * 4;
uint32_t bit_offset = irq % 32;
uint32_t mask = 1U << bit_offset;
uint32_t val;
val = mmio_read_32(gicd_base + GICD_IGROUPR + reg_offset);
if (enable) {
val &= ~mask; // Group 0 (FIQ)
} else {
val |= mask; // Group 1 (IRQ)
}
mmio_write_32(gicd_base + GICD_IGROUPR + reg_offset, val);
SMC_RET1(handle, 0); // Success
default:
SMC_RET1(handle, SMC_UNK);
}
}
DECLARE_RT_SVC(
fiq_svc,
OEN_SIP_START,
OEN_SIP_END,
SMC_TYPE_FAST,
NULL,
fiq_smc_handler
);
4. 修改设备树和固件
需要固件支持:
/ {
firmware {
arm,psci {
compatible = "arm,psci-1.0";
method = "smc";
// 添加自定义 SMC 服务
};
fiq-service {
compatible = "vendor,fiq-smc";
// 声明支持 FIQ 服务
};
};
};
🔧 完整的实现架构
系统架构图
┌─────────────────┐ SMC Call ┌──────────────────┐
│ Linux │ ──────────────→ │ ARM Trusted │
│ (Non-secure) │ │ Firmware (ATF) │
│ │ ←────────────── │ (Secure) │
└─────────────────┘ Return └──────────────────┘
│ │
│ IRQ Handler │ Configure IGROUP
↓ ↓
┌─────────────────┐ ┌──────────────────┐
│ FIQ Handler │ ←─────────────── │ GIC Controller │
│ (Linux) │ FIQ │ (Hardware) │
└─────────────────┘ └──────────────────┘
调用流程
-
初始化阶段:
// Linux 启动时调用 imx6ul_init_fiq_debug();
-
设置 FIQ:
// Linux 调用 SMC imx6ul_set_irq_to_fiq(target_irq, true); ↓ // ATF 处理 SMC,配置 IGROUP setup_fiq_routing(target_irq); ↓ // 硬件开始将该 IRQ 路由为 FIQ
-
FIQ 处理:
// 中断发生时 Hardware IRQ → GIC Group 0 → CPU FIQ → Linux FIQ Handler
📋 总结
FIQ/IRQ 转换确实可能,但需要:
-
硬件支持:
- GIC-400 完全支持 Group 0/1 机制
- ARM Cortex-A 处理器支持 TrustZone
-
固件支持:
- 需要 ARM Trusted Firmware 或类似的 Secure World 代码
- 实现 SMC 服务来配置 IGROUP 寄存器
-
正确的权限:
- Group 设置只能在 EL3(Secure)模式下进行
- Linux 运行在 EL1(Non-secure)模式,无法直接操作
-
系统设计:
- 需要整个系统(固件+内核)协同工作
- 需要定义 SMC 调用接口
为什么说"不支持":
-
Linux 内核限制:
- 运行在 Non-secure 模式,无法直接操作 IGROUP 寄存器
- 标准 Linux 发行版不包含此类支持
-
默认配置:
- 大多数系统将所有中断配置为 Group 1 (IRQ)
- bootloader/固件通常不提供运行时修改接口
-
复杂性:
- 需要修改固件(ATF)和内核
- 增加系统复杂度和安全风险
-
安全考虑:
- FIQ 通常保留给 Secure World 使用
- Group 0 中断属于安全中断
结论
准确的表述应该是:
"GIC-400 硬件完全支持 IRQ 到 FIQ 的转换,但在标准的 Linux 系统中,由于安全模型的限制,无法在运行时动态地将 IRQ 转换为 FIQ,除非有专门的固件支持和 Secure World 协作。"
要实现 IRQ 到 FIQ 的转换,需要:
- 修改 ARM Trusted Firmware 添加 SMC 服务
- 在 Linux 中实现 SMC 调用接口
- 设计安全的权限管理机制
- 充分的测试和验证
这不是 GIC-400 硬件的限制,而是软件架构和安全模型的要求。
GICv1 的 FIQ 支持详解
📋 概述
很多开发者对 GICv1 是否支持 IRQ 到 FIQ 的转换 存在疑问。本文档详细解释 GICv1 对 FIQ 的支持情况,澄清常见误解,并提供实际的检测和实现方案。
🔍 核心问题解答
❓ GICv1 支持 IRQ 作为 FIQ 吗?
答案取决于 GICv1 的具体实现版本:
GIC 版本 | FIQ 支持 | 中断分组 | 说明 |
---|---|---|---|
GICv1 基础版 | ❌ 不支持 | ❌ 无 | 只能产生 IRQ 信号 |
GICv1 + Security Extensions | ✅ 支持 | ✅ 有 | 完整的 FIQ 功能 |
GICv2 | ✅ 标准支持 | ✅ 标准 | 标准化的 FIQ 功能 |
📚 技术背景
1. 基础 GICv1 的限制
基础 GICv1 实现不包含中断分组功能:
// 基础 GICv1 只有这些功能
✅ 中断优先级管理
✅ 中断使能/禁用
✅ IRQ 信号输出
❌ 没有 GICD_IGROUP 寄存器
❌ 没有 Group 0/Group 1 概念
❌ 没有 FIQ 路由功能
架构特点:
- 所有中断都通过单一的 IRQ 信号传递
- 没有中断分组的概念
- 无法将任何中断路由为 FIQ
2. GICv1 + Security Extensions
增加了完整的中断分组功能:
// GICv1 + Security Extensions 新增功能
✅ GICD_IGROUP 寄存器
✅ Group 0 interrupts → FIQ signal
✅ Group 1 interrupts → IRQ signal
✅ Secure/Non-secure 访问控制
✅ 完整的 FIQ 路由能力
功能对比:
传统 GICv1: 所有中断 → IRQ
GICv1 + SEC: Group 0 → FIQ, Group 1 → IRQ
📖 ARM 官方规范说明
GIC 架构规范 2.0 版本明确指出:
"Version 2.0 of the Architecture Specification contains the following changes and additions to version 1.0: A change to the architectural status of interrupt grouping. Interrupt grouping, and the ability to use FIQs to signal Group 0 interrupts, are provided:
- in all GICv2 implementations
- only as part of the optional Security Extensions in GICv1 implementations"
关键理解:
- 中断分组 和 FIQ 功能 在 GICv1 中是 可选的安全扩展
- 中断分组 和 FIQ 功能 在 GICv2 中是 标准功能
- 不是所有 GICv1 实现都包含 Security Extensions
🔧 实际检测方法
1. 检查 Security Extensions 支持
使用我们提供的检测模块:
# 运行 GIC 检测
sudo ./test_gic_capability.sh
# 或手动检查
sudo insmod gic_capability_check.ko
cat /proc/gic_capability
2. 寄存器级检测
// 读取 GICD_TYPER 寄存器
uint32_t gic_typer = readl(GICD_BASE + GICD_TYPER);
// 检查 Security Extensions (bit 10)
bool has_security_ext = !!(gic_typer & (1 << 10));
if (!has_security_ext) {
printk("GICv1 without Security Extensions - FIQ not supported\n");
return -ENOTSUP;
}
// 检查 GICD_IGROUP 寄存器是否存在
void __iomem *igroup_reg = GICD_BASE + GICD_IGROUP(0);
uint32_t igroup_val = readl(igroup_reg);
// 如果能读取到有意义的值,说明支持中断分组
3. 设备树检查
# 检查设备树中的 GIC 信息
cat /proc/device-tree/interrupt-controller*/compatible
# 常见的 GICv1 兼容字符串
# "arm,cortex-a9-gic" - 通常包含 Security Extensions
# "arm,cortex-a7-gic" - 通常包含 Security Extensions
# "arm,pl390" - 基础 GICv1,可能不包含 Security Extensions
🎯 不同平台的实际情况
常见 ARM Cortex-A 平台
平台 | GIC 版本 | Security Ext | FIQ 支持 |
---|---|---|---|
Cortex-A9 | GICv1 | ✅ 通常有 | ✅ 支持 |
Cortex-A7 | GICv1/v2 | ✅ 通常有 | ✅ 支持 |
Cortex-A15 | GICv2 | ✅ 标准 | ✅ 支持 |
Cortex-A53/57 | GICv2/v3 | ✅ 标准 | ✅ 支持 |
IMX 系列平台
# IMX6 系列 (Cortex-A9/A7)
IMX6Q/D/DL/S: GICv1 + Security Extensions ✅
IMX6SX/SL: GICv1 + Security Extensions ✅
IMX6UL/ULL: GICv1 + Security Extensions ✅
# IMX7 系列 (Cortex-A7)
IMX7D/S: GICv2 标准支持 ✅
# IMX8 系列 (Cortex-A53/A72)
IMX8M 系列: GICv3 ✅
⚙️ 实现 FIQ 转换的条件
硬件条件
- ✅ GICv1 + Security Extensions 或更高版本
- ✅ ARM Cortex-A 处理器 支持 FIQ 模式
- ✅ TrustZone 支持 (对于安全相关功能)
软件条件
- ✅ ARM Trusted Firmware (ATF) 支持
- ✅ SMC 调用接口 实现
- ✅ Secure World 配置 GICD_IGROUP 寄存器
- ✅ Linux FIQ 处理程序 实现
🚫 为什么有人说"不支持"?
常见误解来源
-
混淆了不同版本
- 基础 GICv1 确实不支持 FIQ
- 但 GICv1 + Security Extensions 完全支持
-
Linux 的限制
- Linux 运行在 Non-secure 模式
- 无法直接访问 GICD_IGROUP 寄存器
- 需要通过 SMC 调用 Secure World
-
默认配置
- 多数系统默认将所有中断配置为 Group 1 (IRQ)
- Bootloader 通常不提供运行时修改接口
-
复杂性考虑
- 需要修改固件和内核
- 增加系统复杂度
- 安全风险考虑
📋 检测流程图
开始检测 GIC
↓
读取 GICD_TYPER
↓
检查 bit[10] (SecurityExtn)
↓
┌─────────────────┐
│ bit[10] == 0? │
└─────────────────┘
↓ Yes ↓ No
❌ 基础 GICv1 ✅ GICv1 + Security Ext
不支持 FIQ 支持 FIQ
↓ ↓
建议升级到 GICv2 检查 GICD_IGROUP 访问权限
↓ ↓
结束 确认 FIQ 实现方案
🛠️ 实际代码示例
检测代码
#include <linux/io.h>
#define GICD_TYPER 0x004
#define GICD_IGROUP(n) (0x080 + ((n) * 4))
int check_gicv1_fiq_support(void __iomem *gicd_base)
{
u32 typer, igroup;
bool has_security_ext;
/* 读取 GICD_TYPER */
typer = readl_relaxed(gicd_base + GICD_TYPER);
/* 检查 Security Extensions */
has_security_ext = !!(typer & (1 << 10));
if (!has_security_ext) {
pr_warn("GICv1 without Security Extensions - FIQ not supported\n");
return -ENOTSUP;
}
/* 尝试访问 GICD_IGROUP */
igroup = readl_relaxed(gicd_base + GICD_IGROUP(0));
pr_info("GICv1 with Security Extensions - FIQ supported\n");
pr_info("GICD_IGROUP[0] = 0x%08x\n", igroup);
return 0;
}
SMC 调用示例
// Linux 中通过 SMC 调用配置 FIQ
#include <linux/arm-smccc.h>
#define SMC_GIC_SET_GROUP 0x82000001
int set_irq_to_fiq_via_smc(unsigned int irq, bool enable)
{
struct arm_smccc_res res;
arm_smccc_smc(SMC_GIC_SET_GROUP, irq, enable ? 0 : 1, 0, 0, 0, 0, 0, &res);
if (res.a0 != 0) {
pr_err("SMC call failed: %ld\n", res.a0);
return -EIO;
}
return 0;
}
📊 总结对比表
特性 | 基础 GICv1 | GICv1 + Security Ext | GICv2 |
---|---|---|---|
中断分组 | ❌ | ✅ | ✅ |
FIQ 路由 | ❌ | ✅ | ✅ |
GICD_IGROUP | ❌ | ✅ | ✅ |
实现复杂度 | 简单 | 中等 | 标准 |
安全功能 | 无 | 完整 | 完整 |
向后兼容 | - | ✅ | ✅ |
🎯 建议
对于 GICv1 用户
-
首先检测版本
sudo ./test_gic_capability.sh
-
如果是基础 GICv1
- 考虑硬件升级到支持 Security Extensions 的版本
- 或者升级到 GICv2 平台
-
如果是 GICv1 + Security Extensions
- 完全可以实现 FIQ 功能
- 需要 ATF 和 SMC 接口支持
- 参考本项目的实现代码
对于新项目
- 推荐使用 GICv2 或更高版本
- GICv2 标准化了中断分组功能
- 更好的软件生态支持
🔗 相关资源
- ARM GIC Architecture Specification v2.0
- ARMv7-A Architecture Reference Manual
✅ 结论
GICv1 + Security Extensions 完全支持 IRQ 到 FIQ 的转换,功能上与 GICv2 相同。关键是要区分:
- 基础 GICv1: ❌ 不支持 FIQ
- GICv1 + Security Extensions: ✅ 完全支持 FIQ
- GICv2+: ✅ 标准支持 FIQ
使用本项目提供的检测工具可以准确判断你的平台是否支持 FIQ 功能。