162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 262306a36Sopenharmony_ci#ifndef _ASM_POWERPC_BOOK3S_64_KUP_H 362306a36Sopenharmony_ci#define _ASM_POWERPC_BOOK3S_64_KUP_H 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <linux/const.h> 662306a36Sopenharmony_ci#include <asm/reg.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define AMR_KUAP_BLOCK_READ UL(0x5455555555555555) 962306a36Sopenharmony_ci#define AMR_KUAP_BLOCK_WRITE UL(0xa8aaaaaaaaaaaaaa) 1062306a36Sopenharmony_ci#define AMR_KUEP_BLOCKED UL(0x5455555555555555) 1162306a36Sopenharmony_ci#define AMR_KUAP_BLOCKED (AMR_KUAP_BLOCK_READ | AMR_KUAP_BLOCK_WRITE) 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#ifdef __ASSEMBLY__ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci.macro kuap_user_restore gpr1, gpr2 1662306a36Sopenharmony_ci#if defined(CONFIG_PPC_PKEY) 1762306a36Sopenharmony_ci BEGIN_MMU_FTR_SECTION_NESTED(67) 1862306a36Sopenharmony_ci b 100f // skip_restore_amr 1962306a36Sopenharmony_ci END_MMU_FTR_SECTION_NESTED_IFCLR(MMU_FTR_PKEY, 67) 2062306a36Sopenharmony_ci /* 2162306a36Sopenharmony_ci * AMR and IAMR are going to be different when 2262306a36Sopenharmony_ci * returning to userspace. 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ci ld \gpr1, STACK_REGS_AMR(r1) 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci /* 2762306a36Sopenharmony_ci * If kuap feature is not enabled, do the mtspr 2862306a36Sopenharmony_ci * only if AMR value is different. 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_ci BEGIN_MMU_FTR_SECTION_NESTED(68) 3162306a36Sopenharmony_ci mfspr \gpr2, SPRN_AMR 3262306a36Sopenharmony_ci cmpd \gpr1, \gpr2 3362306a36Sopenharmony_ci beq 99f 3462306a36Sopenharmony_ci END_MMU_FTR_SECTION_NESTED_IFCLR(MMU_FTR_KUAP, 68) 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci isync 3762306a36Sopenharmony_ci mtspr SPRN_AMR, \gpr1 3862306a36Sopenharmony_ci99: 3962306a36Sopenharmony_ci /* 4062306a36Sopenharmony_ci * Restore IAMR only when returning to userspace 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_ci ld \gpr1, STACK_REGS_IAMR(r1) 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci /* 4562306a36Sopenharmony_ci * If kuep feature is not enabled, do the mtspr 4662306a36Sopenharmony_ci * only if IAMR value is different. 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_ci BEGIN_MMU_FTR_SECTION_NESTED(69) 4962306a36Sopenharmony_ci mfspr \gpr2, SPRN_IAMR 5062306a36Sopenharmony_ci cmpd \gpr1, \gpr2 5162306a36Sopenharmony_ci beq 100f 5262306a36Sopenharmony_ci END_MMU_FTR_SECTION_NESTED_IFCLR(MMU_FTR_BOOK3S_KUEP, 69) 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci isync 5562306a36Sopenharmony_ci mtspr SPRN_IAMR, \gpr1 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci100: //skip_restore_amr 5862306a36Sopenharmony_ci /* No isync required, see kuap_user_restore() */ 5962306a36Sopenharmony_ci#endif 6062306a36Sopenharmony_ci.endm 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci.macro kuap_kernel_restore gpr1, gpr2 6362306a36Sopenharmony_ci#if defined(CONFIG_PPC_PKEY) 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci BEGIN_MMU_FTR_SECTION_NESTED(67) 6662306a36Sopenharmony_ci /* 6762306a36Sopenharmony_ci * AMR is going to be mostly the same since we are 6862306a36Sopenharmony_ci * returning to the kernel. Compare and do a mtspr. 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_ci ld \gpr2, STACK_REGS_AMR(r1) 7162306a36Sopenharmony_ci mfspr \gpr1, SPRN_AMR 7262306a36Sopenharmony_ci cmpd \gpr1, \gpr2 7362306a36Sopenharmony_ci beq 100f 7462306a36Sopenharmony_ci isync 7562306a36Sopenharmony_ci mtspr SPRN_AMR, \gpr2 7662306a36Sopenharmony_ci /* 7762306a36Sopenharmony_ci * No isync required, see kuap_restore_amr() 7862306a36Sopenharmony_ci * No need to restore IAMR when returning to kernel space. 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_ci100: 8162306a36Sopenharmony_ci END_MMU_FTR_SECTION_NESTED_IFSET(MMU_FTR_KUAP, 67) 8262306a36Sopenharmony_ci#endif 8362306a36Sopenharmony_ci.endm 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci#ifdef CONFIG_PPC_KUAP 8662306a36Sopenharmony_ci.macro kuap_check_amr gpr1, gpr2 8762306a36Sopenharmony_ci#ifdef CONFIG_PPC_KUAP_DEBUG 8862306a36Sopenharmony_ci BEGIN_MMU_FTR_SECTION_NESTED(67) 8962306a36Sopenharmony_ci mfspr \gpr1, SPRN_AMR 9062306a36Sopenharmony_ci /* Prevent access to userspace using any key values */ 9162306a36Sopenharmony_ci LOAD_REG_IMMEDIATE(\gpr2, AMR_KUAP_BLOCKED) 9262306a36Sopenharmony_ci999: tdne \gpr1, \gpr2 9362306a36Sopenharmony_ci EMIT_WARN_ENTRY 999b, __FILE__, __LINE__, (BUGFLAG_WARNING | BUGFLAG_ONCE) 9462306a36Sopenharmony_ci END_MMU_FTR_SECTION_NESTED_IFSET(MMU_FTR_KUAP, 67) 9562306a36Sopenharmony_ci#endif 9662306a36Sopenharmony_ci.endm 9762306a36Sopenharmony_ci#endif 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci/* 10062306a36Sopenharmony_ci * if (pkey) { 10162306a36Sopenharmony_ci * 10262306a36Sopenharmony_ci * save AMR -> stack; 10362306a36Sopenharmony_ci * if (kuap) { 10462306a36Sopenharmony_ci * if (AMR != BLOCKED) 10562306a36Sopenharmony_ci * KUAP_BLOCKED -> AMR; 10662306a36Sopenharmony_ci * } 10762306a36Sopenharmony_ci * if (from_user) { 10862306a36Sopenharmony_ci * save IAMR -> stack; 10962306a36Sopenharmony_ci * if (kuep) { 11062306a36Sopenharmony_ci * KUEP_BLOCKED ->IAMR 11162306a36Sopenharmony_ci * } 11262306a36Sopenharmony_ci * } 11362306a36Sopenharmony_ci * return; 11462306a36Sopenharmony_ci * } 11562306a36Sopenharmony_ci * 11662306a36Sopenharmony_ci * if (kuap) { 11762306a36Sopenharmony_ci * if (from_kernel) { 11862306a36Sopenharmony_ci * save AMR -> stack; 11962306a36Sopenharmony_ci * if (AMR != BLOCKED) 12062306a36Sopenharmony_ci * KUAP_BLOCKED -> AMR; 12162306a36Sopenharmony_ci * } 12262306a36Sopenharmony_ci * 12362306a36Sopenharmony_ci * } 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_ci.macro kuap_save_amr_and_lock gpr1, gpr2, use_cr, msr_pr_cr 12662306a36Sopenharmony_ci#if defined(CONFIG_PPC_PKEY) 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* 12962306a36Sopenharmony_ci * if both pkey and kuap is disabled, nothing to do 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_ci BEGIN_MMU_FTR_SECTION_NESTED(68) 13262306a36Sopenharmony_ci b 100f // skip_save_amr 13362306a36Sopenharmony_ci END_MMU_FTR_SECTION_NESTED_IFCLR(MMU_FTR_PKEY | MMU_FTR_KUAP, 68) 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* 13662306a36Sopenharmony_ci * if pkey is disabled and we are entering from userspace 13762306a36Sopenharmony_ci * don't do anything. 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_ci BEGIN_MMU_FTR_SECTION_NESTED(67) 14062306a36Sopenharmony_ci .ifnb \msr_pr_cr 14162306a36Sopenharmony_ci /* 14262306a36Sopenharmony_ci * Without pkey we are not changing AMR outside the kernel 14362306a36Sopenharmony_ci * hence skip this completely. 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_ci bne \msr_pr_cr, 100f // from userspace 14662306a36Sopenharmony_ci .endif 14762306a36Sopenharmony_ci END_MMU_FTR_SECTION_NESTED_IFCLR(MMU_FTR_PKEY, 67) 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* 15062306a36Sopenharmony_ci * pkey is enabled or pkey is disabled but entering from kernel 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_ci mfspr \gpr1, SPRN_AMR 15362306a36Sopenharmony_ci std \gpr1, STACK_REGS_AMR(r1) 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* 15662306a36Sopenharmony_ci * update kernel AMR with AMR_KUAP_BLOCKED only 15762306a36Sopenharmony_ci * if KUAP feature is enabled 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_ci BEGIN_MMU_FTR_SECTION_NESTED(69) 16062306a36Sopenharmony_ci LOAD_REG_IMMEDIATE(\gpr2, AMR_KUAP_BLOCKED) 16162306a36Sopenharmony_ci cmpd \use_cr, \gpr1, \gpr2 16262306a36Sopenharmony_ci beq \use_cr, 102f 16362306a36Sopenharmony_ci /* 16462306a36Sopenharmony_ci * We don't isync here because we very recently entered via an interrupt 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_ci mtspr SPRN_AMR, \gpr2 16762306a36Sopenharmony_ci isync 16862306a36Sopenharmony_ci102: 16962306a36Sopenharmony_ci END_MMU_FTR_SECTION_NESTED_IFSET(MMU_FTR_KUAP, 69) 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* 17262306a36Sopenharmony_ci * if entering from kernel we don't need save IAMR 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_ci .ifnb \msr_pr_cr 17562306a36Sopenharmony_ci beq \msr_pr_cr, 100f // from kernel space 17662306a36Sopenharmony_ci mfspr \gpr1, SPRN_IAMR 17762306a36Sopenharmony_ci std \gpr1, STACK_REGS_IAMR(r1) 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* 18062306a36Sopenharmony_ci * update kernel IAMR with AMR_KUEP_BLOCKED only 18162306a36Sopenharmony_ci * if KUEP feature is enabled 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_ci BEGIN_MMU_FTR_SECTION_NESTED(70) 18462306a36Sopenharmony_ci LOAD_REG_IMMEDIATE(\gpr2, AMR_KUEP_BLOCKED) 18562306a36Sopenharmony_ci mtspr SPRN_IAMR, \gpr2 18662306a36Sopenharmony_ci isync 18762306a36Sopenharmony_ci END_MMU_FTR_SECTION_NESTED_IFSET(MMU_FTR_BOOK3S_KUEP, 70) 18862306a36Sopenharmony_ci .endif 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci100: // skip_save_amr 19162306a36Sopenharmony_ci#endif 19262306a36Sopenharmony_ci.endm 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci#else /* !__ASSEMBLY__ */ 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci#include <linux/jump_label.h> 19762306a36Sopenharmony_ci#include <linux/sched.h> 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ciDECLARE_STATIC_KEY_FALSE(uaccess_flush_key); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci#ifdef CONFIG_PPC_PKEY 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ciextern u64 __ro_after_init default_uamor; 20462306a36Sopenharmony_ciextern u64 __ro_after_init default_amr; 20562306a36Sopenharmony_ciextern u64 __ro_after_init default_iamr; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci#include <asm/mmu.h> 20862306a36Sopenharmony_ci#include <asm/ptrace.h> 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci/* usage of kthread_use_mm() should inherit the 21162306a36Sopenharmony_ci * AMR value of the operating address space. But, the AMR value is 21262306a36Sopenharmony_ci * thread-specific and we inherit the address space and not thread 21362306a36Sopenharmony_ci * access restrictions. Because of this ignore AMR value when accessing 21462306a36Sopenharmony_ci * userspace via kernel thread. 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_cistatic __always_inline u64 current_thread_amr(void) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci if (current->thread.regs) 21962306a36Sopenharmony_ci return current->thread.regs->amr; 22062306a36Sopenharmony_ci return default_amr; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic __always_inline u64 current_thread_iamr(void) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci if (current->thread.regs) 22662306a36Sopenharmony_ci return current->thread.regs->iamr; 22762306a36Sopenharmony_ci return default_iamr; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci#endif /* CONFIG_PPC_PKEY */ 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci#ifdef CONFIG_PPC_KUAP 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic __always_inline void kuap_user_restore(struct pt_regs *regs) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci bool restore_amr = false, restore_iamr = false; 23662306a36Sopenharmony_ci unsigned long amr, iamr; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (!mmu_has_feature(MMU_FTR_PKEY)) 23962306a36Sopenharmony_ci return; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (!mmu_has_feature(MMU_FTR_KUAP)) { 24262306a36Sopenharmony_ci amr = mfspr(SPRN_AMR); 24362306a36Sopenharmony_ci if (amr != regs->amr) 24462306a36Sopenharmony_ci restore_amr = true; 24562306a36Sopenharmony_ci } else { 24662306a36Sopenharmony_ci restore_amr = true; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (!mmu_has_feature(MMU_FTR_BOOK3S_KUEP)) { 25062306a36Sopenharmony_ci iamr = mfspr(SPRN_IAMR); 25162306a36Sopenharmony_ci if (iamr != regs->iamr) 25262306a36Sopenharmony_ci restore_iamr = true; 25362306a36Sopenharmony_ci } else { 25462306a36Sopenharmony_ci restore_iamr = true; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (restore_amr || restore_iamr) { 25962306a36Sopenharmony_ci isync(); 26062306a36Sopenharmony_ci if (restore_amr) 26162306a36Sopenharmony_ci mtspr(SPRN_AMR, regs->amr); 26262306a36Sopenharmony_ci if (restore_iamr) 26362306a36Sopenharmony_ci mtspr(SPRN_IAMR, regs->iamr); 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci /* 26662306a36Sopenharmony_ci * No isync required here because we are about to rfi 26762306a36Sopenharmony_ci * back to previous context before any user accesses 26862306a36Sopenharmony_ci * would be made, which is a CSI. 26962306a36Sopenharmony_ci */ 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic __always_inline void __kuap_kernel_restore(struct pt_regs *regs, unsigned long amr) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci if (likely(regs->amr == amr)) 27562306a36Sopenharmony_ci return; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci isync(); 27862306a36Sopenharmony_ci mtspr(SPRN_AMR, regs->amr); 27962306a36Sopenharmony_ci /* 28062306a36Sopenharmony_ci * No isync required here because we are about to rfi 28162306a36Sopenharmony_ci * back to previous context before any user accesses 28262306a36Sopenharmony_ci * would be made, which is a CSI. 28362306a36Sopenharmony_ci * 28462306a36Sopenharmony_ci * No need to restore IAMR when returning to kernel space. 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic __always_inline unsigned long __kuap_get_and_assert_locked(void) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci unsigned long amr = mfspr(SPRN_AMR); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_PPC_KUAP_DEBUG)) /* kuap_check_amr() */ 29362306a36Sopenharmony_ci WARN_ON_ONCE(amr != AMR_KUAP_BLOCKED); 29462306a36Sopenharmony_ci return amr; 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci#define __kuap_get_and_assert_locked __kuap_get_and_assert_locked 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci/* __kuap_lock() not required, book3s/64 does that in ASM */ 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci/* 30162306a36Sopenharmony_ci * We support individually allowing read or write, but we don't support nesting 30262306a36Sopenharmony_ci * because that would require an expensive read/modify write of the AMR. 30362306a36Sopenharmony_ci */ 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic __always_inline unsigned long get_kuap(void) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci /* 30862306a36Sopenharmony_ci * We return AMR_KUAP_BLOCKED when we don't support KUAP because 30962306a36Sopenharmony_ci * prevent_user_access_return needs to return AMR_KUAP_BLOCKED to 31062306a36Sopenharmony_ci * cause restore_user_access to do a flush. 31162306a36Sopenharmony_ci * 31262306a36Sopenharmony_ci * This has no effect in terms of actually blocking things on hash, 31362306a36Sopenharmony_ci * so it doesn't break anything. 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_ci if (!mmu_has_feature(MMU_FTR_KUAP)) 31662306a36Sopenharmony_ci return AMR_KUAP_BLOCKED; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci return mfspr(SPRN_AMR); 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic __always_inline void set_kuap(unsigned long value) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci if (!mmu_has_feature(MMU_FTR_KUAP)) 32462306a36Sopenharmony_ci return; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci /* 32762306a36Sopenharmony_ci * ISA v3.0B says we need a CSI (Context Synchronising Instruction) both 32862306a36Sopenharmony_ci * before and after the move to AMR. See table 6 on page 1134. 32962306a36Sopenharmony_ci */ 33062306a36Sopenharmony_ci isync(); 33162306a36Sopenharmony_ci mtspr(SPRN_AMR, value); 33262306a36Sopenharmony_ci isync(); 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic __always_inline bool 33662306a36Sopenharmony_ci__bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci /* 33962306a36Sopenharmony_ci * For radix this will be a storage protection fault (DSISR_PROTFAULT). 34062306a36Sopenharmony_ci * For hash this will be a key fault (DSISR_KEYFAULT) 34162306a36Sopenharmony_ci */ 34262306a36Sopenharmony_ci /* 34362306a36Sopenharmony_ci * We do have exception table entry, but accessing the 34462306a36Sopenharmony_ci * userspace results in fault. This could be because we 34562306a36Sopenharmony_ci * didn't unlock the AMR or access is denied by userspace 34662306a36Sopenharmony_ci * using a key value that blocks access. We are only interested 34762306a36Sopenharmony_ci * in catching the use case of accessing without unlocking 34862306a36Sopenharmony_ci * the AMR. Hence check for BLOCK_WRITE/READ against AMR. 34962306a36Sopenharmony_ci */ 35062306a36Sopenharmony_ci if (is_write) { 35162306a36Sopenharmony_ci return (regs->amr & AMR_KUAP_BLOCK_WRITE) == AMR_KUAP_BLOCK_WRITE; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci return (regs->amr & AMR_KUAP_BLOCK_READ) == AMR_KUAP_BLOCK_READ; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic __always_inline void allow_user_access(void __user *to, const void __user *from, 35762306a36Sopenharmony_ci unsigned long size, unsigned long dir) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci unsigned long thread_amr = 0; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci // This is written so we can resolve to a single case at build time 36262306a36Sopenharmony_ci BUILD_BUG_ON(!__builtin_constant_p(dir)); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (mmu_has_feature(MMU_FTR_PKEY)) 36562306a36Sopenharmony_ci thread_amr = current_thread_amr(); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (dir == KUAP_READ) 36862306a36Sopenharmony_ci set_kuap(thread_amr | AMR_KUAP_BLOCK_WRITE); 36962306a36Sopenharmony_ci else if (dir == KUAP_WRITE) 37062306a36Sopenharmony_ci set_kuap(thread_amr | AMR_KUAP_BLOCK_READ); 37162306a36Sopenharmony_ci else if (dir == KUAP_READ_WRITE) 37262306a36Sopenharmony_ci set_kuap(thread_amr); 37362306a36Sopenharmony_ci else 37462306a36Sopenharmony_ci BUILD_BUG(); 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci#else /* CONFIG_PPC_KUAP */ 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic __always_inline unsigned long get_kuap(void) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci return AMR_KUAP_BLOCKED; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic __always_inline void set_kuap(unsigned long value) { } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic __always_inline void allow_user_access(void __user *to, const void __user *from, 38762306a36Sopenharmony_ci unsigned long size, unsigned long dir) 38862306a36Sopenharmony_ci{ } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci#endif /* !CONFIG_PPC_KUAP */ 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic __always_inline void prevent_user_access(unsigned long dir) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci set_kuap(AMR_KUAP_BLOCKED); 39562306a36Sopenharmony_ci if (static_branch_unlikely(&uaccess_flush_key)) 39662306a36Sopenharmony_ci do_uaccess_flush(); 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic __always_inline unsigned long prevent_user_access_return(void) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci unsigned long flags = get_kuap(); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci set_kuap(AMR_KUAP_BLOCKED); 40462306a36Sopenharmony_ci if (static_branch_unlikely(&uaccess_flush_key)) 40562306a36Sopenharmony_ci do_uaccess_flush(); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci return flags; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic __always_inline void restore_user_access(unsigned long flags) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci set_kuap(flags); 41362306a36Sopenharmony_ci if (static_branch_unlikely(&uaccess_flush_key) && flags == AMR_KUAP_BLOCKED) 41462306a36Sopenharmony_ci do_uaccess_flush(); 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci#endif /* __ASSEMBLY__ */ 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci#endif /* _ASM_POWERPC_BOOK3S_64_KUP_H */ 419