/** @file A non-transitional driver for VirtIo 1.0 PCI devices. Copyright (C) 2016, Red Hat, Inc. 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 distribution. The full text of the license may be found at http://opensource.org/licenses/bsd-license.php THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ #include #include #include #include #include #include #include #include #include #include #include #include #include "Virtio10.h" // // Utility functions // /** Transfer data between the caller and a register in a virtio-1.0 register block. @param[in] PciIo The EFI_PCI_IO_PROTOCOL instance that represents the device. @param[in] Config The "fat pointer" structure that identifies the register block to access. @param[in] Write TRUE if the register should be written, FALSE if the register should be read. @param[in] FieldOffset The offset of the register within the register block. @param[in] FieldSize The size of the register within the register block. Can be one of 1, 2, 4 and 8. Accesses to 8-byte registers are broken up into two 4-byte accesses. @param[in,out] Buffer When Write is TRUE, the register is written with data from Buffer. When Write is FALSE, the caller receives the register value into Buffer. @retval EFI_SUCCESS Register access successful. @retval EFI_INVALID_PARAMETER The register block pointed-to by Config doesn't exist; or FieldOffset and FieldSize would overflow the register block; or FieldSize is invalid. @return Error codes from EFI_PCI_IO_PROTOCOL.(Io|Mem).(Read|Write) member functions. **/ STATIC EFI_STATUS Virtio10Transfer ( IN EFI_PCI_IO_PROTOCOL *PciIo, IN VIRTIO_1_0_CONFIG *Config, IN BOOLEAN Write, IN UINTN FieldOffset, IN UINTN FieldSize, IN OUT VOID *Buffer ) { UINTN Count; EFI_PCI_IO_PROTOCOL_WIDTH Width; EFI_PCI_IO_PROTOCOL_ACCESS *BarType; EFI_PCI_IO_PROTOCOL_IO_MEM Access; if (!Config->Exists || FieldSize > Config->Length || FieldOffset > Config->Length - FieldSize) { return EFI_INVALID_PARAMETER; } 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: return EFI_INVALID_PARAMETER; } BarType = (Config->BarType == Virtio10BarTypeMem) ? &PciIo->Mem : &PciIo->Io; Access = Write ? BarType->Write : BarType->Read; return Access (PciIo, Width, Config->Bar, Config->Offset + FieldOffset, Count, Buffer); } /** Determine if a PCI BAR is IO or MMIO. @param[in] PciIo The EFI_PCI_IO_PROTOCOL instance that represents the device. @param[in] BarIndex The number of the BAR whose type the caller is interested in. @param[out] BarType On output, a VIRTIO_1_0_BAR_TYPE value that gives the type of the BAR. @retval EFI_SUCCESS The BAR type has been recognized and stored in BarType. @retval EFI_UNSUPPORTED The BAR type couldn't be identified. @return Error codes from EFI_PCI_IO_PROTOCOL.GetBarAttributes(). **/ STATIC EFI_STATUS GetBarType ( IN EFI_PCI_IO_PROTOCOL *PciIo, IN UINT8 BarIndex, OUT VIRTIO_1_0_BAR_TYPE *BarType ) { EFI_STATUS Status; VOID *Resources; Status = PciIo->GetBarAttributes (PciIo, BarIndex, NULL, &Resources); if (EFI_ERROR (Status)) { return Status; } Status = EFI_UNSUPPORTED; if (*(UINT8 *)Resources == ACPI_QWORD_ADDRESS_SPACE_DESCRIPTOR) { EFI_ACPI_QWORD_ADDRESS_SPACE_DESCRIPTOR *Descriptor; Descriptor = Resources; switch (Descriptor->ResType) { case ACPI_ADDRESS_SPACE_TYPE_MEM: *BarType = Virtio10BarTypeMem; Status = EFI_SUCCESS; break; case ACPI_ADDRESS_SPACE_TYPE_IO: *BarType = Virtio10BarTypeIo; Status = EFI_SUCCESS; break; default: break; } } FreePool (Resources); return Status; } /* Traverse the PCI capabilities list of a virtio-1.0 device, and capture the locations of the interesting virtio-1.0 register blocks. @param[in,out] Device The VIRTIO_1_0_DEV structure that identifies the device. On input, the caller is responsible that the Device->PciIo member be live, and that the CommonConfig, NotifyConfig, NotifyOffsetMultiplier and SpecificConfig members be zeroed. On output, said members will have been updated from the PCI capabilities found. @retval EFI_SUCCESS Traversal successful. @return Error codes from PciCapPciIoLib, PciCapLib, and the GetBarType() helper function. */ STATIC EFI_STATUS ParseCapabilities ( IN OUT VIRTIO_1_0_DEV *Device ) { EFI_STATUS Status; PCI_CAP_DEV *PciDevice; PCI_CAP_LIST *CapList; UINT16 VendorInstance; PCI_CAP *VendorCap; Status = PciCapPciIoDeviceInit (Device->PciIo, &PciDevice); if (EFI_ERROR (Status)) { return Status; } Status = PciCapListInit (PciDevice, &CapList); if (EFI_ERROR (Status)) { goto UninitPciDevice; } for (VendorInstance = 0; !EFI_ERROR (PciCapListFindCap (CapList, PciCapNormal, EFI_PCI_CAPABILITY_ID_VENDOR, VendorInstance, &VendorCap)); VendorInstance++) { UINT8 CapLen; VIRTIO_PCI_CAP VirtIoCap; VIRTIO_1_0_CONFIG *ParsedConfig; // // Big enough to accommodate a VIRTIO_PCI_CAP structure? // Status = PciCapRead (PciDevice, VendorCap, OFFSET_OF (EFI_PCI_CAPABILITY_VENDOR_HDR, Length), &CapLen, sizeof CapLen); if (EFI_ERROR (Status)) { goto UninitCapList; } if (CapLen < sizeof VirtIoCap) { // // Too small, move to next. // continue; } // // Read interesting part of capability. // Status = PciCapRead (PciDevice, VendorCap, 0, &VirtIoCap, sizeof VirtIoCap); if (EFI_ERROR (Status)) { goto UninitCapList; } switch (VirtIoCap.ConfigType) { case VIRTIO_PCI_CAP_COMMON_CFG: ParsedConfig = &Device->CommonConfig; break; case VIRTIO_PCI_CAP_NOTIFY_CFG: ParsedConfig = &Device->NotifyConfig; break; case VIRTIO_PCI_CAP_DEVICE_CFG: ParsedConfig = &Device->SpecificConfig; break; default: // // Capability is not interesting. // continue; } // // Save the location of the register block into ParsedConfig. // Status = GetBarType (Device->PciIo, VirtIoCap.Bar, &ParsedConfig->BarType); if (EFI_ERROR (Status)) { goto UninitCapList; } ParsedConfig->Bar = VirtIoCap.Bar; ParsedConfig->Offset = VirtIoCap.Offset; ParsedConfig->Length = VirtIoCap.Length; if (VirtIoCap.ConfigType == VIRTIO_PCI_CAP_NOTIFY_CFG) { // // This capability has an additional field called NotifyOffsetMultiplier; // parse it too. // if (CapLen < sizeof VirtIoCap + sizeof Device->NotifyOffsetMultiplier) { // // Too small, move to next. // continue; } Status = PciCapRead (PciDevice, VendorCap, sizeof VirtIoCap, &Device->NotifyOffsetMultiplier, sizeof Device->NotifyOffsetMultiplier); if (EFI_ERROR (Status)) { goto UninitCapList; } } // // Capability parsed successfully. // ParsedConfig->Exists = TRUE; } ASSERT_EFI_ERROR (Status); UninitCapList: PciCapListUninit (CapList); UninitPciDevice: PciCapPciIoDeviceUninit (PciDevice); return Status; } /** Accumulate the BAR type of a virtio-1.0 register block into a UINT64 attribute map, such that the latter is suitable for enabling IO / MMIO decoding with EFI_PCI_IO_PROTOCOL.Attributes(). @param[in] Config The "fat pointer" structure that identifies the register block. It is allowed for the register block not to exist. @param[in,out] Attributes On output, if the register block exists, EFI_PCI_IO_ATTRIBUTE_MEMORY or EFI_PCI_IO_ATTRIBUTE_IO is OR-ed into Attributes, according to the register block's BAR type. **/ STATIC VOID UpdateAttributes ( IN VIRTIO_1_0_CONFIG *Config, IN OUT UINT64 *Attributes ) { if (Config->Exists) { *Attributes |= (Config->BarType == Virtio10BarTypeMem) ? EFI_PCI_IO_ATTRIBUTE_MEMORY: EFI_PCI_IO_ATTRIBUTE_IO; } } // // VIRTIO_DEVICE_PROTOCOL member functions // STATIC EFI_STATUS EFIAPI Virtio10GetDeviceFeatures ( IN VIRTIO_DEVICE_PROTOCOL *This, OUT UINT64 *DeviceFeatures ) { VIRTIO_1_0_DEV *Dev; UINT32 Selector; UINT32 Features32[2]; Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This); for (Selector = 0; Selector < 2; ++Selector) { EFI_STATUS Status; // // Select the low or high half of the features. // Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE, OFFSET_OF (VIRTIO_PCI_COMMON_CFG, DeviceFeatureSelect), sizeof Selector, &Selector); if (EFI_ERROR (Status)) { return Status; } // // Fetch that half. // Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, FALSE, OFFSET_OF (VIRTIO_PCI_COMMON_CFG, DeviceFeature), sizeof Features32[Selector], &Features32[Selector]); if (EFI_ERROR (Status)) { return Status; } } *DeviceFeatures = LShiftU64 (Features32[1], 32) | Features32[0]; return EFI_SUCCESS; } STATIC EFI_STATUS EFIAPI Virtio10SetGuestFeatures ( IN VIRTIO_DEVICE_PROTOCOL *This, IN UINT64 Features ) { VIRTIO_1_0_DEV *Dev; UINT32 Selector; UINT32 Features32[2]; Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This); Features32[0] = (UINT32)Features; Features32[1] = (UINT32)RShiftU64 (Features, 32); for (Selector = 0; Selector < 2; ++Selector) { EFI_STATUS Status; // // Select the low or high half of the features. // Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE, OFFSET_OF (VIRTIO_PCI_COMMON_CFG, DriverFeatureSelect), sizeof Selector, &Selector); if (EFI_ERROR (Status)) { return Status; } // // Write that half. // Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE, OFFSET_OF (VIRTIO_PCI_COMMON_CFG, DriverFeature), sizeof Features32[Selector], &Features32[Selector]); if (EFI_ERROR (Status)) { return Status; } } return EFI_SUCCESS; } STATIC EFI_STATUS EFIAPI Virtio10SetQueueAddress ( IN VIRTIO_DEVICE_PROTOCOL *This, IN VRING *Ring, IN UINT64 RingBaseShift ) { VIRTIO_1_0_DEV *Dev; EFI_STATUS Status; UINT64 Address; UINT16 Enable; Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This); Address = (UINTN)Ring->Desc; Address += RingBaseShift; Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE, OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueDesc), sizeof Address, &Address); if (EFI_ERROR (Status)) { return Status; } Address = (UINTN)Ring->Avail.Flags; Address += RingBaseShift; Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE, OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueAvail), sizeof Address, &Address); if (EFI_ERROR (Status)) { return Status; } Address = (UINTN)Ring->Used.Flags; Address += RingBaseShift; Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE, OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueUsed), sizeof Address, &Address); if (EFI_ERROR (Status)) { return Status; } Enable = 1; Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE, OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueEnable), sizeof Enable, &Enable); return Status; } STATIC EFI_STATUS EFIAPI Virtio10SetQueueSel ( IN VIRTIO_DEVICE_PROTOCOL *This, IN UINT16 Index ) { VIRTIO_1_0_DEV *Dev; EFI_STATUS Status; Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This); Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE, OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueSelect), sizeof Index, &Index); return Status; } STATIC EFI_STATUS EFIAPI Virtio10SetQueueNotify ( IN VIRTIO_DEVICE_PROTOCOL *This, IN UINT16 Index ) { VIRTIO_1_0_DEV *Dev; EFI_STATUS Status; UINT16 SavedQueueSelect; UINT16 NotifyOffset; Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This); // // Read NotifyOffset first. NotifyOffset is queue specific, so we have // to stash & restore the current queue selector around it. // // So, start with saving the current queue selector. // Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, FALSE, OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueSelect), sizeof SavedQueueSelect, &SavedQueueSelect); if (EFI_ERROR (Status)) { return Status; } // // Select the requested queue. // Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE, OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueSelect), sizeof Index, &Index); if (EFI_ERROR (Status)) { return Status; } // // Read the QueueNotifyOff field. // Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, FALSE, OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueNotifyOff), sizeof NotifyOffset, &NotifyOffset); if (EFI_ERROR (Status)) { return Status; } // // Re-select the original queue. // Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE, OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueSelect), sizeof SavedQueueSelect, &SavedQueueSelect); if (EFI_ERROR (Status)) { return Status; } // // We can now kick the queue. // Status = Virtio10Transfer (Dev->PciIo, &Dev->NotifyConfig, TRUE, NotifyOffset * Dev->NotifyOffsetMultiplier, sizeof Index, &Index); return Status; } STATIC EFI_STATUS EFIAPI Virtio10SetQueueAlign ( IN VIRTIO_DEVICE_PROTOCOL *This, IN UINT32 Alignment ) { return (Alignment == EFI_PAGE_SIZE) ? EFI_SUCCESS : EFI_UNSUPPORTED; } STATIC EFI_STATUS EFIAPI Virtio10SetPageSize ( IN VIRTIO_DEVICE_PROTOCOL *This, IN UINT32 PageSize ) { return (PageSize == EFI_PAGE_SIZE) ? EFI_SUCCESS : EFI_UNSUPPORTED; } STATIC EFI_STATUS EFIAPI Virtio10GetQueueNumMax ( IN VIRTIO_DEVICE_PROTOCOL *This, OUT UINT16 *QueueNumMax ) { VIRTIO_1_0_DEV *Dev; EFI_STATUS Status; Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This); Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, FALSE, OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueSize), sizeof *QueueNumMax, QueueNumMax); return Status; } STATIC EFI_STATUS EFIAPI Virtio10SetQueueNum ( IN VIRTIO_DEVICE_PROTOCOL *This, IN UINT16 QueueSize ) { EFI_STATUS Status; UINT16 CurrentSize; // // This member function is required for VirtIo MMIO, and a no-op in // VirtIo PCI 0.9.5. In VirtIo 1.0, drivers can theoretically use this // member to reduce memory consumption, but none of our drivers do. So // just check that they set the size that is already in effect. // Status = Virtio10GetQueueNumMax (This, &CurrentSize); if (EFI_ERROR (Status)) { return Status; } return (CurrentSize == QueueSize) ? EFI_SUCCESS : EFI_UNSUPPORTED; } STATIC EFI_STATUS EFIAPI Virtio10GetDeviceStatus ( IN VIRTIO_DEVICE_PROTOCOL *This, OUT UINT8 *DeviceStatus ) { VIRTIO_1_0_DEV *Dev; EFI_STATUS Status; Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This); Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, FALSE, OFFSET_OF (VIRTIO_PCI_COMMON_CFG, DeviceStatus), sizeof *DeviceStatus, DeviceStatus); return Status; } STATIC EFI_STATUS EFIAPI Virtio10SetDeviceStatus ( IN VIRTIO_DEVICE_PROTOCOL *This, IN UINT8 DeviceStatus ) { VIRTIO_1_0_DEV *Dev; EFI_STATUS Status; Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This); Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE, OFFSET_OF (VIRTIO_PCI_COMMON_CFG, DeviceStatus), sizeof DeviceStatus, &DeviceStatus); return Status; } STATIC EFI_STATUS EFIAPI Virtio10WriteDevice ( IN VIRTIO_DEVICE_PROTOCOL *This, IN UINTN FieldOffset, IN UINTN FieldSize, IN UINT64 Value ) { VIRTIO_1_0_DEV *Dev; EFI_STATUS Status; Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This); Status = Virtio10Transfer (Dev->PciIo, &Dev->SpecificConfig, TRUE, FieldOffset, FieldSize, &Value); return Status; } STATIC EFI_STATUS EFIAPI Virtio10ReadDevice ( IN VIRTIO_DEVICE_PROTOCOL *This, IN UINTN FieldOffset, IN UINTN FieldSize, IN UINTN BufferSize, OUT VOID *Buffer ) { VIRTIO_1_0_DEV *Dev; EFI_STATUS Status; if (FieldSize != BufferSize) { return EFI_INVALID_PARAMETER; } Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This); Status = Virtio10Transfer (Dev->PciIo, &Dev->SpecificConfig, FALSE, FieldOffset, FieldSize, Buffer); return Status; } STATIC EFI_STATUS EFIAPI Virtio10AllocateSharedPages ( IN VIRTIO_DEVICE_PROTOCOL *This, IN UINTN Pages, IN OUT VOID **HostAddress ) { VIRTIO_1_0_DEV *Dev; EFI_STATUS Status; Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This); Status = Dev->PciIo->AllocateBuffer ( Dev->PciIo, AllocateAnyPages, EfiBootServicesData, Pages, HostAddress, EFI_PCI_ATTRIBUTE_MEMORY_CACHED ); return Status; } STATIC VOID EFIAPI Virtio10FreeSharedPages ( IN VIRTIO_DEVICE_PROTOCOL *This, IN UINTN Pages, IN VOID *HostAddress ) { VIRTIO_1_0_DEV *Dev; Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This); Dev->PciIo->FreeBuffer ( Dev->PciIo, Pages, HostAddress ); } STATIC EFI_STATUS EFIAPI Virtio10MapSharedBuffer ( IN VIRTIO_DEVICE_PROTOCOL *This, IN VIRTIO_MAP_OPERATION Operation, IN VOID *HostAddress, IN OUT UINTN *NumberOfBytes, OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, OUT VOID **Mapping ) { EFI_STATUS Status; VIRTIO_1_0_DEV *Dev; EFI_PCI_IO_PROTOCOL_OPERATION PciIoOperation; Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This); // // Map VIRTIO_MAP_OPERATION to EFI_PCI_IO_PROTOCOL_OPERATION // switch (Operation) { case VirtioOperationBusMasterRead: PciIoOperation = EfiPciIoOperationBusMasterRead; break; case VirtioOperationBusMasterWrite: PciIoOperation = EfiPciIoOperationBusMasterWrite; break; case VirtioOperationBusMasterCommonBuffer: PciIoOperation = EfiPciIoOperationBusMasterCommonBuffer; break; default: return EFI_INVALID_PARAMETER; } Status = Dev->PciIo->Map ( Dev->PciIo, PciIoOperation, HostAddress, NumberOfBytes, DeviceAddress, Mapping ); return Status; } STATIC EFI_STATUS EFIAPI Virtio10UnmapSharedBuffer ( IN VIRTIO_DEVICE_PROTOCOL *This, IN VOID *Mapping ) { EFI_STATUS Status; VIRTIO_1_0_DEV *Dev; Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This); Status = Dev->PciIo->Unmap ( Dev->PciIo, Mapping ); return Status; } STATIC CONST VIRTIO_DEVICE_PROTOCOL mVirtIoTemplate = { VIRTIO_SPEC_REVISION (1, 0, 0), 0, // SubSystemDeviceId, filled in dynamically Virtio10GetDeviceFeatures, Virtio10SetGuestFeatures, Virtio10SetQueueAddress, Virtio10SetQueueSel, Virtio10SetQueueNotify, Virtio10SetQueueAlign, Virtio10SetPageSize, Virtio10GetQueueNumMax, Virtio10SetQueueNum, Virtio10GetDeviceStatus, Virtio10SetDeviceStatus, Virtio10WriteDevice, Virtio10ReadDevice, Virtio10AllocateSharedPages, Virtio10FreeSharedPages, Virtio10MapSharedBuffer, Virtio10UnmapSharedBuffer }; // // EFI_DRIVER_BINDING_PROTOCOL member functions // STATIC EFI_STATUS EFIAPI Virtio10BindingSupported ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE DeviceHandle, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath ) { EFI_STATUS Status; EFI_PCI_IO_PROTOCOL *PciIo; PCI_TYPE00 Pci; Status = gBS->OpenProtocol (DeviceHandle, &gEfiPciIoProtocolGuid, (VOID **)&PciIo, This->DriverBindingHandle, DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER); if (EFI_ERROR (Status)) { return Status; } Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, 0, sizeof Pci / sizeof (UINT32), &Pci); if (EFI_ERROR (Status)) { goto CloseProtocol; } Status = EFI_UNSUPPORTED; // // Recognize non-transitional modern devices. Also, we'll have to parse the // PCI capability list, so make sure the CapabilityPtr field will be valid. // if (Pci.Hdr.VendorId == VIRTIO_VENDOR_ID && Pci.Hdr.DeviceId >= 0x1040 && Pci.Hdr.DeviceId <= 0x107F && Pci.Hdr.RevisionID >= 0x01 && Pci.Device.SubsystemID >= 0x40 && (Pci.Hdr.Status & EFI_PCI_STATUS_CAPABILITY) != 0) { // // The virtio-vga device is special. It can be driven both as a VGA device // with a linear framebuffer, and through its underlying, modern, // virtio-gpu-pci device, which has no linear framebuffer itself. For // compatibility with guest OSes that insist on inheriting a linear // framebuffer from the firmware, we should leave virtio-vga to // QemuVideoDxe, and support only virtio-gpu-pci here. // // Both virtio-vga and virtio-gpu-pci have DeviceId 0x1050, but only the // former has device class PCI_CLASS_DISPLAY_VGA. // if (Pci.Hdr.DeviceId != 0x1050 || !IS_PCI_VGA (&Pci)) { Status = EFI_SUCCESS; } } CloseProtocol: gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid, This->DriverBindingHandle, DeviceHandle); return Status; } STATIC EFI_STATUS EFIAPI Virtio10BindingStart ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE DeviceHandle, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath ) { VIRTIO_1_0_DEV *Device; EFI_STATUS Status; PCI_TYPE00 Pci; UINT64 SetAttributes; Device = AllocateZeroPool (sizeof *Device); if (Device == NULL) { return EFI_OUT_OF_RESOURCES; } Device->Signature = VIRTIO_1_0_SIGNATURE; CopyMem (&Device->VirtIo, &mVirtIoTemplate, sizeof mVirtIoTemplate); Status = gBS->OpenProtocol (DeviceHandle, &gEfiPciIoProtocolGuid, (VOID **)&Device->PciIo, This->DriverBindingHandle, DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER); if (EFI_ERROR (Status)) { goto FreeDevice; } Status = Device->PciIo->Pci.Read (Device->PciIo, EfiPciIoWidthUint32, 0, sizeof Pci / sizeof (UINT32), &Pci); if (EFI_ERROR (Status)) { goto ClosePciIo; } Device->VirtIo.SubSystemDeviceId = Pci.Hdr.DeviceId - 0x1040; Status = ParseCapabilities (Device); if (EFI_ERROR (Status)) { goto ClosePciIo; } Status = Device->PciIo->Attributes (Device->PciIo, EfiPciIoAttributeOperationGet, 0, &Device->OriginalPciAttributes); if (EFI_ERROR (Status)) { goto ClosePciIo; } SetAttributes = (EFI_PCI_IO_ATTRIBUTE_BUS_MASTER | EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE); UpdateAttributes (&Device->CommonConfig, &SetAttributes); UpdateAttributes (&Device->NotifyConfig, &SetAttributes); UpdateAttributes (&Device->SpecificConfig, &SetAttributes); Status = Device->PciIo->Attributes (Device->PciIo, EfiPciIoAttributeOperationEnable, SetAttributes, NULL); if (EFI_ERROR (Status)) { goto ClosePciIo; } Status = gBS->InstallProtocolInterface (&DeviceHandle, &gVirtioDeviceProtocolGuid, EFI_NATIVE_INTERFACE, &Device->VirtIo); if (EFI_ERROR (Status)) { goto RestorePciAttributes; } return EFI_SUCCESS; RestorePciAttributes: Device->PciIo->Attributes (Device->PciIo, EfiPciIoAttributeOperationSet, Device->OriginalPciAttributes, NULL); ClosePciIo: gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid, This->DriverBindingHandle, DeviceHandle); FreeDevice: FreePool (Device); return Status; } STATIC EFI_STATUS EFIAPI Virtio10BindingStop ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE DeviceHandle, IN UINTN NumberOfChildren, IN EFI_HANDLE *ChildHandleBuffer ) { EFI_STATUS Status; VIRTIO_DEVICE_PROTOCOL *VirtIo; VIRTIO_1_0_DEV *Device; Status = gBS->OpenProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid, (VOID **)&VirtIo, This->DriverBindingHandle, DeviceHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (EFI_ERROR (Status)) { return Status; } Device = VIRTIO_1_0_FROM_VIRTIO_DEVICE (VirtIo); Status = gBS->UninstallProtocolInterface (DeviceHandle, &gVirtioDeviceProtocolGuid, &Device->VirtIo); if (EFI_ERROR (Status)) { return Status; } Device->PciIo->Attributes (Device->PciIo, EfiPciIoAttributeOperationSet, Device->OriginalPciAttributes, NULL); gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid, This->DriverBindingHandle, DeviceHandle); FreePool (Device); return EFI_SUCCESS; } STATIC EFI_DRIVER_BINDING_PROTOCOL mDriverBinding = { &Virtio10BindingSupported, &Virtio10BindingStart, &Virtio10BindingStop, 0x10, // Version NULL, // ImageHandle, to be overwritten NULL // DriverBindingHandle, to be overwritten }; // // EFI_COMPONENT_NAME_PROTOCOL and EFI_COMPONENT_NAME2_PROTOCOL // implementations // STATIC EFI_UNICODE_STRING_TABLE mDriverNameTable[] = { { "eng;en", L"Virtio 1.0 PCI Driver" }, { NULL, NULL } }; STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName; STATIC EFI_STATUS EFIAPI Virtio10GetDriverName ( IN EFI_COMPONENT_NAME_PROTOCOL *This, IN CHAR8 *Language, OUT CHAR16 **DriverName ) { return LookupUnicodeString2 ( Language, This->SupportedLanguages, mDriverNameTable, DriverName, (BOOLEAN)(This == &mComponentName) // Iso639Language ); } STATIC EFI_STATUS EFIAPI Virtio10GetDeviceName ( IN EFI_COMPONENT_NAME_PROTOCOL *This, IN EFI_HANDLE DeviceHandle, IN EFI_HANDLE ChildHandle, IN CHAR8 *Language, OUT CHAR16 **ControllerName ) { return EFI_UNSUPPORTED; } STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName = { &Virtio10GetDriverName, &Virtio10GetDeviceName, "eng" }; STATIC EFI_COMPONENT_NAME2_PROTOCOL mComponentName2 = { (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &Virtio10GetDriverName, (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &Virtio10GetDeviceName, "en" }; // // Entry point of this driver // EFI_STATUS EFIAPI Virtio10EntryPoint ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { return EfiLibInstallDriverBindingComponentName2 ( ImageHandle, SystemTable, &mDriverBinding, ImageHandle, &mComponentName, &mComponentName2 ); }