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