diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/README.md | 15 | ||||
-rw-r--r-- | src/modchipfw/CMakeLists.txt | 40 | ||||
-rw-r--r-- | src/modchipfw/pico_sdk_import.cmake | 62 | ||||
-rw-r--r-- | src/modchipfw/pulsegen.pio | 70 | ||||
-rw-r--r-- | src/modchipfw/utglitcher.c | 158 | ||||
-rw-r--r-- | src/python/example.py | 72 | ||||
-rw-r--r-- | src/python/pulsegen.py | 130 |
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 |