[RFC PATCH v2 11/11] powerpc/book3s32: Implement Kernel Userspace Access Protection

Christophe Leroy christophe.leroy at c-s.fr
Wed Nov 28 20:27:24 AEDT 2018


This patch implements Kernel Userspace Access Protection for
book3s/32.

Due to limitations of the processor page protection capabilities,
the protection is only against writing. read protection cannot be
achieved using page protection.

In order to provide the protection, Ku and Ks keys are modified in
Userspace Segment registers, and different PP bits are used to:

PP01 provides RW for Key 0 and RO for Key 1
PP10 provides RW for all
PP11 provides RO for all

Today PP10 is used for RW pages and PP11 for RO pages. This patch
modifies page protection to PP01 for RW pages.

Then segment registers are set to Ku 0 and Ks 1. When kernel needs
to write to RW pages, the associated segment register is changed to
Ks 0 in order to allow write access to the kernel.

In order to avoid having the read all segment registers when
locking/unlocking the access, some data is kept in the thread_struct
and saved on stack on exceptions. The field identifies both the
first unlocked segment and the first segment following the last
unlocked one. When no segment is unlocked, it contains value 0.

Signed-off-by: Christophe Leroy <christophe.leroy at c-s.fr>
---
 arch/powerpc/include/asm/book3s/32/kup.h | 98 ++++++++++++++++++++++++++++++++
 arch/powerpc/include/asm/kup.h           |  3 +
 arch/powerpc/kernel/head_32.S            |  2 +-
 arch/powerpc/mm/ppc_mmu_32.c             | 10 ++++
 arch/powerpc/platforms/Kconfig.cputype   |  1 +
 5 files changed, 113 insertions(+), 1 deletion(-)
 create mode 100644 arch/powerpc/include/asm/book3s/32/kup.h

diff --git a/arch/powerpc/include/asm/book3s/32/kup.h b/arch/powerpc/include/asm/book3s/32/kup.h
new file mode 100644
index 000000000000..7455ecaab3f9
--- /dev/null
+++ b/arch/powerpc/include/asm/book3s/32/kup.h
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_POWERPC_BOOK3S_32_KUP_H
+#define _ASM_POWERPC_BOOK3S_32_KUP_H
+
+#ifdef CONFIG_PPC_KUAP
+#define LOCK_USER_ACCESS(val, sp, sr, srmax, thread)			\
+	lwz	sr, KUAP(thread);					\
+	stw	sr, _KUAP(sp);						\
+	cmpli	cr7, sr, 0;						\
+	beq+	cr7, 102f;						\
+	li	val, 0;							\
+	stw	val, KUAP(thread);					\
+	rlwinm	srmax, sr, 28, 0xf0000000;				\
+	mfsrin	val, sr;						\
+	oris	val, val ,0x4000;	/* Set Ks */			\
+101:									\
+	mtsrin	val, sr;						\
+	addi	val, val, 0x111;	/* next VSID */			\
+	rlwinm	val, val, 0, 8, 3;	/* clear VSID overflow */	\
+	addis	sr, sr, 0x1000;		/* address of next segment */	\
+	cmpl	cr7, sr, srmax;						\
+	blt-	cr7, 101b;						\
+102:
+
+#define REST_USER_ACCESS(val, sp, sr, srmax, curr)			\
+	lwz	sr, _KUAP(sp);						\
+	stw	sr, THREAD+KUAP(curr);					\
+	cmpli	cr7, sr, 0;						\
+	beq+	cr7, 102f;						\
+	rlwinm	srmax, sr, 28, 0xf0000000;				\
+	mfsrin	val, sr;						\
+	rlwinm	val, val ,0, ~0x40000000;	/* Clear Ks */		\
+101:									\
+	mtsrin	val, sr;						\
+	addi	val, val, 0x111;	/* next VSID */			\
+	rlwinm	val, val, 0, 8, 3;	/* clear VSID overflow */	\
+	addis	sr, sr, 0x1000;		/* address of next segment */	\
+	cmpl	cr7, sr, srmax;						\
+	blt-	cr7, 101b;						\
+102:
+
+#define KUAP_START			0
+#endif
+
+#ifndef __ASSEMBLY__
+#ifdef CONFIG_PPC_KUAP
+
+#include <linux/sched.h>
+
+static inline void lock_user_access(void __user *to, const void __user *from,
+				    unsigned long size)
+{
+	unsigned long addr = (unsigned long)to;
+	unsigned long end = addr + size;
+	unsigned long sr;
+
+	if (!to)
+		return;
+
+	current->thread.kuap = 0;
+	sr = mfsrin(addr);
+	sr |= 0x40000000;		/* set Ks */
+	mb();	/* make sure all writes are done before SR are updated */
+	while (addr < end) {
+		mtsrin(sr, addr);
+		sr += 0x111;		/* next VSID */
+		sr &= 0xf0ffffff;	/* clear VSID overflow */
+		addr += 0x10000000;	/* address of next segment */
+	}
+}
+
+static inline void unlock_user_access(void __user *to, const void __user *from,
+				      unsigned long size)
+{
+	unsigned long addr = (unsigned long)to;
+	unsigned long end = addr + size;
+	unsigned long kuap = addr & 0xf0000000;
+	unsigned long sr;
+
+	if (!to)
+		return;
+
+	sr = mfsrin(addr);
+	sr &= ~0x40000000;		/* clear Ks */
+	while (addr < end) {
+		mtsrin(sr, addr);
+		sr += 0x111;		/* next VSID */
+		sr &= 0xf0ffffff;	/* clear VSID overflow */
+		addr += 0x10000000;	/* address of next segment */
+	}
+	kuap |= (addr >> 28) & 0xf;
+	current->thread.kuap = kuap;
+	mb();	/* make sure SRs are updated before writing */
+}
+#endif
+#endif
+
+#endif /* _ASM_POWERPC_BOOK3S_32_KUP_H */
diff --git a/arch/powerpc/include/asm/kup.h b/arch/powerpc/include/asm/kup.h
index d4dd242251bd..7813e2bcfb7c 100644
--- a/arch/powerpc/include/asm/kup.h
+++ b/arch/powerpc/include/asm/kup.h
@@ -5,6 +5,9 @@
 #ifdef CONFIG_PPC_8xx
 #include <asm/nohash/32/kup-8xx.h>
 #endif
