git: 1492c8c0d825 - main - qcom_rnd: add initial qualcomm prng driver.
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 04 Nov 2021 16:02:51 UTC
The branch main has been updated by adrian: URL: https://cgit.FreeBSD.org/src/commit/?id=1492c8c0d825d1dcd8a2b2975fe35e4269b0b7de commit 1492c8c0d825d1dcd8a2b2975fe35e4269b0b7de Author: Adrian Chadd <adrian@FreeBSD.org> AuthorDate: 2021-10-21 03:05:10 +0000 Commit: Adrian Chadd <adrian@FreeBSD.org> CommitDate: 2021-11-04 16:02:27 +0000 qcom_rnd: add initial qualcomm prng driver. This is the MVP required to initialise and consume random data from the QCA PRNG hardware found on the IPQ401x. Reviewed by: andrew, manu, imp Differential Revision: https://reviews.freebsd.org/D32723 --- sys/dev/qcom_rnd/qcom_rnd.c | 257 ++++++++++++++++++++++++++++++++++++++++ sys/dev/qcom_rnd/qcom_rnd_reg.h | 43 +++++++ sys/sys/random.h | 1 + 3 files changed, 301 insertions(+) diff --git a/sys/dev/qcom_rnd/qcom_rnd.c b/sys/dev/qcom_rnd/qcom_rnd.c new file mode 100644 index 000000000000..c78438a29ba0 --- /dev/null +++ b/sys/dev/qcom_rnd/qcom_rnd.c @@ -0,0 +1,257 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021, Adrian Chadd <adrian@FreeBSD.org> + * + * 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 unmodified, 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 AUTHOR ``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 AUTHOR 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. + */ + +/* Driver for Qualcomm MSM entropy device. */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/sglist.h> +#include <sys/random.h> +#include <sys/stdatomic.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/bus.h> + +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <dev/random/randomdev.h> +#include <dev/random/random_harvestq.h> + +#include <dev/qcom_rnd/qcom_rnd_reg.h> + +struct qcom_rnd_softc { + device_t dev; + int reg_rid; + struct resource *reg; +}; + +static int qcom_rnd_modevent(module_t, int, void *); + +static int qcom_rnd_probe(device_t); +static int qcom_rnd_attach(device_t); +static int qcom_rnd_detach(device_t); + +static int qcom_rnd_harvest(struct qcom_rnd_softc *, void *, size_t *); +static unsigned qcom_rnd_read(void *, unsigned); + +static struct random_source random_qcom_rnd = { + .rs_ident = "Qualcomm Entropy Adapter", + .rs_source = RANDOM_PURE_QUALCOMM, + .rs_read = qcom_rnd_read, +}; + +/* Kludge for API limitations of random(4). */ +static _Atomic(struct qcom_rnd_softc *) g_qcom_rnd_softc; + +static int +qcom_rnd_modevent(module_t mod, int type, void *unused) +{ + int error; + + switch (type) { + case MOD_LOAD: + case MOD_QUIESCE: + case MOD_UNLOAD: + case MOD_SHUTDOWN: + error = 0; + break; + default: + error = EOPNOTSUPP; + break; + } + + return (error); +} + +static int +qcom_rnd_probe(device_t dev) +{ + if (! ofw_bus_status_okay(dev)) { + return (ENXIO); + } + + if (ofw_bus_is_compatible(dev, "qcom,prng") == 0) { + return (ENXIO); + } + + return (0); +} + +static int +qcom_rnd_attach(device_t dev) +{ + struct qcom_rnd_softc *sc, *exp; + uint32_t reg; + + sc = device_get_softc(dev); + + + /* Found a compatible device! */ + + sc->dev = dev; + + exp = NULL; + if (!atomic_compare_exchange_strong_explicit(&g_qcom_rnd_softc, &exp, + sc, memory_order_release, memory_order_acquire)) { + return (ENXIO); + } + + sc->reg_rid = 0; + sc->reg = bus_alloc_resource_anywhere(dev, SYS_RES_MEMORY, + &sc->reg_rid, 0x140, RF_ACTIVE); + if (sc->reg == NULL) { + device_printf(dev, "Couldn't allocate memory resource!\n"); + return (ENXIO); + } + + device_set_desc(dev, "Qualcomm PRNG"); + + /* + * Check to see whether the PRNG has already been setup or not. + */ + bus_barrier(sc->reg, 0, 0x120, BUS_SPACE_BARRIER_READ); + reg = bus_read_4(sc->reg, QCOM_RND_PRNG_CONFIG); + if (reg & QCOM_RND_PRNG_CONFIG_HW_ENABLE) { + device_printf(dev, "PRNG HW already enabled\n"); + } else { + /* + * Do PRNG setup and then enable it. + */ + reg = bus_read_4(sc->reg, QCOM_RND_PRNG_LFSR_CFG); + reg &= QCOM_RND_PRNG_LFSR_CFG_MASK; + reg |= QCOM_RND_PRNG_LFSR_CFG_CLOCKS; + bus_write_4(sc->reg, QCOM_RND_PRNG_LFSR_CFG, reg); + bus_barrier(sc->reg, 0, 0x120, BUS_SPACE_BARRIER_WRITE); + + reg = bus_read_4(sc->reg, QCOM_RND_PRNG_CONFIG); + reg |= QCOM_RND_PRNG_CONFIG_HW_ENABLE; + bus_write_4(sc->reg, QCOM_RND_PRNG_CONFIG, reg); + bus_barrier(sc->reg, 0, 0x120, BUS_SPACE_BARRIER_WRITE); + } + + random_source_register(&random_qcom_rnd); + return (0); + +} + +static int +qcom_rnd_detach(device_t dev) +{ + struct qcom_rnd_softc *sc; + + sc = device_get_softc(dev); + KASSERT( + atomic_load_explicit(&g_qcom_rnd_softc, memory_order_acquire) == sc, + ("only one global instance at a time")); + + random_source_deregister(&random_qcom_rnd); + if (sc->reg != NULL) { + bus_release_resource(sc->dev, SYS_RES_MEMORY, sc->reg_rid, sc->reg); + } + atomic_store_explicit(&g_qcom_rnd_softc, NULL, memory_order_release); + return (0); +} + +static int +qcom_rnd_harvest(struct qcom_rnd_softc *sc, void *buf, size_t *sz) +{ + /* + * Add data to buf until we either run out of entropy or we + * fill the buffer. + * + * Note - be mindful of the provided buffer size; we're reading + * 4 bytes at a time but we only want to supply up to the max + * buffer size, so don't write past it! + */ + size_t rz = 0; + uint32_t reg; + + while (rz < *sz) { + bus_barrier(sc->reg, 0, 0x120, BUS_SPACE_BARRIER_READ); + reg = bus_read_4(sc->reg, QCOM_RND_PRNG_STATUS); + if ((reg & QCOM_RND_PRNG_STATUS_DATA_AVAIL) == 0) + break; + reg = bus_read_4(sc->reg, QCOM_RND_PRNG_DATA_OUT); + memcpy(((char *) buf) + rz, ®, sizeof(uint32_t)); + rz += sizeof(uint32_t); + } + + if (rz == 0) + return (EAGAIN); + *sz = rz; + return (0); +} + +static unsigned +qcom_rnd_read(void *buf, unsigned usz) +{ + struct qcom_rnd_softc *sc; + size_t sz; + int error; + + sc = g_qcom_rnd_softc; + if (sc == NULL) + return (0); + + sz = usz; + error = qcom_rnd_harvest(sc, buf, &sz); + if (error != 0) + return (0); + + return (sz); +} + +static device_method_t qcom_rnd_methods[] = { + /* Device methods. */ + DEVMETHOD(device_probe, qcom_rnd_probe), + DEVMETHOD(device_attach, qcom_rnd_attach), + DEVMETHOD(device_detach, qcom_rnd_detach), + + DEVMETHOD_END +}; + +static driver_t qcom_rnd_driver = { + "qcom_rnd", + qcom_rnd_methods, + sizeof(struct qcom_rnd_softc) +}; +static devclass_t qcom_rnd_devclass; + +DRIVER_MODULE(qcom_rnd_random, simplebus, qcom_rnd_driver, qcom_rnd_devclass, + qcom_rnd_modevent, 0); +DRIVER_MODULE(qcom_rnd_random, ofwbus, qcom_rnd_driver, qcom_rnd_devclass, + qcom_rnd_modevent, 0); +MODULE_DEPEND(qcom_rnd_random, random_device, 1, 1, 1); +MODULE_VERSION(qcom_rnd_random, 1); diff --git a/sys/dev/qcom_rnd/qcom_rnd_reg.h b/sys/dev/qcom_rnd/qcom_rnd_reg.h new file mode 100644 index 000000000000..64cb0d678b6b --- /dev/null +++ b/sys/dev/qcom_rnd/qcom_rnd_reg.h @@ -0,0 +1,43 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021, Adrian Chadd <adrian@FreeBSD.org> + * + * 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 unmodified, 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 AUTHOR ``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 AUTHOR 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 __QCOM_RND_REG_H__ +#define __QCOM_RND_REG_H__ + +#define QCOM_RND_PRNG_DATA_OUT 0x0000 + +#define QCOM_RND_PRNG_STATUS 0x0004 +#define QCOM_RND_PRNG_STATUS_DATA_AVAIL (1 << 0) + +#define QCOM_RND_PRNG_LFSR_CFG 0x0100 +#define QCOM_RND_PRNG_LFSR_CFG_MASK 0x0000ffff +#define QCOM_RND_PRNG_LFSR_CFG_CLOCKS 0x0000dddd + +#define QCOM_RND_PRNG_CONFIG 0x0104 +#define QCOM_RND_PRNG_CONFIG_HW_ENABLE (1 << 1) + +#endif /* __QCOM_RND_REG_H__ */ diff --git a/sys/sys/random.h b/sys/sys/random.h index e2c4ab144d2a..cfcf4b30e698 100644 --- a/sys/sys/random.h +++ b/sys/sys/random.h @@ -102,6 +102,7 @@ enum random_entropy_source { RANDOM_PURE_DARN, RANDOM_PURE_TPM, RANDOM_PURE_VMGENID, + RANDOM_PURE_QUALCOMM, ENTROPYSOURCE }; _Static_assert(ENTROPYSOURCE <= 32,