/** @file
*  Manage XenBus device path and I/O handles
*
*  Copyright (c) 2015, Linaro Ltd. All rights reserved.
*
*  This program and the accompanying materials are
*  licensed and made available under the terms and conditions of the BSD License
*  which accompanies this distribution.  The full text of the license may be found at
*  http://opensource.org/licenses/bsd-license.php
*
*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
*
**/
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#pragma pack (1)
typedef struct {
  VENDOR_DEVICE_PATH                  Vendor;
  EFI_PHYSICAL_ADDRESS                GrantTableAddress;
  EFI_DEVICE_PATH_PROTOCOL            End;
} XENBUS_ROOT_DEVICE_PATH;
#pragma pack ()
STATIC CONST XENBUS_ROOT_DEVICE_PATH mXenBusRootDevicePathTemplate = {
  {
    {
      HARDWARE_DEVICE_PATH,
      HW_VENDOR_DP,
      { sizeof (VENDOR_DEVICE_PATH) + sizeof (EFI_PHYSICAL_ADDRESS), 0 }
    },
    XENBUS_ROOT_DEVICE_GUID,
  },
  0,
  {
    END_DEVICE_PATH_TYPE,
    END_ENTIRE_DEVICE_PATH_SUBTYPE,
    { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 }
  }
};
/**
  Install the XENBUS_ROOT_DEVICE_PATH and XENIO_PROTOCOL protocols on
  the handle pointed to by @Handle, or on a new handle if it points to
  NULL
  @param  Handle                Pointer to the handle to install the protocols
                                on, may point to a NULL handle.
  @param  GrantTableAddress     The address of the Xen grant table
  @retval EFI_SUCCESS           Protocols were installed successfully
  @retval EFI_OUT_OF_RESOURCES  The function failed to allocate memory required
                                by the XenIo MMIO and device path protocols
  @return                       Status code returned by the boot service
                                InstallMultipleProtocolInterfaces ()
**/
EFI_STATUS
XenIoMmioInstall (
  IN OUT   EFI_HANDLE              *Handle,
  IN       EFI_PHYSICAL_ADDRESS    GrantTableAddress
  )
{
  EFI_STATUS                     Status;
  XENIO_PROTOCOL                 *XenIo;
  XENBUS_ROOT_DEVICE_PATH        *XenBusDevicePath;
  EFI_HANDLE                     OutHandle;
  ASSERT (Handle != NULL);
  OutHandle = *Handle;
  XenIo = AllocateZeroPool (sizeof *XenIo);
  if (!XenIo) {
    return EFI_OUT_OF_RESOURCES;
  }
  XenIo->GrantTableAddress = GrantTableAddress;
  XenBusDevicePath = AllocateCopyPool (sizeof *XenBusDevicePath,
                       &mXenBusRootDevicePathTemplate);
  if (!XenBusDevicePath) {
    DEBUG ((EFI_D_ERROR, "%a: Out of memory\n", __FUNCTION__));
    Status = EFI_OUT_OF_RESOURCES;
    goto FreeXenIo;
  }
  XenBusDevicePath->GrantTableAddress = GrantTableAddress;
  Status = gBS->InstallMultipleProtocolInterfaces (&OutHandle,
                  &gEfiDevicePathProtocolGuid, XenBusDevicePath,
                  &gXenIoProtocolGuid, XenIo,
                  NULL);
  if (!EFI_ERROR (Status)) {
    *Handle = OutHandle;
    return EFI_SUCCESS;
  }
  
  DEBUG ((EFI_D_ERROR, "%a: Failed to install the EFI_DEVICE_PATH and "
    "XENIO_PROTOCOL protocols on handle %p (Status == %r)\n",
    __FUNCTION__, OutHandle, Status));
  FreePool (XenBusDevicePath);
FreeXenIo:
  FreePool (XenIo);
  return Status;
}
/**
  Uninstall the XENBUS_ROOT_DEVICE_PATH and XENIO_PROTOCOL protocols
  @param  Handle          Handle onto which the protocols have been installed
                          earlier by XenIoMmioInstall ()
  @retval EFI_SUCCESS     Protocols were uninstalled successfully
  @return                 Status code returned by the boot service
                          UninstallMultipleProtocolInterfaces ()
**/
EFI_STATUS
XenIoMmioUninstall (
  IN       EFI_HANDLE              Handle
  )
{
  EFI_STATUS    Status;
  VOID          *XenIo;
  VOID          *XenBusDevicePath;
  XenBusDevicePath = NULL;
  gBS->OpenProtocol (Handle, &gEfiDevicePathProtocolGuid, &XenBusDevicePath,
         NULL, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
  XenIo = NULL;
  gBS->OpenProtocol (Handle, &gXenIoProtocolGuid, &XenIo,
         NULL, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
  Status = gBS->UninstallMultipleProtocolInterfaces (Handle,
                  &gEfiDevicePathProtocolGuid, XenBusDevicePath,
                  &gXenIoProtocolGuid, XenIo,
                  NULL);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  FreePool (XenBusDevicePath);
  FreePool (XenIo);
  return EFI_SUCCESS;
}