summaryrefslogtreecommitdiffstats
path: root/tools/testing/selftests/powerpc/include/pkeys.h
blob: 51729d9a711109b11252730eed4e855045124fe5 (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
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright 2020, Sandipan Das, IBM Corp.
 */

#ifndef _SELFTESTS_POWERPC_PKEYS_H
#define _SELFTESTS_POWERPC_PKEYS_H

#include <sys/mman.h>

#include "reg.h"
#include "utils.h"

/*
 * Older versions of libc use the Intel-specific access rights.
 * Hence, override the definitions as they might be incorrect.
 */
#undef PKEY_DISABLE_ACCESS
#define PKEY_DISABLE_ACCESS	0x3

#undef PKEY_DISABLE_WRITE
#define PKEY_DISABLE_WRITE	0x2

#undef PKEY_DISABLE_EXECUTE
#define PKEY_DISABLE_EXECUTE	0x4

/* Older versions of libc do not define this */
#ifndef SEGV_PKUERR
#define SEGV_PKUERR	4
#endif

#define SI_PKEY_OFFSET	0x20

#define __NR_pkey_mprotect	386
#define __NR_pkey_alloc		384
#define __NR_pkey_free		385

#define PKEY_BITS_PER_PKEY	2
#define NR_PKEYS		32
#define PKEY_BITS_MASK		((1UL << PKEY_BITS_PER_PKEY) - 1)

inline unsigned long pkeyreg_get(void)
{
	return mfspr(SPRN_AMR);
}

inline void pkeyreg_set(unsigned long amr)
{
	set_amr(amr);
}

void pkey_set_rights(int pkey, unsigned long rights)
{
	unsigned long amr, shift;

	shift = (NR_PKEYS - pkey - 1) * PKEY_BITS_PER_PKEY;
	amr = pkeyreg_get();
	amr &= ~(PKEY_BITS_MASK << shift);
	amr |= (rights & PKEY_BITS_MASK) << shift;
	pkeyreg_set(amr);
}

int sys_pkey_mprotect(void *addr, size_t len, int prot, int pkey)
{
	return syscall(__NR_pkey_mprotect, addr, len, prot, pkey);
}

int sys_pkey_alloc(unsigned long flags, unsigned long rights)
{
	return syscall(__NR_pkey_alloc, flags, rights);
}

int sys_pkey_free(int pkey)
{
	return syscall(__NR_pkey_free, pkey);
}

int pkeys_unsupported(void)
{
	bool hash_mmu = false;
	int pkey;

	/* Protection keys are currently supported on Hash MMU only */
	FAIL_IF(using_hash_mmu(&hash_mmu));
	SKIP_IF(!hash_mmu);

	/* Check if the system call is supported */
	pkey = sys_pkey_alloc(0, 0);
	SKIP_IF(pkey < 0);
	sys_pkey_free(pkey);

	return 0;
}

int siginfo_pkey(siginfo_t *si)
{
	/*
	 * In older versions of libc, siginfo_t does not have si_pkey as
	 * a member.
	 */
#ifdef si_pkey
	return si->si_pkey;
#else
	return *((int *)(((char *) si) + SI_PKEY_OFFSET));
#endif
}

#define pkey_rights(r) ({						\
	static char buf[4] = "rwx";					\
	unsigned int amr_bits;						\
	if ((r) & PKEY_DISABLE_EXECUTE)					\
		buf[2] = '-';						\
	amr_bits = (r) & PKEY_BITS_MASK;				\
	if (amr_bits & PKEY_DISABLE_WRITE)				\
		buf[1] = '-';						\
	if (amr_bits & PKEY_DISABLE_ACCESS & ~PKEY_DISABLE_WRITE)	\
		buf[0] = '-';						\
	buf;								\
})

unsigned long next_pkey_rights(unsigned long rights)
{
	if (rights == PKEY_DISABLE_ACCESS)
		return PKEY_DISABLE_EXECUTE;
	else if (rights == (PKEY_DISABLE_ACCESS | PKEY_DISABLE_EXECUTE))
		return 0;

	if ((rights & PKEY_BITS_MASK) == 0)
		rights |= PKEY_DISABLE_WRITE;
	else if ((rights & PKEY_BITS_MASK) == PKEY_DISABLE_WRITE)
		rights |= PKEY_DISABLE_ACCESS;

	return rights;
}

#endif /* _SELFTESTS_POWERPC_PKEYS_H */