changeset 3963:c75589ea56bd

efm32: added an hardware random seed driver
author Alexandre Becoulet <alexandre.becoulet@free.fr>
date Mon, 18 Jun 2018 09:13:23 +0200
parents e4692c34dc13
children 445d75e3ddaa
files arch/efm32/drivers/Makefile arch/efm32/drivers/hwrand/Makefile arch/efm32/drivers/hwrand/hwrand.c arch/efm32/drivers/hwrand/hwrand.config arch/efm32/drivers/hwrand/hwrand.h arch/efm32/drivers/hwrand/hwrand_efm32.S
diffstat 6 files changed, 300 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/arch/efm32/drivers/Makefile	Mon Jun 11 17:31:58 2018 +0200
+++ b/arch/efm32/drivers/Makefile	Mon Jun 18 09:13:23 2018 +0200
@@ -13,3 +13,4 @@
 subdirs-$(CONFIG_DRIVER_EFM32_ADC) += adc
 subdirs-$(CONFIG_DRIVER_EFM32_BITBANG) += bitbang
 subdirs-$(CONFIG_DRIVER_EFR32_RADIO) += radio
+subdirs-$(CONFIG_DRIVER_EFM32_HWRAND) += hwrand
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/arch/efm32/drivers/hwrand/Makefile	Mon Jun 18 09:13:23 2018 +0200
@@ -0,0 +1,1 @@
+objs = hwrand.o hwrand_efm32.o
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/arch/efm32/drivers/hwrand/hwrand.c	Mon Jun 18 09:13:23 2018 +0200
@@ -0,0 +1,189 @@
+/*
+    This file is part of MutekH.
+
+    MutekH is free software; you can redistribute it and/or modify it
+    under the terms of the GNU Lesser General Public License as
+    published by the Free Software Foundation; version 2.1 of the
+    License.
+
+    MutekH is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with MutekH; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+    02110-1301 USA.
+
+    Copyright Alexandre Becoulet <alexandre.becoulet@free.fr> (c) 2018
+*/
+
+#include <hexo/types.h>
+#include <hexo/endian.h>
+#include <hexo/iospace.h>
+
+#include <arch/efm32/cmu.h>
+#include <arch/efm32/rtc.h>
+#include <arch/efm32/devaddr.h>
+
+#include <mutek/mem_alloc.h>
+#include <device/device.h>
+#include <device/resources.h>
+#include <device/driver.h>
+#include <device/class/crypto.h>
+
+#include "hwrand.h"
+
+DRIVER_PV(struct efm32_hwrand_private_s
+{
+  uint16_t ptr;
+  uint16_t size;
+  uint8_t pool[0];
+});
+
+static DEV_CRYPTO_INFO(efm32_hwrand_info)
+{
+  memset(info, 0, sizeof(*info));
+
+  if (accessor->number)
+    return -ENOENT;
+
+  info->name = "hwrand";
+  info->modes_mask = 1 << DEV_CRYPTO_MODE_RANDOM;
+
+  return 0;
+}
+
+static DEV_CRYPTO_REQUEST(efm32_hwrand_request)
+{
+  struct device_s *dev = accessor->dev;
+
+  LOCK_SPIN_IRQ(&dev->lock);
+
+  struct efm32_hwrand_private_s * __restrict__ pv = dev->drv_pv;
+
+  rq->err = -ENOENT;
+  if (pv)
+    {
+      struct dev_crypto_context_s * __restrict__ ctx = rq->ctx;
+
+      rq->err = -ENOTSUP;
+      if (ctx->mode == DEV_CRYPTO_MODE_RANDOM &&
+          (rq->op & DEV_CRYPTO_FINALIZE))
+        {
+          size_t l = pv->size - pv->ptr;
+          size_t rl = rq->len;
+
+          rq->err = -ENOENT;
+          if (rl <= l)
+            {
+              uint_fast8_t ptr = pv->ptr;
+              uint8_t *r = pv->pool + ptr;
+              memcpy(rq->out, r, rl);
+              memset(r, 0, rl);
+              pv->ptr = ptr + rl;
+              rq->err = 0;
+
+              if (rl == l)
+                {
+                  mem_free(pv);
+                  dev->drv_pv = NULL;
+                }
+            }
+        }
+    }
+
+  LOCK_RELEASE_IRQ(&dev->lock);
+
+  kroutine_exec(&rq->base.kr);
+}
+
+static DEV_INIT(efm32_hwrand_init)
+{
+  error_t err;
+
+  uintptr_t size;
+  device_get_param_uint_default(dev, "size", &size, 32);
+  size = align_pow2_up(size, 4);
+  if (!size || size > 256)
+    return -EINVAL;
+
+  CPU_INTERRUPT_SAVESTATE_DISABLE;
+
+  /* Check that HFRC is still selected */
+  err = -EBUSY;
+  if ((cpu_mem_read_32(EFM32_CMU_ADDR + EFM32_CMU_STATUS_ADDR) & EFM32_CMU_STATUS_HFRCOSEL))
+    {
+      /* Enable LE clock */
+      uint32_t coreclken = cpu_mem_read_32(EFM32_CMU_ADDR + EFM32_CMU_HFCORECLKEN0_ADDR);
+      cpu_mem_write_32(EFM32_CMU_ADDR + EFM32_CMU_HFCORECLKEN0_ADDR, coreclken | EFM32_CMU_HFCORECLKEN0_LE);
+
+      /* Enable LFRCO */
+      cpu_mem_write_32(EFM32_CMU_ADDR + EFM32_CMU_OSCENCMD_ADDR, EFM32_CMU_OSCENCMD_LFRCOEN);
+      while (!(cpu_mem_read_32(EFM32_CMU_ADDR + EFM32_CMU_STATUS_ADDR) & EFM32_CMU_STATUS_LFRCORDY))
+        ;
+
+      uint32_t lfsel = cpu_mem_read_32(EFM32_CMU_ADDR + EFM32_CMU_LFCLKSEL_ADDR);
+      cpu_mem_write_32(EFM32_CMU_ADDR + EFM32_CMU_LFCLKSEL_ADDR, EFM32_CMU_LFCLKSEL_LFA(LFRCO));
+
+      /* Enable RTC clock */
+      uint32_t lfen = cpu_mem_read_32(EFM32_CMU_ADDR + EFM32_CMU_LFACLKEN0_ADDR);
+      cpu_mem_write_32(EFM32_CMU_ADDR + EFM32_CMU_LFACLKEN0_ADDR, lfen | EFM32_CMU_LFACLKEN0_RTC);
+
+      /* Start RTC */
+      cpu_mem_write_32(EFM32_RTC_ADDR + EFM32_RTC_FREEZE_ADDR, 0);
+      cpu_mem_write_32(EFM32_RTC_ADDR + EFM32_RTC_CTRL_ADDR, EFM32_RTC_CTRL_EN_COUNT);
+
+      /* Start collecting random data */
+      err = -EIO;
+      uint32_t x = efm32_hw_rand32();
+      if (x != HWRAND_CRC32_ALL1 &&
+          x != HWRAND_CRC32_ALL0)
+        {
+          err = -ENOMEM;
+          struct efm32_hwrand_private_s *pv = mem_alloc(sizeof (*pv) + size, (mem_scope_sys));
+
+          if (pv)
+            {
+              dev->drv_pv = pv;
+              pv->ptr = 0;
+              pv->size = size;
+
+              uint8_t *p = pv->pool;
+              endian_le32_na_store(p, x);
+              for (p += 4; size > 4; (p += 4), (size -= 4))
+                endian_le32_na_store(p, efm32_hw_rand32());
+
+              err = 0;
+            }
+        }
+
+      /* Stop RTC */
+      cpu_mem_write_32(EFM32_RTC_ADDR + EFM32_RTC_CTRL_ADDR, 0);
+
+      /* Restore clocks state */
+      cpu_mem_write_32(EFM32_CMU_ADDR + EFM32_CMU_LFACLKEN0_ADDR, lfen);
+      cpu_mem_write_32(EFM32_CMU_ADDR + EFM32_CMU_HFCORECLKEN0_ADDR, coreclken);
+      cpu_mem_write_32(EFM32_CMU_ADDR + EFM32_CMU_LFCLKSEL_ADDR, lfsel);
+    }
+
+  CPU_INTERRUPT_RESTORESTATE;
+  return err;
+}
+
+static DEV_CLEANUP(efm32_hwrand_cleanup)
+{
+  struct efm32_hwrand_private_s  *pv = dev->drv_pv;
+  if (pv)
+    mem_free(pv);
+  return 0;
+}
+
+#define efm32_hwrand_use dev_use_generic
+
+DRIVER_DECLARE(efm32_hwrand_drv, DRIVER_FLAGS_EARLY_INIT,
+               "Hardware random seed", efm32_hwrand,
+               DRIVER_CRYPTO_METHODS(efm32_hwrand));
+
+DRIVER_REGISTER(efm32_hwrand_drv);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/arch/efm32/drivers/hwrand/hwrand.config	Mon Jun 18 09:13:23 2018 +0200
@@ -0,0 +1,6 @@
+%config CONFIG_DRIVER_EFM32_HWRAND
+  desc Enable collecting a random seed at boot
+  parent CONFIG_ARCH_EFM32
+  depend CONFIG_DEVICE_CRYPTO
+  require CONFIG_EFM32_ARCHREV=EFM32_ARCHREV_EFM
+%config end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/arch/efm32/drivers/hwrand/hwrand.h	Mon Jun 18 09:13:23 2018 +0200
@@ -0,0 +1,9 @@
+
+#include <hexo/types.h>
+
+uint32_t efm32_hw_rand32(void);
+
+#define HWRAND_CRC32_POLY 0xe12390be
+#define HWRAND_CRC32_INIT 0xff
+#define HWRAND_CRC32_ALL1 0xa30864d0
+#define HWRAND_CRC32_ALL0 0xdf6e5483
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/arch/efm32/drivers/hwrand/hwrand_efm32.S	Mon Jun 18 09:13:23 2018 +0200
@@ -0,0 +1,94 @@
+/*
+    This file is part of MutekH.
+
+    MutekH is free software; you can redistribute it and/or modify it
+    under the terms of the GNU Lesser General Public License as
+    published by the Free Software Foundation; version 2.1 of the
+    License.
+
+    MutekH is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with MutekH; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+    02110-1301 USA.
+
+    Copyright Alexandre Becoulet <alexandre.becoulet@free.fr> (c) 2018
+*/
+
+#include <hexo/asm.h>
+#include <arch/efm32/devaddr.h>
+#include <arch/efm32/rtc.h>
+#include <arch/efm32/cmu.h>
+
+#include "hwrand.h"
+
+.syntax unified
+
+/* This extracts entropy from the jitter of RC oscillators.
+   It must be called with RTC running on LFRC and cpu running on HFRC. */
+
+FUNC_START(.text, efm32_hw_rand32)
+	push	{r0, r1, r2, r4, r5, r6, r7, lr}
+
+        /* CRC */
+	movs	r0, #HWRAND_CRC32_INIT
+
+        /* LFSR init */
+        movs    r1, #1
+
+        /* RC tunning init */
+	ldr	r6, = EFM32_CMU_ADDR + EFM32_CMU_HFRCOCTRL_ADDR
+        ldr     r2, [r6]
+
+.loop:
+        /* read current RTC value */
+	ldr	r5, = EFM32_RTC_ADDR + EFM32_RTC_CNT_ADDR
+	ldr	r7, [r5]
+
+	movs	r3, #0
+.rtc_poll_loop:
+        /* poll on RTC, waiting for a value change */
+	ldr	r4, [r5]
+	adds	r3, #1
+	cmp	r7, r4
+	beq	.rtc_poll_loop
+
+.rtc_changed:
+	movs	r4, #1
+
+        /* update CRC with jitter bit */
+	lsrs	r5, r0, #1
+	eors	r0, r3
+	ands	r0, r4
+	ldr	r3, = HWRAND_CRC32_POLY
+	negs	r0, r0
+	ands	r0, r3
+	eors	r0, r5
+
+        /* update LFSR */
+	lsrs	r5, r1, #1
+	ands	r1, r4
+	movs	r3, #0xb8  /* polynomial */
+	negs	r1, r1
+	ands	r1, r3
+	eors	r1, r5
+
+        /* change HFRCO tunning */
+        movs    r3, r2
+	eors	r3, r1
+        str     r3, [r6]
+
+        /* while LFSR != 1 */
+	cmp     r4, r1
+	bne	.loop
+
+        /* restore RC tunning */
+        str     r2, [r6]
+
+	pop	{r1, r2, r3, r4, r5, r6, r7, pc}
+
+FUNC_END(efm32_hw_rand32)