summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/mm/pkey-arm64.h
blob: 580e1b0bb38e8de8df03a0b5d692b252f9ba5265 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/* 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 */