X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=OvmfPkg%2FVirtioBlkDxe%2FVirtioBlk.c;h=75f85ca6e09ad01fdadf8bc3245596c8cac45873;hb=d967d6d96fc18d7a53d32223f5813b12cda19e8d;hp=1ac36cd17b486b7a6b00b7f6d870e40f564eb4d0;hpb=2f03f796a40769d7390d0a9e5da70424e795afdc;p=mirror_edk2.git diff --git a/OvmfPkg/VirtioBlkDxe/VirtioBlk.c b/OvmfPkg/VirtioBlkDxe/VirtioBlk.c index 1ac36cd17b..75f85ca6e0 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,7 +23,6 @@ **/ -#include #include #include #include @@ -36,14 +36,14 @@ /** 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. @@ -56,23 +56,24 @@ 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_VBLK (Field), \ - SIZE_OF_VBLK (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_VBLK (Field), \ - SIZE_OF_VBLK (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) \ )) @@ -228,7 +229,7 @@ VerifyReadWriteRequest ( @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. @@ -271,7 +272,7 @@ 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); VirtioPrepare (&Dev->Ring, &Indices); @@ -323,7 +324,7 @@ SynchronousRequest ( // // virtio-blk's only virtqueue is #0, called "requestq" (see Appendix D). // - if (VirtioFlush (Dev->PciIo, 0, &Dev->Ring, &Indices) == EFI_SUCCESS && + if (VirtioFlush (Dev->VirtIo, 0, &Dev->Ring, &Indices) == EFI_SUCCESS && HostStatus == VIRTIO_BLK_S_OK) { return EFI_SUCCESS; } @@ -499,11 +500,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). @@ -515,11 +511,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. **/ @@ -532,56 +528,36 @@ 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; } @@ -593,8 +569,8 @@ VirtioBlkDriverBindingSupported ( 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. @@ -619,25 +595,40 @@ VirtioBlkInit ( UINT32 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; } @@ -645,11 +636,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; } @@ -659,7 +651,7 @@ 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; } @@ -677,14 +669,36 @@ 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; + } + } + // // 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; } @@ -699,22 +713,36 @@ 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 4c -- Report GPFN (guest-physical frame number) of queue. + // + Status = Dev->VirtIo->SetQueueAddress (Dev->VirtIo, + (UINT32) ((UINTN) Dev->Ring.Base >> EFI_PAGE_SHIFT)); + 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). // - Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrGuestFeatureBits, 0); + Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo, 0); if (EFI_ERROR (Status)) { goto ReleaseQueue; } @@ -723,15 +751,14 @@ VirtioBlkInit ( // 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; @@ -743,12 +770,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 = 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: @@ -757,10 +802,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 } @@ -787,7 +832,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); @@ -796,6 +841,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 @@ -814,13 +890,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. **/ @@ -841,43 +917,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. - // - // For virtio-blk we only need IO space access. + // VirtIo access granted, configure virtio-blk device. // - 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; } // @@ -888,20 +946,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: @@ -943,25 +1000,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);