diff mbox

[edk2,v4,2/4] OvmfPkg: implement UEFI driver for Virtio RNG devices

Message ID 1456302876-24702-3-git-send-email-ard.biesheuvel@linaro.org
State Accepted
Commit 5528732a51bc998702ee49f6b8c86f076521d394
Headers show

Commit Message

Ard Biesheuvel Feb. 24, 2016, 8:34 a.m. UTC
This implements a UEFI driver model driver for Virtio devices of type
VIRTIO_SUBSYSTEM_ENTROPY_SOURCE, and exposes them via instances of
the EFI_RNG_PROTOCOL protocol, supporting the EFI_RNG_ALGORITHM_RAW
algorithm only.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>

Reviewed-by: Laszlo Ersek <lersek@redhat.com>

---
 OvmfPkg/VirtioRngDxe/VirtioRng.c   | 653 ++++++++++++++++++++
 OvmfPkg/VirtioRngDxe/VirtioRng.h   |  46 ++
 OvmfPkg/VirtioRngDxe/VirtioRng.inf |  45 ++
 3 files changed, 744 insertions(+)

-- 
2.5.0

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel
diff mbox

Patch

diff --git a/OvmfPkg/VirtioRngDxe/VirtioRng.c b/OvmfPkg/VirtioRngDxe/VirtioRng.c
new file mode 100644
index 000000000000..8eb1aa300b50
--- /dev/null
+++ b/OvmfPkg/VirtioRngDxe/VirtioRng.c
@@ -0,0 +1,653 @@ 
+/** @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>
+
+  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;
+
+  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);
+
+  //
+  // 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,
+      (UINTN)Buffer + Index,
+      BufferSize,
+      VRING_DESC_F_WRITE,
+      &Indices);
+
+    if (VirtioFlush (Dev->VirtIo, 0, &Dev->Ring, &Indices, &Len) !=
+        EFI_SUCCESS) {
+      Status = EFI_DEVICE_ERROR;
+      goto FreeBuffer;
+    }
+    ASSERT (Len > 0);
+    ASSERT (Len <= BufferSize);
+  }
+
+  for (Index = 0; Index < RNGValueLength; Index++) {
+    RNGValue[Index] = Buffer[Index];
+  }
+  Status = EFI_SUCCESS;
+
+FreeBuffer:
+  FreePool ((VOID *)Buffer);
+  return Status;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+VirtioRngInit (
+  IN OUT VIRTIO_RNG_DEV *Dev
+  )
+{
+  UINT8      NextDevStat;
+  EFI_STATUS Status;
+  UINT16     QueueSize;
+  UINT32     Features;
+
+  //
+  // 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;
+  }
+
+  //
+  // 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 (QueueSize, &Dev->Ring);
+  if (EFI_ERROR (Status)) {
+    goto Failed;
+  }
+
+  //
+  // Additional steps for MMIO: align the queue appropriately, and set the
+  // size. If anything fails from here on, we must release the ring resources.
+  //
+  Status = Dev->VirtIo->SetQueueNum (Dev->VirtIo, QueueSize);
+  if (EFI_ERROR (Status)) {
+    goto ReleaseQueue;
+  }
+
+  Status = Dev->VirtIo->SetQueueAlign (Dev->VirtIo, EFI_PAGE_SIZE);
+  if (EFI_ERROR (Status)) {
+    goto ReleaseQueue;
+  }
+
+  //
+  // step 4c -- Report GPFN (guest-physical frame number) of queue.
+  //
+  Status = Dev->VirtIo->SetQueueAddress (Dev->VirtIo,
+      (UINT32) ((UINTN) Dev->Ring.Base >> EFI_PAGE_SHIFT));
+  if (EFI_ERROR (Status)) {
+    goto ReleaseQueue;
+  }
+
+  //
+  // step 5 -- Report understood features and guest-tuneables. None are
+  // currently defined for VirtioRng, and no generic features are needed by
+  // this driver.
+  //
+  Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo, 0);
+  if (EFI_ERROR (Status)) {
+    goto ReleaseQueue;
+  }
+
+  //
+  // step 6 -- initialization complete
+  //
+  NextDevStat |= VSTAT_DRIVER_OK;
+  Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
+  if (EFI_ERROR (Status)) {
+    goto ReleaseQueue;
+  }
+
+  //
+  // populate the exported interface's attributes
+  //
+  Dev->Rng.GetInfo    = VirtioRngGetInfo;
+  Dev->Rng.GetRNG     = VirtioRngGetRNG;
+
+  return EFI_SUCCESS;
+
+ReleaseQueue:
+  VirtioRingUninit (&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->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
+  VirtioRingUninit (&Dev->Ring);
+}
+
+//
+// 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);
+}
+
+
+//
+// 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
+           );
+}
diff --git a/OvmfPkg/VirtioRngDxe/VirtioRng.h b/OvmfPkg/VirtioRngDxe/VirtioRng.h
new file mode 100644
index 000000000000..844550a00c17
--- /dev/null
+++ b/OvmfPkg/VirtioRngDxe/VirtioRng.h
@@ -0,0 +1,46 @@ 
+/** @file
+
+  Private definitions of the VirtioRng RNG 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.
+
+**/
+
+#ifndef _VIRTIO_RNG_DXE_H_
+#define _VIRTIO_RNG_DXE_H_
+
+#include <Protocol/ComponentName.h>
+#include <Protocol/DriverBinding.h>
+#include <Protocol/Rng.h>
+
+#include <IndustryStandard/Virtio.h>
+
+#define VIRTIO_RNG_SIG SIGNATURE_32 ('V', 'R', 'N', 'G')
+
+typedef struct {
+  //
+  // Parts of this structure are initialized / torn down in various functions
+  // at various call depths. The table to the right should make it easier to
+  // track them.
+  //
+  //                        field              init function       init depth
+  //                        ----------------   ------------------  ----------
+  UINT32                    Signature;      // DriverBindingStart   0
+  VIRTIO_DEVICE_PROTOCOL    *VirtIo;        // DriverBindingStart   0
+  EFI_EVENT                 ExitBoot;       // DriverBindingStart   0
+  VRING                     Ring;           // VirtioRingInit       2
+  EFI_RNG_PROTOCOL          Rng;            // VirtioRngInit        1
+} VIRTIO_RNG_DEV;
+
+#define VIRTIO_ENTROPY_SOURCE_FROM_RNG(RngPointer) \
+          CR (RngPointer, VIRTIO_RNG_DEV, Rng, VIRTIO_RNG_SIG)
+
+#endif
diff --git a/OvmfPkg/VirtioRngDxe/VirtioRng.inf b/OvmfPkg/VirtioRngDxe/VirtioRng.inf
new file mode 100644
index 000000000000..471beb37bc7f
--- /dev/null
+++ b/OvmfPkg/VirtioRngDxe/VirtioRng.inf
@@ -0,0 +1,45 @@ 
+## @file
+# This driver produces EFI_RNG_PROTOCOL instances for virtio-rng devices.
+#
+# 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.
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = VirtioRngDxe
+  FILE_GUID                      = 58E26F0D-CBAC-4BBA-B70F-18221415665A
+  MODULE_TYPE                    = UEFI_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = VirtioRngEntryPoint
+
+[Sources]
+  VirtioRng.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+  BaseMemoryLib
+  DebugLib
+  MemoryAllocationLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+  UefiLib
+  VirtioLib
+
+[Protocols]
+  gEfiRngProtocolGuid              ## BY_START
+  gVirtioDeviceProtocolGuid        ## TO_START
+
+[Guids]
+  gEfiRngAlgorithmRaw