diff --git a/contrib/hydra_spi_dump/hydra_spi_dump.py b/contrib/hydra_spi_dump/hydra_spi_dump.py deleted file mode 100644 index 7cc6d21b..00000000 --- a/contrib/hydra_spi_dump/hydra_spi_dump.py +++ /dev/null @@ -1,171 +0,0 @@ -#!/usr/bin/python3 -# -# Script to dump SPI flash chips with Hydrabus, tested with Hydrabus hardware v1.0 and firmware v0.8 beta -# Based on https://github.com/hydrabus/hydrafw/wiki/HydraFW-Binary-SPI-mode-guide -# -# Author: Pedro Ribeiro -# License: GPLv3 (https://choosealicense.com/licenses/gpl-3.0/) -# -import hexdump -import serial -import struct -import sys -import signal - -sector_size = 0x1000 # also the max buffer supported by hydrabus ? (CONFIRM THIS) -hydrabus = None - -def error(msg): - print(msg) - - global hydrabus - - if hydrabus != None: - # cleanup the hydrabus so that we don't have to reset it - hydrabus_cleanup - - quit() - -def signal_handler(signal, frame): - error("CTRL+C pressed, cleaning up and exiting") - -def print_usage(): - print("Usage:") - print("\thydra_spi_dump.py dump [slow|fast]") - print("\t\tDumps n_4k_sectors into dump_file, starting at hex_address.") - print("\t\tBy default, it dumps in slow (320kHz) mode, choose fast to increase to 10.5 mHz.") - print("\n\thydra_spi_flash.py chip_id") - print("\t\tPrints chip idenfification (RDID).") - print("\nThis script requires Python 3.2+, pip3 install serial hexdump") - quit() - -def hex_to_bin(num, padding): - return num.to_bytes(padding, byteorder='big') - -def calc_hex_addr(addr, add): - addr_int = int(addr, 16) - addr_int += add - byte_arr = hex_to_bin(addr_int, 3) - return byte_arr - -def hydrabus_setup(): - global hydrabus - - #Open serial port - hydrabus = serial.Serial('/dev/ttyACM0', 115200) - - #Open binary mode - for i in range(20): - hydrabus.write(b'\x00') - if b"BBIO1" not in hydrabus.read(5): - error("Could not get into binary mode, try again or reset hydrabus.") - - hydrabus.reset_input_buffer() - - # Switching to SPI mode - hydrabus.write(b'\x01') - if b"SPI1" not in hydrabus.read(4): - error("Cannot set SPI mode, try again or reset hydrabus.") - - # Configure SPI port (default polarity and clock phase, SPI1 device) - hydrabus.write(b'\x81') - if b'\x01' not in hydrabus.read(1): - error("Cannot set SPI device settings, try again or reset hydrabus.") - - -def hydrabus_cleanup(): - global hydrabus - - #Return to main binary mode - hydrabus.write(b'\x00') - - #reset to console mode - hydrabus.write(b'\x0F\n') - -def dump_chip(): - if len(sys.argv) < 5: - print_usage - else: - dump_file = sys.argv[2] - sectors = int(sys.argv[3]) - start_addr = sys.argv[4] - if sectors < 1: - print_usage - - hydrabus_setup() - global hydrabus - - if len(sys.argv) == 6 and sys.argv[5] == "fast": - # Set spi speed to 10.5 mHz, a conservative fast speed - hydrabus.write(b'\x61') - if b'\x01' not in hydrabus.read(1): - error("Cannot set SPI speed, try again or reset hydrabus.") - else: - print("Using fast mode (10.5 mHz) to dump the chip.") - - print('Starting to read chip...') - print('Reading ' + str(sectors) + ' sectors') - - sector = 0 - buf = bytearray() - - while sector < sectors: - # write-then-read: write 4 bytes (1 read cmd + 3 read addr), read sector_size bytes - hydrabus.write(b'\x04\x00\x04' + hex_to_bin(sector_size, 2)) - - # read command (\x03) and address - hydrabus.write(b'\x03' + calc_hex_addr(start_addr, sector * sector_size)) - - # Hydrabus will send \x01 in case of success... - ok = hydrabus.read(1) - - #...followed by sector_size read bytes - buf += hydrabus.read(sector_size) - print('Read sector ' + str(sector)) - sector += 1 - - with open(dump_file, 'wb+') as f: - f.write(buf) - - print('Finished dumping to ' + dump_file) - -def chip_id(): - global hydrabus - - buf = bytearray() - - print("Sending RDID command...") - # write-then-read: write one byte, read 3 (rdid) - hydrabus.write(b'\x04\x00\x01\x00\x03') - - # send rdid byte (0x9f) - hydrabus.write(b'\x9f') - - ok = hydrabus.read(1) - buf = hydrabus.read(3) - - print(hexdump.hexdump(buf)) - - print("Finished chip_id function.") - - -if __name__ == '__main__': - - # disable this for debugging - #signal.signal(signal.SIGINT, signal_handler) - - if len(sys.argv) < 2: - print_usage() - - elif sys.argv[1] == "dump": - # hydrabus_setup inside dump_chip because of arg validation - dump_chip() - hydrabus_cleanup() - - elif sys.argv[1] == "chip_id": - hydrabus_setup() - chip_id() - hydrabus_cleanup() - - else: - print_usage() diff --git a/contrib/hydra_spi_dump/README.md b/contrib/hydra_spiflash_nor_dump/README.md similarity index 100% rename from contrib/hydra_spi_dump/README.md rename to contrib/hydra_spiflash_nor_dump/README.md diff --git a/contrib/hydra_spiflash_nor_dump/hydra_spiflash_nor_dump.py b/contrib/hydra_spiflash_nor_dump/hydra_spiflash_nor_dump.py new file mode 100644 index 00000000..38b11d5e --- /dev/null +++ b/contrib/hydra_spiflash_nor_dump/hydra_spiflash_nor_dump.py @@ -0,0 +1,217 @@ +#!/usr/bin/python3 + +# Script to dump SPI flash chips with Hydrabus, tested with Hydrabus hardware v1.0 and firmware v0.8 beta +# Based on https://github.com/hydrabus/hydrafw/wiki/HydraFW-Binary-SPI-mode-guide +# +# Author: Pedro Ribeiro +# Author: Jonathan Borgeaud +# License: GPLv3 (https://choosealicense.com/licenses/gpl-3.0/) +# +import sys +import argparse +import serial + +import signal + +SECTORE_SIZE = 0x1000 # max buffer supported by hydrabus + + +def signal_handler(signal, frame): + print("CTRL+C pressed, cleaning up and exiting") + + +class HydrabusSpiFlash: + + def __init__(self, serial_port): + + self.top_parser = argparse.ArgumentParser(add_help=False) + self.top_parser.add_argument('--spi', + default=1, + help='Set the SPI port to SPI1 or SPI2', + metavar='{1,2}') + + parser = argparse.ArgumentParser( + parents=[self.top_parser], + description='Tool to query NOR memory flash with Hydrabus', + epilog='This script requires python 3.7+, and serial', + usage='''hydra_spiflash_nor_dump.py [] [OPTIONS] + + Commands are: + get_chip_id Return the chip identification + dump Dump the flash in starting at + + ''') + + parser.add_argument('command', + help='dump to dump the flash or get_chip_id') + + args_spi = parser.parse_args() + args = parser.parse_args(sys.argv[1:2]) + if not hasattr(self, args.command): + print('Unrecognized command') + parser.print_help() + sys.exit(1) + + self.serial_port = serial_port + self.hydrabus = None + self.setup(args_spi.spi) + + # use dispatch pattern to invoke method with same name + getattr(self, args.command)() + + def error(self, message): + print(message) + self.cleanup() + sys.exit(1) + + def hex_to_bin(self, num, padding): + return num.to_bytes(padding, byteorder='big') + + def calc_hex_addr(self, addr, add, addr_len): + byte_arr = self.hex_to_bin(addr + add, addr_len) + return byte_arr + + def setup(self, spi): + + # Open serial port + self.hydrabus = serial.Serial(self.serial_port, 115200) + + # Open binary mode + for _ in range(20): + self.hydrabus.write(b'\x00') + if b"BBIO1" not in self.hydrabus.read(5): + self.error( + "Could not get into binary mode, try again or reset hydrabus.") + + self.hydrabus.reset_input_buffer() + + # Switching to SPI mode + self.hydrabus.write(b'\x01') + if b"SPI1" not in self.hydrabus.read(4): + self.error("Cannot set SPI mode, try again or reset hydrabus.") + + if spi == 1: + # Configure SPI port (default polarity and clock phase, SPI1 device) + self.hydrabus.write(b'\x81') + else: + # Configure SPI port (default polarity and clock phase, SPI2 device) + self.hydrabus.write(b'\x80') + if b'\x01' not in self.hydrabus.read(1): + self.error( + "Cannot set SPI device settings, try again or reset hydrabus.") + + def cleanup(self): + + # Return to main binary mode + self.hydrabus.write(b'\x00') + + # reset to console mode + self.hydrabus.write(b'\x0F\n') + + def get_chip_id(self): + + print("Sending RDID command...") + # write-then-read: write one byte, read 3 (rdid) + self.hydrabus.write(b'\x04\x00\x01\x00\x03') + + # send rdid byte (0x9f) + self.hydrabus.write(b'\x9f') + if b'\x01' not in self.hydrabus.read(1): + self.error('Oups something went wrong...') + + chip_id = self.hydrabus.read(3) + if int.from_bytes(chip_id) == 0: + print('Cannot read get chip ID') + + else: + print(f"Chip ID: 0x{chip_id.hex()}") + print("Finished get_chip_id function.") + self.cleanup() + sys.exit(0) + + def dump(self): + parser = argparse.ArgumentParser(description='Dump the NOR flash') + + parser.add_argument('dump_file') + parser.add_argument('n_4k_sectors', type=int) + parser.add_argument('hex_address', help='Starting address') + parser.add_argument('--speed', + action='store', + help='Slow(320Khz) or fast(10.5Mhz)') + args = parser.parse_args(sys.argv[2:]) + if '0x' in args.hex_address: + # we get hexadecimal address, need convert + start_address = int(args.hex_address, 16) + else: + start_address = int(args.hex_address) + + if args.speed == 'slow': + # Set spi speed to 320 Khz + self.hydrabus.write(b'\x60') + else: + # Set spi speed to 10.5 mHz, a conservative fast speed + self.hydrabus.write(b'\x61') + print("Using fast mode (10.5 mHz) to dump the chip.") + if b'\x01' not in self.hydrabus.read(1): + self.error("Cannot set SPI speed, try again or reset hydrabus.") + + print('Starting to read chip...') + print('Reading ' + str(args.n_4k_sectors) + ' sectors') + + sector = 0 + + with open(args.dump_file, 'wb+') as dst: + + print(f"sectors: {args.n_4k_sectors}: {SECTORE_SIZE}") + if args.n_4k_sectors * SECTORE_SIZE > 0xffffff: + print("Size is bigger than 3bytes address, using 0x13 command") + # size is bigger than 3bytes address so use the 0x13 command + while sector < args.n_4k_sectors: + # write-then-read: write 5 bytes + # (1 read cmd + 4 read addr), read SECTORE_SIZE bytes + self.hydrabus.write(b'\x04\x00\x05' + + self.hex_to_bin(SECTORE_SIZE, 2)) + + # read 4bytes address command (\x13) and address + self.hydrabus.write(b'\x13' + self.calc_hex_addr( + start_address, sector * SECTORE_SIZE, 4)) + + # Hydrabus will send \x01 in case of success... + if b'\x01' not in self.hydrabus.read(1): + self.error("Cannot read at address...") + + # ...followed by SECTORE_SIZE read bytes + dst.write(self.hydrabus.read(SECTORE_SIZE)) + print('Read sector ' + str(sector)) + sector += 1 + else: + # the size is less than 3bytes address so use the 0x03 command + while sector < args.n_4k_sectors: + # write-then-read: write 4 bytes + # (1 read cmd + 3 read addr), read SECTORE_SIZE bytes + self.hydrabus.write(b'\x04\x00\x04' + + self.hex_to_bin(SECTORE_SIZE, 2)) + + # read 4bytes address command (\x03) and address + self.hydrabus.write(b'\x03' + self.calc_hex_addr( + start_address, sector * SECTORE_SIZE, 3)) + + # Hydrabus will send \x01 in case of success... + if b'\x01' not in self.hydrabus.read(1): + self.error("Cannot read at address...") + + # ...followed by SECTORE_SIZE read bytes + dst.write(self.hydrabus.read(SECTORE_SIZE)) + print('Read sector ' + str(sector)) + sector += 1 + + print('Finished dumping to ' + args.dump_file) + self.cleanup() + + +if __name__ == '__main__': + + # disable this for debugging + # signal.signal(signal.SIGINT, signal_handler) + + hydra = HydrabusSpiFlash("/dev/ttyACM0")