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 apps 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 1ff6e0262f
commit e9ddf29ce9
No known key found for this signature in database
GPG Key ID: E02DA3D397792C46
25 changed files with 1641 additions and 27 deletions

View File

@ -126,7 +126,15 @@ FIRMWARE_OBJS = \
$(P)/fw/tk1/start.o \
$(P)/fw/tk1/proto.o \
$(P)/fw/tk1/syscall_enable.o \
$(P)/fw/tk1/syscall_handler.o
$(P)/fw/tk1/syscall_handler.o \
$(P)/fw/tk1/spi.o \
$(P)/fw/tk1/flash.o \
$(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/preload_app.o \
$(P)/fw/tk1/mgmt_app.o
FIRMWARE_SOURCES = \
$(P)/fw/tk1/main.c \

View File

@ -121,7 +121,7 @@ int main(void)
}
// But a syscall to get parts of UDI should be able to run
int vidpid = syscall(TK1_SYSCALL_GET_VIDPID, 0);
int vidpid = syscall(TK1_SYSCALL_GET_VIDPID, 0, 0, 0);
if (vidpid != 0x00010203) {
failmsg("Expected VID/PID to be 0x00010203");
@ -223,7 +223,7 @@ int main(void)
}
if (in == '+') {
syscall(TK1_SYSCALL_RESET, 0);
syscall(TK1_SYSCALL_RESET, 0, 0, 0);
}
write(IO_CDC, &in, 1);

View File

@ -6,6 +6,7 @@
#ifndef TKEY_APP_SYSCALL_H
#define TKEY_APP_SYSCALL_H
int syscall(uint32_t number, uint32_t arg1);
int syscall(uint32_t number, uint32_t arg1, uint32_t arg2,
uint32_t arg3);
#endif

View File

@ -0,0 +1,76 @@
// Copyright (C) 2024 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#include <stdbool.h>
#include <stdint.h>
#include <tkey/assert.h>
#include <tkey/lib.h>
#include <tkey/tk1_mem.h>
#include "auth_app.h"
#include "blake2s/blake2s.h"
#include "partition_table.h"
#include "rng.h"
static volatile uint32_t *cdi = (volatile uint32_t *)TK1_MMIO_TK1_CDI_FIRST;
/* Calculates the authentication digest based on a supplied nonce and the CDI.
* Requires that the CDI is already calculated and stored */
static void calculate_auth_digest(uint8_t *nonce, uint8_t *auth_digest)
{
assert(nonce != NULL);
assert(auth_digest != NULL);
blake2s_ctx ctx = {0};
// Generate a 16 byte authentication digest
int blake2err = blake2s_init(&ctx, 16, NULL, 0);
assert(blake2err == 0);
blake2s_update(&ctx, (const void *)cdi, 32);
blake2s_update(&ctx, nonce, 16);
blake2s_final(&ctx, auth_digest);
}
/* Generates a 16 byte nonce */
static void generate_nonce(uint32_t *nonce)
{
assert(nonce != NULL);
for (uint8_t i = 0; i < 4; i++) {
nonce[i] = rng_get_word();
}
return;
}
/* Returns the authentication digest and random nonce. Requires that the CDI is
* already calculated and stored */
void auth_app_create(struct auth_metadata *auth_table)
{
assert(auth_table != NULL);
uint8_t nonce[16] = {0};
uint8_t auth_digest[16] = {0};
generate_nonce((uint32_t *)nonce);
calculate_auth_digest(nonce, auth_digest);
memcpy_s(auth_table->authentication_digest, 16, auth_digest, 16);
memcpy_s(auth_table->nonce, 16, nonce, 16);
return;
}
bool auth_app_authenticate(struct auth_metadata *auth_table)
{
assert(auth_table != NULL);
uint8_t auth_digest[16] = {0};
calculate_auth_digest(auth_table->nonce, auth_digest);
if (memeq(auth_digest, auth_table->authentication_digest, 16)) {
return true;
}
return false;
}

View File

@ -0,0 +1,14 @@
// Copyright (C) 2024 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#ifndef AUTH_APP_H
#define AUTH_APP_H
#include "partition_table.h"
#include <stdbool.h>
void auth_app_create(struct auth_metadata *auth_table);
bool auth_app_authenticate(struct auth_metadata *auth_table);
#endif

View File

