Path: blob/master/arch/powerpc/include/asm/book3s/32/kup.h
26519 views
/* SPDX-License-Identifier: GPL-2.0 */1#ifndef _ASM_POWERPC_BOOK3S_32_KUP_H2#define _ASM_POWERPC_BOOK3S_32_KUP_H34#include <asm/bug.h>5#include <asm/book3s/32/mmu-hash.h>6#include <asm/mmu.h>7#include <asm/synch.h>89#ifndef __ASSEMBLY__1011#ifdef CONFIG_PPC_KUAP1213#include <linux/sched.h>1415#define KUAP_NONE (~0UL)1617static __always_inline void kuap_lock_one(unsigned long addr)18{19mtsr(mfsr(addr) | SR_KS, addr);20isync(); /* Context sync required after mtsr() */21}2223static __always_inline void kuap_unlock_one(unsigned long addr)24{25mtsr(mfsr(addr) & ~SR_KS, addr);26isync(); /* Context sync required after mtsr() */27}2829static __always_inline void uaccess_begin_32s(unsigned long addr)30{31unsigned long tmp;3233asm volatile(ASM_MMU_FTR_IFSET(34"mfsrin %0, %1;"35"rlwinm %0, %0, 0, %2;"36"mtsrin %0, %1;"37"isync", "", %3)38: "=&r"(tmp)39: "r"(addr), "i"(~SR_KS), "i"(MMU_FTR_KUAP)40: "memory");41}4243static __always_inline void uaccess_end_32s(unsigned long addr)44{45unsigned long tmp;4647asm volatile(ASM_MMU_FTR_IFSET(48"mfsrin %0, %1;"49"oris %0, %0, %2;"50"mtsrin %0, %1;"51"isync", "", %3)52: "=&r"(tmp)53: "r"(addr), "i"(SR_KS >> 16), "i"(MMU_FTR_KUAP)54: "memory");55}5657static __always_inline void __kuap_save_and_lock(struct pt_regs *regs)58{59unsigned long kuap = current->thread.kuap;6061regs->kuap = kuap;62if (unlikely(kuap == KUAP_NONE))63return;6465current->thread.kuap = KUAP_NONE;66kuap_lock_one(kuap);67}68#define __kuap_save_and_lock __kuap_save_and_lock6970static __always_inline void kuap_user_restore(struct pt_regs *regs)71{72}7374static __always_inline void __kuap_kernel_restore(struct pt_regs *regs, unsigned long kuap)75{76if (unlikely(kuap != KUAP_NONE)) {77current->thread.kuap = KUAP_NONE;78kuap_lock_one(kuap);79}8081if (likely(regs->kuap == KUAP_NONE))82return;8384current->thread.kuap = regs->kuap;8586kuap_unlock_one(regs->kuap);87}8889static __always_inline unsigned long __kuap_get_and_assert_locked(void)90{91unsigned long kuap = current->thread.kuap;9293WARN_ON_ONCE(IS_ENABLED(CONFIG_PPC_KUAP_DEBUG) && kuap != KUAP_NONE);9495return kuap;96}97#define __kuap_get_and_assert_locked __kuap_get_and_assert_locked9899static __always_inline void allow_user_access(void __user *to, const void __user *from,100u32 size, unsigned long dir)101{102BUILD_BUG_ON(!__builtin_constant_p(dir));103104if (!(dir & KUAP_WRITE))105return;106107current->thread.kuap = (__force u32)to;108uaccess_begin_32s((__force u32)to);109}110111static __always_inline void prevent_user_access(unsigned long dir)112{113u32 kuap = current->thread.kuap;114115BUILD_BUG_ON(!__builtin_constant_p(dir));116117if (!(dir & KUAP_WRITE))118return;119120current->thread.kuap = KUAP_NONE;121uaccess_end_32s(kuap);122}123124static __always_inline unsigned long prevent_user_access_return(void)125{126unsigned long flags = current->thread.kuap;127128if (flags != KUAP_NONE) {129current->thread.kuap = KUAP_NONE;130uaccess_end_32s(flags);131}132133return flags;134}135136static __always_inline void restore_user_access(unsigned long flags)137{138if (flags != KUAP_NONE) {139current->thread.kuap = flags;140uaccess_begin_32s(flags);141}142}143144static __always_inline bool145__bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write)146{147unsigned long kuap = regs->kuap;148149if (!is_write)150return false;151if (kuap == KUAP_NONE)152return true;153154/*155* If faulting address doesn't match unlocked segment, change segment.156* In case of unaligned store crossing two segments, emulate store.157*/158if ((kuap ^ address) & 0xf0000000) {159if (!(kuap & 0x0fffffff) && address > kuap - 4 && fix_alignment(regs)) {160regs_add_return_ip(regs, 4);161emulate_single_step(regs);162} else {163regs->kuap = address;164}165}166167return false;168}169170#endif /* CONFIG_PPC_KUAP */171172#endif /* __ASSEMBLY__ */173174#endif /* _ASM_POWERPC_BOOK3S_32_KUP_H */175176177