synchronous requests and EFI_BLOCK_IO_PROTOCOL for now.\r
\r
Copyright (C) 2012, Red Hat, Inc.\r
+ Copyright (c) 2012, Intel Corporation. All rights reserved.<BR>\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
**/\r
\r
#include <IndustryStandard/Pci.h>\r
+#include <IndustryStandard/VirtioBlk.h>\r
#include <Library/BaseMemoryLib.h>\r
#include <Library/DebugLib.h>\r
#include <Library/MemoryAllocationLib.h>\r
#include <Library/UefiBootServicesTableLib.h>\r
#include <Library/UefiLib.h>\r
+#include <Library/VirtioLib.h>\r
\r
#include "VirtioBlk.h"\r
\r
-/**\r
-\r
- Write a word into Region 0 of the device specified by PciIo.\r
-\r
- Region 0 must be an iomem region. This is an internal function for the\r
- VIRTIO_CFG_WRITE() macro below.\r
-\r
- @param[in] PciIo Target PCI device.\r
-\r
- @param[in] FieldOffset Destination offset.\r
-\r
- @param[in] FieldSize Destination field size, must be in { 1, 2, 4, 8 }.\r
-\r
- @param[in] Value Little endian value to write, converted to UINT64.\r
- The least significant FieldSize bytes will be used.\r
-\r
-\r
- @return Status code returned by PciIo->Io.Write().\r
-\r
-**/\r
-STATIC\r
-EFIAPI\r
-EFI_STATUS\r
-VirtioWrite (\r
- IN EFI_PCI_IO_PROTOCOL *PciIo,\r
- IN UINTN FieldOffset,\r
- IN UINTN FieldSize,\r
- IN UINT64 Value\r
- )\r
-{\r
- UINTN Count;\r
- EFI_PCI_IO_PROTOCOL_WIDTH Width;\r
-\r
- Count = 1;\r
- switch (FieldSize) {\r
- case 1:\r
- Width = EfiPciIoWidthUint8;\r
- break;\r
-\r
- case 2:\r
- Width = EfiPciIoWidthUint16;\r
- break;\r
-\r
- case 8:\r
- Count = 2;\r
- // fall through\r
-\r
- case 4:\r
- Width = EfiPciIoWidthUint32;\r
- break;\r
-\r
- default:\r
- ASSERT (FALSE);\r
- }\r
-\r
- return PciIo->Io.Write (\r
- PciIo,\r
- Width,\r
- PCI_BAR_IDX0,\r
- FieldOffset,\r
- Count,\r
- &Value\r
- );\r
-}\r
-\r
-\r
-/**\r
-\r
- Read a word from Region 0 of the device specified by PciIo.\r
-\r
- Region 0 must be an iomem region. This is an internal function for the\r
- VIRTIO_CFG_READ() macro below.\r
-\r
- @param[in] PciIo Source PCI device.\r
-\r
- @param[in] FieldOffset Source offset.\r
-\r
- @param[in] FieldSize Source field size, must be in { 1, 2, 4, 8 }.\r
-\r
- @param[in] BufferSize Number of bytes available in the target buffer. Must\r
- equal FieldSize.\r
-\r
- @param[out] Buffer Target buffer.\r
-\r
-\r
- @return Status code returned by PciIo->Io.Read().\r
-\r
-**/\r
-STATIC\r
-EFIAPI\r
-EFI_STATUS\r
-VirtioRead (\r
- IN EFI_PCI_IO_PROTOCOL *PciIo,\r
- IN UINTN FieldOffset,\r
- IN UINTN FieldSize,\r
- IN UINTN BufferSize,\r
- OUT VOID *Buffer\r
- )\r
-{\r
- UINTN Count;\r
- EFI_PCI_IO_PROTOCOL_WIDTH Width;\r
-\r
- ASSERT (FieldSize == BufferSize);\r
-\r
- Count = 1;\r
- switch (FieldSize) {\r
- case 1:\r
- Width = EfiPciIoWidthUint8;\r
- break;\r
-\r
- case 2:\r
- Width = EfiPciIoWidthUint16;\r
- break;\r
-\r
- case 8:\r
- Count = 2;\r
- // fall through\r
-\r
- case 4:\r
- Width = EfiPciIoWidthUint32;\r
- break;\r
-\r
- default:\r
- ASSERT (FALSE);\r
- }\r
-\r
- return PciIo->Io.Read (\r
- PciIo,\r
- Width,\r
- PCI_BAR_IDX0,\r
- FieldOffset,\r
- Count,\r
- Buffer\r
- );\r
-}\r
-\r
-\r
/**\r
\r
Convenience macros to read and write region 0 IO space elements of the\r
}\r
\r
\r
-/**\r
-\r
- Append a contiguous buffer for transmission / reception via the virtio ring.\r
-\r
- This function implements the following sections from virtio-0.9.5:\r
- - 2.4.1.1 Placing Buffers into the Descriptor Table\r
- - 2.4.1.2 Updating the Available Ring\r
-\r
- Free space is taken as granted, since this driver supports only synchronous\r
- requests and host side status is processed in lock-step with request\r
- submission. VirtioBlkInit() verifies the ring size in advance.\r
-\r
- @param[in out] Ring The virtio ring to append the buffer to, as a\r
- descriptor.\r
-\r
- @param [in] BufferPhysAddr (Guest pseudo-physical) start address of the\r
- transmit / receive buffer\r
-\r
- @param [in] BufferSize Number of bytes to transmit or receive.\r
-\r
- @param [in] Flags A bitmask of VRING_DESC_F_* flags. The caller\r
- computes this mask dependent on further buffers\r
- to append and transfer direction.\r
- VRING_DESC_F_INDIRECT is unsupported. The\r
- VRING_DESC.Next field is always set, but the\r
- host only interprets it dependent on\r
- VRING_DESC_F_NEXT.\r
-\r
- @param [in] HeadIdx The index identifying the head buffer (first\r
- buffer appended) belonging to this same\r
- request.\r
-\r
- @param [in out] NextAvailIdx On input, the index identifying the next\r
- descriptor available to carry the buffer. On\r
- output, incremented by one, modulo 2^16.\r
-\r
-**/\r
-\r
-STATIC\r
-VOID\r
-EFIAPI\r
-AppendDesc (\r
- IN OUT VRING *Ring,\r
- IN UINTN BufferPhysAddr,\r
- IN UINT32 BufferSize,\r
- IN UINT16 Flags,\r
- IN UINT16 HeadIdx,\r
- IN OUT UINT16 *NextAvailIdx\r
- )\r
-{\r
- volatile VRING_DESC *Desc;\r
-\r
- Desc = &Ring->Desc[*NextAvailIdx % Ring->QueueSize];\r
- Desc->Addr = BufferPhysAddr;\r
- Desc->Len = BufferSize;\r
- Desc->Flags = Flags;\r
- Ring->Avail.Ring[(*NextAvailIdx)++ % Ring->QueueSize] =\r
- HeadIdx % Ring->QueueSize;\r
- Desc->Next = *NextAvailIdx % Ring->QueueSize;\r
-}\r
\r
\r
/**\r
UINT32 BlockSize;\r
volatile VIRTIO_BLK_REQ Request;\r
volatile UINT8 HostStatus;\r
- UINT16 FirstAvailIdx;\r
- UINT16 NextAvailIdx;\r
- UINTN PollPeriodUsecs;\r
+ DESC_INDICES Indices;\r
\r
BlockSize = Dev->BlockIoMedia.BlockSize;\r
\r
(BufferSize == 0 ? VIRTIO_BLK_T_FLUSH : VIRTIO_BLK_T_OUT) :\r
VIRTIO_BLK_T_IN;\r
Request.IoPrio = 0;\r
- Request.Sector = Lba * (BlockSize / 512);\r
+ Request.Sector = MultU64x32(Lba, BlockSize / 512);\r
\r
- //\r
- // Prepare for virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device.\r
- // We're going to poll the answer, the host should not send an interrupt.\r
- //\r
- *Dev->Ring.Avail.Flags = (UINT16) VRING_AVAIL_F_NO_INTERRUPT;\r
+ VirtioPrepare (&Dev->Ring, &Indices);\r
\r
//\r
// preset a host status for ourselves that we do not accept as success\r
//\r
ASSERT (Dev->Ring.QueueSize >= 3);\r
\r
- //\r
- // Implement virtio-0.9.5, 2.4.1 Supplying Buffers to the Device.\r
- //\r
- FirstAvailIdx = *Dev->Ring.Avail.Idx;\r
- NextAvailIdx = FirstAvailIdx;\r
-\r
//\r
// virtio-blk header in first desc\r
//\r
- AppendDesc (&Dev->Ring, (UINTN) &Request, sizeof Request, VRING_DESC_F_NEXT,\r
- FirstAvailIdx, &NextAvailIdx);\r
+ VirtioAppendDesc (&Dev->Ring, (UINTN) &Request, sizeof Request,\r
+ VRING_DESC_F_NEXT, &Indices);\r
\r
//\r
// data buffer for read/write in second desc\r
//\r
// VRING_DESC_F_WRITE is interpreted from the host's point of view.\r
//\r
- AppendDesc (&Dev->Ring, (UINTN) Buffer, (UINT32) BufferSize,\r
+ VirtioAppendDesc (&Dev->Ring, (UINTN) Buffer, (UINT32) BufferSize,\r
VRING_DESC_F_NEXT | (RequestIsWrite ? 0 : VRING_DESC_F_WRITE),\r
- FirstAvailIdx, &NextAvailIdx);\r
+ &Indices);\r
}\r
\r
//\r
// host status in last (second or third) desc\r
//\r
- AppendDesc (&Dev->Ring, (UINTN) &HostStatus, sizeof HostStatus,\r
- VRING_DESC_F_WRITE, FirstAvailIdx, &NextAvailIdx);\r
+ VirtioAppendDesc (&Dev->Ring, (UINTN) &HostStatus, sizeof HostStatus,\r
+ VRING_DESC_F_WRITE, &Indices);\r
\r
//\r
- // virtio-0.9.5, 2.4.1.3 Updating the Index Field\r
+ // virtio-blk's only virtqueue is #0, called "requestq" (see Appendix D).\r
//\r
- MemoryFence();\r
- *Dev->Ring.Avail.Idx = NextAvailIdx;\r
-\r
- //\r
- // virtio-0.9.5, 2.4.1.4 Notifying the Device -- gratuitous notifications are\r
- // OK. virtio-blk's only virtqueue is #0, called "requestq" (see Appendix D).\r
- //\r
- MemoryFence();\r
- if (EFI_ERROR (VIRTIO_CFG_WRITE (Dev, Generic.VhdrQueueNotify, 0))) {\r
- return EFI_DEVICE_ERROR;\r
- }\r
-\r
- //\r
- // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device\r
- // Wait until the host processes and acknowledges our 3-part descriptor\r
- // chain. The condition we use for polling is greatly simplified and relies\r
- // on synchronous, the lock-step progress.\r
- //\r
- // Keep slowing down until we reach a poll period of slightly above 1 ms.\r
- //\r
- PollPeriodUsecs = 1;\r
- MemoryFence();\r
- while (*Dev->Ring.Used.Idx != NextAvailIdx) {\r
- gBS->Stall (PollPeriodUsecs); // calls AcpiTimerLib::MicroSecondDelay\r
-\r
- if (PollPeriodUsecs < 1024) {\r
- PollPeriodUsecs *= 2;\r
- }\r
- MemoryFence();\r
- }\r
-\r
- if (HostStatus == VIRTIO_BLK_S_OK) {\r
+ if (VirtioFlush (Dev->PciIo, 0, &Dev->Ring, &Indices) == EFI_SUCCESS &&\r
+ HostStatus == VIRTIO_BLK_S_OK) {\r
return EFI_SUCCESS;\r
}\r
\r
Status = (Pci.Hdr.VendorId == 0x1AF4 &&\r
Pci.Hdr.DeviceId >= 0x1000 && Pci.Hdr.DeviceId <= 0x103F &&\r
Pci.Hdr.RevisionID == 0x00 &&\r
- Pci.Device.SubsystemID == 0x02) ? EFI_SUCCESS : EFI_UNSUPPORTED;\r
+ Pci.Device.SubsystemID == VIRTIO_SUBSYSTEM_BLOCK_DEVICE) ? EFI_SUCCESS : EFI_UNSUPPORTED;\r
}\r
\r
//\r
}\r
\r
\r
-/**\r
-\r
- Configure a virtio ring.\r
-\r
- This function sets up internal storage (the guest-host communication area)\r
- and lays out several "navigation" (ie. no-ownership) pointers to parts of\r
- that storage.\r
-\r
- Relevant sections from the virtio-0.9.5 spec:\r
- - 1.1 Virtqueues,\r
- - 2.3 Virtqueue Configuration.\r
-\r
- @param[in] The number of descriptors to allocate for the\r
- virtio ring, as requested by the host.\r
-\r
- @param[out] Ring The virtio ring to set up.\r
-\r
- @retval EFI_OUT_OF_RESOURCES AllocatePages() failed to allocate contiguous\r
- pages for the requested QueueSize. Fields of\r
- Ring have indeterminate value.\r
-\r
- @retval EFI_SUCCESS Allocation and setup successful. Ring->Base\r
- (and nothing else) is responsible for\r
- deallocation.\r
-\r
-**/\r
-\r
-STATIC\r
-EFI_STATUS\r
-EFIAPI\r
-VirtioRingInit (\r
- IN UINT16 QueueSize,\r
- OUT VRING *Ring\r
- )\r
-{\r
- UINTN RingSize;\r
- volatile UINT8 *RingPagesPtr;\r
-\r
- RingSize = ALIGN_VALUE (\r
- sizeof *Ring->Desc * QueueSize +\r
- sizeof *Ring->Avail.Flags +\r
- sizeof *Ring->Avail.Idx +\r
- sizeof *Ring->Avail.Ring * QueueSize +\r
- sizeof *Ring->Avail.UsedEvent,\r
- EFI_PAGE_SIZE);\r
-\r
- RingSize += ALIGN_VALUE (\r
- sizeof *Ring->Used.Flags +\r
- sizeof *Ring->Used.Idx +\r
- sizeof *Ring->Used.UsedElem * QueueSize +\r
- sizeof *Ring->Used.AvailEvent,\r
- EFI_PAGE_SIZE);\r
-\r
- Ring->NumPages = EFI_SIZE_TO_PAGES (RingSize);\r
- Ring->Base = AllocatePages (Ring->NumPages);\r
- if (Ring->Base == NULL) {\r
- return EFI_OUT_OF_RESOURCES;\r
- }\r
- SetMem (Ring->Base, RingSize, 0x00);\r
- RingPagesPtr = Ring->Base;\r
-\r
- Ring->Desc = (volatile VOID *) RingPagesPtr;\r
- RingPagesPtr += sizeof *Ring->Desc * QueueSize;\r
-\r
- Ring->Avail.Flags = (volatile VOID *) RingPagesPtr;\r
- RingPagesPtr += sizeof *Ring->Avail.Flags;\r
-\r
- Ring->Avail.Idx = (volatile VOID *) RingPagesPtr;\r
- RingPagesPtr += sizeof *Ring->Avail.Idx;\r
-\r
- Ring->Avail.Ring = (volatile VOID *) RingPagesPtr;\r
- RingPagesPtr += sizeof *Ring->Avail.Ring * QueueSize;\r
-\r
- Ring->Avail.UsedEvent = (volatile VOID *) RingPagesPtr;\r
- RingPagesPtr += sizeof *Ring->Avail.UsedEvent;\r
-\r
- RingPagesPtr = (volatile UINT8 *) Ring->Base +\r
- ALIGN_VALUE (RingPagesPtr - (volatile UINT8 *) Ring->Base,\r
- EFI_PAGE_SIZE);\r
-\r
- Ring->Used.Flags = (volatile VOID *) RingPagesPtr;\r
- RingPagesPtr += sizeof *Ring->Used.Flags;\r
-\r
- Ring->Used.Idx = (volatile VOID *) RingPagesPtr;\r
- RingPagesPtr += sizeof *Ring->Used.Idx;\r
-\r
- Ring->Used.UsedElem = (volatile VOID *) RingPagesPtr;\r
- RingPagesPtr += sizeof *Ring->Used.UsedElem * QueueSize;\r
-\r
- Ring->Used.AvailEvent = (volatile VOID *) RingPagesPtr;\r
- RingPagesPtr += sizeof *Ring->Used.AvailEvent;\r
-\r
- Ring->QueueSize = QueueSize;\r
- return EFI_SUCCESS;\r
-}\r
-\r
-\r
-/**\r
-\r
- Tear down the internal resources of a configured virtio ring.\r
-\r
- The caller is responsible to stop the host from using this ring before\r
- invoking this function: the VSTAT_DRIVER_OK bit must be clear in\r
- VhdrDeviceStatus.\r
-\r
- @param[out] Ring The virtio ring to clean up.\r
-\r
-**/\r
-\r
-\r
-STATIC\r
-VOID\r
-EFIAPI\r
-VirtioRingUninit (\r
- IN OUT VRING *Ring\r
- )\r
-{\r
- FreePages (Ring->Base, Ring->NumPages);\r
- SetMem (Ring, sizeof *Ring, 0x00);\r
-}\r
-\r
-\r
/**\r
\r
Set up all BlockIo and virtio-blk aspects of this driver for the specified\r
goto Failed;\r
}\r
if (BlockSize == 0 || BlockSize % 512 != 0 ||\r
- NumSectors % (BlockSize / 512) != 0) {\r
+ ModU64x32 (NumSectors, BlockSize / 512) != 0) {\r
//\r
// We can only handle a logical block consisting of whole sectors,\r
// and only a disk composed of whole logical blocks.\r
Dev->BlockIoMedia.WriteCaching = !!(Features & VIRTIO_BLK_F_FLUSH);\r
Dev->BlockIoMedia.BlockSize = BlockSize;\r
Dev->BlockIoMedia.IoAlign = 0;\r
- Dev->BlockIoMedia.LastBlock = NumSectors / (BlockSize / 512) - 1;\r
+ Dev->BlockIoMedia.LastBlock = DivU64x32 (NumSectors,\r
+ BlockSize / 512) - 1;\r
return EFI_SUCCESS;\r
\r
ReleaseQueue:\r
IN EFI_HANDLE *ChildHandleBuffer\r
)\r
{\r
- VBLK_DEV *Dev;\r
- EFI_STATUS Status;\r
+ EFI_STATUS Status;\r
+ EFI_BLOCK_IO_PROTOCOL *BlockIo;\r
+ VBLK_DEV *Dev;\r
\r
- Dev = VIRTIO_BLK_FROM_BLOCK_IO (This);\r
+ Status = gBS->OpenProtocol (\r
+ DeviceHandle, // candidate device\r
+ &gEfiBlockIoProtocolGuid, // retrieve the BlockIo iface\r
+ (VOID **)&BlockIo, // target pointer\r
+ This->DriverBindingHandle, // 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
+ Dev = VIRTIO_BLK_FROM_BLOCK_IO (BlockIo);\r
\r
//\r
- // If DriverBindingStop() is called with the driver instance still in use,\r
- // or any of the parameters are invalid, we've caught a bug.\r
+ // Handle Stop() requests for in-use driver instances gracefully.\r
//\r
Status = gBS->UninstallProtocolInterface (DeviceHandle,\r
&gEfiBlockIoProtocolGuid, &Dev->BlockIo);\r
- ASSERT (Status == EFI_SUCCESS);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
\r
VirtioBlkUninit (Dev);\r
\r
// for unambiguous identification.\r
//\r
\r
-STATIC GLOBAL_REMOVE_IF_UNREFERENCED\r
+STATIC\r
EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {\r
{ "eng;en", L"Virtio Block Driver" },\r
{ NULL, NULL }\r
};\r
\r
-STATIC GLOBAL_REMOVE_IF_UNREFERENCED\r
+STATIC\r
EFI_COMPONENT_NAME_PROTOCOL gComponentName;\r
\r
EFI_STATUS\r
return EFI_UNSUPPORTED;\r
}\r
\r
-STATIC GLOBAL_REMOVE_IF_UNREFERENCED\r
+STATIC\r
EFI_COMPONENT_NAME_PROTOCOL gComponentName = {\r
&VirtioBlkGetDriverName,\r
&VirtioBlkGetDeviceName,\r
"eng" // SupportedLanguages, ISO 639-2 language codes\r
};\r
\r
-STATIC GLOBAL_REMOVE_IF_UNREFERENCED\r
+STATIC\r
EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = {\r
(EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &VirtioBlkGetDriverName,\r
(EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &VirtioBlkGetDeviceName,\r