개발/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

https://developer.arm.com/documentation/ddi0601/2025-03/AArch64-Registers/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();
}