Compare commits

..

85 commits

Author SHA1 Message Date
Jonas Thörnblad
a0f699aea5
Update USB product descriptor for Castor 2025-06-27 15:01:29 +02:00
Jonas Thörnblad
62adf4da71
Update USB VID and PID for Castor
VID has been wrong for some reason, therefore changed to the
correct value 0x1209.

New PID to differentiate Castor from Bellatrix.
2025-06-27 15:01:29 +02:00
Jonas Thörnblad
1a904e8857
Fix allowed_app_digest formatting
Fix formating of BLAKE2s digest of app allowed to start from
flash slot 0.
2025-06-27 15:01:29 +02:00
Jonas Thörnblad
03d96c3e96
tool: Fix b2s BLAKE2s digest zero padding 2025-06-24 18:18:17 +02:00
Jonas Thörnblad
c0b3c80620
Add make targets for building CH552 firmware with podman 2025-06-24 17:27:21 +02:00
Mikael Ågren
460d310c73
fw: Fix qemu_firmware build warnings 2025-06-19 08:50:13 +02:00
Michael Cardell Widerkrantz
f4f8c9e6c6
doc: Update Building & flashing docs
- Point out where to find tools.

- Add some description that we assume a Linux dist.

- Move the make targets around: make flash builds and flashes the
  entire thing so someone who just want to exactly that can use it
  right away.

- Explain what kind of hardware the USB controller is.

- Add a description on how to be able to use chprog without being
  root when running chprog.
2025-05-30 18:07:29 +02:00
Sasko Simonovski
ed9395c832
doc: Describe how to test alpha release in release notes
Adds information and link on how to test alpha release.
2025-05-28 11:56:22 +02:00
Mikael Ågren
888f18e5fe
doc: Refer to CH55x Reset Controller silk screen labels in flash docs 2025-05-26 07:59:50 +02:00
Michael Cardell Widerkrantz
24ef7b412b
doc: Add description on how to build and flash USB controller firmware 2025-05-23 15:43:44 +02:00
Michael Cardell Widerkrantz
f5d2cfef15
doc: Mention the tkeyimage tool in firmware README 2025-05-23 14:12:36 +02:00
Michael Cardell Widerkrantz
9a93da087d
doc: Document how to flash with filesystem 2025-05-23 14:12:36 +02:00
Michael Cardell Widerkrantz
916c37eab9
doc: Update release notes
Co-authored-by: Mikael Ågren <mikael@tillitis.se>
2025-05-23 14:05:58 +02:00
Michael Cardell Widerkrantz
29e5888482
doc: Remove old toolchain setup text
Use current information in Developer Handbook,
https://dev.tillitis.se/ instead of relying on this old description.
2025-05-23 14:05:58 +02:00
Mikael Ågren
e8acc7aee2
build: Flash partition table when running make prog_flash
Changes make targets:

- prog_flash in hw/application/Makefile which flashes only the
  bitstream is renamed to prog_flash_bs.

- prog_flash in hw/application/Makefile is modified to flash the
  bitstream, the testloadapp.bin in app slot 0, and the partition table.

- flash in contrib/Makefile is modified to use prog_flash from
  hw/application/Makefile
2025-05-22 15:24:45 +02:00
Mikael Ågren
4172db8dfb
build: Do not use sudo when running tillitis-iceprog 2025-05-22 15:06:39 +02:00
Mikael Ågren
7b1c1e5076
testapp: Update to 24 MHz clock 2025-05-22 09:31:54 +02:00
Michael Cardell Widerkrantz
1fec28ff0d
doc: Complete copyright and licenses
- Point out licensing terms in docs.
- Add missing SPDX tags
- Update the SPDX checker to check all the files we want to check.
- Include spdx-ensure in CI.
2025-05-22 09:31:54 +02:00
Michael Cardell Widerkrantz
8f9c706b9e
doc: Correct the GPL file
Import file

https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt

verbatim since it clearly says in the license that we are now allowed
to change it. I think it might have been a cut and paste from

https://www.gnu.org/licenses/old-licenses/gpl-2.0.html

before?
2025-05-22 09:31:54 +02:00
Mikael Ågren
07e487733b
build: Check app code formatting from application_fpga/Makefile 2025-05-22 09:31:54 +02:00
Michael Cardell Widerkrantz
ba17a2b29e
build: Use only one Makefile for apps
- Use one common Makefile for all test device apps.
- Use a single copy of syscall.[Sh].
- Update docs for building.
2025-05-22 09:31:50 +02:00
Michael Cardell Widerkrantz
6e3034c3ce
build: Move .clang-format to top level 2025-05-21 09:44:17 +02:00
Michael Cardell Widerkrantz
e302910f4d
Remove superfluous file __init__.py 2025-05-20 17:41:18 +02:00
Michael Cardell Widerkrantz
13641cb18b
build: Move test applications and the defaultapp
Instead of having the test apps under fw we create a new directory for
them.
2025-05-20 17:37:58 +02:00
Michael Cardell Widerkrantz
69940d2c64
doc: Link to firmware docs from TKey hardware design 2025-05-20 17:28:01 +02:00
Michael Cardell Widerkrantz
4ec58ce04c
tools: Prune and document tools
Add a tools/README.md.

Remove:

- create_flash_image.py: role now overtaken by tkeyimage which does
  more.
- reset-tk1: Leftover from when production_test was removed.

Rename:

- makehex.py: Moved up one level.
2025-05-20 17:12:46 +02:00
Michael Cardell Widerkrantz
a1f37d17c9
tool: Rename partition_table to tkeyimage 2025-05-20 13:50:55 +02:00
Michael Cardell Widerkrantz
fab126b695
tool: Add docs to partition_table
Co-authored-by: Mikael Ågren <mikael@tillitis.se>
2025-05-20 13:50:55 +02:00
Michael Cardell Widerkrantz
9a17aa6bdb
tool: Add SPDX tag to partition_table 2025-05-20 13:50:55 +02:00
Michael Cardell Widerkrantz
0d6e1d9ba5
fw: Add debug print when reading partition table fails 2025-05-20 13:50:55 +02:00
Michael Cardell Widerkrantz
ea29843037
tool: Make partition_table able to produce flash file
Usage:

  ./partition_table -o flash.bin -f -app0 path/to/app

Will produce a flash.bin that can be used for qemu.

- Adjust the size of the partition table.
- Refactor and rename genPartitionFile().
2025-05-20 13:50:51 +02:00
Mikael Ågren
6afdc114b8
Update binary hashes for bitstream 2025-05-20 11:59:44 +02:00
Mikael Ågren
2556f61f5a
fpga: Bump tk1 core version to 6 2025-05-20 11:27:07 +02:00
Mikael Ågren
b144cdfbdb
fpga: Use Castor specific VID/PID in UDI
Allows an app to determine which type of device it is running on.

- Reserve vendor ID 0x7357 for people using Unlocked.
- Use Castor product ID.
- Serial number is just nonsense, as before.
2025-05-20 11:25:54 +02:00
Michael Cardell Widerkrantz
8965fea947
Reset USB controller endpoints when starting
When starting, reset the USB controller to only enable the USB CDC
endpoint and the internal command channel. If the app resets firmware,
but had differend endpoints enabled, we want to go back to a known
state.
2025-05-16 17:09:13 +02:00
Mikael Ågren
daa7807c0f
Update binary hashes for bitstream & firmware 2025-05-15 16:15:14 +02:00
Mikael Ågren
53bc2d5fa0
fw: Update flash_write_data() to handle sizes larger than 4096 bytes 2025-05-15 16:13:30 +02:00
Mikael Ågren
5a9b77806f
fw: Return 0 on sys_alloc success, -1 on error
It is left to the app to keep track of whether it has had access to the
allocated area before.
2025-05-15 16:13:30 +02:00
Mikael Ågren
887883c8db
fw: Allow last storage area sector to be erased 2025-05-15 16:13:29 +02:00
Mikael Ågren
2dce9828ea
Update binary hashes for bitstream & firmware 2025-05-15 14:14:52 +02:00
Mikael Ågren
a2b77ec348
fw: Return reset() return value in TK1_SYSCALL_RESET 2025-05-15 14:14:39 +02:00
Mikael Ågren
9a3b4b9dca
fw: Use sizeof(resetinfo->app_digest) instead of hardcoded value 2025-05-15 14:14:38 +02:00
Michael Cardell Widerkrantz
48108cb3a2
fw: Build qemu_firmware with different linker script
The qemu_firmware is too large for the real hardware's 8k of ROM. The
emulator, however, has lots of ROM. Use a different linker script for
to reflect this.
2025-05-15 14:03:04 +02:00
Michael Cardell Widerkrantz
e935195846
fw: Add syscall TK1_SYSCALL_GET_APP_DATA
Add a new syscall to enable an app to get the data left for it by the
previous app in chain.

- Change testloadapp to leave some data for the next app to read.
- Call system call with:

  uint8_t next_app_data[RESET_DATA_SIZE];

  syscall(TK1_SYSCALL_GET_APP_DATA, (uint32_t)next_app_data, 0, 0);
2025-05-15 14:03:04 +02:00
Jonas Thörnblad
14e4cd09c9
ch552: Fix FIDO data copy
Fix potential out of bounds write.
2025-05-07 10:39:10 +02:00
Jonas Thörnblad
ec9ef31140
doc: Fix endpoint info 2025-05-07 10:22:29 +02:00
Michael Cardell Widerkrantz
6745c56851
Update binary hashes for bitstream & firmware 2025-05-06 17:52:14 +02:00
Michael Cardell Widerkrantz
fea9df790d
fw/docs: Correct documentation
Co-authored-by: Mikael Ågren <mikael@tillitis.se>
2025-05-06 17:52:10 +02:00
Michael Cardell Widerkrantz
8cf2cd08b7
fw/defaultapp: Introduce simple default app
To retain the default behaviour from Bellatrix, we introduce a simple
default app. If used on flash app slot 0 we get the same behaviour as
in Bellatrix, that is, waiting for an app from the client.
2025-05-06 17:52:09 +02:00
Michael Cardell Widerkrantz
d83d659284
fw: Remove use of timer in flash operations
Since we want to keep the user of the timer to the device apps, remove
the use of the timer for implementing a delay when writing to flash.
Let's try without any delay what so ever, just busylooping the query
to the chip.
2025-05-06 17:52:09 +02:00
Michael Cardell Widerkrantz
4f4de4a07d
fw: Harmonize comment style 2025-05-06 17:52:09 +02:00
Michael Cardell Widerkrantz
f373ad3f68
fw: Introduce reset()
- New function reset.c:reset(). Move code from syscall handler switch
  to this function.

- Rename resetinfo.h to reset.h.
2025-05-06 17:52:05 +02:00
Michael Cardell Widerkrantz
9d1bbffbaa
fw: Remove unneeded variable
Instead of assigning error to a variable, just include the function
returning the error in the if case.
2025-04-29 22:00:54 +02:00
Michael Cardell Widerkrantz
0692dddbae
fw: Simplify error return codes
Since callees doesn't differentiate between different errors, we have
no list of what different error codes mean, just return -1 on all
errors.
2025-04-29 22:00:51 +02:00
Mikael Ågren
15a350da1e
fw: Set LED colors
- Set LED color to white when firmware has initialized
- Set LED color to black when changing state to loading
- Set LED color to blue when starting testloadapp
- Update mgmt app allowed digest since testloadapp changed
2025-04-29 21:58:50 +02:00
Mikael Ågren
edbcdb111f
fw: Update default partition table 2025-04-29 21:58:50 +02:00
Michael Cardell Widerkrantz
3e8ff9671c
fw/tools: Change partition checksum to vanilla BLAKE2s
Instead of using 16 byte BLAKE2s with a dummy key, use plain vanilla
unkeyed 32 byte BLAKE2s for partition checksum.

Co-authored-by: Mikael Ågren <mikael@tillitis.se>
2025-04-29 21:58:50 +02:00
Michael Cardell Widerkrantz
66ea8df1d9
fw: Rename partition digest to checksum
- Rename functions, defines, et c to indicate that it's a checksum
  over the partition, not necessarily a cryptographic hash digest even
  though we use a version of BLAKE2s.

- Add comments describing where the checksum is stored and what it is
  used for.

Co-authored-by: Mikael Ågren <mikael@tillitis.se>
2025-04-29 21:54:06 +02:00
Michael Cardell Widerkrantz
106a7a5613
fw: Check flash app length to be within limits
Complain if the pre-loaded app on flash is larger than app RAM.

Co-authored-by: Mikael Ågren <mikael@tillitis.se>
2025-04-29 21:53:36 +02:00
Michael Cardell Widerkrantz
49d5a26a77
fw: Check syscall arg pointers to be in app RAM
When we pass pointers in system calls these pointers should point to
app RAM, not any other parts of the memory map, and especially not to
memory like FW_RAM that is only available in in a higher privilege
mode.

Co-authored-by: Mikael Ågren <mikael@tillitis.se>
2025-04-29 21:53:24 +02:00
Michael Cardell Widerkrantz
632b6d8fc7
fw: Limit flash offsets to be within sane limits
Limit flash offsets passed to syscalls. Be sure to check the limits
before doing any form of calculation with the passed values.

Co-authored-by: Mikael Ågren <mikael@tillitis.se>
2025-04-28 15:21:10 +02:00
Mikael Ågren
506b4c8269
doc: Add ERASE_DATA syscall 2025-04-24 16:03:21 +02:00
Mikael Ågren
9c1bb53d7a
fw: Add ERASE_DATA syscall
Erase one or more flash sectors in app storage areas
2025-04-24 16:03:20 +02:00
Michael Cardell Widerkrantz
a9d3dd7242
testapp: Use tkey-libs crt0 and linker script 2025-04-24 16:03:05 +02:00
Michael Cardell Widerkrantz
3be9e8ab19
doc: Update release notes with filesystem things 2025-04-24 16:03:05 +02:00
Michael Cardell Widerkrantz
d7ddae42d0
doc: Update firmware README
- Describe all the new functionality.
- Revise text.
2025-04-24 16:03:05 +02:00
Mikael Ågren
18773cdcf2
fw: Use globbing for FMTFILES 2025-04-24 16:03:04 +02:00
Michael Cardell Widerkrantz
25f3300964
fw: Change splint config
- Now uses at least some of the standard libraries like stdlib.h,
  stddef.h, et cetera.

- Include LIBDIR headers.

- Disregard some warnings globally.
2025-04-24 16:03:04 +02:00
Michael Cardell Widerkrantz
c1902c0955
fw: Rename FIRMWARE_SOURCES, use globbing
The symbol is only used for the check targets (with clangd and splint)
and doesn't include all the source files in the firmware. Let's just
use globbing instead.
2025-04-24 16:03:04 +02:00
Mikael Ågren
528f997681
tool: Add script to load pre-loaded app into flash 2025-04-24 16:03:03 +02:00
Michael Cardell Widerkrantz
73ea180b2a
tool: Add default_partition.bin
Add default partition table

Partition table built with `./partition_table/partition_table -o
default_partition.bin --app0 ../fw/testloadapp/testloadapp.bin`
2025-04-24 16:03:03 +02:00
Michael Cardell Widerkrantz
6324da2c90
tool: Introduce b2s tool to help compute BLAKE2s digests 2025-04-24 16:03:03 +02:00
Mikael Ågren
7511e98abe
tool: Add tool to inspect and create partition table binaries 2025-04-24 16:03:02 +02:00
Mikael Ågren
8c091d9719
tool: Add tool to create a flash image containing a preloaded app at slot 0 2025-04-24 16:03:02 +02:00
Michael Cardell Widerkrantz
ce97682758
testloadapp: Add app for testing preloaded app functionality
Co-authored-by: Mikael Ågren <mikael@tillitis.se>
2025-04-24 16:03:02 +02:00
Jonas Thörnblad
e37985938d
reset_test: Add resetinfo testapp
Co-authored-by: Mikael Ågren <mikael@tillitis.se>
Co-authored-by: Michael Cardell Widerkrantz <mc@tillitis.se>
2025-04-24 16:03:01 +02:00
Mikael Ågren
c5c6230664
fw: Replace custom picorv32 instructions when building for qemu 2025-04-24 16:03:01 +02:00
Mikael Ågren
49c06d78d1
testapp: Call storage syscalls
Calls
- TK1_SYSCALL_ALLOC_AREA
- TK1_SYSCALL_WRITE_DATA
- TK1_SYSCALL_READ_DATA
- TK1_SYSCALL_DEALLOC_AREA
2025-04-24 16:03:01 +02:00
Michael Cardell Widerkrantz
2c1c05f180
fw: Add pre loaded flash app and flash data storage
- Add per app flash storage
  - Adds four data areas. An app can allocate an area. Once allocated
    the area is tied to the CDI of the app and can only be
    read/written/deallocated by the same app.
- Add two pre loaded app slots to flash
  - Load an app from the first slot at boot. The app digest must match a
    specific digest specified in firmware.
  - Optionally load an app from the second slot
- Add a resetinfo area in FW_RAM which is used to signal an app's intent
  of resetting the system and, optionally, pass data to firmware or the
  next app in a bootchain.

Co-authored-by: Jonas Thörnblad <jonas@tillitis.se>
Co-authored-by: Mikael Ågren <mikael@tillitis.se>
Co-authored-by: Daniel Jobson <jobson@tillitis.se>
2025-04-24 16:02:34 +02:00
Michael Cardell Widerkrantz
4841b1b127
fw: Use BLAKE2s functions from tkey-libs
Instead of using the firmware's own copy of BLAKE2s functions, use the
functions from tkey-libs.
2025-04-24 09:10:55 +02:00
Michael Cardell Widerkrantz
1be5140850
tkey-libs: Optimize for size
Optimize tkey-libs for size to fit firmware in ROM
2025-04-24 09:10:55 +02:00
Mikael Ågren
353d7e9f50
tkey-libs: Import tag fw-4 of tkey-libs
- Use tag fw-4 from https://github.com/tillitis/tkey-libs/
2025-04-23 15:13:28 +02:00
Jonas Thörnblad
f75620720f
ch552: Clean up debugging of USB stack 2025-04-17 10:27:57 +02:00
Jonas Thörnblad
fb1269b06e
ch552: Fix various USB stack things, add check for IO_CH552 command range.
- Move handling of returning data from USB_GET_DESCRIPTOR request.
- Fix correct size of ActiveCfgDesc descriptor.
- Add missing check for limiting total length in USB_CDC_REQ_TYPE_GET_LINE_CODING.
- Add missing break in DeviceInterrupt to follow coding style.
- Check command range for IO_CH552.
- Add some comments.
2025-04-17 10:27:57 +02:00
Jonas Thörnblad
770acc9b38
ch552: Add CCID (Smart Card) support 2025-04-17 10:27:56 +02:00
81 changed files with 2550 additions and 1831 deletions

View file

@ -31,6 +31,9 @@ jobs:
run: |
make checkfmt
- name: check for SPDX tags
run: ./LICENSES/spdx-ensure
check-firmware:
runs-on: ubuntu-latest
container:
@ -148,8 +151,3 @@ jobs:
- name: check matching hashes for firmware.bin & application_fpga.bin
working-directory: hw/application_fpga
run: make check-binary-hashes
# TODO? first deal with hw/boards/ and hw/production_test/
# - name: check for SPDX tags
# run: ./LICENSES/spdx-ensure

4
.gitignore vendored
View file

@ -28,11 +28,11 @@
/testbench_verilator*
/check.smt2
/check.vcd
/hw/application_fpga/tkey-libs/libblake2s.a
/hw/application_fpga/tkey-libs/libcommon.a
/hw/application_fpga/tkey-libs/libcrt0.a
/hw/application_fpga/tkey-libs/libmonocypher.a
/hw/application_fpga/tkey-libs/libblake2s.a
/hw/application_fpga/tools/partition_table/partition_table
/hw/application_fpga/tools/tkeyimage/tkeyimage
/hw/application_fpga/tools/b2s/b2s
synth.json
synth.txt

View file

@ -2,11 +2,13 @@
## Main license
Unless otherwise noted, the project sources are licensed under the
terms and conditions of the "GNU General Public License v2.0 only".
Unless otherwise noted, the project sources are copyright Tillitis AB,
but you can redistribute it and/or modify it under the terms of the
GNU General Public License version 2 as published by the Free Software
Foundation. See `gpl-2.0.txt`.
The `LICENSES/` directory contains copies of the license texts used by
the sources included in the project source tree.
This directory contains copies of the license texts used by the
sources included in the project source tree.
## SPDX
@ -19,3 +21,21 @@ The current set of valid, predefined SPDX identifiers can be found on
the SPDX License List at:
https://spdx.org/licenses/
## Notable imported projects
- ch552 firmware: `hw/usb_interface/ch552_fw/`
Originally by WCH under MIT. Much changed by Tillitis.
- picorv32: `hw/application_fpga/core/picorv32`
From https://github.com/YosysHQ/picorv32
ISC.
- PicoRV32 custom ops: `hw/application_fpga/fw/tk1/picorv32/`
- tkey-libs: `hw/application_fpga/tkey-libs/`
BSD2. From https://github.com/tillitis/tkey-libs

