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