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