Compare commits

..

17 commits

Author SHA1 Message Date
Michael Cardell Widerkrantz
e10f7007a4
doc: Update release notes with filesystem things 2025-04-16 13:36:18 +02:00
Michael Cardell Widerkrantz
93704287d5
doc: Update firmware README
- Describe all the new functionality.
- Revise text.
2025-04-16 13:35:42 +02:00
Mikael Ågren
ba7df35064
fw: Use globbing for FMTFILES 2025-04-16 13:28:16 +02:00
Michael Cardell Widerkrantz
2746070402
fw: Make splint work on current code. 2025-04-16 13:28:16 +02:00
Michael Cardell Widerkrantz
b70d42471b
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-16 13:28:16 +02:00
Mikael Ågren
8e7108f7ee
tool: Add script to load pre-loaded app into flash 2025-04-16 13:28:15 +02:00
Michael Cardell Widerkrantz
6e793bea26
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-16 13:28:15 +02:00
Michael Cardell Widerkrantz
d34e8e2c12
tool: Introduce b2s tool to help compute BLAKE2s digests 2025-04-16 13:28:15 +02:00
Mikael Ågren
91471c2962
tool: Add tool to inspect and create partition table binaries 2025-04-16 13:28:14 +02:00
Mikael Ågren
0a0f5bcec8
tool: Add tool to create a flash image containing a preloaded app at slot 0 2025-04-16 13:28:14 +02:00
Michael Cardell Widerkrantz
7e859bd06a
testloadapp: Add app for testing preloaded app functionality 2025-04-16 13:28:14 +02:00
Jonas Thörnblad
403013a0e8
resettestapp: Add resetinfo testapp
Co-authored-by: Mikael Ågren <mikael@tillitis.se>
Co-authored-by: Michael Cardell Widerkrantz <mc@tillitis.se>
2025-04-16 13:28:13 +02:00
Mikael Ågren
c9a7910965
fw: Replace custom picorv32 instructions when building for qemu 2025-04-16 13:07:22 +02:00
Mikael Ågren
50966f010d
testapp: Call storage syscalls
Calls
- TK1_SYSCALL_ALLOC_AREA
- TK1_SYSCALL_WRITE_DATA
- TK1_SYSCALL_READ_DATA
- TK1_SYSCALL_DEALLOC_AREA
2025-04-16 13:07:22 +02:00
Michael Cardell Widerkrantz
e9ddf29ce9
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 apps 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-16 13:07:22 +02:00
Michael Cardell Widerkrantz
1ff6e0262f
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-16 13:07:21 +02:00
Michael Cardell Widerkrantz
81f3195592
tkey-libs: Import tag fw-3 of tkey-libs
- Use tag fw-3 from https://github.com/tillitis/tkey-libs/
2025-04-16 13:07:16 +02:00
81 changed files with 1773 additions and 2492 deletions

View file

@ -31,9 +31,6 @@ jobs:
run: |
make checkfmt
- name: check for SPDX tags
run: ./LICENSES/spdx-ensure
check-firmware:
runs-on: ubuntu-latest
container:
@ -151,3 +148,8 @@ 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/tools/tkeyimage/tkeyimage
/hw/application_fpga/tkey-libs/libblake2s.a
/hw/application_fpga/tools/partition_table/partition_table
/hw/application_fpga/tools/b2s/b2s
synth.json
synth.txt

View file

@ -2,13 +2,11 @@
## Main license
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`.
Unless otherwise noted, the project sources are licensed under the
terms and conditions of the "GNU General Public License v2.0 only".
This directory contains copies of the license texts used by the
sources included in the project source tree.
The `LICENSES/` directory contains copies of the license texts used by
the sources included in the project source tree.
## SPDX
@ -21,21 +19,3 @@ 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

View file

@ -1,338 +0,0 @@
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.

245
LICENSES/gpl-v2.only.txt Normal file
View file

@ -0,0 +1,245 @@
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,73 +16,34 @@ 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
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
dco.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,17 +40,9 @@ 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
@ -85,84 +77,27 @@ https://github.com/tillitis/tkey-libs
but keep our own copy of it in the repo. See below.
## Building & flashing
## Building
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:
Building is probably easiest using make and Podman. Do this to see all
targets:
```
cd contrib
make
```
Build the entire FPGA bitstream, which includes the firmware, using
Podman:
```
cd contrib
make run-make
```
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
cd hw/usb_interface/ch552_fw
make
```
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
A copy of [tkey-libs](https://github.com/tillitis/tkey-libs) is kept

View file

@ -9,19 +9,17 @@ 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-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)"
@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)"
run:
podman run --rm --mount type=bind,source="`pwd`/../",target=/build -w /build -it \
@ -35,10 +33,6 @@ 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
@ -51,10 +45,6 @@ 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
@ -64,7 +54,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) make prog_flash
-it $(IMAGE) tillitis-iceprog /build/application_fpga.bin
pull:

