mirror of
https://github.com/licsber/micropython.git
synced 2024-09-20 00:50:24 +08:00
stm32/boards/NUCLEO_WB55: Add error handling to firmware update scripts.
In-the-field use of these FUS/WS firmware update scripts has exposed some weak points, causing corrupted FUS/WS firmware to be flashed to the unit. The problems are mostly caused with the ST GUI application, but sometimes from un-recognised failures during bin file transfer to the WB55 prior to running the rfcore_firmware.py script. Other failures were caused by incorrect load addresses being used, again both from user error copying the address from the HTML release notes to the GUI tool, but also from similarly not updating the address correctly in rfcore_firmware.py To guard against these errors and make it easier to prepare different versions, this commit adds a few features to the rfcore firmware update tools: - When creating the bin file, automatically parse the release note in the folder to get the correct address. - Add a footer to the bin file containing the name, version, CRC, address etc. - Before flashing rfcore, check if the same version is already installed. - Verify the CRC and obfuscation key before flashing bin. - Log the name and version of file being flashed.
This commit is contained in:
parent
6baeded322
commit
6804b6f54f
@ -34,36 +34,54 @@
|
|||||||
#
|
#
|
||||||
# To perform a firmware update:
|
# To perform a firmware update:
|
||||||
#
|
#
|
||||||
# 1. Generate "obfuscated" binary images using rfcore_makefirmware.py
|
# 1. Generate "obfuscated" binary images using rfcore_makefirmware.py, eg.
|
||||||
# ./boards/NUCLEO_WB55/rfcore_makefirmware.py ~/src/github.com/STMicroelectronics/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x/ /tmp
|
# $ python3 ./boards/NUCLEO_WB55/rfcore_makefirmware.py ~/src/github.com/STMicroelectronics/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x/ /tmp
|
||||||
# This will generate /tmp/{fus_102,fus_110,ws_ble_hci}.bin
|
# This will generate /tmp/{fus_102,fus_110,ws_ble_hci}.bin
|
||||||
|
# It may warn that stm32wb5x_FUS_fw_1_0_2.bin cannot be found, newer packs don't include this
|
||||||
|
# which can be ignored unless your currently flashed FUS is older than 1.0.2
|
||||||
#
|
#
|
||||||
# 2. Copy required files to the device filesystem.
|
# 2. Copy required files to the device filesystem.
|
||||||
# In general, it's always safe to copy all three files and the updater will
|
# $ mpremote cp /tmp/fus_102.bin :
|
||||||
# figure out what needs to be done. This is the recommended option.
|
# $ mpremote cp /tmp/fus_110.bin :
|
||||||
# However, if you already have the latest FUS (1.1.0) installed, then just the
|
# $ mpremote cp /tmp/ws_ble_hci.bin :
|
||||||
# WS firmware is required.
|
# $ mpremote cp ./boards/NUCLEO_WB55/rfcore_firmware.py :
|
||||||
# If a FUS binary is present, then the existing WS will be removed so it's a good
|
# In general, it's always safe to copy all three files and the updater will
|
||||||
# idea to always include the WS binary if updating FUS.
|
# figure out what needs to be done. This is the recommended option.
|
||||||
# Note that a WS binary will not be installed unless FUS 1.1.0 is installed.
|
# However, if you already have the latest FUS (1.1.0) installed, then just the
|
||||||
|
# WS firmware is required.
|
||||||
|
# If a FUS binary is present, then the existing WS will be removed so it's a good
|
||||||
|
# idea to always include the WS binary if updating FUS.
|
||||||
|
# Note that a WS binary will not be installed unless FUS 1.1.0 is installed.
|
||||||
#
|
#
|
||||||
# 3. Ensure boot.py calls `rfcore_firmware.resume()`.
|
# 3. Ensure boot.py calls `rfcore_firmware.resume()`.
|
||||||
# The WB55 will reset several times during the firmware update process, so this
|
# $ mpremote exec "import rfcore_firmware; rfcore_firmware.install_boot()"
|
||||||
# script manages the update state using RTC backup registers.
|
# The WB55 will reset several times during the firmware update process, so this
|
||||||
# `rfcore_firmware.resume()` will continue the update operation on startup to
|
# script manages the update state using RTC backup registers.
|
||||||
# resume any in-progress update operation, and either trigger another reset, or
|
# `rfcore_firmware.resume()` will continue the update operation on startup to
|
||||||
# return 0 to indicate that the operation completed successfully, or a reason
|
# resume any in-progress update operation, and either trigger another reset, or
|
||||||
# code (see REASON_* below) to indicate failure.
|
# return 0 to indicate that the operation completed successfully, or a reason
|
||||||
|
# code (see REASON_* below) to indicate failure.
|
||||||
#
|
#
|
||||||
# 4. Call rfcore_firmware.check_for_updates() to start the update process.
|
# 4. Call rfcore_firmware.check_for_updates() to start the update process.
|
||||||
# The device will then immediately reboot and when the firmware update completes,
|
# $ mpremote exec "import rfcore_firmware; rfcore_firmware.check_for_updates()"
|
||||||
# the status will be returned from rfcore_firmware.resume(). See the REASON_ codes below.
|
# The device will then immediately reboot and when the firmware update completes,
|
||||||
# You can use the built-in stm.rfcore_fw_version() to query the installed version
|
# the status will be returned from rfcore_firmware.resume(). See the REASON_ codes below.
|
||||||
# from your application code.
|
# You can use the built-in stm.rfcore_fw_version() to query the installed version
|
||||||
|
# from your application code.
|
||||||
|
|
||||||
import struct, os
|
import struct, os
|
||||||
import machine, stm
|
|
||||||
from micropython import const
|
try:
|
||||||
|
import machine, stm
|
||||||
|
from ubinascii import crc32
|
||||||
|
from micropython import const
|
||||||
|
except ImportError:
|
||||||
|
# cpython
|
||||||
|
from binascii import crc32
|
||||||
|
|
||||||
|
machine = stm = None
|
||||||
|
const = lambda x: x
|
||||||
|
|
||||||
|
|
||||||
_OGF_VENDOR = const(0x3F)
|
_OGF_VENDOR = const(0x3F)
|
||||||
|
|
||||||
@ -174,13 +192,6 @@ _PATH_FUS_102 = "fus_102.bin"
|
|||||||
_PATH_FUS_110 = "fus_110.bin"
|
_PATH_FUS_110 = "fus_110.bin"
|
||||||
_PATH_WS_BLE_HCI = "ws_ble_hci.bin"
|
_PATH_WS_BLE_HCI = "ws_ble_hci.bin"
|
||||||
|
|
||||||
# This address is correct for versions up to v1.8 (assuming existing firmware deleted).
|
|
||||||
# Note any address from the end of the filesystem to the SFSA would be fine, but if
|
|
||||||
# the FUS is fixed in the future to use the specified address then these are the "correct"
|
|
||||||
# ones.
|
|
||||||
_ADDR_FUS = 0x080EC000
|
|
||||||
_ADDR_WS_BLE_HCI = 0x080DC000
|
|
||||||
|
|
||||||
# When installing the FUS/WS it can take a long time to return to the first
|
# When installing the FUS/WS it can take a long time to return to the first
|
||||||
# GET_STATE HCI command.
|
# GET_STATE HCI command.
|
||||||
# e.g. Installing stm32wb5x_BLE_Stack_full_fw.bin takes 3600ms to respond.
|
# e.g. Installing stm32wb5x_BLE_Stack_full_fw.bin takes 3600ms to respond.
|
||||||
@ -242,10 +253,79 @@ class _Flash:
|
|||||||
machine.mem32[stm.FLASH + stm.FLASH_CR] = 0
|
machine.mem32[stm.FLASH + stm.FLASH_CR] = 0
|
||||||
|
|
||||||
|
|
||||||
def _copy_file_to_flash(filename, addr):
|
def validate_crc(f):
|
||||||
|
"""Should match copy of function in rfcore_makefirmware.py to confirm operation"""
|
||||||
|
f.seek(0)
|
||||||
|
file_crc = 0
|
||||||
|
chunk = 16 * 1024
|
||||||
|
buff = bytearray(chunk)
|
||||||
|
while True:
|
||||||
|
read = f.readinto(buff)
|
||||||
|
if read < chunk:
|
||||||
|
file_crc = crc32(buff[0:read], file_crc)
|
||||||
|
break
|
||||||
|
file_crc = crc32(buff, file_crc)
|
||||||
|
|
||||||
|
file_crc = 0xFFFFFFFF & -file_crc - 1
|
||||||
|
f.seek(0)
|
||||||
|
return file_crc == 0
|
||||||
|
|
||||||
|
|
||||||
|
def check_file_details(filename):
|
||||||
|
with open(filename, "rb") as f:
|
||||||
|
if not validate_crc(f):
|
||||||
|
raise ValueError("file validation failed: incorrect crc")
|
||||||
|
|
||||||
|
# Check the footer on the file
|
||||||
|
f.seek(-64, 2)
|
||||||
|
footer = f.read()
|
||||||
|
details = struct.unpack("<37sIIIIbbbII", footer)
|
||||||
|
(
|
||||||
|
src_filename,
|
||||||
|
addr_1m,
|
||||||
|
addr_640k,
|
||||||
|
addr_512k,
|
||||||
|
addr_256k,
|
||||||
|
vers_major,
|
||||||
|
vers_minor,
|
||||||
|
vers_patch,
|
||||||
|
KEY,
|
||||||
|
crc,
|
||||||
|
) = details
|
||||||
|
src_filename = src_filename.strip(b"\x00").decode()
|
||||||
|
if KEY != _OBFUSCATION_KEY:
|
||||||
|
raise ValueError("file validation failed: incorrect key")
|
||||||
|
|
||||||
|
return (
|
||||||
|
src_filename,
|
||||||
|
addr_1m,
|
||||||
|
addr_640k,
|
||||||
|
addr_512k,
|
||||||
|
addr_256k,
|
||||||
|
(vers_major, vers_minor, vers_patch),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _copy_file_to_flash(filename):
|
||||||
flash = _Flash()
|
flash = _Flash()
|
||||||
flash.unlock()
|
flash.unlock()
|
||||||
|
# Reset any previously stored address
|
||||||
|
_write_target_addr(0)
|
||||||
try:
|
try:
|
||||||
|
(
|
||||||
|
src_filename,
|
||||||
|
addr_1m,
|
||||||
|
addr_640k,
|
||||||
|
addr_512k,
|
||||||
|
addr_256k,
|
||||||
|
vers,
|
||||||
|
) = check_file_details(filename)
|
||||||
|
|
||||||
|
# TODO add support for querying the correct flash size on chip
|
||||||
|
addr = load_addr = addr_1m
|
||||||
|
|
||||||
|
log(f"Writing {src_filename} v{vers[0]}.{vers[1]}.{vers[2]} to addr: 0x{addr:x}")
|
||||||
|
|
||||||
# Erase the entire staging area in flash.
|
# Erase the entire staging area in flash.
|
||||||
erase_addr = STAGING_AREA_START
|
erase_addr = STAGING_AREA_START
|
||||||
sfr_sfsa = machine.mem32[stm.FLASH + stm.FLASH_SFR] & 0xFF
|
sfr_sfsa = machine.mem32[stm.FLASH + stm.FLASH_SFR] & 0xFF
|
||||||
@ -266,6 +346,9 @@ def _copy_file_to_flash(filename, addr):
|
|||||||
flash.write(addr, buf, sz, _OBFUSCATION_KEY)
|
flash.write(addr, buf, sz, _OBFUSCATION_KEY)
|
||||||
addr += 4096
|
addr += 4096
|
||||||
|
|
||||||
|
# Cache the intended target load address
|
||||||
|
_write_target_addr(load_addr)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
flash.lock()
|
flash.lock()
|
||||||
|
|
||||||
@ -308,17 +391,25 @@ def _fus_fwdelete():
|
|||||||
return _run_sys_hci_cmd(_OGF_VENDOR, _OCF_FUS_FW_DELETE)
|
return _run_sys_hci_cmd(_OGF_VENDOR, _OCF_FUS_FW_DELETE)
|
||||||
|
|
||||||
|
|
||||||
def _fus_run_fwupgrade(addr):
|
def _fus_run_fwupgrade():
|
||||||
# Note: Address is ignored by the FUS (see comments above).
|
# Note: Address is ignored by the FUS (see comments above).
|
||||||
|
addr = _read_target_addr()
|
||||||
|
if not addr:
|
||||||
|
log(f"Update failed: Invalid load address: 0x{addr:x}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
log(f"Loading to: 0x{addr:x}")
|
||||||
return _run_sys_hci_cmd(_OGF_VENDOR, _OCF_FUS_FW_UPGRADE, struct.pack("<I", addr))
|
return _run_sys_hci_cmd(_OGF_VENDOR, _OCF_FUS_FW_UPGRADE, struct.pack("<I", addr))
|
||||||
|
|
||||||
|
|
||||||
# Get/set current state/reason to RTC Backup Domain.
|
if stm:
|
||||||
# Using the second- and third-last registers (17, 18) as the final one (19)
|
# Get/set current state/reason/addr to RTC Backup Domain.
|
||||||
# is reserved by powerctrl.c for restoring the frequency.
|
# Using the second, third, and fourth-last registers (16, 17, 18) as
|
||||||
# Can be overridden if necessary.
|
# the final one (19) is reserved by powerctrl.c for restoring the frequency.
|
||||||
REG_RTC_STATE = stm.RTC + stm.RTC_BKP18R
|
# Can be overridden if necessary.
|
||||||
REG_RTC_REASON = stm.RTC + stm.RTC_BKP17R
|
REG_RTC_STATE = stm.RTC + stm.RTC_BKP18R
|
||||||
|
REG_RTC_REASON = stm.RTC + stm.RTC_BKP17R
|
||||||
|
REG_RTC_ADDR = stm.RTC + stm.RTC_BKP16R
|
||||||
|
|
||||||
|
|
||||||
def _read_state():
|
def _read_state():
|
||||||
@ -339,8 +430,16 @@ def _write_failure_state(reason):
|
|||||||
return reason
|
return reason
|
||||||
|
|
||||||
|
|
||||||
|
def _read_target_addr():
|
||||||
|
return machine.mem32[REG_RTC_ADDR]
|
||||||
|
|
||||||
|
|
||||||
|
def _write_target_addr(addr):
|
||||||
|
machine.mem32[REG_RTC_ADDR] = addr
|
||||||
|
|
||||||
|
|
||||||
# Check for the presence of a given file and attempt to start installing it.
|
# Check for the presence of a given file and attempt to start installing it.
|
||||||
def _stat_and_start_copy(path, addr, copying_state, copied_state):
|
def _stat_and_start_copy(path, copying_state, copied_state):
|
||||||
try:
|
try:
|
||||||
os.stat(path)
|
os.stat(path)
|
||||||
except OSError:
|
except OSError:
|
||||||
@ -358,7 +457,7 @@ def _stat_and_start_copy(path, addr, copying_state, copied_state):
|
|||||||
log("Copying {} to flash", path)
|
log("Copying {} to flash", path)
|
||||||
# Mark that the flash write has started. Any failure should result in an overall failure.
|
# Mark that the flash write has started. Any failure should result in an overall failure.
|
||||||
_write_state(copying_state) # Either _STATE_COPYING_FUS or _STATE_COPYING_WS
|
_write_state(copying_state) # Either _STATE_COPYING_FUS or _STATE_COPYING_WS
|
||||||
_copy_file_to_flash(path, addr)
|
_copy_file_to_flash(path)
|
||||||
log("Copying complete")
|
log("Copying complete")
|
||||||
# The entire write has completed successfully, start the install.
|
# The entire write has completed successfully, start the install.
|
||||||
_write_state(copied_state) # Either _STATE_COPIED_FUS or _STATE_COPIED_WS
|
_write_state(copied_state) # Either _STATE_COPIED_FUS or _STATE_COPIED_WS
|
||||||
@ -423,22 +522,20 @@ def resume():
|
|||||||
log("FUS version {}", fus_version)
|
log("FUS version {}", fus_version)
|
||||||
if fus_version < _FUS_VERSION_102:
|
if fus_version < _FUS_VERSION_102:
|
||||||
log("Factory FUS detected")
|
log("Factory FUS detected")
|
||||||
if _stat_and_start_copy(
|
if _stat_and_start_copy(_PATH_FUS_102, _STATE_COPYING_FUS, _STATE_COPIED_FUS):
|
||||||
_PATH_FUS_102, _ADDR_FUS, _STATE_COPYING_FUS, _STATE_COPIED_FUS
|
|
||||||
):
|
|
||||||
continue
|
continue
|
||||||
elif fus_version >= _FUS_VERSION_102 and fus_version < _FUS_VERSION_110:
|
elif fus_version >= _FUS_VERSION_102 and fus_version < _FUS_VERSION_110:
|
||||||
log("FUS 1.0.2 detected")
|
log("FUS 1.0.2 detected")
|
||||||
if _stat_and_start_copy(
|
if _stat_and_start_copy(_PATH_FUS_110, _STATE_COPYING_FUS, _STATE_COPIED_FUS):
|
||||||
_PATH_FUS_110, _ADDR_FUS, _STATE_COPYING_FUS, _STATE_COPIED_FUS
|
|
||||||
):
|
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
log("FUS is up-to-date")
|
log("FUS is up-to-date")
|
||||||
|
|
||||||
if fus_version >= _FUS_VERSION_110:
|
if fus_version >= _FUS_VERSION_110:
|
||||||
if _stat_and_start_copy(
|
if _stat_and_start_copy(
|
||||||
_PATH_WS_BLE_HCI, _ADDR_WS_BLE_HCI, _STATE_COPYING_WS, _STATE_COPIED_WS
|
_PATH_WS_BLE_HCI,
|
||||||
|
_STATE_COPYING_WS,
|
||||||
|
_STATE_COPIED_WS,
|
||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
@ -465,7 +562,7 @@ def resume():
|
|||||||
if fus_is_idle():
|
if fus_is_idle():
|
||||||
log("FUS copy complete, installing")
|
log("FUS copy complete, installing")
|
||||||
_write_state(_STATE_INSTALLING_FUS)
|
_write_state(_STATE_INSTALLING_FUS)
|
||||||
_fus_run_fwupgrade(_ADDR_FUS)
|
_fus_run_fwupgrade()
|
||||||
else:
|
else:
|
||||||
log("FUS copy bad state")
|
log("FUS copy bad state")
|
||||||
_write_failure_state(REASON_FLASH_FUS_BAD_STATE)
|
_write_failure_state(REASON_FLASH_FUS_BAD_STATE)
|
||||||
@ -519,7 +616,7 @@ def resume():
|
|||||||
if fus_is_idle():
|
if fus_is_idle():
|
||||||
log("WS copy complete, installing")
|
log("WS copy complete, installing")
|
||||||
_write_state(_STATE_INSTALLING_WS)
|
_write_state(_STATE_INSTALLING_WS)
|
||||||
_fus_run_fwupgrade(_ADDR_WS_BLE_HCI)
|
_fus_run_fwupgrade()
|
||||||
else:
|
else:
|
||||||
log("WS copy bad state")
|
log("WS copy bad state")
|
||||||
_write_failure_state(REASON_FLASH_WS_BAD_STATE)
|
_write_failure_state(REASON_FLASH_WS_BAD_STATE)
|
||||||
@ -556,9 +653,59 @@ def resume():
|
|||||||
_write_failure_state(REASON_WS_VENDOR + result)
|
_write_failure_state(REASON_WS_VENDOR + result)
|
||||||
|
|
||||||
|
|
||||||
|
def install_boot():
|
||||||
|
boot_py = "/flash/boot.py"
|
||||||
|
header = ""
|
||||||
|
mode = "w"
|
||||||
|
try:
|
||||||
|
with open(boot_py, "r") as boot:
|
||||||
|
header = "\n"
|
||||||
|
mode = "a"
|
||||||
|
for line in boot:
|
||||||
|
if "rfcore_firmware.resume()" in line:
|
||||||
|
print("Already installed.")
|
||||||
|
return
|
||||||
|
|
||||||
|
print("boot.py exists, adding upgrade handler.")
|
||||||
|
except OSError:
|
||||||
|
print("boot.py doesn't exists, adding with upgrade handler.")
|
||||||
|
|
||||||
|
with open(boot_py, mode) as boot:
|
||||||
|
boot.write(header)
|
||||||
|
boot.write("# Handle rfcore updates.\n")
|
||||||
|
boot.write("import rfcore_firmware\n")
|
||||||
|
boot.write("rfcore_firmware.resume()\n")
|
||||||
|
|
||||||
|
|
||||||
# Start a firmware update.
|
# Start a firmware update.
|
||||||
# This will immediately trigger a reset and start the update process on boot.
|
# This will immediately trigger a reset and start the update process on boot.
|
||||||
def check_for_updates():
|
def check_for_updates(force=False):
|
||||||
log("Starting firmware update")
|
(
|
||||||
_write_state(_STATE_WAITING_FOR_FUS)
|
src_filename,
|
||||||
machine.reset()
|
addr_1m,
|
||||||
|
addr_640k,
|
||||||
|
addr_512k,
|
||||||
|
addr_256k,
|
||||||
|
vers_fus,
|
||||||
|
) = check_file_details(_PATH_FUS_110)
|
||||||
|
(
|
||||||
|
src_filename,
|
||||||
|
addr_1m,
|
||||||
|
addr_640k,
|
||||||
|
addr_512k,
|
||||||
|
addr_256k,
|
||||||
|
vers_ws,
|
||||||
|
) = check_file_details(_PATH_WS_BLE_HCI)
|
||||||
|
current_version_fus = stm.rfcore_fw_version(_FW_VERSION_FUS)
|
||||||
|
fus_uptodate = current_version_fus[0:3] == vers_fus
|
||||||
|
|
||||||
|
current_version_ws = stm.rfcore_fw_version(_FW_VERSION_WS)
|
||||||
|
ws_uptodate = current_version_ws[0:3] == vers_ws
|
||||||
|
if fus_uptodate and ws_uptodate and not force:
|
||||||
|
log(f"Already up to date: fus: {current_version_fus}, ws: {current_version_ws}")
|
||||||
|
else:
|
||||||
|
log(f"Starting firmware update")
|
||||||
|
log(f" - fus: {current_version_fus} -> {vers_fus}")
|
||||||
|
log(f" - ws: {current_version_ws} -> {vers_ws}")
|
||||||
|
_write_state(_STATE_WAITING_FOR_FUS)
|
||||||
|
machine.reset()
|
||||||
|
@ -30,28 +30,70 @@
|
|||||||
# rfcore_firmware.py as well as instructions on how to use.
|
# rfcore_firmware.py as well as instructions on how to use.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import struct
|
import struct
|
||||||
import sys
|
import sys
|
||||||
|
from binascii import crc32
|
||||||
|
from rfcore_firmware import validate_crc, _OBFUSCATION_KEY
|
||||||
|
|
||||||
# Must match rfcore_firmware.py.
|
|
||||||
_OBFUSCATION_KEY = 0x0573B55AA
|
|
||||||
|
|
||||||
_FIRMWARE_FILES = {
|
_FIRMWARE_FILES = {
|
||||||
"stm32wb5x_FUS_fw_1_0_2.bin": "fus_102.bin",
|
"stm32wb5x_FUS_fw_1_0_2.bin": "fus_102.bin",
|
||||||
"stm32wb5x_FUS_fw.bin": "fus_110.bin",
|
"stm32wb5x_FUS_fw.bin": "fus_110.bin",
|
||||||
"stm32wb5x_BLE_HCILayer_fw.bin": "ws_ble_hci.bin",
|
"stm32wb5x_BLE_HCILayer_fw.bin": "ws_ble_hci.bin",
|
||||||
}
|
}
|
||||||
|
_RELEASE_NOTES = "Release_Notes.html"
|
||||||
|
|
||||||
|
|
||||||
|
def get_details(release_notes, filename):
|
||||||
|
if not release_notes:
|
||||||
|
return None
|
||||||
|
file_details = re.findall(
|
||||||
|
rb"%s,(((0x[\d\S]+?),)+[vV][\d\.]+)[<,]" % filename.encode(),
|
||||||
|
release_notes,
|
||||||
|
flags=re.DOTALL,
|
||||||
|
)
|
||||||
|
# The release note has all past version details also, but current is at top
|
||||||
|
latest_details = file_details[0][0].split(b",")
|
||||||
|
addr_1m, addr_640k, addr_512k, addr_256k, version = latest_details
|
||||||
|
addr_1m = int(addr_1m, 0)
|
||||||
|
addr_640k = int(addr_640k, 0)
|
||||||
|
addr_512k = int(addr_512k, 0)
|
||||||
|
addr_256k = int(addr_256k, 0)
|
||||||
|
version = [int(v) for v in version.lower().lstrip(b"v").split(b".")]
|
||||||
|
return addr_1m, addr_640k, addr_512k, addr_256k, version
|
||||||
|
|
||||||
|
|
||||||
def main(src_path, dest_path):
|
def main(src_path, dest_path):
|
||||||
for src_file, dest_file in _FIRMWARE_FILES.items():
|
|
||||||
src_file = os.path.join(src_path, src_file)
|
# Load the release note to parse for important details
|
||||||
|
with open(os.path.join(src_path, _RELEASE_NOTES), "rb") as f:
|
||||||
|
release_notes = f.read()
|
||||||
|
# remove some formatting
|
||||||
|
release_notes = re.sub(rb"</?strong>", b"", release_notes)
|
||||||
|
# flatten tables
|
||||||
|
release_notes = re.sub(
|
||||||
|
rb"</t[dh]>\W*\n*\W*",
|
||||||
|
b",",
|
||||||
|
release_notes.replace(b"<td>", b"").replace(b"<th>", b""),
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
b"Wireless Coprocessor Binary,STM32WB5xxG(1M),STM32WB5xxY(640k),STM32WB5xxE(512K),STM32WB5xxC(256K),Version,"
|
||||||
|
not in release_notes
|
||||||
|
):
|
||||||
|
raise SystemExit(
|
||||||
|
"Cannot determine binary load address, please confirm Coprocessor folder / Release Notes format."
|
||||||
|
)
|
||||||
|
|
||||||
|
for src_filename, dest_file in _FIRMWARE_FILES.items():
|
||||||
|
src_file = os.path.join(src_path, src_filename)
|
||||||
dest_file = os.path.join(dest_path, dest_file)
|
dest_file = os.path.join(dest_path, dest_file)
|
||||||
if not os.path.exists(src_file):
|
if not os.path.exists(src_file):
|
||||||
print("Unable to find: {}".format(src_file))
|
print("Unable to find: {}".format(src_file))
|
||||||
continue
|
continue
|
||||||
sz = 0
|
sz = 0
|
||||||
with open(src_file, "rb") as src:
|
with open(src_file, "rb") as src:
|
||||||
|
crc = 0
|
||||||
with open(dest_file, "wb") as dest:
|
with open(dest_file, "wb") as dest:
|
||||||
while True:
|
while True:
|
||||||
b = src.read(4)
|
b = src.read(4)
|
||||||
@ -59,9 +101,71 @@ def main(src_path, dest_path):
|
|||||||
break
|
break
|
||||||
(v,) = struct.unpack("<I", b)
|
(v,) = struct.unpack("<I", b)
|
||||||
v ^= _OBFUSCATION_KEY
|
v ^= _OBFUSCATION_KEY
|
||||||
dest.write(struct.pack("<I", v))
|
vs = struct.pack("<I", v)
|
||||||
|
dest.write(vs)
|
||||||
|
crc = crc32(vs, crc)
|
||||||
sz += 4
|
sz += 4
|
||||||
print("Written {} ({} bytes)".format(dest_file, sz))
|
|
||||||
|
addr_1m, addr_640k, addr_512k, addr_256k, version = get_details(
|
||||||
|
release_notes, src_filename
|
||||||
|
)
|
||||||
|
|
||||||
|
footer = struct.pack(
|
||||||
|
"<37sIIIIbbbI",
|
||||||
|
src_filename.encode(),
|
||||||
|
addr_1m,
|
||||||
|
addr_640k,
|
||||||
|
addr_512k,
|
||||||
|
addr_256k,
|
||||||
|
*version,
|
||||||
|
_OBFUSCATION_KEY,
|
||||||
|
)
|
||||||
|
assert len(footer) == 60
|
||||||
|
dest.write(footer)
|
||||||
|
crc = crc32(footer, crc)
|
||||||
|
crc = 0xFFFFFFFF & -crc - 1
|
||||||
|
dest.write(struct.pack("<I", crc))
|
||||||
|
sz += 64
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"Written {src_filename} v{version[0]}.{version[1]}.{version[2]} to {dest_file} ({sz} bytes)"
|
||||||
|
)
|
||||||
|
check_file_details(dest_file)
|
||||||
|
|
||||||
|
|
||||||
|
def check_file_details(filename):
|
||||||
|
"""Should match copy of function in rfcore_firmware.py to confirm operation"""
|
||||||
|
with open(filename, "rb") as f:
|
||||||
|
if not validate_crc(f):
|
||||||
|
raise ValueError("file validation failed: incorrect crc")
|
||||||
|
|
||||||
|
f.seek(-64, 2)
|
||||||
|
footer = f.read()
|
||||||
|
assert len(footer) == 64
|
||||||
|
details = struct.unpack("<37sIIIIbbbII", footer)
|
||||||
|
(
|
||||||
|
src_filename,
|
||||||
|
addr_1m,
|
||||||
|
addr_640k,
|
||||||
|
addr_512k,
|
||||||
|
addr_256k,
|
||||||
|
vers_major,
|
||||||
|
vers_minor,
|
||||||
|
vers_patch,
|
||||||
|
KEY,
|
||||||
|
crc,
|
||||||
|
) = details
|
||||||
|
if KEY != _OBFUSCATION_KEY:
|
||||||
|
raise ValueError("file validation failed: incorrect key")
|
||||||
|
|
||||||
|
return (
|
||||||
|
src_filename,
|
||||||
|
addr_1m,
|
||||||
|
addr_640k,
|
||||||
|
addr_512k,
|
||||||
|
addr_256k,
|
||||||
|
(vers_major, vers_minor, vers_patch),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
Loading…
Reference in New Issue
Block a user