@ -7,7 +7,7 @@ OUTPUT_ARCH("riscv")
ENTRY(_start)
/* Define stack size */
STACK_SIZE = 0xEF0; /* 3824 B */
STACK_SIZE = 3000;
MEMORY
{

View File

@ -0,0 +1,244 @@
// Copyright (C) 2024 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <tkey/assert.h>
#include <tkey/tk1_mem.h>
#include "flash.h"
#include "spi.h"
// clang-format off
static volatile uint32_t *timer = (volatile uint32_t *)TK1_MMIO_TIMER_TIMER;
static volatile uint32_t *timer_prescaler = (volatile uint32_t *)TK1_MMIO_TIMER_PRESCALER;
static volatile uint32_t *timer_status = (volatile uint32_t *)TK1_MMIO_TIMER_STATUS;
static volatile uint32_t *timer_ctrl = (volatile uint32_t *)TK1_MMIO_TIMER_CTRL;
// clang-format on
// CPU clock frequency in Hz
#define CPUFREQ 21000000
#define PAGE_SIZE 256
static bool flash_is_busy(void);
static void flash_wait_busy(void);
static void flash_write_enable(void);
static void delay(int timeout_ms)
{
// Tick once every centisecond
*timer_prescaler = CPUFREQ / 100;
*timer = timeout_ms / 10;
*timer_ctrl |= (1 << TK1_MMIO_TIMER_CTRL_START_BIT);
while (*timer_status != 0) {
}
// Stop timer
*timer_ctrl |= (1 << TK1_MMIO_TIMER_CTRL_STOP_BIT);
}
static bool flash_is_busy(void)
{
uint8_t tx_buf = READ_STATUS_REG_1;
uint8_t rx_buf = {0x00};
assert(spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, &rx_buf,
sizeof(rx_buf)) == 0);
if (rx_buf & (1 << STATUS_REG_BUSY_BIT)) {
return true;
}
return false;
}
// Blocking until !busy
static void flash_wait_busy(void)
{
while (flash_is_busy()) {
delay(10);
}
}
static void flash_write_enable(void)
{
uint8_t tx_buf = WRITE_ENABLE;
assert(spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0) == 0);
}
void flash_write_disable(void)
{
uint8_t tx_buf = WRITE_DISABLE;
assert(spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0) == 0);
}
void flash_sector_erase(uint32_t address)
{
uint8_t tx_buf[4] = {0x00};
tx_buf[0] = SECTOR_ERASE;
tx_buf[1] = (address >> ADDR_BYTE_3_BIT) & 0xFF;
tx_buf[2] = (address >> ADDR_BYTE_2_BIT) & 0xFF;
/* tx_buf[3] is within a sector, and hence does not make a difference */
flash_write_enable();
assert(spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0) == 0);
flash_wait_busy();
}
void flash_block_32_erase(uint32_t address)
{
uint8_t tx_buf[4] = {0x00};
tx_buf[0] = BLOCK_ERASE_32K;
tx_buf[1] = (address >> ADDR_BYTE_3_BIT) & 0xFF;
tx_buf[2] = (address >> ADDR_BYTE_2_BIT) & 0xFF;
tx_buf[3] = (address >> ADDR_BYTE_1_BIT) & 0xFF;
flash_write_enable();
assert(spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0) == 0);
flash_wait_busy();
}
// 64 KiB block erase, only cares about address bits 16 and above.
void flash_block_64_erase(uint32_t address)
{
uint8_t tx_buf[4] = {0x00};
tx_buf[0] = BLOCK_ERASE_64K;
tx_buf[1] = (address >> ADDR_BYTE_3_BIT) & 0xFF;
/* tx_buf[2] and tx_buf[3] is within a block, and hence does not make a
* difference */
flash_write_enable();
assert(spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0) == 0);
flash_wait_busy();
}
void flash_release_powerdown(void)
{
uint8_t tx_buf[4] = {0x00};
tx_buf[0] = RELEASE_POWER_DOWN;
assert(spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0) == 0);
}
void flash_powerdown(void)
{
uint8_t tx_buf = POWER_DOWN;
assert(spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0) == 0);
}
void flash_read_manufacturer_device_id(uint8_t *device_id)
{
assert(device_id != NULL);
uint8_t tx_buf[4] = {0x00};
tx_buf[0] = READ_MANUFACTURER_ID;
assert(spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, device_id, 2) ==
0);
}
void flash_read_jedec_id(uint8_t *jedec_id)
{
assert(jedec_id != NULL);
uint8_t tx_buf = READ_JEDEC_ID;
assert(spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, jedec_id, 3) ==
0);
}
void flash_read_unique_id(uint8_t *unique_id)
{
assert(unique_id != NULL);
uint8_t tx_buf[5] = {0x00};
tx_buf[0] = READ_UNIQUE_ID;
assert(spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, unique_id, 8) ==
0);
}
void flash_read_status(uint8_t *status_reg)
{
assert(status_reg != NULL);
uint8_t tx_buf = READ_STATUS_REG_1;
assert(spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, status_reg, 1) ==
0);
tx_buf = READ_STATUS_REG_2;
assert(spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, status_reg + 1,
1) == 0);
}
int flash_read_data(uint32_t address, uint8_t *dest_buf, size_t size)
{
if (dest_buf == NULL) {
return -1;
}
uint8_t tx_buf[4] = {0x00};
tx_buf[0] = READ_DATA;
tx_buf[1] = (address >> ADDR_BYTE_3_BIT) & 0xFF;
tx_buf[2] = (address >> ADDR_BYTE_2_BIT) & 0xFF;
tx_buf[3] = (address >> ADDR_BYTE_1_BIT) & 0xFF;
return spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, dest_buf, size);
}
// Only handles writes where the least significant byte of the start address is
// zero.
int flash_write_data(uint32_t address, uint8_t *data, size_t size)
{
if (data == NULL) {
return -1;
}
if (size <= 0 || size > 4096) {
return -1;
}
size_t left = size;
uint8_t *p_data = data;
size_t n_bytes = 0;
uint8_t tx_buf[4] = {
PAGE_PROGRAM, /* tx_buf[0] */
(address >> ADDR_BYTE_3_BIT) & 0xFF, /* tx_buf[1] */
(address >> ADDR_BYTE_2_BIT) & 0xFF, /* tx_buf[2] */
0x00, /* tx_buf[3] */
};
while (left > 0) {
if (left >= PAGE_SIZE) {
n_bytes = PAGE_SIZE;
} else {
n_bytes = left;
}
flash_write_enable();
if (spi_transfer(tx_buf, sizeof(tx_buf), p_data, n_bytes, NULL,
0) != 0) {
return -1;
}
left -= n_bytes;
p_data += n_bytes;
address += n_bytes;
tx_buf[1] = (address >> ADDR_BYTE_3_BIT) & 0xFF;
tx_buf[2] = (address >> ADDR_BYTE_2_BIT) & 0xFF;
flash_wait_busy();
}
return 0;
}

