From 16e0ece611a3f1bc82855c6c52957e22533d8257 Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Fri, 14 Jun 2013 07:39:52 +0000 Subject: OvmfPkg: VirtioNetDxe: driver binding Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Laszlo Ersek Reviewed-by: Jordan Justen Reviewed-by: Stefan Hajnoczi git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@14408 6f19259b-4bc3-4df7-8a09-765794883524 --- OvmfPkg/VirtioNetDxe/DriverBinding.c | 674 +++++++++++++++++++++++++++++++++++ 1 file changed, 674 insertions(+) create mode 100644 OvmfPkg/VirtioNetDxe/DriverBinding.c (limited to 'OvmfPkg') diff --git a/OvmfPkg/VirtioNetDxe/DriverBinding.c b/OvmfPkg/VirtioNetDxe/DriverBinding.c new file mode 100644 index 0000000000..d9c0096d08 --- /dev/null +++ b/OvmfPkg/VirtioNetDxe/DriverBinding.c @@ -0,0 +1,674 @@ +/** @file + + Driver Binding code and its private helpers for the virtio-net driver. + + Copyright (C) 2013, Red Hat, Inc. + Copyright (c) 2006 - 2010, 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 + 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 "VirtioNet.h" + +#define RECEIVE_FILTERS_NO_MCAST ((UINT32) ( \ + EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | \ + EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST | \ + EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS \ + )) + +/* + Temporarily enable then reset the virtio-net device in order to retrieve + configuration values needed by Simple Network Protocol and Simple Network + Mode fields. + + Only VirtioNetSnpPopulate() may call this function. + + If the function fails for any reason, the virtio-net device is moved to + VSTAT_FAILED instead of being reset. This serves only informative purposes + for the host side. + + param[in,out] Dev The VNET_DEV structure being created for + the virtio-net device. + param[out] MacAddress MAC address configured by the host. + param[out] MediaPresentSupported Link status is made available by the host. + param[out] MediaPresent If link status is made available by the + host, the current link status is stored in + *MediaPresent. Otherwise MediaPresent is + unused. + + @retval EFI_UNSUPPORTED The host doesn't supply a MAC address. + @return Status codes from Dev->PciIo->Io.Read(), + VIRTIO_CFG_READ() and VIRTIO_CFG_WRITE(). + @retval EFI_SUCCESS Configuration values retrieved. +*/ +STATIC +EFI_STATUS +EFIAPI +VirtioNetGetFeatures ( + IN OUT VNET_DEV *Dev, + OUT EFI_MAC_ADDRESS *MacAddress, + OUT BOOLEAN *MediaPresentSupported, + OUT BOOLEAN *MediaPresent + ) +{ + EFI_STATUS Status; + UINT8 NextDevStat; + UINT32 Features; + UINT16 LinkStatus; + + // + // Interrogate the device for features (virtio-0.9.5, 2.2.1 Device + // Initialization Sequence), but don't complete setting it up. + // + NextDevStat = 0; // step 1 -- reset device + Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, NextDevStat); + if (EFI_ERROR (Status)) { + return Status; + } + + NextDevStat |= VSTAT_ACK; // step 2 -- acknowledge device presence + Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, NextDevStat); + if (EFI_ERROR (Status)) { + goto YieldDevice; + } + + NextDevStat |= VSTAT_DRIVER; // step 3 -- we know how to drive it + Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, NextDevStat); + if (EFI_ERROR (Status)) { + goto YieldDevice; + } + + // + // step 4a -- retrieve and validate features + // + Status = VIRTIO_CFG_READ (Dev, Generic.VhdrDeviceFeatureBits, &Features); + if (EFI_ERROR (Status)) { + goto YieldDevice; + } + + // + // get MAC address byte-wise + // + if ((Features & VIRTIO_NET_F_MAC) == 0) { + Status = EFI_UNSUPPORTED; + goto YieldDevice; + } + Status = Dev->PciIo->Io.Read (Dev->PciIo, // PciIo + EfiPciIoWidthUint8, // Width + PCI_BAR_IDX0, // BarIndex + OFFSET_OF_VNET (VhdrMac), // Offset + SIZE_OF_VNET (VhdrMac), // Count + MacAddress // Buffer + ); + + if (EFI_ERROR (Status)) { + goto YieldDevice; + } + + // + // check if link status is reported, and if so, what the link status is + // + if ((Features & VIRTIO_NET_F_STATUS) == 0) { + *MediaPresentSupported = FALSE; + } + else { + *MediaPresentSupported = TRUE; + Status = VIRTIO_CFG_READ (Dev, VhdrLinkStatus, &LinkStatus); + if (EFI_ERROR (Status)) { + goto YieldDevice; + } + *MediaPresent = !!(LinkStatus & VIRTIO_NET_S_LINK_UP); + } + +YieldDevice: + VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, + EFI_ERROR (Status) ? VSTAT_FAILED : 0); + + return Status; +} + + +/** + Set up the Simple Network Protocol fields, the Simple Network Mode fields, + and the Exit Boot Services Event of the virtio-net driver instance. + + This function may only be called by VirtioNetDriverBindingStart(). + + @param[in,out] Dev The VNET_DEV driver instance being created for the + virtio-net device. + + @return Status codes from the CreateEvent() boot service or the + VirtioNetGetFeatures() function. + @retval EFI_SUCCESS Configuration successful. +*/ +STATIC +EFI_STATUS +EFIAPI +VirtioNetSnpPopulate ( + IN OUT VNET_DEV *Dev + ) +{ + EFI_STATUS Status; + + // + // We set up a function here that is asynchronously callable by an + // external application to check if there are any packets available for + // reception. The least urgent task priority level we can specify for such a + // "software interrupt" is TPL_CALLBACK. + // + // TPL_CALLBACK is also the maximum TPL an SNP implementation is allowed to + // run at (see 6.1 Event, Timer, and Task Priority Services in the UEFI + // Specification 2.3.1+errC). + // + // Since we raise our TPL to TPL_CALLBACK in every single function that + // accesses the device, and the external application also queues its interest + // for received packets at the same TPL_CALLBACK, in effect the + // VirtioNetIsPacketAvailable() function will never interrupt any + // device-accessing driver function, it will be scheduled in isolation. + // + // TPL_CALLBACK (which basically this entire driver runs at) is allowed + // for "[l]ong term operations (such as file system operations and disk + // I/O)". Because none of our functions block, we'd satisfy an even stronger + // requirement. + // + Status = gBS->CreateEvent (EVT_NOTIFY_WAIT, TPL_CALLBACK, + &VirtioNetIsPacketAvailable, Dev, &Dev->Snp.WaitForPacket); + if (EFI_ERROR (Status)) { + return Status; + } + + Dev->Snp.Revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION; + Dev->Snp.Start = &VirtioNetStart; + Dev->Snp.Stop = &VirtioNetStop; + Dev->Snp.Initialize = &VirtioNetInitialize; + Dev->Snp.Reset = &VirtioNetReset; + Dev->Snp.Shutdown = &VirtioNetShutdown; + Dev->Snp.ReceiveFilters = &VirtioNetReceiveFilters; + Dev->Snp.StationAddress = &VirtioNetStationAddress; + Dev->Snp.Statistics = &VirtioNetStatistics; + Dev->Snp.MCastIpToMac = &VirtioNetMcastIpToMac; + Dev->Snp.NvData = &VirtioNetNvData; + Dev->Snp.GetStatus = &VirtioNetGetStatus; + Dev->Snp.Transmit = &VirtioNetTransmit; + Dev->Snp.Receive = &VirtioNetReceive; + Dev->Snp.Mode = &Dev->Snm; + + Dev->Snm.State = EfiSimpleNetworkStopped; + Dev->Snm.HwAddressSize = SIZE_OF_VNET (VhdrMac); + Dev->Snm.MediaHeaderSize = SIZE_OF_VNET (VhdrMac) + // dst MAC + SIZE_OF_VNET (VhdrMac) + // src MAC + 2; // Ethertype + Dev->Snm.MaxPacketSize = 1500; + Dev->Snm.NvRamSize = 0; + Dev->Snm.NvRamAccessSize = 0; + Dev->Snm.ReceiveFilterMask = RECEIVE_FILTERS_NO_MCAST; + Dev->Snm.ReceiveFilterSetting = RECEIVE_FILTERS_NO_MCAST; + Dev->Snm.MaxMCastFilterCount = 0; + Dev->Snm.MCastFilterCount = 0; + Dev->Snm.IfType = 1; // ethernet + Dev->Snm.MacAddressChangeable = FALSE; + Dev->Snm.MultipleTxSupported = TRUE; + + ASSERT (SIZE_OF_VNET (VhdrMac) <= sizeof (EFI_MAC_ADDRESS)); + + Status = VirtioNetGetFeatures (Dev, &Dev->Snm.CurrentAddress, + &Dev->Snm.MediaPresentSupported, &Dev->Snm.MediaPresent); + if (EFI_ERROR (Status)) { + goto CloseWaitForPacket; + } + CopyMem (&Dev->Snm.PermanentAddress, &Dev->Snm.CurrentAddress, + SIZE_OF_VNET (VhdrMac)); + SetMem (&Dev->Snm.BroadcastAddress, SIZE_OF_VNET (VhdrMac), 0xFF); + + // + // VirtioNetExitBoot() is queued by ExitBootServices(); its purpose is to + // cancel any pending virtio requests. The TPL_CALLBACK reasoning is + // identical to the one above. There's one difference: this kind of + // event is "globally visible", which means it can be signalled as soon as + // we create it. We haven't raised our TPL here, hence VirtioNetExitBoot() + // could be entered immediately. VirtioNetExitBoot() checks Dev->Snm.State, + // so we're safe. + // + Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK, + &VirtioNetExitBoot, Dev, &Dev->ExitBoot); + if (EFI_ERROR (Status)) { + goto CloseWaitForPacket; + } + + return EFI_SUCCESS; + +CloseWaitForPacket: + gBS->CloseEvent (Dev->Snp.WaitForPacket); + return Status; +} + + +/** + Release any resources allocated by VirtioNetSnpPopulate(). + + This function may only be called by VirtioNetDriverBindingStart(), when + rolling back a partial, failed driver instance creation, and by + VirtioNetDriverBindingStop(), when disconnecting a virtio-net device from the + driver. + + @param[in,out] Dev The VNET_DEV driver instance being destroyed. +*/ +STATIC +VOID +EFIAPI +VirtioNetSnpEvacuate ( + IN OUT VNET_DEV *Dev + ) +{ + // + // This function runs either at TPL_CALLBACK already (from + // VirtioNetDriverBindingStop()), or it is part of a teardown following + // a partial, failed construction in VirtioNetDriverBindingStart(), when + // WaitForPacket was never accessible to the world. + // + gBS->CloseEvent (Dev->ExitBoot); + gBS->CloseEvent (Dev->Snp.WaitForPacket); +} + + +/** + Tests to see if this driver supports a given controller. If a child device is + provided, it further tests to see if this driver supports creating a handle + for the specified child device. + + This function checks to see if the driver specified by This supports the + device specified by ControllerHandle. Drivers will typically use the device + path attached to ControllerHandle and/or the services from the bus I/O + abstraction attached to ControllerHandle to determine if the driver supports + ControllerHandle. This function may be called many times during platform + initialization. In order to reduce boot times, the tests performed by this + function must be very small, and take as little time as possible to execute. + This function must not change the state of any hardware devices, and this + function must be aware that the device specified by ControllerHandle may + already be managed by the same driver or a different driver. This function + must match its calls to AllocatePages() with FreePages(), AllocatePool() with + FreePool(), and OpenProtocol() with CloseProtocol(). Because ControllerHandle + may have been previously started by the same driver, if a protocol is already + in the opened state, then it must not be closed with CloseProtocol(). This is + required to guarantee the state of ControllerHandle is not modified by this + function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL + instance. + @param[in] ControllerHandle The handle of the controller to test. This + handle must support a protocol interface + that supplies an I/O abstraction to the + driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a + device path. This parameter is ignored by + device drivers, and is optional for bus + drivers. For bus drivers, if this parameter + is not NULL, then the bus driver must + determine if the bus controller specified by + ControllerHandle and the child controller + specified by RemainingDevicePath are both + supported by this bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the + driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed + by the driver specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed + by a different driver or an application that + requires exclusive access. Currently not + implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the + driver specified by This. +**/ + +STATIC +EFI_STATUS +EFIAPI +VirtioNetDriverBindingSupported ( + 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); + + // + // virtio-0.9.5, 2.1 PCI Discovery: + // the network device has Subsystem Device ID 1 + // + if (Status == EFI_SUCCESS) { + Status = (Pci.Hdr.VendorId == 0x1AF4 && + Pci.Hdr.DeviceId >= 0x1000 && Pci.Hdr.DeviceId <= 0x103F && + Pci.Hdr.RevisionID == 0x00 && + Pci.Device.SubsystemID == 0x01) ? EFI_SUCCESS : EFI_UNSUPPORTED; + } + + gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, DeviceHandle); + return Status; +} + + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service + ConnectController(). As a result, much of the error checking on the + parameters to Start() has been moved into this common boot service. It is + legal to call Start() from other locations, but the following calling + restrictions must be followed, or the system behavior will not be + deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a + naturally aligned EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver + specified by This must have been called with the same calling parameters, + and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL + instance. + @param[in] ControllerHandle The handle of the controller to start. This + handle must support a protocol interface + that supplies an I/O abstraction to the + driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a + device path. This parameter is ignored by + device drivers, and is optional for bus + drivers. For a bus driver, if this parameter + is NULL, then handles for all the children + of Controller are created by this driver. + If this parameter is not NULL and the first + Device Path Node is not the End of Device + Path Node, then only the handle for the + child device specified by the first Device + Path Node of RemainingDevicePath is created + by this driver. If the first Device Path + Node of RemainingDevicePath is the End of + Device Path Node, no child handle is created + by this driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a + device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources. + @retval Others The driver failded to start the device. + +**/ + +STATIC +EFI_STATUS +EFIAPI +VirtioNetDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE DeviceHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + VNET_DEV *Dev; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + MAC_ADDR_DEVICE_PATH MacNode; + VOID *ChildPciIo; + + // + // allocate space for the driver instance + // + Dev = (VNET_DEV *) AllocateZeroPool (sizeof *Dev); + if (Dev == NULL) { + return EFI_OUT_OF_RESOURCES; + } + Dev->Signature = VNET_SIG; + + // + // get PCI access to the device and keep it open + // + Status = gBS->OpenProtocol (DeviceHandle, &gEfiPciIoProtocolGuid, + (VOID **)&Dev->PciIo, This->DriverBindingHandle, + DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER); + if (EFI_ERROR (Status)) { + goto FreeVirtioNet; + } + + // + // save original PCI attributes and enable IO space access + // + Status = Dev->PciIo->Attributes (Dev->PciIo, EfiPciIoAttributeOperationGet, + 0, &Dev->OrigPciAttributes); + if (EFI_ERROR (Status)) { + goto ClosePciIo; + } + + Status = Dev->PciIo->Attributes (Dev->PciIo, + EfiPciIoAttributeOperationEnable, + EFI_PCI_IO_ATTRIBUTE_IO, NULL); + if (EFI_ERROR (Status)) { + goto ClosePciIo; + } + + // + // now we can run a basic one-shot virtio-net initialization required to + // retrieve the MAC address + // + Status = VirtioNetSnpPopulate (Dev); + if (EFI_ERROR (Status)) { + goto RestorePciAttributes; + } + + // + // get the device path of the virtio-net PCI device -- one-shot open + // + Status = gBS->OpenProtocol (DeviceHandle, &gEfiDevicePathProtocolGuid, + (VOID **)&DevicePath, This->DriverBindingHandle, + DeviceHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR (Status)) { + goto Evacuate; + } + + // + // create another device path that has the MAC address appended + // + MacNode.Header.Type = MESSAGING_DEVICE_PATH; + MacNode.Header.SubType = MSG_MAC_ADDR_DP; + SetDevicePathNodeLength (&MacNode, sizeof MacNode); + CopyMem (&MacNode.MacAddress, &Dev->Snm.CurrentAddress, + sizeof (EFI_MAC_ADDRESS)); + MacNode.IfType = Dev->Snm.IfType; + + Dev->MacDevicePath = AppendDevicePathNode (DevicePath, &MacNode.Header); + if (Dev->MacDevicePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Evacuate; + } + + // + // create a child handle with the Simple Network Protocol and the new + // device path installed on it + // + Status = gBS->InstallMultipleProtocolInterfaces (&Dev->MacHandle, + &gEfiSimpleNetworkProtocolGuid, &Dev->Snp, + &gEfiDevicePathProtocolGuid, Dev->MacDevicePath, + NULL); + if (EFI_ERROR (Status)) { + goto FreeMacDevicePath; + } + + // + // make a note that we keep this device open with PciIo for the sake of this + // child + // + Status = gBS->OpenProtocol (DeviceHandle, &gEfiPciIoProtocolGuid, + &ChildPciIo, This->DriverBindingHandle, + Dev->MacHandle, EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER); + if (EFI_ERROR (Status)) { + goto UninstallMultiple; + } + + return EFI_SUCCESS; + +UninstallMultiple: + gBS->UninstallMultipleProtocolInterfaces (Dev->MacHandle, + &gEfiDevicePathProtocolGuid, Dev->MacDevicePath, + &gEfiSimpleNetworkProtocolGuid, &Dev->Snp, + NULL); + +FreeMacDevicePath: + FreePool (Dev->MacDevicePath); + +Evacuate: + VirtioNetSnpEvacuate (Dev); + +RestorePciAttributes: + Dev->PciIo->Attributes (Dev->PciIo, EfiPciIoAttributeOperationSet, + Dev->OrigPciAttributes, NULL); + +ClosePciIo: + gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, DeviceHandle); + +FreeVirtioNet: + FreePool (Dev); + + return Status; +} + + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service + DisconnectController(). As a result, much of the error checking on the + parameters to Stop() has been moved into this common boot service. It is + legal to call Stop() from other locations, but the following calling + restrictions must be followed, or the system behavior will not be + deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous + call to this same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a + valid EFI_HANDLE. In addition, all of these handles must have been created + in this driver's Start() function, and the Start() function must have + called OpenProtocol() on ControllerHandle with an Attribute of + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL + instance. + @param[in] ControllerHandle A handle to the device being stopped. The + handle must support a bus specific I/O + protocol for the driver to use to stop the + device. + @param[in] NumberOfChildren The number of child device handles in + ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be + NULL if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device + error. + +**/ +STATIC +EFI_STATUS +EFIAPI +VirtioNetDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE DeviceHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + if (NumberOfChildren > 0) { + // + // free all resources for whose access we need the child handle, because + // the child handle is going away + // + EFI_STATUS Status; + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + VNET_DEV *Dev; + EFI_TPL OldTpl; + + ASSERT (NumberOfChildren == 1); + + Status = gBS->OpenProtocol (ChildHandleBuffer[0], + &gEfiSimpleNetworkProtocolGuid, (VOID **)&Snp, + This->DriverBindingHandle, DeviceHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + ASSERT_EFI_ERROR (Status); + Dev = VIRTIO_NET_FROM_SNP (Snp); + + // + // prevent any interference with WaitForPacket + // + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + ASSERT (Dev->MacHandle == ChildHandleBuffer[0]); + if (Dev->Snm.State != EfiSimpleNetworkStopped) { + // + // device in use, cannot stop driver instance + // + Status = EFI_DEVICE_ERROR; + } + else { + gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, Dev->MacHandle); + gBS->UninstallMultipleProtocolInterfaces (Dev->MacHandle, + &gEfiDevicePathProtocolGuid, Dev->MacDevicePath, + &gEfiSimpleNetworkProtocolGuid, &Dev->Snp, + NULL); + FreePool (Dev->MacDevicePath); + VirtioNetSnpEvacuate (Dev); + Dev->PciIo->Attributes (Dev->PciIo, EfiPciIoAttributeOperationSet, + Dev->OrigPciAttributes, NULL); + FreePool (Dev); + } + + gBS->RestoreTPL (OldTpl); + return Status; + } + + // + // release remaining resources, tied directly to the parent handle + // + gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, DeviceHandle); + + return EFI_SUCCESS; +} + + +EFI_DRIVER_BINDING_PROTOCOL gVirtioNetDriverBinding = { + &VirtioNetDriverBindingSupported, + &VirtioNetDriverBindingStart, + &VirtioNetDriverBindingStop, + 0x10, + NULL, + NULL +}; -- cgit v1.2.3