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 #undef PKEY_UNRESTRICTED
28 #define PKEY_UNRESTRICTED	0x0
29 
30 /* Older versions of libc do not define this */
31 #ifndef SEGV_PKUERR
32 #define SEGV_PKUERR	4
33 #endif
34 
35 #define SI_PKEY_OFFSET	0x20
36 
37 #define __NR_pkey_mprotect	386
38 #define __NR_pkey_alloc		384
39 #define __NR_pkey_free		385
40 
41 #ifndef NT_PPC_PKEY
42 #define NT_PPC_PKEY		0x110
43 #endif
44 
45 #define PKEY_BITS_PER_PKEY	2
46 #define NR_PKEYS		32
47 #define PKEY_BITS_MASK		((1UL << PKEY_BITS_PER_PKEY) - 1)
48 
49 #define AMR_BITS_PER_PKEY 2
50 #define PKEY_REG_BITS (sizeof(u64) * 8)
51 #define pkeyshift(pkey) (PKEY_REG_BITS - ((pkey + 1) * AMR_BITS_PER_PKEY))
52 
pkeyreg_get(void)53 inline unsigned long pkeyreg_get(void)
54 {
55 	return mfspr(SPRN_AMR);
56 }
57 
pkeyreg_set(unsigned long amr)58 inline void pkeyreg_set(unsigned long amr)
59 {
60 	set_amr(amr);
61 }
62 
pkey_set_rights(int pkey,unsigned long rights)63 void pkey_set_rights(int pkey, unsigned long rights)
64 {
65 	unsigned long amr, shift;
66 
67 	shift = (NR_PKEYS - pkey - 1) * PKEY_BITS_PER_PKEY;
68 	amr = pkeyreg_get();
69 	amr &= ~(PKEY_BITS_MASK << shift);
70 	amr |= (rights & PKEY_BITS_MASK) << shift;
71 	pkeyreg_set(amr);
72 }
73 
sys_pkey_mprotect(void * addr,size_t len,int prot,int pkey)74 int sys_pkey_mprotect(void *addr, size_t len, int prot, int pkey)
75 {
76 	return syscall(__NR_pkey_mprotect, addr, len, prot, pkey);
77 }
78 
sys_pkey_alloc(unsigned long flags,unsigned long rights)79 int sys_pkey_alloc(unsigned long flags, unsigned long rights)
80 {
81 	return syscall(__NR_pkey_alloc, flags, rights);
82 }
83 
sys_pkey_free(int pkey)84 int sys_pkey_free(int pkey)
85 {
86 	return syscall(__NR_pkey_free, pkey);
87 }
88 
pkeys_unsupported(void)89 int pkeys_unsupported(void)
90 {
91 	bool hash_mmu = false;
92 	int pkey;
93 
94 	/* Protection keys are currently supported on Hash MMU only */
95 	FAIL_IF(using_hash_mmu(&hash_mmu));
96 	SKIP_IF(!hash_mmu);
97 
98 	/* Check if the system call is supported */
99 	pkey = sys_pkey_alloc(0, PKEY_UNRESTRICTED);
100 	SKIP_IF(pkey < 0);
101 	sys_pkey_free(pkey);
102 
103 	return 0;
104 }
105 
siginfo_pkey(siginfo_t * si)106 int siginfo_pkey(siginfo_t *si)
107 {
108 	/*
109 	 * In older versions of libc, siginfo_t does not have si_pkey as
110 	 * a member.
111 	 */
112 #ifdef si_pkey
113 	return si->si_pkey;
114 #else
115 	return *((int *)(((char *) si) + SI_PKEY_OFFSET));
116 #endif
117 }
118 
119 #define pkey_rights(r) ({						\
120 	static char buf[4] = "rwx";					\
121 	unsigned int amr_bits;						\
122 	if ((r) & PKEY_DISABLE_EXECUTE)					\
123 		buf[2] = '-';						\
124 	amr_bits = (r) & PKEY_BITS_MASK;				\
125 	if (amr_bits & PKEY_DISABLE_WRITE)				\
126 		buf[1] = '-';						\
127 	if (amr_bits & PKEY_DISABLE_ACCESS & ~PKEY_DISABLE_WRITE)	\
128 		buf[0] = '-';						\
129 	buf;								\
130 })
131 
next_pkey_rights(unsigned long rights)132 unsigned long next_pkey_rights(unsigned long rights)
133 {
134 	if (rights == PKEY_DISABLE_ACCESS)
135 		return PKEY_DISABLE_EXECUTE;
136 	else if (rights == (PKEY_DISABLE_ACCESS | PKEY_DISABLE_EXECUTE))
137 		return 0;
138 
139 	if ((rights & PKEY_BITS_MASK) == 0)
140 		rights |= PKEY_DISABLE_WRITE;
141 	else if ((rights & PKEY_BITS_MASK) == PKEY_DISABLE_WRITE)
142 		rights |= PKEY_DISABLE_ACCESS;
143 
144 	return rights;
145 }
146 
147 #endif /* _SELFTESTS_POWERPC_PKEYS_H */
148