[FL-2269] Core2 OTA (#1144)

* C2OTA: wip
* Update Cube to 1.13.3
* Fixed prio
* Functional Core2 updater
* Removed hardware CRC usage; code cleanup & linter fixes
* Moved hardcoded stack params to copro.mk
* Fixing CI bundling of core2 fw
* Removed last traces of hardcoded radio stack
* OB processing draft
* Python scripts cleanup
* Support for comments in ob data
* Sacrificed SD card icon in favor of faster update. Waiting for Storage fix
* Additional handling for OB mismatched values
* Description for new furi_hal apis; spelling fixes
* Rework of OB write, WIP
* Properly restarting OB verification loop
* Split update_task_workers.c
* Checking OBs after enabling post-update mode
* Moved OB verification before flashing
* Removed ob.data for custom stacks
* Fixed progress calculation for OB
* Removed unnecessary OB mask cast

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
hedger
2022-04-27 18:53:48 +03:00
committed by GitHub
parent 81aeda86db
commit 7ce305fca3
41 changed files with 1622 additions and 295 deletions

View File

@@ -50,6 +50,26 @@ class Main(App):
self.parser_copro.add_argument("cube_dir", help="Path to Cube folder")
self.parser_copro.add_argument("output_dir", help="Path to output folder")
self.parser_copro.add_argument("mcu", help="MCU series as in copro folder")
self.parser_copro.add_argument(
"--cube_ver", dest="cube_ver", help="Cube version", required=True
)
self.parser_copro.add_argument(
"--stack_type", dest="stack_type", help="Stack type", required=True
)
self.parser_copro.add_argument(
"--stack_file",
dest="stack_file",
help="Stack file name in copro folder",
required=True,
)
self.parser_copro.add_argument(
"--stack_addr",
dest="stack_addr",
help="Stack flash address, as per release_notes",
type=lambda x: int(x, 16),
default=0,
required=False,
)
self.parser_copro.set_defaults(func=self.copro)
self.parser_dolphin = self.subparsers.add_parser(
@@ -203,13 +223,15 @@ class Main(App):
manifest_file = os.path.join(directory_path, "Manifest")
old_manifest = Manifest()
if os.path.exists(manifest_file):
self.logger.info("old manifest is present, loading for compare")
self.logger.info("Manifest is present, loading to compare")
old_manifest.load(manifest_file)
self.logger.info(f'Creating new Manifest for directory "{directory_path}"')
self.logger.info(
f'Creating temporary Manifest for directory "{directory_path}"'
)
new_manifest = Manifest()
new_manifest.create(directory_path)
self.logger.info(f"Comparing new manifest with old")
self.logger.info(f"Comparing new manifest with existing")
only_in_old, changed, only_in_new = Manifest.compare(old_manifest, new_manifest)
for record in only_in_old:
self.logger.info(f"Only in old: {record}")
@@ -233,9 +255,14 @@ class Main(App):
self.logger.info(f"Bundling coprocessor binaries")
copro = Copro(self.args.mcu)
self.logger.info(f"Loading CUBE info")
copro.loadCubeInfo(self.args.cube_dir)
copro.loadCubeInfo(self.args.cube_dir, self.args.cube_ver)
self.logger.info(f"Bundling")
copro.bundle(self.args.output_dir)
copro.bundle(
self.args.output_dir,
self.args.stack_file,
self.args.stack_type,
self.args.stack_addr,
)
self.logger.info(f"Complete")
return 0

View File

@@ -91,6 +91,7 @@ class Main(App):
self.args.resources,
)
)
bundle_args.extend(self.other_args)
self.logger.info(
f"Use this directory to self-update your Flipper:\n\t{bundle_dir}"
)

View File

