162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
262306a36Sopenharmony_ci#ifndef _ASM_POWERPC_KUP_H_
362306a36Sopenharmony_ci#define _ASM_POWERPC_KUP_H_
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#define KUAP_READ	1
662306a36Sopenharmony_ci#define KUAP_WRITE	2
762306a36Sopenharmony_ci#define KUAP_READ_WRITE	(KUAP_READ | KUAP_WRITE)
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#ifndef __ASSEMBLY__
1062306a36Sopenharmony_ci#include <linux/types.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_cistatic __always_inline bool kuap_is_disabled(void);
1362306a36Sopenharmony_ci#endif
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#ifdef CONFIG_PPC_BOOK3S_64
1662306a36Sopenharmony_ci#include <asm/book3s/64/kup.h>
1762306a36Sopenharmony_ci#endif
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#ifdef CONFIG_PPC_8xx
2062306a36Sopenharmony_ci#include <asm/nohash/32/kup-8xx.h>
2162306a36Sopenharmony_ci#endif
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#ifdef CONFIG_BOOKE_OR_40x
2462306a36Sopenharmony_ci#include <asm/nohash/kup-booke.h>
2562306a36Sopenharmony_ci#endif
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#ifdef CONFIG_PPC_BOOK3S_32
2862306a36Sopenharmony_ci#include <asm/book3s/32/kup.h>
2962306a36Sopenharmony_ci#endif
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#ifdef __ASSEMBLY__
3262306a36Sopenharmony_ci#ifndef CONFIG_PPC_KUAP
3362306a36Sopenharmony_ci.macro kuap_check_amr	gpr1, gpr2
3462306a36Sopenharmony_ci.endm
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#endif
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#else /* !__ASSEMBLY__ */
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ciextern bool disable_kuep;
4162306a36Sopenharmony_ciextern bool disable_kuap;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#include <linux/pgtable.h>
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_civoid setup_kup(void);
4662306a36Sopenharmony_civoid setup_kuep(bool disabled);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#ifdef CONFIG_PPC_KUAP
4962306a36Sopenharmony_civoid setup_kuap(bool disabled);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic __always_inline bool kuap_is_disabled(void)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	return !mmu_has_feature(MMU_FTR_KUAP);
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci#else
5662306a36Sopenharmony_cistatic inline void setup_kuap(bool disabled) { }
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic __always_inline bool kuap_is_disabled(void) { return true; }
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic __always_inline bool
6162306a36Sopenharmony_ci__bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	return false;
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic __always_inline void kuap_user_restore(struct pt_regs *regs) { }
6762306a36Sopenharmony_cistatic __always_inline void __kuap_kernel_restore(struct pt_regs *regs, unsigned long amr) { }
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/*
7062306a36Sopenharmony_ci * book3s/64/kup-radix.h defines these functions for the !KUAP case to flush
7162306a36Sopenharmony_ci * the L1D cache after user accesses. Only include the empty stubs for other
7262306a36Sopenharmony_ci * platforms.
7362306a36Sopenharmony_ci */
7462306a36Sopenharmony_ci#ifndef CONFIG_PPC_BOOK3S_64
7562306a36Sopenharmony_cistatic __always_inline void allow_user_access(void __user *to, const void __user *from,
7662306a36Sopenharmony_ci					      unsigned long size, unsigned long dir) { }
7762306a36Sopenharmony_cistatic __always_inline void prevent_user_access(unsigned long dir) { }
7862306a36Sopenharmony_cistatic __always_inline unsigned long prevent_user_access_return(void) { return 0UL; }
7962306a36Sopenharmony_cistatic __always_inline void restore_user_access(unsigned long flags) { }
8062306a36Sopenharmony_ci#endif /* CONFIG_PPC_BOOK3S_64 */
8162306a36Sopenharmony_ci#endif /* CONFIG_PPC_KUAP */
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic __always_inline bool
8462306a36Sopenharmony_cibad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	if (kuap_is_disabled())
8762306a36Sopenharmony_ci		return false;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	return __bad_kuap_fault(regs, address, is_write);
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic __always_inline void kuap_lock(void)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci#ifdef __kuap_lock
9562306a36Sopenharmony_ci	if (kuap_is_disabled())
9662306a36Sopenharmony_ci		return;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	__kuap_lock();
9962306a36Sopenharmony_ci#endif
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic __always_inline void kuap_save_and_lock(struct pt_regs *regs)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci#ifdef __kuap_save_and_lock
10562306a36Sopenharmony_ci	if (kuap_is_disabled())
10662306a36Sopenharmony_ci		return;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	__kuap_save_and_lock(regs);
10962306a36Sopenharmony_ci#endif
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic __always_inline void kuap_kernel_restore(struct pt_regs *regs, unsigned long amr)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	if (kuap_is_disabled())
11562306a36Sopenharmony_ci		return;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	__kuap_kernel_restore(regs, amr);
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic __always_inline unsigned long kuap_get_and_assert_locked(void)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci#ifdef __kuap_get_and_assert_locked
12362306a36Sopenharmony_ci	if (!kuap_is_disabled())
12462306a36Sopenharmony_ci		return __kuap_get_and_assert_locked();
12562306a36Sopenharmony_ci#endif
12662306a36Sopenharmony_ci	return 0;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic __always_inline void kuap_assert_locked(void)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_PPC_KUAP_DEBUG))
13262306a36Sopenharmony_ci		kuap_get_and_assert_locked();
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic __always_inline void allow_read_from_user(const void __user *from, unsigned long size)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	barrier_nospec();
13862306a36Sopenharmony_ci	allow_user_access(NULL, from, size, KUAP_READ);
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic __always_inline void allow_write_to_user(void __user *to, unsigned long size)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	allow_user_access(to, NULL, size, KUAP_WRITE);
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic __always_inline void allow_read_write_user(void __user *to, const void __user *from,
14762306a36Sopenharmony_ci						  unsigned long size)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	barrier_nospec();
15062306a36Sopenharmony_ci	allow_user_access(to, from, size, KUAP_READ_WRITE);
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic __always_inline void prevent_read_from_user(const void __user *from, unsigned long size)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	prevent_user_access(KUAP_READ);
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic __always_inline void prevent_write_to_user(void __user *to, unsigned long size)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	prevent_user_access(KUAP_WRITE);
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic __always_inline void prevent_read_write_user(void __user *to, const void __user *from,
16462306a36Sopenharmony_ci						    unsigned long size)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	prevent_user_access(KUAP_READ_WRITE);
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic __always_inline void prevent_current_access_user(void)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	prevent_user_access(KUAP_READ_WRITE);
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic __always_inline void prevent_current_read_from_user(void)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	prevent_user_access(KUAP_READ);
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic __always_inline void prevent_current_write_to_user(void)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	prevent_user_access(KUAP_WRITE);
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci#endif /* !__ASSEMBLY__ */
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci#endif /* _ASM_POWERPC_KUAP_H_ */
187