2022-12-01 10:15:27 -05:00
|
|
|
# Tillitis TKey software
|
2022-09-19 02:51:11 -04:00
|
|
|
|
2024-03-15 10:04:57 -04:00
|
|
|
**NOTE:** Documentation migrated to dev.tillitis.se, this is kept for
|
|
|
|
history. This is likely to be outdated.
|
|
|
|
|
2023-03-22 15:37:15 -04:00
|
|
|
## Introduction
|
2022-09-21 02:55:13 -04:00
|
|
|
|
2023-03-22 15:37:15 -04:00
|
|
|
This text is both an introduction to and a requirement specification
|
|
|
|
of the TKey firmware, its protocol, and an overview of how TKey
|
|
|
|
applications are supposed to work. For an overview of the TKey
|
|
|
|
concepts, see [System Description](system_description.md).
|
2022-09-19 02:51:11 -04:00
|
|
|
|
2023-03-22 15:37:15 -04:00
|
|
|
First, some definitions:
|
2022-09-19 02:51:11 -04:00
|
|
|
|
2023-03-22 15:37:15 -04:00
|
|
|
- Firmware - software in ROM responsible for loading applications. The
|
|
|
|
firmware is included as part of the FPGA bit stream.
|
|
|
|
- Application or app - software supplied by the host machine which is
|
|
|
|
received, loaded, measured, and started by the firmware.
|
2022-09-19 02:51:11 -04:00
|
|
|
|
2023-03-22 15:37:15 -04:00
|
|
|
The TKey has two modes of software operation: firmware mode and
|
|
|
|
application mode. The firmware mode has the responsibility of
|
|
|
|
receiving, measuring, loading, and starting the application. When the
|
|
|
|
firmware is about to start the application it switches to a more
|
|
|
|
constrained environment, the application mode.
|
2022-09-19 03:43:53 -04:00
|
|
|
|
2023-03-22 15:37:15 -04:00
|
|
|
The firmware and application uses a memory mapped input/output (MMIO)
|
|
|
|
for communication with the hardware. The memory map is constrained
|
2023-03-28 04:02:57 -04:00
|
|
|
when running in application mode, e.g. FW-RAM and UDS isn't readable,
|
|
|
|
and several MMIO addresses are either not readable or not writable for
|
|
|
|
the application.
|
2022-10-04 05:07:44 -04:00
|
|
|
|
2023-03-22 15:37:15 -04:00
|
|
|
See table in the [System
|
|
|
|
Description](system_description.md#memory-mapped-hardware-functions)
|
2023-03-28 04:02:57 -04:00
|
|
|
for details about access rules control in the memory system and MMIO.
|
2022-09-19 02:51:11 -04:00
|
|
|
|
2023-03-22 15:37:15 -04:00
|
|
|
The firmware (and optionally all software) on the TKey can communicate
|
|
|
|
to the host via the `UART_{RX,TX}_{STATUS,DATA}` registers, using the
|
2024-06-19 10:08:43 -04:00
|
|
|
framing protocol described in the [Framing
|
|
|
|
Protocol](https://dev.tillitis.se/protocol/).
|
2022-09-19 02:51:11 -04:00
|
|
|
|
2023-03-22 15:37:15 -04:00
|
|
|
The firmware defines a protocol on top of this framing layer which is
|
|
|
|
used to bootstrap the application. All commands are initiated by the
|
|
|
|
host. All commands receive a reply. See [Firmware
|
|
|
|
protocol](#firmware-protocol) for specific details.
|
2022-09-19 02:51:11 -04:00
|
|
|
|
2023-03-22 15:37:15 -04:00
|
|
|
Applications define their own protocol used for communication with
|
|
|
|
their host part. They may or may not be based on the Framing Protocol.
|
2022-09-19 02:51:11 -04:00
|
|
|
|
2023-03-22 15:37:15 -04:00
|
|
|
## CPU
|
2022-09-19 03:43:53 -04:00
|
|
|
|
2023-03-22 15:37:15 -04:00
|
|
|
The CPU is a PicoRV32, a 32-bit RISC-V processor (arch: RV32IC\_Zmmul)
|
|
|
|
which runs the firmware and the application. The firmware and
|
|
|
|
application both run in RISC-V machine mode. All types are
|
|
|
|
little-endian.
|
2022-09-19 02:51:11 -04:00
|
|
|
|
2023-03-22 15:37:15 -04:00
|
|
|
## Constraints
|
2022-09-19 02:51:11 -04:00
|
|
|
|
2023-03-22 15:37:15 -04:00
|
|
|
- ROM: 6 kByte.
|
|
|
|
- RAM: 128 kByte.
|
2022-09-19 02:51:11 -04:00
|
|
|
|
|
|
|
## Firmware
|
|
|
|
|
2022-12-01 10:15:27 -05:00
|
|
|
The purpose of the firmware is to bootstrap and measure an
|
|
|
|
application.
|
|
|
|
|
2023-03-22 15:37:15 -04:00
|
|
|
The TKey has 128 kilobyte RAM. Firmware loads the application at the
|
|
|
|
start of RAM. The current C runtime (`crt0.S`) of apps in our [apps
|
2023-03-06 08:07:03 -05:00
|
|
|
repo](https://github.com/tillitis/tillitis-key1-apps) sets up the
|
|
|
|
stack to start just below the end of RAM. This means that a larger app
|
|
|
|
comes at the compromise of it having a smaller stack.
|
2022-09-19 04:59:29 -04:00
|
|
|
|
2022-09-19 03:43:53 -04:00
|
|
|
The firmware is part of FPGA bitstream (ROM), and is loaded at
|
|
|
|
`0x0000_0000`.
|
2022-09-19 02:51:11 -04:00
|
|
|
|
2022-12-01 10:15:27 -05:00
|
|
|
When the firmware starts it clears all RAM and then wait for commands
|
|
|
|
coming in on `UART_RX`.
|
2022-09-19 02:51:11 -04:00
|
|
|
|
2022-12-01 10:15:27 -05:00
|
|
|
Typical use scenario:
|
2022-09-19 03:43:53 -04:00
|
|
|
|
2022-11-24 10:37:23 -05:00
|
|
|
1. The host sends the `FW_CMD_LOAD_APP` command with the size of the
|
2023-03-22 15:37:15 -04:00
|
|
|
device app and the optional user-supplied secret as arguments and
|
|
|
|
and gets a `FW_RSP_LOAD_APP` back. After using this it's not
|
|
|
|
possible to restart the loading of an application.
|
2022-11-24 10:37:23 -05:00
|
|
|
2. If the the host receive a sucessful response, it will send
|
2022-10-12 09:10:10 -04:00
|
|
|
multiple `FW_CMD_LOAD_APP_DATA` commands, together containing the
|
|
|
|
full application.
|
2022-11-24 10:37:23 -05:00
|
|
|
3. On receiving`FW_CMD_LOAD_APP_DATA` commands the firmware places
|
2023-03-22 15:37:15 -04:00
|
|
|
the data into `0x4000_0000` and upwards. The firmware replies
|
2022-11-24 10:37:23 -05:00
|
|
|
with a `FW_RSP_LOAD_APP_DATA` response to the host 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 measurement.
|
|
|
|
5. The Compound Device Identifier (CDI) is then computed by using
|
2023-03-22 15:37:15 -04:00
|
|
|
the `UDS`, application digest, and the `USS`, and placed in
|
|
|
|
`CDI`. (see [Compound Device Identifier
|
|
|
|
computation](#compound-device-identifier-computation)) Then the
|
|
|
|
start address of the device app, `0x4000_0000`, is written to
|
|
|
|
`APP_ADDR` and the size 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.
|
|
|
|
6. The firmware now clears the special `FW_RAM` where it keeps it
|
|
|
|
stack. After this it does no more function calls and uses no more
|
|
|
|
automatic variables.
|
|
|
|
7. Firmware starts the application by first switching to application
|
|
|
|
mode by writing to the `SWITCH_APP` register. In this mode the
|
|
|
|
MMIO region is restricted, e.g. some registers are removed
|
|
|
|
(`UDS`), and some are switched from read/write to read-only (see
|
|
|
|
[memory
|
|
|
|
map](system_description.md#memory-mapped-hardware-functions)).
|
|
|
|
|
|
|
|
Then the firmware jumps to what's in `APP_ADDR` which starts
|
|
|
|
the application.
|
2022-11-24 10:37:23 -05:00
|
|
|
|
|
|
|
There is now no other means of getting back from application mode
|
|
|
|
to firmware mode than resetting/power cycling the device.
|
|
|
|
|
2022-12-01 10:15:27 -05:00
|
|
|
### Developing firmware
|
|
|
|
|
|
|
|
Standing in `hw/application_fpga/` you can run `make firmware.elf` to
|
|
|
|
build just the firmware. You don't need all the FPGA development tools
|
|
|
|
mentioned in [Toolchain setup](../toolchain_setup.md).
|
|
|
|
|
2023-03-22 15:37:15 -04:00
|
|
|
You need clang with 32 RISC-V support `-march=rv32iczmmul` which comes
|
|
|
|
in clang 15. If you don't have version 15 you might get by with
|
2022-12-01 10:15:27 -05:00
|
|
|
`-march=rv32imc` but things will break if you ever cause it to emit
|
|
|
|
`div` instructions.
|
|
|
|
|
2023-03-22 15:37:15 -04:00
|
|
|
You also need `llvm-ar`, `llvm-objcopy`, `llvm-size` and `lld`.
|
|
|
|
Typically these are all available in packages called "clang", "llvm",
|
|
|
|
"lld" or similar.
|
|
|
|
|
2022-12-01 10:15:27 -05:00
|
|
|
If your available `objcopy` and `size` commands is anything other than
|
|
|
|
the default `llvm-objcopy` and `llvm-size` define `OBJCOPY` and `SIZE`
|
|
|
|
to whatever they're called on your system before calling `make
|
|
|
|
firmware.elf`.
|
|
|
|
|
|
|
|
If you want to use our emulator, clone the `tk1` branch of [our
|
|
|
|
version of qemu](https://github.com/tillitis/qemu) and build:
|
|
|
|
|
|
|
|
```
|
|
|
|
$ git clone -b tk1 https://github.com/tillitis/qemu
|
|
|
|
$ mkdir qemu/build
|
|
|
|
$ cd qemu/build
|
|
|
|
$ ../configure --target-list=riscv32-softmmu --disable-werror
|
|
|
|
$ make -j $(nproc)
|
|
|
|
```
|
|
|
|
|
|
|
|
(Built with warnings-as-errors disabled, see [this
|
|
|
|
issue](https://github.com/tillitis/qemu/issues/3).)
|
|
|
|
|
|
|
|
Run it like this:
|
|
|
|
|
|
|
|
```
|
|
|
|
$ /path/to/qemu/build/qemu-system-riscv32 -nographic -M tk1,fifo=chrid -bios firmware.elf \
|
|
|
|
-chardev pty,id=chrid
|
|
|
|
```
|
|
|
|
|
|
|
|
This attaches the FIFO to a tty, something like `/dev/pts/16` which
|
|
|
|
you can use with host software to talk to the firmware.
|
|
|
|
|
|
|
|
To quit QEMU you can use: `Ctrl-a x` (see `Ctrl-a ?` for other commands).
|
|
|
|
|
|
|
|
Debugging? Use the HTIF console by removing `-DNOCONSOLE` from the
|
2022-12-02 09:09:10 -05:00
|
|
|
`CFLAGS` and using the helper functions in `lib.c` like `htif_puts()`
|
|
|
|
`htif_putinthex()` `htif_hexdump()` and friends for printf-like
|
|
|
|
debugging.
|
2022-12-01 10:15:27 -05:00
|
|
|
|
2023-03-22 15:37:15 -04:00
|
|
|
You can add `-d guest_errors` to the qemu commandline to make QEMU
|
|
|
|
send errors from the TK1 machine to stderr, typically things like
|
|
|
|
memory writes outside of mapped regions.
|
|
|
|
|
2022-12-01 10:15:27 -05:00
|
|
|
You can also use the qemu monitor for debugging, e.g. `info
|
|
|
|
registers`, or run qemu with `-d in_asm` or `-d trace:riscv_trap`.
|
|
|
|
|
|
|
|
### Reset
|
|
|
|
|
2023-03-22 15:37:15 -04:00
|
|
|
The PicoRV32 starts executing at `0x0000_0000`. We allow no `.data` or
|
|
|
|
`.bss` sections. Our firmware starts at the `_start` symbol in
|
|
|
|
`start.S` which first clears all RAM for any remaining data from
|
|
|
|
previous applications, then initializes a stack starting at the top of
|
|
|
|
`FW_RAM` at `0xd000_0800` and downwards and then calls `main`.
|
|
|
|
|
|
|
|
When the initialization is finished, the firmware waits for incoming
|
|
|
|
commands from the host, by busy-polling the `UART_RX_{STATUS,DATA}`
|
|
|
|
registers. When a complete command is read, the firmware executes the
|
|
|
|
command.
|
2022-12-01 10:15:27 -05:00
|
|
|
|
|
|
|
### Firmware state machine
|
|
|
|
|
|
|
|
States:
|
|
|
|
|
|
|
|
- `initial` - At start.
|
2023-03-22 15:37:15 -04:00
|
|
|
- `loading` - Expect application data.
|
|
|
|
- `run` - Computes CDI and starts the application.
|
2023-03-07 09:08:24 -05:00
|
|
|
- `fail` - Stops waiting for commands, flashes LED forever.
|
2022-12-01 10:15:27 -05:00
|
|
|
|
|
|
|
Commands in state `initial`:
|
|
|
|
|
2023-03-07 09:08:24 -05:00
|
|
|
| *command* | *next state* |
|
|
|
|
|-----------------------|--------------|
|
|
|
|
| `FW_CMD_NAME_VERSION` | unchanged |
|
|
|
|
| `FW_CMD_GET_UDI` | unchanged |
|
|
|
|
| `FW_CMD_LOAD_APP` | `loading` |
|
|
|
|
| | |
|
2022-12-01 10:15:27 -05:00
|
|
|
|
|
|
|
Commands in state `loading`:
|
|
|
|
|
|
|
|
| *command* | *next state* |
|
|
|
|
|------------------------|----------------------------------|
|
2023-03-07 09:08:24 -05:00
|
|
|
| `FW_CMD_LOAD_APP_DATA` | unchanged or `run` on last chunk |
|
2022-12-01 10:15:27 -05:00
|
|
|
|
2023-03-22 15:37:15 -04:00
|
|
|
Commands in state `run`: None.
|
|
|
|
|
|
|
|
Commands in state `fail`: None.
|
|
|
|
|
2024-06-25 09:26:06 -04:00
|
|
|
See [Firmware protocol in the Dev
|
|
|
|
Handbook](http://dev.tillitis.se/protocol/#firmware-protocol) for the
|
|
|
|
definition of the specific commands and their responses.
|
2022-12-01 10:15:27 -05:00
|
|
|
|
2022-11-24 10:37:23 -05:00
|
|
|
### User-supplied Secret (USS)
|
|
|
|
|
|
|
|
USS is a 32 bytes long secret provided by the user. Typically a host
|
2023-03-22 15:37:15 -04:00
|
|
|
program gets a secret from the user and then does a key derivation
|
|
|
|
function of some sort, for instance a BLAKE2s, to get 32 bytes which
|
|
|
|
it sends to the firmware to be part of the CDI computation.
|
2022-11-24 10:37:23 -05:00
|
|
|
|
2023-03-22 15:37:15 -04:00
|
|
|
### Compound Device Identifier computation
|
2022-10-25 11:27:26 -04:00
|
|
|
|
|
|
|
The CDI is computed by:
|
|
|
|
|
|
|
|
```
|
|
|
|
CDI = blake2s(UDS, blake2s(app), USS)
|
|
|
|
```
|
|
|
|
|
|
|
|
In an ideal world, software would never be able to read UDS at all and
|
|
|
|
we would have a BLAKE2s function in hardware that would be the only
|
|
|
|
thing able to read the UDS. Unfortunately, we couldn't fit a BLAKE2s
|
|
|
|
implementation in the FPGA at this time.
|
|
|
|
|
|
|
|
The firmware instead does the CDI computation using the special
|
|
|
|
firmware-only `FW_RAM` which is invisible after switching to app mode.
|
2023-03-22 15:37:15 -04:00
|
|
|
We keep the entire firmware stack in `FW_RAM` and clear it just before
|
|
|
|
switching to app mode just in case.
|
|
|
|
|
|
|
|
We sleep for a random number of cycles before reading out the UDS,
|
|
|
|
call `blake2s_update()` with it and then immediately call
|
|
|
|
`blake2s_update()` again with the program digest, destroying the UDS
|
|
|
|
stored in the internal context buffer. UDS should now not be in
|
|
|
|
`FW_RAM` anymore. We can read UDS only once per power cycle so UDS
|
|
|
|
should now not be available to firmware at all.
|
2022-10-25 11:27:26 -04:00
|
|
|
|
2023-03-22 15:37:15 -04:00
|
|
|
Then we continue with the CDI computation by updating with an optional
|
|
|
|
USS and then finalizing the hash, storing the resulting digest in
|
|
|
|
`CDI`.
|
2022-11-24 10:37:23 -05:00
|
|
|
|
2022-12-09 11:34:17 -05:00
|
|
|
### Firmware services
|
|
|
|
|
|
|
|
The firmware exposes a BLAKE2s function through a function pointer
|
2023-03-22 15:37:15 -04:00
|
|
|
located in MMIO `BLAKE2S` (see [memory
|
|
|
|
map](system_description.md#memory-mapped-hardware-functions)) with the
|
|
|
|
with function signature:
|
2022-12-09 11:34:17 -05:00
|
|
|
|
|
|
|
```c
|
|
|
|
int blake2s(void *out, unsigned long outlen, const void *key,
|
|
|
|
unsigned long keylen, const void *in, unsigned long inlen,
|
|
|
|
blake2s_ctx *ctx);
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
where `blake2s_ctx` is:
|
|
|
|
|
|
|
|
```c
|
|
|
|
typedef struct {
|
|
|
|
uint8_t b[64]; // input buffer
|
|
|
|
uint32_t h[8]; // chained state
|
|
|
|
uint32_t t[2]; // total number of bytes
|
|
|
|
size_t c; // pointer for b[]
|
|
|
|
size_t outlen; // digest size
|
|
|
|
} blake2s_ctx;
|
|
|
|
```
|
|
|
|
|
|
|
|
The `libcommon` library in
|
|
|
|
[tillitis-key1-apps](https://github.com/tillitis/tillitis-key1-apps/)
|
2023-03-22 15:37:15 -04:00
|
|
|
has a wrapper for using this function called `blake2s()`.
|
2022-09-19 02:51:11 -04:00
|
|
|
|
2023-03-22 15:37:15 -04:00
|
|
|
## Applications
|
2022-09-19 03:43:53 -04:00
|
|
|
|
2023-03-22 15:37:15 -04:00
|
|
|
See [our apps repo](https://github.com/tillitis/tillitis-key1-apps)
|
|
|
|
for examples of client and TKey apps as well as libraries for writing
|
|
|
|
both.
|