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
就分为两种情况:
- 支持gic中断优先级时,使用gic的优先级来屏蔽中断
- 修改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();
}