synchronous requests and EFI_BLOCK_IO_PROTOCOL for now.\r
\r
Copyright (C) 2012, Red Hat, Inc.\r
+ Copyright (c) 2012 - 2018, Intel Corporation. All rights reserved.<BR>\r
+ Copyright (c) 2017, AMD Inc, 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
- 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
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
\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
- virtio-blk PCI device, for configuration purposes.\r
+ virtio-blk 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
+ @param[in] Dev Pointer to the VBLK_DEV structure whose VirtIo space\r
+ we're accessing. Dev->VirtIo must be valid.\r
\r
@param[in] Field A field name from VBLK_HDR, identifying the virtio-blk\r
configuration item to access.\r
one of UINT8, UINT16, UINT32, UINT64.\r
\r
\r
- @return Status code returned by VirtioWrite() / VirtioRead().\r
+ @return Status code returned by Virtio->WriteDevice() /\r
+ Virtio->ReadDevice().\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
+#define VIRTIO_CFG_WRITE(Dev, Field, Value) ((Dev)->VirtIo->WriteDevice ( \\r
+ (Dev)->VirtIo, \\r
+ OFFSET_OF_VBLK (Field), \\r
+ SIZE_OF_VBLK (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
+#define VIRTIO_CFG_READ(Dev, Field, Pointer) ((Dev)->VirtIo->ReadDevice ( \\r
+ (Dev)->VirtIo, \\r
+ OFFSET_OF_VBLK (Field), \\r
+ SIZE_OF_VBLK (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
EFI_STATUS\r
EFIAPI\r
VirtioBlkReset (\r
- IN EFI_BLOCK_IO_PROTOCOL *This,\r
- IN BOOLEAN ExtendedVerification\r
+ IN EFI_BLOCK_IO_PROTOCOL *This,\r
+ IN BOOLEAN ExtendedVerification\r
)\r
{\r
//\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
+ 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
+ UINTN BlockCount;\r
\r
ASSERT (PositiveBufferSize > 0);\r
\r
- if (PositiveBufferSize > SIZE_1GB ||\r
- PositiveBufferSize % Media->BlockSize > 0) {\r
+ if ((PositiveBufferSize > SIZE_1GB) ||\r
+ (PositiveBufferSize % Media->BlockSize > 0))\r
+ {\r
return EFI_BAD_BUFFER_SIZE;\r
}\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
+ if ((Lba > Media->LastBlock) || (BlockCount - 1 > Media->LastBlock - Lba)) {\r
return EFI_INVALID_PARAMETER;\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
\r
@retval EFI_SUCCESS Transfer complete.\r
\r
- @retval EFI_DEVICE_ERROR Failed to notify host side via PCI write, or\r
+ @retval EFI_DEVICE_ERROR Failed to notify host side via VirtIo write, or\r
unable to parse host response, or host response\r
- is not VIRTIO_BLK_S_OK.\r
+ is not VIRTIO_BLK_S_OK or failed to map Buffer\r
+ for a bus master operation.\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
+ 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
+ UINT32 BlockSize;\r
+ volatile VIRTIO_BLK_REQ Request;\r
+ volatile UINT8 *HostStatus;\r
+ VOID *HostStatusBuffer;\r
+ DESC_INDICES Indices;\r
+ VOID *RequestMapping;\r
+ VOID *StatusMapping;\r
+ VOID *BufferMapping;\r
+ EFI_PHYSICAL_ADDRESS BufferDeviceAddress;\r
+ EFI_PHYSICAL_ADDRESS HostStatusDeviceAddress;\r
+ EFI_PHYSICAL_ADDRESS RequestDeviceAddress;\r
+ EFI_STATUS Status;\r
+ EFI_STATUS UnmapStatus;\r
\r
BlockSize = Dev->BlockIoMedia.BlockSize;\r
\r
+ //\r
+ // Set BufferMapping and BufferDeviceAddress to suppress incorrect\r
+ // compiler/analyzer warnings.\r
+ //\r
+ BufferMapping = NULL;\r
+ BufferDeviceAddress = 0;\r
+\r
//\r
// ensured by VirtioBlkInit()\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.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
+ Request.Sector = MultU64x32 (Lba, BlockSize / 512);\r
+\r
+ //\r
+ // Host status is bi-directional (we preset with a value and expect the\r
+ // device to update it). Allocate a host status buffer which can be mapped\r
+ // to access equally by both processor and the device.\r
+ //\r
+ Status = Dev->VirtIo->AllocateSharedPages (\r
+ Dev->VirtIo,\r
+ EFI_SIZE_TO_PAGES (sizeof *HostStatus),\r
+ &HostStatusBuffer\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ HostStatus = HostStatusBuffer;\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
+ // Map virtio-blk request header (must be done after request header is\r
+ // populated)\r
//\r
- *Dev->Ring.Avail.Flags = (UINT16) VRING_AVAIL_F_NO_INTERRUPT;\r
+ Status = VirtioMapAllBytesInSharedBuffer (\r
+ Dev->VirtIo,\r
+ VirtioOperationBusMasterRead,\r
+ (VOID *)&Request,\r
+ sizeof Request,\r
+ &RequestDeviceAddress,\r
+ &RequestMapping\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto FreeHostStatusBuffer;\r
+ }\r
+\r
+ //\r
+ // Map data buffer\r
+ //\r
+ if (BufferSize > 0) {\r
+ Status = VirtioMapAllBytesInSharedBuffer (\r
+ Dev->VirtIo,\r
+ (RequestIsWrite ?\r
+ VirtioOperationBusMasterRead :\r
+ VirtioOperationBusMasterWrite),\r
+ (VOID *)Buffer,\r
+ BufferSize,\r
+ &BufferDeviceAddress,\r
+ &BufferMapping\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto UnmapRequestBuffer;\r
+ }\r
+ }\r
\r
//\r
// preset a host status for ourselves that we do not accept as success\r
//\r
- HostStatus = VIRTIO_BLK_S_IOERR;\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
+ // Map the Status Buffer with VirtioOperationBusMasterCommonBuffer so that\r
+ // both processor and device can access it.\r
//\r
- ASSERT (Dev->Ring.QueueSize >= 3);\r
+ Status = VirtioMapAllBytesInSharedBuffer (\r
+ Dev->VirtIo,\r
+ VirtioOperationBusMasterCommonBuffer,\r
+ HostStatusBuffer,\r
+ sizeof *HostStatus,\r
+ &HostStatusDeviceAddress,\r
+ &StatusMapping\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto UnmapDataBuffer;\r
+ }\r
+\r
+ VirtioPrepare (&Dev->Ring, &Indices);\r
\r
//\r
- // Implement virtio-0.9.5, 2.4.1 Supplying Buffers to the Device.\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
- FirstAvailIdx = *Dev->Ring.Avail.Idx;\r
- NextAvailIdx = FirstAvailIdx;\r
+ ASSERT (Dev->Ring.QueueSize >= 3);\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 (\r
+ &Dev->Ring,\r
+ RequestDeviceAddress,\r
+ sizeof Request,\r
+ VRING_DESC_F_NEXT,\r
+ &Indices\r
+ );\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 (\r
+ &Dev->Ring,\r
+ BufferDeviceAddress,\r
+ (UINT32)BufferSize,\r
VRING_DESC_F_NEXT | (RequestIsWrite ? 0 : VRING_DESC_F_WRITE),\r
- FirstAvailIdx, &NextAvailIdx);\r
+ &Indices\r
+ );\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, Generic.VhdrQueueNotify, 0))) {\r
- return EFI_DEVICE_ERROR;\r
+ VirtioAppendDesc (\r
+ &Dev->Ring,\r
+ HostStatusDeviceAddress,\r
+ sizeof *HostStatus,\r
+ VRING_DESC_F_WRITE,\r
+ &Indices\r
+ );\r
+\r
+ //\r
+ // virtio-blk's only virtqueue is #0, called "requestq" (see Appendix D).\r
+ //\r
+ if ((VirtioFlush (\r
+ Dev->VirtIo,\r
+ 0,\r
+ &Dev->Ring,\r
+ &Indices,\r
+ NULL\r
+ ) == EFI_SUCCESS) &&\r
+ (*HostStatus == VIRTIO_BLK_S_OK))\r
+ {\r
+ Status = EFI_SUCCESS;\r
+ } else {\r
+ Status = 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
+ Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, StatusMapping);\r
\r
- if (PollPeriodUsecs < 1024) {\r
- PollPeriodUsecs *= 2;\r
+UnmapDataBuffer:\r
+ if (BufferSize > 0) {\r
+ UnmapStatus = Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, BufferMapping);\r
+ if (EFI_ERROR (UnmapStatus) && !RequestIsWrite && !EFI_ERROR (Status)) {\r
+ //\r
+ // Data from the bus master may not reach the caller; fail the request.\r
+ //\r
+ Status = EFI_DEVICE_ERROR;\r
}\r
- MemoryFence();\r
}\r
\r
- if (HostStatus == VIRTIO_BLK_S_OK) {\r
- return EFI_SUCCESS;\r
- }\r
+UnmapRequestBuffer:\r
+ Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, RequestMapping);\r
\r
- return EFI_DEVICE_ERROR;\r
-}\r
+FreeHostStatusBuffer:\r
+ Dev->VirtIo->FreeSharedPages (\r
+ Dev->VirtIo,\r
+ EFI_SIZE_TO_PAGES (sizeof *HostStatus),\r
+ HostStatusBuffer\r
+ );\r
\r
+ return Status;\r
+}\r
\r
/**\r
\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
+ 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
+ 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
+ Dev = VIRTIO_BLK_FROM_BLOCK_IO (This);\r
Status = VerifyReadWriteRequest (\r
&Dev->BlockIoMedia,\r
Lba,\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
+ 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
+ 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
+ Dev = VIRTIO_BLK_FROM_BLOCK_IO (This);\r
Status = VerifyReadWriteRequest (\r
&Dev->BlockIoMedia,\r
Lba,\r
);\r
}\r
\r
-\r
/**\r
\r
FlushBlocks() operation for virtio-blk.\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
+ IN EFI_BLOCK_IO_PROTOCOL *This\r
)\r
{\r
- VBLK_DEV *Dev;\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
+ 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
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
@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
+ @retval EFI_UNSUPPORTED Based on virtio-blk discovery, we do not support\r
the device.\r
\r
@return Error codes from the OpenProtocol() boot service or\r
- the PciIo protocol.\r
+ the VirtIo 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
+ 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
+ EFI_STATUS Status;\r
+ VIRTIO_DEVICE_PROTOCOL *VirtIo;\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
+ // Attempt to open the device with the VirtIo set of interfaces. On success,\r
+ // the protocol is "instantiated" for the VirtIo device. Covers duplicate\r
+ // open 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
+ &gVirtioDeviceProtocolGuid, // for generic VirtIo access\r
+ (VOID **)&VirtIo, // 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
+ EFI_OPEN_PROTOCOL_BY_DRIVER // get exclusive VirtIo 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
+ if (VirtIo->SubSystemDeviceId != VIRTIO_SUBSYSTEM_BLOCK_DEVICE) {\r
+ Status = EFI_UNSUPPORTED;\r
}\r
\r
//\r
- // We needed PCI IO access only transitorily, to see whether we support the\r
+ // We needed VirtIo access only transitorily, to see whether we support the\r
// device or not.\r
//\r
- gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
- This->DriverBindingHandle, DeviceHandle);\r
+ gBS->CloseProtocol (\r
+ DeviceHandle,\r
+ &gVirtioDeviceProtocolGuid,\r
+ This->DriverBindingHandle,\r
+ DeviceHandle\r
+ );\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
+ responsible for Dev->VirtIo's validity (ie. working IO\r
+ access to the underlying virtio-blk device).\r
\r
@retval EFI_SUCCESS Setup complete.\r
\r
virtio-blk attributes the host provides.\r
\r
@return Error codes from VirtioRingInit() or\r
- VIRTIO_CFG_READ() / VIRTIO_CFG_WRITE().\r
+ VIRTIO_CFG_READ() / VIRTIO_CFG_WRITE or\r
+ VirtioRingMap().\r
\r
**/\r
-\r
STATIC\r
EFI_STATUS\r
EFIAPI\r
VirtioBlkInit (\r
- IN OUT VBLK_DEV *Dev\r
+ IN OUT VBLK_DEV *Dev\r
)\r
{\r
- UINT8 NextDevStat;\r
- EFI_STATUS Status;\r
+ UINT8 NextDevStat;\r
+ EFI_STATUS Status;\r
\r
- UINT32 Features;\r
- UINT64 NumSectors;\r
- UINT32 BlockSize;\r
- UINT16 QueueSize;\r
+ UINT64 Features;\r
+ UINT64 NumSectors;\r
+ UINT32 BlockSize;\r
+ UINT8 PhysicalBlockExp;\r
+ UINT8 AlignmentOffset;\r
+ UINT32 OptIoSize;\r
+ UINT16 QueueSize;\r
+ UINT64 RingBaseShift;\r
+\r
+ PhysicalBlockExp = 0;\r
+ AlignmentOffset = 0;\r
+ OptIoSize = 0;\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, Generic.VhdrDeviceStatus, NextDevStat);\r
+ Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 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, Generic.VhdrDeviceStatus, NextDevStat);\r
+ Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 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, Generic.VhdrDeviceStatus, NextDevStat);\r
+ Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Failed;\r
+ }\r
+\r
+ //\r
+ // Set Page Size - MMIO VirtIo Specific\r
+ //\r
+ Status = Dev->VirtIo->SetPageSize (Dev->VirtIo, EFI_PAGE_SIZE);\r
if (EFI_ERROR (Status)) {\r
goto Failed;\r
}\r
//\r
// step 4a -- retrieve and validate features\r
//\r
- Status = VIRTIO_CFG_READ (Dev, Generic.VhdrDeviceFeatureBits, &Features);\r
+ Status = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo, &Features);\r
if (EFI_ERROR (Status)) {\r
goto Failed;\r
}\r
- Status = VIRTIO_CFG_READ (Dev, VhdrCapacity, &NumSectors);\r
+\r
+ Status = VIRTIO_CFG_READ (Dev, Capacity, &NumSectors);\r
if (EFI_ERROR (Status)) {\r
goto Failed;\r
}\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
+ Status = VIRTIO_CFG_READ (Dev, BlkSize, &BlockSize);\r
if (EFI_ERROR (Status)) {\r
goto Failed;\r
}\r
- if (BlockSize == 0 || BlockSize % 512 != 0 ||\r
- NumSectors % (BlockSize / 512) != 0) {\r
+\r
+ if ((BlockSize == 0) || (BlockSize % 512 != 0) ||\r
+ (ModU64x32 (NumSectors, BlockSize / 512) != 0))\r
+ {\r
//\r
// We can only handle a logical block consisting of whole sectors,\r
// and only a disk composed of whole logical blocks.\r
Status = EFI_UNSUPPORTED;\r
goto Failed;\r
}\r
- }\r
- else {\r
+ } else {\r
BlockSize = 512;\r
}\r
\r
+ if (Features & VIRTIO_BLK_F_TOPOLOGY) {\r
+ Status = VIRTIO_CFG_READ (\r
+ Dev,\r
+ Topology.PhysicalBlockExp,\r
+ &PhysicalBlockExp\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Failed;\r
+ }\r
+\r
+ if (PhysicalBlockExp >= 32) {\r
+ Status = EFI_UNSUPPORTED;\r
+ goto Failed;\r
+ }\r
+\r
+ Status = VIRTIO_CFG_READ (Dev, Topology.AlignmentOffset, &AlignmentOffset);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Failed;\r
+ }\r
+\r
+ Status = VIRTIO_CFG_READ (Dev, Topology.OptIoSize, &OptIoSize);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Failed;\r
+ }\r
+ }\r
+\r
+ Features &= VIRTIO_BLK_F_BLK_SIZE | VIRTIO_BLK_F_TOPOLOGY | VIRTIO_BLK_F_RO |\r
+ VIRTIO_BLK_F_FLUSH | VIRTIO_F_VERSION_1 |\r
+ VIRTIO_F_IOMMU_PLATFORM;\r
+\r
+ //\r
+ // In virtio-1.0, feature negotiation is expected to complete before queue\r
+ // discovery, and the device can also reject the selected set of features.\r
+ //\r
+ if (Dev->VirtIo->Revision >= VIRTIO_SPEC_REVISION (1, 0, 0)) {\r
+ Status = Virtio10WriteFeatures (Dev->VirtIo, Features, &NextDevStat);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Failed;\r
+ }\r
+ }\r
+\r
//\r
// step 4b -- allocate virtqueue\r
//\r
- Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrQueueSelect, 0);\r
+ Status = Dev->VirtIo->SetQueueSel (Dev->VirtIo, 0);\r
if (EFI_ERROR (Status)) {\r
goto Failed;\r
}\r
- Status = VIRTIO_CFG_READ (Dev, Generic.VhdrQueueSize, &QueueSize);\r
+\r
+ Status = Dev->VirtIo->GetQueueNumMax (Dev->VirtIo, &QueueSize);\r
if (EFI_ERROR (Status)) {\r
goto Failed;\r
}\r
- if (QueueSize < 3) { // SynchronousRequest() uses at most three descriptors\r
+\r
+ if (QueueSize < 3) {\r
+ // SynchronousRequest() uses at most three descriptors\r
Status = EFI_UNSUPPORTED;\r
goto Failed;\r
}\r
\r
- Status = VirtioRingInit (QueueSize, &Dev->Ring);\r
+ Status = VirtioRingInit (Dev->VirtIo, 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
+ // If anything fails from here on, we must release the ring resources\r
//\r
- Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrQueueAddress,\r
- (UINTN) Dev->Ring.Base >> EFI_PAGE_SHIFT);\r
+ Status = VirtioRingMap (\r
+ Dev->VirtIo,\r
+ &Dev->Ring,\r
+ &RingBaseShift,\r
+ &Dev->RingMap\r
+ );\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
+ // Additional steps for MMIO: align the queue appropriately, and set the\r
+ // size. If anything fails from here on, we must unmap the ring resources.\r
//\r
- Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrGuestFeatureBits, 0);\r
+ Status = Dev->VirtIo->SetQueueNum (Dev->VirtIo, QueueSize);\r
if (EFI_ERROR (Status)) {\r
- goto ReleaseQueue;\r
+ goto UnmapQueue;\r
+ }\r
+\r
+ Status = Dev->VirtIo->SetQueueAlign (Dev->VirtIo, EFI_PAGE_SIZE);\r
+ if (EFI_ERROR (Status)) {\r
+ goto UnmapQueue;\r
+ }\r
+\r
+ //\r
+ // step 4c -- Report GPFN (guest-physical frame number) of queue.\r
+ //\r
+ Status = Dev->VirtIo->SetQueueAddress (\r
+ Dev->VirtIo,\r
+ &Dev->Ring,\r
+ RingBaseShift\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto UnmapQueue;\r
+ }\r
+\r
+ //\r
+ // step 5 -- Report understood features.\r
+ //\r
+ if (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) {\r
+ Features &= ~(UINT64)(VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM);\r
+ Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo, Features);\r
+ if (EFI_ERROR (Status)) {\r
+ goto UnmapQueue;\r
+ }\r
}\r
\r
//\r
// step 6 -- initialization complete\r
//\r
NextDevStat |= VSTAT_DRIVER_OK;\r
- Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, NextDevStat);\r
+ Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);\r
if (EFI_ERROR (Status)) {\r
- goto ReleaseQueue;\r
+ goto UnmapQueue;\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
+ // Populate the exported interface's attributes; see UEFI spec v2.4, 12.9 EFI\r
+ // Block I/O Protocol.\r
//\r
Dev->BlockIo.Revision = 0;\r
Dev->BlockIo.Media = &Dev->BlockIoMedia;\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.ReadOnly = (BOOLEAN)((Features & VIRTIO_BLK_F_RO) != 0);\r
+ Dev->BlockIoMedia.WriteCaching = (BOOLEAN)((Features & VIRTIO_BLK_F_FLUSH) != 0);\r
Dev->BlockIoMedia.BlockSize = BlockSize;\r
Dev->BlockIoMedia.IoAlign = 0;\r
- Dev->BlockIoMedia.LastBlock = NumSectors / (BlockSize / 512) - 1;\r
+ Dev->BlockIoMedia.LastBlock = DivU64x32 (\r
+ NumSectors,\r
+ BlockSize / 512\r
+ ) - 1;\r
+\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ "%a: LbaSize=0x%x[B] NumBlocks=0x%Lx[Lba]\n",\r
+ __FUNCTION__,\r
+ Dev->BlockIoMedia.BlockSize,\r
+ Dev->BlockIoMedia.LastBlock + 1\r
+ ));\r
+\r
+ if (Features & VIRTIO_BLK_F_TOPOLOGY) {\r
+ Dev->BlockIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION3;\r
+\r
+ Dev->BlockIoMedia.LowestAlignedLba = AlignmentOffset;\r
+ Dev->BlockIoMedia.LogicalBlocksPerPhysicalBlock = 1u << PhysicalBlockExp;\r
+ Dev->BlockIoMedia.OptimalTransferLengthGranularity = OptIoSize;\r
+\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ "%a: FirstAligned=0x%Lx[Lba] PhysBlkSize=0x%x[Lba]\n",\r
+ __FUNCTION__,\r
+ Dev->BlockIoMedia.LowestAlignedLba,\r
+ Dev->BlockIoMedia.LogicalBlocksPerPhysicalBlock\r
+ ));\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ "%a: OptimalTransferLengthGranularity=0x%x[Lba]\n",\r
+ __FUNCTION__,\r
+ Dev->BlockIoMedia.OptimalTransferLengthGranularity\r
+ ));\r
+ }\r
+\r
return EFI_SUCCESS;\r
\r
+UnmapQueue:\r
+ Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->RingMap);\r
+\r
ReleaseQueue:\r
- VirtioRingUninit (&Dev->Ring);\r
+ VirtioRingUninit (Dev->VirtIo, &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
+ // Status. VirtIo access failure here should not mask the original error.\r
//\r
NextDevStat |= VSTAT_FAILED;\r
- VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, NextDevStat);\r
+ Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 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
@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
+ IN OUT VBLK_DEV *Dev\r
)\r
{\r
//\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, Generic.VhdrDeviceStatus, 0);\r
+ Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);\r
\r
- VirtioRingUninit (&Dev->Ring);\r
+ Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->RingMap);\r
+ VirtioRingUninit (Dev->VirtIo, &Dev->Ring);\r
\r
- SetMem (&Dev->BlockIo, sizeof Dev->BlockIo, 0x00);\r
+ SetMem (&Dev->BlockIo, sizeof Dev->BlockIo, 0x00);\r
SetMem (&Dev->BlockIoMedia, sizeof Dev->BlockIoMedia, 0x00);\r
}\r
\r
+/**\r
+\r
+ Event notification function enqueued by ExitBootServices().\r
+\r
+ @param[in] Event Event whose notification function is being invoked.\r
+\r
+ @param[in] Context Pointer to the VBLK_DEV structure.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+VirtioBlkExitBoot (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ VBLK_DEV *Dev;\r
+\r
+ DEBUG ((DEBUG_VERBOSE, "%a: Context=0x%p\n", __FUNCTION__, Context));\r
+ //\r
+ // Reset the device. This causes the hypervisor to forget about the virtio\r
+ // ring.\r
+ //\r
+ // We allocated said ring in EfiBootServicesData type memory, and code\r
+ // executing after ExitBootServices() is permitted to overwrite it.\r
+ //\r
+ Dev = Context;\r
+ Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);\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
+ Driver Execution Environment) with the following service.\r
\r
See DriverBindingSupported() for specification references.\r
\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
+ initialized for the virtio-blk device, it\r
+ is now accessible 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
+ service, the VirtIo 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
+ 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
+ VBLK_DEV *Dev;\r
+ EFI_STATUS Status;\r
\r
- Dev = (VBLK_DEV *) AllocateZeroPool (sizeof *Dev);\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
+ Status = gBS->OpenProtocol (\r
+ DeviceHandle,\r
+ &gVirtioDeviceProtocolGuid,\r
+ (VOID **)&Dev->VirtIo,\r
+ This->DriverBindingHandle,\r
+ DeviceHandle,\r
+ EFI_OPEN_PROTOCOL_BY_DRIVER\r
+ );\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
+ // VirtIo access granted, configure virtio-blk device.\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
+ Status = VirtioBlkInit (Dev);\r
if (EFI_ERROR (Status)) {\r
- goto ClosePciIo;\r
+ goto CloseVirtIo;\r
}\r
\r
- //\r
- // PCI IO access granted, configure virtio-blk device.\r
- //\r
- Status = VirtioBlkInit (Dev);\r
+ Status = gBS->CreateEvent (\r
+ EVT_SIGNAL_EXIT_BOOT_SERVICES,\r
+ TPL_CALLBACK,\r
+ &VirtioBlkExitBoot,\r
+ Dev,\r
+ &Dev->ExitBoot\r
+ );\r
if (EFI_ERROR (Status)) {\r
- goto RestorePciAttributes;\r
+ goto UninitDev;\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
+ Status = gBS->InstallProtocolInterface (\r
+ &DeviceHandle,\r
+ &gEfiBlockIoProtocolGuid,\r
+ EFI_NATIVE_INTERFACE,\r
+ &Dev->BlockIo\r
+ );\r
if (EFI_ERROR (Status)) {\r
- goto UninitDev;\r
+ goto CloseExitBoot;\r
}\r
\r
return EFI_SUCCESS;\r
\r
+CloseExitBoot:\r
+ gBS->CloseEvent (Dev->ExitBoot);\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
+CloseVirtIo:\r
+ gBS->CloseProtocol (\r
+ DeviceHandle,\r
+ &gVirtioDeviceProtocolGuid,\r
+ This->DriverBindingHandle,\r
+ DeviceHandle\r
+ );\r
\r
FreeVirtioBlk:\r
FreePool (Dev);\r
return Status;\r
}\r
\r
-\r
/**\r
\r
Stop driving a virtio-blk device and remove its BlockIo interface.\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
+ 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
+ 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
+ Status = gBS->UninstallProtocolInterface (\r
+ DeviceHandle,\r
+ &gEfiBlockIoProtocolGuid,\r
+ &Dev->BlockIo\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
\r
- VirtioBlkUninit (Dev);\r
+ gBS->CloseEvent (Dev->ExitBoot);\r
\r
- Dev->PciIo->Attributes (Dev->PciIo, EfiPciIoAttributeOperationSet,\r
- Dev->OriginalPciAttributes, NULL);\r
+ VirtioBlkUninit (Dev);\r
\r
- gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
- This->DriverBindingHandle, DeviceHandle);\r
+ gBS->CloseProtocol (\r
+ DeviceHandle,\r
+ &gVirtioDeviceProtocolGuid,\r
+ This->DriverBindingHandle,\r
+ DeviceHandle\r
+ );\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
+STATIC EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = {\r
&VirtioBlkDriverBindingSupported,\r
&VirtioBlkDriverBindingStart,\r
&VirtioBlkDriverBindingStop,\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
// for unambiguous identification.\r
//\r
\r
-STATIC GLOBAL_REMOVE_IF_UNREFERENCED\r
-EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {\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
-EFI_COMPONENT_NAME_PROTOCOL gComponentName;\r
+STATIC\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
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
+ IN CHAR8 *Language,\r
+ OUT CHAR16 **DriverName\r
)\r
{\r
return LookupUnicodeString2 (\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
+ 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
+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
-EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = {\r
- (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &VirtioBlkGetDriverName,\r
- (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &VirtioBlkGetDeviceName,\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
"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
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
)\r
{\r
return EfiLibInstallDriverBindingComponentName2 (\r
&gComponentName2\r
);\r
}\r
-\r