diff options
| author | Damien George <damien.p.george@gmail.com> | 2019-02-15 15:05:53 +1100 |
|---|---|---|
| committer | Damien George <damien.p.george@gmail.com> | 2019-02-15 15:09:59 +1100 |
| commit | 3669198403e48e5b2001a10d791260b505d8ed5d (patch) | |
| tree | f7d480e7e93d48d0baa5fdaa4b3d9c0db3bc1ccd | |
| parent | 3d0c31e60ed6fe5a104bf282cbe42caca3150146 (diff) | |
stm32/mboot: Add support script which can program mboot and application.
| -rw-r--r-- | ports/stm32/mboot/fwupdate.py | 172 |
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) |
