X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=OvmfPkg%2FVirtioBlkDxe%2FVirtioBlk.c;h=f1c3f87e202700451e7286475cd22217a0167398;hb=fed691a6f9138c3c22d075a9597435be43e660d0;hp=178fe331a42a52e261959903c3054650cd89c1a9;hpb=55c3443a4f9a7bba34f1c60c6121c6b73dd05241;p=mirror_edk2.git diff --git a/OvmfPkg/VirtioBlkDxe/VirtioBlk.c b/OvmfPkg/VirtioBlkDxe/VirtioBlk.c index 178fe331a4..f1c3f87e20 100644 --- a/OvmfPkg/VirtioBlkDxe/VirtioBlk.c +++ b/OvmfPkg/VirtioBlkDxe/VirtioBlk.c @@ -11,6 +11,7 @@ synchronous requests and EFI_BLOCK_IO_PROTOCOL for now. Copyright (C) 2012, Red Hat, Inc. + Copyright (c) 2012 - 2014, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this @@ -22,163 +23,27 @@ **/ -#include +#include #include #include #include #include #include +#include #include "VirtioBlk.h" -/** - - Write a word into Region 0 of the device specified by PciIo. - - Region 0 must be an iomem region. This is an internal function for the - VIRTIO_CFG_WRITE() macro below. - - @param[in] PciIo Target PCI device. - - @param[in] FieldOffset Destination offset. - - @param[in] FieldSize Destination field size, must be in { 1, 2, 4, 8 }. - - @param[in] Value Little endian value to write, converted to UINT64. - The least significant FieldSize bytes will be used. - - - @return Status code returned by PciIo->Io.Write(). - -**/ -STATIC -EFIAPI -EFI_STATUS -VirtioWrite ( - IN EFI_PCI_IO_PROTOCOL *PciIo, - IN UINTN FieldOffset, - IN UINTN FieldSize, - IN UINT64 Value - ) -{ - UINTN Count; - EFI_PCI_IO_PROTOCOL_WIDTH Width; - - Count = 1; - switch (FieldSize) { - case 1: - Width = EfiPciIoWidthUint8; - break; - - case 2: - Width = EfiPciIoWidthUint16; - break; - - case 8: - Count = 2; - // fall through - - case 4: - Width = EfiPciIoWidthUint32; - break; - - default: - ASSERT (FALSE); - } - - return PciIo->Io.Write ( - PciIo, - Width, - PCI_BAR_IDX0, - FieldOffset, - Count, - &Value - ); -} - - -/** - - Read a word from Region 0 of the device specified by PciIo. - - Region 0 must be an iomem region. This is an internal function for the - VIRTIO_CFG_READ() macro below. - - @param[in] PciIo Source PCI device. - - @param[in] FieldOffset Source offset. - - @param[in] FieldSize Source field size, must be in { 1, 2, 4, 8 }. - - @param[in] BufferSize Number of bytes available in the target buffer. Must - equal FieldSize. - - @param[out] Buffer Target buffer. - - - @return Status code returned by PciIo->Io.Read(). - -**/ -STATIC -EFIAPI -EFI_STATUS -VirtioRead ( - IN EFI_PCI_IO_PROTOCOL *PciIo, - IN UINTN FieldOffset, - IN UINTN FieldSize, - IN UINTN BufferSize, - OUT VOID *Buffer - ) -{ - UINTN Count; - EFI_PCI_IO_PROTOCOL_WIDTH Width; - - ASSERT (FieldSize == BufferSize); - - Count = 1; - switch (FieldSize) { - case 1: - Width = EfiPciIoWidthUint8; - break; - - case 2: - Width = EfiPciIoWidthUint16; - break; - - case 8: - Count = 2; - // fall through - - case 4: - Width = EfiPciIoWidthUint32; - break; - - default: - ASSERT (FALSE); - } - - return PciIo->Io.Read ( - PciIo, - Width, - PCI_BAR_IDX0, - FieldOffset, - Count, - Buffer - ); -} - - /** Convenience macros to read and write region 0 IO space elements of the - virtio-blk PCI device, for configuration purposes. + virtio-blk device, for configuration purposes. The following macros make it possible to specify only the "core parameters" for such accesses and to derive the rest. By the time VIRTIO_CFG_WRITE() returns, the transaction will have been completed. - @param[in] Dev Pointer to the VBLK_DEV structure whose PCI IO space - we're accessing. Dev->PciIo must be valid. + @param[in] Dev Pointer to the VBLK_DEV structure whose VirtIo space + we're accessing. Dev->VirtIo must be valid. @param[in] Field A field name from VBLK_HDR, identifying the virtio-blk configuration item to access. @@ -191,23 +56,24 @@ VirtioRead ( one of UINT8, UINT16, UINT32, UINT64. - @return Status code returned by VirtioWrite() / VirtioRead(). + @return Status code returned by Virtio->WriteDevice() / + Virtio->ReadDevice(). **/ -#define VIRTIO_CFG_WRITE(Dev, Field, Value) (VirtioWrite ( \ - (Dev)->PciIo, \ - OFFSET_OF_VHDR (Field), \ - SIZE_OF_VHDR (Field), \ - (Value) \ +#define VIRTIO_CFG_WRITE(Dev, Field, Value) ((Dev)->VirtIo->WriteDevice ( \ + (Dev)->VirtIo, \ + OFFSET_OF_VBLK (Field), \ + SIZE_OF_VBLK (Field), \ + (Value) \ )) -#define VIRTIO_CFG_READ(Dev, Field, Pointer) (VirtioRead ( \ - (Dev)->PciIo, \ - OFFSET_OF_VHDR (Field), \ - SIZE_OF_VHDR (Field), \ - sizeof *(Pointer), \ - (Pointer) \ +#define VIRTIO_CFG_READ(Dev, Field, Pointer) ((Dev)->VirtIo->ReadDevice ( \ + (Dev)->VirtIo, \ + OFFSET_OF_VBLK (Field), \ + SIZE_OF_VBLK (Field), \ + sizeof *(Pointer), \ + (Pointer) \ )) @@ -313,66 +179,6 @@ VerifyReadWriteRequest ( } -/** - - Append a contiguous buffer for transmission / reception via the virtio ring. - - This function implements the following sections from virtio-0.9.5: - - 2.4.1.1 Placing Buffers into the Descriptor Table - - 2.4.1.2 Updating the Available Ring - - Free space is taken as granted, since this driver supports only synchronous - requests and host side status is processed in lock-step with request - submission. VirtioBlkInit() verifies the ring size in advance. - - @param[in out] Ring The virtio ring to append the buffer to, as a - descriptor. - - @param [in] BufferPhysAddr (Guest pseudo-physical) start address of the - transmit / receive buffer - - @param [in] BufferSize Number of bytes to transmit or receive. - - @param [in] Flags A bitmask of VRING_DESC_F_* flags. The caller - computes this mask dependent on further buffers - to append and transfer direction. - VRING_DESC_F_INDIRECT is unsupported. The - VRING_DESC.Next field is always set, but the - host only interprets it dependent on - VRING_DESC_F_NEXT. - - @param [in] HeadIdx The index identifying the head buffer (first - buffer appended) belonging to this same - request. - - @param [in out] NextAvailIdx On input, the index identifying the next - descriptor available to carry the buffer. On - output, incremented by one, modulo 2^16. - -**/ - -STATIC -VOID -EFIAPI -AppendDesc ( - IN OUT VRING *Ring, - IN UINTN BufferPhysAddr, - IN UINT32 BufferSize, - IN UINT16 Flags, - IN UINT16 HeadIdx, - IN OUT UINT16 *NextAvailIdx - ) -{ - volatile VRING_DESC *Desc; - - Desc = &Ring->Desc[*NextAvailIdx % Ring->QueueSize]; - Desc->Addr = BufferPhysAddr; - Desc->Len = BufferSize; - Desc->Flags = Flags; - Ring->Avail.Ring[(*NextAvailIdx)++ % Ring->QueueSize] = - HeadIdx % Ring->QueueSize; - Desc->Next = *NextAvailIdx % Ring->QueueSize; -} /** @@ -423,7 +229,7 @@ AppendDesc ( @retval EFI_SUCCESS Transfer complete. - @retval EFI_DEVICE_ERROR Failed to notify host side via PCI write, or + @retval EFI_DEVICE_ERROR Failed to notify host side via VirtIo write, or unable to parse host response, or host response is not VIRTIO_BLK_S_OK. @@ -443,9 +249,7 @@ SynchronousRequest ( UINT32 BlockSize; volatile VIRTIO_BLK_REQ Request; volatile UINT8 HostStatus; - UINT16 FirstAvailIdx; - UINT16 NextAvailIdx; - UINTN PollPeriodUsecs; + DESC_INDICES Indices; BlockSize = Dev->BlockIoMedia.BlockSize; @@ -468,13 +272,9 @@ SynchronousRequest ( (BufferSize == 0 ? VIRTIO_BLK_T_FLUSH : VIRTIO_BLK_T_OUT) : VIRTIO_BLK_T_IN; Request.IoPrio = 0; - Request.Sector = Lba * (BlockSize / 512); + Request.Sector = MultU64x32(Lba, BlockSize / 512); - // - // Prepare for virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device. - // We're going to poll the answer, the host should not send an interrupt. - // - *Dev->Ring.Avail.Flags = (UINT16) VRING_AVAIL_F_NO_INTERRUPT; + VirtioPrepare (&Dev->Ring, &Indices); // // preset a host status for ourselves that we do not accept as success @@ -487,17 +287,11 @@ SynchronousRequest ( // ASSERT (Dev->Ring.QueueSize >= 3); - // - // Implement virtio-0.9.5, 2.4.1 Supplying Buffers to the Device. - // - FirstAvailIdx = *Dev->Ring.Avail.Idx; - NextAvailIdx = FirstAvailIdx; - // // virtio-blk header in first desc // - AppendDesc (&Dev->Ring, (UINTN) &Request, sizeof Request, VRING_DESC_F_NEXT, - FirstAvailIdx, &NextAvailIdx); + VirtioAppendDesc (&Dev->Ring, (UINTN) &Request, sizeof Request, + VRING_DESC_F_NEXT, &Indices); // // data buffer for read/write in second desc @@ -516,52 +310,23 @@ SynchronousRequest ( // // VRING_DESC_F_WRITE is interpreted from the host's point of view. // - AppendDesc (&Dev->Ring, (UINTN) Buffer, (UINT32) BufferSize, + VirtioAppendDesc (&Dev->Ring, (UINTN) Buffer, (UINT32) BufferSize, VRING_DESC_F_NEXT | (RequestIsWrite ? 0 : VRING_DESC_F_WRITE), - FirstAvailIdx, &NextAvailIdx); + &Indices); } // // host status in last (second or third) desc // - AppendDesc (&Dev->Ring, (UINTN) &HostStatus, sizeof HostStatus, - VRING_DESC_F_WRITE, FirstAvailIdx, &NextAvailIdx); - - // - // virtio-0.9.5, 2.4.1.3 Updating the Index Field - // - MemoryFence(); - *Dev->Ring.Avail.Idx = NextAvailIdx; - - // - // virtio-0.9.5, 2.4.1.4 Notifying the Device -- gratuitous notifications are - // OK. virtio-blk's only virtqueue is #0, called "requestq" (see Appendix D). - // - MemoryFence(); - if (EFI_ERROR (VIRTIO_CFG_WRITE (Dev, Generic.VhdrQueueNotify, 0))) { - return EFI_DEVICE_ERROR; - } + VirtioAppendDesc (&Dev->Ring, (UINTN) &HostStatus, sizeof HostStatus, + VRING_DESC_F_WRITE, &Indices); // - // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device - // Wait until the host processes and acknowledges our 3-part descriptor - // chain. The condition we use for polling is greatly simplified and relies - // on synchronous, the lock-step progress. + // virtio-blk's only virtqueue is #0, called "requestq" (see Appendix D). // - // Keep slowing down until we reach a poll period of slightly above 1 ms. - // - PollPeriodUsecs = 1; - MemoryFence(); - while (*Dev->Ring.Used.Idx != NextAvailIdx) { - gBS->Stall (PollPeriodUsecs); // calls AcpiTimerLib::MicroSecondDelay - - if (PollPeriodUsecs < 1024) { - PollPeriodUsecs *= 2; - } - MemoryFence(); - } - - if (HostStatus == VIRTIO_BLK_S_OK) { + if (VirtioFlush (Dev->VirtIo, 0, &Dev->Ring, &Indices, + NULL) == EFI_SUCCESS && + HostStatus == VIRTIO_BLK_S_OK) { return EFI_SUCCESS; } @@ -736,11 +501,6 @@ VirtioBlkFlushBlocks ( underlying device - 9 Driver Binding Protocol -- for exporting ourselves - Specs relevant in the specific sense: - - UEFI Spec 2.3.1 + Errata C, 13.4 EFI PCI I/O Protocol - - Driver Writer's Guide for UEFI 2.3.1 v1.01, 18 PCI Driver Design - Guidelines, 18.3 PCI drivers. - @param[in] This The EFI_DRIVER_BINDING_PROTOCOL object incorporating this driver (independently of any device). @@ -752,11 +512,11 @@ VirtioBlkFlushBlocks ( @retval EFI_SUCCESS The driver supports the device being probed. - @retval EFI_UNSUPPORTED Based on virtio-blk PCI discovery, we do not support + @retval EFI_UNSUPPORTED Based on virtio-blk discovery, we do not support the device. @return Error codes from the OpenProtocol() boot service or - the PciIo protocol. + the VirtIo protocol. **/ @@ -769,191 +529,49 @@ VirtioBlkDriverBindingSupported ( ) { EFI_STATUS Status; - EFI_PCI_IO_PROTOCOL *PciIo; - PCI_TYPE00 Pci; + VIRTIO_DEVICE_PROTOCOL *VirtIo; // - // Attempt to open the device with the PciIo set of interfaces. On success, - // the protocol is "instantiated" for the PCI device. Covers duplicate open - // attempts (EFI_ALREADY_STARTED). + // Attempt to open the device with the VirtIo set of interfaces. On success, + // the protocol is "instantiated" for the VirtIo device. Covers duplicate + // open attempts (EFI_ALREADY_STARTED). // Status = gBS->OpenProtocol ( DeviceHandle, // candidate device - &gEfiPciIoProtocolGuid, // for generic PCI access - (VOID **)&PciIo, // handle to instantiate + &gVirtioDeviceProtocolGuid, // for generic VirtIo access + (VOID **)&VirtIo, // handle to instantiate This->DriverBindingHandle, // requestor driver identity DeviceHandle, // ControllerHandle, according to // the UEFI Driver Model - EFI_OPEN_PROTOCOL_BY_DRIVER // get exclusive PciIo access to + EFI_OPEN_PROTOCOL_BY_DRIVER // get exclusive VirtIo access to // the device; to be released ); if (EFI_ERROR (Status)) { return Status; } - // - // Read entire PCI configuration header for more extensive check ahead. - // - Status = PciIo->Pci.Read ( - PciIo, // (protocol, device) - // handle - EfiPciIoWidthUint32, // access width & copy - // mode - 0, // Offset - sizeof Pci / sizeof (UINT32), // Count - &Pci // target buffer - ); - - if (Status == EFI_SUCCESS) { - // - // virtio-0.9.5, 2.1 PCI Discovery - // - Status = (Pci.Hdr.VendorId == 0x1AF4 && - Pci.Hdr.DeviceId >= 0x1000 && Pci.Hdr.DeviceId <= 0x103F && - Pci.Hdr.RevisionID == 0x00 && - Pci.Device.SubsystemID == 0x02) ? EFI_SUCCESS : EFI_UNSUPPORTED; + if (VirtIo->SubSystemDeviceId != VIRTIO_SUBSYSTEM_BLOCK_DEVICE) { + Status = EFI_UNSUPPORTED; } // - // We needed PCI IO access only transitorily, to see whether we support the + // We needed VirtIo access only transitorily, to see whether we support the // device or not. // - gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid, + gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid, This->DriverBindingHandle, DeviceHandle); return Status; } -/** - - Configure a virtio ring. - - This function sets up internal storage (the guest-host communication area) - and lays out several "navigation" (ie. no-ownership) pointers to parts of - that storage. - - Relevant sections from the virtio-0.9.5 spec: - - 1.1 Virtqueues, - - 2.3 Virtqueue Configuration. - - @param[in] The number of descriptors to allocate for the - virtio ring, as requested by the host. - - @param[out] Ring The virtio ring to set up. - - @retval EFI_OUT_OF_RESOURCES AllocatePages() failed to allocate contiguous - pages for the requested QueueSize. Fields of - Ring have indeterminate value. - - @retval EFI_SUCCESS Allocation and setup successful. Ring->Base - (and nothing else) is responsible for - deallocation. - -**/ - -STATIC -EFI_STATUS -EFIAPI -VirtioRingInit ( - IN UINT16 QueueSize, - OUT VRING *Ring - ) -{ - UINTN RingSize; - volatile UINT8 *RingPagesPtr; - - RingSize = ALIGN_VALUE ( - sizeof *Ring->Desc * QueueSize + - sizeof *Ring->Avail.Flags + - sizeof *Ring->Avail.Idx + - sizeof *Ring->Avail.Ring * QueueSize + - sizeof *Ring->Avail.UsedEvent, - EFI_PAGE_SIZE); - - RingSize += ALIGN_VALUE ( - sizeof *Ring->Used.Flags + - sizeof *Ring->Used.Idx + - sizeof *Ring->Used.UsedElem * QueueSize + - sizeof *Ring->Used.AvailEvent, - EFI_PAGE_SIZE); - - Ring->NumPages = EFI_SIZE_TO_PAGES (RingSize); - Ring->Base = AllocatePages (Ring->NumPages); - if (Ring->Base == NULL) { - return EFI_OUT_OF_RESOURCES; - } - SetMem (Ring->Base, RingSize, 0x00); - RingPagesPtr = Ring->Base; - - Ring->Desc = (volatile VOID *) RingPagesPtr; - RingPagesPtr += sizeof *Ring->Desc * QueueSize; - - Ring->Avail.Flags = (volatile VOID *) RingPagesPtr; - RingPagesPtr += sizeof *Ring->Avail.Flags; - - Ring->Avail.Idx = (volatile VOID *) RingPagesPtr; - RingPagesPtr += sizeof *Ring->Avail.Idx; - - Ring->Avail.Ring = (volatile VOID *) RingPagesPtr; - RingPagesPtr += sizeof *Ring->Avail.Ring * QueueSize; - - Ring->Avail.UsedEvent = (volatile VOID *) RingPagesPtr; - RingPagesPtr += sizeof *Ring->Avail.UsedEvent; - - RingPagesPtr = (volatile UINT8 *) Ring->Base + - ALIGN_VALUE (RingPagesPtr - (volatile UINT8 *) Ring->Base, - EFI_PAGE_SIZE); - - Ring->Used.Flags = (volatile VOID *) RingPagesPtr; - RingPagesPtr += sizeof *Ring->Used.Flags; - - Ring->Used.Idx = (volatile VOID *) RingPagesPtr; - RingPagesPtr += sizeof *Ring->Used.Idx; - - Ring->Used.UsedElem = (volatile VOID *) RingPagesPtr; - RingPagesPtr += sizeof *Ring->Used.UsedElem * QueueSize; - - Ring->Used.AvailEvent = (volatile VOID *) RingPagesPtr; - RingPagesPtr += sizeof *Ring->Used.AvailEvent; - - Ring->QueueSize = QueueSize; - return EFI_SUCCESS; -} - - -/** - - Tear down the internal resources of a configured virtio ring. - - The caller is responsible to stop the host from using this ring before - invoking this function: the VSTAT_DRIVER_OK bit must be clear in - VhdrDeviceStatus. - - @param[out] Ring The virtio ring to clean up. - -**/ - - -STATIC -VOID -EFIAPI -VirtioRingUninit ( - IN OUT VRING *Ring - ) -{ - FreePages (Ring->Base, Ring->NumPages); - SetMem (Ring, sizeof *Ring, 0x00); -} - - /** Set up all BlockIo and virtio-blk aspects of this driver for the specified device. @param[in out] Dev The driver instance to configure. The caller is - responsible for Dev->PciIo's validity (ie. working IO - access to the underlying virtio-blk PCI device). + responsible for Dev->VirtIo's validity (ie. working IO + access to the underlying virtio-blk device). @retval EFI_SUCCESS Setup complete. @@ -975,28 +593,43 @@ VirtioBlkInit ( UINT8 NextDevStat; EFI_STATUS Status; - UINT32 Features; + UINT64 Features; UINT64 NumSectors; UINT32 BlockSize; + UINT8 PhysicalBlockExp; + UINT8 AlignmentOffset; + UINT32 OptIoSize; UINT16 QueueSize; + PhysicalBlockExp = 0; + AlignmentOffset = 0; + OptIoSize = 0; + // // Execute virtio-0.9.5, 2.2.1 Device Initialization Sequence. // NextDevStat = 0; // step 1 -- reset device - Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, NextDevStat); + Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); if (EFI_ERROR (Status)) { goto Failed; } NextDevStat |= VSTAT_ACK; // step 2 -- acknowledge device presence - Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, NextDevStat); + Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); if (EFI_ERROR (Status)) { goto Failed; } NextDevStat |= VSTAT_DRIVER; // step 3 -- we know how to drive it - Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, NextDevStat); + Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); + if (EFI_ERROR (Status)) { + goto Failed; + } + + // + // Set Page Size - MMIO VirtIo Specific + // + Status = Dev->VirtIo->SetPageSize (Dev->VirtIo, EFI_PAGE_SIZE); if (EFI_ERROR (Status)) { goto Failed; } @@ -1004,11 +637,12 @@ VirtioBlkInit ( // // step 4a -- retrieve and validate features // - Status = VIRTIO_CFG_READ (Dev, Generic.VhdrDeviceFeatureBits, &Features); + Status = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo, &Features); if (EFI_ERROR (Status)) { goto Failed; } - Status = VIRTIO_CFG_READ (Dev, VhdrCapacity, &NumSectors); + + Status = VIRTIO_CFG_READ (Dev, Capacity, &NumSectors); if (EFI_ERROR (Status)) { goto Failed; } @@ -1018,12 +652,12 @@ VirtioBlkInit ( } if (Features & VIRTIO_BLK_F_BLK_SIZE) { - Status = VIRTIO_CFG_READ (Dev, VhdrBlkSize, &BlockSize); + Status = VIRTIO_CFG_READ (Dev, BlkSize, &BlockSize); if (EFI_ERROR (Status)) { goto Failed; } if (BlockSize == 0 || BlockSize % 512 != 0 || - NumSectors % (BlockSize / 512) != 0) { + ModU64x32 (NumSectors, BlockSize / 512) != 0) { // // We can only handle a logical block consisting of whole sectors, // and only a disk composed of whole logical blocks. @@ -1036,14 +670,50 @@ VirtioBlkInit ( BlockSize = 512; } + if (Features & VIRTIO_BLK_F_TOPOLOGY) { + Status = VIRTIO_CFG_READ (Dev, Topology.PhysicalBlockExp, + &PhysicalBlockExp); + if (EFI_ERROR (Status)) { + goto Failed; + } + if (PhysicalBlockExp >= 32) { + Status = EFI_UNSUPPORTED; + goto Failed; + } + + Status = VIRTIO_CFG_READ (Dev, Topology.AlignmentOffset, &AlignmentOffset); + if (EFI_ERROR (Status)) { + goto Failed; + } + + Status = VIRTIO_CFG_READ (Dev, Topology.OptIoSize, &OptIoSize); + if (EFI_ERROR (Status)) { + goto Failed; + } + } + + Features &= VIRTIO_BLK_F_BLK_SIZE | VIRTIO_BLK_F_TOPOLOGY | VIRTIO_BLK_F_RO | + VIRTIO_BLK_F_FLUSH | VIRTIO_F_VERSION_1; + + // + // In virtio-1.0, feature negotiation is expected to complete before queue + // discovery, and the device can also reject the selected set of features. + // + if (Dev->VirtIo->Revision >= VIRTIO_SPEC_REVISION (1, 0, 0)) { + Status = Virtio10WriteFeatures (Dev->VirtIo, Features, &NextDevStat); + if (EFI_ERROR (Status)) { + goto Failed; + } + } + // // step 4b -- allocate virtqueue // - Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrQueueSelect, 0); + Status = Dev->VirtIo->SetQueueSel (Dev->VirtIo, 0); if (EFI_ERROR (Status)) { goto Failed; } - Status = VIRTIO_CFG_READ (Dev, Generic.VhdrQueueSize, &QueueSize); + Status = Dev->VirtIo->GetQueueNumMax (Dev->VirtIo, &QueueSize); if (EFI_ERROR (Status)) { goto Failed; } @@ -1058,39 +728,51 @@ VirtioBlkInit ( } // - // step 4c -- Report GPFN (guest-physical frame number) of queue. If anything - // fails from here on, we must release the ring resources. + // Additional steps for MMIO: align the queue appropriately, and set the + // size. If anything fails from here on, we must release the ring resources. // - Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrQueueAddress, - (UINTN) Dev->Ring.Base >> EFI_PAGE_SHIFT); + Status = Dev->VirtIo->SetQueueNum (Dev->VirtIo, QueueSize); + if (EFI_ERROR (Status)) { + goto ReleaseQueue; + } + + Status = Dev->VirtIo->SetQueueAlign (Dev->VirtIo, EFI_PAGE_SIZE); if (EFI_ERROR (Status)) { goto ReleaseQueue; } // - // step 5 -- Report understood features. There are no virtio-blk specific - // features to negotiate in virtio-0.9.5, plus we do not want any of the - // device-independent (known or unknown) VIRTIO_F_* capabilities (see - // Appendix B). + // step 4c -- Report GPFN (guest-physical frame number) of queue. // - Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrGuestFeatureBits, 0); + Status = Dev->VirtIo->SetQueueAddress (Dev->VirtIo, &Dev->Ring); if (EFI_ERROR (Status)) { goto ReleaseQueue; } + + // + // step 5 -- Report understood features. + // + if (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) { + Features &= ~(UINT64)VIRTIO_F_VERSION_1; + Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo, Features); + if (EFI_ERROR (Status)) { + goto ReleaseQueue; + } + } + // // step 6 -- initialization complete // NextDevStat |= VSTAT_DRIVER_OK; - Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, NextDevStat); + Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); if (EFI_ERROR (Status)) { goto ReleaseQueue; } // - // Populate the exported interface's attributes; see UEFI spec v2.3.1 + - // Errata C, 12.8 EFI Block I/O Protocol. We stick to the lowest possible - // EFI_BLOCK_IO_PROTOCOL revision for now. + // Populate the exported interface's attributes; see UEFI spec v2.4, 12.9 EFI + // Block I/O Protocol. // Dev->BlockIo.Revision = 0; Dev->BlockIo.Media = &Dev->BlockIoMedia; @@ -1102,11 +784,30 @@ VirtioBlkInit ( Dev->BlockIoMedia.RemovableMedia = FALSE; Dev->BlockIoMedia.MediaPresent = TRUE; Dev->BlockIoMedia.LogicalPartition = FALSE; - Dev->BlockIoMedia.ReadOnly = !!(Features & VIRTIO_BLK_F_RO); - Dev->BlockIoMedia.WriteCaching = !!(Features & VIRTIO_BLK_F_FLUSH); + Dev->BlockIoMedia.ReadOnly = (BOOLEAN) ((Features & VIRTIO_BLK_F_RO) != 0); + Dev->BlockIoMedia.WriteCaching = (BOOLEAN) ((Features & VIRTIO_BLK_F_FLUSH) != 0); Dev->BlockIoMedia.BlockSize = BlockSize; Dev->BlockIoMedia.IoAlign = 0; - Dev->BlockIoMedia.LastBlock = NumSectors / (BlockSize / 512) - 1; + Dev->BlockIoMedia.LastBlock = DivU64x32 (NumSectors, + BlockSize / 512) - 1; + + DEBUG ((DEBUG_INFO, "%a: LbaSize=0x%x[B] NumBlocks=0x%Lx[Lba]\n", + __FUNCTION__, Dev->BlockIoMedia.BlockSize, + Dev->BlockIoMedia.LastBlock + 1)); + + if (Features & VIRTIO_BLK_F_TOPOLOGY) { + Dev->BlockIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION3; + + Dev->BlockIoMedia.LowestAlignedLba = AlignmentOffset; + Dev->BlockIoMedia.LogicalBlocksPerPhysicalBlock = 1u << PhysicalBlockExp; + Dev->BlockIoMedia.OptimalTransferLengthGranularity = OptIoSize; + + DEBUG ((DEBUG_INFO, "%a: FirstAligned=0x%Lx[Lba] PhysBlkSize=0x%x[Lba]\n", + __FUNCTION__, Dev->BlockIoMedia.LowestAlignedLba, + Dev->BlockIoMedia.LogicalBlocksPerPhysicalBlock)); + DEBUG ((DEBUG_INFO, "%a: OptimalTransferLengthGranularity=0x%x[Lba]\n", + __FUNCTION__, Dev->BlockIoMedia.OptimalTransferLengthGranularity)); + } return EFI_SUCCESS; ReleaseQueue: @@ -1115,10 +816,10 @@ ReleaseQueue: Failed: // // Notify the host about our failure to setup: virtio-0.9.5, 2.2.2.1 Device - // Status. PCI IO access failure here should not mask the original error. + // Status. VirtIo access failure here should not mask the original error. // NextDevStat |= VSTAT_FAILED; - VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, NextDevStat); + Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); return Status; // reached only via Failed above } @@ -1145,7 +846,7 @@ VirtioBlkUninit ( // VIRTIO_CFG_WRITE() returns, the host will have learned to stay away from // the old comms area. // - VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, 0); + Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0); VirtioRingUninit (&Dev->Ring); @@ -1154,6 +855,37 @@ VirtioBlkUninit ( } +/** + + Event notification function enqueued by ExitBootServices(). + + @param[in] Event Event whose notification function is being invoked. + + @param[in] Context Pointer to the VBLK_DEV structure. + +**/ + +STATIC +VOID +EFIAPI +VirtioBlkExitBoot ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + VBLK_DEV *Dev; + + // + // Reset the device. This causes the hypervisor to forget about the virtio + // ring. + // + // We allocated said ring in EfiBootServicesData type memory, and code + // executing after ExitBootServices() is permitted to overwrite it. + // + Dev = Context; + Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0); +} + /** After we've pronounced support for a specific device in @@ -1172,13 +904,13 @@ VirtioBlkUninit ( @retval EFI_SUCCESS Driver instance has been created and - initialized for the virtio-blk PCI device, it + initialized for the virtio-blk device, it is now accessibla via EFI_BLOCK_IO_PROTOCOL. @retval EFI_OUT_OF_RESOURCES Memory allocation failed. @return Error codes from the OpenProtocol() boot - service, the PciIo protocol, VirtioBlkInit(), + service, the VirtIo protocol, VirtioBlkInit(), or the InstallProtocolInterface() boot service. **/ @@ -1199,43 +931,25 @@ VirtioBlkDriverBindingStart ( return EFI_OUT_OF_RESOURCES; } - Status = gBS->OpenProtocol (DeviceHandle, &gEfiPciIoProtocolGuid, - (VOID **)&Dev->PciIo, This->DriverBindingHandle, + Status = gBS->OpenProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid, + (VOID **)&Dev->VirtIo, This->DriverBindingHandle, DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER); if (EFI_ERROR (Status)) { goto FreeVirtioBlk; } // - // We must retain and ultimately restore the original PCI attributes of the - // device. See Driver Writer's Guide for UEFI 2.3.1 v1.01, 18.3 PCI drivers / - // 18.3.2 Start() and Stop(). - // - // The third parameter ("Attributes", input) is ignored by the Get operation. - // The fourth parameter ("Result", output) is ignored by the Enable and Set - // operations. + // VirtIo access granted, configure virtio-blk device. // - // For virtio-blk we only need IO space access. - // - Status = Dev->PciIo->Attributes (Dev->PciIo, EfiPciIoAttributeOperationGet, - 0, &Dev->OriginalPciAttributes); - if (EFI_ERROR (Status)) { - goto ClosePciIo; - } - - Status = Dev->PciIo->Attributes (Dev->PciIo, - EfiPciIoAttributeOperationEnable, - EFI_PCI_IO_ATTRIBUTE_IO, NULL); + Status = VirtioBlkInit (Dev); if (EFI_ERROR (Status)) { - goto ClosePciIo; + goto CloseVirtIo; } - // - // PCI IO access granted, configure virtio-blk device. - // - Status = VirtioBlkInit (Dev); + Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK, + &VirtioBlkExitBoot, Dev, &Dev->ExitBoot); if (EFI_ERROR (Status)) { - goto RestorePciAttributes; + goto UninitDev; } // @@ -1246,20 +960,19 @@ VirtioBlkDriverBindingStart ( &gEfiBlockIoProtocolGuid, EFI_NATIVE_INTERFACE, &Dev->BlockIo); if (EFI_ERROR (Status)) { - goto UninitDev; + goto CloseExitBoot; } return EFI_SUCCESS; +CloseExitBoot: + gBS->CloseEvent (Dev->ExitBoot); + UninitDev: VirtioBlkUninit (Dev); -RestorePciAttributes: - Dev->PciIo->Attributes (Dev->PciIo, EfiPciIoAttributeOperationSet, - Dev->OriginalPciAttributes, NULL); - -ClosePciIo: - gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid, +CloseVirtIo: + gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid, This->DriverBindingHandle, DeviceHandle); FreeVirtioBlk: @@ -1301,25 +1014,38 @@ VirtioBlkDriverBindingStop ( IN EFI_HANDLE *ChildHandleBuffer ) { - VBLK_DEV *Dev; - EFI_STATUS Status; + EFI_STATUS Status; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + VBLK_DEV *Dev; - Dev = VIRTIO_BLK_FROM_BLOCK_IO (This); + Status = gBS->OpenProtocol ( + DeviceHandle, // candidate device + &gEfiBlockIoProtocolGuid, // retrieve the BlockIo iface + (VOID **)&BlockIo, // target pointer + This->DriverBindingHandle, // requestor driver identity + DeviceHandle, // requesting lookup for dev. + EFI_OPEN_PROTOCOL_GET_PROTOCOL // lookup only, no ref. added + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Dev = VIRTIO_BLK_FROM_BLOCK_IO (BlockIo); // - // If DriverBindingStop() is called with the driver instance still in use, - // or any of the parameters are invalid, we've caught a bug. + // Handle Stop() requests for in-use driver instances gracefully. // Status = gBS->UninstallProtocolInterface (DeviceHandle, &gEfiBlockIoProtocolGuid, &Dev->BlockIo); - ASSERT (Status == EFI_SUCCESS); + if (EFI_ERROR (Status)) { + return Status; + } - VirtioBlkUninit (Dev); + gBS->CloseEvent (Dev->ExitBoot); - Dev->PciIo->Attributes (Dev->PciIo, EfiPciIoAttributeOperationSet, - Dev->OriginalPciAttributes, NULL); + VirtioBlkUninit (Dev); - gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid, + gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid, This->DriverBindingHandle, DeviceHandle); FreePool (Dev); @@ -1356,13 +1082,13 @@ STATIC EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = { // for unambiguous identification. // -STATIC GLOBAL_REMOVE_IF_UNREFERENCED +STATIC EFI_UNICODE_STRING_TABLE mDriverNameTable[] = { { "eng;en", L"Virtio Block Driver" }, { NULL, NULL } }; -STATIC GLOBAL_REMOVE_IF_UNREFERENCED +STATIC EFI_COMPONENT_NAME_PROTOCOL gComponentName; EFI_STATUS @@ -1395,14 +1121,14 @@ VirtioBlkGetDeviceName ( return EFI_UNSUPPORTED; } -STATIC GLOBAL_REMOVE_IF_UNREFERENCED +STATIC EFI_COMPONENT_NAME_PROTOCOL gComponentName = { &VirtioBlkGetDriverName, &VirtioBlkGetDeviceName, "eng" // SupportedLanguages, ISO 639-2 language codes }; -STATIC GLOBAL_REMOVE_IF_UNREFERENCED +STATIC EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = { (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &VirtioBlkGetDriverName, (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &VirtioBlkGetDeviceName,