@@ -7,6 +7,7 @@ import os
from flipper.app import App
from flipper.cube import CubeProgrammer
from flipper.assets.coprobin import CoproBinary
STATEMENT = "AGREE_TO_LOSE_FLIPPER_FEATURES_THAT_USE_CRYPTO_ENCLAVE"
@@ -68,10 +69,15 @@ class Main(App):
)
self._addArgsSWD(self.parser_core2radio)
self.parser_core2radio.add_argument(
"radio_address", type=str, help="Radio Stack Binary Address"
"radio", type=str, help="Radio Stack Binary"
)
self.parser_core2radio.add_argument(
"radio", type=str, help="Radio Stack Binary"
"--addr",
dest="radio_address",
help="Radio Stack Binary Address, as per release_notes",
type=lambda x: int(x, 16),
default=0,
required=False,
)
self.parser_core2radio.set_defaults(func=self.core2radio)
@@ -144,14 +150,27 @@ class Main(App):
return 0
def core2radio(self):
if int(self.args.radio_address, 16) > 0x080E0000:
stack_info = CoproBinary(self.args.radio)
if not stack_info.is_stack():
self.logger.error("Not a Radio Stack")
return 1
self.logger.info(f"Will flash {stack_info.img_sig.get_version()}")
radio_address = self.args.radio_address
if not radio_address:
radio_address = stack_info.get_flash_load_addr()
self.logger.warning(
f"Radio address not provided, guessed as 0x{radio_address:X}"
)
if radio_address > 0x080E0000:
self.logger.error(f"I KNOW WHAT YOU DID LAST SUMMER")
return 1
cp = CubeProgrammer(self._getCubeParams())
self.logger.info(f"Removing Current Radio Stack")
cp.deleteCore2RadioStack()
self.logger.info(f"Flashing Radio Stack")
cp.flashCore2(self.args.radio_address, self.args.radio)
cp.flashCore2(radio_address, self.args.radio)
self.logger.info(f"Complete")
return 0

View File

@@ -16,7 +16,7 @@ class App:
self.init()
def __call__(self, args=None):
self.args, _ = self.parser.parse_known_args(args=args)
self.args, self.other_args = self.parser.parse_known_args(args=args)
# configure log output
self.log_level = logging.DEBUG if self.args.debug else logging.INFO
self.logger.setLevel(self.log_level)

View File

