powerpc/kuap: Simplify KUAP lock/unlock on BOOK3S/32
[platform/kernel/linux-starfive.git] / arch / powerpc / include / asm / book3s / 32 / kup.h
1 /* SPDX-License-Identifier: GPL-2.0 */
2 #ifndef _ASM_POWERPC_BOOK3S_32_KUP_H
3 #define _ASM_POWERPC_BOOK3S_32_KUP_H
4
5 #include <asm/bug.h>
6 #include <asm/book3s/32/mmu-hash.h>
7 #include <asm/mmu.h>
8 #include <asm/synch.h>
9
10 #ifndef __ASSEMBLY__
11
12 #ifdef CONFIG_PPC_KUAP
13
14 #include <linux/sched.h>
15
16 #define KUAP_NONE       (~0UL)
17
18 static inline void kuap_lock_one(unsigned long addr)
19 {
20         mtsr(mfsr(addr) | SR_KS, addr);
21         isync();        /* Context sync required after mtsr() */
22 }
23
24 static inline void kuap_unlock_one(unsigned long addr)
25 {
26         mtsr(mfsr(addr) & ~SR_KS, addr);
27         isync();        /* Context sync required after mtsr() */
28 }
29
30 static inline void __kuap_save_and_lock(struct pt_regs *regs)
31 {
32         unsigned long kuap = current->thread.kuap;
33
34         regs->kuap = kuap;
35         if (unlikely(kuap == KUAP_NONE))
36                 return;
37
38         current->thread.kuap = KUAP_NONE;
39         kuap_lock_one(kuap);
40 }
41 #define __kuap_save_and_lock __kuap_save_and_lock
42
43 static inline void kuap_user_restore(struct pt_regs *regs)
44 {
45 }
46
47 static inline void __kuap_kernel_restore(struct pt_regs *regs, unsigned long kuap)
48 {
49         if (unlikely(kuap != KUAP_NONE)) {
50                 current->thread.kuap = KUAP_NONE;
51                 kuap_lock_one(kuap);
52         }
53
54         if (likely(regs->kuap == KUAP_NONE))
55                 return;
56
57         current->thread.kuap = regs->kuap;
58
59         kuap_unlock_one(regs->kuap);
60 }
61
62 static inline unsigned long __kuap_get_and_assert_locked(void)
63 {
64         unsigned long kuap = current->thread.kuap;
65
66         WARN_ON_ONCE(IS_ENABLED(CONFIG_PPC_KUAP_DEBUG) && kuap != KUAP_NONE);
67
68         return kuap;
69 }
70 #define __kuap_get_and_assert_locked __kuap_get_and_assert_locked
71
72 static __always_inline void __allow_user_access(void __user *to, const void __user *from,
73                                                 u32 size, unsigned long dir)
74 {
75         BUILD_BUG_ON(!__builtin_constant_p(dir));
76
77         if (!(dir & KUAP_WRITE))
78                 return;
79
80         current->thread.kuap = (__force u32)to;
81         kuap_unlock_one((__force u32)to);
82 }
83
84 static __always_inline void __prevent_user_access(unsigned long dir)
85 {
86         u32 kuap = current->thread.kuap;
87
88         BUILD_BUG_ON(!__builtin_constant_p(dir));
89
90         if (!(dir & KUAP_WRITE))
91                 return;
92
93         current->thread.kuap = KUAP_NONE;
94         kuap_lock_one(kuap);
95 }
96
97 static inline unsigned long __prevent_user_access_return(void)
98 {
99         unsigned long flags = current->thread.kuap;
100
101         if (flags != KUAP_NONE) {
102                 current->thread.kuap = KUAP_NONE;
103                 kuap_lock_one(flags);
104         }
105
106         return flags;
107 }
108
109 static inline void __restore_user_access(unsigned long flags)
110 {
111         if (flags != KUAP_NONE) {
112                 current->thread.kuap = flags;
113                 kuap_unlock_one(flags);
114         }
115 }
116
117 static inline bool
118 __bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write)
119 {
120         unsigned long kuap = regs->kuap;
121
122         if (!is_write)
123                 return false;
124         if (kuap == KUAP_NONE)
125                 return true;
126
127         /*
128          * If faulting address doesn't match unlocked segment, change segment.
129          * In case of unaligned store crossing two segments, emulate store.
130          */
131         if ((kuap ^ address) & 0xf0000000) {
132                 if (!(kuap & 0x0fffffff) && address > kuap - 4 && fix_alignment(regs)) {
133                         regs_add_return_ip(regs, 4);
134                         emulate_single_step(regs);
135                 } else {
136                         regs->kuap = address;
137                 }
138         }
139
140         return false;
141 }
142
143 #endif /* CONFIG_PPC_KUAP */
144
145 #endif /* __ASSEMBLY__ */
146
147 #endif /* _ASM_POWERPC_BOOK3S_32_KUP_H */