Commit f5b5ea51 authored by Joey Gouly's avatar Joey Gouly Committed by Will Deacon
Browse files

selftests: mm: make protection_keys test work on arm64



The encoding of the pkey register differs on arm64, than on x86/ppc. On those
platforms, a bit in the register is used to disable permissions, for arm64, a
bit enabled in the register indicates that the permission is allowed.

This drops two asserts of the form:
	 assert(read_pkey_reg() <= orig_pkey_reg);
Because on arm64 this doesn't hold, due to the encoding.

The pkey must be reset to both access allow and write allow in the signal
handler. pkey_access_allow() works currently for PowerPC as the
PKEY_DISABLE_ACCESS and PKEY_DISABLE_WRITE have overlapping bits set.

Access to the uc_mcontext is abstracted, as arm64 has a different structure.

Signed-off-by: default avatarJoey Gouly <joey.gouly@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
Acked-by: default avatarDave Hansen <dave.hansen@linux.intel.com>
Link: https://lore.kernel.org/r/20240822151113.1479789-27-joey.gouly@arm.com


Signed-off-by: default avatarWill Deacon <will@kernel.org>
parent 41bbcf7b
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -26,6 +26,9 @@
#define HDR_SZ \
	sizeof(struct _aarch64_ctx)

#define GET_UC_RESV_HEAD(uc) \
	(struct _aarch64_ctx *)(&(uc->uc_mcontext.__reserved))

#define GET_SF_RESV_HEAD(sf) \
	(struct _aarch64_ctx *)(&(sf).uc.uc_mcontext.__reserved)

+1 −1
Original line number Diff line number Diff line
@@ -104,7 +104,7 @@ TEST_GEN_FILES += $(BINARIES_64)
endif
else

ifneq (,$(findstring $(ARCH),powerpc))
ifneq (,$(filter $(ARCH),arm64 powerpc))
TEST_GEN_FILES += protection_keys
endif

+139 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright (C) 2023 Arm Ltd.
 */

#ifndef _PKEYS_ARM64_H
#define _PKEYS_ARM64_H

#include "vm_util.h"
/* for signal frame parsing */
#include "../arm64/signal/testcases/testcases.h"

#ifndef SYS_mprotect_key
# define SYS_mprotect_key	288
#endif
#ifndef SYS_pkey_alloc
# define SYS_pkey_alloc		289
# define SYS_pkey_free		290
#endif
#define MCONTEXT_IP(mc)		mc.pc
#define MCONTEXT_TRAPNO(mc)	-1

#define PKEY_MASK		0xf

#define POE_NONE		0x0
#define POE_X			0x2
#define POE_RX			0x3
#define POE_RWX			0x7

#define NR_PKEYS		8
#define NR_RESERVED_PKEYS	1 /* pkey-0 */

#define PKEY_ALLOW_ALL		0x77777777

#define PKEY_BITS_PER_PKEY	4
#define PAGE_SIZE		sysconf(_SC_PAGESIZE)
#undef HPAGE_SIZE
#define HPAGE_SIZE		default_huge_page_size()

/* 4-byte instructions * 16384 = 64K page */
#define __page_o_noops() asm(".rept 16384 ; nop; .endr")

static inline u64 __read_pkey_reg(void)
{
	u64 pkey_reg = 0;

	// POR_EL0
	asm volatile("mrs %0, S3_3_c10_c2_4" : "=r" (pkey_reg));

	return pkey_reg;
}

static inline void __write_pkey_reg(u64 pkey_reg)
{
	u64 por = pkey_reg;

	dprintf4("%s() changing %016llx to %016llx\n",
			 __func__, __read_pkey_reg(), pkey_reg);

	// POR_EL0
	asm volatile("msr S3_3_c10_c2_4, %0\nisb" :: "r" (por) :);

	dprintf4("%s() pkey register after changing %016llx to %016llx\n",
			__func__, __read_pkey_reg(), pkey_reg);
}

