/** @file | |
This driver produces EFI_RNG_PROTOCOL instances for virtio-rng devices. | |
The implementation is based on OvmfPkg/VirtioScsiDxe/VirtioScsi.c | |
Copyright (C) 2012, Red Hat, Inc. | |
Copyright (c) 2012 - 2014, Intel Corporation. All rights reserved.<BR> | |
Copyright (c) 2017, AMD Inc, All rights reserved.<BR> | |
This driver: | |
Copyright (C) 2016, Linaro Ltd. | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/DebugLib.h> | |
#include <Library/MemoryAllocationLib.h> | |
#include <Library/UefiBootServicesTableLib.h> | |
#include <Library/UefiLib.h> | |
#include <Library/VirtioLib.h> | |
#include "VirtioRng.h" | |
/** | |
Returns information about the random number generation implementation. | |
@param[in] This A pointer to the EFI_RNG_PROTOCOL | |
instance. | |
@param[in,out] RNGAlgorithmListSize On input, the size in bytes of | |
RNGAlgorithmList. | |
On output with a return code of | |
EFI_SUCCESS, the size in bytes of the | |
data returned in RNGAlgorithmList. On | |
output with a return code of | |
EFI_BUFFER_TOO_SMALL, the size of | |
RNGAlgorithmList required to obtain the | |
list. | |
@param[out] RNGAlgorithmList A caller-allocated memory buffer filled | |
by the driver with one EFI_RNG_ALGORITHM | |
element for each supported RNG algorithm. | |
The list must not change across multiple | |
calls to the same driver. The first | |
algorithm in the list is the default | |
algorithm for the driver. | |
@retval EFI_SUCCESS The RNG algorithm list was returned | |
successfully. | |
@retval EFI_UNSUPPORTED The services is not supported by this | |
driver. | |
@retval EFI_DEVICE_ERROR The list of algorithms could not be | |
retrieved due to a hardware or firmware | |
error. | |
@retval EFI_INVALID_PARAMETER One or more of the parameters are | |
incorrect. | |
@retval EFI_BUFFER_TOO_SMALL The buffer RNGAlgorithmList is too small | |
to hold the result. | |
**/ | |
STATIC | |
EFI_STATUS | |
EFIAPI | |
VirtioRngGetInfo ( | |
IN EFI_RNG_PROTOCOL *This, | |
IN OUT UINTN *RNGAlgorithmListSize, | |
OUT EFI_RNG_ALGORITHM *RNGAlgorithmList | |
) | |
{ | |
if ((This == NULL) || (RNGAlgorithmListSize == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (*RNGAlgorithmListSize < sizeof (EFI_RNG_ALGORITHM)) { | |
*RNGAlgorithmListSize = sizeof (EFI_RNG_ALGORITHM); | |
return EFI_BUFFER_TOO_SMALL; | |
} | |
if (RNGAlgorithmList == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
*RNGAlgorithmListSize = sizeof (EFI_RNG_ALGORITHM); | |
CopyGuid (RNGAlgorithmList, &gEfiRngAlgorithmRaw); | |
return EFI_SUCCESS; | |
} | |
/** | |
Produces and returns an RNG value using either the default or specified RNG | |
algorithm. | |
@param[in] This A pointer to the EFI_RNG_PROTOCOL | |
instance. | |
@param[in] RNGAlgorithm A pointer to the EFI_RNG_ALGORITHM that | |
identifies the RNG algorithm to use. May | |
be NULL in which case the function will | |
use its default RNG algorithm. | |
@param[in] RNGValueLength The length in bytes of the memory buffer | |
pointed to by RNGValue. The driver shall | |
return exactly this numbers of bytes. | |
@param[out] RNGValue A caller-allocated memory buffer filled | |
by the driver with the resulting RNG | |
value. | |
@retval EFI_SUCCESS The RNG value was returned successfully. | |
@retval EFI_UNSUPPORTED The algorithm specified by RNGAlgorithm | |
is not supported by this driver. | |
@retval EFI_DEVICE_ERROR An RNG value could not be retrieved due | |
to a hardware or firmware error. | |
@retval EFI_NOT_READY There is not enough random data available | |
to satisfy the length requested by | |
RNGValueLength. | |
@retval EFI_INVALID_PARAMETER RNGValue is NULL or RNGValueLength is | |
zero. | |
**/ | |
STATIC | |
EFI_STATUS | |
EFIAPI | |
VirtioRngGetRNG ( | |
IN EFI_RNG_PROTOCOL *This, | |
IN EFI_RNG_ALGORITHM *RNGAlgorithm OPTIONAL, | |
IN UINTN RNGValueLength, | |
OUT UINT8 *RNGValue | |
) | |
{ | |
VIRTIO_RNG_DEV *Dev; | |
DESC_INDICES Indices; | |
volatile UINT8 *Buffer; | |
UINTN Index; | |
UINT32 Len; | |
UINT32 BufferSize; | |
EFI_STATUS Status; | |
EFI_PHYSICAL_ADDRESS DeviceAddress; | |
VOID *Mapping; | |
if ((This == NULL) || (RNGValueLength == 0) || (RNGValue == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// We only support the raw algorithm, so reject requests for anything else | |
// | |
if ((RNGAlgorithm != NULL) && | |
!CompareGuid (RNGAlgorithm, &gEfiRngAlgorithmRaw)) | |
{ | |
return EFI_UNSUPPORTED; | |
} | |
Buffer = (volatile UINT8 *)AllocatePool (RNGValueLength); | |
if (Buffer == NULL) { | |
return EFI_DEVICE_ERROR; | |
} | |
Dev = VIRTIO_ENTROPY_SOURCE_FROM_RNG (This); | |
if (!Dev->Ready) { | |
DEBUG ((DEBUG_INFO, "%a: not ready\n", __func__)); | |
return EFI_DEVICE_ERROR; | |
} | |
// | |
// Map Buffer's system physical address to device address | |
// | |
Status = VirtioMapAllBytesInSharedBuffer ( | |
Dev->VirtIo, | |
VirtioOperationBusMasterWrite, | |
(VOID *)Buffer, | |
RNGValueLength, | |
&DeviceAddress, | |
&Mapping | |
); | |
if (EFI_ERROR (Status)) { | |
Status = EFI_DEVICE_ERROR; | |
goto FreeBuffer; | |
} | |
// | |
// The Virtio RNG device may return less data than we asked it to, and can | |
// only return MAX_UINT32 bytes per invocation. So loop as long as needed to | |
// get all the entropy we were asked for. | |
// | |
for (Index = 0; Index < RNGValueLength; Index += Len) { | |
BufferSize = (UINT32)MIN (RNGValueLength - Index, (UINTN)MAX_UINT32); | |
VirtioPrepare (&Dev->Ring, &Indices); | |
VirtioAppendDesc ( | |
&Dev->Ring, | |
DeviceAddress + Index, | |
BufferSize, | |
VRING_DESC_F_WRITE, | |
&Indices | |
); | |
if (VirtioFlush (Dev->VirtIo, 0, &Dev->Ring, &Indices, &Len) != | |
EFI_SUCCESS) | |
{ | |
Status = EFI_DEVICE_ERROR; | |
goto UnmapBuffer; | |
} | |
ASSERT (Len > 0); | |
ASSERT (Len <= BufferSize); | |
} | |
// | |
// Unmap the device buffer before accessing it. | |
// | |
Status = Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Mapping); | |
if (EFI_ERROR (Status)) { | |
Status = EFI_DEVICE_ERROR; | |
goto FreeBuffer; | |
} | |
for (Index = 0; Index < RNGValueLength; Index++) { | |
RNGValue[Index] = Buffer[Index]; | |
} | |
Status = EFI_SUCCESS; | |
UnmapBuffer: | |
// | |
// If we are reached here due to the error then unmap the buffer otherwise | |
// the buffer is already unmapped after VirtioFlush(). | |
// | |
if (EFI_ERROR (Status)) { | |
Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Mapping); | |
} | |
FreeBuffer: | |
FreePool ((VOID *)Buffer); | |
return Status; | |
} | |
STATIC | |
EFI_STATUS | |
EFIAPI | |
VirtioRngInit ( | |
IN OUT VIRTIO_RNG_DEV *Dev | |
) | |
{ | |
UINT8 NextDevStat; | |
EFI_STATUS Status; | |
UINT16 QueueSize; | |
UINT64 Features; | |
UINT64 RingBaseShift; | |
// | |
// Execute virtio-0.9.5, 2.2.1 Device Initialization Sequence. | |
// | |
NextDevStat = 0; // step 1 -- reset device | |
Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
NextDevStat |= VSTAT_ACK; // step 2 -- acknowledge device presence | |
Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
NextDevStat |= VSTAT_DRIVER; // step 3 -- we know how to drive it | |
Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
// | |
// Set Page Size - MMIO VirtIo Specific | |
// | |
Status = Dev->VirtIo->SetPageSize (Dev->VirtIo, EFI_PAGE_SIZE); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
// | |
// step 4a -- retrieve and validate features | |
// | |
Status = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo, &Features); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
Features &= VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM; | |
// | |
// In virtio-1.0, feature negotiation is expected to complete before queue | |
// discovery, and the device can also reject the selected set of features. | |
// | |
if (Dev->VirtIo->Revision >= VIRTIO_SPEC_REVISION (1, 0, 0)) { | |
Status = Virtio10WriteFeatures (Dev->VirtIo, Features, &NextDevStat); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
} | |
// | |
// step 4b -- allocate request virtqueue, just use #0 | |
// | |
Status = Dev->VirtIo->SetQueueSel (Dev->VirtIo, 0); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
Status = Dev->VirtIo->GetQueueNumMax (Dev->VirtIo, &QueueSize); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
// | |
// VirtioRngGetRNG() uses one descriptor | |
// | |
if (QueueSize < 1) { | |
Status = EFI_UNSUPPORTED; | |
goto Failed; | |
} | |
Status = VirtioRingInit (Dev->VirtIo, QueueSize, &Dev->Ring); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
// | |
// If anything fails from here on, we must release the ring resources. | |
// | |
Status = VirtioRingMap ( | |
Dev->VirtIo, | |
&Dev->Ring, | |
&RingBaseShift, | |
&Dev->RingMap | |
); | |
if (EFI_ERROR (Status)) { | |
goto ReleaseQueue; | |
} | |
// | |
// Additional steps for MMIO: align the queue appropriately, and set the | |
// size. If anything fails from here on, we must unmap the ring resources. | |
// | |
Status = Dev->VirtIo->SetQueueNum (Dev->VirtIo, QueueSize); | |
if (EFI_ERROR (Status)) { | |
goto UnmapQueue; | |
} | |
Status = Dev->VirtIo->SetQueueAlign (Dev->VirtIo, EFI_PAGE_SIZE); | |
if (EFI_ERROR (Status)) { | |
goto UnmapQueue; | |
} | |
// | |
// step 4c -- Report GPFN (guest-physical frame number) of queue. | |
// | |
Status = Dev->VirtIo->SetQueueAddress ( | |
Dev->VirtIo, | |
&Dev->Ring, | |
RingBaseShift | |
); | |
if (EFI_ERROR (Status)) { | |
goto UnmapQueue; | |
} | |
// | |
// step 5 -- Report understood features and guest-tuneables. | |
// | |
if (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) { | |
Features &= ~(UINT64)(VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM); | |
Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo, Features); | |
if (EFI_ERROR (Status)) { | |
goto UnmapQueue; | |
} | |
} | |
// | |
// step 6 -- initialization complete | |
// | |
NextDevStat |= VSTAT_DRIVER_OK; | |
Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); | |
if (EFI_ERROR (Status)) { | |
goto UnmapQueue; | |
} | |
// | |
// populate the exported interface's attributes | |
// | |
Dev->Rng.GetInfo = VirtioRngGetInfo; | |
Dev->Rng.GetRNG = VirtioRngGetRNG; | |
Dev->Ready = TRUE; | |
return EFI_SUCCESS; | |
UnmapQueue: | |
Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->RingMap); | |
ReleaseQueue: | |
VirtioRingUninit (Dev->VirtIo, &Dev->Ring); | |
Failed: | |
// | |
// Notify the host about our failure to setup: virtio-0.9.5, 2.2.2.1 Device | |
// Status. VirtIo access failure here should not mask the original error. | |
// | |
NextDevStat |= VSTAT_FAILED; | |
Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); | |
return Status; // reached only via Failed above | |
} | |
STATIC | |
VOID | |
EFIAPI | |
VirtioRngUninit ( | |
IN OUT VIRTIO_RNG_DEV *Dev | |
) | |
{ | |
// | |
// Reset the virtual device -- see virtio-0.9.5, 2.2.2.1 Device Status. When | |
// VIRTIO_CFG_WRITE() returns, the host will have learned to stay away from | |
// the old comms area. | |
// | |
Dev->Ready = FALSE; | |
Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0); | |
Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->RingMap); | |
VirtioRingUninit (Dev->VirtIo, &Dev->Ring); | |
} | |
// | |
// Event notification function enqueued by ExitBootServices(). | |
// | |
STATIC | |
VOID | |
EFIAPI | |
VirtioRngExitBoot ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
VIRTIO_RNG_DEV *Dev; | |
DEBUG ((DEBUG_INFO, "%a: Context=0x%p\n", __func__, Context)); | |
// | |
// Reset the device. This causes the hypervisor to forget about the virtio | |
// ring. | |
// | |
// We allocated said ring in EfiBootServicesData type memory, and code | |
// executing after ExitBootServices() is permitted to overwrite it. | |
// | |
Dev = Context; | |
Dev->Ready = FALSE; | |
Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0); | |
} | |
// | |
// Probe, start and stop functions of this driver, called by the DXE core for | |
// specific devices. | |
// | |
// The following specifications document these interfaces: | |
// - Driver Writer's Guide for UEFI 2.3.1 v1.01, 9 Driver Binding Protocol | |
// - UEFI Spec 2.3.1 + Errata C, 10.1 EFI Driver Binding Protocol | |
// | |
// The implementation follows: | |
// - Driver Writer's Guide for UEFI 2.3.1 v1.01 | |
// - 5.1.3.4 OpenProtocol() and CloseProtocol() | |
// - UEFI Spec 2.3.1 + Errata C | |
// - 6.3 Protocol Handler Services | |
// | |
STATIC | |
EFI_STATUS | |
EFIAPI | |
VirtioRngDriverBindingSupported ( | |
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_ENTROPY_SOURCE) { | |
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; | |
} | |
STATIC | |
EFI_STATUS | |
EFIAPI | |
VirtioRngDriverBindingStart ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE DeviceHandle, | |
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
) | |
{ | |
VIRTIO_RNG_DEV *Dev; | |
EFI_STATUS Status; | |
Dev = (VIRTIO_RNG_DEV *)AllocateZeroPool (sizeof *Dev); | |
if (Dev == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
Status = gBS->OpenProtocol ( | |
DeviceHandle, | |
&gVirtioDeviceProtocolGuid, | |
(VOID **)&Dev->VirtIo, | |
This->DriverBindingHandle, | |
DeviceHandle, | |
EFI_OPEN_PROTOCOL_BY_DRIVER | |
); | |
if (EFI_ERROR (Status)) { | |
goto FreeVirtioRng; | |
} | |
// | |
// VirtIo access granted, configure virtio-rng device. | |
// | |
Status = VirtioRngInit (Dev); | |
if (EFI_ERROR (Status)) { | |
goto CloseVirtIo; | |
} | |
Status = gBS->CreateEvent ( | |
EVT_SIGNAL_EXIT_BOOT_SERVICES, | |
TPL_CALLBACK, | |
&VirtioRngExitBoot, | |
Dev, | |
&Dev->ExitBoot | |
); | |
if (EFI_ERROR (Status)) { | |
goto UninitDev; | |
} | |
// | |
// Setup complete, attempt to export the driver instance's EFI_RNG_PROTOCOL | |
// interface. | |
// | |
Dev->Signature = VIRTIO_RNG_SIG; | |
Status = gBS->InstallProtocolInterface ( | |
&DeviceHandle, | |
&gEfiRngProtocolGuid, | |
EFI_NATIVE_INTERFACE, | |
&Dev->Rng | |
); | |
if (EFI_ERROR (Status)) { | |
goto CloseExitBoot; | |
} | |
return EFI_SUCCESS; | |
CloseExitBoot: | |
gBS->CloseEvent (Dev->ExitBoot); | |
UninitDev: | |
VirtioRngUninit (Dev); | |
CloseVirtIo: | |
gBS->CloseProtocol ( | |
DeviceHandle, | |
&gVirtioDeviceProtocolGuid, | |
This->DriverBindingHandle, | |
DeviceHandle | |
); | |
FreeVirtioRng: | |
FreePool (Dev); | |
return Status; | |
} | |
STATIC | |
EFI_STATUS | |
EFIAPI | |
VirtioRngDriverBindingStop ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE DeviceHandle, | |
IN UINTN NumberOfChildren, | |
IN EFI_HANDLE *ChildHandleBuffer | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_RNG_PROTOCOL *Rng; | |
VIRTIO_RNG_DEV *Dev; | |
Status = gBS->OpenProtocol ( | |
DeviceHandle, // candidate device | |
&gEfiRngProtocolGuid, // retrieve the RNG iface | |
(VOID **)&Rng, // target pointer | |
This->DriverBindingHandle, // requestor driver ident. | |
DeviceHandle, // lookup req. for dev. | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL // lookup only, no new ref. | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Dev = VIRTIO_ENTROPY_SOURCE_FROM_RNG (Rng); | |
// | |
// Handle Stop() requests for in-use driver instances gracefully. | |
// | |
Status = gBS->UninstallProtocolInterface ( | |
DeviceHandle, | |
&gEfiRngProtocolGuid, | |
&Dev->Rng | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
gBS->CloseEvent (Dev->ExitBoot); | |
VirtioRngUninit (Dev); | |
gBS->CloseProtocol ( | |
DeviceHandle, | |
&gVirtioDeviceProtocolGuid, | |
This->DriverBindingHandle, | |
DeviceHandle | |
); | |
FreePool (Dev); | |
return EFI_SUCCESS; | |
} | |
// | |
// The static object that groups the Supported() (ie. probe), Start() and | |
// Stop() functions of the driver together. Refer to UEFI Spec 2.3.1 + Errata | |
// C, 10.1 EFI Driver Binding Protocol. | |
// | |
STATIC EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = { | |
&VirtioRngDriverBindingSupported, | |
&VirtioRngDriverBindingStart, | |
&VirtioRngDriverBindingStop, | |
0x10, // Version, must be in [0x10 .. 0xFFFFFFEF] for IHV-developed drivers | |
NULL, // ImageHandle, to be overwritten by | |
// EfiLibInstallDriverBindingComponentName2() in VirtioRngEntryPoint() | |
NULL // DriverBindingHandle, ditto | |
}; | |
// | |
// The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and | |
// EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's name | |
// in English, for display on standard console devices. This is recommended for | |
// UEFI drivers that follow the UEFI Driver Model. Refer to the Driver Writer's | |
// Guide for UEFI 2.3.1 v1.01, 11 UEFI Driver and Controller Names. | |
// | |
STATIC | |
EFI_UNICODE_STRING_TABLE mDriverNameTable[] = { | |
{ "eng;en", L"Virtio Random Number Generator Driver" }, | |
{ NULL, NULL } | |
}; | |
STATIC | |
EFI_COMPONENT_NAME_PROTOCOL gComponentName; | |
STATIC | |
EFI_STATUS | |
EFIAPI | |
VirtioRngGetDriverName ( | |
IN EFI_COMPONENT_NAME_PROTOCOL *This, | |
IN CHAR8 *Language, | |
OUT CHAR16 **DriverName | |
) | |
{ | |
return LookupUnicodeString2 ( | |
Language, | |
This->SupportedLanguages, | |
mDriverNameTable, | |
DriverName, | |
(BOOLEAN)(This == &gComponentName) // Iso639Language | |
); | |
} | |
STATIC | |
EFI_STATUS | |
EFIAPI | |
VirtioRngGetDeviceName ( | |
IN EFI_COMPONENT_NAME_PROTOCOL *This, | |
IN EFI_HANDLE DeviceHandle, | |
IN EFI_HANDLE ChildHandle, | |
IN CHAR8 *Language, | |
OUT CHAR16 **ControllerName | |
) | |
{ | |
return EFI_UNSUPPORTED; | |
} | |
STATIC | |
EFI_COMPONENT_NAME_PROTOCOL gComponentName = { | |
&VirtioRngGetDriverName, | |
&VirtioRngGetDeviceName, | |
"eng" // SupportedLanguages, ISO 639-2 language codes | |
}; | |
STATIC | |
EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = { | |
(EFI_COMPONENT_NAME2_GET_DRIVER_NAME)&VirtioRngGetDriverName, | |
(EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME)&VirtioRngGetDeviceName, | |
"en" // SupportedLanguages, RFC 4646 language codes | |
}; | |
// | |
// Entry point of this driver. | |
// | |
EFI_STATUS | |
EFIAPI | |
VirtioRngEntryPoint ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
return EfiLibInstallDriverBindingComponentName2 ( | |
ImageHandle, | |
SystemTable, | |
&gDriverBinding, | |
ImageHandle, | |
&gComponentName, | |
&gComponentName2 | |
); | |
} |