Implement GC for data pool in flash memory.
authorNIIBE Yutaka <gniibe@fsij.org>
Mon, 8 Nov 2010 04:17:30 +0000 (13:17 +0900)
committerNIIBE Yutaka <gniibe@fsij.org>
Mon, 8 Nov 2010 04:17:30 +0000 (13:17 +0900)
ChangeLog
src/flash.c
src/gnuk.h
src/gnuk.ld.in
src/main.c
src/openpgp-do.c

index aa8bc9d..d0af702 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,10 +1,27 @@
 2010-11-08  NIIBE Yutaka  <gniibe@fsij.org>
 
+       Implement GC for data pool in flash memory.
+       * src/openpgp-do.c (gpg_write_digital_signature_counter): New.
+       (gpg_increment_digital_signature_counter): Fix for GC.
+       (gpg_data_scan): Rename from gpg_do_table_init.
+       (gpg_data_copy): New function for copying GC.
+       * src/main.c (main): Call gpg_data_scan with the address which
+       flash_init returns.
+       * src/flash.c (flash_erase_page): New function.
+       (FLASH_DATA_POOL_SIZE): data_pool is 2KiB now.
+       (flash_data): Put a header (GC generation).
+       (flash_init): Implement choosing a data pool page.
+       (flash_data_pool): Removed.
+       (flash_copying_gc): New function.
+       (flash_data_pool_allocate): Call flash_copying_gc when full.
+       (flash_do_write_internal, flash_put_data_internal)
+       (flash_bool_write_internal, flash_cnt123_write_internal): New
+       * src/gnuk.ld.in (gnuk_flash): data_pool is 2KiB now.
+
+       Bug fixes.
        * src/openpgp.c (cmd_change_password, cmd_reset_user_password):
        Write to APDU correctly.
-
        * src/flash.c (flash_warning): Make it public.
-
        * src/openpgp-do.c (do_hist_bytes, do_fp_all, do_cafp_all)
        (do_kgtime_all, do_ds_count): Fix return value.
        (rw_pw_status): Correctly return value.
index 540914c..8ec0acd 100644 (file)
  *       be implemented by its own flash page.
  */
 
-/*
- * Note: Garbage collection and page management with flash erase
- *       is *NOT YET* implemented
- */
-
 #include "config.h"
 #include "ch.h"
 #include "hal.h"
@@ -90,7 +85,8 @@ flash_wait_for_last_operation (uint32_t timeout)
   return status;
 }
 
-#define FLASH_PROGRAM_TIMEOUT 0x10000
+#define FLASH_PROGRAM_TIMEOUT 0x00010000
+#define FLASH_ERASE_TIMEOUT   0x01000000
 
 static int
 flash_program_halfword (uint32_t addr, uint16_t data)
@@ -115,6 +111,29 @@ flash_program_halfword (uint32_t addr, uint16_t data)
   return status;
 }
 
+static int
+flash_erase_page (uint32_t addr)
+{
+  int status;
+
+  status = flash_wait_for_last_operation (FLASH_ERASE_TIMEOUT);
+
+  chSysLock ();
+  if (status == FLASH_COMPLETE)
+    {
+      FLASH->CR |= FLASH_CR_PER;
+      FLASH->AR = addr; 
+      FLASH->CR |= FLASH_CR_STRT;
+
+      status = flash_wait_for_last_operation (FLASH_ERASE_TIMEOUT);
+      if (status != FLASH_TIMEOUT)
+       FLASH->CR &= ~FLASH_CR_PER;
+    }
+  chSysUnlock ()
+
+  return status;
+}
+
 /*
  * Flash memory map
  *
@@ -129,10 +148,13 @@ flash_program_halfword (uint32_t addr, uint16_t data)
  *
  * 1-KiB align padding
  *
- * 1-KiB data pool  * 3
+ * 1-KiB data pool  * 2
  *
  * 3-KiB Key store (512-byte (p, q and N) key-store * 6)
  */
