01 · QUESTION
무엇을 증명할 것인가
`/proc/interrupts`가 증가하지 않을 때 driver handler부터 의심하지 않고, 신호선·trigger type·GIC pending·CPU interface·irqdomain 중 처음 끊긴 경계를 어떻게 찾을 것인가?
주변장치의 interrupt 출력이 GIC Distributor와 Redistributor를 지나 CPU의 IRQ exception이 되고, Linux irqdomain을 통해 device driver handler로 전달되는 전 과정을 추적합니다. 이 글은 Linux 로그를 출발점으로 삼지 않습니다. 같은 사건이 보드, controller, firmware, kernel에서 어떤 이름과 번호로 바뀌는지 아래에서 위로 연결합니다.
02 · CONTRACT
입력과 완료 조건
| 구분 | 확인할 내용 |
|---|---|
| 입력 | 주변장치가 만든 level 또는 edge interrupt와 올바른 SPI/PPI 번호, 전원이 켜진 GICD/GICR |
| 출력 | 대상 PE가 INTID를 acknowledge하고 Linux가 등록된 irqaction handler를 실행한 뒤 interrupt가 해제됨 |
| 소유권 | 장치 status는 device driver, SPI routing과 priority는 GIC, virq와 handler 연결은 Linux irqdomain/IRQ core가 소유 |
| 완료 조건 | 장치 원인이 제거되고 EOIR/DIR 처리가 끝나 동일 원인으로 pending이 즉시 재발하지 않음 |
03 · BOTTOM-UP
완전히 아래에서 시작하는 8단계
장치 interrupt 핀의 전압 도메인, pull, polarity, edge/level 특성을 회로도와 datasheet에서 확정합니다. Shared line이면 모든 장치가 deassert할 조건도 적습니다.
SPI의 group, enable, priority, trigger, target affinity를 GICD 레지스터에서 확인합니다. GICD_ISPENDR만 보고 끝내지 말고 ICFGR와 IROUTER가 실제 wiring 의도와 같은지 함께 봅니다.
각 PE의 GICR_WAKER.ChildrenAsleep을 기다리고 SGI/PPI enable, priority, group을 per-CPU Redistributor frame에서 설정합니다. CPU hotplug 때 이 상태는 다시 초기화됩니다.
ICC_SRE_EL1로 system register interface를 사용하고 ICC_PMR_EL1, ICC_BPR1_EL1, ICC_IGRPEN1_EL1이 interrupt를 허용하는지 확인합니다. PSTATE.I도 unmask되어야 합니다.
IRQ exception 진입 후 ICC_IAR1_EL1을 읽으면 가장 높은 우선순위의 INTID가 active 상태가 됩니다. 1020~1023은 일반 장치 IRQ가 아니라 special/spurious ID입니다.
Device Tree의 interrupt-parent와 `<type number flags>`가 GIC binding에 맞아야 합니다. SPI/PPI type 숫자와 Linux hwirq 변환을 섞으면 정상 핀도 다른 INTID에 연결됩니다.
GIC irqdomain이 fwspec을 hwirq로 번역하고 virq를 할당합니다. gic_handle_irq()가 INTID를 읽어 generic_handle_domain_irq()로 넘기면 IRQ core가 irq_desc와 irqaction을 찾습니다.
level interrupt는 handler가 장치 status를 clear한 뒤 EOI해야 합니다. 원인을 지우지 않으면 EOI 직후 다시 pending되어 interrupt storm이 생기므로 장치 register와 GIC state를 한 쌍으로 검증합니다.
1. Board signal
장치 interrupt 핀의 전압 도메인, pull, polarity, edge/level 특성을 회로도와 datasheet에서 확정합니다. Shared line이면 모든 장치가 deassert할 조건도 적습니다.
이 단계의 통과 증거를 저장한 뒤에만 다음 단계인 2. GIC Distributor로 이동합니다.
2. GIC Distributor
SPI의 group, enable, priority, trigger, target affinity를 GICD 레지스터에서 확인합니다. GICD_ISPENDR만 보고 끝내지 말고 ICFGR와 IROUTER가 실제 wiring 의도와 같은지 함께 봅니다.
이 단계의 통과 증거를 저장한 뒤에만 다음 단계인 3. Redistributor로 이동합니다.
3. Redistributor
각 PE의 GICR_WAKER.ChildrenAsleep을 기다리고 SGI/PPI enable, priority, group을 per-CPU Redistributor frame에서 설정합니다. CPU hotplug 때 이 상태는 다시 초기화됩니다.
이 단계의 통과 증거를 저장한 뒤에만 다음 단계인 4. CPU interface로 이동합니다.
4. CPU interface
ICC_SRE_EL1로 system register interface를 사용하고 ICC_PMR_EL1, ICC_BPR1_EL1, ICC_IGRPEN1_EL1이 interrupt를 허용하는지 확인합니다. PSTATE.I도 unmask되어야 합니다.
이 단계의 통과 증거를 저장한 뒤에만 다음 단계인 5. Acknowledge로 이동합니다.
5. Acknowledge
IRQ exception 진입 후 ICC_IAR1_EL1을 읽으면 가장 높은 우선순위의 INTID가 active 상태가 됩니다. 1020~1023은 일반 장치 IRQ가 아니라 special/spurious ID입니다.
이 단계의 통과 증거를 저장한 뒤에만 다음 단계인 6. Firmware description로 이동합니다.
6. Firmware description
Device Tree의 interrupt-parent와 `<type number flags>`가 GIC binding에 맞아야 합니다. SPI/PPI type 숫자와 Linux hwirq 변환을 섞으면 정상 핀도 다른 INTID에 연결됩니다.
이 단계의 통과 증거를 저장한 뒤에만 다음 단계인 7. Linux irqdomain로 이동합니다.
7. Linux irqdomain
GIC irqdomain이 fwspec을 hwirq로 번역하고 virq를 할당합니다. gic_handle_irq()가 INTID를 읽어 generic_handle_domain_irq()로 넘기면 IRQ core가 irq_desc와 irqaction을 찾습니다.
이 단계의 통과 증거를 저장한 뒤에만 다음 단계인 8. Driver completion로 이동합니다.
8. Driver completion
level interrupt는 handler가 장치 status를 clear한 뒤 EOI해야 합니다. 원인을 지우지 않으면 EOI 직후 다시 pending되어 interrupt storm이 생기므로 장치 register와 GIC state를 한 쌍으로 검증합니다.
이 단계의 통과 증거를 저장한 뒤에만 최종 사용자 결과를 판정합니다.
04 · DIAGRAMS
주소와 상태가 이동하는 모습
주변장치가 만든 level 또는 edge interrupt와 올바른 SPI/PPI 번호, 전원이 켜진 GICD/GICR
장치 status는 device driver, SPI routing과 priority는 GIC, virq와 handler 연결은 Linux irqdomain/IRQ core가 소유
대상 PE가 INTID를 acknowledge하고 Linux가 등록된 irqaction handler를 실행한 뒤 interrupt가 해제됨
장치 원인이 제거되고 EOIR/DIR 처리가 끝나 동일 원인으로 pending이 즉시 재발하지 않음
05 · DETAILED MANUAL
GICv3의 pin에서 irq_desc까지 이어 읽는 상세 매뉴얼
SPI/PPI/SGI/LPI 번호 공간, Distributor, Redistributor, ICC_* system register CPU interface와 ITS를 분리합니다. 장치 status에서 시작해 GIC pending/active, INTID acknowledge, irqdomain mapping, Linux handler와 EOI까지 같은 interrupt 사건을 추적합니다.
공개적으로 다시 확인할 수 있는 자료
공개되지 않은 vendor register나 integration별 offset을 추측해서 채우지 않았습니다. 아래 제조사 자료, architecture 문서와 Linux v6.18.37 원본에서 다시 확인할 수 있는 범위만 사용했습니다.
Arm GICv3/v4 overview
GICD, GICR, CPU interface, interrupt state와 routing
Linux GICv3 driver
GIC 초기화, irqdomain, handle_irq와 affinity
Linux GICv3 ITS driver
LPI와 MSI DeviceID/EventID translation
Linux IRQ domain
firmware hwirq와 Linux virq mapping
06 · REGISTERS
읽어야 할 상태와 해석
값 하나만 떼어 보지 말고 동일 사건의 enable, status, ownership, completion register를 한 줄에 기록합니다. read-to-clear나 write-one-to-clear 속성은 반드시 datasheet에서 먼저 확인합니다.
| 레지스터/상태 | 소유 블록 | 검증할 의미 |
|---|---|---|
| GICD_CTLR / TYPER | Distributor | group enable, affinity routing, 구현된 INTID 수 |
| GICD_IGROUPR / IGRPMODR | Distributor | Secure/Non-secure group 분류 |
| GICD_ISENABLER / ISPENDR | Distributor | SPI enable과 pending 상태 |
| GICD_ICFGR / IROUTER | Distributor | edge/level 설정과 PE affinity route |
| GICR_WAKER / ISENABLER0 | Redistributor | PE sleep 상태와 SGI/PPI enable |
| ICC_PMR_EL1 / BPR1_EL1 | CPU interface | priority mask와 preemption grouping |
| ICC_IAR1_EL1 / EOIR1_EL1 / DIR_EL1 | CPU interface | acknowledge, priority drop, deactivate |
| GITS_* tables | ITS | MSI DeviceID/EventID를 LPI와 collection으로 변환 |
07 · LINUX SOURCE
Linux v6.18.37 원본 코드와 줄별 해설
라인 번호를 고정해 외우는 대신 함수 선언을 기준으로 Linux v6.18.37 tree에서 다시 찾았습니다. 로컬 tree에 있는 파일은 실제 코드를 직접 싣고, 나머지는 원본 파일과 읽을 함수 좌표를 연결했습니다.
Linux v6.18.37 · 실제 원본 코드
IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler
drivers/irqchip/irq-gic-v3.c:956-1016
956 gic_pmr_mask_irqs();
957 isb();
958 irqnr = gic_read_iar();
959 gic_write_pmr(pmr);
960
961 __gic_handle_nmi(irqnr, regs);
962}
963
964static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
965{
966 if (unlikely(gic_supports_nmi() && !interrupts_enabled(regs)))
967 __gic_handle_irq_from_irqsoff(regs);
968 else
969 __gic_handle_irq_from_irqson(regs);
970}
971
972static void __init gic_dist_init(void)
973{
974 unsigned int i;
975 u64 affinity;
976 void __iomem *base = gic_data.dist_base;
977 u32 val;
978
979 /* Disable the distributor */
980 writel_relaxed(0, base + GICD_CTLR);
981 gic_dist_wait_for_rwp();
982
983 /*
984 * Configure SPIs as non-secure Group-1. This will only matter
985 * if the GIC only has a single security state. This will not
986 * do the right thing if the kernel is running in secure mode,
987 * but that's not the intended use case anyway.
988 */
989 for (i = 32; i < GIC_LINE_NR; i += 32)
990 writel_relaxed(~0, base + GICD_IGROUPR + i / 8);
991
992 /* Extended SPI range, not handled by the GICv2/GICv3 common code */
993 for (i = 0; i < GIC_ESPI_NR; i += 32) {
994 writel_relaxed(~0U, base + GICD_ICENABLERnE + i / 8);
995 writel_relaxed(~0U, base + GICD_ICACTIVERnE + i / 8);
996 }
997
998 for (i = 0; i < GIC_ESPI_NR; i += 32)
999 writel_relaxed(~0U, base + GICD_IGROUPRnE + i / 8);
1000
1001 for (i = 0; i < GIC_ESPI_NR; i += 16)
1002 writel_relaxed(0, base + GICD_ICFGRnE + i / 4);
1003
1004 for (i = 0; i < GIC_ESPI_NR; i += 4)
1005 writel_relaxed(REPEAT_BYTE_U32(dist_prio_irq),
1006 base + GICD_IPRIORITYRnE + i);
1007
1008 /* Now do the common stuff */
1009 gic_dist_config(base, GIC_LINE_NR, dist_prio_irq);
1010
1011 val = GICD_CTLR_ARE_NS | GICD_CTLR_ENABLE_G1A | GICD_CTLR_ENABLE_G1;
1012 if (gic_data.rdists.gicd_typer2 & GICD_TYPER2_nASSGIcap) {
1013 pr_info("Enabling SGIs without active state\n");
1014 val |= GICD_CTLR_nASSGIreq;
1015 }
1016 gic_pmr_mask_irqs();
IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
isb();
IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
irqnr = gic_read_iar();
공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.
gic_write_pmr(pmr);
hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
__gic_handle_nmi(irqnr, regs);
IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
}
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 입력 계약을 만족하는지 확인합니다.
{
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
if (unlikely(gic_supports_nmi() && !interrupts_enabled(regs)))
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
__gic_handle_irq_from_irqsoff(regs);
IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
else
IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
__gic_handle_irq_from_irqson(regs);
IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
}
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
static void __init gic_dist_init(void)
함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 입력 계약을 만족하는지 확인합니다.
{
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
unsigned int i;
IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
u64 affinity;
IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
void __iomem *base = gic_data.dist_base;
IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
u32 val;
IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
/* Disable the distributor */
원본 주석입니다. IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
writel_relaxed(0, base + GICD_CTLR);
hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.
gic_dist_wait_for_rwp();
IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
/*
원본 주석입니다. IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* Configure SPIs as non-secure Group-1. This will only matter
원본 주석입니다. IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* if the GIC only has a single security state. This will not
원본 주석입니다. IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* do the right thing if the kernel is running in secure mode,
원본 주석입니다. IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
* but that's not the intended use case anyway.
원본 주석입니다. IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
*/
원본 주석입니다. IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
for (i = 32; i < GIC_LINE_NR; i += 32)
여러 pending source 또는 resource를 순회하는 구간입니다. 한 번의 진입에서 처리 가능한 양과 종료 조건이 interrupt latency에 영향을 줍니다.
writel_relaxed(~0, base + GICD_IGROUPR + i / 8);
hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
/* Extended SPI range, not handled by the GICv2/GICv3 common code */
원본 주석입니다. IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
for (i = 0; i < GIC_ESPI_NR; i += 32) {
여러 pending source 또는 resource를 순회하는 구간입니다. 한 번의 진입에서 처리 가능한 양과 종료 조건이 interrupt latency에 영향을 줍니다.
writel_relaxed(~0U, base + GICD_ICENABLERnE + i / 8);
hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.
writel_relaxed(~0U, base + GICD_ICACTIVERnE + i / 8);
hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.
}
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
for (i = 0; i < GIC_ESPI_NR; i += 32)
여러 pending source 또는 resource를 순회하는 구간입니다. 한 번의 진입에서 처리 가능한 양과 종료 조건이 interrupt latency에 영향을 줍니다.
writel_relaxed(~0U, base + GICD_IGROUPRnE + i / 8);
hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
for (i = 0; i < GIC_ESPI_NR; i += 16)
여러 pending source 또는 resource를 순회하는 구간입니다. 한 번의 진입에서 처리 가능한 양과 종료 조건이 interrupt latency에 영향을 줍니다.
writel_relaxed(0, base + GICD_ICFGRnE + i / 4);
hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
for (i = 0; i < GIC_ESPI_NR; i += 4)
여러 pending source 또는 resource를 순회하는 구간입니다. 한 번의 진입에서 처리 가능한 양과 종료 조건이 interrupt latency에 영향을 줍니다.
writel_relaxed(REPEAT_BYTE_U32(dist_prio_irq),
hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.
base + GICD_IPRIORITYRnE + i);
IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
/* Now do the common stuff */
원본 주석입니다. IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
gic_dist_config(base, GIC_LINE_NR, dist_prio_irq);
IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
val = GICD_CTLR_ARE_NS | GICD_CTLR_ENABLE_G1A | GICD_CTLR_ENABLE_G1;
IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
if (gic_data.rdists.gicd_typer2 & GICD_TYPER2_nASSGIcap) {
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
pr_info("Enabling SGIs without active state\n");
IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
val |= GICD_CTLR_nASSGIreq;
IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
}
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
Linux v6.18.37 · 실제 원본 코드
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map
drivers/irqchip/irq-gic-v3.c:1588-1644
1588 .irq_nmi_setup = gic_irq_nmi_setup,
1589 .irq_nmi_teardown = gic_irq_nmi_teardown,
1590 .ipi_send_mask = gic_ipi_send_mask,
1591 .flags = IRQCHIP_SET_TYPE_MASKED |
1592 IRQCHIP_SKIP_SET_WAKE |
1593 IRQCHIP_MASK_ON_SUSPEND,
1594};
1595
1596static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
1597 irq_hw_number_t hw)
1598{
1599 struct irq_chip *chip = &gic_chip;
1600 struct irq_data *irqd = irq_desc_get_irq_data(irq_to_desc(irq));
1601
1602 if (static_branch_likely(&supports_deactivate_key))
1603 chip = &gic_eoimode1_chip;
1604
1605 switch (__get_intid_range(hw)) {
1606 case SGI_RANGE:
1607 case PPI_RANGE:
1608 case EPPI_RANGE:
1609 irq_set_percpu_devid(irq);
1610 irq_domain_set_info(d, irq, hw, chip, d->host_data,
1611 handle_percpu_devid_irq, NULL, NULL);
1612 break;
1613
1614 case SPI_RANGE:
1615 case ESPI_RANGE:
1616 irq_domain_set_info(d, irq, hw, chip, d->host_data,
1617 handle_fasteoi_irq, NULL, NULL);
1618 irq_set_probe(irq);
1619 irqd_set_single_target(irqd);
1620 break;
1621
1622 case LPI_RANGE:
1623 if (!gic_dist_supports_lpis())
1624 return -EPERM;
1625 irq_domain_set_info(d, irq, hw, chip, d->host_data,
1626 handle_fasteoi_irq, NULL, NULL);
1627 break;
1628
1629 default:
1630 return -EPERM;
1631 }
1632
1633 /* Prevents SW retriggers which mess up the ACK/EOI ordering */
1634 irqd_set_handle_enforce_irqctx(irqd);
1635 return 0;
1636}
1637
1638static int gic_irq_domain_translate(struct irq_domain *d,
1639 struct irq_fwspec *fwspec,
1640 unsigned long *hwirq,
1641 unsigned int *type)
1642{
1643 if (fwspec->param_count == 1 && fwspec->param[0] < 16) {
1644 *hwirq = fwspec->param[0];.irq_nmi_setup = gic_irq_nmi_setup,
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
.irq_nmi_teardown = gic_irq_nmi_teardown,
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
.ipi_send_mask = gic_ipi_send_mask,
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
.flags = IRQCHIP_SET_TYPE_MASKED |
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
IRQCHIP_SKIP_SET_WAKE |
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
IRQCHIP_MASK_ON_SUSPEND,
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
};
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 입력 계약을 만족하는지 확인합니다.
irq_hw_number_t hw)
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
{
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
struct irq_chip *chip = &gic_chip;
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
struct irq_data *irqd = irq_desc_get_irq_data(irq_to_desc(irq));
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
if (static_branch_likely(&supports_deactivate_key))
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
chip = &gic_eoimode1_chip;
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
switch (__get_intid_range(hw)) {
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
case SGI_RANGE:
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
case PPI_RANGE:
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
case EPPI_RANGE:
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
irq_set_percpu_devid(irq);
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
irq_domain_set_info(d, irq, hw, chip, d->host_data,
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
handle_percpu_devid_irq, NULL, NULL);
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
break;
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
case SPI_RANGE:
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
case ESPI_RANGE:
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
irq_domain_set_info(d, irq, hw, chip, d->host_data,
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
handle_fasteoi_irq, NULL, NULL);
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
irq_set_probe(irq);
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
irqd_set_single_target(irqd);
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
break;
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
case LPI_RANGE:
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
if (!gic_dist_supports_lpis())
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
return -EPERM;
호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.
irq_domain_set_info(d, irq, hw, chip, d->host_data,
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
handle_fasteoi_irq, NULL, NULL);
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
break;
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
default:
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
return -EPERM;
호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.
}
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
/* Prevents SW retriggers which mess up the ACK/EOI ordering */
원본 주석입니다. GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
irqd_set_handle_enforce_irqctx(irqd);
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
return 0;
호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.
}
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
(blank)
논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.
static int gic_irq_domain_translate(struct irq_domain *d,
함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 입력 계약을 만족하는지 확인합니다.
struct irq_fwspec *fwspec,
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
unsigned long *hwirq,
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
unsigned int *type)
GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.
{
제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.
if (fwspec->param_count == 1 && fwspec->param[0] < 16) {
상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.
*hwirq = fwspec->param[0];
원본 주석입니다. GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.
Linux v6.18.37 · 원본 좌표
Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
firmware가 GIC topology와 interrupt specifier를 기술하는 binding
- 읽을 함수
interrupt-cells- 읽을 함수
redistributor-regions- 읽을 함수
msi-controller
08 · VERIFICATION
실제 장비에서 확인하는 순서
- Pin
실행 오실로스코프나 logic analyzer로 장치 사건 전후 interrupt 핀을 측정합니다.
통과 기준 예상 polarity로 assert되고 level형이면 status clear까지 유지되어야 합니다.
- Device status
실행 장치의 raw/masked interrupt status와 mask register를 같은 시점에 읽습니다.
통과 기준 raw 원인 bit와 출력 mask 조합이 실제 핀 상태를 설명해야 합니다.
- GIC state
실행 해당 INTID의 enable, pending, active, ICFGR, IROUTER를 debugger 또는 안전한 monitor에서 기록합니다.
통과 기준 핀 assert 뒤 pending이 생기고 acknowledge 뒤 active로 이동해야 합니다.
- CPU entry
실행 trace_irq_handler_entry, function_graph 또는 exception entry counter를 사용합니다.
통과 기준 CPU가 INTID를 받은 횟수와 `/proc/interrupts` 증가량이 부하 조건까지 대응해야 합니다.
- Completion
실행 장치 clear 전후와 EOIR 전후를 구분해 재발 시간을 측정합니다.
통과 기준 level 원인이 먼저 제거되고 EOI 뒤 pending이 남지 않아야 합니다.
09 · FAILURE MATRIX
처음 끊긴 경계로 원인을 좁히기
| 관측 결과 | 우선 의심할 경계 | 다음 증거 |
|---|---|---|
| 핀 변화 없음 | 장치 기능, power/reset, pad mux, mask 문제 | 장치 raw status와 실제 핀 |
| 핀 assert, GIC pending 없음 | SPI 번호, 전압/배선, polarity/trigger 불일치 | 회로도와 GICD_ISPENDR/ICFGR |
| GIC pending, CPU entry 없음 | IROUTER affinity, PMR, group enable, PSTATE.I 문제 | GICD_IROUTER와 ICC_* |
| CPU entry, handler 없음 | DT specifier 또는 irqdomain mapping 오류 | hwirq/virq와 irq_desc |
| handler 반복 폭주 | level 원인 미해제, 잘못된 EOI 순서, shared IRQ 오판 | 장치 status와 GIC active/pending |
핵심은 마지막 오류 메시지가 아니라 처음 기대값과 달라진 checkpoint입니다. 그보다 아래의 통과 증거와 그 지점의 실패 증거를 한 묶음으로 남겨야 수정의 효과도 검증할 수 있습니다.