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(lr: 0, sp: &hash); |
130 | hash += 1; |
131 | hashchk(lr: 0, sp: &hash); |
132 | } |
133 | |