summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/kvm/arm64/host_sve.c
blob: 3826772fd47088b5d9036fa688b8ae7e7a0f6fcd (plain)
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
// SPDX-License-Identifier: GPL-2.0-only

/*
 * Host SVE: Check FPSIMD/SVE/SME save/restore over KVM_RUN ioctls.
 *
 * Copyright 2025 Arm, Ltd
 */

#include <errno.h>
#include <signal.h>
#include <sys/auxv.h>
#include <asm/kvm.h>
#include <kvm_util.h>

#include "ucall_common.h"

static void guest_code(void)
{
	for (int i = 0; i < 10; i++) {
		GUEST_UCALL_NONE();
	}

	GUEST_DONE();
}

void handle_sigill(int sig, siginfo_t *info, void *ctx)
{
	ucontext_t *uctx = ctx;

	printf("  < host signal %d >\n", sig);

	/*
	 * Skip the UDF
	 */
	uctx->uc_mcontext.pc += 4;
}

void register_sigill_handler(void)
{
	struct sigaction sa = {
		.sa_sigaction = handle_sigill,
		.sa_flags = SA_SIGINFO,
	};
	sigaction(SIGILL, &sa, NULL);
}

static void do_sve_roundtrip(void)
{
	unsigned long before, after;

	/*
	 * Set all bits in a predicate register, force a save/restore via a
	 * SIGILL (which handle_sigill() will recover from), then report
	 * whether the value has changed.
	 */
	asm volatile(
	"	.arch_extension sve\n"
	"	ptrue	p0.B\n"
	"	cntp	%[before], p0, p0.B\n"
	"	udf #0\n"
	"	cntp	%[after], p0, p0.B\n"
	: [before] "=r" (before),
	  [after] "=r" (after)
	:
	: "p0"
	);

	if (before != after) {
		TEST_FAIL("Signal roundtrip discarded predicate bits (%ld => %ld)\n",
			  before, after);
	} else {
		printf("Signal roundtrip preserved predicate bits (%ld => %ld)\n",
		       before, after);
	}
}

static void test_run(void)
{
	struct kvm_vcpu *vcpu;
	struct kvm_vm *vm;
	struct ucall uc;
	bool guest_done = false;

	register_sigill_handler();

	vm = vm_create_with_one_vcpu(&vcpu, guest_code);

	do_sve_roundtrip();

	while (!guest_done) {

		printf("Running VCPU...\n");
		vcpu_run(vcpu);

		switch (get_ucall(vcpu, &uc)) {
		case UCALL_NONE:
			do_sve_roundtrip();
			do_sve_roundtrip();
			break;
		case UCALL_DONE:
			guest_done = true;
			break;
		case UCALL_ABORT:
			REPORT_GUEST_ASSERT(uc);
			break;
		default:
			TEST_FAIL("Unexpected guest exit");
		}
	}

	kvm_vm_free(vm);
}

int main(void)
{
	/*
	 * This is testing the host environment, we don't care about
	 * guest SVE support.
	 */
	if (!(getauxval(AT_HWCAP) & HWCAP_SVE)) {
		printf("SVE not supported\n");
		return KSFT_SKIP;
	}

	test_run();
	return 0;
}