aboutsummaryrefslogtreecommitdiff
path: root/projects/hsm/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'projects/hsm/main.c')
-rw-r--r--projects/hsm/main.c197
1 files changed, 167 insertions, 30 deletions
diff --git a/projects/hsm/main.c b/projects/hsm/main.c
index 6c3d2e3..95483ee 100644
--- a/projects/hsm/main.c
+++ b/projects/hsm/main.c
@@ -32,6 +32,19 @@
* 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 "cmsis_os.h"
#include "stm-init.h"
@@ -43,88 +56,212 @@
#define HAL_OK HAL_OKAY
#include "hal.h"
+#include "hal_internal.h" /* hal_rpc_sendto, hal_rpc_recvfrom */
+#include "xdr_internal.h" /* hal_xdr_encode_int */
+
+/* RPC buffers. For each active RPC, there will be two - input and output.
+ */
-/* declared in hal_internal.h */
-extern hal_error_t hal_rpc_sendto(const uint8_t * const buf, const size_t len, void *opaque);
-extern hal_error_t hal_rpc_recvfrom(uint8_t * const buf, size_t * const len, void **opaque);
+#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 {
- void *opaque;
size_t len;
uint8_t buf[MAX_PKT_SIZE];
} rpc_buffer_t;
-osPoolDef(rpc_buffer_pool, 16, rpc_buffer_t);
+osPoolDef(rpc_buffer_pool, NUM_RPC_BUFFER, rpc_buffer_t);
osPoolId rpc_buffer_pool;
-rpc_buffer_t *rpc_buffer_alloc(void)
+static rpc_buffer_t *rpc_buffer_alloc(void)
{
- rpc_buffer_t *rbuf = (rpc_buffer_t *)osPoolCAlloc(rpc_buffer_pool);
- if (rbuf)
- rbuf->len = sizeof(rbuf->buf);
- return rbuf;
+ return (rpc_buffer_t *)osPoolCAlloc(rpc_buffer_pool);
}
+/* A mutex to arbitrate concurrent UART transmits, from RPC responses.
+ */
osMutexId uart_mutex;
osMutexDef(uart_mutex);
-void dispatch_thread(void const *args)
+/* Borrowed from xdr.c. We know the target architecture is little-endian,
+ * but we pretend for the sake of appearances.
+ */
+#ifdef __ARMEL__ /* little endian */
+static inline uint32_t htonl(uint32_t w)
+{
+ return
+ ((w & 0x000000ff) << 24) +
+ ((w & 0x0000ff00) << 8) +
+ ((w & 0x00ff0000) >> 8) +
+ ((w & 0xff000000) >> 24);
+}
+#else
+#define htonl(x) (x)
+#endif
+
+/* 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(); // NULL check
- obuf->opaque = ibuf->opaque;
+ rpc_buffer_t *obuf = rpc_buffer_alloc();
+ if (obuf == NULL) {
+ uint32_t err = htonl(HAL_ERROR_ALLOCATION_FAILURE);
+ osMutexWait(uart_mutex, osWaitForever);
+ hal_rpc_sendto((uint8_t *)&err, 4, NULL);
+ osMutexRelease(uart_mutex);
+ osPoolFree(rpc_buffer_pool, ibuf);
+ Error_Handler();
+ }
+ obuf->len = sizeof(obuf->buf);
hal_rpc_server_dispatch(ibuf->buf, ibuf->len, obuf->buf, &obuf->len);
osPoolFree(rpc_buffer_pool, ibuf);
osMutexWait(uart_mutex, osWaitForever);
- hal_rpc_sendto(obuf->buf, obuf->len, obuf->opaque);
+ hal_error_t ret = hal_rpc_sendto(obuf->buf, obuf->len, NULL);
osMutexRelease(uart_mutex);
osPoolFree(rpc_buffer_pool, obuf);
+ if (ret != HAL_OK)
+ Error_Handler();
}
osThreadDef(dispatch_thread, osPriorityNormal, DEFAULT_STACK_SIZE);
-void rpc_server_main(void)
+/* 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 */
+
+/* Add a byte to the input buffer.
+ */
+static inline void ibuf_push(uint8_t c) {
+ if (ibuf->len < MAX_PKT_SIZE)
+ ibuf->buf[ibuf->len++] = c;
+}
+
+/* Callback for HAL_UART_Receive_IT().
+ */
+void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
- hal_error_t ret;
-
- while (1) {
- rpc_buffer_t *ibuf = rpc_buffer_alloc(); // NULL check
- // separate allocations for struct and block of memory?
- ret = hal_rpc_recvfrom(ibuf->buf, &ibuf->len, &ibuf->opaque);
- if (ret == HAL_OK) {
- osThreadCreate(osThread(dispatch_thread), (void *)ibuf);
+/* SLIP special characters */
+#define END 0300 /* indicates end of packet */
+#define ESC 0333 /* indicates byte stuffing */
+#define ESC_END 0334 /* ESC ESC_END means END data byte */
+#define ESC_ESC 0335 /* ESC ESC_ESC means ESC data byte */
+
+ static int esc_flag = 0; /* previous char was ESC */
+
+ /* got 1 char - un-SLIP it */
+ switch (c) {
+ case END:
+ if (ibuf->len)
+ osSemaphoreRelease(rpc_sem);
+ break;
+ case ESC:
+ esc_flag = 1;
+ break;
+ default:
+ if (esc_flag) {
+ esc_flag = 0;
+ switch (c) {
+ case ESC_END:
+ ibuf_push(END);
+ break;
+ case ESC_ESC:
+ ibuf_push(ESC);
+ break;
+ default:
+ ibuf_push(c);
+ }
+ }
+ else {
+ ibuf_push(c);
}
+ break;
}
+
+ HAL_UART_Receive_IT(huart, &c, 1);
+}
+
+/* UART interrupt handler. This eventually calls HAL_UART_RxCpltCallback.
+ */
+void USART2_IRQHandler(void)
+{
+ HAL_UART_IRQHandler(&huart2);
}
+/* 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
- // Blink blue LED for six seconds to not upset the Novena at boot.
+ /* 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);
+ led_on(LED_GREEN);
#endif
- // prepare fmc interface
+ /* 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?
+ /* Launch other threads:
+ * - admin thread on USART1
+ * - csprng warm-up thread?
+ */
#endif
if (hal_rpc_server_init() != HAL_OK)
- return 1;
- rpc_server_main();
+ Error_Handler();
+
+ ibuf = rpc_buffer_alloc();
+ if (ibuf == NULL)
+ /* Something is badly wrong. */
+ Error_Handler();
+
+ /* Start the non-blocking receive */
+ HAL_UART_Receive_IT(&huart2, &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.
+ */
+ }
}