diff --git a/hw/application_fpga/README.md b/hw/application_fpga/README.md index 1f3ab78..ea16d2d 100644 --- a/hw/application_fpga/README.md +++ b/hw/application_fpga/README.md @@ -11,9 +11,10 @@ The design top level is in `rtl/application_fpga.v`. It contains instances of all cores as well as the memory system. The memory system allows the CPU to access cores in different ways -given the current execution mode. There are two execution modes - -firmware and application. Basically, in application mode the access is -more restrictive. +given the current execution mode. There are three execution modes - +firmware, application and system call. Each mode give access to a +different set of resources. Where app mode is the most restrictive and +firmware mode is the least restrictive. The rest of the components are under `cores`. They typically have their own `README.md` file documenting them and their API in detail. @@ -34,6 +35,7 @@ Rough memory map: | UART | 0xc3 | | Touch | 0xc4 | | FW\_RAM | 0xd0 | +| Syscall | 0xe1 | | TK1 | 0xff | ## `clk_reset_gen` @@ -96,6 +98,54 @@ hours, days) there is also a 32 bit prescaler. The timer is available to use by firmware and applications. +## Syscall + +System call trigger area. A 32-bit write to address 0xe1000000 will +trigger interrupt 31, which in turn triggers a system call in the +firmware. + +## Interrupts + +Triggering an interrupt will cause the CPU to execute the interrupt +handler att address 0x10. + +The interrupt handler is shared by all PicoRV32 interrupts but only +interrupt 31 is enabled on the Tkey. Register `x4` can be inspected to +determine the interrupt source. Each interrupt source is assigned one +bit in x4. Triggered interrupts have their bit set to `1`. + +| *Source* | *x4 Bit* | +|----------|----------| +| Syscall | 31 | + +The return address is located in register `x3`. Calling the PicoRV32 +specific instruction `retirq` exits the interrupt handler and clears +the interrupt source. + +No registers are stored/restored when entering/exiting the interrupt +handler. It is up to the software to store/restore as necessary. + +Interrupts can be enabled/disabled using the PicoRV32 specific +`maskirq` instruction. + +## Restricted resources + +The following table shows resource availablility for each execution +mode: + +| *Execution Mode* | *ROM* | *FW RAM* | *SPI* | *UDS* | +|------------------|-------|----------|-------|-------| +| Firmware mode | r/x | r/w | r/w | r/w* | +| Syscall | r/x | r/w | r/w | i | +| Application mode | r | i | i | i | + +Legend: +r = readable +w = writeable +x = executable +i = invisible +* = UDS words are readable only once in firmware mode. + ## `tk1` See [tk1 README](core/tk1/README.md) for details. @@ -107,7 +157,6 @@ Contains: - RGB LED control. - General purpose input/output (GPIO) pin control. - Application introspection: start address and size of binary. -- BLAKE2s function access. - Compound Device Identity (CDI). - Unique Device Identity (UDI). - RAM memory protection. @@ -135,13 +184,13 @@ should make it infeasible to improve asset extraction by observing multiple memory dumps from the same TKey device. The attack should also not directly scale to multiple TKey devices. -The RAM address and data scrambling is done in de RAM core. +The RAM address and data scrambling is done in the RAM core. The memory protection is setup by the firmware. Access to the memory protection controls is disabled for applications. Before the memory protecetion is enabled, the RAM is filled with randomised data using -Xorwow. So during boot the firmware perform the following steps to -setup the memory protection: +Xorwow. During boot the firmware perform the following steps to setup +the memory protection: 1. Get a random 32-bit value from the TRNG to use as data state for Xorwow. diff --git a/hw/application_fpga/core/tk1/README.md b/hw/application_fpga/core/tk1/README.md index 43420fc..df22f04 100644 --- a/hw/application_fpga/core/tk1/README.md +++ b/hw/application_fpga/core/tk1/README.md @@ -23,17 +23,6 @@ and version of the device. They can be read by FW as well as applications. -### Control of execution mode - -``` -ADDR_APP_MODE_CTRL: 0x08 -``` - -This register controls if the device is executing in FW mode or in App -mode. The register can be written once between power cycles, and only -by FW. If set the device is in app mode. - - ### Control of RGB LED ``` @@ -75,19 +64,7 @@ ADDR_APP_SIZE: 0x0d These registers provide read only information to the loaded app to itself - where it was loaded and its size. The values are written by FW as part of the loading of the app. The registers can't be written -when the `ADDR_APP_MODE_CTRL` has been set. - - -### Access to Blake2s - -``` -ADDR_BLAKE2S: 0x10 -``` - -This register provides the 32-bit function pointer address to the -Blake2s hash function in the FW. It is written by FW during boot. The -register can't be written to when the `ADDR_APP_MODE_CTRL` has been -set. +in application mode. ### Access to CDI @@ -99,10 +76,10 @@ ADDR_CDI_LAST: 0x27 These registers provide access to the 256-bit compound device secret calculated by the FW as part of loading an application. The registers -are written by the FW. The register can't be written to when the -`ADDR_APP_MODE_CTRL` has been set. The CDI is readable by apps, which -can then use it as a base secret for any other secrets required to -carry out their intended use case. +are written by the FW. The register can't be written in application +mode. The CDI is readable by apps, which can then use it as a base +secret for any other secrets required to carry out their intended use +case. ### Access to UDI diff --git a/hw/application_fpga/core/uds/README.md b/hw/application_fpga/core/uds/README.md index 8edca6b..8722bf5 100644 --- a/hw/application_fpga/core/uds/README.md +++ b/hw/application_fpga/core/uds/README.md @@ -6,8 +6,7 @@ Unique Device Secret core This core store and protect the Unique Device Secret (UDS) asset. The UDS can be accessed as eight separate 32-bit words. The words can only -be accessed as long as the app_mode input is low, implying that the -CPU is executing the FW. +be accessed as long as the `en` input is high. The UDS words can be accessed in any order, but a given word can only be accessed once between reset cycles. This read once functionality is diff --git a/hw/application_fpga/fw/README.md b/hw/application_fpga/fw/README.md index c91a915..3408e12 100644 --- a/hw/application_fpga/fw/README.md +++ b/hw/application_fpga/fw/README.md @@ -1,35 +1,43 @@ -# Firmware +# Firmware implementation notes ## Introduction -This text is an introduction to, a requirement specification of, -and some implementation notes of the TKey firmware. It also gives a -few hint on developing and debugging the firmware. - -This text is specific for the firmware. For a more general description -on how to implement device apps, see [the TKey Developer -Handbook](https://dev.tillitis.se/). +This text is specific for the firmware, the piece of software in TKey +ROM. For a more general description on how to implement device apps, +see [the TKey Developer Handbook](https://dev.tillitis.se/). ## Definitions -- Firmware - software in ROM responsible for loading applications. The - firmware is included as part of the FPGA bitstream and not - replacable on a usual consumer TKey. -- Device application or app - software supplied by the client which is - received, loaded, measured, and started by the firmware. +- Firmware: Software in ROM responsible for loading, measuring, and + starting applications. The firmware is included as part of the FPGA + bitstream and not replacable on a usual consumer TKey. +- Client: Software running on a computer or a mobile phone the TKey is + inserted into. +- Device application or app: Software supplied by the client that runs + on the TKey. ## CPU modes and firmware The TKey has two modes of software operation: firmware mode and -application mode. The TKey always starts in firmware mode and starts -the firmware. When the firmware is about to start the application it -switches to a more constrained environment, the application mode. +application mode. The TKey always starts in firmware mode when it +starts the firmware. When the application starts the hardware +automatically switches to a more constrained environment: the +application mode. -The TKey hardware cores are memory mapped. Firmware has complete -access, except that the UDS is readable only once. The memory map is -constrained when running in application mode, e.g. FW\_RAM and UDS -isn't readable, and several other hardware addresses are either not -readable or not writable for the application. +The TKey hardware cores are memory mapped but the memory access is +different depending on mode. Firmware has complete access, except that +the Unique Device Secret (UDS) words are readable only once even in +firmware mode. The memory map is constrained when running in +application mode, e.g. FW\_RAM and UDS isn't readable, and several +other hardware addresses are either not readable or not writable for +the application. + +When doing system calls from a device app the context switches back to +firmware mode. However, the UDS is still not available, protected by +two measures: 1) the UDS words can only be read out once and have +already been read by firmware when measuring the app, and, 2) the UDS +is protected by hardware after the execution leaves ROM for the first +time. See the table in [the Developer Handbook](https://dev.tillitis.se/memory/) for an overview about the @@ -72,12 +80,17 @@ Dev Handbook for specific details. ## Memory constraints -- ROM: 6 kByte. -- FW\_RAM: 4 kByte. - - fw stack: 3824 bytes. - - resetinfo: 256 bytes. - - rest is available for .data and .bss. -- RAM: 128 kByte. +| *Name* | *Size* | *FW mode* | *App mode* | +|---------|-----------|-----------|------------| +| ROM | 8 kByte | r-x | r | +| FW\_RAM | 4 kByte* | rw- | - | +| RAM | 128 kByte | rwx | rwx | + +* FW\_RAM is divided into the following areas: + +- fw stack: 3824 bytes. +- resetinfo: 256 bytes. +- rest is available for .data and .bss. ## Firmware behaviour @@ -87,7 +100,7 @@ application received from the client over the USB/UART. The firmware binary is part of the FPGA bitstream as the initial 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 the CPU reset vector. +is also the CPU reset vector. ### Firmware state machine @@ -151,26 +164,30 @@ is received, when state is changed to "running". In "running", the loaded device app is measured, the Compound Device Identifier (CDI) is computed, we do some cleanup of firmware data -structures, flip to application mode, and finally start the app, which -ends the firmware state machine. +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. There is no other -means of getting back from application mode to firmware mode than -resetting/power cycling the device. Note that ROM is still accessible -in the memory map, so it's still possible to execute firmware code in -application mode, but with no privileged access. +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. When reset is released, the CPU starts executing the firmware. It -begins by clearing all CPU registers, clears all FW\_RAM, sets up a -stack for itself there, and then jumps to `main()`. +begins in `start.S` by clearing all CPU registers, clears all FW\_RAM, +sets up a stack for itself there, and then jumps to `main()`. Also +included in the assembly part of firmware is an interrupt handler for +the system calls, but the handler is not yet enabled. -Beginning at `main()` it sets up the "system calls", then 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. +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. Typical expected use scenario: @@ -199,7 +216,7 @@ Typical expected use scenario: ([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). + (USS) digest already received. 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 @@ -207,24 +224,22 @@ Typical expected use scenario: it is, if it wants to relocate in RAM. 7. The firmware now clears the part of the special `FW_RAM` where it - keeps it stack. After this it performs no more function calls and - uses no more automatic variables. + keeps it stack. - 8. Firmware starts the application by first switching from firmware - mode to application mode by writing to the `APP_MODE_CTRL` - 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 [the memory + 8. The interrupt handler for system calls is enabled. + + 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/)). - Then the firmware jumps to what is in `APP_ADDR` which starts the - application. - If during this whole time any commands are received which are not -allowed in the current state, we enter the "failed" state and execute -an illegal instruction. An illegal instruction traps the CPU and -hardware blinks the status LED red until a power cycle. No further -instructions are executed. +allowed in the current state, or any errors occur, we enter the +"failed" state and execute an illegal instruction. An illegal +instruction traps the CPU and hardware blinks the status LED red until +a power cycle. No further instructions are executed. ### User-supplied Secret (USS) @@ -256,42 +271,43 @@ 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. +should now not be available even to firmware. Then we continue with the CDI computation by updating with an optional -USS and then finalizing the hash, storing the resulting digest in +USS digest and finalizing the hash, storing the resulting digest in `CDI`. -### Firmware services +### Firmware system calls -The firmware exposes a BLAKE2s function through a function pointer -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, - unsigned long keylen, const void *in, unsigned long inlen, - blake2s_ctx *ctx); +The firmware provides a system call mechanism through the use of the +PicoRV32 interrupt handler. They are triggered by writing to the +trigger address: 0xe1000000. It's typically done with a function +signature like this: ``` - -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; +int syscall(uint32_t number, uint32_t arg1); ``` -The `libcommon` library in -[tkey-libs](https://github.com/tillitis/tkey-libs/) -has a wrapper for using this function called `blake2s()` which needs -to be maintained if you do any changes to the firmware call. +Arguments are system call number and upto 6 generic arguments passed +to the system call handler. The caller should place the system call +number in the a0 register and the arguments in registers a1 to a7 +according to the RISC-V calling convention. The caller is responsible +for saving and restoring registers. + +The syscall handler returns execution on the next instruction after +the store instruction to the trigger address. The return value from +the syscall is now available in x10 (a0). + +To add or change syscalls, see the `syscall_handler()` in +`syscall_handler.c`. + +Currently supported syscalls: + +| *Name* | *Number* | *Argument* | *Description* | +|-------------|----------|------------|----------------------------------| +| RESET | 1 | Unused | Reset the TKey | +| SET\_LED | 10 | Colour | Set the colour of the status LED | +| GET\_VIDPID | 12 | Unused | Get Vendor and Product ID | ## Developing firmware