aboutsummaryrefslogtreecommitdiff
path: root/projects/hsm
diff options
context:
space:
mode:
authorFredrik Thulin <fredrik@thulin.net>2016-07-09 20:26:57 +0200
committerFredrik Thulin <fredrik@thulin.net>2016-07-09 20:26:57 +0200
commitdcc6cf1b97935bc3a663714f596cacba619a8b3d (patch)
treec38a88e9ae4a1e012cf2cceb2da3c9c10d2bb27a /projects/hsm
parent3ea10bf4fba185a8bfbb33a8c67a69f70b95755a (diff)
parentdf9e82b28aad97664cba1fc238bc4f2563c1de7c (diff)
Merge branch 'master' of git.cryptech.is.:sw/stm32
Diffstat (limited to 'projects/hsm')
-rwxr-xr-xprojects/hsm/cryptech_miniterm45
-rwxr-xr-xprojects/hsm/cryptech_probe145
-rwxr-xr-xprojects/hsm/cryptech_upload213
-rw-r--r--projects/hsm/hsm.c148
-rw-r--r--projects/hsm/mgmt-fpga.c27
-rw-r--r--projects/hsm/mgmt-misc.c19
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");
}