init.c 4.74 KB
Newer Older
Philippe Gerum's avatar
Philippe Gerum committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
 * SPDX-License-Identifier: MIT
 *
 * Copyright (C) 2018 Philippe Gerum  <rpm@xenomai.org>
 */

#include <sys/types.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
#include <string.h>
#include <stdio.h>
17
#include <linux/types.h>
18
#include <valgrind/valgrind.h>
19
20
21
#include <evl/evl.h>
#include <evl/syscall.h>
#include <evl/thread.h>
22
#include <asm/evl/vdso.h>
23
24
#include <uapi/evl/control.h>
#include <uapi/evl/signal.h>
25
#include "parse_vdso.h"
Philippe Gerum's avatar
Philippe Gerum committed
26
27
#include "internal.h"

28
29
30
31
32
33
34
35
#ifndef EVL_ABI_BASE
/*
 * EVL_ABI_BASE was not defined prior to ABI 18, until support for ABI
 * ranges was introduced in the core.
 */
#define EVL_ABI_BASE  17
#else
#endif
Philippe Gerum's avatar
Philippe Gerum committed
36
37
#if !(EVL_ABI_PREREQ >= EVL_ABI_BASE && EVL_ABI_PREREQ <= EVL_ABI_LEVEL)
#error "kernel does not meet our ABI requirements (uapi vs EVL_ABI_PREREQ)"
38
39
#endif

Philippe Gerum's avatar
Philippe Gerum committed
40
41
42
43
44
45
static pthread_once_t init_once = PTHREAD_ONCE_INIT;

static int init_status;

static struct evl_core_info core_info;

46
int evl_ctlfd = -1;
Philippe Gerum's avatar
Philippe Gerum committed
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

void *evl_shared_memory = NULL;

static void atfork_unmap_shmem(void)
{
	if (evl_shared_memory) {
		munmap(evl_shared_memory, core_info.shm_size);
		evl_shared_memory = NULL;
	}

	if (evl_ctlfd >= 0) {
		close(evl_ctlfd);
		evl_ctlfd = -1;
	}

	init_once = PTHREAD_ONCE_INIT;
}

static inline int generic_init(void)
{
	int ctlfd, ret;
	void *shmem;

70
71
	/*
	 * Failing to open the control device with ENOENT is a clear
72
73
	 * sign that we have no EVL core in there. Return with -ENOSYS
	 * to give a clear hint about this.
74
	 */
75
	ctlfd = open(EVL_CONTROL_DEV, O_RDWR);
76
77
78
79
80
81
82
	if (ctlfd < 0) {
		if (errno == ENOENT) {
			fprintf(stderr,	"evl: core not enabled in kernel\n");
			return -ENOSYS;
		}
		return -errno;
	}
Philippe Gerum's avatar
Philippe Gerum committed
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

	ret = fcntl(ctlfd, F_GETFD, 0);
	if (ret < 0) {
		ret = -errno;
		goto fail;
	}

	ret = fcntl(ctlfd, F_SETFD, ret | O_CLOEXEC);
	if (ret < 0) {
		ret = -errno;
		goto fail;
	}

	ret = ioctl(ctlfd, EVL_CTLIOC_GET_COREINFO, &core_info);
	if (ret) {
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
		/*
		 * sizeof(core_info) is encoded into
		 * EVL_CTLIOC_GET_COREINFO, in which case we might
		 * receive ENOTTY if some ABI change involved updating
		 * the core info struct itself.
		 */
		if (errno != ENOTTY) {
			ret = -errno;
			goto fail;
		}
		/*
		 * core_info was not filled in, make sure we catch the
		 * ABI discrepancy.
		 */
		core_info.abi_base = (__u32)-1;
Philippe Gerum's avatar
Philippe Gerum committed
113
114
	}

Philippe Gerum's avatar
Philippe Gerum committed
115
116
	if (EVL_ABI_PREREQ < core_info.abi_base ||
		EVL_ABI_PREREQ > core_info.abi_current) {
117
118
		fprintf(stderr,
			"evl: ABI mismatch, see -ENOEXEC at https://evlproject.org/"
119
			"core/user-api/init/#evl_init\n");
120
121
122
123
		ret = -ENOEXEC;
		goto fail;
	}

124
125
126
127
	ret = attach_evl_clocks();
	if (ret)
		goto fail;

Philippe Gerum's avatar
Philippe Gerum committed
128
	shmem = mmap(NULL, core_info.shm_size, PROT_READ|PROT_WRITE,
Philippe Gerum's avatar
Philippe Gerum committed
129
		MAP_SHARED, ctlfd, 0);
Philippe Gerum's avatar
Philippe Gerum committed
130
131
132
133
134
	if (shmem == MAP_FAILED) {
		ret = -errno;
		goto fail;
	}

135
	pthread_atfork(NULL, NULL, atfork_unmap_shmem);
Philippe Gerum's avatar
Philippe Gerum committed
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
	evl_ctlfd = ctlfd;
	evl_shared_memory = shmem;

	return 0;
fail:
	close(ctlfd);

	return ret;
}

