X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;ds=sidebyside;f=OvmfPkg%2FVirtioScsiDxe%2FVirtioScsi.c;h=1a68f062106c00132f1c9a32002fb52f1cee3820;hb=5e2e5647b9fba569b7ba5ede0a77d06ae3c16504;hp=66f6d31d74b3be684d06c6cc843941d80d0b25b8;hpb=37078a63b1911f8b320bab6d82a2183a84f8858c;p=mirror_edk2.git diff --git a/OvmfPkg/VirtioScsiDxe/VirtioScsi.c b/OvmfPkg/VirtioScsiDxe/VirtioScsi.c index 66f6d31d74..1a68f06210 100644 --- a/OvmfPkg/VirtioScsiDxe/VirtioScsi.c +++ b/OvmfPkg/VirtioScsiDxe/VirtioScsi.c @@ -26,7 +26,8 @@ unreasonable for now. Copyright (C) 2012, Red Hat, Inc. - Copyright (c) 2012, Intel Corporation. All rights reserved.
+ Copyright (c) 2012 - 2014, Intel Corporation. All rights reserved.
+ Copyright (c) 2017, AMD Inc, 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 @@ -38,7 +39,6 @@ **/ -#include #include #include #include @@ -51,15 +51,14 @@ /** - Convenience macros to read and write region 0 IO space elements of the - virtio-scsi PCI device, for configuration purposes. + Convenience macros to read and write configuration elements of the + virtio-scsi VirtIo device. 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 VSCSI_DEV structure whose PCI IO space - we're accessing. Dev->PciIo must be valid. + @param[in] Dev Pointer to the VSCSI_DEV structure. @param[in] Field A field name from VSCSI_HDR, identifying the virtio-scsi configuration item to access. @@ -72,23 +71,23 @@ one of UINT8, UINT16, UINT32, UINT64. - @return Status codes returned by VirtioWrite() / VirtioRead(). + @return Status codes returned by Virtio->WriteDevice() / Virtio->ReadDevice(). **/ -#define VIRTIO_CFG_WRITE(Dev, Field, Value) (VirtioWrite ( \ - (Dev)->PciIo, \ - OFFSET_OF_VSCSI (Field), \ - SIZE_OF_VSCSI (Field), \ - (Value) \ +#define VIRTIO_CFG_WRITE(Dev, Field, Value) ((Dev)->VirtIo->WriteDevice ( \ + (Dev)->VirtIo, \ + OFFSET_OF_VSCSI (Field), \ + SIZE_OF_VSCSI (Field), \ + (Value) \ )) -#define VIRTIO_CFG_READ(Dev, Field, Pointer) (VirtioRead ( \ - (Dev)->PciIo, \ - OFFSET_OF_VSCSI (Field), \ - SIZE_OF_VSCSI (Field), \ - sizeof *(Pointer), \ - (Pointer) \ +#define VIRTIO_CFG_READ(Dev, Field, Pointer) ((Dev)->VirtIo->ReadDevice ( \ + (Dev)->VirtIo, \ + OFFSET_OF_VSCSI (Field), \ + SIZE_OF_VSCSI (Field), \ + sizeof *(Pointer), \ + (Pointer) \ )) @@ -98,7 +97,7 @@ // set some fields in the EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET in/out // parameter on return. The following is a full list of those fields, for // easier validation of PopulateRequest(), ParseResponse(), and -// VirtioScsiPassThru() below. +// ReportHostAdapterError() below. // // - InTransferLength // - OutTransferLength @@ -255,7 +254,7 @@ PopulateRequest ( // Request->Lun[0] = 1; Request->Lun[1] = (UINT8) Target; - Request->Lun[2] = (UINT8) ((Lun >> 8) | 0x40); + Request->Lun[2] = (UINT8) (((UINT32)Lun >> 8) | 0x40); Request->Lun[3] = (UINT8) Lun; // @@ -389,6 +388,37 @@ ParseResponse ( } +/** + + The function can be used to create a fake host adapter error. + + When VirtioScsiPassThru() is failed due to some reasons then this function + can be called to construct a host adapter error. + + @param[out] Packet The Extended SCSI Pass Thru Protocol packet that the host + adapter error shall be placed in. + + + @retval EFI_DEVICE_ERROR The function returns this status code + unconditionally, to be propagated by + VirtioScsiPassThru(). + +**/ +STATIC +EFI_STATUS +ReportHostAdapterError ( + OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) +{ + Packet->InTransferLength = 0; + Packet->OutTransferLength = 0; + Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER; + Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD; + Packet->SenseDataLength = 0; + return EFI_DEVICE_ERROR; +} + + // // The next seven functions implement EFI_EXT_SCSI_PASS_THRU_PROTOCOL // for the virtio-scsi HBA. Refer to UEFI Spec 2.3.1 + Errata C, sections @@ -410,37 +440,157 @@ VirtioScsiPassThru ( UINT16 TargetValue; EFI_STATUS Status; volatile VIRTIO_SCSI_REQ Request; - volatile VIRTIO_SCSI_RESP Response; + volatile VIRTIO_SCSI_RESP *Response; + VOID *ResponseBuffer; DESC_INDICES Indices; - - // - // Zero-initialization of Request & Response with "= { 0 };" doesn't build - // with gcc-4.4: "undefined reference to `memset'". Direct SetMem() is not - // allowed as it would cast away the volatile qualifier. Work it around. - // - union { - VIRTIO_SCSI_REQ Request; - VIRTIO_SCSI_RESP Response; - } Zero; - - SetMem (&Zero, sizeof Zero, 0x00); - Request = Zero.Request; - Response = Zero.Response; + VOID *RequestMapping; + VOID *ResponseMapping; + VOID *InDataMapping; + VOID *OutDataMapping; + EFI_PHYSICAL_ADDRESS RequestDeviceAddress; + EFI_PHYSICAL_ADDRESS ResponseDeviceAddress; + EFI_PHYSICAL_ADDRESS InDataDeviceAddress; + EFI_PHYSICAL_ADDRESS OutDataDeviceAddress; + VOID *InDataBuffer; + UINTN InDataNumPages; + BOOLEAN OutDataBufferIsMapped; + + ZeroMem ((VOID*) &Request, sizeof (Request)); Dev = VIRTIO_SCSI_FROM_PASS_THRU (This); CopyMem (&TargetValue, Target, sizeof TargetValue); + InDataBuffer = NULL; + OutDataBufferIsMapped = FALSE; + InDataNumPages = 0; + Status = PopulateRequest (Dev, TargetValue, Lun, Packet, &Request); if (EFI_ERROR (Status)) { return Status; } - VirtioPrepare (&Dev->Ring, &Indices); + // + // Map the virtio-scsi Request header buffer + // + Status = VirtioMapAllBytesInSharedBuffer ( + Dev->VirtIo, + VirtioOperationBusMasterRead, + (VOID *) &Request, + sizeof Request, + &RequestDeviceAddress, + &RequestMapping); + if (EFI_ERROR (Status)) { + return ReportHostAdapterError (Packet); + } + + // + // Map the input buffer + // + if (Packet->InTransferLength > 0) { + // + // Allocate a intermediate input buffer. This is mainly to handle the + // following case: + // * caller submits a bi-directional request + // * we perform the request fine + // * but we fail to unmap the "InDataMapping" + // + // In that case simply returing the EFI_DEVICE_ERROR is not sufficient. In + // addition to the error code we also need to update Packet fields + // accordingly so that we report the full loss of the incoming transfer. + // + // We allocate a temporary buffer and map it with BusMasterCommonBuffer. If + // the Virtio request is successful then we copy the data from temporary + // buffer into Packet->InDataBuffer. + // + InDataNumPages = EFI_SIZE_TO_PAGES ((UINTN)Packet->InTransferLength); + Status = Dev->VirtIo->AllocateSharedPages ( + Dev->VirtIo, + InDataNumPages, + &InDataBuffer + ); + if (EFI_ERROR (Status)) { + Status = ReportHostAdapterError (Packet); + goto UnmapRequestBuffer; + } + + ZeroMem (InDataBuffer, Packet->InTransferLength); + + Status = VirtioMapAllBytesInSharedBuffer ( + Dev->VirtIo, + VirtioOperationBusMasterCommonBuffer, + InDataBuffer, + Packet->InTransferLength, + &InDataDeviceAddress, + &InDataMapping + ); + if (EFI_ERROR (Status)) { + Status = ReportHostAdapterError (Packet); + goto FreeInDataBuffer; + } + } + + // + // Map the output buffer + // + if (Packet->OutTransferLength > 0) { + Status = VirtioMapAllBytesInSharedBuffer ( + Dev->VirtIo, + VirtioOperationBusMasterRead, + Packet->OutDataBuffer, + Packet->OutTransferLength, + &OutDataDeviceAddress, + &OutDataMapping + ); + if (EFI_ERROR (Status)) { + Status = ReportHostAdapterError (Packet); + goto UnmapInDataBuffer; + } + + OutDataBufferIsMapped = TRUE; + } + + // + // Response header is bi-direction (we preset with host status and expect + // the device to update it). Allocate a response buffer which can be mapped + // to access equally by both processor and device. + // + Status = Dev->VirtIo->AllocateSharedPages ( + Dev->VirtIo, + EFI_SIZE_TO_PAGES (sizeof *Response), + &ResponseBuffer + ); + if (EFI_ERROR (Status)) { + Status = ReportHostAdapterError (Packet); + goto UnmapOutDataBuffer; + } + + Response = ResponseBuffer; + + ZeroMem ((VOID *)Response, sizeof (*Response)); // // preset a host status for ourselves that we do not accept as success // - Response.Response = VIRTIO_SCSI_S_FAILURE; + Response->Response = VIRTIO_SCSI_S_FAILURE; + + // + // Map the response buffer with BusMasterCommonBuffer so that response + // buffer can be accessed by both host and device. + // + Status = VirtioMapAllBytesInSharedBuffer ( + Dev->VirtIo, + VirtioOperationBusMasterCommonBuffer, + ResponseBuffer, + sizeof (*Response), + &ResponseDeviceAddress, + &ResponseMapping + ); + if (EFI_ERROR (Status)) { + Status = ReportHostAdapterError (Packet); + goto FreeResponseBuffer; + } + + VirtioPrepare (&Dev->Ring, &Indices); // // ensured by VirtioScsiInit() -- this predicate, in combination with the @@ -451,48 +601,101 @@ VirtioScsiPassThru ( // // enqueue Request // - VirtioAppendDesc (&Dev->Ring, (UINTN) &Request, sizeof Request, - VRING_DESC_F_NEXT, &Indices); + VirtioAppendDesc ( + &Dev->Ring, + RequestDeviceAddress, + sizeof Request, + VRING_DESC_F_NEXT, + &Indices + ); // // enqueue "dataout" if any // if (Packet->OutTransferLength > 0) { - VirtioAppendDesc (&Dev->Ring, (UINTN) Packet->OutDataBuffer, - Packet->OutTransferLength, VRING_DESC_F_NEXT, &Indices); + VirtioAppendDesc ( + &Dev->Ring, + OutDataDeviceAddress, + Packet->OutTransferLength, + VRING_DESC_F_NEXT, + &Indices + ); } // // enqueue Response, to be written by the host // - VirtioAppendDesc (&Dev->Ring, (UINTN) &Response, sizeof Response, - VRING_DESC_F_WRITE | (Packet->InTransferLength > 0 ? - VRING_DESC_F_NEXT : 0), - &Indices); + VirtioAppendDesc ( + &Dev->Ring, + ResponseDeviceAddress, + sizeof *Response, + VRING_DESC_F_WRITE | (Packet->InTransferLength > 0 ? VRING_DESC_F_NEXT : 0), + &Indices + ); // // enqueue "datain" if any, to be written by the host // if (Packet->InTransferLength > 0) { - VirtioAppendDesc (&Dev->Ring, (UINTN) Packet->InDataBuffer, - Packet->InTransferLength, VRING_DESC_F_WRITE, &Indices); + VirtioAppendDesc ( + &Dev->Ring, + InDataDeviceAddress, + Packet->InTransferLength, + VRING_DESC_F_WRITE, + &Indices + ); } // If kicking the host fails, we must fake a host adapter error. // EFI_NOT_READY would save us the effort, but it would also suggest that the // caller retry. // - if (VirtioFlush (Dev->PciIo, VIRTIO_SCSI_REQUEST_QUEUE, &Dev->Ring, - &Indices) != EFI_SUCCESS) { - Packet->InTransferLength = 0; - Packet->OutTransferLength = 0; - Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER; - Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD; - Packet->SenseDataLength = 0; - return EFI_DEVICE_ERROR; + if (VirtioFlush (Dev->VirtIo, VIRTIO_SCSI_REQUEST_QUEUE, &Dev->Ring, + &Indices, NULL) != EFI_SUCCESS) { + Status = ReportHostAdapterError (Packet); + goto UnmapResponseBuffer; + } + + Status = ParseResponse (Packet, Response); + + // + // If virtio request was successful and it was a CPU read request then we + // have used an intermediate buffer. Copy the data from intermediate buffer + // to the final buffer. + // + if (InDataBuffer != NULL) { + CopyMem (Packet->InDataBuffer, InDataBuffer, Packet->InTransferLength); + } + +UnmapResponseBuffer: + Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, ResponseMapping); + +FreeResponseBuffer: + Dev->VirtIo->FreeSharedPages ( + Dev->VirtIo, + EFI_SIZE_TO_PAGES (sizeof *Response), + ResponseBuffer + ); + +UnmapOutDataBuffer: + if (OutDataBufferIsMapped) { + Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, OutDataMapping); + } + +UnmapInDataBuffer: + if (InDataBuffer != NULL) { + Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, InDataMapping); + } + +FreeInDataBuffer: + if (InDataBuffer != NULL) { + Dev->VirtIo->FreeSharedPages (Dev->VirtIo, InDataNumPages, InDataBuffer); } - return ParseResponse (Packet, &Response); +UnmapRequestBuffer: + Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, RequestMapping); + + return Status; } @@ -719,8 +922,8 @@ VirtioScsiInit ( { UINT8 NextDevStat; EFI_STATUS Status; - - UINT32 Features; + UINT64 RingBaseShift; + UINT64 Features; UINT16 MaxChannel; // for validation only UINT32 NumQueues; // for validation only UINT16 QueueSize; @@ -729,19 +932,27 @@ VirtioScsiInit ( // 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; } @@ -749,13 +960,13 @@ VirtioScsiInit ( // // 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; } - Dev->InOutSupported = !!(Features & VIRTIO_SCSI_F_INOUT); + Dev->InOutSupported = (BOOLEAN) ((Features & VIRTIO_SCSI_F_INOUT) != 0); - Status = VIRTIO_CFG_READ (Dev, VhdrMaxChannel, &MaxChannel); + Status = VIRTIO_CFG_READ (Dev, MaxChannel, &MaxChannel); if (EFI_ERROR (Status)) { goto Failed; } @@ -767,7 +978,7 @@ VirtioScsiInit ( goto Failed; } - Status = VIRTIO_CFG_READ (Dev, VhdrNumQueues, &NumQueues); + Status = VIRTIO_CFG_READ (Dev, NumQueues, &NumQueues); if (EFI_ERROR (Status)) { goto Failed; } @@ -776,7 +987,7 @@ VirtioScsiInit ( goto Failed; } - Status = VIRTIO_CFG_READ (Dev, VhdrMaxTarget, &Dev->MaxTarget); + Status = VIRTIO_CFG_READ (Dev, MaxTarget, &Dev->MaxTarget); if (EFI_ERROR (Status)) { goto Failed; } @@ -784,7 +995,7 @@ VirtioScsiInit ( Dev->MaxTarget = PcdGet16 (PcdVirtioScsiMaxTargetLimit); } - Status = VIRTIO_CFG_READ (Dev, VhdrMaxLun, &Dev->MaxLun); + Status = VIRTIO_CFG_READ (Dev, MaxLun, &Dev->MaxLun); if (EFI_ERROR (Status)) { goto Failed; } @@ -792,7 +1003,7 @@ VirtioScsiInit ( Dev->MaxLun = PcdGet32 (PcdVirtioScsiMaxLunLimit); } - Status = VIRTIO_CFG_READ (Dev, VhdrMaxSectors, &Dev->MaxSectors); + Status = VIRTIO_CFG_READ (Dev, MaxSectors, &Dev->MaxSectors); if (EFI_ERROR (Status)) { goto Failed; } @@ -805,15 +1016,28 @@ VirtioScsiInit ( goto Failed; } + Features &= VIRTIO_SCSI_F_INOUT | VIRTIO_F_VERSION_1 | + VIRTIO_F_IOMMU_PLATFORM; + + // + // 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 request virtqueue // - Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrQueueSelect, - VIRTIO_SCSI_REQUEST_QUEUE); + Status = Dev->VirtIo->SetQueueSel (Dev->VirtIo, VIRTIO_SCSI_REQUEST_QUEUE); 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; } @@ -825,52 +1049,81 @@ VirtioScsiInit ( goto Failed; } - Status = VirtioRingInit (QueueSize, &Dev->Ring); + Status = VirtioRingInit (Dev->VirtIo, QueueSize, &Dev->Ring); if (EFI_ERROR (Status)) { goto Failed; } // - // step 4c -- Report GPFN (guest-physical frame number) of queue. If anything - // fails from here on, we must release the ring resources. + // 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 = VirtioRingMap ( + Dev->VirtIo, + &Dev->Ring, + &RingBaseShift, + &Dev->RingMap + ); if (EFI_ERROR (Status)) { goto ReleaseQueue; } // - // step 5 -- Report understood features and guest-tuneables. We want none of - // the known (or unknown) VIRTIO_SCSI_F_* or VIRTIO_F_* capabilities (see - // virtio-0.9.5, Appendices B and I), except bidirectional transfers. + // Additional steps for MMIO: align the queue appropriately, and set the + // size. If anything fails from here on, we must unmap the ring resources. // - Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrGuestFeatureBits, - Features & VIRTIO_SCSI_F_INOUT); + Status = Dev->VirtIo->SetQueueNum (Dev->VirtIo, QueueSize); if (EFI_ERROR (Status)) { - goto ReleaseQueue; + goto UnmapQueue; + } + + Status = Dev->VirtIo->SetQueueAlign (Dev->VirtIo, EFI_PAGE_SIZE); + if (EFI_ERROR (Status)) { + goto UnmapQueue; + } + + // + // step 4c -- Report GPFN (guest-physical frame number) of queue. + // + Status = Dev->VirtIo->SetQueueAddress ( + Dev->VirtIo, + &Dev->Ring, + RingBaseShift + ); + if (EFI_ERROR (Status)) { + goto UnmapQueue; + } + + // + // step 5 -- Report understood features and guest-tuneables. + // + if (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) { + Features &= ~(UINT64)(VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM); + Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo, Features); + if (EFI_ERROR (Status)) { + goto UnmapQueue; + } } // // We expect these maximum sizes from the host. Since they are // guest-negotiable, ask for them rather than just checking them. // - Status = VIRTIO_CFG_WRITE (Dev, VhdrCdbSize, VIRTIO_SCSI_CDB_SIZE); + Status = VIRTIO_CFG_WRITE (Dev, CdbSize, VIRTIO_SCSI_CDB_SIZE); if (EFI_ERROR (Status)) { - goto ReleaseQueue; + goto UnmapQueue; } - Status = VIRTIO_CFG_WRITE (Dev, VhdrSenseSize, VIRTIO_SCSI_SENSE_SIZE); + Status = VIRTIO_CFG_WRITE (Dev, SenseSize, VIRTIO_SCSI_SENSE_SIZE); if (EFI_ERROR (Status)) { - goto ReleaseQueue; + goto UnmapQueue; } // // 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; + goto UnmapQueue; } // @@ -906,16 +1159,19 @@ VirtioScsiInit ( return EFI_SUCCESS; +UnmapQueue: + Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->RingMap); + ReleaseQueue: - VirtioRingUninit (&Dev->Ring); + VirtioRingUninit (Dev->VirtIo, &Dev->Ring); 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); Dev->InOutSupported = FALSE; Dev->MaxTarget = 0; @@ -926,7 +1182,6 @@ Failed: } - STATIC VOID EFIAPI @@ -939,20 +1194,48 @@ VirtioScsiUninit ( // 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); Dev->InOutSupported = FALSE; Dev->MaxTarget = 0; Dev->MaxLun = 0; Dev->MaxSectors = 0; - VirtioRingUninit (&Dev->Ring); + Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->RingMap); + VirtioRingUninit (Dev->VirtIo, &Dev->Ring); SetMem (&Dev->PassThru, sizeof Dev->PassThru, 0x00); SetMem (&Dev->PassThruMode, sizeof Dev->PassThruMode, 0x00); } +// +// Event notification function enqueued by ExitBootServices(). +// + +STATIC +VOID +EFIAPI +VirtioScsiExitBoot ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + VSCSI_DEV *Dev; + + DEBUG ((DEBUG_VERBOSE, "%a: Context=0x%p\n", __FUNCTION__, Context)); + // + // 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); +} + + // // Probe, start and stop functions of this driver, called by the DXE core for // specific devices. @@ -964,11 +1247,8 @@ VirtioScsiUninit ( // The implementation follows: // - Driver Writer's Guide for UEFI 2.3.1 v1.01 // - 5.1.3.4 OpenProtocol() and CloseProtocol() -// - 18 PCI Driver Design Guidelines -// - 18.3 PCI drivers // - UEFI Spec 2.3.1 + Errata C // - 6.3 Protocol Handler Services -// - 13.4 EFI PCI I/O Protocol // EFI_STATUS @@ -979,57 +1259,37 @@ VirtioScsiDriverBindingSupported ( IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath ) { - EFI_STATUS Status; - EFI_PCI_IO_PROTOCOL *PciIo; - PCI_TYPE00 Pci; + EFI_STATUS Status; + 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 + // 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 == 0x08) ? EFI_SUCCESS : EFI_UNSUPPORTED; + if (VirtIo->SubSystemDeviceId != VIRTIO_SUBSYSTEM_SCSI_HOST) { + 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; } @@ -1051,43 +1311,25 @@ VirtioScsiDriverBindingStart ( 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 FreeVirtioScsi; } // - // 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-scsi device. // - // For virtio-scsi we only need IO space access. - // - Status = Dev->PciIo->Attributes (Dev->PciIo, EfiPciIoAttributeOperationGet, - 0, &Dev->OriginalPciAttributes); + Status = VirtioScsiInit (Dev); if (EFI_ERROR (Status)) { - goto ClosePciIo; + goto CloseVirtIo; } - Status = Dev->PciIo->Attributes (Dev->PciIo, - EfiPciIoAttributeOperationEnable, - EFI_PCI_IO_ATTRIBUTE_IO, NULL); + Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK, + &VirtioScsiExitBoot, Dev, &Dev->ExitBoot); if (EFI_ERROR (Status)) { - goto ClosePciIo; - } - - // - // PCI IO access granted, configure virtio-scsi device. - // - Status = VirtioScsiInit (Dev); - if (EFI_ERROR (Status)) { - goto RestorePciAttributes; + goto UninitDev; } // @@ -1099,20 +1341,19 @@ VirtioScsiDriverBindingStart ( &gEfiExtScsiPassThruProtocolGuid, EFI_NATIVE_INTERFACE, &Dev->PassThru); if (EFI_ERROR (Status)) { - goto UninitDev; + goto CloseExitBoot; } return EFI_SUCCESS; +CloseExitBoot: + gBS->CloseEvent (Dev->ExitBoot); + UninitDev: VirtioScsiUninit (Dev); -RestorePciAttributes: - Dev->PciIo->Attributes (Dev->PciIo, EfiPciIoAttributeOperationSet, - Dev->OriginalPciAttributes, NULL); - -ClosePciIo: - gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid, +CloseVirtIo: + gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid, This->DriverBindingHandle, DeviceHandle); FreeVirtioScsi: @@ -1158,12 +1399,11 @@ VirtioScsiDriverBindingStop ( return Status; } - VirtioScsiUninit (Dev); + gBS->CloseEvent (Dev->ExitBoot); - Dev->PciIo->Attributes (Dev->PciIo, EfiPciIoAttributeOperationSet, - Dev->OriginalPciAttributes, NULL); + VirtioScsiUninit (Dev); - gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid, + gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid, This->DriverBindingHandle, DeviceHandle); FreePool (Dev);