text: /** @file
  
    Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved.<BR>
    Copyright (C) 2013, Red Hat, Inc.
    Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
  
    SPDX-License-Identifier: BSD-2-Clause-Patent
  
  **/
  
  #include "Uefi.h"
  #include <Library/BaseLib.h>
  #include <Library/BaseMemoryLib.h>
  #include <Library/DebugLib.h>
  #include <Library/IoLib.h>
  #include <Library/QemuFwCfgLib.h>
  #include <Library/MemoryAllocationLib.h>
  #include <Library/UefiBootServicesTableLib.h>
  
  #include "QemuFwCfgLibInternal.h"
  
  
  /**
    Selects a firmware configuration item for reading.
    
    Following this call, any data read from this item will start from
    the beginning of the configuration item's data.
  
    @param[in] QemuFwCfgItem - Firmware Configuration item to read
  
  **/
  VOID
  EFIAPI
  QemuFwCfgSelectItem (
    IN FIRMWARE_CONFIG_ITEM   QemuFwCfgItem
    )
  {
    DEBUG ((EFI_D_INFO, "Select Item: 0x%x\n", (UINT16)(UINTN) QemuFwCfgItem));
    IoWrite16 (FW_CFG_IO_SELECTOR, (UINT16)(UINTN) QemuFwCfgItem);
  }
  
  /**
    Reads firmware configuration bytes into a buffer
  
    @param[in] Size - Size in bytes to read
    @param[in] Buffer - Buffer to store data into  (OPTIONAL if Size is 0)
  
  **/
  VOID
  EFIAPI
  InternalQemuFwCfgReadBytes (
    IN UINTN                  Size,
    IN VOID                   *Buffer  OPTIONAL
    )
  {
    if (InternalQemuFwCfgDmaIsAvailable () && Size <= MAX_UINT32) {
      InternalQemuFwCfgDmaBytes ((UINT32)Size, Buffer, FW_CFG_DMA_CTL_READ);
      return;
    }
    IoReadFifo8 (FW_CFG_IO_DATA, Size, Buffer);
  }
  
  
  /**
    Reads firmware configuration bytes into a buffer
  
    If called multiple times, then the data read will
    continue at the offset of the firmware configuration
    item where the previous read ended.
  
    @param[in] Size - Size in bytes to read
    @param[in] Buffer - Buffer to store data into
  
  **/
  VOID
  EFIAPI
  QemuFwCfgReadBytes (
    IN UINTN                  Size,
    IN VOID                   *Buffer
    )
  {
    if (InternalQemuFwCfgIsAvailable ()) {
      InternalQemuFwCfgReadBytes (Size, Buffer);
    } else {
      ZeroMem (Buffer, Size);
    }
  }
  
  /**
    Write firmware configuration bytes from a buffer
  
    If called multiple times, then the data written will
    continue at the offset of the firmware configuration
    item where the previous write ended.
  
    @param[in] Size - Size in bytes to write
    @param[in] Buffer - Buffer to read data from
  
  **/
  VOID
  EFIAPI
  QemuFwCfgWriteBytes (
    IN UINTN                  Size,
    IN VOID                   *Buffer
    )
  {
    if (InternalQemuFwCfgIsAvailable ()) {
      if (InternalQemuFwCfgDmaIsAvailable () && Size <= MAX_UINT32) {
        InternalQemuFwCfgDmaBytes ((UINT32)Size, Buffer, FW_CFG_DMA_CTL_WRITE);
        return;
      }
      IoWriteFifo8 (FW_CFG_IO_DATA, Size, Buffer);
    }
  }
  
  
  /**
    Skip bytes in the firmware configuration item.
  
    Increase the offset of the firmware configuration item without transferring
    bytes between the item and a caller-provided buffer. Subsequent read, write
    or skip operations will commence at the increased offset.
  
    @param[in] Size  Number of bytes to skip.
  **/
  VOID
  EFIAPI
  QemuFwCfgSkipBytes (
    IN UINTN                  Size
    )
  {
    UINTN ChunkSize;
    UINT8 SkipBuffer[256];
  
    if (!InternalQemuFwCfgIsAvailable ()) {
      return;
    }
  
    if (InternalQemuFwCfgDmaIsAvailable () && Size <= MAX_UINT32) {
      InternalQemuFwCfgDmaBytes ((UINT32)Size, NULL, FW_CFG_DMA_CTL_SKIP);
      return;
    }
  
    //
    // Emulate the skip by reading data in chunks, and throwing it away. The
    // implementation below is suitable even for phases where RAM or dynamic
    // allocation is not available or appropriate. It also doesn't affect the
    // static data footprint for client modules. Large skips are not expected,
    // therefore this fallback is not performance critical. The size of
    // SkipBuffer is thought not to exert a large pressure on the stack in any
    // phase.
    //
    while (Size > 0) {
      ChunkSize = MIN (Size, sizeof SkipBuffer);
      IoReadFifo8 (FW_CFG_IO_DATA, ChunkSize, SkipBuffer);
      Size -= ChunkSize;
    }
  }
  
  
  /**
    Reads a UINT8 firmware configuration value
  
    @return    Value of Firmware Configuration item read
  
  **/
  UINT8
  EFIAPI
  QemuFwCfgRead8 (
    VOID
    )
  {
    UINT8 Result;
  
    QemuFwCfgReadBytes (sizeof (Result), &Result);
  
    return Result;
  }
  
  
  /**
    Reads a UINT16 firmware configuration value
  
    @return    Value of Firmware Configuration item read
  
  **/
  UINT16
  EFIAPI
  QemuFwCfgRead16 (
    VOID
    )
  {
    UINT16 Result;
  
    QemuFwCfgReadBytes (sizeof (Result), &Result);
  
    return Result;
  }
  
  
  /**
    Reads a UINT32 firmware configuration value
  
    @return    Value of Firmware Configuration item read
  
  **/
  UINT32
  EFIAPI
  QemuFwCfgRead32 (
    VOID
    )
  {
    UINT32 Result;
  
    QemuFwCfgReadBytes (sizeof (Result), &Result);
  
    return Result;
  }
  
  
  /**
    Reads a UINT64 firmware configuration value
  
    @return    Value of Firmware Configuration item read
  
  **/
  UINT64
  EFIAPI
  QemuFwCfgRead64 (
    VOID
    )
  {
    UINT64 Result;
  
    QemuFwCfgReadBytes (sizeof (Result), &Result);
  
    return Result;
  }
  
  
  /**
    Find the configuration item corresponding to the firmware configuration file.
  
    @param[in]  Name - Name of file to look up.
    @param[out] Item - Configuration item corresponding to the file, to be passed
                       to QemuFwCfgSelectItem ().
    @param[out] Size - Number of bytes in the file.
  
    @return    RETURN_SUCCESS       If file is found.
               RETURN_NOT_FOUND     If file is not found.
               RETURN_UNSUPPORTED   If firmware configuration is unavailable.
  
  **/
  RETURN_STATUS
  EFIAPI
  QemuFwCfgFindFile (
    IN   CONST CHAR8           *Name,
    OUT  FIRMWARE_CONFIG_ITEM  *Item,
    OUT  UINTN                 *Size
    )
  {
    UINT32 Count;
    UINT32 Idx;
  
    if (!InternalQemuFwCfgIsAvailable ()) {
      return RETURN_UNSUPPORTED;
    }
  
    QemuFwCfgSelectItem (QemuFwCfgItemFileDir);
    Count = SwapBytes32 (QemuFwCfgRead32 ());
  
    for (Idx = 0; Idx < Count; ++Idx) {
      UINT32 FileSize;
      UINT16 FileSelect;
      UINT16 FileReserved;
      CHAR8  FName[QEMU_FW_CFG_FNAME_SIZE];
  
      FileSize     = QemuFwCfgRead32 ();
      FileSelect   = QemuFwCfgRead16 ();
      FileReserved = QemuFwCfgRead16 ();
      (VOID) FileReserved; /* Force a do-nothing reference. */
      InternalQemuFwCfgReadBytes (sizeof (FName), FName);
  
      if (AsciiStrCmp (Name, FName) == 0) {
        *Item = SwapBytes16 (FileSelect);
        *Size = SwapBytes32 (FileSize);
        return RETURN_SUCCESS;
      }
    }
  
    return RETURN_NOT_FOUND;
  }
  