1 /* SPDX-License-Identifier: GPL-2.0 */
2 /*
3  * Copyright 2020, Sandipan Das, IBM Corp.
4  */
5 
6 #ifndef _SELFTESTS_POWERPC_PKEYS_H
7 #define _SELFTESTS_POWERPC_PKEYS_H
8 
9 #include <sys/mman.h>
10 
11 #include "reg.h"
12 #include "utils.h"
13 
14 /*
15  * Older versions of libc use the Intel-specific access rights.
16  * Hence, override the definitions as they might be incorrect.
17  */
18 #undef PKEY_DISABLE_ACCESS
19 #define PKEY_DISABLE_ACCESS	0x3
20 
21 #undef PKEY_DISABLE_WRITE
22 #define PKEY_DISABLE_WRITE	0x2
23 
24 #undef PKEY_DISABLE_EXECUTE
25 #define PKEY_DISABLE_EXECUTE	0x4
26 
27 /* Older versions of libc do not not define this */
28 #ifndef SEGV_PKUERR
29 #define SEGV_PKUERR	4
30 #endif
31 
32 #define SI_PKEY_OFFSET	0x20
33 
34 #define __NR_pkey_mprotect	386
35 #define __NR_pkey_alloc		384
36 #define __NR_pkey_free		385
37 
38 #define PKEY_BITS_PER_PKEY	2
39 #define NR_PKEYS		32
40 #define PKEY_BITS_MASK		((1UL << PKEY_BITS_PER_PKEY) - 1)
41 
42 inline unsigned long pkeyreg_get(void)
43 {
44 	return mfspr(SPRN_AMR);
45 }
46 
47 inline void pkeyreg_set(unsigned long amr)
48 {
49 	set_amr(amr);
50 }
51 
52 void pkey_set_rights(int pkey, unsigned long rights)
53 {
54 	unsigned long amr, shift;
55 
56 	shift = (NR_PKEYS - pkey - 1) * PKEY_BITS_PER_PKEY;
57 	amr = pkeyreg_get();
58 	amr &= ~(PKEY_BITS_MASK << shift);
59 	amr |= (rights & PKEY_BITS_MASK) << shift;
60 	pkeyreg_set(amr);
61 }
62 
63 int sys_pkey_mprotect(void *addr, size_t len, int prot, int pkey)
64 {
65 	return syscall(__NR_pkey_mprotect, addr, len, prot, pkey);
66 }
67 
68 int sys_pkey_alloc(unsigned long flags, unsigned long rights)
69 {
70 	return syscall(__NR_pkey_alloc, flags, rights);
71 }
72 
73 int sys_pkey_free(int pkey)
74 {
75 	return syscall(__NR_pkey_free, pkey);
76 }
77 
78 int pkeys_unsupported(void)
79 {
80 	bool hash_mmu = false;
81 	int pkey;
82 
83 	/* Protection keys are currently supported on Hash MMU only */
84 	FAIL_IF(using_hash_mmu(&hash_mmu));
85 	SKIP_IF(!hash_mmu);
86 
87 	/* Check if the system call is supported */
88 	pkey = sys_pkey_alloc(0, 0);
89 	SKIP_IF(pkey < 0);
90 	sys_pkey_free(pkey);
91 
92 	return 0;
93 }
94 
95 int siginfo_pkey(siginfo_t *si)
96 {
97 	/*
98 	 * In older versions of libc, siginfo_t does not have si_pkey as
99 	 * a member.
100 	 */
101 #ifdef si_pkey
102 	return si->si_pkey;
103 #else
104 	return *((int *)(((char *) si) + SI_PKEY_OFFSET));
105 #endif
106 }
107 
108 #define pkey_rights(r) ({						\
109 	static char buf[4] = "rwx";					\
110 	unsigned int amr_bits;						\
111 	if ((r) & PKEY_DISABLE_EXECUTE)					\
112 		buf[2] = '-';						\
113 	amr_bits = (r) & PKEY_BITS_MASK;				\
114 	if (amr_bits & PKEY_DISABLE_WRITE)				\
115 		buf[1] = '-';						\
116 	if (amr_bits & PKEY_DISABLE_ACCESS & ~PKEY_DISABLE_WRITE)	\
117 		buf[0] = '-';						\
118 	buf;								\
119 })
120 
121 unsigned long next_pkey_rights(unsigned long rights)
122 {
123 	if (rights == PKEY_DISABLE_ACCESS)
124 		return PKEY_DISABLE_EXECUTE;
125 	else if (rights == (PKEY_DISABLE_ACCESS | PKEY_DISABLE_EXECUTE))
126 		return 0;
127 
128 	if ((rights & PKEY_BITS_MASK) == 0)
129 		rights |= PKEY_DISABLE_WRITE;
130 	else if ((rights & PKEY_BITS_MASK) == PKEY_DISABLE_WRITE)
131 		rights |= PKEY_DISABLE_ACCESS;
132 
133 	return rights;
134 }
135 
136 #endif /* _SELFTESTS_POWERPC_PKEYS_H */
137