Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  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#include <stddef.h>
 31
 32#include "../kselftest.h"
 33#include "rseq.h"
 34
 35static const ptrdiff_t *libc_rseq_offset_p;
 36static const unsigned int *libc_rseq_size_p;
 37static const unsigned int *libc_rseq_flags_p;
 38
 39/* Offset from the thread pointer to the rseq area.  */
 40ptrdiff_t rseq_offset;
 41
 42/* Size of the registered rseq area.  0 if the registration was
 43   unsuccessful.  */
 44unsigned int rseq_size = -1U;
 45
 46/* Flags used during rseq registration.  */
 47unsigned int rseq_flags;
 48
 49static int rseq_ownership;
 50
 51static
 52__thread struct rseq_abi __rseq_abi __attribute__((tls_model("initial-exec"))) = {
 53	.cpu_id = RSEQ_ABI_CPU_ID_UNINITIALIZED,
 54};
 55
 56static int sys_rseq(struct rseq_abi *rseq_abi, uint32_t rseq_len,
 57		    int flags, uint32_t sig)
 58{
 59	return syscall(__NR_rseq, rseq_abi, rseq_len, flags, sig);
 60}
 61
 62int rseq_available(void)
 63{
 64	int rc;
 65
 66	rc = sys_rseq(NULL, 0, 0, 0);
 67	if (rc != -1)
 68		abort();
 69	switch (errno) {
 70	case ENOSYS:
 71		return 0;
 72	case EINVAL:
 73		return 1;
 74	default:
 75		abort();
 76	}
 77}
 78
 79int rseq_register_current_thread(void)
 80{
 81	int rc;
 82
 83	if (!rseq_ownership) {
 84		/* Treat libc's ownership as a successful registration. */
 85		return 0;
 86	}
 87	rc = sys_rseq(&__rseq_abi, sizeof(struct rseq_abi), 0, RSEQ_SIG);
 88	if (rc)
 89		return -1;
 90	assert(rseq_current_cpu_raw() >= 0);
 91	return 0;
 92}
 93
 94int rseq_unregister_current_thread(void)
 95{
 96	int rc;
 97
 98	if (!rseq_ownership) {
 99		/* Treat libc's ownership as a successful unregistration. */
100		return 0;
101	}
102	rc = sys_rseq(&__rseq_abi, sizeof(struct rseq_abi), RSEQ_ABI_FLAG_UNREGISTER, RSEQ_SIG);
103	if (rc)
104		return -1;
105	return 0;
106}
107
108static __attribute__((constructor))
109void rseq_init(void)
110{
111	libc_rseq_offset_p = dlsym(RTLD_NEXT, "__rseq_offset");
112	libc_rseq_size_p = dlsym(RTLD_NEXT, "__rseq_size");
113	libc_rseq_flags_p = dlsym(RTLD_NEXT, "__rseq_flags");
114	if (libc_rseq_size_p && libc_rseq_offset_p && libc_rseq_flags_p &&
115			*libc_rseq_size_p != 0) {
116		/* rseq registration owned by glibc */
117		rseq_offset = *libc_rseq_offset_p;
118		rseq_size = *libc_rseq_size_p;
119		rseq_flags = *libc_rseq_flags_p;
120		return;
121	}
122	if (!rseq_available())
123		return;
124	rseq_ownership = 1;
125	rseq_offset = (void *)&__rseq_abi - rseq_thread_pointer();
126	rseq_size = sizeof(struct rseq_abi);
127	rseq_flags = 0;
128}
129
130static __attribute__((destructor))
131void rseq_exit(void)
132{
133	if (!rseq_ownership)
134		return;
135	rseq_offset = 0;
136	rseq_size = -1U;
137	rseq_ownership = 0;
138}
139
140int32_t rseq_fallback_current_cpu(void)
141{
142	int32_t cpu;
143
144	cpu = sched_getcpu();
145	if (cpu < 0) {
146		perror("sched_getcpu()");
147		abort();
148	}
149	return cpu;
150}