cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

kup.h (3880B)


      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#include <linux/jump_label.h>
     13
     14extern struct static_key_false disable_kuap_key;
     15
     16static __always_inline bool kuep_is_disabled(void)
     17{
     18	return !IS_ENABLED(CONFIG_PPC_KUEP);
     19}
     20
     21#ifdef CONFIG_PPC_KUAP
     22
     23#include <linux/sched.h>
     24
     25#define KUAP_NONE	(~0UL)
     26#define KUAP_ALL	(~1UL)
     27
     28static __always_inline bool kuap_is_disabled(void)
     29{
     30	return static_branch_unlikely(&disable_kuap_key);
     31}
     32
     33static inline void kuap_lock_one(unsigned long addr)
     34{
     35	mtsr(mfsr(addr) | SR_KS, addr);
     36	isync();	/* Context sync required after mtsr() */
     37}
     38
     39static inline void kuap_unlock_one(unsigned long addr)
     40{
     41	mtsr(mfsr(addr) & ~SR_KS, addr);
     42	isync();	/* Context sync required after mtsr() */
     43}
     44
     45static inline void kuap_lock_all(void)
     46{
     47	update_user_segments(mfsr(0) | SR_KS);
     48	isync();	/* Context sync required after mtsr() */
     49}
     50
     51static inline void kuap_unlock_all(void)
     52{
     53	update_user_segments(mfsr(0) & ~SR_KS);
     54	isync();	/* Context sync required after mtsr() */
     55}
     56
     57void kuap_lock_all_ool(void);
     58void kuap_unlock_all_ool(void);
     59
     60static inline void kuap_lock_addr(unsigned long addr, bool ool)
     61{
     62	if (likely(addr != KUAP_ALL))
     63		kuap_lock_one(addr);
     64	else if (!ool)
     65		kuap_lock_all();
     66	else
     67		kuap_lock_all_ool();
     68}
     69
     70static inline void kuap_unlock(unsigned long addr, bool ool)
     71{
     72	if (likely(addr != KUAP_ALL))
     73		kuap_unlock_one(addr);
     74	else if (!ool)
     75		kuap_unlock_all();
     76	else
     77		kuap_unlock_all_ool();
     78}
     79
     80static inline void __kuap_lock(void)
     81{
     82}
     83
     84static inline void __kuap_save_and_lock(struct pt_regs *regs)
     85{
     86	unsigned long kuap = current->thread.kuap;
     87
     88	regs->kuap = kuap;
     89	if (unlikely(kuap == KUAP_NONE))
     90		return;
     91
     92	current->thread.kuap = KUAP_NONE;
     93	kuap_lock_addr(kuap, false);
     94}
     95
     96static inline void kuap_user_restore(struct pt_regs *regs)
     97{
     98}
     99
    100static inline void __kuap_kernel_restore(struct pt_regs *regs, unsigned long kuap)
    101{
    102	if (unlikely(kuap != KUAP_NONE)) {
    103		current->thread.kuap = KUAP_NONE;
    104		kuap_lock_addr(kuap, false);
    105	}
    106
    107	if (likely(regs->kuap == KUAP_NONE))
    108		return;
    109
    110	current->thread.kuap = regs->kuap;
    111
    112	kuap_unlock(regs->kuap, false);
    113}
    114
    115static inline unsigned long __kuap_get_and_assert_locked(void)
    116{
    117	unsigned long kuap = current->thread.kuap;
    118
    119	WARN_ON_ONCE(IS_ENABLED(CONFIG_PPC_KUAP_DEBUG) && kuap != KUAP_NONE);
    120
    121	return kuap;
    122}
    123
    124static __always_inline void __allow_user_access(void __user *to, const void __user *from,
    125						u32 size, unsigned long dir)
    126{
    127	BUILD_BUG_ON(!__builtin_constant_p(dir));
    128
    129	if (!(dir & KUAP_WRITE))
    130		return;
    131
    132	current->thread.kuap = (__force u32)to;
    133	kuap_unlock_one((__force u32)to);
    134}
    135
    136static __always_inline void __prevent_user_access(unsigned long dir)
    137{
    138	u32 kuap = current->thread.kuap;
    139
    140	BUILD_BUG_ON(!__builtin_constant_p(dir));
    141
    142	if (!(dir & KUAP_WRITE))
    143		return;
    144
    145	current->thread.kuap = KUAP_NONE;
    146	kuap_lock_addr(kuap, true);
    147}
    148
    149static inline unsigned long __prevent_user_access_return(void)
    150{
    151	unsigned long flags = current->thread.kuap;
    152
    153	if (flags != KUAP_NONE) {
    154		current->thread.kuap = KUAP_NONE;
    155		kuap_lock_addr(flags, true);
    156	}
    157
    158	return flags;
    159}
    160
    161static inline void __restore_user_access(unsigned long flags)
    162{
    163	if (flags != KUAP_NONE) {
    164		current->thread.kuap = flags;
    165		kuap_unlock(flags, true);
    166	}
    167}
    168
    169static inline bool
    170__bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write)
    171{
    172	unsigned long kuap = regs->kuap;
    173
    174	if (!is_write || kuap == KUAP_ALL)
    175		return false;
    176	if (kuap == KUAP_NONE)
    177		return true;
    178
    179	/* If faulting address doesn't match unlocked segment, unlock all */
    180	if ((kuap ^ address) & 0xf0000000)
    181		regs->kuap = KUAP_ALL;
    182
    183	return false;
    184}
    185
    186#endif /* CONFIG_PPC_KUAP */
    187
    188#endif /* __ASSEMBLY__ */
    189
    190#endif /* _ASM_POWERPC_BOOK3S_32_KUP_H */