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