+#ifdef CONFIG_PPC_BOOK3S_32
+#include <asm/book3s/32/kup.h>
+#endif
 #ifdef CONFIG_PPC_BOOK3S_64
 #include <asm/book3s/64/kup-radix.h>
 #endif
diff --git a/arch/powerpc/kernel/head_32.S b/arch/powerpc/kernel/head_32.S
index 1aca0dba0ec1..f73db6891901 100644
--- a/arch/powerpc/kernel/head_32.S
+++ b/arch/powerpc/kernel/head_32.S
@@ -1015,7 +1015,7 @@ _ENTRY(switch_mmu_context)
 	mulli	r3,r3,897	/* multiply context by skew factor */
 	rlwinm	r3,r3,4,8,27	/* VSID = (context & 0xfffff) << 4 */
 #ifdef CONFIG_PPC_KUAP
-	addis	r3,r3,0x4000	/* Set Ks, clear Ku bits */
+	addis	r3, r3, 0x4000	/* Set Ks, clear Ku bits */
 #endif
 	li	r0,NUM_USER_SEGMENTS
 	mtctr	r0
diff --git a/arch/powerpc/mm/ppc_mmu_32.c b/arch/powerpc/mm/ppc_mmu_32.c
index f6f575bae3bc..5dfebed93ab6 100644
--- a/arch/powerpc/mm/ppc_mmu_32.c
+++ b/arch/powerpc/mm/ppc_mmu_32.c
@@ -287,3 +287,13 @@ void setup_initial_memory_limit(phys_addr_t first_memblock_base,
 	else /* Anything else has 256M mapped */
 		memblock_set_current_limit(min_t(u64, first_memblock_size, 0x10000000));
 }
+
+#ifdef CONFIG_PPC_KUAP
+void __init setup_kuap(bool disabled)
+{
+	pr_info("Activating Kernel Userspace Access Protection\n");
+
+	if (disabled)
+		pr_warn("KUAP cannot be disabled yet on 6xx when compiled in\n");
+}
+#endif
diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype
index 5fbfa041194d..0b9a8eda413a 100644
--- a/arch/powerpc/platforms/Kconfig.cputype
+++ b/arch/powerpc/platforms/Kconfig.cputype
@@ -24,6 +24,7 @@ choice
 config PPC_BOOK3S_32
 	bool "512x/52xx/6xx/7xx/74xx/82xx/83xx/86xx"
 	select PPC_FPU
+	select PPC_HAVE_KUAP
 
 config PPC_85xx
 	bool "Freescale 85xx"
-- 
2.13.3



More information about the Linuxppc-dev mailing list