mirror of
https://github.com/tillitis/tillitis-key1.git
synced 2025-03-26 00:28:15 -04:00
Add start of pre-loaded app from flash
Based on earlier code by Daniel Jobson <jobson@tillitis.se> now integrated into the new world order. Co-authored-by: Mikael Ågren <mikael@tillitis.se> Co-authored-by: Daniel Jobson <jobson@tillitis.se>
This commit is contained in:
parent
6ef6c36f6f
commit
2cb5f2eca6
@ -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 \
|
||||
|
@ -10,9 +10,12 @@
|
||||
#include <tkey/debug.h>
|
||||
#include <tkey/lib.h>
|
||||
#include <tkey/tk1_mem.h>
|
||||
#include <tkey/led.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
75
hw/application_fpga/fw/tk1/mgmt_app.c
Normal file
75
hw/application_fpga/fw/tk1/mgmt_app.c
Normal file
@ -0,0 +1,75 @@
|
||||
// Copyright (C) 2024 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <tkey/lib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#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;
|
||||
}
|
15
hw/application_fpga/fw/tk1/mgmt_app.h
Normal file
15
hw/application_fpga/fw/tk1/mgmt_app.h
Normal file
@ -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 <stdbool.h>
|
||||
|
||||
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
|
139
hw/application_fpga/fw/tk1/preload_app.c
Normal file
139
hw/application_fpga/fw/tk1/preload_app.c
Normal file
@ -0,0 +1,139 @@
|
||||
// Copyright (C) 2024 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <tkey/debug.h>
|
||||
#include <tkey/lib.h>
|
||||
#include <tkey/tk1_mem.h>
|
||||
|
||||
#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;
|
||||
}
|
20
hw/application_fpga/fw/tk1/preload_app.h
Normal file
20
hw/application_fpga/fw/tk1/preload_app.h
Normal file
@ -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 <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
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
|
@ -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,
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user