Michael Cardell Widerkrantz 6ef6c36f6f
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>
2025-03-18 17:40:10 +01:00

198 lines
5.1 KiB
C

// 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);
}