/** @file
  GCC inline implementation of BaseLib processor specific functions.

  Copyright (c) 2006 - 2020, Intel Corporation. All rights reserved.<BR>
  Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "BaseLibInternals.h"

/**
  Used to serialize load and store operations.

  All loads and stores that proceed calls to this function are guaranteed to be
  globally visible when this function returns.

**/
VOID
EFIAPI
MemoryFence (
  VOID
  )
{
  // This is a little bit of overkill and it is more about the compiler that it is
  // actually processor synchronization. This is like the _ReadWriteBarrier
  // Microsoft specific intrinsic
  __asm__ __volatile__ ("":::"memory");
}

/**
  Requests CPU to pause for a short period of time.

  Requests CPU to pause for a short period of time. Typically used in MP
  systems to prevent memory starvation while waiting for a spin lock.

**/
VOID
EFIAPI
CpuPause (
  VOID
  )
{
  __asm__ __volatile__ ("pause");
}

/**
  Generates a breakpoint on the CPU.

  Generates a breakpoint on the CPU. The breakpoint must be implemented such
  that code can resume normal execution after the breakpoint.

**/
VOID
EFIAPI
CpuBreakpoint (
  VOID
  )
{
  __asm__ __volatile__ ("int $3");
}

/**
  Reads the current value of the EFLAGS register.

  Reads and returns the current value of the EFLAGS register. This function is
  only available on IA-32 and X64. This returns a 32-bit value on IA-32 and a
  64-bit value on X64.

  @return EFLAGS on IA-32 or RFLAGS on X64.

**/
UINTN
EFIAPI
AsmReadEflags (
  VOID
  )
{
  UINTN  Eflags;

  __asm__ __volatile__ (
    "pushfl     \n\t"
    "popl %0        "
    : "=r" (Eflags)
  );

  return Eflags;
}

/**
  Save the current floating point/SSE/SSE2 context to a buffer.

  Saves the current floating point/SSE/SSE2 state to the buffer specified by
  Buffer. Buffer must be aligned on a 16-byte boundary. This function is only
  available on IA-32 and X64.

  @param  Buffer  The pointer to a buffer to save the floating point/SSE/SSE2 context.

**/
VOID
EFIAPI
InternalX86FxSave (
  OUT     IA32_FX_BUFFER  *Buffer
  )
{
  __asm__ __volatile__ (
    "fxsave %0"
    :
    : "m" (*Buffer)  // %0
  );
}

/**
  Restores the current floating point/SSE/SSE2 context from a buffer.

  Restores the current floating point/SSE/SSE2 state from the buffer specified
  by Buffer. Buffer must be aligned on a 16-byte boundary. This function is
  only available on IA-32 and X64.

  @param  Buffer  The pointer to a buffer to save the floating point/SSE/SSE2 context.

**/
VOID
EFIAPI
InternalX86FxRestore (
  IN      CONST IA32_FX_BUFFER  *Buffer
  )
{
  __asm__ __volatile__ (
    "fxrstor %0"
    :
    : "m" (*Buffer)  // %0
  );
}

/**
  Reads the current value of 64-bit MMX Register #0 (MM0).

  Reads and returns the current value of MM0. This function is only available
  on IA-32 and X64.

  @return The current value of MM0.

**/
UINT64
EFIAPI
AsmReadMm0 (
  VOID
  )
{
  UINT64  Data;

  __asm__ __volatile__ (
    "push %%eax          \n\t"
    "push %%eax          \n\t"
    "movq %%mm0,  (%%esp)\n\t"
    "pop  %%eax          \n\t"
    "pop  %%edx          \n\t"
    : "=A"  (Data)       // %0
  );

  return Data;
}

