#! /usr/bin/python """ stlinkv2.py - a tool to control ST-Link/V2 Copyright (C) 2012, 2013, 2015 Free Software Initiative of Japan Author: NIIBE Yutaka This file is a part of Gnuk, a GnuPG USB Token implementation. Gnuk is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Gnuk is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . """ from struct import * import sys, time import usb from colorama import init as colorama_init, Fore, Back, Style from array import array # INPUT: binary file # Assumes only single ST-Link/V2 device is attached to computer. CORE_ID_CORTEX_M3=0x1ba01477 CORE_ID_CORTEX_M0=0x0bb11477 CHIP_ID_STM32F103xB=0x20036410 CHIP_ID_STM32F103xE=0x10016414 # CHIP_ID_STM32F0 0x20006440 # CHIP_ID_STM32F030?? 0x10006444; FSM-55 GPIOA=0x40010800 GPIOB=0x40010C00 OPTION_BYTES_ADDR=0x1ffff800 RDP_KEY_F1=0x00a5 # Unlock readprotection RDP_KEY_F0=0x00aa # Unlock readprotection FLASH_BASE_ADDR=0x40022000 FLASH_KEYR= FLASH_BASE_ADDR+0x04 FLASH_OPTKEYR= FLASH_BASE_ADDR+0x08 FLASH_SR= FLASH_BASE_ADDR+0x0c FLASH_CR= FLASH_BASE_ADDR+0x10 FLASH_AR= FLASH_BASE_ADDR+0x14 FLASH_OBR= FLASH_BASE_ADDR+0x1c FLASH_KEY1=0x45670123 FLASH_KEY2=0xcdef89ab FLASH_SR_BSY= 0x0001 FLASH_SR_PGERR= 0x0004 FLASH_SR_WRPRTERR= 0x0010 FLASH_SR_EOP= 0x0020 FLASH_CR_PG= 0x0001 FLASH_CR_PER= 0x0002 FLASH_CR_MER= 0x0004 FLASH_CR_OPTPG= 0x0010 FLASH_CR_OPTER= 0x0020 FLASH_CR_STRT= 0x0040 FLASH_CR_LOCK= 0x0080 FLASH_CR_OPTWRE= 0x0200 SPI1= 0x40013000 def uint32(v): return v[0] + (v[1]<<8) + (v[2]<<16) + (v[3]<<24) prog_flash_write_body = b"\x0D\x4A\x0B\x48\x0B\x49\x09\x4C\x01\x25\x14\x26\x00\x27\x25\x61" + \ b"\xC3\x5B\xCB\x53\xE3\x68\x2B\x42\xFC\xD1\x33\x42\x02\xD1\x02\x37\x97\x42\xF5\xD1" + \ b"\x00\x27\x27\x61\x00\xBE\xC0\x46\x00\x20\x02\x40\x3C\x00\x00\x20" # .SRC_ADDR: 0x2000003C ## HERE comes: target_addr in 4-byte # .TARGET_ADDR ## HERE comes: size in 4-byte # .SIZE def gen_prog_flash_write(addr,size): return prog_flash_write_body + pack("> 12) & 0x0f, (val >> 6) & 0x3f, val & 0x3f) # For FST-01-00 and FST-01: LED on, USB connect def setup_gpio(self): apb2enr = self.read_memory_u32(0x40021018) apb2enr = apb2enr | 4 | 8 | 0x1000 # Enable port A, port B, and SPI1 self.write_memory_u32(0x40021018, apb2enr) # RCC->APB2ENR self.write_memory_u32(0x4002100c, 4|8|0x1000) # RCC->APB2RSTR self.write_memory_u32(0x4002100c, 0) self.write_memory_u32(GPIOA+0x0c, 0xffffffff) # ODR self.write_memory_u32(GPIOA+0x04, 0x88888383) # CRH self.write_memory_u32(GPIOA+0x00, 0xBBB38888) # CRL self.write_memory_u32(GPIOB+0x0c, 0xffffffff) # ODR self.write_memory_u32(GPIOB+0x04, 0x88888888) # CRH self.write_memory_u32(GPIOB+0x00, 0x88888883) # CRL # For FST-01-00 and FST-01: LED on, USB disconnect def usb_disconnect(self): self.write_memory_u32(GPIOA+0x0c, 0xfffffbff) # ODR # For FST-01-00 and FST-01: LED off, USB connect def finish_gpio(self): self.write_memory_u32(GPIOA+0x0c, 0xfffffeff) # ODR self.write_memory_u32(GPIOB+0x0c, 0xfffffffe) # ODR apb2enr = self.read_memory_u32(0x40021018) apb2enr = apb2enr & ~(4 | 8 | 0x1000) self.write_memory_u32(0x40021018, apb2enr) # RCC->APB2ENR def spi_flash_init(self): self.write_memory_u32(SPI1+0x00, 0x0004); # CR1 <= MSTR i2scfgr = self.read_memory_u32(SPI1+0x1c) # I2SCFGR i2scfgr = i2scfgr & 0xf7ff # self.write_memory_u32(SPI1+0x1c, i2scfgr); # I2SCFGR <= SPI mode self.write_memory_u32(SPI1+0x10, 7); # CRCPR <= 7 self.write_memory_u32(SPI1+0x04, 0x04); # CR2 <= SSOE self.write_memory_u32(SPI1+0x00, 0x0044); # CR1 <= MSTR | SPE def spi_flash_select(self, enable): if enable: self.write_memory_u32(GPIOA+0x0c, 0xffffffef) # ODR else: self.write_memory_u32(GPIOA+0x0c, 0xffffffff) # ODR def spi_flash_sendbyte(self, v): i = 0 while True: status = self.read_memory_u32(SPI1+0x08) # SR if status & 0x02 != 0: # TXE (Data Empty) break time.sleep(0.01) i = i + 1 if i > 10: raise TimeOutError('spi_flash_sendbyte') self.write_memory_u32(SPI1+0x0c, v) # DR i = 0 while True: status = self.read_memory_u32(SPI1+0x08) # SR if status & 0x01 != 0: # RXNE (Data Not Empty) break time.sleep(0.01) i = i + 1 if i > 10: raise TimeOutError('spi_flash_sendbyte') v = self.read_memory_u32(SPI1+0x0c) # DR return v def spi_flash_read_id(self): self.spi_flash_select(True) self.spi_flash_sendbyte(0x9f) t0 = self.spi_flash_sendbyte(0xa5) t1 = self.spi_flash_sendbyte(0xa5) t2 = self.spi_flash_sendbyte(0xa5) self.spi_flash_select(False) return (t0 << 16) | (t1 << 8) | t2 def protection(self): return (self.read_memory_u32(FLASH_OBR) & 0x0002) != 0 def blank_check(self): prog = gen_prog_blank_check(self.flash_size) self.write_memory(SRAM_ADDRESS, prog) self.write_reg(15, SRAM_ADDRESS) self.run() i = 0 while self.get_status() == 0x80: time.sleep(0.050) i = i + 1 if i >= 10: raise TimeOutError("blank check") r0_value = self.read_reg(0) return r0_value == 0 def option_bytes_read(self): return self.read_memory_u32(OPTION_BYTES_ADDR) def option_bytes_write(self,addr,val): self.write_memory_u32(FLASH_KEYR, FLASH_KEY1) self.write_memory_u32(FLASH_KEYR, FLASH_KEY2) self.write_memory_u32(FLASH_SR, FLASH_SR_EOP | FLASH_SR_WRPRTERR | FLASH_SR_PGERR) self.write_memory_u32(FLASH_OPTKEYR, FLASH_KEY1) self.write_memory_u32(FLASH_OPTKEYR, FLASH_KEY2) prog = gen_prog_option_bytes_write(addr,val) self.write_memory(SRAM_ADDRESS, prog) self.write_reg(15, SRAM_ADDRESS) self.run() i = 0 while self.get_status() == 0x80: time.sleep(0.050) i = i + 1 if i >= 10: raise TimeOutError("option bytes write") status = self.read_memory_u32(FLASH_SR) self.write_memory_u32(FLASH_CR, FLASH_CR_LOCK) if (status & FLASH_SR_EOP) == 0: raise OperationFailure("option bytes write") def option_bytes_erase(self): self.write_memory_u32(FLASH_KEYR, FLASH_KEY1) self.write_memory_u32(FLASH_KEYR, FLASH_KEY2) self.write_memory_u32(FLASH_SR, FLASH_SR_EOP | FLASH_SR_WRPRTERR | FLASH_SR_PGERR) self.write_memory_u32(FLASH_OPTKEYR, FLASH_KEY1) self.write_memory_u32(FLASH_OPTKEYR, FLASH_KEY2) self.write_memory_u32(FLASH_CR, FLASH_CR_OPTER) self.write_memory_u32(FLASH_CR, FLASH_CR_STRT | FLASH_CR_OPTER) i = 0 while True: status = self.read_memory_u32(FLASH_SR) if (status & FLASH_SR_BSY) == 0: break i = i + 1 if i >= 1000: break self.write_memory_u32(FLASH_CR, FLASH_CR_LOCK) if (status & FLASH_SR_EOP) == 0: raise OperationFailure("option bytes erase") def flash_write_internal(self, addr, data, off, size): prog = gen_prog_flash_write(addr,size) self.write_memory(SRAM_ADDRESS, prog+data[off:off+size]) self.write_reg(15, SRAM_ADDRESS) self.run() i = 0 while self.get_status() == 0x80: time.sleep(0.050) i = i + 1 if i >= BLOCK_WRITE_TIMEOUT: raise TimeOutError("flash write") status = self.read_memory_u32(FLASH_SR) if (status & FLASH_SR_PGERR) != 0: raise OperationFailure("flash write: write to not erased part") if (status & FLASH_SR_WRPRTERR) != 0: raise OperationFailure("flash write: write to protected part") def flash_write(self, addr, data): self.write_memory_u32(FLASH_KEYR, FLASH_KEY1) self.write_memory_u32(FLASH_KEYR, FLASH_KEY2) self.write_memory_u32(FLASH_SR, FLASH_SR_EOP | FLASH_SR_WRPRTERR | FLASH_SR_PGERR) off = 0 while True: if len(data[off:]) > self.flash_block_size: size = self.flash_block_size self.flash_write_internal(addr, data, off, size) off = off + size addr = addr + size else: size = len(data[off:]) self.flash_write_internal(addr, data, off, size) break self.write_memory_u32(FLASH_CR, FLASH_CR_LOCK) def flash_erase_all(self): self.write_memory_u32(FLASH_KEYR, FLASH_KEY1) self.write_memory_u32(FLASH_KEYR, FLASH_KEY2) self.write_memory_u32(FLASH_SR, FLASH_SR_EOP | FLASH_SR_WRPRTERR | FLASH_SR_PGERR) self.write_memory_u32(FLASH_CR, FLASH_CR_MER) self.write_memory_u32(FLASH_CR, FLASH_CR_STRT | FLASH_CR_MER) i = 0 while True: status = self.read_memory_u32(FLASH_SR) if (status & FLASH_SR_BSY) == 0: break i = i + 1 time.sleep(0.050) if i >= 100: break self.write_memory_u32(FLASH_CR, FLASH_CR_LOCK) if (status & FLASH_SR_EOP) == 0: raise OperationFailure("flash erase all") def flash_erase_page(self, addr): self.write_memory_u32(FLASH_KEYR, FLASH_KEY1) self.write_memory_u32(FLASH_KEYR, FLASH_KEY2) self.write_memory_u32(FLASH_SR, FLASH_SR_EOP | FLASH_SR_WRPRTERR | FLASH_SR_PGERR) self.write_memory_u32(FLASH_CR, FLASH_CR_PER) self.write_memory_u32(FLASH_AR, addr) self.write_memory_u32(FLASH_CR, FLASH_CR_STRT | FLASH_CR_PER) i = 0 while True: status = self.read_memory_u32(FLASH_SR) if (status & FLASH_SR_BSY) == 0: break i = i + 1 if i >= 1000: break self.write_memory_u32(FLASH_CR, FLASH_CR_LOCK) if (status & FLASH_SR_EOP) == 0: raise OperationFailure("flash page erase") def start(self): mode = self.stl_mode() if mode == 2: self.exit_from_debug_swd() elif mode == 5: self.exit_from_debug_swim() elif mode != 1 and mode != 4: self.exit_from_dfu() new_mode = self.stl_mode() print("Change ST-Link/V2 mode %04x -> %04x" % (mode, new_mode)) self.control_nrst(2) self.enter_swd() s = self.get_status() if s != 0x0080: print("Status is %04x" % s) self.run() s = self.get_status() if s != 0x0080: # DCB_DHCSR DBGKEY self.write_debug_reg(0xE000EDF0, 0xA05F0000) s = self.get_status() if s != 0x0080: raise ValueError("Status of core is not running.", s) mode = self.stl_mode() if mode != 2: raise ValueError("Failed to switch debug mode.", mode) self.core_id = self.get_core_id() if self.core_id == CORE_ID_CORTEX_M3: self.chip_stm32 = True elif self.core_id == CORE_ID_CORTEX_M0: self.chip_stm32 = False else: raise ValueError("Unknown core ID", self.core_id) if self.chip_stm32: self.rdp_key = RDP_KEY_F1 self.flash_block_size = FLASH_BLOCK_SIZE_F1 self.require_nrst = False self.external_spi_flash = True self.protection_feature = True else: self.rdp_key = RDP_KEY_F0 self.flash_block_size = FLASH_BLOCK_SIZE_F0 self.require_nrst = True self.external_spi_flash = False self.protection_feature = False return self.core_id def get_chip_id(self): if self.chip_stm32: self.chip_id = self.read_memory_u32(0xE0042000) self.flash_size = (self.read_memory_u32(0x1ffff7e0) & 0xffff)*1024 if self.chip_id == CHIP_ID_STM32F103xB: pass elif self.chip_id == CHIP_ID_STM32F103xE: pass else: raise ValueError("Unknown chip ID", self.chip_id) else: self.chip_id = self.read_memory_u32(0x40015800) self.flash_size = (self.read_memory_u32(0x1ffff7cc) & 0xffff)*1024 print("Flash size: %dKiB" % (self.flash_size/1024)) return self.chip_id def get_rdp_key(self): return self.rdp_key def has_spi_flash(self): return self.external_spi_flash def has_protection(self): return self.protection_feature USB_VENDOR_ST=0x0483 # 0x0483 SGS Thomson Microelectronics USB_VENDOR_STLINKV2=0x3748 # 0x3748 ST-LINK/V2 USB_VENDOR_STLINKV2_1=0x374b # 0x374b ST-LINK/V2_1 def stlinkv2_devices(): busses = usb.busses() for bus in busses: devices = bus.devices for dev in devices: if dev.idVendor != USB_VENDOR_ST: continue if dev.idProduct != USB_VENDOR_STLINKV2 and dev.idProduct != USB_VENDOR_STLINKV2_1: continue yield dev def compare(data_original, data_in_device): i = 0 for d in data_original: if d != data_in_device[i]: raise ValueError("Verify failed at:", i) i += 1 def open_stlinkv2(): for d in stlinkv2_devices(): try: stl = stlinkv2(d) return stl except: pass return None def help(): print("stlinkv2.py [-h]: Show this help message") print("stlinkv2.py [-e]: Erase flash ROM") print("stlinkv2.py [-u]: Unlock flash ROM") print("stlinkv2.py [-s]: Show status") print("stlinkv2.py [-b] [-n] [-r] [-i] FILE: Write content of FILE to flash ROM") print(" -b: Blank check before write (auto erase when not blank)") print(" -n: Don't enable read protection after write") print(" -r: Don't reset after write") print(" -i: Don't test SPI flash") def main(show_help, erase_only, no_protect, spi_flash_check, reset_after_successful_write, skip_blank_check, status_only, unlock, data): if show_help or len(sys.argv) != 1: help() return 1 stl = open_stlinkv2() if not stl: raise ValueError("No ST-Link/V2 device found.", None) print("ST-Link/V2 version info: %d %d %d" % stl.version()) core_id = stl.start() stl.control_nrst(2) stl.enter_debug() status = stl.get_status() if status != 0x0081: print("Core does not halt, try API V2 halt.") # DCB_DHCSR DBGKEY|C_HALT|C_DEBUGEN stl.write_debug_reg(0xE000EDF0, 0xA05F0003) status = stl.get_status() stl.write_debug_reg(0xE000EDF0, 0xA05F0003) status = stl.get_status() if status != 0x0081: raise ValueError("Status of core is not halt.", status) chip_id = stl.get_chip_id() # FST-01 chip id: 0x20036410 print("CORE: %08x, CHIP_ID: %08x" % (core_id, chip_id)) protection = stl.protection() print("Flash ROM read protection: " + ("ON" if protection else "off")) option_bytes = stl.option_bytes_read() print("Option bytes: %08x" % option_bytes) rdp_key = stl.get_rdp_key() if (option_bytes & 0xff) == rdp_key: ob_protection_enable = False else: ob_protection_enable = True if protection: if status_only: print("The MCU is now stopped.") return 0 elif not unlock: raise OperationFailure("Flash ROM is protected") else: if not skip_blank_check: stl.reset_sys() blank = stl.blank_check() print("Flash ROM blank check: %s" % blank) else: blank = True if status_only: stl.reset_sys() stl.run() stl.exit_debug() return 0 elif unlock and not ob_protection_enable: print("No need to unlock. Protection is not enabled.") return 1 if erase_only: if blank: print("No need to erase") return 0 stl.setup_gpio() if unlock: if option_bytes != 0xff: stl.reset_sys() stl.option_bytes_erase() stl.reset_sys() stl.option_bytes_write(OPTION_BYTES_ADDR,rdp_key) stl.usb_disconnect() time.sleep(0.100) stl.finish_gpio() print("Flash ROM read protection disabled. Reset the board, now.") return 0 if spi_flash_check and stl.has_spi_flash(): stl.spi_flash_init() id = stl.spi_flash_read_id() print("SPI Flash ROM ID: %06x" % id) if id != 0xbf254a: raise ValueError("bad spi flash ROM ID") if not blank: print("ERASE ALL") stl.reset_sys() stl.flash_erase_all() if erase_only: stl.usb_disconnect() time.sleep(0.100) stl.finish_gpio() return 0 time.sleep(0.100) print("WRITE") stl.flash_write(0x08000000, data) print("VERIFY") data_received = array('B') size = len(data) off = 0 while size > 0: if size > 1024: blk_size = 1024 else: blk_size = size data_received = data_received + array('B', stl.read_memory(0x08000000+off, blk_size)) size = size - blk_size off = off + blk_size compare(array('B', data), data_received) if not no_protect and stl.has_protection(): print("PROTECT") stl.option_bytes_erase() print("Flash ROM read protection enabled. Reset the board to enable protection.") if reset_after_successful_write: stl.control_nrst(2) stl.usb_disconnect() stl.reset_sys() stl.run() stl.exit_debug() else: stl.finish_gpio() stl.shutdown() return 0 if __name__ == '__main__': show_help = False erase_only = False no_protect = False reset_after_successful_write = True skip_blank_check=True status_only = False unlock = False data = None spi_flash_check = True while len(sys.argv) > 1: if sys.argv[1] == '-h': sys.argv.pop(1) show_help = True break elif sys.argv[1] == '-e': sys.argv.pop(1) erase_only = True skip_blank_check=False break elif sys.argv[1] == '-u': sys.argv.pop(1) unlock = True break elif sys.argv[1] == '-s': sys.argv.pop(1) status_only = True skip_blank_check=False break elif sys.argv[1] == '-b': skip_blank_check=False elif sys.argv[1] == '-n': no_protect = True elif sys.argv[1] == '-r': reset_after_successful_write = False elif sys.argv[1] == '-i': spi_flash_check = False else: filename = sys.argv[1] f = open(filename,'rb') data = f.read() f.close() if len(data) % 1: raise ValueError("The size of file should be even") sys.argv.pop(1) colorama_init() try: r = main(show_help, erase_only, no_protect, spi_flash_check, reset_after_successful_write, skip_blank_check, status_only, unlock, data) if r == 0: print(Fore.WHITE + Back.BLUE + Style.BRIGHT + "SUCCESS" + Style.RESET_ALL) sys.exit(r) except Exception as e: print(Back.RED + Style.BRIGHT + repr(e) + Style.RESET_ALL)