mirror of
https://github.com/tillitis/tillitis-key1.git
synced 2025-07-14 10:49:35 -04:00
Compare commits
154 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a0f699aea5 | ||
![]() |
62adf4da71 | ||
![]() |
1a904e8857 | ||
![]() |
03d96c3e96 | ||
![]() |
c0b3c80620 | ||
![]() |
460d310c73 | ||
![]() |
f4f8c9e6c6 | ||
![]() |
ed9395c832 | ||
![]() |
888f18e5fe | ||
![]() |
24ef7b412b | ||
![]() |
f5d2cfef15 | ||
![]() |
9a93da087d | ||
![]() |
916c37eab9 | ||
![]() |
29e5888482 | ||
![]() |
e8acc7aee2 | ||
![]() |
4172db8dfb | ||
![]() |
7b1c1e5076 | ||
![]() |
1fec28ff0d | ||
![]() |
8f9c706b9e | ||
![]() |
07e487733b | ||
![]() |
ba17a2b29e | ||
![]() |
6e3034c3ce | ||
![]() |
e302910f4d | ||
![]() |
13641cb18b | ||
![]() |
69940d2c64 | ||
![]() |
4ec58ce04c | ||
![]() |
a1f37d17c9 | ||
![]() |
fab126b695 | ||
![]() |
9a17aa6bdb | ||
![]() |
0d6e1d9ba5 | ||
![]() |
ea29843037 | ||
![]() |
6afdc114b8 | ||
![]() |
2556f61f5a | ||
![]() |
b144cdfbdb | ||
![]() |
8965fea947 | ||
![]() |
daa7807c0f | ||
![]() |
53bc2d5fa0 | ||
![]() |
5a9b77806f | ||
![]() |
887883c8db | ||
![]() |
2dce9828ea | ||
![]() |
a2b77ec348 | ||
![]() |
9a3b4b9dca | ||
![]() |
48108cb3a2 | ||
![]() |
e935195846 | ||
![]() |
14e4cd09c9 | ||
![]() |
ec9ef31140 | ||
![]() |
6745c56851 | ||
![]() |
fea9df790d | ||
![]() |
8cf2cd08b7 | ||
![]() |
d83d659284 | ||
![]() |
4f4de4a07d | ||
![]() |
f373ad3f68 | ||
![]() |
9d1bbffbaa | ||
![]() |
0692dddbae | ||
![]() |
15a350da1e | ||
![]() |
edbcdb111f | ||
![]() |
3e8ff9671c | ||
![]() |
66ea8df1d9 | ||
![]() |
106a7a5613 | ||
![]() |
49d5a26a77 | ||
![]() |
632b6d8fc7 | ||
![]() |
506b4c8269 | ||
![]() |
9c1bb53d7a | ||
![]() |
a9d3dd7242 | ||
![]() |
3be9e8ab19 | ||
![]() |
d7ddae42d0 | ||
![]() |
18773cdcf2 | ||
![]() |
25f3300964 | ||
![]() |
c1902c0955 | ||
![]() |
528f997681 | ||
![]() |
73ea180b2a | ||
![]() |
6324da2c90 | ||
![]() |
7511e98abe | ||
![]() |
8c091d9719 | ||
![]() |
ce97682758 | ||
![]() |
e37985938d | ||
![]() |
c5c6230664 | ||
![]() |
49c06d78d1 | ||
![]() |
2c1c05f180 | ||
![]() |
4841b1b127 | ||
![]() |
1be5140850 | ||
![]() |
353d7e9f50 | ||
![]() |
f75620720f | ||
![]() |
fb1269b06e | ||
![]() |
770acc9b38 | ||
![]() |
d0c049cdba | ||
![]() |
7eca72c2c3 | ||
![]() |
d43585ee1a | ||
![]() |
d94387a9e7 | ||
![]() |
7de49d2021 | ||
![]() |
8d8f4c7faf | ||
![]() |
33f14122ad | ||
![]() |
435b1f9d29 | ||
![]() |
16a9e8c367 | ||
![]() |
3dbc31f54c | ||
![]() |
cd1a089763 | ||
![]() |
1d5d721f1e | ||
![]() |
a41360917a | ||
![]() |
b524cd0d6e | ||
![]() |
ad62f6e48f | ||
![]() |
c52442b54c | ||
![]() |
7554787678 | ||
![]() |
77fc5cf578 | ||
![]() |
9e317666d3 | ||
![]() |
df04fd56dd | ||
![]() |
13f40561ab | ||
![]() |
4ba164732d | ||
![]() |
fed9354fe9 | ||
![]() |
d82c3a706e | ||
![]() |
969df46315 | ||
![]() |
0ee971e38c | ||
![]() |
9c0311cdfc | ||
![]() |
d1abaad5da | ||
![]() |
4363637afa | ||
![]() |
5eb020275b | ||
![]() |
24ef39b739 | ||
![]() |
97de5e68fd | ||
![]() |
19ae709c81 | ||
![]() |
dd48b77047 | ||
![]() |
03c0ca7c86 | ||
![]() |
b1047b3618 | ||
![]() |
0b829cc9ee | ||
![]() |
46ef63ee2d | ||
![]() |
0b75d25431 | ||
![]() |
8f2f312531 | ||
![]() |
3126a9c51e | ||
![]() |
9a301403e1 | ||
![]() |
de32c58355 | ||
![]() |
b7ce031bd6 | ||
![]() |
d2c7fb0ba9 | ||
![]() |
179c13e9bf | ||
![]() |
050e0f2673 | ||
![]() |
aedd6102ea | ||
![]() |
f68414c4aa | ||
![]() |
75ad033e03 | ||
![]() |
05bb999759 | ||
![]() |
81ac7bffa0 | ||
![]() |
bb18d5b9e9 | ||
![]() |
8ed16fff6a | ||
![]() |
c292595ee3 | ||
![]() |
361890042a | ||
![]() |
5029eb1d39 | ||
![]() |
04ec938200 | ||
![]() |
bfc43093ec | ||
![]() |
07dc20e4e1 | ||
![]() |
0a634c76da | ||
![]() |
ab4ef5fdf9 | ||
![]() |
f3706dcfcc | ||
![]() |
a0c031eb25 | ||
![]() |
1b9bbc4eba | ||
![]() |
b443359e9c | ||
![]() |
90fca5d3dd | ||
![]() |
0af82ee566 | ||
![]() |
a5ed3cfaa9 |
166 changed files with 14869 additions and 2831 deletions
41
.github/workflows/ci.yaml
vendored
41
.github/workflows/ci.yaml
vendored
|
@ -10,10 +10,10 @@ on:
|
|||
workflow_dispatch: {}
|
||||
|
||||
jobs:
|
||||
check-firmware:
|
||||
check-formatting:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/tillitis/tkey-builder:4
|
||||
image: ghcr.io/tillitis/tkey-builder:5rc1
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
@ -26,11 +26,29 @@ jobs:
|
|||
run: |
|
||||
git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||
|
||||
- name: check indentation in firmware C code
|
||||
- name: check formatting on Verilog and C
|
||||
working-directory: hw/application_fpga
|
||||
run: |
|
||||
make -C fw/tk1 checkfmt
|
||||
make -C fw/testfw checkfmt
|
||||
make checkfmt
|
||||
|
||||
- name: check for SPDX tags
|
||||
run: ./LICENSES/spdx-ensure
|
||||
|
||||
check-firmware:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/tillitis/tkey-builder:5rc1
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
# fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: fix
|
||||
# https://github.com/actions/runner-images/issues/6775
|
||||
run: |
|
||||
git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||
|
||||
- name: run static analysis on firmware C code
|
||||
working-directory: hw/application_fpga
|
||||
|
@ -44,7 +62,7 @@ jobs:
|
|||
check-verilog:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/tillitis/tkey-builder:4
|
||||
image: ghcr.io/tillitis/tkey-builder:5rc1
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
@ -64,7 +82,7 @@ jobs:
|
|||
build-usb-firmware:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/tillitis/tkey-builder:4
|
||||
image: ghcr.io/tillitis/tkey-builder:5rc1
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
@ -86,7 +104,7 @@ jobs:
|
|||
commit_sha: ${{ github.sha }}
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/tillitis/tkey-builder:4
|
||||
image: ghcr.io/tillitis/tkey-builder:5rc1
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
@ -115,7 +133,7 @@ jobs:
|
|||
needs: build-bitstream
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/tillitis/tkey-builder:4
|
||||
image: ghcr.io/tillitis/tkey-builder:5rc1
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
@ -133,8 +151,3 @@ jobs:
|
|||
- name: check matching hashes for firmware.bin & application_fpga.bin
|
||||
working-directory: hw/application_fpga
|
||||
run: make check-binary-hashes
|
||||
|
||||
|
||||
# TODO? first deal with hw/boards/ and hw/production_test/
|
||||
# - name: check for SPDX tags
|
||||
# run: ./LICENSES/spdx-ensure
|
||||
|
|
9
.gitignore
vendored
9
.gitignore
vendored
|
@ -28,6 +28,12 @@
|
|||
/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/tools/b2s/b2s
|
||||
synth.json
|
||||
synth.txt
|
||||
synth.v
|
||||
|
@ -40,6 +46,7 @@ verilated/
|
|||
*.o
|
||||
*.asc
|
||||
*.bin
|
||||
!/hw/application_fpga/tools/default_partition.bin
|
||||
*.elf
|
||||
*.map
|
||||
*.tmp
|
||||
|
@ -49,3 +56,5 @@ verilated/
|
|||
.*.swp
|
||||
*.sch-bak
|
||||
*.sim
|
||||
compile_commands.json
|
||||
.cache
|
||||
|
|
|
@ -2,11 +2,13 @@
|
|||
|
||||
## Main license
|
||||
|
||||
Unless otherwise noted, the project sources are licensed under the
|
||||
terms and conditions of the "GNU General Public License v2.0 only".
|
||||
Unless otherwise noted, the project sources are copyright Tillitis AB,
|
||||
but you can redistribute it and/or modify it under the terms of the
|
||||
GNU General Public License version 2 as published by the Free Software
|
||||
Foundation. See `gpl-2.0.txt`.
|
||||
|
||||
The `LICENSES/` directory contains copies of the license texts used by
|
||||
the sources included in the project source tree.
|
||||
This directory contains copies of the license texts used by the
|
||||
sources included in the project source tree.
|
||||
|
||||
## SPDX
|
||||
|
||||
|
@ -19,3 +21,21 @@ The current set of valid, predefined SPDX identifiers can be found on
|
|||
the SPDX License List at:
|
||||
|
||||
https://spdx.org/licenses/
|
||||
|
||||
## Notable imported projects
|
||||
|
||||
- ch552 firmware: `hw/usb_interface/ch552_fw/`
|
||||
|
||||
Originally by WCH under MIT. Much changed by Tillitis.
|
||||
|
||||
- picorv32: `hw/application_fpga/core/picorv32`
|
||||
|
||||
From https://github.com/YosysHQ/picorv32
|
||||
|
||||
ISC.
|
||||
|
||||
- PicoRV32 custom ops: `hw/application_fpga/fw/tk1/picorv32/`
|
||||
|
||||
- tkey-libs: `hw/application_fpga/tkey-libs/`
|
||||
|
||||
BSD2. From https://github.com/tillitis/tkey-libs
|
||||
|
|
338
LICENSES/gpl-2.0.txt
Normal file
338
LICENSES/gpl-2.0.txt
Normal file
|
@ -0,0 +1,338 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
<https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Moe Ghoul>, 1 April 1989
|
||||
Moe Ghoul, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
|
@ -1,245 +0,0 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
|
||||
Version 2, June 1991
|
||||
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your freedom to share and
|
||||
change it. By contrast, the GNU General Public License is intended to guarantee your
|
||||
freedom to share and change free software--to make sure the software is free for all
|
||||
its users. This General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to using it.
|
||||
(Some other Free Software Foundation software is covered by the GNU Lesser General
|
||||
Public License instead.) You can apply it to your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not price. Our General
|
||||
Public Licenses are designed to make sure that you have the freedom to distribute
|
||||
copies of free software (and charge for this service if you wish), that you receive
|
||||
source code or can get it if you want it, that you can change the software or use
|
||||
pieces of it in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid anyone to deny you
|
||||
these rights or to ask you to surrender the rights. These restrictions translate to
|
||||
certain responsibilities for you if you distribute copies of the software, or if you
|
||||
modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether gratis or for a
|
||||
fee, you must give the recipients all the rights that you have. You must make sure
|
||||
that they, too, receive or can get the source code. And you must show them these
|
||||
terms so they know their rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and (2) offer you
|
||||
this license which gives you legal permission to copy, distribute and/or modify the
|
||||
software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain that everyone
|
||||
understands that there is no warranty for this free software. If the software is
|
||||
modified by someone else and passed on, we want its recipients to know that what
|
||||
they have is not the original, so that any problems introduced by others will not
|
||||
reflect on the original authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software patents. We wish to
|
||||
avoid the danger that redistributors of a free program will individually obtain
|
||||
patent licenses, in effect making the program proprietary. To prevent this, we have
|
||||
made it clear that any patent must be licensed for everyone's free use or not
|
||||
licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and modification follow.
|
||||
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains a notice placed
|
||||
by the copyright holder saying it may be distributed under the terms of this General
|
||||
Public License. The "Program", below, refers to any such program or work, and a
|
||||
"work based on the Program" means either the Program or any derivative work under
|
||||
copyright law: that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another language.
|
||||
(Hereinafter, translation is included without limitation in the term
|
||||
"modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not covered by this
|
||||
License; they are outside its scope. The act of running the Program is not
|
||||
restricted, and the output from the Program is covered only if its contents
|
||||
constitute a work based on the Program (independent of having been made by running
|
||||
the Program). Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and appropriately publish
|
||||
on each copy an appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any warranty; and
|
||||
give any other recipients of the Program a copy of this License along with the
|
||||
Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and you may at
|
||||
your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion of it, thus
|
||||
forming a work based on the Program, and copy and distribute such modifications or
|
||||
work under the terms of Section 1 above, provided that you also meet all of these
|
||||
conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices stating that you
|
||||
changed the files and the date of any change.
|
||||
b) You must cause any work that you distribute or publish, that in whole or in
|
||||
part contains or is derived from the Program or any part thereof, to be licensed
|
||||
as a whole at no charge to all third parties under the terms of this License.
|
||||
c) If the modified program normally reads commands interactively when run, you
|
||||
must cause it, when started running for such interactive use in the most
|
||||
ordinary way, to print or display an announcement including an appropriate
|
||||
copyright notice and a notice that there is no warranty (or else, saying that
|
||||
you provide a warranty) and that users may redistribute the program under these
|
||||
conditions, and telling the user how to view a copy of this License. (Exception:
|
||||
if the Program itself is interactive but does not normally print such an
|
||||
announcement, your work based on the Program is not required to print an
|
||||
announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If identifiable sections
|
||||
of that work are not derived from the Program, and can be reasonably considered
|
||||
independent and separate works in themselves, then this License, and its terms, do
|
||||
not apply to those sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based on the
|
||||
Program, the distribution of the whole must be on the terms of this License, whose
|
||||
permissions for other licensees extend to the entire whole, and thus to each and
|
||||
every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest your rights to
|
||||
work written entirely by you; rather, the intent is to exercise the right to control
|
||||
the distribution of derivative or collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program with the
|
||||
Program (or with a work based on the Program) on a volume of a storage or
|
||||
distribution medium does not bring the other work under the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it, under Section 2)
|
||||
in object code or executable form under the terms of Sections 1 and 2 above provided
|
||||
that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable source code,
|
||||
which must be distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
b) Accompany it with a written offer, valid for at least three years, to give
|
||||
any third party, for a charge no more than your cost of physically performing
|
||||
source distribution, a complete machine-readable copy of the corresponding
|
||||
source code, to be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange; or,
|
||||
c) Accompany it with the information you received as to the offer to distribute
|
||||
corresponding source code. (This alternative is allowed only for noncommercial
|
||||
distribution and only if you received the program in object code or executable
|
||||
form with such an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for making
|
||||
modifications to it. For an executable work, complete source code means all the
|
||||
source code for all modules it contains, plus any associated interface definition
|
||||
files, plus the scripts used to control compilation and installation of the
|
||||
executable. However, as a special exception, the source code distributed need not
|
||||
include anything that is normally distributed (in either source or binary form) with
|
||||
the major components (compiler, kernel, and so on) of the operating system on which
|
||||
the executable runs, unless that component itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering access to copy from
|
||||
a designated place, then offering equivalent access to copy the source code from the
|
||||
same place counts as distribution of the source code, even though third parties are
|
||||
not compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program except as
|
||||
expressly provided under this License. Any attempt otherwise to copy, modify,
|
||||
sublicense or distribute the Program is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies, or rights,
|
||||
from you under this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not signed it.
|
||||
However, nothing else grants you permission to modify or distribute the Program or
|
||||
its derivative works. These actions are prohibited by law if you do not accept this
|
||||
License. Therefore, by modifying or distributing the Program (or any work based on
|
||||
the Program), you indicate your acceptance of this License to do so, and all its
|
||||
terms and conditions for copying, distributing or modifying the Program or works
|
||||
based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the Program), the
|
||||
recipient automatically receives a license from the original licensor to copy,
|
||||
distribute or modify the Program subject to these terms and conditions. You may not
|
||||
impose any further restrictions on the recipients' exercise of the rights granted
|
||||
herein. You are not responsible for enforcing compliance by third parties to this
|
||||
License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent infringement or
|
||||
for any other reason (not limited to patent issues), conditions are imposed on you
|
||||
(whether by court order, agreement or otherwise) that contradict the conditions of
|
||||
this License, they do not excuse you from the conditions of this License. If you
|
||||
cannot distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may not
|
||||
distribute the Program at all. For example, if a patent license would not permit
|
||||
royalty-free redistribution of the Program by all those who receive copies directly
|
||||
or indirectly through you, then the only way you could satisfy both it and this
|
||||
License would be to refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any particular
|
||||
circumstance, the balance of the section is intended to apply and the section as a
|
||||
whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any patents or other
|
||||
property right claims or to contest validity of any such claims; this section has
|
||||
the sole purpose of protecting the integrity of the free software distribution
|
||||
system, which is implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed through that system
|
||||
in reliance on consistent application of that system; it is up to the author/donor
|
||||
to decide if he or she is willing to distribute software through any other system
|
||||
and a licensee cannot impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to be a
|
||||
consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in certain countries
|
||||
either by patents or by copyrighted interfaces, the original copyright holder who
|
||||
places the Program under this License may add an explicit geographical distribution
|
||||
limitation excluding those countries, so that distribution is permitted only in or
|
||||
among countries not thus excluded. In such case, this License incorporates the
|
||||
limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions of the
|
||||
General Public License from time to time. Such new versions will be similar in
|
||||
spirit to the present version, but may differ in detail to address new problems or
|
||||
concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program specifies a
|
||||
version number of this License which applies to it and "any later version", you have
|
||||
the option of following the terms and conditions either of that version or of any
|
||||
later version published by the Free Software Foundation. If the Program does not
|
||||
specify a version number of this License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free programs whose
|
||||
distribution conditions are different, write to the author to ask for permission.
|
||||
For software which is copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our decision will be
|
||||
guided by the two goals of preserving the free status of all derivatives of our free
|
||||
software and of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE
|
||||
PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN
|
||||
WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
|
||||
WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH
|
||||
YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY
|
||||
SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY
|
||||
COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM
|
||||
AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
|
||||
INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE
|
||||
OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE
|
||||
WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
|
@ -16,34 +16,73 @@ LICENSES/
|
|||
doc/
|
||||
hw/application_fpga/core/picorv32/
|
||||
hw/application_fpga/core/uart/
|
||||
hw/application_fpga/fw/tk1/blake2s/
|
||||
)
|
||||
|
||||
missingok_files=(
|
||||
.editorconfig
|
||||
.gitattributes
|
||||
.gitignore
|
||||
.clang-format
|
||||
README.md
|
||||
contrib/99-tillitis.rules
|
||||
contrib/Dockerfile
|
||||
contrib/Makefile
|
||||
dco.md
|
||||
contrib/verible.sha512
|
||||
|
||||
hw/application_fpga/README.md
|
||||
hw/application_fpga/core/clk_reset_gen/README.md
|
||||
hw/application_fpga/core/fw_ram/README.md
|
||||
hw/application_fpga/core/ram/README.md
|
||||
hw/application_fpga/core/rom/README.md
|
||||
hw/application_fpga/core/tk1/tb/udi.hex
|
||||
hw/application_fpga/core/uds/README.md
|
||||
hw/application_fpga/fw/README.md
|
||||
hw/application_fpga/fw/tk1/picorv32/README.md
|
||||
hw/application_fpga/apps/Makefile
|
||||
hw/application_fpga/apps/README.md
|
||||
hw/application_fpga/application_fpga.bin.sha256
|
||||
hw/application_fpga/config.vlt
|
||||
hw/application_fpga/core/timer/README.md
|
||||
hw/application_fpga/core/tk1/README.md
|
||||
hw/application_fpga/core/touch_sense/README.md
|
||||
hw/application_fpga/core/trng/README.md
|
||||
hw/application_fpga/core/uds/README.txt
|
||||
hw/application_fpga/data/udi.hex
|
||||
hw/application_fpga/data/uds.hex
|
||||
hw/application_fpga/firmware.bin.sha512
|
||||
hw/application_fpga/fw/.clang-format
|
||||
hw/application_fpga/fw/testfw/Makefile
|
||||
hw/application_fpga/fw/tk1/Makefile
|
||||
hw/application_fpga/tools/makehex/makehex.py
|
||||
hw/application_fpga/tools/reset-tk1
|
||||
hw/application_fpga/tools/tpt/README.md
|
||||
hw/usb_interface/ch552_fw/.gitignore
|
||||
hw/usb_interface/ch552_fw/LICENSES/GPL-2.0-only.txt
|
||||
hw/usb_interface/ch552_fw/LICENSES/MIT.txt
|
||||
hw/usb_interface/ch552_fw/Makefile
|
||||
hw/usb_interface/ch552_fw/README.md
|
||||
|
||||
# tkey-libs is assumed to be REUSE compliant
|
||||
hw/application_fpga/tkey-libs/LICENSE
|
||||
hw/application_fpga/tkey-libs/LICENSES/BSD-2-Clause.txt
|
||||
hw/application_fpga/tkey-libs/LICENSES/CC0-1.0.txt
|
||||
hw/application_fpga/tkey-libs/Makefile
|
||||
hw/application_fpga/tkey-libs/README-DIST.txt
|
||||
hw/application_fpga/tkey-libs/README.md
|
||||
hw/application_fpga/tkey-libs/RELEASE.md
|
||||
hw/application_fpga/tkey-libs/blake2s/LICENSE
|
||||
hw/application_fpga/tkey-libs/blake2s/Makefile
|
||||
hw/application_fpga/tkey-libs/blake2s/blake2s.c
|
||||
hw/application_fpga/tkey-libs/blake2s/blake2s.h
|
||||
hw/application_fpga/tkey-libs/blake2s/blake2s_test.c
|
||||
hw/application_fpga/tkey-libs/example-app/Makefile
|
||||
hw/application_fpga/tkey-libs/monocypher/LICENSE
|
||||
hw/application_fpga/tkey-libs/monocypher/README.md
|
||||
|
||||
hw/application_fpga/tools/README.md
|
||||
hw/application_fpga/tools/b2s/README.md
|
||||
hw/application_fpga/tools/b2s/go.mod
|
||||
hw/application_fpga/tools/b2s/go.sum
|
||||
hw/application_fpga/tools/default_partition.bin
|
||||
hw/application_fpga/tools/tkeyimage/README.md
|
||||
hw/application_fpga/tools/tkeyimage/go.mod
|
||||
hw/application_fpga/tools/tkeyimage/go.sum
|
||||
)
|
||||
|
||||
is_missingok() {
|
||||
|
|
158
README.md
158
README.md
|
@ -2,6 +2,9 @@
|
|||
|
||||
# Tillitis TKey
|
||||
|
||||
Read about current work in progress
|
||||
[here](#current-work-in-progress-in-this-repository).
|
||||
|
||||
 *The TK1 PCB, also known as
|
||||
TKey.*
|
||||
|
||||
|
@ -25,11 +28,11 @@ With the right application, the TKey can be used for:
|
|||
|
||||
If you want to know more about Tillitis and the TKey, visit:
|
||||
|
||||
- Main web: https://tillitis.se/
|
||||
- Shop: https://shop.tillitis.se/
|
||||
- Developer Handbook: https://dev.tillitis.se/
|
||||
- Officially supported apps: https://tillitis.se/download/
|
||||
- Other known apps: https://dev.tillitis.se/projects/
|
||||
- Main web: <https://tillitis.se/>
|
||||
- Shop: <https://shop.tillitis.se/>
|
||||
- Developer Handbook: <https://dev.tillitis.se/>
|
||||
- Officially supported apps: <https://tillitis.se/download/>
|
||||
- Other known apps: <https://dev.tillitis.se/projects/>
|
||||
|
||||
All of the TKey software, firmware, FPGA Verilog code, schematics and
|
||||
PCB design files are open source, just like all trustworthy security
|
||||
|
@ -37,9 +40,17 @@ software and hardware should be.
|
|||
|
||||
## Licensing
|
||||
|
||||
Unless otherwise noted, the project sources are copyright Tillitis AB.
|
||||
but you can redistribute it and/or modify it under the terms of the
|
||||
GNU General Public License version 2 as published by the Free Software
|
||||
Foundation.
|
||||
|
||||
See [LICENSES](./LICENSES/README.md) for more information about
|
||||
the projects' licenses.
|
||||
|
||||
Each imported project is typically kept in its own directory with its
|
||||
own LICENSE file.
|
||||
|
||||
## Repositories
|
||||
|
||||
This repository contains the FPGA design, the source of the
|
||||
|
@ -56,17 +67,138 @@ releases.
|
|||
The TKey PCB [KiCad](https://www.kicad.org/) design files are kept in
|
||||
a separate repository:
|
||||
|
||||
https://github.com/tillitis/tk1-pcba
|
||||
<https://github.com/tillitis/tk1-pcba>
|
||||
|
||||
The TP1 (TKey programmer 1) PCB design files and the firmware sources
|
||||
are kept in:
|
||||
|
||||
https://github.com/tillitis/tp1
|
||||
<https://github.com/tillitis/tp1>
|
||||
|
||||
Note that the TP1 is only used for provisioning the FPGA bitstream
|
||||
into flash or the FPGA configuration memory. It's not necessary if you
|
||||
just want to develop apps for the TKey.
|
||||
|
||||
We use the tkey-libs libraries used for device app development in the
|
||||
firmware, too:
|
||||
|
||||
https://github.com/tillitis/tkey-libs
|
||||
|
||||
but keep our own copy of it in the repo. See below.
|
||||
|
||||
## Building & flashing
|
||||
|
||||
These instructions assume you're using a Linux distribution. Most of
|
||||
them also assume you're using our OCI image
|
||||
[tkey-builder](https://ghcr.io/tillitis/tkey-builder). If you want to
|
||||
run native tools, look in `contrib/Dockerfile` and
|
||||
`contrib/buildtools.sh` for the tools and versions to use.
|
||||
|
||||
### FPGA
|
||||
|
||||
You need a [TKey
|
||||
Unlocked](https://shop.tillitis.se/products/tkey-not-provisioned), a
|
||||
[the TP1 TKey Programmer
|
||||
board](https://shop.tillitis.se/products/tkey-dev-kit), and probably a
|
||||
[Blinkinlabs CH55x Reset
|
||||
Controller](https://shop-nl.blinkinlabs.com/products/ch55x-reset-controller)
|
||||
to use this on real hardware.
|
||||
|
||||
Building is probably easiest using make and Podman.
|
||||
|
||||
To build everything and then flash the resulting bitstream with the
|
||||
testloadapp in app slot 0 and the partition table copies in one go,
|
||||
place the TKey Unlocked in the TP1, then do:
|
||||
|
||||
```
|
||||
cd contrib
|
||||
make flash
|
||||
```
|
||||
|
||||
This uses the make target `prog_flash` in
|
||||
`hw/application_fpga/Makefile` behind the scenes, but mounts your TP1
|
||||
device into the container.
|
||||
|
||||
To see all targets:
|
||||
|
||||
```
|
||||
cd contrib
|
||||
make
|
||||
```
|
||||
|
||||
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
|
||||
in `hw/application_fpga/tkey-libs`. This is mostly to avoid the
|
||||
subtleties of Git submodules.
|
||||
|
||||
If you want to change something in tkey-libs, always change in the
|
||||
upstream library at:
|
||||
|
||||
https://github.com/tillitis/tkey-libs
|
||||
|
||||
You can build with an out-of-tree copy if you set `LIBDIR`, for
|
||||
example:
|
||||
|
||||
```
|
||||
make LIBDIR=~/git/tkey-libs firmware.elf
|
||||
```
|
||||
|
||||
When it's time to update the in-tree tkey-lib first tag the upstream
|
||||
repo with an `fw` prefix, like `fw-1` even if it already has an
|
||||
official version tag.
|
||||
|
||||
Easiest is probably to just remove the tkey-libs directory and then
|
||||
git clone the desired tag. Use the entire repo, but remove the .-files
|
||||
like `.git`, `.github`, et cetera. Something like:
|
||||
|
||||
```
|
||||
$ rm -rf tkey-libs
|
||||
$ git clone git@github.com:tillitis/tkey-libs.git
|
||||
$ cd tkey-libs
|
||||
$ git checkout fw-3
|
||||
```
|
||||
|
||||
Note that you need to change the optimization flag in the tkey-libs'
|
||||
Makefile to `-Os`.
|
||||
|
||||
## Measured boot
|
||||
|
||||
The key behind guaranteeing security even as a general computer is the
|
||||
|
@ -104,3 +236,15 @@ deterministically generate any cryptographic keys it needs.
|
|||
The TKey unconditional measured boot is inspired by, but not exactly
|
||||
the same as part of [TCG
|
||||
DICE](https://trustedcomputinggroup.org/work-groups/dice-architectures/).
|
||||
|
||||
# Current Work in Progress in this repository
|
||||
|
||||
We are updating the FPGA and firmware on TKey as part of the Castor
|
||||
release. This update will simplify TKey’s usage, laying the groundwork
|
||||
for future support of U2F/FIDO authentication.
|
||||
|
||||
You can track our progress through this
|
||||
[milestone](https://github.com/tillitis/tillitis-key1/milestone/1).
|
||||
|
||||
Note that main branch is in development. We try to keep status of main
|
||||
branch updated in the [release notes](/doc/release_notes.md#upcoming-release-castor).
|
||||
|
|
|
@ -12,6 +12,7 @@ RUN apt-get -qq update -y \
|
|||
clang-format \
|
||||
clang-tidy \
|
||||
cmake \
|
||||
curl \
|
||||
flex \
|
||||
g++ \
|
||||
gawk \
|
||||
|
@ -57,109 +58,12 @@ RUN apt-get -qq update -y \
|
|||
zlib1g-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Enable bash completion
|
||||
RUN sed -i '/#if ! shopt -oq posix; then/ s/^#//' /etc/bash.bashrc
|
||||
RUN sed -i '/# if \[ -f \/usr\/share\/bash-completion\/bash_completion \]; then/ s/^#//' /etc/bash.bashrc
|
||||
RUN sed -i '/# . \/usr\/share\/bash-completion\/bash_completion/ s/^#//' /etc/bash.bashrc
|
||||
RUN sed -i '/# elif \[ -f \/etc\/bash_completion \]; then/ s/^#//' /etc/bash.bashrc
|
||||
RUN sed -i '/# . \/etc\/bash_completion/ s/^#//' /etc/bash.bashrc
|
||||
RUN sed -i '/# fi/ s/^#//' /etc/bash.bashrc
|
||||
RUN sed -i '/#fi/ s/^#//' /etc/bash.bashrc
|
||||
|
||||
FROM base as toolsbuilder
|
||||
|
||||
RUN git clone --depth=1 https://github.com/YosysHQ/icestorm /src
|
||||
WORKDIR /src
|
||||
RUN git checkout 738af822905fdcf0466e9dd784b9ae4b0b34987f \
|
||||
&& make -j$(nproc --ignore=2) \
|
||||
&& make install \
|
||||
&& git describe --all --always --long --dirty > /usr/local/repo-commit-icestorm
|
||||
WORKDIR /
|
||||
RUN rm -rf /src
|
||||
COPY buildtools.sh /buildtools.sh
|
||||
COPY verible.sha512 /verible.sha512
|
||||
|
||||
# Custom iceprog for the RPi 2040-based programmer (will be upstreamed).
|
||||
RUN git clone -b interfaces --depth=1 https://github.com/tillitis/icestorm /src
|
||||
WORKDIR /src/iceprog
|
||||
RUN make -j$(nproc --ignore=2) \
|
||||
&& make PROGRAM_PREFIX=tillitis- install \
|
||||
&& git describe --all --always --long --dirty > /usr/local/repo-commit-tillitis--icestorm
|
||||
WORKDIR /
|
||||
RUN rm -rf /src
|
||||
|
||||
RUN git clone -b 0.45 --depth=1 https://github.com/YosysHQ/yosys /src
|
||||
WORKDIR /src
|
||||
RUN git submodule update --init \
|
||||
&& make -j$(nproc --ignore=2) \
|
||||
&& make install \
|
||||
&& git describe --all --always --long --dirty > /usr/local/repo-commit-yosys
|
||||
WORKDIR /
|
||||
RUN rm -rf /src
|
||||
|
||||
RUN git clone -b nextpnr-0.7 https://github.com/YosysHQ/nextpnr /src
|
||||
WORKDIR /src
|
||||
# Add "Fix handling of RNG seed" #1369
|
||||
RUN git cherry-pick --no-commit 6ca64526bb18ace8690872b09ca1251567c116de
|
||||
# Add early exit if place fails on timing
|
||||
RUN sed -i \
|
||||
'345i \ \ \ \ general.add_options()("exit-on-failed-target-frequency",' \
|
||||
common/kernel/command.cc
|
||||
RUN sed -i \
|
||||
'346i \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ "exit if target frequency is not achieved (use together with "' \
|
||||
common/kernel/command.cc
|
||||
RUN sed -i \
|
||||
'347i \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ "--randomize-seed option)");' \
|
||||
common/kernel/command.cc
|
||||
RUN sed -i \
|
||||
'348i \\' \
|
||||
common/kernel/command.cc
|
||||
RUN sed -i \
|
||||
'662s/if (do_route) {/if (do_route \&\& (vm.count("exit-on-failed-target-frequency") ? !had_nonfatal_error : true)) {/' \
|
||||
common/kernel/command.cc
|
||||
RUN sed -i \
|
||||
'244s/bool warn_on_failure = false/bool warn_on_failure = true/' \
|
||||
common/kernel/timing.h
|
||||
RUN cmake -DARCH=ice40 . \
|
||||
&& make -j$(nproc --ignore=2) \
|
||||
&& make install \
|
||||
&& git describe --all --always --long --dirty > /usr/local/repo-commit-nextpnr
|
||||
WORKDIR /
|
||||
RUN rm -rf /src
|
||||
|
||||
RUN git clone -b v12_0 --depth=1 https://github.com/steveicarus/iverilog /src
|
||||
WORKDIR /src
|
||||
RUN sh autoconf.sh \
|
||||
&& ./configure \
|
||||
&& make -j$(nproc --ignore=2) \
|
||||
&& make install \
|
||||
&& git describe --all --always --long --dirty > /usr/local/repo-commit-iverilog
|
||||
WORKDIR /
|
||||
RUN rm -rf /src
|
||||
|
||||
RUN git clone -b v5.028 --depth=1 https://github.com/verilator/verilator /src
|
||||
WORKDIR /src
|
||||
RUN autoconf \
|
||||
&& ./configure \
|
||||
&& make -j$(nproc --ignore=2) \
|
||||
&& make test \
|
||||
&& make install \
|
||||
&& git describe --all --always --long --dirty > /usr/local/repo-commit-verilator
|
||||
WORKDIR /
|
||||
RUN rm -rf /src
|
||||
|
||||
ADD https://github.com/chipsalliance/verible/releases/download/v0.0-3795-gf4d72375/verible-v0.0-3795-gf4d72375-linux-static-x86_64.tar.gz /src/verible.tar.gz
|
||||
WORKDIR /src
|
||||
RUN tar xvf verible.tar.gz
|
||||
RUN mv -v verible*/bin/* /usr/local/bin
|
||||
RUN verible-verilog-format --version | head -1 > /usr/local/repo-commit-verible
|
||||
WORKDIR /
|
||||
RUN rm -rf /src
|
||||
|
||||
RUN git clone -b v1.9.1 https://github.com/cocotb/cocotb.git /src
|
||||
WORKDIR /src
|
||||
RUN pip install . --break-system-packages \
|
||||
&& git describe --all --always --long --dirty > /usr/local/repo-commit-cocotb
|
||||
WORKDIR /
|
||||
RUN rm -rf /src
|
||||
RUN /buildtools.sh
|
||||
|
||||
FROM base
|
||||
LABEL org.opencontainers.image.description="Toolchain for building TKey FPGA bitstream"
|
||||
|
|
|
@ -5,16 +5,18 @@
|
|||
BUILDIMAGE=tkey-builder-local
|
||||
|
||||
# default image used when running a container
|
||||
IMAGE=ghcr.io/tillitis/tkey-builder:4
|
||||
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 Program the SPI flash on the TKey - needs an existing bitstream"
|
||||
@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"
|
||||
|
@ -23,16 +25,20 @@ all:
|
|||
|
||||
run:
|
||||
podman run --rm --mount type=bind,source="`pwd`/../",target=/build -w /build -it \
|
||||
$(IMAGE) /usr/bin/bash
|
||||
$(IMAGE) /usr/bin/bash -l
|
||||
|
||||
docker-run:
|
||||
docker run --rm --mount type=bind,source="`pwd`/../",target=/build -w /build -it \
|
||||
$(IMAGE) /usr/bin/bash
|
||||
$(IMAGE) /usr/bin/bash -l
|
||||
|
||||
run-make:
|
||||
podman run --rm --mount type=bind,source="`pwd`/../hw/application_fpga",target=/build -w /build -it \
|
||||
$(IMAGE) make clean application_fpga.bin
|
||||
|
||||
run-make-ch552:
|
||||
podman run --rm --mount type=bind,source="`pwd`/../hw/usb_interface/ch552_fw",target=/build -w /build -it \
|
||||
$(IMAGE) make clean usb_device.bin
|
||||
|
||||
run-tb:
|
||||
podman run --rm --mount type=bind,source="`pwd`/../hw/application_fpga",target=/build -w /build -it \
|
||||
$(IMAGE) make clean_tb tb
|
||||
|
@ -45,6 +51,10 @@ run-make-no-clean:
|
|||
podman run --rm --mount type=bind,source="`pwd`/../hw/application_fpga",target=/build -w /build -it \
|
||||
$(IMAGE) make application_fpga.bin
|
||||
|
||||
run-make-ch552-no-clean:
|
||||
podman run --rm --mount type=bind,source="`pwd`/../hw/usb_interface/ch552_fw",target=/build -w /build -it \
|
||||
$(IMAGE) make usb_device.bin
|
||||
|
||||
run-make-clean_fw:
|
||||
podman run --rm --mount type=bind,source="`pwd`/../hw/application_fpga",target=/build -w /build -it \
|
||||
$(IMAGE) make clean_fw application_fpga.bin
|
||||
|
@ -54,7 +64,7 @@ flash:
|
|||
--device /dev/bus/usb/$(lsusb | grep -m 1 1209:8886 | awk '{ printf "%s/%s", $2, substr($4,1,3) }') \
|
||||
--mount type=bind,source="`pwd`/../hw/application_fpga",target=/build \
|
||||
-w /build \
|
||||
-it $(IMAGE) tillitis-iceprog /build/application_fpga.bin
|
||||
-it $(IMAGE) make prog_flash
|
||||
|
||||
|
||||
pull:
|
||||
|
|
134
contrib/buildtools.sh
Executable file
134
contrib/buildtools.sh
Executable file
|
@ -0,0 +1,134 @@
|
|||
#! /bin/sh -e
|
||||
|
||||
# Copyright (C) 2025 Tillitis AB
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
## Build the specific versions of the tools we need to build the TKey
|
||||
## FPGA bitstream and apps.
|
||||
|
||||
cd /
|
||||
mkdir src
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Project icestorm
|
||||
# ----------------------------------------------------------------------
|
||||
git clone https://github.com/YosysHQ/icestorm /src/icestorm
|
||||
cd /src/icestorm
|
||||
|
||||
# No tags or releases yet. Pin down to a specific commit.
|
||||
git checkout 738af822905fdcf0466e9dd784b9ae4b0b34987f
|
||||
make -j$(nproc --ignore=2)
|
||||
make install
|
||||
git describe --all --always --long --dirty > /usr/local/repo-commit-icestorm
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Our own custom iceprog for the RPi 2040-based programmer. Will be
|
||||
# upstreamed.
|
||||
# ----------------------------------------------------------------------
|
||||
git clone -b interfaces --depth=1 https://github.com/tillitis/icestorm /src/icestorm-tillitis
|
||||
cd /src/icestorm-tillitis/iceprog
|
||||
make -j$(nproc --ignore=2)
|
||||
make PROGRAM_PREFIX=tillitis- install
|
||||
git describe --all --always --long --dirty > /usr/local/repo-commit-tillitis--icestorm
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# yosys
|
||||
# ----------------------------------------------------------------------
|
||||
git clone -b 0.45 --depth=1 https://github.com/YosysHQ/yosys /src/yosys
|
||||
cd /src/yosys
|
||||
|
||||
# Make sure the digest is correct and no history has changed
|
||||
git checkout 9ed031ddd588442f22be13ce608547a5809b62f0
|
||||
|
||||
git submodule update --init
|
||||
make -j$(nproc --ignore=2)
|
||||
make install
|
||||
git describe --all --always --long --dirty > /usr/local/repo-commit-yosys
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# nextpnr
|
||||
# ----------------------------------------------------------------------
|
||||
git clone -b nextpnr-0.7 https://github.com/YosysHQ/nextpnr /src/nextpnr
|
||||
cd /src/nextpnr
|
||||
# Make sure the digest is correct and no history has changed
|
||||
git checkout 73b7de74a5769095acb96eb6c6333ffe161452f2
|
||||
|
||||
# Add "Fix handling of RNG seed" #1369
|
||||
git cherry-pick --no-commit 6ca64526bb18ace8690872b09ca1251567c116de
|
||||
|
||||
# Add early exit if place fails on timing
|
||||
sed -i \
|
||||
'345i \ \ \ \ general.add_options()("exit-on-failed-target-frequency",' \
|
||||
common/kernel/command.cc
|
||||
sed -i \
|
||||
'346i \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ "exit if target frequency is not achieved (use together with "' \
|
||||
common/kernel/command.cc
|
||||
sed -i \
|
||||
'347i \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ "--randomize-seed option)");' \
|
||||
common/kernel/command.cc
|
||||
sed -i \
|
||||
'348i \\' \
|
||||
common/kernel/command.cc
|
||||
sed -i \
|
||||
'662s/if (do_route) {/if (do_route \&\& (vm.count("exit-on-failed-target-frequency") ? !had_nonfatal_error : true)) {/' \
|
||||
common/kernel/command.cc
|
||||
sed -i \
|
||||
'244s/bool warn_on_failure = false/bool warn_on_failure = true/' \
|
||||
common/kernel/timing.h
|
||||
|
||||
cmake -DARCH=ice40 .
|
||||
make -j$(nproc --ignore=2)
|
||||
make install
|
||||
git describe --all --always --long --dirty > /usr/local/repo-commit-nextpnr
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# icarus verilog
|
||||
# ----------------------------------------------------------------------
|
||||
git clone -b v12_0 --depth=1 https://github.com/steveicarus/iverilog /src/iverilog
|
||||
cd /src/iverilog
|
||||
|
||||
# Make sure the digest is correct and no history has changed
|
||||
git checkout 4fd5291632232fbe1ba49b2c26bb6b2bf1c6c9cf
|
||||
sh autoconf.sh
|
||||
./configure
|
||||
make -j$(nproc --ignore=2)
|
||||
make install
|
||||
git describe --all --always --long --dirty > /usr/local/repo-commit-iverilog
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# verilator
|
||||
# ----------------------------------------------------------------------
|
||||
git clone -b v5.028 --depth=1 https://github.com/verilator/verilator /src/verilator
|
||||
cd /src/verilator
|
||||
|
||||
# Make sure the digest is correct and no history has changed
|
||||
git checkout 8ca45df9c75c611989ae5bfc4112a32212c3dacf
|
||||
|
||||
autoconf
|
||||
./configure
|
||||
make -j$(nproc --ignore=2)
|
||||
make test
|
||||
make install
|
||||
git describe --all --always --long --dirty > /usr/local/repo-commit-verilator
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# verible
|
||||
# ----------------------------------------------------------------------
|
||||
curl --output /src/verible.tar.gz -L https://github.com/chipsalliance/verible/releases/download/v0.0-3795-gf4d72375/verible-v0.0-3795-gf4d72375-linux-static-x86_64.tar.gz
|
||||
|
||||
# Check against the expected digest
|
||||
sha512sum -c /verible.sha512
|
||||
|
||||
cd /src
|
||||
|
||||
tar xvf verible.tar.gz
|
||||
mv -v verible*/bin/* /usr/local/bin
|
||||
verible-verilog-format --version | head -1 > /usr/local/repo-commit-verible
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# cocotb
|
||||
# ----------------------------------------------------------------------
|
||||
git clone -b v1.9.1 https://github.com/cocotb/cocotb.git /src/cocotb
|
||||
cd /src/cocotb
|
||||
pip install . --break-system-packages
|
||||
git describe --all --always --long --dirty > /usr/local/repo-commit-cocotb
|
1
contrib/verible.sha512
Normal file
1
contrib/verible.sha512
Normal file
|
@ -0,0 +1 @@
|
|||
3e997b8cd494556fa107b96a6daa4e51133208a85b3c0250c0223d7194aedbc98b3f5213fedaee083edd96c317e50057aa9cdc0517197f4638be3834133e2c9f /src/verible.tar.gz
|
|
@ -2,6 +2,205 @@
|
|||
|
||||
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.
|
||||
|
||||
**Note well**: BREAKING CHANGE! Older device apps WILL NOT WORK.
|
||||
|
||||
The introduction of the USB Mode Protocol between the programs running
|
||||
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>
|
||||
|
||||
- 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.
|
||||
|
||||
- Add SPI main controller mainly to access the flash chip.
|
||||
|
||||
- Add system reset API. Device apps can reset the FPGA and restart
|
||||
the firmware.
|
||||
|
||||
- Increase clock frequence to 24 MHz.
|
||||
|
||||
- Increase UART baudrate to 500,000.
|
||||
|
||||
- Fix UART baudrate counter issues noticable at higher baudrates.
|
||||
|
||||
- Fix missing clock cycles in timer core.
|
||||
|
||||
- Remove the UART runtime configuration API.
|
||||
|
||||
- Several minor clean ups of design and testbench.
|
||||
|
||||
- Add hardware clear to send (CTS) signals for communication between
|
||||
UART and CH552.
|
||||
|
||||
- Increase size of ROM and FW\_RAM.
|
||||
|
||||
- Make ROM non-executable in app mode.
|
||||
|
||||
- Remove MMIO address for access to the firmware blake2s() function
|
||||
from apps.
|
||||
|
||||
- Automatically leave firmware mode when execution leaves ROM and
|
||||
remove the now unnecessary APP\_MODE\_CTRL register.
|
||||
|
||||
- Change UDS read protection: When execution leaves ROM the first
|
||||
time, UDS is hardware protected from reads. The already existing
|
||||
protection that UDS is protected after the first read is also still
|
||||
available.
|
||||
|
||||
- Introduce interrupt handler for hardware-based privilege raising and
|
||||
automatically privelege lowering for system calls.
|
||||
|
||||
### Firmware
|
||||
|
||||
- At startup, fill RAM with random data using the xorwow PRNG, seeded
|
||||
by TRNG.
|
||||
|
||||
- Add support for the new USB Mode Protocol to communicate with
|
||||
different USB endpoints in the USB controller.
|
||||
|
||||
- Support a filesystem on flash: There's space for two pre-loaded
|
||||
apps and four storage areas for device apps.
|
||||
|
||||
A typical use is that app slot 0 will contain a loader app for
|
||||
verified boot and app slot 1 contains the app to be verified.
|
||||
|
||||
- Automatically start an app in flash app slot 0 after power cycle and
|
||||
when instructed to by reset intentions.
|
||||
|
||||
The automatically started app is trusted by the firmware by
|
||||
including an app digest in the firmware ROM. This means we extend
|
||||
the user's trust in the firmware to the first app, but only if it's
|
||||
measured to the correct digest by the firmware. Anything else is a
|
||||
hard error which halts the CPU.
|
||||
|
||||
- Support chaining of apps through soft resets, including support for
|
||||
verifying that the next app is the expected one (exact measured
|
||||
digest the previous app expected), and leaving data for the next app
|
||||
to use.
|
||||
|
||||
- Add a system call mechanism and system calls. See [firmware's
|
||||
README](../hw/application_fpga/fw/README.md) for documentation, but
|
||||
its probably easier to use the the syscall wrappers in libsyscall in
|
||||
[tkey-libs](https://github.com/tillitis/tkey-libs) if you're writing
|
||||
in C.
|
||||
|
||||
- Harmonize with [tkey-libs](https://github.com/tillitis/tkey-libs).
|
||||
Import tkey-libs to this repo for convenience.
|
||||
|
||||
- Rewrite test firmware to work with the new leaving ROM-scenario.
|
||||
Introduce a separate `testapp` for the app mode parts.
|
||||
|
||||
### Device apps
|
||||
|
||||
Introduce some device apps mostly for testing.
|
||||
|
||||
- `reset_test`: Test the different types of soft reset.
|
||||
|
||||
- `testapp`: Tests in app mode that used to live in `testfw`.
|
||||
|
||||
- `testloadapp`: A simple loader app for management and verification
|
||||
of a second app.
|
||||
|
||||
- `defaultapp`: An app that immediately resets the TKey to load an app
|
||||
from the client, just like earlier releases.
|
||||
|
||||
### CH552 firmware
|
||||
|
||||
- Use the new CTS signals for communication over the UART.
|
||||
|
||||
- Add support for two HID endpoints (security token and our debug
|
||||
HID).
|
||||
|
||||
- Add support for CCID endpoint.
|
||||
|
||||
- Add a protocol to communicate with the different endpoints: CDC,
|
||||
CCID, FIDO, debug.
|
||||
|
||||
- Change USB frame sending from a software timer to instead be
|
||||
controlled by the USB Controller Protocol.
|
||||
|
||||
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://github.com/Blinkinlabs/ch55x_programmer>
|
||||
|
||||
### Tooling
|
||||
|
||||
- Add tools to parse and generate partition tables and flash images.
|
||||
|
||||
- Add tool to compute a print a BLAKE2s digest, optionally as C code.
|
||||
|
||||
### tkey-builder
|
||||
|
||||
- New versions of:
|
||||
- clang (18.1.3, part of ubuntu 24.04)
|
||||
- icestorm (commit 738af822905fdcf0466e9dd784b9ae4b0b34987f
|
||||
)
|
||||
- yosys (0.45)
|
||||
- nextpnr (0.7) + extra patches for RNG seed handling and early exit
|
||||
- iverilog (v12)
|
||||
- verilator (v5.028)
|
||||
- verible (v0.0-3795)
|
||||
- cocotb (v1.9.1)
|
||||
|
||||
- Remove TKey Programmer (TP) toolchain:
|
||||
|
||||
- gcc-arm-none-eabi: Used for the TKey Programmer firmware, now
|
||||
moved to it's own repo.
|
||||
- libnewlib-arm-none-eabi
|
||||
- libstdc++-arm-none-eabi-newlib
|
||||
- pico-sdk
|
||||
|
||||
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.
|
||||
|
||||
- Protocol docs moved to [the Developer
|
||||
Handbook](https://dev.tillitis.se/)
|
||||
[repo](https://github.com/tillitis/dev-tillitis)
|
||||
|
||||
## TK1-24.03
|
||||
|
||||
|
@ -19,6 +218,7 @@ the following digest:
|
|||
```
|
||||
|
||||
### FPGA
|
||||
|
||||
- Security Monitor now prevents access to RAM outside of the physical
|
||||
memory. If it detects an access outside of the RAM address space, it
|
||||
will halt the CPU.
|
||||
|
@ -30,6 +230,7 @@ the following digest:
|
|||
- Complete testbenches and add 9 tests for the FPGA cores.
|
||||
|
||||
### Firmware
|
||||
|
||||
- Protect zeroisation against compiler optimisation by using
|
||||
secure_wipe(), fixing a memset() that was removed during
|
||||
compilation.
|
||||
|
@ -44,31 +245,35 @@ the following digest:
|
|||
- Fix warnings from splint.
|
||||
|
||||
### TP1
|
||||
|
||||
- New plastic clip o and update of BOM.
|
||||
- Build TP1 firmware in CI.
|
||||
|
||||
### CH552
|
||||
|
||||
- Fixed a bug where a byte of data could in some rare circumstances be
|
||||
dropped, causing a client app to hang.
|
||||
- General clean-up of code, translated all comments to English.
|
||||
|
||||
### TK1
|
||||
|
||||
- New injection moulded plastic case
|
||||
|
||||
### tkey-builder
|
||||
|
||||
- Updated to version 3. Bumping Ubuntu to 23.10, Yosys to 0.36 and
|
||||
nextpnr to 0.6.
|
||||
- Updated to version 4. Bumping pico-sdk to 1.5.1, adding clang-tidy
|
||||
and splint.
|
||||
|
||||
### Docs
|
||||
|
||||
- Fixing broken links, cleaning up docs and READMEs.
|
||||
- Clarify warm boot attack mitigations and scope for Bellatrix in
|
||||
threat model.
|
||||
|
||||
For full change log [see](https://github.com/tillitis/tillitis-key1/compare/TK1-23.03.2...TK1-24.03)
|
||||
|
||||
|
||||
## TK1-23.03.2
|
||||
|
||||
This is the official release of the "Bellatrix" version of the
|
||||
|
@ -98,8 +303,8 @@ This bug fix release contains the following changes:
|
|||
- Change the firmware protocol max frame size back to 128 bytes
|
||||
- Correct a bug with the reading out of UDS
|
||||
|
||||
|
||||
## TK1-23.03
|
||||
|
||||
This is the official release of the "Bellatrix" version of
|
||||
the Tillitis TKey device. This version is ready for general
|
||||
use.
|
||||
|
@ -113,7 +318,6 @@ shasum -a256 application_fpga.bin
|
|||
f11d6b0f57c5405598206dcfea284008413391a2c51f124a2e2ae8600cb78f0b application_fpga.bin
|
||||
```
|
||||
|
||||
|
||||
### New and improved functionality
|
||||
|
||||
- (ALL) The TKey HW design, FW, protocol and first applications has
|
||||
|
@ -177,10 +381,9 @@ f11d6b0f57c5405598206dcfea284008413391a2c51f124a2e2ae8600cb78f0b application_fp
|
|||
- (TOOLS) There is now a version of iceprog able to write to the FPGA
|
||||
bitstream to the NVCM and lock the NVCM from external access
|
||||
|
||||
|
||||
### Bugs fixed
|
||||
- No known bugs have been fixed. Numerous issues has been closed.
|
||||
|
||||
- No known bugs have been fixed. Numerous issues has been closed.
|
||||
|
||||
### Limitations
|
||||
|
||||
|
@ -188,7 +391,6 @@ f11d6b0f57c5405598206dcfea284008413391a2c51f124a2e2ae8600cb78f0b application_fp
|
|||
cryptographically secure. It his however randomized every time
|
||||
a TKey device is powered up.
|
||||
|
||||
|
||||
## engineering-release-2
|
||||
|
||||
### New and improved functionality
|
||||
|
|
|
@ -115,8 +115,8 @@ allowed
|
|||
* Access to compute resources. Possibly access to lab equipment
|
||||
* Will try all possible SW and HW attack vectors. In and out of scope
|
||||
* End game is to find flaws in threat model. Acquire knowledge and
|
||||
findings to produce an interesting talk at CCC, USENIX or Security
|
||||
Fest
|
||||
findings to produce an interesting talk at Chaos Communication
|
||||
Congress, USENIX or Security Fest
|
||||
|
||||
Over time (with new releases), and given feedback by the CCC Hacker,
|
||||
the TKey device should be able to withstand attacks by the CCC Hacker.
|
||||
|
@ -258,7 +258,7 @@ information, see the [Release Notes](/doc/release_notes.md)
|
|||
Note that this mitigates an attack from outside the CPU, not from
|
||||
an exploit towards applications running on it.
|
||||
|
||||
#### Known possible weakneses
|
||||
#### Known possible weaknesses
|
||||
|
||||
The CH552 MCU providing USB host communication contains firmware that
|
||||
implements the UART communication with the FPGA. The CH552 firmware
|
||||
|
@ -297,7 +297,7 @@ board, and is even shipped with a programmer to download new FPGA
|
|||
bitstreams.
|
||||
|
||||
|
||||
#### Known weakneses
|
||||
#### Known weaknesses
|
||||
|
||||
The bitstream, which includes the Unique Device Secret (UDS) as well
|
||||
as the firmware implementing the measured boot are stored as part of
|
||||
|
|
|
@ -1,149 +0,0 @@
|
|||
# Toolchain setup
|
||||
|
||||
**NOTE:** Documentation migrated to dev.tillitis.se, this is kept for
|
||||
history. This is likely to be outdated.
|
||||
|
||||
Here are instructions for setting up the tools required to build the
|
||||
project. Tested on Ubuntu 22.10.
|
||||
|
||||
## General development environment
|
||||
|
||||
The following is intended to be a complete list of the packages that
|
||||
are required for doing all of the following:
|
||||
|
||||
- building and developing [TKey device and client
|
||||
apps](https://github.com/tillitis/tillitis-key1-apps)
|
||||
- building our [QEMU machine](https://github.com/tillitis/qemu/tree/tk1)
|
||||
(useful for apps dev)
|
||||
- building and developing firmware and FPGA gateware (which also
|
||||
requires building the toolchain below)
|
||||
|
||||
```
|
||||
sudo apt install build-essential clang lld llvm bison flex libreadline-dev \
|
||||
gawk tcl-dev libffi-dev git mercurial graphviz \
|
||||
xdot pkg-config python3 libftdi-dev \
|
||||
python3-dev libeigen3-dev \
|
||||
libboost-dev libboost-filesystem-dev \
|
||||
libboost-thread-dev libboost-program-options-dev \
|
||||
libboost-iostreams-dev cmake libusb-1.0-0-dev \
|
||||
ninja-build libglib2.0-dev libpixman-1-dev \
|
||||
golang clang-format \
|
||||
gcc-arm-none-eabi libnewlib-arm-none-eabi \
|
||||
libstdc++-arm-none-eabi-newlib
|
||||
```
|
||||
|
||||
## Device permissions
|
||||
|
||||
To allow sudo-less programming, you can install a udev rule that will
|
||||
assign the tkey programmer, and also an unprogrammed CH552, to the
|
||||
dialout group. You will also need to add your user to this group:
|
||||
|
||||
```
|
||||
sudo cp contrib/99-tillitis.rules /etc/udev/rules.d
|
||||
sudo udevadm control --reload-rules
|
||||
sudo usermod -aG dialout ${USER}
|
||||
```
|
||||
|
||||
To apply the new group, log out and then log back in.
|
||||
|
||||
You can check the device permissions to determine if the group was
|
||||
successfully applied. First, use lsusb to find the location of the
|
||||
programmer:
|
||||
|
||||
```
|
||||
lsusb -d 1209:8886
|
||||
Bus 001 Device 023: ID 1209:8886 Generic TP-1
|
||||
```
|
||||
|
||||
Then, you can check the permissions by using the bus and device
|
||||
number reported above. Note that this pair is ephemeral and may
|
||||
change after every device insertion:
|
||||
|
||||
```
|
||||
ls -l /dev/bus/usb/001/023
|
||||
crw-rw---- 1 root dialout 189, 22 Feb 16 14:58 /dev/bus/usb/001/023
|
||||
```
|
||||
|
||||
## Gateware: Yosys/Icestorm toolchain
|
||||
|
||||
If the LED of your TKey is steady white when you plug it, then the
|
||||
firmware is running and it's already usable! If you want to develop
|
||||
TKey apps, then only the above general development environment is
|
||||
needed.
|
||||
|
||||
Compiling and installing Yosys and friends is only needed if your TKey
|
||||
is not already running the required firmware and FPGA gateware, or if
|
||||
you want to do development on these components.
|
||||
|
||||
These steps are used to build and install the
|
||||
[icestorm](http://bygone.clairexen.net/icestorm/) toolchain. The
|
||||
binaries are installed in `/usr/local`. Note that if you have or
|
||||
install other versions of these tools locally, they could conflict
|
||||
(case in point: `yosys` installed on MacOS using brew).
|
||||
|
||||
git clone https://github.com/YosysHQ/icestorm
|
||||
cd icestorm
|
||||
git checkout d20a5e9001f46262bf0cef220f1a6943946e421d
|
||||
make -j$(nproc)
|
||||
sudo make install
|
||||
cd ..
|
||||
|
||||
# Custom iceprog for the RPi 2040-based programmer (will be upstreamed).
|
||||
# Note: install dependencies for building tillitis-iceprog on Ubuntu:
|
||||
# sudo apt install libftdi-dev libusb-1.0-0-dev
|
||||
git clone -b interfaces https://github.com/tillitis/icestorm tillitis--icestorm
|
||||
cd tillitis--icestorm/iceprog
|
||||
make
|
||||
sudo make PROGRAM_PREFIX=tillitis- install
|
||||
cd ../..
|
||||
|
||||
git clone https://github.com/YosysHQ/yosys
|
||||
cd yosys
|
||||
git checkout yosys-0.26
|
||||
make -j$(nproc)
|
||||
sudo make install
|
||||
cd ..
|
||||
|
||||
git clone https://github.com/YosysHQ/nextpnr
|
||||
cd nextpnr
|
||||
git checkout nextpnr-0.5
|
||||
cmake -DARCH=ice40 -DCMAKE_INSTALL_PREFIX=/usr/local .
|
||||
make -j$(nproc)
|
||||
sudo make install
|
||||
cd ..
|
||||
|
||||
References:
|
||||
* http://bygone.clairexen.net/icestorm/
|
||||
|
||||
## Firmware: riscv toolchain
|
||||
|
||||
The TKey implements a [picorv32](https://github.com/YosysHQ/picorv32)
|
||||
soft core CPU, which is a RISC-V microcontroller with the C
|
||||
instructions and Zmmul extension, multiply without divide
|
||||
(RV32ICZmmul). You can read
|
||||
[more](https://www.sifive.com/blog/all-aboard-part-1-compiler-args)
|
||||
about it.
|
||||
|
||||
The project uses the LLVM/Clang suite and version 15 or later is
|
||||
required. As of writing Ubuntu 22.10 has version 15 packaged. You may
|
||||
be able to get it installed on older Ubuntu and Debian using the
|
||||
instructions on https://apt.llvm.org/ . There are also binary releases
|
||||
here: https://github.com/llvm/llvm-project/releases
|
||||
|
||||
References:
|
||||
* https://github.com/YosysHQ/picorv32
|
||||
|
||||
If your available `objcopy` and `size` commands is anything other than
|
||||
the default `llvm-objcopy` and `llvm-size` define `OBJCOPY` and `SIZE`
|
||||
to whatever they're called on your system before calling `make`.
|
||||
|
||||
## CH552 USB to Serial firmware
|
||||
|
||||
The USB to Serial firmware runs on the CH552 microcontroller, and
|
||||
provides a USB CDC profile which should work with the default drivers
|
||||
on all major operating systems. MTA1-USB-V1 and TK-1 devices come
|
||||
with the CH552 microcontroller pre-programmed.
|
||||
|
||||
Toolchain setup and build instructions for this firmware are detailed
|
||||
in the
|
||||
[ch552_fw directory](../hw/usb_interface/ch552_fw/README.md)
|
|
@ -32,7 +32,7 @@ TARGET_FREQ ?= 24
|
|||
|
||||
# Size in 32-bit words, must be divisible by 256 (pairs of EBRs, because 16
|
||||
# bits wide; an EBR is 128 32-bits words)
|
||||
BRAM_FW_SIZE ?= 1536
|
||||
BRAM_FW_SIZE ?= 2048
|
||||
|
||||
PIN_FILE ?= application_fpga_tk1.pcf
|
||||
|
||||
|
@ -41,13 +41,15 @@ OBJCOPY ?= llvm-objcopy
|
|||
|
||||
CC = clang
|
||||
|
||||
LIBDIR ?= tkey-libs
|
||||
|
||||
CFLAGS = \
|
||||
-target riscv32-unknown-none-elf \
|
||||
-march=rv32iczmmul \
|
||||
-mabi=ilp32 \
|
||||
-static \
|
||||
-std=gnu99 \
|
||||
-O2 \
|
||||
-Os \
|
||||
-ffast-math \
|
||||
-fno-common \
|
||||
-fno-builtin-printf \
|
||||
|
@ -58,8 +60,12 @@ CFLAGS = \
|
|||
-Wall \
|
||||
-Wpedantic \
|
||||
-Wno-language-extension-token \
|
||||
-Wextra \
|
||||
-flto \
|
||||
-g
|
||||
-g \
|
||||
-I $(LIBDIR)/include \
|
||||
-I $(LIBDIR) \
|
||||
-I $(LIBDIR)/blake2s
|
||||
|
||||
AS = clang
|
||||
|
||||
|
@ -67,7 +73,8 @@ ASFLAGS = \
|
|||
-target riscv32-unknown-none-elf \
|
||||
-march=rv32iczmmul \
|
||||
-mabi=ilp32 \
|
||||
-mno-relax
|
||||
-mno-relax \
|
||||
-I $(LIBDIR)/include
|
||||
|
||||
ICE40_SIM_CELLS = $(shell yosys-config --datdir/ice40/cells_sim.v)
|
||||
|
||||
|
@ -113,36 +120,30 @@ PICORV32_SRCS = \
|
|||
$(P)/core/picorv32/rtl/picorv32.v
|
||||
|
||||
FIRMWARE_DEPS = \
|
||||
$(P)/fw/tk1_mem.h \
|
||||
$(P)/fw/tk1/types.h \
|
||||
$(P)/fw/tk1/lib.h \
|
||||
$(P)/fw/tk1/proto.h \
|
||||
$(P)/fw/tk1/assert.h \
|
||||
$(P)/fw/tk1/led.h
|
||||
$(P)/fw/tk1/proto.h
|
||||
|
||||
FIRMWARE_OBJS = \
|
||||
$(P)/fw/tk1/main.o \
|
||||
$(P)/fw/tk1/start.o \
|
||||
$(P)/fw/tk1/proto.o \
|
||||
$(P)/fw/tk1/lib.o \
|
||||
$(P)/fw/tk1/assert.o \
|
||||
$(P)/fw/tk1/led.o \
|
||||
$(P)/fw/tk1/blake2s/blake2s.o
|
||||
$(P)/fw/tk1/syscall_enable.o \
|
||||
$(P)/fw/tk1/syscall_handler.o \
|
||||
$(P)/fw/tk1/spi.o \
|
||||
$(P)/fw/tk1/flash.o \
|
||||
$(P)/fw/tk1/storage.o \
|
||||
$(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
|
||||
|
||||
FIRMWARE_SOURCES = \
|
||||
$(P)/fw/tk1/main.c \
|
||||
$(P)/fw/tk1/proto.c \
|
||||
$(P)/fw/tk1/lib.c \
|
||||
$(P)/fw/tk1/assert.c \
|
||||
$(P)/fw/tk1/led.c \
|
||||
$(P)/fw/tk1/blake2s/blake2s.c
|
||||
CHECK_SOURCES = \
|
||||
$(P)/fw/tk1/*.[ch]
|
||||
|
||||
TESTFW_OBJS = \
|
||||
$(P)/fw/testfw/main.o \
|
||||
$(P)/fw/testfw/start.o \
|
||||
$(P)/fw/tk1/proto.o \
|
||||
$(P)/fw/tk1/lib.o \
|
||||
$(P)/fw/tk1/blake2s/blake2s.o
|
||||
$(P)/fw/testfw/start.o
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
# All: Complete build of HW and FW.
|
||||
|
@ -155,7 +156,10 @@ all: application_fpga.bin
|
|||
# incorrect BRAM_FW_SIZE
|
||||
# -------------------------------------------------------------------
|
||||
%_size_mismatch: %.elf phony_explicit
|
||||
@test $$($(SIZE) $< | awk 'NR==2{print $$4}') -le $$(( 32 / 8 * $(BRAM_FW_SIZE) )) \
|
||||
@test $$(( \
|
||||
$$($(SIZE) -A $< | grep text | awk 'NR==1{print $$2}') + \
|
||||
$$($(SIZE) -A $< | grep text | awk 'NR==2{print $$2}') \
|
||||
)) -le $$(( 32 / 8 * $(BRAM_FW_SIZE) )) \
|
||||
|| { printf "The 'BRAM_FW_SIZE' variable needs to be increased\n"; \
|
||||
[[ $< =~ testfw ]] && printf "Note that testfw fits if built with -Os\n"; \
|
||||
false; }
|
||||
|
@ -176,21 +180,39 @@ secret:
|
|||
# Firmware generation.
|
||||
# Included in the bitstream.
|
||||
#-------------------------------------------------------------------
|
||||
LDFLAGS = -T $(P)/fw/tk1/firmware.lds
|
||||
LDFLAGS = \
|
||||
-T $(P)/fw/tk1/firmware.lds \
|
||||
-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
|
||||
tkey-libs:
|
||||
make -C $(LIBDIR)
|
||||
|
||||
$(FIRMWARE_OBJS): $(FIRMWARE_DEPS)
|
||||
$(TESTFW_OBJS): $(FIRMWARE_DEPS)
|
||||
|
||||
firmware.elf: $(FIRMWARE_OBJS) $(P)/fw/tk1/firmware.lds
|
||||
$(CC) $(CFLAGS) $(FIRMWARE_OBJS) $(LDFLAGS) -o $@
|
||||
#firmware.elf: CFLAGS += -DTKEY_DEBUG
|
||||
firmware.elf: tkey-libs $(FIRMWARE_OBJS) $(P)/fw/tk1/firmware.lds
|
||||
$(CC) $(CFLAGS) $(FIRMWARE_OBJS) $(LDFLAGS) -o $@ > $(basename $@).map
|
||||
|
||||
simfirmware.elf: CFLAGS += -DSIMULATION
|
||||
simfirmware.elf: $(FIRMWARE_OBJS) $(P)/fw/tk1/firmware.lds
|
||||
$(CC) $(CFLAGS) $(FIRMWARE_OBJS) $(LDFLAGS) -o $@
|
||||
$(CC) $(CFLAGS) $(FIRMWARE_OBJS) $(LDFLAGS) -o $@ > $(basename $@).map
|
||||
|
||||
qemu_firmware.elf: CFLAGS += -DQEMU_CONSOLE
|
||||
qemu_firmware.elf: firmware.elf
|
||||
mv firmware.elf qemu_firmware.elf
|
||||
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
|
||||
|
||||
# Create compile_commands.json for clangd and LSP
|
||||
.PHONY: clangd
|
||||
|
@ -201,12 +223,12 @@ compile_commands.json:
|
|||
|
||||
.PHONY: check
|
||||
check:
|
||||
clang-tidy -header-filter=.* -checks=cert-* $(FIRMWARE_SOURCES) -- $(CFLAGS)
|
||||
clang-tidy -header-filter=.* -checks=cert-* $(CHECK_SOURCES) -- $(CFLAGS)
|
||||
|
||||
.PHONY: splint
|
||||
splint:
|
||||
splint \
|
||||
-nolib \
|
||||
+unixlib \
|
||||
-predboolint \
|
||||
+boolint \
|
||||
-nullpass \
|
||||
|
@ -217,21 +239,27 @@ splint:
|
|||
-unreachable \
|
||||
-unqualifiedtrans \
|
||||
-fullinitblock \
|
||||
$(FIRMWARE_SOURCES)
|
||||
+gnuextensions \
|
||||
-fixedformalarray \
|
||||
-mustfreeonly \
|
||||
-I $(LIBDIR)/include \
|
||||
-I $(LIBDIR) \
|
||||
-I $(LIBDIR)/blake2s \
|
||||
$(CHECK_SOURCES)
|
||||
|
||||
testfw.elf: $(TESTFW_OBJS) $(P)/fw/tk1/firmware.lds
|
||||
$(CC) $(CFLAGS) $(TESTFW_OBJS) $(LDFLAGS) -o $@
|
||||
testfw.elf: tkey-libs $(TESTFW_OBJS) $(P)/fw/tk1/firmware.lds
|
||||
$(CC) $(CFLAGS) $(TESTFW_OBJS) $(LDFLAGS) -o $@ > $(basename $@).map
|
||||
|
||||
# Generate a fake BRAM file that will be filled in later after place-n-route
|
||||
bram_fw.hex:
|
||||
$(ICESTORM_PATH)icebram -v -g 32 $(BRAM_FW_SIZE) > $@
|
||||
|
||||
firmware.hex: firmware.bin firmware_size_mismatch
|
||||
python3 $(P)/tools/makehex/makehex.py $< $(BRAM_FW_SIZE) > $@
|
||||
python3 $(P)/tools/makehex.py $< $(BRAM_FW_SIZE) > $@
|
||||
simfirmware.hex: simfirmware.bin simfirmware_size_mismatch
|
||||
python3 $(P)/tools/makehex/makehex.py $< $(BRAM_FW_SIZE) > $@
|
||||
python3 $(P)/tools/makehex.py $< $(BRAM_FW_SIZE) > $@
|
||||
testfw.hex: testfw.bin testfw_size_mismatch
|
||||
python3 $(P)/tools/makehex/makehex.py $< $(BRAM_FW_SIZE) > $@
|
||||
python3 $(P)/tools/makehex.py $< $(BRAM_FW_SIZE) > $@
|
||||
|
||||
.PHONY: check-binary-hashes
|
||||
check-binary-hashes:
|
||||
|
@ -241,9 +269,6 @@ check-binary-hashes:
|
|||
sha256sum -c application_fpga.bin.sha256
|
||||
|
||||
%.bin: %.elf
|
||||
$(SIZE) $<
|
||||
@test "$$($(SIZE) $< | awk 'NR==2{print $$2, $$3}')" = "0 0" \
|
||||
|| { printf "Non-empty data or bss section!\n"; false; }
|
||||
$(OBJCOPY) --input-target=elf32-littleriscv --output-target=binary $< $@
|
||||
chmod -x $@
|
||||
|
||||
|
@ -261,7 +286,8 @@ LINT_FLAGS = \
|
|||
-Wno-WIDTHEXPAND \
|
||||
-Wno-UNOPTFLAT \
|
||||
--timescale 1ns/1ns \
|
||||
-DNO_ICE40_DEFAULT_ASSIGNMENTS
|
||||
-DNO_ICE40_DEFAULT_ASSIGNMENTS \
|
||||
-Wno-GENUNNAMED
|
||||
|
||||
lint: $(FPGA_VERILOG_SRCS) \
|
||||
$(SIM_VERILOG_SRCS) \
|
||||
|
@ -303,6 +329,9 @@ fmt: $(FPGA_VERILOG_SRCS) $(SIM_VERILOG_SRCS) $(VERILATOR_VERILOG_SRCS) $(VERILO
|
|||
# Temporary fix using grep, since the verible with --verify flag only returns
|
||||
# error if the last file is malformatted.
|
||||
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
|
||||
|
@ -353,8 +382,7 @@ tb:
|
|||
|
||||
YOSYS_FLAG ?=
|
||||
|
||||
synth.json: $(FPGA_VERILOG_SRCS) $(VERILOG_SRCS) $(PICORV32_SRCS) bram_fw.hex \
|
||||
$(P)/data/uds.hex $(P)/data/udi.hex
|
||||
synth.json: $(FPGA_VERILOG_SRCS) $(VERILOG_SRCS) $(PICORV32_SRCS) bram_fw.hex
|
||||
$(YOSYS_PATH)yosys \
|
||||
-v3 \
|
||||
-l synth.txt \
|
||||
|
@ -368,7 +396,7 @@ synth.json: $(FPGA_VERILOG_SRCS) $(VERILOG_SRCS) $(PICORV32_SRCS) bram_fw.hex \
|
|||
application_fpga_par.json: synth.json $(P)/data/$(PIN_FILE)
|
||||
$(NEXTPNR_PATH)nextpnr-ice40 \
|
||||
-l application_fpga_par.txt \
|
||||
--seed 9106179903728618585 \
|
||||
--seed 18160564147838858264 \
|
||||
--freq $(TARGET_FREQ) \
|
||||
--ignore-loops \
|
||||
--up5k \
|
||||
|
@ -444,17 +472,23 @@ tb_application_fpga: $(SIM_VERILOG_SRCS) \
|
|||
#-------------------------------------------------------------------
|
||||
|
||||
prog_flash: check-hardware application_fpga.bin
|
||||
sudo tillitis-iceprog application_fpga.bin
|
||||
tillitis-iceprog application_fpga.bin
|
||||
make -C apps
|
||||
(cd tools && ./load_preloaded_app.sh 0 ../apps/testloadapp.bin)
|
||||
.PHONY: prog_flash
|
||||
|
||||
prog_flash_bs: check-hardware application_fpga.bin
|
||||
tillitis-iceprog application_fpga.bin
|
||||
.PHONY: prog_flash_bs
|
||||
|
||||
prog_flash_testfw: check-hardware application_fpga_testfw.bin
|
||||
sudo tillitis-iceprog application_fpga_testfw.bin
|
||||
tillitis-iceprog application_fpga_testfw.bin
|
||||
.PHONY: prog_flash_testfw
|
||||
|
||||
check-hardware:
|
||||
@sudo tillitis-iceprog -t >/dev/null 2>&1 || \
|
||||
@tillitis-iceprog -t >/dev/null 2>&1 || \
|
||||
{ echo "Programmer not plugged in or not accessible"; false; }
|
||||
@if sudo tillitis-iceprog -t 2>&1 | grep -qi "^flash.id:\( 0x\(00\|ff\)\)\{4\}"; then \
|
||||
@if tillitis-iceprog -t 2>&1 | grep -qi "^flash.id:\( 0x\(00\|ff\)\)\{4\}"; then \
|
||||
echo "No USB stick in the programmer?"; false; else true; fi
|
||||
.PHONY: check-hardware
|
||||
|
||||
|
@ -478,18 +512,20 @@ 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:
|
||||
rm -f firmware.{elf,elf.map,bin,hex}
|
||||
rm -f firmware.{elf,map,bin,hex}
|
||||
rm -f $(FIRMWARE_OBJS)
|
||||
rm -f testfw.{elf,elf.map,bin,hex}
|
||||
rm -f testfw.{elf,map,bin,hex}
|
||||
rm -f $(TESTFW_OBJS)
|
||||
rm -f qemu_firmware.elf
|
||||
make -C tkey-libs clean
|
||||
.PHONY: clean_fw
|
||||
|
||||
clean_sim:
|
||||
rm -f simfirmware.{elf,elf.map,bin,hex}
|
||||
rm -f simfirmware.{elf,map,bin,hex}
|
||||
rm -f tb_application_fpga_sim.fst
|
||||
rm -f tb_application_fpga_sim.fst.hier
|
||||
rm -f tb/output_spram*.hex
|
||||
|
@ -525,7 +561,8 @@ help:
|
|||
@echo "tb_application_fpga Build testbench simulation for the design"
|
||||
@echo "lint Run lint on Verilog source files."
|
||||
@echo "tb Run all testbenches"
|
||||
@echo "prog_flash Program device flash with FGPA bitstream including firmware (using the RPi Pico-based programmer)."
|
||||
@echo "prog_flash Program device flash with FGPA bitstream (including firmware), partition table, and testloadapp.bin (using the RPi Pico-based programmer)."
|
||||
@echo "prog_flash_bs Program device flash with FGPA bitstream including firmware (using the RPi Pico-based programmer)."
|
||||
@echo "prog_flash_testfw Program device flash as above, but with testfw."
|
||||
@echo "clean Delete all generated files."
|
||||
@echo "clean_fw Delete only generated files for firmware. Useful for fw devs."
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# TKey hardware design
|
||||
## TKey hardware design
|
||||
|
||||

|
||||
|
||||
|
@ -11,16 +11,18 @@ The design top level is in `rtl/application_fpga.v`. It contains
|
|||
instances of all cores as well as the memory system.
|
||||
|
||||
The memory system allows the CPU to access cores in different ways
|
||||
given the current execution mode. There are two execution modes -
|
||||
firmware and application. Basically, in application mode the access is
|
||||
more restrictive.
|
||||
given the current execution mode. There are three execution modes -
|
||||
firmware, application and system call. Each mode give access to a
|
||||
different set of resources. Where app mode is the most restrictive and
|
||||
firmware mode is the least restrictive.
|
||||
|
||||
The rest of the components are under `cores`. They typically have
|
||||
their own `README.md` file documenting them and their API in detail.
|
||||
The rest of the components are under `cores`, except the firmware,
|
||||
which is under `fw/tk1`. They typically have their own `README.md`
|
||||
file documenting them and their API in detail.
|
||||
|
||||
Hardware functions with APIs, assets, and input/output are memory
|
||||
mapped starting at base address `0xc000_0000`. For specific offsets
|
||||
and bitmasks, see the file `fw/tk1_mem.h`.
|
||||
and bitmasks, see the file `tk1_mem.h` in tkey-libs.
|
||||
|
||||
Rough memory map:
|
||||
|
||||
|
@ -34,8 +36,14 @@ Rough memory map:
|
|||
| UART | 0xc3 |
|
||||
| Touch | 0xc4 |
|
||||
| FW\_RAM | 0xd0 |
|
||||
| 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.
|
||||
|
@ -96,6 +104,54 @@ hours, days) there is also a 32 bit prescaler.
|
|||
|
||||
The timer is available to use by firmware and applications.
|
||||
|
||||
## Syscall
|
||||
|
||||
System call trigger area. A 32-bit write to address 0xe1000000 will
|
||||
trigger interrupt 31, which in turn triggers a system call in the
|
||||
firmware.
|
||||
|
||||
## Interrupts
|
||||
|
||||
Triggering an interrupt will cause the CPU to execute the interrupt
|
||||
handler att address 0x10.
|
||||
|
||||
The interrupt handler is shared by all PicoRV32 interrupts but only
|
||||
interrupt 31 is enabled on the Tkey. Register `x4` can be inspected to
|
||||
determine the interrupt source. Each interrupt source is assigned one
|
||||
bit in x4. Triggered interrupts have their bit set to `1`.
|
||||
|
||||
| *Source* | *x4 Bit* |
|
||||
|----------|----------|
|
||||
| Syscall | 31 |
|
||||
|
||||
The return address is located in register `x3`. Calling the PicoRV32
|
||||
specific instruction `retirq` exits the interrupt handler and clears
|
||||
the interrupt source.
|
||||
|
||||
No registers are stored/restored when entering/exiting the interrupt
|
||||
handler. It is up to the software to store/restore as necessary.
|
||||
|
||||
Interrupts can be enabled/disabled using the PicoRV32 specific
|
||||
`maskirq` instruction.
|
||||
|
||||
## Restricted resources
|
||||
|
||||
The following table shows resource availablility for each execution
|
||||
mode:
|
||||
|
||||
| *Execution Mode* | *ROM* | *FW RAM* | *SPI* | *UDS* |
|
||||
|------------------|-------|----------|-------|-------|
|
||||
| Firmware mode | r/x | r/w | r/w | r/w* |
|
||||
| Syscall | r/x | r/w | r/w | i |
|
||||
| Application mode | r | i | i | i |
|
||||
|
||||
Legend:
|
||||
r = readable
|
||||
w = writeable
|
||||
x = executable
|
||||
i = invisible
|
||||
* = UDS words are readable only once in firmware mode.
|
||||
|
||||
## `tk1`
|
||||
|
||||
See [tk1 README](core/tk1/README.md) for details.
|
||||
|
@ -107,7 +163,6 @@ Contains:
|
|||
- RGB LED control.
|
||||
- General purpose input/output (GPIO) pin control.
|
||||
- Application introspection: start address and size of binary.
|
||||
- BLAKE2s function access.
|
||||
- Compound Device Identity (CDI).
|
||||
- Unique Device Identity (UDI).
|
||||
- RAM memory protection.
|
||||
|
@ -135,13 +190,13 @@ should make it infeasible to improve asset extraction by observing
|
|||
multiple memory dumps from the same TKey device. The attack should
|
||||
also not directly scale to multiple TKey devices.
|
||||
|
||||
The RAM address and data scrambling is done in de RAM core.
|
||||
The RAM address and data scrambling is done in the RAM core.
|
||||
|
||||
The memory protection is setup by the firmware. Access to the memory
|
||||
protection controls is disabled for applications. Before the memory
|
||||
protecetion is enabled, the RAM is filled with randomised data using
|
||||
Xorwow. So during boot the firmware perform the following steps to
|
||||
setup the memory protection:
|
||||
Xorwow. During boot the firmware perform the following steps to setup
|
||||
the memory protection:
|
||||
|
||||
1. Get a random 32-bit value from the TRNG to use as data state for
|
||||
Xorwow.
|
||||
|
|
|
@ -1 +1 @@
|
|||
8b09a7b2c9b864711e19f85de2785c8ea52f454207943c13bb17fff1ce095711 application_fpga.bin
|
||||
dfba361c83337c6bac2364d1fd8f115eeb7feeae5faabbdf0713c79b44bbd78d application_fpga.bin
|
||||
|
|
123
hw/application_fpga/apps/Makefile
Normal file
123
hw/application_fpga/apps/Makefile
Normal file
|
@ -0,0 +1,123 @@
|
|||
P := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
|
||||
LIBDIR ?= ../tkey-libs
|
||||
OBJCOPY ?= llvm-objcopy
|
||||
CC = clang
|
||||
CFLAGS = \
|
||||
-target riscv32-unknown-none-elf \
|
||||
-march=rv32iczmmul \
|
||||
-mabi=ilp32 \
|
||||
-mcmodel=medany \
|
||||
-static \
|
||||
-std=gnu99 \
|
||||
-Os \
|
||||
-ffast-math \
|
||||
-fno-common \
|
||||
-fno-builtin-printf \
|
||||
-fno-builtin-putchar \
|
||||
-fno-builtin-memcpy \
|
||||
-nostdlib \
|
||||
-mno-relax \
|
||||
-Wall \
|
||||
-Wpedantic \
|
||||
-Wno-language-extension-token \
|
||||
-Werror \
|
||||
-flto \
|
||||
-g \
|
||||
-I $(LIBDIR)/include \
|
||||
-I $(LIBDIR) \
|
||||
-I include \
|
||||
-I ../
|
||||
|
||||
AS = clang
|
||||
|
||||
ASFLAGS = \
|
||||
-target riscv32-unknown-none-elf \
|
||||
-march=rv32iczmmul \
|
||||
-mabi=ilp32 \
|
||||
-mno-relax
|
||||
|
||||
LDFLAGS = \
|
||||
-T $(LIBDIR)/app.lds \
|
||||
-L $(LIBDIR) -lcrt0 -lcommon -lmonocypher -lblake2s
|
||||
|
||||
.PHONY: all
|
||||
all: defaultapp.bin reset_test.bin testapp.bin testloadapp.bin
|
||||
|
||||
# Turn elf into bin for device
|
||||
%.bin: %.elf
|
||||
$(OBJCOPY) --input-target=elf32-littleriscv --output-target=binary $^ $@
|
||||
chmod a-x $@
|
||||
|
||||
.PHONY: tkey-libs
|
||||
tkey-libs:
|
||||
make -C $(LIBDIR)
|
||||
|
||||
OBJS=syscall.o
|
||||
|
||||
# syscall.o: syscall.S
|
||||
# $(CC) $(CFLAGS) $(DEFAULTAPP_OBJS) $(LDFLAGS) -o $@
|
||||
|
||||
# defaultapp
|
||||
DEFAULTAPP_FMTFILES = *.[ch]
|
||||
|
||||
DEFAULTAPP_OBJS = \
|
||||
$(P)/defaultapp/main.o
|
||||
|
||||
defaultapp.elf: tkey-libs $(OBJS) $(DEFAULTAPP_OBJS)
|
||||
$(CC) $(CFLAGS) $(OBJS) $(DEFAULTAPP_OBJS) $(LDFLAGS) -o $@
|
||||
|
||||
# reset_test
|
||||
|
||||
RESET_TEST_FMTFILES = *.[ch]
|
||||
|
||||
RESET_TEST_OBJS = \
|
||||
$(P)/reset_test/main.o
|
||||
|
||||
reset_test.elf: tkey-libs $(RESET_TEST_OBJS)
|
||||
$(CC) $(CFLAGS) $(OBJS) $(RESET_TEST_OBJS) $(LDFLAGS) -o $@
|
||||
|
||||
# testapp
|
||||
|
||||
TESTAPP_OBJS = \
|
||||
$(P)/testapp/main.o
|
||||
|
||||
testapp.elf: tkey-libs $(TESTAPP_OBJS)
|
||||
$(CC) $(CFLAGS) $(OBJS) $(TESTAPP_OBJS) $(LDFLAGS) -o $@
|
||||
|
||||
# testloadapp
|
||||
|
||||
TESTLOADAPP_OBJS = \
|
||||
$(P)/testloadapp/main.o
|
||||
|
||||
testloadapp.elf: tkey-libs $(TESTLOADAPP_OBJS)
|
||||
$(CC) $(CFLAGS) $(OBJS) $(TESTLOADAPP_OBJS) $(LDFLAGS) -o $@
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
clang-format --dry-run --ferror-limit=0 defaultapp/*.[ch]
|
||||
clang-format --verbose -i defaultapp/*.[ch]
|
||||
|
||||
clang-format --dry-run --ferror-limit=0 reset_test/*.[ch]
|
||||
clang-format --verbose -i reset_test/*.[ch]
|
||||
|
||||
clang-format --dry-run --ferror-limit=0 testapp/*.[ch]
|
||||
clang-format --verbose -i testapp/*.[ch]
|
||||
|
||||
clang-format --dry-run --ferror-limit=0 testloadapp/*.[ch]
|
||||
clang-format --verbose -i testloadapp/*.[ch]
|
||||
|
||||
.PHONY: checkfmt
|
||||
checkfmt:
|
||||
clang-format --dry-run --ferror-limit=0 defaultapp/*.[ch]
|
||||
|
||||
clang-format --dry-run --ferror-limit=0 reset_test/*.[ch]
|
||||
|
||||
clang-format --dry-run --ferror-limit=0 testapp/*.[ch]
|
||||
|
||||
clang-format --dry-run --ferror-limit=0 testloadapp/*.[ch]
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f *.elf *.bin $(OBJS) $(DEFAULTAPP_OBJS) $(RESET_TEST_OBJS) \
|
||||
$(TESTAPP_OBJS) $(TESTLOADAPP_OBJS)
|
||||
|
36
hw/application_fpga/apps/README.md
Normal file
36
hw/application_fpga/apps/README.md
Normal file
|
@ -0,0 +1,36 @@
|
|||
# Test applications
|
||||
|
||||
- `defaultapp`: Immediately resets the TKey with the intention to
|
||||
start an app from the client, replicating the behaviour of earlier
|
||||
generations.
|
||||
- `testapp`: Runs through a couple of tests that are now impossible
|
||||
to do in the `testfw`.
|
||||
- `reset_test`: Interactively test different reset scenarios.
|
||||
- `testloadapp`: Interactively test management app things like
|
||||
installing an app (hardcoded for a small happy blinking app, see
|
||||
`blink.h` for the entire binary!) and to test verified boot.
|
||||
|
||||
## Build
|
||||
|
||||
```
|
||||
$ make
|
||||
```
|
||||
|
||||
will build all the .elf and .bin files on the top level.
|
||||
|
||||
## Use
|
||||
|
||||
Use `tkey-runapp` from
|
||||
[tkey-devtools](https://github.com/tillitis/tkey-devtools) to load the
|
||||
apps:
|
||||
|
||||
```
|
||||
$ tkey-runapp testapp.bin
|
||||
```
|
||||
|
||||
All of these test apps are controlled through the USB CDC, typically
|
||||
by running picocom or similar terminal program, like:
|
||||
|
||||
```
|
||||
$ picocom /dev/ttyACM1
|
||||
```
|
18
hw/application_fpga/apps/defaultapp/main.c
Normal file
18
hw/application_fpga/apps/defaultapp/main.c
Normal file
|
@ -0,0 +1,18 @@
|
|||
// Copyright (C) 2025 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <fw/tk1/reset.h>
|
||||
#include <fw/tk1/syscall_num.h>
|
||||
#include <syscall.h>
|
||||
#include <tkey/debug.h>
|
||||
#include <tkey/led.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct reset rst = {0};
|
||||
|
||||
led_set(LED_BLUE);
|
||||
|
||||
rst.type = START_CLIENT;
|
||||
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
|
||||
}
|
11
hw/application_fpga/apps/include/syscall.h
Normal file
11
hw/application_fpga/apps/include/syscall.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
// SPDX-FileCopyrightText: 2024 Tillitis AB <tillitis.se>
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef TKEY_APP_SYSCALL_H
|
||||
#define TKEY_APP_SYSCALL_H
|
||||
|
||||
int syscall(uint32_t number, uint32_t arg1, uint32_t arg2, uint32_t arg3);
|
||||
|
||||
#endif
|
158
hw/application_fpga/apps/reset_test/main.c
Normal file
158
hw/application_fpga/apps/reset_test/main.c
Normal file
|
@ -0,0 +1,158 @@
|
|||
// 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>
|
||||
#include <tkey/led.h>
|
||||
#include <tkey/lib.h>
|
||||
#include <tkey/tk1_mem.h>
|
||||
|
||||
// Converts a single hex character to its integer value
|
||||
static uint8_t hex_char_to_byte(uint8_t c)
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
if (c >= 'A' && c <= 'F')
|
||||
return c - 'A' + 10;
|
||||
if (c >= 'a' && c <= 'f')
|
||||
return c - 'a' + 10;
|
||||
return 0; // Invalid character, should not happen if input is valid
|
||||
}
|
||||
|
||||
// Converts a 64-char hex string into a 32-byte array
|
||||
int hex_string_to_bytes(uint8_t *hex_str, uint8_t *out_bytes, size_t out_len)
|
||||
{
|
||||
if (!hex_str || !out_bytes || out_len < 32)
|
||||
return -1; // Error handling
|
||||
|
||||
for (size_t i = 0; i < 32; i++) {
|
||||
out_bytes[i] = (hex_char_to_byte(hex_str[i * 2]) << 4) |
|
||||
hex_char_to_byte(hex_str[i * 2 + 1]);
|
||||
}
|
||||
|
||||
return 0; // Success
|
||||
}
|
||||
//---------------------------------------
|
||||
|
||||
#define BUFSIZE 32
|
||||
|
||||
int main(void)
|
||||
{
|
||||
uint8_t available = 0;
|
||||
uint8_t cmdbuf[BUFSIZE] = {0};
|
||||
enum ioend endpoint = IO_NONE;
|
||||
led_set(LED_BLUE);
|
||||
struct reset rst = {0};
|
||||
|
||||
while (1) {
|
||||
|
||||
puts(IO_CDC, "reset_test: Waiting for command\r\n");
|
||||
|
||||
memset(cmdbuf, 0, BUFSIZE);
|
||||
|
||||
// Wait for data
|
||||
if (readselect(IO_CDC, &endpoint, &available) < 0) {
|
||||
assert(1 == 2);
|
||||
}
|
||||
|
||||
if (read(IO_CDC, cmdbuf, BUFSIZE, available) < 0) {
|
||||
// read failed! I/O broken? Just redblink.
|
||||
assert(1 == 2);
|
||||
}
|
||||
|
||||
led_set(LED_BLUE | LED_RED);
|
||||
|
||||
switch (cmdbuf[0]) {
|
||||
case '1':
|
||||
// Reset into default state
|
||||
|
||||
rst.type = START_DEFAULT;
|
||||
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
|
||||
break;
|
||||
|
||||
case '2':
|
||||
// Reset and load app from client
|
||||
|
||||
rst.type = START_CLIENT;
|
||||
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
|
||||
break;
|
||||
|
||||
case '3':
|
||||
// Reset and load app from second flash slot
|
||||
|
||||
rst.type = START_FLASH1;
|
||||
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
|
||||
break;
|
||||
|
||||
case '4': {
|
||||
// Reset and load app from client with verification
|
||||
// using an invalid digest.
|
||||
//
|
||||
// Should cause firmware to refuse to start app.
|
||||
|
||||
uint8_t string[] = "0123456789abcdef0123456789abcdef012"
|
||||
"3456789abcdef0123456789abcdef";
|
||||
rst.type = START_CLIENT_VER;
|
||||
hex_string_to_bytes(string, (uint8_t *)&rst.app_digest,
|
||||
sizeof(rst.app_digest));
|
||||
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
|
||||
} break;
|
||||
|
||||
case '5': {
|
||||
// Reset and load app from client with verification
|
||||
// using a digest matching the example app (blue.bin)
|
||||
// from tkey-libs
|
||||
|
||||
uint8_t tkeylibs_example_app_digest[] =
|
||||
"96bb4c90603dbbbe09b9a1d7259b5e9e61bedd89a897105c30"
|
||||
"c9d4bf66a98d97";
|
||||
rst.type = START_CLIENT_VER;
|
||||
hex_string_to_bytes(tkeylibs_example_app_digest,
|
||||
(uint8_t *)&rst.app_digest,
|
||||
sizeof(rst.app_digest));
|
||||
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
|
||||
} break;
|
||||
|
||||
case '6': {
|
||||
// Reset and load app from second flash slot with
|
||||
// verification using an invalid digest.
|
||||
//
|
||||
// Should cause firmware to refuse to start app.
|
||||
|
||||
uint8_t string[] = "0123456789abcdef0123456789abcdef012"
|
||||
"3456789abcdef0123456789abcdef";
|
||||
rst.type = START_FLASH1_VER;
|
||||
hex_string_to_bytes(string, (uint8_t *)&rst.app_digest,
|
||||
sizeof(rst.app_digest));
|
||||
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
|
||||
} break;
|
||||
|
||||
case '7': {
|
||||
// Reset and load app from second flash slot with
|
||||
// verification using a digest matching the example app
|
||||
// (blue.bin) from tkey-libs
|
||||
//
|
||||
// Blue.bin has to be present on flash in the second
|
||||
// preloaded app slot (slot 1).
|
||||
|
||||
uint8_t tkeylibs_example_app_digest[] =
|
||||
"96bb4c90603dbbbe09b9a1d7259b5e9e61bedd89a897105c30"
|
||||
"c9d4bf66a98d97";
|
||||
rst.type = START_FLASH1_VER;
|
||||
hex_string_to_bytes(tkeylibs_example_app_digest,
|
||||
(uint8_t *)&rst.app_digest,
|
||||
sizeof(rst.app_digest));
|
||||
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
|
||||
} break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
85
hw/application_fpga/apps/syscall.S
Normal file
85
hw/application_fpga/apps/syscall.S
Normal file
|
@ -0,0 +1,85 @@
|
|||
// SPDX-FileCopyrightText: 2024 Tillitis AB <tillitis.se>
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
#include "../fw/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
|
295
hw/application_fpga/apps/testapp/main.c
Normal file
295
hw/application_fpga/apps/testapp/main.c
Normal file
|
@ -0,0 +1,295 @@
|
|||
// 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>
|
||||
#include <tkey/led.h>
|
||||
#include <tkey/lib.h>
|
||||
#include <tkey/tk1_mem.h>
|
||||
|
||||
#include "syscall.h"
|
||||
|
||||
#define USBMODE_PACKET_SIZE 64
|
||||
|
||||
// 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;
|
||||
volatile uint8_t *fw_ram = (volatile uint8_t *)TK1_MMIO_FW_RAM_BASE;
|
||||
volatile uint32_t *system_reset = (volatile uint32_t *)TK1_MMIO_TK1_SYSTEM_RESET;
|
||||
volatile uint32_t *timer = (volatile uint32_t *)TK1_MMIO_TIMER_TIMER;
|
||||
volatile uint32_t *timer_prescaler = (volatile uint32_t *)TK1_MMIO_TIMER_PRESCALER;
|
||||
volatile uint32_t *timer_status = (volatile uint32_t *)TK1_MMIO_TIMER_STATUS;
|
||||
volatile uint32_t *timer_ctrl = (volatile uint32_t *)TK1_MMIO_TIMER_CTRL;
|
||||
volatile uint32_t *trng_status = (volatile uint32_t *)TK1_MMIO_TRNG_STATUS;
|
||||
volatile uint32_t *trng_entropy = (volatile uint32_t *)TK1_MMIO_TRNG_ENTROPY;
|
||||
// clang-format on
|
||||
|
||||
#define UDS_WORDS 8
|
||||
#define UDI_WORDS 2
|
||||
#define CDI_WORDS 8
|
||||
|
||||
void puthexn(uint8_t *p, int n)
|
||||
{
|
||||
for (int i = 0; i < n; i++) {
|
||||
puthex(IO_CDC, p[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void reverseword(uint32_t *wordp)
|
||||
{
|
||||
*wordp = ((*wordp & 0xff000000) >> 24) | ((*wordp & 0x00ff0000) >> 8) |
|
||||
((*wordp & 0x0000ff00) << 8) | ((*wordp & 0x000000ff) << 24);
|
||||
}
|
||||
|
||||
uint32_t wait_timer_tick(uint32_t last_timer)
|
||||
{
|
||||
uint32_t newtimer;
|
||||
for (;;) {
|
||||
newtimer = *timer;
|
||||
if (newtimer != last_timer) {
|
||||
return newtimer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void failmsg(char *s)
|
||||
{
|
||||
puts(IO_CDC, "FAIL: ");
|
||||
puts(IO_CDC, s);
|
||||
puts(IO_CDC, "\r\n");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
uint8_t in = 0;
|
||||
uint8_t available = 0;
|
||||
enum ioend endpoint = IO_NONE;
|
||||
|
||||
led_set(LED_BLUE);
|
||||
|
||||
// Wait for terminal program and a character to be typed
|
||||
if (readselect(IO_CDC, &endpoint, &available) < 0) {
|
||||
// readselect failed! I/O broken? Just redblink.
|
||||
assert(1 == 2);
|
||||
}
|
||||
|
||||
if (read(IO_CDC, &in, 1, 1) < 0) {
|
||||
// read failed! I/O broken? Just redblink.
|
||||
assert(1 == 2);
|
||||
}
|
||||
|
||||
puts(IO_CDC, "\r\nI'm testapp on:");
|
||||
// Output the TK1 core's NAME0 and NAME1
|
||||
uint32_t name;
|
||||
wordcpy_s(&name, 1, (void *)tk1name0, 1);
|
||||
reverseword(&name);
|
||||
write(IO_CDC, (const uint8_t *)&name, 4);
|
||||
puts(IO_CDC, " ");
|
||||
wordcpy_s(&name, 1, (void *)tk1name1, 1);
|
||||
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);
|
||||
|
||||
int anyfailed = 0;
|
||||
|
||||
uint32_t uds_local[UDS_WORDS];
|
||||
uint32_t udi_local[UDI_WORDS];
|
||||
|
||||
// Should NOT be able to read from UDS in app-mode.
|
||||
wordcpy_s(uds_local, UDS_WORDS, (void *)uds, UDS_WORDS);
|
||||
if (!memeq(uds_local, zeros, UDS_WORDS * 4)) {
|
||||
failmsg("Read from UDS in app-mode");
|
||||
anyfailed = 1;
|
||||
}
|
||||
|
||||
// Should NOT be able to read from UDI in app-mode.
|
||||
wordcpy_s(udi_local, UDI_WORDS, (void *)udi, UDI_WORDS);
|
||||
if (!memeq(udi_local, zeros, UDI_WORDS * 4)) {
|
||||
failmsg("Read from UDI in app-mode");
|
||||
anyfailed = 1;
|
||||
}
|
||||
|
||||
// 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");
|
||||
anyfailed = 1;
|
||||
}
|
||||
|
||||
puts(IO_CDC, "\r\nAllocating storage area...");
|
||||
|
||||
if (syscall(TK1_SYSCALL_ALLOC_AREA, 0, 0, 0) != 0) {
|
||||
failmsg("Failed to allocate storage area");
|
||||
}
|
||||
puts(IO_CDC, "done.\r\n");
|
||||
|
||||
puts(IO_CDC, "\r\nWriting to storage area...");
|
||||
|
||||
uint8_t out_data[14] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};
|
||||
if (syscall(TK1_SYSCALL_WRITE_DATA, 0, (uint32_t)out_data,
|
||||
sizeof(out_data)) != 0) {
|
||||
failmsg("Failed to write to storage area");
|
||||
}
|
||||
puts(IO_CDC, "done.\r\n");
|
||||
|
||||
puts(IO_CDC, "\r\nReading data from storage area...");
|
||||
|
||||
uint8_t in_data[14] = {0};
|
||||
if (syscall(TK1_SYSCALL_READ_DATA, 0, (uint32_t)in_data,
|
||||
sizeof(in_data)) != 0) {
|
||||
failmsg("Failed to write to storage area");
|
||||
}
|
||||
if (!memeq(in_data, out_data, sizeof(in_data))) {
|
||||
failmsg("Failed to read back data from storage area");
|
||||
anyfailed = 1;
|
||||
}
|
||||
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) {
|
||||
failmsg("Failed to deallocate storage area");
|
||||
}
|
||||
puts(IO_CDC, "done.\r\n");
|
||||
|
||||
uint32_t cdi_local[CDI_WORDS];
|
||||
uint32_t cdi_local2[CDI_WORDS];
|
||||
wordcpy_s(cdi_local, CDI_WORDS, (void *)cdi, CDI_WORDS);
|
||||
|
||||
// Write to CDI should NOT have any effect in app mode.
|
||||
wordcpy_s((void *)cdi, CDI_WORDS, zeros, CDI_WORDS);
|
||||
wordcpy_s(cdi_local2, CDI_WORDS, (void *)cdi, CDI_WORDS);
|
||||
if (!memeq(cdi_local, cdi_local2, CDI_WORDS * 4)) {
|
||||
failmsg("Write to CDI in app-mode");
|
||||
anyfailed = 1;
|
||||
}
|
||||
|
||||
// Should NOT be able to reset Tkey from app mode
|
||||
puts(IO_CDC, "\r\nTesting system reset...");
|
||||
*system_reset = 1;
|
||||
puts(IO_CDC, "done.\r\n");
|
||||
|
||||
// Test FW_RAM.
|
||||
*fw_ram = 0x21;
|
||||
if (*fw_ram == 0x21) {
|
||||
failmsg("Write and read FW RAM in app-mode");
|
||||
anyfailed = 1;
|
||||
}
|
||||
|
||||
puts(IO_CDC, "\r\nTesting timer... 3");
|
||||
// Matching clock at 24 MHz, giving us timer in seconds
|
||||
*timer_prescaler = 24 * 1000000;
|
||||
|
||||
// Test timer expiration after 1s
|
||||
*timer = 1;
|
||||
// Start the timer
|
||||
*timer_ctrl = (1 << TK1_MMIO_TIMER_CTRL_START_BIT);
|
||||
while (*timer_status & (1 << TK1_MMIO_TIMER_STATUS_RUNNING_BIT)) {
|
||||
}
|
||||
// Now timer has expired and is ready to run again
|
||||
puts(IO_CDC, " 2");
|
||||
|
||||
// Test to interrupt a timer - and reads from timer register
|
||||
// Starting 10s timer and interrupting it in 3s...
|
||||
*timer = 10;
|
||||
*timer_ctrl = (1 << TK1_MMIO_TIMER_CTRL_START_BIT);
|
||||
uint32_t last_timer = 10;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
last_timer = wait_timer_tick(last_timer);
|
||||
}
|
||||
|
||||
// Stop the timer
|
||||
*timer_ctrl = (1 << TK1_MMIO_TIMER_CTRL_STOP_BIT);
|
||||
puts(IO_CDC, " 1. done.\r\n");
|
||||
|
||||
if (*timer_status & (1 << TK1_MMIO_TIMER_STATUS_RUNNING_BIT)) {
|
||||
failmsg("Timer didn't stop");
|
||||
anyfailed = 1;
|
||||
}
|
||||
|
||||
if (*timer != 10) {
|
||||
failmsg("Timer didn't reset to 10");
|
||||
anyfailed = 1;
|
||||
}
|
||||
|
||||
// Check and display test results.
|
||||
puts(IO_CDC, "\r\n--> ");
|
||||
if (anyfailed) {
|
||||
puts(IO_CDC, "Some test FAILED!\r\n");
|
||||
} else {
|
||||
puts(IO_CDC, "All tests passed.\r\n");
|
||||
}
|
||||
|
||||
puts(IO_CDC, "\r\nHere are 256 bytes from the TRNG:\r\n");
|
||||
for (int j = 0; j < 8; j++) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
while ((*trng_status &
|
||||
(1 << TK1_MMIO_TRNG_STATUS_READY_BIT)) == 0) {
|
||||
}
|
||||
uint32_t rnd = *trng_entropy;
|
||||
puthexn((uint8_t *)&rnd, 4);
|
||||
puts(IO_CDC, " ");
|
||||
}
|
||||
puts(IO_CDC, "\r\n");
|
||||
}
|
||||
puts(IO_CDC, "\r\n");
|
||||
|
||||
puts(IO_CDC, "Now echoing what you type...Type + to reset device\r\n");
|
||||
for (;;) {
|
||||
if (readselect(IO_CDC, &endpoint, &available) < 0) {
|
||||
// readselect failed! I/O broken? Just redblink.
|
||||
assert(1 == 2);
|
||||
}
|
||||
|
||||
if (read(IO_CDC, &in, 1, 1) < 0) {
|
||||
// read failed! I/O broken? Just redblink.
|
||||
assert(1 == 2);
|
||||
}
|
||||
|
||||
if (in == '+') {
|
||||
struct reset rst;
|
||||
memset(&rst, 0, sizeof(rst));
|
||||
rst.type = START_DEFAULT;
|
||||
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
|
||||
}
|
||||
|
||||
write(IO_CDC, &in, 1);
|
||||
}
|
||||
}
|
31
hw/application_fpga/apps/testloadapp/blink.h
Normal file
31
hw/application_fpga/apps/testloadapp/blink.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
// Copyright (C) 2025 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#ifndef BLINK_APP_H
|
||||
#define BLINK_APP_H
|
||||
|
||||
uint8_t blink[] = {
|
||||
0x81, 0x40, 0x01, 0x41, 0x81, 0x41, 0x01, 0x42, 0x81, 0x42, 0x01, 0x43,
|
||||
0x81, 0x43, 0x01, 0x44, 0x81, 0x44, 0x01, 0x45, 0x81, 0x45, 0x01, 0x46,
|
||||
0x81, 0x46, 0x01, 0x47, 0x81, 0x47, 0x01, 0x48, 0x81, 0x48, 0x01, 0x49,
|
||||
0x81, 0x49, 0x01, 0x4a, 0x81, 0x4a, 0x01, 0x4b, 0x81, 0x4b, 0x01, 0x4c,
|
||||
0x81, 0x4c, 0x01, 0x4d, 0x81, 0x4d, 0x01, 0x4e, 0x81, 0x4e, 0x01, 0x4f,
|
||||
0x81, 0x4f, 0x37, 0x01, 0x02, 0x40, 0x41, 0x11, 0x17, 0x05, 0x00, 0x00,
|
||||
0x13, 0x05, 0x45, 0x0c, 0x97, 0x05, 0x00, 0x00, 0x93, 0x85, 0xc5, 0x0b,
|
||||
0x63, 0x57, 0xb5, 0x00, 0x23, 0x20, 0x05, 0x00, 0x11, 0x05, 0xe3, 0x4d,
|
||||
0xb5, 0xfe, 0x97, 0x00, 0x00, 0x00, 0xe7, 0x80, 0xa0, 0x00, 0x00, 0x00,
|
||||
0x41, 0x11, 0x37, 0x05, 0x00, 0xff, 0x11, 0x48, 0xe1, 0x66, 0x13, 0x86,
|
||||
0xf6, 0x69, 0x93, 0x86, 0x06, 0x6a, 0x09, 0x47, 0x85, 0x47, 0x23, 0x22,
|
||||
0x05, 0x03, 0x02, 0xc2, 0x92, 0x45, 0x63, 0x68, 0xb6, 0x00, 0x92, 0x45,
|
||||
0x85, 0x05, 0x2e, 0xc2, 0x92, 0x45, 0xe3, 0xec, 0xd5, 0xfe, 0x58, 0xd1,
|
||||
0x02, 0xc4, 0xa2, 0x45, 0x63, 0x68, 0xb6, 0x00, 0xa2, 0x45, 0x85, 0x05,
|
||||
0x2e, 0xc4, 0xa2, 0x45, 0xe3, 0xec, 0xd5, 0xfe, 0x5c, 0xd1, 0x02, 0xc6,
|
||||
0xb2, 0x45, 0xe3, 0x66, 0xb6, 0xfc, 0xb2, 0x45, 0x85, 0x05, 0x2e, 0xc6,
|
||||
0xb2, 0x45, 0xe3, 0xec, 0xd5, 0xfe, 0x75, 0xbf, 0x19, 0xca, 0x2a, 0x96,
|
||||
0xaa, 0x86, 0x03, 0xc7, 0x05, 0x00, 0x23, 0x80, 0xe6, 0x00, 0x85, 0x06,
|
||||
0x85, 0x05, 0xe3, 0x9a, 0xc6, 0xfe, 0x82, 0x80, 0x11, 0xca, 0x0a, 0x06,
|
||||
0x2a, 0x96, 0xaa, 0x86, 0x98, 0x41, 0x98, 0xc2, 0x91, 0x06, 0x91, 0x05,
|
||||
0xe3, 0x9c, 0xc6, 0xfe, 0x82, 0x80, 0x01, 0xca, 0x2a, 0x96, 0xaa, 0x86,
|
||||
0x23, 0x80, 0xb6, 0x00, 0x85, 0x06, 0xe3, 0x9d, 0xc6, 0xfe, 0x82, 0x80};
|
||||
|
||||
#endif
|
228
hw/application_fpga/apps/testloadapp/main.c
Normal file
228
hw/application_fpga/apps/testloadapp/main.c
Normal file
|
@ -0,0 +1,228 @@
|
|||
// 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 "blink.h"
|
||||
#include "syscall.h"
|
||||
|
||||
// clang-format off
|
||||
static volatile uint32_t *cdi = (volatile uint32_t *) TK1_MMIO_TK1_CDI_FIRST;
|
||||
// clang-format on
|
||||
|
||||
int install_app(uint8_t secret_key[64])
|
||||
{
|
||||
uint8_t app_digest[32];
|
||||
uint8_t app_signature[64];
|
||||
size_t app_size = sizeof(blink);
|
||||
int ret = 0;
|
||||
|
||||
ret = syscall(TK1_SYSCALL_PRELOAD_DELETE, 0, 0, 0);
|
||||
|
||||
if (ret != 0) {
|
||||
puts(IO_CDC, "couldn't delete preloaded app. error: 0x");
|
||||
putinthex(IO_CDC, ret);
|
||||
puts(IO_CDC, "\r\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = syscall(TK1_SYSCALL_PRELOAD_STORE, 0, (uint32_t)blink,
|
||||
sizeof(blink));
|
||||
|
||||
if (ret != 0) {
|
||||
puts(IO_CDC, "couldn't store app, error: 0x");
|
||||
putinthex(IO_CDC, ret);
|
||||
puts(IO_CDC, "\r\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
puts(IO_CDC, "blink: ");
|
||||
putinthex(IO_CDC, (uint32_t)blink);
|
||||
puts(IO_CDC, "\r\n");
|
||||
|
||||
puts(IO_CDC, "blink[0]: ");
|
||||
putinthex(IO_CDC, blink[0]);
|
||||
puts(IO_CDC, "\r\n");
|
||||
|
||||
puts(IO_CDC, "sizeof(blink): ");
|
||||
putinthex(IO_CDC, sizeof(blink));
|
||||
puts(IO_CDC, "\r\n");
|
||||
|
||||
if (blake2s(app_digest, 32, NULL, 0, blink, sizeof(blink)) != 0) {
|
||||
puts(IO_CDC, "couldn't compute digest\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
crypto_ed25519_sign(app_signature, secret_key, app_digest,
|
||||
sizeof(app_digest));
|
||||
|
||||
puts(IO_CDC, "app_digest:\r\n");
|
||||
hexdump(IO_CDC, app_digest, sizeof(app_digest));
|
||||
puts(IO_CDC, "\r\n");
|
||||
|
||||
puts(IO_CDC, "app_signature:\r\n");
|
||||
hexdump(IO_CDC, app_signature, sizeof(app_signature));
|
||||
puts(IO_CDC, "\r\n");
|
||||
|
||||
puts(IO_CDC, "secret_key:\r\n");
|
||||
hexdump(IO_CDC, secret_key, 64);
|
||||
puts(IO_CDC, "\r\n");
|
||||
|
||||
ret = syscall(TK1_SYSCALL_PRELOAD_STORE_FIN, app_size,
|
||||
(uint32_t)app_digest, (uint32_t)app_signature);
|
||||
|
||||
if (ret != 0) {
|
||||
puts(IO_CDC, "couldn't finalize storing app, error:");
|
||||
putinthex(IO_CDC, ret);
|
||||
puts(IO_CDC, "\r\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int verify(uint8_t pubkey[32])
|
||||
{
|
||||
uint8_t app_digest[32];
|
||||
uint8_t app_signature[64];
|
||||
int ret = 0;
|
||||
|
||||
// pubkey we already have
|
||||
// read signature
|
||||
// read digest
|
||||
ret = syscall(TK1_SYSCALL_PRELOAD_GET_DIGSIG, (uint32_t)app_digest,
|
||||
(uint32_t)app_signature, 0);
|
||||
|
||||
if (ret != 0) {
|
||||
puts(IO_CDC, "couldn't get digsig, error:");
|
||||
putinthex(IO_CDC, ret);
|
||||
puts(IO_CDC, "\r\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
puts(IO_CDC, "app_digest:\r\n");
|
||||
hexdump(IO_CDC, app_digest, sizeof(app_digest));
|
||||
puts(IO_CDC, "\r\n");
|
||||
|
||||
puts(IO_CDC, "app_signature:\r\n");
|
||||
hexdump(IO_CDC, app_signature, sizeof(app_signature));
|
||||
puts(IO_CDC, "\r\n");
|
||||
|
||||
puts(IO_CDC, "pubkey:\r\n");
|
||||
hexdump(IO_CDC, pubkey, 32);
|
||||
puts(IO_CDC, "\r\n");
|
||||
|
||||
puts(IO_CDC, "Checking signature...\r\n");
|
||||
|
||||
if (crypto_ed25519_check(app_signature, pubkey, app_digest,
|
||||
sizeof(app_digest)) != 0) {
|
||||
puts(IO_CDC, "signature check failed\r\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
puts(IO_CDC, "Resetting into pre loaded app (slot 2)...\r\n");
|
||||
|
||||
// syscall reset flash1_ver with app_digest
|
||||
struct reset rst;
|
||||
rst.type = START_FLASH1_VER;
|
||||
memcpy_s(rst.app_digest, sizeof(rst.app_digest), app_digest,
|
||||
sizeof(app_digest));
|
||||
memset(rst.next_app_data, 0, sizeof(rst.next_app_data));
|
||||
|
||||
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
|
||||
|
||||
return -2;
|
||||
}
|
||||
|
||||
void reset_from_client(void)
|
||||
{
|
||||
struct reset rst = {0};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
uint8_t secret_key[64];
|
||||
uint8_t pubkey[32];
|
||||
enum ioend endpoint;
|
||||
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);
|
||||
|
||||
if (readselect(IO_CDC, &endpoint, &available) < 0) {
|
||||
// readselect failed! I/O broken? Just redblink.
|
||||
assert(1 == 2);
|
||||
}
|
||||
|
||||
if (read(IO_CDC, &in, 1, 1) < 0) {
|
||||
// read failed! I/O broken? Just redblink.
|
||||
assert(1 == 2);
|
||||
}
|
||||
|
||||
puts(IO_CDC, "Hello from testloadapp! 0 = install app in slot 1, 1 = "
|
||||
"verify app, 2 == load app from client\r\n");
|
||||
|
||||
for (;;) {
|
||||
if (readselect(IO_CDC, &endpoint, &available) < 0) {
|
||||
// readselect failed! I/O broken? Just redblink.
|
||||
assert(1 == 2);
|
||||
}
|
||||
|
||||
if (read(IO_CDC, &in, 1, 1) < 0) {
|
||||
// read failed! I/O broken? Just redblink.
|
||||
assert(1 == 2);
|
||||
}
|
||||
|
||||
switch (in) {
|
||||
case '0':
|
||||
if (install_app(secret_key) < 0) {
|
||||
puts(IO_CDC, "Failed to install app\r\n");
|
||||
} else {
|
||||
puts(IO_CDC, "Installed app!\r\n");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case '1':
|
||||
if (verify(pubkey) < 0) {
|
||||
puts(IO_CDC, "Failed to verify app\r\n");
|
||||
} else {
|
||||
puts(IO_CDC, "Verified app!\r\n");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case '2':
|
||||
reset_from_client();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,6 +21,6 @@ The contents of the fw_ram is cleared when the FPGA is powered up and
|
|||
configured by the bitstream. The contents is not cleared by a system
|
||||
reset.
|
||||
|
||||
If the system_mode input is set, i.e. in app mode, no memory
|
||||
accesses are allowed. Any reads when the system_mode is set will
|
||||
If the app_mode input is set, i.e. in app mode, no memory
|
||||
accesses are allowed. Any reads when the app_mode is set will
|
||||
return an all zero word.
|
||||
|
|
|
@ -17,11 +17,11 @@ module fw_ram (
|
|||
input wire clk,
|
||||
input wire reset_n,
|
||||
|
||||
input wire system_mode,
|
||||
input wire app_mode,
|
||||
|
||||
input wire cs,
|
||||
input wire [ 3 : 0] we,
|
||||
input wire [ 8 : 0] address,
|
||||
input wire [ 9 : 0] address,
|
||||
input wire [31 : 0] write_data,
|
||||
output wire [31 : 0] read_data,
|
||||
output wire ready
|
||||
|
@ -34,10 +34,14 @@ module fw_ram (
|
|||
reg [31 : 0] tmp_read_data;
|
||||
reg [31 : 0] mem_read_data0;
|
||||
reg [31 : 0] mem_read_data1;
|
||||
reg [31 : 0] mem_read_data2;
|
||||
reg [31 : 0] mem_read_data3;
|
||||
reg ready_reg;
|
||||
wire system_mode_cs;
|
||||
wire app_mode_cs;
|
||||
reg bank0;
|
||||
reg bank1;
|
||||
reg bank2;
|
||||
reg bank3;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
@ -45,66 +49,257 @@ module fw_ram (
|
|||
//----------------------------------------------------------------
|
||||
assign read_data = tmp_read_data;
|
||||
assign ready = ready_reg;
|
||||
assign system_mode_cs = cs && ~system_mode;
|
||||
assign app_mode_cs = cs && ~app_mode;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Block RAM instances.
|
||||
//----------------------------------------------------------------
|
||||
SB_RAM40_4K fw_ram0_0 (
|
||||
SB_RAM40_4K #(
|
||||
.INIT_0(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_1(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_2(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_3(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_4(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_5(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_6(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_7(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_8(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_9(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_A(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_B(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_C(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_D(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_E(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_F(256'h0000000000000000000000000000000000000000000000000000000000000000)
|
||||
) fw_ram0_0 (
|
||||
.RDATA(mem_read_data0[15 : 0]),
|
||||
.RADDR({3'h0, address[7 : 0]}),
|
||||
.RCLK(clk),
|
||||
.RCLKE(1'h1),
|
||||
.RE(system_mode_cs & bank0),
|
||||
.RE(app_mode_cs & bank0),
|
||||
.WADDR({3'h0, address[7 : 0]}),
|
||||
.WCLK(clk),
|
||||
.WCLKE(1'h1),
|
||||
.WDATA(write_data[15 : 0]),
|
||||
.WE((|we & system_mode_cs & bank0)),
|
||||
.WE((|we & app_mode_cs & bank0)),
|
||||
.MASK({{8{~we[1]}}, {8{~we[0]}}})
|
||||
);
|
||||
|
||||
SB_RAM40_4K fw_ram0_1 (
|
||||
SB_RAM40_4K #(
|
||||
.INIT_0(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_1(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_2(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_3(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_4(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_5(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_6(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_7(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_8(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_9(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_A(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_B(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_C(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_D(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_E(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_F(256'h0000000000000000000000000000000000000000000000000000000000000000)
|
||||
) fw_ram0_1 (
|
||||
.RDATA(mem_read_data0[31 : 16]),
|
||||
.RADDR({3'h0, address[7 : 0]}),
|
||||
.RCLK(clk),
|
||||
.RCLKE(1'h1),
|
||||
.RE(system_mode_cs & bank0),
|
||||
.RE(app_mode_cs & bank0),
|
||||
.WADDR({3'h0, address[7 : 0]}),
|
||||
.WCLK(clk),
|
||||
.WCLKE(1'h1),
|
||||
.WDATA(write_data[31 : 16]),
|
||||
.WE((|we & system_mode_cs & bank0)),
|
||||
.WE((|we & app_mode_cs & bank0)),
|
||||
.MASK({{8{~we[3]}}, {8{~we[2]}}})
|
||||
);
|
||||
|
||||
|
||||
SB_RAM40_4K fw_ram1_0 (
|
||||
SB_RAM40_4K #(
|
||||
.INIT_0(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_1(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_2(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_3(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_4(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_5(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_6(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_7(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_8(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_9(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_A(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_B(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_C(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_D(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_E(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_F(256'h0000000000000000000000000000000000000000000000000000000000000000)
|
||||
) fw_ram1_0 (
|
||||
.RDATA(mem_read_data1[15 : 0]),
|
||||
.RADDR({3'h0, address[7 : 0]}),
|
||||
.RCLK(clk),
|
||||
.RCLKE(1'h1),
|
||||
.RE(system_mode_cs & bank1),
|
||||
.RE(app_mode_cs & bank1),
|
||||
.WADDR({3'h0, address[7 : 0]}),
|
||||
.WCLK(clk),
|
||||
.WCLKE(1'h1),
|
||||
.WDATA(write_data[15 : 0]),
|
||||
.WE((|we & system_mode_cs & bank1)),
|
||||
.WE((|we & app_mode_cs & bank1)),
|
||||
.MASK({{8{~we[1]}}, {8{~we[0]}}})
|
||||
);
|
||||
|
||||
SB_RAM40_4K fw_ram1_1 (
|
||||
SB_RAM40_4K #(
|
||||
.INIT_0(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_1(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_2(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_3(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_4(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_5(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_6(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_7(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_8(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_9(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_A(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_B(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_C(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_D(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_E(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_F(256'h0000000000000000000000000000000000000000000000000000000000000000)
|
||||
) fw_ram1_1 (
|
||||
.RDATA(mem_read_data1[31 : 16]),
|
||||
.RADDR({3'h0, address[7 : 0]}),
|
||||
.RCLK(clk),
|
||||
.RCLKE(1'h1),
|
||||
.RE(system_mode_cs & bank1),
|
||||
.RE(app_mode_cs & bank1),
|
||||
.WADDR({3'h0, address[7 : 0]}),
|
||||
.WCLK(clk),
|
||||
.WCLKE(1'h1),
|
||||
.WDATA(write_data[31 : 16]),
|
||||
.WE((|we & system_mode_cs & bank1)),
|
||||
.WE((|we & app_mode_cs & bank1)),
|
||||
.MASK({{8{~we[3]}}, {8{~we[2]}}})
|
||||
);
|
||||
|
||||
SB_RAM40_4K #(
|
||||
.INIT_0(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_1(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_2(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_3(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_4(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_5(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_6(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_7(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_8(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_9(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_A(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_B(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_C(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_D(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_E(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_F(256'h0000000000000000000000000000000000000000000000000000000000000000)
|
||||
) fw_ram2_0 (
|
||||
.RDATA(mem_read_data2[15 : 0]),
|
||||
.RADDR({3'h0, address[7 : 0]}),
|
||||
.RCLK(clk),
|
||||
.RCLKE(1'h1),
|
||||
.RE(app_mode_cs & bank2),
|
||||
.WADDR({3'h0, address[7 : 0]}),
|
||||
.WCLK(clk),
|
||||
.WCLKE(1'h1),
|
||||
.WDATA(write_data[15 : 0]),
|
||||
.WE((|we & app_mode_cs & bank2)),
|
||||
.MASK({{8{~we[1]}}, {8{~we[0]}}})
|
||||
);
|
||||
|
||||
SB_RAM40_4K #(
|
||||
.INIT_0(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_1(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_2(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_3(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_4(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_5(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_6(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_7(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_8(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_9(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_A(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_B(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_C(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_D(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_E(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_F(256'h0000000000000000000000000000000000000000000000000000000000000000)
|
||||
) fw_ram2_1 (
|
||||
.RDATA(mem_read_data2[31 : 16]),
|
||||
.RADDR({3'h0, address[7 : 0]}),
|
||||
.RCLK(clk),
|
||||
.RCLKE(1'h1),
|
||||
.RE(app_mode_cs & bank2),
|
||||
.WADDR({3'h0, address[7 : 0]}),
|
||||
.WCLK(clk),
|
||||
.WCLKE(1'h1),
|
||||
.WDATA(write_data[31 : 16]),
|
||||
.WE((|we & app_mode_cs & bank2)),
|
||||
.MASK({{8{~we[3]}}, {8{~we[2]}}})
|
||||
);
|
||||
|
||||
SB_RAM40_4K #(
|
||||
.INIT_0(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_1(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_2(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_3(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_4(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_5(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_6(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_7(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_8(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_9(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_A(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_B(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_C(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_D(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_E(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_F(256'h0000000000000000000000000000000000000000000000000000000000000000)
|
||||
) fw_ram3_0 (
|
||||
.RDATA(mem_read_data3[15 : 0]),
|
||||
.RADDR({3'h0, address[7 : 0]}),
|
||||
.RCLK(clk),
|
||||
.RCLKE(1'h1),
|
||||
.RE(app_mode_cs & bank3),
|
||||
.WADDR({3'h0, address[7 : 0]}),
|
||||
.WCLK(clk),
|
||||
.WCLKE(1'h1),
|
||||
.WDATA(write_data[15 : 0]),
|
||||
.WE((|we & app_mode_cs & bank3)),
|
||||
.MASK({{8{~we[1]}}, {8{~we[0]}}})
|
||||
);
|
||||
|
||||
SB_RAM40_4K #(
|
||||
.INIT_0(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_1(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_2(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_3(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_4(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_5(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_6(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_7(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_8(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_9(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_A(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_B(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_C(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_D(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_E(256'h0000000000000000000000000000000000000000000000000000000000000000),
|
||||
.INIT_F(256'h0000000000000000000000000000000000000000000000000000000000000000)
|
||||
) fw_ram3_1 (
|
||||
.RDATA(mem_read_data3[31 : 16]),
|
||||
.RADDR({3'h0, address[7 : 0]}),
|
||||
.RCLK(clk),
|
||||
.RCLKE(1'h1),
|
||||
.RE(app_mode_cs & bank3),
|
||||
.WADDR({3'h0, address[7 : 0]}),
|
||||
.WCLK(clk),
|
||||
.WCLKE(1'h1),
|
||||
.WDATA(write_data[31 : 16]),
|
||||
.WE((|we & app_mode_cs & bank3)),
|
||||
.MASK({{8{~we[3]}}, {8{~we[2]}}})
|
||||
);
|
||||
|
||||
|
@ -127,17 +322,29 @@ module fw_ram (
|
|||
always @* begin : rw_mux
|
||||
bank0 = 1'h0;
|
||||
bank1 = 1'h0;
|
||||
bank2 = 1'h0;
|
||||
bank3 = 1'h0;
|
||||
tmp_read_data = 32'h0;
|
||||
|
||||
if (system_mode_cs) begin
|
||||
if (address[8]) begin
|
||||
if (app_mode_cs) begin
|
||||
case (address[9:8])
|
||||
2'b11: begin
|
||||
bank3 = 1'h1;
|
||||
tmp_read_data = mem_read_data3;
|
||||
end
|
||||
2'b10: begin
|
||||
bank2 = 1'h1;
|
||||
tmp_read_data = mem_read_data2;
|
||||
end
|
||||
2'b01: begin
|
||||
bank1 = 1'h1;
|
||||
tmp_read_data = mem_read_data1;
|
||||
end
|
||||
else begin
|
||||
2'b00: begin
|
||||
bank0 = 1'h1;
|
||||
tmp_read_data = mem_read_data0;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -23,17 +23,6 @@ and version of the device. They can be read by FW as well as
|
|||
applications.
|
||||
|
||||
|
||||
### Control of execution mode
|
||||
|
||||
```
|
||||
ADDR_SYSTEM_MODE_CTRL: 0x08
|
||||
```
|
||||
|
||||
This register controls if the device is executing in FW mode or in App
|
||||
mode. The register can be written once between power cycles, and only
|
||||
by FW. If set the device is in app mode.
|
||||
|
||||
|
||||
### Control of RGB LED
|
||||
|
||||
```
|
||||
|
@ -75,19 +64,7 @@ ADDR_APP_SIZE: 0x0d
|
|||
These registers provide read only information to the loaded app to
|
||||
itself - where it was loaded and its size. The values are written by
|
||||
FW as part of the loading of the app. The registers can't be written
|
||||
when the `ADDR_SYSTEM_MODE_CTRL` has been set.
|
||||
|
||||
|
||||
### Access to Blake2s
|
||||
|
||||
```
|
||||
ADDR_BLAKE2S: 0x10
|
||||
```
|
||||
|
||||
This register provides the 32-bit function pointer address to the
|
||||
Blake2s hash function in the FW. It is written by FW during boot. The
|
||||
register can't be written to when the `ADDR_SYSTEM_MODE_CTRL` has been
|
||||
set.
|
||||
in application mode.
|
||||
|
||||
|
||||
### Access to CDI
|
||||
|
@ -99,10 +76,10 @@ ADDR_CDI_LAST: 0x27
|
|||
|
||||
These registers provide access to the 256-bit compound device secret
|
||||
calculated by the FW as part of loading an application. The registers
|
||||
are written by the FW. The register can't be written to when the
|
||||
`ADDR_SYSTEM_MODE_CTRL` has been set. The CDI is readable by apps,
|
||||
which can then use it as a base secret for any other secrets required
|
||||
to carry out their intended use case.
|
||||
are written by the FW. The register can't be written in application
|
||||
mode. The CDI is readable by apps, which can then use it as a base
|
||||
secret for any other secrets required to carry out their intended use
|
||||
case.
|
||||
|
||||
|
||||
### Access to UDI
|
||||
|
|
|
@ -20,7 +20,8 @@ module tk1 #(
|
|||
input wire reset_n,
|
||||
|
||||
input wire cpu_trap,
|
||||
output wire system_mode,
|
||||
output wire app_mode,
|
||||
output wire fw_startup_done,
|
||||
|
||||
input wire [31 : 0] cpu_addr,
|
||||
input wire cpu_instr,
|
||||
|
@ -45,6 +46,8 @@ module tk1 #(
|
|||
output wire gpio3,
|
||||
output wire gpio4,
|
||||
|
||||
input wire syscall,
|
||||
|
||||
input wire cs,
|
||||
input wire we,
|
||||
input wire [ 7 : 0] address,
|
||||
|
@ -61,8 +64,6 @@ module tk1 #(
|
|||
localparam ADDR_NAME1 = 8'h01;
|
||||
localparam ADDR_VERSION = 8'h02;
|
||||
|
||||
localparam ADDR_SYSTEM_MODE_CTRL = 8'h08;
|
||||
|
||||
localparam ADDR_LED = 8'h09;
|
||||
localparam LED_R_BIT = 2;
|
||||
localparam LED_G_BIT = 1;
|
||||
|
@ -79,8 +80,6 @@ module tk1 #(
|
|||
localparam ADDR_APP_START = 8'h0c;
|
||||
localparam ADDR_APP_SIZE = 8'h0d;
|
||||
|
||||
localparam ADDR_BLAKE2S = 8'h10;
|
||||
|
||||
localparam ADDR_CDI_FIRST = 8'h20;
|
||||
localparam ADDR_CDI_LAST = 8'h27;
|
||||
|
||||
|
@ -102,11 +101,12 @@ module tk1 #(
|
|||
|
||||
localparam TK1_NAME0 = 32'h746B3120; // "tk1 "
|
||||
localparam TK1_NAME1 = 32'h6d6b6466; // "mkdf"
|
||||
localparam TK1_VERSION = 32'h00000005;
|
||||
localparam TK1_VERSION = 32'h00000006;
|
||||
|
||||
localparam FW_RAM_FIRST = 32'hd0000000;
|
||||
localparam FW_RAM_LAST = 32'hd00007ff;
|
||||
localparam FW_RAM_LAST = 32'hd0000fff; // 4 KB
|
||||
|
||||
localparam FW_ROM_LAST = 32'h00001fff;
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Registers including update variables and write enable.
|
||||
|
@ -114,8 +114,8 @@ module tk1 #(
|
|||
reg [31 : 0] cdi_mem [0 : 7];
|
||||
reg cdi_mem_we;
|
||||
|
||||
reg system_mode_reg;
|
||||
reg system_mode_we;
|
||||
reg fw_startup_done_reg;
|
||||
reg fw_startup_done_set;
|
||||
|
||||
reg [ 2 : 0] led_reg;
|
||||
reg led_we;
|
||||
|
@ -133,9 +133,6 @@ module tk1 #(
|
|||
reg [31 : 0] app_size_reg;
|
||||
reg app_size_we;
|
||||
|
||||
reg [31 : 0] blake2s_addr_reg;
|
||||
reg blake2s_addr_we;
|
||||
|
||||
reg [23 : 0] cpu_trap_ctr_reg;
|
||||
reg [23 : 0] cpu_trap_ctr_new;
|
||||
reg [ 2 : 0] cpu_trap_led_reg;
|
||||
|
@ -187,7 +184,8 @@ module tk1 #(
|
|||
assign read_data = tmp_read_data;
|
||||
assign ready = tmp_ready;
|
||||
|
||||
assign system_mode = system_mode_reg;
|
||||
assign app_mode = fw_startup_done_reg & ~syscall;
|
||||
assign fw_startup_done = fw_startup_done_reg;
|
||||
|
||||
assign force_trap = force_trap_reg;
|
||||
|
||||
|
@ -199,7 +197,6 @@ module tk1 #(
|
|||
|
||||
assign system_reset = system_reset_reg;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Module instance.
|
||||
//----------------------------------------------------------------
|
||||
|
@ -250,7 +247,7 @@ module tk1 #(
|
|||
//----------------------------------------------------------------
|
||||
always @(posedge clk) begin : reg_update
|
||||
if (!reset_n) begin
|
||||
system_mode_reg <= 1'h0;
|
||||
fw_startup_done_reg <= 1'h0;
|
||||
led_reg <= 3'h6;
|
||||
gpio1_reg <= 2'h0;
|
||||
gpio2_reg <= 2'h0;
|
||||
|
@ -258,7 +255,6 @@ module tk1 #(
|
|||
gpio4_reg <= 1'h0;
|
||||
app_start_reg <= 32'h0;
|
||||
app_size_reg <= APP_SIZE;
|
||||
blake2s_addr_reg <= 32'h0;
|
||||
cdi_mem[0] <= 32'h0;
|
||||
cdi_mem[1] <= 32'h0;
|
||||
cdi_mem[2] <= 32'h0;
|
||||
|
@ -289,8 +285,8 @@ module tk1 #(
|
|||
gpio2_reg[0] <= gpio2;
|
||||
gpio2_reg[1] <= gpio2_reg[0];
|
||||
|
||||
if (system_mode_we) begin
|
||||
system_mode_reg <= 1'h1;
|
||||
if (fw_startup_done_set) begin
|
||||
fw_startup_done_reg <= 1'h1;
|
||||
end
|
||||
|
||||
if (led_we) begin
|
||||
|
@ -313,10 +309,6 @@ module tk1 #(
|
|||
app_size_reg <= write_data;
|
||||
end
|
||||
|
||||
if (blake2s_addr_we) begin
|
||||
blake2s_addr_reg <= write_data;
|
||||
end
|
||||
|
||||
if (cdi_mem_we) begin
|
||||
cdi_mem[address[2 : 0]] <= write_data;
|
||||
end
|
||||
|
@ -386,6 +378,9 @@ module tk1 #(
|
|||
//
|
||||
// Trying to execute instructions in FW-RAM.
|
||||
//
|
||||
// Executing instructions in ROM, while ROM is marked as not
|
||||
// executable.
|
||||
//
|
||||
// Trying to execute code in mem area set to be data access only.
|
||||
// This requires execution monitor to have been setup and
|
||||
// enabled.
|
||||
|
@ -395,7 +390,7 @@ module tk1 #(
|
|||
|
||||
if (cpu_valid) begin
|
||||
// Outside ROM area
|
||||
if (cpu_addr[31 : 30] == 2'h0 & |cpu_addr[29 : 14]) begin
|
||||
if (cpu_addr[31 : 30] == 2'h0 & |cpu_addr[29 : 13]) begin
|
||||
force_trap_set = 1'h1;
|
||||
end
|
||||
|
||||
|
@ -443,12 +438,22 @@ module tk1 #(
|
|||
end
|
||||
|
||||
// Outside FW_RAM
|
||||
if (cpu_addr[29 : 24] == 6'h10 & |cpu_addr[23 : 11]) begin
|
||||
if (cpu_addr[29 : 24] == 6'h10 & |cpu_addr[23 : 12]) begin
|
||||
force_trap_set = 1'h1;
|
||||
end
|
||||
|
||||
// In unused space
|
||||
if ((cpu_addr[29 : 24] > 6'h10) && (cpu_addr[29 : 24] < 6'h3f)) begin
|
||||
if ((cpu_addr[29 : 24] > 6'h10) && (cpu_addr[29 : 24] < 6'h21)) begin
|
||||
force_trap_set = 1'h1;
|
||||
end
|
||||
|
||||
// Outside SYSCALL
|
||||
if (cpu_addr[29 : 24] == 6'h21 & |cpu_addr[23 : 2]) begin
|
||||
force_trap_set = 1'h1;
|
||||
end
|
||||
|
||||
// In unused space
|
||||
if ((cpu_addr[29 : 24] > 6'h21) && (cpu_addr[29 : 24] < 6'h3f)) begin
|
||||
force_trap_set = 1'h1;
|
||||
end
|
||||
|
||||
|
@ -463,6 +468,12 @@ module tk1 #(
|
|||
force_trap_set = 1'h1;
|
||||
end
|
||||
|
||||
if (app_mode) begin
|
||||
if (cpu_addr <= FW_ROM_LAST) begin // Only valid as long as ROM starts at address 0x00.
|
||||
force_trap_set = 1'h1;
|
||||
end
|
||||
end
|
||||
|
||||
if (cpu_mon_en_reg) begin
|
||||
if ((cpu_addr >= cpu_mon_first_reg) && (cpu_addr <= cpu_mon_last_reg)) begin
|
||||
force_trap_set = 1'h1;
|
||||
|
@ -472,18 +483,30 @@ module tk1 #(
|
|||
end
|
||||
end
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// fw_startup_done_ctrl
|
||||
//
|
||||
// Automatically lower privilege when executing above ROM.
|
||||
// ----------------------------------------------------------------
|
||||
always @* begin : fw_startup_done_ctrl
|
||||
fw_startup_done_set = 1'h0;
|
||||
|
||||
if (cpu_valid & cpu_instr) begin
|
||||
if (cpu_addr > FW_ROM_LAST) begin
|
||||
fw_startup_done_set = 1'h1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// api
|
||||
//----------------------------------------------------------------
|
||||
always @* begin : api
|
||||
system_mode_we = 1'h0;
|
||||
led_we = 1'h0;
|
||||
gpio3_we = 1'h0;
|
||||
gpio4_we = 1'h0;
|
||||
app_start_we = 1'h0;
|
||||
app_size_we = 1'h0;
|
||||
blake2s_addr_we = 1'h0;
|
||||
cdi_mem_we = 1'h0;
|
||||
ram_addr_rand_we = 1'h0;
|
||||
ram_data_rand_we = 1'h0;
|
||||
|
@ -498,16 +521,12 @@ module tk1 #(
|
|||
spi_start = 1'h0;
|
||||
spi_tx_data_vld = 1'h0;
|
||||
|
||||
spi_enable = write_data[0];
|
||||
spi_tx_data = write_data[7 : 0];
|
||||
spi_enable = write_data[0] & !app_mode;
|
||||
spi_tx_data = write_data[7 : 0] & {8{!app_mode}};
|
||||
|
||||
if (cs) begin
|
||||
tmp_ready = 1'h1;
|
||||
if (we) begin
|
||||
if (address == ADDR_SYSTEM_MODE_CTRL) begin
|
||||
system_mode_we = 1'h1;
|
||||
end
|
||||
|
||||
if (address == ADDR_LED) begin
|
||||
led_we = 1'h1;
|
||||
end
|
||||
|
@ -518,41 +537,37 @@ module tk1 #(
|
|||
end
|
||||
|
||||
if (address == ADDR_APP_START) begin
|
||||
if (!system_mode_reg) begin
|
||||
if (!app_mode) begin
|
||||
app_start_we = 1'h1;
|
||||
end
|
||||
end
|
||||
|
||||
if (address == ADDR_APP_SIZE) begin
|
||||
if (!system_mode_reg) begin
|
||||
if (!app_mode) begin
|
||||
app_size_we = 1'h1;
|
||||
end
|
||||
end
|
||||
|
||||
if (address == ADDR_SYSTEM_RESET) begin
|
||||
if (!app_mode) begin
|
||||
system_reset_new = 1'h1;
|
||||
end
|
||||
|
||||
if (address == ADDR_BLAKE2S) begin
|
||||
if (!system_mode_reg) begin
|
||||
blake2s_addr_we = 1'h1;
|
||||
end
|
||||
end
|
||||
|
||||
if ((address >= ADDR_CDI_FIRST) && (address <= ADDR_CDI_LAST)) begin
|
||||
if (!system_mode_reg) begin
|
||||
if (!app_mode) begin
|
||||
cdi_mem_we = 1'h1;
|
||||
end
|
||||
end
|
||||
|
||||
if (address == ADDR_RAM_ADDR_RAND) begin
|
||||
if (!system_mode_reg) begin
|
||||
if (!app_mode) begin
|
||||
ram_addr_rand_we = 1'h1;
|
||||
end
|
||||
end
|
||||
|
||||
if (address == ADDR_RAM_DATA_RAND) begin
|
||||
if (!system_mode_reg) begin
|
||||
if (!app_mode) begin
|
||||
ram_data_rand_we = 1'h1;
|
||||
end
|
||||
end
|
||||
|
@ -574,16 +589,22 @@ module tk1 #(
|
|||
end
|
||||
|
||||
if (address == ADDR_SPI_EN) begin
|
||||
if (!app_mode) begin
|
||||
spi_enable_vld = 1'h1;
|
||||
end
|
||||
end
|
||||
|
||||
if (address == ADDR_SPI_XFER) begin
|
||||
if (!app_mode) begin
|
||||
spi_start = 1'h1;
|
||||
end
|
||||
end
|
||||
|
||||
if (address == ADDR_SPI_DATA) begin
|
||||
if (!app_mode) begin
|
||||
spi_tx_data_vld = 1'h1;
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
else begin
|
||||
|
@ -599,10 +620,6 @@ module tk1 #(
|
|||
tmp_read_data = TK1_VERSION;
|
||||
end
|
||||
|
||||
if (address == ADDR_SYSTEM_MODE_CTRL) begin
|
||||
tmp_read_data = {32{system_mode_reg}};
|
||||
end
|
||||
|
||||
if (address == ADDR_LED) begin
|
||||
tmp_read_data = {29'h0, led_reg};
|
||||
end
|
||||
|
@ -619,27 +636,27 @@ module tk1 #(
|
|||
tmp_read_data = app_size_reg;
|
||||
end
|
||||
|
||||
if (address == ADDR_BLAKE2S) begin
|
||||
tmp_read_data = blake2s_addr_reg;
|
||||
end
|
||||
|
||||
if ((address >= ADDR_CDI_FIRST) && (address <= ADDR_CDI_LAST)) begin
|
||||
tmp_read_data = cdi_mem[address[2 : 0]];
|
||||
end
|
||||
|
||||
if ((address >= ADDR_UDI_FIRST) && (address <= ADDR_UDI_LAST)) begin
|
||||
if (!system_mode_reg) begin
|
||||
if (!app_mode) begin
|
||||
tmp_read_data = udi_rdata;
|
||||
end
|
||||
end
|
||||
|
||||
if (address == ADDR_SPI_XFER) begin
|
||||
if (!app_mode) begin
|
||||
tmp_read_data[0] = spi_ready;
|
||||
end
|
||||
end
|
||||
|
||||
if (address == ADDR_SPI_DATA) begin
|
||||
if (!app_mode) begin
|
||||
tmp_read_data[7 : 0] = spi_rx_data;
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,7 +18,7 @@ module tb_tk1 ();
|
|||
//----------------------------------------------------------------
|
||||
// Internal constant and parameter definitions.
|
||||
//----------------------------------------------------------------
|
||||
parameter DEBUG = 1;
|
||||
parameter DEBUG = 0;
|
||||
|
||||
parameter CLK_HALF_PERIOD = 1;
|
||||
parameter CLK_PERIOD = 2 * CLK_HALF_PERIOD;
|
||||
|
@ -27,8 +27,6 @@ module tb_tk1 ();
|
|||
localparam ADDR_NAME1 = 8'h01;
|
||||
localparam ADDR_VERSION = 8'h02;
|
||||
|
||||
localparam ADDR_SYSTEM_MODE_CTRL = 8'h08;
|
||||
|
||||
localparam ADDR_LED = 8'h09;
|
||||
localparam LED_R_BIT = 2;
|
||||
localparam LED_G_BIT = 1;
|
||||
|
@ -58,10 +56,16 @@ module tb_tk1 ();
|
|||
localparam ADDR_CPU_MON_FIRST = 8'h61;
|
||||
localparam ADDR_CPU_MON_LAST = 8'h62;
|
||||
|
||||
localparam ADDR_SYSTEM_RESET = 8'h70;
|
||||
|
||||
localparam ADDR_SPI_EN = 8'h80;
|
||||
localparam ADDR_SPI_XFER = 8'h81;
|
||||
localparam ADDR_SPI_DATA = 8'h82;
|
||||
|
||||
localparam APP_RAM_START = 32'h40000000;
|
||||
|
||||
localparam ROM_START = 32'h00000000;
|
||||
localparam ROM_END = 32'h00001fff;
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Register and Wire declarations.
|
||||
|
@ -76,12 +80,13 @@ module tb_tk1 ();
|
|||
reg tb_clk;
|
||||
reg tb_reset_n;
|
||||
reg tb_cpu_trap;
|
||||
wire tb_system_mode;
|
||||
wire tb_app_mode;
|
||||
|
||||
reg [31 : 0] tb_cpu_addr;
|
||||
reg tb_cpu_instr;
|
||||
reg tb_cpu_valid;
|
||||
wire tb_force_trap;
|
||||
wire tb_system_reset;
|
||||
|
||||
wire [14 : 0] tb_ram_addr_rand;
|
||||
wire [31 : 0] tb_ram_data_rand;
|
||||
|
@ -95,6 +100,8 @@ module tb_tk1 ();
|
|||
wire tb_gpio3;
|
||||
wire tb_gpio4;
|
||||
|
||||
reg tb_syscall;
|
||||
|
||||
wire tb_spi_ss;
|
||||
wire tb_spi_sck;
|
||||
wire tb_spi_mosi;
|
||||
|
@ -122,12 +129,13 @@ module tb_tk1 ();
|
|||
.reset_n(tb_reset_n),
|
||||
|
||||
.cpu_trap(tb_cpu_trap),
|
||||
.system_mode(tb_system_mode),
|
||||
.app_mode(tb_app_mode),
|
||||
|
||||
.cpu_addr (tb_cpu_addr),
|
||||
.cpu_instr (tb_cpu_instr),
|
||||
.cpu_valid (tb_cpu_valid),
|
||||
.force_trap(tb_force_trap),
|
||||
.system_reset(tb_system_reset),
|
||||
|
||||
.ram_addr_rand(tb_ram_addr_rand),
|
||||
.ram_data_rand(tb_ram_data_rand),
|
||||
|
@ -141,6 +149,8 @@ module tb_tk1 ();
|
|||
.gpio3(tb_gpio3),
|
||||
.gpio4(tb_gpio4),
|
||||
|
||||
.syscall(tb_syscall),
|
||||
|
||||
.spi_ss (tb_spi_ss),
|
||||
.spi_sck (tb_spi_sck),
|
||||
.spi_mosi(tb_spi_mosi),
|
||||
|
@ -192,7 +202,7 @@ module tb_tk1 ();
|
|||
$display("------------");
|
||||
if (tb_main_monitor) begin
|
||||
$display("Inputs and outputs:");
|
||||
$display("tb_cpu_trap: 0x%1x, system_mode: 0x%1x", tb_cpu_trap, tb_system_mode);
|
||||
$display("tb_cpu_trap: 0x%1x, app_mode: 0x%1x", tb_cpu_trap, dut.app_mode);
|
||||
$display("cpu_addr: 0x%08x, cpu_instr: 0x%1x, cpu_valid: 0x%1x, force_tap: 0x%1x",
|
||||
tb_cpu_addr, tb_cpu_instr, tb_cpu_valid, tb_force_trap);
|
||||
$display("ram_addr_rand: 0x%08x, ram_data_rand: 0x%08x", tb_ram_addr_rand,
|
||||
|
@ -227,7 +237,6 @@ module tb_tk1 ();
|
|||
//----------------------------------------------------------------
|
||||
task reset_dut;
|
||||
begin
|
||||
$display("--- Toggle reset.");
|
||||
tb_reset_n = 0;
|
||||
#(2 * CLK_PERIOD);
|
||||
tb_reset_n = 1;
|
||||
|
@ -277,6 +286,8 @@ module tb_tk1 ();
|
|||
tb_gpio1 = 1'h0;
|
||||
tb_gpio2 = 1'h0;
|
||||
|
||||
tb_syscall = 1'h0;
|
||||
|
||||
tb_cs = 1'h0;
|
||||
tb_we = 1'h0;
|
||||
tb_address = 8'h0;
|
||||
|
@ -285,6 +296,25 @@ module tb_tk1 ();
|
|||
endtask // init_sim
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// restore_mem_bus()
|
||||
//
|
||||
// Restore memory bus to its initial state
|
||||
//----------------------------------------------------------------
|
||||
task restore_mem_bus();
|
||||
begin : restore_mem_bus
|
||||
tb_cpu_addr = 32'h0;
|
||||
tb_cpu_instr = 1'h0;
|
||||
tb_cpu_valid = 1'h0;
|
||||
|
||||
tb_cs = 1'h0;
|
||||
tb_we = 1'h0;
|
||||
tb_address = 8'h0;
|
||||
tb_write_data = 32'h0;
|
||||
end
|
||||
endtask
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// write_word()
|
||||
//
|
||||
|
@ -301,7 +331,7 @@ module tb_tk1 ();
|
|||
tb_write_data = word;
|
||||
tb_cs = 1;
|
||||
tb_we = 1;
|
||||
#(2 * CLK_PERIOD);
|
||||
#(CLK_PERIOD);
|
||||
tb_cs = 0;
|
||||
tb_we = 0;
|
||||
end
|
||||
|
@ -320,12 +350,16 @@ module tb_tk1 ();
|
|||
reg [31 : 0] read_data;
|
||||
|
||||
tb_address = address;
|
||||
tb_cpu_instr = 1'h0;
|
||||
tb_cpu_valid = 1'h1;
|
||||
tb_we = 1'h0;
|
||||
tb_cs = 1'h1;
|
||||
|
||||
#(CLK_PERIOD);
|
||||
read_data = tb_read_data;
|
||||
|
||||
#(CLK_PERIOD);
|
||||
tb_cpu_valid = 1'h0;
|
||||
tb_cs = 1'h0;
|
||||
end
|
||||
endtask // read_word
|
||||
|
@ -354,21 +388,138 @@ module tb_tk1 ();
|
|||
#(CLK_PERIOD);
|
||||
tb_cs = 1'h0;
|
||||
|
||||
if (DEBUG) begin
|
||||
if (read_data == expected) begin
|
||||
if (DEBUG) begin
|
||||
$display("--- Reading 0x%08x from 0x%02x.", read_data, address);
|
||||
end
|
||||
end
|
||||
else begin
|
||||
$display("--- Error: Got 0x%08x when reading from 0x%02x, expected 0x%08x", read_data,
|
||||
address, expected);
|
||||
error_ctr = error_ctr + 1;
|
||||
end
|
||||
$display("");
|
||||
end
|
||||
end
|
||||
endtask // read_check_word
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// check_equal()
|
||||
//
|
||||
// Check that two values are equal
|
||||
//----------------------------------------------------------------
|
||||
task check_equal(input [31 : 0] value, input [31 : 0] expected);
|
||||
begin : check_equal
|
||||
if (value != expected) begin
|
||||
$display("--- Error: Got 0x%08x, expected 0x%08x", value, expected);
|
||||
error_ctr = error_ctr + 1;
|
||||
end
|
||||
end
|
||||
endtask // check_equal
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// fetch_instruction()
|
||||
//
|
||||
// Simulate fetch of an instruction at specified address.
|
||||
//----------------------------------------------------------------
|
||||
task fetch_instruction(input [31 : 0] address);
|
||||
begin : fetch_instruction
|
||||
tb_cpu_addr = address;
|
||||
tb_cpu_instr = 1'h1;
|
||||
tb_cpu_valid = 1'h1;
|
||||
#(CLK_PERIOD);
|
||||
tb_cpu_addr = 32'h0;
|
||||
tb_cpu_instr = 1'h0;
|
||||
tb_cpu_valid = 1'h0;
|
||||
end
|
||||
endtask // fetch_instruction
|
||||
|
||||
// cpu_read_word()
|
||||
//
|
||||
// Read a data word from the given CPU address in the DUT.
|
||||
// the word read will be available in the global variable
|
||||
// tb_read_data.
|
||||
//----------------------------------------------------------------
|
||||
task cpu_read_word(input [31 : 0] address);
|
||||
begin : cpu_read_word
|
||||
reg [31 : 0] read_data;
|
||||
|
||||
tb_cpu_addr = address;
|
||||
tb_address = tb_cpu_addr[13:2];
|
||||
tb_cpu_instr = 1'h0;
|
||||
tb_cpu_valid = 1'h1;
|
||||
tb_we = 1'h0;
|
||||
tb_cs = 1'h1;
|
||||
|
||||
#(CLK_PERIOD);
|
||||
read_data = tb_read_data;
|
||||
|
||||
#(CLK_PERIOD);
|
||||
tb_cpu_addr = 32'h0;
|
||||
tb_cpu_valid = 1'h0;
|
||||
tb_address = 12'h0;
|
||||
tb_cs = 1'h0;
|
||||
end
|
||||
endtask // read_word
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// cpu_read_check_range_should_trap()
|
||||
//
|
||||
// Read data in a range of CPU addresses (32-bit addresses). Fail
|
||||
// if trap signal is not asserted.
|
||||
// Range is inclusive.
|
||||
//----------------------------------------------------------------
|
||||
task cpu_read_check_range_should_trap(input [31 : 0] start_address, input [31 : 0] end_address);
|
||||
begin : read_check_range_should_not_trap
|
||||
reg [32 : 0] address;
|
||||
reg error_detected;
|
||||
|
||||
address = start_address;
|
||||
error_detected = 0;
|
||||
|
||||
while (!error_detected && (address <= end_address)) begin
|
||||
reset_dut();
|
||||
cpu_read_word(address);
|
||||
if (tb_force_trap == 0) begin
|
||||
$display("--- Error: Expected trap when reading from address 0x%08x", address);
|
||||
error_ctr += 1;
|
||||
error_detected = 1;
|
||||
end
|
||||
address += 1;
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// cpu_read_check_range_should_not_trap()
|
||||
//
|
||||
// Read data in a range of CPU addresses (32-bit addresses). Fail
|
||||
// if trap signal is asserted.
|
||||
// Range is inclusive.
|
||||
//----------------------------------------------------------------
|
||||
task cpu_read_check_range_should_not_trap(input [31 : 0] start_address, input [31 : 0] end_address);
|
||||
begin : read_check_should_not_trap
|
||||
reg [31 : 0] address;
|
||||
reg error_detected;
|
||||
|
||||
address = start_address;
|
||||
error_detected = 0;
|
||||
|
||||
while (!error_detected && (address <= end_address)) begin
|
||||
reset_dut();
|
||||
cpu_read_word(address);
|
||||
if (tb_force_trap == 1) begin
|
||||
$display("--- Error: Did not expected trap when reading from address 0x%08x", address);
|
||||
error_ctr += 1;
|
||||
error_detected = 1;
|
||||
end
|
||||
address += 1;
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// test1()
|
||||
// Read out name and version.
|
||||
|
@ -400,10 +551,27 @@ module tb_tk1 ();
|
|||
|
||||
$display("");
|
||||
$display("--- test2: Read out UDI started.");
|
||||
tb_syscall = 0;
|
||||
reset_dut();
|
||||
|
||||
read_check_word(ADDR_UDI_FIRST, 32'h00010203);
|
||||
read_check_word(ADDR_UDI_LAST, 32'h04050607);
|
||||
|
||||
$display("--- test2: Switch to app mode.");
|
||||
fetch_instruction(APP_RAM_START);
|
||||
|
||||
read_check_word(ADDR_UDI_FIRST, 32'h0);
|
||||
read_check_word(ADDR_UDI_LAST, 32'h0);
|
||||
|
||||
$display("--- test2: Enter syscall.");
|
||||
tb_syscall = 1;
|
||||
|
||||
read_check_word(ADDR_UDI_FIRST, 32'h00010203);
|
||||
read_check_word(ADDR_UDI_LAST, 32'h04050607);
|
||||
|
||||
$display("--- test2: Leave syscall.");
|
||||
tb_syscall = 0;
|
||||
|
||||
$display("--- test2: completed.");
|
||||
$display("");
|
||||
end
|
||||
|
@ -418,6 +586,10 @@ module tb_tk1 ();
|
|||
begin
|
||||
tc_ctr = tc_ctr + 1;
|
||||
|
||||
$display("--- test5: Reset DUT to switch to fw mode.");
|
||||
tb_syscall = 0;
|
||||
reset_dut();
|
||||
|
||||
$display("");
|
||||
$display("--- test3: Write and read CDI started.");
|
||||
$display("--- test3: Write CDI.");
|
||||
|
@ -441,9 +613,9 @@ module tb_tk1 ();
|
|||
read_check_word(ADDR_CDI_LAST + 0, 32'h70717273);
|
||||
|
||||
$display("--- test3: Switch to app mode.");
|
||||
write_word(ADDR_SYSTEM_MODE_CTRL, 32'hdeadbeef);
|
||||
fetch_instruction(APP_RAM_START);
|
||||
|
||||
$display("--- test3: Try to write CDI again.");
|
||||
$display("--- test3: Try to write CDI from app mode.");
|
||||
write_word(ADDR_CDI_FIRST + 0, 32'hfffefdfc);
|
||||
write_word(ADDR_CDI_FIRST + 1, 32'hefeeedec);
|
||||
write_word(ADDR_CDI_FIRST + 2, 32'hdfdedddc);
|
||||
|
@ -453,7 +625,7 @@ module tb_tk1 ();
|
|||
write_word(ADDR_CDI_FIRST + 6, 32'h8f8e8d8c);
|
||||
write_word(ADDR_CDI_FIRST + 7, 32'h7f7e7d7c);
|
||||
|
||||
$display("--- test3: Read CDI again.");
|
||||
$display("--- test3: Read CDI from app mode.");
|
||||
read_check_word(ADDR_CDI_FIRST + 0, 32'hf0f1f2f3);
|
||||
read_check_word(ADDR_CDI_FIRST + 1, 32'he0e1e2e3);
|
||||
read_check_word(ADDR_CDI_FIRST + 2, 32'hd0d1d2d3);
|
||||
|
@ -463,46 +635,38 @@ module tb_tk1 ();
|
|||
read_check_word(ADDR_CDI_FIRST + 6, 32'h80818283);
|
||||
read_check_word(ADDR_CDI_LAST + 0, 32'h70717273);
|
||||
|
||||
$display("--- test3: Enter syscall.");
|
||||
tb_syscall = 1;
|
||||
|
||||
$display("--- test3: Try to write CDI from syscall.");
|
||||
write_word(ADDR_CDI_FIRST + 0, 32'hfffefdfc);
|
||||
write_word(ADDR_CDI_FIRST + 1, 32'hefeeedec);
|
||||
write_word(ADDR_CDI_FIRST + 2, 32'hdfdedddc);
|
||||
write_word(ADDR_CDI_FIRST + 3, 32'hcfcecdcc);
|
||||
write_word(ADDR_CDI_FIRST + 4, 32'hafaeadac);
|
||||
write_word(ADDR_CDI_FIRST + 5, 32'h9f9e9d9c);
|
||||
write_word(ADDR_CDI_FIRST + 6, 32'h8f8e8d8c);
|
||||
write_word(ADDR_CDI_FIRST + 7, 32'h7f7e7d7c);
|
||||
|
||||
$display("--- test3: Read CDI from syscall.");
|
||||
read_check_word(ADDR_CDI_FIRST + 0, 32'hfffefdfc);
|
||||
read_check_word(ADDR_CDI_FIRST + 1, 32'hefeeedec);
|
||||
read_check_word(ADDR_CDI_FIRST + 2, 32'hdfdedddc);
|
||||
read_check_word(ADDR_CDI_FIRST + 3, 32'hcfcecdcc);
|
||||
read_check_word(ADDR_CDI_FIRST + 4, 32'hafaeadac);
|
||||
read_check_word(ADDR_CDI_FIRST + 5, 32'h9f9e9d9c);
|
||||
read_check_word(ADDR_CDI_FIRST + 6, 32'h8f8e8d8c);
|
||||
read_check_word(ADDR_CDI_LAST + 0, 32'h7f7e7d7c);
|
||||
|
||||
$display("--- test3: Leave syscall.");
|
||||
tb_syscall = 0;
|
||||
|
||||
$display("--- test3: completed.");
|
||||
$display("");
|
||||
end
|
||||
endtask // test3
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// test4()
|
||||
// Write and read blake2s entry point.
|
||||
//----------------------------------------------------------------
|
||||
task test4;
|
||||
begin
|
||||
tc_ctr = tc_ctr + 1;
|
||||
|
||||
$display("");
|
||||
$display("--- test4: Write and read blake2s entry point in fw mode started.");
|
||||
$display("--- test4: Reset DUT to switch to fw mode.");
|
||||
reset_dut();
|
||||
|
||||
$display("--- test4: Write Blake2s entry point.");
|
||||
write_word(ADDR_BLAKE2S, 32'hcafebabe);
|
||||
|
||||
$display("--- test4: Read Blake2s entry point.");
|
||||
read_check_word(ADDR_BLAKE2S, 32'hcafebabe);
|
||||
|
||||
$display("--- test4: Switch to app mode.");
|
||||
write_word(ADDR_SYSTEM_MODE_CTRL, 32'hf00ff00f);
|
||||
|
||||
$display("--- test4: Write Blake2s entry point again.");
|
||||
write_word(ADDR_BLAKE2S, 32'hdeadbeef);
|
||||
|
||||
$display("--- test4: Read Blake2s entry point again");
|
||||
read_check_word(ADDR_BLAKE2S, 32'hcafebabe);
|
||||
|
||||
$display("--- test4: completed.");
|
||||
$display("");
|
||||
end
|
||||
endtask // test4
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// test5()
|
||||
// Write and read APP start address end size.
|
||||
|
@ -525,7 +689,7 @@ module tb_tk1 ();
|
|||
read_check_word(ADDR_APP_SIZE, 32'h47114711);
|
||||
|
||||
$display("--- test5: Switch to app mode.");
|
||||
write_word(ADDR_SYSTEM_MODE_CTRL, 32'hf000000);
|
||||
fetch_instruction(APP_RAM_START);
|
||||
|
||||
$display("--- test5: Write app start address and size again.");
|
||||
write_word(ADDR_APP_START, 32'hdeadbeef);
|
||||
|
@ -543,7 +707,7 @@ module tb_tk1 ();
|
|||
|
||||
//----------------------------------------------------------------
|
||||
// test6()
|
||||
// Write RAM address and data randomizatio in fw mode.
|
||||
// Write and read RAM-address and data randomization.
|
||||
//----------------------------------------------------------------
|
||||
task test6;
|
||||
begin
|
||||
|
@ -552,6 +716,7 @@ module tb_tk1 ();
|
|||
$display("");
|
||||
$display("--- test6: Write RAM addr and data randomization in fw mode.");
|
||||
$display("--- test6: Reset DUT to switch to fw mode.");
|
||||
tb_syscall = 0;
|
||||
reset_dut();
|
||||
|
||||
$display("--- test6: Write to ADDR_RAM_ADDR_RAND and ADDR_RAM_DATA_RAND .");
|
||||
|
@ -562,9 +727,14 @@ module tb_tk1 ();
|
|||
"--- test6: Check value in dut ADDR_RAM_ADDR_RAND and ADDR_RAM_DATA_RAND registers.");
|
||||
$display("--- test6: ram_addr_rand_reg: 0x%04x, ram_data_rand_reg: 0x%08x",
|
||||
dut.ram_addr_rand, dut.ram_data_rand);
|
||||
check_equal(dut.ram_addr_rand, 15'h1337);
|
||||
check_equal(dut.ram_data_rand, 32'h47114711);
|
||||
read_check_word(ADDR_RAM_ADDR_RAND, 32'h0);
|
||||
read_check_word(ADDR_RAM_DATA_RAND, 32'h0);
|
||||
|
||||
|
||||
$display("--- test6: Switch to app mode.");
|
||||
write_word(ADDR_SYSTEM_MODE_CTRL, 32'hf000000);
|
||||
fetch_instruction(APP_RAM_START);
|
||||
|
||||
$display("--- test6: Write to ADDR_RAM_ADDR_RAND and ADDR_RAM_DATA_RAND again.");
|
||||
write_word(ADDR_RAM_ADDR_RAND, 32'hdeadbeef);
|
||||
|
@ -574,6 +744,30 @@ module tb_tk1 ();
|
|||
"--- test6: Check value in dut ADDR_RAM_ADDR_RAND and ADDR_RAM_DATA_RAND registers.");
|
||||
$display("--- test6: ram_addr_rand_reg: 0x%04x, ram_data_rand_reg: 0x%08x",
|
||||
dut.ram_addr_rand, dut.ram_data_rand);
|
||||
check_equal(dut.ram_addr_rand, 15'h1337);
|
||||
check_equal(dut.ram_data_rand, 32'h47114711);
|
||||
read_check_word(ADDR_RAM_ADDR_RAND, 32'h0);
|
||||
read_check_word(ADDR_RAM_DATA_RAND, 32'h0);
|
||||
|
||||
|
||||
$display("--- test6: Enter syscall.");
|
||||
tb_syscall = 1;
|
||||
|
||||
$display("--- test6: Write to ADDR_RAM_ADDR_RAND and ADDR_RAM_DATA_RAND again.");
|
||||
write_word(ADDR_RAM_ADDR_RAND, 32'hdeadbeef);
|
||||
write_word(ADDR_RAM_DATA_RAND, 32'hf00ff00f);
|
||||
|
||||
$display(
|
||||
"--- test6: Check value in dut ADDR_RAM_ADDR_RAND and ADDR_RAM_DATA_RAND registers.");
|
||||
$display("--- test6: ram_addr_rand_reg: 0x%04x, ram_data_rand_reg: 0x%08x",
|
||||
dut.ram_addr_rand, dut.ram_data_rand);
|
||||
check_equal(dut.ram_addr_rand, 15'h3eef);
|
||||
check_equal(dut.ram_data_rand, 32'hf00ff00f);
|
||||
read_check_word(ADDR_RAM_ADDR_RAND, 32'h0);
|
||||
read_check_word(ADDR_RAM_DATA_RAND, 32'h0);
|
||||
|
||||
$display("--- test6: Leave syscall.");
|
||||
tb_syscall = 0;
|
||||
|
||||
$display("--- test6: completed.");
|
||||
$display("");
|
||||
|
@ -655,17 +849,20 @@ module tb_tk1 ();
|
|||
write_word(ADDR_CPU_MON_LAST, 32'hdeadcafe);
|
||||
$display("--- test9: cpu_mon_first_reg: 0x%08x, cpu_mon_last_reg: 0x%08x",
|
||||
dut.cpu_mon_first_reg, dut.cpu_mon_last_reg);
|
||||
check_equal(dut.cpu_mon_first_reg, 32'h10000000);
|
||||
check_equal(dut.cpu_mon_last_reg, 32'h20000000);
|
||||
|
||||
$display("--- test9: force_trap before illegal access: 0x%1x", tb_force_trap);
|
||||
$display("--- test9: Creating an illegal access.");
|
||||
|
||||
tb_cpu_addr = 32'h13371337;
|
||||
tb_cpu_instr = 1'h1;
|
||||
tb_cpu_valid = 1'h1;
|
||||
#(2 * CLK_PERIOD);
|
||||
fetch_instruction(32'h13371337);
|
||||
$display("--- test9: cpu_addr: 0x%08x, cpu_instr: 0x%1x, cpu_valid: 0x%1x", tb_cpu_addr,
|
||||
tb_cpu_instr, tb_cpu_valid);
|
||||
check_equal(dut.cpu_mon_first_reg, 32'h10000000);
|
||||
check_equal(dut.cpu_mon_last_reg, 32'h20000000);
|
||||
|
||||
$display("--- test9: force_trap: 0x%1x", tb_force_trap);
|
||||
check_equal(tb_force_trap, 1);
|
||||
|
||||
$display("--- test9: completed.");
|
||||
$display("");
|
||||
|
@ -673,6 +870,66 @@ module tb_tk1 ();
|
|||
endtask // test9
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// check_inverting_spi_loopback_transfer_succeeds()
|
||||
// Do an SPI tranfer. Check that the received value is the inverse
|
||||
// of the value sent.
|
||||
//----------------------------------------------------------------
|
||||
task check_inverting_spi_loopback_transfer_succeeds(input [32 : 0] data);
|
||||
begin : check_inverting_spi_loopback_transfer
|
||||
$display("--- test10: Sending a byte.");
|
||||
write_word(ADDR_SPI_EN, 32'h1);
|
||||
write_word(ADDR_SPI_DATA, data);
|
||||
write_word(ADDR_SPI_XFER, 32'h1);
|
||||
|
||||
// Ready ready flag in SPI until it is set.
|
||||
read_word(ADDR_SPI_XFER);
|
||||
while (!tb_read_data) begin
|
||||
read_word(ADDR_SPI_XFER);
|
||||
end
|
||||
$display("--- test10: Byte should have been sent.");
|
||||
|
||||
#(2 * CLK_PERIOD);
|
||||
read_check_word(ADDR_SPI_DATA, ~data[7 : 0] & 8'hff);
|
||||
write_word(ADDR_SPI_EN, 32'h0);
|
||||
end
|
||||
endtask
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// check_spi_does_not_transfer()
|
||||
// Do an SPI transfer. Check that the SS, SCK and MISO signal are
|
||||
// not active.
|
||||
//----------------------------------------------------------------
|
||||
task check_spi_does_not_transfer;
|
||||
begin : check_spi_does_not_transfer
|
||||
reg [31 : 0] wait_ctr;
|
||||
reg error;
|
||||
localparam CLK_PER_SPI_BIT = 3;
|
||||
localparam WAIT_MARGIN = 10;
|
||||
|
||||
error = 0;
|
||||
wait_ctr = CLK_PER_SPI_BIT * 8 * WAIT_MARGIN;
|
||||
|
||||
$display("--- test10: Sending a byte.");
|
||||
write_word(ADDR_SPI_EN, 32'h1);
|
||||
write_word(ADDR_SPI_DATA, 32'haa);
|
||||
write_word(ADDR_SPI_XFER, 32'h1);
|
||||
|
||||
$display("--- test10: Waiting to see if SPI signals change state.");
|
||||
while (!error && (wait_ctr != 0)) begin
|
||||
if (~tb_spi_ss || tb_spi_sck || tb_spi_mosi) begin
|
||||
$display("--- Error: SPI signals changed state");
|
||||
error_ctr = error_ctr + 1;
|
||||
error = 1;
|
||||
end
|
||||
#(CLK_PERIOD);
|
||||
wait_ctr = wait_ctr - 1;
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// test10()
|
||||
// SPI master loopback test.
|
||||
|
@ -683,28 +940,28 @@ module tb_tk1 ();
|
|||
tb_monitor = 0;
|
||||
tb_spi_monitor = 0;
|
||||
|
||||
restore_mem_bus();
|
||||
reset_dut();
|
||||
|
||||
$display("");
|
||||
$display("--- test10: Loopback in SPI Master started.");
|
||||
|
||||
#(CLK_PERIOD);
|
||||
|
||||
// Sending 0xa7 trough the inverting loopback.
|
||||
$display("--- test10: Sending a byte.");
|
||||
write_word(ADDR_SPI_EN, 32'h1);
|
||||
write_word(ADDR_SPI_DATA, 32'ha7);
|
||||
write_word(ADDR_SPI_XFER, 32'h1);
|
||||
check_inverting_spi_loopback_transfer_succeeds(32'ha7);
|
||||
|
||||
// Ready ready flag in SPI until it is set.
|
||||
read_word(ADDR_SPI_XFER);
|
||||
while (!tb_read_data) begin
|
||||
read_word(ADDR_SPI_XFER);
|
||||
end
|
||||
$display("--- test10: Byte should have been sent.");
|
||||
$display("--- test10: Switch to app mode.");
|
||||
fetch_instruction(APP_RAM_START);
|
||||
|
||||
// 0x58 is the inverse of 0xa7.
|
||||
#(2 * CLK_PERIOD);
|
||||
read_check_word(ADDR_SPI_DATA, 32'h58);
|
||||
write_word(ADDR_SPI_EN, 32'h0);
|
||||
check_spi_does_not_transfer();
|
||||
|
||||
$display("--- test10: Enter syscall.");
|
||||
tb_syscall = 1;
|
||||
|
||||
check_inverting_spi_loopback_transfer_succeeds(32'hc8);
|
||||
|
||||
$display("--- test10: Leave syscall.");
|
||||
tb_syscall = 0;
|
||||
|
||||
tb_monitor = 0;
|
||||
tb_spi_monitor = 0;
|
||||
|
@ -714,6 +971,198 @@ module tb_tk1 ();
|
|||
end
|
||||
endtask // test10
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// test11()
|
||||
// Test security monitor trap ranges.
|
||||
// - Check that reading accessible areas does not trap
|
||||
// - Check that reading the start and end of inaccessible areas
|
||||
// trap
|
||||
//----------------------------------------------------------------
|
||||
task test11;
|
||||
begin
|
||||
tc_ctr = tc_ctr + 1;
|
||||
|
||||
$display("");
|
||||
$display("--- test11: Test trap ranges.");
|
||||
|
||||
// ROM trap range: 0x00004000-0x3fffffff
|
||||
$display("--- test11: ROM");
|
||||
cpu_read_check_range_should_not_trap(32'h0, 32'h1fff);
|
||||
cpu_read_check_range_should_trap(32'h2000, 32'h200f);
|
||||
cpu_read_check_range_should_trap(32'h3ffffff0, 32'h3fffffff);
|
||||
|
||||
// RAM trap range: 0x40020000-0x7fffffff
|
||||
$display("--- test11: RAM");
|
||||
cpu_read_check_range_should_not_trap(32'h40000000, 32'h4000000f);
|
||||
cpu_read_check_range_should_trap(32'h40020000, 32'h4002000f);
|
||||
cpu_read_check_range_should_trap(32'h7ffffff0, 32'h7fffffff);
|
||||
|
||||
// Reserved trap range: 0x80000000-0xbfffffff
|
||||
$display("--- test11: Reserved");
|
||||
cpu_read_check_range_should_trap(32'h80000000, 32'h8000000f);
|
||||
cpu_read_check_range_should_trap(32'hbffffff0, 32'hbfffffff);
|
||||
|
||||
// TRNG trap range: 0xc0000400-0xc0ffffff
|
||||
$display("--- test11: TRNG");
|
||||
cpu_read_check_range_should_not_trap(32'hc0000000, 32'hc00003ff);
|
||||
cpu_read_check_range_should_trap(32'hc0000400, 32'hc000040f);
|
||||
cpu_read_check_range_should_trap(32'hc0fffff0, 32'hc0ffffff);
|
||||
|
||||
// TIMER trap range: 0xc1000400-0xc1ffffff
|
||||
$display("--- test11: TIMER");
|
||||
cpu_read_check_range_should_not_trap(32'hc1000000, 32'hc10003ff);
|
||||
cpu_read_check_range_should_trap(32'hc1000400, 32'hc100040f);
|
||||
cpu_read_check_range_should_trap(32'hc1fffff0, 32'hc1ffffff);
|
||||
|
||||
// UDS trap range: 0xc2000020-0xc2ffffff
|
||||
$display("--- test11: UDS");
|
||||
cpu_read_check_range_should_not_trap(32'hc2000000, 32'hc200001f);
|
||||
cpu_read_check_range_should_trap(32'hc2000020, 32'hc200002f);
|
||||
cpu_read_check_range_should_trap(32'hc2fffff0, 32'hc2ffffff);
|
||||
|
||||
// UART trap range: 0xc3000400-0xc3ffffff
|
||||
$display("--- test11: UART");
|
||||
cpu_read_check_range_should_not_trap(32'hc3000000, 32'hc30003ff);
|
||||
cpu_read_check_range_should_trap(32'hc3000400, 32'hc300040f);
|
||||
cpu_read_check_range_should_trap(32'hc3fffff0, 32'hc3ffffff);
|
||||
|
||||
// TOUCH_SENSE trap range: 0xc4000400-0xc4ffffff
|
||||
$display("--- test11: TOUCH_SENSE");
|
||||
cpu_read_check_range_should_not_trap(32'hc4000000, 32'hc40003ff);
|
||||
cpu_read_check_range_should_trap(32'hc4000400, 32'hc400040f);
|
||||
cpu_read_check_range_should_trap(32'hc4fffff0, 32'hc4ffffff);
|
||||
|
||||
// Unused trap range: 0xc5000000-0xcfffffff
|
||||
$display("--- test11: Unused");
|
||||
cpu_read_check_range_should_trap(32'hc5000000, 32'hc500000f);
|
||||
cpu_read_check_range_should_trap(32'hc5fffff0, 32'hc5ffffff);
|
||||
|
||||
// FW_RAM trap range: 0xd0000800-0xd0ffffff
|
||||
$display("--- test11: FW_RAM");
|
||||
cpu_read_check_range_should_not_trap(32'hd0000000, 32'hd0000fff);
|
||||
cpu_read_check_range_should_trap(32'hd0001000, 32'hd000100f);
|
||||
cpu_read_check_range_should_trap(32'hd0fffff0, 32'hd0ffffff);
|
||||
|
||||
// Unused trap range: 0xd1000000-0xfeffffff
|
||||
$display("--- test11: Unused");
|
||||
cpu_read_check_range_should_trap(32'hd1000000, 32'hd100000f);
|
||||
cpu_read_check_range_should_trap(32'he0fffff0, 32'he0ffffff);
|
||||
|
||||
// SYSCALL trap range. 0xe1000004-0xe1ffffff
|
||||
$display("--- test11: SYSCALL");
|
||||
cpu_read_check_range_should_not_trap(32'he1000000, 32'he1000003);
|
||||
cpu_read_check_range_should_trap(32'he1000004, 32'he100000f);
|
||||
cpu_read_check_range_should_trap(32'he1fffff0, 32'he1ffffff);
|
||||
|
||||
// Unused trap range: 0xe2000000-0xfeffffff
|
||||
$display("--- test11: Unused");
|
||||
cpu_read_check_range_should_trap(32'he2000000, 32'he200000f);
|
||||
cpu_read_check_range_should_trap(32'hfefffff0, 32'hfeffffff);
|
||||
|
||||
// TK1 trap range: 0xff000400-0xffffffff
|
||||
$display("--- test11: TK1");
|
||||
cpu_read_check_range_should_not_trap(32'hff000000, 32'hff0003ff);
|
||||
cpu_read_check_range_should_trap(32'hff000400, 32'hff00040f);
|
||||
cpu_read_check_range_should_trap(32'hfffffff0, 32'hffffffff);
|
||||
|
||||
$display("--- test11: completed.");
|
||||
$display("");
|
||||
end
|
||||
endtask // test11
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// test12()
|
||||
// Test ROM execution protection. Test trapping at ROM edges while
|
||||
// executing in different contexts.
|
||||
//----------------------------------------------------------------
|
||||
task test12;
|
||||
begin
|
||||
tc_ctr = tc_ctr + 1;
|
||||
|
||||
restore_mem_bus();
|
||||
|
||||
$display("");
|
||||
$display("--- test12: ROM execution allowed in firmware mode.");
|
||||
|
||||
reset_dut();
|
||||
fetch_instruction(ROM_START);
|
||||
check_equal(tb_force_trap, 0);
|
||||
|
||||
fetch_instruction(ROM_END);
|
||||
check_equal(tb_force_trap, 0);
|
||||
|
||||
$display("--- test12: ROM execution not allowed in app mode.");
|
||||
reset_dut();
|
||||
fetch_instruction(APP_RAM_START);
|
||||
fetch_instruction(ROM_START);
|
||||
check_equal(tb_force_trap, 1);
|
||||
|
||||
reset_dut();
|
||||
fetch_instruction(APP_RAM_START);
|
||||
fetch_instruction(ROM_END);
|
||||
check_equal(tb_force_trap, 1);
|
||||
|
||||
$display("--- test12: ROM execution allowed in syscalls made from app mode.");
|
||||
reset_dut();
|
||||
fetch_instruction(APP_RAM_START);
|
||||
tb_syscall = 1;
|
||||
|
||||
fetch_instruction(ROM_START);
|
||||
check_equal(tb_force_trap, 0);
|
||||
|
||||
fetch_instruction(ROM_END);
|
||||
check_equal(tb_force_trap, 0);
|
||||
|
||||
$display("--- test12: Leave syscall.");
|
||||
tb_syscall = 0;
|
||||
|
||||
$display("--- test12: completed.");
|
||||
$display("");
|
||||
end
|
||||
endtask // test12
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// test13()
|
||||
// System reset
|
||||
//----------------------------------------------------------------
|
||||
task test13;
|
||||
begin
|
||||
tc_ctr = tc_ctr + 1;
|
||||
|
||||
$display("");
|
||||
$display("--- test13: Reset allowed from firmware mode.");
|
||||
tb_syscall = 0;
|
||||
reset_dut();
|
||||
|
||||
write_word(ADDR_SYSTEM_RESET, 32'h1);
|
||||
check_equal(tb_system_reset, 1);
|
||||
|
||||
$display("--- test13: Reset not allowed from app mode.");
|
||||
reset_dut();
|
||||
fetch_instruction(APP_RAM_START);
|
||||
|
||||
write_word(ADDR_SYSTEM_RESET, 32'h1);
|
||||
check_equal(tb_system_reset, 0);
|
||||
|
||||
$display("--- test13: Reset allowed from syscall.");
|
||||
reset_dut();
|
||||
fetch_instruction(APP_RAM_START);
|
||||
tb_syscall = 1;
|
||||
|
||||
write_word(ADDR_SYSTEM_RESET, 32'h1);
|
||||
check_equal(tb_system_reset, 1);
|
||||
|
||||
tb_syscall = 0;
|
||||
|
||||
$display("--- test13: completed.");
|
||||
$display("");
|
||||
end
|
||||
endtask // test13
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// exit_with_error_code()
|
||||
//
|
||||
|
@ -746,7 +1195,6 @@ module tb_tk1 ();
|
|||
test1();
|
||||
test2();
|
||||
test3();
|
||||
test4();
|
||||
test5();
|
||||
test6();
|
||||
test7();
|
||||
|
@ -754,6 +1202,9 @@ module tb_tk1 ();
|
|||
test9();
|
||||
test9();
|
||||
test10();
|
||||
test11();
|
||||
test12();
|
||||
test13();
|
||||
|
||||
display_test_result();
|
||||
$display("");
|
||||
|
|
|
@ -6,8 +6,7 @@ Unique Device Secret core
|
|||
|
||||
This core store and protect the Unique Device Secret (UDS) asset. The
|
||||
UDS can be accessed as eight separate 32-bit words. The words can only
|
||||
be accessed as long as the system_mode input is low, implying that the
|
||||
CPU is executing the FW.
|
||||
be accessed as long as the `en` input is high.
|
||||
|
||||
The UDS words can be accessed in any order, but a given word can only
|
||||
be accessed once between reset cycles. This read once functionality is
|
||||
|
|
|
@ -17,8 +17,7 @@ module uds (
|
|||
input wire clk,
|
||||
input wire reset_n,
|
||||
|
||||
input wire system_mode,
|
||||
|
||||
input wire en,
|
||||
input wire cs,
|
||||
input wire [ 2 : 0] address,
|
||||
output wire [31 : 0] read_data,
|
||||
|
@ -89,7 +88,7 @@ module uds (
|
|||
if (cs) begin
|
||||
tmp_ready = 1'h1;
|
||||
|
||||
if (!system_mode) begin
|
||||
if (en) begin
|
||||
if (uds_rd_reg[address[2 : 0]] == 1'h0) begin
|
||||
uds_rd_we = 1'h1;
|
||||
end
|
||||
|
|
|
@ -37,7 +37,7 @@ module tb_uds ();
|
|||
|
||||
reg tb_clk;
|
||||
reg tb_reset_n;
|
||||
reg tb_system_mode;
|
||||
reg tb_app_mode;
|
||||
reg tb_cs;
|
||||
reg [ 7 : 0] tb_address;
|
||||
wire [31 : 0] tb_read_data;
|
||||
|
@ -50,7 +50,7 @@ module tb_uds ();
|
|||
.clk(tb_clk),
|
||||
.reset_n(tb_reset_n),
|
||||
|
||||
.system_mode(tb_system_mode),
|
||||
.app_mode(tb_app_mode),
|
||||
|
||||
.cs(tb_cs),
|
||||
.address(tb_address),
|
||||
|
@ -95,7 +95,7 @@ module tb_uds ();
|
|||
$display("State of DUT at cycle: %08d", cycle_ctr);
|
||||
$display("------------");
|
||||
$display("Inputs and outputs:");
|
||||
$display("system_mode: 0x%1x", tb_system_mode);
|
||||
$display("app_mode: 0x%1x", tb_app_mode);
|
||||
$display("cs: 0x%1x, address: 0x%02x, read_data: 0x%08x", tb_cs, tb_address, tb_read_data);
|
||||
$display("");
|
||||
|
||||
|
@ -160,7 +160,7 @@ module tb_uds ();
|
|||
|
||||
tb_clk = 1'h0;
|
||||
tb_reset_n = 1'h1;
|
||||
tb_system_mode = 1'h0;
|
||||
tb_app_mode = 1'h0;
|
||||
tb_cs = 1'h0;
|
||||
tb_address = 8'h0;
|
||||
end
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
00010203
|
||||
073570c0
|
||||
04050607
|
||||
|
|
|
@ -1 +1 @@
|
|||
39d5aee11b8553544ba9171f83fbe6f5b7546a15c70d03325e72a2b0ca86c8f7a2b5b6bf121d1d3ffc84a502a2a1a6f3ea140d1424cd424336e055be2f394f83 firmware.bin
|
||||
4be2767d5ddd30b5422f4b58075365cb6d988259e88ffa14d6d243560b289f54eaf0c351e9d744cff8ec3a18b1830f3925a86f36bd2096c12eccce25ed44993c firmware.bin
|
||||
|
|
|
@ -1,35 +1,44 @@
|
|||
# Firmware
|
||||
# Firmware implementation notes
|
||||
|
||||
## Introduction
|
||||
|
||||
This text is an introduction to, a requirement specification of,
|
||||
and some implementation notes of the TKey firmware. It also gives a
|
||||
few hint on developing and debugging the firmware.
|
||||
|
||||
This text is specific for the firmware. For a more general description
|
||||
on how to implement device apps, see [the TKey Developer
|
||||
Handbook](https://dev.tillitis.se/).
|
||||
This text is specific for the firmware, the piece of software in TKey
|
||||
ROM. For a more general description on how to implement device apps,
|
||||
see [the TKey Developer Handbook](https://dev.tillitis.se/).
|
||||
|
||||
## Definitions
|
||||
|
||||
- Firmware - software in ROM responsible for loading applications. The
|
||||
firmware is included as part of the FPGA bitstream and not
|
||||
replacable on a usual consumer TKey.
|
||||
- Device application or app - software supplied by the client which is
|
||||
received, loaded, measured, and started by the firmware.
|
||||
- Firmware: Software in ROM responsible for loading, measuring,
|
||||
starting applications, and providing system calls. The firmware is
|
||||
included as part of the FPGA bitstream and not replacable on a usual
|
||||
consumer TKey.
|
||||
- Client: Software running on a computer or a mobile phone the TKey is
|
||||
inserted into.
|
||||
- Device application or app: Software supplied by the client or from
|
||||
flash that runs on the TKey.
|
||||
|
||||
## CPU modes and firmware
|
||||
|
||||
The TKey has two modes of software operation: firmware mode and
|
||||
application mode. The TKey always starts in firmware mode and starts
|
||||
the firmware. When the firmware is about to start the application it
|
||||
switches to a more constrained environment, the application mode.
|
||||
application mode. The TKey always starts in firmware mode when it
|
||||
starts the firmware. When the application starts the hardware
|
||||
automatically switches to a more constrained environment: the
|
||||
application mode.
|
||||
|
||||
The TKey hardware cores are memory mapped. Firmware has complete
|
||||
access, except that the UDS is readable only once. The memory map is
|
||||
constrained when running in application mode, e.g. FW\_RAM and UDS
|
||||
isn't readable, and several other hardware addresses are either not
|
||||
readable or not writable for the application.
|
||||
The TKey hardware cores are memory mapped but the memory access is
|
||||
different depending on mode. Firmware has complete access, except that
|
||||
the Unique Device Secret (UDS) words are readable only once even in
|
||||
firmware mode. The memory map is constrained when running in
|
||||
application mode, e.g. FW\_RAM and UDS isn't readable, and several
|
||||
other hardware addresses are either not readable or not writable for
|
||||
the application.
|
||||
|
||||
When doing system calls from a device app the context switches back to
|
||||
firmware mode. However, the UDS is still not available, protected by
|
||||
two measures: 1) the UDS words can only be read out once and have
|
||||
already been read by firmware when measuring the app, and, 2) the UDS
|
||||
is protected by hardware after the execution leaves ROM for the first
|
||||
time.
|
||||
|
||||
See the table in [the Developer
|
||||
Handbook](https://dev.tillitis.se/memory/) for an overview about the
|
||||
|
@ -37,171 +46,441 @@ memory access control.
|
|||
|
||||
## Communication
|
||||
|
||||
The firmware communicates to the host via the
|
||||
`UART_{RX,TX}_{STATUS,DATA}` registers, using the framing protocol
|
||||
described in the [Framing
|
||||
Protocol](https://dev.tillitis.se/protocol/).
|
||||
The firmware communicates with the client using the
|
||||
`UART_{RX,TX}_{STATUS,DATA}` registers. On top of that is uses three
|
||||
protocols: The USB Mode protocol, the TKey framing protocol, and the
|
||||
firmware's own protocol.
|
||||
|
||||
To communicate between the CPU and the CH552 USB controller it uses an
|
||||
internal protocol, used only within the TKey, which we call the USB
|
||||
Mode Protocol. It is used in both directions.
|
||||
|
||||
| *Name* | *Size* | *Comment* |
|
||||
|----------|-----------|------------------------------------|
|
||||
| Endpoint | 1B | Origin or destination USB endpoint |
|
||||
| Length | 1B | Number of bytes following |
|
||||
| Payload | See above | Actual data from or to firmware |
|
||||
|
||||
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. |
|
||||
|
||||
You can turn on and off different endpoints dynamically by sending
|
||||
commands to the `CH552` control endpoint. When the TKey starts only
|
||||
the `CH552` and the `CDC` endpoints are active. To change this, send a
|
||||
command to `CH552` in this form:
|
||||
|
||||
| *Name* | *Size* | *Comment* |
|
||||
|----------|--------|-------------------------------|
|
||||
| Command | 1B | Command to the CH552 firmware |
|
||||
| Argument | 1B | Data for the command |
|
||||
|
||||
Commands:
|
||||
|
||||
| *Name* | *Value* | *Argument* |
|
||||
|------------------|---------|----------------------|
|
||||
| Enable endpoints | 0x01 | Bitmask of endpoints |
|
||||
|
||||
On top of the USB Mode Protocol is [the TKey Framing
|
||||
Protocol](https://dev.tillitis.se/protocol/) which is described in the
|
||||
Developer Handbook.
|
||||
|
||||
The firmware uses a protocol on top of this framing layer which is
|
||||
used to bootstrap the application. All commands are initiated by the
|
||||
used to load a device application. All commands are initiated by the
|
||||
client. All commands receive a reply. See [Firmware
|
||||
protocol](#firmware-protocol) for specific details.
|
||||
protocol](http://dev.tillitis.se/protocol/#firmware-protocol) in the
|
||||
Dev Handbook for specific details.
|
||||
|
||||
## Memory constraints
|
||||
|
||||
- ROM: 6 kByte.
|
||||
- FW\_RAM: 2 kByte.
|
||||
- RAM: 128 kByte.
|
||||
| *Name* | *Size* | *FW mode* | *App mode* |
|
||||
|---------|-----------|-----------|------------|
|
||||
| ROM | 8 kByte | r-x | r |
|
||||
| FW\_RAM | 4 kByte* | rw- | - |
|
||||
| RAM | 128 kByte | rwx | rwx |
|
||||
|
||||
* FW\_RAM is divided into the following areas:
|
||||
|
||||
- fw stack: 3000 bytes.
|
||||
- resetinfo: 256 bytes.
|
||||
- .data and .bss: 840 bytes.
|
||||
|
||||
## Firmware behaviour
|
||||
|
||||
The purpose of the firmware is to load, measure, and start an
|
||||
application received from the client over the USB/UART.
|
||||
The purpose of the firmware is to:
|
||||
|
||||
1. Load, measure, and start an application received from the client
|
||||
over the USB/UART or from one of two flash app slots.
|
||||
2. Provide functionality to run only app's with specific BLAKE2s
|
||||
digests.
|
||||
3. Provide system calls to access the filesystem and get other data.
|
||||
|
||||
The firmware binary is part of the FPGA bitstream as the initial
|
||||
values of the Block RAMs used to construct the `FW_ROM`. The `FW_ROM`
|
||||
start address is located at `0x0000_0000` in the CPU memory map, which
|
||||
is the CPU reset vector.
|
||||
values of the Block RAMs used to construct the ROM. The ROM is located
|
||||
at `0x0000_0000`. This is also the CPU reset vector.
|
||||
|
||||
### Reset type
|
||||
|
||||
When the TKey is started or resetted it can load an app from different
|
||||
sources. We call this the reset type. Reset type is located in the
|
||||
resetinfo part of FW\_RAM. The different reset types loads and start
|
||||
an app from:
|
||||
|
||||
1. Flash slot 0 (default): `FLASH0` with a specific app hash defined
|
||||
in a constant in firmware.
|
||||
2. Flash slot 1: `FLASH1`.
|
||||
3. Flash slot 0 with a specific app hash left from previous app:
|
||||
`FLASH0_VER`
|
||||
4. Flash slot 1 with a specific app hash left from previous app:
|
||||
`FLASH1_VER`.
|
||||
5. Client: `CLIENT`.
|
||||
6. Client with a specific app hash left from previous app:
|
||||
`CLIENT_VER`.
|
||||
|
||||
### Firmware state machine
|
||||
|
||||
This is the state diagram of the firmware. There are only four states.
|
||||
Change of state occur when we receive specific I/O or a fatal error
|
||||
occurs.
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
S1: initial
|
||||
S2: loading
|
||||
S3: running
|
||||
SE: failed
|
||||
S0: INITIAL
|
||||
S1: WAITCOMMAND
|
||||
S2: LOADING
|
||||
S3: LOAD_FLASH
|
||||
S4: LOAD_FLASH_MGMT
|
||||
S5: START
|
||||
SE: FAIL
|
||||
|
||||
[*] --> S1
|
||||
[*] --> S0
|
||||
|
||||
S1 --> S1: Commands
|
||||
S0 --> S1
|
||||
S0 --> S4: Default
|
||||
S0 --> S3
|
||||
S0 --> SE: Unknown reset type
|
||||
|
||||
S1 --> S1: Other commands
|
||||
S1 --> S2: LOAD_APP
|
||||
S1 --> SE: Error
|
||||
|
||||
S2 --> S2: LOAD_APP_DATA
|
||||
S2 --> S3: Last block received
|
||||
S2 --> S5: Last block received
|
||||
S2 --> SE: Error
|
||||
|
||||
S3 --> [*]
|
||||
S3 --> S5
|
||||
S3 --> SE
|
||||
|
||||
S4 --> S5
|
||||
S4 --> SE
|
||||
|
||||
SE --> [*]
|
||||
S5 --> [*]
|
||||
```
|
||||
|
||||
States:
|
||||
|
||||
- `initial` - At start. Allows the commands `NAME_VERSION`, `GET_UDI`,
|
||||
`LOAD_APP`.
|
||||
- `loading` - Expect application data. Allows only the command
|
||||
`LOAD_APP_DATA`.
|
||||
- `run` - Computes CDI and starts the application. Allows no commands.
|
||||
- `fail` - Stops waiting for commands, flashes LED forever. Allows no
|
||||
commands.
|
||||
- *INITIAL*: Transitions to next state through reset type left in
|
||||
`FW_RAM`.
|
||||
- *WAITCOMMAND*: Waiting for initial commands from client. Allows the
|
||||
commands `NAME_VERSION`, `GET_UDI`, `LOAD_APP`.
|
||||
- *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.
|
||||
- *START*: Computes CDI. Possibly verifies app. Starts the
|
||||
application. Does not return to firmware. Allows no commands.
|
||||
- *FAIL* - Halts CPU. Allows no commands.
|
||||
|
||||
Commands in state `initial`:
|
||||
Allowed data in state *INITIAL*:
|
||||
|
||||
| *reset type* | *next state* |
|
||||
|--------------|-------------------|
|
||||
| `FLASH0` | *LOAD_FLASH_MGMT* |
|
||||
| `FLASH1` | *LOAD_FLASH* |
|
||||
| `FLASH0_VER` | *LOAD_FLASH* |
|
||||
| `FLASH1_VER` | *LOAD_FLASH* |
|
||||
| `CLIENT` | *WAITCOMMAND* |
|
||||
| `CLIENT_VER` | *WAITCOMMAND* |
|
||||
| unknown | *FAIL* |
|
||||
|
||||
I/O in state *LOAD_FLASH*:
|
||||
|
||||
| *I/O* | *next state* |
|
||||
|--------------------|--------------|
|
||||
| Last app data read | *START* |
|
||||
|
||||
I/O in state *LOAD_FLASH_MGMT*:
|
||||
|
||||
| *I/O* | *next state* |
|
||||
|--------------------|--------------|
|
||||
| Last app data read | *START* |
|
||||
|
||||
Commands in state *WAITCOMMAND*:
|
||||
|
||||
| *command* | *next state* |
|
||||
|-----------------------|--------------|
|
||||
|-----------------------|--------------------------------------------|
|
||||
| `FW_CMD_NAME_VERSION` | unchanged |
|
||||
| `FW_CMD_GET_UDI` | unchanged |
|
||||
| `FW_CMD_LOAD_APP` | `loading` |
|
||||
| `FW_CMD_LOAD_APP` | *LOADING* or unchanged on invalid app size |
|
||||
| | |
|
||||
|
||||
Commands in state `loading`:
|
||||
Commands in state *LOADING*:
|
||||
|
||||
| *command* | *next state* |
|
||||
|------------------------|----------------------------------|
|
||||
| `FW_CMD_LOAD_APP_DATA` | unchanged or `run` on last chunk |
|
||||
|------------------------|------------------------------------|
|
||||
| `FW_CMD_LOAD_APP_DATA` | unchanged or *START* on last chunk |
|
||||
|
||||
No other states allows commands.
|
||||
|
||||
See [Firmware protocol in the Dev
|
||||
Handbook](http://dev.tillitis.se/protocol/#firmware-protocol) for the
|
||||
definition of the specific commands and their responses.
|
||||
|
||||
State changes from "initial" to "loading" when receiving `LOAD_APP`,
|
||||
which also sets the size of the number of data blocks to expect. After
|
||||
that we expect several `LOAD_APP_DATA` commands until the last block
|
||||
is received, when state is changed to "running".
|
||||
Plain text explanation of the states:
|
||||
|
||||
In "running", the loaded device app is measured, the Compound Device
|
||||
Identifier (CDI) is computed, we do some cleanup of firmware data
|
||||
structures, flip to application mode, and finally start the app, which
|
||||
ends the firmware state machine.
|
||||
- *INITIAL*: Start here. Check the `FW_RAM` for the `resetinfo` type
|
||||
for what to do next.
|
||||
|
||||
The device app is now running in application mode. There is no other
|
||||
means of getting back from application mode to firmware mode than
|
||||
resetting/power cycling the device. Note that ROM is still accessible
|
||||
in the memory map, so it's still possible to execute firmware code in
|
||||
application mode, but with no privileged access.
|
||||
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.
|
||||
|
||||
Firmware loads the application at the start of RAM (`0x4000_0000`). It
|
||||
uses the special FW\_RAM for its own stack.
|
||||
For type `CLIENT*` transitionto *WAITCOMMAND* to expect a device app
|
||||
from the client.
|
||||
|
||||
When the firmware starts it clears all FW\_RAM, then sets up a stack
|
||||
there before jumping to `main()`.
|
||||
If type is unknown, error out.
|
||||
|
||||
- *LOAD_FLASH*: Load device app from flash into RAM, app slot taken
|
||||
from context. Compute a BLAKE2s digest over the entire app.
|
||||
Transition to *START*.
|
||||
|
||||
- *LOAD_FLASH_MGMT*: Load device app from flash into RAM, app slot
|
||||
always 0. Compute a BLAKE2s digest over the entire app. Register the
|
||||
app as a prospective management app. Transition to *START*.
|
||||
|
||||
- *WAITCOMMAND*: Wait for commands from the client. Transition to
|
||||
*LOADING* on `LOAD_APP` command, which also sets the size of the
|
||||
number of data blocks to expect.
|
||||
|
||||
- *LOADING*: Wait for several `LOAD_APP_DATA` commands until the last
|
||||
block is received, then transition to *START*.
|
||||
|
||||
- *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.
|
||||
|
||||
Clean up firmware data structures, enable the system calls, and
|
||||
start the app, which ends the firmware state machine. Hardware
|
||||
guarantees that we leave firmware mode automatically when the
|
||||
program counter leaves ROM.
|
||||
|
||||
- *FAIL*: Execute an illegal instruction which traps the CPU. Hardware
|
||||
detects a trapped CPU and blinks the status LED in red until power
|
||||
loss. No further instructions are executed.
|
||||
|
||||
After leaving *START* the device app is now running in application
|
||||
mode. We can, however, return to firmware mode (excepting access to
|
||||
the UDS) by doing system calls. Note that ROM is still readable, but
|
||||
is now hardware protected from execution, except through the system
|
||||
call mechanism.
|
||||
|
||||
If during this whole time any commands are received which are not
|
||||
allowed in the current state, or any errors occur, we enter the *FAIL*
|
||||
state.
|
||||
|
||||
### Golden path from start to default app
|
||||
|
||||
Firmware will load 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.
|
||||
|
||||
When reset is released, the CPU starts executing the firmware. It
|
||||
begins by clearing all CPU registers, and then sets up a stack for
|
||||
itself and then jumps to main().
|
||||
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.
|
||||
|
||||
Beginning at `main()` it sets up the "system calls", then fills the
|
||||
entire RAM with pseudo random data and setting up the RAM address and
|
||||
data hardware scrambling with values from the True Random Number
|
||||
Generator (TRNG). It then waits for data coming in through the UART.
|
||||
Beginning at `main()` it fills the entire RAM with pseudo random data
|
||||
and setting up the RAM address and data hardware scrambling with
|
||||
values from the True Random Number Generator (TRNG).
|
||||
|
||||
Typical expected use scenario:
|
||||
Firmware then proceeds to:
|
||||
|
||||
1. The client sends the `FW_CMD_LOAD_APP` command with the size of
|
||||
1. Read the partition table from flash and store in FW\_RAM.
|
||||
|
||||
2. Reset the CH552 USB controller to a known state, only allowing the
|
||||
CDC USB endpoint and the internal command channel between the CPU
|
||||
and the CH552.
|
||||
|
||||
3. Check the special resetinfo area in FW\_RAM for reset type. Type
|
||||
zero means default behaviour, load from flash app slot 0, expecting
|
||||
the app there to have a specific hardcoded BLAKE2s digest.
|
||||
|
||||
4. Load app data from flash slot 0 into RAM.
|
||||
|
||||
5. Compute a BLAKE2s digest of the loaded app.
|
||||
|
||||
6. Compare the computed digest against the allowed app digest
|
||||
hardcoded in the firmware. If it's not equal, halt CPU.
|
||||
|
||||
7. [Start the device app](#start-the-device-app).
|
||||
|
||||
### Start the device app
|
||||
|
||||
1. Check if there is a verification digest left from the previous app
|
||||
in the resetinfo. If it is, compare with the loaded app's already
|
||||
computed digest. Halt CPU if different.
|
||||
|
||||
2. Compute the Compound Device Identifier
|
||||
([CDI]((#compound-device-identifier-computation))) by doing a
|
||||
BLAKE2s using the Unique Device Secret (UDS), the application
|
||||
digest, and any User Supplied Secret (USS) digest already received.
|
||||
|
||||
3. Write the start address of the device app, currently `0x4000_0000`,
|
||||
to `APP_ADDR` and the size of the loaded binary to `APP_SIZE` to
|
||||
let the device application know where it is loaded and how large it
|
||||
is, if it wants to relocate in RAM.
|
||||
|
||||
4. Clear the stack part of `FW_RAM`.
|
||||
|
||||
5. Enable system call interrupt handler.
|
||||
|
||||
6. Start the application by jumping to the contents of `APP_ADDR`.
|
||||
Hardware automatically switch from firmware mode to application
|
||||
mode. In this mode some memory access is restricted, e.g. some
|
||||
addresses are inaccessible (`UDS`), and some are switched from
|
||||
read/write to read-only (see [the memory
|
||||
map](https://dev.tillitis.se/memory/)).
|
||||
|
||||
### Management app, chaining apps and verified boot
|
||||
|
||||
Normally, the TKey measures a device app and mixes it together with
|
||||
the Unique Device Secret in hardware to produce the [Compound Device
|
||||
Identifier]((#compound-device-identifier-computation)). The CDI can
|
||||
then be used for creating key material. However, since any key
|
||||
material created like this will change if the app is changed even the
|
||||
slightest, this make it hard to upgrade apps and keep the key
|
||||
material.
|
||||
|
||||
This is where a combination of measured boot and verified boot comes
|
||||
in!
|
||||
|
||||
To support verified boot the firmware supports reset types with
|
||||
verification. This means that the firmware will load an app as usual
|
||||
either from flash or from the client, but before starting the app it
|
||||
will verify the new app's computed digest with a verification digest.
|
||||
The verification digest can either be stored in the firmware itself or
|
||||
left to it from a previous app, a verified boot loader app.
|
||||
|
||||
Such a verified boot loader app:
|
||||
|
||||
- Might be loaded from either flash or client.
|
||||
|
||||
- Typically includes a security policy, for instance a public key and
|
||||
code to check a crytographic signature.
|
||||
|
||||
- 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`.
|
||||
|
||||
It works like this:
|
||||
|
||||
- The app reads a digest of the next app in the chain and the
|
||||
signature over the digest from either the filesystem (syscall
|
||||
`PRELOAD_GET_DIGSIG`) or sent from the client.
|
||||
|
||||
- If the signature provided over the digest is verified against the
|
||||
public key the app use the system call `RESET` with the reset type
|
||||
set to `START_FLASH0_VER`, `START_FLASH1_VER`, or `START_CLIENT_VER`
|
||||
depending on where it wants the next app to start from. It also
|
||||
sends the now verified app digest to the firmware in the same system
|
||||
call.
|
||||
|
||||
- The key is reset and firmware starts again. It checks:
|
||||
|
||||
1. The reset type. Start from client or a slot in the filesystem?
|
||||
2. The expected digest of the next app.
|
||||
|
||||
- Firmware loads the app from the expected source.
|
||||
|
||||
- Firmware refuses to start if the loaded app has a different digest.
|
||||
|
||||
- If the app was allowed to start it can now use something
|
||||
deterministic left for it in resetinfo by the verified boot loader
|
||||
app as a seed for it's key material and no longer use CDI for the
|
||||
purpose.
|
||||
|
||||
We propose that a loader app can derive the seed for the next app by
|
||||
creating a shared secret, perhaps something as easy as:
|
||||
|
||||
```
|
||||
secret = blake2s(cdi, "name-of-next-app")
|
||||
```
|
||||
|
||||
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
|
||||
needs it can derive more keys, for instance by having nonces stored on
|
||||
its flash area and doing:
|
||||
|
||||
```
|
||||
secret1 = blake2s(secret, nonce1)
|
||||
secret2 = blake2s(secret, nonce2)
|
||||
...
|
||||
```
|
||||
|
||||
Now it can create many secrets deterministically, as long as there is
|
||||
some space left on flash for the nonces and all of them can be traced
|
||||
to the measured identity of the loader app, giving all the features of
|
||||
the measured boot system.
|
||||
|
||||
### App loaded from client
|
||||
|
||||
The default is always to start from a verified app in flash slot
|
||||
0. To be able to load an app from the client you have to send
|
||||
something to the app to reset the TKey with a reset type of
|
||||
`START_CLIENT` or `START_CLIENT_VER`.
|
||||
|
||||
After reset, firmware will:
|
||||
|
||||
1. Wait for data coming in through the UART.
|
||||
|
||||
2. The client sends the `FW_CMD_LOAD_APP` command with the size of
|
||||
the device app and the optional 32 byte hash of the user-supplied
|
||||
secret as arguments and gets a `FW_RSP_LOAD_APP` back. After
|
||||
using this it's not possible to restart the loading of an
|
||||
application.
|
||||
|
||||
2. If the the client receive a sucessful response, it will send
|
||||
multiple `FW_CMD_LOAD_APP_DATA` commands, together containing the
|
||||
full application.
|
||||
3. On a sucessful response, the client will send multiple
|
||||
`FW_CMD_LOAD_APP_DATA` commands, together containing the full
|
||||
application.
|
||||
|
||||
3. On receiving`FW_CMD_LOAD_APP_DATA` commands the firmware places
|
||||
4. On receiving`FW_CMD_LOAD_APP_DATA` commands the firmware places
|
||||
the data into `0x4000_0000` and upwards. The firmware replies
|
||||
with a `FW_RSP_LOAD_APP_DATA` response to the client for each
|
||||
received block except the last data block.
|
||||
|
||||
4. When the final block of the application image is received with a
|
||||
5. When the final block of the application image is received with a
|
||||
`FW_CMD_LOAD_APP_DATA`, the firmware measure the application by
|
||||
computing a BLAKE2s digest over the entire application. Then
|
||||
firmware send back the `FW_RSP_LOAD_APP_DATA_READY` response
|
||||
containing the digest.
|
||||
|
||||
5. The Compound Device Identifier
|
||||
([CDI]((#compound-device-identifier-computation))) is then
|
||||
computed by doing a new BLAKE2s using the Unique Device Secret
|
||||
(UDS), the application digest, and any User Supplied Secret
|
||||
(USS).
|
||||
|
||||
6. The start address of the device app, currently `0x4000_0000`, is
|
||||
written to `APP_ADDR` and the size of the binary to `APP_SIZE` to
|
||||
let the device application know where it is loaded and how large
|
||||
it is, if it wants to relocate in RAM.
|
||||
|
||||
7. The firmware now clears the special `FW_RAM` where it keeps it
|
||||
stack. After this it performs no more function calls and uses no
|
||||
more automatic variables.
|
||||
|
||||
8. Firmware starts the application by first switching to from
|
||||
firmware mode to application mode by writing to the `SYSTEM_MODE_CTRL`
|
||||
register. In this mode the MMIO region is restricted, e.g. some
|
||||
registers are removed (`UDS`), and some are switched from
|
||||
read/write to read-only (see [the memory
|
||||
map](https://dev.tillitis.se/memory/)).
|
||||
|
||||
Then the firmware jumps to what is in `APP_ADDR` which starts the
|
||||
application.
|
||||
|
||||
If during this whole time any commands are received which are not
|
||||
allowed in the current state, we enter the "failed" state and execute
|
||||
an illegal instruction. An illegal instruction traps the CPU and
|
||||
hardware blinks the status LED red until a power cycle. No further
|
||||
instructions are executed.
|
||||
6. [Start the device app](#start-the-device-app).
|
||||
|
||||
### User-supplied Secret (USS)
|
||||
|
||||
|
@ -221,65 +500,231 @@ CDI = blake2s(UDS, blake2s(app), USS)
|
|||
In an ideal world, software would never be able to read UDS at all and
|
||||
we would have a BLAKE2s function in hardware that would be the only
|
||||
thing able to read the UDS. Unfortunately, we couldn't fit a BLAKE2s
|
||||
implementation in the FPGA at this time.
|
||||
implementation in the FPGA.
|
||||
|
||||
The firmware instead does the CDI computation using the special
|
||||
firmware-only `FW_RAM` which is invisible after switching to app mode.
|
||||
We keep the entire firmware stack in `FW_RAM` and clear it just before
|
||||
switching to app mode just in case.
|
||||
We keep the entire firmware stack in `FW_RAM` and clear the stack just
|
||||
before switching to app mode just in case.
|
||||
|
||||
We sleep for a random number of cycles before reading out the UDS,
|
||||
call `blake2s_update()` with it and then immediately call
|
||||
`blake2s_update()` again with the program digest, destroying the UDS
|
||||
stored in the internal context buffer. UDS should now not be in
|
||||
`FW_RAM` anymore. We can read UDS only once per power cycle so UDS
|
||||
should now not be available to firmware at all.
|
||||
should now not be available even to firmware.
|
||||
|
||||
Then we continue with the CDI computation by updating with an optional
|
||||
USS and then finalizing the hash, storing the resulting digest in
|
||||
USS digest and finalizing the hash, storing the resulting digest in
|
||||
`CDI`.
|
||||
|
||||
We also clear the entire `FW_RAM` where the stack lived, including the
|
||||
BLAKE2s context with the UDS, very soon after that, just before
|
||||
jumping to the application.
|
||||
### System calls
|
||||
|
||||
### Firmware services
|
||||
|
||||
The firmware exposes a BLAKE2s function through a function pointer
|
||||
located in MMIO `BLAKE2S` (see [memory
|
||||
map](system_description.md#memory-mapped-hardware-functions)) with the
|
||||
with function signature:
|
||||
|
||||
```c
|
||||
int blake2s(void *out, unsigned long outlen, const void *key,
|
||||
unsigned long keylen, const void *in, unsigned long inlen,
|
||||
blake2s_ctx *ctx);
|
||||
The firmware provides a system call mechanism through the use of the
|
||||
PicoRV32 interrupt handler. They are triggered by writing to the
|
||||
trigger address: 0xe1000000. It's typically done with a function
|
||||
signature like this:
|
||||
|
||||
```
|
||||
|
||||
where `blake2s_ctx` is:
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
uint8_t b[64]; // input buffer
|
||||
uint32_t h[8]; // chained state
|
||||
uint32_t t[2]; // total number of bytes
|
||||
size_t c; // pointer for b[]
|
||||
size_t outlen; // digest size
|
||||
} blake2s_ctx;
|
||||
int syscall(uint32_t number, uint32_t arg1, uint32_t arg2,
|
||||
uint32_t arg3);
|
||||
```
|
||||
|
||||
The `libcommon` library in
|
||||
[tkey-libs](https://github.com/tillitis/tkey-libs/)
|
||||
has a wrapper for using this function called `blake2s()` which needs
|
||||
to be maintained if you do any changes to the firmware call.
|
||||
Arguments are system call number and up to 6 generic arguments passed
|
||||
to the system call handler. The caller should place the system call
|
||||
number in the a0 register and the arguments in registers a1 to a7
|
||||
according to the RISC-V calling convention. The caller is responsible
|
||||
for saving and restoring registers.
|
||||
|
||||
The syscall handler returns execution on the next instruction after
|
||||
the store instruction to the trigger address. The return value from
|
||||
the syscall is now available in x10 (a0).
|
||||
|
||||
The syscall numbers are kept in `syscall_num.h`. The syscalls are
|
||||
handled in `syscall_handler()` in `syscall_handler.c`.
|
||||
|
||||
#### `RESET`
|
||||
|
||||
```
|
||||
struct reset {
|
||||
uint32_t type; // Reset type
|
||||
uint8_t app_digest[32]; // Digest of next app in chain to verify
|
||||
uint8_t next_app_data[220]; // Data to leave around for next app
|
||||
};
|
||||
|
||||
struct reset rst;
|
||||
uint32_t len; // Length of data in next_app_data.
|
||||
|
||||
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, len, 0);
|
||||
```
|
||||
|
||||
Resets the TKey. Does not return.
|
||||
|
||||
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`:
|
||||
|
||||
| *Name* | *Comment* |
|
||||
|--------------------|------------------------------------------------|
|
||||
| `START_FLASH0` | Load next app from flash slot 0 |
|
||||
| `START_FLASH1` | Load next app from flash slot 1 |
|
||||
| `START_FLASH0_VER` | Load next app from flash slot 0, but verify it |
|
||||
| `START_FLASH1_VER` | Load next app from flash slot 1, but verify it |
|
||||
| `START_CLIENT` | Load next app from client |
|
||||
| `START_CLIENT_VER` | Load next app from client |
|
||||
|
||||
#### `ALLOC_AREA`
|
||||
|
||||
```
|
||||
syscall(TK1_SYSCALL_ALLOC_AREA, 0, 0, 0);
|
||||
```
|
||||
|
||||
Allocate a flash area for the current app. Returns 0 on success.
|
||||
|
||||
#### `DEALLOC_AREA`
|
||||
|
||||
```
|
||||
syscall(TK1_SYSCALL_DEALLOC_AREA, 0, 0, 0);
|
||||
```
|
||||
|
||||
Free an already allocated flash area for the current app. Returns 0 on
|
||||
success.
|
||||
|
||||
#### `WRITE_DATA`
|
||||
|
||||
```
|
||||
uint32_t offset = 0;
|
||||
uint8_t buf[17];
|
||||
|
||||
syscall(TK1_SYSCALL_WRITE_DATA, offset, (uint32_t)buf, sizeof(buf))
|
||||
```
|
||||
|
||||
Write data in `buf` to the app's flash area at byte `offset` within
|
||||
the area. Returns 0 on success.
|
||||
|
||||
At most 4096 bytes can be written at once and `offset` must be a
|
||||
multiple of 4096 bytes.
|
||||
|
||||
#### `READ_DATA`
|
||||
|
||||
```
|
||||
uint32_t offset = 0;
|
||||
uint8_t buf[17];
|
||||
|
||||
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`
|
||||
|
||||
```
|
||||
syscall(TK1_SYSCALL_PRELOAD_DELETE, 0, 0, 0);
|
||||
```
|
||||
|
||||
Delete the app in flash slot 1. Returns 0 on success. Only available
|
||||
for the verified management app.
|
||||
|
||||
#### `PRELOAD_STORE`
|
||||
|
||||
```
|
||||
uint8_t *appbinary;
|
||||
uint32_t offset;
|
||||
uint32_t size;
|
||||
|
||||
syscall(TK1_SYSCALL_PRELOAD_STORE, offset, (uint32_t)appbinary,
|
||||
size);
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
Only available for the verified management app.
|
||||
|
||||
#### `PRELOAD_STORE_FIN`
|
||||
|
||||
```
|
||||
uint8_t app_digest[32];
|
||||
uint8_t app_signature[64];
|
||||
size_t app_size;
|
||||
|
||||
syscall(TK1_SYSCALL_PRELOAD_STORE_FIN, app_size,
|
||||
(uint32_t)app_digest, (uint32_t)app_signature)
|
||||
```
|
||||
|
||||
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`.
|
||||
|
||||
Sign `app_digest` with your Ed25519 private key and pass the
|
||||
resulting signature in `app_signature`.
|
||||
|
||||
#### `PRELOAD_GET_DIGSIG`
|
||||
|
||||
```
|
||||
uint8_t app_digest[32];
|
||||
uint8_t app_signature[64];
|
||||
|
||||
syscall(TK1_SYSCALL_PRELOAD_GET_DIGSIG, (uint32_t)app_digest,
|
||||
(uint32_t)app_signature, 0);
|
||||
```
|
||||
|
||||
Copies the digest and signature of app in flash slot 1 to `app_digest`
|
||||
and `app_signature`. Returns 0 on success. Only available for the
|
||||
verified management app.
|
||||
|
||||
#### `STATUS`
|
||||
|
||||
```
|
||||
syscall(TK1_SYSCALL_PRELOAD_STATUS, 0, 0, 0);
|
||||
```
|
||||
|
||||
Returns filesystem status. Non-zero when problems have been detected,
|
||||
so far only that the first copy of the partition table didn't pass
|
||||
checks.
|
||||
|
||||
#### `GET_VIDPID`
|
||||
|
||||
```
|
||||
syscall(TK1_SYSCALL_PRELOAD_STATUS, 0, 0, 0);
|
||||
```
|
||||
|
||||
Returns Vendor and Product ID. Notably the serial number is not
|
||||
returned, so a device app can't identify what particular TKey it is
|
||||
running on.
|
||||
|
||||
## Developing firmware
|
||||
|
||||
Standing in `hw/application_fpga/` you can run `make firmware.elf` to
|
||||
build just the firmware. You don't need all the FPGA development
|
||||
tools. See [the Developer Handbook](https://dev.tillitis.se/tools/)
|
||||
for the tools you need. The easiest is probably to use your OCI image,
|
||||
for the tools you need. The easiest is probably to use our OCI image,
|
||||
`ghcr.io/tillitis/tkey-builder`.
|
||||
|
||||
[Our version of qemu](https://dev.tillitis.se/tools/#qemu-emulator) is
|
||||
|
@ -287,11 +732,93 @@ also useful for debugging the firmware. You can attach GDB, use
|
|||
breakpoints, et cetera.
|
||||
|
||||
There is a special make target for QEMU: `qemu_firmware.elf`, which
|
||||
sets `-DQEMU_CONSOLE`, so you can use plain debug prints using the
|
||||
helper functions in `lib.c` like `htif_puts()` `htif_putinthex()`
|
||||
`htif_hexdump()` and friends. 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.
|
||||
sets `-DQEMU_DEBUG`, so you can debug prints using the `debug_*()`
|
||||
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
|
||||
warning if it doesn't fit. In that case, just use explicit
|
||||
`puts(IO_DEBUG, ...)` or `puts(IO_CDC, ...)` and so on.
|
||||
|
||||
Note that if you use `TKEY_DEBUG` you *must* have something listening
|
||||
on the corresponding HID device. It's usually the last HID device
|
||||
created. On Linux, for instance, this means the last reported hidraw
|
||||
in `dmesg` is the one you should do `cat /dev/hidrawX` on.
|
||||
|
||||
### tkey-libs
|
||||
|
||||
Most of the utility functions that the firmware use lives in
|
||||
`tkey-libs`. The canonical place where you can find tkey-libs is at:
|
||||
|
||||
https://github.com/tillitis/tkey-libs
|
||||
|
||||
but we have vendored it in for firmware use in `../tkey-libs`. See top
|
||||
README for how to update.
|
||||
|
||||
### Preparing the filesystem
|
||||
|
||||
The TKey supports a simple filesystem. This filesystem must be
|
||||
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
|
||||
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`.
|
||||
|
||||
2. Write the filesystem to flash:
|
||||
|
||||
```
|
||||
$ cd ../tools
|
||||
$ ./load_preloaded_app.sh 0 ../fw/testloadapp/testloadapp.bin
|
||||
```
|
||||
|
||||
If you want to use a different pre-loaded app you have to
|
||||
|
||||
1. Check the BLAKE2s digest of the app. You can use `tools/b2s` to
|
||||
compute it.
|
||||
|
||||
2. Update the `allowed_app_digest` in `tk1/mgmt_app.c`.
|
||||
|
||||
3. Create a new `default_partition.bin` using the
|
||||
`tools/tkeyimage`, typically:
|
||||
|
||||
```
|
||||
$ tkeyimage -app0 path/to/your/app.bin -o default_partition.bin
|
||||
```
|
||||
|
||||
4. Flash the filesystem image:
|
||||
|
||||
```
|
||||
$ ./load_preloaded_app.sh 0 path/to/your/app.bin
|
||||
```
|
||||
|
||||
### Test firmware
|
||||
|
||||
|
@ -304,5 +831,9 @@ terminal program to the serial port device, even if it's running in
|
|||
qemu. It waits for you to type a character before starting the tests.
|
||||
|
||||
It needs to be compiled with `-Os` instead of `-O2` in `CFLAGS` in the
|
||||
ordinary `application_fpga/Makefile` to be able to fit in the 6 kByte
|
||||
ROM.
|
||||
ordinary `application_fpga/Makefile` to be able to fit in ROM.
|
||||
|
||||
### Test apps
|
||||
|
||||
There are a couple of test apps, see `../apps`.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Uses ../.clang-format
|
||||
FMTFILES=main.c
|
||||
FMTFILES=*.[ch]
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
clang-format --dry-run --ferror-limit=0 $(FMTFILES)
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
/*
|
||||
* 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>
|
||||
#include <tkey/assert.h>
|
||||
#include <tkey/io.h>
|
||||
#include <tkey/lib.h>
|
||||
#include <tkey/tk1_mem.h>
|
||||
|
||||
#include "../tk1/blake2s/blake2s.h"
|
||||
#include "../tk1/lib.h"
|
||||
#include "../tk1/proto.h"
|
||||
#include "../tk1/types.h"
|
||||
#include "../tk1_mem.h"
|
||||
|
||||
#define USBMODE_PACKET_SIZE 64
|
||||
|
||||
// clang-format off
|
||||
volatile uint32_t *tk1name0 = (volatile uint32_t *)TK1_MMIO_TK1_NAME0;
|
||||
|
@ -15,80 +18,27 @@ volatile uint32_t *tk1name1 = (volatile uint32_t *)TK1_MMIO_TK1_NAME1;
|
|||
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;
|
||||
volatile uint32_t *system_mode_ctrl = (volatile uint32_t *)TK1_MMIO_TK1_SYSTEM_MODE_CTRL;
|
||||
volatile uint8_t *fw_ram = (volatile uint8_t *)TK1_MMIO_FW_RAM_BASE;
|
||||
volatile uint32_t *system_reset = (volatile uint32_t *)TK1_MMIO_TK1_SYSTEM_RESET;
|
||||
volatile uint32_t *timer = (volatile uint32_t *)TK1_MMIO_TIMER_TIMER;
|
||||
volatile uint32_t *timer_prescaler = (volatile uint32_t *)TK1_MMIO_TIMER_PRESCALER;
|
||||
volatile uint32_t *timer_status = (volatile uint32_t *)TK1_MMIO_TIMER_STATUS;
|
||||
volatile uint32_t *timer_ctrl = (volatile uint32_t *)TK1_MMIO_TIMER_CTRL;
|
||||
volatile uint32_t *trng_status = (volatile uint32_t *)TK1_MMIO_TRNG_STATUS;
|
||||
volatile uint32_t *trng_entropy = (volatile uint32_t *)TK1_MMIO_TRNG_ENTROPY;
|
||||
volatile uint32_t *fw_blake2s_addr = (volatile uint32_t *)TK1_MMIO_TK1_BLAKE2S;
|
||||
// clang-format on
|
||||
|
||||
#define UDS_WORDS 8
|
||||
#define UDI_WORDS 2
|
||||
#define CDI_WORDS 8
|
||||
|
||||
void *memcpy(void *dest, const void *src, size_t n)
|
||||
{
|
||||
uint8_t *src_byte = (uint8_t *)src;
|
||||
uint8_t *dest_byte = (uint8_t *)dest;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
dest_byte[i] = src_byte[i];
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
void puts(char *reason)
|
||||
{
|
||||
for (char *c = reason; *c != '\0'; c++) {
|
||||
writebyte(*c);
|
||||
}
|
||||
}
|
||||
|
||||
void putsn(char *p, int n)
|
||||
{
|
||||
for (int i = 0; i < n; i++) {
|
||||
writebyte(p[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void puthex(uint8_t c)
|
||||
{
|
||||
unsigned int upper = (c >> 4) & 0xf;
|
||||
unsigned int lower = c & 0xf;
|
||||
writebyte(upper < 10 ? '0' + upper : 'a' - 10 + upper);
|
||||
writebyte(lower < 10 ? '0' + lower : 'a' - 10 + lower);
|
||||
}
|
||||
|
||||
void puthexn(uint8_t *p, int n)
|
||||
{
|
||||
for (int i = 0; i < n; i++) {
|
||||
puthex(p[i]);
|
||||
puthex(IO_CDC, p[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void hexdump(void *buf, int len)
|
||||
{
|
||||
uint8_t *byte_buf = (uint8_t *)buf;
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
puthex(byte_buf[i]);
|
||||
if (i % 2 == 1) {
|
||||
writebyte(' ');
|
||||
}
|
||||
|
||||
if (i != 1 && i % 16 == 1) {
|
||||
puts("\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
puts("\r\n");
|
||||
}
|
||||
|
||||
void reverseword(uint32_t *wordp)
|
||||
{
|
||||
*wordp = ((*wordp & 0xff000000) >> 24) | ((*wordp & 0x00ff0000) >> 8) |
|
||||
|
@ -124,19 +74,19 @@ int check_fwram_zero_except(unsigned int offset, uint8_t expected_val)
|
|||
if (i == offset) {
|
||||
if (val != expected_val) {
|
||||
failed_now = 1;
|
||||
puts(" wrong value at: ");
|
||||
puts(IO_CDC, " wrong value at: ");
|
||||
}
|
||||
} else {
|
||||
if (val != 0) {
|
||||
failed_now = 1;
|
||||
puts(" not zero at: ");
|
||||
puts(IO_CDC, " not zero at: ");
|
||||
}
|
||||
}
|
||||
if (failed_now) {
|
||||
failed = 1;
|
||||
reverseword(&addr);
|
||||
puthexn((uint8_t *)&addr, 4);
|
||||
puts("\r\n");
|
||||
puts(IO_CDC, "\r\n");
|
||||
}
|
||||
}
|
||||
return failed;
|
||||
|
@ -144,19 +94,17 @@ int check_fwram_zero_except(unsigned int offset, uint8_t expected_val)
|
|||
|
||||
void failmsg(char *s)
|
||||
{
|
||||
puts("FAIL: ");
|
||||
puts(s);
|
||||
puts("\r\n");
|
||||
puts(IO_CDC, "FAIL: ");
|
||||
puts(IO_CDC, s);
|
||||
puts(IO_CDC, "\r\n");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// Function pointer to blake2s()
|
||||
volatile int (*fw_blake2s)(void *, unsigned long, const void *,
|
||||
unsigned long, const void *, unsigned long,
|
||||
blake2s_ctx *);
|
||||
uint8_t in = 0;
|
||||
uint8_t available = 0;
|
||||
enum ioend endpoint = IO_NONE;
|
||||
|
||||
uint8_t in;
|
||||
// Hard coded test UDS in ../../data/uds.hex
|
||||
// clang-format off
|
||||
uint32_t uds_test[8] = {
|
||||
|
@ -172,19 +120,27 @@ int main(void)
|
|||
// clang-format on
|
||||
|
||||
// Wait for terminal program and a character to be typed
|
||||
in = readbyte();
|
||||
if (readselect(IO_CDC, &endpoint, &available) < 0) {
|
||||
// readselect failed! I/O broken? Just redblink.
|
||||
assert(1 == 2);
|
||||
}
|
||||
|
||||
puts("\r\nI'm testfw on:");
|
||||
if (read(IO_CDC, &in, 1, 1) < 0) {
|
||||
// read failed! I/O broken? Just redblink.
|
||||
assert(1 == 2);
|
||||
}
|
||||
|
||||
puts(IO_CDC, "\r\nI'm testfw on:");
|
||||
// Output the TK1 core's NAME0 and NAME1
|
||||
uint32_t name;
|
||||
wordcpy_s(&name, 1, (void *)tk1name0, 1);
|
||||
reverseword(&name);
|
||||
putsn((char *)&name, 4);
|
||||
puts(" ");
|
||||
write(IO_CDC, (const uint8_t *)&name, 4);
|
||||
puts(IO_CDC, " ");
|
||||
wordcpy_s(&name, 1, (void *)tk1name1, 1);
|
||||
reverseword(&name);
|
||||
putsn((char *)&name, 4);
|
||||
puts("\r\n");
|
||||
write(IO_CDC, (const uint8_t *)&name, 4);
|
||||
puts(IO_CDC, "\r\n");
|
||||
|
||||
uint32_t zeros[8];
|
||||
memset(zeros, 0, 8 * 4);
|
||||
|
@ -200,11 +156,11 @@ int main(void)
|
|||
anyfailed = 1;
|
||||
}
|
||||
|
||||
puts("\r\nUDS: ");
|
||||
puts(IO_CDC, "\r\nUDS: ");
|
||||
for (int i = 0; i < UDS_WORDS * 4; i++) {
|
||||
puthex(((uint8_t *)uds_local)[i]);
|
||||
puthex(IO_CDC, ((uint8_t *)uds_local)[i]);
|
||||
}
|
||||
puts("\r\n");
|
||||
puts(IO_CDC, "\r\n");
|
||||
if (!memeq(uds_local, uds_test, UDS_WORDS * 4)) {
|
||||
failmsg("UDS not equal to test UDS");
|
||||
anyfailed = 1;
|
||||
|
@ -247,7 +203,7 @@ int main(void)
|
|||
}
|
||||
|
||||
// Test FW_RAM.
|
||||
puts("\r\nTesting FW_RAM (takes 15s on hw)...\r\n");
|
||||
puts(IO_CDC, "\r\nTesting FW_RAM (takes 50s on hw)...\r\n");
|
||||
for (unsigned int i = 0; i < TK1_MMIO_FW_RAM_SIZE; i++) {
|
||||
zero_fwram();
|
||||
*(volatile uint8_t *)(TK1_MMIO_FW_RAM_BASE + i) = 0x42;
|
||||
|
@ -257,60 +213,7 @@ int main(void)
|
|||
}
|
||||
}
|
||||
|
||||
uint32_t sw = *system_mode_ctrl;
|
||||
if (sw != 0) {
|
||||
failmsg("system_mode_ctrl is not 0 in fw mode");
|
||||
anyfailed = 1;
|
||||
}
|
||||
|
||||
// Store function pointer to blake2s() so it's reachable from app
|
||||
*fw_blake2s_addr = (uint32_t)blake2s;
|
||||
|
||||
// Turn on application mode.
|
||||
// -------------------------
|
||||
|
||||
*system_mode_ctrl = 1;
|
||||
|
||||
sw = *system_mode_ctrl;
|
||||
if (sw != 0xffffffff) {
|
||||
failmsg("system_mode_ctrl is not 0xffffffff in app mode");
|
||||
anyfailed = 1;
|
||||
}
|
||||
|
||||
// Should NOT be able to read from UDS in app-mode.
|
||||
wordcpy_s(uds_local, UDS_WORDS, (void *)uds, UDS_WORDS);
|
||||
if (!memeq(uds_local, zeros, UDS_WORDS * 4)) {
|
||||
failmsg("Read from UDS in app-mode");
|
||||
anyfailed = 1;
|
||||
}
|
||||
|
||||
// Should NOT be able to read from UDI in app-mode.
|
||||
wordcpy_s(udi_local, UDI_WORDS, (void *)udi, UDI_WORDS);
|
||||
if (!memeq(udi_local, zeros, UDI_WORDS * 4)) {
|
||||
failmsg("Read from UDI in app-mode");
|
||||
anyfailed = 1;
|
||||
}
|
||||
|
||||
uint32_t cdi_local[CDI_WORDS];
|
||||
uint32_t cdi_local2[CDI_WORDS];
|
||||
wordcpy_s(cdi_local, CDI_WORDS, (void *)cdi, CDI_WORDS);
|
||||
|
||||
// Write to CDI should NOT have any effect in app mode.
|
||||
wordcpy_s((void *)cdi, CDI_WORDS, zeros, CDI_WORDS);
|
||||
wordcpy_s(cdi_local2, CDI_WORDS, (void *)cdi, CDI_WORDS);
|
||||
if (!memeq(cdi_local, cdi_local2, CDI_WORDS * 4)) {
|
||||
failmsg("Write to CDI in app-mode");
|
||||
anyfailed = 1;
|
||||
}
|
||||
|
||||
// Test FW_RAM.
|
||||
*fw_ram = 0x21;
|
||||
if (*fw_ram == 0x21) {
|
||||
failmsg("Write and read FW RAM in app-mode");
|
||||
anyfailed = 1;
|
||||
}
|
||||
|
||||
puts("\r\nTesting timer... 3");
|
||||
puts(IO_CDC, "\r\nTesting timer... 3");
|
||||
// Matching clock at 24 MHz, giving us timer in seconds
|
||||
*timer_prescaler = 24 * 1000000;
|
||||
|
||||
|
@ -321,7 +224,7 @@ int main(void)
|
|||
while (*timer_status & (1 << TK1_MMIO_TIMER_STATUS_RUNNING_BIT)) {
|
||||
}
|
||||
// Now timer has expired and is ready to run again
|
||||
puts(" 2");
|
||||
puts(IO_CDC, " 2");
|
||||
|
||||
// Test to interrupt a timer - and reads from timer register
|
||||
// Starting 10s timer and interrupting it in 3s...
|
||||
|
@ -334,7 +237,7 @@ int main(void)
|
|||
|
||||
// Stop the timer
|
||||
*timer_ctrl = (1 << TK1_MMIO_TIMER_CTRL_STOP_BIT);
|
||||
puts(" 1. done.\r\n");
|
||||
puts(IO_CDC, " 1. done.\r\n");
|
||||
|
||||
if (*timer_status & (1 << TK1_MMIO_TIMER_STATUS_RUNNING_BIT)) {
|
||||
failmsg("Timer didn't stop");
|
||||
|
@ -346,41 +249,16 @@ int main(void)
|
|||
anyfailed = 1;
|
||||
}
|
||||
|
||||
// Testing the blake2s MMIO in app mode
|
||||
|
||||
fw_blake2s = (volatile int (*)(void *, unsigned long, const void *,
|
||||
unsigned long, const void *,
|
||||
unsigned long, blake2s_ctx *)) *
|
||||
fw_blake2s_addr;
|
||||
|
||||
char msg[17] = "dldlkjsdkljdslsdj";
|
||||
uint32_t digest0[8];
|
||||
uint32_t digest1[8];
|
||||
blake2s_ctx b2s_ctx;
|
||||
|
||||
blake2s(&digest0[0], 32, NULL, 0, &msg, 17, &b2s_ctx);
|
||||
fw_blake2s(&digest1[0], 32, NULL, 0, &msg, 17, &b2s_ctx);
|
||||
|
||||
puts("\r\ndigest #0: \r\n");
|
||||
hexdump((uint8_t *)digest0, 32);
|
||||
|
||||
puts("digest #1: \r\n");
|
||||
hexdump((uint8_t *)digest1, 32);
|
||||
|
||||
if (!memeq(digest0, digest1, 32)) {
|
||||
failmsg("Digests not the same");
|
||||
anyfailed = 1;
|
||||
}
|
||||
|
||||
// Check and display test results.
|
||||
puts("\r\n--> ");
|
||||
puts(IO_CDC, "\r\n--> ");
|
||||
if (anyfailed) {
|
||||
puts("Some test FAILED!\r\n");
|
||||
puts(IO_CDC, "Some test FAILED!\r\n");
|
||||
} else {
|
||||
puts("All tests passed.\r\n");
|
||||
puts(IO_CDC, "All tests passed.\r\n");
|
||||
}
|
||||
|
||||
puts("\r\nHere are 256 bytes from the TRNG:\r\n");
|
||||
puts(IO_CDC, "\r\nHere are 256 bytes from the TRNG:\r\n");
|
||||
|
||||
for (int j = 0; j < 8; j++) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
while ((*trng_status &
|
||||
|
@ -388,15 +266,28 @@ int main(void)
|
|||
}
|
||||
uint32_t rnd = *trng_entropy;
|
||||
puthexn((uint8_t *)&rnd, 4);
|
||||
puts(" ");
|
||||
puts(IO_CDC, " ");
|
||||
}
|
||||
puts("\r\n");
|
||||
puts(IO_CDC, "\r\n");
|
||||
}
|
||||
puts("\r\n");
|
||||
puts(IO_CDC, "\r\n");
|
||||
|
||||
puts("Now echoing what you type...\r\n");
|
||||
puts(IO_CDC, "Now echoing what you type...Type + to reset device\r\n");
|
||||
for (;;) {
|
||||
in = readbyte(); // blocks
|
||||
writebyte(in);
|
||||
if (readselect(IO_CDC, &endpoint, &available) < 0) {
|
||||
// readselect failed! I/O broken? Just redblink.
|
||||
assert(1 == 2);
|
||||
}
|
||||
|
||||
if (read(IO_CDC, &in, 1, 1) < 0) {
|
||||
// read failed! I/O broken? Just redblink.
|
||||
assert(1 == 2);
|
||||
}
|
||||
|
||||
if (in == '+') {
|
||||
*system_reset = 1;
|
||||
}
|
||||
|
||||
write(IO_CDC, &in, 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2022, 2023 - Tillitis AB
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
// Copyright (C) 2022, 2023 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
.section ".text.init"
|
||||
.globl _start
|
||||
|
@ -38,7 +36,7 @@ _start:
|
|||
li x30,0
|
||||
li x31,0
|
||||
|
||||
/* Clear all RAM */
|
||||
// Clear all RAM
|
||||
li a0, 0x40000000 // TK1_RAM_BASE
|
||||
li a1, 0x40020000 // TK1_RAM_BASE + TK1_RAM_SIZE
|
||||
clear:
|
||||
|
@ -46,9 +44,9 @@ clear:
|
|||
addi a0, a0, 4
|
||||
blt a0, a1, clear
|
||||
|
||||
/*
|
||||
* For testfw we init stack at top of RAM
|
||||
*/
|
||||
// NOTE WELL
|
||||
// For testfw we init stack at top of RAM
|
||||
//
|
||||
li sp, 0x40020000 // TK1_RAM_BASE + TK1_RAM_SIZE
|
||||
|
||||
call main
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Uses ../.clang-format
|
||||
FMTFILES=main.c lib.h lib.c proto.h proto.c types.h assert.c assert.h led.c led.h
|
||||
FMTFILES=*.[ch]
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
clang-format --dry-run --ferror-limit=0 $(FMTFILES)
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2022, 2023 - Tillitis AB
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "assert.h"
|
||||
#include "lib.h"
|
||||
|
||||
void assert_fail(const char *assertion, const char *file, unsigned int line,
|
||||
const char *function)
|
||||
{
|
||||
htif_puts("assert: ");
|
||||
htif_puts(assertion);
|
||||
htif_puts(" ");
|
||||
htif_puts(file);
|
||||
htif_puts(":");
|
||||
htif_putinthex(line);
|
||||
htif_puts(" ");
|
||||
htif_puts(function);
|
||||
htif_lf();
|
||||
|
||||
#ifndef S_SPLINT_S
|
||||
// Force illegal instruction to halt CPU
|
||||
asm volatile("unimp");
|
||||
#endif
|
||||
|
||||
// Not reached
|
||||
__builtin_unreachable();
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2022, 2023 - Tillitis AB
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef ASSERT_H
|
||||
#define ASSERT_H
|
||||
|
||||
#define assert(expr) \
|
||||
((expr) ? (void)(0) : assert_fail(#expr, __FILE__, __LINE__, __func__))
|
||||
|
||||
void assert_fail(const char *assertion, const char *file, unsigned int line,
|
||||
const char *function);
|
||||
|
||||
#endif
|
77
hw/application_fpga/fw/tk1/auth_app.c
Normal file
77
hw/application_fpga/fw/tk1/auth_app.c
Normal file
|
@ -0,0 +1,77 @@
|
|||
// Copyright (C) 2024 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <tkey/assert.h>
|
||||
#include <tkey/lib.h>
|
||||
#include <tkey/tk1_mem.h>
|
||||
|
||||
#include "auth_app.h"
|
||||
#include "blake2s/blake2s.h"
|
||||
#include "partition_table.h"
|
||||
#include "rng.h"
|
||||
|
||||
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
|
||||
static void calculate_auth_digest(uint8_t *nonce, uint8_t *auth_digest)
|
||||
{
|
||||
assert(nonce != NULL);
|
||||
assert(auth_digest != NULL);
|
||||
|
||||
blake2s_ctx ctx = {0};
|
||||
|
||||
// Generate a 16 byte authentication digest
|
||||
int blake2err = blake2s_init(&ctx, 16, NULL, 0);
|
||||
assert(blake2err == 0);
|
||||
blake2s_update(&ctx, (const void *)cdi, 32);
|
||||
blake2s_update(&ctx, nonce, 16);
|
||||
blake2s_final(&ctx, auth_digest);
|
||||
}
|
||||
|
||||
// Generates a 16 byte nonce
|
||||
static void generate_nonce(uint32_t *nonce)
|
||||
{
|
||||
assert(nonce != NULL);
|
||||
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
nonce[i] = rng_get_word();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
uint8_t nonce[16] = {0};
|
||||
uint8_t auth_digest[16] = {0};
|
||||
|
||||
generate_nonce((uint32_t *)nonce);
|
||||
|
||||
calculate_auth_digest(nonce, auth_digest);
|
||||
|
||||
memcpy_s(auth_table->authentication_digest, 16, auth_digest, 16);
|
||||
memcpy_s(auth_table->nonce, 16, nonce, 16);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool auth_app_authenticate(struct auth_metadata *auth_table)
|
||||
{
|
||||
assert(auth_table != NULL);
|
||||
|
||||
uint8_t auth_digest[16] = {0};
|
||||
|
||||
calculate_auth_digest(auth_table->nonce, auth_digest);
|
||||
|
||||
if (memeq(auth_digest, auth_table->authentication_digest, 16)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
14
hw/application_fpga/fw/tk1/auth_app.h
Normal file
14
hw/application_fpga/fw/tk1/auth_app.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
// Copyright (C) 2024 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#ifndef AUTH_APP_H
|
||||
#define AUTH_APP_H
|
||||
|
||||
#include "partition_table.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
void auth_app_create(struct auth_metadata *auth_table);
|
||||
bool auth_app_authenticate(struct auth_metadata *auth_table);
|
||||
|
||||
#endif
|
|
@ -1,11 +0,0 @@
|
|||
# blake2s
|
||||
|
||||
A Blake2s implementation taken from Joachim Strömbergson's
|
||||
|
||||
https://github.com/secworks/blake2s
|
||||
|
||||
Specifically from
|
||||
|
||||
https://github.com/secworks/blake2s/tree/master/src/model
|
||||
|
||||
Minor local changes for build purposes.
|
|
@ -6,9 +6,14 @@
|
|||
OUTPUT_ARCH("riscv")
|
||||
ENTRY(_start)
|
||||
|
||||
/* Define stack size */
|
||||
STACK_SIZE = 3000;
|
||||
|
||||
MEMORY
|
||||
{
|
||||
ROM (rx) : ORIGIN = 0x00000000, LENGTH = 0x20000 /* 128 KB */
|
||||
ROM (rx) : ORIGIN = 0x00000000, LENGTH = 0x2000 /* 8 KB */
|
||||
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 */
|
||||
}
|
||||
|
||||
|
@ -28,31 +33,38 @@ SECTIONS
|
|||
.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) /* .rodata sections (constants, strings, etc.) */
|
||||
*(.srodata*) /* .rodata* sections (constants, strings, etc.) */
|
||||
*(.srodata) /* .srodata sections (constants, strings, etc.) */
|
||||
*(.srodata*) /* .srodata* sections (constants, strings, etc.) */
|
||||
. = ALIGN(4);
|
||||
_etext = .;
|
||||
_sidata = _etext;
|
||||
} >ROM
|
||||
|
||||
/* XXX We don't allow any data or BSS - but they need be defined or linking will fail */
|
||||
.stack (NOLOAD) :
|
||||
{
|
||||
. = ALIGN(16);
|
||||
_sstack = .;
|
||||
. += STACK_SIZE;
|
||||
. = ALIGN(16);
|
||||
_estack = .;
|
||||
} >FWRAM
|
||||
|
||||
.data : AT (_etext)
|
||||
.data :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_sdata = .;
|
||||
. = ALIGN(4);
|
||||
*(.data) /* .data sections */
|
||||
*(.data*) /* .data* sections */
|
||||
*(.sdata) /* .sdata sections */
|
||||
*(.sdata*) /* .sdata* sections */
|
||||
. = ALIGN(4);
|
||||
_edata = .;
|
||||
} >ROM
|
||||
} >FWRAM AT>ROM
|
||||
_sidata = LOADADDR(.data);
|
||||
|
||||
/* Uninitialized data section */
|
||||
.bss :
|
||||
|
@ -64,8 +76,12 @@ SECTIONS
|
|||
*(.sbss)
|
||||
*(.sbss*)
|
||||
*(COMMON)
|
||||
|
||||
. = ALIGN(4);
|
||||
_ebss = .;
|
||||
} >ROM
|
||||
} >FWRAM
|
||||
}
|
||||
|
||||
_sfwram = ORIGIN(FWRAM);
|
||||
_efwram = ORIGIN(FWRAM) + LENGTH(FWRAM);
|
||||
_sresetinfo = ORIGIN(RESETINFO);
|
||||
_eresetinfo = ORIGIN(RESETINFO) + LENGTH(RESETINFO);
|
||||
|
|
229
hw/application_fpga/fw/tk1/flash.c
Normal file
229
hw/application_fpga/fw/tk1/flash.c
Normal file
|
@ -0,0 +1,229 @@
|
|||
// Copyright (C) 2024 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <tkey/assert.h>
|
||||
#include <tkey/tk1_mem.h>
|
||||
|
||||
#include "flash.h"
|
||||
#include "spi.h"
|
||||
|
||||
#define PAGE_SIZE 256
|
||||
|
||||
static bool flash_is_busy(void);
|
||||
static void flash_wait_busy(void);
|
||||
static void flash_write_enable(void);
|
||||
|
||||
static bool flash_is_busy(void)
|
||||
{
|
||||
uint8_t tx_buf = READ_STATUS_REG_1;
|
||||
uint8_t rx_buf = {0x00};
|
||||
|
||||
assert(spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, &rx_buf,
|
||||
sizeof(rx_buf)) == 0);
|
||||
|
||||
if (rx_buf & (1 << STATUS_REG_BUSY_BIT)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Blocking until !busy
|
||||
static void flash_wait_busy(void)
|
||||
{
|
||||
while (flash_is_busy())
|
||||
;
|
||||
}
|
||||
|
||||
static void flash_write_enable(void)
|
||||
{
|
||||
uint8_t tx_buf = WRITE_ENABLE;
|
||||
|
||||
assert(spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0) == 0);
|
||||
}
|
||||
|
||||
void flash_write_disable(void)
|
||||
{
|
||||
uint8_t tx_buf = WRITE_DISABLE;
|
||||
|
||||
assert(spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0) == 0);
|
||||
}
|
||||
|
||||
void flash_sector_erase(uint32_t address)
|
||||
{
|
||||
uint8_t tx_buf[4] = {0x00};
|
||||
tx_buf[0] = SECTOR_ERASE;
|
||||
tx_buf[1] = (address >> ADDR_BYTE_3_BIT) & 0xFF;
|
||||
tx_buf[2] = (address >> ADDR_BYTE_2_BIT) & 0xFF;
|
||||
/* tx_buf[3] is within a sector, and hence does not make a difference */
|
||||
|
||||
flash_write_enable();
|
||||
assert(spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0) == 0);
|
||||
flash_wait_busy();
|
||||
}
|
||||
|
||||
void flash_block_32_erase(uint32_t address)
|
||||
{
|
||||
uint8_t tx_buf[4] = {0x00};
|
||||
tx_buf[0] = BLOCK_ERASE_32K;
|
||||
tx_buf[1] = (address >> ADDR_BYTE_3_BIT) & 0xFF;
|
||||
tx_buf[2] = (address >> ADDR_BYTE_2_BIT) & 0xFF;
|
||||
tx_buf[3] = (address >> ADDR_BYTE_1_BIT) & 0xFF;
|
||||
|
||||
flash_write_enable();
|
||||
assert(spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0) == 0);
|
||||
flash_wait_busy();
|
||||
}
|
||||
|
||||
// 64 KiB block erase, only cares about address bits 16 and above.
|
||||
void flash_block_64_erase(uint32_t address)
|
||||
{
|
||||
uint8_t tx_buf[4] = {0x00};
|
||||
tx_buf[0] = BLOCK_ERASE_64K;
|
||||
tx_buf[1] = (address >> ADDR_BYTE_3_BIT) & 0xFF;
|
||||
/* tx_buf[2] and tx_buf[3] is within a block, and hence does not make a
|
||||
* difference */
|
||||
|
||||
flash_write_enable();
|
||||
assert(spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0) == 0);
|
||||
flash_wait_busy();
|
||||
}
|
||||
|
||||
void flash_release_powerdown(void)
|
||||
{
|
||||
uint8_t tx_buf[4] = {0x00};
|
||||
tx_buf[0] = RELEASE_POWER_DOWN;
|
||||
|
||||
assert(spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0) == 0);
|
||||
}
|
||||
|
||||
void flash_powerdown(void)
|
||||
{
|
||||
uint8_t tx_buf = POWER_DOWN;
|
||||
|
||||
assert(spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0) == 0);
|
||||
}
|
||||
|
||||
void flash_read_manufacturer_device_id(uint8_t *device_id)
|
||||
{
|
||||
assert(device_id != NULL);
|
||||
|
||||
uint8_t tx_buf[4] = {0x00};
|
||||
tx_buf[0] = READ_MANUFACTURER_ID;
|
||||
|
||||
assert(spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, device_id, 2) ==
|
||||
0);
|
||||
}
|
||||
|
||||
void flash_read_jedec_id(uint8_t *jedec_id)
|
||||
{
|
||||
assert(jedec_id != NULL);
|
||||
|
||||
uint8_t tx_buf = READ_JEDEC_ID;
|
||||
|
||||
assert(spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, jedec_id, 3) ==
|
||||
0);
|
||||
}
|
||||
|
||||
void flash_read_unique_id(uint8_t *unique_id)
|
||||
{
|
||||
assert(unique_id != NULL);
|
||||
|
||||
uint8_t tx_buf[5] = {0x00};
|
||||
tx_buf[0] = READ_UNIQUE_ID;
|
||||
|
||||
assert(spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, unique_id, 8) ==
|
||||
0);
|
||||
}
|
||||
|
||||
void flash_read_status(uint8_t *status_reg)
|
||||
{
|
||||
assert(status_reg != NULL);
|
||||
|
||||
uint8_t tx_buf = READ_STATUS_REG_1;
|
||||
|
||||
assert(spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, status_reg, 1) ==
|
||||
0);
|
||||
|
||||
tx_buf = READ_STATUS_REG_2;
|
||||
assert(spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, status_reg + 1,
|
||||
1) == 0);
|
||||
}
|
||||
|
||||
int flash_read_data(uint32_t address, uint8_t *dest_buf, size_t size)
|
||||
{
|
||||
if (dest_buf == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t tx_buf[4] = {0x00};
|
||||
tx_buf[0] = READ_DATA;
|
||||
tx_buf[1] = (address >> ADDR_BYTE_3_BIT) & 0xFF;
|
||||
tx_buf[2] = (address >> ADDR_BYTE_2_BIT) & 0xFF;
|
||||
tx_buf[3] = (address >> ADDR_BYTE_1_BIT) & 0xFF;
|
||||
|
||||
return spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, dest_buf, size);
|
||||
}
|
||||
|
||||
// Only handles writes where the least significant byte of the start address is
|
||||
// zero.
|
||||
int flash_write_data(uint32_t address, uint8_t *data, size_t size)
|
||||
{
|
||||
if (data == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (size <= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (address % 256 != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t left = 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] */
|
||||
(address >> ADDR_BYTE_2_BIT) & 0xFF, /* tx_buf[2] */
|
||||
0x00, /* tx_buf[3] */
|
||||
};
|
||||
|
||||
while (left > 0) {
|
||||
if (left >= PAGE_SIZE) {
|
||||
n_bytes = PAGE_SIZE;
|
||||
} else {
|
||||
n_bytes = left;
|
||||
}
|
||||
|
||||
flash_write_enable();
|
||||
|
||||
if (spi_transfer(tx_buf, sizeof(tx_buf), p_data, n_bytes, NULL,
|
||||
0) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
left -= n_bytes;
|
||||
p_data += n_bytes;
|
||||
|
||||
address += n_bytes;
|
||||
tx_buf[1] = (address >> ADDR_BYTE_3_BIT) & 0xFF;
|
||||
tx_buf[2] = (address >> ADDR_BYTE_2_BIT) & 0xFF;
|
||||
|
||||
flash_wait_busy();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
55
hw/application_fpga/fw/tk1/flash.h
Normal file
55
hw/application_fpga/fw/tk1/flash.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
// Copyright (C) 2024 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#ifndef TKEY_FLASH_H
|
||||
#define TKEY_FLASH_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define WRITE_ENABLE 0x06
|
||||
#define WRITE_DISABLE 0x04
|
||||
|
||||
#define READ_STATUS_REG_1 0x05
|
||||
#define READ_STATUS_REG_2 0x35
|
||||
#define WRITE_STATUS_REG 0x01
|
||||
|
||||
#define PAGE_PROGRAM 0x02
|
||||
#define SECTOR_ERASE 0x20
|
||||
#define BLOCK_ERASE_32K 0x52
|
||||
#define BLOCK_ERASE_64K 0xD8
|
||||
#define CHIP_ERASE 0xC7
|
||||
|
||||
#define POWER_DOWN 0xB9
|
||||
#define READ_DATA 0x03
|
||||
#define RELEASE_POWER_DOWN 0xAB
|
||||
|
||||
#define READ_MANUFACTURER_ID 0x90
|
||||
#define READ_JEDEC_ID 0x9F
|
||||
#define READ_UNIQUE_ID 0x4B
|
||||
|
||||
#define ENABLE_RESET 0x66
|
||||
#define RESET 0x99
|
||||
|
||||
#define ADDR_BYTE_3_BIT 16
|
||||
#define ADDR_BYTE_2_BIT 8
|
||||
#define ADDR_BYTE_1_BIT 0
|
||||
|
||||
#define STATUS_REG_BUSY_BIT 0
|
||||
#define STATUS_REG_WEL_BIT 1
|
||||
|
||||
void flash_write_disable(void);
|
||||
void flash_sector_erase(uint32_t address);
|
||||
void flash_block_32_erase(uint32_t address);
|
||||
void flash_block_64_erase(uint32_t address);
|
||||
void flash_release_powerdown(void);
|
||||
void flash_powerdown(void);
|
||||
void flash_read_manufacturer_device_id(uint8_t *device_id);
|
||||
void flash_read_jedec_id(uint8_t *jedec_id);
|
||||
void flash_read_unique_id(uint8_t *unique_id);
|
||||
void flash_read_status(uint8_t *status_reg);
|
||||
int flash_read_data(uint32_t address, uint8_t *dest_buf, size_t size);
|
||||
int flash_write_data(uint32_t address, uint8_t *data, size_t size);
|
||||
|
||||
#endif
|
|
@ -1,15 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2022, 2023 - Tillitis AB
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "led.h"
|
||||
#include "../tk1_mem.h"
|
||||
#include "types.h"
|
||||
|
||||
static volatile uint32_t *led = (volatile uint32_t *)TK1_MMIO_TK1_LED;
|
||||
|
||||
void set_led(uint32_t led_value)
|
||||
{
|
||||
*led = led_value;
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2022, 2023 - Tillitis AB
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef LED_H
|
||||
#define LED_H
|
||||
|
||||
#include "../tk1_mem.h"
|
||||
#include "types.h"
|
||||
|
||||
// clang-format off
|
||||
#define LED_BLACK 0
|
||||
#define LED_RED (1 << TK1_MMIO_TK1_LED_R_BIT)
|
||||
#define LED_GREEN (1 << TK1_MMIO_TK1_LED_G_BIT)
|
||||
#define LED_BLUE (1 << TK1_MMIO_TK1_LED_B_BIT)
|
||||
#define LED_WHITE (LED_RED | LED_GREEN | LED_BLUE)
|
||||
// clang-format on
|
||||
|
||||
void set_led(uint32_t led_value);
|
||||
#endif
|
|
@ -1,164 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2024 - Tillitis AB
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "lib.h"
|
||||
#include "assert.h"
|
||||
#include "types.h"
|
||||
|
||||
#ifdef QEMU_CONSOLE
|
||||
struct {
|
||||
uint32_t arr[2];
|
||||
} static volatile tohost __attribute__((section(".htif")));
|
||||
struct {
|
||||
uint32_t arr[2];
|
||||
} /*@unused@*/ static volatile fromhost __attribute__((section(".htif")));
|
||||
|
||||
static void htif_send(uint8_t dev, uint8_t cmd, int64_t data)
|
||||
{
|
||||
/* endian neutral encoding with ordered 32-bit writes */
|
||||
union {
|
||||
uint32_t arr[2];
|
||||
uint64_t val;
|
||||
} encode = {.val = (uint64_t)dev << 56 | (uint64_t)cmd << 48 | data};
|
||||
tohost.arr[0] = encode.arr[0];
|
||||
tohost.arr[1] = encode.arr[1];
|
||||
}
|
||||
|
||||
static void htif_set_tohost(uint8_t dev, uint8_t cmd, int64_t data)
|
||||
{
|
||||
/* send data with specified device and command */
|
||||
while (tohost.arr[0]) {
|
||||
#ifndef S_SPLINT_S
|
||||
asm volatile("" : : "r"(fromhost.arr[0]));
|
||||
asm volatile("" : : "r"(fromhost.arr[1]));
|
||||
#endif
|
||||
}
|
||||
htif_send(dev, cmd, data);
|
||||
}
|
||||
|
||||
static void htif_putchar(char ch)
|
||||
{
|
||||
htif_set_tohost((uint8_t)1, (uint8_t)1, (int64_t)ch & 0xff);
|
||||
}
|
||||
|
||||
void htif_puts(const char *s)
|
||||
{
|
||||
while (*s != '\0')
|
||||
htif_putchar(*s++);
|
||||
}
|
||||
|
||||
void htif_hexdump(void *buf, int len)
|
||||
{
|
||||
uint8_t *byte_buf = (uint8_t *)buf;
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
htif_puthex(byte_buf[i]);
|
||||
if (i % 2 == 1) {
|
||||
(void)htif_putchar(' ');
|
||||
}
|
||||
|
||||
if (i != 1 && i % 16 == 1) {
|
||||
htif_lf();
|
||||
}
|
||||
}
|
||||
|
||||
htif_lf();
|
||||
}
|
||||
|
||||
void htif_putc(char ch)
|
||||
{
|
||||
htif_putchar(ch);
|
||||
}
|
||||
|
||||
void htif_lf(void)
|
||||
{
|
||||
htif_putchar('\n');
|
||||
}
|
||||
|
||||
void htif_puthex(uint8_t c)
|
||||
{
|
||||
unsigned int upper = (c >> 4) & 0xf;
|
||||
unsigned int lower = c & 0xf;
|
||||
|
||||
htif_putchar(upper < 10 ? '0' + upper : 'a' - 10 + upper);
|
||||
htif_putchar(lower < 10 ? '0' + lower : 'a' - 10 + lower);
|
||||
}
|
||||
|
||||
void htif_putinthex(const uint32_t n)
|
||||
{
|
||||
uint8_t *buf = (uint8_t *)&n;
|
||||
|
||||
htif_puts("0x");
|
||||
for (int i = 3; i > -1; i--) {
|
||||
htif_puthex(buf[i]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void *memset(void *dest, int c, unsigned n)
|
||||
{
|
||||
uint8_t *s = dest;
|
||||
|
||||
for (; n; n--, s++)
|
||||
*s = (uint8_t)c;
|
||||
|
||||
/*@ -temptrans @*/
|
||||
return dest;
|
||||
}
|
||||
|
||||
void memcpy_s(void *dest, size_t destsize, const void *src, size_t n)
|
||||
{
|
||||
assert(dest != NULL);
|
||||
assert(src != NULL);
|
||||
assert(destsize >= n);
|
||||
|
||||
uint8_t *src_byte = (uint8_t *)src;
|
||||
uint8_t *dest_byte = (uint8_t *)dest;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
/*@ -nullderef @*/
|
||||
/* splint complains that dest_byte and src_byte can be
|
||||
* NULL, but it seems it doesn't understand assert.
|
||||
* See above.
|
||||
*/
|
||||
dest_byte[i] = src_byte[i];
|
||||
}
|
||||
}
|
||||
|
||||
void wordcpy_s(void *dest, size_t destsize, const void *src, size_t n)
|
||||
{
|
||||
assert(dest != NULL);
|
||||
assert(src != NULL);
|
||||
assert(destsize >= n);
|
||||
|
||||
uint32_t *src_word = (uint32_t *)src;
|
||||
uint32_t *dest_word = (uint32_t *)dest;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dest_word[i] = src_word[i];
|
||||
}
|
||||
}
|
||||
|
||||
int memeq(void *dest, const void *src, size_t n)
|
||||
{
|
||||
uint8_t *src_byte = (uint8_t *)src;
|
||||
uint8_t *dest_byte = (uint8_t *)dest;
|
||||
int res = -1;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
if (dest_byte[i] != src_byte[i]) {
|
||||
res = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void secure_wipe(void *v, size_t n)
|
||||
{
|
||||
volatile uint8_t *p = (volatile uint8_t *)v;
|
||||
while (n--)
|
||||
*p++ = 0;
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2024 - Tillitis AB
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef LIB_H
|
||||
#define LIB_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#ifdef QEMU_CONSOLE
|
||||
void htif_putc(char ch);
|
||||
void htif_lf(void);
|
||||
void htif_puthex(uint8_t c);
|
||||
void htif_putinthex(const uint32_t n);
|
||||
void htif_puts(const char *s);
|
||||
void htif_hexdump(void *buf, int len);
|
||||
#else
|
||||
#define htif_putc(ch)
|
||||
#define htif_lf(void)
|
||||
#define htif_puthex(c)
|
||||
#define htif_putinthex(n)
|
||||
#define htif_puts(s)
|
||||
#define htif_hexdump(buf, len)
|
||||
#endif /* QEMU_CONSOLE */
|
||||
|
||||
void *memset(void *dest, int c, unsigned n);
|
||||
void memcpy_s(void *dest, size_t destsize, const void *src, size_t n);
|
||||
void wordcpy_s(void *dest, size_t destsize, const void *src, size_t n);
|
||||
int memeq(void *dest, const void *src, size_t n);
|
||||
void secure_wipe(void *v, size_t n);
|
||||
#endif
|
|
@ -1,19 +1,26 @@
|
|||
/*
|
||||
* 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 "../tk1_mem.h"
|
||||
#include "assert.h"
|
||||
#include "blake2s/blake2s.h"
|
||||
#include "lib.h"
|
||||
#include <blake2s/blake2s.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.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 "mgmt_app.h"
|
||||
#include "partition_table.h"
|
||||
#include "preload_app.h"
|
||||
#include "proto.h"
|
||||
#include "reset.h"
|
||||
#include "state.h"
|
||||
#include "types.h"
|
||||
#include "syscall_enable.h"
|
||||
|
||||
// clang-format off
|
||||
static volatile uint32_t *uds = (volatile uint32_t *)TK1_MMIO_UDS_FIRST;
|
||||
static volatile uint32_t *system_mode_ctrl = (volatile uint32_t *)TK1_MMIO_TK1_SYSTEM_MODE_CTRL;
|
||||
static volatile uint32_t *name0 = (volatile uint32_t *)TK1_MMIO_TK1_NAME0;
|
||||
static volatile uint32_t *name1 = (volatile uint32_t *)TK1_MMIO_TK1_NAME1;
|
||||
static volatile uint32_t *ver = (volatile uint32_t *)TK1_MMIO_TK1_VERSION;
|
||||
|
@ -21,7 +28,6 @@ static volatile uint32_t *udi = (volatile uint32_t *)TK1_MMIO_TK1_U
|
|||
static volatile uint32_t *cdi = (volatile uint32_t *)TK1_MMIO_TK1_CDI_FIRST;
|
||||
static volatile uint32_t *app_addr = (volatile uint32_t *)TK1_MMIO_TK1_APP_ADDR;
|
||||
static volatile uint32_t *app_size = (volatile uint32_t *)TK1_MMIO_TK1_APP_SIZE;
|
||||
static volatile uint32_t *fw_blake2s_addr = (volatile uint32_t *)TK1_MMIO_TK1_BLAKE2S;
|
||||
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;
|
||||
static volatile uint32_t *timer = (volatile uint32_t *)TK1_MMIO_TIMER_TIMER;
|
||||
|
@ -30,15 +36,21 @@ 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;
|
||||
// clang-format on
|
||||
|
||||
struct partition_table_storage part_table_storage;
|
||||
|
||||
// Context for the loading of a TKey program
|
||||
struct context {
|
||||
uint32_t left; // Bytes left to receive
|
||||
uint8_t digest[32]; // Program digest
|
||||
uint8_t *loadaddr; // Where we are currently loading a TKey program
|
||||
uint8_t use_uss; // Use USS?
|
||||
bool use_uss; // Use USS?
|
||||
uint8_t uss[32]; // User Supplied Secret, if any
|
||||
uint8_t flash_slot; // App is loaded from flash slot number
|
||||
/*@null@*/ volatile uint8_t
|
||||
*ver_digest; // Verify loaded app against this digest
|
||||
};
|
||||
|
||||
static void print_hw_version(void);
|
||||
|
@ -53,34 +65,38 @@ static enum state initial_commands(const struct frame_header *hdr,
|
|||
static enum state loading_commands(const struct frame_header *hdr,
|
||||
const uint8_t *cmd, enum state state,
|
||||
struct context *ctx);
|
||||
static void run(const struct context *ctx);
|
||||
#if !defined(SIMULATION)
|
||||
static uint32_t xorwow(uint32_t state, uint32_t acc);
|
||||
#endif
|
||||
static void scramble_ram(void);
|
||||
static int compute_app_digest(uint8_t *digest);
|
||||
static int load_flash_app(struct partition_table *part_table,
|
||||
uint8_t digest[32], uint8_t slot);
|
||||
static enum state start_where(struct context *ctx);
|
||||
|
||||
static void print_hw_version(void)
|
||||
{
|
||||
htif_puts("Hello, I'm firmware with");
|
||||
htif_puts(" tk1_name0:");
|
||||
htif_putinthex(*name0);
|
||||
htif_puts(" tk1_name1:");
|
||||
htif_putinthex(*name1);
|
||||
htif_puts(" tk1_version:");
|
||||
htif_putinthex(*ver);
|
||||
htif_lf();
|
||||
debug_puts("Hello, I'm firmware with");
|
||||
debug_puts(" tk1_name0:");
|
||||
debug_putinthex(*name0);
|
||||
debug_puts(" tk1_name1:");
|
||||
debug_putinthex(*name1);
|
||||
debug_puts(" tk1_version:");
|
||||
debug_putinthex(*ver);
|
||||
debug_lf();
|
||||
}
|
||||
|
||||
static void print_digest(uint8_t *md)
|
||||
{
|
||||
htif_puts("The app digest:\n");
|
||||
debug_puts("The app digest:\n");
|
||||
for (int j = 0; j < 4; j++) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
htif_puthex(md[i + 8 * j]);
|
||||
debug_puthex(md[i + 8 * j]);
|
||||
(void)md;
|
||||
}
|
||||
htif_lf();
|
||||
debug_lf();
|
||||
}
|
||||
htif_lf();
|
||||
debug_lf();
|
||||
}
|
||||
|
||||
static uint32_t rnd_word(void)
|
||||
|
@ -141,6 +157,7 @@ static void compute_cdi(const uint8_t *digest, const uint8_t use_uss,
|
|||
static void copy_name(uint8_t *buf, const size_t bufsiz, const uint32_t word)
|
||||
{
|
||||
assert(bufsiz >= 4);
|
||||
assert(buf != NULL);
|
||||
|
||||
buf[0] = word >> 24;
|
||||
buf[1] = word >> 16;
|
||||
|
@ -152,20 +169,20 @@ static enum state initial_commands(const struct frame_header *hdr,
|
|||
const uint8_t *cmd, enum state state,
|
||||
struct context *ctx)
|
||||
{
|
||||
uint8_t rsp[CMDLEN_MAXBYTES] = {0};
|
||||
uint8_t rsp[CMDSIZE] = {0};
|
||||
|
||||
switch (cmd[0]) {
|
||||
case FW_CMD_NAME_VERSION:
|
||||
htif_puts("cmd: name-version\n");
|
||||
debug_puts("cmd: name-version\n");
|
||||
if (hdr->len != 1) {
|
||||
// Bad length
|
||||
state = FW_STATE_FAIL;
|
||||
break;
|
||||
}
|
||||
|
||||
copy_name(rsp, CMDLEN_MAXBYTES, *name0);
|
||||
copy_name(&rsp[4], CMDLEN_MAXBYTES - 4, *name1);
|
||||
wordcpy_s(&rsp[8], CMDLEN_MAXBYTES / 4 - 2, (void *)ver, 1);
|
||||
copy_name(rsp, CMDSIZE, *name0);
|
||||
copy_name(&rsp[4], CMDSIZE - 4, *name1);
|
||||
wordcpy_s(&rsp[8], CMDSIZE - 8, (void *)ver, 1);
|
||||
|
||||
fwreply(*hdr, FW_RSP_NAME_VERSION, rsp);
|
||||
// still initial state
|
||||
|
@ -174,7 +191,7 @@ static enum state initial_commands(const struct frame_header *hdr,
|
|||
case FW_CMD_GET_UDI: {
|
||||
uint32_t udi_words[2];
|
||||
|
||||
htif_puts("cmd: get-udi\n");
|
||||
debug_puts("cmd: get-udi\n");
|
||||
if (hdr->len != 1) {
|
||||
// Bad length
|
||||
state = FW_STATE_FAIL;
|
||||
|
@ -183,7 +200,7 @@ static enum state initial_commands(const struct frame_header *hdr,
|
|||
|
||||
rsp[0] = STATUS_OK;
|
||||
wordcpy_s(&udi_words, 2, (void *)udi, 2);
|
||||
memcpy_s(&rsp[1], CMDLEN_MAXBYTES - 1, &udi_words, 2 * 4);
|
||||
memcpy_s(&rsp[1], CMDSIZE - 1, &udi_words, 2 * 4);
|
||||
fwreply(*hdr, FW_RSP_GET_UDI, rsp);
|
||||
// still initial state
|
||||
break;
|
||||
|
@ -192,7 +209,7 @@ static enum state initial_commands(const struct frame_header *hdr,
|
|||
case FW_CMD_LOAD_APP: {
|
||||
uint32_t local_app_size;
|
||||
|
||||
htif_puts("cmd: load-app(size, uss)\n");
|
||||
debug_puts("cmd: load-app(size, uss)\n");
|
||||
if (hdr->len != 128) {
|
||||
// Bad length
|
||||
state = FW_STATE_FAIL;
|
||||
|
@ -203,9 +220,9 @@ static enum state initial_commands(const struct frame_header *hdr,
|
|||
local_app_size =
|
||||
cmd[1] + (cmd[2] << 8) + (cmd[3] << 16) + (cmd[4] << 24);
|
||||
|
||||
htif_puts("app size: ");
|
||||
htif_putinthex(local_app_size);
|
||||
htif_lf();
|
||||
debug_puts("app size: ");
|
||||
debug_putinthex(local_app_size);
|
||||
debug_lf();
|
||||
|
||||
if (local_app_size == 0 || local_app_size > TK1_APP_MAX_SIZE) {
|
||||
rsp[0] = STATUS_BAD;
|
||||
|
@ -219,10 +236,10 @@ static enum state initial_commands(const struct frame_header *hdr,
|
|||
// Do we have a USS at all?
|
||||
if (cmd[5] != 0) {
|
||||
// Yes
|
||||
ctx->use_uss = TRUE;
|
||||
ctx->use_uss = true;
|
||||
memcpy_s(ctx->uss, 32, &cmd[6], 32);
|
||||
} else {
|
||||
ctx->use_uss = FALSE;
|
||||
ctx->use_uss = false;
|
||||
}
|
||||
|
||||
rsp[0] = STATUS_OK;
|
||||
|
@ -233,14 +250,16 @@ static enum state initial_commands(const struct frame_header *hdr,
|
|||
|
||||
ctx->left = *app_size;
|
||||
|
||||
led_set(LED_BLACK);
|
||||
|
||||
state = FW_STATE_LOADING;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
htif_puts("Got unknown firmware cmd: 0x");
|
||||
htif_puthex(cmd[0]);
|
||||
htif_lf();
|
||||
debug_puts("Got unknown firmware cmd: 0x");
|
||||
debug_puthex(cmd[0]);
|
||||
debug_lf();
|
||||
state = FW_STATE_FAIL;
|
||||
break;
|
||||
}
|
||||
|
@ -252,12 +271,12 @@ static enum state loading_commands(const struct frame_header *hdr,
|
|||
const uint8_t *cmd, enum state state,
|
||||
struct context *ctx)
|
||||
{
|
||||
uint8_t rsp[CMDLEN_MAXBYTES] = {0};
|
||||
uint8_t rsp[CMDSIZE] = {0};
|
||||
uint32_t nbytes = 0;
|
||||
|
||||
switch (cmd[0]) {
|
||||
case FW_CMD_LOAD_APP_DATA:
|
||||
htif_puts("cmd: load-app-data\n");
|
||||
debug_puts("cmd: load-app-data\n");
|
||||
if (hdr->len != 128) {
|
||||
// Bad length
|
||||
state = FW_STATE_FAIL;
|
||||
|
@ -276,29 +295,25 @@ static enum state loading_commands(const struct frame_header *hdr,
|
|||
ctx->left -= nbytes;
|
||||
|
||||
if (ctx->left == 0) {
|
||||
blake2s_ctx b2s_ctx = {0};
|
||||
int blake2err = 0;
|
||||
|
||||
htif_puts("Fully loaded ");
|
||||
htif_putinthex(*app_size);
|
||||
htif_lf();
|
||||
debug_puts("Fully loaded ");
|
||||
debug_putinthex(*app_size);
|
||||
debug_lf();
|
||||
|
||||
// Compute Blake2S digest of the app,
|
||||
// storing it for FW_STATE_RUN
|
||||
blake2err = blake2s(&ctx->digest, 32, NULL, 0,
|
||||
(const void *)TK1_RAM_BASE,
|
||||
*app_size, &b2s_ctx);
|
||||
blake2err = compute_app_digest(ctx->digest);
|
||||
assert(blake2err == 0);
|
||||
print_digest(ctx->digest);
|
||||
|
||||
// And return the digest in final
|
||||
// response
|
||||
rsp[0] = STATUS_OK;
|
||||
memcpy_s(&rsp[1], CMDLEN_MAXBYTES - 1, &ctx->digest,
|
||||
32);
|
||||
memcpy_s(&rsp[1], CMDSIZE - 1, &ctx->digest, 32);
|
||||
fwreply(*hdr, FW_RSP_LOAD_APP_DATA_READY, rsp);
|
||||
|
||||
state = FW_STATE_RUN;
|
||||
state = FW_STATE_START;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -308,9 +323,9 @@ static enum state loading_commands(const struct frame_header *hdr,
|
|||
break;
|
||||
|
||||
default:
|
||||
htif_puts("Got unknown firmware cmd: 0x");
|
||||
htif_puthex(cmd[0]);
|
||||
htif_lf();
|
||||
debug_puts("Got unknown firmware cmd: 0x");
|
||||
debug_puthex(cmd[0]);
|
||||
debug_lf();
|
||||
state = FW_STATE_FAIL;
|
||||
break;
|
||||
}
|
||||
|
@ -318,24 +333,22 @@ static enum state loading_commands(const struct frame_header *hdr,
|
|||
return state;
|
||||
}
|
||||
|
||||
static void run(const struct context *ctx)
|
||||
static void jump_to_app(void)
|
||||
{
|
||||
/* Start of app is always at the beginning of RAM */
|
||||
*app_addr = TK1_RAM_BASE;
|
||||
|
||||
// CDI = hash(uds, hash(app), uss)
|
||||
compute_cdi(ctx->digest, ctx->use_uss, ctx->uss);
|
||||
|
||||
htif_puts("Flipping to app mode!\n");
|
||||
htif_puts("Jumping to ");
|
||||
htif_putinthex(*app_addr);
|
||||
htif_lf();
|
||||
debug_puts("Flipping to app mode!\n");
|
||||
debug_puts("Jumping to ");
|
||||
debug_putinthex(*app_addr);
|
||||
debug_lf();
|
||||
|
||||
// Clear the firmware stack
|
||||
// clang-format off
|
||||
#ifndef S_SPLINT_S
|
||||
asm volatile(
|
||||
"li a0, 0xd0000000;" // FW_RAM
|
||||
"li a1, 0xd0000800;" // End of 2 KB FW_RAM (just past the end)
|
||||
"la a0, _sstack;"
|
||||
"la a1, _estack;"
|
||||
"loop:;"
|
||||
"sw zero, 0(a0);"
|
||||
"addi a0, a0, 4;"
|
||||
|
@ -344,13 +357,10 @@ static void run(const struct context *ctx)
|
|||
#endif
|
||||
// clang-format on
|
||||
|
||||
// Flip over to application mode
|
||||
*system_mode_ctrl = 1;
|
||||
|
||||
// XXX Firmware stack now no longer available
|
||||
// Don't use any function calls!
|
||||
syscall_enable();
|
||||
|
||||
// Jump to app - doesn't return
|
||||
// Hardware is responsible for switching to app mode
|
||||
// clang-format off
|
||||
#ifndef S_SPLINT_S
|
||||
asm volatile(
|
||||
|
@ -366,6 +376,29 @@ static void run(const struct context *ctx)
|
|||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
static int load_flash_app(struct partition_table *part_table,
|
||||
uint8_t digest[32], uint8_t slot)
|
||||
{
|
||||
if (slot >= N_PRELOADED_APP) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (preload_load(part_table, slot) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*app_size = part_table->pre_app_data[slot].size;
|
||||
if (*app_size > TK1_APP_MAX_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int digest_err = compute_app_digest(digest);
|
||||
assert(digest_err == 0);
|
||||
print_digest(digest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if !defined(SIMULATION)
|
||||
static uint32_t xorwow(uint32_t state, uint32_t acc)
|
||||
{
|
||||
|
@ -400,31 +433,105 @@ static void scramble_ram(void)
|
|||
*ram_data_rand = rnd_word();
|
||||
}
|
||||
|
||||
/* Computes the blake2s digest of the app loaded into RAM */
|
||||
static int compute_app_digest(uint8_t *digest)
|
||||
{
|
||||
return blake2s(digest, 32, NULL, 0, (const void *)TK1_RAM_BASE,
|
||||
*app_size);
|
||||
}
|
||||
|
||||
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?
|
||||
switch (resetinfo->type) {
|
||||
case START_DEFAULT:
|
||||
// fallthrough
|
||||
case START_FLASH0:
|
||||
ctx->flash_slot = 0;
|
||||
ctx->ver_digest = mgmt_app_allowed_digest();
|
||||
|
||||
return FW_STATE_LOAD_FLASH_MGMT;
|
||||
|
||||
case START_FLASH1:
|
||||
ctx->flash_slot = 1;
|
||||
ctx->ver_digest = NULL;
|
||||
|
||||
return FW_STATE_LOAD_FLASH;
|
||||
|
||||
case START_FLASH0_VER:
|
||||
ctx->flash_slot = 0;
|
||||
ctx->ver_digest = resetinfo->app_digest;
|
||||
|
||||
return FW_STATE_LOAD_FLASH;
|
||||
|
||||
case START_FLASH1_VER:
|
||||
ctx->flash_slot = 1;
|
||||
ctx->ver_digest = resetinfo->app_digest;
|
||||
|
||||
return FW_STATE_LOAD_FLASH;
|
||||
|
||||
case START_CLIENT:
|
||||
ctx->ver_digest = NULL;
|
||||
|
||||
return FW_STATE_WAITCOMMAND;
|
||||
|
||||
case START_CLIENT_VER:
|
||||
ctx->ver_digest = resetinfo->app_digest;
|
||||
|
||||
return FW_STATE_WAITCOMMAND;
|
||||
|
||||
default:
|
||||
debug_puts("Unknown startfrom\n");
|
||||
|
||||
return FW_STATE_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct context ctx = {0};
|
||||
struct frame_header hdr = {0};
|
||||
uint8_t cmd[CMDLEN_MAXBYTES] = {0};
|
||||
uint8_t cmd[CMDSIZE] = {0};
|
||||
enum state state = FW_STATE_INITIAL;
|
||||
|
||||
print_hw_version();
|
||||
|
||||
// Let the app know the function adddress for blake2s()
|
||||
*fw_blake2s_addr = (uint32_t)blake2s;
|
||||
|
||||
/*@-mustfreeonly@*/
|
||||
/* Yes, splint, this points directly to RAM and we don't care
|
||||
* about freeing anything was pointing to 0x0 before.
|
||||
*/
|
||||
ctx.loadaddr = (uint8_t *)TK1_RAM_BASE;
|
||||
/*@+mustfreeonly@*/
|
||||
ctx.use_uss = FALSE;
|
||||
|
||||
uint8_t mode = 0;
|
||||
uint8_t mode_bytes_left = 0;
|
||||
ctx.use_uss = false;
|
||||
|
||||
scramble_ram();
|
||||
|
||||
if (part_table_read(&part_table_storage) != 0) {
|
||||
// Couldn't read partition table
|
||||
debug_puts("Couldn't read partition table\n");
|
||||
assert(1 == 2);
|
||||
}
|
||||
|
||||
// Reset the USB controller to only enable the USB CDC
|
||||
// endpoint and the internal command channel.
|
||||
config_endpoints(IO_CDC | IO_CH552);
|
||||
|
||||
led_set(LED_WHITE);
|
||||
|
||||
#if defined(SIMULATION)
|
||||
run(&ctx);
|
||||
#endif
|
||||
|
@ -432,18 +539,23 @@ int main(void)
|
|||
for (;;) {
|
||||
switch (state) {
|
||||
case FW_STATE_INITIAL:
|
||||
if (readcommand(&hdr, cmd, state, &mode,
|
||||
&mode_bytes_left) == -1) {
|
||||
state = start_where(&ctx);
|
||||
break;
|
||||
|
||||
case FW_STATE_WAITCOMMAND:
|
||||
if (readcommand(&hdr, cmd, state) == -1) {
|
||||
state = FW_STATE_FAIL;
|
||||
break;
|
||||
}
|
||||
|
||||
debug_puts("cmd: \n");
|
||||
debug_hexdump(cmd, hdr.len);
|
||||
|
||||
state = initial_commands(&hdr, cmd, state, &ctx);
|
||||
break;
|
||||
|
||||
case FW_STATE_LOADING:
|
||||
if (readcommand(&hdr, cmd, state, &mode,
|
||||
&mode_bytes_left) == -1) {
|
||||
if (readcommand(&hdr, cmd, state) == -1) {
|
||||
state = FW_STATE_FAIL;
|
||||
break;
|
||||
}
|
||||
|
@ -451,22 +563,66 @@ int main(void)
|
|||
state = loading_commands(&hdr, cmd, state, &ctx);
|
||||
break;
|
||||
|
||||
case FW_STATE_RUN:
|
||||
run(&ctx);
|
||||
break; // This is never reached!
|
||||
case FW_STATE_LOAD_FLASH:
|
||||
if (load_flash_app(&part_table_storage.table,
|
||||
ctx.digest, ctx.flash_slot) < 0) {
|
||||
debug_puts("Couldn't load app from flash\n");
|
||||
state = FW_STATE_FAIL;
|
||||
break;
|
||||
}
|
||||
|
||||
state = FW_STATE_START;
|
||||
break;
|
||||
|
||||
case FW_STATE_LOAD_FLASH_MGMT:
|
||||
if (load_flash_app(&part_table_storage.table,
|
||||
ctx.digest, ctx.flash_slot) < 0) {
|
||||
debug_puts("Couldn't load app from flash\n");
|
||||
state = FW_STATE_FAIL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (mgmt_app_init(ctx.digest) != 0) {
|
||||
state = FW_STATE_FAIL;
|
||||
break;
|
||||
}
|
||||
|
||||
state = FW_STATE_START;
|
||||
break;
|
||||
|
||||
case FW_STATE_START:
|
||||
// CDI = hash(uds, hash(app), uss)
|
||||
compute_cdi(ctx.digest, ctx.use_uss, ctx.uss);
|
||||
|
||||
if (ctx.ver_digest != NULL) {
|
||||
print_digest(ctx.digest);
|
||||
if (!memeq(ctx.digest, (void *)ctx.ver_digest,
|
||||
sizeof(ctx.digest))) {
|
||||
debug_puts("Digests do not match\n");
|
||||
state = FW_STATE_FAIL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
(void)memset((void *)resetinfo->app_digest, 0,
|
||||
sizeof(resetinfo->app_digest));
|
||||
|
||||
jump_to_app();
|
||||
break; // Not reached
|
||||
|
||||
case FW_STATE_FAIL:
|
||||
// fallthrough
|
||||
default:
|
||||
htif_puts("firmware state 0x");
|
||||
htif_puthex(state);
|
||||
htif_lf();
|
||||
debug_puts("firmware state 0x");
|
||||
debug_puthex(state);
|
||||
debug_lf();
|
||||
assert(1 == 2);
|
||||
break; // Not reached
|
||||
}
|
||||
}
|
||||
|
||||
/*@ -compdestroy @*/
|
||||
/* We don't care about memory leaks here. */
|
||||
// We don't care about memory leaks here.
|
||||
|
||||
return (int)0xcafebabe;
|
||||
}
|
||||
|
|
46
hw/application_fpga/fw/tk1/mgmt_app.c
Normal file
46
hw/application_fpga/fw/tk1/mgmt_app.c
Normal file
|
@ -0,0 +1,46 @@
|
|||
// Copyright (C) 2024 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <tkey/io.h>
|
||||
#include <tkey/lib.h>
|
||||
|
||||
#include "mgmt_app.h"
|
||||
|
||||
// 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,
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
static uint8_t current_app_digest[32];
|
||||
|
||||
int mgmt_app_init(uint8_t app_digest[32])
|
||||
{
|
||||
if (app_digest == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy_s(current_app_digest, sizeof(current_app_digest), app_digest,
|
||||
32);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Authenticate an management app
|
||||
bool mgmt_app_authenticate(void)
|
||||
{
|
||||
return memeq(current_app_digest, allowed_app_digest, 32) != 0;
|
||||
}
|
||||
|
||||
uint8_t *mgmt_app_allowed_digest(void)
|
||||
{
|
||||
return (uint8_t *)allowed_app_digest;
|
||||
}
|
14
hw/application_fpga/fw/tk1/mgmt_app.h
Normal file
14
hw/application_fpga/fw/tk1/mgmt_app.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
// Copyright (C) 2024 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#ifndef MGMT_APP_H
|
||||
#define MGMT_APP_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
int mgmt_app_init(uint8_t app_digest[32]);
|
||||
bool mgmt_app_authenticate(void);
|
||||
uint8_t *mgmt_app_allowed_digest(void);
|
||||
|
||||
#endif
|
104
hw/application_fpga/fw/tk1/partition_table.c
Normal file
104
hw/application_fpga/fw/tk1/partition_table.c
Normal file
|
@ -0,0 +1,104 @@
|
|||
// Copyright (C) 2024 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <stdint.h>
|
||||
#include <tkey/assert.h>
|
||||
#include <tkey/lib.h>
|
||||
|
||||
#include "blake2s/blake2s.h"
|
||||
#include "flash.h"
|
||||
#include "partition_table.h"
|
||||
#include "proto.h"
|
||||
|
||||
static enum part_status part_status;
|
||||
|
||||
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);
|
||||
|
||||
// part_digest computes a checksum over the partition table to detect
|
||||
// flash problems
|
||||
static void part_checksum(struct partition_table *part_table,
|
||||
uint8_t *out_digest, size_t out_len)
|
||||
{
|
||||
int blake2err = 0;
|
||||
|
||||
assert(part_table != NULL);
|
||||
assert(out_digest != NULL);
|
||||
|
||||
blake2err = blake2s(out_digest, out_len, NULL, 0, part_table,
|
||||
sizeof(struct partition_table));
|
||||
|
||||
assert(blake2err == 0);
|
||||
}
|
||||
|
||||
// part_table_read reads and verifies the partition table storage,
|
||||
// first trying slot 0, then slot 1 if slot 0 does not verify.
|
||||
//
|
||||
// It stores the partition table in storage.
|
||||
//
|
||||
// Returns negative values on errors.
|
||||
int part_table_read(struct partition_table_storage *storage)
|
||||
{
|
||||
uint32_t offset[2] = {
|
||||
ADDR_PARTITION_TABLE_0,
|
||||
ADDR_PARTITION_TABLE_1,
|
||||
};
|
||||
uint8_t check_digest[PART_CHECKSUM_SIZE] = {0};
|
||||
|
||||
if (storage == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
flash_release_powerdown();
|
||||
(void)memset(storage, 0x00, sizeof(*storage));
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (flash_read_data(offset[i], (uint8_t *)storage,
|
||||
sizeof(*storage)) != 0) {
|
||||
return -1;
|
||||
}
|
||||
part_checksum(&storage->table, check_digest,
|
||||
sizeof(check_digest));
|
||||
|
||||
if (memeq(check_digest, storage->checksum,
|
||||
sizeof(check_digest))) {
|
||||
if (i == 1) {
|
||||
part_status = PART_SLOT0_INVALID;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int part_table_write(struct partition_table_storage *storage)
|
||||
{
|
||||
uint32_t offset[2] = {
|
||||
ADDR_PARTITION_TABLE_0,
|
||||
ADDR_PARTITION_TABLE_1,
|
||||
};
|
||||
|
||||
if (storage == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
part_checksum(&storage->table, storage->checksum,
|
||||
sizeof(storage->checksum));
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
flash_sector_erase(offset[i]);
|
||||
if (flash_write_data(offset[i], (uint8_t *)storage,
|
||||
sizeof(*storage)) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
112
hw/application_fpga/fw/tk1/partition_table.h
Normal file
112
hw/application_fpga/fw/tk1/partition_table.h
Normal file
|
@ -0,0 +1,112 @@
|
|||
// Copyright (C) 2024 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#ifndef PARTITION_TABLE_H
|
||||
#define PARTITION_TABLE_H
|
||||
|
||||
#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
|
||||
|
||||
// To simplify all blocks are aligned with the 64KiB blocks on the
|
||||
// W25Q80DL flash.
|
||||
|
||||
#define PART_TABLE_VERSION 1
|
||||
|
||||
#define ADDR_BITSTREAM 0UL
|
||||
#define SIZE_BITSTREAM 0x20000UL // 128KiB
|
||||
|
||||
#define ADDR_PARTITION_TABLE_0 (ADDR_BITSTREAM + SIZE_BITSTREAM)
|
||||
#define ADDR_PARTITION_TABLE_1 0xf0000
|
||||
#define SIZE_PARTITION_TABLE \
|
||||
0x10000UL // 64KiB, 60 KiB reserved, 2 flash pages (2 x 4KiB) for the
|
||||
// partition table
|
||||
|
||||
#define N_PRELOADED_APP 2
|
||||
#define ADDR_PRE_LOADED_APP_0 (ADDR_PARTITION_TABLE_0 + SIZE_PARTITION_TABLE)
|
||||
#define SIZE_PRE_LOADED_APP 0x20000UL // 128KiB
|
||||
|
||||
#define ADDR_STORAGE_AREA \
|
||||
(ADDR_PRE_LOADED_APP_0 + (N_PRELOADED_APP * SIZE_PRE_LOADED_APP))
|
||||
#define SIZE_STORAGE_AREA 0x20000UL // 128KiB
|
||||
#define N_STORAGE_AREA 4
|
||||
|
||||
#define PART_CHECKSUM_SIZE 32
|
||||
|
||||
enum part_status {
|
||||
PART_SLOT0_INVALID = 1,
|
||||
};
|
||||
|
||||
// Partition Table
|
||||
// ----------------------------------------------------------------------
|
||||
// - Table header
|
||||
// - 1 bytes Version
|
||||
//
|
||||
// - Pre-loaded device app 1
|
||||
// - 4 bytes length.
|
||||
// - 32 bytes digest.
|
||||
// - 64 bytes signature.
|
||||
//
|
||||
// - Pre-loaded device app 2
|
||||
// - 4 bytes length.
|
||||
// - 32 bytes digest.
|
||||
// - 64 bytes signature.
|
||||
//
|
||||
// - Device app storage area
|
||||
// - 1 byte status.
|
||||
// - 16 bytes random nonce.
|
||||
// - 16 bytes authentication tag.
|
||||
//
|
||||
// - Checksum over the above
|
||||
|
||||
struct auth_metadata {
|
||||
uint8_t nonce[16];
|
||||
uint8_t authentication_digest[16];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pre_loaded_app_metadata {
|
||||
uint32_t size;
|
||||
uint8_t digest[32];
|
||||
uint8_t signature[64];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct app_storage_area {
|
||||
uint8_t status;
|
||||
struct auth_metadata auth;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct table_header {
|
||||
uint8_t version;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct partition_table {
|
||||
struct table_header header;
|
||||
struct pre_loaded_app_metadata pre_app_data[N_PRELOADED_APP];
|
||||
struct app_storage_area app_storage[N_STORAGE_AREA];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct partition_table_storage {
|
||||
struct partition_table table;
|
||||
uint8_t checksum[PART_CHECKSUM_SIZE]; // Helps detect flash problems
|
||||
} __attribute__((packed));
|
||||
|
||||
enum part_status part_get_status(void);
|
||||
int part_table_read(struct partition_table_storage *storage);
|
||||
int part_table_write(struct partition_table_storage *storage);
|
||||
|
||||
#endif
|
12
hw/application_fpga/fw/tk1/picorv32/README.md
Normal file
12
hw/application_fpga/fw/tk1/picorv32/README.md
Normal file
|
@ -0,0 +1,12 @@
|
|||
# PicoRV32
|
||||
|
||||
## 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.
|
105
hw/application_fpga/fw/tk1/picorv32/custom_ops.S
Normal file
105
hw/application_fpga/fw/tk1/picorv32/custom_ops.S
Normal file
|
@ -0,0 +1,105 @@
|
|||
// 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
|
||||
// distribute this software, either in source code form or as a compiled
|
||||
// binary, for any purpose, commercial or non-commercial, and by any
|
||||
// means.
|
||||
|
||||
#define regnum_q0 0
|
||||
#define regnum_q1 1
|
||||
#define regnum_q2 2
|
||||
#define regnum_q3 3
|
||||
|
||||
#define regnum_x0 0
|
||||
#define regnum_x1 1
|
||||
#define regnum_x2 2
|
||||
#define regnum_x3 3
|
||||
#define regnum_x4 4
|
||||
#define regnum_x5 5
|
||||
#define regnum_x6 6
|
||||
#define regnum_x7 7
|
||||
#define regnum_x8 8
|
||||
#define regnum_x9 9
|
||||
#define regnum_x10 10
|
||||
#define regnum_x11 11
|
||||
#define regnum_x12 12
|
||||
#define regnum_x13 13
|
||||
#define regnum_x14 14
|
||||
#define regnum_x15 15
|
||||
#define regnum_x16 16
|
||||
#define regnum_x17 17
|
||||
#define regnum_x18 18
|
||||
#define regnum_x19 19
|
||||
#define regnum_x20 20
|
||||
#define regnum_x21 21
|
||||
#define regnum_x22 22
|
||||
#define regnum_x23 23
|
||||
#define regnum_x24 24
|
||||
#define regnum_x25 25
|
||||
#define regnum_x26 26
|
||||
#define regnum_x27 27
|
||||
#define regnum_x28 28
|
||||
#define regnum_x29 29
|
||||
#define regnum_x30 30
|
||||
#define regnum_x31 31
|
||||
|
||||
#define regnum_zero 0
|
||||
#define regnum_ra 1
|
||||
#define regnum_sp 2
|
||||
#define regnum_gp 3
|
||||
#define regnum_tp 4
|
||||
#define regnum_t0 5
|
||||
#define regnum_t1 6
|
||||
#define regnum_t2 7
|
||||
#define regnum_s0 8
|
||||
#define regnum_s1 9
|
||||
#define regnum_a0 10
|
||||
#define regnum_a1 11
|
||||
#define regnum_a2 12
|
||||
#define regnum_a3 13
|
||||
#define regnum_a4 14
|
||||
#define regnum_a5 15
|
||||
#define regnum_a6 16
|
||||
#define regnum_a7 17
|
||||
#define regnum_s2 18
|
||||
#define regnum_s3 19
|
||||
#define regnum_s4 20
|
||||
#define regnum_s5 21
|
||||
#define regnum_s6 22
|
||||
#define regnum_s7 23
|
||||
#define regnum_s8 24
|
||||
#define regnum_s9 25
|
||||
#define regnum_s10 26
|
||||
#define regnum_s11 27
|
||||
#define regnum_t3 28
|
||||
#define regnum_t4 29
|
||||
#define regnum_t5 30
|
||||
#define regnum_t6 31
|
||||
|
||||
// x8 is s0 and also fp
|
||||
#define regnum_fp 8
|
||||
|
||||
#define r_type_insn(_f7, _rs2, _rs1, _f3, _rd, _opc) \
|
||||
.word (((_f7) << 25) | ((_rs2) << 20) | ((_rs1) << 15) | ((_f3) << 12) | ((_rd) << 7) | ((_opc) << 0))
|
||||
|
||||
#define picorv32_getq_insn(_rd, _qs) \
|
||||
r_type_insn(0b0000000, 0, regnum_ ## _qs, 0b100, regnum_ ## _rd, 0b0001011)
|
||||
|
||||
#define picorv32_setq_insn(_qd, _rs) \
|
||||
r_type_insn(0b0000001, 0, regnum_ ## _rs, 0b010, regnum_ ## _qd, 0b0001011)
|
||||
|
||||
#define picorv32_retirq_insn() \
|
||||
r_type_insn(0b0000010, 0, 0, 0b000, 0, 0b0001011)
|
||||
|
||||
#define picorv32_maskirq_insn(_rd, _rs) \
|
||||
r_type_insn(0b0000011, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011)
|
||||
|
||||
#define picorv32_waitirq_insn(_rd) \
|
||||
r_type_insn(0b0000100, 0, 0, 0b100, regnum_ ## _rd, 0b0001011)
|
||||
|
||||
#define picorv32_timer_insn(_rd, _rs) \
|
||||
r_type_insn(0b0000101, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011)
|
||||
|
224
hw/application_fpga/fw/tk1/preload_app.c
Normal file
224
hw/application_fpga/fw/tk1/preload_app.c
Normal file
|
@ -0,0 +1,224 @@
|
|||
// Copyright (C) 2024 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <tkey/debug.h>
|
||||
#include <tkey/lib.h>
|
||||
#include <tkey/tk1_mem.h>
|
||||
|
||||
#include "flash.h"
|
||||
#include "mgmt_app.h"
|
||||
#include "partition_table.h"
|
||||
#include "preload_app.h"
|
||||
|
||||
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
|
||||
int preload_load(struct partition_table *part_table, uint8_t from_slot)
|
||||
{
|
||||
if (part_table == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (from_slot >= N_PRELOADED_APP) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check for a valid app in flash
|
||||
if (part_table->pre_app_data[from_slot].size == 0 &&
|
||||
part_table->pre_app_data[from_slot].size <= TK1_APP_MAX_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
uint8_t *loadaddr = (uint8_t *)TK1_RAM_BASE;
|
||||
|
||||
// Read from flash, straight into RAM
|
||||
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.
|
||||
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;
|
||||
}
|
||||
|
||||
if (to_slot >= N_PRELOADED_APP) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check if we are allowed to store
|
||||
if (!mgmt_app_authenticate()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
uint32_t address = slot_to_start_address(to_slot) + offset;
|
||||
|
||||
debug_puts("preload_store: write to addr: ");
|
||||
debug_putinthex(address);
|
||||
debug_lf();
|
||||
|
||||
return flash_write_data(address, data, size);
|
||||
}
|
||||
|
||||
int preload_store_finalize(struct partition_table_storage *part_table_storage,
|
||||
size_t app_size, uint8_t app_digest[32],
|
||||
uint8_t app_signature[64], uint8_t to_slot)
|
||||
{
|
||||
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 (to_slot >= N_PRELOADED_APP) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check if we are allowed to store
|
||||
if (!mgmt_app_authenticate()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
part_table->pre_app_data[to_slot].size = app_size;
|
||||
memcpy_s(part_table->pre_app_data[to_slot].digest,
|
||||
sizeof(part_table->pre_app_data[to_slot].digest), app_digest,
|
||||
32);
|
||||
memcpy_s(part_table->pre_app_data[to_slot].signature,
|
||||
sizeof(part_table->pre_app_data[to_slot].signature),
|
||||
app_signature, 64);
|
||||
debug_puts("preload_*_final: size: ");
|
||||
debug_putinthex(app_size);
|
||||
debug_lf();
|
||||
|
||||
if (part_table_write(part_table_storage) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int preload_delete(struct partition_table_storage *part_table_storage,
|
||||
uint8_t slot)
|
||||
{
|
||||
struct partition_table *part_table = &part_table_storage->table;
|
||||
|
||||
if (part_table_storage == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (slot >= N_PRELOADED_APP) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check if we are allowed to delete
|
||||
if (!mgmt_app_authenticate()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check for a valid app in flash
|
||||
if (part_table->pre_app_data[slot].size == 0) {
|
||||
// Nothing to do.
|
||||
return 0;
|
||||
}
|
||||
|
||||
part_table->pre_app_data[slot].size = 0;
|
||||
|
||||
(void)memset(part_table->pre_app_data[slot].digest, 0,
|
||||
sizeof(part_table->pre_app_data[slot].digest));
|
||||
|
||||
(void)memset(part_table->pre_app_data[slot].signature, 0,
|
||||
sizeof(part_table->pre_app_data[slot].signature));
|
||||
|
||||
if (part_table_write(part_table_storage) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 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) +
|
||||
0x10000); // Erase first 64 KB block
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int preload_get_digsig(struct partition_table *part_table,
|
||||
uint8_t app_digest[32], uint8_t app_signature[64],
|
||||
uint8_t slot)
|
||||
{
|
||||
if (part_table == NULL || app_digest == NULL || app_signature == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (slot >= N_PRELOADED_APP) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check if we are allowed to read
|
||||
if (!mgmt_app_authenticate()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy_s(app_digest, 32, part_table->pre_app_data[slot].digest,
|
||||
sizeof(part_table->pre_app_data[slot].digest));
|
||||
memcpy_s(app_signature, 64, part_table->pre_app_data[slot].signature,
|
||||
sizeof(part_table->pre_app_data[slot].signature));
|
||||
|
||||
return 0;
|
||||
}
|
24
hw/application_fpga/fw/tk1/preload_app.h
Normal file
24
hw/application_fpga/fw/tk1/preload_app.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
// Copyright (C) 2024 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#ifndef PRELOAD_APP_H
|
||||
#define PRELOAD_APP_H
|
||||
|
||||
#include "partition_table.h"
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
int preload_load(struct partition_table *part_table, uint8_t from_slot);
|
||||
int preload_store(struct partition_table *part_table, uint32_t offset,
|
||||
uint8_t *data, size_t size, uint8_t to_slot);
|
||||
int preload_store_finalize(struct partition_table_storage *part_table_storage,
|
||||
size_t app_size, uint8_t app_digest[32],
|
||||
uint8_t app_signature[64], uint8_t to_slot);
|
||||
int preload_delete(struct partition_table_storage *part_table_storage,
|
||||
uint8_t slot);
|
||||
int preload_get_digsig(struct partition_table *part_table,
|
||||
uint8_t app_digest[32], uint8_t app_signature[64],
|
||||
uint8_t slot);
|
||||
|
||||
#endif
|
|
@ -1,29 +1,20 @@
|
|||
/*
|
||||
* 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>
|
||||
#include <tkey/debug.h>
|
||||
#include <tkey/io.h>
|
||||
#include <tkey/led.h>
|
||||
#include <tkey/lib.h>
|
||||
#include <tkey/tk1_mem.h>
|
||||
|
||||
#include "proto.h"
|
||||
#include "../tk1_mem.h"
|
||||
#include "assert.h"
|
||||
#include "led.h"
|
||||
#include "lib.h"
|
||||
#include "state.h"
|
||||
#include "types.h"
|
||||
|
||||
// clang-format off
|
||||
static volatile uint32_t *can_rx = (volatile uint32_t *)TK1_MMIO_UART_RX_STATUS;
|
||||
static volatile uint32_t *rx = (volatile uint32_t *)TK1_MMIO_UART_RX_DATA;
|
||||
static volatile uint32_t *can_tx = (volatile uint32_t *)TK1_MMIO_UART_TX_STATUS;
|
||||
static volatile uint32_t *tx = (volatile uint32_t *)TK1_MMIO_UART_TX_DATA;
|
||||
// clang-format on
|
||||
|
||||
static uint8_t genhdr(uint8_t id, uint8_t endpoint, uint8_t status,
|
||||
enum cmdlen len);
|
||||
static int parseframe(uint8_t b, struct frame_header *hdr);
|
||||
static void write(uint8_t *buf, size_t nbytes);
|
||||
static int read(uint8_t *buf, size_t bufsize, size_t nbytes, uint8_t *mode,
|
||||
uint8_t *mode_bytes_left);
|
||||
static size_t bytelen(enum cmdlen cmdlen);
|
||||
|
||||
static uint8_t genhdr(uint8_t id, uint8_t endpoint, uint8_t status,
|
||||
|
@ -32,29 +23,59 @@ static uint8_t genhdr(uint8_t id, uint8_t endpoint, uint8_t status,
|
|||
return (id << 5) | (endpoint << 3) | (status << 2) | len;
|
||||
}
|
||||
|
||||
int readcommand(struct frame_header *hdr, uint8_t *cmd, int state,
|
||||
uint8_t *mode, uint8_t *mode_bytes_left)
|
||||
int readcommand(struct frame_header *hdr, uint8_t *cmd, int state)
|
||||
{
|
||||
uint8_t in = 0;
|
||||
uint8_t available = 0;
|
||||
enum ioend endpoint = IO_NONE;
|
||||
|
||||
set_led((state == FW_STATE_LOADING) ? LED_BLACK : LED_WHITE);
|
||||
in = readbyte(mode, mode_bytes_left);
|
||||
led_set((state == FW_STATE_LOADING) ? LED_BLACK : LED_WHITE);
|
||||
|
||||
if (parseframe(in, hdr) == -1) {
|
||||
htif_puts("Couldn't parse header\n");
|
||||
debug_puts("readcommand\n");
|
||||
|
||||
if (readselect(IO_CDC, &endpoint, &available) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
(void)memset(cmd, 0, CMDLEN_MAXBYTES);
|
||||
// Now we know the size of the cmd frame, read it all
|
||||
if (read(cmd, CMDLEN_MAXBYTES, hdr->len, mode, mode_bytes_left) != 0) {
|
||||
htif_puts("read: buffer overrun\n");
|
||||
if (read(IO_CDC, &in, 1, 1) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
debug_puts("read 1 byte\n");
|
||||
|
||||
if (parseframe(in, hdr) == -1) {
|
||||
debug_puts("Couldn't parse header\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
debug_puts("parseframe succeeded\n");
|
||||
|
||||
(void)memset(cmd, 0, CMDSIZE);
|
||||
|
||||
// Now we know the size of the cmd frame, read it all
|
||||
uint8_t n = 0;
|
||||
while (n < hdr->len) {
|
||||
// Wait for something to be available
|
||||
if (readselect(IO_CDC, &endpoint, &available) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Read as much as is available of what we expect
|
||||
available = available > hdr->len ? hdr->len : available;
|
||||
|
||||
assert(n < CMDSIZE);
|
||||
int n_bytes_read =
|
||||
read(IO_CDC, &cmd[n], CMDSIZE - n, available);
|
||||
if (n_bytes_read < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
n += n_bytes_read;
|
||||
}
|
||||
|
||||
// Is it for us?
|
||||
if (hdr->endpoint != DST_FW) {
|
||||
htif_puts("Message not meant for us\n");
|
||||
debug_puts("Message not meant for us\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -86,6 +107,7 @@ void fwreply(struct frame_header hdr, enum fwcmd rspcode, uint8_t *buf)
|
|||
{
|
||||
size_t nbytes = 0;
|
||||
enum cmdlen len = 0; // length covering (rspcode + length of buf)
|
||||
uint8_t frame[1 + 128]; // Frame header + longest response
|
||||
|
||||
switch (rspcode) {
|
||||
case FW_RSP_NAME_VERSION:
|
||||
|
@ -109,94 +131,23 @@ void fwreply(struct frame_header hdr, enum fwcmd rspcode, uint8_t *buf)
|
|||
break;
|
||||
|
||||
default:
|
||||
htif_puts("fwreply(): Unknown response code: 0x");
|
||||
htif_puthex(rspcode);
|
||||
htif_lf();
|
||||
debug_puts("fwreply(): Unknown response code: 0x");
|
||||
debug_puthex(rspcode);
|
||||
debug_lf();
|
||||
return;
|
||||
}
|
||||
|
||||
nbytes = bytelen(len);
|
||||
|
||||
// Mode Protocol Header
|
||||
writebyte(MODE_CDC);
|
||||
writebyte(2);
|
||||
|
||||
// Frame Protocol Header
|
||||
writebyte(genhdr(hdr.id, hdr.endpoint, 0x0, len));
|
||||
frame[0] = genhdr(hdr.id, hdr.endpoint, 0x0, len);
|
||||
// App protocol header
|
||||
frame[1] = rspcode;
|
||||
|
||||
// FW protocol header
|
||||
writebyte(rspcode);
|
||||
nbytes--;
|
||||
// Payload
|
||||
memcpy(&frame[2], buf, nbytes - 1);
|
||||
|
||||
while (nbytes > 0) {
|
||||
// Limit transfers to 64 bytes (2 byte header + 62 byte data) to
|
||||
// fit in a single USB frame.
|
||||
size_t tx_count = nbytes > 62 ? 62 : nbytes;
|
||||
// Mode Protocol Header
|
||||
writebyte(MODE_CDC);
|
||||
writebyte(tx_count & 0xff);
|
||||
|
||||
// Data
|
||||
write(buf, tx_count);
|
||||
nbytes -= tx_count;
|
||||
buf += tx_count;
|
||||
}
|
||||
}
|
||||
|
||||
void writebyte(uint8_t b)
|
||||
{
|
||||
for (;;) {
|
||||
if (*can_tx) {
|
||||
*tx = b;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void write(uint8_t *buf, size_t nbytes)
|
||||
{
|
||||
for (int i = 0; i < nbytes; i++) {
|
||||
writebyte(buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t readbyte_(void)
|
||||
{
|
||||
for (;;) {
|
||||
if (*can_rx) {
|
||||
uint32_t b = *rx;
|
||||
return b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t readbyte(uint8_t *mode, uint8_t *mode_bytes_left)
|
||||
{
|
||||
if (*mode_bytes_left == 0) {
|
||||
*mode = readbyte_();
|
||||
if (*mode != MODE_CDC) {
|
||||
htif_puts("We only support MODE_CDC\n");
|
||||
} else {
|
||||
*mode_bytes_left = readbyte_();
|
||||
}
|
||||
}
|
||||
uint8_t b = readbyte_();
|
||||
*mode_bytes_left -= 1;
|
||||
return b;
|
||||
}
|
||||
|
||||
static int read(uint8_t *buf, size_t bufsize, size_t nbytes, uint8_t *mode,
|
||||
uint8_t *mode_bytes_left)
|
||||
{
|
||||
if (nbytes > bufsize) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int n = 0; n < nbytes; n++) {
|
||||
buf[n] = readbyte(mode, mode_bytes_left);
|
||||
}
|
||||
|
||||
return 0;
|
||||
write(IO_CDC, frame, 1 + nbytes);
|
||||
}
|
||||
|
||||
// bytelen returns the number of bytes a cmdlen takes
|
||||
|
|
|
@ -1,19 +1,12 @@
|
|||
/*
|
||||
* 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 "types.h"
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef PROTO_H
|
||||
#define PROTO_H
|
||||
|
||||
enum mode {
|
||||
MODE_TKEYCTRL = 0x20,
|
||||
MODE_CDC = 0x40,
|
||||
MODE_HID = 0x80,
|
||||
};
|
||||
|
||||
enum endpoints {
|
||||
DST_HW_IFPGA,
|
||||
DST_HW_AFPGA,
|
||||
|
@ -28,7 +21,7 @@ enum cmdlen {
|
|||
LEN_128
|
||||
};
|
||||
|
||||
#define CMDLEN_MAXBYTES 128
|
||||
#define CMDSIZE 128
|
||||
|
||||
// clang-format off
|
||||
enum fwcmd {
|
||||
|
@ -57,9 +50,6 @@ struct frame_header {
|
|||
};
|
||||
|
||||
/*@ -exportlocal @*/
|
||||
void writebyte(uint8_t b);
|
||||
uint8_t readbyte(uint8_t *mode, uint8_t *mode_bytes_left);
|
||||
void fwreply(struct frame_header hdr, enum fwcmd rspcode, uint8_t *buf);
|
||||
int readcommand(struct frame_header *hdr, uint8_t *cmd, int state,
|
||||
uint8_t *mode, uint8_t *mode_bytes_left);
|
||||
int readcommand(struct frame_header *hdr, uint8_t *cmd, int state);
|
||||
#endif
|
||||
|
|
87
hw/application_fpga/fw/tk1/qemu_firmware.lds
Normal file
87
hw/application_fpga/fw/tk1/qemu_firmware.lds
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright (C) 2022, 2023 - Tillitis AB
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
|
||||
OUTPUT_ARCH("riscv")
|
||||
ENTRY(_start)
|
||||
|
||||
/* Define stack size */
|
||||
STACK_SIZE = 3000;
|
||||
|
||||
MEMORY
|
||||
{
|
||||
ROM (rx) : ORIGIN = 0x00000000, LENGTH = 128k
|
||||
FWRAM (rw) : ORIGIN = 0xd0000000, LENGTH = 0xF00 /* 3840 B */
|
||||
RESETINFO (rw) : ORIGIN = 0xd0000F00, LENGTH = 0x100 /* 256 B (part of FW_RAM area) */
|
||||
RAM (rwx) : ORIGIN = 0x40000000, LENGTH = 0x20000 /* 128 KB */
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.text.init :
|
||||
{
|
||||
*(.text.init)
|
||||
} >ROM
|
||||
|
||||
.htif :
|
||||
{
|
||||
. = ALIGN(0x00000000);
|
||||
*(.htif)
|
||||
} >ROM
|
||||
|
||||
.text :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_stext = .;
|
||||
*(.text) /* .text sections (code) */
|
||||
*(.text*) /* .text* sections (code) */
|
||||
*(.rodata) /* .rodata sections (constants, strings, etc.) */
|
||||
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
|
||||
*(.srodata) /* .srodata sections (constants, strings, etc.) */
|
||||
*(.srodata*) /* .srodata* sections (constants, strings, etc.) */
|
||||
. = ALIGN(4);
|
||||
_etext = .;
|
||||
} >ROM
|
||||
|
||||
.stack (NOLOAD) :
|
||||
{
|
||||
. = ALIGN(16);
|
||||
_sstack = .;
|
||||
. += STACK_SIZE;
|
||||
. = ALIGN(16);
|
||||
_estack = .;
|
||||
} >FWRAM
|
||||
|
||||
.data :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_sdata = .;
|
||||
*(.data) /* .data sections */
|
||||
*(.data*) /* .data* sections */
|
||||
*(.sdata) /* .sdata sections */
|
||||
*(.sdata*) /* .sdata* sections */
|
||||
. = ALIGN(4);
|
||||
_edata = .;
|
||||
} >FWRAM AT>ROM
|
||||
_sidata = LOADADDR(.data);
|
||||
|
||||
/* Uninitialized data section */
|
||||
.bss :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_sbss = .;
|
||||
*(.bss)
|
||||
*(.bss*)
|
||||
*(.sbss)
|
||||
*(.sbss*)
|
||||
*(COMMON)
|
||||
. = ALIGN(4);
|
||||
_ebss = .;
|
||||
} >FWRAM
|
||||
}
|
||||
|
||||
_sfwram = ORIGIN(FWRAM);
|
||||
_efwram = ORIGIN(FWRAM) + LENGTH(FWRAM);
|
||||
_sresetinfo = ORIGIN(RESETINFO);
|
||||
_eresetinfo = ORIGIN(RESETINFO) + LENGTH(RESETINFO);
|
59
hw/application_fpga/fw/tk1/reset.c
Normal file
59
hw/application_fpga/fw/tk1/reset.c
Normal file
|
@ -0,0 +1,59 @@
|
|||
// Copyright (C) 2025 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <stdint.h>
|
||||
#include <tkey/assert.h>
|
||||
#include <tkey/lib.h>
|
||||
#include <tkey/tk1_mem.h>
|
||||
|
||||
#include "reset.h"
|
||||
|
||||
// clang-format off
|
||||
static volatile uint32_t *system_reset = (volatile uint32_t *)TK1_MMIO_TK1_SYSTEM_RESET;
|
||||
static volatile struct reset *resetinfo = (volatile struct reset *)TK1_MMIO_RESETINFO_BASE;
|
||||
// clang-format on
|
||||
|
||||
int reset(struct reset *userreset, size_t nextlen)
|
||||
{
|
||||
if ((uint32_t)userreset < TK1_RAM_BASE ||
|
||||
(uint32_t)userreset >= TK1_RAM_BASE + TK1_RAM_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (nextlen > sizeof(resetinfo->next_app_data)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
(void)memset((void *)resetinfo, 0, sizeof(*resetinfo));
|
||||
resetinfo->type = userreset->type;
|
||||
memcpy((void *)resetinfo->app_digest, userreset->app_digest,
|
||||
sizeof(resetinfo->app_digest));
|
||||
memcpy((void *)resetinfo->next_app_data, userreset->next_app_data,
|
||||
nextlen);
|
||||
|
||||
// Do the actual reset.
|
||||
*system_reset = 1;
|
||||
|
||||
// Should not be reached.
|
||||
assert(1 == 2);
|
||||
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
int reset_data(uint8_t *next_app_data)
|
||||
{
|
||||
if ((uint32_t)next_app_data < TK1_RAM_BASE ||
|
||||
(uint32_t)next_app_data >= TK1_RAM_BASE + TK1_RAM_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((uint32_t)next_app_data + RESET_DATA_SIZE >
|
||||
TK1_RAM_BASE + TK1_RAM_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(next_app_data, (void *)resetinfo->next_app_data,
|
||||
RESET_DATA_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
33
hw/application_fpga/fw/tk1/reset.h
Normal file
33
hw/application_fpga/fw/tk1/reset.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
// Copyright (C) 2025 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#ifndef TKEY_RESET_H
|
||||
#define TKEY_RESET_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define TK1_MMIO_RESETINFO_BASE 0xd0000f00
|
||||
#define TK1_MMIO_RESETINFO_SIZE 0x100
|
||||
#define RESET_DIGEST_SIZE 32
|
||||
#define RESET_DATA_SIZE 220
|
||||
|
||||
enum reset_start {
|
||||
START_DEFAULT = 0, // Probably cold boot
|
||||
START_FLASH0 = 1,
|
||||
START_FLASH1 = 2,
|
||||
START_FLASH0_VER = 3,
|
||||
START_FLASH1_VER = 4,
|
||||
START_CLIENT = 5,
|
||||
START_CLIENT_VER = 6,
|
||||
};
|
||||
|
||||
struct reset {
|
||||
enum reset_start type;
|
||||
uint8_t app_digest[RESET_DIGEST_SIZE];
|
||||
uint8_t next_app_data[RESET_DATA_SIZE];
|
||||
};
|
||||
|
||||
int reset(struct reset *userreset, size_t nextlen);
|
||||
int reset_data(uint8_t *next_app_data);
|
||||
#endif
|
28
hw/application_fpga/fw/tk1/rng.c
Normal file
28
hw/application_fpga/fw/tk1/rng.c
Normal file
|
@ -0,0 +1,28 @@
|
|||
// Copyright (C) 2024 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include "rng.h"
|
||||
#include <tkey/tk1_mem.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// clang-format off
|
||||
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) {
|
||||
}
|
||||
return *trng_entropy;
|
||||
}
|
||||
|
||||
uint32_t rng_xorwow(uint32_t state, uint32_t acc)
|
||||
{
|
||||
state ^= state << 13;
|
||||
state ^= state >> 17;
|
||||
state ^= state << 5;
|
||||
state += acc;
|
||||
return state;
|
||||
}
|
11
hw/application_fpga/fw/tk1/rng.h
Normal file
11
hw/application_fpga/fw/tk1/rng.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
// Copyright (C) 2024 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#ifndef RNG_H
|
||||
#define RNG_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
uint32_t rng_get_word(void);
|
||||
uint32_t rng_xorwow(uint32_t state, uint32_t acc);
|
||||
|
||||
#endif
|
100
hw/application_fpga/fw/tk1/spi.c
Normal file
100
hw/application_fpga/fw/tk1/spi.c
Normal file
|
@ -0,0 +1,100 @@
|
|||
// Copyright (C) 2024 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include "spi.h"
|
||||
#include <tkey/assert.h>
|
||||
#include <tkey/tk1_mem.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// clang-format off
|
||||
static volatile uint32_t *spi_en = (volatile uint32_t *)(TK1_MMIO_TK1_BASE | 0x200);
|
||||
static volatile uint32_t *spi_xfer = (volatile uint32_t *)(TK1_MMIO_TK1_BASE | 0x204);
|
||||
static volatile uint32_t *spi_data = (volatile uint32_t *)(TK1_MMIO_TK1_BASE | 0x208);
|
||||
// clang-format on
|
||||
|
||||
static int spi_ready(void);
|
||||
static void spi_enable(void);
|
||||
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
|
||||
// ready. This can be used to check if the SPI-master is available
|
||||
// in the hardware.
|
||||
static int spi_ready(void)
|
||||
{
|
||||
return *spi_xfer;
|
||||
}
|
||||
|
||||
static void spi_enable(void)
|
||||
{
|
||||
*spi_en = 1;
|
||||
}
|
||||
|
||||
static void spi_disable(void)
|
||||
{
|
||||
*spi_en = 0;
|
||||
}
|
||||
|
||||
static void spi_write(uint8_t *cmd, size_t size)
|
||||
{
|
||||
assert(cmd != NULL);
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
while (!spi_ready()) {
|
||||
}
|
||||
|
||||
*spi_data = cmd[i];
|
||||
*spi_xfer = 1;
|
||||
}
|
||||
|
||||
while (!spi_ready()) {
|
||||
}
|
||||
}
|
||||
|
||||
static void spi_read(uint8_t *buf, size_t size)
|
||||
{
|
||||
assert(buf != NULL);
|
||||
|
||||
while (!spi_ready()) {
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
|
||||
*spi_data = 0x00;
|
||||
*spi_xfer = 1;
|
||||
|
||||
// wait until spi master is done
|
||||
while (!spi_ready()) {
|
||||
}
|
||||
|
||||
buf[i] = (*spi_data & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to both read and write data to the connected SPI flash.
|
||||
int spi_transfer(uint8_t *cmd, size_t cmd_size, uint8_t *tx_buf, size_t tx_size,
|
||||
uint8_t *rx_buf, size_t rx_size)
|
||||
{
|
||||
if (cmd == NULL || cmd_size == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
spi_enable();
|
||||
|
||||
spi_write(cmd, cmd_size);
|
||||
|
||||
if (tx_buf != NULL && tx_size != 0) {
|
||||
spi_write(tx_buf, tx_size);
|
||||
}
|
||||
|
||||
if (rx_buf != NULL && rx_size != 0) {
|
||||
spi_read(rx_buf, rx_size);
|
||||
}
|
||||
|
||||
spi_disable();
|
||||
|
||||
return 0;
|
||||
}
|
13
hw/application_fpga/fw/tk1/spi.h
Normal file
13
hw/application_fpga/fw/tk1/spi.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
// Copyright (C) 2024 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#ifndef TKEY_SPI_H
|
||||
#define TKEY_SPI_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
int spi_transfer(uint8_t *cmd, size_t cmd_size, uint8_t *tx_buf, size_t tx_size,
|
||||
uint8_t *rx_buf, size_t rx_size);
|
||||
|
||||
#endif
|
|
@ -1,11 +1,117 @@
|
|||
/*
|
||||
* Copyright (C) 2022, 2023 - 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>
|
||||
|
||||
#ifdef QEMU_SYSCALL
|
||||
|
||||
#define picorv32_retirq_insn(...) \
|
||||
mv ra, x3; \
|
||||
ret
|
||||
|
||||
#else
|
||||
|
||||
#include "picorv32/custom_ops.S" // PicoRV32 custom instructions
|
||||
|
||||
#endif
|
||||
|
||||
#define illegal_insn() .word 0
|
||||
|
||||
// Variables in bss
|
||||
.lcomm irq_ret_addr, 4
|
||||
.lcomm app_sp, 4
|
||||
|
||||
.section ".text.init"
|
||||
.globl _start
|
||||
_start:
|
||||
j init
|
||||
|
||||
// IRQ handler
|
||||
.=0x10
|
||||
irq_handler:
|
||||
// PicoRV32 stores the IRQ bitmask in x4.
|
||||
// If bit 31 is 1: IRQ31 was triggered.
|
||||
li t4, (1 << 31)
|
||||
beq x4, t4, irq_source_ok
|
||||
unexpected_irq_source:
|
||||
illegal_insn()
|
||||
j unexpected_irq_source
|
||||
irq_source_ok:
|
||||
|
||||
// Save interrupt return address (x3)
|
||||
la t0, irq_ret_addr
|
||||
sw x3, 0(t0)
|
||||
|
||||
// Save app stack pointer. App is responsible for saving the rest of
|
||||
// the registers.
|
||||
la t0, app_sp
|
||||
sw sp, 0(t0)
|
||||
|
||||
// Setup firmware stack pointer
|
||||
la sp, _estack
|
||||
|
||||
// Run syscall handler
|
||||
call syscall_handler
|
||||
|
||||
// Restore app stack pointer
|
||||
la t0, app_sp
|
||||
lw sp, 0(t0)
|
||||
|
||||
// Restore interrupt return address (x3)
|
||||
la t0, irq_ret_addr
|
||||
lw x3, 0(t0)
|
||||
|
||||
// Verify that interrupt return address (x3) is in app RAM
|
||||
li t0, TK1_RAM_BASE // 0x40000000
|
||||
blt x3, t0, x3_invalid
|
||||
li t0, TK1_RAM_BASE + TK1_RAM_SIZE // 0x40020000
|
||||
bge x3, t0, x3_invalid
|
||||
j x3_valid
|
||||
x3_invalid:
|
||||
illegal_insn()
|
||||
j x3_invalid
|
||||
x3_valid:
|
||||
|
||||
// Remove data left over from the syscall handling
|
||||
mv x0, zero
|
||||
mv x1, zero
|
||||
// x2 (sp) is assumed to be preserved by the interrupt handler
|
||||
// x3 (interrupt return address) need to be preserved
|
||||
mv x4, zero
|
||||
mv x5, zero
|
||||
mv x6, zero
|
||||
mv x7, zero
|
||||
mv x8, zero
|
||||
mv x9, zero
|
||||
// x10 (a0) contains syscall return value. And should not be destroyed.
|
||||
mv x11, zero
|
||||
mv x12, zero
|
||||
mv x13, zero
|
||||
mv x14, zero
|
||||
mv x15, zero
|
||||
mv x16, zero
|
||||
mv x17, zero
|
||||
mv x18, zero
|
||||
mv x19, zero
|
||||
mv x20, zero
|
||||
mv x21, zero
|
||||
mv x22, zero
|
||||
mv x23, zero
|
||||
mv x24, zero
|
||||
mv x25, zero
|
||||
mv x26, zero
|
||||
mv x27, zero
|
||||
mv x28, zero
|
||||
mv x29, zero
|
||||
mv x30, zero
|
||||
mv x31, zero
|
||||
|
||||
picorv32_retirq_insn() // Return from interrupt
|
||||
|
||||
// Init
|
||||
|
||||
.=0x100
|
||||
init:
|
||||
li x1, 0
|
||||
li x2, 0
|
||||
li x3, 0
|
||||
|
@ -38,18 +144,25 @@ _start:
|
|||
li x30,0
|
||||
li x31,0
|
||||
|
||||
/* Clear FW_RAM */
|
||||
li a0, 0xd0000000 // TK1_MMIO_FW_RAM_BASE
|
||||
li a1, 0xd0000800 // TK1_MMIO_FW_RAM_BASE + TK1_MMIO_FW_RAM_SIZE
|
||||
// Clear FW_RAM
|
||||
la a0, _sfwram
|
||||
la a1, _efwram
|
||||
clear:
|
||||
sw zero, 0(a0)
|
||||
addi a0, a0, 4
|
||||
blt a0, a1, clear
|
||||
|
||||
/*
|
||||
* Init stack at top of fw_ram.
|
||||
*/
|
||||
li sp, 0xd0000800 // 2 kiB (TK1_MMIO_FW_RAM_SIZE)
|
||||
// Zero-init bss section
|
||||
la a0, _sbss
|
||||
la a1, _ebss
|
||||
|
||||
loop_init_bss:
|
||||
sw zero, 0(a0)
|
||||
addi a0, a0, 4
|
||||
blt a0, a1, loop_init_bss
|
||||
|
||||
// Init stack
|
||||
la sp, _estack
|
||||
|
||||
call main
|
||||
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
/*
|
||||
* 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
|
||||
|
||||
enum state {
|
||||
FW_STATE_INITIAL,
|
||||
FW_STATE_WAITCOMMAND,
|
||||
FW_STATE_LOADING,
|
||||
FW_STATE_RUN,
|
||||
FW_STATE_LOAD_FLASH,
|
||||
FW_STATE_LOAD_FLASH_MGMT,
|
||||
FW_STATE_START,
|
||||
FW_STATE_FAIL,
|
||||
FW_STATE_MAX,
|
||||
};
|
||||
|
|
317
hw/application_fpga/fw/tk1/storage.c
Normal file
317
hw/application_fpga/fw/tk1/storage.c
Normal file
|
@ -0,0 +1,317 @@
|
|||
// Copyright (C) 2024 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#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.
|
||||
static int get_first_empty(struct partition_table *part_table)
|
||||
{
|
||||
if (part_table == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < N_STORAGE_AREA; i++) {
|
||||
if (part_table->app_storage[i].status == 0x00) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int index_to_address(int index, uint32_t *address)
|
||||
{
|
||||
if (address == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((index < 0) || (index >= N_STORAGE_AREA)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*address = ADDR_STORAGE_AREA + index * SIZE_STORAGE_AREA;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Returns the index of the area an app has allocated.
|
||||
//
|
||||
// Returns -1 on errors.
|
||||
static int storage_get_area(struct partition_table *part_table)
|
||||
{
|
||||
if (part_table == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < N_STORAGE_AREA; i++) {
|
||||
if (part_table->app_storage[i].status != 0x00) {
|
||||
if (auth_app_authenticate(
|
||||
&part_table->app_storage[i].auth)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Allocate a new area for an app. Returns zero on success.
|
||||
int storage_allocate_area(struct partition_table_storage *part_table_storage)
|
||||
{
|
||||
if (part_table_storage == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct partition_table *part_table = &part_table_storage->table;
|
||||
|
||||
if (storage_get_area(part_table) != -1) {
|
||||
/* Already has an area */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int index = get_first_empty(part_table);
|
||||
if (index < 0) {
|
||||
/* No empty slot */
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t start_address = 0;
|
||||
|
||||
if (index_to_address(index, &start_address) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Allocate the empty index found
|
||||
// Erase area first
|
||||
|
||||
// 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
|
||||
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 0;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
struct partition_table *part_table = &part_table_storage->table;
|
||||
|
||||
int index = storage_get_area(part_table);
|
||||
if (index < 0) {
|
||||
// No area to deallocate
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t start_address = 0;
|
||||
if (index_to_address(index, &start_address) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Erase area first
|
||||
|
||||
// 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
|
||||
part_table->app_storage[index].status = 0;
|
||||
|
||||
(void)memset(part_table->app_storage[index].auth.nonce, 0x00,
|
||||
sizeof(part_table->app_storage[index].auth.nonce));
|
||||
|
||||
(void)memset(
|
||||
part_table->app_storage[index].auth.authentication_digest, 0x00,
|
||||
sizeof(part_table->app_storage[index].auth.authentication_digest));
|
||||
|
||||
if (part_table_write(part_table_storage) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Erases sector. Offset of a sector to begin erasing, must be a
|
||||
// multiple of the sector size. Size to erase in bytes, must be a
|
||||
// multiple of the sector size.
|
||||
//
|
||||
// Returns zero on success, negative error code on failure
|
||||
int storage_erase_sector(struct partition_table *part_table, uint32_t offset,
|
||||
size_t size)
|
||||
{
|
||||
if (part_table == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int index = storage_get_area(part_table);
|
||||
if (index == -1) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Cannot only erase entire sectors
|
||||
if (offset % 4096 != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Cannot erase less than one sector
|
||||
if (size < 4096 || size > SIZE_STORAGE_AREA || size % 4096 != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((offset + size) > SIZE_STORAGE_AREA) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t address = start_address + offset;
|
||||
|
||||
debug_puts("storage: erase addr: ");
|
||||
debug_putinthex(address);
|
||||
debug_lf();
|
||||
|
||||
for (size_t i = 0; i < size; i += 4096) {
|
||||
flash_sector_erase(address);
|
||||
address += 4096;
|
||||
}
|
||||
|
||||
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.
|
||||
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;
|
||||
}
|
||||
|
||||
int index = storage_get_area(part_table);
|
||||
if (index == -1) {
|
||||
// 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 > SIZE_STORAGE_AREA) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((offset + size) > SIZE_STORAGE_AREA) {
|
||||
// Writing outside of area
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t address = start_address + offset;
|
||||
|
||||
debug_puts("storage: write to addr: ");
|
||||
debug_putinthex(address);
|
||||
debug_lf();
|
||||
|
||||
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.
|
||||
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;
|
||||
}
|
||||
|
||||
int index = storage_get_area(part_table);
|
||||
if (index == -1) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
if ((offset + size) > SIZE_STORAGE_AREA) {
|
||||
// Reading outside of area
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t address = start_address + offset;
|
||||
|
||||
debug_puts("storage: read from addr: ");
|
||||
debug_putinthex(address);
|
||||
debug_lf();
|
||||
|
||||
return flash_read_data(address, data, size);
|
||||
}
|
22
hw/application_fpga/fw/tk1/storage.h
Normal file
22
hw/application_fpga/fw/tk1/storage.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
// Copyright (C) 2024 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#ifndef STORAGE_H
|
||||
#define STORAGE_H
|
||||
|
||||
#include "partition_table.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
int storage_deallocate_area(struct partition_table_storage *part_table_storage);
|
||||
int storage_allocate_area(struct partition_table_storage *part_table_storage);
|
||||
int storage_erase_sector(struct partition_table *part_table, uint32_t offset,
|
||||
size_t size);
|
||||
int storage_write_data(struct partition_table *part_table, uint32_t offset,
|
||||
uint8_t *data, size_t size);
|
||||
int storage_read_data(struct partition_table *part_table, uint32_t offset,
|
||||
uint8_t *data, size_t size);
|
||||
|
||||
#endif
|
23
hw/application_fpga/fw/tk1/syscall_enable.S
Normal file
23
hw/application_fpga/fw/tk1/syscall_enable.S
Normal file
|
@ -0,0 +1,23 @@
|
|||
// SPDX-FileCopyrightText: 2025 Tillitis AB <tillitis.se>
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#ifdef QEMU_SYSCALL
|
||||
|
||||
#define picorv32_maskirq_insn(...)
|
||||
|
||||
#else
|
||||
|
||||
#include "../tk1/picorv32/custom_ops.S"
|
||||
|
||||
#endif
|
||||
|
||||
.section ".text"
|
||||
.globl syscall_enable
|
||||
|
||||
|
||||
syscall_enable:
|
||||
// Enable syscall IRQ
|
||||
li t0, 0x7fffffff // IRQ31 mask
|
||||
picorv32_maskirq_insn(zero, t0) // Enable IRQs
|
||||
|
||||
ret
|
9
hw/application_fpga/fw/tk1/syscall_enable.h
Normal file
9
hw/application_fpga/fw/tk1/syscall_enable.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
// Copyright (C) 2025 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#ifndef TKEY_SYSCALL_ENABLE_H
|
||||
#define TKEY_SYSCALL_ENABLE_H
|
||||
|
||||
void syscall_enable(void);
|
||||
|
||||
#endif
|
112
hw/application_fpga/fw/tk1/syscall_handler.c
Normal file
112
hw/application_fpga/fw/tk1/syscall_handler.c
Normal file
|
@ -0,0 +1,112 @@
|
|||
// Copyright (C) 2025 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <stdint.h>
|
||||
#include <tkey/assert.h>
|
||||
#include <tkey/debug.h>
|
||||
#include <tkey/lib.h>
|
||||
#include <tkey/tk1_mem.h>
|
||||
|
||||
#include "partition_table.h"
|
||||
#include "preload_app.h"
|
||||
#include "reset.h"
|
||||
#include "storage.h"
|
||||
#include "syscall_num.h"
|
||||
|
||||
// clang-format off
|
||||
static volatile uint32_t *udi = (volatile uint32_t *)TK1_MMIO_TK1_UDI_FIRST;
|
||||
// clang-format on
|
||||
|
||||
extern struct partition_table_storage part_table_storage;
|
||||
extern uint8_t part_status;
|
||||
|
||||
int32_t syscall_handler(uint32_t number, uint32_t arg1, uint32_t arg2,
|
||||
uint32_t arg3)
|
||||
{
|
||||
switch (number) {
|
||||
case TK1_SYSCALL_RESET:
|
||||
return reset((struct reset *)arg1, (size_t)arg2);
|
||||
break;
|
||||
|
||||
case TK1_SYSCALL_ALLOC_AREA:
|
||||
if (storage_allocate_area(&part_table_storage) < 0) {
|
||||
debug_puts("couldn't allocate storage area\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case TK1_SYSCALL_DEALLOC_AREA:
|
||||
if (storage_deallocate_area(&part_table_storage) < 0) {
|
||||
debug_puts("couldn't deallocate storage area\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case TK1_SYSCALL_WRITE_DATA:
|
||||
if (storage_write_data(&part_table_storage.table, arg1,
|
||||
(uint8_t *)arg2, arg3) < 0) {
|
||||
debug_puts("couldn't write storage area\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case TK1_SYSCALL_READ_DATA:
|
||||
if (storage_read_data(&part_table_storage.table, arg1,
|
||||
(uint8_t *)arg2, arg3) < 0) {
|
||||
debug_puts("couldn't read storage area\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case TK1_SYSCALL_ERASE_DATA:
|
||||
if (storage_erase_sector(&part_table_storage.table, arg1,
|
||||
arg2) < 0) {
|
||||
debug_puts("couldn't erase storage area\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case TK1_SYSCALL_GET_VIDPID:
|
||||
// UDI is 2 words: VID/PID & serial. Return just the
|
||||
// first word. Serial is kept secret to the device
|
||||
// app.
|
||||
return udi[0];
|
||||
|
||||
case TK1_SYSCALL_PRELOAD_DELETE:
|
||||
return preload_delete(&part_table_storage, 1);
|
||||
|
||||
case TK1_SYSCALL_PRELOAD_STORE:
|
||||
// arg1 offset
|
||||
// arg2 data
|
||||
// arg3 size
|
||||
// always using slot 1
|
||||
return preload_store(&part_table_storage.table, arg1,
|
||||
(uint8_t *)arg2, arg3, 1);
|
||||
|
||||
case TK1_SYSCALL_PRELOAD_STORE_FIN:
|
||||
// arg1 app_size
|
||||
// arg2 app_digest
|
||||
// arg3 app_signature
|
||||
// always using slot 1
|
||||
return preload_store_finalize(&part_table_storage, arg1,
|
||||
(uint8_t *)arg2, (uint8_t *)arg3,
|
||||
1);
|
||||
|
||||
case TK1_SYSCALL_PRELOAD_GET_DIGSIG:
|
||||
return preload_get_digsig(&part_table_storage.table,
|
||||
(uint8_t *)arg1, (uint8_t *)arg2, 1);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
assert(1 == 2);
|
||||
return -1; // This should never run
|
||||
}
|
24
hw/application_fpga/fw/tk1/syscall_num.h
Normal file
24
hw/application_fpga/fw/tk1/syscall_num.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
// Copyright (C) 2025 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#ifndef TKEY_SYSCALL_NUM_H
|
||||
#define TKEY_SYSCALL_NUM_H
|
||||
|
||||
enum syscall_num {
|
||||
TK1_SYSCALL_RESET = 1,
|
||||
TK1_SYSCALL_ALLOC_AREA = 2,
|
||||
TK1_SYSCALL_DEALLOC_AREA = 3,
|
||||
TK1_SYSCALL_WRITE_DATA = 4,
|
||||
TK1_SYSCALL_READ_DATA = 5,
|
||||
TK1_SYSCALL_ERASE_DATA = 6,
|
||||
TK1_SYSCALL_GET_VIDPID = 7,
|
||||
TK1_SYSCALL_PRELOAD_STORE = 8,
|
||||
TK1_SYSCALL_PRELOAD_STORE_FIN = 9,
|
||||
TK1_SYSCALL_PRELOAD_DELETE = 10,
|
||||
TK1_SYSCALL_PRELOAD_GET_DIGSIG = 11,
|
||||
TK1_SYSCALL_REG_MGMT = 12,
|
||||
TK1_SYSCALL_STATUS = 13,
|
||||
TK1_SYSCALL_GET_APP_DATA = 14,
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2022 - Tillitis AB
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef TYPES_H
|
||||
#define TYPES_H
|
||||
|
||||
typedef unsigned int uintptr_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef int int32_t;
|
||||
typedef long long int64_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned long size_t;
|
||||
|
||||
#define NULL ((char *)0)
|
||||
|
||||
#define FALSE 0
|
||||
#define TRUE !FALSE
|
||||
|
||||
#endif
|
|
@ -57,11 +57,13 @@ module application_fpga (
|
|||
localparam UART_PREFIX = 6'h03;
|
||||
localparam TOUCH_SENSE_PREFIX = 6'h04;
|
||||
localparam FW_RAM_PREFIX = 6'h10;
|
||||
localparam SYSCALL_PREFIX = 6'h21;
|
||||
localparam TK1_PREFIX = 6'h3f;
|
||||
|
||||
// Instruction used to cause a trap.
|
||||
localparam ILLEGAL_INSTRUCTION = 32'h0;
|
||||
|
||||
localparam IRQ31_IRQ_MASK = 2 ** 31;
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Registers, memories with associated wires.
|
||||
|
@ -80,16 +82,18 @@ module application_fpga (
|
|||
wire reset_n;
|
||||
|
||||
/* verilator lint_off UNOPTFLAT */
|
||||
reg [31 : 0] cpu_irq;
|
||||
wire cpu_trap;
|
||||
wire cpu_valid;
|
||||
wire cpu_instr;
|
||||
wire [03 : 0] cpu_wstrb;
|
||||
/* verilator lint_off UNUSED */
|
||||
wire [31 : 0] cpu_eoi;
|
||||
wire [31 : 0] cpu_addr;
|
||||
wire [31 : 0] cpu_wdata;
|
||||
|
||||
reg rom_cs;
|
||||
reg [11 : 0] rom_address;
|
||||
reg [10 : 0] rom_address;
|
||||
wire [31 : 0] rom_read_data;
|
||||
wire rom_ready;
|
||||
|
||||
|
@ -128,7 +132,7 @@ module application_fpga (
|
|||
|
||||
reg fw_ram_cs;
|
||||
reg [ 3 : 0] fw_ram_we;
|
||||
reg [ 8 : 0] fw_ram_address;
|
||||
reg [ 9 : 0] fw_ram_address;
|
||||
reg [31 : 0] fw_ram_write_data;
|
||||
wire [31 : 0] fw_ram_read_data;
|
||||
wire fw_ram_ready;
|
||||
|
@ -139,13 +143,18 @@ module application_fpga (
|
|||
wire [31 : 0] touch_sense_read_data;
|
||||
wire touch_sense_ready;
|
||||
|
||||
reg irq31_cs;
|
||||
reg irq31_we;
|
||||
reg irq31_eoi;
|
||||
|
||||
reg tk1_cs;
|
||||
reg tk1_we;
|
||||
reg [ 7 : 0] tk1_address;
|
||||
reg [31 : 0] tk1_write_data;
|
||||
wire [31 : 0] tk1_read_data;
|
||||
wire tk1_ready;
|
||||
wire system_mode;
|
||||
wire app_mode;
|
||||
wire fw_startup_done;
|
||||
wire force_trap;
|
||||
wire [14 : 0] ram_addr_rand;
|
||||
wire [31 : 0] ram_data_rand;
|
||||
|
@ -171,7 +180,12 @@ module application_fpga (
|
|||
.CATCH_MISALIGN (0),
|
||||
.COMPRESSED_ISA (1),
|
||||
.ENABLE_FAST_MUL (1),
|
||||
.BARREL_SHIFTER (1)
|
||||
.BARREL_SHIFTER (1),
|
||||
.ENABLE_IRQ (1),
|
||||
.ENABLE_IRQ_QREGS(0),
|
||||
.ENABLE_IRQ_TIMER(0),
|
||||
.MASKED_IRQ (~IRQ31_IRQ_MASK),
|
||||
.LATCHED_IRQ (IRQ31_IRQ_MASK)
|
||||
) cpu (
|
||||
.clk(clk),
|
||||
.resetn(reset_n),
|
||||
|
@ -185,11 +199,12 @@ module application_fpga (
|
|||
.mem_rdata(muxed_rdata_reg),
|
||||
.mem_instr(cpu_instr),
|
||||
|
||||
.irq(cpu_irq),
|
||||
.eoi(cpu_eoi),
|
||||
|
||||
// Defined unused ports. Makes lint happy. But
|
||||
// we still needs to help lint with empty ports.
|
||||
/* verilator lint_off PINCONNECTEMPTY */
|
||||
.irq(32'h0),
|
||||
.eoi(),
|
||||
.trace_valid(),
|
||||
.trace_data(),
|
||||
.mem_la_read(),
|
||||
|
@ -240,7 +255,7 @@ module application_fpga (
|
|||
.clk(clk),
|
||||
.reset_n(reset_n),
|
||||
|
||||
.system_mode(system_mode),
|
||||
.app_mode(app_mode),
|
||||
|
||||
.cs(fw_ram_cs),
|
||||
.we(fw_ram_we),
|
||||
|
@ -280,7 +295,7 @@ module application_fpga (
|
|||
.clk(clk),
|
||||
.reset_n(reset_n),
|
||||
|
||||
.system_mode(system_mode),
|
||||
.en(~fw_startup_done),
|
||||
|
||||
.cs(uds_cs),
|
||||
.address(uds_address),
|
||||
|
@ -326,7 +341,8 @@ module application_fpga (
|
|||
.clk(clk),
|
||||
.reset_n(reset_n),
|
||||
|
||||
.system_mode(system_mode),
|
||||
.app_mode(app_mode),
|
||||
.fw_startup_done(fw_startup_done),
|
||||
|
||||
.cpu_addr (cpu_addr),
|
||||
.cpu_instr (cpu_instr),
|
||||
|
@ -353,6 +369,8 @@ module application_fpga (
|
|||
.gpio3(app_gpio3),
|
||||
.gpio4(app_gpio4),
|
||||
|
||||
.syscall(irq31_eoi),
|
||||
|
||||
.cs(tk1_cs),
|
||||
.we(tk1_we),
|
||||
.address(tk1_address),
|
||||
|
@ -379,6 +397,20 @@ module application_fpga (
|
|||
end
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// irq_ctrl
|
||||
// Interrupt logic
|
||||
//----------------------------------------------------------------
|
||||
always @* begin : irq_ctrl
|
||||
reg irq31_set;
|
||||
|
||||
irq31_set = irq31_cs & irq31_we;
|
||||
cpu_irq = {irq31_set, 31'h0};
|
||||
|
||||
irq31_eoi = cpu_eoi[31];
|
||||
end
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// cpu_mem_ctrl
|
||||
// CPU memory decode and control logic.
|
||||
|
@ -394,7 +426,7 @@ module application_fpga (
|
|||
muxed_rdata_new = 32'h0;
|
||||
|
||||
rom_cs = 1'h0;
|
||||
rom_address = cpu_addr[13 : 2];
|
||||
rom_address = cpu_addr[12 : 2];
|
||||
|
||||
ram_cs = 1'h0;
|
||||
ram_we = 4'h0;
|
||||
|
@ -403,7 +435,7 @@ module application_fpga (
|
|||
|
||||
fw_ram_cs = 1'h0;
|
||||
fw_ram_we = cpu_wstrb;
|
||||
fw_ram_address = cpu_addr[10 : 2];
|
||||
fw_ram_address = cpu_addr[11 : 2];
|
||||
fw_ram_write_data = cpu_wdata;
|
||||
|
||||
trng_cs = 1'h0;
|
||||
|
@ -428,6 +460,9 @@ module application_fpga (
|
|||
touch_sense_we = |cpu_wstrb;
|
||||
touch_sense_address = cpu_addr[9 : 2];
|
||||
|
||||
irq31_cs = 1'h0;
|
||||
irq31_we = |cpu_wstrb;
|
||||
|
||||
tk1_cs = 1'h0;
|
||||
tk1_we = |cpu_wstrb;
|
||||
tk1_address = cpu_addr[9 : 2];
|
||||
|
@ -500,6 +535,11 @@ module application_fpga (
|
|||
muxed_ready_new = fw_ram_ready;
|
||||
end
|
||||
|
||||
SYSCALL_PREFIX: begin
|
||||
irq31_cs = 1'h1;
|
||||
muxed_ready_new = 1'h1;
|
||||
end
|
||||
|
||||
TK1_PREFIX: begin
|
||||
tk1_cs = 1'h1;
|
||||
muxed_rdata_new = tk1_read_data;
|
||||
|
|
|
@ -70,11 +70,13 @@ module application_fpga_sim (
|
|||
localparam UART_PREFIX = 6'h03;
|
||||
localparam TOUCH_SENSE_PREFIX = 6'h04;
|
||||
localparam FW_RAM_PREFIX = 6'h10;
|
||||
localparam SYSCALL_PREFIX = 6'h21;
|
||||
localparam TK1_PREFIX = 6'h3f;
|
||||
|
||||
// Instruction used to cause a trap.
|
||||
localparam ILLEGAL_INSTRUCTION = 32'h0;
|
||||
|
||||
localparam IRQ31_IRQ_MASK = 2 ** 31;
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Registers, memories with associated wires.
|
||||
|
@ -92,16 +94,18 @@ module application_fpga_sim (
|
|||
wire reset_n;
|
||||
|
||||
/* verilator lint_off UNOPTFLAT */
|
||||
reg [31 : 0] cpu_irq;
|
||||
wire cpu_trap;
|
||||
wire cpu_valid;
|
||||
wire cpu_instr;
|
||||
wire [ 3 : 0] cpu_wstrb;
|
||||
/* verilator lint_off UNUSED */
|
||||
wire [31 : 0] cpu_eoi;
|
||||
wire [31 : 0] cpu_addr;
|
||||
wire [31 : 0] cpu_wdata;
|
||||
|
||||
reg rom_cs;
|
||||
reg [11 : 0] rom_address;
|
||||
reg [10 : 0] rom_address;
|
||||
wire [31 : 0] rom_read_data;
|
||||
wire rom_ready;
|
||||
|
||||
|
@ -140,7 +144,7 @@ module application_fpga_sim (
|
|||
|
||||
reg fw_ram_cs;
|
||||
reg [ 3 : 0] fw_ram_we;
|
||||
reg [ 8 : 0] fw_ram_address;
|
||||
reg [ 9 : 0] fw_ram_address;
|
||||
reg [31 : 0] fw_ram_write_data;
|
||||
wire [31 : 0] fw_ram_read_data;
|
||||
wire fw_ram_ready;
|
||||
|
@ -151,13 +155,18 @@ module application_fpga_sim (
|
|||
wire [31 : 0] touch_sense_read_data;
|
||||
wire touch_sense_ready;
|
||||
|
||||
reg irq31_cs;
|
||||
reg irq31_we;
|
||||
reg irq31_eoi;
|
||||
|
||||
reg tk1_cs;
|
||||
reg tk1_we;
|
||||
reg [ 7 : 0] tk1_address;
|
||||
reg [31 : 0] tk1_write_data;
|
||||
wire [31 : 0] tk1_read_data;
|
||||
wire tk1_ready;
|
||||
wire system_mode;
|
||||
wire app_mode;
|
||||
wire fw_startup_done;
|
||||
wire force_trap;
|
||||
wire [14 : 0] ram_addr_rand;
|
||||
wire [31 : 0] ram_data_rand;
|
||||
|
@ -182,7 +191,12 @@ module application_fpga_sim (
|
|||
.CATCH_MISALIGN (0),
|
||||
.COMPRESSED_ISA (1),
|
||||
.ENABLE_FAST_MUL (1),
|
||||
.BARREL_SHIFTER (1)
|
||||
.BARREL_SHIFTER (1),
|
||||
.ENABLE_IRQ (1),
|
||||
.ENABLE_IRQ_QREGS(0),
|
||||
.ENABLE_IRQ_TIMER(0),
|
||||
.MASKED_IRQ (~IRQ31_IRQ_MASK),
|
||||
.LATCHED_IRQ (IRQ31_IRQ_MASK)
|
||||
) cpu (
|
||||
.clk(clk),
|
||||
.resetn(reset_n),
|
||||
|
@ -196,11 +210,12 @@ module application_fpga_sim (
|
|||
.mem_rdata(muxed_rdata_reg),
|
||||
.mem_instr(cpu_instr),
|
||||
|
||||
.irq(cpu_irq),
|
||||
.eoi(cpu_eoi),
|
||||
|
||||
// Defined unused ports. Makes lint happy. But
|
||||
// we still needs to help lint with empty ports.
|
||||
/* verilator lint_off PINCONNECTEMPTY */
|
||||
.irq(32'h0),
|
||||
.eoi(),
|
||||
.trace_valid(),
|
||||
.trace_data(),
|
||||
.mem_la_read(),
|
||||
|
@ -251,7 +266,7 @@ module application_fpga_sim (
|
|||
.clk(clk),
|
||||
.reset_n(reset_n),
|
||||
|
||||
.system_mode(system_mode),
|
||||
.app_mode(app_mode),
|
||||
|
||||
.cs(fw_ram_cs),
|
||||
.we(fw_ram_we),
|
||||
|
@ -291,7 +306,7 @@ module application_fpga_sim (
|
|||
.clk(clk),
|
||||
.reset_n(reset_n),
|
||||
|
||||
.system_mode(system_mode),
|
||||
.en(~fw_startup_done),
|
||||
|
||||
.cs(uds_cs),
|
||||
.address(uds_address),
|
||||
|
@ -339,7 +354,8 @@ module application_fpga_sim (
|
|||
.clk(clk),
|
||||
.reset_n(reset_n),
|
||||
|
||||
.system_mode(system_mode),
|
||||
.app_mode(app_mode),
|
||||
.fw_startup_done(fw_startup_done),
|
||||
|
||||
.cpu_addr (cpu_addr),
|
||||
.cpu_instr (cpu_instr),
|
||||
|
@ -366,6 +382,8 @@ module application_fpga_sim (
|
|||
.gpio3(app_gpio3),
|
||||
.gpio4(app_gpio4),
|
||||
|
||||
.syscall(irq31_eoi),
|
||||
|
||||
.cs(tk1_cs),
|
||||
.we(tk1_we),
|
||||
.address(tk1_address),
|
||||
|
@ -391,6 +409,20 @@ module application_fpga_sim (
|
|||
end
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// irq_ctrl
|
||||
// Interrupt logic
|
||||
//----------------------------------------------------------------
|
||||
always @* begin : irq_ctrl
|
||||
reg irq31_set;
|
||||
|
||||
irq31_set = irq31_cs & irq31_we;
|
||||
cpu_irq = {irq31_set, 31'h0};
|
||||
|
||||
irq31_eoi = cpu_eoi[31];
|
||||
end
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// cpu_mem_ctrl
|
||||
// CPU memory decode and control logic.
|
||||
|
@ -408,7 +440,7 @@ module application_fpga_sim (
|
|||
muxed_rdata_new = 32'h0;
|
||||
|
||||
rom_cs = 1'h0;
|
||||
rom_address = cpu_addr[13 : 2];
|
||||
rom_address = cpu_addr[12 : 2];
|
||||
|
||||
ram_cs = 1'h0;
|
||||
ram_we = 4'h0;
|
||||
|
@ -417,7 +449,7 @@ module application_fpga_sim (
|
|||
|
||||
fw_ram_cs = 1'h0;
|
||||
fw_ram_we = cpu_wstrb;
|
||||
fw_ram_address = cpu_addr[10 : 2];
|
||||
fw_ram_address = cpu_addr[11 : 2];
|
||||
fw_ram_write_data = cpu_wdata;
|
||||
|
||||
trng_cs = 1'h0;
|
||||
|
@ -442,6 +474,9 @@ module application_fpga_sim (
|
|||
touch_sense_we = |cpu_wstrb;
|
||||
touch_sense_address = cpu_addr[9 : 2];
|
||||
|
||||
irq31_cs = 1'h0;
|
||||
irq31_we = |cpu_wstrb;
|
||||
|
||||
tk1_cs = 1'h0;
|
||||
tk1_we = |cpu_wstrb;
|
||||
tk1_address = cpu_addr[9 : 2];
|
||||
|
@ -534,6 +569,13 @@ module application_fpga_sim (
|
|||
muxed_ready_new = fw_ram_ready;
|
||||
end
|
||||
|
||||
SYSCALL_PREFIX: begin
|
||||
`verbose($display("Access to syscall interrupt trigger");)
|
||||
ascii_state = "Syscall IRQ trigger";
|
||||
irq31_cs = 1'h1;
|
||||
muxed_ready_new = 1'h1;
|
||||
end
|
||||
|
||||
TK1_PREFIX: begin
|
||||
`verbose($display("Access to TK1 core");)
|
||||
ascii_state = "TK1 core";
|
||||
|
|
26
hw/application_fpga/tkey-libs/LICENSE
Normal file
26
hw/application_fpga/tkey-libs/LICENSE
Normal file
|
@ -0,0 +1,26 @@
|
|||
BSD 2-Clause License
|
||||
|
||||
Copyright 2022 Tillitis AB <tillitis.se>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
24
hw/application_fpga/tkey-libs/LICENSES/BSD-2-Clause.txt
Normal file
24
hw/application_fpga/tkey-libs/LICENSES/BSD-2-Clause.txt
Normal file
|
@ -0,0 +1,24 @@
|
|||
Copyright 2022 Tillitis AB <tillitis.se>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
121
hw/application_fpga/tkey-libs/LICENSES/CC0-1.0.txt
Normal file
121
hw/application_fpga/tkey-libs/LICENSES/CC0-1.0.txt
Normal file
|
@ -0,0 +1,121 @@
|
|||
Creative Commons Legal Code
|
||||
|
||||
CC0 1.0 Universal
|
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||
HEREUNDER.
|
||||
|
||||
Statement of Purpose
|
||||
|
||||
The laws of most jurisdictions throughout the world automatically confer
|
||||
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||
authorship and/or a database (each, a "Work").
|
||||
|
||||
Certain owners wish to permanently relinquish those rights to a Work for
|
||||
the purpose of contributing to a commons of creative, cultural and
|
||||
scientific works ("Commons") that the public can reliably and without fear
|
||||
of later claims of infringement build upon, modify, incorporate in other
|
||||
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||
and for any purposes, including without limitation commercial purposes.
|
||||
These owners may contribute to the Commons to promote the ideal of a free
|
||||
culture and the further production of creative, cultural and scientific
|
||||
works, or to gain reputation or greater distribution for their Work in
|
||||
part through the use and efforts of others.
|
||||
|
||||
For these and/or other purposes and motivations, and without any
|
||||
expectation of additional consideration or compensation, the person
|
||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||
protected by copyright and related or neighboring rights ("Copyright and
|
||||
Related Rights"). Copyright and Related Rights include, but are not
|
||||
limited to, the following:
|
||||
|
||||
i. the right to reproduce, adapt, distribute, perform, display,
|
||||
communicate, and translate a Work;
|
||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||
iii. publicity and privacy rights pertaining to a person's image or
|
||||
likeness depicted in a Work;
|
||||
iv. rights protecting against unfair competition in regards to a Work,
|
||||
subject to the limitations in paragraph 4(a), below;
|
||||
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||
in a Work;
|
||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||
European Parliament and of the Council of 11 March 1996 on the legal
|
||||
protection of databases, and under any national implementation
|
||||
thereof, including any amended or successor version of such
|
||||
directive); and
|
||||
vii. other similar, equivalent or corresponding rights throughout the
|
||||
world based on applicable law or treaty, and any national
|
||||
implementations thereof.
|
||||
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||
of action, whether now known or unknown (including existing as well as
|
||||
future claims and causes of action), in the Work (i) in all territories
|
||||
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||
treaty (including future time extensions), (iii) in any current or future
|
||||
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||
including without limitation commercial, advertising or promotional
|
||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||
member of the public at large and to the detriment of Affirmer's heirs and
|
||||
successors, fully intending that such Waiver shall not be subject to
|
||||
revocation, rescission, cancellation, termination, or any other legal or
|
||||
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||
as contemplated by Affirmer's express Statement of Purpose.
|
||||
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||
be judged legally invalid or ineffective under applicable law, then the
|
||||
Waiver shall be preserved to the maximum extent permitted taking into
|
||||
account Affirmer's express Statement of Purpose. In addition, to the
|
||||
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||
maximum duration provided by applicable law or treaty (including future
|
||||
time extensions), (iii) in any current or future medium and for any number
|
||||
of copies, and (iv) for any purpose whatsoever, including without
|
||||
limitation commercial, advertising or promotional purposes (the
|
||||
"License"). The License shall be deemed effective as of the date CC0 was
|
||||
applied by Affirmer to the Work. Should any part of the License for any
|
||||
reason be judged legally invalid or ineffective under applicable law, such
|
||||
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||
of the License, and in such case Affirmer hereby affirms that he or she
|
||||
will not (i) exercise any of his or her remaining Copyright and Related
|
||||
Rights in the Work or (ii) assert any associated claims and causes of
|
||||
action with respect to the Work, in either case contrary to Affirmer's
|
||||
express Statement of Purpose.
|
||||
|
||||
4. Limitations and Disclaimers.
|
||||
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||
surrendered, licensed or otherwise affected by this document.
|
||||
b. Affirmer offers the Work as-is and makes no representations or
|
||||
warranties of any kind concerning the Work, express, implied,
|
||||
statutory or otherwise, including without limitation warranties of
|
||||
title, merchantability, fitness for a particular purpose, non
|
||||
infringement, or the absence of latent or other defects, accuracy, or
|
||||
the present or absence of errors, whether or not discoverable, all to
|
||||
the greatest extent permissible under applicable law.
|
||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||
that may apply to the Work or any use thereof, including without
|
||||
limitation any person's Copyright and Related Rights in the Work.
|
||||
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||
consents, permissions or other rights required for any use of the
|
||||
Work.
|
||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||
party to this document and has no duty or obligation with respect to
|
||||
this CC0 or use of the Work.
|
101
hw/application_fpga/tkey-libs/Makefile
Normal file
101
hw/application_fpga/tkey-libs/Makefile
Normal file
|
@ -0,0 +1,101 @@
|
|||
OBJCOPY ?= llvm-objcopy
|
||||
|
||||
CC = clang
|
||||
|
||||
INCLUDE=include
|
||||
|
||||
# Set QEMU_DEBUG and TKEY_DEBUG below when compiling tkey-libs if you
|
||||
# want debug prints from tkey-libs functions.
|
||||
#
|
||||
# - QEMU_DEBUG: the debug port on our qemu emulator
|
||||
#
|
||||
# - TKEY_DEBUG: The extra HID endpoint on a real TKey which you can
|
||||
# listen on for debug prints.
|
||||
#
|
||||
# NOTE WELL: If you just want debug prints on either of them in *your
|
||||
# own device app* you just need to include tkey/debug.h and define
|
||||
# either of them. You don't need to recompile tkey-libs.
|
||||
|
||||
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 -nostdlib -mno-relax -flto \
|
||||
-Wall -Werror=implicit-function-declaration \
|
||||
-I $(INCLUDE) -I .
|
||||
|
||||
AS = clang
|
||||
AR = llvm-ar
|
||||
ASFLAGS = -target riscv32-unknown-none-elf -march=rv32iczmmul -mabi=ilp32 \
|
||||
-mcmodel=medany -mno-relax
|
||||
|
||||
LDFLAGS=-T app.lds -L libcommon/ -lcommon -L libcrt0/ -lcrt0
|
||||
|
||||
|
||||
.PHONY: all
|
||||
all: libcrt0.a libcommon.a libmonocypher.a libblake2s.a
|
||||
|
||||
IMAGE=ghcr.io/tillitis/tkey-builder:4
|
||||
|
||||
podman:
|
||||
podman run --rm --mount type=bind,source=$(CURDIR),target=/src \
|
||||
-w /src -it $(IMAGE) make -j
|
||||
|
||||
.PHONY: check
|
||||
check:
|
||||
clang-tidy -header-filter=.* -checks=cert-* libcommon/*.c -- $(CFLAGS)
|
||||
|
||||
# C runtime library
|
||||
libcrt0.a: libcrt0/crt0.o
|
||||
$(AR) -qc $@ libcrt0/crt0.o
|
||||
|
||||
# Common C functions
|
||||
LIBOBJS=libcommon/assert.o libcommon/led.o libcommon/lib.o \
|
||||
libcommon/proto.o libcommon/touch.o libcommon/io.o
|
||||
|
||||
libcommon.a: $(LIBOBJS)
|
||||
$(AR) -qc $@ $(LIBOBJS)
|
||||
$(LIBOBJS): include/tkey/assert.h include/tkey/led.h \
|
||||
include/tkey/lib.h include/tkey/proto.h include/tkey/tk1_mem.h \
|
||||
include/tkey/touch.h include/tkey/debug.h
|
||||
|
||||
# Monocypher
|
||||
MONOOBJS=monocypher/monocypher.o monocypher/monocypher-ed25519.o
|
||||
libmonocypher.a: $(MONOOBJS)
|
||||
$(AR) -qc $@ $(MONOOBJS)
|
||||
$MONOOBJS: monocypher/monocypher-ed25519.h monocypher/monocypher.h
|
||||
|
||||
# blake2s
|
||||
B2OBJS=blake2s/blake2s.o
|
||||
libblake2s.a: $(B2OBJS)
|
||||
$(AR) -qc $@ $(B2OBJS)
|
||||
$B2OBJS: blake2s/blake2s.h
|
||||
|
||||
LIBS=libcrt0.a libcommon.a
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f $(LIBS) $(LIBOBJS) libcrt0/crt0.o
|
||||
rm -f libmonocypher.a $(MONOOBJS)
|
||||
rm -f libblake2s.a $(B2OBJS)
|
||||
|
||||
# Create compile_commands.json for clangd and LSP
|
||||
.PHONY: clangd
|
||||
clangd: compile_commands.json
|
||||
compile_commands.json:
|
||||
$(MAKE) clean
|
||||
bear -- make all
|
||||
|
||||
# Uses ../.clang-format
|
||||
FMTFILES=include/tkey/*.h libcommon/*.c
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
clang-format --dry-run --ferror-limit=0 $(FMTFILES)
|
||||
clang-format --verbose -i $(FMTFILES)
|
||||
.PHONY: checkfmt
|
||||
checkfmt:
|
||||
clang-format --dry-run --ferror-limit=0 --Werror $(FMTFILES)
|
||||
|
||||
.PHONY: update-mem-include
|
||||
update-mem-include:
|
||||
cp -af ../tillitis-key1/hw/application_fpga/fw/tk1_mem.h \
|
||||
include/tkey/tk1_mem.h
|
||||
echo "Remember to update header include guard!"
|
33
hw/application_fpga/tkey-libs/README-DIST.txt
Normal file
33
hw/application_fpga/tkey-libs/README-DIST.txt
Normal file
|
@ -0,0 +1,33 @@
|
|||
tkey-libs binary distribution
|
||||
|
||||
This is the binary distribution of:
|
||||
|
||||
https://github.com/tillitis/tkey-libs
|
||||
|
||||
Which is an SDK for developing device apps for the Tillitis TKey in C.
|
||||
Please see the TKey Developer Handbook for more:
|
||||
|
||||
https://dev.tillitis.se/
|
||||
|
||||
and the company web site:
|
||||
|
||||
https://tillitis.se/
|
||||
|
||||
You should be able to use this distribution directly in device apps
|
||||
simply by pointing LIBDIR to where you unpacked this archive:
|
||||
|
||||
make LIBDIR=~/Download/tkey-libs
|
||||
|
||||
Copyright Tillitis AB.
|
||||
|
||||
These programs are free software: you can redistribute it and/or
|
||||
modify it under the terms of the BSD-2-Clause license.
|
||||
|
||||
See LICENSE for the full BSD-2-Clause license text.
|
||||
|
||||
Note that:
|
||||
|
||||
- Monocypher is Copyright Loup Vaillant and released under CC0
|
||||
1.0 Universal, see monocypher/LICENSE.
|
||||
- blake2s is Copyright Markku-Juhani O. Saarinen and released under CC0
|
||||
1.0 Universal, see blake2s/LICENSE.
|
147
hw/application_fpga/tkey-libs/README.md
Normal file
147
hw/application_fpga/tkey-libs/README.md
Normal file
|
@ -0,0 +1,147 @@
|
|||
[](https://github.com/tillitis/tkey-libs/actions/workflows/ci.yaml)
|
||||
|
||||
# Device libraries for the Tillitis TKey
|
||||
|
||||
- C runtime: libcrt0.
|
||||
- Common C functions including protocol calls: libcommon.
|
||||
- Cryptographic functions: libmonocypher. Based on
|
||||
[Monocypher](https://github.com/LoupVaillant/Monocypher) version
|
||||
4.0.2
|
||||
- BLAKE2s hash function: libblake2s.
|
||||
|
||||
Release notes in [RELEASE.md](RELEASE.md).
|
||||
|
||||
## Licenses
|
||||
|
||||
Unless otherwise noted, the project sources are copyright Tillitis AB,
|
||||
licensed under the terms and conditions of the "BSD-2-Clause" license.
|
||||
See [LICENSE](LICENSE) for the full license text.
|
||||
|
||||
Until Oct 8, 2024, the license was GPL-2.0 Only.
|
||||
|
||||
External source code we have imported are isolated in their own
|
||||
directories. They may be released under other licenses. This is noted
|
||||
with a similar `LICENSE` file in every directory containing imported
|
||||
sources.
|
||||
|
||||
Imported sources:
|
||||
|
||||
- [Monocypher](https://github.com/LoupVaillant/Monocypher) (BSD-2) by
|
||||
Loup Vaillant.
|
||||
|
||||
- blake2s (CC-0), originally based on the reference implementation in
|
||||
[RFC 7693](https://www.rfc-editor.org/rfc/rfc7693.html) written by
|
||||
Markku-Juhani O. Saarinen ([original
|
||||
repository](https://github.com/mjosaarinen/blake2_mjosref). Imported
|
||||
from [Joachim Strömbergson's
|
||||
fork](https://github.com/secworks/blake2s/) used as a model for a
|
||||
hardware implementation.
|
||||
|
||||
### SPDX tags
|
||||
|
||||
The project uses single-line references to Unique License Identifiers
|
||||
as defined by the Linux Foundation's [SPDX project](https://spdx.org/)
|
||||
on its own source files, but not necessarily imported files. The line
|
||||
in each individual source file identifies the license applicable to
|
||||
that file.
|
||||
|
||||
The current set of valid, predefined SPDX identifiers can be found on
|
||||
the SPDX License List at:
|
||||
|
||||
https://spdx.org/licenses/
|
||||
|
||||
We attempt to follow the [REUSE
|
||||
specification](https://reuse.software/).
|
||||
|
||||
## Hardware support
|
||||
|
||||
### Bellatrix and earlier
|
||||
|
||||
Please note that:
|
||||
|
||||
- For reading, only use the blocking `uart_read()`.
|
||||
|
||||
- Only `IO_UART` and `IO_QEMU` destinations are useful for writing as
|
||||
in `write(IO_UART, ...)`, `puts(IO_UART, ...)`, and so on.
|
||||
|
||||
- Defining `QEMU_DEBUG` works with all the `debug_*` functions, but
|
||||
`TKEY_DEBUG` does not.
|
||||
|
||||
## Building
|
||||
|
||||
In order to build, you must have the `make`, `clang`, `llvm`, and
|
||||
`lld` packages installed.
|
||||
|
||||
Version 15 or higher of LLVM/Clang is necessary for the RV32IC\_Zmmul
|
||||
architecture we are using. For more detailed information on the
|
||||
supported build and development environment, please refer to the
|
||||
[Developer Handbook](https://dev.tillitis.se/).
|
||||
## Building using Podman
|
||||
|
||||
You can also build the libraries with our OCI image
|
||||
`ghcr.io/tillitis/tkey-builder`.
|
||||
|
||||
The easiest way to build this is if you have `make` installed:
|
||||
|
||||
```
|
||||
make podman
|
||||
```
|
||||
|
||||
You can also specify a different image by using
|
||||
`IMAGE=localhost/tkey-builder-local`.
|
||||
|
||||
Or use Podman directly:
|
||||
|
||||
```
|
||||
podman run --rm --mount type=bind,source=.,target=/src -w /src -it ghcr.io/tillitis/tkey-builder:4 make -j
|
||||
```
|
||||
|
||||
## Minimal application build
|
||||
|
||||
You will typically need to link at least the `libcrt0` C runtime
|
||||
otherwise your program won't even reach `main()`.
|
||||
|
||||
We provide a linker script in `apps.lds` which shows the linker the
|
||||
memory layout.
|
||||
|
||||
Minimal compilation would look something like:
|
||||
|
||||
```
|
||||
clang -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 -nostdlib -mno-relax -flto \
|
||||
-Wall -Werror=implicit-function-declaration \
|
||||
-I ../tkey-libs/include \
|
||||
-I ../tkey-libs -c -o foo.o foo.c
|
||||
|
||||
clang -target riscv32-unknown-none-elf -march=rv32iczmmul -mabi=ilp32 \
|
||||
-mcmodel=medany -static -ffast-math -fno-common -nostdlib \
|
||||
-T ../tkey-libs/app.lds \
|
||||
-L ../tkey-libs -lcrt0 \
|
||||
-I ../tkey-libs -o foo.elf foo.o
|
||||
|
||||
```
|
||||
|
||||
## Makefile example
|
||||
|
||||
See `example-app/Makefile` for an example Makefile for a simple device
|
||||
application.
|
||||
|
||||
## Debug output
|
||||
|
||||
If you want to have debug prints in your program you can use the
|
||||
`debug_putchar()`, `debug_puts()`, `debug_putinthex()`,
|
||||
`debug_hexdump()` and friends. See `include/tkey/debug.h` for list of
|
||||
functions.
|
||||
|
||||
These functions will be turned on if you define either of these when
|
||||
compiling your program and linking with `libcommon`:
|
||||
|
||||
- `QEMU_DEBUG`: Uses the special debug port only available in qemu to
|
||||
print to the qemu console.
|
||||
- `TKEY_DEBUG`: Uses the extra HID device.
|
||||
|
||||
Note that if you use `TKEY_DEBUG` you *must* have something listening
|
||||
on the corresponding HID device. It's usually the last HID device
|
||||
created. On Linux, for instance, this means the last reported hidraw
|
||||
in `dmesg` is the one you should do `cat /dev/hidrawX` on.
|
191
hw/application_fpga/tkey-libs/RELEASE.md
Normal file
191
hw/application_fpga/tkey-libs/RELEASE.md
Normal file
|
@ -0,0 +1,191 @@
|
|||
# Release notes
|
||||
|
||||
## Upcoming release
|
||||
|
||||
- NOTE WELL! Rewritten I/O functions with new signatures and
|
||||
semantics!
|
||||
- `blake2s()` with new signature.
|
||||
|
||||
### BLAKE2s hash function
|
||||
|
||||
The `blake2s()` function no longer call the firmware.
|
||||
|
||||
- The `blake2s.h` header file has moved to `blake2s/blake2s.h`.
|
||||
|
||||
- The `blake2s()` hash function has changed signature. It's now defined
|
||||
as:
|
||||
|
||||
```
|
||||
// All-in-one convenience function.
|
||||
int blake2s(void *out, size_t outlen, // return buffer for digest
|
||||
const void *key, size_t keylen, // optional secret key
|
||||
const void *in, size_t inlen); // data to be hashed
|
||||
|
||||
```
|
||||
|
||||
- The component functions `blake2s_init()`, `blake2s_update()`, and
|
||||
`blake2s_final()` are now available.
|
||||
|
||||
### I/O
|
||||
|
||||
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
|
||||
endpoints we use an internal USB Mode Protocol between programs
|
||||
running on the PicoRV32 and the CH552 USB Controller.
|
||||
|
||||
The I/O functions has changed accordingly. Please use:
|
||||
|
||||
- `readselect()` with appropriate bitmask (e.g. `IO_CDC|IO_FIDO`) to
|
||||
see if there's anything to read in the endpoints you are interested
|
||||
in. Data from endpoints not mentioned in the bitmask will be
|
||||
discarded.
|
||||
|
||||
- `read()` is now non-blocking and returns the number of bytes read
|
||||
from the endpoint you specify, because more might not be available
|
||||
yet.
|
||||
|
||||
- `write()` now takes an endpoint destination.
|
||||
|
||||
- We also introduce generic `putchar()`, `puts()`, `puthex()`,
|
||||
`putinthex()`, and `hexdump()` functions that take a destination
|
||||
argument.
|
||||
|
||||
We recommend you use only these functions for I/O on Castor and going
|
||||
forward.
|
||||
|
||||
For compatibility to develop device apps for the Bellatrix platform
|
||||
and earlier, use the low-level, blocking function `uart_read()` for
|
||||
reads and *only* the `IO_UART` and `IO_QEMU` destinations for output
|
||||
functions like `write()`, `puts()`.
|
||||
|
||||
### Debug prints
|
||||
|
||||
The optionally built debug prints have changed. You now use
|
||||
`debug_puts()` et cetera instead of `qemu_*()`.
|
||||
|
||||
You define the debug output endpoint when you compile your program by
|
||||
including `debug.h` and defining `QEMU_DEBUG` for the qemu debug port
|
||||
or `TKEY_DEBUG` for output on the DEBUG HID endpoint. If you don't
|
||||
define either, they won't appear in your code.
|
||||
|
||||
Similiarly, `assert()` now also follows `QEMU_DEBUG` or `TKEY_DEBUG`,
|
||||
and prints something on either before halting the CPU.
|
||||
|
||||
Note that on the Bellatrix platform only `QEMU_DEBUG` works.
|
||||
|
||||
## v0.1.2
|
||||
|
||||
From now on tkey-libs is licensed under the BSD-2-Clause license,
|
||||
moving from the previous GPLv2-only.
|
||||
|
||||
Note: There is a possibility that this update may impact the generated
|
||||
CDI for an app that relies on this library. It is recommended to
|
||||
always check for potential CDI changes for each specific app with
|
||||
every update. If the generated CDI does change, and if applicable, it
|
||||
should be clearly communicated to end users to prevent unintentional
|
||||
changes to their identity.
|
||||
|
||||
Changes:
|
||||
- New license, BSD-2-Clause
|
||||
- Reuse compliant, see https://reuse.software/
|
||||
- Fix row alignment in qemu_hexdump
|
||||
- Update memory map, tk1_mem.h, from canonical tillitis-key1 repo
|
||||
- Added make target for creating compile_commands.json for clangd
|
||||
- Added missing include in touch.h
|
||||
|
||||
Full changelog:
|
||||
[v0.1.1...v0.1.2](https://github.com/tillitis/tkey-libs/compare/v0.1.1...v0.1.2)
|
||||
|
||||
## v0.1.1
|
||||
|
||||
This is a minor release correcting a mistake and syncing with the
|
||||
latest HW release, TK1-24.03.
|
||||
|
||||
|
||||
Note: There is a possibility that this update may impact the generated
|
||||
CDI for an app that relies on this library. It is recommended to
|
||||
always check for potential CDI changes for each specific app with
|
||||
every update. If the generated CDI does change, and if applicable, it
|
||||
should be clearly communicated to end users to prevent unintentional
|
||||
changes to their identity.
|
||||
|
||||
Changes:
|
||||
- Update memory map, tk1_mem.h, to match the latest TK1-24.03 release.
|
||||
- Default to tkey-builder:4 for the podman target
|
||||
- Default to have QEMU debug enabled in tkey-libs. Mistakenly removed
|
||||
in previous release.
|
||||
- Revise readme accordingly
|
||||
|
||||
Full changelog:
|
||||
[v0.1.0...v0.1.1](https://github.com/tillitis/tkey-libs/compare/v0.1.0...v0.1.1)
|
||||
|
||||
## v0.1.0
|
||||
|
||||
This release contains some changes that forces applications that use
|
||||
tkey-libs to be updated to work with this release.
|
||||
|
||||
Note: It is highly likely that this update will affect the CDI of the
|
||||
TKey. It is advised to always verify this for each specific app, for
|
||||
every update. If the CDI changes, and it is applicable, it should be
|
||||
stated clearly to end users to avoid unknowingly changing the TKey
|
||||
identity.
|
||||
|
||||
Breaking changes:
|
||||
- Check destination buffer's size for read(). To prevent writing
|
||||
outside of destination buffer.
|
||||
- Renaming LED-functions to follow led_*().
|
||||
|
||||
Changes:
|
||||
- New function, secure_wipe(), to clean memory of secret data.
|
||||
- New function, touch_wait(). Waits for a touch by the user, with
|
||||
selectable timeout.
|
||||
- New function, led_get(). Get the value of the applied LED color.
|
||||
- Upgraded Monocypher to 4.0.2.
|
||||
- Add variable AR in Makefile to enabling passing llvm-ar from command
|
||||
line.
|
||||
- Update example app to use led.h.
|
||||
- Don't have QEMU debug enabled by default.
|
||||
- Minor tweaks and formatting.
|
||||
|
||||
Full changelog:
|
||||
[v0.0.2...v0.1.0](https://github.com/tillitis/tkey-libs/compare/v0.0.2...v0.1.0)
|
||||
|
||||
## v0.0.2
|
||||
|
||||
This release contains some changes that forces applications that use
|
||||
tkey-libs to be updated to work with this release.
|
||||
|
||||
Breaking changes:
|
||||
- Introducing include hierarchy to make it less generic, e.g.,
|
||||
`#include <tkey/led.h>`.
|
||||
- Use stdint.h/stddef.h infavor of types.h.
|
||||
- Library .a files built on top level to simplify inclusion.
|
||||
- Upgraded Monocypher to 4.0.1.
|
||||
- QEMU debug behaviour changed, instead of defining `NODEBUG` to
|
||||
disable debug, one has to enable it by defining `QEMU_DEBUG`.
|
||||
|
||||
Changes:
|
||||
- Introduce functions to control the LED, led.h and led.c.
|
||||
- New function, assert() to make an illegal instruction and forcing
|
||||
the CPU to halt.
|
||||
- Add functions memcpy_s(), wordcpy_s(), memeq() from firmware
|
||||
- Adding `const` to MMIO variables and qemu_* functions.
|
||||
- Minor tweaks, clean up and bugfixes.
|
||||
|
||||
Full changelog:
|
||||
[v0.0.1...v0.0.2](https://github.com/tillitis/tkey-libs/compare/v0.0.1...v0.0.2)
|
||||
|
||||
|
||||
## v0.0.1
|
||||
|
||||
Just ripped from
|
||||
|
||||
https://github.com/tillitis/tillitis-key1-apps
|
||||
|
||||
No semantic changes.
|
39
hw/application_fpga/tkey-libs/REUSE.toml
Normal file
39
hw/application_fpga/tkey-libs/REUSE.toml
Normal file
|
@ -0,0 +1,39 @@
|
|||
# SPDX-FileCopyrightText: 2024 Tillitis AB <tillitis.se>
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
version = 1
|
||||
|
||||
[[annotations]]
|
||||
path = ".github/workflows/*"
|
||||
SPDX-FileCopyrightText = "2022 Tillitis AB <tillitis.se>"
|
||||
SPDX-License-Identifier = "BSD-2-Clause"
|
||||
|
||||
[[annotations]]
|
||||
path = [
|
||||
".clang-format",
|
||||
".editorconfig",
|
||||
".gitignore",
|
||||
"example-app/Makefile",
|
||||
"monocypher/README.md",
|
||||
"Makefile",
|
||||
"README-DIST.txt",
|
||||
"README.md",
|
||||
"RELEASE.md"
|
||||
]
|
||||
SPDX-FileCopyrightText = "2022 Tillitis AB <tillitis.se>"
|
||||
SPDX-License-Identifier = "BSD-2-Clause"
|
||||
|
||||
[[annotations]]
|
||||
path = [
|
||||
"blake2s/*",
|
||||
]
|
||||
|
||||
SPDX-FileCopyrightText = "Markku-Juhani O. Saarinen"
|
||||
SPDX-License-Identifier = "CC0-1.0"
|
||||
|
||||
[[annotations]]
|
||||
path = [
|
||||
"blake2s/Makefile",
|
||||
]
|
||||
|
||||
SPDX-FileCopyrightText = "2014 Secworks Sweden AB"
|
||||
SPDX-License-Identifier = "BSD-2-Clause"
|
64
hw/application_fpga/tkey-libs/app.lds
Normal file
64
hw/application_fpga/tkey-libs/app.lds
Normal 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 */
|
||||
}
|
52
hw/application_fpga/tkey-libs/blake2s/Makefile
Normal file
52
hw/application_fpga/tkey-libs/blake2s/Makefile
Normal file
|
@ -0,0 +1,52 @@
|
|||
#===================================================================
|
||||
#
|
||||
# Makefile
|
||||
# --------
|
||||
# Makefile for building the blake2s model.
|
||||
#
|
||||
#
|
||||
# Author: Joachim Strombergson
|
||||
# Copyright (c) 2014, Secworks Sweden AB
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or
|
||||
# without modification, are permitted provided that the following
|
||||
# conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
#===================================================================
|
||||
|
||||
SRC = blake2s_test.c blake2s.c
|
||||
INC = blake2s.h
|
||||
|
||||
CC = clang
|
||||
CC_FLAGS = -Wall
|
||||
|
||||
blake2s_test: $(SRC) $(INC)
|
||||
$(CC) $(CC_FLAGS) -o $@ $(SRC) -I $(INC)
|
||||
|
||||
|
||||
clean:
|
||||
rm -f ./blake2s_test
|
||||
rm -f *.log
|
||||
rm -f *.txt
|
|
@ -3,22 +3,22 @@
|
|||
// blake2s.c
|
||||
// ---------
|
||||
//
|
||||
// A simple blake2s Reference Implementation.
|
||||
// A simple BLAKE2s reference implementation.
|
||||
//
|
||||
// See LICENSE for license terms.
|
||||
// See README.md in the repo root for info about source code origin.
|
||||
//======================================================================
|
||||
|
||||
#include "../types.h"
|
||||
#include "../lib.h"
|
||||
#include <stdint.h>
|
||||
#include "blake2s.h"
|
||||
|
||||
// Dummy printf() for verbose mode
|
||||
static void printf(const char *format, ...)
|
||||
{
|
||||
}
|
||||
|
||||
#define VERBOSE 0
|
||||
#define SHOW_V 0
|
||||
#define SHOW_M_WORDS 0
|
||||
|
||||
#if VERBOSE || SHOW_V || SHOW_M_WORDS
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
// Cyclic right rotation.
|
||||
#ifndef ROTR32
|
||||
|
@ -41,6 +41,7 @@ static const uint32_t blake2s_iv[8] = {
|
|||
};
|
||||
|
||||
|
||||
#if VERBOSE || SHOW_V
|
||||
//------------------------------------------------------------------
|
||||
//------------------------------------------------------------------
|
||||
void print_v(uint32_t *v) {
|
||||
|
@ -71,24 +72,25 @@ void print_ctx(blake2s_ctx *ctx) {
|
|||
printf("\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// B2S_G macro redefined as a G function.
|
||||
// Allows us to output intermediate values for debugging.
|
||||
//------------------------------------------------------------------
|
||||
void G(uint32_t *v, uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t y) {
|
||||
if (VERBOSE) {
|
||||
#if VERBOSE
|
||||
printf("G started.\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (SHOW_V) {
|
||||
#if SHOW_V
|
||||
printf("v before processing:\n");
|
||||
print_v(&v[0]);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (SHOW_M_WORDS) {
|
||||
#if SHOW_M_WORDS
|
||||
printf("x: 0x%08x, y: 0x%08x\n", x, y);
|
||||
}
|
||||
#endif
|
||||
|
||||
v[a] = v[a] + v[b] + x;
|
||||
v[d] = ROTR32(v[d] ^ v[a], 16);
|
||||
|
@ -99,14 +101,14 @@ void G(uint32_t *v, uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t x,
|
|||
v[c] = v[c] + v[d];
|
||||
v[b] = ROTR32(v[b] ^ v[c], 7);
|
||||
|
||||
if (SHOW_V) {
|
||||
#if SHOW_V
|
||||
printf("v after processing:\n");
|
||||
print_v(&v[0]);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (VERBOSE) {
|
||||
#if VERBOSE
|
||||
printf("G completed.\n\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -131,9 +133,9 @@ static void blake2s_compress(blake2s_ctx *ctx, int last)
|
|||
int i;
|
||||
uint32_t v[16], m[16];
|
||||
|
||||
if (VERBOSE) {
|
||||
#if VERBOSE
|
||||
printf("blake2s_compress started.\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
// init work variables
|
||||
for (i = 0; i < 8; i++) {
|
||||
|
@ -143,9 +145,9 @@ static void blake2s_compress(blake2s_ctx *ctx, int last)
|
|||
|
||||
// low 32 bits of offset
|
||||
// high 32 bits
|
||||
if (VERBOSE) {
|
||||
#if VERBOSE
|
||||
printf("t[0]: 0x%08x, t[1]: 0x%08x\n", ctx->t[0], ctx->t[1]);
|
||||
}
|
||||
#endif
|
||||
v[12] ^= ctx->t[0];
|
||||
v[13] ^= ctx->t[1];
|
||||
|
||||
|
@ -159,52 +161,52 @@ static void blake2s_compress(blake2s_ctx *ctx, int last)
|
|||
m[i] = B2S_GET32(&ctx->b[4 * i]);
|
||||
}
|
||||
|
||||
if (VERBOSE) {
|
||||
#if VERBOSE
|
||||
printf("v before G processing:\n");
|
||||
print_v(&v[0]);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Ten rounds of the G function applied on rows, diagonal.
|
||||
for (i = 0; i < 10; i++) {
|
||||
if (VERBOSE) {
|
||||
#if VERBOSE
|
||||
printf("Round %02d:\n", (i + 1));
|
||||
printf("Row processing started.\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
G(&v[0], 0, 4, 8, 12, m[sigma[i][ 0]], m[sigma[i][ 1]]);
|
||||
G(&v[0], 1, 5, 9, 13, m[sigma[i][ 2]], m[sigma[i][ 3]]);
|
||||
G(&v[0], 2, 6, 10, 14, m[sigma[i][ 4]], m[sigma[i][ 5]]);
|
||||
G(&v[0], 3, 7, 11, 15, m[sigma[i][ 6]], m[sigma[i][ 7]]);
|
||||
|
||||
if (VERBOSE) {
|
||||
#if VERBOSE
|
||||
printf("Row processing completed.\n");
|
||||
printf("Diagonal processing started.\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
G(&v[0], 0, 5, 10, 15, m[sigma[i][ 8]], m[sigma[i][ 9]]);
|
||||
G(&v[0], 1, 6, 11, 12, m[sigma[i][10]], m[sigma[i][11]]);
|
||||
G(&v[0], 2, 7, 8, 13, m[sigma[i][12]], m[sigma[i][13]]);
|
||||
G(&v[0], 3, 4, 9, 14, m[sigma[i][14]], m[sigma[i][15]]);
|
||||
|
||||
if (VERBOSE) {
|
||||
#if VERBOSE
|
||||
printf("Diagonal processing completed.\n");
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (VERBOSE) {
|
||||
#if VERBOSE
|
||||
printf("v after G processing:\n");
|
||||
print_v(&v[0]);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Update the hash state.
|
||||
for (i = 0; i < 8; ++i) {
|
||||
ctx->h[i] ^= v[i] ^ v[i + 8];
|
||||
}
|
||||
|
||||
if (VERBOSE) {
|
||||
#if VERBOSE
|
||||
printf("blake2s_compress completed.\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -218,11 +220,11 @@ int blake2s_init(blake2s_ctx *ctx, size_t outlen,
|
|||
{
|
||||
size_t i;
|
||||
|
||||
if (VERBOSE) {
|
||||
#if VERBOSE
|
||||
printf("blake2s_init started.\n");
|
||||
printf("Context before blake2s_init processing:\n");
|
||||
print_ctx(ctx);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (outlen == 0 || outlen > 32 || keylen > 32)
|
||||
return -1; // illegal parameters
|
||||
|
@ -243,11 +245,11 @@ int blake2s_init(blake2s_ctx *ctx, size_t outlen,
|
|||
ctx->c = 64; // at the end
|
||||
}
|
||||
|
||||
if (VERBOSE) {
|
||||
#if VERBOSE
|
||||
printf("Context after blake2s_init processing:\n");
|
||||
print_ctx(ctx);
|
||||
printf("blake2s_init completed.\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -261,11 +263,11 @@ void blake2s_update(blake2s_ctx *ctx,
|
|||
{
|
||||
size_t i;
|
||||
|
||||
if (VERBOSE) {
|
||||
#if VERBOSE
|
||||
printf("blake2s_update started.\n");
|
||||
printf("Context before blake2s_update processing:\n");
|
||||
print_ctx(ctx);
|
||||
}
|
||||
#endif
|
||||
|
||||
for (i = 0; i < inlen; i++) {
|
||||
if (ctx->c == 64) { // buffer full ?
|
||||
|
@ -278,11 +280,11 @@ void blake2s_update(blake2s_ctx *ctx,
|
|||
ctx->b[ctx->c++] = ((const uint8_t *) in)[i];
|
||||
}
|
||||
|
||||
if (VERBOSE) {
|
||||
#if VERBOSE
|
||||
printf("Context after blake2s_update processing:\n");
|
||||
print_ctx(ctx);
|
||||
printf("blake2s_update completed.\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -294,11 +296,11 @@ void blake2s_final(blake2s_ctx *ctx, void *out)
|
|||
{
|
||||
size_t i;
|
||||
|
||||
if (VERBOSE) {
|
||||
#if VERBOSE
|
||||
printf("blake2s_final started.\n");
|
||||
printf("Context before blake2s_final processing:\n");
|
||||
print_ctx(ctx);
|
||||
}
|
||||
#endif
|
||||
|
||||
ctx->t[0] += ctx->c; // mark last block offset
|
||||
|
||||
|
@ -321,11 +323,11 @@ void blake2s_final(blake2s_ctx *ctx, void *out)
|
|||
(ctx->h[i >> 2] >> (8 * (i & 3))) & 0xFF;
|
||||
}
|
||||
|
||||
if (VERBOSE) {
|
||||
#if VERBOSE
|
||||
printf("Context after blake2s_final processing:\n");
|
||||
print_ctx(ctx);
|
||||
printf("blake2s_final completed.\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -334,15 +336,16 @@ void blake2s_final(blake2s_ctx *ctx, void *out)
|
|||
//------------------------------------------------------------------
|
||||
int blake2s(void *out, size_t outlen,
|
||||
const void *key, size_t keylen,
|
||||
const void *in, size_t inlen,
|
||||
blake2s_ctx *ctx)
|
||||
const void *in, size_t inlen)
|
||||
{
|
||||
if (blake2s_init(ctx, outlen, key, keylen))
|
||||
blake2s_ctx ctx;
|
||||
|
||||
if (blake2s_init(&ctx, outlen, key, keylen))
|
||||
return -1;
|
||||
|
||||
blake2s_update(ctx, in, inlen);
|
||||
blake2s_update(&ctx, in, inlen);
|
||||
|
||||
blake2s_final(ctx, out);
|
||||
blake2s_final(&ctx, out);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,10 +1,18 @@
|
|||
//======================================================================
|
||||
//
|
||||
// blake2s.h
|
||||
// ---------
|
||||
// BLAKE2s Hashing Context and API Prototypes
|
||||
//
|
||||
// See LICENSE for license terms.
|
||||
// See README.md in the repo root for info about source code origin.
|
||||
//======================================================================
|
||||
|
||||
#ifndef BLAKE2S_H
|
||||
#define BLAKE2S_H
|
||||
|
||||
#include "../types.h"
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// state context
|
||||
typedef struct {
|
||||
|
@ -32,8 +40,6 @@ void blake2s_final(blake2s_ctx *ctx, void *out);
|
|||
// All-in-one convenience function.
|
||||
int blake2s(void *out, size_t outlen, // return buffer for digest
|
||||
const void *key, size_t keylen, // optional secret key
|
||||
const void *in, size_t inlen, // data to be hashed
|
||||
blake2s_ctx *ctx);
|
||||
const void *in, size_t inlen); // data to be hashed
|
||||
|
||||
#endif
|
||||
|
138
hw/application_fpga/tkey-libs/blake2s/blake2s_test.c
Normal file
138
hw/application_fpga/tkey-libs/blake2s/blake2s_test.c
Normal file
|
@ -0,0 +1,138 @@
|
|||
//======================================================================
|
||||
//
|
||||
// blake2s_test.c
|
||||
// --------------
|
||||
//
|
||||
//======================================================================
|
||||
|
||||
#include <stdio.h>
|
||||
#include "blake2s.h"
|
||||
|
||||
|
||||
//------------------------------------------------------------------
|
||||
//------------------------------------------------------------------
|
||||
void print_message(uint8_t *m, int mlen) {
|
||||
printf("The message:\n");
|
||||
for (int i = 1 ; i <= mlen ; i++) {
|
||||
printf("0x%02x ", m[(i - 1)]);
|
||||
if (i % 8 == 0) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------
|
||||
//------------------------------------------------------------------
|
||||
void print_digest(uint8_t *md) {
|
||||
printf("The digest:\n");
|
||||
for (int j = 0 ; j < 4 ; j++) {
|
||||
for (int i = 0 ; i < 8 ; i++) {
|
||||
printf("0x%02x ", md[i + 8 * j]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// test_zero_length()
|
||||
// Test with a zero length mwssage.
|
||||
//------------------------------------------------------------------
|
||||
void test_zero_length() {
|
||||
|
||||
uint8_t md[32];
|
||||
|
||||
printf("Testing zero byte message.\n");
|
||||
blake2s(md, 32, NULL, 0, NULL, 0);
|
||||
print_digest(md);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// test_abc_message()
|
||||
// Test with a zero length mwssage.
|
||||
//------------------------------------------------------------------
|
||||
void test_abc_message() {
|
||||
|
||||
uint8_t md[32];
|
||||
uint8_t msg[64] = {'a', 'b', 'c'};
|
||||
|
||||
printf("Testing with RFC 7693 three byte 'abc' message.\n");
|
||||
print_message(msg, 3);
|
||||
|
||||
blake2s(md, 32, NULL, 0, msg, 3);
|
||||
print_digest(md);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// test_one_block_message()
|
||||
// Test with a 64 byte message, filling one block.
|
||||
//------------------------------------------------------------------
|
||||
void test_one_block_message() {
|
||||
|
||||
uint8_t md[32];
|
||||
uint8_t msg[64];
|
||||
|
||||
for (uint8_t i = 0 ; i < 64 ; i++) {
|
||||
msg[i] = i;
|
||||
}
|
||||
|
||||
printf("Testing with 64 byte message.\n");
|
||||
print_message(msg, 64);
|
||||
|
||||
blake2s(md, 32, NULL, 0, msg, 64);
|
||||
print_digest(md);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// test_one_block_one_byte_message()
|
||||
// Test with a 65 byte message, filling one block and a single
|
||||
// byte in the next block.
|
||||
//------------------------------------------------------------------
|
||||
void test_one_block_one_byte_message() {
|
||||
|
||||
uint8_t md[32];
|
||||
uint8_t msg[65];
|
||||
|
||||
for (uint8_t i = 0 ; i < 65 ; i++) {
|
||||
msg[i] = i;
|
||||
}
|
||||
|
||||
printf("Testing with 65 byte message.\n");
|
||||
print_message(msg, 65);
|
||||
|
||||
blake2s(md, 32, NULL, 0, msg, 65);
|
||||
print_digest(md);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------
|
||||
//------------------------------------------------------------------
|
||||
int main(void) {
|
||||
printf("\n");
|
||||
printf("BLAKE2s reference model started. Performing a set of tests..\n");
|
||||
printf("Performing a set of tests.\n");
|
||||
|
||||
test_zero_length();
|
||||
test_abc_message();
|
||||
test_one_block_message();
|
||||
test_one_block_one_byte_message();
|
||||
|
||||
printf("BLAKE2s reference model completed.\n");
|
||||
printf("\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
/// EOF blake2s_test.c
|
||||
//======================================================================
|
33
hw/application_fpga/tkey-libs/example-app/Makefile
Normal file
33
hw/application_fpga/tkey-libs/example-app/Makefile
Normal file
|
@ -0,0 +1,33 @@
|
|||
P := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
|
||||
LIBDIR ?= $(P)/../
|
||||
OBJCOPY ?= llvm-objcopy
|
||||
CC = clang
|
||||
|
||||
# If you want debug_puts() etcetera to output something on our QEMU
|
||||
# debug port, use -DQEMU_DEBUG below, or -DTKEY_DEBUG to use Tkeys USB debug pipe
|
||||
CFLAGS = -g -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 -nostdlib -mno-relax -flto \
|
||||
-Wall -Werror=implicit-function-declaration \
|
||||
-I $(LIBDIR)/include -I $(LIBDIR)
|
||||
# -DQEMU_DEBUG -DTKEY_DEBUG
|
||||
|
||||
INCLUDE=$(LIBDIR)/include
|
||||
|
||||
LDFLAGS=-T $(LIBDIR)/app.lds -L $(LIBDIR) -lcommon -lcrt0
|
||||
|
||||
.PHONY: all
|
||||
all: blue.bin
|
||||
|
||||
# Turn elf into bin for device
|
||||
%.bin: %.elf
|
||||
$(OBJCOPY) --input-target=elf32-littleriscv --output-target=binary $^ $@
|
||||
chmod a-x $@
|
||||
|
||||
BLUEOBJS=blue.o
|
||||
blue.elf: blue.o
|
||||
$(CC) $(CFLAGS) $(BLUEOBJS) $(LDFLAGS) -I $(LIBDIR) -o $@
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f blue.bin blue.elf $(BLUEOBJS)
|
31
hw/application_fpga/tkey-libs/example-app/blue.c
Normal file
31
hw/application_fpga/tkey-libs/example-app/blue.c
Normal file
|
@ -0,0 +1,31 @@
|
|||
// SPDX-FileCopyrightText: 2023 Tillitis AB <tillitis.se>
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
#include <stdint.h>
|
||||
#include <tkey/led.h>
|
||||
#include <tkey/tk1_mem.h>
|
||||
#include <tkey/debug.h>
|
||||
|
||||
#define SLEEPTIME 100000
|
||||
|
||||
void sleep(uint32_t n)
|
||||
{
|
||||
for (volatile int i = 0; i < n; i++);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
debug_puts("Hello, world!\n");
|
||||
debug_puts("Going to sleep between blinks: ");
|
||||
debug_putinthex(SLEEPTIME);
|
||||
debug_lf();
|
||||
|
||||
for (;;) {
|
||||
led_set(LED_RED);
|
||||
sleep(SLEEPTIME);
|
||||
led_set(LED_GREEN);
|
||||
sleep(SLEEPTIME);
|
||||
led_set(LED_BLUE);
|
||||
sleep(SLEEPTIME);
|
||||
}
|
||||
}
|
28
hw/application_fpga/tkey-libs/include/tkey/assert.h
Normal file
28
hw/application_fpga/tkey-libs/include/tkey/assert.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
// SPDX-FileCopyrightText: 2022 Tillitis AB <tillitis.se>
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
#ifndef TKEY_ASSERT_H
|
||||
#define TKEY_ASSERT_H
|
||||
|
||||
#include <tkey/io.h>
|
||||
|
||||
#if defined(QEMU_DEBUG)
|
||||
#define assert(expr) \
|
||||
((expr) ? (void)(0) \
|
||||
: assert_fail(IO_QEMU, #expr, __FILE__, __LINE__, __func__))
|
||||
|
||||
#elif defined(TKEY_DEBUG)
|
||||
|
||||
#define assert(expr) \
|
||||
((expr) ? (void)(0) \
|
||||
: assert_fail(IO_DEBUG, #expr, __FILE__, __LINE__, __func__))
|
||||
|
||||
#else
|
||||
|
||||
#define assert(expr) ((expr) ? (void)(0) : assert_halt())
|
||||
#endif
|
||||
|
||||
void assert_fail(enum ioend dest, const char *assertion, const char *file,
|
||||
unsigned int line, const char *function);
|
||||
void assert_halt(void);
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue