Boot Linux faster!

Check our new training course

Boot Linux faster!

Check our new training course
and Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
/*
 * Copyright (c) 2010-2014 Wind River Systems, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @file
 * @brief Floating point resource sharing routines
 *
 * This module allows multiple tasks and fibers to safely share the system's
 * floating point resources, by allowing the system to save FPU state
 * information in a task or fiber's stack region when a pre-emptive context
 * switch occurs.
 *
 * The floating point resource sharing mechanism is designed for minimal
 * intrusiveness.  Floating point thread saving is only performed for tasks and
 * fibers that explicitly enable FP resource sharing, to avoid impacting the
 * stack size requirements of all other tasks and fibers.  For those tasks and
 * fibers that do require FP resource sharing, a "lazy save/restore" mechanism
 * is employed so that the FPU's register sets are only switched in and out
 * when absolutely necessary; this avoids wasting effort preserving them when
 * there is no risk that they will be altered, or when there is no need to
 * preserve their contents.
 *
 * The following APIs are provided to allow floating point resource sharing to
 * be enabled or disabled at run-time:
 *
 * void fiber_float_enable  (nano_thread_id_t thread_id, unsigned int options)
 * void task_float_enable   (nano_thread_id_t thread_id, unsigned int options)
 * void fiber_float_disable (nano_thread_id_t thread_id)
 * void task_float_disable  (nano_thread_id_t thread_id)
 *
 * The 'options' parameter is used to specify what non-integer capabilities are
 * being used.  The same options accepted by fiber_fiber_start() are used in the
 * aforementioned APIs, namely USE_FP and USE_SSE.
 *
 * If the nanokernel has been built without SSE instruction support
 * (CONFIG_SSE), the system treats USE_SSE as if it was USE_FP.
 *
 * If the nanokernel has been built without floating point resource sharing
 * support (CONFIG_FP_SHARING), the aforementioned APIs and capabilities do not
 * exist.
 *
 * NOTE
 * It is possible for a single task or fiber to utilize floating instructions
 * _without_ enabling the FP resource sharing feature.  Since no other task or
 * fiber uses the FPU the FP registers won't change when the FP-capable task or
 * fiber isn't executing, meaning there is no need to save the registers.
 *
 * WARNING
 * The use of floating point instructions by ISRs is not supported by the
 * kernel.
 *
 * INTERNAL
 * If automatic enabling of floating point resource sharing _is not_ configured
 * the system leaves CR0[TS] = 0 for all tasks and fibers.  This means that any
 * task or fiber can perform floating point operations at any time without
 * causing an exception, and the system won't stop a task or fiber that
 * shouldn't be doing FP stuff from doing it.
 *
 * If automatic enabling of floating point resource sharing _is_ configured
 * the system leaves CR0[TS] = 0 only for tasks and fibers that are allowed to
 * perform FP operations.  All other tasks and fibers have CR0[TS] = 1 so that
 * an attempt to perform an FP operation will cause an exception, allowing the
 * system to enable FP resource sharing on its behalf.
 */

#ifdef CONFIG_MICROKERNEL
#include <microkernel.h>
#include <micro_private_types.h>
#endif /* CONFIG_MICROKERNEL */

#include <nano_private.h>
#include <toolchain.h>
#include <asm_inline.h>

/* the entire library vanishes without the FP_SHARING option enabled */

#ifdef CONFIG_FP_SHARING

#if defined(CONFIG_SSE)
extern uint32_t _sse_mxcsr_default_value; /* SSE control/status register default value */
#endif			/* CONFIG_SSE */

/**
 *
 * @brief Save non-integer context information
 *
 * This routine saves the system's "live" non-integer context into the
 * specified TCS.  If the specified task or fiber supports SSE then
 * x87/MMX/SSEx thread info is saved, otherwise only x87/MMX thread is saved.
 *
 * @param tcs TBD
 *
 * @return N/A
 */
static void _FpCtxSave(struct tcs *tcs)
{
	_do_fp_ctx_save(tcs->flags & USE_SSE, &tcs->preempFloatReg);
}

/**
 *
 * @brief Initialize non-integer context information
 *
 * This routine initializes the system's "live" non-integer context.
 *
 * @param tcs TBD
 *
 * @return N/A
 */
static inline void _FpCtxInit(struct tcs *tcs)
{
	_do_fp_ctx_init(tcs->flags & USE_SSE);
}

