/** @file
  Provide functions to read or write FS base register.

  Copyright (c) 2026, Intel Corporation. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "BaseLibInternals.h"
#include <Register/Intel/Cpuid.h>

/**
  Worker function that returns TRUE if the CPU supports FS/GS base instructions,
  FALSE otherwise.

  @retval TRUE  The CPU supports FS/GS base instructions.
  @retval FALSE The CPU does not support FS/GS base instructions.
**/
BOOLEAN
InternalFsGsBaseSupported (
  VOID
  )
{
  UINT32                                       MaxLeaf;
  CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS_EBX  Ebx;

  AsmCpuid (CPUID_SIGNATURE, &MaxLeaf, NULL, NULL, NULL);
  if (MaxLeaf < CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS) {
    return FALSE;
  }

  AsmCpuidEx (
    CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS,
    CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS_SUB_LEAF_INFO,
    NULL,
    &Ebx.Uint32,
    NULL,
    NULL
    );
  return (BOOLEAN)(Ebx.Bits.FSGSBASE == 1);
}

/**
  Worker function that returns TRUE if the CPU enables FS/GS base instructions,
  FALSE otherwise.

  @retval TRUE  The CPU enables FS/GS base instructions.
  @retval FALSE The CPU does not enable FS/GS base instructions.
**/
BOOLEAN
InternalFsGsBaseEnabled (
  VOID
  )
{
  IA32_CR4  Cr4;

  Cr4.UintN = AsmReadCr4 ();
  return (BOOLEAN)(Cr4.Bits.FSGSBASE == 1);
}

/**
  Reads the current value of the FS segment base address.

  Reads and returns the current value of the FS segment base address using
  the RDFSBASE instruction. This function is only available on X64.

  Note: The function requires that CPUID.(EAX=7,ECX=0):EBX.FSGSBASE=1
  and CR4.FSGSBASE=1.

  @return The current value of the FS segment base address.
**/
UINT64
EFIAPI
AsmReadFsBase (
  VOID
  )
{
  ASSERT (InternalFsGsBaseSupported ());
  ASSERT (InternalFsGsBaseEnabled ());

  return InternalX86ReadFsBase ();
}

/**
  Writes a value to the FS segment base address.

  Writes FsBase to the FS segment base address register using the WRFSBASE
  instruction. This function is only available on X64.

  Note: The function requires that CPUID.(EAX=7,ECX=0):EBX.FSGSBASE=1
  and CR4.FSGSBASE=1.

  @param  FsBase  The value to write to the FS segment base address.

  @return  FsBase
**/
UINT64
EFIAPI
AsmWriteFsBase (
  IN      UINT64  FsBase
  )
{
  ASSERT (InternalFsGsBaseSupported ());
  ASSERT (InternalFsGsBaseEnabled ());

  InternalX86WriteFsBase (FsBase);
  return FsBase;
}
