| /**************************************************************************** |
| * arch/arm/src/stm32/stm32_rng.c |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <stdint.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <debug.h> |
| #include <errno.h> |
| |
| #include <nuttx/irq.h> |
| #include <nuttx/arch.h> |
| #include <nuttx/mutex.h> |
| #include <nuttx/semaphore.h> |
| #include <nuttx/fs/fs.h> |
| #include <nuttx/drivers/drivers.h> |
| |
| #include "hardware/stm32_rng.h" |
| #include "arm_internal.h" |
| |
| #if defined(CONFIG_STM32_RNG) |
| #if defined(CONFIG_DEV_RANDOM) || defined(CONFIG_DEV_URANDOM_ARCH) |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| |
| static int stm32_rng_initialize(void); |
| static int stm32_rng_interrupt(int irq, void *context, void *arg); |
| static void stm32_rng_enable(void); |
| static void stm32_rng_disable(void); |
| static ssize_t stm32_rng_read(struct file *filep, char *buffer, size_t); |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| struct rng_dev_s |
| { |
| mutex_t rd_devlock; /* Threads can only exclusively access the RNG */ |
| sem_t rd_readsem; /* To block until the buffer is filled */ |
| char *rd_buf; |
| size_t rd_buflen; |
| uint32_t rd_lastval; |
| bool rd_first; |
| }; |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| static struct rng_dev_s g_rngdev = |
| { |
| .rd_devlock = NXMUTEX_INITIALIZER, |
| .rd_readsem = SEM_INITIALIZER(0), |
| }; |
| |
| static const struct file_operations g_rngops = |
| { |
| NULL, /* open */ |
| NULL, /* close */ |
| stm32_rng_read, /* read */ |
| }; |
| |
| /**************************************************************************** |
| * Private functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: stm32_rng_initialize |
| ****************************************************************************/ |
| |
| static int stm32_rng_initialize(void) |
| { |
| uint32_t regval; |
| |
| _info("Initializing RNG\n"); |
| |
| if (irq_attach(STM32_IRQ_RNG, stm32_rng_interrupt, NULL)) |
| { |
| /* We could not attach the ISR to the interrupt */ |
| |
| _info("Could not attach IRQ.\n"); |
| |
| return -EAGAIN; |
| } |
| |
| /* Enable interrupts */ |
| |
| regval = getreg32(STM32_RNG_CR); |
| regval |= RNG_CR_IE; |
| putreg32(regval, STM32_RNG_CR); |
| |
| up_enable_irq(STM32_IRQ_RNG); |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: stm32_rng_enable |
| ****************************************************************************/ |
| |
| static void stm32_rng_enable(void) |
| { |
| uint32_t regval; |
| |
| g_rngdev.rd_first = true; |
| |
| regval = getreg32(STM32_RNG_CR); |
| regval |= RNG_CR_RNGEN; |
| putreg32(regval, STM32_RNG_CR); |
| } |
| |
| /**************************************************************************** |
| * Name: stm32_rng_disable |
| ****************************************************************************/ |
| |
| static void stm32_rng_disable(void) |
| { |
| uint32_t regval; |
| regval = getreg32(STM32_RNG_CR); |
| regval &= ~RNG_CR_RNGEN; |
| putreg32(regval, STM32_RNG_CR); |
| } |
| |
| /**************************************************************************** |
| * Name: stm32_rng_interrupt |
| ****************************************************************************/ |
| |
| static int stm32_rng_interrupt(int irq, void *context, void *arg) |
| { |
| uint32_t rngsr; |
| uint32_t data; |
| |
| rngsr = getreg32(STM32_RNG_SR); |
| |
| if ((rngsr & (RNG_SR_SEIS | RNG_SR_CEIS)) /* Check for error bits */ |
| || !(rngsr & RNG_SR_DRDY)) /* Data ready must be set */ |
| { |
| /* This random value is not valid, we will try again. */ |
| |
| return OK; |
| } |
| |
| data = getreg32(STM32_RNG_DR); |
| |
| /* As required by the FIPS PUB (Federal Information Processing Standard |
| * Publication) 140-2, the first random number generated after setting the |
| * RNGEN bit should not be used, but saved for comparison with the next |
| * generated random number. Each subsequent generated random number has to |
| * be compared with the previously generated number. The test fails if any |
| * two compared numbers are equal (continuous random number generator |
| * test). |
| */ |
| |
| if (g_rngdev.rd_first) |
| { |
| g_rngdev.rd_first = false; |
| g_rngdev.rd_lastval = data; |
| return OK; |
| } |
| |
| if (g_rngdev.rd_lastval == data) |
| { |
| /* Two subsequent same numbers, we will try again. */ |
| |
| return OK; |
| } |
| |
| /* If we get here, the random number is valid. */ |
| |
| g_rngdev.rd_lastval = data; |
| |
| if (g_rngdev.rd_buflen >= 4) |
| { |
| g_rngdev.rd_buflen -= 4; |
| *(uint32_t *)&g_rngdev.rd_buf[g_rngdev.rd_buflen] = data; |
| } |
| else |
| { |
| while (g_rngdev.rd_buflen > 0) |
| { |
| g_rngdev.rd_buf[--g_rngdev.rd_buflen] = (char)data; |
| data >>= 8; |
| } |
| } |
| |
| if (g_rngdev.rd_buflen == 0) |
| { |
| /* Buffer filled, stop further interrupts. */ |
| |
| stm32_rng_disable(); |
| nxsem_post(&g_rngdev.rd_readsem); |
| } |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: stm32_rng_read |
| ****************************************************************************/ |
| |
| static ssize_t stm32_rng_read(struct file *filep, char *buffer, |
| size_t buflen) |
| { |
| int ret; |
| |
| ret = nxmutex_lock(&g_rngdev.rd_devlock); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| /* We've got the mutex. */ |
| |
| /* Reset the operation semaphore with 0 for blocking until the |
| * buffer is filled from interrupts. |
| */ |
| |
| nxsem_reset(&g_rngdev.rd_readsem, 0); |
| |
| g_rngdev.rd_buflen = buflen; |
| g_rngdev.rd_buf = buffer; |
| |
| /* Enable RNG with interrupts */ |
| |
| stm32_rng_enable(); |
| |
| /* Wait until the buffer is filled */ |
| |
| ret = nxsem_wait(&g_rngdev.rd_readsem); |
| |
| /* Free RNG for next use */ |
| |
| nxmutex_unlock(&g_rngdev.rd_devlock); |
| return ret < 0 ? ret : buflen; |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: devrandom_register |
| * |
| * Description: |
| * Initialize the RNG hardware and register the /dev/random driver. |
| * Must be called BEFORE devurandom_register. |
| * |
| * Input Parameters: |
| * None |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_DEV_RANDOM |
| void devrandom_register(void) |
| { |
| stm32_rng_initialize(); |
| register_driver("/dev/random", &g_rngops, 0444, NULL); |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: devurandom_register |
| * |
| * Description: |
| * Register /dev/urandom |
| * |
| * Input Parameters: |
| * None |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_DEV_URANDOM_ARCH |
| void devurandom_register(void) |
| { |
| #ifndef CONFIG_DEV_RANDOM |
| stm32_rng_initialize(); |
| #endif |
| register_driver("/dev/urandom", &g_rngops, 0444, NULL); |
| } |
| #endif |
| |
| #endif /* CONFIG_DEV_RANDOM || CONFIG_DEV_URANDOM_ARCH */ |
| #endif /* CONFIG_STM32_RNG */ |