selftests/rseq: Uplift rseq selftests for compatibility with glibc-2.35
[platform/kernel/linux-rpi.git] / tools / testing / selftests / rseq / rseq.c
1 // SPDX-License-Identifier: LGPL-2.1
2 /*
3  * rseq.c
4  *
5  * Copyright (C) 2016 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; only
10  * version 2.1 of the License.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  */
17
18 #define _GNU_SOURCE
19 #include <errno.h>
20 #include <sched.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <syscall.h>
26 #include <assert.h>
27 #include <signal.h>
28 #include <limits.h>
29 #include <dlfcn.h>
30
31 #include "../kselftest.h"
32 #include "rseq.h"
33
34 static const int *libc_rseq_offset_p;
35 static const unsigned int *libc_rseq_size_p;
36 static const unsigned int *libc_rseq_flags_p;
37
38 /* Offset from the thread pointer to the rseq area.  */
39 int rseq_offset;
40
41 /* Size of the registered rseq area.  0 if the registration was
42    unsuccessful.  */
43 unsigned int rseq_size = -1U;
44
45 /* Flags used during rseq registration.  */
46 unsigned int rseq_flags;
47
48 static int rseq_ownership;
49
50 static
51 __thread struct rseq_abi __rseq_abi __attribute__((tls_model("initial-exec"))) = {
52         .cpu_id = RSEQ_ABI_CPU_ID_UNINITIALIZED,
53 };
54
55 static int sys_rseq(struct rseq_abi *rseq_abi, uint32_t rseq_len,
56                     int flags, uint32_t sig)
57 {
58         return syscall(__NR_rseq, rseq_abi, rseq_len, flags, sig);
59 }
60
61 int rseq_available(void)
62 {
63         int rc;
64
65         rc = sys_rseq(NULL, 0, 0, 0);
66         if (rc != -1)
67                 abort();
68         switch (errno) {
69         case ENOSYS:
70                 return 0;
71         case EINVAL:
72                 return 1;
73         default:
74                 abort();
75         }
76 }
77
78 int rseq_register_current_thread(void)
79 {
80         int rc;
81
82         if (!rseq_ownership) {
83                 /* Treat libc's ownership as a successful registration. */
84                 return 0;
85         }
86         rc = sys_rseq(&__rseq_abi, sizeof(struct rseq_abi), 0, RSEQ_SIG);
87         if (rc)
88                 return -1;
89         assert(rseq_current_cpu_raw() >= 0);
90         return 0;
91 }
92
93 int rseq_unregister_current_thread(void)
94 {
95         int rc;
96
97         if (!rseq_ownership) {
98                 /* Treat libc's ownership as a successful unregistration. */
99                 return 0;
100         }
101         rc = sys_rseq(&__rseq_abi, sizeof(struct rseq_abi), RSEQ_ABI_FLAG_UNREGISTER, RSEQ_SIG);
102         if (rc)
103                 return -1;
104         return 0;
105 }
106
107 static __attribute__((constructor))
108 void rseq_init(void)
109 {
110         libc_rseq_offset_p = dlsym(RTLD_NEXT, "__rseq_offset");
111         libc_rseq_size_p = dlsym(RTLD_NEXT, "__rseq_size");
112         libc_rseq_flags_p = dlsym(RTLD_NEXT, "__rseq_flags");
113         if (libc_rseq_size_p && libc_rseq_offset_p && libc_rseq_flags_p) {
114                 /* rseq registration owned by glibc */
115                 rseq_offset = *libc_rseq_offset_p;
116                 rseq_size = *libc_rseq_size_p;
117                 rseq_flags = *libc_rseq_flags_p;
118                 return;
119         }
120         if (!rseq_available())
121                 return;
122         rseq_ownership = 1;
123         rseq_offset = (void *)&__rseq_abi - rseq_thread_pointer();
124         rseq_size = sizeof(struct rseq_abi);
125         rseq_flags = 0;
126 }
127
128 static __attribute__((destructor))
129 void rseq_exit(void)
130 {
131         if (!rseq_ownership)
132                 return;
133         rseq_offset = 0;
134         rseq_size = -1U;
135         rseq_ownership = 0;
136 }
137
138 int32_t rseq_fallback_current_cpu(void)
139 {
140         int32_t cpu;
141
142         cpu = sched_getcpu();
143         if (cpu < 0) {
144                 perror("sched_getcpu()");
145                 abort();
146         }
147         return cpu;
148 }