[FL-1970, FL-1965, FL-1872, FL-1689] Python framework, Scripts and fixes (#779)
* Scripts: add flipper lib, migrate ob to flipper lib, update ob.data * Makefile: speedup build with phony target for .d files * FuriHal,U8G2: full MGG display support and ERC contrast tuning. * Desktop: fix dolphin rename artifact. * Scripts: port otp.py to flipper scripting lib. * Scripts: add wipe and core1 flashing to flash.py, remove obsolete shell scripts * Scripts: replace core1 flashing script with global makefile. * Scripts: final touches and migration to python. Root Makefile for everything.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# About
|
||||
|
||||
This folder contains differnt scripts that automates routine actions.
|
||||
This folder contains supplementary scripts that automates routine actions.
|
||||
Flashing scripts are based on cli version of [STM32CubeProgrammer](https://www.st.com/en/development-tools/stm32cubeprog.html).
|
||||
You will need to add STM32_Programmer_CLI to your path to use them.
|
||||
|
||||
@@ -9,29 +9,26 @@ You will need to add STM32_Programmer_CLI to your path to use them.
|
||||
Always flash your device in the folllowing sequence:
|
||||
|
||||
- OTP (Only on empty MCU)
|
||||
- Core2 firmware
|
||||
- Core1 firmware
|
||||
- Core1 and Core2 firmware flashing
|
||||
- Option Bytes
|
||||
|
||||
## Otp flashing
|
||||
|
||||
!!! Flashing incorrect OTP may permanently brick your device !!!
|
||||
|
||||
Normally OTP data generated and flashed at factory.
|
||||
Normally OTP data generated and flashed at the factory.
|
||||
In case if MCU was replaced you'll need correct OTP data to be able to use companion applications.
|
||||
Use `otp.py` to generate OTP data and `flash_otp_version_*` to flash OTP zone.
|
||||
Use `otp.py` to generate and flash OTP data.
|
||||
You will need exact main board revision to genrate OTP data. It can be found on main PCB.
|
||||
Also display type, region and etc...
|
||||
|
||||
!!! Flashing incorrect OTP may permanently brick your device !!!
|
||||
|
||||
## Core2 flashing
|
||||
## Core1 and Core2 firmware flashing
|
||||
|
||||
Script blindly updates FUS and Radiostack. This operation is going to corrupt bootloader and firmware.
|
||||
Reflash Core1 after Core2.
|
||||
|
||||
## Core1 flashing
|
||||
|
||||
Script compiles and flashes both bootloader and firmware.
|
||||
Main flashing sequence can be found in root `Makefile`.
|
||||
Core2 goes first, then Core1.
|
||||
Never flash FUS or you will loose your job, girlfriend and keys in secure enclave.
|
||||
|
||||
## Option Bytes
|
||||
|
||||
|
152
scripts/flash.py
Executable file
152
scripts/flash.py
Executable file
@@ -0,0 +1,152 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import logging
|
||||
import argparse
|
||||
import sys
|
||||
import os
|
||||
|
||||
from flipper.app import App
|
||||
from flipper.cube import CubeProgrammer
|
||||
|
||||
STATEMENT = "AGREE_TO_LOOSE_FLIPPER_FEATURES_THAT_USES_CRYPTO_ENCLAVE"
|
||||
|
||||
|
||||
class Main(App):
|
||||
def init(self):
|
||||
self.subparsers = self.parser.add_subparsers(help="sub-command help")
|
||||
# Wipe
|
||||
self.parser_wipe = self.subparsers.add_parser("wipe", help="Wipe MCU Flash")
|
||||
self.parser_wipe.set_defaults(func=self.wipe)
|
||||
# Core 1 boot
|
||||
self.parser_core1boot = self.subparsers.add_parser(
|
||||
"core1boot", help="Flash Core1 Bootloader"
|
||||
)
|
||||
self._addArgsSWD(self.parser_core1boot)
|
||||
self.parser_core1boot.add_argument(
|
||||
"bootloader", type=str, help="Bootloader binary"
|
||||
)
|
||||
self.parser_core1boot.set_defaults(func=self.core1boot)
|
||||
# Core 1 firmware
|
||||
self.parser_core1firmware = self.subparsers.add_parser(
|
||||
"core1firmware", help="Flash Core1 Firmware"
|
||||
)
|
||||
self._addArgsSWD(self.parser_core1firmware)
|
||||
self.parser_core1firmware.add_argument(
|
||||
"firmware", type=str, help="Firmware binary"
|
||||
)
|
||||
self.parser_core1firmware.set_defaults(func=self.core1firmware)
|
||||
# Core 1 all
|
||||
self.parser_core1 = self.subparsers.add_parser(
|
||||
"core1", help="Flash Core1 Boot and Firmware"
|
||||
)
|
||||
self._addArgsSWD(self.parser_core1)
|
||||
self.parser_core1.add_argument("bootloader", type=str, help="Bootloader binary")
|
||||
self.parser_core1.add_argument("firmware", type=str, help="Firmware binary")
|
||||
self.parser_core1.set_defaults(func=self.core1)
|
||||
# Core 2 fus
|
||||
self.parser_core2fus = self.subparsers.add_parser(
|
||||
"core2fus", help="Flash Core2 Firmware Update Service"
|
||||
)
|
||||
self._addArgsSWD(self.parser_core2fus)
|
||||
self.parser_core2fus.add_argument(
|
||||
"--statement",
|
||||
type=str,
|
||||
help="NEVER FLASH FUS, IT WILL ERASE CRYPTO ENCLAVE",
|
||||
required=True,
|
||||
)
|
||||
self.parser_core2fus.add_argument(
|
||||
"fus_address", type=str, help="Firmware Update Service Address"
|
||||
)
|
||||
self.parser_core2fus.add_argument(
|
||||
"fus", type=str, help="Firmware Update Service Binary"
|
||||
)
|
||||
self.parser_core2fus.set_defaults(func=self.core2fus)
|
||||
# Core 2 radio stack
|
||||
self.parser_core2radio = self.subparsers.add_parser(
|
||||
"core2radio", help="Flash Core2 Radio stack"
|
||||
)
|
||||
self._addArgsSWD(self.parser_core2radio)
|
||||
self.parser_core2radio.add_argument(
|
||||
"radio_address", type=str, help="Radio Stack Binary Address"
|
||||
)
|
||||
self.parser_core2radio.add_argument(
|
||||
"radio", type=str, help="Radio Stack Binary"
|
||||
)
|
||||
self.parser_core2radio.set_defaults(func=self.core2radio)
|
||||
|
||||
def _addArgsSWD(self, parser):
|
||||
parser.add_argument(
|
||||
"--port", type=str, help="Port to connect: swd or usb1", default="swd"
|
||||
)
|
||||
|
||||
def wipe(self):
|
||||
self.logger.info(f"Wiping flash")
|
||||
cp = CubeProgrammer("swd")
|
||||
self.logger.info(f"Setting RDP to 0xBB")
|
||||
cp.setOptionBytes({"RDP": ("0xBB", "rw")})
|
||||
self.logger.info(f"Verifying RDP")
|
||||
r = cp.checkOptionBytes({"RDP": ("0xBB", "rw")})
|
||||
assert r == True
|
||||
self.logger.info(f"Result: {r}")
|
||||
self.logger.info(f"Setting RDP to 0xAA")
|
||||
cp.setOptionBytes({"RDP": ("0xAA", "rw")})
|
||||
self.logger.info(f"Verifying RDP")
|
||||
r = cp.checkOptionBytes({"RDP": ("0xAA", "rw")})
|
||||
assert r == True
|
||||
self.logger.info(f"Result: {r}")
|
||||
self.logger.info(f"Complete")
|
||||
return 0
|
||||
|
||||
def core1boot(self):
|
||||
self.logger.info(f"Flashing bootloader")
|
||||
cp = CubeProgrammer(self.args.port)
|
||||
cp.flashBin("0x08000000", self.args.bootloader)
|
||||
self.logger.info(f"Complete")
|
||||
cp.resetTarget()
|
||||
return 0
|
||||
|
||||
def core1firmware(self):
|
||||
self.logger.info(f"Flashing firmware")
|
||||
cp = CubeProgrammer(self.args.port)
|
||||
cp.flashBin("0x08008000", self.args.firmware)
|
||||
self.logger.info(f"Complete")
|
||||
cp.resetTarget()
|
||||
return 0
|
||||
|
||||
def core1(self):
|
||||
self.logger.info(f"Flashing bootloader")
|
||||
cp = CubeProgrammer(self.args.port)
|
||||
cp.flashBin("0x08000000", self.args.bootloader)
|
||||
self.logger.info(f"Flashing firmware")
|
||||
cp.flashBin("0x08008000", self.args.firmware)
|
||||
cp.resetTarget()
|
||||
self.logger.info(f"Complete")
|
||||
return 0
|
||||
|
||||
def core2fus(self):
|
||||
if self.args.statement != STATEMENT:
|
||||
self.logger.error(
|
||||
f"PLEASE DON'T. THIS FEATURE INTENDED ONLY FOR FACTORY FLASHING"
|
||||
)
|
||||
return 1
|
||||
self.logger.info(f"Flashing Firmware Update Service")
|
||||
cp = CubeProgrammer(self.args.port)
|
||||
cp.flashCore2(self.args.fus_address, self.args.fus)
|
||||
self.logger.info(f"Complete")
|
||||
return 0
|
||||
|
||||
def core2radio(self):
|
||||
if int(self.args.radio_address, 16) > 0x080E0000:
|
||||
self.logger.error(f"I KNOW WHAT YOU DID LAST SUMMER")
|
||||
return 1
|
||||
cp = CubeProgrammer(self.args.port)
|
||||
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)
|
||||
self.logger.info(f"Complete")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
Main()()
|
@@ -1,12 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -x -e
|
||||
|
||||
SCRIPT_DIR="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
rm "$PROJECT_DIR"/bootloader/.obj/f*/flash || true
|
||||
make -C "$PROJECT_DIR"/bootloader -j9 flash
|
||||
|
||||
rm "$PROJECT_DIR"/firmware/.obj/f*/flash || true
|
||||
make -C "$PROJECT_DIR"/firmware -j9 flash
|
@@ -1,20 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -x -e
|
||||
|
||||
SCRIPT_DIR="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
COPRO_DIR="$PROJECT_DIR/lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x"
|
||||
|
||||
STM32_Programmer_CLI -c port=swd -fwupgrade "$COPRO_DIR"/stm32wb5x_FUS_fw_for_fus_0_5_3.bin 0x080EC000 || true
|
||||
STM32_Programmer_CLI -c port=swd
|
||||
|
||||
STM32_Programmer_CLI -c port=swd -fwupgrade "$COPRO_DIR"/stm32wb5x_FUS_fw.bin 0x080EC000 || true
|
||||
STM32_Programmer_CLI -c port=swd
|
||||
|
||||
STM32_Programmer_CLI -c port=swd -fwdelete
|
||||
|
||||
STM32_Programmer_CLI -c port=swd -fwupgrade "$COPRO_DIR"/stm32wb5x_BLE_Stack_full_fw.bin 0x080CA000 firstinstall=0
|
||||
|
||||
STM32_Programmer_CLI -c port=swd -ob nSWBOOT0=1 nBOOT0=1
|
||||
|
@@ -1,17 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -x -e
|
||||
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "OTP file required"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ ! -f "$1" ]; then
|
||||
echo "Unable to open OTP file"
|
||||
exit
|
||||
fi
|
||||
|
||||
STM32_Programmer_CLI -c port=usb1 -d "$1" 0x1FFF7000
|
||||
|
||||
STM32_Programmer_CLI -c port=usb1 -r8 0x1FFF7000 8
|
@@ -1,17 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -x -e
|
||||
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "OTP file required"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ ! -f "$1" ]; then
|
||||
echo "Unable to open OTP file"
|
||||
exit
|
||||
fi
|
||||
|
||||
STM32_Programmer_CLI -c port=swd -d "$1" 0x1FFF7000
|
||||
|
||||
STM32_Programmer_CLI -c port=swd -r8 0x1FFF7000 8
|
@@ -1,11 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -x -e
|
||||
|
||||
STM32_Programmer_CLI -c port=swd -ob RDP=0xBB
|
||||
|
||||
STM32_Programmer_CLI -c port=swd -ob displ
|
||||
|
||||
STM32_Programmer_CLI -c port=swd --readunprotect
|
||||
|
||||
STM32_Programmer_CLI -c port=swd -ob displ
|
47
scripts/flipper/app.py
Normal file
47
scripts/flipper/app.py
Normal file
@@ -0,0 +1,47 @@
|
||||
import logging
|
||||
import argparse
|
||||
import sys
|
||||
import os
|
||||
|
||||
|
||||
class App:
|
||||
def __init__(self):
|
||||
# Argument Parser
|
||||
self.parser = argparse.ArgumentParser()
|
||||
self.parser.add_argument("-d", "--debug", action="store_true", help="Debug")
|
||||
# Logging
|
||||
self.logger = logging.getLogger()
|
||||
# Application specific initialization
|
||||
self.init()
|
||||
|
||||
def __call__(self):
|
||||
self.args = self.parser.parse_args()
|
||||
if "func" not in self.args:
|
||||
self.parser.error("Choose something to do")
|
||||
# configure log output
|
||||
self.log_level = logging.DEBUG if self.args.debug else logging.INFO
|
||||
self.logger.setLevel(self.log_level)
|
||||
self.handler = logging.StreamHandler(sys.stdout)
|
||||
self.handler.setLevel(self.log_level)
|
||||
self.formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s")
|
||||
self.handler.setFormatter(self.formatter)
|
||||
self.logger.addHandler(self.handler)
|
||||
|
||||
# execute requested function
|
||||
self.before()
|
||||
return_code = self.args.func()
|
||||
self.after()
|
||||
if isinstance(return_code, int):
|
||||
exit(return_code)
|
||||
else:
|
||||
self.logger.error(f"Missing return code")
|
||||
exit(255)
|
||||
|
||||
def init(self):
|
||||
raise Exception("init() is not implemented")
|
||||
|
||||
def before(self):
|
||||
pass
|
||||
|
||||
def after(self):
|
||||
pass
|
91
scripts/flipper/cube.py
Normal file
91
scripts/flipper/cube.py
Normal file
@@ -0,0 +1,91 @@
|
||||
import logging
|
||||
import subprocess
|
||||
|
||||
|
||||
class CubeProgrammer:
|
||||
"""STM32 Cube Programmer cli wrapper"""
|
||||
|
||||
def __init__(self, port, params=[]):
|
||||
self.port = port
|
||||
self.params = params
|
||||
# logging
|
||||
self.logger = logging.getLogger()
|
||||
|
||||
def _execute(self, args):
|
||||
try:
|
||||
output = subprocess.check_output(
|
||||
[
|
||||
"STM32_Programmer_CLI",
|
||||
"-q",
|
||||
f"-c port={self.port}",
|
||||
*self.params,
|
||||
*args,
|
||||
]
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
if e.output:
|
||||
print("Process output:\n", e.output.decode())
|
||||
print("Process return code:", e.returncode)
|
||||
raise e
|
||||
assert output
|
||||
return output.decode()
|
||||
|
||||
def getVersion(self):
|
||||
output = self._execute(["--version"])
|
||||
|
||||
def checkOptionBytes(self, option_bytes):
|
||||
output = self._execute(["-ob displ"])
|
||||
ob_correct = True
|
||||
for line in output.split("\n"):
|
||||
line = line.strip()
|
||||
if not ":" in line:
|
||||
self.logger.debug(f"Skipping line: {line}")
|
||||
continue
|
||||
key, data = line.split(":", 1)
|
||||
key = key.strip()
|
||||
data = data.strip()
|
||||
if not key in option_bytes.keys():
|
||||
self.logger.debug(f"Skipping key: {key}")
|
||||
continue
|
||||
self.logger.debug(f"Processing key: {key} {data}")
|
||||
value, comment = data.split(" ", 1)
|
||||
value = value.strip()
|
||||
comment = comment.strip()
|
||||
if option_bytes[key][0] != value:
|
||||
self.logger.error(
|
||||
f"Invalid OB: {key} {value}, expected: {option_bytes[key][0]}"
|
||||
)
|
||||
ob_correct = False
|
||||
return ob_correct
|
||||
|
||||
def setOptionBytes(self, option_bytes):
|
||||
options = []
|
||||
for key, (value, attr) in option_bytes.items():
|
||||
if "w" in attr:
|
||||
options.append(f"{key}={value}")
|
||||
self._execute(["-ob", *options])
|
||||
return True
|
||||
|
||||
def flashBin(self, address, filename):
|
||||
self._execute(
|
||||
[
|
||||
"-d",
|
||||
filename,
|
||||
f"{address}",
|
||||
]
|
||||
)
|
||||
|
||||
def flashCore2(self, address, filename):
|
||||
self._execute(
|
||||
[
|
||||
"-fwupgrade",
|
||||
filename,
|
||||
f"{address}",
|
||||
]
|
||||
)
|
||||
|
||||
def deleteCore2RadioStack(self):
|
||||
self._execute(["-fwdelete"])
|
||||
|
||||
def resetTarget(self):
|
||||
self._execute([])
|
@@ -25,7 +25,7 @@ SBRSA:0xA:r
|
||||
SBRV:0x32800:r
|
||||
PCROP1A_STRT:0x1FF:r
|
||||
PCROP1A_END:0x0:r
|
||||
PCROP_RDP:0x1:r
|
||||
PCROP_RDP:0x1:rw
|
||||
PCROP1B_STRT:0x1FF:r
|
||||
PCROP1B_END:0x0:r
|
||||
WRP1A_STRT:0xFF:r
|
||||
|
116
scripts/ob.py
116
scripts/ob.py
@@ -6,12 +6,12 @@ import subprocess
|
||||
import sys
|
||||
import os
|
||||
|
||||
from flipper.app import App
|
||||
from flipper.cube import CubeProgrammer
|
||||
|
||||
class Main:
|
||||
def __init__(self):
|
||||
# command args
|
||||
self.parser = argparse.ArgumentParser()
|
||||
self.parser.add_argument("-d", "--debug", action="store_true", help="Debug")
|
||||
|
||||
class Main(App):
|
||||
def init(self):
|
||||
self.subparsers = self.parser.add_subparsers(help="sub-command help")
|
||||
self.parser_check = self.subparsers.add_parser(
|
||||
"check", help="Check Option Bytes"
|
||||
@@ -20,39 +20,16 @@ class Main:
|
||||
"--port", type=str, help="Port to connect: swd or usb1", default="swd"
|
||||
)
|
||||
self.parser_check.set_defaults(func=self.check)
|
||||
# Set command
|
||||
self.parser_set = self.subparsers.add_parser("set", help="Set Option Bytes")
|
||||
self.parser_set.add_argument(
|
||||
"--port", type=str, help="Port to connect: swd or usb1", default="swd"
|
||||
)
|
||||
self.parser_set.set_defaults(func=self.set)
|
||||
# logging
|
||||
self.logger = logging.getLogger()
|
||||
# OB
|
||||
self.ob = {}
|
||||
|
||||
def __call__(self):
|
||||
self.args = self.parser.parse_args()
|
||||
if "func" not in self.args:
|
||||
self.parser.error("Choose something to do")
|
||||
# configure log output
|
||||
self.log_level = logging.DEBUG if self.args.debug else logging.INFO
|
||||
self.logger.setLevel(self.log_level)
|
||||
self.handler = logging.StreamHandler(sys.stdout)
|
||||
self.handler.setLevel(self.log_level)
|
||||
self.formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s")
|
||||
self.handler.setFormatter(self.formatter)
|
||||
self.logger.addHandler(self.handler)
|
||||
# execute requested function
|
||||
self.loadOB()
|
||||
|
||||
return_code = self.args.func()
|
||||
if isinstance(return_code, int):
|
||||
return return_code
|
||||
else:
|
||||
self.logger.error(f"Forgotten return code")
|
||||
return 255
|
||||
|
||||
def loadOB(self):
|
||||
def before(self):
|
||||
self.logger.info(f"Loading Option Bytes data")
|
||||
file_path = os.path.join(os.path.dirname(sys.argv[0]), "ob.data")
|
||||
file = open(file_path, "r")
|
||||
@@ -62,47 +39,8 @@ class Main:
|
||||
|
||||
def check(self):
|
||||
self.logger.info(f"Checking Option Bytes")
|
||||
try:
|
||||
output = subprocess.check_output(
|
||||
[
|
||||
"STM32_Programmer_CLI",
|
||||
"-q",
|
||||
"-c",
|
||||
f"port={self.args.port}",
|
||||
"-ob displ",
|
||||
]
|
||||
)
|
||||
assert output
|
||||
except subprocess.CalledProcessError as e:
|
||||
self.logger.error(e.output.decode())
|
||||
self.logger.error(f"Failed to call STM32_Programmer_CLI")
|
||||
return 127
|
||||
except Exception as e:
|
||||
self.logger.error(f"Failed to call STM32_Programmer_CLI")
|
||||
self.logger.exception(e)
|
||||
return 126
|
||||
ob_correct = True
|
||||
for line in output.decode().split("\n"):
|
||||
line = line.strip()
|
||||
if not ":" in line:
|
||||
self.logger.debug(f"Skipping line: {line}")
|
||||
continue
|
||||
key, data = line.split(":", 1)
|
||||
key = key.strip()
|
||||
data = data.strip()
|
||||
if not key in self.ob.keys():
|
||||
self.logger.debug(f"Skipping key: {key}")
|
||||
continue
|
||||
self.logger.debug(f"Processing key: {key} {data}")
|
||||
value, comment = data.split(" ", 1)
|
||||
value = value.strip()
|
||||
comment = comment.strip()
|
||||
if self.ob[key][0] != value:
|
||||
self.logger.error(
|
||||
f"Invalid OB: {key} {value}, expected: {self.ob[key][0]}"
|
||||
)
|
||||
ob_correct = False
|
||||
if ob_correct:
|
||||
cp = CubeProgrammer(self.args.port)
|
||||
if cp.checkOptionBytes(self.ob):
|
||||
self.logger.info(f"OB Check OK")
|
||||
return 0
|
||||
else:
|
||||
@@ -111,34 +49,14 @@ class Main:
|
||||
|
||||
def set(self):
|
||||
self.logger.info(f"Setting Option Bytes")
|
||||
options = []
|
||||
for key, (value, attr) in self.ob.items():
|
||||
if "w" in attr:
|
||||
options.append(f"{key}={value}")
|
||||
try:
|
||||
output = subprocess.check_output(
|
||||
[
|
||||
"STM32_Programmer_CLI",
|
||||
"-q",
|
||||
"-c",
|
||||
f"port={self.args.port}",
|
||||
"-ob",
|
||||
*options,
|
||||
]
|
||||
)
|
||||
assert output
|
||||
self.logger.info(f"Success")
|
||||
except subprocess.CalledProcessError as e:
|
||||
self.logger.error(e.output.decode())
|
||||
self.logger.error(f"Failed to call STM32_Programmer_CLI")
|
||||
return 125
|
||||
except Exception as e:
|
||||
self.logger.error(f"Failed to call STM32_Programmer_CLI")
|
||||
self.logger.exception(e)
|
||||
return 124
|
||||
return 0
|
||||
cp = CubeProgrammer(self.args.port)
|
||||
if cp.setOptionBytes(self.ob):
|
||||
self.logger.info(f"OB Set OK")
|
||||
return 0
|
||||
else:
|
||||
self.logger.error(f"OB Set FAIL")
|
||||
return 255
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
return_code = Main()()
|
||||
exit(return_code)
|
||||
Main()()
|
||||
|
131
scripts/otp.py
131
scripts/otp.py
@@ -32,12 +32,13 @@ OTP_DISPLAYS = {
|
||||
"mgg": 0x02,
|
||||
}
|
||||
|
||||
from flipper.app import App
|
||||
from flipper.cube import CubeProgrammer
|
||||
|
||||
class Main:
|
||||
def __init__(self):
|
||||
# command args
|
||||
self.parser = argparse.ArgumentParser()
|
||||
self.parser.add_argument("-d", "--debug", action="store_true", help="Debug")
|
||||
|
||||
class Main(App):
|
||||
def init(self):
|
||||
# SubParsers
|
||||
self.subparsers = self.parser.add_subparsers(help="sub-command help")
|
||||
# Generate All
|
||||
self.parser_generate_all = self.subparsers.add_parser(
|
||||
@@ -73,21 +74,6 @@ class Main:
|
||||
self.logger = logging.getLogger()
|
||||
self.timestamp = datetime.datetime.now().timestamp()
|
||||
|
||||
def __call__(self):
|
||||
self.args = self.parser.parse_args()
|
||||
if "func" not in self.args:
|
||||
self.parser.error("Choose something to do")
|
||||
# configure log output
|
||||
self.log_level = logging.DEBUG if self.args.debug else logging.INFO
|
||||
self.logger.setLevel(self.log_level)
|
||||
self.handler = logging.StreamHandler(sys.stdout)
|
||||
self.handler.setLevel(self.log_level)
|
||||
self.formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s")
|
||||
self.handler.setFormatter(self.formatter)
|
||||
self.logger.addHandler(self.handler)
|
||||
# execute requested function
|
||||
self.args.func()
|
||||
|
||||
def _add_swd_args(self, parser):
|
||||
parser.add_argument(
|
||||
"--port", type=str, help="Port to connect: swd or usb1", default="swd"
|
||||
@@ -153,89 +139,90 @@ class Main:
|
||||
)
|
||||
|
||||
def generate_all(self):
|
||||
self.logger.debug(f"Generating OTP")
|
||||
self.logger.info(f"Generating OTP")
|
||||
self._process_first_args()
|
||||
self._process_second_args()
|
||||
open(f"{self.args.file}_first.bin", "wb").write(self._pack_first())
|
||||
open(f"{self.args.file}_second.bin", "wb").write(self._pack_second())
|
||||
self.logger.info(
|
||||
f"Generated files: {self.args.file}_first.bin and {self.args.file}_second.bin"
|
||||
)
|
||||
|
||||
return 0
|
||||
|
||||
def flash_first(self):
|
||||
self.logger.debug(f"Flashing first block of OTP")
|
||||
self.logger.info(f"Flashing first block of OTP")
|
||||
|
||||
self._process_first_args()
|
||||
|
||||
filename = f"otp_unknown_first_{self.timestamp}.bin"
|
||||
file = open(filename, "wb")
|
||||
file.write(self._pack_first())
|
||||
file.close()
|
||||
|
||||
self._flash_bin("0x1FFF7000", filename)
|
||||
try:
|
||||
self.logger.info(f"Packing binary data")
|
||||
file = open(filename, "wb")
|
||||
file.write(self._pack_first())
|
||||
file.close()
|
||||
self.logger.info(f"Flashing OTP")
|
||||
cp = CubeProgrammer(self.args.port)
|
||||
cp.flashBin("0x1FFF7000", filename)
|
||||
cp.resetTarget()
|
||||
self.logger.info(f"Flashed Successfully")
|
||||
os.remove(filename)
|
||||
except Exception as e:
|
||||
self.logger.exception(e)
|
||||
return 0
|
||||
|
||||
os.remove(filename)
|
||||
return 1
|
||||
|
||||
def flash_second(self):
|
||||
self.logger.debug(f"Flashing second block of OTP")
|
||||
self.logger.info(f"Flashing second block of OTP")
|
||||
|
||||
self._process_second_args()
|
||||
|
||||
filename = f"otp_{self.args.name}_second_{self.timestamp}.bin"
|
||||
file = open(filename, "wb")
|
||||
file.write(self._pack_second())
|
||||
file.close()
|
||||
|
||||
self._flash_bin("0x1FFF7010", filename)
|
||||
try:
|
||||
self.logger.info(f"Packing binary data")
|
||||
file = open(filename, "wb")
|
||||
file.write(self._pack_second())
|
||||
file.close()
|
||||
self.logger.info(f"Flashing OTP")
|
||||
cp = CubeProgrammer(self.args.port)
|
||||
cp.flashBin("0x1FFF7010", filename)
|
||||
cp.resetTarget()
|
||||
self.logger.info(f"Flashed Successfully")
|
||||
os.remove(filename)
|
||||
except Exception as e:
|
||||
self.logger.exception(e)
|
||||
return 1
|
||||
|
||||
os.remove(filename)
|
||||
return 0
|
||||
|
||||
def flash_all(self):
|
||||
self.logger.debug(f"Flashing OTP")
|
||||
self.logger.info(f"Flashing OTP")
|
||||
|
||||
self._process_first_args()
|
||||
self._process_second_args()
|
||||
|
||||
filename = f"otp_{self.args.name}_whole_{self.timestamp}.bin"
|
||||
file = open(filename, "wb")
|
||||
file.write(self._pack_first())
|
||||
file.write(self._pack_second())
|
||||
file.close()
|
||||
|
||||
self._flash_bin("0x1FFF7000", filename)
|
||||
|
||||
os.remove(filename)
|
||||
|
||||
def _flash_bin(self, address, filename):
|
||||
self.logger.debug(f"Programming {filename} at {address}")
|
||||
try:
|
||||
output = subprocess.check_output(
|
||||
[
|
||||
"STM32_Programmer_CLI",
|
||||
"-q",
|
||||
"-c",
|
||||
f"port={self.args.port}",
|
||||
"-d",
|
||||
filename,
|
||||
f"{address}",
|
||||
]
|
||||
)
|
||||
assert output
|
||||
self.logger.info(f"Success")
|
||||
except subprocess.CalledProcessError as e:
|
||||
self.logger.error(e.output.decode())
|
||||
self.logger.error(f"Failed to call STM32_Programmer_CLI")
|
||||
return
|
||||
self.logger.info(f"Packing binary data")
|
||||
file = open(filename, "wb")
|
||||
file.write(self._pack_first())
|
||||
file.write(self._pack_second())
|
||||
file.close()
|
||||
self.logger.info(f"Flashing OTP")
|
||||
cp = CubeProgrammer(self.args.port)
|
||||
cp.flashBin("0x1FFF7000", filename)
|
||||
cp.resetTarget()
|
||||
self.logger.info(f"Flashed Successfully")
|
||||
os.remove(filename)
|
||||
except Exception as e:
|
||||
self.logger.error(f"Failed to call STM32_Programmer_CLI")
|
||||
self.logger.exception(e)
|
||||
return
|
||||
# reboot
|
||||
subprocess.check_output(
|
||||
[
|
||||
"STM32_Programmer_CLI",
|
||||
"-q",
|
||||
"-c",
|
||||
f"port={self.args.port}",
|
||||
]
|
||||
)
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
Reference in New Issue
Block a user