+#define FLASH_DATA_POOL_HEADER_SIZE    2
+#define FLASH_DATA_POOL_SIZE           (1024*2)
+#define FLASH_PAGE_SIZE                        1024
 
 static const uint8_t *data_pool;
 static const uint8_t *keystore_pool;
@@ -140,20 +162,35 @@ static const uint8_t *keystore_pool;
 static uint8_t *last_p;
 static const uint8_t *keystore;
 
+/* The first halfward is generation for the data page (little endian) */
 const uint8_t const flash_data[4] __attribute__ ((section (".gnuk_data"))) = {
-  0xff, 0xff, 0xff, 0xff
+  0x01, 0x00, 0xff, 0xff
 };
 
 /* Linker set this symbol */
 extern uint8_t _data_pool;
 
-void
+const uint8_t *
 flash_init (void)
 {
   const uint8_t *p;
   extern uint8_t _keystore_pool;
+  uint16_t gen0, gen1;
+  uint16_t *gen0_p = (uint16_t *)&_data_pool;
+  uint16_t *gen1_p = (uint16_t *)(&_data_pool + FLASH_PAGE_SIZE);
+
+  /* Check data pool generation and choose the page */
+  gen0 = *gen0_p;
+  gen1 = *gen1_p;
+  if (gen0 == 0xffff)
+    data_pool = &_data_pool + FLASH_PAGE_SIZE;
+  else if (gen1 == 0xffff)
+    data_pool = &_data_pool;
+  else if (gen1 > gen0)
+    data_pool = &_data_pool + FLASH_PAGE_SIZE;
+  else
+    data_pool = &_data_pool;
 
-  data_pool = &_data_pool;
   keystore_pool = &_keystore_pool;
 
   /* Seek empty keystore */
@@ -164,6 +201,7 @@ flash_init (void)
   keystore = p;
 
   flash_unlock ();
+  return data_pool + FLASH_DATA_POOL_HEADER_SIZE;
 }
 
 /*
@@ -187,15 +225,6 @@ flash_init (void)
  *    DATA: data * LEN
  *    PAD:  optional byte for 16-bit alignment
  */
-#define FLASH_DATA_POOL_HEADER_SIZE    2
-#define FLASH_DATA_POOL_SIZE           (1024*3)
-#define FLASH_PAGE_SIZE                        1024
-
-const uint8_t *
-flash_data_pool (void)
-{
-  return data_pool + FLASH_DATA_POOL_HEADER_SIZE;
-}
 
 void
 flash_set_data_pool_last (const uint8_t *p)
@@ -203,48 +232,74 @@ flash_set_data_pool_last (const uint8_t *p)
   last_p = (uint8_t *)p;
 }
 
+/*
+ * We use two pages
+ */
+static int
+flash_copying_gc (void)
+{
+  uint8_t *src, *dst;
+  uint16_t generation;
+
+  if (data_pool == &_data_pool)
+    {
+      src = &_data_pool;
+      dst = &_data_pool + FLASH_PAGE_SIZE;
+    }
+  else
+    {
+      src = &_data_pool + FLASH_PAGE_SIZE;
+      dst = &_data_pool;
+    }
+
+  generation = *(uint16_t *)src;
+  data_pool = dst;
+  gpg_data_copy (data_pool + FLASH_DATA_POOL_HEADER_SIZE);
+  flash_erase_page ((uint32_t)src);
+  flash_program_halfword ((uint32_t)dst, generation);
+  return 0;
+}
+
+static int
+is_data_pool_full (size_t size)
+{
+  return last_p + size > data_pool + FLASH_PAGE_SIZE;
+}
+
 static uint8_t *
 flash_data_pool_allocate (size_t size)
 {
-  uint8_t *p = last_p;
+  uint8_t *p;
 
   size = (size + 1) & ~1;      /* allocation unit is 1-halfword (2-byte) */
 
-  if (last_p + size > data_pool - FLASH_DATA_POOL_HEADER_SIZE + FLASH_PAGE_SIZE)
-    return NULL;               /* XXX: here invoke gc/erase page/.../ */
+  if (is_data_pool_full (size))
+    if (flash_copying_gc () < 0 || /*still*/ is_data_pool_full (size))
+      fatal ();
 
+  p = last_p;
   last_p += size;
   return p;
 }
 