static inline int cpu_has_pkeys(void)
{
	/* No simple way to determine this */
	return 1;
}

static inline u32 pkey_bit_position(int pkey)
{
	return pkey * PKEY_BITS_PER_PKEY;
}

static inline int get_arch_reserved_keys(void)
{
	return NR_RESERVED_PKEYS;
}

void expect_fault_on_read_execonly_key(void *p1, int pkey)
{
}

void *malloc_pkey_with_mprotect_subpage(long size, int prot, u16 pkey)
{
	return PTR_ERR_ENOTSUP;
}

#define set_pkey_bits	set_pkey_bits
static inline u64 set_pkey_bits(u64 reg, int pkey, u64 flags)
{
	u32 shift = pkey_bit_position(pkey);
	u64 new_val = POE_RWX;

	/* mask out bits from pkey in old value */
	reg &= ~((u64)PKEY_MASK << shift);

	if (flags & PKEY_DISABLE_ACCESS)
		new_val = POE_X;
	else if (flags & PKEY_DISABLE_WRITE)
		new_val = POE_RX;

	/* OR in new bits for pkey */
	reg |= new_val << shift;

	return reg;
}

#define get_pkey_bits	get_pkey_bits
static inline u64 get_pkey_bits(u64 reg, int pkey)
{
	u32 shift = pkey_bit_position(pkey);
	/*
	 * shift down the relevant bits to the lowest four, then
	 * mask off all the other higher bits
	 */
	u32 perm = (reg >> shift) & PKEY_MASK;

	if (perm == POE_X)
		return PKEY_DISABLE_ACCESS;
	if (perm == POE_RX)
		return PKEY_DISABLE_WRITE;
	return 0;
}

static void aarch64_write_signal_pkey(ucontext_t *uctxt, u64 pkey)
{
	struct _aarch64_ctx *ctx = GET_UC_RESV_HEAD(uctxt);
	struct poe_context *poe_ctx =
		(struct poe_context *) get_header(ctx, POE_MAGIC,
						sizeof(uctxt->uc_mcontext), NULL);
	if (poe_ctx)
		poe_ctx->por_el0 = pkey;
}

#endif /* _PKEYS_ARM64_H */
+8 −0
Original line number Diff line number Diff line
@@ -91,12 +91,17 @@ void record_pkey_malloc(void *ptr, long size, int prot);
#include "pkey-x86.h"
#elif defined(__powerpc64__) /* arch */
#include "pkey-powerpc.h"
#elif defined(__aarch64__) /* arch */
#include "pkey-arm64.h"
#else /* arch */
#error Architecture not supported
#endif /* arch */

#ifndef PKEY_MASK
#define PKEY_MASK	(PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE)
#endif

#ifndef set_pkey_bits
static inline u64 set_pkey_bits(u64 reg, int pkey, u64 flags)
{
	u32 shift = pkey_bit_position(pkey);
@@ -106,7 +111,9 @@ static inline u64 set_pkey_bits(u64 reg, int pkey, u64 flags)
	reg |= (flags & PKEY_MASK) << shift;
	return reg;
}
#endif

#ifndef get_pkey_bits
static inline u64 get_pkey_bits(u64 reg, int pkey)
{
	u32 shift = pkey_bit_position(pkey);
@@ -116,6 +123,7 @@ static inline u64 get_pkey_bits(u64 reg, int pkey)
	 */
	return ((reg >> shift) & PKEY_MASK);
}
#endif

extern u64 shadow_pkey_reg;

+2 −0
Original line number Diff line number Diff line
@@ -8,6 +8,8 @@
# define SYS_pkey_free		385
#endif
#define REG_IP_IDX		PT_NIP
#define MCONTEXT_IP(mc)		mc.gp_regs[REG_IP_IDX]
#define MCONTEXT_TRAPNO(mc)	mc.gp_regs[REG_TRAPNO]
#define REG_TRAPNO		PT_TRAP
#define MCONTEXT_FPREGS
#define gregs			gp_regs
Loading