← HardwareDUJINLABS.COM

Hardware 05 · Interrupt · Linux v6.18.37

Arm GICv3: 핀 입력에서 Linux IRQ handler까지

주변장치의 interrupt 출력이 GIC Distributor와 Redistributor를 지나 CPU의 IRQ exception이 되고, Linux irqdomain을 통해 device driver handler로 전달되는 전 과정을 추적합니다.

검증 깊이
board → Linux 8 layers
그림
4 boundary diagrams
원본 코드
2 excerpts · 118 lines
기준
Linux v6.18.37

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단계

그림 1. 아래에서 Linux까지 올라오는 8개 검증 경계위 계층의 로그보다 아래 계층의 물리 증거를 먼저 확정합니다.
01
1. Board signal

장치 interrupt 핀의 전압 도메인, pull, polarity, edge/level 특성을 회로도와 datasheet에서 확정합니다. Shared line이면 모든 장치가 deassert할 조건도 적습니다.

START
02
2. GIC Distributor

SPI의 group, enable, priority, trigger, target affinity를 GICD 레지스터에서 확인합니다. GICD_ISPENDR만 보고 끝내지 말고 ICFGR와 IROUTER가 실제 wiring 의도와 같은지 함께 봅니다.

CHECKPOINT
03
3. Redistributor

각 PE의 GICR_WAKER.ChildrenAsleep을 기다리고 SGI/PPI enable, priority, group을 per-CPU Redistributor frame에서 설정합니다. CPU hotplug 때 이 상태는 다시 초기화됩니다.

CHECKPOINT
04
4. CPU interface

ICC_SRE_EL1로 system register interface를 사용하고 ICC_PMR_EL1, ICC_BPR1_EL1, ICC_IGRPEN1_EL1이 interrupt를 허용하는지 확인합니다. PSTATE.I도 unmask되어야 합니다.

CHECKPOINT
05
5. Acknowledge

IRQ exception 진입 후 ICC_IAR1_EL1을 읽으면 가장 높은 우선순위의 INTID가 active 상태가 됩니다. 1020~1023은 일반 장치 IRQ가 아니라 special/spurious ID입니다.

CHECKPOINT
06
6. Firmware description

Device Tree의 interrupt-parent와 `<type number flags>`가 GIC binding에 맞아야 합니다. SPI/PPI type 숫자와 Linux hwirq 변환을 섞으면 정상 핀도 다른 INTID에 연결됩니다.

CHECKPOINT
07
7. Linux irqdomain

GIC irqdomain이 fwspec을 hwirq로 번역하고 virq를 할당합니다. gic_handle_irq()가 INTID를 읽어 generic_handle_domain_irq()로 넘기면 IRQ core가 irq_desc와 irqaction을 찾습니다.

CHECKPOINT
08
8. Driver completion

level interrupt는 handler가 장치 status를 clear한 뒤 EOI해야 합니다. 원인을 지우지 않으면 EOI 직후 다시 pending되어 interrupt storm이 생기므로 장치 register와 GIC state를 한 쌍으로 검증합니다.

LINUX / RESULT
01

1. Board signal

장치 interrupt 핀의 전압 도메인, pull, polarity, edge/level 특성을 회로도와 datasheet에서 확정합니다. Shared line이면 모든 장치가 deassert할 조건도 적습니다.

이 단계의 통과 증거를 저장한 뒤에만 다음 단계인 2. GIC Distributor로 이동합니다.

02

2. GIC Distributor

SPI의 group, enable, priority, trigger, target affinity를 GICD 레지스터에서 확인합니다. GICD_ISPENDR만 보고 끝내지 말고 ICFGR와 IROUTER가 실제 wiring 의도와 같은지 함께 봅니다.

이 단계의 통과 증거를 저장한 뒤에만 다음 단계인 3. Redistributor로 이동합니다.

03

3. Redistributor

각 PE의 GICR_WAKER.ChildrenAsleep을 기다리고 SGI/PPI enable, priority, group을 per-CPU Redistributor frame에서 설정합니다. CPU hotplug 때 이 상태는 다시 초기화됩니다.

이 단계의 통과 증거를 저장한 뒤에만 다음 단계인 4. CPU interface로 이동합니다.

04

4. CPU interface

ICC_SRE_EL1로 system register interface를 사용하고 ICC_PMR_EL1, ICC_BPR1_EL1, ICC_IGRPEN1_EL1이 interrupt를 허용하는지 확인합니다. PSTATE.I도 unmask되어야 합니다.

