xref: /openbmc/linux/arch/arm64/include/asm/daifflags.h (revision 9b93eb47)
1 /*
2  * Copyright (C) 2017 ARM Ltd.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15  */
16 #ifndef __ASM_DAIFFLAGS_H
17 #define __ASM_DAIFFLAGS_H
18 
19 #include <linux/irqflags.h>
20 
21 #include <asm/cpufeature.h>
22 
23 #define DAIF_PROCCTX		0
24 #define DAIF_PROCCTX_NOIRQ	PSR_I_BIT
25 #define DAIF_ERRCTX		(PSR_I_BIT | PSR_A_BIT)
26 
27 /* mask/save/unmask/restore all exceptions, including interrupts. */
28 static inline void local_daif_mask(void)
29 {
30 	asm volatile(
31 		"msr	daifset, #0xf		// local_daif_mask\n"
32 		:
33 		:
34 		: "memory");
35 	trace_hardirqs_off();
36 }
37 
38 static inline unsigned long local_daif_save(void)
39 {
40 	unsigned long flags;
41 
42 	flags = read_sysreg(daif);
43 
44 	if (system_uses_irq_prio_masking()) {
45 		/* If IRQs are masked with PMR, reflect it in the flags */
46 		if (read_sysreg_s(SYS_ICC_PMR_EL1) <= GIC_PRIO_IRQOFF)
47 			flags |= PSR_I_BIT;
48 	}
49 
50 	local_daif_mask();
51 
52 	return flags;
53 }
54 
55 static inline void local_daif_restore(unsigned long flags)
56 {
57 	bool irq_disabled = flags & PSR_I_BIT;
58 
59 	if (!irq_disabled) {
60 		trace_hardirqs_on();
61 
62 		if (system_uses_irq_prio_masking())
63 			arch_local_irq_enable();
64 	} else if (!(flags & PSR_A_BIT)) {
65 		/*
66 		 * If interrupts are disabled but we can take
67 		 * asynchronous errors, we can take NMIs
68 		 */
69 		if (system_uses_irq_prio_masking()) {
70 			flags &= ~PSR_I_BIT;
71 			/*
72 			 * There has been concern that the write to daif
73 			 * might be reordered before this write to PMR.
74 			 * From the ARM ARM DDI 0487D.a, section D1.7.1
75 			 * "Accessing PSTATE fields":
76 			 *   Writes to the PSTATE fields have side-effects on
77 			 *   various aspects of the PE operation. All of these
78 			 *   side-effects are guaranteed:
79 			 *     - Not to be visible to earlier instructions in
80 			 *       the execution stream.
81 			 *     - To be visible to later instructions in the
82 			 *       execution stream
83 			 *
84 			 * Also, writes to PMR are self-synchronizing, so no
85 			 * interrupts with a lower priority than PMR is signaled
86 			 * to the PE after the write.
87 			 *
88 			 * So we don't need additional synchronization here.
89 			 */
90 			arch_local_irq_disable();
91 		}
92 	}
93 
94 	write_sysreg(flags, daif);
95 
96 	if (irq_disabled)
97 		trace_hardirqs_off();
98 }
99 
100 #endif
101