1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2021 ARM Limited
4  *
5  * Attempt to change the SVE vector length in a signal hander, this is not
6  * supported and is expected to segfault.
7  */
8 
9 #include <kselftest.h>
10 #include <signal.h>
11 #include <ucontext.h>
12 #include <sys/prctl.h>
13 
14 #include "test_signals_utils.h"
15 #include "testcases.h"
16 
17 struct fake_sigframe sf;
18 static unsigned int vls[SVE_VQ_MAX];
19 unsigned int nvls = 0;
20 
21 static bool sve_get_vls(struct tdescr *td)
22 {
23 	int vq, vl;
24 
25 	/*
26 	 * Enumerate up to SVE_VQ_MAX vector lengths
27 	 */
28 	for (vq = SVE_VQ_MAX; vq > 0; --vq) {
29 		vl = prctl(PR_SVE_SET_VL, vq * 16);
30 		if (vl == -1)
31 			return false;
32 
33 		vl &= PR_SVE_VL_LEN_MASK;
34 
35 		/* Skip missing VLs */
36 		vq = sve_vq_from_vl(vl);
37 
38 		vls[nvls++] = vl;
39 	}
40 
41 	/* We need at least two VLs */
42 	if (nvls < 2) {
43 		fprintf(stderr, "Only %d VL supported\n", nvls);
44 		td->result = KSFT_SKIP;
45 		return false;
46 	}
47 
48 	return true;
49 }
50 
51 static int fake_sigreturn_sve_change_vl(struct tdescr *td,
52 					siginfo_t *si, ucontext_t *uc)
53 {
54 	size_t resv_sz, offset;
55 	struct _aarch64_ctx *head = GET_SF_RESV_HEAD(sf);
56 	struct sve_context *sve;
57 
58 	/* Get a signal context with a SVE frame in it */
59 	if (!get_current_context(td, &sf.uc, sizeof(sf.uc)))
60 		return 1;
61 
62 	resv_sz = GET_SF_RESV_SIZE(sf);
63 	head = get_header(head, SVE_MAGIC, resv_sz, &offset);
64 	if (!head) {
65 		fprintf(stderr, "No SVE context\n");
66 		return 1;
67 	}
68 
69 	if (head->size != sizeof(struct sve_context)) {
70 		fprintf(stderr, "SVE register state active, skipping\n");
71 		return 1;
72 	}
73 
74 	sve = (struct sve_context *)head;
75 
76 	/* No changes are supported; init left us at minimum VL so go to max */
77 	fprintf(stderr, "Attempting to change VL from %d to %d\n",
78 		sve->vl, vls[0]);
79 	sve->vl = vls[0];
80 
81 	fake_sigreturn(&sf, sizeof(sf), 0);
82 
83 	return 1;
84 }
85 
86 struct tdescr tde = {
87 	.name = "FAKE_SIGRETURN_SVE_CHANGE",
88 	.descr = "Attempt to change SVE VL",
89 	.feats_required = FEAT_SVE,
90 	.sig_ok = SIGSEGV,
91 	.timeout = 3,
92 	.init = sve_get_vls,
93 	.run = fake_sigreturn_sve_change_vl,
94 };
95