338
LICENSES/gpl-2.0.txt Normal file
View file

@ -0,0 +1,338 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
<https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Moe Ghoul>, 1 April 1989
Moe Ghoul, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View file

@ -1,245 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your freedom to share and
change it. By contrast, the GNU General Public License is intended to guarantee your
freedom to share and change free software--to make sure the software is free for all
its users. This General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to using it.
(Some other Free Software Foundation software is covered by the GNU Lesser General
Public License instead.) You can apply it to your programs, too.
When we speak of free software, we are referring to freedom, not price. Our General
Public Licenses are designed to make sure that you have the freedom to distribute
copies of free software (and charge for this service if you wish), that you receive
source code or can get it if you want it, that you can change the software or use
pieces of it in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid anyone to deny you
these rights or to ask you to surrender the rights. These restrictions translate to
certain responsibilities for you if you distribute copies of the software, or if you
modify it.
For example, if you distribute copies of such a program, whether gratis or for a
fee, you must give the recipients all the rights that you have. You must make sure
that they, too, receive or can get the source code. And you must show them these
terms so they know their rights.
We protect your rights with two steps: (1) copyright the software, and (2) offer you
this license which gives you legal permission to copy, distribute and/or modify the
software.
Also, for each author's protection and ours, we want to make certain that everyone
understands that there is no warranty for this free software. If the software is
modified by someone else and passed on, we want its recipients to know that what
they have is not the original, so that any problems introduced by others will not
reflect on the original authors' reputations.
Finally, any free program is threatened constantly by software patents. We wish to
avoid the danger that redistributors of a free program will individually obtain
patent licenses, in effect making the program proprietary. To prevent this, we have
made it clear that any patent must be licensed for everyone's free use or not
licensed at all.
The precise terms and conditions for copying, distribution and modification follow.
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains a notice placed
by the copyright holder saying it may be distributed under the terms of this General
Public License. The "Program", below, refers to any such program or work, and a
"work based on the Program" means either the Program or any derivative work under
copyright law: that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another language.
(Hereinafter, translation is included without limitation in the term
"modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not covered by this
License; they are outside its scope. The act of running the Program is not
restricted, and the output from the Program is covered only if its contents
constitute a work based on the Program (independent of having been made by running
the Program). Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and appropriately publish
on each copy an appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any warranty; and
give any other recipients of the Program a copy of this License along with the
Program.
You may charge a fee for the physical act of transferring a copy, and you may at
your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion of it, thus
forming a work based on the Program, and copy and distribute such modifications or
work under the terms of Section 1 above, provided that you also meet all of these
conditions:
a) You must cause the modified files to carry prominent notices stating that you
changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in whole or in
part contains or is derived from the Program or any part thereof, to be licensed
as a whole at no charge to all third parties under the terms of this License.
c) If the modified program normally reads commands interactively when run, you
must cause it, when started running for such interactive use in the most
ordinary way, to print or display an announcement including an appropriate
copyright notice and a notice that there is no warranty (or else, saying that
you provide a warranty) and that users may redistribute the program under these
conditions, and telling the user how to view a copy of this License. (Exception:
if the Program itself is interactive but does not normally print such an
announcement, your work based on the Program is not required to print an
announcement.)
These requirements apply to the modified work as a whole. If identifiable sections
of that work are not derived from the Program, and can be reasonably considered
independent and separate works in themselves, then this License, and its terms, do
not apply to those sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based on the
Program, the distribution of the whole must be on the terms of this License, whose
permissions for other licensees extend to the entire whole, and thus to each and
every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest your rights to
work written entirely by you; rather, the intent is to exercise the right to control
the distribution of derivative or collective works based on the Program.
In addition, mere aggregation of another work not based on the Program with the
Program (or with a work based on the Program) on a volume of a storage or
distribution medium does not bring the other work under the scope of this License.
3. You may copy and distribute the Program (or a work based on it, under Section 2)
in object code or executable form under the terms of Sections 1 and 2 above provided
that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable source code,
which must be distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three years, to give
any third party, for a charge no more than your cost of physically performing
source distribution, a complete machine-readable copy of the corresponding
source code, to be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer to distribute
corresponding source code. (This alternative is allowed only for noncommercial
distribution and only if you received the program in object code or executable
form with such an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for making
modifications to it. For an executable work, complete source code means all the
source code for all modules it contains, plus any associated interface definition
files, plus the scripts used to control compilation and installation of the
executable. However, as a special exception, the source code distributed need not
include anything that is normally distributed (in either source or binary form) with
the major components (compiler, kernel, and so on) of the operating system on which
the executable runs, unless that component itself accompanies the executable.
If distribution of executable or object code is made by offering access to copy from
a designated place, then offering equivalent access to copy the source code from the
same place counts as distribution of the source code, even though third parties are
not compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program except as
expressly provided under this License. Any attempt otherwise to copy, modify,
sublicense or distribute the Program is void, and will automatically terminate your
rights under this License. However, parties who have received copies, or rights,
from you under this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not signed it.
However, nothing else grants you permission to modify or distribute the Program or
its derivative works. These actions are prohibited by law if you do not accept this
License. Therefore, by modifying or distributing the Program (or any work based on
the Program), you indicate your acceptance of this License to do so, and all its
terms and conditions for copying, distributing or modifying the Program or works
based on it.
6. Each time you redistribute the Program (or any work based on the Program), the
recipient automatically receives a license from the original licensor to copy,
distribute or modify the Program subject to these terms and conditions. You may not
impose any further restrictions on the recipients' exercise of the rights granted
herein. You are not responsible for enforcing compliance by third parties to this
License.
7. If, as a consequence of a court judgment or allegation of patent infringement or
for any other reason (not limited to patent issues), conditions are imposed on you
(whether by court order, agreement or otherwise) that contradict the conditions of
this License, they do not excuse you from the conditions of this License. If you
cannot distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may not
distribute the Program at all. For example, if a patent license would not permit
royalty-free redistribution of the Program by all those who receive copies directly
or indirectly through you, then the only way you could satisfy both it and this
License would be to refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under any particular
circumstance, the balance of the section is intended to apply and the section as a
whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any patents or other
property right claims or to contest validity of any such claims; this section has
the sole purpose of protecting the integrity of the free software distribution
system, which is implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed through that system
in reliance on consistent application of that system; it is up to the author/donor
to decide if he or she is willing to distribute software through any other system
and a licensee cannot impose that choice.
This section is intended to make thoroughly clear what is believed to be a
consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in certain countries
either by patents or by copyrighted interfaces, the original copyright holder who
places the Program under this License may add an explicit geographical distribution
limitation excluding those countries, so that distribution is permitted only in or
among countries not thus excluded. In such case, this License incorporates the
limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions of the
General Public License from time to time. Such new versions will be similar in
spirit to the present version, but may differ in detail to address new problems or
concerns.
Each version is given a distinguishing version number. If the Program specifies a
version number of this License which applies to it and "any later version", you have
the option of following the terms and conditions either of that version or of any
later version published by the Free Software Foundation. If the Program does not
specify a version number of this License, you may choose any version ever published
by the Free Software Foundation.
10. If you wish to incorporate parts of the Program into other free programs whose
distribution conditions are different, write to the author to ask for permission.
For software which is copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our decision will be
guided by the two goals of preserving the free status of all derivatives of our free
software and of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE
PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN
WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH
YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY
SERVICING, REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY
COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM
AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE
OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE
WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

View file

@ -16,34 +16,73 @@ LICENSES/
doc/
hw/application_fpga/core/picorv32/
hw/application_fpga/core/uart/
hw/application_fpga/fw/tk1/blake2s/
)
missingok_files=(
.editorconfig
.gitattributes
.gitignore
.clang-format
README.md
contrib/99-tillitis.rules
contrib/Dockerfile
contrib/Makefile
dco.md
contrib/verible.sha512
hw/application_fpga/README.md
hw/application_fpga/core/clk_reset_gen/README.md
hw/application_fpga/core/fw_ram/README.md
hw/application_fpga/core/ram/README.md
hw/application_fpga/core/rom/README.md
hw/application_fpga/core/tk1/tb/udi.hex
hw/application_fpga/core/uds/README.md
hw/application_fpga/fw/README.md
hw/application_fpga/fw/tk1/picorv32/README.md
hw/application_fpga/apps/Makefile
hw/application_fpga/apps/README.md
hw/application_fpga/application_fpga.bin.sha256
hw/application_fpga/config.vlt
hw/application_fpga/core/timer/README.md
hw/application_fpga/core/tk1/README.md
hw/application_fpga/core/touch_sense/README.md
hw/application_fpga/core/trng/README.md
hw/application_fpga/core/uds/README.txt
hw/application_fpga/data/udi.hex
hw/application_fpga/data/uds.hex
hw/application_fpga/firmware.bin.sha512
hw/application_fpga/fw/.clang-format
hw/application_fpga/fw/testfw/Makefile
hw/application_fpga/fw/tk1/Makefile
hw/application_fpga/tools/makehex/makehex.py
hw/application_fpga/tools/reset-tk1
hw/application_fpga/tools/tpt/README.md
hw/usb_interface/ch552_fw/.gitignore
hw/usb_interface/ch552_fw/LICENSES/GPL-2.0-only.txt
hw/usb_interface/ch552_fw/LICENSES/MIT.txt
hw/usb_interface/ch552_fw/Makefile
hw/usb_interface/ch552_fw/README.md
# tkey-libs is assumed to be REUSE compliant
hw/application_fpga/tkey-libs/LICENSE
hw/application_fpga/tkey-libs/LICENSES/BSD-2-Clause.txt
hw/application_fpga/tkey-libs/LICENSES/CC0-1.0.txt
hw/application_fpga/tkey-libs/Makefile
hw/application_fpga/tkey-libs/README-DIST.txt
hw/application_fpga/tkey-libs/README.md
hw/application_fpga/tkey-libs/RELEASE.md
hw/application_fpga/tkey-libs/blake2s/LICENSE
hw/application_fpga/tkey-libs/blake2s/Makefile
hw/application_fpga/tkey-libs/blake2s/blake2s.c
hw/application_fpga/tkey-libs/blake2s/blake2s.h
hw/application_fpga/tkey-libs/blake2s/blake2s_test.c
hw/application_fpga/tkey-libs/example-app/Makefile
hw/application_fpga/tkey-libs/monocypher/LICENSE
hw/application_fpga/tkey-libs/monocypher/README.md
hw/application_fpga/tools/README.md
hw/application_fpga/tools/b2s/README.md
hw/application_fpga/tools/b2s/go.mod
hw/application_fpga/tools/b2s/go.sum
hw/application_fpga/tools/default_partition.bin
hw/application_fpga/tools/tkeyimage/README.md
hw/application_fpga/tools/tkeyimage/go.mod
hw/application_fpga/tools/tkeyimage/go.sum
)
is_missingok() {

View file

@ -40,9 +40,17 @@ software and hardware should be.
## Licensing
Unless otherwise noted, the project sources are copyright Tillitis AB.
but you can redistribute it and/or modify it under the terms of the
GNU General Public License version 2 as published by the Free Software
Foundation.
See [LICENSES](./LICENSES/README.md) for more information about
the projects' licenses.
Each imported project is typically kept in its own directory with its
own LICENSE file.
## Repositories
This repository contains the FPGA design, the source of the
@ -77,26 +85,83 @@ https://github.com/tillitis/tkey-libs
but keep our own copy of it in the repo. See below.
## Building
## Building & flashing
Building is probably easiest using make and Podman. Do this to see all
targets:
These instructions assume you're using a Linux distribution. Most of
them also assume you're using our OCI image
[tkey-builder](https://ghcr.io/tillitis/tkey-builder). If you want to
run native tools, look in `contrib/Dockerfile` and
`contrib/buildtools.sh` for the tools and versions to use.
### FPGA
You need a [TKey
Unlocked](https://shop.tillitis.se/products/tkey-not-provisioned), a
[the TP1 TKey Programmer
board](https://shop.tillitis.se/products/tkey-dev-kit), and probably a
[Blinkinlabs CH55x Reset
Controller](https://shop-nl.blinkinlabs.com/products/ch55x-reset-controller)
to use this on real hardware.
Building is probably easiest using make and Podman.
To build everything and then flash the resulting bitstream with the
testloadapp in app slot 0 and the partition table copies in one go,
place the TKey Unlocked in the TP1, then do:
```
cd contrib
make flash
```
This uses the make target `prog_flash` in
`hw/application_fpga/Makefile` behind the scenes, but mounts your TP1
device into the container.
To see all targets:
```
cd contrib
make
```
Build the entire FPGA bitstream, which includes the firmware, using
Podman:
See the [Tillitis Developer Handbook](https://dev.tillitis.se) for
more.
### USB Controller
The TKey uses a WCH CH552 chip as a USB controller. It has its own
firmware.
Build:
```
cd contrib
make run-make
make run
cd hw/usb_interface/ch552_fw
make
```
See the [Tillitis Developer Handbook](https://dev.tillitis.se) for
more.
To flash the controller with new firmware you need hardware like the
[Blinkinlabs CH55x Reset
Controller](https://shop-nl.blinkinlabs.com/products/ch55x-reset-controller)
and a USB-A to USB-C converter.
[Reset Controller source](https://github.com/Blinkinlabs/ch55x_programmer).
You also need [chprog](https://github.com/ole00/chprog).
The bootloader identifies itself as USB VID 4348, PID 55e0. To be able
to access it and run `chprog` without root you need to allow your user
to access it. Place `contrib/99-tillitis.rules` in `/etc/udev/rules.d`
and run `udevadm control --reload`. Now you can add your user to the
`dialout` group and access it.
1. Connect the Reset Controller to your computer through "DUT\_IN"/"PC".
2. Connect the TKey to "DUT\_OUT"/"DUT".
3. Press the "Bootloader" button.
4. Run `make flash_patched` in `hw/usb_interface/ch552_fw` outside of
a container.
## Updating and working with tkey-libs

View file

@ -9,17 +9,19 @@ IMAGE=ghcr.io/tillitis/tkey-builder:5rc1
all:
@echo "Targets:"
@echo "run Run a shell using image '$(IMAGE)' (Podman)"
@echo "run-make Build the FPGA bitstream using image '$(IMAGE)' (Podman)"
@echo "run-tb Run all the testbenches using image '$(IMAGE)' (Podman)"
@echo "run-make-no-clean Like run-make but without cleaning first, useful for iterative firmware dev"
@echo "run-make-clean_fw Like run-make but cleans only firmware"
@echo "flash Program the SPI flash on the TKey - needs an existing bitstream"
@echo "pull Pull down the image '$(IMAGE)' (Podman)"
@echo "build-image Build a toolchain image named '$(BUILDIMAGE)' (Podman)"
@echo " A newly built image can be used like: make IMAGE=$(BUILDIMAGE) run"
@echo "docker-run Run a shell using image '$(IMAGE)' (Docker)"
@echo "docker-build-image Build a toolchain image named '$(BUILDIMAGE)' (Docker)"
@echo "run Run a shell using image '$(IMAGE)' (Podman)"
@echo "run-make Build the FPGA bitstream using image '$(IMAGE)' (Podman)"
@echo "run-make-ch552 Build the CH552 firmware using image '$(IMAGE)' (Podman)"
@echo "run-tb Run all the testbenches using image '$(IMAGE)' (Podman)"
@echo "run-make-no-clean Like run-make but without cleaning first, useful for iterative firmware dev"
@echo "run-make-ch552-no-clean Like run-make-ch552 but without cleaning first, useful for iterative firmware dev"
@echo "run-make-clean_fw Like run-make but cleans only firmware"
@echo "flash Build bitstream and testloadapp.bin then program them and the partition table onto the TKey SPI flash"
@echo "pull Pull down the image '$(IMAGE)' (Podman)"
@echo "build-image Build a toolchain image named '$(BUILDIMAGE)' (Podman)"
@echo " A newly built image can be used like: make IMAGE=$(BUILDIMAGE) run"
@echo "docker-run Run a shell using image '$(IMAGE)' (Docker)"
@echo "docker-build-image Build a toolchain image named '$(BUILDIMAGE)' (Docker)"
run:
podman run --rm --mount type=bind,source="`pwd`/../",target=/build -w /build -it \
@ -33,6 +35,10 @@ run-make:
podman run --rm --mount type=bind,source="`pwd`/../hw/application_fpga",target=/build -w /build -it \
$(IMAGE) make clean application_fpga.bin
run-make-ch552:
podman run --rm --mount type=bind,source="`pwd`/../hw/usb_interface/ch552_fw",target=/build -w /build -it \
$(IMAGE) make clean usb_device.bin
run-tb:
podman run --rm --mount type=bind,source="`pwd`/../hw/application_fpga",target=/build -w /build -it \
$(IMAGE) make clean_tb tb
@ -45,6 +51,10 @@ run-make-no-clean:
podman run --rm --mount type=bind,source="`pwd`/../hw/application_fpga",target=/build -w /build -it \
$(IMAGE) make application_fpga.bin
run-make-ch552-no-clean:
podman run --rm --mount type=bind,source="`pwd`/../hw/usb_interface/ch552_fw",target=/build -w /build -it \
$(IMAGE) make usb_device.bin
run-make-clean_fw:
podman run --rm --mount type=bind,source="`pwd`/../hw/application_fpga",target=/build -w /build -it \
$(IMAGE) make clean_fw application_fpga.bin
@ -54,7 +64,7 @@ flash:
--device /dev/bus/usb/$(lsusb | grep -m 1 1209:8886 | awk '{ printf "%s/%s", $2, substr($4,1,3) }') \
--mount type=bind,source="`pwd`/../hw/application_fpga",target=/build \
-w /build \
-it $(IMAGE) tillitis-iceprog /build/application_fpga.bin
-it $(IMAGE) make prog_flash
pull:

View file

@ -4,8 +4,12 @@ Descriptions of the tagged TKey releases.
## Upcoming release: Castor
Overview of changes since TK TK1-24.03 for the Castor milestone so
far.
Overview of changes since [TK1-24.03
Bellatrix](https://github.com/tillitis/tillitis-key1/releases/tag/TK1-24.03)
for the Castor milestone so far.
Main branch in the repository contains Castor design and specifically
the tag `TK1-Castor-alpha-1` reflects the upcoming release.
**Note well**: BREAKING CHANGE! Older device apps WILL NOT WORK.
@ -14,38 +18,50 @@ on the PicoRV32 CPU and the CH552 means that device apps that have not
been changed to use the protocol will not have any way to communicate
with the outside world.
### How to test TK1-Castor-alpha
To test this alpha release you will need:
- Tkey Unlocked
- TKey Programmer Board
- CH55x Reset Controller from [Blinkinlabs](https://shop-nl.blinkinlabs.com/products/ch55x-reset-controller)
Read
[here](https://github.com/tillitis/tillitis-key1/blob/main/README.md#building--flashing)
for instructions.
### General
- Split repo:
- tk1, mta1-usb-dev, mta-usb-v1 and mta1-library moves to
https://github.com/tillitis/tk1-pcba
- tk1, mta1-usb-dev, mta-usb-v1 and mta1-library moves to
<https://github.com/tillitis/tk1-pcba>
- tp1, mta1-usb-programmer, mta1-library and KiCad-RP Pico moves to
https://github.com/tillitis/tp1
- tp1, mta1-usb-programmer, mta1-library and KiCad-RP Pico moves to
<https://github.com/tillitis/tp1>
For full change log [see](https://github.com/tillitis/tillitis-key1/compare/TK1-23.03.2...coming-tag)
### FPGA
- Security Monitor memory access checks are now more complete.
- Make Security Monitor memory access checks more complete.
- Add SPI main controller mainly to access the flash chip.
- Add system reset API. Device apps can reset the system and restart
the firmware. The FPGA is not reset.
- Add system reset API. Device apps can reset the FPGA and restart
the firmware.
- Increase clock frequence to 24 MHz.
- Increase UART baudrate to 500,000.
- Fix UART baudrate counter issues noticable at higher baudrates.
- Fix missing clock cycles in timer core.
- Remove the UART runtime configuration API.
- Several clean ups and testbench changes.
- Make Verilator simulation work again.
- Several minor clean ups of design and testbench.
- Add hardware clear to send (CTS) signals for communication between
UART and CH552.
@ -54,19 +70,19 @@ For full change log [see](https://github.com/tillitis/tillitis-key1/compare/TK1-
- Make ROM non-executable in app mode.
- Remove support for access to the firmware blake2s() function from
apps.
- Remove MMIO address for access to the firmware blake2s() function
from apps.
- Automatically leave firmware mode when execution leaves ROM and
remove the now unnecessary APP\_MODE\_CTRL register.
- Add extra protection of UDS: When execution leaves ROM the first
time, UDS is hardware protected from reading, as well as already
existing UDS protection after first read and UDS being unreadable in
app mode.
- Change UDS read protection: When execution leaves ROM the first
time, UDS is hardware protected from reads. The already existing
protection that UDS is protected after the first read is also still
available.
- Introduce interrupt handler for hardware-based privilege raising for
system calls.
- Introduce interrupt handler for hardware-based privilege raising and
automatically privelege lowering for system calls.
### Firmware
@ -74,27 +90,65 @@ For full change log [see](https://github.com/tillitis/tillitis-key1/compare/TK1-
by TRNG.
- Add support for the new USB Mode Protocol to communicate with
different endpoints.
different USB endpoints in the USB controller.
- Support a filesystem on flash.
- Support a filesystem on flash: There's space for two pre-loaded
apps and four storage areas for device apps.
- Add a system call mechanism and system calls: `RESET`, `ALLOC_AREA`,
`DEALLOC_AREA`, `WRITE_DATA`, `READ_DATA`, `PRELOAD_DELETE`,
`PRELOAD_STORE`, `PRELOAD_STORE_FIN`, `PRELOAD_GET_DIGSIG`,
`STATUS`, and `GET_VIDPID`. See [firmware's
README](../hw/application_fpga/fw/README.md) for documentation.
A typical use is that app slot 0 will contain a loader app for
verified boot and app slot 1 contains the app to be verified.
- Automatically start an app in flash app slot 0 after power cycle and
when instructed to by reset intentions.
The automatically started app is trusted by the firmware by
including an app digest in the firmware ROM. This means we extend
the user's trust in the firmware to the first app, but only if it's
measured to the correct digest by the firmware. Anything else is a
hard error which halts the CPU.
- Support chaining of apps through soft resets, including support for
verifying that the next app is the expected one (exact measured
digest the previous app expected), and leaving data for the next app
to use.
- Add a system call mechanism and system calls. See [firmware's
README](../hw/application_fpga/fw/README.md) for documentation, but
its probably easier to use the the syscall wrappers in libsyscall in
[tkey-libs](https://github.com/tillitis/tkey-libs) if you're writing
in C.
- Harmonize with [tkey-libs](https://github.com/tillitis/tkey-libs).
Import tkey-libs to this repo for convenience.
### CH552
- Rewrite test firmware to work with the new leaving ROM-scenario.
Introduce a separate `testapp` for the app mode parts.
### Device apps
Introduce some device apps mostly for testing.
- `reset_test`: Test the different types of soft reset.
- `testapp`: Tests in app mode that used to live in `testfw`.
- `testloadapp`: A simple loader app for management and verification
of a second app.
- `defaultapp`: An app that immediately resets the TKey to load an app
from the client, just like earlier releases.
### CH552 firmware
- Use the new CTS signals for communication over the UART.
- Add support for two HID endpoints.
- Add support for two HID endpoints (security token and our debug
HID).
- Add protocol to communicate with the three different endpoints: CDC,
HID, debug.
- Add support for CCID endpoint.
- Add a protocol to communicate with the different endpoints: CDC,
CCID, FIDO, debug.
- Change USB frame sending from a software timer to instead be
controlled by the USB Controller Protocol.
@ -102,9 +156,15 @@ For full change log [see](https://github.com/tillitis/tillitis-key1/compare/TK1-
Note that to update the CH552 firmware you will need something like
the Blinkinlabs CH55x Reset Controller:
https://shop-nl.blinkinlabs.com/products/ch55x-reset-controller
<https://shop-nl.blinkinlabs.com/products/ch55x-reset-controller>
https://github.com/Blinkinlabs/ch55x_programmer
<https://github.com/Blinkinlabs/ch55x_programmer>
### Tooling
- Add tools to parse and generate partition tables and flash images.
- Add tool to compute a print a BLAKE2s digest, optionally as C code.
### tkey-builder
@ -127,10 +187,13 @@ https://github.com/Blinkinlabs/ch55x_programmer
- libstdc++-arm-none-eabi-newlib
- pico-sdk
TP1 is now in https://github.com/tillitis/tp1
TP1 is now in <https://github.com/tillitis/tp1>
- Remove Go compiler support.
- Introduce buildtools.sh for building upstream tools for inclusion
in the image.
### Docs
- All docs now in READMEs close to the design or code.
@ -155,6 +218,7 @@ the following digest:
```
### FPGA
- Security Monitor now prevents access to RAM outside of the physical
memory. If it detects an access outside of the RAM address space, it
will halt the CPU.
@ -166,6 +230,7 @@ the following digest:
- Complete testbenches and add 9 tests for the FPGA cores.
### Firmware
- Protect zeroisation against compiler optimisation by using
secure_wipe(), fixing a memset() that was removed during
compilation.
@ -180,31 +245,35 @@ the following digest:
- Fix warnings from splint.
### TP1
- New plastic clip o and update of BOM.
- Build TP1 firmware in CI.
### CH552
- Fixed a bug where a byte of data could in some rare circumstances be
dropped, causing a client app to hang.
- General clean-up of code, translated all comments to English.
### TK1
- New injection moulded plastic case
### tkey-builder
- Updated to version 3. Bumping Ubuntu to 23.10, Yosys to 0.36 and
nextpnr to 0.6.
- Updated to version 4. Bumping pico-sdk to 1.5.1, adding clang-tidy
and splint.
### Docs
- Fixing broken links, cleaning up docs and READMEs.
- Clarify warm boot attack mitigations and scope for Bellatrix in
threat model.
For full change log [see](https://github.com/tillitis/tillitis-key1/compare/TK1-23.03.2...TK1-24.03)
## TK1-23.03.2
This is the official release of the "Bellatrix" version of the
@ -234,8 +303,8 @@ This bug fix release contains the following changes:
- Change the firmware protocol max frame size back to 128 bytes
- Correct a bug with the reading out of UDS
## TK1-23.03
This is the official release of the "Bellatrix" version of
the Tillitis TKey device. This version is ready for general
use.
@ -249,7 +318,6 @@ shasum -a256 application_fpga.bin
f11d6b0f57c5405598206dcfea284008413391a2c51f124a2e2ae8600cb78f0b application_fpga.bin
```
### New and improved functionality
- (ALL) The TKey HW design, FW, protocol and first applications has
@ -313,10 +381,9 @@ f11d6b0f57c5405598206dcfea284008413391a2c51f124a2e2ae8600cb78f0b application_fp
- (TOOLS) There is now a version of iceprog able to write to the FPGA
bitstream to the NVCM and lock the NVCM from external access
### Bugs fixed
- No known bugs have been fixed. Numerous issues has been closed.
- No known bugs have been fixed. Numerous issues has been closed.
### Limitations
@ -324,7 +391,6 @@ f11d6b0f57c5405598206dcfea284008413391a2c51f124a2e2ae8600cb78f0b application_fp
cryptographically secure. It his however randomized every time
a TKey device is powered up.
## engineering-release-2
### New and improved functionality

View file

@ -1,149 +0,0 @@
# Toolchain setup
**NOTE:** Documentation migrated to dev.tillitis.se, this is kept for
history. This is likely to be outdated.
Here are instructions for setting up the tools required to build the
project. Tested on Ubuntu 22.10.
## General development environment
The following is intended to be a complete list of the packages that
are required for doing all of the following:
- building and developing [TKey device and client
apps](https://github.com/tillitis/tillitis-key1-apps)
- building our [QEMU machine](https://github.com/tillitis/qemu/tree/tk1)
(useful for apps dev)
- building and developing firmware and FPGA gateware (which also
requires building the toolchain below)
```
sudo apt install build-essential clang lld llvm bison flex libreadline-dev \
gawk tcl-dev libffi-dev git mercurial graphviz \
xdot pkg-config python3 libftdi-dev \
python3-dev libeigen3-dev \
libboost-dev libboost-filesystem-dev \
libboost-thread-dev libboost-program-options-dev \
libboost-iostreams-dev cmake libusb-1.0-0-dev \
ninja-build libglib2.0-dev libpixman-1-dev \
golang clang-format \
gcc-arm-none-eabi libnewlib-arm-none-eabi \
libstdc++-arm-none-eabi-newlib
```
## Device permissions
To allow sudo-less programming, you can install a udev rule that will
assign the tkey programmer, and also an unprogrammed CH552, to the
dialout group. You will also need to add your user to this group:
```
sudo cp contrib/99-tillitis.rules /etc/udev/rules.d
sudo udevadm control --reload-rules
sudo usermod -aG dialout ${USER}
```
To apply the new group, log out and then log back in.
You can check the device permissions to determine if the group was
successfully applied. First, use lsusb to find the location of the
programmer:
```
lsusb -d 1209:8886
Bus 001 Device 023: ID 1209:8886 Generic TP-1
```
Then, you can check the permissions by using the bus and device
number reported above. Note that this pair is ephemeral and may
change after every device insertion:
```
ls -l /dev/bus/usb/001/023
crw-rw---- 1 root dialout 189, 22 Feb 16 14:58 /dev/bus/usb/001/023
```
## Gateware: Yosys/Icestorm toolchain
If the LED of your TKey is steady white when you plug it, then the
firmware is running and it's already usable! If you want to develop
TKey apps, then only the above general development environment is
needed.
Compiling and installing Yosys and friends is only needed if your TKey
is not already running the required firmware and FPGA gateware, or if
you want to do development on these components.
These steps are used to build and install the
[icestorm](http://bygone.clairexen.net/icestorm/) toolchain. The
binaries are installed in `/usr/local`. Note that if you have or
install other versions of these tools locally, they could conflict
(case in point: `yosys` installed on MacOS using brew).
git clone https://github.com/YosysHQ/icestorm
cd icestorm
git checkout d20a5e9001f46262bf0cef220f1a6943946e421d
make -j$(nproc)
sudo make install
cd ..
# Custom iceprog for the RPi 2040-based programmer (will be upstreamed).
# Note: install dependencies for building tillitis-iceprog on Ubuntu:
# sudo apt install libftdi-dev libusb-1.0-0-dev
git clone -b interfaces https://github.com/tillitis/icestorm tillitis--icestorm
cd tillitis--icestorm/iceprog
make
sudo make PROGRAM_PREFIX=tillitis- install
cd ../..
git clone https://github.com/YosysHQ/yosys
cd yosys
git checkout yosys-0.26
make -j$(nproc)
sudo make install
cd ..
git clone https://github.com/YosysHQ/nextpnr
cd nextpnr
git checkout nextpnr-0.5
cmake -DARCH=ice40 -DCMAKE_INSTALL_PREFIX=/usr/local .
make -j$(nproc)
sudo make install
cd ..
References:
* http://bygone.clairexen.net/icestorm/
## Firmware: riscv toolchain
The TKey implements a [picorv32](https://github.com/YosysHQ/picorv32)
soft core CPU, which is a RISC-V microcontroller with the C
instructions and Zmmul extension, multiply without divide
(RV32ICZmmul). You can read
[more](https://www.sifive.com/blog/all-aboard-part-1-compiler-args)
about it.
The project uses the LLVM/Clang suite and version 15 or later is
required. As of writing Ubuntu 22.10 has version 15 packaged. You may
be able to get it installed on older Ubuntu and Debian using the
instructions on https://apt.llvm.org/ . There are also binary releases
here: https://github.com/llvm/llvm-project/releases
References:
* https://github.com/YosysHQ/picorv32
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`.
## CH552 USB to Serial firmware
The USB to Serial firmware runs on the CH552 microcontroller, and
provides a USB CDC profile which should work with the default drivers
on all major operating systems. MTA1-USB-V1 and TK-1 devices come
with the CH552 microcontroller pre-programmed.
Toolchain setup and build instructions for this firmware are detailed
in the
[ch552_fw directory](../hw/usb_interface/ch552_fw/README.md)

View file

@ -134,6 +134,7 @@ FIRMWARE_OBJS = \
$(P)/fw/tk1/partition_table.o \
$(P)/fw/tk1/auth_app.o \
$(P)/fw/tk1/rng.o \
$(P)/fw/tk1/reset.o \
$(P)/fw/tk1/preload_app.o \
$(P)/fw/tk1/mgmt_app.o
@ -184,6 +185,11 @@ LDFLAGS = \
-Wl,--cref,-M \
-L $(LIBDIR) -lcommon -lblake2s
QEMU_LDFLAGS = \
-T $(P)/fw/tk1/qemu_firmware.lds \
-Wl,--cref,-M \
-L $(LIBDIR) -lcommon -lblake2s
# Common libraries the firmware and testfw depend on. See
# https://github.com/tillitis/tkey-libs/
.PHONY: tkey-libs
@ -205,8 +211,8 @@ qemu_firmware.elf: CFLAGS += -DQEMU_DEBUG
qemu_firmware.elf: ASFLAGS += -DQEMU_DEBUG
qemu_firmware.elf: CFLAGS += -DQEMU_SYSCALL
qemu_firmware.elf: ASFLAGS += -DQEMU_SYSCALL
qemu_firmware.elf: firmware.elf
mv firmware.elf qemu_firmware.elf
qemu_firmware.elf: tkey-libs $(FIRMWARE_OBJS) $(P)/fw/tk1/qemu_firmware.lds
$(CC) $(CFLAGS) $(FIRMWARE_OBJS) $(QEMU_LDFLAGS) -o $@ > $(basename $@).map
# Create compile_commands.json for clangd and LSP
.PHONY: clangd
@ -249,11 +255,11 @@ bram_fw.hex:
$(ICESTORM_PATH)icebram -v -g 32 $(BRAM_FW_SIZE) > $@
firmware.hex: firmware.bin firmware_size_mismatch
python3 $(P)/tools/makehex/makehex.py $< $(BRAM_FW_SIZE) > $@
python3 $(P)/tools/makehex.py $< $(BRAM_FW_SIZE) > $@
simfirmware.hex: simfirmware.bin simfirmware_size_mismatch
python3 $(P)/tools/makehex/makehex.py $< $(BRAM_FW_SIZE) > $@
python3 $(P)/tools/makehex.py $< $(BRAM_FW_SIZE) > $@
testfw.hex: testfw.bin testfw_size_mismatch
python3 $(P)/tools/makehex/makehex.py $< $(BRAM_FW_SIZE) > $@
python3 $(P)/tools/makehex.py $< $(BRAM_FW_SIZE) > $@
.PHONY: check-binary-hashes
check-binary-hashes:
@ -325,6 +331,7 @@ fmt: $(FPGA_VERILOG_SRCS) $(SIM_VERILOG_SRCS) $(VERILATOR_VERILOG_SRCS) $(VERILO
checkfmt: $(FPGA_VERILOG_SRCS) $(SIM_VERILOG_SRCS) $(VERILATOR_VERILOG_SRCS) $(VERILOG_SRCS)
make -C fw/tk1 checkfmt
make -C fw/testfw checkfmt
make -C apps checkfmt
$(FORMAT) $(CHECK_FORMAT_FLAGS) $^ 2>&1 | \
grep "Needs formatting" && exit 1 || true
.PHONY: checkfmt
@ -389,7 +396,7 @@ synth.json: $(FPGA_VERILOG_SRCS) $(VERILOG_SRCS) $(PICORV32_SRCS) bram_fw.hex
application_fpga_par.json: synth.json $(P)/data/$(PIN_FILE)
$(NEXTPNR_PATH)nextpnr-ice40 \
-l application_fpga_par.txt \
--seed 9416596747216415304 \
--seed 18160564147838858264 \
--freq $(TARGET_FREQ) \
--ignore-loops \
--up5k \
@ -465,17 +472,23 @@ tb_application_fpga: $(SIM_VERILOG_SRCS) \
#-------------------------------------------------------------------
prog_flash: check-hardware application_fpga.bin
sudo tillitis-iceprog application_fpga.bin
tillitis-iceprog application_fpga.bin
make -C apps
(cd tools && ./load_preloaded_app.sh 0 ../apps/testloadapp.bin)
.PHONY: prog_flash
prog_flash_bs: check-hardware application_fpga.bin
tillitis-iceprog application_fpga.bin
.PHONY: prog_flash_bs
prog_flash_testfw: check-hardware application_fpga_testfw.bin
sudo tillitis-iceprog application_fpga_testfw.bin
tillitis-iceprog application_fpga_testfw.bin
.PHONY: prog_flash_testfw
check-hardware:
@sudo tillitis-iceprog -t >/dev/null 2>&1 || \
@tillitis-iceprog -t >/dev/null 2>&1 || \
{ echo "Programmer not plugged in or not accessible"; false; }
@if sudo tillitis-iceprog -t 2>&1 | grep -qi "^flash.id:\( 0x\(00\|ff\)\)\{4\}"; then \
@if tillitis-iceprog -t 2>&1 | grep -qi "^flash.id:\( 0x\(00\|ff\)\)\{4\}"; then \
echo "No USB stick in the programmer?"; false; else true; fi
.PHONY: check-hardware
@ -499,6 +512,7 @@ clean: clean_sim clean_fw clean_tb
rm -f lint_issues.txt
rm -f tools/tpt/*.hex
rm -rf tools/tpt/__pycache__
make -C apps clean
.PHONY: clean
clean_fw:
@ -547,7 +561,8 @@ help:
@echo "tb_application_fpga Build testbench simulation for the design"
@echo "lint Run lint on Verilog source files."
@echo "tb Run all testbenches"
@echo "prog_flash Program device flash with FGPA bitstream including firmware (using the RPi Pico-based programmer)."
@echo "prog_flash Program device flash with FGPA bitstream (including firmware), partition table, and testloadapp.bin (using the RPi Pico-based programmer)."
@echo "prog_flash_bs Program device flash with FGPA bitstream including firmware (using the RPi Pico-based programmer)."
@echo "prog_flash_testfw Program device flash as above, but with testfw."
@echo "clean Delete all generated files."
@echo "clean_fw Delete only generated files for firmware. Useful for fw devs."

View file

@ -1,4 +1,4 @@
# TKey hardware design
## TKey hardware design
![The Application FPGA block diagram](../../doc/images/application_fpga_block_diagram.png)
@ -16,8 +16,9 @@ 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.
The rest of the components are under `cores`, except the firmware,
which is under `fw/tk1`. They typically have their own `README.md`
file documenting them and their API in detail.
Hardware functions with APIs, assets, and input/output are memory
mapped starting at base address `0xc000_0000`. For specific offsets
@ -38,6 +39,11 @@ Rough memory map:
| Syscall | 0xe1 |
| TK1 | 0xff |
## Firmware
Firmware is kept in ROM. See the [Firmware implementation
notes](fw/README.md).
## `clk_reset_gen`
Generator for system clock and system reset.

View file

@ -1 +1 @@
c770fe25034655241d9e0152b03fcf691c548bc50d30b574a5213abc5b36fe25 application_fpga.bin
dfba361c83337c6bac2364d1fd8f115eeb7feeae5faabbdf0713c79b44bbd78d application_fpga.bin

View file

@ -0,0 +1,123 @@
P := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
LIBDIR ?= ../tkey-libs
OBJCOPY ?= llvm-objcopy
CC = clang
CFLAGS = \
-target riscv32-unknown-none-elf \
-march=rv32iczmmul \
-mabi=ilp32 \
-mcmodel=medany \
-static \
-std=gnu99 \
-Os \
-ffast-math \
-fno-common \
-fno-builtin-printf \
-fno-builtin-putchar \
-fno-builtin-memcpy \
-nostdlib \
-mno-relax \
-Wall \
-Wpedantic \
-Wno-language-extension-token \
-Werror \
-flto \
-g \
-I $(LIBDIR)/include \
-I $(LIBDIR) \
-I include \
-I ../
AS = clang
ASFLAGS = \
-target riscv32-unknown-none-elf \
-march=rv32iczmmul \
-mabi=ilp32 \
-mno-relax
LDFLAGS = \
-T $(LIBDIR)/app.lds \
-L $(LIBDIR) -lcrt0 -lcommon -lmonocypher -lblake2s
.PHONY: all
all: defaultapp.bin reset_test.bin testapp.bin testloadapp.bin
# Turn elf into bin for device
%.bin: %.elf
$(OBJCOPY) --input-target=elf32-littleriscv --output-target=binary $^ $@
chmod a-x $@
.PHONY: tkey-libs
tkey-libs:
make -C $(LIBDIR)
OBJS=syscall.o
# syscall.o: syscall.S
# $(CC) $(CFLAGS) $(DEFAULTAPP_OBJS) $(LDFLAGS) -o $@
# defaultapp
DEFAULTAPP_FMTFILES = *.[ch]
DEFAULTAPP_OBJS = \
$(P)/defaultapp/main.o
defaultapp.elf: tkey-libs $(OBJS) $(DEFAULTAPP_OBJS)
$(CC) $(CFLAGS) $(OBJS) $(DEFAULTAPP_OBJS) $(LDFLAGS) -o $@
# reset_test
RESET_TEST_FMTFILES = *.[ch]
RESET_TEST_OBJS = \
$(P)/reset_test/main.o
reset_test.elf: tkey-libs $(RESET_TEST_OBJS)
$(CC) $(CFLAGS) $(OBJS) $(RESET_TEST_OBJS) $(LDFLAGS) -o $@
# testapp
TESTAPP_OBJS = \
$(P)/testapp/main.o
testapp.elf: tkey-libs $(TESTAPP_OBJS)
$(CC) $(CFLAGS) $(OBJS) $(TESTAPP_OBJS) $(LDFLAGS) -o $@
# testloadapp
TESTLOADAPP_OBJS = \
$(P)/testloadapp/main.o
testloadapp.elf: tkey-libs $(TESTLOADAPP_OBJS)
$(CC) $(CFLAGS) $(OBJS) $(TESTLOADAPP_OBJS) $(LDFLAGS) -o $@
.PHONY: fmt
fmt:
clang-format --dry-run --ferror-limit=0 defaultapp/*.[ch]
clang-format --verbose -i defaultapp/*.[ch]
clang-format --dry-run --ferror-limit=0 reset_test/*.[ch]
clang-format --verbose -i reset_test/*.[ch]
clang-format --dry-run --ferror-limit=0 testapp/*.[ch]
clang-format --verbose -i testapp/*.[ch]
clang-format --dry-run --ferror-limit=0 testloadapp/*.[ch]
clang-format --verbose -i testloadapp/*.[ch]
.PHONY: checkfmt
checkfmt:
clang-format --dry-run --ferror-limit=0 defaultapp/*.[ch]
clang-format --dry-run --ferror-limit=0 reset_test/*.[ch]
clang-format --dry-run --ferror-limit=0 testapp/*.[ch]
clang-format --dry-run --ferror-limit=0 testloadapp/*.[ch]
.PHONY: clean
clean:
rm -f *.elf *.bin $(OBJS) $(DEFAULTAPP_OBJS) $(RESET_TEST_OBJS) \
$(TESTAPP_OBJS) $(TESTLOADAPP_OBJS)

View file

@ -0,0 +1,36 @@
# Test applications
- `defaultapp`: Immediately resets the TKey with the intention to
start an app from the client, replicating the behaviour of earlier
generations.
- `testapp`: Runs through a couple of tests that are now impossible
to do in the `testfw`.
- `reset_test`: Interactively test different reset scenarios.
- `testloadapp`: Interactively test management app things like
installing an app (hardcoded for a small happy blinking app, see
`blink.h` for the entire binary!) and to test verified boot.
## Build
```
$ make
```
will build all the .elf and .bin files on the top level.
## Use
Use `tkey-runapp` from
[tkey-devtools](https://github.com/tillitis/tkey-devtools) to load the
apps:
```
$ tkey-runapp testapp.bin
```
All of these test apps are controlled through the USB CDC, typically
by running picocom or similar terminal program, like:
```
$ picocom /dev/ttyACM1
```

View file

@ -0,0 +1,18 @@
// Copyright (C) 2025 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#include <fw/tk1/reset.h>
#include <fw/tk1/syscall_num.h>
#include <syscall.h>
#include <tkey/debug.h>
#include <tkey/led.h>
int main(void)
{
struct reset rst = {0};
led_set(LED_BLUE);
rst.type = START_CLIENT;
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
}

View file

@ -1,9 +1,11 @@
/*
* Copyright (C) 2022, 2023 - Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
// Copyright (C) 2022, 2023 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#include <fw/tk1/proto.h>
#include <fw/tk1/reset.h>
#include <fw/tk1/syscall_num.h>
#include <stdint.h>
#include <syscall.h>
#include <tkey/assert.h>
#include <tkey/debug.h>
#include <tkey/io.h>
@ -11,11 +13,6 @@
#include <tkey/lib.h>
#include <tkey/tk1_mem.h>
#include "../testapp/syscall.h"
#include "../tk1/proto.h"
#include "../tk1/resetinfo.h"
#include "../tk1/syscall_num.h"
// Converts a single hex character to its integer value
static uint8_t hex_char_to_byte(uint8_t c)
{

View file

@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: 2024 Tillitis AB <tillitis.se>
// SPDX-License-Identifier: BSD-2-Clause
#include "../tk1/picorv32/custom_ops.S"
#include "../fw/tk1/picorv32/custom_ops.S"
.section ".text"
.globl syscall

View file

@ -1,8 +1,9 @@
/*
* Copyright (C) 2022, 2023 - Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
// Copyright (C) 2022, 2023 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#include <fw/tk1/proto.h>
#include <fw/tk1/reset.h>
#include <fw/tk1/syscall_num.h>
#include <stdint.h>
#include <tkey/assert.h>
#include <tkey/io.h>
@ -10,9 +11,6 @@
#include <tkey/lib.h>
#include <tkey/tk1_mem.h>
#include "../tk1/proto.h"
#include "../tk1/resetinfo.h"
#include "../tk1/syscall_num.h"
#include "syscall.h"
#define USBMODE_PACKET_SIZE 64
@ -20,6 +18,7 @@
// clang-format off
volatile uint32_t *tk1name0 = (volatile uint32_t *)TK1_MMIO_TK1_NAME0;
volatile uint32_t *tk1name1 = (volatile uint32_t *)TK1_MMIO_TK1_NAME1;
volatile uint32_t *tk1version = (volatile uint32_t *)TK1_MMIO_TK1_VERSION;
volatile uint32_t *uds = (volatile uint32_t *)TK1_MMIO_UDS_FIRST;
volatile uint32_t *cdi = (volatile uint32_t *)TK1_MMIO_TK1_CDI_FIRST;
volatile uint32_t *udi = (volatile uint32_t *)TK1_MMIO_TK1_UDI_FIRST;
@ -98,6 +97,9 @@ int main(void)
reverseword(&name);
write(IO_CDC, (const uint8_t *)&name, 4);
puts(IO_CDC, "\r\n");
puts(IO_CDC, "Version: ");
putinthex(IO_CDC, *tk1version);
puts(IO_CDC, "\r\n");
uint32_t zeros[8];
memset(zeros, 0, 8 * 4);
@ -124,8 +126,8 @@ int main(void)
// But a syscall to get parts of UDI should be able to run
int vidpid = syscall(TK1_SYSCALL_GET_VIDPID, 0, 0, 0);
if (vidpid != 0x00010203) {
failmsg("Expected VID/PID to be 0x00010203");
if (vidpid != 0x073570c0) {
failmsg("Expected VID/PID to be 0x073570c0");
anyfailed = 1;
}
@ -145,7 +147,7 @@ int main(void)
}
puts(IO_CDC, "done.\r\n");
puts(IO_CDC, "\r\nReading from storage area...");
puts(IO_CDC, "\r\nReading data from storage area...");
uint8_t in_data[14] = {0};
if (syscall(TK1_SYSCALL_READ_DATA, 0, (uint32_t)in_data,
@ -158,6 +160,28 @@ int main(void)
}
puts(IO_CDC, "done.\r\n");
puts(IO_CDC, "\r\nErasing written data from storage area...");
if (syscall(TK1_SYSCALL_ERASE_DATA, 0, 4096, 0) != 0) {
failmsg("Failed to erase storage area");
}
puts(IO_CDC, "done.\r\n");
puts(IO_CDC, "\r\nVerify erased storage area data...");
if (syscall(TK1_SYSCALL_READ_DATA, 0, (uint32_t)in_data,
sizeof(in_data)) != 0) {
failmsg("Failed to write to storage area");
}
uint8_t check_data[sizeof(in_data)] = {0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff};
if (!memeq(in_data, check_data, sizeof(check_data))) {
failmsg("Failed to read back data from storage area");
anyfailed = 1;
}
puts(IO_CDC, "done.\r\n");
puts(IO_CDC, "\r\nDeallocating storage area...");
if (syscall(TK1_SYSCALL_DEALLOC_AREA, 0, 0, 0) != 0) {
@ -190,8 +214,8 @@ int main(void)
}
puts(IO_CDC, "\r\nTesting timer... 3");
// Matching clock at 21 MHz, giving us timer in seconds
*timer_prescaler = 21 * 1000000;
// Matching clock at 24 MHz, giving us timer in seconds
*timer_prescaler = 24 * 1000000;
// Test timer expiration after 1s
*timer = 1;

View file

@ -1,3 +1,6 @@
// Copyright (C) 2025 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#ifndef BLINK_APP_H
#define BLINK_APP_H

View file

@ -1,15 +1,19 @@
// Copyright (C) 2025 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#include <blake2s/blake2s.h>
#include <fw/tk1/reset.h>
#include <fw/tk1/syscall_num.h>
#include <monocypher/monocypher-ed25519.h>
#include <stdint.h>
#include <tkey/assert.h>
#include <tkey/debug.h>
#include <tkey/led.h>
#include <tkey/lib.h>
#include <tkey/tk1_mem.h>
#include "../testapp/syscall.h"
#include "../tk1/resetinfo.h"
#include "../tk1/syscall_num.h"
#include "blink.h"
#include "tkey/assert.h"
#include "syscall.h"
// clang-format off
static volatile uint32_t *cdi = (volatile uint32_t *) TK1_MMIO_TK1_CDI_FIRST;
@ -150,7 +154,11 @@ void reset_from_client(void)
rst.type = START_CLIENT;
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
// Give the next in chain something to look at.
memset(rst.next_app_data, 17, sizeof(rst.next_app_data));
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, sizeof(rst.next_app_data),
0);
}
int main(void)
@ -161,6 +169,8 @@ int main(void)
uint8_t available;
uint8_t in = 0;
led_set(LED_BLUE);
// Generate a key pair from CDI
crypto_ed25519_key_pair(secret_key, pubkey, (uint8_t *)cdi);

View file

@ -101,7 +101,7 @@ module tk1 #(
localparam TK1_NAME0 = 32'h746B3120; // "tk1 "
localparam TK1_NAME1 = 32'h6d6b6466; // "mkdf"
localparam TK1_VERSION = 32'h00000005;
localparam TK1_VERSION = 32'h00000006;
localparam FW_RAM_FIRST = 32'hd0000000;
localparam FW_RAM_LAST = 32'hd0000fff; // 4 KB

View file

@ -1,2 +1,2 @@
00010203
073570c0
04050607

View file

@ -1 +1 @@
e72557c38bee1e16114f550b16fc04412bfba09274d7b1fe971ab6b2ef4f06421d976276175ea4df3b7d590599eb052b85a812b689d728be5031ae412b2f8d24 firmware.bin
4be2767d5ddd30b5422f4b58075365cb6d988259e88ffa14d6d243560b289f54eaf0c351e9d744cff8ec3a18b1830f3925a86f36bd2096c12eccce25ed44993c firmware.bin

View file

@ -65,10 +65,11 @@ The different endpoints:
| *Name* | *Value* | *Comment* |
|--------|---------|----------------------------------------------------------------------|
| DEBUG | 0x20 | A USB HID special debug pipe. Useful for debug prints. |
| CH552 | 0x10 | USB controller control |
| CDC | 0x40 | USB CDC-ACM, a serial port on the client. |
| FIDO | 0x80 | A USB FIDO security token device, useful for FIDO-type applications. |
| CH552 | 0x04 | USB controller control |
| CDC | 0x08 | USB CDC-ACM, a serial port on the client. |
| FIDO | 0x10 | A USB FIDO security token device, useful for FIDO-type applications. |
| CCID | 0x20 | USB CCID, a port for emulating a smart card |
| DEBUG | 0x40 | A USB HID special debug pipe. Useful for debug prints. |
You can turn on and off different endpoints dynamically by sending
commands to the `CH552` control endpoint. When the TKey starts only
@ -162,6 +163,7 @@ stateDiagram-v2
S0 --> S1
S0 --> S4: Default
S0 --> S3
S0 --> SE: Unknown reset type
S1 --> S1: Other commands
S1 --> S2: LOAD_APP
@ -190,8 +192,8 @@ States:
- *LOADING*: Expecting application data from client. Allows only the
command `LOAD_APP_DATA` to continue loading the device app.
- *LOAD_FLASH*: Loading an app from flash. Allows no commands.
- *LOAD_FLASH_MGMT*: Loading and verifyiing a device app from flash.
Allows no commands.
- *LOAD_FLASH_MGMT*: Loading an app from flash and registering it as a
prospective managment app. Allows no commands.
- *START*: Computes CDI. Possibly verifies app. Starts the
application. Does not return to firmware. Allows no commands.
- *FAIL* - Halts CPU. Allows no commands.
@ -206,6 +208,7 @@ Allowed data in state *INITIAL*:
| `FLASH1_VER` | *LOAD_FLASH* |
| `CLIENT` | *WAITCOMMAND* |
| `CLIENT_VER` | *WAITCOMMAND* |
| unknown | *FAIL* |
I/O in state *LOAD_FLASH*:
@ -219,15 +222,16 @@ I/O in state *LOAD_FLASH_MGMT*:
|--------------------|--------------|
| Last app data read | *START* |
Commands in state `waitcommand`:
Commands in state *WAITCOMMAND*:
| *command* | *next state* |
|-----------------------|--------------|
| `FW_CMD_NAME_VERSION` | unchanged |
| `FW_CMD_GET_UDI` | unchanged |
| `FW_CMD_LOAD_APP` | *LOADING* |
| *command* | *next state* |
|-----------------------|--------------------------------------------|
| `FW_CMD_NAME_VERSION` | unchanged |
| `FW_CMD_GET_UDI` | unchanged |
| `FW_CMD_LOAD_APP` | *LOADING* or unchanged on invalid app size |
| | |
Commands in state `loading`:
Commands in state *LOADING*:
| *command* | *next state* |
|------------------------|------------------------------------|
@ -244,17 +248,23 @@ Plain text explanation of the states:
- *INITIAL*: Start here. Check the `FW_RAM` for the `resetinfo` type
for what to do next.
For all types which begins with `FLASH_*`, set next state to
*LOAD_FLASH*, otherwise set next state to *WAITCOMMAND*.
For type `FLASH0` transition to *LOAD_FLASH_MGMT* because the app in
slot 0 is considered a special management app. For all other types
beginning with `FLASH*` transition to *LOAD_FLASH* to load an
ordinary app from flash.
For type `CLIENT*` transitionto *WAITCOMMAND* to expect a device app
from the client.
If type is unknown, error out.
- *LOAD_FLASH*: Load device app from flash into RAM, app slot taken
from context. Compute a BLAKE2s digest over the entire app.
Transition to *START*.
- *LOAD_FLASH_MGMT*: Load device app from flash into RAM, app slot
alway 0. Compute a BLAKE2s digest over the entire app. Register the
app as a prospective management app if it later goes through
verification. Transition to *START*.
always 0. Compute a BLAKE2s digest over the entire app. Register the
app as a prospective management app. Transition to *START*.
- *WAITCOMMAND*: Wait for commands from the client. Transition to
*LOADING* on `LOAD_APP` command, which also sets the size of the
@ -265,7 +275,8 @@ Plain text explanation of the states:
- *START*: Compute the Compound Device Identifier (CDI). If we have a
registered verification digest, verify that the app we are about to
start is indeed the correct app.
start is indeed the correct app. This also means that a prospective
management app is now verified.
Clean up firmware data structures, enable the system calls, and
start the app, which ends the firmware state machine. Hardware
@ -288,29 +299,38 @@ state.
### Golden path from start to default app
Firmware loads the device application at the start of RAM
Firmware will load the device application at the start of RAM
(`0x4000_0000`) from either flash or from the client through the UART.
Firmware uses a part of the FW\_RAM for its own stack.
Firmware is using a part of the FW\_RAM for its own stack.
When reset is released, the CPU starts executing the firmware. It
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.
except the part reserved for the resetinfo area, 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 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).
1. Check the special resetinfo area in FW\_RAM for reset type. Type
Firmware then proceeds to:
1. Read the partition table from flash and store in FW\_RAM.
2. Reset the CH552 USB controller to a known state, only allowing the
CDC USB endpoint and the internal command channel between the CPU
and the CH552.
3. Check the special resetinfo area in FW\_RAM for reset type. Type
zero means default behaviour, load from flash app slot 0, expecting
the app there to have a specific hardcoded BLAKE2s digest.
2. Load app data from flash slot 0 into RAM.
4. Load app data from flash slot 0 into RAM.
3. Compute a BLAKE2s digest of the loaded app.
5. Compute a BLAKE2s digest of the loaded app.
4. Compare the computed digest against the allowed app digest
6. Compare the computed digest against the allowed app digest
hardcoded in the firmware. If it's not equal, halt CPU.
7. [Start the device app](#start-the-device-app).
@ -371,9 +391,9 @@ Such a verified boot loader app:
- Can be specifically trusted by firmware to be able to do filesystem
management to be able to update an app slot on flash. Add the app's
digest to `allowed_app_digest` in `mgmt_app.c` to allow it to allow
it to use `PRELOAD_DELETE`, `PRELOAD_STORE`, and
`PRELOAD_STORE_FIN`.
digest to `allowed_app_digest` in `mgmt_app.c` to allow it to use
`PRELOAD_DELETE`, `PRELOAD_STORE`, `PRELOAD_STORE_FIN`, and
`PRELOAD_GET_DIGSIG`.
It works like this:
@ -413,7 +433,7 @@ The loader shares the secret with the next app by putting it in the
part of `resetinfo` that is reserved for inter-app communication.
The next app can now use the secret as a seed for it's own key
material. Depending on the app's behaviour and the numer of keys it
material. Depending on the app's behaviour and the number of keys it
needs it can derive more keys, for instance by having nonces stored on
its flash area and doing:
@ -533,8 +553,9 @@ struct reset {
};
struct reset rst;
uint32_t len; // Length of data in next_app_data.
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, len, 0);
```
Resets the TKey. Does not return.
@ -543,7 +564,7 @@ You can pass data to the firmware about the reset type `type` and a
digest that the next app must have. You can also leave some data to
the next app in the chain in `next_app_data`.
The types of the reset are defined in `resetinfo.h`:
The types of reset are defined in `reset.h`:
| *Name* | *Comment* |
|--------------------|------------------------------------------------|
@ -577,12 +598,15 @@ success.
uint32_t offset = 0;
uint8_t buf[17];
TK1_SYSCALL_WRITE_DATA, offset, (uint32_t)buf, sizeof(buf))
syscall(TK1_SYSCALL_WRITE_DATA, offset, (uint32_t)buf, sizeof(buf))
```
Write data in `buf` to the app's flash area at byte `offset` within
the area. Returns 0 on success.
At most 4096 bytes can be written at once and `offset` must be a
multiple of 4096 bytes.
#### `READ_DATA`
```
@ -594,6 +618,20 @@ syscall(TK1_SYSCALL_READ_DATA, offset, (uint32_t)buf, sizeof(buf);
Read into `buf` at byte `offset` from the app's flash area.
#### `ERASE_DATA`
```
uint32_t offset = 0;
uint32_t size = 4096;
syscall(TK1_SYSCALL_ERASE_DATA, offset, size, 0);
```
Erase `size` bytes from `offset` within the area. Returns 0 on
success.
Both `size` and `offset` must be a multiple of 4096 bytes.
#### `PRELOAD_DELETE`
```
@ -615,9 +653,14 @@ syscall(TK1_SYSCALL_PRELOAD_STORE, offset, (uint32_t)appbinary,
```
Store an app, or possible just a block of an app, from the `appbinary`
buffer in flash slot 1 at byte `offset` If you can't find your entire
app in the buffer, call `PRELOAD_STORE` many times as you receive the
binary from the client. Returns 0 on success.
buffer in flash slot 1 at byte `offset`.
If you can't fit your entire app in the buffer, call `PRELOAD_STORE`
many times as you receive the binary from the client. Returns 0 on
success.
At most 4096 bytes can be written at once and `offset` must be a
multiple of 4096 bytes.
Only available for the verified management app.
@ -636,7 +679,8 @@ Finalize storing of an app where the complete binary size is
`app_size` in flash slot 1. Returns 0 on success. Only available for
the verified management app.
Compute the `app_digest` with BLAKE2s over the entire binary.
Compute a BLAKE2s hash digest over the entire binary. Pass the result
in `app_digest`.
Sign `app_digest` with your Ed25519 private key and pass the
resulting signature in `app_signature`.
@ -693,6 +737,9 @@ functions. Note that these functions are only usable in QEMU and that
you might need to `make clean` before building, if you have already
built before.
To build a flash image file suitable for use with qemu, use the
`tools/tkeyimage` program. See its documentation.
If you want debug prints to show up on the special TKey HID debug
endpoint instead, define `-DTKEY_DEBUG`. This might mean you can't fit
the firmware in the ROM space available, however. You will get a
@ -721,7 +768,27 @@ initiated before starting for the first time. You need a [TKey
Programmer Board](https://shop.tillitis.se/products/tkey-dev-kit) for
this part.
1. Choose your pre-loaded app. You /must/ have a pre-loaded app, for
If you just want to build and flash the bitstream, the testloadapp in
app slot 0, and the partition table copies in one go, place the TKey
Unlocked in the TP1, then:
Using Podman, from the top level directory:
```
cd contrib
make flash
```
Using native tools:
```
cd hw/application_fpga
make prog_flash
```
If you want to prepare the filesystem yourself:
1. Choose your pre-loaded app. You *must* have a pre-loaded app, for
example `testloadapp`. Build it with the OCI image we use. The
binary needs to produce the BLAKE2s digest in `allowed_app_digest`
`tk1/mgmt_app.c`.
@ -741,10 +808,10 @@ If you want to use a different pre-loaded app you have to
2. Update the `allowed_app_digest` in `tk1/mgmt_app.c`.
3. Create a new `default_partition.bin` using the
`tools/partition_table`, typically:
`tools/tkeyimage`, typically:
```
$ partition_table -app0 path/to/your/app.bin -o default_partition.bin
$ tkeyimage -app0 path/to/your/app.bin -o default_partition.bin
```
4. Flash the filesystem image:
@ -768,19 +835,5 @@ ordinary `application_fpga/Makefile` to be able to fit in ROM.
### Test apps
There are a couple of test apps. All of them are controlled through
the USB CDC, typically by running picocom or similar terminal program,
like:
There are a couple of test apps, see `../apps`.
```
$ picocom /dev/ttyACM1
```
or similar.
- `fw/testapp`: Runs through a couple of tests that are now impossible
to do in the `testfw`.
- `fw/reset_test`: Interactively test different reset scenarios.
- `fw/testloadapp`: Interactively test management app things like
installing an app (hardcoded for a small happy blinking app, see
`blink.h` for the entire binary!) and to test verified boot.

View file

@ -1,75 +0,0 @@
P := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
LIBDIR ?= ../../tkey-libs
OBJCOPY ?= llvm-objcopy
CC = clang
CFLAGS = \
-target riscv32-unknown-none-elf \
-march=rv32iczmmul \
-mabi=ilp32 \
-mcmodel=medany \
-static \
-std=gnu99 \
-O2 \
-ffast-math \
-fno-common \
-fno-builtin-printf \
-fno-builtin-putchar \
-fno-builtin-memcpy \
-nostdlib \
-mno-relax \
-Wall \
-Wpedantic \
-Wno-language-extension-token \
-Werror \
-flto \
-g \
-I $(LIBDIR)/include \
-I $(LIBDIR) \
-DTKEY_DEBUG
AS = clang
ASFLAGS = \
-target riscv32-unknown-none-elf \
-march=rv32iczmmul \
-mabi=ilp32 \
-mno-relax
LDFLAGS = \
-T $(P)/app.lds \
-L $(LIBDIR) -lcommon
.PHONY: all
all: reset_test.bin
# Turn elf into bin for device
%.bin: %.elf
$(OBJCOPY) --input-target=elf32-littleriscv --output-target=binary $^ $@
chmod a-x $@
.PHONY: tkey-libs
tkey-libs:
make -C $(LIBDIR)
RESET_TEST_FMTFILES = *.[ch]
RESET_TEST_OBJS = \
$(P)/main.o \
$(P)/crt0.o \
$(P)/syscall.o
reset_test.elf: tkey-libs $(RESET_TEST_OBJS)
$(CC) $(CFLAGS) $(RESET_TEST_OBJS) $(LDFLAGS) -o $@
.PHONY: fmt
fmt:
clang-format --dry-run --ferror-limit=0 $(RESET_TEST_FMTFILES)
clang-format --verbose -i $(RESET_TEST_FMTFILES)
.PHONY: checkfmt
checkfmt:
clang-format --dry-run --ferror-limit=0 --Werror $(RESET_TEST_FMTFILES)
.PHONY: clean
clean:
rm -f reset_test.bin reset_test.elf $(RESET_TEST_OBJS)

View file

@ -1,64 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 Tillitis AB <tillitis.se>
* SPDX-License-Identifier: BSD-2-Clause
*/
OUTPUT_ARCH( "riscv" )
ENTRY(_start)
MEMORY
{
RAM (rwx) : ORIGIN = 0x40000000, LENGTH = 0x20000 /* 128 KB */
}
SECTIONS
{
.text.init :
{
*(.text.init)
} >RAM
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
*(.srodata) /* .rodata sections (constants, strings, etc.) */
*(.srodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(4);
_etext = .;
_sidata = _etext;
} >RAM
.data : AT (_etext)
{
. = ALIGN(4);
_sdata = .;
. = ALIGN(4);
*(.data) /* .data sections */
*(.data*) /* .data* sections */
*(.sdata) /* .sdata sections */
*(.sdata*) /* .sdata* sections */
. = ALIGN(4);
_edata = .;
} >RAM
/* Uninitialized data section */
.bss :
{
. = ALIGN(4);
_sbss = .;
*(.bss)
*(.bss*)
*(.sbss)
*(.sbss*)
*(COMMON)
. = ALIGN(4);
_ebss = .;
} >RAM
/* libcrt0/crt0.S inits stack to start just below end of RAM */
}

View file

@ -1,53 +0,0 @@
// SPDX-FileCopyrightText: 2022 Tillitis AB <tillitis.se>
// SPDX-License-Identifier: BSD-2-Clause
.section ".text.init"
.global _start
_start:
li x1, 0
li x2, 0
li x3, 0
li x4, 0
li x5, 0
li x6, 0
li x7, 0
li x8, 0
li x9, 0
li x10,0
li x11,0
li x12,0
li x13,0
li x14,0
li x15,0
li x16,0
li x17,0
li x18,0
li x19,0
li x20,0
li x21,0
li x22,0
li x23,0
li x24,0
li x25,0
li x26,0
li x27,0
li x28,0
li x29,0
li x30,0
li x31,0
/* init stack below 0x40020000 (TK1_RAM_BASE+TK1_RAM_SIZE) */
li sp, 0x4001fff0
/* zero-init bss section */
la a0, _sbss
la a1, _ebss
bge a0, a1, end_init_bss
loop_init_bss:
sw zero, 0(a0)
addi a0, a0, 4
blt a0, a1, loop_init_bss
end_init_bss:
call main

View file

@ -1,74 +0,0 @@
P := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
LIBDIR ?= ../../tkey-libs
OBJCOPY ?= llvm-objcopy
CC = clang
CFLAGS = \
-target riscv32-unknown-none-elf \
-march=rv32iczmmul \
-mabi=ilp32 \
-mcmodel=medany \
-static \
-std=gnu99 \
-O2 \
-ffast-math \
-fno-common \
-fno-builtin-printf \
-fno-builtin-putchar \
-fno-builtin-memcpy \
-nostdlib \
-mno-relax \
-Wall \
-Wpedantic \
-Wno-language-extension-token \
-Werror \
-flto \
-g \
-I $(LIBDIR)/include \
-I $(LIBDIR)
AS = clang
ASFLAGS = \
-target riscv32-unknown-none-elf \
-march=rv32iczmmul \
-mabi=ilp32 \
-mno-relax
LDFLAGS = \
-T $(P)/app.lds \
-L $(LIBDIR) -lcommon
.PHONY: all
all: testapp.bin
# Turn elf into bin for device
%.bin: %.elf
$(OBJCOPY) --input-target=elf32-littleriscv --output-target=binary $^ $@
chmod a-x $@
.PHONY: tkey-libs
tkey-libs:
make -C $(LIBDIR)
TESTAPP_FMTFILES = *.[ch]
TESTAPP_OBJS = \
$(P)/main.o \
$(P)/crt0.o \
$(P)/syscall.o
testapp.elf: tkey-libs $(TESTAPP_OBJS)
$(CC) $(CFLAGS) $(TESTAPP_OBJS) $(LDFLAGS) -o $@
.PHONY: fmt
fmt:
clang-format --dry-run --ferror-limit=0 $(TESTAPP_FMTFILES)
clang-format --verbose -i $(TESTAPP_FMTFILES)
.PHONY: checkfmt
checkfmt:
clang-format --dry-run --ferror-limit=0 --Werror $(TESTAPP_FMTFILES)
.PHONY: clean
clean:
rm -f testapp.bin testapp.elf $(TESTAPP_OBJS)

View file

@ -1,64 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 Tillitis AB <tillitis.se>
* SPDX-License-Identifier: BSD-2-Clause
*/
OUTPUT_ARCH( "riscv" )
ENTRY(_start)
MEMORY
{
RAM (rwx) : ORIGIN = 0x40000000, LENGTH = 0x20000 /* 128 KB */
}
SECTIONS
{
.text.init :
{
*(.text.init)
} >RAM
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
*(.srodata) /* .rodata sections (constants, strings, etc.) */
*(.srodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(4);
_etext = .;
_sidata = _etext;
} >RAM
.data : AT (_etext)
{
. = ALIGN(4);
_sdata = .;
. = ALIGN(4);
*(.data) /* .data sections */
*(.data*) /* .data* sections */
*(.sdata) /* .sdata sections */
*(.sdata*) /* .sdata* sections */
. = ALIGN(4);
_edata = .;
} >RAM
/* Uninitialized data section */
.bss :
{
. = ALIGN(4);
_sbss = .;
*(.bss)
*(.bss*)
*(.sbss)
*(.sbss*)
*(COMMON)
. = ALIGN(4);
_ebss = .;
} >RAM
/* libcrt0/crt0.S inits stack to start just below end of RAM */
}

View file

@ -1,53 +0,0 @@
// SPDX-FileCopyrightText: 2022 Tillitis AB <tillitis.se>
// SPDX-License-Identifier: BSD-2-Clause
.section ".text.init"
.global _start
_start:
li x1, 0
li x2, 0
li x3, 0
li x4, 0
li x5, 0
li x6, 0
li x7, 0
li x8, 0
li x9, 0
li x10,0
li x11,0
li x12,0
li x13,0
li x14,0
li x15,0
li x16,0
li x17,0
li x18,0
li x19,0
li x20,0
li x21,0
li x22,0
li x23,0
li x24,0
li x25,0
li x26,0
li x27,0
li x28,0
li x29,0
li x30,0
li x31,0
/* init stack below 0x40020000 (TK1_RAM_BASE+TK1_RAM_SIZE) */
li sp, 0x4001fff0
/* zero-init bss section */
la a0, _sbss
la a1, _ebss
bge a0, a1, end_init_bss
loop_init_bss:
sw zero, 0(a0)
addi a0, a0, 4
blt a0, a1, loop_init_bss
end_init_bss:
call main

View file

@ -1,85 +0,0 @@
// SPDX-FileCopyrightText: 2024 Tillitis AB <tillitis.se>
// SPDX-License-Identifier: BSD-2-Clause
#include "../tk1/picorv32/custom_ops.S"
.section ".text"
.globl syscall
syscall:
// Save registers to stack
addi sp, sp, -32*4
sw x0, 0*4(sp)
sw x1, 1*4(sp)
// x2 (sp) is assumed to be preserved by the interrupt handler.
sw x3, 3*4(sp)
sw x4, 4*4(sp)
sw x5, 5*4(sp)
sw x6, 6*4(sp)
sw x7, 7*4(sp)
sw x8, 8*4(sp)
sw x9, 9*4(sp)
// x10 (a0) will contain syscall return value. And should not be saved.
sw x11, 11*4(sp)
sw x12, 12*4(sp)
sw x13, 13*4(sp)
sw x14, 14*4(sp)
sw x15, 15*4(sp)
sw x16, 16*4(sp)
sw x17, 17*4(sp)
sw x18, 18*4(sp)
sw x19, 19*4(sp)
sw x20, 20*4(sp)
sw x21, 21*4(sp)
sw x22, 22*4(sp)
sw x23, 23*4(sp)
sw x24, 24*4(sp)
sw x25, 25*4(sp)
sw x26, 26*4(sp)
sw x27, 27*4(sp)
sw x28, 28*4(sp)
sw x29, 29*4(sp)
sw x30, 30*4(sp)
sw x31, 31*4(sp)
// Trigger syscall interrupt
li t1, 0xe1000000 // Syscall interrupt trigger address
sw zero, 0(t1) // Trigger interrupt
// Restore registers from stack
lw x0, 0*4(sp)
lw x1, 1*4(sp)
// x2 (sp) is assumed to be preserved by the interrupt handler.
lw x3, 3*4(sp)
lw x4, 4*4(sp)
lw x5, 5*4(sp)
lw x6, 6*4(sp)
lw x7, 7*4(sp)
lw x8, 8*4(sp)
lw x9, 9*4(sp)
// x10 (a0) contains syscall return value. And should not be destroyed.
lw x11, 11*4(sp)
lw x12, 12*4(sp)
lw x13, 13*4(sp)
lw x14, 14*4(sp)
lw x15, 15*4(sp)
lw x16, 16*4(sp)
lw x17, 17*4(sp)
lw x18, 18*4(sp)
lw x19, 19*4(sp)
lw x20, 20*4(sp)
lw x21, 21*4(sp)
lw x22, 22*4(sp)
lw x23, 23*4(sp)
lw x24, 24*4(sp)
lw x25, 25*4(sp)
lw x26, 26*4(sp)
lw x27, 27*4(sp)
lw x28, 28*4(sp)
lw x29, 29*4(sp)
lw x30, 30*4(sp)
lw x31, 31*4(sp)
addi sp, sp, 32*4
ret

View file

@ -1,7 +1,5 @@
/*
* Copyright (C) 2022, 2023 - Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
// Copyright (C) 2022, 2023 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#include <stddef.h>
#include <stdint.h>

View file

@ -1,7 +1,5 @@
/*
* Copyright (C) 2022, 2023 - Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
// Copyright (C) 2022, 2023 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
.section ".text.init"
.globl _start
@ -38,7 +36,7 @@ _start:
li x30,0
li x31,0
/* Clear all RAM */
// Clear all RAM
li a0, 0x40000000 // TK1_RAM_BASE
li a1, 0x40020000 // TK1_RAM_BASE + TK1_RAM_SIZE
clear:
@ -46,9 +44,9 @@ clear:
addi a0, a0, 4
blt a0, a1, clear
/*
* For testfw we init stack at top of RAM
*/
// NOTE WELL
// For testfw we init stack at top of RAM
//
li sp, 0x40020000 // TK1_RAM_BASE + TK1_RAM_SIZE
call main

View file

@ -1,73 +0,0 @@
P := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
LIBDIR ?= ../../tkey-libs
OBJCOPY ?= llvm-objcopy
CC = clang
CFLAGS = \
-target riscv32-unknown-none-elf \
-march=rv32iczmmul \
-mabi=ilp32 \
-mcmodel=medany \
-static \
-std=gnu99 \
-Os \
-ffast-math \
-fno-common \
-fno-builtin-printf \
-fno-builtin-putchar \
-fno-builtin-memcpy \
-nostdlib \
-mno-relax \
-Wall \
-Wpedantic \
-Wno-language-extension-token \
-Werror \
-flto \
-g \
-I $(LIBDIR)/include \
-I $(LIBDIR)
AS = clang
ASFLAGS = \
-target riscv32-unknown-none-elf \
-march=rv32iczmmul \
-mabi=ilp32 \
-mno-relax
LDFLAGS = \
-T $(LIBDIR)/app.lds \
-L $(LIBDIR) -lcrt0 -lcommon -lmonocypher -lblake2s
.PHONY: all
all: testloadapp.bin
# Turn elf into bin for device
%.bin: %.elf
$(OBJCOPY) --input-target=elf32-littleriscv --output-target=binary $^ $@
chmod a-x $@
.PHONY: tkey-libs
tkey-libs:
make -C $(LIBDIR)
TESTLOADAPP_FMTFILES = *.[ch]
TESTLOADAPP_OBJS = \
$(P)/main.o \
../testapp/syscall.o \
testloadapp.elf: tkey-libs $(TESTLOADAPP_OBJS)
$(CC) $(CFLAGS) $(TESTLOADAPP_OBJS) $(LDFLAGS) -o $@
.PHONY: fmt
fmt:
clang-format --dry-run --ferror-limit=0 $(TESTLOADAPP_FMTFILES)
clang-format --verbose -i $(TESTLOADAPP_FMTFILES)
.PHONY: checkfmt
checkfmt:
clang-format --dry-run --ferror-limit=0 --Werror $(TESTLOADAPP_FMTFILES)
.PHONY: clean
clean:
rm -f testloadapp.bin testloadapp.elf $(TESTLOADAPP_OBJS)

View file

@ -14,8 +14,8 @@
static volatile uint32_t *cdi = (volatile uint32_t *)TK1_MMIO_TK1_CDI_FIRST;
/* Calculates the authentication digest based on a supplied nonce and the CDI.
* Requires that the CDI is already calculated and stored */
// Calculates the authentication digest based on a supplied nonce and
// the CDI. Requires that the CDI is already calculated and stored
static void calculate_auth_digest(uint8_t *nonce, uint8_t *auth_digest)
{
assert(nonce != NULL);
@ -31,7 +31,7 @@ static void calculate_auth_digest(uint8_t *nonce, uint8_t *auth_digest)
blake2s_final(&ctx, auth_digest);
}
/* Generates a 16 byte nonce */
// Generates a 16 byte nonce
static void generate_nonce(uint32_t *nonce)
{
assert(nonce != NULL);
@ -41,8 +41,9 @@ static void generate_nonce(uint32_t *nonce)
}
return;
}
/* Returns the authentication digest and random nonce. Requires that the CDI is
* already calculated and stored */
// Returns the authentication digest and random nonce. Requires that
// the CDI is already calculated and stored
void auth_app_create(struct auth_metadata *auth_table)
{
assert(auth_table != NULL);

View file

@ -10,36 +10,12 @@
#include "flash.h"
#include "spi.h"
// clang-format off
static volatile uint32_t *timer = (volatile uint32_t *)TK1_MMIO_TIMER_TIMER;
static volatile uint32_t *timer_prescaler = (volatile uint32_t *)TK1_MMIO_TIMER_PRESCALER;
static volatile uint32_t *timer_status = (volatile uint32_t *)TK1_MMIO_TIMER_STATUS;
static volatile uint32_t *timer_ctrl = (volatile uint32_t *)TK1_MMIO_TIMER_CTRL;
// clang-format on
// CPU clock frequency in Hz
#define CPUFREQ 21000000
#define PAGE_SIZE 256
static bool flash_is_busy(void);
static void flash_wait_busy(void);
static void flash_write_enable(void);
static void delay(int timeout_ms)
{
// Tick once every centisecond
*timer_prescaler = CPUFREQ / 100;
*timer = timeout_ms / 10;
*timer_ctrl |= (1 << TK1_MMIO_TIMER_CTRL_START_BIT);
while (*timer_status != 0) {
}
// Stop timer
*timer_ctrl |= (1 << TK1_MMIO_TIMER_CTRL_STOP_BIT);
}
static bool flash_is_busy(void)
{
uint8_t tx_buf = READ_STATUS_REG_1;
@ -58,9 +34,8 @@ static bool flash_is_busy(void)
// Blocking until !busy
static void flash_wait_busy(void)
{
while (flash_is_busy()) {
delay(10);
}
while (flash_is_busy())
;
}
static void flash_write_enable(void)
@ -201,7 +176,11 @@ int flash_write_data(uint32_t address, uint8_t *data, size_t size)
return -1;
}
if (size <= 0 || size > 4096) {
if (size <= 0) {
return -1;
}
if (address % 256 != 0) {
return -1;
}
@ -209,6 +188,12 @@ int flash_write_data(uint32_t address, uint8_t *data, size_t size)
uint8_t *p_data = data;
size_t n_bytes = 0;
// Page Program allows 1-256 bytes of a page to be written. A page is
// 256 bytes. Behavior when writing past the end of a page is device
// specific.
//
// We set the address LSByte to 0 and only write 256 bytes or less in
// each transfer.
uint8_t tx_buf[4] = {
PAGE_PROGRAM, /* tx_buf[0] */
(address >> ADDR_BYTE_3_BIT) & 0xFF, /* tx_buf[1] */

View file

@ -1,7 +1,5 @@
/*
* Copyright (C) 2022, 2023 - Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
// Copyright (C) 2022, 2023 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#include <blake2s/blake2s.h>
#include <stdbool.h>
@ -17,7 +15,7 @@
#include "partition_table.h"
#include "preload_app.h"
#include "proto.h"
#include "resetinfo.h"
#include "reset.h"
#include "state.h"
#include "syscall_enable.h"
@ -38,7 +36,7 @@ static volatile uint32_t *timer_status = (volatile uint32_t *)TK1_MMIO_TIMER
static volatile uint32_t *timer_ctrl = (volatile uint32_t *)TK1_MMIO_TIMER_CTRL;
static volatile uint32_t *ram_addr_rand = (volatile uint32_t *)TK1_MMIO_TK1_RAM_ADDR_RAND;
static volatile uint32_t *ram_data_rand = (volatile uint32_t *)TK1_MMIO_TK1_RAM_DATA_RAND;
static volatile struct reset *resetinfo = (volatile struct reset *)TK1_MMIO_RESETINFO_BASE;
static volatile struct reset *resetinfo = (volatile struct reset *)TK1_MMIO_RESETINFO_BASE;
// clang-format on
struct partition_table_storage part_table_storage;
@ -252,6 +250,8 @@ static enum state initial_commands(const struct frame_header *hdr,
ctx->left = *app_size;
led_set(LED_BLACK);
state = FW_STATE_LOADING;
break;
}
@ -444,7 +444,19 @@ static enum state start_where(struct context *ctx)
{
assert(ctx != NULL);
// Where do we start? Read resetinfo 'startfrom'
debug_puts("resetinfo->type: ");
debug_putinthex(resetinfo->type);
debug_lf();
debug_puts(" ->app_digest: \n");
debug_hexdump((void *)resetinfo->app_digest, RESET_DIGEST_SIZE);
debug_lf();
debug_puts(" ->next_app_data: \n");
debug_hexdump((void *)resetinfo->next_app_data, RESET_DATA_SIZE);
debug_lf();
// Where do we start?
switch (resetinfo->type) {
case START_DEFAULT:
// fallthrough
@ -496,8 +508,6 @@ int main(void)
uint8_t cmd[CMDSIZE] = {0};
enum state state = FW_STATE_INITIAL;
led_set(LED_BLUE);
print_hw_version();
/*@-mustfreeonly@*/
@ -511,10 +521,17 @@ int main(void)
scramble_ram();
if (part_table_read(&part_table_storage) != 0) {
// Couldn't read or create partition table
// Couldn't read partition table
debug_puts("Couldn't read partition table\n");
assert(1 == 2);
}
// Reset the USB controller to only enable the USB CDC
// endpoint and the internal command channel.
config_endpoints(IO_CDC | IO_CH552);
led_set(LED_WHITE);
#if defined(SIMULATION)
run(&ctx);
#endif
@ -567,6 +584,7 @@ int main(void)
if (mgmt_app_init(ctx.digest) != 0) {
state = FW_STATE_FAIL;
break;
}
state = FW_STATE_START;
@ -604,7 +622,7 @@ int main(void)
}
/*@ -compdestroy @*/
/* We don't care about memory leaks here. */
// We don't care about memory leaks here.
return (int)0xcafebabe;
}

View file

@ -11,11 +11,14 @@
// Lock down what app can start from flash slot 0.
//
// To update this, compute the BLAKE2s digest of the app.bin
// clang-format off
static const uint8_t allowed_app_digest[32] = {
0xb6, 0x86, 0x1b, 0x26, 0xef, 0x69, 0x77, 0x12, 0xed, 0x6c, 0xca,
0xe8, 0x35, 0xb4, 0x5c, 0x01, 0x07, 0x71, 0xab, 0xce, 0x3f, 0x30,
0x79, 0xda, 0xe6, 0xf9, 0xee, 0x4b, 0xe2, 0x06, 0x95, 0x33,
0x85, 0x29, 0xe3, 0x25, 0xf5, 0x8d, 0x53, 0x5f,
0xe1, 0x2a, 0x77, 0x92, 0xe7, 0xdc, 0x4b, 0x4d,
0x01, 0x85, 0x17, 0xca, 0xfd, 0x54, 0x83, 0xb3,
0xbb, 0x28, 0x4f, 0xa1, 0x98, 0x5f, 0x9e, 0x56,
};
// clang-format on
static uint8_t current_app_digest[32];
@ -31,7 +34,7 @@ int mgmt_app_init(uint8_t app_digest[32])
return 0;
}
/* Authenticate an management app */
// Authenticate an management app
bool mgmt_app_authenticate(void)
{
return memeq(current_app_digest, allowed_app_digest, 32) != 0;

View file

@ -17,22 +17,20 @@ enum part_status part_get_status(void)
return part_status;
}
static void part_digest(struct partition_table *part_table, uint8_t *out_digest,
size_t out_len);
static void part_checksum(struct partition_table *part_table,
uint8_t *out_digest, size_t out_len);
static void part_digest(struct partition_table *part_table, uint8_t *out_digest,
size_t out_len)
// part_digest computes a checksum over the partition table to detect
// flash problems
static void part_checksum(struct partition_table *part_table,
uint8_t *out_digest, size_t out_len)
{
int blake2err = 0;
uint8_t key[16] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
assert(part_table != NULL);
assert(out_digest != NULL);
blake2err = blake2s(out_digest, out_len, key, sizeof(key), part_table,
blake2err = blake2s(out_digest, out_len, NULL, 0, part_table,
sizeof(struct partition_table));
assert(blake2err == 0);
@ -50,7 +48,7 @@ int part_table_read(struct partition_table_storage *storage)
ADDR_PARTITION_TABLE_0,
ADDR_PARTITION_TABLE_1,
};
uint8_t check_digest[PART_DIGEST_SIZE] = {0};
uint8_t check_digest[PART_CHECKSUM_SIZE] = {0};
if (storage == NULL) {
return -1;
@ -64,10 +62,10 @@ int part_table_read(struct partition_table_storage *storage)
sizeof(*storage)) != 0) {
return -1;
}
part_digest(&storage->table, check_digest,
sizeof(check_digest));
part_checksum(&storage->table, check_digest,
sizeof(check_digest));
if (memeq(check_digest, storage->check_digest,
if (memeq(check_digest, storage->checksum,
sizeof(check_digest))) {
if (i == 1) {
part_status = PART_SLOT0_INVALID;
@ -91,8 +89,8 @@ int part_table_write(struct partition_table_storage *storage)
return -1;
}
part_digest(&storage->table, storage->check_digest,
sizeof(storage->check_digest));
part_checksum(&storage->table, storage->checksum,
sizeof(storage->checksum));
for (int i = 0; i < 2; i++) {
flash_sector_erase(offset[i]);

View file

@ -6,25 +6,25 @@
#include <stdint.h>
/* ---- Flash ---- ---- */
/* name size start addr */
/* ---- ---- ---- */
/* bitstream 128KiB 0x00 */
/* ---- ---- ---- */
/* Partition 64KiB 0x20000 */
/* ---- ---- ---- */
/* Pre load 1 128KiB 0x30000 */
/* Pre load 2 128KiB 0x50000 */
/* ---- ---- ---- */
/* storage 1 128KiB 0x70000 */
/* storage 2 128KiB 0x90000 */
/* storage 3 128KiB 0xB0000 */
/* storage 4 128KiB 0xD0000 */
/* ---- ---- ---- */
/* Partition2 64KiB 0xf0000 */
// ---- Flash ---- ----
// name size start addr
// ---- ---- ----
// bitstream 128KiB 0x00
// ---- ---- ----
// Partition 64KiB 0x20000
// ---- ---- ----
// Pre load 1 128KiB 0x30000
// Pre load 2 128KiB 0x50000
// ---- ---- ----
// storage 1 128KiB 0x70000
// storage 2 128KiB 0x90000
// storage 3 128KiB 0xB0000
// storage 4 128KiB 0xD0000
// ---- ---- ----
// Partition2 64KiB 0xf0000
/* To simplify all blocks are aligned with the 64KiB blocks on the W25Q80DL
* flash. */
// To simplify all blocks are aligned with the 64KiB blocks on the
// W25Q80DL flash.
#define PART_TABLE_VERSION 1
@ -46,30 +46,33 @@
#define SIZE_STORAGE_AREA 0x20000UL // 128KiB
#define N_STORAGE_AREA 4
#define PART_DIGEST_SIZE 16
#define PART_CHECKSUM_SIZE 32
enum part_status {
PART_SLOT0_INVALID = 1,
};
/* Partition Table */
/*- Table header */
/* - 1 bytes Version */
/**/
/*- Pre-loaded device app 1 */
/* - 4 bytes length. */
/* - 32 bytes digest. */
/* - 64 bytes signature. */
/**/
/*- Pre-loaded device app 2 */
/* - 4 bytes length. */
/* - 32 bytes digest. */
/* - 64 bytes signature. */
/**/
/*- Device app storage area */
/* - 1 byte status. */
/* - 16 bytes random nonce. */
/* - 16 bytes authentication tag. */
// Partition Table
// ----------------------------------------------------------------------
// - Table header
// - 1 bytes Version
//
// - Pre-loaded device app 1
// - 4 bytes length.
// - 32 bytes digest.
// - 64 bytes signature.
//
// - Pre-loaded device app 2
// - 4 bytes length.
// - 32 bytes digest.
// - 64 bytes signature.
//
// - Device app storage area
// - 1 byte status.
// - 16 bytes random nonce.
// - 16 bytes authentication tag.
//
// - Checksum over the above
struct auth_metadata {
uint8_t nonce[16];
@ -99,7 +102,7 @@ struct partition_table {
struct partition_table_storage {
struct partition_table table;
uint8_t check_digest[PART_DIGEST_SIZE];
uint8_t checksum[PART_CHECKSUM_SIZE]; // Helps detect flash problems
} __attribute__((packed));
enum part_status part_get_status(void);

View file

@ -2,6 +2,11 @@
## custom_ops.S
Custom PicoRV32 instructions are located in `custom_ops.S`.
`custom_ops.S` is imported from upstream PicoRV32 commit:
YosysHQ/picorv32@70f3c33
We have imported the custom Custom PicoRV32 instructions in
`custom_ops.S` from https://github.com/YosysHQ/picorv32/ tag v1.0,
commit 6d145b708d5dfa4caa3445bc599927cebc3291d8.
Upstream path is `picorv32/firmware/custom_ops.S`.
The picorv32 firmware is public domain, which we chose to mark as
CC0-1.0.

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Claire Xenia Wolf <claire@yosyshq.com>
// SPDX-License-Identifier: CC0-1.0
// This is free and unencumbered software released into the public domain.
//
// Anyone is free to copy, modify, publish, use, compile, sell, or

View file

@ -18,58 +18,72 @@ static uint32_t slot_to_start_address(uint8_t slot)
return ADDR_PRE_LOADED_APP_0 + slot * SIZE_PRE_LOADED_APP;
}
/* Loads a preloaded app from flash to app RAM */
// Loads a preloaded app from flash to app RAM
int preload_load(struct partition_table *part_table, uint8_t from_slot)
{
if (part_table == NULL) {
return -5;
return -1;
}
if (from_slot >= N_PRELOADED_APP) {
return -4;
return -1;
}
/*Check for a valid app in flash */
if (part_table->pre_app_data[from_slot].size == 0) {
// Check for a valid app in flash
if (part_table->pre_app_data[from_slot].size == 0 &&
part_table->pre_app_data[from_slot].size <= TK1_APP_MAX_SIZE) {
return -1;
}
uint8_t *loadaddr = (uint8_t *)TK1_RAM_BASE;
/* Read from flash, straight into RAM */
// Read from flash, straight into RAM
int ret = flash_read_data(slot_to_start_address(from_slot), loadaddr,
part_table->pre_app_data[from_slot].size);
return ret;
}
/* Expects to receive chunks of data up to 4096 bytes to store into the
* preloaded area. The offset needs to be kept and updated between each call.
* Once done, call preload_store_finalize() with the last parameters.
* */
// preload_store stores chunks of an app in app slot to_slot. data is a buffer
// of size size to be written at byte offset in the slot. offset needs to be
// kept and updated between each call. offset must be a multiple of 256.
//
// When all data has been written call preload_store_finalize() with the last
// parameters.
//
// Returns 0 on success.
int preload_store(struct partition_table *part_table, uint32_t offset,
uint8_t *data, size_t size, uint8_t to_slot)
{
if (part_table == NULL || data == NULL) {
return -5;
return -1;
}
if (to_slot >= N_PRELOADED_APP) {
return -4;
return -1;
}
/* Check if we are allowed to store */
// Check if we are allowed to store
if (!mgmt_app_authenticate()) {
return -3;
return -1;
}
/* Check for a valid app in flash, bale out if it already exists */
// Check for a valid app in flash, bale out if it already
// exists
if (part_table->pre_app_data[to_slot].size != 0) {
return -1;
}
if ((offset + size) > SIZE_PRE_LOADED_APP || size > 4096) {
/* Writing outside of area */
return -2;
if (offset > SIZE_PRE_LOADED_APP) {
return -1;
}
if (size > SIZE_PRE_LOADED_APP) {
return -1;
}
if ((offset + size) > SIZE_PRE_LOADED_APP) {
// Writing outside of area
return -1;
}
uint32_t address = slot_to_start_address(to_slot) + offset;
@ -87,26 +101,38 @@ int preload_store_finalize(struct partition_table_storage *part_table_storage,
{
struct partition_table *part_table = &part_table_storage->table;
if (part_table == NULL || app_digest == NULL || app_signature == NULL) {
return -5;
if (part_table == NULL) {
return -1;
}
// Allow data to point only to app RAM
if (app_digest < (uint8_t *)TK1_RAM_BASE ||
app_digest >= (uint8_t *)(TK1_RAM_BASE + TK1_RAM_SIZE)) {
return -1;
}
if (app_signature < (uint8_t *)TK1_RAM_BASE ||
app_signature >= (uint8_t *)(TK1_RAM_BASE + TK1_RAM_SIZE)) {
return -1;
}
if (to_slot >= N_PRELOADED_APP) {
return -4;
return -1;
}
/* Check if we are allowed to store */
// Check if we are allowed to store
if (!mgmt_app_authenticate()) {
return -3;
return -1;
}
/* Check for a valid app in flash, bale out if it already exists */
// Check for a valid app in flash, bale out if it already
// exists
if (part_table->pre_app_data[to_slot].size != 0) {
return -1;
}
if (app_size == 0 || app_size > SIZE_PRE_LOADED_APP) {
return -2;
return -1;
}
part_table->pre_app_data[to_slot].size = app_size;
@ -121,7 +147,7 @@ int preload_store_finalize(struct partition_table_storage *part_table_storage,
debug_lf();
if (part_table_write(part_table_storage) != 0) {
return -6;
return -1;
}
return 0;
@ -133,19 +159,19 @@ int preload_delete(struct partition_table_storage *part_table_storage,
struct partition_table *part_table = &part_table_storage->table;
if (part_table_storage == NULL) {
return -5;
return -1;
}
if (slot >= N_PRELOADED_APP) {
return -4;
return -1;
}
/* Check if we are allowed to deleted */
// Check if we are allowed to delete
if (!mgmt_app_authenticate()) {
return -3;
return -1;
}
/*Check for a valid app in flash */
// Check for a valid app in flash
if (part_table->pre_app_data[slot].size == 0) {
// Nothing to do.
return 0;
@ -160,10 +186,10 @@ int preload_delete(struct partition_table_storage *part_table_storage,
sizeof(part_table->pre_app_data[slot].signature));
if (part_table_write(part_table_storage) != 0) {
return -6;
return -1;
}
/* Assumes the area is 64 KiB block aligned */
// Assumes the area is 64 KiB block aligned
flash_block_64_erase(
slot_to_start_address(slot)); // Erase first 64 KB block
flash_block_64_erase(slot_to_start_address(slot) +
@ -177,16 +203,16 @@ int preload_get_digsig(struct partition_table *part_table,
uint8_t slot)
{
if (part_table == NULL || app_digest == NULL || app_signature == NULL) {
return -5;
return -1;
}
if (slot >= N_PRELOADED_APP) {
return -4;
return -1;
}
/* Check if we are allowed to read */
// Check if we are allowed to read
if (!mgmt_app_authenticate()) {
return -3;
return -1;
}
memcpy_s(app_digest, 32, part_table->pre_app_data[slot].digest,

View file

@ -1,7 +1,5 @@
/*
* Copyright (C) 2022, 2023 - Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
// Copyright (C) 2022, 2023 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#include <stdint.h>
#include <tkey/assert.h>

View file

@ -1,7 +1,5 @@
/*
* Copyright (C) 2022, 2023 - Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
// Copyright (C) 2022, 2023 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#include <stddef.h>
#include <stdint.h>

View file

@ -0,0 +1,87 @@
/*
* Copyright (C) 2022, 2023 - Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
OUTPUT_ARCH("riscv")
ENTRY(_start)
/* Define stack size */
STACK_SIZE = 3000;
MEMORY
{
ROM (rx) : ORIGIN = 0x00000000, LENGTH = 128k
FWRAM (rw) : ORIGIN = 0xd0000000, LENGTH = 0xF00 /* 3840 B */
RESETINFO (rw) : ORIGIN = 0xd0000F00, LENGTH = 0x100 /* 256 B (part of FW_RAM area) */
RAM (rwx) : ORIGIN = 0x40000000, LENGTH = 0x20000 /* 128 KB */
}
SECTIONS
{
.text.init :
{
*(.text.init)
} >ROM
.htif :
{
. = ALIGN(0x00000000);
*(.htif)
} >ROM
.text :
{
. = ALIGN(4);
_stext = .;
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
*(.srodata) /* .srodata sections (constants, strings, etc.) */
*(.srodata*) /* .srodata* sections (constants, strings, etc.) */
. = ALIGN(4);
_etext = .;
} >ROM
.stack (NOLOAD) :
{
. = ALIGN(16);
_sstack = .;
. += STACK_SIZE;
. = ALIGN(16);
_estack = .;
} >FWRAM
.data :
{
. = ALIGN(4);
_sdata = .;
*(.data) /* .data sections */
*(.data*) /* .data* sections */
*(.sdata) /* .sdata sections */
*(.sdata*) /* .sdata* sections */
. = ALIGN(4);
_edata = .;
} >FWRAM AT>ROM
_sidata = LOADADDR(.data);
/* Uninitialized data section */
.bss :
{
. = ALIGN(4);
_sbss = .;
*(.bss)
*(.bss*)
*(.sbss)
*(.sbss*)
*(COMMON)
. = ALIGN(4);
_ebss = .;
} >FWRAM
}
_sfwram = ORIGIN(FWRAM);
_efwram = ORIGIN(FWRAM) + LENGTH(FWRAM);
_sresetinfo = ORIGIN(RESETINFO);
_eresetinfo = ORIGIN(RESETINFO) + LENGTH(RESETINFO);

View file

@ -0,0 +1,59 @@
// Copyright (C) 2025 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#include <stdint.h>
#include <tkey/assert.h>
#include <tkey/lib.h>
#include <tkey/tk1_mem.h>
#include "reset.h"
// clang-format off
static volatile uint32_t *system_reset = (volatile uint32_t *)TK1_MMIO_TK1_SYSTEM_RESET;
static volatile struct reset *resetinfo = (volatile struct reset *)TK1_MMIO_RESETINFO_BASE;
// clang-format on
int reset(struct reset *userreset, size_t nextlen)
{
if ((uint32_t)userreset < TK1_RAM_BASE ||
(uint32_t)userreset >= TK1_RAM_BASE + TK1_RAM_SIZE) {
return -1;
}
if (nextlen > sizeof(resetinfo->next_app_data)) {
return -1;
}
(void)memset((void *)resetinfo, 0, sizeof(*resetinfo));
resetinfo->type = userreset->type;
memcpy((void *)resetinfo->app_digest, userreset->app_digest,
sizeof(resetinfo->app_digest));
memcpy((void *)resetinfo->next_app_data, userreset->next_app_data,
nextlen);
// Do the actual reset.
*system_reset = 1;
// Should not be reached.
assert(1 == 2);
__builtin_unreachable();
}
int reset_data(uint8_t *next_app_data)
{
if ((uint32_t)next_app_data < TK1_RAM_BASE ||
(uint32_t)next_app_data >= TK1_RAM_BASE + TK1_RAM_SIZE) {
return -1;
}
if ((uint32_t)next_app_data + RESET_DATA_SIZE >
TK1_RAM_BASE + TK1_RAM_SIZE) {
return -1;
}
memcpy(next_app_data, (void *)resetinfo->next_app_data,
RESET_DATA_SIZE);
return 0;
}

View file

@ -1,13 +1,16 @@
// Copyright (C) 2025 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#ifndef TKEY_RESETINFO_H
#define TKEY_RESETINFO_H
#ifndef TKEY_RESET_H
#define TKEY_RESET_H
#include <stddef.h>
#include <stdint.h>
#define TK1_MMIO_RESETINFO_BASE 0xd0000f00
#define TK1_MMIO_RESETINFO_SIZE 0x100
#define RESET_DIGEST_SIZE 32
#define RESET_DATA_SIZE 220
enum reset_start {
START_DEFAULT = 0, // Probably cold boot
@ -20,9 +23,11 @@ enum reset_start {
};
struct reset {
uint32_t type; // Reset type
uint8_t app_digest[32]; // Program digest
uint8_t next_app_data[220]; // Data to leave around for next app
enum reset_start type;
uint8_t app_digest[RESET_DIGEST_SIZE];
uint8_t next_app_data[RESET_DATA_SIZE];
};
int reset(struct reset *userreset, size_t nextlen);
int reset_data(uint8_t *next_app_data);
#endif

View file

@ -10,8 +10,7 @@
static volatile uint32_t *trng_status = (volatile uint32_t *)TK1_MMIO_TRNG_STATUS;
static volatile uint32_t *trng_entropy = (volatile uint32_t *)TK1_MMIO_TRNG_ENTROPY;
// clang-format on
//
//
uint32_t rng_get_word(void)
{
while ((*trng_status & (1 << TK1_MMIO_TRNG_STATUS_READY_BIT)) == 0) {

View file

@ -20,7 +20,7 @@ static void spi_disable(void);
static void spi_write(uint8_t *cmd, size_t size);
static void spi_read(uint8_t *buf, size_t size);
// returns non-zero when the SPI-master is ready, and zero if not
// Returns non-zero when the SPI-master is ready, and zero if not
// ready. This can be used to check if the SPI-master is available
// in the hardware.
static int spi_ready(void)

View file

@ -1,7 +1,5 @@
/*
* Copyright (C) 2022-2025 - Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
// Copyright (C) 2022-2025 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#include <tkey/tk1_mem.h>
@ -28,9 +26,7 @@
_start:
j init
/*
* IRQ handler
*/
// IRQ handler
.=0x10
irq_handler:
// PicoRV32 stores the IRQ bitmask in x4.
@ -112,9 +108,8 @@ x3_valid:
picorv32_retirq_insn() // Return from interrupt
/*
* Init
*/
// Init
.=0x100
init:
li x1, 0
@ -149,7 +144,7 @@ init:
li x30,0
li x31,0
/* Clear FW_RAM */
// Clear FW_RAM
la a0, _sfwram
la a1, _efwram
clear:
@ -157,7 +152,7 @@ clear:
addi a0, a0, 4
blt a0, a1, clear
/* Zero-init bss section */
// Zero-init bss section
la a0, _sbss
la a1, _ebss
@ -166,7 +161,7 @@ loop_init_bss:
addi a0, a0, 4
blt a0, a1, loop_init_bss
/* Init stack */
// Init stack
la sp, _estack
call main

View file

@ -1,7 +1,5 @@
/*
* Copyright (C) 2023 - Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
// Copyright (C) 2023 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#ifndef STATE_H
#define STATE_H

View file

@ -6,18 +6,20 @@
#include <stdint.h>
#include <tkey/debug.h>
#include <tkey/lib.h>
#include <tkey/tk1_mem.h>
#include "auth_app.h"
#include "flash.h"
#include "partition_table.h"
#include "storage.h"
/* Returns the index of the first empty area. If there is no empty area -1 is
* returned. */
// Returns the index of the first empty area.
//
// Returns -1 on errors.
static int get_first_empty(struct partition_table *part_table)
{
if (part_table == NULL) {
return -4;
return -1;
}
for (uint8_t i = 0; i < N_STORAGE_AREA; i++) {
@ -25,13 +27,14 @@ static int get_first_empty(struct partition_table *part_table)
return i;
}
}
return -1;
}
static int index_to_address(int index, uint32_t *address)
{
if (address == NULL) {
return -4;
return -1;
}
if ((index < 0) || (index >= N_STORAGE_AREA)) {
@ -43,12 +46,13 @@ static int index_to_address(int index, uint32_t *address)
return 0;
}
/* Returns the index of the area an app has allocated. If no area is
* authenticated -1 is returned. */
// Returns the index of the area an app has allocated.
//
// Returns -1 on errors.
static int storage_get_area(struct partition_table *part_table)
{
if (part_table == NULL) {
return -4;
return -1;
}
for (uint8_t i = 0; i < N_STORAGE_AREA; i++) {
@ -59,85 +63,85 @@ static int storage_get_area(struct partition_table *part_table)
}
}
}
return -1;
}
/* Allocate a new area for an app. Returns zero if a new area is allocated, one
* if an area already was allocated, and negative values for errors. */
// Allocate a new area for an app. Returns zero on success.
int storage_allocate_area(struct partition_table_storage *part_table_storage)
{
if (part_table_storage == NULL) {
return -4;
return -1;
}
struct partition_table *part_table = &part_table_storage->table;
if (storage_get_area(part_table) != -1) {
/* Already has an area */
return 1;
return 0;
}
int index = get_first_empty(part_table);
if (index == -1) {
if (index < 0) {
/* No empty slot */
return -1;
}
uint32_t start_address = 0;
int err = index_to_address(index, &start_address);
if (err) {
return -3;
if (index_to_address(index, &start_address) != 0) {
return -1;
}
/* Allocate the empty index found */
/* Erase area first */
// Allocate the empty index found
// Erase area first
/* Assumes the area is 64 KiB block aligned */
// Assumes the area is 64 KiB block aligned
flash_block_64_erase(start_address); // Erase first 64 KB block
flash_block_64_erase(start_address +
0x10000); // Erase second 64 KB block
/* Write partition table lastly */
// Write partition table lastly
part_table->app_storage[index].status = 0x01;
auth_app_create(&part_table->app_storage[index].auth);
if (part_table_write(part_table_storage) != 0) {
return -5;
return -1;
}
return 0;
}
/* Dealloacate a previously allocated storage area. Returns zero on success, and
* non-zero on errors. */
// Dealloacate a previously allocated storage area.
//
// Returns zero on success, and non-zero on errors.
int storage_deallocate_area(struct partition_table_storage *part_table_storage)
{
if (part_table_storage == NULL) {
return -4;
return -1;
}
struct partition_table *part_table = &part_table_storage->table;
int index = storage_get_area(part_table);
if (index == -1) {
/* No area to deallocate */
if (index < 0) {
// No area to deallocate
return -1;
}
uint32_t start_address = 0;
int err = index_to_address(index, &start_address);
if (err) {
return -3;
if (index_to_address(index, &start_address) != 0) {
return -1;
}
/* Erase area first */
// Erase area first
/* Assumes the area is 64 KiB block aligned */
// Assumes the area is 64 KiB block aligned
flash_block_64_erase(start_address); // Erase first 64 KB block
flash_block_64_erase(start_address +
0x10000); // Erase second 64 KB block
/* Clear partition table lastly */
// Clear partition table lastly
part_table->app_storage[index].status = 0;
(void)memset(part_table->app_storage[index].auth.nonce, 0x00,
@ -148,46 +152,51 @@ int storage_deallocate_area(struct partition_table_storage *part_table_storage)
sizeof(part_table->app_storage[index].auth.authentication_digest));
if (part_table_write(part_table_storage) != 0) {
return -5;
return -1;
}
return 0;
}
/* Erases sector. Offset of a sector to begin erasing, must be a multiple of
* the sector size. Size to erase in bytes, must be a multiple of the sector *
* size. Returns zero on success, negative error code on failure */
// Erases sector. Offset of a sector to begin erasing, must be a
// multiple of the sector size. Size to erase in bytes, must be a
// multiple of the sector size.
//
// Returns zero on success, negative error code on failure
int storage_erase_sector(struct partition_table *part_table, uint32_t offset,
size_t size)
{
if (part_table == NULL) {
return -4;
return -1;
}
int index = storage_get_area(part_table);
if (index == -1) {
/* No allocated area */
// No allocated area
return -1;
}
uint32_t start_address = 0;
int err = index_to_address(index, &start_address);
if (err) {
return -3;
if (index_to_address(index, &start_address) != 0) {
return -1;
}
/* Cannot only erase entire sectors */
if (offset > SIZE_STORAGE_AREA) {
return -1;
}
// Cannot only erase entire sectors
if (offset % 4096 != 0) {
return -2;
return -1;
}
/* Cannot erase less than one sector */
// Cannot erase less than one sector
if (size < 4096 || size > SIZE_STORAGE_AREA || size % 4096 != 0) {
return -2;
return -1;
}
if ((offset + size) >= SIZE_STORAGE_AREA) {
return -2;
if ((offset + size) > SIZE_STORAGE_AREA) {
return -1;
}
uint32_t address = start_address + offset;
@ -204,32 +213,45 @@ int storage_erase_sector(struct partition_table *part_table, uint32_t offset,
return 0;
}
/* Writes the specified data to the offset inside of the
* allocated area. Assumes area has been erased before hand.
* Currently only handles writes to one sector, hence max size of 4096 bytes.
* Returns zero on success. */
// Writes the specified data to the offset inside of the allocated area.
// Assumes area has been erased before hand. Offset must be a multiple of 256.
//
// Returns zero on success.
int storage_write_data(struct partition_table *part_table, uint32_t offset,
uint8_t *data, size_t size)
{
if (part_table == NULL || data == NULL) {
return -4;
if (part_table == NULL) {
return -1;
}
// Allow data to point only to app RAM
if (data < (uint8_t *)TK1_RAM_BASE ||
data >= (uint8_t *)(TK1_RAM_BASE + TK1_RAM_SIZE)) {
return -1;
}
int index = storage_get_area(part_table);
if (index == -1) {
/* No allocated area */
// No allocated area
return -1;
}
uint32_t start_address = 0;
int err = index_to_address(index, &start_address);
if (err) {
return -3;
if (index_to_address(index, &start_address) != 0) {
return -1;
}
if ((offset + size) > SIZE_STORAGE_AREA || size > 4096) {
/* Writing outside of area */
return -2;
if (offset > SIZE_STORAGE_AREA) {
return -1;
}
if (size > SIZE_STORAGE_AREA) {
return -1;
}
if ((offset + size) > SIZE_STORAGE_AREA) {
// Writing outside of area
return -1;
}
uint32_t address = start_address + offset;
@ -241,31 +263,48 @@ int storage_write_data(struct partition_table *part_table, uint32_t offset,
return flash_write_data(address, data, size);
}
/* Reads size bytes of data at the specified offset inside of
* the allocated area. Returns zero on success. Only read limit
* is the size of the allocated area */
// Reads size bytes of data at the specified offset inside of the
// allocated area.
//
// Only read limit is the size of the allocated area.
//
// Returns zero on success.
int storage_read_data(struct partition_table *part_table, uint32_t offset,
uint8_t *data, size_t size)
{
if (part_table == NULL || data == NULL) {
return -4;
if (part_table == NULL) {
return -1;
}
// Allow data to point only to app RAM
if (data < (uint8_t *)TK1_RAM_BASE ||
data >= (uint8_t *)(TK1_RAM_BASE + TK1_RAM_SIZE)) {
return -1;
}
int index = storage_get_area(part_table);
if (index == -1) {
/* No allocated area */
// No allocated area
return -1;
}
uint32_t start_address = 0;
int err = index_to_address(index, &start_address);
if (err) {
return -3;
if (index_to_address(index, &start_address) != 0) {
return -1;
}
if (offset > SIZE_STORAGE_AREA) {
return -1;
}
if (size > 4096) {
return -1;
}
if ((offset + size) > SIZE_STORAGE_AREA) {
/* Reading outside of area */
return -2;
// Reading outside of area
return -1;
}
uint32_t address = start_address + offset;

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: 2025 Tillitis AB <tillitis.se>
// SPDX-License-Identifier: GPL-2.0-only
#ifdef QEMU_SYSCALL
#define picorv32_maskirq_insn(...)
@ -13,7 +16,7 @@
syscall_enable:
/* Enable syscall IRQ */
// Enable syscall IRQ
li t0, 0x7fffffff // IRQ31 mask
picorv32_maskirq_insn(zero, t0) // Enable IRQs

View file

@ -1,7 +1,5 @@
/*
* Copyright (C) 2025 - Tillitis AB
* SPDX-License-Identifier: GPL-2.0-only
*/
// Copyright (C) 2025 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#include <stdint.h>
#include <tkey/assert.h>
@ -11,15 +9,12 @@
#include "partition_table.h"
#include "preload_app.h"
#include "reset.h"
#include "storage.h"
#include "../tk1/resetinfo.h"
#include "../tk1/syscall_num.h"
#include "syscall_num.h"
// clang-format off
static volatile uint32_t *system_reset = (volatile uint32_t *)TK1_MMIO_TK1_SYSTEM_RESET;
static volatile uint32_t *udi = (volatile uint32_t *)TK1_MMIO_TK1_UDI_FIRST;
static volatile struct reset *resetinfo = (volatile struct reset *)TK1_MMIO_RESETINFO_BASE;
// clang-format on
extern struct partition_table_storage part_table_storage;
@ -28,24 +23,9 @@ extern uint8_t part_status;
int32_t syscall_handler(uint32_t number, uint32_t arg1, uint32_t arg2,
uint32_t arg3)
{
struct reset *userreset = (struct reset *)arg1;
switch (number) {
case TK1_SYSCALL_RESET:
if (arg2 > sizeof(resetinfo->next_app_data)) {
return -1;
}
(void)memset((void *)resetinfo, 0, sizeof(*resetinfo));
resetinfo->type = userreset->type;
memcpy((void *)resetinfo->app_digest, userreset->app_digest,
32);
memcpy((void *)resetinfo->next_app_data,
userreset->next_app_data, arg2);
*system_reset = 1;
// Should not be reached.
assert(1 == 2);
return reset((struct reset *)arg1, (size_t)arg2);
break;
case TK1_SYSCALL_ALLOC_AREA:
@ -53,31 +33,39 @@ int32_t syscall_handler(uint32_t number, uint32_t arg1, uint32_t arg2,
debug_puts("couldn't allocate storage area\n");
return -1;
}
return 0;
case TK1_SYSCALL_DEALLOC_AREA:
if (storage_deallocate_area(&part_table_storage) < 0) {
debug_puts("couldn't deallocate storage area\n");
return -1;
}
return 0;
case TK1_SYSCALL_WRITE_DATA:
if (storage_write_data(&part_table_storage.table, arg1,
(uint8_t *)arg2, arg3) < 0) {
debug_puts("couldn't write storage area\n");
return -1;
}
return 0;
case TK1_SYSCALL_READ_DATA:
if (storage_read_data(&part_table_storage.table, arg1,
(uint8_t *)arg2, arg3) < 0) {
debug_puts("couldn't read storage area\n");
return -1;
}
return 0;
case TK1_SYSCALL_ERASE_DATA:
if (storage_erase_sector(&part_table_storage.table, arg1,
arg2) < 0) {
debug_puts("couldn't erase storage area\n");
return -1;
}
return 0;
case TK1_SYSCALL_GET_VIDPID:
// UDI is 2 words: VID/PID & serial. Return just the
// first word. Serial is kept secret to the device
@ -111,6 +99,10 @@ int32_t syscall_handler(uint32_t number, uint32_t arg1, uint32_t arg2,
case TK1_SYSCALL_STATUS:
return part_get_status();
case TK1_SYSCALL_GET_APP_DATA:
// arg1 next_app_data
return reset_data((uint8_t *)arg1);
default:
assert(1 == 2);
}

View file

@ -18,6 +18,7 @@ enum syscall_num {
TK1_SYSCALL_PRELOAD_GET_DIGSIG = 11,
TK1_SYSCALL_REG_MGMT = 12,
TK1_SYSCALL_STATUS = 13,
TK1_SYSCALL_GET_APP_DATA = 14,
};
#endif

View file

@ -32,6 +32,7 @@ The Castor TKey hardware supports more USB endpoints:
- CDC - the same thing as older versions.
- FIDO security token, for FIDO-like apps.
- CCID, smart card interface.
- DEBUG, a HID debug port.
The communication is still over a single UART. To differ between the

View file

@ -10,20 +10,22 @@
// I/O endpoints. Keep it as bits possible to use in a bitmask in
// readselect().
//
// Note that the DEBUG, CDC, and FIDO should be kept the same on
// the CH552 side.
// Note that the values for IO_CH552, IO_CDC, IO_FIDO, IO_CCID and IO_DEBUG
// should be kept the same in the code for the CH552 side.
enum ioend {
IO_NONE = 0x00, // No endpoint
IO_UART = 0x01, // Only destination, raw UART access
IO_QEMU = 0x02, // Only destination, QEMU debug port
IO_CH552 = 0x10, // Internal CH552 control port
IO_DEBUG = 0x20, // HID debug port
IO_CDC = 0x40, // CDC "serial port"
IO_FIDO = 0x80, // FIDO security token port
IO_CH552 = 0x04, // Internal CH552 control port
IO_CDC = 0x08, // CDC "serial" port
IO_FIDO = 0x10, // FIDO security token port
IO_CCID = 0x20, // CCID "smart card" port
IO_DEBUG = 0x40, // Debug port over USB HID
};
enum ch552cmd {
SET_ENDPOINTS = 0x01, // Config USB endpoints on the CH552
CH552_CMD_MAX,
};
void write(enum ioend dest, const uint8_t *buf, size_t nbytes);

View file

@ -74,15 +74,20 @@ static void write_with_header(enum ioend dest, const uint8_t *buf,
// write blockingly writes nbytes bytes of data from buf to dest which
// is either:
//
// - IO_UART: Low-level UART access, no USB Mode Header added.
//
// - IO_QEMU: QEMU debug port
//
// - IO_UART: Low-level UART access, no USB Mode Header added.
// - IO_CH552: Internal communication between the FPGA and the
// CH552, with header.
//
// - IO_CDC: Through the UART for the CDC endpoint, with header.
//
// - IO_FIDO: Through the UART for the FIDO endpoint, with header.
//
// - IO_DEBUG: Through the UART for the DEBUG HID endpoint, with
// - IO_CCID: Through the UART for the CCID endpoint, with header.
//
// - IO_DEBUG: Through the UART for the DEBUG endpoint (USB HID), with
// header.
void write(enum ioend dest, const uint8_t *buf, size_t nbytes)
{
@ -203,9 +208,11 @@ static int discard(size_t nbytes)
//
// Only endpoints available for read are:
//
// - IO_DEBUG
// - IO_CH552
// - IO_CDC
// - IO_FIDO
// - IO_CCID
// - IO_DEBUG
//
// If you need blocking low-level UART reads, use uart_read() instead.
//
@ -352,7 +359,8 @@ void hexdump(enum ioend dest, void *buf, int len)
// Configure USB endpoints that should be enabled/disabled
//
// Allowed options are:
// - IO_FIDO
// - IO_FIDO (can't be used used together with IO_CCID)
// - IO_CCID (can't be used used together with IO_FIDO)
// - IO_DEBUG
//
// The following are always enabled:

View file

@ -0,0 +1,33 @@
# Tools
We have developed some tools necessary for the build.
- `app_bin_to_spram_hex.py`: Script used to include a device app in a
testbench simulation.
- `b2s`: Compute and print a BLAKE2s digest over a file. Used for the
digest of the app in app slot 0 included in the firmware.
- `default_partition.bin`: Default partition table for the flash.
- `load_preloaded_app.sh`: Script to load two copies of the partition
table to flash and a pre-loaded to app slot 0 or 1. Needs
`default_partition.bin`, generated with `tkeyimage` and the binary
of the device app to load. Call like: `./load_preloaded_app 0
path/to/binary`.
- `makehex.py`: Used to build hex version of firmware ROM for FPGA
bitstream build.
- `patch_uds_udi.py`: Script used to patch in the Unique Device Secret
in `data/uds.hex` and the Unique Device Identifier in `data/udi.hex`
into the bitstream without having to rebuild the entire bitstream.
- `run_pnr.sh`: Script to run place and route with `nextpnr` in order
to find a routing seed that will meet desired timing.
- `tkeyimage`: Utility to create and parse flash images with a TKey
filesystem or the partition table
- `tpt/tpt.py`: Utility to create the Unique Device Secret (UDS) and
Unique Device Identity (UDI) interactively.

View file

@ -20,7 +20,7 @@ func printCDigest(digest [blake2s.Size]byte, fileName string) {
fmt.Printf("const uint8_t digest[32] = {\n")
for _, n := range digest {
fmt.Printf("0x%x, ", n)
fmt.Printf("0x%02x, ", n)
}
fmt.Printf("\n}; \n")

View file

@ -1,28 +0,0 @@
#!/usr/bin/env python3
import argparse
FLASH_SIZE_BYTES = 1 * 1024 * 1024
PRELOADED_APP_0_START = 0x30000
def run(output_path, preloaded_app_0_path):
with (
open(output_path, "wb") as output_file,
open(preloaded_app_0_path, "rb") as preloaded_app_0_file,
):
content = bytearray(b"\xFF") * FLASH_SIZE_BYTES
preloaded_app = preloaded_app_0_file.read()
content[
PRELOADED_APP_0_START : PRELOADED_APP_0_START + len(preloaded_app)
] = preloaded_app
output_file.write(content)
if __name__ == "__main__":
arg_parser = argparse.ArgumentParser()
arg_parser.add_argument("OUTPUT_PATH")
arg_parser.add_argument("PRELOADED_APP_0_PATH")
args = arg_parser.parse_args()
run(args.OUTPUT_PATH, args.PRELOADED_APP_0_PATH)

View file

@ -1,5 +1,7 @@
#!/bin/bash -e
# SPDX-FileCopyrightText: 2025 Tillitis AB <tillitis.se>
# SPDX-License-Identifier: GPL-2.0-only
if [ $# != 2 ]
then
echo "Usage: $0 slot_num app_file"

View file

@ -1,5 +1,8 @@
#!/usr/bin/env python3
#
# SPDX-FileCopyrightText: Claire Xenia Wolf <claire@yosyshq.com>
# SPDX-License-Identifier: CC0-1.0
# This is free and unencumbered software released into the public domain.
#
# Anyone is free to copy, modify, publish, use, compile, sell, or

View file

@ -1,172 +0,0 @@
package main
import (
"encoding/binary"
"flag"
"fmt"
"io"
"os"
"golang.org/x/crypto/blake2s"
)
type PreLoadedAppData struct {
Size uint32
Digest [32]uint8
Signature [64]uint8
}
type Auth struct {
Nonce [16]uint8
AuthDigest [16]uint8
}
type AppStorage struct {
Status uint8
Auth Auth
}
type PartTable struct {
Version uint8
PreLoadedAppData [2]PreLoadedAppData
AppStorage [4]AppStorage
}
type PartTableStorage struct {
PartTable PartTable
Digest [16]uint8
}
type Flash struct {
Bitstream [0x20000]uint8
PartitionTableStorage PartTableStorage
PartitionTablePadding [64*1024 - 349]uint8
PreLoadedApp0 [0x20000]uint8
PreLoadedApp1 [0x20000]uint8
AppStorage [4][0x20000]uint8
EndPadding [0x10000]uint8
}
func readStruct[T PartTableStorage | Flash](filename string) T {
var s T
file, err := os.Open(filename)
if err != nil {
panic(err)
}
if err := binary.Read(file, binary.LittleEndian, &s); err != nil {
panic(err)
}
sLen, err := file.Seek(0, io.SeekCurrent)
if err != nil {
panic(err)
}
fmt.Fprintf(os.Stderr, "INFO: %T struct is %d byte long\n", *new(T), sLen)
return s
}
func printPartTableStorageCondensed(storage PartTableStorage) {
fmt.Printf("Partition Table Storage\n")
fmt.Printf(" Partition Table\n")
fmt.Printf(" Header\n")
fmt.Printf(" Version : %d\n", storage.PartTable.Version)
for i, appData := range storage.PartTable.PreLoadedAppData {
fmt.Printf(" Preloaded App %d\n", i)
fmt.Printf(" Size : %d\n", appData.Size)
fmt.Printf(" Digest : %x\n", appData.Digest[:16])
fmt.Printf(" %x\n", appData.Digest[16:])
fmt.Printf(" Signature : %x\n", appData.Signature[:16])
fmt.Printf(" %x\n", appData.Signature[16:32])
fmt.Printf(" %x\n", appData.Signature[32:48])
fmt.Printf(" %x\n", appData.Signature[48:])
}
fmt.Printf(" Digest : %x\n", storage.Digest)
}
func calculateStorageDigest(data []byte) []byte {
key := [16]byte{}
hash, err := blake2s.New128(key[:])
if err != nil {
panic(err)
}
hash.Write(data)
digest := hash.Sum([]byte{})
return digest
}
func generatePartTableStorage(outputFilename string, app0Filename string) {
storage := PartTableStorage{
PartTable: PartTable{
Version: 1,
PreLoadedAppData: [2]PreLoadedAppData{},
AppStorage: [4]AppStorage{},
},
}
if app0Filename != "" {
stat, err := os.Stat(app0Filename)
if err != nil {
panic(err)
}
storage.PartTable.PreLoadedAppData[0].Size = uint32(stat.Size())
}
buf := make([]byte, 4096, 4096)
len, err := binary.Encode(buf, binary.LittleEndian, storage.PartTable)
if err != nil {
panic(err)
}
fmt.Printf("buf - len: %d, data: %x\n", len, buf[:len])
digest := calculateStorageDigest(buf[:len])
copy(storage.Digest[:], digest)
fmt.Printf("digest: %x\n", digest)
storageFile, err := os.Create(outputFilename)
if err != nil {
panic(err)
}
if err := binary.Write(storageFile, binary.LittleEndian, storage); err != nil {
panic(err)
}
}
func main() {
input := ""
output := ""
app0 := ""
flash := false
flag.StringVar(&input, "i", "", "Input binary dump file. Cannot be used with -o.")
flag.StringVar(&output, "o", "", "Output binary dump file. Cannot be used with -i.")
flag.StringVar(&app0, "app0", "", "Binary in pre loaded app slot 0. Can be used with -o.")
flag.BoolVar(&flash, "f", false, "Treat input file as a dump of the entire flash memory.")
flag.Parse()
if (input == "" && output == "") || (input != "" && output != "") {
flag.Usage()
os.Exit(1)
}
if input != "" {
var storage PartTableStorage
if flash {
storage = readStruct[Flash](input).PartitionTableStorage
} else {
storage = readStruct[PartTableStorage](input)
}
printPartTableStorageCondensed(storage)
} else if output != "" {
generatePartTableStorage(output, app0)
}
}

View file

@ -1,15 +0,0 @@
#!/bin/sh
set -eu
cd "${0%/*}"
cd ../../production_test
if [ ! -e venv ]; then
python3 -m venv venv
. ./venv/bin/activate
pip3 install -r requirements.txt
else
. ./venv/bin/activate
fi
./reset.py

View file

@ -1,5 +1,8 @@
#!/bin/bash
# SPDX-FileCopyrightText: 2025 Tillitis AB <tillitis.se>
# SPDX-License-Identifier: GPL-2.0-only
help() {
echo "Usage: $(basename $0) [OPTION]"
echo "Run multiple place and route threads with nextpnr-ice40"

View file

@ -0,0 +1,114 @@
# tkeyimage
A tool to parse or generate partition table or entire filesystems for
the TKey.
- Parse with `-i file.bin` for "input".
- Generate with `-o file.bin` for "output".
Add `-f` to parse or generate an entire flash image file.
## Usage
### Inspect a partition table dump
Dump the entire data from flash, then inspect:
```
$ tillitis-iceprog -R 1M dump.bin
$ ./tkeyimage -i dump.bin -f
INFO: main.Flash struct is 1048576 byte long
Partition Table Storage
Partition Table
Header
Version : 1
Preloaded App 0
Size : 23796
Digest : 00000000000000000000000000000000
00000000000000000000000000000000
Signature : 00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
Preloaded App 1
Size : 264
Digest : 96bb4c90603dbbbe09b9a1d7259b5e9e
61bedd89a897105c30c9d4bf66a98d97
Signature : ccb60c034e559b8c695f25233b80c245
e099316324e1a4e68a14c82d834eee58
5700cd5c29b64e74159a4dbf3fed030a
140e981fb3b6972c125afb4d4497da0a
Digest : 4628f142764f724e45e05b20363960967705cfcee8285b2d9d207e04a46e275e
```
Read only the first copy of the partition table from flash to file,
then inspect:
```
$ tillitis-iceprog -o 128k -r partition.bin
$ ./tkeyimage -i partition.bin
INFO: main.PartTableStorage struct is 365 byte long
Partition Table Storage
Partition Table
Header
Version : 1
Preloaded App 0
Size : 23796
Digest : 00000000000000000000000000000000
00000000000000000000000000000000
Signature : 00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
Preloaded App 1
Size : 0
Digest : 00000000000000000000000000000000
00000000000000000000000000000000
Signature : 00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000
Digest : 40c6dbb4c8fda561369ec54a907452ae352ccbd736ba7824c4e173fd438b7d7a
```
### Generate a partition table
If you want to generate just a partition table:
```
$ ./tkeyimage -o partition.bin
```
With an app in slot 0, filling in the size in the partition table:
```
$ ./tkeyimage -o partition.bin -app0 ../../fw/testloadapp/testloadapp.bin
```
### Generate flash image
The program can also generate an entire flash image for use either
with real hardware or qemu.
Generate like this:
```
$ ./tkeyimage -o flash.bin -f -app0 ../../fw/testloadapp/testloadapp.bin
```
Using `-app0` is mandatory because TKey firmware won't start without
an app in slot 0.
The qemu args to use to run with `flash.bin` as the flash are:
```
-drive file=flash.bin,if=mtd,format=raw,index=0
```
A complete command example:
```
$ qemu-system-riscv32 -nographic -M tk1-castor,fifo=chrid \
-bios qemu_firmware.elf -chardev pty,id=chrid -s -d guest_errors \
-drive file=flash.bin,if=mtd,format=raw,index=0
```

View file

@ -1,4 +1,4 @@
module partition_table
module tkeyimage
go 1.23.0

View file

@ -0,0 +1,266 @@
// SPDX-FileCopyrightText: 2025 Tillitis AB <tillitis.se>
// SPDX-License-Identifier: BSD-2-Clause
package main
import (
"encoding/binary"
"flag"
"fmt"
"io"
"os"
"golang.org/x/crypto/blake2s"
)
// Maximum allowed size in bytes of a preloaded app.
const MaxAppSize = (128 * 1024)
// Size in bytes of the partition table binary. Find out with -o and
// check the file size.
const PartitionSize = 365
type PreLoadedAppData struct {
Size uint32
Digest [32]uint8
Signature [64]uint8
}
type Auth struct {
Nonce [16]uint8
AuthDigest [16]uint8
}
type AppStorage struct {
Status uint8
Auth Auth
}
type PartTable struct {
Version uint8
PreLoadedAppData [2]PreLoadedAppData
AppStorage [4]AppStorage
}
type PartTableStorage struct {
PartTable PartTable
Checksum [32]byte
}
func (p *PartTableStorage) GenChecksum() {
buf := make([]byte, 4096)
len, err := binary.Encode(buf, binary.LittleEndian, p.PartTable)
if err != nil {
panic(err)
}
p.Checksum = blake2s.Sum256(buf[:len])
}
// Name Size Start addr
// ---- ---- ----
// Bitstream 128KiB 0x00
// ---- ---- ----
// Partition 64KiB 0x20000
// ---- ---- ----
// Pre load 0 128KiB 0x30000
// Pre load 1 128KiB 0x50000
// ---- ---- ----
// storage 0 128KiB 0x70000
// storage 1 128KiB 0x90000
// storage 2 128KiB 0xB0000
// storage 3 128KiB 0xD0000
// ---- ---- ----
// Partition2 64KiB 0xf0000
type Flash struct {
Bitstream [0x20000]uint8 // Reserved for FPGA bitstream
PartitionTable PartTableStorage // 0x20000 partition table
PartitionTablePadding [64*1024 - PartitionSize]uint8 // ~64k padding
PreLoadedApp0 [0x20000]uint8 // 0x30000
PreLoadedApp1 [0x20000]uint8 // 0x50000
AppStorage [4][0x20000]uint8 // 0x70000, 4 * 128 storage for apps
PartitionTable2 PartTableStorage // 0xf0000 second copy of table
PartitionTablePadding2 [64*1024 - PartitionSize]uint8 // ~64k padding
}
func readStruct[T PartTableStorage | Flash](filename string) T {
var s T
file, err := os.Open(filename)
if err != nil {
panic(err)
}
if err := binary.Read(file, binary.LittleEndian, &s); err != nil {
panic(err)
}
sLen, err := file.Seek(0, io.SeekCurrent)
if err != nil {
panic(err)
}
fmt.Fprintf(os.Stderr, "INFO: %T struct is %d byte long\n", *new(T), sLen)
return s
}
func printPartTableStorageCondensed(storage PartTableStorage) {
fmt.Printf("Partition Table Storage\n")
fmt.Printf(" Partition Table\n")
fmt.Printf(" Header\n")
fmt.Printf(" Version : %d\n", storage.PartTable.Version)
for i, appData := range storage.PartTable.PreLoadedAppData {
fmt.Printf(" Preloaded App %d\n", i)
fmt.Printf(" Size : %d\n", appData.Size)
fmt.Printf(" Digest : %x\n", appData.Digest[:16])
fmt.Printf(" %x\n", appData.Digest[16:])
fmt.Printf(" Signature : %x\n", appData.Signature[:16])
fmt.Printf(" %x\n", appData.Signature[16:32])
fmt.Printf(" %x\n", appData.Signature[32:48])
fmt.Printf(" %x\n", appData.Signature[48:])
}
fmt.Printf(" Digest : %x\n", storage.Checksum)
}
func genPartitionFile(outputFilename string, app0Filename string) {
partition := newPartTable(app0Filename)
partition.GenChecksum()
storageFile, err := os.Create(outputFilename)
if err != nil {
panic(err)
}
if err := binary.Write(storageFile, binary.LittleEndian, partition); err != nil {
panic(err)
}
}
// newPartTable generates a new partition table suitable for storage.
// If given app0Filename it also fills in the size of the file.
//
// When you're done with filling in the struct, remember to call
// GenChecksum().
//
// It returns the partition table.
func newPartTable(app0Filename string) PartTableStorage {
storage := PartTableStorage{
PartTable: PartTable{
Version: 1,
PreLoadedAppData: [2]PreLoadedAppData{},
AppStorage: [4]AppStorage{},
},
}
if app0Filename != "" {
stat, err := os.Stat(app0Filename)
if err != nil {
panic(err)
}
storage.PartTable.PreLoadedAppData[0].Size = uint32(stat.Size())
}
return storage
}
func memset(s []byte, c byte) {
for i := range s {
s[i] = c
}
}
func genFlashFile(outputFilename string, app0Filename string) {
var flash Flash
// Set all bits in flash to erased.
memset(flash.Bitstream[:], 0xff)
// partition0 will be filled in below
memset(flash.PartitionTablePadding[:], 0xff)
memset(flash.PreLoadedApp0[:], 0xff)
memset(flash.PreLoadedApp1[:], 0xff)
memset(flash.AppStorage[0][:], 0xff)
memset(flash.AppStorage[1][:], 0xff)
memset(flash.AppStorage[2][:], 0xff)
memset(flash.AppStorage[3][:], 0xff)
// partition1 will be filled in below
memset(flash.PartitionTablePadding2[:], 0xff)
partition := newPartTable(app0Filename)
partition.GenChecksum()
flash.PartitionTable = partition
flash.PartitionTable2 = partition
// Read the entire file.
appBuf, err := os.ReadFile(app0Filename)
if err != nil {
panic(err)
}
if len(appBuf) > MaxAppSize {
fmt.Printf("max app size is 128 k")
os.Exit(1)
}
copy(flash.PreLoadedApp0[:], appBuf)
storageFile, err := os.Create(outputFilename)
if err != nil {
panic(err)
}
if err := binary.Write(storageFile, binary.LittleEndian, flash); err != nil {
panic(err)
}
}
func main() {
var input string
var output string
var app0 string
var flash bool
flag.StringVar(&input, "i", "", "Input binary file. Cannot be used with -o.")
flag.StringVar(&output, "o", "", "Output binary file. Cannot be used with -i. If used with -f, -app0 must also be specified.")
flag.StringVar(&app0, "app0", "", "Binary in pre loaded app slot 0. Can be used with -o.")
flag.BoolVar(&flash, "f", false, "Treat file as a dump of the entire flash memory.")
flag.Parse()
if len(flag.Args()) > 0 {
fmt.Printf("superfluous args\n")
flag.Usage()
os.Exit(1)
}
if (input == "" && output == "") || (input != "" && output != "") {
flag.Usage()
os.Exit(1)
}
if input != "" {
var storage PartTableStorage
if flash {
storage = readStruct[Flash](input).PartitionTable
} else {
storage = readStruct[PartTableStorage](input)
}
printPartTableStorageCondensed(storage)
os.Exit(0)
}
if output != "" {
if flash {
if app0 == "" {
fmt.Printf("need -app0 path/to/app\n")
os.Exit(1)
}
genFlashFile(output, app0)
} else {
genPartitionFile(output, app0)
}
}
}

View file

@ -51,16 +51,20 @@ def format_descriptor(name, value):
if __name__ == "__main__":
strings = {
"ProdDesc": "MTA1-USB-V1",
"ProdDesc": "Tillitis TKEY-USB-V2",
"ManufDesc": "Tillitis",
"SerialDesc": "68de5d27-e223-4874-bc76-a54d6e84068f",
"CdcCtrlInterfaceDesc": "CDC-Ctrl",
"CdcDataInterfaceDesc": "CDC-Data",
"FidoInterfaceDesc": "FIDO",
"CcidInterfaceDesc": "CCID",
"DebugInterfaceDesc": "DEBUG"
}
with open('inc/usb_strings.h', 'w') as f:
f.write('// SPDX-FileCopyrightText: 2024 Tillitis AB <tillitis.se>\n')
f.write('// SPDX-License-Identifier: MIT\n')
f.write('\n')
f.write('#ifndef __USB_STRINGS_H__\n')
f.write('#define __USB_STRINGS_H__\n')
f.write('\n')

View file

@ -94,6 +94,13 @@ Header file for CH554 microcontrollers.
#define USB_CDC_REQ_TYPE_SET_CONTROL_LINE_STATE 0x22
#endif
/* USB CCID (Smart Card) class request code */
#ifndef USB_CCID_REQ_TYPE
#define USB_CCID_REQ_TYPE_ABORT 0x01
#define USB_CCID_REQ_TYPE_GET_CLOCK_FREQUENCIES 0x02
#define USB_CCID_REQ_TYPE_GET_DATA_RATES 0x03
#endif
/* USB request type for hub class request */
#ifndef HUB_GET_HUB_DESCRIPTOR
#define HUB_CLEAR_HUB_FEATURE 0x20
@ -200,7 +207,8 @@ Header file for CH554 microcontrollers.
#define USB_IDX_INTERFACE_CDC_CTRL_STR 0x04
#define USB_IDX_INTERFACE_CDC_DATA_STR 0x05
#define USB_IDX_INTERFACE_FIDO_STR 0x06
#define USB_IDX_INTERFACE_DEBUG_STR 0x07
#define USB_IDX_INTERFACE_CCID_STR 0x07
#define USB_IDX_INTERFACE_DEBUG_STR 0x08
#endif
#ifndef USB_DEVICE_ADDR

View file

@ -8,14 +8,16 @@ enum ioend {
IO_NONE = 0x00, // No endpoint
IO_UART = 0x01, // Only destination, raw UART access
IO_QEMU = 0x02, // Only destination, QEMU debug port
IO_CH552 = 0x10, // Internal CH552 control port
IO_DEBUG = 0x20, // HID debug port
IO_CDC = 0x40, // CDC "serial port"
IO_FIDO = 0x80, // FIDO security token port
IO_CH552 = 0x04, // Internal CH552 control port
IO_CDC = 0x08, // CDC "serial" port
IO_FIDO = 0x10, // FIDO security token port
IO_CCID = 0x20, // CCID "smart card" port
IO_DEBUG = 0x40, // Debug port over USB HID
};
enum ch552cmd {
SET_ENDPOINTS = 0x01, // Config enabled/disabled USB endpoints on the CH552
SET_ENDPOINTS = 0x01, // Config USB endpoints on the CH552
CH552_CMD_MAX,
};
#endif

View file

@ -6,12 +6,14 @@
#include "mem.h"
unsigned char FLASH ProdDesc[] = { // "MTA1-USB-V1"
24, // Length of this descriptor (in bytes)
unsigned char FLASH ProdDesc[] = { // "Tillitis TKEY-USB-V2"
42, // Length of this descriptor (in bytes)
0x03, // Descriptor type (String)
'M', 0, 'T', 0, 'A', 0, '1', 0,
'-', 0, 'U', 0, 'S', 0, 'B', 0,
'-', 0, 'V', 0, '1', 0,
'T', 0, 'i', 0, 'l', 0, 'l', 0,
'i', 0, 't', 0, 'i', 0, 's', 0,
' ', 0, 'T', 0, 'K', 0, 'E', 0,
'Y', 0, '-', 0, 'U', 0, 'S', 0,
'B', 0, '-', 0, 'V', 0, '2', 0,
};
unsigned char FLASH ManufDesc[] = { // "Tillitis"
@ -55,6 +57,12 @@ unsigned char FLASH FidoInterfaceDesc[] = { // "FIDO"
'F', 0, 'I', 0, 'D', 0, 'O', 0,
};
unsigned char FLASH CcidInterfaceDesc[] = { // "CCID"
10, // Length of this descriptor (in bytes)
0x03, // Descriptor type (String)
'C', 0, 'C', 0, 'I', 0, 'D', 0,
};
unsigned char FLASH DebugInterfaceDesc[] = { // "DEBUG"
12, // Length of this descriptor (in bytes)
0x03, // Descriptor type (String)

File diff suppressed because it is too large Load diff