Commit 7af5b901 authored by Linus Walleij's avatar Linus Walleij Committed by Russell King (Oracle)
Browse files

ARM: 9358/2: Implement PAN for LPAE by TTBR0 page table walks disablement



With LPAE enabled, privileged no-access cannot be enforced using CPU
domains as such feature is not available. This patch implements PAN
by disabling TTBR0 page table walks while in kernel mode.

The ARM architecture allows page table walks to be split between TTBR0
and TTBR1. With LPAE enabled, the split is defined by a combination of
TTBCR T0SZ and T1SZ bits. Currently, an LPAE-enabled kernel uses TTBR0
for user addresses and TTBR1 for kernel addresses with the VMSPLIT_2G
and VMSPLIT_3G configurations. The main advantage for the 3:1 split is
that TTBR1 is reduced to 2 levels, so potentially faster TLB refill
(though usually the first level entries are already cached in the TLB).

The PAN support on LPAE-enabled kernels uses TTBR0 when running in user
space or in kernel space during user access routines (TTBCR T0SZ and
T1SZ are both 0). When running user accesses are disabled in kernel
mode, TTBR0 page table walks are disabled by setting TTBCR.EPD0. TTBR1
is used for kernel accesses (including loadable modules; anything
covered by swapper_pg_dir) by reducing the TTBCR.T0SZ to the minimum
(2^(32-7) = 32MB). To avoid user accesses potentially hitting stale TLB
entries, the ASID is switched to 0 (reserved) by setting TTBCR.A1 and
using the ASID value in TTBR1. The difference from a non-PAN kernel is
that with the 3:1 memory split, TTBR1 always uses 3 levels of page
tables.

As part of the change we are using preprocessor elif definied() clauses
so balance these clauses by converting relevant precedingt ifdef
clauses to if defined() clauses.

Signed-off-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
Reviewed-by: default avatarKees Cook <keescook@chromium.org>
Tested-by: default avatarFlorian Fainelli <florian.fainelli@broadcom.com>
Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
Signed-off-by: default avatarRussell King (Oracle) <rmk+kernel@armlinux.org.uk>
parent de7f60f0
Loading
Loading
Loading
Loading
+19 −3
Original line number Diff line number Diff line
@@ -1233,9 +1233,9 @@ config HIGHPTE
	  consumed by page tables.  Setting this option will allow
	  user-space 2nd level page tables to reside in high memory.

config CPU_SW_DOMAIN_PAN
	bool "Enable use of CPU domains to implement privileged no-access"
	depends on MMU && !ARM_LPAE
config ARM_PAN
	bool "Enable privileged no-access"
	depends on MMU
	default y
	help
	  Increase kernel security by ensuring that normal kernel accesses
@@ -1244,10 +1244,26 @@ config CPU_SW_DOMAIN_PAN
	  by ensuring that magic values (such as LIST_POISON) will always
	  fault when dereferenced.

	  The implementation uses CPU domains when !CONFIG_ARM_LPAE and
	  disabling of TTBR0 page table walks with CONFIG_ARM_LPAE.

config CPU_SW_DOMAIN_PAN
	def_bool y
	depends on ARM_PAN && !ARM_LPAE
	help
	  Enable use of CPU domains to implement privileged no-access.

	  CPUs with low-vector mappings use a best-efforts implementation.
	  Their lower 1MB needs to remain accessible for the vectors, but
	  the remainder of userspace will become appropriately inaccessible.

config CPU_TTBR0_PAN
	def_bool y
	depends on ARM_PAN && ARM_LPAE
	help
	  Enable privileged no-access by disabling TTBR0 page table walks when
	  running in kernel mode.

config HW_PERF_EVENTS
	def_bool y
	depends on ARM_PMU
+1 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include <asm/opcodes-virt.h>
#include <asm/asm-offsets.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/thread_info.h>
#include <asm/uaccess-asm.h>

+9 −0
Original line number Diff line number Diff line
@@ -74,6 +74,7 @@
#define PHYS_MASK_SHIFT		(40)
#define PHYS_MASK		((1ULL << PHYS_MASK_SHIFT) - 1)

