162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2017 ARM Ltd. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#ifndef __ASM_DAIFFLAGS_H 662306a36Sopenharmony_ci#define __ASM_DAIFFLAGS_H 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/irqflags.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <asm/arch_gicv3.h> 1162306a36Sopenharmony_ci#include <asm/barrier.h> 1262306a36Sopenharmony_ci#include <asm/cpufeature.h> 1362306a36Sopenharmony_ci#include <asm/ptrace.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define DAIF_PROCCTX 0 1662306a36Sopenharmony_ci#define DAIF_PROCCTX_NOIRQ (PSR_I_BIT | PSR_F_BIT) 1762306a36Sopenharmony_ci#define DAIF_ERRCTX (PSR_A_BIT | PSR_I_BIT | PSR_F_BIT) 1862306a36Sopenharmony_ci#define DAIF_MASK (PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT) 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* mask/save/unmask/restore all exceptions, including interrupts. */ 2262306a36Sopenharmony_cistatic inline void local_daif_mask(void) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci WARN_ON(system_has_prio_mask_debugging() && 2562306a36Sopenharmony_ci (read_sysreg_s(SYS_ICC_PMR_EL1) == (GIC_PRIO_IRQOFF | 2662306a36Sopenharmony_ci GIC_PRIO_PSR_I_SET))); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci asm volatile( 2962306a36Sopenharmony_ci "msr daifset, #0xf // local_daif_mask\n" 3062306a36Sopenharmony_ci : 3162306a36Sopenharmony_ci : 3262306a36Sopenharmony_ci : "memory"); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci /* Don't really care for a dsb here, we don't intend to enable IRQs */ 3562306a36Sopenharmony_ci if (system_uses_irq_prio_masking()) 3662306a36Sopenharmony_ci gic_write_pmr(GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci trace_hardirqs_off(); 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic inline unsigned long local_daif_save_flags(void) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci unsigned long flags; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci flags = read_sysreg(daif); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci if (system_uses_irq_prio_masking()) { 4862306a36Sopenharmony_ci /* If IRQs are masked with PMR, reflect it in the flags */ 4962306a36Sopenharmony_ci if (read_sysreg_s(SYS_ICC_PMR_EL1) != GIC_PRIO_IRQON) 5062306a36Sopenharmony_ci flags |= PSR_I_BIT | PSR_F_BIT; 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci return flags; 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic inline unsigned long local_daif_save(void) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci unsigned long flags; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci flags = local_daif_save_flags(); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci local_daif_mask(); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci return flags; 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic inline void local_daif_restore(unsigned long flags) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci bool irq_disabled = flags & PSR_I_BIT; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci WARN_ON(system_has_prio_mask_debugging() && 7262306a36Sopenharmony_ci (read_sysreg(daif) & (PSR_I_BIT | PSR_F_BIT)) != (PSR_I_BIT | PSR_F_BIT)); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (!irq_disabled) { 7562306a36Sopenharmony_ci trace_hardirqs_on(); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (system_uses_irq_prio_masking()) { 7862306a36Sopenharmony_ci gic_write_pmr(GIC_PRIO_IRQON); 7962306a36Sopenharmony_ci pmr_sync(); 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci } else if (system_uses_irq_prio_masking()) { 8262306a36Sopenharmony_ci u64 pmr; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (!(flags & PSR_A_BIT)) { 8562306a36Sopenharmony_ci /* 8662306a36Sopenharmony_ci * If interrupts are disabled but we can take 8762306a36Sopenharmony_ci * asynchronous errors, we can take NMIs 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ci flags &= ~(PSR_I_BIT | PSR_F_BIT); 9062306a36Sopenharmony_ci pmr = GIC_PRIO_IRQOFF; 9162306a36Sopenharmony_ci } else { 9262306a36Sopenharmony_ci pmr = GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* 9662306a36Sopenharmony_ci * There has been concern that the write to daif 9762306a36Sopenharmony_ci * might be reordered before this write to PMR. 9862306a36Sopenharmony_ci * From the ARM ARM DDI 0487D.a, section D1.7.1 9962306a36Sopenharmony_ci * "Accessing PSTATE fields": 10062306a36Sopenharmony_ci * Writes to the PSTATE fields have side-effects on 10162306a36Sopenharmony_ci * various aspects of the PE operation. All of these 10262306a36Sopenharmony_ci * side-effects are guaranteed: 10362306a36Sopenharmony_ci * - Not to be visible to earlier instructions in 10462306a36Sopenharmony_ci * the execution stream. 10562306a36Sopenharmony_ci * - To be visible to later instructions in the 10662306a36Sopenharmony_ci * execution stream 10762306a36Sopenharmony_ci * 10862306a36Sopenharmony_ci * Also, writes to PMR are self-synchronizing, so no 10962306a36Sopenharmony_ci * interrupts with a lower priority than PMR is signaled 11062306a36Sopenharmony_ci * to the PE after the write. 11162306a36Sopenharmony_ci * 11262306a36Sopenharmony_ci * So we don't need additional synchronization here. 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_ci gic_write_pmr(pmr); 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci write_sysreg(flags, daif); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (irq_disabled) 12062306a36Sopenharmony_ci trace_hardirqs_off(); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* 12462306a36Sopenharmony_ci * Called by synchronous exception handlers to restore the DAIF bits that were 12562306a36Sopenharmony_ci * modified by taking an exception. 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_cistatic inline void local_daif_inherit(struct pt_regs *regs) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci unsigned long flags = regs->pstate & DAIF_MASK; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if (interrupts_enabled(regs)) 13262306a36Sopenharmony_ci trace_hardirqs_on(); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (system_uses_irq_prio_masking()) 13562306a36Sopenharmony_ci gic_write_pmr(regs->pmr_save); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* 13862306a36Sopenharmony_ci * We can't use local_daif_restore(regs->pstate) here as 13962306a36Sopenharmony_ci * system_has_prio_mask_debugging() won't restore the I bit if it can 14062306a36Sopenharmony_ci * use the pmr instead. 14162306a36Sopenharmony_ci */ 14262306a36Sopenharmony_ci write_sysreg(flags, daif); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci#endif 145