Fix few typos in comments and documentation. Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Signed-off-by: Antoine Coeur <coeur@gmx.fr> Reviewed-by: Philippe Mathieu-Daude <philmd@redhat.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Signed-off-by: Philippe Mathieu-Daude <philmd@redhat.com> Message-Id: <20200207010831.9046-59-philmd@redhat.com>
		
			
				
	
	
		
			648 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			648 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
| 
 | |
|   Driver Binding code and its private helpers for the virtio-net driver.
 | |
| 
 | |
|   Copyright (C) 2013, Red Hat, Inc.
 | |
|   Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
 | |
| 
 | |
|   SPDX-License-Identifier: BSD-2-Clause-Patent
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include <Library/BaseMemoryLib.h>
 | |
| #include <Library/DevicePathLib.h>
 | |
| #include <Library/MemoryAllocationLib.h>
 | |
| #include <Library/UefiBootServicesTableLib.h>
 | |
| 
 | |
| #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 VirtIo protocol members.
 | |
|   @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;
 | |
|   UINT64     Features;
 | |
|   UINTN      MacIdx;
 | |
|   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 = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   NextDevStat |= VSTAT_ACK;    // step 2 -- acknowledge device presence
 | |
|   Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     goto YieldDevice;
 | |
|   }
 | |
| 
 | |
|   NextDevStat |= VSTAT_DRIVER; // step 3 -- we know how to drive it
 | |
|   Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     goto YieldDevice;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // step 4a -- retrieve and validate features
 | |
|   //
 | |
|   Status = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo, &Features);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     goto YieldDevice;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // get MAC address byte-wise
 | |
|   //
 | |
|   if ((Features & VIRTIO_NET_F_MAC) == 0) {
 | |
|     Status = EFI_UNSUPPORTED;
 | |
|     goto YieldDevice;
 | |
|   }
 | |
|   for (MacIdx = 0; MacIdx < SIZE_OF_VNET (Mac); ++MacIdx) {
 | |
|     Status = Dev->VirtIo->ReadDevice (Dev->VirtIo,
 | |
|                             OFFSET_OF_VNET (Mac) + MacIdx, // Offset
 | |
|                             1,                             // FieldSize
 | |
|                             1,                             // BufferSize
 | |
|                             &MacAddress->Addr[MacIdx]      // 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, LinkStatus, &LinkStatus);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       goto YieldDevice;
 | |
|     }
 | |
|     *MediaPresent = (BOOLEAN) ((LinkStatus & VIRTIO_NET_S_LINK_UP) != 0);
 | |
|   }
 | |
| 
 | |
| YieldDevice:
 | |
|   Dev->VirtIo->SetDeviceStatus (Dev->VirtIo,
 | |
|     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 (Mac);
 | |
|   Dev->Snm.MediaHeaderSize       = SIZE_OF_VNET (Mac) + // dst MAC
 | |
|                                    SIZE_OF_VNET (Mac) + // 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 (Mac) <= 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 (Mac));
 | |
|   SetMem (&Dev->Snm.BroadcastAddress, SIZE_OF_VNET (Mac), 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;
 | |
|   VIRTIO_DEVICE_PROTOCOL *VirtIo;
 | |
| 
 | |
|   //
 | |
|   // Attempt to open the device with the VirtIo set of interfaces. On success,
 | |
|   // the protocol is "instantiated" for the VirtIo device. Covers duplicate open
 | |
|   // attempts (EFI_ALREADY_STARTED).
 | |
|   //
 | |
|   Status = gBS->OpenProtocol (
 | |
|                   DeviceHandle,               // candidate device
 | |
|                   &gVirtioDeviceProtocolGuid, // for generic VirtIo access
 | |
|                   (VOID **)&VirtIo,           // handle to instantiate
 | |
|                   This->DriverBindingHandle,  // requestor driver identity
 | |
|                   DeviceHandle,               // ControllerHandle, according to
 | |
|                                               // the UEFI Driver Model
 | |
|                   EFI_OPEN_PROTOCOL_BY_DRIVER // get exclusive VirtIo access to
 | |
|                                               // the device; to be released
 | |
|                   );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   if (VirtIo->SubSystemDeviceId != VIRTIO_SUBSYSTEM_NETWORK_CARD) {
 | |
|     Status = EFI_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // We needed VirtIo access only transitorily, to see whether we support the
 | |
|   // device or not.
 | |
|   //
 | |
|   gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
 | |
|          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 failed 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                     *ChildVirtIo;
 | |
| 
 | |
|   //
 | |
|   // allocate space for the driver instance
 | |
|   //
 | |
|   Dev = (VNET_DEV *) AllocateZeroPool (sizeof *Dev);
 | |
|   if (Dev == NULL) {
 | |
|     return EFI_OUT_OF_RESOURCES;
 | |
|   }
 | |
|   Dev->Signature = VNET_SIG;
 | |
| 
 | |
|   Status = gBS->OpenProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
 | |
|                   (VOID **)&Dev->VirtIo, This->DriverBindingHandle,
 | |
|                   DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     goto FreeVirtioNet;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // 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 CloseVirtIo;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // get the device path of the virtio-net 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 VirtIo for the sake of this
 | |
|   // child
 | |
|   //
 | |
|   Status = gBS->OpenProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
 | |
|                   &ChildVirtIo, 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);
 | |
| 
 | |
| CloseVirtIo:
 | |
|   gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
 | |
|          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, &gVirtioDeviceProtocolGuid,
 | |
|              This->DriverBindingHandle, Dev->MacHandle);
 | |
|       gBS->UninstallMultipleProtocolInterfaces (Dev->MacHandle,
 | |
|              &gEfiDevicePathProtocolGuid,    Dev->MacDevicePath,
 | |
|              &gEfiSimpleNetworkProtocolGuid, &Dev->Snp,
 | |
|              NULL);
 | |
|       FreePool (Dev->MacDevicePath);
 | |
|       VirtioNetSnpEvacuate (Dev);
 | |
|       FreePool (Dev);
 | |
|     }
 | |
| 
 | |
|     gBS->RestoreTPL (OldTpl);
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // release remaining resources, tied directly to the parent handle
 | |
|   //
 | |
|   gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
 | |
|          This->DriverBindingHandle, DeviceHandle);
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| EFI_DRIVER_BINDING_PROTOCOL gVirtioNetDriverBinding = {
 | |
|   &VirtioNetDriverBindingSupported,
 | |
|   &VirtioNetDriverBindingStart,
 | |
|   &VirtioNetDriverBindingStop,
 | |
|   0x10,
 | |
|   NULL,
 | |
|   NULL
 | |
| };
 |