/**
 *
 * @brief Enable preservation of non-integer context information
 *
 * This routine allows the specified task/fiber (which may be the active
 * task/fiber) to safely share the system's floating point registers with
 * other tasks/fibers.  The <options> parameter indicates which floating point
 * register sets will be used by the specified task/fiber:
 *
 *  a) USE_FP  indicates x87 FPU and MMX registers only
 *  b) USE_SSE indicates x87 FPU and MMX and SSEx registers
 *
 * Invoking this routine creates a floating point thread for the task/fiber
 * that corresponds to an FPU that has been reset.  The system will thereafter
 * protect the task/fiber's FP context so that it is not altered during
 * a pre-emptive context switch.
 *
 * WARNING
 * This routine should only be used to enable floating point support for a
 * task/fiber that does not currently have such support enabled already.
 *
 * @param tcs  TDB
 * @param options set to either USE_FP or USE_SSE
 *
 * @return N/A
 *
 * INTERNAL
 * Since the transition from "non-FP supporting" to "FP supporting" must be done
 * atomically to avoid confusing the floating point logic used by _Swap(),
 * this routine locks interrupts to ensure that a context switch does not occur,
 * The locking isn't really needed when the routine is called by a fiber
 * (since context switching can't occur), but it is harmless and allows a single
 * routine to be called by both tasks and fibers (thus saving code space).
 *
 * If necessary, the interrupt latency impact of calling this routine from a
 * fiber could be lessened by re-designing things so that only task-type callers
 * locked interrupts (i.e. move the locking to task_float_enable()). However,
 * all calls to fiber_float_enable() would need to be reviewed to ensure they
 * are only used from a fiber, rather than from "generic" code used by both
 * tasks and fibers.
 */
void _FpEnable(struct tcs *tcs, unsigned int options)
{
	unsigned int imask;
	struct tcs *fp_owner;

	/* Lock interrupts to prevent a pre-emptive context switch from occuring
	 */

	imask = irq_lock();

	/* Indicate task/fiber requires non-integer context saving */

	tcs->flags |= options | USE_FP; /* USE_FP is treated as a "dirty bit" */

	/*
	 * Current task/fiber might not allow FP instructions, so clear CR0[TS]
	 * so we can use them. (CR0[TS] gets restored later on, if necessary.)
	 */

	__asm__ volatile("clts\n\t");

	/*
	 * Save the existing non-integer context (since it is about to change),
	 * but only if the FPU is "owned" by an FP-capable task that is
	 * currently
	 * handling an interrupt or exception (meaning it's FP context must be
	 * preserved).
	 */

	fp_owner = _nanokernel.current_fp;
	if (fp_owner) {
		if (fp_owner->flags & INT_OR_EXC_MASK) {
			_FpCtxSave(fp_owner);
		}
	}

	/* Now create a virgin FP context */

	_FpCtxInit(tcs);

	/* Associate the new FP context with the specified task/fiber */

	if (tcs == _nanokernel.current) {
		/*
		 * When enabling FP support for self, just claim ownership of
		 *the FPU
		 * and leave CR0[TS] unset.
		 *
		 * (Note: the FP context is "live" in hardware, not saved in TCS.)
		 */

		_nanokernel.current_fp = tcs;
	} else {
		/*
		 * When enabling FP support for someone else, assign ownership
		 * of the FPU to them (unless we need it ourselves).
		 */

		if ((_nanokernel.current->flags & USE_FP) != USE_FP) {
			/*
			 * We are not FP-capable, so mark FPU as owned by the
			 * thread
			 * we've just enabled FP support for, then disable our
			 * own
			 * FP access by setting CR0[TS] to its original state.
			 */

			_nanokernel.current_fp = tcs;
			_FpAccessDisable();
		} else {
			/*
			 * We are FP-capable (and thus had FPU ownership on
			 *entry), so save
			 * the new FP context in their TCS, leave FPU ownership
			 *with self,
			 * and leave CR0[TS] unset.
			 *
			 * Note: The saved FP context is needed in case the task
			 *or fiber
			 * we enabled FP support for is currently pre-empted,
			 *since _Swap()
			 * uses it to restore FP context when the task/fiber
			 *re-activates.
			 *
			 * Note: Saving the FP context reinits the FPU, and thus
			 *our own
			 * FP context, but that's OK since it didn't need to be
			 *preserved.
			 * (i.e. We aren't currently handling an interrupt or
			 *exception.)
			 */

			_FpCtxSave(tcs);
		}
	}

	irq_unlock(imask);
}

/**
 *
 * @brief Enable preservation of non-integer context information
 *
 * This routine allows a fiber to permit a task/fiber (including itself) to
 * safely share the system's floating point registers with other tasks/fibers.
 *
 * See the description of _FpEnable() for further details.
 *
 * @return N/A
 */
