/** @file
  EDKII Device Security library for SPDM device.
  It follows the SPDM Specification.

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

**/

#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include "hal/base.h"
#include "hal/library/memlib.h"

/**
 * Copies bytes from a source buffer to a destination buffer.
 *
 * This function copies "src_len" bytes from "src_buf" to "dst_buf".
 *
 * Asserts and returns a non-zero value if any of the following are true:
 *   1) "src_buf" or "dst_buf" are NULL.
 *   2) "src_len" or "dst_len" is greater than (SIZE_MAX >> 1).
 *   3) "src_len" is greater than "dst_len".
 *   4) "src_buf" and "dst_buf" overlap.
 *
 * If any of these cases fail, a non-zero value is returned. Additionally if
 * "dst_buf" points to a non-NULL value and "dst_len" is valid, then "dst_len"
 * bytes of "dst_buf" are zeroed.
 *
 * This function follows the C11 cppreference description of memcpy_s.
 * https://en.cppreference.com/w/c/string/byte/memcpy
 * The cppreferece description does NOT allow the source or destination
 * buffers to be NULL.
 *
 * This function differs from the Microsoft and Safeclib memcpy_s implementations
 * in that the Microsoft and Safeclib implementations allow for NULL source and
 * destinations pointers when the number of bytes to copy (src_len) is zero.
 *
 * In addition the Microsoft and Safeclib memcpy_s functions return different
 * negative values on error. For best support, clients should generally check
 * against zero for success or failure.
 *
 * @param    dst_buf   Destination buffer to copy to.
 * @param    dst_len   Maximum length in bytes of the destination buffer.
 * @param    src_buf   Source buffer to copy from.
 * @param    src_len   The number of bytes to copy from the source buffer.
 *
 * @return   0 on success. non-zero on error.
 *
 **/
void
libspdm_copy_mem (
  void        *dst_buf,
  size_t      dst_len,
  const void  *src_buf,
  size_t      src_len
  )
{
  volatile uint8_t        *dst;
  const volatile uint8_t  *src;

  dst = (volatile uint8_t *)dst_buf;
  src = (const volatile uint8_t *)src_buf;

  /* Check for case where "dst" or "dst_len" may be invalid.
   * Do not zero "dst" in this case. */
  if ((dst == NULL) || (dst_len > (SIZE_MAX >> 1))) {
    ASSERT (0);
  }

  /* Gaurd against invalid source. Zero "dst" in this case. */
  if (src == NULL) {
    ZeroMem (dst_buf, dst_len);
    ASSERT (0);
  }

  /* Guard against overlap case. Zero "dst" in these cases. */
  if (((src < dst) && (src + src_len > dst)) || ((dst < src) && (dst + src_len > src))) {
    ZeroMem (dst_buf, dst_len);
    ASSERT (0);
  }

  /* Guard against invalid lengths. Zero "dst" in these cases. */
  if ((src_len > dst_len) ||
      (src_len > (SIZE_MAX >> 1)))
  {
    ZeroMem (dst_buf, dst_len);
    ASSERT (0);
  }

  while (src_len-- != 0) {
    *(dst++) = *(src++);
  }
}

/**
 * Fills a target buffer with a byte value, and returns the target buffer.
 *
 * This function fills length bytes of buffer with value, and returns buffer.
 *
 * If length is greater than (MAX_ADDRESS - buffer + 1), then ASSERT().
 *
 * @param  buffer    The memory to set.
 * @param  length    The number of bytes to set.
 * @param  value     The value with which to fill length bytes of buffer.
 *
 * @return buffer.
 *
 **/
void
libspdm_set_mem (
  void     *buffer,
  size_t   length,
  uint8_t  value
  )
{
  SetMem (buffer, length, value);
}

/**
 * Fills a target buffer with zeros, and returns the target buffer.
 *
 * This function fills length bytes of buffer with zeros, and returns buffer.
 *
 * If length > 0 and buffer is NULL, then ASSERT().
 * If length is greater than (MAX_ADDRESS - buffer + 1), then ASSERT().
 *
 * @param  buffer      The pointer to the target buffer to fill with zeros.
 * @param  length      The number of bytes in buffer to fill with zeros.
 *
 * @return buffer.
 *
 **/
void
libspdm_zero_mem (
  void    *buffer,
  size_t  length
  )
{
  ZeroMem (buffer, length);
}

/**
 * Compares the contents of two buffers in const time.
 *
 * This function compares length bytes of source_buffer to length bytes of destination_buffer.
 * If all length bytes of the two buffers are identical, then 0 is returned.  Otherwise, the
 * value returned is the first mismatched byte in source_buffer subtracted from the first
 * mismatched byte in destination_buffer.
 *
 * If length > 0 and destination_buffer is NULL, then ASSERT().
 * If length > 0 and source_buffer is NULL, then ASSERT().
 * If length is greater than (MAX_ADDRESS - destination_buffer + 1), then ASSERT().
 * If length is greater than (MAX_ADDRESS - source_buffer + 1), then ASSERT().
 *
 * @param  destination_buffer A pointer to the destination buffer to compare.
 * @param  source_buffer      A pointer to the source buffer to compare.
 * @param  length            The number of bytes to compare.
 *
 * @return 0                 All length bytes of the two buffers are identical.
 * @retval Non-zero          There is mismatched between source_buffer and destination_buffer.
 *
 **/
bool
libspdm_consttime_is_mem_equal (
  const void  *destination_buffer,
  const void  *source_buffer,
  size_t      length
  )
{
  if (CompareMem (destination_buffer, source_buffer, length) == 0) {
    return true;
  } else {
    return false;
  }
}
