1 // SPDX-License-Identifier: GPL-2.0+ 2 3 #include <errno.h> 4 #include <setjmp.h> 5 #include <signal.h> 6 #include <sys/prctl.h> 7 #include <sys/types.h> 8 #include <sys/wait.h> 9 10 #include "dexcr.h" 11 #include "reg.h" 12 #include "utils.h" 13 14 static jmp_buf generic_signal_jump_buf; 15 16 static void generic_signal_handler(int signum, siginfo_t *info, void *context) 17 { 18 longjmp(generic_signal_jump_buf, 0); 19 } 20 21 bool dexcr_exists(void) 22 { 23 struct sigaction old; 24 volatile bool exists; 25 26 old = push_signal_handler(SIGILL, generic_signal_handler); 27 if (setjmp(generic_signal_jump_buf)) 28 goto out; 29 30 /* 31 * If the SPR is not recognised by the hardware it triggers 32 * a hypervisor emulation interrupt. If the kernel does not 33 * recognise/try to emulate it, we receive a SIGILL signal. 34 * 35 * If we do not receive a signal, assume we have the SPR or the 36 * kernel is trying to emulate it correctly. 37 */ 38 exists = false; 39 mfspr(SPRN_DEXCR_RO); 40 exists = true; 41 42 out: 43 pop_signal_handler(SIGILL, old); 44 return exists; 45 } 46 47 unsigned int pr_which_to_aspect(unsigned long which) 48 { 49 switch (which) { 50 case PR_PPC_DEXCR_SBHE: 51 return DEXCR_PR_SBHE; 52 case PR_PPC_DEXCR_IBRTPD: 53 return DEXCR_PR_IBRTPD; 54 case PR_PPC_DEXCR_SRAPD: 55 return DEXCR_PR_SRAPD; 56 case PR_PPC_DEXCR_NPHIE: 57 return DEXCR_PR_NPHIE; 58 default: 59 FAIL_IF_EXIT_MSG(true, "unknown PR aspect"); 60 } 61 } 62 63 int pr_get_dexcr(unsigned long which) 64 { 65 return prctl(PR_PPC_GET_DEXCR, which, 0UL, 0UL, 0UL); 66 } 67 68 int pr_set_dexcr(unsigned long which, unsigned long ctrl) 69 { 70 return prctl(PR_PPC_SET_DEXCR, which, ctrl, 0UL, 0UL); 71 } 72 73 bool pr_dexcr_aspect_supported(unsigned long which) 74 { 75 if (pr_get_dexcr(which) == -1) 76 return errno == ENODEV; 77 78 return true; 79 } 80 81 bool pr_dexcr_aspect_editable(unsigned long which) 82 { 83 return pr_get_dexcr(which) & PR_PPC_DEXCR_CTRL_EDITABLE; 84 } 85 86 /* 87 * Just test if a bad hashchk triggers a signal, without checking 88 * for support or if the NPHIE aspect is enabled. 89 */ 90 bool hashchk_triggers(void) 91 { 92 struct sigaction old; 93 volatile bool triggers; 94 95 old = push_signal_handler(SIGILL, generic_signal_handler); 96 if (setjmp(generic_signal_jump_buf)) 97 goto out; 98 99 triggers = true; 100 do_bad_hashchk(); 101 triggers = false; 102 103 out: 104 pop_signal_handler(SIGILL, old); 105 return triggers; 106 } 107 108 unsigned int get_dexcr(enum dexcr_source source) 109 { 110 switch (source) { 111 case DEXCR: 112 return mfspr(SPRN_DEXCR_RO); 113 case HDEXCR: 114 return mfspr(SPRN_HDEXCR_RO); 115 case EFFECTIVE: 116 return mfspr(SPRN_DEXCR_RO) | mfspr(SPRN_HDEXCR_RO); 117 default: 118 FAIL_IF_EXIT_MSG(true, "bad enum dexcr_source"); 119 } 120 } 121 122 void await_child_success(pid_t pid) 123 { 124 int wstatus; 125 126 FAIL_IF_EXIT_MSG(pid == -1, "fork failed"); 127 FAIL_IF_EXIT_MSG(waitpid(pid, &wstatus, 0) == -1, "wait failed"); 128 FAIL_IF_EXIT_MSG(!WIFEXITED(wstatus), "child did not exit cleanly"); 129 FAIL_IF_EXIT_MSG(WEXITSTATUS(wstatus) != 0, "child exit error"); 130 } 131 132 /* 133 * Perform a hashst instruction. The following components determine the result 134 * 135 * 1. The LR value (any register technically) 136 * 2. The SP value (also any register, but it must be a valid address) 137 * 3. A secret key managed by the kernel 138 * 139 * The result is stored to the address held in SP. 140 */ 141 void hashst(unsigned long lr, void *sp) 142 { 143 asm volatile ("addi 31, %0, 0;" /* set r31 (pretend LR) to lr */ 144 "addi 30, %1, 8;" /* set r30 (pretend SP) to sp + 8 */ 145 PPC_RAW_HASHST(31, -8, 30) /* compute hash into stack location */ 146 : : "r" (lr), "r" (sp) : "r31", "r30", "memory"); 147 } 148 149 /* 150 * Perform a hashchk instruction. A hash is computed as per hashst(), 151 * however the result is not stored to memory. Instead the existing 152 * value is read and compared against the computed hash. 153 * 154 * If they match, execution continues. 155 * If they differ, an interrupt triggers. 156 */ 157 void hashchk(unsigned long lr, void *sp) 158 { 159 asm volatile ("addi 31, %0, 0;" /* set r31 (pretend LR) to lr */ 160 "addi 30, %1, 8;" /* set r30 (pretend SP) to sp + 8 */ 161 PPC_RAW_HASHCHK(31, -8, 30) /* check hash at stack location */ 162 : : "r" (lr), "r" (sp) : "r31", "r30", "memory"); 163 } 164 165 void do_bad_hashchk(void) 166 { 167 unsigned long hash = 0; 168 169 hashst(0, &hash); 170 hash += 1; 171 hashchk(0, &hash); 172 } 173