/**
  Reads the current value of 64-bit MMX Register #1 (MM1).

  Reads and returns the current value of MM1. This function is only available
  on IA-32 and X64.

  @return The current value of MM1.

**/
UINT64
EFIAPI
AsmReadMm1 (
  VOID
  )
{
  UINT64  Data;

  __asm__ __volatile__ (
    "push %%eax          \n\t"
    "push %%eax          \n\t"
    "movq %%mm1,  (%%esp)\n\t"
    "pop  %%eax          \n\t"
    "pop  %%edx          \n\t"
    : "=A"  (Data)       // %0
  );

  return Data;
}

/**
  Reads the current value of 64-bit MMX Register #2 (MM2).

  Reads and returns the current value of MM2. This function is only available
  on IA-32 and X64.

  @return The current value of MM2.

**/
UINT64
EFIAPI
AsmReadMm2 (
  VOID
  )
{
  UINT64  Data;

  __asm__ __volatile__ (
    "push %%eax          \n\t"
    "push %%eax          \n\t"
    "movq %%mm2,  (%%esp)\n\t"
    "pop  %%eax          \n\t"
    "pop  %%edx          \n\t"
    : "=A"  (Data)       // %0
  );

  return Data;
}

/**
  Reads the current value of 64-bit MMX Register #3 (MM3).

  Reads and returns the current value of MM3. This function is only available
  on IA-32 and X64.

  @return The current value of MM3.

**/
UINT64
EFIAPI
AsmReadMm3 (
  VOID
  )
{
  UINT64  Data;

  __asm__ __volatile__ (
    "push %%eax          \n\t"
    "push %%eax          \n\t"
    "movq %%mm3,  (%%esp)\n\t"
    "pop  %%eax          \n\t"
    "pop  %%edx          \n\t"
    : "=A"  (Data)       // %0
  );

  return Data;
}

/**
  Reads the current value of 64-bit MMX Register #4 (MM4).

  Reads and returns the current value of MM4. This function is only available
  on IA-32 and X64.

  @return The current value of MM4.

**/
UINT64
EFIAPI
AsmReadMm4 (
  VOID
  )
{
  UINT64  Data;

  __asm__ __volatile__ (
    "push %%eax          \n\t"
    "push %%eax          \n\t"
    "movq %%mm4,  (%%esp)\n\t"
    "pop  %%eax          \n\t"
    "pop  %%edx          \n\t"
    : "=A"  (Data)       // %0
  );

  return Data;
}

/**
  Reads the current value of 64-bit MMX Register #5 (MM5).

  Reads and returns the current value of MM5. This function is only available
  on IA-32 and X64.

  @return The current value of MM5.

**/
UINT64
EFIAPI
AsmReadMm5 (
  VOID
  )
{
  UINT64  Data;

  __asm__ __volatile__ (
    "push %%eax          \n\t"
    "push %%eax          \n\t"
    "movq %%mm5,  (%%esp)\n\t"
    "pop  %%eax          \n\t"
    "pop  %%edx          \n\t"
    : "=A"  (Data)       // %0
  );

  return Data;
}

/**
  Reads the current value of 64-bit MMX Register #6 (MM6).

  Reads and returns the current value of MM6. This function is only available
  on IA-32 and X64.

  @return The current value of MM6.

**/
UINT64
EFIAPI
AsmReadMm6 (
  VOID
  )
{
  UINT64  Data;

  __asm__ __volatile__ (
    "push %%eax          \n\t"
    "push %%eax          \n\t"
    "movq %%mm6,  (%%esp)\n\t"
    "pop  %%eax          \n\t"
    "pop  %%edx          \n\t"
    : "=A"  (Data)       // %0
  );

  return Data;
}

/**
  Reads the current value of 64-bit MMX Register #7 (MM7).

  Reads and returns the current value of MM7. This function is only available
  on IA-32 and X64.

  @return The current value of MM7.

**/
UINT64
EFIAPI
AsmReadMm7 (
  VOID
  )
{
  UINT64  Data;

  __asm__ __volatile__ (
    "push %%eax          \n\t"
    "push %%eax          \n\t"
    "movq %%mm7,  (%%esp)\n\t"
    "pop  %%eax          \n\t"
    "pop  %%edx          \n\t"
    : "=A"  (Data)       // %0
  );

  return Data;
}