이 단계의 통과 증거를 저장한 뒤에만 다음 단계인 5. Acknowledge로 이동합니다.

05

5. Acknowledge

IRQ exception 진입 후 ICC_IAR1_EL1을 읽으면 가장 높은 우선순위의 INTID가 active 상태가 됩니다. 1020~1023은 일반 장치 IRQ가 아니라 special/spurious ID입니다.

이 단계의 통과 증거를 저장한 뒤에만 다음 단계인 6. Firmware description로 이동합니다.

06

6. Firmware description

Device Tree의 interrupt-parent와 `<type number flags>`가 GIC binding에 맞아야 합니다. SPI/PPI type 숫자와 Linux hwirq 변환을 섞으면 정상 핀도 다른 INTID에 연결됩니다.

이 단계의 통과 증거를 저장한 뒤에만 다음 단계인 7. Linux irqdomain로 이동합니다.

07

7. Linux irqdomain

GIC irqdomain이 fwspec을 hwirq로 번역하고 virq를 할당합니다. gic_handle_irq()가 INTID를 읽어 generic_handle_domain_irq()로 넘기면 IRQ core가 irq_desc와 irqaction을 찾습니다.

이 단계의 통과 증거를 저장한 뒤에만 다음 단계인 8. Driver completion로 이동합니다.

08

8. Driver completion

level interrupt는 handler가 장치 status를 clear한 뒤 EOI해야 합니다. 원인을 지우지 않으면 EOI 직후 다시 pending되어 interrupt storm이 생기므로 장치 register와 GIC state를 한 쌍으로 검증합니다.

이 단계의 통과 증거를 저장한 뒤에만 최종 사용자 결과를 판정합니다.

04 · DIAGRAMS

주소와 상태가 이동하는 모습

그림 2. Interrupt 실행 경로왼쪽에서 오른쪽으로 실제 소유권과 관찰 지점이 이동합니다.
01 Device status=1
02 IRQ pin assert
03 GICD pending
04 priority/route
05 ICC_IAR1_EL1
06 irqdomain
07 driver handler
08 status clear+EOI
그림 3. firmware와 Linux 번호 공간의 연결왼쪽에서 오른쪽으로 실제 소유권과 관찰 지점이 이동합니다.
01 DT interrupt specifier
02 gic_irq_domain_translate
03 hwirq
04 virq/irq_desc
05 request_irq
06 irqaction
07 runtime dispatch
그림 4. 입력, 소유권, 완료 조건의 경계완료 조건을 충족하기 전에는 다음 계층이 해당 자원을 재사용하면 안 됩니다.
INPUT

주변장치가 만든 level 또는 edge interrupt와 올바른 SPI/PPI 번호, 전원이 켜진 GICD/GICR

OWNER

장치 status는 device driver, SPI routing과 priority는 GIC, virq와 handler 연결은 Linux irqdomain/IRQ core가 소유

OUTPUT

대상 PE가 INTID를 acknowledge하고 Linux가 등록된 irqaction handler를 실행한 뒤 interrupt가 해제됨

DONE

장치 원인이 제거되고 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 원본에서 다시 확인할 수 있는 범위만 사용했습니다.

06 · REGISTERS

읽어야 할 상태와 해석

값 하나만 떼어 보지 말고 동일 사건의 enable, status, ownership, completion register를 한 줄에 기록합니다. read-to-clear나 write-one-to-clear 속성은 반드시 datasheet에서 먼저 확인합니다.

레지스터/상태소유 블록검증할 의미
GICD_CTLR / TYPERDistributorgroup enable, affinity routing, 구현된 INTID 수
GICD_IGROUPR / IGRPMODRDistributorSecure/Non-secure group 분류
GICD_ISENABLER / ISPENDRDistributorSPI enable과 pending 상태
GICD_ICFGR / IROUTERDistributoredge/level 설정과 PE affinity route
GICR_WAKER / ISENABLER0RedistributorPE sleep 상태와 SGI/PPI enable
ICC_PMR_EL1 / BPR1_EL1CPU interfacepriority mask와 preemption grouping
ICC_IAR1_EL1 / EOIR1_EL1 / DIR_EL1CPU interfaceacknowledge, priority drop, deactivate
GITS_* tablesITSMSI 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 
L956 gic_pmr_mask_irqs();

IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L957 isb();

IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L958 irqnr = gic_read_iar();

공유 상태나 MMIO를 읽습니다. 이 값은 단순 변수라기보다 hardware가 갱신한 시점과 read ordering을 포함해 해석해야 합니다.

