public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "Pete Batard" <pete@akeo.ie>
To: devel@edk2.groups.io
Cc: ard.biesheuvel@linaro.org, leif.lindholm@linaro.org, philmd@redhat.com
Subject: [edk2-platforms][PATCH 3/3] Silicon/Broadcom/Bcm283x: Add Bcm2838 RNG driver
Date: Mon,  2 Dec 2019 11:55:06 +0000	[thread overview]
Message-ID: <20191202115506.4068-4-pete@akeo.ie> (raw)
In-Reply-To: <20191202115506.4068-1-pete@akeo.ie>

This adds the Bcm2838 Random Number Generator Driver, which
is also the type of RNG that the Bmc2711-based Raspberry Pi 4
uses.

Signed-off-by: Pete Batard <pete@akeo.ie>
---
 Silicon/Broadcom/Bcm283x/Drivers/Bcm2838RngDxe/Bcm2838RngDxe.c   | 285 ++++++++++++++++++++
 Silicon/Broadcom/Bcm283x/Drivers/Bcm2838RngDxe/Bcm2838RngDxe.inf |  46 ++++
 Silicon/Broadcom/Bcm283x/Include/IndustryStandard/Bcm2838Rng.h   |  30 +++
 3 files changed, 361 insertions(+)

diff --git a/Silicon/Broadcom/Bcm283x/Drivers/Bcm2838RngDxe/Bcm2838RngDxe.c b/Silicon/Broadcom/Bcm283x/Drivers/Bcm2838RngDxe/Bcm2838RngDxe.c
new file mode 100644
index 000000000000..5737876e52c6
--- /dev/null
+++ b/Silicon/Broadcom/Bcm283x/Drivers/Bcm2838RngDxe/Bcm2838RngDxe.c
@@ -0,0 +1,285 @@
+/** @file
+
+  This driver produces an EFI_RNG_PROTOCOL instance for the Broadcom 2838 RNG
+
+  Copyright (C) 2019, Pete Batard <pete@akeo.ie>
+  Copyright (C) 2019, Linaro Ltd. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+#include <Library/PcdLib.h>
+#include <Library/TimerLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include <IndustryStandard/Bcm2838Rng.h>
+
+#include <Protocol/Rng.h>
+
+#define RNG_WARMUP_COUNT        0x40000
+#define RNG_MAX_RETRIES         0x100         // arbitrary upper bound
+
+/**
+  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
+Bcm2838RngGetInfo (
+  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;
+}
+
+/**
+  Read a single random value, in either FIFO or regular mode.
+
+  @param[in]  Val                     A pointer to the 32-bit word that is to
+                                      be filled with a random value.
+
+  @retval EFI_SUCCESS                 A random value was successfully read.
+  @retval EFI_NOT_READY               The number of retries elapsed before a
+                                      random value was generated.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+Bcm2838RngReadValue (
+  IN OUT  UINT32                  *Val
+)
+{
+  UINT32 Avail;
+  UINT32 i;
+
+  ASSERT (Val != NULL);
+
+  Avail = MmioRead32 (RNG_FIFO_COUNT) & RNG_FIFO_DATA_AVAIL_MASK;
+
+  //
+  // If we don't have a value ready, wait 1 us and retry.
+  //
+  // Empirical testing on the platform this driver is designed to be used
+  // with shows that, unless you set a large divisor for the sample rate,
+  // random bits should be generated around the MHz frequency.
+  // Therefore a retry that doesn't expire until at least RNG_MAX_RETRIES
+  // microseconds should give us ample time to obtain a value. Besides,
+  // even outside of calling MicroSecondDelay (), we expect MMIO reads to
+  // be slow anyway...
+  //
+  // On the other hand, we may run into a timeout here if the warmup period
+  // has not been completed since the RNG locks RNG_FIFO_COUNT to zero
+  // until then. However, with the values we use for the target platform,
+  // (RPi4) you'd need to start requesting random data within the first
+  // 250 to 500 ms after driver instantiation for this to happen.
+  //
+  for (i = 0; Avail < 1 && i < RNG_MAX_RETRIES; i++) {
+    MicroSecondDelay (1);
+    Avail = MmioRead32 (RNG_FIFO_COUNT) & RNG_FIFO_DATA_AVAIL_MASK;
+  }
+  if (Avail < 1) {
+    return EFI_NOT_READY;
+  }
+
+  *Val = MmioRead32 (RNG_FIFO_DATA);
+
+  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
+Bcm2838RngGetRNG (
+  IN EFI_RNG_PROTOCOL            *This,
+  IN EFI_RNG_ALGORITHM           *RNGAlgorithm, OPTIONAL
+  IN UINTN                       RNGValueLength,
+  OUT UINT8                      *RNGValue
+  )
+{
+  EFI_STATUS      Status;
+  UINT32          Val;
+
+  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;
+  }
+
+  //
+  // The Linux driver from Broadcom checks RNG_BIT_COUNT here to ensure that
+  // the warmup threshold has been reached, but our testing shows that this is
+  // pointless as RNG_BIT_COUNT is increased even during warmup and, as long
+  // as warmup isn't complete, the FIFO reports that no values are available.
+  //
+  // Also note that RNG_BIT_COUNT doesn't roll over. Once it reaches 0xFFFFFFFF
+  // it just stays there...
+  //
+
+  while (RNGValueLength >= sizeof (UINT32)) {
+    Status = Bcm2838RngReadValue (&Val);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+    WriteUnaligned32 ((VOID *)RNGValue, Val);
+    RNGValue += sizeof (UINT32);
+    RNGValueLength -= sizeof (UINT32);
+  }
+
+  if (RNGValueLength > 0) {
+    Status = Bcm2838RngReadValue (&Val);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+    while (RNGValueLength--) {
+      *RNGValue++ = (UINT8)Val;
+      Val >>= 8;
+    }
+  }
+  return EFI_SUCCESS;
+}
+
+STATIC EFI_RNG_PROTOCOL mBcm2838RngProtocol = {
+  Bcm2838RngGetInfo,
+  Bcm2838RngGetRNG
+};
+
+//
+// Entry point of this driver.
+//
+EFI_STATUS
+EFIAPI
+Bcm2838RngEntryPoint (
+  IN EFI_HANDLE       ImageHandle,
+  IN EFI_SYSTEM_TABLE *SystemTable
+  )
+{
+  EFI_STATUS      Status;
+
+  Status = gBS->InstallMultipleProtocolInterfaces (&ImageHandle,
+                  &gEfiRngProtocolGuid, &mBcm2838RngProtocol,
+                  NULL);
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Set the warm up bit count.
+  //
+  // This results in the RNG holding off from populating any value into the
+  // FIFO until the value below has been reached in RNG_BIT_COUNT.
+  //
+  MmioWrite32 (RNG_BIT_COUNT_THRESHOLD, RNG_WARMUP_COUNT);
+
+  //
+  // We would disable RNG interrupts here... if we had access to the datasheet.
+  //
+
+  //
+  // Enable the RNG with a sample rate divisor of 3.
+  //
+  // For a Bcm2711 running at 1.5 GHz, this should generate random bits at a
+  // rate of about 1 every microsecond (1 MHz).
+  //
+  // Note that The divisor is a power of two index, meaning that each
+  // consecutive divisor halves the frequency at which bits are being
+  // produced. Zero is a special value that seems to set the rate somewhere
+  // between divisors 1 and 2.
+  //
+  // Also note that Broadcom set the whole ENABLE_MASK in the Linux driver,
+  // instead of single bits, which may be unintended. But since we don't have
+  // any public documentation on what each of these bits do, we follow suit.
+  //
+  MmioWrite32 (RNG_CTRL,
+    RNG_CTRL_ENABLE_MASK | (3 << RNG_CTRL_SAMPLE_RATE_DIVISOR_SHIFT));
+
+  return EFI_SUCCESS;
+}
diff --git a/Silicon/Broadcom/Bcm283x/Drivers/Bcm2838RngDxe/Bcm2838RngDxe.inf b/Silicon/Broadcom/Bcm283x/Drivers/Bcm2838RngDxe/Bcm2838RngDxe.inf
new file mode 100644
index 000000000000..fdc1b25743cf
--- /dev/null
+++ b/Silicon/Broadcom/Bcm283x/Drivers/Bcm2838RngDxe/Bcm2838RngDxe.inf
@@ -0,0 +1,46 @@
+#/** @file
+#
+#  Copyright (c) 2019, Pete Batard <pete@akeo.ie>
+#  Copyright (c) 2019, ARM Limited. All rights reserved.
+#  Copyright (c) 2019, Linaro, Ltd. All rights reserved.
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#**/
+
+[Defines]
+  INF_VERSION                    = 0x0001001B
+  BASE_NAME                      = Bcm2838RngDxe
+  FILE_GUID                      = 5ec564ce-b656-435c-a652-5cb719784542
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = Bcm2838RngEntryPoint
+
+[Sources]
+  Bcm2838RngDxe.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  Silicon/Broadcom/Bcm283x/Bcm283x.dec
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  DebugLib
+  IoLib
+  PcdLib
+  TimerLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+
+[Protocols]
+  gEfiRngProtocolGuid              ## PRODUCES
+
+[Guids]
+  gEfiRngAlgorithmRaw
+
+[FixedPcd]
+  gBcm283xTokenSpaceGuid.PcdBcm283xRegistersAddress
+
+[Depex]
+  TRUE
diff --git a/Silicon/Broadcom/Bcm283x/Include/IndustryStandard/Bcm2838Rng.h b/Silicon/Broadcom/Bcm283x/Include/IndustryStandard/Bcm2838Rng.h
new file mode 100644
index 000000000000..003866fa866e
--- /dev/null
+++ b/Silicon/Broadcom/Bcm283x/Include/IndustryStandard/Bcm2838Rng.h
@@ -0,0 +1,30 @@
+/** @file
+ *
+ *  Copyright (c) 2019, Pete Batard <pete@akeo.ie>
+ *
+ *  SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+ **/
+
+#ifndef BCM2838_RNG_H__
+#define BCM2838_RNG_H__
+
+#define BCM2838_RNG_OFFSET                  0x00104000
+#define RNG_BASE_ADDRESS                    ((FixedPcdGet64 (PcdBcm283xRegistersAddress)) \
+                                            + BCM2838_RNG_OFFSET)
+
+#define RNG_CTRL                            (RNG_BASE_ADDRESS + 0x0)
+#define RNG_STATUS                          (RNG_BASE_ADDRESS + 0x4)
+#define RNG_DATA                            (RNG_BASE_ADDRESS + 0x8)
+#define RNG_BIT_COUNT                       (RNG_BASE_ADDRESS + 0xc)
+#define RNG_BIT_COUNT_THRESHOLD             (RNG_BASE_ADDRESS + 0x10)
+#define RNG_INT_STATUS                      (RNG_BASE_ADDRESS + 0x18)
+#define RNG_INT_ENABLE                      (RNG_BASE_ADDRESS + 0x1c)
+#define RNG_FIFO_DATA                       (RNG_BASE_ADDRESS + 0x20)
+#define RNG_FIFO_COUNT                      (RNG_BASE_ADDRESS + 0x24)
+
+#define RNG_CTRL_ENABLE_MASK                0x1fff
+#define RNG_CTRL_SAMPLE_RATE_DIVISOR_SHIFT  13      // Unmasked bits from above
+#define RNG_FIFO_DATA_AVAIL_MASK            0xff
+
+#endif /* BCM2838_RNG_H__ */
-- 
2.21.0.windows.1


  parent reply	other threads:[~2019-12-02 11:55 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-12-02 11:55 [edk2-platforms][PATCH 0/3] Silicon/Broadcom/Bcm283x: RNG improvements Pete Batard
2019-12-02 11:55 ` [edk2-platforms][PATCH 1/3] Silicon/Broadcom/Bcm283x: Rename Bcm2835 RNG driver Pete Batard
2019-12-02 12:16   ` Philippe Mathieu-Daudé
2019-12-02 11:55 ` [edk2-platforms][PATCH 2/3] Silicon/Broadcom/Bcm283x: Move Bcm2835 RNG defines to their own header Pete Batard
2019-12-02 12:17   ` Philippe Mathieu-Daudé
2019-12-02 11:55 ` Pete Batard [this message]
2019-12-02 16:03 ` [edk2-platforms][PATCH 0/3] Silicon/Broadcom/Bcm283x: RNG improvements Ard Biesheuvel
2019-12-02 16:53   ` [edk2-devel] " Pete Batard

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20191202115506.4068-4-pete@akeo.ie \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox