diff options
Diffstat (limited to 'projects')
74 files changed, 6710 insertions, 992 deletions
diff --git a/projects/board-test/Makefile b/projects/board-test/Makefile index 9b1812f..45e75fc 100644 --- a/projects/board-test/Makefile +++ b/projects/board-test/Makefile @@ -1,4 +1,7 @@ -TEST = led-test short-test uart-test fmc-test fmc-perf fmc-probe rtc-test +TEST = led-test short-test uart-test fmc-test fmc-perf fmc-probe +ifeq (${BOARD},TARGET_CRYPTECH_ALPHA) +TEST += rtc-test spiflash-perf keystore-perf +endif all: $(TEST:=.elf) diff --git a/projects/board-test/fmc-perf.c b/projects/board-test/fmc-perf.c index 0c753a7..5af0946 100644 --- a/projects/board-test/fmc-perf.c +++ b/projects/board-test/fmc-perf.c @@ -1,7 +1,6 @@ /* * Test read/write performance of the fmc bus */ -#include "stm32f4xx_hal.h" #include "stm-init.h" #include "stm-led.h" #include "stm-fmc.h" @@ -32,14 +31,8 @@ static void sanity(void) uint32_t rnd, data; rnd = random(); - if (fmc_write_32(0, &rnd) != 0) { - uart_send_string("fmc_write_32 failed\r\n"); - Error_Handler(); - } - if (fmc_read_32(0, &data) != 0) { - uart_send_string("fmc_read_32 failed\r\n"); - Error_Handler(); - } + fmc_write_32(0, rnd); + fmc_read_32(0, &data); if (data != rnd) { uart_send_string("Data bus fail: expected "); uart_send_hex(rnd, 8); @@ -57,11 +50,11 @@ static void _time_check(char *label, const uint32_t t0) uint32_t t = HAL_GetTick() - t0; uart_send_string(label); - uart_send_integer(t / 1000, 0); + uart_send_integer(t / 1000, 1); uart_send_char('.'); uart_send_integer(t % 1000, 3); uart_send_string(" seconds, "); - uart_send_integer(((1000 * TEST_NUM_ROUNDS) / t), 0); + uart_send_integer(((1000 * TEST_NUM_ROUNDS) / t), 1); uart_send_string("/sec\r\n"); } @@ -77,10 +70,7 @@ static void test_read(void) uint32_t i, data; for (i = 0; i < TEST_NUM_ROUNDS; ++i) { - if (fmc_read_32(0, &data) != 0) { - uart_send_string("fmc_read_32 failed\r\n"); - Error_Handler(); - } + fmc_read_32(0, &data); } } @@ -89,33 +79,16 @@ static void test_write(void) uint32_t i; for (i = 0; i < TEST_NUM_ROUNDS; ++i) { - if (fmc_write_32(0, &i) != 0) { - uart_send_string("fmc_write_32 failed\r\n"); - Error_Handler(); - } + fmc_write_32(0, i); } } int main(void) { stm_init(); - - uart_send_string("Keep calm for Novena boot...\r\n"); - - // Blink blue LED for six seconds to not upset the Novena at boot. - led_on(LED_BLUE); - for (int i = 0; i < 12; i++) { - HAL_Delay(500); - led_toggle(LED_BLUE); - } - led_off(LED_BLUE); - // initialize rng MX_RNG_Init(); - // prepare fmc interface - fmc_init(); - sanity(); time_check("read ", test_read()); diff --git a/projects/board-test/fmc-probe.c b/projects/board-test/fmc-probe.c index 55d3521..38897ab 100644 --- a/projects/board-test/fmc-probe.c +++ b/projects/board-test/fmc-probe.c @@ -2,7 +2,6 @@ * in other cases, it will be the core name and version strings. */ -#include "stm32f4xx_hal.h" #include "stm-init.h" #include "stm-led.h" #include "stm-fmc.h" @@ -22,37 +21,15 @@ static uint32_t read0(uint32_t addr) { uint32_t data; - if (fmc_read_32(addr, &data) != 0) { - uart_send_string("fmc_read_32 failed\r\n"); - Error_Handler(); - } + fmc_read_32(addr, &data); return data; } int main(void) { - int i; - stm_init(); - - uart_send_string("Keep calm for Novena boot...\r\n"); - - // Blink blue LED for six seconds to not upset the Novena at boot. - led_on(LED_BLUE); - for (i = 0; i < 12; i++) { - HAL_Delay(500); - led_toggle(LED_BLUE); - } - - // prepare fmc interface - fmc_init(); - - // turn on green led, turn off other leds led_on(LED_GREEN); - led_off(LED_YELLOW); - led_off(LED_RED); - led_off(LED_BLUE); for (uint32_t addr = 0; addr < 0x00080000; addr += 4) { uint32_t data = read0(addr); diff --git a/projects/board-test/fmc-test.c b/projects/board-test/fmc-test.c index bc5a768..bd30dd5 100644 --- a/projects/board-test/fmc-test.c +++ b/projects/board-test/fmc-test.c @@ -4,8 +4,7 @@ /* This requires a special bitstream with a special test register. - See core/platform/novena/fmc/rtl/novena_fmc_top.v, sections marked - `ifdef test: + See core/platform/alpha/rtl/alpha_fmc_test.v: //---------------------------------------------------------------- // Dummy Register // @@ -34,11 +33,11 @@ //------------------------------------------------------------------------------ // Headers //------------------------------------------------------------------------------ -#include "stm32f4xx_hal.h" #include "stm-init.h" #include "stm-led.h" #include "stm-fmc.h" #include "stm-uart.h" +#include "stm-fpgacfg.h" //------------------------------------------------------------------------------ // Defines @@ -76,6 +75,7 @@ int test_fpga_address_bus(void); // Defines //------------------------------------------------------------------------------ #define TEST_NUM_ROUNDS 100000 +#define VERBOSE 0 //------------------------------------------------------------------------------ @@ -88,7 +88,7 @@ int main(void) uart_send_string("Keep calm for FPGA bitstream loading...\r\n"); - // Blink blue LED until the FPGA reports it has loaded it's bitstream + // Blink blue LED until the FPGA reports it has loaded its bitstream led_on(LED_BLUE); while (! fpgacfg_check_done()) { for (i = 0; i < 4; i++) { @@ -100,9 +100,6 @@ int main(void) // initialize rng MX_RNG_Init(); - // prepare fmc interface - fmc_init(); - // turn on green led, turn off other leds led_on(LED_GREEN); led_off(LED_YELLOW); @@ -120,11 +117,15 @@ int main(void) // test address bus addr_test_ok = test_fpga_address_bus(); - uart_send_string("Data: "); - uart_send_integer(data_test_ok, 6); - uart_send_string(", addr: "); - uart_send_integer(addr_test_ok, 6); - uart_send_string("\r\n"); + if (VERBOSE || + (data_test_ok != TEST_NUM_ROUNDS || + addr_test_ok != TEST_NUM_ROUNDS)) { + uart_send_string("Data: "); + uart_send_integer(data_test_ok, 6); + uart_send_string(", addr: "); + uart_send_integer(addr_test_ok, 6); + uart_send_string("\r\n"); + } if (data_test_ok == TEST_NUM_ROUNDS && addr_test_ok == TEST_NUM_ROUNDS) { @@ -140,11 +141,12 @@ int main(void) } uart_send_string("Success "); - uart_send_integer(successful_runs, 0); + uart_send_integer(successful_runs, 1); uart_send_string(", fail "); - uart_send_integer(failed_runs, 0); - uart_send_string("\r\n\r\n"); - + uart_send_integer(failed_runs, 1); + uart_send_string("\r\n"); + if (VERBOSE) + uart_send_string("\r\n"); HAL_Delay(sleep); } @@ -156,7 +158,7 @@ int main(void) int test_fpga_data_bus(void) //------------------------------------------------------------------------------ { - int c, ok; + int c; uint32_t rnd, buf; HAL_StatusTypeDef hal_result; @@ -169,12 +171,10 @@ int test_fpga_data_bus(void) if (hal_result != HAL_OK) break; // write value to fpga at address 0 - ok = fmc_write_32(0, &rnd); - if (ok != 0) break; + fmc_write_32(0, rnd); // read value from fpga - ok = fmc_read_32(0, &buf); - if (ok != 0) break; + fmc_read_32(0, &buf); // compare (abort testing in case of error) if (buf != rnd) @@ -197,13 +197,16 @@ int test_fpga_data_bus(void) data_diff = buf; data_diff ^= rnd; - uart_send_string("Sample of data bus test data: expected "); - uart_send_binary(rnd, 32); - uart_send_string(", got "); - uart_send_binary(buf, 32); - uart_send_string(", diff "); - uart_send_binary(data_diff, 32); - uart_send_string("\r\n"); + if (VERBOSE || data_diff) { + uart_send_string("Sample of data bus test data: expected "); + uart_send_binary(rnd, 32); + uart_send_string(", got "); + uart_send_binary(buf, 32); + uart_send_string(", diff "); + uart_send_binary(data_diff, 32); + uart_send_string("\r\n"); + } + // return number of successful tests return c; } @@ -213,7 +216,7 @@ int test_fpga_data_bus(void) int test_fpga_address_bus(void) //------------------------------------------------------------------------------ { - int c, ok; + int c; uint32_t rnd, buf; HAL_StatusTypeDef hal_result; @@ -234,12 +237,10 @@ int test_fpga_address_bus(void) if (rnd == 0) continue; // write dummy value to fpga at some non-zero address - ok = fmc_write_32(rnd, &buf); - if (ok != 0) break; + fmc_write_32(rnd, buf); // read value from fpga - ok = fmc_read_32(0, &buf); - if (ok != 0) break; + fmc_read_32(0, &buf); // fpga receives address of 32-bit word, while we need // byte address here to compare @@ -266,13 +267,15 @@ int test_fpga_address_bus(void) addr_diff = buf; addr_diff ^= rnd; - uart_send_string("Sample of addr bus test data: expected "); - uart_send_binary(rnd, 32); - uart_send_string(", got "); - uart_send_binary(buf, 32); - uart_send_string(", diff "); - uart_send_binary(addr_diff, 32); - uart_send_string("\r\n"); + if (VERBOSE || addr_diff) { + uart_send_string("Sample of addr bus test data: expected "); + uart_send_binary(rnd, 32); + uart_send_string(", got "); + uart_send_binary(buf, 32); + uart_send_string(", diff "); + uart_send_binary(addr_diff, 32); + uart_send_string("\r\n"); + } return c; } diff --git a/projects/board-test/keystore-perf.c b/projects/board-test/keystore-perf.c new file mode 100644 index 0000000..c2aa4fb --- /dev/null +++ b/projects/board-test/keystore-perf.c @@ -0,0 +1,197 @@ +/* + * Test read/write/erase performance of the flash keystore. + */ + +#include "string.h" + +#include "stm-init.h" +#include "stm-led.h" +#include "stm-uart.h" +#include "stm-keystore.h" + +/* + * 1. Read the entire flash by subsectors, ignoring data. + */ +static void test_read_data(void) +{ + uint8_t read_buf[KEYSTORE_SUBSECTOR_SIZE]; + uint32_t i; + HAL_StatusTypeDef err; + + for (i = 0; i < KEYSTORE_NUM_SUBSECTORS; ++i) { + err = keystore_read_data(i * KEYSTORE_SUBSECTOR_SIZE, read_buf, KEYSTORE_SUBSECTOR_SIZE); + if (err != HAL_OK) { + uart_send_string("ERROR: keystore_read_data returned "); + uart_send_integer(err, 1); + uart_send_string("\r\n"); + break; + } + } +} + +/* + * Read the flash data and verify it against a known pattern. + */ +static void _read_verify(uint8_t *vrfy_buf) +{ + uint8_t read_buf[KEYSTORE_SUBSECTOR_SIZE]; + uint32_t i; + HAL_StatusTypeDef err; + + for (i = 0; i < KEYSTORE_NUM_SUBSECTORS; ++i) { + err = keystore_read_data(i * KEYSTORE_SUBSECTOR_SIZE, read_buf, KEYSTORE_SUBSECTOR_SIZE); + if (err != HAL_OK) { + uart_send_string("ERROR: keystore_read_data returned "); + uart_send_integer(err, 1); + uart_send_string("\r\n"); + break; + } + if (memcmp(read_buf, vrfy_buf, KEYSTORE_SUBSECTOR_SIZE) != 0) { + uart_send_string("ERROR: verify failed in subsector "); + uart_send_integer(i, 1); + uart_send_string("\r\n"); + break; + } + } +} + +/* + * 2a. Erase the entire flash by sectors. + */ +static void test_erase_sector(void) +{ + uint32_t i; + HAL_StatusTypeDef err; + + for (i = 0; i < KEYSTORE_NUM_SECTORS; ++i) { + err = keystore_erase_sector(i); + if (err != HAL_OK) { + uart_send_string("ERROR: keystore_erase_sector returned "); + uart_send_integer(err, 1); + uart_send_string("\r\n"); + break; + } + } +} + +/* + * 2b. Erase the entire flash by subsectors. + */ +static void test_erase_subsector(void) +{ + uint32_t i; + HAL_StatusTypeDef err; + + for (i = 0; i < KEYSTORE_NUM_SUBSECTORS; ++i) { + err = keystore_erase_subsector(i); + if (err != HAL_OK) { + uart_send_string("ERROR: keystore_erase_subsector returned "); + uart_send_integer(err, 1); + uart_send_string("\r\n"); + break; + } + } +} + +/* + * 2c. Read the entire flash, verify erasure. + */ +static void test_verify_erase(void) +{ + uint8_t vrfy_buf[KEYSTORE_SUBSECTOR_SIZE]; + uint32_t i; + + for (i = 0; i < sizeof(vrfy_buf); ++i) + vrfy_buf[i] = 0xFF; + + _read_verify(vrfy_buf); +} + +/* + * 3a. Write the entire flash with a pattern. + */ +static void test_write_data(void) +{ + uint8_t write_buf[KEYSTORE_SUBSECTOR_SIZE]; + uint32_t i; + HAL_StatusTypeDef err; + + for (i = 0; i < sizeof(write_buf); ++i) + write_buf[i] = i & 0xFF; + + for (i = 0; i < KEYSTORE_NUM_SUBSECTORS; ++i) { + err = keystore_write_data(i * KEYSTORE_SUBSECTOR_SIZE, write_buf, KEYSTORE_SUBSECTOR_SIZE); + if (err != HAL_OK) { + uart_send_string("ERROR: keystore_write_data returned "); + uart_send_integer(err, 1); + uart_send_string(" for subsector "); + uart_send_integer(i, 1); + uart_send_string("\r\n"); + break; + } + } +} + +/* + * 3b. Read the entire flash, verify data. + */ +static void test_verify_write(void) +{ + uint8_t vrfy_buf[KEYSTORE_SUBSECTOR_SIZE]; + uint32_t i; + + for (i = 0; i < sizeof(vrfy_buf); ++i) + vrfy_buf[i] = i & 0xFF; + + _read_verify(vrfy_buf); +} + +static void _time_check(char *label, const uint32_t t0, uint32_t n_rounds) +{ + uint32_t t = HAL_GetTick() - t0; + + uart_send_string(label); + uart_send_integer(t / 1000, 1); + uart_send_char('.'); + uart_send_integer(t % 1000, 3); + uart_send_string(" sec"); + if (n_rounds > 1) { + uart_send_string(" for "); + uart_send_integer(n_rounds, 1); + uart_send_string(" rounds, "); + uart_send_integer(t / n_rounds, 1); + uart_send_char('.'); + uart_send_integer(((t % n_rounds) * 100) / n_rounds, 2); + uart_send_string(" ms each"); + } + uart_send_string("\r\n"); +} + +#define time_check(_label_, _expr_, _n_) \ + do { \ + uint32_t _t = HAL_GetTick(); \ + (_expr_); \ + _time_check(_label_, _t, _n_); \ + } while (0) + +int main(void) +{ + stm_init(); + + if (keystore_check_id() != HAL_OK) { + uart_send_string("ERROR: keystore_check_id failed\r\n"); + return 0; + } + + uart_send_string("Starting...\r\n"); + + time_check("read data ", test_read_data(), KEYSTORE_NUM_SUBSECTORS); + time_check("erase subsector ", test_erase_subsector(), KEYSTORE_NUM_SUBSECTORS); + time_check("erase sector ", test_erase_sector(), KEYSTORE_NUM_SECTORS); + time_check("verify erase ", test_verify_erase(), KEYSTORE_NUM_SUBSECTORS); + time_check("write data ", test_write_data(), KEYSTORE_NUM_SUBSECTORS); + time_check("verify write ", test_verify_write(), KEYSTORE_NUM_SUBSECTORS); + + uart_send_string("Done.\r\n\r\n"); + return 0; +} diff --git a/projects/board-test/led-test.c b/projects/board-test/led-test.c index 7e72788..2ec7c9d 100644 --- a/projects/board-test/led-test.c +++ b/projects/board-test/led-test.c @@ -1,7 +1,6 @@ /* * Blink the four LEDs on the rev01 board in a pattern. */ -#include "stm32f4xx_hal.h" #include "stm-init.h" #include "stm-led.h" diff --git a/projects/board-test/rtc-test.c b/projects/board-test/rtc-test.c index b8a7511..bbb297a 100644 --- a/projects/board-test/rtc-test.c +++ b/projects/board-test/rtc-test.c @@ -8,7 +8,6 @@ */ #include <string.h> -#include "stm32f4xx_hal.h" #include "stm-init.h" #include "stm-led.h" #include "stm-uart.h" @@ -23,18 +22,18 @@ uint32_t i; uint32_t device_ready(uint16_t i2c_addr) { - uart_send_string2(STM_UART_MGMT, "Checking readiness of 0x"); - uart_send_number2(STM_UART_MGMT, i2c_addr, 4, 16); - uart_send_string2(STM_UART_MGMT, "..."); + uart_send_string("Checking readiness of 0x"); + uart_send_hex(i2c_addr, 4); + uart_send_string("..."); if (rtc_device_ready(i2c_addr) == HAL_OK) { - uart_send_string2(STM_UART_MGMT, "OK\r\n"); + uart_send_string("OK\r\n"); return 1; } - uart_send_string2(STM_UART_MGMT, "Not ready (0x"); - uart_send_number2(STM_UART_MGMT, i, 4, 16); - uart_send_string2(STM_UART_MGMT, ")\r\n"); + uart_send_string("Not ready (0x"); + uart_send_hex(i, 4); + uart_send_string(")\r\n"); return 0; } @@ -44,34 +43,34 @@ void send_byte(const uint16_t i2c_addr, const uint8_t value) { uint8_t ch = value; - uart_send_string2(STM_UART_MGMT, "Sending "); - uart_send_number2(STM_UART_MGMT, ch, 2, 16); - uart_send_string2(STM_UART_MGMT, " to 0x"); - uart_send_number2(STM_UART_MGMT, i2c_addr, 4, 16); - uart_send_string2(STM_UART_MGMT, "..."); + uart_send_string("Sending "); + uart_send_hex(ch, 2); + uart_send_string(" to 0x"); + uart_send_hex(i2c_addr, 4); + uart_send_string("..."); if (rtc_send_byte(i2c_addr, ch, 1000) != HAL_OK) { - uart_send_string2(STM_UART_MGMT, "Timeout\r\n"); + uart_send_string("Timeout\r\n"); Error_Handler(); } - uart_send_string2(STM_UART_MGMT, "OK\r\n"); + uart_send_string("OK\r\n"); } void read_bytes (uint8_t *buf, const uint16_t i2c_addr, const uint8_t len) { - uart_send_string2(STM_UART_MGMT, "Reading "); - uart_send_number2(STM_UART_MGMT, len, 3, 10); - uart_send_string2(STM_UART_MGMT, " bytes from 0x"); - uart_send_number2(STM_UART_MGMT, i2c_addr, 4, 16); - uart_send_string2(STM_UART_MGMT, "..."); + uart_send_string("Reading "); + uart_send_integer(len, 1); + uart_send_string(" bytes from 0x"); + uart_send_hex(i2c_addr, 4); + uart_send_string("..."); if (rtc_read_bytes(i2c_addr, buf, len, 1000) != HAL_OK) { - uart_send_string2(STM_UART_MGMT, "Timeout\r\n"); + uart_send_string("Timeout\r\n"); Error_Handler(); } - uart_send_string2(STM_UART_MGMT, "OK\r\n"); + uart_send_string("OK\r\n"); } void request_data(uint8_t *buf, const uint16_t i2c_addr, const uint8_t offset, const uint8_t bytes) @@ -85,8 +84,8 @@ void print_time() request_data(buf, RTC_RTC_ADDR, RTC_TIME_OFFSET, RTC_TIME_BYTES); for (i = 0; i < RTC_TIME_BYTES; i++) { - uart_send_number2(STM_UART_MGMT, buf[i], 2, 16); - uart_send_string2(STM_UART_MGMT, " "); + uart_send_hex(buf[i], 2); + uart_send_string(" "); } } @@ -94,37 +93,37 @@ void dump_sram() { request_data(buf, RTC_RTC_ADDR, 0x0, RTC_SRAM_TOTAL_BYTES); - uart_send_string2(STM_UART_MGMT, "SRAM contents:\r\n"); - uart_send_hexdump(STM_UART_MGMT, buf, 0, RTC_SRAM_TOTAL_BYTES); + uart_send_string("SRAM contents:\r\n"); + uart_send_hexdump(buf, 0, RTC_SRAM_TOTAL_BYTES); - uart_send_string2(STM_UART_MGMT, "\r\n"); + uart_send_string("\r\n"); } void dump_eeprom() { request_data(buf, RTC_EEPROM_ADDR, 0x0, RTC_EEPROM_TOTAL_BYTES); - uart_send_string2(STM_UART_MGMT, "EEPROM contents:\r\n"); - uart_send_hexdump(STM_UART_MGMT, buf, 0, RTC_EEPROM_TOTAL_BYTES); - uart_send_string2(STM_UART_MGMT, "\r\n"); + uart_send_string("EEPROM contents:\r\n"); + uart_send_hexdump(buf, 0, RTC_EEPROM_TOTAL_BYTES); + uart_send_string("\r\n"); request_data(buf, RTC_EEPROM_ADDR, RTC_EEPROM_EUI48_OFFSET, RTC_EEPROM_EUI48_BYTES); - uart_send_string2(STM_UART_MGMT, "EEPROM EUI-48:\r\n"); - uart_send_hexdump(STM_UART_MGMT, buf, RTC_EEPROM_EUI48_OFFSET, RTC_EEPROM_EUI48_BYTES); + uart_send_string("EEPROM EUI-48:\r\n"); + uart_send_hexdump(buf, RTC_EEPROM_EUI48_OFFSET, RTC_EEPROM_EUI48_BYTES); - uart_send_string2(STM_UART_MGMT, "\r\n"); + uart_send_string("\r\n"); } void enable_oscillator() { - uart_send_string2(STM_UART_MGMT, "Enabling oscillator...\r\n"); + uart_send_string("Enabling oscillator...\r\n"); if (rtc_enable_oscillator() != HAL_OK) { - uart_send_string2(STM_UART_MGMT, "Timeout\r\n"); + uart_send_string("Timeout\r\n"); Error_Handler(); } - uart_send_string2(STM_UART_MGMT, "OK\r\n"); + uart_send_string("OK\r\n"); } @@ -132,7 +131,7 @@ int main() { stm_init(); - uart_send_string2(STM_UART_MGMT, "\r\n\r\n*** Init done\r\n"); + uart_send_string("\r\n\r\n*** Init done\r\n"); dump_sram(); dump_eeprom(); @@ -148,7 +147,7 @@ main() print_time(buf); - uart_send_string2(STM_UART_MGMT, "\r\n\r\n"); + uart_send_string("\r\n\r\n"); HAL_GPIO_TogglePin(LED_PORT, LED_GREEN); DELAY(); diff --git a/projects/board-test/short-test.c b/projects/board-test/short-test.c index 27b8e7a..db0251b 100644 --- a/projects/board-test/short-test.c +++ b/projects/board-test/short-test.c @@ -5,7 +5,6 @@ * Toggles the BLUE LED slowly and the RED LED for every * character sent. */ -#include "stm32f4xx_hal.h" #include "stm-init.h" #include "stm-led.h" #include "stm-uart.h" diff --git a/projects/board-test/spiflash-perf.c b/projects/board-test/spiflash-perf.c new file mode 100644 index 0000000..36c6131 --- /dev/null +++ b/projects/board-test/spiflash-perf.c @@ -0,0 +1,249 @@ +/* + * Test read/write/erase performance of the N25Q128 SPI flash chip. + */ + +#include "string.h" + +#include "stm-init.h" +#include "stm-led.h" +#include "stm-uart.h" +#include "stm-keystore.h" +#include "spiflash_n25q128.h" + +/* + * Use the keystore memory for testing, because it's less involved than + * using the FPGA configuration memory, and less work to restore it to a + * useful configuration. + * + * However, rather than using the stm-keystore abstractions, this version + * goes straight to the low-level API. + */ + +extern struct spiflash_ctx keystore_ctx; +static struct spiflash_ctx *ctx = &keystore_ctx; + +/* + * 1a. Read the entire flash by pages, ignoring data. + */ +static void test_read_page(void) +{ + uint8_t read_buf[N25Q128_PAGE_SIZE]; + uint32_t i; + int err; + + for (i = 0; i < N25Q128_NUM_PAGES; ++i) { + err = n25q128_read_page(ctx, i, read_buf); + if (err != HAL_OK) { + uart_send_string("ERROR: n25q128_read_page returned "); + uart_send_integer(err, 1); + uart_send_string("\r\n"); + break; + } + } +} + +/* + * 1b. Read the entire flash by subsectors, ignoring data. + */ +static void test_read_subsector(void) +{ + uint8_t read_buf[N25Q128_SUBSECTOR_SIZE]; + uint32_t i; + int err; + + for (i = 0; i < N25Q128_NUM_SUBSECTORS; ++i) { + err = n25q128_read_subsector(ctx, i, read_buf); + if (err != HAL_OK) { + uart_send_string("ERROR: n25q128_read_subsector returned "); + uart_send_integer(err, 1); + uart_send_string("\r\n"); + break; + } + } +} + +/* + * Read the flash data and verify it against a known pattern. + * It turns out that verification doesn't slow us down in any measurable + * way, because memcmp on 256 bytes is pretty inconsequential. + */ +static void _read_verify(uint8_t *vrfy_buf) +{ + uint8_t read_buf[N25Q128_PAGE_SIZE]; + uint32_t i; + int err; + + for (i = 0; i < N25Q128_NUM_PAGES; ++i) { + err = n25q128_read_page(ctx, i, read_buf); + if (err != HAL_OK) { + uart_send_string("ERROR: n25q128_read_page returned "); + uart_send_integer(err, 1); + uart_send_string("\r\n"); + break; + } + if (memcmp(read_buf, vrfy_buf, N25Q128_PAGE_SIZE) != 0) { + uart_send_string("ERROR: verify failed in page "); + uart_send_integer(i, 1); + uart_send_string("\r\n"); + break; + } + } +} + +/* + * 2a. Erase the entire flash by sectors. + */ +static void test_erase_sector(void) +{ + uint32_t i; + int err; + + for (i = 0; i < N25Q128_NUM_SECTORS; ++i) { + err = n25q128_erase_sector(ctx, i); + if (err != HAL_OK) { + uart_send_string("ERROR: n25q128_erase_sector returned "); + uart_send_integer(err, 1); + uart_send_string("\r\n"); + break; + } + } +} + +/* + * 2b. Erase the entire flash by subsectors. + */ +static void test_erase_subsector(void) +{ + uint32_t i; + int err; + + for (i = 0; i < N25Q128_NUM_SUBSECTORS; ++i) { + err = n25q128_erase_subsector(ctx, i); + if (err != HAL_OK) { + uart_send_string("ERROR: n25q128_erase_subsector returned "); + uart_send_integer(err, 1); + uart_send_string("\r\n"); + break; + } + } +} + +/* + * 2c. Erase the entire flash in bulk. + */ +static void test_erase_bulk(void) +{ + int err; + + err = n25q128_erase_bulk(ctx); + if (err != HAL_OK) { + uart_send_string("ERROR: n25q128_erase_bulk returned "); + uart_send_integer(err, 1); + uart_send_string("\r\n"); + } +} + +/* + * 2d. Read the entire flash, verify erasure. + */ +static void test_verify_erase(void) +{ + uint8_t vrfy_buf[N25Q128_PAGE_SIZE]; + uint32_t i; + + for (i = 0; i < sizeof(vrfy_buf); ++i) + vrfy_buf[i] = 0xFF; + + _read_verify(vrfy_buf); +} + +/* + * 3a. Write the entire flash with a pattern. + */ +static void test_write_page(void) +{ + uint8_t write_buf[N25Q128_PAGE_SIZE]; + uint32_t i; + int err; + + for (i = 0; i < sizeof(write_buf); ++i) + write_buf[i] = i & 0xFF; + + for (i = 0; i < N25Q128_NUM_PAGES; ++i) { + err = n25q128_write_page(ctx, i, write_buf); + if (err != HAL_OK) { + uart_send_string("ERROR: n25q128_write_page returned "); + uart_send_integer(err, 1); + uart_send_string(" for page "); + uart_send_integer(i, 1); + uart_send_string("\r\n"); + break; + } + } +} + +/* + * 3b. Read the entire flash, verify data. + */ +static void test_verify_write(void) +{ + uint8_t vrfy_buf[N25Q128_PAGE_SIZE]; + uint32_t i; + + for (i = 0; i < sizeof(vrfy_buf); ++i) + vrfy_buf[i] = i & 0xFF; + + _read_verify(vrfy_buf); +} + +static void _time_check(char *label, const uint32_t t0, uint32_t n_rounds) +{ + uint32_t t = HAL_GetTick() - t0; + + uart_send_string(label); + uart_send_integer(t / 1000, 1); + uart_send_char('.'); + uart_send_integer(t % 1000, 3); + uart_send_string(" sec"); + if (n_rounds > 1) { + uart_send_string(" for "); + uart_send_integer(n_rounds, 1); + uart_send_string(" rounds, "); + uart_send_integer(t / n_rounds, 1); + uart_send_char('.'); + uart_send_integer(((t % n_rounds) * 100) / n_rounds, 2); + uart_send_string(" ms each"); + } + uart_send_string("\r\n"); +} + +#define time_check(_label_, _expr_, _n_) \ + do { \ + uint32_t _t = HAL_GetTick(); \ + (_expr_); \ + _time_check(_label_, _t, _n_); \ + } while (0) + +int main(void) +{ + stm_init(); + + if (n25q128_check_id(ctx) != HAL_OK) { + uart_send_string("ERROR: n25q128_check_id failed\r\n"); + return 0; + } + + uart_send_string("Starting...\r\n"); + + time_check("read page ", test_read_page(), N25Q128_NUM_PAGES); + time_check("read subsector ", test_read_subsector(), N25Q128_NUM_SUBSECTORS); + time_check("erase subsector ", test_erase_subsector(), N25Q128_NUM_SUBSECTORS); + time_check("erase sector ", test_erase_sector(), N25Q128_NUM_SECTORS); + time_check("erase bulk ", test_erase_bulk(), 1); + time_check("verify erase ", test_verify_erase(), N25Q128_NUM_PAGES); + time_check("write page ", test_write_page(), N25Q128_NUM_PAGES); + time_check("verify write ", test_verify_write(), N25Q128_NUM_PAGES); + + uart_send_string("Done.\r\n\r\n"); + return 0; +} diff --git a/projects/board-test/uart-test.c b/projects/board-test/uart-test.c index be06863..9a56dee 100644 --- a/projects/board-test/uart-test.c +++ b/projects/board-test/uart-test.c @@ -6,7 +6,6 @@ * Toggles the BLUE LED slowly and the GREEN LED for every * character sent. */ -#include "stm32f4xx_hal.h" #include "stm-init.h" #include "stm-led.h" #include "stm-uart.h" diff --git a/projects/bootloader/Makefile b/projects/bootloader/Makefile index 4eef758..6c1012f 100644 --- a/projects/bootloader/Makefile +++ b/projects/bootloader/Makefile @@ -1,6 +1,22 @@ PROG = bootloader -OBJS = crc32.o dfu.o +OBJS = dfu.o log.o + +BOARD_OBJS = \ + ./stm-init.o \ + $(TOPLEVEL)/stm-fmc.o \ + $(TOPLEVEL)/stm-uart.o \ + $(TOPLEVEL)/spiflash_n25q128.o \ + $(TOPLEVEL)/stm-keystore.o \ + $(TOPLEVEL)/stm-flash.o \ + $(TOPLEVEL)/syscalls.o \ + $(BOARD_DIR)/system_stm32f4xx.o \ + $(BOARD_DIR)/stm32f4xx_hal_msp.o \ + ./startup_stm32f429xx.o \ + $(BOARD_DIR)/stm32f4xx_it.o + +CFLAGS += -I$(LIBHAL_SRC) +LIBS += $(LIBHAL_BLD)/libhal.a $(LIBTFM_BLD)/libtfm.a all: $(PROG:=.elf) diff --git a/projects/bootloader/bootloader.c b/projects/bootloader/bootloader.c index ab3c1d9..6413597 100644 --- a/projects/bootloader/bootloader.c +++ b/projects/bootloader/bootloader.c @@ -5,6 +5,8 @@ * or jump to previously installed firmware. * * Copyright (c) 2016, NORDUnet A/S All rights reserved. + * Copyright: 2020, The Commons Conservancy Cryptech Project + * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -16,9 +18,9 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * - Neither the name of the NORDUnet nor the names of its contributors may - * be used to endorse or promote products derived from this software - * without specific prior written permission. + * - Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED @@ -32,11 +34,30 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +/* Ignore "deprecated" warnings in ARM-supplied CMSIS code: + * + * libraries/mbed/targets/cmsis/core_cm4.h:85:28: warning: listing the stack pointer register 'sp' in a clobber list is deprecated + * libraries/mbed/targets/cmsis/core_cm4.h:85:28: note: the value of the stack pointer after an 'asm' statement must be the same as it was before the statement + * + * This comes from our use of __set_MSP to set the stack pointer when + * switching tasks. If GCC ever decides to actually forbid this, then + * we'll have to figure out something else, possibly a native assembly + * function. + */ +#pragma GCC diagnostic ignored "-Wdeprecated" + #include "stm-init.h" #include "stm-led.h" #include "stm-uart.h" +#include "stm-fmc.h" #include "dfu.h" +/* stub these out to avoid linker error */ +void fpgacfg_init(void) { } +void sdram_init(void) { } +void *hal_allocate_static_memory(const size_t size) { return 0; } + /* Linker symbols are strange in C. Make regular pointers for sanity. */ __IO uint32_t *dfu_control = &CRYPTECH_DFU_CONTROL; __IO uint32_t *dfu_firmware = &CRYPTECH_FIRMWARE_START; @@ -49,16 +70,20 @@ __IO uint32_t *dfu_code_ptr = &CRYPTECH_FIRMWARE_START + 1; typedef void (*pFunction)(void); -/* This is it's own function to make it more convenient to set a breakpoint at it in gdb */ -void do_early_dfu_jump(void) +/* called from Reset_Handler */ +void check_early_dfu_jump(void) { - pFunction loaded_app = (pFunction) *dfu_code_ptr; - /* Set the stack pointer to the correct one for the firmware */ - __set_MSP(*dfu_msp_ptr); - /* Set the Vector Table Offset Register */ - SCB->VTOR = (uint32_t) dfu_firmware; - loaded_app(); - while (1); + /* Check if we've just rebooted in order to jump to the firmware. */ + if (*dfu_control == HARDWARE_EARLY_DFU_JUMP) { + *dfu_control = 0; + pFunction loaded_app = (pFunction) *dfu_code_ptr; + /* Set the stack pointer to the correct one for the firmware */ + __set_MSP(*dfu_msp_ptr); + /* Set the Vector Table Offset Register */ + SCB->VTOR = (uint32_t) dfu_firmware; + loaded_app(); + while (1); + } } int should_dfu() @@ -66,35 +91,31 @@ int should_dfu() int i; uint8_t rx = 0; - /* While blinking the blue LED for one second, see if we receive a CR on the MGMT UART. + /* While blinking the blue LED for 5 seconds, see if we receive a CR on the MGMT UART. * We've discussed also requiring one or both of the FPGA config jumpers installed * before allowing DFU of the STM32 - that check could be done here. */ led_on(LED_BLUE); - for (i = 0; i < 10; i++) { + for (i = 0; i < 50; i++) { HAL_Delay(100); led_toggle(LED_BLUE); - if (uart_recv_char2(STM_UART_MGMT, &rx, 0) == HAL_OK) { + if (uart_recv_char(&rx, 0) == HAL_OK) { if (rx == 13) return 1; } } return 0; } -int -main() +/* Sleep for specified number of seconds -- used after bad PIN. */ +void hal_sleep(const unsigned seconds) { HAL_Delay(seconds * 1000); } + +int main(void) { int status; - /* Check if we've just rebooted in order to jump to the firmware. */ - if (*dfu_control == HARDWARE_EARLY_DFU_JUMP) { - *dfu_control = 0; - do_early_dfu_jump(); - } - stm_init(); - uart_send_string2(STM_UART_MGMT, (char *) "\r\n\r\nThis is the bootloader speaking..."); + uart_send_string("\r\n\r\nThis is the bootloader speaking..."); if (should_dfu()) { led_off(LED_BLUE); @@ -104,9 +125,9 @@ main() */ led_off(LED_BLUE); led_on(LED_RED); - uart_send_string2(STM_UART_MGMT, (char *) "dfu_receive_firmware failed: "); - uart_send_number2(STM_UART_MGMT, status, 3, 16); - uart_send_string2(STM_UART_MGMT, (char *) "\r\n\r\nRebooting in three seconds\r\n"); + uart_send_string("dfu_receive_firmware failed: "); + uart_send_hex(status, 2); + uart_send_string("\r\n\r\nRebooting in three seconds\r\n"); HAL_Delay(3000); HAL_NVIC_SystemReset(); while (1) {}; @@ -118,7 +139,7 @@ main() */ *dfu_control = HARDWARE_EARLY_DFU_JUMP; - uart_send_string2(STM_UART_MGMT, (char *) "loading firmware\r\n\r\n"); + uart_send_string("loading firmware\r\n\r\n"); /* De-initialize hardware by rebooting */ HAL_NVIC_SystemReset(); diff --git a/projects/bootloader/crc32.c b/projects/bootloader/crc32.c deleted file mode 100644 index 4d1a0bc..0000000 --- a/projects/bootloader/crc32.c +++ /dev/null @@ -1,62 +0,0 @@ -/* Reference code from RFC1952. Not meant to be used outside test code. */ - -#include "stm32f4xx_hal.h" - - -/* Table of CRCs of all 8-bit messages. */ -unsigned long crc_table[256]; - -/* Flag: has the table been computed? Initially false. */ -int crc_table_computed = 0; - -/* Make the table for a fast CRC. */ -void make_crc_table(void) -{ - unsigned long c; - - int n, k; - for (n = 0; n < 256; n++) { - c = (unsigned long) n; - for (k = 0; k < 8; k++) { - if (c & 1) { - c = 0xedb88320L ^ (c >> 1); - } else { - c = c >> 1; - } - } - crc_table[n] = c; - } - crc_table_computed = 1; -} - -/* - Update a running crc with the bytes buf[0..len-1] and return - the updated crc. The crc should be initialized to zero. Pre- and - post-conditioning (one's complement) is performed within this - function so it shouldn't be done by the caller. Usage example: - - unsigned long crc = 0L; - - while (read_buffer(buffer, length) != EOF) { - crc = update_crc(crc, buffer, length); - } - if (crc != original_crc) error(); -*/ -uint32_t update_crc(uint32_t crc, uint8_t *buf, int len) -{ - unsigned long c = crc ^ 0xffffffffL; - int n; - - if (!crc_table_computed) - make_crc_table(); - for (n = 0; n < len; n++) { - c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8); - } - return c ^ 0xffffffffL; -} - -/* Return the CRC of the bytes buf[0..len-1]. */ -unsigned long crc(unsigned char *buf, int len) -{ - return update_crc(0L, buf, len); -} diff --git a/projects/bootloader/dfu.c b/projects/bootloader/dfu.c index 231e388..83aef20 100644 --- a/projects/bootloader/dfu.c +++ b/projects/bootloader/dfu.c @@ -31,43 +31,132 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +/* Rename both CMSIS HAL_OK and libhal HAL_OK to disambiguate */ +#define HAL_OK CMSIS_HAL_OK #include "dfu.h" #include "stm-led.h" #include "stm-uart.h" #include "stm-flash.h" +#undef HAL_OK + +#define HAL_OK LIBHAL_OK +#include "hal.h" +#include "hal_internal.h" +#undef HAL_OK #include <string.h> -extern uint32_t update_crc(uint32_t crc, uint8_t *buf, int len); +static int getline(char *buf, int len) +{ + int i; + uint8_t c; + + for (i = 0; i < len; ++i) { + if (uart_recv_char(&c, HAL_MAX_DELAY) != CMSIS_HAL_OK) + return -1; + if (c == '\r') { + buf[i] = '\0'; + break; + } + buf[i] = c; + } + return i; +} +static void uart_flush(void) +{ + uint8_t c; + while (uart_recv_char(&c, 0) == CMSIS_HAL_OK) { ; } +} + +static int do_login(void) +{ + char username[8]; + char pin[hal_rpc_max_pin_length]; + hal_client_handle_t client = { -1 }; + hal_user_t user; + int n; + + uart_flush(); + uart_send_string("\r\nUsername: "); + if (getline(username, sizeof(username)) <= 0) + return -1; + if (strcmp(username, "wheel") == 0) + user = HAL_USER_WHEEL; + else if (strcmp(username, "so") == 0) + user = HAL_USER_SO; + else if (strcmp(username, "user") == 0) + user = HAL_USER_NORMAL; + else + user = HAL_USER_NONE; + + uart_flush(); + uart_send_string("\r\nPassword: "); + if ((n = getline(pin, sizeof(pin))) <= 0) + return -1; + + uart_flush(); + + hal_ks_init_read_only_pins_only(); + + if (hal_rpc_login(client, user, pin, n) != LIBHAL_OK) { + uart_send_string("\r\nAccess denied\r\n"); + return -1; + } + return 0; +} int dfu_receive_firmware(void) { - uint32_t filesize = 0, crc = 0, my_crc = 0, counter = 0; uint32_t offset = DFU_FIRMWARE_ADDR, n = DFU_UPLOAD_CHUNK_SIZE; - uint32_t buf[DFU_UPLOAD_CHUNK_SIZE / 4]; + hal_crc32_t crc = 0, my_crc = hal_crc32_init(); + uint32_t filesize = 0, counter = 0; + uint8_t buf[DFU_UPLOAD_CHUNK_SIZE]; + + if (do_login() != 0) + return -1; + + /* Fake the CLI */ + uart_send_string("\r\ncryptech> "); + char cmd[64]; + if (getline(cmd, sizeof(cmd)) <= 0) + return -1; + if (strcmp(cmd, "firmware upload") != 0) { + uart_send_string("\r\nInvalid command \""); + uart_send_string(cmd); + uart_send_string("\"\r\n"); + return -1; + } - uart_send_string2(STM_UART_MGMT, (char *) "\r\nOK, bootloader waiting for new firmware\r\n"); + uart_send_string("OK, write size (4 bytes), data in 4096 byte chunks, CRC-32 (4 bytes)\r\n"); /* Read file size (4 bytes) */ - uart_receive_bytes(STM_UART_MGMT, (void *) &filesize, 4, 1000); + uart_receive_bytes((void *) &filesize, sizeof(filesize), 10000); if (filesize < 512 || filesize > DFU_FIRMWARE_END_ADDR - DFU_FIRMWARE_ADDR) { + uart_send_string("Invalid filesize "); + uart_send_integer(filesize, 1); + uart_send_string("\r\n"); return -1; } HAL_FLASH_Unlock(); + uart_send_string("Send "); + uart_send_integer(filesize, 1); + uart_send_string(" bytes of data\r\n"); + while (filesize) { /* By initializing buf to the same value that erased flash has (0xff), we don't * have to try and be smart when writing the last page of data to the memory. */ - memset(buf, 0xffffffff, sizeof(buf)); + memset(buf, 0xff, sizeof(buf)); if (filesize < n) { n = filesize; } - if (uart_receive_bytes(STM_UART_MGMT, (void *) &buf, n, 1000) != HAL_OK) { + if (uart_receive_bytes((void *) buf, n, 10000) != CMSIS_HAL_OK) { return -2; } filesize -= n; @@ -75,23 +164,34 @@ int dfu_receive_firmware(void) /* After reception of a chunk but before ACKing we have "all" the time in the world to * calculate CRC and write it to flash. */ - my_crc = update_crc(my_crc, (uint8_t *) buf, n); - stm_flash_write32(offset, buf, sizeof(buf) / 4); + my_crc = hal_crc32_update(my_crc, buf, n); + stm_flash_write32(offset, (uint32_t *)buf, sizeof(buf)/4); offset += DFU_UPLOAD_CHUNK_SIZE; /* ACK this chunk by sending the current chunk counter (4 bytes) */ counter++; - uart_send_bytes(STM_UART_MGMT, (void *) &counter, 4); + uart_send_bytes((void *) &counter, 4); led_toggle(LED_BLUE); } + my_crc = hal_crc32_finalize(my_crc); + HAL_FLASH_Lock(); - /* The sending side will now send it's calculated CRC-32 */ - uart_receive_bytes(STM_UART_MGMT, (void *) &crc, 4, 1000); + uart_send_string("Send CRC-32\r\n"); + + /* The sending side will now send its calculated CRC-32 */ + uart_receive_bytes((void *) &crc, sizeof(crc), 10000); + + uart_send_string("CRC-32 0x"); + uart_send_hex(crc, 1); + uart_send_string(", calculated CRC 0x"); + uart_send_hex(my_crc, 1); if (crc == my_crc) { - uart_send_string2(STM_UART_MGMT, (char *) "\r\nSuccess\r\n"); - return 0; + uart_send_string("CRC checksum MATCHED\r\n"); + return 0; + } else { + uart_send_string("CRC checksum did NOT match\r\n"); } led_on(LED_RED); diff --git a/projects/bootloader/log.c b/projects/bootloader/log.c new file mode 100644 index 0000000..fbc0e73 --- /dev/null +++ b/projects/bootloader/log.c @@ -0,0 +1,68 @@ +/* + * log.c + * ----- + * Implement libhal logging API on Alpha. + * + * Copyright (c) 2017, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdarg.h> + +#define HAL_OK CMSIS_HAL_OK +#include "stm-uart.h" +#undef HAL_OK + +#define HAL_OK LIBHAL_OK +#include "hal.h" +#include "hal_internal.h" +#undef HAL_OK + +static hal_log_level_t current_log_level; + +void hal_log_set_level(const hal_log_level_t level) +{ + current_log_level = level; +} + +void hal_log(const hal_log_level_t level, const char *format, ...) +{ + if (level < current_log_level) + return; + + char buffer[2048]; + va_list ap; + + va_start(ap, format); + vsnprintf(buffer, sizeof(buffer), format, ap); + va_end(ap); + + uart_send_string(buffer); + uart_send_string("\r\n"); +} diff --git a/projects/bootloader/startup_stm32f429xx.S b/projects/bootloader/startup_stm32f429xx.S new file mode 100644 index 0000000..fc8bf34 --- /dev/null +++ b/projects/bootloader/startup_stm32f429xx.S @@ -0,0 +1,564 @@ +/** + ****************************************************************************** + * @file startup_stm32f429xx.s + * @author MCD Application Team + * @version V2.4.1 + * @date 09-October-2015 + * @brief STM32F429xx Devices vector table for GCC based toolchains. + * This module performs: + * - Set the initial SP + * - Set the initial PC == Reset_Handler, + * - Set the vector table entries with the exceptions ISR address + * - Branches to main in the C library (which eventually + * calls main()). + * After Reset the Cortex-M4 processor is in Thread mode, + * priority is Privileged, and the Stack is set to Main. + ****************************************************************************** + * @attention + * + * <h2><center>© COPYRIGHT 2015 STMicroelectronics</center></h2> + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + */ + + .syntax unified + .cpu cortex-m4 + .fpu softvfp + .thumb + +.global g_pfnVectors +.global Default_Handler + +/* start address for the initialization values of the .data section. +defined in linker script */ +.word _sidata +/* start address for the .data section. defined in linker script */ +.word _sdata +/* end address for the .data section. defined in linker script */ +.word _edata +/* start address for the .bss section. defined in linker script */ +.word _sbss +/* end address for the .bss section. defined in linker script */ +.word _ebss +/* stack used for SystemInit_ExtMemCtl; always internal RAM used */ + +/** + * @brief This is the code that gets called when the processor first + * starts execution following a reset event. Only the absolutely + * necessary set is performed, after which the application + * supplied main() routine is called. + * @param None + * @retval : None +*/ + + .section .text.Reset_Handler + .weak Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + ldr sp, =_estack /* set stack pointer */ + +/* Copy the data segment initializers from flash to SRAM */ + movs r1, #0 + b LoopCopyDataInit + +CopyDataInit: + ldr r3, =_sidata + ldr r3, [r3, r1] + str r3, [r0, r1] + adds r1, r1, #4 + +LoopCopyDataInit: + ldr r0, =_sdata + ldr r3, =_edata + adds r2, r0, r1 + cmp r2, r3 + bcc CopyDataInit + ldr r2, =_sbss + b LoopFillZerobss +/* Zero fill the bss segment. */ +FillZerobss: + movs r3, #0 + str r3, [r2], #4 + +LoopFillZerobss: + ldr r3, = _ebss + cmp r2, r3 + bcc FillZerobss + + /* Check if we've just rebooted in order to jump to the firmware. */ + bl check_early_dfu_jump + +/* Call the clock system intitialization function.*/ + bl SystemInit +/* Call static constructors */ + bl __libc_init_array +/* Call the application's entry point.*/ + bl main + bx lr +.size Reset_Handler, .-Reset_Handler + +/** + * @brief This is the code that gets called when the processor receives an + * unexpected interrupt. This simply enters an infinite loop, preserving + * the system state for examination by a debugger. + * @param None + * @retval None +*/ + .section .text.Default_Handler,"ax",%progbits +Default_Handler: +Infinite_Loop: + b Infinite_Loop + .size Default_Handler, .-Default_Handler +/****************************************************************************** +* +* The minimal vector table for a Cortex M3. Note that the proper constructs +* must be placed on this to ensure that it ends up at physical address +* 0x0000.0000. +* +*******************************************************************************/ + .section .isr_vector,"a",%progbits + .type g_pfnVectors, %object + .size g_pfnVectors, .-g_pfnVectors + +g_pfnVectors: + .word _estack + .word Reset_Handler + + .word NMI_Handler + .word HardFault_Handler + .word MemManage_Handler + .word BusFault_Handler + .word UsageFault_Handler + .word 0 + .word 0 + .word 0 + .word 0 + .word SVC_Handler + .word DebugMon_Handler + .word 0 + .word PendSV_Handler + .word SysTick_Handler + + /* External Interrupts */ + .word WWDG_IRQHandler /* Window WatchDog */ + .word PVD_IRQHandler /* PVD through EXTI Line detection */ + .word TAMP_STAMP_IRQHandler /* Tamper and TimeStamps through the EXTI line */ + .word RTC_WKUP_IRQHandler /* RTC Wakeup through the EXTI line */ + .word FLASH_IRQHandler /* FLASH */ + .word RCC_IRQHandler /* RCC */ + .word EXTI0_IRQHandler /* EXTI Line0 */ + .word EXTI1_IRQHandler /* EXTI Line1 */ + .word EXTI2_IRQHandler /* EXTI Line2 */ + .word EXTI3_IRQHandler /* EXTI Line3 */ + .word EXTI4_IRQHandler /* EXTI Line4 */ + .word DMA1_Stream0_IRQHandler /* DMA1 Stream 0 */ + .word DMA1_Stream1_IRQHandler /* DMA1 Stream 1 */ + .word DMA1_Stream2_IRQHandler /* DMA1 Stream 2 */ + .word DMA1_Stream3_IRQHandler /* DMA1 Stream 3 */ + .word DMA1_Stream4_IRQHandler /* DMA1 Stream 4 */ + .word DMA1_Stream5_IRQHandler /* DMA1 Stream 5 */ + .word DMA1_Stream6_IRQHandler /* DMA1 Stream 6 */ + .word ADC_IRQHandler /* ADC1, ADC2 and ADC3s */ + .word CAN1_TX_IRQHandler /* CAN1 TX */ + .word CAN1_RX0_IRQHandler /* CAN1 RX0 */ + .word CAN1_RX1_IRQHandler /* CAN1 RX1 */ + .word CAN1_SCE_IRQHandler /* CAN1 SCE */ + .word EXTI9_5_IRQHandler /* External Line[9:5]s */ + .word TIM1_BRK_TIM9_IRQHandler /* TIM1 Break and TIM9 */ + .word TIM1_UP_TIM10_IRQHandler /* TIM1 Update and TIM10 */ + .word TIM1_TRG_COM_TIM11_IRQHandler /* TIM1 Trigger and Commutation and TIM11 */ + .word TIM1_CC_IRQHandler /* TIM1 Capture Compare */ + .word TIM2_IRQHandler /* TIM2 */ + .word TIM3_IRQHandler /* TIM3 */ + .word TIM4_IRQHandler /* TIM4 */ + .word I2C1_EV_IRQHandler /* I2C1 Event */ + .word I2C1_ER_IRQHandler /* I2C1 Error */ + .word I2C2_EV_IRQHandler /* I2C2 Event */ + .word I2C2_ER_IRQHandler /* I2C2 Error */ + .word SPI1_IRQHandler /* SPI1 */ + .word SPI2_IRQHandler /* SPI2 */ + .word USART1_IRQHandler /* USART1 */ + .word USART2_IRQHandler /* USART2 */ + .word USART3_IRQHandler /* USART3 */ + .word EXTI15_10_IRQHandler /* External Line[15:10]s */ + .word RTC_Alarm_IRQHandler /* RTC Alarm (A and B) through EXTI Line */ + .word OTG_FS_WKUP_IRQHandler /* USB OTG FS Wakeup through EXTI line */ + .word TIM8_BRK_TIM12_IRQHandler /* TIM8 Break and TIM12 */ + .word TIM8_UP_TIM13_IRQHandler /* TIM8 Update and TIM13 */ + .word TIM8_TRG_COM_TIM14_IRQHandler /* TIM8 Trigger and Commutation and TIM14 */ + .word TIM8_CC_IRQHandler /* TIM8 Capture Compare */ + .word DMA1_Stream7_IRQHandler /* DMA1 Stream7 */ + .word FMC_IRQHandler /* FMC */ + .word SDIO_IRQHandler /* SDIO */ + .word TIM5_IRQHandler /* TIM5 */ + .word SPI3_IRQHandler /* SPI3 */ + .word UART4_IRQHandler /* UART4 */ + .word UART5_IRQHandler /* UART5 */ + .word TIM6_DAC_IRQHandler /* TIM6 and DAC1&2 underrun errors */ + .word TIM7_IRQHandler /* TIM7 */ + .word DMA2_Stream0_IRQHandler /* DMA2 Stream 0 */ + .word DMA2_Stream1_IRQHandler /* DMA2 Stream 1 */ + .word DMA2_Stream2_IRQHandler /* DMA2 Stream 2 */ + .word DMA2_Stream3_IRQHandler /* DMA2 Stream 3 */ + .word DMA2_Stream4_IRQHandler /* DMA2 Stream 4 */ + .word ETH_IRQHandler /* Ethernet */ + .word ETH_WKUP_IRQHandler /* Ethernet Wakeup through EXTI line */ + .word CAN2_TX_IRQHandler /* CAN2 TX */ + .word CAN2_RX0_IRQHandler /* CAN2 RX0 */ + .word CAN2_RX1_IRQHandler /* CAN2 RX1 */ + .word CAN2_SCE_IRQHandler /* CAN2 SCE */ + .word OTG_FS_IRQHandler /* USB OTG FS */ + .word DMA2_Stream5_IRQHandler /* DMA2 Stream 5 */ + .word DMA2_Stream6_IRQHandler /* DMA2 Stream 6 */ + .word DMA2_Stream7_IRQHandler /* DMA2 Stream 7 */ + .word USART6_IRQHandler /* USART6 */ + .word I2C3_EV_IRQHandler /* I2C3 event */ + .word I2C3_ER_IRQHandler /* I2C3 error */ + .word OTG_HS_EP1_OUT_IRQHandler /* USB OTG HS End Point 1 Out */ + .word OTG_HS_EP1_IN_IRQHandler /* USB OTG HS End Point 1 In */ + .word OTG_HS_WKUP_IRQHandler /* USB OTG HS Wakeup through EXTI */ + .word OTG_HS_IRQHandler /* USB OTG HS */ + .word DCMI_IRQHandler /* DCMI */ + .word 0 /* Reserved */ + .word HASH_RNG_IRQHandler /* Hash and Rng */ + .word FPU_IRQHandler /* FPU */ + .word UART7_IRQHandler /* UART7 */ + .word UART8_IRQHandler /* UART8 */ + .word SPI4_IRQHandler /* SPI4 */ + .word SPI5_IRQHandler /* SPI5 */ + .word SPI6_IRQHandler /* SPI6 */ + .word SAI1_IRQHandler /* SAI1 */ + .word LTDC_IRQHandler /* LTDC_IRQHandler */ + .word LTDC_ER_IRQHandler /* LTDC_ER_IRQHandler */ + .word DMA2D_IRQHandler /* DMA2D */ + +/******************************************************************************* +* +* Provide weak aliases for each Exception handler to the Default_Handler. +* As they are weak aliases, any function with the same name will override +* this definition. +* +*******************************************************************************/ + .weak NMI_Handler + .thumb_set NMI_Handler,Default_Handler + + .weak HardFault_Handler + .thumb_set HardFault_Handler,Default_Handler + + .weak MemManage_Handler + .thumb_set MemManage_Handler,Default_Handler + + .weak BusFault_Handler + .thumb_set BusFault_Handler,Default_Handler + + .weak UsageFault_Handler + .thumb_set UsageFault_Handler,Default_Handler + + .weak SVC_Handler + .thumb_set SVC_Handler,Default_Handler + + .weak DebugMon_Handler + .thumb_set DebugMon_Handler,Default_Handler + + .weak PendSV_Handler + .thumb_set PendSV_Handler,Default_Handler + + .weak SysTick_Handler + .thumb_set SysTick_Handler,Default_Handler + + .weak WWDG_IRQHandler + .thumb_set WWDG_IRQHandler,Default_Handler + + .weak PVD_IRQHandler + .thumb_set PVD_IRQHandler,Default_Handler + + .weak TAMP_STAMP_IRQHandler + .thumb_set TAMP_STAMP_IRQHandler,Default_Handler + + .weak RTC_WKUP_IRQHandler + .thumb_set RTC_WKUP_IRQHandler,Default_Handler + + .weak FLASH_IRQHandler + .thumb_set FLASH_IRQHandler,Default_Handler + + .weak RCC_IRQHandler + .thumb_set RCC_IRQHandler,Default_Handler + + .weak EXTI0_IRQHandler + .thumb_set EXTI0_IRQHandler,Default_Handler + + .weak EXTI1_IRQHandler + .thumb_set EXTI1_IRQHandler,Default_Handler + + .weak EXTI2_IRQHandler + .thumb_set EXTI2_IRQHandler,Default_Handler + + .weak EXTI3_IRQHandler + .thumb_set EXTI3_IRQHandler,Default_Handler + + .weak EXTI4_IRQHandler + .thumb_set EXTI4_IRQHandler,Default_Handler + + .weak DMA1_Stream0_IRQHandler + .thumb_set DMA1_Stream0_IRQHandler,Default_Handler + + .weak DMA1_Stream1_IRQHandler + .thumb_set DMA1_Stream1_IRQHandler,Default_Handler + + .weak DMA1_Stream2_IRQHandler + .thumb_set DMA1_Stream2_IRQHandler,Default_Handler + + .weak DMA1_Stream3_IRQHandler + .thumb_set DMA1_Stream3_IRQHandler,Default_Handler + + .weak DMA1_Stream4_IRQHandler + .thumb_set DMA1_Stream4_IRQHandler,Default_Handler + + .weak DMA1_Stream5_IRQHandler + .thumb_set DMA1_Stream5_IRQHandler,Default_Handler + + .weak DMA1_Stream6_IRQHandler + .thumb_set DMA1_Stream6_IRQHandler,Default_Handler + + .weak ADC_IRQHandler + .thumb_set ADC_IRQHandler,Default_Handler + + .weak CAN1_TX_IRQHandler + .thumb_set CAN1_TX_IRQHandler,Default_Handler + + .weak CAN1_RX0_IRQHandler + .thumb_set CAN1_RX0_IRQHandler,Default_Handler + + .weak CAN1_RX1_IRQHandler + .thumb_set CAN1_RX1_IRQHandler,Default_Handler + + .weak CAN1_SCE_IRQHandler + .thumb_set CAN1_SCE_IRQHandler,Default_Handler + + .weak EXTI9_5_IRQHandler + .thumb_set EXTI9_5_IRQHandler,Default_Handler + + .weak TIM1_BRK_TIM9_IRQHandler + .thumb_set TIM1_BRK_TIM9_IRQHandler,Default_Handler + + .weak TIM1_UP_TIM10_IRQHandler + .thumb_set TIM1_UP_TIM10_IRQHandler,Default_Handler + + .weak TIM1_TRG_COM_TIM11_IRQHandler + .thumb_set TIM1_TRG_COM_TIM11_IRQHandler,Default_Handler + + .weak TIM1_CC_IRQHandler + .thumb_set TIM1_CC_IRQHandler,Default_Handler + + .weak TIM2_IRQHandler + .thumb_set TIM2_IRQHandler,Default_Handler + + .weak TIM3_IRQHandler + .thumb_set TIM3_IRQHandler,Default_Handler + + .weak TIM4_IRQHandler + .thumb_set TIM4_IRQHandler,Default_Handler + + .weak I2C1_EV_IRQHandler + .thumb_set I2C1_EV_IRQHandler,Default_Handler + + .weak I2C1_ER_IRQHandler + .thumb_set I2C1_ER_IRQHandler,Default_Handler + + .weak I2C2_EV_IRQHandler + .thumb_set I2C2_EV_IRQHandler,Default_Handler + + .weak I2C2_ER_IRQHandler + .thumb_set I2C2_ER_IRQHandler,Default_Handler + + .weak SPI1_IRQHandler + .thumb_set SPI1_IRQHandler,Default_Handler + + .weak SPI2_IRQHandler + .thumb_set SPI2_IRQHandler,Default_Handler + + .weak USART1_IRQHandler + .thumb_set USART1_IRQHandler,Default_Handler + + .weak USART2_IRQHandler + .thumb_set USART2_IRQHandler,Default_Handler + + .weak USART3_IRQHandler + .thumb_set USART3_IRQHandler,Default_Handler + + .weak EXTI15_10_IRQHandler + .thumb_set EXTI15_10_IRQHandler,Default_Handler + + .weak RTC_Alarm_IRQHandler + .thumb_set RTC_Alarm_IRQHandler,Default_Handler + + .weak OTG_FS_WKUP_IRQHandler + .thumb_set OTG_FS_WKUP_IRQHandler,Default_Handler + + .weak TIM8_BRK_TIM12_IRQHandler + .thumb_set TIM8_BRK_TIM12_IRQHandler,Default_Handler + + .weak TIM8_UP_TIM13_IRQHandler + .thumb_set TIM8_UP_TIM13_IRQHandler,Default_Handler + + .weak TIM8_TRG_COM_TIM14_IRQHandler + .thumb_set TIM8_TRG_COM_TIM14_IRQHandler,Default_Handler + + .weak TIM8_CC_IRQHandler + .thumb_set TIM8_CC_IRQHandler,Default_Handler + + .weak DMA1_Stream7_IRQHandler + .thumb_set DMA1_Stream7_IRQHandler,Default_Handler + + .weak FMC_IRQHandler + .thumb_set FMC_IRQHandler,Default_Handler + + .weak SDIO_IRQHandler + .thumb_set SDIO_IRQHandler,Default_Handler + + .weak TIM5_IRQHandler + .thumb_set TIM5_IRQHandler,Default_Handler + + .weak SPI3_IRQHandler + .thumb_set SPI3_IRQHandler,Default_Handler + + .weak UART4_IRQHandler + .thumb_set UART4_IRQHandler,Default_Handler + + .weak UART5_IRQHandler + .thumb_set UART5_IRQHandler,Default_Handler + + .weak TIM6_DAC_IRQHandler + .thumb_set TIM6_DAC_IRQHandler,Default_Handler + + .weak TIM7_IRQHandler + .thumb_set TIM7_IRQHandler,Default_Handler + + .weak DMA2_Stream0_IRQHandler + .thumb_set DMA2_Stream0_IRQHandler,Default_Handler + + .weak DMA2_Stream1_IRQHandler + .thumb_set DMA2_Stream1_IRQHandler,Default_Handler + + .weak DMA2_Stream2_IRQHandler + .thumb_set DMA2_Stream2_IRQHandler,Default_Handler + + .weak DMA2_Stream3_IRQHandler + .thumb_set DMA2_Stream3_IRQHandler,Default_Handler + + .weak DMA2_Stream4_IRQHandler + .thumb_set DMA2_Stream4_IRQHandler,Default_Handler + + .weak ETH_IRQHandler + .thumb_set ETH_IRQHandler,Default_Handler + + .weak ETH_WKUP_IRQHandler + .thumb_set ETH_WKUP_IRQHandler,Default_Handler + + .weak CAN2_TX_IRQHandler + .thumb_set CAN2_TX_IRQHandler,Default_Handler + + .weak CAN2_RX0_IRQHandler + .thumb_set CAN2_RX0_IRQHandler,Default_Handler + + .weak CAN2_RX1_IRQHandler + .thumb_set CAN2_RX1_IRQHandler,Default_Handler + + .weak CAN2_SCE_IRQHandler + .thumb_set CAN2_SCE_IRQHandler,Default_Handler + + .weak OTG_FS_IRQHandler + .thumb_set OTG_FS_IRQHandler,Default_Handler + + .weak DMA2_Stream5_IRQHandler + .thumb_set DMA2_Stream5_IRQHandler,Default_Handler + + .weak DMA2_Stream6_IRQHandler + .thumb_set DMA2_Stream6_IRQHandler,Default_Handler + + .weak DMA2_Stream7_IRQHandler + .thumb_set DMA2_Stream7_IRQHandler,Default_Handler + + .weak USART6_IRQHandler + .thumb_set USART6_IRQHandler,Default_Handler + + .weak I2C3_EV_IRQHandler + .thumb_set I2C3_EV_IRQHandler,Default_Handler + + .weak I2C3_ER_IRQHandler + .thumb_set I2C3_ER_IRQHandler,Default_Handler + + .weak OTG_HS_EP1_OUT_IRQHandler + .thumb_set OTG_HS_EP1_OUT_IRQHandler,Default_Handler + + .weak OTG_HS_EP1_IN_IRQHandler + .thumb_set OTG_HS_EP1_IN_IRQHandler,Default_Handler + + .weak OTG_HS_WKUP_IRQHandler + .thumb_set OTG_HS_WKUP_IRQHandler,Default_Handler + + .weak OTG_HS_IRQHandler + .thumb_set OTG_HS_IRQHandler,Default_Handler + + .weak DCMI_IRQHandler + .thumb_set DCMI_IRQHandler,Default_Handler + + .weak HASH_RNG_IRQHandler + .thumb_set HASH_RNG_IRQHandler,Default_Handler + + .weak FPU_IRQHandler + .thumb_set FPU_IRQHandler,Default_Handler + + .weak UART7_IRQHandler + .thumb_set UART7_IRQHandler,Default_Handler + + .weak UART8_IRQHandler + .thumb_set UART8_IRQHandler,Default_Handler + + .weak SPI4_IRQHandler + .thumb_set SPI4_IRQHandler,Default_Handler + + .weak SPI5_IRQHandler + .thumb_set SPI5_IRQHandler,Default_Handler + + .weak SPI6_IRQHandler + .thumb_set SPI6_IRQHandler,Default_Handler + + .weak SAI1_IRQHandler + .thumb_set SAI1_IRQHandler,Default_Handler + + .weak LTDC_IRQHandler + .thumb_set LTDC_IRQHandler,Default_Handler + + .weak LTDC_ER_IRQHandler + .thumb_set LTDC_ER_IRQHandler,Default_Handler + + .weak DMA2D_IRQHandler + .thumb_set DMA2D_IRQHandler,Default_Handler + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ + + + diff --git a/projects/bootloader/stm-init.c b/projects/bootloader/stm-init.c new file mode 100644 index 0000000..b331b8a --- /dev/null +++ b/projects/bootloader/stm-init.c @@ -0,0 +1,7 @@ +/* Disable modules that the bootloader doesn't need. */ + +#include "stm32f4xx_hal.h" + +#undef HAL_I2C_MODULE_ENABLED + +#include "../../stm-init.c" diff --git a/projects/cli-test/Makefile b/projects/cli-test/Makefile index acf2720..5bb3fb1 100644 --- a/projects/cli-test/Makefile +++ b/projects/cli-test/Makefile @@ -1,11 +1,28 @@ -TEST = cli-test +PROJ = cli-test -OBJS = crc32.o test_sdram.o mgmt-cli.o mgmt-dfu.c mgmt-fpga.c mgmt-misc.c mgmt-show.c mgmt-test.c +# objs in addition to $(PROJ).o +OBJS = \ + mgmt-cli.o \ + mgmt-dfu.o \ + mgmt-fpga.o \ + mgmt-keystore.o \ + mgmt-masterkey.o \ + mgmt-misc.o \ + mgmt-show.o -CFLAGS += -I$(LIBCLI_DIR) -LIBS += $(LIBCLI_DIR)/libcli.a +CFLAGS += -I$(LIBCLI_SRC) -I$(LIBHAL_SRC) +CFLAGS += -I$(LIBTFM_BLD) +CFLAGS += -Wno-missing-field-initializers -all: $(TEST:=.elf) +ifdef DO_TIMING +CFLAGS += -DDO_TIMING +OBJS += mgmt-timing.o $(TOPLEVEL)/stm-dwt.o +LDFLAGS += -lm +endif + +LIBS += $(LIBCLI_BLD)/libcli.a $(LIBHAL_BLD)/libhal.a $(LIBTFM_BLD)/libtfm.a + +all: $(PROJ:=.elf) %.elf: %.o $(BOARD_OBJS) $(OBJS) $(LIBS) $(CC) $(CFLAGS) $^ -o $@ -T$(LDSCRIPT) -g -Wl,-Map=$*.map diff --git a/projects/cli-test/cli-test.c b/projects/cli-test/cli-test.c index ed6aac3..c50338b 100644 --- a/projects/cli-test/cli-test.c +++ b/projects/cli-test/cli-test.c @@ -33,6 +33,7 @@ */ #include "stm-init.h" #include "stm-led.h" +#include "stm-uart.h" #include "mgmt-cli.h" #include "mgmt-dfu.h" @@ -40,72 +41,78 @@ #include "mgmt-misc.h" #include "mgmt-show.h" #include "mgmt-test.h" +#include "mgmt-keystore.h" #include <string.h> +#include <strings.h> /* MGMT UART interrupt receive buffer (data will be put in a larger ring buffer) */ volatile uint8_t uart_rx; - -int check_auth(const char *username, const char *password) -{ - if (strcasecmp(username, "ct") != 0) - return CLI_ERROR; - if (strcasecmp(password, "ct") != 0) - return CLI_ERROR; - return CLI_OK; -} - -typedef void (*pFunction)(void); - -/* This is it's own function to make it more convenient to set a breakpoint at it in gdb */ -void do_early_dfu_jump(void) -{ - pFunction loaded_app = (pFunction) *dfu_code_ptr; - /* Set the stack pointer to the correct one for the firmware */ - __set_MSP(*dfu_msp_ptr); - /* Set the Vector Table Offset Register */ - SCB->VTOR = (uint32_t) dfu_firmware; - loaded_app(); - while (1); -} +/* Sleep for specified number of seconds -- used after bad PIN. */ +void hal_sleep(const unsigned seconds) { HAL_Delay(seconds * 1000); } int main() { - static struct cli_def cli; + stm_init(); + led_on(LED_GREEN); - /* Check if we've just rebooted in order to jump to the firmware. */ - if (*dfu_control == HARDWARE_EARLY_DFU_JUMP) { - *dfu_control = 0; - do_early_dfu_jump(); + /* For the hsm, the keystores are initialized in hal_rpc_server_init(), + * which is...less than optimal, but this is not the time or place to fix + * it, especially in light of all the terrible things I'm doing in this + * version of cli-test. + */ + extern void *hal_ks_token, *hal_ks_volatile; + extern int hal_ks_init(void *, int); + if (hal_ks_init(hal_ks_volatile, 1) != 0 || hal_ks_init(hal_ks_token, 1) != 0) + Error_Handler(); + + while (1) { + cli_main(); + /* embedded_cli_loop returns when the user enters 'quit' or 'exit' */ } - stm_init(); + /* NOT REACHED */ + Error_Handler(); +} - led_on(LED_RED); - mgmt_cli_init(&cli); - cli_set_auth_callback(&cli, check_auth); +/* end of variables declared with __attribute__((section(".sdram1"))) */ +extern uint8_t _esdram1 __asm ("_esdram1"); +/* end of SDRAM1 section */ +extern uint8_t __end_sdram1 __asm ("__end_sdram1"); +static uint8_t *sdram_heap = &_esdram1; - configure_cli_show(&cli); - configure_cli_fpga(&cli); - configure_cli_test(&cli); - configure_cli_misc(&cli); - configure_cli_dfu(&cli); +/* Allocate memory from SDRAM1. */ +static uint8_t *sdram_malloc(size_t size) +{ + uint8_t *p = sdram_heap; - led_off(LED_RED); - led_on(LED_GREEN); +#define pad(n) (((n) + 3) & ~3) + size = pad(size); - embedded_cli_loop(&cli); + if (p + size + sizeof(uint32_t) > &__end_sdram1) + return NULL; - /* embedded_cli_loop returns when the user enters 'quit' or 'exit' */ + *(uint32_t *)p = (uint32_t)size; + p += sizeof(uint32_t); - cli_print(&cli, "Rebooting in 3 seconds"); - HAL_Delay(3000); - HAL_NVIC_SystemReset(); + sdram_heap += size + sizeof(uint32_t); + return p; +} - /* NOT REACHED */ - Error_Handler(); +/* Implement static memory allocation for libhal over sdram_malloc(). + * Used in hal_ks_init. + */ +void *hal_allocate_static_memory(const size_t size) +{ + return sdram_malloc(size); +} + +int hal_free_static_memory(const void * const ptr) +{ + return 0; } + diff --git a/projects/cli-test/crc32.c b/projects/cli-test/crc32.c deleted file mode 100644 index 4d1a0bc..0000000 --- a/projects/cli-test/crc32.c +++ /dev/null @@ -1,62 +0,0 @@ -/* Reference code from RFC1952. Not meant to be used outside test code. */ - -#include "stm32f4xx_hal.h" - - -/* Table of CRCs of all 8-bit messages. */ -unsigned long crc_table[256]; - -/* Flag: has the table been computed? Initially false. */ -int crc_table_computed = 0; - -/* Make the table for a fast CRC. */ -void make_crc_table(void) -{ - unsigned long c; - - int n, k; - for (n = 0; n < 256; n++) { - c = (unsigned long) n; - for (k = 0; k < 8; k++) { - if (c & 1) { - c = 0xedb88320L ^ (c >> 1); - } else { - c = c >> 1; - } - } - crc_table[n] = c; - } - crc_table_computed = 1; -} - -/* - Update a running crc with the bytes buf[0..len-1] and return - the updated crc. The crc should be initialized to zero. Pre- and - post-conditioning (one's complement) is performed within this - function so it shouldn't be done by the caller. Usage example: - - unsigned long crc = 0L; - - while (read_buffer(buffer, length) != EOF) { - crc = update_crc(crc, buffer, length); - } - if (crc != original_crc) error(); -*/ -uint32_t update_crc(uint32_t crc, uint8_t *buf, int len) -{ - unsigned long c = crc ^ 0xffffffffL; - int n; - - if (!crc_table_computed) - make_crc_table(); - for (n = 0; n < len; n++) { - c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8); - } - return c ^ 0xffffffffL; -} - -/* Return the CRC of the bytes buf[0..len-1]. */ -unsigned long crc(unsigned char *buf, int len) -{ - return update_crc(0L, buf, len); -} diff --git a/projects/cli-test/filetransfer b/projects/cli-test/filetransfer index f704e31..6e58781 100755 --- a/projects/cli-test/filetransfer +++ b/projects/cli-test/filetransfer @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # # Copyright (c) 2016, NORDUnet A/S All rights reserved. # @@ -37,7 +37,7 @@ import struct import serial import argparse -from binascii import crc32 +from binascii import crc32, hexlify CHUNK_SIZE = 256 DFU_CHUNK_SIZE = 256 @@ -79,19 +79,19 @@ def parse_args(): def _write(dst, data): dst.write(data) if len(data) == 4: - print("Wrote 0x{!s}".format(data.encode('hex'))) + print(("Wrote 0x{!s}".format(hexlify(data).decode("ascii")))) else: - print("Wrote {!r}".format(data)) + print(("Wrote {!r}".format(data))) def _read(dst): - res = '' + res = b'' while True: x = dst.read(1) if not x: break res += x - print ("Read {!r}".format(res)) + print(("Read {!r}".format(res))) return res @@ -142,19 +142,19 @@ def send_file(filename, args, dst): if not data: break dst.write(data) - print("Wrote {!s} bytes (chunk {!s}/{!s})".format(len(data), counter, int(size / chunk_size))) + print(("Wrote {!s} bytes (chunk {!s}/{!s})".format(len(data), counter, int(size / chunk_size)))) # read ACK (a counter of number of 4k chunks received) while True: ack_bytes = dst.read(4) if len(ack_bytes) == 4: break - print('ERROR: Did not receive an ACK, got {!r}'.format(ack_bytes)) + print(('ERROR: Did not receive an ACK, got {!r}'.format(ack_bytes))) dst.write('\r') # eventually get back to the CLI prompt ack = struct.unpack('<I', ack_bytes)[0] if ack != counter + 1: - print('ERROR: Did not receive the expected counter as ACK (got {!r}/{!r}, not {!r})'.format(ack, ack_bytes, counter)) + print(('ERROR: Did not receive the expected counter as ACK (got {!r}/{!r}, not {!r})'.format(ack, ack_bytes, counter))) flush = dst.read(100) - print('FLUSH data: {!r}'.format(flush)) + print(('FLUSH data: {!r}'.format(flush))) return False counter += 1 diff --git a/projects/cli-test/mgmt-cli.c b/projects/cli-test/mgmt-cli.c index 3b4ffe9..960d691 100644 --- a/projects/cli-test/mgmt-cli.c +++ b/projects/cli-test/mgmt-cli.c @@ -3,7 +3,9 @@ * --------- * Management CLI code. * - * Copyright (c) 2016, NORDUnet A/S All rights reserved. + * Copyright (c) 2016-2017, NORDUnet A/S All rights reserved. + * Copyright: 2020, The Commons Conservancy Cryptech Project + * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -15,9 +17,9 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * - Neither the name of the NORDUnet nor the names of its contributors may - * be used to endorse or promote products derived from this software - * without specific prior written permission. + * - Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED @@ -31,140 +33,166 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include <string.h> + +/* Rename both CMSIS HAL_OK and libhal HAL_OK to disambiguate */ +#define HAL_OK CMSIS_HAL_OK #include "stm-init.h" #include "stm-uart.h" #include "stm-led.h" + #include "mgmt-cli.h" +#include "mgmt-fpga.h" +#include "mgmt-misc.h" +#include "mgmt-show.h" +#include "mgmt-test.h" +#include "mgmt-keystore.h" +#include "mgmt-masterkey.h" +#ifdef DO_TIMING +#include "mgmt-timing.h" +#endif + +#undef HAL_OK +#define HAL_OK LIBHAL_OK +#include "hal.h" +#undef HAL_OK + +#ifndef CLI_UART_RECVBUF_SIZE +#define CLI_UART_RECVBUF_SIZE 256 +#endif + +typedef struct { + unsigned ridx; + unsigned widx; + mgmt_cli_dma_state_t rx_state; + uint8_t buf[CLI_UART_RECVBUF_SIZE]; +} ringbuf_t; -#include <string.h> +inline void ringbuf_init(ringbuf_t *rb) +{ + memset(rb, 0, sizeof(*rb)); +} -extern uint8_t uart_rx; +/* return number of characters read */ +inline int ringbuf_read_char(ringbuf_t *rb, uint8_t *c) +{ + if (rb->ridx != rb->widx) { + *c = rb->buf[rb->ridx]; + if (++rb->ridx >= sizeof(rb->buf)) + rb->ridx = 0; + return 1; + } + return 0; +} -struct uart_ringbuf_t { - uint32_t enabled, ridx; - enum mgmt_cli_dma_state rx_state; - uint8_t buf[CLI_UART_RECVBUF_SIZE]; -}; +inline void ringbuf_write_char(ringbuf_t *rb, uint8_t c) +{ + rb->buf[rb->widx] = c; + if (++rb->widx >= sizeof(rb->buf)) + rb->widx = 0; +} + +static ringbuf_t uart_ringbuf; -volatile struct uart_ringbuf_t uart_ringbuf = {1, 0, DMA_RX_STOP, {0}}; +/* current character received from UART */ +static uint8_t uart_rx; -#define RINGBUF_RIDX(rb) (rb.ridx & CLI_UART_RECVBUF_MASK) -#define RINGBUF_WIDX(rb) (sizeof(rb.buf) - __HAL_DMA_GET_COUNTER(huart_mgmt.hdmarx)) -#define RINGBUF_COUNT(rb) ((unsigned)(RINGBUF_WIDX(rb) - RINGBUF_RIDX(rb))) -#define RINGBUF_READ(rb, dst) {dst = rb.buf[RINGBUF_RIDX(rb)]; rb.buf[RINGBUF_RIDX(rb)] = '.'; rb.ridx++;} +/* Callback for HAL_UART_Receive_DMA(). + */ +void HAL_UART1_RxCpltCallback(UART_HandleTypeDef *huart) +{ + huart = huart; -void uart_cli_print(struct cli_def *cli __attribute__ ((unused)), const char *buf) + ringbuf_write_char(&uart_ringbuf, uart_rx); +} + +static void uart_cli_print(struct cli_def *cli __attribute__ ((unused)), const char *buf) { - char crlf[] = "\r\n"; - uart_send_string2(STM_UART_MGMT, buf); - uart_send_string2(STM_UART_MGMT, crlf); + uart_send_string(buf); + uart_send_string("\r\n"); } -int uart_cli_read(struct cli_def *cli __attribute__ ((unused)), void *buf, size_t count) +static ssize_t uart_cli_read(struct cli_def *cli __attribute__ ((unused)), void *buf, size_t count) { - uint32_t timeout = 0xffffff; - while (count && timeout) { - if (RINGBUF_COUNT(uart_ringbuf)) { - RINGBUF_READ(uart_ringbuf, *(uint8_t *) buf); - buf++; - count--; + for (size_t i = 0; i < count; ++i) { + while (ringbuf_read_char(&uart_ringbuf, (uint8_t *)(buf + i)) == 0) { } - timeout--; } - if (! timeout) return 0; - - return 1; + return (ssize_t)count; } -int uart_cli_write(struct cli_def *cli __attribute__ ((unused)), const void *buf, size_t count) +static ssize_t uart_cli_write(struct cli_def *cli __attribute__ ((unused)), const void *buf, size_t count) { - uart_send_bytes(STM_UART_MGMT, (uint8_t *) buf, count); - return (int) count; + uart_send_bytes((uint8_t *) buf, count); + return (ssize_t)count; } -int control_mgmt_uart_dma_rx(enum mgmt_cli_dma_state state) +int control_mgmt_uart_dma_rx(mgmt_cli_dma_state_t state) { if (state == DMA_RX_START) { if (uart_ringbuf.rx_state != DMA_RX_START) { - memset((void *) uart_ringbuf.buf, 0, sizeof(uart_ringbuf.buf)); - - /* Start receiving data from the UART using DMA */ - HAL_UART_Receive_DMA(&huart_mgmt, (uint8_t *) uart_ringbuf.buf, sizeof(uart_ringbuf.buf)); - uart_ringbuf.ridx = 0; + ringbuf_init(&uart_ringbuf); + HAL_UART_Receive_DMA(&huart_mgmt, &uart_rx, 1); uart_ringbuf.rx_state = DMA_RX_START; } return 1; } else if (state == DMA_RX_STOP) { - if (HAL_UART_DMAStop(&huart_mgmt) != HAL_OK) return 0; + if (HAL_UART_DMAStop(&huart_mgmt) != CMSIS_HAL_OK) return 0; uart_ringbuf.rx_state = DMA_RX_STOP; return 1; } return 0; } -int embedded_cli_loop(struct cli_def *cli) +static struct cli_def *mgmt_cli_init(void) { - unsigned char c; - int n = 0; - static struct cli_loop_ctx ctx; - - memset(&ctx, 0, sizeof(ctx)); - ctx.insertmode = 1; + struct cli_def *cli; + cli = cli_init(); + cli_read_callback(cli, uart_cli_read); + cli_write_callback(cli, uart_cli_write); + cli_print_callback(cli, uart_cli_print); + cli_set_banner(cli, "Cryptech Alpha test CLI"); + cli_set_hostname(cli, "cryptech"); + return cli; +} - cli->state = CLI_STATE_LOGIN; +hal_user_t user; - /* start off in unprivileged mode */ - cli_set_privilege(cli, PRIVILEGE_UNPRIVILEGED); - cli_set_configmode(cli, MODE_EXEC, NULL); +static int check_auth(const char *username, const char *password) +{ + if (strcmp(username, "ct") != 0) + return CLI_ERROR; + if (strcmp(password, "ct") != 0) + return CLI_ERROR; + return CLI_OK; +} - cli_error(cli, "%s", cli->banner); +int cli_main(void) +{ + struct cli_def *cli; + cli = mgmt_cli_init(); + cli_set_auth_callback(cli, check_auth); + + configure_cli_show(cli); + configure_cli_fpga(cli); + configure_cli_misc(cli); + configure_cli_keystore(cli); + configure_cli_masterkey(cli); +#ifdef DO_TIMING + configure_cli_timing(cli); +#endif while (1) { - cli_loop_start_new_command(cli, &ctx); - - control_mgmt_uart_dma_rx(DMA_RX_START); - - while (1) { - cli_loop_show_prompt(cli, &ctx); - - n = cli_loop_read_next_char(cli, &ctx, &c); - - /* - cli_print(cli, "Next char: '%c'/%i, ringbuf ridx %i, widx %i", - c, (int) c, - uart_ringbuf.ridx, - RINGBUF_WIDX(uart_ringbuf) - */ - if (n == CLI_LOOP_CTRL_BREAK) - break; - if (n == CLI_LOOP_CTRL_CONTINUE) - continue; - - n = cli_loop_process_char(cli, &ctx, c); - if (n == CLI_LOOP_CTRL_BREAK) - break; - if (n == CLI_LOOP_CTRL_CONTINUE) - continue; - } - - if (ctx.l < 0) break; + control_mgmt_uart_dma_rx(DMA_RX_START); - /* cli_print(cli, "Process command: '%s'", ctx.cmd); */ - n = cli_loop_process_cmd(cli, &ctx); - if (n == CLI_LOOP_CTRL_BREAK) - break; + cli_loop(cli, 0); + /* cli_loop returns when the user enters 'quit' or 'exit' */ + cli_print(cli, "\nLogging out...\n"); + user = HAL_USER_NONE; } - return CLI_OK; + /*NOTREACHED*/ + return -1; } -void mgmt_cli_init(struct cli_def *cli) -{ - cli_init(cli); - cli_read_callback(cli, uart_cli_read); - cli_write_callback(cli, uart_cli_write); - cli_print_callback(cli, uart_cli_print); - cli_set_banner(cli, "Cryptech Alpha"); - cli_set_hostname(cli, "cryptech"); - cli_telnet_protocol(cli, 0); -} diff --git a/projects/cli-test/mgmt-cli.h b/projects/cli-test/mgmt-cli.h index 16c9fbd98..0b9c40c 100644 --- a/projects/cli-test/mgmt-cli.h +++ b/projects/cli-test/mgmt-cli.h @@ -35,52 +35,15 @@ #ifndef __STM32_MGMT_CLI_H #define __STM32_MGMT_CLI_H -#include "stm-init.h" #include <libcli.h> - -/* A bunch of defines to make it easier to add/maintain the CLI commands. - * - */ -#define _cli_cmd_struct(name, fullname, func, help) \ - static struct cli_command cmd_##fullname##_s = \ - {(char *) #name, func, 0, help, \ - PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL, NULL, NULL} - -/* ROOT is a top-level label with no command */ -#define cli_command_root(name) \ - _cli_cmd_struct(name, name, NULL, NULL); \ - cli_register_command2(cli, &cmd_##name##_s, NULL) - -/* BRANCH is a label with a parent, but no command */ -#define cli_command_branch(parent, name) \ - _cli_cmd_struct(name, parent##_##name, NULL, NULL); \ - cli_register_command2(cli, &cmd_##parent##_##name##_s, &cmd_##parent##_s) - -/* NODE is a label with a parent and with a command associated with it */ -#define cli_command_node(parent, name, help) \ - _cli_cmd_struct(name, parent##_##name, cmd_##parent##_##name, (char *) help); \ - cli_register_command2(cli, &cmd_##parent##_##name##_s, &cmd_##parent##_s) - -/* ROOT NODE is a label without a parent, but with a command associated with it */ -#define cli_command_root_node(name, help) \ - _cli_cmd_struct(name, name, cmd_##name, (char *) help); \ - cli_register_command2(cli, &cmd_##name##_s, NULL) - - -#define CLI_UART_RECVBUF_SIZE 256 /* This must be a power of 2 */ -#define CLI_UART_RECVBUF_MASK (CLI_UART_RECVBUF_SIZE - 1) - -enum mgmt_cli_dma_state { +typedef enum { DMA_RX_STOP, DMA_RX_START, -}; +} mgmt_cli_dma_state_t; + +extern int control_mgmt_uart_dma_rx(mgmt_cli_dma_state_t state); -extern void uart_cli_print(struct cli_def *cli __attribute__ ((unused)), const char *buf); -extern int uart_cli_read(struct cli_def *cli __attribute__ ((unused)), void *buf, size_t count); -extern int uart_cli_write(struct cli_def *cli __attribute__ ((unused)), const void *buf, size_t count); -extern int embedded_cli_loop(struct cli_def *cli); -extern void mgmt_cli_init(struct cli_def *cli); -extern int control_mgmt_uart_dma_rx(enum mgmt_cli_dma_state state); +extern int cli_main(void); #endif /* __STM32_MGMT_CLI_H */ diff --git a/projects/cli-test/mgmt-dfu.c b/projects/cli-test/mgmt-dfu.c index 27fd722..c7273f4 100644 --- a/projects/cli-test/mgmt-dfu.c +++ b/projects/cli-test/mgmt-dfu.c @@ -40,7 +40,10 @@ #include <string.h> -extern uint32_t update_crc(uint32_t crc, uint8_t *buf, int len); +#define DFU_FIRMWARE_ADDR ((uint32_t) &CRYPTECH_FIRMWARE_START) +#define DFU_FIRMWARE_END_ADDR ((uint32_t) &CRYPTECH_FIRMWARE_END) +#define DFU_UPLOAD_CHUNK_SIZE 256 +#define HARDWARE_EARLY_DFU_JUMP 0xBADABADA /* Linker symbols are strange in C. Make regular pointers for sanity. */ __IO uint32_t *dfu_control = &CRYPTECH_DFU_CONTROL; @@ -53,25 +56,29 @@ __IO uint32_t *dfu_msp_ptr = &CRYPTECH_FIRMWARE_START; */ __IO uint32_t *dfu_code_ptr = &CRYPTECH_FIRMWARE_START + 1; - - -int cmd_dfu_dump(struct cli_def *cli, const char *command, char *argv[], int argc) +static int cmd_dfu_dump(struct cli_def *cli, const char *command, char *argv[], int argc) { + command = command; + argv = argv; + argc = argc; + cli_print(cli, "First 256 bytes from DFU application address %p:\r\n", dfu_firmware); - uart_send_hexdump(STM_UART_MGMT, (uint8_t *) dfu_firmware, 0, 0xff); - uart_send_string2(STM_UART_MGMT, (char *) "\r\n\r\n"); + uart_send_hexdump((uint8_t *) dfu_firmware, 0, 0xff); + cli_print(cli, "\n"); return CLI_OK; } -int cmd_dfu_erase(struct cli_def *cli, const char *command, char *argv[], int argc) +static int cmd_dfu_erase(struct cli_def *cli, const char *command, char *argv[], int argc) { int status; - cli_print(cli, "Erasing flash sectors %i to %i (address %p to %p) - expect the CLI to crash now", - stm_flash_sector_num((uint32_t) dfu_firmware), - stm_flash_sector_num((uint32_t) dfu_firmware_end), + command = command; + argv = argv; + argc = argc; + + cli_print(cli, "Erasing flash address %p to %p - expect the CLI to crash now", dfu_firmware, dfu_firmware_end); @@ -82,9 +89,14 @@ int cmd_dfu_erase(struct cli_def *cli, const char *command, char *argv[], int ar return CLI_OK; } -int cmd_dfu_jump(struct cli_def *cli, const char *command, char *argv[], int argc) +static int cmd_dfu_jump(struct cli_def *cli, const char *command, char *argv[], int argc) { uint32_t i; + + command = command; + argv = argv; + argc = argc; + /* Load first byte from the DFU_FIRMWARE_PTR to verify it contains an IVT before * jumping there. */ @@ -112,9 +124,11 @@ int cmd_dfu_jump(struct cli_def *cli, const char *command, char *argv[], int arg void configure_cli_dfu(struct cli_def *cli) { - cli_command_root(dfu); + struct cli_command *c; + + c = cli_register_command(cli, NULL, "dfu", NULL, 0, 0, NULL); - cli_command_node(dfu, dump, "Show the first 256 bytes of the loaded firmware"); - cli_command_node(dfu, jump, "Jump to the loaded firmware"); - cli_command_node(dfu, erase, "Erase the firmware memory (will crash the CLI)"); + cli_register_command(cli, c, "dump", cmd_dfu_dump, 0, 0, "Show the first 256 bytes of the loaded firmware"); + cli_register_command(cli, c, "jump", cmd_dfu_jump, 0, 0, "Jump to the loaded firmware"); + cli_register_command(cli, c, "erase", cmd_dfu_erase, 0, 0, "Erase the firmware memory (will crash the CLI)"); } diff --git a/projects/cli-test/mgmt-dfu.h b/projects/cli-test/mgmt-dfu.h index ac6589c..6128b35 100644 --- a/projects/cli-test/mgmt-dfu.h +++ b/projects/cli-test/mgmt-dfu.h @@ -35,7 +35,6 @@ #ifndef __STM32_CLI_MGMT_DFU_H #define __STM32_CLI_MGMT_DFU_H -#include "stm-init.h" #include <libcli.h> /* symbols defined in the linker script (STM32F429BI.ld) */ @@ -43,17 +42,6 @@ extern uint32_t CRYPTECH_FIRMWARE_START; extern uint32_t CRYPTECH_FIRMWARE_END; extern uint32_t CRYPTECH_DFU_CONTROL; -#define DFU_FIRMWARE_ADDR ((uint32_t) &CRYPTECH_FIRMWARE_START) -#define DFU_FIRMWARE_END_ADDR ((uint32_t) &CRYPTECH_FIRMWARE_END) -#define DFU_UPLOAD_CHUNK_SIZE 256 -#define HARDWARE_EARLY_DFU_JUMP 0xBADABADA - -extern __IO uint32_t *dfu_control; -extern __IO uint32_t *dfu_firmware; -extern __IO uint32_t *dfu_msp_ptr; -extern __IO uint32_t *dfu_code_ptr; - - extern void configure_cli_dfu(struct cli_def *cli); #endif /* __STM32_CLI_MGMT_DFU_H */ diff --git a/projects/cli-test/mgmt-fpga.c b/projects/cli-test/mgmt-fpga.c index 8c1b2a8..b913316 100644 --- a/projects/cli-test/mgmt-fpga.c +++ b/projects/cli-test/mgmt-fpga.c @@ -3,7 +3,7 @@ * ----------- * CLI code to manage the FPGA configuration etc. * - * Copyright (c) 2016, NORDUnet A/S All rights reserved. + * Copyright (c) 2016-2017, NORDUnet A/S All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -43,25 +43,43 @@ #include <string.h> -volatile uint32_t dfu_offset = 0; +static volatile uint32_t dfu_offset = 0; -int _flash_write_callback(uint8_t *buf, size_t len) { - int res = fpgacfg_write_data(dfu_offset, buf, BITSTREAM_UPLOAD_CHUNK_SIZE) == 1; + +static HAL_StatusTypeDef _flash_write_callback(uint8_t *buf, size_t len) +{ + HAL_StatusTypeDef res; + + if ((dfu_offset % FPGACFG_SECTOR_SIZE) == 0) + /* first page in sector, need to erase sector */ + if ((res = fpgacfg_erase_sector(dfu_offset / FPGACFG_SECTOR_SIZE)) != HAL_OK) + return res; + + /* fpgacfg_write_data (a thin wrapper around n25q128_write_data) + * requires the offset and length to be page-aligned. The last chunk + * will be short, so we pad it out to the full chunk size. + */ + len = len; + res = fpgacfg_write_data(dfu_offset, buf, BITSTREAM_UPLOAD_CHUNK_SIZE); dfu_offset += BITSTREAM_UPLOAD_CHUNK_SIZE; return res; } -int cmd_fpga_bitstream_upload(struct cli_def *cli, const char *command, char *argv[], int argc) +static int cmd_fpga_bitstream_upload(struct cli_def *cli, const char *command, char *argv[], int argc) { uint8_t buf[BITSTREAM_UPLOAD_CHUNK_SIZE]; + command = command; + argv = argv; + argc = argc; + dfu_offset = 0; fpgacfg_access_control(ALLOW_ARM); cli_print(cli, "Checking if FPGA config memory is accessible"); - if (fpgacfg_check_id() != 1) { + if (fpgacfg_check_id() != HAL_OK) { cli_print(cli, "ERROR: FPGA config memory not accessible. Check that jumpers JP7 and JP8 are installed."); return CLI_ERROR; } @@ -74,12 +92,16 @@ int cmd_fpga_bitstream_upload(struct cli_def *cli, const char *command, char *ar return CLI_OK; } -int cmd_fpga_bitstream_erase(struct cli_def *cli, const char *command, char *argv[], int argc) +static int cmd_fpga_bitstream_erase(struct cli_def *cli, const char *command, char *argv[], int argc) { + command = command; + argv = argv; + argc = argc; + fpgacfg_access_control(ALLOW_ARM); cli_print(cli, "Checking if FPGA config memory is accessible"); - if (fpgacfg_check_id() != 1) { + if (fpgacfg_check_id() != HAL_OK) { cli_print(cli, "ERROR: FPGA config memory not accessible. Check that jumpers JP7 and JP8 are installed."); return CLI_ERROR; } @@ -90,7 +112,7 @@ int cmd_fpga_bitstream_erase(struct cli_def *cli, const char *command, char *arg * * This command could be made to accept an argument indicating the whole memory should be erased. */ - if (! fpgacfg_erase_sectors(1)) { + if (fpgacfg_erase_sector(0) != HAL_OK) { cli_print(cli, "Erasing first sector in FPGA config memory failed"); return CLI_ERROR; } @@ -101,8 +123,12 @@ int cmd_fpga_bitstream_erase(struct cli_def *cli, const char *command, char *arg return CLI_OK; } -int cmd_fpga_reset(struct cli_def *cli, const char *command, char *argv[], int argc) +static int cmd_fpga_reset(struct cli_def *cli, const char *command, char *argv[], int argc) { + command = command; + argv = argv; + argc = argc; + fpgacfg_access_control(ALLOW_FPGA); fpgacfg_reset_fpga(RESET_FULL); cli_print(cli, "FPGA has been reset"); @@ -110,8 +136,12 @@ int cmd_fpga_reset(struct cli_def *cli, const char *command, char *argv[], int a return CLI_OK; } -int cmd_fpga_reset_registers(struct cli_def *cli, const char *command, char *argv[], int argc) +static int cmd_fpga_reset_registers(struct cli_def *cli, const char *command, char *argv[], int argc) { + command = command; + argv = argv; + argc = argc; + fpgacfg_access_control(ALLOW_FPGA); fpgacfg_reset_fpga(RESET_REGISTERS); cli_print(cli, "FPGA registers have been reset"); @@ -121,16 +151,19 @@ int cmd_fpga_reset_registers(struct cli_def *cli, const char *command, char *arg void configure_cli_fpga(struct cli_def *cli) { - /* fpga */ - cli_command_root(fpga); + struct cli_command *c = cli_register_command(cli, NULL, "fpga", NULL, 0, 0, NULL); + /* fpga reset */ - cli_command_node(fpga, reset, "Reset FPGA (config reset)"); + struct cli_command *c_reset = cli_register_command(cli, c, "reset", cmd_fpga_reset, 0, 0, "Reset FPGA (config reset)"); + /* fpga reset registers */ - cli_command_node(fpga_reset, registers, "Reset FPGA registers (soft reset)"); + cli_register_command(cli, c_reset, "registers", cmd_fpga_reset_registers, 0, 0, "Reset FPGA registers (soft reset)"); + + struct cli_command *c_bitstream = cli_register_command(cli, c, "bitstream", NULL, 0, 0, NULL); - cli_command_branch(fpga, bitstream); /* fpga bitstream upload */ - cli_command_node(fpga_bitstream, upload, "Upload new FPGA bitstream"); + cli_register_command(cli, c_bitstream, "upload", cmd_fpga_bitstream_upload, 0, 0, "Upload new FPGA bitstream"); + /* fpga bitstream erase */ - cli_command_node(fpga_bitstream, erase, "Erase FPGA config memory"); + cli_register_command(cli, c_bitstream, "erase", cmd_fpga_bitstream_erase, 0, 0, "Erase FPGA config memory"); } diff --git a/projects/cli-test/mgmt-fpga.h b/projects/cli-test/mgmt-fpga.h index ce185de..9d0aedc 100644 --- a/projects/cli-test/mgmt-fpga.h +++ b/projects/cli-test/mgmt-fpga.h @@ -35,7 +35,6 @@ #ifndef __STM32_CLI_MGMT_FPGA_H #define __STM32_CLI_MGMT_FPGA_H -#include "stm-init.h" #include <libcli.h> diff --git a/projects/cli-test/mgmt-keystore.c b/projects/cli-test/mgmt-keystore.c new file mode 100644 index 0000000..6d0d38d --- /dev/null +++ b/projects/cli-test/mgmt-keystore.c @@ -0,0 +1,412 @@ +/* + * mgmt-keystore.c + * --------------- + * CLI 'keystore' commands. + * + * Copyright (c) 2016, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Rename both CMSIS HAL_OK and libhal HAL_OK to disambiguate */ + +#define HAL_OK CMSIS_HAL_OK +#include "stm-init.h" +#include "stm-keystore.h" +#include "stm-fpgacfg.h" +#include "stm-uart.h" +#include "mgmt-cli.h" +#include "mgmt-show.h" +#undef HAL_OK + +#define HAL_OK LIBHAL_OK +#include "hal.h" +#warning Really should not be including hal_internal.h here, fix API instead of bypassing it +#include "hal_internal.h" +#undef HAL_OK + +#include <stdlib.h> +#include <string.h> + + +static int cmd_keystore_set_pin(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + hal_user_t user; + hal_error_t status; + hal_client_handle_t client = { -1 }; + + command = command; + + if (argc != 2) { + cli_print(cli, "Wrong number of arguments (%i).", argc); + cli_print(cli, "Syntax: keystore set pin <user|so|wheel> <pin>"); + return CLI_ERROR; + } + + if (!strcmp(argv[0], "user")) + user = HAL_USER_NORMAL; + else if (!strcmp(argv[0], "so")) + user = HAL_USER_SO; + else if (!strcmp(argv[0], "wheel")) + user = HAL_USER_WHEEL; + else { + cli_print(cli, "First argument must be 'user', 'so' or 'wheel' - not '%s'", argv[0]); + return CLI_ERROR; + } + + status = hal_rpc_set_pin(client, user, argv[1], strlen(argv[1])); + if (status != LIBHAL_OK) { + cli_print(cli, "Failed setting PIN: %s", hal_error_string(status)); + return CLI_ERROR; + } + + return CLI_OK; +} + +static int cmd_keystore_clear_pin(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + hal_user_t user; + hal_error_t status; + hal_client_handle_t client = { -1 }; + + command = command; + + if (argc != 1) { + cli_print(cli, "Wrong number of arguments (%i).", argc); + cli_print(cli, "Syntax: keystore clear pin <user|so|wheel>"); + return CLI_ERROR; + } + + if (!strcmp(argv[0], "user")) + user = HAL_USER_NORMAL; + else if (!strcmp(argv[0], "so")) + user = HAL_USER_SO; + else if (!strcmp(argv[0], "wheel")) + user = HAL_USER_WHEEL; + else { + cli_print(cli, "First argument must be 'user', 'so' or 'wheel' - not '%s'", argv[0]); + return CLI_ERROR; + } + + status = hal_rpc_set_pin(client, user, "", 0); + if (status != LIBHAL_OK) { + cli_print(cli, "Failed setting PIN: %s", hal_error_string(status)); + return CLI_ERROR; + } + + return CLI_OK; +} + +static int cmd_keystore_set_pin_iterations(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + hal_error_t status; + hal_client_handle_t client = { -1 }; + + command = command; + + if (argc != 1) { + cli_print(cli, "Wrong number of arguments (%i).", argc); + cli_print(cli, "Syntax: keystore set pin iterations <number>"); + return CLI_ERROR; + } + + status = hal_set_pin_default_iterations(client, strtoul(argv[0], NULL, 0)); + if (status != LIBHAL_OK) { + cli_print(cli, "Failed setting iterations: %s", hal_error_string(status)); + return CLI_ERROR; + } + + return CLI_OK; +} + +/* + * This is badly broken under either old or new keystore API: + * + * + DER is a binary format, it's not safe to read it this way, + * and strlen() will not do what anybody wants; + * + * + As written, this stores an EC public key on no known curve, + * ie, useless nonsense. + * + * The usual text format for DER objects is Base64, often with + * so-called "PEM" header and footer lines. Key type, curve, etcetera + * would be extra command line parameters. + */ +#if 0 +static int cmd_keystore_set_key(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + hal_error_t status; + int hint = 0; + + command = command; + + if (argc != 2) { + cli_print(cli, "Wrong number of arguments (%i).", argc); + cli_print(cli, "Syntax: keystore set key <name> <der>"); + return CLI_ERROR; + } + + if ((status = hal_ks_store(HAL_KEY_TYPE_EC_PUBLIC, + HAL_CURVE_NONE, + 0, + (uint8_t *) argv[0], strlen(argv[0]), + (uint8_t *) argv[1], strlen(argv[1]), + &hint)) != LIBHAL_OK) { + + cli_print(cli, "Failed storing key: %s", hal_error_string(status)); + return CLI_ERROR; + } + + cli_print(cli, "Stored key %i", hint); + + return CLI_OK; +} +#endif /* 0 */ + +static int cmd_keystore_delete_key(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + const hal_client_handle_t client = { HAL_HANDLE_NONE }; + const hal_session_handle_t session = { HAL_HANDLE_NONE }; + hal_pkey_handle_t pkey = { HAL_HANDLE_NONE }; + hal_error_t status; + hal_uuid_t name; + + command = command; + + if (argc != 1) { + cli_print(cli, "Wrong number of arguments (%i).", argc); + cli_print(cli, "Syntax: keystore delete key <name>"); + return CLI_ERROR; + } + + if ((status = hal_uuid_parse(&name, argv[0])) != LIBHAL_OK) { + cli_print(cli, "Couldn't parse key name: %s", hal_error_string(status)); + return CLI_ERROR; + } + + if ((status = hal_rpc_pkey_open(client, session, &pkey, &name)) != LIBHAL_OK || + (status = hal_rpc_pkey_delete(pkey)) != LIBHAL_OK) { + cli_print(cli, "Failed deleting key: %s", hal_error_string(status)); + return CLI_ERROR; + } + + cli_print(cli, "Deleted key %s", argv[0]); + + return CLI_OK; +} + +static int cmd_keystore_show_data(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + uint8_t buf[KEYSTORE_PAGE_SIZE]; + uint32_t i; + + command = command; + argv = argv; + argc = argc; + + if (keystore_check_id() != CMSIS_HAL_OK) { + cli_print(cli, "ERROR: The keystore memory is not accessible."); + } + + memset(buf, 0, sizeof(buf)); + if ((i = keystore_read_data(0, buf, sizeof(buf))) != CMSIS_HAL_OK) { + cli_print(cli, "Failed reading first page from keystore memory: %li", i); + return CLI_ERROR; + } + + cli_print(cli, "First page from keystore memory:\r\n"); + uart_send_hexdump(buf, 0, sizeof(buf) - 1); + cli_print(cli, "\n"); + + return CLI_OK; +} + +static int show_keys(struct cli_def *cli, const char *title) +{ + const hal_client_handle_t client = { -1 }; + const hal_session_handle_t session = { HAL_HANDLE_NONE }; + char key_name[HAL_UUID_TEXT_SIZE]; + hal_uuid_t previous_uuid = {{0}}; + hal_pkey_handle_t pkey; + hal_curve_name_t curve; + hal_key_flags_t flags; + unsigned n, state = 0; + hal_key_type_t type; + hal_error_t status; + hal_uuid_t uuids[50]; + int done = 0; + + cli_print(cli, title); + + while (!done) { + + if ((status = hal_rpc_pkey_match(client, session, HAL_KEY_TYPE_NONE, HAL_CURVE_NONE, + 0, 0, NULL, 0, &state, uuids, &n, + sizeof(uuids)/sizeof(*uuids), + &previous_uuid)) != LIBHAL_OK) { + cli_print(cli, "Could not fetch UUID list: %s", hal_error_string(status)); + return 0; + } + + done = n < sizeof(uuids)/sizeof(*uuids); + + if (!done) + previous_uuid = uuids[sizeof(uuids)/sizeof(*uuids) - 1]; + + for (unsigned i = 0; i < n; i++) { + + if ((status = hal_uuid_format(&uuids[i], key_name, sizeof(key_name))) != LIBHAL_OK) { + cli_print(cli, "Could not convert key name: %s", + hal_error_string(status)); + return 0; + } + + if ((status = hal_rpc_pkey_open(client, session, &pkey, &uuids[i])) != LIBHAL_OK) { + cli_print(cli, "Could not open key %s: %s", + key_name, hal_error_string(status)); + return 0; + } + + if ((status = hal_rpc_pkey_get_key_type(pkey, &type)) != LIBHAL_OK || + (status = hal_rpc_pkey_get_key_curve(pkey, &curve)) != LIBHAL_OK || + (status = hal_rpc_pkey_get_key_flags(pkey, &flags)) != LIBHAL_OK) + cli_print(cli, "Could not fetch metadata for key %s: %s", + key_name, hal_error_string(status)); + + if (status == LIBHAL_OK) + status = hal_rpc_pkey_close(pkey); + else + (void) hal_rpc_pkey_close(pkey); + + if (status != LIBHAL_OK) + return 0; + + const char *type_name = "unknown"; + switch (type) { + case HAL_KEY_TYPE_NONE: type_name = "none"; break; + case HAL_KEY_TYPE_RSA_PRIVATE: type_name = "RSA private"; break; + case HAL_KEY_TYPE_RSA_PUBLIC: type_name = "RSA public"; break; + case HAL_KEY_TYPE_EC_PRIVATE: type_name = "EC private"; break; + case HAL_KEY_TYPE_EC_PUBLIC: type_name = "EC public"; break; + } + + const char *curve_name = "unknown"; + switch (curve) { + case HAL_CURVE_NONE: curve_name = "none"; break; + case HAL_CURVE_P256: curve_name = "P-256"; break; + case HAL_CURVE_P384: curve_name = "P-384"; break; + case HAL_CURVE_P521: curve_name = "P-521"; break; + } + + cli_print(cli, "Key %2i, name %s, type %s, curve %s, flags 0x%lx", + i, key_name, type_name, curve_name, (unsigned long) flags); + } + } + + return 1; +} + +static int cmd_keystore_show_keys(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + command = command; + argv = argv; + argc = argc; + + if (show_keys(cli, "Keystore:")) + return CLI_OK; + else + return CLI_ERROR; +} + +static int cmd_keystore_erase(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + hal_error_t err; + int status; + + command = command; + + if (argc != 1 || strcmp(argv[0], "YesIAmSure") != 0) { + cli_print(cli, "Syntax: keystore erase YesIAmSure"); + return CLI_ERROR; + } + + cli_print(cli, "OK, erasing keystore, this might take a while..."); + if ((status = keystore_erase_bulk()) != CMSIS_HAL_OK) { + cli_print(cli, "Failed erasing token keystore: %i", status); + return CLI_ERROR; + } + + if ((err = hal_ks_init(hal_ks_token, 0)) != LIBHAL_OK) { + cli_print(cli, "Failed to reinitialize token keystore: %s", hal_error_string(err)); + return CLI_ERROR; + } + + if ((err = hal_ks_init(hal_ks_volatile, 0)) != LIBHAL_OK) { + cli_print(cli, "Failed to reinitialize memory keystore: %s", hal_error_string(err)); + return CLI_ERROR; + } + + cli_print(cli, "Keystore erased"); + return CLI_OK; +} + +void configure_cli_keystore(struct cli_def *cli) +{ + struct cli_command *c = cli_register_command(cli, NULL, "keystore", NULL, 0, 0, NULL); + + struct cli_command *c_set = cli_register_command(cli, c, "set", NULL, 0, 0, NULL); + struct cli_command *c_clear = cli_register_command(cli, c, "clear", NULL, 0, 0, NULL); + struct cli_command *c_delete = cli_register_command(cli, c, "delete", NULL, 0, 0, NULL); + struct cli_command *c_show = cli_register_command(cli, c, "show", NULL, 0, 0, NULL); + + /* keystore erase */ + cli_register_command(cli, c, "erase", cmd_keystore_erase, 0, 0, "Erase the whole keystore"); + + /* keystore set pin */ + struct cli_command *c_set_pin = cli_register_command(cli, c_set, "pin", cmd_keystore_set_pin, 0, 0, "Set either 'wheel', 'user' or 'so' PIN"); + + /* keystore set pin iterations */ + cli_register_command(cli, c_set_pin, "iterations", cmd_keystore_set_pin_iterations, 0, 0, "Set PBKDF2 iterations for PINs"); + + /* keystore clear pin */ + cli_register_command(cli, c_clear, "pin", cmd_keystore_clear_pin, 0, 0, "Clear either 'wheel', 'user' or 'so' PIN"); + +#if 0 + /* keystore set key */ + cli_register_command(cli, c_set, "key", cmd_keystore_set_key, 0, 0, "Set a key"); +#endif + + /* keystore delete key */ + cli_register_command(cli, c_delete, "key", cmd_keystore_delete_key, 0, 0, "Delete a key"); + + /* keystore show data */ + cli_register_command(cli, c_show, "data", cmd_keystore_show_data, 0, 0, "Dump the first page from the keystore memory"); + + /* keystore show keys */ + cli_register_command(cli, c_show, "keys", cmd_keystore_show_keys, 0, 0, "Show what keys are in the keystore"); + +} diff --git a/projects/cli-test/mgmt-keystore.h b/projects/cli-test/mgmt-keystore.h new file mode 100644 index 0000000..9e14ac6 --- /dev/null +++ b/projects/cli-test/mgmt-keystore.h @@ -0,0 +1,42 @@ +/* + * mgmt-keystore.h + * ---------- + * Management CLI 'keystore' functions. + * + * Copyright (c) 2016, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __STM32_CLI_MGMT_KEYSTORE_H +#define __STM32_CLI_MGMT_KEYSTORE_H + +#include <libcli.h> + +extern void configure_cli_keystore(struct cli_def *cli); + +#endif /* __STM32_CLI_MGMT_KEYSTORE_H */ diff --git a/projects/cli-test/mgmt-keywrap.c b/projects/cli-test/mgmt-keywrap.c new file mode 100644 index 0000000..7d3e219 --- /dev/null +++ b/projects/cli-test/mgmt-keywrap.c @@ -0,0 +1,316 @@ +/* + * mgmt-keywrap.h + * ----------- + * Management CLI functions related to AES keywrap + * + * Copyright (c) 2018, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define HAL_OK CMSIS_HAL_OK +#include "stm-init.h" +#include "stm-uart.h" +#include "mgmt-cli.h" +#include "mgmt-keywrap.h" +#undef HAL_OK + +#define HAL_OK LIBHAL_OK +#include "hal.h" +#include "hal_internal.h" +#undef HAL_OK + +#include <string.h> + +/* test vectors and test code are from test-aes-key-wrap.c */ + +/* + * Test cases from RFC 5649 all use a 192-bit key, which our AES + * implementation doesn't support, so had to write our own. + */ + +static const uint8_t Q[] = { /* Plaintext, 81 bytes */ + 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x21, 0x20, 0x20, 0x4d, 0x79, 0x20, 0x6e, + 0x61, 0x6d, 0x65, 0x20, 0x69, 0x73, 0x20, 0x49, 0x6e, 0x69, 0x67, 0x6f, + 0x20, 0x4d, 0x6f, 0x6e, 0x74, 0x6f, 0x79, 0x61, 0x2e, 0x20, 0x20, 0x59, + 0x6f, 0x75, 0x20, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x20, 0x6d, 0x79, 0x20, + 0x41, 0x45, 0x53, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x77, 0x72, 0x61, 0x70, + 0x70, 0x65, 0x72, 0x2e, 0x20, 0x20, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, + 0x65, 0x20, 0x74, 0x6f, 0x20, 0x64, 0x69, 0x65, 0x2e +}; + +static const uint8_t K_128[] = { /* 128-bit KEK, 16 bytes */ + 0xbc, 0x2a, 0xd8, 0x90, 0xd8, 0x91, 0x10, 0x65, 0xf0, 0x42, 0x10, 0x1b, + 0x4a, 0x6b, 0xaf, 0x99 +}; + +static const uint8_t K_256[] = { /* 256-bit KEK, 32 bytes */ + 0xe3, 0x97, 0x52, 0x81, 0x2b, 0x7e, 0xc2, 0xa4, 0x6a, 0xac, 0x50, 0x18, + 0x0d, 0x10, 0xc6, 0x85, 0x2c, 0xcf, 0x86, 0x0a, 0xa9, 0x4f, 0x69, 0xab, + 0x16, 0xa6, 0x4f, 0x3e, 0x96, 0xa0, 0xbd, 0x9e +}; + +static const uint8_t C_128[] = { /* Plaintext wrapped by 128-bit KEK, 96 bytes */ + 0xb0, 0x10, 0x91, 0x7b, 0xe7, 0x67, 0x9c, 0x10, 0x16, 0x64, 0xe7, 0x73, + 0xd2, 0x68, 0xba, 0xed, 0x8c, 0x50, 0x49, 0x80, 0x16, 0x2f, 0x4e, 0x97, + 0xe8, 0x45, 0x5c, 0x2f, 0x2b, 0x7a, 0x88, 0x0e, 0xd8, 0xef, 0xaa, 0x40, + 0xb0, 0x2e, 0xb4, 0x50, 0xe7, 0x60, 0xf7, 0xbb, 0xed, 0x56, 0x79, 0x16, + 0x65, 0xb7, 0x13, 0x9b, 0x4c, 0x66, 0x86, 0x5f, 0x4d, 0x53, 0x2d, 0xcd, + 0x83, 0x41, 0x01, 0x35, 0x0d, 0x06, 0x39, 0x4e, 0x9e, 0xfe, 0x68, 0xc5, + 0x2f, 0x37, 0x33, 0x99, 0xbb, 0x88, 0xf7, 0x76, 0x1e, 0x82, 0x48, 0xd6, + 0xa2, 0xf3, 0x9b, 0x92, 0x01, 0x65, 0xcb, 0x48, 0x36, 0xf5, 0x42, 0xd3 +}; + +static const uint8_t C_256[] = { /* Plaintext wrapped by 256-bit KEK, 96 bytes */ + 0x08, 0x00, 0xbc, 0x1b, 0x35, 0xe4, 0x2a, 0x69, 0x3f, 0x43, 0x07, 0x54, + 0x31, 0xba, 0xb6, 0x89, 0x7c, 0x64, 0x9f, 0x03, 0x84, 0xc4, 0x4a, 0x71, + 0xdb, 0xcb, 0xae, 0x55, 0x30, 0xdf, 0xb0, 0x2b, 0xc3, 0x91, 0x5d, 0x07, + 0xa9, 0x24, 0xdb, 0xe7, 0xbe, 0x4d, 0x0d, 0x62, 0xd4, 0xf8, 0xb1, 0x94, + 0xf1, 0xb9, 0x22, 0xb5, 0x94, 0xab, 0x7e, 0x0b, 0x15, 0x6a, 0xd9, 0x5f, + 0x6c, 0x20, 0xb7, 0x7e, 0x13, 0x19, 0xfa, 0xc4, 0x70, 0xec, 0x0d, 0xbd, + 0xf7, 0x01, 0xc6, 0xb3, 0x9a, 0x19, 0xaf, 0xf2, 0x47, 0x68, 0xea, 0x7e, + 0x97, 0x7e, 0x52, 0x2e, 0xd4, 0x03, 0x31, 0xcb, 0x22, 0xb6, 0xfe, 0xf5 +}; + +static int run_test(struct cli_def *cli, + const uint8_t * const K, const size_t K_len, + const uint8_t * const C, const size_t C_len) +{ +#define TC_BUFSIZE 96 /* sizeof(C) */ + const size_t Q_len = sizeof(Q); + uint8_t q[TC_BUFSIZE], c[TC_BUFSIZE]; + size_t q_len = sizeof(q), c_len = sizeof(c); + hal_error_t err; + int ok1 = 1, ok2 = 1; + + /* + * Wrap and compare results. + */ + + cli_print(cli, "Wrapping with %lu-bit KEK...", (unsigned long) K_len * 8); + if ((err = hal_aes_keywrap(NULL, K, K_len, Q, Q_len, c, &c_len)) != LIBHAL_OK) { + cli_print(cli, "Couldn't wrap with %lu-bit KEK: %s", + (unsigned long) K_len * 8, hal_error_string(err)); + ok1 = 0; + } + else if (C_len != c_len || memcmp(C, c, C_len) != 0) { + cli_print(cli, "Ciphertext mismatch:\n Want: "); + uart_send_hexdump(C, 0, C_len - 1); + cli_print(cli, "\n Got: "); + uart_send_hexdump(c, 0, c_len - 1); + cli_print(cli, ""); + ok1 = 0; + } + else { + cli_print(cli, "OK"); + } + + /* + * Unwrap and compare results. + */ + + cli_print(cli, "Unwrapping with %lu-bit KEK...", (unsigned long) K_len * 8); + if ((err = hal_aes_keyunwrap(NULL, K, K_len, C, C_len, q, &q_len)) != LIBHAL_OK) { + cli_print(cli, "Couldn't unwrap with %lu-bit KEK: %s", + (unsigned long) K_len * 8, hal_error_string(err)); + ok2 = 0; + } + else if (Q_len != q_len || memcmp(Q, q, Q_len) != 0) { + cli_print(cli, "Plaintext mismatch:\n Want: "); + uart_send_hexdump(Q, 0, Q_len - 1); + cli_print(cli, "\n Got: "); + uart_send_hexdump(q, 0, q_len - 1); + cli_print(cli, ""); + ok2 = 0; + } + else { + cli_print(cli, "OK"); + } + + return ok1 && ok2; +} + +static int cmd_keywrap_test(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + command = command; + + if (argc == 0) { + cli_print(cli, "1. Test vectors with software keywrap"); + hal_aes_use_keywrap_core(0); + run_test(cli, K_128, sizeof(K_128), C_128, sizeof(C_128)); + run_test(cli, K_256, sizeof(K_256), C_256, sizeof(C_256)); + + cli_print(cli, "\n2. Test vectors with keywrap core"); + if (hal_aes_use_keywrap_core(1) == 0) { + cli_print(cli, "keywrap core not found, skipping"); + } + else { + hal_aes_use_keywrap_core(1); + run_test(cli, K_128, sizeof(K_128), C_128, sizeof(C_128)); + run_test(cli, K_256, sizeof(K_256), C_256, sizeof(C_256)); + } + + cli_print(cli, "\nFor more tests: keywrap test <keysize> <iterations>"); + return CLI_OK; + } + + hal_error_t err; + + if (argc != 2) { + usage: + cli_print(cli, "Syntax: keywrap test <keysize> <iterations>"); + return CLI_ERROR; + } + + const int keysize = atoi(argv[0]); + const int iterations = atoi(argv[1]); + if (keysize <= 0 || iterations <= 0) + goto usage; + + uint8_t Q[keysize + 8]; size_t Q_len; + uint8_t C[keysize + 8]; size_t C_len; + memset(C, 0, sizeof(C)); + if ((err = hal_get_random(NULL, Q, keysize)) != LIBHAL_OK) { + cli_print(cli, "hal_get_random: %s", hal_error_string(err)); + return CLI_ERROR; + } + + cli_print(cli, "1. sanity test"); + C_len = sizeof(C); + if ((err = hal_aes_keywrap(NULL, K_256, sizeof(K_256), Q, keysize, C, &C_len)) != LIBHAL_OK) { + cli_print(cli, "hal_aes_keywrap: %s", hal_error_string(err)); + return CLI_ERROR; + } + + for (int i = 0; i <= 1; ++i) { + if (!hal_aes_use_keywrap_core(i) && i) { + cli_print(cli, "keywrap core not found, skipping"); + continue; + } + uint8_t q[keysize + 8]; + size_t q_len = sizeof(q); + if ((err = hal_aes_keyunwrap(NULL, K_256, sizeof(K_256), C, C_len, q, &q_len)) != LIBHAL_OK) { + cli_print(cli, "hal_aes_keyunwrap: %s", hal_error_string(err)); + return CLI_ERROR; + } + if (q_len != (size_t)keysize) { + cli_print(cli, "unwrap size mismatch: expected %d, got %d", (int)keysize, (int)q_len); + return CLI_ERROR; + } + if (memcmp(Q, q, q_len) != 0) { + cli_print(cli, "unwrap mismatch:\n Want: "); + uart_send_hexdump(Q, 0, Q_len - 1); + cli_print(cli, "\n Got: "); + uart_send_hexdump(q, 0, q_len - 1); + cli_print(cli, ""); + return CLI_ERROR; + } + cli_print(cli, "with %s: OK", i ? "keywrap core" : "software keywrap"); + } + + cli_print(cli, "\n2. wrap timing with software keywrap"); + + hal_aes_use_keywrap_core(0); + uint32_t start = HAL_GetTick(); + for (int i = 0; i < iterations; ++i) { + C_len = sizeof(C); + if ((err = hal_aes_keywrap(NULL, K_256, sizeof(K_256), Q, keysize, C, &C_len)) != LIBHAL_OK) { + cli_print(cli, "hal_aes_keywrap: %s", hal_error_string(err)); + return CLI_ERROR; + } + } + uint32_t elapsed = HAL_GetTick() - start; + uint32_t per = 1000 * elapsed / iterations; + cli_print(cli, "%ld.%03lds total, %ld.%03ldms per wrap", + elapsed / 1000, elapsed % 1000, per / 1000, per % 1000); + + cli_print(cli, "\n3. wrap timing with keywrap core"); + + if (hal_aes_use_keywrap_core(1) == 0) { + cli_print(cli, "keywrap core not found, skipping"); + } + else { + start = HAL_GetTick(); + for (int i = 0; i < iterations; ++i) { + C_len = sizeof(C); + if ((err = hal_aes_keywrap(NULL, K_256, sizeof(K_256), Q, keysize, C, &C_len)) != LIBHAL_OK) { + cli_print(cli, "hal_aes_keywrap: %s", hal_error_string(err)); + return CLI_ERROR; + } + } + elapsed = HAL_GetTick() - start; + per = 1000 * elapsed / iterations; + cli_print(cli, "%ld.%03lds total, %ld.%03ldms per wrap", + elapsed / 1000, elapsed % 1000, per / 1000, per % 1000); + } + + cli_print(cli, "\n4. unwrap timing with software keywrap"); + + hal_aes_use_keywrap_core(0); + start = HAL_GetTick(); + for (int i = 0; i < iterations; ++i) { + Q_len = sizeof(Q); + if ((err = hal_aes_keyunwrap(NULL, K_256, sizeof(K_256), C, C_len, Q, &Q_len)) != LIBHAL_OK) { + cli_print(cli, "hal_aes_keyunwrap: %s", hal_error_string(err)); + return CLI_ERROR; + } + } + elapsed = HAL_GetTick() - start; + per = 1000 * elapsed / iterations; + cli_print(cli, "%ld.%03lds total, %ld.%03ldms per wrap", + elapsed / 1000, elapsed % 1000, per / 1000, per % 1000); + + cli_print(cli, "\n5. unwrap timing with keywrap core"); + + if (hal_aes_use_keywrap_core(1) == 0) { + cli_print(cli, "keywrap core not found, skipping"); + } + else { + start = HAL_GetTick(); + for (int i = 0; i < iterations; ++i) { + Q_len = sizeof(Q); + if ((err = hal_aes_keyunwrap(NULL, K_256, sizeof(K_256), C, C_len, Q, &Q_len)) != LIBHAL_OK) { + cli_print(cli, "hal_aes_keywrap: %s", hal_error_string(err)); + return CLI_ERROR; + } + } + elapsed = HAL_GetTick() - start; + per = 1000 * elapsed / iterations; + cli_print(cli, "%ld.%03lds total, %ld.%03ldms per wrap", + elapsed / 1000, elapsed % 1000, per / 1000, per % 1000); + } + + return CLI_OK; +} + +void configure_cli_keywrap(struct cli_def *cli) +{ + struct cli_command *c_keywrap = cli_register_command(cli, NULL, "keywrap", NULL, 0, 0, NULL); + + /* keywrap test */ + cli_register_command(cli, c_keywrap, "test", cmd_keywrap_test, 0, 0, "Test the keywrap core"); +} diff --git a/projects/cli-test/mgmt-keywrap.h b/projects/cli-test/mgmt-keywrap.h new file mode 100644 index 0000000..a18aded --- /dev/null +++ b/projects/cli-test/mgmt-keywrap.h @@ -0,0 +1,42 @@ +/* + * mgmt-keywrap.h + * ----------- + * Management CLI functions related to AES keywrap + * + * Copyright (c) 2018, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __STM32_CLI_MGMT_KEYWRAP_H +#define __STM32_CLI_MGMT_KEYWRAP_H + +#include <libcli.h> + +extern void configure_cli_keywrap(struct cli_def *cli); + +#endif /* __STM32_CLI_MGMT_KEYWRAP_H */ diff --git a/projects/cli-test/mgmt-masterkey.c b/projects/cli-test/mgmt-masterkey.c new file mode 100644 index 0000000..811e15b --- /dev/null +++ b/projects/cli-test/mgmt-masterkey.c @@ -0,0 +1,225 @@ +/* + * mgmt-masterkey.c + * ---------------- + * Masterkey CLI functions. + * + * Copyright (c) 2016, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Rename both CMSIS HAL_OK and libhal HAL_OK to disambiguate */ +#define HAL_OK CMSIS_HAL_OK +#include "stm-init.h" +#include "stm-uart.h" +#include "mgmt-cli.h" +#include "mgmt-masterkey.h" + +#undef HAL_OK +#define LIBHAL_OK HAL_OK +#include <hal.h> +#warning Refactor so we do not need to include hal_internal here +#include <hal_internal.h> +#undef HAL_OK + +#include <stdlib.h> + +static char * _status2str(const hal_error_t status) +{ + switch (status) { + case LIBHAL_OK: + return (char *) "Set"; + case HAL_ERROR_MASTERKEY_NOT_SET: + return (char *) "Not set"; + default: + return (char *) "Unknown"; + } +} + +static int _parse_hex_groups(uint8_t *buf, size_t len, char *argv[], int argc) +{ + int i; + uint32_t *dst = (uint32_t *) buf; + uint32_t *end = (uint32_t *) buf + len - 1; + char *err_ptr = NULL; + + if (! argc) return 0; + + for (i = 0; i < argc; i++) { + if (dst >= end) return -1; + *dst++ = strtoul(argv[i], &err_ptr, 16); + if (*err_ptr) return -2; + } + + return 1; +} + +static int cmd_masterkey_status(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + hal_error_t status; + uint8_t buf[KEK_LENGTH] = {0}; + + command = command; + argv = argv; + argc = argc; + + cli_print(cli, "Status of master key:\n"); + + status = hal_mkm_volatile_read(NULL, 0); + cli_print(cli, " volatile: %s / %s", _status2str(status), hal_error_string(status)); + + status = hal_mkm_flash_read(NULL, 0); + cli_print(cli, " flash: %s / %s", _status2str(status), hal_error_string(status)); + + /* XXX Temporary gaping security hole while developing the master key functionality. + * REMOVE READ-OUT OF MASTER KEY. + */ + + status = hal_mkm_volatile_read(&buf[0], sizeof(buf)); + if (status == LIBHAL_OK || status == HAL_ERROR_MASTERKEY_NOT_SET) { + cli_print(cli, "\nVolatile read-out:\n"); + uart_send_hexdump(buf, 0, sizeof(buf) - 1); + cli_print(cli, "\n"); + } else { + cli_print(cli, "Failed reading from volatile memory: %s", hal_error_string(status)); + } + + status = hal_mkm_flash_read(&buf[0], sizeof(buf)); + if (status == LIBHAL_OK || status == HAL_ERROR_MASTERKEY_NOT_SET) { + cli_print(cli, "\nFlash read-out:\n"); + uart_send_hexdump(buf, 0, sizeof(buf) - 1); + cli_print(cli, "\n"); + } else { + cli_print(cli, "Failed reading from flash: %s", hal_error_string(status)); + } + + return CLI_OK; +} + +static int cmd_masterkey_set(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + uint8_t buf[KEK_LENGTH] = {0}; + hal_error_t err; + int i; + + command = command; + + if ((i = _parse_hex_groups(&buf[0], sizeof(buf), argv, argc)) != 1) { + cli_print(cli, "Failed parsing master key (%i)", i); + return CLI_OK; + } + + cli_print(cli, "Parsed key:\n"); + uart_send_hexdump(buf, 0, sizeof(buf) - 1); + cli_print(cli, "\n"); + + if ((err = hal_mkm_volatile_write(buf, sizeof(buf))) == LIBHAL_OK) { + cli_print(cli, "Master key set in volatile memory"); + } else { + cli_print(cli, "Failed writing key to volatile memory: %s", hal_error_string(err)); + } + return CLI_OK; +} + +static int cmd_masterkey_erase(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + hal_error_t err; + + command = command; + argv = argv; + argc = argc; + + if ((err = hal_mkm_volatile_erase(KEK_LENGTH)) == LIBHAL_OK) { + cli_print(cli, "Erased master key from volatile memory"); + } else { + cli_print(cli, "Failed erasing master key from volatile memory: %s", hal_error_string(err)); + } + return CLI_OK; +} + +static int cmd_masterkey_unsecure_set(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + uint8_t buf[KEK_LENGTH] = {0}; + hal_error_t err; + int i; + + command = command; + + if ((i = _parse_hex_groups(&buf[0], sizeof(buf), argv, argc)) != 1) { + cli_print(cli, "Failed parsing master key (%i)", i); + return CLI_OK; + } + + cli_print(cli, "Parsed key:\n"); + uart_send_hexdump(buf, 0, sizeof(buf) - 1); + cli_print(cli, "\n"); + + if ((err = hal_mkm_flash_write(buf, sizeof(buf))) == LIBHAL_OK) { + cli_print(cli, "Master key set in unsecure flash memory"); + } else { + cli_print(cli, "Failed writing key to unsecure flash memory: %s", hal_error_string(err)); + } + return CLI_OK; +} + +static int cmd_masterkey_unsecure_erase(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + hal_error_t err; + + command = command; + argv = argv; + argc = argc; + + if ((err = hal_mkm_flash_erase(KEK_LENGTH)) == LIBHAL_OK) { + cli_print(cli, "Erased unsecure master key from flash"); + } else { + cli_print(cli, "Failed erasing unsecure master key from flash: %s", hal_error_string(err)); + } + return CLI_OK; +} + +void configure_cli_masterkey(struct cli_def *cli) +{ + struct cli_command *c = cli_register_command(cli, NULL, "masterkey", NULL, 0, 0, NULL); + + /* masterkey status */ + cli_register_command(cli, c, "status", cmd_masterkey_status, 0, 0, "Show status of master key in RAM/flash"); + + /* masterkey set */ + cli_register_command(cli, c, "set", cmd_masterkey_set, 0, 0, "Set the master key in the volatile Master Key Memory"); + + /* masterkey erase */ + cli_register_command(cli, c, "erase", cmd_masterkey_erase, 0, 0, "Erase the master key from the volatile Master Key Memory"); + + struct cli_command *c_unsecure = cli_register_command(cli, c, "unsecure", NULL, 0, 0, NULL); + + /* masterkey unsecure set */ + cli_register_command(cli, c_unsecure, "set", cmd_masterkey_unsecure_set, 0, 0, "Set master key in unprotected flash memory (if unsure, DON'T)"); + + /* masterkey unsecure erase */ + cli_register_command(cli, c_unsecure, "erase", cmd_masterkey_unsecure_erase, 0, 0, "Erase master key from unprotected flash memory"); +} diff --git a/projects/cli-test/mgmt-masterkey.h b/projects/cli-test/mgmt-masterkey.h new file mode 100644 index 0000000..67835e9 --- /dev/null +++ b/projects/cli-test/mgmt-masterkey.h @@ -0,0 +1,42 @@ +/* + * mgmt-masterkey.h + * ----------- + * Management CLI masterkeyellaneous functions. + * + * Copyright (c) 2016, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __STM32_CLI_MGMT_MASTERKEY_H +#define __STM32_CLI_MGMT_MASTERKEY_H + +#include <libcli.h> + +extern void configure_cli_masterkey(struct cli_def *cli); + +#endif /* __STM32_CLI_MGMT_MASTERKEY_H */ diff --git a/projects/cli-test/mgmt-misc.c b/projects/cli-test/mgmt-misc.c index aea790a..ca95c63 100644 --- a/projects/cli-test/mgmt-misc.c +++ b/projects/cli-test/mgmt-misc.c @@ -3,7 +3,9 @@ * ----------- * Miscellaneous CLI functions. * - * Copyright (c) 2016, NORDUnet A/S All rights reserved. + * Copyright (c) 2016-2017, NORDUnet A/S All rights reserved. + * Copyright: 2020, The Commons Conservancy Cryptech Project + * SPDX-License-Identifier: BSD-3-Clause * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -15,9 +17,9 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * - Neither the name of the NORDUnet nor the names of its contributors may - * be used to endorse or promote products derived from this software - * without specific prior written permission. + * - Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED @@ -32,41 +34,44 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#define HAL_OK CMSIS_HAL_OK #include "stm-init.h" #include "stm-uart.h" - #include "mgmt-cli.h" #include "mgmt-misc.h" +#undef HAL_OK -#include <string.h> - - -extern uint32_t update_crc(uint32_t crc, uint8_t *buf, int len); +#define HAL_OK LIBHAL_OK +#include "hal.h" +#include "hal_internal.h" +#undef HAL_OK +#include <string.h> -volatile uint32_t demo_crc = 0; +static volatile hal_crc32_t demo_crc; -int _count_bytes_callback(uint8_t *buf, size_t len) { - demo_crc = update_crc(demo_crc, buf, len); - return 1; +static HAL_StatusTypeDef _count_bytes_callback(uint8_t *buf, size_t len) { + demo_crc = hal_crc32_update(demo_crc, buf, len); + return CMSIS_HAL_OK; } int cli_receive_data(struct cli_def *cli, uint8_t *buf, size_t len, cli_data_callback data_callback) { - uint32_t filesize = 0, crc = 0, my_crc = 0, counter = 0; + hal_crc32_t crc = 0, my_crc = hal_crc32_init(); + uint32_t filesize = 0, counter = 0; size_t n = len; if (! control_mgmt_uart_dma_rx(DMA_RX_STOP)) { cli_print(cli, "Failed stopping DMA"); - return CLI_OK; + goto fail; } cli_print(cli, "OK, write size (4 bytes), data in %li byte chunks, CRC-32 (4 bytes)", (uint32_t) n); - if (uart_receive_bytes(STM_UART_MGMT, (void *) &filesize, 4, 1000) != HAL_OK) { + if (uart_receive_bytes((void *) &filesize, sizeof(filesize), 1000) != CMSIS_HAL_OK) { cli_print(cli, "Receive timed out"); - return CLI_ERROR; + goto fail; } cli_print(cli, "Send %li bytes of data", filesize); @@ -79,27 +84,28 @@ int cli_receive_data(struct cli_def *cli, uint8_t *buf, size_t len, cli_data_cal if (filesize < n) n = filesize; - if (uart_receive_bytes(STM_UART_MGMT, (void *) buf, n, 1000) != HAL_OK) { + if (uart_receive_bytes((void *) buf, n, 1000) != CMSIS_HAL_OK) { cli_print(cli, "Receive timed out"); - return CLI_ERROR; + goto fail; } filesize -= n; - my_crc = update_crc(my_crc, buf, n); + my_crc = hal_crc32_update(my_crc, buf, n); /* After reception of a chunk but before ACKing we have "all" the time in the world to * calculate CRC and invoke the data_callback. */ - if (data_callback != NULL && ! data_callback(buf, (size_t) n)) { + if (data_callback != NULL && data_callback(buf, n) != CMSIS_HAL_OK) { cli_print(cli, "Data processing failed"); - return CLI_OK; + goto fail; } counter++; - uart_send_bytes(STM_UART_MGMT, (void *) &counter, 4); + uart_send_bytes((void *) &counter, 4); } + my_crc = hal_crc32_finalize(my_crc); cli_print(cli, "Send CRC-32"); - uart_receive_bytes(STM_UART_MGMT, (void *) &crc, 4, 1000); + uart_receive_bytes((void *) &crc, sizeof(crc), 1000); cli_print(cli, "CRC-32 0x%x, calculated CRC 0x%x", (unsigned int) crc, (unsigned int) my_crc); if (crc == my_crc) { cli_print(cli, "CRC checksum MATCHED"); @@ -107,31 +113,129 @@ int cli_receive_data(struct cli_def *cli, uint8_t *buf, size_t len, cli_data_cal cli_print(cli, "CRC checksum did NOT match"); } + fail: + control_mgmt_uart_dma_rx(DMA_RX_START); return CLI_OK; } -int cmd_filetransfer(struct cli_def *cli, const char *command, char *argv[], int argc) +static int cmd_filetransfer(struct cli_def *cli, const char *command, char *argv[], int argc) { uint8_t buf[FILETRANSFER_UPLOAD_CHUNK_SIZE]; - demo_crc = 0; + command = command; + argv = argv; + argc = argc; + + demo_crc = hal_crc32_init(); cli_receive_data(cli, &buf[0], sizeof(buf), _count_bytes_callback); + demo_crc = hal_crc32_finalize(demo_crc); cli_print(cli, "Demo CRC is: %li/0x%x", demo_crc, (unsigned int) demo_crc); return CLI_OK; } -int cmd_reboot(struct cli_def *cli, const char *command, char *argv[], int argc) +static int cmd_reboot(struct cli_def *cli, const char *command, char *argv[], int argc) { + command = command; + argv = argv; + argc = argc; + cli_print(cli, "\n\n\nRebooting\n\n\n"); HAL_NVIC_SystemReset(); - while (1) {}; + + /*NOTREACHED*/ + return CLI_OK; +} + +static int cmd_rsa_blinding(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + if (argc != 1) { + cli_print(cli, "Wrong number of arguments (%i).", argc); + cli_print(cli, "Syntax: %s <on|off|clear>", command); + return CLI_ERROR; + } + + if (strcmp(argv[0], "on") == 0) + hal_rsa_set_blinding(1); + else if (strcmp(argv[0], "off") == 0) + hal_rsa_set_blinding(0); + else if (strcmp(argv[0], "clear") == 0) + hal_rsa_clear_blinding_cache(); + else { + cli_print(cli, "Argument must be 'on', 'off', or 'clear' - not '%s'", argv[0]); + return CLI_ERROR; + } + + return CLI_OK; +} + +static int cmd_rsa_crt(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int onoff; + + if (argc != 1) { + cli_print(cli, "Wrong number of arguments (%i).", argc); + cli_print(cli, "Syntax: %s <on|off>", command); + return CLI_ERROR; + } + + if (strcmp(argv[0], "on") == 0) + onoff = 1; + else if (strcmp(argv[0], "off") == 0) + onoff = 0; + else { + cli_print(cli, "Argument must be 'on' or 'off' - not '%s'", argv[0]); + return CLI_ERROR; + } + + hal_rsa_set_crt(onoff); + + return CLI_OK; +} + +static int cmd_rsa_modexpng(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int onoff; + + if (argc != 1) { + cli_print(cli, "Wrong number of arguments (%i).", argc); + cli_print(cli, "Syntax: %s <on|off>", command); + return CLI_ERROR; + } + + if (strcmp(argv[0], "on") == 0) + onoff = 1; + else if (strcmp(argv[0], "off") == 0) + onoff = 0; + else { + cli_print(cli, "Argument must be 'on' or 'off' - not '%s'", argv[0]); + return CLI_ERROR; + } + + hal_error_t err; + if ((err = hal_modexp_use_modexpng(onoff)) == LIBHAL_OK) + return CLI_OK; + + cli_print(cli, hal_error_string(err)); + return CLI_ERROR; } void configure_cli_misc(struct cli_def *cli) { /* filetransfer */ - cli_command_root_node(filetransfer, "Test file transfering"); + cli_register_command(cli, NULL, "filetransfer", cmd_filetransfer, 0, 0, "Test file transfering"); + + struct cli_command *c_rsa = cli_register_command(cli, NULL, "rsa", NULL, 0, 0, NULL); + + /* rsa blinding */ + cli_register_command(cli, c_rsa, "blinding", cmd_rsa_blinding, 0, 0, "Set use of RSA blinding"); + + /* rsa crt */ + cli_register_command(cli, c_rsa, "crt", cmd_rsa_crt, 0, 0, "Set use of RSA CRT"); + + /* rsa modexpng */ + cli_register_command(cli, c_rsa, "modexpng", cmd_rsa_modexpng, 0, 0, "Set use of ModExpNG"); + /* reboot */ - cli_command_root_node(reboot, "Reboot the STM32"); + cli_register_command(cli, NULL, "reboot", cmd_reboot, 0, 0, "Reboot the STM32"); } diff --git a/projects/cli-test/mgmt-misc.h b/projects/cli-test/mgmt-misc.h index b7eb4f4..c0581c9 100644 --- a/projects/cli-test/mgmt-misc.h +++ b/projects/cli-test/mgmt-misc.h @@ -35,15 +35,14 @@ #ifndef __STM32_CLI_MGMT_MISC_H #define __STM32_CLI_MGMT_MISC_H -#include "stm-init.h" #include <libcli.h> - #define FILETRANSFER_UPLOAD_CHUNK_SIZE 256 -typedef int (*cli_data_callback)(uint8_t *, size_t); +typedef HAL_StatusTypeDef (*cli_data_callback)(uint8_t *, size_t); -extern void configure_cli_misc(struct cli_def *cli); extern int cli_receive_data(struct cli_def *cli, uint8_t *buf, size_t len, cli_data_callback data_callback); +extern void configure_cli_misc(struct cli_def *cli); + #endif /* __STM32_CLI_MGMT_MISC_H */ diff --git a/projects/cli-test/mgmt-show.c b/projects/cli-test/mgmt-show.c index 3ae196e..4338dcd 100644 --- a/projects/cli-test/mgmt-show.c +++ b/projects/cli-test/mgmt-show.c @@ -32,6 +32,8 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/* Rename both CMSIS HAL_OK and libhal HAL_OK to disambiguate */ +#define HAL_OK CMSIS_HAL_OK #include "stm-init.h" #include "stm-keystore.h" #include "stm-fpgacfg.h" @@ -40,13 +42,24 @@ #include "mgmt-cli.h" #include "mgmt-show.h" -#include <string.h> +#undef HAL_OK +#define LIBHAL_OK HAL_OK +#include "hal.h" + +#define HAL_STATIC_PKEY_STATE_BLOCKS 6 +#include "hal_internal.h" +#undef HAL_OK +#include <string.h> -int cmd_show_cpuspeed(struct cli_def *cli, const char *command, char *argv[], int argc) +static int cmd_show_cpuspeed(struct cli_def *cli, const char *command, char *argv[], int argc) { volatile uint32_t hclk; + command = command; + argv = argv; + argc = argc; + hclk = HAL_RCC_GetHCLKFreq(); cli_print(cli, "HSE_VALUE: %li", HSE_VALUE); cli_print(cli, "HCLK: %li (%i MHz)", hclk, (int) hclk / 1000 / 1000); @@ -54,36 +67,71 @@ int cmd_show_cpuspeed(struct cli_def *cli, const char *command, char *argv[], in return CLI_OK; } -int cmd_show_fpga_status(struct cli_def *cli, const char *command, char *argv[], int argc) +static int cmd_show_fpga_status(struct cli_def *cli, const char *command, char *argv[], int argc) { - cli_print(cli, "FPGA has %sloaded a bitstream", fpgacfg_check_done() ? "":"NOT "); + command = command; + argv = argv; + argc = argc; + + cli_print(cli, "FPGA has %sloaded a bitstream", (fpgacfg_check_done() == CMSIS_HAL_OK) ? "":"NOT "); return CLI_OK; } -int cmd_show_keystore_status(struct cli_def *cli, const char *command, char *argv[], int argc) +static int cmd_show_fpga_cores(struct cli_def *cli, const char *command, char *argv[], int argc) { - cli_print(cli, "Keystore memory is %sonline", (keystore_check_id() != 1) ? "NOT ":""); + hal_core_t *core; + const hal_core_info_t *info; + + command = command; + argv = argv; + argc = argc; + + if (fpgacfg_check_done() != CMSIS_HAL_OK) { + cli_print(cli, "FPGA has not loaded a bitstream"); + return CLI_OK; + } + + for (core = hal_core_iterate(NULL); core != NULL; core = hal_core_iterate(core)) { + info = hal_core_info(core); + cli_print(cli, "%04x: %8.8s %4.4s", + (unsigned int)info->base, info->name, info->version); + } + return CLI_OK; } -int cmd_show_keystore_data(struct cli_def *cli, const char *command, char *argv[], int argc) +static int cmd_show_keystore_status(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + command = command; + argv = argv; + argc = argc; + + cli_print(cli, "Keystore memory is %sonline", (keystore_check_id() == CMSIS_HAL_OK) ? "":"NOT "); + return CLI_OK; +} + +static int cmd_show_keystore_data(struct cli_def *cli, const char *command, char *argv[], int argc) { uint8_t buf[KEYSTORE_PAGE_SIZE]; uint32_t i; - if (keystore_check_id() != 1) { + command = command; + argv = argv; + argc = argc; + + if (keystore_check_id() != CMSIS_HAL_OK) { cli_print(cli, "ERROR: The keystore memory is not accessible."); } memset(buf, 0, sizeof(buf)); - if ((i = keystore_read_data(0, buf, sizeof(buf))) != 1) { + if ((i = keystore_read_data(0, buf, sizeof(buf))) != CMSIS_HAL_OK) { cli_print(cli, "Failed reading first page from keystore memory: %li", i); return CLI_ERROR; } cli_print(cli, "First page from keystore memory:\r\n"); - uart_send_hexdump(STM_UART_MGMT, buf, 0, sizeof(buf) - 1); - uart_send_string2(STM_UART_MGMT, (char *) "\r\n\r\n"); + uart_send_hexdump(buf, 0, sizeof(buf) - 1); + cli_print(cli, "\n"); for (i = 0; i < 8; i++) { if (buf[i] == 0xff) break; /* never written */ @@ -97,14 +145,14 @@ int cmd_show_keystore_data(struct cli_def *cli, const char *command, char *argv[ if (buf[i] == 0xff) { cli_print(cli, "Tombstoning byte %li", i); buf[i] = 0x55; - if ((i = keystore_write_data(0, buf, sizeof(buf))) != 1) { + if ((i = keystore_write_data(0, buf, sizeof(buf))) != CMSIS_HAL_OK) { cli_print(cli, "Failed writing data at offset 0: %li", i); return CLI_ERROR; } } } else { cli_print(cli, "Erasing first sector since all the first 8 bytes are tombstones"); - if ((i = keystore_erase_sectors(1)) != 1) { + if ((i = keystore_erase_sector(0)) != CMSIS_HAL_OK) { cli_print(cli, "Failed erasing the first sector: %li", i); return CLI_ERROR; } @@ -116,18 +164,24 @@ int cmd_show_keystore_data(struct cli_def *cli, const char *command, char *argv[ void configure_cli_show(struct cli_def *cli) { - /* show */ - cli_command_root(show); + struct cli_command *c = cli_register_command(cli, NULL, "show", NULL, 0, 0, NULL); /* show cpuspeed */ - cli_command_node(show, cpuspeed, "Show the speed at which the CPU currently operates"); + cli_register_command(cli, c, "cpuspeed", cmd_show_cpuspeed, 0, 0, "Show the speed at which the CPU currently operates"); + + struct cli_command *c_fpga = cli_register_command(cli, c, "fpga", NULL, 0, 0, NULL); - cli_command_branch(show, fpga); /* show fpga status*/ - cli_command_node(show_fpga, status, "Show status about the FPGA"); + cli_register_command(cli, c_fpga, "status", cmd_show_fpga_status, 0, 0, "Show status about the FPGA"); + + /* show fpga cores*/ + cli_register_command(cli, c_fpga, "cores", cmd_show_fpga_cores, 0, 0, "Show the currently available FPGA cores"); + + struct cli_command *c_keystore = cli_register_command(cli, c, "keystore", NULL, 0, 0, NULL); - cli_command_branch(show, keystore); /* show keystore status*/ - cli_command_node(show_keystore, status, "Show status of the keystore memory"); - cli_command_node(show_keystore, data, "Show the first page of the keystore memory"); + cli_register_command(cli, c_keystore, "status", cmd_show_keystore_status, 0, 0, "Show status of the keystore memory"); + + /* show keystore data */ + cli_register_command(cli, c_keystore, "data", cmd_show_keystore_data, 0, 0, "Show the first page of the keystore memory"); } diff --git a/projects/cli-test/mgmt-show.h b/projects/cli-test/mgmt-show.h index 0d7ba3a..7b80a30 100644 --- a/projects/cli-test/mgmt-show.h +++ b/projects/cli-test/mgmt-show.h @@ -1,5 +1,5 @@ /* - * mgmt-misc.h + * mgmt-show.h * ----------- * Management CLI 'show' functions. * @@ -35,7 +35,6 @@ #ifndef __STM32_CLI_MGMT_SHOW_H #define __STM32_CLI_MGMT_SHOW_H -#include "stm-init.h" #include <libcli.h> extern void configure_cli_show(struct cli_def *cli); diff --git a/projects/cli-test/mgmt-test.c b/projects/cli-test/mgmt-test.c index c1f255e..9b9972d 100644 --- a/projects/cli-test/mgmt-test.c +++ b/projects/cli-test/mgmt-test.c @@ -35,34 +35,31 @@ #include "stm-init.h" #include "stm-led.h" #include "stm-sdram.h" +#include "stm-fmc.h" +#include "stm-fpgacfg.h" #include "mgmt-cli.h" #include "mgmt-test.h" #include "test_sdram.h" +#include "test_mkmif.h" +#include "test-fmc.h" #include <stdlib.h> - -int cmd_test_sdram(struct cli_def *cli, const char *command, char *argv[], int argc) +static int cmd_test_sdram(struct cli_def *cli, const char *command, char *argv[], int argc) { // run external memory initialization sequence - HAL_StatusTypeDef status; int ok, num_cycles = 1, i, test_completed; + command = command; + if (argc == 1) { num_cycles = strtol(argv[0], NULL, 0); if (num_cycles > 100) num_cycles = 100; if (num_cycles < 1) num_cycles = 1; } - cli_print(cli, "Initializing SDRAM"); - status = sdram_init(); - if (status != HAL_OK) { - cli_print(cli, "Failed initializing SDRAM: %i", (int) status); - return CLI_OK; - } - for (i = 1; i <= num_cycles; i++) { cli_print(cli, "Starting SDRAM test (%i/%i)", i, num_cycles); test_completed = 0; @@ -107,11 +104,83 @@ int cmd_test_sdram(struct cli_def *cli, const char *command, char *argv[], int a return CLI_OK; } +static int cmd_test_fmc(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int i, num_cycles = 1, num_rounds = 100000; + + command = command; + + if (argc >= 1) { + num_cycles = strtol(argv[0], NULL, 0); + if (num_cycles > 100000) num_cycles = 100000; + if (num_cycles < 1) num_cycles = 1; + } + + if (argc == 2) { + num_rounds = strtol(argv[1], NULL, 0); + if (num_rounds > 1000000) num_rounds = 1000000; + if (num_rounds < 1) num_rounds = 1; + } + + cli_print(cli, "Checking if FPGA has loaded it's bitstream"); + // Blink blue LED until the FPGA reports it has loaded it's bitstream + led_on(LED_BLUE); + while (! fpgacfg_check_done()) { + for (i = 0; i < 4; i++) { + HAL_Delay(500); + led_toggle(LED_BLUE); + } + } + + // turn on green led, turn off other leds + led_on(LED_GREEN); + led_off(LED_YELLOW); + led_off(LED_RED); + led_off(LED_BLUE); + + // vars + volatile int data_test_ok = 0, addr_test_ok = 0, successful_runs = 0, failed_runs = 0, sleep = 0; + + for (i = 1; i <= num_cycles; i++) { + cli_print(cli, "Starting FMC test (%i/%i)", i, num_cycles); + + // test data bus + data_test_ok = test_fpga_data_bus(cli, num_rounds); + // test address bus + addr_test_ok = test_fpga_address_bus(cli, num_rounds); + + cli_print(cli, "Data: %i, addr %i", data_test_ok, addr_test_ok); + + if (data_test_ok == num_rounds && + addr_test_ok == num_rounds) { + // toggle yellow led to indicate, that we are alive + led_toggle(LED_YELLOW); + + successful_runs++; + sleep = 0; + } else { + led_on(LED_RED); + failed_runs++; + sleep = 2000; + } + + cli_print(cli, "Success %i, failed %i runs\r\n", successful_runs, failed_runs); + HAL_Delay(sleep); + } + + return CLI_OK; +} + void configure_cli_test(struct cli_def *cli) { - /* test */ - cli_command_root(test); + struct cli_command *c = cli_register_command(cli, NULL, "test", NULL, 0, 0, NULL); /* test sdram */ - cli_command_node(test, sdram, "Run SDRAM tests"); + cli_register_command(cli, c, "sdram", cmd_test_sdram, 0, 0, "Run SDRAM tests"); + + /* test mkmif */ + cli_register_command(cli, c, "mkmif", cmd_test_mkmif, 0, 0, "Run Master Key Memory Interface tests"); + + /* test fmc */ + cli_register_command(cli, c, "fmc", cmd_test_fmc, 0, 0, "Run FMC bus tests"); } diff --git a/projects/cli-test/test-fmc.c b/projects/cli-test/test-fmc.c new file mode 100644 index 0000000..d9b0c9b --- /dev/null +++ b/projects/cli-test/test-fmc.c @@ -0,0 +1,217 @@ +/* + * test-fmc.c + * ----------- + * FPGA communication bus (FMC) tests. + * + * Copyright (c) 2016, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + This requires a special bitstream with a special test register. + See core/platform/novena/fmc/rtl/novena_fmc_top.v, sections marked + `ifdef test: + //---------------------------------------------------------------- + // Dummy Register + // + // General-purpose register to test FMC interface using STM32 + // demo program instead of core selector logic. + // + // This register is a bit tricky, but it allows testing of both + // data and address buses. Reading from FPGA will always return + // value, which is currently stored in the test register, + // regardless of read transaction address. Writing to FPGA has + // two variants: a) writing to address 0 will store output data + // data value in the test register, b) writing to any non-zero + // address will store _address_ of write transaction in the test + // register. + // + // To test data bus, write some different patterns to address 0, + // then readback from any address and compare. + // + // To test address bus, write anything to some different non-zero + // addresses, then readback from any address and compare returned + // value with previously written address. + // + //---------------------------------------------------------------- + */ + +#include "stm-init.h" +#include "stm-fmc.h" +#include "stm-uart.h" + +#include "test-fmc.h" + +static RNG_HandleTypeDef rng_inst; + +/* These are some interesting-to-look-at-in-the-debugger values that are declared + * volatile so that the compiler wouldn't optimize/obscure them. + */ +volatile uint32_t data_diff = 0; +volatile uint32_t addr_diff = 0; + + +static int _write_then_read(struct cli_def *cli, uint32_t addr, uint32_t write_buf, uint32_t *read_buf) +{ + int ok; + + fmc_write_32(addr, write_buf); + fmc_read_32(0, read_buf); + + return 1; +} + +int test_fpga_data_bus(struct cli_def *cli, uint32_t test_rounds) +{ + int i, c; + uint32_t rnd, buf; + HAL_StatusTypeDef hal_result; + + /* initialize stm32 rng */ + rng_inst.Instance = RNG; + HAL_RNG_Init(&rng_inst); + + /* run some rounds of data bus test */ + for (c = 0; c < (int)test_rounds; c++) { + data_diff = 0; + /* try to generate "random" number */ + hal_result = HAL_RNG_GenerateRandomNumber(&rng_inst, &rnd); + if (hal_result != HAL_OK) { + cli_print(cli, "STM32 RNG failed"); + break; + } + + /* write value to fpga at address 0 and then read it back from the test register */ + if (! _write_then_read(cli, 0, rnd, &buf)) break; + + /* compare (abort testing in case of error) */ + data_diff = buf ^ rnd; + if (data_diff) { + cli_print(cli, "Data bus FAIL: expected %lx got %lx", rnd, buf); + uart_send_string((char *) "Binary diff: "); + uart_send_binary(data_diff, 32); + uart_send_string("\r\n"); + + break; + } + } + + if (! data_diff) { + cli_print(cli, "Sample of data bus test data: expected 0x%lx got 0x%lx", rnd, buf); + } else { + uint32_t data; + cli_print(cli, "\nFMC data bus per-bit analysis:"); + for (i = 0; i < 31; i++) { + data = 1 << i; + + if (! _write_then_read(cli, 0, data, &buf)) break; + + if (data == buf) { + cli_print(cli, "Data 0x%08lx (FMC_D%02i) - OK", data, i + 1); + } else { + cli_print(cli, "Data 0x%08lx (FMC_D%02i) - FAIL (read 0x%08lx)", data, i + 1, buf); + } + } + } + + + /* return number of successful tests */ + return c; +} + +int test_fpga_address_bus(struct cli_def *cli, uint32_t test_rounds) +{ + int i, c; + uint32_t addr, buf, dummy = 1; + HAL_StatusTypeDef hal_result; + + /* initialize stm32 rng */ + rng_inst.Instance = RNG; + HAL_RNG_Init(&rng_inst); + + /* run some rounds of address bus test */ + for (c = 0; c < (int)test_rounds; c++) { + addr_diff = 0; + /* try to generate "random" number */ + hal_result = HAL_RNG_GenerateRandomNumber(&rng_inst, &addr); + if (hal_result != HAL_OK) break; + + /* there are 26 physicaly connected address lines on the alpha, + but "only" 24 usable for now (the top two ones are used by FMC + to choose bank, and we only have one bank set up currently) + */ + addr &= 0x3fffffc; + + /* don't test zero addresses (fpga will store data, not address) */ + if (addr == 0) continue; + + /* write dummy value to fpga at some non-zero address and then read from the + test register to see what address the FPGA thought we wrote to + */ + if (! _write_then_read(cli, addr, dummy, &buf)) break; + + /* fpga receives address of 32-bit word, while we need + byte address here to compare + */ + buf <<= 2; + + /* compare (abort testing in case of error) */ + addr_diff = buf ^ addr; + if (addr_diff) { + cli_print(cli, "Address bus FAIL: expected 0x%lx got 0x%lx", addr, buf); + uart_send_string((char *) "Binary diff: "); + uart_send_binary(addr_diff, 32); + uart_send_string("\r\n"); + + break; + } + } + + if (! addr_diff) { + cli_print(cli, "Sample of address bus test data: expected 0x%lx got 0x%lx", addr, buf); + } else { + cli_print(cli, "\nFMC address bus per-bit analysis:"); + for (i = 0; i < 23; i++) { + uint32_t shifted_addr; + addr = 1 << i; + + shifted_addr = addr << 2; + + if (! _write_then_read(cli, shifted_addr, dummy, &buf)) break; + + if (addr == buf) { + cli_print(cli, "Address 0x%08lx (FMC_A%02i) - OK", addr, i + 1); + } else { + cli_print(cli, "Address 0x%08lx (FMC_A%02i) - FAIL (read 0x%08lx)", addr, i + 1, buf); + } + } + } + + /* return number of successful tests */ + return c; +} diff --git a/projects/cli-test/test-fmc.h b/projects/cli-test/test-fmc.h new file mode 100644 index 0000000..c49da48 --- /dev/null +++ b/projects/cli-test/test-fmc.h @@ -0,0 +1,43 @@ +/* + * test-fmc.h + * ------------ + * Prototypes and defines for testing the FMC bus comms with the FPGA. + * + * Copyright (c) 2016, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __STM32_CLI_TEST_FMC_H +#define __STM32_CLI_TEST_FMC_H + +#include "mgmt-cli.h" + +extern int test_fpga_data_bus(struct cli_def *cli, uint32_t test_rounds); +extern int test_fpga_address_bus(struct cli_def *cli, uint32_t test_rounds); + + +#endif /* __STM32_CLI_TEST_FMC_H */ diff --git a/projects/cli-test/test-mkmif.c b/projects/cli-test/test-mkmif.c new file mode 100644 index 0000000..cd71040 --- /dev/null +++ b/projects/cli-test/test-mkmif.c @@ -0,0 +1,166 @@ +/* + * Test Joachim's MKMIF core. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <assert.h> + +#include <sys/time.h> + +/* Rename both CMSIS HAL_OK and libhal HAL_OK to disambiguate */ +#define HAL_OK CMSIS_HAL_OK +#include "mgmt-cli.h" + +#undef HAL_OK +#define HAL_OK LIBHAL_OK +#include "hal.h" +#undef HAL_OK + + +#define SCLK_DIV 0x20 + +typedef union { + uint8_t byte[4]; + uint32_t word; +} byteword_t; + +static hal_error_t sclk_test(struct cli_def *cli, hal_core_t *core, const uint32_t divisor) +{ + uint32_t readback; + hal_error_t err; + + cli_print(cli, "Trying to adjust the clockspeed (divisor %x).", (unsigned int) divisor); + + if ((err = hal_mkmif_set_clockspeed(core, divisor)) != LIBHAL_OK) { + cli_print(cli, "hal_mkmif_set_clockspeed: %s", hal_error_string(err)); + return err; + } + if ((err = hal_mkmif_get_clockspeed(core, &readback)) != LIBHAL_OK) { + cli_print(cli, "hal_mkmif_get_clockspeed: %s", hal_error_string(err)); + return err; + } + if (readback != divisor) { + cli_print(cli, "expected %x, got %x", (unsigned int)divisor, (unsigned int)readback); + return HAL_ERROR_IO_UNEXPECTED; + } + return LIBHAL_OK; +} + +static hal_error_t init_test(struct cli_def *cli, hal_core_t *core) +{ + hal_error_t err; + + cli_print(cli, "Trying to init to the memory in continuous mode."); + + if ((err = hal_mkmif_init(core)) != LIBHAL_OK) { + cli_print(cli, "hal_mkmif_init: %s", hal_error_string(err)); + return err; + } + + return LIBHAL_OK; +} + +static hal_error_t write_test(struct cli_def *cli, hal_core_t *core) +{ + uint32_t write_data; + uint32_t write_address; + int i; + hal_error_t err; + + for (write_data = 0x01020304, write_address = 0, i = 0; + i < 0x10; + write_data += 0x01010101, write_address += 4, ++i) { + + cli_print(cli, "Trying to write 0x%08x to memory address 0x%08x.", + (unsigned int)write_data, (unsigned int)write_address); + + if ((err = hal_mkmif_write_word(core, write_address, write_data)) != LIBHAL_OK) { + cli_print(cli, "hal_mkmif_write: %s", hal_error_string(err)); + return err; + } + } + + return LIBHAL_OK; +} + +static hal_error_t read_test(struct cli_def *cli, hal_core_t *core) +{ + uint32_t read_data; + uint32_t read_address; + int i; + hal_error_t err; + + for (read_address = 0, i = 0; + i < 0x10; + read_address += 4, ++i) { + + cli_print(cli, "Trying to read from memory address 0x%08x.", (unsigned int)read_address); + + if ((err = hal_mkmif_read_word(core, read_address, &read_data)) != LIBHAL_OK) { + cli_print(cli, "hal_mkmif_read: %s", hal_error_string(err)); + return err; + } + cli_print(cli, "Data read: 0x%08x", (unsigned int)read_data); + } + + return LIBHAL_OK; +} + +static hal_error_t write_read_test(struct cli_def *cli, hal_core_t *core) +{ + uint32_t data; + uint32_t readback; + hal_error_t err; + + cli_print(cli, "Trying to write 0xdeadbeef to the memory and then read back."); + + data = 0xdeadbeef; + + if ((err = hal_mkmif_write_word(core, 0x00000000, data)) != LIBHAL_OK) { + cli_print(cli, "write error: %s", hal_error_string(err)); + return err; + } + + if ((err = hal_mkmif_read_word(core, 0x00000000, &readback)) != LIBHAL_OK) { + cli_print(cli, "read error: %s", hal_error_string(err)); + return err; + } + + if (readback != data) { + cli_print(cli, "read %08x, expected %08x", (unsigned int)readback, (unsigned int)data); + return HAL_ERROR_IO_UNEXPECTED; + } + + return LIBHAL_OK; +} + +int cmd_test_mkmif(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + hal_core_t *core = hal_core_find(MKMIF_NAME, NULL); + hal_error_t res; + + command = command; + argv = argv; + argc = argc; + + if (core == NULL) { + cli_print(cli, "MKMIF core not present, not testing."); + return HAL_ERROR_CORE_NOT_FOUND; + } + + res = + sclk_test(cli, core, SCLK_DIV) || + init_test(cli, core) || + write_read_test(cli, core) || + write_test(cli, core) || + read_test(cli, core); + + if (res != LIBHAL_OK) { + cli_print(cli, "\nTest FAILED"); + } + + return CLI_OK; +} diff --git a/projects/cli-test/test_mkmif.h b/projects/cli-test/test_mkmif.h new file mode 100644 index 0000000..d5f2f75 --- /dev/null +++ b/projects/cli-test/test_mkmif.h @@ -0,0 +1,40 @@ +/* + * test_mkmif.h + * ------------ + * Prototypes and defines for testing the master key memory interface. + * + * Copyright (c) 2016, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __STM32_CLI_TEST_MKMIF_H +#define __STM32_CLI_TEST_MKMIF_H + +extern int cmd_test_mkmif(struct cli_def *cli, const char *command, char *argv[], int argc); + + +#endif /* __STM32_CLI_TEST_MKMIF_H */ diff --git a/projects/cli-test/test_sdram.c b/projects/cli-test/test_sdram.c index e720667..4961b94 100644 --- a/projects/cli-test/test_sdram.c +++ b/projects/cli-test/test_sdram.c @@ -31,7 +31,6 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include "stm32f4xx_hal.h"
#include "stm-led.h"
#include "stm-sdram.h"
#include "test_sdram.h"
diff --git a/projects/hsm/Makefile b/projects/hsm/Makefile index f5546d8..7fd3ad6 100644 --- a/projects/hsm/Makefile +++ b/projects/hsm/Makefile @@ -1,19 +1,53 @@ PROJ = hsm -SRCS = main.c +# objs in addition to $(PROJ).o +OBJS = mgmt-cli.o \ + mgmt-firmware.o \ + mgmt-bootloader.o \ + mgmt-fpga.o \ + mgmt-keystore.o \ + mgmt-masterkey.o \ + mgmt-misc.o \ + mgmt-task.o \ + log.o \ + $(TOPLEVEL)/task.o -OBJS = $(SRCS:.c=.o) +CFLAGS += -DNUM_RPC_TASK=8 -CFLAGS += -I $(LIBHAL_DIR) +CFLAGS += -I$(LIBHAL_SRC) +CFLAGS += -I$(LIBCLI_SRC) +CFLAGS += -I$(LIBTFM_BLD) +CFLAGS += -Wno-missing-field-initializers -LIBS += $(LIBHAL_DIR)/libhal.a $(LIBTFM_DIR)/libtfm.a +LIBS += $(LIBHAL_BLD)/libhal.a $(LIBTFM_BLD)/libtfm.a +LIBS += $(LIBCLI_BLD)/libcli.a + +LDFLAGS += -mcpu=cortex-m4 -mthumb -mlittle-endian -mthumb-interwork +LDFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16 +LDFLAGS += -Wl,--gc-sections + +ifdef DO_PROFILING +LDFLAGS += --specs=rdimon.specs -lc -lrdimon +endif + +ifdef DO_TASK_METRICS +CFLAGS += -DDO_TASK_METRICS +endif + +ifdef DO_TIMING +CFLAGS += -DDO_TIMING +CFLAGS += -I../cli-test +CFLAGS += -DCLI_STACK_SIZE=65536 +OBJS += ../cli-test/mgmt-timing.o $(TOPLEVEL)/stm-dwt.o +LDFLAGS += -lm +endif all: $(PROJ:=.elf) -$(PROJ).elf: $(OBJS) $(BOARD_OBJS) $(LIBS) - $(CC) $(CFLAGS) $^ -o $@ -T$(LDSCRIPT) -g -Wl,-Map=$(PROJ).map - $(OBJCOPY) -O binary $(PROJ).elf $(PROJ).bin - $(SIZE) $(PROJ).elf +%.elf: %.o $(BOARD_OBJS) $(OBJS) $(LIBS) + $(CC) $^ -o $@ -T$(LDSCRIPT) -g -Wl,-Map=$*.map $(LDFLAGS) + $(OBJCOPY) -O binary $*.elf $*.bin + $(SIZE) $*.elf clean: rm -f *.o diff --git a/projects/hsm/cryptech_miniterm b/projects/hsm/cryptech_miniterm new file mode 100755 index 0000000..b646811 --- /dev/null +++ b/projects/hsm/cryptech_miniterm @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2016, NORDUnet A/S All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# - Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# - Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# - Neither the name of the NORDUnet nor the names of its contributors may +# be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +Utility to run PySerial's "miniterm" with default settings suitable +for talking to the Cryptech Alpha's console port. +""" + +import serial.tools.miniterm +import sys +import os + +default_port = os.getenv("CRYPTECH_CTY_CLIENT_SERIAL_DEVICE") +default_baud = os.getenv("CRYPTECH_CTY_CLIENT_SERIAL_SPEED", 921600) + +sys.exit(serial.tools.miniterm.main(default_port = default_port, + default_baudrate = int(default_baud))) + diff --git a/projects/hsm/cryptech_probe b/projects/hsm/cryptech_probe new file mode 100755 index 0000000..356931a --- /dev/null +++ b/projects/hsm/cryptech_probe @@ -0,0 +1,158 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2016, NORDUnet A/S All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# - Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# - Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# - Neither the name of the NORDUnet nor the names of its contributors may +# be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +Utility to probe USB serial port(s) trying to figure out which one(s) +we have plugged in today. stdout is environment variable settings, +suitable for use in bash with "eval `cryptech_probe`"; all other output +goes to stderr. +""" + +import sys +import time +import argparse +import serial.tools.list_ports_posix + +if sys.version_info.major == 2: + def colon_hex(raw): + return ":".join("{:02x}".format(ord(b)) for b in raw) +else: + def colon_hex(raw): + return ":".join("{:02x}".format(b) for b in raw) + +class positive_integer(int): + def __init__(self, value): + if self <= 0: + raise ValueError + +parser = argparse.ArgumentParser(formatter_class = argparse.ArgumentDefaultsHelpFormatter) +parser.add_argument("-v", "--verbose", action = "store_true", help = "produce human-readable output") +parser.add_argument("-d", "--debug", action = "store_true", help = "blather about what we're doing") +parser.add_argument("--no-cleanup", action = "store_true", help = "don't send cleanup sequences after probing") +parser.add_argument("--read-buffer-size", type = positive_integer, help = "size of read buffer", default = 1024) +args = parser.parse_args() + +SLIP_END = b"\300" # Indicates end of SLIP packet +SLIP_ESC = b"\333" # Indicates byte stuffing +SLIP_ESC_END = b"\334" # ESC ESC_END means END data byte +SLIP_ESC_ESC = b"\335" # ESC ESC_ESC means ESC data byte + +Control_U = b"\025" # Console: clear line +Control_M = b"\015" # Console: end of line + +RPC_query = b"\0" * 8 # client_handle = 0, function code = RPC_FUNC_GET_VERSION +RPC_reply = b"\0" * 12 # opcode = RPC_FUNC_GET_VERSION, client_handle = 0, valret = HAL_OK + +# This is the query string we send to each USB port we find. It's +# intended to be relatively harmless, at least for either of the HSM +# ports: the final Control-U should prevent the console from trying to +# interpret the RPC command, and the SLIP_END markers should cause +# the RPC server to treat the ASCII control characters as noise. +# +# Yes, this is a total kludge. Useful identifiers for the USB ports +# are are on the wish list for a future revision of the hardware, but +# for the moment, we do what we can with what we have. + +probe_string = SLIP_END + Control_U + SLIP_END + RPC_query + SLIP_END + Control_U + Control_M + +ports = [port for port, desc, hwid in serial.tools.list_ports_posix.comports() + if "VID:PID=0403:6014" in hwid] + +if not ports: + sys.exit("Couldn't find any likely USB ports") + +if args.debug: + sys.stderr.write("Candidate USB ports: {}\n".format(", ".join(ports))) + +env = {} + +for port in ports: + + while True: + try: + tty = serial.Serial(port, 921600, timeout=0.1) + break + except serial.SerialException: + time.sleep(0.2) + + # Not sure we really need to dribble the probe string out this slowly anymore, + # but once upon a time we did this for a reason and it's not like this program + # is a performance bottleneck, so stick with the safe version. + + for i in range(len(probe_string)): + tty.write(probe_string[i:i+1]) + time.sleep(0.1) + + response = tty.read(args.read_buffer_size) + if args.debug: + sys.stderr.write("Received from {}: {!r} ({})\n".format(port, response, colon_hex(response))) + + # Check whether we got a known console prompt. + + is_cty = any(prompt in response for prompt in (b"Username:", b"Password:", b"cryptech>")) + + # Check whether we got something that looks like the response to an RPC version query. + # We skip over the version value itself, as it might change, but we check that it's + # terminated properly. This is fragile, and will need to handle SLIP decoding if + # we ever bump one of the version fields up into the range where the SLIP control + # characters live, but it will do for the moment. + + try: + is_hsm = response[response.index(SLIP_END + RPC_reply) + len(SLIP_END + RPC_reply) + 4] == SLIP_END[0] + except ValueError: + is_hsm = False + except IndexError: + is_hsm = False + + if is_cty and args.verbose: + sys.stderr.write("{} looks like the Cryptech HSM console port\n".format(port)) + + if is_hsm and args.verbose: + sys.stderr.write("{} looks like the Cryptech HSM RPC port\n".format(port)) + + if is_cty: + env.update(CRYPTECH_CTY_CLIENT_SERIAL_DEVICE = port) + + if is_hsm: + env.update(CRYPTECH_RPC_CLIENT_SERIAL_DEVICE = port) + + if (is_cty or is_hsm) and not args.no_cleanup: + if is_cty: + tty.write(Control_U) + if is_hsm: + tty.write(SLIP_END) + while tty.read(args.read_buffer_size): + pass + + tty.close() + +if env: + sys.stdout.write("export {}\n".format( + " ".join("{}='{}'".format(var, env[var]) for var in sorted(env)))) diff --git a/projects/hsm/cryptech_upload b/projects/hsm/cryptech_upload new file mode 100755 index 0000000..b40427d --- /dev/null +++ b/projects/hsm/cryptech_upload @@ -0,0 +1,385 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2016-2017, NORDUnet A/S All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# - Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# - Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# - Neither the name of the NORDUnet nor the names of its contributors may +# be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +Utility to upload a new firmware image or FPGA bitstream. +""" + +import os +import sys +import time +import struct +import serial +import socket +import getpass +import os.path +import tarfile +import argparse +import platform + +from binascii import crc32, hexlify + +FIRMWARE_CHUNK_SIZE = 4096 +FPGA_CHUNK_SIZE = 4096 + + +def parse_args(): + """ + Parse the command line arguments + """ + + share_directory = "/usr/share" if platform.system() == "Linux" else "/usr/local/share" + + default_tarball = os.path.join(share_directory, "cryptech-alpha-firmware.tar.gz") + + if not os.path.exists(default_tarball): + default_tarball = None + + parser = argparse.ArgumentParser(description = __doc__, + formatter_class = argparse.ArgumentDefaultsHelpFormatter, + ) + + parser.add_argument("-d", "--device", + default = os.getenv("CRYPTECH_CTY_CLIENT_SERIAL_DEVICE", "/dev/ttyUSB0"), + help = "Name of management port USB serial device", + ) + + parser.add_argument("--socket", + default = os.getenv("CRYPTECH_CTY_CLIENT_SOCKET_NAME", + "/tmp/.cryptech_muxd.cty"), + help = "Name of cryptech_muxd management port socket", + ) + + parser.add_argument("--firmware-tarball", + type = argparse.FileType("rb"), + default = default_tarball, + help = "Location of firmware tarball", + ) + + parser.add_argument("--username", + choices = ("so", "wheel"), + default = "so", + help = "Username to use when logging into the HSM", + ) + + parser.add_argument("--pin", + help = "PIN to use when logging into the HSM", + ) + + parser.add_argument("--separate-pins", + action = "store_true", + help = "Prompt separately for each PIN required during firmware upload") + + actions = parser.add_mutually_exclusive_group(required = True) + actions.add_argument("--fpga", + action = "store_true", + help = "Upload FPGA bitstream", + ) + actions.add_argument("--firmware", "--hsm", + action = "store_true", + help = "Upload HSM firmware image", + ) + actions.add_argument("--bootloader", + action = "store_true", + help = "Upload bootloader image (dangerous!)", + ) + + parser.add_argument("--simon-says-whack-my-bootloader", + action = "store_true", + help = "Confirm that you really want to risk bricking the HSM", + ) + + parser.add_argument("-i", "--explicit-image", + type = argparse.FileType("rb"), + help = "Explicit source image file for upload, overrides firmware tarball") + + parser.add_argument("--debug", + action = "store_true", + help = "Enable debugging of upload protocol", + ) + + parser.add_argument("-q", "--quiet", + action = "store_true", + help = "Only report errors", + ) + + return parser.parse_args() + + +class ManagementPortAbstract(object): + """ + Abstract class encapsulating actions on the HSM management port. + """ + + def __init__(self, args): + self.args = args + + def write(self, data): + numeric = isinstance(data, int) + if numeric: + data = struct.pack("<I", data) + self.send(data) + if self.args.debug: + if numeric: + print("Wrote 0x{}".format(hexlify(data).decode("ascii"))) + else: + print("Wrote {!r}".format(data)) + + def read(self): + res = b"" + x = self.recv() + while not x: + x = self.recv() + while x: + res += x + x = self.recv() + if self.args.debug: + print("Read {!r}".format(res)) + return res + + def execute(self, cmd): + self.write(b"\r") + prompt = self.read() + #if prompt.endswith("This is the bootloader speaking..."): + # prompt = self.read() + if prompt.endswith(b"Username: "): + self.write(self.args.username.encode("ascii") + b"\r") + prompt = self.read() + if prompt.endswith(b"Password: "): + if not self.args.pin or self.args.separate_pins: + self.args.pin = getpass.getpass("{} PIN: ".format(self.args.username)) + self.write(self.args.pin.encode("ascii") + b"\r") + prompt = self.read() + if not prompt.endswith((b"> ", b"# ")): + print("Device does not seem to be ready for a file transfer (got {!r})".format(prompt)) + return prompt + self.write(cmd + b"\r") + response = self.read() + return response + + +class ManagementPortSerial(ManagementPortAbstract): + """ + Implmentation of HSM management port abstraction over a direct + serial connection. + """ + + def __init__(self, args, timeout = 1): + super(ManagementPortSerial, self).__init__(args) + self.serial = serial.Serial(args.device, 921600, timeout = timeout) + + def send(self, data): + self.serial.write(data) + self.serial.flush() + + def recv(self): + return self.serial.read(1) + + def set_timeout(self, timeout): + self.serial.timeout = timeout + + def close(self): + self.serial.close() + +class ManagementPortSocket(ManagementPortAbstract): + """ + Implmentation of HSM management port abstraction over a PF_UNIX + socket connection to the cryptech_muxd management socket. + """ + + def __init__(self, args, timeout = 1): + super(ManagementPortSocket, self).__init__(args) + self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self.socket.connect(args.socket) + self.socket.settimeout(timeout) + + def send(self, data): + self.socket.sendall(data) + + def recv(self): + try: + return self.socket.recv(1) + except socket.timeout: + return b"" + + def set_timeout(self, timeout): + self.socket.settimeout(timeout) + + def close(self): + self.socket.close() + + +def send_file(src, size, args, dst): + """ + Upload an image from some file-like source to the management port. + Details depend on what kind of image it is. + """ + + if args.fpga: + chunk_size = FPGA_CHUNK_SIZE + response = dst.execute(b"fpga bitstream upload") + elif args.firmware: + chunk_size = FIRMWARE_CHUNK_SIZE + response = dst.execute(b"firmware upload") + if b"Rebooting" in response: + response = dst.execute(b"firmware upload") + elif args.bootloader: + chunk_size = FIRMWARE_CHUNK_SIZE + response = dst.execute(b"bootloader upload") + if b"Access denied" in response: + print("Access denied") + return False + if not b"OK" in response: + print("Device did not accept the upload command (got {!r})".format(response)) + return False + + dst.set_timeout(0.001) + crc = 0 + counter = 0 + # 1. Write size of file (4 bytes) + dst.write(struct.pack("<I", size)) + response = dst.read() + if not response.startswith(b"Send "): + print(response) + return False + + # 2. Write file contents while calculating CRC-32 + chunks = int((size + chunk_size - 1) / chunk_size) + for counter in range(chunks): + data = src.read(chunk_size) + dst.write(data) + if not args.quiet: + print("Wrote {!s} bytes (chunk {!s}/{!s})".format(len(data), counter + 1, chunks)) + # read ACK (a counter of number of 4k chunks received) + ack_bytes = b"" + while len(ack_bytes) < 4: + ack_bytes += dst.read() + ack = struct.unpack("<I", ack_bytes[:4])[0] + if ack != counter + 1: + print("ERROR: Did not receive the expected counter as ACK (got {!r}/{!r}, not {!r})".format(ack, ack_bytes, counter)) + return False + counter += 1 + + crc = crc32(data, crc) & 0xffffffff + + # 3. Write CRC-32 (4 bytes) + dst.write(struct.pack("<I", crc)) + response = dst.read() + if not args.quiet: + print(response) + + src.close() + + if args.fpga: + # tell the fpga to read its new configuration + dst.execute(b"fpga reset") + # log out of the CLI + # (firmware/bootloader upgrades reboot, don't need an exit) + dst.execute(b"exit") + + return True + + +dire_bootloader_warning = ''' + WARNING + +Updating the bootloader risks bricking your HSM! If something goes +badly wrong here, or you upload a bad bootloader image, you will not +be able to recover without an ST-LINK programmer. + +In most cases a normal "--firmware" upgrade should be all that is +necessary to bring your HSM up to date, there is seldom any real need +to update the bootloader. + +Do not proceed with this unless you REALLY know what you are doing. + +If you got here by accident, ^C now, without answering the PIN prompt. +''' + + +def main(): + global args + args = parse_args() + + + if args.bootloader: + if not args.simon_says_whack_my_bootloader: + sys.exit("You didn't say \"Simon says\"") + print(dire_bootloader_warning) + args.pin = None + + if args.explicit_image is None and args.firmware_tarball is None: + sys.exit("No source file specified for upload and firmware tarball not found") + + if args.explicit_image: + src = args.explicit_image # file-like object, thanks to argparse + size = os.fstat(src.fileno()).st_size + if size == 0: # Flashing from stdin won't work, sorry + sys.exit("Can't flash from a pipe or zero-length file") + if not args.quiet: + print("Uploading from explicitly-specified file {}".format(args.explicit_image.name)) + + else: + tar = tarfile.open(fileobj = args.firmware_tarball) + if not args.quiet: + print("Firmware tarball {} content:".format(args.firmware_tarball.name)) + tar.list(True) + if args.fpga: + name = "alpha_fmc.bit" + elif args.firmware: + name = "hsm.bin" + elif args.bootloader: + name = "bootloader.bin" + else: + # Somebody updated other part of this script without updating this part :( + sys.exit("Don't know which component to select from firmware tarball, sorry") + try: + size = tar.getmember(name).size + except KeyError: + sys.exit("Expected component {} missing from firmware tarball {}".format(name, args.firmware_tarball.name)) + src = tar.extractfile(name) + if not args.quiet: + print("Uploading {} from {}".format(name, args.firmware_tarball.name)) + + if not args.quiet: + print("Initializing management port and synchronizing with HSM, this may take a few seconds") + try: + dst = ManagementPortSocket(args, timeout = 1) + except socket.error as e: + dst = ManagementPortSerial(args, timeout = 1) + send_file(src, size, args, dst) + dst.close() + + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + pass diff --git a/projects/hsm/hsm.c b/projects/hsm/hsm.c new file mode 100644 index 0000000..52157c9 --- /dev/null +++ b/projects/hsm/hsm.c @@ -0,0 +1,522 @@ +/* + * hsm.c + * ---------------- + * Main module for the HSM project. + * + * Copyright (c) 2016-2017, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This is the main RPC server module. At the moment, it has a single + * worker thread to handle RPC requests, while the main thread handles CLI + * activity. The design allows for multiple worker threads to handle + * concurrent RPC requests from multiple clients (muxed through a daemon + * on the host). + */ + +#include <string.h> + +/* Rename both CMSIS HAL_OK and libhal HAL_OK to disambiguate */ +#define HAL_OK CMSIS_HAL_OK +#include "stm-init.h" +#include "stm-led.h" +#include "stm-fmc.h" +#include "stm-uart.h" +#include "stm-sdram.h" +#include "task.h" + +#include "mgmt-cli.h" + +#undef HAL_OK +#define HAL_OK LIBHAL_OK +#include "hal.h" +#include "hal_internal.h" +#include "slip_internal.h" +#include "xdr_internal.h" +#undef HAL_OK + +#ifndef NUM_RPC_TASK +#define NUM_RPC_TASK 1 +#elif NUM_RPC_TASK < 1 || NUM_RPC_TASK > 10 +#error invalid NUM_RPC_TASK +#endif + +#ifndef TASK_STACK_SIZE +/* Define an absurdly large task stack, because some pkey operation use a + * lot of stack variables. This has to go in SDRAM, because it exceeds the + * total RAM on the ARM. + */ +#define TASK_STACK_SIZE 200*1024 +#endif + +/* Stack for the busy task. This doesn't need to be very big. + */ +#ifndef BUSY_STACK_SIZE +#define BUSY_STACK_SIZE 1*1024 +#endif +static uint8_t busy_stack[BUSY_STACK_SIZE]; + +/* Stack for the CLI task. This needs to be big enough to accept a + * 4096-byte block of an FPGA or bootloader image upload. + */ +#ifndef CLI_STACK_SIZE +#define CLI_STACK_SIZE 16*1024 +#endif + +/* RPC buffers. For each active request, there will be two - input and output. + */ +typedef struct rpc_buffer_s { + size_t len; + uint8_t buf[HAL_RPC_MAX_PKT_SIZE]; + struct rpc_buffer_s *next; /* for ibuf queue linking */ +} rpc_buffer_t; + +/* RPC input (requst) buffers */ +static rpc_buffer_t *ibufs; + +/* ibuf queue structure */ +typedef struct { + rpc_buffer_t *head, *tail; + size_t len, max; /* for reporting */ +} ibufq_t; + +/* ibuf queues. These correspond roughly to task states - 'waiting' is for + * unallocated ibufs, while 'ready' is for requests that are ready to be + * processed. + */ +static ibufq_t ibuf_waiting, ibuf_ready; + +/* Get an ibuf from a queue. */ +static rpc_buffer_t *ibuf_get(ibufq_t *q) +{ + hal_critical_section_start(); + rpc_buffer_t *ibuf = q->head; + if (ibuf) { + q->head = ibuf->next; + if (q->head == NULL) + q->tail = NULL; + ibuf->next = NULL; + --q->len; + } + hal_critical_section_end(); + return ibuf; +} + +/* Put an ibuf on a queue. */ +static void ibuf_put(ibufq_t *q, rpc_buffer_t *ibuf) +{ + hal_critical_section_start(); + if (q->tail) + q->tail->next = ibuf; + else + q->head = ibuf; + q->tail = ibuf; + ibuf->next = NULL; + if (++q->len > q->max) + q->max = q->len; + hal_critical_section_end(); +} + +/* Get the current length of the 'ready' queue, for reporting in the CLI. */ +size_t request_queue_len(void) +{ + size_t n; + + hal_critical_section_start(); + n = ibuf_ready.len; + hal_critical_section_end(); + + return n; +} + +/* Get the maximum length of the 'ready' queue, for reporting in the CLI. */ +size_t request_queue_max(void) +{ + size_t n; + + hal_critical_section_start(); + n = ibuf_ready.max; + hal_critical_section_end(); + + return n; +} + +static void dispatch_task(void); +static void busy_task(void); +static tcb_t *busy_tcb; + +/* Select an available dispatch task. For simplicity, this doesn't try to + * allocate tasks in a round-robin fashion, so the lowest-numbered task + * will see the most action. OTOH, this lets us gauge the level of system + * activity in the CLI's 'task show' command. + */ +static tcb_t *task_next_waiting(void) +{ + for (tcb_t *t = task_iterate(NULL); t; t = task_iterate(t)) { + if (task_get_func(t) == dispatch_task && + task_get_state(t) == TASK_WAITING) + return t; + } + return NULL; +} + +static uint8_t *sdram_malloc(size_t size); + +/* Callback for HAL_UART_Receive_DMA(). + */ +static void RxCallback(uint8_t c) +{ + int complete; + static rpc_buffer_t *ibuf = NULL; + + /* If we couldn't previously get an ibuf, a task may have freed one up + * in the meantime. Otherwise, allocate one from SDRAM. In normal + * operation, the number of ibufs will expand to the number of remote + * clients (which we don't know and can't predict). It would take an + * active attempt to DOS the system to exhaust SDRAM, and there are + * easier ways to attack the device (don't release hash or pkey handles). + */ + if (ibuf == NULL) { + ibuf = ibuf_get(&ibuf_waiting); + if (ibuf == NULL) { + ibuf = (rpc_buffer_t *)sdram_malloc(sizeof(rpc_buffer_t)); + if (ibuf == NULL) + Error_Handler(); + } + ibuf->len = 0; + } + + /* Process this character into the ibuf. */ + if (hal_slip_process_char(c, ibuf->buf, &ibuf->len, sizeof(ibuf->buf), &complete) != LIBHAL_OK) + Error_Handler(); + + if (complete) { + /* Add the ibuf to the request queue, and try to get another ibuf. + */ + ibuf_put(&ibuf_ready, ibuf); + ibuf = ibuf_get(&ibuf_waiting); + if (ibuf != NULL) + ibuf->len = 0; + /* else all ibufs are busy, try again next time */ + + /* Wake a dispatch task to deal with this request, or wake the + * busy task to re-try scheduling a dispatch task. + */ + tcb_t *t = task_next_waiting(); + if (t) + task_wake(t); + else + task_wake(busy_tcb); + } +} + +/* A ring buffer for the UART DMA receiver. In theory, it should get at most + * 92 characters per 1ms tick, but we're going to up-size it for safety. + */ +#ifndef RPC_UART_RECVBUF_SIZE +#define RPC_UART_RECVBUF_SIZE 1024 /* must be a power of 2 */ +#endif +#define RPC_UART_RECVBUF_MASK (RPC_UART_RECVBUF_SIZE - 1) + +typedef struct { + uint32_t ridx; + uint8_t buf[RPC_UART_RECVBUF_SIZE]; +} uart_ringbuf_t; + +volatile uart_ringbuf_t uart_ringbuf = {0, {0}}; + +#define RINGBUF_RIDX(rb) (rb.ridx & RPC_UART_RECVBUF_MASK) +#define RINGBUF_WIDX(rb) (sizeof(rb.buf) - __HAL_DMA_GET_COUNTER(huart_user.hdmarx)) +#define RINGBUF_COUNT(rb) ((RINGBUF_WIDX(rb) - RINGBUF_RIDX(rb)) & RPC_UART_RECVBUF_MASK) +#define RINGBUF_READ(rb, dst) {dst = rb.buf[RINGBUF_RIDX(rb)]; rb.ridx++;} + +size_t uart_rx_max = 0; + +void HAL_SYSTICK_Callback(void) +{ +#ifdef DO_PROFILING + extern void profil_callback(void); + profil_callback(); +#endif + + size_t count = RINGBUF_COUNT(uart_ringbuf); + if (uart_rx_max < count) uart_rx_max = count; + + while (RINGBUF_COUNT(uart_ringbuf)) { + uint8_t c; + RINGBUF_READ(uart_ringbuf, c); + RxCallback(c); + } +} + +/* Send one character over the UART. This is called from + * hal_slip_send_char(). + */ +hal_error_t hal_serial_send_char(uint8_t c) +{ + return (uart_send_char2(STM_UART_USER, c) == 0) ? LIBHAL_OK : HAL_ERROR_RPC_TRANSPORT; +} + +/* Task entry point for the RPC request handler. + */ +static void dispatch_task(void) +{ + rpc_buffer_t obuf_s, *obuf = &obuf_s; + + while (1) { + /* Wait for a complete RPC request */ + task_sleep(); + + rpc_buffer_t *ibuf = ibuf_get(&ibuf_ready); + if (ibuf == NULL) + /* probably an error, but go back to sleep */ + continue; + + memset(obuf, 0, sizeof(*obuf)); + obuf->len = sizeof(obuf->buf); + + /* Process the request */ + hal_error_t ret = hal_rpc_server_dispatch(ibuf->buf, ibuf->len, obuf->buf, &obuf->len); + ibuf_put(&ibuf_waiting, ibuf); + if (ret == LIBHAL_OK) { + /* Send the response */ + if (hal_rpc_sendto(obuf->buf, obuf->len, NULL) != LIBHAL_OK) + Error_Handler(); + } + /* Else hal_rpc_server_dispatch failed with an XDR error, which + * probably means the request packet was garbage. In any case, we + * have nothing to transmit. + */ + } +} + +/* Task entry point for the task-rescheduling task. + */ +static void busy_task(void) +{ + while (1) { + /* Wake as many tasks as we have requests. + */ + size_t n; + for (n = request_queue_len(); n > 0; --n) { + tcb_t *t; + if ((t = task_next_waiting()) != NULL) + task_wake(t); + else + break; + } + if (n == 0) + /* flushed the queue, our work here is done */ + task_sleep(); + else + /* more work to do, try again after some tasks have run */ + task_yield(); + } +} + +#include "stm-fpgacfg.h" + +static void hashsig_restart_task(void) +{ + /* wait for the fpga to configure itself on cold-boot */ + while (fpgacfg_check_done() != CMSIS_HAL_OK) + task_yield(); + + /* reinitialize the hashsig key structures after a device restart */ + hal_hashsig_ks_init(); + + /* done, convert this task to an RPC handler */ + task_mod((char *)task_get_cookie(NULL), dispatch_task, NULL); +} + +/* end of variables declared with __attribute__((section(".sdram1"))) */ +extern uint8_t _esdram1 __asm ("_esdram1"); +/* end of SDRAM1 section */ +extern uint8_t __end_sdram1 __asm ("__end_sdram1"); +static uint8_t *sdram_heap = &_esdram1; + +/* Allocate memory from SDRAM1. */ +static uint8_t *sdram_malloc(size_t size) +{ + uint8_t *p = sdram_heap; + +#define pad(n) (((n) + 3) & ~3) + size = pad(size); + + if (p + size + sizeof(uint32_t) > &__end_sdram1) + return NULL; + + *(uint32_t *)p = (uint32_t)size; + p += sizeof(uint32_t); + + sdram_heap += size + sizeof(uint32_t); + return p; +} + +/* A very limited form of free(), which only frees memory if it's at the + * top of the heap. + */ +static hal_error_t sdram_free(uint8_t *ptr) +{ + uint8_t *p = ptr - sizeof(uint32_t); + uint32_t size = *(uint32_t *)p; + if (ptr + size == sdram_heap) { + sdram_heap = p; + return LIBHAL_OK; + } + else + return HAL_ERROR_FORBIDDEN; +} + +hal_error_t sdram_stats(size_t *used, size_t *available) +{ + if (used == NULL || available == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + *used = sdram_heap - &_esdram1; + *available = &__end_sdram1 - sdram_heap; + + return LIBHAL_OK; +} + +/* Implement static memory allocation for libhal over sdram_malloc(). + */ +void *hal_allocate_static_memory(const size_t size) +{ + return sdram_malloc(size); +} + +hal_error_t hal_free_static_memory(const void * const ptr) +{ + return sdram_free((uint8_t *)ptr); +} + +/* Critical section start/end - temporarily disable interrupts. + */ +void hal_critical_section_start(void) +{ + __disable_irq(); +} + +void hal_critical_section_end(void) +{ + __enable_irq(); +} + +/* A genericized public interface to task_yield(), for calling from + * libhal. + */ +void hal_task_yield(void) +{ + task_yield(); +} + +void hal_task_yield_maybe(void) +{ + task_yield_maybe(); +} + +/* A mutex to arbitrate concurrent access to the keystore. + */ +task_mutex_t ks_mutex = { 0 }; +void hal_ks_lock(void) { task_mutex_lock(&ks_mutex); } +void hal_ks_unlock(void) { task_mutex_unlock(&ks_mutex); } + +/* A mutex to arbitrary concurrent access to the RSA blinding factors cache. + */ +task_mutex_t rsa_bf_mutex = { 0 }; +void hal_rsa_bf_lock(void) { task_mutex_lock(&rsa_bf_mutex); } +void hal_rsa_bf_unlock(void) { task_mutex_unlock(&rsa_bf_mutex); } + +/* Sleep for specified number of seconds. + */ +void hal_sleep(const unsigned seconds) { task_delay(seconds * 1000); } + +/* The main task. This does all the setup, and the worker tasks handle + * the rest. + */ +int main(void) +{ + stm_init(); + led_on(LED_GREEN); + + if (hal_rpc_server_init() != LIBHAL_OK) + Error_Handler(); + + /* Initialize the ibuf queues. */ + ibufs = (rpc_buffer_t *)sdram_malloc(NUM_RPC_TASK * sizeof(rpc_buffer_t)); + if (ibufs == NULL) + Error_Handler(); + memset(ibufs, 0, NUM_RPC_TASK * sizeof(rpc_buffer_t)); + memset(&ibuf_waiting, 0, sizeof(ibuf_waiting)); + memset(&ibuf_ready, 0, sizeof(ibuf_ready)); + for (size_t i = 0; i < NUM_RPC_TASK; ++i) + ibuf_put(&ibuf_waiting, &ibufs[i]); + + /* Create the rpc dispatch worker tasks. */ + static char label[NUM_RPC_TASK][sizeof("dispatch0")]; + for (int i = 0; i < NUM_RPC_TASK; ++i) { + sprintf(label[i], "dispatch%d", i); + void *stack = (void *)sdram_malloc(TASK_STACK_SIZE); + if (stack == NULL) + Error_Handler(); + if (i == NUM_RPC_TASK - 1) { + if (task_add("hashsig_restart", hashsig_restart_task, label[i], stack, TASK_STACK_SIZE) == NULL) + Error_Handler(); + } + else { + if (task_add(label[i], dispatch_task, NULL, stack, TASK_STACK_SIZE) == NULL) + Error_Handler(); + } + } + + /* Create the busy task. */ + busy_tcb = task_add("busy", busy_task, NULL, busy_stack, sizeof(busy_stack)); + if (busy_tcb == NULL) + Error_Handler(); + + /* Start the UART receiver. */ + if (HAL_UART_Receive_DMA(&huart_user, (uint8_t *) uart_ringbuf.buf, sizeof(uart_ringbuf.buf)) != CMSIS_HAL_OK) + Error_Handler(); + + /* Launch other tasks (csprng warm-up task?) + * Wait for FPGA_DONE interrupt. + */ + + /* Create the CLI task. */ + void *cli_stack = (void *)sdram_malloc(CLI_STACK_SIZE); + if (task_add("cli", (funcp_t)cli_main, NULL, cli_stack, CLI_STACK_SIZE) == NULL) + Error_Handler(); + + /* Start the tasker */ + task_yield(); + + /*NOTREACHED*/ + return 0; +} diff --git a/projects/hsm/log.c b/projects/hsm/log.c new file mode 100644 index 0000000..fbc0e73 --- /dev/null +++ b/projects/hsm/log.c @@ -0,0 +1,68 @@ +/* + * log.c + * ----- + * Implement libhal logging API on Alpha. + * + * Copyright (c) 2017, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdarg.h> + +#define HAL_OK CMSIS_HAL_OK +#include "stm-uart.h" +#undef HAL_OK + +#define HAL_OK LIBHAL_OK +#include "hal.h" +#include "hal_internal.h" +#undef HAL_OK + +static hal_log_level_t current_log_level; + +void hal_log_set_level(const hal_log_level_t level) +{ + current_log_level = level; +} + +void hal_log(const hal_log_level_t level, const char *format, ...) +{ + if (level < current_log_level) + return; + + char buffer[2048]; + va_list ap; + + va_start(ap, format); + vsnprintf(buffer, sizeof(buffer), format, ap); + va_end(ap); + + uart_send_string(buffer); + uart_send_string("\r\n"); +} diff --git a/projects/hsm/main.c b/projects/hsm/main.c deleted file mode 100644 index 79c567b..0000000 --- a/projects/hsm/main.c +++ /dev/null @@ -1,226 +0,0 @@ -/* - * rpc_server.c - * ------------ - * Remote procedure call server-side private API implementation. - * - * Copyright (c) 2016, NORDUnet A/S All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * - Neither the name of the NORDUnet nor the names of its contributors may - * be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * This is the main RPC server moddule. It creates a new thread to deal - * with each request, to prevent a long-running request (e.g. RSA keygen) - * from blocking independent requests from other clients. This has a - * number of consequences. We can't do a blocking receive in the main - * thread, because that prevents the dispatch thread from transmitting the - * response (because they both want to lock the UART - see - * stm32f4xx_hal_uart.c). So we have to do a non-blocking receive with a - * callback routine. But we can't create a thread from the callback - * routine, because it's in the context of an ISR, so we raise a semaphore - * for the main thread to create the dispatch thread. - */ - -#include <string.h> - -#include "cmsis_os.h" - -#include "stm-init.h" -#include "stm-led.h" -#include "stm-fmc.h" -#include "stm-uart.h" - -/* stm32f4xx_hal_def.h and hal.h both define HAL_OK as an enum value */ -#define HAL_OK HAL_OKAY - -#include "hal.h" -#include "hal_internal.h" -#include "slip_internal.h" -#include "xdr_internal.h" - -/* RPC buffers. For each active RPC, there will be two - input and output. - */ - -#ifndef NUM_RPC_BUFFER -/* An arbitrary number, but we don't expect to have more than 8 concurrent - * RPC requests. - */ -#define NUM_RPC_BUFFER 16 -#endif - -#ifndef MAX_PKT_SIZE -/* Another arbitrary number, more or less driven by the 4096-bit RSA - * keygen test. - */ -#define MAX_PKT_SIZE 4096 -#endif - -/* The thread entry point takes a single void* argument, so we bundle the - * packet buffer and length arguments together. - */ -typedef struct { - size_t len; - uint8_t buf[MAX_PKT_SIZE]; -} rpc_buffer_t; - -osPoolDef(rpc_buffer_pool, NUM_RPC_BUFFER, rpc_buffer_t); -osPoolId rpc_buffer_pool; - -static rpc_buffer_t *rpc_buffer_alloc(void) -{ - return (rpc_buffer_t *)osPoolCAlloc(rpc_buffer_pool); -} - -/* A mutex to arbitrate concurrent UART transmits, from RPC responses. - */ -osMutexId uart_mutex; -osMutexDef(uart_mutex); - -/* Thread entry point for the RPC request handler. - */ -static void dispatch_thread(void const *args) -{ - rpc_buffer_t *ibuf = (rpc_buffer_t *)args; - rpc_buffer_t *obuf = rpc_buffer_alloc(); - if (obuf == NULL) { - uint8_t buf[8]; - uint8_t * bufptr = &buf[4]; - const uint8_t * const limit = buf + sizeof(buf); - memcpy(buf, ibuf->buf, 4); - hal_xdr_encode_int(&bufptr, limit, HAL_ERROR_ALLOCATION_FAILURE); - osMutexWait(uart_mutex, osWaitForever); - hal_rpc_sendto(ibuf->buf, sizeof(buf), NULL); - osMutexRelease(uart_mutex); - osPoolFree(rpc_buffer_pool, ibuf); - Error_Handler(); - } - /* copy client ID from request to response */ - memcpy(obuf->buf, ibuf->buf, 4); - obuf->len = sizeof(obuf->buf) - 4; - hal_rpc_server_dispatch(ibuf->buf + 4, ibuf->len - 4, obuf->buf + 4, &obuf->len); - osPoolFree(rpc_buffer_pool, ibuf); - osMutexWait(uart_mutex, osWaitForever); - hal_error_t ret = hal_rpc_sendto(obuf->buf, obuf->len + 4, NULL); - osMutexRelease(uart_mutex); - osPoolFree(rpc_buffer_pool, obuf); - if (ret != HAL_OK) - Error_Handler(); -} -osThreadDef(dispatch_thread, osPriorityNormal, DEFAULT_STACK_SIZE); - -/* Semaphore to inform the main thread that there's a new RPC request. - */ -osSemaphoreId rpc_sem; -osSemaphoreDef(rpc_sem); - -static uint8_t c; /* current character received from UART */ -static rpc_buffer_t *ibuf; /* current RPC input buffer */ - -/* Callback for HAL_UART_Receive_IT(). - */ -void HAL_UART2_RxCpltCallback(UART_HandleTypeDef *huart) -{ - int complete; - hal_slip_recv_char(ibuf->buf, &ibuf->len, sizeof(ibuf->buf), &complete); - if (complete) - osSemaphoreRelease(rpc_sem); - - HAL_UART_Receive_IT(huart, &c, 1); -} - -hal_error_t hal_serial_send_char(uint8_t c) -{ - return (uart_send_char(c) == 0) ? HAL_OK : HAL_ERROR_RPC_TRANSPORT; -} - -hal_error_t hal_serial_recv_char(uint8_t *cp) -{ - /* return the character from HAL_UART_Receive_IT */ - *cp = c; - return HAL_OK; -} - -/* The main thread. After the system setup, it waits for the RPC-request - * semaphore from HAL_UART_RxCpltCallback, and spawns a dispatch thread. - */ -int main() -{ - stm_init(); - -#ifdef TARGET_CRYPTECH_DEV_BRIDGE - /* Wait six seconds to not upset the Novena at boot. */ - led_on(LED_BLUE); - for (int i = 0; i < 12; i++) { - osDelay(500); - led_toggle(LED_BLUE); - } - led_off(LED_BLUE); -#endif - led_on(LED_GREEN); - /* Prepare FMC interface. */ - fmc_init(); - - /* Haaaack. probe_cores() calls malloc(), which works from the main - * thread, but not from a spawned thread. It would be better to - * rewrite it to use static memory, but for now, just force it to - * probe early. - */ - hal_core_iterate(NULL); - - rpc_buffer_pool = osPoolCreate(osPool(rpc_buffer_pool)); - uart_mutex = osMutexCreate(osMutex(uart_mutex)); - rpc_sem = osSemaphoreCreate(osSemaphore(rpc_sem), 0); - -#ifdef TARGET_CRYPTECH_ALPHA - /* Launch other threads: - * - admin thread on USART1 - * - csprng warm-up thread? - */ -#endif - - if (hal_rpc_server_init() != HAL_OK) - Error_Handler(); - - ibuf = rpc_buffer_alloc(); - if (ibuf == NULL) - /* Something is badly wrong. */ - Error_Handler(); - - /* Start the non-blocking receive */ - HAL_UART_Receive_IT(&huart_user, &c, 1); - - while (1) { - osSemaphoreWait(rpc_sem, osWaitForever); - if (osThreadCreate(osThread(dispatch_thread), (void *)ibuf) == NULL) - Error_Handler(); - while ((ibuf = rpc_buffer_alloc()) == NULL); - /* XXX There's a potential race condition, where another request - * could write into the old ibuf, or into the null pointer if - * we're out of ibufs. - */ - } -} diff --git a/projects/hsm/mgmt-bootloader.c b/projects/hsm/mgmt-bootloader.c new file mode 100644 index 0000000..1d8b8ad --- /dev/null +++ b/projects/hsm/mgmt-bootloader.c @@ -0,0 +1,89 @@ +/* + * mgmt-bootloader.c + * ----------------- + * CLI code for updating the bootloader. + * + * Copyright (c) 2016, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Rename both CMSIS HAL_OK and libhal HAL_OK to disambiguate */ +#define HAL_OK CMSIS_HAL_OK +#include "stm-init.h" +#include "stm-uart.h" +#include "stm-flash.h" +#include "mgmt-cli.h" +#include "mgmt-misc.h" +#include "mgmt-bootloader.h" + +#undef HAL_OK +#define HAL_OK LIBHAL_OK +#include "hal.h" +#undef HAL_OK + +extern hal_user_t user; + +static uint32_t dfu_offset; + +static HAL_StatusTypeDef _flash_write_callback(uint8_t *buf, size_t len) +{ + HAL_StatusTypeDef status = stm_flash_write32(dfu_offset, (uint32_t *)buf, len/4); + dfu_offset += DFU_UPLOAD_CHUNK_SIZE; + return status; +} + +static int cmd_bootloader_upload(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + command = command; + argv = argv; + argc = argc; + + if (user < HAL_USER_SO) { + cli_print(cli, "Permission denied."); + return CLI_ERROR; + } + + uint8_t buf[DFU_UPLOAD_CHUNK_SIZE]; + dfu_offset = DFU_BOOTLOADER_ADDR; + + int ret = cli_receive_data(cli, buf, sizeof(buf), _flash_write_callback); + if (ret == CLI_OK) { + cli_print(cli, "\nRebooting\n"); + HAL_NVIC_SystemReset(); + } + return ret; +} + +void configure_cli_bootloader(struct cli_def *cli) +{ + struct cli_command *c; + + c = cli_register_command(cli, NULL, "bootloader", NULL, 0, 0, NULL); + + cli_register_command(cli, c, "upload", cmd_bootloader_upload, 0, 0, "Upload new bootloader image"); +} diff --git a/projects/hsm/mgmt-bootloader.h b/projects/hsm/mgmt-bootloader.h new file mode 100644 index 0000000..31dbefc --- /dev/null +++ b/projects/hsm/mgmt-bootloader.h @@ -0,0 +1,51 @@ +/* + * mgmt-bootloader.h + * --------------- + * Management CLI bootloader upgrade code. + * + * Copyright (c) 2016, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __STM32_CLI_MGMT_BOOTLOADER_H +#define __STM32_CLI_MGMT_BOOTLOADER_H + +#include <libcli.h> + +/* symbols defined in the linker script (STM32F429BI_bootloader.ld) */ +extern uint32_t CRYPTECH_BOOTLOADER_START; +extern uint32_t CRYPTECH_BOOTLOADER_END; +extern uint32_t CRYPTECH_DFU_CONTROL; + +#define DFU_BOOTLOADER_ADDR ((uint32_t) &CRYPTECH_BOOTLOADER_START) +#define DFU_BOOTLOADER_END_ADDR ((uint32_t) &CRYPTECH_BOOTLOADER_END) +#define DFU_UPLOAD_CHUNK_SIZE 4096 + +extern void configure_cli_bootloader(struct cli_def *cli); + +#endif /* __STM32_CLI_MGMT_BOOTLOADER_H */ diff --git a/projects/hsm/mgmt-cli.c b/projects/hsm/mgmt-cli.c new file mode 100644 index 0000000..fd5c90a --- /dev/null +++ b/projects/hsm/mgmt-cli.c @@ -0,0 +1,220 @@ +/* + * mgmt-cli.c + * --------- + * Management CLI code. + * + * Copyright (c) 2016-2017, NORDUnet A/S All rights reserved. + * Copyright: 2020, The Commons Conservancy Cryptech Project + * SPDX-License-Identifier: BSD-3-Clause + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <string.h> + +/* Rename both CMSIS HAL_OK and libhal HAL_OK to disambiguate */ +#define HAL_OK CMSIS_HAL_OK +#include "stm-init.h" +#include "stm-uart.h" +#include "stm-led.h" +#include "task.h" + +#include "mgmt-cli.h" +#include "mgmt-firmware.h" +#include "mgmt-bootloader.h" +#include "mgmt-fpga.h" +#include "mgmt-misc.h" +#include "mgmt-keystore.h" +#include "mgmt-masterkey.h" +#include "mgmt-task.h" +#ifdef DO_TIMING +#include "mgmt-timing.h" +#endif + +#undef HAL_OK +#define HAL_OK LIBHAL_OK +#include "hal.h" +#warning Refactor so we do not need to include hal_internal.h here +#include "hal_internal.h" +#undef HAL_OK + +static tcb_t *cli_task; + +#ifndef CLI_UART_RECVBUF_SIZE +#define CLI_UART_RECVBUF_SIZE 256 +#endif + +typedef struct { + unsigned ridx; + unsigned widx; + mgmt_cli_dma_state_t rx_state; + uint8_t buf[CLI_UART_RECVBUF_SIZE]; +} ringbuf_t; + +inline void ringbuf_init(ringbuf_t *rb) +{ + memset(rb, 0, sizeof(*rb)); +} + +/* return number of characters read */ +inline int ringbuf_read_char(ringbuf_t *rb, uint8_t *c) +{ + if (rb->ridx != rb->widx) { + *c = rb->buf[rb->ridx]; + if (++rb->ridx >= sizeof(rb->buf)) + rb->ridx = 0; + return 1; + } + return 0; +} + +inline void ringbuf_write_char(ringbuf_t *rb, uint8_t c) +{ + rb->buf[rb->widx] = c; + if (++rb->widx >= sizeof(rb->buf)) + rb->widx = 0; +} + +static ringbuf_t uart_ringbuf; + +/* current character received from UART */ +static uint8_t uart_rx; + +/* Callback for HAL_UART_Receive_DMA(). + */ +void HAL_UART1_RxCpltCallback(UART_HandleTypeDef *huart) +{ + huart = huart; + + ringbuf_write_char(&uart_ringbuf, uart_rx); + task_wake(cli_task); +} + +static void uart_cli_print(struct cli_def *cli __attribute__ ((unused)), const char *buf) +{ + char crlf[] = "\r\n"; + uart_send_string(buf); + uart_send_string(crlf); +} + +static ssize_t uart_cli_read(struct cli_def *cli __attribute__ ((unused)), void *buf, size_t count) +{ + for (size_t i = 0; i < count; ++i) { + while (ringbuf_read_char(&uart_ringbuf, (uint8_t *)(buf + i)) == 0) + task_sleep(); + } + return (ssize_t)count; +} + +static ssize_t uart_cli_write(struct cli_def *cli __attribute__ ((unused)), const void *buf, size_t count) +{ + uart_send_bytes((uint8_t *) buf, count); + return (ssize_t)count; +} + +int control_mgmt_uart_dma_rx(mgmt_cli_dma_state_t state) +{ + if (state == DMA_RX_START) { + if (uart_ringbuf.rx_state != DMA_RX_START) { + ringbuf_init(&uart_ringbuf); + HAL_UART_Receive_DMA(&huart_mgmt, &uart_rx, 1); + uart_ringbuf.rx_state = DMA_RX_START; + } + return 1; + } else if (state == DMA_RX_STOP) { + if (HAL_UART_DMAStop(&huart_mgmt) != CMSIS_HAL_OK) return 0; + uart_ringbuf.rx_state = DMA_RX_STOP; + return 1; + } + return 0; +} + +hal_user_t user; + +static int check_auth(const char *username, const char *password) +{ + hal_client_handle_t client = { -1 }; + + /* PIN-based login */ + if (strcmp(username, "wheel") == 0) + user = HAL_USER_WHEEL; + else if (strcmp(username, "so") == 0) + user = HAL_USER_SO; + else if (strcmp(username, "user") == 0) + user = HAL_USER_NORMAL; + else + user = HAL_USER_NONE; + + if (hal_rpc_login(client, user, password, strlen(password)) == LIBHAL_OK) + return CLI_OK; + + user = HAL_USER_NONE; + return CLI_ERROR; +} + +int cli_main(void) +{ + cli_task = task_get_tcb(); + + struct cli_def *cli; + cli = cli_init(); + if (cli == NULL) + Error_Handler(); + + cli_read_callback(cli, uart_cli_read); + cli_write_callback(cli, uart_cli_write); + cli_print_callback(cli, uart_cli_print); + cli_set_banner(cli, "Cryptech Alpha"); + cli_set_hostname(cli, "cryptech"); + cli_set_auth_callback(cli, check_auth); + + /* we don't have any privileged commands at the moment */ + cli_unregister_command(cli, "enable"); + + configure_cli_fpga(cli); + configure_cli_keystore(cli); + configure_cli_masterkey(cli); + configure_cli_firmware(cli); + configure_cli_bootloader(cli); + configure_cli_misc(cli); + configure_cli_task(cli); +#ifdef DO_TIMING + configure_cli_timing(cli); +#endif + + while (1) { + control_mgmt_uart_dma_rx(DMA_RX_START); + + cli_loop(cli, 0); + /* cli_loop returns when the user enters 'quit' or 'exit' */ + cli_print(cli, "\nLogging out...\n"); + user = HAL_USER_NONE; + } + + /*NOTREACHED*/ + return -1; +} diff --git a/projects/hsm/mgmt-cli.h b/projects/hsm/mgmt-cli.h new file mode 100644 index 0000000..0b9c40c --- /dev/null +++ b/projects/hsm/mgmt-cli.h @@ -0,0 +1,49 @@ +/* + * mgmt-cli.h + * --------- + * Management CLI code. + * + * Copyright (c) 2016, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __STM32_MGMT_CLI_H +#define __STM32_MGMT_CLI_H + +#include <libcli.h> + +typedef enum { + DMA_RX_STOP, + DMA_RX_START, +} mgmt_cli_dma_state_t; + +extern int control_mgmt_uart_dma_rx(mgmt_cli_dma_state_t state); + +extern int cli_main(void); + +#endif /* __STM32_MGMT_CLI_H */ diff --git a/projects/hsm/mgmt-firmware.c b/projects/hsm/mgmt-firmware.c new file mode 100644 index 0000000..b6b3321 --- /dev/null +++ b/projects/hsm/mgmt-firmware.c @@ -0,0 +1,75 @@ +/* + * mgmt-firmware.c + * --------------- + * CLI code for managing the loaded firmware. + * + * Copyright (c) 2016, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Rename both CMSIS HAL_OK and libhal HAL_OK to disambiguate */ +#define HAL_OK CMSIS_HAL_OK +#include "stm-init.h" +#include "stm-uart.h" + +#include "mgmt-cli.h" + +#undef HAL_OK +#define HAL_OK LIBHAL_OK +#include "hal.h" +#undef HAL_OK + +extern hal_user_t user; + +static int cmd_firmware_upload(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + command = command; + argv = argv; + argc = argc; + + if (user < HAL_USER_SO) { + cli_print(cli, "Permission denied."); + return CLI_ERROR; + } + + /* reboot and let the bootloader handle the upload */ + cli_print(cli, "\n\n\nRebooting\n\n\n"); + HAL_NVIC_SystemReset(); + + /*NOTREACHED*/ + return CLI_OK; +} + +void configure_cli_firmware(struct cli_def *cli) +{ + struct cli_command *c; + + c = cli_register_command(cli, NULL, "firmware", NULL, 0, 0, NULL); + + cli_register_command(cli, c, "upload", cmd_firmware_upload, 0, 0, "Upload new firmware image"); +} diff --git a/projects/hsm/mgmt-firmware.h b/projects/hsm/mgmt-firmware.h new file mode 100644 index 0000000..af7c67c --- /dev/null +++ b/projects/hsm/mgmt-firmware.h @@ -0,0 +1,42 @@ +/* + * mgmt-firmware.h + * --------------- + * Management CLI Device Firmware Upgrade code. + * + * Copyright (c) 2016, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __STM32_CLI_MGMT_FIRMWARE_H +#define __STM32_CLI_MGMT_FIRMWARE_H + +#include <libcli.h> + +extern void configure_cli_firmware(struct cli_def *cli); + +#endif /* __STM32_CLI_MGMT_FIRMWARE_H */ diff --git a/projects/hsm/mgmt-fpga.c b/projects/hsm/mgmt-fpga.c new file mode 100644 index 0000000..af7ba11 --- /dev/null +++ b/projects/hsm/mgmt-fpga.c @@ -0,0 +1,194 @@ +/* + * mgmt-fpga.c + * ----------- + * CLI code to manage the FPGA configuration etc. + * + * Copyright (c) 2016-2017, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Rename both CMSIS HAL_OK and libhal HAL_OK to disambiguate */ +#define HAL_OK CMSIS_HAL_OK +#include "stm-init.h" +#include "stm-uart.h" +#include "stm-fpgacfg.h" + +#include "mgmt-cli.h" +#include "mgmt-fpga.h" +#include "mgmt-misc.h" + +#undef HAL_OK +#define HAL_OK LIBHAL_OK +#include "hal.h" +#undef HAL_OK + +#include <string.h> + + +extern hal_user_t user; + +static volatile uint32_t dfu_offset = 0; + + +static HAL_StatusTypeDef _flash_write_callback(uint8_t *buf, size_t len) +{ + HAL_StatusTypeDef res; + + if ((dfu_offset % FPGACFG_SECTOR_SIZE) == 0) + /* first page in sector, need to erase sector */ + if ((res = fpgacfg_erase_sector(dfu_offset / FPGACFG_SECTOR_SIZE)) != CMSIS_HAL_OK) + return res; + + /* fpgacfg_write_data (a thin wrapper around n25q128_write_data) + * requires the offset and length to be page-aligned. The last chunk + * will be short, so we pad it out to the full chunk size. + */ + len = len; + res = fpgacfg_write_data(dfu_offset, buf, BITSTREAM_UPLOAD_CHUNK_SIZE); + dfu_offset += BITSTREAM_UPLOAD_CHUNK_SIZE; + return res; +} + +static int cmd_fpga_bitstream_upload(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + command = command; + argv = argv; + argc = argc; + + if (user < HAL_USER_SO) { + cli_print(cli, "Permission denied."); + return CLI_ERROR; + } + + uint8_t buf[BITSTREAM_UPLOAD_CHUNK_SIZE]; + + dfu_offset = 0; + + fpgacfg_access_control(ALLOW_ARM); + + cli_print(cli, "Checking if FPGA config memory is accessible"); + if (fpgacfg_check_id() != CMSIS_HAL_OK) { + cli_print(cli, "ERROR: FPGA config memory not accessible. Check that jumpers JP7 and JP8 are installed."); + return CLI_ERROR; + } + + cli_receive_data(cli, &buf[0], sizeof(buf), _flash_write_callback); + + fpgacfg_access_control(ALLOW_FPGA); + + cli_print(cli, "DFU offset now: %li (%li chunks)", dfu_offset, dfu_offset / BITSTREAM_UPLOAD_CHUNK_SIZE); + return CLI_OK; +} + +static int cmd_fpga_bitstream_erase(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + command = command; + argv = argv; + argc = argc; + + fpgacfg_access_control(ALLOW_ARM); + + cli_print(cli, "Checking if FPGA config memory is accessible"); + if (fpgacfg_check_id() != CMSIS_HAL_OK) { + cli_print(cli, "ERROR: FPGA config memory not accessible. Check that jumpers JP7 and JP8 are installed."); + return CLI_ERROR; + } + + /* Erasing the whole config memory takes a while, we just need to erase the first sector. + * The bitstream has an EOF marker, so even if the next bitstream uploaded is shorter than + * the current one there should be no problem. + * + * This command could be made to accept an argument indicating the whole memory should be erased. + */ + if (fpgacfg_erase_sector(0) != CMSIS_HAL_OK) { + cli_print(cli, "Erasing first sector in FPGA config memory failed"); + return CLI_ERROR; + } + + cli_print(cli, "Erased FPGA config memory"); + fpgacfg_access_control(ALLOW_FPGA); + + return CLI_OK; +} + +static int cmd_fpga_reset(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + command = command; + argv = argv; + argc = argc; + + fpgacfg_access_control(ALLOW_FPGA); + fpgacfg_reset_fpga(RESET_FULL); + hal_core_reset_table(); + cli_print(cli, "FPGA has been reset"); + + return CLI_OK; +} + +static int cmd_fpga_show_cores(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + hal_core_t *core; + const hal_core_info_t *info; + + command = command; + argv = argv; + argc = argc; + + if (fpgacfg_check_done() != CMSIS_HAL_OK) { + cli_print(cli, "FPGA has not loaded a bitstream"); + return CLI_OK; + } + + for (core = hal_core_iterate(NULL); core != NULL; core = hal_core_iterate(core)) { + info = hal_core_info(core); + cli_print(cli, "%04x: %8.8s %4.4s", + (unsigned int)info->base, info->name, info->version); + } + + return CLI_OK; +} + +void configure_cli_fpga(struct cli_def *cli) +{ + struct cli_command *c = cli_register_command(cli, NULL, "fpga", NULL, 0, 0, NULL); + + struct cli_command *c_show = cli_register_command(cli, c, "show", NULL, 0, 0, NULL); + struct cli_command *c_bitstream = cli_register_command(cli, c, "bitstream", NULL, 0, 0, NULL); + + /* fpga show cores */ + cli_register_command(cli, c_show, "cores", cmd_fpga_show_cores, 0, 0, "Show FPGA core names and versions"); + + /* fpga reset */ + cli_register_command(cli, c, "reset", cmd_fpga_reset, 0, 0, "Reset FPGA (config reset)"); + + /* fpga bitstream upload */ + cli_register_command(cli, c_bitstream, "upload", cmd_fpga_bitstream_upload, 0, 0, "Upload new FPGA bitstream"); + + /* fpga bitstream erase */ + cli_register_command(cli, c_bitstream, "erase", cmd_fpga_bitstream_erase, 0, 0, "Erase FPGA config memory"); +} diff --git a/projects/hsm/mgmt-fpga.h b/projects/hsm/mgmt-fpga.h new file mode 100644 index 0000000..9d0aedc --- /dev/null +++ b/projects/hsm/mgmt-fpga.h @@ -0,0 +1,49 @@ +/* + * mgmt-fpga.h + * ----------- + * Management FPGA related code. + * + * Copyright (c) 2016, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __STM32_CLI_MGMT_FPGA_H +#define __STM32_CLI_MGMT_FPGA_H + +#include <libcli.h> + + +/* The chunk size have to be a multiple of the SPI flash page size (256 bytes), + and it has to match the chunk size in the program sending the bitstream over the UART. +*/ +#define BITSTREAM_UPLOAD_CHUNK_SIZE 4096 + + +extern void configure_cli_fpga(struct cli_def *cli); + +#endif /* __STM32_CLI_MGMT_FPGA_H */ diff --git a/projects/hsm/mgmt-keystore.c b/projects/hsm/mgmt-keystore.c new file mode 100644 index 0000000..9eb42da --- /dev/null +++ b/projects/hsm/mgmt-keystore.c @@ -0,0 +1,408 @@ +/* + * mgmt-keystore.c + * --------------- + * CLI 'keystore' commands. + * + * Copyright (c) 2016-2017, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Rename both CMSIS HAL_OK and libhal HAL_OK to disambiguate */ +#define HAL_OK CMSIS_HAL_OK +#include "stm-init.h" +#include "stm-keystore.h" +#include "stm-fpgacfg.h" +#include "stm-uart.h" + +#include "mgmt-cli.h" + +#undef HAL_OK +#define LIBHAL_OK HAL_OK +#include "hal.h" +#warning Really should not be including hal_internal.h here, fix API instead of bypassing it +#include "hal_internal.h" +#undef HAL_OK + +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <ctype.h> + + +static int cmd_keystore_set_pin(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + hal_user_t user; + hal_error_t status; + hal_client_handle_t client = { -1 }; + + command = command; + + if (argc != 2) { + cli_print(cli, "Wrong number of arguments (%i).", argc); + cli_print(cli, "Syntax: keystore set pin <user|so|wheel> <pin>"); + return CLI_ERROR; + } + + if (strcmp(argv[0], "user") == 0) + user = HAL_USER_NORMAL; + else if (strcmp(argv[0], "so") == 0) + user = HAL_USER_SO; + else if (strcmp(argv[0], "wheel") == 0) + user = HAL_USER_WHEEL; + else { + cli_print(cli, "First argument must be 'user', 'so' or 'wheel' - not '%s'", argv[0]); + return CLI_ERROR; + } + + status = hal_rpc_set_pin(client, user, argv[1], strlen(argv[1])); + if (status != LIBHAL_OK) { + cli_print(cli, "Failed setting PIN: %s", hal_error_string(status)); + return CLI_ERROR; + } + + return CLI_OK; +} + +static int cmd_keystore_clear_pin(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + hal_user_t user; + hal_error_t status; + hal_client_handle_t client = { -1 }; + + command = command; + + if (argc != 1) { + cli_print(cli, "Wrong number of arguments (%i).", argc); + cli_print(cli, "Syntax: keystore clear pin <user|so|wheel>"); + return CLI_ERROR; + } + + user = HAL_USER_NONE; + if (strcmp(argv[0], "user") == 0) + user = HAL_USER_NORMAL; + else if (strcmp(argv[0], "so") == 0) + user = HAL_USER_SO; + else if (strcmp(argv[0], "wheel") == 0) + user = HAL_USER_WHEEL; + else { + cli_print(cli, "First argument must be 'user', 'so' or 'wheel' - not '%s'", argv[0]); + return CLI_ERROR; + } + + if ((status = hal_rpc_set_pin(client, user, "", 0)) != LIBHAL_OK) { + cli_print(cli, "Failed clearing PIN: %s", hal_error_string(status)); + return CLI_ERROR; + } + + return CLI_OK; +} + +static int cmd_keystore_set_pin_iterations(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + hal_error_t status; + hal_client_handle_t client = { -1 }; + + command = command; + + if (argc != 1) { + cli_print(cli, "Wrong number of arguments (%i).", argc); + cli_print(cli, "Syntax: keystore set pin iterations <number>"); + return CLI_ERROR; + } + + status = hal_set_pin_default_iterations(client, strtoul(argv[0], NULL, 0)); + if (status != LIBHAL_OK) { + cli_print(cli, "Failed setting iterations: %s", hal_error_string(status)); + return CLI_ERROR; + } + + return CLI_OK; +} + +static int cmd_keystore_delete_key(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + const hal_client_handle_t client = { -1 }; + const hal_session_handle_t session = { HAL_HANDLE_NONE }; + hal_pkey_handle_t pkey = { HAL_HANDLE_NONE }; + hal_error_t status; + hal_uuid_t name; + + command = command; + + if (argc != 1) { + cli_print(cli, "Wrong number of arguments (%i).", argc); + cli_print(cli, "Syntax: keystore delete key <name>"); + return CLI_ERROR; + } + + if ((status = hal_uuid_parse(&name, argv[0])) != LIBHAL_OK) { + cli_print(cli, "Couldn't parse key name: %s", hal_error_string(status)); + return CLI_ERROR; + } + + if ((status = hal_rpc_pkey_open(client, session, &pkey, &name)) != LIBHAL_OK) { + cli_print(cli, "Couldn't find key: %s", hal_error_string(status)); + return CLI_ERROR; + } + + if ((status = hal_rpc_pkey_delete(pkey)) != LIBHAL_OK) { + cli_print(cli, "Failed deleting key: %s", hal_error_string(status)); + (void) hal_rpc_pkey_close(pkey); + return CLI_ERROR; + } + + cli_print(cli, "Deleted key %s", argv[0]); + + return CLI_OK; +} + +#include "ks.h" + +static int show_keys(struct cli_def *cli, const char *title) +{ + const hal_client_handle_t client = { -1 }; + const hal_session_handle_t session = { HAL_HANDLE_NONE }; + char key_name[HAL_UUID_TEXT_SIZE]; + hal_uuid_t previous_uuid = {{0}}; + hal_pkey_handle_t pkey; + hal_curve_name_t curve; + hal_key_flags_t flags; + unsigned n, state = 0; + hal_uuid_t uuids[50]; + hal_key_type_t type; + hal_error_t status; + int count = 0; + int done = 0; + + cli_print(cli, title); + + size_t avail; + if ((status = hal_ks_available(hal_ks_token, &avail)) == HAL_OK) + cli_print(cli, "Token keystore: %d available", avail); + else + cli_print(cli, "Error reading token keystore: %s", hal_error_string(status)); + if ((status = hal_ks_available(hal_ks_volatile, &avail)) == HAL_OK) + cli_print(cli, "Volatile keystore: %d available", avail); + else + cli_print(cli, "Error reading volatile keystore: %s", hal_error_string(status)); + + while (!done) { + + if ((status = hal_rpc_pkey_match(client, session, HAL_KEY_TYPE_NONE, HAL_CURVE_NONE, + 0, 0, NULL, 0, &state, uuids, &n, + sizeof(uuids)/sizeof(*uuids), + &previous_uuid)) != LIBHAL_OK) { + cli_print(cli, "Could not fetch UUID list: %s", hal_error_string(status)); + return CLI_ERROR; + } + + done = n < sizeof(uuids)/sizeof(*uuids); + + if (!done) + previous_uuid = uuids[sizeof(uuids)/sizeof(*uuids) - 1]; + + for (unsigned i = 0; i < n; i++) { + + if ((status = hal_uuid_format(&uuids[i], key_name, sizeof(key_name))) != LIBHAL_OK) { + cli_print(cli, "Could not convert key name, skipping: %s", + hal_error_string(status)); + continue; + } + + if ((status = hal_rpc_pkey_open(client, session, &pkey, &uuids[i])) != LIBHAL_OK) { + cli_print(cli, "Could not open key %s, skipping: %s", + key_name, hal_error_string(status)); + continue; + } + + if ((status = hal_rpc_pkey_get_key_type(pkey, &type)) != LIBHAL_OK || + (status = hal_rpc_pkey_get_key_curve(pkey, &curve)) != LIBHAL_OK || + (status = hal_rpc_pkey_get_key_flags(pkey, &flags)) != LIBHAL_OK) + cli_print(cli, "Could not fetch metadata for key %s, skipping: %s", + key_name, hal_error_string(status)); + + if (status == LIBHAL_OK) + status = hal_rpc_pkey_close(pkey); + else + (void) hal_rpc_pkey_close(pkey); + + if (status != LIBHAL_OK) + continue; + + const char *type_name = "unknown"; + switch (type) { + case HAL_KEY_TYPE_NONE: type_name = "none"; break; + case HAL_KEY_TYPE_RSA_PRIVATE: type_name = "RSA private"; break; + case HAL_KEY_TYPE_RSA_PUBLIC: type_name = "RSA public"; break; + case HAL_KEY_TYPE_EC_PRIVATE: type_name = "EC private"; break; + case HAL_KEY_TYPE_EC_PUBLIC: type_name = "EC public"; break; + case HAL_KEY_TYPE_HASHSIG_PRIVATE: type_name = "hashsig private"; break; + case HAL_KEY_TYPE_HASHSIG_PUBLIC: type_name = "hashsig public"; break; + case HAL_KEY_TYPE_HASHSIG_LMS: type_name = "hashsig lms"; break; + case HAL_KEY_TYPE_HASHSIG_LMOTS: type_name = "hashsig lmots"; break; + } + + const char *curve_name = "unknown"; + switch (curve) { + case HAL_CURVE_NONE: curve_name = "none"; break; + case HAL_CURVE_P256: curve_name = "P-256"; break; + case HAL_CURVE_P384: curve_name = "P-384"; break; + case HAL_CURVE_P521: curve_name = "P-521"; break; + } + + cli_print(cli, "Key %2i, name %s, type %s, curve %s, flags 0x%lx", + count++, key_name, type_name, curve_name, (unsigned long) flags); + } + } + + return CLI_OK; +} + +static int show_pin(struct cli_def *cli, char *label, hal_user_t user) +{ + const hal_ks_pin_t *p; + + if (hal_get_pin(user, &p) != HAL_OK) + return CLI_ERROR; + + /* + * I'm not sure iterations is the most interesting thing to show, but + * it's what we had before. + */ + + cli_print(cli, "%s iterations: 0x%lx", label, p->iterations); + return CLI_OK; +} + +static int cmd_keystore_show_keys(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + command = command; + argv = argv; + argc = argc; + + int err = 0; + + err |= show_keys(cli, "Keystore:"); + + cli_print(cli, "\nPins:"); + err |= show_pin(cli, "Wheel", HAL_USER_WHEEL); + err |= show_pin(cli, "SO ", HAL_USER_SO); + err |= show_pin(cli, "User ", HAL_USER_NORMAL); + + return err ? CLI_ERROR : CLI_OK; +} + +static int cmd_keystore_erase(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + hal_error_t err; + HAL_StatusTypeDef status; + int preserve_PINs = 0; + + command = command; + + if (argc < 1 || argc > 2 || strcmp(argv[0], "YesIAmSure") != 0) { + usage: + cli_print(cli, "Syntax: keystore erase YesIAmSure [preservePINs]"); + return CLI_ERROR; + } + if (argc == 2) { + if (strcasecmp(argv[1], "preservePINs") != 0) + goto usage; + else + preserve_PINs = 1; + } + + hal_user_t users[3] = { HAL_USER_NORMAL, HAL_USER_SO, HAL_USER_WHEEL }; + hal_ks_pin_t pins[3]; + if (preserve_PINs) { + for (size_t i = 0; i < 3; ++i) { + const hal_ks_pin_t *pin; + if (hal_get_pin(users[i], &pin) != HAL_OK) { + cli_print(cli, "Failed to get the PINs"); + return CLI_ERROR; + } + memcpy(&pins[i], pin, sizeof(*pin)); + } + } + + cli_print(cli, "OK, erasing keystore, this will take about 45 seconds..."); + if ((status = keystore_erase_bulk()) != CMSIS_HAL_OK) { + cli_print(cli, "Failed erasing token keystore: %i", status); + return CLI_ERROR; + } + + if ((err = hal_ks_init(hal_ks_token, 0)) != LIBHAL_OK) { + cli_print(cli, "Failed to reinitialize token keystore: %s", hal_error_string(err)); + return CLI_ERROR; + } + + if ((err = hal_ks_init(hal_ks_volatile, 0)) != LIBHAL_OK) { + cli_print(cli, "Failed to reinitialize memory keystore: %s", hal_error_string(err)); + return CLI_ERROR; + } + + if (preserve_PINs) { + for (size_t i = 0; i < 3; ++i) { + if (hal_set_pin(users[i], &pins[i]) != HAL_OK) { + cli_print(cli, "Failed to restore the PINs"); + return CLI_ERROR; + } + } + } + + cli_print(cli, "Keystore erased"); + return CLI_OK; +} + +void configure_cli_keystore(struct cli_def *cli) +{ + struct cli_command *c = cli_register_command(cli, NULL, "keystore", NULL, 0, 0, NULL); + + struct cli_command *c_show = cli_register_command(cli, c, "show", NULL, 0, 0, NULL); + struct cli_command *c_set = cli_register_command(cli, c, "set", NULL, 0, 0, NULL); + struct cli_command *c_clear = cli_register_command(cli, c, "clear", NULL, 0, 0, NULL); + struct cli_command *c_delete = cli_register_command(cli, c, "delete", NULL, 0, 0, NULL); + + /* keystore show keys */ + cli_register_command(cli, c_show, "keys", cmd_keystore_show_keys, 0, 0, "Show what PINs and keys are in the keystore"); + + /* keystore set pin */ + struct cli_command *c_set_pin = cli_register_command(cli, c_set, "pin", cmd_keystore_set_pin, 0, 0, "Set either 'wheel', 'user' or 'so' PIN"); + + /* keystore set pin iterations */ + cli_register_command(cli, c_set_pin, "iterations", cmd_keystore_set_pin_iterations, 0, 0, "Set PBKDF2 iterations for PINs"); + + /* keystore clear pin */ + cli_register_command(cli, c_clear, "pin", cmd_keystore_clear_pin, 0, 0, "Clear either 'wheel', 'user' or 'so' PIN"); + + /* keystore delete key */ + cli_register_command(cli, c_delete, "key", cmd_keystore_delete_key, 0, 0, "Delete a key"); + + /* keystore erase */ + cli_register_command(cli, c, "erase", cmd_keystore_erase, 0, 0, "Erase the whole keystore"); +} diff --git a/projects/hsm/mgmt-keystore.h b/projects/hsm/mgmt-keystore.h new file mode 100644 index 0000000..9e14ac6 --- /dev/null +++ b/projects/hsm/mgmt-keystore.h @@ -0,0 +1,42 @@ +/* + * mgmt-keystore.h + * ---------- + * Management CLI 'keystore' functions. + * + * Copyright (c) 2016, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __STM32_CLI_MGMT_KEYSTORE_H +#define __STM32_CLI_MGMT_KEYSTORE_H + +#include <libcli.h> + +extern void configure_cli_keystore(struct cli_def *cli); + +#endif /* __STM32_CLI_MGMT_KEYSTORE_H */ diff --git a/projects/hsm/mgmt-masterkey.c b/projects/hsm/mgmt-masterkey.c new file mode 100644 index 0000000..97e62a0 --- /dev/null +++ b/projects/hsm/mgmt-masterkey.c @@ -0,0 +1,244 @@ +/* + * mgmt-masterkey.c + * ---------------- + * Masterkey CLI functions. + * + * Copyright (c) 2016, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Rename both CMSIS HAL_OK and libhal HAL_OK to disambiguate */ +#define HAL_OK CMSIS_HAL_OK +#include "stm-init.h" +#include "stm-uart.h" +#include "mgmt-cli.h" +#include "mgmt-masterkey.h" + +#undef HAL_OK +#define LIBHAL_OK HAL_OK +#include <hal.h> +#warning Refactor so we do not need to include hal_internal.h here +#include <hal_internal.h> +#undef HAL_OK + +#include <stdlib.h> + +static char * _status2str(const hal_error_t status) +{ + switch (status) { + case LIBHAL_OK: + return (char *) "Set"; + case HAL_ERROR_MASTERKEY_NOT_SET: + return (char *) "Not set"; + default: + return (char *) "Unknown"; + } +} + +static int cmd_masterkey_status(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + hal_error_t status; + + command = command; + argv = argv; + argc = argc; + + cli_print(cli, "Status of master key:\n"); + + status = hal_keywrap_mkm_status(NULL); + cli_print(cli, " volatile: %s / %s", _status2str(status), hal_error_string(status)); + + status = hal_mkm_flash_read(NULL, 0); + cli_print(cli, " flash: %s / %s", _status2str(status), hal_error_string(status)); + + return CLI_OK; +} + +static int str_to_hex_digit(char c) +{ + if (c >= '0' && c <= '9') + c -= '0'; + else if (c >= 'a' && c <= 'f') + c = c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + c = c - 'A' + 10; + else + return -1; + + return c; +} + +static inline char hex_to_str_digit(const uint8_t c) +{ + return (c < 10) ? ((char)c + '0') : ((char)c + 'A' - 10); +} + +static char *hexdump_kek(const uint8_t * const kek) +{ + /* This is only for dumping masterkey values, so has no length checks. + * Do not use it for anything else. + * + * For convenience of possibly hand-copying and hand-retyping, the key + * is divided into 8 4-byte (8-character) groups. + */ + + static char buf[2 * KEK_LENGTH + 8]; + char *dst = buf; + + for (size_t i = 0; i < KEK_LENGTH; ++i) { + uint8_t b = kek[i]; + *dst++ = hex_to_str_digit(b >> 4); + *dst++ = hex_to_str_digit(b & 0xf); + if ((i & 3) == 3) + *dst++ = ' '; + } + buf[sizeof(buf) - 1] = '\0'; + + return buf; +} + +static int _masterkey_set(struct cli_def *cli, char *argv[], int argc, + char *label, hal_error_t (*writer)(const uint8_t * const, const size_t)) +{ + uint8_t buf[KEK_LENGTH] = {0}; + hal_error_t err; + + if (argc == 0) { + /* fill master key with yummy randomness */ + if ((err = hal_get_random(NULL, buf, sizeof(buf))) != LIBHAL_OK) { + cli_print(cli, "Error getting random key: %s", hal_error_string(err)); + return CLI_ERROR; + } + cli_print(cli, "Random key:\n%s", hexdump_kek(buf)); + } + + else { + /* input is 32 hex bytes, arranged however the user wants */ + size_t len = 0; + for (int i = 0; i < argc; ++i) { + for (char *cp = argv[i]; *cp != '\0'; ) { + int c; + if ((c = str_to_hex_digit(*cp++)) < 0) + goto errout; + buf[len] = c << 4; + if ((c = str_to_hex_digit(*cp++)) < 0) + goto errout; + buf[len] |= c & 0xf; + if (++len > KEK_LENGTH) + goto errout; + } + } + if (len < KEK_LENGTH) { + errout: + cli_print(cli, "Failed parsing master key, expected exactly %d hex bytes", KEK_LENGTH); + return CLI_ERROR; + } + + cli_print(cli, "Parsed key:\n%s", hexdump_kek(buf)); + } + + if ((err = writer(buf, sizeof(buf))) == LIBHAL_OK) { + cli_print(cli, "Master key set in %s memory", label); + } else { + cli_print(cli, "Failed writing key to %s memory: %s", label, hal_error_string(err)); + } + return CLI_OK; +} + +static hal_error_t _mkm_volatile_write(const uint8_t *kek, const size_t kek_len) +{ + return hal_keywrap_mkm_write(NULL, kek, kek_len); +} + +static int cmd_masterkey_set(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + command = command; + + return _masterkey_set(cli, argv, argc, "volatile", _mkm_volatile_write); +} + +static int cmd_masterkey_erase(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + hal_error_t err; + + command = command; + argv = argv; + argc = argc; + + if ((err = hal_keywrap_mkm_erase(NULL, KEK_LENGTH)) == LIBHAL_OK) { + cli_print(cli, "Erased master key from volatile memory"); + } else { + cli_print(cli, "Failed erasing master key from volatile memory: %s", hal_error_string(err)); + } + return CLI_OK; +} + +static int cmd_masterkey_unsecure_set(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + command = command; + + return _masterkey_set(cli, argv, argc, "flash", hal_mkm_flash_write); +} + +static int cmd_masterkey_unsecure_erase(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + hal_error_t err; + + command = command; + argv = argv; + argc = argc; + + if ((err = hal_mkm_flash_erase(KEK_LENGTH)) == LIBHAL_OK) { + cli_print(cli, "Erased unsecure master key from flash"); + } else { + cli_print(cli, "Failed erasing unsecure master key from flash: %s", hal_error_string(err)); + } + return CLI_OK; +} + +void configure_cli_masterkey(struct cli_def *cli) +{ + struct cli_command *c = cli_register_command(cli, NULL, "masterkey", NULL, 0, 0, NULL); + + /* masterkey status */ + cli_register_command(cli, c, "status", cmd_masterkey_status, 0, 0, "Show status of master key in RAM/flash"); + + /* masterkey set */ + cli_register_command(cli, c, "set", cmd_masterkey_set, 0, 0, "Set the master key in the volatile Master Key Memory"); + + /* masterkey erase */ + cli_register_command(cli, c, "erase", cmd_masterkey_erase, 0, 0, "Erase the master key from the volatile Master Key Memory"); + + struct cli_command *c_unsecure = cli_register_command(cli, c, "unsecure", NULL, 0, 0, NULL); + + /* masterkey unsecure set */ + cli_register_command(cli, c_unsecure, "set", cmd_masterkey_unsecure_set, 0, 0, "Set master key in unprotected flash memory (if unsure, DON'T)"); + + /* masterkey unsecure erase */ + cli_register_command(cli, c_unsecure, "erase", cmd_masterkey_unsecure_erase, 0, 0, "Erase master key from unprotected flash memory"); +} diff --git a/projects/hsm/mgmt-masterkey.h b/projects/hsm/mgmt-masterkey.h new file mode 100644 index 0000000..67835e9 --- /dev/null +++ b/projects/hsm/mgmt-masterkey.h @@ -0,0 +1,42 @@ +/* + * mgmt-masterkey.h + * ----------- + * Management CLI masterkeyellaneous functions. + * + * Copyright (c) 2016, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __STM32_CLI_MGMT_MASTERKEY_H +#define __STM32_CLI_MGMT_MASTERKEY_H + +#include <libcli.h> + +extern void configure_cli_masterkey(struct cli_def *cli); + +#endif /* __STM32_CLI_MGMT_MASTERKEY_H */ diff --git a/projects/hsm/mgmt-misc.c b/projects/hsm/mgmt-misc.c new file mode 100644 index 0000000..377af73 --- /dev/null +++ b/projects/hsm/mgmt-misc.c @@ -0,0 +1,259 @@ +/* + * mgmt-misc.c + * ----------- + * Miscellaneous CLI functions. + * + * Copyright (c) 2016-2018, NORDUnet A/S All rights reserved. + * Copyright: 2020, The Commons Conservancy Cryptech Project + * SPDX-License-Identifier: BSD-3-Clause + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define HAL_OK CMSIS_HAL_OK +#include "stm-init.h" +#include "stm-uart.h" +#include "mgmt-cli.h" +#include "mgmt-misc.h" +#undef HAL_OK + +#define HAL_OK LIBHAL_OK +#include "hal.h" +#include "hal_internal.h" +#undef HAL_OK + +#include <string.h> + + +int cli_receive_data(struct cli_def *cli, uint8_t *buf, size_t len, cli_data_callback data_callback) +{ + hal_crc32_t crc = 0, my_crc = hal_crc32_init(); + uint32_t filesize = 0, counter = 0; + size_t n = len; + + if (! control_mgmt_uart_dma_rx(DMA_RX_STOP)) { + cli_print(cli, "Failed stopping DMA"); + goto okay; + } + + cli_print(cli, "OK, write size (4 bytes), data in %li byte chunks, CRC-32 (4 bytes)", (uint32_t) n); + + if (uart_receive_bytes((void *) &filesize, sizeof(filesize), 2000) != CMSIS_HAL_OK) { + cli_print(cli, "Receive timed out"); + goto fail; + } + + cli_print(cli, "Send %li bytes of data", filesize); + + while (filesize) { + /* By initializing buf to the same value that erased flash has (0xff), we don't + * have to try and be smart when writing the last page of data to a flash memory. + */ + memset(buf, 0xff, len); + + if (filesize < n) n = filesize; + + if (uart_receive_bytes((void *) buf, n, 2000) != CMSIS_HAL_OK) { + cli_print(cli, "Receive timed out"); + goto fail; + } + filesize -= n; + my_crc = hal_crc32_update(my_crc, buf, n); + + /* After reception of a chunk but before ACKing we have "all" the time in the world to + * calculate CRC and invoke the data_callback. + */ + if (data_callback != NULL && data_callback(buf, n) != CMSIS_HAL_OK) { + cli_print(cli, "Data processing failed"); + goto okay; + } + + counter++; + uart_send_bytes((void *) &counter, 4); + } + + my_crc = hal_crc32_finalize(my_crc); + cli_print(cli, "Send CRC-32"); + uart_receive_bytes((void *) &crc, sizeof(crc), 2000); + cli_print(cli, "CRC-32 0x%x, calculated CRC 0x%x", (unsigned int) crc, (unsigned int) my_crc); + if (crc == my_crc) { + cli_print(cli, "CRC checksum MATCHED"); + } else { + cli_print(cli, "CRC checksum did NOT match"); + } + + okay: + control_mgmt_uart_dma_rx(DMA_RX_START); + return CLI_OK; + + fail: + control_mgmt_uart_dma_rx(DMA_RX_START); + return CLI_ERROR; +} + +#ifdef DO_PROFILING +static int cmd_profile_start(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + cli = cli; + command = command; + argv = argv; + argc = argc; + + extern uint32_t CRYPTECH_FIRMWARE_START; + extern char __etext; /* end of text/code symbol, defined by linker */ + extern void monstartup (size_t lowpc, size_t highpc); + monstartup((size_t)&CRYPTECH_FIRMWARE_START, (size_t)&__etext); + return CLI_OK; +} + +static int cmd_profile_stop(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + cli = cli; + command = command; + argv = argv; + argc = argc; + + extern void _mcleanup(void); + _mcleanup(); + return CLI_OK; +} + +#endif + +static int cmd_reboot(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + command = command; + argv = argv; + argc = argc; + + cli_print(cli, "\n\n\nRebooting\n\n\n"); + HAL_NVIC_SystemReset(); + + /*NOTREACHED*/ + return CLI_OK; +} + +static int cmd_rsa_blinding(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + if (argc != 1) { + cli_print(cli, "Wrong number of arguments (%i).", argc); + cli_print(cli, "Syntax: %s <on|off|clear>", command); + return CLI_ERROR; + } + + if (strcmp(argv[0], "on") == 0) + hal_rsa_set_blinding(1); + else if (strcmp(argv[0], "off") == 0) + hal_rsa_set_blinding(0); + else if (strcmp(argv[0], "clear") == 0) + hal_rsa_clear_blinding_cache(); + else { + cli_print(cli, "Argument must be 'on', 'off', or 'clear' - not '%s'", argv[0]); + return CLI_ERROR; + } + + return CLI_OK; +} + +static int cmd_rsa_crt(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int onoff; + + if (argc != 1) { + cli_print(cli, "Wrong number of arguments (%i).", argc); + cli_print(cli, "Syntax: %s <on|off>", command); + return CLI_ERROR; + } + + if (strcmp(argv[0], "on") == 0) + onoff = 1; + else if (strcmp(argv[0], "off") == 0) + onoff = 0; + else { + cli_print(cli, "Argument must be 'on' or 'off' - not '%s'", argv[0]); + return CLI_ERROR; + } + + hal_rsa_set_crt(onoff); + + return CLI_OK; +} + +static int cmd_rsa_modexpng(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + int onoff; + + if (argc != 1) { + cli_print(cli, "Wrong number of arguments (%i).", argc); + cli_print(cli, "Syntax: %s <on|off>", command); + return CLI_ERROR; + } + + if (strcmp(argv[0], "on") == 0) + onoff = 1; + else if (strcmp(argv[0], "off") == 0) + onoff = 0; + else { + cli_print(cli, "Argument must be 'on' or 'off' - not '%s'", argv[0]); + return CLI_ERROR; + } + + hal_error_t err; + if ((err = hal_modexp_use_modexpng(onoff)) == LIBHAL_OK) + return CLI_OK; + + cli_print(cli, hal_error_string(err)); + return CLI_ERROR; +} + +void configure_cli_misc(struct cli_def *cli) +{ +#ifdef DO_PROFILING + struct cli_command *c_profile = cli_register_command(cli, NULL, "profile", NULL, 0, 0, NULL); + + /* profile start */ + cli_register_command(cli, c_profile, "start", cmd_profile_start, 0, 0, "Start collecting profiling data"); + + /* profile stop */ + cli_register_command(cli, c_profile, "stop", cmd_profile_stop, 0, 0, "Stop collecting profiling data"); +#endif + + struct cli_command *c_rsa = cli_register_command(cli, NULL, "rsa", NULL, 0, 0, NULL); + + /* rsa blinding */ + cli_register_command(cli, c_rsa, "blinding", cmd_rsa_blinding, 0, 0, "Set use of RSA blinding"); + + /* rsa crt */ + cli_register_command(cli, c_rsa, "crt", cmd_rsa_crt, 0, 0, "Set use of RSA CRT"); + + /* rsa modexpng */ + cli_register_command(cli, c_rsa, "modexpng", cmd_rsa_modexpng, 0, 0, "Set use of ModExpNG"); + + /* reboot */ + cli_register_command(cli, NULL, "reboot", cmd_reboot, 0, 0, "Reboot the STM32"); +} + diff --git a/projects/hsm/mgmt-misc.h b/projects/hsm/mgmt-misc.h new file mode 100644 index 0000000..ef63a9e --- /dev/null +++ b/projects/hsm/mgmt-misc.h @@ -0,0 +1,47 @@ +/* + * mgmt-misc.h + * ----------- + * Management CLI miscellaneous functions. + * + * Copyright (c) 2016, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __STM32_CLI_MGMT_MISC_H +#define __STM32_CLI_MGMT_MISC_H + +#include <libcli.h> + +/* Write a chunk of received data to flash. */ +typedef HAL_StatusTypeDef (*cli_data_callback)(uint8_t *, size_t); + +extern int cli_receive_data(struct cli_def *cli, uint8_t *buf, size_t len, cli_data_callback data_callback); + +extern void configure_cli_misc(struct cli_def *cli); + +#endif /* __STM32_CLI_MGMT_MISC_H */ diff --git a/projects/hsm/mgmt-task.c b/projects/hsm/mgmt-task.c new file mode 100644 index 0000000..180c6d9 --- /dev/null +++ b/projects/hsm/mgmt-task.c @@ -0,0 +1,136 @@ +/* + * mgmt-task.c + * ----------- + * CLI 'task' functions. + * + * Copyright (c) 2016-2017, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Show the active tasks. This is mostly for debugging, and looks deeply + * into OS-level structures, but sometimes you just need to know... + */ + +#include "mgmt-cli.h" +#include "mgmt-task.h" +#include "task.h" + +static char *task_state[] = { + "INIT", + "WAITING", + "READY" +}; + +extern size_t request_queue_len(void); +extern size_t request_queue_max(void); + +static int cmd_task_show(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + command = command; + argv = argv; + argc = argc; + + cli_print(cli, "name state stack high water"); + cli_print(cli, "-------- -------- ----------------"); + + for (tcb_t *t = task_iterate(NULL); t != NULL; t = task_iterate(t)) { + cli_print(cli, "%-15s %-15s %d", + task_get_name(t), + task_state[task_get_state(t)], + task_get_stack_highwater(t)); + } + + cli_print(cli, " "); + cli_print(cli, "RPC request queue current length: %u", request_queue_len()); + cli_print(cli, "RPC request queue maximum length: %u", request_queue_max()); + + extern size_t uart_rx_max; + cli_print(cli, " "); + cli_print(cli, "UART receive queue maximum length: %u", uart_rx_max); + + size_t used, available; + extern void sdram_stats(size_t *used, size_t *available); + sdram_stats(&used, &available); + cli_print(cli, " "); + cli_print(cli, "SDRAM used: %u, available: %u", used, available); + + return CLI_OK; +} + +#ifdef DO_TASK_METRICS +static int cmd_task_show_metrics(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + command = command; + argv = argv; + argc = argc; + + struct task_metrics tm; + + task_get_metrics(&tm); + + cli_print(cli, "avg time between yields: %ld.%06ld sec", tm.avg.tv_sec, tm.avg.tv_usec); + cli_print(cli, "max time between yields: %ld.%06ld sec", tm.max.tv_sec, tm.max.tv_usec); + + return CLI_OK; +} + +static int cmd_task_reset_metrics(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + cli = cli; + command = command; + argv = argv; + argc = argc; + + task_reset_metrics(); + + return CLI_OK; +} +#endif + +void configure_cli_task(struct cli_def *cli) +{ + struct cli_command *c = cli_register_command(cli, NULL, "task", NULL, 0, 0, NULL); + + /* task show */ +#ifdef DO_TASK_METRICS + struct cli_command *c_show = +#endif + cli_register_command(cli, c, "show", cmd_task_show, 0, 0, "Show the active tasks"); + +#ifdef DO_TASK_METRICS + /* task show metrics */ + cli_register_command(cli, c_show, "metrics", cmd_task_show_metrics, 0, 0, "Show task metrics"); + + /* task reset */ + struct cli_command *c_reset = cli_register_command(cli, c, "reset", NULL, 0, 0, NULL); + + /* task reset metrics */ + cli_register_command(cli, c_reset, "metrics", cmd_task_reset_metrics, 0, 0, "Reset task metrics"); +#endif +} diff --git a/projects/hsm/mgmt-task.h b/projects/hsm/mgmt-task.h new file mode 100644 index 0000000..f903962 --- /dev/null +++ b/projects/hsm/mgmt-task.h @@ -0,0 +1,42 @@ +/* + * mgmt-task.h + * ----------- + * Management CLI 'task' functions. + * + * Copyright (c) 2016-2017, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __STM32_CLI_MGMT_TASK_H +#define __STM32_CLI_MGMT_TASK_H + +#include <libcli.h> + +extern void configure_cli_task(struct cli_def *cli); + +#endif /* __STM32_CLI_MGMT_TASK_H */ diff --git a/projects/libhal-test/Makefile b/projects/libhal-test/Makefile index f58f480..c5dbc84 100644 --- a/projects/libhal-test/Makefile +++ b/projects/libhal-test/Makefile @@ -1,12 +1,12 @@ -TEST = cores test-bus test-trng test-hash test-aes-key-wrap test-pbkdf2 test-ecdsa test-rsa test-mkmif +TEST = cores test-bus test-trng test-hash test-aes-key-wrap test-pbkdf2 test-ecdsa test-rsa -CFLAGS += -I $(LIBHAL_DIR) +CFLAGS += -I $(LIBHAL_SRC) LIBC_OBJS = printf.o gettimeofday.o -LIBS += $(LIBHAL_DIR)/libhal.a $(LIBTFM_DIR)/libtfm.a +LIBS += $(LIBHAL_BLD)/libhal.a $(LIBTFM_BLD)/libtfm.a all: $(TEST:=.elf) -vpath %.c $(LIBHAL_DIR)/tests $(LIBHAL_DIR)/utils +vpath %.c $(LIBHAL_SRC)/tests $(LIBHAL_SRC)/utils # .mo extension for files with main() that need to be wrapped as __main() %.mo: %.c @@ -17,9 +17,6 @@ vpath %.c $(LIBHAL_DIR)/tests $(LIBHAL_DIR)/utils $(OBJCOPY) -O binary $*.elf $*.bin $(SIZE) $*.elf -# don't automatically delete objects, to avoid a lot of unnecessary rebuilding -.SECONDARY: $(BOARD_OBJS) $(LIBC_OBJS) - clean: rm -f *.o *.mo rm -f *.elf diff --git a/projects/libhal-test/gettimeofday.c b/projects/libhal-test/gettimeofday.c index b13485d..f7a87f1 100644 --- a/projects/libhal-test/gettimeofday.c +++ b/projects/libhal-test/gettimeofday.c @@ -56,6 +56,8 @@ int gettimeofday(struct timeval *tv, struct timezone *tz) { uint32_t tick = HAL_GetTick(); /* uptime in ms */ + tz = tz; + tv->tv_sec = tick / 1000; tv->tv_usec = (tick % 1000) * 1000; diff --git a/projects/libhal-test/main.c b/projects/libhal-test/main.c index a40871b..c0d9330 100644 --- a/projects/libhal-test/main.c +++ b/projects/libhal-test/main.c @@ -43,18 +43,8 @@ extern void __main(void); int main(void) { stm_init(); - -#ifdef TARGET_CRYPTECH_DEV_BRIDGE - // Blink blue LED for six seconds to not upset the Novena at boot. - led_on(LED_BLUE); - for (int i = 0; i < 12; i++) { - HAL_Delay(500); - led_toggle(LED_BLUE); - } - led_off(LED_BLUE); -#endif + HAL_Delay(500); led_on(LED_GREEN); - fmc_init(); __main(); diff --git a/projects/libhal-test/printf.c b/projects/libhal-test/printf.c index 5a15d12..85af09b 100644 --- a/projects/libhal-test/printf.c +++ b/projects/libhal-test/printf.c @@ -244,7 +244,7 @@ OK, I found my mistake. The math here is _always_ unsigned */ if (precision != 0)
{
width = max(width, precision);
- if (precision > strlen(where))
+ if (precision > strlen((const char *)where))
flags |= PR_LZ;
precision = 0;
}
@@ -295,7 +295,7 @@ EMIT2: if((flags & PR_LJ) == 0) count++;
}
/* emit string/char/converted number */
- for(int i = (flags & PR_WS) ? 1 : 0;
+ for(unsigned i = (flags & PR_WS) ? 1 : 0;
i < length; ++i)
{
fn(*where++, &ptr);
@@ -362,6 +362,8 @@ You must write your own putchar() *****************************************************************************/
int vprintf_help(unsigned c, void **ptr)
{
+ ptr = ptr;
+
putchar(c);
return 0 ;
}
diff --git a/projects/rtos-test/Makefile b/projects/rtos-test/Makefile deleted file mode 100644 index dd2cab5..0000000 --- a/projects/rtos-test/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -TEST = thread-test semaphore-test mutex-test - -all: $(TEST:=.elf) - -%.elf: %.o $(BOARD_OBJS) $(LIBS) - $(CC) $(CFLAGS) $^ -o $@ -T$(LDSCRIPT) -g -Wl,-Map=$*.map - $(OBJCOPY) -O ihex $*.elf $*.hex - $(OBJCOPY) -O binary $*.elf $*.bin - $(OBJDUMP) -St $*.elf >$*.lst - $(SIZE) $*.elf - -clean: - rm -f *.o - rm -f *.elf - rm -f *.hex - rm -f *.bin - rm -f *.map - rm -f *.lst diff --git a/projects/rtos-test/mutex-test.c b/projects/rtos-test/mutex-test.c deleted file mode 100644 index 402f9ba..0000000 --- a/projects/rtos-test/mutex-test.c +++ /dev/null @@ -1,40 +0,0 @@ -#include "cmsis_os.h" - -#include "stm-init.h" -#include "stm-uart.h" - -osMutexId stdio_mutex; -osMutexDef(stdio_mutex); - -void notify(const char* name, int state) { - osMutexWait(stdio_mutex, osWaitForever); - //printf("%s: %d\n\r", name, state); - uart_send_string(name); - uart_send_string(": "); - uart_send_integer(state, 1); - uart_send_string("\r\n"); - osMutexRelease(stdio_mutex); -} - -void test_thread(void const *args) { - while (1) { - notify((const char*)args, 0); osDelay(1000); - notify((const char*)args, 1); osDelay(1000); - } -} - -void t2(void const *argument) {test_thread("Th 2");} -osThreadDef(t2, osPriorityNormal, DEFAULT_STACK_SIZE); - -void t3(void const *argument) {test_thread("Th 3");} -osThreadDef(t3, osPriorityNormal, DEFAULT_STACK_SIZE); - -int main() { - stm_init(); - stdio_mutex = osMutexCreate(osMutex(stdio_mutex)); - - osThreadCreate(osThread(t2), NULL); - osThreadCreate(osThread(t3), NULL); - - test_thread((void *)"Th 1"); -} diff --git a/projects/rtos-test/semaphore-test.c b/projects/rtos-test/semaphore-test.c deleted file mode 100644 index 3a3b5de..0000000 --- a/projects/rtos-test/semaphore-test.c +++ /dev/null @@ -1,34 +0,0 @@ -#include "cmsis_os.h" - -#include "stm-init.h" -#include "stm-uart.h" - -osSemaphoreId two_slots; -osSemaphoreDef(two_slots); - -void test_thread(void const *name) { - while (1) { - osSemaphoreWait(two_slots, osWaitForever); - //printf("%s\n\r", (const char*)name); - uart_send_string((const char*)name); - uart_send_string("\r\n"); - osDelay(1000); - osSemaphoreRelease(two_slots); - } -} - -void t2(void const *argument) {test_thread("Th 2");} -osThreadDef(t2, osPriorityNormal, DEFAULT_STACK_SIZE); - -void t3(void const *argument) {test_thread("Th 3");} -osThreadDef(t3, osPriorityNormal, DEFAULT_STACK_SIZE); - -int main (void) { - stm_init(); - two_slots = osSemaphoreCreate(osSemaphore(two_slots), 2); - - osThreadCreate(osThread(t2), NULL); - osThreadCreate(osThread(t3), NULL); - - test_thread((void *)"Th 1"); -} diff --git a/projects/rtos-test/thread-test.c b/projects/rtos-test/thread-test.c deleted file mode 100644 index 8b31a26..0000000 --- a/projects/rtos-test/thread-test.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "cmsis_os.h" - -#include "stm-init.h" -#include "stm-led.h" - -void led2_thread(void const *args) -{ - while (1) { - led_toggle(LED_BLUE); - osDelay(1000); - } -} -osThreadDef(led2_thread, osPriorityNormal, DEFAULT_STACK_SIZE); - -int main() -{ - stm_init(); - osThreadCreate(osThread(led2_thread), NULL); - - while (1) { - led_toggle(LED_GREEN); - osDelay(500); - } -} |