summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/README.md15
-rw-r--r--src/modchipfw/CMakeLists.txt40
-rw-r--r--src/modchipfw/pico_sdk_import.cmake62
-rw-r--r--src/modchipfw/pulsegen.pio70
-rw-r--r--src/modchipfw/utglitcher.c158
-rw-r--r--src/python/example.py72
-rw-r--r--src/python/pulsegen.py130
7 files changed, 547 insertions, 0 deletions
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 <PICO_SDK_PATH>/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 <stdio.h>
+#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