summaryrefslogtreecommitdiff
path: root/arch/powerpc/include/asm/kup.h
blob: ad7e8c5aec3f82f318ef74883d813c0d16d59f29 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_POWERPC_KUP_H_
#define _ASM_POWERPC_KUP_H_

#define KUAP_READ	1
#define KUAP_WRITE	2
#define KUAP_READ_WRITE	(KUAP_READ | KUAP_WRITE)

#ifndef __ASSEMBLY__
#include <linux/types.h>

static __always_inline bool kuap_is_disabled(void);
#endif

#ifdef CONFIG_PPC_BOOK3S_64
#include <asm/book3s/64/kup.h>
#endif

#ifdef CONFIG_PPC_8xx
#include <asm/nohash/32/kup-8xx.h>
#endif

#ifdef CONFIG_BOOKE_OR_40x
#include <asm/nohash/kup-booke.h>
#endif

#ifdef CONFIG_PPC_BOOK3S_32
#include <asm/book3s/32/kup.h>
#endif

#ifdef __ASSEMBLY__
#ifndef CONFIG_PPC_KUAP
.macro kuap_check_amr	gpr1, gpr2
.endm

#endif

#else /* !__ASSEMBLY__ */

extern bool disable_kuep;
extern bool disable_kuap;

#include <linux/pgtable.h>

void setup_kup(void);
void setup_kuep(bool disabled);

#ifdef CONFIG_PPC_KUAP
void setup_kuap(bool disabled);

static __always_inline bool kuap_is_disabled(void)
{
	return !mmu_has_feature(MMU_FTR_KUAP);
}
#else
static inline void setup_kuap(bool disabled) { }

static __always_inline bool kuap_is_disabled(void) { return true; }

static __always_inline bool
__bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write)
{
	return false;
}

static __always_inline void kuap_user_restore(struct pt_regs *regs) { }
static __always_inline void __kuap_kernel_restore(struct pt_regs *regs, unsigned long amr) { }

/*
 * book3s/64/kup-radix.h defines these functions for the !KUAP case to flush
 * the L1D cache after user accesses. Only include the empty stubs for other
 * platforms.
 */
#ifndef CONFIG_PPC_BOOK3S_64
static __always_inline void allow_user_access(void __user *to, const void __user *from,
					      unsigned long size, unsigned long dir) { }
static __always_inline void prevent_user_access(unsigned long dir) { }
static __always_inline unsigned long prevent_user_access_return(void) { return 0UL; }
static __always_inline void restore_user_access(unsigned long flags) { }
#endif /* CONFIG_PPC_BOOK3S_64 */
#endif /* CONFIG_PPC_KUAP */

static __always_inline bool
bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write)
{
	if (kuap_is_disabled())
		return false;

	return __bad_kuap_fault(regs, address, is_write);
}

static __always_inline void kuap_lock(void)
{
#ifdef __kuap_lock
	if (kuap_is_disabled())
		return;

	__kuap_lock();
#endif
}

static __always_inline void kuap_save_and_lock(struct pt_regs *regs)
{
#ifdef __kuap_save_and_lock
	if (kuap_is_disabled())
		return;

	__kuap_save_and_lock(regs);
#endif
}

static __always_inline void kuap_kernel_restore(struct pt_regs *regs, unsigned long amr)
{
	if (kuap_is_disabled())
		return;

	__kuap_kernel_restore(regs, amr);
}

static __always_inline unsigned long kuap_get_and_assert_locked(void)
{
#ifdef __kuap_get_and_assert_locked
	if (!kuap_is_disabled())
		return __kuap_get_and_assert_locked();
#endif
	return 0;
}

static __always_inline void kuap_assert_locked(void)
{
	if (IS_ENABLED(CONFIG_PPC_KUAP_DEBUG))
		kuap_get_and_assert_locked();
}

static __always_inline void allow_read_from_user(const void __user *from, unsigned long size)
{
	barrier_nospec();
	allow_user_access(NULL, from, size, KUAP_READ);
}

static __always_inline void allow_write_to_user(void __user *to, unsigned long size)
{
	allow_user_access(to, NULL, size, KUAP_WRITE);
}

static __always_inline void allow_read_write_user(void __user *to, const void __user *from,
						  unsigned long size)
{
	barrier_nospec();
	allow_user_access(to, from, size, KUAP_READ_WRITE);
}

static __always_inline void prevent_read_from_user(const void __user *from, unsigned long size)
{
	prevent_user_access(KUAP_READ);
}

static __always_inline void prevent_write_to_user(void __user *to, unsigned long size)
{
	prevent_user_access(KUAP_WRITE);
}

static __always_inline void prevent_read_write_user(void __user *to, const void __user *from,
						    unsigned long size)
{
	prevent_user_access(KUAP_READ_WRITE);
}

static __always_inline void prevent_current_access_user(void)
{
	prevent_user_access(KUAP_READ_WRITE);
}

static __always_inline void prevent_current_read_from_user(void)
{
	prevent_user_access(KUAP_READ);
}

static __always_inline void prevent_current_write_to_user(void)
{
	prevent_user_access(KUAP_WRITE);
}

#endif /* !__ASSEMBLY__ */

#endif /* _ASM_POWERPC_KUAP_H_ */