4d90d24f278a347a3b9e5fe6490d10cc16eda59f
[fw/altos] / src / drivers / ao_trng.c
1 /*
2  * Copyright © 2015 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #include <ao.h>
19 #include <ao_adc_fast.h>
20 #include <ao_crc.h>
21 #include <ao_boot.h>
22 #include <ao_trng.h>
23
24 static struct ao_task ao_blink_green_task;
25 static uint8_t ao_blinking_green = 0;
26
27 static void
28 ao_blink_green(void)
29 {
30         for (;;) {
31                 while (!ao_blinking_green)
32                         ao_sleep(&ao_blinking_green);
33                 while (ao_blinking_green) {
34                         ao_led_toggle(AO_LED_GREEN);
35                         ao_delay(AO_MS_TO_TICKS(1000));
36                 }
37         }
38 }
39
40 static struct ao_task ao_blink_red_task;
41 static uint8_t ao_failed = 0; /* 0 NOMINAL, 1 FAILED */
42 static uint8_t ao_post = 0; /* 0 POST needed, 1 powered up */
43
44 /* On handling failure, keithp said:
45  We could disconnect from USB easily enough, or disconnect and come back
46  with a different setup that the kernel driver could report as an
47  error. Lots of options.
48 */
49
50 void
51 ao_trng_failure()
52 {
53         ao_failed = 1;
54         ao_wakeup(&ao_failed);
55 }
56
57 #ifdef DEBUG_FIPS
58
59 static void
60 ao_trng_fetch(void)
61 {
62         static uint16_t *buffer[2];
63         uint32_t        kbytes = 1;
64         uint32_t        count;
65         int             usb_buf_id;
66         uint16_t        i;
67         uint16_t        *buf;
68         uint16_t        t;
69         uint32_t        *rnd = (uint32_t *) ao_adc_ring;
70         uint32_t        cur;
71         uint32_t        prev = 0;
72         uint8_t         prev_set = 0; /* prev has been set */
73
74         if (!buffer[0]) {
75                 buffer[0] = ao_usb_alloc();
76                 buffer[1] = ao_usb_alloc();
77                 if (!buffer[0])
78                         return;
79         }
80
81         ao_cmd_decimal();
82         if (ao_cmd_status == ao_cmd_success)
83                 kbytes = ao_cmd_lex_u32;
84         else
85                 ao_cmd_status = ao_cmd_success;
86         usb_buf_id = 0;
87         count = kbytes * (1024/AO_USB_IN_SIZE);
88
89         ao_crc_reset();
90
91         ao_led_on(AO_LED_TRNG_READ);
92         while (count--) {
93                 t = ao_adc_get(AO_USB_IN_SIZE) >> 1;    /* one 16-bit value per output byte */
94                 buf = buffer[usb_buf_id];
95                 for (i = 0; i < AO_USB_IN_SIZE / sizeof (uint16_t); i++) {
96                         cur = rnd[t];
97                         if (prev_set && (cur == prev))
98                                 ao_trng_failure();
99                         *buf++ = ao_crc_in_32_out_16(cur);
100                         t = (t + 1) & ((AO_ADC_RING_SIZE>>1) - 1);
101                         prev = cur;
102                         prev_set = 1;
103                 }
104                 ao_adc_ack(AO_USB_IN_SIZE);
105                 ao_led_toggle(AO_LED_TRNG_READ|AO_LED_TRNG_WRITE);
106                 ao_usb_write(buffer[usb_buf_id], AO_USB_IN_SIZE);
107                 ao_led_toggle(AO_LED_TRNG_READ|AO_LED_TRNG_WRITE);
108                 usb_buf_id = 1-usb_buf_id;
109         }
110         ao_led_off(AO_LED_TRNG_READ|AO_LED_TRNG_WRITE);
111         flush();
112 }
113
114 static void
115 ao_trng_fetch_cmd(void)
116 {
117         if (!ao_failed)
118                 ao_trng_fetch();
119 }
120
121 static void
122 ao_trng_status(void)
123 {
124         if (ao_failed)
125                 printf("FAILED\n");
126         else
127                 printf("NOMINAL\n");
128 }
129
130 void ao_trng_reset(void); /* forward declaration */
131
132 static void
133 ao_blink_green_toggle(void)
134 {
135         ao_blinking_green = !ao_blinking_green;
136         if (!ao_blinking_green)
137                 ao_led_off(AO_LED_GREEN);
138         ao_wakeup(&ao_blinking_green);
139 }
140
141 static const struct ao_cmds ao_trng_cmds[] = {
142         { ao_trng_fetch_cmd, "f <kbytes>\0Fetch a block of numbers" },
143         { ao_trng_reset, "R\0Reset" },
144         { ao_blink_green_toggle, "G\0Toggle green LED blinking" },
145         { ao_trng_status, "s\0Show status" },
146         { ao_trng_failure, "z\0Simulate failure" },
147         { 0, NULL },
148 };
149
150 #else
151
152 static void
153 ao_trng_send(void)
154 {
155         static uint16_t *buffer[2];
156         int             usb_buf_id;
157         uint16_t        i;
158         uint16_t        *buf;
159         uint16_t        t;
160         uint32_t        *rnd = (uint32_t *) ao_adc_ring;
161
162         if (!buffer[0]) {
163                 buffer[0] = ao_usb_alloc();
164                 buffer[1] = ao_usb_alloc();
165                 if (!buffer[0])
166                         return;
167         }
168
169         usb_buf_id = 0;
170
171         ao_crc_reset();
172
173         for (;;) {
174                 ao_led_on(AO_LED_TRNG_ACTIVE);
175                 t = ao_adc_get(AO_USB_IN_SIZE) >> 1;    /* one 16-bit value per output byte */
176                 buf = buffer[usb_buf_id];
177                 for (i = 0; i < AO_USB_IN_SIZE / sizeof (uint16_t); i++) {
178                         *buf++ = ao_crc_in_32_out_16(rnd[t]);
179                         t = (t + 1) & ((AO_ADC_RING_SIZE>>1) - 1);
180                 }
181                 ao_adc_ack(AO_USB_IN_SIZE);
182                 ao_led_off(AO_LED_TRNG_ACTIVE);
183                 ao_usb_write(buffer[usb_buf_id], AO_USB_IN_SIZE);
184                 usb_buf_id = 1-usb_buf_id;
185         }
186 }
187
188 static struct ao_task ao_trng_send_task;
189
190 static void
191 ao_bootloader_cmd(void)
192 {
193         for (;;) {
194                 getchar(); /* any char will do */
195                 /* give feedback we are going into bootloader mode */
196                 ao_led_on(AO_LED_GREEN);
197                 ao_delay(AO_MS_TO_TICKS(500));
198                 ao_led_off(AO_LED_GREEN);
199                 ao_delay(AO_MS_TO_TICKS(500));
200                 ao_led_on(AO_LED_GREEN);
201                 ao_delay(AO_MS_TO_TICKS(500));
202                 ao_led_off(AO_LED_GREEN);
203                 ao_delay(AO_MS_TO_TICKS(500));
204                 ao_led_on(AO_LED_GREEN);
205                 ao_delay(AO_MS_TO_TICKS(500));
206                 ao_led_off(AO_LED_GREEN);
207                 ao_boot_loader();
208         }
209 }
210
211 static struct ao_task ao_bootloader_cmd_task;
212
213 #endif
214
215
216 /* NOTE: the reset function also functions as the Power On Self Test */
217 void
218 ao_trng_reset(void)
219 {
220         /* printf("Resetting...\n"); */
221         ao_failed = 0;
222         ao_led_off(AO_LED_RED);
223         ao_wakeup(&ao_failed);
224         /* get the first 1k bits and ensure there are no duplicates */
225         /* FIXME ao_trng_fetch(); */
226         if (!ao_failed) {
227                 ao_led_on(AO_LED_GREEN);
228                 ao_delay(AO_MS_TO_TICKS(1000));
229                 ao_led_off(AO_LED_GREEN);
230         }
231 }
232
233 static void
234 ao_blink_red(void)
235 {
236         if (!ao_post) {
237                 ao_trng_reset(); /* POST */
238                 ao_post = 1;
239         }
240         for (;;) {
241                 while (!ao_failed)
242                         ao_sleep(&ao_failed);
243                 while (ao_failed) {
244                         ao_led_toggle(AO_LED_RED);
245                         ao_delay(AO_MS_TO_TICKS(500));
246                 }
247         }
248 }
249
250 void
251 ao_trng_init(void)
252 {
253         ao_add_task(&ao_blink_red_task, ao_blink_red, "blink_red");
254         ao_add_task(&ao_blink_green_task, ao_blink_green, "blink_green");
255 #ifdef DEBUG_FIPS
256         ao_cmd_register(ao_trng_cmds);
257 #else
258         ao_add_task(&ao_bootloader_cmd_task, ao_bootloader_cmd, "bootloader_cmd");
259         ao_add_task(&ao_trng_send_task, ao_trng_send, "trng_send");
260 #endif
261 }