tillitis-key/hw/application_fpga/fw/mta1_mkdf/main.c
Daniel Lublin 55c5081486
Adjust and document the firmware state-machine, including USS
In particular, order of LOAD_USS and LOAD_APP_SIZE is not required, but
the need to send both is documented. This is followed up with adjustment
in the host programs' Go code, to try to reinforce this. LoadApp() will
take the secretPhrase parameter (to be hashed as USS), and loadUSS()
will be unexported.

Correct CMD/RSP lengths in pseudo-code.
2022-10-12 15:12:07 +02:00

281 lines
6.6 KiB
C

/*
* Copyright (C) 2022 - Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
#include "../mta1_mkdf_mem.h"
#include "blake2s/blake2s.h"
#include "lib.h"
#include "proto.h"
#include "types.h"
// In RAM + above the stack (0x40010000)
#define APP_RAM_ADDR (MTA1_MKDF_RAM_BASE + 0x10000)
#define APP_MAX_SIZE 65536
// clang-format off
static volatile uint32_t *uds = (volatile uint32_t *)MTA1_MKDF_MMIO_UDS_FIRST;
static volatile uint32_t *switch_app = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_SWITCH_APP;
static volatile uint32_t *name0 = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_NAME0;
static volatile uint32_t *name1 = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_NAME1;
static volatile uint32_t *ver = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_VERSION;
static volatile uint32_t *cdi = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_CDI_FIRST;
static volatile uint32_t *app_addr = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_APP_ADDR;
static volatile uint32_t *app_size = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_APP_SIZE;
#define LED_RED (1 << MTA1_MKDF_MMIO_MTA1_LED_R_BIT)
#define LED_GREEN (1 << MTA1_MKDF_MMIO_MTA1_LED_G_BIT)
#define LED_BLUE (1 << MTA1_MKDF_MMIO_MTA1_LED_B_BIT)
#define LED_WHITE (LED_RED | LED_GREEN | LED_BLUE)
// clang-format on
static void print_hw_version(uint32_t name0, uint32_t name1, uint32_t ver)
{
puts("Hello, I'm ");
putc(name0 >> 24);
putc(name0 >> 16);
putc(name0 >> 8);
putc(name0);
putc('-');
putc(name1 >> 24);
putc(name1 >> 16);
putc(name1 >> 8);
putc(name1);
putc(' ');
putinthex(ver);
lf();
}
static void print_digest(uint8_t *md)
{
puts("The app digest:\n");
for (int j = 0; j < 4; j++) {
for (int i = 0; i < 8; i++) {
puthex(md[i + 8 * j]);
}
lf();
}
lf();
}
int main()
{
uint32_t local_name0 = *name0;
uint32_t local_name1 = *name1;
uint32_t local_ver = *ver;
struct frame_header hdr; // Used in both directions
uint8_t cmd[CMDLEN_MAXBYTES];
uint8_t rsp[CMDLEN_MAXBYTES];
uint8_t *loadaddr = (uint8_t *)APP_RAM_ADDR;
int left = 0; // Bytes left to receive
uint8_t uss[32] = {0};
uint8_t digest[32] = {0};
print_hw_version(local_name0, local_name1, local_ver);
for (;;) {
// blocking; fw flashing white while waiting for cmd
uint8_t in = readbyte_ledflash(LED_WHITE, 800000);
if (parseframe(in, &hdr) == -1) {
puts("Couldn't parse header\n");
continue;
}
memset(cmd, 0, CMDLEN_MAXBYTES);
// Read firmware command: Blocks!
read(cmd, hdr.len);
// Is it for us?
if (hdr.endpoint != DST_FW) {
puts("Message not meant for us\n");
continue;
}
// Reset response buffer
memset(rsp, 0, CMDLEN_MAXBYTES);
// Min length is 1 byte so this should always be here
switch (cmd[0]) {
case FW_CMD_NAME_VERSION:
puts("cmd: name-version\n");
if (hdr.len != 1) {
// Bad length - give them an empty response
fwreply(hdr, FW_RSP_NAME_VERSION, rsp);
break;
}
memcpy(rsp, (uint8_t *)&local_name0, 4);
memcpy(rsp + 4, (uint8_t *)&local_name1, 4);
memcpy(rsp + 8, (uint8_t *)&local_ver, 4);
fwreply(hdr, FW_RSP_NAME_VERSION, rsp);
break;
case FW_CMD_LOAD_USS:
puts("cmd: load-uss\n");
if (hdr.len != 128) {
// Bad cmd length
rsp[0] = STATUS_BAD;
fwreply(hdr, FW_RSP_LOAD_USS, rsp);
break;
}
memcpy(uss, cmd + 1, 32);
rsp[0] = STATUS_OK;
fwreply(hdr, FW_RSP_LOAD_USS, rsp);
break;
case FW_CMD_LOAD_APP_SIZE:
puts("cmd: load-app-size\n");
if (hdr.len != 32) {
// Bad length
rsp[0] = STATUS_BAD;
fwreply(hdr, FW_RSP_LOAD_APP_SIZE, rsp);
break;
}
// cmd[1..4] contains the size.
uint32_t local_app_size = cmd[1] + (cmd[2] << 8) +
(cmd[3] << 16) +
(cmd[4] << 24);
puts("app size: ");
putinthex(local_app_size);
lf();
if (local_app_size > APP_MAX_SIZE) {
rsp[0] = STATUS_BAD;
fwreply(hdr, FW_RSP_LOAD_APP_SIZE, rsp);
break;
}
*app_size = local_app_size;
*app_addr = 0;
// Clear digest as GET_APP_DIGEST returns it even if it
// has not been calculated
memset(digest, 0, 32);
// Reset where to start loading the program
loadaddr = (uint8_t *)APP_RAM_ADDR;
left = *app_size;
rsp[0] = STATUS_OK;
fwreply(hdr, FW_RSP_LOAD_APP_SIZE, rsp);
break;
case FW_CMD_LOAD_APP_DATA:
puts("cmd: load-app-data\n");
if (hdr.len != 128 || *app_size == 0 ||
*app_addr != 0) {
// Bad length, or app_size not yet set, or
// app_addr already set (fully loaded!)
rsp[0] = STATUS_BAD;
fwreply(hdr, FW_RSP_LOAD_APP_DATA, rsp);
break;
}
int nbytes;
if (left > 127) {
nbytes = 127;
} else {
nbytes = left;
}
memcpy(loadaddr, cmd + 1, nbytes);
loadaddr += nbytes;
left -= nbytes;
if (left == 0) {
uint8_t scratch[96];
puts("Fully loaded ");
putinthex(*app_size);
lf();
*app_addr = APP_RAM_ADDR;
// Get the Blake2S digest of the app - store it
// for later queries
blake2s(digest, 32, NULL, 0,
(const void *)*app_addr, *app_size);
print_digest(digest);
// CDI = hash(uds, hash(app), uss)
uint32_t local_cdi[8];
// Only word aligned access to UDS
wordcpy(scratch, (void *)uds, 8);
memcpy(scratch + 32, digest, 32);
memcpy(scratch + 64, uss, 32);
blake2s((void *)local_cdi, 32, NULL, 0,
(const void *)scratch, 96);
// Only word aligned access to CDI
wordcpy((void *)cdi, (void *)local_cdi, 8);
}
rsp[0] = STATUS_OK;
fwreply(hdr, FW_RSP_LOAD_APP_DATA, rsp);
break;
case FW_CMD_RUN_APP:
puts("cmd: run-app\n");
if (hdr.len != 1 || *app_size == 0 || *app_addr == 0) {
// Bad cmd length, or app_size or app_addr are
// not yet set
rsp[0] = STATUS_BAD;
fwreply(hdr, FW_RSP_RUN_APP, rsp);
break;
}
rsp[0] = STATUS_OK;
fwreply(hdr, FW_RSP_RUN_APP, rsp);
// Flip over to application mode
*switch_app = 1;
// Jump to app - doesn't return
// First clears memory of firmware remains
puts("Jumping to ");
putinthex(*app_addr);
lf();
// clang-format off
asm volatile(
"li a0, 0x40000000;" // MTA1_MKDF_RAM_BASE
"li a1, 0x40010000;"
"loop:;"
"sw zero, 0(a0);"
"addi a0, a0, 4;"
"blt a0, a1, loop;"
// Get value at MTA1_MKDF_MMIO_MTA1_APP_ADDR
"lui a0,0xff000;"
"lw a0,0x030(a0);"
"jalr x0,0(a0);"
::: "memory");
// clang-format on
break; // This is never reached!
case FW_CMD_GET_APP_DIGEST:
puts("cmd: get-app-digest\n");
memcpy(rsp, &digest, 32);
fwreply(hdr, FW_RSP_GET_APP_DIGEST, rsp);
break;
default:
puts("Got unknown firmware cmd: 0x");
puthex(cmd[0]);
lf();
}
}
return (int)0xcafebabe;
}