123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717 |
- /** @file
- This driver produces Virtio Device Protocol instances for Virtio PCI devices.
- Copyright (C) 2012, Red Hat, Inc.
- Copyright (c) 2012 - 2016, Intel Corporation. All rights reserved.<BR>
- Copyright (C) 2013, ARM Ltd.
- Copyright (C) 2017, AMD Inc, All rights reserved.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
- **/
- #include <IndustryStandard/Pci.h>
- #include <Library/BaseMemoryLib.h>
- #include <Library/DebugLib.h>
- #include <Library/MemoryAllocationLib.h>
- #include <Library/UefiBootServicesTableLib.h>
- #include <Library/UefiLib.h>
- #include "VirtioPciDevice.h"
- STATIC VIRTIO_DEVICE_PROTOCOL mDeviceProtocolTemplate = {
- 0, // Revision
- 0, // SubSystemDeviceId
- VirtioPciGetDeviceFeatures, // GetDeviceFeatures
- VirtioPciSetGuestFeatures, // SetGuestFeatures
- VirtioPciSetQueueAddress, // SetQueueAddress
- VirtioPciSetQueueSel, // SetQueueSel
- VirtioPciSetQueueNotify, // SetQueueNotify
- VirtioPciSetQueueAlignment, // SetQueueAlignment
- VirtioPciSetPageSize, // SetPageSize
- VirtioPciGetQueueSize, // GetQueueNumMax
- VirtioPciSetQueueSize, // SetQueueNum
- VirtioPciGetDeviceStatus, // GetDeviceStatus
- VirtioPciSetDeviceStatus, // SetDeviceStatus
- VirtioPciDeviceWrite, // WriteDevice
- VirtioPciDeviceRead, // ReadDevice
- VirtioPciAllocateSharedPages, // AllocateSharedPages
- VirtioPciFreeSharedPages, // FreeSharedPages
- VirtioPciMapSharedBuffer, // MapSharedBuffer
- VirtioPciUnmapSharedBuffer, // UnmapSharedBuffer
- };
- /**
- 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 PCI
- implementation of the protocol.
- @param[in] Dev Virtio 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().
- **/
- EFI_STATUS
- EFIAPI
- VirtioPciIoRead (
- IN VIRTIO_PCI_DEVICE *Dev,
- IN UINTN FieldOffset,
- IN UINTN FieldSize,
- IN UINTN BufferSize,
- OUT VOID *Buffer
- )
- {
- UINTN Count;
- EFI_PCI_IO_PROTOCOL_WIDTH Width;
- EFI_PCI_IO_PROTOCOL *PciIo;
- ASSERT (FieldSize == BufferSize);
- PciIo = Dev->PciIo;
- Count = 1;
- switch (FieldSize) {
- case 1:
- Width = EfiPciIoWidthUint8;
- break;
- case 2:
- Width = EfiPciIoWidthUint16;
- break;
- case 8:
- //
- // The 64bit PCI I/O is broken down into two 32bit reads to prevent
- // any alignment or width issues.
- // The UEFI spec says under EFI_PCI_IO_PROTOCOL.Io.Write():
- //
- // The I/O operations are carried out exactly as requested. The caller
- // is responsible for any alignment and I/O width issues which the
- // bus, device, platform, or type of I/O might require. For example on
- // some platforms, width requests of EfiPciIoWidthUint64 do not work.
- //
- Count = 2;
- //
- // fall through
- //
- case 4:
- Width = EfiPciIoWidthUint32;
- break;
- default:
- ASSERT (FALSE);
- return EFI_INVALID_PARAMETER;
- }
- return PciIo->Io.Read (
- PciIo,
- Width,
- PCI_BAR_IDX0,
- FieldOffset,
- Count,
- Buffer
- );
- }
- /**
- 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 PCI
- implementation of the protocol.
- @param[in] Dev Virtio 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().
- **/
- EFI_STATUS
- EFIAPI
- VirtioPciIoWrite (
- IN VIRTIO_PCI_DEVICE *Dev,
- IN UINTN FieldOffset,
- IN UINTN FieldSize,
- IN UINT64 Value
- )
- {
- UINTN Count;
- EFI_PCI_IO_PROTOCOL_WIDTH Width;
- EFI_PCI_IO_PROTOCOL *PciIo;
- PciIo = Dev->PciIo;
- Count = 1;
- switch (FieldSize) {
- case 1:
- Width = EfiPciIoWidthUint8;
- break;
- case 2:
- Width = EfiPciIoWidthUint16;
- break;
- case 8:
- //
- // The 64bit PCI I/O is broken down into two 32bit writes to prevent
- // any alignment or width issues.
- // The UEFI spec says under EFI_PCI_IO_PROTOCOL.Io.Write():
- //
- // The I/O operations are carried out exactly as requested. The caller
- // is responsible for any alignment and I/O width issues which the
- // bus, device, platform, or type of I/O might require. For example on
- // some platforms, width requests of EfiPciIoWidthUint64 do not work
- //
- Count = Count * 2;
- //
- // fall through
- //
- case 4:
- Width = EfiPciIoWidthUint32;
- break;
- default:
- ASSERT (FALSE);
- return EFI_INVALID_PARAMETER;
- }
- return PciIo->Io.Write (
- PciIo,
- Width,
- PCI_BAR_IDX0,
- FieldOffset,
- Count,
- &Value
- );
- }
- /**
- Device probe function for this driver.
- The DXE core calls this function for any given device in order to see if the
- driver can drive the device.
- @param[in] This The EFI_DRIVER_BINDING_PROTOCOL object
- incorporating this driver (independently of
- any device).
- @param[in] DeviceHandle The device to probe.
- @param[in] RemainingDevicePath Relevant only for bus drivers, ignored.
- @retval EFI_SUCCESS The driver supports the device being probed.
- @retval EFI_UNSUPPORTED Based on virtio-pci discovery, we do not support
- the device.
- @return Error codes from the OpenProtocol() boot service or
- the PciIo protocol.
- **/
- STATIC
- EFI_STATUS
- EFIAPI
- VirtioPciDeviceBindingSupported (
- 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;
- //
- // 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).
- //
- Status = gBS->OpenProtocol (
- DeviceHandle, // candidate device
- &gEfiPciIoProtocolGuid, // for generic PCI access
- (VOID **)&PciIo, // 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
- // 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
- //
- if ((Pci.Hdr.VendorId == VIRTIO_VENDOR_ID) &&
- (Pci.Hdr.DeviceId >= 0x1000) &&
- (Pci.Hdr.DeviceId <= 0x103F) &&
- (Pci.Hdr.RevisionID == 0x00))
- {
- Status = EFI_SUCCESS;
- } else {
- Status = EFI_UNSUPPORTED;
- }
- }
- //
- // We needed PCI IO access only transitorily, to see whether we support the
- // device or not.
- //
- gBS->CloseProtocol (
- DeviceHandle,
- &gEfiPciIoProtocolGuid,
- This->DriverBindingHandle,
- DeviceHandle
- );
- return Status;
- }
- /**
- Initialize the VirtIo PCI Device
- @param[in, out] Dev The driver instance to configure. The caller is
- responsible for Device->PciIo's validity (ie. working IO
- access to the underlying virtio-pci device).
- @retval EFI_SUCCESS Setup complete.
- @retval EFI_UNSUPPORTED The underlying IO device doesn't support the
- provided address offset and read size.
- @return Error codes from PciIo->Pci.Read().
- **/
- STATIC
- EFI_STATUS
- EFIAPI
- VirtioPciInit (
- IN OUT VIRTIO_PCI_DEVICE *Device
- )
- {
- EFI_STATUS Status;
- EFI_PCI_IO_PROTOCOL *PciIo;
- PCI_TYPE00 Pci;
- ASSERT (Device != NULL);
- PciIo = Device->PciIo;
- ASSERT (PciIo != NULL);
- ASSERT (PciIo->Pci.Read != NULL);
- Status = PciIo->Pci.Read (
- PciIo, // (protocol, device)
- // handle
- EfiPciIoWidthUint32, // access width & copy
- // mode
- 0, // Offset
- sizeof (Pci) / sizeof (UINT32), // Count
- &Pci // target buffer
- );
- if (EFI_ERROR (Status)) {
- return Status;
- }
- //
- // Copy protocol template
- //
- CopyMem (
- &Device->VirtioDevice,
- &mDeviceProtocolTemplate,
- sizeof (VIRTIO_DEVICE_PROTOCOL)
- );
- //
- // Initialize the protocol interface attributes
- //
- Device->VirtioDevice.Revision = VIRTIO_SPEC_REVISION (0, 9, 5);
- Device->VirtioDevice.SubSystemDeviceId = Pci.Device.SubsystemID;
- //
- // Note: We don't support the MSI-X capability. If we did,
- // the offset would become 24 after enabling MSI-X.
- //
- Device->DeviceSpecificConfigurationOffset =
- VIRTIO_DEVICE_SPECIFIC_CONFIGURATION_OFFSET_PCI;
- return EFI_SUCCESS;
- }
- /**
- Uninitialize the internals of a virtio-pci device that has been successfully
- set up with VirtioPciInit().
- @param[in, out] Dev The device to clean up.
- **/
- STATIC
- VOID
- EFIAPI
- VirtioPciUninit (
- IN OUT VIRTIO_PCI_DEVICE *Device
- )
- {
- // Note: This function mirrors VirtioPciInit() that does not allocate any
- // resources - there's nothing to free here.
- }
- /**
- After we've pronounced support for a specific device in
- DriverBindingSupported(), we start managing said device (passed in by the
- Driver Execution Environment) with the following service.
- See DriverBindingSupported() for specification references.
- @param[in] This The EFI_DRIVER_BINDING_PROTOCOL object
- incorporating this driver (independently of
- any device).
- @param[in] DeviceHandle The supported device to drive.
- @param[in] RemainingDevicePath Relevant only for bus drivers, ignored.
- @retval EFI_SUCCESS Driver instance has been created and
- initialized for the virtio-pci device, it
- is now accessible via VIRTIO_DEVICE_PROTOCOL.
- @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
- @return Error codes from the OpenProtocol() boot
- service, the PciIo protocol, VirtioPciInit(),
- or the InstallProtocolInterface() boot service.
- **/
- STATIC
- EFI_STATUS
- EFIAPI
- VirtioPciDeviceBindingStart (
- IN EFI_DRIVER_BINDING_PROTOCOL *This,
- IN EFI_HANDLE DeviceHandle,
- IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
- )
- {
- VIRTIO_PCI_DEVICE *Device;
- EFI_STATUS Status;
- Device = (VIRTIO_PCI_DEVICE *)AllocateZeroPool (sizeof *Device);
- if (Device == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
- Status = gBS->OpenProtocol (
- DeviceHandle,
- &gEfiPciIoProtocolGuid,
- (VOID **)&Device->PciIo,
- This->DriverBindingHandle,
- DeviceHandle,
- EFI_OPEN_PROTOCOL_BY_DRIVER
- );
- if (EFI_ERROR (Status)) {
- goto FreeVirtioPci;
- }
- //
- // 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-pci we only need IO space access.
- //
- Status = Device->PciIo->Attributes (
- Device->PciIo,
- EfiPciIoAttributeOperationGet,
- 0,
- &Device->OriginalPciAttributes
- );
- if (EFI_ERROR (Status)) {
- goto ClosePciIo;
- }
- Status = Device->PciIo->Attributes (
- Device->PciIo,
- EfiPciIoAttributeOperationEnable,
- (EFI_PCI_IO_ATTRIBUTE_IO |
- EFI_PCI_IO_ATTRIBUTE_BUS_MASTER),
- NULL
- );
- if (EFI_ERROR (Status)) {
- goto ClosePciIo;
- }
- //
- // PCI IO access granted, configure protocol instance
- //
- Status = VirtioPciInit (Device);
- if (EFI_ERROR (Status)) {
- goto RestorePciAttributes;
- }
- //
- // Setup complete, attempt to export the driver instance's VirtioDevice
- // interface.
- //
- Device->Signature = VIRTIO_PCI_DEVICE_SIGNATURE;
- Status = gBS->InstallProtocolInterface (
- &DeviceHandle,
- &gVirtioDeviceProtocolGuid,
- EFI_NATIVE_INTERFACE,
- &Device->VirtioDevice
- );
- if (EFI_ERROR (Status)) {
- goto UninitDev;
- }
- return EFI_SUCCESS;
- UninitDev:
- VirtioPciUninit (Device);
- RestorePciAttributes:
- Device->PciIo->Attributes (
- Device->PciIo,
- EfiPciIoAttributeOperationSet,
- Device->OriginalPciAttributes,
- NULL
- );
- ClosePciIo:
- gBS->CloseProtocol (
- DeviceHandle,
- &gEfiPciIoProtocolGuid,
- This->DriverBindingHandle,
- DeviceHandle
- );
- FreeVirtioPci:
- FreePool (Device);
- return Status;
- }
- /**
- Stop driving the Virtio PCI device
- @param[in] This The EFI_DRIVER_BINDING_PROTOCOL object
- incorporating this driver (independently of any
- device).
- @param[in] DeviceHandle Stop driving this device.
- @param[in] NumberOfChildren Since this function belongs to a device driver
- only (as opposed to a bus driver), the caller
- environment sets NumberOfChildren to zero, and
- we ignore it.
- @param[in] ChildHandleBuffer Ignored (corresponding to NumberOfChildren).
- @retval EFI_SUCCESS Driver instance has been stopped and the PCI
- configuration attributes have been restored.
- @return Error codes from the OpenProtocol() or
- CloseProtocol(), UninstallProtocolInterface()
- boot services.
- **/
- STATIC
- EFI_STATUS
- EFIAPI
- VirtioPciDeviceBindingStop (
- IN EFI_DRIVER_BINDING_PROTOCOL *This,
- IN EFI_HANDLE DeviceHandle,
- IN UINTN NumberOfChildren,
- IN EFI_HANDLE *ChildHandleBuffer
- )
- {
- EFI_STATUS Status;
- VIRTIO_DEVICE_PROTOCOL *VirtioDevice;
- VIRTIO_PCI_DEVICE *Device;
- Status = gBS->OpenProtocol (
- DeviceHandle, // candidate device
- &gVirtioDeviceProtocolGuid, // retrieve the VirtIo iface
- (VOID **)&VirtioDevice, // 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;
- }
- Device = VIRTIO_PCI_DEVICE_FROM_VIRTIO_DEVICE (VirtioDevice);
- //
- // Handle Stop() requests for in-use driver instances gracefully.
- //
- Status = gBS->UninstallProtocolInterface (
- DeviceHandle,
- &gVirtioDeviceProtocolGuid,
- &Device->VirtioDevice
- );
- if (EFI_ERROR (Status)) {
- return Status;
- }
- VirtioPciUninit (Device);
- Device->PciIo->Attributes (
- Device->PciIo,
- EfiPciIoAttributeOperationSet,
- Device->OriginalPciAttributes,
- NULL
- );
- Status = gBS->CloseProtocol (
- DeviceHandle,
- &gEfiPciIoProtocolGuid,
- This->DriverBindingHandle,
- DeviceHandle
- );
- FreePool (Device);
- return Status;
- }
- //
- // The static object that groups the Supported() (ie. probe), Start() and
- // Stop() functions of the driver together. Refer to UEFI Spec 2.3.1 + Errata
- // C, 10.1 EFI Driver Binding Protocol.
- //
- STATIC EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = {
- &VirtioPciDeviceBindingSupported,
- &VirtioPciDeviceBindingStart,
- &VirtioPciDeviceBindingStop,
- 0x10, // Version, must be in [0x10 .. 0xFFFFFFEF] for IHV-developed drivers
- NULL, // ImageHandle, to be overwritten by
- // EfiLibInstallDriverBindingComponentName2() in VirtioPciEntryPoint()
- NULL // DriverBindingHandle, ditto
- };
- //
- // The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and
- // EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's name
- // in English, for display on standard console devices. This is recommended for
- // UEFI drivers that follow the UEFI Driver Model. Refer to the Driver Writer's
- // Guide for UEFI 2.3.1 v1.01, 11 UEFI Driver and Controller Names.
- //
- STATIC
- EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {
- { "eng;en", L"Virtio PCI Driver" },
- { NULL, NULL }
- };
- STATIC
- EFI_COMPONENT_NAME_PROTOCOL gComponentName;
- EFI_STATUS
- EFIAPI
- VirtioPciGetDriverName (
- IN EFI_COMPONENT_NAME_PROTOCOL *This,
- IN CHAR8 *Language,
- OUT CHAR16 **DriverName
- )
- {
- return LookupUnicodeString2 (
- Language,
- This->SupportedLanguages,
- mDriverNameTable,
- DriverName,
- (BOOLEAN)(This == &gComponentName) // Iso639Language
- );
- }
- EFI_STATUS
- EFIAPI
- VirtioPciGetDeviceName (
- 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 gComponentName = {
- &VirtioPciGetDriverName,
- &VirtioPciGetDeviceName,
- "eng" // SupportedLanguages, ISO 639-2 language codes
- };
- STATIC
- EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = {
- (EFI_COMPONENT_NAME2_GET_DRIVER_NAME)&VirtioPciGetDriverName,
- (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME)&VirtioPciGetDeviceName,
- "en" // SupportedLanguages, RFC 4646 language codes
- };
- //
- // Entry point of this driver.
- //
- EFI_STATUS
- EFIAPI
- VirtioPciDeviceEntryPoint (
- IN EFI_HANDLE ImageHandle,
- IN EFI_SYSTEM_TABLE *SystemTable
- )
- {
- return EfiLibInstallDriverBindingComponentName2 (
- ImageHandle,
- SystemTable,
- &gDriverBinding,
- ImageHandle,
- &gComponentName,
- &gComponentName2
- );
- }
|