View file

@ -4,12 +4,8 @@ Descriptions of the tagged TKey releases.
## Upcoming release: Castor
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.
Overview of changes since TK TK1-24.03 for the Castor milestone so
far.
**Note well**: BREAKING CHANGE! Older device apps WILL NOT WORK.
@ -18,50 +14,38 @@ 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
- Make Security Monitor memory access checks more complete.
- Security Monitor memory access checks are now more complete.
- Add SPI main controller mainly to access the flash chip.
- Add system reset API. Device apps can reset the FPGA and restart
the firmware.
- Add system reset API. Device apps can reset the system and restart
the firmware. The FPGA is not reset.
- 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 minor clean ups of design and testbench.
- Several clean ups and testbench changes.
- Make Verilator simulation work again.
- Add hardware clear to send (CTS) signals for communication between
UART and CH552.
@ -70,19 +54,19 @@ For full change log [see](https://github.com/tillitis/tillitis-key1/compare/TK1-
- Make ROM non-executable in app mode.
- Remove MMIO address for access to the firmware blake2s() function
from apps.
- Remove support 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.
- 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.
- 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.
- Introduce interrupt handler for hardware-based privilege raising and
automatically privelege lowering for system calls.
- Introduce interrupt handler for hardware-based privilege raising for
system calls.
### Firmware
@ -90,65 +74,27 @@ 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 USB endpoints in the USB controller.
different endpoints.
- Support a filesystem on flash: There's space for two pre-loaded
apps and four storage areas for device apps.
- Support a filesystem on flash.
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.
- 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.
- Harmonize with [tkey-libs](https://github.com/tillitis/tkey-libs).
Import tkey-libs to this repo for convenience.
- 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
### CH552
- Use the new CTS signals for communication over the UART.
- Add support for two HID endpoints (security token and our debug
HID).
- Add support for two HID endpoints.
- Add support for CCID endpoint.
- Add a protocol to communicate with the different endpoints: CDC,
CCID, FIDO, debug.
- Add protocol to communicate with the three different endpoints: CDC,
HID, debug.
- Change USB frame sending from a software timer to instead be
controlled by the USB Controller Protocol.
@ -156,15 +102,9 @@ Introduce some device apps mostly for testing.
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>
### 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.
https://github.com/Blinkinlabs/ch55x_programmer
### tkey-builder
@ -187,13 +127,10 @@ the Blinkinlabs CH55x Reset Controller:
- 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.
@ -218,7 +155,6 @@ 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.
@ -230,7 +166,6 @@ 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.
@ -245,35 +180,31 @@ 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
@ -303,8 +234,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
## TK1-23.03
This is the official release of the "Bellatrix" version of
the Tillitis TKey device. This version is ready for general
use.
@ -318,6 +249,7 @@ shasum -a256 application_fpga.bin
f11d6b0f57c5405598206dcfea284008413391a2c51f124a2e2ae8600cb78f0b application_fpga.bin
```
### New and improved functionality
- (ALL) The TKey HW design, FW, protocol and first applications has
@ -381,16 +313,18 @@ 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
### Bugs fixed
- No known bugs have been fixed. Numerous issues has been closed.
### Limitations
- The RAM address and data scrambling in this release is not
cryptographically secure. It his however randomized every time
a TKey device is powered up.
## engineering-release-2
### New and improved functionality

149
doc/toolchain_setup.md Normal file
View file

@ -0,0 +1,149 @@
# 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,7 +134,6 @@ 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
@ -185,11 +184,6 @@ 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
@ -211,8 +205,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: tkey-libs $(FIRMWARE_OBJS) $(P)/fw/tk1/qemu_firmware.lds
$(CC) $(CFLAGS) $(FIRMWARE_OBJS) $(QEMU_LDFLAGS) -o $@ > $(basename $@).map
qemu_firmware.elf: firmware.elf
mv firmware.elf qemu_firmware.elf
# Create compile_commands.json for clangd and LSP
.PHONY: clangd
@ -255,11 +249,11 @@ bram_fw.hex:
$(ICESTORM_PATH)icebram -v -g 32 $(BRAM_FW_SIZE) > $@
firmware.hex: firmware.bin firmware_size_mismatch
python3 $(P)/tools/makehex.py $< $(BRAM_FW_SIZE) > $@
python3 $(P)/tools/makehex/makehex.py $< $(BRAM_FW_SIZE) > $@
simfirmware.hex: simfirmware.bin simfirmware_size_mismatch
python3 $(P)/tools/makehex.py $< $(BRAM_FW_SIZE) > $@
python3 $(P)/tools/makehex/makehex.py $< $(BRAM_FW_SIZE) > $@
testfw.hex: testfw.bin testfw_size_mismatch
python3 $(P)/tools/makehex.py $< $(BRAM_FW_SIZE) > $@
python3 $(P)/tools/makehex/makehex.py $< $(BRAM_FW_SIZE) > $@
.PHONY: check-binary-hashes
check-binary-hashes:
@ -331,7 +325,6 @@ 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
@ -396,7 +389,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 18160564147838858264 \
--seed 9416596747216415304 \
--freq $(TARGET_FREQ) \
--ignore-loops \
--up5k \
@ -472,23 +465,17 @@ tb_application_fpga: $(SIM_VERILOG_SRCS) \
#-------------------------------------------------------------------
prog_flash: check-hardware application_fpga.bin
tillitis-iceprog application_fpga.bin
make -C apps
(cd tools && ./load_preloaded_app.sh 0 ../apps/testloadapp.bin)
sudo tillitis-iceprog application_fpga.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
tillitis-iceprog application_fpga_testfw.bin
sudo tillitis-iceprog application_fpga_testfw.bin
.PHONY: prog_flash_testfw
check-hardware:
@tillitis-iceprog -t >/dev/null 2>&1 || \
@sudo tillitis-iceprog -t >/dev/null 2>&1 || \
{ echo "Programmer not plugged in or not accessible"; false; }
@if tillitis-iceprog -t 2>&1 | grep -qi "^flash.id:\( 0x\(00\|ff\)\)\{4\}"; then \
@if sudo 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
@ -512,7 +499,6 @@ 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:
@ -561,8 +547,7 @@ 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), 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 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,9 +16,8 @@ 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`, except the firmware,
which is under `fw/tk1`. They typically have their own `README.md`
file documenting them and their API in detail.
The rest of the components are under `cores`. 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
@ -39,11 +38,6 @@ 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 @@
dfba361c83337c6bac2364d1fd8f115eeb7feeae5faabbdf0713c79b44bbd78d application_fpga.bin
c770fe25034655241d9e0152b03fcf691c548bc50d30b574a5213abc5b36fe25 application_fpga.bin

View file

@ -1,123 +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) \
-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

@ -1,36 +0,0 @@
# 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

@ -1,18 +0,0 @@
// 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

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

View file

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

View file

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

View file

@ -65,11 +65,10 @@ The different endpoints:
| *Name* | *Value* | *Comment* |
|--------|---------|----------------------------------------------------------------------|
| 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. |
| 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. |
You can turn on and off different endpoints dynamically by sending
commands to the `CH552` control endpoint. When the TKey starts only
@ -163,7 +162,6 @@ stateDiagram-v2
S0 --> S1
S0 --> S4: Default
S0 --> S3
S0 --> SE: Unknown reset type
S1 --> S1: Other commands
S1 --> S2: LOAD_APP
@ -192,8 +190,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 an app from flash and registering it as a
prospective managment app. Allows no commands.
- *LOAD_FLASH_MGMT*: Loading and verifyiing a device app from flash.
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.
@ -208,7 +206,6 @@ Allowed data in state *INITIAL*:
| `FLASH1_VER` | *LOAD_FLASH* |
| `CLIENT` | *WAITCOMMAND* |
| `CLIENT_VER` | *WAITCOMMAND* |
| unknown | *FAIL* |
I/O in state *LOAD_FLASH*:
@ -222,16 +219,15 @@ 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* or unchanged on invalid app size |
| | |
| *command* | *next state* |
|-----------------------|--------------|
| `FW_CMD_NAME_VERSION` | unchanged |
| `FW_CMD_GET_UDI` | unchanged |
| `FW_CMD_LOAD_APP` | *LOADING* |
Commands in state *LOADING*:
Commands in state `loading`:
| *command* | *next state* |
|------------------------|------------------------------------|
@ -248,23 +244,17 @@ Plain text explanation of the states:
- *INITIAL*: Start here. Check the `FW_RAM` for the `resetinfo` type
for what to do next.
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.
For all types which begins with `FLASH_*`, set next state to
*LOAD_FLASH*, otherwise set next state to *WAITCOMMAND*.
- *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
always 0. Compute a BLAKE2s digest over the entire app. Register the
app as a prospective management app. Transition to *START*.
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*.
- *WAITCOMMAND*: Wait for commands from the client. Transition to
*LOADING* on `LOAD_APP` command, which also sets the size of the
@ -275,8 +265,7 @@ 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. This also means that a prospective
management app is now verified.
start is indeed the correct app.
Clean up firmware data structures, enable the system calls, and
start the app, which ends the firmware state machine. Hardware
@ -299,38 +288,29 @@ state.
### Golden path from start to default app
Firmware will load the device application at the start of RAM
Firmware loads the device application at the start of RAM
(`0x4000_0000`) from either flash or from the client through the UART.
Firmware is using a part of the FW\_RAM for its own stack.
Firmware uses 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,
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.
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).
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
1. 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.
4. Load app data from flash slot 0 into RAM.
2. Load app data from flash slot 0 into RAM.
5. Compute a BLAKE2s digest of the loaded app.
3. Compute a BLAKE2s digest of the loaded app.
6. Compare the computed digest against the allowed app digest
4. 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).
@ -391,9 +371,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 use
`PRELOAD_DELETE`, `PRELOAD_STORE`, `PRELOAD_STORE_FIN`, and
`PRELOAD_GET_DIGSIG`.
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`.
It works like this:
@ -433,7 +413,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 number of keys it
material. Depending on the app's behaviour and the numer of keys it
needs it can derive more keys, for instance by having nonces stored on
its flash area and doing:
@ -553,9 +533,8 @@ struct reset {
};
struct reset rst;
uint32_t len; // Length of data in next_app_data.
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, len, 0);
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
```
Resets the TKey. Does not return.
@ -564,7 +543,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 reset are defined in `reset.h`:
The types of the reset are defined in `resetinfo.h`:
| *Name* | *Comment* |
|--------------------|------------------------------------------------|
@ -598,15 +577,12 @@ success.
uint32_t offset = 0;
uint8_t buf[17];
syscall(TK1_SYSCALL_WRITE_DATA, offset, (uint32_t)buf, sizeof(buf))
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`
```
@ -618,20 +594,6 @@ 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`
```
@ -653,14 +615,9 @@ 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 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.
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.
Only available for the verified management app.
@ -679,8 +636,7 @@ 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 a BLAKE2s hash digest over the entire binary. Pass the result
in `app_digest`.
Compute the `app_digest` with BLAKE2s over the entire binary.
Sign `app_digest` with your Ed25519 private key and pass the
resulting signature in `app_signature`.
@ -737,9 +693,6 @@ 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
@ -768,27 +721,7 @@ initiated before starting for the first time. You need a [TKey
Programmer Board](https://shop.tillitis.se/products/tkey-dev-kit) for
this part.
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
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`.
@ -808,10 +741,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/tkeyimage`, typically:
`tools/partition_table`, typically:
```
$ tkeyimage -app0 path/to/your/app.bin -o default_partition.bin
$ partition_table -app0 path/to/your/app.bin -o default_partition.bin
```
4. Flash the filesystem image:
@ -835,5 +768,19 @@ ordinary `application_fpga/Makefile` to be able to fit in ROM.
### Test apps
There are a couple of test apps, see `../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:
```
$ 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

@ -0,0 +1,75 @@
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

@ -0,0 +1,64 @@
/*
* 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

@ -0,0 +1,53 @@
// 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,11 +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 <syscall.h>
#include <tkey/assert.h>
#include <tkey/debug.h>
#include <tkey/io.h>
@ -13,6 +11,11 @@
#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 "../fw/tk1/picorv32/custom_ops.S"
#include "../tk1/picorv32/custom_ops.S"
.section ".text"
.globl syscall

View file

@ -0,0 +1,74 @@
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

@ -0,0 +1,64 @@
/*
* 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

@ -0,0 +1,53 @@
// 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,9 +1,8 @@
// 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>
@ -11,6 +10,9 @@
#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
@ -18,7 +20,6 @@
// 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;
@ -97,9 +98,6 @@ 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);
@ -126,8 +124,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 != 0x073570c0) {
failmsg("Expected VID/PID to be 0x073570c0");
if (vidpid != 0x00010203) {
failmsg("Expected VID/PID to be 0x00010203");
anyfailed = 1;
}
@ -147,7 +145,7 @@ int main(void)
}
puts(IO_CDC, "done.\r\n");
puts(IO_CDC, "\r\nReading data from storage area...");
puts(IO_CDC, "\r\nReading from storage area...");
uint8_t in_data[14] = {0};
if (syscall(TK1_SYSCALL_READ_DATA, 0, (uint32_t)in_data,
@ -160,28 +158,6 @@ 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) {
@ -214,8 +190,8 @@ int main(void)
}
puts(IO_CDC, "\r\nTesting timer... 3");
// Matching clock at 24 MHz, giving us timer in seconds
*timer_prescaler = 24 * 1000000;
// Matching clock at 21 MHz, giving us timer in seconds
*timer_prescaler = 21 * 1000000;
// Test timer expiration after 1s
*timer = 1;

View file

@ -0,0 +1,85 @@
// 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,5 +1,7 @@
// 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,5 +1,7 @@
// 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
@ -36,7 +38,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:
@ -44,9 +46,9 @@ clear:
addi a0, a0, 4
blt a0, a1, clear
// NOTE WELL
// For testfw we init stack at top of RAM
//
/*
* For testfw we init stack at top of RAM
*/
li sp, 0x40020000 // TK1_RAM_BASE + TK1_RAM_SIZE
call main

View file

@ -0,0 +1,73 @@
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

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

View file

@ -1,19 +1,15 @@
// 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 "syscall.h"
#include "tkey/assert.h"
// clang-format off
static volatile uint32_t *cdi = (volatile uint32_t *) TK1_MMIO_TK1_CDI_FIRST;
@ -154,11 +150,7 @@ void reset_from_client(void)
rst.type = START_CLIENT;
// 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);
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
}
int main(void)
@ -169,8 +161,6 @@ 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

