/*/@file
  Qemu fw-cfg wrappers for hardware info parsing.
  Provides an alternative to parse hardware information from a fw-cfg
  file without relying on dynamic memory allocations.

  Copyright 2021 - 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include <Library/DebugLib.h>
#include <Library/QemuFwCfgLib.h>

#include <Library/HardwareInfoLib.h>

/**
  Update an optional pointer value if possible

  @param[out] DataSize     Pointer to variable to be updated
  @param[in]  Value        Value to set the pointed variable to.
**/
STATIC
VOID
UpdateDataSize (
  OUT UINTN  *DataSize,
  IN  UINTN  Value
  )
{
  if (DataSize == NULL) {
    return;
  }

  *DataSize = Value;
}

EFI_STATUS
QemuFwCfgReadNextHardwareInfoByType (
  IN      HARDWARE_INFO_TYPE  Type,
  IN      UINTN               TypeSize,
  IN      UINTN               TotalFileSize,
  OUT     VOID                *Data,
  OUT     UINTN               *DataSize         OPTIONAL,
  IN OUT  UINTN               *ReadIndex
  )
{
  HARDWARE_INFO_HEADER  Header;

  if ((Data == NULL) ||
      (ReadIndex == NULL) ||
      (TypeSize == 0) ||
      (Type == HardwareInfoTypeUndefined) ||
      (TotalFileSize == 0))
  {
    return EFI_INVALID_PARAMETER;
  }

  UpdateDataSize (DataSize, 0);

  while (*ReadIndex < TotalFileSize) {
    QemuFwCfgReadBytes (sizeof (Header), &Header);
    *ReadIndex += sizeof (Header);

    if ((Header.Size > MAX_UINTN) || (((UINT64)*ReadIndex + Header.Size) > TotalFileSize)) {
      *ReadIndex = TotalFileSize;
      return EFI_ABORTED;
    }

    if ((Header.Type.Value == Type) && (Header.Size <= TypeSize)) {
      QemuFwCfgReadBytes ((UINTN)Header.Size, Data);

      *ReadIndex += (UINTN)Header.Size;
      UpdateDataSize (DataSize, (UINTN)Header.Size);

      return EFI_SUCCESS;
    }

    //
    // Skip the bytes corresponding to the next element as it is
    // not of the expected type and/or size. The TotalFileSize
    // and individual elements sizes should match so the size
    // check is skipped.
    //
    QemuFwCfgSkipBytes ((UINTN)Header.Size);
    *ReadIndex += (UINTN)Header.Size;
  }

  return EFI_END_OF_FILE;
}
