From 4290a2cc10082e17eee5261d04feb11b581091d4 Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Wed, 20 Jun 2012 14:44:20 +0900 Subject: [PATCH] ST-Link/V2 flash ROM writer --- ChangeLog | 7 + NEWS | 2 +- tool/asm-thumb/README | 2 + tool/asm-thumb/flash_write.S | 39 +++ tool/asm-thumb/opt_bytes_write.S | 29 ++ tool/stlinkv2.py | 475 +++++++++++++++++++++++++++++++ 6 files changed, 553 insertions(+), 1 deletion(-) create mode 100644 tool/asm-thumb/README create mode 100644 tool/asm-thumb/flash_write.S create mode 100644 tool/asm-thumb/opt_bytes_write.S create mode 100755 tool/stlinkv2.py diff --git a/ChangeLog b/ChangeLog index 8d66b32..28e258e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2012-06-20 Niibe Yutaka + + ST-Link/V2 flash ROM writer. + * tool/stlinkv2.py: New. + * tool/asm-thumb/opt_bytes_write.S: New. + * tool/asm-thumb/flash_write.S: New. + 2012-06-19 Niibe Yutaka * Version 0.20. diff --git a/NEWS b/NEWS index fbcc3fb..8607878 100644 --- a/NEWS +++ b/NEWS @@ -23,7 +23,7 @@ Keystring is now computed by SHA-256 (it was SHA1 before). ** Protection improvements (even when internal data is disclosed) Three improvements. (1) Even if PW1 and Reset-code is same, content -of encripted DEK is different now. (2) DEK is now encrypted and +of encrypted DEK is different now. (2) DEK is now encrypted and decrypted by keystring in ECB mode (it was just a kind of xor by single block CFB mode). (3) Key data plus checksum are encrypted in CFB mode with initial vector (it will be able to switch OCB mode diff --git a/tool/asm-thumb/README b/tool/asm-thumb/README new file mode 100644 index 0000000..e24b92a --- /dev/null +++ b/tool/asm-thumb/README @@ -0,0 +1,2 @@ +These assembler program are source code of program fragments in +stlinkv2.py. diff --git a/tool/asm-thumb/flash_write.S b/tool/asm-thumb/flash_write.S new file mode 100644 index 0000000..8f4b16c --- /dev/null +++ b/tool/asm-thumb/flash_write.S @@ -0,0 +1,39 @@ +/* ARM Thumb Assembler code */ +// arm-none-eabi-gcc -Wa,-amhls=flash_write.lst -c flash_write.S + +#define FLASH_CR_PG 0x0001 // == FLASH_SR_BSY +#define FLASH_CR_ERRORS 0x0014 // == PGERR | WRPRTERR +#define FLASH_SR_BSY 0x0001 + +#define FLASH_SR_OFFSET 0x0c +#define FLASH_CR_OFFSET 0x10 + +#define COUNT 0x1000 + + .cpu cortex-m3 + .thumb + movw r2, #COUNT + ldr r0, .SRC_ADDR + ldr r1, .TARGET_ADDR + ldr r4, .FLASH_BASE_ADDR + mov r5, #FLASH_CR_PG + mov r6, #FLASH_CR_ERRORS + mov r7, #0 + str r5, [r4, #FLASH_CR_OFFSET] +0: ldrh r3, [r0, r7] + strh r3, [r1, r7] +1: ldr r3, [r4, #FLASH_SR_OFFSET] + tst r3, r5 + bne 1b + tst r3, r6 + bne 2f + add r7, r7, #0x02 + cmp r7, r2 + bne 0b +2: mov r7, #0 + str r7, [r4, #FLASH_CR_OFFSET] + bkpt #0x00 + .align 2 +.FLASH_BASE_ADDR: .word 0x40022000 +.SRC_ADDR: .word 0x20000038 +.TARGET_ADDR: .word 0x08000000 diff --git a/tool/asm-thumb/opt_bytes_write.S b/tool/asm-thumb/opt_bytes_write.S new file mode 100644 index 0000000..dad89bb --- /dev/null +++ b/tool/asm-thumb/opt_bytes_write.S @@ -0,0 +1,29 @@ +/* ARM Thumb Assembler code */ +// arm-none-eabi-gcc -Wa,-amhls=opt_bytes_write.lst -c opt_bytes_write.S + +#define FLASH_CR_OPTPG 0x0010 +#define FLASH_SR_BSY 0x0001 + +#define FLASH_SR_OFFSET 0x0c +#define FLASH_CR_OFFSET 0x10 + +#define OB_RDP_UNLOCK 0x00a5 + + .cpu cortex-m3 + .thumb + movw r0, #OB_RDP_UNLOCK + ldr r1, .TARGET_ADDR + ldr r2, .FLASH_BASE_ADDR + mov r3, #FLASH_CR_OPTPG + mov r4, #FLASH_SR_BSY + str r3, [r2, #FLASH_CR_OFFSET] + strh r0, [r1] +1: ldr r0, [r2, #FLASH_SR_OFFSET] + tst r0, r4 + bne 1b + mov r0, #0 + str r0, [r2, #FLASH_CR_OFFSET] + bkpt #0x00 + .align 2 +.FLASH_BASE_ADDR: .word 0x40022000 +.TARGET_ADDR: .word 0x1FFFF800 diff --git a/tool/stlinkv2.py b/tool/stlinkv2.py new file mode 100755 index 0000000..300bb1d --- /dev/null +++ b/tool/stlinkv2.py @@ -0,0 +1,475 @@ +#! /usr/bin/python + +""" +stlinkv2.py - a tool to control ST-Link/V2 + +Copyright (C) 2012 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 + +# INPUT: binary file + +# Assumes only single ST-Link/V2 device is attached to computer. + +import usb + +GPIOA=0x40010800 +OPTION_BYTES_ADDR=0x1ffff800 +RDP_KEY=0x00a5 # 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 + + +def uint32(v): + return v[0] + (v[1]<<8) + (v[2]<<16) + (v[3]<<24) + +## HERE comes: "movw r2,#SIZE" instruction +prog_flash_write_body = "\x0A\x48" + "\x0B\x49" + \ + "\x08\x4C" + "\x01\x25" + "\x14\x26" + "\x00\x27" + "\x25\x61" + \ + "\xC3\x5B" + "\xCB\x53" + "\xE3\x68" + "\x2B\x42" + "\xFC\xD1" + \ + "\x33\x42" + "\x02\xD1" + "\x02\x37" + "\x97\x42" + "\xF5\xD1" + \ + "\x00\x27" + "\x27\x61" + "\x00\xBE" + "\x00\x20\x02\x40" + \ + "\x38\x00\x00\x20" +# .SRC_ADDR: 0x20000038 +## HERE comes: target_addr in 4-byte +# .TARGET_ADDR + +def gen_prog_flash_write(addr,size): + return pack(">12), (0xf2 | (size&0x0800)>>9), + (size & 0x00ff), (0x02 | ((size&0x0700) >> 4))) + \ + prog_flash_write_body + pack(">12), (0xf2 | (val&0x0800)>>9), + (val & 0x00ff), (0x00 | ((val&0x0700) >> 4))) + \ + prog_option_bytes_write_body + pack("= 10: + print "ERROR: option bytes write timeout" + break + + status = self.read_memory_u32(FLASH_SR) + self.write_memory_u32(FLASH_CR, FLASH_CR_LOCK) + if (status & FLASH_SR_EOP) == 0: + print "ERROR: option bytes write error" + + 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 ValueError, "option bytes erase failed" + + 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: + print "ERROR: flash write timeout" + break + status = self.read_memory_u32(FLASH_SR) + if (status & FLASH_SR_PGERR) != 0: + print "ERROR: write to a location that was not erased" + if (status & FLASH_SR_WRPRTERR) != 0: + print "ERROR: write to a location that was write protected" + + 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:]) > BLOCK_SIZE: + size = 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 ValueError, "flash erase all failed" + + 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 ValueError, "flash page erase failed" + + def start(self): + mode = self.stl_mode() + if mode == 2: + return + elif mode != 1: + self.exit_from_dfu() + mode = self.stl_mode() + print "Change mode to: %04x" % mode + self.enter_swd() + s = self.get_status() + print "Status: %04x" % s + if self.stl_mode() != 2: + raise ValueError, "Failed to switch debug mode" + + +USB_VENDOR_ST=0x0483 # 0x0483 SGS Thomson Microelectronics +USB_VENDOR_STLINKV2=0x3748 # 0x3748 ST-LINK/V2 + +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: + continue + yield dev + +def compare(data_original, data_in_device): + i = 0 + for d in data_original: + if ord(d) != data_in_device[i]: + raise ValueError, "verify failed at %08x" % i + i += 1 + +if __name__ == '__main__': + erase = False + if sys.argv[1] == '-e': + erase = True + sys.argv.pop(1) + + no_protect = False + if sys.argv[1] == '-n': + no_protect = True + sys.argv.pop(1) + + status_only = False + unlock = False + if sys.argv[1] == '-u': + unlock = True + sys.argv.pop(1) + elif sys.argv[1] == '-s': + status_only = True + else: + filename = sys.argv[1] + f = open(filename) + data = f.read() + f.close() + + for d in stlinkv2_devices(): + try: + stl = stlinkv2(d) + print "Device: ", d.filename + break + except: + pass + stl.start() + core_id = stl.core_id() + chip_id = stl.read_memory_u32(0xE0042000) + # FST-01 chip id: 0x20036410 + print "CORE: %08x, CHIP_ID: %08x" % (core_id, chip_id) + print "Flash ROM read protection:", + protection = stl.protection() + if protection: + print "ON" + if status_only: + print "The MCU is now stopped. No way to run by STLink/V2. Please reset the board" + exit (0) + elif not unlock: + print "Please unlock flash ROM protection, at first. By invoking with -u option" + exit(1) + else: + print "off" + if status_only: + stl.reset_sys() + stl.run() + stl.exit_debug() + exit (0) + elif unlock: + print "No need to unlock. Protection is not enabled." + exit(1) + + print "status: %02x" % stl.get_status() + stl.enter_debug() + + if unlock: + stl.reset_sys() + stl.option_bytes_write(OPTION_BYTES_ADDR,RDP_KEY) + print "Flash ROM read protection disabled. Reset the board, now." + exit(0) + + print "option bytes: %08x" % stl.option_bytes_read() + + if erase: + print "ERASE ALL" + stl.reset_sys() + stl.flash_erase_all() + time.sleep(0.100) + + print "WRITE" + stl.flash_write(0x08000000, data) + + print "VERIFY" + data_received = () + size = len(data) + off = 0 + while size > 0: + if size > 1024: + blk_size = 1024 + else: + blk_size = size + data_received = data_received + stl.read_memory(0x08000000+off, 1024) + size = size - blk_size + off = off + blk_size + compare(data, data_received) + + if not no_protect: + print "PROTECT" + stl.option_bytes_erase() + print "Flash ROM read protection enabled. Reset the board to enable protection."