From 2cb5f2eca67bbec998b5b073c7beda9d2882ba88 Mon Sep 17 00:00:00 2001 From: Michael Cardell Widerkrantz Date: Thu, 13 Mar 2025 14:18:38 +0100 Subject: [PATCH] Add start of pre-loaded app from flash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Based on earlier code by Daniel Jobson now integrated into the new world order. Co-authored-by: Mikael Ă…gren Co-authored-by: Daniel Jobson --- hw/application_fpga/Makefile | 4 +- hw/application_fpga/fw/tk1/main.c | 99 ++++++++++++++++ hw/application_fpga/fw/tk1/mgmt_app.c | 75 ++++++++++++ hw/application_fpga/fw/tk1/mgmt_app.h | 15 +++ hw/application_fpga/fw/tk1/preload_app.c | 139 +++++++++++++++++++++++ hw/application_fpga/fw/tk1/preload_app.h | 20 ++++ hw/application_fpga/fw/tk1/state.h | 1 + 7 files changed, 352 insertions(+), 1 deletion(-) create mode 100644 hw/application_fpga/fw/tk1/mgmt_app.c create mode 100644 hw/application_fpga/fw/tk1/mgmt_app.h create mode 100644 hw/application_fpga/fw/tk1/preload_app.c create mode 100644 hw/application_fpga/fw/tk1/preload_app.h diff --git a/hw/application_fpga/Makefile b/hw/application_fpga/Makefile index 8ec26a6..75d076d 100644 --- a/hw/application_fpga/Makefile +++ b/hw/application_fpga/Makefile @@ -132,7 +132,9 @@ FIRMWARE_OBJS = \ $(P)/fw/tk1/storage.o \ $(P)/fw/tk1/partition_table.o \ $(P)/fw/tk1/auth_app.o \ - $(P)/fw/tk1/rng.o + $(P)/fw/tk1/rng.o \ + $(P)/fw/tk1/preload_app.o \ + $(P)/fw/tk1/mgmt_app.o FIRMWARE_SOURCES = \ $(P)/fw/tk1/main.c \ diff --git a/hw/application_fpga/fw/tk1/main.c b/hw/application_fpga/fw/tk1/main.c index 1b3b944..5197489 100644 --- a/hw/application_fpga/fw/tk1/main.c +++ b/hw/application_fpga/fw/tk1/main.c @@ -10,9 +10,12 @@ #include #include #include +#include +#include "auth_app.h" #include "blake2s/blake2s.h" #include "partition_table.h" +#include "preload_app.h" #include "proto.h" #include "state.h" #include "syscall_enable.h" @@ -45,6 +48,7 @@ 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 + bool from_flash; // App is loaded from flash }; static void print_hw_version(void); @@ -323,8 +327,68 @@ static enum state loading_commands(const struct frame_header *hdr, return state; } +static void run_flash(const struct context *ctx, struct partition_table *part_table) +{ + /* At this point we expect an app to be loaded into RAM */ + *app_addr = TK1_RAM_BASE; + + // CDI = hash(uds, hash(app), uss) + compute_cdi(ctx->digest, ctx->use_uss, ctx->uss); + + if (part_table->pre_app_data.status == 0x02) { + debug_puts("Create auth\n"); + auth_app_create(&part_table->pre_app_data.auth); + part_table->pre_app_data.status = 0x01; + part_table_write(part_table); + } + + if (!auth_app_authenticate(&part_table->pre_app_data.auth)) { + debug_puts("!Authenticated\n"); + assert(1 == 2); + } + + debug_puts("Flipping to app mode!\n"); + debug_puts("Jumping to "); + debug_putinthex(*app_addr); + debug_lf(); + + // Clear the firmware stack + // clang-format off +#ifndef S_SPLINT_S + asm volatile( + "la a0, _sstack;" + "la a1, _estack;" + "loop:;" + "sw zero, 0(a0);" + "addi a0, a0, 4;" + "blt a0, a1, loop;" + ::: "memory"); +#endif + // clang-format on + + syscall_enable(); + + // Jump to app - doesn't return + // Hardware is responsible for switching to app mode + // clang-format off +#ifndef S_SPLINT_S + asm volatile( + // Get value at TK1_MMIO_TK1_APP_ADDR + "lui a0,0xff000;" + "lw a0,0x030(a0);" + // Jump to it + "jalr x0,0(a0);" + ::: "memory"); +#endif + // clang-format on + + __builtin_unreachable(); +} + + static void run(const struct context *ctx) { + /* At this point we expect an app to be loaded into RAM */ *app_addr = TK1_RAM_BASE; // CDI = hash(uds, hash(app), uss) @@ -402,6 +466,15 @@ 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) +{ + blake2s_ctx b2s_ctx = {0}; + + return blake2s(digest, 32, NULL, 0, (const void *)TK1_RAM_BASE, + *app_size, &b2s_ctx); +} + int main(void) { struct context ctx = {0}; @@ -409,6 +482,8 @@ int main(void) uint8_t cmd[CMDSIZE] = {0}; enum state state = FW_STATE_INITIAL; + led_set(LED_BLUE); + print_hw_version(); /*@-mustfreeonly@*/ @@ -426,10 +501,29 @@ int main(void) assert(1 != 2); } + #if defined(SIMULATION) run(&ctx); #endif + // Just start the preloaded app. + if (preload_load(&part_table) == -1) { + state = FW_STATE_FAIL; + } + + *app_size = part_table.pre_app_data.size; + assert(*app_size <= TK1_APP_MAX_SIZE); + + int digest_err = compute_app_digest(ctx.digest); + assert(digest_err == 0); + print_digest(ctx.digest); + + part_table.pre_app_data.status = 0x02; + + state = FW_STATE_RUN_FLASH; + + led_set(LED_GREEN); + for (;;) { switch (state) { case FW_STATE_INITIAL: @@ -457,6 +551,10 @@ int main(void) run(&ctx); break; // This is never reached! + case FW_STATE_RUN_FLASH: + run_flash(&ctx, &part_table); + break; // This is never reached! + case FW_STATE_FAIL: // fallthrough default: @@ -470,5 +568,6 @@ int main(void) /*@ -compdestroy @*/ /* We don't care about memory leaks here. */ + return (int)0xcafebabe; } diff --git a/hw/application_fpga/fw/tk1/mgmt_app.c b/hw/application_fpga/fw/tk1/mgmt_app.c new file mode 100644 index 0000000..a61f26a --- /dev/null +++ b/hw/application_fpga/fw/tk1/mgmt_app.c @@ -0,0 +1,75 @@ +// Copyright (C) 2024 - Tillitis AB +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include + +#include "mgmt_app.h" +#include "auth_app.h" +#include "partition_table.h" + +/* Returns true if an management app is already registered */ +static bool mgmt_app_registered(struct management_app_metadata *mgmt_table) +{ + + if (mgmt_table->status == 0x00) { + /* No management app registered */ + return false; + // TODO: Should we also check nonce, authentication digest for + // non-zero? + } + + return true; +} + +/* Authenticate an management app */ +bool mgmt_app_authenticate(struct management_app_metadata *mgmt_table) +{ + if (!mgmt_app_registered(mgmt_table)) { + return false; + } + + return auth_app_authenticate(&mgmt_table->auth); +} + +/* Register an management app, returns zero on success */ +int mgmt_app_register(struct partition_table *part_table) +{ + /* Check if the current app is the mgmt app */ + if (mgmt_app_authenticate(&part_table->mgmt_app_data)) { + return 0; + } + + /* Check if another management app is registered */ + if (mgmt_app_registered(&part_table->mgmt_app_data)) { + return -1; + } + + auth_app_create(&part_table->mgmt_app_data.auth); + part_table->mgmt_app_data.status = 0x01; + + part_table_write(part_table); + + return 0; +} + +/* Unregister the currently registered app, returns zero on success */ +int mgmt_app_unregister(struct partition_table *part_table) +{ + /* Only the management app should be able to unregister itself */ + if (!mgmt_app_authenticate(&part_table->mgmt_app_data)) { + return -1; + } + + part_table->mgmt_app_data.status = 0; + + memset(part_table->mgmt_app_data.auth.nonce, 0x00, + sizeof(part_table->mgmt_app_data.auth.nonce)); + + memset(part_table->mgmt_app_data.auth.authentication_digest, 0x00, + sizeof(part_table->mgmt_app_data.auth.authentication_digest)); + + part_table_write(part_table); + + return 0; +} diff --git a/hw/application_fpga/fw/tk1/mgmt_app.h b/hw/application_fpga/fw/tk1/mgmt_app.h new file mode 100644 index 0000000..1b99b0d --- /dev/null +++ b/hw/application_fpga/fw/tk1/mgmt_app.h @@ -0,0 +1,15 @@ +// Copyright (C) 2024 - Tillitis AB +// SPDX-License-Identifier: GPL-2.0-only + +#ifndef MGMT_APP_H +#define MGMT_APP_H + +#include "partition_table.h" + +#include + +bool mgmt_app_authenticate(struct management_app_metadata *mgmt_table); +int mgmt_app_register(struct partition_table *part_table); +int mgmt_app_unregister(struct partition_table *part_table); + +#endif diff --git a/hw/application_fpga/fw/tk1/preload_app.c b/hw/application_fpga/fw/tk1/preload_app.c new file mode 100644 index 0000000..f47d021 --- /dev/null +++ b/hw/application_fpga/fw/tk1/preload_app.c @@ -0,0 +1,139 @@ +// Copyright (C) 2024 - Tillitis AB +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include + +#include "flash.h" +#include "mgmt_app.h" +#include "partition_table.h" +#include "preload_app.h" + +/* Returns non-zero if the app is valid */ +bool preload_check_valid_app(struct partition_table *part_table) +{ + + if (part_table->pre_app_data.status == 0x00 && + part_table->pre_app_data.size == 0) { + /*No valid app*/ + return false; + } + + return true; +} + +/* Loads a preloaded app from flash to app RAM */ +int preload_load(struct partition_table *part_table) +{ + /*Check for a valid app in flash */ + if (!preload_check_valid_app(part_table)) { + return -1; + } + uint8_t *loadaddr = (uint8_t *)TK1_RAM_BASE; + + /* Read from flash, straight into RAM */ + int ret = flash_read_data(ADDR_PRE_LOADED_APP, loadaddr, + part_table->pre_app_data.size); + + return ret; +} + +/* Expects to receive chunks of data up to 4096 bytes to store into the + * preloaded area. The offset needs to be kept and updated between each call. + * Once done, call preload_store_finalize() with the last parameters. + * */ +int preload_store(struct partition_table *part_table, uint32_t offset, + uint8_t *data, size_t size) +{ + /* Check if we are allowed to store */ + if (!mgmt_app_authenticate(&part_table->mgmt_app_data)) { + return -3; + } + + /* Check for a valid app in flash, bale out if it already exists */ + if (preload_check_valid_app(part_table)) { + return -1; + } + + if ((offset + size) > SIZE_PRE_LOADED_APP || size > 4096) { + /* Writing outside of area */ + return -2; + } + + uint32_t address = ADDR_PRE_LOADED_APP + offset; + + debug_puts("preload_store: write to addr: "); + debug_putinthex(address); + debug_lf(); + + return flash_write_data(address, data, size); +} + +int preload_store_finalize(struct partition_table *part_table, bool use_uss, + uint8_t *uss, size_t app_size) +{ + /* Check if we are allowed to store */ + if (!mgmt_app_authenticate(&part_table->mgmt_app_data)) { + return -3; + } + + /* Check for a valid app in flash, bale out if it already exists */ + if (preload_check_valid_app(part_table)) { + return -1; + } + + // TODO: Maybe add the uss fields + + if (app_size == 0 || app_size > SIZE_PRE_LOADED_APP) { + return -2; + } + + part_table->pre_app_data.size = app_size; + part_table->pre_app_data.status = + 0x02; /* Stored but not yet authenticated */ + debug_puts("preload_*_final: size: "); + debug_putinthex(app_size); + debug_lf(); + + part_table_write(part_table); + + /* Force a restart to authenticate the stored app */ + /* TODO: Should this be done by the management app or by firmware? */ + + return 0; +} + +int preload_delete(struct partition_table *part_table) +{ + /* Check if we are allowed to deleted */ + if (!mgmt_app_authenticate(&part_table->mgmt_app_data)) { + return -3; + } + + /*Check for a valid app in flash */ + if (!preload_check_valid_app(part_table)) { + return 0; + // TODO: Nothing here, return zero like all is good? + } + part_table->pre_app_data.size = 0; + part_table->pre_app_data.status = 0; + + memset(part_table->pre_app_data.auth.nonce, 0x00, + sizeof(part_table->pre_app_data.auth.nonce)); + + memset(part_table->pre_app_data.auth.authentication_digest, 0x00, + sizeof(part_table->pre_app_data.auth.authentication_digest)); + + part_table_write(part_table); + + /* Assumes the area is 64 KiB block aligned */ + flash_block_64_erase(ADDR_PRE_LOADED_APP); // Erase first 64 KB block + flash_block_64_erase(ADDR_PRE_LOADED_APP + + 0x10000); // Erase second 64 KB block + + return 0; +} diff --git a/hw/application_fpga/fw/tk1/preload_app.h b/hw/application_fpga/fw/tk1/preload_app.h new file mode 100644 index 0000000..d518129 --- /dev/null +++ b/hw/application_fpga/fw/tk1/preload_app.h @@ -0,0 +1,20 @@ +// Copyright (C) 2024 - Tillitis AB +// SPDX-License-Identifier: GPL-2.0-only + +#ifndef PRELOAD_APP_H +#define PRELOAD_APP_H + +#include "partition_table.h" +#include +#include +#include + +bool preload_check_valid_app(struct partition_table *part_table); +int preload_load(struct partition_table *part_table); +int preload_store(struct partition_table *part_table, uint32_t offset, + uint8_t *data, size_t size); +int preload_store_finalize(struct partition_table *part_table, bool use_uss, + uint8_t *uss, size_t app_size); +int preload_delete(struct partition_table *part_table); + +#endif diff --git a/hw/application_fpga/fw/tk1/state.h b/hw/application_fpga/fw/tk1/state.h index 694448b..2ded225 100644 --- a/hw/application_fpga/fw/tk1/state.h +++ b/hw/application_fpga/fw/tk1/state.h @@ -10,6 +10,7 @@ enum state { FW_STATE_INITIAL, FW_STATE_LOADING, FW_STATE_RUN, + FW_STATE_RUN_FLASH, FW_STATE_FAIL, FW_STATE_MAX, };