Document state machine

Golden path not updated
This commit is contained in:
Michael Cardell Widerkrantz 2025-03-17 10:31:32 +01:00 committed by Mikael Ågren
parent ab12c201c4
commit 76b25bc121
No known key found for this signature in database
GPG key ID: E02DA3D397792C46

View file

@ -119,67 +119,90 @@ 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 start address is located at `0x0000_0000` in the CPU memory map, which
is also the CPU reset vector. 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 ### Firmware state machine
This is the state diagram of the firmware. There are only six states. This is the state diagram of the firmware. Change of state occur when
we receive specific I/O or a fatal error occurs.
Change of state occur when we receive specific I/O or a fatal error
occurs.
```mermaid ```mermaid
stateDiagram-v2 stateDiagram-v2
S0: resetinfo S0: initial
S4: loadflash
S1: waitcommand S1: waitcommand
S2: loading S2: loading
S3: running S3: flash_loading
S4: auth_app
S5: starting
S6: compute_cdi
SE: failed SE: failed
[*] --> S0 [*] --> S0
S0 --> S4: load 1 (def) or load 2
S0 --> S1 S0 --> S1
S0 --> S4: Default
S1 --> S1: Commands S1 --> S1: Commands
S1 --> S2: LOAD_APP S1 --> S2: LOAD_APP
S1 --> SE: Error S1 --> SE: Error
S2 --> S2: LOAD_APP_DATA S2 --> S2: LOAD_APP_DATA
S2 --> S3: Last block received S2 --> S6: Last block received
S2 --> SE: Error S2 --> SE: Error
S4 --> S3 S6 --> S3
S3 --> S5
S4 --> S5
S4 --> SE: Error S4 --> SE: Error
SE --> [*] SE --> [*]
S3 --> [*] S5 --> [*]
``` ```
States: States:
- `resetinfo` - We start by checking resetinfo data in `FW_RAM` - `initial`: We start by checking resetinfo data in `FW_RAM` for
- `waitcommand` - Waiting for initial commands from client. Allows the `startfrom`.
- `waitcommand`: Waiting for initial commands from client. Allows the
commands `NAME_VERSION`, `GET_UDI`, `LOAD_APP`. commands `NAME_VERSION`, `GET_UDI`, `LOAD_APP`.
- `loadflash` - Loading an app from flash. - `loading`: Expecting application data from client. Allows only the
- `loading` - Expect application data. Allows only the command command `LOAD_APP_DATA`.
`LOAD_APP_DATA`. - `flash_loading`: Loading and authentication app from flash. Computes CDI,
- `running` - Computes CDI and starts the application. Allows no commands. 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. - `failed` - Halts CPU. Allows no commands.
Allowed data in state `resetinfo`: Allowed data in state `resetinfo`:
| *startfrom* | *next state* | | *startfrom* | *next state* |
|----------------------|---------------| |--------------|-----------------|
| `default` | `loadflash` | | `FLASH1` | `flash_loading` |
| `Start flash slot 1` | `loadflash` | | `FLASH2` | `flash_loading` |
| `Start flash slot 2` | `loadflash` | | `FLASH1_VER` | `flash_loading` |
| `Start from client` | `waitcommand` | | `FLASH2_VER` | `flash_loading` |
| `CLIENT` | `waitcommand` |
| `CLIENT_VER` | `waitcommand` |
I/O in state `loadflash`: I/O in state `flash_loading`:
| *I/O* | *next state* | | *I/O* | *next state* |
|--------------------|--------------| |--------------------|--------------|
| Last app data read | `run` | | Last app data read | `starting` |
Commands in state `waitcommand`: Commands in state `waitcommand`:
@ -192,40 +215,52 @@ Commands in state `waitcommand`:
Commands in state `loading`: Commands in state `loading`:
| *command* | *next state* | | *command* | *next state* |
|------------------------|----------------------------------| |------------------------|---------------------------------------|
| `FW_CMD_LOAD_APP_DATA` | unchanged or `run` on last chunk | | `FW_CMD_LOAD_APP_DATA` | unchanged or `starting` on last chunk |
No other states allows commands.
See [Firmware protocol in the Dev See [Firmware protocol in the Dev
Handbook](http://dev.tillitis.se/protocol/#firmware-protocol) for the Handbook](http://dev.tillitis.se/protocol/#firmware-protocol) for the
definition of the specific commands and their responses. definition of the specific commands and their responses.
Exection starts in state `resetinfo` where the firmware checks in Plain text explanation of the states:
`FW_RAM` for what to do next.
State changes to `loadflash` if the `FW_RAM` data indicates - `initial`: Execution starts here. The firmware checks in the
that it should start one of the two flash apps. `FW_RAM` for `startfrom` for what to do next.
State changes to `waitcommand` if the `FW_RAM` data indicates that it For all `startfrom` values `FLASH_*` the next state is `startflash`.
instead should wait for commands from a client. Otherwise it goes to `waitcommand`, indicating that it should wait
for further commands from the client.
In `loadflash` state changes to `running` if the app has been - `flash_loading` loads and measure an app from flash, the Compound
successfully loaded into RAM or to `failed` otherwise. 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:
State changes from `waitcommand` to `loading` when receiving digest = blake2s(cdi, nonce from flash)
`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`.
In `running`, the loaded device app is measured, the Compound Device and then compared against the stored digest in the app's flash slot.
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.
The device app is now running in application mode. We can, however, - `waitcommand` waits for command from the client. State changes to
return to firmware mode (excepting access to the UDS) by doing system `loading` when receiving `LOAD_APP`, which also sets the size of the
calls. Note that ROM is still readable, but is now hardware protected number of data blocks to expect. After that we expect several
from execution, except through the system call mechanism. `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 ### Golden path