From 7e099ac86044e82464b30e946f60978e3bbd674c Mon Sep 17 00:00:00 2001 From: Taylor R Campbell Date: Thu, 7 Apr 2022 21:00:08 +0000 Subject: [PATCH] WIP: arm: Add support for Arm DEN0098 SMC TRNG firmware interface. Problem: Can't find any hardware or even emulators that implement it. So totally untested (also some debugging crud left). --- sys/arch/aarch64/aarch64/cpu.c | 6 + sys/arch/aarch64/conf/files.aarch64 | 3 + sys/arch/arm/arm/smc_trng.c | 354 ++++++++++++++++++++++++++++ sys/arch/arm/arm/smc_trng.h | 34 +++ sys/arch/evbarm/conf/GENERIC64 | 3 + 5 files changed, 400 insertions(+) create mode 100644 sys/arch/arm/arm/smc_trng.c create mode 100644 sys/arch/arm/arm/smc_trng.h diff --git a/sys/arch/aarch64/aarch64/cpu.c b/sys/arch/aarch64/aarch64/cpu.c index 7e9e0845af60..6fcb5891fa8b 100644 --- a/sys/arch/aarch64/aarch64/cpu.c +++ b/sys/arch/aarch64/aarch64/cpu.c @@ -31,6 +31,7 @@ __KERNEL_RCSID(1, "$NetBSD: cpu.c,v 1.69 2022/03/03 06:26:05 riastradh Exp $"); #include "locators.h" #include "opt_arm_debug.h" +#include "opt_arm_smc_trng.h" #include "opt_ddb.h" #include "opt_fdt.h" #include "opt_multiprocessor.h" @@ -64,6 +65,7 @@ __KERNEL_RCSID(1, "$NetBSD: cpu.c,v 1.69 2022/03/03 06:26:05 riastradh Exp $"); #ifdef FDT #include #endif +#include #ifdef VERBOSE_INIT_ARM #define VPRINTF(...) printf(__VA_ARGS__) @@ -178,6 +180,10 @@ cpu_attach(device_t dv, cpuid_t id) cpu_setup_rng(dv, ci); cpu_setup_aes(dv, ci); cpu_setup_chacha(dv, ci); + +#ifdef ARM_SMC_TRNG + printf("arm_smc_trng: %d\n", smc_trng_probe()); +#endif } struct cpuidtab { diff --git a/sys/arch/aarch64/conf/files.aarch64 b/sys/arch/aarch64/conf/files.aarch64 index 820ee5a30cf2..73bfeacedcfc 100644 --- a/sys/arch/aarch64/conf/files.aarch64 +++ b/sys/arch/aarch64/conf/files.aarch64 @@ -41,6 +41,9 @@ file arch/aarch64/aarch64/fpu.c define smccc file arch/arm/arm/smccc.c smccc +defflag opt_arm_smc_trng.h ARM_SMC_TRNG: smccc +file arch/arm/arm/smc_trng.c arm_smc_trng + # Power State Coordination Interface (PSCI) device psci: smccc file arch/arm/arm/psci.c psci diff --git a/sys/arch/arm/arm/smc_trng.c b/sys/arch/arm/arm/smc_trng.c new file mode 100644 index 000000000000..9a582d1a4f54 --- /dev/null +++ b/sys/arch/arm/arm/smc_trng.c @@ -0,0 +1,354 @@ +/* $NetBSD$ */ + +/*- + * Copyright (c) 2022 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Arm True Random Number Generator Firmware Interface 1.0, Platform + * Design Document, Document number: ARM DEN 0098, Version 1.0 REL0, + * 2022-01-14. + * + * https://developer.arm.com/documentation/den0098/1-0 + * https://developer.arm.com/documentation/den0098/latest + */ + +#include +__KERNEL_RCSID(1, "$NetBSD$"); + +#include + +#include +#include +#include + +#include +#include + +#define TRNG_NAME "arm_smc_trng" + +#define TRNG_VERSION 0x84000050u +#define TRNG_FEATURES 0x84000051u +#define TRNG_GET_UUID 0x84000052u +#define TRNG_RND 0x84000053u +#define TRNG_RND64 0xc4000053u + +#define TRNG_VERSION_1_0 0x00010000u + +#define TRNG_RND_MAXBYTES 12 +#define TRNG_RND64_MAXBYTES 24 + +#define TRNG_SUCCESS 0 +#define TRNG_NOT_SUPPORTED -1 +#define TRNG_INVALID_PARAMETERS -2 +#define TRNG_NO_ENTROPY -3 + +static uint8_t trng_uuid[16] __read_mostly; +static int trng_rnd_features __read_mostly = TRNG_NOT_SUPPORTED; +static struct krndsource trng_source; +static struct sysctllog *trng_sysctllog __read_mostly; +static struct evcnt trng_no_entropy; +static struct evcnt trng_failed; + +static int +trng_version(void) +{ + + return smccc_call(TRNG_VERSION, 0, 0, 0, 0, NULL, NULL, NULL, NULL); +} + +static int +trng_features(uint32_t trng_func_id) +{ + + return smccc_call(TRNG_FEATURES, trng_func_id, 0, 0, 0, + NULL, NULL, NULL, NULL); +} + +static int +trng_get_uuid(void) +{ + register_t x0, x1, x2, x3; + + if (smccc_call(TRNG_GET_UUID, 0, 0, 0, 0, &x0, &x1, &x2, &x3) == -1) + return ENODEV; + + /* XXX Byte order? */ + le32enc(trng_uuid, x0); + le32enc(trng_uuid + 4, x1); + le32enc(trng_uuid + 8, x2); + le32enc(trng_uuid + 12, x3); + return 0; +} + +static int +trng_rnd(uint8_t b[static TRNG_RND_MAXBYTES]) +{ + register_t x0, x1, x2, x3; + + (void)smccc_call(TRNG_RND, NBBY*TRNG_RND_MAXBYTES, 0, 0, 0, + &x0, &x1, &x2, &x3); + if (x0) + return x0; + le32enc(b, x1); + le32enc(b + 4, x2); + le32enc(b + 8, x3); + CTASSERT(TRNG_RND_MAXBYTES == 12); + return 0; +} + +static int +trng_sysctl(SYSCTLFN_ARGS) +{ + uint8_t b[TRNG_RND_MAXBYTES]; + struct sysctlnode node = *rnode; + size_t size; + int error; + + /* If oldp == NULL, the caller wants to learn the size. */ + if (oldp == NULL) { + *oldlenp = sizeof(b); + return 0; + } + + /* Verify the output buffer size is reasonable. */ + size = *oldlenp; + if (size > sizeof(b)) /* size_t, so never negative */ + return E2BIG; + if (size == 0) + return 0; /* nothing to do */ + + /* + * Generate data, or translate TRNG errors to errno(3) -- + * TRNG_NO_ENTROPY means we might get more later; anything else + * we treat as an I/O error, because we pass valid arguments + * from the driver. + */ + error = trng_rnd(b); + switch (error) { + case TRNG_SUCCESS: + break; + case TRNG_NO_ENTROPY: + return EAGAIN; + default: + return EIO; + } + + /* Copy out the data. */ + node.sysctl_data = b; + node.sysctl_size = size; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + + /* Clear the buffer. */ + explicit_memset(b, 0, sizeof(b)); + + /* Return the sysctl_lookup error, if any. */ + return error; +} + +static void +trng_callback(size_t nbytes, void *cookie) +{ + size_t nbits = NBBY*nbytes; + uint8_t b[TRNG_RND_MAXBYTES]; + unsigned nretries = 20; + int error; + + /* + * eRepeat until no more bits needed. + */ + while (nbits) { + /* + * Grab a batch of up to 12 bytes. If there's nothing + * available yet, try up to 20 times -- and allow + * preemption, in case that helps make progress -- + * before giving up. + */ + switch (error = trng_rnd(b)) { + case TRNG_SUCCESS: + break; + case TRNG_NO_ENTROPY: + trng_no_entropy.ev_count++; + if (--nretries == 0) + goto out; + preempt_point(); + continue; + default: + trng_failed.ev_count++; + printf("%s: TRNG_RND failed, error=%d\n", TRNG_NAME, + error); + goto out; + } + + /* + * From the SMCCC TRNG spec: + * + * 1.1.2 Back end implementation requirements + * + * The conditioned entropy that is provided by a + * TRNG Back end can be used in security sensitive + * use-cases. The security properties of these + * use-cases root themselves on the guarantees + * provided by the TRNG Back end. + * + * A Back end implementation must be analyzed. + * The outcome of this analysis should guarantee + * that: + * + * . Each conditioned entropy bit is discarded + * after being provided to a client. + * + * . If multiple entropy sources exist in the + * system, they must be uncorrelated. + * + * . The entropy source complies with the + * guidelines that are detailed in [NIST + * SP800-90B]. + * + * - The Back end implementation must implement + * a health test on the raw noise. + * + * - The Back end implementation must implement + * a vetted conditioning component. + * + * Unless we have information about a specific device + * failing to meet these requirements, which we can + * identify by UUID, we'll just take the samples to + * have full entropy. + */ + rnd_add_data_sync(&trng_source, b, sizeof(b), NBBY*sizeof(b)); + nbits -= MIN(nbits, NBBY*sizeof(b)); + } + +out: explicit_memset(b, 0, sizeof(b)); +} + +int +smc_trng_probe(void) +{ + printf("%s:%d %s\n", __FILE__, __LINE__, __func__); + const struct sysctlnode *node; + int error; + + /* + * Probe the SMCCC: + * + * - Require SMCCC version >=1.1. + * - Require TRNG SMC version >=1.0. + * - Require TRNG_RND to be supported. + * - Get the UUID if possible. + * + * XXX Consider disabling counting entropy if the UUID is not + * available -- that means we have no opportunity to avoid + * counting entropy from sources we know to be bad by their + * UUID, in case information comes out about them. + */ + if (smccc_version() < 0x10001) + return ENXIO; + printf("%s:%d %s\n", __FILE__, __LINE__, __func__); + if (trng_version() < TRNG_VERSION_1_0) + return ENXIO; + printf("%s:%d %s\n", __FILE__, __LINE__, __func__); + if ((trng_rnd_features = trng_features(TRNG_RND)) < 0) + return ENXIO; + printf("%s:%d %s\n", __FILE__, __LINE__, __func__); + if (trng_features(TRNG_GET_UUID) >= 0) { + if (trng_get_uuid() != 0) + aprint_debug("Arm SMC TRNG: TRNG_GET_UUID failed\n"); + } + + printf("%s:%d %s\n", __FILE__, __LINE__, __func__); + + /* hw.arm_smc_trng (node) */ + error = sysctl_createv(&trng_sysctllog, 0, NULL, &node, + CTLFLAG_PERMANENT, CTLTYPE_NODE, TRNG_NAME, + SYSCTL_DESCR("Arm SMC TRNG"), + NULL, 0, NULL, 0, + CTL_HW, CTL_CREATE, CTL_EOL); + if (error) { + aprint_error("%s: failed to set up sysctl hw.%s: %d\n", + TRNG_NAME, TRNG_NAME, error); + return error; + } + + printf("%s:%d %s\n", __FILE__, __LINE__, __func__); + + /* hw.arm_smc_trng.uuid (`struct', 16-byte array) */ + error = sysctl_createv(&trng_sysctllog, 0, &node, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READONLY|CTLFLAG_PRIVATE, CTLTYPE_STRUCT, + "uuid", SYSCTL_DESCR("TRNG_RND features"), + NULL, 0, trng_uuid, sizeof(trng_uuid), + CTL_CREATE, CTL_EOL); + if (error) { + aprint_error("%s: failed to set up sysctl hw.%s.uuid: %d\n", + TRNG_NAME, TRNG_NAME, error); + return error; + } + + printf("%s:%d %s\n", __FILE__, __LINE__, __func__); + + /* hw.arm_smc_trng.rnd_features (int) */ + error = sysctl_createv(&trng_sysctllog, 0, &node, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READONLY|CTLFLAG_PRIVATE, CTLTYPE_INT, + "rnd_features", SYSCTL_DESCR("TRNG_RND features"), + NULL, 0, &trng_rnd_features, sizeof(trng_rnd_features), + CTL_CREATE, CTL_EOL); + if (error) { + aprint_error("%s: failed to set up sysctl hw.%s.rnd_features:" + " %d\n", + TRNG_NAME, TRNG_NAME, error); + return error; + } + + printf("%s:%d %s\n", __FILE__, __LINE__, __func__); + + /* hw.arm_smc_trng.rnd (`struct', 12-byte array) */ + error = sysctl_createv(&trng_sysctllog, 0, &node, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READONLY|CTLFLAG_PRIVATE, CTLTYPE_STRUCT, + "rnd", SYSCTL_DESCR("Issue TRNG_RND to read 12 bytes"), + &trng_sysctl, 0, NULL, 0, CTL_CREATE, CTL_EOL); + if (error) { + aprint_error("%s: failed to set up sysctl hw.%s.rnd: %d\n", + TRNG_NAME, TRNG_NAME, error); + return error; + } + + printf("%s:%d %s\n", __FILE__, __LINE__, __func__); + + evcnt_attach_dynamic(&trng_no_entropy, EVCNT_TYPE_MISC, NULL, + TRNG_NAME, "no entropy"); + evcnt_attach_dynamic(&trng_failed, EVCNT_TYPE_MISC, NULL, + TRNG_NAME, "failed"); + + printf("%s:%d %s\n", __FILE__, __LINE__, __func__); + + rndsource_setcb(&trng_source, &trng_callback, NULL); + rnd_attach_source(&trng_source, TRNG_NAME, RND_TYPE_RNG, + RND_FLAG_DEFAULT|RND_FLAG_HASCB); + + printf("%s:%d %s\n", __FILE__, __LINE__, __func__); + + return 0; +} diff --git a/sys/arch/arm/arm/smc_trng.h b/sys/arch/arm/arm/smc_trng.h new file mode 100644 index 000000000000..0d8ff06c1176 --- /dev/null +++ b/sys/arch/arm/arm/smc_trng.h @@ -0,0 +1,34 @@ +/* $NetBSD$ */ + +/*- + * Copyright (c) 2022 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _ARM_ARM_SMC_TRNG_H_ +#define _ARM_ARM_SMC_TRNG_H_ + +int smc_trng_probe(void); + +#endif /* _ARM_ARM_SMC_TRNG_H_ */ diff --git a/sys/arch/evbarm/conf/GENERIC64 b/sys/arch/evbarm/conf/GENERIC64 index d34bf6ef0947..8e9fa37c2f29 100644 --- a/sys/arch/evbarm/conf/GENERIC64 +++ b/sys/arch/evbarm/conf/GENERIC64 @@ -62,6 +62,9 @@ options ARMV81_PAN #makeoptions ARMV85_BTI=1 #options ARMV85_BTI +# Firmware TRNG through SMC interface. +options ARM_SMC_TRNG + # Kernel Undefined Behavior Sanitizer (kUBSan). Use UBSAN_ALWAYS_FATAL # if you want panics instead of warnings. #options KUBSAN # mandatory