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