arm A64 local_irq_disable/local_irq_save实现

Linux很多地方会使用 local_irq_disable/local_irq_save函数,那么不同CPU架构,有不同的实现方式,arm64又是怎么实现的呢?

下面是spin_lock_irqsave的代码调用层次关系:

-> spin_lock_irqsave    /* include/linux/spinlock.h */
  -> raw_spin_lock_irqsave    /* include/linux/spinlock.h */
    -> _raw_spin_lock_irqsave    /* include/linux/spinlock_api_smp.h */
      -> __raw_spin_lock_irqsave    /* include/linux/spinlock_api_smp.h */
        -> local_irq_save    /* include/linux/irqflags.h */
          -> raw_local_irq_save    /* include/linux/irqflags.h */
            -> arch_local_irq_save    /* arch/arm64/include/asm/irqflags.h */

arm nmi使用了 GIC 架构中的中断优先级特性。Linux 对特定中断号进行编程,使其其优先级高于所有其他中断。然后重写了arm64特定的中断启用和禁用函数来更改CPU中断优先级掩码(ICC_PMR_EL1),而不是直接操作CPU IRQ异常标志(PSTATE.I),从达到特定中断即可视为NMI。

从代码来看那么arch_local_irq_disable就分为两种情况:

  1. 支持gic中断优先级时,使用gic的优先级来屏蔽中断
  2. 修改daif csr寄存器,即PSTATE.IF来屏蔽
static __always_inline void __daif_local_irq_disable(void)
{
	barrier();
	asm volatile("msr daifset, #3");
	barrier();
}

static __always_inline void __pmr_local_irq_disable(void)
{
	if (IS_ENABLED(CONFIG_ARM64_DEBUG_PRIORITY_MASKING)) {
		u32 pmr = read_sysreg_s(SYS_ICC_PMR_EL1);
		WARN_ON_ONCE(pmr != GIC_PRIO_IRQON && pmr != GIC_PRIO_IRQOFF);
	}

	barrier();
	write_sysreg_s(GIC_PRIO_IRQOFF, SYS_ICC_PMR_EL1);
	barrier();
}
posted @ 2024-03-05 19:12  zephyr~  阅读(81)  评论(0编辑  收藏  举报