| /* SPDX-License-Identifier: GPL-2.0-only */ | |
| /* | |
| * Copyright (C) 2017 ARM Ltd. | |
| */ | |
| #ifndef __ASM_DAIFFLAGS_H | |
| #define __ASM_DAIFFLAGS_H | |
| #include <linux/irqflags.h> | |
| #include <asm/arch_gicv3.h> | |
| #include <asm/barrier.h> | |
| #include <asm/cpufeature.h> | |
| #include <asm/ptrace.h> | |
| #define DAIF_PROCCTX 0 | |
| #define DAIF_PROCCTX_NOIRQ (PSR_I_BIT | PSR_F_BIT) | |
| #define DAIF_ERRCTX (PSR_A_BIT | PSR_I_BIT | PSR_F_BIT) | |
| #define DAIF_MASK (PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT) | |
| /* mask/save/unmask/restore all exceptions, including interrupts. */ | |
| static __always_inline void local_daif_mask(void) | |
| { | |
| WARN_ON(system_has_prio_mask_debugging() && | |
| (read_sysreg_s(SYS_ICC_PMR_EL1) == (GIC_PRIO_IRQOFF | | |
| GIC_PRIO_PSR_I_SET))); | |
| asm volatile( | |
| "msr daifset, #0xf // local_daif_mask\n" | |
| : | |
| : | |
| : "memory"); | |
| /* Don't really care for a dsb here, we don't intend to enable IRQs */ | |
| if (system_uses_irq_prio_masking()) | |
| gic_write_pmr(GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET); | |
| trace_hardirqs_off(); | |
| } | |
| static __always_inline unsigned long local_daif_save_flags(void) | |
| { | |
| unsigned long flags; | |
| flags = read_sysreg(daif); | |
| if (system_uses_irq_prio_masking()) { | |
| /* If IRQs are masked with PMR, reflect it in the flags */ | |
| if (read_sysreg_s(SYS_ICC_PMR_EL1) != GIC_PRIO_IRQON) | |
| flags |= PSR_I_BIT | PSR_F_BIT; | |
| } | |
| return flags; | |
| } | |
| static __always_inline unsigned long local_daif_save(void) | |
| { | |
| unsigned long flags; | |
| flags = local_daif_save_flags(); | |
| local_daif_mask(); | |
| return flags; | |
| } | |
| static __always_inline void local_daif_restore(unsigned long flags) | |
| { | |
| bool irq_disabled = flags & PSR_I_BIT; | |
| 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) { | |
| trace_hardirqs_on(); | |
| if (system_uses_irq_prio_masking()) { | |
| gic_write_pmr(GIC_PRIO_IRQON); | |
| pmr_sync(); | |
| } | |
| } else if (system_uses_irq_prio_masking()) { | |
| u64 pmr; | |
| if (!(flags & PSR_A_BIT)) { | |
| /* | |
| * If interrupts are disabled but we can take | |
| * asynchronous errors, we can take NMIs | |
| */ | |
| flags &= ~(PSR_I_BIT | PSR_F_BIT); | |
| pmr = GIC_PRIO_IRQOFF; | |
| } else { | |
| pmr = GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET; | |
| } | |
| /* | |
| * 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(); | |
| } | |
| /* | |
| * Called by synchronous exception handlers to restore the DAIF bits that were | |
| * modified by taking an exception. | |
| */ | |
| static __always_inline void local_daif_inherit(struct pt_regs *regs) | |
| { | |
| unsigned long flags = regs->pstate & DAIF_MASK; | |
| if (!regs_irqs_disabled(regs)) | |
| trace_hardirqs_on(); | |
| if (system_uses_irq_prio_masking()) | |
| gic_write_pmr(regs->pmr); | |
| /* | |
| * We can't use local_daif_restore(regs->pstate) here as | |
| * system_has_prio_mask_debugging() won't restore the I bit if it can | |
| * use the pmr instead. | |
| */ | |
| write_sysreg(flags, daif); | |
| } | |
| #endif |