@@ -2,9 +2,12 @@ import logging
import datetime
import shutil
import json
from os.path import basename
import xml.etree.ElementTree as ET
from flipper.utils import *
from flipper.assets.coprobin import CoproBinary, get_stack_type
CUBE_COPRO_PATH = "Projects/STM32WB_Copro_Wireless_Binaries"
@@ -13,14 +16,7 @@ MANIFEST_TEMPLATE = {
"copro": {
"fus": {"version": {"major": 1, "minor": 2, "sub": 0}, "files": []},
"radio": {
"version": {
"type": 3,
"major": 1,
"minor": 13,
"sub": 0,
"branch": 0,
"release": 5,
},
"version": {},
"files": [],
},
},
@@ -35,7 +31,7 @@ class Copro:
self.mcu_copro = None
self.logger = logging.getLogger(self.__class__.__name__)
def loadCubeInfo(self, cube_dir):
def loadCubeInfo(self, cube_dir, cube_version):
if not os.path.isdir(cube_dir):
raise Exception(f'"{cube_dir}" doesn\'t exists')
self.cube_dir = cube_dir
@@ -51,8 +47,8 @@ class Copro:
if not cube_version or not cube_version.startswith("FW.WB"):
raise Exception(f"Incorrect Cube package or version info")
cube_version = cube_version.replace("FW.WB.", "", 1)
if cube_version != "1.13.1":
raise Exception(f"Unknonwn cube version")
if cube_version != cube_version:
raise Exception(f"Unsupported cube version")
self.version = cube_version
def addFile(self, array, filename, **kwargs):
@@ -63,14 +59,32 @@ class Copro:
{"name": filename, "sha256": file_sha256(destination_file), **kwargs}
)
def bundle(self, output_dir):
def bundle(self, output_dir, stack_file_name, stack_type, stack_addr=None):
if not os.path.isdir(output_dir):
raise Exception(f'"{output_dir}" doesn\'t exists')
self.output_dir = output_dir
stack_file = os.path.join(self.mcu_copro, stack_file_name)
manifest_file = os.path.join(self.output_dir, "Manifest.json")
# Form Manifest
manifest = dict(MANIFEST_TEMPLATE)
manifest["manifest"]["timestamp"] = timestamp()
copro_bin = CoproBinary(stack_file)
self.logger.info(f"Bundling {copro_bin.img_sig.get_version()}")
stack_type_code = get_stack_type(stack_type)
manifest["copro"]["radio"]["version"].update(
{
"type": stack_type_code,
"major": copro_bin.img_sig.version_major,
"minor": copro_bin.img_sig.version_minor,
"sub": copro_bin.img_sig.version_sub,
"branch": copro_bin.img_sig.version_branch,
"release": copro_bin.img_sig.version_build,
}
)
if not stack_addr:
stack_addr = copro_bin.get_flash_load_addr()
self.logger.info(f"Using guessed flash address 0x{stack_addr:x}")
# Old FUS Update
self.addFile(
manifest["copro"]["fus"]["files"],
@@ -88,8 +102,8 @@ class Copro:
# BLE Full Stack
self.addFile(
manifest["copro"]["radio"]["files"],
"stm32wb5x_BLE_Stack_light_fw.bin",
address="0x080D7000",
stack_file_name,
address=f"0x{stack_addr:X}",
)
# Save manifest to
json.dump(manifest, open(manifest_file, "w"))

View File

@@ -0,0 +1,187 @@
import struct
import math
import os, os.path
import sys
# From STM32CubeWB\Middlewares\ST\STM32_WPAN\interface\patterns\ble_thread\shci\shci.h
__STACK_TYPE_CODES = {
"BLE_FULL": 0x01,
"BLE_HCI": 0x02,
"BLE_LIGHT": 0x03,
"BLE_BEACON": 0x04,
"BLE_BASIC": 0x05,
"BLE_FULL_EXT_ADV": 0x06,
"BLE_HCI_EXT_ADV": 0x07,
"THREAD_FTD": 0x10,
"THREAD_MTD": 0x11,
"ZIGBEE_FFD": 0x30,
"ZIGBEE_RFD": 0x31,
"MAC": 0x40,
"BLE_THREAD_FTD_STATIC": 0x50,
"BLE_THREAD_FTD_DYAMIC": 0x51,
"802154_LLD_TESTS": 0x60,
"802154_PHY_VALID": 0x61,
"BLE_PHY_VALID": 0x62,
"BLE_LLD_TESTS": 0x63,
"BLE_RLV": 0x64,
"802154_RLV": 0x65,
"BLE_ZIGBEE_FFD_STATIC": 0x70,
"BLE_ZIGBEE_RFD_STATIC": 0x71,
"BLE_ZIGBEE_FFD_DYNAMIC": 0x78,
"BLE_ZIGBEE_RFD_DYNAMIC": 0x79,
"RLV": 0x80,
"BLE_MAC_STATIC": 0x90,
}
class CoproException(ValueError):
pass
# Formats based on AN5185
class CoproFooterBase:
SIG_BIN_SIZE = 5 * 4
_SIG_BIN_COMMON_SIZE = 2 * 4
def get_version(self):
return f"Version {self.version_major}.{self.version_minor}.{self.version_sub}, branch {self.version_branch}, build {self.version_build} (magic {self.magic:X})"
def get_details(self):
raise CoproException("Not implemented")
def __init__(self, raw: bytes):
if len(raw) != self.SIG_BIN_SIZE:
raise CoproException("Invalid footer size")
sig_common_part = raw[-self._SIG_BIN_COMMON_SIZE :]
parts = struct.unpack("BBBBI", sig_common_part)
self.version_major = parts[3]
self.version_minor = parts[2]
self.version_sub = parts[1]
# AN5185 mismatch: swapping byte halves
self.version_build = parts[0] & 0x0F
self.version_branch = (parts[0] & 0xF0) >> 4
self.magic = parts[4]
class CoproFusFooter(CoproFooterBase):
FUS_MAGIC_IMG_STACK = 0x23372991
FUS_MAGIC_IMG_FUS = 0x32279221
FUS_MAGIC_IMG_OTHER = 0x42769811
FUS_BASE = 0x80F4000
FLASH_PAGE_SIZE = 4 * 1024
def __init__(self, raw: bytes):
super().__init__(raw)
if self.magic not in (
self.FUS_MAGIC_IMG_OTHER,
self.FUS_MAGIC_IMG_FUS,
self.FUS_MAGIC_IMG_STACK,
):
raise CoproException(f"Invalid FUS img magic {self.magic:x}")
own_data = raw[: -self._SIG_BIN_COMMON_SIZE]
parts = struct.unpack("IIBBBB", own_data)
self.info1 = parts[0]
self.info2 = parts[1]
self.sram2b_1ks = parts[5]
self.sram2a_1ks = parts[4]
self.flash_4ks = parts[2]
def get_details(self):
return f"SRAM2b={self.sram2b_1ks}k SRAM2a={self.sram2a_1ks}k flash={self.flash_4ks}p"
def is_stack(self):
return self.magic == self.FUS_MAGIC_IMG_STACK
def get_flash_pages(self, fullsize):
return math.ceil(fullsize / self.FLASH_PAGE_SIZE)
def get_flash_base(self, fullsize):
if not self.is_stack():
raise CoproException("Not a stack image")
return self.FUS_BASE - self.get_flash_pages(fullsize) * self.FLASH_PAGE_SIZE
class CoproSigFooter(CoproFooterBase):
SIG_MAGIC_ST = 0xD3A12C5E
SIG_MAGIC_CUSTOMER = 0xE2B51D4A
def __init__(self, raw: bytes):
super().__init__(raw)
if self.magic not in (self.SIG_MAGIC_ST, self.SIG_MAGIC_CUSTOMER):
raise CoproException(f"Invalid FUS img magic {self.magic:x}")
own_data = raw[: -self._SIG_BIN_COMMON_SIZE]
parts = struct.unpack("IIBBH", own_data)
self.reserved_1 = parts[0]
self.reserved_2 = parts[1]
self.size = parts[2]
self.source = parts[3]
self.reserved_34 = parts[4]
def get_details(self):
return f"Signature Src {self.source:x} size {self.size:x}"
class CoproBinary:
def __init__(self, binary_path):
self.binary_path = binary_path
self.img_sig_footer = None
self.img_sig = None
self.binary_size = -1
self._load()
def _load(self):
with open(self.binary_path, "rb") as fin:
whole_file = fin.read()
self.binary_size = len(whole_file)
img_sig_footer_bin = whole_file[-CoproFooterBase.SIG_BIN_SIZE :]
self.img_sig_footer = CoproSigFooter(img_sig_footer_bin)
img_sig_size = self.img_sig_footer.size + CoproSigFooter.SIG_BIN_SIZE
img_sig_bin = whole_file[
-(img_sig_size + CoproFusFooter.SIG_BIN_SIZE) : -img_sig_size
]
self.img_sig = CoproFusFooter(img_sig_bin)
def is_valid(self):
return self.img_sig_footer is not None and self.img_sig is not None
def is_stack(self):
return self.img_sig and self.img_sig.is_stack()
def get_flash_load_addr(self):
if not self.is_stack():
raise CoproException("Not a stack image")
return self.img_sig.get_flash_base(self.binary_size)
def get_stack_type(typestr: str):
stack_code = __STACK_TYPE_CODES.get(typestr.upper(), None)
if stack_code is None:
raise CoproException(f"Unknown stack type {typestr}. See shci.h")
return stack_code
def _load_bin(binary_path: str):
print(binary_path)
copro_bin = CoproBinary(binary_path)
print(copro_bin.img_sig.get_version())
if copro_bin.img_sig.is_stack():
print(f"\t>> FLASH AT {copro_bin.get_flash_load_addr():X}\n")
def main():
coprodir = (
sys.argv[1]
if len(sys.argv) > 1
else "../../../lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x"
)
for fn in os.listdir(coprodir):
if not fn.endswith(".bin"):
continue
_load_bin(os.path.join(coprodir, fn))
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,208 @@
#!/usr/bin/env python3
import logging
import struct
from enum import Enum
from dataclasses import dataclass
from typing import Tuple
from array import array
class OBException(ValueError):
pass
@dataclass
class OBParams:
word_idx: int
bits: Tuple[int, int]
name: str
_OBS_descr = (
OBParams(0, (0, 8), "RDP"),
OBParams(0, (8, 9), "ESE"),
OBParams(0, (9, 12), "BOR_LEV"),
OBParams(0, (12, 13), "nRST_STOP"),
OBParams(0, (13, 14), "nRST_STDBY"),
OBParams(0, (14, 15), "nRSTSHDW"),
OBParams(0, (15, 16), "UNUSED1"),
OBParams(0, (16, 17), "IWDGSW"),
OBParams(0, (17, 18), "IWDGSTOP"),
OBParams(0, (18, 19), "IWGDSTDBY"), # ST's typo: IWDGSTDBY
OBParams(0, (18, 19), "IWDGSTDBY"), # ST's typo: IWDGSTDBY
OBParams(0, (19, 20), "WWDGSW"),
OBParams(0, (20, 23), "UNUSED2"),
OBParams(0, (23, 24), "nBOOT1"),
OBParams(0, (24, 25), "SRAM2PE"),
OBParams(0, (25, 26), "SRAM2RST"),
OBParams(0, (26, 27), "nSWBOOT0"),
OBParams(0, (27, 28), "nBOOT0"),
OBParams(0, (28, 29), "UNUSED3"),
OBParams(0, (29, 32), "AGC_TRIM"),
OBParams(1, (0, 9), "PCROP1A_STRT"),
OBParams(1, (9, 32), "UNUSED"),
OBParams(2, (0, 9), "PCROP1A_END"),
OBParams(2, (9, 31), "UNUSED"),
OBParams(2, (31, 32), "PCROP_RDP"),
OBParams(3, (0, 8), "WRP1A_STRT"),
OBParams(3, (8, 16), "UNUSED1"),
OBParams(3, (16, 24), "WRP1A_END"),
OBParams(3, (24, 32), "UNUSED2"),
OBParams(4, (0, 8), "WRP1B_STRT"),
OBParams(4, (8, 16), "UNUSED1"),
OBParams(4, (16, 24), "WRP1B_END"),
OBParams(4, (24, 32), "UNUSED2"),
OBParams(5, (0, 9), "PCROP1B_STRT"),
OBParams(5, (9, 32), "UNUSED"),
OBParams(6, (0, 9), "PCROP1B_END"),
OBParams(6, (9, 32), "UNUSED"),
OBParams(13, (0, 14), "IPCCDBA"),
OBParams(13, (14, 32), "UNUSED"),
OBParams(14, (0, 8), "SFSA"),
OBParams(14, (8, 9), "FSD"),
OBParams(14, (9, 12), "UNUSED1"),
OBParams(14, (12, 13), "DDS"),
OBParams(14, (13, 32), "UNUSED2"),
OBParams(15, (0, 18), "SBRV"),
OBParams(15, (18, 23), "SBRSA"),
OBParams(15, (23, 24), "BRSD"),
OBParams(15, (24, 25), "UNUSED1"),
OBParams(15, (25, 30), "SNBRSA"),
OBParams(15, (30, 31), "NBRSD"),
OBParams(15, (31, 32), "C2OPT"),
)
_OBS = dict((param.name, param) for param in _OBS_descr)
@dataclass
class EncodedOBValue:
value: int
mask: int
params: OBParams
class OptionByte:
class OBMode(Enum):
IGNORE = 0
READ = 1
READ_WRITE = 2
@classmethod
def from_str(cls, value):
if value == "r":
return cls.READ
elif value == "rw":
return cls.READ_WRITE
else:
raise OBException(f"Unknown OB check mode '{value}'")
def __init__(self, obstr):
parts = obstr.split(":")
if len(parts) != 3:
raise OBException(f"Invalid OB value definition {obstr}")
self.name = parts[0]
self.value = int(parts[1], 16)
self.mode = OptionByte.OBMode.from_str(parts[2].strip())
self.descr = _OBS.get(self.name, None)
if self.descr is None:
raise OBException(f"Missing OB descriptor for {self.name}")
def encode(self):
startbit, endbit = self.descr.bits
value_mask = 2 ** (endbit - startbit) - 1
value_corrected = self.value & value_mask
value_shifted = value_corrected << startbit
value_mask_shifted = value_mask << startbit
return EncodedOBValue(value_shifted, value_mask_shifted, self)
def __repr__(self):
return f"<OB {self.name}, 0x{self.value:x}, {self.mode} at 0x{id(self):X}>"
@dataclass
class ObReferenceValues:
reference: bytes
compare_mask: bytes
write_mask: bytes
class ObReferenceValuesGenerator:
def __init__(self):
self.compare_mask = array("I", [0] * 16)
self.write_mask = array("I", [0] * 16)
self.ref_values = array("I", [0] * 16)
def __repr__(self):
return (
f"<OBRefs REFS=[{' '.join(hex(v) for v in self.ref_values)}] "
f"CMPMASK=[{' '.join(hex(v) for v in self.compare_mask)}] "
f"WRMASK=[{' '.join(hex(v) for v in self.write_mask)}] "
)
def export_values(self):
export_cmpmask = array("I")
for value in self.compare_mask:
export_cmpmask.append(value)
export_cmpmask.append(value)
export_wrmask = array("I")
for value in self.write_mask:
export_wrmask.append(value)
export_wrmask.append(value)
export_refvals = array("I")
for cmpmask, refval in zip(self.compare_mask, self.ref_values):
export_refvals.append(refval)
export_refvals.append((refval ^ 0xFFFFFFFF) & cmpmask)
return export_refvals, export_cmpmask, export_wrmask
def export(self):
return ObReferenceValues(*map(lambda a: a.tobytes(), self.export_values()))
def apply(self, ob):
ob_params = ob.descr
encoded_ob = ob.encode()
self.compare_mask[ob_params.word_idx] |= encoded_ob.mask
self.ref_values[ob_params.word_idx] |= encoded_ob.value
if ob.mode == OptionByte.OBMode.READ_WRITE:
self.write_mask[ob_params.word_idx] |= encoded_ob.mask
class OptionBytesData:
def __init__(self, obfname):
self.obs = list()
with open(obfname, "rt") as obfin:
self.obs = list(
OptionByte(line) for line in obfin if not line.startswith("#")
)
def gen_values(self):
obref = ObReferenceValuesGenerator()
converted_refs = list(obref.apply(ob) for ob in self.obs)
return obref
def main():
with open("../../../../logs/obs.bin", "rb") as obsbin:
ob_sample = obsbin.read(128)
ob_sample_arr = array("I", ob_sample)
print(ob_sample_arr)
obd = OptionBytesData("../../ob.data")
print(obd.obs)
# print(obd.gen_values().export())
ref, mask, wrmask = obd.gen_values().export_values()
for idx in range(len(ob_sample_arr)):
real_masked = ob_sample_arr[idx] & mask[idx]
print(
f"#{idx}: ref {ref[idx]:08x} real {real_masked:08x} ({ob_sample_arr[idx]:08x} & {mask[idx]:08x}) match {ref[idx]==real_masked}"
)
# print(ob_sample)
if __name__ == "__main__":
main()

View File

@@ -2,11 +2,14 @@
from flipper.app import App
from flipper.utils.fff import FlipperFormatFile
from flipper.assets.coprobin import CoproBinary, get_stack_type
from flipper.assets.obdata import OptionBytesData
from os.path import basename, join, exists
import os
import shutil
import zlib
import tarfile
import math
class Main(App):
@@ -28,19 +31,28 @@ class Main(App):
self.parser_generate.add_argument("-d", dest="directory", required=True)
self.parser_generate.add_argument("-v", dest="version", required=True)
self.parser_generate.add_argument("-t", dest="target", required=True)
self.parser_generate.add_argument("--dfu", dest="dfu", required=False)
self.parser_generate.add_argument(
"--dfu", dest="dfu", default="", required=False
)
self.parser_generate.add_argument("-r", dest="resources", required=False)
self.parser_generate.add_argument("--stage", dest="stage", required=True)
self.parser_generate.add_argument(
"--radio", dest="radiobin", default="", required=False
)
self.parser_generate.add_argument(
"--radioaddr", dest="radioaddr", required=False
"--radioaddr",
dest="radioaddr",
type=lambda x: int(x, 16),
default=0,
required=False,
)
self.parser_generate.add_argument(
"--radiover", dest="radioversion", required=False
"--radiotype", dest="radiotype", required=False
)
self.parser_generate.add_argument("--obdata", dest="obdata", required=False)
self.parser_generate.set_defaults(func=self.generate)
def generate(self):
@@ -49,11 +61,27 @@ class Main(App):
radiobin_basename = basename(self.args.radiobin)
resources_basename = ""
radio_version = 0
radio_meta = None
radio_addr = self.args.radioaddr
if self.args.radiobin:
if not self.args.radiotype:
raise ValueError("Missing --radiotype")
radio_meta = CoproBinary(self.args.radiobin)
radio_version = self.copro_version_as_int(radio_meta, self.args.radiotype)
if radio_addr == 0:
radio_addr = radio_meta.get_flash_load_addr()
self.logger.info(
f"Using guessed radio address 0x{radio_addr:X}, verify with Release_Notes"
" or specify --radioaddr"
)
if not exists(self.args.directory):
os.makedirs(self.args.directory)
shutil.copyfile(self.args.stage, join(self.args.directory, stage_basename))
shutil.copyfile(self.args.dfu, join(self.args.directory, dfu_basename))
if self.args.dfu:
shutil.copyfile(self.args.dfu, join(self.args.directory, dfu_basename))
if radiobin_basename:
shutil.copyfile(
self.args.radiobin, join(self.args.directory, radiobin_basename)
@@ -73,13 +101,22 @@ class Main(App):
file.writeKey("Loader CRC", self.int2ffhex(self.crc(self.args.stage)))
file.writeKey("Firmware", dfu_basename)
file.writeKey("Radio", radiobin_basename or "")
file.writeKey("Radio address", self.int2ffhex(self.args.radioaddr or 0))
file.writeKey("Radio version", self.int2ffhex(self.args.radioversion or 0))
file.writeKey("Radio address", self.int2ffhex(radio_addr))
file.writeKey("Radio version", self.int2ffhex(radio_version))
if radiobin_basename:
file.writeKey("Radio CRC", self.int2ffhex(self.crc(self.args.radiobin)))
else:
file.writeKey("Radio CRC", self.int2ffhex(0))
file.writeKey("Resources", resources_basename)
file.writeComment(
"NEVER EVER MESS WITH THESE VALUES, YOU WILL BRICK YOUR DEVICE"
)
if self.args.obdata:
obd = OptionBytesData(self.args.obdata)
obvalues = obd.gen_values().export()
file.writeKey("OB reference", self.bytes2ffhex(obvalues.reference))
file.writeKey("OB mask", self.bytes2ffhex(obvalues.compare_mask))
file.writeKey("OB write mask", self.bytes2ffhex(obvalues.write_mask))
file.save(join(self.args.directory, self.UPDATE_MANIFEST_NAME))
return 0
@@ -90,9 +127,34 @@ class Main(App):
) as tarball:
tarball.add(srcdir, arcname="")
@staticmethod
def copro_version_as_int(coprometa, stacktype):
major = coprometa.img_sig.version_major
minor = coprometa.img_sig.version_minor
sub = coprometa.img_sig.version_sub
branch = coprometa.img_sig.version_branch
release = coprometa.img_sig.version_build
stype = get_stack_type(stacktype)
return (
major
| (minor << 8)
| (sub << 16)
| (branch << 24)
| (release << 32)
| (stype << 40)
)
@staticmethod
def bytes2ffhex(value: bytes):
return " ".join(f"{b:02X}" for b in value)
@staticmethod
def int2ffhex(value: int):
hexstr = "%08X" % value
n_hex_bytes = 4
if value:
n_hex_bytes = math.ceil(math.ceil(math.log2(value)) / 8) * 2
fmtstr = f"%0{n_hex_bytes}X"
hexstr = fmtstr % value
return " ".join(list(Main.batch(hexstr, 2))[::-1])
@staticmethod