From 749a7a1d35d5db9333412ea17a3349b1772d64df Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Mon, 8 Nov 2010 13:17:30 +0900 Subject: [PATCH] Implement GC for data pool in flash memory. --- ChangeLog | 21 +++++- src/flash.c | 193 +++++++++++++++++++++++++++++++++++------------ src/gnuk.h | 11 ++- src/gnuk.ld.in | 2 +- src/main.c | 5 +- src/openpgp-do.c | 88 ++++++++++++++++----- 6 files changed, 248 insertions(+), 72 deletions(-) diff --git a/ChangeLog b/ChangeLog index aa8bc9d..d0af702 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,10 +1,27 @@ 2010-11-08 NIIBE Yutaka + 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. diff --git a/src/flash.c b/src/flash.c index 540914c..8ec0acd 100644 --- a/src/flash.c +++ b/src/flash.c @@ -29,11 +29,6 @@ * 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,27 +232,90 @@ 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; } +void +flash_do_write_internal (const uint8_t *p, int nr, const uint8_t *data, int len) +{ + uint16_t hw; + uint32_t addr; + int i; + + addr = (uint32_t)p; + hw = nr | (len << 8); + if (flash_program_halfword (addr, hw) != FLASH_COMPLETE) + 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) + flash_warning ("DO WRITE ERROR"); + addr += 2; + } + + if ((len & 1)) + { + hw = data[i*2] | 0xff00; + if (flash_program_halfword (addr, hw) != FLASH_COMPLETE) + 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; - uint16_t hw; - uint32_t addr; - int i; DEBUG_INFO ("flash DO\r\n"); @@ -234,28 +326,7 @@ flash_do_write (uint8_t nr, const uint8_t *data, int len) return NULL; } - addr = (uint32_t)p; - hw = nr | (len << 8); - if (flash_program_halfword (addr, hw) != FLASH_COMPLETE) - return NULL; - 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; - addr += 2; - } - - if ((len & 1)) - { - hw = data[i*2] | 0xff00; - if (flash_program_halfword (addr, hw) != FLASH_COMPLETE) - return NULL; - addr += 2; - } - + 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) { diff --git a/src/gnuk.h b/src/gnuk.h index f643c5c..8aac0f1 100644 --- a/src/gnuk.h +++ b/src/gnuk.h @@ -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); diff --git a/src/gnuk.ld.in b/src/gnuk.ld.in index c74ce9b..44b9128 100644 --- a/src/gnuk.ld.in +++ b/src/gnuk.ld.in @@ -126,7 +126,7 @@ SECTIONS KEEP(*(.gnuk_data)) FILL(0xffffffff); . = ALIGN(1024); - . += 1024*2; + . += 1024; _keystore_pool = .; FILL(0xffffffff); . += 1024*3; diff --git a/src/main.c b/src/main.c index 0e6cb03..57bb856 100644 --- a/src/main.c +++ b/src/main.c @@ -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(); diff --git a/src/openpgp-do.c b/src/openpgp-do.c index 30fcf82..9635a00 100644 --- a/src/openpgp-do.c +++ b/src/openpgp-do.c @@ -34,30 +34,47 @@ 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 *