Add filesystem code and storage syscalls

Adds syscalls:

- ALLOCATE_AREA
- DEALLOCATE_AREA
- WRITE_DATA
- READ_DATA

and code to access the filesystem and the flash over SPI.

Based on original work by Daniel Jobson <jobson@tillitis.see> for
these files:

- auth_app.[ch]
- flash.[ch]
- spi.[ch]
- partition_table.[ch]
- rng.[ch]
- storage.[ch]

which are used with small changes to integrate with the new syscall
method.

Co-authored-by: Daniel Jobson <jobson@tillitis.se>
Co-authored-by: Mikael Ågren <mikael@tillitis.se>
This commit is contained in:
Michael Cardell Widerkrantz 2025-03-12 16:17:48 +01:00
parent e86e60fcfe
commit 6ef6c36f6f
No known key found for this signature in database
GPG Key ID: D3DB3DDF57E704E5
19 changed files with 974 additions and 7 deletions

View File

@ -126,7 +126,13 @@ FIRMWARE_OBJS = \
$(P)/fw/tk1/proto.o \
$(P)/fw/tk1/blake2s/blake2s.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
FIRMWARE_SOURCES = \
$(P)/fw/tk1/main.c \

View File

@ -121,13 +121,48 @@ 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");
anyfailed = 1;
}
puts(IO_CDC, "\r\nAllocating storage area...");
if (syscall(TK1_SYSCALL_ALLOC_AREA, 0, 0, 0) != 0) {
failmsg("Failed to allocate storage area");
}
puts(IO_CDC, "done.\r\n");
puts(IO_CDC, "\r\nWriting to storage area...");
uint8_t out_data[14] = { 0, 1, 2, 3, 4, 5, 6, 7, 8,
9, 10, 11, 12, 13 };
if (syscall(TK1_SYSCALL_WRITE_DATA, 0, (uint32_t)out_data, sizeof(out_data)) != 0) {
failmsg("Failed to write to storage area");
}
puts(IO_CDC, "done.\r\n");
puts(IO_CDC, "\r\nReading from storage area...");
uint8_t in_data[14] = { 0 };
if (syscall(TK1_SYSCALL_READ_DATA, 0, (uint32_t)in_data, sizeof(in_data)) != 0) {
failmsg("Failed to write to storage area");
}
if (!memeq(in_data, out_data, sizeof(in_data))) {
failmsg("Failed to read back data from storage area");
anyfailed = 1;
}
puts(IO_CDC, "done.\r\n");
puts(IO_CDC, "\r\nDeallocating storage area...");
if (syscall(TK1_SYSCALL_DEALLOC_AREA, 0, 0, 0) != 0) {
failmsg("Failed to deallocate storage area");
}
puts(IO_CDC, "done.\r\n");
uint32_t cdi_local[CDI_WORDS];
uint32_t cdi_local2[CDI_WORDS];
wordcpy_s(cdi_local, CDI_WORDS, (void *)cdi, CDI_WORDS);
@ -223,7 +258,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,68 @@
// Copyright (C) 2024 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#include <stdbool.h>
#include <stdint.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)
{
/* TODO: Check so the CDI is non-zero? */
blake2s_ctx ctx = {0};
// Generate a 16 byte authentication digest
blake2s_init(&ctx, 16, NULL, 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)
{
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)
{
uint8_t nonce[16];
uint8_t auth_digest[16];
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)
{
uint8_t auth_digest[16];
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,217 @@
// Copyright (C) 2024 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#include <stdbool.h>
#include <stddef.h>
#include <stdint.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 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);
}
bool flash_is_busy(void)
{
uint8_t tx_buf = READ_STATUS_REG_1;
uint8_t rx_buf = {0x00};
spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, &rx_buf, sizeof(rx_buf));
if (rx_buf & (1 << STATUS_REG_BUSY_BIT)) {
return true;
}
return false;
}
// Blocking until !busy
void flash_wait_busy(void)
{
while (flash_is_busy()) {
delay(10);
}
}
void flash_write_enable(void)
{
uint8_t tx_buf = WRITE_ENABLE;
spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0);
}
void flash_write_disable(void)
{
uint8_t tx_buf = WRITE_DISABLE;
spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, NULL, 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();
spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, NULL, 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();
spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, NULL, 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();
spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0);
flash_wait_busy();
}
void flash_release_powerdown(void)
{
uint8_t tx_buf[4] = {0x00};
tx_buf[0] = RELEASE_POWER_DOWN;
spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0);
}
void flash_powerdown(void)
{
uint8_t tx_buf = POWER_DOWN;
spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0);
}
void flash_read_manufacturer_device_id(uint8_t *device_id)
{
uint8_t tx_buf[4] = {0x00};
tx_buf[0] = READ_MANUFACTURER_ID;
spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, device_id, 2);
}
void flash_read_jedec_id(uint8_t *jedec_id)
{
uint8_t tx_buf = READ_JEDEC_ID;
spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, jedec_id, 3);
}
void flash_read_unique_id(uint8_t *unique_id)
{
uint8_t tx_buf[5] = {0x00};
tx_buf[0] = READ_UNIQUE_ID;
spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, unique_id, 8);
}
void flash_read_status(uint8_t *status_reg)
{
uint8_t tx_buf = READ_STATUS_REG_1;
spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, status_reg, 1);
tx_buf = READ_STATUS_REG_2;
spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, status_reg + 1, 1);
}
int flash_read_data(uint32_t address, uint8_t *dest_buf, size_t size)
{
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 (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,58 @@
// 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
bool flash_is_busy(void);
void flash_wait_busy(void);
void flash_write_enable(void);
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

@ -12,6 +12,7 @@
#include <tkey/tk1_mem.h>
#include "blake2s/blake2s.h"
#include "partition_table.h"
#include "proto.h"
#include "state.h"
#include "syscall_enable.h"
@ -35,6 +36,8 @@ static volatile uint32_t *ram_addr_rand = (volatile uint32_t *)TK1_MMIO_TK1_R
static volatile uint32_t *ram_data_rand = (volatile uint32_t *)TK1_MMIO_TK1_RAM_DATA_RAND;
// clang-format on
struct partition_table part_table;
// Context for the loading of a TKey program
struct context {
uint32_t left; // Bytes left to receive
@ -418,6 +421,11 @@ int main(void)
scramble_ram();
if (part_table_read(&part_table) != 0) {
// Couldn't read or create partition table
assert(1 != 2);
}
#if defined(SIMULATION)
run(&ctx);
#endif

View File

@ -0,0 +1,50 @@
// Copyright (C) 2024 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#include <stdint.h>
#include <tkey/lib.h>
#include "flash.h"
#include "partition_table.h"
#include "proto.h"
int part_table_read(struct partition_table *part_table)
{
// Read from flash, if it exists, otherwise create a new one.
flash_release_powerdown();
memset(part_table, 0x00, sizeof(*part_table));
flash_read_data(ADDR_PARTITION_TABLE, (uint8_t *)part_table,
sizeof(*part_table));
// TODO: Implement redundancy and consistency check
if (part_table->header.version != PART_TABLE_VERSION) {
// Partition table is not ours. Make a new one, and store it.
memset(part_table, 0x00, sizeof(*part_table));
part_table->header.version = PART_TABLE_VERSION;
for (int i = 0; i < 4; i++) {
part_table->app_storage[i].addr_start =
(ADDR_STORAGE_AREA + i * SIZE_STORAGE_AREA);
part_table->app_storage[i].size = SIZE_STORAGE_AREA;
}
part_table_write(part_table);
}
// Now the partition table is synced between flash and RAM.
return 0;
}
int part_table_write(struct partition_table *part_table)
{
flash_sector_erase(ADDR_PARTITION_TABLE);
flash_write_data(ADDR_PARTITION_TABLE, (uint8_t *)part_table,
sizeof(*part_table));
return 0;
}

View File

@ -0,0 +1,105 @@
// 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 128KiB 0x30000 */
/* ---- ---- ---- */
/* storage 1 128KiB 0x50000 */
/* storage 2 128KiB 0x70000 */
/* storage 3 128KiB 0x90000 */
/* storage 4 128KiB 0xB0000 */
/* ---- ---- ---- */
/* 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 (ADDR_BITSTREAM + SIZE_BITSTREAM)
#define SIZE_PARTITION_TABLE \
0x10000UL // 64KiB, 60 KiB reserved, 2 flash pages (2 x 4KiB) for the
// partition table
#define ADDR_PRE_LOADED_APP (ADDR_PARTITION_TABLE + SIZE_PARTITION_TABLE)
#define SIZE_PRE_LOADED_APP 0x20000UL // 128KiB
#define ADDR_STORAGE_AREA (ADDR_PRE_LOADED_APP + SIZE_PRE_LOADED_APP)
#define SIZE_STORAGE_AREA 0x20000UL // 128KiB
#define N_STORAGE_AREA 4
#define EMPTY_AREA
/* Partition Table */
/*- Table header */
/* - 1 bytes Version */
/**/
/*- Management device app */
/* - Status. */
/* - 16 byte random nonce. */
/* - 16 byte authentication digest. */
/**/
/*- Pre-loaded device app */
/* - 1 byte status. */
/* - 4 bytes length. */
/* - 16 bytes random nonce. */
/* - 16 bytes authentication digest. */
/**/
/*- Device app storage area */
/* - 1 byte status. */
/* - 16 bytes random nonce. */
/* - 16 bytes authentication tag. */
/* - 4 bytes physical start address. */
/* - 4 bytes physical end address. */
struct auth_metadata {
uint8_t nonce[16];
uint8_t authentication_digest[16];
} __attribute__((packed));
struct management_app_metadata {
uint8_t status;
struct auth_metadata auth;
} __attribute__((packed));
struct pre_loaded_app_metadata {
uint8_t status;
uint32_t size;
struct auth_metadata auth;
} __attribute__((packed));
struct app_storage_area {
uint8_t status;
struct auth_metadata auth;
uint32_t addr_start;
uint32_t size;
} __attribute__((packed));
struct table_header {
uint8_t version;
} __attribute__((packed));
struct partition_table {
struct table_header header;
struct management_app_metadata mgmt_app_data;
struct pre_loaded_app_metadata pre_app_data;
struct app_storage_area app_storage[N_STORAGE_AREA];
} __attribute__((packed));
int part_table_read(struct partition_table *part_table);
int part_table_write(struct partition_table *part_table);
#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,90 @@
// Copyright (C) 2024 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#include "spi.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
// 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.
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)
{
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)
{
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,14 @@
// 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_ready(void);
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

@ -0,0 +1,197 @@
// 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)
{
for (uint8_t i = 0; i < N_STORAGE_AREA; i++) {
if (part_table->app_storage[i].status == 0x00) {
return i;
}
}
return -1;
}
/* 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)
{
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 *part_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;
}
/* Allocate the empty index found */
/* Erase area first */
/* Assumes the area is 64 KiB block aligned */
flash_block_64_erase(part_table->app_storage[index]
.addr_start); // Erase first 64 KB block
flash_block_64_erase(part_table->app_storage[index].addr_start +
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);
part_table_write(part_table);
return 0;
}
/* Dealloacate a previously allocated storage area. Returns zero on success, and
* non-zero on errors. */
int storage_deallocate_area(struct partition_table *part_table)
{
int index = storage_get_area(part_table);
if (index == -1) {
/* No area to deallocate */
return -1;
}
/* Erase area first */
/* Assumes the area is 64 KiB block aligned */
flash_block_64_erase(part_table->app_storage[index]
.addr_start); // Erase first 64 KB block
flash_block_64_erase(part_table->app_storage[index].addr_start +
0x10000); // Erase second 64 KB block
/* Clear partition table lastly */
part_table->app_storage[index].status = 0;
memset(part_table->app_storage[index].auth.nonce, 0x00,
sizeof(part_table->app_storage[index].auth.nonce));
memset(
part_table->app_storage[index].auth.authentication_digest, 0x00,
sizeof(part_table->app_storage[index].auth.authentication_digest));
part_table_write(part_table);
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)
{
int index = storage_get_area(part_table);
if (index == -1) {
/* No allocated area */
return -1;
}
/* Cannot erase less than one sector */
if (size < 4096 || size > part_table->app_storage[index].size ||
size % 4096 != 0) {
return -2;
}
if ((offset) >= part_table->app_storage[index].size) {
return -2;
}
uint32_t address = part_table->app_storage[index].addr_start + 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)
{
int index = storage_get_area(part_table);
if (index == -1) {
/* No allocated area */
return -1;
}
if ((offset + size) > part_table->app_storage[index].size ||
size > 4096) {
/* Writing outside of area */
return -2;
}
uint32_t address = part_table->app_storage[index].addr_start + 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)
{
int index = storage_get_area(part_table);
if (index == -1) {
/* No allocated area */
return -1;
}
if ((offset + size) > part_table->app_storage[index].size) {
/* Reading outside of area */
return -2;
}
uint32_t address = part_table->app_storage[index].addr_start + 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_allocate_area(struct partition_table *part_table);
int storage_deallocate_area(struct partition_table *part_table);
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,8 +5,12 @@
#include <stdint.h>
#include <tkey/assert.h>
#include <tkey/debug.h>
#include <tkey/led.h>
#include "partition_table.h"
#include "storage.h"
#include "../tk1/syscall_num.h"
// clang-format off
@ -14,11 +18,44 @@ static volatile uint32_t *system_reset = (volatile uint32_t *)TK1_MMIO_TK1_SYSTE
static volatile uint32_t *udi = (volatile uint32_t *)TK1_MMIO_TK1_UDI_FIRST;
// clang-format on
int32_t syscall_handler(uint32_t number, uint32_t arg1)
extern struct partition_table part_table;
int32_t syscall_handler(uint32_t number, uint32_t arg1, uint32_t arg2,
uint32_t arg3)
{
switch (number) {
case TK1_SYSCALL_RESET:
*system_reset = 1;
return 0;
case TK1_SYSCALL_ALLOC_AREA:
if (storage_allocate_area(&part_table) < 0) {
debug_puts("couldn't allocate storage area\n");
return -1;
}
return 0;
case TK1_SYSCALL_DEALLOC_AREA:
if (storage_deallocate_area(&part_table) < 0) {
debug_puts("couldn't deallocate storage area\n");
return -1;
}
return 0;
case TK1_SYSCALL_WRITE_DATA:
if (storage_write_data(&part_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, arg1, (uint8_t *)arg2,
arg3) < 0) {
debug_puts("couldn't read storage area\n");
return -1;
}
return 0;
case TK1_SYSCALL_SET_LED:
led_set(arg1);

View File

@ -6,8 +6,13 @@
enum syscall_num {
TK1_SYSCALL_RESET = 1,
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_SET_LED = 10,
TK1_SYSCALL_GET_VIDPID = 12,
};
#endif