From cbde04c9bc45ea54cc509a65247c62a82f64bca9 Mon Sep 17 00:00:00 2001
From: LennertW <4999638+LennertW@users.noreply.github.com>
Date: Sat, 6 Aug 2022 18:39:43 +0200
Subject: Initial commit
---
GlitchedOnEarth_slides.pdf | Bin 0 -> 14371440 bytes
README.md | 74 +-
img/decoupling.jpg | Bin 0 -> 454750 bytes
img/emmc_testpoints.jpg | Bin 0 -> 703742 bytes
img/installed_modchip.jpg | Bin 0 -> 800603 bytes
img/modchip.jpg | Bin 0 -> 138124 bytes
pcb/README.md | 20 +
pcb/gerbers_modchip.zip | Bin 0 -> 130820 bytes
pcb/ibom.html | 4345 +++++++++++++++++++++++++++++++++++
pcb/interposer_footprint.kicad_mod | 248 ++
pcb/interposer_symbol.kicad_sym | 40 +
pcb/schematic.pdf | Bin 0 -> 120480 bytes
src/README.md | 15 +
src/modchipfw/CMakeLists.txt | 40 +
src/modchipfw/pico_sdk_import.cmake | 62 +
src/modchipfw/pulsegen.pio | 70 +
src/modchipfw/utglitcher.c | 158 ++
src/python/example.py | 72 +
src/python/pulsegen.py | 130 ++
19 files changed, 5273 insertions(+), 1 deletion(-)
create mode 100644 GlitchedOnEarth_slides.pdf
create mode 100644 img/decoupling.jpg
create mode 100644 img/emmc_testpoints.jpg
create mode 100755 img/installed_modchip.jpg
create mode 100755 img/modchip.jpg
create mode 100644 pcb/README.md
create mode 100644 pcb/gerbers_modchip.zip
create mode 100644 pcb/ibom.html
create mode 100644 pcb/interposer_footprint.kicad_mod
create mode 100644 pcb/interposer_symbol.kicad_sym
create mode 100644 pcb/schematic.pdf
create mode 100644 src/README.md
create mode 100644 src/modchipfw/CMakeLists.txt
create mode 100644 src/modchipfw/pico_sdk_import.cmake
create mode 100644 src/modchipfw/pulsegen.pio
create mode 100644 src/modchipfw/utglitcher.c
create mode 100644 src/python/example.py
create mode 100644 src/python/pulsegen.py
diff --git a/GlitchedOnEarth_slides.pdf b/GlitchedOnEarth_slides.pdf
new file mode 100644
index 0000000..709b5b6
Binary files /dev/null and b/GlitchedOnEarth_slides.pdf differ
diff --git a/README.md b/README.md
index 71a04a7..41b7f53 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,75 @@
# Starlink User Terminal Modchip
+This repository accompanies the talk titled "Glitched on Earth by Humans: A Black-Box Security Evaluation of the SpaceX Starlink User Terminal".
+A slide deck is available [here](./GlitchedOnEarth_slides.pdf), a recording of the talk should be available soon.
-## Known issues / limitations
\ No newline at end of file
+The talk covers how we managed to execute arbitrary code on the Starlink User Terminal using a custom modchip that performs voltage fault injection.
+The modchip can be used to bypass signature verification during execution of the System-on-Chip (SoC) ROM bootloader (BL1). This allows to execute arbitrary code on the SoC from BL2 onwards and allows to further explore the Starlink User Terminal and networking side of the system.
+We provide the modchip design so that other researchers can build upon our work.
+
+# 🚨 USE AT YOUR OWN RISK 🚨
+Even though we tested and use the provided modchip design, it is possible to cause permanent damage to a user terminal using this modchip.
+Similarly, disassembling the user terminal may result in permanent damage and will likely void your warranty.
+
+
+## Modchip design overview
+The modchip is controlled by a RP2040 microcontroller that triggers on the eMMC D0 line and creates two pulses with a (programmable) delay and offset for the MOSFET driver.
+One of these pulses controls the glitch MOSFET, when the gate of this MOSFET is driven high the SoC core voltage supply will be shorted to ground. The second pulse generated by the RP2040 allows to control two MOSFETs that can enable/disable two capacitor banks. These capacitors are required for the UT to fully boot, but the capacitors banks cannot be enabled during the voltage glitch as this would prevent us from obtaining the desired fault.
+
+The schematic and gerbers to produce your own modchip are provided [here](./pcb/).
+
+![Modchip overview](./img/modchip.jpg)
+
+## Prerequisites
+* Disassemble your UT. Stop here if you do not want to void your UT warranty or if you are not willing to risk permanent damage.
+ * [several](https://www.youtube.com/watch?v=omScudUro3s) [videos](https://youtu.be/iOmdQnIlnRo) [on](https://www.youtube.com/watch?v=yBnOS7V3oS4) [YouTube](https://youtu.be/-v7E7JIrW5Y) demonstrate the process.
+* Read the contents of the eMMC chip. Make a backup!
+ * The eMMC chip can be read in-circuit by connecting to the CMD, CLK and D0 test points. ![eMMC pinout](./img/emmc_testpoints.jpg)
+ * The eMMC expects 1V8 logic levels!
+ * Several tools exist to read eMMC chips, a good and cheap ($12) option is the [Low Voltage eMMC Adapter by the exploitee.rs](https://shop.exploitee.rs/shop/p/low-voltage-emmc-adapter).
+* You will have to extract, patch and repackage the different boot stages to disable signature verification.
+ * The eMMC layout is documented in the [U-Boot GPL release](https://github.com/SpaceExplorationTechnologies/u-boot/releases/tag/sx_2022_05_03) in the file `spacex_catson_boot.h`.
+ * More information on extracting the firmware can be found in our [blog post](https://www.esat.kuleuven.be/cosic/blog/dumping-and-extracting-the-spacex-starlink-user-terminal-firmware/).
+ * The early boot stages are all based on the [ARM Trusted Firmware-A project](https://trustedfirmware-a.readthedocs.io/en/latest/).
+ * You can use TF fiptool to unpack the Firmware Image Packages into the distinct parts.
+ * Patching these bootstages can be done using [Ghidra](https://github.com/NationalSecurityAgency/ghidra) after some basic static analysis (use the open source TF-A code to guide your efforts).
+ * Make sure to disable signature verification and to re-enable UART output.
+ * Update the firmware hash in the certificates. We glitch signature verification, but the hash still has to be valid.
+ * Similarly, you will have to make some changes to the U-Boot image to disable signature verification.
+ * You can also increase the `bootdelay` parameter.
+ * The Flattened uImage Tree can be unpacked using [dumpimage](https://github.com/u-boot/u-boot/blob/master/tools/dumpimage.c), provided as part of the U-Boot project.
+ * Make the changes you want and repackage.
+* Rewrite the patched firmware to the eMMC chip.
+
+## Mounting the modchip on the Starlink UT PCB
+
+* Make sure that the modchip you assembled works before you try to solder it into place.
+* Remove the decoupling capacitors within the red squares, these are normally used to stabilise the SoC core voltage supply.
+![Decoupling capacitors](./img/decoupling.jpg)
+
+* Align the assembled modchip on the UT PCB and solder it into place using the castellated holes. Make sure to not make any shorts between the core voltage supply and ground.
+ * The rightmost castellated holes are not in the correct position. You can redesign the PCB or simply mask the exposed pads on the UT PCB and connect the castellated holes to a nearby ground pad (see picture).
+
+* Connect the test point marked `UT RST` to the enable pin of the the core voltage regulator.
+* Connect the test point marked `12V` to a nearby 12V source on the UT PCB.
+* Connect the rightmost jumper pad (below the button) to the eMMC D0 test point.
+ * We had to do this because of a firmware update that disabled the UART output.
+* Connect the test point marked `1V8` to a nearby 1.8V source on the UT PCB (there is a decoupling capacitor next to the eMMC that is connected to 1V8).
+
+You should be all set now to start glitching, good luck!
+The [Python folder](./src/) contains an example that demonstrates how you can start using the modchip for experimentation.
+
+
+![Modchip mounted to the UT.](./img/installed_modchip.jpg)
+
+
+## Known issues and limitations
+* The modchip was designed before SpaceX introduced a firmware update that blew a fuse to disable all UART output. The modchip was initially designed to trigger on UART output, but can be adapted to trigger on the eMMC D0 signal. On a newer revision it would be useful to have the ability to read UART output as well as trigger on eMMC D0 data. UART can be re-enabled in BL2 and can provide an easy way of verifying that the glitch succeeded.
+* The modchip was designed for the circular user terminal, the same attack should work on the square user terminal but will require you to create a new PCB design.
+* One section of castellated holes is in the wrong location. Be careful when using the design as is and follow the mounting instruction accordingly.
+* Unplugging the modchip from the control PC after the glitch succeeded may result in the dish rebooting. This is likely because of a missing pull-down resistor on the `IN A` pin of the MCP1405 MOSFET driver.
+* The provided firmware expects an external control PC to orchestrate the fault injection attempts. It should be fairly straightforward to turn this into a stand-alone modchip using the (currently unused) second RP2040 Cortex-M0 core.
+
+## FAQ
+* We are not selling finished modchips
+* We are not providing (patched) Starlink User Terminal firmware
+* We are not providing exact glitch parameters. The presentation slides contain various hints and the parameters will vary depending on how you patch the firmware.
\ No newline at end of file
diff --git a/img/decoupling.jpg b/img/decoupling.jpg
new file mode 100644
index 0000000..c77b203
Binary files /dev/null and b/img/decoupling.jpg differ
diff --git a/img/emmc_testpoints.jpg b/img/emmc_testpoints.jpg
new file mode 100644
index 0000000..76413f7
Binary files /dev/null and b/img/emmc_testpoints.jpg differ
diff --git a/img/installed_modchip.jpg b/img/installed_modchip.jpg
new file mode 100755
index 0000000..18b2014
Binary files /dev/null and b/img/installed_modchip.jpg differ
diff --git a/img/modchip.jpg b/img/modchip.jpg
new file mode 100755
index 0000000..ef7de4d
Binary files /dev/null and b/img/modchip.jpg differ
diff --git a/pcb/README.md b/pcb/README.md
new file mode 100644
index 0000000..a4c83fb
--- /dev/null
+++ b/pcb/README.md
@@ -0,0 +1,20 @@
+# PCB files
+This folder contains the schematic and gerber files to produce your own modchip PCB.
+We ordered our modchip PCB from JLCPCB and selected a PCB thickness of 0.8mm, 1.6mm might work but is not tested.
+
+An interactive BOM is [provided](./ibom.html), the main active components on the PCB are:
+* RaspberryPi RP2040 microcontroller
+* W25Q128JVS Flash storage
+* MCP1405 MOSFET driver
+* Vishay SISS54DN-T1-GE3 Capacitor switching MOSFET
+* Vishay SISH112DN-T1-GE3 Crowbar MOSFET
+* NLSV1T34 Level shifter
+* NCP1117 SOT223 3.3V voltage regulator
+
+At the the time of writing most of these components appear to be available or easily replaceable.
+The cost for all components on the PCB at low volume should be less than 25 EUR.
+
+## Optional improvements before building the PCB
+* Add a pull-down resistor on the `IN A` pin of the MCP1405 MOSFET driver.
+* Modify the locations of the castellated holes in `interposer_footprint.kicad_mod`. Currently the rightmost castellated holes are not conveniently located, see the mounting instructions.
+* Do some additional routing such that the RP2040 can monitor eMMC D0 and UART at the same time.
\ No newline at end of file
diff --git a/pcb/gerbers_modchip.zip b/pcb/gerbers_modchip.zip
new file mode 100644
index 0000000..8877b56
Binary files /dev/null and b/pcb/gerbers_modchip.zip differ
diff --git a/pcb/ibom.html b/pcb/ibom.html
new file mode 100644
index 0000000..fd77a3c
--- /dev/null
+++ b/pcb/ibom.html
@@ -0,0 +1,4345 @@
+
+
+
+
+
+
+ Interactive BOM for KiCAD
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Board stats
+
Front
+
Back
+
Total
+
+
+
Components
+
~
+
~
+
~
+
+
+
Groups
+
~
+
~
+
~
+
+
+
SMD pads
+
~
+
~
+
~
+
+
+
TH pads
+
~
+
+
+
+
+
+
+
+
Checkboxes
+
+
+
+
+
+
+
+
+
+
Save board image
+
+
+ X
+
+
+
+
+
+
+
+
+
+ Config and checkbox state
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Title
+
+
+ Revision
+
+
+
+
+ Company
+
+
+ Date
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pcb/interposer_footprint.kicad_mod b/pcb/interposer_footprint.kicad_mod
new file mode 100644
index 0000000..e38cac7
--- /dev/null
+++ b/pcb/interposer_footprint.kicad_mod
@@ -0,0 +1,248 @@
+(footprint "ut_glitcher_interposer" (version 20211014) (generator pcbnew)
+ (layer "F.Cu")
+ (tedit 62261FD6)
+ (attr smd)
+ (fp_text reference "REF**" (at 46.2 -27.4 unlocked) (layer "F.SilkS")
+ (effects (font (size 1 1) (thickness 0.15)))
+ (tstamp 4c9fdea7-ba0c-45cc-8f66-240980c37d5c)
+ )
+ (fp_text value "ut_glitcher_interposer" (at 57.8 -25.1 unlocked) (layer "F.Fab")
+ (effects (font (size 1 1) (thickness 0.15)))
+ (tstamp c58960d9-4cac-4036-ad2e-1aef26946dae)
+ )
+ (fp_text user "${REFERENCE}" (at 45.8 -25 unlocked) (layer "F.Fab")
+ (effects (font (size 1 1) (thickness 0.15)))
+ (tstamp 5b96c1ad-46ba-4366-8241-fbc1cd0e9bbd)
+ )
+ (fp_line (start 38.323399 -45.189993) (end 38.323399 -45.189993) (layer "Edge.Cuts") (width 0.1) (tstamp 000b46d6-b833-4804-8f56-56d539f76d09))
+ (fp_line (start 33.996975 -60.655741) (end 38.978215 -60.655741) (layer "Edge.Cuts") (width 0.1) (tstamp 099473f1-6598-46ff-a50f-4c520832170d))
+ (fp_line (start 78.448956 0) (end 78.448956 0) (layer "Edge.Cuts") (width 0.1) (tstamp 0c5dddf1-38df-43d2-b49c-e7b691dab0ab))
+ (fp_line (start 78.448956 -17.78149) (end 78.448956 0) (layer "Edge.Cuts") (width 0.1) (tstamp 0ce1dd44-f307-4f98-9f0d-478fd87daa64))
+ (fp_line (start 35.259819 -45.189993) (end 35.259819 -45.189993) (layer "Edge.Cuts") (width 0.1) (tstamp 113ffcdf-4c54-4e37-81dc-f91efa934ba7))
+ (fp_line (start 42.065178 -43.646507) (end 42.065178 -43.646507) (layer "Edge.Cuts") (width 0.1) (tstamp 15699041-ed40-45ee-87d8-f5e206a88536))
+ (fp_line (start 29.646199 -29.966616) (end 29.693934 -30.012423) (layer "Edge.Cuts") (width 0.1) (tstamp 162e5bdd-61a8-46a3-8485-826b5d58e1a1))
+ (fp_line (start 0 -16.191239) (end 0 -16.191239) (layer "Edge.Cuts") (width 0.1) (tstamp 1855ca44-ab48-4b76-a210-97fc81d916c4))
+ (fp_line (start 33.996975 -60.655741) (end 33.996975 -60.655741) (layer "Edge.Cuts") (width 0.1) (tstamp 1876c30c-72b2-4a8d-9f32-bf8b213530b4))
+ (fp_line (start 38.978215 -60.655741) (end 38.978215 -49.352712) (layer "Edge.Cuts") (width 0.1) (tstamp 199124ca-dd64-45cf-a063-97cc545cbea7))
+ (fp_line (start 42.065178 -49.352712) (end 42.065178 -49.352712) (layer "Edge.Cuts") (width 0.1) (tstamp 1bd80cf9-f42a-4aee-a408-9dbf4e81e625))
+ (fp_line (start 16.480797 -16.191239) (end 16.480797 -16.191239) (layer "Edge.Cuts") (width 0.1) (tstamp 1bf7d0f9-0dcf-4d7c-b58c-318e3dc42bc9))
+ (fp_line (start 24.555453 -27.6147) (end 24.555453 -27.6147) (layer "Edge.Cuts") (width 0.1) (tstamp 1cacb878-9da4-41fc-aa80-018bc841e19a))
+ (fp_line (start 24.710388 -27.769634) (end 29.59199 -32.651247) (layer "Edge.Cuts") (width 0.1) (tstamp 1de61170-5337-44c5-ba28-bd477db4bff1))
+ (fp_line (start 29.693934 -30.012423) (end 32.243016 -32.561508) (layer "Edge.Cuts") (width 0.1) (tstamp 2102c637-9f11-48f1-aae6-b4139dc22be2))
+ (fp_line (start 26.115862 -25.826309) (end 24.411946 -27.471192) (layer "Edge.Cuts") (width 0.1) (tstamp 247ebffd-2cb6-4379-ba6e-21861fea3913))
+ (fp_line (start 0 0) (end 0 0) (layer "Edge.Cuts") (width 0.1) (tstamp 254f7cc6-cee1-44ca-9afe-939b318201aa))
+ (fp_line (start 37.820595 -43.646507) (end 37.820595 -43.646507) (layer "Edge.Cuts") (width 0.1) (tstamp 26a22c19-4cc5-4237-9651-0edc4f854154))
+ (fp_line (start 32.243016 -32.561508) (end 32.384506 -32.703005) (layer "Edge.Cuts") (width 0.1) (tstamp 272c2a78-b5f5-4b61-aed3-ec69e0e92729))
+ (fp_line (start 31.553124 -28.059693) (end 31.553124 -28.059693) (layer "Edge.Cuts") (width 0.1) (tstamp 2b25e886-ded1-450a-ada1-ece4208052e4))
+ (fp_line (start 0 -16.191239) (end 15.171174 -16.191239) (layer "Edge.Cuts") (width 0.1) (tstamp 3457afc5-3e4f-4220-81d1-b079f653a722))
+ (fp_line (start 35.259819 -47.318126) (end 38.323399 -47.318126) (layer "Edge.Cuts") (width 0.1) (tstamp 3a1a39fc-8030-4c93-9d9c-d79ba6824099))
+ (fp_line (start 37.820595 -40.746632) (end 38.744343 -40.746632) (layer "Edge.Cuts") (width 0.1) (tstamp 3b65c51e-c243-447e-bee9-832d94c1630e))
+ (fp_line (start 40.241055 -22.787239) (end 40.241055 -22.787239) (layer "Edge.Cuts") (width 0.1) (tstamp 3bbbbb7d-391c-4fee-ac81-3c47878edc38))
+ (fp_line (start 32.243016 -32.561508) (end 32.243016 -32.561508) (layer "Edge.Cuts") (width 0.1) (tstamp 3f2a6679-91d7-4b6c-bf5c-c4d5abb2bc44))
+ (fp_line (start 37.820595 -40.746632) (end 37.820595 -40.746632) (layer "Edge.Cuts") (width 0.1) (tstamp 402c62e6-8d8e-473a-a0cf-2b86e4908cd7))
+ (fp_line (start 29.646199 -29.966616) (end 29.646199 -29.966616) (layer "Edge.Cuts") (width 0.1) (tstamp 456c5e47-d71e-4708-b061-1e61634d8648))
+ (fp_line (start 48.964062 -17.78149) (end 78.448956 -17.78149) (layer "Edge.Cuts") (width 0.1) (tstamp 4970ec6e-3725-4619-b57d-dc2c2cb86ed0))
+ (fp_line (start 38.323399 -47.318126) (end 38.323399 -47.318126) (layer "Edge.Cuts") (width 0.1) (tstamp 49b5f540-e128-4e08-bb09-f321f8e64056))
+ (fp_line (start 40.241055 -22.787239) (end 48.964062 -22.787239) (layer "Edge.Cuts") (width 0.1) (tstamp 4a53fa56-d65b-42a4-a4be-8f49c4c015bb))
+ (fp_line (start 29.689041 -47.453547) (end 29.689041 -47.453547) (layer "Edge.Cuts") (width 0.1) (tstamp 4bbde53d-6894-4e18-9480-84a6a26d5f6b))
+ (fp_line (start 24.555453 -27.6147) (end 24.710388 -27.769634) (layer "Edge.Cuts") (width 0.1) (tstamp 4ce9470f-5633-41bf-89ac-74a810939893))
+ (fp_line (start 22.688019 -39.55522) (end 22.688019 -39.55522) (layer "Edge.Cuts") (width 0.1) (tstamp 4cfd9a02-97ef-4af4-a6b8-db9be1a8fda5))
+ (fp_line (start 24.519421 -27.578663) (end 24.519421 -27.578663) (layer "Edge.Cuts") (width 0.1) (tstamp 51cc007a-3378-4ce3-909c-71e94822f8d1))
+ (fp_line (start 30.278254 -36.115635) (end 30.352667 -36.041222) (layer "Edge.Cuts") (width 0.1) (tstamp 54ed3ee1-891b-418e-ab9c-6a18747d7388))
+ (fp_line (start 24.519421 -27.578663) (end 24.555453 -27.6147) (layer "Edge.Cuts") (width 0.1) (tstamp 5576cd03-3bad-40c5-9316-1d286895d52a))
+ (fp_line (start 38.978215 -49.352712) (end 42.065178 -49.352712) (layer "Edge.Cuts") (width 0.1) (tstamp 57f248a7-365e-4c42-b80d-5a7d1f9dfaf3))
+ (fp_line (start 15.966301 -16.191239) (end 15.966301 -16.191239) (layer "Edge.Cuts") (width 0.1) (tstamp 58390862-1833-41dd-9c4e-98073ea0da33))
+ (fp_line (start 40.241055 -29.942255) (end 40.241055 -29.942255) (layer "Edge.Cuts") (width 0.1) (tstamp 5bab6a37-1fdf-4cf8-b571-44c962ed86e9))
+ (fp_line (start 15.171174 -16.191239) (end 15.966301 -16.191239) (layer "Edge.Cuts") (width 0.1) (tstamp 5e755161-24a5-4650-a6e3-9836bf074412))
+ (fp_line (start 0 0) (end 0 -16.191239) (layer "Edge.Cuts") (width 0.1) (tstamp 5f48b0f2-82cf-40ce-afac-440f97643c36))
+ (fp_line (start 48.964062 -22.787239) (end 48.964062 -22.787239) (layer "Edge.Cuts") (width 0.1) (tstamp 6150c02b-beb5-4af1-951e-3666a285a6ea))
+ (fp_line (start 34.220314 -30.867199) (end 34.220314 -30.867199) (layer "Edge.Cuts") (width 0.1) (tstamp 62f15a9a-9893-486e-9ad0-ea43f88fc9e7))
+ (fp_line (start 40.241055 -29.942255) (end 40.241055 -23.16029) (layer "Edge.Cuts") (width 0.1) (tstamp 706c1cb9-5d96-4282-9efc-6147f0125147))
+ (fp_line (start 32.384506 -32.703005) (end 34.220314 -30.867199) (layer "Edge.Cuts") (width 0.1) (tstamp 7273dd21-e834-41d3-b279-d7de727709ca))
+ (fp_line (start 30.278254 -36.115635) (end 30.278254 -36.115635) (layer "Edge.Cuts") (width 0.1) (tstamp 749d9ed0-2ff2-4b55-abc5-f7231ec3aa28))
+ (fp_line (start 29.59199 -32.651247) (end 22.688019 -39.55522) (layer "Edge.Cuts") (width 0.1) (tstamp 751d823e-1d7b-4501-9658-d06d459b0e16))
+ (fp_line (start 48.964062 -17.78149) (end 48.964062 -17.78149) (layer "Edge.Cuts") (width 0.1) (tstamp 755f94aa-38f0-4a64-a7c7-6c71cb18cddf))
+ (fp_line (start 42.065178 -49.352712) (end 42.065178 -43.646507) (layer "Edge.Cuts") (width 0.1) (tstamp 80095e91-6317-4cfb-9aea-884c9a1accc5))
+ (fp_line (start 24.411946 -27.471192) (end 24.496271 -27.555515) (layer "Edge.Cuts") (width 0.1) (tstamp 83184391-76ed-44f0-8cd0-01f89f157bdb))
+ (fp_line (start 38.744343 -40.746632) (end 38.744343 -29.942255) (layer "Edge.Cuts") (width 0.1) (tstamp 88deea08-baa5-4041-beb7-01c299cf00e6))
+ (fp_line (start 24.763346 -41.630545) (end 30.278254 -36.115635) (layer "Edge.Cuts") (width 0.1) (tstamp 8a8c373f-9bc3-4cf7-8f41-4802da916698))
+ (fp_line (start 33.996975 -51.761479) (end 33.996975 -60.655741) (layer "Edge.Cuts") (width 0.1) (tstamp 9112ddd5-10d5-48b8-954f-f1d5adcacbd9))
+ (fp_line (start 15.966301 -16.191239) (end 16.480797 -16.191239) (layer "Edge.Cuts") (width 0.1) (tstamp 9208ea78-8dde-4b3d-91e9-5755ab5efd9a))
+ (fp_line (start 24.763346 -41.630545) (end 24.763346 -41.630545) (layer "Edge.Cuts") (width 0.1) (tstamp 92761c09-a591-4c8e-af4d-e0e2262cb01d))
+ (fp_line (start 38.744343 -29.942255) (end 40.241055 -29.942255) (layer "Edge.Cuts") (width 0.1) (tstamp 92f063a3-7cce-4a96-8a3a-cf5767f700c6))
+ (fp_line (start 26.115862 -25.826309) (end 26.115862 -25.826309) (layer "Edge.Cuts") (width 0.1) (tstamp 94d24676-7ae3-483c-8bd6-88d31adf00b4))
+ (fp_line (start 24.411946 -27.471192) (end 24.411946 -27.471192) (layer "Edge.Cuts") (width 0.1) (tstamp 966ee9ec-860e-45bb-af89-30bda72b2032))
+ (fp_line (start 42.065178 -43.646507) (end 37.820595 -43.646507) (layer "Edge.Cuts") (width 0.1) (tstamp 968a6172-7a4e-40ab-a78a-e4d03671e136))
+ (fp_line (start 24.496271 -27.555515) (end 24.519421 -27.578663) (layer "Edge.Cuts") (width 0.1) (tstamp 96ef76a5-90c3-4767-98ba-2b61887e28d3))
+ (fp_line (start 48.964062 -22.787239) (end 48.964062 -17.78149) (layer "Edge.Cuts") (width 0.1) (tstamp 9c2999b2-1cf1-4204-9d23-243401b77aa3))
+ (fp_line (start 40.241055 -23.16029) (end 40.241055 -22.787239) (layer "Edge.Cuts") (width 0.1) (tstamp 9ed09117-33cf-45a3-85a7-2606522feaf8))
+ (fp_line (start 38.744343 -40.746632) (end 38.744343 -40.746632) (layer "Edge.Cuts") (width 0.1) (tstamp a177c3b4-b04c-490e-b3fe-d3d4d7aa24a7))
+ (fp_line (start 32.384506 -32.703005) (end 32.384506 -32.703005) (layer "Edge.Cuts") (width 0.1) (tstamp a3fab380-991d-404b-95d5-1c209b047b6e))
+ (fp_line (start 24.710388 -27.769634) (end 24.710388 -27.769634) (layer "Edge.Cuts") (width 0.1) (tstamp aa23bfe3-454b-4a2b-bfe1-101c747eb84e))
+ (fp_line (start 22.688019 -39.55522) (end 24.763346 -41.630545) (layer "Edge.Cuts") (width 0.1) (tstamp aadc3df5-0e2d-4f3d-b72e-6f184da74c89))
+ (fp_line (start 38.744343 -29.942255) (end 38.744343 -29.942255) (layer "Edge.Cuts") (width 0.1) (tstamp ad4d05f5-6957-42f8-b65c-c657b9a26485))
+ (fp_line (start 30.352667 -36.041222) (end 35.727013 -41.415571) (layer "Edge.Cuts") (width 0.1) (tstamp af76ce95-feca-41fb-bf31-edaa26d6766a))
+ (fp_line (start 34.220314 -30.867199) (end 34.290474 -30.797041) (layer "Edge.Cuts") (width 0.1) (tstamp b2b363dd-8e47-4a76-a142-e00e28334875))
+ (fp_line (start 34.290474 -30.797041) (end 34.290474 -30.797041) (layer "Edge.Cuts") (width 0.1) (tstamp c15b2f75-2e10-4b71-bebb-e2b872171b92))
+ (fp_line (start 37.820595 -43.646507) (end 37.820595 -40.746632) (layer "Edge.Cuts") (width 0.1) (tstamp c1b11207-7c0a-49b3-a41d-2fe677d5f3b8))
+ (fp_line (start 38.978215 -49.352712) (end 38.978215 -49.352712) (layer "Edge.Cuts") (width 0.1) (tstamp c346b00c-b5e0-4939-beb4-7f48172ef334))
+ (fp_line (start 33.996975 -51.761479) (end 33.996975 -51.761479) (layer "Edge.Cuts") (width 0.1) (tstamp c3d5daf8-d359-42b2-a7c2-0d080ba7e212))
+ (fp_line (start 35.259819 -45.189993) (end 35.259819 -47.318126) (layer "Edge.Cuts") (width 0.1) (tstamp c7cd39db-931a-4d86-96b8-57e6b39f58f9))
+ (fp_line (start 78.448956 0) (end 0 0) (layer "Edge.Cuts") (width 0.1) (tstamp ca56e1ad-54bf-4df5-a4f7-99f5d61d0de9))
+ (fp_line (start 38.978215 -60.655741) (end 38.978215 -60.655741) (layer "Edge.Cuts") (width 0.1) (tstamp ca9b74ce-0dee-401c-9544-f599f4cf538d))
+ (fp_line (start 38.323399 -45.189993) (end 35.259819 -45.189993) (layer "Edge.Cuts") (width 0.1) (tstamp ceb12634-32ca-4cbf-9ff5-5e8b53ab18ad))
+ (fp_line (start 29.689041 -47.453547) (end 33.996975 -51.761479) (layer "Edge.Cuts") (width 0.1) (tstamp d3dd7cdb-b730-487d-804d-99150ba318ef))
+ (fp_line (start 24.496271 -27.555515) (end 24.496271 -27.555515) (layer "Edge.Cuts") (width 0.1) (tstamp db6412d3-e6c3-4bdd-abf4-a8f55d56df31))
+ (fp_line (start 38.323399 -47.318126) (end 38.323399 -45.189993) (layer "Edge.Cuts") (width 0.1) (tstamp dd70858b-2f9a-4b3f-9af5-ead3a9ba57e9))
+ (fp_line (start 35.727013 -41.415571) (end 35.727013 -41.415571) (layer "Edge.Cuts") (width 0.1) (tstamp e11ae5a5-aa10-4f10-b346-f16e33c7899a))
+ (fp_line (start 16.480797 -16.191239) (end 26.115862 -25.826309) (layer "Edge.Cuts") (width 0.1) (tstamp e45aa7d8-0254-4176-afd9-766820762e19))
+ (fp_line (start 15.171174 -16.191239) (end 15.171174 -16.191239) (layer "Edge.Cuts") (width 0.1) (tstamp e86e4fae-9ca7-4857-a93c-bc6a3048f887))
+ (fp_line (start 40.241055 -23.16029) (end 40.241055 -23.16029) (layer "Edge.Cuts") (width 0.1) (tstamp eb391a95-1c1d-4613-b508-c76b8bc13a73))
+ (fp_line (start 35.727013 -41.415571) (end 29.689041 -47.453547) (layer "Edge.Cuts") (width 0.1) (tstamp f23ac723-a36d-491d-9473-7ec0ffed332d))
+ (fp_line (start 34.290474 -30.797041) (end 31.553124 -28.059693) (layer "Edge.Cuts") (width 0.1) (tstamp f6a5c856-f2b5-40eb-a958-b666a0d408a0))
+ (fp_line (start 78.448956 -17.78149) (end 78.448956 -17.78149) (layer "Edge.Cuts") (width 0.1) (tstamp f8b47531-6c06-4e54-9fc9-cd9d0f3dd69f))
+ (fp_line (start 30.352667 -36.041222) (end 30.352667 -36.041222) (layer "Edge.Cuts") (width 0.1) (tstamp fd60415a-f01a-46c5-9369-ea970e435e5b))
+ (fp_line (start 31.553124 -28.059693) (end 29.646199 -29.966616) (layer "Edge.Cuts") (width 0.1) (tstamp ffa442c7-cbef-461f-8613-c211201cec06))
+ (pad "1" thru_hole rect (at 36.2 -45.2 90) (size 1 1) (drill 0.6) (layers *.Cu *.Mask) (tstamp 1087999d-983e-42bf-b325-b81c766947cc))
+ (pad "1" thru_hole rect (at 32.516117 -38.216117 135) (size 1 1) (drill 0.6) (layers *.Cu *.Mask) (tstamp 3da59bc6-70b3-471f-bbfc-55990eeb98e5))
+ (pad "1" thru_hole rect (at 34.283883 -39.983883 135) (size 1 1) (drill 0.6) (layers *.Cu *.Mask) (tstamp 6e2f7fa6-1ee9-4775-917f-ada02dc13bcd))
+ (pad "1" thru_hole rect (at 31.632233 -37.332233 135) (size 1 1) (drill 0.6) (layers *.Cu *.Mask) (tstamp 99f4f4aa-2f14-4bf9-b8a7-da1480e9e168))
+ (pad "1" thru_hole rect (at 33.4 -39.1 135) (size 1 1) (drill 0.6) (layers *.Cu *.Mask) (tstamp b80aa845-c1c7-4a36-86eb-13202c5b8807))
+ (pad "1" thru_hole rect (at 37.45 -45.2 90) (size 1 1) (drill 0.6) (layers *.Cu *.Mask) (tstamp e0a50294-8c6e-4d53-aeda-b230ef3f0916))
+ (pad "2" thru_hole rect (at 25.167767 -37.132233 135) (size 1 1) (drill 0.6) (layers *.Cu *.Mask) (tstamp 0a1ac2c6-8da8-4410-b772-69afa2855077))
+ (pad "2" thru_hole rect (at 28.7033 -33.5967 135) (size 1 1) (drill 0.6) (layers *.Cu *.Mask) (tstamp 11547ba3-d459-4ced-9333-92979d5b86e1))
+ (pad "2" thru_hole rect (at 27.819417 -34.480583 135) (size 1 1) (drill 0.6) (layers *.Cu *.Mask) (tstamp 26584013-aa69-4f6e-9469-cf96829118fe))
+ (pad "2" thru_hole rect (at 23.4 -38.9 135) (size 1 1) (drill 0.6) (layers *.Cu *.Mask) (tstamp 7efaeda2-e767-44b9-adb2-3a0c3f4d2f1d))
+ (pad "2" thru_hole rect (at 26.935533 -35.364467 135) (size 1 1) (drill 0.6) (layers *.Cu *.Mask) (tstamp 8157d0c3-4115-4fef-882d-18ff9f3b1e49))
+ (pad "2" thru_hole rect (at 26.05165 -36.24835 135) (size 1 1) (drill 0.6) (layers *.Cu *.Mask) (tstamp 9d221b3b-0bfe-4439-a426-0f2594b9c7bf))
+ (pad "2" thru_hole rect (at 24.283883 -38.016117 135) (size 1 1) (drill 0.6) (layers *.Cu *.Mask) (tstamp dff62e1d-c592-4963-80cb-25d776cdc1f4))
+ (pad "3" thru_hole rect (at 49 -18.9 180) (size 1 1) (drill 0.6) (layers *.Cu *.Mask) (tstamp 9c5eb8ba-0370-4530-b0dd-d326ff9cd796))
+ (pad "3" thru_hole rect (at 49 -21.4 180) (size 1 1) (drill 0.6) (layers *.Cu *.Mask) (tstamp 9d5e7df5-7472-4dc8-a9fc-73987a422b16))
+ (pad "3" thru_hole rect (at 49 -20.150001 180) (size 1 1) (drill 0.6) (layers *.Cu *.Mask) (tstamp acdd6813-47a9-43b9-8c0d-c0787f18321e))
+ (pad "4" thru_hole rect (at 37.45 -47.3 90) (size 1 1) (drill 0.6) (layers *.Cu *.Mask) (tstamp b7986f62-ea7a-4dc5-91cd-26acb8e0379b))
+ (pad "4" thru_hole rect (at 36.2 -47.3 90) (size 1 1) (drill 0.6) (layers *.Cu *.Mask) (tstamp d0e758c8-d140-4a8a-8239-760094b94ecd))
+ (group "" (id 2a6753e8-f9e7-4c11-a472-dc9c7e1759c8)
+ (members
+ 1087999d-983e-42bf-b325-b81c766947cc
+ e0a50294-8c6e-4d53-aeda-b230ef3f0916
+ )
+ )
+ (group "" (id 319c683d-aed6-4e7d-aee2-ff9871746d52)
+ (members
+ 000b46d6-b833-4804-8f56-56d539f76d09
+ 099473f1-6598-46ff-a50f-4c520832170d
+ 0c5dddf1-38df-43d2-b49c-e7b691dab0ab
+ 0ce1dd44-f307-4f98-9f0d-478fd87daa64
+ 113ffcdf-4c54-4e37-81dc-f91efa934ba7
+ 15699041-ed40-45ee-87d8-f5e206a88536
+ 162e5bdd-61a8-46a3-8485-826b5d58e1a1
+ 1855ca44-ab48-4b76-a210-97fc81d916c4
+ 1876c30c-72b2-4a8d-9f32-bf8b213530b4
+ 199124ca-dd64-45cf-a063-97cc545cbea7
+ 1bd80cf9-f42a-4aee-a408-9dbf4e81e625
+ 1bf7d0f9-0dcf-4d7c-b58c-318e3dc42bc9
+ 1cacb878-9da4-41fc-aa80-018bc841e19a
+ 1de61170-5337-44c5-ba28-bd477db4bff1
+ 2102c637-9f11-48f1-aae6-b4139dc22be2
+ 247ebffd-2cb6-4379-ba6e-21861fea3913
+ 254f7cc6-cee1-44ca-9afe-939b318201aa
+ 26a22c19-4cc5-4237-9651-0edc4f854154
+ 272c2a78-b5f5-4b61-aed3-ec69e0e92729
+ 2b25e886-ded1-450a-ada1-ece4208052e4
+ 3457afc5-3e4f-4220-81d1-b079f653a722
+ 3a1a39fc-8030-4c93-9d9c-d79ba6824099
+ 3b65c51e-c243-447e-bee9-832d94c1630e
+ 3bbbbb7d-391c-4fee-ac81-3c47878edc38
+ 3f2a6679-91d7-4b6c-bf5c-c4d5abb2bc44
+ 402c62e6-8d8e-473a-a0cf-2b86e4908cd7
+ 456c5e47-d71e-4708-b061-1e61634d8648
+ 4970ec6e-3725-4619-b57d-dc2c2cb86ed0
+ 49b5f540-e128-4e08-bb09-f321f8e64056
+ 4a53fa56-d65b-42a4-a4be-8f49c4c015bb
+ 4bbde53d-6894-4e18-9480-84a6a26d5f6b
+ 4ce9470f-5633-41bf-89ac-74a810939893
+ 4cfd9a02-97ef-4af4-a6b8-db9be1a8fda5
+ 51cc007a-3378-4ce3-909c-71e94822f8d1
+ 54ed3ee1-891b-418e-ab9c-6a18747d7388
+ 5576cd03-3bad-40c5-9316-1d286895d52a
+ 57f248a7-365e-4c42-b80d-5a7d1f9dfaf3
+ 58390862-1833-41dd-9c4e-98073ea0da33
+ 5bab6a37-1fdf-4cf8-b571-44c962ed86e9
+ 5e755161-24a5-4650-a6e3-9836bf074412
+ 5f48b0f2-82cf-40ce-afac-440f97643c36
+ 6150c02b-beb5-4af1-951e-3666a285a6ea
+ 62f15a9a-9893-486e-9ad0-ea43f88fc9e7
+ 706c1cb9-5d96-4282-9efc-6147f0125147
+ 7273dd21-e834-41d3-b279-d7de727709ca
+ 749d9ed0-2ff2-4b55-abc5-f7231ec3aa28
+ 751d823e-1d7b-4501-9658-d06d459b0e16
+ 755f94aa-38f0-4a64-a7c7-6c71cb18cddf
+ 80095e91-6317-4cfb-9aea-884c9a1accc5
+ 83184391-76ed-44f0-8cd0-01f89f157bdb
+ 88deea08-baa5-4041-beb7-01c299cf00e6
+ 8a8c373f-9bc3-4cf7-8f41-4802da916698
+ 9112ddd5-10d5-48b8-954f-f1d5adcacbd9
+ 9208ea78-8dde-4b3d-91e9-5755ab5efd9a
+ 92761c09-a591-4c8e-af4d-e0e2262cb01d
+ 92f063a3-7cce-4a96-8a3a-cf5767f700c6
+ 94d24676-7ae3-483c-8bd6-88d31adf00b4
+ 966ee9ec-860e-45bb-af89-30bda72b2032
+ 968a6172-7a4e-40ab-a78a-e4d03671e136
+ 96ef76a5-90c3-4767-98ba-2b61887e28d3
+ 9c2999b2-1cf1-4204-9d23-243401b77aa3
+ 9ed09117-33cf-45a3-85a7-2606522feaf8
+ a177c3b4-b04c-490e-b3fe-d3d4d7aa24a7
+ a3fab380-991d-404b-95d5-1c209b047b6e
+ aa23bfe3-454b-4a2b-bfe1-101c747eb84e
+ aadc3df5-0e2d-4f3d-b72e-6f184da74c89
+ ad4d05f5-6957-42f8-b65c-c657b9a26485
+ af76ce95-feca-41fb-bf31-edaa26d6766a
+ b2b363dd-8e47-4a76-a142-e00e28334875
+ c15b2f75-2e10-4b71-bebb-e2b872171b92
+ c1b11207-7c0a-49b3-a41d-2fe677d5f3b8
+ c346b00c-b5e0-4939-beb4-7f48172ef334
+ c3d5daf8-d359-42b2-a7c2-0d080ba7e212
+ c7cd39db-931a-4d86-96b8-57e6b39f58f9
+ ca56e1ad-54bf-4df5-a4f7-99f5d61d0de9
+ ca9b74ce-0dee-401c-9544-f599f4cf538d
+ ceb12634-32ca-4cbf-9ff5-5e8b53ab18ad
+ d3dd7cdb-b730-487d-804d-99150ba318ef
+ db6412d3-e6c3-4bdd-abf4-a8f55d56df31
+ dd70858b-2f9a-4b3f-9af5-ead3a9ba57e9
+ e11ae5a5-aa10-4f10-b346-f16e33c7899a
+ e45aa7d8-0254-4176-afd9-766820762e19
+ e86e4fae-9ca7-4857-a93c-bc6a3048f887
+ eb391a95-1c1d-4613-b508-c76b8bc13a73
+ f23ac723-a36d-491d-9473-7ec0ffed332d
+ f6a5c856-f2b5-40eb-a958-b666a0d408a0
+ f8b47531-6c06-4e54-9fc9-cd9d0f3dd69f
+ fd60415a-f01a-46c5-9369-ea970e435e5b
+ ffa442c7-cbef-461f-8613-c211201cec06
+ )
+ )
+ (group "" (id 7c0cf58c-e25b-422b-8099-af386f9b94eb)
+ (members
+ b7986f62-ea7a-4dc5-91cd-26acb8e0379b
+ d0e758c8-d140-4a8a-8239-760094b94ecd
+ )
+ )
+ (group "" (id b81cd904-69d1-4c8b-81f2-302fdf1cfeb0)
+ (members
+ 0a1ac2c6-8da8-4410-b772-69afa2855077
+ 11547ba3-d459-4ced-9333-92979d5b86e1
+ 26584013-aa69-4f6e-9469-cf96829118fe
+ 7efaeda2-e767-44b9-adb2-3a0c3f4d2f1d
+ 8157d0c3-4115-4fef-882d-18ff9f3b1e49
+ 9d221b3b-0bfe-4439-a426-0f2594b9c7bf
+ dff62e1d-c592-4963-80cb-25d776cdc1f4
+ )
+ )
+ (group "" (id dd7274bb-36be-4baa-903e-939c1f1b99f6)
+ (members
+ 3da59bc6-70b3-471f-bbfc-55990eeb98e5
+ 6e2f7fa6-1ee9-4775-917f-ada02dc13bcd
+ 99f4f4aa-2f14-4bf9-b8a7-da1480e9e168
+ b80aa845-c1c7-4a36-86eb-13202c5b8807
+ )
+ )
+)
diff --git a/pcb/interposer_symbol.kicad_sym b/pcb/interposer_symbol.kicad_sym
new file mode 100644
index 0000000..faf8831
--- /dev/null
+++ b/pcb/interposer_symbol.kicad_sym
@@ -0,0 +1,40 @@
+(kicad_symbol_lib (version 20211014) (generator kicad_symbol_editor)
+ (symbol "interposer_conn" (in_bom yes) (on_board yes)
+ (property "Reference" "U" (id 0) (at 5.08 -1.27 0)
+ (effects (font (size 1.27 1.27)))
+ )
+ (property "Value" "interposer_conn" (id 1) (at 5.08 20.32 0)
+ (effects (font (size 1.27 1.27)))
+ )
+ (property "Footprint" "" (id 2) (at 0 0 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (property "Datasheet" "" (id 3) (at 0 0 0)
+ (effects (font (size 1.27 1.27)) hide)
+ )
+ (symbol "interposer_conn_0_1"
+ (rectangle (start 0 19.05) (end 10.16 0)
+ (stroke (width 0.1524) (type default) (color 0 0 0 0))
+ (fill (type none))
+ )
+ )
+ (symbol "interposer_conn_1_1"
+ (pin input line (at 0 16.51 0) (length 2.54)
+ (name "VCore" (effects (font (size 1.27 1.27))))
+ (number "1" (effects (font (size 1.27 1.27))))
+ )
+ (pin input line (at 0 10.16 0) (length 2.54)
+ (name "GND1" (effects (font (size 1.27 1.27))))
+ (number "2" (effects (font (size 1.27 1.27))))
+ )
+ (pin input line (at 0 6.35 0) (length 2.54)
+ (name "GND2" (effects (font (size 1.27 1.27))))
+ (number "3" (effects (font (size 1.27 1.27))))
+ )
+ (pin input line (at 0 2.54 0) (length 2.54)
+ (name "GND3" (effects (font (size 1.27 1.27))))
+ (number "4" (effects (font (size 1.27 1.27))))
+ )
+ )
+ )
+)
diff --git a/pcb/schematic.pdf b/pcb/schematic.pdf
new file mode 100644
index 0000000..bcff61d
Binary files /dev/null and b/pcb/schematic.pdf differ
diff --git a/src/README.md b/src/README.md
new file mode 100644
index 0000000..068ad8a
--- /dev/null
+++ b/src/README.md
@@ -0,0 +1,15 @@
+## Modchip software
+
+The firmware for the RP2040 microcontroller can be found in the `modchipfw` folder. You can compile this by running the following commands from within the `modchipfw` folder:
+
+```
+mkdir build && cd build
+cmake ..
+make
+```
+
+To update the firmware on the RP2040 you can simply press the button on the modchip before plugging it in. It should now enumerate as a removable disk, copy the `utglitcher.uf2` to this removable disk to update the firmware.
+
+## Host Python software
+In the Python folder you can find `pulsegen.py`, this file contains the `PicoPulseGen` class that handles communication with the modchip.
+The `example.py` script is a basic example to demonstrate how you can interact with the modchip and how you can set glitch parameters.
\ No newline at end of file
diff --git a/src/modchipfw/CMakeLists.txt b/src/modchipfw/CMakeLists.txt
new file mode 100644
index 0000000..9367d0f
--- /dev/null
+++ b/src/modchipfw/CMakeLists.txt
@@ -0,0 +1,40 @@
+# Generated Cmake Pico project file
+
+cmake_minimum_required(VERSION 3.13)
+
+set(CMAKE_C_STANDARD 11)
+set(CMAKE_CXX_STANDARD 17)
+
+# Initialise pico_sdk from installed location
+# (note this can come from environment, CMake cache etc)
+# set(PICO_SDK_PATH "~/pico/pico-sdk")
+
+# Pull in Raspberry Pi Pico SDK (must be before project)
+include(pico_sdk_import.cmake)
+
+project(utglitcher C CXX ASM)
+
+# Initialise the Raspberry Pi Pico SDK
+pico_sdk_init()
+
+# Add executable. Default name is the project name, version 0.1
+
+add_executable(utglitcher utglitcher.c )
+
+pico_set_program_name(utglitcher "utglitcher")
+pico_set_program_version(utglitcher "0.3")
+
+pico_enable_stdio_uart(utglitcher 0)
+pico_enable_stdio_usb(utglitcher 1)
+
+# Add the standard library to the build
+target_link_libraries(utglitcher pico_stdlib)
+
+# Add any user requested libraries
+target_link_libraries(utglitcher
+ hardware_pio
+ pico_multicore
+ )
+
+pico_generate_pio_header(utglitcher ${CMAKE_CURRENT_LIST_DIR}/pulsegen.pio)
+pico_add_extra_outputs(utglitcher)
diff --git a/src/modchipfw/pico_sdk_import.cmake b/src/modchipfw/pico_sdk_import.cmake
new file mode 100644
index 0000000..28efe9e
--- /dev/null
+++ b/src/modchipfw/pico_sdk_import.cmake
@@ -0,0 +1,62 @@
+# This is a copy of /external/pico_sdk_import.cmake
+
+# This can be dropped into an external project to help locate this SDK
+# It should be include()ed prior to project()
+
+if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
+ set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
+ message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
+endif ()
+
+if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
+ set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
+ message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
+endif ()
+
+if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
+ set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
+ message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
+endif ()
+
+set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
+set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
+set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
+
+if (NOT PICO_SDK_PATH)
+ if (PICO_SDK_FETCH_FROM_GIT)
+ include(FetchContent)
+ set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
+ if (PICO_SDK_FETCH_FROM_GIT_PATH)
+ get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
+ endif ()
+ FetchContent_Declare(
+ pico_sdk
+ GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
+ GIT_TAG master
+ )
+ if (NOT pico_sdk)
+ message("Downloading Raspberry Pi Pico SDK")
+ FetchContent_Populate(pico_sdk)
+ set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
+ endif ()
+ set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
+ else ()
+ message(FATAL_ERROR
+ "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
+ )
+ endif ()
+endif ()
+
+get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
+if (NOT EXISTS ${PICO_SDK_PATH})
+ message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
+endif ()
+
+set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
+if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
+ message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
+endif ()
+
+set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
+
+include(${PICO_SDK_INIT_CMAKE_FILE})
diff --git a/src/modchipfw/pulsegen.pio b/src/modchipfw/pulsegen.pio
new file mode 100644
index 0000000..4068940
--- /dev/null
+++ b/src/modchipfw/pulsegen.pio
@@ -0,0 +1,70 @@
+.program pulsegen
+
+; we use 2 bits from the delay bits for side set
+.side_set 2
+
+entry:
+ ; Could probably get rid of these blocking PULLs by enabling autopull and writing to the fifo before enabling the SM?
+ ; Read number of edges
+ PULL BLOCK side 0
+ MOV X, OSR side 0 ; Might want to use OUT here instead of MOV
+
+ ; Read pulse offset
+ PULL BLOCK side 0
+ MOV Y, OSR side 0
+
+ ; Clear interrupt (might not be needed if we clear it on the M0 side of things?)
+ IRQ CLEAR 0 side 0
+
+nedges:
+ ; Wait for rising-edges
+ WAIT 0 PIN 0 side 0
+ WAIT 1 PIN 0 side 0
+ JMP X-- nedges side 0
+
+ ; Read pulse width
+ ; This will cause a fixed delay between the trigger event and the glitch insertion. Fine for our purposes.
+ PULL BLOCK side 0
+ MOV X, OSR side 0
+
+; Loop for pulse offset cycles
+poffset:
+ JMP Y-- poffset side 2
+
+; Loop for pulse width cycles
+pwidth:
+ JMP X-- pwidth side 3
+
+
+ SET Y, 31 side 2 ; A fixed delay to ensure that the glitch has been inserted before the capacitors are enabled again.
+delay:
+ NOP side 2 [7]
+ NOP side 2 [7]
+ NOP side 2 [7]
+ NOP side 2 [7]
+ NOP side 2 [7]
+ JMP Y-- delay side 2
+
+ ; Signal that the pulse has been inserted, and disable the pulse using sideset
+ IRQ WAIT 0 side 0
+
+
+% c-sdk {
+void pulsegen_program_init(PIO pio, uint sm, uint offset, uint trigger_pin, uint pulse_pin, uint caps_pin) {
+ pio_sm_config c = pulsegen_program_get_default_config(offset);
+
+ sm_config_set_sideset_pins(&c, pulse_pin);
+ sm_config_set_in_pins(&c, trigger_pin);
+ sm_config_set_in_shift(&c, false, false, 32);
+
+ pio_gpio_init(pio, trigger_pin);
+ pio_gpio_init(pio, pulse_pin);
+ pio_gpio_init(pio, caps_pin);
+
+ pio_sm_set_consecutive_pindirs(pio, sm, trigger_pin, 1, false);
+ pio_sm_set_consecutive_pindirs(pio, sm, pulse_pin, 2, true);
+
+ sm_config_set_clkdiv(&c, 1);
+ pio_sm_init(pio, sm, offset, &c);
+}
+%}
\ No newline at end of file
diff --git a/src/modchipfw/utglitcher.c b/src/modchipfw/utglitcher.c
new file mode 100644
index 0000000..5ba5c94
--- /dev/null
+++ b/src/modchipfw/utglitcher.c
@@ -0,0 +1,158 @@
+#include
+#include "pico/stdlib.h"
+#include "hardware/uart.h"
+#include "hardware/gpio.h"
+#include "hardware/pio.h"
+#include "hardware/clocks.h"
+#include "hardware/vreg.h"
+#include "pulsegen.pio.h"
+
+#define PIN_NRST 7
+#define PIN_TRIG 6
+#define PIN_PULSE 0
+#define PIN_CAPS 1
+
+#define PIN_LED1 16
+#define PIN_LED2 17
+
+int main()
+{
+ /*
+ * For some reason the serial communication fails after some time when running at 200MHz
+ * For me everything worked fine at 250 MHz without changing the core voltage (vreg_set_voltage)
+ */
+ set_sys_clock_khz(250000, true);
+
+ stdio_init_all();
+
+ // GPIO initialisation.
+ gpio_init(PIN_NRST);
+ gpio_init(PIN_TRIG);
+ gpio_init(PIN_PULSE);
+ gpio_init(PIN_CAPS);
+ gpio_init(PIN_LED1);
+ gpio_init(PIN_LED2);
+ gpio_set_dir(PIN_NRST, GPIO_OUT);
+ gpio_set_dir(PIN_TRIG, GPIO_IN);
+ gpio_set_dir(PIN_PULSE, GPIO_OUT);
+ gpio_set_dir(PIN_CAPS, GPIO_OUT);
+ gpio_set_dir(PIN_LED1, GPIO_OUT);
+ gpio_set_dir(PIN_LED2, GPIO_OUT);
+ gpio_set_pulls(PIN_CAPS, true, false);
+ gpio_set_drive_strength(PIN_PULSE, GPIO_DRIVE_STRENGTH_12MA);
+ gpio_set_drive_strength(PIN_CAPS, GPIO_DRIVE_STRENGTH_12MA);
+ gpio_set_slew_rate(PIN_PULSE, GPIO_SLEW_RATE_FAST);
+
+ // Setup PIO
+ PIO pio = pio0;
+ uint32_t sm = pio_claim_unused_sm(pio, true);
+ uint32_t pio_offset = pio_add_program(pio, &pulsegen_program);
+ pulsegen_program_init(pio, sm, pio_offset, PIN_TRIG, PIN_PULSE, PIN_CAPS);
+
+ // Wait for serial connection
+ while (!stdio_usb_connected()) {
+ sleep_ms(500);
+ }
+
+ gpio_put(PIN_LED1, true);
+ gpio_put(PIN_NRST, false);
+
+ char cmd;
+ uint32_t pulse_offset = 0;
+ uint32_t pulse_width = 0;
+ uint32_t trig_edges = 1;
+ uint32_t gpio_states = 0;
+
+ uint8_t gpio_pin = 0;
+ uint8_t gpio_state = 0;
+
+ while (true) {
+ cmd = getchar();
+
+ switch (cmd)
+ {
+ // Enable glitch SM
+ case 'A':
+ gpio_put(PIN_LED2, true);
+ pio_sm_put_blocking(pio, sm, trig_edges);
+ pio_sm_put_blocking(pio, sm, pulse_offset);
+ pio_sm_put_blocking(pio, sm, pulse_width);
+
+ gpio_put(PIN_NRST, true);
+ sleep_ms(46); // Delay to make sure all UT signals are stable
+
+ pio_sm_set_enabled(pio, sm, true);
+ printf("A\n");
+ break;
+
+ // Wait for trigger
+ case 'B':
+ while(!pio_interrupt_get(pio0, 0)) {
+ cmd = getchar_timeout_us(1);
+ if (cmd == 'D') break; // Disarm
+ };
+
+ pio_sm_set_enabled(pio, sm, false);
+ pio_interrupt_clear(pio, 0);
+ pio_sm_clear_fifos(pio, sm);
+ pio_sm_drain_tx_fifo(pio, sm);
+ pio_sm_restart(pio, sm);
+ pio_sm_set_enabled(pio, sm, false);
+
+ pio_sm_exec_wait_blocking(pio, sm, pio_encode_set(pio_x, pio_offset));
+ pio_sm_exec_wait_blocking(pio, sm, pio_encode_mov(pio_pc, pio_x));
+ printf("T\n");
+ gpio_put(PIN_LED2, false);
+ break;
+
+ // Set the number of edges before inserting a pulse
+ case 'E':
+ fread(&trig_edges, 1, 4, stdin);
+ printf("%d\n", trig_edges);
+ break;
+
+ // Set the pulse offset
+ case 'O':
+ fread(&pulse_offset, 1, 4, stdin);
+ printf("%d\n", pulse_offset);
+ break;
+
+ // Set the pulse width
+ case 'W':
+ fread(&pulse_width, 1, 4, stdin);
+ printf("%d\n", pulse_width);
+ break;
+
+ // print the current pulse offset and width
+ case 'S':
+ printf("PulseGenerator offset: %d, width: %d, edges: %d\n", pulse_offset, pulse_width, trig_edges);
+ break;
+
+ // control a gpio pin, can be expanded to handle multiple pins
+ case 'G':
+ fread(&gpio_pin, 1, 1, stdin);
+ fread(&gpio_state, 1, 1, stdin);
+
+ if (gpio_pin == PIN_NRST) {
+ if (gpio_state == 0) {
+ gpio_put(PIN_NRST, false);
+ } else {
+ gpio_put(PIN_NRST, true);
+ }
+ }
+ printf("G\n");
+ break;
+
+ // Read state of GPIOs
+ case 'R':
+ gpio_states = gpio_get_all();
+ printf("%d\n", gpio_states);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
\ No newline at end of file
diff --git a/src/python/example.py b/src/python/example.py
new file mode 100644
index 0000000..6995940
--- /dev/null
+++ b/src/python/example.py
@@ -0,0 +1,72 @@
+import numpy as np
+import time
+import serial
+from tqdm import tnrange, tqdm
+import random
+from pulsegen import PicoPulseGen
+
+# Open serial interface
+# I'm using this to detect when the glitch was successful
+try:
+ ser = serial.Serial('/dev/ttyUSB0', 115200)
+
+except Exception as e:
+ print('Could not open /dev/ttyUSB0')
+ exit()
+
+# Connect to modchip
+try:
+ glitcher = PicoPulseGen('/dev/ttyACM0')
+ logger.info('Connected to modchip')
+
+ # You have to figure out the trig_edges parameter
+ # You have to figure out ranges for the pulse_offset and pulse_width parameters
+ glitcher.trig_edges = 0
+ glitcher.pulse_offset = 0
+ glitcher.pulse_width = 0
+ glitcher.set_gpio(0)
+
+except Exception as e:
+ print('Could not connect to modchip')
+ exit()
+
+input("Press enter to start.")
+
+def generator():
+ while True:
+ yield
+
+idx = 0
+success = False
+for _ in tqdm(generator()):
+ if idx % 10 == 0:
+ # Pulse width and offset are expressed in number of cycles of the PIO state machine operating frequency (default in the provided fw is 250MHz).
+ glitch_width = random.randint(A, B) # You have to figure out good ranges here
+ glitch_offset = random.randint(C, D)
+
+ glitcher.pulse_offset = glitch_offset
+ glitcher.pulse_width = glitch_width
+
+ ser.reset_input_buffer()
+ glitcher.arm() # Arm the modchip, it will try to power up the UT and will wait for the number of set trigger pulses to occur before inserting a glitch
+ glitcher.wait_trig(timeout=5) # Waits for the modchip to signal it has triggered. The modchip will be disarmed if no glitch has occurred within 5 seconds.
+
+ time.sleep(0.55) # Have to wait for the second stage to start to see serial output
+ data = ser.read(ser.in_waiting)
+
+ if b'LENNERT' in data: # a check to determine if the glitch was successful. My BL2 has been modified to print LENNERT.
+ success = True
+ break
+
+ glitcher.set_gpio(0) # Disables the core voltage regulator. The modchip firmware will re-enable the regulator automatically on the next glitch attempt.
+ time.sleep(0.1)
+
+ idx += 1
+
+if success:
+ print('Glitch successul!')
+ logger.debug('%d, %d, %d' %(idx, glitch_width, glitch_offset))
+ logger.debug(data.decode('utf-8', 'ignore'))
+
+ser.close()
+glitcher.close()
\ No newline at end of file
diff --git a/src/python/pulsegen.py b/src/python/pulsegen.py
new file mode 100644
index 0000000..161625a
--- /dev/null
+++ b/src/python/pulsegen.py
@@ -0,0 +1,130 @@
+import serial
+import time
+import signal
+
+class PicoPulseGen:
+ def __init__(self, port='/dev/ttyACM0'):
+ self._pulse_offset = 0
+ self._pulse_width = 0
+ self._trig_edges = 0
+
+ self.pico = serial.Serial(port, 115200)
+ time.sleep(0.1)
+ self.pico.write(b'S')
+
+ test = self.pico.readline()
+ if b'PulseGenerator' not in test:
+ raise ConnectionError('Could not connect to the PulseGenerator :(')
+
+ signal.signal(signal.SIGALRM, self.arm_abort)
+
+
+ @property
+ def pulse_offset(self):
+ return self._pulse_offset
+
+
+ @pulse_offset.setter
+ def pulse_offset(self, offset):
+ if type(offset) != int or offset < 0 or offset > 0xFFFFFFFF:
+ raise ValueError('Offset has to be an int between 0 and 0xFFFFFFFF')
+
+ self._pulse_offset = offset
+
+ self.pico.flushInput()
+ self.pico.write(b'O')
+ self.pico.write((self._pulse_offset).to_bytes(4, 'little'))
+ ret = self.pico.readline()
+ assert int(ret.strip()) == self._pulse_offset, ret
+
+
+ @property
+ def pulse_width(self):
+ return self._pulse_offset
+
+
+ @pulse_width.setter
+ def pulse_width(self, width):
+ if type(width) != int or width < 0 or width > 0xFFFFFFFF:
+ raise ValueError('Width has to be an int between 0 and 0xFFFFFFFF')
+
+ self._pulse_width = width
+
+ self.pico.flushInput()
+ self.pico.write(b'W')
+ self.pico.write((self._pulse_width).to_bytes(4, 'little'))
+ ret = self.pico.readline()
+ assert int(ret.strip()) == self._pulse_width, ret
+
+
+ @property
+ def trig_edges(self):
+ return self._trig_edges
+
+
+ @trig_edges.setter
+ def trig_edges(self, edges):
+ if type(edges) != int or edges < 0 or edges > 0xFFFFFFFF:
+ raise ValueError('Width has to be an int between 0 and 0xFFFFFFFF')
+
+ self._trig_edges = edges
+
+ self.pico.write(b'E')
+ self.pico.write((self._trig_edges).to_bytes(4, 'little'))
+ ret = self.pico.readline()
+ assert int(ret.strip()) == self._trig_edges, ret
+
+
+ def arm(self):
+ self.pico.write(b'A')
+ ret = self.pico.readline()
+ assert b'A' in ret
+
+
+ def wait_trig(self, timeout=5):
+ self.pico.write(b'B')
+ signal.alarm(timeout)
+ ret = self.pico.readline()
+ signal.alarm(0)
+ assert b'T' in ret
+
+
+ def arm_abort(self, signum, frame):
+ print('No trigger observed, disarming!')
+ self.pico.write(b'D')
+
+
+ def status(self):
+ self.pico.write(b'S')
+ ret = self.pico.readline()
+ print(ret.decode('utf-8'))
+
+
+ def set_gpio(self, state):
+ if type(state) != int or state < 0:
+ raise ValueError('State has to be zero (GPIO 0) or a positive value larger than zero (GPIO 1)')
+
+ self.pico.write(b'G')
+ self.pico.write(bytes([7])) # For now there is only one GPIO pin used for this functionality
+ if state:
+ self.pico.write(bytes([1]))
+ else:
+ self.pico.write(bytes([0]))
+
+ ret = self.pico.readline()
+ assert b'G' in ret
+
+
+ def read_gpios(self):
+ self.pico.write(b'R')
+ ret = self.pico.readline()
+ ret = int(ret.strip())
+ return ret
+
+
+ def close(self):
+ self.pico.close()
+
+
+ def __del__(self):
+ self.pico.close()
\ No newline at end of file
--
cgit v1.2.3