View File

@ -0,0 +1,55 @@
// Copyright (C) 2024 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#ifndef TKEY_FLASH_H
#define TKEY_FLASH_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#define WRITE_ENABLE 0x06
#define WRITE_DISABLE 0x04
#define READ_STATUS_REG_1 0x05
#define READ_STATUS_REG_2 0x35
#define WRITE_STATUS_REG 0x01
#define PAGE_PROGRAM 0x02
#define SECTOR_ERASE 0x20
#define BLOCK_ERASE_32K 0x52
#define BLOCK_ERASE_64K 0xD8
#define CHIP_ERASE 0xC7
#define POWER_DOWN 0xB9
#define READ_DATA 0x03
#define RELEASE_POWER_DOWN 0xAB
#define READ_MANUFACTURER_ID 0x90
#define READ_JEDEC_ID 0x9F
#define READ_UNIQUE_ID 0x4B
#define ENABLE_RESET 0x66
#define RESET 0x99
#define ADDR_BYTE_3_BIT 16
#define ADDR_BYTE_2_BIT 8
#define ADDR_BYTE_1_BIT 0
#define STATUS_REG_BUSY_BIT 0
#define STATUS_REG_WEL_BIT 1
void flash_write_disable(void);
void flash_sector_erase(uint32_t address);
void flash_block_32_erase(uint32_t address);
void flash_block_64_erase(uint32_t address);
void flash_release_powerdown(void);
void flash_powerdown(void);
void flash_read_manufacturer_device_id(uint8_t *device_id);
void flash_read_jedec_id(uint8_t *jedec_id);
void flash_read_unique_id(uint8_t *unique_id);
void flash_read_status(uint8_t *status_reg);
int flash_read_data(uint32_t address, uint8_t *dest_buf, size_t size);
int flash_write_data(uint32_t address, uint8_t *data, size_t size);
#endif

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,51 @@ 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;
}
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 +605,6 @@ int main(void)
/*@ -compdestroy @*/
/* We don't care about memory leaks here. */
return (int)0xcafebabe;
}

View File

@ -0,0 +1,43 @@
// Copyright (C) 2024 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#include <stdbool.h>
#include <stdint.h>
#include <tkey/io.h>
#include <tkey/lib.h>
#include "mgmt_app.h"
// Lock down what app can start from flash slot 0.
//
// To update this, compute the BLAKE2s digest of the app.bin
static const uint8_t allowed_app_digest[32] = {
0xb6, 0x86, 0x1b, 0x26, 0xef, 0x69, 0x77, 0x12, 0xed, 0x6c, 0xca,
0xe8, 0x35, 0xb4, 0x5c, 0x01, 0x07, 0x71, 0xab, 0xce, 0x3f, 0x30,
0x79, 0xda, 0xe6, 0xf9, 0xee, 0x4b, 0xe2, 0x06, 0x95, 0x33,
};
static uint8_t current_app_digest[32];
int mgmt_app_init(uint8_t app_digest[32])
{
if (app_digest == NULL) {
return -1;
}
memcpy_s(current_app_digest, sizeof(current_app_digest), app_digest,
32);
return 0;
}
/* Authenticate an management app */
bool mgmt_app_authenticate(void)
{
return memeq(current_app_digest, allowed_app_digest, 32) != 0;
}
uint8_t *mgmt_app_allowed_digest(void)
{
return (uint8_t *)allowed_app_digest;
}

View File

@ -0,0 +1,14 @@
// Copyright (C) 2024 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#ifndef MGMT_APP_H
#define MGMT_APP_H
#include <stdbool.h>
#include <stdint.h>
int mgmt_app_init(uint8_t app_digest[32]);
bool mgmt_app_authenticate(void);
uint8_t *mgmt_app_allowed_digest(void);
#endif

View File

