/** @file
  This driver produces Virtio Device Protocol instances for Virtio Mmio devices.
  Copyright (C) 2013, ARM Ltd.
  Copyright (C) 2017, AMD Inc. All rights reserved.
  SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include 
#include 
#include "VirtioMmioDevice.h"
STATIC CONST VIRTIO_DEVICE_PROTOCOL mMmioDeviceProtocolTemplate = {
    0,                                     // Revision
    0,                                     // SubSystemDeviceId
    VirtioMmioGetDeviceFeatures,           // GetDeviceFeatures
    VirtioMmioSetGuestFeatures,            // SetGuestFeatures
    VirtioMmioSetQueueAddress,             // SetQueueAddress
    VirtioMmioSetQueueSel,                 // SetQueueSel
    VirtioMmioSetQueueNotify,              // SetQueueNotify
    VirtioMmioSetQueueAlignment,           // SetQueueAlign
    VirtioMmioSetPageSize,                 // SetPageSize
    VirtioMmioGetQueueSize,                // GetQueueNumMax
    VirtioMmioSetQueueSize,                // SetQueueNum
    VirtioMmioGetDeviceStatus,             // GetDeviceStatus
    VirtioMmioSetDeviceStatus,             // SetDeviceStatus
    VirtioMmioDeviceWrite,                 // WriteDevice
    VirtioMmioDeviceRead,                  // ReadDevice
    VirtioMmioAllocateSharedPages,         // AllocateSharedPages
    VirtioMmioFreeSharedPages,             // FreeSharedPages
    VirtioMmioMapSharedBuffer,             // MapSharedBuffer
    VirtioMmioUnmapSharedBuffer            // UnmapSharedBuffer
};
/**
  Initialize the VirtIo MMIO Device
  @param[in] BaseAddress   Base Address of the VirtIo MMIO Device
  @param[in, out] Device   The driver instance to configure.
  @retval EFI_SUCCESS      Setup complete.
  @retval EFI_UNSUPPORTED  The driver is not a VirtIo MMIO device.
**/
STATIC
EFI_STATUS
EFIAPI
VirtioMmioInit (
  IN PHYSICAL_ADDRESS        BaseAddress,
  IN OUT VIRTIO_MMIO_DEVICE *Device
  )
{
  UINT32     MagicValue;
  UINT32     VendorId;
  UINT32     Version;
  //
  // Initialize VirtIo Mmio Device
  //
  CopyMem (&Device->VirtioDevice, &mMmioDeviceProtocolTemplate,
        sizeof (VIRTIO_DEVICE_PROTOCOL));
  Device->BaseAddress = BaseAddress;
  Device->VirtioDevice.Revision = VIRTIO_SPEC_REVISION (0, 9, 5);
  Device->VirtioDevice.SubSystemDeviceId =
          MmioRead32 (BaseAddress + VIRTIO_MMIO_OFFSET_DEVICE_ID);
  //
  // Double-check MMIO-specific values
  //
  MagicValue = VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_MAGIC);
  if (MagicValue != VIRTIO_MMIO_MAGIC) {
    return EFI_UNSUPPORTED;
  }
  Version = VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_VERSION);
  if (Version != 1) {
    return EFI_UNSUPPORTED;
  }
  //
  // Double-check MMIO-specific values
  //
  VendorId = VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_VENDOR_ID);
  if (VendorId != VIRTIO_VENDOR_ID) {
    //
    // The ARM Base and Foundation Models do not report a valid VirtIo VendorId.
    // They return a value of 0x0 for the VendorId.
    //
    DEBUG((DEBUG_WARN, "VirtioMmioInit: Warning: The VendorId (0x%X) does not "
                       "match the VirtIo VendorId (0x%X).\n",
                       VendorId, VIRTIO_VENDOR_ID));
  }
  return EFI_SUCCESS;
}
/**
  Uninitialize the internals of a virtio-mmio device that has been successfully
  set up with VirtioMmioInit().
  @param[in, out]  Device  The device to clean up.
**/
STATIC
VOID
EFIAPI
VirtioMmioUninit (
  IN VIRTIO_MMIO_DEVICE *Device
  )
{
  //
  // Note: This function mirrors VirtioMmioInit() that does not allocate any
  //       resources - there's nothing to free here.
  //
}
EFI_STATUS
VirtioMmioInstallDevice (
  IN PHYSICAL_ADDRESS       BaseAddress,
  IN EFI_HANDLE             Handle
  )
{
  EFI_STATUS          Status;
  VIRTIO_MMIO_DEVICE *VirtIo;
  if (!BaseAddress) {
    return EFI_INVALID_PARAMETER;
  }
  if (Handle == NULL) {
    return EFI_INVALID_PARAMETER;
  }
  //
  // Allocate VIRTIO_MMIO_DEVICE
  //
  VirtIo = AllocateZeroPool (sizeof (VIRTIO_MMIO_DEVICE));
  if (VirtIo == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
  VirtIo->Signature = VIRTIO_MMIO_DEVICE_SIGNATURE;
  Status = VirtioMmioInit (BaseAddress, VirtIo);
  if (EFI_ERROR (Status)) {
    goto FreeVirtioMem;
  }
  //
  // Install VIRTIO_DEVICE_PROTOCOL to Handle
  //
  Status = gBS->InstallProtocolInterface (&Handle,
                  &gVirtioDeviceProtocolGuid, EFI_NATIVE_INTERFACE,
                  &VirtIo->VirtioDevice);
  if (EFI_ERROR (Status)) {
    goto UninitVirtio;
  }
  return EFI_SUCCESS;
UninitVirtio:
  VirtioMmioUninit (VirtIo);
FreeVirtioMem:
  FreePool (VirtIo);
  return Status;
}
EFI_STATUS
VirtioMmioUninstallDevice (
  IN EFI_HANDLE             DeviceHandle
  )
{
  VIRTIO_DEVICE_PROTOCOL  *VirtioDevice;
  VIRTIO_MMIO_DEVICE      *MmioDevice;
  EFI_STATUS              Status;
  Status = gBS->OpenProtocol (
                  DeviceHandle,                  // candidate device
                  &gVirtioDeviceProtocolGuid,    // retrieve the VirtIo iface
                  (VOID **)&VirtioDevice,        // target pointer
                  DeviceHandle,                  // requestor driver identity
                  DeviceHandle,                  // requesting lookup for dev.
                  EFI_OPEN_PROTOCOL_GET_PROTOCOL // lookup only, no ref. added
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // Get the MMIO device from the VirtIo Device instance
  //
  MmioDevice = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (VirtioDevice);
  //
  // Uninstall the protocol interface
  //
  Status = gBS->UninstallProtocolInterface (DeviceHandle,
      &gVirtioDeviceProtocolGuid, &MmioDevice->VirtioDevice
      );
  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // Uninitialize the VirtIo Device
  //
  VirtioMmioUninit (MmioDevice);
  FreePool (MmioDevice);
  return EFI_SUCCESS;
}