diff --git a/doc/system_description/fpga.md b/doc/system_description/fpga.md index 9811fbf..0c0200a 100644 --- a/doc/system_description/fpga.md +++ b/doc/system_description/fpga.md @@ -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 diff --git a/doc/system_description/software.md b/doc/system_description/software.md index 58787dd..a311303 100644 --- a/doc/system_description/software.md +++ b/doc/system_description/software.md @@ -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. diff --git a/doc/system_description/system_description.md b/doc/system_description/system_description.md index 6603bba..f104fc1 100644 --- a/doc/system_description/system_description.md +++ b/doc/system_description/system_description.md @@ -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