diff options
author | Fredrik Thulin <fredrik@thulin.net> | 2016-07-09 20:26:57 +0200 |
---|---|---|
committer | Fredrik Thulin <fredrik@thulin.net> | 2016-07-09 20:26:57 +0200 |
commit | dcc6cf1b97935bc3a663714f596cacba619a8b3d (patch) | |
tree | c38a88e9ae4a1e012cf2cceb2da3c9c10d2bb27a /projects | |
parent | 3ea10bf4fba185a8bfbb33a8c67a69f70b95755a (diff) | |
parent | df9e82b28aad97664cba1fc238bc4f2563c1de7c (diff) |
Merge branch 'master' of git.cryptech.is.:sw/stm32
Diffstat (limited to 'projects')
-rwxr-xr-x | projects/hsm/cryptech_miniterm | 45 | ||||
-rwxr-xr-x | projects/hsm/cryptech_probe | 145 | ||||
-rwxr-xr-x | projects/hsm/cryptech_upload | 213 | ||||
-rw-r--r-- | projects/hsm/hsm.c | 148 | ||||
-rw-r--r-- | projects/hsm/mgmt-fpga.c | 27 | ||||
-rw-r--r-- | projects/hsm/mgmt-misc.c | 19 |
6 files changed, 444 insertions, 153 deletions
diff --git a/projects/hsm/cryptech_miniterm b/projects/hsm/cryptech_miniterm new file mode 100755 index 0000000..b8ea3b1 --- /dev/null +++ b/projects/hsm/cryptech_miniterm @@ -0,0 +1,45 @@ +#!/usr/bin/env python +# +# 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..14dae01 --- /dev/null +++ b/projects/hsm/cryptech_probe @@ -0,0 +1,145 @@ +#!/usr/bin/env python +# +# 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 + +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 = chr(0300) # Indicates end of SLIP packet +SLIP_ESC = chr(0333) # Indicates byte stuffing +SLIP_ESC_END = chr(0334) # ESC ESC_END means END data byte +SLIP_ESC_ESC = chr(0335) # ESC ESC_ESC means ESC data byte + +Control_U = chr(0025) # Console: clear line +Control_M = chr(0015) # Console: end of line + +RPC_query = chr(0) * 8 # client_handle = 0, function code = RPC_FUNC_GET_VERSION +RPC_reply = chr(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) + + for c in probe_string: + tty.write(c) + time.sleep(0.1) + + response = tty.read(args.read_buffer_size) + if args.debug: + sys.stderr.write("Received from {}: {!r} ({})\n".format(port, response, ":".join("{:02x}".format(ord(c)) for c in response))) + + # Check whether we got a known console prompt. + + is_cty = any(prompt in response for prompt in ("Username:", "Password:", "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 + 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() + +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 index 722e37b..6d6d4f7 100755 --- a/projects/hsm/cryptech_upload +++ b/projects/hsm/cryptech_upload @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # # Copyright (c) 2016, NORDUnet A/S All rights reserved. # @@ -27,16 +27,21 @@ # 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 new a firmware image or FPGA bitstream +Utility to upload a new firmware image or FPGA bitstream. """ + import os import sys import time import struct import serial -import argparse import getpass +import os.path +import tarfile +import argparse +import platform from binascii import crc32 @@ -48,35 +53,51 @@ def parse_args(): """ Parse the command line arguments """ - parser = argparse.ArgumentParser(description = "File uploader", - add_help = True, + + 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', - dest='device', - default='/dev/ttyUSB0', - help='Name of management port USB serial device', + 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('--fpga', - dest='fpga', - action='store_true', default=False, - help='Upload FPGA bitstream', + parser.add_argument("--firmware-tarball", + type = argparse.FileType("rb"), + default = default_tarball, + help = "Location of firmware tarball", ) - parser.add_argument('--firmware', - dest='firmware', - action='store_true', default=False, - help='Upload firmware image', - ) - parser.add_argument('--bootloader', - dest='bootloader', - action='store_true', default=False, - help='Upload bootloader image', + + 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", ) - # positional argument(s) - parser.add_argument('filename') + parser.add_argument("-i", "--explicit-image", + type = argparse.FileType("rb"), + help = "Explicit source image file for upload, overrides firmware tarball") return parser.parse_args() @@ -84,13 +105,13 @@ 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(data.encode("hex"))) #else: # print("Wrote {!r}".format(data)) def _read(dst): - res = '' + res = "" x = dst.read(1) while not x: x = dst.read(1) @@ -104,50 +125,51 @@ pin = None def _execute(dst, cmd): global pin - _write(dst, '\r') + _write(dst, "\r") prompt = _read(dst) - if prompt.endswith('Username: '): - _write(dst, 'so\r') + if prompt.endswith("Username: "): + _write(dst, "so\r") prompt = _read(dst) - if prompt.endswith('Password: '): + if prompt.endswith("Password: "): if not pin: - pin = getpass.getpass('SO PIN: ') - _write(dst, pin + '\r') + pin = getpass.getpass("SO PIN: ") + _write(dst, pin + "\r") prompt = _read(dst) - if not prompt.endswith('> '): - #sys.stderr.write('Device does not seem to be ready for a file transfer (got {!r})\n'.format(prompt)) + if not prompt.endswith(("> ", "# ")): + print("Device does not seem to be ready for a file transfer (got {!r})".format(prompt)) return prompt - _write(dst, cmd + '\r') + _write(dst, cmd + "\r") response = _read(dst) return response -def send_file(filename, args, dst): - s = os.stat(filename) - size = s.st_size - src = open(filename, 'rb') +def send_file(src, size, args, dst): if args.fpga: chunk_size = FPGA_CHUNK_SIZE - response = _execute(dst, 'fpga bitstream upload') + response = _execute(dst, "fpga bitstream upload") elif args.firmware: chunk_size = FIRMWARE_CHUNK_SIZE - response = _execute(dst, 'firmware upload') - if 'Rebooting' in response: - response = _execute(dst, 'firmware upload') + response = _execute(dst, "firmware upload") + if "Rebooting" in response: + response = _execute(dst, "firmware upload") elif args.bootloader: chunk_size = FIRMWARE_CHUNK_SIZE - response = _execute(dst, 'bootloader upload') - if 'Access denied' in response: - print 'Access denied' + response = _execute(dst, "bootloader upload") + if "Access denied" in response: + print "Access denied" return False - if not 'OK' in response: - sys.stderr.write('Device did not accept the upload command (got {!r})\n'.format(response)) + if not "OK" in response: + print("Device did not accept the upload command (got {!r})".format(response)) return False crc = 0 counter = 0 # 1. Write size of file (4 bytes) - _write(dst, struct.pack('<I', size)) - _read(dst) + _write(dst, struct.pack("<I", size)) + response = _read(dst) + if not response.startswith("Send "): + print response + return False + # 2. Write file contents while calculating CRC-32 while True: data = src.read(chunk_size) @@ -160,13 +182,13 @@ def send_file(filename, args, dst): ack_bytes = dst.read(4) if len(ack_bytes) == 4: break - 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] + 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 @@ -175,35 +197,88 @@ def send_file(filename, args, dst): _read(dst) # 3. Write CRC-32 (4 bytes) - _write(dst, struct.pack('<I', crc)) - _read(dst) + _write(dst, struct.pack("<I", crc)) + response = _read(dst) + print response src.close() if args.fpga: # tell the fpga to read its new configuration - _execute(dst, 'fpga reset') + _execute(dst, "fpga reset") if args.fpga or args.bootloader: # log out of the CLI # firmware upgrade reboots, doesn't need an exit - _execute(dst, 'exit') + _execute(dst, "exit") return True -def main(args): - dst = serial.Serial(args.device, 921600, timeout=2) - send_file(args.filename, args, dst) +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(): + args = parse_args() + + if args.bootloader and not args.simon_says_whack_my_bootloader: + sys.exit("You didn't say \"Simon says\"") + + if args.bootloader: + print dire_bootloader_warning + + 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") + print "Uploading from explicitly-specified file {}".format(args.explicit_image.name) + + else: + tar = tarfile.open(fileobj = args.firmware_tarball) + 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) + print "Uploading {} from {}".format(name, args.firmware_tarball.name) + + print "Initializing serial port and synchronizing with HSM, this may take a few seconds" + dst = serial.Serial(args.device, 921600, timeout = 2) + send_file(src, size, args, dst) dst.close() - return True -if __name__ == '__main__': + +if __name__ == "__main__": try: - args = parse_args() - if main(args): - sys.exit(0) - sys.exit(1) + main() except KeyboardInterrupt: pass - diff --git a/projects/hsm/hsm.c b/projects/hsm/hsm.c index 5758f05..3186bc5 100644 --- a/projects/hsm/hsm.c +++ b/projects/hsm/hsm.c @@ -1,7 +1,7 @@ /* - * rpc_server.c - * ------------ - * Remote procedure call server-side private API implementation. + * hsm.c + * ---------------- + * Main module for the HSM project. * * Copyright (c) 2016, NORDUnet A/S All rights reserved. * @@ -33,20 +33,17 @@ */ /* - * 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. + * 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 "cmsis_os.h" #include "stm-init.h" @@ -57,46 +54,44 @@ #include "mgmt-cli.h" -/* stm32f4xx_hal_def.h and hal.h both define HAL_OK as an enum value */ -#define HAL_OK HAL_OKAY - +#undef HAL_OK +#define HAL_OK LIBHAL_OK #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. - */ +#undef HAL_OK #ifndef NUM_RPC_TASK -/* An arbitrary number, but we don't expect to have more than 8 concurrent - * RPC requests. +/* Just one RPC task for now. More will require active resource management + * of at least the FPGA cores. */ -#define NUM_RPC_TASK 8 +#define NUM_RPC_TASK 1 #endif #ifndef TASK_STACK_SIZE /* Define an absurdly large task stack, because some pkey operation use a - * lot of stack variables. + * 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 #ifndef MAX_PKT_SIZE -/* Another arbitrary number, more or less driven by the 4096-bit RSA +/* An 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. +/* RPC buffers. For each active RPC, there will be two - input and output. */ typedef struct { size_t len; uint8_t buf[MAX_PKT_SIZE]; } rpc_buffer_t; +#if NUM_RPC_TASK > 1 /* A mutex to arbitrate concurrent UART transmits, from RPC responses. */ osMutexId uart_mutex; @@ -106,6 +101,7 @@ osMutexDef(uart_mutex); */ osMutexId dispatch_mutex; osMutexDef(dispatch_mutex); +#endif /* Semaphore to inform the dispatch thread that there's a new RPC request. */ @@ -114,65 +110,97 @@ osSemaphoreDef(rpc_sem); static volatile uint8_t uart_rx; /* current character received from UART */ static rpc_buffer_t * volatile rbuf; /* current RPC input buffer */ +static volatile int reenable_recv = 1; /* Callback for HAL_UART_Receive_IT(). + * With multiple worker threads, we can't do a blocking receive, because + * that prevents other threads from sending RPC responses (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. + * Even with only one worker thread, context-switching to the CLI thread + * causes HAL_UART_Receive to miss input. */ void HAL_UART2_RxCpltCallback(UART_HandleTypeDef *huart) { int complete; - hal_slip_recv_char(rbuf->buf, &rbuf->len, sizeof(rbuf->buf), &complete); - if (complete) - osSemaphoreRelease(rpc_sem); - HAL_UART_Receive_IT(huart, (uint8_t *)&uart_rx, 1); -} + if (hal_slip_recv_char(rbuf->buf, &rbuf->len, sizeof(rbuf->buf), &complete) != LIBHAL_OK) + Error_Handler(); -hal_error_t hal_serial_send_char(uint8_t c) -{ - return (uart_send_char(c) == 0) ? HAL_OK : HAL_ERROR_RPC_TRANSPORT; + if (complete) + if (osSemaphoreRelease(rpc_sem) != osOK) + Error_Handler(); + + if (HAL_UART_Receive_IT(huart, (uint8_t *)&uart_rx, 1) != CMSIS_HAL_OK) + /* We may have collided with a transmit. + * Signal the dispatch_thread to try again. + */ + reenable_recv = 1; } hal_error_t hal_serial_recv_char(uint8_t *cp) { /* return the character from HAL_UART_Receive_IT */ *cp = uart_rx; - return HAL_OK; + return LIBHAL_OK; +} + +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; } /* Thread entry point for the RPC request handler. */ -static void dispatch_thread(void const *args) +void dispatch_thread(void const *args) { rpc_buffer_t ibuf, obuf; while (1) { memset(&ibuf, 0, sizeof(ibuf)); memset(&obuf, 0, sizeof(obuf)); + obuf.len = sizeof(obuf.buf); +#if NUM_RPC_TASK > 1 /* Wait for access to the uart */ osMutexWait(dispatch_mutex, osWaitForever); +#endif + + /* Start receiving, or re-enable after failing in the callback. */ + if (reenable_recv) { + if (HAL_UART_Receive_IT(&huart_user, (uint8_t *)&uart_rx, 1) != CMSIS_HAL_OK) + Error_Handler(); + reenable_recv = 0; + } /* Wait for the complete rpc request */ rbuf = &ibuf; osSemaphoreWait(rpc_sem, osWaitForever); +#if NUM_RPC_TASK > 1 /* Let the next thread handle the next request */ osMutexRelease(dispatch_mutex); /* Let the next thread take the mutex */ osThreadYield(); - - /* Copy client ID from request to response */ - memcpy(obuf.buf, ibuf.buf, 4); - obuf.len = sizeof(obuf.buf) - 4; +#endif /* Process the request */ - hal_rpc_server_dispatch(ibuf.buf + 4, ibuf.len - 4, obuf.buf + 4, &obuf.len); + if (hal_rpc_server_dispatch(ibuf.buf, ibuf.len, obuf.buf, &obuf.len) != LIBHAL_OK) + /* If hal_rpc_server_dispatch failed with an XDR error, it + * probably means the request packet was garbage. In any case, we + * have nothing to transmit. + */ + continue; /* Send the response */ +#if NUM_RPC_TASK > 1 osMutexWait(uart_mutex, osWaitForever); - hal_error_t ret = hal_rpc_sendto(obuf.buf, obuf.len + 4, NULL); +#endif + hal_error_t ret = hal_rpc_sendto(obuf.buf, obuf.len, NULL); +#if NUM_RPC_TASK > 1 osMutexRelease(uart_mutex); - if (ret != HAL_OK) +#endif + if (ret != LIBHAL_OK) Error_Handler(); } } @@ -206,16 +234,8 @@ static uint8_t *sdram_malloc(size_t size) int main() { stm_init(); + uart_set_default(STM_UART_MGMT); -#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(); @@ -228,21 +248,18 @@ int main() */ hal_core_iterate(NULL); - uart_mutex = osMutexCreate(osMutex(uart_mutex)); - dispatch_mutex = osMutexCreate(osMutex(dispatch_mutex)); - rpc_sem = osSemaphoreCreate(osSemaphore(rpc_sem), 0); - -#ifdef TARGET_CRYPTECH_ALPHA - /* Launch other threads: - * - admin thread on USART1 - * - csprng warm-up thread? - */ +#if NUM_RPC_TASK > 1 + if ((uart_mutex = osMutexCreate(osMutex(uart_mutex))) == NULL || + (dispatch_mutex = osMutexCreate(osMutex(dispatch_mutex)) == NULL) + Error_Handler(); #endif + if ((rpc_sem = osSemaphoreCreate(osSemaphore(rpc_sem), 0)) == NULL) + Error_Handler(); - if (hal_rpc_server_init() != HAL_OK) + if (hal_rpc_server_init() != LIBHAL_OK) Error_Handler(); - /* Create the rpc dispatch threads */ + /* Create the rpc dispatch worker threads. */ for (int i = 0; i < NUM_RPC_TASK; ++i) { osThreadDef_t *ot = &thread_def[i]; ot->pthread = dispatch_thread; @@ -255,8 +272,9 @@ int main() Error_Handler(); } - /* Start the non-blocking receive */ - HAL_UART_Receive_IT(&huart_user, (uint8_t *)&uart_rx, 1); + /* Launch other threads (csprng warm-up thread?) + * Wait for FPGA_DONE interrupt. + */ return cli_main(); } diff --git a/projects/hsm/mgmt-fpga.c b/projects/hsm/mgmt-fpga.c index b74392e..45bd33c 100644 --- a/projects/hsm/mgmt-fpga.c +++ b/projects/hsm/mgmt-fpga.c @@ -133,10 +133,37 @@ static int cmd_fpga_reset_registers(struct cli_def *cli, const char *command, ch return CLI_OK; } +static int cmd_fpga_show_status(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + cli_print(cli, "FPGA has %sloaded a bitstream", fpgacfg_check_done() ? "":"NOT "); + return CLI_OK; +} + +static int cmd_fpga_show_cores(struct cli_def *cli, const char *command, char *argv[], int argc) +{ + const hal_core_t *core; + const hal_core_info_t *info; + + 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) { /* fpga */ cli_command_root(fpga); + + cli_command_branch(fpga, show); + /* show fpga status*/ + cli_command_node(fpga_show, status, "Show status about the FPGA"); + /* show fpga cores*/ + cli_command_node(fpga_show, cores, "Show FPGA core names and versions"); + /* fpga reset */ cli_command_node(fpga, reset, "Reset FPGA (config reset)"); /* fpga reset registers */ diff --git a/projects/hsm/mgmt-misc.c b/projects/hsm/mgmt-misc.c index 67bc875..15b2d13 100644 --- a/projects/hsm/mgmt-misc.c +++ b/projects/hsm/mgmt-misc.c @@ -44,13 +44,6 @@ extern uint32_t update_crc(uint32_t crc, uint8_t *buf, int len); -static volatile uint32_t demo_crc = 0; - -static int _count_bytes_callback(uint8_t *buf, size_t len) { - demo_crc = update_crc(demo_crc, buf, len); - return 1; -} - 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; @@ -109,16 +102,6 @@ int cli_receive_data(struct cli_def *cli, uint8_t *buf, size_t len, cli_data_cal return CLI_OK; } -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; - cli_receive_data(cli, &buf[0], sizeof(buf), _count_bytes_callback); - cli_print(cli, "Demo CRC is: %li/0x%x", demo_crc, (unsigned int) demo_crc); - return CLI_OK; -} - static int cmd_reboot(struct cli_def *cli, const char *command, char *argv[], int argc) { cli_print(cli, "\n\n\nRebooting\n\n\n"); @@ -130,8 +113,6 @@ static int cmd_reboot(struct cli_def *cli, const char *command, char *argv[], in void configure_cli_misc(struct cli_def *cli) { - /* filetransfer */ - cli_command_root_node(filetransfer, "Test file transfering"); /* reboot */ cli_command_root_node(reboot, "Reboot the STM32"); } |