/** @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.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #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 ); }