Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
14static jmp_buf generic_signal_jump_buf;
15
16static void generic_signal_handler(int signum, siginfo_t *info, void *context)
17{
18 longjmp(generic_signal_jump_buf, 0);
19}
20
21bool 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
42out:
43 pop_signal_handler(SIGILL, old);
44 return exists;
45}
46
47unsigned 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
63int pr_get_dexcr(unsigned long which)
64{
65 return prctl(PR_PPC_GET_DEXCR, which, 0UL, 0UL, 0UL);
66}
67
68int pr_set_dexcr(unsigned long which, unsigned long ctrl)
69{
70 return prctl(PR_PPC_SET_DEXCR, which, ctrl, 0UL, 0UL);
71}
72
73bool 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
81bool 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 */
90bool 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
103out:
104 pop_signal_handler(SIGILL, old);
105 return triggers;
106}
107
108unsigned 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
122void 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 */
141void 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 */
157void 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
165void do_bad_hashchk(void)
166{
167 unsigned long hash = 0;
168
169 hashst(0, &hash);
170 hash += 1;
171 hashchk(0, &hash);
172}