diff options
-rw-r--r-- | README.md | 163 | ||||
-rwxr-xr-x | libraries/libprof/profile-runner.py | 73 | ||||
-rw-r--r-- | libraries/mbed/Makefile | 2 | ||||
-rw-r--r-- | projects/board-test/fmc-perf.c | 4 | ||||
-rw-r--r-- | projects/board-test/fmc-test.c | 4 | ||||
-rw-r--r-- | projects/cli-test/test-fmc.c | 10 | ||||
-rw-r--r-- | projects/hsm/hsm.c | 14 | ||||
-rw-r--r-- | stm-fmc.c | 80 | ||||
-rw-r--r-- | stm-fmc.h | 58 |
9 files changed, 254 insertions, 154 deletions
@@ -1,80 +1,127 @@ -STM32 software for dev-bridge/Alpha board -========================================= +STM32 firmware for Cryptech Alpha board +======================================= -The dev-bridge board is a daughterboard for the Novena, which talks to the -Novena's FPGA through the high-speed expansion connector. +The Alpha board is our first full prototype for an open-source hardware +security module (HSM). It is a custom board with an STM32 Cortex-M4 +microcontroller and an Artix-7 FPGA, flash-based keystore, separate memory +for the Key Encryption Key, etc. See the `hardware` repository for +schematics and production files. See the wiki for design documents. -The Alpha board is a stand-alone board with an Artix-7 FPGA, a STM32 Cortex-M4 -microcontroller, two USB interfaces etc. - -See user/ft/stm32-dev-bridge/hardware/rev01 for schematics of the bridge -board. There will be more information on the wiki shortly. +The code in this repository builds the firmware that provides the HSM +functionality on the Alpha board. +There is some residual code here to support the "dev-bridge" board, a +daughterboard for the Novena, which talks to the Novena's FPGA through the +high-speed expansion connector. Only a few of these boards were ever made, +and all development/testing ceased as soon as the Alpha became available, +so the dev-bridge should be considered deprecated, and support may be +removed in the future. Copyrights ========== The license for all work done on this in the CrypTech project is a -3-clause BSD license (see LICENSE.txt for details). Some files have -been generated using the STMicroelectronics initialization code -generator STM32CubeMX and thus have additional copyright header(s). +3-clause BSD license. + +Third-party components, as well as code generated using the +STMicroelectronics initialization code generator STM32CubeMX, or adapted +from STM example/support code, may have different licensing, detailed +below. + +Components +========== + +Libraries +--------- + +* `mbed` - A stripped down copy of the ARM CMSIS library, copied from the + mbed github (see `libraries/mbed/README.txt` for details). The bulk of + this library is covered under 3-clause BSD licenses from either ARM or + STMicroelectronics, but one file is covered under an Apache license from + ARM. + +* `libhal` - Build directory for our own Hardware Adaption Library + (hardware-independent Cryptech components). Source is expected to be in + `sw/libhal`. + +* `libtfm` - Build directory for "Tom's Fast Math", which is used heavily + for bignum math in the RSA and ECDSA code. This code is covered under an + unrestricted public domain license, and source is expected to be in + `sw/thirdparty/libtfm`. + +* `libcli` - Build directory for a third-party Command Line Interface + library. The source is not currently under `sw/thirdparty` because the + license is LGPLv2.1; we are negotiating to see if we can get a + BSD-compatible license for it. + +* `libprof` - A port of the `gmon` profiling package, to be used in + development only, not in production code (obviously). The licensing is a + mix of BSD and "Cygwin license", which now seems to be LGPLv3. + +Projects +-------- -The "Noise generator" and "Amplifier" parts of the circuit diagram are -copied from Benedikt Stockebrand's ARRGH project. ARRGH copyright -statement is included in LICENSE.txt. +These directories build different firmware images for the Alpha board. -A stripped down copy of the ARM CMSIS library version 3.20 is included -in the Drivers/CMSIS/ directory. Unused parts (and documentation etc.) -have been removed, but every attempt have been made to keep any -licensing information intact. See in particular the file -Drivers/CMSIS/CMSIS END USER LICENCE AGREEMENT.pdf. +* `hsm` - Firmware providing HSM functionality. Clients communicate via + RPC requests on the USER USB port, or interactively on the MGMT USB + port. -A full copy of the STM32F4xx HAL Drivers is included in the -Drivers/STM32F4xx_HAL_Driver/ directory. +* `bootloader` - The first thing that runs on the device. It either starts + the primary firmware, or installs new firmware. +* `board-test` - Tests of hardware components. + +* `cli-test` - Test of the CLI itself, plus some interactive tests of + hardware components. Duplicates way too much of the HSM CLI. + +* `libhal-test` - A framework for running the libhal component + tests. Hasn't been run in a while, probably still works. Building ======== -The following packages need to be installed (on Ubuntu 14.04): +Our primary build environments are Debian and Ubuntu, but this should work +on any system with Gnu tools installed. - apt-get install gcc-arm-none-eabi gdb-arm-none-eabi openocd +The following packages need to be installed: -To build the source code, issue "make" from the top level directory -(where this file is). The first time, this will build the complete STM -CMSIS library. A subsequent "make clean" will *not* clean away the CMSIS -library, but a "make distclean" will. + $ apt-get install gcc-arm-none-eabi gdb-arm-none-eabi openocd +The Makefile assumes that all Cryptech repositories have been fetched into +a canonical directory structure, e.g. `libhal` and `thirdparty` are +siblings to this directory, under `sw`. + +To build the source code, issue `make` from the top level directory +(where this file is). The first time, this will build the complete STM +CMSIS library. A subsequent `make clean` will *not* clean away the CMSIS +library, but a `make distclean` will. Installing ========== -Do "bin/flash-target" from the top level directory (where this file is) +Do `bin/flash-target` from the top level directory (where this file is) to flash a built image into the microcontroller. See the section ST-LINK below for information about the actual hardware programming device needed. -Example loading the bootloader and the led-test firmware to get some LEDs -flashing: +Example loading the HSM firmware: - $ make bootloader board-test - $ ./bin/flash-target projects/board-test/led-test - $ ./bin/flash-target projects/bootloader/bootloader + $ make hsm + $ ./bin/flash-target projects/hsm/hsm At this point, the STM32 will reset into the bootloader which flashes the -blue LED five times in one second, and then execution of the LED test -firmware will begin. The LED test firmware will flash the green, yellow, -red and blue LEDs in order until the end of time. +blue LED five times in one second, and then jumps to the primary firmware. Once the bootloader is installed, regular firmware can be loaded without an ST-LINK cable like this: - $ ./bin/dfu projects/board-test/led-test.bin + $ cryptech_upload --firmware -i projects/hsm/hsm.bin Then reboot the Alpha board. - ST-LINK -======= +------- + To program the MCU, an ST-LINK adapter is used. The cheapest way to get one is to buy an evaluation board with an ST-LINK integrated, and pinouts to program external chips. This should work with any evaluation board from @@ -86,7 +133,7 @@ printed on the circuit board. The pin-outs is shown on the circuit board (follow the thin white line from J1 to the white box with STM32_SWD written in it). From left to right, the pins are - 3V3, CLK, GND, I/O, NRST and N/C + 3V3, CLK, GND, I/O, NRST and N/C This matches the pin-out on the DISCO and NUCLEO boards we have tried. @@ -94,34 +141,30 @@ First remove the pair of ST-LINK jumpers (CN4 on the DISCO, CN2 on the NUCLEO). Then find the 6-pin SWD header on the left of the STM board (CN2 on the DISCO, CN4 on the NUCLEO), and connect them to the Alpha board: - NUCLEO / DISCO CRYPTECH ALPHA - -------------- -------------- -* 1 VDD_TARGET <-> 3V3 -* 2 SWCLK / T_JTCK <-> CLK -* 3 GND <-> GND -* 4 SWDIO / T_JTMS <-> IO -* 5 NRST / T_NRST <-> NRST - -N/C (pin 6) means Not Connected. + NUCLEO / DISCO CRYPTECH ALPHA + -------------- -------------- + * 1 VDD_TARGET <-> 3V3 + * 2 SWCLK / T_JTCK <-> CLK + * 3 GND <-> GND + * 4 SWDIO / T_JTMS <-> IO + * 5 NRST / T_NRST <-> NRST + * 6 N/C The Alpha board should be powered on before attempting to flash it. - Debugging the firmware -====================== - -This site shows several ways to use various debuggers to debug the -firmware in an STM32: +---------------------- - http://fun-tech.se/stm32/OpenOCD/gdb.php +[This site](http://fun-tech.se/stm32/OpenOCD/gdb.php) shows several ways +to use various debuggers to debug the firmware in an STM32. There is a shell script called 'bin/debug' that starts an OpenOCD server and GDB. Example: - $ ./bin/debug projects/board-test/led-test + $ ./bin/debug projects/hsm/hsm -Once in GDB, issue "monitor reset halt" to reset the STM32 before debugging. +Once in GDB, issue `monitor reset halt` to reset the STM32 before debugging. Remember that the first code to run will be the bootloader, but if you do -e.g. "break main" and "continue" you will end up in led-test main() after -the bootloader has jumped there. +e.g. `break main` and `continue` you will end up in main() after the +bootloader has jumped there. diff --git a/libraries/libprof/profile-runner.py b/libraries/libprof/profile-runner.py new file mode 100755 index 0000000..e7ec55f --- /dev/null +++ b/libraries/libprof/profile-runner.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python + +""" +Tool to run some test code under the profiler on the Cryptech Alpha. + +This assumes that the HSM code was built with DO_PROFILING=1, and +requires an ST-LINK programmer and the Python pexpect package. +""" + +import subprocess +import argparse +import pexpect +import atexit +import time +import sys +import os + +parser = argparse.ArgumentParser(description = __doc__, + formatter_class = argparse.ArgumentDefaultsHelpFormatter) +parser.add_argument("--hsm-elf", + default = os.path.expanduser("~/git.cryptech.is/sw/stm32/projects/hsm/hsm.elf"), + help = "where you keep the profiled hsm.elf binary") +parser.add_argument("--openocd-config", + default = "/usr/share/openocd/scripts/board/st_nucleo_f401re.cfg", + help = "OpenOCD ST-LINK configuration file ") +parser.add_argument("--gmon-output", + default = "profile-runner.gmon", + help = "where to leave raw profiler output") +parser.add_argument("--gprof-output", type = argparse.FileType("w"), + default = "profile-runner.gprof", + help = "where to leave profiler output after processing with gprof") +parser.add_argument("--user", + default = "wheel", + help = "user name for logging in on the HSM console") +parser.add_argument("--pin", + default = "fnord", + help = "PIN for logging in on the HSM console") +parser.add_argument("command", nargs = 1, + help = "test program to run with profiling") +parser.add_argument("arguments", nargs = argparse.REMAINDER, + help = argparse.SUPPRESS) +args = parser.parse_args() + +openocd = subprocess.Popen(("openocd", "-f", args.openocd_config)) +atexit.register(openocd.terminate) + +time.sleep(5) + +telnet = pexpect.spawn("telnet localhost 4444") +telnet.expect(">") +telnet.sendline("arm semihosting enable") +telnet.expect(">") +telnet.sendline("exit") + +console = pexpect.spawn("cryptech_console") +console.sendline("") +if console.expect(["cryptech>", "Username:"]): + console.sendline(args.user) + console.expect("Password:") + console.sendline(args.pin) + console.expect("cryptech>") +console.sendline("profile start") +console.expect("cryptech>") + +cmd = args.command + args.arguments +sys.stderr.write("Running command: {}\n".format(" ".join(cmd))) +subprocess.check_call(cmd) + +console.sendline("profile stop") +console.expect("cryptech>", timeout = 900) +os.rename("gmon.out", args.gmon_output) + +subprocess.check_call(("gprof", args.hsm_elf, args.gmon_output), stdout = args.gprof_output) diff --git a/libraries/mbed/Makefile b/libraries/mbed/Makefile index 873359d..eb2bd2b 100644 --- a/libraries/mbed/Makefile +++ b/libraries/mbed/Makefile @@ -5,7 +5,7 @@ CFLAGS += -Wno-unused-parameter ########################################### -vpath %.c targets/cmsis/TARGET_STM/TARGET_STM32F4 targets/cmsis/TARGET_STM/TARGET_STM32F4/TARGET_CRYPTECH_DEV_BRIDGE +vpath %.c $(CMSIS_DIR) $(BOARD_DIR) SRCS = stm32f4xx_hal.c \ stm32f4xx_hal_adc.c \ diff --git a/projects/board-test/fmc-perf.c b/projects/board-test/fmc-perf.c index e87f282..71d0149 100644 --- a/projects/board-test/fmc-perf.c +++ b/projects/board-test/fmc-perf.c @@ -31,7 +31,7 @@ static void sanity(void) uint32_t rnd, data; rnd = random(); - if (fmc_write_32(0, &rnd) != 0) { + if (fmc_write_32(0, rnd) != 0) { uart_send_string("fmc_write_32 failed\r\n"); Error_Handler(); } @@ -88,7 +88,7 @@ static void test_write(void) uint32_t i; for (i = 0; i < TEST_NUM_ROUNDS; ++i) { - if (fmc_write_32(0, &i) != 0) { + if (fmc_write_32(0, i) != 0) { uart_send_string("fmc_write_32 failed\r\n"); Error_Handler(); } diff --git a/projects/board-test/fmc-test.c b/projects/board-test/fmc-test.c index 2002f57..1421db0 100644 --- a/projects/board-test/fmc-test.c +++ b/projects/board-test/fmc-test.c @@ -171,7 +171,7 @@ 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); + ok = fmc_write_32(0, rnd); if (ok != 0) break; // read value from fpga @@ -239,7 +239,7 @@ 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); + ok = fmc_write_32(rnd, buf); if (ok != 0) break; // read value from fpga diff --git a/projects/cli-test/test-fmc.c b/projects/cli-test/test-fmc.c index 87f80ce..6773cfc 100644 --- a/projects/cli-test/test-fmc.c +++ b/projects/cli-test/test-fmc.c @@ -76,7 +76,7 @@ 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) +static int _write_then_read(struct cli_def *cli, uint32_t addr, uint32_t write_buf, uint32_t *read_buf) { int ok; @@ -115,7 +115,7 @@ int test_fpga_data_bus(struct cli_def *cli, uint32_t test_rounds) } /* 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; + if (! _write_then_read(cli, 0, rnd, &buf)) break; /* compare (abort testing in case of error) */ data_diff = buf ^ rnd; @@ -137,7 +137,7 @@ int test_fpga_data_bus(struct cli_def *cli, uint32_t test_rounds) for (i = 0; i < 31; i++) { data = 1 << i; - if (! _write_then_read(cli, 0, &data, &buf)) break; + 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); @@ -181,7 +181,7 @@ int test_fpga_address_bus(struct cli_def *cli, uint32_t test_rounds) /* 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; + if (! _write_then_read(cli, addr, dummy, &buf)) break; /* fpga receives address of 32-bit word, while we need byte address here to compare @@ -210,7 +210,7 @@ int test_fpga_address_bus(struct cli_def *cli, uint32_t test_rounds) shifted_addr = addr << 2; - if (! _write_then_read(cli, shifted_addr, &dummy, &buf)) break; + 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); diff --git a/projects/hsm/hsm.c b/projects/hsm/hsm.c index c9d8d28..29509e8 100644 --- a/projects/hsm/hsm.c +++ b/projects/hsm/hsm.c @@ -99,7 +99,7 @@ typedef struct rpc_buffer_s { } rpc_buffer_t; /* RPC input (requst) buffers */ -static rpc_buffer_t ibufs[NUM_RPC_TASK]; +static rpc_buffer_t *ibufs; /* ibuf queue structure */ typedef struct { @@ -439,6 +439,12 @@ 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); } @@ -455,9 +461,13 @@ int main(void) 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 < sizeof(ibufs)/sizeof(ibufs[0]); ++i) + for (size_t i = 0; i < NUM_RPC_TASK; ++i) ibuf_put(&ibuf_waiting, &ibufs[i]); /* Create the rpc dispatch worker tasks. */ @@ -164,83 +164,3 @@ void fmc_init(void) // initialize fmc HAL_SRAM_Init(&_fmc_fpga_inst, &fmc_timing, NULL); } - - -static HAL_StatusTypeDef _fmc_nwait_idle(void) -{ - int cnt; - - // poll NWAIT (number of iterations is limited) - for (cnt=0; cnt<FMC_FPGA_NWAIT_MAX_POLL_TICKS; cnt++) - { - // read pin state - if (HAL_GPIO_ReadPin(FMC_GPIO_PORT_NWAIT, FMC_GPIO_PIN_NWAIT) == FMC_NWAIT_IDLE) - return HAL_OK; - } - - return HAL_ERROR; -} - -HAL_StatusTypeDef fmc_write_32(uint32_t addr, uint32_t *data) -{ - // calculate target fpga address - uint32_t ptr = FMC_FPGA_BASE_ADDR + (addr & FMC_FPGA_ADDR_MASK); - - __disable_irq(); - - HAL_StatusTypeDef status = - // write data to fpga - HAL_SRAM_Write_32b(&_fmc_fpga_inst, (uint32_t *)ptr, data, 1); - if (status == HAL_OK) - // wait for transaction to complete - status = _fmc_nwait_idle(); - - __enable_irq(); - - return status; -} - -static inline HAL_StatusTypeDef _fmc_read_32(uint32_t *ptr, uint32_t *data) -{ - HAL_StatusTypeDef status = - // read data from fpga - HAL_SRAM_Read_32b(&_fmc_fpga_inst, (uint32_t *)ptr, data, 1); - if (status == HAL_OK) - // wait for transaction to complete - status = _fmc_nwait_idle(); - - return status; -} - -HAL_StatusTypeDef fmc_read_32(uint32_t addr, uint32_t *data) -{ - // calculate target fpga address - uint32_t ptr = FMC_FPGA_BASE_ADDR + (addr & FMC_FPGA_ADDR_MASK); - - /* Pavel says: - * The short story is like, on one hand STM32 has a dedicated FMC_NWAIT - * pin, that can be used in variable-latency data transfer mode. On the - * other hand STM32 also has a very nasty hardware bug associated with - * FMC_WAIT, that causes processor to freeze under certain conditions. - * Because of this FMC_NWAIT cannot be used and FPGA can't properly signal - * to STM32, when data transfer is done. Because of that we have to read - * two times. - */ - - /* Add some level of reentrancy protection. When running under a - * preemptive multitasker, with two threads banging on the fpga, we appear - * to sometimes read the wrong value. I think this is because the second - * read counts on the first read to put the correct value on the address - * bus. - */ - __disable_irq(); - - HAL_StatusTypeDef status = - _fmc_read_32((uint32_t *)ptr, data); - if (status == HAL_OK) - status = _fmc_read_32((uint32_t *)ptr, data); - - __enable_irq(); - - return status; -} @@ -57,7 +57,61 @@ extern void fmc_init(void); -extern HAL_StatusTypeDef fmc_write_32(uint32_t addr, uint32_t *data); -extern HAL_StatusTypeDef fmc_read_32(uint32_t addr, uint32_t *data); + +static inline HAL_StatusTypeDef _fmc_nwait_idle(void) +{ + int cnt; + + // poll NWAIT (number of iterations is limited) + for (cnt=0; cnt<FMC_FPGA_NWAIT_MAX_POLL_TICKS; cnt++) + { + // read pin state + if (HAL_GPIO_ReadPin(FMC_GPIO_PORT_NWAIT, FMC_GPIO_PIN_NWAIT) == FMC_NWAIT_IDLE) + return HAL_OK; + } + + return HAL_ERROR; +} + +static inline HAL_StatusTypeDef fmc_write_32(const uint32_t addr, const uint32_t data) +{ + // calculate target fpga address + uint32_t *ptr = (uint32_t *) (FMC_FPGA_BASE_ADDR + (addr & FMC_FPGA_ADDR_MASK)); + + // write data to fpga + *ptr = data; + + // wait for transaction to complete + return _fmc_nwait_idle(); +} + +static inline HAL_StatusTypeDef fmc_read_32(const uint32_t addr, uint32_t * const data) +{ + // calculate target fpga address + uint32_t *ptr = (uint32_t *) (FMC_FPGA_BASE_ADDR + (addr & FMC_FPGA_ADDR_MASK)); + + /* Pavel says: + * The short story is like, on one hand STM32 has a dedicated FMC_NWAIT + * pin, that can be used in variable-latency data transfer mode. On the + * other hand STM32 also has a very nasty hardware bug associated with + * FMC_WAIT, that causes processor to freeze under certain conditions. + * Because of this FMC_NWAIT cannot be used and FPGA can't properly signal + * to STM32, when data transfer is done. Because of that we have to read + * two times. + */ + + HAL_StatusTypeDef status; + + *data = *ptr; + status = _fmc_nwait_idle(); + + if (status != HAL_OK) + return status; + + *data = *ptr; + status = _fmc_nwait_idle(); + + return status; +} #endif /* __STM_FMC_H */ |