7ce305fca3
* 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>
209 lines
6.3 KiB
Python
209 lines
6.3 KiB
Python
#!/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()
|