18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2017 ARM Ltd. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci#ifndef __ASM_DAIFFLAGS_H 68c2ecf20Sopenharmony_ci#define __ASM_DAIFFLAGS_H 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/irqflags.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <asm/arch_gicv3.h> 118c2ecf20Sopenharmony_ci#include <asm/barrier.h> 128c2ecf20Sopenharmony_ci#include <asm/cpufeature.h> 138c2ecf20Sopenharmony_ci#include <asm/ptrace.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define DAIF_PROCCTX 0 168c2ecf20Sopenharmony_ci#define DAIF_PROCCTX_NOIRQ PSR_I_BIT 178c2ecf20Sopenharmony_ci#define DAIF_ERRCTX (PSR_I_BIT | PSR_A_BIT) 188c2ecf20Sopenharmony_ci#define DAIF_MASK (PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT) 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* mask/save/unmask/restore all exceptions, including interrupts. */ 228c2ecf20Sopenharmony_cistatic inline void local_daif_mask(void) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci WARN_ON(system_has_prio_mask_debugging() && 258c2ecf20Sopenharmony_ci (read_sysreg_s(SYS_ICC_PMR_EL1) == (GIC_PRIO_IRQOFF | 268c2ecf20Sopenharmony_ci GIC_PRIO_PSR_I_SET))); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci asm volatile( 298c2ecf20Sopenharmony_ci "msr daifset, #0xf // local_daif_mask\n" 308c2ecf20Sopenharmony_ci : 318c2ecf20Sopenharmony_ci : 328c2ecf20Sopenharmony_ci : "memory"); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci /* Don't really care for a dsb here, we don't intend to enable IRQs */ 358c2ecf20Sopenharmony_ci if (system_uses_irq_prio_masking()) 368c2ecf20Sopenharmony_ci gic_write_pmr(GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci trace_hardirqs_off(); 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic inline unsigned long local_daif_save_flags(void) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci unsigned long flags; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci flags = read_sysreg(daif); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci if (system_uses_irq_prio_masking()) { 488c2ecf20Sopenharmony_ci /* If IRQs are masked with PMR, reflect it in the flags */ 498c2ecf20Sopenharmony_ci if (read_sysreg_s(SYS_ICC_PMR_EL1) != GIC_PRIO_IRQON) 508c2ecf20Sopenharmony_ci flags |= PSR_I_BIT; 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci return flags; 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic inline unsigned long local_daif_save(void) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci unsigned long flags; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci flags = local_daif_save_flags(); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci local_daif_mask(); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci return flags; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic inline void local_daif_restore(unsigned long flags) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci bool irq_disabled = flags & PSR_I_BIT; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci WARN_ON(system_has_prio_mask_debugging() && 728c2ecf20Sopenharmony_ci !(read_sysreg(daif) & PSR_I_BIT)); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (!irq_disabled) { 758c2ecf20Sopenharmony_ci trace_hardirqs_on(); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (system_uses_irq_prio_masking()) { 788c2ecf20Sopenharmony_ci gic_write_pmr(GIC_PRIO_IRQON); 798c2ecf20Sopenharmony_ci pmr_sync(); 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci } else if (system_uses_irq_prio_masking()) { 828c2ecf20Sopenharmony_ci u64 pmr; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (!(flags & PSR_A_BIT)) { 858c2ecf20Sopenharmony_ci /* 868c2ecf20Sopenharmony_ci * If interrupts are disabled but we can take 878c2ecf20Sopenharmony_ci * asynchronous errors, we can take NMIs 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_ci flags &= ~PSR_I_BIT; 908c2ecf20Sopenharmony_ci pmr = GIC_PRIO_IRQOFF; 918c2ecf20Sopenharmony_ci } else { 928c2ecf20Sopenharmony_ci pmr = GIC_PRIO_IRQON | GIC_PRIO_PSR_I_SET; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* 968c2ecf20Sopenharmony_ci * There has been concern that the write to daif 978c2ecf20Sopenharmony_ci * might be reordered before this write to PMR. 988c2ecf20Sopenharmony_ci * From the ARM ARM DDI 0487D.a, section D1.7.1 998c2ecf20Sopenharmony_ci * "Accessing PSTATE fields": 1008c2ecf20Sopenharmony_ci * Writes to the PSTATE fields have side-effects on 1018c2ecf20Sopenharmony_ci * various aspects of the PE operation. All of these 1028c2ecf20Sopenharmony_ci * side-effects are guaranteed: 1038c2ecf20Sopenharmony_ci * - Not to be visible to earlier instructions in 1048c2ecf20Sopenharmony_ci * the execution stream. 1058c2ecf20Sopenharmony_ci * - To be visible to later instructions in the 1068c2ecf20Sopenharmony_ci * execution stream 1078c2ecf20Sopenharmony_ci * 1088c2ecf20Sopenharmony_ci * Also, writes to PMR are self-synchronizing, so no 1098c2ecf20Sopenharmony_ci * interrupts with a lower priority than PMR is signaled 1108c2ecf20Sopenharmony_ci * to the PE after the write. 1118c2ecf20Sopenharmony_ci * 1128c2ecf20Sopenharmony_ci * So we don't need additional synchronization here. 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_ci gic_write_pmr(pmr); 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci write_sysreg(flags, daif); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (irq_disabled) 1208c2ecf20Sopenharmony_ci trace_hardirqs_off(); 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci/* 1248c2ecf20Sopenharmony_ci * Called by synchronous exception handlers to restore the DAIF bits that were 1258c2ecf20Sopenharmony_ci * modified by taking an exception. 1268c2ecf20Sopenharmony_ci */ 1278c2ecf20Sopenharmony_cistatic inline void local_daif_inherit(struct pt_regs *regs) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci unsigned long flags = regs->pstate & DAIF_MASK; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (interrupts_enabled(regs)) 1328c2ecf20Sopenharmony_ci trace_hardirqs_on(); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (system_uses_irq_prio_masking()) 1358c2ecf20Sopenharmony_ci gic_write_pmr(regs->pmr_save); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* 1388c2ecf20Sopenharmony_ci * We can't use local_daif_restore(regs->pstate) here as 1398c2ecf20Sopenharmony_ci * system_has_prio_mask_debugging() won't restore the I bit if it can 1408c2ecf20Sopenharmony_ci * use the pmr instead. 1418c2ecf20Sopenharmony_ci */ 1428c2ecf20Sopenharmony_ci write_sysreg(flags, daif); 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci#endif 145