From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wm1-f67.google.com (mail-wm1-f67.google.com [209.85.128.67]) by mx.groups.io with SMTP id smtpd.web10.50.1575287727948378392 for ; Mon, 02 Dec 2019 03:55:28 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@akeo-ie.20150623.gappssmtp.com header.s=20150623 header.b=kAryHYFr; spf=none, err=permanent DNS error (domain: akeo.ie, ip: 209.85.128.67, mailfrom: pete@akeo.ie) Received: by mail-wm1-f67.google.com with SMTP id f129so22950353wmf.2 for ; Mon, 02 Dec 2019 03:55:27 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=akeo-ie.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=AUxPCtuwrCKnBNzJ0L1g0GIEgOaIsq+QHF7IEODkG3o=; b=kAryHYFrOxuPse8d4O29XQmeKMXVwy29xfzOlY5CRhHAaFYW+hAduJQcmlyeEwY3mN 8mAR5dWZhWICrCf6Y538CT+7hCIphNc/7YRqVEAR5alAP69cFZ71tevX+DZ8s9WzYwve L7p9FWz90Y4oj9/PP0UOdXE126JCOLltMViLfgJqyq+AYqNKY0drvDU19rb4nZIpnqmE 1S4Sjx9T5EE7j14Ac5gNcIkLZcWduQmc1z1Knjy0H7vY4+y2CM6vSLIEqhZpNuoLGnCL /RhHhfiRE5VVbE0doRvnn5na0vQHljLcQhlyWSAB6r42u9jHVNXI8EDqLbCk8OnzEA4d IEdw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=AUxPCtuwrCKnBNzJ0L1g0GIEgOaIsq+QHF7IEODkG3o=; b=RVRmiq0HPAdVRrmwKQwvLo2Bkx+1oQD1tQWgvbVx8PFsEkeeWKZr0RBORcaoVhssgA 6nA5J2xEQY1HGfWRt+WBDdq8O21usSE8TKLj7E9C5c7tAvIykMgNpD+apUpYfSVkJRQd m1Z10Ot4ZFh7StY+bzeCCfio3kqKRNsVNfwWzeA6Sir4SMpHtQV9KyWcPoC9goy0Pb66 zhhKtvOOaE1R815t0xu+VlXymy/KInrmJqr59aot+iSf+UFibGKZWFCls51h1qEfsLgA RViCGsiZtlyLceZMbdYxGwQ7EWNvv7BBtiuIFAyHcDRI8aziQnh57oaEIkzulxwE/Rdc wNqQ== X-Gm-Message-State: APjAAAXuYkLduIhRq3DpGUuuNh6iElF2VaKTEk6M/pghaFBZLrSQdibv Bwl8js9rdgYMprJGEp2JU81NqwnpHL4= X-Google-Smtp-Source: APXvYqyc50yWUYJxMaP+/DC+HaC2L3pcSyiJRFuuR8FdzYM30fjW5s70UWaO4Kf7eDb2cAgLhYO/hQ== X-Received: by 2002:a7b:c318:: with SMTP id k24mr15322457wmj.54.1575287725673; Mon, 02 Dec 2019 03:55:25 -0800 (PST) Return-Path: Received: from localhost.localdomain ([84.203.45.230]) by smtp.gmail.com with ESMTPSA id p10sm8500291wmi.15.2019.12.02.03.55.23 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 02 Dec 2019 03:55:24 -0800 (PST) From: "Pete Batard" 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 Message-Id: <20191202115506.4068-4-pete@akeo.ie> X-Mailer: git-send-email 2.21.0.windows.1 In-Reply-To: <20191202115506.4068-1-pete@akeo.ie> References: <20191202115506.4068-1-pete@akeo.ie> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 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 --- 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 + Copyright (C) 2019, Linaro Ltd. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#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 +# 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 + * + * 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