-const uint8_t *
-flash_do_write (uint8_t nr, const uint8_t *data, int len)
+void
+flash_do_write_internal (const uint8_t *p, int nr, const uint8_t *data, int len)
 {
-  const uint8_t *p;
   uint16_t hw;
   uint32_t addr;
   int i;
 
-  DEBUG_INFO ("flash DO\r\n");
-
-  p = flash_data_pool_allocate (2 + len);
-  if (p == NULL)
-    {
-      DEBUG_INFO ("flash data pool allocation failure.\r\n");
-      return NULL;
-    }
-
   addr = (uint32_t)p;
   hw = nr | (len << 8);
   if (flash_program_halfword (addr, hw) != FLASH_COMPLETE)
-    return NULL;
+    flash_warning ("DO WRITE ERROR");
   addr += 2;
 
   for (i = 0; i < len/2; i ++)
     {
       hw = data[i*2] | (data[i*2+1]<<8);
       if (flash_program_halfword (addr, hw) != FLASH_COMPLETE)
-       return NULL;
+       flash_warning ("DO WRITE ERROR");
       addr += 2;
     }
 
@@ -252,10 +307,26 @@ flash_do_write (uint8_t nr, const uint8_t *data, int len)
     {
       hw = data[i*2] | 0xff00;
       if (flash_program_halfword (addr, hw) != FLASH_COMPLETE)
-       return NULL;
+       flash_warning ("DO WRITE ERROR");
       addr += 2;
     }
+}
+
+const uint8_t *
+flash_do_write (uint8_t nr, const uint8_t *data, int len)
+{
+  const uint8_t *p;
+
+  DEBUG_INFO ("flash DO\r\n");
+
+  p = flash_data_pool_allocate (2 + len);
+  if (p == NULL)
+    {
+      DEBUG_INFO ("flash data pool allocation failure.\r\n");
+      return NULL;
+    }
 
+  flash_do_write_internal (p, nr, data, len);
   DEBUG_INFO ("flash DO...done\r\n");
   return p + 1;
 }
@@ -353,6 +424,12 @@ flash_clear_halfword (uint32_t addr)
 }
 
 
+void
+flash_put_data_internal (const uint8_t *p, uint16_t hw)
+{
+  flash_program_halfword ((uint32_t)p, hw);
+}
+
 void
 flash_put_data (uint16_t hw)
 {
@@ -380,6 +457,12 @@ flash_bool_clear (const uint8_t **addr_p)
   *addr_p = NULL;
 }
 
+void
+flash_bool_write_internal (const uint8_t *p, int nr)
+{
+  flash_program_halfword ((uint32_t)p, nr);
+}
+
 const uint8_t *
 flash_bool_write (uint8_t nr)
 {
@@ -423,6 +506,22 @@ flash_cnt123_get_value (const uint8_t *p)
     }
 }
 