@ -0,0 +1,106 @@
// Copyright (C) 2024 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#include <stdint.h>
#include <tkey/assert.h>
#include <tkey/lib.h>
#include "blake2s/blake2s.h"
#include "flash.h"
#include "partition_table.h"
#include "proto.h"
static enum part_status part_status;
enum part_status part_get_status(void)
{
return part_status;
}
static void part_digest(struct partition_table *part_table, uint8_t *out_digest,
size_t out_len);
static void part_digest(struct partition_table *part_table, uint8_t *out_digest,
size_t out_len)
{
int blake2err = 0;
uint8_t key[16] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
assert(part_table != NULL);
assert(out_digest != NULL);
blake2err = blake2s(out_digest, out_len, key, sizeof(key), part_table,
sizeof(struct partition_table));
assert(blake2err == 0);
}
// part_table_read reads and verifies the partition table storage,
// first trying slot 0, then slot 1 if slot 0 does not verify.
//
// It stores the partition table in storage.
//
// Returns negative values on errors.
int part_table_read(struct partition_table_storage *storage)
{
uint32_t offset[2] = {
ADDR_PARTITION_TABLE_0,
ADDR_PARTITION_TABLE_1,
};
uint8_t check_digest[PART_DIGEST_SIZE] = {0};
if (storage == NULL) {
return -1;
}
flash_release_powerdown();
(void)memset(storage, 0x00, sizeof(*storage));
for (int i = 0; i < 2; i++) {
if (flash_read_data(offset[i], (uint8_t *)storage,
sizeof(*storage)) != 0) {
return -1;
}
part_digest(&storage->table, check_digest,
sizeof(check_digest));
if (memeq(check_digest, storage->check_digest,
sizeof(check_digest))) {
if (i == 1) {
part_status = PART_SLOT0_INVALID;
}
return 0;
}
}
return -1;
}
int part_table_write(struct partition_table_storage *storage)
{
uint32_t offset[2] = {
ADDR_PARTITION_TABLE_0,
ADDR_PARTITION_TABLE_1,
};
if (storage == NULL) {
return -1;
}
part_digest(&storage->table, storage->check_digest,
sizeof(storage->check_digest));
for (int i = 0; i < 2; i++) {
flash_sector_erase(offset[i]);
if (flash_write_data(offset[i], (uint8_t *)storage,
sizeof(*storage)) != 0) {
return -1;
}
}
return 0;
}

View File

@ -0,0 +1,109 @@
// Copyright (C) 2024 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#ifndef PARTITION_TABLE_H
#define PARTITION_TABLE_H
#include <stdint.h>
/* ---- Flash ---- ---- */
/* name size start addr */
/* ---- ---- ---- */
/* bitstream 128KiB 0x00 */
/* ---- ---- ---- */
/* Partition 64KiB 0x20000 */
/* ---- ---- ---- */
/* Pre load 1 128KiB 0x30000 */
/* Pre load 2 128KiB 0x50000 */
/* ---- ---- ---- */
/* storage 1 128KiB 0x70000 */
/* storage 2 128KiB 0x90000 */
/* storage 3 128KiB 0xB0000 */
/* storage 4 128KiB 0xD0000 */
/* ---- ---- ---- */
/* Partition2 64KiB 0xf0000 */
/* To simplify all blocks are aligned with the 64KiB blocks on the W25Q80DL
* flash. */
#define PART_TABLE_VERSION 1
#define ADDR_BITSTREAM 0UL
#define SIZE_BITSTREAM 0x20000UL // 128KiB
#define ADDR_PARTITION_TABLE_0 (ADDR_BITSTREAM + SIZE_BITSTREAM)
#define ADDR_PARTITION_TABLE_1 0xf0000
#define SIZE_PARTITION_TABLE \
0x10000UL // 64KiB, 60 KiB reserved, 2 flash pages (2 x 4KiB) for the
// partition table
#define N_PRELOADED_APP 2
#define ADDR_PRE_LOADED_APP_0 (ADDR_PARTITION_TABLE_0 + SIZE_PARTITION_TABLE)
#define SIZE_PRE_LOADED_APP 0x20000UL // 128KiB
#define ADDR_STORAGE_AREA \
(ADDR_PRE_LOADED_APP_0 + (N_PRELOADED_APP * SIZE_PRE_LOADED_APP))
#define SIZE_STORAGE_AREA 0x20000UL // 128KiB
#define N_STORAGE_AREA 4
#define PART_DIGEST_SIZE 16
enum part_status {
PART_SLOT0_INVALID = 1,
};
/* Partition Table */
/*- Table header */
/* - 1 bytes Version */
/**/
/*- Pre-loaded device app 1 */
/* - 4 bytes length. */
/* - 32 bytes digest. */
/* - 64 bytes signature. */
/**/
/*- Pre-loaded device app 2 */
/* - 4 bytes length. */
/* - 32 bytes digest. */
/* - 64 bytes signature. */
/**/
/*- Device app storage area */
/* - 1 byte status. */
/* - 16 bytes random nonce. */
/* - 16 bytes authentication tag. */
struct auth_metadata {
uint8_t nonce[16];
uint8_t authentication_digest[16];
} __attribute__((packed));
struct pre_loaded_app_metadata {
uint32_t size;
uint8_t digest[32];
uint8_t signature[64];
} __attribute__((packed));
struct app_storage_area {
uint8_t status;
struct auth_metadata auth;
} __attribute__((packed));
struct table_header {
uint8_t version;
} __attribute__((packed));
struct partition_table {
struct table_header header;
struct pre_loaded_app_metadata pre_app_data[N_PRELOADED_APP];
struct app_storage_area app_storage[N_STORAGE_AREA];
} __attribute__((packed));
struct partition_table_storage {
struct partition_table table;
uint8_t check_digest[PART_DIGEST_SIZE];
} __attribute__((packed));
enum part_status part_get_status(void);
int part_table_read(struct partition_table_storage *storage);
int part_table_write(struct partition_table_storage *storage);
#endif

View File

