xref: /linux/tools/testing/selftests/powerpc/dexcr/dexcr.c (revision ff2632d7d08edc11e8bd0629e9fcfebab25c78b4)
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