diff options
author | Fredrik Thulin <fredrik@thulin.net> | 2015-01-15 17:04:03 +0100 |
---|---|---|
committer | Fredrik Thulin <fredrik@thulin.net> | 2015-01-15 17:04:03 +0100 |
commit | 39cac7918987fc603dc54886e107af026583592e (patch) | |
tree | acb95e5cebc2f410840659cc7e6d1a74331e3985 /src/entropy/main.c | |
parent | 3347165e2927ec73014eafdfc0c650904a4b67d4 (diff) |
init
Diffstat (limited to 'src/entropy/main.c')
-rw-r--r-- | src/entropy/main.c | 369 |
1 files changed, 369 insertions, 0 deletions
diff --git a/src/entropy/main.c b/src/entropy/main.c new file mode 100644 index 0000000..e29e00c --- /dev/null +++ b/src/entropy/main.c @@ -0,0 +1,369 @@ +/* + * Copyright (c) 2014, 2015 NORDUnet A/S + * 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. + * 3. Neither the name of the NORDUnet nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 + * COPYRIGHT HOLDER 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. + */ +#include <string.h> + +#include "main.h" +#include "stm_init.h" + +#define MODE_DELTAS 1 +#define MODE_ENTROPY 2 + +#define UART_RANDOM_BYTES_PER_CHUNK 8 +#define UART_DELTA_WORDS_PER_CHUNK 32 + +extern DMA_HandleTypeDef hdma_tim; + +__IO ITStatus UartReady = RESET; + +static union { + uint8_t rnd[257]; /* 256 bytes + 1 for use in the POST */ + uint32_t rnd32[64]; +} buf; + +/* First DMA value (DMA_counters[0]) is unreliable, leftover in DMA FIFO perhaps? */ +#define FIRST_DMA_IDX_USED 3 + +/* + * Number of counters used to produce 8 bits of entropy is: + * 8 * 4 - four flanks are used to produce two (hopefully) uncorrelated bits (a and b) + * * 2 - von Neumann will on average discard 1/2 of the bits 'a' and 'b' + */ +#define DMA_COUNTERS_NUM ((UART_RANDOM_BYTES_PER_CHUNK * 8 * 4 * 2) + FIRST_DMA_IDX_USED + 1) +struct DMA_params { + volatile uint32_t buf0[DMA_COUNTERS_NUM]; + volatile uint32_t buf1[DMA_COUNTERS_NUM]; + volatile uint32_t write_buf; +}; + +static struct DMA_params DMA = { + {}, + {}, + 0, +}; + + +/* The main work horse functions */ +void get_entropy32(uint32_t num_bytes, uint32_t buf_idx); +void perform_delta32(uint32_t count, const uint32_t start); +/* Various support functions */ +inline uint32_t get_one_bit(void) __attribute__((__always_inline__)); +volatile uint32_t *restart_DMA(void); +inline volatile uint32_t *get_DMA_read_buf(void); +inline uint32_t safe_get_counter(volatile uint32_t *dmabuf, const uint32_t dmabuf_idx); +/* static void Error_Handler(void); */ + + + +int +main() +{ + uint32_t count = 0, send_bytes, idx = 255, send_sync_bytes_in = 0, i; + uint32_t mode = MODE_ENTROPY; + + /* Initialize buffers */ + memset(buf.rnd, 0, sizeof(buf.rnd)); + for (i = 0; i < DMA_COUNTERS_NUM; i++) { + DMA.buf0[i] = 0xffff0000 + i; + DMA.buf1[i] = 0xffff0100 + i; + } + + stm_init((uint32_t *) &DMA.buf0, DMA_COUNTERS_NUM); + /* Ensure there is actual Timer IC counters in both DMA buffers. */ + restart_DMA(); + restart_DMA(); + + /* Toggle GREEN LED to show we've initialized */ + { + for (i = 0; i < 10; i++) { + HAL_GPIO_TogglePin(LED_PORT, LED_GREEN); + HAL_Delay(125); + } + } + + if (mode == MODE_ENTROPY) { + /* Initialize buffer with the first round of entropy */ + idx = 0; + send_bytes = UART_RANDOM_BYTES_PER_CHUNK; + get_entropy32(send_bytes / 4, idx); + } + + /* + * Main loop + */ + while (1) { + if (! (count % 1000)) { + HAL_GPIO_TogglePin(LED_PORT, LED_YELLOW); + } + + switch (mode) + { + case MODE_DELTAS: + if (! send_sync_bytes_in) { + /* Send 128 bits of sync-bytes every 1024 bytes */ + send_sync_bytes_in = 1024; + memset(buf.rnd, 0xf0, sizeof(buf.rnd)); + send_bytes = 16; + } else { + perform_delta32(UART_DELTA_WORDS_PER_CHUNK, 0); + send_bytes = UART_DELTA_WORDS_PER_CHUNK * 4; + send_sync_bytes_in -= send_bytes; + } + idx = 0; + break;; + case MODE_ENTROPY: + break;; + } + + /* Send buf on UART (non blocking interrupt driven send). */ + if (HAL_UART_Transmit_IT(&huart1, (uint8_t *) buf.rnd + idx, (uint16_t) send_bytes) == HAL_OK) { + + if (mode == MODE_ENTROPY) { + /* Flip-flop idx between the value 0 and the value BYTES_PER_CHUNK. + * This enables collecting of BYTES_PER_CHUNK bytes of entropy at the same + * time as the USART sends the previous BYTES_PER_CHUNK using interrupts. + */ + idx = idx ? 0 : UART_RANDOM_BYTES_PER_CHUNK; + get_entropy32(send_bytes / 4, idx / 4); + } + + while (UartReady != SET) { ; } + UartReady = RESET; + } else { + /* Turn on RED LED for one second */ + HAL_GPIO_WritePin(LED_PORT, LED_RED, GPIO_PIN_SET); + HAL_Delay(1000); + HAL_GPIO_WritePin(LED_PORT, LED_RED, GPIO_PIN_RESET); + } + + count++; + } + +} + +/** + * @brief Fill a buffer (buf.rnd32) with Timer IC counter values. + * Each value is 16 bits, but this function packs two counters + * into a 32 bit word so (2 * count) timer values are collected. + * @param count: Number of 32 bit words to collect. + * @param start: Start index value into buf.rnd32. + * @retval None + */ +void perform_delta32(uint32_t count, const uint32_t start) +{ + /* Start at end of buffer so restart_DMA() is called. */ + static uint32_t dmabuf_idx = DMA_COUNTERS_NUM - 1; + volatile uint32_t *dmabuf; + uint32_t i, buf_idx; + + dmabuf = get_DMA_read_buf(); + buf_idx = start; + + do { + if (dmabuf_idx > DMA_COUNTERS_NUM - 1 - 2) { + /* If there are less than two counters available in the dmabuf, + * we need to get a fresh DMA buffer first. + */ + dmabuf = restart_DMA(); + dmabuf_idx = FIRST_DMA_IDX_USED; + } + + i = safe_get_counter(dmabuf, dmabuf_idx++) << 16; + i |= safe_get_counter(dmabuf, dmabuf_idx++); + + /* Store the 32 bits in output buffer */ + buf.rnd32[buf_idx++] = i; + } while (--count); +} + + +/** + * @brief Collect `count' times 32 bits of entropy. + * @param count: Number of 32 bit words to collect. + * @param start: Start index value into buf.rnd32. + * @retval None + */ +inline void get_entropy32(uint32_t count, const uint32_t start) +{ + uint32_t i, bits, buf_idx; + + buf_idx = start; + + do { + bits = 0; + /* Get 32 bits of entropy. + */ + for (i = 32; i; i--) { + bits <<= 1; + bits += get_one_bit(); + } + + /* Store the 32 bits in output buffer */ + buf.rnd32[buf_idx++] = bits; + } while (--count); +} + +/** + * @brief Return one bit of entropy. + * @param None + * @retval One bit, in the LSB of an uint32_t since this is a 32 bit MCU. + */ +inline uint32_t get_one_bit() +{ + register uint32_t a, b, temp; + /* Start at end of buffer so restart_DMA() is called. */ + static uint32_t dmabuf_idx = DMA_COUNTERS_NUM - 1; + volatile uint32_t *dmabuf; + + dmabuf = get_DMA_read_buf(); + + do { + if (dmabuf_idx > DMA_COUNTERS_NUM - 1 - 4) { + /* If there are less than four counters available in the dmabuf, + * we need to get a fresh DMA buffer first. + */ + dmabuf = restart_DMA(); + dmabuf_idx = FIRST_DMA_IDX_USED; + } + + /* Get one bit from two subsequent counter values */ + a = safe_get_counter(dmabuf, dmabuf_idx++) & 1; + temp = safe_get_counter(dmabuf, dmabuf_idx++) & 1; + a ^= temp; + + /* Get another bit from two other counter values. Getting + * two bits from two unrelated [1] pairs of counters is + * supposed to help against phase correlations between the + * frequency of the noise and the MCU sampling rate. + * + * [1] This is how it is done in the ARRGH board, although + * since this is a faster MCU and DMA is used the two + * pairs are more likely to still be related since they + * are more likely to be directly subsequent diode + * breakdowns. Have to evaluate if this is enough. + */ + b = safe_get_counter(dmabuf, dmabuf_idx++) & 1; + temp = safe_get_counter(dmabuf, dmabuf_idx++) & 1; + b ^= temp; + + /* Do von Neumann extraction of a and b to eliminate bias + * (only eliminates bias if a and b are uncorrelated) + */ + } while (a == b); + + return a; +} + +/** + * @brief Return a pointer to the DMA.buf NOT currently being written to + * @param None + * @retval Pointer to buffer currently being read from. + */ +inline volatile uint32_t *get_DMA_read_buf(void) +{ + return DMA.write_buf ? DMA.buf0 : DMA.buf1; +} + +/** + * @brief Return a pointer to the DMA.buf currently being written to + * @param None + * @retval Pointer to buffer currently being written to. + */ +inline volatile uint32_t *get_DMA_write_buf(void) +{ + return DMA.write_buf ? DMA.buf1 : DMA.buf0; +} + +/** + * @brief Initiate DMA collection of another buffer of Timer IC values. + * @param None + * @retval Pointer to buffer full of Timer IC values ready to be consumed. + */ +volatile uint32_t *restart_DMA(void) +{ + /* Wait for transfer complete flag to become SET. Trying to change the + * M0AR register while the DMA is running is a no-no. + */ + while(__HAL_DMA_GET_FLAG(&hdma_tim, __HAL_DMA_GET_TC_FLAG_INDEX(&hdma_tim)) == RESET) { ; } + + /* Switch buffer being written to */ + DMA.write_buf ^= 1; + hdma_tim.Instance->M0AR = (uint32_t) get_DMA_write_buf(); + + /* Start at 0 to help manual inspection */ + TIM2->CNT = 0; + + /* Clear the transfer complete flag before re-enabling DMA */ + __HAL_DMA_CLEAR_FLAG(&hdma_tim, __HAL_DMA_GET_TC_FLAG_INDEX(&hdma_tim)); + __HAL_DMA_ENABLE(&hdma_tim); + + return get_DMA_read_buf(); +} + +/** + * @brief Get one counter value, guaranteed to not have been used before. + * @param dmabuf: Pointer to the current DMA read buffer. + * @param dmabuf_idx: Word index into `dmabuf'. + * @retval One Timer IC counter value. + */ +inline uint32_t safe_get_counter(volatile uint32_t *dmabuf, const uint32_t dmabuf_idx) { + register uint32_t a; + /* Prevent re-use of values. DMA stored values are <= 0xffff. */ + do { + a = dmabuf[dmabuf_idx]; + } while (a > 0xffff); + dmabuf[dmabuf_idx] = 0xffff0000; + return a; +} + +/* UART transmit complete callback */ +void HAL_UART_TxCpltCallback(UART_HandleTypeDef *UH) +{ + if (UH->Instance == USART1) { + /* Signal UART transmit complete to the code in the main loop. */ + UartReady = SET; + } +} + +/** + * @brief This function is executed in case of error occurrence. + * @param None + * @retval None + */ +/* Currently unused function */ +#if 0 +static void Error_Handler(void) +{ + /* Turn on RED LED and then loop indefinitely */ + HAL_GPIO_WritePin(LED_PORT, LED_RED, GPIO_PIN_SET); + while(1) { ; } +} +#endif |