mirror of
https://github.com/tillitis/tillitis-key1.git
synced 2025-04-21 15:46:31 -04:00
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:
parent
1ff6e0262f
commit
e9ddf29ce9
@ -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 \
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
76
hw/application_fpga/fw/tk1/auth_app.c
Normal file
76
hw/application_fpga/fw/tk1/auth_app.c
Normal 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;
|
||||
}
|
14
hw/application_fpga/fw/tk1/auth_app.h
Normal file
14
hw/application_fpga/fw/tk1/auth_app.h
Normal 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
|
@ -7,7 +7,7 @@ OUTPUT_ARCH("riscv")
|
||||
ENTRY(_start)
|
||||
|
||||
/* Define stack size */
|
||||
STACK_SIZE = 0xEF0; /* 3824 B */
|
||||
STACK_SIZE = 3000;
|
||||
|
||||
MEMORY
|
||||
{
|
||||
|
244
hw/application_fpga/fw/tk1/flash.c
Normal file
244
hw/application_fpga/fw/tk1/flash.c
Normal 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;
|
||||
}
|
55
hw/application_fpga/fw/tk1/flash.h
Normal file
55
hw/application_fpga/fw/tk1/flash.h
Normal 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
|
@ -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;
|
||||
}
|
||||
|
43
hw/application_fpga/fw/tk1/mgmt_app.c
Normal file
43
hw/application_fpga/fw/tk1/mgmt_app.c
Normal 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;
|
||||
}
|
14
hw/application_fpga/fw/tk1/mgmt_app.h
Normal file
14
hw/application_fpga/fw/tk1/mgmt_app.h
Normal 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
|
106
hw/application_fpga/fw/tk1/partition_table.c
Normal file
106
hw/application_fpga/fw/tk1/partition_table.c
Normal 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;
|
||||
}
|
109
hw/application_fpga/fw/tk1/partition_table.h
Normal file
109
hw/application_fpga/fw/tk1/partition_table.h
Normal 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
|
198
hw/application_fpga/fw/tk1/preload_app.c
Normal file
198
hw/application_fpga/fw/tk1/preload_app.c
Normal 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;
|
||||
}
|
24
hw/application_fpga/fw/tk1/preload_app.h
Normal file
24
hw/application_fpga/fw/tk1/preload_app.h
Normal 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
|
28
hw/application_fpga/fw/tk1/resetinfo.h
Normal file
28
hw/application_fpga/fw/tk1/resetinfo.h
Normal 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
|
29
hw/application_fpga/fw/tk1/rng.c
Normal file
29
hw/application_fpga/fw/tk1/rng.c
Normal 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;
|
||||
}
|
11
hw/application_fpga/fw/tk1/rng.h
Normal file
11
hw/application_fpga/fw/tk1/rng.h
Normal 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
|
100
hw/application_fpga/fw/tk1/spi.c
Normal file
100
hw/application_fpga/fw/tk1/spi.c
Normal 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;
|
||||
}
|
13
hw/application_fpga/fw/tk1/spi.h
Normal file
13
hw/application_fpga/fw/tk1/spi.h
Normal 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
|
@ -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,
|
||||
};
|
||||
|
278
hw/application_fpga/fw/tk1/storage.c
Normal file
278
hw/application_fpga/fw/tk1/storage.c
Normal 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);
|
||||
}
|
22
hw/application_fpga/fw/tk1/storage.h
Normal file
22
hw/application_fpga/fw/tk1/storage.h
Normal 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
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user