L959 gic_write_pmr(pmr);

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L960 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L961 __gic_handle_nmi(irqnr, regs);

IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L962 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L963 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L964 static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 입력 계약을 만족하는지 확인합니다.

L965 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L966 if (unlikely(gic_supports_nmi() && !interrupts_enabled(regs)))

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L967 __gic_handle_irq_from_irqsoff(regs);

IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L968 else

IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L969 __gic_handle_irq_from_irqson(regs);

IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L970 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L971 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L972 static void __init gic_dist_init(void)

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 입력 계약을 만족하는지 확인합니다.

L973 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L974 unsigned int i;

IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L975 u64 affinity;

IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L976 void __iomem *base = gic_data.dist_base;

IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L977 u32 val;

IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L978 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L979 /* Disable the distributor */

원본 주석입니다. IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L980 writel_relaxed(0, base + GICD_CTLR);

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L981 gic_dist_wait_for_rwp();

IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L982 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L983 /*

원본 주석입니다. IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L984 * Configure SPIs as non-secure Group-1. This will only matter

원본 주석입니다. IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L985 * if the GIC only has a single security state. This will not

원본 주석입니다. IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L986 * do the right thing if the kernel is running in secure mode,

원본 주석입니다. IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L987 * but that's not the intended use case anyway.

원본 주석입니다. IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L988 */

원본 주석입니다. IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L989 for (i = 32; i < GIC_LINE_NR; i += 32)

여러 pending source 또는 resource를 순회하는 구간입니다. 한 번의 진입에서 처리 가능한 양과 종료 조건이 interrupt latency에 영향을 줍니다.

L990 writel_relaxed(~0, base + GICD_IGROUPR + i / 8);

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L991 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L992 /* Extended SPI range, not handled by the GICv2/GICv3 common code */

원본 주석입니다. IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L993 for (i = 0; i < GIC_ESPI_NR; i += 32) {

여러 pending source 또는 resource를 순회하는 구간입니다. 한 번의 진입에서 처리 가능한 양과 종료 조건이 interrupt latency에 영향을 줍니다.

L994 writel_relaxed(~0U, base + GICD_ICENABLERnE + i / 8);

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L995 writel_relaxed(~0U, base + GICD_ICACTIVERnE + i / 8);

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L996 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L997 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L998 for (i = 0; i < GIC_ESPI_NR; i += 32)

여러 pending source 또는 resource를 순회하는 구간입니다. 한 번의 진입에서 처리 가능한 양과 종료 조건이 interrupt latency에 영향을 줍니다.

L999 writel_relaxed(~0U, base + GICD_IGROUPRnE + i / 8);

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L1000 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1001 for (i = 0; i < GIC_ESPI_NR; i += 16)

여러 pending source 또는 resource를 순회하는 구간입니다. 한 번의 진입에서 처리 가능한 양과 종료 조건이 interrupt latency에 영향을 줍니다.

L1002 writel_relaxed(0, base + GICD_ICFGRnE + i / 4);

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L1003 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1004 for (i = 0; i < GIC_ESPI_NR; i += 4)

여러 pending source 또는 resource를 순회하는 구간입니다. 한 번의 진입에서 처리 가능한 양과 종료 조건이 interrupt latency에 영향을 줍니다.

L1005 writel_relaxed(REPEAT_BYTE_U32(dist_prio_irq),

hardware 또는 공유 메모리에 상태를 게시합니다. write가 완료되는 지점과 다음 read/doorbell 사이의 ordering 조건을 확인합니다.

L1006 base + GICD_IPRIORITYRnE + i);

IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1007 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1008 /* Now do the common stuff */

원본 주석입니다. IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1009 gic_dist_config(base, GIC_LINE_NR, dist_prio_irq);

IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1010 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1011 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와 연결해 확인합니다.

L1012 if (gic_data.rdists.gicd_typer2 & GICD_TYPER2_nASSGIcap) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L1013 pr_info("Enabling SGIs without active state\n");

IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1014 val |= GICD_CTLR_nASSGIreq;

IRQ exception에서 INTID를 읽고 Linux IRQ core로 넘기는 GICv3 최상위 handler의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1015 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L1016 (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];
L1588 .irq_nmi_setup = gic_irq_nmi_setup,

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1589 .irq_nmi_teardown = gic_irq_nmi_teardown,

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1590 .ipi_send_mask = gic_ipi_send_mask,

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1591 .flags = IRQCHIP_SET_TYPE_MASKED |

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1592 IRQCHIP_SKIP_SET_WAKE |

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1593 IRQCHIP_MASK_ON_SUSPEND,

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1594 };

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1595 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1596 static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 입력 계약을 만족하는지 확인합니다.

