mirror of
https://github.com/tillitis/tillitis-key1.git
synced 2025-03-26 00:28:15 -04:00
Merge b6e05828e94e6ba0099a75135b67f9a74e947cde into 8d8f4c7fafe84ee198f6794ed4b9aa0a0aecdefe
This commit is contained in:
commit
162acd07b8
@ -126,7 +126,15 @@ 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 \
|
||||
$(P)/fw/tk1/preload_app.o \
|
||||
$(P)/fw/tk1/mgmt_app.o
|
||||
|
||||
FIRMWARE_SOURCES = \
|
||||
$(P)/fw/tk1/main.c \
|
||||
@ -187,7 +195,7 @@ tkey-libs:
|
||||
$(FIRMWARE_OBJS): $(FIRMWARE_DEPS)
|
||||
$(TESTFW_OBJS): $(FIRMWARE_DEPS)
|
||||
|
||||
#firmware.elf: CFLAGS += -DTKEY_DEBUG
|
||||
# firmware.elf: CFLAGS += -DTKEY_DEBUG
|
||||
firmware.elf: tkey-libs $(FIRMWARE_OBJS) $(P)/fw/tk1/firmware.lds
|
||||
$(CC) $(CFLAGS) $(FIRMWARE_OBJS) $(LDFLAGS) -o $@ > $(basename $@).map
|
||||
|
||||
@ -196,6 +204,9 @@ simfirmware.elf: $(FIRMWARE_OBJS) $(P)/fw/tk1/firmware.lds
|
||||
$(CC) $(CFLAGS) $(FIRMWARE_OBJS) $(LDFLAGS) -o $@ > $(basename $@).map
|
||||
|
||||
qemu_firmware.elf: CFLAGS += -DQEMU_DEBUG
|
||||
qemu_firmware.elf: ASFLAGS += -DQEMU_DEBUG
|
||||
qemu_firmware.elf: CFLAGS += -DQEMU_SYSCALL
|
||||
qemu_firmware.elf: ASFLAGS += -DQEMU_SYSCALL
|
||||
qemu_firmware.elf: firmware.elf
|
||||
mv firmware.elf qemu_firmware.elf
|
||||
|
||||
|
@ -102,81 +102,154 @@ values of the Block RAMs used to construct the `FW_ROM`. The `FW_ROM`
|
||||
start address is located at `0x0000_0000` in the CPU memory map, which
|
||||
is also the CPU reset vector.
|
||||
|
||||
### Reset intentions
|
||||
|
||||
We have a number of reset options we call `startfrom`:
|
||||
|
||||
1. Start from flash slot 1 (default): `FLASH1`
|
||||
2. Start from flash slot 2: `FLASH2`.
|
||||
3. Load and start an app from flash slot 1 with a specific app hash:
|
||||
`FLASH1_VER`
|
||||
4. Load and start an app from flash slot 2 with a specific app hash:
|
||||
`FLASH2_VER`.
|
||||
5. Load and start a new app from client: `CLIENT`.
|
||||
6. load and start an app from client with a specific app hash:
|
||||
`CLIENT_VER`.
|
||||
|
||||
### Firmware state machine
|
||||
|
||||
This is the state diagram of the firmware. There are only four states.
|
||||
Change of state occur when we receive specific I/O or a fatal error
|
||||
occurs.
|
||||
This is the state diagram of the firmware. Change of state occur when
|
||||
we receive specific I/O or a fatal error occurs.
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
S1: initial
|
||||
S0: initial
|
||||
S1: waitcommand
|
||||
S2: loading
|
||||
S3: running
|
||||
S3: flash_loading
|
||||
S4: auth_app
|
||||
S5: starting
|
||||
S6: compute_cdi
|
||||
SE: failed
|
||||
|
||||
[*] --> S1
|
||||
[*] --> S0
|
||||
|
||||
S0 --> S1
|
||||
S0 --> S4: Default
|
||||
|
||||
S1 --> S1: Commands
|
||||
S1 --> S2: LOAD_APP
|
||||
S1 --> SE: Error
|
||||
|
||||
S2 --> S2: LOAD_APP_DATA
|
||||
S2 --> S3: Last block received
|
||||
S2 --> S6: Last block received
|
||||
S2 --> SE: Error
|
||||
|
||||
S3 --> [*]
|
||||
S6 --> S3
|
||||
|
||||
S3 --> S5
|
||||
|
||||
S4 --> S5
|
||||
S4 --> SE: Error
|
||||
|
||||
SE --> [*]
|
||||
S5 --> [*]
|
||||
```
|
||||
|
||||
States:
|
||||
|
||||
- `initial` - At start. Allows the commands `NAME_VERSION`, `GET_UDI`,
|
||||
`LOAD_APP`.
|
||||
- `loading` - Expect application data. Allows only the command
|
||||
`LOAD_APP_DATA`.
|
||||
- `run` - Computes CDI and starts the application. Allows no commands.
|
||||
- `fail` - Stops waiting for commands, flashes LED forever. Allows no
|
||||
commands.
|
||||
- `initial`: We start by checking resetinfo data in `FW_RAM` for
|
||||
`startfrom`.
|
||||
- `waitcommand`: Waiting for initial commands from client. Allows the
|
||||
commands `NAME_VERSION`, `GET_UDI`, `LOAD_APP`.
|
||||
- `loading`: Expecting application data from client. Allows only the
|
||||
command `LOAD_APP_DATA`.
|
||||
- `flash_loading`: Loading and authentication app from flash. Computes CDI,
|
||||
creates or checks the authentication of the flash app. Allows no commands.
|
||||
- `starting`: Starts the application. Does not return to firmware.
|
||||
Allows no commands.
|
||||
- `failed` - Halts CPU. Allows no commands.
|
||||
|
||||
Commands in state `initial`:
|
||||
Allowed data in state `resetinfo`:
|
||||
|
||||
| *startfrom* | *next state* |
|
||||
|--------------|-----------------|
|
||||
| `FLASH1` | `flash_loading` |
|
||||
| `FLASH2` | `flash_loading` |
|
||||
| `FLASH1_VER` | `flash_loading` |
|
||||
| `FLASH2_VER` | `flash_loading` |
|
||||
| `CLIENT` | `waitcommand` |
|
||||
| `CLIENT_VER` | `waitcommand` |
|
||||
|
||||
I/O in state `flash_loading`:
|
||||
|
||||
| *I/O* | *next state* |
|
||||
|--------------------|--------------|
|
||||
| Last app data read | `starting` |
|
||||
|
||||
Commands in state `waitcommand`:
|
||||
|
||||
| *command* | *next state* |
|
||||
|-----------------------|--------------|
|
||||
| `FW_CMD_NAME_VERSION` | unchanged |
|
||||
| `FW_CMD_GET_UDI` | unchanged |
|
||||
| `FW_CMD_LOAD_APP` | `loading` |
|
||||
| | |
|
||||
|
||||
Commands in state `loading`:
|
||||
|
||||
| *command* | *next state* |
|
||||
|------------------------|----------------------------------|
|
||||
| `FW_CMD_LOAD_APP_DATA` | unchanged or `run` on last chunk |
|
||||
| *command* | *next state* |
|
||||
|------------------------|---------------------------------------|
|
||||
| `FW_CMD_LOAD_APP_DATA` | unchanged or `starting` on last chunk |
|
||||
|
||||
No other states allows commands.
|
||||
|
||||
See [Firmware protocol in the Dev
|
||||
Handbook](http://dev.tillitis.se/protocol/#firmware-protocol) for the
|
||||
definition of the specific commands and their responses.
|
||||
|
||||
State changes from "initial" to "loading" when receiving `LOAD_APP`,
|
||||
which also sets the size of the number of data blocks to expect. After
|
||||
that we expect several `LOAD_APP_DATA` commands until the last block
|
||||
is received, when state is changed to "running".
|
||||
Plain text explanation of the states:
|
||||
|
||||
In "running", the loaded device app is measured, the Compound Device
|
||||
Identifier (CDI) is computed, we do some cleanup of firmware data
|
||||
structures, enable the system calls, and finally start the app, which
|
||||
ends the firmware state machine. Hardware guarantees that we leave
|
||||
firmware mode automatically when the program counter leaves ROM.
|
||||
- `initial`: Execution starts here. The firmware checks in the
|
||||
`FW_RAM` for `startfrom` for what to do next.
|
||||
|
||||
The device app is now running in application mode. We can, however,
|
||||
return to firmware mode (excepting access to the UDS) by doing system
|
||||
calls. Note that ROM is still readable, but is now hardware protected
|
||||
from execution, except through the system call mechanism.
|
||||
For all `startfrom` values `FLASH_*` the next state is `startflash`.
|
||||
Otherwise it goes to `waitcommand`, indicating that it should wait
|
||||
for further commands from the client.
|
||||
|
||||
- `flash_loading` loads and measure an app from flash, the Compound
|
||||
Device Identifier (CDI) is computed, then the app is authenticated
|
||||
against a stored digest to see that no one has changed the app by
|
||||
manipulating the flash. The compuation is done by:
|
||||
|
||||
digest = blake2s(cdi, nonce from flash)
|
||||
|
||||
and then compared against the stored digest in the app's flash slot.
|
||||
|
||||
- `waitcommand` waits for command from the client. State changes to
|
||||
`loading` when receiving `LOAD_APP`, which also sets the size of the
|
||||
number of data blocks to expect. After that we expect several
|
||||
`LOAD_APP_DATA` commands until the last block is received, when
|
||||
state is changed to `running`.
|
||||
|
||||
- `compute_cdi`: The the Compound Device Identifier (CDI) is computed
|
||||
and we go to `starting`.
|
||||
|
||||
- `starting`: Clean up firmware data structures, enable the system
|
||||
calls, and start the app, which ends the firmware state machine.
|
||||
Hardware guarantees that we leave firmware mode automatically when
|
||||
the program counter leaves ROM.
|
||||
|
||||
After `starting` the device app is now running in application mode. We
|
||||
can, however, return to firmware mode (excepting access to the UDS) by
|
||||
doing system calls. Note that ROM is still readable, but is now
|
||||
hardware protected from execution, except through the system call
|
||||
mechanism.
|
||||
|
||||
### Golden path
|
||||
|
||||
Firmware loads the application at the start of RAM (`0x4000_0000`). It
|
||||
use a part of the special FW\_RAM for its own stack.
|
||||
Firmware loads the application at the start of RAM (`0x4000_0000`)
|
||||
from either flash or the UART. It use a part of the special FW\_RAM
|
||||
for its own stack.
|
||||
|
||||
When reset is released, the CPU starts executing the firmware. It
|
||||
begins in `start.S` by clearing all CPU registers, clears all FW\_RAM,
|
||||
@ -186,54 +259,101 @@ the system calls, but the handler is not yet enabled.
|
||||
|
||||
Beginning at `main()` it fills the entire RAM with pseudo random data
|
||||
and setting up the RAM address and data hardware scrambling with
|
||||
values from the True Random Number Generator (TRNG). It then waits for
|
||||
data coming in through the UART.
|
||||
values from the True Random Number Generator (TRNG).
|
||||
|
||||
Typical expected use scenario:
|
||||
1. Check the special resetinfo area in FW\_RAM to see if there is any
|
||||
data about why a reset has been made. All zeroes(?) meaning default
|
||||
behaviour.
|
||||
|
||||
1. The client sends the `FW_CMD_LOAD_APP` command with the size of
|
||||
the device app and the optional 32 byte hash of the user-supplied
|
||||
secret as arguments and gets a `FW_RSP_LOAD_APP` back. After
|
||||
using this it's not possible to restart the loading of an
|
||||
application.
|
||||
2. If it was reset with intention to start a device app from client,
|
||||
see App loaded from client below.
|
||||
|
||||
2. If the the client receive a sucessful response, it will send
|
||||
multiple `FW_CMD_LOAD_APP_DATA` commands, together containing the
|
||||
full application.
|
||||
3. Default is to start the first device app from flash. If resetinfo
|
||||
says otherwise it starts the other one.
|
||||
|
||||
3. On receiving`FW_CMD_LOAD_APP_DATA` commands the firmware places
|
||||
the data into `0x4000_0000` and upwards. The firmware replies
|
||||
with a `FW_RSP_LOAD_APP_DATA` response to the client for each
|
||||
received block except the last data block.
|
||||
4. Load flash app into RAM without USS.
|
||||
|
||||
4. When the final block of the application image is received with a
|
||||
`FW_CMD_LOAD_APP_DATA`, the firmware measure the application by
|
||||
computing a BLAKE2s digest over the entire application. Then
|
||||
firmware send back the `FW_RSP_LOAD_APP_DATA_READY` response
|
||||
containing the digest.
|
||||
5. Compute digest of loaded app.
|
||||
|
||||
5. The Compound Device Identifier
|
||||
([CDI]((#compound-device-identifier-computation))) is then
|
||||
computed by doing a new BLAKE2s using the Unique Device Secret
|
||||
(UDS), the application digest, and any User Supplied Secret
|
||||
(USS) digest already received.
|
||||
6. Compare against stored app digest in partition table to note if app
|
||||
has been corrupted on flash. If corrupted, halt CPU.
|
||||
|
||||
6. The start address of the device app, currently `0x4000_0000`, is
|
||||
written to `APP_ADDR` and the size of the binary to `APP_SIZE` to
|
||||
let the device application know where it is loaded and how large
|
||||
it is, if it wants to relocate in RAM.
|
||||
7. Proceed to [Start the device app](#start-the-device-app) below.
|
||||
|
||||
7. The firmware now clears the part of the special `FW_RAM` where it
|
||||
keeps it stack.
|
||||
If the app is the first set in a chain, it's the job of the app itself
|
||||
to reset the TKey when it has done its job. For instance, a verified
|
||||
boot loader app:
|
||||
|
||||
8. The interrupt handler for system calls is enabled.
|
||||
- includes a security policy, for instance a public key and code to
|
||||
check a signature.
|
||||
|
||||
9. Firmware starts the application by jumping to the contents of
|
||||
`APP_ADDR`. Hardware automatically switches from firmware mode to
|
||||
application mode. In this mode some memory access is restricted,
|
||||
e.g. some addresses are inaccessible (`UDS`), and some are
|
||||
switched from read/write to read-only (see [the memory
|
||||
map](https://dev.tillitis.se/memory/)).
|
||||
- the app reads the message and the signature over the message (the
|
||||
digest of the next app in the chain) from the filesystem or from
|
||||
the client.
|
||||
|
||||
- if the signature provided over the message is verified to be done
|
||||
by the corresponding private key, this app would do a `reset()`,
|
||||
passing the digest to the firmware for control and instructing it
|
||||
to start *just* that app.
|
||||
|
||||
- firmware would see the instructions about the reset in FW\_RAM:
|
||||
|
||||
1. Where to expect the next app from: client, a slot in the
|
||||
filesystem?
|
||||
2. The expected digest of the next app.
|
||||
|
||||
#### App loaded from client
|
||||
|
||||
Firmware waits for data coming in through the UART.
|
||||
|
||||
1. The client sends the `FW_CMD_LOAD_APP` command with the size of
|
||||
the device app and the optional 32 byte hash of the user-supplied
|
||||
secret as arguments and gets a `FW_RSP_LOAD_APP` back. After
|
||||
using this it's not possible to restart the loading of an
|
||||
application.
|
||||
|
||||
2. If the the client receive a sucessful response, it will send
|
||||
multiple `FW_CMD_LOAD_APP_DATA` commands, together containing the
|
||||
full application.
|
||||
|
||||
3. On receiving`FW_CMD_LOAD_APP_DATA` commands the firmware places
|
||||
the data into `0x4000_0000` and upwards. The firmware replies
|
||||
with a `FW_RSP_LOAD_APP_DATA` response to the client for each
|
||||
received block except the last data block.
|
||||
|
||||
4. When the final block of the application image is received with a
|
||||
`FW_CMD_LOAD_APP_DATA`, the firmware measure the application by
|
||||
computing a BLAKE2s digest over the entire application. Then
|
||||
firmware send back the `FW_RSP_LOAD_APP_DATA_READY` response
|
||||
containing the digest.
|
||||
|
||||
#### Start the device app
|
||||
|
||||
1. If there is an app digest in the resetinfo left from previous app,
|
||||
compare the digests. Halt CPU if differences.
|
||||
|
||||
2. The Compound Device Identifier
|
||||
([CDI]((#compound-device-identifier-computation))) is then computed
|
||||
by doing a new BLAKE2s using the Unique Device Secret (UDS), the
|
||||
application digest, and any User Supplied Secret (USS) digest
|
||||
already received.
|
||||
|
||||
3. The start address of the device app, currently `0x4000_0000`, is
|
||||
written to `APP_ADDR` and the size of the binary to `APP_SIZE` to
|
||||
let the device application know where it is loaded and how large it
|
||||
is, if it wants to relocate in RAM.
|
||||
|
||||
4. The firmware now clears the part of the special `FW_RAM` where it
|
||||
keeps it stack.
|
||||
|
||||
5. The interrupt handler for system calls is enabled.
|
||||
|
||||
6. Firmware starts the application by jumping to the contents of
|
||||
`APP_ADDR`. Hardware automatically switches from firmware mode to
|
||||
application mode. In this mode some memory access is restricted,
|
||||
e.g. some addresses are inaccessible (`UDS`), and some are switched
|
||||
from read/write to read-only (see [the memory
|
||||
map](https://dev.tillitis.se/memory/)).
|
||||
|
||||
If during this whole time any commands are received which are not
|
||||
allowed in the current state, or any errors occur, we enter the
|
||||
|
76
hw/application_fpga/fw/reset_test/Makefile
Normal file
76
hw/application_fpga/fw/reset_test/Makefile
Normal file
@ -0,0 +1,76 @@
|
||||
P := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
|
||||
LIBDIR ?= ../../tkey-libs
|
||||
OBJCOPY ?= llvm-objcopy
|
||||
CC = clang
|
||||
CFLAGS = \
|
||||
-target riscv32-unknown-none-elf \
|
||||
-march=rv32iczmmul \
|
||||
-mabi=ilp32 \
|
||||
-mcmodel=medany \
|
||||
-static \
|
||||
-std=gnu99 \
|
||||
-O2 \
|
||||
-ffast-math \
|
||||
-fno-common \
|
||||
-fno-builtin-printf \
|
||||
-fno-builtin-putchar \
|
||||
-fno-builtin-memcpy \
|
||||
-nostdlib \
|
||||
-mno-relax \
|
||||
-Wall \
|
||||
-Wpedantic \
|
||||
-Wno-language-extension-token \
|
||||
-Werror \
|
||||
-flto \
|
||||
-g \
|
||||
-I $(LIBDIR)/include \
|
||||
-I $(LIBDIR) \
|
||||
-DTKEY_DEBUG
|
||||
|
||||
AS = clang
|
||||
|
||||
ASFLAGS = \
|
||||
-target riscv32-unknown-none-elf \
|
||||
-march=rv32iczmmul \
|
||||
-mabi=ilp32 \
|
||||
-mno-relax
|
||||
|
||||
LDFLAGS = \
|
||||
-T $(P)/app.lds \
|
||||
-L $(LIBDIR) -lcommon
|
||||
|
||||
.PHONY: all
|
||||
all: reset_test.bin
|
||||
|
||||
# Turn elf into bin for device
|
||||
%.bin: %.elf
|
||||
$(OBJCOPY) --input-target=elf32-littleriscv --output-target=binary $^ $@
|
||||
chmod a-x $@
|
||||
|
||||
.PHONY: tkey-libs
|
||||
tkey-libs:
|
||||
make -C $(LIBDIR)
|
||||
|
||||
RESET_TEST_FMTFILES = \
|
||||
$(P)/main.c \
|
||||
|
||||
RESET_TEST_OBJS = \
|
||||
$(P)/main.o \
|
||||
$(P)/crt0.o \
|
||||
$(P)/syscall.o
|
||||
|
||||
reset_test.elf: tkey-libs $(RESET_TEST_OBJS)
|
||||
$(CC) $(CFLAGS) $(RESET_TEST_OBJS) $(LDFLAGS) -o $@
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
clang-format --dry-run --ferror-limit=0 $(RESET_TEST_FMTFILES)
|
||||
clang-format --verbose -i $(RESET_TEST_FMTFILES)
|
||||
|
||||
.PHONY: checkfmt
|
||||
checkfmt:
|
||||
clang-format --dry-run --ferror-limit=0 --Werror $(RESET_TEST_FMTFILES)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f reset_test.bin reset_test.elf $(RESET_TEST_OBJS)
|
64
hw/application_fpga/fw/reset_test/app.lds
Normal file
64
hw/application_fpga/fw/reset_test/app.lds
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Tillitis AB <tillitis.se>
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
OUTPUT_ARCH( "riscv" )
|
||||
ENTRY(_start)
|
||||
|
||||
MEMORY
|
||||
{
|
||||
RAM (rwx) : ORIGIN = 0x40000000, LENGTH = 0x20000 /* 128 KB */
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.text.init :
|
||||
{
|
||||
*(.text.init)
|
||||
} >RAM
|
||||
|
||||
.text :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.text) /* .text sections (code) */
|
||||
*(.text*) /* .text* sections (code) */
|
||||
*(.rodata) /* .rodata sections (constants, strings, etc.) */
|
||||
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
|
||||
*(.srodata) /* .rodata sections (constants, strings, etc.) */
|
||||
*(.srodata*) /* .rodata* sections (constants, strings, etc.) */
|
||||
. = ALIGN(4);
|
||||
_etext = .;
|
||||
_sidata = _etext;
|
||||
} >RAM
|
||||
|
||||
.data : AT (_etext)
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_sdata = .;
|
||||
. = ALIGN(4);
|
||||
*(.data) /* .data sections */
|
||||
*(.data*) /* .data* sections */
|
||||
*(.sdata) /* .sdata sections */
|
||||
*(.sdata*) /* .sdata* sections */
|
||||
. = ALIGN(4);
|
||||
_edata = .;
|
||||
} >RAM
|
||||
|
||||
/* Uninitialized data section */
|
||||
.bss :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_sbss = .;
|
||||
*(.bss)
|
||||
*(.bss*)
|
||||
*(.sbss)
|
||||
*(.sbss*)
|
||||
*(COMMON)
|
||||
|
||||
. = ALIGN(4);
|
||||
_ebss = .;
|
||||
} >RAM
|
||||
|
||||
/* libcrt0/crt0.S inits stack to start just below end of RAM */
|
||||
}
|
53
hw/application_fpga/fw/reset_test/crt0.S
Normal file
53
hw/application_fpga/fw/reset_test/crt0.S
Normal file
@ -0,0 +1,53 @@
|
||||
// SPDX-FileCopyrightText: 2022 Tillitis AB <tillitis.se>
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
.section ".text.init"
|
||||
.global _start
|
||||
_start:
|
||||
li x1, 0
|
||||
li x2, 0
|
||||
li x3, 0
|
||||
li x4, 0
|
||||
li x5, 0
|
||||
li x6, 0
|
||||
li x7, 0
|
||||
li x8, 0
|
||||
li x9, 0
|
||||
li x10,0
|
||||
li x11,0
|
||||
li x12,0
|
||||
li x13,0
|
||||
li x14,0
|
||||
li x15,0
|
||||
li x16,0
|
||||
li x17,0
|
||||
li x18,0
|
||||
li x19,0
|
||||
li x20,0
|
||||
li x21,0
|
||||
li x22,0
|
||||
li x23,0
|
||||
li x24,0
|
||||
li x25,0
|
||||
li x26,0
|
||||
li x27,0
|
||||
li x28,0
|
||||
li x29,0
|
||||
li x30,0
|
||||
li x31,0
|
||||
|
||||
/* init stack below 0x40020000 (TK1_RAM_BASE+TK1_RAM_SIZE) */
|
||||
li sp, 0x4001fff0
|
||||
|
||||
/* zero-init bss section */
|
||||
la a0, _sbss
|
||||
la a1, _ebss
|
||||
bge a0, a1, end_init_bss
|
||||
|
||||
loop_init_bss:
|
||||
sw zero, 0(a0)
|
||||
addi a0, a0, 4
|
||||
blt a0, a1, loop_init_bss
|
||||
|
||||
end_init_bss:
|
||||
call main
|
134
hw/application_fpga/fw/reset_test/main.c
Normal file
134
hw/application_fpga/fw/reset_test/main.c
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (C) 2022, 2023 - Tillitis AB
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <tkey/assert.h>
|
||||
#include <tkey/debug.h>
|
||||
#include <tkey/io.h>
|
||||
#include <tkey/led.h>
|
||||
#include <tkey/lib.h>
|
||||
#include <tkey/tk1_mem.h>
|
||||
|
||||
#include "../testapp/syscall.h"
|
||||
#include "../tk1/proto.h"
|
||||
#include "../tk1/resetinfo.h"
|
||||
#include "../tk1/syscall_num.h"
|
||||
|
||||
// Converts a single hex character to its integer value
|
||||
static uint8_t hex_char_to_byte(uint8_t c)
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
if (c >= 'A' && c <= 'F')
|
||||
return c - 'A' + 10;
|
||||
if (c >= 'a' && c <= 'f')
|
||||
return c - 'a' + 10;
|
||||
return 0; // Invalid character, should not happen if input is valid
|
||||
}
|
||||
|
||||
// Converts a 64-char hex string into a 32-byte array
|
||||
int hex_string_to_bytes(uint8_t *hex_str, uint8_t *out_bytes, size_t out_len)
|
||||
{
|
||||
if (!hex_str || !out_bytes || out_len < 32)
|
||||
return -1; // Error handling
|
||||
|
||||
for (size_t i = 0; i < 32; i++) {
|
||||
out_bytes[i] = (hex_char_to_byte(hex_str[i * 2]) << 4) |
|
||||
hex_char_to_byte(hex_str[i * 2 + 1]);
|
||||
}
|
||||
|
||||
return 0; // Success
|
||||
}
|
||||
//---------------------------------------
|
||||
|
||||
#define BUFSIZE 32
|
||||
|
||||
int main(void)
|
||||
{
|
||||
uint8_t available = 0;
|
||||
uint8_t cmdbuf[BUFSIZE] = {0};
|
||||
enum ioend endpoint = IO_NONE;
|
||||
led_set(LED_BLUE);
|
||||
struct reset rst = {0};
|
||||
|
||||
while (1) {
|
||||
|
||||
debug_puts("reset_test: Waiting for command\n");
|
||||
|
||||
memset(cmdbuf, 0, BUFSIZE);
|
||||
|
||||
// Wait for data
|
||||
if (readselect(IO_TKEYCTRL, &endpoint, &available) < 0) {
|
||||
assert(1 == 2);
|
||||
}
|
||||
|
||||
if (read(IO_TKEYCTRL, cmdbuf, BUFSIZE, available) < 0) {
|
||||
// read failed! I/O broken? Just redblink.
|
||||
assert(1 == 2);
|
||||
}
|
||||
|
||||
led_set(LED_BLUE | LED_RED);
|
||||
|
||||
switch (cmdbuf[0]) {
|
||||
case '1':
|
||||
rst.type = START_DEFAULT;
|
||||
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
|
||||
break;
|
||||
|
||||
case '2':
|
||||
rst.type = START_CLIENT;
|
||||
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
|
||||
break;
|
||||
|
||||
case '3':
|
||||
rst.type = START_FLASH1;
|
||||
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
|
||||
break;
|
||||
|
||||
case '4': {
|
||||
uint8_t string[] = "0123456789abcdef0123456789abcdef012"
|
||||
"3456789abcdef0123456789abcdef";
|
||||
rst.type = START_CLIENT_VER;
|
||||
hex_string_to_bytes(string, (uint8_t *)&rst.app_digest,
|
||||
sizeof(rst.app_digest));
|
||||
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
|
||||
} break;
|
||||
|
||||
case '5': {
|
||||
uint8_t tkeylibs_example_app_digest[] =
|
||||
"96bb4c90603dbbbe09b9a1d7259b5e9e61bedd89a897105c30"
|
||||
"c9d4bf66a98d97";
|
||||
rst.type = START_CLIENT_VER;
|
||||
hex_string_to_bytes(tkeylibs_example_app_digest,
|
||||
(uint8_t *)&rst.app_digest,
|
||||
sizeof(rst.app_digest));
|
||||
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
|
||||
} break;
|
||||
|
||||
case '6': {
|
||||
uint8_t string[] = "0123456789abcdef0123456789abcdef012"
|
||||
"3456789abcdef0123456789abcdef";
|
||||
rst.type = START_FLASH2_VER;
|
||||
hex_string_to_bytes(string, (uint8_t *)&rst.app_digest,
|
||||
sizeof(rst.app_digest));
|
||||
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
|
||||
} break;
|
||||
|
||||
case '7': {
|
||||
uint8_t tkeylibs_example_app_digest[] =
|
||||
"a97f6ec2112067c4b5b5860521e252a095d221652f7b3d056b"
|
||||
"d98eaba40b4967";
|
||||
rst.type = START_FLASH2_VER;
|
||||
hex_string_to_bytes(tkeylibs_example_app_digest,
|
||||
(uint8_t *)&rst.app_digest,
|
||||
sizeof(rst.app_digest));
|
||||
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
|
||||
} break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
85
hw/application_fpga/fw/reset_test/syscall.S
Normal file
85
hw/application_fpga/fw/reset_test/syscall.S
Normal file
@ -0,0 +1,85 @@
|
||||
// SPDX-FileCopyrightText: 2024 Tillitis AB <tillitis.se>
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
#include "../tk1/picorv32/custom_ops.S"
|
||||
|
||||
.section ".text"
|
||||
.globl syscall
|
||||
|
||||
|
||||
syscall:
|
||||
// Save registers to stack
|
||||
addi sp, sp, -32*4
|
||||
sw x0, 0*4(sp)
|
||||
sw x1, 1*4(sp)
|
||||
// x2 (sp) is assumed to be preserved by the interrupt handler.
|
||||
sw x3, 3*4(sp)
|
||||
sw x4, 4*4(sp)
|
||||
sw x5, 5*4(sp)
|
||||
sw x6, 6*4(sp)
|
||||
sw x7, 7*4(sp)
|
||||
sw x8, 8*4(sp)
|
||||
sw x9, 9*4(sp)
|
||||
// x10 (a0) will contain syscall return value. And should not be saved.
|
||||
sw x11, 11*4(sp)
|
||||
sw x12, 12*4(sp)
|
||||
sw x13, 13*4(sp)
|
||||
sw x14, 14*4(sp)
|
||||
sw x15, 15*4(sp)
|
||||
sw x16, 16*4(sp)
|
||||
sw x17, 17*4(sp)
|
||||
sw x18, 18*4(sp)
|
||||
sw x19, 19*4(sp)
|
||||
sw x20, 20*4(sp)
|
||||
sw x21, 21*4(sp)
|
||||
sw x22, 22*4(sp)
|
||||
sw x23, 23*4(sp)
|
||||
sw x24, 24*4(sp)
|
||||
sw x25, 25*4(sp)
|
||||
sw x26, 26*4(sp)
|
||||
sw x27, 27*4(sp)
|
||||
sw x28, 28*4(sp)
|
||||
sw x29, 29*4(sp)
|
||||
sw x30, 30*4(sp)
|
||||
sw x31, 31*4(sp)
|
||||
|
||||
// Trigger syscall interrupt
|
||||
li t1, 0xe1000000 // Syscall interrupt trigger address
|
||||
sw zero, 0(t1) // Trigger interrupt
|
||||
|
||||
// Restore registers from stack
|
||||
lw x0, 0*4(sp)
|
||||
lw x1, 1*4(sp)
|
||||
// x2 (sp) is assumed to be preserved by the interrupt handler.
|
||||
lw x3, 3*4(sp)
|
||||
lw x4, 4*4(sp)
|
||||
lw x5, 5*4(sp)
|
||||
lw x6, 6*4(sp)
|
||||
lw x7, 7*4(sp)
|
||||
lw x8, 8*4(sp)
|
||||
lw x9, 9*4(sp)
|
||||
// x10 (a0) contains syscall return value. And should not be destroyed.
|
||||
lw x11, 11*4(sp)
|
||||
lw x12, 12*4(sp)
|
||||
lw x13, 13*4(sp)
|
||||
lw x14, 14*4(sp)
|
||||
lw x15, 15*4(sp)
|
||||
lw x16, 16*4(sp)
|
||||
lw x17, 17*4(sp)
|
||||
lw x18, 18*4(sp)
|
||||
lw x19, 19*4(sp)
|
||||
lw x20, 20*4(sp)
|
||||
lw x21, 21*4(sp)
|
||||
lw x22, 22*4(sp)
|
||||
lw x23, 23*4(sp)
|
||||
lw x24, 24*4(sp)
|
||||
lw x25, 25*4(sp)
|
||||
lw x26, 26*4(sp)
|
||||
lw x27, 27*4(sp)
|
||||
lw x28, 28*4(sp)
|
||||
lw x29, 29*4(sp)
|
||||
lw x30, 30*4(sp)
|
||||
lw x31, 31*4(sp)
|
||||
addi sp, sp, 32*4
|
||||
|
||||
ret
|
@ -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);
|
||||
|
@ -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
|
||||
|
74
hw/application_fpga/fw/testloadapp/Makefile
Normal file
74
hw/application_fpga/fw/testloadapp/Makefile
Normal file
@ -0,0 +1,74 @@
|
||||
P := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
|
||||
LIBDIR ?= ../../tkey-libs
|
||||
OBJCOPY ?= llvm-objcopy
|
||||
CC = clang
|
||||
CFLAGS = \
|
||||
-target riscv32-unknown-none-elf \
|
||||
-march=rv32iczmmul \
|
||||
-mabi=ilp32 \
|
||||
-mcmodel=medany \
|
||||
-static \
|
||||
-std=gnu99 \
|
||||
-Os \
|
||||
-ffast-math \
|
||||
-fno-common \
|
||||
-fno-builtin-printf \
|
||||
-fno-builtin-putchar \
|
||||
-fno-builtin-memcpy \
|
||||
-nostdlib \
|
||||
-mno-relax \
|
||||
-Wall \
|
||||
-Wpedantic \
|
||||
-Wno-language-extension-token \
|
||||
-Werror \
|
||||
-flto \
|
||||
-g \
|
||||
-I $(LIBDIR)/include \
|
||||
-I $(LIBDIR)
|
||||
|
||||
AS = clang
|
||||
|
||||
ASFLAGS = \
|
||||
-target riscv32-unknown-none-elf \
|
||||
-march=rv32iczmmul \
|
||||
-mabi=ilp32 \
|
||||
-mno-relax
|
||||
|
||||
LDFLAGS = \
|
||||
-T $(LIBDIR)/app.lds \
|
||||
-L $(LIBDIR) -lcrt0 -lcommon -lmonocypher -lblake2s
|
||||
|
||||
.PHONY: all
|
||||
all: testloadapp.bin
|
||||
|
||||
# Turn elf into bin for device
|
||||
%.bin: %.elf
|
||||
$(OBJCOPY) --input-target=elf32-littleriscv --output-target=binary $^ $@
|
||||
chmod a-x $@
|
||||
|
||||
.PHONY: tkey-libs
|
||||
tkey-libs:
|
||||
make -C $(LIBDIR)
|
||||
|
||||
TESTLOADAPP_FMTFILES = \
|
||||
$(P)/main.c
|
||||
|
||||
TESTLOADAPP_OBJS = \
|
||||
$(P)/main.o \
|
||||
../testapp/syscall.o \
|
||||
|
||||
testloadapp.elf: tkey-libs $(TESTLOADAPP_OBJS)
|
||||
$(CC) $(CFLAGS) $(TESTLOADAPP_OBJS) $(LDFLAGS) -o $@
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
clang-format --dry-run --ferror-limit=0 $(TESTLOADAPP_FMTFILES)
|
||||
clang-format --verbose -i $(TESTLOADAPP_FMTFILES)
|
||||
|
||||
.PHONY: checkfmt
|
||||
checkfmt:
|
||||
clang-format --dry-run --ferror-limit=0 --Werror $(TESTLOADAPP_FMTFILES)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f testloadapp.bin testloadapp.elf $(TESTLOADAPP_OBJS)
|
24
hw/application_fpga/fw/testloadapp/blink.h
Normal file
24
hw/application_fpga/fw/testloadapp/blink.h
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef BLINK_APP_H
|
||||
#define BLINK_APP_H
|
||||
|
||||
uint8_t blink[] = {
|
||||
0x81, 0x40, 0x01, 0x41, 0x81, 0x41, 0x01, 0x42, 0x81, 0x42, 0x01, 0x43, 0x81, 0x43, 0x01, 0x44,
|
||||
0x81, 0x44, 0x01, 0x45, 0x81, 0x45, 0x01, 0x46, 0x81, 0x46, 0x01, 0x47, 0x81, 0x47, 0x01, 0x48,
|
||||
0x81, 0x48, 0x01, 0x49, 0x81, 0x49, 0x01, 0x4a, 0x81, 0x4a, 0x01, 0x4b, 0x81, 0x4b, 0x01, 0x4c,
|
||||
0x81, 0x4c, 0x01, 0x4d, 0x81, 0x4d, 0x01, 0x4e, 0x81, 0x4e, 0x01, 0x4f, 0x81, 0x4f, 0x37, 0x01,
|
||||
0x02, 0x40, 0x41, 0x11, 0x17, 0x05, 0x00, 0x00, 0x13, 0x05, 0x45, 0x0c, 0x97, 0x05, 0x00, 0x00,
|
||||
0x93, 0x85, 0xc5, 0x0b, 0x63, 0x57, 0xb5, 0x00, 0x23, 0x20, 0x05, 0x00, 0x11, 0x05, 0xe3, 0x4d,
|
||||
0xb5, 0xfe, 0x97, 0x00, 0x00, 0x00, 0xe7, 0x80, 0xa0, 0x00, 0x00, 0x00, 0x41, 0x11, 0x37, 0x05,
|
||||
0x00, 0xff, 0x11, 0x48, 0xe1, 0x66, 0x13, 0x86, 0xf6, 0x69, 0x93, 0x86, 0x06, 0x6a, 0x09, 0x47,
|
||||
0x85, 0x47, 0x23, 0x22, 0x05, 0x03, 0x02, 0xc2, 0x92, 0x45, 0x63, 0x68, 0xb6, 0x00, 0x92, 0x45,
|
||||
0x85, 0x05, 0x2e, 0xc2, 0x92, 0x45, 0xe3, 0xec, 0xd5, 0xfe, 0x58, 0xd1, 0x02, 0xc4, 0xa2, 0x45,
|
||||
0x63, 0x68, 0xb6, 0x00, 0xa2, 0x45, 0x85, 0x05, 0x2e, 0xc4, 0xa2, 0x45, 0xe3, 0xec, 0xd5, 0xfe,
|
||||
0x5c, 0xd1, 0x02, 0xc6, 0xb2, 0x45, 0xe3, 0x66, 0xb6, 0xfc, 0xb2, 0x45, 0x85, 0x05, 0x2e, 0xc6,
|
||||
0xb2, 0x45, 0xe3, 0xec, 0xd5, 0xfe, 0x75, 0xbf, 0x19, 0xca, 0x2a, 0x96, 0xaa, 0x86, 0x03, 0xc7,
|
||||
0x05, 0x00, 0x23, 0x80, 0xe6, 0x00, 0x85, 0x06, 0x85, 0x05, 0xe3, 0x9a, 0xc6, 0xfe, 0x82, 0x80,
|
||||
0x11, 0xca, 0x0a, 0x06, 0x2a, 0x96, 0xaa, 0x86, 0x98, 0x41, 0x98, 0xc2, 0x91, 0x06, 0x91, 0x05,
|
||||
0xe3, 0x9c, 0xc6, 0xfe, 0x82, 0x80, 0x01, 0xca, 0x2a, 0x96, 0xaa, 0x86, 0x23, 0x80, 0xb6, 0x00,
|
||||
0x85, 0x06, 0xe3, 0x9d, 0xc6, 0xfe, 0x82, 0x80
|
||||
};
|
||||
|
||||
#endif
|
187
hw/application_fpga/fw/testloadapp/main.c
Normal file
187
hw/application_fpga/fw/testloadapp/main.c
Normal file
@ -0,0 +1,187 @@
|
||||
#include <blake2s/blake2s.h>
|
||||
#include <monocypher/monocypher-ed25519.h>
|
||||
#include <stdint.h>
|
||||
#include <tkey/lib.h>
|
||||
#include <tkey/tk1_mem.h>
|
||||
#include <tkey/debug.h>
|
||||
|
||||
#include "../testapp/syscall.h"
|
||||
#include "../tk1/resetinfo.h"
|
||||
#include "../tk1/syscall_num.h"
|
||||
#include "blink.h"
|
||||
#include "tkey/assert.h"
|
||||
|
||||
// clang-format off
|
||||
static volatile uint32_t *cdi = (volatile uint32_t *) TK1_MMIO_TK1_CDI_FIRST;
|
||||
// clang-format on
|
||||
|
||||
int install_app(uint8_t secret_key[64])
|
||||
{
|
||||
uint8_t app_digest[32];
|
||||
uint8_t app_signature[64];
|
||||
size_t app_size = sizeof(blink);
|
||||
|
||||
if (syscall(TK1_SYSCALL_REG_MGMT, 0, 0, 0) < 0) {
|
||||
puts(IO_CDC, "couldn't register as mgmt\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (syscall(TK1_SYSCALL_PRELOAD_DELETE, 0, 0, 0) < 0) {
|
||||
puts(IO_CDC, "couldn't delete preloaded app\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int err = syscall(TK1_SYSCALL_PRELOAD_STORE, 0, (uint32_t)blink,
|
||||
sizeof(blink));
|
||||
|
||||
if (err < 0) {
|
||||
puts(IO_CDC, "couldn't store app, error: ");
|
||||
putinthex(IO_CDC, err);
|
||||
puts(IO_CDC, "\r\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
puts(IO_CDC, "blink: ");
|
||||
putinthex(IO_CDC, (uint32_t)blink);
|
||||
puts(IO_CDC, "\r\n");
|
||||
|
||||
puts(IO_CDC, "blink[0]: ");
|
||||
putinthex(IO_CDC, blink[0]);
|
||||
puts(IO_CDC, "\r\n");
|
||||
|
||||
puts(IO_CDC, "sizeof(blink): ");
|
||||
putinthex(IO_CDC, sizeof(blink));
|
||||
puts(IO_CDC, "\r\n");
|
||||
|
||||
if (blake2s(app_digest, 32, NULL, 0, blink, sizeof(blink)) != 0) {
|
||||
puts(IO_CDC, "couldn't compute digest\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
crypto_ed25519_sign(app_signature, secret_key, app_digest,
|
||||
sizeof(app_digest));
|
||||
|
||||
puts(IO_CDC, "app_digest:\r\n");
|
||||
hexdump(IO_CDC, app_digest, sizeof(app_digest));
|
||||
puts(IO_CDC, "\r\n");
|
||||
|
||||
puts(IO_CDC, "app_signature:\r\n");
|
||||
hexdump(IO_CDC, app_signature, sizeof(app_signature));
|
||||
puts(IO_CDC, "\r\n");
|
||||
|
||||
puts(IO_CDC, "secret_key:\r\n");
|
||||
hexdump(IO_CDC, secret_key, 64);
|
||||
puts(IO_CDC, "\r\n");
|
||||
|
||||
if (syscall(TK1_SYSCALL_PRELOAD_STORE_FIN, app_size,
|
||||
(uint32_t)app_digest, (uint32_t)app_signature) < 0) {
|
||||
puts(IO_CDC, "couldn't finalize storing app\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int verify(uint8_t pubkey[32])
|
||||
{
|
||||
uint8_t app_digest[32];
|
||||
uint8_t app_signature[64];
|
||||
|
||||
// pubkey we already have
|
||||
// read signature
|
||||
// read digest
|
||||
syscall(TK1_SYSCALL_PRELOAD_GET_DIGSIG, (uint32_t)app_digest,
|
||||
(uint32_t)app_signature, 0);
|
||||
|
||||
puts(IO_CDC, "app_digest:\r\n");
|
||||
hexdump(IO_CDC, app_digest, sizeof(app_digest));
|
||||
puts(IO_CDC, "\r\n");
|
||||
|
||||
puts(IO_CDC, "app_signature:\r\n");
|
||||
hexdump(IO_CDC, app_signature, sizeof(app_signature));
|
||||
puts(IO_CDC, "\r\n");
|
||||
|
||||
puts(IO_CDC, "pubkey:\r\n");
|
||||
hexdump(IO_CDC, pubkey, 32);
|
||||
puts(IO_CDC, "\r\n");
|
||||
|
||||
puts(IO_CDC, "Checking signature...\r\n");
|
||||
|
||||
if (crypto_ed25519_check(app_signature, pubkey, app_digest,
|
||||
sizeof(app_digest)) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
puts(IO_CDC, "Resetting into pre loaded app (slot 2)...\r\n");
|
||||
|
||||
// syscall reset flash2_ver with app_digest
|
||||
struct reset rst;
|
||||
rst.type = START_FLASH2_VER;
|
||||
memcpy_s(rst.app_digest, sizeof(rst.app_digest), app_digest,
|
||||
sizeof(app_digest));
|
||||
memset(rst.next_app_data, 0, sizeof(rst.next_app_data));
|
||||
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
|
||||
|
||||
return -2;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
uint8_t secret_key[64];
|
||||
uint8_t pubkey[32];
|
||||
enum ioend endpoint;
|
||||
uint8_t available;
|
||||
uint8_t in = 0;
|
||||
|
||||
// Generate a key pair from CDI
|
||||
crypto_ed25519_key_pair(secret_key, pubkey, (uint8_t *)cdi);
|
||||
|
||||
if (readselect(IO_CDC, &endpoint, &available) < 0) {
|
||||
// readselect failed! I/O broken? Just redblink.
|
||||
assert(1 == 2);
|
||||
}
|
||||
|
||||
if (read(IO_CDC, &in, 1, 1) < 0) {
|
||||
// read failed! I/O broken? Just redblink.
|
||||
assert(1 == 2);
|
||||
}
|
||||
|
||||
puts(IO_CDC, "Hello from testloadapp! 0 = install app in slot 1, 1 = "
|
||||
"verify app\r\n");
|
||||
|
||||
for (;;) {
|
||||
if (readselect(IO_CDC, &endpoint, &available) < 0) {
|
||||
// readselect failed! I/O broken? Just redblink.
|
||||
assert(1 == 2);
|
||||
}
|
||||
|
||||
if (read(IO_CDC, &in, 1, 1) < 0) {
|
||||
// read failed! I/O broken? Just redblink.
|
||||
assert(1 == 2);
|
||||
}
|
||||
|
||||
switch (in) {
|
||||
case '0':
|
||||
if (install_app(secret_key) < 0) {
|
||||
puts(IO_CDC, "Failed to install app\r\n");
|
||||
} else {
|
||||
puts(IO_CDC, "Installed app!\r\n");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case '1':
|
||||
if (verify(pubkey) < 0) {
|
||||
puts(IO_CDC, "Failed to verify app\r\n");
|
||||
} else {
|
||||
puts(IO_CDC, "Verified app!\r\n");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
68
hw/application_fpga/fw/tk1/auth_app.c
Normal file
68
hw/application_fpga/fw/tk1/auth_app.c
Normal 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;
|
||||
}
|
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
|
||||
{
|
||||
|
217
hw/application_fpga/fw/tk1/flash.c
Normal file
217
hw/application_fpga/fw/tk1/flash.c
Normal 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;
|
||||
}
|
58
hw/application_fpga/fw/tk1/flash.h
Normal file
58
hw/application_fpga/fw/tk1/flash.h
Normal 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
|
@ -10,11 +10,16 @@
|
||||
#include <tkey/debug.h>
|
||||
#include <tkey/lib.h>
|
||||
#include <tkey/tk1_mem.h>
|
||||
#include <tkey/led.h>
|
||||
|
||||
#include "auth_app.h"
|
||||
#include "blake2s/blake2s.h"
|
||||
#include "partition_table.h"
|
||||
#include "preload_app.h"
|
||||
#include "proto.h"
|
||||
#include "state.h"
|
||||
#include "syscall_enable.h"
|
||||
#include "resetinfo.h"
|
||||
|
||||
// clang-format off
|
||||
static volatile uint32_t *uds = (volatile uint32_t *)TK1_MMIO_UDS_FIRST;
|
||||
@ -33,8 +38,24 @@ 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 part_table;
|
||||
|
||||
// Locked down what app can start from first flash slot to be exactly
|
||||
// this size, producing this digest.
|
||||
//
|
||||
// To update this, compute the BLAKE2s digest of the app.bin and
|
||||
// insert the size in bytes.
|
||||
#define APP_SIZE_SLOT0 21684
|
||||
// BLAKE2s digest of testloadapp.bin
|
||||
const uint8_t allowed_app_digest[32] = {
|
||||
0x3a, 0x34, 0x6f, 0x1f, 0xb7, 0x7f, 0xa6, 0x71, 0x9b, 0x69, 0x8,
|
||||
0x36, 0xa0, 0x5, 0xe, 0x26, 0x48, 0x8d, 0xab, 0x6a, 0x51, 0xa6,
|
||||
0xe1, 0x18, 0x53, 0xa3, 0x64, 0xc6, 0x5b, 0x42, 0x49, 0xb7,
|
||||
};
|
||||
|
||||
// Context for the loading of a TKey program
|
||||
struct context {
|
||||
uint32_t left; // Bytes left to receive
|
||||
@ -42,6 +63,8 @@ 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
|
||||
volatile uint8_t *ver_digest; // Verify loaded app against this digest
|
||||
};
|
||||
|
||||
static void print_hw_version(void);
|
||||
@ -56,11 +79,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)
|
||||
{
|
||||
@ -300,7 +326,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_CDI;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -320,13 +346,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);
|
||||
@ -365,6 +389,51 @@ 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;
|
||||
}
|
||||
|
||||
static enum state auth_flash_app(const struct context *ctx, struct partition_table *part_table)
|
||||
{
|
||||
if (ctx->flash_slot >= N_PRELOADED_APP) {
|
||||
return FW_STATE_FAIL;
|
||||
}
|
||||
|
||||
if (part_table->pre_app_data[ctx->flash_slot].status == PRE_LOADED_STATUS_PRESENT) {
|
||||
debug_puts("Create auth\n");
|
||||
auth_app_create(&part_table->pre_app_data[ctx->flash_slot].auth);
|
||||
part_table->pre_app_data[ctx->flash_slot].status = PRE_LOADED_STATUS_AUTH;
|
||||
part_table_write(part_table);
|
||||
}
|
||||
|
||||
if (!auth_app_authenticate(&part_table->pre_app_data[ctx->flash_slot].auth)) {
|
||||
debug_puts("!Authenticated\n");
|
||||
|
||||
return FW_STATE_FAIL;
|
||||
}
|
||||
|
||||
return FW_STATE_START;
|
||||
}
|
||||
|
||||
#if !defined(SIMULATION)
|
||||
static uint32_t xorwow(uint32_t state, uint32_t acc)
|
||||
{
|
||||
@ -399,6 +468,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)
|
||||
{
|
||||
blake2s_ctx b2s_ctx = {0};
|
||||
|
||||
return blake2s(digest, 32, NULL, 0, (const void *)TK1_RAM_BASE,
|
||||
*app_size, &b2s_ctx);
|
||||
}
|
||||
|
||||
static enum state start_where(struct context *ctx)
|
||||
{
|
||||
// Where do we start? Read resetinfo 'startfrom'
|
||||
switch (resetinfo->type) {
|
||||
case START_DEFAULT:
|
||||
// fallthrough
|
||||
case START_FLASH1:
|
||||
ctx->flash_slot = 0;
|
||||
ctx->ver_digest = NULL;
|
||||
|
||||
return FW_STATE_LOAD_FLASH;
|
||||
|
||||
case START_FLASH2:
|
||||
ctx->flash_slot = 1;
|
||||
ctx->ver_digest = NULL;
|
||||
|
||||
return FW_STATE_LOAD_FLASH;
|
||||
|
||||
case START_FLASH1_VER:
|
||||
ctx->flash_slot = 0;
|
||||
ctx->ver_digest = resetinfo->app_digest;
|
||||
|
||||
return FW_STATE_LOAD_FLASH;
|
||||
|
||||
case START_FLASH2_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};
|
||||
@ -406,6 +531,8 @@ int main(void)
|
||||
uint8_t cmd[CMDSIZE] = {0};
|
||||
enum state state = FW_STATE_INITIAL;
|
||||
|
||||
led_set(LED_BLUE);
|
||||
|
||||
print_hw_version();
|
||||
|
||||
/*@-mustfreeonly@*/
|
||||
@ -418,13 +545,50 @@ int main(void)
|
||||
|
||||
scramble_ram();
|
||||
|
||||
// TODO Remove
|
||||
// Wait for terminal program and a character to be typed
|
||||
/*
|
||||
enum ioend endpoint = IO_NONE;
|
||||
uint8_t available = 0;
|
||||
uint8_t in = 0;
|
||||
|
||||
if (readselect(IO_CDC, &endpoint, &available) < 0) {
|
||||
// readselect failed! I/O broken? Just redblink.
|
||||
assert(1 == 2);
|
||||
}
|
||||
|
||||
if (read(IO_CDC, &in, 1, 1) < 0) {
|
||||
// read failed! I/O broken? Just redblink.
|
||||
assert(1 == 2);
|
||||
}
|
||||
*/
|
||||
|
||||
// TODO end of remove block
|
||||
|
||||
if (part_table_read(&part_table) != 0) {
|
||||
// Couldn't read or create partition table
|
||||
assert(1 != 2);
|
||||
}
|
||||
|
||||
#if defined(SIMULATION)
|
||||
run(&ctx);
|
||||
#endif
|
||||
|
||||
// Hardocde size of slot 0
|
||||
part_table.pre_app_data[0].size = APP_SIZE_SLOT0;
|
||||
// part_table.pre_app_data[1].size = 0x20000;
|
||||
|
||||
// TODO Just start something from flash without looking in
|
||||
// FW_RAM.
|
||||
//state = FW_STATE_LOAD_FLASH;
|
||||
|
||||
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;
|
||||
@ -445,9 +609,52 @@ 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_CDI:
|
||||
// CDI = hash(uds, hash(app), uss)
|
||||
compute_cdi(ctx.digest, ctx.use_uss, ctx.uss);
|
||||
|
||||
state = FW_STATE_START;
|
||||
break;
|
||||
|
||||
case FW_STATE_LOAD_FLASH:
|
||||
// TODO Just lie and say that an app is present but not yet
|
||||
// authenticated.
|
||||
part_table.pre_app_data[ctx.flash_slot].status = PRE_LOADED_STATUS_PRESENT;
|
||||
|
||||
if (load_flash_app(&part_table, ctx.digest, ctx.flash_slot) < 0) {
|
||||
debug_puts("Couldn't load app from flash\n");
|
||||
state = FW_STATE_FAIL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ctx.flash_slot == 0) {
|
||||
print_digest(allowed_app_digest);
|
||||
if (!memeq(ctx.digest, allowed_app_digest, 32)) {
|
||||
puts(IO_CDC, "app not allowed!\r\n");
|
||||
assert(1 == 2);
|
||||
}
|
||||
}
|
||||
|
||||
// CDI = hash(uds, hash(app), uss)
|
||||
compute_cdi(ctx.digest, ctx.use_uss, ctx.uss);
|
||||
|
||||
state = auth_flash_app(&ctx, &part_table);
|
||||
break;
|
||||
|
||||
case FW_STATE_START:
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
memset((void*)resetinfo->app_digest, 0, sizeof(resetinfo->app_digest));
|
||||
|
||||
jump_to_app();
|
||||
break; // Not reached
|
||||
|
||||
case FW_STATE_FAIL:
|
||||
// fallthrough
|
||||
@ -462,5 +669,6 @@ int main(void)
|
||||
|
||||
/*@ -compdestroy @*/
|
||||
/* We don't care about memory leaks here. */
|
||||
|
||||
return (int)0xcafebabe;
|
||||
}
|
||||
|
75
hw/application_fpga/fw/tk1/mgmt_app.c
Normal file
75
hw/application_fpga/fw/tk1/mgmt_app.c
Normal file
@ -0,0 +1,75 @@
|
||||
// Copyright (C) 2024 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <tkey/lib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "mgmt_app.h"
|
||||
#include "auth_app.h"
|
||||
#include "partition_table.h"
|
||||
|
||||
/* Returns true if an management app is already registered */
|
||||
static bool mgmt_app_registered(struct management_app_metadata *mgmt_table)
|
||||
{
|
||||
|
||||
if (mgmt_table->status == 0x00) {
|
||||
/* No management app registered */
|
||||
return false;
|
||||
// TODO: Should we also check nonce, authentication digest for
|
||||
// non-zero?
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Authenticate an management app */
|
||||
bool mgmt_app_authenticate(struct management_app_metadata *mgmt_table)
|
||||
{
|
||||
if (!mgmt_app_registered(mgmt_table)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return auth_app_authenticate(&mgmt_table->auth);
|
||||
}
|
||||
|
||||
/* Register an management app, returns zero on success */
|
||||
int mgmt_app_register(struct partition_table *part_table)
|
||||
{
|
||||
/* Check if the current app is the mgmt app */
|
||||
if (mgmt_app_authenticate(&part_table->mgmt_app_data)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check if another management app is registered */
|
||||
if (mgmt_app_registered(&part_table->mgmt_app_data)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auth_app_create(&part_table->mgmt_app_data.auth);
|
||||
part_table->mgmt_app_data.status = 0x01;
|
||||
|
||||
part_table_write(part_table);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Unregister the currently registered app, returns zero on success */
|
||||
int mgmt_app_unregister(struct partition_table *part_table)
|
||||
{
|
||||
/* Only the management app should be able to unregister itself */
|
||||
if (!mgmt_app_authenticate(&part_table->mgmt_app_data)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
part_table->mgmt_app_data.status = 0;
|
||||
|
||||
memset(part_table->mgmt_app_data.auth.nonce, 0x00,
|
||||
sizeof(part_table->mgmt_app_data.auth.nonce));
|
||||
|
||||
memset(part_table->mgmt_app_data.auth.authentication_digest, 0x00,
|
||||
sizeof(part_table->mgmt_app_data.auth.authentication_digest));
|
||||
|
||||
part_table_write(part_table);
|
||||
|
||||
return 0;
|
||||
}
|
15
hw/application_fpga/fw/tk1/mgmt_app.h
Normal file
15
hw/application_fpga/fw/tk1/mgmt_app.h
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright (C) 2024 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#ifndef MGMT_APP_H
|
||||
#define MGMT_APP_H
|
||||
|
||||
#include "partition_table.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
bool mgmt_app_authenticate(struct management_app_metadata *mgmt_table);
|
||||
int mgmt_app_register(struct partition_table *part_table);
|
||||
int mgmt_app_unregister(struct partition_table *part_table);
|
||||
|
||||
#endif
|
50
hw/application_fpga/fw/tk1/partition_table.c
Normal file
50
hw/application_fpga/fw/tk1/partition_table.c
Normal 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;
|
||||
}
|
124
hw/application_fpga/fw/tk1/partition_table.h
Normal file
124
hw/application_fpga/fw/tk1/partition_table.h
Normal file
@ -0,0 +1,124 @@
|
||||
// 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 */
|
||||
/* ---- ---- ---- */
|
||||
|
||||
/* 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 N_PRELOADED_APP 2
|
||||
#define ADDR_PRE_LOADED_APP_0 (ADDR_PARTITION_TABLE + SIZE_PARTITION_TABLE)
|
||||
#define SIZE_PRE_LOADED_APP 0x20000UL // 128KiB
|
||||
|
||||
// Pre-loaded app present and authenticated
|
||||
#define PRE_LOADED_STATUS_AUTH 0x01
|
||||
// Pre-loaded app present but not yet authenticated
|
||||
#define PRE_LOADED_STATUS_PRESENT 0x02
|
||||
|
||||
#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 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 */
|
||||
/* - 1 byte status. */
|
||||
/* - 4 bytes length. */
|
||||
/* - 16 bytes random nonce. */
|
||||
/* - 16 bytes authentication digest. */
|
||||
/* - 32 bytes digest. */
|
||||
/* - 64 bytes signature. */
|
||||
/**/
|
||||
/*- Pre-loaded device app 2 */
|
||||
/* - 1 byte status. */
|
||||
/* - 4 bytes length. */
|
||||
/* - 16 bytes random nonce. */
|
||||
/* - 16 bytes authentication digest. */
|
||||
/* - 32 bytes digest. */
|
||||
/* - 64 bytes signature. */
|
||||
/**/
|
||||
/*- 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;
|
||||
uint8_t digest[32];
|
||||
uint8_t signature[64];
|
||||
} __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[N_PRELOADED_APP];
|
||||
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
|
180
hw/application_fpga/fw/tk1/preload_app.c
Normal file
180
hw/application_fpga/fw/tk1/preload_app.c
Normal file
@ -0,0 +1,180 @@
|
||||
// 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;
|
||||
}
|
||||
|
||||
/* Returns non-zero if the app is valid */
|
||||
bool preload_check_valid_app(struct partition_table *part_table,
|
||||
uint8_t slot)
|
||||
{
|
||||
if (slot >= N_PRELOADED_APP) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (part_table->pre_app_data[slot].status == 0x00 &&
|
||||
part_table->pre_app_data[slot].size == 0) {
|
||||
/*No valid app*/
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Loads a preloaded app from flash to app RAM */
|
||||
int preload_load(struct partition_table *part_table, uint8_t from_slot)
|
||||
{
|
||||
if (from_slot >= N_PRELOADED_APP) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*Check for a valid app in flash */
|
||||
if (!preload_check_valid_app(part_table, from_slot)) {
|
||||
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)
|
||||
{
|
||||
/* Check if we are allowed to store */
|
||||
if (!mgmt_app_authenticate(&part_table->mgmt_app_data)) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
/* Check for a valid app in flash, bale out if it already exists */
|
||||
if (preload_check_valid_app(part_table, to_slot)) {
|
||||
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 *part_table, size_t app_size,
|
||||
uint8_t app_digest[32], uint8_t app_signature[64],
|
||||
uint8_t to_slot)
|
||||
{
|
||||
if (to_slot >= N_PRELOADED_APP) {
|
||||
return -4;
|
||||
}
|
||||
|
||||
/* Check if we are allowed to store */
|
||||
if (!mgmt_app_authenticate(&part_table->mgmt_app_data)) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
/* Check for a valid app in flash, bale out if it already exists */
|
||||
if (preload_check_valid_app(part_table, to_slot)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (app_size == 0 || app_size > SIZE_PRE_LOADED_APP) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
part_table->pre_app_data[to_slot].size = app_size;
|
||||
part_table->pre_app_data[to_slot].status =
|
||||
PRE_LOADED_STATUS_PRESENT; /* Stored but not yet authenticated */
|
||||
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();
|
||||
|
||||
part_table_write(part_table);
|
||||
|
||||
/* Force a restart to authenticate the stored app */
|
||||
/* TODO: Should this be done by the management app or by firmware? */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int preload_delete(struct partition_table *part_table, uint8_t slot)
|
||||
{
|
||||
if (slot >= N_PRELOADED_APP) {
|
||||
return -4;
|
||||
}
|
||||
|
||||
/* Check if we are allowed to deleted */
|
||||
if (!mgmt_app_authenticate(&part_table->mgmt_app_data)) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
/*Check for a valid app in flash */
|
||||
if (!preload_check_valid_app(part_table, slot)) {
|
||||
return 0;
|
||||
// TODO: Nothing here, return zero like all is good?
|
||||
}
|
||||
part_table->pre_app_data[slot].size = 0;
|
||||
part_table->pre_app_data[slot].status = 0;
|
||||
|
||||
memset(part_table->pre_app_data[slot].auth.nonce, 0x00,
|
||||
sizeof(part_table->pre_app_data[slot].auth.nonce));
|
||||
|
||||
memset(part_table->pre_app_data[slot].auth.authentication_digest, 0x00,
|
||||
sizeof(part_table->pre_app_data[slot].auth.authentication_digest));
|
||||
|
||||
memset(part_table->pre_app_data[slot].digest, 0,
|
||||
sizeof(part_table->pre_app_data[slot].digest));
|
||||
|
||||
memset(part_table->pre_app_data[slot].signature, 0,
|
||||
sizeof(part_table->pre_app_data[slot].signature));
|
||||
|
||||
part_table_write(part_table);
|
||||
|
||||
/* 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 (slot >= N_PRELOADED_APP) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
25
hw/application_fpga/fw/tk1/preload_app.h
Normal file
25
hw/application_fpga/fw/tk1/preload_app.h
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright (C) 2024 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#ifndef PRELOAD_APP_H
|
||||
#define PRELOAD_APP_H
|
||||
|
||||
#include "partition_table.h"
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
bool preload_check_valid_app(struct partition_table *part_table,
|
||||
uint8_t slot);
|
||||
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 *part_table, size_t app_size,
|
||||
uint8_t app_digest[32], uint8_t app_signature[64],
|
||||
uint8_t to_slot);
|
||||
int preload_delete(struct partition_table *part_table, 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
|
30
hw/application_fpga/fw/tk1/resetinfo.h
Normal file
30
hw/application_fpga/fw/tk1/resetinfo.h
Normal file
@ -0,0 +1,30 @@
|
||||
// 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_FLASH1 = 1,
|
||||
START_FLASH2 = 2,
|
||||
START_FLASH1_VER = 3,
|
||||
START_FLASH2_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
|
90
hw/application_fpga/fw/tk1/spi.c
Normal file
90
hw/application_fpga/fw/tk1/spi.c
Normal 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;
|
||||
}
|
14
hw/application_fpga/fw/tk1/spi.h
Normal file
14
hw/application_fpga/fw/tk1/spi.h
Normal 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
|
@ -4,8 +4,19 @@
|
||||
*/
|
||||
|
||||
#include <tkey/tk1_mem.h>
|
||||
|
||||
#ifdef QEMU_SYSCALL
|
||||
|
||||
#define picorv32_retirq_insn(...) \
|
||||
mv ra, x3; \
|
||||
ret
|
||||
|
||||
#else
|
||||
|
||||
#include "picorv32/custom_ops.S" // PicoRV32 custom instructions
|
||||
|
||||
#endif
|
||||
|
||||
#define illegal_insn() .word 0
|
||||
|
||||
// Variables in bss
|
||||
|
@ -8,8 +8,11 @@
|
||||
|
||||
enum state {
|
||||
FW_STATE_INITIAL,
|
||||
FW_STATE_WAITCOMMAND,
|
||||
FW_STATE_LOADING,
|
||||
FW_STATE_RUN,
|
||||
FW_STATE_CDI,
|
||||
FW_STATE_LOAD_FLASH,
|
||||
FW_STATE_START,
|
||||
FW_STATE_FAIL,
|
||||
FW_STATE_MAX,
|
||||
};
|
||||
|
197
hw/application_fpga/fw/tk1/storage.c
Normal file
197
hw/application_fpga/fw/tk1/storage.c
Normal 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);
|
||||
}
|
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_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
|
@ -1,5 +1,13 @@
|
||||
#ifdef QEMU_SYSCALL
|
||||
|
||||
#define picorv32_maskirq_insn(...)
|
||||
|
||||
#else
|
||||
|
||||
#include "../tk1/picorv32/custom_ops.S"
|
||||
|
||||
#endif
|
||||
|
||||
.section ".text"
|
||||
.globl syscall_enable
|
||||
|
||||
|
@ -5,20 +5,65 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <tkey/assert.h>
|
||||
#include <tkey/debug.h>
|
||||
#include <tkey/led.h>
|
||||
#include <tkey/lib.h>
|
||||
|
||||
#include "mgmt_app.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 uint8_t *resetinfo = (volatile uint8_t *) TK1_MMIO_RESETINFO_BASE;
|
||||
// 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:
|
||||
// TODO: Take length from user
|
||||
memcpy((uint8_t *)resetinfo, (uint8_t *)arg1, sizeof(struct 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);
|
||||
@ -28,6 +73,30 @@ int32_t syscall_handler(uint32_t number, uint32_t arg1)
|
||||
// first word. Serial is kept secret to the device
|
||||
// app.
|
||||
return udi[0];
|
||||
|
||||
case TK1_SYSCALL_PRELOAD_DELETE:
|
||||
return preload_delete(&part_table, 1);
|
||||
|
||||
case TK1_SYSCALL_PRELOAD_STORE:
|
||||
// arg1 offset
|
||||
// arg2 data
|
||||
// arg3 size
|
||||
// always using slot 1
|
||||
return preload_store(&part_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, arg1, (uint8_t *)arg2, (uint8_t *)arg3, 1);
|
||||
|
||||
case TK1_SYSCALL_PRELOAD_GET_DIGSIG:
|
||||
return preload_get_digsig(&part_table, (uint8_t *)arg1, (uint8_t *)arg2, 1);
|
||||
|
||||
case TK1_SYSCALL_REG_MGMT:
|
||||
return mgmt_app_register(&part_table);
|
||||
|
||||
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_SET_LED = 30,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
28
hw/application_fpga/tools/create_flash_image.py
Executable file
28
hw/application_fpga/tools/create_flash_image.py
Executable file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
|
||||
FLASH_SIZE_BYTES = 1 * 1024 * 1024
|
||||
PRELOADED_APP_0_START = 0x30000
|
||||
|
||||
|
||||
def run(output_path, preloaded_app_0_path):
|
||||
with (
|
||||
open(output_path, "wb") as output_file,
|
||||
open(preloaded_app_0_path, "rb") as preloaded_app_0_file,
|
||||
):
|
||||
content = bytearray(b"\xFF") * FLASH_SIZE_BYTES
|
||||
preloaded_app = preloaded_app_0_file.read()
|
||||
content[
|
||||
PRELOADED_APP_0_START : PRELOADED_APP_0_START + len(preloaded_app)
|
||||
] = preloaded_app
|
||||
output_file.write(content)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arg_parser = argparse.ArgumentParser()
|
||||
arg_parser.add_argument("OUTPUT_PATH")
|
||||
arg_parser.add_argument("PRELOADED_APP_0_PATH")
|
||||
args = arg_parser.parse_args()
|
||||
|
||||
run(args.OUTPUT_PATH, args.PRELOADED_APP_0_PATH)
|
33
hw/application_fpga/tools/load_preloaded_app.sh
Executable file
33
hw/application_fpga/tools/load_preloaded_app.sh
Executable file
@ -0,0 +1,33 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
if [ $# != 2 ]
|
||||
then
|
||||
echo "Usage: $0 slot_num app_file"
|
||||
echo ""
|
||||
echo "Where slot_num is 0 or 1."
|
||||
exit
|
||||
fi
|
||||
|
||||
SLOT_NUM="$1"
|
||||
APP="$2"
|
||||
|
||||
if [ "$SLOT_NUM" = "0" ]; then
|
||||
START_ADDRESS=0x30000
|
||||
elif [ "$SLOT_NUM" = "1" ]; then
|
||||
START_ADDRESS=0x50000
|
||||
else
|
||||
echo "Invalid slot_num"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "WARNING: Will erase entire partition table."
|
||||
read -p "Press CTRL-C to abort. Press key to continue." -n1 -s
|
||||
|
||||
# Erase partition table
|
||||
tillitis-iceprog -o 0x20000 -e 64k
|
||||
|
||||
# Erase existing pre loaded app
|
||||
tillitis-iceprog -o "$START_ADDRESS" -e 128k
|
||||
|
||||
# Write pre loaded app
|
||||
tillitis-iceprog -o "$START_ADDRESS" "$APP"
|
Loading…
x
Reference in New Issue
Block a user