3 Driver Binding code and its private helpers for the virtio-net driver.
5 Copyright (C) 2013, Red Hat, Inc.
6 Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
8 This program and the accompanying materials are licensed and made available
9 under the terms and conditions of the BSD License which accompanies this
10 distribution. The full text of the license may be found at
11 http://opensource.org/licenses/bsd-license.php
13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
14 WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 #include <Library/BaseMemoryLib.h>
19 #include <Library/DevicePathLib.h>
20 #include <Library/MemoryAllocationLib.h>
21 #include <Library/UefiBootServicesTableLib.h>
23 #include "VirtioNet.h"
25 #define RECEIVE_FILTERS_NO_MCAST ((UINT32) ( \
26 EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | \
27 EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST | \
28 EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS \
32 Temporarily enable then reset the virtio-net device in order to retrieve
33 configuration values needed by Simple Network Protocol and Simple Network
36 Only VirtioNetSnpPopulate() may call this function.
38 If the function fails for any reason, the virtio-net device is moved to
39 VSTAT_FAILED instead of being reset. This serves only informative purposes
42 param[in,out] Dev The VNET_DEV structure being created for
43 the virtio-net device.
44 param[out] MacAddress MAC address configured by the host.
45 param[out] MediaPresentSupported Link status is made available by the host.
46 param[out] MediaPresent If link status is made available by the
47 host, the current link status is stored in
48 *MediaPresent. Otherwise MediaPresent is
51 @retval EFI_UNSUPPORTED The host doesn't supply a MAC address.
52 @return Status codes from VirtIo protocol members.
53 @retval EFI_SUCCESS Configuration values retrieved.
58 VirtioNetGetFeatures (
60 OUT EFI_MAC_ADDRESS
*MacAddress
,
61 OUT BOOLEAN
*MediaPresentSupported
,
62 OUT BOOLEAN
*MediaPresent
72 // Interrogate the device for features (virtio-0.9.5, 2.2.1 Device
73 // Initialization Sequence), but don't complete setting it up.
75 NextDevStat
= 0; // step 1 -- reset device
76 Status
= Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, NextDevStat
);
77 if (EFI_ERROR (Status
)) {
81 NextDevStat
|= VSTAT_ACK
; // step 2 -- acknowledge device presence
82 Status
= Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, NextDevStat
);
83 if (EFI_ERROR (Status
)) {
87 NextDevStat
|= VSTAT_DRIVER
; // step 3 -- we know how to drive it
88 Status
= Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, NextDevStat
);
89 if (EFI_ERROR (Status
)) {
94 // step 4a -- retrieve and validate features
96 Status
= Dev
->VirtIo
->GetDeviceFeatures (Dev
->VirtIo
, &Features
);
97 if (EFI_ERROR (Status
)) {
102 // get MAC address byte-wise
104 if ((Features
& VIRTIO_NET_F_MAC
) == 0) {
105 Status
= EFI_UNSUPPORTED
;
108 for (MacIdx
= 0; MacIdx
< SIZE_OF_VNET (Mac
); ++MacIdx
) {
109 Status
= Dev
->VirtIo
->ReadDevice (Dev
->VirtIo
,
110 OFFSET_OF_VNET (Mac
) + MacIdx
, // Offset
113 &MacAddress
->Addr
[MacIdx
] // Buffer
115 if (EFI_ERROR (Status
)) {
121 // check if link status is reported, and if so, what the link status is
123 if ((Features
& VIRTIO_NET_F_STATUS
) == 0) {
124 *MediaPresentSupported
= FALSE
;
127 *MediaPresentSupported
= TRUE
;
128 Status
= VIRTIO_CFG_READ (Dev
, LinkStatus
, &LinkStatus
);
129 if (EFI_ERROR (Status
)) {
132 *MediaPresent
= (BOOLEAN
) ((LinkStatus
& VIRTIO_NET_S_LINK_UP
) != 0);
136 Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
,
137 EFI_ERROR (Status
) ? VSTAT_FAILED
: 0);
144 Set up the Simple Network Protocol fields, the Simple Network Mode fields,
145 and the Exit Boot Services Event of the virtio-net driver instance.
147 This function may only be called by VirtioNetDriverBindingStart().
149 @param[in,out] Dev The VNET_DEV driver instance being created for the
152 @return Status codes from the CreateEvent() boot service or the
153 VirtioNetGetFeatures() function.
154 @retval EFI_SUCCESS Configuration successful.
159 VirtioNetSnpPopulate (
166 // We set up a function here that is asynchronously callable by an
167 // external application to check if there are any packets available for
168 // reception. The least urgent task priority level we can specify for such a
169 // "software interrupt" is TPL_CALLBACK.
171 // TPL_CALLBACK is also the maximum TPL an SNP implementation is allowed to
172 // run at (see 6.1 Event, Timer, and Task Priority Services in the UEFI
173 // Specification 2.3.1+errC).
175 // Since we raise our TPL to TPL_CALLBACK in every single function that
176 // accesses the device, and the external application also queues its interest
177 // for received packets at the same TPL_CALLBACK, in effect the
178 // VirtioNetIsPacketAvailable() function will never interrupt any
179 // device-accessing driver function, it will be scheduled in isolation.
181 // TPL_CALLBACK (which basically this entire driver runs at) is allowed
182 // for "[l]ong term operations (such as file system operations and disk
183 // I/O)". Because none of our functions block, we'd satisfy an even stronger
186 Status
= gBS
->CreateEvent (EVT_NOTIFY_WAIT
, TPL_CALLBACK
,
187 &VirtioNetIsPacketAvailable
, Dev
, &Dev
->Snp
.WaitForPacket
);
188 if (EFI_ERROR (Status
)) {
192 Dev
->Snp
.Revision
= EFI_SIMPLE_NETWORK_PROTOCOL_REVISION
;
193 Dev
->Snp
.Start
= &VirtioNetStart
;
194 Dev
->Snp
.Stop
= &VirtioNetStop
;
195 Dev
->Snp
.Initialize
= &VirtioNetInitialize
;
196 Dev
->Snp
.Reset
= &VirtioNetReset
;
197 Dev
->Snp
.Shutdown
= &VirtioNetShutdown
;
198 Dev
->Snp
.ReceiveFilters
= &VirtioNetReceiveFilters
;
199 Dev
->Snp
.StationAddress
= &VirtioNetStationAddress
;
200 Dev
->Snp
.Statistics
= &VirtioNetStatistics
;
201 Dev
->Snp
.MCastIpToMac
= &VirtioNetMcastIpToMac
;
202 Dev
->Snp
.NvData
= &VirtioNetNvData
;
203 Dev
->Snp
.GetStatus
= &VirtioNetGetStatus
;
204 Dev
->Snp
.Transmit
= &VirtioNetTransmit
;
205 Dev
->Snp
.Receive
= &VirtioNetReceive
;
206 Dev
->Snp
.Mode
= &Dev
->Snm
;
208 Dev
->Snm
.State
= EfiSimpleNetworkStopped
;
209 Dev
->Snm
.HwAddressSize
= SIZE_OF_VNET (Mac
);
210 Dev
->Snm
.MediaHeaderSize
= SIZE_OF_VNET (Mac
) + // dst MAC
211 SIZE_OF_VNET (Mac
) + // src MAC
213 Dev
->Snm
.MaxPacketSize
= 1500;
214 Dev
->Snm
.NvRamSize
= 0;
215 Dev
->Snm
.NvRamAccessSize
= 0;
216 Dev
->Snm
.ReceiveFilterMask
= RECEIVE_FILTERS_NO_MCAST
;
217 Dev
->Snm
.ReceiveFilterSetting
= RECEIVE_FILTERS_NO_MCAST
;
218 Dev
->Snm
.MaxMCastFilterCount
= 0;
219 Dev
->Snm
.MCastFilterCount
= 0;
220 Dev
->Snm
.IfType
= 1; // ethernet
221 Dev
->Snm
.MacAddressChangeable
= FALSE
;
222 Dev
->Snm
.MultipleTxSupported
= TRUE
;
224 ASSERT (SIZE_OF_VNET (Mac
) <= sizeof (EFI_MAC_ADDRESS
));
226 Status
= VirtioNetGetFeatures (Dev
, &Dev
->Snm
.CurrentAddress
,
227 &Dev
->Snm
.MediaPresentSupported
, &Dev
->Snm
.MediaPresent
);
228 if (EFI_ERROR (Status
)) {
229 goto CloseWaitForPacket
;
231 CopyMem (&Dev
->Snm
.PermanentAddress
, &Dev
->Snm
.CurrentAddress
,
233 SetMem (&Dev
->Snm
.BroadcastAddress
, SIZE_OF_VNET (Mac
), 0xFF);
236 // VirtioNetExitBoot() is queued by ExitBootServices(); its purpose is to
237 // cancel any pending virtio requests. The TPL_CALLBACK reasoning is
238 // identical to the one above. There's one difference: this kind of
239 // event is "globally visible", which means it can be signalled as soon as
240 // we create it. We haven't raised our TPL here, hence VirtioNetExitBoot()
241 // could be entered immediately. VirtioNetExitBoot() checks Dev->Snm.State,
244 Status
= gBS
->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES
, TPL_CALLBACK
,
245 &VirtioNetExitBoot
, Dev
, &Dev
->ExitBoot
);
246 if (EFI_ERROR (Status
)) {
247 goto CloseWaitForPacket
;
253 gBS
->CloseEvent (Dev
->Snp
.WaitForPacket
);
259 Release any resources allocated by VirtioNetSnpPopulate().
261 This function may only be called by VirtioNetDriverBindingStart(), when
262 rolling back a partial, failed driver instance creation, and by
263 VirtioNetDriverBindingStop(), when disconnecting a virtio-net device from the
266 @param[in,out] Dev The VNET_DEV driver instance being destroyed.
271 VirtioNetSnpEvacuate (
276 // This function runs either at TPL_CALLBACK already (from
277 // VirtioNetDriverBindingStop()), or it is part of a teardown following
278 // a partial, failed construction in VirtioNetDriverBindingStart(), when
279 // WaitForPacket was never accessible to the world.
281 gBS
->CloseEvent (Dev
->ExitBoot
);
282 gBS
->CloseEvent (Dev
->Snp
.WaitForPacket
);
287 Tests to see if this driver supports a given controller. If a child device is
288 provided, it further tests to see if this driver supports creating a handle
289 for the specified child device.
291 This function checks to see if the driver specified by This supports the
292 device specified by ControllerHandle. Drivers will typically use the device
293 path attached to ControllerHandle and/or the services from the bus I/O
294 abstraction attached to ControllerHandle to determine if the driver supports
295 ControllerHandle. This function may be called many times during platform
296 initialization. In order to reduce boot times, the tests performed by this
297 function must be very small, and take as little time as possible to execute.
298 This function must not change the state of any hardware devices, and this
299 function must be aware that the device specified by ControllerHandle may
300 already be managed by the same driver or a different driver. This function
301 must match its calls to AllocatePages() with FreePages(), AllocatePool() with
302 FreePool(), and OpenProtocol() with CloseProtocol(). Because ControllerHandle
303 may have been previously started by the same driver, if a protocol is already
304 in the opened state, then it must not be closed with CloseProtocol(). This is
305 required to guarantee the state of ControllerHandle is not modified by this
308 @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL
310 @param[in] ControllerHandle The handle of the controller to test. This
311 handle must support a protocol interface
312 that supplies an I/O abstraction to the
314 @param[in] RemainingDevicePath A pointer to the remaining portion of a
315 device path. This parameter is ignored by
316 device drivers, and is optional for bus
317 drivers. For bus drivers, if this parameter
318 is not NULL, then the bus driver must
319 determine if the bus controller specified by
320 ControllerHandle and the child controller
321 specified by RemainingDevicePath are both
322 supported by this bus driver.
324 @retval EFI_SUCCESS The device specified by ControllerHandle and
325 RemainingDevicePath is supported by the
326 driver specified by This.
327 @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
328 RemainingDevicePath is already being managed
329 by the driver specified by This.
330 @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
331 RemainingDevicePath is already being managed
332 by a different driver or an application that
333 requires exclusive access. Currently not
335 @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
336 RemainingDevicePath is not supported by the
337 driver specified by This.
343 VirtioNetDriverBindingSupported (
344 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
345 IN EFI_HANDLE DeviceHandle
,
346 IN EFI_DEVICE_PATH_PROTOCOL
*RemainingDevicePath
350 VIRTIO_DEVICE_PROTOCOL
*VirtIo
;
353 // Attempt to open the device with the VirtIo set of interfaces. On success,
354 // the protocol is "instantiated" for the VirtIo device. Covers duplicate open
355 // attempts (EFI_ALREADY_STARTED).
357 Status
= gBS
->OpenProtocol (
358 DeviceHandle
, // candidate device
359 &gVirtioDeviceProtocolGuid
, // for generic VirtIo access
360 (VOID
**)&VirtIo
, // handle to instantiate
361 This
->DriverBindingHandle
, // requestor driver identity
362 DeviceHandle
, // ControllerHandle, according to
363 // the UEFI Driver Model
364 EFI_OPEN_PROTOCOL_BY_DRIVER
// get exclusive VirtIo access to
365 // the device; to be released
367 if (EFI_ERROR (Status
)) {
371 if (VirtIo
->SubSystemDeviceId
!= VIRTIO_SUBSYSTEM_NETWORK_CARD
) {
372 Status
= EFI_UNSUPPORTED
;
376 // We needed VirtIo access only transitorily, to see whether we support the
379 gBS
->CloseProtocol (DeviceHandle
, &gVirtioDeviceProtocolGuid
,
380 This
->DriverBindingHandle
, DeviceHandle
);
386 Starts a device controller or a bus controller.
388 The Start() function is designed to be invoked from the EFI boot service
389 ConnectController(). As a result, much of the error checking on the
390 parameters to Start() has been moved into this common boot service. It is
391 legal to call Start() from other locations, but the following calling
392 restrictions must be followed, or the system behavior will not be
394 1. ControllerHandle must be a valid EFI_HANDLE.
395 2. If RemainingDevicePath is not NULL, then it must be a pointer to a
396 naturally aligned EFI_DEVICE_PATH_PROTOCOL.
397 3. Prior to calling Start(), the Supported() function for the driver
398 specified by This must have been called with the same calling parameters,
399 and Supported() must have returned EFI_SUCCESS.
401 @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL
403 @param[in] ControllerHandle The handle of the controller to start. This
404 handle must support a protocol interface
405 that supplies an I/O abstraction to the
407 @param[in] RemainingDevicePath A pointer to the remaining portion of a
408 device path. This parameter is ignored by
409 device drivers, and is optional for bus
410 drivers. For a bus driver, if this parameter
411 is NULL, then handles for all the children
412 of Controller are created by this driver.
413 If this parameter is not NULL and the first
414 Device Path Node is not the End of Device
415 Path Node, then only the handle for the
416 child device specified by the first Device
417 Path Node of RemainingDevicePath is created
418 by this driver. If the first Device Path
419 Node of RemainingDevicePath is the End of
420 Device Path Node, no child handle is created
423 @retval EFI_SUCCESS The device was started.
424 @retval EFI_DEVICE_ERROR The device could not be started due to a
425 device error.Currently not implemented.
426 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a
428 @retval Others The driver failded to start the device.
435 VirtioNetDriverBindingStart (
436 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
437 IN EFI_HANDLE DeviceHandle
,
438 IN EFI_DEVICE_PATH_PROTOCOL
*RemainingDevicePath
443 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
444 MAC_ADDR_DEVICE_PATH MacNode
;
448 // allocate space for the driver instance
450 Dev
= (VNET_DEV
*) AllocateZeroPool (sizeof *Dev
);
452 return EFI_OUT_OF_RESOURCES
;
454 Dev
->Signature
= VNET_SIG
;
456 Status
= gBS
->OpenProtocol (DeviceHandle
, &gVirtioDeviceProtocolGuid
,
457 (VOID
**)&Dev
->VirtIo
, This
->DriverBindingHandle
,
458 DeviceHandle
, EFI_OPEN_PROTOCOL_BY_DRIVER
);
459 if (EFI_ERROR (Status
)) {
464 // now we can run a basic one-shot virtio-net initialization required to
465 // retrieve the MAC address
467 Status
= VirtioNetSnpPopulate (Dev
);
468 if (EFI_ERROR (Status
)) {
473 // get the device path of the virtio-net device -- one-shot open
475 Status
= gBS
->OpenProtocol (DeviceHandle
, &gEfiDevicePathProtocolGuid
,
476 (VOID
**)&DevicePath
, This
->DriverBindingHandle
,
477 DeviceHandle
, EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
478 if (EFI_ERROR (Status
)) {
483 // create another device path that has the MAC address appended
485 MacNode
.Header
.Type
= MESSAGING_DEVICE_PATH
;
486 MacNode
.Header
.SubType
= MSG_MAC_ADDR_DP
;
487 SetDevicePathNodeLength (&MacNode
, sizeof MacNode
);
488 CopyMem (&MacNode
.MacAddress
, &Dev
->Snm
.CurrentAddress
,
489 sizeof (EFI_MAC_ADDRESS
));
490 MacNode
.IfType
= Dev
->Snm
.IfType
;
492 Dev
->MacDevicePath
= AppendDevicePathNode (DevicePath
, &MacNode
.Header
);
493 if (Dev
->MacDevicePath
== NULL
) {
494 Status
= EFI_OUT_OF_RESOURCES
;
499 // create a child handle with the Simple Network Protocol and the new
500 // device path installed on it
502 Status
= gBS
->InstallMultipleProtocolInterfaces (&Dev
->MacHandle
,
503 &gEfiSimpleNetworkProtocolGuid
, &Dev
->Snp
,
504 &gEfiDevicePathProtocolGuid
, Dev
->MacDevicePath
,
506 if (EFI_ERROR (Status
)) {
507 goto FreeMacDevicePath
;
511 // make a note that we keep this device open with VirtIo for the sake of this
514 Status
= gBS
->OpenProtocol (DeviceHandle
, &gVirtioDeviceProtocolGuid
,
515 &ChildVirtIo
, This
->DriverBindingHandle
,
516 Dev
->MacHandle
, EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
);
517 if (EFI_ERROR (Status
)) {
518 goto UninstallMultiple
;
524 gBS
->UninstallMultipleProtocolInterfaces (Dev
->MacHandle
,
525 &gEfiDevicePathProtocolGuid
, Dev
->MacDevicePath
,
526 &gEfiSimpleNetworkProtocolGuid
, &Dev
->Snp
,
530 FreePool (Dev
->MacDevicePath
);
533 VirtioNetSnpEvacuate (Dev
);
536 gBS
->CloseProtocol (DeviceHandle
, &gVirtioDeviceProtocolGuid
,
537 This
->DriverBindingHandle
, DeviceHandle
);
547 Stops a device controller or a bus controller.
549 The Stop() function is designed to be invoked from the EFI boot service
550 DisconnectController(). As a result, much of the error checking on the
551 parameters to Stop() has been moved into this common boot service. It is
552 legal to call Stop() from other locations, but the following calling
553 restrictions must be followed, or the system behavior will not be
555 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous
556 call to this same driver's Start() function.
557 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a
558 valid EFI_HANDLE. In addition, all of these handles must have been created
559 in this driver's Start() function, and the Start() function must have
560 called OpenProtocol() on ControllerHandle with an Attribute of
561 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
563 @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL
565 @param[in] ControllerHandle A handle to the device being stopped. The
566 handle must support a bus specific I/O
567 protocol for the driver to use to stop the
569 @param[in] NumberOfChildren The number of child device handles in
571 @param[in] ChildHandleBuffer An array of child handles to be freed. May be
572 NULL if NumberOfChildren is 0.
574 @retval EFI_SUCCESS The device was stopped.
575 @retval EFI_DEVICE_ERROR The device could not be stopped due to a device
582 VirtioNetDriverBindingStop (
583 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
584 IN EFI_HANDLE DeviceHandle
,
585 IN UINTN NumberOfChildren
,
586 IN EFI_HANDLE
*ChildHandleBuffer
589 if (NumberOfChildren
> 0) {
591 // free all resources for whose access we need the child handle, because
592 // the child handle is going away
595 EFI_SIMPLE_NETWORK_PROTOCOL
*Snp
;
599 ASSERT (NumberOfChildren
== 1);
601 Status
= gBS
->OpenProtocol (ChildHandleBuffer
[0],
602 &gEfiSimpleNetworkProtocolGuid
, (VOID
**)&Snp
,
603 This
->DriverBindingHandle
, DeviceHandle
,
604 EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
605 ASSERT_EFI_ERROR (Status
);
606 Dev
= VIRTIO_NET_FROM_SNP (Snp
);
609 // prevent any interference with WaitForPacket
611 OldTpl
= gBS
->RaiseTPL (TPL_CALLBACK
);
613 ASSERT (Dev
->MacHandle
== ChildHandleBuffer
[0]);
614 if (Dev
->Snm
.State
!= EfiSimpleNetworkStopped
) {
616 // device in use, cannot stop driver instance
618 Status
= EFI_DEVICE_ERROR
;
621 gBS
->CloseProtocol (DeviceHandle
, &gVirtioDeviceProtocolGuid
,
622 This
->DriverBindingHandle
, Dev
->MacHandle
);
623 gBS
->UninstallMultipleProtocolInterfaces (Dev
->MacHandle
,
624 &gEfiDevicePathProtocolGuid
, Dev
->MacDevicePath
,
625 &gEfiSimpleNetworkProtocolGuid
, &Dev
->Snp
,
627 FreePool (Dev
->MacDevicePath
);
628 VirtioNetSnpEvacuate (Dev
);
632 gBS
->RestoreTPL (OldTpl
);
637 // release remaining resources, tied directly to the parent handle
639 gBS
->CloseProtocol (DeviceHandle
, &gVirtioDeviceProtocolGuid
,
640 This
->DriverBindingHandle
, DeviceHandle
);
646 EFI_DRIVER_BINDING_PROTOCOL gVirtioNetDriverBinding
= {
647 &VirtioNetDriverBindingSupported
,
648 &VirtioNetDriverBindingStart
,
649 &VirtioNetDriverBindingStop
,