The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/qcom_rnd/qcom_rnd.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2021, Adrian Chadd <adrian@FreeBSD.org>
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice unmodified, this list of conditions, and the following
   11  *    disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   26  */
   27 
   28 /* Driver for Qualcomm MSM entropy device. */
   29 
   30 #include <sys/cdefs.h>
   31 __FBSDID("$FreeBSD$");
   32 
   33 #include <sys/param.h>
   34 #include <sys/kernel.h>
   35 #include <sys/malloc.h>
   36 #include <sys/module.h>
   37 #include <sys/sglist.h>
   38 #include <sys/random.h>
   39 #include <sys/stdatomic.h>
   40 
   41 #include <machine/bus.h>
   42 #include <machine/resource.h>
   43 #include <sys/bus.h>
   44 
   45 #include <dev/fdt/fdt_common.h>
   46 #include <dev/ofw/ofw_bus.h>
   47 #include <dev/ofw/ofw_bus_subr.h>
   48 
   49 #include <dev/random/randomdev.h>
   50 #include <dev/random/random_harvestq.h>
   51 
   52 #include <dev/qcom_rnd/qcom_rnd_reg.h>
   53 
   54 struct qcom_rnd_softc {
   55         device_t         dev;
   56         int             reg_rid;
   57         struct resource *reg;
   58 };
   59 
   60 static int      qcom_rnd_modevent(module_t, int, void *);
   61 
   62 static int      qcom_rnd_probe(device_t);
   63 static int      qcom_rnd_attach(device_t);
   64 static int      qcom_rnd_detach(device_t);
   65 
   66 static int      qcom_rnd_harvest(struct qcom_rnd_softc *, void *, size_t *);
   67 static unsigned qcom_rnd_read(void *, unsigned);
   68 
   69 static struct random_source random_qcom_rnd = {
   70         .rs_ident = "Qualcomm Entropy Adapter",
   71         .rs_source = RANDOM_PURE_QUALCOMM,
   72         .rs_read = qcom_rnd_read,
   73 };
   74 
   75 /* Kludge for API limitations of random(4). */
   76 static _Atomic(struct qcom_rnd_softc *) g_qcom_rnd_softc;
   77 
   78 static int
   79 qcom_rnd_modevent(module_t mod, int type, void *unused)
   80 {
   81         int error;
   82 
   83         switch (type) {
   84         case MOD_LOAD:
   85         case MOD_QUIESCE:
   86         case MOD_UNLOAD:
   87         case MOD_SHUTDOWN:
   88                 error = 0;
   89                 break;
   90         default:
   91                 error = EOPNOTSUPP;
   92                 break;
   93         }
   94 
   95         return (error);
   96 }
   97 
   98 static int
   99 qcom_rnd_probe(device_t dev)
  100 {
  101         if (! ofw_bus_status_okay(dev)) {
  102                 return (ENXIO);
  103         }
  104 
  105         if (ofw_bus_is_compatible(dev, "qcom,prng") == 0) {
  106                 return (ENXIO);
  107         }
  108 
  109         return (0);
  110 }
  111 
  112 static int
  113 qcom_rnd_attach(device_t dev)
  114 {
  115         struct qcom_rnd_softc *sc, *exp;
  116         uint32_t reg;
  117 
  118         sc = device_get_softc(dev);
  119 
  120 
  121         /* Found a compatible device! */
  122 
  123         sc->dev = dev;
  124 
  125         exp = NULL;
  126         if (!atomic_compare_exchange_strong_explicit(&g_qcom_rnd_softc, &exp,
  127             sc, memory_order_release, memory_order_acquire)) {
  128                 return (ENXIO);
  129         }
  130 
  131         sc->reg_rid = 0;
  132         sc->reg = bus_alloc_resource_anywhere(dev, SYS_RES_MEMORY,
  133             &sc->reg_rid, 0x140, RF_ACTIVE);
  134         if (sc->reg == NULL) {
  135                 device_printf(dev, "Couldn't allocate memory resource!\n");
  136                 return (ENXIO);
  137         }
  138 
  139         device_set_desc(dev, "Qualcomm PRNG");
  140 
  141         /*
  142          * Check to see whether the PRNG has already been setup or not.
  143          */
  144         bus_barrier(sc->reg, 0, 0x120, BUS_SPACE_BARRIER_READ);
  145         reg = bus_read_4(sc->reg, QCOM_RND_PRNG_CONFIG);
  146         if (reg & QCOM_RND_PRNG_CONFIG_HW_ENABLE) {
  147                 device_printf(dev, "PRNG HW already enabled\n");
  148         } else {
  149                 /*
  150                  * Do PRNG setup and then enable it.
  151                  */
  152                 reg = bus_read_4(sc->reg, QCOM_RND_PRNG_LFSR_CFG);
  153                 reg &= QCOM_RND_PRNG_LFSR_CFG_MASK;
  154                 reg |= QCOM_RND_PRNG_LFSR_CFG_CLOCKS;
  155                 bus_write_4(sc->reg, QCOM_RND_PRNG_LFSR_CFG, reg);
  156                 bus_barrier(sc->reg, 0, 0x120, BUS_SPACE_BARRIER_WRITE);
  157 
  158                 reg = bus_read_4(sc->reg, QCOM_RND_PRNG_CONFIG);
  159                 reg |= QCOM_RND_PRNG_CONFIG_HW_ENABLE;
  160                 bus_write_4(sc->reg, QCOM_RND_PRNG_CONFIG, reg);
  161                 bus_barrier(sc->reg, 0, 0x120, BUS_SPACE_BARRIER_WRITE);
  162         }
  163 
  164         random_source_register(&random_qcom_rnd);
  165         return (0);
  166 
  167 }
  168 
  169 static int
  170 qcom_rnd_detach(device_t dev)
  171 {
  172         struct qcom_rnd_softc *sc;
  173 
  174         sc = device_get_softc(dev);
  175         KASSERT(
  176             atomic_load_explicit(&g_qcom_rnd_softc, memory_order_acquire) == sc,
  177             ("only one global instance at a time"));
  178 
  179         random_source_deregister(&random_qcom_rnd);
  180         if (sc->reg != NULL) {
  181                 bus_release_resource(sc->dev, SYS_RES_MEMORY, sc->reg_rid, sc->reg);
  182         }
  183         atomic_store_explicit(&g_qcom_rnd_softc, NULL, memory_order_release);
  184         return (0);
  185 }
  186 
  187 static int
  188 qcom_rnd_harvest(struct qcom_rnd_softc *sc, void *buf, size_t *sz)
  189 {
  190         /*
  191          * Add data to buf until we either run out of entropy or we
  192          * fill the buffer.
  193          *
  194          * Note - be mindful of the provided buffer size; we're reading
  195          * 4 bytes at a time but we only want to supply up to the max
  196          * buffer size, so don't write past it!
  197          */
  198         size_t rz = 0;
  199         uint32_t reg;
  200 
  201         while (rz < *sz) {
  202                 bus_barrier(sc->reg, 0, 0x120, BUS_SPACE_BARRIER_READ);
  203                 reg = bus_read_4(sc->reg, QCOM_RND_PRNG_STATUS);
  204                 if ((reg & QCOM_RND_PRNG_STATUS_DATA_AVAIL) == 0)
  205                         break;
  206                 reg = bus_read_4(sc->reg, QCOM_RND_PRNG_DATA_OUT);
  207                 memcpy(((char *) buf) + rz, &reg, sizeof(uint32_t));
  208                 rz += sizeof(uint32_t);
  209         }
  210 
  211         if (rz == 0)
  212                 return (EAGAIN);
  213         *sz = rz;
  214         return (0);
  215 }
  216 
  217 static unsigned
  218 qcom_rnd_read(void *buf, unsigned usz)
  219 {
  220         struct qcom_rnd_softc *sc;
  221         size_t sz;
  222         int error;
  223 
  224         sc = g_qcom_rnd_softc;
  225         if (sc == NULL)
  226                 return (0);
  227 
  228         sz = usz;
  229         error = qcom_rnd_harvest(sc, buf, &sz);
  230         if (error != 0)
  231                 return (0);
  232 
  233         return (sz);
  234 }
  235 
  236 static device_method_t qcom_rnd_methods[] = {
  237         /* Device methods. */
  238         DEVMETHOD(device_probe,         qcom_rnd_probe),
  239         DEVMETHOD(device_attach,        qcom_rnd_attach),
  240         DEVMETHOD(device_detach,        qcom_rnd_detach),
  241 
  242         DEVMETHOD_END
  243 };
  244 
  245 static driver_t qcom_rnd_driver = {
  246         "qcom_rnd",
  247         qcom_rnd_methods,
  248         sizeof(struct qcom_rnd_softc)
  249 };
  250 
  251 DRIVER_MODULE(qcom_rnd_random, simplebus, qcom_rnd_driver,
  252     qcom_rnd_modevent, 0);
  253 DRIVER_MODULE(qcom_rnd_random, ofwbus, qcom_rnd_driver,
  254     qcom_rnd_modevent, 0);
  255 MODULE_DEPEND(qcom_rnd_random, random_device, 1, 1, 1);
  256 MODULE_VERSION(qcom_rnd_random, 1);

Cache object: 7af50f59948c7a0cba7dae80dd65cf33


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.