summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien George <damien.p.george@gmail.com>2019-02-15 15:05:53 +1100
committerDamien George <damien.p.george@gmail.com>2019-02-15 15:09:59 +1100
commit3669198403e48e5b2001a10d791260b505d8ed5d (patch)
treef7d480e7e93d48d0baa5fdaa4b3d9c0db3bc1ccd
parent3d0c31e60ed6fe5a104bf282cbe42caca3150146 (diff)
stm32/mboot: Add support script which can program mboot and application.
-rw-r--r--ports/stm32/mboot/fwupdate.py172
1 files changed, 172 insertions, 0 deletions
diff --git a/ports/stm32/mboot/fwupdate.py b/ports/stm32/mboot/fwupdate.py
new file mode 100644
index 000000000..b2690e8cd
--- /dev/null
+++ b/ports/stm32/mboot/fwupdate.py
@@ -0,0 +1,172 @@
+# Update Mboot or MicroPython from a .dfu.gz file on the board's filesystem
+# MIT license; Copyright (c) 2019 Damien P. George
+
+import struct, time
+import uzlib, machine, stm
+
+
+FLASH_KEY1 = 0x45670123
+FLASH_KEY2 = 0xcdef89ab
+
+
+def check_mem_contains(addr, buf):
+ mem8 = stm.mem8
+ r = range(len(buf))
+ for off in r:
+ if mem8[addr + off] != buf[off]:
+ return False
+ return True
+
+def check_mem_erased(addr, size):
+ mem16 = stm.mem16
+ r = range(0, size, 2)
+ for off in r:
+ if mem16[addr + off] != 0xffff:
+ return False
+ return True
+
+def dfu_read(filename):
+ f = open(filename, 'rb')
+
+ hdr = f.read(3)
+ f.seek(0)
+ if hdr == b'Dfu':
+ pass
+ elif hdr == b'\x1f\x8b\x08':
+ f = uzlib.DecompIO(f, 16 + 15)
+ else:
+ print('Invalid firmware', filename)
+ return None
+
+ elems = []
+
+ hdr = f.read(11)
+ sig, ver, size, num_targ = struct.unpack('<5sBIB', hdr)
+
+ file_offset = 11
+
+ for i in range(num_targ):
+ hdr = f.read(274)
+ sig, alt, has_name, name, t_size, num_elem = struct.unpack('<6sBi255sII', hdr)
+
+ file_offset += 274
+ file_offset_t = file_offset
+ for j in range(num_elem):
+ hdr = f.read(8)
+ addr, e_size = struct.unpack('<II', hdr)
+ data = f.read(e_size)
+ elems.append((addr, data))
+ file_offset += 8 + e_size
+
+ if t_size != file_offset - file_offset_t:
+ print('corrupt DFU', t_size, file_offset - file_offset_t)
+ return None
+
+ if size != file_offset:
+ print('corrupt DFU', size, file_offset)
+ return None
+
+ hdr = f.read(16)
+ hdr = struct.unpack('<HHHH3sBI', hdr)
+
+ return elems
+
+def flash_wait_not_busy():
+ while stm.mem32[stm.FLASH + stm.FLASH_SR] & 1 << 16:
+ machine.idle()
+
+def flash_unlock():
+ stm.mem32[stm.FLASH + stm.FLASH_KEYR] = FLASH_KEY1
+ stm.mem32[stm.FLASH + stm.FLASH_KEYR] = FLASH_KEY2
+
+def flash_lock():
+ stm.mem32[stm.FLASH + stm.FLASH_CR] = 1 << 31 # LOCK
+
+def flash_erase_sector(sector):
+ assert 0 <= sector <= 7 # for F722
+ flash_wait_not_busy()
+ cr = (
+ 2 << 8 # PSIZE = 32 bits
+ | sector << 3 # SNB
+ | 1 << 1 # SER
+ )
+ stm.mem32[stm.FLASH + stm.FLASH_CR] = cr
+ stm.mem32[stm.FLASH + stm.FLASH_CR] = cr | 1 << 16 # STRT
+ flash_wait_not_busy()
+ stm.mem32[stm.FLASH + stm.FLASH_CR] = 0
+
+def flash_write(addr, buf):
+ assert len(buf) % 4 == 0
+ flash_wait_not_busy()
+ cr = (
+ 2 << 8 # PSIZE = 32 bits
+ | 1 << 0 # PG
+ )
+ stm.mem32[stm.FLASH + stm.FLASH_CR] = cr
+ for off in range(0, len(buf), 4):
+ stm.mem32[addr + off] = struct.unpack_from('I', buf, off)[0]
+ flash_wait_not_busy()
+ stm.mem32[stm.FLASH + stm.FLASH_CR] = 0
+
+def update_mboot(filename):
+ print('Loading file', filename)
+
+ mboot_fw = dfu_read(filename)
+ if mboot_fw is None:
+ return
+ if len(mboot_fw) != 1:
+ assert 0
+ mboot_addr, mboot_fw = mboot_fw[0]
+ if mboot_addr != 0x08000000:
+ assert 0
+
+ # TODO: Validate firmware in a simple way
+
+ print('Found Mboot data with size %u.' % len(mboot_fw))
+
+ chk = check_mem_contains(mboot_addr, mboot_fw)
+ if chk:
+ print('Supplied version of Mboot is already on device.')
+ return
+
+ print('Programming Mboot, do not turn off!')
+ time.sleep_ms(50)
+
+ irq = machine.disable_irq()
+ flash_unlock()
+ flash_erase_sector(0)
+ if len(mboot_fw) > 16 * 1024 and not check_mem_erased(mboot_addr + 16 * 1024, 16 * 1024):
+ flash_erase_sector(1)
+ flash_write(mboot_addr, mboot_fw)
+ flash_lock()
+ machine.enable_irq(irq)
+
+ print('New Mboot programmed.')
+
+ if check_mem_contains(mboot_addr, mboot_fw):
+ print('Verification of new Mboot succeeded.')
+ else:
+ print('Verification of new Mboot FAILED! Try rerunning.')
+
+ print('Programming finished, can now reset or turn off.')
+
+def update_mpy(filename, fs_base, fs_len):
+ # Check firmware is of .dfu.gz type
+ try:
+ with open(filename, 'rb') as f:
+ hdr = uzlib.DecompIO(f, 16 + 15).read(6)
+ except Exception:
+ hdr = None
+ if hdr != b'DfuSe\x01':
+ print('Firmware must be a .dfu.gz file.')
+ return
+
+ ELEM_TYPE_END = 1
+ ELEM_TYPE_MOUNT = 2
+ ELEM_TYPE_FSLOAD = 3
+ ELEM_MOUNT_FAT = 1
+ mount_point = 1
+ mount = struct.pack('<BBBBLL', ELEM_TYPE_MOUNT, 10, mount_point, ELEM_MOUNT_FAT, fs_base, fs_len)
+ fsup = struct.pack('<BBB', ELEM_TYPE_FSLOAD, 1 + len(filename), mount_point) + bytes(filename, 'ascii')
+ end = struct.pack('<BB', ELEM_TYPE_END, 0)
+ machine.bootloader(mount + fsup + end)