/**
  Writes the current value of 64-bit MMX Register #0 (MM0).

  Writes the current value of MM0. This function is only available on IA32 and
  X64.

  @param  Value The 64-bit value to write to MM0.

**/
VOID
EFIAPI
AsmWriteMm0 (
  IN      UINT64  Value
  )
{
  __asm__ __volatile__ (
    "movq %0, %%mm0"  // %0
    :
    : "m" (Value)
  );
}

/**
  Writes the current value of 64-bit MMX Register #1 (MM1).

  Writes the current value of MM1. This function is only available on IA32 and
  X64.

  @param  Value The 64-bit value to write to MM1.

**/
VOID
EFIAPI
AsmWriteMm1 (
  IN      UINT64  Value
  )
{
  __asm__ __volatile__ (
    "movq %0, %%mm1"  // %0
    :
    : "m" (Value)
  );
}

/**
  Writes the current value of 64-bit MMX Register #2 (MM2).

  Writes the current value of MM2. This function is only available on IA32 and
  X64.

  @param  Value The 64-bit value to write to MM2.

**/
VOID
EFIAPI
AsmWriteMm2 (
  IN      UINT64  Value
  )
{
  __asm__ __volatile__ (
    "movq %0, %%mm2"  // %0
    :
    : "m" (Value)
  );
}

/**
  Writes the current value of 64-bit MMX Register #3 (MM3).

  Writes the current value of MM3. This function is only available on IA32 and
  X64.

  @param  Value The 64-bit value to write to MM3.

**/
VOID
EFIAPI
AsmWriteMm3 (
  IN      UINT64  Value
  )
{
  __asm__ __volatile__ (
    "movq %0, %%mm3"  // %0
    :
    : "m" (Value)
  );
}

/**
  Writes the current value of 64-bit MMX Register #4 (MM4).

  Writes the current value of MM4. This function is only available on IA32 and
  X64.

  @param  Value The 64-bit value to write to MM4.

**/
VOID
EFIAPI
AsmWriteMm4 (
  IN      UINT64  Value
  )
{
  __asm__ __volatile__ (
    "movq %0, %%mm4"  // %0
    :
    : "m" (Value)
  );
}

/**
  Writes the current value of 64-bit MMX Register #5 (MM5).

  Writes the current value of MM5. This function is only available on IA32 and
  X64.

  @param  Value The 64-bit value to write to MM5.

**/
VOID
EFIAPI
AsmWriteMm5 (
  IN      UINT64  Value
  )
{
  __asm__ __volatile__ (
    "movq %0, %%mm5"  // %0
    :
    : "m" (Value)
  );
}

/**
  Writes the current value of 64-bit MMX Register #6 (MM6).

  Writes the current value of MM6. This function is only available on IA32 and
  X64.

  @param  Value The 64-bit value to write to MM6.

**/
VOID
EFIAPI
AsmWriteMm6 (
  IN      UINT64  Value
  )
{
  __asm__ __volatile__ (
    "movq %0, %%mm6"  // %0
    :
    : "m" (Value)
  );
}

/**
  Writes the current value of 64-bit MMX Register #7 (MM7).

  Writes the current value of MM7. This function is only available on IA32 and
  X64.

  @param  Value The 64-bit value to write to MM7.

**/
VOID
EFIAPI
AsmWriteMm7 (
  IN      UINT64  Value
  )
{
  __asm__ __volatile__ (
    "movq %0, %%mm7"  // %0
    :
    : "m" (Value)
  );
}

/**
  Reads the current value of Time Stamp Counter (TSC).

  Reads and returns the current value of TSC. This function is only available
  on IA-32 and X64.

  @return The current value of TSC

**/
UINT64
EFIAPI
AsmReadTsc (
  VOID
  )
{
  UINT64  Data;

  __asm__ __volatile__ (
    "rdtsc"
    : "=A" (Data)
  );

  return Data;
}
