xref: /linux/arch/powerpc/include/asm/book3s/32/kup.h (revision 179ae57dbad1b9a83eec376aa44d54fc24352e37)
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