aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/tools/testing/selftests/powerpc/dexcr/dexcr.c
blob: 65ec5347de988032beffce4265de6218232650d1 (plain) (blame)
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);
}