0950f751ff0f2d77d680f4a91e97258442a6f748
[gnuk/gnuk.git] / src / main.c
1 /*
2  * main.c - main routine of Gnuk
3  *
4  * Copyright (C) 2010, 2011, 2012, 2013 Free Software Initiative of Japan
5  * Author: NIIBE Yutaka <gniibe@fsij.org>
6  *
7  * This file is a part of Gnuk, a GnuPG USB Token implementation.
8  *
9  * Gnuk is free software: you can redistribute it and/or modify it
10  * under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Gnuk is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
17  * License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  */
23
24 #include <stdint.h>
25 #include <string.h>
26 #include <chopstx.h>
27 #include <eventflag.h>
28
29 #include "config.h"
30 #include "board.h"
31
32 #include "sys.h"
33 #include "adc.h"
34 #include "gnuk.h"
35 #include "usb_lld.h"
36 #include "usb-cdc.h"
37 #include "random.h"
38 #include "stm32f103.h"
39
40 #ifdef DEBUG
41 #include "debug.h"
42
43 struct stdout stdout;
44
45 static void
46 stdout_init (void)
47 {
48   chopstx_mutex_init (&stdout.m);
49   chopstx_mutex_init (&stdout.m_dev);
50   chopstx_cond_init (&stdout.cond_dev);
51   stdout.connected = 0;
52 }
53
54 void
55 _write (const char *s, int len)
56 {
57   int packet_len;
58
59   if (len == 0)
60     return;
61
62   chopstx_mutex_lock (&stdout.m);
63
64   chopstx_mutex_lock (&stdout.m_dev);
65   if (!stdout.connected)
66     chopstx_cond_wait (&stdout.cond_dev, &stdout.m_dev);
67   chopstx_mutex_unlock (&stdout.m_dev);
68
69   do
70     {
71       packet_len =
72         (len < VIRTUAL_COM_PORT_DATA_SIZE) ? len : VIRTUAL_COM_PORT_DATA_SIZE;
73
74       chopstx_mutex_lock (&stdout.m_dev);     
75       usb_lld_write (ENDP3, s, packet_len);
76       chopstx_cond_wait (&stdout.cond_dev, &stdout.m_dev);
77       chopstx_mutex_unlock (&stdout.m_dev);
78
79       s += packet_len;
80       len -= packet_len;
81     }
82   /* Send a Zero-Length-Packet if the last packet is full size.  */
83   while (len != 0 || packet_len == VIRTUAL_COM_PORT_DATA_SIZE);
84
85   chopstx_mutex_unlock (&stdout.m);
86 }
87
88 void
89 EP3_IN_Callback (void)
90 {
91   chopstx_mutex_lock (&stdout.m_dev);
92   chopstx_cond_signal (&stdout.cond_dev);
93   chopstx_mutex_unlock (&stdout.m_dev);
94 }
95
96 void
97 EP5_OUT_Callback (void)
98 {
99   chopstx_mutex_lock (&stdout.m_dev);
100   usb_lld_rx_enable (ENDP5);
101   chopstx_mutex_unlock (&stdout.m_dev);
102 }
103 #else
104 void
105 _write (const char *s, int size)
106 {
107   (void)s;
108   (void)size;
109 }
110 #endif
111
112 extern void *USBthread (void *arg);
113
114 /*
115  * main thread does 1-bit LED display output
116  */
117 #define MAIN_TIMEOUT_INTERVAL   (5000*1000)
118
119 #define LED_TIMEOUT_INTERVAL    (75*1000)
120 #define LED_TIMEOUT_ZERO        (25*1000)
121 #define LED_TIMEOUT_ONE         (100*1000)
122 #define LED_TIMEOUT_STOP        (200*1000)
123
124
125 #define ID_OFFSET (2+SERIALNO_STR_LEN*2)
126 static void
127 device_initialize_once (void)
128 {
129   const uint8_t *p = &gnukStringSerial[ID_OFFSET];
130
131   if (p[0] == 0xff && p[1] == 0xff && p[2] == 0xff && p[3] == 0xff)
132     {
133       /*
134        * This is the first time invocation.
135        * Setup serial number by unique device ID.
136        */
137       const uint8_t *u = unique_device_id ();
138       int i;
139
140       for (i = 0; i < 4; i++)
141         {
142           uint8_t b = u[i];
143           uint8_t nibble;
144
145           nibble = (b >> 4);
146           nibble += (nibble >= 10 ? ('A' - 10) : '0');
147           flash_put_data_internal (&p[i*4], nibble);
148           nibble = (b & 0x0f);
149           nibble += (nibble >= 10 ? ('A' - 10) : '0');
150           flash_put_data_internal (&p[i*4+2], nibble);
151         }
152     }
153 }
154
155
156 static volatile uint8_t fatal_code;
157 static struct eventflag led_event;
158
159 static void display_fatal_code (void)
160 {
161   while (1)
162     {
163       set_led (1);
164       chopstx_usec_wait (LED_TIMEOUT_ZERO);
165       set_led (0);
166       chopstx_usec_wait (LED_TIMEOUT_INTERVAL);
167       set_led (1);
168       chopstx_usec_wait (LED_TIMEOUT_ZERO);
169       set_led (0);
170       chopstx_usec_wait (LED_TIMEOUT_INTERVAL);
171       set_led (1);
172       chopstx_usec_wait (LED_TIMEOUT_ZERO);
173       set_led (0);
174       chopstx_usec_wait (LED_TIMEOUT_STOP);
175       set_led (1);
176       if (fatal_code & 1)
177         chopstx_usec_wait (LED_TIMEOUT_ONE);
178       else
179         chopstx_usec_wait (LED_TIMEOUT_ZERO);
180       set_led (0);
181       chopstx_usec_wait (LED_TIMEOUT_INTERVAL);
182       set_led (1);
183       if (fatal_code & 2)
184         chopstx_usec_wait (LED_TIMEOUT_ONE);
185       else
186         chopstx_usec_wait (LED_TIMEOUT_ZERO);
187       set_led (0);
188       chopstx_usec_wait (LED_TIMEOUT_INTERVAL);
189       set_led (1);
190       chopstx_usec_wait (LED_TIMEOUT_STOP);
191       set_led (0);
192       chopstx_usec_wait (LED_TIMEOUT_INTERVAL*10);
193     }
194 }
195
196 static uint8_t led_inverted;
197
198 static eventmask_t emit_led (int on_time, int off_time)
199 {
200   eventmask_t m;
201
202   set_led (!led_inverted);
203   m = eventflag_wait_timeout (&led_event, on_time);
204   set_led (led_inverted);
205   if (m) return m;
206   if ((m = eventflag_wait_timeout (&led_event, off_time)))
207     return m;
208   return 0;
209 }
210
211 static eventmask_t display_status_code (void)
212 {
213   enum icc_state icc_state;
214   eventmask_t m;
215
216   if (icc_state_p == NULL)
217     icc_state = ICC_STATE_START;
218   else
219     icc_state = *icc_state_p;
220
221   if (icc_state == ICC_STATE_START)
222     return emit_led (LED_TIMEOUT_ONE, LED_TIMEOUT_STOP);
223   else
224     /* OpenPGP card thread  running */
225     {
226       if ((m = emit_led ((auth_status & AC_ADMIN_AUTHORIZED)?
227                           LED_TIMEOUT_ONE : LED_TIMEOUT_ZERO,
228                           LED_TIMEOUT_INTERVAL)))
229         return m;
230       if ((m = emit_led ((auth_status & AC_OTHER_AUTHORIZED)?
231                           LED_TIMEOUT_ONE : LED_TIMEOUT_ZERO,
232                           LED_TIMEOUT_INTERVAL)))
233         return m;
234       if ((m = emit_led ((auth_status & AC_PSO_CDS_AUTHORIZED)?
235                           LED_TIMEOUT_ONE : LED_TIMEOUT_ZERO,
236                           LED_TIMEOUT_INTERVAL)))
237         return m;
238
239       if (icc_state == ICC_STATE_WAIT)
240         {
241           if ((m = eventflag_wait_timeout (&led_event, LED_TIMEOUT_STOP * 2)))
242             return m;
243         }
244       else
245         {
246           if ((m = eventflag_wait_timeout (&led_event, LED_TIMEOUT_INTERVAL)))
247             return m;
248
249           if ((m = emit_led (icc_state == ICC_STATE_RECEIVE?
250                               LED_TIMEOUT_ONE : LED_TIMEOUT_ZERO,
251                               LED_TIMEOUT_STOP)))
252             return m;
253         }
254
255       return 0;
256     }
257 }
258
259 void
260 led_blink (int spec)
261 {
262   eventflag_signal (&led_event, spec);
263 }
264
265 /*
266  * In Gnuk 1.0.[12], reGNUal was not relocatable.
267  * Now, it's relocatable, but we need to calculate its entry address
268  * based on it's pre-defined address.
269  */
270 #define REGNUAL_START_ADDRESS_COMPATIBLE 0x20001400
271 static uint32_t
272 calculate_regnual_entry_address (const uint8_t *addr)
273 {
274   const uint8_t *p = addr + 4;
275   uint32_t v = p[0] + (p[1] << 8) + (p[2] << 16) + (p[3] << 24);
276
277   v -= REGNUAL_START_ADDRESS_COMPATIBLE;
278   v += (uint32_t)addr;
279   return v;
280 }
281
282 extern uint8_t __process1_stack_base__, __process1_stack_size__;
283 extern uint8_t __process4_stack_base__, __process4_stack_size__;
284
285 const uint32_t __stackaddr_ccid = (uint32_t)&__process1_stack_base__;
286 const size_t __stacksize_ccid = (size_t)&__process1_stack_size__;
287
288 const uint32_t __stackaddr_usb = (uint32_t)&__process4_stack_base__;
289 const size_t __stacksize_usb = (size_t)&__process4_stack_size__;
290
291 #define PRIO_CCID 3
292 #define PRIO_USB  4
293
294 extern void *usb_intr (void *arg);
295
296 static void gnuk_malloc_init (void);
297
298
299 extern uint32_t bDeviceState;
300
301 /*
302  * Entry point.
303  *
304  * NOTE: the main function is already a thread in the system on entry.
305  *       See the hwinit1_common function.
306  */
307 int
308 main (int argc, char *argv[])
309 {
310   unsigned int count = 0;
311   uint32_t entry;
312   chopstx_t usb_thd;
313   chopstx_t ccid_thd;
314
315   (void)argc;
316   (void)argv;
317
318   gnuk_malloc_init ();
319
320   flash_unlock ();
321   device_initialize_once ();
322
323   adc_init ();
324
325   eventflag_init (&led_event, chopstx_main);
326
327   random_init ();
328
329 #ifdef DEBUG
330   stdout_init ();
331 #endif
332
333   ccid_thd = chopstx_create (PRIO_CCID, __stackaddr_ccid,
334                              __stacksize_ccid, USBthread, NULL);
335
336 #ifdef PINPAD_CIR_SUPPORT
337   cir_init ();
338 #endif
339 #ifdef PINPAD_DND_SUPPORT
340   msc_init ();
341 #endif
342
343   usb_thd = chopstx_create (PRIO_USB, __stackaddr_usb, __stacksize_usb,
344                             usb_intr, NULL);
345
346   while (1)
347     {
348       if (bDeviceState != UNCONNECTED)
349         break;
350
351       chopstx_usec_wait (250*1000);
352     }
353
354   while (1)
355     {
356       eventmask_t m;
357
358       if (icc_state_p != NULL && *icc_state_p == ICC_STATE_EXEC_REQUESTED)
359         break;
360
361       m = eventflag_wait_timeout (&led_event, MAIN_TIMEOUT_INTERVAL);
362     got_it:
363       count++;
364       switch (m)
365         {
366         case LED_ONESHOT:
367           if ((m = emit_led (100*1000, MAIN_TIMEOUT_INTERVAL))) goto got_it;
368           break;
369         case LED_TWOSHOTS:
370           if ((m = emit_led (50*1000, 50*1000))) goto got_it;
371           if ((m = emit_led (50*1000, MAIN_TIMEOUT_INTERVAL))) goto got_it;
372           break;
373         case LED_SHOW_STATUS:
374           if ((count & 0x07) != 0) continue; /* Display once for eight times */
375           if ((m = display_status_code ())) goto got_it;
376           break;
377         case LED_START_COMMAND:
378           set_led (1);
379           led_inverted = 1;
380           break;
381         case LED_FINISH_COMMAND:
382           m = eventflag_wait_timeout (&led_event, LED_TIMEOUT_STOP);
383           led_inverted = 0;
384           set_led (0);
385           if (m)
386             goto got_it;
387           break;
388         case LED_FATAL:
389           display_fatal_code ();
390           break;
391         default:
392           if ((m = emit_led (LED_TIMEOUT_ZERO, LED_TIMEOUT_STOP)))
393             goto got_it;
394           break;
395         }
396
397 #ifdef DEBUG_MORE
398       if (stdout.connected && (count % 10) == 0)
399         {
400           DEBUG_SHORT (count / 10);
401           _write ("\r\nThis is Gnuk on STM32F103.\r\n"
402                   "Testing USB driver.\n\n"
403                   "Hello world\r\n\r\n", 30+21+15);
404         }
405 #endif
406     }
407
408   random_fini ();
409
410   set_led (1);
411   usb_lld_shutdown ();
412
413   /* Finish application.  */
414   chopstx_join (ccid_thd, NULL);
415
416   chopstx_cancel (usb_thd);
417   chopstx_join (usb_thd, NULL);
418
419   /* Set vector */
420   SCB->VTOR = (uint32_t)&_regnual_start;
421   entry = calculate_regnual_entry_address (&_regnual_start);
422 #ifdef DFU_SUPPORT
423 #define FLASH_SYS_START_ADDR 0x08000000
424 #define FLASH_SYS_END_ADDR (0x08000000+0x1000)
425   {
426     extern uint8_t _sys;
427     uint32_t addr;
428     handler *new_vector = (handler *)FLASH_SYS_START_ADDR;
429     void (*func) (void (*)(void)) = (void (*)(void (*)(void)))new_vector[9];
430
431     /* Kill DFU */
432     for (addr = FLASH_SYS_START_ADDR; addr < FLASH_SYS_END_ADDR;
433          addr += FLASH_PAGE_SIZE)
434       flash_erase_page (addr);
435
436     /* copy system service routines */
437     flash_write (FLASH_SYS_START_ADDR, &_sys, 0x1000);
438
439     /* Leave Gnuk to exec reGNUal */
440     (*func) ((void (*)(void))entry);
441     for (;;);
442   }
443 #else
444   /* Leave Gnuk to exec reGNUal */
445   flash_erase_all_and_exec ((void (*)(void))entry);
446 #endif
447
448   /* Never reached */
449   return 0;
450 }
451
452 void
453 fatal (uint8_t code)
454 {
455   fatal_code = code;
456   eventflag_signal (&led_event, LED_FATAL);
457   _write ("fatal\r\n", 7);
458   for (;;);
459 }
460 \f
461 /*
462  * Malloc for Gnuk.
463  *
464  * Each memory chunk has header with size information.
465  * The size of chunk is at least 16.
466  *
467  * Free memory is managed by FREE_LIST.
468  *
469  * When it is managed in FREE_LIST, three pointers, ->NEXT, ->PREV,
470  * and ->NEIGHBOR is used.  NEXT and PREV is to implement doubly
471  * linked list.  NEIGHBOR is to link adjacent memory chunk to be
472  * reclaimed to system.
473  */
474
475 extern uint8_t __heap_base__[];
476 extern uint8_t __heap_end__[];
477
478 #define MEMORY_END (__heap_end__)
479 #define MEMORY_ALIGNMENT 16
480 #define MEMORY_ALIGN(n) (((n) + MEMORY_ALIGNMENT - 1) & ~(MEMORY_ALIGNMENT - 1))
481
482 static uint8_t *heap_p;
483 static chopstx_mutex_t malloc_mtx;
484
485 struct mem_head {
486   uint32_t size;
487   /**/
488   struct mem_head *next, *prev; /* free list chain */
489   struct mem_head *neighbor;    /* backlink to neighbor */
490 };
491
492 static struct mem_head *free_list;
493
494 static void
495 gnuk_malloc_init (void)
496 {
497   chopstx_mutex_init (&malloc_mtx);
498   heap_p = __heap_base__;
499   free_list = NULL;
500 }
501
502 static void *
503 sbrk (size_t size)
504 {
505   void *p = (void *)heap_p;
506
507   if ((size_t)(MEMORY_END - heap_p) < size)
508     return NULL;
509
510   heap_p += size;
511   return p;
512 }
513
514 static void
515 remove_from_free_list (struct mem_head *m)
516 {
517   if (m->prev)
518     m->prev->next = m->next;
519   else
520     free_list = m->next;
521   if (m->next)
522     m->next->prev = m->prev;
523 }
524
525
526 void *
527 gnuk_malloc (size_t size)
528 {
529   struct mem_head *m;
530   struct mem_head *m0;
531
532   size = MEMORY_ALIGN (size + sizeof (uint32_t));
533
534   chopstx_mutex_lock (&malloc_mtx);
535   DEBUG_INFO ("malloc: ");
536   DEBUG_SHORT (size);
537   m = free_list;
538
539   while (1)
540     {
541       if (m == NULL)
542         {
543           m = (struct mem_head *)sbrk (size);
544           if (m)
545             m->size = size;
546           break;
547         }
548
549       if (m->size == size)
550         {
551           remove_from_free_list (m);
552           m0 = free_list;
553           while (m0)
554             if (m0->neighbor == m)
555               m0->neighbor = NULL;
556             else
557               m0 = m0->next;
558           break;
559         }
560
561       m = m->next;
562     }
563
564   chopstx_mutex_unlock (&malloc_mtx);
565   if (m == NULL)
566     {
567       DEBUG_WORD (0);
568       return m;
569     }
570   else
571     {
572       DEBUG_WORD ((uint32_t)m + sizeof (uint32_t));
573       return (void *)m + sizeof (uint32_t);
574     }
575 }
576
577
578 void
579 gnuk_free (void *p)
580 {
581   struct mem_head *m = (struct mem_head *)((void *)p - sizeof (uint32_t));
582   struct mem_head *m0;
583
584   chopstx_mutex_lock (&malloc_mtx);
585   m0 = free_list;
586   DEBUG_INFO ("free: ");
587   DEBUG_SHORT (m->size);
588   DEBUG_WORD ((uint32_t)p);
589
590   m->neighbor = NULL;
591   while (m0)
592     {
593       if ((void *)m + m->size == (void *)m0)
594         m0->neighbor = m;
595       else if ((void *)m0 + m0->size == (void *)m)
596         m->neighbor = m0;
597
598       m0 = m0->next;
599     }
600
601   if ((void *)m + m->size == heap_p)
602     {
603       struct mem_head *mn = m->neighbor;
604
605       heap_p -= m->size;
606       while (mn)
607         {
608           heap_p -= mn->size;
609           remove_from_free_list (mn);
610           mn = mn->neighbor;
611         }
612     }
613   else
614     {
615       m->next = free_list;
616       m->prev = NULL;
617       if (free_list)
618         free_list->prev = m;
619       free_list = m;
620     }
621
622   chopstx_mutex_unlock (&malloc_mtx);
623 }