+/** @file\r
+\r
+ This driver produces Virtio Device Protocol instances for Virtio Mmio devices.\r
+\r
+ Copyright (C) 2013, ARM Ltd.\r
+\r
+ This program and the accompanying materials are licensed and made available\r
+ under the terms and conditions of the BSD License which accompanies this\r
+ distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
+ WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+\r
+#include "VirtioMmioDevice.h"\r
+\r
+static VIRTIO_DEVICE_PROTOCOL mMmioDeviceProtocolTemplate = {\r
+ 0, // Revision\r
+ 0, // SubSystemDeviceId\r
+ VirtioMmioGetDeviceFeatures, // GetDeviceFeatures\r
+ VirtioMmioSetGuestFeatures, // SetGuestFeatures\r
+ VirtioMmioGetQueueAddress, // GetQueueAddress\r
+ VirtioMmioSetQueueAddress, // SetQueueAddress\r
+ VirtioMmioSetQueueSel, // SetQueueSel\r
+ VirtioMmioSetQueueNotify, // SetQueueNotify\r
+ VirtioMmioSetQueueAlignment, // SetQueueAlign\r
+ VirtioMmioSetPageSize, // SetPageSize\r
+ VirtioMmioGetQueueSize, // GetQueueNumMax\r
+ VirtioMmioSetQueueSize, // SetQueueNum\r
+ VirtioMmioGetDeviceStatus, // GetDeviceStatus\r
+ VirtioMmioSetDeviceStatus, // SetDeviceStatus\r
+ VirtioMmioDeviceWrite, // WriteDevice\r
+ VirtioMmioDeviceRead // ReadDevice\r
+};\r
+\r
+/**\r
+\r
+ Initialize the VirtIo MMIO Device\r
+\r
+ @param[in] BaseAddress Base Address of the VirtIo MMIO Device\r
+\r
+ @param[in, out] Device The driver instance to configure.\r
+\r
+ @retval EFI_SUCCESS Setup complete.\r
+\r
+ @retval EFI_UNSUPPORTED The driver is not a VirtIo MMIO device.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioMmioInit (\r
+ IN PHYSICAL_ADDRESS BaseAddress,\r
+ IN OUT VIRTIO_MMIO_DEVICE *Device\r
+ )\r
+{\r
+ UINT32 MagicValue;\r
+ UINT32 VendorId;\r
+ UINT32 Version;\r
+\r
+ //\r
+ // Initialize VirtIo Mmio Device\r
+ //\r
+ CopyMem (&Device->VirtioDevice, &mMmioDeviceProtocolTemplate,\r
+ sizeof (VIRTIO_DEVICE_PROTOCOL));\r
+ Device->BaseAddress = BaseAddress;\r
+ Device->VirtioDevice.Revision = VIRTIO_SPEC_REVISION (0, 9, 5);\r
+ Device->VirtioDevice.SubSystemDeviceId =\r
+ MmioRead32 (BaseAddress + VIRTIO_MMIO_OFFSET_DEVICE_ID);\r
+\r
+ //\r
+ // Double-check MMIO-specific values\r
+ //\r
+ MagicValue = VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_MAGIC);\r
+ if (MagicValue != VIRTIO_MMIO_MAGIC) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ Version = VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_VERSION);\r
+ if (Version != 1) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ //\r
+ // Double-check MMIO-specific values\r
+ //\r
+ VendorId = VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_VENDOR_ID);\r
+ if (VendorId != VIRTIO_VENDOR_ID) {\r
+ //\r
+ // The ARM Base and Foundation Models do not report a valid VirtIo VendorId.\r
+ // They return a value of 0x0 for the VendorId.\r
+ //\r
+ DEBUG((EFI_D_WARN, "VirtioMmioInit: Warning: The VendorId (0x%X) does not "\r
+ "match the VirtIo VendorId (0x%X).\n",\r
+ VendorId, VIRTIO_VENDOR_ID));\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+\r
+ Uninitialize the internals of a virtio-mmio device that has been successfully\r
+ set up with VirtioMmioInit().\r
+\r
+ @param[in, out] Device The device to clean up.\r
+\r
+**/\r
+\r
+STATIC\r
+VOID\r
+EFIAPI\r
+VirtioMmioUninit (\r
+ IN VIRTIO_MMIO_DEVICE *Device\r
+ )\r
+{\r
+ //\r
+ // Note: This function mirrors VirtioMmioInit() that does not allocate any\r
+ // resources - there's nothing to free here.\r
+ //\r
+}\r
+\r
+EFI_STATUS\r
+VirtioMmioInstallDevice (\r
+ IN PHYSICAL_ADDRESS BaseAddress,\r
+ IN EFI_HANDLE Handle\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ VIRTIO_MMIO_DEVICE *VirtIo;\r
+\r
+ if (!BaseAddress) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ if (Handle == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Allocate VIRTIO_MMIO_DEVICE\r
+ //\r
+ VirtIo = AllocateZeroPool (sizeof (VIRTIO_MMIO_DEVICE));\r
+ if (VirtIo == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ VirtIo->Signature = VIRTIO_MMIO_DEVICE_SIGNATURE;\r
+\r
+ Status = VirtioMmioInit (BaseAddress, VirtIo);\r
+ if (EFI_ERROR (Status)) {\r
+ goto FreeVirtioMem;\r
+ }\r
+\r
+ //\r
+ // Install VIRTIO_DEVICE_PROTOCOL to Handle\r
+ //\r
+ Status = gBS->InstallProtocolInterface (&Handle,\r
+ &gVirtioDeviceProtocolGuid, EFI_NATIVE_INTERFACE,\r
+ &VirtIo->VirtioDevice);\r
+ if (EFI_ERROR (Status)) {\r
+ goto UninitVirtio;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+\r
+UninitVirtio:\r
+ VirtioMmioUninit (VirtIo);\r
+\r
+FreeVirtioMem:\r
+ FreePool (VirtIo);\r
+ return Status;\r
+}\r
+\r
+EFI_STATUS\r
+VirtioMmioUninstallDevice (\r
+ IN EFI_HANDLE DeviceHandle\r
+ )\r
+{\r
+ VIRTIO_DEVICE_PROTOCOL *VirtioDevice;\r
+ VIRTIO_MMIO_DEVICE *MmioDevice;\r
+ EFI_STATUS Status;\r
+\r
+ Status = gBS->OpenProtocol (\r
+ DeviceHandle, // candidate device\r
+ &gVirtioDeviceProtocolGuid, // retrieve the VirtIo iface\r
+ (VOID **)&VirtioDevice, // target pointer\r
+ DeviceHandle, // requestor driver identity\r
+ DeviceHandle, // requesting lookup for dev.\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL // lookup only, no ref. added\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Get the MMIO device from the VirtIo Device instance\r
+ //\r
+ MmioDevice = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (VirtioDevice);\r
+\r
+ //\r
+ // Uninstall the protocol interface\r
+ //\r
+ Status = gBS->UninstallProtocolInterface (DeviceHandle,\r
+ &gVirtioDeviceProtocolGuid, &MmioDevice->VirtioDevice\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Uninitialize the VirtIo Device\r
+ //\r
+ VirtioMmioUninit (MmioDevice);\r
+ FreePool (MmioDevice);\r
+\r
+ return EFI_SUCCESS;\r
+}\r