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