#define raw_write_out(__msg)					\
	do {							\
		int __ret;					\
		__ret = write(1, __msg , strlen(__msg));	\
		(void)__ret;					\
	} while (0)

static const char *sigdebug_msg[] = {
154
155
156
157
158
159
160
161
	[EVL_HMDIAG_SIGDEMOTE] = "switched inband (signal)\n",
	[EVL_HMDIAG_SYSDEMOTE] = "switched inband (syscall)\n",
	[EVL_HMDIAG_EXDEMOTE] = "switched inband (fault)\n",
	[EVL_HMDIAG_LKDEPEND] = "switched inband while holding mutex\n",
	[EVL_HMDIAG_WATCHDOG] = "watchdog triggered\n",
	[EVL_HMDIAG_LKIMBALANCE] = "mutex lock/unlock imbalance\n",
	[EVL_HMDIAG_LKSLEEP] = "attempt to sleep while holding a mutex\n",
	[EVL_HMDIAG_STAGEX] = "locked out from out-of-band stage (stax)\n",
Philippe Gerum's avatar
Philippe Gerum committed
162
163
};

164
165
/* A basic SIGDEBUG handler which only prints out the cause. */

Philippe Gerum's avatar
Philippe Gerum committed
166
167
168
169
void evl_sigdebug_handler(int sig, siginfo_t *si, void *ctxt)
{
	if (sigdebug_marked(si)) {
		switch (sigdebug_cause(si)) {
170
171
172
173
174
175
176
177
		case EVL_HMDIAG_SIGDEMOTE:
		case EVL_HMDIAG_SYSDEMOTE:
		case EVL_HMDIAG_EXDEMOTE:
		case EVL_HMDIAG_LKDEPEND:
		case EVL_HMDIAG_WATCHDOG:
		case EVL_HMDIAG_LKIMBALANCE:
		case EVL_HMDIAG_LKSLEEP:
		case EVL_HMDIAG_STAGEX:
Philippe Gerum's avatar
Philippe Gerum committed
178
179
180
181
182
183
			raw_write_out(sigdebug_msg[sigdebug_cause(si)]);
			break;
		}
	}
}

184
185
static void resolve_vdso_calls(void)
{
186
187
188
189
190
191
192
	/*
	 * We have no vDSO if running Valgrind, always use fallback
	 * calls.
	 */
	if (RUNNING_ON_VALGRIND)
		return;

193
194
195
196
197
198
199
	evl_init_vdso();

	__evl_clock_gettime = evl_request_vdso(__EVL_VDSO_KVERSION,
					__EVL_VDSO_GETTIME);
}

static int do_init(void)
Philippe Gerum's avatar
Philippe Gerum committed
200
201
202
{
	int ret;

203
	resolve_vdso_calls();
Philippe Gerum's avatar
Philippe Gerum committed
204
205
206
207
208
209
210
211
212

	ret = mlockall(MCL_CURRENT | MCL_FUTURE);
	if (ret)
		return -errno;

	ret = generic_init();
	if (ret)
		return ret;

213
	init_proxy_streams();
Philippe Gerum's avatar
Philippe Gerum committed
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236

	return 0;
}

static void do_init_once(void)
{
	init_status = do_init();
}

int evl_init(void)
{
	pthread_once(&init_once, do_init_once);

	return init_status;
}

unsigned int evl_detect_fpu(void)
{
	if (evl_init())
		return 0;

	return core_info.fpu_features;
}