@ -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,9 +41,8 @@ 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,12 +10,36 @@
#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;
@ -34,8 +58,9 @@ static bool flash_is_busy(void)
// Blocking until !busy
static void flash_wait_busy(void)
{
while (flash_is_busy())
;
while (flash_is_busy()) {
delay(10);
}
}
static void flash_write_enable(void)
@ -176,11 +201,7 @@ int flash_write_data(uint32_t address, uint8_t *data, size_t size)
return -1;
}
if (size <= 0) {
return -1;
}
if (address % 256 != 0) {
if (size <= 0 || size > 4096) {
return -1;
}
@ -188,12 +209,6 @@ 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,5 +1,7 @@
// 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>
@ -15,7 +17,7 @@
#include "partition_table.h"
#include "preload_app.h"
#include "proto.h"
#include "reset.h"
#include "resetinfo.h"
#include "state.h"
#include "syscall_enable.h"
@ -36,7 +38,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;
@ -250,8 +252,6 @@ static enum state initial_commands(const struct frame_header *hdr,
ctx->left = *app_size;
led_set(LED_BLACK);
state = FW_STATE_LOADING;
break;
}
@ -444,19 +444,7 @@ static enum state start_where(struct context *ctx)
{
assert(ctx != NULL);
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?
// Where do we start? Read resetinfo 'startfrom'
switch (resetinfo->type) {
case START_DEFAULT:
// fallthrough
@ -508,6 +496,8 @@ int main(void)
uint8_t cmd[CMDSIZE] = {0};
enum state state = FW_STATE_INITIAL;
led_set(LED_BLUE);
print_hw_version();
/*@-mustfreeonly@*/
@ -521,17 +511,10 @@ int main(void)
scramble_ram();
if (part_table_read(&part_table_storage) != 0) {
// Couldn't read partition table
debug_puts("Couldn't read partition table\n");
// Couldn't read or create partition table
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
@ -584,7 +567,6 @@ int main(void)
if (mgmt_app_init(ctx.digest) != 0) {
state = FW_STATE_FAIL;
break;
}
state = FW_STATE_START;
@ -622,7 +604,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,14 +11,11 @@
// 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] = {
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,
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,
};
// clang-format on
static uint8_t current_app_digest[32];
@ -34,7 +31,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,20 +17,22 @@ enum part_status part_get_status(void)
return part_status;
}
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)
static void part_digest(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, NULL, 0, part_table,
blake2err = blake2s(out_digest, out_len, key, sizeof(key), part_table,
sizeof(struct partition_table));
assert(blake2err == 0);
@ -48,7 +50,7 @@ int part_table_read(struct partition_table_storage *storage)
ADDR_PARTITION_TABLE_0,
ADDR_PARTITION_TABLE_1,
};
uint8_t check_digest[PART_CHECKSUM_SIZE] = {0};
uint8_t check_digest[PART_DIGEST_SIZE] = {0};
if (storage == NULL) {
return -1;
@ -62,10 +64,10 @@ int part_table_read(struct partition_table_storage *storage)
sizeof(*storage)) != 0) {
return -1;
}
part_checksum(&storage->table, check_digest,
sizeof(check_digest));
part_digest(&storage->table, check_digest,
sizeof(check_digest));
if (memeq(check_digest, storage->checksum,
if (memeq(check_digest, storage->check_digest,
sizeof(check_digest))) {
if (i == 1) {
part_status = PART_SLOT0_INVALID;
@ -89,8 +91,8 @@ int part_table_write(struct partition_table_storage *storage)
return -1;
}
part_checksum(&storage->table, storage->checksum,
sizeof(storage->checksum));
part_digest(&storage->table, storage->check_digest,
sizeof(storage->check_digest));
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,33 +46,30 @@
#define SIZE_STORAGE_AREA 0x20000UL // 128KiB
#define N_STORAGE_AREA 4
#define PART_CHECKSUM_SIZE 32
#define PART_DIGEST_SIZE 16
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.
//
// - Checksum over the above
/* 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. */
struct auth_metadata {
uint8_t nonce[16];
@ -102,7 +99,7 @@ struct partition_table {
struct partition_table_storage {
struct partition_table table;
uint8_t checksum[PART_CHECKSUM_SIZE]; // Helps detect flash problems
uint8_t check_digest[PART_DIGEST_SIZE];
} __attribute__((packed));
enum part_status part_get_status(void);

View file

@ -2,11 +2,6 @@
## custom_ops.S
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.
Custom PicoRV32 instructions are located in `custom_ops.S`.
`custom_ops.S` is imported from upstream PicoRV32 commit:
YosysHQ/picorv32@70f3c33

View file

@ -1,6 +1,3 @@
// 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,72 +18,58 @@ 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 -1;
return -5;
}
if (from_slot >= N_PRELOADED_APP) {
return -1;
return -4;
}
// 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) {
/*Check for a valid app in flash */
if (part_table->pre_app_data[from_slot].size == 0) {
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;
}
// 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.
/* 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.
* */
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 -1;
return -5;
}
if (to_slot >= N_PRELOADED_APP) {
return -1;
return -4;
}
// Check if we are allowed to store
/* Check if we are allowed to store */
if (!mgmt_app_authenticate()) {
return -1;
return -3;
}
// 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_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;
if ((offset + size) > SIZE_PRE_LOADED_APP || size > 4096) {
/* Writing outside of area */
return -2;
}
uint32_t address = slot_to_start_address(to_slot) + offset;
@ -101,38 +87,26 @@ int preload_store_finalize(struct partition_table_storage *part_table_storage,
{
struct partition_table *part_table = &part_table_storage->table;
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 (part_table == NULL || app_digest == NULL || app_signature == NULL) {
return -5;
}
if (to_slot >= N_PRELOADED_APP) {
return -1;
return -4;
}
// Check if we are allowed to store
/* Check if we are allowed to store */
if (!mgmt_app_authenticate()) {
return -1;
return -3;
}
// 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 -1;
return -2;
}
part_table->pre_app_data[to_slot].size = app_size;
@ -147,7 +121,7 @@ int preload_store_finalize(struct partition_table_storage *part_table_storage,
debug_lf();
if (part_table_write(part_table_storage) != 0) {
return -1;
return -6;
}
return 0;
@ -159,19 +133,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 -1;
return -5;
}
if (slot >= N_PRELOADED_APP) {
return -1;
return -4;
}
// Check if we are allowed to delete
/* Check if we are allowed to deleted */
if (!mgmt_app_authenticate()) {
return -1;
return -3;
}
// 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;
@ -186,10 +160,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 -1;
return -6;
}
// 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) +
@ -203,16 +177,16 @@ int preload_get_digsig(struct partition_table *part_table,
uint8_t slot)
{
if (part_table == NULL || app_digest == NULL || app_signature == NULL) {
return -1;
return -5;
}
if (slot >= N_PRELOADED_APP) {
return -1;
return -4;
}
// Check if we are allowed to read
/* Check if we are allowed to read */
if (!mgmt_app_authenticate()) {
return -1;
return -3;
}
memcpy_s(app_digest, 32, part_table->pre_app_data[slot].digest,

View file

@ -1,5 +1,7 @@
// 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,5 +1,7 @@
// 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,87 +0,0 @@
/*
* 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

@ -1,59 +0,0 @@
// 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,16 +1,13 @@
// Copyright (C) 2025 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
#ifndef TKEY_RESET_H
#define TKEY_RESET_H
#ifndef TKEY_RESETINFO_H
#define TKEY_RESETINFO_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
@ -23,11 +20,9 @@ enum reset_start {
};
struct reset {
enum reset_start type;
uint8_t app_digest[RESET_DIGEST_SIZE];
uint8_t next_app_data[RESET_DATA_SIZE];
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
};
int reset(struct reset *userreset, size_t nextlen);
int reset_data(uint8_t *next_app_data);
#endif

View file

@ -10,7 +10,8 @@
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,5 +1,7 @@
// 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>
@ -26,7 +28,9 @@
_start:
j init
// IRQ handler
/*
* IRQ handler
*/
.=0x10
irq_handler:
// PicoRV32 stores the IRQ bitmask in x4.
@ -108,8 +112,9 @@ x3_valid:
picorv32_retirq_insn() // Return from interrupt
// Init
/*
* Init
*/
.=0x100
init:
li x1, 0
@ -144,7 +149,7 @@ init:
li x30,0
li x31,0
// Clear FW_RAM
/* Clear FW_RAM */
la a0, _sfwram
la a1, _efwram
clear:
@ -152,7 +157,7 @@ clear:
addi a0, a0, 4
blt a0, a1, clear
// Zero-init bss section
/* Zero-init bss section */
la a0, _sbss
la a1, _ebss
@ -161,7 +166,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,5 +1,7 @@
// 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,20 +6,18 @@
#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.
//
// Returns -1 on errors.
/* Returns the index of the first empty area. If there is no empty area -1 is
* returned. */
static int get_first_empty(struct partition_table *part_table)
{
if (part_table == NULL) {
return -1;
return -4;
}
for (uint8_t i = 0; i < N_STORAGE_AREA; i++) {
@ -27,14 +25,13 @@ 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 -1;
return -4;
}
if ((index < 0) || (index >= N_STORAGE_AREA)) {
@ -46,13 +43,12 @@ static int index_to_address(int index, uint32_t *address)
return 0;
}
// Returns the index of the area an app has allocated.
//
// Returns -1 on errors.
/* Returns the index of the area an app has allocated. If no area is
* authenticated -1 is returned. */
static int storage_get_area(struct partition_table *part_table)
{
if (part_table == NULL) {
return -1;
return -4;
}
for (uint8_t i = 0; i < N_STORAGE_AREA; i++) {
@ -63,85 +59,85 @@ static int storage_get_area(struct partition_table *part_table)
}
}
}
return -1;
}
// Allocate a new area for an app. Returns zero on success.
/* 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. */
int storage_allocate_area(struct partition_table_storage *part_table_storage)
{
if (part_table_storage == NULL) {
return -1;
return -4;
}
struct partition_table *part_table = &part_table_storage->table;
if (storage_get_area(part_table) != -1) {
/* Already has an area */
return 0;
return 1;
}
int index = get_first_empty(part_table);
if (index < 0) {
if (index == -1) {
/* No empty slot */
return -1;
}
uint32_t start_address = 0;
if (index_to_address(index, &start_address) != 0) {
return -1;
int err = index_to_address(index, &start_address);
if (err) {
return -3;
}
// 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 -1;
return -5;
}
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 -1;
return -4;
}
struct partition_table *part_table = &part_table_storage->table;
int index = storage_get_area(part_table);
if (index < 0) {
// No area to deallocate
if (index == -1) {
/* No area to deallocate */
return -1;
}
uint32_t start_address = 0;
if (index_to_address(index, &start_address) != 0) {
return -1;
int err = index_to_address(index, &start_address);
if (err) {
return -3;
}
// 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,
@ -152,51 +148,46 @@ 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 -1;
return -5;
}
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 -1;
return -4;
}
int index = storage_get_area(part_table);
if (index == -1) {
// No allocated area
/* No allocated area */
return -1;
}
uint32_t start_address = 0;
if (index_to_address(index, &start_address) != 0) {
return -1;
int err = index_to_address(index, &start_address);
if (err) {
return -3;
}
if (offset > SIZE_STORAGE_AREA) {
return -1;
}
// Cannot only erase entire sectors
/* Cannot only erase entire sectors */
if (offset % 4096 != 0) {
return -1;
return -2;
}
// Cannot erase less than one sector
/* Cannot erase less than one sector */
if (size < 4096 || size > SIZE_STORAGE_AREA || size % 4096 != 0) {
return -1;
return -2;
}
if ((offset + size) > SIZE_STORAGE_AREA) {
return -1;
if ((offset + size) >= SIZE_STORAGE_AREA) {
return -2;
}
uint32_t address = start_address + offset;
@ -213,45 +204,32 @@ 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. Offset must be a multiple of 256.
//
// Returns zero on success.
/* 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. */
int storage_write_data(struct partition_table *part_table, uint32_t offset,
uint8_t *data, size_t size)
{
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;
if (part_table == NULL || data == NULL) {
return -4;
}
int index = storage_get_area(part_table);
if (index == -1) {
// No allocated area
/* No allocated area */
return -1;
}
uint32_t start_address = 0;
if (index_to_address(index, &start_address) != 0) {
return -1;
int err = index_to_address(index, &start_address);
if (err) {
return -3;
}
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;
if ((offset + size) > SIZE_STORAGE_AREA || size > 4096) {
/* Writing outside of area */
return -2;
}
uint32_t address = start_address + offset;
@ -263,48 +241,31 @@ 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.
//
// Only read limit is the size of the allocated area.
//
// Returns zero on success.
/* 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 */
int storage_read_data(struct partition_table *part_table, uint32_t offset,
uint8_t *data, size_t size)
{
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;
if (part_table == NULL || data == NULL) {
return -4;
}
int index = storage_get_area(part_table);
if (index == -1) {
// No allocated area
/* No allocated area */
return -1;
}
uint32_t start_address = 0;
if (index_to_address(index, &start_address) != 0) {
return -1;
}
if (offset > SIZE_STORAGE_AREA) {
return -1;
}
if (size > 4096) {
return -1;
int err = index_to_address(index, &start_address);
if (err) {
return -3;
}
if ((offset + size) > SIZE_STORAGE_AREA) {
// Reading outside of area
return -1;
/* Reading outside of area */
return -2;
}
uint32_t address = start_address + offset;

View file

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

View file

@ -1,5 +1,7 @@
// 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>
@ -9,12 +11,15 @@
#include "partition_table.h"
#include "preload_app.h"
#include "reset.h"
#include "storage.h"
#include "syscall_num.h"
#include "../tk1/resetinfo.h"
#include "../tk1/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;
@ -23,9 +28,24 @@ 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:
return reset((struct reset *)arg1, (size_t)arg2);
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);
break;
case TK1_SYSCALL_ALLOC_AREA:
@ -33,39 +53,31 @@ 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;
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;
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;
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
@ -99,10 +111,6 @@ 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,7 +18,6 @@ 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,7 +32,6 @@ 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,22 +10,20 @@
// I/O endpoints. Keep it as bits possible to use in a bitmask in
// readselect().
//
// 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.
// Note that the DEBUG, CDC, and FIDO should be kept the same on
// 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 = 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
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
};
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,20 +74,15 @@ 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_CH552: Internal communication between the FPGA and the
// CH552, with header.
// - IO_UART: Low-level UART access, no USB Mode Header added.
//
// - IO_CDC: Through the UART for the CDC endpoint, with header.
//
// - IO_FIDO: Through the UART for the FIDO endpoint, with header.
//
// - IO_CCID: Through the UART for the CCID endpoint, with header.
//
// - IO_DEBUG: Through the UART for the DEBUG endpoint (USB HID), with
// - IO_DEBUG: Through the UART for the DEBUG HID endpoint, with
// header.
void write(enum ioend dest, const uint8_t *buf, size_t nbytes)
{
@ -208,11 +203,9 @@ static int discard(size_t nbytes)
//
// Only endpoints available for read are:
//
// - IO_CH552
// - IO_DEBUG
// - IO_CDC
// - IO_FIDO
// - IO_CCID
// - IO_DEBUG
//
// If you need blocking low-level UART reads, use uart_read() instead.
//
@ -359,8 +352,7 @@ void hexdump(enum ioend dest, void *buf, int len)
// Configure USB endpoints that should be enabled/disabled
//
// Allowed options are:
// - IO_FIDO (can't be used used together with IO_CCID)
// - IO_CCID (can't be used used together with IO_FIDO)
// - IO_FIDO
// - IO_DEBUG
//
// The following are always enabled:

View file

@ -1,33 +0,0 @@
# 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%02x, ", n)
fmt.Printf("0x%x, ", n)
}
fmt.Printf("\n}; \n")

View file

@ -0,0 +1,28 @@
#!/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,7 +1,5 @@
#!/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,8 +1,5 @@
#!/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,4 +1,4 @@
module tkeyimage
module partition_table
go 1.23.0

View file

@ -0,0 +1,172 @@
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

@ -0,0 +1,15 @@
#!/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,8 +1,5 @@
#!/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

@ -1,114 +0,0 @@
# 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,266 +0,0 @@
// 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,20 +51,16 @@ def format_descriptor(name, value):
if __name__ == "__main__":
strings = {
"ProdDesc": "Tillitis TKEY-USB-V2",
"ProdDesc": "MTA1-USB-V1",
"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,13 +94,6 @@ 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
@ -207,8 +200,7 @@ 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_CCID_STR 0x07
#define USB_IDX_INTERFACE_DEBUG_STR 0x08
#define USB_IDX_INTERFACE_DEBUG_STR 0x07
#endif
#ifndef USB_DEVICE_ADDR

View file

@ -8,16 +8,14 @@ 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 = 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
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
};
enum ch552cmd {
SET_ENDPOINTS = 0x01, // Config USB endpoints on the CH552
CH552_CMD_MAX,
SET_ENDPOINTS = 0x01, // Config enabled/disabled USB endpoints on the CH552
};
#endif

View file

@ -6,14 +6,12 @@
#include "mem.h"
unsigned char FLASH ProdDesc[] = { // "Tillitis TKEY-USB-V2"
42, // Length of this descriptor (in bytes)
unsigned char FLASH ProdDesc[] = { // "MTA1-USB-V1"
24, // Length of this descriptor (in bytes)
0x03, // Descriptor type (String)
'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,
'M', 0, 'T', 0, 'A', 0, '1', 0,
'-', 0, 'U', 0, 'S', 0, 'B', 0,
'-', 0, 'V', 0, '1', 0,
};
unsigned char FLASH ManufDesc[] = { // "Tillitis"
@ -57,12 +55,6 @@ 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