Linux preempt-rt

Check our new training course

Real-Time Linux with PREEMPT_RT

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

Bootlin logo

Elixir Cross Referencer

/* Intel x86 GCC specific floating point register macros */

/*
 * Copyright (c) 2015, 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.
 */

#ifndef _FLOAT_REGS_X86_GCC_H
#define _FLOAT_REGS_X86_GCC_H

#if !defined(__GNUC__) || !defined(CONFIG_ISA_IA32)
#error test_asm_inline_gcc.h goes only with x86 GCC
#endif

#include <toolchain.h>
#include "float_context.h"

/**
 *
 * @brief Load all floating point registers
 *
 * This function loads ALL floating point registers from the memory buffer
 * specified by <pFromBuffer>. It is expected that a subsequent call to
 * _StoreAllFloatRegisters() will be issued to dump the floating point registers
 * to memory.
 *
 * The format/organization of the FP_REG_SET structure is not important; the
 * generic C test code (main.c and fiber.c) merely treat the FP_REG_SET
 * (and FP_NONVOLATILE_REG_SET) as an array of bytes.
 *
 * The only requirement is that the arch specific implementations of
 * _LoadAllFloatRegisters(), _StoreAllFloatRegisters(), and
 * _LoadThenStoreAllFloatRegisters agree on the format.
 *
 * @return N/A
 */

static inline void _LoadAllFloatRegisters(FP_REG_SET *pFromBuffer)
{
	/*
	 * The 'movdqu' is the "move double quad unaligned" instruction: Move
	 * a double quadword (16 bytes) between memory and an XMM register (or
	 * between a pair of XMM registers). The memory destination/source operand
	 * may be unaligned on a 16-byte boundary without causing an exception.
	 *
	 * The 'fldt' is the "load floating point value" instruction: Push an 80-bit
	 * (double extended-precision) onto the FPU register stack.
	 *
	 * A note about operand size specification in the AT&T assembler syntax:
	 *
	 *   Instructions are generally suffixed with the a letter or a pair of
	 *   letters to specify the operand size:
	 *
	 *    b  = byte (8 bit)
	 *    s  = short (16 bit integer) or single (32-bit floating point)
	 *    w  = word (16 bit)
	 *    l  = long (32 bit integer or 64-bit floating point)
	 *    q  = quad (64 bit)
	 *    t  = ten bytes (80-bit floating point)
	 *    dq = double quad (128 bit)
	 */

	__asm__ volatile (
		"movdqu  0(%0), %%xmm0\n\t;"
		"movdqu 16(%0), %%xmm1\n\t;"
		"movdqu 32(%0), %%xmm2\n\t;"
		"movdqu 48(%0), %%xmm3\n\t;"
		"movdqu 64(%0), %%xmm4\n\t;"
		"movdqu 80(%0), %%xmm5\n\t;"
		"movdqu 96(%0), %%xmm6\n\t;"
		"movdqu 112(%0), %%xmm7\n\t;"

		"fldt   128(%0)\n\t;"
		"fldt   138(%0)\n\t;"
		"fldt   148(%0)\n\t;"
		"fldt   158(%0)\n\t;"
		"fldt   168(%0)\n\t;"
		"fldt   178(%0)\n\t;"
		"fldt   188(%0)\n\t;"
		"fldt   198(%0)\n\t;"

		:: "r" (pFromBuffer)
		);
}


/**
 *
 * @brief Load then dump all float registers to memory
 *
 * This function loads ALL floating point registers from the memory buffer
 * specified by <pFromToBuffer>, and then stores them back to that buffer.
 *
 * This routine is called by a high priority thread prior to calling a primitive
 * that pends and triggers a co-operative context switch to a low priority
 * thread. Because the kernel doesn't save floating point context for
 * co-operative context switches, the x87 FPU register stack must be put back
 * in an empty state before the switch occurs in case the next task to perform
 * floating point operations was also co-operatively switched out and simply
 * inherits the existing x87 FPU state (expecting the stack to be empty).
 *
 * @return N/A
 */

static inline void _LoadThenStoreAllFloatRegisters(FP_REG_SET *pFromToBuffer)
{
	__asm__ volatile (
		"movdqu  0(%0), %%xmm0\n\t;"
		"movdqu 16(%0), %%xmm1\n\t;"
		"movdqu 32(%0), %%xmm2\n\t;"
		"movdqu 48(%0), %%xmm3\n\t;"
		"movdqu 64(%0), %%xmm4\n\t;"
		"movdqu 80(%0), %%xmm5\n\t;"
		"movdqu 96(%0), %%xmm6\n\t;"
		"movdqu 112(%0), %%xmm7\n\t;"

		"fldt   128(%0)\n\t;"
		"fldt   138(%0)\n\t;"
		"fldt   148(%0)\n\t;"
		"fldt   158(%0)\n\t;"
		"fldt   168(%0)\n\t;"
		"fldt   178(%0)\n\t;"
		"fldt   188(%0)\n\t;"
		"fldt   198(%0)\n\t;"

		/* pop the x87 FPU registers back to memory */

		"fstpt  198(%0)\n\t;"
		"fstpt  188(%0)\n\t;"
		"fstpt  178(%0)\n\t;"
		"fstpt  168(%0)\n\t;"
		"fstpt  158(%0)\n\t;"
		"fstpt  148(%0)\n\t;"
		"fstpt  138(%0)\n\t;"
		"fstpt  128(%0)\n\t;"

		:: "r" (pFromToBuffer)
		);
}


/**
 *
 * @brief Dump all floating point registers to memory
 *
 * This function stores ALL floating point registers to the memory buffer
 * specified by <pToBuffer>. It is expected that a previous invocation of
 * _LoadAllFloatRegisters() occurred to load all the floating point registers
 * from a memory buffer.
 *
 * @return N/A
 */

static inline void _StoreAllFloatRegisters(FP_REG_SET *pToBuffer)
{
	__asm__ volatile (
		"movdqu %%xmm0, 0(%0)\n\t;"
		"movdqu %%xmm1, 16(%0)\n\t;"
		"movdqu %%xmm2, 32(%0)\n\t;"
		"movdqu %%xmm3, 48(%0)\n\t;"
		"movdqu %%xmm4, 64(%0)\n\t;"
		"movdqu %%xmm5, 80(%0)\n\t;"
		"movdqu %%xmm6, 96(%0)\n\t;"
		"movdqu %%xmm7, 112(%0)\n\t;"

		"fstpt  198(%0)\n\t;"
		"fstpt  188(%0)\n\t;"
		"fstpt  178(%0)\n\t;"
		"fstpt  168(%0)\n\t;"
		"fstpt  158(%0)\n\t;"
		"fstpt  148(%0)\n\t;"
		"fstpt  138(%0)\n\t;"
		"fstpt  128(%0)\n\t;"

		:: "r" (pToBuffer) : "memory"
		);
}

/**
 *
 * @brief Dump non-volatile FP registers to memory
 *
 * This routine is called by a high priority thread after resuming execution
 * from calling a primitive that will pend and thus result in a co-operative
 * context switch to a low priority thread.
 *
 * Only the non-volatile floating point registers are expected to survive across
 * a function call, regardless of whether the call results in the thread being
 * pended.
 *
 * @return N/A
 */

void _StoreNonVolatileFloatRegisters(FP_NONVOLATILE_REG_SET *pToBuffer)
{
	ARG_UNUSED(pToBuffer);
	/* do nothing; there are no non-volatile floating point registers */
}
#endif /* _FLOAT_REGS_X86_GCC_H */