131ed2b13SChristophe Leroy /* SPDX-License-Identifier: GPL-2.0 */ 231ed2b13SChristophe Leroy #ifndef _ASM_POWERPC_BOOK3S_32_KUP_H 331ed2b13SChristophe Leroy #define _ASM_POWERPC_BOOK3S_32_KUP_H 431ed2b13SChristophe Leroy 574016701SChristophe Leroy #include <asm/bug.h> 631ed2b13SChristophe Leroy #include <asm/book3s/32/mmu-hash.h> 731ed2b13SChristophe Leroy 831ed2b13SChristophe Leroy #ifdef __ASSEMBLY__ 931ed2b13SChristophe Leroy 1031ed2b13SChristophe Leroy .macro kuep_update_sr gpr1, gpr2 /* NEVER use r0 as gpr2 due to addis */ 1131ed2b13SChristophe Leroy 101: mtsrin \gpr1, \gpr2 1231ed2b13SChristophe Leroy addi \gpr1, \gpr1, 0x111 /* next VSID */ 1331ed2b13SChristophe Leroy rlwinm \gpr1, \gpr1, 0, 0xf0ffffff /* clear VSID overflow */ 1431ed2b13SChristophe Leroy addis \gpr2, \gpr2, 0x1000 /* address of next segment */ 1531ed2b13SChristophe Leroy bdnz 101b 1631ed2b13SChristophe Leroy isync 1731ed2b13SChristophe Leroy .endm 1831ed2b13SChristophe Leroy 1931ed2b13SChristophe Leroy .macro kuep_lock gpr1, gpr2 2031ed2b13SChristophe Leroy #ifdef CONFIG_PPC_KUEP 2131ed2b13SChristophe Leroy li \gpr1, NUM_USER_SEGMENTS 2231ed2b13SChristophe Leroy li \gpr2, 0 2331ed2b13SChristophe Leroy mtctr \gpr1 2431ed2b13SChristophe Leroy mfsrin \gpr1, \gpr2 2531ed2b13SChristophe Leroy oris \gpr1, \gpr1, SR_NX@h /* set Nx */ 2631ed2b13SChristophe Leroy kuep_update_sr \gpr1, \gpr2 2731ed2b13SChristophe Leroy #endif 2831ed2b13SChristophe Leroy .endm 2931ed2b13SChristophe Leroy 3031ed2b13SChristophe Leroy .macro kuep_unlock gpr1, gpr2 3131ed2b13SChristophe Leroy #ifdef CONFIG_PPC_KUEP 3231ed2b13SChristophe Leroy li \gpr1, NUM_USER_SEGMENTS 3331ed2b13SChristophe Leroy li \gpr2, 0 3431ed2b13SChristophe Leroy mtctr \gpr1 3531ed2b13SChristophe Leroy mfsrin \gpr1, \gpr2 3631ed2b13SChristophe Leroy rlwinm \gpr1, \gpr1, 0, ~SR_NX /* Clear Nx */ 3731ed2b13SChristophe Leroy kuep_update_sr \gpr1, \gpr2 3831ed2b13SChristophe Leroy #endif 3931ed2b13SChristophe Leroy .endm 4031ed2b13SChristophe Leroy 41a68c31fcSChristophe Leroy #ifdef CONFIG_PPC_KUAP 42a68c31fcSChristophe Leroy 43a68c31fcSChristophe Leroy .macro kuap_update_sr gpr1, gpr2, gpr3 /* NEVER use r0 as gpr2 due to addis */ 44a68c31fcSChristophe Leroy 101: mtsrin \gpr1, \gpr2 45a68c31fcSChristophe Leroy addi \gpr1, \gpr1, 0x111 /* next VSID */ 46a68c31fcSChristophe Leroy rlwinm \gpr1, \gpr1, 0, 0xf0ffffff /* clear VSID overflow */ 47a68c31fcSChristophe Leroy addis \gpr2, \gpr2, 0x1000 /* address of next segment */ 48a68c31fcSChristophe Leroy cmplw \gpr2, \gpr3 49a68c31fcSChristophe Leroy blt- 101b 50a68c31fcSChristophe Leroy isync 51a68c31fcSChristophe Leroy .endm 52a68c31fcSChristophe Leroy 53a68c31fcSChristophe Leroy .macro kuap_save_and_lock sp, thread, gpr1, gpr2, gpr3 54a68c31fcSChristophe Leroy lwz \gpr2, KUAP(\thread) 55a68c31fcSChristophe Leroy rlwinm. \gpr3, \gpr2, 28, 0xf0000000 56a68c31fcSChristophe Leroy stw \gpr2, STACK_REGS_KUAP(\sp) 57a68c31fcSChristophe Leroy beq+ 102f 58a68c31fcSChristophe Leroy li \gpr1, 0 59a68c31fcSChristophe Leroy stw \gpr1, KUAP(\thread) 60a68c31fcSChristophe Leroy mfsrin \gpr1, \gpr2 61a68c31fcSChristophe Leroy oris \gpr1, \gpr1, SR_KS@h /* set Ks */ 62a68c31fcSChristophe Leroy kuap_update_sr \gpr1, \gpr2, \gpr3 63a68c31fcSChristophe Leroy 102: 64a68c31fcSChristophe Leroy .endm 65a68c31fcSChristophe Leroy 66a68c31fcSChristophe Leroy .macro kuap_restore sp, current, gpr1, gpr2, gpr3 67a68c31fcSChristophe Leroy lwz \gpr2, STACK_REGS_KUAP(\sp) 68a68c31fcSChristophe Leroy rlwinm. \gpr3, \gpr2, 28, 0xf0000000 69a68c31fcSChristophe Leroy stw \gpr2, THREAD + KUAP(\current) 70a68c31fcSChristophe Leroy beq+ 102f 71a68c31fcSChristophe Leroy mfsrin \gpr1, \gpr2 72a68c31fcSChristophe Leroy rlwinm \gpr1, \gpr1, 0, ~SR_KS /* Clear Ks */ 73a68c31fcSChristophe Leroy kuap_update_sr \gpr1, \gpr2, \gpr3 74a68c31fcSChristophe Leroy 102: 75a68c31fcSChristophe Leroy .endm 76a68c31fcSChristophe Leroy 77a68c31fcSChristophe Leroy .macro kuap_check current, gpr 78a68c31fcSChristophe Leroy #ifdef CONFIG_PPC_KUAP_DEBUG 7974016701SChristophe Leroy lwz \gpr, THREAD + KUAP(\current) 80a68c31fcSChristophe Leroy 999: twnei \gpr, 0 81a68c31fcSChristophe Leroy EMIT_BUG_ENTRY 999b, __FILE__, __LINE__, (BUGFLAG_WARNING | BUGFLAG_ONCE) 82a68c31fcSChristophe Leroy #endif 83a68c31fcSChristophe Leroy .endm 84a68c31fcSChristophe Leroy 85a68c31fcSChristophe Leroy #endif /* CONFIG_PPC_KUAP */ 86a68c31fcSChristophe Leroy 87a68c31fcSChristophe Leroy #else /* !__ASSEMBLY__ */ 88a68c31fcSChristophe Leroy 89a68c31fcSChristophe Leroy #ifdef CONFIG_PPC_KUAP 90a68c31fcSChristophe Leroy 91a68c31fcSChristophe Leroy #include <linux/sched.h> 92a68c31fcSChristophe Leroy 93a68c31fcSChristophe Leroy static inline void kuap_update_sr(u32 sr, u32 addr, u32 end) 94a68c31fcSChristophe Leroy { 95d10f60aeSChristophe Leroy addr &= 0xf0000000; /* align addr to start of segment */ 96a68c31fcSChristophe Leroy barrier(); /* make sure thread.kuap is updated before playing with SRs */ 97a68c31fcSChristophe Leroy while (addr < end) { 98179ae57dSChristophe Leroy mtsr(sr, addr); 99a68c31fcSChristophe Leroy sr += 0x111; /* next VSID */ 100a68c31fcSChristophe Leroy sr &= 0xf0ffffff; /* clear VSID overflow */ 101a68c31fcSChristophe Leroy addr += 0x10000000; /* address of next segment */ 102a68c31fcSChristophe Leroy } 103179ae57dSChristophe Leroy isync(); /* Context sync required after mtsr() */ 104a68c31fcSChristophe Leroy } 105a68c31fcSChristophe Leroy 1061d8f739bSChristophe Leroy static __always_inline void allow_user_access(void __user *to, const void __user *from, 1071d8f739bSChristophe Leroy u32 size, unsigned long dir) 108a68c31fcSChristophe Leroy { 109a68c31fcSChristophe Leroy u32 addr, end; 110a68c31fcSChristophe Leroy 1111d8f739bSChristophe Leroy BUILD_BUG_ON(!__builtin_constant_p(dir)); 1124fe5cda9SChristophe Leroy BUILD_BUG_ON(dir & ~KUAP_READ_WRITE); 113bedb4dbeSChristophe Leroy 1141d8f739bSChristophe Leroy if (!(dir & KUAP_WRITE)) 115a68c31fcSChristophe Leroy return; 116a68c31fcSChristophe Leroy 117a68c31fcSChristophe Leroy addr = (__force u32)to; 118a68c31fcSChristophe Leroy 11988f8c080SChristophe Leroy if (unlikely(addr >= TASK_SIZE || !size)) 120a68c31fcSChristophe Leroy return; 121a68c31fcSChristophe Leroy 122a68c31fcSChristophe Leroy end = min(addr + size, TASK_SIZE); 123bedb4dbeSChristophe Leroy 124a68c31fcSChristophe Leroy current->thread.kuap = (addr & 0xf0000000) | ((((end - 1) >> 28) + 1) & 0xf); 125179ae57dSChristophe Leroy kuap_update_sr(mfsr(addr) & ~SR_KS, addr, end); /* Clear Ks */ 126a68c31fcSChristophe Leroy } 127a68c31fcSChristophe Leroy 1281d8f739bSChristophe Leroy static __always_inline void prevent_user_access(void __user *to, const void __user *from, 1291d8f739bSChristophe Leroy u32 size, unsigned long dir) 130a68c31fcSChristophe Leroy { 13188f8c080SChristophe Leroy u32 addr, end; 132a68c31fcSChristophe Leroy 1331d8f739bSChristophe Leroy BUILD_BUG_ON(!__builtin_constant_p(dir)); 134bedb4dbeSChristophe Leroy 1354fe5cda9SChristophe Leroy if (dir & KUAP_CURRENT_WRITE) { 136bedb4dbeSChristophe Leroy u32 kuap = current->thread.kuap; 137bedb4dbeSChristophe Leroy 138bedb4dbeSChristophe Leroy if (unlikely(!kuap)) 1391d8f739bSChristophe Leroy return; 1401d8f739bSChristophe Leroy 141bedb4dbeSChristophe Leroy addr = kuap & 0xf0000000; 142bedb4dbeSChristophe Leroy end = kuap << 28; 143bedb4dbeSChristophe Leroy } else if (dir & KUAP_WRITE) { 14488f8c080SChristophe Leroy addr = (__force u32)to; 145bedb4dbeSChristophe Leroy end = min(addr + size, TASK_SIZE); 14688f8c080SChristophe Leroy 14788f8c080SChristophe Leroy if (unlikely(addr >= TASK_SIZE || !size)) 148a68c31fcSChristophe Leroy return; 149bedb4dbeSChristophe Leroy } else { 150bedb4dbeSChristophe Leroy return; 151bedb4dbeSChristophe Leroy } 152a68c31fcSChristophe Leroy 153a68c31fcSChristophe Leroy current->thread.kuap = 0; 154179ae57dSChristophe Leroy kuap_update_sr(mfsr(addr) | SR_KS, addr, end); /* set Ks */ 155a68c31fcSChristophe Leroy } 156a68c31fcSChristophe Leroy 1573d7dfd63SChristophe Leroy static inline unsigned long prevent_user_access_return(void) 1583d7dfd63SChristophe Leroy { 1593d7dfd63SChristophe Leroy unsigned long flags = current->thread.kuap; 1603d7dfd63SChristophe Leroy unsigned long addr = flags & 0xf0000000; 1613d7dfd63SChristophe Leroy unsigned long end = flags << 28; 1623d7dfd63SChristophe Leroy void __user *to = (__force void __user *)addr; 1633d7dfd63SChristophe Leroy 1643d7dfd63SChristophe Leroy if (flags) 1653d7dfd63SChristophe Leroy prevent_user_access(to, to, end - addr, KUAP_READ_WRITE); 1663d7dfd63SChristophe Leroy 1673d7dfd63SChristophe Leroy return flags; 1683d7dfd63SChristophe Leroy } 1693d7dfd63SChristophe Leroy 1703d7dfd63SChristophe Leroy static inline void restore_user_access(unsigned long flags) 1713d7dfd63SChristophe Leroy { 1723d7dfd63SChristophe Leroy unsigned long addr = flags & 0xf0000000; 1733d7dfd63SChristophe Leroy unsigned long end = flags << 28; 1743d7dfd63SChristophe Leroy void __user *to = (__force void __user *)addr; 1753d7dfd63SChristophe Leroy 1763d7dfd63SChristophe Leroy if (flags) 1773d7dfd63SChristophe Leroy allow_user_access(to, to, end - addr, KUAP_READ_WRITE); 1783d7dfd63SChristophe Leroy } 1793d7dfd63SChristophe Leroy 180475c8749SAneesh Kumar K.V static inline bool 181475c8749SAneesh Kumar K.V bad_kuap_fault(struct pt_regs *regs, unsigned long address, bool is_write) 182a68c31fcSChristophe Leroy { 1836ec20aa2SChristophe Leroy unsigned long begin = regs->kuap & 0xf0000000; 1846ec20aa2SChristophe Leroy unsigned long end = regs->kuap << 28; 1856ec20aa2SChristophe Leroy 1863dc12dfeSChristophe Leroy return is_write && (address < begin || address >= end); 187a68c31fcSChristophe Leroy } 188a68c31fcSChristophe Leroy 189a68c31fcSChristophe Leroy #endif /* CONFIG_PPC_KUAP */ 190a68c31fcSChristophe Leroy 19131ed2b13SChristophe Leroy #endif /* __ASSEMBLY__ */ 19231ed2b13SChristophe Leroy 19331ed2b13SChristophe Leroy #endif /* _ASM_POWERPC_BOOK3S_32_KUP_H */ 194