#ifndef CONFIG_CPU_TTBR0_PAN
/*
 * TTBR0/TTBR1 split (PAGE_OFFSET):
 *   0x40000000: T0SZ = 2, T1SZ = 0 (not used)
@@ -93,6 +94,14 @@
#endif

#define TTBR1_SIZE	(((PAGE_OFFSET >> 30) - 1) << 16)
#else
/*
 * With CONFIG_CPU_TTBR0_PAN enabled, TTBR1 is only used during uaccess
 * disabled regions when TTBR0 is disabled.
 */
#define TTBR1_OFFSET	0			/* pointing to swapper_pg_dir */
#define TTBR1_SIZE	0			/* TTBR1 size controlled via TTBCR.T0SZ */
#endif

/*
 * TTBCR register bits.
+1 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ struct pt_regs {
struct svc_pt_regs {
	struct pt_regs regs;
	u32 dacr;
	u32 ttbcr;
};

#define to_svc_pt_regs(r) container_of(r, struct svc_pt_regs, regs)
+43 −1
Original line number Diff line number Diff line
@@ -39,7 +39,7 @@
#endif
	.endm

#ifdef CONFIG_CPU_SW_DOMAIN_PAN
#if defined(CONFIG_CPU_SW_DOMAIN_PAN)

	.macro	uaccess_disable, tmp, isb=1
	/*
@@ -65,6 +65,37 @@
	.endif
	.endm

#elif defined(CONFIG_CPU_TTBR0_PAN)

	.macro	uaccess_disable, tmp, isb=1
	/*
	 * Disable TTBR0 page table walks (EDP0 = 1), use the reserved ASID
	 * from TTBR1 (A1 = 1) and enable TTBR1 page table walks for kernel
	 * addresses by reducing TTBR0 range to 32MB (T0SZ = 7).
	 */
	mrc	p15, 0, \tmp, c2, c0, 2		@ read TTBCR
	orr	\tmp, \tmp, #TTBCR_EPD0 | TTBCR_T0SZ_MASK
	orr	\tmp, \tmp, #TTBCR_A1
	mcr	p15, 0, \tmp, c2, c0, 2		@ write TTBCR
	.if	\isb
	instr_sync
	.endif
	.endm

	.macro	uaccess_enable, tmp, isb=1
	/*
	 * Enable TTBR0 page table walks (T0SZ = 0, EDP0 = 0) and ASID from
	 * TTBR0 (A1 = 0).
	 */
	mrc	p15, 0, \tmp, c2, c0, 2		@ read TTBCR
	bic	\tmp, \tmp, #TTBCR_EPD0 | TTBCR_T0SZ_MASK
	bic	\tmp, \tmp, #TTBCR_A1
	mcr	p15, 0, \tmp, c2, c0, 2		@ write TTBCR
	.if	\isb
	instr_sync
	.endif
	.endm

#else

	.macro	uaccess_disable, tmp, isb=1
@@ -79,6 +110,12 @@
#define DACR(x...)	x
#else
#define DACR(x...)
#endif

#ifdef CONFIG_CPU_TTBR0_PAN
#define PAN(x...)	x
#else
#define PAN(x...)
#endif

	/*
@@ -94,6 +131,8 @@
	.macro	uaccess_entry, tsk, tmp0, tmp1, tmp2, disable
 DACR(	mrc	p15, 0, \tmp0, c3, c0, 0)
 DACR(	str	\tmp0, [sp, #SVC_DACR])
 PAN(	mrc	p15, 0, \tmp0, c2, c0, 2)
 PAN(	str	\tmp0, [sp, #SVC_TTBCR])
	.if \disable && IS_ENABLED(CONFIG_CPU_SW_DOMAIN_PAN)
	/* kernel=client, user=no access */
	mov	\tmp2, #DACR_UACCESS_DISABLE
@@ -112,8 +151,11 @@
	.macro	uaccess_exit, tsk, tmp0, tmp1
 DACR(	ldr	\tmp0, [sp, #SVC_DACR])
 DACR(	mcr	p15, 0, \tmp0, c3, c0, 0)
 PAN(	ldr	\tmp0, [sp, #SVC_TTBCR])
 PAN(	mcr	p15, 0, \tmp0, c2, c0, 2)
	.endm

#undef DACR
#undef PAN

#endif /* __ASM_UACCESS_ASM_H__ */
Loading