2016-02-19 11:43:12 +01:00
|
|
|
/** @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>
|
2017-08-23 12:57:17 +02:00
|
|
|
Copyright (c) 2017, AMD Inc, All rights reserved.<BR>
|
2016-02-19 11:43:12 +01:00
|
|
|
|
|
|
|
This driver:
|
|
|
|
|
|
|
|
Copyright (C) 2016, Linaro Ltd.
|
|
|
|
|
|
|
|
This program and the accompanying materials are licensed and made available
|
|
|
|
under the terms and conditions of the BSD License which accompanies this
|
|
|
|
distribution. The full text of the license may be found at
|
|
|
|
http://opensource.org/licenses/bsd-license.php
|
|
|
|
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
|
|
|
|
WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
|
|
|
|
|
**/
|
|
|
|
|
|
|
|
#include <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;
|
2017-08-23 12:57:19 +02:00
|
|
|
EFI_PHYSICAL_ADDRESS DeviceAddress;
|
|
|
|
VOID *Mapping;
|
2016-02-19 11:43:12 +01:00
|
|
|
|
|
|
|
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);
|
2017-08-23 12:57:19 +02:00
|
|
|
//
|
|
|
|
// Map Buffer's system phyiscal address to device address
|
|
|
|
//
|
|
|
|
Status = VirtioMapAllBytesInSharedBuffer (
|
|
|
|
Dev->VirtIo,
|
|
|
|
VirtioOperationBusMasterWrite,
|
|
|
|
(VOID *)Buffer,
|
|
|
|
RNGValueLength,
|
|
|
|
&DeviceAddress,
|
|
|
|
&Mapping
|
|
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
Status = EFI_DEVICE_ERROR;
|
|
|
|
goto FreeBuffer;
|
|
|
|
}
|
2016-02-19 11:43:12 +01:00
|
|
|
|
|
|
|
//
|
|
|
|
// 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,
|
2017-08-23 12:57:19 +02:00
|
|
|
DeviceAddress + Index,
|
2016-02-19 11:43:12 +01:00
|
|
|
BufferSize,
|
|
|
|
VRING_DESC_F_WRITE,
|
|
|
|
&Indices);
|
|
|
|
|
|
|
|
if (VirtioFlush (Dev->VirtIo, 0, &Dev->Ring, &Indices, &Len) !=
|
|
|
|
EFI_SUCCESS) {
|
|
|
|
Status = EFI_DEVICE_ERROR;
|
2017-08-23 12:57:19 +02:00
|
|
|
goto UnmapBuffer;
|
2016-02-19 11:43:12 +01:00
|
|
|
}
|
|
|
|
ASSERT (Len > 0);
|
|
|
|
ASSERT (Len <= BufferSize);
|
|
|
|
}
|
|
|
|
|
2017-08-23 12:57:19 +02:00
|
|
|
//
|
|
|
|
// Unmap the device buffer before accessing it.
|
|
|
|
//
|
|
|
|
Status = Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Mapping);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
Status = EFI_DEVICE_ERROR;
|
|
|
|
goto FreeBuffer;
|
|
|
|
}
|
|
|
|
|
2016-02-19 11:43:12 +01:00
|
|
|
for (Index = 0; Index < RNGValueLength; Index++) {
|
|
|
|
RNGValue[Index] = Buffer[Index];
|
|
|
|
}
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
|
2017-08-23 12:57:19 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2016-02-19 11:43:12 +01:00
|
|
|
FreeBuffer:
|
|
|
|
FreePool ((VOID *)Buffer);
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
STATIC
|
|
|
|
EFI_STATUS
|
|
|
|
EFIAPI
|
|
|
|
VirtioRngInit (
|
|
|
|
IN OUT VIRTIO_RNG_DEV *Dev
|
|
|
|
)
|
|
|
|
{
|
|
|
|
UINT8 NextDevStat;
|
|
|
|
EFI_STATUS Status;
|
|
|
|
UINT16 QueueSize;
|
2016-03-12 00:58:12 +01:00
|
|
|
UINT64 Features;
|
2017-08-23 12:57:19 +02:00
|
|
|
UINT64 RingBaseShift;
|
2016-02-19 11:43:12 +01:00
|
|
|
|
|
|
|
//
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2017-08-25 12:37:47 +02:00
|
|
|
Features &= VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM;
|
2016-03-12 14:41:26 +01:00
|
|
|
|
|
|
|
//
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-19 11:43:12 +01:00
|
|
|
//
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2017-08-23 12:57:17 +02:00
|
|
|
Status = VirtioRingInit (Dev->VirtIo, QueueSize, &Dev->Ring);
|
2016-02-19 11:43:12 +01:00
|
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
goto Failed;
|
|
|
|
}
|
|
|
|
|
2017-08-23 12:57:19 +02:00
|
|
|
//
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2016-02-19 11:43:12 +01:00
|
|
|
//
|
|
|
|
// Additional steps for MMIO: align the queue appropriately, and set the
|
2017-08-23 12:57:19 +02:00
|
|
|
// size. If anything fails from here on, we must unmap the ring resources.
|
2016-02-19 11:43:12 +01:00
|
|
|
//
|
|
|
|
Status = Dev->VirtIo->SetQueueNum (Dev->VirtIo, QueueSize);
|
|
|
|
if (EFI_ERROR (Status)) {
|
2017-08-23 12:57:19 +02:00
|
|
|
goto UnmapQueue;
|
2016-02-19 11:43:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Status = Dev->VirtIo->SetQueueAlign (Dev->VirtIo, EFI_PAGE_SIZE);
|
|
|
|
if (EFI_ERROR (Status)) {
|
2017-08-23 12:57:19 +02:00
|
|
|
goto UnmapQueue;
|
2016-02-19 11:43:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// step 4c -- Report GPFN (guest-physical frame number) of queue.
|
|
|
|
//
|
2017-08-23 12:57:19 +02:00
|
|
|
Status = Dev->VirtIo->SetQueueAddress (
|
|
|
|
Dev->VirtIo,
|
|
|
|
&Dev->Ring,
|
|
|
|
RingBaseShift
|
|
|
|
);
|
2016-02-19 11:43:12 +01:00
|
|
|
if (EFI_ERROR (Status)) {
|
2017-08-23 12:57:19 +02:00
|
|
|
goto UnmapQueue;
|
2016-02-19 11:43:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
2016-03-12 14:41:26 +01:00
|
|
|
// step 5 -- Report understood features and guest-tuneables.
|
2016-02-19 11:43:12 +01:00
|
|
|
//
|
2016-03-12 14:41:26 +01:00
|
|
|
if (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) {
|
2017-08-25 12:37:47 +02:00
|
|
|
Features &= ~(UINT64)(VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM);
|
2016-03-12 14:41:26 +01:00
|
|
|
Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo, Features);
|
|
|
|
if (EFI_ERROR (Status)) {
|
2017-08-23 12:57:19 +02:00
|
|
|
goto UnmapQueue;
|
2016-03-12 14:41:26 +01:00
|
|
|
}
|
2016-02-19 11:43:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// step 6 -- initialization complete
|
|
|
|
//
|
|
|
|
NextDevStat |= VSTAT_DRIVER_OK;
|
|
|
|
Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
|
|
|
|
if (EFI_ERROR (Status)) {
|
2017-08-23 12:57:19 +02:00
|
|
|
goto UnmapQueue;
|
2016-02-19 11:43:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// populate the exported interface's attributes
|
|
|
|
//
|
|
|
|
Dev->Rng.GetInfo = VirtioRngGetInfo;
|
|
|
|
Dev->Rng.GetRNG = VirtioRngGetRNG;
|
|
|
|
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
|
2017-08-23 12:57:19 +02:00
|
|
|
UnmapQueue:
|
|
|
|
Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->RingMap);
|
|
|
|
|
2016-02-19 11:43:12 +01:00
|
|
|
ReleaseQueue:
|
2017-08-23 12:57:17 +02:00
|
|
|
VirtioRingUninit (Dev->VirtIo, &Dev->Ring);
|
2016-02-19 11:43:12 +01:00
|
|
|
|
|
|
|
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->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
|
2017-08-23 12:57:19 +02:00
|
|
|
|
|
|
|
Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->RingMap);
|
|
|
|
|
2017-08-23 12:57:17 +02:00
|
|
|
VirtioRingUninit (Dev->VirtIo, &Dev->Ring);
|
2016-02-19 11:43:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Event notification function enqueued by ExitBootServices().
|
|
|
|
//
|
|
|
|
|
|
|
|
STATIC
|
|
|
|
VOID
|
|
|
|
EFIAPI
|
|
|
|
VirtioRngExitBoot (
|
|
|
|
IN EFI_EVENT Event,
|
|
|
|
IN VOID *Context
|
|
|
|
)
|
|
|
|
{
|
|
|
|
VIRTIO_RNG_DEV *Dev;
|
|
|
|
|
|
|
|
//
|
|
|
|
// 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->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
|
2017-08-23 12:57:19 +02:00
|
|
|
|
|
|
|
//
|
|
|
|
// Unmap the ring buffer so that hypervisor will not be able to get readable
|
|
|
|
// data after device reset.
|
|
|
|
//
|
|
|
|
Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->RingMap);
|
2016-02-19 11:43:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// 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
|
|
|
|
);
|
|
|
|
}
|