fw: Add pre loaded flash app and flash data storage

- Add per app flash storage
  - Adds four data areas. An app can allocate an area. Once allocated
    the area is tied to the CDI of the app and can only be
    read/written/deallocated by the same app.
- Add two pre loaded app slots to flash
  - Load an app from the first slot at boot. The app digest must match a
    specific digest specified in firmware.
  - Optionally load an app from the second slot
- Add a resetinfo area in FW_RAM which is used to signal an app's intent
  of resetting the system and, optionally, pass data to firmware or the
  next app in a bootchain.

Co-authored-by: Jonas Thörnblad <jonas@tillitis.se>
Co-authored-by: Mikael Ågren <mikael@tillitis.se>
Co-authored-by: Daniel Jobson <jobson@tillitis.se>
This commit is contained in:
Michael Cardell Widerkrantz 2025-03-12 16:17:48 +01:00 committed by Mikael Ågren
parent 4841b1b127
commit 2c1c05f180
No known key found for this signature in database
GPG key ID: E02DA3D397792C46
25 changed files with 1642 additions and 27 deletions

View file

@ -3,16 +3,21 @@
* SPDX-License-Identifier: GPL-2.0-only
*/
#include <blake2s/blake2s.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <tkey/assert.h>
#include <tkey/debug.h>
#include <tkey/led.h>
#include <tkey/lib.h>
#include <tkey/tk1_mem.h>
#include "blake2s/blake2s.h"
#include "mgmt_app.h"
#include "partition_table.h"
#include "preload_app.h"
#include "proto.h"
#include "resetinfo.h"
#include "state.h"
#include "syscall_enable.h"
@ -33,8 +38,11 @@ static volatile uint32_t *timer_status = (volatile uint32_t *)TK1_MMIO_TIMER
static volatile uint32_t *timer_ctrl = (volatile uint32_t *)TK1_MMIO_TIMER_CTRL;
static volatile uint32_t *ram_addr_rand = (volatile uint32_t *)TK1_MMIO_TK1_RAM_ADDR_RAND;
static volatile uint32_t *ram_data_rand = (volatile uint32_t *)TK1_MMIO_TK1_RAM_DATA_RAND;
static volatile struct reset *resetinfo = (volatile struct reset *)TK1_MMIO_RESETINFO_BASE;
// clang-format on
struct partition_table_storage part_table_storage;
// Context for the loading of a TKey program
struct context {
uint32_t left; // Bytes left to receive
@ -42,6 +50,9 @@ struct context {
uint8_t *loadaddr; // Where we are currently loading a TKey program
bool use_uss; // Use USS?
uint8_t uss[32]; // User Supplied Secret, if any
uint8_t flash_slot; // App is loaded from flash slot number
/*@null@*/ volatile uint8_t
*ver_digest; // Verify loaded app against this digest
};
static void print_hw_version(void);
@ -56,11 +67,14 @@ static enum state initial_commands(const struct frame_header *hdr,
static enum state loading_commands(const struct frame_header *hdr,
const uint8_t *cmd, enum state state,
struct context *ctx);
static void run(const struct context *ctx);
#if !defined(SIMULATION)
static uint32_t xorwow(uint32_t state, uint32_t acc);
#endif
static void scramble_ram(void);
static int compute_app_digest(uint8_t *digest);
static int load_flash_app(struct partition_table *part_table,
uint8_t digest[32], uint8_t slot);
static enum state start_where(struct context *ctx);
static void print_hw_version(void)
{
@ -80,6 +94,7 @@ static void print_digest(uint8_t *md)
for (int j = 0; j < 4; j++) {
for (int i = 0; i < 8; i++) {
debug_puthex(md[i + 8 * j]);
(void)md;
}
debug_lf();
}
@ -144,6 +159,7 @@ static void compute_cdi(const uint8_t *digest, const uint8_t use_uss,
static void copy_name(uint8_t *buf, const size_t bufsiz, const uint32_t word)
{
assert(bufsiz >= 4);
assert(buf != NULL);
buf[0] = word >> 24;
buf[1] = word >> 16;
@ -287,9 +303,7 @@ static enum state loading_commands(const struct frame_header *hdr,
// Compute Blake2S digest of the app,
// storing it for FW_STATE_RUN
blake2err =
blake2s(&ctx->digest, 32, NULL, 0,
(const void *)TK1_RAM_BASE, *app_size);
blake2err = compute_app_digest(ctx->digest);
assert(blake2err == 0);
print_digest(ctx->digest);
@ -299,7 +313,7 @@ static enum state loading_commands(const struct frame_header *hdr,
memcpy_s(&rsp[1], CMDSIZE - 1, &ctx->digest, 32);
fwreply(*hdr, FW_RSP_LOAD_APP_DATA_READY, rsp);
state = FW_STATE_RUN;
state = FW_STATE_START;
break;
}
@ -319,13 +333,11 @@ static enum state loading_commands(const struct frame_header *hdr,
return state;
}
static void run(const struct context *ctx)
static void jump_to_app(void)
{
/* Start of app is always at the beginning of RAM */
*app_addr = TK1_RAM_BASE;
// CDI = hash(uds, hash(app), uss)
compute_cdi(ctx->digest, ctx->use_uss, ctx->uss);
debug_puts("Flipping to app mode!\n");
debug_puts("Jumping to ");
debug_putinthex(*app_addr);
@ -364,6 +376,29 @@ static void run(const struct context *ctx)
__builtin_unreachable();
}
static int load_flash_app(struct partition_table *part_table,
uint8_t digest[32], uint8_t slot)
{
if (slot >= N_PRELOADED_APP) {
return -1;
}
if (preload_load(part_table, slot) == -1) {
return -1;
}
*app_size = part_table->pre_app_data[slot].size;
if (*app_size > TK1_APP_MAX_SIZE) {
return -1;
}
int digest_err = compute_app_digest(digest);
assert(digest_err == 0);
print_digest(digest);
return 0;
}
#if !defined(SIMULATION)
static uint32_t xorwow(uint32_t state, uint32_t acc)
{
@ -398,6 +433,62 @@ static void scramble_ram(void)
*ram_data_rand = rnd_word();
}
/* Computes the blake2s digest of the app loaded into RAM */
static int compute_app_digest(uint8_t *digest)
{
return blake2s(digest, 32, NULL, 0, (const void *)TK1_RAM_BASE,
*app_size);
}
static enum state start_where(struct context *ctx)
{
assert(ctx != NULL);
// Where do we start? Read resetinfo 'startfrom'
switch (resetinfo->type) {
case START_DEFAULT:
// fallthrough
case START_FLASH0:
ctx->flash_slot = 0;
ctx->ver_digest = mgmt_app_allowed_digest();
return FW_STATE_LOAD_FLASH_MGMT;
case START_FLASH1:
ctx->flash_slot = 1;
ctx->ver_digest = NULL;
return FW_STATE_LOAD_FLASH;
case START_FLASH0_VER:
ctx->flash_slot = 0;
ctx->ver_digest = resetinfo->app_digest;
return FW_STATE_LOAD_FLASH;
case START_FLASH1_VER:
ctx->flash_slot = 1;
ctx->ver_digest = resetinfo->app_digest;
return FW_STATE_LOAD_FLASH;
case START_CLIENT:
ctx->ver_digest = NULL;
return FW_STATE_WAITCOMMAND;
case START_CLIENT_VER:
ctx->ver_digest = resetinfo->app_digest;
return FW_STATE_WAITCOMMAND;
default:
debug_puts("Unknown startfrom\n");
return FW_STATE_FAIL;
}
}
int main(void)
{
struct context ctx = {0};
@ -405,6 +496,8 @@ int main(void)
uint8_t cmd[CMDSIZE] = {0};
enum state state = FW_STATE_INITIAL;
led_set(LED_BLUE);
print_hw_version();
/*@-mustfreeonly@*/
@ -417,6 +510,11 @@ int main(void)
scramble_ram();
if (part_table_read(&part_table_storage) != 0) {
// Couldn't read or create partition table
assert(1 == 2);
}
#if defined(SIMULATION)
run(&ctx);
#endif
@ -424,6 +522,10 @@ int main(void)
for (;;) {
switch (state) {
case FW_STATE_INITIAL:
state = start_where(&ctx);
break;
case FW_STATE_WAITCOMMAND:
if (readcommand(&hdr, cmd, state) == -1) {
state = FW_STATE_FAIL;
break;
@ -444,9 +546,52 @@ int main(void)
state = loading_commands(&hdr, cmd, state, &ctx);
break;
case FW_STATE_RUN:
run(&ctx);
break; // This is never reached!
case FW_STATE_LOAD_FLASH:
if (load_flash_app(&part_table_storage.table,
ctx.digest, ctx.flash_slot) < 0) {
debug_puts("Couldn't load app from flash\n");
state = FW_STATE_FAIL;
break;
}
state = FW_STATE_START;
break;
case FW_STATE_LOAD_FLASH_MGMT:
if (load_flash_app(&part_table_storage.table,
ctx.digest, ctx.flash_slot) < 0) {
debug_puts("Couldn't load app from flash\n");
state = FW_STATE_FAIL;
break;
}
if (mgmt_app_init(ctx.digest) != 0) {
state = FW_STATE_FAIL;
break;
}
state = FW_STATE_START;
break;
case FW_STATE_START:
// CDI = hash(uds, hash(app), uss)
compute_cdi(ctx.digest, ctx.use_uss, ctx.uss);
if (ctx.ver_digest != NULL) {
print_digest(ctx.digest);
if (!memeq(ctx.digest, (void *)ctx.ver_digest,
sizeof(ctx.digest))) {
debug_puts("Digests do not match\n");
state = FW_STATE_FAIL;
break;
}
}
(void)memset((void *)resetinfo->app_digest, 0,
sizeof(resetinfo->app_digest));
jump_to_app();
break; // Not reached
case FW_STATE_FAIL:
// fallthrough
@ -461,5 +606,6 @@ int main(void)
/*@ -compdestroy @*/
/* We don't care about memory leaks here. */
return (int)0xcafebabe;
}