FUNC_ALIAS(_FpEnable, fiber_float_enable, void);

/**
 *
 * @brief Enable preservation of non-integer context information
 *
 * This routine allows a task to permit a task/fiber (including itself) to
 * safely share the system's floating point registers with other tasks/fibers.
 *
 * See the description of _FpEnable() for further details.
 *
 * @return N/A
 */
FUNC_ALIAS(_FpEnable, task_float_enable, void);

/**
 *
 * @brief Disable preservation of non-integer context information
 *
 * This routine prevents the specified task/fiber (which may be the active
 * task/fiber) from safely sharing any of the system's floating point registers
 * with other tasks/fibers.
 *
 * WARNING
 * This routine should only be used to disable floating point support for
 * a task/fiber that currently has such support enabled.
 *
 * @param tcs TBD
 *
 * @return N/A
 *
 * INTERNAL
 * Since the transition from "FP supporting" to "non-FP supporting" must be done
 * atomically to avoid confusing the floating point logic used by _Swap(),
 * this routine locks interrupts to ensure that a context switch does not occur,
 * The locking isn't really needed when the routine is called by a fiber
 * (since context switching can't occur), but it is harmless and allows a single
 * routine to be called by both tasks and fibers (thus saving code space).
 *
 * If necessary, the interrupt latency impact of calling this routine from a
 * fiber could be lessened by re-designing things so that only task-type callers
 * locked interrupts (i.e. move the locking to task_float_disable()). However,
 * all calls to fiber_float_disable() would need to be reviewed to ensure they
 * are only used from a fiber, rather than from "generic" code used by both
 * tasks and fibers.
 */
void _FpDisable(struct tcs *tcs)
{
	unsigned int imask;

	/* Lock interrupts to prevent a pre-emptive context switch from occuring
	 */

	imask = irq_lock();

	/*
	 * Disable _all_ floating point capabilities for the task/fiber,
	 * regardless
	 * of the options specified at the time support was enabled.
	 */

	tcs->flags &= ~(USE_FP | USE_SSE);

	if (tcs == _nanokernel.current) {
		_FpAccessDisable();
		_nanokernel.current_fp = (struct tcs *)0;
	} else {
		if (_nanokernel.current_fp == tcs)
			_nanokernel.current_fp = (struct tcs *)0;
	}

	irq_unlock(imask);
}

/**
 *
 * @brief Disable preservation of non-integer context
 *information
 *
 * This routine allows a fiber to disallow a task/fiber (including itself) from
 * safely sharing any of the system's floating point registers with other
 * tasks/fibers.
 *
 * WARNING
 * This routine should only be used to disable floating point support for
 * a task/fiber that currently has such support enabled.
 *
 * @return N/A
 */
FUNC_ALIAS(_FpDisable, fiber_float_disable, void);

/**
 *
 * @brief Disable preservation of non-integer context information
 *
 * This routine allows a task to disallow a task/fiber (including itself) from
 * safely sharing any of the system's floating point registers with other
 * tasks/fibers.
 *
 * WARNING
 * This routine should only be used to disable floating point support for
 * a task/fiber that currently has such support enabled.
 *
 * @return N/A
 */
FUNC_ALIAS(_FpDisable, task_float_disable, void);


/**
 *
 * @brief Handler for "device not available" exception
 *
 * This routine is registered to handle the "device not available" exception
 * (vector = 7)
 *
 * The processor will generate this exception if any x87 FPU, MMX, or SSEx
 * instruction is executed while CR0[TS]=1.  The handler then enables the
 * current task or fiber with the USE_FP option (or the USE_SSE option if the
 * SSE configuration option has been enabled).
 *
 * @param pEsf this value is not used for this architecture
 *
 * @return N/A
 */
void _FpNotAvailableExcHandler(NANO_ESF * pEsf)
{
	unsigned int enableOption;

	ARG_UNUSED(pEsf);

	/*
	 * Assume the exception did not occur in the thread of an ISR.
	 * (In other words, CPU cycles will not be consumed to perform
	 * error checking to ensure the exception was not generated in an ISR.)
	 */

	PRINTK("_FpNotAvailableExcHandler() exception handler has been "
	       "invoked\n");

/* Enable the highest level of FP capability configured into the kernel */

#ifdef CONFIG_SSE
	enableOption = USE_SSE;
#else
	enableOption = USE_FP;
#endif

	_FpEnable(_nanokernel.current, enableOption);
}
_EXCEPTION_CONNECT_NOCODE(_FpNotAvailableExcHandler, IV_DEVICE_NOT_AVAILABLE);

#endif /* CONFIG_FP_SHARING */