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
next prev 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