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
generic_signal_handler(int signum,siginfo_t * info,void * context)16 static void generic_signal_handler(int signum, siginfo_t *info, void *context)
17 {
18 longjmp(generic_signal_jump_buf, 0);
19 }
20
dexcr_exists(void)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
pr_which_to_aspect(unsigned long which)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
pr_get_dexcr(unsigned long which)63 int pr_get_dexcr(unsigned long which)
64 {
65 return prctl(PR_PPC_GET_DEXCR, which, 0UL, 0UL, 0UL);
66 }
67
pr_set_dexcr(unsigned long which,unsigned long ctrl)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
pr_dexcr_aspect_supported(unsigned long which)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
pr_dexcr_aspect_editable(unsigned long which)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 */
hashchk_triggers(void)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
get_dexcr(enum dexcr_source source)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
await_child_success(pid_t pid)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 */
hashst(unsigned long lr,void * sp)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 */
hashchk(unsigned long lr,void * sp)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
do_bad_hashchk(void)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