@ -0,0 +1,198 @@
// 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"
static uint32_t slot_to_start_address(uint8_t slot)
{
return ADDR_PRE_LOADED_APP_0 + slot * SIZE_PRE_LOADED_APP;
}
/* Loads a preloaded app from flash to app RAM */
int preload_load(struct partition_table *part_table, uint8_t from_slot)
{
if (part_table == NULL) {
return -5;
}
if (from_slot >= N_PRELOADED_APP) {
return -4;
}
/*Check for a valid app in flash */
if (part_table->pre_app_data[from_slot].size == 0) {
return -1;
}
uint8_t *loadaddr = (uint8_t *)TK1_RAM_BASE;
/* Read from flash, straight into RAM */
int ret = flash_read_data(slot_to_start_address(from_slot), loadaddr,
part_table->pre_app_data[from_slot].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, uint8_t to_slot)
{
if (part_table == NULL || data == NULL) {
return -5;
}
if (to_slot >= N_PRELOADED_APP) {
return -4;
}
/* Check if we are allowed to store */
if (!mgmt_app_authenticate()) {
return -3;
}
/* Check for a valid app in flash, bale out if it already exists */
if (part_table->pre_app_data[to_slot].size != 0) {
return -1;
}
if ((offset + size) > SIZE_PRE_LOADED_APP || size > 4096) {
/* Writing outside of area */
return -2;
}
uint32_t address = slot_to_start_address(to_slot) + 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_storage *part_table_storage,
size_t app_size, uint8_t app_digest[32],
uint8_t app_signature[64], uint8_t to_slot)
{
struct partition_table *part_table = &part_table_storage->table;
if (part_table == NULL || app_digest == NULL || app_signature == NULL) {
return -5;
}
if (to_slot >= N_PRELOADED_APP) {
return -4;
}
/* Check if we are allowed to store */
if (!mgmt_app_authenticate()) {
return -3;
}
/* Check for a valid app in flash, bale out if it already exists */
if (part_table->pre_app_data[to_slot].size != 0) {
return -1;
}
if (app_size == 0 || app_size > SIZE_PRE_LOADED_APP) {
return -2;
}
part_table->pre_app_data[to_slot].size = app_size;
memcpy_s(part_table->pre_app_data[to_slot].digest,
sizeof(part_table->pre_app_data[to_slot].digest), app_digest,
32);
memcpy_s(part_table->pre_app_data[to_slot].signature,
sizeof(part_table->pre_app_data[to_slot].signature),
app_signature, 64);
debug_puts("preload_*_final: size: ");
debug_putinthex(app_size);
debug_lf();
if (part_table_write(part_table_storage) != 0) {
return -6;
}
return 0;
}
int preload_delete(struct partition_table_storage *part_table_storage,
uint8_t slot)
{
struct partition_table *part_table = &part_table_storage->table;
if (part_table_storage == NULL) {
return -5;
}
if (slot >= N_PRELOADED_APP) {
return -4;
}
/* Check if we are allowed to deleted */
if (!mgmt_app_authenticate()) {
return -3;
}
/*Check for a valid app in flash */
if (part_table->pre_app_data[slot].size == 0) {
// Nothing to do.
return 0;
}
part_table->pre_app_data[slot].size = 0;
(void)memset(part_table->pre_app_data[slot].digest, 0,
sizeof(part_table->pre_app_data[slot].digest));
(void)memset(part_table->pre_app_data[slot].signature, 0,
sizeof(part_table->pre_app_data[slot].signature));
if (part_table_write(part_table_storage) != 0) {
return -6;
}
/* Assumes the area is 64 KiB block aligned */
flash_block_64_erase(
slot_to_start_address(slot)); // Erase first 64 KB block
flash_block_64_erase(slot_to_start_address(slot) +
0x10000); // Erase first 64 KB block
return 0;
}
int preload_get_digsig(struct partition_table *part_table,
uint8_t app_digest[32], uint8_t app_signature[64],
uint8_t slot)
{
if (part_table == NULL || app_digest == NULL || app_signature == NULL) {
return -5;
}
if (slot >= N_PRELOADED_APP) {
return -4;
}
/* Check if we are allowed to read */
if (!mgmt_app_authenticate()) {
return -3;
}
memcpy_s(app_digest, 32, part_table->pre_app_data[slot].digest,
sizeof(part_table->pre_app_data[slot].digest));
memcpy_s(app_signature, 64, part_table->pre_app_data[slot].signature,
sizeof(part_table->pre_app_data[slot].signature));
return 0;
}

View File

@ -0,0 +1,24 @@
// 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>
int preload_load(struct partition_table *part_table, uint8_t from_slot);
int preload_store(struct partition_table *part_table, uint32_t offset,
uint8_t *data, size_t size, uint8_t to_slot);
int preload_store_finalize(struct partition_table_storage *part_table_storage,
size_t app_size, uint8_t app_digest[32],
uint8_t app_signature[64], uint8_t to_slot);
int preload_delete(struct partition_table_storage *part_table_storage,
uint8_t slot);
int preload_get_digsig(struct partition_table *part_table,
uint8_t app_digest[32], uint8_t app_signature[64],
uint8_t slot);
#endif

View File

@ -0,0 +1,28 @@
// Copyright (C) 2025 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#ifndef TKEY_RESETINFO_H
#define TKEY_RESETINFO_H
#include <stdint.h>
#define TK1_MMIO_RESETINFO_BASE 0xd0000f00
#define TK1_MMIO_RESETINFO_SIZE 0x100
enum reset_start {
START_DEFAULT = 0, // Probably cold boot
START_FLASH0 = 1,
START_FLASH1 = 2,
START_FLASH0_VER = 3,
START_FLASH1_VER = 4,
START_CLIENT = 5,
START_CLIENT_VER = 6,
};
struct reset {
uint32_t type; // Reset type
uint8_t app_digest[32]; // Program digest
uint8_t next_app_data[220]; // Data to leave around for next app
};
#endif

View File

@ -0,0 +1,29 @@
// Copyright (C) 2024 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#include "rng.h"
#include <tkey/tk1_mem.h>
#include <stdint.h>
// clang-format off
static volatile uint32_t *trng_status = (volatile uint32_t *)TK1_MMIO_TRNG_STATUS;
static volatile uint32_t *trng_entropy = (volatile uint32_t *)TK1_MMIO_TRNG_ENTROPY;
// clang-format on
//
//
uint32_t rng_get_word(void)
{
while ((*trng_status & (1 << TK1_MMIO_TRNG_STATUS_READY_BIT)) == 0) {
}
return *trng_entropy;
}
uint32_t rng_xorwow(uint32_t state, uint32_t acc)
{
state ^= state << 13;
state ^= state >> 17;
state ^= state << 5;
state += acc;
return state;
}

View File

@ -0,0 +1,11 @@
// Copyright (C) 2024 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#ifndef RNG_H
#define RNG_H
#include <stdint.h>
uint32_t rng_get_word(void);
uint32_t rng_xorwow(uint32_t state, uint32_t acc);
#endif

View File

@ -0,0 +1,100 @@
// Copyright (C) 2024 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#include "spi.h"
#include <tkey/assert.h>
#include <tkey/tk1_mem.h>
#include <stddef.h>
#include <stdint.h>
// clang-format off
static volatile uint32_t *spi_en = (volatile uint32_t *)(TK1_MMIO_TK1_BASE | 0x200);
static volatile uint32_t *spi_xfer = (volatile uint32_t *)(TK1_MMIO_TK1_BASE | 0x204);
static volatile uint32_t *spi_data = (volatile uint32_t *)(TK1_MMIO_TK1_BASE | 0x208);
// clang-format on
static int spi_ready(void);
static void spi_enable(void);
static void spi_disable(void);
static void spi_write(uint8_t *cmd, size_t size);
static void spi_read(uint8_t *buf, size_t size);
// returns non-zero when the SPI-master is ready, and zero if not
// ready. This can be used to check if the SPI-master is available
// in the hardware.
static int spi_ready(void)
{
return *spi_xfer;
}
static void spi_enable(void)
{
*spi_en = 1;
}
static void spi_disable(void)
{
*spi_en = 0;
}
static void spi_write(uint8_t *cmd, size_t size)
{
assert(cmd != NULL);
for (size_t i = 0; i < size; i++) {
while (!spi_ready()) {
}
*spi_data = cmd[i];
*spi_xfer = 1;
}
while (!spi_ready()) {
}
}
static void spi_read(uint8_t *buf, size_t size)
{
assert(buf != NULL);
while (!spi_ready()) {
}
for (size_t i = 0; i < size; i++) {
*spi_data = 0x00;
*spi_xfer = 1;
// wait until spi master is done
while (!spi_ready()) {
}
buf[i] = (*spi_data & 0xff);
}
}
// Function to both read and write data to the connected SPI flash.
int spi_transfer(uint8_t *cmd, size_t cmd_size, uint8_t *tx_buf, size_t tx_size,
uint8_t *rx_buf, size_t rx_size)
{
if (cmd == NULL || cmd_size == 0) {
return -1;
}
spi_enable();
spi_write(cmd, cmd_size);
if (tx_buf != NULL && tx_size != 0) {
spi_write(tx_buf, tx_size);
}
if (rx_buf != NULL && rx_size != 0) {
spi_read(rx_buf, rx_size);
}
spi_disable();
return 0;
}

View File

@ -0,0 +1,13 @@
// Copyright (C) 2024 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#ifndef TKEY_SPI_H
#define TKEY_SPI_H
#include <stddef.h>
#include <stdint.h>
int spi_transfer(uint8_t *cmd, size_t cmd_size, uint8_t *tx_buf, size_t tx_size,
uint8_t *rx_buf, size_t rx_size);
#endif

View File

@ -8,8 +8,11 @@
enum state {
FW_STATE_INITIAL,
FW_STATE_WAITCOMMAND,
FW_STATE_LOADING,
FW_STATE_RUN,
FW_STATE_LOAD_FLASH,
FW_STATE_LOAD_FLASH_MGMT,
FW_STATE_START,
FW_STATE_FAIL,
FW_STATE_MAX,
};

View File

@ -0,0 +1,278 @@
// 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 "auth_app.h"
#include "flash.h"
#include "partition_table.h"
#include "storage.h"
/* Returns the index of the first empty area. If there is no empty area -1 is
* returned. */
static int get_first_empty(struct partition_table *part_table)
{
if (part_table == NULL) {
return -4;
}
for (uint8_t i = 0; i < N_STORAGE_AREA; i++) {
if (part_table->app_storage[i].status == 0x00) {
return i;
}
}
return -1;
}
static int index_to_address(int index, uint32_t *address)
{
if (address == NULL) {
return -4;
}
if ((index < 0) || (index >= N_STORAGE_AREA)) {
return -1;
}
*address = ADDR_STORAGE_AREA + index * SIZE_STORAGE_AREA;
return 0;
}
/* Returns the index of the area an app has allocated. If no area is
* authenticated -1 is returned. */
static int storage_get_area(struct partition_table *part_table)
{
if (part_table == NULL) {
return -4;
}
for (uint8_t i = 0; i < N_STORAGE_AREA; i++) {
if (part_table->app_storage[i].status != 0x00) {
if (auth_app_authenticate(
&part_table->app_storage[i].auth)) {
return i;
}
}
}
return -1;
}
/* Allocate a new area for an app. Returns zero if a new area is allocated, one
* if an area already was allocated, and negative values for errors. */
int storage_allocate_area(struct partition_table_storage *part_table_storage)
{
if (part_table_storage == NULL) {
return -4;
}
struct partition_table *part_table = &part_table_storage->table;
if (storage_get_area(part_table) != -1) {
/* Already has an area */
return 1;
}
int index = get_first_empty(part_table);
if (index == -1) {
/* No empty slot */
return -1;
}
uint32_t start_address = 0;
int err = index_to_address(index, &start_address);
if (err) {
return -3;
}
/* Allocate the empty index found */
/* Erase area first */
/* Assumes the area is 64 KiB block aligned */
flash_block_64_erase(start_address); // Erase first 64 KB block
flash_block_64_erase(start_address +
0x10000); // Erase second 64 KB block
/* Write partition table lastly */
part_table->app_storage[index].status = 0x01;
auth_app_create(&part_table->app_storage[index].auth);
if (part_table_write(part_table_storage) != 0) {
return -5;
}
return 0;
}
/* Dealloacate a previously allocated storage area. Returns zero on success, and
* non-zero on errors. */
int storage_deallocate_area(struct partition_table_storage *part_table_storage)
{
if (part_table_storage == NULL) {
return -4;
}
struct partition_table *part_table = &part_table_storage->table;
int index = storage_get_area(part_table);
if (index == -1) {
/* No area to deallocate */
return -1;
}
uint32_t start_address = 0;
int err = index_to_address(index, &start_address);
if (err) {
return -3;
}
/* Erase area first */
/* Assumes the area is 64 KiB block aligned */
flash_block_64_erase(start_address); // Erase first 64 KB block
flash_block_64_erase(start_address +
0x10000); // Erase second 64 KB block
/* Clear partition table lastly */
part_table->app_storage[index].status = 0;
(void)memset(part_table->app_storage[index].auth.nonce, 0x00,
sizeof(part_table->app_storage[index].auth.nonce));
(void)memset(
part_table->app_storage[index].auth.authentication_digest, 0x00,
sizeof(part_table->app_storage[index].auth.authentication_digest));
if (part_table_write(part_table_storage) != 0) {
return -5;
}
return 0;
}
/* Erases sector. Offset of a sector to begin erasing, must be a multiple of
* the sector size. Size to erase in bytes, must be a multiple of the sector *
* size. Returns zero on success, negative error code on failure */
int storage_erase_sector(struct partition_table *part_table, uint32_t offset,
size_t size)
{
if (part_table == NULL) {
return -4;
}
int index = storage_get_area(part_table);
if (index == -1) {
/* No allocated area */
return -1;
}
uint32_t start_address = 0;
int err = index_to_address(index, &start_address);
if (err) {
return -3;
}
/* Cannot only erase entire sectors */
if (offset % 4096 != 0) {
return -2;
}
/* Cannot erase less than one sector */
if (size < 4096 || size > SIZE_STORAGE_AREA || size % 4096 != 0) {
return -2;
}
if ((offset + size) >= SIZE_STORAGE_AREA) {
return -2;
}
uint32_t address = start_address + offset;
debug_puts("storage: erase addr: ");
debug_putinthex(address);
debug_lf();
for (size_t i = 0; i < size; i += 4096) {
flash_sector_erase(address);
address += 4096;
}
return 0;
}
/* Writes the specified data to the offset inside of the
* allocated area. Assumes area has been erased before hand.
* Currently only handles writes to one sector, hence max size of 4096 bytes.
* Returns zero on success. */
int storage_write_data(struct partition_table *part_table, uint32_t offset,
uint8_t *data, size_t size)
{
if (part_table == NULL || data == NULL) {
return -4;
}
int index = storage_get_area(part_table);
if (index == -1) {
/* No allocated area */
return -1;
}
uint32_t start_address = 0;
int err = index_to_address(index, &start_address);
if (err) {
return -3;
}
if ((offset + size) > SIZE_STORAGE_AREA || size > 4096) {
/* Writing outside of area */
return -2;
}
uint32_t address = start_address + offset;
debug_puts("storage: write to addr: ");
debug_putinthex(address);
debug_lf();
return flash_write_data(address, data, size);
}
/* Reads size bytes of data at the specified offset inside of
* the allocated area. Returns zero on success. Only read limit
* is the size of the allocated area */
int storage_read_data(struct partition_table *part_table, uint32_t offset,
uint8_t *data, size_t size)
{
if (part_table == NULL || data == NULL) {
return -4;
}
int index = storage_get_area(part_table);
if (index == -1) {
/* No allocated area */
return -1;
}
uint32_t start_address = 0;
int err = index_to_address(index, &start_address);
if (err) {
return -3;
}
if ((offset + size) > SIZE_STORAGE_AREA) {
/* Reading outside of area */
return -2;
}
uint32_t address = start_address + offset;
debug_puts("storage: read from addr: ");
debug_putinthex(address);
debug_lf();
return flash_read_data(address, data, size);
}

View File

@ -0,0 +1,22 @@
// Copyright (C) 2024 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#ifndef STORAGE_H
#define STORAGE_H
#include "partition_table.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
int storage_deallocate_area(struct partition_table_storage *part_table_storage);
int storage_allocate_area(struct partition_table_storage *part_table_storage);
int storage_erase_sector(struct partition_table *part_table, uint32_t offset,
size_t size);
int storage_write_data(struct partition_table *part_table, uint32_t offset,
uint8_t *data, size_t size);
int storage_read_data(struct partition_table *part_table, uint32_t offset,
uint8_t *data, size_t size);
#endif

View File

@ -5,29 +5,112 @@
#include <stdint.h>
#include <tkey/assert.h>
#include <tkey/led.h>
#include <tkey/debug.h>
#include <tkey/lib.h>
#include <tkey/tk1_mem.h>
#include "partition_table.h"
#include "preload_app.h"
#include "storage.h"
#include "../tk1/resetinfo.h"
#include "../tk1/syscall_num.h"
// clang-format off
static volatile uint32_t *system_reset = (volatile uint32_t *)TK1_MMIO_TK1_SYSTEM_RESET;
static volatile uint32_t *udi = (volatile uint32_t *)TK1_MMIO_TK1_UDI_FIRST;
static volatile uint32_t *system_reset = (volatile uint32_t *)TK1_MMIO_TK1_SYSTEM_RESET;
static volatile uint32_t *udi = (volatile uint32_t *)TK1_MMIO_TK1_UDI_FIRST;
static volatile struct reset *resetinfo = (volatile struct reset *)TK1_MMIO_RESETINFO_BASE;
// clang-format on
int32_t syscall_handler(uint32_t number, uint32_t arg1)
extern struct partition_table_storage part_table_storage;
extern uint8_t part_status;
int32_t syscall_handler(uint32_t number, uint32_t arg1, uint32_t arg2,
uint32_t arg3)
{
struct reset *userreset = (struct reset *)arg1;
switch (number) {
case TK1_SYSCALL_RESET:
if (arg2 > sizeof(resetinfo->next_app_data)) {
return -1;
}
(void)memset((void *)resetinfo, 0, sizeof(*resetinfo));
resetinfo->type = userreset->type;
memcpy((void *)resetinfo->app_digest, userreset->app_digest,
32);
memcpy((void *)resetinfo->next_app_data,
userreset->next_app_data, arg2);
*system_reset = 1;
// Should not be reached.
assert(1 == 2);
break;
case TK1_SYSCALL_ALLOC_AREA:
if (storage_allocate_area(&part_table_storage) < 0) {
debug_puts("couldn't allocate storage area\n");
return -1;
}
return 0;
case TK1_SYSCALL_SET_LED:
led_set(arg1);
case TK1_SYSCALL_DEALLOC_AREA:
if (storage_deallocate_area(&part_table_storage) < 0) {
debug_puts("couldn't deallocate storage area\n");
return -1;
}
return 0;
case TK1_SYSCALL_WRITE_DATA:
if (storage_write_data(&part_table_storage.table, arg1,
(uint8_t *)arg2, arg3) < 0) {
debug_puts("couldn't write storage area\n");
return -1;
}
return 0;
case TK1_SYSCALL_READ_DATA:
if (storage_read_data(&part_table_storage.table, arg1,
(uint8_t *)arg2, arg3) < 0) {
debug_puts("couldn't read storage area\n");
return -1;
}
return 0;
case TK1_SYSCALL_GET_VIDPID:
// UDI is 2 words: VID/PID & serial. Return just the
// first word. Serial is kept secret to the device
// app.
return udi[0];
case TK1_SYSCALL_PRELOAD_DELETE:
return preload_delete(&part_table_storage, 1);
case TK1_SYSCALL_PRELOAD_STORE:
// arg1 offset
// arg2 data
// arg3 size
// always using slot 1
return preload_store(&part_table_storage.table, arg1,
(uint8_t *)arg2, arg3, 1);
case TK1_SYSCALL_PRELOAD_STORE_FIN:
// arg1 app_size
// arg2 app_digest
// arg3 app_signature
// always using slot 1
return preload_store_finalize(&part_table_storage, arg1,
(uint8_t *)arg2, (uint8_t *)arg3,
1);
case TK1_SYSCALL_PRELOAD_GET_DIGSIG:
return preload_get_digsig(&part_table_storage.table,
(uint8_t *)arg1, (uint8_t *)arg2, 1);
case TK1_SYSCALL_STATUS:
return part_get_status();
default:
assert(1 == 2);
}

View File

@ -6,8 +6,18 @@
enum syscall_num {
TK1_SYSCALL_RESET = 1,
TK1_SYSCALL_SET_LED = 10,
TK1_SYSCALL_GET_VIDPID = 12,
TK1_SYSCALL_ALLOC_AREA = 2,
TK1_SYSCALL_DEALLOC_AREA = 3,
TK1_SYSCALL_WRITE_DATA = 4,
TK1_SYSCALL_READ_DATA = 5,
TK1_SYSCALL_ERASE_DATA = 6,
TK1_SYSCALL_GET_VIDPID = 7,
TK1_SYSCALL_PRELOAD_STORE = 8,
TK1_SYSCALL_PRELOAD_STORE_FIN = 9,
TK1_SYSCALL_PRELOAD_DELETE = 10,
TK1_SYSCALL_PRELOAD_GET_DIGSIG = 11,
TK1_SYSCALL_REG_MGMT = 12,
TK1_SYSCALL_STATUS = 13,
};
#endif