--- /dev/null
+/** @file\r
+\r
+ This driver produces Block I/O Protocol instances for virtio-blk devices.\r
+\r
+ The implementation is basic:\r
+\r
+ - No attach/detach (ie. removable media).\r
+\r
+ - Although the non-blocking interfaces of EFI_BLOCK_IO2_PROTOCOL could be a\r
+ good match for multiple in-flight virtio-blk requests, we stick to\r
+ synchronous requests and EFI_BLOCK_IO_PROTOCOL for now.\r
+\r
+ Copyright (C) 2012, Red Hat, Inc.\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 <IndustryStandard/Pci.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
+\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
+ virtio-blk PCI device, for configuration purposes.\r
+\r
+ The following macros make it possible to specify only the "core parameters"\r
+ for such accesses and to derive the rest. By the time VIRTIO_CFG_WRITE()\r
+ returns, the transaction will have been completed.\r
+\r
+ @param[in] Dev Pointer to the VBLK_DEV structure whose PCI IO space\r
+ we're accessing. Dev->PciIo must be valid.\r
+\r
+ @param[in] Field A field name from VBLK_HDR, identifying the virtio-blk\r
+ configuration item to access.\r
+\r
+ @param[in] Value (VIRTIO_CFG_WRITE() only.) The value to write to the\r
+ selected configuration item.\r
+\r
+ @param[out] Pointer (VIRTIO_CFG_READ() only.) The object to receive the\r
+ value read from the configuration item. Its type must be\r
+ one of UINT8, UINT16, UINT32, UINT64.\r
+\r
+\r
+ @return Status code returned by VirtioWrite() / VirtioRead().\r
+\r
+**/\r
+\r
+#define VIRTIO_CFG_WRITE(Dev, Field, Value) (VirtioWrite ( \\r
+ (Dev)->PciIo, \\r
+ OFFSET_OF_VHDR (Field), \\r
+ SIZE_OF_VHDR (Field), \\r
+ (Value) \\r
+ ))\r
+\r
+#define VIRTIO_CFG_READ(Dev, Field, Pointer) (VirtioRead ( \\r
+ (Dev)->PciIo, \\r
+ OFFSET_OF_VHDR (Field), \\r
+ SIZE_OF_VHDR (Field), \\r
+ sizeof *(Pointer), \\r
+ (Pointer) \\r
+ ))\r
+\r
+\r
+//\r
+// UEFI Spec 2.3.1 + Errata C, 12.8 EFI Block I/O Protocol\r
+// Driver Writer's Guide for UEFI 2.3.1 v1.01,\r
+// 24.2 Block I/O Protocol Implementations\r
+//\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkReset (\r
+ IN EFI_BLOCK_IO_PROTOCOL *This,\r
+ IN BOOLEAN ExtendedVerification\r
+ )\r
+{\r
+ //\r
+ // If we managed to initialize and install the driver, then the device is\r
+ // working correctly.\r
+ //\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+\r
+ Verify correctness of the read/write (not flush) request submitted to the\r
+ EFI_BLOCK_IO_PROTOCOL instance.\r
+\r
+ This function provides most verification steps described in:\r
+\r
+ UEFI Spec 2.3.1 + Errata C, 12.8 EFI Block I/O Protocol, 12.8 EFI Block I/O\r
+ Protocol,\r
+ - EFI_BLOCK_IO_PROTOCOL.ReadBlocks()\r
+ - EFI_BLOCK_IO_PROTOCOL.WriteBlocks()\r
+\r
+ Driver Writer's Guide for UEFI 2.3.1 v1.01,\r
+ - 24.2.2. ReadBlocks() and ReadBlocksEx() Implementation\r
+ - 24.2.3 WriteBlocks() and WriteBlockEx() Implementation\r
+\r
+ Request sizes are limited to 1 GB (checked). This is not a practical\r
+ limitation, just conformance to virtio-0.9.5, 2.3.2 Descriptor Table: "no\r
+ descriptor chain may be more than 2^32 bytes long in total".\r
+\r
+ Some Media characteristics are hardcoded in VirtioBlkInit() below (like\r
+ non-removable media, no restriction on buffer alignment etc); we rely on\r
+ those here without explicit mention.\r
+\r
+ @param[in] Media The EFI_BLOCK_IO_MEDIA characteristics for\r
+ this driver instance, extracted from the\r
+ underlying virtio-blk device at initialization\r
+ time. We validate the request against this set\r
+ of attributes.\r
+\r
+\r
+ @param[in] Lba Logical Block Address: number of logical\r
+ blocks to skip from the beginning of the\r
+ device.\r
+\r
+ @param[in] PositiveBufferSize Size of buffer to transfer, in bytes. The\r
+ caller is responsible to ensure this parameter\r
+ is positive.\r
+\r
+ @param[in] RequestIsWrite TRUE iff data transfer goes from guest to\r
+ device.\r
+\r
+\r
+ @@return Validation result to be forwarded outwards by\r
+ ReadBlocks() and WriteBlocks, as required by\r
+ the specs above.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+VerifyReadWriteRequest (\r
+ IN EFI_BLOCK_IO_MEDIA *Media,\r
+ IN EFI_LBA Lba,\r
+ IN UINTN PositiveBufferSize,\r
+ IN BOOLEAN RequestIsWrite\r
+ )\r
+{\r
+ UINTN BlockCount;\r
+\r
+ ASSERT (PositiveBufferSize > 0);\r
+\r
+ if (PositiveBufferSize > SIZE_1GB ||\r
+ PositiveBufferSize % Media->BlockSize > 0) {\r
+ return EFI_BAD_BUFFER_SIZE;\r
+ }\r
+ BlockCount = PositiveBufferSize / Media->BlockSize;\r
+\r
+ //\r
+ // Avoid unsigned wraparound on either side in the second comparison.\r
+ //\r
+ if (Lba > Media->LastBlock || BlockCount - 1 > Media->LastBlock - Lba) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (RequestIsWrite && Media->ReadOnly) {\r
+ return EFI_WRITE_PROTECTED;\r
+ }\r
+\r
+ return EFI_SUCCESS;\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
+\r
+ Format a read / write / flush request as three consecutive virtio\r
+ descriptors, push them to the host, and poll for the response.\r
+\r
+ This is the main workhorse function. Two use cases are supported, read/write\r
+ and flush. The function may only be called after the request parameters have\r
+ been verified by\r
+ - specific checks in ReadBlocks() / WriteBlocks() / FlushBlocks(), and\r
+ - VerifyReadWriteRequest() (for read/write only).\r
+\r
+ Parameters handled commonly:\r
+\r
+ @param[in] Dev The virtio-blk device the request is targeted\r
+ at.\r
+\r
+ Flush request:\r
+\r
+ @param[in] Lba Must be zero.\r
+\r
+ @param[in] BufferSize Must be zero.\r
+\r
+ @param[in out] Buffer Ignored by the function.\r
+\r
+ @param[in] RequestIsWrite Must be TRUE.\r
+\r
+ Read/Write request:\r
+\r
+ @param[in] Lba Logical Block Address: number of logical blocks\r
+ to skip from the beginning of the device.\r
+\r
+ @param[in] BufferSize Size of buffer to transfer, in bytes. The caller\r
+ is responsible to ensure this parameter is\r
+ positive.\r
+\r
+ @param[in out] Buffer The guest side area to read data from the device\r
+ into, or write data to the device from.\r
+\r
+ @param[in] RequestIsWrite TRUE iff data transfer goes from guest to\r
+ device.\r
+\r
+ Return values are common to both use cases, and are appropriate to be\r
+ forwarded by the EFI_BLOCK_IO_PROTOCOL functions (ReadBlocks(),\r
+ WriteBlocks(), FlushBlocks()).\r
+\r
+\r
+ @retval EFI_SUCCESS Transfer complete.\r
+\r
+ @retval EFI_DEVICE_ERROR Failed to notify host side via PCI write, or\r
+ unable to parse host response, or host response\r
+ is not VIRTIO_BLK_S_OK.\r
+\r
+**/\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+SynchronousRequest (\r
+ IN VBLK_DEV *Dev,\r
+ IN EFI_LBA Lba,\r
+ IN UINTN BufferSize,\r
+ IN OUT volatile VOID *Buffer,\r
+ IN BOOLEAN RequestIsWrite\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
+\r
+ BlockSize = Dev->BlockIoMedia.BlockSize;\r
+\r
+ //\r
+ // ensured by VirtioBlkInit()\r
+ //\r
+ ASSERT (BlockSize > 0);\r
+ ASSERT (BlockSize % 512 == 0);\r
+\r
+ //\r
+ // ensured by contract above, plus VerifyReadWriteRequest()\r
+ //\r
+ ASSERT (BufferSize % BlockSize == 0);\r
+\r
+ //\r
+ // Prepare virtio-blk request header, setting zero size for flush.\r
+ // IO Priority is homogeneously 0.\r
+ //\r
+ Request.Type = RequestIsWrite ?\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
+\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
+\r
+ //\r
+ // preset a host status for ourselves that we do not accept as success\r
+ //\r
+ HostStatus = VIRTIO_BLK_S_IOERR;\r
+\r
+ //\r
+ // ensured by VirtioBlkInit() -- this predicate, in combination with the\r
+ // lock-step progress, ensures we don't have to track free descriptors.\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
+\r
+ //\r
+ // data buffer for read/write in second desc\r
+ //\r
+ if (BufferSize > 0) {\r
+ //\r
+ // From virtio-0.9.5, 2.3.2 Descriptor Table:\r
+ // "no descriptor chain may be more than 2^32 bytes long in total".\r
+ //\r
+ // The predicate is ensured by the call contract above (for flush), or\r
+ // VerifyReadWriteRequest() (for read/write). It also implies that\r
+ // converting BufferSize to UINT32 will not truncate it.\r
+ //\r
+ ASSERT (BufferSize <= SIZE_1GB);\r
+\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
+ VRING_DESC_F_NEXT | (RequestIsWrite ? 0 : VRING_DESC_F_WRITE),\r
+ FirstAvailIdx, &NextAvailIdx);\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
+\r
+ //\r
+ // virtio-0.9.5, 2.4.1.3 Updating the Index Field\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, 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
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ return EFI_DEVICE_ERROR;\r
+}\r
+\r
+\r
+/**\r
+\r
+ ReadBlocks() operation for virtio-blk.\r
+\r
+ See\r
+ - UEFI Spec 2.3.1 + Errata C, 12.8 EFI Block I/O Protocol, 12.8 EFI Block I/O\r
+ Protocol, EFI_BLOCK_IO_PROTOCOL.ReadBlocks().\r
+ - Driver Writer's Guide for UEFI 2.3.1 v1.01, 24.2.2. ReadBlocks() and\r
+ ReadBlocksEx() Implementation.\r
+\r
+ Parameter checks and conformant return values are implemented in\r
+ VerifyReadWriteRequest() and SynchronousRequest().\r
+\r
+ A zero BufferSize doesn't seem to be prohibited, so do nothing in that case,\r
+ successfully.\r
+\r
+**/\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkReadBlocks (\r
+ IN EFI_BLOCK_IO_PROTOCOL *This,\r
+ IN UINT32 MediaId,\r
+ IN EFI_LBA Lba,\r
+ IN UINTN BufferSize,\r
+ OUT VOID *Buffer\r
+ )\r
+{\r
+ VBLK_DEV *Dev;\r
+ EFI_STATUS Status;\r
+\r
+ if (BufferSize == 0) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ Dev = VIRTIO_BLK_FROM_BLOCK_IO (This);\r
+ Status = VerifyReadWriteRequest (\r
+ &Dev->BlockIoMedia,\r
+ Lba,\r
+ BufferSize,\r
+ FALSE // RequestIsWrite\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ return SynchronousRequest (\r
+ Dev,\r
+ Lba,\r
+ BufferSize,\r
+ Buffer,\r
+ FALSE // RequestIsWrite\r
+ );\r
+}\r
+\r
+/**\r
+\r
+ WriteBlocks() operation for virtio-blk.\r
+\r
+ See\r
+ - UEFI Spec 2.3.1 + Errata C, 12.8 EFI Block I/O Protocol, 12.8 EFI Block I/O\r
+ Protocol, EFI_BLOCK_IO_PROTOCOL.WriteBlocks().\r
+ - Driver Writer's Guide for UEFI 2.3.1 v1.01, 24.2.3 WriteBlocks() and\r
+ WriteBlockEx() Implementation.\r
+\r
+ Parameter checks and conformant return values are implemented in\r
+ VerifyReadWriteRequest() and SynchronousRequest().\r
+\r
+ A zero BufferSize doesn't seem to be prohibited, so do nothing in that case,\r
+ successfully.\r
+\r
+**/\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkWriteBlocks (\r
+ IN EFI_BLOCK_IO_PROTOCOL *This,\r
+ IN UINT32 MediaId,\r
+ IN EFI_LBA Lba,\r
+ IN UINTN BufferSize,\r
+ IN VOID *Buffer\r
+ )\r
+{\r
+ VBLK_DEV *Dev;\r
+ EFI_STATUS Status;\r
+\r
+ if (BufferSize == 0) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ Dev = VIRTIO_BLK_FROM_BLOCK_IO (This);\r
+ Status = VerifyReadWriteRequest (\r
+ &Dev->BlockIoMedia,\r
+ Lba,\r
+ BufferSize,\r
+ TRUE // RequestIsWrite\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ return SynchronousRequest (\r
+ Dev,\r
+ Lba,\r
+ BufferSize,\r
+ Buffer,\r
+ TRUE // RequestIsWrite\r
+ );\r
+}\r
+\r
+\r
+/**\r
+\r
+ FlushBlocks() operation for virtio-blk.\r
+\r
+ See\r
+ - UEFI Spec 2.3.1 + Errata C, 12.8 EFI Block I/O Protocol, 12.8 EFI Block I/O\r
+ Protocol, EFI_BLOCK_IO_PROTOCOL.FlushBlocks().\r
+ - Driver Writer's Guide for UEFI 2.3.1 v1.01, 24.2.4 FlushBlocks() and\r
+ FlushBlocksEx() Implementation.\r
+\r
+ If the underlying virtio-blk device doesn't support flushing (ie.\r
+ write-caching), then this function should not be called by higher layers,\r
+ according to EFI_BLOCK_IO_MEDIA characteristics set in VirtioBlkInit().\r
+ Should they do nonetheless, we do nothing, successfully.\r
+\r
+**/\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkFlushBlocks (\r
+ IN EFI_BLOCK_IO_PROTOCOL *This\r
+ )\r
+{\r
+ VBLK_DEV *Dev;\r
+\r
+ Dev = VIRTIO_BLK_FROM_BLOCK_IO (This);\r
+ return Dev->BlockIoMedia.WriteCaching ?\r
+ SynchronousRequest (\r
+ Dev,\r
+ 0, // Lba\r
+ 0, // BufferSize\r
+ NULL, // Buffer\r
+ TRUE // RequestIsWrite\r
+ ) :\r
+ EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+\r
+ Device probe function for this driver.\r
+\r
+ The DXE core calls this function for any given device in order to see if the\r
+ driver can drive the device.\r
+\r
+ Specs relevant in the general sense:\r
+\r
+ - UEFI Spec 2.3.1 + Errata C:\r
+ - 6.3 Protocol Handler Services -- for accessing the underlying device\r
+ - 10.1 EFI Driver Binding Protocol -- for exporting ourselves\r
+\r
+ - Driver Writer's Guide for UEFI 2.3.1 v1.01:\r
+ - 5.1.3.4 OpenProtocol() and CloseProtocol() -- for accessing the\r
+ underlying device\r
+ - 9 Driver Binding Protocol -- for exporting ourselves\r
+\r
+ Specs relevant in the specific sense:\r
+ - UEFI Spec 2.3.1 + Errata C, 13.4 EFI PCI I/O Protocol\r
+ - Driver Writer's Guide for UEFI 2.3.1 v1.01, 18 PCI Driver Design\r
+ Guidelines, 18.3 PCI drivers.\r
+\r
+ @param[in] This The EFI_DRIVER_BINDING_PROTOCOL object\r
+ incorporating this driver (independently of\r
+ any device).\r
+\r
+ @param[in] DeviceHandle The device to probe.\r
+\r
+ @param[in] RemainingDevicePath Relevant only for bus drivers, ignored.\r
+\r
+\r
+ @retval EFI_SUCCESS The driver supports the device being probed.\r
+\r
+ @retval EFI_UNSUPPORTED Based on virtio-blk PCI discovery, we do not support\r
+ the device.\r
+\r
+ @return Error codes from the OpenProtocol() boot service or\r
+ the PciIo protocol.\r
+\r
+**/\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkDriverBindingSupported (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE DeviceHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_PCI_IO_PROTOCOL *PciIo;\r
+ PCI_TYPE00 Pci;\r
+\r
+ //\r
+ // Attempt to open the device with the PciIo set of interfaces. On success,\r
+ // the protocol is "instantiated" for the PCI device. Covers duplicate open\r
+ // attempts (EFI_ALREADY_STARTED).\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ DeviceHandle, // candidate device\r
+ &gEfiPciIoProtocolGuid, // for generic PCI access\r
+ (VOID **)&PciIo, // handle to instantiate\r
+ This->DriverBindingHandle, // requestor driver identity\r
+ DeviceHandle, // ControllerHandle, according to\r
+ // the UEFI Driver Model\r
+ EFI_OPEN_PROTOCOL_BY_DRIVER // get exclusive PciIo access to\r
+ // the device; to be released\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Read entire PCI configuration header for more extensive check ahead.\r
+ //\r
+ Status = PciIo->Pci.Read (\r
+ PciIo, // (protocol, device)\r
+ // handle\r
+ EfiPciIoWidthUint32, // access width & copy\r
+ // mode\r
+ 0, // Offset\r
+ sizeof Pci / sizeof (UINT32), // Count\r
+ &Pci // target buffer\r
+ );\r
+\r
+ if (Status == EFI_SUCCESS) {\r
+ //\r
+ // virtio-0.9.5, 2.1 PCI Discovery\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
+ }\r
+\r
+ //\r
+ // We needed PCI IO access only transitorily, to see whether we support the\r
+ // device or not.\r
+ //\r
+ gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
+ This->DriverBindingHandle, DeviceHandle);\r
+ return Status;\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
+ device.\r
+\r
+ @param[in out] Dev The driver instance to configure. The caller is\r
+ responsible for Dev->PciIo's validity (ie. working IO\r
+ access to the underlying virtio-blk PCI device).\r
+\r
+ @retval EFI_SUCCESS Setup complete.\r
+\r
+ @retval EFI_UNSUPPORTED The driver is unable to work with the virtio ring or\r
+ virtio-blk attributes the host provides.\r
+\r
+ @return Error codes from VirtioRingInit() or\r
+ VIRTIO_CFG_READ() / VIRTIO_CFG_WRITE().\r
+\r
+**/\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkInit (\r
+ IN OUT VBLK_DEV *Dev\r
+ )\r
+{\r
+ UINT8 NextDevStat;\r
+ EFI_STATUS Status;\r
+\r
+ UINT32 Features;\r
+ UINT64 NumSectors;\r
+ UINT32 BlockSize;\r
+ UINT16 QueueSize;\r
+\r
+ //\r
+ // Execute virtio-0.9.5, 2.2.1 Device Initialization Sequence.\r
+ //\r
+ NextDevStat = 0; // step 1 -- reset device\r
+ Status = VIRTIO_CFG_WRITE (Dev, VhdrDeviceStatus, NextDevStat);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Failed;\r
+ }\r
+\r
+ NextDevStat |= VSTAT_ACK; // step 2 -- acknowledge device presence\r
+ Status = VIRTIO_CFG_WRITE (Dev, VhdrDeviceStatus, NextDevStat);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Failed;\r
+ }\r
+\r
+ NextDevStat |= VSTAT_DRIVER; // step 3 -- we know how to drive it\r
+ Status = VIRTIO_CFG_WRITE (Dev, VhdrDeviceStatus, NextDevStat);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Failed;\r
+ }\r
+\r
+ //\r
+ // step 4a -- retrieve and validate features\r
+ //\r
+ Status = VIRTIO_CFG_READ (Dev, VhdrDeviceFeatureBits, &Features);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Failed;\r
+ }\r
+ Status = VIRTIO_CFG_READ (Dev, VhdrCapacity, &NumSectors);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Failed;\r
+ }\r
+ if (NumSectors == 0) {\r
+ Status = EFI_UNSUPPORTED;\r
+ goto Failed;\r
+ }\r
+\r
+ if (Features & VIRTIO_BLK_F_BLK_SIZE) {\r
+ Status = VIRTIO_CFG_READ (Dev, VhdrBlkSize, &BlockSize);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Failed;\r
+ }\r
+ if (BlockSize == 0 || BlockSize % 512 != 0 ||\r
+ 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
+ //\r
+ Status = EFI_UNSUPPORTED;\r
+ goto Failed;\r
+ }\r
+ }\r
+ else {\r
+ BlockSize = 512;\r
+ }\r
+\r
+ //\r
+ // step 4b -- allocate virtqueue\r
+ //\r
+ Status = VIRTIO_CFG_WRITE (Dev, VhdrQueueSelect, 0);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Failed;\r
+ }\r
+ Status = VIRTIO_CFG_READ (Dev, VhdrQueueSize, &QueueSize);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Failed;\r
+ }\r
+ if (QueueSize < 3) { // SynchronousRequest() uses at most three descriptors\r
+ Status = EFI_UNSUPPORTED;\r
+ goto Failed;\r
+ }\r
+\r
+ Status = VirtioRingInit (QueueSize, &Dev->Ring);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Failed;\r
+ }\r
+\r
+ //\r
+ // step 4c -- Report GPFN (guest-physical frame number) of queue. If anything\r
+ // fails from here on, we must release the ring resources.\r
+ //\r
+ Status = VIRTIO_CFG_WRITE (Dev, VhdrQueueAddress,\r
+ (UINTN) Dev->Ring.Base >> EFI_PAGE_SHIFT);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ReleaseQueue;\r
+ }\r
+\r
+ //\r
+ // step 5 -- Report understood features. There are no virtio-blk specific\r
+ // features to negotiate in virtio-0.9.5, plus we do not want any of the\r
+ // device-independent (known or unknown) VIRTIO_F_* capabilities (see\r
+ // Appendix B).\r
+ //\r
+ Status = VIRTIO_CFG_WRITE (Dev, VhdrGuestFeatureBits, 0);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ReleaseQueue;\r
+ }\r
+\r
+ //\r
+ // step 6 -- initialization complete\r
+ //\r
+ NextDevStat |= VSTAT_DRIVER_OK;\r
+ Status = VIRTIO_CFG_WRITE (Dev, VhdrDeviceStatus, NextDevStat);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ReleaseQueue;\r
+ }\r
+\r
+ //\r
+ // Populate the exported interface's attributes; see UEFI spec v2.3.1 +\r
+ // Errata C, 12.8 EFI Block I/O Protocol. We stick to the lowest possible\r
+ // EFI_BLOCK_IO_PROTOCOL revision for now.\r
+ //\r
+ Dev->BlockIo.Revision = 0;\r
+ Dev->BlockIo.Media = &Dev->BlockIoMedia;\r
+ Dev->BlockIo.Reset = &VirtioBlkReset;\r
+ Dev->BlockIo.ReadBlocks = &VirtioBlkReadBlocks;\r
+ Dev->BlockIo.WriteBlocks = &VirtioBlkWriteBlocks;\r
+ Dev->BlockIo.FlushBlocks = &VirtioBlkFlushBlocks;\r
+ Dev->BlockIoMedia.MediaId = 0;\r
+ Dev->BlockIoMedia.RemovableMedia = FALSE;\r
+ Dev->BlockIoMedia.MediaPresent = TRUE;\r
+ Dev->BlockIoMedia.LogicalPartition = FALSE;\r
+ Dev->BlockIoMedia.ReadOnly = !!(Features & VIRTIO_BLK_F_RO);\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
+ return EFI_SUCCESS;\r
+\r
+ReleaseQueue:\r
+ VirtioRingUninit (&Dev->Ring);\r
+\r
+Failed:\r
+ //\r
+ // Notify the host about our failure to setup: virtio-0.9.5, 2.2.2.1 Device\r
+ // Status. PCI IO access failure here should not mask the original error.\r
+ //\r
+ NextDevStat |= VSTAT_FAILED;\r
+ VIRTIO_CFG_WRITE (Dev, VhdrDeviceStatus, NextDevStat);\r
+\r
+ return Status; // reached only via Failed above\r
+}\r
+\r
+\r
+/**\r
+\r
+ Uninitialize the internals of a virtio-blk device that has been successfully\r
+ set up with VirtioBlkInit().\r
+\r
+ @param[in out] Dev The device to clean up.\r
+\r
+**/\r
+\r
+STATIC\r
+VOID\r
+EFIAPI\r
+VirtioBlkUninit (\r
+ IN OUT VBLK_DEV *Dev\r
+ )\r
+{\r
+ //\r
+ // Reset the virtual device -- see virtio-0.9.5, 2.2.2.1 Device Status. When\r
+ // VIRTIO_CFG_WRITE() returns, the host will have learned to stay away from\r
+ // the old comms area.\r
+ //\r
+ VIRTIO_CFG_WRITE (Dev, VhdrDeviceStatus, 0);\r
+\r
+ VirtioRingUninit (&Dev->Ring);\r
+\r
+ SetMem (&Dev->BlockIo, sizeof Dev->BlockIo, 0x00);\r
+ SetMem (&Dev->BlockIoMedia, sizeof Dev->BlockIoMedia, 0x00);\r
+}\r
+\r
+\r
+/**\r
+\r
+ After we've pronounced support for a specific device in\r
+ DriverBindingSupported(), we start managing said device (passed in by the\r
+ Driver Exeuction Environment) with the following service.\r
+\r
+ See DriverBindingSupported() for specification references.\r
+\r
+ @param[in] This The EFI_DRIVER_BINDING_PROTOCOL object\r
+ incorporating this driver (independently of\r
+ any device).\r
+\r
+ @param[in] DeviceHandle The supported device to drive.\r
+\r
+ @param[in] RemainingDevicePath Relevant only for bus drivers, ignored.\r
+\r
+\r
+ @retval EFI_SUCCESS Driver instance has been created and\r
+ initialized for the virtio-blk PCI device, it\r
+ is now accessibla via EFI_BLOCK_IO_PROTOCOL.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed.\r
+\r
+ @return Error codes from the OpenProtocol() boot\r
+ service, the PciIo protocol, VirtioBlkInit(),\r
+ or the InstallProtocolInterface() boot service.\r
+\r
+**/\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkDriverBindingStart (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE DeviceHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath\r
+ )\r
+{\r
+ VBLK_DEV *Dev;\r
+ EFI_STATUS Status;\r
+\r
+ Dev = (VBLK_DEV *) AllocateZeroPool (sizeof *Dev);\r
+ if (Dev == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ Status = gBS->OpenProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
+ (VOID **)&Dev->PciIo, This->DriverBindingHandle,\r
+ DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);\r
+ if (EFI_ERROR (Status)) {\r
+ goto FreeVirtioBlk;\r
+ }\r
+\r
+ //\r
+ // We must retain and ultimately restore the original PCI attributes of the\r
+ // device. See Driver Writer's Guide for UEFI 2.3.1 v1.01, 18.3 PCI drivers /\r
+ // 18.3.2 Start() and Stop().\r
+ //\r
+ // The third parameter ("Attributes", input) is ignored by the Get operation.\r
+ // The fourth parameter ("Result", output) is ignored by the Enable and Set\r
+ // operations.\r
+ //\r
+ // For virtio-blk we only need IO space access.\r
+ //\r
+ Status = Dev->PciIo->Attributes (Dev->PciIo, EfiPciIoAttributeOperationGet,\r
+ 0, &Dev->OriginalPciAttributes);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ClosePciIo;\r
+ }\r
+\r
+ Status = Dev->PciIo->Attributes (Dev->PciIo,\r
+ EfiPciIoAttributeOperationEnable,\r
+ EFI_PCI_IO_ATTRIBUTE_IO, NULL);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ClosePciIo;\r
+ }\r
+\r
+ //\r
+ // PCI IO access granted, configure virtio-blk device.\r
+ //\r
+ Status = VirtioBlkInit (Dev);\r
+ if (EFI_ERROR (Status)) {\r
+ goto RestorePciAttributes;\r
+ }\r
+\r
+ //\r
+ // Setup complete, attempt to export the driver instance's BlockIo interface.\r
+ //\r
+ Dev->Signature = VBLK_SIG;\r
+ Status = gBS->InstallProtocolInterface (&DeviceHandle,\r
+ &gEfiBlockIoProtocolGuid, EFI_NATIVE_INTERFACE,\r
+ &Dev->BlockIo);\r
+ if (EFI_ERROR (Status)) {\r
+ goto UninitDev;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+\r
+UninitDev:\r
+ VirtioBlkUninit (Dev);\r
+\r
+RestorePciAttributes:\r
+ Dev->PciIo->Attributes (Dev->PciIo, EfiPciIoAttributeOperationSet,\r
+ Dev->OriginalPciAttributes, NULL);\r
+\r
+ClosePciIo:\r
+ gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
+ This->DriverBindingHandle, DeviceHandle);\r
+\r
+FreeVirtioBlk:\r
+ FreePool (Dev);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+\r
+ Stop driving a virtio-blk device and remove its BlockIo interface.\r
+\r
+ This function replays the success path of DriverBindingStart() in reverse.\r
+ The host side virtio-blk device is reset, so that the OS boot loader or the\r
+ OS may reinitialize it.\r
+\r
+ @param[in] This The EFI_DRIVER_BINDING_PROTOCOL object\r
+ incorporating this driver (independently of any\r
+ device).\r
+\r
+ @param[in] DeviceHandle Stop driving this device.\r
+\r
+ @param[in] NumberOfChildren Since this function belongs to a device driver\r
+ only (as opposed to a bus driver), the caller\r
+ environment sets NumberOfChildren to zero, and\r
+ we ignore it.\r
+\r
+ @param[in] ChildHandleBuffer Ignored (corresponding to NumberOfChildren).\r
+\r
+**/\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkDriverBindingStop (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE DeviceHandle,\r
+ IN UINTN NumberOfChildren,\r
+ IN EFI_HANDLE *ChildHandleBuffer\r
+ )\r
+{\r
+ VBLK_DEV *Dev;\r
+ EFI_STATUS Status;\r
+\r
+ Dev = VIRTIO_BLK_FROM_BLOCK_IO (This);\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
+ //\r
+ Status = gBS->UninstallProtocolInterface (DeviceHandle,\r
+ &gEfiBlockIoProtocolGuid, &Dev->BlockIo);\r
+ ASSERT (Status == EFI_SUCCESS);\r
+\r
+ VirtioBlkUninit (Dev);\r
+\r
+ Dev->PciIo->Attributes (Dev->PciIo, EfiPciIoAttributeOperationSet,\r
+ Dev->OriginalPciAttributes, NULL);\r
+\r
+ gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
+ This->DriverBindingHandle, DeviceHandle);\r
+\r
+ FreePool (Dev);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+//\r
+// The static object that groups the Supported() (ie. probe), Start() and\r
+// Stop() functions of the driver together. Refer to UEFI Spec 2.3.1 + Errata\r
+// C, 10.1 EFI Driver Binding Protocol.\r
+//\r
+STATIC EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = {\r
+ &VirtioBlkDriverBindingSupported,\r
+ &VirtioBlkDriverBindingStart,\r
+ &VirtioBlkDriverBindingStop,\r
+ 0x10, // Version, must be in [0x10 .. 0xFFFFFFEF] for IHV-developed drivers\r
+ NULL, // ImageHandle, to be overwritten by\r
+ // EfiLibInstallDriverBindingComponentName2() in VirtioBlkEntryPoint()\r
+ NULL // DriverBindingHandle, ditto\r
+};\r
+\r
+\r
+//\r
+// The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and\r
+// EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's name\r
+// in English, for display on standard console devices. This is recommended for\r
+// UEFI drivers that follow the UEFI Driver Model. Refer to the Driver Writer's\r
+// Guide for UEFI 2.3.1 v1.01, 11 UEFI Driver and Controller Names.\r
+//\r
+// Device type names ("Virtio Block Device") are not formatted because the\r
+// driver supports only that device type. Therefore the driver name suffices\r
+// for unambiguous identification.\r
+//\r
+\r
+STATIC GLOBAL_REMOVE_IF_UNREFERENCED\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
+EFI_COMPONENT_NAME_PROTOCOL gComponentName;\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkGetDriverName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **DriverName\r
+ )\r
+{\r
+ return LookupUnicodeString2 (\r
+ Language,\r
+ This->SupportedLanguages,\r
+ mDriverNameTable,\r
+ DriverName,\r
+ (BOOLEAN)(This == &gComponentName) // Iso639Language\r
+ );\r
+}\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkGetDeviceName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN EFI_HANDLE DeviceHandle,\r
+ IN EFI_HANDLE ChildHandle,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **ControllerName\r
+ )\r
+{\r
+ return EFI_UNSUPPORTED;\r
+}\r
+\r
+STATIC GLOBAL_REMOVE_IF_UNREFERENCED\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
+EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = {\r
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &VirtioBlkGetDriverName,\r
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &VirtioBlkGetDeviceName,\r
+ "en" // SupportedLanguages, RFC 4646 language codes\r
+};\r
+\r
+\r
+//\r
+// Entry point of this driver.\r
+//\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkEntryPoint (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ return EfiLibInstallDriverBindingComponentName2 (\r
+ ImageHandle,\r
+ SystemTable,\r
+ &gDriverBinding,\r
+ ImageHandle,\r
+ &gComponentName,\r
+ &gComponentName2\r
+ );\r
+}\r
+\r
--- /dev/null
+/** @file\r
+\r
+ Internal definitions for the virtio-blk driver, which produces Block I/O\r
+ Protocol instances for virtio-blk devices.\r
+\r
+ Copyright (C) 2012, Red Hat, Inc.\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 <Protocol/BlockIo.h>\r
+#include <Protocol/ComponentName.h>\r
+#include <Protocol/DriverBinding.h>\r
+#include <Protocol/PciIo.h>\r
+\r
+#include "Virtio.h"\r
+\r
+\r
+#define VBLK_SIG SIGNATURE_32 ('V', 'B', 'L', 'K')\r
+\r
+typedef struct {\r
+ //\r
+ // Parts of this structure are initialized / torn down in various functions\r
+ // at various call depths. The table to the right should make it easier to\r
+ // track them.\r
+ //\r
+ // field init function init dpth\r
+ // ---------------------- ------------------ ---------\r
+ UINT32 Signature; // DriverBindingStart 0\r
+ EFI_PCI_IO_PROTOCOL *PciIo; // DriverBindingStart 0\r
+ UINT64 OriginalPciAttributes; // DriverBindingStart 0\r
+ VRING Ring; // VirtioRingInit 2\r
+ EFI_BLOCK_IO_PROTOCOL BlockIo; // VirtioBlkInit 1\r
+ EFI_BLOCK_IO_MEDIA BlockIoMedia; // VirtioBlkInit 1\r
+} VBLK_DEV;\r
+\r
+#define VIRTIO_BLK_FROM_BLOCK_IO(BlockIoPointer) \\r
+ CR (BlockIoPointer, VBLK_DEV, BlockIo, VBLK_SIG)\r
+\r
+\r
+/**\r
+\r
+ Device probe function for this driver.\r
+\r
+ The DXE core calls this function for any given device in order to see if the\r
+ driver can drive the device.\r
+\r
+ Specs relevant in the general sense:\r
+\r
+ - UEFI Spec 2.3.1 + Errata C:\r
+ - 6.3 Protocol Handler Services -- for accessing the underlying device\r
+ - 10.1 EFI Driver Binding Protocol -- for exporting ourselves\r
+\r
+ - Driver Writer's Guide for UEFI 2.3.1 v1.01:\r
+ - 5.1.3.4 OpenProtocol() and CloseProtocol() -- for accessing the\r
+ underlying device\r
+ - 9 Driver Binding Protocol -- for exporting ourselves\r
+\r
+ Specs relevant in the specific sense:\r
+ - UEFI Spec 2.3.1 + Errata C, 13.4 EFI PCI I/O Protocol\r
+ - Driver Writer's Guide for UEFI 2.3.1 v1.01, 18 PCI Driver Design\r
+ Guidelines, 18.3 PCI drivers.\r
+\r
+ @param[in] This The EFI_DRIVER_BINDING_PROTOCOL object\r
+ incorporating this driver (independently of\r
+ any device).\r
+\r
+ @param[in] DeviceHandle The device to probe.\r
+\r
+ @param[in] RemainingDevicePath Relevant only for bus drivers, ignored.\r
+\r
+\r
+ @retval EFI_SUCCESS The driver supports the device being probed.\r
+\r
+ @retval EFI_UNSUPPORTED Based on virtio-blk PCI discovery, we do not support\r
+ the device.\r
+\r
+ @return Error codes from the OpenProtocol() boot service or\r
+ the PciIo protocol.\r
+\r
+**/\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkDriverBindingSupported (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE DeviceHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath\r
+ );\r
+\r
+\r
+/**\r
+\r
+ After we've pronounced support for a specific device in\r
+ DriverBindingSupported(), we start managing said device (passed in by the\r
+ Driver Exeuction Environment) with the following service.\r
+\r
+ See DriverBindingSupported() for specification references.\r
+\r
+ @param[in] This The EFI_DRIVER_BINDING_PROTOCOL object\r
+ incorporating this driver (independently of\r
+ any device).\r
+\r
+ @param[in] DeviceHandle The supported device to drive.\r
+\r
+ @param[in] RemainingDevicePath Relevant only for bus drivers, ignored.\r
+\r
+\r
+ @retval EFI_SUCCESS Driver instance has been created and\r
+ initialized for the virtio-blk PCI device, it\r
+ is now accessibla via EFI_BLOCK_IO_PROTOCOL.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed.\r
+\r
+ @return Error codes from the OpenProtocol() boot\r
+ service, the PciIo protocol, VirtioBlkInit(),\r
+ or the InstallProtocolInterface() boot service.\r
+\r
+**/\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkDriverBindingStart (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE DeviceHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath\r
+ );\r
+\r
+\r
+/**\r
+\r
+ Stop driving a virtio-blk device and remove its BlockIo interface.\r
+\r
+ This function replays the success path of DriverBindingStart() in reverse.\r
+ The host side virtio-blk device is reset, so that the OS boot loader or the\r
+ OS may reinitialize it.\r
+\r
+ @param[in] This The EFI_DRIVER_BINDING_PROTOCOL object\r
+ incorporating this driver (independently of any\r
+ device).\r
+\r
+ @param[in] DeviceHandle Stop driving this device.\r
+\r
+ @param[in] NumberOfChildren Since this function belongs to a device driver\r
+ only (as opposed to a bus driver), the caller\r
+ environment sets NumberOfChildren to zero, and\r
+ we ignore it.\r
+\r
+ @param[in] ChildHandleBuffer Ignored (corresponding to NumberOfChildren).\r
+\r
+**/\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkDriverBindingStop (\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE DeviceHandle,\r
+ IN UINTN NumberOfChildren,\r
+ IN EFI_HANDLE *ChildHandleBuffer\r
+ );\r
+\r
+\r
+//\r
+// UEFI Spec 2.3.1 + Errata C, 12.8 EFI Block I/O Protocol\r
+// Driver Writer's Guide for UEFI 2.3.1 v1.01,\r
+// 24.2 Block I/O Protocol Implementations\r
+//\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkReset (\r
+ IN EFI_BLOCK_IO_PROTOCOL *This,\r
+ IN BOOLEAN ExtendedVerification\r
+ );\r
+\r
+\r
+/**\r
+\r
+ ReadBlocks() operation for virtio-blk.\r
+\r
+ See\r
+ - UEFI Spec 2.3.1 + Errata C, 12.8 EFI Block I/O Protocol, 12.8 EFI Block I/O\r
+ Protocol, EFI_BLOCK_IO_PROTOCOL.ReadBlocks().\r
+ - Driver Writer's Guide for UEFI 2.3.1 v1.01, 24.2.2. ReadBlocks() and\r
+ ReadBlocksEx() Implementation.\r
+\r
+ Parameter checks and conformant return values are implemented in\r
+ VerifyReadWriteRequest() and SynchronousRequest().\r
+\r
+ A zero BufferSize doesn't seem to be prohibited, so do nothing in that case,\r
+ successfully.\r
+\r
+**/\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkReadBlocks (\r
+ IN EFI_BLOCK_IO_PROTOCOL *This,\r
+ IN UINT32 MediaId,\r
+ IN EFI_LBA Lba,\r
+ IN UINTN BufferSize,\r
+ OUT VOID *Buffer\r
+ );\r
+\r
+\r
+/**\r
+\r
+ WriteBlocks() operation for virtio-blk.\r
+\r
+ See\r
+ - UEFI Spec 2.3.1 + Errata C, 12.8 EFI Block I/O Protocol, 12.8 EFI Block I/O\r
+ Protocol, EFI_BLOCK_IO_PROTOCOL.WriteBlocks().\r
+ - Driver Writer's Guide for UEFI 2.3.1 v1.01, 24.2.3 WriteBlocks() and\r
+ WriteBlockEx() Implementation.\r
+\r
+ Parameter checks and conformant return values are implemented in\r
+ VerifyReadWriteRequest() and SynchronousRequest().\r
+\r
+ A zero BufferSize doesn't seem to be prohibited, so do nothing in that case,\r
+ successfully.\r
+\r
+**/\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkWriteBlocks (\r
+ IN EFI_BLOCK_IO_PROTOCOL *This,\r
+ IN UINT32 MediaId,\r
+ IN EFI_LBA Lba,\r
+ IN UINTN BufferSize,\r
+ IN VOID *Buffer\r
+ );\r
+\r
+\r
+/**\r
+\r
+ FlushBlocks() operation for virtio-blk.\r
+\r
+ See\r
+ - UEFI Spec 2.3.1 + Errata C, 12.8 EFI Block I/O Protocol, 12.8 EFI Block I/O\r
+ Protocol, EFI_BLOCK_IO_PROTOCOL.FlushBlocks().\r
+ - Driver Writer's Guide for UEFI 2.3.1 v1.01, 24.2.4 FlushBlocks() and\r
+ FlushBlocksEx() Implementation.\r
+\r
+ If the underlying virtio-blk device doesn't support flushing (ie.\r
+ write-caching), then this function should not be called by higher layers,\r
+ according to EFI_BLOCK_IO_MEDIA characteristics set in VirtioBlkInit().\r
+ Should they do nonetheless, we do nothing, successfully.\r
+\r
+**/\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkFlushBlocks (\r
+ IN EFI_BLOCK_IO_PROTOCOL *This\r
+ );\r
+\r
+\r
+//\r
+// The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and\r
+// EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's name\r
+// in English, for display on standard console devices. This is recommended for\r
+// UEFI drivers that follow the UEFI Driver Model. Refer to the Driver Writer's\r
+// Guide for UEFI 2.3.1 v1.01, 11 UEFI Driver and Controller Names.\r
+//\r
+// Device type names ("Virtio Block Device") are not formatted because the\r
+// driver supports only that device type. Therefore the driver name suffices\r
+// for unambiguous identification.\r
+//\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkGetDriverName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **DriverName\r
+ );\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioBlkGetDeviceName (\r
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN EFI_HANDLE DeviceHandle,\r
+ IN EFI_HANDLE ChildHandle,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **ControllerName\r
+ );\r