mirror of
https://github.com/tillitis/tillitis-key1.git
synced 2024-12-20 13:24:24 -05:00
docs: Update firmware docs and move memory map
Update firmware docs to reflect new state machine, the new stack in FW_RAM, and new loading address for app. Remove superflous technical details from the software description. Move memory subsystem and memory map to system_description.md and refer to it directly by subsection elsewhere.
This commit is contained in:
parent
fddfd88db2
commit
6613b7e695
@ -21,7 +21,9 @@ cores in different ways given the current exection mode. There are two
|
||||
execution modes - firmware and application. Basically, in application mode
|
||||
the access is more restrictive.
|
||||
|
||||
The API for all cores is described in the [Software documentation](software.md).
|
||||
The API for all cores is described in the Memory mapped hardware
|
||||
functions in the [System
|
||||
Description](system_description.md#memory-mapped-hardware-functions).
|
||||
|
||||
|
||||
### Cores
|
||||
|
@ -1,70 +1,66 @@
|
||||
# Tillitis TKey software
|
||||
|
||||
## Definitions
|
||||
|
||||
* Firmware - software in ROM responsible for loading applications.
|
||||
The firmware is included as part of the FPGA bit stream.
|
||||
* Application - software supplied by the host machine, which is
|
||||
received, loaded, and measured by the firmware (by hashing a
|
||||
digest over the binary).
|
||||
|
||||
Learn more about the concepts in the
|
||||
[system_description.md](system_description.md).
|
||||
|
||||
## CPU
|
||||
|
||||
We use a PicoRV32, a 32-bit RISC-V system (RV32ICZmmul), as the CPU
|
||||
for running the firmware and the loaded app. The firmware and device
|
||||
app both run in machine mode. All types are little-endian.
|
||||
|
||||
## Constraints
|
||||
|
||||
The application FPGA is a Lattice ICE40 UP5K, with the following
|
||||
specifications:
|
||||
|
||||
* 30 EBR[^1] x 4 Kbit => 120 Kbit. PicoRV32 uses ~4 EBRs internally
|
||||
=> 13 KB for Firmware. As of this writing firmware uses 2998 bytes.
|
||||
* 4 SPRAM[^2] x 32 KB => 128 KB RAM for application/software
|
||||
|
||||
[^1]: Embedded Block RAM (also BRAM) residing in the FPGA, can
|
||||
be configured as RAM or ROM.
|
||||
[^2]: Single Port RAM (also SRAM).
|
||||
|
||||
## Introduction
|
||||
|
||||
The TKey has two modes of operation; firmware/loader mode and
|
||||
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).
|
||||
|
||||
First, some definitions:
|
||||
|
||||
- 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.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
The firmware and application uses a memory mapped IO for SoC
|
||||
communication. This MMIO resides at `0xc000_0000`. *Nota bene*: Almost
|
||||
all access to MMIO should be word (32 bit) aligned. See table below.
|
||||
The firmware and application uses a memory mapped input/output (MMIO)
|
||||
for communication with the hardware. The memory map is constrained
|
||||
when running in application mode, e.g. UDS isn't readable, and the
|
||||
`APP_{ADDR, SIZE}` are not writable for the application.
|
||||
|
||||
The application has a constrained variant of the firmware memory map,
|
||||
which is outlined below. E.g. UDS isn't readable, and the `APP_{ADDR,
|
||||
SIZE}` are not writable for the application.
|
||||
See table in the [System
|
||||
Description](system_description.md#memory-mapped-hardware-functions)
|
||||
for details about the memory system and MMIO.
|
||||
|
||||
The firmware (and optionally all software) on the TKey communicates to
|
||||
the host via the `UART_{RX,TX}_{STATUS,DATA}` registers, using the
|
||||
The firmware (and optionally all software) on the TKey can communicate
|
||||
to the host via the `UART_{RX,TX}_{STATUS,DATA}` registers, using the
|
||||
framing protocol described in [Framing
|
||||
Protocol](../framing_protocol/framing_protocol.md).
|
||||
|
||||
The firmware defines a protocol (command/response interface) on top of
|
||||
this framing layer which is used to bootstrap the application onto the
|
||||
device. All commands are initiated by the host. All commands receive a
|
||||
reply.
|
||||
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.
|
||||
|
||||
Applications define their own per-application protocol used for
|
||||
communication with their host part. They may or may not be based on
|
||||
the Framing Protocol.
|
||||
Applications define their own protocol used for communication with
|
||||
their host part. They may or may not be based on the Framing Protocol.
|
||||
|
||||
## CPU
|
||||
|
||||
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.
|
||||
|
||||
## Constraints
|
||||
|
||||
- ROM: 6 kByte.
|
||||
- RAM: 128 kByte.
|
||||
|
||||
## Firmware
|
||||
|
||||
The purpose of the firmware is to bootstrap and measure an
|
||||
application.
|
||||
|
||||
The TKey has 128 KB RAM. Firmware loads the app at the start of RAM.
|
||||
The current C runtime (crt0.S) of apps in our [apps
|
||||
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
|
||||
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.
|
||||
@ -78,14 +74,14 @@ coming in on `UART_RX`.
|
||||
Typical use scenario:
|
||||
|
||||
1. The host sends the `FW_CMD_LOAD_APP` command with the size of the
|
||||
device app and the 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 a device app.
|
||||
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.
|
||||
2. If the the host 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_7000` and upwards. The firmware replies
|
||||
the data into `0x4000_0000` and upwards. The firmware replies
|
||||
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
|
||||
@ -94,20 +90,25 @@ Typical use scenario:
|
||||
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
|
||||
the `UDS`, the measurement of the application, and the `USS`, and
|
||||
placed in the `CDI` register. Then the start address of the
|
||||
device app, `0x4000_7000`, 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 starts the application by 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. This is outlined in the memory map below.
|
||||
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)).
|
||||
|
||||
The firmware now executes assembler code that writes zeros to
|
||||
stack and data of the firmware, then jumps to what's in
|
||||
`APP_ADDR` which starts device app execution.
|
||||
Then the firmware jumps to what's in `APP_ADDR` which starts
|
||||
the application.
|
||||
|
||||
There is now no other means of getting back from application mode
|
||||
to firmware mode than resetting/power cycling the device.
|
||||
@ -118,11 +119,15 @@ 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).
|
||||
|
||||
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
|
||||
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
|
||||
`-march=rv32imc` but things will break if you ever cause it to emit
|
||||
`div` instructions.
|
||||
|
||||
You also need `llvm-ar`, `llvm-objcopy`, `llvm-size` and `lld`.
|
||||
Typically these are all available in packages called "clang", "llvm",
|
||||
"lld" or similar.
|
||||
|
||||
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
|
||||
@ -159,26 +164,33 @@ Debugging? Use the HTIF console by removing `-DNOCONSOLE` from the
|
||||
`htif_putinthex()` `htif_hexdump()` and friends for printf-like
|
||||
debugging.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
The PicoRV32 starts executing at `0x0000_0000`. Our firmware starts at
|
||||
`_start` from `start.S` which initializes the `.data`, and `.bss` at
|
||||
`0x4000_0000` and upwards. A stack is also initialized, starting at
|
||||
0x4000_6ff0 and downwards. 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.
|
||||
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.
|
||||
|
||||
### Firmware state machine
|
||||
|
||||
States:
|
||||
|
||||
- `initial` - At start.
|
||||
- `loading` - Expect app data.
|
||||
- `run` - Computes CDI and starts the device app.
|
||||
- `loading` - Expect application data.
|
||||
- `run` - Computes CDI and starts the application.
|
||||
- `fail` - Stops waiting for commands, flashes LED forever.
|
||||
|
||||
Commands in state `initial`:
|
||||
@ -194,20 +206,23 @@ Commands in state `loading`:
|
||||
|
||||
| *command* | *next state* |
|
||||
|------------------------|----------------------------------|
|
||||
| `FW_CMD_NAME_VERSION` | unchanged |
|
||||
| `FW_CMD_GET_UDI` | unchanged |
|
||||
| `FW_CMD_LOAD_APP_DATA` | unchanged or `run` on last chunk |
|
||||
|
||||
See below for firmware protocol definition.
|
||||
Commands in state `run`: None.
|
||||
|
||||
Commands in state `fail`: None.
|
||||
|
||||
See [Firmware protocol](#firmware-protocol) for the definition of the
|
||||
specific commands and their responses.
|
||||
|
||||
### User-supplied Secret (USS)
|
||||
|
||||
USS is a 32 bytes long secret provided by the user. Typically a host
|
||||
program gets a passphrase from the user and then does KDF of some
|
||||
sort, for instance a BLAKE2s, to get 32 bytes which it sends to the
|
||||
firmware to be part of the CDI computation.
|
||||
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.
|
||||
|
||||
### Compound Device Identifier (CDI) computation
|
||||
### Compound Device Identifier computation
|
||||
|
||||
The CDI is computed by:
|
||||
|
||||
@ -222,14 +237,21 @@ 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.
|
||||
The `blake2s()` function in the firmware is fed with a buffer in
|
||||
`FW_RAM` containing the UDS, the app digest, and the USS. It is also
|
||||
fed with a context used for computations that is also part of the
|
||||
`FW_RAM`.
|
||||
We keep the entire firmware stack in `FW_RAM` and clear it just before
|
||||
switching to app mode just in case.
|
||||
|
||||
After doing the computation `FW_RAM` is also cleared with zeroes.
|
||||
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.
|
||||
|
||||
### Firmware protocol definition
|
||||
Then we continue with the CDI computation by updating with an optional
|
||||
USS and then finalizing the hash, storing the resulting digest in
|
||||
`CDI`.
|
||||
|
||||
### Firmware protocol
|
||||
|
||||
The firmware commands and responses are built on top of the [Framing
|
||||
Protocol](../framing_protocol/framing_protocol.md).
|
||||
@ -414,8 +436,9 @@ host <-
|
||||
### Firmware services
|
||||
|
||||
The firmware exposes a BLAKE2s function through a function pointer
|
||||
located in MMIO `BLAKE2S` (see memory map below) with the with
|
||||
function signature:
|
||||
located in MMIO `BLAKE2S` (see [memory
|
||||
map](system_description.md#memory-mapped-hardware-functions)) with the
|
||||
with function signature:
|
||||
|
||||
```c
|
||||
int blake2s(void *out, unsigned long outlen, const void *key,
|
||||
@ -438,98 +461,10 @@ typedef struct {
|
||||
|
||||
The `libcommon` library in
|
||||
[tillitis-key1-apps](https://github.com/tillitis/tillitis-key1-apps/)
|
||||
has a wrapper for using this function.
|
||||
has a wrapper for using this function called `blake2s()`.
|
||||
|
||||
## Memory map
|
||||
## Applications
|
||||
|
||||
Assigned top level prefixes:
|
||||
|
||||
| *name* | *prefix* | *address length* |
|
||||
|----------|----------|--------------------------------------|
|
||||
| ROM | 0b00 | 30 bit address |
|
||||
| RAM | 0b01 | 30 bit address |
|
||||
| reserved | 0b10 | |
|
||||
| MMIO | 0b11 | 6 bits for core select, 24 bits rest |
|
||||
|
||||
Addressing:
|
||||
|
||||
```
|
||||
31st bit 0th bit
|
||||
v v
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
|
||||
- Bits [31 .. 30] (2 bits): Top level prefix (described above)
|
||||
- Bits [29 .. 24] (6 bits): Core select. We want to support at least 16 cores
|
||||
- Bits [23 .. 0] (24 bits): Memory/in-core address.
|
||||
```
|
||||
|
||||
The memory exposes SoC functionality to the software when in firmware
|
||||
mode. It is a set of memory mapped registers (MMIO), starting at base
|
||||
address `0xc000_0000`. For specific offsets/bitmasks, see the file
|
||||
[tk1_mem.h](../../hw/application_fpga/fw/tk1_mem.h) (in this repo).
|
||||
|
||||
Assigned core prefixes:
|
||||
|
||||
| *name* | *prefix* |
|
||||
|--------|----------|
|
||||
| ROM | 0x00 |
|
||||
| RAM | 0x40 |
|
||||
| TRNG | 0xc0 |
|
||||
| TIMER | 0xc1 |
|
||||
| UDS | 0xc2 |
|
||||
| UART | 0xc3 |
|
||||
| TOUCH | 0xc4 |
|
||||
| FW_RAM | 0xd0 |
|
||||
| TK1 | 0xff |
|
||||
|
||||
*Nota bene*: MMIO accesses should be 32 bit wide, e.g use `lw` and
|
||||
`sw`. Exceptions are `FW_RAM` and `QEMU_DEBUG`.
|
||||
|
||||
| *name* | *fw* | *app* | *size* | *type* | *content* | *description* |
|
||||
|-------------------|-------|-----------|--------|----------|-----------|-------------------------------------------------------------------------|
|
||||
| `TRNG_STATUS` | r | r | | | | TRNG_STATUS_READY_BIT is 1 when an entropy word is available. |
|
||||
| `TRNG_ENTROPY` | r | r | 4B | u32 | | Entropy word. Reading a word will clear status. |
|
||||
| `TIMER_CTRL` | r/w | r/w | | | | If TIMER_STATUS_RUNNING_BIT in TIMER_STATUS is 0, setting |
|
||||
| | | | | | | TIMER_CTRL_START_BIT here starts the timer. |
|
||||
| | | | | | | If TIMER_STATUS_RUNNING_BIT in TIMER_STATUS is 1, setting |
|
||||
| | | | | | | TIMER_CTRL_STOP_BIT here stops the timer. |
|
||||
| `TIMER_STATUS` | r | r | | | | TIMER_STATUS_RUNNING_BIT is 1 when the timer is running. |
|
||||
| `TIMER_PRESCALER` | r/w | r/w | 4B | | | Prescaler init value. Write blocked when running. |
|
||||
| `TIMER_TIMER` | r/w | r/w | 4B | | | Timer init or current value while running. Write blocked when running. |
|
||||
| `UDS_FIRST` | r[^3] | invisible | 4B | u8[32] | | First word of Unique Device Secret key. |
|
||||
| `UDS_LAST` | | invisible | | | | The last word of the UDS |
|
||||
| `UART_BITRATE` | r/w | | | | | TBD |
|
||||
| `UART_DATABITS` | r/w | | | | | TBD |
|
||||
| `UART_STOPBITS` | r/w | | | | | TBD |
|
||||
| `UART_RX_STATUS` | r | r | 1B | u8 | | Non-zero when there is data to read |
|
||||
| `UART_RX_DATA` | r | r | 1B | u8 | | Data to read. Only LSB contains data |
|
||||
| `UART_RX_BYTES` | r | r | 4B | u32 | | Number of bytes received from the host and not yet read by SW, FW. |
|
||||
| `UART_TX_STATUS` | r | r | 1B | u8 | | Non-zero when it's OK to write data |
|
||||
| `UART_TX_DATA` | w | w | 1B | u8 | | Data to send. Only LSB contains data |
|
||||
| `TOUCH_STATUS` | r/w | r/w | | | | TOUCH_STATUS_EVENT_BIT is 1 when touched. After detecting a touch |
|
||||
| | | | | | | event (reading a 1), write anything here to acknowledge it. |
|
||||
| `FW_RAM` | r/w | invisible | 2 kiB | u8[2048] | | Firmware-only RAM. |
|
||||
| `UDI` | r | invisible | 8B | u64 | | Unique Device ID (UDI). |
|
||||
| `QEMU_DEBUG` | w | w | | u8 | | Debug console (only in QEMU) |
|
||||
| `NAME0` | r | r | 4B | char[4] | "tk1 " | ID of core/stick |
|
||||
| `NAME1` | r | r | 4B | char[4] | "mkdf" | ID of core/stick |
|
||||
| `VERSION` | r | r | 4B | u32 | 1 | Current version. |
|
||||
| `SWITCH_APP` | r/w | r | 1B | u8 | | Write anything here to trigger the switch to application mode. Reading |
|
||||
| | | | | | | returns 0 if device is in firmware mode, 0xffffffff if in app mode. |
|
||||
| `LED` | r/w | r/w | 1B | u8 | | Control of the color LEDs in RBG LED on the board. |
|
||||
| | | | | | | Bit 0 is Blue, bit 1 is Green, and bit 2 is Red LED. |
|
||||
| `GPIO` | r/w | r/w | 1B | u8 | | Bits 0 and 1 contain the input level of GPIO 1 and 2. |
|
||||
| | | | | u8 | | Bits 3 and 4 store the output level of GPIO 3 and 4. |
|
||||
| `APP_ADDR` | r/w | r | 4B | u32 | | Firmware stores app load address here, so app can read its own location |
|
||||
| `APP_SIZE` | r/w | r | 4B | u32 | | Firmware stores app app size here, so app can read its own size |
|
||||
| `BLAKE2S` | r/w | r | 4B | u32 | | Function pointer to a BLAKE2S function in the firmware |
|
||||
| `CDI_FIRST` | r/w | r | 32B | u8[32] | | Compound Device Identifier (CDI). UDS+measurement... |
|
||||
| `CDI_LAST` | | r | | | | Last word of CDI |
|
||||
| `RAM_ASLR` | w | invisible | 4B | u32 | | Address Space Randomization seed value for the RAM |
|
||||
| `RAM_SCRAMBLE` | w | invisible | 4B | u32 | | Data scrambling seed value for the RAM |
|
||||
| `CPU_MON_CTRL` | w | w | 4B | u32 | | Bit 0 enables CPU execution monitor. Can't be unset. Lock adresses |
|
||||
| `CPU_MON_FIRST` | w | w | 4B | u32 | | First address of the area monitored for execution attempts |
|
||||
| `CPU_MON_LAST` | w | w | 4B | u32 | | Last address of the area monitored for execution attempts |
|
||||
|
||||
|
||||
[^3]: The UDS can only be read *once* per power-cycle.
|
||||
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.
|
||||
|
@ -16,7 +16,7 @@ The described functionality and requirements applies to version 1 of
|
||||
the TKey (TK1)
|
||||
|
||||
The intended users of this document are:
|
||||
- Implementors of the TKkey hardware, firmware and SDKs
|
||||
- Implementors of the TKey hardware, firmware and SDKs
|
||||
- Developers of secure applications for the TKey
|
||||
- Technically skilled third parties that wants to understand the
|
||||
TKey
|
||||
@ -56,7 +56,7 @@ The derivation can also be combined with a User Supplied Secret
|
||||
(USS). This means that keys derived are both based on something the user
|
||||
has - the specific device, and something the user knows (the USS). And
|
||||
the derived can be trusted because of the measurement being used
|
||||
by the derivation, thereby verifying the intergrity od the application.
|
||||
by the derivation, thereby verifying the intergrity of the application.
|
||||
|
||||
### Execution monitor
|
||||
|
||||
@ -86,7 +86,6 @@ monitor for the area by writing to the ADDR\_CPU\_MON\_CTRL register.
|
||||
Note that once the monitor has been enabled it can't be disabled and
|
||||
the addresses defining the area can't be changed.
|
||||
|
||||
|
||||
### Illegal instruction monitor
|
||||
|
||||
Execution of illegal instructions will cause the CPU to enter its trap
|
||||
@ -95,7 +94,6 @@ the CPU state. If the CPU enters the trap state, the hardware will
|
||||
start flashing the RED led, signalling that the TKey is stuck in an
|
||||
error state.
|
||||
|
||||
|
||||
### RAM memory protection
|
||||
|
||||
The TKey hardware includes a simple form of RAM memory protection. The
|
||||
@ -171,11 +169,10 @@ The TKey store and use the following assets internally:
|
||||
altered during the life time of a given device. May be copied,
|
||||
extracted, read from the device.
|
||||
|
||||
- CDI - Compound Device Identity. Dervied by the FW when an application
|
||||
is loaded using the UDS and the application binary. Used by the
|
||||
application to derive secrets, keys as needed. The CDI should never
|
||||
be exposed outside of the application\_fpga
|
||||
|
||||
- CDI - Compound Device Identity. Computed by the FW when an
|
||||
application is loaded using the UDS and the application binary. Used
|
||||
by the application to derive secrets, keys as needed. The CDI should
|
||||
never be exposed.
|
||||
|
||||
Additionally the following asset could be provided from the host:
|
||||
|
||||
@ -183,6 +180,104 @@ Additionally the following asset could be provided from the host:
|
||||
Supplied from the host to the device. Should not be revealed to a
|
||||
third party.
|
||||
|
||||
## Memory
|
||||
|
||||
Addressing:
|
||||
|
||||
```
|
||||
31st bit 0th bit
|
||||
v v
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
|
||||
- Bits [31 .. 30] (2 bits): Top level prefix (described below)
|
||||
- Bits [29 .. 24] (6 bits): Core select. We want to support at least 16 cores
|
||||
- Bits [23 .. 0] (24 bits): Memory/in-core address.
|
||||
```
|
||||
|
||||
Assigned top level prefixes:
|
||||
|
||||
| *name* | *prefix* | *address length* |
|
||||
|----------|----------|--------------------------------------|
|
||||
| ROM | 0b00 | 30 bit address |
|
||||
| RAM | 0b01 | 30 bit address |
|
||||
| reserved | 0b10 | |
|
||||
| MMIO | 0b11 | 6 bits for core select, 24 bits rest |
|
||||
|
||||
| *memory* | *first byte* | *last byte* |
|
||||
|----------|--------------|------------------------------|
|
||||
| ROM | 0x0000\_0000 | 0x0000\_17ff |
|
||||
| RAM | 0x4000\_0000 | 0x4001\_ffff |
|
||||
| MMIO | 0xc000\_0000 | 0xffff\_ffff (last possible) |
|
||||
|
||||
### Memory mapped hardware functions
|
||||
|
||||
Hardware functions, assets, and input/output are memory mapped (MMIO)
|
||||
starting at base address `0xc000_0000`. For specific offsets/bitmasks,
|
||||
see the file [tk1_mem.h](../../hw/application_fpga/fw/tk1_mem.h) (in
|
||||
this repo).
|
||||
|
||||
Assigned core prefixes:
|
||||
|
||||
| *name* | *address prefix* |
|
||||
|--------|------------------|
|
||||
| TRNG | 0xc0 |
|
||||
| TIMER | 0xc1 |
|
||||
| UDS | 0xc2 |
|
||||
| UART | 0xc3 |
|
||||
| TOUCH | 0xc4 |
|
||||
| FW_RAM | 0xd0 |
|
||||
| TK1 | 0xff |
|
||||
|
||||
*Nota bene*: MMIO accesses should be 32 bit wide, e.g use `lw` and
|
||||
`sw`. Exceptions are `UDS`, `FW_RAM` and `QEMU_DEBUG`.
|
||||
|
||||
| *name* | *fw* | *app* | *size* | *type* | *content* | *description* |
|
||||
|-------------------|-------|-----------|--------|----------|-----------|-------------------------------------------------------------------------|
|
||||
| `TRNG_STATUS` | r | r | | | | TRNG_STATUS_READY_BIT is 1 when an entropy word is available. |
|
||||
| `TRNG_ENTROPY` | r | r | 4B | u32 | | Entropy word. Reading a word will clear status. |
|
||||
| `TIMER_CTRL` | r/w | r/w | | | | If TIMER_STATUS_RUNNING_BIT in TIMER_STATUS is 0, setting |
|
||||
| | | | | | | TIMER_CTRL_START_BIT here starts the timer. |
|
||||
| | | | | | | If TIMER_STATUS_RUNNING_BIT in TIMER_STATUS is 1, setting |
|
||||
| | | | | | | TIMER_CTRL_STOP_BIT here stops the timer. |
|
||||
| `TIMER_STATUS` | r | r | | | | TIMER_STATUS_RUNNING_BIT is 1 when the timer is running. |
|
||||
| `TIMER_PRESCALER` | r/w | r/w | 4B | | | Prescaler init value. Write blocked when running. |
|
||||
| `TIMER_TIMER` | r/w | r/w | 4B | | | Timer init or current value while running. Write blocked when running. |
|
||||
| `UDS_FIRST` | r[^3] | invisible | 4B | u8[32] | | First word of Unique Device Secret key. |
|
||||
| `UDS_LAST` | | invisible | | | | The last word of the UDS |
|
||||
| `UART_BITRATE` | r/w | | | | | TBD |
|
||||
| `UART_DATABITS` | r/w | | | | | TBD |
|
||||
| `UART_STOPBITS` | r/w | | | | | TBD |
|
||||
| `UART_RX_STATUS` | r | r | 1B | u8 | | Non-zero when there is data to read |
|
||||
| `UART_RX_DATA` | r | r | 1B | u8 | | Data to read. Only LSB contains data |
|
||||
| `UART_RX_BYTES` | r | r | 4B | u32 | | Number of bytes received from the host and not yet read by SW, FW. |
|
||||
| `UART_TX_STATUS` | r | r | 1B | u8 | | Non-zero when it's OK to write data |
|
||||
| `UART_TX_DATA` | w | w | 1B | u8 | | Data to send. Only LSB contains data |
|
||||
| `TOUCH_STATUS` | r/w | r/w | | | | TOUCH_STATUS_EVENT_BIT is 1 when touched. After detecting a touch |
|
||||
| | | | | | | event (reading a 1), write anything here to acknowledge it. |
|
||||
| `FW_RAM` | r/w | invisible | 2 kiB | u8[2048] | | Firmware-only RAM. |
|
||||
| `UDI` | r | invisible | 8B | u64 | | Unique Device ID (UDI). |
|
||||
| `QEMU_DEBUG` | w | w | | u8 | | Debug console (only in QEMU) |
|
||||
| `NAME0` | r | r | 4B | char[4] | "tk1 " | ID of core/stick |
|
||||
| `NAME1` | r | r | 4B | char[4] | "mkdf" | ID of core/stick |
|
||||
| `VERSION` | r | r | 4B | u32 | 1 | Current version. |
|
||||
| `SWITCH_APP` | r/w | r | 1B | u8 | | Write anything here to trigger the switch to application mode. Reading |
|
||||
| | | | | | | returns 0 if device is in firmware mode, 0xffffffff if in app mode. |
|
||||
| `LED` | r/w | r/w | 1B | u8 | | Control of the color LEDs in RBG LED on the board. |
|
||||
| | | | | | | Bit 0 is Blue, bit 1 is Green, and bit 2 is Red LED. |
|
||||
| `GPIO` | r/w | r/w | 1B | u8 | | Bits 0 and 1 contain the input level of GPIO 1 and 2. |
|
||||
| | | | | u8 | | Bits 3 and 4 store the output level of GPIO 3 and 4. |
|
||||
| `APP_ADDR` | r/w | r | 4B | u32 | | Firmware stores app load address here, so app can read its own location |
|
||||
| `APP_SIZE` | r/w | r | 4B | u32 | | Firmware stores app app size here, so app can read its own size |
|
||||
| `BLAKE2S` | r/w | r | 4B | u32 | | Function pointer to a BLAKE2S function in the firmware |
|
||||
| `CDI_FIRST` | r/w | r | 32B | u8[32] | | Compound Device Identifier (CDI). UDS+measurement... |
|
||||
| `CDI_LAST` | | r | | | | Last word of CDI |
|
||||
| `RAM_ASLR` | w | invisible | 4B | u32 | | Address Space Randomization seed value for the RAM |
|
||||
| `RAM_SCRAMBLE` | w | invisible | 4B | u32 | | Data scrambling seed value for the RAM |
|
||||
| `CPU_MON_CTRL` | w | w | 4B | u32 | | Bit 0 enables CPU execution monitor. Can't be unset. Lock adresses |
|
||||
| `CPU_MON_FIRST` | w | w | 4B | u32 | | First address of the area monitored for execution attempts |
|
||||
| `CPU_MON_LAST` | w | w | 4B | u32 | | Last address of the area monitored for execution attempts |
|
||||
|
||||
[^3]: The UDS can only be read *once* per power-cycle.
|
||||
|
||||
## Subsystems and Components
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user