/** @file

  Copyright (c) 2018-2020, Arm Limited. All rights reserved.

  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include <Guid/SgiVirtioDevicesFormSet.h>
#include <Library/VirtioMmioDeviceLib.h>
#include <Library/DevicePathLib.h>
#include <Library/DebugLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <SgiPlatform.h>

#pragma pack (1)
typedef struct {
  VENDOR_DEVICE_PATH                  VendorDevicePath;
  EFI_DEVICE_PATH_PROTOCOL            End;
} VIRTIO_DEVICE_PATH;
#pragma pack ()

STATIC VIRTIO_DEVICE_PATH mVirtioBlockDevicePath =
{
  {
    {
      HARDWARE_DEVICE_PATH,
      HW_VENDOR_DP,
      {
        (UINT8)(sizeof (VENDOR_DEVICE_PATH)),
        (UINT8)((sizeof (VENDOR_DEVICE_PATH)) >> 8)
      }
    },
    SGI_VIRTIO_BLOCK_GUID,
  },
  {
    END_DEVICE_PATH_TYPE,
    END_ENTIRE_DEVICE_PATH_SUBTYPE,
    {
      sizeof (EFI_DEVICE_PATH_PROTOCOL),
      0
    }
  }
};

STATIC VIRTIO_DEVICE_PATH mVirtioNetDevicePath =
{
  {
    {
      HARDWARE_DEVICE_PATH,
      HW_VENDOR_DP,
      {
        (UINT8)(sizeof (VENDOR_DEVICE_PATH)),
        (UINT8)((sizeof (VENDOR_DEVICE_PATH)) >> 8)
      }
    },
    SGI_VIRTIO_NET_GUID,
  },
  {
    END_DEVICE_PATH_TYPE,
    END_ENTIRE_DEVICE_PATH_SUBTYPE,
    {
      sizeof (EFI_DEVICE_PATH_PROTOCOL),
      0
    }
  }
};

/**
  Initialize platform Virtio devices.

  @return None.
**/
VOID
InitVirtioDevices (
  VOID
  )
{
  EFI_STATUS Status;
  STATIC EFI_HANDLE mVirtIoBlkController = NULL;
  STATIC EFI_HANDLE mVirtIoNetController = NULL;

  // Install protocol interface for storage device
  if ((FeaturePcdGet (PcdVirtioBlkSupported)) &&
      (FixedPcdGet32 (PcdVirtioBlkBaseAddress) != 0)) {
    Status = gBS->InstallProtocolInterface (&mVirtIoBlkController,
                    &gEfiDevicePathProtocolGuid, EFI_NATIVE_INTERFACE,
                    &mVirtioBlockDevicePath);
    if (EFI_ERROR (Status)) {
      DEBUG ((DEBUG_ERROR, "%a: Failed to install EFI_DEVICE_PATH protocol "
        "for Virtio Block device (Status = %r)\n",
        __FUNCTION__, Status));
    } else {
      // Declare the Virtio BlockIo device
      Status = VirtioMmioInstallDevice (FixedPcdGet32 (PcdVirtioBlkBaseAddress),
                 mVirtIoBlkController);
      if (EFI_ERROR (Status)) {
        DEBUG ((DEBUG_ERROR, "%a: Unable to find Virtio Block MMIO device "
          "(Status == %r)\n", __FUNCTION__, Status));
        gBS->UninstallProtocolInterface (
               mVirtIoBlkController,
               &gEfiDevicePathProtocolGuid,
               &mVirtioBlockDevicePath
             );
      } else {
        DEBUG ((DEBUG_INIT, "%a: Installed Virtio Block device\n",
          __FUNCTION__));
      }
    }
  }

  // Install protocol interface for network device
  if ((FeaturePcdGet (PcdVirtioNetSupported)) &&
      (FixedPcdGet32 (PcdVirtioNetBaseAddress) != 0)) {
    Status = gBS->InstallProtocolInterface (&mVirtIoNetController,
                    &gEfiDevicePathProtocolGuid, EFI_NATIVE_INTERFACE,
                    &mVirtioNetDevicePath);
    if (EFI_ERROR (Status)) {
      DEBUG ((DEBUG_ERROR, "%a: Failed to install EFI_DEVICE_PATH protocol "
        "for Virtio Network device (Status = %r)\n",
        __FUNCTION__, Status));
    } else {
      // Declare the Virtio Net device
      Status = VirtioMmioInstallDevice (FixedPcdGet32 (PcdVirtioNetBaseAddress),
                 mVirtIoNetController);
      if (EFI_ERROR (Status)) {
        DEBUG ((DEBUG_ERROR, "%a: Unable to find Virtio Network MMIO device "
          "(Status == %r)\n",
          __FUNCTION__, Status));
        gBS->UninstallProtocolInterface (
               mVirtIoNetController,
               &gEfiDevicePathProtocolGuid,
               &mVirtioNetDevicePath
            );
      } else {
        DEBUG ((DEBUG_INIT, "%a: Installed Virtio Network device\n",
          __FUNCTION__));
      }
    }
  }
}