L1597 irq_hw_number_t hw)

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1598 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L1599 struct irq_chip *chip = &gic_chip;

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1600 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와 연결해 확인합니다.

L1601 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1602 if (static_branch_likely(&supports_deactivate_key))

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L1603 chip = &gic_eoimode1_chip;

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1604 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1605 switch (__get_intid_range(hw)) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L1606 case SGI_RANGE:

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1607 case PPI_RANGE:

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1608 case EPPI_RANGE:

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1609 irq_set_percpu_devid(irq);

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1610 irq_domain_set_info(d, irq, hw, chip, d->host_data,

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1611 handle_percpu_devid_irq, NULL, NULL);

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1612 break;

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1613 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1614 case SPI_RANGE:

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1615 case ESPI_RANGE:

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1616 irq_domain_set_info(d, irq, hw, chip, d->host_data,

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1617 handle_fasteoi_irq, NULL, NULL);

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1618 irq_set_probe(irq);

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1619 irqd_set_single_target(irqd);

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1620 break;

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1621 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1622 case LPI_RANGE:

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1623 if (!gic_dist_supports_lpis())

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L1624 return -EPERM;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L1625 irq_domain_set_info(d, irq, hw, chip, d->host_data,

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1626 handle_fasteoi_irq, NULL, NULL);

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1627 break;

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1628 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1629 default:

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1630 return -EPERM;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L1631 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L1632 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1633 /* Prevents SW retriggers which mess up the ACK/EOI ordering */

원본 주석입니다. GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map에서 뒤따르는 코드가 전제하는 하드웨어 조건이나 예외를 먼저 확인합니다.

L1634 irqd_set_handle_enforce_irqctx(irqd);

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1635 return 0;

호출자에게 상태를 반환합니다. 반환값만 보지 말고 그 전에 controller ownership이나 active 상태가 정리되었는지 확인합니다.

L1636 }

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L1637 (blank)

논리 구간을 나누는 빈 줄입니다. 바로 위에서 준비한 상태와 다음 제어 흐름을 분리해 읽습니다.

L1638 static int gic_irq_domain_translate(struct irq_domain *d,

함수 경계입니다. 호출자가 넘긴 번호 공간과 현재 CPU/controller context가 GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 입력 계약을 만족하는지 확인합니다.

L1639 struct irq_fwspec *fwspec,

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1640 unsigned long *hwirq,

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1641 unsigned int *type)

GIC hwirq를 Linux virq chip/handler와 연결하는 irqdomain map의 한 단계입니다. 이 줄이 바꾸는 software 상태를 대응하는 controller register나 trace checkpoint와 연결해 확인합니다.

L1642 {

제어 블록의 범위를 표시합니다. 이 괄호 안에서만 유효한 lock, CPU context, resource lifetime을 함께 표시해 둡니다.

L1643 if (fwspec->param_count == 1 && fwspec->param[0] < 16) {

상태나 ID를 분류하는 분기입니다. 이 조건의 참·거짓은 장치 register, firmware 기술 또는 CPU mode 중 하나에서 결정됩니다.

L1644 *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

Linux v6.18.37 원본 파일 열기 →

08 · VERIFICATION

실제 장비에서 확인하는 순서

  1. Pin

    실행 오실로스코프나 logic analyzer로 장치 사건 전후 interrupt 핀을 측정합니다.

    통과 기준 예상 polarity로 assert되고 level형이면 status clear까지 유지되어야 합니다.

  2. Device status

    실행 장치의 raw/masked interrupt status와 mask register를 같은 시점에 읽습니다.

    통과 기준 raw 원인 bit와 출력 mask 조합이 실제 핀 상태를 설명해야 합니다.

  3. GIC state

    실행 해당 INTID의 enable, pending, active, ICFGR, IROUTER를 debugger 또는 안전한 monitor에서 기록합니다.

    통과 기준 핀 assert 뒤 pending이 생기고 acknowledge 뒤 active로 이동해야 합니다.

  4. CPU entry

    실행 trace_irq_handler_entry, function_graph 또는 exception entry counter를 사용합니다.

    통과 기준 CPU가 INTID를 받은 횟수와 `/proc/interrupts` 증가량이 부하 조건까지 대응해야 합니다.

  5. 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입니다. 그보다 아래의 통과 증거와 그 지점의 실패 증거를 한 묶음으로 남겨야 수정의 효과도 검증할 수 있습니다.