개발/arm64 linux 6 분석
local_daif_restore (setup_arch)
clucle
2025. 5. 31. 19:55
- primary cpu 는 커널에 진입할때 모든 exception 이 마스크된 채로 들어옵니다. DAIF = 1111
- 디버깅과(D) SError(A) 를 마스킹 해제 해야 합니다.
- IF 는 irqchip (gic) 초기화 후 열수 있기 때문에 마스킹 합니다.
#define DAIF_PROCCTX_NOIRQ (PSR_I_BIT | PSR_F_BIT)
void __init __no_sanitize_address setup_arch(char **cmdline_p)
{
/*
* The primary CPU enters the kernel with all DAIF exceptions masked.
*
* We must unmask Debug and SError before preemption or scheduling is
* possible to ensure that these are consistently unmasked across
* threads, and we want to unmask SError as soon as possible after
* initializing earlycon so that we can report any SErrors immediately.
*
* IRQ and FIQ will be unmasked after the root irqchip has been
* detected and initialized.
*/
// DAIF_PROCCTX_NOIRQ // #define DAIF_PROCCTX_NOIRQ (PSR_I_BIT | PSR_F_BIT)
// IF 를 활성화하지 않는 이유는 irqchip (gic 등) 초기화 후 활성화하기 때문
local_daif_restore(DAIF_PROCCTX_NOIRQ); // IF 를 마스킹함
daif 란?
Interrupt Mask Bits

- 비트값이 0인경우 not maksed
- 비트값이 1인경우 masked (비활성화)
system_has_prio_mask_debugging, system_uses_irq_prio_masking 란?
Generic Interrupt Controller 의 이해
- 하드웨어 레벨에서 인터럽트의 우선순위를 관리하고, 필요한 곳으로 인터럽트를 라우팅 시켜줍니다.
- PMR(Priority Mask Register) 등 레지스터를 통해 동작을 제어합니다.
pmr
- Priority filter
- 숫자가 작을수록 높은 우선순위를 갖는 인터럽트
#define GICV3_PRIO_UNMASKED 0xe0 (0x00 ~ 0xdf 를 허용)
#define GICV3_PRIO_IRQ 0xc0 (0x00 ~ 0xbf 를 허용)
#define GICV3_PRIO_NMI 0x80 (0x00 ~ 0x7f 를 허용)
#define GICV3_PRIO_PSR_I_SET (1 << 4) // DAIF 가 1이면 적용되는 보정값
#define GIC_PRIO_IRQON GICV3_PRIO_UNMASKED
#define GIC_PRIO_IRQOFF GICV3_PRIO_IRQ
#define GIC_PRIO_PSR_I_SET GICV3_PRIO_PSR_I_SET
/proc/interrupts 에서 인터럽트 목록 확인 가능
static inline void local_daif_restore(unsigned long flags)
{
bool irq_disabled = flags & PSR_I_BIT; // flag 의 irq 가 비활성 상태인지
// system_has_prio_mask_debugging 인 경우 현재 레지스터의 상황이 IRQ, FIQ 모두 비활성화여야 함.
// 왜?
WARN_ON(system_has_prio_mask_debugging() &&
(read_sysreg(daif) & (PSR_I_BIT | PSR_F_BIT)) != (PSR_I_BIT | PSR_F_BIT));
if (!irq_disabled) { // IRQ 를 활성화 하려고 시도
trace_hardirqs_on();
if (system_uses_irq_prio_masking()) { // GIC PRIO 사용중이라면
gic_write_pmr(GIC_PRIO_IRQON); // IRQ 가 활성화 되어있으니, GIC 의 우선순위도 IRQ 를 받을 수 있도록 설정
pmr_sync();
}
} else if (system_uses_irq_prio_masking()) { // IRQ 를 비활성화 하려하고, GIC PRIO 사용중이라면
u64 pmr;
if (!(flags & PSR_A_BIT)) { // async abort 를 활성화 하려고 한다면
/*
* If interrupts are disabled but we can take
* asynchronous errors, we can take NMIs
*/
// Non-maskable interrupt. 무시될 수 없는 중요한 인터럽트
flags &= ~(PSR_I_BIT | PSR_F_BIT); // abort 를 받기 위해, IF 비트도 켜줘야 합니다. 실제로는 NMI 를 받기 위함입니다.
pmr = GIC_PRIO_IRQOFF; // 결과적으로 인터럽트는 들어오지 않고, NMI/SError 만 들어올 수 있는 상태
} else {
pmr = GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET; // 우선순위는 낮게 설정하지만, IRQ 가 비트가 켜짐을 표현하여 꺼져있는 상황을 표현.
}
/*
* There has been concern that the write to daif
* might be reordered before this write to PMR.
* From the ARM ARM DDI 0487D.a, section D1.7.1
* "Accessing PSTATE fields":
* Writes to the PSTATE fields have side-effects on
* various aspects of the PE operation. All of these
* side-effects are guaranteed:
* - Not to be visible to earlier instructions in
* the execution stream.
* - To be visible to later instructions in the
* execution stream
*
* Also, writes to PMR are self-synchronizing, so no
* interrupts with a lower priority than PMR is signaled
* to the PE after the write.
*
* So we don't need additional synchronization here.
*/
gic_write_pmr(pmr);
}
write_sysreg(flags, daif);
if (irq_disabled)
trace_hardirqs_off();
}