+void
+flash_cnt123_write_internal (const uint8_t *p, int which, int v)
+{
+  uint16_t hw;
+
+  hw = NR_COUNTER_123 | (which << 8);
+  flash_program_halfword ((uint32_t)p, hw);
+
+  if (v == 1)
+    return;
+  else if (v == 2)
+    flash_program_halfword ((uint32_t)p+2, 0xc3c3);
+  else                         /* v == 3 */
+    flash_program_halfword ((uint32_t)p+2, 0);
+}
+
 void
 flash_cnt123_increment (uint8_t which, const uint8_t **addr_p)
 {
index f643c5c..8aac0f1 100644 (file)
@@ -72,7 +72,8 @@ extern void write_res_apdu (const uint8_t *p, int len,
                            uint8_t sw1, uint8_t sw2);
 uint16_t data_objects_number_of_bytes;
 
-extern int gpg_do_table_init (void);
+extern void gpg_data_scan (const uint8_t *p);
+extern void gpg_data_copy (const uint8_t *p);
 extern void gpg_do_get_data (uint16_t tag);
 extern void gpg_do_put_data (uint16_t tag, const uint8_t *data, int len);
 extern void gpg_do_public_key (uint8_t kk_byte);
@@ -85,12 +86,11 @@ enum kind_of_key {
   GPG_KEY_FOR_AUTHENTICATION,
 };
 
-extern void flash_init (void);
+extern const uint8_t *flash_init (void);
 extern void flash_do_release (const uint8_t *);
 extern const uint8_t *flash_do_write (uint8_t nr, const uint8_t *data, int len);
 extern uint8_t *flash_key_alloc (void);
 extern void flash_key_release (const uint8_t *);
-extern const uint8_t *flash_data_pool (void);
 extern void flash_set_data_pool_last (const uint8_t *p);
 extern void flash_clear_halfword (uint32_t addr);
 extern void flash_increment_counter (uint8_t counter_tag_nr);
@@ -286,3 +286,8 @@ extern void flash_cnt123_increment (uint8_t which, const uint8_t **addr_p);
 extern void flash_cnt123_clear (const uint8_t **addr_p);
 extern void flash_put_data (uint16_t hw);
 extern void flash_warning (const char *msg);
+
+extern void flash_put_data_internal (const uint8_t *p, uint16_t hw);
+extern void flash_bool_write_internal (const uint8_t *p, int nr);
+extern void flash_cnt123_write_internal (const uint8_t *p, int which, int v);
+extern void flash_do_write_internal (const uint8_t *p, int nr, const uint8_t *data, int len);
index c74ce9b..44b9128 100644 (file)
@@ -126,7 +126,7 @@ SECTIONS
        KEEP(*(.gnuk_data))
        FILL(0xffffffff);
        . = ALIGN(1024);
-       . += 1024*2;
+       . += 1024;
        _keystore_pool = .;
        FILL(0xffffffff);
        . += 1024*3;
index 0e6cb03..57bb856 100644 (file)
@@ -176,14 +176,15 @@ main (int argc, char **argv)
   eventmask_t m;
   uint8_t led_state = 0;
   int count = 0;
+  const uint8_t *flash_data_start;
 
   (void)argc;
   (void)argv;
 
   blinker_thread = chThdSelf ();
 
-  flash_init ();
-  gpg_do_table_init ();
+  flash_data_start = flash_init ();
+  gpg_data_scan (flash_data_start);
 
   usb_lld_init ();
   USB_Init();
index 30fcf82..9635a00 100644 (file)
 
 static uint32_t digital_signature_counter;
 
+static const uint8_t *
+gpg_write_digital_signature_counter (const uint8_t *p, uint32_t dsc)
+{
+  uint16_t hw0, hw1;
+
+  if ((dsc >> 10) == 0)
+    { /* no upper bits */
+      hw1 = NR_COUNTER_DS_LSB | ((dsc & 0x0300) >> 8) | ((dsc & 0x00ff) << 8);
+      flash_put_data_internal (p, hw1);
+      return p+2;
+    }
+  else
+    {
+      hw0 = NR_COUNTER_DS | ((dsc & 0xfc0000) >> 18) | ((dsc & 0x03fc00) >> 2);
+      hw1 = NR_COUNTER_DS_LSB;
+      flash_put_data_internal (p, hw0);
+      flash_put_data_internal (p+2, hw1);
+      return p+4;
+    }
+}
+
 void
 gpg_increment_digital_signature_counter (void)
 {
   uint16_t hw0, hw1;
+  uint32_t dsc = (digital_signature_counter + 1) & 0x00ffffff;
 
-  digital_signature_counter++;
-  digital_signature_counter &= 0x00ffffff;
-
-  if ((digital_signature_counter & 0x03ff) == 0)
+  if ((dsc & 0x03ff) == 0)
     { /* carry occurs from l10 to h14 */
-      hw0 = NR_COUNTER_DS
-       | ((digital_signature_counter & 0x00fc0000) >> 18)
-       | ((digital_signature_counter & 0x0003fc00) >> 2);
-      hw1 = NR_COUNTER_DS_LSB;
+      hw0 = NR_COUNTER_DS | ((dsc & 0xfc0000) >> 18) | ((dsc & 0x03fc00) >> 2);
+      hw1 = NR_COUNTER_DS_LSB; /* zero */
       flash_put_data (hw0);
       flash_put_data (hw1);
     }
   else
     {
-      hw1 = NR_COUNTER_DS_LSB
-       | ((digital_signature_counter & 0x0300) >> 8)
-       | ((digital_signature_counter & 0x00ff) << 8);
+      hw1 = NR_COUNTER_DS_LSB | ((dsc & 0x0300) >> 8) | ((dsc & 0x00ff) << 8);
       flash_put_data (hw1);
     }
+
+  digital_signature_counter = dsc;
 }
 
 #define PASSWORD_ERRORS_MAX 3  /* >= errors, it will be locked */
@@ -901,16 +918,14 @@ gpg_do_table[] = {
 /*
  * Reading data from Flash ROM, initialize DO_PTR, PW_ERR_COUNTERS, etc. 
  */
-int
-gpg_do_table_init (void)
+void
+gpg_data_scan (const uint8_t *p_start)
 {
-  const uint8_t *p, *p_start;
+  const uint8_t *p;
   int i;
   const uint8_t *dsc_h14_p, *dsc_l10_p;
   int dsc_h14, dsc_l10;
 
-  p_start = flash_data_pool ();
-
   dsc_h14_p = dsc_l10_p = NULL;
   pw1_lifetime_p = NULL;
   pw_err_counter_p[PW_ERR_PW1] = NULL;
@@ -999,7 +1014,46 @@ gpg_do_table_init (void)
     }
 
   digital_signature_counter = (dsc_h14 << 10) | dsc_l10;
-  return 0;
+}
+
+void
+gpg_data_copy (const uint8_t *p_start)
+{
+  const uint8_t *p;
+  int i;
+  int v;
+
+  p = gpg_write_digital_signature_counter (p_start, digital_signature_counter);
+
+  if (pw1_lifetime_p != NULL)
+    {
+      flash_bool_write_internal (p, NR_BOOL_PW1_LIFETIME);
+      pw1_lifetime_p = p;
+      p += 2;
+    }
+
+  for (i = 0; i < 3; i++)
+    if ((v = flash_cnt123_get_value (pw_err_counter_p[i])) != 0)
+      {
+       flash_cnt123_write_internal (p, i, v);
+       pw_err_counter_p[i] = p + 2;
+       p += 4;
+      }
+
+  data_objects_number_of_bytes = 0;
+  for (i = NR_DO__FIRST__; i < NR_DO__LAST__; i++)
+    if (do_ptr[i - NR_DO__FIRST__] != NULL)
+      {
+       const uint8_t *do_data = do_ptr[i - NR_DO__FIRST__];
+       int len = do_data[0];
+
+       flash_do_write_internal (p, i, &do_data[1], len);
+       do_ptr[i - NR_DO__FIRST__] = p + 1;
+       p += 2 + ((len + 1) & ~1);
+       data_objects_number_of_bytes += len;
+      }
+
+  flash_set_data_pool_last (p);
 }
 
 static const struct do_table_entry *